Mercurial > wow > reaction
changeset 257:920d17851a93 stable
Merge 1.1 beta 4 to stable
author | Flick |
---|---|
date | Tue, 12 Apr 2011 16:06:31 -0700 |
parents | b2b105747466 (current diff) 586447595262 (diff) |
children | a7d2efb6ffc9 |
files | .hgtags classes/ActionButton.lua classes/BagButton.lua classes/Bar.lua classes/Button.lua classes/MultiCastButton.lua classes/Overlay.lua classes/PetActionButton.lua classes/StanceButton.lua classes/VehicleExitButton.lua classes/classes.xml modules/Action.lua modules/Bag.lua modules/HideBlizzard.lua modules/LBF.lua modules/ModuleTemplate.lua modules/PetAction.lua modules/Stance.lua modules/State.lua modules/Totem.lua modules/VehicleExit.lua modules/modules.xml |
diffstat | 39 files changed, 6534 insertions(+), 7213 deletions(-) [+] |
line wrap: on
line diff
--- a/.hgtags Thu Nov 18 13:11:08 2010 -0800 +++ b/.hgtags Tue Apr 12 16:06:31 2011 -0700 @@ -1,3 +1,4 @@ 2c05008464e5cf809251d40c8f88ad87fa3ac554 1.1 Beta 1 9945a783d8b29e6e81eb8d42823d56dc84759d15 1.1 Beta 2 3e451836ce6dfa758794c27a4b3a2a1f12595e83 1.1 Beta 3 +47818b3938c912c19018419cb5738d4013107167 1.1 beta 4
--- a/.pkgmeta Thu Nov 18 13:11:08 2010 -0800 +++ b/.pkgmeta Tue Apr 12 16:06:31 2011 -0700 @@ -4,10 +4,6 @@ - libdatabroker-1-1 - buttonfacade -optional-dependencies: - - ace3 - - libkeybound-1-0 - externals: lib/AceAddon-3.0: url: svn://svn.wowace.com/wow/ace3/mainline/trunk/AceAddon-3.0
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ActionButton.lua Tue Apr 12 16:06:31 2011 -0700 @@ -0,0 +1,806 @@ +local addonName, addonTable = ... +local ReAction = addonTable.ReAction +local L = ReAction.L +local _G = _G +local CreateFrame = CreateFrame +local format = string.format +local IsUsableAction = IsUsableAction +local IsEquippedAction = IsEquippedAction +local IsConsumableAction = IsConsumableAction +local IsStackableAction = IsStackableAction +local GetActionText = GetActionText +local GetCVar = GetCVar +local GameTooltip_SetDefaultAnchor = GameTooltip_SetDefaultAnchor +local IsCurrentAction = IsCurrentAction +local IsAutoRepeatAction = IsAutoRepeatAction +local IsUsableAction = IsUsableAction +local IsAttackAction = IsAttackAction +local CooldownFrame_SetTimer = CooldownFrame_SetTimer +local GetActionCooldown = GetActionCooldown +local GetActionTexture = GetActionTexture +local ATTACK_BUTTON_FLASH_TIME = ATTACK_BUTTON_FLASH_TIME +local TOOLTIP_UPDATE_TIME = TOOLTIP_UPDATE_TIME +local IsActionInRange = IsActionInRange +local InCombatLockdown = InCombatLockdown +local HasAction = HasAction + +-- +-- Secure snippets +-- These are run within the context of the bar's sandbox, as the +-- buttons themselves do not have their own sandbox. +-- +local _onstate_mc = -- function(self, stateid, newstate) +[[ + local oldMcVehicleState = mcVehicleState + mcVehicleState = newstate + control:ChildUpdate() + if oldMcVehicleState == "vehicle" or mcVehicleState == "vehicle" then + control:ChildUpdate("vehicle") + end +]] + +local _childupdate = -- function(self, snippetid, message) +[[ + local action = nil + if (doVehicle and mcVehicleState == "vehicle") or + (doMindControl and mcVehicleState == "mc") then + local idx = self:GetAttribute("bar-idx") + local maxN = (doVehicle and mcVehicleState == "vehicle") and 7 or 12 + if idx and idx <= maxN then + action = 120 + idx + else + action = 0 + end + elseif state and settings[state] and settings[state].page then + action = self:GetAttribute("action-"..settings[state].page) + end + if action == nil then + action = self:GetAttribute("default-action") + end + + self:SetAttribute("action",action) + + if not self:GetAttribute("showgrid") then + local tempShow = self:GetAttribute("showgrid-temp") + local evtShow = self:GetAttribute("showgrid-event") + if tempShow then tempShow = (tempShow > 0) end + if evtShow then evtShow = (evtShow > 0) end + + if tempShow or evtShow or HasAction(action) then + self:Show() + else + self:Hide() + end + end +]] + +local _childupdate_vehicleExit = -- function(self, snippetid, message) +[[ + local show = (mcVehicleState == "vehicle") + if show and doVehicle then + self:SetAttribute("type","macro") + self:Show() + else + self:SetAttribute("type","action") + end + control:CallMethod("ShowVehicleExit",show) +]] + +local _childupdate_showgrid = -- function(self, snippetid, message) +[[ + showgrid_event = message + self:SetAttribute("showgrid-event",message) + if not self:GetAttribute("showgrid") then + local count = message + (self:GetAttribute("showgrid-temp") or 0) + if count <= 0 then + local action = self:GetAttribute("action") + -- note that HasAction is not updated on the target of a drag until after ACTIONBAR_HIDEGRID + -- so, we set a flag for this button in pre-click/receive-drag and check for it here + if HasAction(action) or self:GetAttribute("showgrid-clicked") then + if not self:IsShown() then + self:Show() + end + self:SetAttribute("showgrid-clicked",false) + else + if self:IsShown() then + self:Hide() + end + end + else + self:Show() + end + end +]] + +local _onDragStart = -- function(self, button, kind, value, ...) +[[ + if lockButtons and (PlayerInCombat() or not lockButtonsCombat) and not IsModifiedClick("PICKUPACTION") then + return kind, value, ... + else + return "action", self:GetAttribute("action") + end +]] + +local _onReceiveDrag = -- function(self, button, kind, value, ...) +[[ + if kind ~= "spell" and kind ~= "item" and kind ~= "macro" and kind ~= "flyout" then + return kind, value, ... + else + if showgrid_event and showgrid_event > 0 then + self:SetAttribute("showgrid-clicked",true) + end + return "action", self:GetAttribute("action") + end +]] + +local _onClick = -- function(self, button, down) +[[ + if showgrid_event and showgrid_event > 0 then + self:SetAttribute("showgrid-clicked",true) + end + return button +]] + +-- +-- private +-- +local eventList = { + "PLAYER_ENTERING_WORLD", + "ACTIONBAR_PAGE_CHANGED", + "ACTIONBAR_SLOT_CHANGED", + "UPDATE_BINDINGS", + "ACTIONBAR_UPDATE_STATE", + "ACTIONBAR_UPDATE_USABLE", + "ACTIONBAR_UPDATE_COOLDOWN", + "UNIT_INVENTORY_CHANGED", + "LEARNED_SPELL_IN_TAB", + "UPDATE_INVENTORY_ALERTS", + "PLAYER_TARGET_CHANGED", + "TRADE_SKILL_SHOW", + "TRADE_SKILL_CLOSE", + "PLAYER_ENTER_COMBAT", + "PLAYER_LEAVE_COMBAT", + "START_AUTOREPEAT_SPELL", + "STOP_AUTOREPEAT_SPELL", + "UNIT_ENTERED_VEHICLE", + "UNIT_EXITED_VEHICLE", + "COMPANION_UPDATE", +} + +-- +-- Action Button class +-- +local buttonTypeID = "Action" +local Super = ReAction.Button +local Action = setmetatable( + { + defaultBarConfig = { + type = buttonTypeID, + btnWidth = 36, + btnHeight = 36, + btnRows = 1, + btnColumns = 12, + spacing = 3, + buttons = { }, + }, + + barType = L["Action Bar"], + buttonTypeID = buttonTypeID + }, + { __index = Super } ) + +ReAction.Button.Action = Action +ReAction:RegisterBarType(Action, true) + +function Action:New( config, bar, idx, idHint ) + local name = format("ReAction_%s_Action_%d",bar:GetName(),idx) + + self = Super.New(self, name, config, bar, idx, "SecureActionButtonTemplate, ActionButtonTemplate" ) + self.barConfig = bar:GetConfig() + + local f = self:GetFrame() + local barFrame = bar:GetFrame() + + self.rangeTimer = TOOLTIP_UPDATE_TIME + + -- set up the base action ID + self:SetActionIDPool("action",120) + config.actionID = self:AcquireActionID(config.actionID, idHint) + self.nPages = 1 + + -- attribute setup + f:SetAttribute("type","action") + f:SetAttribute("checkselfcast", true) + f:SetAttribute("checkfocuscast", true) + f:SetAttribute("action", config.actionID) + f:SetAttribute("default-action", config.actionID) + f:SetAttribute("bar-idx",idx) + f:SetAttribute("showgrid-temp",0) + f:SetAttribute("showgrid-event",0) + f:SetAttribute("showgrid",not self:GetBarConfig().hideEmpty) + + -- non secure scripts + f:SetScript("OnEvent", function(frame, ...) self:OnEvent(...) end) + f:SetScript("OnEnter", function(frame) self:OnEnter() end) + f:SetScript("OnLeave", function(frame) self:OnLeave() end) + f:SetScript("OnAttributeChanged", function(frame, attr, value) self:OnAttributeChanged(attr, value) end) + f:SetScript("PostClick", function(frame, ...) self:PostClick(...) end) + f:SetScript("OnUpdate", function(frame, elapsed) self:OnUpdate(elapsed) end) + f:SetScript("OnDragStart", function(frame) self:OnDragStart() end) + f:SetScript("OnReceiveDrag", function(frame) self:OnReceiveDrag() end) + + -- event registration + f:EnableMouse(true) + f:RegisterForDrag("LeftButton", "RightButton") + f:RegisterForClicks("AnyUp") + for _, evt in pairs(eventList) do + f:RegisterEvent(evt) + end + + f.action = config.actionID -- need this to support silly ActionButton_UpdateFlyout. Should not taint anything anymore. + + -- secure handlers + f:SetAttribute("_childupdate", _childupdate) + f:SetAttribute("_childupdate-showgrid",_childupdate_showgrid) + barFrame:WrapScript(f, "OnDragStart", _onDragStart) + barFrame:WrapScript(f, "OnReceiveDrag", _onReceiveDrag) + barFrame:WrapScript(f, "OnClick", _onClick) + + -- attach to skinner + bar:SkinButton(self) + + -- initial display + if ReAction:GetConfigMode() then + self:ShowGridTemp(true) + end + + self:Refresh() + + return self +end + +function Action:Destroy() + local f = self:GetFrame() + local c = self:GetConfig() + + f:SetAttribute("_childupdate-vehicle",nil) + f:SetAttribute("action",c.actionID) -- so that Super.Destroy releases the right one + + if c.pageactions and #c.pageactions > 1 then + for i = 2, #c.pageactions do + self:ReleaseActionID(c.pageactions[i]) + self:ReleaseActionID(id) + end + end + + Super.Destroy(self) +end + +function Action:GetBarConfig() + -- this is the per-bar Action module config structure, + -- not the config structure of the bar itself + return self.barConfig +end + +function Action:Refresh() + Super.Refresh(self) + self:RefreshPages() + self:InstallVehicle() + self:ShowGrid(not self:GetBarConfig().hideEmpty) + self:UpdateAction() +end + +function Action:InstallVehicle() + local f = self:GetFrame() + if self.idx == 7 and self:GetBarConfig().vehicle then + if not self.vehicleInstalled then + self.vehicleInstalled = true + -- install vehicle-exit button on 7th button (only) + f:SetAttribute("_childupdate-vehicle", _childupdate_vehicleExit) + f:SetAttribute("macrotext","/run VehicleExit()") + self:GetBar():GetFrame().ShowVehicleExit = function(bar,show) + self:ShowVehicleExit(show) + end + end + -- setscale blows away tex coords + self:UpdateIcon() + elseif self.vehicleInstalled then + self.vehicleInstalled = false + f:SetAttribute("_childupdate-vehicle",nil) + f:SetAttribute("macrotext",nil) + end +end + +function Action:ShowGrid( show ) + if not InCombatLockdown() then + self.frame:SetAttribute("showgrid", show) + self:UpdateShowGrid() + end +end + +function Action:ShowGridTemp( show ) + -- This function only modifies the show-grid when out + -- of combat, and is ignored by the secure handler. Use + -- it for configuration modes. + if not InCombatLockdown() then + local count = self.showGridTempCount or 0 + if show then + count = count + 1 + else + count = count - 1 + end + if count < 0 then count = 0 end + self.showGridTempCount = count + self:GetFrame():SetAttribute("showgrid-temp",count) + self:UpdateShowGrid() + end +end + +function Action:UpdateAll() + self:UpdateActionIDLabel(ReAction:GetConfigMode()) + self:UpdateHotkey() + self:UpdateShowGrid() + self:UpdateIcon() + self:UpdateBorder() + self:UpdateMacroText() + self:UpdateCount() + self:UpdateTooltip() + self:UpdateCheckedState() + self:UpdateUsable() + self:UpdateCooldown() + self:UpdateFlash() +end + +function Action:UpdateAction() + local action = self:GetActionID() + if action ~= self.actionID then + self.actionID = action + self:UpdateAll() + end +end + +function Action:UpdateShowGrid() + if not InCombatLockdown() then + local f = self:GetFrame() + local count = (f:GetAttribute("showgrid-event") or 0) + + (self.showGridTempCount or 0) + + (f:GetAttribute("showgrid") and 1 or 0) + + if count <= 0 and not HasAction(self.actionID) then + if f:IsShown() then + f:Hide() + end + elseif not f:IsShown() then + f:Show() + end + end +end + +function Action:UpdateIcon() + local texture, tLeft, tRight, tTop, tBottom = self:GetIconTexture() + local icon = self.frames.icon + local hotkey = self.frames.hotkey + local f = self:GetFrame() + + if self.vehicleExitMode then + texture = "Interface\\Vehicles\\UI-Vehicles-Button-Exit-Up" + icon:SetTexCoord(0.140625, 0.859375, 0.140625, 0.859375) + icon:SetVertexColor(1,1,1) + else + icon:SetTexCoord(0,1,0,1) + end + + if texture then + icon:SetTexture(texture) + if tLeft then + icon:SetTexCoord(tLeft,tRight,tTop,tBottom) + end + icon:Show() + self.rangeTimer = -1 + f:SetNormalTexture("Interface\\Buttons\\UI-Quickslot2") + else + icon:Hide() + self.frames.cooldown:Hide() + self.rangeTimer = nil + f:SetNormalTexture("Interface\\Buttons\\UI-Quickslot") + end +end + +function Action:GetIconTexture() + return GetActionTexture(self.actionID) +end + +function Action:UpdateBorder() + local action = self.actionID + if ReAction:GetKeybindMode() then + self:UpdateKeybindModeDisplay(true) + elseif IsEquippedAction(action) then + self.frames.border:SetVertexColor(0, 1.0, 0, 0.35) + self.frames.border:Show() + else + self.frames.border:Hide() + end +end + +function Action:UpdateMacroText() + local action = self.actionID + if not IsConsumableAction(action) and not IsStackableAction(action) then + self.frames.name:SetText(GetActionText(action)) + else + self.frames.name:SetText("") + end +end + +function Action:UpdateCount() + local action = self.actionID + if IsConsumableAction(action) or IsStackableAction(action) then + self.frames.count:SetText(GetActionCount(action)) + else + self.frames.count:SetText("") + end +end + +function Action:UpdateTooltip() + local f = self:GetFrame() + if GameTooltip:GetOwner() == f then + self:SetTooltip() + end +end + +function Action:SetTooltip() + local f = self:GetFrame() + if GetCVar("UberTooltips") == "1" then + GameTooltip_SetDefaultAnchor(GameTooltip, f) + else + GameTooltip:SetOwner(f,"ANCHOR_RIGHT") + end + GameTooltip:SetAction(self.actionID) +end + +function Action:UpdateCheckedState() + local action = self.actionID + if IsCurrentAction(action) or IsAutoRepeatAction(action) then + self:GetFrame():SetChecked(1) + else + self:GetFrame():SetChecked(0) + end +end + +function Action:UpdateUsable() + local isUsable, notEnoughMana = self:GetUsable() + local noRange = self:GetInRange() + + isUsable = self.vehicleExitMode or (isUsable and not noRange) + + if isUsable then + if self.usableStatus ~= "usable" then + self.frames.icon:SetVertexColor(1.0, 1.0, 1.0) + self.frames.normalTexture:SetVertexColor(1.0, 1.0, 1.0) + self.usableStatus = "usable" + end + elseif noRange then + if self.usableStatus ~= "norange" then + self.frames.icon:SetVertexColor(1.0,0.1,0.1) + self.frames.normalTexture:SetVertexColor(1.0, 1.0, 1.0) + self.usableStatus = "norange" + end + elseif notEnoughMana then + if self.usableStatus ~= "oom" then + self.frames.icon:SetVertexColor(0.5, 0.5, 1.0) + self.frames.normalTexture:SetVertexColor(0.5, 0.5, 1.0) + self.usableStatus = "oom" + end + else + if self.usableStatus ~= "unusable" then + self.frames.icon:SetVertexColor(0.4, 0.4, 0.4) + self.frames.normalTexture:SetVertexColor(1.0, 1.0, 1.0) + self.usableStatus = "unusable" + end + end +end + +function Action:GetUsable() + return IsUsableAction(self.actionID) +end + +function Action:GetInRange() + return IsActionInRange(self.actionID) == 0 +end + +function Action:UpdateCooldown() + CooldownFrame_SetTimer(self.frames.cooldown, self:GetCooldown()) +end + +function Action:GetCooldown() + return GetActionCooldown(self.actionID) +end + +function Action:UpdateFlash() + local action = self.actionID + self:SetFlash( (IsAttackAction(action) and IsCurrentAction(action)) or IsAutoRepeatAction(action) ) +end + +function Action:SetFlash(flash) + if self.flashing ~= flash then + self.flashing = flash + self.flashtime = 0 + if not flash then + self.frames.flash:Hide() + end + self:UpdateCheckedState() + end +end + +function Action:RunFlash(elapsed) + if self.flashing then + local flashtime = self.flashtime - elapsed + self.flashtime = flashtime + if flashtime <= 0 then + local overtime = -flashtime + if overtime >= ATTACK_BUTTON_FLASH_TIME then + overtime = 0 + end + flashtime = ATTACK_BUTTON_FLASH_TIME - overtime + local flash = self.frames.flash + if flash:IsShown() then + flash:Hide() + else + flash:Show() + end + end + end +end + +function Action:RunRangeFinder(elapsed) + local rangeTimer = self.rangeTimer + if rangeTimer then + rangeTimer = rangeTimer - elapsed + self.rangeTimer = rangeTimer + if rangeTimer <= 0 then + self:UpdateUsable() + self.rangeTimer = TOOLTIP_UPDATE_TIME + end + end +end + +function Action:GetActionID(page) + if page == nil then + -- get the effective ID + return self:GetFrame():GetAttribute("action") + else + if page == 1 then + return self.config.actionID + else + return self.config.pageactions and self.config.pageactions[page] or self.config.actionID + end + end +end + +function Action:SetActionID( id, page ) + id = tonumber(id) + page = tonumber(page) + if id == nil or id < 1 or id > 120 then + ReAction:UserError(L["Action ID range is 1-120"]) + return + end + if page and page ~= 1 then + if not self.config.pageactions then + self.config.pageactions = { } + end + self:ReleaseActionID(self.config.pageactions[page]) + self.config.pageactions[page] = id + self:AcquireActionID(self.config.pageactions[page]) + self.frame:SetAttribute("action-page"..page,id) + else + self:ReleaseActionID(self.config.actionID) + self.config.actionID = id + self:AcquireActionID(self.config.actionID) + self.frame:SetAttribute("action",id) + self.frame:SetAttribute("default-action",id) + if self.config.pageactions then + self.config.pageactions[1] = id + self.frame:SetAttribute("action-page1",id) + end + end +end + +function Action:RefreshPages( force ) + local nPages = self:GetBarConfig().nPages + if nPages and (nPages ~= self.nPages or force) then + local f = self:GetFrame() + local c = self.config.pageactions + if nPages > 1 and not c then + c = { } + self.config.pageactions = c + end + for i = 1, nPages do + if i > 1 then + c[i] = self:AcquireActionID(c[i], self.config.actionID + (i-1)*self.bar:GetNumButtons()) + else + c[i] = self.config.actionID -- page 1 is the same as the base actionID + end + f:SetAttribute("action-page"..i,c[i]) + end + for i = nPages+1, #c do + self:ReleaseActionID(c[i]) + c[i] = nil + f:SetAttribute("action-page"..i,nil) + end + self.nPages = nPages + end +end + +function Action:SetupBar( bar ) + Super.SetupBar(self,bar) + + local f = bar:GetFrame() + local config = bar:GetConfig() + f:SetAttribute("mindcontrol",config.mindcontrol) + f:SetAttribute("vehicle",config.vehicle) + f:Execute( + [[ + doMindControl = self:GetAttribute("mindcontrol") + doVehicle = self:GetAttribute("vehicle") + control:ChildUpdate() + ]]) + + f:SetAttribute("_onstate-mc", _onstate_mc) + RegisterStateDriver(f, "mc", "[vehicleui] vehicle; [bonusbar:5] mc; none") + + f:SetAttribute("lockbuttons",config.lockButtons) + f:SetAttribute("lockbuttonscombat",config.lockButtonsCombat) + f:Execute( + [[ + lockButtons = self:GetAttribute("lockbuttons") + lockButtonsCombat = self:GetAttribute("lockbuttonscombat") + ]]) +end + + +function Action:SetButtonLock( bar, lock, lockCombat ) + local f = bar:GetFrame() + f:SetAttribute("lockbuttons",lock) + f:SetAttribute("lockbuttonscombat",lockCombat) + f:Execute( + [[ + lockButtons = self:GetAttribute("lockbuttons") + lockButtonsCombat = self:GetAttribute("lockbuttonscombat") + ]]) +end + + +function Action:ShowVehicleExit(show) + self.vehicleExitMode = show and self:GetBarConfig().vehicle + self:UpdateIcon() +end + +function Action:OnEnter( ) + self:SetTooltip() +end + +function Action:OnLeave( ) + GameTooltip:Hide() +end + +function Action:OnAttributeChanged( attr, value ) + if attr ~= "statehidden" then + self:UpdateAction() + end + local f = self:GetFrame() + f.action = f:GetAttribute("action") -- support ActionButton_UpdateFlyout +end + +function Action:PostClick( ) + self:UpdateCheckedState() +end + +function Action:OnUpdate( elapsed ) + self:RunFlash(elapsed) + self:RunRangeFinder(elapsed) +end + +function Action:OnDragStart() + self:UpdateCheckedState() + self:UpdateFlash() +end + +function Action:OnReceiveDrag() + self:UpdateCheckedState() + self:UpdateFlash() +end + +function Action:OnEvent(event, ...) + if self[event] then + self[event](self, event, ...) + end +end + +function Action:ACTIONBAR_SLOT_CHANGED(event, action) + if action == 0 or action == self.actionID then + self:UpdateAll() + end +end + +function Action:PLAYER_ENTERING_WORLD() + self:UpdateAction() +end + +function Action:ACTIONBAR_PAGE_CHANGED() + self:UpdateAction() +end + +function Action:UPDATE_BONUS_ACTIONBAR() + self:UpdateAction() +end + +function Action:UPDATE_BINDINGS() + self:UpdateHotkey() +end + +function Action:PLAYER_TARGET_CHANGED() + self.rangeTimer = -1 +end + +function Action:ACTIONBAR_UPDATE_STATE() + self:UpdateCheckedState() +end + +function Action:TRADE_SKILL_SHOW() + self:UpdateCheckedState() +end +Action.TRADE_SKILL_CLOSE = Action.TRADE_SKILL_CLOSE + +function Action:UNIT_ENTERED_VEHICLE(event,unit) + if unit == "player" then + self:UpdateCheckedState() + end +end +Action.UNIT_EXITED_VEHICLE = Action.UNIT_ENTERED_VEHICLE + +function Action:COMPANION_UPDATE(event,unit) + if unit == "mount" then + self:UpdateCheckedState() + end +end + +function Action:ACTIONBAR_UPDATE_USABLE() + self:UpdateUsable() +end + +function Action:ACTIONBAR_UPDATE_COOLDOWN() + self:UpdateCooldown() +end + +function Action:PLAYER_ENTER_COMBAT() + if IsAttackAction(self.actionID) then + self:SetFlash(true) + end +end + +function Action:PLAYER_LEAVE_COMBAT() + if IsAttackAction(self.actionID) then + self:SetFlash(false) + end +end + +function Action:START_AUTOREPEAT_SPELL() + if IsAutoRepeatAction(self.actionID) then + self:SetFlash(true) + end +end + +function Action:STOP_AUTOREPEAT_SPELL() + if not IsAttackAction(self.actionID) then + self:SetFlash(false) + end +end + +function Action:UNIT_INVENTORY_CHANGED(unit) + if unit == "player" then + self:UpdateTooltip() + end +end + +function Action:LEARNED_SPELL_IN_TAB() + self:UpdateTooltip() +end
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/BagButton.lua Tue Apr 12 16:06:31 2011 -0700 @@ -0,0 +1,499 @@ +local addonName, addonTable = ... +local ReAction = addonTable.ReAction +local L = ReAction.L +local _G = _G +local CreateFrame = CreateFrame +local format = string.format +local GetCVar = GetCVar +local ContainerIDToInventoryID = ContainerIDToInventoryID +local NUM_CONTAINER_FRAMES = NUM_CONTAINER_FRAMES +local IsModifiedClick = IsModifiedClick +local CursorHasItem = CursorHasItem +local GetInventoryItemTexture = GetInventoryItemTexture +local GetInventorySlotInfo = GetInventorySlotInfo +local PickupBagFromSlot = PickupBagFromSlot +local CursorCanGoInSlot = CursorCanGoInSlot + +-- class declarations +local buttonTypeID = "Bag" +local weak = { __mode = "k" } +local Super = ReAction.Button +local BagBase = setmetatable( + { + defaultBarConfig = { + type = buttonTypeID, + btnWidth = 30, + btnHeight = 30, + btnRows = 1, + btnColumns = 6, + spacing = 4, + buttons = { } + }, + + barType = L["Bag Bar"], + buttonTypeID = buttonTypeID, + + allButtons = setmetatable( { }, weak ) + }, + { __index = Super } ) + +local Bag = setmetatable( { }, { __index = BagBase } ) +local Backpack = setmetatable( { }, { __index = BagBase } ) +local Keyring = setmetatable( { }, { __index = BagBase } ) + +ReAction.Button.Bag = BagBase +ReAction:RegisterBarType(BagBase) + +-- +-- Bag Button base class +-- + +function BagBase:New( btnCfg, bar, idx, idHint ) + local name = format("ReAction_%s_Bag_%d",bar:GetName(),idx) + + -- use a variable private leaf implementation class + -- unlike traditional OO programming, we can initialize the leaf + -- class before initializing its parent + local class = Bag + if idx == 1 then + class = Backpack + elseif idx == 6 then + class = Keyring + end + self = class:New(name, btnCfg, bar, idx) + + local f = self:GetFrame() + local config = self:GetConfig() + + -- set up the bag ID pool + self:SetActionIDPool("bag",6) + config.bagID = self:AcquireActionID(config.bagID, idHint, true) + + -- non secure scripts + f:SetScript("OnEvent", function(frame, ...) self:OnEvent(...) end) + f:SetScript("OnEnter", function(frame) self:OnEnter() end) + f:SetScript("OnLeave", function(frame) self:OnLeave() end) + f:SetScript("OnReceiveDrag", function(frame, ...) self:OnReceiveDrag(...) end) + f:SetScript("OnClick", function(frame, ...) self:OnClick(...) end) + + -- secure handlers + -- (none) + + -- event registration + f:EnableMouse(true) + f:RegisterForClicks("LeftButtonUp","RightButtonUp") + f:RegisterEvent("UPDATE_BINDINGS") + + -- frame setup + f:SetID(self:GetBagID()) + + if not f.hotkey then + local h = f:CreateFontString(name.."HotKey","ARTWORK","NumberFontNormalSmallGray") + h:SetWidth(30) + h:SetHeight(10) + h:SetJustifyH("RIGHT") + h:SetPoint("TOPLEFT",f,"TOPLEFT",-2,-2) + h:Show() + f.hotkey = h + end + + if not _G[name.."ItemAnim"] then + local anim = CreateFrame("Model",name.."ItemAnim",f,"ItemAnimTemplate") + anim:SetPoint("BOTTOMRIGHT",f,"BOTTOMRIGHT",-10,0) + anim:Hide() + end + + if not f.border then + local b = f:CreateTexture(name.."Border","OVERLAY") + b:SetAllPoints() + b:SetWidth(f:GetWidth()*(62/36)) + b:SetHeight(f:GetHeight()*(62/36)) + b:SetTexture("Interface\\Buttons\UI-ActionButton-Border") + b:SetBlendMode("ADD") + b:Hide() + f.border = b + end + + self.frames.count:SetDrawLayer("ARTWORK") + + self.frames.hotkey = f.hotkey + self.frames.border = _G[name.."Border"] + self.frames.icon = _G[name.."IconTexture"] + self.frames.anim = _G[name.."ItemAnim"] + + -- initial display + if ReAction:GetConfigMode() then + self:GetFrame():Show() + end + + self:Refresh() + + BagBase.allButtons[self] = true + + return self +end + +function BagBase:Destroy() + BagBase.allButtons[self] = nil + Super.Destroy(self) +end + + +function BagBase:GetActionID() + return self.config.bagID +end + +function BagBase:GetBagID() + return self:GetActionID() - 1 +end + +function BagBase:Refresh() + Super.Refresh(self) + self:UpdateHotkey() + self:Update() +end + +function BagBase:Update() + self:UpdateChecked() +end + +function BagBase:UpdateChecked(force) + if force == nil then + for i=1, NUM_CONTAINER_FRAMES do + local c = _G["ContainerFrame"..i] + if c:GetID() == self:GetBagID() and c:IsShown() then + self:GetFrame():SetChecked(1) + return + end + end + self:GetFrame():SetChecked(0) + end + self:GetFrame():SetChecked(force) +end + +function BagBase:OnEvent(evt, ...) + if self[evt] then + self[evt](self, ...) + end +end + +function BagBase:OnEnter() + self:SetTooltip() +end + +function BagBase:OnLeave() + GameTooltip:Hide() +end + +function BagBase:UPDATE_BINDINGS() + self:UpdateHotkey() +end + +function BagBase:IterateAllButtons() + return pairs(self.allButtons) +end + + +-- +-- Bag Button class +-- +function Bag:New(name, cfg, bar, idx) + self = Super.New(self, name, cfg, bar, idx, "ItemButtonTemplate" ) + + local f = self:GetFrame() + + f:SetCheckedTexture("Interface\\Buttons\\CheckButtonHilight") + + f:RegisterEvent("CURSOR_UPDATE") + f:RegisterEvent("BAG_UPDATE") + f:RegisterEvent("BAG_CLOSED") + f:SetScript("OnDragStart", function(frame, ...) self:OnDragStart(...) end) + f:RegisterForDrag("LeftButton") + + -- attach to skinner + bar:SkinButton(self, + { + Icon = _G[name.."IconTexture"] + } + ) + + return self +end + +function Bag:GetInventorySlot() + return ContainerIDToInventoryID(self:GetBagID()) +end + +function Bag:GetInventorySlotName() + return "Bag"..(self:GetBagID()-1).."Slot" +end + +function Bag:SetTooltip() + GameTooltip:SetOwner(self:GetFrame(), "ANCHOR_LEFT") + if not GameTooltip:SetInventoryItem("player", self:GetInventorySlot()) then + GameTooltip:SetText(EQUIP_CONTAINER, 1.0, 1.0, 1.0) + end +end + +function Bag:Update() + local texture = GetInventoryItemTexture("player", self:GetInventorySlot()) + if texture then + self.frames.icon:SetTexture(texture) + self.frames.icon:Show() + self:GetFrame():SetNormalTexture("Interface\\Buttons\\UI-Quickslot2") + else + local _, bgTex = GetInventorySlotInfo(self:GetInventorySlotName()) + self.frames.icon:SetTexture(bgTex) + self:GetFrame():SetNormalTexture("Interface\\Buttons\\UI-Quickslot") + end + self:UpdateChecked() +end + +function Bag:OnClick() + if IsModifiedClick("OPENALLBAGS") then + OpenAllBags() + else + if not PutItemInBag(self:GetInventorySlot()) then + ToggleBag(self:GetBagID()) + end + end + self:UpdateChecked() +end + +function Bag:OnReceiveDrag() + if CursorHasItem() then + PutItemInBag(self:GetInventorySlot()) + end +end + +function Bag:OnDragStart() + PickupBagFromSlot(self:GetInventorySlot()) + self:Update() +end + +function Bag:BAG_UPDATE(bag) + if bag == self:GetBagID() then + self:Update() + end +end + +function Bag:CURSOR_UPDATE() + if CursorCanGoInSlot(self:GetInventorySlot()) then + self:GetFrame():LockHighlight() + else + self:GetFrame():UnlockHighlight() + end +end + +function Bag:BAG_CLOSED(bag) + if bag == self:GetBagID() then + self:Update() + end +end + + +-- +-- Backpack Button class +-- +function Backpack:New(name, cfg, bar, idx) + self = Super.New(self, name, cfg, bar, idx, "ItemButtonTemplate" ) + + local f = self:GetFrame() + local icon = _G[name.."IconTexture"] + icon:SetTexture("Interface\\Buttons\\Button-Backpack-Up") + icon:Show() + f:SetCheckedTexture("Interface\\Buttons\\CheckButtonHilight") + f:RegisterEvent("PLAYER_ENTERING_WORLD"); + f:RegisterEvent("CVAR_UPDATE"); + f:SetScript("OnShow", function(frame, ...) self:OnShow(...) end) + + -- attach to skinner + bar:SkinButton(self, + { + Icon = _G[name.."IconTexture"] + } + ) + + return self +end + +function Backpack:Update() + self:UpdateFreeSlots() + self:UpdateChecked() +end + +function Backpack:UpdateFreeSlots() + if GetCVar("displayFreeBagSlots") == "1" then + local total = 0 + for i = BACKPACK_CONTAINER, NUM_BAG_SLOTS do + local free, family = GetContainerNumFreeSlots(i) + if family == 0 then + total = total + free + end + end + + self.freeSlots = total + self.frames.count:SetText(format("(%s)", self.freeSlots)) + self.frames.count:Show() + elseif self.frames.count:IsShown() then + self.frames.count:Hide() + end +end + +function Backpack:SetTooltip() + GameTooltip:SetOwner(self:GetFrame(), "ANCHOR_LEFT") + GameTooltip:SetText(BACKPACK_TOOLTIP, 1.0, 1.0, 1.0) + GameTooltip:AddLine(string.format(NUM_FREE_SLOTS, (self.freeSlots or 0))) + GameTooltip:Show(); +end + +function Backpack:OnShow() + self:UpdateFreeSlots() +end + +function Backpack:OnClick() + if IsModifiedClick("OPENALLBAGS") then + OpenAllBags() + else + if not PutItemInBackpack() then + ToggleBackpack() + end + end + self:UpdateChecked() +end + +function Backpack:OnReceiveDrag() + if CursorHasItem() then + PutItemInBackpack() + end +end + +function Backpack:PLAYER_ENTERING_WORLD() + self:CVAR_UPDATE("DISPLAY_FREE_BAG_SLOTS", GetCVar("displayFreeBagSlots")) +end + +function Backpack:CVAR_UPDATE( cvar, value ) + if cvar == "DISPLAY_FREE_BAG_SLOTS" then + if value == "1" then + self:GetFrame():RegisterEvent("BAG_UPDATE") + else + self:GetFrame():UnregisterEvent("BAG_UPDATE") + end + self:UpdateFreeSlots() + end +end + +function Backpack:BAG_UPDATE(bag) + if bag >= BACKPACK_CONTAINER and bag <= NUM_BAG_SLOTS then + self:UpdateFreeSlots() + end +end + + +-- +-- Keyring Button class +-- +function Keyring:New(name, cfg, bar, idx) + self = Super.New(self, name, cfg, bar, idx, "ItemButtonTemplate" ) + + local f = self:GetFrame() + + f:SetWidth(18) + f:SetHeight(39) + + local tex = f:GetNormalTexture() + tex:ClearAllPoints() + tex:SetAllPoints() + + f:SetNormalTexture("Interface\\Buttons\\UI-Button-KeyRing") + f:SetHighlightTexture("Interface\\Buttons\\UI-Button-KeyRing-Highlight") + f:SetPushedTexture("Interface\\Buttons\\UI-Button-KeyRing-Down") + f:GetNormalTexture():SetTexCoord(0,0.5625,0,0.609375) + f:GetHighlightTexture():SetTexCoord(0,0.5625,0,0.609375) + f:GetPushedTexture():SetTexCoord(0,0.5625,0,0.609375) + + if not HasKey() then + f:Hide() + end + + -- DO NOT attach to skinner + + return self +end + +function Keyring:GetBagID() + return KEYRING_CONTAINER +end + +function Keyring:Refresh() + local f = self:GetFrame() + self.bar:PlaceButton( self, f:GetHeight(), f:GetHeight() ) -- use height x height since it's an odd size + self:UpdateHotkey() + self:Update() +end + +function Keyring:SetTooltip() + GameTooltip:SetOwner(self:GetFrame(), "ANCHOR_RIGHT"); + GameTooltip:SetText(KEYRING, HIGHLIGHT_FONT_COLOR.r, HIGHLIGHT_FONT_COLOR.g, HIGHLIGHT_FONT_COLOR.b); + GameTooltip:AddLine(); +end + +function Keyring:OnReceiveDrag() + if CursorHasItem() then + PutKeyInKeyRing() + end +end + +function Keyring:OnClick() + if CursorHasItem() then + PutKeyInKeyRing() + else + ToggleKeyRing() + end + self:UpdateChecked() +end + +function Keyring:ShowGridTemp(show) + if not HasKey() then + if show then + self:GetFrame():Show() + else + self:GetFrame():Hide() + end + end +end + + + +-- hook some functions to propagate to our bag buttons +hooksecurefunc("Disable_BagButtons", + function() + for b in BagBase:IterateAllButtons() do + local f = b:GetFrame() + f:Disable() + SetDesaturation(b.frames.icon,1) + end + end) + +hooksecurefunc("Enable_BagButtons", + function() + for b in BagBase:IterateAllButtons() do + local f = b:GetFrame() + f:Enable() + SetDesaturation(b.frames.icon,nil) + end + end) + +hooksecurefunc("ContainerFrame_OnHide", + function() + for b in BagBase:IterateAllButtons() do + b:Update() + end + end) + +hooksecurefunc("ContainerFrame_OnShow", + function() + for b in BagBase:IterateAllButtons() do + b:Update() + end + end)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Bar.lua Tue Apr 12 16:06:31 2011 -0700 @@ -0,0 +1,854 @@ +local addonName, addonTable = ... +local ReAction = addonTable.ReAction +local L = ReAction.L +local LKB = ReAction.LKB +local _G = _G +local CreateFrame = CreateFrame +local floor = math.floor +local fmod = math.fmod +local format = string.format +local tfetch = addonTable.tfetch +local tbuild = addonTable.tbuild +local fieldsort = addonTable.fieldsort + +local LSG = LibStub("ReAction-LibShowActionGrid-1.0") + +---- Secure snippets ---- +local _reaction_init = +[[ + anchorKeys = newtable("point","relPoint","x","y") + + state = nil + set_state = nil + state_override = nil + unit_exists = nil + + showAll = false + hidden = false + + defaultAlpha = 1.0 + defaultScale = 1.0 + defaultAnchor = newtable() + + activeStates = newtable() + settings = newtable() + extensions = newtable() +]] + +local _reaction_refresh = +[[ + local oldState = state + state = state_override or set_state or state + + local hide = nil + if state then + local settings = settings[state] + if settings then + -- show/hide + hide = settings.hide + -- re-anchor + local old_anchor = activeStates.anchor + activeStates.anchor = settings.anchorEnable and state + if old_anchor ~= activeStates.anchor or not set_state then + if activeStates.anchor then + if settings.anchorPoint then + self:ClearAllPoints() + local f = self:GetAttribute("frameref-anchor-"..state) + if f then + self:SetPoint(settings.anchorPoint, f, settings.anchorRelPoint, settings.anchorX, settings.anchorY) + end + end + elseif defaultAnchor.point then + self:ClearAllPoints() + self:SetPoint(defaultAnchor.point, defaultAnchor.frame, + defaultAnchor.relPoint, defaultAnchor.x, defaultAnchor.y) + end + end + -- re-scale + local old_scale = activeStates.scale + activeStates.scale = settings.enableScale and state + if old_scale ~= activeStates.scale or not set_state then + self:SetScale(activeStates.scale and settings.scale or defaultScale) + end + -- alpha + local old_alpha = activeStates.alpha + activeStates.alpha = settings.enableAlpha and state + if old_alpha ~= activeStates.alpha or not set_state then + self:SetAlpha(activeStates.alpha and settings.alpha or defaultAlpha) + end + end + end + + -- hide if state or unit_exists says to + hide = not showAll and (hide or unithide) + if hide ~= hidden then + hidden = hide + if hide then + self:Hide() + else + self:Show() + end + end + + for _, attr in pairs(extensions) do + control:RunAttribute(attr) + end + + control:ChildUpdate() + + if showAll then + control:CallMethod("UpdateHiddenLabel", state and settings[state] and settings[state].hide) + end + + if oldState ~= state then + control:CallMethod("StateRefresh", state) + end +]] + +local _onstate_reaction = -- function( self, stateid, newstate ) +[[ + set_state = newstate +]] .. _reaction_refresh + +local _onstate_showgrid = -- function( self, stateid, newstate ) +[[ + control:ChildUpdate(stateid,newstate) + control:CallMethod("UpdateShowGrid") +]] + +local _onstate_unitexists = -- function( self, stateid, newstate ) +[[ + unithide = not newstate or newstate == "hide" +]] .. _reaction_refresh + +local _onclick = -- function( self, button, down ) +[[ + if state_override == button then + state_override = nil -- toggle + else + state_override = button + end +]] .. _reaction_refresh + +-- For reference +-- the option field names must match the field names of the options table, below +local stateProperties = { + hide = true, + --keybindState = true, TODO: broken + anchorEnable = true, + anchorFrame = true, + anchorPoint = true, + anchorRelPoint = true, + anchorX = true, + anchorY = true, + enableScale = true, + scale = true, + enableAlpha = true, + alpha = true, +} + + + +---- Bar class ---- +local Bar = { } +local frameList = { } + +ReAction.Bar = Bar -- export to ReAction + +function Bar:New( name, config, buttonClass ) + if type(config) ~= "table" then + error("ReAction.Bar: config table required") + end + + -- create new self + self = setmetatable( + { + config = config, + name = name, + buttons = { }, + buttonClass = buttonClass, + width = config.width or 480, + height = config.height or 40, + }, + {__index = self} ) + + -- The frame type is 'Button' in order to have an OnClick handler. However, the frame itself is + -- not mouse-clickable by the user. + local parent = config.parent and (ReAction:GetBar(config.parent) or _G[config.parent]) or UIParent + name = name and "ReAction-"..name + local f = name and frameList[name] + if not f then + f = CreateFrame("Button", name, parent, "SecureHandlerStateTemplate, SecureHandlerClickTemplate") + if name then + frameList[name] = f + end + end + f:SetFrameStrata("MEDIUM") + f:SetWidth(self.width) + f:SetHeight(self.height) + f:SetAlpha(config.alpha or 1.0) + f:Show() + f:EnableMouse(false) + f:SetClampedToScreen(true) + LSG:AddFrame(f) + + -- secure handlers + f:Execute(_reaction_init) + f:SetAttribute("_onstate-reaction", _onstate_reaction) + f:SetAttribute("_onstate-showgrid", _onstate_showgrid) + f:SetAttribute("_onstate-unitexists", _onstate_unitexists) + f:SetAttribute("_onclick", _onclick) + + -- secure handler CallMethod()s + f.UpdateShowGrid = function() self:UpdateShowGrid() end + f.StateRefresh = function() self:RefreshControls() end + f.UpdateHiddenLabel = function(f,hidden) self:SetLabelSubtext(hidden and L["Hidden"]) end + + -- Override the default frame accessor to provide strict read-only access + function self:GetFrame() + return f + end + + self:ApplyAnchor() + self:SetConfigMode(ReAction:GetConfigMode()) + self:SetKeybindMode(ReAction:GetKeybindMode()) + + if ReAction.LBF then + local g = ReAction.LBF:Group(L["ReAction"], self.name) + self.config.ButtonFacade = self.config.ButtonFacade or { + skinID = "Blizzard", + backdrop = true, + gloss = 0, + colors = {}, + } + local c = self.config.ButtonFacade + g:Skin(c.skinID, c.gloss, c.backdrop, c.colors) + self.LBFGroup = g + end + + ReAction.RegisterCallback(self, "OnConfigModeChanged") + + buttonClass:SetupBar(self) + self:ApplyStates() + + return self +end + +function Bar:Destroy() + local f = self:GetFrame() + self:CleanupStates() + for idx, b in self:IterateButtons() do + b:Destroy() + end + f:UnregisterAllEvents() + self:ShowControls(false) + ReAction.UnregisterAllCallbacks(self) + LKB.UnregisterAllCallbacks(self) + if self.LBFGroup then + self.LBFGroup:Delete(true) + end + LSG:RemoveFrame(f) + f:SetParent(UIParent) + f:ClearAllPoints() + f:Hide() +end + +-- +-- Events +-- + +function Bar:OnConfigModeChanged(event, mode) + self:SetConfigMode(mode) +end + +-- +-- Accessors +-- + +function Bar:GetName() + return self.name +end + +-- only ReAction:RenameBar() should call this function. Calling from any other +-- context will desync the bar list in the ReAction class. +function Bar:SetName(name) + if self.LBFGroup then + -- LBF doesn't offer a method of renaming a group, so delete and remake the group. + local c = self.config.ButtonFacade + local g = ReAction.LBF:Group(L["ReAction"], name) + for idx, b in self:IterateButtons() do + self.LBFGroup:RemoveButton(b:GetFrame(), true) + g:AddButton(b:GetFrame()) + end + self.LBFGroup:Delete(true) + self.LBFGroup = g + self.LBFGroup:Skin(c.skinID, c.gloss, c.backdrop, c.colors) + end + self.name = name + if self.overlay then + self.overlay:SetLabel(self.name) + end +end + +function Bar:GetFrame() + -- this method is included for documentation purposes. It is overridden + -- for each object in the :New() method. + error("Invalid Bar object: used without initialization") +end + +function Bar:GetButton(idx) + return self.buttons[idx] +end + +function Bar:GetButtonClass() + return self.buttonClass +end + +function Bar:GetConfig() + return self.config +end + +function Bar:GetAnchor() + local c = self.config + return (c.point or "CENTER"), + (c.anchor or self:GetFrame():GetParent():GetName()), + (c.relpoint or c.point or "CENTER"), + (c.x or 0), + (c.y or 0) +end + +function Bar:SetAnchor(point, frame, relativePoint, x, y) + local c = self.config + c.point = point or c.point + c.anchor = frame or c.anchor + c.relpoint = relativePoint or c.relpoint + c.x = x or c.x + c.y = y or c.y + self:ApplyAnchor() +end + +function Bar:GetSize() + local f = self:GetFrame() + return f:GetWidth(), f:GetHeight() +end + +function Bar:SetSize(w,h) + local f = self:GetFrame() + self.config.width = w + self.config.height = h + f:SetWidth(w) + f:SetHeight(h) +end + +function Bar:GetButtonSize() + local w = self.config.btnWidth or 32 + local h = self.config.btnHeight or 32 + -- TODO: get from modules? + return w,h +end + +function Bar:SetButtonSize(w,h) + if w > 0 and h > 0 then + self.config.btnWidth = w + self.config.btnHeight = h + end +end + +function Bar:GetNumButtons() + local r,c = self:GetButtonGrid() + return r*c +end + +function Bar:GetButtonGrid() + local cfg = self.config + local r = cfg.btnRows or 1 + local c = cfg.btnColumns or 1 + local s = cfg.spacing or 4 + return r,c,s +end + +function Bar:SetButtonGrid(r,c,s) + if r > 0 and c > 0 and s > 0 then + local cfg = self.config + cfg.btnRows = r + cfg.btnColumns = c + cfg.spacing = s + end + self.buttonClass:SetupBar(self) +end + +function Bar:GetAlpha() + return self.config.alpha or 1.0 +end + +function Bar:SetAlpha(value) + self.config.alpha = value + self:GetFrame():SetAlpha(value or 1.0) + self:UpdateDefaultStateAlpha() +end + +function Bar:IterateButtons() + -- iterator returns idx, button, but does NOT iterate in index order + return pairs(self.buttons) +end + +-- +-- Methods +-- + +function Bar:SetConfigMode(mode) + self:SetSecureData("showAll",mode) + self:ShowControls(mode) + for idx, b in self:IterateButtons() do + b:ShowGridTemp(mode) + b:UpdateActionIDLabel(mode) + end +end + +function Bar:SetKeybindMode(mode) + self:SetSecureData("showAll",mode) + for idx, b in self:IterateButtons() do + b:SetKeybindMode(mode) + end +end + +function Bar:ApplyAnchor() + local f = self:GetFrame() + local c = self.config + local p = c.point + + f:SetWidth(c.width) + f:SetHeight(c.height) + f:ClearAllPoints() + + if p then + local a = f:GetParent() + if c.anchor then + local bar = ReAction:GetBar(c.anchor) + if bar then + a = bar:GetFrame() + else + a = _G[c.anchor] + end + end + local fr = a or f:GetParent() + f:SetPoint(p, a or f:GetParent(), c.relpoint, c.x or 0, c.y or 0) + else + f:SetPoint("CENTER") + end + + self:UpdateDefaultStateAnchor() +end + +function Bar:ClipNButtons( n ) + local cfg = self.config + local r = cfg.btnRows or 1 + local c = cfg.btnColumns or 1 + + cfg.btnRows = ceil(n/c) + cfg.btnColumns = min(n,c) +end + +function Bar:AddButton(idx, button) + local f = self:GetFrame() + + self.buttons[idx] = button + + -- Store a properly wrapped reference to the child frame as an attribute + -- (accessible via "frameref-btn#") + f:SetFrameRef(format("btn%d",idx), button:GetFrame()) + + -- button constructors are responsible for calling SkinButton +end + +function Bar:RemoveButton(button) + local idx = button:GetIndex() + if idx then + self:GetFrame():SetAttribute(format("frameref-btn%d",idx),nil) + self.buttons[idx] = nil + end + if self.LBFGroup then + self.LBFGroup:RemoveButton(button:GetFrame(),true) + end +end + +function Bar:PlaceButton(button, baseW, baseH) + local idx = button:GetIndex() + if idx then + local r, c, s = self:GetButtonGrid() + local bh, bw = self:GetButtonSize() + local row, col = floor((idx-1)/c), fmod((idx-1),c) -- zero-based + local x, y = col*bw + (col+0.5)*s, -(row*bh + (row+0.5)*s) + local scale = bw/baseW + local b = button:GetFrame() + + b:ClearAllPoints() + b:SetPoint("TOPLEFT",x/scale,y/scale) + b:SetScale(scale) + end +end + +function Bar:SkinButton( button, data ) + if self.LBFGroup then + self.LBFGroup:AddButton(button:GetFrame(), data) + end +end + +function Bar:UpdateShowGrid() + for idx, button in self:IterateButtons() do + button:UpdateShowGrid() + end +end + +function Bar:ShowControls(show) + if show then + if not self.overlay then + self.overlay = Bar.Overlay:New(self) -- see Overlay.lua + end + self.overlay:Show() + self:RefreshSecureState() + elseif self.overlay then + self.overlay:Hide() + end +end + +function Bar:RefreshControls() + if self.overlay and self.overlay:IsShown() then + self.overlay:RefreshControls() + end +end + +function Bar:SetLabelSubtext(text) + if self.overlay then + self.overlay:SetLabelSubtext(text) + end +end + +-- +-- Secure state functions +-- + +function Bar:GetSecureState() + local env = GetManagedEnvironment(self:GetFrame()) + return env and env.state +end + +function Bar:GetStateProperty(state, propname) + return tfetch(self:GetConfig(), "states", state, propname) +end + +function Bar:SetStateProperty(state, propname, value) + local s = tbuild(self:GetConfig(), "states", state) + s[propname] = value + self:SetSecureStateData(state, propname, value) +end + +function Bar:ApplyStates() + local states = tfetch(self:GetConfig(), "states") + if states then + self:SetStateDriver(states) + end +end + +function Bar:CleanupStates() + self:SetStateDriver(nil) +end + +function Bar:RefreshSecureState() + self:GetFrame():Execute(_reaction_refresh) +end + +-- usage: SetSecureData(globalname, [tblkey1, tblkey2, ...], value) +function Bar:SetSecureData( ... ) + local n = select('#',...) + if n < 2 then + error("ReAction.Bar:SetSecureData() requires at least 2 arguments") + end + local f = self:GetFrame() + f:SetAttribute("data-depth",n-1) + f:SetAttribute("data-value",select(n,...)) + for i = 1, n-1 do + local key = select(i,...) + if key == nil then + error("ReAction.Bar:SetSecureData() - nil table key in argument list (#"..i..")") + end + f:SetAttribute("data-key-"..i, key) + end + f:Execute( + [[ + local n = self:GetAttribute("data-depth") + if n > 0 then + local value = self:GetAttribute("data-value") + local t = _G + for i = 1, n do + local key = self:GetAttribute("data-key-"..i) + if not key then return end + if not t[key] then + t[key] = newtable() + end + if i == n then + t[key] = value + else + t = t[key] + end + end + end + ]]) + self:RefreshSecureState() +end + +function Bar:SetSecureStateData( state, key, value ) + self:SetSecureData("settings",state,key,value) +end + +-- sets a snippet to be run as an extension to _onstate-reaction +function Bar:SetSecureStateExtension( id, snippet ) + if id == nil then + error("ReAction.Bar:SetSecureStateExtension() requires an id") + end + local f = self:GetFrame() + f:SetAttribute("input-secure-ext-id",id) + f:SetAttribute("secure-ext-"..id,snippet) + f:Execute( + [[ + local id = self:GetAttribute("input-secure-ext-id") + if id then + extensions[id] = self:GetAttribute("secure-ext-"..id) or nil + end + ]]) + self:RefreshSecureState() +end + +function Bar:SetFrameRef( name, refFrame ) + if refFrame then + local _, explicit = refFrame:IsProtected() + if not explicit then + refFrame = nil + end + end + if refFrame then + self:GetFrame():SetFrameRef(name,refFrame) + else + self:GetFrame():SetAttribute("frameref-"..name,nil) + end +end + +function Bar:SetStateDriver( states ) + if states then + for state, props in pairs(states) do + self:SetSecureStateData(state, "active_", true) -- make sure there's a 'settings' field for this state + for propname, value in pairs(props) do + if propname == "anchorFrame" then + self:SetFrameRef("anchor-"..state, _G[value]) + elseif propname == "rule" then + -- do nothing + else + self:SetSecureStateData(state, propname, value) + end + end + end + end + local rule = states and self:BuildStateRule(states) + if rule then + RegisterStateDriver(self:GetFrame(),"reaction",rule) + elseif self.statedriver then + UnregisterStateDriver(self:GetFrame(),"reaction") + end + self.statedriver = rule + self:BuildStateKeybinds(states) + self:RefreshSecureState() +end + +-- pass unit=nil to set up the unit elsewhere, if you want something more complex +function Bar:RegisterUnitWatch( unit, enable ) + local f = self:GetFrame() + if unit then + f:SetAttribute("unit",unit) + end + if enable then + if not self.unitwatch then + RegisterUnitWatch(self:GetFrame(),true) + end + elseif self.unitwatch then + UnregisterUnitWatch(self:GetFrame()) + end + self.unitwatch = enable + self:RefreshSecureState() +end + +function Bar:SetStateKeybind( key, state ) + local f = self:GetFrame() + local binds = self.statebinds + if not binds then + binds = { } + self.statebinds = binds + end + + -- clear the old binding, if any + if binds[state] then + SetOverrideBinding(f, false, binds[state], nil) + end + + if key then + SetOverrideBindingClick(f, false, key, f:GetName(), state) -- state name is virtual mouse button + end + binds[state] = key +end + +function Bar:GetStateKeybind( state ) + if self.statebinds and state then + return self.statebinds[state] + end +end + +function Bar:UpdateDefaultStateAnchor() + local point, frame, relPoint, x, y = self:GetAnchor() + local f = self:GetFrame() + f:SetAttribute("defaultAnchor-point",point) + f:SetAttribute("defaultAnchor-relPoint",relPoint) + f:SetAttribute("defaultAnchor-x",x) + f:SetAttribute("defaultAnchor-y",y) + self:SetFrameRef("defaultAnchor",_G[frame or "UIParent"]) + f:Execute([[ + for _, k in pairs(anchorKeys) do + defaultAnchor[k] = self:GetAttribute("defaultAnchor-"..k) + end + defaultAnchor.frame = self:GetAttribute("frameref-defaultAnchor") + ]]) +end + +function Bar:UpdateDefaultStateAlpha() + local f = self:GetFrame() + f:SetAttribute("defaultAlpha",self:GetAlpha()) + f:Execute([[ + defaultAlpha = self:GetAttribute("defaultAlpha") + ]]) +end + +---- secure state driver rules ---- + +local playerClass = select(2, UnitClass("player")) +local function ClassFilter(...) + for i = 1, select('#',...) do + if playerClass == select(i,...) then + return false + end + end + return true +end + +local ruleformats = { + stealth = { format = "stealth", filter = ClassFilter("ROGUE","DRUID") }, + nostealth = { format = "nostealth", filter = ClassFilter("ROGUE","DRUID") }, + shadowdance = { format = "bonusbar:2", filter = ClassFilter("ROGUE") }, + shadowform = { format = "form:1", filter = ClassFilter("PRIEST") }, + noshadowform = { format = "noform", filter = ClassFilter("PRIEST") }, + battle = { format = "stance:1", filter = ClassFilter("WARRIOR") }, + defensive = { format = "stance:2", filter = ClassFilter("WARRIOR") }, + berserker = { format = "stance:3", filter = ClassFilter("WARRIOR") }, + caster = { format = "form:0/2/4/5/6", filter = ClassFilter("DRUID") }, + bear = { format = "form:1", filter = ClassFilter("DRUID") }, + cat = { format = "form:3", filter = ClassFilter("DRUID") }, + tree = { format = "form:5", filter = ClassFilter("DRUID") }, + moonkin = { format = "form:5", filter = ClassFilter("DRUID") }, + demon = { format = "form:2", filter = ClassFilter("WARLOCK") }, + nodemon = { format = "noform", filter = ClassFilter("WARLOCK") }, + pet = { format = "pet" }, + nopet = { format = "nopet" }, + harm = { format = "@target,harm" }, + help = { format = "@target,help" }, + notarget = { format = "@target,noexists" }, + focusharm = { format = "@focus,harm" }, + focushelp = { format = "@focus,help" }, + nofocus = { format = "@focus,noexists" }, + raid = { format = "group:raid" }, + party = { format = "group:party" }, + solo = { format = "nogroup" }, + combat = { format = "combat" }, + nocombat = { format = "nocombat" }, + possess = { format = "@vehicle,noexists,bonusbar:5" }, + vehicle = { format = "@vehicle,exists,bonusbar:5" }, +} + +function Bar.InitRuleFormats() + local forms = { } + for i = 1, GetNumShapeshiftForms() do + local _, name = GetShapeshiftFormInfo(i) + forms[name] = i; + end + -- use 9 if not found since 9 is never a valid stance/form + local defensive = forms[GetSpellInfo(71)] or 9 + local berserker = forms[GetSpellInfo(2458)] or 9 + local bear = forms[GetSpellInfo(5487)] or 9 + local aquatic = forms[GetSpellInfo(1066)] or 9 + local cat = forms[GetSpellInfo(768)] or 9 + local travel = forms[GetSpellInfo(783)] or 9 + local tree = forms[GetSpellInfo(33891)] or 9 + local moonkin = forms[GetSpellInfo(24858)] or 9 + local flight = forms[GetSpellInfo(40120)] or forms[GetSpellInfo(33943)] or 9 + + ruleformats.defensive.format = "stance:"..defensive + ruleformats.berserker.format = "stance:"..berserker + ruleformats.caster.format = format("form:0/%d/%d/%d", aquatic, travel, flight) + ruleformats.bear.format = "form:"..bear + ruleformats.cat.format = "form:"..cat + ruleformats.tree.format = "form:"..tree + ruleformats.moonkin.format = "form:"..moonkin +end + +function Bar:BuildStateRule(states) + -- states is a table : + -- states[statename].rule = { + -- order = #, + -- type = "default"/"custom"/"any"/"all", + -- values = { ... }, -- keys of ruleformats[] + -- custom = "...", + -- } + local rules = { } + local default + + for idx, state in ipairs(fieldsort(states, "rule", "order")) do + local c = states[state].rule + local type = c.type + if type == "default" then + default = default or state + elseif type == "custom" then + if c.custom then + -- strip out all spaces from the custom rule + table.insert(rules, format("%s %s", c.custom:gsub("%s",""), state)) + end + elseif type == "any" or type == "all" then + if c.values then + local clauses = { } + for key, value in pairs(c.values) do + if ruleformats[key] and not ruleformats[key].filter then + table.insert(clauses, ruleformats[key].format) + end + end + if #clauses > 0 then + local sep = (type == "any") and "][" or "," + table.insert(rules, format("[%s] %s", table.concat(clauses,sep), state)) + end + end + end + end + -- make sure that the default, if any, is last + if default then + table.insert(rules, default) + end + return table.concat(rules,";") +end + +function Bar:BuildStateKeybinds( states ) + if states then + for name, state in pairs(states) do + local rule = tfetch(state, "rule") + if rule and rule.type == "keybind" then + self:SetStateKeybind(rule.keybind, name) + else + self:SetStateKeybind(nil, name) -- this clears an existing keybind + end + end + end +end +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Button.lua Tue Apr 12 16:06:31 2011 -0700 @@ -0,0 +1,359 @@ +--[[ + ReAction Button base class +--]] + +-- local imports +local addonName, addonTable = ... +local ReAction = addonTable.ReAction +local L = ReAction.L +local LKB = ReAction.LKB +local _G = _G +local CreateFrame = CreateFrame +local GetBindingKey = GetBindingKey +local format = string.format + +-- private +local trash = CreateFrame("Frame") +local frameList = { } +local idPools = { } + +local function kb_onEnter( frame ) + LKB:Set(frame) +end + +-- Button class +local buttonTypeID = "Button" +local Button = { + defaultBarConfig = { + type = buttonTypeID, + btnWidth = 36, + btnHeight = 36, + btnRows = 1, + btnColumns = 12, + spacing = 3 + }, + barType = L["Button Bar"] +} + +ReAction.Button = Button -- export to ReAction + +function Button:New( name, config, bar, idx, inherits, buttonType ) + buttonType = buttonType or "CheckButton" + + -- create new self + self = setmetatable( + { + bar = bar, + idx = idx, + config = config, + name = name, + }, + { __index = self } ) + + -- have to recycle frames with the same name: CreateFrame() doesn't overwrite + -- existing globals. Can't set to nil in the global because it's then tainted. + -- Caller is responsible for ensuring global uniqueness of names. + local f = name and frameList[name] + if f then + f:SetParent(bar:GetFrame()) + else + f = CreateFrame(buttonType, name, bar:GetFrame(), inherits) + if name then + frameList[name] = f + end + end + + self.frame = f + + local frames = { } + self.frames = frames + frames.icon = _G[name.."Icon"] + frames.flash = _G[name.."Flash"] + frames.hotkey = _G[name.."HotKey"] + frames.count = _G[name.."Count"] + frames.name = _G[name.."Name"] + frames.border = _G[name.."Border"] + frames.cooldown = _G[name.."Cooldown"] + frames.normalTexture = _G[name.."NormalTexture"] + + if config then + config.name = name + end + + -- install LibKeyBound handlers onto frame + function f:GetActionName() + return format("%s:%s", bar:GetName(), idx) + end + + local clickBinding = format("CLICK %s:LeftButton", name) + function f:GetHotkey() + return LKB:ToShortKey(GetBindingKey(clickBinding)) + end + + return self +end + +function Button:Destroy() + local f = self:GetFrame() + f:UnregisterAllEvents() + self:ReleaseActionID(self:GetActionID()) + if f then + f:Hide() + f:SetParent(trash) + f:ClearAllPoints() + end +end + +function Button:GetBar() + return self.bar +end + +function Button:GetFrame() + return self.frame +end + +function Button:GetIndex() + return self.idx +end + +function Button:GetName() + return self.name +end + +function Button:GetDefaultBarConfig() + return self.defaultBarConfig +end + +function Button:GetBarType() + return self.barType +end + +function Button:GetButtonTypeID() + return self.buttonTypeID +end + +function Button:GetConfig() + return self.config +end + +function Button:GetActionID() + -- derived classes should override this + return nil +end + +function Button:SetActionIDPool( poolID, maxID ) + self.actionPoolID = poolID + self.actionMaxID = maxID +end + +function Button:SetupBar( bar ) + local config = bar:GetConfig() + if not config.buttons then + config.buttons = { } + end + local btnCfg = config.buttons + + local r, c = bar:GetButtonGrid() + local n = r*c + local cfgN = n + + local hint = nil + local i = 1 + repeat + local b = bar:GetButton(i) + if b then + if i > n then + bar:RemoveButton(b) + b:Destroy() + if i > cfgN then + btnCfg[i] = nil + end + else + b:Refresh() + hint = b:GetActionID() + end + elseif i <= n then + local cfg = btnCfg[i] or { } + local success, r = pcall(self.New, self, cfg, bar, i, hint) -- note call semantics for derived class constructors + if success and r then + b = r + bar:AddButton(i,b) + btnCfg[i] = cfg + b:Refresh() + hint = b:GetActionID() + else + n = i - 1 + if not success then + bar:ClipNButtons(n) + cfgN = n + geterrorhandler()(r) + end + end + end + i = i + 1 + until b == nil +end + +function Button:AcquireActionID( id, hint, unique ) + local poolID = self.actionPoolID + local maxID = self.actionMaxID + if not poolID or not maxID then + error("AcquireActionID: must setup pool first with SetActionIDPool") + end + local pool = idPools[poolID] + if not pool then + pool = { nWraps = 0, useCount = { } } + for i = 1, maxID do + pool.useCount[i] = 0 + end + idPools[poolID] = pool + end + local useCount = pool.useCount + if id == nil then + repeat + local nWraps = pool.nWraps or 0 + if hint and (useCount[hint] == nil or useCount[hint] == nWraps) then + id = hint + else + local start = hint or 1 + for i = start, maxID do + if useCount[i] == nil or useCount[i] == nWraps then + id = i + break + end + end + if not id then + for i = 1, start do + if useCount[i] == nil or useCount[i] == nWraps then + id = i + break + end + end + end + end + if id == nil then + if unique then + return nil + end + pool.nWraps = nWraps + 1 + end + until id ~= nil + end + useCount[id] = (useCount[id] or 0) + 1 + return id +end + +function Button:ReleaseActionID( id ) + local poolID = self.actionPoolID + if not poolID then + error("ReleaseActionID: must setup pool first with SetActionIDPool") + end + local pool = idPools[poolID] + if pool and id and pool.useCount[id] then + pool.useCount[id] = pool.useCount[id] - 1 + pool.nWraps = min(pool.useCount[id], pool.nWraps) + end +end + +function Button:Refresh() + local f = self:GetFrame() + self.bar:PlaceButton( self, f:GetWidth(), f:GetHeight() ) +end + +function Button:SetKeybindMode( mode ) + local f = self.frame + if mode then + self.oldOnEnter = f:GetScript("OnEnter") + f:SetScript("OnEnter", kb_onEnter) + elseif self.oldOnEnter then + f:SetScript("OnEnter", self.oldOnEnter) + self.oldOnEnter = nil + end + self:ShowGridTemp(mode) + self:UpdateKeybindModeDisplay( mode ) +end + +function Button:UpdateKeybindModeDisplay( mode ) + local border = self.frames.border or _G[format("%sBorder",tostring(self:GetName()))] + if border then + if mode then + border:SetVertexColor(LKB:GetColorKeyBoundMode()) + border:Show() + else + border:Hide() + end + end +end + +function Button:UpdateHotkey( hotkey ) + hotkey = hotkey or self.frames.hotkey + if not hotkey then + hotkey = _G[self:GetName().."HotKey"] + self.frames.hotkey = hotkey + end + if hotkey then + local txt = self.frame:GetHotkey() + hotkey:SetText( txt ) + if txt == nil or txt == "" then + hotkey:Hide() + else + hotkey:Show() + end + end +end + +function Button:GetActionIDLabel( create ) + local f = self:GetFrame() + if not f.actionIDLabel and create then + local label = f:CreateFontString(nil,"OVERLAY","GameFontNormalLarge") + label:SetAllPoints() + label:SetJustifyH("CENTER") + label:SetShadowColor(0,0,0,1) + label:SetShadowOffset(2,-2) + f.actionIDLabel = label -- store the label with the frame for recycling + end + return f.actionIDLabel +end + +function Button:UpdateActionIDLabel( show ) + local label = self:GetActionIDLabel( show ) + if label then + if show then + local id = self:GetActionID() + if id then + label:SetText(tostring(id)) + label:Show() + return + end + end + label:Hide() + end +end + +function Button:SetNormalVertexColor( r, g, b, a ) + if ReAction.LBF then + ReAction.LBF:SetNormalVertexColor(self:GetFrame(), r, g, b, a) + else + self:GetFrame():GetNormalTexture():SetVertexColor(r,g,b,a) + end +end + +function Button:GetNormalVertexColor() + if ReAction.LBF then + return ReAction.LBF:GetNormalVertexColor(self:GetFrame()) + else + return self:GetFrame():GetNormalTexture():GetVertexColor() + end +end + +function Button:UpdateShowGrid() + -- does nothing by default +end + +function Button:ShowGridTemp(show) + -- does nothing by default +end + +function Button:ShowGrid(show) + -- does nothing by default +end
--- a/Editor.lua Thu Nov 18 13:11:08 2010 -0800 +++ b/Editor.lua Tue Apr 12 16:06:31 2011 -0700 @@ -3,12 +3,17 @@ local L = ReAction.L local _G = _G local wipe = wipe +local format = string.format +local InCombatLockdown = InCombatLockdown +local tfetch = addonTable.tfetch +local tbuild = addonTable.tbuild local AceConfigReg = LibStub("AceConfigRegistry-3.0") local AceConfigDialog = LibStub("AceConfigDialog-3.0") local pointTable = { + NONE = " ", CENTER = L["Center"], LEFT = L["Left"], RIGHT = L["Right"], @@ -20,7 +25,9 @@ BOTTOMRIGHT = L["Bottom Right"], } -local Editor = { } +local Editor = { + buttonHandlers = { } +} function Editor:New() -- create new self @@ -90,12 +97,12 @@ name = L["Button Type"], get = function() return self.tmp.barType or ReAction:GetDefaultBarType() or "" end, set = function(info, val) - local c = ReAction:GetBarTypeConfig(val) + local c = ReAction:GetDefaultBarConfig(val) self.tmp.barType = val - self.tmp.barSize = c.defaultButtonSize or self.tmp.barSize - self.tmp.barRows = c.defaultBarRows or self.tmp.barRows - self.tmp.barCols = c.defaultBarCols or self.tmp.barCols - self.tmp.barSpacing = c.defaultBarSpacing or self.tmp.barSpacing + self.tmp.barSize = c.btnWidth or self.tmp.barSize + self.tmp.barRows = c.btnRows or self.tmp.barRows + self.tmp.barCols = c.btnColumns or self.tmp.barCols + self.tmp.barSpacing = c.spacing or self.tmp.barSpacing end, values = "GetBarTypes", order = 3, @@ -173,12 +180,9 @@ ReAction.RegisterCallback(self,"OnCreateBar") ReAction.RegisterCallback(self,"OnDestroyBar") - ReAction.RegisterCallback(self,"OnEraseBar") ReAction.RegisterCallback(self,"OnRenameBar") - for name, bar in ReAction:IterateBars() do - self:CreateBarTree(bar) - end + self:RefreshBarOptions() return self end @@ -203,160 +207,170 @@ AceConfigReg:NotifyChange(self.configID) end -function Editor:CreateBarTree(bar) +function Editor:UpdateBarOptions(bar) local name = bar:GetName() - -- AceConfig doesn't allow spaces, etc, in arg key names, and they must be - -- unique strings. So generate a unique key (it can be whatever) for the bar + local key = self.barOptMap[name] local args = self.options.args - local key - local i = 1 - repeat - key = ("bar%s"):format(i) - i = i+1 - until args[key] == nil - self.barOptMap[name] = key - args[key] = { - type = "group", - name = name, - childGroups = "tab", - order = i+100, - args = { - general = { - type = "group", - name = L["General"], - order = 1, - args = { - name = { - type = "input", - name = L["Rename Bar"], - get = function() return bar:GetName() end, - set = function(info, value) return ReAction:RenameBar(bar, value) end, - order = 1, - }, - delete = { - type = "execute", - name = L["Delete Bar"], - desc = function() return bar:GetName() end, - confirm = true, - func = function() ReAction:EraseBar(bar) end, - order = 2 - }, - anchor = { - type = "group", - name = L["Anchor"], - inline = true, - args = { - frame = { - type = "input", - name = L["Frame"], - desc = L["The frame that the bar is anchored to"], - get = function() local _, f = bar:GetAnchor(); return f end, - set = function(info, val) bar:SetAnchor(nil,val) end, - validate = function(info, name) - if name then - local f = ReAction:GetBar(name) - if f then - return true - else - f = _G[name] - if f and type(f) == "table" and f.IsObjectType and f:IsObjectType("Frame") then - local _, explicit = f:IsProtected() - return explicit + + if not key then + -- AceConfig doesn't allow spaces, etc, in arg key names, and they must be + -- unique strings. So generate a unique key (it can be whatever) for the bar + local i = 1 + repeat + key = ("bar%s"):format(i) + i = i+1 + until args[key] == nil + self.barOptMap[name] = key + + args[key] = { + type = "group", + name = name, + childGroups = "tab", + order = i+100, + args = { + general = { + type = "group", + name = L["General"], + order = 1, + args = { + name = { + type = "input", + name = L["Rename Bar"], + get = function() return bar:GetName() end, + set = function(info, value) return ReAction:RenameBar(bar, value) end, + order = 1, + }, + delete = { + type = "execute", + name = L["Delete Bar"], + desc = function() return bar:GetName() end, + confirm = true, + func = function() ReAction:EraseBar(bar) end, + order = 2 + }, + anchor = { + type = "group", + name = L["Anchor"], + inline = true, + args = { + frame = { + type = "input", + name = L["Frame"], + desc = L["The frame that the bar is anchored to"], + get = function() local _, f = bar:GetAnchor(); return f end, + set = function(info, val) bar:SetAnchor(nil,val) end, + validate = function(info, name) + if name then + local f = ReAction:GetBar(name) + if f then + return true + else + f = _G[name] + if f and type(f) == "table" and f.IsObjectType and f:IsObjectType("Frame") then + local _, explicit = f:IsProtected() + return explicit + end end end - end - return false - end, - width = "double", - order = 1 + return false + end, + width = "double", + order = 1 + }, + point = { + type = "select", + name = L["Point"], + desc = L["Anchor point on the bar frame"], + style = "dropdown", + get = function() return bar:GetAnchor() end, + set = function(info, val) bar:SetAnchor(val) end, + values = pointTable, + order = 2, + }, + relativePoint = { + type = "select", + name = L["Relative Point"], + desc = L["Anchor point on the target frame"], + style = "dropdown", + get = function() local p,f,r = bar:GetAnchor(); return r end, + set = function(info, val) bar:SetAnchor(nil,nil,val) end, + values = pointTable, + order = 3, + }, + x = { + type = "input", + pattern = "\-?%d+", + name = L["X offset"], + get = function() local p,f,r,x = bar:GetAnchor(); return ("%d"):format(x) end, + set = function(info,val) bar:SetAnchor(nil,nil,nil,val) end, + order = 4 + }, + y = { + type = "input", + pattern = "\-?%d+", + name = L["Y offset"], + get = function() local p,f,r,x,y = bar:GetAnchor(); return ("%d"):format(y) end, + set = function(info,val) bar:SetAnchor(nil,nil,nil,nil,val) end, + order = 5 + }, }, - point = { - type = "select", - name = L["Point"], - desc = L["Anchor point on the bar frame"], - style = "dropdown", - get = function() return bar:GetAnchor() end, - set = function(info, val) bar:SetAnchor(val) end, - values = pointTable, - order = 2, - }, - relativePoint = { - type = "select", - name = L["Relative Point"], - desc = L["Anchor point on the target frame"], - style = "dropdown", - get = function() local p,f,r = bar:GetAnchor(); return r end, - set = function(info, val) bar:SetAnchor(nil,nil,val) end, - values = pointTable, - order = 3, - }, - x = { - type = "input", - pattern = "\-?%d+", - name = L["X offset"], - get = function() local p,f,r,x = bar:GetAnchor(); return ("%d"):format(x) end, - set = function(info,val) bar:SetAnchor(nil,nil,nil,val) end, - order = 4 - }, - y = { - type = "input", - pattern = "\-?%d+", - name = L["Y offset"], - get = function() local p,f,r,x,y = bar:GetAnchor(); return ("%d"):format(y) end, - set = function(info,val) bar:SetAnchor(nil,nil,nil,nil,val) end, - order = 5 - }, + order = 3 }, - order = 3 - }, - alpha = { - type = "range", - name = L["Transparency"], - get = function() return bar:GetAlpha() end, - set = function(info, val) bar:SetAlpha(val) end, - min = 0, - max = 1, - isPercent = true, - step = 0.01, - bigStep = 0.05, - order = 4, + alpha = { + type = "range", + name = L["Transparency"], + get = function() return bar:GetAlpha() end, + set = function(info, val) bar:SetAlpha(val) end, + min = 0, + max = 1, + isPercent = true, + step = 0.01, + bigStep = 0.05, + order = 4, + }, }, }, - }, + buttonOpts = self:CreateButtonOptions(bar), + stateOpts = self:CreateStateOptions(bar) + } } - } - self:RefreshBarOptions() + end + +end + +function Editor:CreateButtonOptions(bar) + local buttonClass = bar:GetButtonClass() + local classID = buttonClass:GetButtonTypeID() + local handler = self.buttonHandlers[classID] + + if handler then + local h = handler:New(bar) + return h:GetOptions() + end end function Editor:RefreshBarOptions() for name, key in pairs(self.barOptMap) do - local bar = ReAction:GetBar(name) - if bar and key and self.options.args[key] then - self.options.args[key].plugins = self:GenerateBarOptionsTable(bar) + if not ReAction:GetBar(name) then + self.barOptMap[name] = nil + self.options.args[key] = nil end end - AceConfigReg:NotifyChange(self.configID) + for name, bar in ReAction:IterateBars() do + self:UpdateBarOptions(bar) + end + self:Refresh() end function Editor:OnCreateBar(evt, bar) - if not self.tmp.creating then - -- a bit of hack to work around OnCreateBar event handler ordering - self:CreateBarTree(bar) - end + self:UpdateBarOptions(bar) + self:Refresh() end function Editor:OnDestroyBar(evt, bar, name) local key = self.barOptMap[name] if key then - self.options.args[key] = nil - end - self:Refresh() -end - -function Editor:OnEraseBar(evt, name) - local key = self.barOptMap[name] - self.barOptMap[name] = nil - if key then + self.barOptMap[name] = nil self.options.args[key] = nil self:Refresh() end @@ -364,8 +378,8 @@ function Editor:OnRenameBar(evt, bar, oldname, newname) local key = self.barOptMap[oldname] - self.barOptMap[oldname], self.barOptMap[newname] = nil, key if key then + self.barOptMap[oldname], self.barOptMap[newname] = nil, key self.options.args[key].name = newname self:Refresh() end @@ -379,42 +393,1300 @@ function Editor:CreateBar() if self.tmp.barName and self.tmp.barName ~= "" then - self.tmp.creating = true local bar = ReAction:CreateBar(self.tmp.barName, self.tmp.barType or ReAction:GetDefaultBarType(), self.tmp.barRows, self.tmp.barCols, self.tmp.barSize, self.tmp.barSpacing) if bar then - self:CreateBarTree(bar) AceConfigDialog:SelectGroup(self.configID, self.barOptMap[self.tmp.barName]) self.tmp.barName = nil end - self.tmp.creating = false end end -function Editor:GenerateBarOptionsTable( bar ) - local opts = { } - if not ReAction.barOptionGenerators then - return +------------------------------- +---- Action button handler ---- +------------------------------- + +do + local ActionHandler = { + buttonClass = ReAction.Button.Action, + options = { + hideEmpty = { + name = L["Hide Empty Buttons"], + order = 1, + type = "toggle", + width = "double", + get = "GetHideEmpty", + set = "SetHideEmpty", + }, + lockButtons = { + name = L["Lock Buttons"], + desc = L["Prevents picking up/dragging actions (use SHIFT to override this behavior)"], + order = 2, + type = "toggle", + get = "GetLockButtons", + set = "SetLockButtons", + }, + lockOnlyCombat = { + name = L["Only in Combat"], + desc = L["Only lock the buttons when in combat"], + order = 3, + type = "toggle", + disabled = "LockButtonsCombatDisabled", + get = "GetLockButtonsCombat", + set = "SetLockButtonsCombat", + }, + pages = { + name = L["# Pages"], + desc = L["Use the Dynamic State tab to specify page transitions"], + order = 4, + type = "range", + min = 1, + max = 10, + step = 1, + get = "GetNumPages", + set = "SetNumPages", + }, + mindcontrol = { + name = L["Mind Control Support"], + desc = L["When possessing a target (e.g. via Mind Control), map the first 12 buttons of this bar to the possessed target's actions."], + order = 5, + type = "toggle", + width = "double", + set = "SetMindControl", + get = "GetMindControl", + }, + vehicle = { + name = L["Vehicle Support"], + desc = L["When on a vehicle, map the first 6 buttons of this bar to the vehicle actions. The vehicle-exit button is mapped to the 7th button. Pitch controls are not supported."], + order = 6, + type = "toggle", + width = "double", + get = "GetVehicle", + set = "SetVehicle", + }, + actions = { + name = L["Edit Action IDs"], + order = 7, + type = "group", + inline = true, + args = { + method = { + name = L["Assign"], + order = 1, + type = "select", + width = "double", + values = { [0] = L["Choose Method..."], + [1] = L["Individually"], + [2] = L["All at Once"], }, + get = "GetActionEditMethod", + set = "SetActionEditMethod", + }, + rowSelect = { + name = L["Row"], + desc = L["Rows are numbered top to bottom"], + order = 2, + type = "select", + width = "half", + hidden = "IsButtonSelectHidden", + values = "GetRowList", + get = "GetSelectedRow", + set = "SetSelectedRow", + }, + colSelect = { + name = L["Col"], + desc = L["Columns are numbered left to right"], + order = 3, + type = "select", + width = "half", + hidden = "IsButtonSelectHidden", + values = "GetColumnList", + get = "GetSelectedColumn", + set = "SetSelectedColumn", + }, + pageSelect = { + name = L["Page"], + order = 4, + type = "select", + width = "half", + hidden = "IsPageSelectHidden", + values = "GetPageList", + get = "GetSelectedPage", + set = "SetSelectedPage", + }, + single = { + name = L["Action ID"], + usage = L["Specify ID 1-120"], + order = 5, + type = "input", + width = "half", + hidden = "IsButtonSelectHidden", + get = "GetActionID", + set = "SetActionID", + validate = "ValidateActionID", + }, + multi = { + name = L["ID List"], + usage = L["Specify a comma-separated list of IDs for each button in the bar (in order). Separate multiple pages with semicolons (;)"], + order = 6, + type = "input", + multiline = true, + width = "double", + hidden = "IsMultiIDHidden", + get = "GetMultiID", + set = "SetMultiID", + validate = "ValidateMultiID", + }, + }, + }, + } + } + + Editor.buttonHandlers[ActionHandler.buttonClass:GetButtonTypeID()] = ActionHandler + + local meta = { __index = ActionHandler } + + function ActionHandler:New( bar ) + return setmetatable( + { + bar = bar, + config = bar:GetConfig(), + }, + meta) end - for module, func in pairs(ReAction.barOptionGenerators) do - local success, r - if type(func) == "string" then - success, r = pcall(module[func], module, bar) - else - success, r = pcall(func, bar) - end - if success then - if r then - opts[module:GetName()] = { [module:GetName()] = r } + function ActionHandler:Refresh() + self.buttonClass:SetupBar(self.bar) + end + + function ActionHandler:UpdateButtonLock() + self.buttonClass:SetButtonLock(self.bar, self.config.lockButtons, self.config.lockButtonsCombat) + end + + function ActionHandler:GetLastButton() + return self.bar:GetButton(self.bar:GetNumButtons()) + end + + -- options handlers + function ActionHandler:GetOptions() + return { + type = "group", + name = L["Action Buttons"], + handler = self, + order = 2, + args = self.options + } + end + + function ActionHandler:SetHideEmpty(info, value) + if value ~= self.config.hideEmpty then + self.config.hideEmpty = value + for _, b in self.bar:IterateButtons() do + b:ShowGrid(not value) end - else - geterrorhandler()(r) end end - return opts + + function ActionHandler:GetHideEmpty() + return self.config.hideEmpty + end + + function ActionHandler:GetLockButtons() + return self.config.lockButtons + end + + function ActionHandler:SetLockButtons(info, value) + self.config.lockButtons = value + self:UpdateButtonLock() + end + + function ActionHandler:GetLockButtonsCombat() + return self.config.lockButtonsCombat + end + + function ActionHandler:SetLockButtonsCombat(info, value) + self.config.lockButtonsCombat = value + self:UpdateButtonLock() + end + + function ActionHandler:LockButtonsCombatDisabled() + return not self.config.lockButtons + end + + function ActionHandler:GetNumPages() + return self.config.nPages + end + + function ActionHandler:SetNumPages(info, value) + self.config.nPages = value + self:Refresh() + end + + function ActionHandler:GetMindControl() + return self.config.mindcontrol + end + + function ActionHandler:SetMindControl(info, value) + self.config.mindcontrol = value + self:Refresh() + end + + function ActionHandler:GetVehicle() + return self.config.vehicle + end + + function ActionHandler:SetVehicle(info, value) + self.config.vehicle = value + self:Refresh() + end + + function ActionHandler:GetActionEditMethod() + return self.editMethod or 0 + end + + function ActionHandler:SetActionEditMethod(info, value) + self.editMethod = value + end + + function ActionHandler:IsButtonSelectHidden() + return self.editMethod ~= 1 + end + + function ActionHandler:GetRowList() + local r,c = self.bar:GetButtonGrid() + if self.rowList == nil or #self.rowList ~= r then + local list = { } + for i = 1, r do + table.insert(list,i) + end + self.rowList = list + end + return self.rowList + end + + function ActionHandler:GetSelectedRow() + local r, c = self.bar:GetButtonGrid() + local row = self.selectedRow or 1 + if row > r then + row = 1 + end + self.selectedRow = row + return row + end + + function ActionHandler:SetSelectedRow(info, value) + self.selectedRow = value + end + + function ActionHandler:GetColumnList() + local r,c = self.bar:GetButtonGrid() + if self.columnList == nil or #self.columnList ~= c then + local list = { } + for i = 1, c do + table.insert(list,i) + end + self.columnList = list + end + return self.columnList + end + + function ActionHandler:GetSelectedColumn() + local r, c = self.bar:GetButtonGrid() + local col = self.selectedColumn or 1 + if col > c then + col = 1 + end + self.selectedColumn = col + return col + end + + function ActionHandler:SetSelectedColumn(info, value) + self.selectedColumn = value + end + + function ActionHandler:IsPageSelectHidden() + return self.editMethod ~= 1 or (self.config.nPages or 1) < 2 + end + + function ActionHandler:GetPageList() + local n = self.config.nPages or 1 + if self.pageList == nil or #self.pageList ~= n then + local p = { } + for i = 1, n do + table.insert(p,i) + end + self.pageList = p + end + return self.pageList + end + + function ActionHandler:GetSelectedPage() + local p = self.selectedPage or 1 + if p > (self.config.nPages or 1) then + p = 1 + end + self.selectedPage = p + return p + end + + function ActionHandler:SetSelectedPage(info, value) + self.selectedPage = value + end + + function ActionHandler:GetActionID() + local row = self.selectedRow or 1 + local col = self.selectedColumn or 1 + local r, c = self.bar:GetButtonGrid() + local n = (row-1) * c + col + local btn = self.bar:GetButton(n) + if btn then + return tostring(btn:GetActionID(self.selectedPage or 1)) + end + end + + function ActionHandler:SetActionID(info, value) + local row = self.selectedRow or 1 + local col = self.selectedColumn or 1 + local r, c = self.bar:GetButtonGrid() + local n = (row-1) * c + col + local btn = self.bar:GetButton(n) + if btn then + btn:SetActionID(tonumber(value), self.selectedPage or 1) + end + end + + function ActionHandler:ValidateActionID(info, value) + value = tonumber(value) + if value == nil or value < 1 or value > 120 then + return L["Specify ID 1-120"] + end + return true + end + + function ActionHandler:IsMultiIDHidden() + return self.editMethod ~= 2 + end + + function ActionHandler:GetMultiID() + local p = { } + for i = 1, self.config.nPages or 1 do + local b = { } + for _, btn in self.bar:IterateButtons() do + table.insert(b, btn:GetActionID(i)) + end + table.insert(p, table.concat(b,",")) + end + return table.concat(p,";\n") + end + + + local function ParseMultiID(nBtns, nPages, s) + if s:match("[^%d%s,;]") then + return nil + end + local p = { } + for list in s:gmatch("[^;]+") do + local pattern = ("^%s?$"):format(("%s*(%d+)%s*,"):rep(nBtns)) + local ids = { list:match(pattern) } + if #ids ~= nBtns then + return nil + end + table.insert(p,ids) + end + if #p ~= nPages then + return nil + end + return p + end + + function ActionHandler:SetMultiID(info, value) + local p = ParseMultiID(self.bar:GetNumButtons(), self.config.nPages or 1, value) + for page, b in ipairs(p) do + for button, id in ipairs(b) do + self.bar:GetButton(button):SetActionID(id, page) + end + end + end + + function ActionHandler:ValidateMultiID(info, value) + local bad = L["Invalid action ID list string"] + if value == nil or ParseMultiID(self.bar:GetNumButtons(), self.config.nPages or 1, value) == nil then + return bad + end + return true + end end +---------------------------------- +---- PetAction button handler ---- +---------------------------------- + +do + local PetHandler = { + buttonClass = ReAction.Button.PetAction, + } + + Editor.buttonHandlers[PetHandler.buttonClass:GetButtonTypeID()] = PetHandler + + local meta = { __index = PetHandler } + + function PetHandler:New(bar) + return setmetatable( + { + bar = bar, + config = bar.config + }, meta) + end + + function PetHandler:GetLockButtons() + return self.config.lockButtons + end + + function PetHandler:SetLockButtons(info, value) + self.config.lockButtons = value + self.buttonClass:UpdateButtonLock(self.bar) + end + + function PetHandler:GetLockButtonsCombat() + return self.config.lockButtonsCombat + end + + function PetHandler:SetLockButtonsCombat(info, value) + self.config.lockButtonsCombat = value + self.buttonClass:UpdateButtonLock(self.bar) + end + + function PetHandler:LockButtonsCombatDisabled() + return not self.config.lockButtons + end + + function PetHandler:GetOptions() + return { + type = "group", + name = L["Pet Buttons"], + handler = self, + order = 2, + args = { + lockButtons = { + name = L["Lock Buttons"], + desc = L["Prevents picking up/dragging actions (use SHIFT to override this behavior)"], + order = 2, + type = "toggle", + get = "GetLockButtons", + set = "SetLockButtons", + }, + lockOnlyCombat = { + name = L["Only in Combat"], + desc = L["Only lock the buttons when in combat"], + order = 3, + type = "toggle", + disabled = "LockButtonsCombatDisabled", + get = "GetLockButtonsCombat", + set = "SetLockButtonsCombat", + }, + } + } + end +end + + +------------------------------------- +---- Vehicle Exit button handler ---- +------------------------------------- + +do + local VExitHandler = { + buttonClass = ReAction.Button.VehicleExit, + } + + Editor.buttonHandlers[VExitHandler.buttonClass:GetButtonTypeID()] = VExitHandler + + local meta = { __index = VExitHandler } + + function VExitHandler:New(bar) + return setmetatable( + { + bar = bar, + }, meta) + end + + function VExitHandler:GetConfig() + return self.bar:GetConfig() + end + + function VExitHandler:GetPassengerOnly() + return not self:GetConfig().withControls + end + + function VExitHandler:SetPassengerOnly(info, value) + self:GetConfig().withControls = not value + self.buttonClass:UpdateRegistration(self.bar) + end + + + function VExitHandler:GetOptions() + return { + type = "group", + name = L["Exit Vehicle"], + handler = self, + args = { + passengerOnly = { + name = L["Show only when passenger"], + desc = L["Only show the button when riding as a passenger in a vehicle (no vehicle controls)"], + order = 2, + width = "double", + type = "toggle", + get = "GetPassengerOnly", + set = "SetPassengerOnly", + }, + } + } + end +end + + +------------------------------ +--- Dynamic State options ---- +------------------------------ +do + local ApplyStates = ReAction.Bar.ApplyStates + local CleanupStates = ReAction.Bar.CleanupStates + local SetProperty = ReAction.Bar.SetStateProperty + local GetProperty = ReAction.Bar.GetStateProperty + + -- pre-sorted by the order they should appear in + local rules = { + -- rule fields + { "stance", { {battle = L["Battle Stance"]}, {defensive = L["Defensive Stance"]}, {berserker = L["Berserker Stance"]} } }, + { "form", { {caster = L["Caster Form"]}, {bear = L["Bear Form"]}, {cat = L["Cat Form"]}, {tree = L["Tree of Life"]}, {moonkin = L["Moonkin Form"]} } }, + { "stealth", { {stealth = L["Stealth"]}, {nostealth = L["No Stealth"]}, {shadowdance = L["Shadow Dance"]} } }, + { "shadow", { {shadowform = L["Shadowform"]}, {noshadowform = L["No Shadowform"]} } }, + { "demon", { {demon = L["Demon Form"]}, {nodemon = L["No Demon Form"]} } }, + { "pet", { {pet = L["With Pet"]}, {nopet = L["Without Pet"]} } }, + { "target", { {harm = L["Hostile Target"]}, {help = L["Friendly Target"]}, {notarget = L["No Target"]} } }, + { "focus", { {focusharm = L["Hostile Focus"]}, {focushelp = L["Friendly Focus"]}, {nofocus = L["No Focus"]} } }, + { "possess", { {possess = L["Mind Control"]} } }, + { "vehicle", { {vehicle = L["In a Vehicle"]} } }, + { "group", { {raid = L["Raid"]}, {party = L["Party"]}, {solo = L["Solo"]} } }, + { "combat", { {combat = L["In Combat"]}, {nocombat = L["Out of Combat"]} } }, + } + + local ruleSelect = { } + local ruleMap = { } + local optionMap = setmetatable({},{__mode="k"}) + + + -- unpack rules table into ruleSelect and ruleMap + for _, c in ipairs(rules) do + local rule, fields = unpack(c) + for _, field in ipairs(fields) do + local key, label = next(field) + table.insert(ruleSelect, label) + table.insert(ruleMap, key) + end + end + + local stateOptions = { + ordering = { + name = L["Info"], + order = 1, + type = "group", + args = { + delete = { + name = L["Delete this State"], + order = -1, + type = "execute", + func = "DeleteState", + }, + rename = { + name = L["Name"], + order = 1, + type = "input", + get = "GetName", + set = "SetStateName", + pattern = "^%w*$", + usage = L["State names must be alphanumeric without spaces"], + }, + ordering = { + name = L["Evaluation Order"], + desc = L["State transitions are evaluated in the order listed:\nMove a state up or down to change the order"], + order = 2, + type = "group", + inline = true, + args = { + up = { + name = L["Up"], + order = 1, + type = "execute", + width = "half", + func = "MoveStateUp", + }, + down = { + name = L["Down"], + order = 2, + type = "execute", + width = "half", + func = "MoveStateDown", + } + } + } + } + }, + properties = { + name = L["Properties"], + order = 2, + type = "group", + args = { + desc = { + name = L["Set the properties for the bar when in this state"], + order = 1, + type = "description" + }, + page = { + name = L["Show Page #"], + order = 11, + type = "select", + width = "half", + disabled = "IsPageDisabled", + hidden = "IsPageHidden", + values = "GetPageValues", + set = "SetProp", + get = "GetPage", + }, + hide = { + name = L["Hide Bar"], + order = 90, + type = "toggle", + set = "SetProp", + get = "GetProp", + }, + --[[ BROKEN + keybindState = { + name = L["Override Keybinds"], + desc = L["Set this state to maintain its own set of keybinds which override the defaults when active"], + order = 91, + type = "toggle", + set = "SetProp", + get = "GetProp", + }, ]] + position = { + name = L["Position"], + order = 92, + type = "group", + inline = true, + args = { + anchorEnable = { + name = L["Reposition"], + order = 1, + type = "toggle", + set = "SetProp", + get = "GetProp", + }, + anchorFrame = { + name = L["Anchor Frame"], + order = 2, + type = "select", + values = "GetAnchorFrames", + set = "SetAnchorFrame", + get = "GetAnchorFrame", + disabled = "GetAnchorDisabled", + hidden = "GetAnchorDisabled", + }, + anchorPoint = { + name = L["Point"], + order = 3, + type = "select", + values = pointTable, + set = "SetAnchorPointProp", + get = "GetAnchorPointProp", + disabled = "GetAnchorDisabled", + hidden = "GetAnchorDisabled", + }, + anchorRelPoint = { + name = L["Relative Point"], + order = 4, + type = "select", + values = pointTable, + set = "SetAnchorPointProp", + get = "GetAnchorPointProp", + disabled = "GetAnchorDisabled", + hidden = "GetAnchorDisabled", + }, + anchorX = { + name = L["X Offset"], + order = 5, + type = "range", + min = -100, + max = 100, + step = 1, + set = "SetProp", + get = "GetProp", + disabled = "GetAnchorDisabled", + hidden = "GetAnchorDisabled", + }, + anchorY = { + name = L["Y Offset"], + order = 6, + type = "range", + min = -100, + max = 100, + step = 1, + set = "SetProp", + get = "GetProp", + disabled = "GetAnchorDisabled", + hidden = "GetAnchorDisabled", + }, + }, + }, + scale = { + name = L["Scale"], + order = 93, + type = "group", + inline = true, + args = { + enableScale = { + name = L["Set New Scale"], + order = 1, + type = "toggle", + set = "SetProp", + get = "GetProp", + }, + scale = { + name = L["Scale"], + order = 2, + type = "range", + min = 0.25, + max = 2.5, + step = 0.05, + isPercent = true, + set = "SetProp", + get = "GetScale", + disabled = "GetScaleDisabled", + hidden = "GetScaleDisabled", + }, + }, + }, + alpha = { + name = L["Transparency"], + order = 94, + type = "group", + inline = true, + args = { + enableAlpha = { + name = L["Set Transparency"], + order = 1, + type = "toggle", + set = "SetProp", + get = "GetProp", + }, + alpha = { + name = L["Transparency"], + order = 2, + type = "range", + min = 0, + max = 1, + step = 0.01, + bigStep = 0.05, + isPercent = true, + set = "SetProp", + get = "GetAlpha", + disabled = "GetAlphaDisabled", + hidden = "GetAlphaDisabled", + }, + }, + }, + }, + plugins = { } + }, + rules = { + name = L["Rule"], + order = 3, + type = "group", + args = { + mode = { + name = L["Select this state"], + order = 2, + type = "select", + style = "radio", + values = { + default = L["by default"], + any = L["when ANY of these"], + all = L["when ALL of these"], + custom = L["via custom rule"], + keybind = L["via keybinding"], + }, + set = "SetType", + get = "GetType", + }, + clear = { + name = L["Clear All"], + order = 3, + type = "execute", + hidden = "GetClearAllDisabled", + disabled = "GetClearAllDisabled", + func = "ClearAllConditions", + }, + inputs = { + name = L["Conditions"], + order = 4, + type = "multiselect", + hidden = "GetConditionsDisabled", + disabled = "GetConditionsDisabled", + values = ruleSelect, + set = "SetCondition", + get = "GetCondition", + }, + custom = { + name = L["Custom Rule"], + order = 5, + type = "input", + multiline = true, + hidden = "GetCustomDisabled", + disabled = "GetCustomDisabled", + desc = L["Syntax like macro rules: see preset rules for examples"], + set = "SetCustomRule", + get = "GetCustomRule", + validate = "ValidateCustomRule", + }, + keybind = { + name = L["Keybinding"], + order = 6, + inline = true, + hidden = "GetKeybindDisabled", + disabled = "GetKeybindDisabled", + type = "group", + args = { + desc = { + name = L["Invoking a state keybind toggles an override of all other transition rules."], + order = 1, + type = "description", + }, + keybind = { + name = L["State Hotkey"], + desc = L["Define an override toggle keybind"], + order = 2, + type = "keybinding", + set = "SetKeybind", + get = "GetKeybind", + }, + }, + }, + }, + }, + } + + local StateHandler = { } + local meta = { __index = StateHandler } + + function StateHandler:New( bar, opts ) + local self = setmetatable( + { + bar = bar + }, + meta ) + + function self:GetName() + return opts.name + end + + function self:SetName(name) + opts.name = name + end + + function self:GetOrder() + return opts.order + end + + -- get reference to states table: even if the bar + -- name changes the states table ref won't + self.states = tbuild(bar:GetConfig(), "states") + self.state = tbuild(self.states, opts.name) + + opts.order = self:GetRuleField("order") + if opts.order == nil then + -- add after the highest + opts.order = 100 + for _, state in pairs(self.states) do + local x = tonumber(tfetch(state, "rule", "order")) + if x and x >= opts.order then + opts.order = x + 1 + end + end + self:SetRuleField("order",opts.order) + end + + return self + end + + -- helper methods + + function StateHandler:SetRuleField( key, value, ... ) + tbuild(self.state, "rule", ...)[key] = value + end + + function StateHandler:GetRuleField( ... ) + return tfetch(self.state, "rule", ...) + end + + function StateHandler:FixAll( setkey ) + -- if multiple selections in the same group are chosen when 'all' is selected, + -- keep only one of them. If changing the mode, the first in the fields list will + -- be chosen arbitrarily. Otherwise, if selecting a new checkbox from the field-set, + -- it will be retained. + local notified = false + if self:GetRuleField("type") == "all" then + for _, c in ipairs(rules) do + local rule, fields = unpack(c) + local once = false + if setkey then + for idx, field in ipairs(fields) do + if next(field) == setkey then + once = true + end + end + end + for idx, field in ipairs(fields) do + local key = next(field) + if self:GetRuleField("values",key) then + if once and key ~= setkey then + self:SetRuleField(key,false,"values") + if not setkey and not notified then + ReAction:UserError(L["Warning: one or more incompatible rules were turned off"]) + notified = true + end + end + once = true + end + end + end + end + end + + function StateHandler:GetNeighbors() + local before, after + for k, v in pairs(self.states) do + local o = tonumber(tfetch(v, "rule", "order")) + if o and k ~= self:GetName() then + local obefore = tfetch(self.states,before,"rule","order") + local oafter = tfetch(self.states,after,"rule","order") + if o < self:GetOrder() and (not obefore or obefore < o) then + before = k + end + if o > self:GetOrder() and (not oafter or oafter > o) then + after = k + end + end + end + return before, after + end + + function StateHandler:SwapOrder( a, b ) + -- do options table + local args = optionMap[self.bar].args + args[a].order, args[b].order = args[b].order, args[a].order + -- do profile + a = tbuild(self.states, a, "rule") + b = tbuild(self.states, b, "rule") + a.order, b.order = b.order, a.order + end + + -- handler methods + + function StateHandler:GetProp( info ) + -- gets property of the same name as the options arg + return GetProperty(self.bar, self:GetName(), info[#info]) + end + + function StateHandler:SetProp( info, value ) + -- sets property of the same name as the options arg + SetProperty(self.bar, self:GetName(), info[#info], value) + end + + function StateHandler:DeleteState() + if self.states[self:GetName()] then + self.states[self:GetName()] = nil + ApplyStates(self.bar) + end + optionMap[self.bar].args[self:GetName()] = nil + end + + function StateHandler:SetStateName(info, value) + -- check for existing state name + if self.states[value] then + ReAction:UserError(format(L["State named '%s' already exists"],value)) + return + end + local args = optionMap[self.bar].args + local name = self:GetName() + self.states[value], args[value], self.states[name], args[name] = self.states[name], args[name], nil, nil + self:SetName(value) + ApplyStates(self.bar) + ReAction:ShowEditor(self.bar, moduleID, value) + end + + function StateHandler:MoveStateUp() + local before, after = self:GetNeighbors() + if before then + self:SwapOrder(before, self:GetName()) + ApplyStates(self.bar) + end + end + + function StateHandler:MoveStateDown() + local before, after = self:GetNeighbors() + if after then + self:SwapOrder(self:GetName(), after) + ApplyStates(self.bar) + end + end + + function StateHandler:GetAnchorDisabled() + return not GetProperty(self.bar, self:GetName(), "anchorEnable") + end + + function StateHandler:IsPageDisabled() + local n = self.bar:GetConfig().nPages or 1 + return not (n > 1) + end + + function StateHandler:IsPageHidden() + return not self.bar:GetConfig().nPages + end + + function StateHandler:GetPageValues() + if not self._pagevalues then + self._pagevalues = { } + end + local n = self.bar:GetConfig().nPages + -- cache the results + if self._npages ~= n then + self._npages = n + wipe(self._pagevalues) + for i = 1, n do + self._pagevalues["page"..i] = i + end + end + return self._pagevalues + end + + function StateHandler:GetPage(info) + return self:GetProp(info) or 1 + end + + function StateHandler:GetAnchorFrames(info) + self._anchorframes = self._anchorframes or { } + table.wipe(self._anchorframes) + + table.insert(self._anchorframes, "UIParent") + for name, bar in ReAction:IterateBars() do + table.insert(self._anchorframes, bar:GetFrame():GetName()) + end + return self._anchorframes + end + + function StateHandler:GetAnchorFrame(info) + local value = self:GetProp(info) + for k,v in pairs(self._anchorframes) do + if v == value then + return k + end + end + end + + function StateHandler:SetAnchorFrame(info, value) + local f = _G[self._anchorframes[value]] + if f then + self.bar:SetFrameRef("anchor-"..self:GetName(), f) + self:SetProp(info, f:GetName()) + end + end + + function StateHandler:SetAnchorPointProp(info, value) + self:SetProp(info, value ~= "NONE" and value or nil) + end + + function StateHandler:GetAnchorPointProp(info) + return self:GetProp(info) or "NONE" + end + + function StateHandler:GetScale(info) + return self:GetProp(info) or 1.0 + end + + function StateHandler:GetScaleDisabled() + return not GetProperty(self.bar, self:GetName(), "enableScale") + end + + function StateHandler:GetAlpha(info) + return self:GetProp(info) or 1.0 + end + + function StateHandler:GetAlphaDisabled() + return not GetProperty(self.bar, self:GetName(), "enableAlpha") + end + + function StateHandler:SetType(info, value) + self:SetRuleField("type", value) + self:FixAll() + ApplyStates(self.bar) + end + + function StateHandler:GetType() + return self:GetRuleField("type") + end + + function StateHandler:GetClearAllDisabled() + local t = self:GetRuleField("type") + return not( t == "any" or t == "all" or t == "custom") + end + + function StateHandler:ClearAllConditions() + local t = self:GetRuleField("type") + if t == "custom" then + self:SetRuleField("custom","") + elseif t == "any" or t == "all" then + self:SetRuleField("values", {}) + end + ApplyStates(self.bar) + end + + function StateHandler:GetConditionsDisabled() + local t = self:GetRuleField("type") + return not( t == "any" or t == "all") + end + + function StateHandler:SetCondition(info, key, value) + self:SetRuleField(ruleMap[key], value or nil, "values") + if value then + self:FixAll(ruleMap[key]) + end + ApplyStates(self.bar) + end + + function StateHandler:GetCondition(info, key) + return self:GetRuleField("values", ruleMap[key]) or false + end + + function StateHandler:GetCustomDisabled() + return self:GetRuleField("type") ~= "custom" + end + + function StateHandler:SetCustomRule(info, value) + self:SetRuleField("custom",value) + ApplyStates(self.bar) + end + + function StateHandler:GetCustomRule() + return self:GetRuleField("custom") or "" + end + + function StateHandler:ValidateCustomRule(info, value) + local s = value:gsub("%s","") -- remove all spaces + -- unfortunately %b and captures don't support the '+' notation, or this would be considerably simpler + repeat + if s == "" then + return true + end + local c, r = s:match("(%b[])(.*)") + if c == nil and s and #s > 0 then + return format(L["Invalid custom rule '%s': each clause must appear within [brackets]"],value or "") + end + s = r + until c == nil + return true + end + + function StateHandler:GetKeybindDisabled() + return self:GetRuleField("type") ~= "keybind" + end + + function StateHandler:GetKeybind() + return self:GetRuleField("keybind") + end + + function StateHandler:SetKeybind(info, value) + if value and #value == 0 then + value = nil + end + self:SetRuleField("keybind",value) + ApplyStates(self.bar) + end + + local function CreateStateOptions(bar, name) + local opts = { + type = "group", + name = name, + childGroups = "tab", + args = stateOptions + } + + opts.handler = StateHandler:New(bar,opts) + + return opts + end + + function Editor:CreateStateOptions(bar) + local private = { } + local states = tbuild(bar:GetConfig(), "states") + local options = { + name = L["Dynamic State"], + type = "group", + order = -1, + childGroups = "tree", + disabled = InCombatLockdown, + args = { + __desc__ = { + name = L["States are evaluated in the order they are listed"], + order = 1, + type = "description", + }, + __new__ = { + name = L["New State..."], + order = 2, + type = "group", + args = { + name = { + name = L["State Name"], + desc = L["Set a name for the new state"], + order = 1, + type = "input", + get = function() return private.newstatename or "" end, + set = function(info,value) private.newstatename = value end, + pattern = "^%w*$", + usage = L["State names must be alphanumeric without spaces"], + }, + create = { + name = L["Create State"], + order = 2, + type = "execute", + func = function () + local name = private.newstatename + if states[name] then + ReAction:UserError(format(L["State named '%s' already exists"],name)) + else + -- TODO: select default state options and pass as final argument + states[name] = { } + optionMap[bar].args[name] = CreateStateOptions(bar,name) + ReAction:ShowEditor(bar, moduleID, name) + private.newstatename = "" + end + end, + disabled = function() + local name = private.newstatename or "" + return #name == 0 or name:find("%W") + end, + } + } + } + } + } + for name, config in pairs(states) do + options.args[name] = CreateStateOptions(bar,name) + end + optionMap[bar] = options + return options + end +end + ---- Export to ReAction ---- function ReAction:ShowEditor(bar, ...) @@ -439,22 +1711,3 @@ end end -function ReAction:RegisterBarOptionGenerator( module, func ) - if not module or type(module) ~= "table" then -- doesn't need to be a proper module, strictly - error("ReAction:RegisterBarOptionGenerator() : Invalid module") - end - if type(func) == "string" then - if not module[func] then - error(("ReAction:RegisterBarOptionGenerator() : Invalid method '%s'"):format(func)) - end - elseif func and type(func) ~= "function" then - error("ReAction:RegisterBarOptionGenerator() : Invalid function") - end - self.barOptionGenerators = self.barOptionGenerators or { } - self.barOptionGenerators[module] = func - - if self.editor then - self.editor:RefreshBarOptions() - end -end -
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MultiCastButton.lua Tue Apr 12 16:06:31 2011 -0700 @@ -0,0 +1,779 @@ +local addonName, addonTable = ... +local ReAction = addonTable.ReAction +local L = ReAction.L +local _G = _G +local CreateFrame = CreateFrame +local format = string.format +local unpack = unpack +local GetCVar = GetCVar +local GameTooltip_SetDefaultAnchor = GameTooltip_SetDefaultAnchor +local CooldownFrame_SetTimer = CooldownFrame_SetTimer +local InCombatLockdown = InCombatLockdown +local IsUsableSpell = IsUsableSpell +local IsUsableAction = IsUsableAction +local IsSpellKnown = IsSpellKnown +local IsSpellInRange = IsSpellInRange +local IsActionInRange = IsActionInRange +local GetSpellInfo = GetSpellInfo +local GetSpellCooldown = GetSpellCooldown +local GetActionCooldown = GetActionCooldown +local GetSpellTexture = GetSpellTexture +local GetActionTexture = GetActionTexture +local GetMultiCastTotemSpells = GetMultiCastTotemSpells + +--[[ + Blizzard Constants: + - NUM_MULTI_CAST_BUTTONS_PER_PAGE = 4 + - NUM_MULTI_CAST_PAGES = 3 + - SHAMAN_TOTEM_PRIORITIES = { } -- sets the order of the totems + - TOTEM_MULTI_CAST_SUMMON_SPELLS = { } -- list of summon spellIDs + - TOTEM_MULTI_CAST_RECALL_SPELLS = { } -- list of recall spellIDs + + Blizzard Events: + - UPDATE_MULTI_CAST_ACTIONBAR + + Blizzard APIs: + - GetMultiCastBarOffset() : returns 6 + + - SetMultiCastSpell(actionID, spellID) (protected) OR + SetAttribute("type","multispell") + SetAttribute("action",actionID) + SetAttribute("spell",spellID) + + note: multicast actionID page is NUM_ACTIONBAR_PAGES + GetMultiCastBarOffset(), + so that's action ID 132-144. + + - spell1, spell2, spell3, ... = GetMultiCastTotemSpells(slot) + returns spellIDs for all known totems that fit that slot. This function is available in + the secure environment. + + Blizzard textures: + All the textures for the multicast bar (arrows, empty-slot icons, etc) are part of a single + texture: each texture uses SetTexCoord() to display only a slice of the textures. I suppose + this is to slightly optimize texture load performance, but it makes the UI code more clumsy. + + Each totem button and arrow has a colored border indicating its elemental type. + + TODO: + - make whether to show the colored border configurable (looks really bad with ButtonFacade:Zoomed) + - apply ButtonFacade to the flyout buttons? Or at least zoom the textures slightly? + - use a multiplier with SetTexCoord on totem bar texture? + + Design Notes: + - Only the header has a secure context. All other frames execute in its context. + + - Each button is either type "spell" (summon/recall) or type "action" (totem action IDs are + GetBonusBarOffset()=6, 132-144) with 3 pages of 4 buttons. The paging is controlled by + the summon flyout, which is also paged with the summon spells (the recall button is not paged) + + - A spell list is updated in the secure context at setup time (TODO: redo setup when learning new + spells) with the list of spells known for each slot. + + - Each button (except recall) has an arrow button which appears on mouseover and when clicked + opens the flyout via a wrapped OnClick handler. When the flyout is open, the arrow does not + appear. + + - A single flyout with N+1 (1 slot is to select no totem for the set) flyout-buttons is a child + of the bar. Each time the flyout panel is opened, the individual buttons grab their corresponding + spell/type from the list, according to the slot which opened the flyout. Each button either sets + the current page (summon) or sets a multispell to an actionID via type="multispell". None of them + actually cast any spells (though, I suppose we could modify this so that e.g. configurable + right-click casts the spell). The flyout also has a close button which closes the flyout: the + flyout-open code positions the close button anchored to the last button in the flyout (which + changes dynamically because each slot has a different number of items in the list). + + - Multicast sets are not stances, there's no need (or ability) to handle swapping sets if one of + the summon spells is cast from elsewhere. + + - The default UI has Call of the Elements always selected on UI load. This module remembers the last + selected one and restores it. + + +]]-- + + +-- +-- Secure snippets +-- + +-- bar +local _bar_init = -- function(self) +[[ + -- set up some globals in the secure environment + flyout = self:GetFrameRef("flyout") + flyoutSlot = nil + summonSlot = self:GetAttribute("summonSlot") + recallSlot = self:GetAttribute("recallSlot") + baseActionID = self:GetAttribute("baseActionID") + slotsPerPage = self:GetAttribute("slotsPerPage") + currentPage = currentPage or self:GetAttribute("lastSummon") or 1 + + totemIDsBySlot = newtable() + for i = 1, slotsPerPage do + totemIDsBySlot[i] = self:GetAttribute("TOTEM_PRIORITY_"..i) + end + + -- these are set up in bar:SetupBar() + flyoutChildren = flyoutChildren or newtable() + summonSpells = summonSpells or newtable() +]] + +local _onstate_multispellpage = -- function(self, stateid, newstate) +[[ + currentPage = tonumber(newstate) + control:CallMethod("UpdateLastSummon",currentPage) + control:ChildUpdate() +]] + + +-- buttons +local _childupdate = -- function(self, snippetid, message) +[[ + local t = self:GetAttribute("type") + self:SetAttribute(t, self:GetAttribute(t.."-page"..currentPage)) +]] + +local _onEnter = -- function(self) + -- for whatever reason, RegisterAutoHide is unreliable + -- unless you re-anchor the frame prior to calling it. + -- Even then, it's still not terribly reliable. +[[ + local slot = self:GetAttribute("bar-idx") + local arrow = owner:GetFrameRef("arrow-"..slot) + if arrow and not arrow:IsShown() and not (flyout:IsVisible() and flyoutSlot == slot) then + arrow:ClearAllPoints() + arrow:SetPoint("BOTTOM",self,"TOP",0,0) + arrow:Show() + arrow:RegisterAutoHide(0) + arrow:AddToAutoHide(self) + end +]] + + +-- flyout arrow +local _arrow_openFlyout = -- function(self) +[[ + local slot = self:GetAttribute("bar-idx") + local totemID = totemIDsBySlot[slot - (summonSlot or 0)] + if totemID == 0 then + totemID = "summon" + end + + local lastButton, lastPage + for page, b in ipairs(flyoutChildren) do + b:Hide() + b:SetAttribute("totemSlot",totemID) + if slot == summonSlot then + local spellID = self:GetParent():GetAttribute("spell-page"..page) + if spellID then + b:SetAttribute("type","changePage") + b:SetAttribute("spell",spellID) + b:Show() + lastButton = b + lastPage = page + end + else + local spell = select(page, 0, GetMultiCastTotemSpells(totemID) ) + if spell then + b:SetAttribute("type","multispell") + b:SetAttribute("action", baseActionID + (currentPage - 1)*slotsPerPage + totemID) + b:SetAttribute("spell", spell) + b:Show() + lastButton = b + lastPage = page + end + end + end + + local close = owner:GetFrameRef("close") + if lastButton and close then + close:ClearAllPoints() + close:SetPoint("BOTTOM",lastButton,"TOP",0,0) -- TODO: better anchoring + close:Show() + control:CallMethod("UpdateFlyoutTextures",totemID) + end + + flyout:ClearAllPoints() + flyout:SetPoint("BOTTOM",self,"BOTTOM",0,0) -- TODO: better anchoring + if lastPage then + flyout:SetHeight(lastPage * 27 + (close and close:GetHeight() or 0)) + end + flyout:Show() + flyout:RegisterAutoHide(1) -- TODO: configurable + flyout:AddToAutoHide(owner) + flyoutSlot = slot + self:Hide() +]] + +local _closeFlyout = -- function(self) +[[ + flyout:Hide() +]] + + +-- flyout child buttons +local _flyout_child_preClick = -- function(self, button, down) +[[ + local button = button + if self:GetAttribute("type") == "changePage" then + owner:SetAttribute("state-multispellpage",self:GetAttribute("index")) + self:GetParent():Hide() + return false + else + return nil, "close" + end +]] + +local _flyout_child_postClick = -- function(self, message, button, down) +[[ + if message == "close" then + self:GetParent():Hide() -- hide flyout after selecting + end +]] + + +-- +-- The Blizzard totem bar textures are all actually one big texture, +-- with texcoord offsets. Shamelessly stolen from FrameXML/MultiCastActionBarFrame.lua +-- +local TOTEM_TEXTURE = "Interface\\Buttons\\UI-TotemBar" +local FLYOUT_UP_BUTTON_HL_TCOORDS = { 72/128, 92/128, 88/256, 98/256 } +local FLYOUT_DOWN_BUTTON_HL_TCOORDS = { 72/128, 92/128, 69/256, 79/256 } + +local SLOT_EMPTY_TCOORDS = { + [EARTH_TOTEM_SLOT] = { 66/128, 96/128, 3/256, 33/256 }, + [FIRE_TOTEM_SLOT] = { 67/128, 97/128, 100/256, 130/256 }, + [WATER_TOTEM_SLOT] = { 39/128, 69/128, 209/256, 239/256 }, + [AIR_TOTEM_SLOT] = { 66/128, 96/128, 36/256, 66/256 }, +} + +local SLOT_OVERLAY_TCOORDS = { + [EARTH_TOTEM_SLOT] = { 1/128, 35/128, 172/256, 206/256 }, + [FIRE_TOTEM_SLOT] = { 36/128, 70/128, 172/256, 206/256 }, + [WATER_TOTEM_SLOT] = { 1/128, 35/128, 207/256, 240/256 }, + [AIR_TOTEM_SLOT] = { 36/128, 70/128, 137/256, 171/256 }, +} + +local FLYOUT_UP_BUTTON_TCOORDS = { + ["summon"] = { 99/128, 127/128, 84/256, 102/256 }, + [EARTH_TOTEM_SLOT] = { 99/128, 127/128, 160/256, 178/256 }, + [FIRE_TOTEM_SLOT] = { 99/128, 127/128, 122/256, 140/256 }, + [WATER_TOTEM_SLOT] = { 99/128, 127/128, 199/256, 217/256 }, + [AIR_TOTEM_SLOT] = { 99/128, 127/128, 237/256, 255/256 }, +} + +local FLYOUT_DOWN_BUTTON_TCOORDS = { + ["summon"] = { 99/128, 127/128, 65/256, 83/256 }, + [EARTH_TOTEM_SLOT] = { 99/128, 127/128, 141/256, 159/256 }, + [FIRE_TOTEM_SLOT] = { 99/128, 127/128, 103/256, 121/256 }, + [WATER_TOTEM_SLOT] = { 99/128, 127/128, 180/256, 198/256 }, + [AIR_TOTEM_SLOT] = { 99/128, 127/128, 218/256, 236/256 }, +} + +local FLYOUT_TOP_TCOORDS = { + ["summon"] = { 33/128, 65/128, 1/256, 23/256 }, + [EARTH_TOTEM_SLOT] = { 0/128, 32/128, 46/256, 68/256 }, + [FIRE_TOTEM_SLOT] = { 33/128, 65/128, 46/256, 68/256 }, + [WATER_TOTEM_SLOT] = { 0/128, 32/128, 1/256, 23/256 }, + [AIR_TOTEM_SLOT] = { 0/128, 32/128, 91/256, 113/256 }, +} + +local FLYOUT_MIDDLE_TCOORDS = { + ["summon"] = { 33/128, 65/128, 23/256, 43/256 }, + [EARTH_TOTEM_SLOT] = { 0/128, 32/128, 68/256, 88/256 }, + [FIRE_TOTEM_SLOT] = { 33/128, 65/128, 68/256, 88/256 }, + [WATER_TOTEM_SLOT] = { 0/128, 32/128, 23/256, 43/256 }, + [AIR_TOTEM_SLOT] = { 0/128, 32/128, 113/256, 133/256 }, +} + +local eventList = { + "ACTIONBAR_SLOT_CHANGED", + "ACTIONBAR_UPDATE_STATE", + "ACTIONBAR_UPDATE_USABLE", + "ACTIONBAR_UPDATE_COOLDOWN", + "UPDATE_BINDINGS", + "UPDATE_MULTI_CAST_ACTIONBAR", +} + +-- +-- MultiCast Button class +-- Inherits implementation methods from Action button class, but circumvents the constructor +-- and redefines/removes some methods. +-- +local buttonTypeID = "Totem" +local Super = ReAction.Button +local Action = ReAction.Button.Action +local MultiCast = setmetatable( + { + defaultBarConfig = { + type = buttonTypeID, + btnWidth = 36, + btnHeight = 36, + btnRows = 1, + btnColumns = 6, + spacing = 3, + buttons = { } + }, + + barType = L["Totem Bar"], + buttonTypeID = buttonTypeID + }, + { __index = Action } ) + +ReAction.Button.MultiCast = MultiCast +ReAction:RegisterBarType(MultiCast) + +function MultiCast:New( btnConfig, bar, idx ) + local maxIndex = bar.nTotemSlots or 0 + if bar.summonSlot then + maxIndex = maxIndex + 1 + end + if bar.recallSlot then + maxIndex = maxIndex + 1 + end + + if not bar.hasMulticast or idx > maxIndex then + return false + end + + if idx < 1 then + error("invalid index") + end + + local name = format("ReAction_%s_Totem_%d",bar:GetName(),idx) + + self = Super.New(self, name, btnConfig, bar, idx, "SecureActionButtonTemplate, ActionButtonTemplate" ) + + local barFrame = bar:GetFrame() + local f = self:GetFrame() + + -- attributes + local page = (idx == bar.recallSlot) and 1 or bar:GetConfig().lastSummon or 1 + if idx == bar.recallSlot or idx == bar.summonSlot then + f:SetAttribute("type","spell") + local spells = (idx == bar.summonSlot) and TOTEM_MULTI_CAST_SUMMON_SPELLS or TOTEM_MULTI_CAST_RECALL_SPELLS + f:SetAttribute("spell",spells[page]) + for i, spell in ipairs(spells) do + if spell and IsSpellKnown(spell) then + f:SetAttribute("spell-page"..i, spell) + end + end + else + local offset = bar.summonSlot and 1 or 0 + local slot = SHAMAN_TOTEM_PRIORITIES[idx - offset] + local baseAction = barFrame:GetAttribute("baseActionID") + slot + self.totemSlot = slot + f:SetAttribute("type","action") + f:SetAttribute("action", baseAction + (page - 1) * NUM_MULTI_CAST_BUTTONS_PER_PAGE) + for i = 1, NUM_MULTI_CAST_PAGES do + f:SetAttribute("action-page"..i, baseAction + (i-1) * NUM_MULTI_CAST_BUTTONS_PER_PAGE) + end + if not f.overlayTex then + local tx = f:CreateTexture("OVERLAY") + tx:SetTexture(TOTEM_TEXTURE) + tx:SetTexCoord(unpack(SLOT_OVERLAY_TCOORDS[self.totemSlot])) + tx:SetWidth(34) + tx:SetHeight(34) + tx:SetPoint("CENTER") + tx:Show() + f.overlayTex = tx + end + end + f:SetAttribute("bar-idx",idx) + + -- non secure scripts + f:SetScript("OnEvent", function(frame, ...) self:OnEvent(...) end) + f:SetScript("OnEnter", function(frame) self:OnEnter() end) + f:SetScript("OnLeave", function(frame) self:OnLeave() end) + f:SetScript("OnAttributeChanged", function(frame, attr, value) self:OnAttributeChanged(attr, value) end) + f:SetScript("PostClick", function(frame, ...) self:PostClick(...) end) + + -- secure handlers + if idx ~= bar.recallSlot then + f:SetAttribute("_childupdate",_childupdate) + end + barFrame:WrapScript(f, "OnEnter", _onEnter) + + -- event registration + f:EnableMouse(true) + f:RegisterForClicks("AnyUp") + for _, evt in pairs(eventList) do + f:RegisterEvent(evt) + end + + -- Set up a proxy for the icon texture for use with ButtonFacade + local SetTexCoordRaw = self.frames.icon.SetTexCoord + self.frames.icon.SetTexCoord = function( tx, ... ) + if self:GetIconTexture() == TOTEM_TEXTURE then + SetTexCoordRaw(tx,select(2,self:GetIconTexture())) + else + SetTexCoordRaw(tx,...) + end + end + + -- attach to skinner + bar:SkinButton(self) + + f:Show() + + -- open arrow and flyout background textures + if idx ~= bar.recallSlot then + local arrow = f._arrowFrame or CreateFrame("Button", nil, f, "SecureFrameTemplate") + f._arrowFrame = arrow + arrow:SetWidth(28) + arrow:SetHeight(18) + arrow:SetPoint("BOTTOM",self:GetFrame(),"TOP",0,0) -- TODO: better anchoring + arrow:SetNormalTexture(TOTEM_TEXTURE) + local slot = self.totemSlot or "summon" + arrow:GetNormalTexture():SetTexCoord( unpack(FLYOUT_UP_BUTTON_TCOORDS[slot]) ) + arrow:SetHighlightTexture(TOTEM_TEXTURE) + arrow:GetHighlightTexture():SetTexCoord( unpack(FLYOUT_UP_BUTTON_HL_TCOORDS) ) + arrow:SetAttribute("bar-idx",idx) + arrow:Hide() + barFrame:WrapScript(arrow, "OnClick", _arrow_openFlyout) + barFrame:SetFrameRef("arrow-"..idx,arrow) + end + + self:Refresh() + + return self +end + +function MultiCast:Destroy() + local barFrame = self.bar:GetFrame() + local f = self:GetFrame() + pcall( barFrame.UnwrapScript, barFrame, f, "OnEnter" ) -- ignore errors + if f._arrowFrame then + pcall( barFrame.UnwrapScript, barFrame, f._arrowFrame,"OnClick" ) -- ignore errors + end + Super.Destroy(self) +end + +function MultiCast:Refresh() + Super.Refresh(self) + self:UpdateAction() +end + +function MultiCast:ShowGrid( show ) +end + +function MultiCast:ShowGridTemp( show ) +end + +function MultiCast:AcquireActionID() +end + +function MultiCast:ReleaseActionID() +end + +function MultiCast:UpdateShowGrid() +end + +function MultiCast:UpdateBorder() +end + +function MultiCast:UpdateMacroText() +end + +function MultiCast:UpdateCount() +end + +function MultiCast:UpdateCheckedState() + local action = self:GetActionID() + if action and IsCurrentAction(action) then + self:GetFrame():SetChecked(1) + else + self:GetFrame():SetChecked(0) + end +end + +function MultiCast:RefreshHasActionAttributes() +end + +function MultiCast:UpdateFlash() +end + +function MultiCast:GetIconTexture() + local tx + if self.spellID then + tx = GetSpellTexture(GetSpellInfo(self.spellID)) + elseif self.actionID then + tx = GetActionTexture(self.actionID) + end + if tx then + return tx + else + return TOTEM_TEXTURE, unpack(SLOT_EMPTY_TCOORDS[self.totemSlot or 1]) + end +end + +function MultiCast:UpdateAction() + local action = self:GetActionID() + if action then + if action ~= self.actionID then + self.actionID = action + self:UpdateAll() + end + else + local spellID = self:GetSpellID() + if spellID ~= self.spellID then + self.spellID = spellID + self:UpdateAll() + end + end +end + +function MultiCast:GetActionID(page) + return self:GetFrame():GetAttribute("action") +end + +function MultiCast:GetSpellID(page) + return self:GetFrame():GetAttribute("spell") +end + +function MultiCast:SetActionID( id ) + error("Can not set action ID of multicast buttons") +end + +function MultiCast:SetTooltip() + local barFrame = self:GetFrame() + if GetCVar("UberTooltips") == "1" then + GameTooltip_SetDefaultAnchor(GameTooltip, barFrame) + else + GameTooltip:SetOwner(barFrame) + end + if self.spellID then + GameTooltip:SetSpellByID(self.spellID,false,true) + elseif self.actionID then + GameTooltip:SetAction(self.actionID) + end +end + +function MultiCast:GetUsable() + if self.spellID then + return IsUsableSpell((GetSpellInfo(self.spellID))) + elseif self.actionID then + return IsUsableAction(self.actionID) + end +end + +function MultiCast:GetInRange() + if self.spellID then + return IsSpellInRange((GetSpellInfo(self.spellID))) == 0 + elseif self.actionID then + return IsActionInRange(self.actionID) == 0 + end +end + +function MultiCast:GetCooldown() + if self.spellID then + return GetSpellCooldown((GetSpellInfo(self.spellID))) + elseif self.actionID then + return GetActionCooldown(self.actionID) + else + return 0, 0, 0 + end +end + +function MultiCast:UPDATE_MULTI_CAST_ACTIONBAR() + self:UpdateAll() +end + + +-- +-- flyout setup +-- +local function ShowFlyoutTooltip(frame) + if GetCVar("UberTooltips") == "1" then + GameTooltip_SetDefaultAnchor(GameTooltip, frame) + else + GameTooltip:SetOwner(frame) + end + local spell = frame:GetAttribute("spell") + if spell == nil or spell == 0 then + GameTooltip:SetText(MULTI_CAST_TOOLTIP_NO_TOTEM, HIGHLIGHT_FONT_COLOR.r, HIGHLIGHT_FONT_COLOR.g, HIGHLIGHT_FONT_COLOR.b) + else + GameTooltip:SetSpellByID(spell,false,true) + end +end + +local function HideFlyoutTooltip() + GameTooltip:Hide() +end + +local function UpdateFlyoutIcon(frame) + local spellID = frame:GetAttribute("spell") + if spellID == 0 or spellID == nil then + frame.icon:SetTexture(TOTEM_TEXTURE) + local slot = tonumber(frame:GetAttribute("totemSlot")) or 1 + frame.icon:SetTexCoord( unpack(SLOT_EMPTY_TCOORDS[slot]) ) + else + frame.icon:SetTexture(GetSpellTexture(GetSpellInfo(spellID))) + frame.icon:SetTexCoord(0,1,0,1) + end +end + +function MultiCast:SetupBar( bar ) + Super.SetupBar(self,bar) + + local slot = 0 + local nTotemSlots = 0 + local summonSlot = nil + local recallSlot = nil + + -- figure out the capabilities of the character + for i, spell in ipairs(TOTEM_MULTI_CAST_SUMMON_SPELLS) do + if spell and IsSpellKnown(spell) then + slot = 1 + summonSlot = 1 + end + end + + for i = 1, NUM_MULTI_CAST_BUTTONS_PER_PAGE do + local totem = SHAMAN_TOTEM_PRIORITIES[i]; + if GetTotemInfo(totem) and GetMultiCastTotemSpells(totem) then + nTotemSlots = nTotemSlots + 1 + slot = slot + 1 + end + end + + slot = slot + 1 + for i, spell in ipairs(TOTEM_MULTI_CAST_RECALL_SPELLS) do + if spell and IsSpellKnown(spell) then + recallSlot = slot + end + end + + if nTotemSlots == 0 then + bar.hasMulticast = false -- no multicast capability + return + end + + bar.hasMulticast = true + bar.summonSlot = summonSlot + bar.recallSlot = recallSlot + bar.nTotemSlots = nTotemSlots + + + local f = bar:GetFrame() + + -- init bar secure environment + f:SetAttribute("lastSummon", bar:GetConfig().lastSummon) + f:SetAttribute("summonSlot", summonSlot) + f:SetAttribute("recallSlot", recallSlot) + f:SetAttribute("slotsPerPage", NUM_MULTI_CAST_BUTTONS_PER_PAGE) + f:SetAttribute("baseActionID", (NUM_ACTIONBAR_PAGES + GetMultiCastBarOffset() - 1)*NUM_ACTIONBAR_BUTTONS) + for i, p in ipairs(SHAMAN_TOTEM_PRIORITIES) do + f:SetAttribute("TOTEM_PRIORITY_"..i,p) + end + f:SetAttribute("_onstate-multispellpage", _onstate_multispellpage) + + function f:UpdateLastSummon(value) + bar:GetConfig().lastSummon = value + end + + -- create flyout container frame and close arrow + local flyout = bar._flyoutFrame + if not flyout then + flyout = CreateFrame("Frame", nil, f, "SecureFrameTemplate") + bar._flyoutFrame = flyout + f:SetFrameRef("flyout",flyout) + flyout.buttons = { } + flyout:Hide() + flyout:SetWidth(24) + flyout:SetHeight(1) + flyout:SetPoint("BOTTOM",f,"TOP",0,0) + + local close = CreateFrame("Button", nil, flyout, "SecureFrameTemplate") + close:SetWidth(28) + close:SetHeight(18) + close:SetPoint("BOTTOM",flyout,"TOP") + close:SetNormalTexture(TOTEM_TEXTURE) + close:GetNormalTexture():SetTexCoord(unpack(FLYOUT_DOWN_BUTTON_TCOORDS["summon"])) + close:SetHighlightTexture(TOTEM_TEXTURE) + close:GetHighlightTexture():SetTexCoord( unpack(FLYOUT_DOWN_BUTTON_HL_TCOORDS) ) + f:SetFrameRef("close",close) + f:WrapScript(close, "OnClick", _closeFlyout) + close:Show() + + local midTx = flyout:CreateTexture("BACKGROUND") + midTx:SetWidth(32) + midTx:SetHeight(20) + midTx:SetPoint("BOTTOM") + midTx:SetTexture(TOTEM_TEXTURE) + midTx:SetTexCoord(unpack(FLYOUT_MIDDLE_TCOORDS["summon"])) + midTx:Show() + + local topTx = flyout:CreateTexture("BACKGROUND") + topTx:SetWidth(32) + topTx:SetHeight(20) + topTx:SetTexture(TOTEM_TEXTURE) + midTx:SetTexCoord(unpack(FLYOUT_TOP_TCOORDS["summon"])) + topTx:SetPoint("BOTTOM",midTx,"TOP",0,-10) + topTx:Show() + + function flyout:UpdateTextures(slot) + slot = slot or "summon" + close:GetNormalTexture():SetTexCoord(unpack(FLYOUT_DOWN_BUTTON_TCOORDS[slot])) + midTx:ClearAllPoints() + midTx:SetPoint("BOTTOM") + midTx:SetPoint("TOP",close,"BOTTOM",0,0) + midTx:SetTexCoord(unpack(FLYOUT_MIDDLE_TCOORDS[slot])) + topTx:SetTexCoord(unpack(FLYOUT_TOP_TCOORDS[slot])) + end + + -- create flyout buttons + for i = 1, 10 do -- maximum 9 spells + 1 empty slot + local b = CreateFrame("Button",nil,flyout,"SecureActionButtonTemplate") + b:SetWidth(24) + b:SetHeight(24) + local prev = flyout.buttons[i-1] + b:SetPoint("BOTTOM", prev or flyout, prev and "TOP" or "BOTTOM", 0, 3) -- TODO: better anchoring + b.icon = b:CreateTexture("BACKGROUND") + b.icon:SetAllPoints() + b.icon:Show() + b:SetHighlightTexture("Interface\\Buttons\\ButtonHilight-Square") + b:GetHighlightTexture():SetBlendMode("ADD") + b:RegisterForClicks("AnyUp") + b:SetScript("OnShow",UpdateFlyoutIcon) + b:SetScript("OnEnter",ShowFlyoutTooltip) + b:SetScript("OnLeave",HideFlyoutTooltip) + b:SetAttribute("index",i) + f:SetAttribute("flyout-child-idx",i) + f:SetFrameRef("flyout-child",b) + f:Execute([[ + flyoutChildren = flyoutChildren or newtable() + flyoutChildren[self:GetAttribute("flyout-child-idx")] = self:GetFrameRef("flyout-child") + ]]) + f:WrapScript(b, "OnClick", _flyout_child_preClick, _flyout_child_postClick) + b:Show() + flyout.buttons[i] = b + end + end + + -- scale flyout frame + local scale = bar:GetButtonSize() / 36 + flyout:SetScale(scale) + + function f:UpdateFlyoutTextures(slot) + flyout:UpdateTextures(slot) + end + + -- re-execute setup when new spells are loaded + if not f.events_registered then + f:RegisterEvent("UPDATE_MULTI_CAST_ACTIONBAR") + f:RegisterEvent("PLAYER_ENTERING_WORLD") + -- Bar.frame does not use OnEvent + f:SetScript("OnEvent", + function() + if not InCombatLockdown() then + self:SetupBar(bar) + end + end) + f.events_registered = true + end + + + f:Execute(_bar_init) +end +
--- a/Options.lua Thu Nov 18 13:11:08 2010 -0800 +++ b/Options.lua Tue Apr 12 16:06:31 2011 -0700 @@ -40,10 +40,9 @@ type = "toggle", name = L["Hide Blizzard Action Bars"], desc = L["Hide the default main bar and extra action bars"], - handler = self:GetModule("HideBlizzard"), - get = "IsHidden", - set = "SetHidden", - disabled = "OptionDisabled", + get = "OptionGetHideBlizzardBars", + set = "OptionSetHideBlizzardBars", + disabled = InCombatLockdown, width = "full", order = 3, }, @@ -51,10 +50,9 @@ type = "toggle", name = L["Hide Blizzard Vehicle Bar"], desc = L["Hide the default vechicle action bar"], - handler = self:GetModule("HideBlizzard"), - get = "IsHidden", - set = "SetHidden", - disabled = "OptionDisabled", + get = "OptionGetHideBlizzardVehicleBar", + set = "OptionSetHideBlizzardVehicleBar", + disabled = function() return InCombatLockdown() or ReAction:OptionGetHideBlizzardBars() == false end, width = "full", order = 4, }, @@ -101,10 +99,6 @@ AceConfigDialog:AddToBlizOptions(configID, options.args.profiles.name, configID, "profiles") - self.db.RegisterCallback(self,"OnProfileChanged") - self.db.RegisterCallback(self,"OnProfileReset", "OnProfileChanged") - self.db.RegisterCallback(self,"OnProfileCopied","OnProfileChanged") - SlashCmdList["REACTION"] = function(option) option = string.match(option or "", "^%s*(%S+)") if option == "config" or option == "options" then @@ -139,6 +133,14 @@ showAlert = true, whileDead = true, } + + -- reroute blizzard action bar config to ReAction config window + InterfaceOptionsActionBarsPanel:HookScript("OnShow", + function() + if ReAction:OptionGetHideBlizzardBars() then + ReAction:ShowOptions() + end + end ) end @@ -147,14 +149,6 @@ end -function ReAction:OnProfileChanged() - self:RebuildAll() - if not self.db.global.skipKeybindWarning then - StaticPopup_Show("REACTION_KB_WARN") - end -end - - function ReAction:OptionSetConfigMode(info, value) self:SetConfigMode(value) end @@ -181,6 +175,25 @@ end +function ReAction:OptionSetHideBlizzardBars( info, hide ) + self.db.profile.options.hideBlizzardBars = hide + self:ManageBlizzardBars() +end + +function ReAction:OptionGetHideBlizzardBars() + return self.db.profile.options.hideBlizzardBars +end + +function ReAction:OptionSetHideBlizzardVehicleBar( info, hide ) + self.db.profile.options.hideBlizzardVehicleBar = hide + self:ManageBlizzardBars() +end + +function ReAction:OptionGetHideBlizzardVehicleBar() + return self.db.profile.options.hideBlizzardVehicleBar +end + + -- export to LDB local LDB = LibStub:GetLibrary("LibDataBroker-1.1") if LDB then
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Overlay.lua Tue Apr 12 16:06:31 2011 -0700 @@ -0,0 +1,754 @@ +local addonName, addonTable = ... +local ReAction = addonTable.ReAction +local L = ReAction.L +local LKB = ReAction.LKB +local CreateFrame = CreateFrame +local InCombatLockdown = InCombatLockdown +local floor = math.floor +local min = math.min +local format = string.format +local GameTooltip = GameTooltip +local Bar = ReAction.Bar +local GetSize = Bar.GetSize +local SetSize = Bar.SetSize +local GetButtonSize = Bar.GetButtonSize +local GetButtonGrid = Bar.GetButtonGrid +local SetButtonSize = Bar.SetButtonSize +local SetButtonGrid = Bar.SetButtonGrid +local ApplyAnchor = Bar.ApplyAnchor +local GameTooltipTextRight1 = GameTooltipTextRight1 +local GameTooltipTextRight2 = GameTooltipTextRight2 +local GameTooltipTextRight3 = GameTooltipTextRight3 + +-- +-- Wrap some of the bar manipulators to make them state-aware +-- +local function SetAnchor( bar, point, frame, relPoint, x, y ) + local state = bar:GetSecureState() + if state then + local anchorstate = bar:GetStateProperty(state, "anchorEnable") + if anchorstate then + bar:SetStateProperty(state, "anchorFrame", frame) + bar:SetStateProperty(state, "anchorPoint", point) + bar:SetStateProperty(state, "anchorRelPoint", relPoint) + bar:SetStateProperty(state, "anchorX", x or 0) + bar:SetStateProperty(state, "anchorY", y or 0) + bar:SetAnchor(bar:GetAnchor()) + return + end + end + bar:SetAnchor(point, frame, relPoint, x, y) +end + +local function GetStateScale( bar ) + local state = bar:GetSecureState() + if state and bar:GetStateProperty(state, "enableScale") then + return bar:GetStateProperty(state, "scale") + end +end + +local function SetStateScale( bar, scale ) + local state = bar:GetSecureState() + if state and bar:GetStateProperty(state, "enableScale") then + bar:SetStateProperty(state, "scale", scale) + end +end + + +-- +-- Bar config overlay +-- + +local function GetNormalTextColor() + return 1.0, 1.0, 1.0, 1.0 +end + +local function GetAnchoredTextColor() + return 1.0, 1.0, 1.0, 1.0 +end + +local function GetNormalBgColor() + return 0.7, 0.7, 1.0, 0.3 +end + +local function GetAnchoredBgColor() + return 0.9, 0.2, 0.7, 0.3 +end + +local function StoreSize(bar) + local f = bar:GetFrame() + SetSize( bar, f:GetWidth(), f:GetHeight() ) +end + +local function StoreExtents(bar) + local f = bar:GetFrame() + local p, fr, rp, x, y = f:GetPoint(1) + fr = fr and fr:GetName() or "UIParent" + SetAnchor( bar, p, fr, rp, x, y ) + SetSize( bar, f:GetWidth(), f:GetHeight() ) +end + +local function RecomputeButtonSize(bar) + local w, h = GetSize(bar) + local bw, bh = GetButtonSize(bar) + local r, c, s = GetButtonGrid(bar) + + local scaleW = (floor(w/c) - s) / bw + local scaleH = (floor(h/r) - s) / bh + local scale = min(scaleW, scaleH) + + SetButtonSize(bar, scale * bw, scale * bh, s) +end + +local function ComputeBarScale(bar, overlay) + local w, h = overlay:GetWidth() - 8, overlay:GetHeight() - 8 + local bw, bh = GetButtonSize(bar) + local r, c, s = GetButtonGrid(bar) + + local scaleW = w / (c*(bw+s)) + local scaleH = h / (r*(bh+s)) + local scale = min(scaleW, scaleH) + + if scale > 2.5 then + scale = 2.5 + elseif scale < 0.25 then + scale = 0.25 + end + + return scale +end + +local function RecomputeButtonSpacing(bar) + local w, h = GetSize(bar) + local bw, bh = GetButtonSize(bar) + local r, c, s = GetButtonGrid(bar) + + SetButtonGrid(bar,r,c,min(floor(w/c) - bw, floor(h/r) - bh)) +end + +local function RecomputeGrid(bar) + local w, h = GetSize(bar) + local bw, bh = GetButtonSize(bar) + local r, c, s = GetButtonGrid(bar) + + SetButtonGrid(bar, floor(h/(bh+s)), floor(w/(bw+s)), s) +end + +local function ClampToButtons(bar) + local bw, bh = GetButtonSize(bar) + local r, c, s = GetButtonGrid(bar) + SetSize(bar, (bw+s)*c + 1, (bh+s)*r + 1) +end + +local function HideGameTooltip() + GameTooltip:Hide() +end + +local anchorInside = { inside = true } +local anchorOutside = { outside = true } +local edges = { "BOTTOM", "TOP", "LEFT", "RIGHT" } +local oppositeEdges = { + TOP = "BOTTOM", + BOTTOM = "TOP", + LEFT = "RIGHT", + RIGHT = "LEFT" +} +local pointsOnEdge = { + BOTTOM = { "BOTTOM", "BOTTOMLEFT", "BOTTOMRIGHT", }, + TOP = { "TOP", "TOPLEFT", "TOPRIGHT", }, + RIGHT = { "RIGHT", "BOTTOMRIGHT", "TOPRIGHT", }, + LEFT = { "LEFT", "BOTTOMLEFT", "TOPLEFT", }, +} +local edgeSelector = { + BOTTOM = 1, -- select x of x,y + TOP = 1, -- select x of x,y + LEFT = 2, -- select y of x,y + RIGHT = 2, -- select y of x,y +} +local snapPoints = { + [anchorOutside] = { + BOTTOMLEFT = {"BOTTOMRIGHT","TOPLEFT","TOPRIGHT"}, + BOTTOM = {"TOP"}, + BOTTOMRIGHT = {"BOTTOMLEFT","TOPRIGHT","TOPLEFT"}, + RIGHT = {"LEFT"}, + TOPRIGHT = {"TOPLEFT","BOTTOMRIGHT","BOTTOMLEFT"}, + TOP = {"BOTTOM"}, + TOPLEFT = {"TOPRIGHT","BOTTOMLEFT","BOTTOMRIGHT"}, + LEFT = {"RIGHT"}, + CENTER = {"CENTER"} + }, + [anchorInside] = { + BOTTOMLEFT = {"BOTTOMLEFT"}, + BOTTOM = {"BOTTOM"}, + BOTTOMRIGHT = {"BOTTOMRIGHT"}, + RIGHT = {"RIGHT"}, + TOPRIGHT = {"TOPRIGHT"}, + TOP = {"TOP"}, + TOPLEFT = {"TOPLEFT"}, + LEFT = {"LEFT"}, + CENTER = {"CENTER"} + } +} +local insidePointOffsetFuncs = { + BOTTOMLEFT = function(x, y) return x, y end, + BOTTOM = function(x, y) return 0, y end, + BOTTOMRIGHT = function(x, y) return -x, y end, + RIGHT = function(x, y) return -x, 0 end, + TOPRIGHT = function(x, y) return -x, -y end, + TOP = function(x, y) return 0, -y end, + TOPLEFT = function(x, y) return x, -y end, + LEFT = function(x, y) return x, 0 end, + CENTER = function(x, y) return x, y end, +} +local pointCoordFuncs = { + BOTTOMLEFT = function(f) return f:GetLeft(), f:GetBottom() end, + BOTTOM = function(f) return nil, f:GetBottom() end, + BOTTOMRIGHT = function(f) return f:GetRight(), f:GetBottom() end, + RIGHT = function(f) return f:GetRight(), nil end, + TOPRIGHT = function(f) return f:GetRight(), f:GetTop() end, + TOP = function(f) return nil, f:GetTop() end, + TOPLEFT = function(f) return f:GetLeft(), f:GetTop() end, + LEFT = function(f) return f:GetLeft(), nil end, + CENTER = function(f) return f:GetCenter() end, +} +local edgeBoundsFuncs = { + BOTTOM = function(f) return f:GetLeft(), f:GetRight() end, + LEFT = function(f) return f:GetBottom(), f:GetTop() end +} +edgeBoundsFuncs.TOP = edgeBoundsFuncs.BOTTOM +edgeBoundsFuncs.RIGHT = edgeBoundsFuncs.LEFT +local cornerTexCoords = { + -- ULx, ULy, LLx, LLy, URx, URy, LRx, LRy + TOPLEFT = { 1, 1, 1, 0, 0, 1, 0, 0 }, + TOPRIGHT = { 1, 0, 0, 0, 1, 1, 0, 1 }, + BOTTOMLEFT = { 0, 1, 1, 1, 0, 0, 1, 0 }, + BOTTOMRIGHT = { 0, 0, 0, 1, 1, 0, 1, 1 }, +} + +-- Returns absolute coordinates x,y of the named point 'p' of frame 'f' +local function GetPointCoords( f, p ) + local x, y = pointCoordFuncs[p](f) + if not(x and y) then + local cx, cy = f:GetCenter() + x = x or cx + y = y or cy + end + return x, y +end + + +-- Returns true if frame 'f1' can be anchored to frame 'f2' +local function CheckAnchorable( f1, f2 ) + -- can't anchor a frame to itself or to nil + if f1 == f2 or f2 == nil then + return false + end + + -- can always anchor to UIParent + if f2 == UIParent then + return true + end + + -- also can't do circular anchoring of frames + -- walk the anchor chain, which generally shouldn't be that expensive + -- (who nests draggables that deep anyway?) + for i = 1, f2:GetNumPoints() do + local _, f = f2:GetPoint(i) + if not f then f = f2:GetParent() end + return CheckAnchorable(f1,f) + end + + return true +end + +-- Returns true if frames f1 and f2 specified edges overlap +local function CheckEdgeOverlap( f1, f2, e ) + local l1, u1 = edgeBoundsFuncs[e](f1) + local l2, u2 = edgeBoundsFuncs[e](f2) + return l1 <= l2 and l2 <= u1 or l2 <= l1 and l1 <= u2 +end + +-- Returns true if point p1 on frame f1 overlaps edge e2 on frame f2 +local function CheckPointEdgeOverlap( f1, p1, f2, e2 ) + local l, u = edgeBoundsFuncs[e2](f2) + local x, y = GetPointCoords(f1,p1) + x = select(edgeSelector[e2], x, y) + return l <= x and x <= u +end + +-- Returns the distance between corresponding edges. It is +-- assumed that the passed in edges e1 and e2 are the same or opposites +local function GetEdgeDistance( f1, f2, e1, e2 ) + local x1, y1 = pointCoordFuncs[e1](f1) + local x2, y2 = pointCoordFuncs[e2](f2) + return math.abs((x1 or y1) - (x2 or y2)) +end + +local globalSnapTargets = { [UIParent] = anchorInside } + +local function GetClosestFrameEdge(f1,f2,a) + local dist, edge, opp + if f2:IsVisible() and CheckAnchorable(f1,f2) then + for _, e in pairs(edges) do + local o = a.inside and e or oppositeEdges[e] + if CheckEdgeOverlap(f1,f2,e) then + local d = GetEdgeDistance(f1, f2, e, o) + if not dist or (d < dist) then + dist, edge, opp = d, e, o + end + end + end + end + return dist, edge, opp +end + +local function GetClosestVisibleEdge( f ) + local r, o, e1, e2 + local a = anchorOutside + for _, b in ReAction:IterateBars() do + local d, e, opp = GetClosestFrameEdge(f,b:GetFrame(),a) + if d and (not r or d < r) then + r, o, e1, e2 = d, b:GetFrame(), e, opp + end + end + for f2, a2 in pairs(globalSnapTargets) do + local d, e, opp = GetClosestFrameEdge(f,f2,a2) + if d and (not r or d < r) then + r, o, e1, e2, a = d, f2, e, opp, a2 + end + end + return o, e1, e2, a +end + +local function GetClosestVisiblePoint(f1) + local f2, e1, e2, a = GetClosestVisibleEdge(f1) + if f2 then + local rsq, p, rp, x, y + -- iterate pointsOnEdge in order and use < to prefer edge centers to corners + for _, p1 in ipairs(pointsOnEdge[e1]) do + if CheckPointEdgeOverlap(f1,p1,f2,e2) then + for _, p2 in pairs(snapPoints[a][p1]) do + local x1, y1 = GetPointCoords(f1,p1) + local x2, y2 = GetPointCoords(f2,p2) + local dx = x1 - x2 + local dy = y1 - y2 + local rsq2 = dx*dx + dy*dy + if not rsq or rsq2 < rsq then + rsq, p, rp, x, y = rsq2, p1, p2, dx, dy + end + end + end + end + return f2, p, rp, x, y + end +end + +local function GetClosestPointSnapped(f1, rx, ry, xOff, yOff) + local o, p, rp, x, y = GetClosestVisiblePoint(f1) + local s = false + + local insideOffsetFunc = p and insidePointOffsetFuncs[p] + local coordFunc = p and pointCoordFuncs[p] + if not insideOffsetFunc or not coordFunc then + return + end + + local sx, sy = insideOffsetFunc(xOff or 0, yOff or 0) + local xx, yy = coordFunc(f1) + if xx and yy then + if math.abs(x) <= rx then + if math.abs(y) <= ry then + x = sx + y = sy + s = true + elseif CheckEdgeOverlap(f1,o,"LEFT") then + x = sx + s = true + end + elseif math.abs(y) <= ry and CheckEdgeOverlap(f1,o,"TOP") then + y = sy + s = true + end + elseif xx then + if math.abs(x) <= rx then + x = sx + s = true + if math.abs(y) <= ry then + y = sy + end + end + elseif yy then + if math.abs(y) <= ry then + y = sy + s = true + if math.abs(x) <= rx then + x = sx + end + end + end + + -- correct for some Lua oddities with doubles + if x == -0 then x = 0 end + if y == -0 then y = 0 end + + if s then + return o, p, rp, math.floor(x), math.floor(y) + end +end + +local function CreateSnapIndicator() + local si = CreateFrame("Frame",nil,UIParent) + si:SetFrameStrata("HIGH") + si:SetHeight(16) + si:SetWidth(16) + local tex = si:CreateTexture() + tex:SetAllPoints() + tex:SetTexture("Interface\\AddOns\\ReAction\\img\\lock") + tex:SetBlendMode("ADD") + tex:SetDrawLayer("OVERLAY") + return si +end + +local si1 = CreateSnapIndicator() +local si2 = CreateSnapIndicator() + +local function DisplaySnapIndicator( f, rx, ry, xOff, yOff ) + local o, p, rp, x, y, snap = GetClosestPointSnapped(f, rx, ry, xOff, yOff) + if o then + si1:ClearAllPoints() + si2:ClearAllPoints() + si1:SetPoint("CENTER", f, p, 0, 0) + local xx, yy = pointCoordFuncs[rp](o) + x = math.abs(x) <=rx and xx and 0 or x + y = math.abs(y) <=ry and yy and 0 or y + si2:SetPoint("CENTER", o, rp, x, y) + si1:Show() + si2:Show() + else + if si1:IsVisible() then + si1:Hide() + si2:Hide() + end + end + return o, p +end + +local function HideSnapIndicator() + if si1:IsVisible() then + si1:Hide() + si2:Hide() + end +end + +local function UpdateLabelString(overlay) + local label = overlay.labelString + if label then + local name = overlay.labelName + if name and overlay.labelSubtext then + name = format("%s (%s)", name, overlay.labelSubtext) + end + label:SetText(name or "") + end +end + +local function CreateControls(bar) + local f = bar:GetFrame() + + f:SetMovable(true) + f:SetResizable(true) + + -- child of UIParent so that alpha and scale doesn't propagate to it + local overlay = CreateFrame("Button", nil, UIParent) + overlay:EnableMouse(true) + overlay:SetFrameLevel(10) -- set it above the buttons + overlay:SetPoint("TOPLEFT", f, "TOPLEFT", -4, 4) + overlay:SetPoint("BOTTOMRIGHT", f, "BOTTOMRIGHT", 4, -4) + overlay:SetBackdrop({ + edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border", + tile = true, + tileSize = 16, + edgeSize = 16, + insets = { left = 0, right = 0, top = 0, bottom = 0 }, + }) + + -- textures + local bgTex = overlay:CreateTexture(nil,"BACKGROUND") + bgTex:SetTexture(0.7,0.7,1.0,0.2) + bgTex:SetPoint("TOPLEFT",4,-4) + bgTex:SetPoint("BOTTOMRIGHT",-4,4) + local hTex = overlay:CreateTexture(nil,"HIGHLIGHT") + hTex:SetTexture(0.7,0.7,1.0,0.2) + hTex:SetPoint("TOPLEFT",4,-4) + hTex:SetPoint("BOTTOMRIGHT",-4,4) + hTex:SetBlendMode("ADD") + local aTex = overlay:CreateTexture(nil,"ARTWORK") + aTex:SetTexture("Interface\\AddOns\\ReAction\\img\\lock") + aTex:SetWidth(16) + aTex:SetHeight(16) + aTex:Hide() + + -- label + local label = overlay:CreateFontString(nil,"OVERLAY","GameFontNormalLarge") + label:SetAllPoints() + label:SetJustifyH("CENTER") + label:SetShadowColor(0,0,0,1) + label:SetShadowOffset(3,-3) + label:SetTextColor(GetNormalTextColor()) + label:SetText(bar:GetName()) + label:Show() + overlay.labelString = label + overlay.labelName = bar:GetName() + + local function UpdateAnchorDecoration() + local point, anchor, relPoint, x, y = f:GetPoint(1) + if point then + local ofsx, ofsy = insidePointOffsetFuncs[point](x,y) + if (anchor and anchor ~= UIParent) or (ofsx == 0 and ofsy == 0) then + bgTex:SetTexture( GetAnchoredBgColor() ) + hTex:SetTexture( GetAnchoredBgColor() ) + label:SetTextColor( GetAnchoredTextColor() ) + aTex:ClearAllPoints() + aTex:SetPoint(point) + aTex:Show() + return + end + end + bgTex:SetTexture( GetNormalBgColor() ) + hTex:SetTexture( GetNormalBgColor() ) + label:SetTextColor( GetNormalTextColor() ) + aTex:Hide() + end + + local function StopResize() + f:StopMovingOrSizing() + f.isMoving = false + f:SetScript("OnUpdate",nil) + StoreSize(bar) + ClampToButtons(bar) + ReAction:RefreshEditor() + end + + local function CornerUpdate() + local bw, bh = GetButtonSize(bar) + local r, c, s = GetButtonGrid(bar) + local ss = GetStateScale(bar) + if IsShiftKeyDown() then + if ss then + f:SetMinResize( ((s+bw)*c*0.25)/ss, ((s+bh)*r*0.25)/ss ) + f:SetMaxResize( ((s+bw)*c*2.5 + 1)/ss, ((s+bh)*r*2.5 + 1)/ss ) + scale = ComputeBarScale(bar, overlay) + else + f:SetMinResize( (s+12)*c+1, (s+12)*r+1 ) + f:SetMaxResize( (s+128)*c+1, (s+128)*r+1 ) + RecomputeButtonSize(bar) + end + elseif not ss and IsAltKeyDown() then + f:SetMinResize( bw*c, bh*r ) + f:SetMaxResize( 2*bw*c, 2*bh*r ) + RecomputeButtonSpacing(bar) + else + f:SetMinResize( bw+s+1, bh+s+1 ) + f:SetMaxResize( 50*(bw+s)+1, 50*(bh+s)+1 ) + RecomputeGrid(bar) + end + GameTooltipTextRight2:SetText(format("%d x %d",r,c)) + + local ss = GetStateScale(bar) + if ss then + GameTooltipTextRight4:SetText(format("%d%%", scale*100)) + else + local size = (bw == bh) and tostring(bw) or format("%d x %d",bw,bh) + GameTooltipTextRight3:SetText(size) + GameTooltipTextRight4:SetText(tostring(s)) + end + end + + -- corner drag handles + for _, point in pairs({"BOTTOMLEFT","TOPLEFT","BOTTOMRIGHT","TOPRIGHT"}) do + local corner = CreateFrame("Frame",nil,overlay) + corner:EnableMouse(true) + corner:SetWidth(16) + corner:SetHeight(16) + corner:SetPoint(point) + + local tex = corner:CreateTexture(nil,"HIGHLIGHT") + tex:SetTexture("Interface\\AddOns\\ReAction\\img\\corner") + tex:SetTexCoord(unpack(cornerTexCoords[point])) + tex:SetBlendMode("ADD") + tex:SetAlpha(0.6) + tex:SetAllPoints() + + corner:SetScript("OnMouseDown", + function(_,btn) + f:SetScript("OnUpdate", CornerUpdate) + f:StartSizing(point) + end + ) + corner:SetScript("OnMouseUp", + function() + local ss = GetStateScale(bar) + if ss then + SetStateScale(bar, ComputeBarScale(bar, overlay)) + end + StopResize() + end) + corner:SetScript("OnEnter", + function() + local bw, bh = GetButtonSize(bar) + local r, c, s = bar:GetButtonGrid() + local size = (bw == bh) and tostring(bw) or format("%d x %d",bw,bh) + local ss = GetStateScale(bar) + local state = bar:GetSecureState() + GameTooltip:SetOwner(f, "ANCHOR_"..point) + if ss then + GameTooltip:AddLine(format("%s (%s: %s)", bar:GetName(), L["State"], state)) + else + GameTooltip:AddLine(bar:GetName()) + end + GameTooltip:AddDoubleLine(format("|cffcccccc%s|r %s",L["Drag"],L["to add/remove buttons:"]), format("%d x %d",r,c)) + if ss then + GameTooltip:AddLine(L["State Scale Override"]) + GameTooltip:AddDoubleLine(format("|cff00ff00%s|r %s",L["Hold Shift"],L["to change scale:"]), format("%d%%", bar:GetStateProperty(state,"scale")*100)) + else + GameTooltip:AddDoubleLine(format("|cff00ff00%s|r %s",L["Hold Shift"],L["to resize buttons:"]), tostring(floor(size))) + GameTooltip:AddDoubleLine(format("|cff0033cc%s|r %s",L["Hold Alt"], L["to change spacing:"]), tostring(floor(s))) + end + GameTooltip:Show() + end + ) + corner:SetScript("OnLeave", + function() + GameTooltip:Hide() + f:SetScript("OnUpdate",nil) + end + ) + end + + overlay:RegisterForDrag("LeftButton") + overlay:RegisterForClicks("RightButtonUp") + + overlay:SetScript("OnDragStart", + function() + f:StartMoving() + f.isMoving = true + local w,h = bar:GetButtonSize() + f:ClearAllPoints() + UpdateAnchorDecoration() + f:SetScript("OnUpdate", function() + if IsShiftKeyDown() then + local f, p = DisplaySnapIndicator(f,w,h) + else + HideSnapIndicator() + end + end) + end + ) + + local function UpdateDragTooltip() + GameTooltip:SetOwner(f, "ANCHOR_TOPRIGHT") + local ss = GetStateScale(bar) + local state = bar:GetSecureState() + if ss then + GameTooltip:AddLine(format("%s (%s: %s)", bar:GetName(), L["State"], state)) + else + GameTooltip:AddLine(bar:GetName()) + end + GameTooltip:AddLine(format("|cffcccccc%s|r %s",L["Drag"],L["to move"])) + GameTooltip:AddLine(format("|cff00ff00%s|r %s",L["Hold Shift"],L["to anchor to nearby frames"])) + GameTooltip:AddLine(format("|cff00cccc%s|r %s",L["Right-click"],L["for options..."])) + local point, frame, relpoint, x, y = bar:GetFrame():GetPoint(1) + if point then + local ofsx, ofsy = insidePointOffsetFuncs[point](x,y) + if (frame and frame ~= UIParent) or (ofsx == 0 and ofsy == 0) then + frame = frame or UIParent + GameTooltip:AddLine(format("%s <%s>",L["Currently anchored to"],frame:GetName())) + end + end + GameTooltip:Show() + end + + overlay:SetScript("OnDragStop", + function() + f:StopMovingOrSizing() + f.isMoving = false + f:SetScript("OnUpdate",nil) + + if IsShiftKeyDown() then + local w, h = bar:GetButtonSize() + local a, p, rp, x, y = GetClosestPointSnapped(f,w,h) + if a then + f:ClearAllPoints() + f:SetPoint(p,a,rp,x,y) + end + HideSnapIndicator() + end + + StoreExtents(bar) + ReAction:RefreshEditor() + UpdateDragTooltip() + UpdateAnchorDecoration() + end + ) + + overlay:SetScript("OnEnter", + function() + UpdateDragTooltip() + end + ) + + overlay:SetScript("OnLeave", HideGameTooltip) + + overlay:SetScript("OnClick", + function() + ReAction:ShowEditor(bar) + end + ) + + function overlay:RefreshControls() + UpdateAnchorDecoration() + end + + overlay:SetScript("OnShow", overlay.RefreshControls) + + if ReAction:GetKeybindMode() then + overlay:SetFrameLevel(1) + end + + UpdateLabelString(overlay) + UpdateAnchorDecoration() + + return overlay +end + + +-- export methods to the Bar prototype +Bar.Overlay = { } +function Bar.Overlay:New( bar ) + return setmetatable( {frame = CreateControls(bar)}, {__index=self} ) +end + +function Bar.Overlay:SetLabel(name) + self.frame.labelName = name + UpdateLabelString(self.frame) +end + +function Bar.Overlay:SetLabelSubtext(text) + self.frame.labelSubtext = text + UpdateLabelString(self.frame) +end + +function Bar.Overlay:Show() + self.frame:Show() +end + +function Bar.Overlay:Hide() + self.frame:Hide() +end + +function Bar.Overlay:IsShown() + return self.frame:IsShown() +end + +function Bar.Overlay:RefreshControls() + self.frame:RefreshControls() +end \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/PetActionButton.lua Tue Apr 12 16:06:31 2011 -0700 @@ -0,0 +1,324 @@ +local addonName, addonTable = ... +local ReAction = addonTable.ReAction +local L = ReAction.L +local _G = _G +local CreateFrame = CreateFrame +local format = string.format +local GetCVar = GetCVar +local InCombatLockdown = InCombatLockdown +local GetPetActionInfo = GetPetActionInfo +local GetPetActionSlotUsable = GetPetActionSlotUsable +local GetPetActionCooldown = GetPetActionCooldown +local AutoCastShine_AutoCastStart = AutoCastShine_AutoCastStart +local AutoCastShine_AutoCastStop = AutoCastShine_AutoCastStop +local SetDesaturation = SetDesaturation +local CooldownFrame_SetTimer = CooldownFrame_SetTimer +local GameTooltip_SetDefaultAnchor = GameTooltip_SetDefaultAnchor + +-- +-- Secure snippets +-- These are run within the context of the bar's sandbox, as the +-- buttons themselves do not have their own sandbox. +-- +local _onDragStart = -- function(self, button, kind, value, ...) +[[ + if lockButtons and (PlayerInCombat() or not lockButtonsCombat) and not IsModifiedClick("PICKUPACTION") then + return kind, value, ... + else + return "petaction", self:GetAttribute("action") + end +]] + +local _onReceiveDrag = -- function(self, button, kind, value, ...) +[[ + if kind then -- pet spells on the cursor return nil from GetCursorInfo(), which is very strange + return kind, value, ... + end + return "petaction", self:GetAttribute("action") +]] + +-- +-- private +-- +local eventList = { +"PLAYER_CONTROL_LOST", +"PLAYER_CONTROL_GAINED", +"PLAYER_FARSIGHT_FOCUS_CHANGED", +"UNIT_PET", +"UNIT_FLAGS", +"UNIT_AURA", +"PET_BAR_UPDATE", +"PET_BAR_UPDATE_COOLDOWN", +"PET_BAR_UPDATE_USABLE", +"UPDATE_BINDINGS", +} + +-- +-- Pet Action Button class +-- +local buttonTypeID = "PetAction" +local Super = ReAction.Button +local Pet = setmetatable( + { + defaultBarConfig = { + type = buttonTypeID, + btnWidth = 30, + btnHeight = 30, + btnRows = 1, + btnColumns = 10, + spacing = 8, + buttons = { } + }, + + barType = L["Pet Action Bar"], + buttonTypeID = buttonTypeID + }, + { __index = Super } ) + +ReAction.Button.PetAction = Pet +ReAction:RegisterBarType(Pet) + +function Pet:New( config, bar, idx, idHint ) + local name = format("ReAction_%s_PetAction_%d",bar:GetName(),idx) + + self = Super.New(self, name, config, bar, idx, "SecureActionButtonTemplate, ActionButtonTemplate" ) + + local f = self:GetFrame() + if not f.autoCastTexture then + -- store autocast stuff with the frame for recycling + local tex = f:CreateTexture(nil,"OVERLAY") + tex:SetTexture([[Interface\Buttons\UI-AutoCastableOverlay]]) + tex:SetHeight(58) + tex:SetWidth(58) + tex:SetPoint("CENTER") + f.autoCastTexture = tex + f.autoCastShine = CreateFrame("Frame",name.."Shine",f,"AutoCastShineTemplate") -- create after autocast texture so it's on top + -- move the cooldown around + local cd = self.frames.cooldown + cd:ClearAllPoints() + cd:SetWidth(33) + cd:SetHeight(33) + cd:SetPoint("CENTER", f, "CENTER", -2, -1) + -- resize to 30x30 + f:SetHeight(30) + f:SetWidth(30) + local nt = _G[name.."NormalTexture"] + nt:SetHeight(54) + nt:SetWidth(54) + end + local barFrame = bar:GetFrame() + + -- set up the base action ID + self:SetActionIDPool("pet",10) + config.actionID = self:AcquireActionID(config.actionID, idHint, true) + + -- attribute setup + -- There's no secure way to do PetAutoCastToggle by actionID, so use + -- a click-through proxy to the Blizzard pet buttons for right-click + -- Note that technically this doesn't do PetStopAttack() when + -- IsPetAttackActive() is true: however that's only true when using + -- Eyes of the Beast and appears not to really do anything (at least + -- I can't find any difference) + f:SetAttribute("type","pet") + f:SetAttribute("type2","click") + f:SetAttribute("clickbutton2",_G["PetActionButton"..config.actionID]) + f:SetAttribute("action",config.actionID) + f:SetAttribute("checkselfcast", true) + f:SetAttribute("checkfocuscast", true) + + -- non secure scripts + f:SetScript("OnEvent", function(frame, ...) self:OnEvent(...) end) + f:SetScript("OnEnter", function(frame) self:OnEnter() end) + f:SetScript("OnLeave", function(frame) self:OnLeave() end) + f:SetScript("OnAttributeChanged", function(frame, attr, value) self:OnAttributeChanged(attr, value) end) + f:SetScript("PreClick", function(frame) self:PreClick() end) + f:SetScript("OnDragStart", function(frame) self:OnDragStart() end) + f:SetScript("OnReceiveDrag", function(frame) self:OnReceiveDrag() end) + + -- secure handlers + barFrame:WrapScript(f, "OnDragStart", _onDragStart) + barFrame:WrapScript(f, "OnReceiveDrag", _onReceiveDrag) + + -- event registration + f:EnableMouse(true) + f:RegisterForDrag("LeftButton", "RightButton") + f:RegisterForClicks("AnyUp") + for _, evt in pairs(eventList) do + f:RegisterEvent(evt) + end + + -- attach to skinner + bar:SkinButton(self, + { + AutoCast = f.autoCastShine, + AutoCastable = f.autoCastTexture + }) + + self:Refresh() + f:Show() + + return self +end + +function Pet:SetupBar(bar) + Super.SetupBar(self,bar) + + -- auto show/hide when pet exists + bar:RegisterUnitWatch("pet",true) + + self:UpdateButtonLock(bar) +end + +function Pet:UpdateButtonLock(bar) + local f = bar:GetFrame() + f:SetAttribute("lockbuttons",bar.config.lockButtons) + f:SetAttribute("lockbuttonscombat",bar.config.lockButtonsCombat) + f:Execute( + [[ + lockButtons = self:GetAttribute("lockbuttons") + lockButtonsCombat = self:GetAttribute("lockbuttonscombat") + ]]) +end + +function Pet:Refresh() + Super.Refresh(self) + self:Update() + self:UpdateHotkey() +end + +function Pet:GetActionID() + return self.config.actionID +end + +function Pet:SetActionID(id) + if not InCombatLockdown() then + if id < 0 or id > 10 then + ReAction:UserError(L["Pet action ID range is 1-10"]) + return + end + self.config.actionID = id + f:SetAttribute("clickbutton2",_G["PetActionButton"..id]) + f:SetAttribute("action",id) + self:Update() + self:UpdateHotkey() + end +end + +function Pet:Update() + local action = self.config.actionID + local name, subtext, texture, isToken, isActive, autoCastAllowed, autoCastEnabled = GetPetActionInfo(action) + local f = self:GetFrame() + local icon = self.frames.icon + + if isToken then + icon:SetTexture(_G[texture]) + self.tooltipName = _G[name] + else + icon:SetTexture(texture) + self.tooltipName = name + end + + self.isToken = isToken + self.tooltipSubtext = subtext + f:SetChecked( isActive and 1 or 0 ) + + if autoCastAllowed then + f.autoCastTexture:Show() + else + f.autoCastTexture:Hide() + end + + if autoCastEnabled then + AutoCastShine_AutoCastStart(f.autoCastShine) + else + AutoCastShine_AutoCastStop(f.autoCastShine) + end + + if texture then + if GetPetActionSlotUsable(action) then + SetDesaturation(icon,nil) + else + SetDesaturation(icon,1) + end + icon:Show() + f:SetNormalTexture("Interface\\Buttons\\UI-Quickslot2") + else + icon:Hide() + f:SetNormalTexture("Interface\\Buttons\\UI-Quickslot") + end + + self:UpdateCooldown() +end + +function Pet:UpdateCooldown() + CooldownFrame_SetTimer(self.frames.cooldown, GetPetActionCooldown(self.config.actionID)) +end + +function Pet:SetTooltip() + if self.tooltipName then + local f = self:GetFrame() + local uber = GetCVar("UberTooltips") + if self.isToken or (uber == "0") then + if uber == "0" then + GameTooltip:SetOwner(f, "ANCHOR_RIGHT") + else + GameTooltip_SetDefaultAnchor(GameTooltip, f) + end + GameTooltip:SetText(self.tooltipName) + if self.tooltipSubtext then + GameTooltip:AddLine(self.tooltipSubtext, "", 0.5, 0.5, 0.5) + end + GameTooltip:Show() + else + GameTooltip_SetDefaultAnchor(GameTooltip, f) + GameTooltip:SetPetAction(self.config.actionID) + end + else + GameTooltip:Hide() + end +end + +function Pet:OnEvent(event, unit) + if event =="PET_BAR_UPDATE_COOLDOWN" then + self:UpdateCooldown() + elseif event == "UPDATE_BINDINGS" then + self:UpdateHotkey() + elseif event == "UNIT_PET" then + if unit == "player" then + self:Update() + end + elseif event == "UNIT_FLAGS" or event == "UNIT_AURA" then + if unit == "pet" then + self:Update() + end + else + self:Update() + end +end + +function Pet:OnEnter() + self:SetTooltip() +end + +function Pet:OnLeave() + GameTooltip:Hide() +end + +function Pet:OnAttributeChanged(attr,value) + self:Update() +end + +function Pet:PreClick() + self:GetFrame():SetChecked(0) +end + +function Pet:OnDragStart() + self:SetChecked(0) + self:Update() +end + +function Pet:OnReceiveDrag() + self:SetChecked(0) + self:Update() +end +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Profile.lua Tue Apr 12 16:06:31 2011 -0700 @@ -0,0 +1,157 @@ +local _, addonTable = ... +local ReAction = addonTable.ReAction + +ReAction.PROFILEVERSION_LATEST = 2 + +ReAction.defaultProfile = { + profile = { + dbversion = nil, + bars = { }, + options = { + hideBlizzardBars = false, + hideBlizzardVehicleBar = false, + }, + }, + global = { + skipKeybindWarning = false, + } +} + +function ReAction:UpgradeProfile() + local db = self.db + + if not db.profile.dbversion then + -- upgrade from legacy db to v1 + + -- (1) remove unused defaultBars table (cleanup) + db.profile.defaultBars = nil + + -- (2) HideBlizzard is no longer a module + local hb = db:GetNamespace("HideBlizzard",true) or db:RegisterNamespace("HideBlizzard") + if hb then + db.profile.options.hideBlizzardBars = hb.profile.hide + db.profile.options.hideBlizzardVehicleBar = hb.profile.hideVehicle + hb:ResetProfile() + end + + -- (3) LBF is no longer a module + local bf = db:GetNamespace("ButtonFacade",true) or db:RegisterNamespace("ButtonFacade") + if bf then + for name, bar in pairs(db.profile.bars) do + bar.ButtonFacade = bf.profile[name] or bar.ButtonFacade + end + bf:ResetProfile() + end + + -- (4) Action module uses the bar config directly + local action = db:GetNamespace("Action",true) or db:RegisterNamespace("Action") + if action then + for name, bar in pairs(db.profile.bars) do + local ac = action.profile.bars and action.profile.bars[name] + if ac then + for key, value in pairs(ac) do + bar[key] = value + end + end + end + action:ResetProfile() + end + + -- (5) Bags module uses the bar config directly + local bag = db:GetNamespace("Bag",true) or db:RegisterNamespace("Bag") + if bag then + for name, bar in pairs(db.profile.bars) do + local bc = bag.profile.buttons and bag.profile.buttons[name] + if bc then + bar.buttons = bc + end + end + bag:ResetProfile() + end + + -- (6) Pet module uses the bar config directly + local pet = db:GetNamespace("PetAction",true) or db:RegisterNamespace("PetAction") + if pet then + for name, bar in pairs(db.profile.bars) do + local pc = pet.profile.buttons and pet.profile.buttons[name] + if pc then + bar.buttons = pc + end + end + pet:ResetProfile() + end + + -- (7) Stance module uses the bar config directly + local stance = db:GetNamespace("Stance",true) or db:RegisterNamespace("Stance") + if stance then + for name, bar in pairs(db.profile.bars) do + local sc = stance.profile.buttons and stance.profile.buttons[name] + if sc then + bar.buttons = sc + end + end + stance:ResetProfile() + end + + -- (8) Totem module uses the bar config directly + local totem = db:GetNamespace("Totem",true) or db:RegisterNamespace("Totem") + if totem then + for name, bar in pairs(db.profile.bars) do + local tc = totem.profile.buttons and totem.profile.buttons[name] + if tc then + bar.buttons = tc + end + end + totem:ResetProfile() + end + + -- (9) Vehicle exit button uses the bar config directly + local vehicle = db:GetNamespace("VehicleExit",true) or db:RegisterNamespace("VehicleExit") + if vehicle then + for name, bar in pairs(db.profile.bars) do + local vc = vehicle.profile.buttons and vehicle.profile.buttons[name] + if vc then + bar.buttons = vc + end + end + vehicle:ResetProfile() + end + + db.profile.dbversion = 1 + end + + + if db.profile.dbversion < 2 then + -- upgrade from v1 to v2 + + -- (10) State module uses the bar config directly + local state = db:GetNamespace("State",true) or db:RegisterNamespace("State") + if state then + for name, bar in pairs(db.profile.bars) do + local sc = state.profile.bars and state.profile.bars[name] and state.profile.bars[name].states + if sc then + bar.states = sc + end + end + state:ResetProfile() + end + + db.profile.dbversion = 2 + end + + db.profile.dbversion = self.PROFILEVERSION_LATEST +end + +function ReAction:OnProfileChanged() + self:UpgradeProfile() + self:RebuildAll() + if not self.db.global.skipKeybindWarning then + StaticPopup_Show("REACTION_KB_WARN") -- see Options.lua + end +end + +function ReAction:OnNewProfile() + self.db.profile.dbversion = ReAction.PROFILEVERSION_LATEST + self:OnProfileChanged() +end +
--- a/ReAction.lua Thu Nov 18 13:11:08 2010 -0800 +++ b/ReAction.lua Tue Apr 12 16:06:31 2011 -0700 @@ -3,59 +3,120 @@ local pairs = pairs local type = type local geterrorhandler = geterrorhandler -local LKB = LibStub("LibKeyBound-1.0") local L = LibStub("AceLocale-3.0"):GetLocale("ReAction") +local LKB = LibStub("LibKeyBound-1.0",true) +if not LKB then + LoadAddOn("LibKeyBound-1.0") + LKB = LibStub("LibKeyBound-1.0") +end ------ Utility ------ -local tcopy -do - function tcopy(x) - if type(x) ~= "table" then - return x - end - local r = {} - for k,v in pairs(x) do - r[k] = tcopy(v) - end - return r +-- make a deep copy of a table +local function tcopy(x) + if type(x) ~= "table" then + return x end + local r = {} + for k,v in pairs(x) do + r[k] = tcopy(v) + end + return r end +-- traverse a table tree by key list and fetch the result or first nil +local function tfetch(t, ...) + for i = 1, select('#', ...) do + t = t and t[select(i, ...)] + end + return t +end + +-- traverse a table tree by key list and build tree as necessary +local function tbuild(t, ...) + for i = 1, select('#', ...) do + local key = select(i, ...) + if not t[key] then t[key] = { } end + t = t[key] + end + return t +end + +-- return a new array of keys of table 't', sorted by comparing +-- sub-fields (obtained via tfetch) of the table values +local function fieldsort( t, ... ) + local r = { } + for k in pairs(t) do + table.insert(r,k) + end + local path = { ... } + table.sort(r, function(lhs, rhs) + local olhs = tfetch(t[lhs], unpack(path)) or 0 + local orhs = tfetch(t[rhs], unpack(path)) or 0 + return olhs < orhs + end) + return r +end + +-- store in the addon table +addonTable.tcopy = tcopy +addonTable.tfetch = tfetch +addonTable.tbuild = tbuild +addonTable.fieldsort = fieldsort + ------ Core ------ local ReAction = LibStub("AceAddon-3.0"):NewAddon( "ReAction", "AceEvent-3.0" ) addonTable.ReAction = ReAction -ReAction.version = "1.1" +ReAction.version = GetAddOnMetadata("ReAction","Version") ReAction.L = L ReAction.LKB = LKB +ReAction.barTypes = { } + ------ Handlers ------ function ReAction:OnInitialize() self.db = LibStub("AceDB-3.0"):New("ReAction_DB", - { - profile = { - bars = { }, - defaultBar = { }, - } - }, + self.defaultProfile, true -- use global 'Default' (locale-specific) ) + self:UpgradeProfile() + self.bars = { } - self.defaultBarConfig = { } + + self.LBF = LibStub("LibButtonFacade",true) + if self.LBF then + self.LBF:RegisterSkinCallback("ReAction", self.OnSkinChanged, self) + end + + -- It's fairly normal to use the Blizzard vehicle bar, and to have + -- your regular buttons in the same location. If you do this, and don't + -- bother to hide your buttons, they'll obscure some parts of the vehicle bar. + VehicleMenuBar:SetFrameLevel(VehicleMenuBar:GetFrameLevel()+3) self.callbacks = LibStub("CallbackHandler-1.0"):New(self) + LKB.RegisterCallback(self,"LIBKEYBOUND_ENABLED") LKB.RegisterCallback(self,"LIBKEYBOUND_DISABLED") LKB.RegisterCallback(self, "LIBKEYBOUND_MODE_COLOR_CHANGED","LIBKEYBOUND_ENABLED") + + -- see Profile.lua for these callback implementations + self.db.RegisterCallback(self,"OnProfileChanged") + self.db.RegisterCallback(self,"OnProfileCopied","OnProfileChanged") + self.db.RegisterCallback(self,"OnNewProfile") + self.db.RegisterCallback(self,"OnProfileReset", "OnNewProfile") + self:RegisterEvent("PLAYER_REGEN_DISABLED") + self:RegisterEvent("UPDATE_SHAPESHIFT_FORMS") + self:InitializeOptions() end function ReAction:OnEnable() self:InitializeBars() + self:UPDATE_SHAPESHIFT_FORMS() -- it doesn't fire on a /reloadui end function ReAction:OnDisable() @@ -71,6 +132,16 @@ end end +function ReAction:UPDATE_SHAPESHIFT_FORMS() + -- Re-parse the rules table according to the new form list. + -- This happens both at initial login (after PLAYER_ENTERING_WORLD) + -- as well as when gaining new abilities. + self.Bar:InitRuleFormats() + for _, bar in self:IterateBars() do + bar:ApplyStates() + end +end + function ReAction:LIBKEYBOUND_ENABLED( evt ) self:SetKeybindMode(true) end @@ -79,6 +150,24 @@ return self:SetKeybindMode(false) end +function ReAction:OnSkinChanged( skinID, gloss, backdrop, group, button, colors ) + if group == nil then + -- don't store global + else + -- 'group' is the bar-name + local bar = self:GetBar(group) + if bar then + local c = bar:GetConfig().ButtonFacade + if c then + c.skinID = skinID + c.gloss = gloss + c.backdrop = backdrop + c.colors = colors + end + end + end +end + ------ Methods ------ @@ -110,31 +199,26 @@ function ReAction:CreateBar(name, config, ...) local profile = self.db.profile - if name then - if self.bars[name] then - self:UserError(format(L["ReAction: name '%s' already in use"],name)) - return nil - end - else - local prefix = L["Bar "] - local i = 1 - repeat - name = prefix..i - i = i + 1 - until self.bars[name] == nil + name = tostring(name) + if not name or name == "" then + error("ReAction:CreateBar() - bar name string required") + elseif self.bars[name] then + self:UserError(format(L["ReAction: name '%s' already in use"],name)) + return nil end + local class if type(config) == "string" then - config = self.defaultBarConfig[config] - if not config then - error(("ReAction:CreateBar() - unknown bar type '%s'"):format(tostring(select(1,...)))) + class = self.barTypes[config] + if not class then + error(("ReAction:CreateBar() - unknown bar type '%s'"):format(config)) end - config = tcopy(config) - config.btnRows = select(1,...) or config.btnRows or 1 - config.btnColumns = select(2,...) or config.btnColumns or 12 - config.btnWidth = select(3,...) or config.btnWidth or 36 - config.btnHeight = select(3,...) or config.btnHeight or 36 - config.spacing = select(4,...) or config.spacing or 3 + config = tcopy(class:GetDefaultBarConfig()) + config.btnRows = select(1,...) or config.btnRows + config.btnColumns = select(2,...) or config.btnColumns + config.btnWidth = select(3,...) or config.btnWidth + config.btnHeight = select(3,...) or config.btnHeight + config.spacing = select(4,...) or config.spacing config.width = config.width or config.btnColumns*(config.btnWidth + config.spacing) + 1 config.height = config.height or config.btnRows*(config.btnHeight + config.spacing) + 1 config.anchor = config.anchor or "UIParent" @@ -142,11 +226,16 @@ config.relpoint = config.relpoint or "BOTTOM" config.y = config.y or 200 config.x = config.x or 0 + else + config = config or profile.bars[name] or { } + if not config or not config.type or not self.barTypes[config.type] then + error(("ReAction: Unable to construct/fetch config table for bar '%s'"):format(name)) + end + class = self.barTypes[config.type] end - config = config or profile.bars[name] or tcopy(profile.defaultBar) profile.bars[name] = config - local bar = self.Bar:New( name, config ) -- ReAction.Bar defined in Bar.lua + local bar = self.Bar:New( name, config, class ) -- ReAction.Bar defined in Bar.lua self.bars[name] = bar self.callbacks:Fire("OnCreateBar", bar, name) if self.configMode then @@ -165,24 +254,18 @@ end end -function ReAction:RefreshBar(x) - local bar, name = self:GetBar(x) - if bar and name then - self.callbacks:Fire("OnRefreshBar", bar, name) - end -end - function ReAction:InitializeBars() if not self.barsInitialized then + self:ManageBlizzardBars() + for name, config in pairs(self.db.profile.bars) do if config then self:CreateBar(name, config) end end - -- re-anchor and refresh in case anchor order does not match init order + -- re-anchor in case anchor order does not match init order for name, bar in pairs(self.bars) do bar:ApplyAnchor() - self.callbacks:Fire("OnRefreshBar", bar, name) end self.barsInitialized = true end @@ -226,35 +309,57 @@ function ReAction:EraseBar(x) local bar, name = self:GetBar(x) if bar and name then - self.callbacks:Fire("OnEraseBar", bar, name) self:DestroyBar(bar) self.db.profile.bars[name] = nil end end -function ReAction:RegisterBarType( name, config, isDefaultChoice ) - self.defaultBarConfig[name] = config - if isDefaultChoice then - self.defaultBarConfigChoice = name +local blizzFrames = { + MainMenuBar, + MultiBarLeft, + MultiBarRight, + MultiBarBottomLeft, + MultiBarBottomRight, +} + +local hideFrame = CreateFrame("Frame") +hideFrame:Hide() +local hiddenParents = { } +local function ManageBlizzFrame(f, hide) + if hide and not hiddenParents[f] then + hiddenParents[f] = f:GetParent() + f:SetParent(hideFrame) + elseif not hide and hiddenParents[f] then + f:SetParent(hiddenParents[f]) + hiddenParents[f] = nil + if f:IsShown() then + f:Show() -- refresh + end end - self:RefreshEditor() end -function ReAction:UnregisterBarType( name ) - self.defaultBarConfig[name] = nil - if self.defaultBarConfigChoice == name then - self.defaultBarConfigChoice = nil +function ReAction:ManageBlizzardBars() + for _, f in pairs(blizzFrames) do + ManageBlizzFrame(f, self.db.profile.options.hideBlizzardBars) end - self:RefreshEditor() + ManageBlizzFrame(VehicleMenuBar, self.db.profile.options.hideBlizzardVehicleBar) +end + +function ReAction:RegisterBarType( class, isDefault ) + local name = class:GetButtonTypeID() + self.barTypes[name] = class + if isDefault then + self.defaultBarType = name + end end function ReAction:IterateBarTypes() - return pairs(self.defaultBarConfig) + return pairs(self.barTypes) end -function ReAction:GetBarTypeConfig(name) - if name then - return self.defaultBarConfig[name] +function ReAction:GetDefaultBarConfig(barType) + if barType and self.barTypes[barType] then + return self.barTypes[barType]:GetDefaultBarConfig() end end @@ -267,7 +372,7 @@ end function ReAction:GetDefaultBarType() - return self.defaultBarConfigChoice + return self.defaultBarType end function ReAction:SetConfigMode( mode ) @@ -302,3 +407,18 @@ function ReAction:GetKeybindMode( mode ) return self.kbMode end + + +-- ConfigMode support +CONFIGMODE_CALLBACKS = CONFIGMODE_CALLBACKS or {} + +function CONFIGMODE_CALLBACKS.ReAction( action, mode ) + if action == "ON" then + ReAction:SetConfigMode(true) + elseif action == "OFF" then + ReAction:SetConfigMode(false) + elseif action == "LISTMODES" then + -- no modes + end +end +
--- a/ReAction.toc Thu Nov 18 13:11:08 2010 -0800 +++ b/ReAction.toc Tue Apr 12 16:06:31 2011 -0700 @@ -4,9 +4,8 @@ ## DefaultState: enabled ## LoadOnDemand: 0 ## Author: Flick -## Version: 1.1 +## Version: 1.1 beta 4 ## SavedVariables: ReAction_DB -## OptionalDeps: Ace3, LibKeyBound-1.0 ## X-Embeds: Ace3, LibKeyBound-1.0 ## X-Category: Action Bars ## X-License: MIT
--- a/ReAction.xml Thu Nov 18 13:11:08 2010 -0800 +++ b/ReAction.xml Tue Apr 12 16:06:31 2011 -0700 @@ -6,10 +6,16 @@ <Include file="locale\locale.xml"/> <Script file="ReAction.lua"/> + <Script file="Profile.lua"/> + <Script file="Bar.lua"/> + <Script file="Overlay.lua"/> + <Script file="Button.lua"/> + <Script file="ActionButton.lua"/> + <Script file="PetActionButton.lua"/> + <Script file="StanceButton.lua"/> + <Script file="BagButton.lua"/> + <Script file="VehicleExitButton.lua"/> + <Script file="MultiCastButton.lua"/> <Script file="Options.lua"/> <Script file="Editor.lua"/> - - <Include file="classes\classes.xml"/> - <Include file="modules\modules.xml"/> - </Ui>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/StanceButton.lua Tue Apr 12 16:06:31 2011 -0700 @@ -0,0 +1,198 @@ +local addonName, addonTable = ... +local ReAction = addonTable.ReAction +local L = ReAction.L +local _G = _G +local CreateFrame = CreateFrame +local format = string.format +local GetCVar = GetCVar +local GameTooltip_SetDefaultAnchor = GameTooltip_SetDefaultAnchor +local CooldownFrame_SetTimer = CooldownFrame_SetTimer +local InCombatLockdown = InCombatLockdown +local GetNumShapeshiftForms = GetNumShapeshiftForms +local GetShapeshiftFormInfo = GetShapeshiftFormInfo +local IsUsableSpell = IsUsableSpell +local GetSpellInfo = GetSpellInfo + +-- +-- private +-- +local playerClass = select(2,UnitClass("player")) + +local eventList = { + "PLAYER_REGEN_ENABLED", + "PLAYER_ENTERING_WORLD", + "UPDATE_SHAPESHIFT_FORM", + "UPDATE_SHAPESHIFT_FORMS", + "UPDATE_SHAPESHIFT_USABLE", + "UPDATE_SHAPESHIFT_COOLDOWN", + "UPDATE_BINDINGS", +} + +-- +-- Stance Button class +-- +local buttonTypeID = "Stance" +local Super = ReAction.Button +local Stance = setmetatable( + { + defaultConfig = { + type = buttonTypeID, + btnHeight = 36, + btnWidth = 36, + btnRows = 1, + btnColumns = 6, + spacing = 3 + }, + + barType = L["Stance Bar"], + buttonTypeID = buttonTypeID + }, + { __index = Super } ) + +ReAction.Button.Stance = Stance +ReAction:RegisterBarType(Stance) + +function Stance:New( config, bar, idx, idHint ) + local name = format("ReAction_%s_Stance_%d",bar:GetName(),idx) + + self = Super.New(self, name, config, bar, idx, "SecureActionButtonTemplate, ActionButtonTemplate" ) + + local f = self:GetFrame() + local barFrame = bar:GetFrame() + local config = self:GetConfig() + + -- set up the base stance ID + self:SetActionIDPool("stance",8) + config.stanceID = self:AcquireActionID(config.stanceID, idHint, true) + + -- attribute setup + f:SetAttribute("type","spell") + + -- non secure scripts + f:SetScript("OnEvent", function(frame, ...) self:OnEvent(...) end) + f:SetScript("OnEnter", function(frame) self:OnEnter() end) + f:SetScript("OnLeave", function(frame) self:OnLeave() end) + f:SetScript("PreClick", function(frame, ...) self:PreClick(...) end) + + -- secure handlers + -- (none) + + -- event registration + f:EnableMouse(true) + f:RegisterForClicks("AnyUp") + for _, evt in pairs(eventList) do + f:RegisterEvent(evt) + end + + -- attach to skinner + bar:SkinButton(self) + + -- initial display + if ReAction:GetConfigMode() then + self:GetFrame():Show() + end + + self:Refresh() + + return self +end + +function Stance:GetActionID() + return self.config.stanceID +end + +function Stance:UpdateAction() + if InCombatLockdown() then + self.updatePending = true + else + self.updatePending = false + local idx = self:GetActionID() + local f = self:GetFrame() + if idx > GetNumShapeshiftForms() then + f:Hide() + else + f:SetAttribute("spell", select(2,GetShapeshiftFormInfo(idx))) + f:Show() + self:Update() + end + end +end + +function Stance:Refresh() + Super.Refresh(self) + self:UpdateHotkey() + self:UpdateAction() +end + +function Stance:Update() + local texture, _, isActive, isCastable = GetShapeshiftFormInfo(self:GetActionID()) + + local icon = self.frames.icon + icon:SetTexture(texture) + self:GetFrame():SetChecked( isActive and 1 or 0 ) + if isCastable then + self.frames.hotkey:Show() + icon:SetVertexColor(1.0, 1.0, 1.0) + else + icon:SetVertexColor(0.4, 0.4, 0.4) + end + + self:UpdateCooldown() +end + +function Stance:UpdateCooldown() + local start, duration, enabled = GetShapeshiftFormCooldown(self:GetActionID()) + if start then + CooldownFrame_SetTimer(self.frames.cooldown, start, duration, enabled) + end +end + +function Stance:SetTooltip() + if GetCVar("UberTooltips") == "1" then + GameTooltip_SetDefaultAnchor(GameTooltip, self:GetFrame()) + else + GameTooltip:SetOwner(self:GetFrame(), "ANCHOR_RIGHT") + end + GameTooltip:SetShapeshift(self:GetActionID()) +end + +function Stance:OnEnter() + self:SetTooltip() +end + +function Stance:OnLeave() + GameTooltip:Hide() +end + +function Stance:PreClick() + local f = self:GetFrame() + f:SetChecked( not f:GetChecked() ) +end + +function Stance:OnEvent(event, arg) + if event == "PLAYER_REGEN_ENABLED" then + if self.updatePending then + self:UpdateAction() + end + elseif event == "UPDATE_SHAPESHIFT_COOLDOWN" then + self:UpdateCooldown() + elseif event == "UPDATE_SHAPESHIFT_FORMS" then + self:UpdateAction() + elseif event == "UNIT_AURA" then + if arg == "player" then + self:Update() + end + elseif event == "UPDATE_BINDINGS" then + self:UpdateHotkey() + else + self:Update() + end +end + +function Stance:ShowGridTemp(show) + if show then + self:GetFrame():Show() + else + self:UpdateAction() + end +end
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/VehicleExitButton.lua Tue Apr 12 16:06:31 2011 -0700 @@ -0,0 +1,108 @@ +local addonName, addonTable = ... +local ReAction = addonTable.ReAction +local L = ReAction.L +local format = string.format + +-- +-- VExitButton Button class +-- +local buttonTypeID = "VehicleExit" +local Super = ReAction.Button +local VExitButton = setmetatable( + { + defaultBarConfig = { + type = buttonTypeID , + btnWidth = 36, + btnHeight = 36, + btnRows = 1, + btnColumns = 1, + spacing = 3, + buttons = { } + }, + + barType = L["Exit Vehicle Floater"], + buttonTypeID = buttonTypeID + }, + { __index = Super } ) + +ReAction.Button.VehicleExit = VExitButton +ReAction:RegisterBarType(VExitButton) + +function VExitButton:New( config, bar, idx ) + local name = format("ReAction_%s_VehicleExit_%d",bar:GetName(),idx) + + self = Super.New(self, name, config, bar, idx, "SecureFrameTemplate, ActionButtonTemplate", "Button") + + -- frame setup + local f = self:GetFrame() + self.frames.icon:SetTexture("Interface\\Vehicles\\UI-Vehicles-Button-Exit-Up") + self.frames.icon:SetTexCoord(0.140625, 0.859375, 0.140625, 0.859375) + + -- attribute setup + -- (none) + + -- non secure scripts + f:SetScript("OnClick", VehicleExit) + f:SetScript("OnEnter", function(frame) GameTooltip_AddNewbieTip(frame, LEAVE_VEHICLE, 1.0, 1.0, 1.0, nil) end) + f:SetScript("OnLeave", GameTooltip_Hide) + f:SetScript("OnEvent", function(frame, evt, ...) self:OnEvent(evt,...) end) + + -- event registration + f:EnableMouse(true) + f:RegisterForClicks("AnyUp") + f:RegisterEvent("UPDATE_BINDINGS") + + -- attach to skinner + bar:SkinButton(self) + + self:Refresh() + self:UpdateHotkey() + + return self +end + +function VExitButton:SetupBar(bar) + Super.SetupBar(self,bar) + self:UpdateRegistration(bar) +end + +function VExitButton:GetActionID() + return 1 +end + +function VExitButton:Refresh() + Super.Refresh(self) + -- it seems that setscale kills the texcoord, have to refresh it + self.frames.icon:SetTexCoord(0.140625, 0.859375, 0.140625, 0.859375) +end + +function VExitButton:OnEvent(event, ...) + if self[event] then + self[event](self, event, ...) + end +end + +function VExitButton:UPDATE_BINDINGS() + self:UpdateHotkey() +end + +function VExitButton:UpdateRegistration(bar) + -- auto show/hide when on a vehicle + local config = bar:GetConfig() + local f = bar:GetFrame() + if config.withControls then + if bar.vehicleExitStateRegistered then + UnregisterStateDriver(f, "unitexists") + bar.vehicleExitStateRegistered = false + end + bar:RegisterUnitWatch("vehicle",true) + else + bar:RegisterUnitWatch("vehicle",false) + if not bar.vehicleExitStateRegistered then + f:SetAttribute("unit","vehicle") + RegisterStateDriver(f, "unitexists", "[target=vehicle,exists,novehicleui] show; hide") -- spoof onstate-unitexists + bar.vehicleExitStateRegistered = true + end + end +end +
--- a/classes/ActionButton.lua Thu Nov 18 13:11:08 2010 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,782 +0,0 @@ -local addonName, addonTable = ... -local ReAction = addonTable.ReAction -local L = ReAction.L -local _G = _G -local CreateFrame = CreateFrame -local format = string.format -local IsUsableAction = IsUsableAction -local IsEquippedAction = IsEquippedAction -local IsConsumableAction = IsConsumableAction -local IsStackableAction = IsStackableAction -local GetActionText = GetActionText -local GetCVar = GetCVar -local GameTooltip_SetDefaultAnchor = GameTooltip_SetDefaultAnchor -local IsCurrentAction = IsCurrentAction -local IsAutoRepeatAction = IsAutoRepeatAction -local IsUsableAction = IsUsableAction -local IsAttackAction = IsAttackAction -local CooldownFrame_SetTimer = CooldownFrame_SetTimer -local GetActionCooldown = GetActionCooldown -local GetActionTexture = GetActionTexture -local ATTACK_BUTTON_FLASH_TIME = ATTACK_BUTTON_FLASH_TIME -local TOOLTIP_UPDATE_TIME = TOOLTIP_UPDATE_TIME -local IsActionInRange = IsActionInRange -local InCombatLockdown = InCombatLockdown -local HasAction = HasAction - --- --- Secure snippets --- These are run within the context of the bar's sandbox, as the --- buttons themselves do not have their own sandbox. --- -local _onstate_mc = -- function(self, stateid, newstate) -[[ - local oldMcVehicleState = mcVehicleState - mcVehicleState = newstate - control:ChildUpdate() - if oldMcVehicleState == "vehicle" or mcVehicleState == "vehicle" then - control:ChildUpdate("vehicle") - end -]] - -local _childupdate = -- function(self, snippetid, message) -[[ - local action = nil - if (doVehicle and mcVehicleState == "vehicle") or - (doMindControl and mcVehicleState == "mc") then - local idx = self:GetAttribute("bar-idx") - local maxN = (doVehicle and mcVehicleState == "vehicle") and 7 or 12 - if idx and idx <= maxN then - action = 120 + idx - else - action = 0 - end - elseif state and settings[state] and settings[state].page then - action = self:GetAttribute("action-"..settings[state].page) - end - if action == nil then - action = self:GetAttribute("default-action") - end - - self:SetAttribute("action",action) - - if not self:GetAttribute("showgrid") then - local tempShow = self:GetAttribute("showgrid-temp") - local evtShow = self:GetAttribute("showgrid-event") - if tempShow then tempShow = (tempShow > 0) end - if evtShow then evtShow = (evtShow > 0) end - - if tempShow or evtShow or HasAction(action) then - self:Show() - else - self:Hide() - end - end -]] - -local _childupdate_vehicleExit = -- function(self, snippetid, message) -[[ - local show = (mcVehicleState == "vehicle") - if show and doVehicle then - self:SetAttribute("type","macro") - self:Show() - else - self:SetAttribute("type","action") - end - control:CallMethod("ShowVehicleExit",show) -]] - -local _childupdate_showgrid = -- function(self, snippetid, message) -[[ - showgrid_event = message - self:SetAttribute("showgrid-event",message) - if not self:GetAttribute("showgrid") then - local count = message + (self:GetAttribute("showgrid-temp") or 0) - if count <= 0 then - local action = self:GetAttribute("action") - -- note that HasAction is not updated on the target of a drag until after ACTIONBAR_HIDEGRID - -- so, we set a flag for this button in pre-click/receive-drag and check for it here - if HasAction(action) or self:GetAttribute("showgrid-clicked") then - if not self:IsShown() then - self:Show() - end - self:SetAttribute("showgrid-clicked",false) - else - if self:IsShown() then - self:Hide() - end - end - else - self:Show() - end - end -]] - -local _onDragStart = -- function(self, button, kind, value, ...) -[[ - if lockButtons and (PlayerInCombat() or not lockButtonsCombat) and not IsModifiedClick("PICKUPACTION") then - return kind, value, ... - else - return "action", self:GetAttribute("action") - end -]] - -local _onReceiveDrag = -- function(self, button, kind, value, ...) -[[ - if kind ~= "spell" and kind ~= "item" and kind ~= "macro" then - return kind, value, ... - else - if showgrid_event and showgrid_event > 0 then - self:SetAttribute("showgrid-clicked",true) - end - return "action", self:GetAttribute("action") - end -]] - -local _onClick = -- function(self, button, down) -[[ - if showgrid_event and showgrid_event > 0 then - self:SetAttribute("showgrid-clicked",true) - end - return button -]] - --- --- private --- -local eventList = { - "PLAYER_ENTERING_WORLD", - "ACTIONBAR_PAGE_CHANGED", - "ACTIONBAR_SLOT_CHANGED", - "UPDATE_BINDINGS", - "ACTIONBAR_UPDATE_STATE", - "ACTIONBAR_UPDATE_USABLE", - "ACTIONBAR_UPDATE_COOLDOWN", - "UNIT_INVENTORY_CHANGED", - "LEARNED_SPELL_IN_TAB", - "UPDATE_INVENTORY_ALERTS", - "PLAYER_TARGET_CHANGED", - "TRADE_SKILL_SHOW", - "TRADE_SKILL_CLOSE", - "PLAYER_ENTER_COMBAT", - "PLAYER_LEAVE_COMBAT", - "START_AUTOREPEAT_SPELL", - "STOP_AUTOREPEAT_SPELL", - "UNIT_ENTERED_VEHICLE", - "UNIT_EXITED_VEHICLE", - "COMPANION_UPDATE", -} - --- --- Action Button class --- -local Super = ReAction.Button -local Action = setmetatable( { }, { __index = Super } ) -ReAction.Button.Action = Action - -function Action:New( idx, barConfig, bar, idHint ) - local name = format("ReAction_%s_Action_%d",bar:GetName(),idx) - - self = Super.New(self, name, barConfig.buttons[idx], bar, idx, "SecureActionButtonTemplate, ActionButtonTemplate" ) - self.barConfig = barConfig - - local f = self:GetFrame() - local barFrame = bar:GetFrame() - local config = self:GetConfig() - - self.rangeTimer = TOOLTIP_UPDATE_TIME - - -- set up the base action ID - self:SetActionIDPool("action",120) - config.actionID = self:AcquireActionID(config.actionID, idHint) - self.nPages = 1 - - -- attribute setup - f:SetAttribute("type","action") - f:SetAttribute("checkselfcast", true) - f:SetAttribute("checkfocuscast", true) - f:SetAttribute("action", config.actionID) - f:SetAttribute("default-action", config.actionID) - f:SetAttribute("bar-idx",idx) - f:SetAttribute("showgrid-temp",0) - f:SetAttribute("showgrid-event",0) - f:SetAttribute("showgrid",not self:GetBarConfig().hideEmpty) - - -- non secure scripts - f:SetScript("OnEvent", function(frame, ...) self:OnEvent(...) end) - f:SetScript("OnEnter", function(frame) self:OnEnter() end) - f:SetScript("OnLeave", function(frame) self:OnLeave() end) - f:SetScript("OnAttributeChanged", function(frame, attr, value) self:OnAttributeChanged(attr, value) end) - f:SetScript("PostClick", function(frame, ...) self:PostClick(...) end) - f:SetScript("OnUpdate", function(frame, elapsed) self:OnUpdate(elapsed) end) - f:SetScript("OnDragStart", function(frame) self:OnDragStart() end) - f:SetScript("OnReceiveDrag", function(frame) self:OnReceiveDrag() end) - - -- event registration - f:EnableMouse(true) - f:RegisterForDrag("LeftButton", "RightButton") - f:RegisterForClicks("AnyUp") - for _, evt in pairs(eventList) do - f:RegisterEvent(evt) - end - - -- secure handlers - f:SetAttribute("_childupdate", _childupdate) - f:SetAttribute("_childupdate-showgrid",_childupdate_showgrid) - barFrame:WrapScript(f, "OnDragStart", _onDragStart) - barFrame:WrapScript(f, "OnReceiveDrag", _onReceiveDrag) - barFrame:WrapScript(f, "OnClick", _onClick) - - -- attach to skinner - bar:SkinButton(self) - - -- initial display - if ReAction:GetConfigMode() then - self:ShowGridTemp(true) - end - - self:Refresh() - - return self -end - -function Action:Destroy() - local f = self:GetFrame() - local c = self:GetConfig() - - f:SetAttribute("_childupdate-vehicle",nil) - f:SetAttribute("action",c.actionID) -- so that Super.Destroy releases the right one - - if c.pageactions and #c.pageactions > 1 then - for i = 2, #c.pageactions do - self:ReleaseActionID(c.pageactions[i]) - self:ReleaseActionID(id) - end - end - - Super.Destroy(self) -end - -function Action:GetBarConfig() - -- this is the per-bar Action module config structure, - -- not the config structure of the bar itself - return self.barConfig -end - -function Action:Refresh() - Super.Refresh(self) - self:RefreshPages() - self:InstallVehicle() - self:ShowGrid(not self:GetBarConfig().hideEmpty) - self:UpdateAction() -end - -function Action:InstallVehicle() - local f = self:GetFrame() - if self.idx == 7 and self:GetBarConfig().vehicle then - if not self.vehicleInstalled then - self.vehicleInstalled = true - -- install vehicle-exit button on 7th button (only) - f:SetAttribute("_childupdate-vehicle", _childupdate_vehicleExit) - f:SetAttribute("macrotext","/run VehicleExit()") - self:GetBar():GetFrame().ShowVehicleExit = function(bar,show) - self:ShowVehicleExit(show) - end - end - -- setscale blows away tex coords - self:UpdateIcon() - elseif self.vehicleInstalled then - self.vehicleInstalled = false - f:SetAttribute("_childupdate-vehicle",nil) - f:SetAttribute("macrotext",nil) - end -end - -function Action:ShowGrid( show ) - if not InCombatLockdown() then - self.frame:SetAttribute("showgrid", show) - self:UpdateShowGrid() - end -end - -function Action:ShowGridTemp( show ) - -- This function only modifies the show-grid when out - -- of combat, and is ignored by the secure handler. Use - -- it for configuration modes. - if not InCombatLockdown() then - local count = self.showGridTempCount or 0 - if show then - count = count + 1 - else - count = count - 1 - end - if count < 0 then count = 0 end - self.showGridTempCount = count - self:GetFrame():SetAttribute("showgrid-temp",count) - self:UpdateShowGrid() - end -end - -function Action:UpdateAll() - self:UpdateActionIDLabel(ReAction:GetConfigMode()) - self:UpdateHotkey() - self:UpdateShowGrid() - self:UpdateIcon() - self:UpdateBorder() - self:UpdateMacroText() - self:UpdateCount() - self:UpdateTooltip() - self:UpdateCheckedState() - self:UpdateUsable() - self:UpdateCooldown() - self:UpdateFlash() -end - -function Action:UpdateAction() - local action = self:GetActionID() - if action ~= self.actionID then - self.actionID = action - self:UpdateAll() - end -end - -function Action:UpdateShowGrid() - if not InCombatLockdown() then - local f = self:GetFrame() - local count = (f:GetAttribute("showgrid-event") or 0) + - (self.showGridTempCount or 0) + - (f:GetAttribute("showgrid") and 1 or 0) - - if count <= 0 and not HasAction(self.actionID) then - if f:IsShown() then - f:Hide() - end - elseif not f:IsShown() then - f:Show() - end - end -end - -function Action:UpdateIcon() - local texture, tLeft, tRight, tTop, tBottom = self:GetIconTexture() - local icon = self.frames.icon - local hotkey = self.frames.hotkey - local f = self:GetFrame() - - if self.vehicleExitMode then - texture = "Interface\\Vehicles\\UI-Vehicles-Button-Exit-Up" - icon:SetTexCoord(0.140625, 0.859375, 0.140625, 0.859375) - icon:SetVertexColor(1,1,1) - else - icon:SetTexCoord(0,1,0,1) - end - - if texture then - icon:SetTexture(texture) - if tLeft then - icon:SetTexCoord(tLeft,tRight,tTop,tBottom) - end - icon:Show() - self.rangeTimer = -1 - f:SetNormalTexture("Interface\\Buttons\\UI-Quickslot2") - else - icon:Hide() - self.frames.cooldown:Hide() - self.rangeTimer = nil - f:SetNormalTexture("Interface\\Buttons\\UI-Quickslot") - end -end - -function Action:GetIconTexture() - return GetActionTexture(self.actionID) -end - -function Action:UpdateBorder() - local action = self.actionID - if ReAction:GetKeybindMode() then - self:UpdateKeybindModeDisplay(true) - elseif IsEquippedAction(action) then - self.frames.border:SetVertexColor(0, 1.0, 0, 0.35) - self.frames.border:Show() - else - self.frames.border:Hide() - end -end - -function Action:UpdateMacroText() - local action = self.actionID - if not IsConsumableAction(action) and not IsStackableAction(action) then - self.frames.name:SetText(GetActionText(action)) - else - self.frames.name:SetText("") - end -end - -function Action:UpdateCount() - local action = self.actionID - if IsConsumableAction(action) or IsStackableAction(action) then - self.frames.count:SetText(GetActionCount(action)) - else - self.frames.count:SetText("") - end -end - -function Action:UpdateTooltip() - local f = self:GetFrame() - if GameTooltip:GetOwner() == f then - self:SetTooltip() - end -end - -function Action:SetTooltip() - local f = self:GetFrame() - if GetCVar("UberTooltips") == "1" then - GameTooltip_SetDefaultAnchor(GameTooltip, f) - else - GameTooltip:SetOwner(f,"ANCHOR_RIGHT") - end - GameTooltip:SetAction(self.actionID) -end - -function Action:UpdateCheckedState() - local action = self.actionID - if IsCurrentAction(action) or IsAutoRepeatAction(action) then - self:GetFrame():SetChecked(1) - else - self:GetFrame():SetChecked(0) - end -end - -function Action:UpdateUsable() - local isUsable, notEnoughMana = self:GetUsable() - local noRange = self:GetInRange() - - isUsable = self.vehicleExitMode or (isUsable and not noRange) - - if isUsable then - if self.usableStatus ~= "usable" then - self.frames.icon:SetVertexColor(1.0, 1.0, 1.0) - self.frames.normalTexture:SetVertexColor(1.0, 1.0, 1.0) - self.usableStatus = "usable" - end - elseif noRange then - if self.usableStatus ~= "norange" then - self.frames.icon:SetVertexColor(1.0,0.1,0.1) - self.frames.normalTexture:SetVertexColor(1.0, 1.0, 1.0) - self.usableStatus = "norange" - end - elseif notEnoughMana then - if self.usableStatus ~= "oom" then - self.frames.icon:SetVertexColor(0.5, 0.5, 1.0) - self.frames.normalTexture:SetVertexColor(0.5, 0.5, 1.0) - self.usableStatus = "oom" - end - else - if self.usableStatus ~= "unusable" then - self.frames.icon:SetVertexColor(0.4, 0.4, 0.4) - self.frames.normalTexture:SetVertexColor(1.0, 1.0, 1.0) - self.usableStatus = "unusable" - end - end -end - -function Action:GetUsable() - return IsUsableAction(self.actionID) -end - -function Action:GetInRange() - return IsActionInRange(self.actionID) == 0 -end - -function Action:UpdateCooldown() - CooldownFrame_SetTimer(self.frames.cooldown, self:GetCooldown()) -end - -function Action:GetCooldown() - return GetActionCooldown(self.actionID) -end - -function Action:UpdateFlash() - local action = self.actionID - self:SetFlash( (IsAttackAction(action) and IsCurrentAction(action)) or IsAutoRepeatAction(action) ) -end - -function Action:SetFlash(flash) - if self.flashing ~= flash then - self.flashing = flash - self.flashtime = 0 - if not flash then - self.frames.flash:Hide() - end - self:UpdateCheckedState() - end -end - -function Action:RunFlash(elapsed) - if self.flashing then - local flashtime = self.flashtime - elapsed - self.flashtime = flashtime - if flashtime <= 0 then - local overtime = -flashtime - if overtime >= ATTACK_BUTTON_FLASH_TIME then - overtime = 0 - end - flashtime = ATTACK_BUTTON_FLASH_TIME - overtime - local flash = self.frames.flash - if flash:IsShown() then - flash:Hide() - else - flash:Show() - end - end - end -end - -function Action:RunRangeFinder(elapsed) - local rangeTimer = self.rangeTimer - if rangeTimer then - rangeTimer = rangeTimer - elapsed - self.rangeTimer = rangeTimer - if rangeTimer <= 0 then - self:UpdateUsable() - self.rangeTimer = TOOLTIP_UPDATE_TIME - end - end -end - -function Action:GetActionID(page) - if page == nil then - -- get the effective ID - return self:GetFrame():GetAttribute("action") - else - if page == 1 then - return self.config.actionID - else - return self.config.pageactions and self.config.pageactions[page] or self.config.actionID - end - end -end - -function Action:SetActionID( id, page ) - id = tonumber(id) - page = tonumber(page) - if id == nil or id < 1 or id > 120 then - ReAction:UserError(L["Action ID range is 1-120"]) - return - end - if page and page ~= 1 then - if not self.config.pageactions then - self.config.pageactions = { } - end - self:ReleaseActionID(self.config.pageactions[page]) - self.config.pageactions[page] = id - self:AcquireActionID(self.config.pageactions[page]) - self.frame:SetAttribute("action-page"..page,id) - else - self:ReleaseActionID(self.config.actionID) - self.config.actionID = id - self:AcquireActionID(self.config.actionID) - self.frame:SetAttribute("action",id) - self.frame:SetAttribute("default-action",id) - if self.config.pageactions then - self.config.pageactions[1] = id - self.frame:SetAttribute("action-page1",id) - end - end -end - -function Action:RefreshPages( force ) - local nPages = self:GetBarConfig().nPages - if nPages and (nPages ~= self.nPages or force) then - local f = self:GetFrame() - local c = self.config.pageactions - if nPages > 1 and not c then - c = { } - self.config.pageactions = c - end - for i = 1, nPages do - if i > 1 then - c[i] = self:AcquireActionID(c[i], self.config.actionID + (i-1)*self.bar:GetNumButtons()) - else - c[i] = self.config.actionID -- page 1 is the same as the base actionID - end - f:SetAttribute("action-page"..i,c[i]) - end - for i = nPages+1, #c do - self:ReleaseActionID(c[i]) - c[i] = nil - f:SetAttribute("action-page"..i,nil) - end - self.nPages = nPages - end -end - -function Action.SetupBarHeader( bar, config ) -- call this as a static method - local f = bar:GetFrame() - f:SetAttribute("mindcontrol",config.mindcontrol) - f:SetAttribute("vehicle",config.vehicle) - f:Execute( - [[ - doMindControl = self:GetAttribute("mindcontrol") - doVehicle = self:GetAttribute("vehicle") - control:ChildUpdate() - ]]) - - f:SetAttribute("_onstate-mc", _onstate_mc) - RegisterStateDriver(f, "mc", "[vehicleui] vehicle; [bonusbar:5] mc; none") - - f:SetAttribute("lockbuttons",config.lockButtons) - f:SetAttribute("lockbuttonscombat",config.lockButtonsCombat) - f:Execute( - [[ - lockButtons = self:GetAttribute("lockbuttons") - lockButtonsCombat = self:GetAttribute("lockbuttonscombat") - ]]) -end - - -function Action.SetButtonLock( bar, lock, lockCombat ) -- call this as a static method - local f = bar:GetFrame() - f:SetAttribute("lockbuttons",lock) - f:SetAttribute("lockbuttonscombat",lockCombat) - f:Execute( - [[ - lockButtons = self:GetAttribute("lockbuttons") - lockButtonsCombat = self:GetAttribute("lockbuttonscombat") - ]]) -end - - -function Action:ShowVehicleExit(show) - self.vehicleExitMode = show and self:GetBarConfig().vehicle - self:UpdateIcon() -end - -function Action:OnEnter( ) - self:SetTooltip() -end - -function Action:OnLeave( ) - GameTooltip:Hide() -end - -function Action:OnAttributeChanged( attr, value ) - if attr ~= "statehidden" then - self:UpdateAction() - end -end - -function Action:PostClick( ) - self:UpdateCheckedState() -end - -function Action:OnUpdate( elapsed ) - self:RunFlash(elapsed) - self:RunRangeFinder(elapsed) -end - -function Action:OnDragStart() - self:UpdateCheckedState() - self:UpdateFlash() -end - -function Action:OnReceiveDrag() - self:UpdateCheckedState() - self:UpdateFlash() -end - -function Action:OnEvent(event, ...) - if self[event] then - self[event](self, event, ...) - end -end - -function Action:ACTIONBAR_SLOT_CHANGED(event, action) - if action == 0 or action == self.actionID then - self:UpdateAll() - end -end - -function Action:PLAYER_ENTERING_WORLD() - self:UpdateAction() -end - -function Action:ACTIONBAR_PAGE_CHANGED() - self:UpdateAction() -end - -function Action:UPDATE_BONUS_ACTIONBAR() - self:UpdateAction() -end - -function Action:UPDATE_BINDINGS() - self:UpdateHotkey() -end - -function Action:PLAYER_TARGET_CHANGED() - self.rangeTimer = -1 -end - -function Action:ACTIONBAR_UPDATE_STATE() - self:UpdateCheckedState() -end - -function Action:TRADE_SKILL_SHOW() - self:UpdateCheckedState() -end -Action.TRADE_SKILL_CLOSE = Action.TRADE_SKILL_CLOSE - -function Action:UNIT_ENTERED_VEHICLE(event,unit) - if unit == "player" then - self:UpdateCheckedState() - end -end -Action.UNIT_EXITED_VEHICLE = Action.UNIT_ENTERED_VEHICLE - -function Action:COMPANION_UPDATE(event,unit) - if unit == "mount" then - self:UpdateCheckedState() - end -end - -function Action:ACTIONBAR_UPDATE_USABLE() - self:UpdateUsable() -end - -function Action:ACTIONBAR_UPDATE_COOLDOWN() - self:UpdateCooldown() -end - -function Action:PLAYER_ENTER_COMBAT() - if IsAttackAction(self.actionID) then - self:SetFlash(true) - end -end - -function Action:PLAYER_LEAVE_COMBAT() - if IsAttackAction(self.actionID) then - self:SetFlash(false) - end -end - -function Action:START_AUTOREPEAT_SPELL() - if IsAutoRepeatAction(self.actionID) then - self:SetFlash(true) - end -end - -function Action:STOP_AUTOREPEAT_SPELL() - if not IsAttackAction(self.actionID) then - self:SetFlash(false) - end -end - -function Action:UNIT_INVENTORY_CHANGED(unit) - if unit == "player" then - self:UpdateTooltip() - end -end - -function Action:LEARNED_SPELL_IN_TAB() - self:UpdateTooltip() -end
--- a/classes/BagButton.lua Thu Nov 18 13:11:08 2010 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,439 +0,0 @@ -local addonName, addonTable = ... -local ReAction = addonTable.ReAction -local L = ReAction.L -local _G = _G -local CreateFrame = CreateFrame -local format = string.format -local GetCVar = GetCVar -local ContainerIDToInventoryID = ContainerIDToInventoryID -local NUM_CONTAINER_FRAMES = NUM_CONTAINER_FRAMES -local IsModifiedClick = IsModifiedClick -local CursorHasItem = CursorHasItem -local GetInventoryItemTexture = GetInventoryItemTexture -local GetInventorySlotInfo = GetInventorySlotInfo -local PickupBagFromSlot = PickupBagFromSlot -local CursorCanGoInSlot = CursorCanGoInSlot - --- class declarations -local Super = ReAction.Button -local BagBase = setmetatable( { }, { __index = Super } ) -local Bag = setmetatable( { }, { __index = BagBase } ) -local Backpack = setmetatable( { }, { __index = BagBase } ) -local Keyring = setmetatable( { }, { __index = BagBase } ) - -ReAction.Button.Bag = BagBase - --- --- Bag Button base class --- - -function BagBase:New( idx, moduleConfig, bar, idHint ) - local name = format("ReAction_%s_Bag_%d",bar:GetName(),idx) - - -- use a variable private leaf implementation class - -- unlike traditional OO programming, we can initialize the leaf - -- class before initializing its derived class - local class = Bag - if idx == 1 then - class = Backpack - elseif idx == 6 then - class = Keyring - end - self = class:New(name,moduleConfig.buttons[bar:GetName()][idx], bar, idx) - self.moduleConfig = moduleConfig - - local f = self:GetFrame() - local config = self:GetConfig() - - -- set up the bag ID pool - self:SetActionIDPool("bag",6) - config.bagID = self:AcquireActionID(config.bagID, idHint, true) - - -- non secure scripts - f:SetScript("OnEvent", function(frame, ...) self:OnEvent(...) end) - f:SetScript("OnEnter", function(frame) self:OnEnter() end) - f:SetScript("OnLeave", function(frame) self:OnLeave() end) - f:SetScript("OnReceiveDrag", function(frame, ...) self:OnReceiveDrag(...) end) - f:SetScript("OnClick", function(frame, ...) self:OnClick(...) end) - - -- secure handlers - -- (none) - - -- event registration - f:EnableMouse(true) - f:RegisterForClicks("LeftButtonUp","RightButtonUp") - f:RegisterEvent("UPDATE_BINDINGS") - - -- frame setup - f:SetID(self:GetBagID()) - - if not f.hotkey then - local h = f:CreateFontString(name.."HotKey","ARTWORK","NumberFontNormalSmallGray") - h:SetWidth(30) - h:SetHeight(10) - h:SetJustifyH("RIGHT") - h:SetPoint("TOPLEFT",f,"TOPLEFT",-2,-2) - h:Show() - f.hotkey = h - end - - if not _G[name.."ItemAnim"] then - local anim = CreateFrame("Model",name.."ItemAnim",f,"ItemAnimTemplate") - anim:SetPoint("BOTTOMRIGHT",f,"BOTTOMRIGHT",-10,0) - anim:Hide() - end - - if not f.border then - local b = f:CreateTexture(name.."Border","OVERLAY") - b:SetAllPoints() - b:SetWidth(f:GetWidth()*(62/36)) - b:SetHeight(f:GetHeight()*(62/36)) - b:SetTexture("Interface\\Buttons\UI-ActionButton-Border") - b:SetBlendMode("ADD") - b:Hide() - f.border = b - end - - self.frames.count:SetDrawLayer("ARTWORK") - - self.frames.hotkey = f.hotkey - self.frames.border = _G[name.."Border"] - self.frames.icon = _G[name.."IconTexture"] - self.frames.anim = _G[name.."ItemAnim"] - - -- initial display - if ReAction:GetConfigMode() then - self:GetFrame():Show() - end - - self:Refresh() - - return self -end - -function BagBase:GetModuleConfig() - -- this is the Bag module config structure, - -- not the config structure of the bar itself - return self.moduleConfig -end - -function BagBase:GetActionID() - return self.config.bagID -end - -function BagBase:GetBagID() - return self:GetActionID() - 1 -end - -function BagBase:Refresh() - Super.Refresh(self) - self:UpdateHotkey() - self:Update() -end - -function BagBase:Update() - self:UpdateChecked() -end - -function BagBase:UpdateChecked(force) - if force == nil then - for i=1, NUM_CONTAINER_FRAMES do - local c = _G["ContainerFrame"..i] - if c:GetID() == self:GetBagID() and c:IsShown() then - self:GetFrame():SetChecked(1) - return - end - end - self:GetFrame():SetChecked(0) - end - self:GetFrame():SetChecked(force) -end - -function BagBase:OnEvent(evt, ...) - if self[evt] then - self[evt](self, ...) - end -end - -function BagBase:OnEnter() - self:SetTooltip() -end - -function BagBase:OnLeave() - GameTooltip:Hide() -end - -function BagBase:UPDATE_BINDINGS() - self:UpdateHotkey() -end - - --- --- Bag Button class --- -function Bag:New(name, cfg, bar, idx) - self = Super.New(self, name, cfg, bar, idx, "ItemButtonTemplate" ) - - local f = self:GetFrame() - - f:SetCheckedTexture("Interface\\Buttons\\CheckButtonHilight") - - f:RegisterEvent("CURSOR_UPDATE") - f:RegisterEvent("BAG_UPDATE") - f:RegisterEvent("BAG_CLOSED") - f:SetScript("OnDragStart", function(frame, ...) self:OnDragStart(...) end) - f:RegisterForDrag("LeftButton") - - -- attach to skinner - bar:SkinButton(self, - { - Icon = _G[name.."IconTexture"] - } - ) - - return self -end - -function Bag:GetInventorySlot() - return ContainerIDToInventoryID(self:GetBagID()) -end - -function Bag:GetInventorySlotName() - return "Bag"..(self:GetBagID()-1).."Slot" -end - -function Bag:SetTooltip() - GameTooltip:SetOwner(self:GetFrame(), "ANCHOR_LEFT") - if not GameTooltip:SetInventoryItem("player", self:GetInventorySlot()) then - GameTooltip:SetText(EQUIP_CONTAINER, 1.0, 1.0, 1.0) - end -end - -function Bag:Update() - local texture = GetInventoryItemTexture("player", self:GetInventorySlot()) - if texture then - self.frames.icon:SetTexture(texture) - self.frames.icon:Show() - self:GetFrame():SetNormalTexture("Interface\\Buttons\\UI-Quickslot2") - else - local _, bgTex = GetInventorySlotInfo(self:GetInventorySlotName()) - self.frames.icon:SetTexture(bgTex) - self:GetFrame():SetNormalTexture("Interface\\Buttons\\UI-Quickslot") - end - self:UpdateChecked() -end - -function Bag:OnClick() - if IsModifiedClick("OPENALLBAGS") then - OpenAllBags() - else - if not PutItemInBag(self:GetInventorySlot()) then - ToggleBag(self:GetBagID()) - end - end - self:UpdateChecked() -end - -function Bag:OnReceiveDrag() - if CursorHasItem() then - PutItemInBag(self:GetInventorySlot()) - end -end - -function Bag:OnDragStart() - PickupBagFromSlot(self:GetInventorySlot()) - self:Update() -end - -function Bag:BAG_UPDATE(bag) - if bag == self:GetBagID() then - self:Update() - end -end - -function Bag:CURSOR_UPDATE() - if CursorCanGoInSlot(self:GetInventorySlot()) then - self:GetFrame():LockHighlight() - else - self:GetFrame():UnlockHighlight() - end -end - -function Bag:BAG_CLOSED(bag) - if bag == self:GetBagID() then - self:Update() - end -end - - --- --- Backpack Button class --- -function Backpack:New(name, cfg, bar, idx) - self = Super.New(self, name, cfg, bar, idx, "ItemButtonTemplate" ) - - local f = self:GetFrame() - local icon = _G[name.."IconTexture"] - icon:SetTexture("Interface\\Buttons\\Button-Backpack-Up") - icon:Show() - f:SetCheckedTexture("Interface\\Buttons\\CheckButtonHilight") - f:RegisterEvent("PLAYER_ENTERING_WORLD"); - f:RegisterEvent("CVAR_UPDATE"); - f:SetScript("OnShow", function(frame, ...) self:OnShow(...) end) - - -- attach to skinner - bar:SkinButton(self, - { - Icon = _G[name.."IconTexture"] - } - ) - - return self -end - -function Backpack:Update() - self:UpdateFreeSlots() - self:UpdateChecked() -end - -function Backpack:UpdateFreeSlots() - if GetCVar("displayFreeBagSlots") == "1" then - local total = 0 - for i = BACKPACK_CONTAINER, NUM_BAG_SLOTS do - local free, family = GetContainerNumFreeSlots(i) - if family == 0 then - total = total + free - end - end - - self.freeSlots = total - self.frames.count:SetText(format("(%s)", self.freeSlots)) - self.frames.count:Show() - elseif self.frames.count:IsShown() then - self.frames.count:Hide() - end -end - -function Backpack:SetTooltip() - GameTooltip:SetOwner(self:GetFrame(), "ANCHOR_LEFT") - GameTooltip:SetText(BACKPACK_TOOLTIP, 1.0, 1.0, 1.0) - GameTooltip:AddLine(string.format(NUM_FREE_SLOTS, (self.freeSlots or 0))) - GameTooltip:Show(); -end - -function Backpack:OnShow() - self:UpdateFreeSlots() -end - -function Backpack:OnClick() - if IsModifiedClick("OPENALLBAGS") then - OpenAllBags() - else - if not PutItemInBackpack() then - ToggleBackpack() - end - end - self:UpdateChecked() -end - -function Backpack:OnReceiveDrag() - if CursorHasItem() then - PutItemInBackpack() - end -end - -function Backpack:PLAYER_ENTERING_WORLD() - self:CVAR_UPDATE("DISPLAY_FREE_BAG_SLOTS", GetCVar("displayFreeBagSlots")) -end - -function Backpack:CVAR_UPDATE( cvar, value ) - if cvar == "DISPLAY_FREE_BAG_SLOTS" then - if value == "1" then - self:GetFrame():RegisterEvent("BAG_UPDATE") - else - self:GetFrame():UnregisterEvent("BAG_UPDATE") - end - self:UpdateFreeSlots() - end -end - -function Backpack:BAG_UPDATE(bag) - if bag >= BACKPACK_CONTAINER and bag <= NUM_BAG_SLOTS then - self:UpdateFreeSlots() - end -end - - --- --- Keyring Button class --- -function Keyring:New(name, cfg, bar, idx) - self = Super.New(self, name, cfg, bar, idx, "ItemButtonTemplate" ) - - local f = self:GetFrame() - - f:SetWidth(18) - f:SetHeight(39) - - local tex = f:GetNormalTexture() - tex:ClearAllPoints() - tex:SetAllPoints() - - f:SetNormalTexture("Interface\\Buttons\\UI-Button-KeyRing") - f:SetHighlightTexture("Interface\\Buttons\\UI-Button-KeyRing-Highlight") - f:SetPushedTexture("Interface\\Buttons\\UI-Button-KeyRing-Down") - f:GetNormalTexture():SetTexCoord(0,0.5625,0,0.609375) - f:GetHighlightTexture():SetTexCoord(0,0.5625,0,0.609375) - f:GetPushedTexture():SetTexCoord(0,0.5625,0,0.609375) - - if not HasKey() then - f:Hide() - end - - -- DO NOT attach to skinner - - return self -end - -function Keyring:GetBagID() - return KEYRING_CONTAINER -end - -function Keyring:Refresh() - local f = self:GetFrame() - self.bar:PlaceButton( self, f:GetHeight(), f:GetHeight() ) -- use height x height since it's an odd size - self:UpdateHotkey() - self:Update() -end - -function Keyring:SetTooltip() - GameTooltip:SetOwner(self:GetFrame(), "ANCHOR_RIGHT"); - GameTooltip:SetText(KEYRING, HIGHLIGHT_FONT_COLOR.r, HIGHLIGHT_FONT_COLOR.g, HIGHLIGHT_FONT_COLOR.b); - GameTooltip:AddLine(); -end - -function Keyring:OnReceiveDrag() - if CursorHasItem() then - PutKeyInKeyRing() - end -end - -function Keyring:OnClick() - if CursorHasItem() then - PutKeyInKeyRing() - else - ToggleKeyRing() - end - self:UpdateChecked() -end - -function Keyring:ShowGridTemp(show) - if not HasKey() then - if show then - self:GetFrame():Show() - else - self:GetFrame():Hide() - end - end -end -
--- a/classes/Bar.lua Thu Nov 18 13:11:08 2010 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,826 +0,0 @@ -local addonName, addonTable = ... -local ReAction = addonTable.ReAction -local L = ReAction.L -local LKB = ReAction.LKB -local _G = _G -local CreateFrame = CreateFrame -local floor = math.floor -local fmod = math.fmod -local format = string.format - -local LSG = LibStub("ReAction-LibShowActionGrid-1.0") - ----- Secure snippets ---- -local _reaction_init = -[[ - anchorKeys = newtable("point","relPoint","x","y") - - state = nil - set_state = nil - state_override = nil - unit_exists = nil - - showAll = false - hidden = false - - defaultAlpha = 1.0 - defaultScale = 1.0 - defaultAnchor = newtable() - - activeStates = newtable() - settings = newtable() - extensions = newtable() -]] - -local _reaction_refresh = -[[ - local oldState = state - state = state_override or set_state or state - - local hide = nil - if state then - local settings = settings[state] - if settings then - -- show/hide - hide = settings.hide - -- re-anchor - local old_anchor = activeStates.anchor - activeStates.anchor = settings.anchorEnable and state - if old_anchor ~= activeStates.anchor or not set_state then - if activeStates.anchor then - if settings.anchorPoint then - self:ClearAllPoints() - local f = self:GetAttribute("frameref-anchor-"..state) - if f then - self:SetPoint(settings.anchorPoint, f, settings.anchorRelPoint, settings.anchorX, settings.anchorY) - end - end - elseif defaultAnchor.point then - self:ClearAllPoints() - self:SetPoint(defaultAnchor.point, defaultAnchor.frame, - defaultAnchor.relPoint, defaultAnchor.x, defaultAnchor.y) - end - end - -- re-scale - local old_scale = activeStates.scale - activeStates.scale = settings.enableScale and state - if old_scale ~= activeStates.scale or not set_state then - self:SetScale(activeStates.scale and settings.scale or defaultScale) - end - -- alpha - local old_alpha = activeStates.alpha - activeStates.alpha = settings.enableAlpha and state - if old_alpha ~= activeStates.alpha or not set_state then - self:SetAlpha(activeStates.alpha and settings.alpha or defaultAlpha) - end - end - end - - -- hide if state or unit_exists says to - hide = not showAll and (hide or unithide) - if hide ~= hidden then - hidden = hide - if hide then - self:Hide() - else - self:Show() - end - end - - for _, attr in pairs(extensions) do - control:RunAttribute(attr) - end - - control:ChildUpdate() - - if showAll then - control:CallMethod("UpdateHiddenLabel", state and settings[state] and settings[state].hide) - end - - if oldState ~= state then - control:CallMethod("StateRefresh", state) - end -]] - -local _onstate_reaction = -- function( self, stateid, newstate ) -[[ - set_state = newstate -]] .. _reaction_refresh - -local _onstate_showgrid = -- function( self, stateid, newstate ) -[[ - control:ChildUpdate(stateid,newstate) - control:CallMethod("UpdateShowGrid") -]] - -local _onstate_unitexists = -- function( self, stateid, newstate ) -[[ - unithide = not newstate or newstate == "hide" -]] .. _reaction_refresh - -local _onclick = -- function( self, button, down ) -[[ - if state_override == button then - state_override = nil -- toggle - else - state_override = button - end -]] .. _reaction_refresh - --- For reference --- the option field names must match the field names of the options table, below -local stateProperties = { - hide = true, - --keybindState = true, TODO: broken - anchorEnable = true, - anchorFrame = true, - anchorPoint = true, - anchorRelPoint = true, - anchorX = true, - anchorY = true, - enableScale = true, - scale = true, - enableAlpha = true, - alpha = true, -} - - ----- Utility functions ---- - --- traverse a table tree by key list and fetch the result or first nil -local function tfetch(t, ...) - for i = 1, select('#', ...) do - t = t and t[select(i, ...)] - end - return t -end - --- traverse a table tree by key list and build tree as necessary -local function tbuild(t, ...) - for i = 1, select('#', ...) do - local key = select(i, ...) - if not t[key] then t[key] = { } end - t = t[key] - end - return t -end - --- return a new array of keys of table 't', sorted by comparing --- sub-fields (obtained via tfetch) of the table values -local function fieldsort( t, ... ) - local r = { } - for k in pairs(t) do - table.insert(r,k) - end - local path = { ... } - table.sort(r, function(lhs, rhs) - local olhs = tfetch(t[lhs], unpack(path)) or 0 - local orhs = tfetch(t[rhs], unpack(path)) or 0 - return olhs < orhs - end) - return r -end - - ----- Bar class ---- -local Bar = { } -local weak = { __mode = "k" } -local frameList = { } - -ReAction.Bar = Bar -- export to ReAction - -function Bar:New( name, config ) - if type(config) ~= "table" then - error("ReAction.Bar: config table required") - end - - -- create new self - self = setmetatable( - { - config = config, - name = name, - buttons = setmetatable( { }, weak ), - width = config.width or 480, - height = config.height or 40, - }, - {__index = self} ) - - -- The frame type is 'Button' in order to have an OnClick handler. However, the frame itself is - -- not mouse-clickable by the user. - local parent = config.parent and (ReAction:GetBar(config.parent) or _G[config.parent]) or UIParent - name = name and "ReAction-"..name - local f = name and frameList[name] - if not f then - f = CreateFrame("Button", name, parent, "SecureHandlerStateTemplate, SecureHandlerClickTemplate") - if name then - frameList[name] = f - end - end - f:SetFrameStrata("MEDIUM") - f:SetWidth(self.width) - f:SetHeight(self.height) - f:SetAlpha(config.alpha or 1.0) - f:Show() - f:EnableMouse(false) - f:SetClampedToScreen(true) - LSG:AddFrame(f) - - -- secure handlers - f:Execute(_reaction_init) - f:SetAttribute("_onstate-reaction", _onstate_reaction) - f:SetAttribute("_onstate-showgrid", _onstate_showgrid) - f:SetAttribute("_onstate-unitexists", _onstate_unitexists) - f:SetAttribute("_onclick", _onclick) - - -- secure handler CallMethod()s - f.UpdateShowGrid = function() self:UpdateShowGrid() end - f.StateRefresh = function() self:RefreshControls() end - f.UpdateHiddenLabel = function(f,hidden) self:SetLabelSubtext(hidden and L["Hidden"]) end - - -- Override the default frame accessor to provide strict read-only access - function self:GetFrame() - return f - end - - self:ApplyAnchor() - self:SetConfigMode(ReAction:GetConfigMode()) - self:SetKeybindMode(ReAction:GetKeybindMode()) - - ReAction.RegisterCallback(self, "OnConfigModeChanged") - - return self -end - -function Bar:Destroy() - local f = self:GetFrame() - f:UnregisterAllEvents() - self:ShowControls(false) - ReAction.UnregisterAllCallbacks(self) - LKB.UnregisterAllCallbacks(self) - LSG:RemoveFrame(f) - f:SetParent(UIParent) - f:ClearAllPoints() - f:Hide() -end - --- --- Events --- - -function Bar:OnConfigModeChanged(event, mode) - self:SetConfigMode(mode) -end - --- --- Accessors --- - -function Bar:GetName() - return self.name -end - --- only ReAction:RenameBar() should call this function. Calling from any other --- context will desync the bar list in the ReAction class. -function Bar:SetName(name) - self.name = name - if self.overlay then - self.overlay:SetLabel(self.name) - end -end - -function Bar:GetFrame() - -- this method is included for documentation purposes. It is overridden - -- for each object in the :New() method. - error("Invalid Bar object: used without initialization") -end - -function Bar:GetConfig() - return self.config -end - -function Bar:GetAnchor() - local c = self.config - return (c.point or "CENTER"), - (c.anchor or self:GetFrame():GetParent():GetName()), - (c.relpoint or c.point or "CENTER"), - (c.x or 0), - (c.y or 0) -end - -function Bar:SetAnchor(point, frame, relativePoint, x, y) - local c = self.config - c.point = point or c.point - c.anchor = frame or c.anchor - c.relpoint = relativePoint or c.relpoint - c.x = x or c.x - c.y = y or c.y - self:ApplyAnchor() - ReAction:RefreshBar(self) -end - -function Bar:GetSize() - local f = self:GetFrame() - return f:GetWidth(), f:GetHeight() -end - -function Bar:SetSize(w,h) - local f = self:GetFrame() - self.config.width = w - self.config.height = h - f:SetWidth(w) - f:SetHeight(h) -end - -function Bar:GetButtonSize() - local w = self.config.btnWidth or 32 - local h = self.config.btnHeight or 32 - -- TODO: get from modules? - return w,h -end - -function Bar:SetButtonSize(w,h) - if w > 0 and h > 0 then - self.config.btnWidth = w - self.config.btnHeight = h - end - ReAction:RefreshBar(self) -end - -function Bar:GetNumButtons() - local r,c = self:GetButtonGrid() - return r*c -end - -function Bar:GetButtonGrid() - local cfg = self.config - local r = cfg.btnRows or 1 - local c = cfg.btnColumns or 1 - local s = cfg.spacing or 4 - return r,c,s -end - -function Bar:SetButtonGrid(r,c,s) - if r > 0 and c > 0 and s > 0 then - local cfg = self.config - cfg.btnRows = r - cfg.btnColumns = c - cfg.spacing = s - end - ReAction:RefreshBar(self) -end - -function Bar:GetAlpha() - return self.config.alpha or 1.0 -end - -function Bar:SetAlpha(value) - self.config.alpha = value - self:GetFrame():SetAlpha(value or 1.0) - self:UpdateDefaultStateAlpha() - ReAction:RefreshBar(self) -end - -function Bar:IterateButtons() - -- iterator returns button, idx and does NOT iterate in index order - return pairs(self.buttons) -end - --- --- Methods --- - -function Bar:SetConfigMode(mode) - self:SetSecureData("showAll",mode) - self:ShowControls(mode) - for b in self:IterateButtons() do - b:ShowGridTemp(mode) - b:UpdateActionIDLabel(mode) - end -end - -function Bar:SetKeybindMode(mode) - self:SetSecureData("showAll",mode) - for b in self:IterateButtons() do - b:SetKeybindMode(mode) - end -end - -function Bar:ApplyAnchor() - local f = self:GetFrame() - local c = self.config - local p = c.point - - f:SetWidth(c.width) - f:SetHeight(c.height) - f:ClearAllPoints() - - if p then - local a = f:GetParent() - if c.anchor then - local bar = ReAction:GetBar(c.anchor) - if bar then - a = bar:GetFrame() - else - a = _G[c.anchor] - end - end - local fr = a or f:GetParent() - f:SetPoint(p, a or f:GetParent(), c.relpoint, c.x or 0, c.y or 0) - else - f:SetPoint("CENTER") - end - - self:UpdateDefaultStateAnchor() -end - -function Bar:ClipNButtons( n ) - local cfg = self.config - local r = cfg.btnRows or 1 - local c = cfg.btnColumns or 1 - - cfg.btnRows = ceil(n/c) - cfg.btnColumns = min(n,c) -end - -function Bar:AddButton(idx, button) - local f = self:GetFrame() - - -- store in a weak reverse-index array - self.buttons[button] = idx - - -- Store a properly wrapped reference to the child frame as an attribute - -- (accessible via "frameref-btn#") - f:SetFrameRef(format("btn%d",idx), button:GetFrame()) -end - -function Bar:RemoveButton(button) - local idx = self.buttons[button] - if idx then - self:GetFrame():SetAttribute(format("frameref-btn%d",idx),nil) - self.buttons[button] = nil - end -end - -function Bar:PlaceButton(button, baseW, baseH) - local idx = self.buttons[button] - if idx then - local r, c, s = self:GetButtonGrid() - local bh, bw = self:GetButtonSize() - local row, col = floor((idx-1)/c), fmod((idx-1),c) -- zero-based - local x, y = col*bw + (col+0.5)*s, -(row*bh + (row+0.5)*s) - local scale = bw/baseW - local b = button:GetFrame() - - b:ClearAllPoints() - b:SetPoint("TOPLEFT",x/scale,y/scale) - b:SetScale(scale) - end -end - -function Bar:SkinButton() - -- does nothing by default -end - -function Bar:UpdateShowGrid() - for button in self:IterateButtons() do - button:UpdateShowGrid() - end -end - -function Bar:ShowControls(show) - if show then - if not self.overlay then - self.overlay = Bar.Overlay:New(self) -- see Overlay.lua - end - self.overlay:Show() - self:RefreshSecureState() - elseif self.overlay then - self.overlay:Hide() - end -end - -function Bar:RefreshControls() - if self.overlay and self.overlay:IsShown() then - self.overlay:RefreshControls() - end -end - -function Bar:SetLabelSubtext(text) - if self.overlay then - self.overlay:SetLabelSubtext(text) - end -end - --- --- Secure state functions --- - -function Bar:GetSecureState() - local env = GetManagedEnvironment(self:GetFrame()) - return env and env.state -end - -function Bar:GetStateProperty(state, propname) - -- override in modules/State.lua for now -end - -function Bar:SetStateProperty(state, propname, value) - -- override in modules/State.lua for now -end - -function Bar:RefreshSecureState() - self:GetFrame():Execute(_reaction_refresh) -end - --- usage: SetSecureData(globalname, [tblkey1, tblkey2, ...], value) -function Bar:SetSecureData( ... ) - local n = select('#',...) - if n < 2 then - error("ReAction.Bar:SetSecureData() requires at least 2 arguments") - end - local f = self:GetFrame() - f:SetAttribute("data-depth",n-1) - f:SetAttribute("data-value",select(n,...)) - for i = 1, n-1 do - local key = select(i,...) - if key == nil then - error("ReAction.Bar:SetSecureData() - nil table key in argument list (#"..i..")") - end - f:SetAttribute("data-key-"..i, key) - end - f:Execute( - [[ - local n = self:GetAttribute("data-depth") - if n > 0 then - local value = self:GetAttribute("data-value") - local t = _G - for i = 1, n do - local key = self:GetAttribute("data-key-"..i) - if not key then return end - if not t[key] then - t[key] = newtable() - end - if i == n then - t[key] = value - else - t = t[key] - end - end - end - ]]) - self:RefreshSecureState() -end - -function Bar:SetSecureStateData( state, key, value ) - self:SetSecureData("settings",state,key,value) -end - --- sets a snippet to be run as an extension to _onstate-reaction -function Bar:SetSecureStateExtension( id, snippet ) - if id == nil then - error("ReAction.Bar:SetSecureStateExtension() requires an id") - end - local f = self:GetFrame() - f:SetAttribute("input-secure-ext-id",id) - f:SetAttribute("secure-ext-"..id,snippet) - f:Execute( - [[ - local id = self:GetAttribute("input-secure-ext-id") - if id then - extensions[id] = self:GetAttribute("secure-ext-"..id) or nil - end - ]]) - self:RefreshSecureState() -end - -function Bar:SetFrameRef( name, refFrame ) - if refFrame then - local _, explicit = refFrame:IsProtected() - if not explicit then - refFrame = nil - end - end - if refFrame then - self:GetFrame():SetFrameRef(name,refFrame) - else - self:GetFrame():SetAttribute("frameref-"..name,nil) - end -end - -function Bar:SetStateDriver( states ) - if states then - for state, props in pairs(states) do - self:SetSecureStateData(state, "active_", true) -- make sure there's a 'settings' field for this state - for propname, value in pairs(props) do - if propname == "anchorFrame" then - self:SetFrameRef("anchor-"..state, _G[value]) - elseif propname == "rule" then - -- do nothing - else - self:SetSecureStateData(state, propname, value) - end - end - end - end - local rule = states and self:BuildStateRule(states) - if rule then - RegisterStateDriver(self:GetFrame(),"reaction",rule) - elseif self.statedriver then - UnregisterStateDriver(self:GetFrame(),"reaction") - end - self.statedriver = rule - self:BuildStateKeybinds(states) - self:RefreshSecureState() -end - --- pass unit=nil to set up the unit elsewhere, if you want something more complex -function Bar:RegisterUnitWatch( unit, enable ) - local f = self:GetFrame() - if unit then - f:SetAttribute("unit",unit) - end - if enable then - RegisterUnitWatch(self:GetFrame(),true) - elseif self.unitwatch then - UnregisterUnitWatch(self:GetFrame()) - end - self.unitwatch = enable - self:RefreshSecureState() -end - -function Bar:SetStateKeybind( key, state ) - local f = self:GetFrame() - local binds = self.statebinds - if not binds then - binds = { } - self.statebinds = binds - end - - -- clear the old binding, if any - if binds[state] then - SetOverrideBinding(f, false, binds[state], nil) - end - - if key then - SetOverrideBindingClick(f, false, key, f:GetName(), state) -- state name is virtual mouse button - end - binds[state] = key -end - -function Bar:GetStateKeybind( state ) - if self.statebinds and state then - return self.statebinds[state] - end -end - -function Bar:UpdateDefaultStateAnchor() - local point, frame, relPoint, x, y = self:GetAnchor() - local f = self:GetFrame() - f:SetAttribute("defaultAnchor-point",point) - f:SetAttribute("defaultAnchor-relPoint",relPoint) - f:SetAttribute("defaultAnchor-x",x) - f:SetAttribute("defaultAnchor-y",y) - self:SetFrameRef("defaultAnchor",_G[frame or "UIParent"]) - f:Execute([[ - for _, k in pairs(anchorKeys) do - defaultAnchor[k] = self:GetAttribute("defaultAnchor-"..k) - end - defaultAnchor.frame = self:GetAttribute("frameref-defaultAnchor") - ]]) -end - -function Bar:UpdateDefaultStateAlpha() - local f = self:GetFrame() - f:SetAttribute("defaultAlpha",self:GetAlpha()) - f:Execute([[ - defaultAlpha = self:GetAttribute("defaultAlpha") - ]]) -end - ----- secure state driver rules ---- - -local playerClass = select(2, UnitClass("player")) -local function ClassFilter(...) - for i = 1, select('#',...) do - if playerClass == select(i,...) then - return false - end - end - return true -end - -local ruleformats = { - stealth = { format = "stealth", filter = ClassFilter("ROGUE","DRUID") }, - nostealth = { format = "nostealth", filter = ClassFilter("ROGUE","DRUID") }, - shadowdance = { format = "bonusbar:2", filter = ClassFilter("ROGUE") }, - shadowform = { format = "form:1", filter = ClassFilter("PRIEST") }, - noshadowform = { format = "noform", filter = ClassFilter("PRIEST") }, - battle = { format = "stance:1", filter = ClassFilter("WARRIOR") }, - defensive = { format = "stance:2", filter = ClassFilter("WARRIOR") }, - berserker = { format = "stance:3", filter = ClassFilter("WARRIOR") }, - caster = { format = "form:0/2/4/5/6", filter = ClassFilter("DRUID") }, - bear = { format = "form:1", filter = ClassFilter("DRUID") }, - cat = { format = "form:3", filter = ClassFilter("DRUID") }, - tree = { format = "form:5", filter = ClassFilter("DRUID") }, - moonkin = { format = "form:5", filter = ClassFilter("DRUID") }, - demon = { format = "form:2", filter = ClassFilter("WARLOCK") }, - nodemon = { format = "noform", filter = ClassFilter("WARLOCK") }, - pet = { format = "pet" }, - nopet = { format = "nopet" }, - harm = { format = "@target,harm" }, - help = { format = "@target,help" }, - notarget = { format = "@target,noexists" }, - focusharm = { format = "@focus,harm" }, - focushelp = { format = "@focus,help" }, - nofocus = { format = "@focus,noexists" }, - raid = { format = "group:raid" }, - party = { format = "group:party" }, - solo = { format = "nogroup" }, - combat = { format = "combat" }, - nocombat = { format = "nocombat" }, - possess = { format = "@vehicle,noexists,bonusbar:5" }, - vehicle = { format = "@vehicle,exists,bonusbar:5" }, -} - -function Bar.InitRuleFormats() - local forms = { } - for i = 1, GetNumShapeshiftForms() do - local _, name = GetShapeshiftFormInfo(i) - forms[name] = i; - end - -- use 9 if not found since 9 is never a valid stance/form - local defensive = forms[GetSpellInfo(71)] or 9 - local berserker = forms[GetSpellInfo(2458)] or 9 - local bear = forms[GetSpellInfo(5487)] or 9 - local aquatic = forms[GetSpellInfo(1066)] or 9 - local cat = forms[GetSpellInfo(768)] or 9 - local travel = forms[GetSpellInfo(783)] or 9 - local tree = forms[GetSpellInfo(33891)] or 9 - local moonkin = forms[GetSpellInfo(24858)] or 9 - local flight = forms[GetSpellInfo(40120)] or forms[GetSpellInfo(33943)] or 9 - - ruleformats.defensive.format = "stance:"..defensive - ruleformats.berserker.format = "stance:"..berserker - ruleformats.caster.format = format("form:0/%d/%d/%d", aquatic, travel, flight) - ruleformats.bear.format = "form:"..bear - ruleformats.cat.format = "form:"..cat - ruleformats.tree.format = "form:"..tree - ruleformats.moonkin.format = "form:"..moonkin -end - -function Bar:BuildStateRule(states) - -- states is a table : - -- states[statename].rule = { - -- order = #, - -- type = "default"/"custom"/"any"/"all", - -- values = { ... }, -- keys of ruleformats[] - -- custom = "...", - -- } - local rules = { } - local default - - for idx, state in ipairs(fieldsort(states, "rule", "order")) do - local c = states[state].rule - local type = c.type - if type == "default" then - default = default or state - elseif type == "custom" then - if c.custom then - -- strip out all spaces from the custom rule - table.insert(rules, format("%s %s", c.custom:gsub("%s",""), state)) - end - elseif type == "any" or type == "all" then - if c.values then - local clauses = { } - for key, value in pairs(c.values) do - if ruleformats[key] and not ruleformats[key].filter then - table.insert(clauses, ruleformats[key].format) - end - end - if #clauses > 0 then - local sep = (type == "any") and "][" or "," - table.insert(rules, format("[%s] %s", table.concat(clauses,sep), state)) - end - end - end - end - -- make sure that the default, if any, is last - if default then - table.insert(rules, default) - end - return table.concat(rules,";") -end - -function Bar:BuildStateKeybinds( states ) - if states then - for name, state in pairs(states) do - local rule = tfetch(state, "rule") - if rule and rule.type == "keybind" then - self:SetStateKeybind(rule.keybind, name) - else - self:SetStateKeybind(nil, name) -- this clears an existing keybind - end - end - end -end -
--- a/classes/Button.lua Thu Nov 18 13:11:08 2010 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,287 +0,0 @@ ---[[ - ReAction Button base class ---]] - --- local imports -local addonName, addonTable = ... -local ReAction = addonTable.ReAction -local L = ReAction.L -local LKB = ReAction.LKB -local _G = _G -local CreateFrame = CreateFrame -local GetBindingKey = GetBindingKey -local format = string.format - --- libraries -local LBF = LibStub("LibButtonFacade",true) -- optional - --- private -local trash = CreateFrame("Frame") -local frameList = { } -local idPools = { } - -local function kb_onEnter( frame ) - LKB:Set(frame) -end - --- Button class -local Button = { } - -ReAction.Button = Button -- export to ReAction - -function Button:New( name, config, bar, idx, inherits, buttonType ) - buttonType = buttonType or "CheckButton" - - -- create new self - self = setmetatable( - { - bar = bar, - idx = idx, - config = config, - name = name, - }, - { __index = self } ) - - -- have to recycle frames with the same name: CreateFrame() doesn't overwrite - -- existing globals. Can't set to nil in the global because it's then tainted. - -- Caller is responsible for ensuring global uniqueness of names. - local f = name and frameList[name] - if f then - f:SetParent(bar:GetFrame()) - else - f = CreateFrame(buttonType, name, bar:GetFrame(), inherits) - if name then - frameList[name] = f - end - end - - self.frame = f - - local frames = { } - self.frames = frames - frames.icon = _G[name.."Icon"] - frames.flash = _G[name.."Flash"] - frames.hotkey = _G[name.."HotKey"] - frames.count = _G[name.."Count"] - frames.name = _G[name.."Name"] - frames.border = _G[name.."Border"] - frames.cooldown = _G[name.."Cooldown"] - frames.normalTexture = _G[name.."NormalTexture"] - - if config then - config.name = name - end - - -- install LibKeyBound handlers onto frame - function f:GetActionName() - return format("%s:%s", bar:GetName(), idx) - end - - local clickBinding = format("CLICK %s:LeftButton", name) - function f:GetHotkey() - return LKB:ToShortKey(GetBindingKey(clickBinding)) - end - - return self -end - -function Button:Destroy() - local f = self:GetFrame() - f:UnregisterAllEvents() - self:ReleaseActionID(self:GetActionID()) - if f then - f:Hide() - f:SetParent(trash) - f:ClearAllPoints() - end -end - -function Button:GetBar() - return self.bar -end - -function Button:GetFrame() - return self.frame -end - -function Button:GetName() - return self.name -end - -function Button:GetConfig() - return self.config -end - -function Button:GetActionID() - -- derived classes should override this - return nil -end - -function Button:SetActionIDPool( poolID, maxID ) - self.actionPoolID = poolID - self.actionMaxID = maxID -end - -function Button:AcquireActionID( id, hint, unique ) - local poolID = self.actionPoolID - local maxID = self.actionMaxID - if not poolID or not maxID then - error("AcquireActionID: must setup pool first with SetActionIDPool") - end - local pool = idPools[poolID] - if not pool then - pool = { nWraps = 0, useCount = { } } - for i = 1, maxID do - pool.useCount[i] = 0 - end - idPools[poolID] = pool - end - local useCount = pool.useCount - if id == nil then - repeat - local nWraps = pool.nWraps or 0 - if hint and (useCount[hint] == nil or useCount[hint] == nWraps) then - id = hint - else - local start = hint or 1 - for i = start, maxID do - if useCount[i] == nil or useCount[i] == nWraps then - id = i - break - end - end - if not id then - for i = 1, start do - if useCount[i] == nil or useCount[i] == nWraps then - id = i - break - end - end - end - end - if id == nil then - if unique then - return nil - end - pool.nWraps = nWraps + 1 - end - until id ~= nil - end - useCount[id] = (useCount[id] or 0) + 1 - return id -end - -function Button:ReleaseActionID( id ) - local poolID = self.actionPoolID - if not poolID then - error("ReleaseActionID: must setup pool first with SetActionIDPool") - end - local pool = idPools[poolID] - if pool and id and pool.useCount[id] then - pool.useCount[id] = pool.useCount[id] - 1 - pool.nWraps = min(pool.useCount[id], pool.nWraps) - end -end - -function Button:Refresh() - local f = self:GetFrame() - self.bar:PlaceButton( self, f:GetWidth(), f:GetHeight() ) -end - -function Button:SetKeybindMode( mode ) - local f = self.frame - if mode then - self.oldOnEnter = f:GetScript("OnEnter") - f:SetScript("OnEnter", kb_onEnter) - elseif self.oldOnEnter then - f:SetScript("OnEnter", self.oldOnEnter) - self.oldOnEnter = nil - end - self:ShowGridTemp(mode) - self:UpdateKeybindModeDisplay( mode ) -end - -function Button:UpdateKeybindModeDisplay( mode ) - local border = self.frames.border or _G[format("%sBorder",tostring(self:GetName()))] - if border then - if mode then - border:SetVertexColor(LKB:GetColorKeyBoundMode()) - border:Show() - else - border:Hide() - end - end -end - -function Button:UpdateHotkey( hotkey ) - hotkey = hotkey or self.frames.hotkey - if not hotkey then - hotkey = _G[self:GetName().."HotKey"] - self.frames.hotkey = hotkey - end - if hotkey then - local txt = self.frame:GetHotkey() - hotkey:SetText( txt ) - if txt == nil or txt == "" then - hotkey:Hide() - else - hotkey:Show() - end - end -end - -function Button:GetActionIDLabel( create ) - local f = self:GetFrame() - if not f.actionIDLabel and create then - local label = f:CreateFontString(nil,"OVERLAY","GameFontNormalLarge") - label:SetAllPoints() - label:SetJustifyH("CENTER") - label:SetShadowColor(0,0,0,1) - label:SetShadowOffset(2,-2) - f.actionIDLabel = label -- store the label with the frame for recycling - end - return f.actionIDLabel -end - -function Button:UpdateActionIDLabel( show ) - local label = self:GetActionIDLabel( show ) - if label then - if show then - local id = self:GetActionID() - if id then - label:SetText(tostring(id)) - label:Show() - return - end - end - label:Hide() - end -end - -function Button:SetNormalVertexColor( r, g, b, a ) - if LBF then - LBF:SetNormalVertexColor(self:GetFrame(), r, g, b, a) - else - self:GetFrame():GetNormalTexture():SetVertexColor(r,g,b,a) - end -end - -function Button:GetNormalVertexColor() - if LBF then - return LBF:GetNormalVertexColor(self:GetFrame()) - else - return self:GetFrame():GetNormalTexture():GetVertexColor() - end -end - -function Button:UpdateShowGrid() - -- does nothing by default -end - -function Button:ShowGridTemp(show) - -- does nothing by default -end - -function Button:ShowGrid(show) - -- does nothing by default -end
--- a/classes/MultiCastButton.lua Thu Nov 18 13:11:08 2010 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,744 +0,0 @@ -local addonName, addonTable = ... -local ReAction = addonTable.ReAction -local L = ReAction.L -local _G = _G -local CreateFrame = CreateFrame -local format = string.format -local unpack = unpack -local GetCVar = GetCVar -local GameTooltip_SetDefaultAnchor = GameTooltip_SetDefaultAnchor -local CooldownFrame_SetTimer = CooldownFrame_SetTimer -local InCombatLockdown = InCombatLockdown -local IsUsableSpell = IsUsableSpell -local IsUsableAction = IsUsableAction -local IsSpellKnown = IsSpellKnown -local IsSpellInRange = IsSpellInRange -local IsActionInRange = IsActionInRange -local GetSpellInfo = GetSpellInfo -local GetSpellCooldown = GetSpellCooldown -local GetActionCooldown = GetActionCooldown -local GetSpellTexture = GetSpellTexture -local GetActionTexture = GetActionTexture -local GetMultiCastTotemSpells = GetMultiCastTotemSpells - ---[[ - Blizzard Constants: - - NUM_MULTI_CAST_BUTTONS_PER_PAGE = 4 - - NUM_MULTI_CAST_PAGES = 3 - - SHAMAN_TOTEM_PRIORITIES = { } -- sets the order of the totems - - TOTEM_MULTI_CAST_SUMMON_SPELLS = { } -- list of summon spellIDs - - TOTEM_MULTI_CAST_RECALL_SPELLS = { } -- list of recall spellIDs - - Blizzard Events: - - UPDATE_MULTI_CAST_ACTIONBAR - - Blizzard APIs: - - GetMultiCastBarOffset() : returns 6 - - - SetMultiCastSpell(actionID, spellID) (protected) OR - SetAttribute("type","multispell") - SetAttribute("action",actionID) - SetAttribute("spell",spellID) - - note: multicast actionID page is NUM_ACTIONBAR_PAGES + GetMultiCastBarOffset(), - so that's action ID 132-144. - - - spell1, spell2, spell3, ... = GetMultiCastTotemSpells(slot) - returns spellIDs for all known totems that fit that slot. This function is available in - the secure environment. - - Blizzard textures: - All the textures for the multicast bar (arrows, empty-slot icons, etc) are part of a single - texture: each texture uses SetTexCoord() to display only a slice of the textures. I suppose - this is to slightly optimize texture load performance, but it makes the UI code more clumsy. - - Each totem button and arrow has a colored border indicating its elemental type. - - TODO: - - make whether to show the colored border configurable (looks really bad with ButtonFacade:Zoomed) - - apply ButtonFacade to the flyout buttons? Or at least zoom the textures slightly? - - use a multiplier with SetTexCoord on totem bar texture? - - Design Notes: - - Only the header has a secure context. All other frames execute in its context. - - - Each button is either type "spell" (summon/recall) or type "action" (totem action IDs are - GetBonusBarOffset()=6, 132-144) with 3 pages of 4 buttons. The paging is controlled by - the summon flyout, which is also paged with the summon spells (the recall button is not paged) - - - A spell list is updated in the secure context at setup time (TODO: redo setup when learning new - spells) with the list of spells known for each slot. - - - Each button (except recall) has an arrow button which appears on mouseover and when clicked - opens the flyout via a wrapped OnClick handler. When the flyout is open, the arrow does not - appear. - - - A single flyout with N+1 (1 slot is to select no totem for the set) flyout-buttons is a child - of the bar. Each time the flyout panel is opened, the individual buttons grab their corresponding - spell/type from the list, according to the slot which opened the flyout. Each button either sets - the current page (summon) or sets a multispell to an actionID via type="multispell". None of them - actually cast any spells (though, I suppose we could modify this so that e.g. configurable - right-click casts the spell). The flyout also has a close button which closes the flyout: the - flyout-open code positions the close button anchored to the last button in the flyout (which - changes dynamically because each slot has a different number of items in the list). - - - Multicast sets are not stances, there's no need (or ability) to handle swapping sets if one of - the summon spells is cast from elsewhere. - - - The default UI has Call of the Elements always selected on UI load. This module remembers the last - selected one and restores it. - - -]]-- - - --- --- Secure snippets --- - --- bar -local _bar_init = -- function(self) -[[ - -- set up some globals in the secure environment - flyout = self:GetFrameRef("flyout") - flyoutSlot = nil - summonSlot = self:GetAttribute("summonSlot") - recallSlot = self:GetAttribute("recallSlot") - baseActionID = self:GetAttribute("baseActionID") - slotsPerPage = self:GetAttribute("slotsPerPage") - currentPage = currentPage or self:GetAttribute("lastSummon") or 1 - - totemIDsBySlot = newtable() - for i = 1, slotsPerPage do - totemIDsBySlot[i] = self:GetAttribute("TOTEM_PRIORITY_"..i) - end - - -- these are set up in bar:SetupBarHeader() - flyoutChildren = flyoutChildren or newtable() - summonSpells = summonSpells or newtable() -]] - -local _onstate_multispellpage = -- function(self, stateid, newstate) -[[ - currentPage = tonumber(newstate) - control:CallMethod("UpdateLastSummon",currentPage) - control:ChildUpdate() -]] - - --- buttons -local _childupdate = -- function(self, snippetid, message) -[[ - local t = self:GetAttribute("type") - self:SetAttribute(t, self:GetAttribute(t.."-page"..currentPage)) -]] - -local _onEnter = -- function(self) - -- for whatever reason, RegisterAutoHide is unreliable - -- unless you re-anchor the frame prior to calling it. - -- Even then, it's still not terribly reliable. -[[ - local slot = self:GetAttribute("bar-idx") - local arrow = owner:GetFrameRef("arrow-"..slot) - if arrow and not arrow:IsShown() and not (flyout:IsVisible() and flyoutSlot == slot) then - arrow:ClearAllPoints() - arrow:SetPoint("BOTTOM",self,"TOP",0,0) - arrow:Show() - arrow:RegisterAutoHide(0) - arrow:AddToAutoHide(self) - end -]] - - --- flyout arrow -local _arrow_openFlyout = -- function(self) -[[ - local slot = self:GetAttribute("bar-idx") - local totemID = totemIDsBySlot[slot - (summonSlot or 0)] - if totemID == 0 then - totemID = "summon" - end - - local lastButton, lastPage - for page, b in ipairs(flyoutChildren) do - b:Hide() - b:SetAttribute("totemSlot",totemID) - if slot == summonSlot then - local spellID = self:GetParent():GetAttribute("spell-page"..page) - if spellID then - b:SetAttribute("type","changePage") - b:SetAttribute("spell",spellID) - b:Show() - lastButton = b - lastPage = page - end - else - local spell = select(page, 0, GetMultiCastTotemSpells(totemID) ) - if spell then - b:SetAttribute("type","multispell") - b:SetAttribute("action", baseActionID + (currentPage - 1)*slotsPerPage + totemID) - b:SetAttribute("spell", spell) - b:Show() - lastButton = b - lastPage = page - end - end - end - - local close = owner:GetFrameRef("close") - if lastButton and close then - close:ClearAllPoints() - close:SetPoint("BOTTOM",lastButton,"TOP",0,0) -- TODO: better anchoring - close:Show() - control:CallMethod("UpdateFlyoutTextures",totemID) - end - - flyout:ClearAllPoints() - flyout:SetPoint("BOTTOM",self,"BOTTOM",0,0) -- TODO: better anchoring - if lastPage then - flyout:SetHeight(lastPage * 27 + (close and close:GetHeight() or 0)) - end - flyout:Show() - flyout:RegisterAutoHide(1) -- TODO: configurable - flyout:AddToAutoHide(owner) - flyoutSlot = slot - self:Hide() -]] - -local _closeFlyout = -- function(self) -[[ - flyout:Hide() -]] - - --- flyout child buttons -local _flyout_child_preClick = -- function(self, button, down) -[[ - local button = button - if self:GetAttribute("type") == "changePage" then - owner:SetAttribute("state-multispellpage",self:GetAttribute("index")) - self:GetParent():Hide() - return false - else - return nil, "close" - end -]] - -local _flyout_child_postClick = -- function(self, message, button, down) -[[ - if message == "close" then - self:GetParent():Hide() -- hide flyout after selecting - end -]] - - --- --- The Blizzard totem bar textures are all actually one big texture, --- with texcoord offsets. Shamelessly stolen from FrameXML/MultiCastActionBarFrame.lua --- -local TOTEM_TEXTURE = "Interface\\Buttons\\UI-TotemBar" -local FLYOUT_UP_BUTTON_HL_TCOORDS = { 72/128, 92/128, 88/256, 98/256 } -local FLYOUT_DOWN_BUTTON_HL_TCOORDS = { 72/128, 92/128, 69/256, 79/256 } - -local SLOT_EMPTY_TCOORDS = { - [EARTH_TOTEM_SLOT] = { 66/128, 96/128, 3/256, 33/256 }, - [FIRE_TOTEM_SLOT] = { 67/128, 97/128, 100/256, 130/256 }, - [WATER_TOTEM_SLOT] = { 39/128, 69/128, 209/256, 239/256 }, - [AIR_TOTEM_SLOT] = { 66/128, 96/128, 36/256, 66/256 }, -} - -local SLOT_OVERLAY_TCOORDS = { - [EARTH_TOTEM_SLOT] = { 1/128, 35/128, 172/256, 206/256 }, - [FIRE_TOTEM_SLOT] = { 36/128, 70/128, 172/256, 206/256 }, - [WATER_TOTEM_SLOT] = { 1/128, 35/128, 207/256, 240/256 }, - [AIR_TOTEM_SLOT] = { 36/128, 70/128, 137/256, 171/256 }, -} - -local FLYOUT_UP_BUTTON_TCOORDS = { - ["summon"] = { 99/128, 127/128, 84/256, 102/256 }, - [EARTH_TOTEM_SLOT] = { 99/128, 127/128, 160/256, 178/256 }, - [FIRE_TOTEM_SLOT] = { 99/128, 127/128, 122/256, 140/256 }, - [WATER_TOTEM_SLOT] = { 99/128, 127/128, 199/256, 217/256 }, - [AIR_TOTEM_SLOT] = { 99/128, 127/128, 237/256, 255/256 }, -} - -local FLYOUT_DOWN_BUTTON_TCOORDS = { - ["summon"] = { 99/128, 127/128, 65/256, 83/256 }, - [EARTH_TOTEM_SLOT] = { 99/128, 127/128, 141/256, 159/256 }, - [FIRE_TOTEM_SLOT] = { 99/128, 127/128, 103/256, 121/256 }, - [WATER_TOTEM_SLOT] = { 99/128, 127/128, 180/256, 198/256 }, - [AIR_TOTEM_SLOT] = { 99/128, 127/128, 218/256, 236/256 }, -} - -local FLYOUT_TOP_TCOORDS = { - ["summon"] = { 33/128, 65/128, 1/256, 23/256 }, - [EARTH_TOTEM_SLOT] = { 0/128, 32/128, 46/256, 68/256 }, - [FIRE_TOTEM_SLOT] = { 33/128, 65/128, 46/256, 68/256 }, - [WATER_TOTEM_SLOT] = { 0/128, 32/128, 1/256, 23/256 }, - [AIR_TOTEM_SLOT] = { 0/128, 32/128, 91/256, 113/256 }, -} - -local FLYOUT_MIDDLE_TCOORDS = { - ["summon"] = { 33/128, 65/128, 23/256, 43/256 }, - [EARTH_TOTEM_SLOT] = { 0/128, 32/128, 68/256, 88/256 }, - [FIRE_TOTEM_SLOT] = { 33/128, 65/128, 68/256, 88/256 }, - [WATER_TOTEM_SLOT] = { 0/128, 32/128, 23/256, 43/256 }, - [AIR_TOTEM_SLOT] = { 0/128, 32/128, 113/256, 133/256 }, -} - -local eventList = { - "ACTIONBAR_SLOT_CHANGED", - "ACTIONBAR_UPDATE_STATE", - "ACTIONBAR_UPDATE_USABLE", - "ACTIONBAR_UPDATE_COOLDOWN", - "UPDATE_BINDINGS", - "UPDATE_MULTI_CAST_ACTIONBAR", -} - --- --- MultiCast Button class --- Inherits implementation methods from Action button class, but circumvents the constructor --- and redefines/removes some methods. --- -local Super = ReAction.Button -local Action = ReAction.Button.Action -local MultiCast = setmetatable( { }, { __index = Action } ) -ReAction.Button.MultiCast = MultiCast - -function MultiCast:New( idx, btnConfig, bar ) - local maxIndex = bar.nTotemSlots or 0 - if bar.summonSlot then - maxIndex = maxIndex + 1 - end - if bar.recallSlot then - maxIndex = maxIndex + 1 - end - - if not bar.hasMulticast or idx > maxIndex then - return false - end - - if idx < 1 then - error("invalid index") - end - - local name = format("ReAction_%s_Totem_%d",bar:GetName(),idx) - - self = Super.New(self, name, btnConfig, bar, idx, "SecureActionButtonTemplate, ActionButtonTemplate" ) - - local barFrame = bar:GetFrame() - local f = self:GetFrame() - - -- attributes - local page = (idx == bar.recallSlot) and 1 or bar:GetConfig().lastSummon or 1 - if idx == bar.recallSlot or idx == bar.summonSlot then - f:SetAttribute("type","spell") - local spells = (idx == bar.summonSlot) and TOTEM_MULTI_CAST_SUMMON_SPELLS or TOTEM_MULTI_CAST_RECALL_SPELLS - f:SetAttribute("spell",spells[page]) - for i, spell in ipairs(spells) do - if spell and IsSpellKnown(spell) then - f:SetAttribute("spell-page"..i, spell) - end - end - else - local offset = bar.summonSlot and 1 or 0 - local slot = SHAMAN_TOTEM_PRIORITIES[idx - offset] - local baseAction = barFrame:GetAttribute("baseActionID") + slot - self.totemSlot = slot - f:SetAttribute("type","action") - f:SetAttribute("action", baseAction + (page - 1) * NUM_MULTI_CAST_BUTTONS_PER_PAGE) - for i = 1, NUM_MULTI_CAST_PAGES do - f:SetAttribute("action-page"..i, baseAction + (i-1) * NUM_MULTI_CAST_BUTTONS_PER_PAGE) - end - if not f.overlayTex then - local tx = f:CreateTexture("OVERLAY") - tx:SetTexture(TOTEM_TEXTURE) - tx:SetTexCoord(unpack(SLOT_OVERLAY_TCOORDS[self.totemSlot])) - tx:SetWidth(34) - tx:SetHeight(34) - tx:SetPoint("CENTER") - tx:Show() - f.overlayTex = tx - end - end - f:SetAttribute("bar-idx",idx) - - -- non secure scripts - f:SetScript("OnEvent", function(frame, ...) self:OnEvent(...) end) - f:SetScript("OnEnter", function(frame) self:OnEnter() end) - f:SetScript("OnLeave", function(frame) self:OnLeave() end) - f:SetScript("OnAttributeChanged", function(frame, attr, value) self:OnAttributeChanged(attr, value) end) - f:SetScript("PostClick", function(frame, ...) self:PostClick(...) end) - - -- secure handlers - if idx ~= bar.recallSlot then - f:SetAttribute("_childupdate",_childupdate) - end - barFrame:WrapScript(f, "OnEnter", _onEnter) - - -- event registration - f:EnableMouse(true) - f:RegisterForClicks("AnyUp") - for _, evt in pairs(eventList) do - f:RegisterEvent(evt) - end - - -- Set up a proxy for the icon texture for use with ButtonFacade - local SetTexCoordRaw = self.frames.icon.SetTexCoord - self.frames.icon.SetTexCoord = function( tx, ... ) - if self:GetIconTexture() == TOTEM_TEXTURE then - SetTexCoordRaw(tx,select(2,self:GetIconTexture())) - else - SetTexCoordRaw(tx,...) - end - end - - -- attach to skinner - bar:SkinButton(self) - - f:Show() - - -- open arrow and flyout background textures - if idx ~= bar.recallSlot then - local arrow = f._arrowFrame or CreateFrame("Button", nil, f, "SecureFrameTemplate") - f._arrowFrame = arrow - arrow:SetWidth(28) - arrow:SetHeight(18) - arrow:SetPoint("BOTTOM",self:GetFrame(),"TOP",0,0) -- TODO: better anchoring - arrow:SetNormalTexture(TOTEM_TEXTURE) - local slot = self.totemSlot or "summon" - arrow:GetNormalTexture():SetTexCoord( unpack(FLYOUT_UP_BUTTON_TCOORDS[slot]) ) - arrow:SetHighlightTexture(TOTEM_TEXTURE) - arrow:GetHighlightTexture():SetTexCoord( unpack(FLYOUT_UP_BUTTON_HL_TCOORDS) ) - arrow:SetAttribute("bar-idx",idx) - arrow:Hide() - barFrame:WrapScript(arrow, "OnClick", _arrow_openFlyout) - barFrame:SetFrameRef("arrow-"..idx,arrow) - end - - self:Refresh() - - return self -end - -function MultiCast:Destroy() - local barFrame = self.bar:GetFrame() - local f = self:GetFrame() - pcall( barFrame.UnwrapScript, barFrame, f, "OnEnter" ) -- ignore errors - if f._arrowFrame then - pcall( barFrame.UnwrapScript, barFrame, f._arrowFrame,"OnClick" ) -- ignore errors - end - Super.Destroy(self) -end - -function MultiCast:Refresh() - Super.Refresh(self) - self:UpdateAction() -end - -function MultiCast:ShowGrid( show ) -end - -function MultiCast:ShowGridTemp( show ) -end - -function MultiCast:AcquireActionID() -end - -function MultiCast:ReleaseActionID() -end - -function MultiCast:UpdateShowGrid() -end - -function MultiCast:UpdateBorder() -end - -function MultiCast:UpdateMacroText() -end - -function MultiCast:UpdateCount() -end - -function MultiCast:UpdateCheckedState() - local action = self:GetActionID() - if action and IsCurrentAction(action) then - self:GetFrame():SetChecked(1) - else - self:GetFrame():SetChecked(0) - end -end - -function MultiCast:RefreshHasActionAttributes() -end - -function MultiCast:UpdateFlash() -end - -function MultiCast:GetIconTexture() - local tx - if self.spellID then - tx = GetSpellTexture(GetSpellInfo(self.spellID)) - elseif self.actionID then - tx = GetActionTexture(self.actionID) - end - if tx then - return tx - else - return TOTEM_TEXTURE, unpack(SLOT_EMPTY_TCOORDS[self.totemSlot or 1]) - end -end - -function MultiCast:UpdateAction() - local action = self:GetActionID() - if action then - if action ~= self.actionID then - self.actionID = action - self:UpdateAll() - end - else - local spellID = self:GetSpellID() - if spellID ~= self.spellID then - self.spellID = spellID - self:UpdateAll() - end - end -end - -function MultiCast:GetActionID(page) - return self:GetFrame():GetAttribute("action") -end - -function MultiCast:GetSpellID(page) - return self:GetFrame():GetAttribute("spell") -end - -function MultiCast:SetActionID( id ) - error("Can not set action ID of multicast buttons") -end - -function MultiCast:SetTooltip() - local barFrame = self:GetFrame() - if GetCVar("UberTooltips") == "1" then - GameTooltip_SetDefaultAnchor(GameTooltip, barFrame) - else - GameTooltip:SetOwner(barFrame) - end - if self.spellID then - GameTooltip:SetSpellByID(self.spellID,false,true) - elseif self.actionID then - GameTooltip:SetAction(self.actionID) - end -end - -function MultiCast:GetUsable() - if self.spellID then - return IsUsableSpell((GetSpellInfo(self.spellID))) - elseif self.actionID then - return IsUsableAction(self.actionID) - end -end - -function MultiCast:GetInRange() - if self.spellID then - return IsSpellInRange((GetSpellInfo(self.spellID))) == 0 - elseif self.actionID then - return IsActionInRange(self.actionID) == 0 - end -end - -function MultiCast:GetCooldown() - if self.spellID then - return GetSpellCooldown((GetSpellInfo(self.spellID))) - elseif self.actionID then - return GetActionCooldown(self.actionID) - else - return 0, 0, 0 - end -end - -function MultiCast:UPDATE_MULTI_CAST_ACTIONBAR() - self:UpdateAll() -end - - --- --- flyout setup --- -local function ShowFlyoutTooltip(frame) - if GetCVar("UberTooltips") == "1" then - GameTooltip_SetDefaultAnchor(GameTooltip, frame) - else - GameTooltip:SetOwner(frame) - end - local spell = frame:GetAttribute("spell") - if spell == nil or spell == 0 then - GameTooltip:SetText(MULTI_CAST_TOOLTIP_NO_TOTEM, HIGHLIGHT_FONT_COLOR.r, HIGHLIGHT_FONT_COLOR.g, HIGHLIGHT_FONT_COLOR.b) - else - GameTooltip:SetSpellByID(spell,false,true) - end -end - -local function HideFlyoutTooltip() - GameTooltip:Hide() -end - -local function UpdateFlyoutIcon(frame) - local spellID = frame:GetAttribute("spell") - if spellID == 0 or spellID == nil then - frame.icon:SetTexture(TOTEM_TEXTURE) - local slot = tonumber(frame:GetAttribute("totemSlot")) or 1 - frame.icon:SetTexCoord( unpack(SLOT_EMPTY_TCOORDS[slot]) ) - else - frame.icon:SetTexture(GetSpellTexture(GetSpellInfo(spellID))) - frame.icon:SetTexCoord(0,1,0,1) - end -end - -function MultiCast.SetupBarHeader( bar ) -- call this as a static method - local slot = 0 - local nTotemSlots = 0 - local summonSlot = nil - local recallSlot = nil - - -- figure out the capabilities of the character - for i, spell in ipairs(TOTEM_MULTI_CAST_SUMMON_SPELLS) do - if spell and IsSpellKnown(spell) then - slot = 1 - summonSlot = 1 - end - end - - for i = 1, NUM_MULTI_CAST_BUTTONS_PER_PAGE do - local totem = SHAMAN_TOTEM_PRIORITIES[i]; - if GetTotemInfo(totem) and GetMultiCastTotemSpells(totem) then - nTotemSlots = nTotemSlots + 1 - slot = slot + 1 - end - end - - slot = slot + 1 - for i, spell in ipairs(TOTEM_MULTI_CAST_RECALL_SPELLS) do - if spell and IsSpellKnown(spell) then - recallSlot = slot - end - end - - if nTotemSlots == 0 then - bar.hasMulticast = false -- no multicast capability - return - end - - bar.hasMulticast = true - bar.summonSlot = summonSlot - bar.recallSlot = recallSlot - bar.nTotemSlots = nTotemSlots - - - local f = bar:GetFrame() - - -- init bar secure environment - f:SetAttribute("lastSummon", bar:GetConfig().lastSummon) - f:SetAttribute("summonSlot", summonSlot) - f:SetAttribute("recallSlot", recallSlot) - f:SetAttribute("slotsPerPage", NUM_MULTI_CAST_BUTTONS_PER_PAGE) - f:SetAttribute("baseActionID", (NUM_ACTIONBAR_PAGES + GetMultiCastBarOffset() - 1)*NUM_ACTIONBAR_BUTTONS) - for i, p in ipairs(SHAMAN_TOTEM_PRIORITIES) do - f:SetAttribute("TOTEM_PRIORITY_"..i,p) - end - f:SetAttribute("_onstate-multispellpage", _onstate_multispellpage) - - function f:UpdateLastSummon(value) - bar:GetConfig().lastSummon = value - end - - -- create flyout container frame and close arrow - local flyout = bar._flyoutFrame - if not flyout then - flyout = CreateFrame("Frame", nil, f, "SecureFrameTemplate") - bar._flyoutFrame = flyout - f:SetFrameRef("flyout",flyout) - flyout.buttons = { } - flyout:Hide() - flyout:SetWidth(24) - flyout:SetHeight(1) - flyout:SetPoint("BOTTOM",f,"TOP",0,0) - - local close = CreateFrame("Button", nil, flyout, "SecureFrameTemplate") - close:SetWidth(28) - close:SetHeight(18) - close:SetPoint("BOTTOM",flyout,"TOP") - close:SetNormalTexture(TOTEM_TEXTURE) - close:GetNormalTexture():SetTexCoord(unpack(FLYOUT_DOWN_BUTTON_TCOORDS["summon"])) - close:SetHighlightTexture(TOTEM_TEXTURE) - close:GetHighlightTexture():SetTexCoord( unpack(FLYOUT_DOWN_BUTTON_HL_TCOORDS) ) - f:SetFrameRef("close",close) - f:WrapScript(close, "OnClick", _closeFlyout) - close:Show() - - local midTx = flyout:CreateTexture("BACKGROUND") - midTx:SetWidth(32) - midTx:SetHeight(20) - midTx:SetPoint("BOTTOM") - midTx:SetTexture(TOTEM_TEXTURE) - midTx:SetTexCoord(unpack(FLYOUT_MIDDLE_TCOORDS["summon"])) - midTx:Show() - - local topTx = flyout:CreateTexture("BACKGROUND") - topTx:SetWidth(32) - topTx:SetHeight(20) - topTx:SetTexture(TOTEM_TEXTURE) - midTx:SetTexCoord(unpack(FLYOUT_TOP_TCOORDS["summon"])) - topTx:SetPoint("BOTTOM",midTx,"TOP",0,-10) - topTx:Show() - - function flyout:UpdateTextures(slot) - slot = slot or "summon" - close:GetNormalTexture():SetTexCoord(unpack(FLYOUT_DOWN_BUTTON_TCOORDS[slot])) - midTx:ClearAllPoints() - midTx:SetPoint("BOTTOM") - midTx:SetPoint("TOP",close,"BOTTOM",0,0) - midTx:SetTexCoord(unpack(FLYOUT_MIDDLE_TCOORDS[slot])) - topTx:SetTexCoord(unpack(FLYOUT_TOP_TCOORDS[slot])) - end - - -- create flyout buttons - for i = 1, 10 do -- maximum 9 spells + 1 empty slot - local b = CreateFrame("Button",nil,flyout,"SecureActionButtonTemplate") - b:SetWidth(24) - b:SetHeight(24) - local prev = flyout.buttons[i-1] - b:SetPoint("BOTTOM", prev or flyout, prev and "TOP" or "BOTTOM", 0, 3) -- TODO: better anchoring - b.icon = b:CreateTexture("BACKGROUND") - b.icon:SetAllPoints() - b.icon:Show() - b:SetHighlightTexture("Interface\\Buttons\\ButtonHilight-Square") - b:GetHighlightTexture():SetBlendMode("ADD") - b:RegisterForClicks("AnyUp") - b:SetScript("OnShow",UpdateFlyoutIcon) - b:SetScript("OnEnter",ShowFlyoutTooltip) - b:SetScript("OnLeave",HideFlyoutTooltip) - b:SetAttribute("index",i) - f:SetAttribute("flyout-child-idx",i) - f:SetFrameRef("flyout-child",b) - f:Execute([[ - flyoutChildren = flyoutChildren or newtable() - flyoutChildren[self:GetAttribute("flyout-child-idx")] = self:GetFrameRef("flyout-child") - ]]) - f:WrapScript(b, "OnClick", _flyout_child_preClick, _flyout_child_postClick) - b:Show() - flyout.buttons[i] = b - end - end - - -- scale flyout frame - local scale = bar:GetButtonSize() / 36 - flyout:SetScale(scale) - - function f:UpdateFlyoutTextures(slot) - flyout:UpdateTextures(slot) - end - - f:Execute(_bar_init) -end -
--- a/classes/Overlay.lua Thu Nov 18 13:11:08 2010 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,754 +0,0 @@ -local addonName, addonTable = ... -local ReAction = addonTable.ReAction -local L = ReAction.L -local LKB = ReAction.LKB -local CreateFrame = CreateFrame -local InCombatLockdown = InCombatLockdown -local floor = math.floor -local min = math.min -local format = string.format -local GameTooltip = GameTooltip -local Bar = ReAction.Bar -local GetSize = Bar.GetSize -local SetSize = Bar.SetSize -local GetButtonSize = Bar.GetButtonSize -local GetButtonGrid = Bar.GetButtonGrid -local SetButtonSize = Bar.SetButtonSize -local SetButtonGrid = Bar.SetButtonGrid -local ApplyAnchor = Bar.ApplyAnchor -local GameTooltipTextRight1 = GameTooltipTextRight1 -local GameTooltipTextRight2 = GameTooltipTextRight2 -local GameTooltipTextRight3 = GameTooltipTextRight3 - --- --- Wrap some of the bar manipulators to make them state-aware --- -local function SetAnchor( bar, point, frame, relPoint, x, y ) - local state = bar:GetSecureState() - if state then - local anchorstate = bar:GetStateProperty(state, "anchorEnable") - if anchorstate then - bar:SetStateProperty(state, "anchorFrame", frame) - bar:SetStateProperty(state, "anchorPoint", point) - bar:SetStateProperty(state, "anchorRelPoint", relPoint) - bar:SetStateProperty(state, "anchorX", x or 0) - bar:SetStateProperty(state, "anchorY", y or 0) - bar:SetAnchor(bar:GetAnchor()) - return - end - end - bar:SetAnchor(point, frame, relPoint, x, y) -end - -local function GetStateScale( bar ) - local state = bar:GetSecureState() - if state and bar:GetStateProperty(state, "enableScale") then - return bar:GetStateProperty(state, "scale") - end -end - -local function SetStateScale( bar, scale ) - local state = bar:GetSecureState() - if state and bar:GetStateProperty(state, "enableScale") then - bar:SetStateProperty(state, "scale", scale) - end -end - - --- --- Bar config overlay --- - -local function GetNormalTextColor() - return 1.0, 1.0, 1.0, 1.0 -end - -local function GetAnchoredTextColor() - return 1.0, 1.0, 1.0, 1.0 -end - -local function GetNormalBgColor() - return 0.7, 0.7, 1.0, 0.3 -end - -local function GetAnchoredBgColor() - return 0.9, 0.2, 0.7, 0.3 -end - -local function StoreSize(bar) - local f = bar:GetFrame() - SetSize( bar, f:GetWidth(), f:GetHeight() ) -end - -local function StoreExtents(bar) - local f = bar:GetFrame() - local p, fr, rp, x, y = f:GetPoint(1) - fr = fr and fr:GetName() or "UIParent" - SetAnchor( bar, p, fr, rp, x, y ) - SetSize( bar, f:GetWidth(), f:GetHeight() ) -end - -local function RecomputeButtonSize(bar) - local w, h = GetSize(bar) - local bw, bh = GetButtonSize(bar) - local r, c, s = GetButtonGrid(bar) - - local scaleW = (floor(w/c) - s) / bw - local scaleH = (floor(h/r) - s) / bh - local scale = min(scaleW, scaleH) - - SetButtonSize(bar, scale * bw, scale * bh, s) -end - -local function ComputeBarScale(bar, overlay) - local w, h = overlay:GetWidth() - 8, overlay:GetHeight() - 8 - local bw, bh = GetButtonSize(bar) - local r, c, s = GetButtonGrid(bar) - - local scaleW = w / (c*(bw+s)) - local scaleH = h / (r*(bh+s)) - local scale = min(scaleW, scaleH) - - if scale > 2.5 then - scale = 2.5 - elseif scale < 0.25 then - scale = 0.25 - end - - return scale -end - -local function RecomputeButtonSpacing(bar) - local w, h = GetSize(bar) - local bw, bh = GetButtonSize(bar) - local r, c, s = GetButtonGrid(bar) - - SetButtonGrid(bar,r,c,min(floor(w/c) - bw, floor(h/r) - bh)) -end - -local function RecomputeGrid(bar) - local w, h = GetSize(bar) - local bw, bh = GetButtonSize(bar) - local r, c, s = GetButtonGrid(bar) - - SetButtonGrid(bar, floor(h/(bh+s)), floor(w/(bw+s)), s) -end - -local function ClampToButtons(bar) - local bw, bh = GetButtonSize(bar) - local r, c, s = GetButtonGrid(bar) - SetSize(bar, (bw+s)*c + 1, (bh+s)*r + 1) -end - -local function HideGameTooltip() - GameTooltip:Hide() -end - -local anchorInside = { inside = true } -local anchorOutside = { outside = true } -local edges = { "BOTTOM", "TOP", "LEFT", "RIGHT" } -local oppositeEdges = { - TOP = "BOTTOM", - BOTTOM = "TOP", - LEFT = "RIGHT", - RIGHT = "LEFT" -} -local pointsOnEdge = { - BOTTOM = { "BOTTOM", "BOTTOMLEFT", "BOTTOMRIGHT", }, - TOP = { "TOP", "TOPLEFT", "TOPRIGHT", }, - RIGHT = { "RIGHT", "BOTTOMRIGHT", "TOPRIGHT", }, - LEFT = { "LEFT", "BOTTOMLEFT", "TOPLEFT", }, -} -local edgeSelector = { - BOTTOM = 1, -- select x of x,y - TOP = 1, -- select x of x,y - LEFT = 2, -- select y of x,y - RIGHT = 2, -- select y of x,y -} -local snapPoints = { - [anchorOutside] = { - BOTTOMLEFT = {"BOTTOMRIGHT","TOPLEFT","TOPRIGHT"}, - BOTTOM = {"TOP"}, - BOTTOMRIGHT = {"BOTTOMLEFT","TOPRIGHT","TOPLEFT"}, - RIGHT = {"LEFT"}, - TOPRIGHT = {"TOPLEFT","BOTTOMRIGHT","BOTTOMLEFT"}, - TOP = {"BOTTOM"}, - TOPLEFT = {"TOPRIGHT","BOTTOMLEFT","BOTTOMRIGHT"}, - LEFT = {"RIGHT"}, - CENTER = {"CENTER"} - }, - [anchorInside] = { - BOTTOMLEFT = {"BOTTOMLEFT"}, - BOTTOM = {"BOTTOM"}, - BOTTOMRIGHT = {"BOTTOMRIGHT"}, - RIGHT = {"RIGHT"}, - TOPRIGHT = {"TOPRIGHT"}, - TOP = {"TOP"}, - TOPLEFT = {"TOPLEFT"}, - LEFT = {"LEFT"}, - CENTER = {"CENTER"} - } -} -local insidePointOffsetFuncs = { - BOTTOMLEFT = function(x, y) return x, y end, - BOTTOM = function(x, y) return 0, y end, - BOTTOMRIGHT = function(x, y) return -x, y end, - RIGHT = function(x, y) return -x, 0 end, - TOPRIGHT = function(x, y) return -x, -y end, - TOP = function(x, y) return 0, -y end, - TOPLEFT = function(x, y) return x, -y end, - LEFT = function(x, y) return x, 0 end, - CENTER = function(x, y) return x, y end, -} -local pointCoordFuncs = { - BOTTOMLEFT = function(f) return f:GetLeft(), f:GetBottom() end, - BOTTOM = function(f) return nil, f:GetBottom() end, - BOTTOMRIGHT = function(f) return f:GetRight(), f:GetBottom() end, - RIGHT = function(f) return f:GetRight(), nil end, - TOPRIGHT = function(f) return f:GetRight(), f:GetTop() end, - TOP = function(f) return nil, f:GetTop() end, - TOPLEFT = function(f) return f:GetLeft(), f:GetTop() end, - LEFT = function(f) return f:GetLeft(), nil end, - CENTER = function(f) return f:GetCenter() end, -} -local edgeBoundsFuncs = { - BOTTOM = function(f) return f:GetLeft(), f:GetRight() end, - LEFT = function(f) return f:GetBottom(), f:GetTop() end -} -edgeBoundsFuncs.TOP = edgeBoundsFuncs.BOTTOM -edgeBoundsFuncs.RIGHT = edgeBoundsFuncs.LEFT -local cornerTexCoords = { - -- ULx, ULy, LLx, LLy, URx, URy, LRx, LRy - TOPLEFT = { 1, 1, 1, 0, 0, 1, 0, 0 }, - TOPRIGHT = { 1, 0, 0, 0, 1, 1, 0, 1 }, - BOTTOMLEFT = { 0, 1, 1, 1, 0, 0, 1, 0 }, - BOTTOMRIGHT = { 0, 0, 0, 1, 1, 0, 1, 1 }, -} - --- Returns absolute coordinates x,y of the named point 'p' of frame 'f' -local function GetPointCoords( f, p ) - local x, y = pointCoordFuncs[p](f) - if not(x and y) then - local cx, cy = f:GetCenter() - x = x or cx - y = y or cy - end - return x, y -end - - --- Returns true if frame 'f1' can be anchored to frame 'f2' -local function CheckAnchorable( f1, f2 ) - -- can't anchor a frame to itself or to nil - if f1 == f2 or f2 == nil then - return false - end - - -- can always anchor to UIParent - if f2 == UIParent then - return true - end - - -- also can't do circular anchoring of frames - -- walk the anchor chain, which generally shouldn't be that expensive - -- (who nests draggables that deep anyway?) - for i = 1, f2:GetNumPoints() do - local _, f = f2:GetPoint(i) - if not f then f = f2:GetParent() end - return CheckAnchorable(f1,f) - end - - return true -end - --- Returns true if frames f1 and f2 specified edges overlap -local function CheckEdgeOverlap( f1, f2, e ) - local l1, u1 = edgeBoundsFuncs[e](f1) - local l2, u2 = edgeBoundsFuncs[e](f2) - return l1 <= l2 and l2 <= u1 or l2 <= l1 and l1 <= u2 -end - --- Returns true if point p1 on frame f1 overlaps edge e2 on frame f2 -local function CheckPointEdgeOverlap( f1, p1, f2, e2 ) - local l, u = edgeBoundsFuncs[e2](f2) - local x, y = GetPointCoords(f1,p1) - x = select(edgeSelector[e2], x, y) - return l <= x and x <= u -end - --- Returns the distance between corresponding edges. It is --- assumed that the passed in edges e1 and e2 are the same or opposites -local function GetEdgeDistance( f1, f2, e1, e2 ) - local x1, y1 = pointCoordFuncs[e1](f1) - local x2, y2 = pointCoordFuncs[e2](f2) - return math.abs((x1 or y1) - (x2 or y2)) -end - -local globalSnapTargets = { [UIParent] = anchorInside } - -local function GetClosestFrameEdge(f1,f2,a) - local dist, edge, opp - if f2:IsVisible() and CheckAnchorable(f1,f2) then - for _, e in pairs(edges) do - local o = a.inside and e or oppositeEdges[e] - if CheckEdgeOverlap(f1,f2,e) then - local d = GetEdgeDistance(f1, f2, e, o) - if not dist or (d < dist) then - dist, edge, opp = d, e, o - end - end - end - end - return dist, edge, opp -end - -local function GetClosestVisibleEdge( f ) - local r, o, e1, e2 - local a = anchorOutside - for _, b in ReAction:IterateBars() do - local d, e, opp = GetClosestFrameEdge(f,b:GetFrame(),a) - if d and (not r or d < r) then - r, o, e1, e2 = d, b:GetFrame(), e, opp - end - end - for f2, a2 in pairs(globalSnapTargets) do - local d, e, opp = GetClosestFrameEdge(f,f2,a2) - if d and (not r or d < r) then - r, o, e1, e2, a = d, f2, e, opp, a2 - end - end - return o, e1, e2, a -end - -local function GetClosestVisiblePoint(f1) - local f2, e1, e2, a = GetClosestVisibleEdge(f1) - if f2 then - local rsq, p, rp, x, y - -- iterate pointsOnEdge in order and use < to prefer edge centers to corners - for _, p1 in ipairs(pointsOnEdge[e1]) do - if CheckPointEdgeOverlap(f1,p1,f2,e2) then - for _, p2 in pairs(snapPoints[a][p1]) do - local x1, y1 = GetPointCoords(f1,p1) - local x2, y2 = GetPointCoords(f2,p2) - local dx = x1 - x2 - local dy = y1 - y2 - local rsq2 = dx*dx + dy*dy - if not rsq or rsq2 < rsq then - rsq, p, rp, x, y = rsq2, p1, p2, dx, dy - end - end - end - end - return f2, p, rp, x, y - end -end - -local function GetClosestPointSnapped(f1, rx, ry, xOff, yOff) - local o, p, rp, x, y = GetClosestVisiblePoint(f1) - local s = false - - local insideOffsetFunc = p and insidePointOffsetFuncs[p] - local coordFunc = p and pointCoordFuncs[p] - if not insideOffsetFunc or not coordFunc then - return - end - - local sx, sy = insideOffsetFunc(xOff or 0, yOff or 0) - local xx, yy = coordFunc(f1) - if xx and yy then - if math.abs(x) <= rx then - if math.abs(y) <= ry then - x = sx - y = sy - s = true - elseif CheckEdgeOverlap(f1,o,"LEFT") then - x = sx - s = true - end - elseif math.abs(y) <= ry and CheckEdgeOverlap(f1,o,"TOP") then - y = sy - s = true - end - elseif xx then - if math.abs(x) <= rx then - x = sx - s = true - if math.abs(y) <= ry then - y = sy - end - end - elseif yy then - if math.abs(y) <= ry then - y = sy - s = true - if math.abs(x) <= rx then - x = sx - end - end - end - - -- correct for some Lua oddities with doubles - if x == -0 then x = 0 end - if y == -0 then y = 0 end - - if s then - return o, p, rp, math.floor(x), math.floor(y) - end -end - -local function CreateSnapIndicator() - local si = CreateFrame("Frame",nil,UIParent) - si:SetFrameStrata("HIGH") - si:SetHeight(16) - si:SetWidth(16) - local tex = si:CreateTexture() - tex:SetAllPoints() - tex:SetTexture("Interface\\AddOns\\ReAction\\img\\lock") - tex:SetBlendMode("ADD") - tex:SetDrawLayer("OVERLAY") - return si -end - -local si1 = CreateSnapIndicator() -local si2 = CreateSnapIndicator() - -local function DisplaySnapIndicator( f, rx, ry, xOff, yOff ) - local o, p, rp, x, y, snap = GetClosestPointSnapped(f, rx, ry, xOff, yOff) - if o then - si1:ClearAllPoints() - si2:ClearAllPoints() - si1:SetPoint("CENTER", f, p, 0, 0) - local xx, yy = pointCoordFuncs[rp](o) - x = math.abs(x) <=rx and xx and 0 or x - y = math.abs(y) <=ry and yy and 0 or y - si2:SetPoint("CENTER", o, rp, x, y) - si1:Show() - si2:Show() - else - if si1:IsVisible() then - si1:Hide() - si2:Hide() - end - end - return o, p -end - -local function HideSnapIndicator() - if si1:IsVisible() then - si1:Hide() - si2:Hide() - end -end - -local function UpdateLabelString(overlay) - local label = overlay.labelString - if label then - local name = overlay.labelName - if name and overlay.labelSubtext then - name = format("%s (%s)", name, overlay.labelSubtext) - end - label:SetText(name or "") - end -end - -local function CreateControls(bar) - local f = bar:GetFrame() - - f:SetMovable(true) - f:SetResizable(true) - - -- child of UIParent so that alpha and scale doesn't propagate to it - local overlay = CreateFrame("Button", nil, UIParent) - overlay:EnableMouse(true) - overlay:SetFrameLevel(10) -- set it above the buttons - overlay:SetPoint("TOPLEFT", f, "TOPLEFT", -4, 4) - overlay:SetPoint("BOTTOMRIGHT", f, "BOTTOMRIGHT", 4, -4) - overlay:SetBackdrop({ - edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border", - tile = true, - tileSize = 16, - edgeSize = 16, - insets = { left = 0, right = 0, top = 0, bottom = 0 }, - }) - - -- textures - local bgTex = overlay:CreateTexture(nil,"BACKGROUND") - bgTex:SetTexture(0.7,0.7,1.0,0.2) - bgTex:SetPoint("TOPLEFT",4,-4) - bgTex:SetPoint("BOTTOMRIGHT",-4,4) - local hTex = overlay:CreateTexture(nil,"HIGHLIGHT") - hTex:SetTexture(0.7,0.7,1.0,0.2) - hTex:SetPoint("TOPLEFT",4,-4) - hTex:SetPoint("BOTTOMRIGHT",-4,4) - hTex:SetBlendMode("ADD") - local aTex = overlay:CreateTexture(nil,"ARTWORK") - aTex:SetTexture("Interface\\AddOns\\ReAction\\img\\lock") - aTex:SetWidth(16) - aTex:SetHeight(16) - aTex:Hide() - - -- label - local label = overlay:CreateFontString(nil,"OVERLAY","GameFontNormalLarge") - label:SetAllPoints() - label:SetJustifyH("CENTER") - label:SetShadowColor(0,0,0,1) - label:SetShadowOffset(3,-3) - label:SetTextColor(GetNormalTextColor()) - label:SetText(bar:GetName()) - label:Show() - overlay.labelString = label - overlay.labelName = bar:GetName() - - local function UpdateAnchorDecoration() - local point, anchor, relPoint, x, y = f:GetPoint(1) - if point then - local ofsx, ofsy = insidePointOffsetFuncs[point](x,y) - if (anchor and anchor ~= UIParent) or (ofsx == 0 and ofsy == 0) then - bgTex:SetTexture( GetAnchoredBgColor() ) - hTex:SetTexture( GetAnchoredBgColor() ) - label:SetTextColor( GetAnchoredTextColor() ) - aTex:ClearAllPoints() - aTex:SetPoint(point) - aTex:Show() - return - end - end - bgTex:SetTexture( GetNormalBgColor() ) - hTex:SetTexture( GetNormalBgColor() ) - label:SetTextColor( GetNormalTextColor() ) - aTex:Hide() - end - - local function StopResize() - f:StopMovingOrSizing() - f.isMoving = false - f:SetScript("OnUpdate",nil) - StoreSize(bar) - ClampToButtons(bar) - ReAction:RefreshEditor() - end - - local function CornerUpdate() - local bw, bh = GetButtonSize(bar) - local r, c, s = GetButtonGrid(bar) - local ss = GetStateScale(bar) - if IsShiftKeyDown() then - if ss then - f:SetMinResize( ((s+bw)*c*0.25)/ss, ((s+bh)*r*0.25)/ss ) - f:SetMaxResize( ((s+bw)*c*2.5 + 1)/ss, ((s+bh)*r*2.5 + 1)/ss ) - scale = ComputeBarScale(bar, overlay) - else - f:SetMinResize( (s+12)*c+1, (s+12)*r+1 ) - f:SetMaxResize( (s+128)*c+1, (s+128)*r+1 ) - RecomputeButtonSize(bar) - end - elseif not ss and IsAltKeyDown() then - f:SetMinResize( bw*c, bh*r ) - f:SetMaxResize( 2*bw*c, 2*bh*r ) - RecomputeButtonSpacing(bar) - else - f:SetMinResize( bw+s+1, bh+s+1 ) - f:SetMaxResize( 50*(bw+s)+1, 50*(bh+s)+1 ) - RecomputeGrid(bar) - end - GameTooltipTextRight2:SetText(format("%d x %d",r,c)) - - local ss = GetStateScale(bar) - if ss then - GameTooltipTextRight4:SetText(format("%d%%", scale*100)) - else - local size = (bw == bh) and tostring(bw) or format("%d x %d",bw,bh) - GameTooltipTextRight3:SetText(size) - GameTooltipTextRight4:SetText(tostring(s)) - end - end - - -- corner drag handles - for _, point in pairs({"BOTTOMLEFT","TOPLEFT","BOTTOMRIGHT","TOPRIGHT"}) do - local corner = CreateFrame("Frame",nil,overlay) - corner:EnableMouse(true) - corner:SetWidth(16) - corner:SetHeight(16) - corner:SetPoint(point) - - local tex = corner:CreateTexture(nil,"HIGHLIGHT") - tex:SetTexture("Interface\\AddOns\\ReAction\\img\\corner") - tex:SetTexCoord(unpack(cornerTexCoords[point])) - tex:SetBlendMode("ADD") - tex:SetAlpha(0.6) - tex:SetAllPoints() - - corner:SetScript("OnMouseDown", - function(_,btn) - f:SetScript("OnUpdate", CornerUpdate) - f:StartSizing(point) - end - ) - corner:SetScript("OnMouseUp", - function() - local ss = GetStateScale(bar) - if ss then - SetStateScale(bar, ComputeBarScale(bar, overlay)) - end - StopResize() - end) - corner:SetScript("OnEnter", - function() - local bw, bh = GetButtonSize(bar) - local r, c, s = bar:GetButtonGrid() - local size = (bw == bh) and tostring(bw) or format("%d x %d",bw,bh) - local ss = GetStateScale(bar) - local state = bar:GetSecureState() - GameTooltip:SetOwner(f, "ANCHOR_"..point) - if ss then - GameTooltip:AddLine(format("%s (%s: %s)", bar:GetName(), L["State"], state)) - else - GameTooltip:AddLine(bar:GetName()) - end - GameTooltip:AddDoubleLine(format("|cffcccccc%s|r %s",L["Drag"],L["to add/remove buttons:"]), format("%d x %d",r,c)) - if ss then - GameTooltip:AddLine(L["State Scale Override"]) - GameTooltip:AddDoubleLine(format("|cff00ff00%s|r %s",L["Hold Shift"],L["to change scale:"]), format("%d%%", bar:GetStateProperty(state,"scale")*100)) - else - GameTooltip:AddDoubleLine(format("|cff00ff00%s|r %s",L["Hold Shift"],L["to resize buttons:"]), tostring(floor(size))) - GameTooltip:AddDoubleLine(format("|cff0033cc%s|r %s",L["Hold Alt"], L["to change spacing:"]), tostring(floor(s))) - end - GameTooltip:Show() - end - ) - corner:SetScript("OnLeave", - function() - GameTooltip:Hide() - f:SetScript("OnUpdate",nil) - end - ) - end - - overlay:RegisterForDrag("LeftButton") - overlay:RegisterForClicks("RightButtonUp") - - overlay:SetScript("OnDragStart", - function() - f:StartMoving() - f.isMoving = true - local w,h = bar:GetButtonSize() - f:ClearAllPoints() - UpdateAnchorDecoration() - f:SetScript("OnUpdate", function() - if IsShiftKeyDown() then - local f, p = DisplaySnapIndicator(f,w,h) - else - HideSnapIndicator() - end - end) - end - ) - - local function UpdateDragTooltip() - GameTooltip:SetOwner(f, "ANCHOR_TOPRIGHT") - local ss = GetStateScale(bar) - local state = bar:GetSecureState() - if ss then - GameTooltip:AddLine(format("%s (%s: %s)", bar:GetName(), L["State"], state)) - else - GameTooltip:AddLine(bar:GetName()) - end - GameTooltip:AddLine(format("|cffcccccc%s|r %s",L["Drag"],L["to move"])) - GameTooltip:AddLine(format("|cff00ff00%s|r %s",L["Hold Shift"],L["to anchor to nearby frames"])) - GameTooltip:AddLine(format("|cff00cccc%s|r %s",L["Right-click"],L["for options..."])) - local point, frame, relpoint, x, y = bar:GetFrame():GetPoint(1) - if point then - local ofsx, ofsy = insidePointOffsetFuncs[point](x,y) - if (frame and frame ~= UIParent) or (ofsx == 0 and ofsy == 0) then - frame = frame or UIParent - GameTooltip:AddLine(format("%s <%s>",L["Currently anchored to"],frame:GetName())) - end - end - GameTooltip:Show() - end - - overlay:SetScript("OnDragStop", - function() - f:StopMovingOrSizing() - f.isMoving = false - f:SetScript("OnUpdate",nil) - - if IsShiftKeyDown() then - local w, h = bar:GetButtonSize() - local a, p, rp, x, y = GetClosestPointSnapped(f,w,h) - if a then - f:ClearAllPoints() - f:SetPoint(p,a,rp,x,y) - end - HideSnapIndicator() - end - - StoreExtents(bar) - ReAction:RefreshEditor() - UpdateDragTooltip() - UpdateAnchorDecoration() - end - ) - - overlay:SetScript("OnEnter", - function() - UpdateDragTooltip() - end - ) - - overlay:SetScript("OnLeave", HideGameTooltip) - - overlay:SetScript("OnClick", - function() - ReAction:ShowEditor(bar) - end - ) - - function overlay:RefreshControls() - UpdateAnchorDecoration() - end - - overlay:SetScript("OnShow", overlay.RefreshControls) - - if ReAction:GetKeybindMode() then - overlay:SetFrameLevel(1) - end - - UpdateLabelString(overlay) - UpdateAnchorDecoration() - - return overlay -end - - --- export methods to the Bar prototype -Bar.Overlay = { } -function Bar.Overlay:New( bar ) - return setmetatable( {frame = CreateControls(bar)}, {__index=self} ) -end - -function Bar.Overlay:SetLabel(name) - self.frame.labelName = name - UpdateLabelString(self.frame) -end - -function Bar.Overlay:SetLabelSubtext(text) - self.frame.labelSubtext = text - UpdateLabelString(self.frame) -end - -function Bar.Overlay:Show() - self.frame:Show() -end - -function Bar.Overlay:Hide() - self.frame:Hide() -end - -function Bar.Overlay:IsShown() - return self.frame:IsShown() -end - -function Bar.Overlay:RefreshControls() - self.frame:RefreshControls() -end \ No newline at end of file
--- a/classes/PetActionButton.lua Thu Nov 18 13:11:08 2010 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,286 +0,0 @@ -local addonName, addonTable = ... -local ReAction = addonTable.ReAction -local L = ReAction.L -local _G = _G -local CreateFrame = CreateFrame -local format = string.format -local GetCVar = GetCVar -local InCombatLockdown = InCombatLockdown -local GetPetActionInfo = GetPetActionInfo -local GetPetActionSlotUsable = GetPetActionSlotUsable -local GetPetActionCooldown = GetPetActionCooldown -local AutoCastShine_AutoCastStart = AutoCastShine_AutoCastStart -local AutoCastShine_AutoCastStop = AutoCastShine_AutoCastStop -local SetDesaturation = SetDesaturation -local CooldownFrame_SetTimer = CooldownFrame_SetTimer -local GameTooltip_SetDefaultAnchor = GameTooltip_SetDefaultAnchor - --- --- Secure snippets --- These are run within the context of the bar's sandbox, as the --- buttons themselves do not have their own sandbox. --- -local _onDragStart = -- function(self, button, kind, value, ...) -[[ - if lockButtons and (PlayerInCombat() or not lockButtonsCombat) and not IsModifiedClick("PICKUPACTION") then - return kind, value, ... - else - return "petaction", self:GetAttribute("action") - end -]] - -local _onReceiveDrag = -- function(self, button, kind, value, ...) -[[ - if kind then -- pet spells on the cursor return nil from GetCursorInfo(), which is very strange - return kind, value, ... - end - return "petaction", self:GetAttribute("action") -]] - --- --- private --- -local eventList = { -"PLAYER_CONTROL_LOST", -"PLAYER_CONTROL_GAINED", -"PLAYER_FARSIGHT_FOCUS_CHANGED", -"UNIT_PET", -"UNIT_FLAGS", -"UNIT_AURA", -"PET_BAR_UPDATE", -"PET_BAR_UPDATE_COOLDOWN", -"PET_BAR_UPDATE_USABLE", -"UPDATE_BINDINGS", -} - --- --- Pet Action Button class --- -local Super = ReAction.Button -local Pet = setmetatable( { }, { __index = Super } ) -ReAction.Button.PetAction = Pet - -function Pet:New( idx, config, bar, idHint ) - local name = format("ReAction_%s_PetAction_%d",bar:GetName(),idx) - - self = Super.New(self, name, config, bar, idx, "SecureActionButtonTemplate, ActionButtonTemplate" ) - - local f = self:GetFrame() - if not f.autoCastTexture then - -- store autocast stuff with the frame for recycling - local tex = f:CreateTexture(nil,"OVERLAY") - tex:SetTexture([[Interface\Buttons\UI-AutoCastableOverlay]]) - tex:SetHeight(58) - tex:SetWidth(58) - tex:SetPoint("CENTER") - f.autoCastTexture = tex - f.autoCastShine = CreateFrame("Frame",name.."Shine",f,"AutoCastShineTemplate") -- create after autocast texture so it's on top - -- move the cooldown around - local cd = self.frames.cooldown - cd:ClearAllPoints() - cd:SetWidth(33) - cd:SetHeight(33) - cd:SetPoint("CENTER", f, "CENTER", -2, -1) - -- resize to 30x30 - f:SetHeight(30) - f:SetWidth(30) - local nt = _G[name.."NormalTexture"] - nt:SetHeight(54) - nt:SetWidth(54) - end - local barFrame = bar:GetFrame() - - -- set up the base action ID - self:SetActionIDPool("pet",10) - config.actionID = self:AcquireActionID(config.actionID, idHint, true) - - -- attribute setup - -- There's no secure way to do PetAutoCastToggle by actionID, so use - -- a click-through proxy to the Blizzard pet buttons for right-click - -- Note that technically this doesn't do PetStopAttack() when - -- IsPetAttackActive() is true: however that's only true when using - -- Eyes of the Beast and appears not to really do anything (at least - -- I can't find any difference) - f:SetAttribute("type","pet") - f:SetAttribute("type2","click") - f:SetAttribute("clickbutton2",_G["PetActionButton"..config.actionID]) - f:SetAttribute("action",config.actionID) - f:SetAttribute("checkselfcast", true) - f:SetAttribute("checkfocuscast", true) - - -- non secure scripts - f:SetScript("OnEvent", function(frame, ...) self:OnEvent(...) end) - f:SetScript("OnEnter", function(frame) self:OnEnter() end) - f:SetScript("OnLeave", function(frame) self:OnLeave() end) - f:SetScript("OnAttributeChanged", function(frame, attr, value) self:OnAttributeChanged(attr, value) end) - f:SetScript("PreClick", function(frame) self:PreClick() end) - f:SetScript("OnDragStart", function(frame) self:OnDragStart() end) - f:SetScript("OnReceiveDrag", function(frame) self:OnReceiveDrag() end) - - -- secure handlers - barFrame:WrapScript(f, "OnDragStart", _onDragStart) - barFrame:WrapScript(f, "OnReceiveDrag", _onReceiveDrag) - - -- event registration - f:EnableMouse(true) - f:RegisterForDrag("LeftButton", "RightButton") - f:RegisterForClicks("AnyUp") - for _, evt in pairs(eventList) do - f:RegisterEvent(evt) - end - - -- attach to skinner - bar:SkinButton(self, - { - AutoCast = f.autoCastShine, - AutoCastable = f.autoCastTexture - }) - - self:Refresh() - f:Show() - - return self -end - -function Pet:Refresh() - Super.Refresh(self) - self:Update() - self:UpdateHotkey() -end - -function Pet:GetActionID() - return self.config.actionID -end - -function Pet:SetActionID(id) - if not InCombatLockdown() then - if id < 0 or id > 10 then - ReAction:UserError(L["Pet action ID range is 1-10"]) - return - end - self.config.actionID = id - f:SetAttribute("clickbutton2",_G["PetActionButton"..id]) - f:SetAttribute("action",id) - self:Update() - self:UpdateHotkey() - end -end - -function Pet:Update() - local action = self.config.actionID - local name, subtext, texture, isToken, isActive, autoCastAllowed, autoCastEnabled = GetPetActionInfo(action) - local f = self:GetFrame() - local icon = self.frames.icon - - if isToken then - icon:SetTexture(_G[texture]) - self.tooltipName = _G[name] - else - icon:SetTexture(texture) - self.tooltipName = name - end - - self.isToken = isToken - self.tooltipSubtext = subtext - f:SetChecked( isActive and 1 or 0 ) - - if autoCastAllowed then - f.autoCastTexture:Show() - else - f.autoCastTexture:Hide() - end - - if autoCastEnabled then - AutoCastShine_AutoCastStart(f.autoCastShine) - else - AutoCastShine_AutoCastStop(f.autoCastShine) - end - - if texture then - if GetPetActionSlotUsable(action) then - SetDesaturation(icon,nil) - else - SetDesaturation(icon,1) - end - icon:Show() - f:SetNormalTexture("Interface\\Buttons\\UI-Quickslot2") - else - icon:Hide() - f:SetNormalTexture("Interface\\Buttons\\UI-Quickslot") - end - - self:UpdateCooldown() -end - -function Pet:UpdateCooldown() - CooldownFrame_SetTimer(self.frames.cooldown, GetPetActionCooldown(self.config.actionID)) -end - -function Pet:SetTooltip() - if self.tooltipName then - local f = self:GetFrame() - local uber = GetCVar("UberTooltips") - if self.isToken or (uber == "0") then - if uber == "0" then - GameTooltip:SetOwner(f, "ANCHOR_RIGHT") - else - GameTooltip_SetDefaultAnchor(GameTooltip, f) - end - GameTooltip:SetText(self.tooltipName) - if self.tooltipSubtext then - GameTooltip:AddLine(self.tooltipSubtext, "", 0.5, 0.5, 0.5) - end - GameTooltip:Show() - else - GameTooltip_SetDefaultAnchor(GameTooltip, f) - GameTooltip:SetPetAction(self.config.actionID) - end - else - GameTooltip:Hide() - end -end - -function Pet:OnEvent(event, unit) - if event =="PET_BAR_UPDATE_COOLDOWN" then - self:UpdateCooldown() - elseif event == "UPDATE_BINDINGS" then - self:UpdateHotkey() - elseif event == "UNIT_PET" then - if unit == "player" then - self:Update() - end - elseif event == "UNIT_FLAGS" or event == "UNIT_AURA" then - if unit == "pet" then - self:Update() - end - else - self:Update() - end -end - -function Pet:OnEnter() - self:SetTooltip() -end - -function Pet:OnLeave() - GameTooltip:Hide() -end - -function Pet:OnAttributeChanged(attr,value) - self:Update() -end - -function Pet:PreClick() - self:GetFrame():SetChecked(0) -end - -function Pet:OnDragStart() - self:SetChecked(0) - self:Update() -end - -function Pet:OnReceiveDrag() - self:SetChecked(0) - self:Update() -end -
--- a/classes/StanceButton.lua Thu Nov 18 13:11:08 2010 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,182 +0,0 @@ -local addonName, addonTable = ... -local ReAction = addonTable.ReAction -local L = ReAction.L -local _G = _G -local CreateFrame = CreateFrame -local format = string.format -local GetCVar = GetCVar -local GameTooltip_SetDefaultAnchor = GameTooltip_SetDefaultAnchor -local CooldownFrame_SetTimer = CooldownFrame_SetTimer -local InCombatLockdown = InCombatLockdown -local GetNumShapeshiftForms = GetNumShapeshiftForms -local GetShapeshiftFormInfo = GetShapeshiftFormInfo -local IsUsableSpell = IsUsableSpell -local GetSpellInfo = GetSpellInfo - --- --- private --- -local playerClass = select(2,UnitClass("player")) - -local eventList = { - "PLAYER_REGEN_ENABLED", - "PLAYER_ENTERING_WORLD", - "UPDATE_SHAPESHIFT_FORM", - "UPDATE_SHAPESHIFT_FORMS", - "UPDATE_SHAPESHIFT_USABLE", - "UPDATE_SHAPESHIFT_COOLDOWN", - "UPDATE_BINDINGS", -} - --- --- Stance Button class --- -local Super = ReAction.Button -local Stance = setmetatable( { }, { __index = Super } ) -ReAction.Button.Stance = Stance - -function Stance:New( idx, moduleConfig, bar, idHint ) - local name = format("ReAction_%s_Stance_%d",bar:GetName(),idx) - - self = Super.New(self, name, moduleConfig.buttons[bar:GetName()][idx], bar, idx, "SecureActionButtonTemplate, ActionButtonTemplate" ) - self.moduleConfig = moduleConfig - - local f = self:GetFrame() - local barFrame = bar:GetFrame() - local config = self:GetConfig() - - -- set up the base stance ID - self:SetActionIDPool("stance",8) - config.stanceID = self:AcquireActionID(config.stanceID, idHint, true) - - -- attribute setup - f:SetAttribute("type","spell") - - -- non secure scripts - f:SetScript("OnEvent", function(frame, ...) self:OnEvent(...) end) - f:SetScript("OnEnter", function(frame) self:OnEnter() end) - f:SetScript("OnLeave", function(frame) self:OnLeave() end) - f:SetScript("PreClick", function(frame, ...) self:PreClick(...) end) - - -- secure handlers - -- (none) - - -- event registration - f:EnableMouse(true) - f:RegisterForClicks("AnyUp") - for _, evt in pairs(eventList) do - f:RegisterEvent(evt) - end - - -- attach to skinner - bar:SkinButton(self) - - -- initial display - if ReAction:GetConfigMode() then - self:GetFrame():Show() - end - - self:Refresh() - - return self -end - -function Stance:GetActionID() - return self.config.stanceID -end - -function Stance:UpdateAction() - if InCombatLockdown() then - self.updatePending = true - else - self.updatePending = false - local idx = self:GetActionID() - local f = self:GetFrame() - if idx > GetNumShapeshiftForms() then - f:Hide() - else - f:SetAttribute("spell", select(2,GetShapeshiftFormInfo(idx))) - f:Show() - self:Update() - end - end -end - -function Stance:Refresh() - Super.Refresh(self) - self:UpdateHotkey() - self:UpdateAction() -end - -function Stance:Update() - local texture, _, isActive, isCastable = GetShapeshiftFormInfo(self:GetActionID()) - - local icon = self.frames.icon - icon:SetTexture(texture) - self:GetFrame():SetChecked( isActive and 1 or 0 ) - if isCastable then - self.frames.hotkey:Show() - icon:SetVertexColor(1.0, 1.0, 1.0) - else - icon:SetVertexColor(0.4, 0.4, 0.4) - end - - self:UpdateCooldown() -end - -function Stance:UpdateCooldown() - local start, duration, enabled = GetShapeshiftFormCooldown(self:GetActionID()) - if start then - CooldownFrame_SetTimer(self.frames.cooldown, start, duration, enabled) - end -end - -function Stance:SetTooltip() - if GetCVar("UberTooltips") == "1" then - GameTooltip_SetDefaultAnchor(GameTooltip, self:GetFrame()) - else - GameTooltip:SetOwner(self:GetFrame(), "ANCHOR_RIGHT") - end - GameTooltip:SetShapeshift(self:GetActionID()) -end - -function Stance:OnEnter() - self:SetTooltip() -end - -function Stance:OnLeave() - GameTooltip:Hide() -end - -function Stance:PreClick() - local f = self:GetFrame() - f:SetChecked( not f:GetChecked() ) -end - -function Stance:OnEvent(event, arg) - if event == "PLAYER_REGEN_ENABLED" then - if self.updatePending then - self:UpdateAction() - end - elseif event == "UPDATE_SHAPESHIFT_COOLDOWN" then - self:UpdateCooldown() - elseif event == "UPDATE_SHAPESHIFT_FORMS" then - self:UpdateAction() - elseif event == "UNIT_AURA" then - if arg == "player" then - self:Update() - end - elseif event == "UPDATE_BINDINGS" then - self:UpdateHotkey() - else - self:Update() - end -end - -function Stance:ShowGridTemp(show) - if show then - self:GetFrame():Show() - else - self:UpdateAction() - end -end
--- a/classes/VehicleExitButton.lua Thu Nov 18 13:11:08 2010 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,63 +0,0 @@ -local addonName, addonTable = ... -local ReAction = addonTable.ReAction -local format = string.format - --- --- VExitButton Button class --- -local Super = ReAction.Button -local VExitButton = setmetatable( { }, { __index = Super } ) -ReAction.Button.VehicleExit = VExitButton - -function VExitButton:New( idx, config, bar ) - local name = format("ReAction_%s_VehicleExit_%d",bar:GetName(),idx) - - self = Super.New(self, name, config, bar, idx, "SecureFrameTemplate, ActionButtonTemplate", "Button") - - -- frame setup - local f = self:GetFrame() - self.frames.icon:SetTexture("Interface\\Vehicles\\UI-Vehicles-Button-Exit-Up") - self.frames.icon:SetTexCoord(0.140625, 0.859375, 0.140625, 0.859375) - - -- attribute setup - -- (none) - - -- non secure scripts - f:SetScript("OnClick", VehicleExit) - f:SetScript("OnEnter", function(frame) GameTooltip_AddNewbieTip(frame, LEAVE_VEHICLE, 1.0, 1.0, 1.0, nil) end) - f:SetScript("OnLeave", GameTooltip_Hide) - f:SetScript("OnEvent", function(frame, evt, ...) self:OnEvent(evt,...) end) - - -- event registration - f:EnableMouse(true) - f:RegisterForClicks("AnyUp") - f:RegisterEvent("UPDATE_BINDINGS") - - -- attach to skinner - bar:SkinButton(self) - - self:Refresh() - self:UpdateHotkey() - - return self -end - -function VExitButton:GetActionID() - return 1 -end - -function VExitButton:Refresh() - Super.Refresh(self) - -- it seems that setscale kills the texcoord, have to refresh it - self.frames.icon:SetTexCoord(0.140625, 0.859375, 0.140625, 0.859375) -end - -function VExitButton:OnEvent(event, ...) - if self[event] then - self[event](self, event, ...) - end -end - -function VExitButton:UPDATE_BINDINGS() - self:UpdateHotkey() -end
--- a/classes/classes.xml Thu Nov 18 13:11:08 2010 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,15 +0,0 @@ -<Ui xmlns="http://www.blizzard.com/wow/ui/" - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:schemaLocation="http://www.blizzard.com/wow/ui/..\FrameXML\UI.xsd"> - -<Script file="Bar.lua"/> -<Script file="Overlay.lua"/> -<Script file="Button.lua"/> -<Script file="ActionButton.lua"/> -<Script file="PetActionButton.lua"/> -<Script file="StanceButton.lua"/> -<Script file="BagButton.lua"/> -<Script file="VehicleExitButton.lua"/> -<Script file="MultiCastButton.lua"/> - -</Ui> \ No newline at end of file
--- a/locale/enUS.lua Thu Nov 18 13:11:08 2010 -0800 +++ b/locale/enUS.lua Tue Apr 12 16:06:31 2011 -0700 @@ -4,7 +4,6 @@ for _, string in pairs({ -- ReAction.lua -"Bar ", "ReAction: name '%s' already in use", "ReAction config mode disabled during combat.", @@ -80,13 +79,20 @@ "Y offset", "Transparency", --- PetActionButton.lua -"Pet action ID range is 1-10", - --- ActionButton.lua +-- classes/ActionButton.lua +"Action Bar", "Action ID range is 1-120", --- Overlay.lua +-- classes/BagButton.lua -- +"Bag Bar", + +-- classes/Bar.lua +"Hidden", + +-- classes/Button.lua -- +"Button Bar", + +-- classes/Overlay.lua "Hold Shift", "Hold Alt", "Right-click", @@ -102,10 +108,20 @@ "State", "State Scale Override", --- Bar.lua -"Hidden", +-- classes/MultiCastButton.lua +"Totem Bar", --- State.lua +-- classes/PetActionButton.lua +"Pet Action Bar", +"Pet action ID range is 1-10", + +-- classes/StanceButton.lua +"Stance Bar", + +-- classes/VehicleExitButton.lua +"Exit Vehicle Floater", + +-- modules/State.lua "State named '%s' already exists", "Battle Stance", "Defensive Stance", @@ -187,8 +203,7 @@ "Create State", "State named '%s' already exists", --- Action -"Action Bar", +-- modules/Action.lua "Action Bars", "Hide Empty Buttons", "Lock Buttons", @@ -219,12 +234,10 @@ "Show Page #", "Action Buttons", --- PetAction -"Pet Action Bar", +-- modules/PetAction.lua "Pet Buttons", --- Stance -"Stance Bar", +-- modules/Stance.lua "Stance Buttons", "Show Aspects", "Show Hunter aspects as stances", @@ -235,15 +248,7 @@ "Hide Auras", "Do not show Paladin Auras as stances", --- Totem -"Totem Bar", -"Totem Buttons", - --- Bag -"Bag Bar", - --- VehicleExit -"Exit Vehicle Floater", +-- modules/VehicleExit.lua "Exit Vehicle", "Show only when passenger", "Only show the button when riding as a passenger in a vehicle (no vehicle controls)",
--- a/modules/Action.lua Thu Nov 18 13:11:08 2010 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,611 +0,0 @@ -local addonName, addonTable = ... -local ReAction = addonTable.ReAction -local L = ReAction.L -local _G = _G -local CreateFrame = CreateFrame -local format = string.format -local wipe = wipe - -local weak = { __mode="k" } - --- module declaration -local moduleID = "Action" -local module = ReAction:NewModule( moduleID ) - --- Class declarations -local Button = ReAction.Button.Action -- see /classes/ActionButton.lua -local Handle = { } -local PropHandler = { } - --- Event handlers -function module:OnInitialize() - self.db = ReAction.db:RegisterNamespace( moduleID, - { - profile = { - bars = { }, - } - } - ) - self.handles = setmetatable({ }, weak) - - ReAction:RegisterBarOptionGenerator(self, "GetBarOptions") - - ReAction.RegisterCallback(self, "OnCreateBar") - ReAction.RegisterCallback(self, "OnRefreshBar") - ReAction.RegisterCallback(self, "OnDestroyBar") - ReAction.RegisterCallback(self, "OnEraseBar") - ReAction.RegisterCallback(self, "OnRenameBar") -end - -function module:OnEnable() - ReAction:RegisterBarType(L["Action Bar"], - { - type = moduleID, - defaultButtonSize = 36, - defaultBarRows = 1, - defaultBarCols = 12, - defaultBarSpacing = 3 - }, true) - ReAction:GetModule("State"):RegisterStateProperty("page", nil, PropHandler.GetOptions(), PropHandler) -end - -function module:OnDisable() - ReAction:UnregisterBarType(L["Action Bar"]) - ReAction:GetModule("State"):UnregisterStateProperty("page") -end - -function module:OnCreateBar(event, bar, name) - if bar.config.type == moduleID then - local profile = self.db.profile - if profile.bars[name] == nil then - profile.bars[name] = { - buttons = { } - } - end - if self.handles[bar] == nil then - self.handles[bar] = Handle:New(bar, profile.bars[name]) - end - end -end - -function module:OnRefreshBar(event, bar, name) - if self.handles[bar] then - self.handles[bar]:Refresh() - end -end - -function module:OnDestroyBar(event, bar, name) - if self.handles[bar] then - self.handles[bar]:Destroy() - self.handles[bar] = nil - end -end - -function module:OnEraseBar(event, bar, name) - self.db.profile.bars[name] = nil -end - -function module:OnRenameBar(event, bar, oldname, newname) - b = self.db.profile.bars - b[newname], b[oldname] = b[oldname], nil -end - ----- Interface ---- -function module:GetBarOptions(bar) - local h = self.handles[bar] - if h then - return h:GetOptions() - end -end - - ----- Bar Handle ---- - -do - local options = { - hideEmpty = { - name = L["Hide Empty Buttons"], - order = 1, - type = "toggle", - width = "double", - get = "GetHideEmpty", - set = "SetHideEmpty", - }, - lockButtons = { - name = L["Lock Buttons"], - desc = L["Prevents picking up/dragging actions (use SHIFT to override this behavior)"], - order = 2, - type = "toggle", - get = "GetLockButtons", - set = "SetLockButtons", - }, - lockOnlyCombat = { - name = L["Only in Combat"], - desc = L["Only lock the buttons when in combat"], - order = 3, - type = "toggle", - disabled = "LockButtonsCombatDisabled", - get = "GetLockButtonsCombat", - set = "SetLockButtonsCombat", - }, - pages = { - name = L["# Pages"], - desc = L["Use the Dynamic State tab to specify page transitions"], - order = 4, - type = "range", - min = 1, - max = 10, - step = 1, - get = "GetNumPages", - set = "SetNumPages", - }, - mindcontrol = { - name = L["Mind Control Support"], - desc = L["When possessing a target (e.g. via Mind Control), map the first 12 buttons of this bar to the possessed target's actions."], - order = 5, - type = "toggle", - width = "double", - set = "SetMindControl", - get = "GetMindControl", - }, - vehicle = { - name = L["Vehicle Support"], - desc = L["When on a vehicle, map the first 6 buttons of this bar to the vehicle actions. The vehicle-exit button is mapped to the 7th button. Pitch controls are not supported."], - order = 6, - type = "toggle", - width = "double", - get = "GetVehicle", - set = "SetVehicle", - }, - actions = { - name = L["Edit Action IDs"], - order = 7, - type = "group", - inline = true, - args = { - method = { - name = L["Assign"], - order = 1, - type = "select", - width = "double", - values = { [0] = L["Choose Method..."], - [1] = L["Individually"], - [2] = L["All at Once"], }, - get = "GetActionEditMethod", - set = "SetActionEditMethod", - }, - rowSelect = { - name = L["Row"], - desc = L["Rows are numbered top to bottom"], - order = 2, - type = "select", - width = "half", - hidden = "IsButtonSelectHidden", - values = "GetRowList", - get = "GetSelectedRow", - set = "SetSelectedRow", - }, - colSelect = { - name = L["Col"], - desc = L["Columns are numbered left to right"], - order = 3, - type = "select", - width = "half", - hidden = "IsButtonSelectHidden", - values = "GetColumnList", - get = "GetSelectedColumn", - set = "SetSelectedColumn", - }, - pageSelect = { - name = L["Page"], - order = 4, - type = "select", - width = "half", - hidden = "IsPageSelectHidden", - values = "GetPageList", - get = "GetSelectedPage", - set = "SetSelectedPage", - }, - single = { - name = L["Action ID"], - usage = L["Specify ID 1-120"], - order = 5, - type = "input", - width = "half", - hidden = "IsButtonSelectHidden", - get = "GetActionID", - set = "SetActionID", - validate = "ValidateActionID", - }, - multi = { - name = L["ID List"], - usage = L["Specify a comma-separated list of IDs for each button in the bar (in order). Separate multiple pages with semicolons (;)"], - order = 6, - type = "input", - multiline = true, - width = "double", - hidden = "IsMultiIDHidden", - get = "GetMultiID", - set = "SetMultiID", - validate = "ValidateMultiID", - }, - }, - }, - } - - local meta = { __index = Handle } - - function Handle:New( bar, config ) - local self = setmetatable( - { - bar = bar, - config = config, - btns = { } - }, - meta) - - if self.config.buttons == nil then - self.config.buttons = { } - end - self:Refresh() - return self - end - - function Handle:Refresh() - local r, c = self.bar:GetButtonGrid() - local n = r*c - local btnCfg = self.config.buttons - if n ~= #self.btns then - for i = 1, n do - if btnCfg[i] == nil then - btnCfg[i] = {} - end - if self.btns[i] == nil then - local lastButton = self:GetLastButton() - local hint = lastButton and lastButton.config.actionID - local b = Button:New(i, self.config, self.bar, hint) - self.btns[i] = b - self.bar:AddButton(i,b) - end - end - for i = n+1, #self.btns do - if self.btns[i] then - self.bar:RemoveButton(self.btns[i]) - self.btns[i]:Destroy() - self.btns[i] = nil - btnCfg[i] = nil - end - end - end - for _, b in ipairs(self.btns) do - b:Refresh() - end - Button.SetupBarHeader(self.bar,self.config) - self:UpdateButtonLock() - end - - function Handle:Destroy() - for _,b in pairs(self.btns) do - if b then - b:Destroy() - end - end - end - - function Handle:UpdateButtonLock() - Button.SetButtonLock(self.bar, self.config.lockButtons, self.config.lockButtonsCombat) - end - - function Handle:GetLastButton() - return self.btns[#self.btns] - end - - -- options handlers - function Handle:GetOptions() - return { - type = "group", - name = L["Action Buttons"], - handler = self, - args = options - } - end - - function Handle:SetHideEmpty(info, value) - if value ~= self.config.hideEmpty then - self.config.hideEmpty = value - for _, b in pairs(self.btns) do - b:ShowGrid(not value) - end - end - end - - function Handle:GetHideEmpty() - return self.config.hideEmpty - end - - function Handle:GetLockButtons() - return self.config.lockButtons - end - - function Handle:SetLockButtons(info, value) - self.config.lockButtons = value - self:UpdateButtonLock() - end - - function Handle:GetLockButtonsCombat() - return self.config.lockButtonsCombat - end - - function Handle:SetLockButtonsCombat(info, value) - self.config.lockButtonsCombat = value - self:UpdateButtonLock() - end - - function Handle:LockButtonsCombatDisabled() - return not self.config.lockButtons - end - - function Handle:GetNumPages() - return self.config.nPages - end - - function Handle:SetNumPages(info, value) - self.config.nPages = value - self:Refresh() - end - - function Handle:GetMindControl() - return self.config.mindcontrol - end - - function Handle:SetMindControl(info, value) - self.config.mindcontrol = value - self:Refresh() - end - - function Handle:GetVehicle() - return self.config.vehicle - end - - function Handle:SetVehicle(info, value) - self.config.vehicle = value - self:Refresh() - end - - function Handle:GetActionEditMethod() - return self.editMethod or 0 - end - - function Handle:SetActionEditMethod(info, value) - self.editMethod = value - end - - function Handle:IsButtonSelectHidden() - return self.editMethod ~= 1 - end - - function Handle:GetRowList() - local r,c = self.bar:GetButtonGrid() - if self.rowList == nil or #self.rowList ~= r then - local list = { } - for i = 1, r do - table.insert(list,i) - end - self.rowList = list - end - return self.rowList - end - - function Handle:GetSelectedRow() - local r, c = self.bar:GetButtonGrid() - local row = self.selectedRow or 1 - if row > r then - row = 1 - end - self.selectedRow = row - return row - end - - function Handle:SetSelectedRow(info, value) - self.selectedRow = value - end - - function Handle:GetColumnList() - local r,c = self.bar:GetButtonGrid() - if self.columnList == nil or #self.columnList ~= c then - local list = { } - for i = 1, c do - table.insert(list,i) - end - self.columnList = list - end - return self.columnList - end - - function Handle:GetSelectedColumn() - local r, c = self.bar:GetButtonGrid() - local col = self.selectedColumn or 1 - if col > c then - col = 1 - end - self.selectedColumn = col - return col - end - - function Handle:SetSelectedColumn(info, value) - self.selectedColumn = value - end - - function Handle:IsPageSelectHidden() - return self.editMethod ~= 1 or (self.config.nPages or 1) < 2 - end - - function Handle:GetPageList() - local n = self.config.nPages or 1 - if self.pageList == nil or #self.pageList ~= n then - local p = { } - for i = 1, n do - table.insert(p,i) - end - self.pageList = p - end - return self.pageList - end - - function Handle:GetSelectedPage() - local p = self.selectedPage or 1 - if p > (self.config.nPages or 1) then - p = 1 - end - self.selectedPage = p - return p - end - - function Handle:SetSelectedPage(info, value) - self.selectedPage = value - end - - function Handle:GetActionID() - local row = self.selectedRow or 1 - local col = self.selectedColumn or 1 - local r, c = self.bar:GetButtonGrid() - local n = (row-1) * c + col - local btn = self.btns[n] - if btn then - return tostring(btn:GetActionID(self.selectedPage or 1)) - end - end - - function Handle:SetActionID(info, value) - local row = self.selectedRow or 1 - local col = self.selectedColumn or 1 - local r, c = self.bar:GetButtonGrid() - local n = (row-1) * c + col - local btn = self.btns[n] - if btn then - btn:SetActionID(tonumber(value), self.selectedPage or 1) - end - end - - function Handle:ValidateActionID(info, value) - value = tonumber(value) - if value == nil or value < 1 or value > 120 then - return L["Specify ID 1-120"] - end - return true - end - - function Handle:IsMultiIDHidden() - return self.editMethod ~= 2 - end - - function Handle:GetMultiID() - local p = { } - for i = 1, self.config.nPages or 1 do - local b = { } - for _, btn in ipairs(self.btns) do - table.insert(b, btn:GetActionID(i)) - end - table.insert(p, table.concat(b,",")) - end - return table.concat(p,";\n") - end - - - local function ParseMultiID(nBtns, nPages, s) - if s:match("[^%d%s,;]") then - return nil - end - local p = { } - for list in s:gmatch("[^;]+") do - local pattern = ("^%s?$"):format(("%s*(%d+)%s*,"):rep(nBtns)) - local ids = { list:match(pattern) } - if #ids ~= nBtns then - return nil - end - table.insert(p,ids) - end - if #p ~= nPages then - return nil - end - return p - end - - function Handle:SetMultiID(info, value) - local p = ParseMultiID(#self.btns, self.config.nPages or 1, value) - for page, b in ipairs(p) do - for button, id in ipairs(b) do - self.btns[button]:SetActionID(id, page) - end - end - end - - function Handle:ValidateMultiID(info, value) - local bad = L["Invalid action ID list string"] - if value == nil or ParseMultiID(#self.btns, self.config.nPages or 1, value) == nil then - return bad - end - return true - end -end - - ------- State property options ------ -do - local pageOptions = { - page = { - name = L["Show Page #"], - order = 11, - type = "select", - width = "half", - disabled = "IsPageDisabled", - hidden = "IsPageHidden", - values = "GetPageValues", - set = "SetProp", - get = "GetPage", - }, - } - - local function GetBarConfig(bar) - return module.db.profile.bars[bar:GetName()] - end - - function PropHandler.GetOptions() - return pageOptions - end - - function PropHandler:IsPageDisabled() - local c = GetBarConfig(self.bar) - local n = c and c.nPages or 1 - return not (n > 1) - end - - function PropHandler:IsPageHidden() - return not GetBarConfig(self.bar) - end - - function PropHandler:GetPageValues() - if not self._pagevalues then - self._pagevalues = { } - end - local c = GetBarConfig(self.bar) - if c then - local n = c.nPages - -- cache the results - if self._npages ~= n then - self._npages = n - wipe(self._pagevalues) - for i = 1, n do - self._pagevalues["page"..i] = i - end - end - end - return self._pagevalues - end - - function PropHandler:GetPage(info) - return self:GetProp(info) or 1 - end - -end -
--- a/modules/Bag.lua Thu Nov 18 13:11:08 2010 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,161 +0,0 @@ ---[[ - ReAction Bag button module - ---]] - --- local imports -local addonName, addonTable = ... -local ReAction = addonTable.ReAction -local L = ReAction.L -local _G = _G - --- Bag button -local Button = ReAction.Button.Bag - --- module declaration -local moduleID = "Bag" -local module = ReAction:NewModule( moduleID - -- mixins go here -) - --- handlers -function module:OnInitialize() - self.db = ReAction.db:RegisterNamespace( moduleID, - { - profile = { - buttons = { } - } - } - ) - - self.buttons = { } - - ReAction.RegisterCallback(self, "OnCreateBar", "OnRefreshBar") - ReAction.RegisterCallback(self, "OnDestroyBar") - ReAction.RegisterCallback(self, "OnRefreshBar") - ReAction.RegisterCallback(self, "OnEraseBar") - ReAction.RegisterCallback(self, "OnRenameBar") -end - -function module:OnEnable() - ReAction:RegisterBarType(L["Bag Bar"], - { - type = moduleID , - defaultButtonSize = 30, - defaultBarRows = 1, - defaultBarCols = 6, - defaultBarSpacing = 4 - }) -end - -function module:OnDisable() - ReAction:UnregisterBarType(L["Bag Bar"]) -end - -function module:OnDestroyBar(event, bar, name) - local btns = self.buttons[bar] - if btns then - for _,b in pairs(btns) do - if b then - b:Destroy() - end - end - self.buttons[bar] = nil - end -end - -function module:OnRefreshBar(event, bar, name) - if bar.config.type == moduleID then - local btns = self.buttons[bar] - if btns == nil then - btns = { } - self.buttons[bar] = btns - end - local profile = self.db.profile - if profile.buttons[name] == nil then - profile.buttons[name] = {} - end - local btnCfg = profile.buttons[name] - - local r, c = bar:GetButtonGrid() - local n = r*c - for i = 1, n do - if btnCfg[i] == nil then - btnCfg[i] = {} - end - if btns[i] == nil then - local success, r = pcall(Button.New,Button,i,profile,bar,i>1 and btnCfg[i-1].bagID) - if success and r then - btns[i] = r - bar:AddButton(i,r) - else - n = i - 1 - bar:ClipNButtons(n) - break - end - end - btns[i]:Refresh() - end - for i = n+1, #btns do - if btns[i] then - bar:RemoveButton(btns[i]) - btns[i] = btns[i]:Destroy() - if btnCfg[i] then - btnCfg[i] = nil - end - end - end - end - -end - -function module:OnEraseBar(event, bar, name) - self.db.profile.buttons[name] = nil -end - -function module:OnRenameBar(event, bar, oldName, newName) - local b = self.db.profile.buttons - b[newname], b[oldname] = b[oldname], nil -end - - --- hook some functions to propagate to our bag buttons -hooksecurefunc("Disable_BagButtons", - function() - for _, buttons in pairs(module.buttons) do - for _, b in pairs(buttons) do - local f = b:GetFrame() - f:Disable() - SetDesaturation(b.frames.icon,1) - end - end - end) - -hooksecurefunc("Enable_BagButtons", - function() - for _, buttons in pairs(module.buttons) do - for _, b in pairs(buttons) do - local f = b:GetFrame() - f:Enable() - SetDesaturation(b.frames.icon,nil) - end - end - end) - -hooksecurefunc("ContainerFrame_OnHide", - function() - for _, buttons in pairs(module.buttons) do - for _, b in pairs(buttons) do - b:Update() - end - end - end) - -hooksecurefunc("ContainerFrame_OnShow", - function() - for _, buttons in pairs(module.buttons) do - for _, b in pairs(buttons) do - b:Update() - end - end - end)
--- a/modules/HideBlizzard.lua Thu Nov 18 13:11:08 2010 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,115 +0,0 @@ ---[[ - ReAction 'Hide Blizzard' module - - Hides Blizzard action bars. This hides the extra action bars, stance bar, pet bar, and - main menu bar, which in turn hides the experience bar, bag bar, micro menu bar, and lag meter. - ---]] - --- local imports -local addonName, addonTable = ... -local ReAction = addonTable.ReAction -local L = ReAction.L - --- module declaration -local moduleID = "HideBlizzard" -local module = ReAction:NewModule( moduleID ) - - --- module methods -function module:OnInitialize() - self.db = ReAction.db:RegisterNamespace( moduleID, - { - profile = { - hide = false - } - } - ) - self.db.RegisterCallback(self,"OnProfileChanged") - self.db.RegisterCallback(self,"OnProfileCopied", "OnProfileChanged") - self.db.RegisterCallback(self,"OnProfileReset", "OnProfileChanged") - - self.hiddenFrame = CreateFrame("Frame") - self.hiddenFrame:Hide() - - -- It's fairly normal to use the Blizzard vehicle bar, and to have - -- your regular buttons in the same location. If you do this, and don't - -- bother to hide your buttons, they'll obscure some parts of the vehicle bar. - VehicleMenuBar:SetFrameLevel(VehicleMenuBar:GetFrameLevel()+3) - -end - -function module:OnEnable() - self:UpdateFrames() -end - -function module:OnDisable() - self:UpdateFrames(true) -end - -function module:OnProfileChanged() - self:UpdateFrames() -end - -local frames = { - [ MainMenuBar ] = { }, - [ MultiBarLeft ] = { }, - [ MultiBarRight ] = { }, - [ MultiBarBottomLeft ] = { }, - [ MultiBarBottomRight ] = { }, - [ VehicleMenuBar ] = { field = "hideVehicle" }, -} - -function module:UpdateFrames( show ) - show = show or not self.db.profile.hide - for frame, info in pairs(frames) do - local show = show -- make a local copy for this frame - if info.field then - show = show or not self.db.profile[info.field] - end - - if show then - if info.parent then - frame:SetParent(info.parent) - end - if frame:IsShown() then - frame:Show() -- refresh - end - else - if not info.parent then - info.parent = frame:GetParent() - end - frame:SetParent(self.hiddenFrame) - end - end -end - -function module:IsHidden(info) - if info then - return self.db.profile.hide and self.db.profile[info[#info]] - else - return self.db.profile.hide - end -end - -function module:SetHidden(info,value) - self.db.profile[info[#info]] = value - self:UpdateFrames() -end - -function module:OptionDisabled(info) - local disabled = InCombatLockdown() - if not disabled and info[#info] ~= "hide" then - disabled = not self.db.profile.hide - end - return disabled -end - - --- reroute blizzard action bar config to ReAction config window -InterfaceOptionsActionBarsPanel:HookScript("OnShow", - function() - if module:IsEnabled() and module:IsHidden() then - ReAction:ShowOptions() - end - end )
--- a/modules/LBF.lua Thu Nov 18 13:11:08 2010 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,115 +0,0 @@ --- local imports -local addonName, addonTable = ... -local ReAction = addonTable.ReAction -local L = ReAction.L -local _G = _G - --- module declaration -local moduleID = "ButtonFacade" -local module = ReAction:NewModule( moduleID ) - --- handlers -function module:OnInitialize() - self.db = ReAction.db:RegisterNamespace( moduleID, - { - profile = { - -- default profile goes here - } - } - ) - - self.groups = { } - - self.LBF = LibStub("LibButtonFacade",true) - - if not self.LBF then -- no more initialization - return - end - - -- override a method of ReAction.Bar - -- note that 'self' in this context refers to the bar - function ReAction.Bar:SkinButton( button, data ) - module:GetGroup(self:GetName()):AddButton(button:GetFrame(), data) - end - - -- register some common events - ReAction.RegisterCallback(self, "OnCreateBar") - ReAction.RegisterCallback(self, "OnDestroyBar") - ReAction.RegisterCallback(self, "OnRefreshBar") - ReAction.RegisterCallback(self, "OnEraseBar") - ReAction.RegisterCallback(self, "OnRenameBar") - - self.LBF:RegisterSkinCallback("ReAction", self.OnSkinChanged, self) -end - -function module:OnEnable() - -end - -function module:OnDisable() - -end - -function module:OnCreateBar(event, bar, name) - local c = self.db.profile[name] - if not c then - c = { - skinID = "Blizzard", - backdrop = true, - gloss = 0, - colors = {}, - } - self.db.profile[name] = c - end - - local g = self:GetGroup(name) - g.SkinID = c.skinID or "Blizzard" - g.Backdrop = c.backdrop - g.Gloss = c.gloss - g.Colors = c.colors -end - -function module:OnDestroyBar(event, bar, name) - if self.groups[name] then - self.groups[name]:Delete() - self.groups[name] = nil - end -end - -function module:OnRefreshBar(event, bar, name) - local c = self.db.profile[name] - local g = self.groups[name] - if c and g then - g:Skin(c.skinID, c.gloss, c.backdrop, c.colors) - end -end - -function module:OnEraseBar(event, bar, name) - self:OnDestroyBar(event, bar, name) - self.db.profile[name] = nil -end - -function module:OnRenameBar(event, bar, oldName, newName) - if self.groups[name] then - self.groups[name]:Delete(true) - self.db.profile[oldName], self.db.profile[newName] = nil, self.db.profile[oldName] - self:OnCreateBar(event, bar, newName) - end -end - -function module:OnSkinChanged( skinID, gloss, backdrop, group, button, colors ) - local c = self.db.profile[group] - if c then - c.skinID = skinID - c.gloss = gloss - c.backdrop = backdrop - c.colors = colors - end -end - -function module:GetGroup( name ) - if not self.groups[name] then - self.groups[name] = self.LBF:Group("ReAction", name) - end - return self.groups[name] -end
--- a/modules/ModuleTemplate.lua Thu Nov 18 13:11:08 2010 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,69 +0,0 @@ ---[[ - ReAction module template - ---]] - --- local imports -local addonName, addonTable = ... -local ReAction = addonTable.ReAction -local L = ReAction.L -local _G = _G - --- module declaration -local moduleID = "MyModuleName" -local module = ReAction:NewModule( moduleID - -- mixins go here -) - --- handlers -function module:OnInitialize() - self.db = ReAction.db:RegisterNamespace( moduleID, - { - profile = { - -- default profile goes here - } - } - ) - - -- register some common events - ReAction.RegisterCallback(self, "OnCreateBar") - ReAction.RegisterCallback(self, "OnDestroyBar") - ReAction.RegisterCallback(self, "OnRefreshBar") - ReAction.RegisterCallback(self, "OnEraseBar") - ReAction.RegisterCallback(self, "OnRenameBar") -end - -function module:OnEnable() - -end - -function module:OnDisable() - -end - --- apply module features and settings to a bar object (see Bar.lua for Bar API) -function module:OnCreateBar(event, bar, name) - -end - --- remove module features and settings from a bar object -function module:OnDestroyBar(event, bar, name) - -end - --- refresh module features and settings on a bar object -function module:OnRefreshBar(event, bar, name) - -end - --- erase any local configuration entries for the supplied bar name -function module:OnEraseBar(event, bar, name) - -end - --- update any local configuration/option entries with the new bar name index -function module:OnRenameBar(event, bar, oldName, newName) - -end - -
--- a/modules/PetAction.lua Thu Nov 18 13:11:08 2010 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,209 +0,0 @@ ---[[ - ReAction Pet Action button module - - The button module implements standard action button functionality by wrapping Blizzard's - PetActionButton frame and associated functions. - ---]] - --- local imports -local addonName, addonTable = ... -local ReAction = addonTable.ReAction -local L = ReAction.L -local _G = _G -local CreateFrame = CreateFrame -local format = string.format - --- module declaration -local moduleID = "PetAction" -local module = ReAction:NewModule( moduleID ) - --- Button class -local Button = ReAction.Button.PetAction - --- private -local function UpdateButtonLock(bar) - local f = bar:GetFrame() - f:SetAttribute("lockbuttons",bar.config.lockButtons) - f:SetAttribute("lockbuttonscombat",bar.config.lockButtonsCombat) - f:Execute( - [[ - lockButtons = self:GetAttribute("lockbuttons") - lockButtonsCombat = self:GetAttribute("lockbuttonscombat") - ]]) -end - --- module methods -function module:OnInitialize() - self.db = ReAction.db:RegisterNamespace( moduleID, - { - profile = { - buttons = { } - } - } - ) - self.buttons = { } - - ReAction:RegisterBarOptionGenerator(self, "GetBarOptions") - - ReAction.RegisterCallback(self, "OnCreateBar") - ReAction.RegisterCallback(self, "OnDestroyBar") - ReAction.RegisterCallback(self, "OnRefreshBar") - ReAction.RegisterCallback(self, "OnEraseBar") - ReAction.RegisterCallback(self, "OnRenameBar") -end - -function module:OnEnable() - ReAction:RegisterBarType(L["Pet Action Bar"], - { - type = moduleID , - defaultButtonSize = 30, - defaultBarRows = 1, - defaultBarCols = 10, - defaultBarSpacing = 8 - }) -end - -function module:OnDisable() - ReAction:UnregisterBarType(L["Pet Action Bar"]) -end - -function module:OnCreateBar(event, bar, name) - if bar.config.type == moduleID then - -- auto show/hide when pet exists - bar:RegisterUnitWatch("pet",true) - self:OnRefreshBar(event, bar, name) - end -end - -function module:OnRefreshBar(event, bar, name) - if bar.config.type == moduleID then - if self.buttons[bar] == nil then - self.buttons[bar] = { } - end - local btns = self.buttons[bar] - local profile = self.db.profile - if profile.buttons[name] == nil then - profile.buttons[name] = {} - end - local btnCfg = profile.buttons[name] - - local r, c = bar:GetButtonGrid() - local n = r*c - for i = 1, n do - if btnCfg[i] == nil then - btnCfg[i] = {} - end - if btns[i] == nil then - local success, r = pcall(Button.New,Button,i,btnCfg[i],bar,i>1 and btnCfg[i-1].actionID) - if success and r then - btns[i] = r - bar:AddButton(i,r) - else - n = i - 1 - bar:ClipNButtons(n) - break - end - end - btns[i]:Refresh() - end - for i = n+1, #btns do - if btns[i] then - bar:RemoveButton(btns[i]) - btns[i] = btns[i]:Destroy() - if btnCfg[i] then - btnCfg[i] = nil - end - end - end - UpdateButtonLock(bar) - end -end - -function module:OnDestroyBar(event, bar, name) - if self.buttons[bar] then - local btns = self.buttons[bar] - for _,b in pairs(btns) do - if b then - b:Destroy() - end - end - self.buttons[bar] = nil - end -end - -function module:OnEraseBar(event, bar, name) - self.db.profile.buttons[name] = nil -end - -function module:OnRenameBar(event, bar, oldname, newname) - local b = self.db.profile.buttons - b[newname], b[oldname] = b[oldname], nil -end - - ----- Options ---- -local Handler = { } -local meta = { __index = Handler } - -function Handler:New(bar) - return setmetatable( - { - bar = bar, - config = bar.config - }, meta) -end - -function Handler:GetLockButtons() - return self.config.lockButtons -end - -function Handler:SetLockButtons(info, value) - self.config.lockButtons = value - UpdateButtonLock(self.bar) -end - -function Handler:GetLockButtonsCombat() - return self.config.lockButtonsCombat -end - -function Handler:SetLockButtonsCombat(info, value) - self.config.lockButtonsCombat = value - UpdateButtonLock(self.bar) -end - -function Handler:LockButtonsCombatDisabled() - return not self.config.lockButtons -end - - -function module:GetBarOptions(bar) - if bar.config.type == moduleID then - return { - type = "group", - name = L["Pet Buttons"], - handler = Handler:New(bar), - args = { - lockButtons = { - name = L["Lock Buttons"], - desc = L["Prevents picking up/dragging actions (use SHIFT to override this behavior)"], - order = 2, - type = "toggle", - get = "GetLockButtons", - set = "SetLockButtons", - }, - lockOnlyCombat = { - name = L["Only in Combat"], - desc = L["Only lock the buttons when in combat"], - order = 3, - type = "toggle", - disabled = "LockButtonsCombatDisabled", - get = "GetLockButtonsCombat", - set = "SetLockButtonsCombat", - }, - } - } - end -end - -
--- a/modules/Stance.lua Thu Nov 18 13:11:08 2010 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,127 +0,0 @@ ---[[ - ReAction Stance button module - ---]] - --- local imports -local addonName, addonTable = ... -local ReAction = addonTable.ReAction -local L = ReAction.L -local _G = _G - --- Stance button -local Button = ReAction.Button.Stance - --- module declaration -local moduleID = "Stance" -local module = ReAction:NewModule( moduleID - -- mixins go here -) - --- handlers -function module:OnInitialize() - self.db = ReAction.db:RegisterNamespace( moduleID, - { - profile = { - buttons = { } - } - } - ) - - self.buttons = { } - - ReAction.RegisterCallback(self, "OnCreateBar", "OnRefreshBar") - ReAction.RegisterCallback(self, "OnDestroyBar") - ReAction.RegisterCallback(self, "OnRefreshBar") - ReAction.RegisterCallback(self, "OnEraseBar") - ReAction.RegisterCallback(self, "OnRenameBar") -end - -function module:OnEnable() - ReAction:RegisterBarType(L["Stance Bar"], - { - type = moduleID , - defaultButtonSize = 36, - defaultBarRows = 1, - defaultBarCols = 6, - defaultBarSpacing = 3 - }) - -end - -function module:OnDisable() - ReAction:UnregisterBarType(L["Stance Bar"]) -end - -function module:OnDestroyBar(event, bar, name) - local btns = self.buttons[bar] - if btns then - for _,b in pairs(btns) do - if b then - b:Destroy() - end - end - self.buttons[bar] = nil - end -end - -function module:OnRefreshBar(event, bar, name) - if bar.config.type == moduleID then - local btns = self.buttons[bar] - if btns == nil then - btns = { } - self.buttons[bar] = btns - end - local profile = self.db.profile - if profile.buttons[name] == nil then - profile.buttons[name] = {} - end - local btnCfg = profile.buttons[name] - - local r, c = bar:GetButtonGrid() - local n = r*c - for i = 1, n do - if btnCfg[i] == nil then - btnCfg[i] = {} - end - if btns[i] == nil then - local success, r = pcall(Button.New,Button,i,profile,bar,i>1 and btnCfg[i-1].stanceID) - if success and r then - btns[i] = r - bar:AddButton(i,r) - else - n = i - 1 - bar:ClipNButtons(n) - break - end - end - btns[i]:Refresh() - end - for i = n+1, #btns do - if btns[i] then - bar:RemoveButton(btns[i]) - btns[i] = btns[i]:Destroy() - if btnCfg[i] then - btnCfg[i] = nil - end - end - end - end - -end - -function module:OnEraseBar(event, bar, name) - self.db.profile.buttons[name] = nil -end - -function module:OnRenameBar(event, bar, oldName, newName) - local b = self.db.profile.buttons - b[newname], b[oldname] = b[oldname], nil -end - -function module:RefreshAll() - for bar in pairs(self.buttons) do - self:OnRefreshBar(nil,bar,bar:GetName()) - end -end -
--- a/modules/State.lua Thu Nov 18 13:11:08 2010 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,915 +0,0 @@ ---[[ - ReAction bar state driver interface - ---]] - --- local imports -local addonName, addonTable = ... -local ReAction = addonTable.ReAction -local L = ReAction.L -local _G = _G -local format = string.format -local InCombatLockdown = InCombatLockdown -local RegisterStateDriver = RegisterStateDriver - --- module declaration -local moduleID = "State" -local module = ReAction:NewModule( moduleID, "AceEvent-3.0" ) - --- Utility -- - --- traverse a table tree by key list and fetch the result or first nil -local function tfetch(t, ...) - for i = 1, select('#', ...) do - t = t and t[select(i, ...)] - end - return t -end - --- traverse a table tree by key list and build tree as necessary -local function tbuild(t, ...) - for i = 1, select('#', ...) do - local key = select(i, ...) - if not t[key] then t[key] = { } end - t = t[key] - end - return t -end - --- return a new array of keys of table 't', sorted by comparing --- sub-fields (obtained via tfetch) of the table values -local function fieldsort( t, ... ) - local r = { } - for k in pairs(t) do - table.insert(r,k) - end - local path = { ... } - table.sort(r, function(lhs, rhs) - local olhs = tfetch(t[lhs], unpack(path)) or 0 - local orhs = tfetch(t[rhs], unpack(path)) or 0 - return olhs < orhs - end) - return r -end - - -local ApplyStates, CleanupStates, SetProperty, GetProperty, RegisterProperty - --- PRIVATE -- -do - function GetProperty( bar, state, propname ) - return tfetch(module.db.profile.bars, bar:GetName(), "states", state, propname) - end - - function SetProperty( bar, state, propname, value ) - local s = tbuild(module.db.profile.bars, bar:GetName(), "states", state) - s[propname] = value - bar:SetSecureStateData(state, propname, value) - end - - function RegisterProperty( propname, snippet ) - for _, bar in ReAction:IterateBars() do - if type(snippet) == "string" then - bar:SetSecureStateExtension(propname,snippet) - end - ApplyStates(bar) - end - end - - function UnregisterProperty( propname ) - for _, bar in ReAction:IterateBars() do - bar:SetSecureStateExtension(propname,nil) - ApplyStates(bar) - end - end - - function ApplyStates( bar ) - local states = tfetch(module.db.profile.bars, bar:GetName(), "states") - if states then - bar:SetStateDriver(states) - end - end - - function CleanupStates( bar ) - bar:SetStateDriver(nil) - end -end - - - --- module event handlers -- - -function module:OnInitialize() - self.db = ReAction.db:RegisterNamespace( moduleID, - { - profile = { - bars = { }, - } - } - ) - - self:RegisterEvent("UPDATE_SHAPESHIFT_FORMS") - - ReAction:RegisterBarOptionGenerator(self, "GetBarOptions") - - ReAction.RegisterCallback(self, "OnCreateBar","OnRefreshBar") - ReAction.RegisterCallback(self, "OnDestroyBar") - ReAction.RegisterCallback(self, "OnRefreshBar") - ReAction.RegisterCallback(self, "OnEraseBar") - ReAction.RegisterCallback(self, "OnRenameBar") -end - -function module:OnEnable() - self:UPDATE_SHAPESHIFT_FORMS() -- it doesn't fire on a /reloadui -end - -function module:UPDATE_SHAPESHIFT_FORMS() - -- Re-parse the rules table according to the new form list. - -- This happens both at initial login (after PLAYER_ENTERING_WORLD) - -- as well as when gaining new abilities. - ReAction.Bar.InitRuleFormats() - for _, bar in ReAction:IterateBars() do - ApplyStates(bar) - end -end - -function module:OnRefreshBar(event, bar, name) - local c = self.db.profile.bars[name] - if c then - ApplyStates(bar) - end -end - -function module:OnDestroyBar(event, bar, name) - CleanupStates(bar) -end - -function module:OnEraseBar(event, bar, name) - self.db.profile.bars[name] = nil -end - -function module:OnRenameBar(event, bar, oldname, newname) - local bars = self.db.profile.bars - bars[newname], bars[oldname] = bars[oldname], nil -end - - - --- Options -- - -local CreateBarOptions, RegisterPropertyOptions -do - -- pre-sorted by the order they should appear in - local rules = { - -- rule fields - { "stance", { {battle = L["Battle Stance"]}, {defensive = L["Defensive Stance"]}, {berserker = L["Berserker Stance"]} } }, - { "form", { {caster = L["Caster Form"]}, {bear = L["Bear Form"]}, {cat = L["Cat Form"]}, {tree = L["Tree of Life"]}, {moonkin = L["Moonkin Form"]} } }, - { "stealth", { {stealth = L["Stealth"]}, {nostealth = L["No Stealth"]}, {shadowdance = L["Shadow Dance"]} } }, - { "shadow", { {shadowform = L["Shadowform"]}, {noshadowform = L["No Shadowform"]} } }, - { "demon", { {demon = L["Demon Form"]}, {nodemon = L["No Demon Form"]} } }, - { "pet", { {pet = L["With Pet"]}, {nopet = L["Without Pet"]} } }, - { "target", { {harm = L["Hostile Target"]}, {help = L["Friendly Target"]}, {notarget = L["No Target"]} } }, - { "focus", { {focusharm = L["Hostile Focus"]}, {focushelp = L["Friendly Focus"]}, {nofocus = L["No Focus"]} } }, - { "possess", { {possess = L["Mind Control"]} } }, - { "vehicle", { {vehicle = L["In a Vehicle"]} } }, - { "group", { {raid = L["Raid"]}, {party = L["Party"]}, {solo = L["Solo"]} } }, - { "combat", { {combat = L["In Combat"]}, {nocombat = L["Out of Combat"]} } }, - } - - local ruleSelect = { } - local ruleMap = { } - local optionMap = setmetatable({},{__mode="k"}) - - local pointTable = { - NONE = " ", - CENTER = L["Center"], - LEFT = L["Left"], - RIGHT = L["Right"], - TOP = L["Top"], - BOTTOM = L["Bottom"], - TOPLEFT = L["Top Left"], - TOPRIGHT = L["Top Right"], - BOTTOMLEFT = L["Bottom Left"], - BOTTOMRIGHT = L["Bottom Right"], - } - - -- unpack rules table into ruleSelect and ruleMap - for _, c in ipairs(rules) do - local rule, fields = unpack(c) - for _, field in ipairs(fields) do - local key, label = next(field) - table.insert(ruleSelect, label) - table.insert(ruleMap, key) - end - end - - local stateOptions = { - ordering = { - name = L["Info"], - order = 1, - type = "group", - args = { - delete = { - name = L["Delete this State"], - order = -1, - type = "execute", - func = "DeleteState", - }, - rename = { - name = L["Name"], - order = 1, - type = "input", - get = "GetName", - set = "SetStateName", - pattern = "^%w*$", - usage = L["State names must be alphanumeric without spaces"], - }, - ordering = { - name = L["Evaluation Order"], - desc = L["State transitions are evaluated in the order listed:\nMove a state up or down to change the order"], - order = 2, - type = "group", - inline = true, - args = { - up = { - name = L["Up"], - order = 1, - type = "execute", - width = "half", - func = "MoveStateUp", - }, - down = { - name = L["Down"], - order = 2, - type = "execute", - width = "half", - func = "MoveStateDown", - } - } - } - } - }, - properties = { - name = L["Properties"], - order = 2, - type = "group", - args = { - desc = { - name = L["Set the properties for the bar when in this state"], - order = 1, - type = "description" - }, - hide = { - name = L["Hide Bar"], - order = 90, - type = "toggle", - set = "SetProp", - get = "GetProp", - }, - --[[ BROKEN - keybindState = { - name = L["Override Keybinds"], - desc = L["Set this state to maintain its own set of keybinds which override the defaults when active"], - order = 91, - type = "toggle", - set = "SetProp", - get = "GetProp", - }, ]] - position = { - name = L["Position"], - order = 92, - type = "group", - inline = true, - args = { - anchorEnable = { - name = L["Reposition"], - order = 1, - type = "toggle", - set = "SetProp", - get = "GetProp", - }, - anchorFrame = { - name = L["Anchor Frame"], - order = 2, - type = "select", - values = "GetAnchorFrames", - set = "SetAnchorFrame", - get = "GetAnchorFrame", - disabled = "GetAnchorDisabled", - hidden = "GetAnchorDisabled", - }, - anchorPoint = { - name = L["Point"], - order = 3, - type = "select", - values = pointTable, - set = "SetAnchorPointProp", - get = "GetAnchorPointProp", - disabled = "GetAnchorDisabled", - hidden = "GetAnchorDisabled", - }, - anchorRelPoint = { - name = L["Relative Point"], - order = 4, - type = "select", - values = pointTable, - set = "SetAnchorPointProp", - get = "GetAnchorPointProp", - disabled = "GetAnchorDisabled", - hidden = "GetAnchorDisabled", - }, - anchorX = { - name = L["X Offset"], - order = 5, - type = "range", - min = -100, - max = 100, - step = 1, - set = "SetProp", - get = "GetProp", - disabled = "GetAnchorDisabled", - hidden = "GetAnchorDisabled", - }, - anchorY = { - name = L["Y Offset"], - order = 6, - type = "range", - min = -100, - max = 100, - step = 1, - set = "SetProp", - get = "GetProp", - disabled = "GetAnchorDisabled", - hidden = "GetAnchorDisabled", - }, - }, - }, - scale = { - name = L["Scale"], - order = 93, - type = "group", - inline = true, - args = { - enableScale = { - name = L["Set New Scale"], - order = 1, - type = "toggle", - set = "SetProp", - get = "GetProp", - }, - scale = { - name = L["Scale"], - order = 2, - type = "range", - min = 0.25, - max = 2.5, - step = 0.05, - isPercent = true, - set = "SetProp", - get = "GetScale", - disabled = "GetScaleDisabled", - hidden = "GetScaleDisabled", - }, - }, - }, - alpha = { - name = L["Transparency"], - order = 94, - type = "group", - inline = true, - args = { - enableAlpha = { - name = L["Set Transparency"], - order = 1, - type = "toggle", - set = "SetProp", - get = "GetProp", - }, - alpha = { - name = L["Transparency"], - order = 2, - type = "range", - min = 0, - max = 1, - step = 0.01, - bigStep = 0.05, - isPercent = true, - set = "SetProp", - get = "GetAlpha", - disabled = "GetAlphaDisabled", - hidden = "GetAlphaDisabled", - }, - }, - }, - }, - plugins = { } - }, - rules = { - name = L["Rule"], - order = 3, - type = "group", - args = { - mode = { - name = L["Select this state"], - order = 2, - type = "select", - style = "radio", - values = { - default = L["by default"], - any = L["when ANY of these"], - all = L["when ALL of these"], - custom = L["via custom rule"], - keybind = L["via keybinding"], - }, - set = "SetType", - get = "GetType", - }, - clear = { - name = L["Clear All"], - order = 3, - type = "execute", - hidden = "GetClearAllDisabled", - disabled = "GetClearAllDisabled", - func = "ClearAllConditions", - }, - inputs = { - name = L["Conditions"], - order = 4, - type = "multiselect", - hidden = "GetConditionsDisabled", - disabled = "GetConditionsDisabled", - values = ruleSelect, - set = "SetCondition", - get = "GetCondition", - }, - custom = { - name = L["Custom Rule"], - order = 5, - type = "input", - multiline = true, - hidden = "GetCustomDisabled", - disabled = "GetCustomDisabled", - desc = L["Syntax like macro rules: see preset rules for examples"], - set = "SetCustomRule", - get = "GetCustomRule", - validate = "ValidateCustomRule", - }, - keybind = { - name = L["Keybinding"], - order = 6, - inline = true, - hidden = "GetKeybindDisabled", - disabled = "GetKeybindDisabled", - type = "group", - args = { - desc = { - name = L["Invoking a state keybind toggles an override of all other transition rules."], - order = 1, - type = "description", - }, - keybind = { - name = L["State Hotkey"], - desc = L["Define an override toggle keybind"], - order = 2, - type = "keybinding", - set = "SetKeybind", - get = "GetKeybind", - }, - }, - }, - }, - }, - } - - local handlers = { } - local meta = { - __index = function(self, key) - for _, h in pairs(handlers) do - if h[key] then - return h[key] - end - end - end, - } - local StateHandler = setmetatable({ }, meta) - local proto = { __index = StateHandler } - - function RegisterPropertyOptions( field, options, handler ) - stateOptions.properties.plugins[field] = options - handlers[field] = handler - end - - function UnregisterPropertyOptions( field ) - stateOptions.properties.plugins[field] = nil - handlers[field] = nil - end - - function StateHandler:New( bar, opts ) - local self = setmetatable( - { - bar = bar - }, - proto ) - - function self:GetName() - return opts.name - end - - function self:SetName(name) - opts.name = name - end - - function self:GetOrder() - return opts.order - end - - -- get reference to states table: even if the bar - -- name changes the states table ref won't - self.states = tbuild(module.db.profile.bars, bar:GetName(), "states") - self.state = tbuild(self.states, opts.name) - - opts.order = self:GetRuleField("order") - if opts.order == nil then - -- add after the highest - opts.order = 100 - for _, state in pairs(self.states) do - local x = tonumber(tfetch(state, "rule", "order")) - if x and x >= opts.order then - opts.order = x + 1 - end - end - self:SetRuleField("order",opts.order) - end - - return self - end - - -- helper methods - - function StateHandler:SetRuleField( key, value, ... ) - tbuild(self.state, "rule", ...)[key] = value - end - - function StateHandler:GetRuleField( ... ) - return tfetch(self.state, "rule", ...) - end - - function StateHandler:FixAll( setkey ) - -- if multiple selections in the same group are chosen when 'all' is selected, - -- keep only one of them. If changing the mode, the first in the fields list will - -- be chosen arbitrarily. Otherwise, if selecting a new checkbox from the field-set, - -- it will be retained. - local notified = false - if self:GetRuleField("type") == "all" then - for _, c in ipairs(rules) do - local rule, fields = unpack(c) - local once = false - if setkey then - for idx, field in ipairs(fields) do - if next(field) == setkey then - once = true - end - end - end - for idx, field in ipairs(fields) do - local key = next(field) - if self:GetRuleField("values",key) then - if once and key ~= setkey then - self:SetRuleField(key,false,"values") - if not setkey and not notified then - ReAction:UserError(L["Warning: one or more incompatible rules were turned off"]) - notified = true - end - end - once = true - end - end - end - end - end - - function StateHandler:GetNeighbors() - local before, after - for k, v in pairs(self.states) do - local o = tonumber(tfetch(v, "rule", "order")) - if o and k ~= self:GetName() then - local obefore = tfetch(self.states,before,"rule","order") - local oafter = tfetch(self.states,after,"rule","order") - if o < self:GetOrder() and (not obefore or obefore < o) then - before = k - end - if o > self:GetOrder() and (not oafter or oafter > o) then - after = k - end - end - end - return before, after - end - - function StateHandler:SwapOrder( a, b ) - -- do options table - local args = optionMap[self.bar].args - args[a].order, args[b].order = args[b].order, args[a].order - -- do profile - a = tbuild(self.states, a, "rule") - b = tbuild(self.states, b, "rule") - a.order, b.order = b.order, a.order - end - - -- handler methods - - function StateHandler:GetProp( info ) - -- gets property of the same name as the options arg - return GetProperty(self.bar, self:GetName(), info[#info]) - end - - function StateHandler:SetProp( info, value ) - -- sets property of the same name as the options arg - SetProperty(self.bar, self:GetName(), info[#info], value) - end - - function StateHandler:DeleteState() - if self.states[self:GetName()] then - self.states[self:GetName()] = nil - ApplyStates(self.bar) - end - optionMap[self.bar].args[self:GetName()] = nil - end - - function StateHandler:SetStateName(info, value) - -- check for existing state name - if self.states[value] then - ReAction:UserError(format(L["State named '%s' already exists"],value)) - return - end - local args = optionMap[self.bar].args - local name = self:GetName() - self.states[value], args[value], self.states[name], args[name] = self.states[name], args[name], nil, nil - self:SetName(value) - ApplyStates(self.bar) - ReAction:ShowEditor(self.bar, moduleID, value) - end - - function StateHandler:MoveStateUp() - local before, after = self:GetNeighbors() - if before then - self:SwapOrder(before, self:GetName()) - ApplyStates(self.bar) - end - end - - function StateHandler:MoveStateDown() - local before, after = self:GetNeighbors() - if after then - self:SwapOrder(self:GetName(), after) - ApplyStates(self.bar) - end - end - - function StateHandler:GetAnchorDisabled() - return not GetProperty(self.bar, self:GetName(), "anchorEnable") - end - - function StateHandler:GetAnchorFrames(info) - self._anchorframes = self._anchorframes or { } - table.wipe(self._anchorframes) - - table.insert(self._anchorframes, "UIParent") - for name, bar in ReAction:IterateBars() do - table.insert(self._anchorframes, bar:GetFrame():GetName()) - end - return self._anchorframes - end - - function StateHandler:GetAnchorFrame(info) - local value = self:GetProp(info) - for k,v in pairs(self._anchorframes) do - if v == value then - return k - end - end - end - - function StateHandler:SetAnchorFrame(info, value) - local f = _G[self._anchorframes[value]] - if f then - self.bar:SetFrameRef("anchor-"..self:GetName(), f) - self:SetProp(info, f:GetName()) - end - end - - function StateHandler:SetAnchorPointProp(info, value) - self:SetProp(info, value ~= "NONE" and value or nil) - end - - function StateHandler:GetAnchorPointProp(info) - return self:GetProp(info) or "NONE" - end - - function StateHandler:GetScale(info) - return self:GetProp(info) or 1.0 - end - - function StateHandler:GetScaleDisabled() - return not GetProperty(self.bar, self:GetName(), "enableScale") - end - - function StateHandler:GetAlpha(info) - return self:GetProp(info) or 1.0 - end - - function StateHandler:GetAlphaDisabled() - return not GetProperty(self.bar, self:GetName(), "enableAlpha") - end - - function StateHandler:SetType(info, value) - self:SetRuleField("type", value) - self:FixAll() - ApplyStates(self.bar) - end - - function StateHandler:GetType() - return self:GetRuleField("type") - end - - function StateHandler:GetClearAllDisabled() - local t = self:GetRuleField("type") - return not( t == "any" or t == "all" or t == "custom") - end - - function StateHandler:ClearAllConditions() - local t = self:GetRuleField("type") - if t == "custom" then - self:SetRuleField("custom","") - elseif t == "any" or t == "all" then - self:SetRuleField("values", {}) - end - ApplyStates(self.bar) - end - - function StateHandler:GetConditionsDisabled() - local t = self:GetRuleField("type") - return not( t == "any" or t == "all") - end - - function StateHandler:SetCondition(info, key, value) - self:SetRuleField(ruleMap[key], value or nil, "values") - if value then - self:FixAll(ruleMap[key]) - end - ApplyStates(self.bar) - end - - function StateHandler:GetCondition(info, key) - return self:GetRuleField("values", ruleMap[key]) or false - end - - function StateHandler:GetCustomDisabled() - return self:GetRuleField("type") ~= "custom" - end - - function StateHandler:SetCustomRule(info, value) - self:SetRuleField("custom",value) - ApplyStates(self.bar) - end - - function StateHandler:GetCustomRule() - return self:GetRuleField("custom") or "" - end - - function StateHandler:ValidateCustomRule(info, value) - local s = value:gsub("%s","") -- remove all spaces - -- unfortunately %b and captures don't support the '+' notation, or this would be considerably simpler - repeat - if s == "" then - return true - end - local c, r = s:match("(%b[])(.*)") - if c == nil and s and #s > 0 then - return format(L["Invalid custom rule '%s': each clause must appear within [brackets]"],value or "") - end - s = r - until c == nil - return true - end - - function StateHandler:GetKeybindDisabled() - return self:GetRuleField("type") ~= "keybind" - end - - function StateHandler:GetKeybind() - return self:GetRuleField("keybind") - end - - function StateHandler:SetKeybind(info, value) - if value and #value == 0 then - value = nil - end - self:SetRuleField("keybind",value) - ApplyStates(self.bar) - end - - local function CreateStateOptions(bar, name) - local opts = { - type = "group", - name = name, - childGroups = "tab", - args = stateOptions - } - - opts.handler = StateHandler:New(bar,opts) - - return opts - end - - function module:GetBarOptions(bar) - local private = { } - local states = tbuild(module.db.profile.bars, bar:GetName(), "states") - local options = { - name = L["Dynamic State"], - type = "group", - order = -1, - childGroups = "tree", - disabled = InCombatLockdown, - args = { - __desc__ = { - name = L["States are evaluated in the order they are listed"], - order = 1, - type = "description", - }, - __new__ = { - name = L["New State..."], - order = 2, - type = "group", - args = { - name = { - name = L["State Name"], - desc = L["Set a name for the new state"], - order = 1, - type = "input", - get = function() return private.newstatename or "" end, - set = function(info,value) private.newstatename = value end, - pattern = "^%w*$", - usage = L["State names must be alphanumeric without spaces"], - }, - create = { - name = L["Create State"], - order = 2, - type = "execute", - func = function () - local name = private.newstatename - if states[name] then - ReAction:UserError(format(L["State named '%s' already exists"],name)) - else - -- TODO: select default state options and pass as final argument - states[name] = { } - optionMap[bar].args[name] = CreateStateOptions(bar,name) - ReAction:ShowEditor(bar, moduleID, name) - private.newstatename = "" - end - end, - disabled = function() - local name = private.newstatename or "" - return #name == 0 or name:find("%W") - end, - } - } - } - } - } - for name, config in pairs(states) do - options.args[name] = CreateStateOptions(bar,name) - end - optionMap[bar] = options - return options - end -end - --- Module API -- - --- Pass in a property field-name, an implementation secure snippet, a static options table, and an --- optional options handler method-table --- --- The options table is static, i.e. not bar-specific and should only reference handler method --- strings (either existing ones or those added via optHandler). The existing options are ordered --- 90-99. Order #1 is reserved for the heading. --- --- The contents of optHandler, if provided, will be added to the existing StateHandler options metatable. --- See above, for existing API. In particular see the properties set up in the New method: self.bar, --- self.states, and self:GetName(), and the generic property handlers self:GetProp() and self:SetProp(). --- -function module:RegisterStateProperty( field, snippetHandler, options, optHandler ) - RegisterProperty(field, snippetHandler) - RegisterPropertyOptions(field, options, optHandler) -end - -function module:UnregisterStateProperty( field ) - UnregisterProperty(field) - UnregisterPropertyOptions(field) -end - - --- Export methods to Bar class -- - -ReAction.Bar.GetStateProperty = GetProperty -ReAction.Bar.SetStateProperty = SetProperty
--- a/modules/Totem.lua Thu Nov 18 13:11:08 2010 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,151 +0,0 @@ ---[[ - ReAction Totem button module - ---]] - --- local imports -local addonName, addonTable = ... -local ReAction = addonTable.ReAction -local L = ReAction.L -local _G = _G - --- button -local Button = ReAction.Button.MultiCast - --- module declaration -local moduleID = "Totem" -local module = ReAction:NewModule( moduleID, - "AceEvent-3.0" - -- mixins go here -) - --- handlers -function module:OnInitialize() - self.db = ReAction.db:RegisterNamespace( moduleID, - { - profile = { - buttons = { } - } - } - ) - - self.buttons = { } - - ReAction.RegisterCallback(self, "OnCreateBar", "OnRefreshBar") - ReAction.RegisterCallback(self, "OnDestroyBar") - ReAction.RegisterCallback(self, "OnRefreshBar") - ReAction.RegisterCallback(self, "OnEraseBar") - ReAction.RegisterCallback(self, "OnRenameBar") - - self:RegisterEvent("UPDATE_MULTI_CAST_ACTIONBAR","PLAYER_ENTERING_WORLD") -end - -function module:OnEnable() - ReAction:RegisterBarType(L["Totem Bar"], - { - type = moduleID , - defaultButtonSize = 36, - defaultBarRows = 1, - defaultBarCols = 6, - defaultBarSpacing = 3 - }) - -end - -function module:OnDisable() - ReAction:UnregisterBarType(L["Totem Bar"]) -end - -function module:OnDestroyBar(event, bar, name) - local btns = self.buttons[bar] - if btns then - for _,b in pairs(btns) do - if b then - b:Destroy() - end - end - self.buttons[bar] = nil - end -end - -function module:OnRefreshBar(event, bar, name) - if bar.config.type == moduleID then - local btns = self.buttons[bar] - if btns == nil then - btns = { } - self.buttons[bar] = btns - end - local profile = self.db.profile - if profile.buttons[name] == nil then - profile.buttons[name] = {} - end - local btnCfg = profile.buttons[name] - - local r, c = bar:GetButtonGrid() - local n = min(r*c,6) - Button.SetupBarHeader(bar) - for i = 1, n do - if btnCfg[i] == nil then - btnCfg[i] = {} - end - if not btns[i] then - local success, r = pcall(Button.New,Button,i,btnCfg,bar) - if success then - btns[i] = r - if r then - bar:AddButton(i,r) - end - else - geterrorhandler()(r) - n = i - 1 - bar:ClipNButtons(n) - break - end - end - if btns[i] then - btns[i]:Refresh() - end - end - for i = n+1, #btns do - if btns[i] then - bar:RemoveButton(btns[i]) - btns[i] = btns[i]:Destroy() - if btnCfg[i] then - btnCfg[i] = nil - end - end - end - end - -end - -function module:OnEraseBar(event, bar, name) - self.db.profile.buttons[name] = nil -end - -function module:OnRenameBar(event, bar, oldName, newName) - local b = self.db.profile.buttons - b[newname], b[oldname] = b[oldname], nil -end - -function module:RefreshAll() - for bar in pairs(self.buttons) do - self:OnRefreshBar(nil,bar,bar:GetName()) - end -end - -function module:UPDATE_MULTI_CAST_ACTIONBAR() - if not InCombatLockdown() then - for bar in pairs(self.buttons) do - self:OnRefreshBar("OnRefreshBar", bar, bar:GetName()) - end - end -end - -function module:PLAYER_ENTERING_WORLD() - for bar in pairs(self.buttons) do - self:OnRefreshBar("OnRefreshBar", bar, bar:GetName()) - end -end - -
--- a/modules/VehicleExit.lua Thu Nov 18 13:11:08 2010 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,172 +0,0 @@ ---[[ - ReAction Vehicle Exit button module - - The button module implements a single button which you can use - to exit a vehicle that doesn't have controls (replacement for - MainMenuBarLeaveVehicleButton). - ---]] - --- local imports -local addonName, addonTable = ... -local ReAction = addonTable.ReAction -local L = ReAction.L - --- module declaration -local moduleID = "VehicleExit" -local module = ReAction:NewModule( moduleID ) - --- Button class -local Button = ReAction.Button.VehicleExit - --- module methods -function module:OnInitialize() - self.db = ReAction.db:RegisterNamespace( moduleID, - { - profile = { - buttons = { } - } - } - ) - self.registered = { } - self.buttons = { } - - ReAction:RegisterBarOptionGenerator(self, "GetBarOptions") - - ReAction.RegisterCallback(self, "OnCreateBar") - ReAction.RegisterCallback(self, "OnDestroyBar") - ReAction.RegisterCallback(self, "OnRefreshBar") - ReAction.RegisterCallback(self, "OnEraseBar") - ReAction.RegisterCallback(self, "OnRenameBar") -end - -function module:OnEnable() - ReAction:RegisterBarType(L["Exit Vehicle Floater"], - { - type = moduleID , - defaultButtonSize = 36, - defaultBarRows = 1, - defaultBarCols = 1, - defaultBarSpacing = 3 - }) -end - -function module:OnDisable() - ReAction:UnregisterBarType(L["Exit Vehicle Floater"]) -end - -function module:OnCreateBar(event, bar, name) - if bar.config.type == moduleID then - self:OnRefreshBar(event, bar, name) - end -end - -function module:OnRefreshBar(event, bar, name) - if bar.config.type == moduleID then - local profile = self.db.profile - if profile.buttons[name] == nil then - profile.buttons[name] = {} - end - local btnCfg = profile.buttons[name] - - if profile.buttons[name] == nil then - profile.buttons[name] = { } - end - if self.buttons[bar] == nil then - local success, r = pcall(Button.New, Button, 1, profile.buttons[name], bar) - if success and r then - self.buttons[bar] = r - bar:AddButton(1,r) - end - else - self.buttons[bar]:Refresh() - end - bar:ClipNButtons(1) - self:UpdateRegistration(bar) - end -end - -function module:OnDestroyBar(event, bar, name) - if self.buttons[bar] then - self.buttons[bar]:Destroy() - self.buttons[bar] = nil - end -end - -function module:OnEraseBar(event, bar, name) - self.db.profile.buttons[name] = nil -end - -function module:OnRenameBar(event, bar, oldname, newname) - local b = self.db.profile.buttons - b[newname], b[oldname] = b[oldname], nil -end - - -function module:UpdateRegistration(bar) - -- auto show/hide when on a vehicle - local config = self.db.profile.buttons[bar:GetName()] - local f = bar:GetFrame() - if config.withControls then - if bar.vehicleExitStateRegistered then - UnregisterStateDriver(f, "unitexists") - bar.vehicleExitStateRegistered = false - end - bar:RegisterUnitWatch("vehicle",true) - else - bar:RegisterUnitWatch("vehicle",false) - if not bar.vehicleExitStateRegistered then - f:SetAttribute("unit","vehicle") - RegisterStateDriver(f, "unitexists", "[target=vehicle,exists,novehicleui] show; hide") -- spoof onstate-unitexists - bar.vehicleExitStateRegistered = true - end - end -end - ----- Options ---- -local Handler = { } -local meta = { __index = Handler } - -function Handler:New(bar) - return setmetatable( - { - bar = bar - }, meta) -end - -function Handler:GetConfig() - return module.db.profile.buttons[self.bar:GetName()] -end - -function Handler:GetPassengerOnly() - return not self:GetConfig().withControls -end - -function Handler:SetPassengerOnly(info, value) - self:GetConfig().withControls = not value - module:UpdateRegistration(self.bar) -end - - -function module:GetBarOptions(bar) - if bar.config.type == moduleID then - return { - type = "group", - name = L["Exit Vehicle"], - handler = Handler:New(bar), - args = { - passengerOnly = { - name = L["Show only when passenger"], - desc = L["Only show the button when riding as a passenger in a vehicle (no vehicle controls)"], - order = 2, - width = "double", - type = "toggle", - get = "GetPassengerOnly", - set = "SetPassengerOnly", - }, - } - } - end -end - -
--- a/modules/modules.xml Thu Nov 18 13:11:08 2010 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,15 +0,0 @@ -<Ui xmlns="http://www.blizzard.com/wow/ui/" - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:schemaLocation="http://www.blizzard.com/wow/ui/..\FrameXML\UI.xsd"> - -<Script file="State.lua"/> -<Script file="LBF.lua"/> -<Script file="HideBlizzard.lua"/> -<Script file="Action.lua"/> -<Script file="PetAction.lua"/> -<Script file="Stance.lua"/> -<Script file="Bag.lua"/> -<Script file="VehicleExit.lua"/> -<Script file="Totem.lua"/> - -</Ui> \ No newline at end of file