flickerstreak@24: --[[ flickerstreak@53: ReAction Action button module. flickerstreak@24: flickerstreak@24: The button module implements standard action button functionality by wrapping Blizzard's flickerstreak@87: ActionBarButtonTemplate frame and associated functions. flickerstreak@77: flickerstreak@87: It also provides action remapping support for multiple pages and possessed targets flickerstreak@90: (Mind Control, Eyes of the Beast, Karazhan Chess event, various quests, etc). flickerstreak@24: --]] flickerstreak@24: flickerstreak@24: -- local imports flickerstreak@24: local ReAction = ReAction flickerstreak@24: local L = ReAction.L flickerstreak@24: local _G = _G flickerstreak@24: local CreateFrame = CreateFrame flickerstreak@88: local format = string.format flickerstreak@92: local wipe = wipe flickerstreak@24: flickerstreak@87: ReAction:UpdateRevision("$Revision$") flickerstreak@77: flickerstreak@116: local weak = { __mode="k" } flickerstreak@116: flickerstreak@88: -- libraries flickerstreak@88: local KB = LibStub("LibKeyBound-1.0") flickerstreak@108: local LBF -- initialized later flickerstreak@88: flickerstreak@24: -- module declaration flickerstreak@24: local moduleID = "Action" flickerstreak@28: local module = ReAction:NewModule( moduleID ) flickerstreak@24: flickerstreak@90: -- Class declarations flickerstreak@77: local Button = { } flickerstreak@90: local Handle = { } flickerstreak@90: local PropHandler = { } flickerstreak@87: flickerstreak@77: -- Event handlers flickerstreak@24: function module:OnInitialize() flickerstreak@28: self.db = ReAction.db:RegisterNamespace( moduleID, flickerstreak@24: { flickerstreak@28: profile = { flickerstreak@75: bars = { }, flickerstreak@28: } flickerstreak@24: } flickerstreak@24: ) flickerstreak@90: self.handles = setmetatable({ }, weak) flickerstreak@49: flickerstreak@63: ReAction:RegisterBarOptionGenerator(self, "GetBarOptions") flickerstreak@63: flickerstreak@90: ReAction.RegisterCallback(self, "OnCreateBar") flickerstreak@90: ReAction.RegisterCallback(self, "OnRefreshBar") flickerstreak@63: ReAction.RegisterCallback(self, "OnDestroyBar") flickerstreak@63: ReAction.RegisterCallback(self, "OnEraseBar") flickerstreak@63: ReAction.RegisterCallback(self, "OnRenameBar") flickerstreak@63: ReAction.RegisterCallback(self, "OnConfigModeChanged") flickerstreak@63: flickerstreak@108: LBF = LibStub("LibButtonFacade",true) flickerstreak@108: flickerstreak@88: KB.RegisterCallback(self, "LIBKEYBOUND_ENABLED") flickerstreak@88: KB.RegisterCallback(self, "LIBKEYBOUND_DISABLED") flickerstreak@88: KB.RegisterCallback(self, "LIBKEYBOUND_MODE_COLOR_CHANGED","LIBKEYBOUND_ENABLED") flickerstreak@24: end flickerstreak@24: flickerstreak@24: function module:OnEnable() flickerstreak@53: ReAction:RegisterBarType(L["Action Bar"], flickerstreak@53: { flickerstreak@53: type = moduleID, flickerstreak@91: defaultButtonSize = 36, flickerstreak@53: defaultBarRows = 1, flickerstreak@53: defaultBarCols = 12, flickerstreak@53: defaultBarSpacing = 3 flickerstreak@53: }, true) flickerstreak@90: ReAction:GetModule("State"):RegisterStateProperty("page", nil, PropHandler.GetOptions(), PropHandler) flickerstreak@24: end flickerstreak@24: flickerstreak@24: function module:OnDisable() flickerstreak@53: ReAction:UnregisterBarType(L["Action Bar"]) flickerstreak@90: ReAction:GetModule("State"):UnregisterStateProperty("page") flickerstreak@90: end flickerstreak@90: flickerstreak@90: function module:OnCreateBar(event, bar, name) flickerstreak@90: if bar.config.type == moduleID then flickerstreak@90: local profile = self.db.profile flickerstreak@90: if profile.bars[name] == nil then flickerstreak@90: profile.bars[name] = { flickerstreak@90: buttons = { } flickerstreak@90: } flickerstreak@90: end flickerstreak@90: if self.handles[bar] == nil then flickerstreak@90: self.handles[bar] = Handle:New(bar, profile.bars[name]) flickerstreak@90: end flickerstreak@90: end flickerstreak@24: end flickerstreak@24: flickerstreak@63: function module:OnRefreshBar(event, bar, name) flickerstreak@90: if self.handles[bar] then flickerstreak@90: self.handles[bar]:Refresh() flickerstreak@24: end flickerstreak@24: end flickerstreak@24: flickerstreak@63: function module:OnDestroyBar(event, bar, name) flickerstreak@90: if self.handles[bar] then flickerstreak@90: self.handles[bar]:Destroy() flickerstreak@90: self.handles[bar] = nil flickerstreak@24: end flickerstreak@24: end flickerstreak@24: flickerstreak@63: function module:OnEraseBar(event, bar, name) flickerstreak@75: self.db.profile.bars[name] = nil flickerstreak@24: end flickerstreak@24: flickerstreak@63: function module:OnRenameBar(event, bar, oldname, newname) flickerstreak@75: b = self.db.profile.bars flickerstreak@75: b[newname], b[oldname] = b[oldname], nil flickerstreak@48: end flickerstreak@48: flickerstreak@63: function module:OnConfigModeChanged(event, mode) flickerstreak@90: for _, h in pairs(self.handles) do flickerstreak@90: h:SetConfigMode(mode) flickerstreak@24: end flickerstreak@24: end flickerstreak@24: flickerstreak@88: function module:LIBKEYBOUND_ENABLED(evt) flickerstreak@90: for _, h in pairs(self.handles) do flickerstreak@93: h:ShowGrid(true) flickerstreak@90: h:SetKeybindMode(true) flickerstreak@88: end flickerstreak@88: end flickerstreak@88: flickerstreak@88: function module:LIBKEYBOUND_DISABLED(evt) flickerstreak@90: for _, h in pairs(self.handles) do flickerstreak@93: h:ShowGrid(false) flickerstreak@90: h:SetKeybindMode(false) flickerstreak@88: end flickerstreak@88: end flickerstreak@88: flickerstreak@50: flickerstreak@90: ---- Interface ---- flickerstreak@90: function module:GetBarOptions(bar) flickerstreak@90: local h = self.handles[bar] flickerstreak@90: if h then flickerstreak@90: return h:GetOptions() flickerstreak@90: end flickerstreak@90: end flickerstreak@90: flickerstreak@90: flickerstreak@90: ---- Bar Handle ---- flickerstreak@90: flickerstreak@87: do flickerstreak@87: local options = { flickerstreak@87: hideEmpty = { flickerstreak@87: name = L["Hide Empty Buttons"], flickerstreak@87: order = 1, flickerstreak@87: type = "toggle", flickerstreak@87: width = "double", flickerstreak@87: get = "GetHideEmpty", flickerstreak@87: set = "SetHideEmpty", flickerstreak@87: }, flickerstreak@102: lockButtons = { flickerstreak@102: name = L["Lock Buttons"], flickerstreak@102: desc = L["Prevents picking up/dragging actions.|nNOTE: This setting is overridden by the global setting in Blizzard's Action Buttons tab"], flickerstreak@102: order = 2, flickerstreak@102: type = "toggle", flickerstreak@102: disabled = "LockButtonsDisabled", flickerstreak@102: get = "GetLockButtons", flickerstreak@102: set = "SetLockButtons", flickerstreak@102: }, flickerstreak@102: lockOnlyCombat = { flickerstreak@102: name = L["Only in Combat"], flickerstreak@102: desc = L["Only lock the buttons when in combat"], flickerstreak@102: order = 3, flickerstreak@102: type = "toggle", flickerstreak@102: disabled = "LockButtonsCombatDisabled", flickerstreak@102: get = "GetLockButtonsCombat", flickerstreak@102: set = "SetLockButtonsCombat", flickerstreak@102: }, flickerstreak@87: pages = { flickerstreak@87: name = L["# Pages"], flickerstreak@87: desc = L["Use the Dynamic State tab to specify page transitions"], flickerstreak@102: order = 4, flickerstreak@87: type = "range", flickerstreak@87: min = 1, flickerstreak@87: max = 10, flickerstreak@87: step = 1, flickerstreak@87: get = "GetNumPages", flickerstreak@87: set = "SetNumPages", flickerstreak@87: }, flickerstreak@90: mindcontrol = { flickerstreak@90: name = L["Mind Control Support"], flickerstreak@90: desc = L["When possessing a target (e.g. via Mind Control), map the first 12 buttons of this bar to the possessed target's actions."], flickerstreak@102: order = 5, flickerstreak@90: type = "toggle", flickerstreak@90: width = "double", flickerstreak@90: set = "SetMindControl", flickerstreak@90: get = "GetMindControl", flickerstreak@90: }, flickerstreak@121: vehicle = { flickerstreak@121: name = L["Vehicle Support"], flickerstreak@121: desc = L["When on a vehicle, map the first 6 buttons of this bar to the vehicle actions. The vehicle-exit button is mapped to the 7th button. Pitch controls are not supported."], flickerstreak@121: order = 6, flickerstreak@121: type = "toggle", flickerstreak@121: width = "double", flickerstreak@121: get = "GetVehicle", flickerstreak@121: set = "SetVehicle", flickerstreak@121: }, flickerstreak@87: actions = { flickerstreak@87: name = L["Edit Action IDs"], flickerstreak@121: order = 7, flickerstreak@87: type = "group", flickerstreak@87: inline = true, flickerstreak@87: args = { flickerstreak@87: method = { flickerstreak@87: name = L["Assign"], flickerstreak@87: order = 1, flickerstreak@87: type = "select", flickerstreak@87: width = "double", flickerstreak@87: values = { [0] = L["Choose Method..."], flickerstreak@87: [1] = L["Individually"], flickerstreak@87: [2] = L["All at Once"], }, flickerstreak@87: get = "GetActionEditMethod", flickerstreak@87: set = "SetActionEditMethod", flickerstreak@87: }, flickerstreak@87: rowSelect = { flickerstreak@87: name = L["Row"], flickerstreak@87: desc = L["Rows are numbered top to bottom"], flickerstreak@87: order = 2, flickerstreak@87: type = "select", flickerstreak@87: width = "half", flickerstreak@87: hidden = "IsButtonSelectHidden", flickerstreak@87: values = "GetRowList", flickerstreak@87: get = "GetSelectedRow", flickerstreak@87: set = "SetSelectedRow", flickerstreak@87: }, flickerstreak@87: colSelect = { flickerstreak@87: name = L["Col"], flickerstreak@87: desc = L["Columns are numbered left to right"], flickerstreak@87: order = 3, flickerstreak@87: type = "select", flickerstreak@87: width = "half", flickerstreak@87: hidden = "IsButtonSelectHidden", flickerstreak@87: values = "GetColumnList", flickerstreak@87: get = "GetSelectedColumn", flickerstreak@87: set = "SetSelectedColumn", flickerstreak@87: }, flickerstreak@87: pageSelect = { flickerstreak@87: name = L["Page"], flickerstreak@87: order = 4, flickerstreak@87: type = "select", flickerstreak@87: width = "half", flickerstreak@87: hidden = "IsPageSelectHidden", flickerstreak@87: values = "GetPageList", flickerstreak@87: get = "GetSelectedPage", flickerstreak@87: set = "SetSelectedPage", flickerstreak@87: }, flickerstreak@87: single = { flickerstreak@87: name = L["Action ID"], flickerstreak@87: usage = L["Specify ID 1-120"], flickerstreak@87: order = 5, flickerstreak@87: type = "input", flickerstreak@87: width = "half", flickerstreak@87: hidden = "IsButtonSelectHidden", flickerstreak@87: get = "GetActionID", flickerstreak@87: set = "SetActionID", flickerstreak@87: validate = "ValidateActionID", flickerstreak@87: }, flickerstreak@87: multi = { flickerstreak@87: name = L["ID List"], flickerstreak@87: usage = L["Specify a comma-separated list of IDs for each button in the bar (in order). Separate multiple pages with semicolons (;)"], flickerstreak@87: order = 6, flickerstreak@87: type = "input", flickerstreak@87: multiline = true, flickerstreak@87: width = "double", flickerstreak@87: hidden = "IsMultiIDHidden", flickerstreak@87: get = "GetMultiID", flickerstreak@87: set = "SetMultiID", flickerstreak@87: validate = "ValidateMultiID", flickerstreak@90: }, flickerstreak@90: }, flickerstreak@87: }, flickerstreak@87: } flickerstreak@77: flickerstreak@90: local meta = { __index = Handle } flickerstreak@90: flickerstreak@90: function Handle:New( bar, config ) flickerstreak@90: local self = setmetatable( flickerstreak@90: { flickerstreak@90: bar = bar, flickerstreak@90: config = config, flickerstreak@90: btns = { } flickerstreak@90: }, flickerstreak@90: meta) flickerstreak@90: flickerstreak@90: if self.config.buttons == nil then flickerstreak@90: self.config.buttons = { } flickerstreak@90: end flickerstreak@90: self:Refresh() flickerstreak@92: self:SetKeybindMode(ReAction:GetKeybindMode()) flickerstreak@90: return self flickerstreak@90: end flickerstreak@90: flickerstreak@90: function Handle:Refresh() flickerstreak@90: local r, c = self.bar:GetButtonGrid() flickerstreak@90: local n = r*c flickerstreak@90: local btnCfg = self.config.buttons flickerstreak@90: if n ~= #self.btns then flickerstreak@90: for i = 1, n do flickerstreak@90: if btnCfg[i] == nil then flickerstreak@90: btnCfg[i] = {} flickerstreak@90: end flickerstreak@90: if self.btns[i] == nil then flickerstreak@90: local b = Button:New(self, i, btnCfg[i], self.config) flickerstreak@90: self.btns[i] = b flickerstreak@90: self.bar:AddButton(i,b) flickerstreak@90: end flickerstreak@90: end flickerstreak@90: for i = n+1, #self.btns do flickerstreak@90: if self.btns[i] then flickerstreak@90: self.bar:RemoveButton(self.btns[i]) flickerstreak@90: self.btns[i]:Destroy() flickerstreak@90: self.btns[i] = nil flickerstreak@90: btnCfg[i] = nil flickerstreak@90: end flickerstreak@90: end flickerstreak@90: end flickerstreak@102: local f = self.bar:GetFrame() flickerstreak@90: for _, b in ipairs(self.btns) do flickerstreak@90: b:Refresh() flickerstreak@90: end flickerstreak@90: f:SetAttribute("mindcontrol",self.config.mindcontrol) flickerstreak@121: f:SetAttribute("vehicle",self.config.vehicle) flickerstreak@90: f:Execute( flickerstreak@90: [[ flickerstreak@90: doMindControl = self:GetAttribute("mindcontrol") flickerstreak@121: doVehicle = self:GetAttribute("vehicle") flickerstreak@90: control:ChildUpdate() flickerstreak@90: ]]) flickerstreak@90: flickerstreak@121: f:SetAttribute("_onstate-mc", flickerstreak@121: -- function _onstate-mc(self, stateid, newstate) flickerstreak@90: [[ flickerstreak@121: local oldMcVehicleState = mcVehicleState flickerstreak@121: mcVehicleState = newstate flickerstreak@90: control:ChildUpdate() flickerstreak@121: if oldMcVehicleState == "vehicle" or mcVehicleState == "vehicle" then flickerstreak@121: control:ChildUpdate("vehicle") flickerstreak@121: end flickerstreak@90: ]]) flickerstreak@121: RegisterStateDriver(f, "mc", "[target=vehicle,exists] vehicle; [bonusbar:5] mc; none") flickerstreak@121: flickerstreak@102: self:UpdateButtonLock() flickerstreak@90: end flickerstreak@90: flickerstreak@90: function Handle:Destroy() flickerstreak@90: for _,b in pairs(self.btns) do flickerstreak@90: if b then flickerstreak@90: b:Destroy() flickerstreak@90: end flickerstreak@90: end flickerstreak@90: end flickerstreak@90: flickerstreak@90: function Handle:SetConfigMode(mode) flickerstreak@90: for _, b in pairs(self.btns) do flickerstreak@90: b:ShowGrid(mode) flickerstreak@90: b:ShowActionIDLabel(mode) flickerstreak@90: end flickerstreak@90: end flickerstreak@90: flickerstreak@93: function Handle:ShowGrid(show) flickerstreak@93: for _, b in pairs(self.btns) do flickerstreak@93: b:ShowGrid(show) flickerstreak@93: end flickerstreak@93: end flickerstreak@93: flickerstreak@102: function Handle:UpdateButtonLock() flickerstreak@102: local f = self.bar:GetFrame() flickerstreak@102: f:SetAttribute("lockbuttons",self.config.lockButtons) flickerstreak@102: f:SetAttribute("lockbuttonscombat",self.config.lockButtonsCombat) flickerstreak@102: f:Execute( flickerstreak@102: [[ flickerstreak@102: lockButtons = self:GetAttribute("lockbuttons") flickerstreak@102: lockButtonsCombat = self:GetAttribute("lockbuttonscombat") flickerstreak@102: ]]) flickerstreak@102: end flickerstreak@102: flickerstreak@90: function Handle:SetKeybindMode(mode) flickerstreak@90: for _, b in pairs(self.btns) do flickerstreak@116: b:SetKeybindMode(mode) flickerstreak@90: end flickerstreak@90: end flickerstreak@90: flickerstreak@90: function Handle:GetLastButton() flickerstreak@90: return self.btns[#self.btns] flickerstreak@90: end flickerstreak@90: flickerstreak@90: -- options handlers flickerstreak@90: function Handle:GetOptions() flickerstreak@87: return { flickerstreak@87: type = "group", flickerstreak@87: name = L["Action Buttons"], flickerstreak@90: handler = self, flickerstreak@87: args = options flickerstreak@87: } flickerstreak@77: end flickerstreak@77: flickerstreak@90: function Handle:SetHideEmpty(info, value) flickerstreak@90: if value ~= self.config.hideEmpty then flickerstreak@90: self.config.hideEmpty = value flickerstreak@102: self:ShowGrid(not value) flickerstreak@77: end flickerstreak@77: end flickerstreak@77: flickerstreak@90: function Handle:GetHideEmpty() flickerstreak@90: return self.config.hideEmpty flickerstreak@77: end flickerstreak@87: flickerstreak@102: function Handle:GetLockButtons() flickerstreak@102: return LOCK_ACTIONBAR == "1" or self.config.lockButtons flickerstreak@102: end flickerstreak@102: flickerstreak@102: function Handle:SetLockButtons(info, value) flickerstreak@102: self.config.lockButtons = value flickerstreak@102: self:UpdateButtonLock() flickerstreak@102: end flickerstreak@102: flickerstreak@102: function Handle:LockButtonsDisabled() flickerstreak@102: return LOCK_ACTIONBAR == "1" flickerstreak@102: end flickerstreak@102: flickerstreak@102: function Handle:GetLockButtonsCombat() flickerstreak@102: return self.config.lockButtonsCombat flickerstreak@102: end flickerstreak@102: flickerstreak@102: function Handle:SetLockButtonsCombat(info, value) flickerstreak@102: self.config.lockButtonsCombat = value flickerstreak@102: self:UpdateButtonLock() flickerstreak@102: end flickerstreak@102: flickerstreak@102: function Handle:LockButtonsCombatDisabled() flickerstreak@102: return LOCK_ACTIONBAR == "1" or not self.config.lockButtons flickerstreak@102: end flickerstreak@102: flickerstreak@90: function Handle:GetNumPages() flickerstreak@90: return self.config.nPages flickerstreak@87: end flickerstreak@87: flickerstreak@90: function Handle:SetNumPages(info, value) flickerstreak@90: self.config.nPages = value flickerstreak@90: self:Refresh() flickerstreak@87: end flickerstreak@87: flickerstreak@90: function Handle:GetMindControl() flickerstreak@90: return self.config.mindcontrol flickerstreak@90: end flickerstreak@90: flickerstreak@90: function Handle:SetMindControl(info, value) flickerstreak@90: self.config.mindcontrol = value flickerstreak@90: self:Refresh() flickerstreak@90: end flickerstreak@90: flickerstreak@121: function Handle:GetVehicle() flickerstreak@121: return self.config.vehicle flickerstreak@121: end flickerstreak@121: flickerstreak@121: function Handle:SetVehicle(info, value) flickerstreak@121: self.config.vehicle = value flickerstreak@121: self:Refresh() flickerstreak@121: end flickerstreak@121: flickerstreak@90: function Handle:GetActionEditMethod() flickerstreak@87: return self.editMethod or 0 flickerstreak@87: end flickerstreak@87: flickerstreak@90: function Handle:SetActionEditMethod(info, value) flickerstreak@87: self.editMethod = value flickerstreak@87: end flickerstreak@87: flickerstreak@90: function Handle:IsButtonSelectHidden() flickerstreak@87: return self.editMethod ~= 1 flickerstreak@87: end flickerstreak@87: flickerstreak@90: function Handle:GetRowList() flickerstreak@87: local r,c = self.bar:GetButtonGrid() flickerstreak@87: if self.rowList == nil or #self.rowList ~= r then flickerstreak@87: local list = { } flickerstreak@87: for i = 1, r do flickerstreak@87: table.insert(list,i) flickerstreak@87: end flickerstreak@87: self.rowList = list flickerstreak@87: end flickerstreak@87: return self.rowList flickerstreak@87: end flickerstreak@87: flickerstreak@90: function Handle:GetSelectedRow() flickerstreak@87: local r, c = self.bar:GetButtonGrid() flickerstreak@87: local row = self.selectedRow or 1 flickerstreak@87: if row > r then flickerstreak@87: row = 1 flickerstreak@87: end flickerstreak@87: self.selectedRow = row flickerstreak@87: return row flickerstreak@87: end flickerstreak@87: flickerstreak@90: function Handle:SetSelectedRow(info, value) flickerstreak@87: self.selectedRow = value flickerstreak@87: end flickerstreak@87: flickerstreak@90: function Handle:GetColumnList() flickerstreak@87: local r,c = self.bar:GetButtonGrid() flickerstreak@87: if self.columnList == nil or #self.columnList ~= c then flickerstreak@87: local list = { } flickerstreak@87: for i = 1, c do flickerstreak@87: table.insert(list,i) flickerstreak@87: end flickerstreak@87: self.columnList = list flickerstreak@87: end flickerstreak@87: return self.columnList flickerstreak@87: end flickerstreak@87: flickerstreak@90: function Handle:GetSelectedColumn() flickerstreak@87: local r, c = self.bar:GetButtonGrid() flickerstreak@87: local col = self.selectedColumn or 1 flickerstreak@87: if col > c then flickerstreak@87: col = 1 flickerstreak@87: end flickerstreak@87: self.selectedColumn = col flickerstreak@87: return col flickerstreak@87: end flickerstreak@87: flickerstreak@90: function Handle:SetSelectedColumn(info, value) flickerstreak@87: self.selectedColumn = value flickerstreak@87: end flickerstreak@87: flickerstreak@90: function Handle:IsPageSelectHidden() flickerstreak@90: return self.editMethod ~= 1 or (self.config.nPages or 1) < 2 flickerstreak@87: end flickerstreak@87: flickerstreak@90: function Handle:GetPageList() flickerstreak@90: local n = self.config.nPages or 1 flickerstreak@87: if self.pageList == nil or #self.pageList ~= n then flickerstreak@87: local p = { } flickerstreak@87: for i = 1, n do flickerstreak@87: table.insert(p,i) flickerstreak@87: end flickerstreak@87: self.pageList = p flickerstreak@87: end flickerstreak@87: return self.pageList flickerstreak@87: end flickerstreak@87: flickerstreak@90: function Handle:GetSelectedPage() flickerstreak@87: local p = self.selectedPage or 1 flickerstreak@90: if p > (self.config.nPages or 1) then flickerstreak@87: p = 1 flickerstreak@87: end flickerstreak@87: self.selectedPage = p flickerstreak@87: return p flickerstreak@87: end flickerstreak@87: flickerstreak@90: function Handle:SetSelectedPage(info, value) flickerstreak@87: self.selectedPage = value flickerstreak@87: end flickerstreak@87: flickerstreak@90: function Handle:GetActionID() flickerstreak@87: local row = self.selectedRow or 1 flickerstreak@87: local col = self.selectedColumn or 1 flickerstreak@87: local r, c = self.bar:GetButtonGrid() flickerstreak@87: local n = (row-1) * c + col flickerstreak@90: local btn = self.btns[n] flickerstreak@87: if btn then flickerstreak@87: return tostring(btn:GetActionID(self.selectedPage or 1)) flickerstreak@87: end flickerstreak@87: end flickerstreak@87: flickerstreak@90: function Handle:SetActionID(info, value) flickerstreak@87: local row = self.selectedRow or 1 flickerstreak@87: local col = self.selectedColumn or 1 flickerstreak@87: local r, c = self.bar:GetButtonGrid() flickerstreak@87: local n = (row-1) * c + col flickerstreak@90: local btn = self.btns[n] flickerstreak@87: if btn then flickerstreak@87: btn:SetActionID(tonumber(value), self.selectedPage or 1) flickerstreak@87: end flickerstreak@87: end flickerstreak@87: flickerstreak@90: function Handle:ValidateActionID(info, value) flickerstreak@87: value = tonumber(value) flickerstreak@87: if value == nil or value < 1 or value > 120 then flickerstreak@87: return L["Specify ID 1-120"] flickerstreak@87: end flickerstreak@87: return true flickerstreak@87: end flickerstreak@87: flickerstreak@90: function Handle:IsMultiIDHidden() flickerstreak@87: return self.editMethod ~= 2 flickerstreak@87: end flickerstreak@87: flickerstreak@90: function Handle:GetMultiID() flickerstreak@87: local p = { } flickerstreak@90: for i = 1, self.config.nPages or 1 do flickerstreak@87: local b = { } flickerstreak@90: for _, btn in ipairs(self.btns) do flickerstreak@87: table.insert(b, btn:GetActionID(i)) flickerstreak@87: end flickerstreak@87: table.insert(p, table.concat(b,",")) flickerstreak@87: end flickerstreak@87: return table.concat(p,";\n") flickerstreak@87: end flickerstreak@87: flickerstreak@87: flickerstreak@87: local function ParseMultiID(nBtns, nPages, s) flickerstreak@87: if s:match("[^%d%s,;]") then flickerstreak@87: ReAction:Print("items other than digits, spaces, commas, and semicolons in string",s) flickerstreak@87: return nil flickerstreak@87: end flickerstreak@87: local p = { } flickerstreak@87: for list in s:gmatch("[^;]+") do flickerstreak@87: local pattern = ("^%s?$"):format(("%s*(%d+)%s*,"):rep(nBtns)) flickerstreak@87: local ids = { list:match(pattern) } flickerstreak@87: if #ids ~= nBtns then flickerstreak@87: ReAction:Print("found",#ids,"buttons instead of",nBtns) flickerstreak@87: return nil flickerstreak@87: end flickerstreak@87: table.insert(p,ids) flickerstreak@87: end flickerstreak@87: if #p ~= nPages then flickerstreak@87: ReAction:Print("found",#p,"pages instead of",nPages) flickerstreak@87: return nil flickerstreak@87: end flickerstreak@87: return p flickerstreak@87: end flickerstreak@87: flickerstreak@90: function Handle:SetMultiID(info, value) flickerstreak@91: local p = ParseMultiID(#self.btns, self.config.nPages or 1, value) flickerstreak@87: for page, b in ipairs(p) do flickerstreak@87: for button, id in ipairs(b) do flickerstreak@90: self.btns[button]:SetActionID(id, page) flickerstreak@87: end flickerstreak@87: end flickerstreak@87: end flickerstreak@87: flickerstreak@90: function Handle:ValidateMultiID(info, value) flickerstreak@87: local bad = L["Invalid action ID list string"] flickerstreak@90: if value == nil or ParseMultiID(#self.btns, self.config.nPages or 1, value) == nil then flickerstreak@87: return bad flickerstreak@87: end flickerstreak@87: return true flickerstreak@87: end flickerstreak@77: end flickerstreak@77: flickerstreak@77: flickerstreak@87: ------ State property options ------ flickerstreak@87: do flickerstreak@87: local pageOptions = { flickerstreak@87: page = { flickerstreak@92: name = L["Show Page #"], flickerstreak@92: order = 11, flickerstreak@92: type = "select", flickerstreak@92: width = "half", flickerstreak@87: disabled = "IsPageDisabled", flickerstreak@87: hidden = "IsPageHidden", flickerstreak@87: values = "GetPageValues", flickerstreak@87: set = "SetProp", flickerstreak@87: get = "GetPage", flickerstreak@87: }, flickerstreak@87: } flickerstreak@50: flickerstreak@90: local function GetBarConfig(bar) flickerstreak@90: return module.db.profile.bars[bar:GetName()] flickerstreak@87: end flickerstreak@87: flickerstreak@90: function PropHandler.GetOptions() flickerstreak@90: return pageOptions flickerstreak@87: end flickerstreak@87: flickerstreak@90: function PropHandler:IsPageDisabled() flickerstreak@90: local c = GetBarConfig(self.bar) flickerstreak@90: local n = c and c.nPages or 1 flickerstreak@90: return not (n > 1) flickerstreak@87: end flickerstreak@87: flickerstreak@90: function PropHandler:IsPageHidden() flickerstreak@87: return not GetBarConfig(self.bar) flickerstreak@87: end flickerstreak@87: flickerstreak@90: function PropHandler:GetPageValues() flickerstreak@92: if not self._pagevalues then flickerstreak@92: self._pagevalues = { } flickerstreak@92: end flickerstreak@87: local c = GetBarConfig(self.bar) flickerstreak@87: if c then flickerstreak@87: local n = c.nPages flickerstreak@92: -- cache the results flickerstreak@87: if self._npages ~= n then flickerstreak@87: self._npages = n flickerstreak@92: wipe(self._pagevalues) flickerstreak@87: for i = 1, n do flickerstreak@90: self._pagevalues["page"..i] = i flickerstreak@87: end flickerstreak@87: end flickerstreak@87: end flickerstreak@92: return self._pagevalues flickerstreak@87: end flickerstreak@87: flickerstreak@90: function PropHandler:GetPage(info) flickerstreak@87: return self:GetProp(info) or 1 flickerstreak@87: end flickerstreak@90: flickerstreak@87: end flickerstreak@87: flickerstreak@87: ------ ActionID allocation ------ flickerstreak@87: -- this needs to be high performance when requesting new IDs, flickerstreak@87: -- or certain controls will become sluggish. However, the new-request flickerstreak@87: -- infrastructure can be built lazily the first time that a new request flickerstreak@87: -- comes in (which will only happen at user config time: at static startup flickerstreak@87: -- config time all actionIDs should already have been assigned and stored flickerstreak@87: -- in the config file) flickerstreak@87: flickerstreak@87: local IDAlloc flickerstreak@87: do flickerstreak@87: local n = 120 flickerstreak@87: flickerstreak@87: IDAlloc = setmetatable({ wrap = 1, freecount = n }, {__index = function() return 0 end}) flickerstreak@87: flickerstreak@87: function IDAlloc:Acquire(id, hint) flickerstreak@87: id = tonumber(id) flickerstreak@87: hint = tonumber(hint) flickerstreak@87: if id and (id < 1 or id > n) then flickerstreak@87: id = nil flickerstreak@87: end flickerstreak@87: if hint and (hint < 1 or hint > n) then flickerstreak@87: hint = nil flickerstreak@87: end flickerstreak@87: if id == nil then flickerstreak@87: -- get a free ID flickerstreak@87: if hint and self[hint] == 0 then flickerstreak@87: -- use the hint if it's free flickerstreak@87: id = hint flickerstreak@87: elseif self.freecount > 0 then flickerstreak@87: -- if neither the id nor the hint are defined or free, but flickerstreak@87: -- the list is known to have free IDs, then start searching flickerstreak@87: -- at the hint for a free one flickerstreak@87: for i = hint or 1, n do flickerstreak@87: if self[i] == 0 then flickerstreak@87: id = i flickerstreak@87: break flickerstreak@87: end flickerstreak@87: end flickerstreak@87: -- self.wrap the search flickerstreak@87: if id == nil and hint and hint > 1 then flickerstreak@87: for i = 1, hint - 1 do flickerstreak@87: if self[i] == 0 then flickerstreak@87: id = i flickerstreak@87: break flickerstreak@87: end flickerstreak@87: end flickerstreak@87: end flickerstreak@87: end flickerstreak@87: if id == nil then flickerstreak@87: -- if there are no free IDs, start wrapping at 1 flickerstreak@87: id = self.wrap flickerstreak@87: self.wrap = id + 1 flickerstreak@87: if self.wrap > n then flickerstreak@87: self.wrap = 1 flickerstreak@87: end flickerstreak@24: end flickerstreak@24: end flickerstreak@87: if self[id] == 0 then flickerstreak@87: self.freecount = self.freecount - 1 flickerstreak@87: end flickerstreak@87: self[id] = self[id] + 1 flickerstreak@87: return id flickerstreak@24: end flickerstreak@24: flickerstreak@87: function IDAlloc:Release(id) flickerstreak@87: id = tonumber(id) flickerstreak@87: if id and (id >= 1 or id <= n) then flickerstreak@87: self[id] = self[id] - 1 flickerstreak@87: if self[id] == 0 then flickerstreak@87: self.freecount = self.freecount + 1 flickerstreak@87: self.wrap = 1 flickerstreak@87: end flickerstreak@87: end flickerstreak@87: end flickerstreak@87: end flickerstreak@87: flickerstreak@88: ------ Button class ------ flickerstreak@116: local frameRecycler = { } flickerstreak@116: local trash = CreateFrame("Frame") flickerstreak@121: local OnUpdate, GetActionName, GetHotkey flickerstreak@116: do flickerstreak@116: local ATTACK_BUTTON_FLASH_TIME = ATTACK_BUTTON_FLASH_TIME flickerstreak@116: local IsActionInRange = IsActionInRange flickerstreak@88: flickerstreak@116: function OnUpdate(frame, elapsed) flickerstreak@116: -- note: This function taints frame.flashtime and frame.rangeTimer. Both of these flickerstreak@116: -- are only read by ActionButton_OnUpdate (which this function replaces). In flickerstreak@116: -- all other places they're just written, so it doesn't taint any secure code. flickerstreak@116: if frame.flashing == 1 then flickerstreak@116: frame.flashtime = frame.flashtime - elapsed flickerstreak@116: if frame.flashtime <= 0 then flickerstreak@116: local overtime = -frame.flashtime flickerstreak@116: if overtime >= ATTACK_BUTTON_FLASH_TIME then flickerstreak@116: overtime = 0 flickerstreak@116: end flickerstreak@116: frame.flashtime = ATTACK_BUTTON_FLASH_TIME - overtime flickerstreak@87: flickerstreak@116: local flashTexture = frame.flash flickerstreak@116: if flashTexture:IsShown() then flickerstreak@116: flashTexture:Hide() flickerstreak@116: else flickerstreak@116: flashTexture:Show() flickerstreak@88: end flickerstreak@88: end flickerstreak@88: end flickerstreak@116: flickerstreak@116: if frame.rangeTimer then flickerstreak@116: frame.rangeTimer = frame.rangeTimer - elapsed; flickerstreak@90: flickerstreak@116: if frame.rangeTimer <= 0 then flickerstreak@116: if IsActionInRange(frame.action) == 0 then flickerstreak@116: frame.icon:SetVertexColor(1.0,0.1,0.1) flickerstreak@116: else flickerstreak@116: ActionButton_UpdateUsable(frame) flickerstreak@90: end flickerstreak@116: frame.rangeTimer = 0.1 flickerstreak@92: end flickerstreak@90: end flickerstreak@90: end flickerstreak@90: flickerstreak@121: function GetActionName(f) flickerstreak@116: local b = f and f._reactionButton flickerstreak@116: if b then flickerstreak@116: return format("%s:%s", b.bar:GetName(), b.idx) flickerstreak@88: end flickerstreak@88: end flickerstreak@88: flickerstreak@116: function GetHotkey(f) flickerstreak@116: return KB:ToShortKey(GetBindingKey(format("CLICK %s:LeftButton",f:GetName()))) flickerstreak@116: end flickerstreak@116: flickerstreak@116: -- This is a bit hokey : install a bare hook on ActionButton_UpdateHotkey because flickerstreak@116: -- even though it's secure it's never called in a way that can cause taint. This is flickerstreak@116: -- for performance reasons to avoid having to hook frame:OnEvent securely. flickerstreak@116: local UpdateHotkey_old = ActionButton_UpdateHotkeys flickerstreak@116: ActionButton_UpdateHotkeys = function( frame, ... ) flickerstreak@116: local b = frame._reactionButton flickerstreak@116: if b then flickerstreak@116: b.hotkey:SetText( GetHotkey(frame) ) flickerstreak@116: else flickerstreak@116: return UpdateHotkey_old(frame, ...) flickerstreak@88: end flickerstreak@88: end flickerstreak@116: end flickerstreak@88: flickerstreak@116: local meta = {__index = Button} flickerstreak@90: flickerstreak@116: function Button:New( handle, idx, config, barConfig ) flickerstreak@116: local bar = handle.bar flickerstreak@116: flickerstreak@116: -- create new self flickerstreak@116: self = setmetatable( flickerstreak@116: { flickerstreak@116: bar = bar, flickerstreak@116: idx = idx, flickerstreak@116: config = config, flickerstreak@116: barConfig = barConfig, flickerstreak@116: }, meta ) flickerstreak@116: flickerstreak@116: local name = config.name or ("ReAction_%s_%s_%d"):format(bar:GetName(),moduleID,idx) flickerstreak@116: self.name = name flickerstreak@116: config.name = name flickerstreak@116: local lastButton = handle:GetLastButton() flickerstreak@116: config.actionID = IDAlloc:Acquire(config.actionID, lastButton and lastButton.config.actionID) -- gets a free one if none configured flickerstreak@116: self.nPages = 1 flickerstreak@116: flickerstreak@116: -- have to recycle frames with the same name: CreateFrame() doesn't overwrite flickerstreak@116: -- existing globals. Can't set to nil in the global because it's then tainted. flickerstreak@116: local parent = bar:GetFrame() flickerstreak@116: local f = frameRecycler[name] flickerstreak@116: if f then flickerstreak@116: f:SetParent(parent) flickerstreak@116: else flickerstreak@116: f = CreateFrame("CheckButton", name, parent, "ActionBarButtonTemplate") flickerstreak@116: -- ditch the old hotkey text because it's tied in ActionButton_Update() to the flickerstreak@116: -- standard binding. flickerstreak@116: local hotkey = _G[name.."HotKey"] flickerstreak@116: hotkey:SetParent(trash) flickerstreak@116: hotkey = f:CreateFontString(nil, "ARTWORK", "NumberFontNormalSmallGray") flickerstreak@116: hotkey:SetWidth(36) flickerstreak@116: hotkey:SetHeight(18) flickerstreak@116: hotkey:SetJustifyH("RIGHT") flickerstreak@116: hotkey:SetJustifyV("TOP") flickerstreak@116: hotkey:SetPoint("TOPLEFT",f,"TOPLEFT",-2,-2) flickerstreak@116: f.hotkey = hotkey flickerstreak@116: f.icon = _G[name.."Icon"] flickerstreak@116: f.flash = _G[name.."Flash"] flickerstreak@116: f:SetScript("OnUpdate",OnUpdate) flickerstreak@116: end flickerstreak@116: flickerstreak@116: f._reactionButton = self flickerstreak@116: flickerstreak@116: self.hotkey = f.hotkey flickerstreak@116: self.border = _G[name.."Border"] flickerstreak@116: flickerstreak@116: f:SetAttribute("action", config.actionID) flickerstreak@116: f:SetAttribute("default-action", config.actionID) flickerstreak@116: -- install mind control actions for all buttons just for simplicity flickerstreak@116: if self.idx <= 12 then flickerstreak@121: f:SetAttribute("mc-action", 120 + self.idx) flickerstreak@116: end flickerstreak@121: flickerstreak@121: -- set a tooltip onEnter flickerstreak@121: f:SetScript("OnEnter", flickerstreak@121: function(frame) flickerstreak@121: if ReAction:GetKeybindMode() then flickerstreak@121: KB:Set(frame) flickerstreak@121: elseif frame.vehicleExitMode then flickerstreak@121: GameTooltip_AddNewbieTip(frame, LEAVE_VEHICLE, 1.0, 1.0, 1.0, nil); flickerstreak@121: else flickerstreak@121: ActionButton_SetTooltip(frame) flickerstreak@121: end flickerstreak@121: end) flickerstreak@116: flickerstreak@116: -- set a _childupdate handler, called within the header's context flickerstreak@116: f:SetAttribute("_childupdate", flickerstreak@116: -- function _childupdate(self, snippetid, message) flickerstreak@116: [[ flickerstreak@116: local action = "default-action" flickerstreak@121: if (doVehicle and mcVehicleState == "vehicle") or flickerstreak@121: (doMindControl and mcVehicleState == "mc") then flickerstreak@121: action = "mc-action" flickerstreak@116: elseif page and state and page[state] then flickerstreak@116: action = "action-"..page[state] flickerstreak@88: end flickerstreak@121: flickerstreak@116: local value = self:GetAttribute(action) flickerstreak@116: if value then flickerstreak@116: self:SetAttribute("action",value) flickerstreak@116: end flickerstreak@116: ]]) flickerstreak@116: flickerstreak@121: -- Install a handler for the 7th button (only) to show/hide a flickerstreak@121: -- vehicle exit button. This is more than a little bit hack-ish and flickerstreak@121: -- will be replaced in the next iteration with the reimplementation flickerstreak@121: -- of action button functionality. flickerstreak@121: if idx == 7 then flickerstreak@121: local barFrame = bar:GetFrame() flickerstreak@121: function barFrame:ShowVehicleExit(show) flickerstreak@121: local tx = f.vehicleExitTexture flickerstreak@121: if show then flickerstreak@121: if not tx then flickerstreak@121: tx = f:CreateTexture(nil,"ARTWORK") flickerstreak@121: tx:SetAllPoints() flickerstreak@121: -- copied from Blizzard/VehicleMenuBar.lua SkinsData flickerstreak@121: tx:SetTexture("Interface\\Vehicles\\UI-Vehicles-Button-Exit-Up") flickerstreak@121: tx:SetTexCoord(0.140625, 0.859375, 0.140625, 0.859375) flickerstreak@121: f.vehicleExitTexture = tx flickerstreak@121: end flickerstreak@121: tx:Show() flickerstreak@121: f.vehicleExitMode = true flickerstreak@121: elseif tx then flickerstreak@121: tx:SetTexCoord(0,1,0,1) flickerstreak@121: tx:Hide() flickerstreak@121: f.vehicleExitMode = false flickerstreak@121: end flickerstreak@121: end flickerstreak@121: flickerstreak@121: f:SetAttribute("macrotext","/run VehicleExit()") flickerstreak@121: f:SetAttribute("_childupdate-vehicle", flickerstreak@121: -- function _childupdate-vehicle(self, snippetid, message) flickerstreak@121: [[ flickerstreak@121: local show = (mcVehicleState == "vehicle") flickerstreak@121: if show then flickerstreak@121: self:SetAttribute("type","macro") flickerstreak@121: self:SetAttribute("showgrid",self:GetAttribute("showgrid")+1) flickerstreak@121: self:Show() flickerstreak@121: else flickerstreak@121: self:SetAttribute("type","action") flickerstreak@121: local showgrid = self:GetAttribute("showgrid") flickerstreak@121: showgrid = showgrid - 1 flickerstreak@121: if showgrid < 0 then showgrid = 0 end flickerstreak@121: self:SetAttribute("showgrid",self:GetAttribute("showgrid")-1) flickerstreak@121: if showgrid <= 0 then flickerstreak@121: self:Hide() flickerstreak@121: end flickerstreak@121: end flickerstreak@121: control:CallMethod("ShowVehicleExit",show) flickerstreak@121: ]]) flickerstreak@121: end flickerstreak@121: flickerstreak@116: -- install drag wrappers to lock buttons flickerstreak@116: bar:GetFrame():WrapScript(f, "OnDragStart", flickerstreak@116: -- OnDragStart(self, button, kind, value, ...) flickerstreak@116: [[ flickerstreak@116: if lockButtons and (PlayerInCombat() or not lockButtonsCombat) and not IsModifiedClick("PICKUPACTION") then flickerstreak@116: return "clear" flickerstreak@116: end flickerstreak@116: ]]) flickerstreak@116: flickerstreak@116: self.frame = f flickerstreak@116: flickerstreak@116: -- initialize the hide state flickerstreak@116: f:SetAttribute("showgrid",0) flickerstreak@116: self:ShowGrid(not barConfig.hideEmpty) flickerstreak@116: if ReAction:GetConfigMode() then flickerstreak@116: self:ShowGrid(true) flickerstreak@116: end flickerstreak@116: flickerstreak@116: -- set the hotkey text flickerstreak@116: self.hotkey:SetText( GetHotkey(self.frame) ) flickerstreak@116: flickerstreak@116: -- show the ID label if applicable flickerstreak@116: self:ShowActionIDLabel(ReAction:GetConfigMode()) flickerstreak@116: flickerstreak@116: -- attach to skinner flickerstreak@116: bar:SkinButton(self, flickerstreak@116: { flickerstreak@116: HotKey = self.hotkey, flickerstreak@116: } flickerstreak@116: ) flickerstreak@116: flickerstreak@116: self:Refresh() flickerstreak@116: return self flickerstreak@116: end flickerstreak@116: flickerstreak@116: function Button:Destroy() flickerstreak@116: local f = self.frame flickerstreak@116: f:UnregisterAllEvents() flickerstreak@116: f:Hide() flickerstreak@116: f:SetParent(UIParent) flickerstreak@116: f:ClearAllPoints() flickerstreak@121: f:SetAttribute("_childupdate",nil) flickerstreak@121: f:SetAttribute("_childupdate-vehicle",nil) flickerstreak@116: if self.name then flickerstreak@116: frameRecycler[self.name] = f flickerstreak@116: end flickerstreak@116: if self.config.actionID then flickerstreak@116: IDAlloc:Release(self.config.actionID) flickerstreak@116: end flickerstreak@116: if self.config.pageactions then flickerstreak@116: for _, id in ipairs(self.config.pageactions) do flickerstreak@116: IDAlloc:Release(id) flickerstreak@88: end flickerstreak@88: end flickerstreak@116: f._reactionButton = nil flickerstreak@116: self.frame = nil flickerstreak@116: self.config = nil flickerstreak@116: self.bar = nil flickerstreak@116: end flickerstreak@88: flickerstreak@116: function Button:Refresh() flickerstreak@116: local f = self.frame flickerstreak@116: self.bar:PlaceButton(self, 36, 36) flickerstreak@116: self:RefreshPages() flickerstreak@116: end flickerstreak@116: flickerstreak@116: function Button:GetFrame() flickerstreak@116: return self.frame flickerstreak@116: end flickerstreak@116: flickerstreak@116: function Button:GetName() flickerstreak@116: return self.name flickerstreak@116: end flickerstreak@116: flickerstreak@116: function Button:GetConfig() flickerstreak@116: return self.config flickerstreak@116: end flickerstreak@116: flickerstreak@116: function Button:GetActionID(page) flickerstreak@116: if page == nil then flickerstreak@116: -- get the effective ID flickerstreak@116: return self.frame.action -- kept up-to-date by Blizzard's ActionButton_CalculateAction() flickerstreak@116: else flickerstreak@116: if page == 1 then flickerstreak@116: return self.config.actionID flickerstreak@116: else flickerstreak@116: return self.config.pageactions and self.config.pageactions[page] or self.config.actionID flickerstreak@116: end flickerstreak@88: end flickerstreak@88: end flickerstreak@116: flickerstreak@116: function Button:SetActionID( id, page ) flickerstreak@116: id = tonumber(id) flickerstreak@116: page = tonumber(page) flickerstreak@116: if id == nil or id < 1 or id > 120 then flickerstreak@116: error("Button:SetActionID - invalid action ID") flickerstreak@116: end flickerstreak@116: if page and page ~= 1 then flickerstreak@116: if not self.config.pageactions then flickerstreak@116: self.config.pageactions = { } flickerstreak@116: end flickerstreak@116: if self.config.pageactions[page] then flickerstreak@116: IDAlloc:Release(self.config.pageactions[page]) flickerstreak@116: end flickerstreak@116: self.config.pageactions[page] = id flickerstreak@116: IDAlloc:Acquire(self.config.pageactions[page]) flickerstreak@116: self.frame:SetAttribute(("action-page%d"):format(page),id) flickerstreak@116: else flickerstreak@116: IDAlloc:Release(self.config.actionID) flickerstreak@116: self.config.actionID = id flickerstreak@116: IDAlloc:Acquire(self.config.actionID) flickerstreak@116: self.frame:SetAttribute("action",id) flickerstreak@116: if self.config.pageactions then flickerstreak@116: self.config.pageactions[1] = id flickerstreak@116: self.frame:SetAttribute("action-page1",id) flickerstreak@116: end flickerstreak@116: end flickerstreak@116: end flickerstreak@116: flickerstreak@116: function Button:RefreshPages( force ) flickerstreak@116: local nPages = self.barConfig.nPages flickerstreak@116: if nPages and (nPages ~= self.nPages or force) then flickerstreak@116: local f = self:GetFrame() flickerstreak@116: local c = self.config.pageactions flickerstreak@116: if nPages > 1 and not c then flickerstreak@116: c = { } flickerstreak@116: self.config.pageactions = c flickerstreak@116: end flickerstreak@116: for i = 1, nPages do flickerstreak@116: if i > 1 then flickerstreak@116: c[i] = IDAlloc:Acquire(c[i], self.config.actionID + (i-1)*self.bar:GetNumButtons()) flickerstreak@116: else flickerstreak@116: c[i] = self.config.actionID -- page 1 is the same as the base actionID flickerstreak@116: end flickerstreak@116: f:SetAttribute(("action-page%d"):format(i),c[i]) flickerstreak@116: end flickerstreak@116: for i = nPages+1, #c do flickerstreak@116: IDAlloc:Release(c[i]) flickerstreak@116: c[i] = nil flickerstreak@116: f:SetAttribute(("action-page%d"):format(i),nil) flickerstreak@116: end flickerstreak@116: self.nPages = nPages flickerstreak@116: end flickerstreak@116: end flickerstreak@116: flickerstreak@116: function Button:ShowGrid( show ) flickerstreak@116: if not InCombatLockdown() then flickerstreak@116: local f = self.frame flickerstreak@116: local count = f:GetAttribute("showgrid") flickerstreak@116: if show then flickerstreak@116: count = count + 1 flickerstreak@116: else flickerstreak@116: count = count - 1 flickerstreak@116: end flickerstreak@116: if count < 0 then flickerstreak@116: count = 0 flickerstreak@116: end flickerstreak@116: f:SetAttribute("showgrid",count) flickerstreak@116: flickerstreak@116: if count >= 1 and not f:GetAttribute("statehidden") then flickerstreak@116: if LBF then flickerstreak@116: LBF:SetNormalVertexColor(self.frame, 1.0, 1.0, 1.0, 0.5) flickerstreak@116: else flickerstreak@116: self.frame:GetNormalTexture():SetVertexColor(1.0, 1.0, 1.0, 0.5); flickerstreak@116: end flickerstreak@116: f:Show() flickerstreak@116: elseif count < 1 and not HasAction(self:GetActionID()) then flickerstreak@116: f:Hide() flickerstreak@116: end flickerstreak@116: end flickerstreak@116: end flickerstreak@116: flickerstreak@116: function Button:ShowActionIDLabel( show ) flickerstreak@116: local f = self:GetFrame() flickerstreak@116: if show then flickerstreak@116: local id = self:GetActionID() flickerstreak@116: if not f.actionIDLabel then flickerstreak@116: local label = f:CreateFontString(nil,"OVERLAY","GameFontNormalLarge") flickerstreak@116: label:SetAllPoints() flickerstreak@116: label:SetJustifyH("CENTER") flickerstreak@116: label:SetShadowColor(0,0,0,1) flickerstreak@116: label:SetShadowOffset(2,-2) flickerstreak@116: f.actionIDLabel = label -- store the label with the frame for recycling flickerstreak@116: flickerstreak@116: f:HookScript("OnAttributeChanged", flickerstreak@116: function(frame, attr, value) flickerstreak@116: if label:IsVisible() and attr:match("action") then flickerstreak@116: label:SetText(tostring(frame.action)) flickerstreak@116: end flickerstreak@116: end) flickerstreak@116: end flickerstreak@116: f.actionIDLabel:SetText(tostring(id)) flickerstreak@116: f.actionIDLabel:Show() flickerstreak@116: elseif f.actionIDLabel then flickerstreak@116: f.actionIDLabel:Hide() flickerstreak@116: end flickerstreak@116: end flickerstreak@116: flickerstreak@116: function Button:SetKeybindMode( mode ) flickerstreak@116: if mode then flickerstreak@121: self.frame.GetActionName = GetActionName flickerstreak@121: self.frame.GetHotkey = GetHotkey flickerstreak@116: -- set the border for all buttons to the keybind-enable color flickerstreak@116: self.border:SetVertexColor(KB:GetColorKeyBoundMode()) flickerstreak@116: self.border:Show() flickerstreak@116: elseif IsEquippedAction(self:GetActionID()) then flickerstreak@116: self.border:SetVertexColor(0, 1.0, 0, 0.35) -- from ActionButton.lua flickerstreak@116: else flickerstreak@116: self.border:Hide() flickerstreak@116: end flickerstreak@116: end