diff --git a/AddonCore.lua b/AddonCore.lua new file mode 100644 index 0000000..a765133 --- /dev/null +++ b/AddonCore.lua @@ -0,0 +1,277 @@ +--[[------------------------------------------------------------------------- +-- AddonCore.lua +-- +-- This is a very simple, bare-minimum core for addon development. It provide +-- methods to register events, call initialization functions, and sets up the +-- localization table so it can be used elsewhere. This file is designed to be +-- loaded first, as it has no further dependencies. +-- +-- Events registered: +-- * ADDON_LOADED - Watch for saved variables to be loaded, and call the +-- 'Initialize' function in response. +-- * PLAYER_LOGIN - Call the 'Enable' method once the major UI elements +-- have been loaded and initialized. +-------------------------------------------------------------------------]]-- + +local addonName, addon = ... + +-- Set global name of addon +_G[addonName] = addon + +-- Extract version information from TOC file +addon.version = GetAddOnMetadata(addonName, "Version") +if addon.version == "@project-version" or addon.version == "wowi:version" then + addon.version = "SCM" +end + +--[[------------------------------------------------------------------------- +-- Debug support +-------------------------------------------------------------------------]]-- + +local EMERGENCY_DEBUG = false +if EMERGENCY_DEBUG then + local private = {} + for k,v in pairs(addon) do + rawset(private, k, v) + rawset(addon, k, nil) + end + + setmetatable(addon, { + __index = function(t, k) + local value = rawget(private, k) + if type(value) == "function" then + print("CALL", addonName .. "." .. tostring(k)) + end + return value + end, + __newindex = function(t, k, v) + print(addonName, "NEWINDEX", k, v) + rawset(private, k, v) + end, + }) +end + +--[[------------------------------------------------------------------------- +-- Print/Printf support +-------------------------------------------------------------------------]]-- + +local printHeader = "|cFF33FF99%s|r: " + +function addon:Printf(msg, ...) + msg = printHeader .. msg + local success, txt = pcall(string.format, msg, addonName, ...) + if success then + print(txt) + else + error(string.gsub(txt, "'%?'", string.format("'%s'", "Printf")), 3) + end +end + +--[[------------------------------------------------------------------------- +-- Event registration and dispatch +-------------------------------------------------------------------------]]-- + +addon.eventFrame = CreateFrame("Frame", addonName .. "EventFrame", UIParent) +local eventMap = {} + +function addon:RegisterEvent(event, handler) + assert(eventMap[event] == nil, "Attempt to re-register event: " .. tostring(event)) + eventMap[event] = handler and handler or event + addon.eventFrame:RegisterEvent(event) +end + +function addon:UnregisterEvent(event) + assert(type(event) == "string", "Invalid argument to 'UnregisterEvent'") + eventMap[event] = nil + addon.eventFrame:UnregisterEvent(event) +end + +addon.eventFrame:SetScript("OnEvent", function(frame, event, ...) + local handler = eventMap[event] + local handler_t = type(handler) + if handler_t == "function" then + handler(event, ...) + elseif handler_t == "string" and addon[handler] then + addon[handler](addon, event, ...) + end +end) + +--[[------------------------------------------------------------------------- +-- Message support +-------------------------------------------------------------------------]]-- + +local messageMap = {} + +function addon:RegisterMessage(name, handler) + assert(messageMap[name] == nil, "Attempt to re-register message: " .. tostring(name)) + messageMap[name] = handler and handler or name +end + +function addon:UnregisterMessage(name) + assert(type(event) == "string", "Invalid argument to 'UnregisterMessage'") + messageMap[name] = nil +end + +function addon:FireMessage(name, ...) + assert(type(name) == "string", "Invalid argument to 'FireMessage'") + local handler = messageMap[name] + local handler_t = type(handler) + if handler_t == "function" then + handler(name, ...) + elseif handler_t == "string" and addon[handler] then + addon[handler](addon, event, ...) + end +end + +--[[------------------------------------------------------------------------- +-- Setup Initialize/Enable support +-------------------------------------------------------------------------]]-- + +addon:RegisterEvent("PLAYER_LOGIN", "Enable") +addon:RegisterEvent("ADDON_LOADED", function(event, ...) + if ... == addonName then + addon:UnregisterEvent("ADDON_LOADED") + if type(addon["Initialize"]) == "function" then + addon["Initialize"](addon) + end + + -- If this addon was loaded-on-demand, trigger 'Enable' as well + if IsLoggedIn() and type(addon["Enable"]) == "function" then + addon["Enable"](addon) + end + end +end) + +--[[------------------------------------------------------------------------- +-- Support for deferred execution (when in-combat) +-------------------------------------------------------------------------]]-- + +local deferframe = CreateFrame("Frame") +deferframe.queue = {} + +local function runDeferred(thing) + local thing_t = type(thing) + if thing_t == "string" and addon[thing] then + addon[thing](addon) + elseif thing_t == "function" then + thing(addon) + end +end + +-- This method will defer the execution of a method or function until the +-- player has exited combat. If they are already out of combat, it will +-- execute the function immediately. +function addon:Defer(...) + for i = 1, select("#", ...) do + local thing = select(i, ...) + local thing_t = type(thing) + if thing_t == "string" or thing_t == "function" then + if InCombatLockdown() then + deferframe.queue[#deferframe.queue + 1] = select(i, ...) + else + runDeferred(thing) + end + else + error("Invalid object passed to 'Defer'") + end + end +end + +deferframe:RegisterEvent("PLAYER_REGEN_ENABLED") +deferframe:SetScript("OnEvent", function(self, event, ...) + for idx, thing in ipairs(deferframe.queue) do + runDeferred(thing) + end + table.wipe(deferframe.queue) +end) + +--[[------------------------------------------------------------------------- +-- Localization +-------------------------------------------------------------------------]]-- + +addon.L = addon.L or setmetatable({}, { + __index = function(t, k) + rawset(t, k, k) + return k + end, + __newindex = function(t, k, v) + if v == true then + rawset(t, k, k) + else + rawset(t, k, v) + end + end, +}) + +function addon:RegisterLocale(locale, tbl) + if locale == "enUS" or locale == GetLocale() then + for k,v in pairs(tbl) do + if v == true then + self.L[k] = k + elseif type(v) == "string" then + self.L[k] = v + else + self.L[k] = k + end + end + end +end + +--[[------------------------------------------------------------------------- +-- Addon 'About' Dialog for Interface Options +-- +-- Some of this code was taken from/inspired by tekKonfigAboutPanel +-------------------------------------------------------------------------]]-- + +local about = CreateFrame("Frame", addonName .. "AboutPanel", InterfaceOptionsFramePanelContainer) +about.name = addonName +about:Hide() + +function about.OnShow(frame) + local fields = {"Version", "Author", "X-Category", "X-License", "X-Email", "X-Website", "X-Credits"} + local notes = GetAddOnMetadata(addonName, "Notes") + + local title = frame:CreateFontString(nil, "ARTWORK", "GameFontNormalLarge") + + title:SetPoint("TOPLEFT", 16, -16) + title:SetText(addonName) + + local subtitle = frame:CreateFontString(nil, "ARTWORK", "GameFontHighlightSmall") + subtitle:SetHeight(32) + subtitle:SetPoint("TOPLEFT", title, "BOTTOMLEFT", 0, -8) + subtitle:SetPoint("RIGHT", about, -32, 0) + subtitle:SetNonSpaceWrap(true) + subtitle:SetJustifyH("LEFT") + subtitle:SetJustifyV("TOP") + subtitle:SetText(notes) + + local anchor + for _,field in pairs(fields) do + local val = GetAddOnMetadata(addonName, field) + if val then + local title = frame:CreateFontString(nil, "ARTWORK", "GameFontNormalSmall") + title:SetWidth(75) + if not anchor then title:SetPoint("TOPLEFT", subtitle, "BOTTOMLEFT", -2, -8) + else title:SetPoint("TOPLEFT", anchor, "BOTTOMLEFT", 0, -6) end + title:SetJustifyH("RIGHT") + title:SetText(field:gsub("X%-", "")) + + local detail = frame:CreateFontString(nil, "ARTWORK", "GameFontHighlightSmall") + detail:SetPoint("LEFT", title, "RIGHT", 4, 0) + detail:SetPoint("RIGHT", -16, 0) + detail:SetJustifyH("LEFT") + detail:SetText(val) + + anchor = title + end + end + + -- Clear the OnShow so it only happens once + frame:SetScript("OnShow", nil) +end + +addon.optpanels = addon.optpanels or {} +addon.optpanels.ABOUT = about + +about:SetScript("OnShow", about.OnShow) +InterfaceOptions_AddCategory(about) diff --git a/TomTom.lua b/TomTom.lua index 177f6c0..2d7c8e6 100755 --- a/TomTom.lua +++ b/TomTom.lua @@ -10,32 +10,8 @@ local ldb = LibStub("LibDataBroker-1.1") local astrolabe = DongleStub("TTAstrolabe-1.0") local lmd = LibStub("LibMapData-1.0") --- Create the addon object -TomTom = { - events = {}, - eventFrame = CreateFrame("Frame"), - RegisterEvent = function(self, event, method) - self.eventFrame:RegisterEvent(event) - self.events[event] = method or event - end, - UnregisterEvent = function(self, event) - self.eventFrame:UnregisterEvent(event) - self.events[event] = nil - end, - version = GetAddOnMetadata("TomTom", "Version") -} - -if TomTom.version == "wowi:revision" then TomTom.version = "SVN" end -if TomTom.version == "@project-version@" then TomTom.version = "SCM" end - -TomTom.eventFrame:SetScript("OnEvent", function(self, event, ...) - local method = TomTom.events[event] - if method and TomTom[method] then - TomTom[method](TomTom, event, ...) - end -end) - -TomTom:RegisterEvent("ADDON_LOADED") +local addonName, addon = ... +local TomTom = addon -- Local definitions local GetCurrentCursorPosition @@ -47,158 +23,155 @@ local RoundCoords local waypoints = {} -function TomTom:ADDON_LOADED(event, addon) - if addon == "TomTom" then - self:UnregisterEvent("ADDON_LOADED") - self.defaults = { - profile = { - general = { - confirmremoveall = true, - announce = false, - corpse_arrow = true, - }, - block = { - enable = true, - accuracy = 2, - bordercolor = {1, 0.8, 0, 0.8}, - bgcolor = {0, 0, 0, 0.4}, - lock = false, - height = 30, - width = 100, - fontsize = 12, - throttle = 0.2, - }, - mapcoords = { - playerenable = true, - playeraccuracy = 2, - cursorenable = true, - cursoraccuracy = 2, - }, - arrow = { - enable = true, - goodcolor = {0, 1, 0}, - badcolor = {1, 0, 0}, - middlecolor = {1, 1, 0}, - arrival = 15, - lock = false, - noclick = false, - showtta = true, - autoqueue = true, - menu = true, - scale = 1.0, - alpha = 1.0, - title_width = 0, - title_height = 0, - title_scale = 1, - title_alpha = 1, - setclosest = true, - enablePing = false, - }, - minimap = { - enable = true, - otherzone = true, - tooltip = true, - menu = true, - }, - worldmap = { - enable = true, - tooltip = true, - otherzone = true, - clickcreate = true, - menu = true, - create_modifier = "C", - }, - comm = { - enable = true, - prompt = false, - }, - persistence = { - cleardistance = 10, - savewaypoints = true, - }, - feeds = { - coords = false, - coords_throttle = 0.3, - coords_accuracy = 2, - arrow = false, - arrow_throttle = 0.1, - }, - poi = { - enable = true, - modifier = "C", - setClosest = false, - arrival = 0, - }, +function TomTom:Initialize(event, addon) + self.defaults = { + profile = { + general = { + confirmremoveall = true, + announce = false, + corpse_arrow = true, }, - } - - self.waydefaults = { - global = { - converted = { - ["*"] = {}, - }, + block = { + enable = true, + accuracy = 2, + bordercolor = {1, 0.8, 0, 0.8}, + bgcolor = {0, 0, 0, 0.4}, + lock = false, + height = 30, + width = 100, + fontsize = 12, + throttle = 0.2, + }, + mapcoords = { + playerenable = true, + playeraccuracy = 2, + cursorenable = true, + cursoraccuracy = 2, + }, + arrow = { + enable = true, + goodcolor = {0, 1, 0}, + badcolor = {1, 0, 0}, + middlecolor = {1, 1, 0}, + arrival = 15, + lock = false, + noclick = false, + showtta = true, + autoqueue = true, + menu = true, + scale = 1.0, + alpha = 1.0, + title_width = 0, + title_height = 0, + title_scale = 1, + title_alpha = 1, + setclosest = true, + enablePing = false, + }, + minimap = { + enable = true, + otherzone = true, + tooltip = true, + menu = true, + }, + worldmap = { + enable = true, + tooltip = true, + otherzone = true, + clickcreate = true, + menu = true, + create_modifier = "C", + }, + comm = { + enable = true, + prompt = false, + }, + persistence = { + cleardistance = 10, + savewaypoints = true, + }, + feeds = { + coords = false, + coords_throttle = 0.3, + coords_accuracy = 2, + arrow = false, + arrow_throttle = 0.1, }, - profile = { + poi = { + enable = true, + modifier = "C", + setClosest = false, + arrival = 0, + }, + }, + } + + self.waydefaults = { + global = { + converted = { ["*"] = {}, }, - } + }, + profile = { + ["*"] = {}, + }, + } - self.db = LibStub("AceDB-3.0"):New("TomTomDB", self.defaults, "Default") - self.waydb = LibStub("AceDB-3.0"):New("TomTomWaypointsMF", self.waydefaults) + self.db = LibStub("AceDB-3.0"):New("TomTomDB", self.defaults, "Default") + self.waydb = LibStub("AceDB-3.0"):New("TomTomWaypointsMF", self.waydefaults) - self.db.RegisterCallback(self, "OnProfileChanged", "ReloadOptions") - self.db.RegisterCallback(self, "OnProfileCopied", "ReloadOptions") - self.db.RegisterCallback(self, "OnProfileReset", "ReloadOptions") - self.waydb.RegisterCallback(self, "OnProfileChanged", "ReloadWaypoints") - self.waydb.RegisterCallback(self, "OnProfileCopied", "ReloadWaypoints") - self.waydb.RegisterCallback(self, "OnProfileReset", "ReloadWaypoints") + self.db.RegisterCallback(self, "OnProfileChanged", "ReloadOptions") + self.db.RegisterCallback(self, "OnProfileCopied", "ReloadOptions") + self.db.RegisterCallback(self, "OnProfileReset", "ReloadOptions") + self.waydb.RegisterCallback(self, "OnProfileChanged", "ReloadWaypoints") + self.waydb.RegisterCallback(self, "OnProfileCopied", "ReloadWaypoints") + self.waydb.RegisterCallback(self, "OnProfileReset", "ReloadWaypoints") - self.tooltip = CreateFrame("GameTooltip", "TomTomTooltip", nil, "GameTooltipTemplate") - self.tooltip:SetFrameStrata("DIALOG") + self.tooltip = CreateFrame("GameTooltip", "TomTomTooltip", nil, "GameTooltipTemplate") + self.tooltip:SetFrameStrata("DIALOG") - self.dropdown = CreateFrame("Frame", "TomTomDropdown", nil, "UIDropDownMenuTemplate") + self.dropdown = CreateFrame("Frame", "TomTomDropdown", nil, "UIDropDownMenuTemplate") - -- Both the waypoints and waypointprofile tables are going to contain subtables for each - -- of the mapids that might exist. Under these will be a hash of key/waypoint pairs consisting - -- of the waypoints for the given map file. - self.waypoints = waypoints - self.waypointprofile = self.waydb.profile + -- Both the waypoints and waypointprofile tables are going to contain subtables for each + -- of the mapids that might exist. Under these will be a hash of key/waypoint pairs consisting + -- of the waypoints for the given map file. + self.waypoints = waypoints + self.waypointprofile = self.waydb.profile - self:RegisterEvent("PLAYER_LEAVING_WORLD") - self:RegisterEvent("CHAT_MSG_ADDON") + self:RegisterEvent("PLAYER_LEAVING_WORLD") + self:RegisterEvent("CHAT_MSG_ADDON") - self:ReloadOptions() - self:ReloadWaypoints() + self:ReloadOptions() + self:ReloadWaypoints() - if self.db.profile.feeds.coords then - -- Create a data feed for coordinates - local feed_coords = ldb:NewDataObject("TomTom_Coords", { - type = "data source", - icon = "Interface\\Icons\\INV_Misc_Map_01", - text = "", - }) + if self.db.profile.feeds.coords then + -- Create a data feed for coordinates + local feed_coords = ldb:NewDataObject("TomTom_Coords", { + type = "data source", + icon = "Interface\\Icons\\INV_Misc_Map_01", + text = "", + }) - local coordFeedFrame = CreateFrame("Frame") - local throttle, counter = self.db.profile.feeds.coords_throttle, 0 - function TomTom:_privateupdatecoordthrottle(x) - throttle = x - end + local coordFeedFrame = CreateFrame("Frame") + local throttle, counter = self.db.profile.feeds.coords_throttle, 0 + function TomTom:_privateupdatecoordthrottle(x) + throttle = x + end - coordFeedFrame:SetScript("OnUpdate", function(self, elapsed) - counter = counter + elapsed - if counter < throttle then - return - end + coordFeedFrame:SetScript("OnUpdate", function(self, elapsed) + counter = counter + elapsed + if counter < throttle then + return + end - counter = 0 - local m, f, x, y = TomTom:GetCurrentPlayerPosition() + counter = 0 + local m, f, x, y = TomTom:GetCurrentPlayerPosition() - if x and y then - local opt = TomTom.db.profile.feeds - feed_coords.text = string.format("%s", RoundCoords(x, y, opt.coords_accuracy)) - end - end) - end + if x and y then + local opt = TomTom.db.profile.feeds + feed_coords.text = string.format("%s", RoundCoords(x, y, opt.coords_accuracy)) + end + end) end end diff --git a/TomTom.toc b/TomTom.toc index b1f49f7..137bdbf 100755 --- a/TomTom.toc +++ b/TomTom.toc @@ -23,6 +23,8 @@ libs\AceDBOptions-3.0\AceDBOptions-3.0.xml libs\LibDataBroker-1.1\LibDataBroker-1.1.lua libs\LibMapData-1.0\library.lua +AddonCore.lua + Localization.enUS.lua Localization.deDE.lua Localization.zhCN.lua diff --git a/TomTom_Config.lua b/TomTom_Config.lua index 892005e..8dbabc7 100644 --- a/TomTom_Config.lua +++ b/TomTom_Config.lua @@ -720,19 +720,6 @@ local options local function createBlizzOptions() options = createconfig() - config:RegisterOptionsTable("TomTom-Bliz", { - name = L["TomTom"], - type = "group", - args = { - help = { - type = "description", - name = "TomTom is a simple navigation assistant", - }, - }, - }) - dialog:SetDefaultSize("TomTom-Bliz", 600, 400) - dialog:AddToBlizOptions("TomTom-Bliz", "TomTom") - -- General Options config:RegisterOptionsTable("TomTom-General", options.args.general) local blizzPanel = dialog:AddToBlizOptions("TomTom-General", options.args.general.name, "TomTom")