comparison modules/ReAction_Action/ReAction_Action.lua @ 87:3499ac7c3a9b

Implemented paged actions and mind control actions, and config menus to suit. There's still some sort of bug in the actionID-selection routine, it doesn't always auto-select the IDs that I think it's going to if you resize a bar several times (especially in the presence of multiple pages).
author Flick <flickerstreak@gmail.com>
date Sat, 28 Jun 2008 00:54:21 +0000
parents 502cdb5666e2
children fc83b3f5b322
comparison
equal deleted inserted replaced
86:f32e2375e39b 87:3499ac7c3a9b
1 --[[ 1 --[[
2 ReAction Action button module. 2 ReAction Action button module.
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 ActionButton frame and associated functions. 5 ActionBarButtonTemplate frame and associated functions.
6 6
7 It also provides support for multiple pages (interacting with the State module) as well 7 It also provides action remapping support for multiple pages and possessed targets
8 as optional action remapping for possessed targets (mind control). 8 (Mind Control, Eyes of the Beast, Karazhan Chess event, various quests, etc). This is done
9 9 by interacting with the built-in State module to map these features to states via the
10 "statebutton" attribute.
10 --]] 11 --]]
11 12
12 -- local imports 13 -- local imports
13 local ReAction = ReAction 14 local ReAction = ReAction
14 local L = ReAction.L 15 local L = ReAction.L
15 local _G = _G 16 local _G = _G
16 local CreateFrame = CreateFrame 17 local CreateFrame = CreateFrame
17 18
18 ReAction:UpdateRevision("$Revision: 103 $") 19 ReAction:UpdateRevision("$Revision$")
19 20
20 -- module declaration 21 -- module declaration
21 local moduleID = "Action" 22 local moduleID = "Action"
22 local module = ReAction:NewModule( moduleID ) 23 local module = ReAction:NewModule( moduleID )
23 24
24 -- Button class declaration 25 -- Button class declaration
25 local Button = { } 26 local Button = { }
26 27
27 -- private -- 28 -- private utility --
28 local function RefreshLite(bar) 29 local function RefreshLite(bar)
29 local btns = module.buttons[bar] 30 local btns = module.buttons[bar]
30 if btns then 31 if btns then
31 for _, b in ipairs(btns) do 32 for _, b in ipairs(btns) do
32 b:Refresh() 33 b:Refresh()
33 end 34 end
34 end 35 end
35 end 36 end
37
38 local function GetBarConfig(bar)
39 return module.db.profile.bars[bar:GetName()]
40 end
41
36 42
37 -- Event handlers 43 -- Event handlers
38 function module:OnInitialize() 44 function module:OnInitialize()
39 self.db = ReAction.db:RegisterNamespace( moduleID, 45 self.db = ReAction.db:RegisterNamespace( moduleID,
40 { 46 {
88 local btnCfg = profile.buttons[name] 94 local btnCfg = profile.buttons[name]
89 local barCfg = profile.bars[name] 95 local barCfg = profile.bars[name]
90 96
91 local r, c = bar:GetButtonGrid() 97 local r, c = bar:GetButtonGrid()
92 local n = r*c 98 local n = r*c
93 for i = 1, n do 99 if n ~= #btns then
94 if btnCfg[i] == nil then 100 for i = 1, n do
95 btnCfg[i] = {} 101 if btnCfg[i] == nil then
96 end 102 btnCfg[i] = {}
97 if btns[i] == nil then 103 end
98 local b = Button:New(bar, i, btnCfg[i], barCfg) 104 if btns[i] == nil then
99 btns[i] = b 105 local b = Button:New(bar, i, btnCfg[i], barCfg)
100 bar:AddButton(i,b) 106 btns[i] = b
101 end 107 bar:AddButton(i,b)
102 end 108 end
103 for i = n+1, #btns do 109 end
104 if btns[i] then 110 for i = n+1, #btns do
105 bar:RemoveButton(btns[i]) 111 if btns[i] then
106 btns[i] = btns[i]:Destroy() 112 bar:RemoveButton(btns[i])
107 if btnCfg[i] then 113 btns[i] = btns[i]:Destroy()
108 btnCfg[i] = nil 114 if btnCfg[i] then
115 btnCfg[i] = nil
116 end
109 end 117 end
110 end 118 end
111 end 119 end
112 RefreshLite(bar) 120 RefreshLite(bar)
113 end 121 end
147 end 155 end
148 end 156 end
149 157
150 158
151 ---- Options ---- 159 ---- Options ----
152 local Handler = { } 160 do
153 161 local Handler = { }
154 local options = { 162
155 hideEmpty = { 163 local options = {
156 name = L["Hide Empty Buttons"], 164 hideEmpty = {
157 desc = L["Hide buttons when empty. This option is not supported for multi-state bars"], 165 name = L["Hide Empty Buttons"],
158 order = 1, 166 order = 1,
159 type = "toggle", 167 type = "toggle",
160 get = "GetHideEmpty", 168 width = "double",
161 set = "SetHideEmpty", 169 get = "GetHideEmpty",
162 }, 170 set = "SetHideEmpty",
163 } 171 },
164 172 pages = {
165 function module:GetBarOptions(bar) 173 name = L["# Pages"],
166 return { 174 desc = L["Use the Dynamic State tab to specify page transitions"],
167 type = "group", 175 order = 2,
168 name = L["Action Buttons"], 176 type = "range",
169 handler = Handler:New(bar), 177 min = 1,
170 hidden = "Hidden", 178 max = 10,
171 args = options 179 step = 1,
180 get = "GetNumPages",
181 set = "SetNumPages",
182 },
183 actions = {
184 name = L["Edit Action IDs"],
185 order = 13,
186 type = "group",
187 inline = true,
188 args = {
189 method = {
190 name = L["Assign"],
191 order = 1,
192 type = "select",
193 width = "double",
194 values = { [0] = L["Choose Method..."],
195 [1] = L["Individually"],
196 [2] = L["All at Once"], },
197 get = "GetActionEditMethod",
198 set = "SetActionEditMethod",
199 },
200 rowSelect = {
201 name = L["Row"],
202 desc = L["Rows are numbered top to bottom"],
203 order = 2,
204 type = "select",
205 width = "half",
206 hidden = "IsButtonSelectHidden",
207 values = "GetRowList",
208 get = "GetSelectedRow",
209 set = "SetSelectedRow",
210 },
211 colSelect = {
212 name = L["Col"],
213 desc = L["Columns are numbered left to right"],
214 order = 3,
215 type = "select",
216 width = "half",
217 hidden = "IsButtonSelectHidden",
218 values = "GetColumnList",
219 get = "GetSelectedColumn",
220 set = "SetSelectedColumn",
221 },
222 pageSelect = {
223 name = L["Page"],
224 order = 4,
225 type = "select",
226 width = "half",
227 hidden = "IsPageSelectHidden",
228 values = "GetPageList",
229 get = "GetSelectedPage",
230 set = "SetSelectedPage",
231 },
232 single = {
233 name = L["Action ID"],
234 usage = L["Specify ID 1-120"],
235 order = 5,
236 type = "input",
237 width = "half",
238 hidden = "IsButtonSelectHidden",
239 get = "GetActionID",
240 set = "SetActionID",
241 validate = "ValidateActionID",
242 },
243 multi = {
244 name = L["ID List"],
245 usage = L["Specify a comma-separated list of IDs for each button in the bar (in order). Separate multiple pages with semicolons (;)"],
246 order = 6,
247 type = "input",
248 multiline = true,
249 width = "double",
250 hidden = "IsMultiIDHidden",
251 get = "GetMultiID",
252 set = "SetMultiID",
253 validate = "ValidateMultiID",
254 }
255 }
256 },
172 } 257 }
173 end 258
174 259 function module:GetBarOptions(bar)
175 -- options handler private 260 return {
176 do 261 type = "group",
177 local function GetBarConfig( bar ) 262 name = L["Action Buttons"],
178 return module.db.profile.bars[bar:GetName()] 263 handler = Handler:New(bar),
179 end 264 hidden = "Hidden",
180 265 args = options
266 }
267 end
268
269 -- options handler private
181 function Handler:New(bar) 270 function Handler:New(bar)
182 return setmetatable( { bar = bar }, { __index = Handler } ) 271 return setmetatable( { bar = bar }, { __index = Handler } )
183 end 272 end
184 273
185 function Handler:Hidden() 274 function Handler:Hidden()
197 end 286 end
198 287
199 function Handler:GetHideEmpty() 288 function Handler:GetHideEmpty()
200 return GetBarConfig(self.bar).hideEmpty 289 return GetBarConfig(self.bar).hideEmpty
201 end 290 end
202 end 291
203 292 function Handler:GetNumPages()
293 return GetBarConfig(self.bar).nPages
294 end
295
296 function Handler:SetNumPages(info, value)
297 GetBarConfig(self.bar).nPages = value
298 RefreshLite(self.bar)
299 end
300
301 function Handler:GetActionEditMethod()
302 return self.editMethod or 0
303 end
304
305 function Handler:SetActionEditMethod(info, value)
306 self.editMethod = value
307 end
308
309 function Handler:IsButtonSelectHidden()
310 return self.editMethod ~= 1
311 end
312
313 function Handler:GetRowList()
314 local r,c = self.bar:GetButtonGrid()
315 if self.rowList == nil or #self.rowList ~= r then
316 local list = { }
317 for i = 1, r do
318 table.insert(list,i)
319 end
320 self.rowList = list
321 end
322 return self.rowList
323 end
324
325 function Handler:GetSelectedRow()
326 local r, c = self.bar:GetButtonGrid()
327 local row = self.selectedRow or 1
328 if row > r then
329 row = 1
330 end
331 self.selectedRow = row
332 return row
333 end
334
335 function Handler:SetSelectedRow(info, value)
336 self.selectedRow = value
337 end
338
339 function Handler:GetColumnList()
340 local r,c = self.bar:GetButtonGrid()
341 if self.columnList == nil or #self.columnList ~= c then
342 local list = { }
343 for i = 1, c do
344 table.insert(list,i)
345 end
346 self.columnList = list
347 end
348 return self.columnList
349 end
350
351 function Handler:GetSelectedColumn()
352 local r, c = self.bar:GetButtonGrid()
353 local col = self.selectedColumn or 1
354 if col > c then
355 col = 1
356 end
357 self.selectedColumn = col
358 return col
359 end
360
361 function Handler:SetSelectedColumn(info, value)
362 self.selectedColumn = value
363 end
364
365 function Handler:IsPageSelectHidden()
366 return self.editMethod ~= 1 or (GetBarConfig(self.bar).nPages or 1) < 2
367 end
368
369 function Handler:GetPageList()
370 local n = GetBarConfig(self.bar).nPages or 1
371 if self.pageList == nil or #self.pageList ~= n then
372 local p = { }
373 for i = 1, n do
374 table.insert(p,i)
375 end
376 self.pageList = p
377 end
378 return self.pageList
379 end
380
381 function Handler:GetSelectedPage()
382 local p = self.selectedPage or 1
383 if p > (GetBarConfig(self.bar).nPages or 1) then
384 p = 1
385 end
386 self.selectedPage = p
387 return p
388 end
389
390 function Handler:SetSelectedPage(info, value)
391 self.selectedPage = value
392 end
393
394 function Handler:GetActionID()
395 local row = self.selectedRow or 1
396 local col = self.selectedColumn or 1
397 local r, c = self.bar:GetButtonGrid()
398 local n = (row-1) * c + col
399 local btn = module.buttons[self.bar][n]
400 if btn then
401 return tostring(btn:GetActionID(self.selectedPage or 1))
402 end
403 end
404
405 function Handler:SetActionID(info, value)
406 local row = self.selectedRow or 1
407 local col = self.selectedColumn or 1
408 local r, c = self.bar:GetButtonGrid()
409 local n = (row-1) * c + col
410 local btn = module.buttons[self.bar][n]
411 if btn then
412 btn:SetActionID(tonumber(value), self.selectedPage or 1)
413 end
414 end
415
416 function Handler:ValidateActionID(info, value)
417 value = tonumber(value)
418 if value == nil or value < 1 or value > 120 then
419 return L["Specify ID 1-120"]
420 end
421 return true
422 end
423
424 function Handler:IsMultiIDHidden()
425 return self.editMethod ~= 2
426 end
427
428 function Handler:GetMultiID()
429 local p = { }
430 for i = 1, GetBarConfig(self.bar).nPages or 1 do
431 local b = { }
432 for _, btn in ipairs(module.buttons[self.bar]) do
433 table.insert(b, btn:GetActionID(i))
434 end
435 table.insert(p, table.concat(b,","))
436 end
437 return table.concat(p,";\n")
438 end
439
440
441 local function ParseMultiID(nBtns, nPages, s)
442 if s:match("[^%d%s,;]") then
443 ReAction:Print("items other than digits, spaces, commas, and semicolons in string",s)
444 return nil
445 end
446 local p = { }
447 for list in s:gmatch("[^;]+") do
448 local pattern = ("^%s?$"):format(("%s*(%d+)%s*,"):rep(nBtns))
449 local ids = { list:match(pattern) }
450 if #ids ~= nBtns then
451 ReAction:Print("found",#ids,"buttons instead of",nBtns)
452 return nil
453 end
454 table.insert(p,ids)
455 end
456 if #p ~= nPages then
457 ReAction:Print("found",#p,"pages instead of",nPages)
458 return nil
459 end
460 return p
461 end
462
463 function Handler:SetMultiID(info, value)
464 local btns = module.buttons[self.bar]
465 local p = ParseMultiID(#btns, GetBarConfig(self.bar).nPages or 1, value)
466 for page, b in ipairs(p) do
467 for button, id in ipairs(b) do
468 btns[button]:SetActionID(id, page)
469 end
470 end
471 end
472
473 function Handler:ValidateMultiID(info, value)
474 local bad = L["Invalid action ID list string"]
475 if value == nil or ParseMultiID(#module.buttons[self.bar], GetBarConfig(self.bar).nPages or 1, value) == nil then
476 return bad
477 end
478 return true
479 end
480 end
481
482
483 ------ State property options ------
484 do
485 local pageOptions = {
486 mindcontrol = {
487 name = L["Mind Control Support"],
488 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."],
489 order = 11,
490 type = "toggle",
491 disabled = "IsMCDisabled",
492 hidden = "IsPageHidden",
493 width = "double",
494 set = "SetProp",
495 get = "GetProp",
496 },
497 page = {
498 name = L["Show Page #"],
499 order = 12,
500 type = "select",
501 width = "half",
502 disabled = "IsPageDisabled",
503 hidden = "IsPageHidden",
504 values = "GetPageValues",
505 set = "SetProp",
506 get = "GetPage",
507 },
508 }
509
510 local function pageImpl( bar, states )
511 local map = { }
512 for state, c in pairs(states) do
513 if c.mindcontrol then
514 map[state] = "mc"
515 elseif c.page then
516 map[state] = ("page%d"):format(c.page)
517 end
518 end
519 bar:SetStateAttribute("statebutton", map, 1, true)
520 end
521
522 local PageOptsHandler = { } -- will inherit properties and methods via State:RegisterStateProperty
523
524 function PageOptsHandler:IsMCDisabled(info)
525 if self:IsPageHidden() then
526 return true
527 end
528 -- only allow this if the mind-control selector or custom/keybind is chosen
529 -- see State.lua for the structure of the 'rule' config element
530 local rule = self.states[self:GetName()].rule
531 if rule then
532 if rule.type == "custom" or rule.type == "keybind" then
533 return false
534 else
535 if rule.values and rule.values.possess then
536 return false
537 end
538 end
539 end
540 return true
541 end
542
543 function PageOptsHandler:IsPageDisabled()
544 -- disabled if not an action button
545 return not GetBarConfig(self.bar) or
546 -- OR mind-control remapping is enabled
547 self.states[self:GetName()].mindcontrol or
548 -- OR only one page is enabled
549 (GetBarConfig(self.bar).nPages and GetBarConfig(self.bar).nPages < 2)
550 end
551
552 function PageOptsHandler:IsPageHidden()
553 return not GetBarConfig(self.bar)
554 end
555
556 function PageOptsHandler:GetPageValues()
557 local c = GetBarConfig(self.bar)
558 if c then
559 local n = c.nPages
560 if self._npages ~= n then
561 self._pagevalues = { }
562 self._npages = n
563 -- cache the results
564 for i = 1, n do
565 self._pagevalues[i] = i
566 end
567 end
568 return self._pagevalues
569 end
570 end
571
572 function PageOptsHandler:GetPage(info)
573 return self:GetProp(info) or 1
574 end
575
576 ReAction:GetModule("State"):RegisterStateProperty("page", pageImpl, pageOptions, PageOptsHandler)
577 end
578
579 ------ ActionID allocation ------
580 -- this needs to be high performance when requesting new IDs,
581 -- or certain controls will become sluggish. However, the new-request
582 -- infrastructure can be built lazily the first time that a new request
583 -- comes in (which will only happen at user config time: at static startup
584 -- config time all actionIDs should already have been assigned and stored
585 -- in the config file)
586
587 local IDAlloc
588 do
589 local n = 120
590
591 IDAlloc = setmetatable({ wrap = 1, freecount = n }, {__index = function() return 0 end})
592
593 function IDAlloc:Acquire(id, hint)
594 id = tonumber(id)
595 hint = tonumber(hint)
596 if id and (id < 1 or id > n) then
597 id = nil
598 end
599 if hint and (hint < 1 or hint > n) then
600 hint = nil
601 end
602 if id == nil then
603 -- get a free ID
604 if hint and self[hint] == 0 then
605 -- use the hint if it's free
606 id = hint
607 elseif self.freecount > 0 then
608 -- if neither the id nor the hint are defined or free, but
609 -- the list is known to have free IDs, then start searching
610 -- at the hint for a free one
611 for i = hint or 1, n do
612 if self[i] == 0 then
613 id = i
614 break
615 end
616 end
617 -- self.wrap the search
618 if id == nil and hint and hint > 1 then
619 for i = 1, hint - 1 do
620 if self[i] == 0 then
621 id = i
622 break
623 end
624 end
625 end
626 end
627 if id == nil then
628 -- if there are no free IDs, start wrapping at 1
629 id = self.wrap
630 self.wrap = id + 1
631 if self.wrap > n then
632 self.wrap = 1
633 end
634 end
635 end
636 if self[id] == 0 then
637 self.freecount = self.freecount - 1
638 end
639 self[id] = self[id] + 1
640 return id
641 end
642
643 function IDAlloc:Release(id)
644 id = tonumber(id)
645 if id and (id >= 1 or id <= n) then
646 self[id] = self[id] - 1
647 if self[id] == 0 then
648 self.freecount = self.freecount + 1
649 self.wrap = 1
650 end
651 end
652 end
653 end
654
655 local frameRecycler = { }
204 656
205 ------ Button class ------ 657 ------ Button class ------
206
207 -- use-count of action IDs
208 local nActionIDs = 120
209 local ActionIDList = setmetatable( {}, {
210 __index = function(self, idx)
211 if idx == nil then
212 for i = 1, nActionIDs do
213 if rawget(self,i) == nil then
214 rawset(self,i,1)
215 return i
216 end
217 end
218 error("ran out of action IDs")
219 else
220 local c = rawget(self,idx) or 0
221 rawset(self,idx,c+1)
222 return idx
223 end
224 end,
225 __newindex = function(self,idx,value)
226 if value == nil then
227 value = rawget(self,idx)
228 if value == 1 then
229 value = nil
230 elseif value then
231 value = value - 1
232 end
233 end
234 rawset(self,idx,value)
235 end
236 })
237
238 function Button:New( bar, idx, config, barConfig ) 658 function Button:New( bar, idx, config, barConfig )
239 -- create new self 659 -- create new self
240 self = setmetatable( { }, {__index = Button} ) 660 self = setmetatable( { }, {__index = Button} )
241 self.bar, self.idx, self.config, self.barConfig = bar, idx, config, barConfig 661 self.bar, self.idx, self.config, self.barConfig = bar, idx, config, barConfig
242 662
243 config.name = config.name or ("ReAction_%s_%d"):format(bar:GetName(),idx) 663 local name = config.name or ("ReAction_%s_%s_%d"):format(bar:GetName(),moduleID,idx)
244 self.name = config.name 664 self.name = name
245 config.actionID = ActionIDList[config.actionID] -- gets a free one if none configured 665 config.name = name
666 local lastButton = module.buttons[bar][#module.buttons[bar]]
667 config.actionID = IDAlloc:Acquire(config.actionID, lastButton and lastButton.config.actionID) -- gets a free one if none configured
246 self.nPages = 1 668 self.nPages = 1
247 669
248 local f = CreateFrame("CheckButton", self.name, bar:GetButtonFrame(), "ActionBarButtonTemplate") 670 -- have to recycle frames with the same name: CreateFrame()
671 -- doesn't overwrite existing globals (below). Can't set to nil in the global
672 -- table because you end up getting taint
673 local parent = bar:GetButtonFrame()
674 local f = frameRecycler[name]
675 if f then
676 f:SetParent(parent)
677 else
678 f = CreateFrame("CheckButton", name, parent, "ActionBarButtonTemplate")
679 end
249 680
250 f:SetAttribute("action", config.actionID) 681 f:SetAttribute("action", config.actionID)
251 -- install mind control action support for all buttons here just for simplicity 682 -- install mind control action support for all buttons here just for simplicity
252 if self.idx <= 12 then 683 if self.idx <= 12 then
253 f:SetAttribute("action-mc", 120 + self.idx) 684 f:SetAttribute("*action-mc", 120 + self.idx)
254 end 685 end
255 686
256 self.frame = f 687 self.frame = f
257 self.normalTexture = getglobal(format("%sNormalTexture",f:GetName())) 688 self.normalTexture = getglobal(format("%sNormalTexture",f:GetName()))
258 689
259 -- initialize the hide state 690 -- initialize the hide state
691 f:SetAttribute("showgrid",0)
260 self:ShowGrid(not barConfig.hideEmpty) 692 self:ShowGrid(not barConfig.hideEmpty)
261 if ReAction:GetConfigMode() then 693 if ReAction:GetConfigMode() then
262 self:ShowGrid(true) 694 self:ShowGrid(true)
263 end 695 end
264 696
274 f:UnregisterAllEvents() 706 f:UnregisterAllEvents()
275 f:Hide() 707 f:Hide()
276 f:SetParent(UIParent) 708 f:SetParent(UIParent)
277 f:ClearAllPoints() 709 f:ClearAllPoints()
278 if self.name then 710 if self.name then
279 _G[self.name] = nil 711 frameRecycler[self.name] = f
280 end 712 end
281 if self.config.actionID then 713 if self.config.actionID then
282 ActionIDList[self.config.actionID] = nil 714 IDAlloc:Release(self.config.actionID)
283 end 715 end
284 if self.config.pages then 716 if self.config.pages then
285 for _, id in ipairs(self.config.pages) do 717 for _, id in ipairs(self.config.pageactions) do
286 ActionIDList[id] = nil 718 IDAlloc:Release(id)
287 end 719 end
288 end 720 end
289 self.frame = nil 721 self.frame = nil
290 self.config = nil 722 self.config = nil
291 self.bar = nil 723 self.bar = nil
306 738
307 function Button:GetName() 739 function Button:GetName()
308 return self.name 740 return self.name
309 end 741 end
310 742
311 function Button:GetActionID() 743 function Button:GetActionID(page)
312 return SecureButton_GetModifiedAttribute(self.frame, "action") 744 if page == nil then
313 end 745 -- get the effective ID
314 746 return self.frame.action -- kept up-to-date by Blizzard's ActionButton_CalculateAction()
315 function Button:RefreshPages() 747 else
316 local nPages = 1 --self.bar:GetNumPages() 748 if page == 1 then
317 if nPages ~= self.nPages then 749 return self.config.actionID
750 else
751 return self.config.pageactions and self.config.pageactions[page] or self.config.actionID
752 end
753 end
754 end
755
756 function Button:SetActionID( id, page )
757 id = tonumber(id)
758 page = tonumber(page)
759 if id == nil or id < 1 or id > 120 then
760 error("Button:SetActionID - invalid action ID")
761 end
762 if page and page ~= 1 then
763 if not self.config.pageactions then
764 self.config.pageactions = { }
765 end
766 if self.config.pageactions[page] then
767 IDAlloc:Release(self.config.pageactions[page])
768 end
769 self.config.pageactions[page] = id
770 IDAlloc:Acquire(self.config.pageactions[page])
771 self.frame:SetAttribute(("*action-page%d"):format(page),id)
772 else
773 IDAlloc:Release(self.config.actionID)
774 self.config.actionID = id
775 IDAlloc:Acquire(self.config.actionID)
776 self.frame:SetAttribute("action",id)
777 if self.config.pageactions then
778 self.config.pageactions[1] = id
779 self.frame:SetAttribute("*action-page1",id)
780 end
781 end
782 end
783
784 function Button:RefreshPages( force )
785 local nPages = self.barConfig.nPages
786 if nPages and (nPages ~= self.nPages or force) then
318 local f = self:GetFrame() 787 local f = self:GetFrame()
319 local c = self.config.pages 788 local c = self.config.pageactions
320 if nPages > 1 and not c then 789 if nPages > 1 and not c then
321 c = { } 790 c = { }
322 self.config.pages = c 791 self.config.pageactions = c
323 end 792 end
324 for i = 1, nPages do 793 for i = 1, nPages do
325 c[i] = ActionIDList[c[i]] -- gets a free one if none configured 794 if i > 1 then
326 f:SetAttribute(("action-page%d"):format(i)) 795 c[i] = IDAlloc:Acquire(c[i], self.config.actionID + (i-1)*self.bar:GetNumButtons())
796 else
797 c[i] = self.config.actionID -- page 1 is the same as the base actionID
798 end
799 f:SetAttribute(("*action-page%d"):format(i),c[i])
327 end 800 end
328 for i = nPages+1, #c do 801 for i = nPages+1, #c do
329 ActionIDList[c[i]] = nil 802 IDAlloc:Release(c[i])
330 c[i] = nil 803 c[i] = nil
331 f:SetAttribute(("action-page%d"):format(i)) 804 f:SetAttribute(("*action-page%d"):format(i),nil)
332 end 805 end
333 806 self.nPages = nPages
334 -- TODO:
335 -- apply next-page, prev-page, and direct-page keybinds (via bar:SetStateKeybind abstraction)
336 end 807 end
337 end 808 end
338 809
339 function Button:ShowGrid( show ) 810 function Button:ShowGrid( show )
340 if not InCombatLockdown() then 811 if not InCombatLockdown() then
359 end 830 end
360 end 831 end
361 end 832 end
362 833
363 function Button:ShowActionIDLabel( show ) 834 function Button:ShowActionIDLabel( show )
835 local f = self:GetFrame()
364 if show then 836 if show then
365 local id = self:GetActionID() 837 local id = self:GetActionID()
366 if not self.actionIDLabel and id and id ~= 0 then 838 if not f.actionIDLabel then
367 local f = self:GetFrame()
368 local label = f:CreateFontString(nil,"OVERLAY","GameFontNormalLarge") 839 local label = f:CreateFontString(nil,"OVERLAY","GameFontNormalLarge")
369 label:SetAllPoints() 840 label:SetAllPoints()
370 label:SetJustifyH("CENTER") 841 label:SetJustifyH("CENTER")
371 label:SetShadowColor(0,0,0,1) 842 label:SetShadowColor(0,0,0,1)
372 label:SetShadowOffset(2,-2) 843 label:SetShadowOffset(2,-2)
373 label:SetText(tostring(id)) 844 f.actionIDLabel = label -- store the label with the frame for recycling
374 self.actionIDLabel = label
375 f:HookScript("OnAttributeChanged", 845 f:HookScript("OnAttributeChanged",
376 function(frame, attr, value) 846 function(frame, attr, value)
377 if attr == "state-parent" then 847 if label:IsVisible() and (attr == "state-parent" or attr:match("action")) then
378 label:SetText(tostring(self:GetActionID())) 848 label:SetText(tostring(frame.action))
379 end 849 end
380 end) 850 end)
381 end 851 end
382 self.actionIDLabel:Show() 852 f.actionIDLabel:SetText(tostring(id))
383 elseif self.actionIDLabel then 853 f.actionIDLabel:Show()
384 self.actionIDLabel:Hide() 854 elseif f.actionIDLabel then
385 end 855 f.actionIDLabel:Hide()
386 end 856 end
387 857 end
858