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