Mercurial > wow > reaction
diff modules/PetAction.lua @ 109:410d036c43b2
- reorganize modularity file structure (part 1)
author | Flick <flickerstreak@gmail.com> |
---|---|
date | Thu, 08 Jan 2009 00:57:27 +0000 |
parents | modules/ReAction_PetAction/ReAction_PetAction.lua@b2fb8f7dc780 |
children | fb48811a8736 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/modules/PetAction.lua Thu Jan 08 00:57:27 2009 +0000 @@ -0,0 +1,637 @@ +--[[ + ReAction Pet Action button module + + The button module implements standard action button functionality by wrapping Blizzard's + PetActionButton frame and associated functions. + +--]] + +-- local imports +local ReAction = ReAction +local L = ReAction.L +local _G = _G +local CreateFrame = CreateFrame +local format = string.format + +ReAction:UpdateRevision("$Revision$") + +-- libraries +local KB = LibStub("LibKeyBound-1.0") + +-- module declaration +local moduleID = "PetAction" +local module = ReAction:NewModule( moduleID ) + +-- Button class declaration +local Button = { } + +-- private +local function UpdateButtonLock(bar) + local f = bar:GetFrame() + f:SetAttribute("lockbuttons",bar.config.lockButtons) + f:SetAttribute("lockbuttonscombat",bar.config.lockButtonsCombat) + f:Execute( + [[ + lockButtons = self:GetAttribute("lockbuttons") + lockButtonsCombat = self:GetAttribute("lockbuttonscombat") + ]]) +end + +-- module methods +function module:OnInitialize() + self.db = ReAction.db:RegisterNamespace( moduleID, + { + profile = { + buttons = { } + } + } + ) + self.buttons = { } + + ReAction:RegisterBarOptionGenerator(self, "GetBarOptions") + + ReAction.RegisterCallback(self, "OnCreateBar") + ReAction.RegisterCallback(self, "OnDestroyBar") + ReAction.RegisterCallback(self, "OnRefreshBar") + ReAction.RegisterCallback(self, "OnEraseBar") + ReAction.RegisterCallback(self, "OnRenameBar") + ReAction.RegisterCallback(self, "OnConfigModeChanged") + + KB.RegisterCallback(self, "LIBKEYBOUND_ENABLED") + KB.RegisterCallback(self, "LIBKEYBOUND_DISABLED") + KB.RegisterCallback(self, "LIBKEYBOUND_MODE_COLOR_CHANGED","LIBKEYBOUND_ENABLED") +end + +function module:OnEnable() + ReAction:RegisterBarType(L["Pet Action Bar"], + { + type = moduleID , + defaultButtonSize = 30, + defaultBarRows = 1, + defaultBarCols = 10, + defaultBarSpacing = 8 + }) +end + +function module:OnDisable() + ReAction:UnregisterBarType(L["Pet Action Bar"]) +end + +function module:OnCreateBar(event, bar, name) + if bar.config.type == moduleID then + -- auto show/hide when pet exists + bar:GetFrame():SetAttribute("unit","pet") + if not ReAction:GetConfigMode() then + RegisterUnitWatch(bar:GetFrame()) + end + self:OnRefreshBar(event, bar, name) + end +end + +function module:OnRefreshBar(event, bar, name) + if bar.config.type == moduleID then + if self.buttons[bar] == nil then + self.buttons[bar] = { } + end + local btns = self.buttons[bar] + local profile = self.db.profile + if profile.buttons[name] == nil then + profile.buttons[name] = {} + end + local btnCfg = profile.buttons[name] + + local r, c = bar:GetButtonGrid() + local n = r*c + for i = 1, n do + if btnCfg[i] == nil then + btnCfg[i] = {} + end + if btns[i] == nil then + local success, r = pcall(Button.New,Button,bar,i,btnCfg[i]) + if success and r then + btns[i] = r + bar:AddButton(i,r) + else + n = i - 1 + bar:ClipNButtons(n) + break + end + end + btns[i]:Refresh() + end + for i = n+1, #btns do + if btns[i] then + bar:RemoveButton(btns[i]) + btns[i] = btns[i]:Destroy() + if btnCfg[i] then + btnCfg[i] = nil + end + end + end + UpdateButtonLock(bar) + end +end + +function module:OnDestroyBar(event, bar, name) + if self.buttons[bar] then + local btns = self.buttons[bar] + for _,b in pairs(btns) do + if b then + b:Destroy() + end + end + self.buttons[bar] = nil + end +end + +function module:OnEraseBar(event, bar, name) + self.db.profile.buttons[name] = nil +end + +function module:OnRenameBar(event, bar, oldname, newname) + local b = self.db.profile.buttons + b[newname], b[oldname] = b[oldname], nil +end + + +function module:OnConfigModeChanged(event, mode) + for _, buttons in pairs(self.buttons) do + for _, b in pairs(buttons) do + b:ShowActionIDLabel(mode) + end + end + for _, bar in ReAction:IterateBars() do + if bar and self.buttons[bar] then + local f = bar:GetFrame() + if mode then + UnregisterUnitWatch(f) + f:Show() + else + RegisterUnitWatch(f) + end + end + end +end + +function module:LIBKEYBOUND_ENABLED(evt) + for _, buttons in pairs(self.buttons) do + for _, b in pairs(buttons) do + b:SetKeybindMode(true) + end + end +end + +function module:LIBKEYBOUND_DISABLED(evt) + for _, buttons in pairs(self.buttons) do + for _, b in pairs(buttons) do + b:SetKeybindMode(false) + end + end +end + + +---- Options ---- +local Handler = { } +local meta = { __index = Handler } + +function Handler:New(bar) + return setmetatable( + { + bar = bar, + config = bar.config + }, meta) +end + +function Handler:GetLockButtons() + return LOCK_ACTIONBAR == "1" or self.config.lockButtons +end + +function Handler:SetLockButtons(info, value) + self.config.lockButtons = value + UpdateButtonLock(self.bar) +end + +function Handler:LockButtonsDisabled() + return LOCK_ACTIONBAR == "1" +end + +function Handler:GetLockButtonsCombat() + return self.config.lockButtonsCombat +end + +function Handler:SetLockButtonsCombat(info, value) + self.config.lockButtonsCombat = value + UpdateButtonLock(self.bar) +end + +function Handler:LockButtonsCombatDisabled() + return LOCK_ACTIONBAR == "1" or not self.config.lockButtons +end + + +function module:GetBarOptions(bar) + if bar.config.type == moduleID then + return { + type = "group", + name = L["Pet Buttons"], + handler = Handler:New(bar), + args = { + lockButtons = { + name = L["Lock Buttons"], + desc = L["Prevents picking up/dragging actions.|nNOTE: This setting is overridden by the global setting in Blizzard's Action Buttons tab"], + order = 2, + type = "toggle", + disabled = "LockButtonsDisabled", + get = "GetLockButtons", + set = "SetLockButtons", + }, + lockOnlyCombat = { + name = L["Only in Combat"], + desc = L["Only lock the buttons when in combat"], + order = 3, + type = "toggle", + disabled = "LockButtonsCombatDisabled", + get = "GetLockButtonsCombat", + set = "SetLockButtonsCombat", + }, + } + } + end +end + + + +------ Button class ------ + +-- use-count of action IDs +local nActionIDs = NUM_PET_ACTION_SLOTS +local ActionIDList = setmetatable( {}, { + __index = function(self, idx) + if idx == nil then + for i = 1, nActionIDs do + if rawget(self,i) == nil then + rawset(self,i,1) + return i + end + end + error("ran out of pet action IDs") + else + local c = rawget(self,idx) or 0 + rawset(self,idx,c+1) + return idx + end + end, + __newindex = function(self,idx,value) + if value == nil then + value = rawget(self,idx) + if value == 1 then + value = nil + elseif value then + value = value - 1 + end + end + rawset(self,idx,value) + end +}) + +local frameRecycler = {} +local trash = CreateFrame("Frame") +local KBAttach, GetActionName, GetHotkey, SetKey, FreeKey, ClearBindings, GetBindings, OnEnter, OnLeave +do + local buttonLookup = setmetatable({},{__mode="kv"}) + + -- Use KeyBound-1.0 for binding, but use Override bindings instead of + -- regular bindings to support multiple profile use. This is a little + -- weird with the KeyBound dialog box (which has per-char selector as well + -- as an OK/Cancel box) but it's the least amount of effort to implement. + function GetActionName(f) + local b = buttonLookup[f] + if b then + return format("%s:%s", b.bar:GetName(), b.idx) + end + end + + function GetHotkey(f) + local b = buttonLookup[f] + if b then + return KB:ToShortKey(b:GetConfig().hotkey) + end + end + + function SetKey(f, key) + local b = buttonLookup[f] + if b then + local c = b:GetConfig() + if c.hotkey then + SetOverrideBinding(f, false, c.hotkey, nil) + end + if key then + SetOverrideBindingClick(f, false, key, f:GetName(), nil) + end + c.hotkey = key + b:DisplayHotkey(GetHotkey(f)) + end + end + + function FreeKey(f, key) + local b = buttonLookup[f] + if b then + local c = b:GetConfig() + if c.hotkey == key then + local action = f:GetActionName() + SetOverrideBinding(f, false, c.hotkey, nil) + c.hotkey = nil + b:DisplayHotkey(nil) + return action + end + end + return ReAction:FreeOverrideHotkey(key) + end + + function ClearBindings(f) + SetKey(f, nil) + end + + function GetBindings(f) + local b = buttonLookup[f] + if b then + return b:GetConfig().hotkey + end + end + + function KBAttach( button ) + local f = button:GetFrame() + f.GetActionName = GetActionName + f.GetHotkey = GetHotkey + f.SetKey = SetKey + f.FreeKey = FreeKey + f.ClearBindings = ClearBindings + f.GetBindings = GetBindings + buttonLookup[f] = button + f:SetKey(button:GetConfig().hotkey) + ReAction:RegisterKeybindFrame(f) + if ReAction:GetKeybindMode() then + button.border:SetVertexColor(KB:GetColorKeyBoundMode()) + button.border:Show() + end + end + + function OnEnter( self ) + if not self.tooltipName then + return; + end + local uber = GetCVar("UberTooltips") + if self.isToken or (uber == "0") then + if uber == "0" then + GameTooltip:SetOwner(self, "ANCHOR_RIGHT") + else + GameTooltip_SetDefaultAnchor(GameTooltip, self) + end + local tooltip = self.tooltipName + local k = GetBindings(self) + if k then + tooltip = tooltip .. format(" %s(%s)%s", NORMAL_FONT_COLOR_CODE, k, FONT_COLOR_CODE_CLOSE) + end + GameTooltip:SetText(tooltip) + if self.tooltipSubtext then + GameTooltip:AddLine(self.tooltipSubtext, "", 0.5, 0.5, 0.5) + end + GameTooltip:Show() + else + GameTooltip_SetDefaultAnchor(GameTooltip, self) + GameTooltip:SetPetAction(self:GetID()) + end + end + + function OnLeave() + GameTooltip:Hide() + end + +end + +local meta = { __index = Button } + +function Button:New( bar, idx, config ) + -- create new self + self = setmetatable( + { + bar = bar, + idx = idx, + config = config, + }, meta ) + + local name = config.name or ("ReAction_%s_%s_%d"):format(bar:GetName(),moduleID,idx) + config.name = name + self.name = name + config.actionID = ActionIDList[config.actionID] -- gets a free one if none configured + + -- have to recycle frames with the same name: + -- otherwise you either get references to old textures because named CreateFrame() + -- doesn't overwrite existing globals. Can't set them to nil in the global table, + -- as it causes taint. + local parent = bar:GetFrame() + local f = frameRecycler[name] + if f then + f:SetParent(parent) + else + f = CreateFrame("CheckButton", name, parent, "PetActionButtonTemplate") + -- ditch the old hotkey text because it's tied in ActionButton_Update() to the + -- standard binding. We use override bindings. + local hotkey = _G[name.."HotKey"] + hotkey:SetParent(trash) + hotkey = f:CreateFontString(nil, "ARTWORK", "NumberFontNormalSmallGray") + hotkey:SetWidth(36) + hotkey:SetHeight(18) + hotkey:SetJustifyH("RIGHT") + hotkey:SetJustifyV("TOP") + hotkey:SetPoint("TOPLEFT",f,"TOPLEFT",-2,-2) + f.hotkey = hotkey + f:HookScript("OnDragStart", function() self:Update() end) + f:HookScript("OnReceiveDrag", function() self:Update() end) + f:SetScript("OnEnter", OnEnter) + f:SetScript("OnLeave", OnLeave) + end + if config.actionID then + f:SetID(config.actionID) -- PetActionButtonTemplate isn't a proper SecureActionButton + end + f:SetFrameStrata("MEDIUM") + self.frame = f + self.icon = _G[("%sIcon"):format(name)] + self.acTex = _G[("%sAutoCastable"):format(name)] + self.acModel = _G[("%sShine"):format(name)] + self.cooldown = _G[("%sCooldown"):format(name)] + self.hotkey = f.hotkey + self.border = _G[("%sBorder"):format(name)] + + + f:RegisterEvent("PLAYER_CONTROL_LOST"); + f:RegisterEvent("PLAYER_CONTROL_GAINED"); + f:RegisterEvent("PLAYER_FARSIGHT_FOCUS_CHANGED"); + f:RegisterEvent("UNIT_PET"); + f:RegisterEvent("UNIT_FLAGS"); + f:RegisterEvent("UNIT_AURA"); + f:RegisterEvent("PET_BAR_UPDATE"); + f:RegisterEvent("PET_BAR_UPDATE_COOLDOWN"); + + f:SetScript("OnEvent", + function(event,arg1) + if event =="PET_BAR_UPDATE_COOLDOWN" then + self:UpdateCooldown() + elseif event == "UPDATE_BINDINGS" then + self:UpdateHotkey() + else + self:Update() + end + end) + + -- install drag wrappers to lock buttons + bar:GetFrame():WrapScript(f, "OnDragStart", + -- OnDragStart(self, button, kind, value, ...) + [[ + if lockButtons and (PlayerInCombat() or not lockButtonsCombat) and not IsModifiedClick("PICKUPACTION") then + return "clear" + end + ]]) + + KBAttach(self) + + -- attach to skinner + bar:SkinButton(self, + { + HotKey = self.hotkey, + } + ) + + self:Refresh() + self:SetKeybindMode(ReAction:GetKeybindMode()) + + return self +end + +function Button:Destroy() + local f = self.frame + f:UnregisterAllEvents() + f:Hide() + f:SetParent(UIParent) + f:ClearAllPoints() + if self.name then + frameRecycler[self.name] = f + _G[self.name] = nil + end + if self.config.actionID then + ActionIDList[self.config.actionID] = nil + end + self.frame = nil + self.config = nil + self.bar = nil +end + +function Button:Refresh() + self.bar:PlaceButton(self, 30, 30) + self:Update() + self:UpdateHotkey() + self.frame:Show() +end + +function Button:GetFrame() + return self.frame +end + +function Button:GetName() + return self.name +end + +function Button:GetConfig() + return self.config +end + +function Button:GetActionID() + return self.config.actionID +end + +function Button:Update() + local id = self.frame:GetID() + local name, subtext, texture, isToken, isActive, autoCastAllowed, autoCastEnabled = GetPetActionInfo(id); + local f = self.frame + --ReAction:Print(("id %d: '%s', '%s', '%s', '%s', '%s', '%s', '%s'"):format(tostring(id), tostring(name),tostring(subtext),tostring(texture),tostring(isToken),tostring(isActive),tostring(autoCastAllowed),tostring(autoCastEnabled))) + + if isToken then + self.icon:SetTexture(_G[texture]); + f.tooltipName = _G[name]; + else + self.icon:SetTexture(texture); + f.tooltipName = name; + end + + f.isToken = isToken; + f.tooltipSubtext = subtext; + f:SetChecked( isActive and 1 or 0); + + if autoCastAllowed then + self.acTex:Show(); + else + self.acTex:Hide(); + end + + if autoCastEnabled then + AutoCastShine_AutoCastStart(self.acModel) + else + AutoCastShine_AutoCastStop(self.acModel) + end + + if texture then + if GetPetActionSlotUsable(id) then + SetDesaturation(self.icon,nil) + else + SetDesaturation(self.icon,1) + end + self.icon:Show(); + f:SetNormalTexture("Interface\\Buttons\\UI-Quickslot2"); + else + self.icon:Hide(); + f:SetNormalTexture("Interface\\Buttons\\UI-Quickslot"); + end + + self:UpdateCooldown() +end + +function Button:UpdateCooldown() + local start, duration, enable = GetPetActionCooldown(self.frame:GetID()); + CooldownFrame_SetTimer(self.cooldown, start, duration, enable); +end + +function Button:UpdateHotkey() + self:DisplayHotkey(GetHotkey(self.frame)) +end + +function Button:ShowActionIDLabel(show) + if show then + -- store the action ID label in the frame due to frame recycling + if not self.actionIDLabel and self:GetActionID() then + local label = self.frame:CreateFontString(nil,"OVERLAY","GameFontNormalLarge") + label:SetAllPoints() + label:SetJustifyH("CENTER") + label:SetShadowColor(0,0,0,1) + label:SetShadowOffset(2,-2) + label:SetText(tostring(self:GetActionID())) + self.actionIDLabel = label + end + self.actionIDLabel:Show() + elseif self.actionIDLabel then + self.actionIDLabel:Hide() + end +end + + +function Button:SetKeybindMode(mode) + if mode then + self.border:SetVertexColor(KB:GetColorKeyBoundMode()) + self.border:Show() + else + self.border:Hide() + end +end + +function Button:DisplayHotkey( key ) + self.hotkey:SetText(key or "") +end