Mercurial > wow > reaction
changeset 245:65f2805957a0
No real reason to store some of the code in a subdirectory.
author | Flick |
---|---|
date | Sat, 26 Mar 2011 12:35:08 -0700 |
parents | f255cd69e890 |
children | 47818b3938c9 |
files | ActionButton.lua BagButton.lua Bar.lua Button.lua MultiCastButton.lua Overlay.lua PetActionButton.lua ReAction.xml StanceButton.lua VehicleExitButton.lua 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 |
diffstat | 20 files changed, 4690 insertions(+), 4646 deletions(-) [+] |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ActionButton.lua Sat Mar 26 12:35:08 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 Sat Mar 26 12:35:08 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 Sat Mar 26 12:35:08 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 Sat Mar 26 12:35:08 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
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MultiCastButton.lua Sat Mar 26 12:35:08 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 +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Overlay.lua Sat Mar 26 12:35:08 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 Sat Mar 26 12:35:08 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 +
--- a/ReAction.xml Sat Mar 26 12:26:55 2011 -0700 +++ b/ReAction.xml Sat Mar 26 12:35:08 2011 -0700 @@ -7,9 +7,15 @@ <Script file="ReAction.lua"/> <Script file="Profile.lua"/> - - <Include file="classes\classes.xml"/> - + <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"/> </Ui>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/StanceButton.lua Sat Mar 26 12:35:08 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 Sat Mar 26 12:35:08 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 Sat Mar 26 12:26:55 2011 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,806 +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" 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
--- a/classes/BagButton.lua Sat Mar 26 12:26:55 2011 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,499 +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 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)
--- a/classes/Bar.lua Sat Mar 26 12:26:55 2011 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,854 +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 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 -
--- a/classes/Button.lua Sat Mar 26 12:26:55 2011 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,359 +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 - --- 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/classes/MultiCastButton.lua Sat Mar 26 12:26:55 2011 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,779 +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: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/classes/Overlay.lua Sat Mar 26 12:26:55 2011 -0700 +++ /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 Sat Mar 26 12:26:55 2011 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,324 +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 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 -
--- a/classes/StanceButton.lua Sat Mar 26 12:26:55 2011 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,198 +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 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
--- a/classes/VehicleExitButton.lua Sat Mar 26 12:26:55 2011 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,108 +0,0 @@ -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/classes.xml Sat Mar 26 12:26:55 2011 -0700 +++ /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