flickerstreak@7: -- main.lua flickerstreak@1: -- flickerstreak@1: -- Top-level file for the ReAction Action Bar add-on flickerstreak@1: -- flickerstreak@7: -- implemented in terms of the Ace 2 development framework library: http://www.wowace.com flickerstreak@1: -- flickerstreak@1: flickerstreak@7: -- Ace Library local object initialization flickerstreak@7: local L = AceLibrary("AceLocale-2.2"):new("ReAction") flickerstreak@7: local dewdrop = AceLibrary("Dewdrop-2.0") flickerstreak@7: local tablet = AceLibrary("Tablet-2.0") flickerstreak@1: flickerstreak@7: -- private functions flickerstreak@2: local function tcopy(t) flickerstreak@2: local r = { } flickerstreak@2: for k, v in pairs(t) do flickerstreak@2: r[k] = (type(v) == "table" and tcopy(v) or v) flickerstreak@2: end flickerstreak@2: return r flickerstreak@2: end flickerstreak@2: flickerstreak@7: -- private constants flickerstreak@7: local EMPTY_BAR_SLOT = -1 flickerstreak@7: flickerstreak@7: -- key binding label constants flickerstreak@7: BINDING_HEADER_REACTION = L["ReAction"] flickerstreak@7: BINDING_NAME_REACTION_TOGGLELOCK = L["Toggle ReAction Bar Lock"] flickerstreak@7: BINDING_NAME_REBOUND_TOGGLEBINDINGMODE = L["Toggle ReBound Keybinding Mode"] flickerstreak@7: flickerstreak@7: flickerstreak@7: -- main object flickerstreak@7: local main = AceLibrary("AceAddon-2.0"):new( flickerstreak@7: "AceConsole-2.0", flickerstreak@7: "AceEvent-2.0", flickerstreak@7: "AceDB-2.0", flickerstreak@7: "FuBarPlugin-2.0" flickerstreak@7: ) flickerstreak@1: flickerstreak@1: -- initial non-persistent state flickerstreak@7: main.locked = true flickerstreak@1: flickerstreak@7: -- set a global variable for Bindings.xml flickerstreak@7: ReActionAddOn = main flickerstreak@7: flickerstreak@7: flickerstreak@7: -- FuBar plugin setup flickerstreak@7: -- Even if FuBar isn't installed, this gives us a nice minimap-button interface. flickerstreak@7: main.hasIcon = "Interface\\Icons\\INV_Qiraj_JewelEncased" flickerstreak@7: main.hasNoColor = true flickerstreak@7: main.hideMenuTitle = true flickerstreak@7: main.defaultPosition = "LEFT" flickerstreak@7: main.defaultMinimapPosition = 240 -- degrees flickerstreak@7: main.OnMenuRequest = tcopy(ReActionGlobalMenuOptions) -- use a copy, or bar menus will have FuBar inserted items flickerstreak@7: main.independentProfile = true flickerstreak@7: flickerstreak@7: -- set the handler for the global bar menu options flickerstreak@7: -- have to do this after tcopy() above, otherwise it will try to copy the handler object (bad idea) flickerstreak@7: ReActionGlobalMenuOptions.handler = main flickerstreak@7: flickerstreak@1: flickerstreak@1: flickerstreak@1: flickerstreak@1: -- Event handling flickerstreak@7: function main:OnInitialize() flickerstreak@7: self:RegisterChatCommand( {L["/reaction"], L["/rxn"]}, ReActionConsoleOptions, "REACTION" ) flickerstreak@1: self:RegisterDB("ReActionDB","ReActionDBPC") flickerstreak@7: self:RegisterDefaults("profile", ReAction_DefaultProfile) flickerstreak@1: self:RegisterEvent("PLAYER_REGEN_DISABLED","CombatLockdown") flickerstreak@2: self:RegisterEvent("PLAYER_ENTERING_WORLD","HideDefaultBars") flickerstreak@7: self:RegisterEvent("EVENT_REBOUND_KEYBINDING_MODE") flickerstreak@1: end flickerstreak@1: flickerstreak@7: function main:OnEnable() flickerstreak@7: -- this gets called at startup and when the profile is changed flickerstreak@1: if self.db.profile.firstRunDone ~= true then flickerstreak@1: -- Do some "first-run" setup flickerstreak@7: self:StealKeyBindings() flickerstreak@1: self.db.profile.firstRunDone = true flickerstreak@7: self.db.profile.bars = tcopy(ReAction_DefaultBlizzardBars) flickerstreak@1: end flickerstreak@7: self:DestroyAllBars() flickerstreak@2: self:SetupBars() flickerstreak@1: end flickerstreak@1: flickerstreak@7: function main:OnDisable() flickerstreak@1: self:Lock() flickerstreak@1: end flickerstreak@1: flickerstreak@7: function main:OnProfileEnable() flickerstreak@7: -- for profile switching flickerstreak@7: self:OnEnable() flickerstreak@1: end flickerstreak@1: flickerstreak@7: function main:CombatLockdown() flickerstreak@1: if not self:IsLocked() then flickerstreak@1: self:Lock() flickerstreak@7: ReBound:Disable() flickerstreak@7: UIErrorsFrame:AddMessage(L["ReAction bars locked when in combat"]) flickerstreak@1: end flickerstreak@1: end flickerstreak@1: flickerstreak@7: function main:EVENT_REBOUND_KEYBINDING_MODE(enabled) flickerstreak@7: for _, bar in pairs(self.bars) do flickerstreak@7: for __, button in pairs(bar.buttons) do flickerstreak@8: if button and button ~= EMPTY_BAR_SLOT then flickerstreak@8: button:TempShow(enabled) flickerstreak@8: end flickerstreak@7: end flickerstreak@7: end flickerstreak@7: end flickerstreak@1: flickerstreak@7: flickerstreak@7: -- FuBar plugin methods flickerstreak@7: function main:OnTooltipUpdate() flickerstreak@7: local c = tablet:AddCategory("columns", 2) flickerstreak@7: c:AddLine("text", L["Bar lock"], "text2", self.locked and ("|cffff0000"..L["Locked"].."|r") or ("|cffffcc00"..L["Unlocked"].."|r")) flickerstreak@7: c:AddLine("text", L["Button lock"], "text2", LOCK_ACTIONBAR == "1" and ("|cffcc0000"..L["Locked"].."|r") or ("|cff00cc00"..L["Unlocked"].."|r")) flickerstreak@7: c:AddLine("text", L["Kebinding mode"], "text2", ReBound:IsEnabled() and ("|cff33ff33"..L["On"].."|r") or ("|cffffcc00"..L["Off"].."|r")) flickerstreak@7: tablet:SetHint(L["|cffffcc00Shift-Click for bar lock|n|cff33ff33Alt-Click|r for keybindings|nRight-click for menu"]) flickerstreak@7: end flickerstreak@7: flickerstreak@7: function main:OnClick(button) flickerstreak@7: if IsShiftKeyDown() then flickerstreak@7: self:ToggleLocked() flickerstreak@7: self:UpdateDisplay() flickerstreak@7: elseif IsAltKeyDown() then flickerstreak@7: ReBound:ToggleEnabled() flickerstreak@7: self:UpdateDisplay() flickerstreak@7: end flickerstreak@7: end flickerstreak@7: flickerstreak@7: flickerstreak@7: -- lock/unlock bars flickerstreak@7: function main:SetLocked( lock ) flickerstreak@2: if lock ~= self.locked then flickerstreak@2: if not lock and InCombatLockdown() then flickerstreak@2: UIErrorsFrame:AddMessage(SPELL_FAILED_AFFECTING_COMBAT) flickerstreak@2: else flickerstreak@2: self.locked = lock and true or false -- force data integrity flickerstreak@2: for _, bar in pairs(self.bars) do flickerstreak@7: if bar ~= EMPTY_BAR_SLOT then flickerstreak@7: if self.locked then flickerstreak@7: bar:HideControls() flickerstreak@7: -- close any dewdrop menu owned by the bar flickerstreak@7: if bar:GetControlFrame() == dewdrop:GetOpenedParent() then flickerstreak@7: dewdrop:Close() flickerstreak@7: end flickerstreak@7: else flickerstreak@7: bar:ShowControls() flickerstreak@7: end flickerstreak@7: end flickerstreak@2: end flickerstreak@2: end flickerstreak@1: end flickerstreak@1: end flickerstreak@1: flickerstreak@7: function main:IsLocked() flickerstreak@1: return self.locked flickerstreak@1: end flickerstreak@1: flickerstreak@7: function main:Lock() flickerstreak@1: self:SetLocked(true) flickerstreak@1: end flickerstreak@1: flickerstreak@7: function main:Unlock() flickerstreak@1: self:SetLocked(false) flickerstreak@1: end flickerstreak@1: flickerstreak@7: function main:ToggleLocked() flickerstreak@7: main:SetLocked( not(self.locked) ) flickerstreak@1: end flickerstreak@1: flickerstreak@1: flickerstreak@1: flickerstreak@1: -- Hide the default Blizzard main bar artwork flickerstreak@7: function main:HideArt() flickerstreak@1: if self.db.profile.hideArt then flickerstreak@7: -- the pet bar is a child of MainMenuBar, and can't be hidden. Need to reparent it flickerstreak@7: PetActionBarFrame:SetParent(UIParent) flickerstreak@1: MainMenuBar:Hide() -- this also hides the bags, xp bar, lag meter, and micro menu buttons. flickerstreak@7: -- these two are the pet bar background flickerstreak@7: -- unfortunately UIParent_ManageFramePositions() shows and hides these too flickerstreak@7: -- so they get reparented to MainMenuBar flickerstreak@7: SlidingActionBarTexture0:SetParent(MainMenuBar) flickerstreak@7: SlidingActionBarTexture1:SetParent(MainMenuBar) flickerstreak@1: else flickerstreak@7: SlidingActionBarTexture0:SetParent(PetActionBarFrame) flickerstreak@7: SlidingActionBarTexture1:SetParent(PetActionBarFrame) flickerstreak@1: MainMenuBar:Show() flickerstreak@1: end flickerstreak@1: end flickerstreak@1: flickerstreak@7: function main:IsArtHidden() flickerstreak@1: return self.db.profile.hideArt flickerstreak@1: end flickerstreak@1: flickerstreak@7: function main:SetHideArt( hide ) flickerstreak@2: if InCombatLockdown() then flickerstreak@2: UIErrorsFrame:AddMessage(SPELL_FAILED_AFFECTING_COMBAT) flickerstreak@2: else flickerstreak@2: self.db.profile.hideArt = hide and true or false -- force data integrity flickerstreak@2: self:HideArt() flickerstreak@2: end flickerstreak@1: end flickerstreak@1: flickerstreak@7: function main:ToggleHideArt() flickerstreak@1: self:SetHideArt( not self:IsArtHidden() ) flickerstreak@1: end flickerstreak@1: flickerstreak@1: flickerstreak@1: flickerstreak@1: -- Hide default Blizzard bars flickerstreak@1: local blizzDefaultBars = { flickerstreak@1: ActionButton1, flickerstreak@1: ActionButton2, flickerstreak@1: ActionButton3, flickerstreak@1: ActionButton4, flickerstreak@1: ActionButton5, flickerstreak@1: ActionButton6, flickerstreak@1: ActionButton7, flickerstreak@1: ActionButton8, flickerstreak@1: ActionButton9, flickerstreak@1: ActionButton10, flickerstreak@1: ActionButton11, flickerstreak@1: ActionButton12, flickerstreak@7: PetActionButton1, flickerstreak@7: PetActionButton2, flickerstreak@7: PetActionButton3, flickerstreak@7: PetActionButton4, flickerstreak@7: PetActionButton5, flickerstreak@7: PetActionButton6, flickerstreak@7: PetActionButton7, flickerstreak@7: PetActionButton8, flickerstreak@7: PetActionButton9, flickerstreak@7: PetActionButton10, flickerstreak@7: -- NOT the PetActionBarFrame, though - we need that to auto-hide/show our pet action bars flickerstreak@7: MainMenuBarPageNumber, flickerstreak@7: ActionBarUpButton, flickerstreak@7: ActionBarDownButton, flickerstreak@1: BonusActionBarFrame, flickerstreak@7: ShapeshiftBarFrame, flickerstreak@1: MultiBarLeft, flickerstreak@1: MultiBarRight, flickerstreak@1: MultiBarBottomLeft, flickerstreak@7: MultiBarBottomRight, flickerstreak@1: } flickerstreak@1: flickerstreak@7: function main:StealKeyBindings() flickerstreak@7: -- steal the keybindings of the main action bar and assign them to rebar 1, buttons 1-12 flickerstreak@7: for i = 1, 12 do flickerstreak@7: -- TODO: when we convert to override bindings flickerstreak@1: end flickerstreak@1: end flickerstreak@1: flickerstreak@7: local function disableUIOptions() flickerstreak@7: -- disable the buttons to hide/show the blizzard multiaction bars flickerstreak@7: -- see UIOptionsFrame.lua and .xml flickerstreak@7: -- This is called every time the options panel is shown, after it is set up flickerstreak@7: for _, idx in pairs( { 33, 34, 35, 36, 37, 40 } ) do flickerstreak@7: local f = getglobal("UIOptionsFrameCheckButton"..idx) flickerstreak@7: f.disabled = true flickerstreak@7: OptionsFrame_DisableCheckBox(f) flickerstreak@7: f:SetChecked(false) flickerstreak@7: end flickerstreak@7: end flickerstreak@1: flickerstreak@7: function main:HideDefaultBars() flickerstreak@7: for _, f in pairs(blizzDefaultBars) do flickerstreak@7: f:Hide() flickerstreak@7: f:ClearAllPoints() flickerstreak@7: f:SetParent(ReAction.recycler) flickerstreak@7: f:SetPoint("TOPLEFT") flickerstreak@7: end flickerstreak@7: flickerstreak@7: MainMenuBar:SetFrameStrata("LOW") -- otherwise it appears on top of bars, if it isn't hidden flickerstreak@7: hooksecurefunc("UIOptionsFrame_Load",disableUIOptions) flickerstreak@7: end flickerstreak@7: flickerstreak@7: flickerstreak@7: -- Reset bars to blizzard defaults flickerstreak@7: function main:ResetBars() flickerstreak@2: if InCombatLockdown() then flickerstreak@2: UIErrorsFrame:AddMessage(SPELL_FAILED_AFFECTING_COMBAT) flickerstreak@2: else flickerstreak@7: self:DestroyAllBars() flickerstreak@7: self.db.profile.bars = tcopy(ReAction_DefaultBlizzardBars) flickerstreak@2: self:SetupBars() flickerstreak@2: end flickerstreak@1: end flickerstreak@1: flickerstreak@1: flickerstreak@1: -- re-sync action IDs flickerstreak@7: function main:ResyncActionIDs() flickerstreak@1: -- TODO flickerstreak@1: end flickerstreak@1: flickerstreak@1: flickerstreak@1: flickerstreak@1: -- Bar manipulation flickerstreak@7: main.bars = { } flickerstreak@1: flickerstreak@7: function main:DestroyAllBars() flickerstreak@7: -- destroy any existing bars flickerstreak@7: for id = 1, table.maxn(self.bars) do flickerstreak@7: self:DestroyBar(id) flickerstreak@7: end flickerstreak@7: end flickerstreak@7: flickerstreak@7: flickerstreak@7: function main:SetupBars() flickerstreak@1: -- hide the default Blizzard art, if configued flickerstreak@1: self:HideArt() flickerstreak@1: flickerstreak@1: -- set up the bars from the profile flickerstreak@2: -- note the use of table.maxn rather than # or ipairs: flickerstreak@2: -- our array of bars can in fact contain holes flickerstreak@2: for id = 1, table.maxn(self.db.profile.bars) do flickerstreak@2: local config = self.db.profile.bars[id] flickerstreak@2: if config then flickerstreak@7: self:CreateBar(config, id) flickerstreak@2: end flickerstreak@1: end flickerstreak@1: flickerstreak@1: -- anchor the bars, have to do this in a second pass because flickerstreak@1: -- they might be anchored to each other in a non-ordered way flickerstreak@2: for _, bar in pairs(self.bars) do flickerstreak@7: if bar ~= EMPTY_BAR_SLOT then flickerstreak@7: bar:ApplyAnchor() flickerstreak@2: end flickerstreak@2: end flickerstreak@1: end flickerstreak@1: flickerstreak@7: function main:CreateBar( config, id ) flickerstreak@7: local bar = ReBar:new(config, id) flickerstreak@7: local buttonType = ReAction:GetButtonType(config.btnConfig.subtype) flickerstreak@1: flickerstreak@7: if buttonType then flickerstreak@7: self.bars[id] = bar flickerstreak@7: self.db.profile.bars[id] = config flickerstreak@7: flickerstreak@7: -- initialize dewdrop menu flickerstreak@7: local cf = bar:GetControlFrame() flickerstreak@7: dewdrop:Register(cf, flickerstreak@7: 'children', flickerstreak@7: function() flickerstreak@7: dewdrop:FeedAceOptionsTable(ReActionGlobalMenuOptions) flickerstreak@7: dewdrop:FeedAceOptionsTable(GenerateReActionBarOptions(bar,self)) flickerstreak@7: dewdrop:FeedAceOptionsTable(buttonType:GenerateOptionsTable(config.btnConfig, function() return bar:GetButtonList() end)) flickerstreak@7: end, flickerstreak@7: 'cursorX', true, flickerstreak@7: 'cursorY', true flickerstreak@7: ) flickerstreak@7: flickerstreak@7: bar:GetControlFrame():SetScript("OnClick", flickerstreak@7: function(btn) flickerstreak@7: if btn == "RightButton" then flickerstreak@7: dewdrop:Open(cf) flickerstreak@7: end flickerstreak@7: end flickerstreak@7: ) flickerstreak@7: flickerstreak@7: if not self.locked then flickerstreak@7: bar:ShowControls() flickerstreak@7: end flickerstreak@7: return bar flickerstreak@7: else flickerstreak@7: if bar then flickerstreak@7: bar:Destroy() flickerstreak@7: end flickerstreak@7: error(L["Tried to create a button of unknown type"]) flickerstreak@7: end flickerstreak@7: end flickerstreak@7: flickerstreak@7: function main:DestroyBar( id ) flickerstreak@7: local bar = self.bars[id] flickerstreak@7: if bar and bar ~= EMPTY_BAR_SLOT then flickerstreak@7: local cf = bar:GetControlFrame() flickerstreak@7: if cf == dewdrop:GetOpenedParent() then flickerstreak@7: dewdrop:Close() flickerstreak@7: dewdrop:Unregister(cf) flickerstreak@7: end flickerstreak@7: bar:Destroy() flickerstreak@7: -- we can't do tremove because each bar ID is encoded into the flickerstreak@7: -- frame names as they're created. Need a blank entry in the table. flickerstreak@7: -- The nice thing is that table.insert in NewBar() will automatically flickerstreak@7: -- find the lowest numbered nil slot. flickerstreak@7: self.bars[id] = EMPTY_BAR_SLOT flickerstreak@7: end flickerstreak@7: end flickerstreak@7: flickerstreak@7: function main:NewBar( type ) flickerstreak@7: if InCombatLockdown() then flickerstreak@7: UIErrorsFrame:AddMessage(SPELL_FAILED_AFFECTING_COMBAT) flickerstreak@7: else flickerstreak@7: local t = ReAction:GetButtonType(type) flickerstreak@7: if t then flickerstreak@7: local c = tcopy(ReAction_DefaultBarConfig["ReAction"][type]) flickerstreak@7: local id = nil flickerstreak@7: for i = 1, table.maxn(self.bars) + 1 do -- there may be holes, so #self.bars won't work flickerstreak@7: if self.bars[i] == nil or self.bars[i] == EMPTY_BAR_SLOT then flickerstreak@7: id = i flickerstreak@7: break flickerstreak@7: end flickerstreak@7: end flickerstreak@7: local bar = self:CreateBar(c, id) flickerstreak@7: bar:ApplyAnchor() flickerstreak@7: self:Unlock() flickerstreak@7: end flickerstreak@7: end flickerstreak@7: end flickerstreak@7: flickerstreak@7: flickerstreak@7: function main:DeleteBar(id) flickerstreak@2: if InCombatLockdown() then flickerstreak@2: UIErrorsFrame:AddMessage(SPELL_FAILED_AFFECTING_COMBAT) flickerstreak@2: else flickerstreak@2: if self.bars[id] then flickerstreak@7: self:DestroyBar(id) flickerstreak@2: self.db.profile.bars[id] = nil flickerstreak@2: end flickerstreak@1: end flickerstreak@1: end flickerstreak@1: flickerstreak@7: function main:ToggleIds() flickerstreak@7: if self.showIds then flickerstreak@7: ReAction:HideAllIds() flickerstreak@1: else flickerstreak@7: ReAction:ShowAllIds() flickerstreak@1: end flickerstreak@7: self.showIds = not self.showIds flickerstreak@1: end flickerstreak@1: flickerstreak@7: function main:AreIdsVisible() flickerstreak@7: return self.showIds flickerstreak@1: end flickerstreak@1: flickerstreak@1: flickerstreak@1: