| 
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@24
 | 
     5   ActionButton frame and associated functions. It also provides some button layout
 | 
| 
flickerstreak@24
 | 
     6   modification tools.
 | 
| 
flickerstreak@24
 | 
     7 
 | 
| 
flickerstreak@24
 | 
     8 --]]
 | 
| 
flickerstreak@24
 | 
     9 
 | 
| 
flickerstreak@24
 | 
    10 -- local imports
 | 
| 
flickerstreak@24
 | 
    11 local ReAction = ReAction
 | 
| 
flickerstreak@24
 | 
    12 local L = ReAction.L
 | 
| 
flickerstreak@24
 | 
    13 local _G = _G
 | 
| 
flickerstreak@24
 | 
    14 local CreateFrame = CreateFrame
 | 
| 
flickerstreak@24
 | 
    15 
 | 
| 
flickerstreak@24
 | 
    16 -- module declaration
 | 
| 
flickerstreak@24
 | 
    17 local moduleID = "Action"
 | 
| 
flickerstreak@28
 | 
    18 local module = ReAction:NewModule( moduleID )
 | 
| 
flickerstreak@24
 | 
    19 
 | 
| 
flickerstreak@24
 | 
    20 -- module methods
 | 
| 
flickerstreak@24
 | 
    21 function module:OnInitialize()
 | 
| 
flickerstreak@28
 | 
    22   self.db = ReAction.db:RegisterNamespace( moduleID,
 | 
| 
flickerstreak@24
 | 
    23     { 
 | 
| 
flickerstreak@28
 | 
    24       profile = {
 | 
| 
flickerstreak@28
 | 
    25         buttons = { }
 | 
| 
flickerstreak@28
 | 
    26       }
 | 
| 
flickerstreak@24
 | 
    27     }
 | 
| 
flickerstreak@24
 | 
    28   )
 | 
| 
flickerstreak@24
 | 
    29   self.buttons = { }
 | 
| 
flickerstreak@49
 | 
    30 
 | 
| 
flickerstreak@49
 | 
    31   ReAction:RegisterOptions("global", self, {
 | 
| 
flickerstreak@49
 | 
    32     hideEmpty = {
 | 
| 
flickerstreak@49
 | 
    33       type = "toggle",
 | 
| 
flickerstreak@49
 | 
    34       name = L["Hide Empty Buttons"],
 | 
| 
flickerstreak@49
 | 
    35       get  = function() return self.db.profile.hideEmptyButtons end,
 | 
| 
flickerstreak@49
 | 
    36       set  = function(info, val) module:SetHideEmptyButtons(val) end,
 | 
| 
flickerstreak@49
 | 
    37     }
 | 
| 
flickerstreak@49
 | 
    38   })
 | 
| 
flickerstreak@59
 | 
    39 
 | 
| 
flickerstreak@59
 | 
    40   ReAction:RegisterOptions("bar", self, {
 | 
| 
flickerstreak@59
 | 
    41     type = "group",
 | 
| 
flickerstreak@59
 | 
    42     name = L["Action Buttons"],
 | 
| 
flickerstreak@59
 | 
    43     hidden = "BarOptionsHidden",
 | 
| 
flickerstreak@59
 | 
    44     args = {
 | 
| 
flickerstreak@59
 | 
    45     }
 | 
| 
flickerstreak@59
 | 
    46   })
 | 
| 
flickerstreak@24
 | 
    47 end
 | 
| 
flickerstreak@24
 | 
    48 
 | 
| 
flickerstreak@24
 | 
    49 function module:OnEnable()
 | 
| 
flickerstreak@53
 | 
    50   ReAction:RegisterBarType(L["Action Bar"], 
 | 
| 
flickerstreak@53
 | 
    51     { 
 | 
| 
flickerstreak@53
 | 
    52       type = moduleID,
 | 
| 
flickerstreak@53
 | 
    53       defaultButtonSize = 36,
 | 
| 
flickerstreak@53
 | 
    54       defaultBarRows = 1,
 | 
| 
flickerstreak@53
 | 
    55       defaultBarCols = 12,
 | 
| 
flickerstreak@53
 | 
    56       defaultBarSpacing = 3
 | 
| 
flickerstreak@53
 | 
    57     }, true)
 | 
