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