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 |