| 
flickerstreak@24
 | 
    58 end
 | 
| 
flickerstreak@24
 | 
    59 
 | 
| 
flickerstreak@24
 | 
    60 function module:OnDisable()
 | 
| 
flickerstreak@53
 | 
    61   ReAction:UnregisterBarType(L["Action Bar"])
 | 
| 
flickerstreak@24
 | 
    62 end
 | 
| 
flickerstreak@24
 | 
    63 
 | 
| 
flickerstreak@24
 | 
    64 function module:ApplyToBar(bar)
 | 
| 
flickerstreak@52
 | 
    65   self:RefreshBar(bar)
 | 
| 
flickerstreak@24
 | 
    66 end
 | 
| 
flickerstreak@24
 | 
    67 
 | 
| 
flickerstreak@24
 | 
    68 function module:RefreshBar(bar)
 | 
| 
flickerstreak@53
 | 
    69   if bar.config.type == moduleID then
 | 
| 
flickerstreak@48
 | 
    70     if self.buttons[bar] == nil then
 | 
| 
flickerstreak@48
 | 
    71       self.buttons[bar] = { }
 | 
| 
flickerstreak@48
 | 
    72     end
 | 
| 
flickerstreak@48
 | 
    73     local btns = self.buttons[bar]
 | 
| 
flickerstreak@48
 | 
    74     local profile = self.db.profile
 | 
| 
flickerstreak@48
 | 
    75     local barName = bar:GetName()
 | 
| 
flickerstreak@48
 | 
    76     if profile.buttons[barName] == nil then
 | 
| 
flickerstreak@48
 | 
    77       profile.buttons[barName] = {}
 | 
| 
flickerstreak@48
 | 
    78     end
 | 
| 
flickerstreak@48
 | 
    79     local btnCfg = profile.buttons[barName]
 | 
| 
flickerstreak@24
 | 
    80 
 | 
| 
flickerstreak@48
 | 
    81     local r, c = bar:GetButtonGrid()
 | 
| 
flickerstreak@48
 | 
    82     local n = r*c
 | 
| 
flickerstreak@48
 | 
    83     for i = 1, n do
 | 
| 
flickerstreak@48
 | 
    84       if btnCfg[i] == nil then
 | 
| 
flickerstreak@48
 | 
    85         btnCfg[i] = {}
 | 
| 
flickerstreak@48
 | 
    86       end
 | 
| 
flickerstreak@48
 | 
    87       if btns[i] == nil then
 | 
| 
flickerstreak@52
 | 
    88         local ok, b = pcall(self.BtnClass.new, self.BtnClass, bar, i, btnCfg[i])
 | 
| 
flickerstreak@52
 | 
    89         if ok and b then
 | 
| 
flickerstreak@52
 | 
    90           btns[i] = b
 | 
| 
flickerstreak@52
 | 
    91         end
 | 
| 
flickerstreak@48
 | 
    92       else
 | 
| 
flickerstreak@48
 | 
    93         btns[i]:Refresh(bar,i)
 | 
| 
flickerstreak@48
 | 
    94       end
 | 
| 
flickerstreak@24
 | 
    95     end
 | 
| 
flickerstreak@48
 | 
    96     for i = n+1, #btns do
 | 
| 
flickerstreak@52
 | 
    97       if btns[i] then
 | 
| 
flickerstreak@52
 | 
    98         btns[i] = btns[i]:Destroy()
 | 
| 
flickerstreak@52
 | 
    99         if btnCfg[i] then
 | 
| 
flickerstreak@52
 | 
   100           btnCfg[i] = nil
 | 
| 
flickerstreak@52
 | 
   101         end
 | 
| 
flickerstreak@48
 | 
   102       end
 | 
