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