| 
flickerstreak@24
 | 
     1 --[[
 | 
| 
flickerstreak@53
 | 
     2   ReAction Action button module.
 | 
| 
flickerstreak@24
 | 
     3 
 | 
| 
flickerstreak@24
 | 
     4   The button module implements standard action button functionality by wrapping Blizzard's 
 | 
| 
flickerstreak@87
 | 
     5   ActionBarButtonTemplate frame and associated functions.
 | 
| 
flickerstreak@77
 | 
     6 
 | 
| 
flickerstreak@87
 | 
     7   It also provides action remapping support for multiple pages and possessed targets
 | 
| 
flickerstreak@90
 | 
     8   (Mind Control, Eyes of the Beast, Karazhan Chess event, various quests, etc).
 | 
| 
flickerstreak@24
 | 
     9 --]]
 | 
| 
flickerstreak@24
 | 
    10 
 | 
| 
flickerstreak@24
 | 
    11 -- local imports
 | 
| 
flickerstreak@24
 | 
    12 local ReAction = ReAction
 | 
| 
flickerstreak@24
 | 
    13 local L = ReAction.L
 | 
| 
flickerstreak@24
 | 
    14 local _G = _G
 | 
| 
flickerstreak@24
 | 
    15 local CreateFrame = CreateFrame
 | 
| 
flickerstreak@88
 | 
    16 local format = string.format
 | 
| 
flickerstreak@24
 | 
    17 
 | 
| 
flickerstreak@87
 | 
    18 ReAction:UpdateRevision("$Revision$")
 | 
| 
flickerstreak@77
 | 
    19 
 | 
| 
flickerstreak@88
 | 
    20 -- libraries
 | 
| 
flickerstreak@88
 | 
    21 local KB = LibStub("LibKeyBound-1.0")
 | 
| 
flickerstreak@88
 | 
    22 
 | 
| 
flickerstreak@24
 | 
    23 -- module declaration
 | 
| 
flickerstreak@24
 | 
    24 local moduleID = "Action"
 | 
| 
flickerstreak@28
 | 
    25 local module = ReAction:NewModule( moduleID )
 | 
| 
flickerstreak@24
 | 
    26 
 | 
| 
flickerstreak@90
 | 
    27 -- Class declarations
 | 
| 
flickerstreak@77
 | 
    28 local Button = { }
 | 
| 
flickerstreak@90
 | 
    29 local Handle = { }
 | 
| 
flickerstreak@90
 | 
    30 local PropHandler = { }
 | 
| 
flickerstreak@87
 | 
    31 
 | 
| 
flickerstreak@77
 | 
    32 -- Event handlers
 | 
| 
flickerstreak@24
 | 
    33 function module:OnInitialize()
 | 
| 
flickerstreak@28
 | 
    34   self.db = ReAction.db:RegisterNamespace( moduleID,
 | 
| 
flickerstreak@24
 | 
    35     { 
 | 
| 
flickerstreak@28
 | 
    36       profile = {
 | 
| 
flickerstreak@75
 | 
    37         bars = { },
 | 
| 
flickerstreak@28
 | 
    38       }
 | 
| 
flickerstreak@24
 | 
    39     }
 | 
| 
flickerstreak@24
 | 
    40   )
 | 
| 
flickerstreak@90
 | 
    41   self.handles = setmetatable({ }, weak)
 | 
| 
flickerstreak@49
 | 
    42 
 | 
| 
flickerstreak@63
 | 
    43   ReAction:RegisterBarOptionGenerator(self, "GetBarOptions")
 | 
| 
flickerstreak@63
 | 
    44 
 | 
| 
flickerstreak@90
 | 
    45   ReAction.RegisterCallback(self, "OnCreateBar")
 | 
| 
flickerstreak@90
 | 
    46   ReAction.RegisterCallback(self, "OnRefreshBar")
 | 
| 
flickerstreak@63
 | 
    47   ReAction.RegisterCallback(self, "OnDestroyBar")
 | 
| 
flickerstreak@63
 | 
    48   ReAction.RegisterCallback(self, "OnEraseBar")
 | 
| 
flickerstreak@63
 | 
    49   ReAction.RegisterCallback(self, "OnRenameBar")
 | 
| 
flickerstreak@63
 | 
    50   ReAction.RegisterCallback(self, "OnConfigModeChanged")
 | 
| 
flickerstreak@63
 | 
    51 
 | 
| 
flickerstreak@88
 | 
    52   KB.RegisterCallback(self, "LIBKEYBOUND_ENABLED")
 | 
| 
flickerstreak@88
 | 
    53   KB.RegisterCallback(self, "LIBKEYBOUND_DISABLED")
 | 
| 
flickerstreak@88
 | 
    54   KB.RegisterCallback(self, "LIBKEYBOUND_MODE_COLOR_CHANGED","LIBKEYBOUND_ENABLED")
 | 
| 
flickerstreak@24
 | 
    55 end
 | 
| 
flickerstreak@24
 | 
    56 
 | 
| 
flickerstreak@24
 | 
    57 function module:OnEnable()
 | 
| 
flickerstreak@53
 | 
    58   ReAction:RegisterBarType(L["Action Bar"], 
 | 
| 
flickerstreak@53
 | 
    59     { 
 | 
| 
flickerstreak@53
 | 
    60       type = moduleID,
 | 
| 
flickerstreak@91
 | 
    61       defaultButtonSize = 36,
 | 
| 
flickerstreak@53
 | 
    62       defaultBarRows = 1,
 | 
| 
flickerstreak@53
 | 
    63       defaultBarCols = 12,
 | 
| 
flickerstreak@53
 | 
    64       defaultBarSpacing = 3
 | 
| 
flickerstreak@53
 | 
    65     }, true)
 | 
| 
flickerstreak@90
 | 
    66   ReAction:GetModule("State"):RegisterStateProperty("page", nil, PropHandler.GetOptions(), PropHandler)
 | 
| 
flickerstreak@24
 | 
    67 end
 | 
| 
flickerstreak@24
 | 
    68 
 | 
| 
flickerstreak@24
 | 
    69 function module:OnDisable()
 | 
| 
flickerstreak@53
 | 
    70   ReAction:UnregisterBarType(L["Action Bar"])
 | 
| 
flickerstreak@90
 | 
    71   ReAction:GetModule("State"):UnregisterStateProperty("page")
 | 
| 
flickerstreak@90
 | 
    72 end
 | 
| 
flickerstreak@90
 | 
    73 
 | 
| 
flickerstreak@90
 | 
    74 function module:OnCreateBar(event, bar, name)
 | 
| 
flickerstreak@90
 | 
    75   if bar.config.type == moduleID then
 | 
| 
flickerstreak@90
 | 
    76     local profile = self.db.profile
 | 
| 
flickerstreak@90
 | 
    77     if profile.bars[name] == nil then
 | 
| 
flickerstreak@90
 | 
    78       profile.bars[name] = {
 | 
| 
flickerstreak@90
 | 
    79         buttons = { }
 | 
| 
flickerstreak@90
 | 
    80       }
 | 
| 
flickerstreak@90
 | 
    81     end
 | 
| 
flickerstreak@90
 | 
    82     if self.handles[bar] == nil then
 | 
| 
flickerstreak@90
 | 
    83       self.handles[bar] = Handle:New(bar, profile.bars[name])
 | 
| 
flickerstreak@90
 | 
    84     end
 | 
| 
flickerstreak@90
 | 
    85   end
 | 
| 
flickerstreak@24
 | 
    86 end
 | 
| 
flickerstreak@24
 | 
    87 
 | 
| 
flickerstreak@63
 | 
    88 function module:OnRefreshBar(event, bar, name)
 | 
| 
flickerstreak@90
 | 
    89   if self.handles[bar] then
 | 
| 
flickerstreak@90
 | 
    90     self.handles[bar]:Refresh()
 | 
| 
flickerstreak@24
 | 
    91   end
 | 
| 
flickerstreak@24
 | 
    92 end
 | 
| 
flickerstreak@24
 | 
    93 
 | 
| 
flickerstreak@63
 | 
    94 function module:OnDestroyBar(event, bar, name)
 | 
| 
flickerstreak@90
 | 
    95   if self.handles[bar] then
 | 
| 
flickerstreak@90
 | 
    96     self.handles[bar]:Destroy()
 | 
| 
flickerstreak@90
 | 
    97     self.handles[bar] = nil
 | 
| 
flickerstreak@24
 | 
    98   end
 | 
| 
flickerstreak@24
 | 
    99 end
 | 
| 
flickerstreak@24
 | 
   100 
 | 
| 
flickerstreak@63
 | 
   101 function module:OnEraseBar(event, bar, name)
 | 
| 
flickerstreak@75
 | 
   102   self.db.profile.bars[name] = nil
 | 
| 
flickerstreak@24
 | 
   103 end
 | 
| 
flickerstreak@24
 | 
   104 
 | 
| 
flickerstreak@63
 | 
   105 function module:OnRenameBar(event, bar, oldname, newname)
 | 
| 
flickerstreak@75
 | 
   106   b = self.db.profile.bars
 | 
| 
flickerstreak@75
 | 
   107   b[newname], b[oldname] = b[oldname], nil
 | 
| 
flickerstreak@48
 | 
   108 end
 | 
| 
flickerstreak@48
 | 
   109 
 | 
| 
flickerstreak@63
 | 
   110 function module:OnConfigModeChanged(event, mode)
 | 
| 
flickerstreak@90
 | 
   111   for _, h in pairs(self.handles) do
 | 
| 
flickerstreak@90
 | 
   112     h:SetConfigMode(mode)
 | 
| 
flickerstreak@24
 | 
   113   end
 | 
| 
flickerstreak@24
 | 
   114 end
 | 
| 
flickerstreak@24
 | 
   115 
 | 
| 
flickerstreak@88
 | 
   116 function module:LIBKEYBOUND_ENABLED(evt)
 | 
| 
flickerstreak@90
 | 
   117   for _, h in pairs(self.handles) do
 | 
| 
flickerstreak@90
 | 
   118     h:SetKeybindMode(true)
 | 
| 
flickerstreak@88
 | 
   119   end
 | 
| 
flickerstreak@88
 | 
   120 end
 | 
| 
flickerstreak@88
 | 
   121 
 | 
| 
flickerstreak@88
 | 
   122 function module:LIBKEYBOUND_DISABLED(evt)
 | 
| 
flickerstreak@90
 | 
   123   for _, h in pairs(self.handles) do
 | 
| 
flickerstreak@90
 | 
   124     h:SetKeybindMode(false)
 | 
| 
flickerstreak@88
 | 
   125   end
 | 
| 
flickerstreak@88
 | 
   126 end
 | 
| 
flickerstreak@88
 | 
   127 
 | 
| 
flickerstreak@50
 | 
   128 
 | 
| 
flickerstreak@90
 | 
   129 ---- Interface ----
 | 
| 
flickerstreak@90
 | 
   130 function module:GetBarOptions(bar)
 | 
| 
flickerstreak@90
 | 
   131   local h = self.handles[bar]
 | 