| 
flickerstreak@24
 | 
   103     end
 | 
| 
flickerstreak@24
 | 
   104   end
 | 
| 
flickerstreak@24
 | 
   105 end
 | 
| 
flickerstreak@24
 | 
   106 
 | 
| 
flickerstreak@24
 | 
   107 function module:RemoveFromBar(bar)
 | 
| 
flickerstreak@24
 | 
   108   if self.buttons[bar] then
 | 
| 
flickerstreak@24
 | 
   109     local btns = self.buttons[bar]
 | 
| 
flickerstreak@24
 | 
   110     for _,b in pairs(btns) do
 | 
| 
flickerstreak@24
 | 
   111       if b then
 | 
| 
flickerstreak@24
 | 
   112         b:Destroy()
 | 
| 
flickerstreak@24
 | 
   113       end
 | 
| 
flickerstreak@24
 | 
   114     end
 | 
| 
flickerstreak@24
 | 
   115     self.buttons[bar] = nil
 | 
| 
flickerstreak@24
 | 
   116   end
 | 
| 
flickerstreak@24
 | 
   117 end
 | 
| 
flickerstreak@24
 | 
   118 
 | 
| 
flickerstreak@24
 | 
   119 function module:EraseBarConfig(barName)
 | 
| 
flickerstreak@24
 | 
   120   self.db.profile.buttons[barName] = nil
 | 
| 
flickerstreak@24
 | 
   121 end
 | 
| 
flickerstreak@24
 | 
   122 
 | 
| 
flickerstreak@48
 | 
   123 function module:RenameBarConfig(oldname, newname)
 | 
| 
flickerstreak@48
 | 
   124   local b = self.db.profile.buttons
 | 
| 
flickerstreak@48
 | 
   125   b[newname], b[oldname] = b[oldname], nil
 | 
| 
flickerstreak@48
 | 
   126 end
 | 
| 
flickerstreak@48
 | 
   127 
 | 
| 
flickerstreak@49
 | 
   128 function module:SetHideEmptyButtons(hide)
 | 
| 
flickerstreak@49
 | 
   129   if hide ~= self.db.profile.hideEmptyButtons then
 | 
| 
flickerstreak@49
 | 
   130     for _, bar in pairs(self.buttons) do
 | 
| 
flickerstreak@49
 | 
   131       for _, b in pairs(bar) do
 | 
| 
flickerstreak@49
 | 
   132         if hide then
 | 
| 
flickerstreak@49
 | 
   133           ActionButton_HideGrid(b.frame)
 | 
| 
flickerstreak@49
 | 
   134         else
 | 
| 
flickerstreak@49
 | 
   135           ActionButton_ShowGrid(b.frame)
 | 
| 
flickerstreak@49
 | 
   136         end
 | 
| 
flickerstreak@49
 | 
   137       end
 | 
| 
flickerstreak@49
 | 
   138     end
 | 
| 
flickerstreak@49
 | 
   139     self.db.profile.hideEmptyButtons = hide
 | 
| 
flickerstreak@49
 | 
   140   end
 | 
| 
flickerstreak@49
 | 
   141 end
 | 
| 
flickerstreak@49
 | 
   142 
 | 
| 
flickerstreak@24
 | 
   143 function module:ApplyConfigMode(mode,bars)
 | 
| 
flickerstreak@49
 | 
   144   for _, bar in pairs(bars) do
 | 
| 
flickerstreak@49
 | 
   145     if bar and self.buttons[bar] then
 | 
| 
flickerstreak@49
 | 
   146       for _, b in pairs(self.buttons[bar]) do
 | 
| 
flickerstreak@24
 | 
   147         if b then
 | 
| 
flickerstreak@24
 | 
   148           if mode then
 | 
| 
flickerstreak@49
 | 
   149             ActionButton_ShowGrid(b.frame)
 | 
| 
flickerstreak@50
 | 
   150             self:showActionIDLabel(b)
 | 
| 
flickerstreak@49
 | 
   151           else
 | 
