bkader

KTracker.lua

Apr 21st, 2020 (edited)
1,324
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 134.90 KB | None | 0 0
  1. local addonName, addon = ...
  2. local L = addon.L
  3. local utils = addon.utils
  4.  
  5. local _G = _G
  6. _G["KTracker"] = addon
  7.  
  8. --
  9. -- default addon options
  10. --
  11. local def = {
  12.     -- maximum allowed groups, columns and rows
  13.     maxGroups = 64,
  14.     maxColumns = 8,
  15.     maxRows = 7,
  16.     -- account DB template
  17.     DB = {sync = true, groups = {}},
  18.     -- character DB template
  19.     CharDB = {enabled = true, locked = false, groups = {}},
  20.     -- default group template
  21.     group = {
  22.         enabled = true,
  23.         name = "",
  24.         spec = 0,
  25.         columns = 4,
  26.         rows = 1,
  27.         hspacing = 0,
  28.         vspacing = 0,
  29.         scale = 2,
  30.         combat = false,
  31.         created = 0,
  32.         icons = {},
  33.         position = {},
  34.         style = {}
  35.     },
  36.     -- default icon template
  37.     icon = {
  38.         enabled = false,
  39.         name = "",
  40.         type = "",
  41.         subtype = "",
  42.         when = 1,
  43.         unit = "player",
  44.         mine = false,
  45.         timer = false,
  46.         filepath = nil,
  47.         effect = "none"
  48.     },
  49.     -- group parts, account's and character's.
  50.     DBGroup = {
  51.         name = "",
  52.         columns = 4,
  53.         rows = 1,
  54.         created = 0,
  55.         icons = {}
  56.     },
  57.     CharDBGroup = {
  58.         enabled = false,
  59.         hspacing = 0,
  60.         vspacing = 0,
  61.         scale = 2,
  62.         spec = 0,
  63.         combat = false,
  64.         position = {},
  65.         style = {}
  66.     }
  67. }
  68.  
  69. --
  70. -- SavedVariables
  71. --
  72. KTrackerDB = {}
  73. KTrackerCharDB = {}
  74.  
  75. --
  76. -- simple title holder
  77. --
  78. local titleString = "|cfff58cbaKader|r|caaf49141Tracker|r"
  79.  
  80. --
  81. -- whether we're using and cooldown addon
  82. --
  83. local hasOmniCC, hasElvUI
  84.  
  85. --
  86. -- addon synchronization
  87. --
  88. local syncPrefix = "KaderTracker"
  89. local syncHandlers = {}
  90.  
  91. -- placeholders
  92. local holderGroup = "KTrackerGroup%d"
  93. local holderIcon = "KTrackerGroup%dIcon%d"
  94.  
  95. --
  96. -- textures to be used
  97. --
  98. local textures = {
  99.     "Interface\\Icons\\INV_Misc_QuestionMark",
  100.     "Interface\\Icons\\INV_Misc_PocketWatch_01"
  101. }
  102.  
  103. local Group, Icon = {}, {}
  104. local groups, numGroups = {}, 0
  105. local minimapButton
  106.  
  107. local Icon_EffectTrigger, Icon_EffectReset
  108.  
  109. --
  110. -- cache some globals
  111. --
  112. local tinsert, tremove = _G.table.insert, _G.table.remove
  113. local pairs, ipairs = _G.pairs, _G.ipairs
  114. local type, select = _G.type, _G.select
  115. local find, format, gsub = _G.string.find, _G.string.format, _G.string.gsub
  116. local tostring, tonumber = _G.tostring, _G.tonumber
  117. local time, GetTime = _G.time, _G.GetTime
  118. local GetBuildInfo = _G.GetBuildInfo
  119.  
  120. local IsAddOnLoaded, CreateFrame = _G.IsAddOnLoaded, _G.CreateFrame
  121. local wipe = _G.wipe
  122.  
  123. local GetInventorySlotInfo = _G.GetInventorySlotInfo
  124. local GetInventoryItemTexture = _G.GetInventoryItemTexture
  125.  
  126. local GetWeaponEnchantInfo = _G.GetWeaponEnchantInfo
  127. local GetTotemInfo = _G.GetTotemInfo
  128. local GetCursorPosition = _G.GetCursorPosition
  129. local IsSpellInRange, IsUsableSpell = _G.IsSpellInRange, _G.IsUsableSpell
  130.  
  131. local UnitName, UnitClass, UnitGUID = _G.UnitName, _G.UnitClass, _G.UnitGUID
  132. local UnitExists, UnitIsDead = _G.UnitExists, _G.UnitIsDead
  133. local UnitAura, UnitReaction = _G.UnitAura, _G.UnitReaction
  134. local UnitBuff, UnitDebuff = _G.UnitBuff, _G.UnitDebuff
  135. local unitName = UnitName("player")
  136.  
  137. local GetSpellInfo = _G.GetSpellInfo
  138. local GetSpellTexture = _G.GetSpellTexture
  139. local GetSpellCooldown = _G.GetSpellCooldown
  140.  
  141. local GetItemInfo = _G.GetItemInfo
  142. local GetItemCooldown = _G.GetItemCooldown
  143.  
  144. -- external libraries
  145. local LBF = LibStub:GetLibrary("LibButtonFacade", true)
  146. local LiCD = LibStub:GetLibrary("LibInternalCooldowns", true)
  147.  
  148. -- we override GetItemCooldown and use LibInternalCooldowns one:
  149. if LiCD and LiCD.GetItemCooldown then
  150.     GetItemCooldown = function(...)
  151.         return LiCD:GetItemCooldown(...)
  152.     end
  153. end
  154.  
  155. --------------------------------------------------------------------------
  156. -- AddOn initialization
  157.  
  158. local mainFrame, LoadDatabase = CreateFrame("Frame", "KTracker_EventFrame")
  159. do
  160.     --
  161.     -- makes sure to properly setup or load database
  162.     --
  163.     function LoadDatabase()
  164.         -- we fill the account's DB if empty.
  165.         if utils.isEmpty(KTrackerDB) then
  166.             utils.fillTable(KTrackerDB, def.DB)
  167.         end
  168.  
  169.         -- we fill the character's DB if empty.
  170.         if utils.isEmpty(KTrackerCharDB) then
  171.             utils.fillTable(KTrackerCharDB, def.CharDB)
  172.         end
  173.  
  174.         -- keep reference of addon enable and lock statuses
  175.         addon.sync = KTrackerDB.sync
  176.         addon.enabled = KTrackerCharDB.enabled
  177.         addon.locked = KTrackerCharDB.locked
  178.  
  179.         -- minimap button
  180.         if KTrackerCharDB.minimap == nil then
  181.             KTrackerCharDB.minimap = true
  182.         end
  183.         addon.minimap = KTrackerCharDB.minimap
  184.  
  185.         if not addon.minimap then
  186.             addon:HideMinimapButton()
  187.         end
  188.  
  189.         -- this step is crucial. If the account has not groups
  190.         -- or all groups were deleted we make sure to create
  191.         -- the default group.
  192.         if utils.isEmpty(KTrackerDB.groups) then
  193.             local group = utils.deepCopy(def.group)
  194.             group.name = L["Default Group"]
  195.             group.enabled = true
  196.             Group:Save(group)
  197.         end
  198.  
  199.         -- check if the character has all groups added to his/her table
  200.         Group:Check()
  201.     end
  202.  
  203.     --
  204.     -- addon's slash command handler
  205.     --
  206.     local function SlashCommandHandler(cmd)
  207.         if cmd == "config" or cmd == "options" then
  208.             addon:Config()
  209.         elseif cmd == "reset" then
  210.             StaticPopup_Show("KTRACKER_DIALOG_RESET")
  211.         else
  212.             addon:Toggle()
  213.         end
  214.         L_CloseDropDownMenus() -- always close them.
  215.     end
  216.  
  217.     --
  218.     -- handles main frame events
  219.     --
  220.     local function EventHandler(self, event, ...)
  221.         -- on ADDON_LOADED event.
  222.         if event == "ADDON_LOADED" then
  223.             local name = ...
  224.             if name:upper() == addonName:upper() then
  225.                 mainFrame:UnregisterEvent("ADDON_LOADED")
  226.                 mainFrame:RegisterEvent("PLAYER_LOGIN")
  227.                 mainFrame:RegisterEvent("PLAYER_ENTERING_WORLD")
  228.                 mainFrame:RegisterEvent("CHAT_MSG_ADDON")
  229.  
  230.                 LoadDatabase()
  231.  
  232.                 SlashCmdList["KTRACKER"] = SlashCommandHandler
  233.                 SLASH_KTRACKER1, SLASH_KTRACKER2 = "/ktracker", "/kt"
  234.  
  235.                 -- ButtonFacade calllback
  236.                 if LBF then
  237.                     LBF:RegisterSkinCallback(addonName, addon.OnSkin, addon)
  238.                 end
  239.  
  240.                 addon:Print(L["addon loaded"])
  241.             end
  242.         elseif event == "PLAYER_LOGIN" then
  243.             mainFrame:RegisterEvent("PLAYER_TALENT_UPDATE")
  244.  
  245.             -- using a cooldown count?
  246.             if OmniCC or CooldownCount or YarkoCooldowns or ElvUI then
  247.                 hasOmniCC = true
  248.             end
  249.  
  250.             -- using ElvUI?
  251.             if ElvUI then
  252.                 hasElvUI = true
  253.             end
  254.  
  255.             addon:Initialize(true)
  256.         elseif event == "PLAYER_ENTERING_WORLD" then
  257.             mainFrame:RegisterEvent("PLAYER_TALENT_UPDATE")
  258.             addon:Initialize()
  259.         elseif event == "PLAYER_TALENT_UPDATE" then
  260.             -- addon messages
  261.             addon:SetCurrentSpec()
  262.             addon:Initialize()
  263.         elseif event == "CHAT_MSG_ADDON" and addon.sync then
  264.             local prefix, msg, channel, sender = ...
  265.  
  266.             if msg and prefix == syncPrefix and sender ~= unitName then
  267.                 local handler = syncHandlers[prefix]
  268.                 if handler and type(handler) == "function" then
  269.                     handler(msg, channel, sender)
  270.                 end
  271.             end
  272.         end
  273.     end
  274.  
  275.     -- register required event and script
  276.     mainFrame:RegisterEvent("ADDON_LOADED")
  277.     mainFrame:SetScript("OnEvent", EventHandler)
  278. end
  279.  
  280. --
  281. -- called when the addon needs to be initialized
  282. --
  283. function addon:Load()
  284.     Group:Check()
  285.     wipe(groups)
  286.     for i = 1, def.maxGroups do
  287.         Group:Load(i)
  288.     end
  289.     numGroups = #groups
  290. end
  291.  
  292. --
  293. -- toggle addon's locked/unlocked status
  294. --
  295. function addon:Toggle()
  296.     PlaySound("UChatScrollButton")
  297.     StaticPopup_Hide("KTRACKER_DIALOG_RESET")
  298.     StaticPopup_Hide("KTRACKER_DIALOG_CLEAR")
  299.     StaticPopup_Hide("KTRACKER_DIALOG_NAME")
  300.     StaticPopup_Hide("KTRACKER_DIALOG_UNITNAME")
  301.     StaticPopup_Hide("KTRACKER_DIALOG_SHARE_SEND")
  302.     StaticPopup_Hide("KTRACKER_DIALOG_SHARE_RECEIVE")
  303.     L_CloseDropDownMenus()
  304.     KTrackerCharDB.locked = not KTrackerCharDB.locked
  305.     self.locked = KTrackerCharDB.locked
  306.     self:Initialize()
  307. end
  308.  
  309. --
  310. -- addon synchronization
  311. --
  312. function addon:Sync(msg, channel, target)
  313.     if self.sync then
  314.         utils.sync(syncPrefix, msg, channel, target)
  315.     end
  316. end
  317.  
  318. --
  319. -- ButtonFacade skin handler
  320. --
  321. function addon:OnSkin(skin, glossAlpha, gloss, group, _, colors)
  322.     local style
  323.  
  324.     if not utils.isEmpty(groups) then
  325.         for k, v in pairs(groups) do
  326.             if v.name == group then
  327.                 style = KTrackerCharDB.groups[v.created].style
  328.                 break
  329.             end
  330.         end
  331.     end
  332.  
  333.     if style then
  334.         style[1] = skin
  335.         style[2] = glossAlpha
  336.         style[3] = gloss
  337.         style[4] = colors
  338.     end
  339. end
  340.  
  341. --------------------------------------------------------------------------
  342. -- Groups functions
  343.  
  344. --
  345. -- this function is useful and makes sure the character has
  346. -- all groups references and options added to his/her table.
  347. --
  348. function Group:Check()
  349.     local DBGroups = KTrackerDB.groups
  350.     local CharDBGroups = KTrackerCharDB.groups
  351.  
  352.     -- hold the time we are doing the check.
  353.     local checkTime = time()
  354.  
  355.     -- list of the groups that the current character
  356.     -- doesn't have on his database.
  357.     local checked = {}
  358.  
  359.     -- first step: delete groups that were probably deleted but
  360.     -- their data accidentally remained in character's database
  361.     local safe = {}
  362.     for _, obj in ipairs(DBGroups) do
  363.         for id, _ in pairs(CharDBGroups) do
  364.             if obj.created == id then
  365.                 safe[id] = true
  366.             end
  367.         end
  368.     end
  369.     for id, _ in pairs(CharDBGroups) do
  370.         if not safe[id] then
  371.             CharDBGroups[id] = nil
  372.         end
  373.     end
  374.  
  375.     -- second step: add missing groups to character
  376.     for _, obj in ipairs(DBGroups) do
  377.         if obj.created == 0 then
  378.             obj.created = checkTime
  379.         end
  380.         if not CharDBGroups[obj.created] then
  381.             local group = checked[obj.created] or utils.deepCopy(def.CharDBGroup)
  382.             CharDBGroups[obj.created] = group
  383.         end
  384.     end
  385. end
  386.  
  387. --
  388. -- creates a new group from the def table
  389. --
  390. function Group:Save(obj, id)
  391.     -- are we updating and existing group?
  392.     if id and KTrackerDB.groups[id] then
  393.         local DB = KTrackerDB.groups[id]
  394.  
  395.         -- creation date:
  396.         obj.created = DB.created or time()
  397.  
  398.         -- check character's database
  399.         local db = KTrackerCharDB.groups[DB.created]
  400.         if not db then
  401.             KTrackerCharDB.groups[DB.created] = utils.deepCopy(def.CharDBGroup)
  402.             KTrackerCharDB.groups[DB.created].enabled = true
  403.             db = KTrackerCharDB.groups[DB.created]
  404.         end
  405.  
  406.         -- we proceed to update
  407.         for k, v in pairs(obj) do
  408.             if def.DBGroup[k] ~= nil then
  409.                 DB[k] = v -- account
  410.             end
  411.             if def.CharDBGroup[k] ~= nil then
  412.                 db[k] = v -- character
  413.             end
  414.         end
  415.  
  416.         return true
  417.     end
  418.  
  419.     -- creating a new group
  420.  
  421.     obj = obj or {}
  422.     if type(obj) == "string" then
  423.         obj = {name = obj}
  424.     end
  425.  
  426.     -- creation date:
  427.     obj.created = time()
  428.  
  429.     -- prepare account and character tables
  430.     local DB, db = utils.deepCopy(def.DBGroup), utils.deepCopy(def.CharDBGroup)
  431.     for k, v in pairs(obj) do
  432.         if def.DBGroup[k] ~= nil then
  433.             DB[k] = v -- account
  434.         end
  435.         if def.CharDBGroup[k] ~= nil then
  436.             db[k] = v -- character
  437.         end
  438.     end
  439.  
  440.     -- fill the group with required number of icons
  441.     local num = DB.columns * DB.rows
  442.     for i = 1, num do
  443.         if not DB.icons[i] then
  444.             local icon = utils.deepCopy(def.icon)
  445.             tinsert(DB.icons, i, icon)
  446.         end
  447.     end
  448.  
  449.     -- save the final results to tables.
  450.     tinsert(KTrackerDB.groups, DB)
  451.     KTrackerCharDB.groups[obj.created] = db
  452.     return #KTrackerDB.groups
  453. end
  454.  
  455. do
  456.     --
  457.     -- resize button OnMouseDown and OnMouseUp functions
  458.     --
  459.     local Sizer_OnMouseDown, Sizer_OnMouseUp
  460.  
  461.     do
  462.         --
  463.         -- handles group resizing
  464.         --
  465.         local function Sizer_OnUpdate(self)
  466.             local uiScale = UIParent:GetScale()
  467.             local f = self:GetParent()
  468.             local cursorX, cursorY = GetCursorPosition(UIParent)
  469.  
  470.             -- calculate the new scale
  471.             local newXScale =
  472.                 f.oldScale * (cursorX / uiScale - f.oldX * f.oldScale) /
  473.                 (self.oldCursorX / uiScale - f.oldX * f.oldScale)
  474.             local newYScale =
  475.                 f.oldScale * (cursorY / uiScale - f.oldY * f.oldScale) /
  476.                 (self.oldCursorY / uiScale - f.oldY * f.oldScale)
  477.             local newScale = math.max(0.6, newXScale, newYScale)
  478.             f:SetScale(newScale)
  479.  
  480.             -- calculate new frame position
  481.             local newX = f.oldX * f.oldScale / newScale
  482.             local newY = f.oldY * f.oldScale / newScale
  483.             f:SetPoint("TOPLEFT", UIParent, "BOTTOMLEFT", newX, newY)
  484.         end
  485.  
  486.         --
  487.         -- called on OnMouseDown event
  488.         --
  489.         function Sizer_OnMouseDown(self, button)
  490.             -- resize only if the addon is not locked
  491.             if addon.locked then
  492.                 return
  493.             end
  494.  
  495.             if button == "LeftButton" then
  496.                 local f = self:GetParent()
  497.                 f.oldScale = f:GetScale()
  498.                 self.oldCursorX, self.oldCursorY = GetCursorPosition(UIParent)
  499.                 f.oldX, f.oldY = f:GetLeft(), f:GetTop()
  500.                 self:SetScript("OnUpdate", Sizer_OnUpdate)
  501.             end
  502.         end
  503.  
  504.         --
  505.         -- called on OnMouseUp event
  506.         --
  507.         function Sizer_OnMouseUp(self, button)
  508.             self:SetScript("OnUpdate", nil)
  509.             if addon.locked then
  510.                 return
  511.             end
  512.  
  513.             -- Left button released? save scale
  514.             if button == "LeftButton" then
  515.                 local f = self:GetParent()
  516.                 local id = f:GetID()
  517.                 local DB = KTrackerDB.groups[id]
  518.                 local db = KTrackerCharDB.groups[DB.created]
  519.                 if DB and db then
  520.                     db.scale = f:GetScale()
  521.                     Group:Load(id)
  522.                 end
  523.             end
  524.         end
  525.     end
  526.  
  527.     do
  528.         --
  529.         -- hide all group icons before loading -- hotfix
  530.         --
  531.         local function ResetGroupIcons(id)
  532.             local i = 1
  533.             local btnName = format(holderIcon, id, i)
  534.             local btn = _G[btnName]
  535.             while btn ~= nil do
  536.                 btn:Hide()
  537.                 i = i + 1
  538.                 btnName = format(holderIcon, id, i)
  539.                 btn = _G[btnName]
  540.             end
  541.         end
  542.  
  543.         --
  544.         -- load a group and draws it into screen
  545.         --
  546.         function Group:Load(id)
  547.             -- make sure the the group exists
  548.             if not KTrackerDB.groups[id] then
  549.                 return
  550.             end
  551.             ResetGroupIcons(id)
  552.             local obj = utils.deepCopy(KTrackerDB.groups[id])
  553.  
  554.             local db = KTrackerCharDB.groups[obj.created]
  555.             if db then
  556.                 utils.mixTable(obj, db)
  557.                 if obj.spacing then -- fix old spacing
  558.                     obj.hspacing = obj.hspacing or obj.spacing
  559.                     obj.vspacing = obj.vspacing or obj.spacing
  560.                     db.spacing = nil
  561.                 end
  562.             end
  563.             utils.fillTable(obj, def.group)
  564.  
  565.             -- cache the group
  566.             if not groups[id] then
  567.                 tinsert(groups, id, obj)
  568.             end
  569.  
  570.             -- we create the group frame
  571.             local groupName = format(holderGroup, id)
  572.             local group = _G[groupName]
  573.             if not group then
  574.                 group = CreateFrame("Frame", groupName, UIParent, "KTrackerGroupTemplate")
  575.             end
  576.             group:SetID(id)
  577.  
  578.             if LBF then
  579.                 LBF:Group(addonName, obj.name):Skin(unpack(obj.style))
  580.             end
  581.  
  582.             -- set the group title
  583.             group.title = _G[groupName .. "Title"]
  584.             group.title:SetText(obj.name)
  585.  
  586.             -- hold group resize button
  587.             group.sizer = _G[groupName .. "Resizer"]
  588.             group.sizer:RegisterForClicks("AnyUp")
  589.             local sizerTexture = _G[groupName .. "ResizerTexture"]
  590.             sizerTexture:SetVertexColor(0.6, 0.6, 0.6)
  591.  
  592.             if addon.locked then
  593.                 local spec = addon:GetCurrentSpec()
  594.                 if obj.spec > 0 and obj.spec ~= spec then
  595.                     obj.enabled = false
  596.                 end
  597.  
  598.                 group.title:Hide()
  599.                 group.sizer:Hide()
  600.             else
  601.                 group.title:Show()
  602.                 group.sizer:Show()
  603.  
  604.                 -- set resize button tooltip and scripts.
  605.                 utils.setTooltip(group.sizer, L["Click and drag to change size."], nil, L["Resize"])
  606.                 group.sizer:SetScript("OnMouseDown", Sizer_OnMouseDown)
  607.                 group.sizer:SetScript("OnMouseUp", Sizer_OnMouseUp)
  608.             end
  609.  
  610.             if obj.enabled then
  611.                 -- group's width and height
  612.                 local width, height = 36, 36
  613.  
  614.                 -- draw icons
  615.                 for r = 1, obj.rows do
  616.                     for c = 1, obj.columns do
  617.                         local i = (r - 1) * obj.columns + c
  618.                         local iconName = format(holderIcon, id, i)
  619.                         local icon = _G[iconName] or CreateFrame("Button", iconName, group, "KTrackerIconTemplate")
  620.                         icon:SetID(i)
  621.  
  622.                         if c > 1 then
  623.                             icon:SetPoint("TOPLEFT", _G[groupName .. "Icon" .. (i - 1)], "TOPRIGHT", obj.hspacing, 0)
  624.  
  625.                             -- we set the group width from the first row only.
  626.                             if r == 1 and c <= obj.columns then
  627.                                 width = width + 36 + obj.hspacing
  628.                             end
  629.                         elseif r > 1 and c == 1 then
  630.                             icon:SetPoint(
  631.                                 "TOPLEFT",
  632.                                 _G[groupName .. "Icon" .. (i - obj.columns)],
  633.                                 "BOTTOMLEFT",
  634.                                 0,
  635.                                 -obj.vspacing
  636.                             )
  637.  
  638.                             height = height + obj.vspacing + 36 -- increment the height
  639.                         elseif i == 1 then
  640.                             icon:SetPoint("TOPLEFT", group, "TOPLEFT")
  641.                         end
  642.  
  643.                         -- we update the icon now
  644.                         if not obj.enabled then
  645.                             Icon:ClearScripts(icon)
  646.                         end
  647.                         -- add the name of the icon
  648.                         icon.fname = iconName
  649.                         Icon:Load(icon, id, i)
  650.  
  651.                         if LBF then
  652.                             LBF:Group(addonName, obj.name):AddButton(icon)
  653.                         else
  654.                             _G[iconName .. "Icon"]:SetSize(36, 36)
  655.                             icon:SetNormalTexture(nil)
  656.                             icon.texture:SetTexCoord(0.07, 0.93, 0.07, 0.93)
  657.                             icon:SetBackdrop(
  658.                                 {
  659.                                     bgFile = "Interface\\DialogFrame\\UI-DialogBox-Background",
  660.                                     edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
  661.                                     tile = true,
  662.                                     tileSize = 2,
  663.                                     edgeSize = 4,
  664.                                     insets = {left = 0, right = 0, top = 0, bottom = 0}
  665.                                 }
  666.                             )
  667.                         end
  668.                     end
  669.                 end
  670.  
  671.                 -- we make sure to change group size in order to be fully
  672.                 -- clamped to screen, then we set its scale.
  673.                 group:SetSize(width, height)
  674.                 group:SetScale(obj.scale)
  675.  
  676.                 -- we position the group only if it was moved
  677.                 if not utils.isEmpty(obj.position) then
  678.                     group:ClearAllPoints()
  679.                     group:SetPoint(
  680.                         obj.position.point or "CENTER",
  681.                         obj.position.relativeTo or UIParent,
  682.                         obj.position.relativePoint or "CENTER",
  683.                         obj.position.xOfs or 0,
  684.                         obj.position.yOfs or 0
  685.                     )
  686.                 end
  687.             end
  688.  
  689.             -- register/unregister group events
  690.             if obj.combat and obj.enabled and addon.locked then
  691.                 group:RegisterEvent("PLAYER_REGEN_ENABLED")
  692.                 group:RegisterEvent("PLAYER_REGEN_DISABLED")
  693.                 group:SetScript(
  694.                     "OnEvent",
  695.                     function(self, event)
  696.                         if event == "PLAYER_REGEN_ENABLED" then
  697.                             self:Hide()
  698.                         elseif event == "PLAYER_REGEN_DISABLED" then
  699.                             self:Show()
  700.                         end
  701.                     end
  702.                 )
  703.                 group:Hide()
  704.             else
  705.                 group:UnregisterEvent("PLAYER_REGEN_ENABLED")
  706.                 group:UnregisterEvent("PLAYER_REGEN_DISABLED")
  707.                 group:SetScript("OnEvent", nil)
  708.                 utils.showHide(group, obj.enabled)
  709.             end
  710.         end
  711.     end
  712. end
  713.  
  714. --------------------------------------------------------------------------
  715. -- Icons functions
  716.  
  717. do
  718.     --
  719.     -- current selected icon and menu
  720.     --
  721.     local current, menu = {}
  722.  
  723.     --
  724.     -- icon general, reactive and aura checkers
  725.     ---
  726.     local Icon_ReactiveCheck
  727.  
  728.     --
  729.     -- opens the menu for the current icon
  730.     --
  731.     local Icon_OpenMenu
  732.     do
  733.         --
  734.         -- icon menu list
  735.         --
  736.         local menuList, menu = {
  737.             -- icon type --
  738.             IconType = {
  739.                 {text = L["Cooldown"], arg1 = "type", arg2 = "cooldown", value = "spell"},
  740.                 {text = L["Buff or Debuff"], arg1 = "type", arg2 = "aura", value = "HELPFUL"},
  741.                 {text = L["Reactive spell or ability"], arg1 = "type", arg2 = "reactive", value = "spell"},
  742.                 {text = L["Temporary weapon enchant"], arg1 = "type", arg2 = "wpnenchant", value = "mainhand"},
  743.                 {text = L["Totem/non-MoG Ghoul"], arg1 = "type", arg2 = "totem", value = ""}
  744.             },
  745.             SpellType = {
  746.                 {text = L["Spell"], arg1 = "subtype", arg2 = "spell"},
  747.                 {text = L["Item"], arg1 = "subtype", arg2 = "item"},
  748.                 {text = L["Talent"], arg1 = "subtype", arg2 = "talent"}
  749.             },
  750.             AuraType = {
  751.                 {text = L["Buff"], arg1 = "subtype", arg2 = "HELPFUL"},
  752.                 {text = L["Debuff"], arg1 = "subtype", arg2 = "HARMFUL"}
  753.             },
  754.             WpnEnchantType = {
  755.                 {text = L["Main Hand"], arg1 = "subtype", arg2 = "mainhand"},
  756.                 {text = L["Off-Hand"], arg1 = "subtype", arg2 = "offhand"}
  757.             },
  758.             SpellWhen = {
  759.                 {text = L["Usable"], arg1 = "when", arg2 = 1},
  760.                 {text = L["Unusable"], arg1 = "when", arg2 = -1},
  761.                 {text = L["Always"], arg1 = "when", arg2 = 0}
  762.             },
  763.             TalentWhen = {
  764.                 {text = L["Off Cooldown"], arg1 = "when", arg2 = 1},
  765.                 {text = L["On Cooldown"], arg1 = "when", arg2 = -1},
  766.                 {text = L["Always"], arg1 = "when", arg2 = 0}
  767.             },
  768.             AuraWhen = {
  769.                 {text = L["Present"], arg1 = "when", arg2 = 1},
  770.                 {text = L["Absent"], arg1 = "when", arg2 = -1},
  771.                 {text = L["Always"], arg1 = "when", arg2 = 0}
  772.             },
  773.             Unit = {
  774.                 {text = L["Player"], arg1 = "unit", arg2 = "player"},
  775.                 {text = L["Target"], arg1 = "unit", arg2 = "target"},
  776.                 {text = L["Target's Target"], arg1 = "unit", arg2 = "targettarget"},
  777.                 {text = L["Focus"], arg1 = "unit", arg2 = "focus"},
  778.                 {text = L["Focus Target"], arg1 = "unit", arg2 = "focustarget"},
  779.                 {text = L["Pet"], arg1 = "unit", arg2 = "pet"},
  780.                 {text = L["Pet's Target"], arg1 = "unit", arg2 = "pettarget"},
  781.                 {disabled = true},
  782.                 {text = L["Party Unit"], hasArrow = true, value = "UnitParty"},
  783.                 {text = L["Arena Unit"], hasArrow = true, value = "UnitArena"},
  784.                 {disabled = true}
  785.             },
  786.             UnitParty = {
  787.                 {text = L:F("Party %d", 1), arg1 = "unit", arg2 = "party1"},
  788.                 {text = L:F("Party %d", 2), arg1 = "unit", arg2 = "party2"},
  789.                 {text = L:F("Party %d", 3), arg1 = "unit", arg2 = "party3"},
  790.                 {text = L:F("Party %d", 4), arg1 = "unit", arg2 = "party4"}
  791.             },
  792.             UnitArena = {
  793.                 {text = L:F("Arena %d", 1), arg1 = "unit", arg2 = "arena1"},
  794.                 {text = L:F("Arena %d", 2), arg1 = "unit", arg2 = "arena2"},
  795.                 {text = L:F("Arena %d", 3), arg1 = "unit", arg2 = "arena3"},
  796.                 {text = L:F("Arena %d", 4), arg1 = "unit", arg2 = "arena4"},
  797.                 {text = L:F("Arena %d", 5), arg1 = "unit", arg2 = "arena5"}
  798.             }
  799.         }
  800.  
  801.         --
  802.         -- used for true and false values
  803.         --
  804.         local function Icon_OptionToggle()
  805.             local g, i = current.group, current.icon
  806.             local obj = KTrackerDB.groups[g].icons[i]
  807.             if obj and obj[this.value] ~= nil then
  808.                 KTrackerDB.groups[g].icons[i][this.value] = this.checked
  809.                 local iconName = format(holderIcon, g, i)
  810.                 Icon:Load(_G[iconName], g, i)
  811.             end
  812.         end
  813.  
  814.         --
  815.         -- used to set strings and numbers
  816.         --
  817.         local function Icon_OptionChoose(self, arg1, arg2)
  818.             local g, i = current.group, current.icon
  819.             local obj = KTrackerDB.groups[g].icons[i]
  820.             -- double check the icon
  821.             if obj and obj[arg1] ~= nil then
  822.                 KTrackerDB.groups[g].icons[i][arg1] = arg2
  823.                 if arg1 == "type" then
  824.                     KTrackerDB.groups[g].icons[i].filepath = nil
  825.                     if
  826.                         this.value == "spell" or this.value == "talent" or this.value == "HELPFUL" or
  827.                             this.value == "mainhand" or
  828.                             this.value == "none"
  829.                      then
  830.                         KTrackerDB.groups[g].icons[i].subtype = this.value
  831.                     end
  832.                     L_CloseDropDownMenus()
  833.                 end
  834.                 local iconName = format(holderIcon, g, i)
  835.                 Icon:Load(_G[iconName], g, i)
  836.                 return
  837.             end
  838.             L_CloseDropDownMenus()
  839.         end
  840.  
  841.         --
  842.         -- clear the selected icon
  843.         --
  844.         local function Icon_OptionClear()
  845.             local i, g = current.icon, current.group
  846.             if KTrackerDB.groups[g].icons[i] then
  847.                 KTrackerDB.groups[g].icons[i] = utils.deepCopy(def.icon)
  848.                 local iconName = format(holderIcon, g, i)
  849.                 Icon:Load(_G[iconName], g, i)
  850.             end
  851.             L_CloseDropDownMenus()
  852.         end
  853.  
  854.         --
  855.         --  the main menu handler function
  856.         --
  857.         function Icon_OpenMenu(icon)
  858.             if not icon then
  859.                 return
  860.             end
  861.             local i, g = icon:GetID(), icon:GetParent():GetID()
  862.             local obj = KTrackerDB.groups[g].icons[i]
  863.             if not obj then
  864.                 return
  865.             end
  866.             current.icon, current.group = i, g
  867.  
  868.             if addon.effects and not menuList.Effects then
  869.                 menuList.Effects = {
  870.                     {
  871.                         text = L["None"],
  872.                         arg1 = "effect",
  873.                         arg2 = "none"
  874.                     }
  875.                 }
  876.                 for i, effect in ipairs(addon.effects) do
  877.                     tinsert(
  878.                         menuList.Effects,
  879.                         i + 1,
  880.                         {
  881.                             text = effect.name,
  882.                             arg1 = "effect",
  883.                             arg2 = effect.id
  884.                         }
  885.                     )
  886.                 end
  887.             end
  888.  
  889.             -- generate the menu
  890.             if not menu then
  891.                 menu = CreateFrame("Frame", "KTrackerIconMenu")
  892.             end
  893.             menu.displayMode = "MENU"
  894.             menu.initialize = function(self, level)
  895.                 local info = L_UIDropDownMenu_CreateInfo()
  896.                 level = level or 1
  897.  
  898.                 if level >= 2 then
  899.                     local tar = L_UIDROPDOWNMENU_MENU_VALUE
  900.                     local menuItems = {}
  901.                     if tar == "Unit" then
  902.                         menuItems = utils.deepCopy(menuList.Unit)
  903.                         tinsert(
  904.                             menuItems,
  905.                             {
  906.                                 text = L["Custom Unit"],
  907.                                 func = function()
  908.                                     StaticPopup_Show("KTRACKER_DIALOG_UNITNAME", nil, nil, icon)
  909.                                 end
  910.                             }
  911.                         )
  912.                     elseif menuList[tar] then
  913.                         menuItems = utils.deepCopy(menuList[tar])
  914.                     end
  915.  
  916.                     for _, v in ipairs(menuItems) do
  917.                         info = utils.deepCopy(v)
  918.                         info.checked = (v.arg2 and obj[v.arg1] == v.arg2)
  919.                         info.func = v.func and v.func or Icon_OptionChoose
  920.                         L_UIDropDownMenu_AddButton(info, level)
  921.                         wipe(info)
  922.                     end
  923.  
  924.                     return
  925.                 end
  926.  
  927.                 -- display icon's name if set
  928.                 if obj.name and obj.name ~= "" then
  929.                     local name
  930.                     if obj.name:len() >= 24 then
  931.                         name = obj.name:sub(0, 21) .. "..."
  932.                     else
  933.                         name = obj.name
  934.                     end
  935.                     info.text = name
  936.                     info.isTitle = true
  937.                     info.notCheckable = true
  938.                     L_UIDropDownMenu_AddButton(info, level)
  939.                     wipe(info)
  940.                 end
  941.  
  942.                 -- let the player choose the name if the icon
  943.                 -- type is not set to weapon enchant.
  944.                 if obj.type ~= "wpnenchant" then
  945.                     info.text = L["Choose Name"]
  946.                     info.notCheckable = true
  947.                     info.func = function()
  948.                         StaticPopup_Show("KTRACKER_DIALOG_NAME")
  949.                     end
  950.                     L_UIDropDownMenu_AddButton(info, level)
  951.                     wipe(info)
  952.                 end
  953.  
  954.                 -- toggle icon enable status
  955.                 info.text = L["Enabled"]
  956.                 info.value = "enabled"
  957.                 info.checked = obj.enabled
  958.                 info.isNotRadio = true
  959.                 info.func = Icon_OptionToggle
  960.                 info.keepShownOnClick = true
  961.                 L_UIDropDownMenu_AddButton(info, level)
  962.                 wipe(info)
  963.  
  964.                 -- icon type selection
  965.                 info.text = L["Icon Type"]
  966.                 info.value = "IconType"
  967.                 info.hasArrow = true
  968.                 info.notCheckable = true
  969.                 L_UIDropDownMenu_AddButton(info, level)
  970.                 wipe(info)
  971.  
  972.                 -- in case no type is set
  973.                 if obj.type == "" then
  974.                     info.text = L["More Options"]
  975.                     info.disabled = true
  976.                     info.notCheckable = true
  977.                     L_UIDropDownMenu_AddButton(info)
  978.                     wipe(info)
  979.                 else
  980.                     -- icon effect (animation) -- not available for talents
  981.                     if (obj.subtype ~= "talent") and (addon.effects and menuList.Effects) then
  982.                         info.text = L["Icon Effect"]
  983.                         info.value = "Effects"
  984.                         info.hasArrow = true
  985.                         info.notCheckable = true
  986.                         L_UIDropDownMenu_AddButton(info, level)
  987.                         wipe(info)
  988.                     end
  989.  
  990.                     info.disabled = true
  991.                     L_UIDropDownMenu_AddButton(info)
  992.                     wipe(info)
  993.  
  994.                     if obj.type == "cooldown" then
  995.                         info.text = L["Cooldown Type"]
  996.                         info.value = "SpellType"
  997.                         info.hasArrow = true
  998.                         info.notCheckable = true
  999.                         L_UIDropDownMenu_AddButton(info, level)
  1000.                         wipe(info)
  1001.  
  1002.                         info.text = L["Show When"]
  1003.                         info.value = (obj.subtype == "spell") and "SpellWhen" or "TalentWhen"
  1004.                         info.hasArrow = true
  1005.                         info.notCheckable = true
  1006.                         L_UIDropDownMenu_AddButton(info, level)
  1007.                         wipe(info)
  1008.  
  1009.                         info.text = L["Show Timer"]
  1010.                         info.value = "timer"
  1011.                         info.checked = obj.timer
  1012.                         info.isNotRadio = true
  1013.                         info.func = Icon_OptionToggle
  1014.                         info.keepShownOnClick = true
  1015.                         L_UIDropDownMenu_AddButton(info, level)
  1016.                         wipe(info)
  1017.                     elseif obj.type == "aura" then
  1018.                         info.text = L["Buff or Debuff"]
  1019.                         info.value = "AuraType"
  1020.                         info.hasArrow = true
  1021.                         info.notCheckable = true
  1022.                         L_UIDropDownMenu_AddButton(info, level)
  1023.                         wipe(info)
  1024.  
  1025.                         info.text = L["Unit to Watch"]
  1026.                         info.value = "Unit"
  1027.                         info.hasArrow = true
  1028.                         info.notCheckable = true
  1029.                         L_UIDropDownMenu_AddButton(info, level)
  1030.                         wipe(info)
  1031.  
  1032.                         info.text = L["Show When"]
  1033.                         info.value = "AuraWhen"
  1034.                         info.hasArrow = true
  1035.                         info.notCheckable = true
  1036.                         L_UIDropDownMenu_AddButton(info, level)
  1037.                         wipe(info)
  1038.  
  1039.                         info.text = L["Show Timer"]
  1040.                         info.value = "timer"
  1041.                         info.checked = obj.timer
  1042.                         info.isNotRadio = true
  1043.                         info.func = Icon_OptionToggle
  1044.                         info.keepShownOnClick = true
  1045.                         L_UIDropDownMenu_AddButton(info, level)
  1046.                         wipe(info)
  1047.  
  1048.                         info.text = L["Only Mine"]
  1049.                         info.value = "mine"
  1050.                         info.checked = icon.mine
  1051.                         info.isNotRadio = true
  1052.                         info.func = Icon_OptionToggle
  1053.                         info.keepShownOnClick = true
  1054.                         L_UIDropDownMenu_AddButton(info, level)
  1055.                         wipe(info)
  1056.                     elseif obj.type == "reactive" then
  1057.                         info.text = L["Show When"]
  1058.                         info.value = "SpellWhen"
  1059.                         info.hasArrow = true
  1060.                         info.notCheckable = true
  1061.                         L_UIDropDownMenu_AddButton(info, level)
  1062.                         wipe(info)
  1063.  
  1064.                         info.text = L["Show Timer"]
  1065.                         info.value = "timer"
  1066.                         info.checked = obj.timer
  1067.                         info.isNotRadio = true
  1068.                         info.func = Icon_OptionToggle
  1069.                         info.keepShownOnClick = true
  1070.                         L_UIDropDownMenu_AddButton(info, level)
  1071.                         wipe(info)
  1072.                     elseif obj.type == "wpnenchant" then
  1073.                         info.text = L["Weapon Slot"]
  1074.                         info.value = "WpnEnchantType"
  1075.                         info.hasArrow = true
  1076.                         info.notCheckable = true
  1077.                         info.keepShownOnClick = true
  1078.                         L_UIDropDownMenu_AddButton(info, level)
  1079.                         wipe(info)
  1080.  
  1081.                         info.text = L["Show When"]
  1082.                         info.value = "AuraWhen"
  1083.                         info.hasArrow = true
  1084.                         info.notCheckable = true
  1085.                         L_UIDropDownMenu_AddButton(info, level)
  1086.                         wipe(info)
  1087.  
  1088.                         info.text = L["Show Timer"]
  1089.                         info.value = "timer"
  1090.                         info.checked = obj.timer
  1091.                         info.isNotRadio = true
  1092.                         info.func = Icon_OptionToggle
  1093.                         info.keepShownOnClick = true
  1094.                         L_UIDropDownMenu_AddButton(info, level)
  1095.                         wipe(info)
  1096.                     elseif obj.type == "totem" then
  1097.                         info.text = L["Unit"]
  1098.                         info.value = "Unit"
  1099.                         info.hasArrow = true
  1100.                         info.notCheckable = true
  1101.                         info.keepShownOnClick = true
  1102.                         L_UIDropDownMenu_AddButton(info, level)
  1103.                         wipe(info)
  1104.  
  1105.                         info.text = L["Show When"]
  1106.                         info.value = "AuraWhen"
  1107.                         info.hasArrow = true
  1108.                         info.notCheckable = true
  1109.                         L_UIDropDownMenu_AddButton(info, level)
  1110.                         wipe(info)
  1111.  
  1112.                         info.text = L["Show Timer"]
  1113.                         info.value = "timer"
  1114.                         info.checked = obj.timer
  1115.                         info.isNotRadio = true
  1116.                         info.func = Icon_OptionToggle
  1117.                         info.keepShownOnClick = true
  1118.                         L_UIDropDownMenu_AddButton(info, level)
  1119.                         wipe(info)
  1120.                     end
  1121.                 end
  1122.  
  1123.                 -- adding the clear settings button
  1124.                 if (obj.name and obj.name ~= "") or obj.type ~= "" then
  1125.                     -- separator
  1126.                     info.disabled = true
  1127.                     L_UIDropDownMenu_AddButton(info)
  1128.                     wipe(info)
  1129.  
  1130.                     -- clear settings button
  1131.                     info.text = L["Clear Settings"]
  1132.                     info.func = Icon_OptionClear
  1133.                     info.notCheckable = true
  1134.                     L_UIDropDownMenu_AddButton(info)
  1135.                     wipe(info)
  1136.                 end
  1137.             end
  1138.  
  1139.             L_ToggleDropDownMenu(1, nil, menu, "cursor", 0, 0)
  1140.         end
  1141.     end
  1142.  
  1143.     --
  1144.     -- handles icon's OnMouseDown event
  1145.     --
  1146.     local function Icon_OnMouseDown(self, button)
  1147.         if button == "LeftButton" then
  1148.             local f = self:GetParent()
  1149.             f:StartMoving()
  1150.         end
  1151.     end
  1152.     --
  1153.     -- hands icon's OnMouseUp event
  1154.     --
  1155.     local function Icon_OnMouseUp(self, button)
  1156.         -- opening the menu
  1157.         if button == "RightButton" then
  1158.             StaticPopup_Hide("KTRACKER_DIALOG_RESET")
  1159.             StaticPopup_Hide("KTRACKER_DIALOG_CLEAR")
  1160.             StaticPopup_Hide("KTRACKER_DIALOG_NAME")
  1161.             StaticPopup_Hide("KTRACKER_DIALOG_UNITNAME")
  1162.             StaticPopup_Hide("KTRACKER_DIALOG_SHARE_SEND")
  1163.             StaticPopup_Hide("KTRACKER_DIALOG_SHARE_RECEIVE")
  1164.             PlaySound("UChatScrollButton")
  1165.             Icon_OpenMenu(self)
  1166.         elseif button == "MiddleButton" then
  1167.             local parent = self:GetParent()
  1168.             local g, i = parent:GetID(), self:GetID()
  1169.             local DB = KTrackerDB.groups[g].icons[i]
  1170.             if DB then
  1171.                 DB.enabled = not DB.enabled
  1172.                 L_CloseDropDownMenus()
  1173.                 Icon:Load(self, g, i)
  1174.             end
  1175.         elseif button == "LeftButton" then
  1176.             local f = self:GetParent()
  1177.             f:StopMovingOrSizing()
  1178.  
  1179.             local id = f:GetID()
  1180.             local DB = KTrackerDB.groups[id]
  1181.             if not DB then
  1182.                 return
  1183.             end
  1184.  
  1185.             local db = KTrackerCharDB.groups[DB.created]
  1186.             if not db then
  1187.                 return
  1188.             end
  1189.  
  1190.             local point, _, relativePoint, xOfs, yOfs = f:GetPoint()
  1191.             db.position.xOfs = xOfs
  1192.             db.position.yOfs = yOfs
  1193.             db.position.point = point
  1194.             db.position.relativePoint = relativePoint
  1195.             Group:Load(id)
  1196.         end
  1197.     end
  1198.  
  1199.     --
  1200.     -- called whenever an icon needs to be updated
  1201.     --
  1202.     function Icon:Load(icon, groupID, iconID)
  1203.         -- we make sure the icon frame exists.
  1204.         if not icon then
  1205.             return
  1206.         end
  1207.  
  1208.         -- hold the reference so we call later update the icon.
  1209.         local obj = KTrackerDB.groups[groupID].icons[iconID]
  1210.         -- we make sure to create the icon in case it was missing
  1211.         if not obj then
  1212.             KTrackerDB.groups[groupID].icons[iconID] = utils.deepCopy(def.icon)
  1213.             obj = KTrackerDB.groups[groupID].icons[iconID]
  1214.         end
  1215.         utils.mixTable(icon, obj)
  1216.  
  1217.         local iconName = icon:GetName()
  1218.         icon.texture = _G[iconName .. "Icon"]
  1219.         icon.stacks = _G[iconName .. "Stacks"]
  1220.         icon.cooldown = _G[iconName .. "Cooldown"]
  1221.         icon.coords = {groupID, iconID} -- used to alter database
  1222.  
  1223.         icon.stacks:Hide()
  1224.         icon.cooldown:Hide()
  1225.  
  1226.         -- icon transparency depends on when to use it
  1227.         if icon.when == 1 then
  1228.             icon.alphap = 1
  1229.             icon.alphan = 0
  1230.         elseif icon.when == 0 then
  1231.             icon.alphap = 1
  1232.             icon.alphan = 1
  1233.         elseif icon.when == -1 then
  1234.             icon.alphap = 0
  1235.             icon.alphan = 1
  1236.         else
  1237.             error(L:F("Alpha not assigned: %s", icon.name))
  1238.             icon.alphap = 1
  1239.             icon.alphan = 1
  1240.         end
  1241.  
  1242.         icon:Show()
  1243.         Icon:LoadTexture(icon)
  1244.  
  1245.         if addon.locked then
  1246.             icon:EnableMouse(0)
  1247.             icon:SetAlpha(icon.alphan)
  1248.  
  1249.             if icon.name == "" and icon.type ~= "wpnenchant" then
  1250.                 icon.enabled = false
  1251.             end
  1252.  
  1253.             if icon.enabled then
  1254.                 Icon:SetScripts(icon)
  1255.                 Icon:Check(icon)
  1256.             else
  1257.                 Icon:ClearScripts(icon)
  1258.                 icon:Hide()
  1259.             end
  1260.         else
  1261.             icon:EnableMouse(1)
  1262.             icon:SetAlpha(icon.enabled and 1 or 0.4)
  1263.             Icon:ClearScripts(icon)
  1264.             icon.texture:SetVertexColor(1, 1, 1, 1)
  1265.  
  1266.             -- set icon tooltip and needed scripts
  1267.             utils.setTooltip(
  1268.                 icon,
  1269.                 {
  1270.                     L["Right-click for icon options."],
  1271.                     L["Left-click to move the group."],
  1272.                     L["Middle-click to enable/disable."],
  1273.                     L['Type "|caaf49141/kt config|r" for addon config']
  1274.                 },
  1275.                 nil,
  1276.                 titleString
  1277.             )
  1278.             icon:SetScript("OnMouseDown", Icon_OnMouseDown)
  1279.             icon:SetScript("OnMouseUp", Icon_OnMouseUp)
  1280.         end
  1281.     end
  1282.  
  1283.     --
  1284.     -- pop up dialog that allows the user to set the name
  1285.     --
  1286.     StaticPopupDialogs["KTRACKER_DIALOG_NAME"] = {
  1287.         text = L[
  1288.             "Enter the name or ID of the spell. You can add multiple buffs or debuffs by separatig them with semicolons."
  1289.         ],
  1290.         button1 = SAVE,
  1291.         button2 = CANCEL,
  1292.         hasEditBox = true,
  1293.         hasWideEditBox = true,
  1294.         maxLetters = 254,
  1295.         timeout = 0,
  1296.         whileDead = true,
  1297.         hideOnEscape = true,
  1298.         OnShow = function(self)
  1299.             local g, i = current.group, current.icon
  1300.             local obj = KTrackerDB.groups[g].icons[i]
  1301.             if not obj then
  1302.                 self:Hide()
  1303.             end
  1304.             _G[self:GetName() .. "WideEditBox"]:SetText(obj.name)
  1305.             _G[self:GetName() .. "WideEditBox"]:SetFocus()
  1306.         end,
  1307.         OnHide = function(self)
  1308.             if ChatFrameEditBox and ChatFrameEditBox:IsVisible() then
  1309.                 ChatFrameEditBox:SetFocus()
  1310.             end
  1311.             _G[self:GetName() .. "WideEditBox"]:SetText("")
  1312.             _G[self:GetName() .. "WideEditBox"]:ClearFocus()
  1313.             wipe(current)
  1314.         end,
  1315.         OnAccept = function(self)
  1316.             local text = _G[self:GetName() .. "WideEditBox"]:GetText()
  1317.             local g, i = current.group, current.icon
  1318.             local obj = KTrackerDB.groups[g].icons[i]
  1319.             if obj then
  1320.                 obj.name = text:trim()
  1321.                 wipe(current)
  1322.                 local iconName = format(holderIcon, g, i)
  1323.                 Icon:Load(_G[iconName], g, i)
  1324.             end
  1325.             self:Hide()
  1326.         end,
  1327.         EditBoxOnEnterPressed = function(self)
  1328.             local parent = self:GetParent()
  1329.             local text = _G[parent:GetName() .. "WideEditBox"]:GetText()
  1330.             local g, i = current.group, current.icon
  1331.             local obj = KTrackerDB.groups[g].icons[i]
  1332.             if obj then
  1333.                 obj.name = text:trim()
  1334.                 wipe(current)
  1335.                 local iconName = format(holderIcon, g, i)
  1336.                 Icon:Load(_G[iconName], g, i)
  1337.             end
  1338.             parent:Hide()
  1339.         end,
  1340.         EditBoxOnEscapePressed = function(self)
  1341.             wipe(current)
  1342.             self:GetParent():Hide()
  1343.         end
  1344.     }
  1345.  
  1346.     --
  1347.     -- pop up dialog that allows the user to set custom icon unit
  1348.     --
  1349.     StaticPopupDialogs["KTRACKER_DIALOG_UNITNAME"] = {
  1350.         text = L["Enter the unit name on which you want to track the aura."],
  1351.         button1 = SAVE,
  1352.         button2 = CANCEL,
  1353.         hasEditBox = true,
  1354.         maxLetters = 12,
  1355.         timeout = 0,
  1356.         whileDead = true,
  1357.         hideOnEscape = true,
  1358.         OnShow = function(self, icon)
  1359.             if icon then
  1360.                 _G[self:GetName() .. "EditBox"]:SetText(icon.unit)
  1361.                 _G[self:GetName() .. "EditBox"]:SetFocus()
  1362.             end
  1363.         end,
  1364.         OnHide = function(self)
  1365.             if ChatFrameEditBox and ChatFrameEditBox:IsVisible() then
  1366.                 ChatFrameEditBox:SetFocus()
  1367.             end
  1368.             _G[self:GetName() .. "EditBox"]:SetText("")
  1369.             _G[self:GetName() .. "EditBox"]:ClearFocus()
  1370.         end,
  1371.         OnAccept = function(self)
  1372.             local unit = _G[self:GetName() .. "EditBox"]:GetText():trim()
  1373.             if unit == "" then
  1374.                 unit = "player"
  1375.             elseif not UnitExists(unit) then
  1376.                 addon:PrintError(L["Could not find that unit."])
  1377.                 return
  1378.             end
  1379.             local g, i = current.group, current.icon
  1380.             local obj = KTrackerDB.groups[g].icons[i]
  1381.             if obj then
  1382.                 obj.unit = unit
  1383.                 wipe(current)
  1384.                 local iconName = format(holderIcon, g, i)
  1385.                 Icon:Load(_G[iconName], g, i)
  1386.             end
  1387.             self:Hide()
  1388.         end,
  1389.         EditBoxOnEnterPressed = function(self)
  1390.             local parent = self:GetParent()
  1391.             local unit = _G[parent:GetName() .. "EditBox"]:GetText()
  1392.             if unit == "" then
  1393.                 unit = "player"
  1394.             elseif not UnitExists(unit) then
  1395.                 addon:PrintError(L["Could not find that unit."])
  1396.                 return
  1397.             end
  1398.             local g, i = current.group, current.icon
  1399.             local obj = KTrackerDB.groups[g].icons[i]
  1400.             if obj then
  1401.                 obj.unit = unit
  1402.                 wipe(current)
  1403.                 local iconName = format(holderIcon, g, i)
  1404.                 Icon:Load(_G[iconName], g, i)
  1405.             end
  1406.             parent:Hide()
  1407.         end,
  1408.         EditBoxOnEscapePressed = function(self)
  1409.             wipe(current)
  1410.             self:GetParent():Hide()
  1411.         end
  1412.     }
  1413. end
  1414.  
  1415. --
  1416. -- called whenever an icon needs to has its scripts removed
  1417. --
  1418. function Icon:ClearScripts(icon)
  1419.     if not icon then
  1420.         return
  1421.     end
  1422.     -- remove scripts
  1423.     icon:SetScript("OnEvent", nil)
  1424.     icon:SetScript("OnUpdate", nil)
  1425.     -- unregister events
  1426.     icon:UnregisterEvent("ACTIONBAR_UPDATE_COOLDOWN")
  1427.     icon:UnregisterEvent("ACTIONBAR_UPDATE_USABLE")
  1428.     icon:UnregisterEvent("BAG_UPDATE_COOLDOWN")
  1429.     icon:UnregisterEvent("COMBAT_LOG_EVENT_UNFILTERED")
  1430.     icon:UnregisterEvent("PLAYER_FOCUS_CHANGED")
  1431.     icon:UnregisterEvent("PLAYER_TARGET")
  1432.     icon:UnregisterEvent("PLAYER_TARGET_CHANGED")
  1433.     icon:UnregisterEvent("PLAYER_TOTEM_UPDATE")
  1434.     icon:UnregisterEvent("UNIT_AURA")
  1435.     icon:UnregisterEvent("UNIT_INVENTORY_CHANGED")
  1436. end
  1437.  
  1438. --
  1439. -- simply changes the icon texture
  1440. --
  1441. function Icon:LoadTexture(icon)
  1442.     if not icon then
  1443.         return
  1444.     end
  1445.  
  1446.     local noTexture, texture
  1447.  
  1448.     if icon.type == "cooldown" or icon.type == "reactive" then
  1449.         noTexture = textures[1]
  1450.  
  1451.         if icon.subtype == "spell" or icon.subtype == "talent" then
  1452.             local name = addon:GetSpellNames(icon.name, true)
  1453.             texture = GetSpellTexture(name) or select(3, GetSpellInfo(icon.name))
  1454.  
  1455.             -- still no texture? Try with backup
  1456.             if not texture and LiCD.talentsRev[icon.name] then
  1457.                 texture = select(3, GetSpellInfo(LiCD.talentsRev[icon.name]))
  1458.             end
  1459.         elseif icon.subtype == "item" then
  1460.             local name = addon:GetItemNames(icon.name, true)
  1461.             texture = select(10, GetItemInfo(name))
  1462.         end
  1463.     elseif icon.type == "aura" then
  1464.         noTexture = textures[2]
  1465.         texture = icon.filepath
  1466.  
  1467.         if not texture and icon.name ~= "" then
  1468.             local name = addon:GetSpellNames(icon.name, true)
  1469.             texture = GetSpellTexture(name)
  1470.         end
  1471.     elseif icon.type == "wpnenchant" then
  1472.         noTexture = textures[1]
  1473.  
  1474.         local slot
  1475.         if icon.subtype == "mainhand" then
  1476.             slot = select(1, GetInventorySlotInfo("MainHandSlot"))
  1477.         elseif icon.subtype == "offhand" then
  1478.             slot = select(1, GetInventorySlotInfo("SecondaryHandSlot"))
  1479.         end
  1480.         texture = GetInventoryItemTexture("player", slot)
  1481.     elseif icon.type == "totem" then
  1482.         noTexture = textures[1]
  1483.         local name = addon:GetSpellNames(icon.name, true)
  1484.         texture = GetSpellTexture(name)
  1485.     end
  1486.  
  1487.     utils.setTexture(icon.texture, texture, noTexture, texture)
  1488. end
  1489.  
  1490. --
  1491. -- checks the icon
  1492. --
  1493. function Icon:Check(icon)
  1494.     if not icon then
  1495.         -- reactive spells or cooldowns.
  1496.         return
  1497.     elseif icon.type == "cooldown" or icon.type == "reactive" then
  1498.         -- auras: buffs and debuffs
  1499.         -- buffs and debuffs
  1500.         local startTime, duration
  1501.  
  1502.         if icon.startTime and icon.duration then
  1503.             startTime, duration = icon.startTime, icon.duration
  1504.         else
  1505.             if icon.subtype == "spell" or icon.subtype == "talent" then
  1506.                 icon.rname = addon:GetSpellNames(icon.name, true)
  1507.                 startTime, duration, _ = GetSpellCooldown(icon.rname)
  1508.             elseif icon.subtype == "item" then
  1509.                 icon.rname = addon:GetItemNames(icon.name, true)
  1510.                 startTime, duration, _ = GetItemCooldown(icon.rname)
  1511.             end
  1512.         end
  1513.  
  1514.         if icon.rname and icon.timer and duration then
  1515.             if duration > 0 then
  1516.                 CooldownFrame_SetTimer(icon.cooldown, startTime, duration, 1)
  1517.             else
  1518.                 CooldownFrame_SetTimer(icon.cooldown, 0, 0, 0)
  1519.             end
  1520.         end
  1521.     elseif icon.type == "aura" and UnitExists(icon.unit) then
  1522.         -- weapon enchants
  1523.         local auras = addon:GetSpellNames(icon.name, nil, true)
  1524.         local i, name, hasAura
  1525.  
  1526.         local filter = icon.subtype .. (icon.mine and "|PLAYER" or "")
  1527.  
  1528.         for i, name in ipairs(auras) do
  1529.             local spellId, spellName
  1530.             if tonumber(name) ~= nil then
  1531.                 spellId = tonumber(name)
  1532.                 spellName = select(1, GetSpellInfo(spellId))
  1533.             else
  1534.                 spellName = name:trim()
  1535.             end
  1536.  
  1537.             local _name, _, _icon, _count, _, _duration, _expires, _, _, _, _spellId =
  1538.                 UnitAura(icon.unit, spellName, nil, filter)
  1539.  
  1540.             if _name then
  1541.                 -- we have used the ID? we need to be precise
  1542.                 -- if spellId and (_name:lower() ~= spellName:lower()) then
  1543.                 if spellId then
  1544.                     if spellId == _spellId then
  1545.                         icon.duration = _duration
  1546.                         icon.rname = spellName
  1547.                         hasAura = true
  1548.                     else
  1549.                         -- the idea here is to go through all unit's auras one by one
  1550.                         -- grab the info and compare it to what we have.
  1551.                         for x = 1, 40 do
  1552.                             local _n, _, _i, _c, _, _d, _e, _, _, _, _s = UnitAura(icon.unit, x, nil, filter)
  1553.  
  1554.                             -- if we have a proper aura and it is exactly the one
  1555.                             -- we are looking for, aka same id, we use it.
  1556.                             if (_n and _s) and (spellId == _s) then
  1557.                                 _name, _icon, _count = _n, _i, _c
  1558.                                 _duration, _expires = _d, _e
  1559.                                 icon.duration = _d
  1560.                                 icon.rname = _n
  1561.                                 hasAura = true
  1562.                                 break -- no need to go further
  1563.                             end
  1564.                         end
  1565.                     end
  1566.                 elseif _name:lower() == spellName:lower() then
  1567.                     icon.duration = _duration
  1568.                     icon.rname = _name
  1569.                     hasAura = true
  1570.                 end
  1571.  
  1572.                 if hasAura then
  1573.                     if icon.texture:GetTexture() ~= _icon then
  1574.                         icon.texture:SetTexture(_icon)
  1575.                         KTrackerDB.groups[icon.coords[1]].icons[icon.coords[2]].filepath = _icon
  1576.                     end
  1577.  
  1578.                     icon:SetAlpha(icon.alphap)
  1579.                     icon.texture:SetVertexColor(1, 1, 1, 1)
  1580.  
  1581.                     if _count > 1 then
  1582.                         icon.stacks:SetText(_count)
  1583.                         icon.stacks:Show()
  1584.                     else
  1585.                         icon.stacks:Hide()
  1586.                     end
  1587.  
  1588.                     if icon.timer and not UnitIsDead(icon.unit) then
  1589.                         CooldownFrame_SetTimer(icon.cooldown, _expires - _duration, _duration, 1)
  1590.                     end
  1591.                 end
  1592.             end
  1593.         end
  1594.  
  1595.         if not hasAura then
  1596.             icon:SetAlpha(icon.alphan)
  1597.  
  1598.             if icon.alphap == 1 and icon.alphan == 1 then
  1599.                 icon.texture:SetVertexColor(1, 0.35, 0.35, 1)
  1600.             end
  1601.  
  1602.             icon.stacks:Hide()
  1603.  
  1604.             if icon.timer then
  1605.                 CooldownFrame_SetTimer(icon.cooldown, 0, 0, 0)
  1606.             end
  1607.  
  1608.             icon.duration = 0
  1609.             icon.rname = auras[1]
  1610.         end
  1611.     elseif icon.type == "wpnenchant" then
  1612.         -- totems
  1613.         local now = GetTime()
  1614.         local duration = 0
  1615.         local hasMHEnchant, mhExpiration, mhCharges, hasOHEnchant, ohExpiration, ohCharges = GetWeaponEnchantInfo()
  1616.  
  1617.         if icon.subtype == "mainhand" and hasMHEnchant then
  1618.             icon:SetAlpha(icon.alphap)
  1619.  
  1620.             if mhCharges > 0 then
  1621.                 icon.stacks:SetText(mhCharges)
  1622.                 icon.stacks:Show()
  1623.             else
  1624.                 icon.stacks:SetText("")
  1625.                 icon.stacks:Hide()
  1626.             end
  1627.  
  1628.             duration = mhExpiration / 1000
  1629.         elseif icon.subtype == "offhand" and hasOHEnchant then
  1630.             icon:SetAlpha(icon.alphap)
  1631.  
  1632.             if ohCharges > 0 then
  1633.                 icon.stacks:SetText(ohCharges)
  1634.                 icon.stacks:Show()
  1635.             else
  1636.                 icon.stacks:SetText("")
  1637.                 icon.stacks:Hide()
  1638.             end
  1639.  
  1640.             duration = ohExpiration / 1000
  1641.         else
  1642.             icon:SetAlpha(icon.alphan)
  1643.         end
  1644.  
  1645.         if icon.timer and duration > 0 then
  1646.             icon.texture:SetVertexColor(1, 1, 1, 1)
  1647.             CooldownFrame_SetTimer(icon.cooldown, now, duration, 1)
  1648.         elseif icon.timer then
  1649.             icon.texture:SetVertexColor(1, 0.35, 0.35, 1)
  1650.             CooldownFrame_SetTimer(icon.cooldown, 0, 0, 0)
  1651.         end
  1652.  
  1653.         icon.duration = duration
  1654.     elseif icon.type == "totem" then
  1655.         -- if none of the above.
  1656.         local totems = addon:GetSpellNames(icon.name)
  1657.         local found, texture, rname
  1658.         local startTime, duration = 0, 0
  1659.         local precise = GetTime()
  1660.  
  1661.         for i = 1, 4 do
  1662.             local haveTotem, totemName, totemStartTime, totemDuration, totemTexture = GetTotemInfo(i)
  1663.             for i, name in ipairs(totems) do
  1664.                 local spellName = select(1, GetSpellInfo(name))
  1665.                 if totemName and totemName:find(name) then
  1666.                     found, texture, rname = true, totemTexture, name
  1667.  
  1668.                     startTime = ((precise - totemStartTime) > 1) and totemStartTime + 1 or precise
  1669.                     duration = totemDuration
  1670.  
  1671.                     found = true
  1672.                     break
  1673.                 end
  1674.             end
  1675.         end
  1676.  
  1677.         icon.rname = rname
  1678.         icon.duration = duration
  1679.  
  1680.         if found then
  1681.             icon:SetAlpha(icon.alphap)
  1682.             icon.texture:SetVertexColor(1, 1, 1, 1)
  1683.  
  1684.             if icon.timer then
  1685.                 CooldownFrame_SetTimer(icon.cooldown, startTime, duration, 1)
  1686.             end
  1687.         else
  1688.             icon:SetAlpha(icon.alphan)
  1689.             if icon.alphan == 1 and icon.alphap == 1 then
  1690.                 icon.texture:SetVertexColor(1, 0.35, 0.35, 1)
  1691.             end
  1692.             CooldownFrame_SetTimer(icon.cooldown, 0, 0, 0)
  1693.         end
  1694.     else
  1695.         icon:SetAlpha(icon.alphan)
  1696.  
  1697.         -- stop the cooldown.
  1698.         CooldownFrame_SetTimer(icon.cooldown, 0, 0, 0)
  1699.  
  1700.         if icon.alphap == 1 and icon.alphan == 1 then
  1701.             icon.texture:SetVertexColor(1, 0.35, 0.35, 1)
  1702.         end
  1703.     end
  1704. end
  1705.  
  1706. do
  1707.     -- list of aura events
  1708.     local auraEvents = {
  1709.         SPELL_AURA_APPLIED = true,
  1710.         SPELL_AURA_APPLIED_DOSE = true,
  1711.         SPELL_AURA_BROKEN = true,
  1712.         SPELL_AURA_BROKEN_SPELL = true,
  1713.         SPELL_AURA_REFRESH = true,
  1714.         SPELL_AURA_REMOVED = true,
  1715.         SPELL_AURA_REMOVED_DOSE = true,
  1716.         SPELL_ENERGIZE = true -- for talents internal cooldowns
  1717.     }
  1718.  
  1719.     -- list of other events to check
  1720.     local otherEvents = {
  1721.         ACTIONBAR_UPDATE_COOLDOWN = true,
  1722.         ACTIONBAR_UPDATE_USABLE = true,
  1723.         PLAYER_REGEN_DISABLED = true,
  1724.         PLAYER_REGEN_ENABLED = true,
  1725.         PLAYER_TOTEM_UPDATE = true
  1726.     }
  1727.  
  1728.     --
  1729.     -- handles icons OnEvent event
  1730.     --
  1731.     local function Icon_OnEvent(self, event, ...)
  1732.         if event == "COMBAT_LOG_EVENT_UNFILTERED" then
  1733.             local e = select(2, ...)
  1734.             if auraEvents[e] then
  1735.                 local destGUID = select(6, ...)
  1736.                 if destGUID == UnitGUID(self.unit) then
  1737.                     local id, name = select(9, ...)
  1738.                     if self.name:find(id) or self.name:find(name) then
  1739.                         -- add spell id if found - hotfix
  1740.                         if e == "SPELL_ENERGIZE" and LiCD and LiCD.talents[id] then
  1741.                             self.startTime, self.duration = GetTime(), LiCD.cooldowns[id]
  1742.                         end
  1743.                         Icon:Check(self)
  1744.                     end
  1745.                 end
  1746.             end
  1747.         elseif otherEvents[event] then
  1748.             Icon:Check(self)
  1749.         elseif event == "UNIT_AURA" then
  1750.             local unit = select(1, ...)
  1751.             if UnitExists(unit) then
  1752.                 Icon:Check(self)
  1753.             else
  1754.                 local name = select(1, UnitName(unit))
  1755.                 if name:lower() == self.unit:lower() then
  1756.                     Icon:Check(self)
  1757.                 end
  1758.             end
  1759.         elseif event == "BAG_UPDATE_COOLDOWN" then
  1760.             Icon:Check(self)
  1761.         elseif event == "UNIT_INVENTORY_CHANGED" and select(1, ...) == "player" then
  1762.             Icon:Check(self)
  1763.         elseif event == "PLAYER_TARGET_CHANGED" or event == "PLAYER_FOCUS_CHANGED" then
  1764.             Icon:Check(self)
  1765.         elseif event == "UNIT_TARGET" and select(1, ...) == "target" then
  1766.             Icon:Check(self)
  1767.         end
  1768.     end
  1769.  
  1770.     --
  1771.     -- handles icons OnUpdate event
  1772.     --
  1773.     local function Icon_OnUpdate(self, elapsed)
  1774.         if utils.update(self, self:GetName(), 0.1, elapsed) then
  1775.             if self.rname and self.type == "cooldown" then
  1776.                 -- buffs and debuffs
  1777.                 -- item cooldown
  1778.                 if self.subtype == "item" then
  1779.                     -- spell cooldown
  1780.                     local _, duration, _ = GetItemCooldown(self.rname)
  1781.                     local onGCD = addon:GetGCD()
  1782.  
  1783.                     if duration then
  1784.                         if duration == 0 or onGCD == duration then
  1785.                             self:SetAlpha(self.alphap)
  1786.                             Icon_EffectTrigger(self)
  1787.                         elseif duration > 0 and onGCD ~= duration then
  1788.                             self:SetAlpha(self.alphan)
  1789.                             Icon_EffectReset(self)
  1790.                         end
  1791.                     end
  1792.                 elseif self.subtype == "spell" then
  1793.                     -- talent cooldown
  1794.                     local startTime, duration, _ = GetSpellCooldown(self.rname)
  1795.                     local onGCD = (addon:GetGCD() == duration and duration > 0)
  1796.                     local usable, noMana = IsUsableSpell(self.rname)
  1797.                     local inRange = IsSpellInRange(self.rname, self.unit)
  1798.                     local _, _, _, _, _, _, _, minRange, maxRange = GetSpellInfo(self.rname)
  1799.                     if not maxRange or inRange == nil then
  1800.                         inRange = 1
  1801.                     end
  1802.  
  1803.                     if duration then
  1804.                         if (duration == 0 or onGCD) and inRange == 1 and not noMana then
  1805.                             self.texture:SetVertexColor(1, 1, 1, 1)
  1806.                             self:SetAlpha(self.alphap)
  1807.                             Icon_EffectTrigger(self)
  1808.                         elseif (duration == 0 or onGCD) and self.alphap == 1 then
  1809.                             self.texture:SetVertexColor(0.35, 0.35, 0.35, 1)
  1810.                             self:SetAlpha(self.alphap)
  1811.                         else
  1812.                             self.texture:SetVertexColor(1, 1, 1, 1)
  1813.                             self:SetAlpha(self.alphan)
  1814.                             Icon_EffectReset(self)
  1815.                         end
  1816.                     else
  1817.                         self:SetAlpha(self.alphan)
  1818.                     end
  1819.                 elseif self.subtype == "talent" and LiCD.talentsRev[self.rname] then
  1820.                     local startTime = self.startTime or 0
  1821.                     local duration = (startTime == 0) and 0 or LiCD.cooldowns[LiCD.talentsRev[self.rname]]
  1822.  
  1823.                     -- should we reset the cooldown? we reset the timer.
  1824.                     -- because the cooldown duration depends on the its start time
  1825.                     -- all we have to do is to set the latter to 0.
  1826.                     if (duration > 0) and (GetTime() - startTime >= duration) then
  1827.                         -- is the spell on cooldown?
  1828.                         self.startTime = 0
  1829.                     elseif duration > 0 then
  1830.                         self:SetAlpha(self.alphan)
  1831.                     else
  1832.                         self:SetAlpha(self.alphap)
  1833.                     end
  1834.                 end
  1835.             elseif self.rname and self.type == "aura" and UnitExists(self.unit) then
  1836.                 -- reactive spell
  1837.                 local animate
  1838.  
  1839.                 if self.subtype == "HARMFUL" then
  1840.                     local reaction = UnitReaction("player", self.unit)
  1841.                     if reaction and reaction <= 4 then
  1842.                         animate = (self.when >= 0 and self.duration == 0)
  1843.                     else
  1844.                         animate = (self.when >= 0 and self.duration > 0)
  1845.                     end
  1846.                 elseif self.subtype == "HELPFUL" then
  1847.                     animate = (self.duration <= 60)
  1848.                 end
  1849.  
  1850.                 if animate then
  1851.                     addon:TriggerEffect(self.cooldown, self.effect)
  1852.                 end
  1853.             elseif self.rname and self.type == "reactive" then
  1854.                 -- weapon enchants and totems
  1855.                 -- item cooldown
  1856.                 local startTime, duration, _ = GetSpellCooldown(self.rname)
  1857.                 local usable, noMana = IsUsableSpell(self.rname)
  1858.                 local inRange = IsSpellInRange(self.rname, self.unit)
  1859.                 local _, _, _, _, _, _, _, minRange, maxRange = GetSpellInfo(self.rname)
  1860.                 if not maxRange or inRange == nil then
  1861.                     inRange = 1
  1862.                 end
  1863.  
  1864.                 if usable then
  1865.                     if inRange and not noMana then
  1866.                         self.texture:SetVertexColor(1, 1, 1, 1)
  1867.                         self:SetAlpha(self.alphap)
  1868.                         Icon_EffectTrigger(self)
  1869.                     elseif not inRange or noMana then
  1870.                         self.texture:SetVertexColor(0.35, 0.35, 0.35, 1)
  1871.                         self:SetAlpha(self.alphap)
  1872.                     else
  1873.                         self.texture:SetVertexColor(1, 1, 1, 1)
  1874.                         self:SetAlpha(self.alphan)
  1875.                         Icon_EffectReset(self)
  1876.                     end
  1877.                 else
  1878.                     self:SetAlpha(self.alphan)
  1879.                 end
  1880.             elseif self.type == "wpnenchant" or (self.rname and self.type == "totem") then
  1881.                 local animate = (self.when <= 0 and self.duration == 0)
  1882.  
  1883.                 if animate then
  1884.                     addon:TriggerEffect(self.cooldown, self.effect)
  1885.                 end
  1886.             end
  1887.         end
  1888.     end
  1889.  
  1890.     --
  1891.     -- sets icon scripts and events
  1892.     --
  1893.     function Icon:SetScripts(icon)
  1894.         if not icon then
  1895.             return
  1896.         end
  1897.         icon:SetScript("OnEvent", Icon_OnEvent)
  1898.         icon:SetScript("OnUpdate", Icon_OnUpdate)
  1899.  
  1900.         if icon.type == "totem" then
  1901.             icon:RegisterEvent("PLAYER_TOTEM_UPDATE")
  1902.         elseif icon.type == "wpnenchant" then
  1903.             icon.cooldown:SetReverse(true)
  1904.             icon:RegisterEvent("UNIT_INVENTORY_CHANGED")
  1905.         elseif icon.type == "aura" then
  1906.             icon.cooldown:SetReverse(true)
  1907.             icon:RegisterEvent("UNIT_AURA")
  1908.             if icon.unit == "target" then
  1909.                 icon:RegisterEvent("PLAYER_TARGET_CHANGED")
  1910.             elseif icon.unit == "focus" then
  1911.                 icon:RegisterEvent("PLAYER_FOCUS_CHANGED")
  1912.             elseif icon.unit == "targettarget" then
  1913.                 icon:RegisterEvent("PLAYER_TARGET_CHANGED")
  1914.                 icon:RegisterEvent("UNIT_TARGET")
  1915.                 icon:RegisterEvent("COMBAT_LOG_EVENT_UNFILTERED")
  1916.             end
  1917.         elseif icon.type == "cooldown" or icon.type == "reactive" then
  1918.             icon.cooldown:SetReverse(false)
  1919.             if icon.subtype == "spell" or icon.subtype == "talent" then
  1920.                 icon:RegisterEvent("COMBAT_LOG_EVENT_UNFILTERED") -- hotfix
  1921.                 icon:RegisterEvent("ACTIONBAR_UPDATE_USABLE")
  1922.                 icon:RegisterEvent("ACTIONBAR_UPDATE_COOLDOWN")
  1923.             elseif icon.subtype == "item" then
  1924.                 icon:RegisterEvent("BAG_UPDATE_COOLDOWN")
  1925.             end
  1926.         end
  1927.     end
  1928. end
  1929.  
  1930. --------------------------------------------------------------------------
  1931. -- Talents functions
  1932.  
  1933. do
  1934.     -- holds the character's spec
  1935.     local spec
  1936.  
  1937.     --
  1938.     -- caches the character's spec
  1939.     --
  1940.     function addon:SetCurrentSpec()
  1941.         spec = GetActiveTalentGroup()
  1942.     end
  1943.  
  1944.     --
  1945.     -- returns the character's current spec
  1946.     --
  1947.     function addon:GetCurrentSpec()
  1948.         if not spec then
  1949.             self:SetCurrentSpec()
  1950.         end
  1951.         return spec
  1952.     end
  1953. end
  1954.  
  1955. --------------------------------------------------------------------------
  1956. -- Spells functions
  1957.  
  1958. do
  1959.     --
  1960.     -- list of terms that can be used by the user to the track
  1961.     -- multiple buffs or debuffs
  1962.     --
  1963.     local equivalents = {
  1964.         Bleeding = "Pounce Bleed;Rake;Rip;Lacerate;Rupture;Garrote;Savage Rend;Rend;Deep Wound",
  1965.         DontMelee = "Berserk;Evasion;Shield Wall;Retaliation;Dispersion;Hand of Sacrifice;Hand of Protection;Divine Shield;Divine Protection;Ice Block;Icebound Fortitude;Cyclone;Banish",
  1966.         FaerieFires = "Faerie Fire;Faerie Fire (Feral)",
  1967.         ImmuneToMagicCC = "Divine Shield;Ice Block;The Beast Within;Beastial Wrath;Cyclone;Banish",
  1968.         ImmuneToStun = "Divine Shield;Ice Block;The Beast Within;Beastial Wrath;Icebound Fortitude;Hand of Protection;Cyclone;Banish",
  1969.         Incapacitated = "Gouge;Maim;Repentance;Reckless Charge;Hungering Cold",
  1970.         MeleeSlowed = "Rocket Burst;Infected Wounds;Judgements of the Just;Earth Shock;Thunder Clap;Icy Touch",
  1971.         MovementSlowed = "Incapacitating Shout;Chains of Ice;Icy Clutch;Slow;Daze;Hamstring;Piercing Howl;Wing Clip;Frost Trap Aura;Frostbolt;Cone of Cold;Blast Wave;Mind Flay;Crippling Poison;Deadly Throw;Frost Shock;Earthbind;Curse of Exhaustion",
  1972.         Stunned = "Reckless Charge;Bash;Maim;Pounce;Starfire Stun;Intimidation;Impact;Hammer of Justice;Stun;Blackout;Kidney Shot;Cheap Shot;Shadowfury;Intercept;Charge Stun;Concussion Blow;War Stomp",
  1973.         StunnedOrIncapacitated = "Gouge;Maim;Repentance;Reckless Charge;Hungering Cold;Bash;Pounce;Starfire Stun;Intimidation;Impact;Hammer of Justice;Stun;Blackout;Kidney Shot;Cheap Shot;Shadowfury;Intercept;Charge Stun;Concussion Blow;War Stomp",
  1974.         VulnerableToBleed = "Mangle (Cat);Mangle (Bear);Trauma",
  1975.         WoTLKDebuffs = "71204;71237;71289;72293;72410;72219;72551;72552;72553;69279;69674;72272;72273;73020;70447;70672;70911;74118;74119;72999;71822;70867;70923;71267;71340;70873;70106;69762;69766;70128;70126;70541;73779;73780;73781;70337;69409;73797;73798;74453;74367;74562;74792"
  1976.     }
  1977.  
  1978.     --
  1979.     -- splits the names of spells if separated using semicolons.
  1980.     --
  1981.     local function SplitNames(str, item, strict)
  1982.         local list = {}
  1983.  
  1984.         if strict == nil then
  1985.             strict = false
  1986.         end
  1987.  
  1988.         if (str:find(";") ~= nil) then
  1989.             list = {strsplit(";", str)}
  1990.         else
  1991.             list = {str}
  1992.         end
  1993.  
  1994.         if not strict then
  1995.             local i, name
  1996.             for i, name in ipairs(list) do
  1997.                 if tonumber(name) ~= nil then
  1998.                     list[i] = (item == "item") and GetItemInfo(name) or GetSpellInfo(name)
  1999.                 end
  2000.             end
  2001.         end
  2002.         return list
  2003.     end
  2004.  
  2005.     --
  2006.     -- returns a single or a list of spells names
  2007.     --
  2008.     function addon:GetSpellNames(str, first, strict)
  2009.         local list = SplitNames(equivalents[str] or str, nil, strict)
  2010.  
  2011.         if first then
  2012.             return list[1]
  2013.         end
  2014.  
  2015.         return list
  2016.     end
  2017.  
  2018.     --
  2019.     -- returns a single or a list of item names
  2020.     --
  2021.     function addon:GetItemNames(str, first, strict)
  2022.         local list = SplitNames(str, "item", strict)
  2023.         return first and list[1] or list
  2024.     end
  2025.  
  2026.     --
  2027.     -- returns the global cooldown if the player using an instant cast spell
  2028.     --
  2029.     function addon:GetGCD()
  2030.         local ver = select(4, GetBuildInfo())
  2031.         local spells
  2032.  
  2033.         if ver >= 30000 then
  2034.             spells = {
  2035.                 ROGUE = GetSpellInfo(1752), -- sinister strike
  2036.                 PRIEST = GetSpellInfo(139), -- renew
  2037.                 DRUID = GetSpellInfo(774), -- rejuvenation
  2038.                 WARRIOR = GetSpellInfo(6673), -- battle shout
  2039.                 MAGE = GetSpellInfo(168), -- frost armor
  2040.                 WARLOCK = GetSpellInfo(1454), -- life tap
  2041.                 PALADIN = GetSpellInfo(1152), -- purify
  2042.                 SHAMAN = GetSpellInfo(324), -- lightning shield
  2043.                 HUNTER = GetSpellInfo(1978), -- serpent sting
  2044.                 DEATHKNIGHT = GetSpellInfo(45462) -- plague strike
  2045.             }
  2046.         else
  2047.             spells = {
  2048.                 ROGUE = GetSpellInfo(1752), -- sinister strike
  2049.                 PRIEST = GetSpellInfo(139), -- renew
  2050.                 DRUID = GetSpellInfo(774), -- rejuvenation
  2051.                 WARRIOR = GetSpellInfo(6673), -- battle shout
  2052.                 MAGE = GetSpellInfo(168), -- frost armor
  2053.                 WARLOCK = GetSpellInfo(1454), -- life tap
  2054.                 PALADIN = GetSpellInfo(1152), -- purify
  2055.                 SHAMAN = GetSpellInfo(324), -- lightning shield
  2056.                 HUNTER = GetSpellInfo(1978) -- serpent sting
  2057.             }
  2058.         end
  2059.         local _, unitClass = UnitClass("player")
  2060.         return select(2, GetSpellCooldown(self:GetSpellNames(spells[unitClass], true)))
  2061.     end
  2062. end
  2063.  
  2064. --------------------------------------------------------------------------
  2065. -- Animations functions
  2066.  
  2067. do
  2068.     --
  2069.     -- holds the list of already animated spell icons
  2070.     --
  2071.     local animated = {}
  2072.  
  2073.     --
  2074.     -- used to trigger icons animation
  2075.     --
  2076.     function Icon_EffectTrigger(icon)
  2077.         if icon and not animated[icon:GetName()] then
  2078.             addon:TriggerEffect(icon.cooldown, icon.effect)
  2079.             animated[icon:GetName()] = true
  2080.         end
  2081.     end
  2082.  
  2083.     --
  2084.     -- resets the icon's animation
  2085.     --
  2086.     function Icon_EffectReset(icon)
  2087.         if icon and animated[icon:GetName()] then
  2088.             animated[icon:GetName()] = nil
  2089.         end
  2090.     end
  2091.     --
  2092.     -- register a new animation effect
  2093.     -- TODO: work more on it.
  2094.     --
  2095.     function addon:RegisterEffect(effect)
  2096.         if not self:GetEffect(effect.id) then
  2097.             self.effects = self.effects or {}
  2098.             tinsert(self.effects, effect)
  2099.         end
  2100.     end
  2101.  
  2102.     --
  2103.     -- retrieves an previously registered effect
  2104.     --
  2105.     function addon:GetEffect(id)
  2106.         if self.effects then
  2107.             for _, effect in ipairs(self.effects) do
  2108.                 if effect.id == id then
  2109.                     return effect
  2110.                 end
  2111.             end
  2112.         end
  2113.     end
  2114.  
  2115.     --
  2116.     -- triggers an animation effect
  2117.     --
  2118.     function addon:TriggerEffect(cooldown, id)
  2119.         local effect = self:GetEffect(id)
  2120.         if effect then
  2121.             effect:Run(cooldown)
  2122.         end
  2123.     end
  2124.  
  2125.     -- --------------- glow effect --------------- --
  2126.  
  2127.     do
  2128.         --
  2129.         -- we create the glow table first
  2130.         --
  2131.         local Glow = utils.newClass("Frame")
  2132.  
  2133.         function Glow:New(parent)
  2134.             local f = self:Bind(CreateFrame("Frame", nil, parent))
  2135.             f:SetAllPoints(parent)
  2136.             f:SetToplevel(true)
  2137.             f:Hide()
  2138.  
  2139.             f.animation = f:CreateGlowAnimation()
  2140.             f:SetScript("OnHide", f.OnHide)
  2141.  
  2142.             local icon = f:CreateTexture(nil, "OVERLAY")
  2143.             icon:SetTexture([[Interface\TargetingFrame\UI-TargetingFrame-Stealable]])
  2144.             icon:SetPoint("TOPLEFT", -7, 7)
  2145.             icon:SetPoint("BOTTOMRIGHT", 6, -6)
  2146.             icon:SetBlendMode("ADD")
  2147.  
  2148.             return f
  2149.         end
  2150.  
  2151.         do
  2152.             local function animation_OnFinished(self)
  2153.                 local parent = self:GetParent()
  2154.                 if parent:IsShown() then
  2155.                     parent:Hide()
  2156.                 end
  2157.             end
  2158.  
  2159.             function Glow:CreateGlowAnimation()
  2160.                 local g = self:CreateAnimationGroup()
  2161.                 g:SetLooping("REPEAT")
  2162.                 g:SetScript("OnFinished", animation_OnFinished)
  2163.  
  2164.                 local a1 = g:CreateAnimation("Alpha")
  2165.                 a1:SetChange(-1)
  2166.                 a1:SetDuration(.2)
  2167.                 a1:SetOrder(1)
  2168.  
  2169.                 local a2 = g:CreateAnimation("Alpha")
  2170.                 a2:SetChange(1)
  2171.                 a2:SetDuration(.2)
  2172.                 a2:SetOrder(2)
  2173.  
  2174.                 return g
  2175.             end
  2176.         end
  2177.  
  2178.         function Glow:OnHide()
  2179.             self.animation:Finish()
  2180.             self:Hide()
  2181.         end
  2182.  
  2183.         function Glow:Start(texture)
  2184.             if not self.animation:IsPlaying() then
  2185.                 self:Show()
  2186.                 self.animation:Play()
  2187.             end
  2188.         end
  2189.  
  2190.         -- register the effect
  2191.         do
  2192.             local glows =
  2193.                 setmetatable(
  2194.                 {},
  2195.                 {
  2196.                     __index = function(t, k)
  2197.                         local f = Glow:New(k)
  2198.                         t[k] = f
  2199.                         return f
  2200.                     end
  2201.                 }
  2202.             )
  2203.  
  2204.             addon:RegisterEffect {
  2205.                 id = "glow",
  2206.                 name = L["Glow"],
  2207.                 Run = function(self, cooldown)
  2208.                     local p = cooldown:GetParent()
  2209.                     if p then
  2210.                         glows[p]:Start()
  2211.                     end
  2212.                 end
  2213.             }
  2214.         end
  2215.     end
  2216.  
  2217.     -- --------------- pulse effect --------------- --
  2218.  
  2219.     do
  2220.         --
  2221.         -- we create the pulse table first
  2222.         --
  2223.         local Pulse = utils.newClass("Frame")
  2224.  
  2225.         function Pulse:New(parent)
  2226.             local f = self:Bind(CreateFrame("Frame", nil, parent))
  2227.             f:SetAllPoints(parent)
  2228.             f:SetToplevel(true)
  2229.             f:Hide()
  2230.  
  2231.             f.animation = f:CreatePulseAnimation()
  2232.             f:SetScript("OnHide", f.OnHide)
  2233.  
  2234.             local icon = f:CreateTexture(nil, "OVERLAY")
  2235.             icon:SetPoint("CENTER")
  2236.             icon:SetBlendMode("ADD")
  2237.             icon:SetAllPoints(f)
  2238.             f.icon = icon
  2239.  
  2240.             return f
  2241.         end
  2242.  
  2243.         do
  2244.             --
  2245.             -- trigger by the end of the animation
  2246.             --
  2247.             local function animation_OnFinished(self)
  2248.                 local parent = self:GetParent()
  2249.                 utils.hide(parent)
  2250.             end
  2251.  
  2252.             --
  2253.             -- creates the pulse animation
  2254.             --
  2255.             function Pulse:CreatePulseAnimation()
  2256.                 local g = self:CreateAnimationGroup()
  2257.                 g:SetLooping("NONE")
  2258.                 g:SetScript("OnFinished", animation_OnFinished)
  2259.  
  2260.                 local grow = g:CreateAnimation("scale")
  2261.                 grow:SetScale(1.5, 1.5)
  2262.                 grow:SetOrigin("CENTER", 0, 0)
  2263.                 grow:SetDuration(0.2)
  2264.                 grow:SetOrder(1)
  2265.  
  2266.                 local shrink = g:CreateAnimation("scale")
  2267.                 shrink:SetScale(-1.5, -1.5)
  2268.                 shrink:SetOrigin("CENTER", 0, 0)
  2269.                 shrink:SetDuration(0.2)
  2270.                 shrink:SetOrder(2)
  2271.  
  2272.                 return g
  2273.             end
  2274.         end
  2275.  
  2276.         --
  2277.         -- handles animation hide
  2278.         --
  2279.         function Pulse:OnHide()
  2280.             self.animation:Finish()
  2281.             self:Hide()
  2282.         end
  2283.  
  2284.         --
  2285.         -- starts the animation
  2286.         --
  2287.         function Pulse:Start(texture)
  2288.             if not self.animation:IsPlaying() then
  2289.                 local icon = self.icon
  2290.                 local r, g, b = icon:GetVertexColor()
  2291.                 icon:SetVertexColor(r, g, b, 0.7)
  2292.                 icon:SetTexture(texture:GetTexture())
  2293.                 -- fix the texture coordinates for a better look.
  2294.                 if not LBF then
  2295.                     icon:SetTexCoord(0.089, 0.9, 0.09, 0.9)
  2296.                 end
  2297.                 self:Show()
  2298.                 self.animation:Play()
  2299.             end
  2300.         end
  2301.  
  2302.         --
  2303.         -- we now register the pulse effect
  2304.         --
  2305.         do
  2306.             local pulses =
  2307.                 setmetatable(
  2308.                 {},
  2309.                 {
  2310.                     __index = function(t, k)
  2311.                         local f = Pulse:New(k)
  2312.                         t[k] = f
  2313.                         return f
  2314.                     end
  2315.                 }
  2316.             )
  2317.  
  2318.             local function getTexture(frame)
  2319.                 if not frame then
  2320.                     return
  2321.                 end
  2322.  
  2323.                 local icon = frame.icon or frame.texture
  2324.                 if not icon then
  2325.                     local name = frame:GetName()
  2326.                     icon = _G[name .. "Icon"] or _G[name .. "Texture"]
  2327.                 end
  2328.  
  2329.                 if icon and (icon.GetNormalTexture or icon.GetTexture) then
  2330.                     return icon
  2331.                 end
  2332.             end
  2333.  
  2334.             -- finish
  2335.             addon:RegisterEffect {
  2336.                 id = "pulse",
  2337.                 name = L["Pulse"],
  2338.                 Run = function(self, cooldown)
  2339.                     local p = cooldown:GetParent()
  2340.                     local texture = getTexture(p)
  2341.                     if texture then
  2342.                         pulses[p]:Start(texture)
  2343.                     end
  2344.                 end
  2345.             }
  2346.         end
  2347.     end
  2348.  
  2349.     -- --------------- shine effect --------------- --
  2350.  
  2351.     do
  2352.         --
  2353.         -- we create the shine table first
  2354.         --
  2355.         local Shine = utils.newClass("Frame")
  2356.  
  2357.         function Shine:New(parent)
  2358.             local f = self:Bind(CreateFrame("Frame", nil, parent))
  2359.             f:SetAllPoints(parent)
  2360.             f:SetToplevel(true)
  2361.             f:Hide()
  2362.  
  2363.             f.animation = f:CreateShineAnimation()
  2364.             f:SetScript("OnHide", f.OnHide)
  2365.  
  2366.             local icon = f:CreateTexture(nil, "OVERLAY")
  2367.             icon:SetPoint("CENTER")
  2368.             icon:SetBlendMode("ADD")
  2369.             icon:SetAllPoints(f)
  2370.             icon:SetTexture("Interface\\Cooldown\\star4")
  2371.  
  2372.             return f
  2373.         end
  2374.  
  2375.         do
  2376.             local function animation_OnFinished(self)
  2377.                 local parent = self:GetParent()
  2378.                 if parent:IsShown() then
  2379.                     parent:Hide()
  2380.                 end
  2381.             end
  2382.  
  2383.             function Shine:CreateShineAnimation()
  2384.                 local g = self:CreateAnimationGroup()
  2385.                 g:SetLooping("NONE")
  2386.                 g:SetScript("OnFinished", animation_OnFinished)
  2387.  
  2388.                 local startAlpha = g:CreateAnimation("Alpha")
  2389.                 startAlpha:SetChange(-1)
  2390.                 startAlpha:SetDuration(0)
  2391.                 startAlpha:SetOrder(0)
  2392.  
  2393.                 local grow = g:CreateAnimation("Scale")
  2394.                 grow:SetOrigin("CENTER", 0, 0)
  2395.                 grow:SetScale(2, 2)
  2396.                 grow:SetDuration(0.2)
  2397.                 grow:SetOrder(1)
  2398.  
  2399.                 local brighten = g:CreateAnimation("Alpha")
  2400.                 brighten:SetChange(1)
  2401.                 brighten:SetDuration(0.2)
  2402.                 brighten:SetOrder(1)
  2403.  
  2404.                 local shrink = g:CreateAnimation("Scale")
  2405.                 shrink:SetOrigin("CENTER", 0, 0)
  2406.                 shrink:SetScale(-2, -2)
  2407.                 shrink:SetDuration(0.2)
  2408.                 shrink:SetOrder(2)
  2409.  
  2410.                 local fade = g:CreateAnimation("Alpha")
  2411.                 fade:SetChange(-1)
  2412.                 fade:SetDuration(0.2)
  2413.                 fade:SetOrder(2)
  2414.  
  2415.                 return g
  2416.             end
  2417.         end
  2418.  
  2419.         function Shine:OnHide()
  2420.             self.animation:Finish()
  2421.             self:Hide()
  2422.         end
  2423.  
  2424.         function Shine:Start(texture)
  2425.             if not self.animation:IsPlaying() then
  2426.                 self:Show()
  2427.                 self.animation:Play()
  2428.             end
  2429.         end
  2430.  
  2431.         -- register the effect
  2432.         do
  2433.             local shines =
  2434.                 setmetatable(
  2435.                 {},
  2436.                 {
  2437.                     __index = function(t, k)
  2438.                         local f = Shine:New(k)
  2439.                         t[k] = f
  2440.                         return f
  2441.                     end
  2442.                 }
  2443.             )
  2444.  
  2445.             addon:RegisterEffect {
  2446.                 id = "shine",
  2447.                 name = L["Shine"],
  2448.                 Run = function(self, cooldown)
  2449.                     local p = cooldown:GetParent()
  2450.                     if p then
  2451.                         shines[p]:Start()
  2452.                     end
  2453.                 end
  2454.             }
  2455.         end
  2456.     end
  2457. end
  2458.  
  2459. --------------------------------------------------------------------------
  2460. -- Cooldown count functions
  2461.  
  2462. do
  2463.     local Cooldown_SetTimer, Cooldown_OnUpdate
  2464.  
  2465.     do
  2466.         -- table of cooldown font options
  2467.         local fontOptions = {
  2468.             face = _G.STANDARD_TEXT_FONT,
  2469.             size = 18,
  2470.             scales = {1.3, 1.2, .8, .6}
  2471.         }
  2472.  
  2473.         -- list of counters and their frames
  2474.         local counters, counterFrames = {}, {}
  2475.         local ceil, floor = _G.math.ceil, _G.math.floor
  2476.  
  2477.         --
  2478.         -- used to return the final text to display as well
  2479.         -- as the color used for it.
  2480.         --
  2481.         local function Cooldown_GetTimeFormat(timeleft)
  2482.             local str1, str2
  2483.             local color = {1, 1, 1, 1}
  2484.  
  2485.             if timeleft >= 86400 then
  2486.                 str1 = format("%d", ceil(timeleft / 86400))
  2487.                 str2 = str1 .. ((strlen(str1) > 2 and "\n") or "") .. "d"
  2488.                 color = {0.7, 0.7, 0.7, 1}
  2489.             elseif timeleft >= 3600 then
  2490.                 str1 = format("%d", ceil(timeleft / 3600))
  2491.                 str2 = str1 .. ((strlen(str1) > 2 and "\n") or "") .. "h"
  2492.                 color = {0.7, 0.7, 0.7, 1}
  2493.             elseif timeleft >= 60 then
  2494.                 str1 = format("%d", ceil(timeleft / 60))
  2495.                 str2 = str1 .. ((strlen(str1) > 2 and "\n") or "") .. "m"
  2496.                 color = {1, 1, 1, 1}
  2497.             else
  2498.                 local s = ceil(timeleft)
  2499.                 str1 = format("%d", s)
  2500.                 str2 = str1
  2501.                 color = {1, 1, 0, 1}
  2502.  
  2503.                 if s <= 5.5 then
  2504.                     color = {1, 0, 0, 1}
  2505.                 end
  2506.             end
  2507.             return str2, strlen(str1), color
  2508.         end
  2509.  
  2510.         --
  2511.         -- used to draw the final cooldown frame
  2512.         --
  2513.         local function Cooldown_DrawCooldown(frame)
  2514.             local text, len, color = frame.text, frame.length, frame.color
  2515.             if len > 4 then
  2516.                 len = 4
  2517.             end
  2518.             frame:SetScale(fontOptions.scales[len] * (frame.cooldown:GetParent():GetWidth() / ActionButton1:GetWidth()))
  2519.             local counterText = _G[frame:GetName() .. "Text"]
  2520.             utils.setText(counterText, frame.text)
  2521.             counterText:SetTextColor(unpack(frame.color))
  2522.         end
  2523.  
  2524.         --
  2525.         -- handle font visual formatting
  2526.         --
  2527.         local function Cooldown_UpdateFont(fname)
  2528.             local frame, text = _G[fname], _G[fname .. "Text"]
  2529.             text:SetShadowOffset(1, -1)
  2530.             text:SetFont(fontOptions.face, fontOptions.size, "OUTLINE")
  2531.  
  2532.             -- dram the the cooldown if visible and it has a time left
  2533.             if frame:IsVisible() and frame.timeleft then
  2534.                 frame.text, frame.length, frame.color = Cooldown_GetTimeFormat(frame.timeleft)
  2535.                 Cooldown_DrawCooldown(frame)
  2536.             end
  2537.         end
  2538.  
  2539.         --
  2540.         -- this function is hooked to all actions bars including
  2541.         -- all icons created by our addon.
  2542.         --
  2543.         function Cooldown_SetTimer(self, start, duration, enable)
  2544.             if self.mark == 0 then
  2545.                 return
  2546.             end
  2547.  
  2548.             local name = self:GetName()
  2549.  
  2550.             if not self.mark then
  2551.                 if not name then
  2552.                     self.mark = 0
  2553.                     return
  2554.                 end
  2555.  
  2556.                 self.mark =
  2557.                     (((self:GetParent():GetWidth() >= 28 or self:GetParent():GetParent():GetName() == "WatchFrameLines") and
  2558.                     1) or
  2559.                     0)
  2560.             end
  2561.  
  2562.             if self.mark == 1 then
  2563.                 local fname = "KTrackerCooldown" .. name .. "Timer"
  2564.  
  2565.                 if start > 0 and duration > 1.5 and enable > 0 then
  2566.                     if not _G[fname] then
  2567.                         local special = (ButtonFacade or Bartender4)
  2568.  
  2569.                         local frame =
  2570.                             CreateFrame("Frame", fname, ((not special) and self) or nil, "KTrackerCooldownTemplate")
  2571.                         frame:SetPoint("CENTER", self:GetParent(), "CENTER", 0, 0)
  2572.  
  2573.                         if special then
  2574.                             frame:SetFrameStrata(self:GetParent():GetFrameStrata())
  2575.                         end
  2576.  
  2577.                         frame:SetFrameLevel(self:GetFrameLevel() + 5)
  2578.                         frame:SetToplevel(true)
  2579.                         frame.cooldown = self
  2580.                         self.counter = frame
  2581.  
  2582.                         self:HookScript(
  2583.                             "OnShow",
  2584.                             function(self)
  2585.                                 self.counter:Show()
  2586.                             end
  2587.                         )
  2588.                         self:HookScript(
  2589.                             "OnHide",
  2590.                             function(self)
  2591.                                 self.counter:Hide()
  2592.                             end
  2593.                         )
  2594.  
  2595.                         tinsert(counterFrames, fname)
  2596.                         Cooldown_UpdateFont(fname)
  2597.                         frame:Show()
  2598.                     end
  2599.  
  2600.                     -- cache the counter
  2601.                     counters[name] = {
  2602.                         start = start,
  2603.                         duration = duration - 1,
  2604.                         enable = enable
  2605.                     }
  2606.                 else
  2607.                     utils.setText(_G[fname .. "Text"], "")
  2608.                     counters[name] = nil
  2609.                 end
  2610.             end
  2611.         end
  2612.  
  2613.         --
  2614.         -- attached to addon's main frame in order to handle
  2615.         -- spells and icons cooldown timers.
  2616.         --
  2617.         function Cooldown_OnUpdate(self, elapsed)
  2618.             if utils.update(self, "KTracker_EventFrame", 0.1, elapsed) then
  2619.                 local currentTime = GetTime()
  2620.                 for k, v in pairs(counters) do
  2621.                     local timeleft = v.start + v.duration - currentTime
  2622.  
  2623.                     local fname = "KTrackerCooldown" .. k .. "Timer"
  2624.                     local counter = _G[fname]
  2625.                     local counterText = _G[fname .. "Text"]
  2626.                     counter.timeleft = timeleft
  2627.  
  2628.                     if timeleft > 0 and v.enable > 0 then
  2629.                         if _G[k]:IsVisible() then
  2630.                             counter.text, counter.length, counter.color = Cooldown_GetTimeFormat(timeleft)
  2631.  
  2632.                             if counter.text ~= counterText:GetText() then
  2633.                                 Cooldown_DrawCooldown(counter)
  2634.                             end
  2635.                         else
  2636.                             utils.setText(counterText, "")
  2637.                         end
  2638.                     else
  2639.                         counters[k] = nil
  2640.                         utils.setText(counterText, "")
  2641.                     end
  2642.                 end
  2643.             end
  2644.         end
  2645.     end
  2646.  
  2647.     do
  2648.         -- a flag so we don't hook twice
  2649.         local hooked = false
  2650.  
  2651.         --
  2652.         -- called to check if the player is already using a cooldown
  2653.         -- count addon or not. If he is not/she is not, we set our own
  2654.         --
  2655.         function addon:Initialize(all)
  2656.             if not hooked then
  2657.                 hooked = true
  2658.                 -- implement our cooldown count:
  2659.                 if not hasOmniCC then
  2660.                     hooksecurefunc("CooldownFrame_SetTimer", Cooldown_SetTimer)
  2661.                     mainFrame:SetScript("OnUpdate", Cooldown_OnUpdate)
  2662.                 end
  2663.             end
  2664.             if not all then
  2665.                 self:Load()
  2666.             end
  2667.         end
  2668.     end
  2669. end
  2670.  
  2671. --------------------------------------------------------------------------
  2672. -- minimap button
  2673.  
  2674. do
  2675.     local menu, dragMode
  2676.     local abs, sqrt = _G.math.abs, _G.math.sqrt
  2677.  
  2678.     --
  2679.     -- handles the minimap button moving
  2680.     --
  2681.     local function MoveButton(self)
  2682.         local centerX, centerY = Minimap:GetCenter()
  2683.         local x, y = GetCursorPosition()
  2684.         x, y = x / self:GetEffectiveScale() - centerX, y / self:GetEffectiveScale() - centerY
  2685.  
  2686.         if dragMode == "free" then
  2687.             self:ClearAllPoints()
  2688.             self:SetPoint("CENTER", x, y)
  2689.         else
  2690.             centerX, centerY = abs(x), abs(y)
  2691.             centerX, centerY =
  2692.                 (centerX / sqrt(centerX ^ 2 + centerY ^ 2)) * 80,
  2693.                 (centerY / sqrt(centerX ^ 2 + centerY ^ 2)) * 80
  2694.             centerX = x < 0 and -centerX or centerX
  2695.             centerY = y < 0 and -centerY or centerY
  2696.             self:ClearAllPoints()
  2697.             self:SetPoint("CENTER", centerX, centerY)
  2698.         end
  2699.     end
  2700.  
  2701.     --
  2702.     -- button OnMouseDown
  2703.     --
  2704.     local function Minimap_OnMouseDown(self, button)
  2705.         if IsAltKeyDown() then
  2706.             dragMode = "free"
  2707.             self:SetScript("OnUpdate", MoveButton)
  2708.         elseif IsShiftKeyDown() then
  2709.             dragMode = nil
  2710.             self:SetScript("OnUpdate", MoveButton)
  2711.         end
  2712.     end
  2713.  
  2714.     --
  2715.     -- button OnMouseUp
  2716.     --
  2717.     local function Minimap_OnMouseUp(self, button)
  2718.         self:SetScript("OnUpdate", nil)
  2719.     end
  2720.  
  2721.     --
  2722.     -- button OnClick
  2723.     --
  2724.     local function Minimap_OnClick(self, button)
  2725.         if IsShiftKeyDown() or IsAltKeyDown() then
  2726.             return
  2727.         elseif button == "RightButton" then
  2728.             addon:Config()
  2729.         elseif button == "LeftButton" then
  2730.             addon:Toggle()
  2731.         end
  2732.     end
  2733.  
  2734.     --
  2735.     -- called OnLoad the minimap button
  2736.     --
  2737.     function addon:OnLoadMinimap(btn)
  2738.         if not btn then
  2739.             return
  2740.         end
  2741.         minimapButton = btn
  2742.         minimapButton:RegisterForClicks("LeftButtonUp", "RightButtonUp")
  2743.         minimapButton:SetUserPlaced(true)
  2744.         minimapButton:SetScript("OnMouseDown", Minimap_OnMouseDown)
  2745.         minimapButton:SetScript("OnMouseUp", Minimap_OnMouseUp)
  2746.         minimapButton:SetScript("OnClick", Minimap_OnClick)
  2747.         utils.setTooltip(
  2748.             minimapButton,
  2749.             {
  2750.                 L["|cffffd700Left-Click|r to lock/unlock."],
  2751.                 L["|cffffd700Right-Click|r to access settings."],
  2752.                 L["|cffffd700Shift+Click|r to move."],
  2753.                 L["|cffffd700Alt+Click|r for free drag and drop."]
  2754.             },
  2755.             nil,
  2756.             titleString
  2757.         )
  2758.     end
  2759.  
  2760.     --
  2761.     -- toggles Minimap button visibility
  2762.     --
  2763.     function addon:ToggleMinimapButton()
  2764.         KTrackerCharDB.minimap = not KTrackerCharDB.minimap
  2765.         self.minimap = KTrackerCharDB.minimap
  2766.         utils.showHide(minimapButton, self.minimap)
  2767.     end
  2768.  
  2769.     --
  2770.     -- hide the minimap button
  2771.     --
  2772.     function addon:HideMinimapButton()
  2773.         utils.hide(minimapButton)
  2774.     end
  2775. end
  2776.  
  2777. --------------------------------------------------------------------------
  2778. -- Config Frame
  2779.  
  2780. do
  2781.     --
  2782.     -- config table, ui frame and frame name
  2783.     --
  2784.     local Config = {}
  2785.     local UIFrame, frameName
  2786.  
  2787.     --
  2788.     -- required locals
  2789.     --
  2790.     local LocalizeUIFrame, localized
  2791.     local updateInterval, UpdateUIFrame = 0.05
  2792.     local FetchGroups, SaveGroup, fetched
  2793.     local FillFields, ResetFields
  2794.  
  2795.     --
  2796.     -- objects used to hold Temporary data or default data
  2797.     --
  2798.     local tempObj, defObj = {},
  2799.         {
  2800.             id = "",
  2801.             enabled = true,
  2802.             name = "",
  2803.             spec = 0,
  2804.             columns = 4,
  2805.             rows = 1,
  2806.             hspacing = 0,
  2807.             vspacing = 0,
  2808.             combat = false,
  2809.             created = 0
  2810.         }
  2811.  
  2812.     --
  2813.     -- useful flags to update the frame
  2814.     --
  2815.     local isEdit, currentId, validId
  2816.  
  2817.     --
  2818.     -- frame input fields
  2819.     --
  2820.     local newID, newName, newSpec, newCols, newRows
  2821.     local newHSpacing, newHSpacingBox
  2822.     local newVSpacing, newVSpacingBox
  2823.     local newEnabled, newCombat
  2824.  
  2825.     --
  2826.     -- frame buttons
  2827.     --
  2828.     local btnSave, btnCancel
  2829.     local btnReset, btnLock, btnSync, btnMinimap
  2830.     local btnColumnsLeft, btnColumnsRight
  2831.     local btnRowsLeft, btnRowsRight
  2832.  
  2833.     --
  2834.     -- used to fill frame fields when the user wants
  2835.     -- to modify an existing group of icons
  2836.     --
  2837.     do
  2838.         local function GroupsUnlockhighlight(except)
  2839.             for i, _ in ipairs(groups) do
  2840.                 if except and except == i then
  2841.                     _G[frameName .. "GroupBtn" .. i]:LockHighlight()
  2842.                 else
  2843.                     _G[frameName .. "GroupBtn" .. i]:UnlockHighlight()
  2844.                 end
  2845.             end
  2846.         end
  2847.  
  2848.         function FillFields(obj)
  2849.             if obj then
  2850.                 ResetFields()
  2851.  
  2852.                 GroupsUnlockhighlight(newID:GetNumber())
  2853.  
  2854.                 newName:SetText(obj.name)
  2855.                 newCols:SetText(obj.columns)
  2856.                 newRows:SetText(obj.rows)
  2857.                 newHSpacing:SetValue(obj.hspacing or 0)
  2858.                 newVSpacing:SetValue(obj.vspacing or 0)
  2859.                 newEnabled:SetChecked(obj.enabled)
  2860.                 newCombat:SetChecked(obj.combat)
  2861.  
  2862.                 -- spec
  2863.                 if obj.spec == 1 then
  2864.                     L_UIDropDownMenu_SetText(newSpec, L["Primary"])
  2865.                 elseif obj.spec == 2 then
  2866.                     L_UIDropDownMenu_SetText(newSpec, L["Secondary"])
  2867.                 else
  2868.                     L_UIDropDownMenu_SetText(newSpec, L["Both"])
  2869.                 end
  2870.                 isEdit = true
  2871.             end
  2872.         end
  2873.  
  2874.         --
  2875.         -- called after saving fields to reset them
  2876.         --
  2877.         function ResetFields()
  2878.             tempObj = utils.deepCopy(defObj)
  2879.             newName:SetText("")
  2880.             newName:ClearFocus()
  2881.             L_UIDropDownMenu_SetText(newSpec, L["Both"])
  2882.             newCols:SetText(tempObj.columns)
  2883.             newRows:SetText(tempObj.rows)
  2884.             newHSpacingBox:SetNumber(0)
  2885.             newVSpacingBox:SetNumber(0)
  2886.             newHSpacingBox:ClearFocus()
  2887.             newVSpacingBox:ClearFocus()
  2888.             newHSpacing:SetValue(tempObj.hspacing)
  2889.             newVSpacing:SetValue(tempObj.vspacing)
  2890.             newEnabled:SetChecked(tempObj.enabled)
  2891.             newCombat:SetChecked(tempObj.combat)
  2892.             GroupsUnlockhighlight()
  2893.         end
  2894.     end
  2895.  
  2896.     --
  2897.     -- handles the edit/save button actions
  2898.     --
  2899.     function SaveGroup()
  2900.         -- name is required though...
  2901.         if tempObj.name == "" then
  2902.             addon:PrintError(L["The group name is required"])
  2903.             return
  2904.         end
  2905.  
  2906.         -- we'll see if maxGroups should be changed or not
  2907.         if numGroups >= def.maxGroups then
  2908.             addon:PrintError(L["You have reached the maximum allowed groups number"])
  2909.             ResetFields()
  2910.             return
  2911.         end
  2912.  
  2913.         Group:Save(tempObj, tempObj.id)
  2914.  
  2915.         -- we reset fields, reload the addon then the list
  2916.         ResetFields()
  2917.         addon:Initialize()
  2918.         fetched, isEdit = false, false
  2919.         newID:SetNumber(0)
  2920.     end
  2921.  
  2922.     do
  2923.         --
  2924.         -- called whenever the groups list requires update
  2925.         --
  2926.         local function ResetList()
  2927.             local index = 1
  2928.             local btn = _G[frameName .. "GroupBtn" .. index]
  2929.             while btn ~= nil do
  2930.                 btn:Hide()
  2931.                 index = index + 1
  2932.                 btn = _G[frameName .. "GroupBtn" .. index]
  2933.             end
  2934.         end
  2935.  
  2936.         do
  2937.             local Group_OpenMenu, menu
  2938.             local current = 0
  2939.             do
  2940.                 local function Group_OptionToggle()
  2941.                     local DB = KTrackerDB.groups[current]
  2942.                     if DB then
  2943.                         local db = KTrackerCharDB.groups[DB.created]
  2944.                         if db then
  2945.                             db[this.value] = this.checked
  2946.                             addon:Initialize()
  2947.                             fetched, isEdit = false, false
  2948.                             return
  2949.                         end
  2950.                     end
  2951.                     L_CloseDropDownMenus()
  2952.                 end
  2953.  
  2954.                 local function Group_OptionChoose(self, arg1)
  2955.                     local DB = KTrackerDB.groups[current]
  2956.                     if DB then
  2957.                         local db = KTrackerCharDB.groups[DB.created]
  2958.                         local success = false
  2959.                         if db[this.value] ~= nil then
  2960.                             db[this.value] = arg1
  2961.                             if this.value == "position" then
  2962.                                 db.scale = def.CharDBGroup.scale
  2963.                                 local fname = format(holderGroup, current)
  2964.                                 local f = _G[fname]
  2965.                                 f:ClearAllPoints()
  2966.                                 f:SetPoint("CENTER", "UIParent", "CENTER", 0, 0)
  2967.                             end
  2968.                             success = true
  2969.                         end
  2970.  
  2971.                         if success then
  2972.                             addon:Initialize()
  2973.                             fetched, isEdit = false, false
  2974.                             return
  2975.                         end
  2976.                     end
  2977.                     L_CloseDropDownMenus()
  2978.                 end
  2979.  
  2980.                 function Group_OpenMenu(self)
  2981.                     local id = self:GetID()
  2982.                     local DB = KTrackerDB.groups[id]
  2983.                     if not DB then
  2984.                         return
  2985.                     end
  2986.                     local db = KTrackerCharDB.groups[DB.created]
  2987.                     if not db then
  2988.                         return
  2989.                     end
  2990.                     current = id
  2991.  
  2992.                     if not menu then
  2993.                         menu = CreateFrame("Frame", "KTrackerGroupMenu")
  2994.                     end
  2995.  
  2996.                     menu.displayMode = "MENU"
  2997.                     menu.initialize = function(self, level)
  2998.                         local info = L_UIDropDownMenu_CreateInfo()
  2999.                         level = level or 1
  3000.  
  3001.                         if level == 2 then
  3002.                             if L_UIDROPDOWNMENU_MENU_VALUE == "spec" then
  3003.                                 info.text = L["Both"]
  3004.                                 info.checked = (db.spec == 0)
  3005.                                 info.value = "spec"
  3006.                                 info.arg1 = 0
  3007.                                 info.func = Group_OptionChoose
  3008.                                 L_UIDropDownMenu_AddButton(info, level)
  3009.                                 wipe(info)
  3010.  
  3011.                                 info.text = L["Primary"]
  3012.                                 info.checked = (db.spec == 1)
  3013.                                 info.value = "spec"
  3014.                                 info.arg1 = 1
  3015.                                 info.func = Group_OptionChoose
  3016.                                 L_UIDropDownMenu_AddButton(info, level)
  3017.                                 wipe(info)
  3018.  
  3019.                                 info.text = L["Secondary"]
  3020.                                 info.checked = (db.spec == 2)
  3021.                                 info.value = "spec"
  3022.                                 info.arg1 = 2
  3023.                                 info.func = Group_OptionChoose
  3024.                                 L_UIDropDownMenu_AddButton(info, level)
  3025.                                 wipe(info)
  3026.                             elseif L_UIDROPDOWNMENU_MENU_VALUE == "reset" then
  3027.                                 info.text = L["Icons"]
  3028.                                 info.func = function()
  3029.                                     StaticPopup_Show("KTRACKER_DIALOG_CLEAR", DB.name, nil, id)
  3030.                                 end
  3031.                                 L_UIDropDownMenu_AddButton(info, level)
  3032.                                 wipe(info)
  3033.  
  3034.                                 info.text = L["Position"]
  3035.                                 info.value = "position"
  3036.                                 info.arg1 = {}
  3037.                                 info.func = Group_OptionChoose
  3038.                                 L_UIDropDownMenu_AddButton(info, level)
  3039.                                 wipe(info)
  3040.                             end
  3041.                             return
  3042.                         end
  3043.  
  3044.                         info.text = DB.name
  3045.                         info.isTitle = true
  3046.                         info.notCheckable = true
  3047.                         L_UIDropDownMenu_AddButton(info, level)
  3048.                         wipe(info)
  3049.  
  3050.                         info.text = L["Edit"]
  3051.                         info.notCheckable = true
  3052.                         info.func = function()
  3053.                             local obj = utils.deepCopy(DB)
  3054.                             utils.mixTable(obj, db)
  3055.                             newID:SetNumber(id)
  3056.                             FillFields(obj)
  3057.                         end
  3058.                         L_UIDropDownMenu_AddButton(info, level)
  3059.                         wipe(info)
  3060.  
  3061.                         info.text = DELETE
  3062.                         info.notCheckable = true
  3063.                         info.func = function()
  3064.                             StaticPopup_Show("KTRACKER_DIALOG_DELETE", DB.name, nil, id)
  3065.                         end
  3066.                         L_UIDropDownMenu_AddButton(info, level)
  3067.                         wipe(info)
  3068.  
  3069.                         info.text = L["Duplicate"]
  3070.                         info.notCheckable = true
  3071.                         info.func = function()
  3072.                             -- copy group and complete missing data
  3073.                             local group = utils.deepCopy(def.group)
  3074.                             utils.mixTable(group, DB)
  3075.                             utils.mixTable(group, db)
  3076.  
  3077.                             -- set few key things before proceeding
  3078.                             group.created = nil
  3079.                             group.icons = utils.deepCopy(DB.icons)
  3080.                             group.position = utils.deepCopy(db.position)
  3081.                             group.style = utils.deepCopy(db.style)
  3082.  
  3083.                             -- save and load the group if succeeded
  3084.                             local id = Group:Save(group)
  3085.                             if id then
  3086.                                 Group:Load(id)
  3087.                                 fetched, isEdit = false, false
  3088.                             end
  3089.                         end
  3090.                         L_UIDropDownMenu_AddButton(info, level)
  3091.                         wipe(info)
  3092.  
  3093.                         if addon.sync then
  3094.                             info.text = L["Share"]
  3095.                             info.notCheckable = true
  3096.                             info.func = function()
  3097.                                 StaticPopup_Show("KTRACKER_DIALOG_SHARE_SEND", nil, nil, id)
  3098.                             end
  3099.                             L_UIDropDownMenu_AddButton(info, level)
  3100.                             wipe(info)
  3101.                         end
  3102.  
  3103.                         info.disabled = true
  3104.                         L_UIDropDownMenu_AddButton(info, level)
  3105.                         wipe(info)
  3106.  
  3107.                         info.text = L["Group Status"]
  3108.                         info.isTitle = true
  3109.                         info.notCheckable = true
  3110.                         L_UIDropDownMenu_AddButton(info, level)
  3111.                         wipe(info)
  3112.  
  3113.                         info.text = L["Enabled"]
  3114.                         info.checked = db.enabled
  3115.                         info.value = "enabled"
  3116.                         info.func = Group_OptionToggle
  3117.                         info.keepShownOnClick = true
  3118.                         info.isNotRadio = true
  3119.                         L_UIDropDownMenu_AddButton(info, level)
  3120.                         wipe(info)
  3121.  
  3122.                         info.text = L["Combat"]
  3123.                         info.checked = db.combat
  3124.                         info.value = "combat"
  3125.                         info.func = Group_OptionToggle
  3126.                         info.keepShownOnClick = true
  3127.                         info.isNotRadio = true
  3128.                         L_UIDropDownMenu_AddButton(info, level)
  3129.                         wipe(info)
  3130.  
  3131.                         info.text = L["Talents Spec"]
  3132.                         info.value = "spec"
  3133.                         info.hasArrow = true
  3134.                         info.notCheckable = true
  3135.                         info.keepShownOnClick = true
  3136.                         L_UIDropDownMenu_AddButton(info, level)
  3137.                         wipe(info)
  3138.  
  3139.                         info.disabled = true
  3140.                         L_UIDropDownMenu_AddButton(info, level)
  3141.                         wipe(info)
  3142.  
  3143.                         info.text = RESET
  3144.                         info.value = "reset"
  3145.                         info.hasArrow = true
  3146.                         info.notCheckable = true
  3147.                         L_UIDropDownMenu_AddButton(info, level)
  3148.                         wipe(info)
  3149.                     end
  3150.  
  3151.                     L_ToggleDropDownMenu(1, nil, menu, "cursor", 0, 0)
  3152.                 end
  3153.             end
  3154.  
  3155.             local function Group_OnClick(self, button)
  3156.                 if button == "RightButton" then
  3157.                     Group_OpenMenu(self)
  3158.                 elseif button == "MiddleButton" then
  3159.                     local id = self:GetID()
  3160.                     local DB = KTrackerDB.groups[id]
  3161.                     if not DB then
  3162.                         return
  3163.                     end
  3164.                     local db = KTrackerCharDB.groups[DB.created]
  3165.                     if db then
  3166.                         db.enabled = not db.enabled
  3167.                         addon:Initialize()
  3168.                         fetched, isEdit = false, false
  3169.                     end
  3170.                 end
  3171.             end
  3172.  
  3173.             --
  3174.             -- fetches all groups and put then on the list
  3175.             --
  3176.             function FetchGroups()
  3177.                 if not groups or fetched then
  3178.                     return
  3179.                 end
  3180.                 ResetList()
  3181.  
  3182.                 _G[frameName .. "CountLabel"]:SetText(L:F("Groups: %d/%d", numGroups, def.maxGroups))
  3183.  
  3184.                 local scrollFrame = _G[frameName .. "ScrollFrame"]
  3185.                 local scrollChild = _G[frameName .. "ScrollFrameScrollChild"]
  3186.                 scrollChild:SetHeight(scrollFrame:GetHeight())
  3187.                 scrollChild:SetWidth(scrollFrame:GetWidth())
  3188.  
  3189.                 local height = 0
  3190.                 local num = #groups
  3191.  
  3192.                 for i = num, 1, -1 do
  3193.                     local group = groups[i]
  3194.                     local btnName = frameName .. "GroupBtn" .. i
  3195.                     local btn =
  3196.                         _G[btnName] or CreateFrame("Button", btnName, scrollChild, "KTrackerGroupButtonTemplate")
  3197.                     btn:Show()
  3198.                     btn:SetID(i)
  3199.                     btn:SetPoint("TOPLEFT", scrollChild, "TOPLEFT", 0, -height)
  3200.                     btn:SetPoint("RIGHT", scrollChild, "RIGHT", 0, 0)
  3201.                     height = height + btn:GetHeight()
  3202.  
  3203.                     -- Set data
  3204.                     _G[btnName .. "ID"]:SetText(i)
  3205.                     _G[btnName .. "Name"]:SetText(group.name)
  3206.                     _G[btnName .. "Size"]:SetText(group.columns .. "x" .. group.rows)
  3207.  
  3208.                     local spec = L["Both"]
  3209.                     if group.spec > 0 then
  3210.                         spec = (group.spec == 1) and L["Primary"] or L["Secondary"]
  3211.                     end
  3212.                     _G[btnName .. "Spec"]:SetText(spec)
  3213.  
  3214.                     utils.setTextIf(_G[btnName .. "Combat"], YES, NO, group.combat)
  3215.                     utils.setTextIf(_G[btnName .. "Enabled"], YES, NO, group.enabled)
  3216.  
  3217.                     btn:SetScript("OnClick", Group_OnClick)
  3218.                 end
  3219.  
  3220.                 fetched = true
  3221.             end
  3222.         end
  3223.     end
  3224.  
  3225.     do
  3226.         --
  3227.         -- simple dummy function that sets text of the spec menu
  3228.         --
  3229.         local function ChooseSpec()
  3230.             local specs = {L["Both"], L["Primary"], L["Secondary"]}
  3231.             L_UIDropDownMenu_SetText(newSpec, specs[this.value + 1])
  3232.         end
  3233.  
  3234.         --
  3235.         -- initializes the spec dropdown menu
  3236.         --
  3237.         local function InitializeSpecDropdown(self, level)
  3238.             L_UIDropDownMenu_JustifyText(self, "LEFT")
  3239.             if level == 1 then
  3240.                 local info = L_UIDropDownMenu_CreateInfo()
  3241.  
  3242.                 -- both specs
  3243.                 info.text = L["Both"]
  3244.                 info.value = 0
  3245.                 info.func = ChooseSpec
  3246.                 info.notCheckable = true
  3247.                 L_UIDropDownMenu_AddButton(info)
  3248.                 wipe(info)
  3249.  
  3250.                 -- primary
  3251.                 info.text = L["Primary"]
  3252.                 info.value = 1
  3253.                 info.func = ChooseSpec
  3254.                 info.notCheckable = true
  3255.                 L_UIDropDownMenu_AddButton(info)
  3256.                 wipe(info)
  3257.  
  3258.                 -- Secondary
  3259.                 info.text = L["Secondary"]
  3260.                 info.value = 2
  3261.                 info.func = ChooseSpec
  3262.                 info.notCheckable = true
  3263.                 L_UIDropDownMenu_AddButton(info)
  3264.                 wipe(info)
  3265.  
  3266.                 return
  3267.             end
  3268.         end
  3269.  
  3270.         do
  3271.             local function ColumnsLeft_OnClick(self, button)
  3272.                 tempObj.columns = tempObj.columns - 1
  3273.                 if tempObj.columns <= 0 then
  3274.                     tempObj.columns = 1
  3275.                 end
  3276.                 newCols:SetText(tempObj.columns)
  3277.             end
  3278.  
  3279.             local function ColumnsRight_OnClick(self, button)
  3280.                 tempObj.columns = tempObj.columns + 1
  3281.                 if tempObj.columns >= def.maxColumns then
  3282.                     tempObj.columns = def.maxColumns
  3283.                 end
  3284.                 newCols:SetText(tempObj.columns)
  3285.             end
  3286.  
  3287.             local function RowsLeft_OnClick(self, button)
  3288.                 tempObj.rows = tempObj.rows - 1
  3289.                 if tempObj.rows <= 0 then
  3290.                     tempObj.rows = 1
  3291.                 end
  3292.                 newRows:SetText(tempObj.rows)
  3293.             end
  3294.  
  3295.             local function RowsRight_OnClick(self, button)
  3296.                 tempObj.rows = tempObj.rows + 1
  3297.                 if tempObj.rows >= def.maxRows then
  3298.                     tempObj.rows = def.maxRows
  3299.                 end
  3300.                 newRows:SetText(tempObj.rows)
  3301.             end
  3302.  
  3303.             local function SpacingBox_OnEnterPressed(self)
  3304.                 local value = self:GetNumber()
  3305.                 if value >= 250 then
  3306.                     value = 250
  3307.                 end
  3308.                 if self:GetName():find("HSpacing") ~= nil then
  3309.                     newHSpacing:SetValue(value)
  3310.                 else
  3311.                     newVSpacing:SetValue(value)
  3312.                 end
  3313.                 self:ClearFocus()
  3314.             end
  3315.  
  3316.             local function SyncBtn_OnClick(self)
  3317.                 KTrackerDB.sync = (self:GetChecked() == 1)
  3318.                 addon.sync = KTrackerDB.sync
  3319.                 L_CloseDropDownMenus()
  3320.             end
  3321.  
  3322.             local function MinimapBtn_OnClick(self)
  3323.                 addon:ToggleMinimapButton()
  3324.                 L_CloseDropDownMenus()
  3325.             end
  3326.  
  3327.             --
  3328.             -- this function is called only once on loading.
  3329.             -- it simply localizes the frame and registers some
  3330.             -- required elements.
  3331.             --
  3332.             function LocalizeUIFrame()
  3333.                 if localized then
  3334.                     return
  3335.                 end
  3336.  
  3337.                 -- hold frame form inputs
  3338.                 newID = _G[frameName .. "ID"]
  3339.                 newName = _G[frameName .. "Name"]
  3340.                 newSpec = _G[frameName .. "SpecDropDown"]
  3341.                 newCols = _G[frameName .. "ColumnsText"]
  3342.                 newRows = _G[frameName .. "RowsText"]
  3343.                 newHSpacing = _G[frameName .. "HSpacing"]
  3344.                 newVSpacing = _G[frameName .. "VSpacing"]
  3345.                 newHSpacingBox = _G[frameName .. "HSpacingValue"]
  3346.                 newVSpacingBox = _G[frameName .. "VSpacingValue"]
  3347.                 newEnabled = _G[frameName .. "Enabled"]
  3348.                 newCombat = _G[frameName .. "Combat"]
  3349.  
  3350.                 L_UIDropDownMenu_Initialize(newSpec, InitializeSpecDropdown, nil, 1)
  3351.  
  3352.                 -- frame buttons
  3353.                 btnSave = _G[frameName .. "SaveBtn"]
  3354.                 btnCancel = _G[frameName .. "CancelBtn"]
  3355.                 btnReset = _G[frameName .. "ResetBtn"]
  3356.                 btnLock = _G[frameName .. "LockBtn"]
  3357.  
  3358.                 btnColumnsLeft = _G[frameName .. "ColumnsLeft"]
  3359.                 btnColumnsRight = _G[frameName .. "ColumnsRight"]
  3360.                 btnRowsLeft = _G[frameName .. "RowsLeft"]
  3361.                 btnRowsRight = _G[frameName .. "RowsRight"]
  3362.  
  3363.                 if GetLocale() ~= "enUS" and GetLocale() ~= "enGB" then
  3364.                     btnLock:SetText(L["Lock"])
  3365.                     _G[frameName .. "NameLabel"]:SetText(L["Group Name"])
  3366.                     _G[frameName .. "SpecLabel"]:SetText(L["Talents Spec"])
  3367.                     _G[frameName .. "ColumnsLabel"]:SetText(L["Columns"])
  3368.                     _G[frameName .. "RowsLabel"]:SetText(L["Rows"])
  3369.                     _G[frameName .. "HSpacingLabel"]:SetText(L["Spacing H"])
  3370.                     _G[frameName .. "VSpacingLabel"]:SetText(L["Spacing V"])
  3371.                     _G[frameName .. "EnabledLabel"]:SetText(L["Enable Group"])
  3372.                     _G[frameName .. "CombatLabel"]:SetText(L["Only in Combat"])
  3373.                     _G[frameName .. "HeaderSize"]:SetText(L["Size"])
  3374.                     _G[frameName .. "HeaderSpec"]:SetText(L["Spec"])
  3375.                     _G[frameName .. "HeaderCombat"]:SetText(L["Combat"])
  3376.                     _G[frameName .. "HeaderEnabled"]:SetText(L["Enabled"])
  3377.                 end
  3378.  
  3379.                 _G[frameName .. "Title"]:SetText(
  3380.                     "|cfff58cbaK|r|caaf49141Tracker|r - Made with love by |cfff58cbaKader|r (|cffffffffNovus Ordo|r @ |cff996019Warmane|r-Icecrown)"
  3381.                 )
  3382.  
  3383.                 -- prepare our temporary group object
  3384.                 tempObj = utils.deepCopy(defObj)
  3385.                 newCols:SetText(tempObj.columns)
  3386.                 newRows:SetText(tempObj.rows)
  3387.  
  3388.                 -- left and right columns buttons functions
  3389.                 btnColumnsLeft:SetScript("OnClick", ColumnsLeft_OnClick)
  3390.                 btnColumnsRight:SetScript("OnClick", ColumnsRight_OnClick)
  3391.                 btnRowsLeft:SetScript("OnClick", RowsLeft_OnClick)
  3392.                 btnRowsRight:SetScript("OnClick", RowsRight_OnClick)
  3393.  
  3394.                 -- name and spacing on enter processed
  3395.                 newName:SetScript("OnEnterPressed", SaveGroup)
  3396.                 newHSpacingBox:SetScript("OnEnterPressed", SpacingBox_OnEnterPressed)
  3397.                 newVSpacingBox:SetScript("OnEnterPressed", SpacingBox_OnEnterPressed)
  3398.  
  3399.                 -- buttons scripts
  3400.                 btnSave:SetScript("OnClick", SaveGroup)
  3401.                 btnCancel:SetScript("OnClick", ResetFields)
  3402.                 btnReset:SetScript(
  3403.                     "OnClick",
  3404.                     function(self)
  3405.                         StaticPopup_Show("KTRACKER_DIALOG_RESET")
  3406.                     end
  3407.                 )
  3408.  
  3409.                 -- Sync button
  3410.                 local syncName = frameName .. "SyncBtn"
  3411.                 btnSync = _G[syncName]
  3412.                 _G[syncName .. "Text"]:SetText(L["Enable Sync"])
  3413.                 utils.setTooltip(
  3414.                     btnSync,
  3415.                     L["Check this you want to enable group sharing."],
  3416.                     "ANCHOR_CURSOR",
  3417.                     L["Enable Sync"]
  3418.                 )
  3419.                 btnSync:SetScript("OnClick", SyncBtn_OnClick)
  3420.  
  3421.                 -- Minimap button
  3422.                 local mmbName = frameName .. "MinimapBtn"
  3423.                 btnMinimap = _G[mmbName]
  3424.                 _G[mmbName .. "Text"]:SetText(L["Minimap Button"])
  3425.                 utils.setTooltip(
  3426.                     btnMinimap,
  3427.                     L["Check this you want show the minimap button."],
  3428.                     "ANCHOR_CURSOR",
  3429.                     L["Minimap Button"]
  3430.                 )
  3431.                 btnMinimap:SetScript("OnClick", MinimapBtn_OnClick)
  3432.  
  3433.                 localized = true
  3434.             end
  3435.         end
  3436.     end
  3437.  
  3438.     --
  3439.     -- handles the config frame OnUpdate event
  3440.     --
  3441.     function UpdateUIFrame(self, elapsed)
  3442.         if utils.update(self, self:GetName(), updateInterval, elapsed) then
  3443.             FetchGroups()
  3444.             btnSync:SetChecked(addon.sync)
  3445.             btnMinimap:SetChecked(addon.minimap)
  3446.  
  3447.             -- group id
  3448.             local id = newID:GetNumber()
  3449.             tempObj.id = (id > 0) and id or nil
  3450.  
  3451.             -- disable few things
  3452.             tempObj.name = newName:GetText():trim()
  3453.             local hasObject = (tempObj.name ~= "")
  3454.  
  3455.             -- spec
  3456.             if tempObj.name == "" then
  3457.                 L_UIDropDownMenu_DisableDropDown(newSpec)
  3458.             else
  3459.                 L_UIDropDownMenu_EnableDropDown(newSpec)
  3460.             end
  3461.  
  3462.             local spec = L_UIDropDownMenu_GetText(newSpec)
  3463.             if spec == L["Primary"] then
  3464.                 tempObj.spec = 1
  3465.             elseif spec == L["Secondary"] then
  3466.                 tempObj.spec = 2
  3467.             else
  3468.                 L_UIDropDownMenu_SetText(newSpec, L["Both"])
  3469.                 tempObj.spec = 0
  3470.             end
  3471.  
  3472.             tempObj.columns = tonumber(newCols:GetText())
  3473.             tempObj.rows = tonumber(newRows:GetText())
  3474.             tempObj.hspacing = newHSpacing:GetValue()
  3475.             tempObj.vspacing = newVSpacing:GetValue()
  3476.  
  3477.             if not newHSpacingBox:HasFocus() then
  3478.                 newHSpacingBox:SetText(tempObj.hspacing)
  3479.             end
  3480.             if not newVSpacingBox:HasFocus() then
  3481.                 newVSpacingBox:SetText(tempObj.vspacing)
  3482.             end
  3483.  
  3484.             tempObj.enabled = (newEnabled:GetChecked() == 1)
  3485.             tempObj.combat = (newCombat:GetChecked() == 1)
  3486.  
  3487.             -- columns and rows
  3488.             newCols:SetText(tempObj.columns)
  3489.             newRows:SetText(tempObj.rows)
  3490.  
  3491.             -- change lock/unlock button text.
  3492.             utils.setTextIf(btnLock, L["Unlock"], L["Lock"], addon.locked)
  3493.  
  3494.             -- enable/disable or show/hide items.
  3495.             if hasObject then
  3496.                 newHSpacingBox:Show()
  3497.                 newVSpacingBox:Show()
  3498.                 btnSave:Enable()
  3499.                 btnCancel:Enable()
  3500.                 newEnabled:Enable()
  3501.                 newCombat:Enable()
  3502.                 utils.enableDisable(btnColumnsLeft, tempObj.columns > 1)
  3503.                 utils.enableDisable(btnColumnsRight, tempObj.columns < def.maxColumns)
  3504.                 utils.enableDisable(btnRowsLeft, tempObj.rows > 1)
  3505.                 utils.enableDisable(btnRowsRight, tempObj.rows < def.maxRows)
  3506.             else
  3507.                 newHSpacingBox:Hide()
  3508.                 newVSpacingBox:Hide()
  3509.                 btnSave:Disable()
  3510.                 btnCancel:Disable()
  3511.                 newEnabled:Disable()
  3512.                 newCombat:Disable()
  3513.                 btnColumnsLeft:Disable()
  3514.                 btnColumnsRight:Disable()
  3515.                 btnRowsLeft:Disable()
  3516.                 btnRowsRight:Disable()
  3517.             end
  3518.         end
  3519.     end
  3520.  
  3521.     --
  3522.     -- called when the configuration frame loads the first time
  3523.     --
  3524.     function addon:ConfigOnLoad(frame)
  3525.         if not frame then
  3526.             return
  3527.         end
  3528.         UIFrame = frame
  3529.         frameName = frame:GetName()
  3530.         frame:RegisterForDrag("LeftButton")
  3531.         LocalizeUIFrame()
  3532.         frame:SetScript("OnUpdate", UpdateUIFrame)
  3533.         frame:SetScript("OnHide", ResetFields)
  3534.         utils.hide(frame)
  3535.         tinsert(UISpecialFrames, frameName)
  3536.     end
  3537.  
  3538.     --
  3539.     -- toggles the configuration frame
  3540.     --
  3541.     function addon:Config()
  3542.         utils.toggle(UIFrame)
  3543.     end
  3544.  
  3545.     --
  3546.     -- delete a group pop up dialog
  3547.     --
  3548.     StaticPopupDialogs["KTRACKER_DIALOG_DELETE"] = {
  3549.         text = L["|caaf49141%s|r : Are you sure you want to delete this group?"],
  3550.         button1 = DELETE,
  3551.         button2 = CANCEL,
  3552.         timeout = 0,
  3553.         whileDead = true,
  3554.         hideOnEscape = true,
  3555.         OnShow = function(self, id)
  3556.             utils.highlight(_G[frameName .. "GroupBtn" .. id], true)
  3557.         end,
  3558.         OnHide = function(self, id)
  3559.             utils.highlight(_G[frameName .. "GroupBtn" .. id], false)
  3560.         end,
  3561.         OnAccept = function(self, id)
  3562.             if id and KTrackerDB.groups[id] then
  3563.                 local DB = KTrackerDB.groups[id]
  3564.                 if DB then
  3565.                     local groupName = format(holderGroup, id)
  3566.                     utils.hide(_G[groupName])
  3567.                     tremove(KTrackerDB.groups, id)
  3568.                     KTrackerCharDB.groups[DB.created] = nil
  3569.                     fetched, isEdit = false, false
  3570.                     addon:Initialize()
  3571.                 end
  3572.             end
  3573.             self:Hide()
  3574.         end
  3575.     }
  3576.  
  3577.     --
  3578.     -- clear all group icons pop up dialog
  3579.     --
  3580.     StaticPopupDialogs["KTRACKER_DIALOG_CLEAR"] = {
  3581.         text = L["|caaf49141%s|r : Are you sure you want to clear all icons?"],
  3582.         button1 = YES,
  3583.         button2 = CANCEL,
  3584.         timeout = 0,
  3585.         whileDead = true,
  3586.         hideOnEscape = true,
  3587.         OnShow = function(self, id)
  3588.             utils.highlight(_G[frameName .. "GroupBtn" .. id], true)
  3589.         end,
  3590.         OnHide = function(self, id)
  3591.             utils.highlight(_G[frameName .. "GroupBtn" .. id], false)
  3592.         end,
  3593.         OnAccept = function(self, id)
  3594.             if id and KTrackerDB.groups[id] then
  3595.                 L_CloseDropDownMenus()
  3596.                 wipe(KTrackerDB.groups[id].icons)
  3597.                 addon:Initialize()
  3598.                 fetched, isEdit = false, false
  3599.             end
  3600.             self:Hide()
  3601.         end
  3602.     }
  3603.  
  3604.     --
  3605.     -- reset all addon to default
  3606.     --
  3607.     StaticPopupDialogs["KTRACKER_DIALOG_RESET"] = {
  3608.         text = L[
  3609.             "You are about to reset everything, all groups, icons and their settings will be deleted. \nDo you want to continue?"
  3610.         ],
  3611.         button1 = YES,
  3612.         button2 = CANCEL,
  3613.         timeout = 0,
  3614.         whileDead = true,
  3615.         hideOnEscape = true,
  3616.         OnAccept = function(self)
  3617.             -- wipe tables?
  3618.             wipe(KTrackerDB)
  3619.             wipe(KTrackerCharDB)
  3620.             wipe(groups)
  3621.  
  3622.             -- hide groups
  3623.             for i = 1, def.maxGroups do
  3624.                 local groupName = format(holderGroup, i)
  3625.                 utils.hide(_G[groupName])
  3626.             end
  3627.             LoadDatabase()
  3628.             addon:Initialize()
  3629.             fetched, isEdit = false, false
  3630.             self:Hide()
  3631.         end
  3632.     }
  3633.  
  3634.     --
  3635.     -- synchronization handler
  3636.     --
  3637.     do
  3638.         --
  3639.         -- holds messages per character
  3640.         --
  3641.         local data = {}
  3642.  
  3643.         --
  3644.         -- send new group dialog
  3645.         --
  3646.         local function ProcessShare(id, channel, target)
  3647.             local DB = KTrackerDB.groups[id]
  3648.  
  3649.             -- double check
  3650.             if not DB then
  3651.                 return
  3652.             end
  3653.  
  3654.             local str = "GroupStart\t" .. id .. "\t" .. DB.name .. "\t" .. DB.columns .. "\t" .. DB.rows
  3655.             addon:Sync(str, channel, target)
  3656.  
  3657.             local num = DB.columns * DB.rows
  3658.             for i = 1, num do
  3659.                 local icon = DB.icons[i]
  3660.                 local msg = "GroupIcon\t" .. id .. "\t" .. icon.name .. "\t" .. tostring(icon.enabled)
  3661.                 msg = msg .. "\t" .. icon.type .. "\t" .. icon.subtype .. "\t" .. tostring(icon.when)
  3662.                 msg = msg .. "\t" .. icon.unit .. "\t" .. tostring(icon.mine) .. "\t" .. tostring(icon.timer)
  3663.                 msg = msg .. "\t" .. icon.effect
  3664.                 addon:Sync(msg, channel, target)
  3665.  
  3666.                 -- final icon? Tell to stop
  3667.                 if i == num then
  3668.                     addon:Sync("GroupEnd\t" .. id .. "\t" .. DB.name, channel, target)
  3669.                 end
  3670.             end
  3671.         end
  3672.  
  3673.         --
  3674.         -- handles all sync actions
  3675.         --
  3676.         local function SyncHandler(msg, channel, sender)
  3677.             if not msg then
  3678.                 return
  3679.             end
  3680.  
  3681.             local command, other = strsplit("\t", msg, 2)
  3682.  
  3683.             -- sending share request
  3684.             if command == "GroupShare" and other:trim() ~= "" then
  3685.                 local str = sender .. "\t" .. other
  3686.                 StaticPopup_Show("KTRACKER_DIALOG_SHARE_RECEIVE", sender, nil, str)
  3687.                 return
  3688.             end
  3689.  
  3690.             -- accepting the group
  3691.             if command == "GroupAccept" and other:trim() ~= "" then
  3692.                 local id = tonumber(other)
  3693.                 for i, obj in ipairs(KTrackerDB.groups) do
  3694.                     if i == id then
  3695.                         ProcessShare(id, "WHISPER", sender)
  3696.                         break
  3697.                     end
  3698.                 end
  3699.                 return
  3700.             end
  3701.  
  3702.             -- receiving a new group:
  3703.             if command == "GroupStart" then
  3704.                 local id, name, columns, rows = strsplit("\t", other, 4)
  3705.                 columns, rows = tonumber(columns), tonumber(rows)
  3706.  
  3707.                 data = data or {}
  3708.                 data[sender] = data[sender] or {}
  3709.                 data[sender][id] = data[sender][id] or {}
  3710.  
  3711.                 local group = utils.deepCopy(def.DBGroup)
  3712.                 group.id = nil
  3713.                 group.name = name
  3714.                 group.enabled = true
  3715.                 group.columns = columns
  3716.                 group.rows = rows
  3717.  
  3718.                 data[sender][id] = group
  3719.                 return
  3720.             end
  3721.  
  3722.             -- receiving group icons
  3723.             if command == "GroupIcon" then
  3724.                 -- we make sure the player started sending.
  3725.                 if not data[sender] then
  3726.                     return
  3727.                 end
  3728.  
  3729.                 local id, name, enabled, iconType, subtype, when, unit, mine, timer, effect = strsplit("\t", other, 10)
  3730.  
  3731.                 if not data[sender][id] then
  3732.                     return
  3733.                 end
  3734.  
  3735.                 local icon = utils.deepCopy(def.icon)
  3736.                 icon.enabled = (enabled == "true")
  3737.                 icon.name = name
  3738.                 icon.type = iconType
  3739.                 icon.subtype = subtype
  3740.                 icon.when = tonumber(when)
  3741.                 icon.unit = unit
  3742.                 icon.mine = (mine == "true")
  3743.                 icon.timer = (timer == "true")
  3744.                 icon.effect = effect
  3745.  
  3746.                 tinsert(data[sender][id].icons, icon)
  3747.                 return
  3748.             end
  3749.  
  3750.             if command == "GroupEnd" then
  3751.                 local id, name = strsplit("\t", other, 2)
  3752.                 if not id then
  3753.                     return
  3754.                 end
  3755.                 if data and (not data[sender] or not data[sender][id]) then
  3756.                     return
  3757.                 end
  3758.  
  3759.                 Group:Save(data[sender][id])
  3760.                 wipe(data[sender])
  3761.  
  3762.                 addon:PrintSuccess(L:F('"%s" group successfully imported.', name))
  3763.  
  3764.                 addon:Initialize()
  3765.                 fetched, isEdit = false, false
  3766.                 return
  3767.             end
  3768.         end
  3769.  
  3770.         syncHandlers[syncPrefix] = SyncHandler
  3771.  
  3772.         --
  3773.         -- prompt dialog to share group
  3774.         --
  3775.         StaticPopupDialogs["KTRACKER_DIALOG_SHARE_SEND"] = {
  3776.             text = L[
  3777.                 "Enter the name of the character to share the group with, or leave empty to share it with your party/raid members."
  3778.             ],
  3779.             button1 = SEND_LABEL,
  3780.             button2 = CANCEL,
  3781.             hasEditBox = true,
  3782.             timeout = 0,
  3783.             whileDead = true,
  3784.             hideOnEscape = true,
  3785.             OnShow = function(self, id)
  3786.                 utils.highlight(_G[frameName .. "GroupBtn" .. id], true)
  3787.                 _G[self:GetName() .. "EditBox"]:SetFocus()
  3788.             end,
  3789.             OnHide = function(self, id)
  3790.                 utils.highlight(_G[frameName .. "GroupBtn" .. id], false)
  3791.                 local box = _G[self:GetName() .. "WideEditBox"]
  3792.                 box:SetText("")
  3793.                 box:ClearFocus()
  3794.             end,
  3795.             OnAccept = function(self, id)
  3796.                 local target = _G[self:GetName() .. "EditBox"]:GetText():trim()
  3797.                 local channel = "WHISPER"
  3798.                 if target == "" then
  3799.                     channel, target = nil, nil
  3800.                 end
  3801.                 if KTrackerDB.groups[id] then
  3802.                     addon:Sync("GroupShare\t" .. id, channel, target)
  3803.                 end
  3804.                 self:Hide()
  3805.             end,
  3806.             EditBoxOnEnterPressed = function(self, id)
  3807.                 local target = _G[self:GetName()]:GetText():trim()
  3808.                 local channel, data = "WHISPER", ""
  3809.                 if target == "" then
  3810.                     channel, target = nil, nil
  3811.                 end
  3812.                 if KTrackerDB.groups[id] then
  3813.                     addon:Sync("GroupShare\t" .. id, channel, target)
  3814.                 end
  3815.                 self:GetParent():Hide()
  3816.             end,
  3817.             EditBoxOnEscapePressed = function(self)
  3818.                 self:SetText("")
  3819.                 self:ClearFocus()
  3820.                 self:GetParent():Hide()
  3821.             end
  3822.         }
  3823.  
  3824.         --
  3825.         -- prompt dialog to ask share permission
  3826.         --
  3827.         StaticPopupDialogs["KTRACKER_DIALOG_SHARE_RECEIVE"] = {
  3828.             text = L["%s wants to share a group of icons with you. Do you want to import it?"],
  3829.             button1 = ACCEPT,
  3830.             button2 = CANCEL,
  3831.             timeout = 0,
  3832.             whileDead = true,
  3833.             hideOnEscape = true,
  3834.             OnAccept = function(self, str)
  3835.                 if str then
  3836.                     local sender, id = strsplit("\t", str)
  3837.                     id = tonumber(id)
  3838.                     if sender and id > 0 then
  3839.                         addon:Sync("GroupAccept\t" .. id, "WHISPER", sender)
  3840.                     end
  3841.                 end
  3842.                 self:Hide()
  3843.             end
  3844.         }
  3845.     end
  3846. end
  3847.  
Add Comment
Please, Sign In to add comment