--[[ Auctioneer Version: 5.9.4961 (WhackyWallaby) Revision: $Id: CoreUtil.lua 4880 2010-09-15 20:02:11Z Nechckn $ URL: http://auctioneeraddon.com/ This is an addon for World of Warcraft that adds statistical history to the auction data that is collected when the auction is scanned, so that you can easily determine what price you will be able to sell an item for at auction or at a vendor whenever you mouse-over an item in the game License: This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program(see GPL.txt); if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Note: This AddOn's source code is specifically designed to work with World of Warcraft's interpreted AddOn system. You have an implicit license to use this AddOn with these facilities since that is its designated purpose as per: http://www.fsf.org/licensing/licenses/gpl-faq.html#InterpreterIncompat ]] if not AucAdvanced then return end local lib = AucAdvanced local private = {} local coremodule = AucAdvanced.GetCoreModule("CoreUtil") if not coremodule then return end -- Someone has explicitely broken us local tooltip = LibStub("nTipHelper:1") local Const = lib.Const function coremodule.OnLoad(addon) if addon == "auc-advanced" then private.FactionOnLoad() end end coremodule.Processors = {} function coremodule.Processors.auctionopen() private.isAHOpen = true end function coremodule.Processors.auctionclose() private.isAHOpen = false end function coremodule.Processors.newmodule(event, libType, libName) -- resetting caches here allows us to respond to modules that are not created by lib.NewModule, -- as long as they correctly send a "newmodule" message when created private.modulecache = nil private.resetPriceModels() end --Localization via babylonian local Babylonian = LibStub("Babylonian") assert(Babylonian, "Babylonian is not installed") local babylonian = Babylonian(AuctioneerLocalizations) function lib.localizations(stringKey) local locale = lib.Settings.GetSetting("SelectedLocale")--locales are user choose-able -- translated key or english Key or Raw Key return babylonian(locale, stringKey) or babylonian[stringKey] or stringKey end --The following function will build tables correlating Chat Frame names with their index numbers, and return different formats according to an option passed in. function lib.getFrameNames(option) local frames = {} local frameName = "" --This iterates through the first 10 ChatWindows, getting the name associated with it. for i=1, 10 do local name, fontSize, r, g, b, a, shown, locked, docked = GetChatWindowInfo(i) --If the name isn't blank, then we build tables where the Chat Frame names are the keys, and indexes the values if the frame is either shown or docked. if (name ~= "" and (shown or docked)) then frames[name] = i end end --Next, if a number was passed as an option, we simply retrieve the frame name and assign it to frameName if (type(option) == "number") then local name, fontSize, r, g, b, a, shown, locked, docked = GetChatWindowInfo(option); frameName = name return frameName --If no option was specified, we return the Name=>Index table elseif (not option) then return frames end end --This function will be called by our configuration screen to build the appropriate list. function lib.configFramesList() local configFrames = {} for i=1, 10 do local name, fontSize, r, g, b, a, shown, locked, docked = GetChatWindowInfo(i) if (name ~= "") and (docked or shown) then table.insert(configFrames, {name, name}) end end return configFrames end --This function will retrieve the index we've got stored for the user function lib.getFrameIndex() --Check to make sure AucAdvanced.Settings exists, if not initialize it if (not AucAdvanced.Settings) then AucAdvanced.Settings = {} end local get = AucAdvanced.Settings.GetSetting --Get the value of AucAdvanced.Settings["printwindow"] local value = get("printwindow") --If that value doesn't exist, we return a default of 0 if (not value) then return 0 end --Otherwise, we return the value return value end --This function is used to store the user's preferred chatframe function lib.setFrame(frame) local set = AucAdvanced.Settings.SetSetting --If called with no argument if (not frame) then frame = 0 --Compatibility, for those who had set this previously. elseif (type(frame) == "number" and frame > 0 and frame < 10) then frame,_,_,_,_,_,_,_,_=GetChatWindowInfo(frame) --If the frame argument is a string, set that as the frameNumber elseif (type(frame) == "string") then set("printwindow", frame) --Otherwise set it to 0 else set("printwindow", 0) end end --This is the printing function. If the user has selected a preferred chatframe, we'll use it. If not, we'll default to the first one. function lib.Print(...) local output, part, frameIndex local frame = AucAdvanced.getFrameIndex() local allFrames = AucAdvanced.getFrameNames() if (type(frame) == "string") then if (allFrames[frame]) then frameIndex = allFrames[frame] else frameIndex = 1 end --For backward compatibility elseif (type(frame) == "number" and frame > 0 and frame < 9) then frameIndex = frame elseif (frame == 0) then frameIndex = 1 else frameIndex = 1 end local frameReference = _G["ChatFrame"..frameIndex] for i=1, select("#", ...) do part = select(i, ...) part = tostring(part):gsub("{{", "|cffddeeff"):gsub("}}", "|r") if (output) then output = output .. " " .. part else output = part end end --If we have a stored chat frame, print our output there if (frameReference) then frameReference:AddMessage(output, 0.3, 0.9, 0.8) --Otherwise, print it to the client's DEFAULT_CHAT_FRAME else DEFAULT_CHAT_FRAME:AddMessage(output, 0.3, 0.9, 0.8) end end --Creates the list of locales to choose from function lib.changeLocale() local localizations, default = {}, lib.Settings.GetSetting("SelectedLocale") --get the user choosen locale make it first on the list for i in pairs(AuctioneerLocalizations) do if default == i then table.insert(localizations,1, {i, i}) else table.insert(localizations, {i, i}) end end return localizations end -- Creates the list of Pricing Models for use in dropdowns -- Usage: gui:AddControl(id, "Selectbox", column, indent, AucAdvanced.selectorPriceModels, "util.modulename.model") do local pricemodels function private.resetPriceModels() -- called every time a new module loads pricemodels = nil end function lib.selectorPriceModels() if not pricemodels then -- delay creating table until function is first called, to give all modules a chance to load first pricemodels = {} table.insert(pricemodels,{"market", lib.localizations("UCUT_Interface_MarketValue")})--Market value {Reusing Undercut's existing localization string} local algoList = AucAdvanced.API.GetAlgorithms() for pos, name in ipairs(algoList) do table.insert(pricemodels,{name, lib.localizations("APPR_Interface_Stats").." "..name})--Stats: {Reusing Appraiser's existing localization string} end end return pricemodels end end -- Creates the list of Auction Durations for use in deposit cost dropdowns -- Usage: gui:AddControl(id, "Selectbox", column, indent, AucAdvanced.selectorAuctionLength, "util.modulename.deplength") do local auctionlength = { {12, FORMATED_HOURS:format(12)}, {24, FORMATED_HOURS:format(24)}, {48, FORMATED_HOURS:format(48)}, } function lib.selectorAuctionLength() return auctionlength end end function lib.GetFactor(...) return tooltip:GetFactor(...) end function lib.SanitizeLink(...) return tooltip:SanitizeLink(...) end function lib.DecodeLink(...) return tooltip:DecodeLink(...) end function lib.GetLinkQuality(...) return tooltip:GetLinkQuality(...) end function lib.ShowItemLink(...) return tooltip:ShowItemLink(...) end function lib.BreakHyperlink(...) return tooltip:BreakHyperlink(...) end lib.breakHyperlink = lib.BreakHyperlink do -- Faction and ServerKey related functions local splitcache = {} local localizedfactions = { -- the following entries are placeholders ["Alliance"] = "Alliance", ["Horde"] = "Horde", ["Neutral"] = "Neutral", } function lib.SplitServerKey(serverKey) local split = splitcache[serverKey] if not split then if type(serverKey) ~= "string" then return end local realm, faction = strmatch(serverKey, "^(.+)%-(%u%l+)$") local transfaction = localizedfactions[faction] if not transfaction then return end split = {realm, faction, realm.." - "..transfaction} splitcache[serverKey] = split end return split[1], split[2], split[3] end local lookupfaction = { ["alliance"] = "Alliance", [FACTION_ALLIANCE:lower()] = "Alliance", ["horde"] = "Horde", [FACTION_HORDE:lower()] = "Horde", ["neutral"] = "Neutral", } -- Used to check user text input for some form of a faction name; returns standardized form if found -- Possible results are "Alliance", "Horde", "Neutral" or nil if not found function lib.IsFaction(faction) if type(faction) == "string" then return lookupfaction[faction:lower()] end end function lib.GetFaction() local factionGroup = lib.GetFactionGroup() if not factionGroup then return end if factionGroup ~= lib.curFactionGroup then local curFaction if (factionGroup == "Neutral") then lib.cutRate = 0.15 lib.depositRate = 0.25 -- deprecated curFaction = Const.ServerKeyNeutral else lib.cutRate = 0.05 lib.depositRate = 0.05 -- deprecated curFaction = Const.ServerKeyHome end lib.curFaction = curFaction -- deprecated (it's a serverKey, so calling it curFaction is confusing) lib.curFactionGroup = factionGroup lib.curServerKey = curFaction end return lib.curServerKey, Const.PlayerRealm, factionGroup end local zonefactions = {} function lib.GetFactionGroup() if private.isAHOpen or not lib.Settings.GetSetting("alwaysHomeFaction") then local currentZone = GetMinimapZoneText() local factionGroup = zonefactions[currentZone] if not factionGroup then SetMapToCurrentZone() local map = GetMapInfo() if ((map == "Tanaris") or (map == "Winterspring") or (map == "Stranglethorn")) then factionGroup = "Neutral" else factionGroup = Const.PlayerFaction end zonefactions[currentZone] = factionGroup end return factionGroup end return Const.PlayerFaction end function private.FactionOnLoad() local alliance = lib.localizations("ADV_Interface_FactionAlliance") local horde = lib.localizations("ADV_Interface_FactionHorde") local neutral = lib.localizations("ADV_Interface_FactionNeutral") localizedfactions.Alliance = alliance localizedfactions.Horde = horde localizedfactions.Neutral = neutral lookupfaction[alliance:lower()] = "Alliance" lookupfaction[horde:lower()] = "Horde" lookupfaction[neutral:lower()] = "Neutral" wipe(splitcache) end end function private.relevelFrame(frame) return private.relevelFrames(frame:GetFrameLevel() + 2, frame:GetChildren()) end function private.relevelFrames(myLevel, ...) for i = 1, select("#", ...) do local child = select(i, ...) child:SetFrameLevel(myLevel) private.relevelFrame(child) end end function lib.AddTab(tabButton, tabFrame) -- Count the number of auction house tabs (including the tab we are going -- to insert). local tabCount = 1; while (_G["AuctionFrameTab"..(tabCount)]) do --check that tab has not already been created, since we can optionally remove tabs now if _G["AuctionFrameTab"..(tabCount)]:GetName() == tabButton:GetName() then lib.Print("Tab with that name already exists") return end tabCount = tabCount + 1; end -- Find the correct location to insert our Search Auctions and Post Auctions -- tabs. We want to insert them at the end or before BeanCounter's -- Transactions tab. local tabIndex = 1; while (_G["AuctionFrameTab"..(tabIndex)] and _G["AuctionFrameTab"..(tabIndex)]:GetName() ~= "AuctionFrameTabUtilBeanCounter") do tabIndex = tabIndex + 1; end -- Make room for the tab, if needed. for index = tabCount, tabIndex + 1, -1 do _G["AuctionFrameTab"..(index)] = _G["AuctionFrameTab"..(index - 1)]; _G["AuctionFrameTab"..(index)]:SetID(index); end -- Configure the frame. tabFrame:SetParent("AuctionFrame"); tabFrame:SetPoint("TOPLEFT", "AuctionFrame", "TOPLEFT", 0, 0); private.relevelFrame(tabFrame); -- Configure the tab button. _G["AuctionFrameTab"..tabIndex] = tabButton; tabButton:SetParent("AuctionFrame"); tabButton:SetPoint("TOPLEFT", _G["AuctionFrameTab"..(tabIndex - 1)]:GetName(), "TOPRIGHT", -8, 0); tabButton:SetID(tabIndex); tabButton:Show(); -- If we inserted a tab in the middle, adjust the layout of the next tab button. if (tabIndex < tabCount) then nextTabButton = _G["AuctionFrameTab"..(tabIndex + 1)]; nextTabButton:SetPoint("TOPLEFT", tabButton:GetName(), "TOPRIGHT", -8, 0); end -- Update the tab count. PanelTemplates_SetNumTabs(AuctionFrame, tabCount) end --used by modules to "hide" their auction house tab. function lib.RemoveTab(tabButton, tabFrame) -- Count the number of auction house tabs. local tabCount = 0 while (_G["AuctionFrameTab"..(tabCount+1)]) do tabCount = tabCount + 1; end -- Find the correct location to remove the tab local tabIndex, tabFound = 1 while _G["AuctionFrameTab"..(tabIndex)] do if _G["AuctionFrameTab"..(tabIndex)]:GetName() == tabButton:GetName() then tabFound = tabIndex break end tabIndex = tabIndex + 1 end --if we did nto find the correct tab then end if not tabFound then return end -- If we inserted a tab in the middle, adjust the layout of the next tab button after removal. if tabFound and (tabFound < tabCount) then nextTabButton = _G["AuctionFrameTab"..(tabFound + 1)] nextTabButton:SetPoint("TOPLEFT", _G["AuctionFrameTab"..(tabFound - 1)]:GetName(), "TOPRIGHT", -8, 0) end -- Reduce count on tabs remaining _G["AuctionFrameTab"..(tabFound)] = nil --remove old tab from namespace for index = tabFound, tabCount do local tab = _G["AuctionFrameTab"..(index + 1)] if tab then _G["AuctionFrameTab"..(index)] = tab _G["AuctionFrameTab"..(index)]:SetID(index) else --last tab index needs to be removed _G["AuctionFrameTab"..(index)] = nil end end -- Hide the frame. tabFrame:Hide() tabButton:Hide() -- Update the tab count. PanelTemplates_SetNumTabs(AuctionFrame, tabCount - 1) end -- Table management functions: local function replicate(source, depth, history) if type(source) ~= "table" then return source end assert(depth==nil or tonumber(depth), "Unknown depth: " .. tostring(depth)) if not depth then depth = 0 history = {} end assert(history, "Have depth but without history") assert(depth < 100, "Structure is too deep") local dest = {} history[source] = dest for k, v in pairs(source) do if type(v) == "table" then if history[v] then dest[k] = history[v] else dest[k] = replicate(v, depth+1, history) end else dest[k] = v end end return dest end local function empty(item) if type(item) == 'table' then wipe(item) end end local function fill(item, ...) if type(item) ~= 'table' then return end wipe(item) local n = select('#', ...) for i = 1,n do item[i] = select(i, ...) end end -- End table management functions -- Old functions (compatability) lib.Recycle = function() end lib.Acquire = function(...) return {...} end lib.Clone = replicate lib.Scrub = empty -- New functions lib.Replicate = replicate lib.Empty = empty lib.Fill = fill --[[ Functions for establishing a new copy of the library. Recommended method: local libType, libName = "myType", "myName" local lib,parent,private = AucAdvanced.NewModule(libType, libName) if not lib then return end local print,decode,_,_,replicate,empty,get,set,default,debugPrint,fill,_TRANS = AucAdvanced.GetModuleLocals() --]] --[[ Usage: local lib,parent,private = AucAdvanced.NewModule(libType, libName) --]] local moduleKit = {} function lib.NewModule(libType, libName) assert(lib.Modules[libType], "Invalid AucAdvanced libType specified: "..tostring(libType)) if not lib.Modules[libType][libName] then local module = {} local modulePrivate = {} module.libType = libType module.libName = libName module.Private = modulePrivate module.GetName = function() return libName end for k,v in pairs(moduleKit) do module[k] = v end lib.Modules[libType][libName] = module lib.SendProcessorMessage("newmodule", libType, libName) return module, lib, modulePrivate end end --[[ Usage: local libCopy = AucAdvanced.GetModule(libType, libName) --]] function lib.GetModule(libType, libName) assert(lib.Modules[libType], "Invalid AucAdvanced libType specified: "..tostring(libType)) if lib.Modules[libType][libName] then return lib.Modules[libType][libName], lib, private end end --[[ Usage: local print,decode,_,_,replicate,empty,get,set,default,debugPrint,fill,_TRANS = AucAdvanced.GetModuleLocals() -- ]] function lib.GetModuleLocals() return lib.Print, lib.DecodeLink, lib.Recycle, lib.Acquire, lib.Replicate, lib.Empty, lib.Settings.GetSetting, lib.Settings.SetSetting, lib.Settings.SetDefault, lib.Debug.DebugPrint, lib.Fill, lib.localizations end private.checkedModules = {} function private.CheckModule(name, module) if private.checkedModules[module] then return end local fix if not module.GetName or type(module.GetName) ~= "function" then -- Sometimes we find engines that don't have a "GetName()" function. -- All engines should have this function! if nLog then nLog.AddMessage("Auctioneer", "CheckModule", N_WARNING, "Module without GetName()", "No GetName() function was found for module indexed as \""..name.."\". Please fix this!") end fix = true else local reportedName = module.GetName() if reportedName ~= name then if nLog then nLog.AddMessage("Auctioneer", "CheckModule", N_WARNING, "Module GetName() mismatch", "The GetName() function was found for module indexed as \""..name.."\", but actually returns \""..reportedName.."\". Please fix this!") end fix = true end end if fix then module.GetName = function() return name end end private.checkedModules[module] = true end function lib.GetAllModules(having, findSystem, findEngine) local modules if findSystem then findSystem = findSystem:lower() end if findEngine then findEngine = findEngine:lower() end if not findEngine then modules = {} end if not having and not findSystem and private.modulecache then for k,v in ipairs(private.modulecache) do modules[k] = v end return modules end for system, systemMods in pairs(AucAdvanced.Modules) do if not findSystem or system:lower() == findSystem then for engine, engineLib in pairs(systemMods) do private.CheckModule(engine, engineLib) if not having or engineLib[having] then if not findEngine then table.insert(modules, engineLib) elseif engine:lower() == findEngine then return engineLib, system, engine end end end end end if not having and not findSystem then if not private.modulecache then private.modulecache = {} end for k,v in ipairs(modules) do private.modulecache[k] = v end end return modules end --[[ End of CoreModule ]]-- local spmArray = {} function lib.SendProcessorMessage(spmMsg, ...) local spmp = spmArray[spmMsg] if (spmp) then for i=1,#spmp do local x = spmp[i] local f = x.Func --if (nLog) then nLog.AddMessage("Auctioneer", "Coreutil", N_INFO, ("SendProcessorMessage Called %s For %s"):format(x.Name, spmMsg), ("SendProcessorMessage Called %s For %s"):format(x.Name, spmMsg)) end good,msg=pcall(f, spmMsg, ...) if not good then lib.Debug.DebugPrint(msg, "SendProcessorMessage", "Processor Error in "..(x.Name or "??"), 0, "Debug") end end else spmp = {} spmArray[spmMsg] = spmp local modules = AucAdvanced.GetAllModules("Processors") local good, msg for pos, engineLib in ipairs(modules) do local f = engineLib.Processors[spmMsg] if f then local x = {} x.Name = engineLib.GetName() if (spmMsg=="tooltip") then local f1 = f x.Func = function(spmMsg, ...) -- TODO: Make these defaults configurable tooltip:SetColor(0.3, 0.9, 0.8) tooltip:SetMoneyAsText(false) tooltip:SetEmbed(false) f1(spmMsg, ...) end else x.Func = f end table.insert(spmp, x) --if (nLog) then nLog.AddMessage("Auctioneer", "Coreutil", N_INFO, ("SendProcessorMessage Called %s For %s (using Processors)"):format(x.Name, spmMsg), ("SendProcessorMessage Called %s For %s"):format(x.Name, spmMsg)) end good,msg=pcall(f, spmMsg, ...) if not good then lib.Debug.DebugPrint(msg, "SendProcessorMessage", "Processor Error in "..(x.Name or "??"), 0, "Debug") end end end modules = AucAdvanced.GetAllModules("Processor") local good, msg for pos, engineLib in ipairs(modules) do if (not engineLib.Processors) then local x = {} x.Name = engineLib.GetName() x.Func = engineLib.Processor lib.Debug.DebugPrint("Module Using Deprecated Processor to recieve "..(spmMsg or "Unknown").." processor messages.", "SendProcessorMessage", "Deprecated Function Seen in "..(x.Name or "??"), 0, "Warning") table.insert(spmp, x) good,msg=pcall(engineLib.Processor, spmMsg, ...) if not good then lib.Debug.DebugPrint(msg, "SendProcessorMessage", "Processor Error in "..(x.Name or "??"), 0, "Debug") end end end end end function lib.ResetSPMArray() spmArray = {} end -- Returns the tooltip helper function lib.GetTooltip() return tooltip end -- Turns a copper amount into colorized text function lib.Coins(amount, graphic) return (tooltip:Coins(amount, graphic)) end -- Creates a new coin object function lib.CreateMoney(height) return (tooltip:CreateMoney(height)) end AucAdvanced.RegisterRevision("$URL: http://svn.norganna.org/auctioneer/branches/5.9/Auc-Advanced/CoreUtil.lua $", "$Rev: 4880 $")