| 
flickerstreak@49
 | 
   152             ActionButton_HideGrid(b.frame)
 | 
| 
flickerstreak@50
 | 
   153             self:hideActionIDLabel(b)
 | 
| 
flickerstreak@24
 | 
   154           end
 | 
| 
flickerstreak@24
 | 
   155         end
 | 
| 
flickerstreak@24
 | 
   156       end
 | 
| 
flickerstreak@24
 | 
   157     end
 | 
| 
flickerstreak@24
 | 
   158   end
 | 
| 
flickerstreak@24
 | 
   159 end
 | 
| 
flickerstreak@24
 | 
   160 
 | 
| 
flickerstreak@50
 | 
   161 function module:showActionIDLabel(button)
 | 
| 
flickerstreak@52
 | 
   162   if not button.actionIDLabel and button:GetActionID() then
 | 
| 
flickerstreak@50
 | 
   163     local label = button:GetFrame():CreateFontString(nil,"OVERLAY","GameFontNormalLarge")
 | 
| 
flickerstreak@50
 | 
   164     label:SetAllPoints()
 | 
| 
flickerstreak@50
 | 
   165     label:SetJustifyH("CENTER")
 | 
| 
flickerstreak@50
 | 
   166     label:SetShadowColor(0,0,0,1)
 | 
| 
flickerstreak@50
 | 
   167     label:SetShadowOffset(2,-2)
 | 
| 
flickerstreak@50
 | 
   168     label:SetText(tostring(button:GetActionID()))
 | 
| 
flickerstreak@50
 | 
   169     button.actionIDLabel = label
 | 
| 
flickerstreak@50
 | 
   170   end
 | 
| 
flickerstreak@50
 | 
   171   button.actionIDLabel:Show()
 | 
| 
flickerstreak@50
 | 
   172 end
 | 
| 
flickerstreak@50
 | 
   173 
 | 
| 
flickerstreak@50
 | 
   174 function module:hideActionIDLabel(button)
 | 
| 
flickerstreak@50
 | 
   175   if button.actionIDLabel then
 | 
| 
flickerstreak@50
 | 
   176     button.actionIDLabel:Hide()
 | 
| 
flickerstreak@50
 | 
   177   end
 | 
| 
flickerstreak@50
 | 
   178 end
 | 
| 
flickerstreak@50
 | 
   179 
 | 
| 
flickerstreak@50
 | 
   180 
 | 
| 
flickerstreak@59
 | 
   181 ---- Options handlers ----
 | 
| 
flickerstreak@59
 | 
   182 function module:BarOptionsHidden(bar)
 | 
| 
flickerstreak@59
 | 
   183   return bar.config.type ~= moduleID
 | 
| 
flickerstreak@59
 | 
   184 end
 | 
| 
flickerstreak@59
 | 
   185 
 | 
| 
flickerstreak@50
 | 
   186 
 | 
| 
flickerstreak@24
 | 
   187 -- use-count of action IDs
 | 
| 
flickerstreak@53
 | 
   188 local nActionIDs = 120
 | 
