a5089ab - add mage action bar setup
[wowui.git] / OmaAB / ActionBars.lua
1 -- ActionBars.lua
2 local _;
3 local pairs = pairs;
4 local format = string.format;
5 local ssub = string.sub;
6 local GetActionInfo, GetActionTexture = GetActionInfo, GetActionTexture;
7 local GetActionLossOfControlCooldown = GetActionLossOfControlCooldown;
8 local GetActionCooldown, GetActionCharges = GetActionCooldown, GetActionCharges;
9 local GetActionText, GetBindingKey = GetActionText, GetBindingKey;
10 local IsConsumableAction, IsStackableAction = IsConsumableAction, IsStackableAction;
11 local IsItemAction, GetActionCount = IsItemAction, GetActionCount;
12 local IsSpellOverlayed, GetMacroSpell = IsSpellOverlayed, GetMacroSpell;
13 local IsMounted = IsMounted;
14 local HasAction, IsUsableAction = HasAction, IsUsableAction;
15 local IsCurrentAction, IsAutoRepeatAction = IsCurrentAction, IsAutoRepeatAction;
16 local CreateFrame = CreateFrame;
17 local RegisterStateDriver = RegisterStateDriver;
18 local CooldownFrame_Set, CooldownFrame_Clear = CooldownFrame_Set, CooldownFrame_Clear;
19 local GameTooltip = GameTooltip;
20 local GameTooltip_SetDefaultAnchor = GameTooltip_SetDefaultAnchor;
21 local COOLDOWN_TYPE_LOSS_OF_CONTROL = COOLDOWN_TYPE_LOSS_OF_CONTROL;
22 local COOLDOWN_TYPE_NORMAL = COOLDOWN_TYPE_NORMAL;
23 local CDTexture = "Interface\\Cooldown\\edge";
24 local locCDTexture = "Interface\\Cooldown\\edge-LoC";
25
26 local BUTTONLOCK = true; -- change to lock button dragging
27
28 local settings = {
29     ["Oma1"] = {
30         bar = 1,
31         start = 1,
32         length = 12,
33         columns = 4,
34         size = 40,
35         x = 580,
36         y = 300,
37         nomouse = true,
38     },
39     ["Oma2"] = {
40         bar = 2,
41         start = 13,
42         length = 12,
43         columns = 4,
44         size = 40,
45         x = 580,
46         y = 180,
47         nomouse = true,
48     },
49     ["Oma3"] = {
50         bar = 3,
51         start = 25,
52         length = 12,
53         columns = 3,
54         x = 1824,
55         y = 128,
56         flyout = "LEFT",
57     },
58     ["Oma4"] = {
59         bar = 4,
60         start = 37,
61         length = 12,
62         columns = 3,
63         x = 1824,
64         y = 256,
65     },
66     ["Oma5"] = {
67         bar = 5,
68         start = 49,
69         length = 12,
70         x = 1000,
71         y = 840,
72     },
73     ["Oma6"] = {
74         bar = 6,
75         start = 61,
76         length = 12,
77         x = 1000,
78         y = 600,
79     },
80     -- used as bonus bars for some classes
81     ["Oma7"] = {
82         bar = 7,
83         start = 73,
84         length = 12,
85         x = 1000,
86         y = 760,
87     },
88     ["Oma8"] = {
89         bar = 8,
90         start = 85,
91         length = 12,
92         x = 1000,
93         y = 720,
94     },
95     ["Oma9"] = {
96         bar = 9,
97         start = 97,
98         length = 12,
99         x = 1000,
100         y = 680,
101     },
102     ["Oma10"] = {
103         bar = 10,
104         start = 109,
105         length = 12,
106         x = 1000,
107         y = 640,
108     },
109 };
110
111 local usingBonusbars = {
112     --["WARRIOR"] = {[7]=true, [8]=true, [9]=true}, -- not using stance separated actionbars
113     ["DRUID"] = {[7]=true, [8]=true, [9]=true}, -- moonkin form page is usable anyway
114     --["DRUID"] = {[7]=true, [8]=true, [9]=true,[10]=true},
115     ["ROGUE"] = {[7]=true},
116     --["PRIEST"] = {[7]=true}, -- shadowform doesn't change abilities
117 };
118
119 local chars = {
120     ["Sylvanas"] = {
121         ["Vildana"] = {1, 2, 3, 4,},
122     },
123     ["Stormreaver"] = {
124         ["Vildan"] = {1, 2, 3, 4,},
125         ["Gedren"] = {1, 2, 3, 4,},
126         ["Gazden"] = {1, 2, 3, 4,},
127         ["Gedran"] = {1, 2, 3, 4,},
128         ["Iled"] = {1, 2, 3, 4,},
129         ["Gilden"] = {1, 2, 3, 4,},
130         ["Gran"] = {1, 2, 3, 4,},
131         ["Gedrin"] = {1, 2, 3, 4,},
132     },
133 };
134
135 local buttons = {};
136 local activeButtons = {};
137
138 local ActionBars = CreateFrame("Frame", "OmaActionBars", UIParent);
139 local inheritedFrames =
140 "SecureActionButtonTemplate,SecureHandlerDragTemplate,SecureHandlerStateTemplate";
141
142 local function showTooltip(secure)
143     GameTooltip_SetDefaultAnchor(GameTooltip, secure);
144     GameTooltip:SetAction(secure:GetAttribute("action"));
145 end
146
147 local function hideTooltip()
148     GameTooltip:Hide();
149 end
150
151 local numChargeCDs = 0;
152 local function createChargeCD(parent)
153     numChargeCDs = numChargeCDs + 1;
154     local frame = CreateFrame("Cooldown", "OmaChargeCD"..numChargeCDs, parent, "CooldownFrameTemplate");
155     frame:SetHideCountdownNumbers(false);
156     frame:SetDrawSwipe(false);
157     frame:SetAllPoints(parent);
158     frame:SetFrameStrata("TOOLTIP");
159     return frame;
160 end
161
162 local function clearChargeCD(parent)
163     if parent.chargecd then CooldownFrame_Clear(parent.chargecd) end
164 end
165
166 local function startChargeCD(parent, start, duration, modrate)
167     if start == 0 then
168         return clearChargeCD(parent);
169     end
170     parent.chargecd = parent.chargecd or createChargeCD(parent);
171     CooldownFrame_Set(parent.chargecd, start, duration, true, true, modrate);
172 end
173
174 local redoCooldown;
175
176 local function updateCooldown(button, slot)
177     -- CD update from FrameXML/ActionButton.lua
178     local locstart, locduration = GetActionLossOfControlCooldown(slot);
179     local start, duration, enable, modrate = GetActionCooldown(slot);
180     local charges, maxcharges, chargestart, chargeduration, chargemodrate = GetActionCharges(slot);
181     -- avoid as many updates as possible by checking if there's changes first
182     if button.prev and
183        button.prev[1] == locstart    and button.prev[2] == locduration and
184        button.prev[3] == start       and button.prev[4] == duration and
185        button.prev[5] == enable      and button.prev[6] == modrate and
186        button.prev[7] == charges     and button.prev[8] == maxcharges and
187        button.prev[9] == chargestart and button.prev[10] == chargeduration and
188        button.prev[11] == chargemodrate then
189         return;
190     end
191     button.prev = { locstart, locduration, start, duration, enable, modrate,
192         charges, maxcharges, chargestart, chargeduration, chargemodrate };
193     if (locstart + locduration) > (start + duration) then
194         if button.cd.currentCooldownType ~= COOLDOWN_TYPE_LOSS_OF_CONTROL then
195             button.cd:SetEdgeTexture(locCDTexture);
196             button.cd:SetSwipeColor(0.17, 0, 0);
197             button.cd:SetHideCountdownNumbers(true);
198             button.cd.currentCooldownType = COOLDOWN_TYPE_LOSS_OF_CONTROL;
199         end
200
201         CooldownFrame_Set(button.cd, locstart, locduration, true, true, modrate);
202         clearChargeCD(button);
203     else
204         if button.cd.currentCooldownType ~= COOLDOWN_TYPE_NORMAL then
205             button.cd:SetEdgeTexture(CDTexture);
206             button.cd:SetSwipeColor(0, 0, 0);
207             button.cd:SetHideCountdownNumbers(false);
208             button.cd.currentCooldownType = COOLDOWN_TYPE_NORMAL;
209         end
210
211         if locstart > 0 then
212             button.cd:SetScript("OnCooldownDone", redoCooldown);
213         end
214         if charges and maxcharges and maxcharges > 1 and charges < maxcharges then
215             startChargeCD(button, chargestart, chargeduration, chargemodrate);
216         else
217             clearChargeCD(button);
218         end
219         CooldownFrame_Set(button.cd, start, duration, enable, false, modrate);
220     end
221 end
222
223 local function redoCooldown(cd)
224     local button = cd:GetParent();
225     cd:SetScript("OnCooldownDone", nil);
226     updateCooldown(button, button.slot);
227 end
228
229 local function updateCount(button, slot)
230     if IsConsumableAction(slot) or IsStackableAction(slot) or
231             (not IsItemAction(slot) and GetActionCount(slot) > 0) then
232         local count = GetActionCount(slot);
233         if count > 99 then
234             button.count:SetText("*");
235         else
236             button.count:SetText(count);
237         end
238         button.count:Show();
239     else
240         local charges, maxcharges = GetActionCharges(slot);
241         if maxcharges > 1 then
242             button.count:SetText(charges);
243             button.count:Show();
244         else
245             button.count:Hide();
246         end
247     end
248 end
249
250 local function updateUsable(button, slot)
251     local isUsable, noMana = IsUsableAction(slot);
252     if isUsable then
253         button.icon:SetVertexColor(1, 1, 1);
254     elseif noMana then
255         button.icon:SetVertexColor(0, 0.5, 1);
256     else
257         button.icon:SetVertexColor(0.4, 0.4, 0.4);
258     end
259 end
260
261 local function updateState(button, slot)
262     button:SetChecked(IsCurrentAction(slot) or IsAutoRepeatAction(slot));
263 end
264
265 local function updateGlow(button, slot)
266     local stype, id = GetActionInfo(slot);
267     if stype == "spell" and IsSpellOverlayed(id) then
268         button.glow:Show();
269     elseif stype == "macro" then
270         local macroid = GetMacroSpell(id);
271         if macroid and IsSpellOverlayed(macroid) then
272             button.glow:Show();
273         else
274             button.glow:Hide();
275         end
276     else -- TODO FlyoutHasSpell glow
277         button.glow:Hide();
278     end
279 end
280
281 local function startGlow(button, slot, spell)
282     local stype, id = GetActionInfo(slot);
283     if stype == "spell" and id == spell then
284         button.glow:Show();
285     elseif stype == "macro" then
286         local macroid = GetMacroSpell(id);
287         if macroid and macroid == spell then
288             button.glow:Show();
289         end
290     end
291     -- TODO FlyoutHasSpell glow
292 end
293
294 local function stopGlow(button, slot, spell)
295     local stype, id = GetActionInfo(slot);
296     if stype == "spell" and id == spell then
297         button.glow:Hide();
298     elseif stype == "macro" then
299         local macroid = GetMacroSpell(id);
300         if macroid and macroid == spell then
301             button.glow:Hide();
302         end
303     end
304     -- TODO FlyoutHasSpell glow
305 end
306
307 local function updateButton(button, slot)
308     if HasAction(slot) then
309         activeButtons[slot] = button;
310         button.base:Show();
311         button.icon:SetTexture(GetActionTexture(slot));
312         updateCooldown(button, slot);
313         updateUsable(button, slot);
314         updateState(button, slot);
315         updateCount(button, slot);
316         updateGlow(button, slot);
317         if not button.notext and not IsConsumableAction(slot) and not IsStackableAction(slot) then
318             button.text:SetText(ssub(GetActionText(slot) or "", 1, 4));
319             button.text:Show();
320         end
321         if button.hotkey.shown then button.hotkey:Show() end
322     else
323         activeButtons[slot] = nil;
324         if not button.grid then button.base:Hide() end
325         button.icon:SetTexture(nil);
326         button.cd:Hide();
327         button.count:Hide();
328         button.hotkey:Hide();
329         button.text:Hide();
330         button.glow:Hide();
331         button:SetChecked(false);
332     end
333 end
334
335 local function updateHotkeys(button)
336     local key = GetBindingKey(format("CLICK %s:LeftButton", button:GetName()));
337     if key and key ~= "" then
338         -- from LibKeyBound-1.0
339         key = key:upper();
340         key = key:gsub(" ", "");
341         key = key:gsub("ALT%-", "a");
342         key = key:gsub("CTRL%-", "c");
343         key = key:gsub("SHIFT%-", "s");
344         key = key:gsub("NUMPAD", "n");
345         button.hotkey:SetText(key);
346         button.hotkey.shown = true;
347         button.hotkey:Show();
348     else
349         button.hotkey.shown = nil;
350         button.hotkey:Hide();
351     end
352 end
353
354 local mainbartoggle = "[overridebar][possessbar][shapeshift]possess;";
355 mainbartoggle = mainbartoggle.."[bonusbar:1,stealth:1]bonusbar2;"; -- prowl
356 mainbartoggle = mainbartoggle.."[bonusbar:1]bonusbar1;[bonusbar:2]bonusbar2;"; -- cat form, unused
357 mainbartoggle = mainbartoggle.."[bonusbar:3]bonusbar3;[bonusbar:4]bonusbar4;"; -- bear form, moonkin form
358 mainbartoggle = mainbartoggle.."normal";
359 local function setupSnippets(secure, slot)
360     -- FrameXML/SecureHandlers.lua has arguments and return value
361     -- args: self, button, kind, value, ... (kind, value, ... from GetCursorInfo())
362     -- returns: kind, target, detail
363     -- or: "clear", kind, target, detail
364     -- used for Pickup* functions
365     -- some of these snippets based on LibActionButton-1.0
366     secure:SetAttribute("_ondragstart", [=[
367         return "action", self:GetAttribute("action");
368     ]=]);
369     secure:SetAttribute("_onreceivedrag", [=[
370         if not kind or not value then return nil end
371         return "action", self:GetAttribute("action");
372     ]=]);
373     -- pre-wrapper can pass a message to post-wrapper
374     secure:WrapScript(secure, "OnDragStart", [=[
375         local kind, value = GetActionInfo(self:GetAttribute("action"));
376         return "message", format("%s|%s", tostring(kind), tostring(value));
377     ]=], [=[
378         local kind, value = GetActionInfo(self:GetAttribute("action"));
379         if message ~= format("%s|%s", tostring(kind), tostring(value)) then
380             self:CallMethod("ActionChanged");
381         end
382     ]=]);
383     secure:WrapScript(secure, "OnReceiveDrag", [=[
384         local kind, value = GetActionInfo(self:GetAttribute("action"));
385         return "message", format("%s|%s", tostring(kind), tostring(value));
386     ]=], [=[
387         local kind, value = GetActionInfo(self:GetAttribute("action"));
388         if message ~= format("%s|%s", tostring(kind), tostring(value)) then
389             self:CallMethod("ActionChanged");
390         end
391     ]=]);
392     function secure:UpdateState()
393         return updateState(self, self.slot);
394     end
395     secure:WrapScript(secure, "OnClick", [=[
396         local kind, value = GetActionInfo(self:GetAttribute("action"));
397         return nil, format("%s|%s", tostring(kind), tostring(value));
398     ]=], [=[
399         local kind, value = GetActionInfo(self:GetAttribute("action"));
400         if message ~= format("%s|%s", tostring(kind), tostring(value)) then
401             self:CallMethod("ActionChanged");
402         else
403             self:CallMethod("UpdateState");
404         end
405     ]=]);
406     if slot < 13 then
407         -- first action bar has possible states based on vehicle/possess etc.
408         secure:SetAttribute("origaction", slot);
409         secure:SetAttribute("_onstate-possess", [=[
410             local oldslot = self:GetAttribute("action");
411             if newstate == "possess" then
412                 local slot;
413                 if HasVehicleActionBar() then
414                     slot = (GetVehicleBarIndex()-1)*12+self:GetAttribute("origaction");
415                 elseif HasOverrideActionBar() then
416                     slot = (GetOverrideBarIndex()-1)*12+self:GetAttribute("origaction");
417                 elseif HasTempShapeshiftActionBar() then
418                     slot = (GetTempShapeshiftBarIndex()-1)*12+self:GetAttribute("origaction");
419                 else
420                     -- something wrong, just revert to normal
421                     print("Possess bar index not found");
422                     slot = self:GetAttribute("origaction");
423                 end
424                 self:SetAttribute("action", slot);
425             elseif newstate == "bonusbar1" then
426                 self:SetAttribute("action", 72+self:GetAttribute("origaction"));
427             elseif newstate == "bonusbar2" then
428                 self:SetAttribute("action", 84+self:GetAttribute("origaction"));
429             elseif newstate == "bonusbar3" then
430                 self:SetAttribute("action", 96+self:GetAttribute("origaction"));
431             elseif newstate == "bonusbar4" then
432                 --self:SetAttribute("action", 108+self:GetAttribute("origaction"));
433                 -- moonkin form, don't change actionbar
434                 self:SetAttribute("action", self:GetAttribute("origaction"));
435             else
436                 self:SetAttribute("action", self:GetAttribute("origaction"));
437             end
438             self:CallMethod("ActionChanged", oldslot);
439         ]=]);
440         RegisterStateDriver(secure, "possess", mainbartoggle);
441     else
442         function secure:ShowButton() if HasAction(slot) then activeButtons[slot] = self end end
443         function secure:HideButton() activeButtons[slot] = nil end
444         -- all other action bar are hidden if with overridebar or vehicleui (not shapeshift, possessbar)
445         -- default Bartender4 options
446         secure:SetAttribute("_onstate-possess", [=[
447             if newstate == "possess" then
448                 self:Hide();
449                 self:CallMethod("HideButton");
450             else
451                 self:Show();
452                 self:CallMethod("ShowButton");
453             end
454         ]=]);
455         RegisterStateDriver(secure, "possess", "[overridebar][vehicleui] possess; normal");
456     end
457 end
458
459 local function createActionBar(parent, config)
460     local prev;
461     local i = 0;
462     local bar = CreateFrame("Frame", "OmaBTBar"..config.bar, parent, "SecureFrameTemplate");
463     bar:SetPoint("TOPLEFT", parent, "BOTTOMLEFT", config.x, config.y);
464     bar:SetWidth(1);
465     bar:SetHeight(1);
466     if config.hidden then
467         bar:Hide();
468     end
469     for slot = config.start, config.start+config.length-1 do
470         local secure = CreateFrame("CheckButton", "OmaBT"..slot, bar, inheritedFrames);
471         secure.slot = slot;
472         if slot == config.start then
473             secure:SetPoint("TOPLEFT");
474         elseif config.columns and i % config.columns == 0 then
475             secure:SetPoint("TOPLEFT", _G["OmaBT"..(slot-config.columns)], "BOTTOMLEFT");
476         else
477             secure:SetPoint("TOPLEFT", prev, "TOPRIGHT");
478         end
479         secure:RegisterForClicks("AnyUp");
480         if not BUTTONLOCK then
481             secure:RegisterForDrag("LeftButton", "RightButton");
482         end
483         if config.nomouse then
484             secure:EnableMouse(false);
485         else
486             -- only show tooltips for bars with mouse interaction
487             secure:SetScript("OnEnter", showTooltip);
488             secure:SetScript("OnLeave", hideTooltip);
489         end
490         secure:SetWidth(config.size or 32);
491         secure:SetHeight(config.size or 32);
492         secure.base = secure:CreateTexture(nil, "BACKGROUND");
493         secure.base:SetAllPoints();
494         secure.base:SetColorTexture(0, 0, 0, 0.5);
495         secure.iconbase = secure:CreateTexture(nil, "BORDER");
496         secure.iconbase:SetPoint("TOPLEFT", secure.base, "TOPLEFT", 1, -1);
497         secure.iconbase:SetPoint("BOTTOMRIGHT", secure.base, "BOTTOMRIGHT", -1, 1);
498         secure.iconbase:SetColorTexture(0, 0, 0, 0.5);
499         secure.iconbase:Hide();
500         secure.icon = secure:CreateTexture(nil, "ARTWORK");
501         secure.icon:SetPoint("TOPLEFT", secure.iconbase, "TOPLEFT");
502         secure.icon:SetPoint("BOTTOMRIGHT", secure.iconbase, "BOTTOMRIGHT");
503         secure.icon:SetTexCoord(0.07, 0.93, 0.07, 0.93);
504         secure:SetCheckedTexture("Interface\\Buttons\\CheckButtonHilight");
505         secure.autocastable = secure:CreateTexture(nil, "OVERLAY");
506         secure.autocastable:SetPoint("CENTER");
507         secure.autocastable:SetWidth(58);
508         secure.autocastable:SetHeight(58);
509         secure.autocastable:SetTexture("Interface\\Buttons\\UI-AutoCastableOverlay");
510         secure.autocastable:Hide();
511         secure.glow = secure:CreateTexture(nil, "OVERLAY", nil, 1);
512         secure.glow:SetPoint("CENTER");
513         secure.glow:SetWidth(config.size and config.size+26 or 53);
514         secure.glow:SetHeight(config.size and config.size+26 or 53);
515         secure.glow:SetTexture("Interface\\SpellActivationOverlay\\IconAlert");
516         secure.glow:SetTexCoord(0.00781250, 0.50781250, 0.27734375, 0.52634375);
517         secure.glow:Hide();
518         secure.hotkey = secure:CreateFontString(nil, "OVERLAY", "NumberFontNormalGray");
519         secure.hotkey:SetPoint("TOPRIGHT", secure, "TOPRIGHT", 2, -1);
520         secure.count = secure:CreateFontString(nil, "OVERLAY", "NumberFontNormal");
521         secure.count:SetPoint("BOTTOMRIGHT", secure, "BOTTOMRIGHT", 2, -1);
522         secure.text = secure:CreateFontString(nil, "OVERLAY", "NumberFontNormal");
523         secure.text:SetPoint("BOTTOMLEFT", secure, "BOTTOMLEFT", 2, -1);
524         secure.text:Hide();
525         secure.notext = config.notext;
526         secure.cd = CreateFrame("Cooldown", "OmaBTCD"..slot, secure, "CooldownFrameTemplate");
527         secure.cd:SetAllPoints();
528         secure:SetAttribute("type", "action");
529         secure:SetAttribute("action", slot);
530         if config.flyout then
531             secure:SetAttribute("flyoutDirection", config.flyout);
532         end
533         function secure:ActionChanged(oldslot)
534             if oldslot then
535                 activeButtons[oldslot] = nil;
536                 self.prev = nil; -- invalidate previous CD when slot changes
537             end
538             self.slot = self:GetAttribute("action");
539             return updateButton(self, self.slot);
540         end
541         secure:ActionChanged(); -- initial update
542         setupSnippets(secure, slot);
543         updateHotkeys(secure);
544         buttons[slot] = secure;
545         prev = secure;
546         i = i + 1;
547     end
548 end
549
550 local function initialize()
551     local _, class = UnitClass("player");
552     local name, realm = UnitFullName("player");
553     if class == "MAGE" then
554         settings.Oma1.x = 750;
555         settings.Oma1.y = 386;
556         settings.Oma1.columns = 12;
557         settings.Oma1.size = 34;
558         settings.Oma1.notext = true;
559         settings.Oma2.x = 580;
560         settings.Oma2.y = 300;
561     end
562     ActionBars:SetFrameStrata("LOW");
563     ActionBars:SetPoint("BOTTOMLEFT");
564     ActionBars:SetWidth(1);
565     ActionBars:SetHeight(1);
566     for _, config in pairs(settings) do
567         if (not usingBonusbars[class] or not usingBonusbars[class][config.bar]) and
568            (not chars[realm] or not chars[realm][name] or chars[realm][name][config.bar]) then
569             createActionBar(ActionBars, config);
570         end
571     end
572 end
573
574 local function setupBindings()
575     _G["BINDING_HEADER_OmaAB"] = "Oma Action Bar";
576     for i = 1,10 do
577         _G["BINDING_HEADER_OMAABBLANK"..i] = "Bar "..i;
578         for j = 1,12 do
579             _G[format("BINDING_NAME_CLICK OmaBT%d:LeftButton", (i-1)*12+j)] = format("Bar %d Button %d", i, j);
580         end
581     end
582 end
583
584 local mounted = false;
585
586 local events = {
587     ["ACTIONBAR_UPDATE_COOLDOWN"] = function()
588         for _, button in pairs(activeButtons) do
589             updateCooldown(button, button.slot);
590         end
591     end,
592     ["SPELL_UPDATE_CHARGES"] = function()
593         for _, button in pairs(activeButtons) do
594             updateCount(button, button.slot);
595         end
596     end,
597     ["ACTIONBAR_SLOT_CHANGED"] = function(slot)
598         if buttons[slot] then buttons[slot]:ActionChanged() end
599     end,
600     ["ACTIONBAR_SHOWGRID"] = function()
601         for _, button in pairs(buttons) do
602             button.grid = true;
603             button.iconbase:Show();
604             if not activeButtons[button.slot] then button.base:Show() end
605         end
606     end,
607     ["ACTIONBAR_HIDEGRID"] = function()
608         for _, button in pairs(buttons) do
609             button.grid = nil;
610             button.iconbase:Hide();
611             if not activeButtons[button.slot] then button.base:Hide() end
612         end
613     end,
614     ["ACTIONBAR_UPDATE_STATE"] = function()
615         for _, button in pairs(activeButtons) do
616             updateState(button, button.slot);
617         end
618     end,
619     ["ACTIONBAR_UPDATE_USABLE"] = function()
620         for _, button in pairs(activeButtons) do
621             updateUsable(button, button.slot);
622         end
623     end,
624     ["UPDATE_OVERRIDE_ACTIONBAR"] = function()
625         if buttons[1] then -- called before PLAYER_LOGIN
626             for _, button in pairs(buttons) do
627                 updateButton(button, button.slot);
628             end
629         end
630     end,
631     ["START_AUTOREPEAT_SPELL"] = function()
632         for _, button in pairs(activeButtons) do
633             if IsAutoRepeatAction(button.slot) then
634                 button.autorepeating = true;
635                 button.icon:SetVertexColor(0, 1, 0.5);
636             end
637         end
638     end,
639     ["STOP_AUTOREPEAT_SPELL"] = function()
640         for _, button in pairs(activeButtons) do
641             if button.autorepeating then
642                 button.autorepeating = nil;
643                 updateUsable(button, button.slot);
644             end
645         end
646     end,
647     ["SPELL_ACTIVATION_OVERLAY_GLOW_SHOW"] = function(spell)
648         -- TODO create mapping from spellIDs to buttons
649         for _, button in pairs(activeButtons) do
650             startGlow(button, button.slot, spell);
651         end
652     end,
653     ["SPELL_ACTIVATION_OVERLAY_GLOW_HIDE"] = function(spell)
654         -- TODO create mapping from spellIDs to buttons
655         for _, button in pairs(activeButtons) do
656             stopGlow(button, button.slot, spell);
657         end
658     end,
659     ["UPDATE_BINDINGS"] = function()
660         for _, button in pairs(buttons) do
661             updateHotkeys(button);
662         end
663     end,
664     ["UNIT_AURA"] = function(unit)
665         -- using UNIT_AURA instead of COMPANION_UPDATE to not update every time
666         -- someone mounts, tracking player mount status with COMPANION_UPDATE is
667         -- inconsistent
668         if (not mounted and IsMounted()) or (mounted and not IsMounted()) then
669             mounted = not mounted;
670             for _, button in pairs(activeButtons) do
671                 updateState(button, button.slot);
672             end
673         end
674     end,
675     ["UPDATE_ALL_BUTTONS"] = function()
676         for _, button in pairs(buttons) do
677             updateButton(button, button.slot);
678         end
679     end,
680     ["PLAYER_LOGIN"] = function()
681         initialize();
682     end,
683     ["ADDON_LOADED"] = function(addon)
684         if addon == "OmaAB" then
685             setupBindings();
686             ActionBars:UnregisterEvent("ADDON_LOADED");
687         end
688     end,
689 };
690 events["LOSS_OF_CONTROL_ADDED"] = events["ACTIONBAR_UPDATE_COOLDOWN"];
691 events["LOSS_OF_CONTROL_UPDATE"] = events["ACTIONBAR_UPDATE_COOLDOWN"];
692 events["PLAYER_MOUNT_DISPLAY_CHANGED"] = events["ACTIONBAR_UPDATE_USABLE"];
693 events["TRADE_SKILL_SHOW"] = events["ACTIONBAR_UPDATE_STATE"];
694 events["TRADE_SKILL_CLOSE"] = events["ACTIONBAR_UPDATE_STATE"];
695 events["ARCHAEOLOGY_CLOSED"] = events["ACTIONBAR_UPDATE_STATE"];
696 events["PLAYER_ENTERING_WORLD"] = events["UPDATE_ALL_BUTTONS"];
697 events["UPDATE_VEHICLE_ACTIONBAR"] = events["UPDATE_ALL_BUTTONS"];
698 events["UPDATE_SHAPESHIFT_FORM"] = events["UPDATE_ALL_BUTTONS"];
699 events["SPELL_UPDATE_ICON"] = events["UPDATE_ALL_BUTTONS"];
700 events["PET_STABLE_UPDATE"] = events["UPDATE_ALL_BUTTONS"];
701 events["PET_STABLE_SHOW"] = events["UPDATE_ALL_BUTTONS"];
702 events["PLAYER_SPECIALIZATION_CHANGED"] = events["UPDATE_ALL_BUTTONS"];
703 events["UNIT_ENTERED_VEHICLE"] = function(unit)
704     if unit == "player" then events["ACTIONBAR_UPDATE_STATE"]() end
705 end
706 events["UNIT_EXITED_VEHICLE"] = events["UNIT_ENTERED_VEHICLE"];
707
708 ActionBars:RegisterEvent("ADDON_LOADED");
709 ActionBars:RegisterEvent("PLAYER_LOGIN");
710 ActionBars:RegisterEvent("ACTIONBAR_UPDATE_COOLDOWN");
711 ActionBars:RegisterEvent("ACTIONBAR_UPDATE_USABLE");
712 ActionBars:RegisterEvent("ACTIONBAR_UPDATE_STATE");
713 ActionBars:RegisterEvent("ACTIONBAR_SLOT_CHANGED");
714 ActionBars:RegisterEvent("ACTIONBAR_SHOWGRID");
715 ActionBars:RegisterEvent("ACTIONBAR_HIDEGRID");
716 ActionBars:RegisterEvent("SPELL_UPDATE_ICON");
717 ActionBars:RegisterEvent("SPELL_UPDATE_CHARGES");
718 ActionBars:RegisterEvent("SPELL_ACTIVATION_OVERLAY_GLOW_SHOW");
719 ActionBars:RegisterEvent("SPELL_ACTIVATION_OVERLAY_GLOW_HIDE");
720 ActionBars:RegisterEvent("UPDATE_VEHICLE_ACTIONBAR");
721 ActionBars:RegisterEvent("UPDATE_OVERRIDE_ACTIONBAR");
722 ActionBars:RegisterEvent("PLAYER_MOUNT_DISPLAY_CHANGED");
723 ActionBars:RegisterEvent("PLAYER_SPECIALIZATION_CHANGED");
724 ActionBars:RegisterEvent("UNIT_ENTERED_VEHICLE");
725 ActionBars:RegisterEvent("UNIT_EXITED_VEHICLE");
726 ActionBars:RegisterEvent("PET_STABLE_UPDATE");
727 ActionBars:RegisterEvent("PET_STABLE_SHOW");
728 ActionBars:RegisterEvent("UPDATE_BINDINGS");
729 ActionBars:RegisterUnitEvent("UNIT_AURA", "player");
730 ActionBars:SetScript("OnEvent", function(self, event, arg1)
731     events[event](arg1);
732 end);