| 
flickerstreak@90
 | 
   132   if h then
 | 
| 
flickerstreak@90
 | 
   133     return h:GetOptions()
 | 
| 
flickerstreak@90
 | 
   134   end
 | 
| 
flickerstreak@90
 | 
   135 end
 | 
| 
flickerstreak@90
 | 
   136 
 | 
| 
flickerstreak@90
 | 
   137 
 | 
| 
flickerstreak@90
 | 
   138 ---- Bar Handle ----
 | 
| 
flickerstreak@90
 | 
   139 
 | 
| 
flickerstreak@87
 | 
   140 do
 | 
| 
flickerstreak@87
 | 
   141   local options = {
 | 
| 
flickerstreak@87
 | 
   142     hideEmpty = {
 | 
| 
flickerstreak@87
 | 
   143       name = L["Hide Empty Buttons"],
 | 
| 
flickerstreak@87
 | 
   144       order = 1,
 | 
| 
flickerstreak@87
 | 
   145       type = "toggle",
 | 
| 
flickerstreak@87
 | 
   146       width = "double",
 | 
| 
flickerstreak@87
 | 
   147       get  = "GetHideEmpty",
 | 
| 
flickerstreak@87
 | 
   148       set  = "SetHideEmpty",
 | 
| 
flickerstreak@87
 | 
   149     },
 | 
| 
flickerstreak@87
 | 
   150     pages = {
 | 
| 
flickerstreak@87
 | 
   151       name  = L["# Pages"],
 | 
| 
flickerstreak@87
 | 
   152       desc  = L["Use the Dynamic State tab to specify page transitions"],
 | 
| 
flickerstreak@87
 | 
   153       order = 2,
 | 
| 
flickerstreak@87
 | 
   154       type  = "range",
 | 
| 
flickerstreak@87
 | 
   155       min   = 1,
 | 
| 
flickerstreak@87
 | 
   156       max   = 10,
 | 
| 
flickerstreak@87
 | 
   157       step  = 1,
 | 
| 
flickerstreak@87
 | 
   158       get   = "GetNumPages",
 | 
| 
flickerstreak@87
 | 
   159       set   = "SetNumPages",
 | 
| 
flickerstreak@87
 | 
   160     },
 | 
| 
flickerstreak@90
 | 
   161     mindcontrol = {
 | 
| 
flickerstreak@90
 | 
   162       name = L["Mind Control Support"],
 | 
| 
flickerstreak@90
 | 
   163       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@90
 | 
   164       order = 3,
 | 
| 
flickerstreak@90
 | 
   165       type = "toggle",
 | 
| 
flickerstreak@90
 | 
   166       width = "double",
 | 
| 
flickerstreak@90
 | 
   167       set = "SetMindControl",
 | 
| 
flickerstreak@90
 | 
   168       get = "GetMindControl",
 | 
| 
flickerstreak@90
 | 
   169     },
 | 
| 
flickerstreak@87
 | 
   170     actions = {
 | 
| 
flickerstreak@87
 | 
   171       name   = L["Edit Action IDs"],
 | 
| 
flickerstreak@90
 | 
   172       order  = 4,
 | 
| 
flickerstreak@87
 | 
   173       type   = "group",
 | 
| 
flickerstreak@87
 | 
   174       inline = true,
 | 
| 
flickerstreak@87
 | 
   175       args   = {
 | 
| 
flickerstreak@87
 | 
   176         method = {
 | 
| 
flickerstreak@87
 | 
   177           name   = L["Assign"],
 | 
| 
flickerstreak@87
 | 
   178           order  = 1,
 | 
| 
flickerstreak@87
 | 
   179           type   = "select",
 | 
| 
flickerstreak@87
 | 
   180           width  = "double",
 | 
| 
flickerstreak@87
 | 
   181           values = { [0] = L["Choose Method..."],
 | 
| 
flickerstreak@87
 | 
   182                      [1] = L["Individually"],
 | 
| 
flickerstreak@87
 | 
   183                      [2] = L["All at Once"], },
 | 
| 
flickerstreak@87
 | 
   184           get    = "GetActionEditMethod",
 | 
| 
flickerstreak@87
 | 
   185           set    = "SetActionEditMethod",
 | 
| 
flickerstreak@87
 | 
   186         },
 | 
| 
flickerstreak@87
 | 
   187         rowSelect = {
 | 
| 
flickerstreak@87
 | 
   188           name   = L["Row"],
 | 
| 
flickerstreak@87
 | 
   189           desc   = L["Rows are numbered top to bottom"],
 | 
| 
flickerstreak@87
 | 
   190           order  = 2,
 | 
| 
flickerstreak@87
 | 
   191           type   = "select",
 | 
| 
flickerstreak@87
 | 
   192           width  = "half",
 | 
| 
flickerstreak@87
 | 
   193           hidden = "IsButtonSelectHidden",
 | 
| 
flickerstreak@87
 | 
   194           values = "GetRowList",
 | 
| 
flickerstreak@87
 | 
   195           get    = "GetSelectedRow",
 | 
| 
flickerstreak@87
 | 
   196           set    = "SetSelectedRow",
 | 
| 
flickerstreak@87
 | 
   197         },
 | 
| 
flickerstreak@87
 | 
   198         colSelect = {
 | 
| 
flickerstreak@87
 | 
   199           name   = L["Col"],
 | 
| 
flickerstreak@87
 | 
   200           desc   = L["Columns are numbered left to right"],
 | 
| 
flickerstreak@87
 | 
   201           order  = 3,
 | 
| 
flickerstreak@87
 | 
   202           type   = "select",
 | 
| 
flickerstreak@87
 | 
   203           width  = "half",
 | 
| 
flickerstreak@87
 | 
   204           hidden = "IsButtonSelectHidden",
 | 
| 
flickerstreak@87
 | 
   205           values = "GetColumnList",
 | 
| 
flickerstreak@87
 | 
   206           get    = "GetSelectedColumn",
 | 
| 
flickerstreak@87
 | 
   207           set    = "SetSelectedColumn",
 | 
| 
flickerstreak@87
 | 
   208         },
 | 
| 
flickerstreak@87
 | 
   209         pageSelect = {
 | 
| 
flickerstreak@87
 | 
   210           name   = L["Page"],
 | 
| 
flickerstreak@87
 | 
   211           order  = 4,
 | 
| 
flickerstreak@87
 | 
   212           type   = "select",
 | 
| 
flickerstreak@87
 | 
   213           width  = "half",
 | 
| 
flickerstreak@87
 | 
   214           hidden = "IsPageSelectHidden",
 | 
| 
flickerstreak@87
 | 
   215           values = "GetPageList",
 | 
| 
flickerstreak@87
 | 
   216           get    = "GetSelectedPage",
 | 
| 
flickerstreak@87
 | 
   217           set    = "SetSelectedPage",
 | 
| 
flickerstreak@87
 | 
   218         },
 | 
| 
flickerstreak@87
 | 
   219         single = {
 | 
| 
flickerstreak@87
 | 
   220           name   = L["Action ID"],
 | 
| 
flickerstreak@87
 | 
   221           usage  = L["Specify ID 1-120"],
 | 
| 
flickerstreak@87
 | 
   222           order  = 5,
 | 
| 
flickerstreak@87
 | 
   223           type   = "input",
 | 
| 
flickerstreak@87
 | 
   224           width  = "half",
 | 
| 
flickerstreak@87
 | 
   225           hidden = "IsButtonSelectHidden",
 | 
| 
flickerstreak@87
 | 
   226           get    = "GetActionID",
 | 
| 
flickerstreak@87
 | 
   227           set    = "SetActionID",
 | 
| 
flickerstreak@87
 | 
   228           validate = "ValidateActionID",
 | 
| 
flickerstreak@87
 | 
   229         },
 | 
| 
flickerstreak@87
 | 
   230         multi = {
 | 
| 
flickerstreak@87
 | 
   231           name   = L["ID List"],
 | 
| 
flickerstreak@87
 | 
   232           usage  = L["Specify a comma-separated list of IDs for each button in the bar (in order). Separate multiple pages with semicolons (;)"],
 | 
| 
flickerstreak@87
 | 
   233           order  = 6,
 | 
| 
flickerstreak@87
 | 
   234           type   = "input",
 | 
| 
flickerstreak@87
 | 
   235           multiline = true,
 | 
| 
flickerstreak@87
 | 
   236           width  = "double",
 | 
| 
flickerstreak@87
 | 
   237           hidden = "IsMultiIDHidden",
 | 
| 
flickerstreak@87
 | 
   238           get    = "GetMultiID",
 | 
| 
flickerstreak@87
 | 
   239           set    = "SetMultiID",
 | 
| 
flickerstreak@87
 | 
   240           validate = "ValidateMultiID",
 | 
| 
flickerstreak@90
 | 
   241         },
 | 
| 
flickerstreak@90
 | 
   242       },
 | 
| 
flickerstreak@87
 | 
   243     },
 | 
| 
flickerstreak@87
 | 
   244   }
 | 
| 
flickerstreak@77
 | 
   245 
 | 
| 
flickerstreak@90
 | 
   246   local weak  = { __mode="k" }
 | 
| 
flickerstreak@90
 | 
   247   local meta = { __index = Handle }
 | 
| 
flickerstreak@90
 | 
   248 
 | 
| 
flickerstreak@90
 | 
   249   function Handle:New( bar, config )
 | 
| 
flickerstreak@90
 | 
   250     local self = setmetatable(
 | 
| 
flickerstreak@90
 | 
   251       {
 | 
| 
flickerstreak@90
 | 
   252         bar = bar,
 | 
| 
flickerstreak@90
 | 
   253         config = config,
 | 
| 
flickerstreak@90
 | 
   254         btns = { }
 | 
| 
flickerstreak@90
 | 
   255       }, 
 | 
| 
flickerstreak@90
 | 
   256       meta)
 | 
| 
flickerstreak@90
 | 
   257     
 | 
| 
flickerstreak@90
 | 
   258     if self.config.buttons == nil then
 | 
| 
flickerstreak@90
 | 
   259       self.config.buttons = { }
 | 
| 
flickerstreak@90
 | 
   260     end
 | 
| 
flickerstreak@90
 | 
   261     self:Refresh()
 | 
| 
flickerstreak@90
 | 
   262     return self
 | 
| 
flickerstreak@90
 | 
   263   end
 | 
| 
flickerstreak@90
 | 
   264 
 | 
| 
flickerstreak@90
 | 
   265   function Handle:Refresh()
 | 
| 
flickerstreak@90
 | 
   266     local r, c = self.bar:GetButtonGrid()
 | 
| 
flickerstreak@90
 | 
   267     local n = r*c
 | 
| 
flickerstreak@90
 | 
   268     local btnCfg = self.config.buttons
 | 
| 
flickerstreak@90
 | 
   269     if n ~= #self.btns then
 | 
| 
flickerstreak@90
 | 
   270       for i = 1, n do
 | 
| 
flickerstreak@90
 | 
   271         if btnCfg[i] == nil then
 | 
