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