-- Neuron is a World of Warcraft® user interface addon. -- Copyright (c) 2017-2021 Britt W. Yazel -- Copyright (c) 2006-2014 Connor H. Chenoweth -- This code is licensed under the MIT license (see LICENSE for details) local _, addonTable = ... local Neuron = addonTable.Neuron ---@class NeuronButton : CheckButton @define Button as inheriting from CheckButton local Button = setmetatable({}, {__index = CreateFrame("CheckButton")}) --this is the metatable for our button object Neuron.Button = Button local Skin = LibStub("Masque", true) local L = LibStub("AceLocale-3.0"):GetLocale("Neuron") LibStub("AceBucket-3.0"):Embed(Button) LibStub("AceEvent-3.0"):Embed(Button) LibStub("AceTimer-3.0"):Embed(Button) LibStub("AceHook-3.0"):Embed(Button) local ButtonEditor = addonTable.overlay.ButtonEditor local DEFAULT_VIRTUAL_KEY = "LeftButton" local NEURON_VIRTUAL_KEY = "Hotkey" ---Constructor: Create a new Neuron Button object (this is the base object for all Neuron button types) ---@param bar Bar @Bar Object this button will be a child of ---@param buttonID number @Button ID that this button will be assigned ---@param baseObj Button @Base object class for this specific button ---@param barClass string @Class type for the bar the button will be on ---@param objType string @Type of object this button will be ---@param template string @The template name that this frame will derive from ---@return NeuronButton @ A newly created Button object function Button.new(bar, buttonID, baseObj, barClass, objType, template) local newButton local newButtonName = bar:GetName().."_"..objType..buttonID if _G[newButtonName] then --try to reuse a current frame if it exists instead of making a new one newButton = _G[newButtonName] else newButton = CreateFrame("CheckButton", newButtonName, bar, template) --create the new button frame using the desired parameters setmetatable(newButton, {__index = baseObj}) end --crosslink the bar and button for easy referencing bar.buttons[buttonID] = newButton newButton.bar = bar newButton.class = barClass newButton.id = buttonID newButton.objType = objType if not bar.data.buttons[buttonID] then --if the database for a bar doesn't exist (because it's a new bar) make a new table bar.data.buttons[buttonID] = {} end newButton.DB = bar.data.buttons[buttonID] --set our button database table as the DB for our object --this is a hack to add some unique information to an object so it doesn't get wiped from the database if newButton.DB.config then newButton.DB.config.date = date("%m/%d/%y %H:%M:%S") end return newButton end ------------------------------------------------- -----Base Methods that all buttons have---------- ---These will often be overwritten per bar type-- ------------------------------------------------ function Button.ChangeSelectedButton(newButton) if newButton == Neuron.currentButton then return elseif newButton and Neuron.currentButton then if Neuron.currentButton.bar ~= newButton.bar then local bar = Neuron.currentButton.bar if bar.handler:GetAttribute("assertstate") then bar.handler:SetAttribute("state-"..bar.handler:GetAttribute("assertstate"), bar.handler:GetAttribute("activestate") or "homestate") end newButton.bar.handler:SetAttribute("fauxstate", bar.handler:GetAttribute("activestate")) end ButtonEditor.deactivate(Neuron.currentButton.editFrame) ButtonEditor.activate(newButton.editFrame) Neuron.currentButton = newButton Neuron.currentBar = newButton.bar elseif newButton and not Neuron.currentButton then ButtonEditor.activate(newButton.editFrame) Neuron.currentButton = newButton Neuron.currentBar = newButton.bar else -- not newButton and Neuron.currentButton ButtonEditor.deactivate(Neuron.currentButton.editFrame) Neuron.currentButton = nil end end function Button:CancelCooldownTimer(stopAnimation) --cleanup so on state changes the cooldowns don't persist if self:TimeLeft(self.Cooldown.cooldownTimer) ~= 0 then self:CancelTimer(self.Cooldown.cooldownTimer) end if self:TimeLeft(self.Cooldown.cooldownUpdateTimer) ~= 0 then self:CancelTimer(self.Cooldown.cooldownUpdateTimer) end self.Countdown:SetText("") self.Cooldown.showCountdownTimer = false self.Cooldown.showCountdownAlpha = false --clear previous sweeping cooldown animations if stopAnimation then CooldownFrame_Clear(self.Cooldown) --clear the cooldown frame end self:UpdateVisibility() end function Button:SetCooldownTimer(start, duration, enable, modrate, showCountdownTimer, color1, color2, showCountdownAlpha, charges, maxCharges) if not self.isShown then --if the button isn't shown, don't do set any cooldowns --if there's currently a timer, cancel it self:CancelCooldownTimer(true) return end if start and start > 0 and duration > 0 and enable > 0 then if duration > 2 then --sets non GCD cooldowns if charges and charges > 0 and maxCharges > 1 then self.Cooldown:SetDrawSwipe(false); CooldownFrame_Set(self.Cooldown, start, duration, enable, true, modrate) --set clock style cooldown animation. Show Draw Edge. else self.Cooldown:SetDrawSwipe(true); CooldownFrame_Set(self.Cooldown, start, duration, enable, true, modrate) --set clock style cooldown animation for ability cooldown. Show Draw Edge. end else --sets GCD cooldowns self.Cooldown:SetDrawSwipe(true); CooldownFrame_Set(self.Cooldown, start, duration, enable, false, modrate) --don't show the Draw Edge for the GCD end --this is only for abilities that have CD's >4 sec. Any less than that and we don't want to track the CD with text or alpha, just with the standard animation if duration >= Neuron.TIMERLIMIT then --if spells have a cooldown less than 4sec then don't show a full cooldown if showCountdownTimer or showCountdownAlpha then --only set a timer if we explicitely want to (this saves CPU for a lot of people) --set a local variable to the boolean state of either Timer or the Alpha self.Cooldown.showCountdownTimer = showCountdownTimer self.Cooldown.showCountdownAlpha = showCountdownAlpha self.Cooldown.charges = charges or 0 --used to know if we should set alpha on the button (if cdAlpha is enabled) immediately, or if we need to wait for charges to run out --clear old timer before starting a new one if self:TimeLeft(self.Cooldown.cooldownTimer) ~= 0 then self:CancelTimer(self.Cooldown.cooldownTimer) end --Get the remaining time left so when we re-call the timer when switching back to a state it has the correct time left instead of the full time local timeleft = duration-(GetTime()-start) --safety check in case some timeleft value comes back ridiculously long. This happened once after a weird game glitch, it came back as like 42000000. We should cap it at 1 day max (even that's overkill) if timeleft > 86400 then timeleft = 86400 end --set timer that is both our cooldown counter, but also the cancels the repeating updating timer at the end self.Cooldown.cooldownTimer = self:ScheduleTimer(function() self:CancelTimer(self.Cooldown.cooldownUpdateTimer) end, timeleft + 1) --add 1 to the length of the timer to keep it going for 1 second once the spell cd is over (to fully finish the animations/alpha transition) --clear old timer before starting a new one if self:TimeLeft(self.Cooldown.cooldownUpdateTimer) ~= 0 then self:CancelTimer(self.Cooldown.cooldownUpdateTimer) end --schedule a repeating timer that is physically keeping track of the countdown and switching the alpha and count text self.Cooldown.cooldownUpdateTimer = self:ScheduleRepeatingTimer("CooldownCounterUpdate", 0.20) self.Cooldown.normalcolor = color1 self.Cooldown.expirecolor = color2 else self.Cooldown.showCountdownTimer = false self.Cooldown.showCountdownAlpha = false end else self:CancelCooldownTimer(false) end else self:CancelCooldownTimer(true) end end ---this function is called by a repeating timer set in SetCooldownTimer every 0.2sec, which is autoGmatically is set to terminate 1sec after the cooldown timer ends ---this function's job is to overlay the countdown text on a button and set the button's cooldown alpha function Button:CooldownCounterUpdate() local coolDown, formatted, size local normalcolor = self.Cooldown.normalcolor local expirecolor = self.Cooldown.expirecolor coolDown = self:TimeLeft(self.Cooldown.cooldownTimer) - 1 --subtract 1 from the timer because we added 1 in SetCooldownTimer to keep the timer runing for 1 extra second after the spell if self.Cooldown.showCountdownTimer then --check if flag is set, otherwise skip if coolDown < 1 then if coolDown <= 0 then self.Countdown:SetText("") self.Cooldown.expirecolor = nil self.Cooldown.cdsize = nil elseif coolDown > 0 then if self.Cooldown.alphafade then self.Cooldown:SetAlpha(coolDown) end end else if coolDown >= 86400 then --append a "d" if the timer is longer than 1 day formatted = string.format( "%.0f", coolDown/86400) formatted = formatted.."d" size = self:GetWidth()*0.3 self.Countdown:SetTextColor(normalcolor[1], normalcolor[2], normalcolor[3]) elseif coolDown >= 3600 then --append a "h" if the timer is longer than 1 hour formatted = string.format( "%.0f",coolDown/3600) formatted = formatted.."h" size = self:GetWidth()*0.3 self.Countdown:SetTextColor(normalcolor[1], normalcolor[2], normalcolor[3]) elseif coolDown >= 60 then --append a "m" if the timer is longer than 1 min formatted = string.format( "%.0f",coolDown/60) formatted = formatted.."m" size = self:GetWidth()*0.3 self.Countdown:SetTextColor(normalcolor[1], normalcolor[2], normalcolor[3]) elseif coolDown >=6 then --this is the 'normal' countdown text state formatted = string.format( "%.0f",coolDown) size = self:GetWidth()*0.45 self.Countdown:SetTextColor(normalcolor[1], normalcolor[2], normalcolor[3]) elseif coolDown < 6 then --this is the countdown text state but with the text larger and set to the expire color (usually red) formatted = string.format( "%.0f",coolDown) size = self:GetWidth()*0.6 if expirecolor then self.Countdown:SetTextColor(expirecolor[1], expirecolor[2], expirecolor[3]) expirecolor = nil end end if not self.Cooldown.cdsize or self.Cooldown.cdsize ~= size then self.Countdown:SetFont(STANDARD_TEXT_FONT, size, "OUTLINE") self.Cooldown.cdsize = size end self.Countdown:SetText(formatted) end end if self.Cooldown.showCountdownAlpha and self.Cooldown.charges == 0 then --check if flag is set and if charges are nil or zero, otherwise skip if self.Cooldown.showCountdownAlpha and self.Cooldown.charges == 0 then --check if flag is set and if charges are nil or zero, otherwise skip if coolDown > 0 then self:SetAlpha(0.2) else self:SetAlpha(1) end else self:SetAlpha(1) end end end function Button:LoadDataFromDatabase(curSpec, curState) self.config = self.DB.config self.keys = self.DB.keys if self.class ~= "ActionBar" then self.data = self.DB.data else self.statedata = self.DB[curSpec] --all of the states for a given spec self.data = self.statedata[curState] --loads a single state of a single spec into self.data for state, data in pairs(self.statedata) do self:SetAttribute(state.."-macro_Text", data.macro_Text) self:SetAttribute(state.."-actionID", data.actionID) end end end function Button:SetDefaults(defaults) if not defaults then return end if defaults.config then for k, v in pairs(defaults.config) do self.DB.config[k] = v end end if defaults.keys then for k, v in pairs(defaults.keys) do self.DB.keys[k] = v end end end function Button:SetSkinned(flyout) if Skin then local btnData = { Normal = self.Normal, Icon = self.Icon, HotKey = self.Hotkey, Count = self.Count, Name = self.Name, Border = self.Border, Shine = self.Shine, Cooldown = self.Cooldown, AutoCastable = self.AutoCastable, Checked = self.Checked, Pushed = self.Pushed, Disabled = self:GetDisabledTexture(), Highlight = self.Highlight, } if flyout then Skin:Group("Neuron", self.anchor.bar.data.name):AddButton(self, btnData, "Action") else Skin:Group("Neuron", self.bar.data.name):AddButton(self, btnData, "Action") end end end function Button:HasAction() if self.actionID then if self.actionID == 0 then return true elseif HasAction(self.actionID) then return true elseif self.class == "PetBar" and GetPetActionInfo(self.actionID) then return true end elseif self.spell or self.item then return true else return false end end ----------------------------------------------------------------------------------------- ------------------------------------- Update Functions ---------------------------------- ----------------------------------------------------------------------------------------- --- Applies binding to button function Button:ApplyBindings() local virtualKey ---checks if the button is a Neuron action or a special Blizzard action (such as a zone ability) ---this is necessary because Blizzard buttons usually won't work and can give very weird results ---if clicked with a virtual key other than the default "LeftButton" if self.class == "ActionBar" then virtualKey = NEURON_VIRTUAL_KEY else virtualKey = DEFAULT_VIRTUAL_KEY end if self:IsVisible() or self:GetParent():GetAttribute("concealed") then self.keys.hotKeys:gsub("[^:]+", function(key) SetOverrideBindingClick(self, self.keys.hotKeyPri, key, self:GetName(), virtualKey) end) end if not InCombatLockdown() then self:SetAttribute("hotkeypri", self.keys.hotKeyPri) self:SetAttribute("hotkeys", self.keys.hotKeys) end self.Hotkey:SetText(Button.hotKeyText(self.keys.hotKeys:match("^:([^:]+)") or "")) if self.bar:GetShowBindText() then self.Hotkey:Show() else self.Hotkey:Hide() end end -- spell in action, extra, pet, zone, -- actionID in action, extra, pet, -- spellID in action, extra, zone -- macroequipmentset used in action -- item in action -- macro_BlizzMacro in action function Button:UpdateAll() self:UpdateData() self:UpdateIcon() self:UpdateStatus() self:UpdateCooldown() self:UpdateUsable() end function Button:UpdateData() -- empty -- end function Button:UpdateIcon() -- empty -- end function Button:UpdateNormalTexture() local actionTexture, noactionTexture if self.__MSQ_NormalSkin and self.__MSQ_NormalSkin.Texture then actionTexture = self.__MSQ_NormalSkin.Texture noactionTexture = self.__MSQ_NormalSkin.Texture.EmptyTexture else actionTexture = "Interface\\Buttons\\UI-Quickslot2" noactionTexture = "Interface\\Buttons\\UI-Quickslot" end if self:HasAction() then self:SetNormalTexture(actionTexture or "") self:GetNormalTexture():SetVertexColor(1,1,1,1) else self:SetNormalTexture(noactionTexture or "") self:GetNormalTexture():SetVertexColor(1,1,1,0.5) end end function Button:UpdateVisibility() if self.isShown or Neuron.barEditMode or (Neuron.buttonEditMode and self.editFrame) or (Neuron.bindingMode and self.keybindFrame) then self.isShown = true else self.isShown = false end if self.isShown then self:SetAlpha(1) else self:SetAlpha(0) end end --- Returns the text value of a keybind --- @param key string @The key to look up --- @return string @The text value for the key function Button.hotKeyText(key) local keytext if key:find("Button") then keytext = key:gsub("([Bb][Uu][Tt][Tt][Oo][Nn])(%d+)","m%2") elseif key:find("NUMPAD") then keytext = key:gsub("NUMPAD","n") keytext = keytext:gsub("DIVIDE","/") keytext = keytext:gsub("MULTIPLY","*") keytext = keytext:gsub("MINUS","-") keytext = keytext:gsub("PLUS","+") keytext = keytext:gsub("DECIMAL",".") elseif key:find("MOUSEWHEEL") then keytext = key:gsub("MOUSEWHEEL","mw") keytext = keytext:gsub("UP","U") keytext = keytext:gsub("DOWN","D") else keytext = key end keytext = keytext:gsub("ALT%-","a") keytext = keytext:gsub("CTRL%-","c") keytext = keytext:gsub("SHIFT%-","s") keytext = keytext:gsub("INSERT","Ins") keytext = keytext:gsub("DELETE","Del") keytext = keytext:gsub("HOME","Home") keytext = keytext:gsub("END","End") keytext = keytext:gsub("PAGEUP","PgUp") keytext = keytext:gsub("PAGEDOWN","PgDn") keytext = keytext:gsub("BACKSPACE","Bksp") keytext = keytext:gsub("SPACE","Spc") return keytext end ----------------------------------------------------------------------------------------- ------------------------------------- Set Count/Charge ---------------------------------- ----------------------------------------------------------------------------------------- function Button:UpdateCount() if self.actionID then self:UpdateActionCount() elseif self.spell then self:UpdateSpellCount() elseif self.item then self:UpdateItemCount() else self.Count:SetText("") end end ---Updates the buttons "count", i.e. the spell charges function Button:UpdateSpellCount() local charges, maxCharges = GetSpellCharges(self.spell) local count = GetSpellCount(self.spell) if maxCharges and maxCharges > 1 then self.Count:SetText(charges) elseif count and count > 0 then self.Count:SetText(count) else self.Count:SetText("") end end ---Updates the buttons "count", i.e. the item stack size function Button:UpdateItemCount() local count = GetItemCount(self.item,nil,true) if count and count > 1 then self.Count:SetText(count) else self.Count:SetText("") end end function Button:UpdateActionCount() local count = GetActionCount(self.actionID) if count and count > 0 then self.Count:SetText(count) else self.Count:SetText("") end end ----------------------------------------------------------------------------------------- ------------------------------------- Set Cooldown -------------------------------------- ----------------------------------------------------------------------------------------- function Button:UpdateCooldown() if self.actionID then self:UpdateActionCooldown() elseif self.spell then self:UpdateSpellCooldown() elseif self.item then self:UpdateItemCooldown() else --this is super important for removing CD's from empty buttons, like when switching states. You don't want the CD from one state to show on a different state. self:CancelCooldownTimer(true) end end function Button:UpdateSpellCooldown() if self.spell and self.isShown then local start, duration, enable, modrate = GetSpellCooldown(self.spell) local charges, maxCharges, chStart, chDuration, chargemodrate = GetSpellCharges(self.spell) if charges and maxCharges and maxCharges > 0 and charges < maxCharges then self:SetCooldownTimer(chStart, chDuration, enable, chargemodrate, self.bar:GetShowCooldownText(), self.bar:GetCooldownColor1(), self.bar:GetCooldownColor2(), self.bar:GetShowCooldownAlpha(), charges, maxCharges) --only evoke charge cooldown (outer border) if charges are present and less than maxCharges (this is the case with the GCD) else self:SetCooldownTimer(start, duration, enable, modrate, self.bar:GetShowCooldownText(), self.bar:GetCooldownColor1(), self.bar:GetCooldownColor2(), self.bar:GetShowCooldownAlpha()) --call standard cooldown, handles both abilty cooldowns and GCD end else self:CancelCooldownTimer(true) end end function Button:UpdateItemCooldown() if self.item and self.isShown then local start, duration, enable, modrate if Neuron.itemCache[self.item:lower()] then start, duration, enable, modrate = C_Container.GetItemCooldown(Neuron.itemCache[self.item:lower()]) else local itemID = GetItemInfoInstant(self.item) start, duration, enable, modrate = C_Container.GetItemCooldown(itemID) end self:SetCooldownTimer(start, duration, enable, modrate, self.bar:GetShowCooldownText(), self.bar:GetCooldownColor1(), self.bar:GetCooldownColor2(), self.bar:GetShowCooldownAlpha()) else self:CancelCooldownTimer(true) end end function Button:UpdateActionCooldown() if self.actionID and self.isShown then if HasAction(self.actionID) then local start, duration, enable, modrate = GetActionCooldown(self.actionID) self:SetCooldownTimer(start, duration, enable, modrate, self.bar:GetShowCooldownText(), self.bar:GetCooldownColor1(), self.bar:GetCooldownColor2(), self.bar:GetShowCooldownAlpha()) end else self:CancelCooldownTimer(true) end end ----------------------------------------------------------------------------------------- ------------------------------------- Set Usable ---------------------------------------- ----------------------------------------------------------------------------------------- function Button:UpdateUsable() if Neuron.buttonEditMode or Neuron.bindingMode then self.Icon:SetVertexColor(0.2, 0.2, 0.2) elseif self.actionID then self:UpdateUsableAction() elseif self.spell then self:UpdateUsableSpell() elseif self.item then self:UpdateUsableItem() else self.Icon:SetVertexColor(1.0, 1.0, 1.0) end end function Button:UpdateUsableSpell() local isUsable, notEnoughMana = IsUsableSpell(self.spell) if notEnoughMana and self.bar:GetManaColor() then self.Icon:SetVertexColor(self.bar:GetManaColor()[1], self.bar:GetManaColor()[2], self.bar:GetManaColor()[3]) elseif isUsable then if self.bar:GetShowRangeIndicator() and IsSpellInRange(self.spell, self.unit) == 0 then self.Icon:SetVertexColor(self.bar:GetRangeColor()[1], self.bar:GetRangeColor()[2], self.bar:GetRangeColor()[3]) elseif self.bar:GetShowRangeIndicator() and Neuron.spellCache[self.spell:lower()] and IsSpellInRange(Neuron.spellCache[self.spell:lower()].index,"spell", self.unit) == 0 then self.Icon:SetVertexColor(self.bar:GetRangeColor()[1], self.bar:GetRangeColor()[2], self.bar:GetRangeColor()[3]) else self.Icon:SetVertexColor(1.0, 1.0, 1.0) end else self.Icon:SetVertexColor(0.4, 0.4, 0.4) end end function Button:UpdateUsableItem() local isUsable, notEnoughMana = IsUsableItem(self.item) --for some reason toys don't show as usable items, so this is a workaround for that if not isUsable then local itemID = GetItemInfoInstant(self.item) if Neuron.isWoWRetail and itemID and PlayerHasToy(itemID) then isUsable = true end end if notEnoughMana and self.bar:GetManaColor() then self.Icon:SetVertexColor(self.bar:GetManaColor()[1], self.bar:GetManaColor()[2], self.bar:GetManaColor()[3]) elseif isUsable then if self.bar:GetShowRangeIndicator() and IsItemInRange(self.item, self.unit) == 0 then self.Icon:SetVertexColor(self.bar:GetRangeColor()[1], self.bar:GetRangeColor()[2], self.bar:GetRangeColor()[3]) elseif Neuron.itemCache[self.item:lower()] and self.bar:GetShowRangeIndicator() and IsItemInRange(Neuron.itemCache[self.item:lower()], self.unit) == 0 then self.Icon:SetVertexColor(self.bar:GetRangeColor()[1], self.bar:GetRangeColor()[2], self.bar:GetRangeColor()[3]) else self.Icon:SetVertexColor(1.0, 1.0, 1.0) end else self.Icon:SetVertexColor(0.4, 0.4, 0.4) end end function Button:UpdateUsableAction() if self.actionID == 0 then self.Icon:SetVertexColor(1.0, 1.0, 1.0) return end local isUsable, notEnoughMana = IsUsableAction(self.actionID) if notEnoughMana and self.bar:GetManaColor() then self.Icon:SetVertexColor(self.bar:GetManaColor()[1], self.bar:GetManaColor()[2], self.bar:GetManaColor()[3]) elseif isUsable then if self.bar:GetShowRangeIndicator() and IsActionInRange(self.spell, self.unit) == 0 then self.Icon:SetVertexColor(self.bar:GetRangeColor()[1], self.bar:GetRangeColor()[2], self.bar:GetRangeColor()[3]) else self.Icon:SetVertexColor(1.0, 1.0, 1.0) end else self.Icon:SetVertexColor(0.4, 0.4, 0.4) end end ----------------------------------------------------------------------------------------- -------------------------------------- Set Status --------------------------------------- ----------------------------------------------------------------------------------------- ---used by actionbutton, extra, zone, exit function Button:UpdateStatus() -- actionID in pet, action, extra if self.actionID then self:UpdateActionStatus() -- macroequipmentset used in action and flyout elseif self:GetMacroEquipmentSet() then self.Name:SetText(self:GetMacroName()) -- spell in zone, extra, pet, action -- spellID in zone, extra, action elseif self.spell then self:UpdateSpellStatus() -- item in action elseif self.item then self:UpdateItemStatus() -- macro must go after spells and items, for blizz macro #showtooltip to work -- macro_BlizzMacro in action elseif self:GetMacroBlizzMacro() then self.Name:SetText(self:GetMacroName()) else self:SetChecked(false) self.Name:SetText("") self.Count:SetText("") end end function Button:UpdateSpellStatus() if IsCurrentSpell(self.spell) or IsAutoRepeatSpell(self.spell) then self:SetChecked(true) else self:SetChecked(false) end self.Name:SetText(self:GetMacroName()) self:UpdateCount() self:UpdateUsable() end function Button:UpdateItemStatus() if IsCurrentItem(self.item) then self:SetChecked(true) else self:SetChecked(false) end self.Name:SetText(self:GetMacroName()) self:UpdateCount() self:UpdateUsable() end function Button:UpdateActionStatus() local name if self.actionID then if IsCurrentAction(self.actionID) or IsAutoRepeatAction(self.actionID) then self:SetChecked(true) else self:SetChecked(false) end --find out the action name local type, id, _ = GetActionInfo(self.actionID) if type == "spell" then name = GetSpellInfo(id) elseif type == "item" then name = GetItemInfo(id) end else self:SetChecked(false) end if name then self.Name:SetText(name) else self.Name:SetText("") end self:UpdateUsable() end ----------------------------------------------------------------------------------------- ------------------------------------- Set Tooltip --------------------------------------- ----------------------------------------------------------------------------------------- function Button:UpdateTooltip() if self.bar:GetTooltipOption() ~= "off" then --if the bar isn't showing tooltips, don't proceed --if we are in combat and we don't have tooltips enable in-combat, don't go any further if InCombatLockdown() and not self.bar:GetTooltipCombat() then return end GameTooltip:SetOwner(self, "ANCHOR_RIGHT") if self.actionID then self:UpdateActionTooltip() elseif self:GetMacroEquipmentSet() then GameTooltip:SetEquipmentSet(self:GetMacroEquipmentSet()) elseif self.spell then self:UpdateSpellTooltip() elseif self.item then self:UpdateItemTooltip() -- macro must go after spells and items, for blizz macro #showtooltip to work elseif self:GetMacroBlizzMacro() then GameTooltip:SetText(self:GetMacroName()) elseif self:GetMacroText() and #self:GetMacroText() > 0 then GameTooltip:SetText(self:GetMacroName()) end -- it seems that recently wow decided to start hiding tooltips -- if you call "show" on them and they are already visible if not GameTooltip:IsShown() then GameTooltip:Show() end end end function Button:UpdateSpellTooltip() if self.spell and self.spellID then --try to get the correct spell from the spellbook first if self.bar:GetTooltipOption() == "normal" then GameTooltip:SetSpellByID(self.spellID) elseif self.bar:GetTooltipOption() == "minimal" then GameTooltip:SetText(self.spell, 1, 1, 1) end elseif Neuron.spellCache[self.spell:lower()] then --if the spell isn't in the spellbook, check our spell cache if self.bar:GetTooltipOption() == "normal" then GameTooltip:SetSpellByID(Neuron.spellCache[self.spell:lower()].spellID) elseif self.bar:GetTooltipOption() == "minimal" then GameTooltip:SetText(Neuron.spellCache[self.spell:lower()].spellName, 1, 1, 1) end else GameTooltip:SetText(UNKNOWN, 1, 1, 1) end end function Button:UpdateItemTooltip() local name, link = GetItemInfo(self.item) name = name or Neuron.itemCache[self.item:lower()] link = link or "item:"..name..":0:0:0:0:0:0:0" if not name or not link then return end if self.bar:GetTooltipOption() == "normal" then GameTooltip:SetHyperlink(link) elseif self.bar:GetTooltipOption() == "minimal" then GameTooltip:SetText(name, 1, 1, 1) end end function Button:UpdateActionTooltip() if HasAction(self.actionID) then if self.bar:GetTooltipOption() == "normal" or self.bar:GetTooltipOption() == "minimal" then GameTooltip:SetAction(self.actionID) end end end ----------------------------------------------------- -------------------Sets and Gets--------------------- ----------------------------------------------------- --Macro Icon function Button:SetMacroIcon(newIcon) if newIcon then self.data.macro_Icon = newIcon else self.data.macro_Icon = false end end function Button:GetMacroIcon() return self.data.macro_Icon end --Macro Text function Button:SetMacroText(newText) if newText then self.data.macro_Text = newText else self.data.macro_Text = "" end end function Button:GetMacroText() return self.data.macro_Text end --Macro Name function Button:SetMacroName(newName) if newName then self.data.macro_Name = newName else self.data.macro_Name = "" end end function Button:GetMacroName() return self.data.macro_Name end --Macro Note function Button:SetMacroNote(newNote) if newNote then self.data.macro_Note = newNote else self.data.macro_Note = "" end end function Button:GetMacroNote() return self.data.macro_Note end --Macro Use Note function Button:SetMacroUseNote(newUseNote) if newUseNote then self.data.macro_UseNote = newUseNote else self.data.macro_UseNote = false end end function Button:GetMacroUseNote() return self.data.macro_UseNote end --Macro Blizz Macro function Button:SetMacroBlizzMacro(newBlizzMacro) if newBlizzMacro then self.data.macro_BlizzMacro = newBlizzMacro else self.data.macro_BlizzMacro = false end end function Button:GetMacroBlizzMacro() return self.data.macro_BlizzMacro end --Macro EquipmentSet function Button:SetMacroEquipmentSet(newEquipmentSet) if newEquipmentSet then self.data.macro_EquipmentSet = newEquipmentSet else self.data.macro_EquipmentSet = false end end function Button:GetMacroEquipmentSet() return self.data.macro_EquipmentSet end