| 
flickerstreak@24
 | 
   189 local ActionIDList = setmetatable( {}, {
 | 
| 
flickerstreak@24
 | 
   190   __index = function(self, idx)
 | 
| 
flickerstreak@24
 | 
   191     if idx == nil then
 | 
| 
flickerstreak@53
 | 
   192       for i = 1, nActionIDs do
 | 
| 
flickerstreak@24
 | 
   193         if rawget(self,i) == nil then
 | 
| 
flickerstreak@24
 | 
   194           rawset(self,i,1)
 | 
| 
flickerstreak@24
 | 
   195           return i
 | 
| 
flickerstreak@24
 | 
   196         end
 | 
| 
flickerstreak@24
 | 
   197       end
 | 
| 
flickerstreak@52
 | 
   198       error("ran out of action IDs")
 | 
| 
flickerstreak@24
 | 
   199     else
 | 
| 
flickerstreak@24
 | 
   200       local c = rawget(self,idx) or 0
 | 
| 
flickerstreak@24
 | 
   201       rawset(self,idx,c+1)
 | 
| 
flickerstreak@24
 | 
   202       return idx
 | 
| 
flickerstreak@24
 | 
   203     end
 | 
| 
flickerstreak@24
 | 
   204   end,
 | 
| 
flickerstreak@24
 | 
   205   __newindex = function(self,idx,value)
 | 
| 
flickerstreak@24
 | 
   206     if value == nil then
 | 
| 
flickerstreak@24
 | 
   207       value = rawget(self,idx)
 | 
| 
flickerstreak@24
 | 
   208       if value == 1 then
 | 
| 
flickerstreak@24
 | 
   209         value = nil
 | 
| 
flickerstreak@24
 | 
   210       elseif value then
 | 
| 
flickerstreak@24
 | 
   211         value = value - 1
 | 
| 
flickerstreak@24
 | 
   212       end
 | 
| 
flickerstreak@24
 | 
   213     end
 | 
| 
flickerstreak@24
 | 
   214     rawset(self,idx,value)
 | 
| 
flickerstreak@24
 | 
   215   end
 | 
| 
flickerstreak@24
 | 
   216 })
 | 
| 
flickerstreak@24
 | 
   217 
 | 
| 
flickerstreak@24
 | 
   218 
 | 
| 
flickerstreak@24
 | 
   219 
 | 
| 
flickerstreak@28
 | 
   220 
 | 
| 
flickerstreak@28
 | 
   221 ------ Button class ------
 | 
| 
flickerstreak@28
 | 
   222 local Button = { }
 | 
| 
flickerstreak@28
 | 
   223 
 | 
| 
flickerstreak@28
 | 
   224 local function Constructor( self, bar, idx, config )
 | 
| 
flickerstreak@24
 | 
   225   self.bar, self.idx, self.config = bar, idx, config
 | 
| 
flickerstreak@24
 | 
   226 
 | 
| 
flickerstreak@24
 | 
   227   local barFrame = bar:GetFrame()
 | 
| 
flickerstreak@24
 | 
   228 
 | 
| 
flickerstreak@56
 | 
   229   config.name = config.name or ("ReAction_%s_%d"):format(bar:GetName(),idx)
 | 
| 
flickerstreak@49
 | 
   230   self.name = config.name
 | 
| 
flickerstreak@24
 | 
   231   config.actionID = ActionIDList[config.actionID] -- gets a free one if none configured
 | 
| 
flickerstreak@24
 | 
   232   
 | 
| 
flickerstreak@24
 | 
   233   local f = CreateFrame("CheckButton", self.name, barFrame, "ActionBarButtonTemplate")
 | 
| 
flickerstreak@49
 | 
   234 
 | 
| 
flickerstreak@24
 | 
   235   -- TODO: re-implement ActionButton event handlers that don't do secure stuff
 | 
| 
flickerstreak@24
 | 
   236 
 | 
| 
flickerstreak@24
 | 
   237   -- this will probably cause taint, using right now for display/debugging purposes
 | 
| 
flickerstreak@30
 | 
   238   f:SetScript("OnAttributeChanged", ActionButton_UpdateAction)
 | 
| 
flickerstreak@24
 | 
   239   f:SetAttribute("action", config.actionID)
 | 
| 
flickerstreak@49
 | 
   240 
 | 
| 
flickerstreak@24
 | 
   241   barFrame:SetAttribute("addchild",f)
 | 
| 
flickerstreak@49
 | 
   242 
 | 
| 
flickerstreak@24
 | 
   243   self.frame = f
 | 
| 
flickerstreak@24
 | 
   244   self:Refresh(bar,idx)
 | 
| 
flickerstreak@49
 | 
   245 
 | 
| 
flickerstreak@49
 | 
   246   if not module.db.profile.hideEmptyButtons then
 | 
| 
flickerstreak@49
 | 
   247     ActionButton_ShowGrid(self.frame)
 | 