| 
flickerstreak@90
 | 
   272           btnCfg[i] = {}
 | 
| 
flickerstreak@90
 | 
   273         end
 | 
| 
flickerstreak@90
 | 
   274         if self.btns[i] == nil then
 | 
| 
flickerstreak@90
 | 
   275           local b = Button:New(self, i, btnCfg[i], self.config)
 | 
| 
flickerstreak@90
 | 
   276           self.btns[i] = b
 | 
| 
flickerstreak@90
 | 
   277           self.bar:AddButton(i,b)
 | 
| 
flickerstreak@90
 | 
   278         end
 | 
| 
flickerstreak@90
 | 
   279       end
 | 
| 
flickerstreak@90
 | 
   280       for i = n+1, #self.btns do
 | 
| 
flickerstreak@90
 | 
   281         if self.btns[i] then
 | 
| 
flickerstreak@90
 | 
   282           self.bar:RemoveButton(self.btns[i])
 | 
| 
flickerstreak@90
 | 
   283           self.btns[i]:Destroy()
 | 
| 
flickerstreak@90
 | 
   284           self.btns[i] = nil
 | 
| 
flickerstreak@90
 | 
   285           btnCfg[i] = nil
 | 
| 
flickerstreak@90
 | 
   286         end
 | 
| 
flickerstreak@90
 | 
   287       end
 | 
| 
flickerstreak@90
 | 
   288     end
 | 
| 
flickerstreak@90
 | 
   289     for _, b in ipairs(self.btns) do
 | 
| 
flickerstreak@90
 | 
   290       b:Refresh()
 | 
| 
flickerstreak@90
 | 
   291     end
 | 
| 
flickerstreak@90
 | 
   292     local f = self.bar:GetFrame()
 | 
| 
flickerstreak@90
 | 
   293     f:SetAttribute("mindcontrol",self.config.mindcontrol)
 | 
| 
flickerstreak@90
 | 
   294     f:Execute(
 | 
| 
flickerstreak@90
 | 
   295       [[
 | 
| 
flickerstreak@90
 | 
   296       doMindControl = self:GetAttribute("mindcontrol")
 | 
| 
flickerstreak@90
 | 
   297       control:ChildUpdate()
 | 
| 
flickerstreak@90
 | 
   298       ]])
 | 
| 
flickerstreak@90
 | 
   299 
 | 
| 
flickerstreak@90
 | 
   300     f:SetAttribute("_onstate-mindcontrol",
 | 
| 
flickerstreak@90
 | 
   301       -- function _onstate-mindcontrol(self, stateid, newstate)
 | 
| 
flickerstreak@90
 | 
   302       [[
 | 
| 
flickerstreak@90
 | 
   303         control:ChildUpdate()
 | 
| 
flickerstreak@90
 | 
   304       ]])
 | 
| 
flickerstreak@90
 | 
   305     RegisterStateDriver(f, "mindcontrol", "[bonusbar:5] mc; none")
 | 
| 
flickerstreak@90
 | 
   306   end
 | 
| 
flickerstreak@90
 | 
   307 
 | 
| 
flickerstreak@90
 | 
   308   function Handle:Destroy()
 | 
| 
flickerstreak@90
 | 
   309     for _,b in pairs(self.btns) do
 | 
| 
flickerstreak@90
 | 
   310       if b then
 | 
| 
flickerstreak@90
 | 
   311         b:Destroy()
 | 
| 
flickerstreak@90
 | 
   312       end
 | 
| 
flickerstreak@90
 | 
   313     end
 | 
| 
flickerstreak@90
 | 
   314   end
 | 
| 
flickerstreak@90
 | 
   315 
 | 
| 
flickerstreak@90
 | 
   316   function Handle:SetConfigMode(mode)
 | 
| 
flickerstreak@90
 | 
   317     for _, b in pairs(self.btns) do
 | 
| 
flickerstreak@90
 | 
   318       b:ShowGrid(mode)
 | 
| 
flickerstreak@90
 | 
   319       b:ShowActionIDLabel(mode)
 | 
| 
flickerstreak@90
 | 
   320     end
 | 
| 
flickerstreak@90
 | 
   321   end
 | 
| 
flickerstreak@90
 | 
   322 
 | 
| 
flickerstreak@90
 | 
   323   function Handle:SetKeybindMode(mode)
 | 
| 
flickerstreak@90
 | 
   324     for _, b in pairs(self.btns) do
 | 
| 
flickerstreak@90
 | 
   325       if mode then
 | 
| 
flickerstreak@90
 | 
   326         -- set the border for all buttons to the keybind-enable color
 | 
| 
flickerstreak@90
 | 
   327         local r,g,b,a = KB:GetColorKeyBoundMode()
 | 
| 
flickerstreak@90
 | 
   328       	b.border:SetVertexColor(r,g,b,a)
 | 
| 
flickerstreak@90
 | 
   329         b.border:Show()
 | 
| 
flickerstreak@90
 | 
   330       else
 | 
| 
flickerstreak@90
 | 
   331         b.border:Hide()
 | 
| 
flickerstreak@90
 | 
   332       end
 | 
| 
flickerstreak@90
 | 
   333     end
 | 
| 
flickerstreak@90
 | 
   334   end
 | 
| 
flickerstreak@90
 | 
   335 
 | 
| 
flickerstreak@90
 | 
   336   function Handle:GetLastButton()
 | 
