Mercurial > wow > reaction
changeset 7:f920db5fc6b1
version 0.3
author | Flick <flickerstreak@gmail.com> |
---|---|
date | Tue, 20 Mar 2007 21:25:29 +0000 |
parents | 2da5089ab7ff |
children | c05fd3e18b4f |
files | Bindings.xml Button.xml Buttons.lua Defaults.lua Options.lua README.html ReAction.toc classes/ReAction.lua classes/ReAction_ActionDisplay.lua classes/ReAction_ActionType.lua classes/ReAction_ColorScheme.lua classes/ReAction_PetActionDisplay.lua classes/ReAction_PetActionType.lua classes/ReAnchor.lua classes/ReAnchor.xml classes/ReBar.lua classes/ReBar.xml classes/ReBound.lua classes/ReBound.xml libs/AceLibrary/AceLibrary.lua libs/AceLocale-2.2/AceLocale-2.2.lua locale-enUS.lua main.lua |
diffstat | 23 files changed, 4104 insertions(+), 1606 deletions(-) [+] |
line wrap: on
line diff
--- a/Bindings.xml Tue Mar 20 21:20:20 2007 +0000 +++ b/Bindings.xml Tue Mar 20 21:25:29 2007 +0000 @@ -1,8 +1,8 @@ <Bindings> <Binding name="REACTION_TOGGLELOCK" header="REACTION"> - ReAction:ToggleLocked() + ReActionAddOn:ToggleLocked() </Binding> - <Binding name="REBINDER_TOGGLEBINDINGMODE"> - ReBinder:ToggleEnabled() + <Binding name="REBOUND_TOGGLEBINDINGMODE"> + ReBound:ToggleEnabled() </Binding> </Bindings>
--- a/Button.xml Tue Mar 20 21:20:20 2007 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,143 +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"> - - - <CheckButton name="ReActionButtonTemplate" inherits="ActionButtonTemplate, SecureActionButtonTemplate" virtual="true" enableMouse="true"> - <Layers> - <Layer level="ARTWORK"> - <FontString name="$parentActionID" inherits="NumberFontNormalSmall" hidden="true" text=""> - <Anchors> - <Anchor point="BOTTOMLEFT"/> - </Anchors> - <Color r="1.0" g="0.82" b="0.0"/> - </FontString> - </Layer> - </Layers> - <Scripts> - <OnLoad> - this:RegisterForDrag("LeftButton", "RightButton") - this:RegisterForClicks("AnyUp") - </OnLoad> - <PostClick> - if this.rxnBtn:ShouldPickupAction(button) then - PickupAction(this.rxnBtn:GetActionID()) - end - this.rxnBtn:UpdateCheckedState() - </PostClick> - <OnDragStart> - if LOCK_ACTIONBAR ~= "1" then - PickupAction(this.rxnBtn:GetActionID()) - this.rxnBtn:UpdateDisplay() - end - </OnDragStart> - <OnReceiveDrag> - if LOCK_ACTIONBAR ~= "1" then - PlaceAction(this.rxnBtn:GetActionID()) - this.rxnBtn:UpdateDisplay() - end - </OnReceiveDrag> - <OnEnter> - this.rxnBtn:SetTooltip() - </OnEnter> - <OnLeave> - this.rxnBtn:ClearTooltip() - </OnLeave> - </Scripts> - </CheckButton> - - <Frame name="ReActionButtonRecyler" parent="UIParent" hidden="true" setAllPoints="true" enableMouse="false"/> - - <Frame name="ReActionKeybindFrame" frameStrata="DIALOG" enableMouse="true" enableKeyboard="true" parent="UIParent" hidden="true"> - <Size> - <AbsDimension x="160" y="52"/> - </Size> - <Backdrop file="Interface\Tooltips\UI-Tooltip-Background" edgeFile="Interface\Tooltips\UI-Tooltip-Border" tile="true"> - <EdgeSize> - <AbsValue val="16"/> - </EdgeSize> - <TileSize> - <AbsValue val="16"/> - </TileSize> - <BackgroundInsets> - <AbsInset left="0" right="0" top="0" bottom="0"/> - </BackgroundInsets> - </Backdrop> - <Layers> - <Layer level="BACKGROUND"> - <Texture> - <Color r="0" g="0" b="0" a="0.5"/> - </Texture> - </Layer> - <Layer level="ARTWORK"> - <FontString inherits="GameFontNormal" text="Set Keybinding"> - <Anchors> - <Anchor point="TOP"> - <Offset> - <AbsDimension x="0" y="-5"/> - </Offset> - </Anchor> - </Anchors> - </FontString> - </Layer> - </Layers> - <Frames> - <Button name="$parentButton" inherits="UIPanelButtonTemplate2" enableKeyboard="true" enableMouse="true"> - <Size> - <AbsDimension x="120" y="22"/> - </Size> - <Anchors> - <Anchor point="BOTTOM" relativePoint="BOTTOM"> - <Offset> - <AbsDimension x="-2" y="5"/> - </Offset> - </Anchor> - </Anchors> - <Scripts> - <OnLoad> - this:RegisterForClicks("AnyUp") - </OnLoad> - <OnClick> - if this.selected and this.keybindTarget then - this.keybindTarget:HandleKeybindAssign(this, nil, arg1) - else - this.selected = not(this.selected) - end - if this.selected and this.keybindTarget then - this:LockHighlight() - this:SetScript("OnKeyDown", - function() - this.keybindTarget:HandleKeybindAssign(this,arg1) - if not this.selected then - this:UnlockHighlight() - this:SetScript("OnKeyDown", nil) - end - end ) - else - this:UnlockHighlight() - this:SetScript("OnKeyDown", nil) - end - </OnClick> - <OnShow> - this.keybindTarget.button:SetChecked(1) - this.keybindTarget:UpdateDisplay() - </OnShow> - <OnHide> - if this.keybindTarget then - this.keybindTarget.button:SetChecked(0) - this.keybindTarget:UpdateDisplay() - end - </OnHide> - </Scripts> - <NormalFont inherits="GameFontHighlightSmall"/> - <HighlightFont inherits="GameFontHighlightSmall"/> - </Button> - </Frames> - <Scripts> - <OnLoad> - table.insert(UISpecialFrames, this:GetName()) -- auto-hide on escape - </OnLoad> - </Scripts> - </Frame> - -</Ui>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Buttons.lua Tue Mar 20 21:25:29 2007 +0000 @@ -0,0 +1,271 @@ +-- Buttons.lua +-- +-- Defines button types for use with the ReAction AddOn +-- + +local AceOO = AceLibrary("AceOO-2.0") + +local Action = AceOO.Class( + ReAction, + ReAction.ActionType, + ReAction.ActionDisplay, + ReAction.DefaultColorScheme +) + +local PetAction = AceOO.Class( + ReAction, + ReAction.PetActionType, + ReAction.PetActionDisplay, + ReAction.DefaultColorScheme +) + + +ReAction:AddButtonType( "Action", Action, 120 ) +ReAction:AddButtonType( "Pet Action", PetAction, 10 ) + + + +----------------------- +-- static class members +----------------------- +Action.defaultProfile = { + type = "ReAction", + subtype = "Action", + ids = { }, + selfcast = nil, + keyBindLoc = "TOPRIGHT", + stackCountLoc = "BOTTOMRIGHT", + showKeyBind = true, + showStackCount = true, + showMacroText = true, + showGrid = false, + showBorder = true, + keyBindColorCode = false, +} + +PetAction.defaultProfile = { + type = "ReAction", + subtype = "Pet Action", + ids = { }, + keyBindLoc = "TOPRIGHT", + showKeyBind = false, + showGrid = false, + showBorder = true, + keyBindColorCode = false, +} + + + +----------------------- +-- static class methods +----------------------- +function Action:GetDefaultProfile() + return self.defaultProfile +end + +function PetAction:GetDefaultProfile() + return self.defaultProfile +end + + + +local labelPlacementOptions = { + "TOP", + "BOTTOM", + "LEFT", + "RIGHT", + "TOPLEFT", + "TOPRIGHT", + "BOTTOMLEFT", + "BOTTOMRIGHT" +} + + +function Action:GenerateOptionsTable( config, buttonListFunc ) + + local function refresh() + for _, b in pairs(buttonListFunc()) do + b:UpdateAction() + b:UpdateTooltip() + b:UpdateDisplay() + end + end + + return { + type = "group", + args = { + selfcast = { + type = "text", + name = "Self Cast", + desc = "Choose a modifier key to hold down or an alternate button to click to self-cast spells. ".. + "'default' uses the settings in the Interface Options Advanced panel (use this to achieve 'smart self cast'). ".. + "'right-click' self-casting is not supported for multi-page bars.", + get = function() return config.selfcast or "default" end, + set = function(opt) + if opt == "default" then opt = nil end + config.selfcast = opt + for _, b in pairs(buttonListFunc()) do + b:UpdateSelfcast() + end + end, + validate = { "default", "none", "alt", "ctrl", "shift", "right-click" }, + }, + + keyloc = { + type = "text", + name = "Hotkey Location", + desc = "Sets hotkey location", + get = function() return config.keyBindLoc end, + set = function(loc) config.keyBindLoc = loc ; refresh() end, + validate = labelPlacementOptions, + }, + + stackloc = { + type = "text", + name = "Stack Count Location", + desc = "Sets stack count location", + get = function() return config.stackCountLoc end, + set = function(loc) config.stackCountLoc = loc ; refresh() end, + validate = labelPlacementOptions, + }, + + showkeybind = { + type = "toggle", + name = "Show Hotkey", + desc = "Toggle show/hide hot key labels", + get = function() return config.showKeyBind end, + set = function() config.showKeyBind = not config.showKeyBind ; refresh() end, + }, + + showstackcount = { + type = "toggle", + name = "Show Stack Count", + desc = "Toggle show/hide stack count labels", + get = function() return config.showStackCount end, + set = function() config.showStackCount = not config.showStackCount ; refresh() end, + }, + + showmacrotext = { + type = "toggle", + name = "Show Macro Names", + desc = "Toggle show/hide macro name labels", + get = function() return config.showMacroText end, + set = function() config.showMacroText = not config.showMacroText ; refresh() end, + }, + + showgrid = { + type = "toggle", + name = "Always Show Buttons", + desc = "Show button placeholders when no action is assigned or on the cursor. Note that buttons are always shown when bars are unlocked.", + get = function() return config.showGrid end, + set = function() config.showGrid = not config.showGrid ; refresh() end, + }, + + colorhotkeys = { + type = "toggle", + name = "Colorize Hotkeys", + desc = "Toggles coloring hotkeys based on the modifier key. Out-of-range coloring is always enabled.", + get = function() return config.keyBindColorCode end, + set = function() config.keyBindColorCode = not config.keyBindColorCode ; refresh() end, + }, + + --[[ + hideborder = { + type = "toggle", + name = "Hide Border", + desc = "Toggles hiding of the button border frame.", + get = function() return not config.showBorder end, + set = function() config.showBorder = not config.showBorder ; refresh() end, + },]] + } + } +end + + +function PetAction:GenerateOptionsTable( config, buttonListFunc ) + + local function refresh() + for _, b in pairs(buttonListFunc()) do + b:UpdateAction() + b:UpdateTooltip() + b:UpdateDisplay() + end + end + + return { + type = "group", + args = { + keyloc = { + type = "text", + name = "Hotkey Location", + desc = "Sets hotkey location", + get = function() return config.keyBindLoc end, + set = function(loc) config.keyBindLoc = loc ; refresh() end, + validate = { "TOP", "BOTTOM", "LEFT", "RIGHT", "TOPLEFT", "TOPRIGHT", "BOTTOMLEFT", "BOTTOMRIGHT" }, + }, + + showkeybind = { + type = "toggle", + name = "Show Hotkey", + desc = "Toggle show/hide hot key labels", + get = function() return config.showKeyBind end, + set = function() config.showKeyBind = not config.showGrid ; refresh() end, + }, + + showgrid = { + type = "toggle", + name = "Always Show Buttons", + desc = "Show button placeholders when no action is assigned or on the cursor. Note that buttons are always shown when bars are unlocked.", + get = function() return config.showGrid end, + set = function() config.showGrid = not config.showGrid ; refresh() end, + }, + + colorhotkeys = { + type = "toggle", + name = "Colorize Hotkeys", + desc = "Toggles coloring hotkeys based on the modifier key. Out-of-range coloring is always enabled.", + get = function() return config.keyBindColorCode end, + set = function() config.keyBindColorCode = not config.keyBindColorCode ; refresh() end, + }, + + --[[ + hideborder = { + type = "toggle", + name = "Hide Border", + desc = "Toggles hiding of the button border frame.", + get = function() return not config.showBorder end, + set = function() config.showBorder = not config.showBorder ; refresh() end, + },]] + } + } +end + + + +---------------------- +-- Instance methods +---------------------- +function Action.prototype:init( id ) + Action.super.prototype.init(self) + + self:SetupDisplay("ReActionButton"..id) + self:SetupAction() + + -- register button with ReBound for keybinding + if ReBound then + ReBound:AddKeybindTarget(self:GetActionFrame()) + end +end + +function PetAction.prototype:init( id ) + Action.super.prototype.init(self) + + self:SetupDisplay("ReActionPetButton"..id) + self:SetupAction() + + -- register button with ReBound for keybinding + if ReBound then + ReBound:AddKeybindTarget(self:GetActionFrame()) + end +end +
--- a/Defaults.lua Tue Mar 20 21:20:20 2007 +0000 +++ b/Defaults.lua Tue Mar 20 21:25:29 2007 +0000 @@ -1,79 +1,231 @@ +local function tcopy(t) + local r = { } + for k, v in pairs(t) do + r[k] = (type(v) == "table" and tcopy(v) or v) + end + return r +end + -- ReAction default variable tables +local defaultActionConfig = ReAction:GetButtonType("Action"):GetDefaultProfile() +local defaultPetActionConfig = ReAction:GetButtonType("Pet Action"):GetDefaultProfile() --- configuration options for ReActionButton groups -ReActionButtonConfigDefaults = { - type = "ReActionButton", - actionIDs = { }, - keyBindLoc = "TOPLEFT", - keyBindColorCode = true, - stackCountLoc = "BOTTOMRIGHT", - showKeyBind = true, - showNumericCooldown = false, - showStackCount = true, - showMacroName = true, - showGrid = true, +-- default saved variables +ReAction_DefaultProfile = { + hideArt = false, + + bars = { } } -ReActionBarConfigDefaults = { - visibility = true, - size = 36, - spacing = 6, - rows = 1, - columns = 12, - pages = 1, - opacity = 100, - anchor = { - to = "UIParent", - point = "CENTER", - relPoint = "CENTER", - x = 0, - y = 0 - }, - btnConfig = ReActionButtonConfigDefaults -} - - - --- default variables -ReActionProfileDefaults = { - - -- global options - hideArt = false, - - - -- default layout replicates Blizzard layout - bars = { - - -- main paged action bar - [1] = { - visibility = true, +ReAction_DefaultBarConfig = { + ["ReAction"] = { + ["Action"] = { + visible = true, size = 36, spacing = 6, rows = 1, columns = 12, - pages = 5, + pages = nil, opacity = 100, - anchor = { - to = "UIParent", - point = "BOTTOMRIGHT", - relPoint = "BOTTOM", - x = 2, - y = -4 + anchor = { + frame = "UIParent", + point = "CENTER", + relPoint = "CENTER", + x = 0, + y = 0, }, - btnConfig = { - type = "ReActionButton", - actionIDs = { }, - keyBindLoc = "TOPLEFT", - keyBindColorCode = true, - stackCountLoc = "BOTTOMRIGHT", - showKeyBind = true, - showNumericCooldown = false, - showStackCount = true, - showMacroName = true, - showGrid = true, - } + btnConfig = tcopy(defaultActionConfig) + }, + ["Pet Action"] = { + visible = true, + parent = "PetActionBarFrame", + size = 30, + spacing = 8, + rows = 1, + columns = 10, + pages = nil, + opacity = 100, + anchor = { + frame = "UIParent", + point = "CENTER", + relPoint = "CENTER", + x = 0, + y = 0, + }, + btnConfig = tcopy(defaultPetActionConfig), + } + }, +} +-- startup layout replicates Blizzard layout (only set on first-run or reset) +ReAction_DefaultBlizzardBars = { + + -- main paged action bar + [1] = { + visible = true, + size = 36, + spacing = 6, + rows = 1, + columns = 12, + growLeft = false, + growUp = false, + columnMajor = false, + pages = { + n = 6, + showControls = true, + controlsLoc = "Blizzard", + autoStanceSwitch = true, -- priests will get a shadowform bar switch, unlike blizzard's + autoStealthSwitch = true, -- this is different from blizzard's layout, only for druids }, + opacity = 100, + anchor = { + frame = "MainMenuBarArtFrame", + point = "BOTTOMLEFT", + relPoint = "BOTTOMLEFT", + x = 3, + y = 0, + }, + btnConfig = tcopy(defaultActionConfig), + }, + + -- multibar right + [2] = { + visible = false, + size = 36, + spacing = 6, + rows = 12, + columns = 1, + growLeft = true, + growUp = false, + columnMajor = true, + pages = nil, + opacity = 100, + anchor = { + frame = "UIParent", + point = "BOTTOMRIGHT", + relPoint = "BOTTOMRIGHT", + x = -4, + y = 95, + }, + btnConfig = tcopy(defaultActionConfig), + }, + + -- multibar left + [3] = { + visible = false, + size = 36, + spacing = 6, + rows = 12, + columns = 1, + growLeft = true, + growUp = false, + columnMajor = true, + pages = nil, + opacity = 100, + anchor = { + frame = "UIParent", + point = "BOTTOMRIGHT", + relPoint = "BOTTOMRIGHT", + x = -51, + y = 95, + }, + btnConfig = tcopy(defaultActionConfig), + }, + + -- multibar bottom right + [4] = { + visible = false, + size = 36, + spacing = 6, + rows = 1, + columns = 12, + growLeft = false, + growUp = false, + columnMajor = false, + pages = nil, + opacity = 100, + anchor = { + frame = "MainMenuBarArtFrame", + point = "BOTTOMLEFT", + relPoint = "BOTTOMLEFT", + x = 514, + y = 53, + }, + btnConfig = tcopy(defaultActionConfig), + }, + + -- multibar bottom left + [5] = { + visible = false, + size = 36, + spacing = 6, + rows = 1, + columns = 12, + growLeft = false, + growUp = false, + columnMajor = false, + pages = nil, + opacity = 100, + anchor = { + frame = "MainMenuBarArtFrame", + point = "BOTTOMLEFT", + relPoint = "BOTTOMLEFT", + x = 3, + y = 53, + }, + btnConfig = tcopy(defaultActionConfig), + }, + + -- pet action bar + [6] = { + visible = true, + parent = "PetActionBarFrame", + size = 30, + spacing = 8, + rows = 1, + columns = 10, + growLeft = false, + growUp = false, + columnMajor = false, + pages = nil, + opacity = 100, + anchor = { + frame = "PetActionBarFrame", + point = "BOTTOMLEFT", + relPoint = "BOTTOMLEFT", + x = 31, + y = -1, + }, + btnConfig = tcopy(defaultPetActionConfig), + }, + +} + +-- default settings for action IDs match Blizzard's settings... +-- ... except on the main bar extra pages, which map directly to the default shapeshift IDs +-- rather than mirroring the multi action bars, to give access to all 120 actions +local bars = ReAction_DefaultBlizzardBars + +for i = 1, 12 do + bars[1].btnConfig.ids[i] = { + i, + 72+i, + 84+i, + 96+i, + 108+i, + 12+i } -} +end + +for b = 2, 5 do + for i = 1, 12 do + bars[b].btnConfig.ids[i] = { 12*(b-1) + i } + end +end + +for i = 1, 10 do + bars[6].btnConfig.ids[i] = { i } +end + +
--- a/Options.lua Tue Mar 20 21:20:20 2007 +0000 +++ b/Options.lua Tue Mar 20 21:25:29 2007 +0000 @@ -1,4 +1,19 @@ --- Ace2 Options table for ReAction +-- Ace2 global options tables for ReAction + + +-- autogenerate the NewBar sub-types table +local function GenerateNewBarArgs() + local args = { } + for _, name in pairs(ReAction:GetButtonTypeList() ) do + args[name] = { + type="execute", + name=name, + desc=name, + } + end + return args +end + ReActionConsoleOptions = { type="group", @@ -21,7 +36,7 @@ type = "execute", name = "bindings", desc = "Launches keybinding setup mode", - func = function() ReBinder:Enable() end, + func = function() ReBound:Enable() end, }, hideart = { @@ -31,27 +46,29 @@ get = "IsArtHidden", set = "ToggleHideArt", }, - - new = { - type = "execute", - name = "new", - desc = "Create a new bar with default settings", - func = "NewBar" - }, - + showid = { type = "toggle", name = "showid", desc = "Show ActionIDs on buttons", - get = "IsActionIDVisible", - set = "ToggleActionID", + get = "AreIdsVisible", + set = "ToggleIds", + }, + + create = { + type = "group", + name = "create", + pass = true, + func = "NewBar", + args = GenerateNewBarArgs(), + desc = "Create a new bar", }, resetall = { type = "execute", name = "resetall", desc = "Resets to single bar in the default position", - func = "ResetBars" + func = "ResetBars", }, --[[ @@ -59,7 +76,8 @@ type = "execute", name = "resync", desc = "Re-orders action IDs sequentially amongst bars", - func = "ResyncActionIDs" + func = "ResyncActionIDs", + disabled = true -- not yet implemented }, ]] } @@ -67,213 +85,320 @@ ReActionGlobalMenuOptions = { - type="group", + type = "group", + -- handler = nil, -- NOTE: this variable isn't defined yet, must be added later args={ lockbars = { type = "toggle", name = "Lock Bars", desc = "Locks action bars and disables rearrangement", - get = function() return ReAction:IsLocked() end, - set = function() ReAction:ToggleLocked() end, + get = "IsLocked", + set = "ToggleLocked", order = 1, }, lockbtns = { type = "toggle", name = "Lock Buttons", - desc = "Locks action bars and disables rearrangement", + desc = "Prevents buttons from being dragged off accidentally. Shift-drag instead.", get = function() return LOCK_ACTIONBAR == "1" end, set = function() LOCK_ACTIONBAR = (LOCK_ACTIONBAR == "1" and "0" or "1") end, order = 2, }, + new = { + type = "group", + name = "New bar", + pass = true, + func = "NewBar", + args = GenerateNewBarArgs(), + desc = "Create a new bar", + order = 3, + }, + bindings = { type = "execute", name = "Set Key Bindings", desc = "Launches keybinding setup mode", - func = function() ReBinder:Enable() end, - order = 3, + func = function() ReBound:Enable() end, + order = 4, }, - new = { - type = "execute", - name = "New Bar", - desc = "Create a new bar with default settings", - func = function() ReAction:NewBar() end, - order = 4, - }, - showid = { type = "toggle", name = "Show Action IDs", desc = "Show ActionIDs on buttons", - get = function() return ReAction:IsActionIDVisible() end, - set = function() ReAction:ToggleActionID() end, + get = "AreIdsVisible", + set = "ToggleIds", order = 5, }, + hidedefault = { + type = "toggle", + name = "Hide Default Main Menu Bar", + desc = "Hides default Blizzard main menu bar, including bag bar, micro menu bar, shapeshift bar, lag meter, and XP bar", + get = "IsArtHidden", + set = "ToggleHideArt", + order = 6, + }, + --[[ resync = { type = "execute", name = "Re-sync Action IDs", desc = "Re-orders action IDs sequentially amongst bars", - func = function() ReAction:ResyncActionIDs() end, - order = 6, + func = "ResyncActionIDs", + disabled = true, -- not yet implemented }, ]] - hideart = { - type = "toggle", - name = "Hide Default Art", - desc = "Hide default Blizzard action bar artwork and XP bar", - get = function() return ReAction:IsArtHidden() end, - set = function() return ReAction:ToggleHideArt() end, - order = 7, - }, - - --[[ - reset = { - type = "execute", - name = "Reset Bars", - desc = "Resets to single bar in the default position", - func = function() ReAction:ResetBars() end, - order = 8, - }, - ]] - } } -function GenerateReActionBarOptions( bar ) - return { + + + + +function GenerateReActionBarOptions( bar, main ) + local opts = { type = "group", + handler = bar, args = { sep1 = { type = "header", name = " ", desc = " ", - order = 9, + order = 1, }, hdr1 = { type = "header", - name = "Bar Options", - des = "Bar Options", - order = 10, + name = "Options for Bar #"..bar.barID, + desc = "Options for Bar #"..bar.barID, + order = 2, + }, + + layout = { + type = "group", + name = "Layout", + desc = "Button ordering options", + order = 3, + args = { + growLeft = { + type = "toggle", + name = "Right to Left", + desc = "Lay out buttons right-to-left rather than left-to-right", + get = "GetGrowLeft", + set = "SetGrowLeft", + order = 3, + }, + + growUp = { + type = "toggle", + name = "Bottom to Top", + desc = "Lay out buttons bottom-to-top rather than top-to-bottom", + get = "GetGrowUp", + set = "SetGrowUp", + order = 4, + }, + + columnMajor = { + type = "toggle", + name = "Arrange in Columns", + desc = "Lay out buttons sequentially in columns, rather than in rows", + get = "GetColumnMajor", + set = "SetColumnMajor", + order = 5, + }, + + flip = { + type = "execute", + name = "Flip rows/columns", + desc = "Swaps the number of rows and columns, and inverts the button numbering", + func = "FlipRowsColumns", + order = 6, + }, + }, }, - --[[ - hidden = { - type = "toggle", - name = "Hidden", - desc = "Hides the bar except when rearranging bars", - get = function() return not bar:GetVisibility() end, - set = function() bar:ToggleVisibility() end, - order = 11, + paging = { + type = "group", + name = "Paging", + desc = "Multi-page options", + order = 4, + args = { + pages = { + type = "range", + name = "Number of Pages", + desc = "Sets the number of pages", + get = "GetPages", + set = "SetPages", + min = 1, + max = 10, + step = 1, + order = 1, + }, + + autostance = { + type = "toggle", + name = "Auto Stance Switch", + desc = "Automatically switch pages when changing stance or shapeshift form.", + get = "GetAutoStanceSwitch", + set = "ToggleAutoStanceSwitch", + order = 2, + }, + + autostealth = { + type = "toggle", + name = "Auto Stealth Switch", + desc = "Automatically switch pages when stealthing/unstealthing.", + get = "GetAutoStealthSwitch", + set = "ToggleAutoStealthSwitch", + order = 3, + }, + + hidecontrols = { + type = "toggle", + name = "Hide Paging Controls", + desc = "Hide the page up/down controls", + get = "ArePageControlsHidden", + set = "TogglePageControlsHidden", + order = 4, + disabled = "IsPagingDisabled", + }, + + controlsloc = { + type = "text", + name = "Control location", + desc = "Location of the page up/down controls", + get = function() return bar:GetPageControlsLoc() or "Blizzard" end, + set = function(loc) bar:SetPageControlsLoc(loc) end, + order = 5, + disabled = function() return bar:IsPagingDisabled() or bar:ArePageControlsHidden() end, + validate = { "Blizzard", "LEFT", "RIGHT", "TOP", "BOTTOM" } + }, + }, }, - ]] + + visibility = { + type = "group", + name = "Visibility", + desc = "Set bar visibility options", + order = 5, + args = { + visible = { + type = "toggle", + name = "Always Visible", + desc = "The bar will always be visible.|n This setting overrides conditional settings below.", + get = "GetVisibility", + set = function() bar:SetVisibility(true) end, + order = 1, + }, + + hidden = { + type = "toggle", + name = "Always Hidden", + desc = "The bar will always be hidden, except when bars are unlocked.|n This setting overrides conditional settings below.", + get = function() return not bar:GetVisibility() end, + set = function() bar:SetVisibility(false) end, + order = 2, + }, + + spring = { + type = "toggle", + name = "Spring Bar Mode", + desc = "The bar is collapsed to a single button, which displays the last action used. Mousing over the button shows the entire bar.|n This setting overrides conditional settings below.", + get = function() end, + set = function() end, + hidden = true, + disabled = true, -- not yet implemented + order = 3, + }, + + conditional = { + type = "group", + name = "Auto Show", + desc = "Dynamically hide and show the entire bar based on certain conditions.", + hidden = true, + disabled = true, -- not yet implemented + order = 4, + args = { + + mouseover = { + type = "toggle", + name = "Show On Mouseover", + desc = "Show the bar only when the mouse is over it.", + disabled = true, -- not yet implemented + get = function() end, + set = function() end, + order = 1, + }, + + combat = { + type = "toggle", + name = "Show In Combat", + desc = "Show the bar only when in combat.", + disabled = true, -- not yet implemented + get = function() end, + set = function() end, + order = 2, + }, + + nocombat = { + type = "toggle", + name = "Hide in Combat", + desc = "Show the bar only when not in combat.", + disabled = true, -- not yet implemented + get = function() end, + set = function() end, + order = 3, + } + } + }, + + } + }, opacity = { type = "range", name = "Opacity", - desc = "Set bar opacity", - get = function() return bar:GetOpacity() end, - set = function(o) bar:SetOpacity(o) end, + desc = "Set the bar alpha value, from fully transparent (0) to fully opaque (100).", + get = "GetOpacity", + set = "SetOpacity", min = 0, max = 100, step = 1, - order = 12 + order = 6, }, delete = { type = "execute", - name = "Delete Bar", - desc = "Deletes the bar", - func = function() ReAction:DeleteBar(bar.barID) end, - order = 13, + name = "Delete Bar #"..bar.barID, + desc = "Deletes bar #"..bar.barID, + func = function() main:DeleteBar(bar.barID) end, + order = 7 }, } } + + -- generate the auto-hide options for shapeshift forms. Note that this will + -- only show forms that the character has learned, and the ordering may (? did this get fixed?) + -- vary from character to character of the same class + local args = opts.args.visibility.args.conditional.args + for i = 1, GetNumShapeshiftForms() do + local _, name = GetShapeshiftFormInfo(i) + args["stance"..i] = { + type = "toggle", + name = "Show In "..name, + desc = "Show the bar when in "..name.."." , + get = function() end, + set = function() end, + order = #args + 1, + disabled = true, + } + end + + return opts end - -local function setButtonConfig( bar, field, value ) - if bar and bar.config and bar.config.btnConfig then - bar.config.btnConfig[field] = value - for _, b in ipairs(bar.buttons) do - b:ApplyLayout() - b:UpdateDisplay() - end - end -end - -local function getButtonConfig( bar, field ) - if bar and bar.config and bar.config.btnConfig then - return bar.config.btnConfig[field] - end -end - -local function toggleButtonConfig( bar, field ) - if bar and bar.config and bar.config.btnConfig then - bar.config.btnConfig[field] = not bar.config.btnConfig[field] - for _, b in ipairs(bar.buttons) do - b:ApplyLayout() - b:UpdateDisplay() - end - end -end - - -function GenerateReActionButtonOptions( bar ) - return { - type = "group", - args = { - - sep2 = { - type = "header", - name = " ", - desc = " ", - order = 14, - }, - - hdr2 = { - type = "header", - name = "Button Options", - desc = "Button Options", - order = 15, - }, - - showgrid = { - type = "toggle", - name = "Always Show Buttons", - desc = "Show button placeholders when no action is assigned or on the cursor. Note that buttons are always shown when bars are unlocked.", - get = function() return getButtonConfig(bar, "showGrid") end, - set = function() toggleButtonConfig(bar, "showGrid") end, - order = 16, - }, - - colorkeys = { - type = "toggle", - name = "Color Hotkeys", - desc = "Enables/disables colorizing hotkeys by key modifier", - get = function() return getButtonConfig(bar, "keyBindColorCode") end, - set = function() toggleButtonConfig(bar, "keyBindColorCode", c) end, - order = 17, - }, - - keyloc = { - type = "text", - name = "Hotkey Location", - desc = "Sets hotkey location", - get = function() return getButtonConfig(bar, "keyBindLoc") end, - set = function(loc) setButtonConfig(bar, "keyBindLoc", loc) end, - validate = { "TOP", "BOTTOM", "TOPLEFT", "TOPRIGHT", "BOTTOMLEFT", "BOTTOMRIGHT" }, - order = 18, - }, - - } - } -end - -
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/README.html Tue Mar 20 21:25:29 2007 +0000 @@ -0,0 +1,202 @@ +<html> +<head> +<title>ReAction: README</title> +<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"> +</head> + +<body bgcolor="#FFFFFF" text="#000000"> +<h1 align="center">ReAction</h1> +<h3 align="center">AddOn for World of Warcraft</h3> +<p><font size="2">Current Version: 0.3 (beta)<br> + Released: 29 Jan 2007</font><font size="2"><br> + WoW Version Compatibility/TOC: 2.0.6 / TOC 20003</font></p> +<h2>The Basics</h2> +<p>ReAction is a replacement for the default Blizzard action bars. It allows you + redefine your action button layout any way you like.</p> +<h2>Features</h2> +<ul> + <li>Move, resize, create, and arrange as many action bars as you want. Each + bar can contain any number of buttons arranged in a grid layout of any size + and spacing. The full complement of 120 action slots is supported.</li> + <li>Works with all types of actions, including abilities, items, and macros</li> + <li>Layout is done with the mouse, by dragging bars and bar edges on screen. + Automatically anchor bars to each other or the screen edges by holding shift + down as you drag.</li> + <li>Context menus provide independent configuration options for each bar.</li> + <li>Point and click keybinding interface.</li> + <li>Supports multiple 'pages' per bar, configured independently. Automatic stance, + form, and stealth switching among pages is supported, including Shadowform.</li> + <li>Pet action bar is fully supported and configurable.</li> + <li>Settings can be saved per account, realm, character, class, or independent + profile. </li> + <li>Compatible with OmniCC</li> +</ul> +<p>ReAction is built using the <a href="http://www.wowace.com/Wiki/Ace2">Ace2</a> + development framework.</p> +<h2>Using ReAction</h2> +<h3>Installation</h3> +<p>To install ReAction, drag the ReAction folder to World of Warcraft/Interface/AddOns. + Exit World of Warcraft if it's running, then restart.</p> +<h3>Welcome to ReAction</h3> +<p>When you first enter World of Warcraft with ReAction installed, it will look + very similar to the default Blizzard UI, depending on what other AddOns are + installed. Any keybindings you have made to the action bars, however, are not + in effect. Also, only the main menu bar is shown by default. The bottom left, + bottom right, and two right action bars are hidden.</p> +<p>If you have FuBar installed, on the left side you should see the ReAction plugin + icon and label. If not, there should be a button on your minimap. In either + case, that button (the ReAction Control Button) is the gateway to configuring + ReAction.</p> +<p>ReAction has three modes of operation:</p> +<ul> + <li>Normal ("locked")</li> + <li>Configuration ("unlocked")</li> + <li>Keybinding</li> +</ul> +<p>You can toggle Configuration mode by shift-clicking the ReAction Control Button + and toggle Keybinding mode by alt-clicking the button. Global configuration + options are also available by right-clicking the ReAction button. There are + also a limited set of console commands that can be accessed with the "/reaction" + or "/rxn" slash-commands. Type /rxn in the chat box to print a list + of commands.</p> +<p>You can also set key bindings to toggle Configuration and Keybinding modes, + in the standard UI keybindings panel.</p> +<h3>Configuring ReAction</h3> +<p>Start by shift-clicking the ReAction button to enter Configuration mode. All + bars, including hidden bars, are shown in this mode and <b>normal button click + operation is disabled </b>(though keybindings still work). Mouse over the bars, + edges, and corners to see tooltip instructions for moving, resizing, and rearranging + them. Right click each bar for a menu of options pertaining to that bar. For + example, to change a hidden bar to a visible bar, right-click on a bar and choose + Visibility -> Always Visible. Tooltips for each menu option provide details.</p> +<p>When you're done configuring ReAction, shift-click the ReAction button again + to go back to Normal mode.</p> +<p>Configuration mode is not available if you are in combat, and is automatically + cancelled if you enter combat.</p> +<h3>Setting Keybindings</h3> +<p><b>ReAction buttons don't use the regular keybindings of the default UI.</b> + In order to get your buttons to use hotkeys, enter ReAction Keybinding Setup + mode by alt-clicking the ReAction Control Button (or choosing 'keybinding mode' + from the right-click menu). <b>ReAction button keybindings are not available + from the standard keybindings menu!</b> You <i>must</i> use the ReAction keybinding + interface.</p> +<p>Like configuration mode, normal button operation is disabled while in keybinding + mode (though again, any hotkeys you have assigned will work).</p> +<p>With the keybinding interface out, you can mouseover any ReAction button to + see what its current keybinding is. You can set the keybinding on a button by + pressing the key, then clicking the button. To clear a keybinding from a ReAction + button, right-click the button. If you've set up bars with multiple pages, you + can also bind a key to the page up/down buttons on a per-bar basis. Just make + sure the buttons are showing (they can be hidden later) and assign the keybinding + like any other ReAction button.</p> +<p>Keybindings are saved on a per-character basis. Keybinding mode is not available + if you are in combat, and is automatically cancelled if you enter combat.</p> +<h3>Playing with ReAction</h3> +<p>Once you get your keybindings and configuration set up, you're ready to go. + The buttons behave just like regular Blizzard buttons. However, at any time + when you're not in combat, you can quickly switch to configuration mode and + create a new bar, rearrange bars, hide/show a special-use bar, etc. Perfect + for when you get some goofy quest item that has to be used temporarily, or get + a new skill that doesn't fit on your current layout.</p> +<h2>Limitations</h2> +<ul> + <li>Due to a lack of certain functionality being made available by Blizzard, + dragging and dropping actions onto action bars while in combat is a little + funky. You have to click the destination button rather than just release drag + on it.</li> + <li>You can't configure the pet bar unless you actually have a pet out at the + moment. </li> + <li>No support for shapeshift/stance bar (yet)</li> + <li>If you have a bar with empty buttons, even though they're invisible they + still block mouse input from reaching whatever might be under the button. + This is a workaround so that you can drag and drop actions onto hidden slots + during combat.</li> + <li>If you hide the default main menu bar, there is no substitute for the XP + bar, bag bar, or micro menu bar (although the hotkeys for them still work)</li> + <li>The pet bar, by default, appears underneath bar #6 (lower left). You'll + have to move bar 6 out of the way if you want to move the pet bar. Also, it + does not</li> +</ul> +<h2>Known Issues</h2> +<ul> + <li>There may or may not be a particular case in which turning off a pet's attack + while using Eyes of the Beast doesn't work. Further investigation (and leveling + a hunter to get Eyes of the Beast :-P) is required.</li> + <li>The initial mapping of action IDs for shapeshift forms doesn't exactly match + Blizzard's mapping. When first starting up as a warrior, druid, or rogue, + you will probably need to move your actions around.</li> + <li>Action IDs may not always be laid out in sequence, meaning that similar + layouts on different machines may put the actions in a different order.</li> + <li>Keybindings are only saved per character, which causes problems when switching + profiles.</li> + <li>There seems to be a big performance hit when first dragging a bar after + entering configuration mode, and when first enabling keybinding mode.</li> +</ul> +<h2>Future Plans</h2> +<ul> + <li>Use override bindings for the keybindings, and automatically switch with + profiles. Also "steal" bindings from the default UI on first run + so that they're populated with something reasonable.</li> + <li>Add support for bag bar, shapeshift bar, and micro menu bar. May also add + XP bar to default unit frames for those who don't want to use a unit frame + addon to get the XP bar back.</li> + <li>More dynamic bar support: auto show/hide/fade on stance switch, in combat, + key pressed, etc.</li> + <li>"pop-up" bar support, in which a single button shows/hides a bar + when moused over</li> + <li>Better and more flexible interface for defining page transitions on stance/form + shift </li> + <li>Provide a method for normalizing the action ID layout, permitting better + compatibility when used on different computers.</li> + <li>Provide some additional console commands so that enterprising users can + macro certain configuration functionality (notably hiding and showing individual + bars) </li> + <li>Yet more configurable button display options (disable cooldown flash, different + borders, etc)</li> + <li>Localization</li> +</ul> +<h2>Version History</h2> +<p>Version 0.3</p> +<ul> + <li>Complete rewrite of the innards for better modularity</li> + <li>Lots of bug fixes</li> + <li>Multi-paged bar support</li> + <li>Pet bar support</li> + <li>Auto stance/form/stealth switching</li> + <li>Lots and lots of new configuration options</li> +</ul> +<p>Version 0.2</p> +<ul> + <li>Bug fixes from 0.1</li> + <li>new keybinding interface</li> +</ul> +<p>Version 0.1</p> +<ul> + <li>Initial concept, button arrangement</li> +</ul> +<h2>Credits</h2> +<p>Huge credit to the <a href="http://www.wowace.com">Ace 2</a> development team, + whose framework provides a level of functionality that I consider critical to + the success of this addon.</p> +<p>Similarly, enormous credit goes to <a href="http://ckknight.wowinterface.com">ckknight</a>, + whose Dewdrop menuing system and FuBar plugin system are ridiculously easy to + use.</p> +<p>Finally, thanks to my alpha testers and stalwart adventuring companions: Deor, + Nogrim, Sorabel, and Yngvi.</p> +<h2>Copyright</h2> +<p>ReAction © 2007 by Ryan Findley.</p> +<p>You are free to distribute this AddOn package without notice to the author, + as long as the package is unmodified, this readme.html file accompanies the + distribution, and no claim of authorship of the contents of the package is made. + Derived works are expressly permitted but must be clearly labeled as not the + work of the original author and packaged separately, with credit given to the + original author. Inclusion of this package within a compilation is expressly + permitted.</p> +<p>The author expressly disclaims any warranty of any kind for the material contained + in this package. USE AT YOUR OWN RISK.</p> +<p>All files in this package under the 'libs/' directory are libraries made available + by <a href="http://www.wowace.com">WoWAce</a>, and are not the work of this + author.</p> +<p>World of Warcraft is a registered trademark of Blizzard Entertainment, Inc.</p> +</body> +</html>
--- a/ReAction.toc Tue Mar 20 21:20:20 2007 +0000 +++ b/ReAction.toc Tue Mar 20 21:25:29 2007 +0000 @@ -1,15 +1,16 @@ -## Interface: 20000 +## Interface: 20003 ## Title: ReAction |cff7fff7f -Ace2-|r -## Notes: An action button layout tool +## Notes: An action bar and button layout tool ## DefaultState: enabled ## LoadOnDemand: 0 ## Author: Flick -## Version: 0.1 +## Version: 0.3 ## X-Description: An action bar and button layout tool ## X-Category: Action Bars ## SavedVariables: ReActionDB ## SavedVariablesPerCharacter: ReActionDBPC -## OptionalDeps: Ace2 +## OptionalDeps: Ace2, FuBar +## X-Embeds: Ace2, DewdropLib, TabletLib libs\AceLibrary\AceLibrary.lua libs\AceOO-2.0\AceOO-2.0.lua @@ -17,22 +18,29 @@ libs\AceDB-2.0\AceDB-2.0.lua libs\AceConsole-2.0\AceConsole-2.0.lua libs\AceEvent-2.0\AceEvent-2.0.lua +libs\AceLocale-2.2\AceLocale-2.2.lua libs\Dewdrop-2.0\Dewdrop-2.0.lua libs\Tablet-2.0\Tablet-2.0.lua libs\FuBarPlugin-2.0\FuBarPlugin-2.0.lua -ReBinder.lua -ReBinder.xml +locale-enUS.lua +classes\ReAnchor.xml +classes\ReAnchor.lua +classes\ReBound.xml +classes\ReBound.lua +classes\ReBar.xml +classes\ReBar.lua + +classes\ReAction.lua +classes\ReAction_ActionType.lua +classes\ReAction_ActionDisplay.lua +classes\ReAction_ColorScheme.lua +classes\ReAction_PetActionType.lua +classes\ReAction_PetActionDisplay.lua + +Buttons.lua Defaults.lua Options.lua -ReBar.lua -ReBar.xml - -Button.lua -Button.xml - -ReAction.lua - - +main.lua
--- a/classes/ReAction.lua Tue Mar 20 21:20:20 2007 +0000 +++ b/classes/ReAction.lua Tue Mar 20 21:25:29 2007 +0000 @@ -1,19 +1,39 @@ +-- +-- ReAction is a base class for action button management. It provides a +-- framework for button setup, placement, recycling, keybinding, etc. +-- It does not define any layout or actual action functionality, +-- which is deferred to derived classes. +-- +-- ReAction implements the ReBar.IButton interface. It is designed to be used with ReBar +-- for grouping and laying out buttons. +-- +-- ReAction supports the ReBound keybinding interface (only). +-- +-- Each instance of a ReAction-derived object is associated with a single action +-- button frame, which may or may not have a one-to-one mapping with actionID. +-- +-- ReAction makes use of a configuration structure, which can (and should) be +-- extended by implementations. A single config structure is shared amongst all +-- ReAction class instances within a single ReBar group, so the structure should +-- contain sub-tables for any property that needs to be defined on a per-button +-- basis. Each button is passed a 'barIdx' parameter to be used as an index into +-- such tables. +-- +-- The base config structure is as follows: +-- +-- config = { +-- type = "ReAction", -- static string (used by ReBar) +-- subtype = "string", -- ReAction implementation identifier (index into ReAction.buttonTypes) +-- ids = { {paged list}, {paged list}, ... } -- indexed by self.barIdx +-- } +-- + + +local AceOO = AceLibrary("AceOO-2.0") +local kbValidate = AceLibrary("AceConsole-2.0").keybindingValidateFunc + + -- private constants -local namePrefix = "ReActionButton" -local _G = getfenv(0) -local ACTION_FREE = { } -local MAX_ACTIONS = 120 - -local hotKeyDefaultColor = { r=1.0, g=1.0, b=1.0, a=1.0 } -local hotKeyDisabledColor = { r=0.6, g=0.6, b=0.6, a=1.0 } -local hotKeyOutOfRangeColor = { r=1.0, g=0.2, b=0.2, a=1.0 } - -local hotKeyModifierColors = { - S = { r=0.6, g=0.6, b=1.0, a=1.0 }, -- shift - C = { r=1.0, g=0.82, b=0, a=1.0 }, -- ctrl - A = { r=0.1, g=1.0, b=0.1, a=1.0 }, -- alt -} - -- TODO: localize these key names with GetBindingText(KEY_) local keybindAbbreviations = { ["Mouse Button "] = "M-", @@ -24,750 +44,324 @@ [" Arrow"] = "", } -local equippedActionBorderColor = { r=0, g=1.0, b=0, a=0.35 } -local actionUsableColor = { r=1.0, g=1.0, b=1.0, a=1.0 } -local actionNotUsableColor = { r=0.4, g=0.4, b=0.4, a=1.0 } -local actionNotEnoughManaColor = { r=0.2, g=0.2, b=0.7, a=1.0 } -local actionOutOfRangeColor = { r=1.0, g=0.2, b=0.2, a=1.0 } +------------------------ +-- Interface Declarations +------------------------ --- private variables -local kbValidate = AceLibrary("AceConsole-2.0").keybindingValidateFunc -local actionButtonTbl = { } +-- The ActionType interface defines what the button does when it is clicked. At a +-- minimum it must do the equivalent of SetAttribute("type", ...) and handle events that +-- cause the action to change. +-- ReAction implementations must provide this interface (implicitly or explicitly). +local IActionType = AceOO.Interface { + SetID = "function", -- SetID(id, [page]) optional argument indicates page #: omitting indicates default. self.config.idx[barIdx] must be updated. + GetID = "function", -- id = GetID([page]) optional argument indicates page #: omitting indicates current page + IsActionEmpty = "function", -- bool = IsActionEmpty() + SetupAction = "function", -- one-time setup + UpdateAction = "function", -- general action properties should be refreshed + PickupAction = "function", -- pick up the action on the button and put on the cursor + PlaceAction = "function", -- place the action on the cursor + UpdateTooltip = "function", -- update the tooltip with the action's info +} +-- The Display interface defines the "look and feel" of the action button. It should define the +-- actual widgets and widget layout and provide methods to update the display. +-- ReAction implementations must provide this interface (implicitly or explicitly). +-- Note that ReAction implementations may also require additional display interfaces to be supported. +-- +-- Also note: the class 'new' method must take *only* the primary button ID as an argument. +local IDisplay = AceOO.Interface { + SetupDisplay = "function", -- SetupDisplay(buttonName), one-time setup + UpdateDisplay = "function", -- UpdateDisplay(), general display state should be refreshed + TempShow = "function", -- TempShow(visible), calls to this can be nested so keep track. + GetActionFrame = "function", -- f = GetActionFrame(), return a frame derived from SecureActionButtonTemplate (note: this is inherited unimplemented from ReBar.IButton) + GetBaseButtonSize = "function", -- sz = GetBaseButtonSize(), return size in pixels of the nominal button (square) + DisplayID = "function", -- DisplayID(id), show the action ID (or equivalent). Pass nil to hide. + DisplayHotkey = "function", -- DisplayHotkey(keyText), set the hotkey display text +} --- ReActionButton is a class prototype object. -ReActionButton = AceLibrary("AceOO-2.0").Class("AceEvent-2.0") +---------------------------- +-- ReAction class definition +---------------------------- -------------------- --- Class methods -------------------- +ReAction = AceOO.Class("AceEvent-2.0", ReBar.IButton, IActionType, IDisplay) +ReAction.virtual = true +ReAction.IActionType = IActionType +ReAction.IDisplay = IDisplay --- In addition to supporting the 'new' creation method (in which case an action ID must be specified directly), --- ReActionButton supports the 'acquire'/'release' creation method, which recycles objects and manages actionID assignment. -function ReActionButton:acquire(parent, config, barIdx) +----------------------- +-- Static class members +----------------------- + +ReAction.recycler = CreateFrame("Frame",nil,UIParent) +ReAction.recycler:SetAllPoints(UIParent) +ReAction.recycler:Hide() + +ReAction.buttonTypes = { } + + + +----------------------- +-- Static class methods +----------------------- + +function ReAction:AddButtonType( name, class, maxIDs ) + self.buttonTypes[name] = { subtype = class, maxIDs = maxIDs } +end + +function ReAction:GetButtonType( name ) + if name then + local t = self.buttonTypes[name] + if t then + return t.subtype, t.maxIDs + end + end +end + +function ReAction:GetButtonTypeList() + local list = { } + for name, _ in pairs(self.buttonTypes) do + table.insert(list,name) + end + return list +end + +function ReAction:GetAvailableID( subtype, hint ) + local class, maxIDs = self:GetButtonType(subtype) + + -- store the list of action buttons in use in the button type class factory + if class._idTbl == nil then class._idTbl = { } end + local t = class._idTbl local id = nil - for i = 1, MAX_ACTIONS do - if actionButtonTbl[i] == nil or actionButtonTbl[i].inUse == false then + + -- find lowest ID not in use + for i = 1, maxIDs do + if t[i] == nil or t[i].inUse == false then id = i break end end - if id == nil then return nil end -- all buttons and action ids are in use + if id == nil then return nil end -- all action ids are in use - local hint = config.actionIDs[barIdx] - if hint and (actionButtonTbl[hint] == nil or actionButtonTbl[hint].inUse == false) then + -- if a hint is given, see if that one is free instead + if hint and (t[hint] == nil or t[hint].inUse == false) then id = hint end - if actionButtonTbl[id] == nil then - actionButtonTbl[id] = { } + if t[id] == nil then + t[id] = { } end - local t = actionButtonTbl[id] + t[id].inUse = true - t.inUse = true - if t.button then - t.button:Configure(parent,config,barIdx) - else - t.button = self:new(parent,config,barIdx,id) + return id, t[id] +end + +function ReAction:Acquire(config, barIdx, pages, buttonsPerPage) + local btnType = self:GetButtonType(config.subtype) + if not btnType then + error("ReAction: Unknown button type specified.") + end + pages = pages or 1 + + local ids = { } + local primary = nil + + for i = 1, pages do + local hint = config.ids[barIdx] and config.ids[barIdx][i] + if hint == nil and i > 1 and ids[i-1] then + hint = ids[i-1] + (buttonsPerPage or 0) + end + local id, p = self:GetAvailableID(config.subtype, hint) + if id == nil then + break + end + primary = primary or p + if id then + ids[i] = id + end end - -- fix screwy config with overlapping IDs - config.actionIDs[barIdx] = id - return t.button + if primary then + if not primary.button then + primary.button = btnType:new(ids[1]) + end + if primary.button then + config.ids[barIdx] = ids + primary.button:Configure(config,barIdx) + end + end + + return primary and primary.button end -function ReActionButton:release( b ) +function ReAction:Release( b ) if b then - actionButtonTbl[b:GetActionID()].inUse = false + for i = 1, #b.config.ids[b.barIdx] do + local id = b:GetID(i) + if id then + b.class._idTbl[id].inUse = false + end + end + b.config = nil b:Recycle() end end - - -function ReActionButton:ShowAllActionIDs() - for _, b in ipairs(actionButtonTbl) do - b:ShowActionID() +function ReAction:ShowAllIds() + for _, t in pairs(self.buttonTypes) do + if t.subtype._idTbl then + for _, tbl in pairs(t.subtype._idTbl) do + if tbl.button then tbl.button:DisplayID(tbl.button:GetID()) end + end + end end + self.showIDs_ = true end -function ReActionButton:HideAllActionIDs() - for _, b in ipairs(actionButtonTbl) do - b:HideActionID() +function ReAction:HideAllIds() + for _, t in pairs(self.buttonTypes) do + if t.subtype._idTbl then + for _, tbl in pairs(t.subtype._idTbl) do + if tbl.button then tbl.button:DisplayID(nil) end + end + end end + self.showIDs_ = false end ---------------------- -- Instance methods ---------------------- -function ReActionButton.prototype:init( parentFrame, config, barIdx, id ) - ReActionButton.super.prototype.init(self) - -- create the button widget - self.name = namePrefix..id - self.button = CreateFrame("CheckButton", self.name, parentFrame, "ReActionButtonTemplate") - - -- store references to the various sub-frames so we don't have to look it up all the time - self.frames = { - hotkey = _G[self.name.."HotKey"], - count = _G[self.name.."Count"], - cooldown = _G[self.name.."Cooldown"], --- nCooldown = _G[self.name.."CooldownNumeric"], - macro = _G[self.name.."Name"], - icon = _G[self.name.."Icon"], - border = _G[self.name.."Border"], - normalTexture = _G[self.name.."NormalTexture"], - flash = _G[self.name.."Flash"], - actionID = _G[self.name.."ActionID"], - } +-- constructor - -- provide a reference back to this object for the frame to use in event handlers - self.button.rxnBtn = self - - -- set the action ID - self:SetActionID(id) - - -- register button with ReBinder for keybinding - ReBinder:AddKeybindTarget(self.button) - - -- initialize - self:Configure(parentFrame, config, barIdx) +function ReAction.prototype:init() + ReAction.super.prototype.init(self) end -local function tcopy(t) - local r = { } - for k, v in pairs(t) do - r[k] = (type(v) == "table" and tcopy(v) or v) - end - return r + +-- ReBar.IButton interface + +function ReAction.prototype:BarUnlocked() + self:TempShow(true) end -function ReActionButton.prototype:Recycle() - local b = self.button - - self:SetKeyBinding(nil) - self:UpdateDisplay() - b:UnregisterAllEvents() - b:SetParent(ReActionButtonRecycleFrame) - b:ClearAllPoints() - b:SetPoint("TOPLEFT",0,0) - b:Hide() - self.config = tcopy(self.config) -- ew, but necessary +function ReAction.prototype:BarLocked() + self:TempShow(false) end -function ReActionButton.prototype:BarUnlocked() - self:ShowGridTmp() -end - -function ReActionButton.prototype:BarLocked() - self:HideGridTmp() -end - - --- set the button location -function ReActionButton.prototype:PlaceButton(point, x, y, sz) - local b = self.button - local scale = sz / 36 +function ReAction.prototype:PlaceButton(parent, point, x, y, sz) + local b = self:GetActionFrame() + local baseSize = self:GetBaseButtonSize() + local scale = baseSize and baseSize ~= 0 and ((sz or 1) / baseSize) or 1 + if scale == 0 then scale = 1 end b:ClearAllPoints() - b:SetScale( scale ) + b:SetParent(parent) + b:SetScale(scale) b:SetPoint(point,x/scale,y/scale) end - -function ReActionButton.prototype:ShowActionID() - self.frames.actionID:Show() -end - -function ReActionButton.prototype:HideActionID() - self.frames.actionID:Hide() -end - - - --- configuration and setup -function ReActionButton.prototype:Configure( parentFrame, config, barIdx ) - self.config = config - self.barIdx = barIdx - self.showGridTmp_ = 0 - - self.button:ClearAllPoints() - self.button:SetParent(parentFrame) - - self:SetupAttributes() - self:RegisterStaticEvents() - - self:ApplyLayout() - self:ApplyStyle() - - self:UpdateDisplay() -end - -function ReActionButton.prototype:SetupAttributes() - local b = self.button - b:SetAttribute("type", "action") - b:SetAttribute("shift-type*", ATTRIBUTE_NOOP) - b:SetAttribute("checkselfcast", true) - b:SetAttribute("useparent-unit", true) -end - -function ReActionButton.prototype:RegisterStaticEvents() - self:RegisterEvent("PLAYER_ENTERING_WORLD") - self:RegisterEvent("ACTIONBAR_SLOT_CHANGED") - self:RegisterEvent("UPDATE_BINDINGS") - self:RegisterEvent("ACTIONBAR_SHOWGRID") - self:RegisterEvent("ACTIONBAR_HIDEGRID") -end - -function ReActionButton.prototype:RegisterActionEvents() - self:RegisterEvent("ACTIONBAR_UPDATE_STATE") - self:RegisterEvent("ACTIONBAR_UPDATE_USABLE") - self:RegisterEvent("ACTIONBAR_UPDATE_COOLDOWN", "ACTIONBAR_UPDATE_USABLE") - self:RegisterEvent("UPDATE_INVENTORY_ALERTS", "ACTIONBAR_UPDATE_USABLE") - self:RegisterEvent("PLAYER_AURAS_CHANGED", "ACTIONBAR_UPDATE_USABLE") - self:RegisterEvent("PLAYER_TARGET_CHANGED", "ACTIONBAR_UPDATE_USABLE") - self:RegisterEvent("UNIT_INVENTORY_CHANGED") - self:RegisterEvent("CRAFT_SHOW") - self:RegisterEvent("CRAFT_CLOSE", "CRAFT_SHOW") - self:RegisterEvent("TRADE_SKILL_SHOW", "CRAFT_SHOW") - self:RegisterEvent("TRADE_SKILL_CLOSE", "CRAFT_SHOW") - self:RegisterEvent("PLAYER_ENTER_COMBAT", "CRAFT_SHOW") - self:RegisterEvent("PLAYER_LEAVE_COMBAT") - self:RegisterEvent("START_AUTOREPEAT_SPELL") - self:RegisterEvent("STOP_AUTOREPEAT_SPELL") - - self.button:SetScript("OnUpdate", function() self:OnUpdate(arg1) end) - self.actionEventsRegistered = true -end - -function ReActionButton.prototype:UnregisterActionEvents() - self:UnregisterEvent("ACTIONBAR_UPDATE_STATE") - self:UnregisterEvent("ACTIONBAR_UPDATE_USABLE") - self:UnregisterEvent("ACTIONBAR_UPDATE_COOLDOWN") - self:UnregisterEvent("UPDATE_INVENTORY_ALERTS") - self:UnregisterEvent("PLAYER_AURAS_CHANGED") - self:UnregisterEvent("PLAYER_TARGET_CHANGED") - self:UnregisterEvent("UNIT_INVENTORY_CHANGED") - self:UnregisterEvent("CRAFT_SHOW") - self:UnregisterEvent("CRAFT_CLOSE") - self:UnregisterEvent("TRADE_SKILL_SHOW") - self:UnregisterEvent("TRADE_SKILL_CLOSE") - self:UnregisterEvent("PLAYER_ENTER_COMBAT") - self:UnregisterEvent("PLAYER_LEAVE_COMBAT") - self:UnregisterEvent("START_AUTOREPEAT_SPELL") - self:UnregisterEvent("STOP_AUTOREPEAT_SPELL") - - self.button:SetScript("OnUpdate", nil) - self.actionEventsRegistered = false -end - - --- event handlers -function ReActionButton.prototype:ACTIONBAR_SLOT_CHANGED() - if arg1 == 0 or arg1 == self:GetActionID() then - self:UpdateDisplay() - end -end - -function ReActionButton.prototype:PLAYER_ENTERING_WORLD() - self:UpdateDisplay() -end - -function ReActionButton.prototype:UPDATE_BINDINGS() - self:UpdateDisplay() -end - -function ReActionButton.prototype:ACTIONBAR_SHOWGRID() - self:ShowGridTmp() -end - -function ReActionButton.prototype:ACTIONBAR_HIDEGRID() - self:HideGridTmp() -end - -function ReActionButton.prototype:ACTIONBAR_UPDATE_STATE() - self:UpdateCheckedState() -end - -function ReActionButton.prototype:ACTIONBAR_UPDATE_USABLE() - self:UpdateUsable() - self:UpdateCooldown() - self:ColorHotKey() -end - -function ReActionButton.prototype:UNIT_INVENTORY_CHANGED() - if arg1 == "player" then - self:UpdateDisplay() - end -end - -function ReActionButton.prototype:CRAFT_SHOW() - self:UpdateCheckedState() -end - -function ReActionButton.prototype:PLAYER_ENTER_COMBAT() - if IsAttackAction(self:GetActionID()) then - self:StartFlash() - end -end - -function ReActionButton.prototype:PLAYER_LEAVE_COMBAT() - if IsAttackAction(self:GetActionID()) then - self:StopFlash() - end -end - -function ReActionButton.prototype:START_AUTOREPEAT_SPELL() - if IsAutoRepeatAction(self:GetActionID()) then - self:StartFlash() - end -end - -function ReActionButton.prototype:STOP_AUTOREPEAT_SPELL() - if self:IsFlashing() and not IsAttackAction(self:GetActionID()) then - self:StopFlash() - end -end - - --- OnUpdate handler -function ReActionButton.prototype:OnUpdate(elapsed) - local action = self:GetActionID() - local f = self.frames - - -- handle flashing - if self:IsFlashing() then - self.flashtime = self.flashtime - elapsed - if self.flashtime <= 0 then - local overtime = -self.flashtime - if overtime >= ATTACK_BUTTON_FLASH_TIME then - overtime = 0 - end - self.flashtime = ATTACK_BUTTON_FLASH_TIME - overtime - - if f.flash:IsVisible() then - f.flash:Hide() - else - f.flash:Show() - end - end - end - - -- Handle range indicator - if self.rangeTimer then - self.rangeTimer = self.rangeTimer - elapsed - if self.rangeTimer <= 0 then - self:ColorHotKey() - self:UpdateUsable() - self.rangeTimer = TOOLTIP_UPDATE_TIME - end - end - - -- handle toltip update - if self.tooltipTime then - self.tooltipTime = self.tooltipTime - elapsed - if self.tooltipTime <= 0 then - if GameTooltip:IsOwned(self.button) then - self:UpdateTooltip() - else - self.tooltipTime = nil +function ReAction.prototype:SetPages( n ) + n = tonumber(n) + local ids = self.config.ids[self.barIdx] + if n and n >= 1 then + -- note that as long as n >= 1 then id[1] will never be modified, which is what we want + -- because then the button frame ID would be out of sync with the static button name + while #ids < n do + local id = ReAction:GetAvailableID(self.config.subtype, ids[#ids] + #self.config.ids) + if id == nil then + break end + self:SetID( id, #ids + 1 ) + table.insert(ids, id) + end + while #ids > n do + local id = table.remove(ids) + self:SetID( nil, #ids + 1 ) + self.class._idTbl[id].inUse = false end end end +-- Event handlers +function ReAction.prototype:UPDATE_BINDINGS() + self:DisplayHotkey(self:GetKeyBindingText(nil, true)) +end --- keybinding functions -function ReActionButton.prototype:SetKeyBinding( k ) +-- Internal functions + +function ReAction.prototype:Recycle() + --self:SetKeyBinding(nil) -- TODO: only if we're using override bindings + self:UnregisterAllEvents() + + -- tuck the frame away + local b = self:GetActionFrame() + b:SetParent(ReAction.recycler) + b:ClearAllPoints() + b:SetPoint("TOPLEFT",0,0) + b:Hide() +end + +function ReAction.prototype:Configure( config, barIdx ) + self.config = config + self.barIdx = barIdx + + local ids = config.ids[barIdx] + self:SetID(ids[1]) -- default id + for i = 1, #ids do + self:SetID(ids[i], i) -- paged ids + end + self:UpdateAction() + self:UpdateDisplay() + self:DisplayHotkey(self:GetKeyBindingText(nil, true)) + + self:RegisterEvent("UPDATE_BINDINGS") +end + +function ReAction.prototype:SetKeyBinding( k, mouseBtn ) if k == nil or kbValidate(k) then - local current = self:GetKeyBinding() - ClearOverrideBindings(self.button) + local current = self:GetKeyBinding(mouseBtn) + -- !!!TODO: do we need this? + -- ClearOverrideBindings(self:GetActionFrame()) if current then SetBinding(current,nil) end if k then - SetBindingClick(k, self.name, "LeftButton") + -- TODO: use OverrideBinding and store the keybinding in the profile. + SetBindingClick(k, self:GetActionFrame():GetName(), mouseBtn or "LeftButton") end end end -function ReActionButton.prototype:GetKeyBinding() - return GetBindingKey("CLICK "..self.name..":LeftButton") +function ReAction.prototype:GetKeyBinding( mouseBtn ) + return GetBindingKey("CLICK "..self:GetActionFrame():GetName()..":"..(mouseBtn or "LeftButton")) end --- action ID functions -function ReActionButton.prototype:SetActionID( id ) - self.actionID = tonumber(id) -- force data integrity - self:ApplyActionID() +function ReAction.prototype:GetKeyBindingText( mouseBtn, abbrev ) + local key = self:GetKeyBinding(mouseBtn) + local txt = key and GetBindingText(key, "KEY_", abbrev and 1) or "" + + if txt and abbrev then + -- further abbreviate some key names + for pat, rep in pairs(keybindAbbreviations) do + txt = string.gsub(txt,pat,rep) + end + end + return txt end -function ReActionButton.prototype:GetActionID() - return self.actionID -end - -function ReActionButton.prototype:ApplyActionID() - local action = tonumber(self:GetActionID()) - self.button:SetAttribute("action",action) - self.frames.actionID:SetText(action or "") -end - -function ReActionButton.prototype:ShowActionID() - self.frames.actionID:Show() -end - -function ReActionButton.prototype:HideActionID() - self.frames.actionID:Hide() -end - -function ReActionButton:ShowAllActionIDs() -- class-wide function - for _, tbl in pairs(actionButtonTbl) do - if tbl.button then tbl.button:ShowActionID() end - end -end - -function ReActionButton:HideAllActionIDs() -- class-wide function - for _, tbl in pairs(actionButtonTbl) do - if tbl.button then tbl.button:HideActionID() end - end -end - - --- action transfer functions -function ReActionButton.prototype:ShouldPickupAction(mouseButton) - return IsShiftKeyDown() and not SecureButton_GetModifiedAttribute(self.button, "type", mouseButton) -end - -function ReActionButton.prototype:ShowGridTmp() - self.showGridTmp_ = self.showGridTmp_ + 1 - self:UpdateVisibility() -end - -function ReActionButton.prototype:HideGridTmp() - self.showGridTmp_ = self.showGridTmp_ - 1 - self:UpdateVisibility() -end - -function ReActionButton.prototype:ShowGrid() - self.config.showGrid = true - self:UpdateVisibility() -end - -function ReActionButton.prototype:HideGrid() - self.config.showGrid = false - self:UpdateVisibility() -end - - - --- layout & style functions -function ReActionButton.prototype:ApplyLayout() - local f = self.frames - - if self.config.keyBindLoc then - local h = f.hotkey - local loc = self.config.keyBindLoc - local top = string.match(loc,"TOP") - local bottom = string.match(loc, "BOTTOM") - h:ClearAllPoints() - h:SetWidth(40) - h:SetPoint(top or bottom,0,top and 2 or -2) - local j - if string.match(loc,"LEFT") then - j = "LEFT" - elseif string.match(loc,"RIGHT") then - j = "RIGHT" - else - j = "CENTER" - end - h:SetJustifyH(j) - end - - if self.config.stackCountLoc then - local c = f.count - local loc = self.config.stackCountLoc - local top = string.match(loc,"TOP") - local bottom = string.match(loc, "BOTTOM") - c:ClearAllPoints() - c:SetWidth(40) - c:SetPoint(top or bottom,0,top and 2 or -2) - local j - if string.match(loc,"LEFT") then - j = "LEFT" - elseif string.match(loc,"RIGHT") then - j = "RIGHT" - else - j = "CENTER" - end - c:SetJustifyH(j) - end - - if self.config.showKeyBind then - f.hotkey:Show() - else - f.hotkey:Hide() - end - - if self.config.showStackCount then - f.count:Show() - else - f.count:Hide() - end - ---[[ - if self.config.showNumericCooldown then - f.nCooldown:Show() - else - f.nCooldown:Hide() - end -]] - - if self.config.showMacroName then - f.macro:Show() - else - f.macro:Hide() - end -end - -function ReActionButton.prototype:ApplyStyle() - local f = self.frames - -- for now, just a static style - f.hotkey:SetFontObject(NumberFontNormal) - f.count:SetFontObject(NumberFontNormalYellow) -end - - - --- start/stop flashing -function ReActionButton.prototype:StartFlash() - self.flashing = true - self.flashtime = 0 - self:UpdateCheckedState() -end - -function ReActionButton.prototype:StopFlash() - self.flashing = false - self.frames.flash:Hide() - self:UpdateCheckedState() -end - -function ReActionButton.prototype:IsFlashing() - return self.flashing -end - - - - - --- set the tooltip -function ReActionButton.prototype:SetTooltip() - GameTooltip_SetDefaultAnchor(GameTooltip, self.button) +function ReAction.prototype:SetTooltip() + GameTooltip_SetDefaultAnchor(GameTooltip, self:GetActionFrame()) self:UpdateTooltip() end -function ReActionButton.prototype:ClearTooltip() +function ReAction.prototype:ClearTooltip() tooltipTime = nil GameTooltip:Hide() end - - - --- colorize the hotkey -function ReActionButton.prototype:ColorHotKey() - local action = self:GetActionID() - local c = hotKeyDefaultColor - - if action and HasAction(action) then - if IsActionInRange(action) == 0 then - c = hotKeyOutOfRangeColor - elseif self.config.keyBindColorCode then - local modKey = string.match( self.frames.hotkey:GetText() or "", "([ACS])%-") - c = modKey and hotKeyModifierColors[modKey] or c - end - else - c = hotKeyDisabledColor - end - - self.frames.hotkey:SetTextColor(c.r, c.g, c.b) -end - - - - --- display update functions -function ReActionButton.prototype:UpdateDisplay() - self:UpdateIcon() - self:UpdateHotkey() - self:UpdateCount() - self:UpdateMacroText() - self:UpdateUsable() - self:UpdateCooldown() - self:UpdateFlash() - self:UpdateEvents() - self:UpdateVisibility() - self:UpdateTooltip() -end - -function ReActionButton.prototype:UpdateIcon() - local f = self.frames - local b = self.button - - local action = self:GetActionID() - local texture = action and GetActionTexture(action) - - if action and texture then - f.icon:SetTexture(texture) - f.icon:Show() - self.rangeTimer = -1 - b:SetNormalTexture("Interface\\Buttons\\UI-Quickslot2") - else - f.icon:Hide() - f.cooldown:Hide() - self.rangeTimer = nil - b:SetNormalTexture("Interface\\Buttons\\UI-Quickslot") - end - - self:UpdateCheckedState() - - -- Add a green border if action is an equipped item - if action and IsEquippedAction(action) then - local c = equippedActionBorderColor - f.border:SetVertexColor(c.r, c.g, c.b, c.a or 1) - f.border:Show() - else - f.border:Hide() - end -end - -function ReActionButton.prototype:UpdateCheckedState() - local action = self:GetActionID() - if action and (IsCurrentAction(action) or IsAutoRepeatAction(action)) then - self.button:SetChecked(1) - else - self.button:SetChecked(0) - end -end - - -function ReActionButton.prototype:UpdateHotkey() - local action = self:GetActionID() - local b = self.button - local f = self.frames - local key = self:GetKeyBinding() - local txt = GetBindingText(key, "KEY_",1) - - -- abbreviate long key names - for pat, rep in pairs(keybindAbbreviations) do - txt = string.gsub(txt,pat,rep) - end - - if txt then - f.hotkey:SetText(string.upper(txt)) - self:ColorHotKey() - else - f.hotkey:SetText("") - end -end - -function ReActionButton.prototype:UpdateCount() - local action = self:GetActionID() - if action and (IsConsumableAction(action) or IsStackableAction(action)) then - self.frames.count:SetText(GetActionCount(action)) - else - self.frames.count:SetText("") - end -end - -function ReActionButton.prototype:UpdateMacroText() - local action = self:GetActionID() - self.frames.macro:SetText(action and GetActionText(action) or "") -end - -function ReActionButton.prototype:UpdateUsable() - local f = self.frames - local action = self:GetActionID() - local isUsable, notEnoughMana - if action then - isUsable, notEnoughMana = IsUsableAction(action) - end - if isUsable then - local c = actionUsableColor - if IsActionInRange(action) == 0 then - c = actionOutOfRangeColor - else - f.normalTexture:SetVertexColor(c.r, c.g, c.b, c.a) - end - f.icon:SetVertexColor(c.r, c.g, c.b, c.a) - elseif notEnoughMana then - local c = actionNotEnoughManaColor - f.icon:SetVertexColor(c.r, c.g, c.b, c.a) - f.normalTexture:SetVertexColor(c.r, c.g, c.b, c.a) - else - local c = actionNotUsableColor - f.icon:SetVertexColor(c.r, c.g, c.b, c.a) - f.normalTexture:SetVertexColor(1.0, 1.0, 1.0) - end -end - -function ReActionButton.prototype:UpdateCooldown() - local action = self:GetActionID() - if action then - local start, duration, enable = GetActionCooldown(self:GetActionID()) - CooldownFrame_SetTimer(self.frames.cooldown, start, duration, enable) - -- do numeric cooldown stuff here - end -end - -function ReActionButton.prototype:UpdateFlash() - local b = self.button - local action = self:GetActionID() - if action and ((IsAttackAction(action) and IsCurrentAction(action)) or IsAutoRepeatAction(action)) then - self:StartFlash() - else - self:StopFlash() - end -end - -function ReActionButton.prototype:UpdateVisibility() - local action = self:GetActionID() - local b = self.button - - if b:GetAttribute("statehidden") then - b:Hide() - elseif action and HasAction(action) then - b:GetNormalTexture():SetAlpha(1.0) - b:Show() - elseif self.showGridTmp_ > 0 or self.config.showGrid then - b:GetNormalTexture():SetAlpha(0.5) - self.frames.cooldown:Hide() - b:Show() - else - b:Hide() - end -end - -function ReActionButton.prototype:UpdateEvents() - local action = self:GetActionID() - if action and HasAction(action) then - self:RegisterActionEvents() - elseif self.actionEventsRegistered then - self:UnregisterActionEvents() - end -end - -function ReActionButton.prototype:UpdateTooltip() - local action = self:GetActionID() - if GameTooltip:IsOwned(self.button) and action and GameTooltip:SetAction(action) then - self.tooltipTime = TOOLTIP_UPDATE_TIME - else - self.tooltipTime = nil - end -end - - -
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/classes/ReAction_ActionDisplay.lua Tue Mar 20 21:25:29 2007 +0000 @@ -0,0 +1,391 @@ +-- The ReAction.ActionDisplay mixin defines 'regular' action button display functionality +-- and is an implementation of the ReAction.IDisplay and ReAction.ActionType.IDisplay interfaces. +-- +-- This Mixin assumes that it has been mixed in with a ReAction-derived class which implements +-- the ReAction.IActionType and ReAction.IColorScheme interfaces. +-- +-- This mixin uses properties of self.config to define display elements: +-- +-- self.config = { +-- keyBindLoc = "POSITION", -- keybind anchor location +-- stackCountLoc = "POSITION", -- stack count anchor location +-- showKeyBind = true/false, -- show keybind labels +-- showStackCount = true/false, -- show stack count labels +-- showMacroText = true/false, -- show macro name labels +-- showGrid = true/false, -- always show empty buttons +-- } +-- + +local AceOO = AceLibrary("AceOO-2.0") + +ReAction.ActionDisplay = AceOO.Mixin { + -- ReAction.IDisplay interface + "SetupDisplay", + "UpdateDisplay", + "TempShow", + "GetActionFrame", + "GetBaseButtonSize", + "DisplayID", + "DisplayHotkey", + + -- ReAction.ActionType.IDisplay interface + "DisplayUsable", + "DisplayEquipped", + "DisplayAutoRepeat", + "DisplayInUse", + "DisplayIcon", + "DisplayName", + "DisplayCount", + "DisplayCooldown", + + -- Event handlers + "PostClick", + "OnDragStart", + "OnReceiveDrag", + "OnEnter", + "OnLeave", + "OnUpdate", + + -- internal functions + "ApplyLayout", + "ApplyStyle", + "StartFlash", + "StopFlash", + "IsFlashing", + "DisplayVisibility", +} + +local RAAD = ReAction.ActionDisplay + + +-- private constants +local _G = getfenv(0) + +local equippedActionBorderColor = { r=0.00, g=1.00, b=0.00, a=0.35 } -- transparent green +local actionIDColor = { r=1.00, g=0.82, b=0.00, a=1.00 } -- gold + +-- private functions +-- extract and return color fields from a table, to be fed into SetVertexColor()/SetTextColor() +local function tcolor(c) + return c.r, c.g, c.b, c.a +end + + +----------------------------------- +-- Interface Implementation Methods +----------------------------------- +function RAAD:SetupDisplay( name ) + -- create the button widget + local b = CreateFrame("CheckButton", name, nil, "SecureActionButtonTemplate, ActionButtonTemplate") + + -- store references to the various sub-frames of ActionButtonTemplate so we don't have to look it up all the time + self.frames = { + button = b, + hotkey = _G[name.."HotKey"], + count = _G[name.."Count"], + cooldown = _G[name.."Cooldown"], + macro = _G[name.."Name"], + icon = _G[name.."Icon"], + border = _G[name.."Border"], + flash = _G[name.."Flash"], + normalTexture = _G[name.."NormalTexture"], + actionID = nil, -- defer creating actionID font string until it's actually requested + } + + -- ??? odd: why do we have to increment the cooldown frame level to get it to show? + -- (otherwise it's behind the icon). The default UI doesn't have to (or at least I can't + -- find where it does) but for some reason we have to here. + self.frames.cooldown:SetFrameLevel(self.frames.cooldown:GetFrameLevel() + 1) + + b:EnableMouse() + b:RegisterForDrag("LeftButton", "RightButton") + b:RegisterForClicks("AnyUp") + b:SetScript("PostClick", function(arg1) self:PostClick(arg1) end) + b:SetScript("OnDragStart", function(arg1) self:OnDragStart(arg1) end) + b:SetScript("OnReceiveDrag", function() self:OnReceiveDrag() end) + b:SetScript("OnEnter", function() self:OnEnter() end) + b:SetScript("OnLeave", function() self:OnLeave() end) + -- defer setting OnUpdate until actions are actually attached + + self.tmpShow_ = 0 +end + +function RAAD:UpdateDisplay() + self:ApplyLayout() + self:ApplyStyle() + self:DisplayVisibility() + -- refresh the action ID display + if ReAction.showIDs_ then + self:DisplayID(self:GetID()) + end +end + +function RAAD:TempShow( visible ) + visible = visible and true or false -- force data integrity + self.showTmp_ = max(0, (self.showTmp_ or 0) + (visible and 1 or -1)) + self:DisplayVisibility() +end + +function RAAD:GetActionFrame() + return self.frames.button +end + +function RAAD:GetBaseButtonSize() + return 36 +end + +function RAAD:DisplayID( id ) + local f = self.frames.actionID + if id then + if not f then + -- create the actionID label + f = self.frames.button:CreateFontString(nil,"ARTWORK","NumberFontNormalSmall") + f:SetPoint("BOTTOMLEFT") + f:SetTextColor( tcolor(actionIDColor) ) + self.frames.actionID = f + end + f:SetText(tostring(id)) + f:Show() + elseif f then + f:Hide() + end +end + +function RAAD:DisplayHotkey(txt) + self.frames.hotkey:SetText(string.upper(txt or "")) + self:UpdateUsable() +end + +function RAAD:DisplayUsable( isUsable, notEnoughMana, outOfRange ) + local f = self.frames + f.icon:SetVertexColor( self:GetIconColor(isUsable, notEnoughMana, outOfRange) ) + f.button:GetNormalTexture():SetVertexColor( self:GetBorderColor(isUsable, notEnoughMana, outOfRange) ) + f.hotkey:SetTextColor( self:GetHotkeyColor(isUsable, notEnoughMana, outOfRange, f.hotkey:GetText()) ) +end + +function RAAD:DisplayEquipped( equipped ) + local b = self.frames.border + if equipped then + b:Show() + else + b:Hide() + end +end + +function RAAD:DisplayAutoRepeat( r ) + if r then + self:StartFlash() + else + self:StopFlash() + end +end + +function RAAD:DisplayInUse( inUse ) + self.frames.button:SetChecked( inUse and 1 or 0 ) +end + +function RAAD:DisplayIcon( texture ) + local f = self.frames.button + local icon = self.frames.icon + if texture then + icon:SetTexture(texture) + icon:Show() + self.rangeTimer = -1 + f:SetNormalTexture("Interface\\Buttons\\UI-Quickslot2") + if f:GetScript("OnUpdate") == nil then + f:SetScript("OnUpdate", function(frame, elapsed) self:OnUpdate(elapsed) end) + end + else + icon:Hide() + self.rangeTimer = nil + f:SetNormalTexture("Interface\\Buttons\\UI-Quickslot") + f:SetScript("OnUpdate",nil) + end + self:DisplayVisibility() +end + +function RAAD:DisplayCooldown( start, duration, enable ) + CooldownFrame_SetTimer(self.frames.cooldown, start, duration, enable) +end + +function RAAD:DisplayName( name ) + self.frames.macro:SetText(name and tostring(name) or "") +end + +function RAAD:DisplayCount( count ) + self.frames.count:SetText(count and tostring(count) or "") +end + + + + + +---------------------- +-- Event Handlers +---------------------- +function RAAD:PostClick() + self:UpdateInUse() +end + +function RAAD:OnDragStart() + if LOCK_ACTIONBAR ~= "1" or IsShiftKeyDown() then + self:PickupAction() + end +end + +function RAAD:OnReceiveDrag() + self:PlaceAction() +end + +function RAAD:OnEnter() + self:SetTooltip() -- from ReAction base class + self.tooltipTime = TOOLTIP_UPDATE_TIME +end + +function RAAD:OnLeave() + self:ClearTooltip() -- from ReAction base class + self.tooltipTime = nil +end + +function RAAD:OnUpdate(elapsed) + -- handle flashing + if self:IsFlashing() then + self.flashtime = self.flashtime - elapsed + if self.flashtime <= 0 then + local overtime = -self.flashtime + if overtime >= ATTACK_BUTTON_FLASH_TIME then + overtime = 0 + end + self.flashtime = ATTACK_BUTTON_FLASH_TIME - overtime + + local f = self.frames.flash + if f then + if f:IsVisible() then + f:Hide() + else + f:Show() + end + end + end + end + + -- Handle range indicator + if self.rangeTimer then + self.rangeTimer = self.rangeTimer - elapsed + if self.rangeTimer <= 0 then + self:UpdateUsable() + self.rangeTimer = TOOLTIP_UPDATE_TIME + end + end + + -- handle tooltip update + if self.tooltipTime then + self.tooltipTime = self.tooltipTime - elapsed + if self.tooltipTime <= 0 then + if GameTooltip:IsOwned(self.frames.button) then + self:UpdateTooltip() + self.tooltipTime = TOOLTIP_UPDATE_TIME + else + self.tooltipTime = nil + end + end + end +end + + + +---------------------- +-- Internal methods +---------------------- + +local function placeLabel( label, anchor ) + local top = string.match(anchor,"TOP") + local bottom = string.match(anchor, "BOTTOM") + label:ClearAllPoints() + label:SetWidth(40) + label:SetPoint(top or bottom or "CENTER",0,top and 2 or bottom and -2 or 0) + local j + if string.match(anchor,"LEFT") then + j = "LEFT" + elseif string.match(anchor,"RIGHT") then + j = "RIGHT" + else + j = "CENTER" + end + label:SetJustifyH(j) +end + + +function RAAD:ApplyLayout() + local f = self.frames + + if self.config.keyBindLoc then + placeLabel(f.hotkey, self.config.keyBindLoc) + end + + if self.config.stackCountLoc then + placeLabel(f.count, self.config.stackCountLoc) + end + + if self.config.showKeyBind then + f.hotkey:Show() + else + f.hotkey:Hide() + end + + if self.config.showStackCount then + f.count:Show() + else + f.count:Hide() + end + + if self.config.showMacroName then + f.macro:Show() + else + f.macro:Hide() + end + +end + +function RAAD:ApplyStyle() + local f = self.frames + -- for now, just a static style + f.hotkey:SetFontObject(NumberFontNormal) + f.count:SetFontObject(NumberFontNormalYellow) + f.border:SetVertexColor( tcolor(equippedActionBorderColor) ) +end + +function RAAD:StartFlash() + self.flashing = true + self.flashtime = 0 +end + +function RAAD:StopFlash() + self.flashing = false + self.frames.flash:Hide() +end + +function RAAD:IsFlashing() + return self.flashing +end + +function RAAD:DisplayVisibility() + local b = self.frames.button + + if b:GetAttribute("statehidden") then + -- can't hide/show in combat + if not InCombatLockdown() then + b:Hide() + end + elseif not self:IsActionEmpty() then + b:GetNormalTexture():SetAlpha(1.0) + b:SetAlpha(1) + elseif self.showTmp_ and self.showTmp_ > 0 or self.config.showGrid then + b:GetNormalTexture():SetAlpha(0.5) + self.frames.cooldown:Hide() + b:SetAlpha(1) + else + b:SetAlpha(0) + end +end +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/classes/ReAction_ActionType.lua Tue Mar 20 21:25:29 2007 +0000 @@ -0,0 +1,361 @@ +-- The ReAction.ActionType mixin defines 'regular' action button functionality +-- and is an implementation of the ReAction.IActionType interface. +-- +-- The Mixin assumes that it is being mixed in with a ReAction-derived class +-- which implements the ReAction.IDisplay and ReAction.ActionType.IDisplay interfaces. +-- +-- This mixin using the following configuration properties: +-- +-- self.config = { +-- selfcast = nil / "alt" / "ctrl" / "shift" / "auto" / "none". nil (default) = use Interface Options menu setting. +-- } +-- + +local AceOO = AceLibrary("AceOO-2.0") + +ReAction.ActionType = AceOO.Mixin { + -- ReAction.IActionType interface + "SetID", + "GetID", + "SetupAction", + "UpdateAction", + "PickupAction", + "PlaceAction", + "IsActionEmpty", + "UpdateTooltip", + + -- event handlers + "ACTIONBAR_SLOT_CHANGED", + "PLAYER_ENTERING_WORLD", + "ACTIONBAR_SHOWGRID", + "ACTIONBAR_HIDEGRID", + "ACTIONBAR_UPDATE_STATE", + "ACTIONBAR_UPDATE_USABLE", + "UNIT_INVENTORY_CHANGED", + "CRAFT_SHOW", + "PLAYER_ENTER_COMBAT", + "PLAYER_LEAVE_COMBAT", + "START_AUTOREPEAT_SPELL", + "STOP_AUTOREPEAT_SPELL", + "OnAttributeChanged", + + -- internal functions + "UpdateSelfcast", + "UpdateIcon", + "UpdateInUse", + "UpdateCount", + "UpdateMacroText", + "UpdateUsable", + "UpdateCooldown", + "UpdateActionEvents", +} + +local RAAT = ReAction.ActionType + + +-- Required display elements +RAAT.IDisplay = AceOO.Interface { + DisplayUsable = "function", -- DisplayUsable(bool, notEnoughMana, outOfRange), change display to indicate action usable/unusable + DisplayEquipped = "function", -- DisplayEquipped(bool) + DisplayAutoRepeat = "function", -- DisplayAutoRepeat(bool) + DisplayInUse = "function", -- DisplayInUse(bool) + DisplayIcon = "function", -- DisplayIcon(texture), nil means empty slot + DisplayName = "function", -- DisplayName(text), macro names + DisplayCount = "function", -- DisplayCount(number), stack count + DisplayCooldown = "function", -- DisplayCooldown(start,duration,enable) +} + + +--------------------------------------- +-- ReAction.IActionType interface implementation +--------------------------------------- +function RAAT:SetID( id, page ) + local f = self:GetActionFrame() + id = tonumber(id) -- force data integrity + page = tonumber(page) + local button = page and ("-page"..page) or "*" + if id then + f:SetAttribute("*action"..button, id) + if page and page > 1 and self.config.selfcast == "right-click" then + -- disable right-click auto-cast + self.config.selfcast = nil + end + elseif page then + f:SetAttribute("*action"..button, ATTRIBUTE_NOOP) + end + self:UpdateSelfcast() +end + +function RAAT:GetID(page) + local f = self:GetActionFrame() + page = tonumber(page) + local button = page and ("page"..page) or SecureStateChild_GetEffectiveButton(f) + return SecureButton_GetModifiedAttribute(f, "action", button) +end + +function RAAT:SetupAction() + local f = self:GetActionFrame() + f:SetAttribute("useparent*", true) + f:SetAttribute("type", "action") + + self:RegisterEvent("PLAYER_ENTERING_WORLD") + self:RegisterEvent("ACTIONBAR_SLOT_CHANGED") + self:RegisterEvent("ACTIONBAR_SHOWGRID") + self:RegisterEvent("ACTIONBAR_HIDEGRID") + + self:UpdateSelfcast() + + f:SetScript("OnAttributeChanged", function(frame,name,value) self:OnAttributeChanged(name,value) end) +end + +function RAAT:UpdateAction() + self:UpdateIcon() + self:UpdateCount() + self:UpdateMacroText() + self:UpdateUsable() + self:UpdateCooldown() + self:UpdateActionEvents() +end + +function RAAT:PickupAction() + PickupAction(self:GetID()) + self:UpdateAction() +end + +function RAAT:PlaceAction() + if not InCombatLockdown() then + -- PlaceAction() is protected. However the user can still drop a new action + -- onto a button while in combat by dragging then clicking, because + -- UseAction() appears to swap the cursor action for the current action if + -- an action is on the cursor. + PlaceAction(self:GetID()) + end + self:UpdateActionEvents() + self:UpdateIcon() +end + +function RAAT:IsActionEmpty() + local slot = self:GetID() + return not(slot and HasAction(slot)) +end + +function RAAT:UpdateTooltip() + local action = self:GetID() + if action and GameTooltip:IsOwned(self:GetActionFrame()) then + GameTooltip:SetAction(action) + end +end + + + + + + + +----------------------------- +-- Event Handling +----------------------------- +function RAAT:ACTIONBAR_SLOT_CHANGED(slot) + if slot == 0 or slot == self:GetID() then + self:UpdateAction() + end +end + +function RAAT:PLAYER_ENTERING_WORLD() + self:UpdateAction() +end + +function RAAT:ACTIONBAR_SHOWGRID() + self:TempShow(true) +end + +function RAAT:ACTIONBAR_HIDEGRID() + self:TempShow(false) +end + +function RAAT:ACTIONBAR_UPDATE_STATE() + self:UpdateInUse() +end + +function RAAT:ACTIONBAR_UPDATE_USABLE() + self:UpdateUsable() + self:UpdateCooldown() +end + +function RAAT:UNIT_INVENTORY_CHANGED(unit) + if unit == "player" then + self:UpdateIcon() + self:UpdateCount() + end +end + +function RAAT:CRAFT_SHOW() + self:UpdateInUse() +end + +function RAAT:PLAYER_ENTER_COMBAT() + if IsAttackAction(self:GetID()) then + self:DisplayAutoRepeat(true) + self:UpdateInUse() + end +end + +function RAAT:PLAYER_LEAVE_COMBAT() + if IsAttackAction(self:GetID()) then + self:DisplayAutoRepeat(false) + self:UpdateInUse() + end +end + +function RAAT:START_AUTOREPEAT_SPELL() + if IsAutoRepeatAction(self:GetID()) then + self:DisplayAutoRepeat(true) + end +end + +function RAAT:STOP_AUTOREPEAT_SPELL() + if not IsAttackAction(self:GetID()) then + self:DisplayAutoRepeat(false) + self:UpdateInUse() + end +end + +function RAAT:OnAttributeChanged(name, value) + if self.config then + self:UpdateAction() + self:UpdateDisplay() + end +end + + + + +--------------------------------- +-- Internal methods +--------------------------------- +function RAAT:UpdateSelfcast() + if not InCombatLockdown() then + local c = self.config and self.config.selfcast + local f = self:GetActionFrame() + + f:SetAttribute("alt-unit*",nil) + f:SetAttribute("ctrl-unit*",nil) + f:SetAttribute("shift-unit*",nil) + f:SetAttribute("*unit2",nil) + if c == nil then + f:SetAttribute("unit",nil) + f:SetAttribute("checkselfcast", true) + else + f:SetAttribute("checkselfcast",ATTRIBUTE_NOOP) + f:SetAttribute("unit","none") -- "none" gives you the glowing cast hand if no target selected, or casts on target if target selected + if c == "none" then + -- nothing to do + elseif c == "alt" or c == "ctrl" or c == "shift" then + f:SetAttribute(c.."-unit*","player") + elseif c == "right-click" then + if f:GetAttribute("*action-page2") then + -- right-click modifier not supported with multipage + self.config.selfcast = nil + f:SetAttribute("unit",nil) + f:SetAttribute("checkselfcast",true) + else + f:SetAttribute("*unit2","player") + end + end + end + end +end + +function RAAT:UpdateIcon() + local action = self:GetID() + local texture = action and GetActionTexture(action) + + self:DisplayIcon(action and texture) + self:DisplayEquipped(action and IsEquippedAction(action)) + self:UpdateInUse() + self:UpdateUsable() + self:UpdateCooldown() +end + +function RAAT:UpdateInUse() + local action = self:GetID() + if action and (IsCurrentAction(action) or IsAutoRepeatAction(action)) then + self:DisplayInUse(true) + else + self:DisplayInUse(false) + end +end + +function RAAT:UpdateCount() + local action = self:GetID() + if action and (IsConsumableAction(action) or IsStackableAction(action)) then + self:DisplayCount(GetActionCount(action)) -- will display a 0 if none remaining + else + self:DisplayCount(nil) -- will display nothing + end +end + +function RAAT:UpdateMacroText() + local action = self:GetID() + self:DisplayName(action and GetActionText(action)) +end + +function RAAT:UpdateUsable() + local action = self:GetID() + if action and HasAction(action) then + local isUsable, notEnoughMana = IsUsableAction(action) + local outOfRange = IsActionInRange(action) == 0 + self:DisplayUsable(isUsable and not outOfRange, notEnoughMana, outOfRange) + else + self:DisplayUsable(false, false, false) + end +end + +function RAAT:UpdateCooldown() + local action = self:GetID() + if action then + self:DisplayCooldown(GetActionCooldown(action)) + end +end + +function RAAT:UpdateActionEvents() + local action = self:GetID() + if action and HasAction(action) then + if not self.actionEventsRegistered then + self:RegisterEvent("ACTIONBAR_UPDATE_STATE") + self:RegisterEvent("ACTIONBAR_UPDATE_USABLE") + self:RegisterEvent("ACTIONBAR_UPDATE_COOLDOWN", "ACTIONBAR_UPDATE_USABLE") + self:RegisterEvent("UPDATE_INVENTORY_ALERTS", "ACTIONBAR_UPDATE_USABLE") + self:RegisterEvent("PLAYER_AURAS_CHANGED", "ACTIONBAR_UPDATE_USABLE") + self:RegisterEvent("PLAYER_TARGET_CHANGED", "ACTIONBAR_UPDATE_USABLE") + self:RegisterEvent("UNIT_INVENTORY_CHANGED") + self:RegisterEvent("CRAFT_SHOW") + self:RegisterEvent("CRAFT_CLOSE", "CRAFT_SHOW") + self:RegisterEvent("TRADE_SKILL_SHOW", "CRAFT_SHOW") + self:RegisterEvent("TRADE_SKILL_CLOSE", "CRAFT_SHOW") + self:RegisterEvent("PLAYER_ENTER_COMBAT") + self:RegisterEvent("PLAYER_LEAVE_COMBAT") + self:RegisterEvent("START_AUTOREPEAT_SPELL") + self:RegisterEvent("STOP_AUTOREPEAT_SPELL") + self.actionEventsRegistered = true + end + elseif self.actionEventsRegistered then + self:UnregisterEvent("ACTIONBAR_UPDATE_STATE") + self:UnregisterEvent("ACTIONBAR_UPDATE_USABLE") + self:UnregisterEvent("ACTIONBAR_UPDATE_COOLDOWN") + self:UnregisterEvent("UPDATE_INVENTORY_ALERTS") + self:UnregisterEvent("PLAYER_AURAS_CHANGED") + self:UnregisterEvent("PLAYER_TARGET_CHANGED") + self:UnregisterEvent("UNIT_INVENTORY_CHANGED") + self:UnregisterEvent("CRAFT_SHOW") + self:UnregisterEvent("CRAFT_CLOSE") + self:UnregisterEvent("TRADE_SKILL_SHOW") + self:UnregisterEvent("TRADE_SKILL_CLOSE") + self:UnregisterEvent("PLAYER_ENTER_COMBAT") + self:UnregisterEvent("PLAYER_LEAVE_COMBAT") + self:UnregisterEvent("START_AUTOREPEAT_SPELL") + self:UnregisterEvent("STOP_AUTOREPEAT_SPELL") + self.actionEventsRegistered = false + end +end +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/classes/ReAction_ColorScheme.lua Tue Mar 20 21:25:29 2007 +0000 @@ -0,0 +1,107 @@ +-- ReAction.IColorScheme is an interface to describe color schemes for hotkey labels and +-- icon/border colorization, depending on the current state. +-- + +local AceOO = AceLibrary("AceOO-2.0") + +ReAction.IColorScheme = AceOO.Interface { + GetHotkeyColor = "function", -- r,g,b,a = GetHotkeyColor( isUsable, notEnoughMana, outOfRange, [bindingText] ) + GetIconColor = "function", -- r,g,b,a = GetIconColor( isUsable, notEnoughMana, outOfRange ) + GetBorderColor = "function", -- r,g,b,a = GetBorderColor( isUsable, notEnoughMana, outOfRange ) +} + + + +-- ReAction.DefaultColorScheme is a Mixin implementation of ReAction.IColorScheme which +-- supports the standard Blizzard colorization plus optional hotkey modifier-driven colorization +-- and colorizing icons red (in addition to hotkeys) when out of range. +-- +-- DefaultColorScheme makes use of self.config as follows: +-- +-- self.config = { +-- keyBindColorCode = true/false, -- color-code keybindings based on modifier key +-- } + + +ReAction.DefaultColorScheme = AceOO.Mixin { + "GetHotkeyColor", + "GetModifiedHotkeyColor", -- returns the default modified color (if configured) + "GetIconColor", + "GetBorderColor" +} + + +-- private variables +local hotKeyDefaultColor = { r=1.00, g=1.00, b=1.00, a=1.00 } -- white +local hotKeyDisabledColor = { r=0.60, g=0.60, b=0.60, a=1.00 } -- 60% gray +local hotKeyOutOfRangeColor = { r=1.00, g=0.20, b=0.20, a=1.00 } -- red + +local actionUsableColor = { r=1.00, g=1.00, b=1.00, a=1.00 } -- white +local actionNotUsableColor = { r=0.40, g=0.40, b=0.40, a=1.00 } -- 40% gray +local actionNotEnoughManaColor = { r=0.20, g=0.20, b=0.70, a=1.00 } -- medium blue +local actionOutOfRangeColor = { r=1.00, g=0.20, b=0.20, a=1.00 } -- red + +local hotKeyModifierColors = { + S = { r=0.60, g=0.60, b=1.00, a=1.00 }, -- shift (blue) + C = { r=1.00, g=0.82, b=0.00, a=1.00 }, -- ctrl (gold) + A = { r=0.10, g=1.00, b=0.10, a=1.00 }, -- alt (green) + M = { r=0.90, g=0.30, b=1.00, a=1.00 }, -- mouse (purple) +} + +-- build list of modifier keys (as a string) from table above +local hotKeyModifiers = "" +for k, _ in pairs(hotKeyModifierColors) do + hotKeyModifiers = hotKeyModifiers..k +end + +-- private functions +-- extract and return color fields from a table, to be fed into SetVertexColor()/SetTextColor() +local function tcolor(c) + return c.r, c.g, c.b, c.a +end + + + +-- mixin methods + +local RADCS = ReAction.DefaultColorScheme + +function RADCS:GetHotkeyColor( isUsable, notEnoughMana, outOfRange, bindingTxt ) + if isUsable or notEnoughMana then + return tcolor(self:GetModifiedHotkeyColor(bindingTxt)) + elseif outOfRange then + return tcolor(hotKeyOutOfRangeColor) + else + return tcolor(hotKeyDisabledColor) + end +end + +function RADCS:GetModifiedHotkeyColor(txt) + local c = hotKeyDefaultColor + if txt and self.config.keyBindColorCode then + local modKey = string.match( txt or "", "(["..hotKeyModifiers.."])%-") + c = modKey and hotKeyModifierColors[modKey] or c + end + return c +end + +function RADCS:GetIconColor( isUsable, notEnoughMana, outOfRange ) + if isUsable then + return tcolor(actionUsableColor) + elseif notEnoughMana then + return tcolor(actionNotEnoughManaColor) + elseif outOfRange then + return tcolor(actionOutOfRangeColor) + else + return tcolor(actionNotUsableColor) + end +end + +function RADCS:GetBorderColor( isUsable, notEnoughMana, outOfRange ) + if isUsable or notEnoughMana or outOfRange then + return tcolor(actionUsableColor) + else + return tcolor(actionNotUsableColor) + end +end +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/classes/ReAction_PetActionDisplay.lua Tue Mar 20 21:25:29 2007 +0000 @@ -0,0 +1,322 @@ +-- The ReAction.PetActionDisplay mixin defines 'regular' action button display functionality +-- and is an implementation of the ReAction.IDisplay and ReAction.ActionType.IDisplay interfaces. +-- +-- This Mixin assumes that it has been mixed in with a ReAction-derived class which implements +-- the ReAction.IActionType and ReAction.IColorScheme interfaces. +-- +-- This mixin uses properties of self.config to define display elements: +-- +-- self.config = { +-- keyBindLoc = "POSITION", -- keybind anchor location +-- showKeyBind = true/false, -- show keybind labels +-- showGrid = true/false, -- always show empty buttons +-- } +-- + +local AceOO = AceLibrary("AceOO-2.0") + +ReAction.PetActionDisplay = AceOO.Mixin { + -- ReAction.IDisplay interface + "SetupDisplay", + "UpdateDisplay", + "TempShow", + "GetActionFrame", + "GetBaseButtonSize", + "DisplayID", + "DisplayHotkey", + + -- ReAction.PetActionType.IDisplay interface + "DisplayAutoCast", + "DisplayAutoCastable", + "DisplayInUse", + "DisplayUsable", + "DisplayIcon", + "DisplayCooldown", + + -- Event handlers + "PreClick", + "PostClick", + "OnDragStart", + "OnReceiveDrag", + "OnEnter", + "OnLeave", + + -- internal functions + "ApplyLayout", + "ApplyStyle", + "DisplayVisibility", +} + +local RAPAD = ReAction.PetActionDisplay + + +-- private constants +local _G = getfenv(0) + +local actionIDColor = { r=1.00, g=0.82, b=0.00, a=1.00 } -- gold + + +-- private functions +-- extract and return color fields from a table, to be fed into SetVertexColor()/SetTextColor() +local function tcolor(c) + return c.r, c.g, c.b, c.a +end + + +----------------------------------- +-- Interface Implementation Methods +----------------------------------- +function RAPAD:SetupDisplay( name ) + -- create the button widget + local b = CreateFrame("CheckButton", name, nil, "ActionButtonTemplate, SecureActionButtonTemplate") + b:EnableMouse() + b:RegisterForDrag("LeftButton", "RightButton") + b:RegisterForClicks("AnyUp") + b:SetScript("PreClick", function(arg1) self:PreClick(arg1) end) + b:SetScript("PostClick", function(arg1) self:PostClick(arg1) end) + b:SetScript("OnDragStart", function(arg1) self:OnDragStart(arg1) end) + b:SetScript("OnReceiveDrag", function() self:OnReceiveDrag() end) + b:SetScript("OnEnter", function() self:OnEnter() end) + b:SetScript("OnLeave", function() self:OnLeave() end) + + + b:SetWidth(30) + b:SetHeight(30) + + local tx = b:GetNormalTexture() + tx:ClearAllPoints() + tx:SetWidth(54) + tx:SetHeight(54) + tx:SetPoint("CENTER",0,-1) + + local autoCastable = b:CreateTexture(nil, "OVERLAY") + autoCastable:SetTexture("Interface\\Buttons\\UI-AutoCastableOverlay") + autoCastable:SetWidth(58) + autoCastable:SetHeight(58) + autoCastable:SetPoint("CENTER") + autoCastable:Hide() + + local autoCast = CreateFrame("Model",nil,b) + autoCast:SetModel("Interface\\Buttons\\UI-AutoCastButton.mdx") + autoCast:SetScale(1.2) + autoCast:SetAllPoints() + autoCast:SetSequence(0) + autoCast:SetSequenceTime(0,0) + autoCast:SetFrameStrata("HIGH") -- Otherwise it won't appear on top of the icon for some reason + autoCast:Hide() + + + local cd = _G[name.."Cooldown"] + cd:SetScale(1); + cd:ClearAllPoints(); + cd:SetWidth(33); + cd:SetHeight(33); + cd:SetPoint("CENTER", -2, -1); + + -- store references to the various sub-frames of ActionButtonTemplate so we don't have to look it up all the time + self.frames = { + button = b, + autoCastable = autoCastable, --_G[name.."AutoCastable"], + autoCast = autoCast, --_G[name.."AutoCast"], + hotkey = _G[name.."HotKey"], + cooldown = cd, + icon = _G[name.."Icon"], + normalTexture = tx, --_G[name.."NormalTexture2"], + actionID = nil, -- defer creating actionID font string until it's actually requested + } + + self.tmpShow_ = 0 +end + +function RAPAD:UpdateDisplay() + self:ApplyLayout() + self:ApplyStyle() + self:DisplayVisibility() + -- refresh the action ID display + if ReAction.showIDs_ then + self:DisplayID(self:GetID()) + end +end + +function RAPAD:TempShow( visible ) + visible = visible and true or false -- force data integrity + self.showTmp_ = max(0, (self.showTmp_ or 0) + (visible and 1 or -1)) + self:DisplayVisibility() +end + +function RAPAD:GetActionFrame() + return self.frames.button +end + +function RAPAD:GetBaseButtonSize() + return 30 +end + +function RAPAD:DisplayID( id ) + local f = self.frames.actionID + if id then + if not f then + -- create the actionID label + f = self.frames.button:CreateFontString(nil,"ARTWORK","NumberFontNormalSmall") + f:SetPoint("BOTTOMLEFT") + f:SetTextColor( tcolor(actionIDColor) ) + self.frames.actionID = f + end + f:SetText(tostring(id)) + f:Show() + elseif f then + f:Hide() + end +end + +function RAPAD:DisplayHotkey(txt) + self.frames.hotkey:SetText(string.upper(txt or "")) +end + +function RAPAD:DisplayInUse( inUse ) + self.frames.button:SetChecked( inUse and 1 or 0 ) +end + +function RAPAD:DisplayUsable( usable ) + SetDesaturation(self.frames.icon, (not usable) and 1 or nil) -- argument is backward + self.frames.hotkey:SetTextColor(self:GetHotkeyColor(usable, false, false, self.frames.hotkey:GetText()) ) +end + +function RAPAD:DisplayAutoCast( show ) + if show then + self.frames.autoCast:Show() + else + self.frames.autoCast:Hide() + end +end + +function RAPAD:DisplayAutoCastable( show ) + if show then + self.frames.autoCastable:Show() + else + self.frames.autoCastable:Hide() + end +end + +function RAPAD:DisplayIcon( texture ) + local f = self.frames.button + local icon = self.frames.icon + if texture then + icon:SetTexture(texture) + icon:Show() + f:SetNormalTexture("Interface\\Buttons\\UI-Quickslot2") + else + f:SetScript("OnUpdate",nil) + icon:Hide() + f:SetNormalTexture("Interface\\Buttons\\UI-Quickslot") + end + self:DisplayVisibility() +end + +function RAPAD:DisplayCooldown( start, duration, enable ) + CooldownFrame_SetTimer(self.frames.cooldown, start, duration, enable) +end + + + + + + +---------------------- +-- Event Handlers +---------------------- +function RAPAD:PreClick() + -- not sure why we have to do this + self.frames.button:SetChecked(0) +end + +function RAPAD:PostClick() + self:UpdateAction() +end + +function RAPAD:OnDragStart() + if LOCK_ACTIONBAR ~= "1" or IsShiftKeyDown() then + self:PickupAction() + end +end + +function RAPAD:OnReceiveDrag() + self:PlaceAction() +end + +function RAPAD:OnEnter() + self:SetTooltip() -- from ReAction base class +end + +function RAPAD:OnLeave() + self:ClearTooltip() -- from ReAction base class +end + + + +---------------------- +-- Internal methods +---------------------- + +local function placeLabel( label, anchor ) + local top = string.match(anchor,"TOP") + local bottom = string.match(anchor, "BOTTOM") + label:ClearAllPoints() + label:SetWidth(40) + label:SetPoint(top or bottom,0,top and 2 or -2) + local j + if string.match(anchor,"LEFT") then + j = "LEFT" + elseif string.match(anchor,"RIGHT") then + j = "RIGHT" + else + j = "CENTER" + end + label:SetJustifyH(j) +end + + +function RAPAD:ApplyLayout() + local f = self.frames + + if self.config.keyBindLoc then + placeLabel(f.hotkey, self.config.keyBindLoc) + end + + if self.config.showKeyBind then + f.hotkey:Show() + else + f.hotkey:Hide() + end + + if self.config.showBorder then + f.normalTexture:SetAlpha(1) + else + f.normalTexture:SetAlpha(0) + end +end + +function RAPAD:ApplyStyle() + +end + +function RAPAD:DisplayVisibility() + local b = self.frames.button + + -- can't hide/show in combat + if not InCombatLockdown() then + if b:GetAttribute("statehidden") then + b:Hide() + elseif not self:IsActionEmpty() then + b:GetNormalTexture():SetAlpha(1.0) + b:Show() + elseif self.showTmp_ and self.showTmp_ > 0 or self.config.showGrid then + b:GetNormalTexture():SetAlpha(0.5) + self.frames.cooldown:Hide() + b:Show() + else + b:Hide() + end + end +end +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/classes/ReAction_PetActionType.lua Tue Mar 20 21:25:29 2007 +0000 @@ -0,0 +1,222 @@ +-- The ReAction.PetActionType mixin defines Pet action button functionality +-- and is an implementation of the ReAction.IActionType interface. +-- +-- The Mixin assumes that it is being mixed in with a ReAction-derived class +-- which implements the ReAction.IDisplay and ReAction.PetActionType.IDisplay interfaces. + +local AceOO = AceLibrary("AceOO-2.0") + +ReAction.PetActionType = AceOO.Mixin { + -- ReAction.IDisplay interface + "SetID", + "GetID", + "SetupAction", + "UpdateAction", + "PickupAction", + "PlaceAction", + "IsActionEmpty", + "UpdateTooltip", + + -- Event handlers + "PLAYER_ENTERING_WORLD", + "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_SHOWGRID", + "PET_BAR_HIDEGRID", + + -- Internal functions + "UpdateCooldown", +} + +local RAPAT = ReAction.PetActionType + +-- Required display elements +RAPAT.IDisplay = AceOO.Interface { + DisplayAutoCast = "function", -- DisplayAutoCast(bool) + DisplayAutoCastable = "function", -- DisplayAutoCastable(bool) + DisplayIcon = "function", -- DisplayIcon(texture), Display the icon texture (nil for empty slot) + DisplayCooldown = "function", -- DisplayCooldown(start, duration, enable), display cooldown timer + DisplayInUse = "function", -- DisplayInUse(bool), display whether the action is in use + DisplayUsable = "function", -- DisplayUsable(bool), display whether the action can be used now +} + + +-- private constants +local actionIDColor = { r=1.00, g=0.82, b=0.00, a=1.00 } -- gold + +-- private functions +-- extract and return color fields from a table, to be fed into SetVertexColor()/SetTextColor() +local function tcolor(c) + return c.r, c.g, c.b, c.a +end + +--------------------------------------- +-- ReAction.IActionType interface implementation +--------------------------------------- +function RAPAT:SetID( id ) -- paging not supported + id = tonumber(id) -- force data integrity + if id then + self.config.ids[self.barIdx] = { id } + local f = self:GetActionFrame() + f:SetAttribute("action",id) + -- the following is the goofy hack to work around Blizzard not exporting the pet functions securely + f:SetAttribute("clickbutton2",getglobal("PetActionButton"..id)) + end +end + +function RAPAT:GetID() + return SecureButton_GetModifiedAttribute(self:GetActionFrame(), "action", button) +end + +function RAPAT:SetupAction() + local b = self:GetActionFrame() + -- Blizzard didn't support TogglePetAutocast functionality in + -- SecureButton_OnClick(), so we have to spoof it + -- by delegating right-clicks to the hidden default action buttons + b:SetAttribute("type", "pet") + b:SetAttribute("type2", "click") + + -- shift-clicking to drag locked buttons off + b:SetAttribute("checkselfcast", true) + b:SetAttribute("useparent-unit", true) + + self:RegisterEvent("PLAYER_ENTERING_WORLD") + self:RegisterEvent("PLAYER_CONTROL_LOST"); + self:RegisterEvent("PLAYER_CONTROL_GAINED"); + self:RegisterEvent("PLAYER_FARSIGHT_FOCUS_CHANGED"); + self:RegisterEvent("UNIT_PET"); + self:RegisterEvent("UNIT_FLAGS"); + self:RegisterEvent("UNIT_AURA"); + self:RegisterEvent("PET_BAR_UPDATE"); + self:RegisterEvent("PET_BAR_UPDATE_COOLDOWN"); + self:RegisterEvent("PET_BAR_SHOWGRID"); + self:RegisterEvent("PET_BAR_HIDEGRID"); + + self:UpdateAction() +end + +function RAPAT:UpdateAction() + local id = self:GetID() + if id then + local name, subtext, texture, isToken, isActive, autoCastAllowed, autoCastEnabled = GetPetActionInfo(id); + self:DisplayIcon( isToken and getglobal(texture) or texture ) + self:DisplayInUse( isActive ) + self:DisplayAutoCastable( autoCastAllowed ) + self:DisplayAutoCast( autoCastEnabled ) + self:DisplayCooldown( GetPetActionCooldown(id) ) + self:DisplayUsable(GetPetActionsUsable()) + -- If it's a 'token' save away the tooltip name for updateTooltip + self.isToken = isToken + if isToken then + self.tooltipName = getglobal(name) + self.tooltipSubtext = subtext + end + end +end + +function RAPAT:PickupAction() + PickupPetAction(self:GetID()) + self:UpdateAction() +end + +function RAPAT:PlaceAction() + if not InCombatLockdown() then + PlacePetAction(self:GetID()) + end + self:UpdateAction() +end + +function RAPAT:IsActionEmpty() + local id = self:GetID() + return not(id and GetPetActionInfo(id)) +end + +function RAPAT:UpdateTooltip() + local id = self:GetID() + if GameTooltip:IsOwned(self:GetActionFrame()) and id then + if self.isToken then + GameTooltip:SetText(self.tooltipName, 1.0, 1.0, 1.0) + if ( self.tooltipSubtext ) then + GameTooltip:AddLine(self.tooltipSubtext, "", 0.5, 0.5, 0.5); + end + GameTooltip:Show(); + else + if GameTooltip:SetPetAction(id) then + self.tooltipTime = TOOLTIP_UPDATE_TIME + end + end + else + self.tooltipTime = nil + end +end + + +----------------------------- +-- Event Handling +----------------------------- +function RAPAT:PLAYER_ENTERING_WORLD() + self:UpdateAction() +end + +function RAPAT:PLAYER_CONTROL_LOST() + self:UpdateAction() +end + +function RAPAT:PLAYER_CONTROL_GAINED() + self:UpdateAction() +end + +function RAPAT:PLAYER_FARSIGHT_FOCUS_CHANGED() + self:UpdateAction() +end + +function RAPAT:UNIT_PET(unit) + if unit == "player" then + self:UpdateAction() + end +end + +function RAPAT:UNIT_FLAGS(unit) + if unit == "pet" then + self:UpdateAction() + end +end + +function RAPAT:UNIT_AURA(unit) + if unit == "pet" then + self:UpdateAction() + end +end + +function RAPAT:PET_BAR_UPDATE() + self:UpdateAction() +end + +function RAPAT:PET_BAR_UPDATE_COOLDOWN() + self:UpdateCooldown() +end + +function RAPAT:PET_BAR_SHOWGRID() + self:TempShow(true) +end + +function RAPAT:PET_BAR_HIDEGRID() + self:TempShow(false) +end + + +------------------------------------- +-- Internal functions +------------------------------------- +function RAPAT:UpdateCooldown() + local id = self:GetID() + if id then + self:DisplayCooldown( GetPetActionCooldown(id) ) + end +end
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/classes/ReAnchor.lua Tue Mar 20 21:25:29 2007 +0000 @@ -0,0 +1,314 @@ +-- +-- ReAnchor.lua +-- +-- Provides drag-placement facilities for frames. +-- + +-- local constants +local AceOO = AceLibrary("AceOO-2.0") + +local edges = { "BOTTOM", "TOP", "LEFT", "RIGHT" } + +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 oppositePoints = { + BOTTOMLEFT = "TOPRIGHT", + BOTTOM = "TOP", + BOTTOMRIGHT = "TOPLEFT", + RIGHT = "LEFT", + TOPRIGHT = "BOTTOMLEFT", + TOP = "BOTTOM", + TOPLEFT = "BOTTOMRIGHT", + LEFT = "RIGHT", + 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 0, 0 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 utility functions + +-- 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) + 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 + +-- Returns interior offsets (specified absolutely) from a point +local function GetInteriorOffsetsToPoint(p, x, y) + return insidePointOffsetFuncs[p](x,y) +end + + + + + +-- ReAnchor is a Mixin which provides some anchoring and +-- placement methods for frames. +-- An object with the ReAnchor mixin must support the +-- IAnchorable interface (implicitly or explicitly). +-- The mixin methods also require arguments to support +-- that interface. + +-- In the method prototypes, 'IRObjs' is used to refer to a +-- table of objects which support the IAnchorable interface. + + +ReAnchor = AceOO.Mixin { + "GetClosestVisibleEdge", + "GetClosestVisiblePoint", + "GetClosestPointSnapped", + "DisplaySnapIndicator", + "HideSnapIndicator", +} + +--------------------------------------------------------- +-- Constants and classes that are not exported via mixin +--------------------------------------------------------- +ReAnchor.IAnchorable = AceOO.Interface { + GetFrame = "function", + GetAnchorage = "function", -- return ReAnchor.anchorInside or .anchorOutside +} + +ReAnchor.anchorInside = { inside = true } +ReAnchor.anchorOutside = { outside = true } + +ReAnchor.snapIndicator1 = CreateFrame("Frame",nil,UIParent,"ReAnchorSnapIndicatorTemplate") +ReAnchor.snapIndicator2 = CreateFrame("Frame",nil,UIParent,"ReAnchorSnapIndicatorTemplate") + + + + +-------------------- +-- Mixin methods +-------------------- + +-- returns: +-- (1) o : the closest IRObj +-- (2) e1 : the point (edge) on self:GetFrame() +-- (3) e2 : the point (edge) on o:GetFrame() +function ReAnchor:GetClosestVisibleEdge( IRObjs ) + local f1 = self:GetFrame() + local r, o, e1, e2 + for _, o2 in pairs(IRObjs) do + local f2 = o2:GetFrame() + local a = o2:GetAnchorage() + if f2:IsVisible() and CheckAnchorable(f1,f2) then + for _, e in pairs(edges) do + local opp = a.inside and e or oppositePoints[e] + if CheckEdgeOverlap(f1,f2,e) then + local d = GetEdgeDistance(f1, f2, e, opp) + if not r or d < r then + r, o, e1, e2 = d, o2, e, opp + end + end + end + end + end + return o, e1, e2 +end + +-- returns: +-- (1) o: the closest IRObj +-- (1) p: the point on self:GetFrame() +-- (2) rp: the relativePoint on o:GetFrame() +-- (3) x: x offset +-- (4) y: y offset +-- such that self:GetFrame():SetPoint(p,o:GetFrame(),rp,x,y) preserves the current location +function ReAnchor:GetClosestVisiblePoint( IRObjs ) + local f1 = self:GetFrame() + local o, e1, e2 = self:GetClosestVisibleEdge( IRObjs ) + local f2 = o:GetFrame() + 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 + local p2 = o:GetAnchorage().outside and oppositePoints[p1] or p1 + 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 + return o, p, rp, x, y +end + + +-- Calls self:GetClosestVisiblePoint() and then snaps to the specified +-- offsets if within the given range in x and y (as appropriate) +-- Return semantic is the same as GetClosestVisiblePoint(). Returns nil +-- if no snap can be done. +function ReAnchor:GetClosestPointSnapped(IRObjs, r, xOff, yOff) + local f1 = self:GetFrame() + local o, p, rp, x, y = self:GetClosestVisiblePoint(IRObjs) + local s = false + + if r then + local sx, sy = GetInteriorOffsetsToPoint(p, xOff or 0, yOff or 0) + local xx, yy = pointCoordFuncs[p](f1) + if xx and yy then + if math.abs(x) <= r then + x = sx + s = true + end + if math.abs(y) <= r then + y = sy + s = true + end + elseif xx then + if math.abs(x) <= r then + x = sx + s = true + if math.abs(y) <= r then + y = sy + end + end + elseif yy then + if math.abs(y) <= r then + y = sy + s = true + if math.abs(x) <= r then + x = sx + end + end + end + end + + if s then + return o, p, rp, x, y + end +end + + + +-- shows anchor-indicators on the associated frame and the target frame +-- when a snap is warranted. +function ReAnchor:DisplaySnapIndicator( IRObjs, r, xOff, yOff ) + local o, p, rp, x, y, snap = self:GetClosestPointSnapped(IRObjs, r, xOff, yOff) + local si1 = ReAnchor.snapIndicator1 + local si2 = ReAnchor.snapIndicator2 + if o then + si1:ClearAllPoints() + si2:ClearAllPoints() + si1:SetPoint("CENTER", self:GetFrame(), p, 0, 0) + local xx, yy = pointCoordFuncs[rp](o:GetFrame()) + x = math.abs(x) <=r and xx and 0 or x + y = math.abs(y) <=r and yy and 0 or y + si2:SetPoint("CENTER", o:GetFrame(), rp, x, y) + si1:Show() + si2:Show() + else + if si1:IsVisible() then + si1:Hide() + si2:Hide() + end + end +end + + +function ReAnchor:HideSnapIndicator() + local si1 = ReAnchor.snapIndicator1 + local si2 = ReAnchor.snapIndicator2 + if si1:IsVisible() then + si1:Hide() + si2:Hide() + end +end +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/classes/ReAnchor.xml Tue Mar 20 21:25:29 2007 +0000 @@ -0,0 +1,18 @@ +<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"> + + <Frame name="ReAnchorSnapIndicatorTemplate" virtual="true" frameStrata="HIGH" enableMouse="false" hidden="true"> + <Size> + <AbsDimension x="8" y="8"/> + </Size> + <Layers> + <Layer level="OVERLAY"> + <Texture alphaMode="ADD"> + <Color r="1.0" g="0.82" b="0" a="0.8"/> + </Texture> + </Layer> + </Layers> + </Frame> + +</Ui>
--- a/classes/ReBar.lua Tue Mar 20 21:20:20 2007 +0000 +++ b/classes/ReBar.lua Tue Mar 20 21:25:29 2007 +0000 @@ -1,55 +1,155 @@ +-- private constants +local anchoredLabelColor = { r = 1.0, g = 0.82, b = 0.0 } +local nonAnchoredLabelColor = { r = 1.0, g = 1.0, b = 1.0 } --- private constants -local insideFrame = 1 -local outsideFrame = 2 +local DRAG_UPDATE_RATE = 0.125 -- cap at 8 Hz -local pointFindTable = { - 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, +local nStancePages = { + WARRIOR = 3, + ROGUE = 0, + HUNTER = 0, + DRUID = 3, -- updated to 4 if talented + PALADIN = 0, + SHAMAN = 0, -- As far as I know, ghost wolf is not a proper shapeshift form, it's just a buff + MAGE = 0, + PRIEST = 0, -- updated to 1 if talented + WARLOCK = 0, } -local oppositePointTable = { - BOTTOMLEFT = "TOPRIGHT", - BOTTOM = "TOP", - BOTTOMRIGHT = "TOPLEFT", - RIGHT = "LEFT", - TOPRIGHT = "BOTTOMLEFT", - TOP = "BOTTOM", - TOPLEFT = "BOTTOMRIGHT", - LEFT = "RIGHT" +local stanceMaps = { + WARRIOR = { + -- note: warriors are never in shapeshift form 0. + [1] = "*:1", -- battle stance. All states go to state (page) 1. + [2] = "*:2", -- defensive stance. All states go to state (page) 2. + [3] = "*:3", -- berserker stance. All states go to state (page) 3. + }, + ROGUE = {}, + HUNTER = {}, + DRUID = { + [0] = "*:1", -- humanoid form. All states go to state (page) 1. + [1] = "*:2", -- bear/dire bear form. All states go to state (page) 2. + [2] = "*:1", -- aquatic form. All states to go state (page) 1 (same as humanoid) + [3] = "*:3", -- cat form. All states go to state (page) 3 + [4] = "*:1", -- travel form. All states go to state (page) 1 (same as humanoid) + [5] = "*:4", -- oomkin/tree form [talent only]. All states go to state (page) 4 + [6] = "*:1", -- flight form. All states go to state (page) 1 (same as humanoid) (??? How does this work with oomkin/tree?) + }, + PALADIN = {}, + SHAMAN = {}, + MAGE = {}, + PRIEST = { + [0] = "*:1", -- normal form. All states go to page 1. + [1] = "*:2", -- shadowform (talent only). All states go to page 2. + }, + WARLOCK = {}, } -local anchoredLabelColor = { r =0.6, g = 0.2, b = 1.0 } -local nonAnchoredLabelColor = { r = 1.0, g = 0.82, b = 0.0 } - --- private variables -local stickyTargets = { - [UIParent] = insideFrame, - [WorldFrame] = insideFrame +local stealthMaps = { + WARRIOR = "", + ROGUE = "1:2", -- go to page 2 for rogues + HUNTER = "", + DRUID = "3:5", -- we'll replace with 1:2 if stance mapping is not enabled, and page 5 with 6 if oomkin/tree + PALADIN = "", + SHAMAN = "", + MAGE = "", + PRIEST = "", } +local unstealthMaps = { + WARRIOR = "", + ROGUE = "2:1", -- go to page 1 for rogues + HUNTER = "", + DRUID = "5:3", -- we'll replace with 2:1 if stance mapping is not enabled, and page 5 with 6 if oomkin/tree + PALADIN = "", + SHAMAN = "", + MAGE = "", + PRIEST = "", +} + +-- Immediately fix if a druid with oomkin or tree (note: can't have both) +local _, playerClass = UnitClass("player") +if playerClass == "DRUID" and GetNumShapeshiftForms() > 4 then + nStancePages.DRUID = 4 + stanceMaps.DRUID[5] = "*:4" + stealthMaps.DRUID = "3:6" + unstealthMaps.DRUID ="6:3" +end + +-- Immediately fix if a priest with shadowform +if playerClass == "PRIEST" and GetNumShapeshiftForms() > 1 then + nStancePages.PRIEST = 2 +end + +local AceOO = AceLibrary("AceOO-2.0") + -- ReBar is an Ace 2 class prototype object. -ReBar = AceLibrary("AceOO-2.0").Class("AceEvent-2.0") +ReBar = AceOO.Class("AceEvent-2.0", ReAnchor, ReAnchor.IAnchorable) -local dewdrop = AceLibrary("Dewdrop-2.0") +---------------------- +-- Static members +---------------------- +-- IButtonClass is an interface required of any button class type (not the class objects themselves) to be used with the bar +ReBar.IButtonClass = AceOO.Interface { + Acquire = "function", -- btn = Acquire(config, barIdx, pages, buttonsPerPage) + -- analogous to new(), this should re-use recycled frames for memory efficiency + Release = "function", -- Release(btn) should hide and dispose of the button (and recycle it) +} + +-- IButton is an interface required of any button objects to be used with the bar +ReBar.IButton = AceOO.Interface { + GetActionFrame = "function", -- obtains the action frame, presumably inherited from SecureActionButtonTemplate or similar. + BarLocked = "function", -- BarLocked() called when the bar is locked. + BarUnlocked = "function", -- BarUnlocked() called when the bar is unlocked. + PlaceButton = "function", -- PlaceButton(parent, anchorPoint, offsetX, offsetY, squareSz), one-stop position and size setting + SetPages = "function", -- SetPages(n): sets number of pages for multi-paging. Can error if paging not supported. +} + +-- wrap UIParent and WorldFrame in objects which implement the ReAnchor.IAnchorable interface +local UIParentPlaceable = { + GetFrame = function() return UIParent end, + GetAnchorage = function() return ReAnchor.anchorInside end, +} + +local WorldFramePlaceable = { + GetFrame = function() return WorldFrame end, + GetAnchorage = function() return ReAnchor.anchorInside end, +} + +ReBar.anchorTargets = { + UIParentPlaceable, + WorldFramePlaceable +} + +ReBar.UIParentAnchorTarget = { + UIParentPlaceable +} + + + + + + +-------------------- +-- instance methods +-------------------- +-- construction and destruction function ReBar.prototype:init( config, id ) ReBar.super.prototype.init(self) - local buttonClass = config and config.btnConfig and config.btnConfig.type and getglobal(config.btnConfig.type) self.config = config self.barID = id - self.class = { button = buttonClass } self.buttons = { } + self.locked = true + + self.buttonClass = config.btnConfig and config.btnConfig.type and getglobal(config.btnConfig.type) + if not AceOO.inherits(self.buttonClass, ReBar.IButton) then + error("ReBar: Supplied Button type does not meet required interface.") + end + -- create the bar and control widgets - self.barFrame = CreateFrame("Frame", "ReBar_"..self.barID, UIParent, "ReBarTemplate") + self.barFrame = CreateFrame("Frame", "ReBar"..id, config.parent and getglobal(config.parent) or UIParent, "ReBarTemplate") self.controlFrame = getglobal(self.barFrame:GetName().."Controls") self.controlFrame.reBar = self self.barFrame:SetClampedToScreen(true) @@ -58,82 +158,113 @@ self.labelString = getglobal(self.controlFrame:GetName().."LabelString") self.labelString:SetText(id) + -- initial stateheader state + self.barFrame.StateChanged = function() self:StateChanged() end + self.barFrame:SetAttribute("state",config.pages and config.pages.currentPage or 1) -- initial state + -- initialize the bar layout self:ApplySize() self:ApplyAnchor() + self:AcquireButtons() self:LayoutButtons() self:ApplyVisibility() - -- add bar to stickyTargets list - stickyTargets[self.barFrame] = outsideFrame - - -- initialize dewdrop menu - dewdrop:Register(self.controlFrame, 'children', function() - dewdrop:FeedAceOptionsTable(ReActionGlobalMenuOptions) - dewdrop:FeedAceOptionsTable(GenerateReActionBarOptions(self)) - dewdrop:FeedAceOptionsTable(GenerateReActionButtonOptions(self)) - end, - 'cursorX', true, - 'cursorY', true - ) + if self.config.pages then + self:SetPages(self.config.pages.n) + self:ApplyAutoStanceSwitch() + self:ApplyAutoStealthSwitch() + self:RefreshPageControls() + end + + -- register page up/down buttons with ReBound for keybinding + if ReBound then + ReBound:AddKeybindTarget(getglobal(self.barFrame:GetName().."PageUp")) + ReBound:AddKeybindTarget(getglobal(self.barFrame:GetName().."PageDown")) + end + + -- add bar to anchorTargets list + table.insert(ReBar.anchorTargets, self) end +function ReBar.prototype:Destroy() + local f = self.barFrame -function ReBar.prototype:Destroy() - if self.barFrame == dewdrop:GetOpenedParent() then - dewdrop:Close() - dewdrop:Unregister(self.barFrame) + self:HideControls() + f:Hide() + f:ClearAllPoints() + f:SetParent(nil) + f:SetPoint("BOTTOMRIGHT", UIParent, "TOPLEFT", 0, 0) + + while #self.buttons > 0 do + self.buttonClass:Release(table.remove(self.buttons)) end - self:HideControls() - self.barFrame:Hide() - self.barFrame:ClearAllPoints() - self.barFrame:SetParent(nil) - self.barFrame:SetPoint("BOTTOMRIGHT", UIParent, "TOPLEFT", 0, 0) + -- remove from anchorTargets table + for idx, b in ipairs(ReBar.anchorTargets) do + if b == self then + table.remove(ReBar.anchorTargets, idx) + break + end + end - -- need to keep around self.config for dewdrop menus in the process of deleting self + -- remove from globals + local n = f:GetName() + setglobal(n, nil) + setglobal(n.."Control", nil) + setglobal(n.."Controls", nil) + setglobal(n.."ControlsLabelString", nil) + setglobal(n.."PageUp", nil) + setglobal(n.."PageDown", nil) + setglobal(n.."Page", nil) + setglobal(n.."PageNumber", nil) + setglobal(n.."PageNumberLabel", nil) + setglobal(n.."PageNumberLabelText", nil) +end - while #self.buttons > 0 do - self.class.button:release(table.remove(self.buttons)) - end - -- remove from sticky targets table - stickyTargets[self.barFrame] = nil - - -- remove from global table - -- for some reason after a destroy/recreate the globals still reference - -- the old frames - setglobal(self.barFrame:GetName(), nil) - setglobal(self.barFrame:GetName().."Controls", nil) - setglobal(self.controlFrame:GetName().."LabelString", nil) + + +-- ReAnchor.IAnchorable interface implementation +function ReBar.prototype:GetFrame() + return self.barFrame end +function ReBar.prototype:GetAnchorage() + return ReAnchor.anchorOutside +end + + + -- show/hide the control frame function ReBar.prototype:ShowControls() + self.locked = false self.controlFrame:Show() for _, b in ipairs(self.buttons) do b:BarUnlocked() + b:DisplayVisibility() end + self:ApplyVisibility() end function ReBar.prototype:HideControls() + self.locked = true local b = self.barFrame if b.isMoving or b.resizing then b:StopMovingOrSizing() b:SetScript("OnUpdate",nil) end - -- close any dewdrop menu owned by us - if self.barFrame == dewdrop:GetOpenedParent() then - dewdrop:Close() - end for _, b in ipairs(self.buttons) do b:BarLocked() + b:DisplayVisibility() end self.controlFrame:Hide() + self:ApplyVisibility() end - +function ReBar.prototype:GetControlFrame() + return self.controlFrame +end -- accessors @@ -142,20 +273,265 @@ end function ReBar.prototype:ToggleVisibility() - self.config.visible = not self.config.visible + self:SetVisibility( not self:GetVisibility() ) +end + +function ReBar.prototype:SetVisibility(v) + self.config.visible = v and true or false -- force data integrity self:ApplyVisibility() end function ReBar.prototype:GetOpacity() - return self.config.opacity or 100 + return tonumber(self.config.opacity) or 100 end function ReBar.prototype:SetOpacity( o ) - self.config.opacity = tonumber(o) - self:ApplyVisibility() - return self.config.opacity + o = tonumber(o) + if o then + self.config.opacity = o + self:ApplyVisibility() + return self.config.opacity + end end +function ReBar.prototype:GetButtonList() + return self.buttons +end + +function ReBar.prototype:GetGrowLeft() + return self.config.growLeft +end + +function ReBar.prototype:SetGrowLeft(g) + self.config.growLeft = g and true or false + self:LayoutButtons() +end + +function ReBar.prototype:GetGrowUp() + return self.config.growUp +end + +function ReBar.prototype:SetGrowUp(g) + self.config.growUp = g and true or false + self:LayoutButtons() +end + +function ReBar.prototype:GetColumnMajor() + return self.config.columnMajor +end + +function ReBar.prototype:SetColumnMajor(m) + self.config.columnMajor = m and true or false + self:LayoutButtons() +end + + +-- paging methods +function ReBar.prototype:IsPagingDisabled() + return not (self.config.pages and self.config.pages.n > 1) +end + +function ReBar.prototype:GetPages() + return self.config.pages and self.config.pages.n or 1 +end + +function ReBar.prototype:SetPages(n) + n = tonumber(n) + if n and n >= 1 then + self.config.pages = self.config.pages or { } + self.config.pages.n = n + for _, btn in pairs(self.buttons) do + btn:SetPages(n) + end + if n > 1 then + local statebutton = "0:page1;" + -- map states 1-n to 'page1'-'pagen' + -- page 0 is the same as page 1. + for i = 1, n do + statebutton = statebutton..i..":page"..i..";" + end + self.barFrame:SetAttribute("statebutton",statebutton) + else + self.barFrame:SetAttribute("statebutton",ATTRIBUTE_NOOP) + end + self.barFrame:SetAttribute("statemap-anchor-next","1-"..n) + self.barFrame:SetAttribute("statemap-anchor-prev",tostring(n).."-1") + self:RefreshPageControls() + end +end + +function ReBar.prototype:GetAutoStanceSwitch() + return self.config.pages and self.config.pages.autoStanceSwitch +end + +function ReBar.prototype:SetAutoStanceSwitch( s ) + if not self.config.pages then + self.config.pages = { n = 1 } + end + self.config.pages.autoStanceSwitch = s and true or false + self:ApplyAutoStanceSwitch() +end + +function ReBar.prototype:ToggleAutoStanceSwitch() + self:SetAutoStanceSwitch( not self:GetAutoStanceSwitch() ) +end + +function ReBar.prototype:ApplyAutoStanceSwitch() + local switch = self:GetAutoStanceSwitch() + local _, class = UnitClass("player") + if switch then + -- check that the number of pages available is sufficient + local totalPages = nStancePages[class] + (self:GetAutoStealthSwitch() and 1 or 0) + if self:GetPages() < totalPages then + self:SetPages(totalPages) + end + for form, spec in pairs(stanceMaps[class]) do + self.barFrame:SetAttribute("statemap-stance-"..form,spec) + end + -- set initial value + self.barFrame:SetAttribute("state-stance",GetShapeshiftForm(true)) + else + for form, _ in pairs(stanceMaps[class]) do + self.barFrame:SetAttribute("statemap-stance-"..form, ATTRIBUTE_NOOP) + end + end +end + +function ReBar.prototype:GetAutoStealthSwitch() + return self.config.pages and self.config.pages.autoStealthSwitch +end + +function ReBar.prototype:SetAutoStealthSwitch( s ) + if not self.config.pages then + self.config.pages = { n = 1 } + end + self.config.pages.autoStealthSwitch = s and true or false + self:ApplyAutoStealthSwitch() +end + +function ReBar.prototype:ToggleAutoStealthSwitch() + self:SetAutoStealthSwitch( not self:GetAutoStealthSwitch() ) +end + +function ReBar.prototype:ApplyAutoStealthSwitch() + local switch = self:GetAutoStealthSwitch() + local _, class = UnitClass("player") + if switch then + -- check that the number of pages available is sufficient + local totalPages = (self:GetAutoStanceSwitch() and nStancePages[class] > 0 and nStancePages[class] or 1) + 1 + if self:GetPages() < totalPages then + self:SetPages(totalPages) + end + local s, s2 + if class == "DRUID" and not self:GetAutoStanceSwitch() then + -- change mapping for cat->prowl and prowl->cat to 1:2 and 2:1 since no stance mapping + s = "1:2" + s2 = "2:1" + end + self.barFrame:SetAttribute("statemap-stealth-1",s or stealthMaps[class]) + self.barFrame:SetAttribute("statemap-stealth-0",s2 or unstealthMaps[class]) + -- set initial value + self.barFrame:SetAttribute("state-stealth",IsStealthed() or 0) + else + self.barFrame:SetAttribute("statemap-stealth-1",ATTRIBUTE_NOOP) + self.barFrame:SetAttribute("statemap-stealth-0",ATTRIBUTE_NOOP) + end +end + +function ReBar.prototype:ArePageControlsHidden() + return not ( self.config.pages and self.config.pages.showControls ) +end + +function ReBar.prototype:TogglePageControlsHidden() + if self.config.pages then + self.config.pages.showControls = not self.config.pages.showControls + self:RefreshPageControls() + end +end + +function ReBar.prototype:GetPageControlsLoc() + return self.config.pages and self.config.pages.controlsLoc +end + +function ReBar.prototype:SetPageControlsLoc(loc) + if self.config.pages then + self.config.pages.controlsLoc = loc + self:RefreshPageControls() + end +end + +function ReBar.prototype:RefreshPageControls() + local b = self.barFrame; + local upArrow = getglobal(b:GetName().."PageUp") + local downArrow = getglobal(b:GetName().."PageDown") + local pageNum = getglobal(b:GetName().."PageNumber") + + if self:GetPages() > 1 and self.config.pages.showControls then + local loc = self.config.pages.controlsLoc + + pageNum:ClearAllPoints() + upArrow:ClearAllPoints() + downArrow:ClearAllPoints() + + local vertical = { 0,0,0,1,1,0,1,1 } + local horizontal = { 0,1,1,1,0,0,1,0 } + local textures = { + upArrow:GetNormalTexture(), + upArrow:GetPushedTexture(), + upArrow:GetDisabledTexture(), + upArrow:GetHighlightTexture(), + downArrow:GetNormalTexture(), + downArrow:GetPushedTexture(), + downArrow:GetDisabledTexture(), + downArrow:GetHighlightTexture(), + } + + local offset = 10 + local mult = (loc == "RIGHT" or loc == "TOP") and 1 or -1 + local pageNumOffset = mult * (self.config.spacing/2 - offset) + local arrowOffset = mult * offset + + if loc == "Blizzard" or loc == nil then + pageNum:SetPoint("LEFT", b, "RIGHT", 28, 0) + upArrow:SetPoint("LEFT", b, "RIGHT", -2, 9) + downArrow:SetPoint("LEFT", b, "RIGHT", -2, -10) + for _, tex in ipairs(textures) do + tex:SetTexCoord(unpack(vertical)) + end + elseif loc == "RIGHT" or loc == "LEFT" then + local relPoint = loc == "RIGHT" and "LEFT" or "RIGHT" + pageNum:SetPoint(relPoint, b, loc, pageNumOffset, 0) + upArrow:SetPoint("BOTTOM",pageNum,"TOP",arrowOffset, -12) + downArrow:SetPoint("TOP",pageNum,"BOTTOM",arrowOffset, 12) + for _, tex in ipairs(textures) do + tex:SetTexCoord(unpack(vertical)) + end + else + pageNum:SetPoint(loc == "BOTTOM" and "TOP" or "BOTTOM", b, loc, 0, pageNumOffset) + upArrow:SetPoint("LEFT",pageNum,"RIGHT",-12,arrowOffset) + downArrow:SetPoint("RIGHT",pageNum,"LEFT",12,arrowOffset) + for _, tex in ipairs(textures) do + tex:SetTexCoord(unpack(horizontal)) + end + end + self:StateChanged() + upArrow:Show() + downArrow:Show() + pageNum:Show() + else + upArrow:Hide() + downArrow:Hide() + pageNum:Hide() + end +end + +function ReBar.prototype:StateChanged() + local page = self.barFrame:GetAttribute("state") or 1 + getglobal(self.barFrame:GetName().."PageNumberLabelText"):SetText(page) + if self.config.pages then self.config.pages.currentPage = page end +end + + -- layout methods function ReBar.prototype:ApplySize() @@ -163,14 +539,14 @@ local spacing = self.config.spacing or 4 local rows = self.config.rows or 1 local columns = self.config.columns or 12 - local w = buttonSz * columns + spacing * (columns + 1) - local h = buttonSz * rows + spacing * (rows + 1) + local w = buttonSz * columns + spacing * columns + local h = buttonSz * rows + spacing * rows local f = self.barFrame - -- +1: avoid resizing oddities caused by fractional UI scale setting - f:SetMinResize(buttonSz + spacing*2 + 1, buttonSz + spacing*2 + 1) - f:SetWidth(w + 1) - f:SetHeight(h + 1) + -- + 0.1: avoid issues with UI scaling + f:SetMinResize(buttonSz + spacing + 0.1, buttonSz + spacing + 0.1) + f:SetWidth(w + 0.1) + f:SetHeight(h + 0.1) end function ReBar.prototype:ApplyAnchor() @@ -178,9 +554,9 @@ local f = self.barFrame if a then f:ClearAllPoints() - f:SetPoint(a.point,getglobal(a.to),a.relPoint,a.x,a.y) + f:SetPoint(a.point,getglobal(a.frame),a.relPoint,a.x,a.y) local color = anchoredLabelColor - if a.to == "UIParent" or a.to == "WorldFrame" then + if a.frame == "UIParent" or a.frame == "WorldFrame" then color = nonAnchoredLabelColor end self.labelString:SetTextColor(color.r, color.g, color.b) @@ -188,18 +564,9 @@ end function ReBar.prototype:ApplyVisibility() - local v = self.config.visibility - if type(v) == "table" then - if v.class then - local _, c = UnitClass("player") - v = v.class[c] - end - elseif type(v) == "string" then - local value = getglobal(v) - v = value - end + local v = self.config.visible or not self.locked - if self.config.opacity then + if tonumber(self.config.opacity) then self.barFrame:SetAlpha(self.config.opacity / 100) end @@ -213,26 +580,55 @@ function ReBar.prototype:LayoutButtons() local r = self.config.rows local c = self.config.columns - local n = r * c local sp = self.config.spacing local sz = self.config.size local gSize = sp + sz + local point = (self.config.growUp and "BOTTOM" or "TOP")..(self.config.growLeft and "RIGHT" or "LEFT") + local major = self.config.columnMajor and r or c + + for i, b in ipairs(self.buttons) do + local x = sp/2 + gSize * math.fmod(i-1,major) + local y = sp/2 + gSize * math.floor((i-1)/major) + if self.config.columnMajor then x,y = y,x end + if self.config.growLeft then x = -x end + if not self.config.growUp then y = -y end + b:PlaceButton(self.barFrame, point, x, y, sz) + end +end + +function ReBar.prototype:FlipRowsColumns() + self.config.rows, self.config.columns = self.config.columns, self.config.rows + self.config.columnMajor = not self.config.columnMajor + self:ApplySize() + self:LayoutButtons() +end + +function ReBar.prototype:AcquireButtons() + local n = self.config.rows * self.config.columns for i = 1, n do if self.buttons[i] == nil then - table.insert(self.buttons, self.class.button:acquire(self.barFrame, self.config.btnConfig, i)) + local b = self.buttonClass:Acquire(self.config.btnConfig, i, self.config.pages and self.config.pages.n, n) + if b then + if not AceOO.inherits(b, ReBar.IButton) then + error("ReBar: specified Button class object did not meet required interface") + end + if self.locked == false then + b:BarUnlocked() -- buttons assume they are created in a barlocked state + end + self.buttons[i] = b + self.barFrame:SetAttribute("addchild",b:GetActionFrame()) + end end - local b = self.buttons[i] - if b == nil then - break -- handling for button types that support limited numbers - end - b:PlaceButton("TOPLEFT", sp + gSize * math.fmod(i-1,c), - (sp + gSize * math.floor((i-1)/c)), sz) end - -- b == nil, above, should always be the case if and only if i == n. ReBar never monkeys - -- with buttons in the middle of the sequence: it always adds or removes on the array end - while #self.buttons > n do - self.class.button:release(table.remove(self.buttons)) + local maxn = table.maxn(self.buttons) + for i = n+1, table.maxn(self.buttons) do + local b = self.buttons[i] + self.buttons[i] = nil + if b then + self.buttonClass:Release(b) + end end end @@ -243,7 +639,7 @@ -- no point if we can't store the name or the offsets are incomplete if name and x and y then self.config.anchor = { - to = name, + frame = name, point = p, relPoint = rp or p, x = x, @@ -254,45 +650,53 @@ +function ReBar.prototype:StickyIndicatorUpdate() + if IsShiftKeyDown() then + local snapRange = self.config.size + self.config.spacing + self:DisplaySnapIndicator(ReBar.anchorTargets, snapRange, 0, 0) + else + self:HideSnapIndicator() + end +end + + -- mouse event handlers (clicking/dragging/resizing the bar) function ReBar.prototype:BeginDrag() local f = self.barFrame f:StartMoving() f.isMoving = true - f:SetScript("OnUpdate", function() self:StickyIndicatorUpdate() end) + self.updateTime = DRAG_UPDATE_RATE + f:SetScript("OnUpdate", function(frame, elapsed) self:StickyIndicatorUpdate(elapsed) end) end function ReBar.prototype:FinishDrag() - local f, p, rp, x, y + local o, p, rp, x, y local bf = self.barFrame + local snapRange = self.config.size + self.config.spacing bf:StopMovingOrSizing() bf.isMoving = false bf:SetScript("OnUpdate",nil) if IsShiftKeyDown() then - f, p, rp, x, y = self:GetStickyAnchor() - ReBarStickyIndicator1:Hide() - ReBarStickyIndicator2:Hide() + o, p, rp, x, y = self:GetClosestPointSnapped(ReBar.anchorTargets, snapRange, 0, 0) end - if f == nil then - f = UIParent - local _ - _, p,rp,x,y = self:GetClosestPointTo(f) + if o == nil then + o, p, rp, x, y = self:GetClosestVisiblePoint(ReBar.UIParentAnchorTarget, snapRange, 0, 0) end - - if f then - self:StoreAnchor(f,p,rp,x,y) - self:ApplyAnchor() - end + + self:HideSnapIndicator() + self:StoreAnchor(o:GetFrame(), p, rp, x, y) + self:ApplyAnchor() end function ReBar.prototype:BeginBarResize( sizingPoint ) local f = self.barFrame f:StartSizing(sizingPoint) f.resizing = true - f:SetScript("OnUpdate",function() self:ReflowButtons() end) + self.updateTime = DRAG_UPDATE_RATE + f:SetScript("OnUpdate",function(frame, elapsed) self:ReflowButtons(elapsed) end) end function ReBar.prototype:BeginButtonResize( sizingPoint, mouseBtn ) @@ -303,12 +707,13 @@ local c = self.config.columns local s = self.config.spacing local sz = self.config.size + self.updateTime = DRAG_UPDATE_RATE if mouseBtn == "LeftButton" then - f:SetMinResize(c*(12 + 2*s) +1, r*(12 + 2*s) +1) - f:SetScript("OnUpdate",function() self:DragSizeButtons() end) + f:SetMinResize(c*(12 + s) +0.1, r*(12 + s) +0.1) + f:SetScript("OnUpdate",function(frame, elapsed) self:DragSizeButtons(elapsed) end) elseif mouseBtn == "RightButton" then - f:SetMinResize(c*sz+1, r*sz+1) - f:SetScript("OnUpdate",function() self:DragSizeSpacing() end) + f:SetMinResize(c*sz+0.1, r*sz+0.1) + f:SetScript("OnUpdate",function(frame, elapsed) self:DragSizeSpacing(elapsed) end) end end @@ -323,122 +728,6 @@ --- sticky anchoring functions -function ReBar.prototype:StickyIndicatorUpdate() - local si1 = ReBarStickyIndicator1 - local si2 = ReBarStickyIndicator2 - if IsShiftKeyDown() then - local f, p, rp, x, y = self:GetStickyAnchor() - if f then - si1:ClearAllPoints() - si2:ClearAllPoints() - si1:SetPoint("CENTER",self.barFrame,p,0,0) - si2:SetPoint("CENTER",f,rp,x,y) - si1:Show() - si2:Show() - return nil - end - end - si1:Hide() - si2:Hide() - si1:ClearAllPoints() - si2:ClearAllPoints() -end - -function ReBar.prototype:CheckAnchorable(f) - -- can't anchor to self or to a hidden frame - if f == self.barFrame or not(f:IsShown()) then return false end - - -- also can't anchor to frames that are anchored to self - for i = 1, f:GetNumPoints() do - local _, f2 = f:GetPoint(i) - if f2 == self.barFrame then return false end - end - - return true -end - - -function ReBar.prototype:GetStickyAnchor() - local snapRange = (self.config.size + self.config.spacing) - local r2, f, p, rp, x, y = self:GetClosestAnchor() - - if f and p then - local xx, yy = pointFindTable[p](f) - if r2 and r2 < (snapRange*snapRange) then - if xx or math.abs(x) < snapRange then x = 0 end - if yy or math.abs(y) < snapRange then y = 0 end - elseif not(yy) and math.abs(x) < snapRange then - x = 0 - elseif not(xx) and math.abs(y) < snapRange then - y = 0 - else - f = nil -- nothing in range - end - end - return f, p, rp, x, y -end - -function ReBar.prototype:GetClosestAnchor() - -- choose the closest anchor point on the list of target frames - local range2, frame, point, relPoint, offsetX, offsetY - - for f, tgtRegion in pairs(stickyTargets) do - if self:CheckAnchorable(f) then - local r2 ,p, rp, x, y = self:GetClosestPointTo(f,tgtRegion) - if r2 then - if not(range2 and range2 < r2) then - range2, frame, point, relPoint, offsetX, offsetY = r2, f, p, rp, x, y - end - end - end - end - - return range2, frame, point, relPoint, offsetX, offsetY -end - -function ReBar.prototype:GetClosestPointTo(f,inside) - local range2, point, relPoint, offsetX, offsetY - local pft = pointFindTable - local cx, cy = self.barFrame:GetCenter() - local fcx, fcy = f:GetCenter() - local fh = f:GetHeight() - local fw = f:GetWidth() - - -- compute whether edge bisector intersects target edge - local dcx = math.abs(cx-fcx) < fw/2 and (cx-fcx) - local dcy = math.abs(cy-fcy) < fh/2 and (cy-fcy) - - for p, func in pairs(pft) do - local rp, x, y - if inside == outsideFrame then - rp = oppositePointTable[p] - x, y = self:GetOffsetToPoint(f, func, pft[rp]) - else - rp = p - x, y = self:GetOffsetToPoint(f, func, func) - end - - -- if anchoring to an edge, only anchor if the center point overlaps the other edge - if (x or dcx) and (y or dcy) then - local r2 = (x or 0)^2 + (y or 0)^2 - if range2 == nil or r2 < range2 then - range2, point, relPoint, offsetX, offsetY = r2, p, rp, x or dcx, y or dcy - end - end - end - return range2, point, relPoint, offsetX, offsetY -end - -function ReBar.prototype:GetOffsetToPoint(f,func,ffunc) - local x, y = func(self.barFrame) -- coordinates of the point on this frame - local fx, fy = ffunc(f) -- coordinates of the point on the target frame - -- guarantees: if x then fx, if y then fy - return x and (x-fx), y and (y-fy) -end - - - @@ -449,47 +738,64 @@ return f:GetWidth(), f:GetHeight(), c.size, c.rows, c.columns, c.spacing end + -- add and remove buttons dynamically as the bar is resized -function ReBar.prototype:ReflowButtons() - local w, h, sz, r, c, sp = self:GetLayout() +function ReBar.prototype:ReflowButtons( elapsed ) + self.updateTime = self.updateTime - elapsed + if self.updateTime <= 0 then + self.updateTime = DRAG_UPDATE_RATE + + local w, h, sz, r, c, sp = self:GetLayout() - self.config.rows = math.floor( (h - sp) / (sz + sp) ) - self.config.columns = math.floor( (w - sp) / (sz + sp) ) + self.config.rows = math.floor( (h+1) / (sz + sp) ) + self.config.columns = math.floor( (w+1) / (sz + sp) ) - if self.config.rows ~= r or self.config.columns ~= c then - self:LayoutButtons() + if self.config.rows ~= r or self.config.columns ~= c then + self:AcquireButtons() + self:LayoutButtons() + end end end -- change the size of buttons as the bar is resized -function ReBar.prototype:DragSizeButtons() - local w, h, sz, r, c, sp = self:GetLayout() +function ReBar.prototype:DragSizeButtons( elapsed ) + self.updateTime = self.updateTime - elapsed + if self.updateTime <= 0 then + self.updateTime = DRAG_UPDATE_RATE + + local w, h, sz, r, c, sp = self:GetLayout() - local newSzW = math.floor((w - (c+1)*sp)/c) - local newSzH = math.floor((h - (r+1)*sp)/r) - - self.config.size = math.max(12, math.min(newSzW, newSzH)) + local newSzW = math.floor((w - c*sp)/c) + local newSzH = math.floor((h - r*sp)/r) + + self.config.size = math.max(12, math.min(newSzW, newSzH)) - if self.config.size ~= sz then - self:LayoutButtons() - self:UpdateResizeTooltip() + if self.config.size ~= sz then + self:LayoutButtons() + self:UpdateResizeTooltip() + end end end -- change the spacing of buttons as the bar is resized -function ReBar.prototype:DragSizeSpacing() - local w, h, sz, r, c, sp = self:GetLayout() +function ReBar.prototype:DragSizeSpacing( elapsed ) + self.updateTime = self.updateTime - elapsed + if self.updateTime <= 0 then + self.updateTime = DRAG_UPDATE_RATE + + local w, h, sz, r, c, sp = self:GetLayout() - local newSpW = math.floor((w - c*sz)/(c+1)) - local newSpH = math.floor((h - r*sz)/(r+1)) + local newSpW = math.floor((w - c*sz)/c) + local newSpH = math.floor((h - r*sz)/r) - self.config.spacing = math.max(0, math.min(newSpW, newSpH)) + self.config.spacing = math.max(0, math.min(newSpW, newSpH)) - if self.config.spacing ~= sp then - self:LayoutButtons() - self:UpdateResizeTooltip() + if self.config.spacing ~= sp then + self:LayoutButtons() + self:UpdateResizeTooltip() + end end end @@ -503,7 +809,7 @@ function ReBar.prototype:ShowTooltip() GameTooltip:SetOwner(self.barFrame, "ANCHOR_TOPRIGHT") - GameTooltip:AddLine("Bar "..self.barID) + GameTooltip:AddLine(self.config.btnConfig.subtype.." Bar "..self.barID..(self.config.visible and "" or " (Hidden)")) GameTooltip:AddLine("Drag to move") GameTooltip:AddLine("Shift-drag for sticky mode") GameTooltip:AddLine("Right-click for options")
--- a/classes/ReBar.xml Tue Mar 20 21:20:20 2007 +0000 +++ b/classes/ReBar.xml Tue Mar 20 21:25:29 2007 +0000 @@ -65,31 +65,25 @@ </Frame> - - <Frame name="ReBarStickyIndicatorTemplate" virtual="true" frameStrata="HIGH" enableMouse="false" hidden="true" parent="UIParent"> + <Button name="ReBarPageArrowTemplate" inherits="SecureAnchorButtonTemplate" virtual="true"> <Size> - <AbsDimension x="8" y="8"/> + <AbsDimension x="32" y="32"/> </Size> <Layers> - <Layer level="OVERLAY"> - <Texture alphaMode="ADD"> - <Color r="1.0" g="0.82" b="0" a="0.8"/> - </Texture> + <Layer level="BACKGROUND"> + <Color r="1" g="1" b="0" setAllPoints="true"/> </Layer> </Layers> - </Frame> + <HitRectInsets> + <AbsInset left="6" right="6" top="7" bottom="7"/> + </HitRectInsets> + </Button> - <Frame name="ReBarStickyIndicator1" inherits="ReBarStickyIndicatorTemplate"/> - <Frame name="ReBarStickyIndicator2" inherits="ReBarStickyIndicatorTemplate"/> - - <!-- A ReAction bar is a container for buttons. The bar container itself is invisible and non-responsive to + <!-- A ReBar is a container for buttons. The bar container itself is invisible and non-responsive to mouse input, but when unlocked a normally invisible child control frame becomes visible and consumes mouse events to move, resize, and set bar options. --> - <Frame name="ReBarTemplate" virtual="true" toplevel="true" enableMouse="true" movable="true" resizable="true"> - <Layers> - <Layer level="BACKGROUND"/> - </Layers> + <Frame name="ReBarTemplate" inherits="SecureStateDriverTemplate" frameStrata="MEDIUM" virtual="true" toplevel="true" movable="true" resizable="true"> <Frames> <Frame name="$parentControl" setAllPoints="true"> <!-- this nesting is to ensure the control frame is on top of the buttons, which will @@ -156,6 +150,20 @@ <Color r="0.7" g="0.7" b="1.0" a="0.2"/> </Texture> </Layer> + <Layer level="OVERLAY"> + <FontString name="$parentLabelString" inherits="GameFontNormalLarge" justifyH="CENTER" text="(barID)" setAllPoints="true" outline="THICK"> + <Anchors> + <Anchor point="CENTER"/> + </Anchors> + <Shadow> + <Offset> + <AbsDimension x="2" y="-2"/> + </Offset> + <Color r="0" g="0" b="0" a="1"/> + </Shadow> + <Color r="1" g="1" b="1" a="1"/> + </FontString> + </Layer> </Layers> <Frames> <!-- edge drag handles --> @@ -241,54 +249,6 @@ </Frames> </Frame> - <Frame name="$parentLabel"> - <Size> - <AbsDimension x="32" y="24"/> - </Size> - <Anchors> - <Anchor point="CENTER"/> - </Anchors> - <Backdrop edgeFile="Interface\Tooltips\UI-Tooltip-Border" tile="true"> - <EdgeSize> - <AbsValue val="16"/> - </EdgeSize> - <TileSize> - <AbsValue val="16"/> - </TileSize> - <BackgroundInsets> - <AbsInset left="0" right="0" top="0" bottom="0"/> - </BackgroundInsets> - </Backdrop> - <Layers> - <Layer level="BACKGROUND"> - <Texture> - <Anchors> - <Anchor point="TOPLEFT"> - <Offset> - <AbsDimension x="4" y="-4"/> - </Offset> - </Anchor> - <Anchor point="BOTTOMRIGHT"> - <Offset> - <AbsDimension x="-4" y="4"/> - </Offset> - </Anchor> - </Anchors> - <Color r="0.0" g="0.0" b="0.0"/> - </Texture> - </Layer> - <Layer level="ARTWORK"> - <FontString name="$parentString" inherits="GameFontNormalLarge" justifyH="CENTER" text="(barID)"> - <Size> - <AbsDimension x="24" y="18"/> - </Size> - <Anchors> - <Anchor point="CENTER"/> - </Anchors> - </FontString> - </Layer> - </Layers> - </Frame> </Frames> <Scripts> <OnLoad> @@ -311,6 +271,77 @@ </Button> </Frames> </Frame> + + <Button name="$parentPageUp" inherits="ReBarPageArrowTemplate" hidden="true"> + <Scripts> + <OnLoad> + this:SetAttribute("anchorchild",this:GetParent()) + this:SetAttribute("childstate","^next") + </OnLoad> + </Scripts> + <NormalTexture file="Interface\MainMenuBar\UI-MainMenu-ScrollUpButton-Up"/> + <PushedTexture file="Interface\MainMenuBar\UI-MainMenu-ScrollUpButton-Down"/> + <DisabledTexture file="Interface\Buttons\UI-ScrollBar-ScrollUpButton-Disabled"/> + <HighlightTexture alphaMode="ADD" file="Interface\MainMenuBar\UI-MainMenu-ScrollUpButton-Highlight"/> + </Button> + + <Button name="$parentPageDown" inherits="ReBarPageArrowTemplate" hidden="true"> + <Scripts> + <OnLoad> + this:SetAttribute("anchorchild",this:GetParent()) + this:SetAttribute("childstate","^prev") + </OnLoad> + </Scripts> + <NormalTexture file="Interface\MainMenuBar\UI-MainMenu-ScrollDownButton-Up"/> + <PushedTexture file="Interface\MainMenuBar\UI-MainMenu-ScrollDownButton-Down"/> + <DisabledTexture file="Interface\Buttons\UI-ScrollBar-ScrollDownButton-Disabled"/> + <HighlightTexture alphaMode="ADD" file="Interface\MainMenuBar\UI-MainMenu-ScrollDownButton-Highlight"/> + </Button> + + <Frame name="$parentPage"> + <Frames> <!-- nesting so it appears on top of arrows --> + <Frame name="$parentNumber"> + <Size> + <AbsDimension x="12" y="12"/> + </Size> + <Layers> + <Layer level="BACKGROUND"> + <Texture> + <Color r="0.2" g="0.2" b="0.2" a="1.0"/> + </Texture> + </Layer> + </Layers> + <Frames> + <Frame name="$parentLabel"> + <Size> + <AbsDimension x="18" y="18"/> + </Size> + <Anchors> + <Anchor point="CENTER"/> + </Anchors> + <Backdrop edgeFile="Interface\Tooltips\UI-Tooltip-Border" tile="true"> + <EdgeSize> + <AbsValue val="12"/> + </EdgeSize> + <TileSize> + <AbsValue val="12"/> + </TileSize> + </Backdrop> + <Layers> + <Layer level="ARTWORK"> + <FontString name="$parentText" inherits="GameFontNormalSmall" text="0"> + <Anchors> + <Anchor point="CENTER"/> + </Anchors> + <Color r="1.0" g="0.82" b="0"/> + </FontString> + </Layer> + </Layers> + </Frame> + </Frames> + </Frame> + </Frames> + </Frame> </Frames> </Frame>
--- a/classes/ReBound.lua Tue Mar 20 21:20:20 2007 +0000 +++ b/classes/ReBound.lua Tue Mar 20 21:25:29 2007 +0000 @@ -1,43 +1,49 @@ --- ReBinder.lua +-- ReBound.lua -- -ReBinder = { } + +local AceEvent = AceLibrary("AceEvent-2.0") + +ReBound = { } -- initial values -ReBinder.active = false +ReBound.active = false -ReBinder.targets = { } +ReBound.targets = { } -function ReBinder:AddKeybindTarget( t ) +function ReBound:AddKeybindTarget( t ) if t then - self.targets[t] = CreateFrame("Button", nil, t, "ReBinderClickBindingTemplate") + self.targets[t] = CreateFrame("Button", nil, t, "ReBoundClickBindingTemplate") self.targets[t].keybindTarget = t:GetName() end end -function ReBinder:RemoveKeybindTarget( t ) +function ReBound:RemoveKeybindTarget( t ) if t then self.targets[t] = nil end end -function ReBinder:ShowClickBindingButtons() - for _, clickFrame in pairs(self.targets) do +function ReBound:ShowClickBindingButtons() + AceEvent:TriggerEvent("EVENT_REBOUND_KEYBINDING_MODE", true) + for tgt, clickFrame in pairs(self.targets) do clickFrame:Show() end end -function ReBinder:HideClickBindingButtons() - for _, clickFrame in pairs(self.targets) do +function ReBound:HideClickBindingButtons() + AceEvent:TriggerEvent("EVENT_REBOUND_KEYBINDING_MODE", false) + for tgt, clickFrame in pairs(self.targets) do clickFrame:Hide() end end -function ReBinder:ClearSelectedKey() +function ReBound:ClearSelectedKey() self.selectedKey = nil + SetCursor(nil) -- reset cursor to default state end -function ReBinder:ToggleEnabled() +function ReBound:ToggleEnabled() if self:IsEnabled() then self:Disable() else @@ -45,20 +51,32 @@ end end -function ReBinder:IsEnabled() - return ReBinderFrame:IsVisible() +function ReBound:IsEnabled() + return ReBoundFrame:IsVisible() end -function ReBinder:Enable() - ReBinderFrame:Show() +function ReBound:Enable() + if InCombatLockdown() then + UIErrorsFrame:AddMessage("Can't set keybindings in combat") + else + ReBoundFrame:Show() + end end -function ReBinder:Disable() - ReBinderFrame:Hide() +function ReBound:Disable() + ReBoundFrame:Hide() end -function ReBinder:HandleKeyPressed( key ) +local mouseButtonConvert = { + LeftButton = "BUTTON1", + RightButton = "BUTTON2", + MiddleButton = "BUTTON3", + Button4 = "BUTTON4", + Button5 = "BUTTON5" +} + +function ReBound:HandleKeyPressed( key ) if key == nil or key == "UNKNOWN" or key == "SHIFT" or key == "CTRL" or key == "ALT" then return end @@ -71,12 +89,16 @@ if IsAltKeyDown() then key = "ALT-"..key end - if key == "ESCAPE" or GetBindingAction(key) == "REBINDER_TOGGLEBINDINGMODE" then - ReBinderFrame:Hide() + if key == "ESCAPE" or GetBindingAction(key) == "ReBound_TOGGLEBINDINGMODE" then + ReBoundFrame:Hide() return nil, nil end - + key = mouseButtonConvert[key] or key + self.selectedKey = key + + -- change cursor to glowing hand + SetCursor("CAST_CURSOR") local keyTxt = GetBindingText(key, "KEY_") local cmd = GetBindingAction(key) @@ -97,31 +119,31 @@ end -- TODO: move to override-binding model and store data in profile -function ReBinder:BindSelectedKeyTo( btnName ) +function ReBound:BindSelectedKeyTo( btnName ) if self.selectedKey and btnName then self:ClearBinding(btnName) SetBindingClick(self.selectedKey, btnName, "LeftButton") SaveBindings(2) -- 2 = character-specific - ReBinderFrame.statusMsg:SetText(GetBindingText(self.selectedKey, "KEY_") .. " is now bound to " .. btnName) - ReBinderFrame.selectedKey:SetText("(none)") - ReBinderFrame.currentAction:SetText("(none)") - self.selectedKey = nil + ReBoundFrame.statusMsg:SetText(GetBindingText(self.selectedKey, "KEY_") .. " is now bound to " .. btnName) + ReBoundFrame.selectedKey:SetText("(none)") + ReBoundFrame.currentAction:SetText("(none)") + self:ClearSelectedKey() end end -function ReBinder:ClearBinding( btnName ) +function ReBound:ClearBinding( btnName ) if btnName then local current = GetBindingKey("CLICK "..btnName..":LeftButton") if current then SetBinding(current, nil) - ReBinderFrame.statusMsg:SetText("|cFFFF3333"..btnName .. " is now unbound|r") + ReBoundFrame.statusMsg:SetText("|cFFFF3333"..btnName .. " is now unbound|r") end end end -function ReBinder:UpdateCurrentTarget( btnName ) +function ReBound:UpdateCurrentTarget( btnName ) local msg = "" if btnName then msg = btnName.." is currently " @@ -132,5 +154,5 @@ msg = msg .. " not bound" end end - ReBinderFrame.statusMsg:SetText(msg) + ReBoundFrame.statusMsg:SetText(msg) end
--- a/classes/ReBound.xml Tue Mar 20 21:20:20 2007 +0000 +++ b/classes/ReBound.xml Tue Mar 20 21:25:29 2007 +0000 @@ -3,7 +3,7 @@ xsi:schemaLocation="http://www.blizzard.com/wow/ui/..\FrameXML\UI.xsd"> - <Button name="ReBinderClickBindingTemplate" virtual="true" hidden="true" toplevel="true" setAllPoints="true"> + <Button name="ReBoundClickBindingTemplate" virtual="true" hidden="true" toplevel="true" setAllPoints="true"> <HighlightTexture alphaMode="ADD" file="Interface\Buttons\ButtonHilight-Square"/> <Layers> <Layer level="BACKGROUND"> @@ -19,26 +19,26 @@ <OnClick> local mouseBtn = arg1 if mouseBtn == "LeftButton" then - ReBinder:BindSelectedKeyTo(this.keybindTarget) + ReBound:BindSelectedKeyTo(this.keybindTarget) elseif mouseBtn == "RightButton" then - ReBinder:ClearBinding(this.keybindTarget) + ReBound:ClearBinding(this.keybindTarget) end </OnClick> <PostClick> this:SetButtonState("NORMAL") </PostClick> <OnEnter> - ReBinder:UpdateCurrentTarget(this.keybindTarget) + ReBound:UpdateCurrentTarget(this.keybindTarget) </OnEnter> <OnLeave> - ReBinder:UpdateCurrentTarget(nil) + ReBound:UpdateCurrentTarget(nil) </OnLeave> </Scripts> </Button> <!-- this frame covers the entire UIParent. It is visible but empty and in the background, so all it does is consume key presses and unhandled mouse clicks --> - <Button name="ReBinderFrame" frameStrata="BACKGROUND" movable="false" enableMouse="true" enableKeyboard="true" parent="UIParent" hidden="true" setAllPoints="true"> + <Button name="ReBoundFrame" frameStrata="BACKGROUND" movable="false" enableMouse="true" enableKeyboard="true" parent="UIParent" hidden="true" setAllPoints="true"> <Frames> <!-- this is a dialog frame that appears to provide user feedback for the outer frame --> <Button name="$parentDialog" frameStrata="DIALOG" movable="true" enableMouse="true"> @@ -195,7 +195,7 @@ this:RegisterForClicks("MiddleButtonUp","Button4Up","Button5Up") </OnLoad> <OnClick> - local k, a = ReBinder:HandleKeyPressed(arg1) + local k, a = ReBound:HandleKeyPressed(arg1) if k then this.selectedKey:SetText(k) this.currentAction:SetText(a or "(none)") @@ -211,31 +211,35 @@ this.statusMsg = getglobal(this:GetName().."DialogStatusMsg") tinsert(UISpecialFrames,this:GetName()) this:RegisterForClicks("MiddleButtonUp","Button4Up","Button5Up") + this:RegisterEvent("PLAYER_REGEN_DISABLED") </OnLoad> <OnShow> this.selectedKey:SetText("(none)") this.currentAction:SetText("(none)") this.statusMsg:SetText("") - ReBinder:ShowClickBindingButtons() + ReBound:ShowClickBindingButtons() </OnShow> <OnHide> - ReBinder:HideClickBindingButtons() - ReBinder:ClearSelectedKey() + ReBound:HideClickBindingButtons() + ReBound:ClearSelectedKey() </OnHide> <OnKeyDown> - local k, a = ReBinder:HandleKeyPressed(arg1) + local k, a = ReBound:HandleKeyPressed(arg1) if k then this.selectedKey:SetText(k) this.currentAction:SetText(a or "(none)") end </OnKeyDown> <OnClick> - local k, a = ReBinder:HandleKeyPressed(arg1) + local k, a = ReBound:HandleKeyPressed(arg1) if k then this.selectedKey:SetText(k) this.currentAction:SetText(a or "(none)") end </OnClick> + <OnEvent> + this:Hide() -- only event is enter-combat + </OnEvent> </Scripts> </Button>
--- a/libs/AceLibrary/AceLibrary.lua Tue Mar 20 21:20:20 2007 +0000 +++ b/libs/AceLibrary/AceLibrary.lua Tue Mar 20 21:25:29 2007 +0000 @@ -1,10 +1,10 @@ --[[ Name: AceLibrary -Revision: $Rev: 19062 $ +Revision: $Rev$ Developed by: The Ace Development Team (http://www.wowace.com/index.php/The_Ace_Development_Team) Inspired By: Iriel (iriel@vigilance-committee.org) Tekkub (tekkub@gmail.com) - Revision: $Rev: 19062 $ + Revision: $Rev$ Website: http://www.wowace.com/ Documentation: http://www.wowace.com/index.php/AceLibrary SVN: http://svn.wowace.com/root/trunk/Ace2/AceLibrary @@ -14,10 +14,11 @@ proper error tools. It is handy because all the errors occur in the file that called it, not in the library file itself. Dependencies: None +License: LGPL v2.1 ]] local ACELIBRARY_MAJOR = "AceLibrary" -local ACELIBRARY_MINOR = "$Revision: 19062 $" +local ACELIBRARY_MINOR = "$Revision: 20000 $" local _G = getfenv(0) local previous = _G[ACELIBRARY_MAJOR] @@ -25,7 +26,7 @@ local function safecall(func,...) local success, err = pcall(func,...) - if not success then geterrorhandler()(err) end + if not success then geterrorhandler()(err:find("%.lua:%d+:") and err or (debugstack():match("\n(.-: )in.-\n") or "") .. err) end end -- @table AceLibrary @@ -36,15 +37,15 @@ local function error(self, message, ...) if type(self) ~= "table" then - return _G.error(string.format("Bad argument #1 to `error' (table expected, got %s)", type(self)), 2) + return _G.error(("Bad argument #1 to `error' (table expected, got %s)"):format(type(self)), 2) end local stack = debugstack() if not message then - local _,_,second = string.find(stack, "\n(.-)\n") + local _,_,second = stack:find("\n(.-)\n") message = "error raised! " .. second else - local arg = { ... } + local arg = { ... } -- not worried about table creation, as errors don't happen often for i = 1, #arg do arg[i] = tostring(arg[i]) @@ -52,35 +53,35 @@ for i = 1, 10 do table.insert(arg, "nil") end - message = string.format(message, unpack(arg)) + message = message:format(unpack(arg)) end if getmetatable(self) and getmetatable(self).__tostring then - message = string.format("%s: %s", tostring(self), message) + message = ("%s: %s"):format(tostring(self), message) elseif type(rawget(self, 'GetLibraryVersion')) == "function" and AceLibrary:HasInstance(self:GetLibraryVersion()) then - message = string.format("%s: %s", self:GetLibraryVersion(), message) + message = ("%s: %s"):format(self:GetLibraryVersion(), message) elseif type(rawget(self, 'class')) == "table" and type(rawget(self.class, 'GetLibraryVersion')) == "function" and AceLibrary:HasInstance(self.class:GetLibraryVersion()) then - message = string.format("%s: %s", self.class:GetLibraryVersion(), message) + message = ("%s: %s"):format(self.class:GetLibraryVersion(), message) end - local first = string.gsub(stack, "\n.*", "") - local file = string.gsub(first, ".*\\(.*).lua:%d+: .*", "%1") - file = string.gsub(file, "([%(%)%.%*%+%-%[%]%?%^%$%%])", "%%%1") + local first = stack:gsub("\n.*", "") + local file = first:gsub(".*\\(.*).lua:%d+: .*", "%1") + file = file:gsub("([%(%)%.%*%+%-%[%]%?%^%$%%])", "%%%1") local i = 0 - for s in string.gmatch(stack, "\n([^\n]*)") do + for s in stack:gmatch("\n([^\n]*)") do i = i + 1 - if not string.find(s, file .. "%.lua:%d+:") and not string.find(s, "%(tail call%)") then - file = string.gsub(s, "^.*\\(.*).lua:%d+: .*", "%1") - file = string.gsub(file, "([%(%)%.%*%+%-%[%]%?%^%$%%])", "%%%1") + if not s:find(file .. "%.lua:%d+:") and not s:find("%(tail call%)") then + file = s:gsub("^.*\\(.*).lua:%d+: .*", "%1") + file = file:gsub("([%(%)%.%*%+%-%[%]%?%^%$%%])", "%%%1") break end end local j = 0 - for s in string.gmatch(stack, "\n([^\n]*)") do + for s in stack:gmatch("\n([^\n]*)") do j = j + 1 - if j > i and not string.find(s, file .. "%.lua:%d+:") and not string.find(s, "%(tail call%)") then + if j > i and not s:find(file .. "%.lua:%d+:") and not s:find("%(tail call%)") then return _G.error(message, j+1) end end @@ -91,7 +92,7 @@ if not condition then if not message then local stack = debugstack() - local _,_,second = string.find(stack, "\n(.-)\n") + local _,_,second = stack:find("\n(.-)\n") message = "assertion failed! " .. second end return error(self, message, ...) @@ -108,9 +109,10 @@ local errored = false arg = type(arg) if arg ~= kind and arg ~= kind2 and arg ~= kind3 and arg ~= kind4 and arg ~= kind5 then - local _,_,func = string.find(debugstack(), "`argCheck'.-([`<].-['>])") + local stack = debugstack() + local _,_,func = stack:find("`argCheck'.-([`<].-['>])") if not func then - _,_,func = string.find(debugstack(), "([`<].-['>])") + _,_,func = stack:find("([`<].-['>])") end if kind5 then return error(self, "Bad argument #%s to %s (%s, %s, %s, %s, or %s expected, got %s)", tonumber(num) or 0/0, func, kind, kind2, kind3, kind4, kind5, arg) @@ -130,7 +132,8 @@ do local function check(self, ret, ...) if not ret then - return error(self, (string.gsub(..., ".-%.lua:%d-: ", ""))) + local s = ... + return error(self, (s:gsub(".-%.lua:%d-: ", ""))) else return ... end @@ -164,12 +167,12 @@ local function svnRevisionToNumber(text) if type(text) == "string" then - if string.find(text, "^%$Revision: (%d+) %$$") then - return tonumber((string.gsub(text, "^%$Revision: (%d+) %$$", "%1"))) - elseif string.find(text, "^%$Rev: (%d+) %$$") then - return tonumber((string.gsub(text, "^%$Rev: (%d+) %$$", "%1"))) - elseif string.find(text, "^%$LastChangedRevision: (%d+) %$$") then - return tonumber((string.gsub(text, "^%$LastChangedRevision: (%d+) %$$", "%1"))) + if text:find("^%$Revision: (%d+) %$$") then + return tonumber((text:gsub("^%$Revision: (%d+) %$$", "%1"))) + elseif text:find("^%$Rev: (%d+) %$$") then + return tonumber((text:gsub("^%$Rev: (%d+) %$$", "%1"))) + elseif text:find("^%$LastChangedRevision: (%d+) %$$") then + return tonumber((text:gsub("^%$LastChangedRevision: (%d+) %$$", "%1"))) end elseif type(text) == "number" then return text @@ -367,7 +370,7 @@ if m then minor = m else - _G.error(string.format("Bad argument #3 to `IsNewVersion'. Must be a number or SVN revision string. %q is not appropriate", minor), 2) + _G.error(("Bad argument #3 to `IsNewVersion'. Must be a number or SVN revision string. %q is not appropriate"):format(minor), 2) end end argCheck(self, minor, 3, "number") @@ -393,7 +396,7 @@ if m then minor = m else - _G.error(string.format("Bad argument #3 to `HasInstance'. Must be a number or SVN revision string. %q is not appropriate", minor), 2) + _G.error(("Bad argument #3 to `HasInstance'. Must be a number or SVN revision string. %q is not appropriate"):format(minor), 2) end end argCheck(self, minor, 3, "number") @@ -416,7 +419,7 @@ local data = self.libs[major] if not data then - _G.error(string.format("Cannot find a library instance of %s.", major), 2) + _G.error(("Cannot find a library instance of %s."):format(major), 2) return end if minor then @@ -425,12 +428,12 @@ if m then minor = m else - _G.error(string.format("Bad argument #3 to `GetInstance'. Must be a number or SVN revision string. %q is not appropriate", minor), 2) + _G.error(("Bad argument #3 to `GetInstance'. Must be a number or SVN revision string. %q is not appropriate"):format(minor), 2) end end argCheck(self, minor, 2, "number") if data.minor ~= minor then - _G.error(string.format("Cannot find a library instance of %s, minor version %d.", major, minor), 2) + _G.error(("Cannot find a library instance of %s, minor version %d."):format(major, minor), 2) end end return data.instance @@ -463,11 +466,11 @@ if major ~= ACELIBRARY_MAJOR then for k,v in pairs(_G) do if v == newInstance then - geterrorhandler()(string.format("Cannot register library %q. It is part of the global table in _G[%q].", major, k)) + geterrorhandler()((debugstack():match("(.-: )in.-\n") or "") .. ("Cannot register library %q. It is part of the global table in _G[%q]."):format(major, k)) end end end - if major ~= ACELIBRARY_MAJOR and not string.find(major, "^[%a%-][%a%d%-]+[%a%-]%-%d+%.%d+$") and not string.find(major, "^[%a%-]+%-%d+%.%d+$") then + if major ~= ACELIBRARY_MAJOR and not major:find("^[%a%-][%a%d%-]*%-%d+%.%d+$") then _G.error(string.format("Bad argument #3 to `Register'. Must be in the form of \"Name-1.0\". %q is not appropriate", major), 2) end if type(minor) == "string" then @@ -475,7 +478,7 @@ if m then minor = m else - _G.error(string.format("Bad argument #4 to `Register'. Must be a number or SVN revision string. %q is not appropriate", minor), 2) + _G.error(("Bad argument #4 to `Register'. Must be a number or SVN revision string. %q is not appropriate"):format(minor), 2) end end argCheck(self, minor, 4, "number") @@ -526,7 +529,7 @@ --[[ if major ~= ACELIBRARY_MAJOR then for k,v in pairs(_G) do if v == instance then - geterrorhandler()(string.format("Cannot register library %q. It is part of the global table in _G[%q].", major, k)) + geterrorhandler()((debugstack():match("(.-: )in.-\n") or "") .. ("Cannot register library %q. It is part of the global table in _G[%q]."):format(major, k)) end end end]] @@ -557,7 +560,7 @@ local instance = data.instance if minor <= data.minor then -- This one is already obsolete, raise an error. - _G.error(string.format("Obsolete library registered. %s is already registered at version %d. You are trying to register version %d. Hint: if not AceLibrary:IsNewVersion(%q, %d) then return end", major, data.minor, minor, major, minor), 2) + _G.error(("Obsolete library registered. %s is already registered at version %d. You are trying to register version %d. Hint: if not AceLibrary:IsNewVersion(%q, %d) then return end"):format(major, data.minor, minor, major, minor), 2) return end -- This is an update @@ -623,13 +626,13 @@ if activateFunc then safecall(activateFunc, instance, oldInstance, oldDeactivateFunc) - if major ~= ACELIBRARY_MAJOR then +--[[ if major ~= ACELIBRARY_MAJOR then for k,v in pairs(_G) do if v == instance then - geterrorhandler()(string.format("Cannot register library %q. It is part of the global table in _G[%q].", major, k)) + geterrorhandler()((debugstack():match("(.-: )in.-\n") or "") .. ("Cannot register library %q. It is part of the global table in _G[%q]."):format(major, k)) end end - end + end]] else safecall(oldDeactivateFunc, oldInstance) end
--- a/libs/AceLocale-2.2/AceLocale-2.2.lua Tue Mar 20 21:20:20 2007 +0000 +++ b/libs/AceLocale-2.2/AceLocale-2.2.lua Tue Mar 20 21:25:29 2007 +0000 @@ -1,6 +1,6 @@ --[[ Name: AceLocale-2.2 -Revision: $Rev: 18708 $ +Revision: $Rev$ Developed by: The Ace Development Team (http://www.wowace.com/index.php/The_Ace_Development_Team) Inspired By: Ace 1.x by Turan (turan@gryphon.com) Website: http://www.wowace.com/ @@ -9,10 +9,11 @@ Description: Localization library for addons to use to handle proper localization and internationalization. Dependencies: AceLibrary +License: LGPL v2.1 ]] local MAJOR_VERSION = "AceLocale-2.2" -local MINOR_VERSION = "$Revision: 18708 $" +local MINOR_VERSION = "$Revision: 20000 $" if not AceLibrary then error(MAJOR_VERSION .. " requires AceLibrary.") end if not AceLibrary:IsNewVersion(MAJOR_VERSION, MINOR_VERSION) then return end @@ -216,6 +217,17 @@ end end rawset(self, CURRENT_LOCALE, locale) + if not rawget(self, 'reverse') then + rawset(self, 'reverse', setmetatable({}, { __index = function(self2, key) + local self = AceLocale.reverseToBase[self2] + if not rawget(self, REVERSE_TRANSLATIONS) then + self:GetReverseTranslation(key) + end + self.reverse = self[REVERSE_TRANSLATIONS] + return self.reverse[key] + end })) + AceLocale.reverseToBase[self.reverse] = self + end refixInstance(self) if rawget(self, DEBUGGING) or rawget(self, DYNAMIC_LOCALES) then if not rawget(self, TRANSLATION_TABLES) then @@ -297,7 +309,9 @@ end local function initReverse(self) - rawset(self, REVERSE_TRANSLATIONS, {}) + rawset(self, REVERSE_TRANSLATIONS, setmetatable({}, { __index = function(_, key) + AceLocale.error(self, "Reverse translation for %q does not exist", key) + end })) local alpha = self[TRANSLATIONS] local bravo = self[REVERSE_TRANSLATIONS] for base, localized in pairs(alpha) do @@ -380,7 +394,7 @@ initReverse(self) x = self[REVERSE_TRANSLATIONS] end - return x[text] and true + return rawget(x, text) and true end function AceLocale.prototype:Debug() @@ -393,7 +407,7 @@ DEFAULT_CHAT_FRAME:AddMessage("--- AceLocale Debug ---") for _,locale in ipairs(locales) do if not self[TRANSLATION_TABLES][locale] then - DEFAULT_CHAT_FRAME:AddMessage(string.format("Locale %q not found", locale)) + DEFAULT_CHAT_FRAME:AddMessage(("Locale %q not found"):format(locale)) else localizations[locale] = self[TRANSLATION_TABLES][locale] end @@ -435,11 +449,11 @@ end for locale, t in pairs(localeDebug) do if not next(t) then - DEFAULT_CHAT_FRAME:AddMessage(string.format("Locale %q complete", locale)) + DEFAULT_CHAT_FRAME:AddMessage(("Locale %q complete"):format(locale)) else - DEFAULT_CHAT_FRAME:AddMessage(string.format("Locale %q missing:", locale)) + DEFAULT_CHAT_FRAME:AddMessage(("Locale %q missing:"):format(locale)) for word in pairs(t) do - DEFAULT_CHAT_FRAME:AddMessage(string.format(" %q", word)) + DEFAULT_CHAT_FRAME:AddMessage((" %q"):format(word)) end end end @@ -470,6 +484,7 @@ self.NAME = oldLib and oldLib.NAME or {} self.DYNAMIC_LOCALES = oldLib and oldLib.DYNAMIC_LOCALES or {} self.CURRENT_LOCALE = oldLib and oldLib.CURRENT_LOCALE or {} + self.reverseToBase = oldLib and oldLib.reverseToBase or {} BASE_TRANSLATIONS = self.BASE_TRANSLATIONS DEBUGGING = self.DEBUGGING
--- a/locale-enUS.lua Tue Mar 20 21:20:20 2007 +0000 +++ b/locale-enUS.lua Tue Mar 20 21:25:29 2007 +0000 @@ -1,10 +1,25 @@ -- English localization file for ReAction -local L = AceLibrary("AceLocale-2.0"):new("ReAction") +local L = AceLibrary("AceLocale-2.2"):new("ReAction") -L:RegisterTranslations("enUS", function() - return { - ["Lock"] = true, - } -end -) +L:RegisterTranslations( "enUS", function() +return { + -- main.lua + ["ReAction"] = true, + ["Toggle ReAction Bar Lock"] = true, + ["Toggle ReBound Keybinding Mode"] = true, + ["/reaction"] = true, + ["/rxn"] = true, + ["ReAction bars locked when in combat"] = true, + ["Bar lock"] = true, + ["Locked"] = true, + ["Unlocked"] = true, + ["Button lock"] = true, + ["On"] = true, + ["Off"] = true, + ["Kebinding mode"] = true, + ["Tried to create a button of unknown type"] = true, + ["|cffffcc00Shift-Click for bar lock|n|cff33ff33Alt-Click|r for keybindings|nRight-click for menu"] = true, + +} +end )
--- a/main.lua Tue Mar 20 21:20:20 2007 +0000 +++ b/main.lua Tue Mar 20 21:25:29 2007 +0000 @@ -1,23 +1,16 @@ --- ReAction.lua +-- main.lua -- -- Top-level file for the ReAction Action Bar add-on -- --- ReAction is implemented in terms of the Ace 2 library: http://www.wowace.com +-- implemented in terms of the Ace 2 development framework library: http://www.wowace.com -- --- key binding label constants -BINDING_HEADER_REACTION = "ReAction" -BINDING_NAME_REACTION_TOGGLELOCK = "Lock/Unlock ReAction Bars" -BINDING_NAME_REBINDER_TOGGLEBINDINGMODE = "Toggle ReAction keybinding mode" +-- Ace Library local object initialization +local L = AceLibrary("AceLocale-2.2"):new("ReAction") +local dewdrop = AceLibrary("Dewdrop-2.0") +local tablet = AceLibrary("Tablet-2.0") --- ReAction addon setup via Ace 2 -ReAction = AceLibrary("AceAddon-2.0"):new( - "AceConsole-2.0", - "AceEvent-2.0", - "AceDB-2.0", - "FuBarPlugin-2.0" -) - +-- private functions local function tcopy(t) local r = { } for k, v in pairs(t) do @@ -26,109 +19,180 @@ return r end --- FuBar plugin setup -ReAction.hasIcon = false -ReAction.hasNoColor = true -ReAction.hideMenuTitle = true -ReAction.defaultPosition = "RIGHT" -ReAction.defaultMinimapPosition = 240 -- degrees -ReAction.OnMenuRequest = tcopy(ReActionGlobalMenuOptions) +-- private constants +local EMPTY_BAR_SLOT = -1 + +-- key binding label constants +BINDING_HEADER_REACTION = L["ReAction"] +BINDING_NAME_REACTION_TOGGLELOCK = L["Toggle ReAction Bar Lock"] +BINDING_NAME_REBOUND_TOGGLEBINDINGMODE = L["Toggle ReBound Keybinding Mode"] + + +-- main object +local main = AceLibrary("AceAddon-2.0"):new( + "AceConsole-2.0", + "AceEvent-2.0", + "AceDB-2.0", + "FuBarPlugin-2.0" +) -- initial non-persistent state -ReAction.locked = true +main.locked = true --- localization --- local L = AceLibrary("AceLocale-2.0"):new("ReAction") +-- set a global variable for Bindings.xml +ReActionAddOn = main + + +-- FuBar plugin setup +-- Even if FuBar isn't installed, this gives us a nice minimap-button interface. +main.hasIcon = "Interface\\Icons\\INV_Qiraj_JewelEncased" +main.hasNoColor = true +main.hideMenuTitle = true +main.defaultPosition = "LEFT" +main.defaultMinimapPosition = 240 -- degrees +main.OnMenuRequest = tcopy(ReActionGlobalMenuOptions) -- use a copy, or bar menus will have FuBar inserted items +main.independentProfile = true + +-- set the handler for the global bar menu options +-- have to do this after tcopy() above, otherwise it will try to copy the handler object (bad idea) +ReActionGlobalMenuOptions.handler = main + -- Event handling -function ReAction:OnInitialize() - self:RegisterChatCommand( {"/reaction", "/rxn"}, ReActionConsoleOptions, "REACTION" ) - +function main:OnInitialize() + self:RegisterChatCommand( {L["/reaction"], L["/rxn"]}, ReActionConsoleOptions, "REACTION" ) self:RegisterDB("ReActionDB","ReActionDBPC") - self:RegisterDefaults("profile", ReActionProfileDefaults) + self:RegisterDefaults("profile", ReAction_DefaultProfile) self:RegisterEvent("PLAYER_REGEN_DISABLED","CombatLockdown") self:RegisterEvent("PLAYER_ENTERING_WORLD","HideDefaultBars") + self:RegisterEvent("EVENT_REBOUND_KEYBINDING_MODE") end -function ReAction:OnEnable() +function main:OnEnable() + -- this gets called at startup and when the profile is changed if self.db.profile.firstRunDone ~= true then -- Do some "first-run" setup + self:StealKeyBindings() self.db.profile.firstRunDone = true - elseif self.db.profile.disabled == true then - -- print some kind of a warning + self.db.profile.bars = tcopy(ReAction_DefaultBlizzardBars) end + self:DestroyAllBars() self:SetupBars() end -function ReAction:OnDisable() +function main:OnDisable() self:Lock() end -function ReAction:OnProfileEnable() - -- handle profile switching - self:Lock() - self:SetupBars() +function main:OnProfileEnable() + -- for profile switching + self:OnEnable() end -function ReAction:CombatLockdown() +function main:CombatLockdown() if not self:IsLocked() then self:Lock() - UIErrorsFrame:AddMessage("ReAction bars locked when in combat") + ReBound:Disable() + UIErrorsFrame:AddMessage(L["ReAction bars locked when in combat"]) end end +function main:EVENT_REBOUND_KEYBINDING_MODE(enabled) + for _, bar in pairs(self.bars) do + for __, button in pairs(bar.buttons) do + button:TempShow(enabled) + end + end +end --- lock/unlock ReAction -function ReAction:SetLocked( lock ) + +-- FuBar plugin methods +function main:OnTooltipUpdate() + local c = tablet:AddCategory("columns", 2) + c:AddLine("text", L["Bar lock"], "text2", self.locked and ("|cffff0000"..L["Locked"].."|r") or ("|cffffcc00"..L["Unlocked"].."|r")) + c:AddLine("text", L["Button lock"], "text2", LOCK_ACTIONBAR == "1" and ("|cffcc0000"..L["Locked"].."|r") or ("|cff00cc00"..L["Unlocked"].."|r")) + c:AddLine("text", L["Kebinding mode"], "text2", ReBound:IsEnabled() and ("|cff33ff33"..L["On"].."|r") or ("|cffffcc00"..L["Off"].."|r")) + tablet:SetHint(L["|cffffcc00Shift-Click for bar lock|n|cff33ff33Alt-Click|r for keybindings|nRight-click for menu"]) +end + +function main:OnClick(button) + if IsShiftKeyDown() then + self:ToggleLocked() + self:UpdateDisplay() + elseif IsAltKeyDown() then + ReBound:ToggleEnabled() + self:UpdateDisplay() + end +end + + +-- lock/unlock bars +function main:SetLocked( lock ) if lock ~= self.locked then - if not lock then - self:Print("Buttons disabled while unlocked") - end if not lock and InCombatLockdown() then UIErrorsFrame:AddMessage(SPELL_FAILED_AFFECTING_COMBAT) else self.locked = lock and true or false -- force data integrity for _, bar in pairs(self.bars) do - if self.locked then bar:HideControls() else bar:ShowControls() end + if bar ~= EMPTY_BAR_SLOT then + if self.locked then + bar:HideControls() + -- close any dewdrop menu owned by the bar + if bar:GetControlFrame() == dewdrop:GetOpenedParent() then + dewdrop:Close() + end + else + bar:ShowControls() + end + end end end end end -function ReAction:IsLocked() +function main:IsLocked() return self.locked end -function ReAction:Lock() +function main:Lock() self:SetLocked(true) end -function ReAction:Unlock() +function main:Unlock() self:SetLocked(false) end -function ReAction:ToggleLocked() - ReAction:SetLocked( not(self.locked) ) +function main:ToggleLocked() + main:SetLocked( not(self.locked) ) end -- Hide the default Blizzard main bar artwork -function ReAction:HideArt() +function main:HideArt() if self.db.profile.hideArt then + -- the pet bar is a child of MainMenuBar, and can't be hidden. Need to reparent it + PetActionBarFrame:SetParent(UIParent) MainMenuBar:Hide() -- this also hides the bags, xp bar, lag meter, and micro menu buttons. + -- these two are the pet bar background + -- unfortunately UIParent_ManageFramePositions() shows and hides these too + -- so they get reparented to MainMenuBar + SlidingActionBarTexture0:SetParent(MainMenuBar) + SlidingActionBarTexture1:SetParent(MainMenuBar) else + SlidingActionBarTexture0:SetParent(PetActionBarFrame) + SlidingActionBarTexture1:SetParent(PetActionBarFrame) MainMenuBar:Show() end end -function ReAction:IsArtHidden() +function main:IsArtHidden() return self.db.profile.hideArt end -function ReAction:SetHideArt( hide ) +function main:SetHideArt( hide ) if InCombatLockdown() then UIErrorsFrame:AddMessage(SPELL_FAILED_AFFECTING_COMBAT) else @@ -137,27 +201,12 @@ end end -function ReAction:ToggleHideArt() +function main:ToggleHideArt() self:SetHideArt( not self:IsArtHidden() ) end --- Keybinding color coding -function ReAction:SetKeyColorCoding( cc ) - self.db.profile.keyColorCode = cc -end - -function ReAction:IsKeyColorCodeEnabled() - return self.db.profile.keyColorCode -end - -function ReAction:ToggleKeyColorCoding() - self:SetKeyColorCoding(not self.db.profile.keyColorCode) -end - - - -- Hide default Blizzard bars local blizzDefaultBars = { ActionButton1, @@ -172,143 +221,216 @@ ActionButton10, ActionButton11, ActionButton12, + PetActionButton1, + PetActionButton2, + PetActionButton3, + PetActionButton4, + PetActionButton5, + PetActionButton6, + PetActionButton7, + PetActionButton8, + PetActionButton9, + PetActionButton10, + -- NOT the PetActionBarFrame, though - we need that to auto-hide/show our pet action bars + MainMenuBarPageNumber, + ActionBarUpButton, + ActionBarDownButton, BonusActionBarFrame, + ShapeshiftBarFrame, MultiBarLeft, MultiBarRight, MultiBarBottomLeft, - MultiBarBottomRight + MultiBarBottomRight, } -function ReAction:HideDefaultBars() - for _, f in pairs(blizzDefaultBars) do - f:UnregisterAllEvents() - f:Hide() - f:SetParent(ReActionButtonRecycler) -- I mean it! - f:ClearAllPoints() -- no, I really mean it! +function main:StealKeyBindings() + -- steal the keybindings of the main action bar and assign them to rebar 1, buttons 1-12 + for i = 1, 12 do + -- TODO: when we convert to override bindings end end +local function disableUIOptions() + -- disable the buttons to hide/show the blizzard multiaction bars + -- see UIOptionsFrame.lua and .xml + -- This is called every time the options panel is shown, after it is set up + for _, idx in pairs( { 33, 34, 35, 36, 37, 40 } ) do + local f = getglobal("UIOptionsFrameCheckButton"..idx) + f.disabled = true + OptionsFrame_DisableCheckBox(f) + f:SetChecked(false) + end +end --- Reset bars to defaults -function ReAction:ResetBars() +function main:HideDefaultBars() + for _, f in pairs(blizzDefaultBars) do + f:Hide() + f:ClearAllPoints() + f:SetParent(ReAction.recycler) + f:SetPoint("TOPLEFT") + end + + MainMenuBar:SetFrameStrata("LOW") -- otherwise it appears on top of bars, if it isn't hidden + hooksecurefunc("UIOptionsFrame_Load",disableUIOptions) +end + + +-- Reset bars to blizzard defaults +function main:ResetBars() if InCombatLockdown() then UIErrorsFrame:AddMessage(SPELL_FAILED_AFFECTING_COMBAT) else - self.db.profile.bars = ReActionProfileDefaults.bars + self:DestroyAllBars() + self.db.profile.bars = tcopy(ReAction_DefaultBlizzardBars) self:SetupBars() end end -- re-sync action IDs -function ReAction:ResyncActionIDs() +function main:ResyncActionIDs() -- TODO end -- Bar manipulation -ReAction.bars = { } +main.bars = { } -function ReAction:SetupBars() +function main:DestroyAllBars() + -- destroy any existing bars + for id = 1, table.maxn(self.bars) do + self:DestroyBar(id) + end +end + + +function main:SetupBars() -- hide the default Blizzard art, if configued self:HideArt() - -- hide the default Blizzard bars - self:HideDefaultBars() -- set up the bars from the profile -- note the use of table.maxn rather than # or ipairs: -- our array of bars can in fact contain holes for id = 1, table.maxn(self.db.profile.bars) do local config = self.db.profile.bars[id] - if self.bars[id] then - self.bars[id]:Destroy() -- remove old version of bar if switching profiles - end if config then - self.bars[id] = ReBar:new(config, id) - end - end - - -- remove excess bars - for id = table.maxn(self.db.profile.bars) + 1, table.maxn(self.bars) do - if self.bars[id] then - self.bars[id]:Destroy() - self.bars[id] = nil + self:CreateBar(config, id) end end -- anchor the bars, have to do this in a second pass because -- they might be anchored to each other in a non-ordered way for _, bar in pairs(self.bars) do - bar:ApplyAnchor() - end -end - - -function ReAction:NewBar() - if InCombatLockdown() then - UIErrorsFrame:AddMessage(SPELL_FAILED_AFFECTING_COMBAT) - else - local c = tcopy(ReActionBarConfigDefaults) - local bar = ReBar:new(c, #self.bars+1) - table.insert(self.bars, bar) - table.insert(self.db.profile.bars, c) - if not self.locked then - bar:ShowControls() + if bar ~= EMPTY_BAR_SLOT then + bar:ApplyAnchor() end end end +function main:CreateBar( config, id ) + local bar = ReBar:new(config, id) + local buttonType = ReAction:GetButtonType(config.btnConfig.subtype) -function ReAction:DeleteBar(id) + if buttonType then + self.bars[id] = bar + self.db.profile.bars[id] = config + + -- initialize dewdrop menu + local cf = bar:GetControlFrame() + dewdrop:Register(cf, + 'children', + function() + dewdrop:FeedAceOptionsTable(ReActionGlobalMenuOptions) + dewdrop:FeedAceOptionsTable(GenerateReActionBarOptions(bar,self)) + dewdrop:FeedAceOptionsTable(buttonType:GenerateOptionsTable(config.btnConfig, function() return bar:GetButtonList() end)) + end, + 'cursorX', true, + 'cursorY', true + ) + + bar:GetControlFrame():SetScript("OnClick", + function(btn) + if btn == "RightButton" then + dewdrop:Open(cf) + end + end + ) + + if not self.locked then + bar:ShowControls() + end + return bar + else + if bar then + bar:Destroy() + end + error(L["Tried to create a button of unknown type"]) + end +end + +function main:DestroyBar( id ) + local bar = self.bars[id] + if bar and bar ~= EMPTY_BAR_SLOT then + local cf = bar:GetControlFrame() + if cf == dewdrop:GetOpenedParent() then + dewdrop:Close() + dewdrop:Unregister(cf) + end + bar:Destroy() + -- we can't do tremove because each bar ID is encoded into the + -- frame names as they're created. Need a blank entry in the table. + -- The nice thing is that table.insert in NewBar() will automatically + -- find the lowest numbered nil slot. + self.bars[id] = EMPTY_BAR_SLOT + end +end + +function main:NewBar( type ) + if InCombatLockdown() then + UIErrorsFrame:AddMessage(SPELL_FAILED_AFFECTING_COMBAT) + else + local t = ReAction:GetButtonType(type) + if t then + local c = tcopy(ReAction_DefaultBarConfig["ReAction"][type]) + local id = nil + for i = 1, table.maxn(self.bars) + 1 do -- there may be holes, so #self.bars won't work + if self.bars[i] == nil or self.bars[i] == EMPTY_BAR_SLOT then + id = i + break + end + end + local bar = self:CreateBar(c, id) + bar:ApplyAnchor() + self:Unlock() + end + end +end + + +function main:DeleteBar(id) if InCombatLockdown() then UIErrorsFrame:AddMessage(SPELL_FAILED_AFFECTING_COMBAT) else if self.bars[id] then - -- we can't do tremove because each bar ID is encoded into the - -- frame names as they're created. Need a nil entry in the table. - -- The nice thing is that table.insert in NewBar() will automatically - -- find the first nil slot. - self.bars[id]:Destroy() - self.bars[id] = nil + self:DestroyBar(id) self.db.profile.bars[id] = nil end end end -function ReAction:ToggleActionID() - if self.showActionIDs then - ReActionButton:HideAllActionIDs() +function main:ToggleIds() + if self.showIds then + ReAction:HideAllIds() else - ReActionButton:ShowAllActionIDs() + ReAction:ShowAllIds() end - self.showActionIDs = not self.showActionIDs + self.showIds = not self.showIds end -function ReAction:IsActionIDVisible() - return self.showActionIDs +function main:AreIdsVisible() + return self.showIds end --- FuBar plugin methods -local tablet = AceLibrary("Tablet-2.0") - -function ReAction:OnTooltipUpdate() - local c = tablet:AddCategory("columns", 2) - c:AddLine("text", "Bar lock", "text2", self.locked and "|cffcc0000Locked|r" or "|cff00cc00Unlocked|r") - c:AddLine("text", "Button lock", "text2", LOCK_ACTIONBAR == "1" and "|cffcc0000Locked|r" or "|cff00cc00Unlocked|r") - c:AddLine("text", "Kebinding mode", "text2", ReBinder:IsEnabled() and "|cff33ff33On|r" or "|cffffcc00Off|r") - tablet:SetHint("|cffffcc00Shift-Click|r for bar lock|n".. - "|cff33ff33Alt-Click|r for keybindings|n".. - "Right-click for menu") -end - -function ReAction:OnClick(button) - if IsShiftKeyDown() then - self:ToggleLocked() - self:UpdateDisplay() - elseif IsAltKeyDown() then - ReBinder:ToggleEnabled() - end -end