| 
flickerstreak@49
 | 
   248   end
 | 
| 
flickerstreak@50
 | 
   249 
 | 
| 
flickerstreak@50
 | 
   250   if ReAction.configMode then
 | 
| 
flickerstreak@50
 | 
   251     ActionButton_ShowGrid(self.frame)
 | 
| 
flickerstreak@50
 | 
   252     module:showActionIDLabel(self)
 | 
| 
flickerstreak@50
 | 
   253   end
 | 
| 
flickerstreak@24
 | 
   254 end
 | 
| 
flickerstreak@24
 | 
   255 
 | 
| 
flickerstreak@24
 | 
   256 function Button:Destroy()
 | 
| 
flickerstreak@24
 | 
   257   local f = self.frame
 | 
| 
flickerstreak@24
 | 
   258   f:UnregisterAllEvents()
 | 
| 
flickerstreak@24
 | 
   259   f:Hide()
 | 
| 
flickerstreak@24
 | 
   260   f:SetParent(UIParent)
 | 
| 
flickerstreak@24
 | 
   261   f:ClearAllPoints()
 | 
| 
flickerstreak@28
 | 
   262   if self.name then
 | 
| 
flickerstreak@28
 | 
   263     _G[self.name] = nil
 | 
| 
flickerstreak@24
 | 
   264   end
 | 
| 
flickerstreak@52
 | 
   265   if self.config.actionID then
 | 
| 
flickerstreak@52
 | 
   266     ActionIDList[self.config.actionID] = nil
 | 
| 
flickerstreak@52
 | 
   267   end
 | 
| 
flickerstreak@24
 | 
   268   self.frame = nil
 | 
| 
flickerstreak@24
 | 
   269   self.config = nil
 | 
| 
flickerstreak@24
 | 
   270   self.bar = nil
 | 
| 
flickerstreak@24
 | 
   271 end
 | 
| 
flickerstreak@24
 | 
   272 
 | 
| 
flickerstreak@24
 | 
   273 function Button:Refresh(bar,idx)
 | 
| 
flickerstreak@24
 | 
   274   bar:PlaceButton(self.frame, idx, 36, 36)
 | 
| 
flickerstreak@24
 | 
   275 end
 | 
| 
flickerstreak@24
 | 
   276 
 | 
| 
flickerstreak@24
 | 
   277 function Button:GetFrame()
 | 
| 
flickerstreak@24
 | 
   278   return self.frame
 | 
| 
flickerstreak@24
 | 
   279 end
 | 
| 
flickerstreak@24
 | 
   280 
 | 
| 
flickerstreak@24
 | 
   281 function Button:GetName()
 | 
| 
flickerstreak@24
 | 
   282   return self.name
 | 
| 
flickerstreak@24
 | 
   283 end
 | 
| 
flickerstreak@24
 | 
   284 
 | 
| 
flickerstreak@24
 | 
   285 function Button:GetActionID()
 | 
| 
flickerstreak@24
 | 
   286   return self.config.actionID
 | 
| 
flickerstreak@24
 | 
   287 end
 | 
| 
flickerstreak@28
 | 
   288 
 | 
| 
flickerstreak@28
 | 
   289 
 | 
| 
flickerstreak@28
 | 
   290 -- export as a class-factory to module
 | 
| 
flickerstreak@28
 | 
   291 module.BtnClass = {
 | 
| 
flickerstreak@28
 | 
   292   new = function(self, ...)
 | 
| 
flickerstreak@28
 | 
   293     local x = { }
 | 
| 
flickerstreak@28
 | 
   294     for k,v in pairs(Button) do
 | 
| 
flickerstreak@28
 | 
   295       x[k] = v
 | 
| 
flickerstreak@28
 | 
   296     end
 | 
| 
flickerstreak@28
 | 
   297     Constructor(x, ...)
 | 
| 
flickerstreak@28
 | 
   298     return x
 | 
| 
flickerstreak@28
 | 
   299   end
 | 
| 
flickerstreak@28
 | 
   300 }
 |