| 
flickerstreak@90
 | 
   337     return self.btns[#self.btns]
 | 
| 
flickerstreak@90
 | 
   338   end
 | 
| 
flickerstreak@90
 | 
   339 
 | 
| 
flickerstreak@90
 | 
   340     -- options handlers
 | 
| 
flickerstreak@90
 | 
   341   function Handle:GetOptions()
 | 
| 
flickerstreak@87
 | 
   342     return {
 | 
| 
flickerstreak@87
 | 
   343       type = "group",
 | 
| 
flickerstreak@87
 | 
   344       name = L["Action Buttons"],
 | 
| 
flickerstreak@90
 | 
   345       handler = self,
 | 
| 
flickerstreak@87
 | 
   346       args = options
 | 
| 
flickerstreak@87
 | 
   347     }
 | 
| 
flickerstreak@77
 | 
   348   end
 | 
| 
flickerstreak@77
 | 
   349 
 | 
| 
flickerstreak@90
 | 
   350   function Handle:SetHideEmpty(info, value)
 | 
| 
flickerstreak@90
 | 
   351     if value ~= self.config.hideEmpty then
 | 
| 
flickerstreak@77
 | 
   352       for b in self.bar:IterateButtons() do
 | 
| 
flickerstreak@77
 | 
   353         b:ShowGrid(not value)
 | 
| 
flickerstreak@77
 | 
   354       end
 | 
| 
flickerstreak@90
 | 
   355       self.config.hideEmpty = value
 | 
| 
flickerstreak@77
 | 
   356     end
 | 
| 
flickerstreak@77
 | 
   357   end
 | 
| 
flickerstreak@77
 | 
   358 
 | 
| 
flickerstreak@90
 | 
   359   function Handle:GetHideEmpty()
 | 
| 
flickerstreak@90
 | 
   360     return self.config.hideEmpty
 | 
| 
flickerstreak@77
 | 
   361   end
 | 
| 
flickerstreak@87
 | 
   362 
 | 
| 
flickerstreak@90
 | 
   363   function Handle:GetNumPages()
 | 
| 
flickerstreak@90
 | 
   364     return self.config.nPages
 | 
| 
flickerstreak@87
 | 
   365   end
 | 
| 
flickerstreak@87
 | 
   366 
 | 
| 
flickerstreak@90
 | 
   367   function Handle:SetNumPages(info, value)
 | 
| 
flickerstreak@90
 | 
   368     self.config.nPages = value
 | 
| 
flickerstreak@90
 | 
   369     self:Refresh()
 | 
| 
flickerstreak@87
 | 
   370   end
 | 
| 
flickerstreak@87
 | 
   371 
 | 
| 
flickerstreak@90
 | 
   372   function Handle:GetMindControl()
 | 
| 
flickerstreak@90
 | 
   373     return self.config.mindcontrol
 | 
| 
flickerstreak@90
 | 
   374   end
 | 
| 
flickerstreak@90
 | 
   375 
 | 
| 
flickerstreak@90
 | 
   376   function Handle:SetMindControl(info, value)
 | 
| 
flickerstreak@90
 | 
   377     self.config.mindcontrol = value
 | 
| 
flickerstreak@90
 | 
   378     self:Refresh()
 | 
| 
flickerstreak@90
 | 
   379   end
 | 
| 
flickerstreak@90
 | 
   380 
 | 
| 
flickerstreak@90
 | 
   381   function Handle:GetActionEditMethod()
 | 
| 
flickerstreak@87
 | 
   382     return self.editMethod or 0
 | 
| 
flickerstreak@87
 | 
   383   end
 | 
| 
flickerstreak@87
 | 
   384 
 | 
| 
flickerstreak@90
 | 
   385   function Handle:SetActionEditMethod(info, value)
 | 
| 
flickerstreak@87
 | 
   386     self.editMethod = value
 | 
| 
flickerstreak@87
 | 
   387   end
 | 
| 
flickerstreak@87
 | 
   388 
 | 
| 
flickerstreak@90
 | 
   389   function Handle:IsButtonSelectHidden()
 | 
| 
flickerstreak@87
 | 
   390     return self.editMethod ~= 1
 | 
| 
flickerstreak@87
 | 
   391   end
 | 
| 
flickerstreak@87
 | 
   392 
 | 
| 
flickerstreak@90
 | 
   393   function Handle:GetRowList()
 | 
| 
flickerstreak@87
 | 
   394     local r,c = self.bar:GetButtonGrid()
 | 
| 
flickerstreak@87
 | 
   395     if self.rowList == nil or #self.rowList ~= r then
 | 
| 
flickerstreak@87
 | 
   396       local list = { }
 | 
| 
flickerstreak@87
 | 
   397       for i = 1, r do
 | 
| 
flickerstreak@87
 | 
   398         table.insert(list,i)
 | 
| 
flickerstreak@87
 | 
   399       end
 | 
| 
flickerstreak@87
 | 
   400       self.rowList = list
 | 
| 
flickerstreak@87
 | 
   401     end
 | 
| 
flickerstreak@87
 | 
   402     return self.rowList
 | 
| 
flickerstreak@87
 | 
   403   end
 | 
| 
flickerstreak@87
 | 
   404 
 | 
| 
flickerstreak@90
 | 
   405   function Handle:GetSelectedRow()
 | 
| 
flickerstreak@87
 | 
   406     local r, c = self.bar:GetButtonGrid()
 | 
| 
flickerstreak@87
 | 
   407     local row = self.selectedRow or 1
 | 
| 
flickerstreak@87
 | 
   408     if row > r then
 | 
| 
flickerstreak@87
 | 
   409       row = 1
 | 
| 
flickerstreak@87
 | 
   410     end
 | 
| 
flickerstreak@87
 | 
   411     self.selectedRow = row
 | 
| 
flickerstreak@87
 | 
   412     return row
 | 
| 
flickerstreak@87
 | 
   413   end
 | 
| 
flickerstreak@87
 | 
   414 
 | 
| 
flickerstreak@90
 | 
   415   function Handle:SetSelectedRow(info, value)
 | 
| 
flickerstreak@87
 | 
   416     self.selectedRow = value
 | 
| 
flickerstreak@87
 | 
   417   end
 | 
| 
flickerstreak@87
 | 
   418 
 | 
| 
flickerstreak@90
 | 
   419   function Handle:GetColumnList()
 | 
| 
flickerstreak@87
 | 
   420     local r,c = self.bar:GetButtonGrid()
 | 
| 
flickerstreak@87
 | 
   421     if self.columnList == nil or #self.columnList ~= c then
 | 
| 
flickerstreak@87
 | 
   422       local list = { }
 | 
| 
flickerstreak@87
 | 
   423       for i = 1, c do
 | 
| 
flickerstreak@87
 | 
   424         table.insert(list,i)
 | 
| 
flickerstreak@87
 | 
   425       end
 | 
| 
flickerstreak@87
 | 
   426       self.columnList = list
 | 
| 
flickerstreak@87
 | 
   427     end
 | 
| 
flickerstreak@87
 | 
   428     return self.columnList
 | 
| 
flickerstreak@87
 | 
   429   end
 | 
| 
flickerstreak@87
 | 
   430 
 | 
| 
flickerstreak@90
 | 
   431   function Handle:GetSelectedColumn()
 | 
| 
flickerstreak@87
 | 
   432     local r, c = self.bar:GetButtonGrid()
 | 
| 
flickerstreak@87
 | 
   433     local col = self.selectedColumn or 1
 | 
| 
flickerstreak@87
 | 
   434     if col > c then
 | 
| 
flickerstreak@87
 | 
   435       col = 1
 | 
| 
flickerstreak@87
 | 
   436     end
 | 
| 
flickerstreak@87
 | 
   437     self.selectedColumn = col
 | 
| 
flickerstreak@87
 | 
   438     return col
 | 
| 
flickerstreak@87
 | 
   439   end
 | 
| 
flickerstreak@87
 | 
   440 
 | 
| 
flickerstreak@90
 | 
   441   function Handle:SetSelectedColumn(info, value)
 | 
| 
flickerstreak@87
 | 
   442     self.selectedColumn = value
 | 
| 
flickerstreak@87
 | 
   443   end
 | 
| 
flickerstreak@87
 | 
   444 
 | 
| 
flickerstreak@90
 | 
   445   function Handle:IsPageSelectHidden()
 | 
| 
flickerstreak@90
 | 
   446     return self.editMethod ~= 1 or (self.config.nPages or 1) < 2
 | 
| 
flickerstreak@87
 | 
   447   end
 | 
| 
flickerstreak@87
 | 
   448 
 | 
| 
flickerstreak@90
 | 
   449   function Handle:GetPageList()
 | 
| 
flickerstreak@90
 | 
   450     local n = self.config.nPages or 1
 | 
| 
flickerstreak@87
 | 
   451     if self.pageList == nil or #self.pageList ~= n then
 | 
| 
flickerstreak@87
 | 
   452       local p = { }
 | 
| 
flickerstreak@87
 | 
   453       for i = 1, n do
 | 
| 
flickerstreak@87
 | 
   454         table.insert(p,i)
 | 
| 
flickerstreak@87
 | 
   455       end
 | 
| 
flickerstreak@87
 | 
   456       self.pageList = p
 | 
| 
flickerstreak@87
 | 
   457     end
 | 
| 
flickerstreak@87
 | 
   458     return self.pageList
 | 
| 
flickerstreak@87
 | 
   459   end
 | 
| 
flickerstreak@87
 | 
   460 
 | 
| 
flickerstreak@90
 | 
   461   function Handle:GetSelectedPage()
 | 
| 
flickerstreak@87
 | 
   462     local p = self.selectedPage or 1
 | 
| 
flickerstreak@90
 | 
   463     if p > (self.config.nPages or 1) then
 | 
| 
flickerstreak@87
 | 
   464       p = 1
 | 
| 
flickerstreak@87
 | 
   465     end
 | 
| 
flickerstreak@87
 | 
   466     self.selectedPage = p
 | 
| 
flickerstreak@87
 | 
   467     return p
 | 
| 
flickerstreak@87
 | 
   468   end
 | 
| 
flickerstreak@87
 | 
   469 
 | 
| 
flickerstreak@90
 | 
   470   function Handle:SetSelectedPage(info, value)
 | 
| 
flickerstreak@87
 | 
   471     self.selectedPage = value
 | 
| 
flickerstreak@87
 | 
   472   end
 | 
| 
flickerstreak@87
 | 
   473 
 | 
| 
flickerstreak@90
 | 
   474   function Handle:GetActionID()
 | 
| 
flickerstreak@87
 | 
   475     local row = self.selectedRow or 1
 | 
| 
flickerstreak@87
 | 
   476     local col = self.selectedColumn or 1
 | 
| 
flickerstreak@87
 | 
   477     local r, c = self.bar:GetButtonGrid()
 | 
| 
flickerstreak@87
 | 
   478     local n = (row-1) * c + col
 | 
| 
flickerstreak@90
 | 
   479     local btn = self.btns[n]
 | 
| 
flickerstreak@87
 | 
   480     if btn then
 | 
| 
flickerstreak@87
 | 
   481       return tostring(btn:GetActionID(self.selectedPage or 1))
 | 
| 
flickerstreak@87
 | 
   482     end
 | 
| 
flickerstreak@87
 | 
   483   end
 | 
| 
flickerstreak@87
 | 
   484 
 | 
| 
flickerstreak@90
 | 
   485   function Handle:SetActionID(info, value)
 | 
| 
flickerstreak@87
 | 
   486     local row = self.selectedRow or 1
 | 
| 
flickerstreak@87
 | 
   487     local col = self.selectedColumn or 1
 | 
| 
flickerstreak@87
 | 
   488     local r, c = self.bar:GetButtonGrid()
 | 
| 
flickerstreak@87
 | 
   489     local n = (row-1) * c + col
 | 
| 
flickerstreak@90
 | 
   490     local btn = self.btns[n]
 | 
| 
flickerstreak@87
 | 
   491     if btn then
 | 
| 
flickerstreak@87
 | 
   492       btn:SetActionID(tonumber(value), self.selectedPage or 1)
 | 
| 
flickerstreak@87
 | 
   493     end
 | 
| 
flickerstreak@87
 | 
   494   end
 | 
| 
flickerstreak@87
 | 
   495 
 | 
| 
flickerstreak@90
 | 
   496   function Handle:ValidateActionID(info, value)
 | 
| 
flickerstreak@87
 | 
   497     value = tonumber(value)
 | 
| 
flickerstreak@87
 | 
   498     if value == nil or value < 1 or value > 120 then
 | 
| 
flickerstreak@87
 | 
   499       return L["Specify ID 1-120"]
 | 
| 
flickerstreak@87
 | 
   500     end
 | 
| 
flickerstreak@87
 | 
   501     return true
 | 
| 
flickerstreak@87
 | 
   502   end
 | 
| 
flickerstreak@87
 | 
   503 
 | 
| 
flickerstreak@90
 | 
   504   function Handle:IsMultiIDHidden()
 | 
| 
flickerstreak@87
 | 
   505     return self.editMethod ~= 2
 | 
| 
flickerstreak@87
 | 
   506   end
 | 
| 
flickerstreak@87
 | 
   507 
 | 
| 
flickerstreak@90
 | 
   508   function Handle:GetMultiID()
 | 
| 
flickerstreak@87
 | 
   509     local p = { }
 | 
| 
flickerstreak@90
 | 
   510     for i = 1, self.config.nPages or 1 do
 | 
| 
flickerstreak@87
 | 
   511       local b = { }
 | 
| 
flickerstreak@90
 | 
   512       for _, btn in ipairs(self.btns) do
 | 
| 
flickerstreak@87
 | 
   513         table.insert(b, btn:GetActionID(i))
 | 
| 
flickerstreak@87
 | 
   514       end
 | 
| 
flickerstreak@87
 | 
   515       table.insert(p, table.concat(b,","))
 | 
| 
flickerstreak@87
 | 
   516     end
 | 
| 
flickerstreak@87
 | 
   517     return table.concat(p,";\n")
 | 
| 
flickerstreak@87
 | 
   518   end
 | 
| 
flickerstreak@87
 | 
   519 
 | 
| 
flickerstreak@87
 | 
   520 
 | 
| 
flickerstreak@87
 | 
   521   local function ParseMultiID(nBtns, nPages, s)
 | 
| 
flickerstreak@87
 | 
   522     if s:match("[^%d%s,;]") then
 | 
| 
flickerstreak@87
 | 
   523       ReAction:Print("items other than digits, spaces, commas, and semicolons in string",s)
 | 
| 
flickerstreak@87
 | 
   524       return nil
 | 
| 
flickerstreak@87
 | 
   525     end
 | 
| 
flickerstreak@87
 | 
   526     local p = { }
 | 
| 
flickerstreak@87
 | 
   527     for list in s:gmatch("[^;]+") do
 | 
| 
flickerstreak@87
 | 
   528       local pattern = ("^%s?$"):format(("%s*(%d+)%s*,"):rep(nBtns))
 | 
| 
flickerstreak@87
 | 
   529       local ids = { list:match(pattern) }
 | 
| 
flickerstreak@87
 | 
   530       if #ids ~= nBtns then
 | 
| 
flickerstreak@87
 | 
   531         ReAction:Print("found",#ids,"buttons instead of",nBtns)
 | 
| 
flickerstreak@87
 | 
   532         return nil
 | 
| 
flickerstreak@87
 | 
   533       end
 | 
| 
flickerstreak@87
 | 
   534       table.insert(p,ids)
 | 
| 
flickerstreak@87
 | 
   535     end
 | 
| 
flickerstreak@87
 | 
   536     if #p ~= nPages then
 | 
| 
flickerstreak@87
 | 
   537       ReAction:Print("found",#p,"pages instead of",nPages)
 | 
| 
flickerstreak@87
 | 
   538       return nil
 | 
| 
flickerstreak@87
 | 
   539     end
 | 
| 
flickerstreak@87
 | 
   540     return p
 | 
| 
flickerstreak@87
 | 
   541   end
 | 
| 
flickerstreak@87
 | 
   542 
 | 
| 
flickerstreak@90
 | 
   543   function Handle:SetMultiID(info, value)
 | 
| 
flickerstreak@91
 | 
   544     local p = ParseMultiID(#self.btns, self.config.nPages or 1, value)
 | 
| 
flickerstreak@87
 | 
   545     for page, b in ipairs(p) do
 | 
| 
flickerstreak@87
 | 
   546       for button, id in ipairs(b) do
 | 
| 
flickerstreak@90
 | 
   547         self.btns[button]:SetActionID(id, page)
 | 
| 
flickerstreak@87
 | 
   548       end
 | 
| 
flickerstreak@87
 | 
   549     end
 | 
| 
flickerstreak@87
 | 
   550   end
 | 
| 
flickerstreak@87
 | 
   551 
 | 
| 
flickerstreak@90
 | 
   552   function Handle:ValidateMultiID(info, value)
 | 
| 
flickerstreak@87
 | 
   553     local bad = L["Invalid action ID list string"]
 | 
| 
flickerstreak@90
 | 
   554     if value == nil or ParseMultiID(#self.btns, self.config.nPages or 1, value) == nil then
 | 
| 
flickerstreak@87
 | 
   555       return bad
 | 
| 
flickerstreak@87
 | 
   556     end
 | 
| 
flickerstreak@87
 | 
   557     return true
 | 
| 
flickerstreak@87
 | 
   558   end
 | 
| 
flickerstreak@77
 | 
   559 end
 | 
| 
flickerstreak@77
 | 
   560 
 | 
| 
flickerstreak@77
 | 
   561 
 | 
| 
flickerstreak@87
 | 
   562 ------ State property options ------
 | 
| 
flickerstreak@87
 | 
   563 do
 | 
| 
flickerstreak@87
 | 
   564   local pageOptions = {
 | 
| 
flickerstreak@87
 | 
   565     page = {
 | 
| 
flickerstreak@87
 | 
   566       name  = L["Show Page #"],
 | 
| 
flickerstreak@90
 | 
   567       order = 11,
 | 
| 
flickerstreak@87
 | 
   568       type  = "select",
 | 
| 
flickerstreak@87
 | 
   569       width = "half",
 | 
| 
flickerstreak@87
 | 
   570       disabled = "IsPageDisabled",
 | 
| 
flickerstreak@87
 | 
   571       hidden   = "IsPageHidden",
 | 
| 
flickerstreak@87
 | 
   572       values   = "GetPageValues",
 | 
| 
flickerstreak@87
 | 
   573       set      = "SetProp",
 | 
| 
flickerstreak@87
 | 
   574       get      = "GetPage",
 | 
| 
flickerstreak@87
 | 
   575     },
 | 
| 
flickerstreak@87
 | 
   576   }
 | 
| 
flickerstreak@50
 | 
   577 
 | 
| 
flickerstreak@90
 | 
   578   local function GetBarConfig(bar)
 | 
| 
flickerstreak@90
 | 
   579     return module.db.profile.bars[bar:GetName()]
 | 
| 
flickerstreak@87
 | 
   580   end
 | 
| 
flickerstreak@87
 | 
   581 
 | 
| 
flickerstreak@90
 | 
   582   function PropHandler.GetOptions()
 | 
| 
flickerstreak@90
 | 
   583     return pageOptions
 | 
| 
flickerstreak@87
 | 
   584   end
 | 
| 
flickerstreak@87
 | 
   585 
 | 
| 
flickerstreak@90
 | 
   586   function PropHandler:IsPageDisabled()
 | 
| 
flickerstreak@90
 | 
   587     local c = GetBarConfig(self.bar)
 | 
| 
flickerstreak@90
 | 
   588     local n = c and c.nPages or 1
 | 
| 
flickerstreak@90
 | 
   589     return not (n > 1)
 | 
| 
flickerstreak@87
 | 
   590   end
 | 
| 
flickerstreak@87
 | 
   591 
 | 
| 
flickerstreak@90
 | 
   592   function PropHandler:IsPageHidden()
 | 
| 
flickerstreak@87
 | 
   593     return not GetBarConfig(self.bar)
 | 
| 
flickerstreak@87
 | 
   594   end
 | 
| 
flickerstreak@87
 | 
   595 
 | 
| 
flickerstreak@90
 | 
   596   function PropHandler:GetPageValues()
 | 
| 
flickerstreak@87
 | 
   597     local c = GetBarConfig(self.bar)
 | 
| 
flickerstreak@87
 | 
   598     if c then
 | 
| 
flickerstreak@87
 | 
   599       local n = c.nPages
 | 
| 
flickerstreak@87
 | 
   600       if self._npages ~= n then
 | 
| 
flickerstreak@87
 | 
   601         self._pagevalues = { }
 | 
| 
flickerstreak@87
 | 
   602         self._npages = n
 | 
| 
flickerstreak@87
 | 
   603         -- cache the results
 | 
| 
flickerstreak@87
 | 
   604         for i = 1, n do
 | 
| 
flickerstreak@90
 | 
   605           self._pagevalues["page"..i] = i
 | 
| 
flickerstreak@87
 | 
   606         end
 | 
| 
flickerstreak@87
 | 
   607       end
 | 
| 
flickerstreak@87
 | 
   608       return self._pagevalues
 | 
| 
flickerstreak@87
 | 
   609     end
 | 
| 
flickerstreak@87
 | 
   610   end
 | 
| 
flickerstreak@87
 | 
   611 
 | 
| 
flickerstreak@90
 | 
   612   function PropHandler:GetPage(info)
 | 
| 
flickerstreak@87
 | 
   613     return self:GetProp(info) or 1
 | 
| 
flickerstreak@87
 | 
   614   end
 | 
| 
flickerstreak@90
 | 
   615 
 | 
| 
flickerstreak@87
 | 
   616 end
 | 
| 
flickerstreak@87
 | 
   617 
 | 
| 
flickerstreak@87
 | 
   618 ------ ActionID allocation ------
 | 
| 
flickerstreak@87
 | 
   619 -- this needs to be high performance when requesting new IDs,
 | 
| 
flickerstreak@87
 | 
   620 -- or certain controls will become sluggish. However, the new-request
 | 
| 
flickerstreak@87
 | 
   621 -- infrastructure can be built lazily the first time that a new request
 | 
| 
flickerstreak@87
 | 
   622 -- comes in (which will only happen at user config time: at static startup
 | 
| 
flickerstreak@87
 | 
   623 -- config time all actionIDs should already have been assigned and stored
 | 
| 
flickerstreak@87
 | 
   624 -- in the config file)
 | 
| 
flickerstreak@87
 | 
   625 
 | 
| 
flickerstreak@87
 | 
   626 local IDAlloc
 | 
| 
flickerstreak@87
 | 
   627 do
 | 
| 
flickerstreak@87
 | 
   628   local n = 120
 | 
| 
flickerstreak@87
 | 
   629   
 | 
| 
flickerstreak@87
 | 
   630   IDAlloc = setmetatable({ wrap = 1, freecount = n }, {__index = function() return 0 end})
 | 
| 
flickerstreak@87
 | 
   631 
 | 
| 
flickerstreak@87
 | 
   632   function IDAlloc:Acquire(id, hint)
 | 
| 
flickerstreak@87
 | 
   633     id = tonumber(id)
 | 
| 
flickerstreak@87
 | 
   634     hint = tonumber(hint)
 | 
| 
flickerstreak@87
 | 
   635     if id and (id < 1 or id > n) then
 | 
| 
flickerstreak@87
 | 
   636       id = nil
 | 
| 
flickerstreak@87
 | 
   637     end
 | 
| 
flickerstreak@87
 | 
   638     if hint and (hint < 1 or hint > n) then
 | 
| 
flickerstreak@87
 | 
   639       hint = nil
 | 
| 
flickerstreak@87
 | 
   640     end
 | 
| 
flickerstreak@87
 | 
   641     if id == nil then
 | 
| 
flickerstreak@87
 | 
   642       -- get a free ID
 | 
| 
flickerstreak@87
 | 
   643       if hint and self[hint] == 0 then
 | 
| 
flickerstreak@87
 | 
   644         -- use the hint if it's free
 | 
| 
flickerstreak@87
 | 
   645         id = hint
 | 
| 
flickerstreak@87
 | 
   646       elseif self.freecount > 0 then
 | 
| 
flickerstreak@87
 | 
   647         -- if neither the id nor the hint are defined or free, but
 | 
| 
flickerstreak@87
 | 
   648         -- the list is known to have free IDs, then start searching
 | 
| 
flickerstreak@87
 | 
   649         -- at the hint for a free one
 | 
| 
flickerstreak@87
 | 
   650         for i = hint or 1, n do
 | 
| 
flickerstreak@87
 | 
   651           if self[i] == 0 then
 | 
| 
flickerstreak@87
 | 
   652             id = i
 | 
| 
flickerstreak@87
 | 
   653             break
 | 
| 
flickerstreak@87
 | 
   654           end
 | 
| 
flickerstreak@87
 | 
   655         end
 | 
| 
flickerstreak@87
 | 
   656         -- self.wrap the search
 | 
| 
flickerstreak@87
 | 
   657         if id == nil and hint and hint > 1 then
 | 
| 
flickerstreak@87
 | 
   658           for i = 1, hint - 1 do
 | 
| 
flickerstreak@87
 | 
   659             if self[i] == 0 then
 | 
| 
flickerstreak@87
 | 
   660               id = i
 | 
| 
flickerstreak@87
 | 
   661               break
 | 
| 
flickerstreak@87
 | 
   662             end
 | 
| 
flickerstreak@87
 | 
   663           end
 | 
| 
flickerstreak@87
 | 
   664         end
 | 
| 
flickerstreak@87
 | 
   665       end
 | 
| 
flickerstreak@87
 | 
   666       if id == nil then
 | 
| 
flickerstreak@87
 | 
   667         -- if there are no free IDs, start wrapping at 1
 | 
| 
flickerstreak@87
 | 
   668         id = self.wrap
 | 
| 
flickerstreak@87
 | 
   669         self.wrap = id + 1
 | 
| 
flickerstreak@87
 | 
   670         if self.wrap > n then
 | 
| 
flickerstreak@87
 | 
   671           self.wrap = 1
 | 
| 
flickerstreak@87
 | 
   672         end
 | 
| 
flickerstreak@24
 | 
   673       end
 | 
| 
flickerstreak@24
 | 
   674     end
 | 
| 
flickerstreak@87
 | 
   675     if self[id] == 0 then
 | 
| 
flickerstreak@87
 | 
   676       self.freecount = self.freecount - 1
 | 
| 
flickerstreak@87
 | 
   677     end
 | 
| 
flickerstreak@87
 | 
   678     self[id] = self[id] + 1
 | 
| 
flickerstreak@87
 | 
   679     return id
 | 
| 
flickerstreak@24
 | 
   680   end
 | 
| 
flickerstreak@24
 | 
   681 
 | 
| 
flickerstreak@87
 | 
   682   function IDAlloc:Release(id)
 | 
| 
flickerstreak@87
 | 
   683     id = tonumber(id)
 | 
| 
flickerstreak@87
 | 
   684     if id and (id >= 1 or id <= n) then
 | 
| 
flickerstreak@87
 | 
   685       self[id] = self[id] - 1
 | 
| 
flickerstreak@87
 | 
   686       if self[id] == 0 then
 | 
| 
flickerstreak@87
 | 
   687         self.freecount = self.freecount + 1
 | 
| 
flickerstreak@87
 | 
   688         self.wrap = 1
 | 
| 
flickerstreak@87
 | 
   689       end
 | 
| 
flickerstreak@87
 | 
   690     end
 | 
| 
flickerstreak@87
 | 
   691   end
 | 
| 
flickerstreak@87
 | 
   692 end
 | 
| 
flickerstreak@87
 | 
   693 
 | 
| 
flickerstreak@88
 | 
   694 ------ Button class ------
 | 
| 
flickerstreak@88
 | 
   695 
 | 
| 
flickerstreak@88
 | 
   696 do
 | 
| 
flickerstreak@90
 | 
   697   local frameRecycler = { }
 | 
| 
flickerstreak@90
 | 
   698   local trash = CreateFrame("Frame")
 | 
| 
flickerstreak@90
 | 
   699   local OnUpdate, KBAttach, GetActionName, GetHotkey, SetKey, FreeKey, ClearBindings, GetBindings
 | 
| 
flickerstreak@90
 | 
   700   do
 | 
| 
flickerstreak@90
 | 
   701     local ATTACK_BUTTON_FLASH_TIME = ATTACK_BUTTON_FLASH_TIME
 | 
| 
flickerstreak@90
 | 
   702     local IsActionInRange = IsActionInRange
 | 
| 
flickerstreak@87
 | 
   703 
 | 
| 
flickerstreak@90
 | 
   704     local buttonLookup = setmetatable({},{__mode="kv"})
 | 
| 
flickerstreak@88
 | 
   705 
 | 
| 
flickerstreak@90
 | 
   706     function OnUpdate(frame, elapsed)
 | 
| 
flickerstreak@90
 | 
   707       -- note: This function taints frame.flashtime and frame.rangeTimer. Both of these
 | 
| 
flickerstreak@90
 | 
   708       --       are only read by ActionButton_OnUpdate (which this function replaces). In
 | 
| 
flickerstreak@90
 | 
   709       --       all other places they're just written, so it doesn't taint any secure code.
 | 
| 
flickerstreak@90
 | 
   710       if frame.flashing == 1 then
 | 
| 
flickerstreak@90
 | 
   711         frame.flashtime = frame.flashtime - elapsed
 | 
| 
flickerstreak@90
 | 
   712         if frame.flashtime <= 0 then
 | 
| 
flickerstreak@90
 | 
   713           local overtime = -frame.flashtime
 | 
| 
flickerstreak@90
 | 
   714           if overtime >= ATTACK_BUTTON_FLASH_TIME then
 | 
| 
flickerstreak@90
 | 
   715             overtime = 0
 | 
| 
flickerstreak@90
 | 
   716           end
 | 
| 
flickerstreak@90
 | 
   717           frame.flashtime = ATTACK_BUTTON_FLASH_TIME - overtime
 | 
| 
flickerstreak@90
 | 
   718 
 | 
| 
flickerstreak@90
 | 
   719           local flashTexture = frame.flash
 | 
| 
flickerstreak@90
 | 
   720           if flashTexture:IsShown() then
 | 
| 
flickerstreak@90
 | 
   721             flashTexture:Hide()
 | 
| 
flickerstreak@90
 | 
   722           else
 | 
| 
flickerstreak@90
 | 
   723             flashTexture:Show()
 | 
| 
flickerstreak@90
 | 
   724           end
 | 
| 
flickerstreak@88
 | 
   725         end
 | 
| 
flickerstreak@90
 | 
   726       end
 | 
| 
flickerstreak@90
 | 
   727       
 | 
| 
flickerstreak@90
 | 
   728       if frame.rangeTimer then
 | 
| 
flickerstreak@90
 | 
   729         frame.rangeTimer = frame.rangeTimer - elapsed;
 | 
| 
flickerstreak@88
 | 
   730 
 | 
| 
flickerstreak@90
 | 
   731         if frame.rangeTimer <= 0 then
 | 
| 
flickerstreak@90
 | 
   732           if IsActionInRange(frame.action) == 0 then
 | 
| 
flickerstreak@90
 | 
   733             frame.icon:SetVertexColor(1.0,0.1,0.1)
 | 
| 
flickerstreak@90
 | 
   734           else
 | 
| 
flickerstreak@90
 | 
   735             ActionButton_UpdateUsable(frame)
 | 
| 
flickerstreak@90
 | 
   736           end
 | 
| 
flickerstreak@90
 | 
   737           frame.rangeTimer = 0.1
 | 
| 
flickerstreak@88
 | 
   738         end
 | 
| 
flickerstreak@88
 | 
   739       end
 | 
| 
flickerstreak@88
 | 
   740     end
 | 
| 
flickerstreak@90
 | 
   741 
 | 
| 
flickerstreak@90
 | 
   742     -- Use KeyBound-1.0 for binding, but use Override bindings instead of
 | 
| 
flickerstreak@90
 | 
   743     -- regular bindings to support multiple profile use. This is a little
 | 
| 
flickerstreak@90
 | 
   744     -- weird with the KeyBound dialog box (which has per-char selector as well
 | 
| 
flickerstreak@90
 | 
   745     -- as an OK/Cancel box) but it's the least amount of effort to implement.
 | 
| 
flickerstreak@90
 | 
   746     function GetActionName(f)
 | 
| 
flickerstreak@90
 | 
   747       local b = buttonLookup[f]
 | 
| 
flickerstreak@90
 | 
   748       if b then
 | 
| 
flickerstreak@90
 | 
   749         return format("%s:%s", b.bar:GetName(), b.idx)
 | 
| 
flickerstreak@90
 | 
   750       end
 | 
| 
flickerstreak@90
 | 
   751     end
 | 
| 
flickerstreak@90
 | 
   752 
 | 
| 
flickerstreak@90
 | 
   753     function GetHotkey(f)
 | 
| 
flickerstreak@90
 | 
   754       local b = buttonLookup[f]
 | 
| 
flickerstreak@90
 | 
   755       if b then
 | 
| 
flickerstreak@90
 | 
   756         return KB:ToShortKey(b:GetConfig().hotkey)
 | 
| 
flickerstreak@90
 | 
   757       end
 | 
| 
flickerstreak@90
 | 
   758     end
 | 
| 
flickerstreak@90
 | 
   759 
 | 
| 
flickerstreak@90
 | 
   760     function SetKey(f, key)
 | 
| 
flickerstreak@90
 | 
   761       local b = buttonLookup[f]
 | 
| 
flickerstreak@90
 | 
   762       if b then
 | 
| 
flickerstreak@90
 | 
   763         local c = b:GetConfig()
 | 
| 
flickerstreak@90
 | 
   764         if c.hotkey then
 | 
| 
flickerstreak@90
 | 
   765           SetOverrideBinding(f, false, c.hotkey, nil)
 | 
| 
flickerstreak@90
 | 
   766         end
 | 
| 
flickerstreak@90
 | 
   767         if key then
 | 
| 
flickerstreak@90
 | 
   768           SetOverrideBindingClick(f, false, key, f:GetName(), nil)
 | 
| 
flickerstreak@90
 | 
   769         end
 | 
| 
flickerstreak@90
 | 
   770         c.hotkey = key
 | 
| 
flickerstreak@90
 | 
   771         b:DisplayHotkey(GetHotkey(f))
 | 
| 
flickerstreak@90
 | 
   772       end
 | 
| 
flickerstreak@90
 | 
   773     end
 | 
| 
flickerstreak@90
 | 
   774 
 | 
| 
flickerstreak@90
 | 
   775     function FreeKey(f, key)
 | 
| 
flickerstreak@90
 | 
   776       local b = buttonLookup[f]
 | 
| 
flickerstreak@90
 | 
   777       if b then
 | 
| 
flickerstreak@90
 | 
   778         local c = b:GetConfig()
 | 
| 
flickerstreak@90
 | 
   779         if c.hotkey == key then
 | 
| 
flickerstreak@90
 | 
   780           local action = f:GetActionName()
 | 
| 
flickerstreak@90
 | 
   781           SetOverrideBinding(f, false, c.hotkey, nil)
 | 
| 
flickerstreak@90
 | 
   782           c.hotkey = nil
 | 
| 
flickerstreak@90
 | 
   783           b:DisplayHotkey(nil)
 | 
| 
flickerstreak@90
 | 
   784           return action
 | 
| 
flickerstreak@90
 | 
   785         end
 | 
| 
flickerstreak@90
 | 
   786       end
 | 
| 
flickerstreak@90
 | 
   787       return ReAction:FreeOverrideHotkey(key)
 | 
| 
flickerstreak@90
 | 
   788     end
 | 
| 
flickerstreak@90
 | 
   789 
 | 
| 
flickerstreak@90
 | 
   790     function ClearBindings(f)
 | 
| 
flickerstreak@90
 | 
   791       SetKey(f, nil)
 | 
| 
flickerstreak@90
 | 
   792     end
 | 
| 
flickerstreak@90
 | 
   793 
 | 
| 
flickerstreak@90
 | 
   794     function GetBindings(f)
 | 
| 
flickerstreak@90
 | 
   795       local b = buttonLookup[f]
 | 
| 
flickerstreak@90
 | 
   796       if b then
 | 
| 
flickerstreak@90
 | 
   797         return b:GetConfig().hotkey
 | 
| 
flickerstreak@90
 | 
   798       end
 | 
| 
flickerstreak@90
 | 
   799     end
 | 
| 
flickerstreak@90
 | 
   800 
 | 
| 
flickerstreak@90
 | 
   801     function KBAttach( button )
 | 
| 
flickerstreak@90
 | 
   802       local f = button:GetFrame()
 | 
| 
flickerstreak@90
 | 
   803       f.GetActionName = GetActionName
 | 
| 
flickerstreak@90
 | 
   804       f.GetHotkey     = GetHotkey
 | 
| 
flickerstreak@90
 | 
   805       f.SetKey        = SetKey
 | 
| 
flickerstreak@90
 | 
   806       f.FreeKey       = FreeKey
 | 
| 
flickerstreak@90
 | 
   807       f.ClearBindings = ClearBindings
 | 
| 
flickerstreak@90
 | 
   808       f.GetBindings   = GetBindings
 | 
| 
flickerstreak@90
 | 
   809       buttonLookup[f] = button
 | 
| 
flickerstreak@90
 | 
   810       f:SetKey(button:GetConfig().hotkey)
 | 
| 
flickerstreak@90
 | 
   811       ReAction:RegisterKeybindFrame(f)
 | 
| 
flickerstreak@90
 | 
   812     end
 | 
| 
flickerstreak@90
 | 
   813   end
 | 
| 
flickerstreak@90
 | 
   814 
 | 
| 
flickerstreak@90
 | 
   815   local meta = {__index = Button}
 | 
| 
flickerstreak@90
 | 
   816 
 | 
| 
flickerstreak@90
 | 
   817   function Button:New( handle, idx, config, barConfig )
 | 
| 
flickerstreak@90
 | 
   818     local bar = handle.bar
 | 
| 
flickerstreak@90
 | 
   819 
 | 
| 
flickerstreak@90
 | 
   820     -- create new self
 | 
| 
flickerstreak@90
 | 
   821     self = setmetatable( 
 | 
| 
flickerstreak@90
 | 
   822       { 
 | 
| 
flickerstreak@90
 | 
   823         bar = bar,
 | 
| 
flickerstreak@90
 | 
   824         idx = idx,
 | 
| 
flickerstreak@90
 | 
   825         config = config,
 | 
| 
flickerstreak@90
 | 
   826         barConfig = barConfig,
 | 
| 
flickerstreak@90
 | 
   827       }, meta )
 | 
| 
flickerstreak@90
 | 
   828 
 | 
| 
flickerstreak@90
 | 
   829     local name = config.name or ("ReAction_%s_%s_%d"):format(bar:GetName(),moduleID,idx)
 | 
| 
flickerstreak@90
 | 
   830     self.name = name
 | 
| 
flickerstreak@90
 | 
   831     config.name = name
 | 
| 
flickerstreak@90
 | 
   832     local lastButton = handle:GetLastButton()
 | 
| 
flickerstreak@90
 | 
   833     config.actionID = IDAlloc:Acquire(config.actionID, lastButton and lastButton.config.actionID) -- gets a free one if none configured
 | 
| 
flickerstreak@90
 | 
   834     self.nPages = 1
 | 
| 
flickerstreak@88
 | 
   835     
 | 
| 
flickerstreak@90
 | 
   836     -- have to recycle frames with the same name: CreateFrame() doesn't overwrite
 | 
| 
flickerstreak@90
 | 
   837     -- existing globals. Can't set to nil in the global because it's then tainted.
 | 
| 
flickerstreak@90
 | 
   838     local parent = bar:GetFrame()
 | 
| 
flickerstreak@90
 | 
   839     local f = frameRecycler[name]
 | 
| 
flickerstreak@90
 | 
   840     if f then
 | 
| 
flickerstreak@90
 | 
   841       f:SetParent(parent)
 | 
| 
flickerstreak@90
 | 
   842     else
 | 
| 
flickerstreak@90
 | 
   843       f = CreateFrame("CheckButton", name, parent, "ActionBarButtonTemplate")
 | 
| 
flickerstreak@90
 | 
   844       -- ditch the old hotkey text because it's tied in ActionButton_Update() to the
 | 
| 
flickerstreak@90
 | 
   845       -- standard binding. We use override bindings.
 | 
| 
flickerstreak@90
 | 
   846       local hotkey = _G[name.."HotKey"]
 | 
| 
flickerstreak@90
 | 
   847       hotkey:SetParent(trash)
 | 
| 
flickerstreak@90
 | 
   848       hotkey = f:CreateFontString(nil, "ARTWORK", "NumberFontNormalSmallGray")
 | 
| 
flickerstreak@90
 | 
   849       hotkey:SetWidth(36)
 | 
| 
flickerstreak@90
 | 
   850       hotkey:SetHeight(18)
 | 
| 
flickerstreak@90
 | 
   851       hotkey:SetJustifyH("RIGHT")
 | 
| 
flickerstreak@90
 | 
   852       hotkey:SetJustifyV("TOP")
 | 
| 
flickerstreak@90
 | 
   853       hotkey:SetPoint("TOPLEFT",f,"TOPLEFT",-2,-2)
 | 
| 
flickerstreak@90
 | 
   854       f.hotkey = hotkey
 | 
| 
flickerstreak@90
 | 
   855       f.icon = _G[name.."Icon"]
 | 
| 
flickerstreak@90
 | 
   856       f.flash = _G[name.."Flash"]
 | 
| 
flickerstreak@90
 | 
   857       f:SetScript("OnUpdate",OnUpdate)
 | 
| 
flickerstreak@90
 | 
   858     end
 | 
| 
flickerstreak@88
 | 
   859 
 | 
| 
flickerstreak@90
 | 
   860     self.hotkey = f.hotkey
 | 
| 
flickerstreak@90
 | 
   861     self.border = _G[name.."Border"]
 | 
| 
flickerstreak@90
 | 
   862 
 | 
| 
flickerstreak@90
 | 
   863     f:SetAttribute("action", config.actionID)
 | 
| 
flickerstreak@90
 | 
   864     -- install mind control actions for all buttons just for simplicity
 | 
| 
flickerstreak@90
 | 
   865     if self.idx <= 12 then
 | 
| 
flickerstreak@90
 | 
   866       f:SetAttribute("*action-mc", 120 + self.idx)
 | 
| 
flickerstreak@90
 | 
   867     end
 | 
| 
flickerstreak@90
 | 
   868     
 | 
| 
flickerstreak@90
 | 
   869     -- wrap the OnClick handler to use a pagemap from the header's context
 | 
| 
flickerstreak@90
 | 
   870     parent:WrapScript(f, "OnClick", 
 | 
| 
flickerstreak@90
 | 
   871       -- function OnClick(self, button, down)
 | 
| 
flickerstreak@90
 | 
   872       [[
 | 
| 
flickerstreak@90
 | 
   873         if doMindControl and GetBonusBarOffset() == 5 then
 | 
| 
flickerstreak@90
 | 
   874           return "mc"
 | 
| 
flickerstreak@88
 | 
   875         else
 | 
| 
flickerstreak@90
 | 
   876           return state and page and page[state] or button
 | 
| 
flickerstreak@88
 | 
   877         end
 | 
| 
flickerstreak@90
 | 
   878       ]])
 | 
| 
flickerstreak@90
 | 
   879 
 | 
| 
flickerstreak@90
 | 
   880     -- set a _childupdate handler, called within the header's context
 | 
| 
flickerstreak@90
 | 
   881     -- SetAttribute() is a brute-force way to trigger ActionButton_UpdateAction(). Setting "*action1"
 | 
| 
flickerstreak@90
 | 
   882     -- will, in the absence of a useful replacement for SecureButton_GetEffectiveButton(), force
 | 
| 
flickerstreak@90
 | 
   883     -- ActionButton_CalculateAction() to use the new action-id for display purposes. It also 
 | 
| 
flickerstreak@90
 | 
   884     -- sort of obviates the OnClick handler, but hopefully this is only temporary until
 | 
| 
flickerstreak@90
 | 
   885     -- SecureButton_GetEffectiveButton() gets fixed.
 | 
| 
flickerstreak@90
 | 
   886     f:SetAttribute("_childupdate", 
 | 
| 
flickerstreak@90
 | 
   887       -- function _childupdate(self, snippetid, message)
 | 
| 
flickerstreak@90
 | 
   888       [[
 | 
| 
flickerstreak@90
 | 
   889         local action = "action"
 | 
| 
flickerstreak@90
 | 
   890         if doMindControl and GetBonusBarOffset() == 5 then
 | 
| 
flickerstreak@90
 | 
   891           action = "*action-mc"
 | 
| 
flickerstreak@90
 | 
   892         elseif page and state and page[state] then
 | 
| 
flickerstreak@90
 | 
   893           action = "*action-"..page[state]
 | 
| 
flickerstreak@90
 | 
   894         end
 | 
| 
flickerstreak@90
 | 
   895         local value = self:GetAttribute(action)
 | 
| 
flickerstreak@90
 | 
   896         self:SetAttribute("*action1",value)
 | 
| 
flickerstreak@90
 | 
   897       ]])
 | 
| 
flickerstreak@90
 | 
   898 
 | 
| 
flickerstreak@90
 | 
   899     self.frame = f
 | 
| 
flickerstreak@90
 | 
   900     self.normalTexture = getglobal(format("%sNormalTexture",f:GetName()))
 | 
| 
flickerstreak@90
 | 
   901 
 | 
| 
flickerstreak@90
 | 
   902     -- initialize the hide state
 | 
| 
flickerstreak@90
 | 
   903     f:SetAttribute("showgrid",0)
 | 
| 
flickerstreak@90
 | 
   904     self:ShowGrid(not barConfig.hideEmpty)
 | 
| 
flickerstreak@90
 | 
   905     if ReAction:GetConfigMode() then
 | 
| 
flickerstreak@90
 | 
   906       self:ShowGrid(true)
 | 
| 
flickerstreak@90
 | 
   907     end
 | 
| 
flickerstreak@90
 | 
   908 
 | 
| 
flickerstreak@90
 | 
   909     -- show the ID label if applicable
 | 
| 
flickerstreak@90
 | 
   910     self:ShowActionIDLabel(ReAction:GetConfigMode())
 | 
| 
flickerstreak@90
 | 
   911 
 | 
| 
flickerstreak@90
 | 
   912     -- attach the keybinder
 | 
| 
flickerstreak@90
 | 
   913     KBAttach(self)
 | 
| 
flickerstreak@90
 | 
   914 
 | 
| 
flickerstreak@90
 | 
   915     self:Refresh()
 | 
| 
flickerstreak@90
 | 
   916     return self
 | 
| 
flickerstreak@90
 | 
   917   end
 | 
| 
flickerstreak@90
 | 
   918 
 | 
| 
flickerstreak@90
 | 
   919   function Button:Destroy()
 | 
| 
flickerstreak@90
 | 
   920     local f = self.frame
 | 
| 
flickerstreak@90
 | 
   921     f:UnregisterAllEvents()
 | 
| 
flickerstreak@90
 | 
   922     f:Hide()
 | 
| 
flickerstreak@90
 | 
   923     f:SetParent(UIParent)
 | 
| 
flickerstreak@90
 | 
   924     f:ClearAllPoints()
 | 
| 
flickerstreak@90
 | 
   925     if self.name then
 | 
| 
flickerstreak@90
 | 
   926       frameRecycler[self.name] = f
 | 
| 
flickerstreak@90
 | 
   927     end
 | 
| 
flickerstreak@90
 | 
   928     if self.config.actionID then
 | 
| 
flickerstreak@90
 | 
   929       IDAlloc:Release(self.config.actionID)
 | 
| 
flickerstreak@90
 | 
   930     end
 | 
| 
flickerstreak@90
 | 
   931     if self.config.pageactions then
 | 
| 
flickerstreak@90
 | 
   932       for _, id in ipairs(self.config.pageactions) do
 | 
| 
flickerstreak@90
 | 
   933         IDAlloc:Release(id)
 | 
| 
flickerstreak@90
 | 
   934       end
 | 
| 
flickerstreak@90
 | 
   935     end
 | 
| 
flickerstreak@90
 | 
   936     self.frame = nil
 | 
| 
flickerstreak@90
 | 
   937     self.config = nil
 | 
| 
flickerstreak@90
 | 
   938     self.bar = nil
 | 
| 
flickerstreak@90
 | 
   939   end
 | 
| 
flickerstreak@90
 | 
   940 
 | 
| 
flickerstreak@90
 | 
   941   function Button:Refresh()
 | 
| 
flickerstreak@90
 | 
   942     local f = self.frame
 | 
| 
flickerstreak@90
 | 
   943     self.bar:PlaceButton(self, 36, 36)
 | 
| 
flickerstreak@90
 | 
   944     self:RefreshPages()
 | 
| 
flickerstreak@90
 | 
   945   end
 | 
| 
flickerstreak@90
 | 
   946 
 | 
| 
flickerstreak@90
 | 
   947   function Button:GetFrame()
 | 
| 
flickerstreak@90
 | 
   948     return self.frame
 | 
| 
flickerstreak@90
 | 
   949   end
 | 
| 
flickerstreak@90
 | 
   950 
 | 
| 
flickerstreak@90
 | 
   951   function Button:GetName()
 | 
| 
flickerstreak@90
 | 
   952     return self.name
 | 
| 
flickerstreak@90
 | 
   953   end
 | 
| 
flickerstreak@90
 | 
   954 
 | 
| 
flickerstreak@90
 | 
   955   function Button:GetConfig()
 | 
| 
flickerstreak@90
 | 
   956     return self.config
 | 
| 
flickerstreak@90
 | 
   957   end
 | 
| 
flickerstreak@90
 | 
   958 
 | 
| 
flickerstreak@90
 | 
   959   function Button:GetActionID(page)
 | 
| 
flickerstreak@90
 | 
   960     if page == nil then
 | 
| 
flickerstreak@90
 | 
   961       -- get the effective ID
 | 
| 
flickerstreak@90
 | 
   962       return self.frame.action -- kept up-to-date by Blizzard's ActionButton_CalculateAction()
 | 
| 
flickerstreak@90
 | 
   963     else
 | 
| 
flickerstreak@90
 | 
   964       if page == 1 then
 | 
| 
flickerstreak@90
 | 
   965         return self.config.actionID
 | 
| 
flickerstreak@90
 | 
   966       else
 | 
| 
flickerstreak@90
 | 
   967         return self.config.pageactions and self.config.pageactions[page] or self.config.actionID
 | 
| 
flickerstreak@88
 | 
   968       end
 | 
| 
flickerstreak@88
 | 
   969     end
 | 
| 
flickerstreak@88
 | 
   970   end
 | 
| 
flickerstreak@88
 | 
   971 
 | 
| 
flickerstreak@90
 | 
   972   function Button:SetActionID( id, page )
 | 
| 
flickerstreak@90
 | 
   973     id = tonumber(id)
 | 
| 
flickerstreak@90
 | 
   974     page = tonumber(page)
 | 
| 
flickerstreak@90
 | 
   975     if id == nil or id < 1 or id > 120 then
 | 
| 
flickerstreak@90
 | 
   976       error("Button:SetActionID - invalid action ID")
 | 
| 
flickerstreak@90
 | 
   977     end
 | 
| 
flickerstreak@90
 | 
   978     if page and page ~= 1 then
 | 
| 
flickerstreak@90
 | 
   979       if not self.config.pageactions then
 | 
| 
flickerstreak@90
 | 
   980         self.config.pageactions = { }
 | 
| 
flickerstreak@90
 | 
   981       end
 | 
| 
flickerstreak@90
 | 
   982       if self.config.pageactions[page] then
 | 
| 
flickerstreak@90
 | 
   983         IDAlloc:Release(self.config.pageactions[page])
 | 
| 
flickerstreak@90
 | 
   984       end
 | 
| 
flickerstreak@90
 | 
   985       self.config.pageactions[page] = id
 | 
| 
flickerstreak@90
 | 
   986       IDAlloc:Acquire(self.config.pageactions[page])
 | 
| 
flickerstreak@90
 | 
   987       self.frame:SetAttribute(("*action-page%d"):format(page),id)
 | 
| 
flickerstreak@90
 | 
   988     else
 | 
| 
flickerstreak@90
 | 
   989       IDAlloc:Release(self.config.actionID)
 | 
| 
flickerstreak@90
 | 
   990       self.config.actionID = id
 | 
| 
flickerstreak@90
 | 
   991       IDAlloc:Acquire(self.config.actionID)
 | 
| 
flickerstreak@90
 | 
   992       self.frame:SetAttribute("action",id)
 | 
| 
flickerstreak@90
 | 
   993       if self.config.pageactions then
 | 
| 
flickerstreak@90
 | 
   994         self.config.pageactions[1] = id
 | 
| 
flickerstreak@90
 | 
   995         self.frame:SetAttribute("*action-page1",id)
 | 
| 
flickerstreak@90
 | 
   996       end
 | 
| 
flickerstreak@88
 | 
   997     end
 | 
| 
flickerstreak@88
 | 
   998   end
 | 
| 
flickerstreak@88
 | 
   999 
 | 
| 
flickerstreak@90
 | 
  1000   function Button:RefreshPages( force )
 | 
| 
flickerstreak@90
 | 
  1001     local nPages = self.barConfig.nPages
 | 
| 
flickerstreak@90
 | 
  1002     if nPages and (nPages ~= self.nPages or force) then
 | 
| 
flickerstreak@90
 | 
  1003       local f = self:GetFrame()
 | 
| 
flickerstreak@90
 | 
  1004       local c = self.config.pageactions
 | 
| 
flickerstreak@90
 | 
  1005       if nPages > 1 and not c then
 | 
| 
flickerstreak@90
 | 
  1006         c = { }
 | 
| 
flickerstreak@90
 | 
  1007         self.config.pageactions = c
 | 
| 
flickerstreak@90
 | 
  1008       end
 | 
| 
flickerstreak@90
 | 
  1009       for i = 1, nPages do
 | 
| 
flickerstreak@90
 | 
  1010         if i > 1 then
 | 
| 
flickerstreak@90
 | 
  1011           c[i] = IDAlloc:Acquire(c[i], self.config.actionID + (i-1)*self.bar:GetNumButtons())
 | 
| 
flickerstreak@90
 | 
  1012         else
 | 
| 
flickerstreak@90
 | 
  1013           c[i] = self.config.actionID  -- page 1 is the same as the base actionID
 | 
| 
flickerstreak@90
 | 
  1014         end
 | 
| 
flickerstreak@90
 | 
  1015         f:SetAttribute(("*action-page%d"):format(i),c[i])
 | 
| 
flickerstreak@90
 | 
  1016       end
 | 
| 
flickerstreak@90
 | 
  1017       for i = nPages+1, #c do
 | 
| 
flickerstreak@90
 | 
  1018         IDAlloc:Release(c[i])
 | 
| 
flickerstreak@90
 | 
  1019         c[i] = nil
 | 
| 
flickerstreak@90
 | 
  1020         f:SetAttribute(("*action-page%d"):format(i),nil)
 | 
| 
flickerstreak@90
 | 
  1021       end
 | 
| 
flickerstreak@90
 | 
  1022       self.nPages = nPages
 | 
| 
flickerstreak@88
 | 
  1023     end
 | 
| 
flickerstreak@88
 | 
  1024   end
 | 
| 
flickerstreak@88
 | 
  1025 
 | 
| 
flickerstreak@90
 | 
  1026   function Button:ShowGrid( show )
 | 
| 
flickerstreak@90
 | 
  1027     if not InCombatLockdown() then
 | 
| 
flickerstreak@90
 | 
  1028       local f = self.frame
 | 
| 
flickerstreak@90
 | 
  1029       local count = f:GetAttribute("showgrid")
 | 
| 
flickerstreak@90
 | 
  1030       if show then
 | 
| 
flickerstreak@90
 | 
  1031         count = count + 1
 | 
| 
flickerstreak@90
 | 
  1032       else
 | 
| 
flickerstreak@90
 | 
  1033         count = count - 1
 | 
| 
flickerstreak@88
 | 
  1034       end
 | 
| 
flickerstreak@90
 | 
  1035       if count < 0 then
 | 
| 
flickerstreak@90
 | 
  1036         count = 0
 | 
| 
flickerstreak@88
 | 
  1037       end
 | 
| 
flickerstreak@90
 | 
  1038       f:SetAttribute("showgrid",count)
 | 
| 
flickerstreak@90
 | 
  1039 
 | 
| 
flickerstreak@90
 | 
  1040       if count >= 1 and not f:GetAttribute("statehidden") then
 | 
| 
flickerstreak@90
 | 
  1041         self.normalTexture:SetVertexColor(1.0, 1.0, 1.0, 0.5);
 | 
| 
flickerstreak@90
 | 
  1042         f:Show()
 | 
| 
flickerstreak@90
 | 
  1043       elseif count < 1 and not HasAction(self:GetActionID()) then
 | 
| 
flickerstreak@90
 | 
  1044         f:Hide()
 | 
| 
flickerstreak@90
 | 
  1045       end
 | 
| 
flickerstreak@88
 | 
  1046     end
 | 
| 
flickerstreak@88
 | 
  1047   end
 | 
| 
flickerstreak@88
 | 
  1048 
 | 
| 
flickerstreak@90
 | 
  1049   function Button:ShowActionIDLabel( show )
 | 
| 
flickerstreak@90
 | 
  1050     local f = self:GetFrame()
 | 
| 
flickerstreak@90
 | 
  1051     if show then
 | 
| 
flickerstreak@90
 | 
  1052       local id = self:GetActionID()
 | 
| 
flickerstreak@90
 | 
  1053       if not f.actionIDLabel then
 | 
| 
flickerstreak@90
 | 
  1054         local label = f:CreateFontString(nil,"OVERLAY","GameFontNormalLarge")
 | 
| 
flickerstreak@90
 | 
  1055         label:SetAllPoints()
 | 
| 
flickerstreak@90
 | 
  1056         label:SetJustifyH("CENTER")
 | 
| 
flickerstreak@90
 | 
  1057         label:SetShadowColor(0,0,0,1)
 | 
| 
flickerstreak@90
 | 
  1058         label:SetShadowOffset(2,-2)
 | 
| 
flickerstreak@90
 | 
  1059         f.actionIDLabel = label -- store the label with the frame for recycling
 | 
| 
flickerstreak@90
 | 
  1060 
 | 
| 
flickerstreak@90
 | 
  1061         f:HookScript("OnAttributeChanged", 
 | 
| 
flickerstreak@90
 | 
  1062           function(frame, attr, value)
 | 
| 
flickerstreak@90
 | 
  1063             if label:IsVisible() and attr:match("action") then
 | 
| 
flickerstreak@90
 | 
  1064               label:SetText(tostring(frame.action))
 | 
| 
flickerstreak@90
 | 
  1065             end
 | 
| 
flickerstreak@90
 | 
  1066           end)
 | 
| 
flickerstreak@88
 | 
  1067       end
 | 
| 
flickerstreak@90
 | 
  1068       f.actionIDLabel:SetText(tostring(id))
 | 
| 
flickerstreak@90
 | 
  1069       f.actionIDLabel:Show()
 | 
| 
flickerstreak@90
 | 
  1070     elseif f.actionIDLabel then
 | 
| 
flickerstreak@90
 | 
  1071       f.actionIDLabel:Hide()
 | 
| 
flickerstreak@88
 | 
  1072     end
 | 
| 
flickerstreak@88
 | 
  1073   end
 | 
| 
flickerstreak@88
 | 
  1074 
 | 
| 
flickerstreak@90
 | 
  1075   function Button:DisplayHotkey( key )
 | 
| 
flickerstreak@90
 | 
  1076     self.hotkey:SetText(key or "")
 | 
| 
flickerstreak@88
 | 
  1077   end
 | 
| 
flickerstreak@88
 | 
  1078 end
 |