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