Mercurial > wow > reaction
changeset 109:410d036c43b2
- reorganize modularity file structure (part 1)
author | Flick <flickerstreak@gmail.com> |
---|---|
date | Thu, 08 Jan 2009 00:57:27 +0000 |
parents | b2fb8f7dc780 |
children | 711ed6b639ac |
files | Bar.lua Bindings.lua LDB.lua Overlay.lua ReAction.lua ReAction.xml State.lua modules/Action.lua modules/Bar.lua modules/Bindings.lua modules/Config.lua modules/HideBlizzard.lua modules/LBF.lua modules/LDB.lua modules/ModuleTemplate.lua modules/Overlay.lua modules/PetAction.lua modules/ReAction_Action/ReAction_Action.lua modules/ReAction_Action/ReAction_Action.toc modules/ReAction_Action/ReAction_Action.xml modules/ReAction_ButtonFacade/ReAction_ButtonFacade.lua modules/ReAction_ButtonFacade/ReAction_ButtonFacade.toc modules/ReAction_ButtonFacade/ReAction_ButtonFacade.xml modules/ReAction_HideBlizzard/ReAction_HideBlizzard.lua modules/ReAction_HideBlizzard/ReAction_HideBlizzard.toc modules/ReAction_HideBlizzard/ReAction_HideBlizzard.xml modules/ReAction_ModuleTemplate/ReAction_ModuleName.lua modules/ReAction_ModuleTemplate/ReAction_ModuleName.toc modules/ReAction_ModuleTemplate/ReAction_ModuleName.xml modules/ReAction_PetAction/ReAction_PetAction.lua modules/ReAction_PetAction/ReAction_PetAction.toc modules/ReAction_PetAction/ReAction_PetAction.xml modules/State.lua modules/modules.xml |
diffstat | 34 files changed, 5049 insertions(+), 4556 deletions(-) [+] |
line wrap: on
line diff
--- a/Bar.lua Wed Jan 07 00:57:02 2009 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,245 +0,0 @@ -local ReAction = ReAction -local L = ReAction.L -local _G = _G -local CreateFrame = CreateFrame -local floor = math.floor -local fmod = math.fmod -local format = string.format - -ReAction:UpdateRevision("$Revision$") - -local Bar = { } -local proto = { __index = Bar } -local weak = { __mode = "k" } - -ReAction.Bar = Bar -- export to ReAction - -function Bar:New( name, config ) - if type(config) ~= "table" then - error("ReAction.Bar: config table required") - end - - -- create new self - self = setmetatable( - { - config = config, - name = name, - buttons = setmetatable( { }, weak ), - width = config.width or 480, - height = config.height or 40 - }, - proto ) - - -- The frame type is 'Button' in order to have an OnClick handler. However, the frame itself is - -- not mouse-clickable by the user. - local parent = config.parent and (ReAction:GetBar(config.parent) or _G[config.parent]) or UIParent - local f = CreateFrame("Button", name and format("ReAction-%s",name), parent, - "SecureHandlerStateTemplate, SecureHandlerClickTemplate") - f:SetFrameStrata("MEDIUM") - f:SetWidth(self.width) - f:SetWidth(self.height) - f:SetAlpha(config.alpha or 1.0) - f:Show() - f:EnableMouse(false) - f:SetClampedToScreen(true) - - -- Override the default frame accessor to provide strict read-only access - function self:GetFrame() - return f - end - - self:ApplyAnchor() - ReAction.RegisterCallback(self, "OnConfigModeChanged") - - return self -end - -function Bar:Destroy() - local f = self:GetFrame() - f:UnregisterAllEvents() - ReAction.UnregisterAllCallbacks(self) - f:Hide() - f:SetParent(UIParent) - f:ClearAllPoints() -end - -function Bar:OnConfigModeChanged(event, mode) - self:ShowControls(mode) -- Bar:ShowControls() defined in Overlay.lua -end - -function Bar:ApplyAnchor() - local f = self:GetFrame() - local c = self.config - local p = c.point - - f:SetWidth(c.width) - f:SetHeight(c.height) - f:ClearAllPoints() - - if p then - local a = f:GetParent() - if c.anchor then - local bar = ReAction:GetBar(c.anchor) - if bar then - a = bar:GetFrame() - else - a = _G[c.anchor] - end - end - local fr = a or f:GetParent() - f:SetPoint(p, a or f:GetParent(), c.relpoint, c.x or 0, c.y or 0) - else - f:SetPoint("CENTER") - end -end - -function Bar:SetAnchor(point, frame, relativePoint, x, y) - local c = self.config - c.point = point or c.point - c.anchor = frame or c.anchor - c.relpoint = relativePoint or c.relpoint - c.x = x or c.x - c.y = y or c.y - self:ApplyAnchor() - ReAction:RefreshBar(self) -end - -function Bar:GetAnchor() - local c = self.config - return (c.point or "CENTER"), - (c.anchor or self:GetFrame():GetParent():GetName()), - (c.relpoint or c.point or "CENTER"), - (c.x or 0), - (c.y or 0) -end - -function Bar:GetSize() - local f = self:GetFrame() - return f:GetWidth(), f:GetHeight() -end - -function Bar:SetSize(w,h) - local f = self:GetFrame() - self.config.width = w - self.config.height = h - f:SetWidth(w) - f:SetHeight(h) -end - -function Bar:GetButtonSize() - local w = self.config.btnWidth or 32 - local h = self.config.btnHeight or 32 - -- TODO: get from modules? - return w,h -end - -function Bar:SetButtonSize(w,h) - if w > 0 and h > 0 then - self.config.btnWidth = w - self.config.btnHeight = h - end - ReAction:RefreshBar(self) -end - -function Bar:GetButtonGrid() - local cfg = self.config - local r = cfg.btnRows or 1 - local c = cfg.btnColumns or 1 - local s = cfg.spacing or 4 - return r,c,s -end - -function Bar:GetNumButtons() - local r,c = self:GetButtonGrid() - return r*c -end - -function Bar:SetButtonGrid(r,c,s) - if r > 0 and c > 0 and s > 0 then - local cfg = self.config - cfg.btnRows = r - cfg.btnColumns = c - cfg.spacing = s - end - ReAction:RefreshBar(self) -end - -function Bar:ClipNButtons( n ) - local cfg = self.config - local r = cfg.btnRows or 1 - local c = cfg.btnColumns or 1 - - cfg.btnRows = ceil(n/c) - cfg.btnColumns = min(n,c) -end - -function Bar:GetName() - return self.name -end - -function Bar:GetFrame() - -- this method is included for documentation purposes. It is overridden - -- for each object in the :New() method. - error("Invalid Bar object: used without initialization") -end - --- only ReAction:RenameBar() should call this function. Calling from any other --- context will desync the bar list in the ReAction class. -function Bar:SetName(name) - self.name = name - self:SetLabel(self.name) -- Bar:SetLabel() defined in Overlay.lua -end - -function Bar:GetAlpha() - return self.config.alpha or 1.0 -end - -function Bar:SetAlpha(value) - self.config.alpha = value - self:GetFrame():SetAlpha(value or 1.0) - ReAction:RefreshBar(self) -end - -function Bar:AddButton(idx, button) - local f = self:GetFrame() - - -- store in a weak reverse-index array - self.buttons[button] = idx - - -- Store a properly wrapped reference to the child frame as an attribute - -- (accessible via "frameref-btn#") - f:SetFrameRef(format("btn%d",idx), button:GetFrame()) -end - -function Bar:RemoveButton(button) - local idx = self.buttons[button] - if idx then - self:GetFrame():SetAttribute(format("frameref-btn%d",idx),nil) - self.buttons[button] = nil - end -end - --- iterator returns button, idx and does NOT iterate in index order -function Bar:IterateButtons() - return pairs(self.buttons) -end - -function Bar:PlaceButton(button, baseW, baseH) - local idx = self.buttons[button] - if idx then - local r, c, s = self:GetButtonGrid() - local bh, bw = self:GetButtonSize() - local row, col = floor((idx-1)/c), fmod((idx-1),c) -- zero-based - local x, y = col*bw + (col+0.5)*s, -(row*bh + (row+0.5)*s) - local scale = bw/baseW - local b = button:GetFrame() - - b:ClearAllPoints() - b:SetPoint("TOPLEFT",x/scale,y/scale) - b:SetScale(scale) - end -end - -function Bar:SkinButton() - -- does nothing by default -end \ No newline at end of file
--- a/Bindings.lua Wed Jan 07 00:57:02 2009 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,7 +0,0 @@ -local L = LibStub("AceLocale-3.0"):GetLocale("ReAction") -local _G = _G - -_G["BINDING_HEADER_REACTION"] = L["ReAction"] - -_G["BINDING_NAME_REACTION_TOGGLELOCK"] = L["Toggle ReAction Bar Lock"] -_G["BINDING_NAME_REACTION_TOGGLEKEYBIND"] = L["ReAction Keybinding Mode"]
--- a/LDB.lua Wed Jan 07 00:57:02 2009 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,36 +0,0 @@ -local ReAction = ReAction -local L = ReAction.L -local format = string.format - --- Export ReAction launcher to LibDataBroker-aware displays -LibStub:GetLibrary("LibDataBroker-1.1"):NewDataObject( "ReAction", - { - type = "launcher", - icon = "Interface\\Icons\\INV_Qiraj_JewelEncased", - - OnClick = function( frame, button ) - if not InCombatLockdown() then - if IsAltKeyDown() then - ReAction:SetKeybindMode( not ReAction:GetKeybindMode() ) - elseif IsShiftKeyDown() then - ReAction:SetConfigMode( not ReAction:GetConfigMode() ) - elseif button == "RightButton" then - ReAction:ShowEditor() - else - ReAction:ShowConfig() - end - else - ReAction:UserError(L["ReAction: can't configure in combat"]) - end - end, - - -- this isn't included in the 'launcher' type LDB spec but it seems all launcher displays use it - OnTooltipShow = function( tooltip ) - tooltip:AddLine(format("|cffffffff%s|r %s",L["Click"],L["for global configuration"])) - tooltip:AddLine(format("|cffffd200%s|r %s",L["Right-click"],L["for bar editor dialog"])) - tooltip:AddLine(format("|cff00ff00%s|r %s",L["Shift-click"],L["to unlock bars"])) - tooltip:AddLine(format("|cff00cccc%s|r %s",L["Alt-click"],L["for keybind mode"])) - end, - - } -)
--- a/Overlay.lua Wed Jan 07 00:57:02 2009 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,769 +0,0 @@ -local ReAction = ReAction -local L = ReAction.L -local CreateFrame = CreateFrame -local InCombatLockdown = InCombatLockdown -local floor = math.floor -local min = math.min -local format = string.format -local GameTooltip = GameTooltip -local Bar = ReAction.Bar -local GetSize = Bar.GetSize -local SetSize = Bar.SetSize -local GetButtonSize = Bar.GetButtonSize -local GetButtonGrid = Bar.GetButtonGrid -local SetButtonSize = Bar.SetButtonSize -local SetButtonGrid = Bar.SetButtonGrid -local ApplyAnchor = Bar.ApplyAnchor -local GameTooltipTextRight1 = GameTooltipTextRight1 -local GameTooltipTextRight2 = GameTooltipTextRight2 -local GameTooltipTextRight3 = GameTooltipTextRight3 - -local KB = LibStub("LibKeyBound-1.0") - -ReAction:UpdateRevision("$Revision$") - - --- --- Wrap some of the bar manipulators to make them state-aware --- -local function SetAnchor( bar, point, frame, relPoint, x, y ) - local state = bar:GetState() - if state then - local anchorstate = bar:GetStateProperty(state, "anchorEnable") - if anchorstate then - bar:SetStateProperty(state, "anchorFrame", frame) - bar:SetStateProperty(state, "anchorPoint", point) - bar:SetStateProperty(state, "anchorRelPoint", relPoint) - bar:SetStateProperty(state, "anchorX", x or 0) - bar:SetStateProperty(state, "anchorY", y or 0) - bar:SetAnchor(bar:GetAnchor()) - return - end - end - bar:SetAnchor(point, frame, relPoint, x, y) -end - -local function GetStateScale( bar ) - local state = bar:GetState() - if state and bar:GetStateProperty(state, "enableScale") then - return bar:GetStateProperty(state, "scale") - end -end - -local function SetStateScale( bar, scale ) - local state = bar:GetState() - if state and bar:GetStateProperty(state, "enableScale") then - bar:SetStateProperty(state, "scale", scale) - end -end - - --- --- Bar config overlay --- - -local function GetNormalTextColor() - return 1.0, 1.0, 1.0, 1.0 -end - -local function GetAnchoredTextColor() - return 1.0, 1.0, 1.0, 1.0 -end - -local function GetNormalBgColor() - return 0.7, 0.7, 1.0, 0.3 -end - -local function GetAnchoredBgColor() - return 0.9, 0.2, 0.7, 0.3 -end - -local function StoreSize(bar) - local f = bar:GetFrame() - SetSize( bar, f:GetWidth(), f:GetHeight() ) -end - -local function StoreExtents(bar) - local f = bar:GetFrame() - local p, fr, rp, x, y = f:GetPoint(1) - fr = fr and fr:GetName() or "UIParent" - SetAnchor( bar, p, fr, rp, x, y ) - SetSize( bar, f:GetWidth(), f:GetHeight() ) -end - -local function RecomputeButtonSize(bar) - local w, h = GetSize(bar) - local bw, bh = GetButtonSize(bar) - local r, c, s = GetButtonGrid(bar) - - local scaleW = (floor(w/c) - s) / bw - local scaleH = (floor(h/r) - s) / bh - local scale = min(scaleW, scaleH) - - SetButtonSize(bar, scale * bw, scale * bh, s) -end - -local function ComputeBarScale(bar) - local w, h = bar.controlFrame:GetWidth() - 8, bar.controlFrame:GetHeight() - 8 - local bw, bh = GetButtonSize(bar) - local r, c, s = GetButtonGrid(bar) - - local scaleW = w / (c*(bw+s)) - local scaleH = h / (r*(bh+s)) - local scale = min(scaleW, scaleH) - - if scale > 2.5 then - scale = 2.5 - elseif scale < 0.25 then - scale = 0.25 - end - - return scale -end - -local function RecomputeButtonSpacing(bar) - local w, h = GetSize(bar) - local bw, bh = GetButtonSize(bar) - local r, c, s = GetButtonGrid(bar) - - SetButtonGrid(bar,r,c,min(floor(w/c) - bw, floor(h/r) - bh)) -end - -local function RecomputeGrid(bar) - local w, h = GetSize(bar) - local bw, bh = GetButtonSize(bar) - local r, c, s = GetButtonGrid(bar) - - SetButtonGrid(bar, floor(h/(bh+s)), floor(w/(bw+s)), s) -end - -local function ClampToButtons(bar) - local bw, bh = GetButtonSize(bar) - local r, c, s = GetButtonGrid(bar) - SetSize(bar, (bw+s)*c + 1, (bh+s)*r + 1) -end - -local function HideGameTooltip() - GameTooltip:Hide() -end - -local anchorInside = { inside = true } -local anchorOutside = { outside = true } -local edges = { "BOTTOM", "TOP", "LEFT", "RIGHT" } -local oppositeEdges = { - TOP = "BOTTOM", - BOTTOM = "TOP", - LEFT = "RIGHT", - RIGHT = "LEFT" -} -local pointsOnEdge = { - BOTTOM = { "BOTTOM", "BOTTOMLEFT", "BOTTOMRIGHT", }, - TOP = { "TOP", "TOPLEFT", "TOPRIGHT", }, - RIGHT = { "RIGHT", "BOTTOMRIGHT", "TOPRIGHT", }, - LEFT = { "LEFT", "BOTTOMLEFT", "TOPLEFT", }, -} -local edgeSelector = { - BOTTOM = 1, -- select x of x,y - TOP = 1, -- select x of x,y - LEFT = 2, -- select y of x,y - RIGHT = 2, -- select y of x,y -} -local snapPoints = { - [anchorOutside] = { - BOTTOMLEFT = {"BOTTOMRIGHT","TOPLEFT","TOPRIGHT"}, - BOTTOM = {"TOP"}, - BOTTOMRIGHT = {"BOTTOMLEFT","TOPRIGHT","TOPLEFT"}, - RIGHT = {"LEFT"}, - TOPRIGHT = {"TOPLEFT","BOTTOMRIGHT","BOTTOMLEFT"}, - TOP = {"BOTTOM"}, - TOPLEFT = {"TOPRIGHT","BOTTOMLEFT","BOTTOMRIGHT"}, - LEFT = {"RIGHT"}, - CENTER = {"CENTER"} - }, - [anchorInside] = { - BOTTOMLEFT = {"BOTTOMLEFT"}, - BOTTOM = {"BOTTOM"}, - BOTTOMRIGHT = {"BOTTOMRIGHT"}, - RIGHT = {"RIGHT"}, - TOPRIGHT = {"TOPRIGHT"}, - TOP = {"TOP"}, - TOPLEFT = {"TOPLEFT"}, - LEFT = {"LEFT"}, - CENTER = {"CENTER"} - } -} -local insidePointOffsetFuncs = { - BOTTOMLEFT = function(x, y) return x, y end, - BOTTOM = function(x, y) return 0, y end, - BOTTOMRIGHT = function(x, y) return -x, y end, - RIGHT = function(x, y) return -x, 0 end, - TOPRIGHT = function(x, y) return -x, -y end, - TOP = function(x, y) return 0, -y end, - TOPLEFT = function(x, y) return x, -y end, - LEFT = function(x, y) return x, 0 end, - CENTER = function(x, y) return x, y end, -} -local pointCoordFuncs = { - BOTTOMLEFT = function(f) return f:GetLeft(), f:GetBottom() end, - BOTTOM = function(f) return nil, f:GetBottom() end, - BOTTOMRIGHT = function(f) return f:GetRight(), f:GetBottom() end, - RIGHT = function(f) return f:GetRight(), nil end, - TOPRIGHT = function(f) return f:GetRight(), f:GetTop() end, - TOP = function(f) return nil, f:GetTop() end, - TOPLEFT = function(f) return f:GetLeft(), f:GetTop() end, - LEFT = function(f) return f:GetLeft(), nil end, - CENTER = function(f) return f:GetCenter() end, -} -local edgeBoundsFuncs = { - BOTTOM = function(f) return f:GetLeft(), f:GetRight() end, - LEFT = function(f) return f:GetBottom(), f:GetTop() end -} -edgeBoundsFuncs.TOP = edgeBoundsFuncs.BOTTOM -edgeBoundsFuncs.RIGHT = edgeBoundsFuncs.LEFT -local cornerTexCoords = { - -- ULx, ULy, LLx, LLy, URx, URy, LRx, LRy - TOPLEFT = { 1, 1, 1, 0, 0, 1, 0, 0 }, - TOPRIGHT = { 1, 0, 0, 0, 1, 1, 0, 1 }, - BOTTOMLEFT = { 0, 1, 1, 1, 0, 0, 1, 0 }, - BOTTOMRIGHT = { 0, 0, 0, 1, 1, 0, 1, 1 }, -} - --- Returns absolute coordinates x,y of the named point 'p' of frame 'f' -local function GetPointCoords( f, p ) - local x, y = pointCoordFuncs[p](f) - if not(x and y) then - local cx, cy = f:GetCenter() - x = x or cx - y = y or cy - end - return x, y -end - - --- Returns true if frame 'f1' can be anchored to frame 'f2' -local function CheckAnchorable( f1, f2 ) - -- can't anchor a frame to itself or to nil - if f1 == f2 or f2 == nil then - return false - end - - -- can always anchor to UIParent - if f2 == UIParent then - return true - end - - -- also can't do circular anchoring of frames - -- walk the anchor chain, which generally shouldn't be that expensive - -- (who nests draggables that deep anyway?) - for i = 1, f2:GetNumPoints() do - local _, f = f2:GetPoint(i) - if not f then f = f2:GetParent() end - return CheckAnchorable(f1,f) - end - - return true -end - --- Returns true if frames f1 and f2 specified edges overlap -local function CheckEdgeOverlap( f1, f2, e ) - local l1, u1 = edgeBoundsFuncs[e](f1) - local l2, u2 = edgeBoundsFuncs[e](f2) - return l1 <= l2 and l2 <= u1 or l2 <= l1 and l1 <= u2 -end - --- Returns true if point p1 on frame f1 overlaps edge e2 on frame f2 -local function CheckPointEdgeOverlap( f1, p1, f2, e2 ) - local l, u = edgeBoundsFuncs[e2](f2) - local x, y = GetPointCoords(f1,p1) - x = select(edgeSelector[e2], x, y) - return l <= x and x <= u -end - --- Returns the distance between corresponding edges. It is --- assumed that the passed in edges e1 and e2 are the same or opposites -local function GetEdgeDistance( f1, f2, e1, e2 ) - local x1, y1 = pointCoordFuncs[e1](f1) - local x2, y2 = pointCoordFuncs[e2](f2) - return math.abs((x1 or y1) - (x2 or y2)) -end - -local globalSnapTargets = { [UIParent] = anchorInside } - -local function GetClosestFrameEdge(f1,f2,a) - local dist, edge, opp - if f2:IsVisible() and CheckAnchorable(f1,f2) then - for _, e in pairs(edges) do - local o = a.inside and e or oppositeEdges[e] - if CheckEdgeOverlap(f1,f2,e) then - local d = GetEdgeDistance(f1, f2, e, o) - if not dist or (d < dist) then - dist, edge, opp = d, e, o - end - end - end - end - return dist, edge, opp -end - -local function GetClosestVisibleEdge( f ) - local r, o, e1, e2 - local a = anchorOutside - for _, b in ReAction:IterateBars() do - local d, e, opp = GetClosestFrameEdge(f,b:GetFrame(),a) - if d and (not r or d < r) then - r, o, e1, e2 = d, b:GetFrame(), e, opp - end - end - for f2, a2 in pairs(globalSnapTargets) do - local d, e, opp = GetClosestFrameEdge(f,f2,a2) - if d and (not r or d < r) then - r, o, e1, e2, a = d, f2, e, opp, a2 - end - end - return o, e1, e2, a -end - -local function GetClosestVisiblePoint(f1) - local f2, e1, e2, a = GetClosestVisibleEdge(f1) - if f2 then - local rsq, p, rp, x, y - -- iterate pointsOnEdge in order and use < to prefer edge centers to corners - for _, p1 in ipairs(pointsOnEdge[e1]) do - if CheckPointEdgeOverlap(f1,p1,f2,e2) then - for _, p2 in pairs(snapPoints[a][p1]) do - local x1, y1 = GetPointCoords(f1,p1) - local x2, y2 = GetPointCoords(f2,p2) - local dx = x1 - x2 - local dy = y1 - y2 - local rsq2 = dx*dx + dy*dy - if not rsq or rsq2 < rsq then - rsq, p, rp, x, y = rsq2, p1, p2, dx, dy - end - end - end - end - return f2, p, rp, x, y - end -end - -local function GetClosestPointSnapped(f1, rx, ry, xOff, yOff) - local o, p, rp, x, y = GetClosestVisiblePoint(f1) - local s = false - - local insideOffsetFunc = p and insidePointOffsetFuncs[p] - local coordFunc = p and pointCoordFuncs[p] - if not insideOffsetFunc or not coordFunc then - return - end - - local sx, sy = insideOffsetFunc(xOff or 0, yOff or 0) - local xx, yy = coordFunc(f1) - if xx and yy then - if math.abs(x) <= rx then - if math.abs(y) <= ry then - x = sx - y = sy - s = true - elseif CheckEdgeOverlap(f1,o,"LEFT") then - x = sx - s = true - end - elseif math.abs(y) <= ry and CheckEdgeOverlap(f1,o,"TOP") then - y = sy - s = true - end - elseif xx then - if math.abs(x) <= rx then - x = sx - s = true - if math.abs(y) <= ry then - y = sy - end - end - elseif yy then - if math.abs(y) <= ry then - y = sy - s = true - if math.abs(x) <= rx then - x = sx - end - end - end - - -- correct for some Lua oddities with doubles - if x == -0 then x = 0 end - if y == -0 then y = 0 end - - if s then - return o, p, rp, math.floor(x), math.floor(y) - end -end - -local function CreateSnapIndicator() - local si = CreateFrame("Frame",nil,UIParent) - si:SetFrameStrata("HIGH") - si:SetHeight(16) - si:SetWidth(16) - local tex = si:CreateTexture() - tex:SetAllPoints() - tex:SetTexture("Interface\\AddOns\\ReAction\\img\\lock") - tex:SetBlendMode("ADD") - tex:SetDrawLayer("OVERLAY") - return si -end - -local si1 = CreateSnapIndicator() -local si2 = CreateSnapIndicator() - -local function DisplaySnapIndicator( f, rx, ry, xOff, yOff ) - local o, p, rp, x, y, snap = GetClosestPointSnapped(f, rx, ry, xOff, yOff) - if o then - si1:ClearAllPoints() - si2:ClearAllPoints() - si1:SetPoint("CENTER", f, p, 0, 0) - local xx, yy = pointCoordFuncs[rp](o) - x = math.abs(x) <=rx and xx and 0 or x - y = math.abs(y) <=ry and yy and 0 or y - si2:SetPoint("CENTER", o, rp, x, y) - si1:Show() - si2:Show() - else - if si1:IsVisible() then - si1:Hide() - si2:Hide() - end - end - return o, p -end - -local function HideSnapIndicator() - if si1:IsVisible() then - si1:Hide() - si2:Hide() - end -end - -local function UpdateLabelString(bar) - local label = bar.controlLabelString - if label then - local name = bar.labelName - if name and bar.labelSubtext then - name = format("%s (%s)", name, bar.labelSubtext) - end - label:SetText(name or "") - end -end - -local function CreateControls(bar) - local f = bar:GetFrame() - - f:SetMovable(true) - f:SetResizable(true) - - -- child of UIParent so that alpha and scale doesn't propagate to it - local overlay = CreateFrame("Button", nil, UIParent) - overlay:EnableMouse(true) - overlay:SetFrameLevel(3) -- set it above the buttons - overlay:SetPoint("TOPLEFT", f, "TOPLEFT", -4, 4) - overlay:SetPoint("BOTTOMRIGHT", f, "BOTTOMRIGHT", 4, -4) - overlay:SetBackdrop({ - edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border", - tile = true, - tileSize = 16, - edgeSize = 16, - insets = { left = 0, right = 0, top = 0, bottom = 0 }, - }) - - -- textures - local bgTex = overlay:CreateTexture(nil,"BACKGROUND") - bgTex:SetTexture(0.7,0.7,1.0,0.2) - bgTex:SetPoint("TOPLEFT",4,-4) - bgTex:SetPoint("BOTTOMRIGHT",-4,4) - local hTex = overlay:CreateTexture(nil,"HIGHLIGHT") - hTex:SetTexture(0.7,0.7,1.0,0.2) - hTex:SetPoint("TOPLEFT",4,-4) - hTex:SetPoint("BOTTOMRIGHT",-4,4) - hTex:SetBlendMode("ADD") - local aTex = overlay:CreateTexture(nil,"ARTWORK") - aTex:SetTexture("Interface\\AddOns\\ReAction\\img\\lock") - aTex:SetWidth(16) - aTex:SetHeight(16) - aTex:Hide() - - -- label - local label = overlay:CreateFontString(nil,"OVERLAY","GameFontNormalLarge") - label:SetAllPoints() - label:SetJustifyH("CENTER") - label:SetShadowColor(0,0,0,1) - label:SetShadowOffset(3,-3) - label:SetTextColor(GetNormalTextColor()) - label:SetText(bar:GetName()) - label:Show() - bar.controlLabelString = label -- so that bar:SetLabel() can update it - - local function UpdateAnchorDecoration() - local point, anchor, relPoint, x, y = f:GetPoint(1) - if point then - local ofsx, ofsy = insidePointOffsetFuncs[point](x,y) - if (anchor and anchor ~= UIParent) or (ofsx == 0 and ofsy == 0) then - bgTex:SetTexture( GetAnchoredBgColor() ) - hTex:SetTexture( GetAnchoredBgColor() ) - label:SetTextColor( GetAnchoredTextColor() ) - aTex:ClearAllPoints() - aTex:SetPoint(point) - aTex:Show() - return - end - end - bgTex:SetTexture( GetNormalBgColor() ) - hTex:SetTexture( GetNormalBgColor() ) - label:SetTextColor( GetNormalTextColor() ) - aTex:Hide() - end - - local function StopResize() - f:StopMovingOrSizing() - f.isMoving = false - f:SetScript("OnUpdate",nil) - StoreSize(bar) - ClampToButtons(bar) - ReAction:RefreshOptions() - end - - local function CornerUpdate() - local bw, bh = GetButtonSize(bar) - local r, c, s = GetButtonGrid(bar) - local ss = GetStateScale(bar) - if IsShiftKeyDown() then - if ss then - f:SetMinResize( ((s+bw)*c*0.25)/ss, ((s+bh)*r*0.25)/ss ) - f:SetMaxResize( ((s+bw)*c*2.5 + 1)/ss, ((s+bh)*r*2.5 + 1)/ss ) - scale = ComputeBarScale(bar) - else - f:SetMinResize( (s+12)*c+1, (s+12)*r+1 ) - f:SetMaxResize( (s+128)*c+1, (s+128)*r+1 ) - RecomputeButtonSize(bar) - end - elseif not ss and IsAltKeyDown() then - f:SetMinResize( bw*c, bh*r ) - f:SetMaxResize( 2*bw*c, 2*bh*r ) - RecomputeButtonSpacing(bar) - else - f:SetMinResize( bw+s+1, bh+s+1 ) - f:SetMaxResize( 50*(bw+s)+1, 50*(bh+s)+1 ) - RecomputeGrid(bar) - end - GameTooltipTextRight2:SetText(format("%d x %d",r,c)) - - local ss = GetStateScale(bar) - local state = bar:GetState() - if ss then - GameTooltipTextRight4:SetText(format("%d%%", scale*100)) - else - local size = (bw == bh) and tostring(bw) or format("%d x %d",bw,bh) - GameTooltipTextRight3:SetText(size) - GameTooltipTextRight4:SetText(tostring(s)) - end - end - - -- corner drag handles - for _, point in pairs({"BOTTOMLEFT","TOPLEFT","BOTTOMRIGHT","TOPRIGHT"}) do - local corner = CreateFrame("Frame",nil,overlay) - corner:EnableMouse(true) - corner:SetWidth(16) - corner:SetHeight(16) - corner:SetPoint(point) - - local tex = corner:CreateTexture(nil,"HIGHLIGHT") - tex:SetTexture("Interface\\AddOns\\ReAction\\img\\corner") - tex:SetTexCoord(unpack(cornerTexCoords[point])) - tex:SetBlendMode("ADD") - tex:SetAlpha(0.6) - tex:SetAllPoints() - - corner:SetScript("OnMouseDown", - function(_,btn) - f:SetScript("OnUpdate", CornerUpdate) - f:StartSizing(point) - end - ) - corner:SetScript("OnMouseUp", - function() - local ss = GetStateScale(bar) - if ss then - local state = bar:GetState() - SetStateScale(bar, ComputeBarScale(bar)) - end - StopResize() - end) - corner:SetScript("OnEnter", - function() - local bw, bh = GetButtonSize(bar) - local r, c, s = bar:GetButtonGrid() - local size = (bw == bh) and tostring(bw) or format("%d x %d",bw,bh) - local ss = GetStateScale(bar) - local state = bar:GetState() - GameTooltip:SetOwner(f, "ANCHOR_"..point) - if ss then - GameTooltip:AddLine(format("%s (%s: %s)", bar:GetName(), L["State"], state)) - else - GameTooltip:AddLine(bar:GetName()) - end - GameTooltip:AddDoubleLine(format("|cffcccccc%s|r %s",L["Drag"],L["to add/remove buttons:"]), format("%d x %d",r,c)) - if ss then - GameTooltip:AddLine(L["State Scale Override"]) - GameTooltip:AddDoubleLine(format("|cff00ff00%s|r %s",L["Hold Shift"],L["to change scale:"]), format("%d%%", bar:GetStateProperty(state,"scale")*100)) - else - GameTooltip:AddDoubleLine(format("|cff00ff00%s|r %s",L["Hold Shift"],L["to resize buttons:"]), tostring(floor(size))) - GameTooltip:AddDoubleLine(format("|cff0033cc%s|r %s",L["Hold Alt"], L["to change spacing:"]), tostring(floor(s))) - end - GameTooltip:Show() - end - ) - corner:SetScript("OnLeave", - function() - GameTooltip:Hide() - f:SetScript("OnUpdate",nil) - end - ) - end - - overlay:RegisterForDrag("LeftButton") - overlay:RegisterForClicks("RightButtonUp") - - overlay:SetScript("OnDragStart", - function() - f:StartMoving() - f.isMoving = true - local w,h = bar:GetButtonSize() - f:ClearAllPoints() - UpdateAnchorDecoration() - f:SetScript("OnUpdate", function() - if IsShiftKeyDown() then - local f, p = DisplaySnapIndicator(f,w,h) - else - HideSnapIndicator() - end - end) - end - ) - - local function UpdateDragTooltip() - GameTooltip:SetOwner(f, "ANCHOR_TOPRIGHT") - local ss = GetStateScale(bar) - local state = bar:GetState() - if ss then - GameTooltip:AddLine(format("%s (%s: %s)", bar:GetName(), L["State"], state)) - else - GameTooltip:AddLine(bar:GetName()) - end - GameTooltip:AddLine(format("|cffcccccc%s|r %s",L["Drag"],L["to move"])) - GameTooltip:AddLine(format("|cff00ff00%s|r %s",L["Hold Shift"],L["to anchor to nearby frames"])) - GameTooltip:AddLine(format("|cff00cccc%s|r %s",L["Right-click"],L["for options..."])) - local point, frame, relpoint, x, y = bar:GetFrame():GetPoint(1) - if point then - local ofsx, ofsy = insidePointOffsetFuncs[point](x,y) - if (frame and frame ~= UIParent) or (ofsx == 0 and ofsy == 0) then - frame = frame or UIParent - GameTooltip:AddLine(format("%s <%s>",L["Currently anchored to"],frame:GetName())) - end - end - GameTooltip:Show() - end - - overlay:SetScript("OnDragStop", - function() - f:StopMovingOrSizing() - f.isMoving = false - f:SetScript("OnUpdate",nil) - - if IsShiftKeyDown() then - local w, h = bar:GetButtonSize() - local a, p, rp, x, y = GetClosestPointSnapped(f,w,h) - if a then - f:ClearAllPoints() - f:SetPoint(p,a,rp,x,y) - end - HideSnapIndicator() - end - - StoreExtents(bar) - ReAction:RefreshOptions() - UpdateDragTooltip() - UpdateAnchorDecoration() - end - ) - - overlay:SetScript("OnEnter", - function() - UpdateDragTooltip() - end - ) - - overlay:SetScript("OnLeave", HideGameTooltip) - - overlay:SetScript("OnClick", - function() - ReAction:ShowEditor(bar) - end - ) - - function overlay:LIBKEYBOUND_ENABLED(evt) - self:SetFrameLevel(1) - end - - function overlay:LIBKEYBOUND_DISABLED(evt) - self:SetFrameLevel(3) - end - - function overlay:RefreshControls() - UpdateAnchorDecoration() - end - - overlay:SetScript("OnShow", overlay.RefreshControls) - - KB.RegisterCallback(overlay,"LIBKEYBOUND_ENABLED") - KB.RegisterCallback(overlay,"LIBKEYBOUND_DISABLED") - - if ReAction:GetKeybindMode() then - overlay:SetFrameLevel(1) - end - - bar:SetLabel(bar:GetName()) - UpdateLabelString(bar) - UpdateAnchorDecoration() - - return overlay -end - - --- export methods to the Bar prototype - -function Bar:ShowControls(show) - local f = self.controlFrame - if show then - if not f then - f = CreateControls(self) - self.controlFrame = f - end - f:Show() - elseif f then - f:Hide() - end -end - -function Bar:RefreshControls() - if self.controlFrame and self.controlFrame:IsShown() then - self.controlFrame:RefreshControls() - end -end - -function Bar:SetLabel(name) - self.labelName = name - UpdateLabelString(self) -end - -function Bar:SetLabelSubtext(text) - self.labelSubtext = text - UpdateLabelString(self) -end
--- a/ReAction.lua Wed Jan 07 00:57:02 2009 +0000 +++ b/ReAction.lua Thu Jan 08 00:57:27 2009 +0000 @@ -183,13 +183,6 @@ function CallModuleMethod(modulename, method, ...) local m = self:GetModule(modulename,true) - if not m then - LoadAddOn(("ReAction_%s"):format(modulename)) - m = self:GetModule(modulename,true) - if m then - dbprint(("succesfully loaded LOD module: %s"):format(modulename)) - end - end if m then if type(m) == "table" and type(m[method]) == "function" then m[method](m,...)
--- a/ReAction.xml Wed Jan 07 00:57:02 2009 +0000 +++ b/ReAction.xml Thu Jan 08 00:57:27 2009 +0000 @@ -6,12 +6,7 @@ <Include file="locale\locale.xml"/> <Script file="ReAction.lua"/> -<Script file="Bar.lua"/> -<Script file="Overlay.lua"/> -<Script file="State.lua"/> -<Script file="LDB.lua"/> <Include file="modules\modules.xml"/> -<Include file="bindings.lua"/> </Ui>
--- a/State.lua Wed Jan 07 00:57:02 2009 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1338 +0,0 @@ ---[[ - ReAction bar state driver interface - ---]] - --- local imports -local ReAction = ReAction -local L = ReAction.L -local _G = _G -local format = string.format -local InCombatLockdown = InCombatLockdown -local RegisterStateDriver = RegisterStateDriver - -ReAction:UpdateRevision("$Revision$") - --- module declaration -local moduleID = "State" -local module = ReAction:NewModule( moduleID, "AceEvent-3.0" ) - --- Utility -- - --- traverse a table tree by key list and fetch the result or first nil -local function tfetch(t, ...) - for i = 1, select('#', ...) do - t = t and t[select(i, ...)] - end - return t -end - --- traverse a table tree by key list and build tree as necessary -local function tbuild(t, ...) - for i = 1, select('#', ...) do - local key = select(i, ...) - if not t[key] then t[key] = { } end - t = t[key] - end - return t -end - --- return a new array of keys of table 't', sorted by comparing --- sub-fields (obtained via tfetch) of the table values -local function fieldsort( t, ... ) - local r = { } - for k in pairs(t) do - table.insert(r,k) - end - local path = { ... } - table.sort(r, function(lhs, rhs) - local olhs = tfetch(t[lhs], unpack(path)) or 0 - local orhs = tfetch(t[rhs], unpack(path)) or 0 - return olhs < orhs - end) - return r -end - --- set a frame-ref, if the frame is valid, or set nil to the --- corresponding attribute -local function SetFrameRef(frame, name, refFrame) - if refFrame then - local _, explicit = refFrame:IsProtected() - if not explicit then - refFrame = nil - end - end - if refFrame then - frame:SetFrameRef(name,refFrame) - else - frame:SetAttribute("frameref-"..name,nil) - end -end - - -local InitRules, ApplyStates, CleanupStates, SetProperty, GetProperty, RegisterProperty, ShowAll - --- PRIVATE -- -do - - -- the field names must match the field names of the options table, below - -- the field values are secure snippets or 'true' to skip the snippet for that property. - local properties = { - hide = - [[ - local h = hide and hide[state] and not showAll - if h ~= hidden then - if h then - self:Hide() - else - self:Show() - end - hidden = h - end - if showAll then - control:CallMethod("UpdateHiddenLabel", hide and hide[state]) - end - ]], - - --keybindState TODO: broken - - anchorEnable = - [[ - local old_anchor = anchorstate - anchorstate = (anchorEnable and anchorEnable[state]) and state - if old_anchor ~= anchorstate or not set_state then - if anchorstate and anchorPoint then - if anchorPoint[state] then - self:ClearAllPoints() - local f = self:GetAttribute("frameref-anchor-"..anchorstate) - if f then - self:SetPoint(anchorPoint[state], f, anchorRelPoint[state], anchorX[state], anchorY[state]) - end - end - elseif defaultAnchor and defaultAnchor.point then - self:ClearAllPoints() - self:SetPoint(defaultAnchor.point, defaultAnchor.frame, - defaultAnchor.relPoint, defaultAnchor.x, defaultAnchor.y) - end - end - ]], - -- anchorEnable handles all the other bits - anchorFrame = true, - anchorPoint = true, - anchorRelPoint = true, - anchorX = true, - anchorY = true, - - - enableScale = - [[ - local old_scale = scalestate - scalestate = (enableScale and enableScale[state]) and state - if old_scale ~= scalestate or not set_state then - if scalestate and scale then - if scale[state] then - self:SetScale(scale[state]) - end - else - self:SetScale(1.0) - end - end - ]], - -- enableScale handles scale - scale = true, - - enableAlpha = - [[ - local old_alpha = alphastate - alphastate = (enableAlpha and enableAlpha[state]) and state - if old_alpha ~= alphastate or not set_state then - control:CallMethod("UpdateAlpha", alphastate and alpha[state] or defaultAlpha) - end - ]], - -- enableAlpha handles alpha - alpha = true, - } - - local weak = { __mode = "k" } - local statedrivers = setmetatable( { }, weak ) - local keybinds = setmetatable( { }, weak ) - - -- - -- Secure Handler Snippets - -- - local SetHandlerData, SetStateDriver, SetStateKeybind, RefreshState - do - local stateHandler_propInit = - [[ - propfuncs = table.new() - local proplist = self:GetAttribute("prop-func-list") - for s in string.gmatch(proplist, "(%w+)") do - table.insert(propfuncs, s) - end - ]] - - local onStateHandler = - -- function _onstate-reaction( self, stateid, newstate ) - [[ - set_state = newstate - - local oldState = state - state = state_override or set_state or state - for i = 1, #propfuncs do - control:RunAttribute("func-"..propfuncs[i]) - end - - control:ChildUpdate() - - if oldState ~= state then - control:CallMethod("StateRefresh", state) - end - ]] - - local onClickHandler = - -- function OnClick( self, button, down ) - [[ - if state_override == button then - state_override = nil -- toggle - else - state_override = button - end - ]] .. onStateHandler - - local function UpdateAlpha( frame, alpha ) - if alpha then - frame:SetAlpha(alpha) - end - end - - -- Construct a lua assignment as a code string and execute it within the header - -- frame's sandbox. 'value' must be a string, boolean, number, or nil. If called - -- with four arguments, then it treats 'varname' as an existing global table and - -- sets a key-value pair. For a slight efficiency boost, pass the values in as - -- attributes and fetch them as attributes from the snippet code, to leverage snippet - -- caching. - function SetHandlerData( bar, varname, value, key ) - local f = bar:GetFrame() - f:SetAttribute("data-varname",varname) - f:SetAttribute("data-value", value) - f:SetAttribute("data-key", key) - f:Execute( - [[ - local name = self:GetAttribute("data-varname") - local value = self:GetAttribute("data-value") - local key = self:GetAttribute("data-key") - if name then - if key then - if not _G[name] then - _G[name] = table.new() - end - _G[name][key] = value - else - _G[name] = value - end - end - ]]) - end - - function SetDefaultAnchor( bar ) - local point, frame, relPoint, x, y = bar:GetAnchor() - SetHandlerData(bar, "defaultAnchor", point, "point") - SetHandlerData(bar, "defaultAnchor", relPoint, "relPoint") - SetHandlerData(bar, "defaultAnchor", x, "x") - SetHandlerData(bar, "defaultAnchor", y, "y") - SetHandlerData(bar, "defaultAlpha", bar:GetAlpha()) - - local f = bar:GetFrame() - f.UpdateAlpha = UpdateAlpha - SetFrameRef(f, "defaultAnchor", _G[frame or "UIParent"]) - f:Execute( - [[ - defaultAnchor.frame = self:GetAttribute("frameref-defaultAnchor") - ]]) - end - - function RefreshState( bar ) - SetDefaultAnchor(bar) - bar:GetFrame():Execute( - [[ - if self:GetAttribute("reaction-refresh") then - control:RunAttribute("reaction-refresh") - end - ]]) - end - - function SetStateDriver( bar, rule ) - local f = bar:GetFrame() - - if not f.UpdateHiddenLabel then - function f:UpdateHiddenLabel(hide) - bar:SetLabelSubtext( hide and L["Hidden"] ) - end - end - - function f:StateRefresh( state ) - bar:RefreshControls() - end - - local props = { } - for p, h in pairs(properties) do - if type(h) == "string" then - table.insert(props,p) - f:SetAttribute("func-"..p, h) - end - end - f:SetAttribute("prop-func-list", table.concat(props," ")) - f:Execute(stateHandler_propInit) - f:SetAttribute("reaction-refresh", onStateHandler) - - if rule and #rule > 0 then - f:SetAttribute( "_onstate-reaction", onStateHandler ) - RegisterStateDriver(f, "reaction", rule) - statedrivers[bar] = rule - elseif statedrivers[bar] then - UnregisterStateDriver(f, "reaction") - f:SetAttribute( "_onstate-reaction", nil ) - statedrivers[bar] = nil - end - end - - function SetStateKeybind( bar, key, state ) - local f = bar:GetFrame() - - local kb = keybinds[bar] - if kb == nil then - if key == nil then - -- nothing to do - return - end - kb = { } - keybinds[bar] = kb - end - - -- clear the old binding, if any - if kb[state] then - SetOverrideBinding(f, false, kb[state], nil) - end - kb[state] = key - - if key then - f:SetAttribute("_onclick", onClickHandler) - SetOverrideBindingClick(f, false, key, state, nil) -- state name is the virtual mouse button - end - end - end - - -- As far as I can tell the macro clauses are NOT locale-specific. - local ruleformats = { - stealth = "stealth", - nostealth = "nostealth", - shadowform = "form:1", - noshadowform = "noform", - pet = "pet", - nopet = "nopet", - harm = "target=target,harm", - help = "target=target,help", - notarget = "target=target,noexists", - focusharm = "target=focus,harm", - focushelp = "target=focus,help", - nofocus = "target=focus,noexists", - raid = "group:raid", - party = "group:party", - solo = "nogroup", - combat = "combat", - nocombat = "nocombat", - possess = "bonusbar:5", - } - - -- Have to do these shenanigans instead of hardcoding the stances/forms because the - -- ordering varies if the character is missing a form. For warriors this is rarely - -- a problem (c'mon, who actually skips the level 10 def stance quest?) but for druids - -- it can be. Some people never bother to do the aquatic form quest until well past - -- when they get cat form, and stance 5/6 can be flight, tree, or moonkin depending - -- on talents. - function InitRules() - local forms = { } - -- sort by icon since it's locale-independent - for i = 1, GetNumShapeshiftForms() do - local icon, name, active = GetShapeshiftFormInfo(i) - -- if it's the current form, the icon is wrong (Ability_Spell_WispSplode) - -- so capture it from the spell info directly - if active then - local _1, _2 - _1, _2, icon = GetSpellInfo(name) - end - forms[icon] = i; - end - -- use 9 if not found since 9 is never a valid stance/form - local defensive = forms["Interface\\Icons\\Ability_Warrior_DefensiveStance"] or 9 - local berserker = forms["Interface\\Icons\\Ability_Racial_Avatar"] or 9 - local bear = forms["Interface\\Icons\\Ability_Racial_BearForm"] or 9 -- bear and dire bear share the same icon - local aquatic = forms["Interface\\Icons\\Ability_Druid_AquaticForm"] or 9 - local cat = forms["Interface\\Icons\\Ability_Druid_CatForm"] or 9 - local travel = forms["Interface\\Icons\\Ability_Druid_TravelForm"] or 9 - local tree = forms["Interface\\Icons\\Ability_Druid_TreeofLife"] or 9 - local moonkin = forms["Interface\\Icons\\Spell_Nature_ForceOfNature"] or 9 - local flight = forms["Interface\\Icons\\Ability_Druid_FlightForm"] or 9 -- flight and swift flight share the same icon - - ruleformats.battle = "stance:1" - ruleformats.defensive = format("stance:%d",defensive) - ruleformats.berserker = format("stance:%d",berserker) - ruleformats.caster = format("form:0/%d/%d/%d",aquatic, travel, flight) - ruleformats.bear = format("form:%d",bear) - ruleformats.cat = format("form:%d",cat) - ruleformats.tree = format("form:%d",tree) - ruleformats.moonkin = format("form:%d",moonkin) - end - - local function BuildRule(states) - local rules = { } - local default - - for idx, state in ipairs(fieldsort(states, "rule", "order")) do - local c = states[state].rule - local type = c.type - if type == "default" then - default = default or state - elseif type == "custom" then - if c.custom then - -- strip out all spaces from the custom rule - table.insert(rules, format("%s %s", c.custom:gsub("%s",""), state)) - end - elseif type == "any" or type == "all" then - if c.values then - local clauses = { } - for key, value in pairs(c.values) do - table.insert(clauses, ruleformats[key]) - end - if #clauses > 0 then - local sep = (type == "any") and "][" or "," - table.insert(rules, format("[%s] %s", table.concat(clauses,sep), state)) - end - end - end - end - -- make sure that the default, if any, is last - if default then - table.insert(rules, default) - end - return table.concat(rules,";") - end - - local function BuildKeybinds( bar, states ) - for name, state in pairs(states) do - local type = tfetch(state, "rule", "type") - if type == "keybind" then - local key = tfetch(state, "rule", "keybind") - SetStateKeybind(bar, key, name) - else - SetStateKeybind(bar, nil, name) -- this clears an existing keybind - end - end - end - - function GetProperty( bar, state, propname ) - return tfetch(module.db.profile.bars, bar:GetName(), "states", state, propname) - end - - function SetProperty( bar, state, propname, value ) - local s = tbuild(module.db.profile.bars, bar:GetName(), "states", state) - s[propname] = value - SetHandlerData(bar, propname, value, state) - RefreshState(bar) - end - - function RegisterProperty( propname, snippet ) - properties[propname] = snippet or true - for _, bar in ReAction:IterateBars() do - local states = tfetch(module.db.profile.bars, bar:GetName(), "states") - if states then - for name, s in pairs(states) do - SetHandlerData(bar, propname, s[propname], name) - end - SetStateDriver(bar, BuildRule(states)) - RefreshState(bar) - end - end - end - - function UnregisterProperty( propname ) - properties[propname] = nil - for _, bar in ReAction:IterateBars() do - SetHandlerData(bar, propname, nil) - SetStateDriver(bar, BuildRule(states)) - RefreshState(bar) - end - end - - function ApplyStates( bar ) - local states = tfetch(module.db.profile.bars, bar:GetName(), "states") - if states then - for propname in pairs(properties) do - for name, s in pairs(states) do - if propname == "anchorFrame" then - SetFrameRef(bar:GetFrame(), "anchor-"..name, _G[s.anchorFrame]) - else - SetHandlerData(bar, propname, s[propname], name) - end - end - end - BuildKeybinds(bar, states) - SetHandlerData(bar, "showAll", ReAction:GetConfigMode()) - SetStateDriver(bar, BuildRule(states)) - RefreshState(bar) - end - end - - function CleanupStates( bar ) - SetStateDriver(bar, nil) - end - - function ShowAll( bar, show ) - if statedrivers[bar] then - SetHandlerData(bar, "showAll", show) - RefreshState(bar) - end - end -end - - - --- module event handlers -- - -function module:OnInitialize() - self.db = ReAction.db:RegisterNamespace( moduleID, - { - profile = { - bars = { }, - } - } - ) - - self:RegisterEvent("UPDATE_SHAPESHIFT_FORMS") - - ReAction:RegisterBarOptionGenerator(self, "GetBarOptions") - - ReAction.RegisterCallback(self, "OnCreateBar","OnRefreshBar") - ReAction.RegisterCallback(self, "OnDestroyBar") - ReAction.RegisterCallback(self, "OnRefreshBar") - ReAction.RegisterCallback(self, "OnEraseBar") - ReAction.RegisterCallback(self, "OnRenameBar") - ReAction.RegisterCallback(self, "OnConfigModeChanged") -end - -function module:OnEnable() - self:UPDATE_SHAPESHIFT_FORMS() -- it doesn't fire on a /reloadui -end - -function module:UPDATE_SHAPESHIFT_FORMS() - -- Re-parse the rules table according to the new form list. - -- This happens both at initial login (after PLAYER_ENTERING_WORLD) - -- as well as when gaining new abilities. - InitRules() - for name, bar in ReAction:IterateBars() do - self:OnRefreshBar(nil,bar,name) - end -end - -function module:OnRefreshBar(event, bar, name) - local c = self.db.profile.bars[name] - if c then - ApplyStates(bar) - end -end - -function module:OnDestroyBar(event, bar, name) - CleanupStates(bar) -end - -function module:OnEraseBar(event, bar, name) - self.db.profile.bars[name] = nil -end - -function module:OnRenameBar(event, bar, oldname, newname) - local bars = self.db.profile.bars - bars[newname], bars[oldname] = bars[oldname], nil -end - -function module:OnConfigModeChanged(event, mode) - for name, bar in ReAction:IterateBars() do - if self.db.profile.bars[name] then - ShowAll(bar, mode) - end - end -end - - - --- Options -- - -local CreateBarOptions, RegisterPropertyOptions -do - local playerClass = select(2, UnitClass("player")) - local function ClassCheck(...) - for i = 1, select('#',...) do - if playerClass == select(i,...) then - return false - end - end - return true - end - - -- pre-sorted by the order they should appear in - local rules = { - -- rule hidden fields - { "stance", ClassCheck("WARRIOR"), { {battle = L["Battle Stance"]}, {defensive = L["Defensive Stance"]}, {berserker = L["Berserker Stance"]} } }, - { "form", ClassCheck("DRUID"), { {caster = L["Caster Form"]}, {bear = L["Bear Form"]}, {cat = L["Cat Form"]}, {tree = L["Tree of Life"]}, {moonkin = L["Moonkin Form"]} } }, - { "stealth", ClassCheck("ROGUE","DRUID"), { {stealth = L["Stealth"]}, {nostealth = L["No Stealth"]} } }, - { "shadow", ClassCheck("PRIEST"), { {shadowform = L["Shadowform"]}, {noshadowform = L["No Shadowform"]} } }, - { "pet", ClassCheck("HUNTER","WARLOCK"), { {pet = L["With Pet"]}, {nopet = L["Without Pet"]} } }, - { "target", false, { {harm = L["Hostile Target"]}, {help = L["Friendly Target"]}, {notarget = L["No Target"]} } }, - { "focus", false, { {focusharm = L["Hostile Focus"]}, {focushelp = L["Friendly Focus"]}, {nofocus = L["No Focus"]} } }, - { "possess", false, { {possess = L["Mind Control"]} } }, - { "group", false, { {raid = L["Raid"]}, {party = L["Party"]}, {solo = L["Solo"]} } }, - { "combat", false, { {combat = L["In Combat"]}, {nocombat = L["Out of Combat"]} } }, - } - - local ruleSelect = { } - local ruleMap = { } - local optionMap = setmetatable({},{__mode="k"}) - - local pointTable = { - NONE = " ", - CENTER = L["Center"], - LEFT = L["Left"], - RIGHT = L["Right"], - TOP = L["Top"], - BOTTOM = L["Bottom"], - TOPLEFT = L["Top Left"], - TOPRIGHT = L["Top Right"], - BOTTOMLEFT = L["Bottom Left"], - BOTTOMRIGHT = L["Bottom Right"], - } - - -- unpack rules table into ruleSelect and ruleMap - for _, c in ipairs(rules) do - local rule, hidden, fields = unpack(c) - if not hidden then - for _, field in ipairs(fields) do - local key, label = next(field) - table.insert(ruleSelect, label) - table.insert(ruleMap, key) - end - end - end - - local stateOptions = { - ordering = { - name = L["Info"], - order = 1, - type = "group", - args = { - delete = { - name = L["Delete this State"], - order = -1, - type = "execute", - func = "DeleteState", - }, - rename = { - name = L["Name"], - order = 1, - type = "input", - get = "GetName", - set = "SetStateName", - pattern = "^%w*$", - usage = L["State names must be alphanumeric without spaces"], - }, - ordering = { - name = L["Evaluation Order"], - desc = L["State transitions are evaluated in the order listed:\nMove a state up or down to change the order"], - order = 2, - type = "group", - inline = true, - args = { - up = { - name = L["Up"], - order = 1, - type = "execute", - width = "half", - func = "MoveStateUp", - }, - down = { - name = L["Down"], - order = 2, - type = "execute", - width = "half", - func = "MoveStateDown", - } - } - } - } - }, - properties = { - name = L["Properties"], - order = 2, - type = "group", - args = { - desc = { - name = L["Set the properties for the bar when in this state"], - order = 1, - type = "description" - }, - hide = { - name = L["Hide Bar"], - order = 90, - type = "toggle", - set = "SetProp", - get = "GetProp", - }, - --[[ BROKEN - keybindState = { - name = L["Override Keybinds"], - desc = L["Set this state to maintain its own set of keybinds which override the defaults when active"], - order = 91, - type = "toggle", - set = "SetProp", - get = "GetProp", - }, ]] - position = { - name = L["Position"], - order = 92, - type = "group", - inline = true, - args = { - anchorEnable = { - name = L["Reposition"], - order = 1, - type = "toggle", - set = "SetProp", - get = "GetProp", - }, - anchorFrame = { - name = L["Anchor Frame"], - order = 2, - type = "select", - values = "GetAnchorFrames", - set = "SetAnchorFrame", - get = "GetAnchorFrame", - disabled = "GetAnchorDisabled", - hidden = "GetAnchorDisabled", - }, - anchorPoint = { - name = L["Point"], - order = 3, - type = "select", - values = pointTable, - set = "SetAnchorPointProp", - get = "GetAnchorPointProp", - disabled = "GetAnchorDisabled", - hidden = "GetAnchorDisabled", - }, - anchorRelPoint = { - name = L["Relative Point"], - order = 4, - type = "select", - values = pointTable, - set = "SetAnchorPointProp", - get = "GetAnchorPointProp", - disabled = "GetAnchorDisabled", - hidden = "GetAnchorDisabled", - }, - anchorX = { - name = L["X Offset"], - order = 5, - type = "range", - min = -100, - max = 100, - step = 1, - set = "SetProp", - get = "GetProp", - disabled = "GetAnchorDisabled", - hidden = "GetAnchorDisabled", - }, - anchorY = { - name = L["Y Offset"], - order = 6, - type = "range", - min = -100, - max = 100, - step = 1, - set = "SetProp", - get = "GetProp", - disabled = "GetAnchorDisabled", - hidden = "GetAnchorDisabled", - }, - }, - }, - scale = { - name = L["Scale"], - order = 93, - type = "group", - inline = true, - args = { - enableScale = { - name = L["Set New Scale"], - order = 1, - type = "toggle", - set = "SetProp", - get = "GetProp", - }, - scale = { - name = L["Scale"], - order = 2, - type = "range", - min = 0.25, - max = 2.5, - step = 0.05, - isPercent = true, - set = "SetProp", - get = "GetScale", - disabled = "GetScaleDisabled", - hidden = "GetScaleDisabled", - }, - }, - }, - alpha = { - name = L["Transparency"], - order = 94, - type = "group", - inline = true, - args = { - enableAlpha = { - name = L["Set Transparency"], - order = 1, - type = "toggle", - set = "SetProp", - get = "GetProp", - }, - alpha = { - name = L["Transparency"], - order = 2, - type = "range", - min = 0, - max = 1, - step = 0.01, - bigStep = 0.05, - isPercent = true, - set = "SetProp", - get = "GetAlpha", - disabled = "GetAlphaDisabled", - hidden = "GetAlphaDisabled", - }, - }, - }, - }, - plugins = { } - }, - rules = { - name = L["Rule"], - order = 3, - type = "group", - args = { - mode = { - name = L["Select this state"], - order = 2, - type = "select", - style = "radio", - values = { - default = L["by default"], - any = L["when ANY of these"], - all = L["when ALL of these"], - custom = L["via custom rule"], - keybind = L["via keybinding"], - }, - set = "SetType", - get = "GetType", - }, - clear = { - name = L["Clear All"], - order = 3, - type = "execute", - hidden = "GetClearAllDisabled", - disabled = "GetClearAllDisabled", - func = "ClearAllConditions", - }, - inputs = { - name = L["Conditions"], - order = 4, - type = "multiselect", - hidden = "GetConditionsDisabled", - disabled = "GetConditionsDisabled", - values = ruleSelect, - set = "SetCondition", - get = "GetCondition", - }, - custom = { - name = L["Custom Rule"], - order = 5, - type = "input", - multiline = true, - hidden = "GetCustomDisabled", - disabled = "GetCustomDisabled", - desc = L["Syntax like macro rules: see preset rules for examples"], - set = "SetCustomRule", - get = "GetCustomRule", - validate = "ValidateCustomRule", - }, - keybind = { - name = L["Keybinding"], - order = 6, - inline = true, - hidden = "GetKeybindDisabled", - disabled = "GetKeybindDisabled", - type = "group", - args = { - desc = { - name = L["Invoking a state keybind toggles an override of all other transition rules."], - order = 1, - type = "description", - }, - keybind = { - name = L["State Hotkey"], - desc = L["Define an override toggle keybind"], - order = 2, - type = "keybinding", - set = "SetKeybind", - get = "GetKeybind", - }, - }, - }, - }, - }, - } - - local handlers = { } - local meta = { - __index = function(self, key) - for _, h in pairs(handlers) do - if h[key] then - return h[key] - end - end - end, - } - local StateHandler = setmetatable({ }, meta) - local proto = { __index = StateHandler } - - function RegisterPropertyOptions( field, options, handler ) - stateOptions.properties.plugins[field] = options - handlers[field] = handler - end - - function UnregisterPropertyOptions( field ) - stateOptions.properties.plugins[field] = nil - handlers[field] = nil - end - - function StateHandler:New( bar, opts ) - local self = setmetatable( - { - bar = bar - }, - proto ) - - function self:GetName() - return opts.name - end - - function self:SetName(name) - opts.name = name - end - - function self:GetOrder() - return opts.order - end - - -- get reference to states table: even if the bar - -- name changes the states table ref won't - self.states = tbuild(module.db.profile.bars, bar:GetName(), "states") - self.state = tbuild(self.states, opts.name) - - opts.order = self:GetRuleField("order") - if opts.order == nil then - -- add after the highest - opts.order = 100 - for _, state in pairs(self.states) do - local x = tonumber(tfetch(state, "rule", "order")) - if x and x >= opts.order then - opts.order = x + 1 - end - end - self:SetRuleField("order",opts.order) - end - - return self - end - - -- helper methods - - function StateHandler:SetRuleField( key, value, ... ) - tbuild(self.state, "rule", ...)[key] = value - end - - function StateHandler:GetRuleField( ... ) - return tfetch(self.state, "rule", ...) - end - - function StateHandler:FixAll( setkey ) - -- if multiple selections in the same group are chosen when 'all' is selected, - -- keep only one of them. If changing the mode, the first in the fields list will - -- be chosen arbitrarily. Otherwise, if selecting a new checkbox from the field-set, - -- it will be retained. - local notified = false - if self:GetRuleField("type") == "all" then - for _, c in ipairs(rules) do - local rule, hidden, fields = unpack(c) - local once = false - if setkey then - for idx, field in ipairs(fields) do - if next(field) == setkey then - once = true - end - end - end - for idx, field in ipairs(fields) do - local key = next(field) - if self:GetRuleField("values",key) then - if once and key ~= setkey then - self:SetRuleField(key,false,"values") - if not setkey and not notified then - ReAction:UserError(L["Warning: one or more incompatible rules were turned off"]) - notified = true - end - end - once = true - end - end - end - end - end - - function StateHandler:GetNeighbors() - local before, after - for k, v in pairs(self.states) do - local o = tonumber(tfetch(v, "rule", "order")) - if o and k ~= self:GetName() then - local obefore = tfetch(self.states,before,"rule","order") - local oafter = tfetch(self.states,after,"rule","order") - if o < self:GetOrder() and (not obefore or obefore < o) then - before = k - end - if o > self:GetOrder() and (not oafter or oafter > o) then - after = k - end - end - end - return before, after - end - - function StateHandler:SwapOrder( a, b ) - -- do options table - local args = optionMap[self.bar].args - args[a].order, args[b].order = args[b].order, args[a].order - -- do profile - a = tbuild(self.states, a, "rule") - b = tbuild(self.states, b, "rule") - a.order, b.order = b.order, a.order - end - - -- handler methods - - function StateHandler:GetProp( info ) - -- gets property of the same name as the options arg - return GetProperty(self.bar, self:GetName(), info[#info]) - end - - function StateHandler:SetProp( info, value ) - -- sets property of the same name as the options arg - SetProperty(self.bar, self:GetName(), info[#info], value) - end - - function StateHandler:DeleteState() - if self.states[self:GetName()] then - self.states[self:GetName()] = nil - ApplyStates(self.bar) - end - optionMap[self.bar].args[self:GetName()] = nil - end - - function StateHandler:SetStateName(info, value) - -- check for existing state name - if self.states[value] then - ReAction:UserError(format(L["State named '%s' already exists"],value)) - return - end - local args = optionMap[self.bar].args - local name = self:GetName() - self.states[value], args[value], self.states[name], args[name] = self.states[name], args[name], nil, nil - self:SetName(value) - ApplyStates(self.bar) - ReAction:ShowEditor(self.bar, moduleID, value) - end - - function StateHandler:MoveStateUp() - local before, after = self:GetNeighbors() - if before then - self:SwapOrder(before, self:GetName()) - ApplyStates(self.bar) - end - end - - function StateHandler:MoveStateDown() - local before, after = self:GetNeighbors() - if after then - self:SwapOrder(self:GetName(), after) - ApplyStates(self.bar) - end - end - - function StateHandler:GetAnchorDisabled() - return not GetProperty(self.bar, self:GetName(), "anchorEnable") - end - - function StateHandler:GetAnchorFrames(info) - self._anchorframes = self._anchorframes or { } - table.wipe(self._anchorframes) - - table.insert(self._anchorframes, "UIParent") - for name, bar in ReAction:IterateBars() do - table.insert(self._anchorframes, bar:GetFrame():GetName()) - end - return self._anchorframes - end - - function StateHandler:GetAnchorFrame(info) - local value = self:GetProp(info) - for k,v in pairs(self._anchorframes) do - if v == value then - return k - end - end - end - - function StateHandler:SetAnchorFrame(info, value) - local f = _G[self._anchorframes[value]] - if f then - SetFrameRef(self.bar:GetFrame(), "anchor-"..self:GetName(), f) - self:SetProp(info, f:GetName()) - end - end - - function StateHandler:SetAnchorPointProp(info, value) - self:SetProp(info, value ~= "NONE" and value or nil) - end - - function StateHandler:GetAnchorPointProp(info) - return self:GetProp(info) or "NONE" - end - - function StateHandler:GetScale(info) - return self:GetProp(info) or 1.0 - end - - function StateHandler:GetScaleDisabled() - return not GetProperty(self.bar, self:GetName(), "enableScale") - end - - function StateHandler:GetAlpha(info) - return self:GetProp(info) or 1.0 - end - - function StateHandler:GetAlphaDisabled() - return not GetProperty(self.bar, self:GetName(), "enableAlpha") - end - - function StateHandler:SetType(info, value) - self:SetRuleField("type", value) - self:FixAll() - ApplyStates(self.bar) - end - - function StateHandler:GetType() - return self:GetRuleField("type") - end - - function StateHandler:GetClearAllDisabled() - local t = self:GetRuleField("type") - return not( t == "any" or t == "all" or t == "custom") - end - - function StateHandler:ClearAllConditions() - local t = self:GetRuleField("type") - if t == "custom" then - self:SetRuleField("custom","") - elseif t == "any" or t == "all" then - self:SetRuleField("values", {}) - end - ApplyStates(self.bar) - end - - function StateHandler:GetConditionsDisabled() - local t = self:GetRuleField("type") - return not( t == "any" or t == "all") - end - - function StateHandler:SetCondition(info, key, value) - self:SetRuleField(ruleMap[key], value or nil, "values") - if value then - self:FixAll(ruleMap[key]) - end - ApplyStates(self.bar) - end - - function StateHandler:GetCondition(info, key) - return self:GetRuleField("values", ruleMap[key]) or false - end - - function StateHandler:GetCustomDisabled() - return self:GetRuleField("type") ~= "custom" - end - - function StateHandler:SetCustomRule(info, value) - self:SetRuleField("custom",value) - ApplyStates(self.bar) - end - - function StateHandler:GetCustomRule() - return self:GetRuleField("custom") or "" - end - - function StateHandler:ValidateCustomRule(info, value) - local s = value:gsub("%s","") -- remove all spaces - -- unfortunately %b and captures don't support the '+' notation, or this would be considerably simpler - repeat - if s == "" then - return true - end - local c, r = s:match("(%b[])(.*)") - if c == nil and s and #s > 0 then - return format(L["Invalid custom rule '%s': each clause must appear within [brackets]"],value or "") - end - s = r - until c == nil - return true - end - - function StateHandler:GetKeybindDisabled() - return self:GetRuleField("type") ~= "keybind" - end - - function StateHandler:GetKeybind() - return self:GetRuleField("keybind") - end - - function StateHandler:SetKeybind(info, value) - if value and #value == 0 then - value = nil - end - self:SetRuleField("keybind",value) - ApplyStates(self.bar) - end - - local function CreateStateOptions(bar, name) - local opts = { - type = "group", - name = name, - childGroups = "tab", - args = stateOptions - } - - opts.handler = StateHandler:New(bar,opts) - - return opts - end - - function module:GetBarOptions(bar) - local private = { } - local states = tbuild(module.db.profile.bars, bar:GetName(), "states") - local options = { - name = L["Dynamic State"], - type = "group", - order = -1, - childGroups = "tree", - disabled = InCombatLockdown, - args = { - __desc__ = { - name = L["States are evaluated in the order they are listed"], - order = 1, - type = "description", - }, - __new__ = { - name = L["New State..."], - order = 2, - type = "group", - args = { - name = { - name = L["State Name"], - desc = L["Set a name for the new state"], - order = 1, - type = "input", - get = function() return private.newstatename or "" end, - set = function(info,value) private.newstatename = value end, - pattern = "^%w*$", - usage = L["State names must be alphanumeric without spaces"], - }, - create = { - name = L["Create State"], - order = 2, - type = "execute", - func = function () - local name = private.newstatename - if states[name] then - ReAction:UserError(format(L["State named '%s' already exists"],name)) - else - -- TODO: select default state options and pass as final argument - states[name] = { } - optionMap[bar].args[name] = CreateStateOptions(bar,name) - ReAction:ShowEditor(bar, moduleID, name) - private.newstatename = "" - end - end, - disabled = function() - local name = private.newstatename or "" - return #name == 0 or name:find("%W") - end, - } - } - } - } - } - for name, config in pairs(states) do - options.args[name] = CreateStateOptions(bar,name) - end - optionMap[bar] = options - return options - end -end - --- Module API -- - --- Pass in a property field-name, an implementation secure snippet, a static options table, and an --- optional options handler method-table --- --- The options table is static, i.e. not bar-specific and should only reference handler method --- strings (either existing ones or those added via optHandler). The existing options are ordered --- 90-99. Order #1 is reserved for the heading. --- --- The contents of optHandler, if provided, will be added to the existing StateHandler options metatable. --- See above, for existing API. In particular see the properties set up in the New method: self.bar, --- self.states, and self:GetName(), and the generic property handlers self:GetProp() and self:SetProp(). --- -function module:RegisterStateProperty( field, snippetHandler, options, optHandler ) - RegisterProperty(field, snippetHandler) - RegisterPropertyOptions(field, options, optHandler) -end - -function module:UnregisterStateProperty( field ) - UnregisterProperty(field) - UnregisterPropertyOptions(field) -end - - --- Export methods to Bar class -- - -function ReAction.Bar:GetState() - return GetManagedEnvironment(self:GetFrame()).state -end - -ReAction.Bar.GetStateProperty = GetProperty -ReAction.Bar.SetStateProperty = SetProperty
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/modules/Action.lua Thu Jan 08 00:57:27 2009 +0000 @@ -0,0 +1,1157 @@ +--[[ + ReAction Action button module. + + The button module implements standard action button functionality by wrapping Blizzard's + ActionBarButtonTemplate frame and associated functions. + + It also provides action remapping support for multiple pages and possessed targets + (Mind Control, Eyes of the Beast, Karazhan Chess event, various quests, etc). +--]] + +-- local imports +local ReAction = ReAction +local L = ReAction.L +local _G = _G +local CreateFrame = CreateFrame +local format = string.format +local wipe = wipe + +ReAction:UpdateRevision("$Revision$") + +-- libraries +local KB = LibStub("LibKeyBound-1.0") +local LBF -- initialized later + +-- module declaration +local moduleID = "Action" +local module = ReAction:NewModule( moduleID ) + +-- Class declarations +local Button = { } +local Handle = { } +local PropHandler = { } + +-- Event handlers +function module:OnInitialize() + self.db = ReAction.db:RegisterNamespace( moduleID, + { + profile = { + bars = { }, + } + } + ) + self.handles = setmetatable({ }, weak) + + ReAction:RegisterBarOptionGenerator(self, "GetBarOptions") + + ReAction.RegisterCallback(self, "OnCreateBar") + ReAction.RegisterCallback(self, "OnRefreshBar") + ReAction.RegisterCallback(self, "OnDestroyBar") + ReAction.RegisterCallback(self, "OnEraseBar") + ReAction.RegisterCallback(self, "OnRenameBar") + ReAction.RegisterCallback(self, "OnConfigModeChanged") + + LBF = LibStub("LibButtonFacade",true) + + KB.RegisterCallback(self, "LIBKEYBOUND_ENABLED") + KB.RegisterCallback(self, "LIBKEYBOUND_DISABLED") + KB.RegisterCallback(self, "LIBKEYBOUND_MODE_COLOR_CHANGED","LIBKEYBOUND_ENABLED") +end + +function module:OnEnable() + ReAction:RegisterBarType(L["Action Bar"], + { + type = moduleID, + defaultButtonSize = 36, + defaultBarRows = 1, + defaultBarCols = 12, + defaultBarSpacing = 3 + }, true) + ReAction:GetModule("State"):RegisterStateProperty("page", nil, PropHandler.GetOptions(), PropHandler) +end + +function module:OnDisable() + ReAction:UnregisterBarType(L["Action Bar"]) + ReAction:GetModule("State"):UnregisterStateProperty("page") +end + +function module:OnCreateBar(event, bar, name) + if bar.config.type == moduleID then + local profile = self.db.profile + if profile.bars[name] == nil then + profile.bars[name] = { + buttons = { } + } + end + if self.handles[bar] == nil then + self.handles[bar] = Handle:New(bar, profile.bars[name]) + end + end +end + +function module:OnRefreshBar(event, bar, name) + if self.handles[bar] then + self.handles[bar]:Refresh() + end +end + +function module:OnDestroyBar(event, bar, name) + if self.handles[bar] then + self.handles[bar]:Destroy() + self.handles[bar] = nil + end +end + +function module:OnEraseBar(event, bar, name) + self.db.profile.bars[name] = nil +end + +function module:OnRenameBar(event, bar, oldname, newname) + b = self.db.profile.bars + b[newname], b[oldname] = b[oldname], nil +end + +function module:OnConfigModeChanged(event, mode) + for _, h in pairs(self.handles) do + h:SetConfigMode(mode) + end +end + +function module:LIBKEYBOUND_ENABLED(evt) + for _, h in pairs(self.handles) do + h:ShowGrid(true) + h:SetKeybindMode(true) + end +end + +function module:LIBKEYBOUND_DISABLED(evt) + for _, h in pairs(self.handles) do + h:ShowGrid(false) + h:SetKeybindMode(false) + end +end + + +---- Interface ---- +function module:GetBarOptions(bar) + local h = self.handles[bar] + if h then + return h:GetOptions() + end +end + + +---- Bar Handle ---- + +do + local options = { + hideEmpty = { + name = L["Hide Empty Buttons"], + order = 1, + type = "toggle", + width = "double", + get = "GetHideEmpty", + set = "SetHideEmpty", + }, + lockButtons = { + name = L["Lock Buttons"], + desc = L["Prevents picking up/dragging actions.|nNOTE: This setting is overridden by the global setting in Blizzard's Action Buttons tab"], + order = 2, + type = "toggle", + disabled = "LockButtonsDisabled", + get = "GetLockButtons", + set = "SetLockButtons", + }, + lockOnlyCombat = { + name = L["Only in Combat"], + desc = L["Only lock the buttons when in combat"], + order = 3, + type = "toggle", + disabled = "LockButtonsCombatDisabled", + get = "GetLockButtonsCombat", + set = "SetLockButtonsCombat", + }, + pages = { + name = L["# Pages"], + desc = L["Use the Dynamic State tab to specify page transitions"], + order = 4, + type = "range", + min = 1, + max = 10, + step = 1, + get = "GetNumPages", + set = "SetNumPages", + }, + mindcontrol = { + name = L["Mind Control Support"], + desc = L["When possessing a target (e.g. via Mind Control), map the first 12 buttons of this bar to the possessed target's actions."], + order = 5, + type = "toggle", + width = "double", + set = "SetMindControl", + get = "GetMindControl", + }, + actions = { + name = L["Edit Action IDs"], + order = 6, + type = "group", + inline = true, + args = { + method = { + name = L["Assign"], + order = 1, + type = "select", + width = "double", + values = { [0] = L["Choose Method..."], + [1] = L["Individually"], + [2] = L["All at Once"], }, + get = "GetActionEditMethod", + set = "SetActionEditMethod", + }, + rowSelect = { + name = L["Row"], + desc = L["Rows are numbered top to bottom"], + order = 2, + type = "select", + width = "half", + hidden = "IsButtonSelectHidden", + values = "GetRowList", + get = "GetSelectedRow", + set = "SetSelectedRow", + }, + colSelect = { + name = L["Col"], + desc = L["Columns are numbered left to right"], + order = 3, + type = "select", + width = "half", + hidden = "IsButtonSelectHidden", + values = "GetColumnList", + get = "GetSelectedColumn", + set = "SetSelectedColumn", + }, + pageSelect = { + name = L["Page"], + order = 4, + type = "select", + width = "half", + hidden = "IsPageSelectHidden", + values = "GetPageList", + get = "GetSelectedPage", + set = "SetSelectedPage", + }, + single = { + name = L["Action ID"], + usage = L["Specify ID 1-120"], + order = 5, + type = "input", + width = "half", + hidden = "IsButtonSelectHidden", + get = "GetActionID", + set = "SetActionID", + validate = "ValidateActionID", + }, + multi = { + name = L["ID List"], + usage = L["Specify a comma-separated list of IDs for each button in the bar (in order). Separate multiple pages with semicolons (;)"], + order = 6, + type = "input", + multiline = true, + width = "double", + hidden = "IsMultiIDHidden", + get = "GetMultiID", + set = "SetMultiID", + validate = "ValidateMultiID", + }, + }, + }, + } + + local weak = { __mode="k" } + local meta = { __index = Handle } + + function Handle:New( bar, config ) + local self = setmetatable( + { + bar = bar, + config = config, + btns = { } + }, + meta) + + if self.config.buttons == nil then + self.config.buttons = { } + end + self:Refresh() + self:SetKeybindMode(ReAction:GetKeybindMode()) + return self + end + + function Handle:Refresh() + local r, c = self.bar:GetButtonGrid() + local n = r*c + local btnCfg = self.config.buttons + if n ~= #self.btns then + for i = 1, n do + if btnCfg[i] == nil then + btnCfg[i] = {} + end + if self.btns[i] == nil then + local b = Button:New(self, i, btnCfg[i], self.config) + self.btns[i] = b + self.bar:AddButton(i,b) + end + end + for i = n+1, #self.btns do + if self.btns[i] then + self.bar:RemoveButton(self.btns[i]) + self.btns[i]:Destroy() + self.btns[i] = nil + btnCfg[i] = nil + end + end + end + local f = self.bar:GetFrame() + for _, b in ipairs(self.btns) do + b:Refresh() + end + f:SetAttribute("mindcontrol",self.config.mindcontrol) + f:Execute( + [[ + doMindControl = self:GetAttribute("mindcontrol") + control:ChildUpdate() + ]]) + + f:SetAttribute("_onstate-mindcontrol", + -- function _onstate-mindcontrol(self, stateid, newstate) + [[ + control:ChildUpdate() + ]]) + RegisterStateDriver(f, "mindcontrol", "[bonusbar:5] mc; none") + self:UpdateButtonLock() + end + + function Handle:Destroy() + for _,b in pairs(self.btns) do + if b then + b:Destroy() + end + end + end + + function Handle:SetConfigMode(mode) + for _, b in pairs(self.btns) do + b:ShowGrid(mode) + b:ShowActionIDLabel(mode) + end + end + + function Handle:ShowGrid(show) + for _, b in pairs(self.btns) do + b:ShowGrid(show) + end + end + + function Handle:UpdateButtonLock() + local f = self.bar:GetFrame() + f:SetAttribute("lockbuttons",self.config.lockButtons) + f:SetAttribute("lockbuttonscombat",self.config.lockButtonsCombat) + f:Execute( + [[ + lockButtons = self:GetAttribute("lockbuttons") + lockButtonsCombat = self:GetAttribute("lockbuttonscombat") + ]]) + end + + function Handle:SetKeybindMode(mode) + for _, b in pairs(self.btns) do + if mode then + -- set the border for all buttons to the keybind-enable color + b.border:SetVertexColor(KB:GetColorKeyBoundMode()) + b.border:Show() + elseif IsEquippedAction(b:GetActionID()) then + b.border:SetVertexColor(0, 1.0, 0, 0.35) -- from ActionButton.lua + else + b.border:Hide() + end + end + end + + function Handle:GetLastButton() + return self.btns[#self.btns] + end + + -- options handlers + function Handle:GetOptions() + return { + type = "group", + name = L["Action Buttons"], + handler = self, + args = options + } + end + + function Handle:SetHideEmpty(info, value) + if value ~= self.config.hideEmpty then + self.config.hideEmpty = value + self:ShowGrid(not value) + end + end + + function Handle:GetHideEmpty() + return self.config.hideEmpty + end + + function Handle:GetLockButtons() + return LOCK_ACTIONBAR == "1" or self.config.lockButtons + end + + function Handle:SetLockButtons(info, value) + self.config.lockButtons = value + self:UpdateButtonLock() + end + + function Handle:LockButtonsDisabled() + return LOCK_ACTIONBAR == "1" + end + + function Handle:GetLockButtonsCombat() + return self.config.lockButtonsCombat + end + + function Handle:SetLockButtonsCombat(info, value) + self.config.lockButtonsCombat = value + self:UpdateButtonLock() + end + + function Handle:LockButtonsCombatDisabled() + return LOCK_ACTIONBAR == "1" or not self.config.lockButtons + end + + function Handle:GetNumPages() + return self.config.nPages + end + + function Handle:SetNumPages(info, value) + self.config.nPages = value + self:Refresh() + end + + function Handle:GetMindControl() + return self.config.mindcontrol + end + + function Handle:SetMindControl(info, value) + self.config.mindcontrol = value + self:Refresh() + end + + function Handle:GetActionEditMethod() + return self.editMethod or 0 + end + + function Handle:SetActionEditMethod(info, value) + self.editMethod = value + end + + function Handle:IsButtonSelectHidden() + return self.editMethod ~= 1 + end + + function Handle:GetRowList() + local r,c = self.bar:GetButtonGrid() + if self.rowList == nil or #self.rowList ~= r then + local list = { } + for i = 1, r do + table.insert(list,i) + end + self.rowList = list + end + return self.rowList + end + + function Handle:GetSelectedRow() + local r, c = self.bar:GetButtonGrid() + local row = self.selectedRow or 1 + if row > r then + row = 1 + end + self.selectedRow = row + return row + end + + function Handle:SetSelectedRow(info, value) + self.selectedRow = value + end + + function Handle:GetColumnList() + local r,c = self.bar:GetButtonGrid() + if self.columnList == nil or #self.columnList ~= c then + local list = { } + for i = 1, c do + table.insert(list,i) + end + self.columnList = list + end + return self.columnList + end + + function Handle:GetSelectedColumn() + local r, c = self.bar:GetButtonGrid() + local col = self.selectedColumn or 1 + if col > c then + col = 1 + end + self.selectedColumn = col + return col + end + + function Handle:SetSelectedColumn(info, value) + self.selectedColumn = value + end + + function Handle:IsPageSelectHidden() + return self.editMethod ~= 1 or (self.config.nPages or 1) < 2 + end + + function Handle:GetPageList() + local n = self.config.nPages or 1 + if self.pageList == nil or #self.pageList ~= n then + local p = { } + for i = 1, n do + table.insert(p,i) + end + self.pageList = p + end + return self.pageList + end + + function Handle:GetSelectedPage() + local p = self.selectedPage or 1 + if p > (self.config.nPages or 1) then + p = 1 + end + self.selectedPage = p + return p + end + + function Handle:SetSelectedPage(info, value) + self.selectedPage = value + end + + function Handle:GetActionID() + local row = self.selectedRow or 1 + local col = self.selectedColumn or 1 + local r, c = self.bar:GetButtonGrid() + local n = (row-1) * c + col + local btn = self.btns[n] + if btn then + return tostring(btn:GetActionID(self.selectedPage or 1)) + end + end + + function Handle:SetActionID(info, value) + local row = self.selectedRow or 1 + local col = self.selectedColumn or 1 + local r, c = self.bar:GetButtonGrid() + local n = (row-1) * c + col + local btn = self.btns[n] + if btn then + btn:SetActionID(tonumber(value), self.selectedPage or 1) + end + end + + function Handle:ValidateActionID(info, value) + value = tonumber(value) + if value == nil or value < 1 or value > 120 then + return L["Specify ID 1-120"] + end + return true + end + + function Handle:IsMultiIDHidden() + return self.editMethod ~= 2 + end + + function Handle:GetMultiID() + local p = { } + for i = 1, self.config.nPages or 1 do + local b = { } + for _, btn in ipairs(self.btns) do + table.insert(b, btn:GetActionID(i)) + end + table.insert(p, table.concat(b,",")) + end + return table.concat(p,";\n") + end + + + local function ParseMultiID(nBtns, nPages, s) + if s:match("[^%d%s,;]") then + ReAction:Print("items other than digits, spaces, commas, and semicolons in string",s) + return nil + end + local p = { } + for list in s:gmatch("[^;]+") do + local pattern = ("^%s?$"):format(("%s*(%d+)%s*,"):rep(nBtns)) + local ids = { list:match(pattern) } + if #ids ~= nBtns then + ReAction:Print("found",#ids,"buttons instead of",nBtns) + return nil + end + table.insert(p,ids) + end + if #p ~= nPages then + ReAction:Print("found",#p,"pages instead of",nPages) + return nil + end + return p + end + + function Handle:SetMultiID(info, value) + local p = ParseMultiID(#self.btns, self.config.nPages or 1, value) + for page, b in ipairs(p) do + for button, id in ipairs(b) do + self.btns[button]:SetActionID(id, page) + end + end + end + + function Handle:ValidateMultiID(info, value) + local bad = L["Invalid action ID list string"] + if value == nil or ParseMultiID(#self.btns, self.config.nPages or 1, value) == nil then + return bad + end + return true + end +end + + +------ State property options ------ +do + local pageOptions = { + page = { + name = L["Show Page #"], + order = 11, + type = "select", + width = "half", + disabled = "IsPageDisabled", + hidden = "IsPageHidden", + values = "GetPageValues", + set = "SetProp", + get = "GetPage", + }, + } + + local function GetBarConfig(bar) + return module.db.profile.bars[bar:GetName()] + end + + function PropHandler.GetOptions() + return pageOptions + end + + function PropHandler:IsPageDisabled() + local c = GetBarConfig(self.bar) + local n = c and c.nPages or 1 + return not (n > 1) + end + + function PropHandler:IsPageHidden() + return not GetBarConfig(self.bar) + end + + function PropHandler:GetPageValues() + if not self._pagevalues then + self._pagevalues = { } + end + local c = GetBarConfig(self.bar) + if c then + local n = c.nPages + -- cache the results + if self._npages ~= n then + self._npages = n + wipe(self._pagevalues) + for i = 1, n do + self._pagevalues["page"..i] = i + end + end + end + return self._pagevalues + end + + function PropHandler:GetPage(info) + return self:GetProp(info) or 1 + end + +end + +------ ActionID allocation ------ +-- this needs to be high performance when requesting new IDs, +-- or certain controls will become sluggish. However, the new-request +-- infrastructure can be built lazily the first time that a new request +-- comes in (which will only happen at user config time: at static startup +-- config time all actionIDs should already have been assigned and stored +-- in the config file) + +local IDAlloc +do + local n = 120 + + IDAlloc = setmetatable({ wrap = 1, freecount = n }, {__index = function() return 0 end}) + + function IDAlloc:Acquire(id, hint) + id = tonumber(id) + hint = tonumber(hint) + if id and (id < 1 or id > n) then + id = nil + end + if hint and (hint < 1 or hint > n) then + hint = nil + end + if id == nil then + -- get a free ID + if hint and self[hint] == 0 then + -- use the hint if it's free + id = hint + elseif self.freecount > 0 then + -- if neither the id nor the hint are defined or free, but + -- the list is known to have free IDs, then start searching + -- at the hint for a free one + for i = hint or 1, n do + if self[i] == 0 then + id = i + break + end + end + -- self.wrap the search + if id == nil and hint and hint > 1 then + for i = 1, hint - 1 do + if self[i] == 0 then + id = i + break + end + end + end + end + if id == nil then + -- if there are no free IDs, start wrapping at 1 + id = self.wrap + self.wrap = id + 1 + if self.wrap > n then + self.wrap = 1 + end + end + end + if self[id] == 0 then + self.freecount = self.freecount - 1 + end + self[id] = self[id] + 1 + return id + end + + function IDAlloc:Release(id) + id = tonumber(id) + if id and (id >= 1 or id <= n) then + self[id] = self[id] - 1 + if self[id] == 0 then + self.freecount = self.freecount + 1 + self.wrap = 1 + end + end + end +end + +------ Button class ------ + +do + local frameRecycler = { } + local trash = CreateFrame("Frame") + local OnUpdate, KBAttach, GetActionName, GetHotkey, SetKey, FreeKey, ClearBindings, GetBindings + do + local ATTACK_BUTTON_FLASH_TIME = ATTACK_BUTTON_FLASH_TIME + local IsActionInRange = IsActionInRange + + local buttonLookup = setmetatable({},{__mode="kv"}) + + function OnUpdate(frame, elapsed) + -- note: This function taints frame.flashtime and frame.rangeTimer. Both of these + -- are only read by ActionButton_OnUpdate (which this function replaces). In + -- all other places they're just written, so it doesn't taint any secure code. + if frame.flashing == 1 then + frame.flashtime = frame.flashtime - elapsed + if frame.flashtime <= 0 then + local overtime = -frame.flashtime + if overtime >= ATTACK_BUTTON_FLASH_TIME then + overtime = 0 + end + frame.flashtime = ATTACK_BUTTON_FLASH_TIME - overtime + + local flashTexture = frame.flash + if flashTexture:IsShown() then + flashTexture:Hide() + else + flashTexture:Show() + end + end + end + + if frame.rangeTimer then + frame.rangeTimer = frame.rangeTimer - elapsed; + + if frame.rangeTimer <= 0 then + if IsActionInRange(frame.action) == 0 then + frame.icon:SetVertexColor(1.0,0.1,0.1) + else + ActionButton_UpdateUsable(frame) + end + frame.rangeTimer = 0.1 + end + end + end + + -- Use KeyBound-1.0 for binding, but use Override bindings instead of + -- regular bindings to support multiple profile use. This is a little + -- weird with the KeyBound dialog box (which has per-char selector as well + -- as an OK/Cancel box) but it's the least amount of effort to implement. + function GetActionName(f) + local b = buttonLookup[f] + if b then + return format("%s:%s", b.bar:GetName(), b.idx) + end + end + + function GetHotkey(f) + local b = buttonLookup[f] + if b then + return KB:ToShortKey(b:GetConfig().hotkey) + end + end + + function SetKey(f, key) + local b = buttonLookup[f] + if b then + local c = b:GetConfig() + if c.hotkey then + SetOverrideBinding(f, false, c.hotkey, nil) + end + if key then + SetOverrideBindingClick(f, false, key, f:GetName(), nil) + end + c.hotkey = key + b:DisplayHotkey(GetHotkey(f)) + end + end + + function FreeKey(f, key) + local b = buttonLookup[f] + if b then + local c = b:GetConfig() + if c.hotkey == key then + local action = f:GetActionName() + SetOverrideBinding(f, false, c.hotkey, nil) + c.hotkey = nil + b:DisplayHotkey(nil) + return action + end + end + return ReAction:FreeOverrideHotkey(key) + end + + function ClearBindings(f) + SetKey(f, nil) + end + + function GetBindings(f) + local b = buttonLookup[f] + if b then + return b:GetConfig().hotkey + end + end + + function KBAttach( button ) + local f = button:GetFrame() + f.GetActionName = GetActionName + f.GetHotkey = GetHotkey + f.SetKey = SetKey + f.FreeKey = FreeKey + f.ClearBindings = ClearBindings + f.GetBindings = GetBindings + buttonLookup[f] = button + f:SetKey(button:GetConfig().hotkey) + ReAction:RegisterKeybindFrame(f) + if ReAction:GetKeybindMode() then + button.border:SetVertexColor(KB:GetColorKeyBoundMode()) + button.border:Show() + end + end + end + + local meta = {__index = Button} + + function Button:New( handle, idx, config, barConfig ) + local bar = handle.bar + + -- create new self + self = setmetatable( + { + bar = bar, + idx = idx, + config = config, + barConfig = barConfig, + }, meta ) + + local name = config.name or ("ReAction_%s_%s_%d"):format(bar:GetName(),moduleID,idx) + self.name = name + config.name = name + local lastButton = handle:GetLastButton() + config.actionID = IDAlloc:Acquire(config.actionID, lastButton and lastButton.config.actionID) -- gets a free one if none configured + self.nPages = 1 + + -- have to recycle frames with the same name: CreateFrame() doesn't overwrite + -- existing globals. Can't set to nil in the global because it's then tainted. + local parent = bar:GetFrame() + local f = frameRecycler[name] + if f then + f:SetParent(parent) + else + f = CreateFrame("CheckButton", name, parent, "ActionBarButtonTemplate") + -- ditch the old hotkey text because it's tied in ActionButton_Update() to the + -- standard binding. We use override bindings. + local hotkey = _G[name.."HotKey"] + hotkey:SetParent(trash) + hotkey = f:CreateFontString(nil, "ARTWORK", "NumberFontNormalSmallGray") + hotkey:SetWidth(36) + hotkey:SetHeight(18) + hotkey:SetJustifyH("RIGHT") + hotkey:SetJustifyV("TOP") + hotkey:SetPoint("TOPLEFT",f,"TOPLEFT",-2,-2) + f.hotkey = hotkey + f.icon = _G[name.."Icon"] + f.flash = _G[name.."Flash"] + f:SetScript("OnUpdate",OnUpdate) + end + + self.hotkey = f.hotkey + self.border = _G[name.."Border"] + + f:SetAttribute("action", config.actionID) + -- install mind control actions for all buttons just for simplicity + if self.idx <= 12 then + f:SetAttribute("*action-mc", 120 + self.idx) + end + + -- set a _childupdate handler, called within the header's context + f:SetAttribute("_childupdate", + -- function _childupdate(self, snippetid, message) + [[ + local action = "action" + if doMindControl and GetBonusBarOffset() == 5 then + action = "*action-mc" + elseif page and state and page[state] then + action = "*action-"..page[state] + end + local value = self:GetAttribute(action) + self:SetAttribute("action",value) + ]]) + + -- install drag wrappers to lock buttons + bar:GetFrame():WrapScript(f, "OnDragStart", + -- OnDragStart(self, button, kind, value, ...) + [[ + if lockButtons and (PlayerInCombat() or not lockButtonsCombat) and not IsModifiedClick("PICKUPACTION") then + return "clear" + end + ]]) + + self.frame = f + + + -- initialize the hide state + f:SetAttribute("showgrid",0) + self:ShowGrid(not barConfig.hideEmpty) + if ReAction:GetConfigMode() then + self:ShowGrid(true) + end + + -- show the ID label if applicable + self:ShowActionIDLabel(ReAction:GetConfigMode()) + + -- attach the keybinder + KBAttach(self) + + -- attach to skinner + bar:SkinButton(self, + { + HotKey = self.hotkey, + } + ) + + self:Refresh() + return self + end + + function Button:Destroy() + local f = self.frame + f:UnregisterAllEvents() + f:Hide() + f:SetParent(UIParent) + f:ClearAllPoints() + if self.name then + frameRecycler[self.name] = f + end + if self.config.actionID then + IDAlloc:Release(self.config.actionID) + end + if self.config.pageactions then + for _, id in ipairs(self.config.pageactions) do + IDAlloc:Release(id) + end + end + self.frame = nil + self.config = nil + self.bar = nil + end + + function Button:Refresh() + local f = self.frame + self.bar:PlaceButton(self, 36, 36) + self:RefreshPages() + end + + function Button:GetFrame() + return self.frame + end + + function Button:GetName() + return self.name + end + + function Button:GetConfig() + return self.config + end + + function Button:GetActionID(page) + if page == nil then + -- get the effective ID + return self.frame.action -- kept up-to-date by Blizzard's ActionButton_CalculateAction() + else + if page == 1 then + return self.config.actionID + else + return self.config.pageactions and self.config.pageactions[page] or self.config.actionID + end + end + end + + function Button:SetActionID( id, page ) + id = tonumber(id) + page = tonumber(page) + if id == nil or id < 1 or id > 120 then + error("Button:SetActionID - invalid action ID") + end + if page and page ~= 1 then + if not self.config.pageactions then + self.config.pageactions = { } + end + if self.config.pageactions[page] then + IDAlloc:Release(self.config.pageactions[page]) + end + self.config.pageactions[page] = id + IDAlloc:Acquire(self.config.pageactions[page]) + self.frame:SetAttribute(("*action-page%d"):format(page),id) + else + IDAlloc:Release(self.config.actionID) + self.config.actionID = id + IDAlloc:Acquire(self.config.actionID) + self.frame:SetAttribute("action",id) + if self.config.pageactions then + self.config.pageactions[1] = id + self.frame:SetAttribute("*action-page1",id) + end + end + end + + function Button:RefreshPages( force ) + local nPages = self.barConfig.nPages + if nPages and (nPages ~= self.nPages or force) then + local f = self:GetFrame() + local c = self.config.pageactions + if nPages > 1 and not c then + c = { } + self.config.pageactions = c + end + for i = 1, nPages do + if i > 1 then + c[i] = IDAlloc:Acquire(c[i], self.config.actionID + (i-1)*self.bar:GetNumButtons()) + else + c[i] = self.config.actionID -- page 1 is the same as the base actionID + end + f:SetAttribute(("*action-page%d"):format(i),c[i]) + end + for i = nPages+1, #c do + IDAlloc:Release(c[i]) + c[i] = nil + f:SetAttribute(("*action-page%d"):format(i),nil) + end + self.nPages = nPages + end + end + + function Button:ShowGrid( show ) + if not InCombatLockdown() then + local f = self.frame + local count = f:GetAttribute("showgrid") + if show then + count = count + 1 + else + count = count - 1 + end + if count < 0 then + count = 0 + end + f:SetAttribute("showgrid",count) + + if count >= 1 and not f:GetAttribute("statehidden") then + if LBF then + LBF:SetNormalVertexColor(self.frame, 1.0, 1.0, 1.0, 0.5) + else + self.frame:GetNormalTexture():SetVertexColor(1.0, 1.0, 1.0, 0.5); + end + f:Show() + elseif count < 1 and not HasAction(self:GetActionID()) then + f:Hide() + end + end + end + + function Button:ShowActionIDLabel( show ) + local f = self:GetFrame() + if show then + local id = self:GetActionID() + if not f.actionIDLabel then + local label = f:CreateFontString(nil,"OVERLAY","GameFontNormalLarge") + label:SetAllPoints() + label:SetJustifyH("CENTER") + label:SetShadowColor(0,0,0,1) + label:SetShadowOffset(2,-2) + f.actionIDLabel = label -- store the label with the frame for recycling + + f:HookScript("OnAttributeChanged", + function(frame, attr, value) + if label:IsVisible() and attr:match("action") then + label:SetText(tostring(frame.action)) + end + end) + end + f.actionIDLabel:SetText(tostring(id)) + f.actionIDLabel:Show() + elseif f.actionIDLabel then + f.actionIDLabel:Hide() + end + end + + function Button:DisplayHotkey( key ) + self.hotkey:SetText(key or "") + end +end
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/modules/Bar.lua Thu Jan 08 00:57:27 2009 +0000 @@ -0,0 +1,245 @@ +local ReAction = ReAction +local L = ReAction.L +local _G = _G +local CreateFrame = CreateFrame +local floor = math.floor +local fmod = math.fmod +local format = string.format + +ReAction:UpdateRevision("$Revision$") + +local Bar = { } +local proto = { __index = Bar } +local weak = { __mode = "k" } + +ReAction.Bar = Bar -- export to ReAction + +function Bar:New( name, config ) + if type(config) ~= "table" then + error("ReAction.Bar: config table required") + end + + -- create new self + self = setmetatable( + { + config = config, + name = name, + buttons = setmetatable( { }, weak ), + width = config.width or 480, + height = config.height or 40 + }, + proto ) + + -- The frame type is 'Button' in order to have an OnClick handler. However, the frame itself is + -- not mouse-clickable by the user. + local parent = config.parent and (ReAction:GetBar(config.parent) or _G[config.parent]) or UIParent + local f = CreateFrame("Button", name and format("ReAction-%s",name), parent, + "SecureHandlerStateTemplate, SecureHandlerClickTemplate") + f:SetFrameStrata("MEDIUM") + f:SetWidth(self.width) + f:SetWidth(self.height) + f:SetAlpha(config.alpha or 1.0) + f:Show() + f:EnableMouse(false) + f:SetClampedToScreen(true) + + -- Override the default frame accessor to provide strict read-only access + function self:GetFrame() + return f + end + + self:ApplyAnchor() + ReAction.RegisterCallback(self, "OnConfigModeChanged") + + return self +end + +function Bar:Destroy() + local f = self:GetFrame() + f:UnregisterAllEvents() + ReAction.UnregisterAllCallbacks(self) + f:Hide() + f:SetParent(UIParent) + f:ClearAllPoints() +end + +function Bar:OnConfigModeChanged(event, mode) + self:ShowControls(mode) -- Bar:ShowControls() defined in Overlay.lua +end + +function Bar:ApplyAnchor() + local f = self:GetFrame() + local c = self.config + local p = c.point + + f:SetWidth(c.width) + f:SetHeight(c.height) + f:ClearAllPoints() + + if p then + local a = f:GetParent() + if c.anchor then + local bar = ReAction:GetBar(c.anchor) + if bar then + a = bar:GetFrame() + else + a = _G[c.anchor] + end + end + local fr = a or f:GetParent() + f:SetPoint(p, a or f:GetParent(), c.relpoint, c.x or 0, c.y or 0) + else + f:SetPoint("CENTER") + end +end + +function Bar:SetAnchor(point, frame, relativePoint, x, y) + local c = self.config + c.point = point or c.point + c.anchor = frame or c.anchor + c.relpoint = relativePoint or c.relpoint + c.x = x or c.x + c.y = y or c.y + self:ApplyAnchor() + ReAction:RefreshBar(self) +end + +function Bar:GetAnchor() + local c = self.config + return (c.point or "CENTER"), + (c.anchor or self:GetFrame():GetParent():GetName()), + (c.relpoint or c.point or "CENTER"), + (c.x or 0), + (c.y or 0) +end + +function Bar:GetSize() + local f = self:GetFrame() + return f:GetWidth(), f:GetHeight() +end + +function Bar:SetSize(w,h) + local f = self:GetFrame() + self.config.width = w + self.config.height = h + f:SetWidth(w) + f:SetHeight(h) +end + +function Bar:GetButtonSize() + local w = self.config.btnWidth or 32 + local h = self.config.btnHeight or 32 + -- TODO: get from modules? + return w,h +end + +function Bar:SetButtonSize(w,h) + if w > 0 and h > 0 then + self.config.btnWidth = w + self.config.btnHeight = h + end + ReAction:RefreshBar(self) +end + +function Bar:GetButtonGrid() + local cfg = self.config + local r = cfg.btnRows or 1 + local c = cfg.btnColumns or 1 + local s = cfg.spacing or 4 + return r,c,s +end + +function Bar:GetNumButtons() + local r,c = self:GetButtonGrid() + return r*c +end + +function Bar:SetButtonGrid(r,c,s) + if r > 0 and c > 0 and s > 0 then + local cfg = self.config + cfg.btnRows = r + cfg.btnColumns = c + cfg.spacing = s + end + ReAction:RefreshBar(self) +end + +function Bar:ClipNButtons( n ) + local cfg = self.config + local r = cfg.btnRows or 1 + local c = cfg.btnColumns or 1 + + cfg.btnRows = ceil(n/c) + cfg.btnColumns = min(n,c) +end + +function Bar:GetName() + return self.name +end + +function Bar:GetFrame() + -- this method is included for documentation purposes. It is overridden + -- for each object in the :New() method. + error("Invalid Bar object: used without initialization") +end + +-- only ReAction:RenameBar() should call this function. Calling from any other +-- context will desync the bar list in the ReAction class. +function Bar:SetName(name) + self.name = name + self:SetLabel(self.name) -- Bar:SetLabel() defined in Overlay.lua +end + +function Bar:GetAlpha() + return self.config.alpha or 1.0 +end + +function Bar:SetAlpha(value) + self.config.alpha = value + self:GetFrame():SetAlpha(value or 1.0) + ReAction:RefreshBar(self) +end + +function Bar:AddButton(idx, button) + local f = self:GetFrame() + + -- store in a weak reverse-index array + self.buttons[button] = idx + + -- Store a properly wrapped reference to the child frame as an attribute + -- (accessible via "frameref-btn#") + f:SetFrameRef(format("btn%d",idx), button:GetFrame()) +end + +function Bar:RemoveButton(button) + local idx = self.buttons[button] + if idx then + self:GetFrame():SetAttribute(format("frameref-btn%d",idx),nil) + self.buttons[button] = nil + end +end + +-- iterator returns button, idx and does NOT iterate in index order +function Bar:IterateButtons() + return pairs(self.buttons) +end + +function Bar:PlaceButton(button, baseW, baseH) + local idx = self.buttons[button] + if idx then + local r, c, s = self:GetButtonGrid() + local bh, bw = self:GetButtonSize() + local row, col = floor((idx-1)/c), fmod((idx-1),c) -- zero-based + local x, y = col*bw + (col+0.5)*s, -(row*bh + (row+0.5)*s) + local scale = bw/baseW + local b = button:GetFrame() + + b:ClearAllPoints() + b:SetPoint("TOPLEFT",x/scale,y/scale) + b:SetScale(scale) + end +end + +function Bar:SkinButton() + -- does nothing by default +end \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/modules/Bindings.lua Thu Jan 08 00:57:27 2009 +0000 @@ -0,0 +1,7 @@ +local L = ReAction.L +local _G = _G + +_G["BINDING_HEADER_REACTION"] = L["ReAction"] + +_G["BINDING_NAME_REACTION_TOGGLELOCK"] = L["Toggle ReAction Bar Lock"] +_G["BINDING_NAME_REACTION_TOGGLEKEYBIND"] = L["ReAction Keybinding Mode"]
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/modules/Config.lua Thu Jan 08 00:57:27 2009 +0000 @@ -0,0 +1,529 @@ +--[[ + ReAction Configuration UI module + + Hooks into Blizzard Interface Options AddOns panel +--]] + +-- local imports +local ReAction = ReAction +local L = ReAction.L +local _G = _G +local AceConfigReg = LibStub("AceConfigRegistry-3.0") +local AceConfigDialog = LibStub("AceConfigDialog-3.0") + +ReAction:UpdateRevision("$Revision$") + +-- some constants +local configName = "ReAction" + +-- module declaration +local moduleID = "ConfigUI" +local module = ReAction:NewModule( moduleID, + "AceEvent-3.0" +) + +-- module methods +function module:OnInitialize() + self.db = ReAction.db:RegisterNamespace( moduleID, + { + profile = { + closeOnLaunch = true, + editorCloseOnLaunch = true, + } + } + ) + + self:RegisterEvent("PLAYER_REGEN_DISABLED") + ReAction.RegisterCallback(self,"OnOptionsRegistered","OnOptionsRefreshed") + ReAction.RegisterCallback(self,"OnOptionsRefreshed") + self:InitializeOptions() +end + +function module:OnOptionsRefreshed(evt) + AceConfigReg:NotifyChange(configName) + if self.editor then self.editor:Refresh() end +end + +function module:PLAYER_REGEN_DISABLED() + if self.editor then + self.editor:Hide() + end +end + +function module:OpenConfig() + InterfaceOptionsFrame_OpenToCategory(configName) +end + +function module:InitializeOptions() + ReAction:RegisterOptions(self, { + _launchEditor = { + type = "execute", + handler = self, + name = L["Edit Bars..."], + desc = L["Show the ReAction Bar Editor dialogue"], + func = function() + self:LaunchBarEditor() + -- you can't close a dialog in response to an options click, because the end of the + -- handler for all the button events calls lib:Open() + -- So, schedule a close on the next OnUpdate + if self.db.profile.closeOnLaunch then + self.editor.closePending = true + end + end, + order = 2, + }, + _closeThis = { + type = "toggle", + name = L["Close on Launch"], + desc = L["Close the Interface Options window when launching the ReAction Bar Editor"], + get = function() return self.db.profile.closeOnLaunch end, + set = function(info, val) self.db.profile.closeOnLaunch = val end, + order = 3, + }, + _keybind = { + type = "execute", + handler = self, + name = L["Key Bindings"], + desc = L["Show the keybinding dialogue"], + func = function() + ReAction:SetKeybindMode(true) + end, + order = 4, + }, + }, true) -- global + + AceConfigReg:RegisterOptionsTable(configName,ReAction.options) + self.frame = AceConfigDialog:AddToBlizOptions(configName, configName) + self.frame.obj:SetCallback("default", + function() + ReAction.db:ResetProfile() + ReAction:RefreshOptions() + end ) +end + + + + +-- Bar Editor -- +local function NewEditor() + -- private variables + local editorName = "ReAction-Editor" + local barOptMap = setmetatable({},{__mode="v"}) + local tmp = { } + local pointTable = { + CENTER = L["Center"], + LEFT = L["Left"], + RIGHT = L["Right"], + TOP = L["Top"], + BOTTOM = L["Bottom"], + TOPLEFT = L["Top Left"], + TOPRIGHT = L["Top Right"], + BOTTOMLEFT = L["Bottom Left"], + BOTTOMRIGHT = L["Bottom Right"], + } + + + -- use a local GUI container to work around AceConfigDialog closing + -- both the bar editor and the global options when interface options is closed + local editor = LibStub("AceGUI-3.0"):Create("Frame") + local frame = editor.frame + frame:SetClampedToScreen(true) + frame:Hide() + local old_OnUpdate = frame:GetScript("OnUpdate") + frame:SetScript("OnUpdate", function(dt) + if old_OnUpdate then + old_OnUpdate(dt) + end + if editor.closePending then + InterfaceOptionsFrame:Hide() + editor.closePending = false + end + if editor.selfClosePending then + editor:Hide() + AceConfigReg:NotifyChange(configName) + editor.selfClosePending = false + end + end ) + editor:SetCallback("OnClose", + function() + ReAction:SetConfigMode(false) + end ) + AceConfigDialog:SetDefaultSize(editorName, 700, 540) + + + local name = ("ReAction - %s"):format(L["Bar Editor"]) + editor:SetTitle(name) + local options = { + type = "group", + name = name, + handler = editor, + childGroups = "tree", + args = { + desc = { + type = "description", + name = L["Use the mouse to arrange and resize the bars on screen. Tooltips on bars indicate additional functionality."], + order = 1 + }, + launchConfig = { + type = "execute", + name = L["Global Config"], + desc = L["Opens ReAction global configuration settings panel"], + func = function() + module:OpenConfig() + -- you can't close a dialog in response to an options click, because the end of the + -- handler for all the button events calls lib:Open() + -- So, schedule a close on the next OnUpdate + if module.db.profile.editorCloseOnLaunch then + editor.selfClosePending = true + end + end, + order = 2 + }, + closeThis = { + type = "toggle", + name = L["Close on Launch"], + desc = L["Close the Bar Editor when opening the ReAction global Interface Options"], + get = function() return module.db.profile.editorCloseOnLaunch end, + set = function(info, val) module.db.profile.editorCloseOnLaunch = val end, + order = 3, + }, + new = { + type = "group", + name = L["New Bar..."], + order = 4, + args = { + desc = { + type = "description", + name = L["Choose a name, type, and initial grid for your new action bar:"], + order = 1, + }, + name = { + type = "input", + name = L["Bar Name"], + desc = L["Enter a name for your new action bar"], + get = function() return tmp.barName or "" end, + set = function(info, val) tmp.barName = val end, + order = 2, + }, + type = { + type = "select", + name = L["Button Type"], + get = function() return tmp.barType or ReAction:GetDefaultBarType() or "" end, + set = function(info, val) + local c = ReAction:GetBarTypeConfig(val) + tmp.barType = val + tmp.barSize = c.defaultButtonSize or tmp.barSize + tmp.barRows = c.defaultBarRows or tmp.barRows + tmp.barCols = c.defaultBarCols or tmp.barCols + tmp.barSpacing = c.defaultBarSpacing or tmp.barSpacing + end, + values = "GetBarTypes", + order = 3, + }, + grid = { + type = "group", + name = L["Button Grid"], + inline = true, + args = { + hdr = { + type = "header", + name = L["Button Grid"], + order = 1, + }, + rows = { + type = "range", + name = L["Rows"], + get = function() return tmp.barRows or 1 end, + set = function(info, val) tmp.barRows = val end, + width = "half", + min = 1, + max = 32, + step = 1, + order = 2, + }, + cols = { + type = "range", + name = L["Columns"], + get = function() return tmp.barCols or 12 end, + set = function(info, val) tmp.barCols = val end, + width = "half", + min = 1, + max = 32, + step = 1, + order = 3, + }, + sz = { + type = "range", + name = L["Size"], + get = function() return tmp.barSize or 36 end, + set = function(info, val) tmp.barSize = val end, + width = "half", + min = 10, + max = 72, + step = 1, + order = 4, + }, + spacing = { + type = "range", + name = L["Spacing"], + get = function() return tmp.barSpacing or 3 end, + set = function(info, val) tmp.barSpacing = val end, + width = "half", + min = 0, + max = 24, + step = 1, + order = 5, + } + }, + order = 4 + }, + spacer = { + type = "header", + name = "", + width = "full", + order = -2 + }, + go = { + type = "execute", + name = L["Create Bar"], + func = "CreateBar", + order = -1, + } + } + } + } + } + AceConfigReg:RegisterOptionsTable(editorName, options) + + function editor:Open(bar, ...) + if not frame:IsVisible() then + AceConfigDialog:Open(editorName,self) + end + if bar then + AceConfigDialog:SelectGroup(editorName, barOptMap[bar:GetName()], ...) + end + end + + function editor:Refresh() + AceConfigReg:NotifyChange(editorName) + end + + function editor:CreateBarTree(bar) + local name = bar:GetName() + -- AceConfig doesn't allow spaces, etc, in arg key names, and they must be + -- unique strings. So generate a unique key (it can be whatever) for the bar + local args = options.args + local key + local i = 1 + repeat + key = ("bar%s"):format(i) + i = i+1 + until args[key] == nil + barOptMap[name] = key + args[key] = { + type = "group", + name = name, + childGroups = "tab", + args = { + general = { + type = "group", + name = L["General"], + order = 1, + args = { + name = { + type = "input", + name = L["Rename Bar"], + get = function() return bar:GetName() end, + set = function(info, value) return ReAction:RenameBar(bar, value) end, + order = 1, + }, + delete = { + type = "execute", + name = L["Delete Bar"], + desc = function() return bar:GetName() end, + confirm = true, + func = function() ReAction:EraseBar(bar) end, + order = 2 + }, + anchor = { + type = "group", + name = L["Anchor"], + inline = true, + args = { + frame = { + type = "input", + name = L["Frame"], + desc = L["The frame that the bar is anchored to"], + get = function() local _, f = bar:GetAnchor(); return f end, + set = function(info, val) bar:SetAnchor(nil,val) end, + validate = function(info, name) + if name then + local f = ReAction:GetBar(name) + if f then + return true + else + f = _G[name] + if f and type(f) == "table" and f.IsObjectType and f:IsObjectType("Frame") then + local _, explicit = f:IsProtected() + return explicit + end + end + end + return false + end, + width = "double", + order = 1 + }, + point = { + type = "select", + name = L["Point"], + desc = L["Anchor point on the bar frame"], + style = "dropdown", + get = function() return bar:GetAnchor() end, + set = function(info, val) bar:SetAnchor(val) end, + values = pointTable, + order = 2, + }, + relativePoint = { + type = "select", + name = L["Relative Point"], + desc = L["Anchor point on the target frame"], + style = "dropdown", + get = function() local p,f,r = bar:GetAnchor(); return r end, + set = function(info, val) bar:SetAnchor(nil,nil,val) end, + values = pointTable, + order = 3, + }, + x = { + type = "input", + pattern = "\-?%d+", + name = L["X offset"], + get = function() local p,f,r,x = bar:GetAnchor(); return ("%d"):format(x) end, + set = function(info,val) bar:SetAnchor(nil,nil,nil,val) end, + order = 4 + }, + y = { + type = "input", + pattern = "\-?%d+", + name = L["Y offset"], + get = function() local p,f,r,x,y = bar:GetAnchor(); return ("%d"):format(y) end, + set = function(info,val) bar:SetAnchor(nil,nil,nil,nil,val) end, + order = 5 + }, + }, + order = 3 + }, + alpha = { + type = "range", + name = L["Transparency"], + get = function() return bar:GetAlpha() end, + set = function(info, val) bar:SetAlpha(val) end, + min = 0, + max = 1, + isPercent = true, + step = 0.01, + bigStep = 0.05, + order = 4, + }, + }, + }, + } + } + self:RefreshBarTree(bar) + end + + function editor:RefreshBarTree(bar) + local key = barOptMap[bar:GetName()] + if key and options.args[key] then + options.args[key].plugins = ReAction:GenerateBarOptionsTable(bar) + AceConfigReg:NotifyChange(editorName) + end + end + + function editor:OnCreateBar(evt, bar) + if not tmp.creating then + -- a bit of hack to work around OnCreateBar event handler ordering + self:CreateBarTree(bar) + end + end + + function editor:OnDestroyBar(evt, bar, name) + local key = barOptMap[name] + if key then + options.args[key] = nil + end + self:Refresh() + end + + function editor:OnEraseBar(evt, name) + local key = barOptMap[name] + barOptMap[name] = nil + if key then + options.args[key] = nil + self:Refresh() + end + end + + function editor:OnRenameBar(evt, bar, oldname, newname) + local key = barOptMap[oldname] + barOptMap[oldname], barOptMap[newname] = nil, key + if key then + options.args[key].name = newname + self:Refresh() + end + end + + function editor:OnBarOptionGeneratorRegistered(evt) + for name in pairs(barOptMap) do + local bar = ReAction:GetBar(name) + if bar then + self:RefreshBarTree(bar) + end + end + end + + local _scratch = { } + function editor:GetBarTypes() + for k,v in pairs(_scratch) do + _scratch[k] = nil + end + return ReAction:GetBarTypeOptions(_scratch) + end + + function editor:CreateBar() + if tmp.barName and tmp.barName ~= "" then + tmp.creating = true + local bar = ReAction:CreateBar(tmp.barName, tmp.barType or ReAction:GetDefaultBarType(), tmp.barRows, tmp.barCols, tmp.barSize, tmp.barSpacing) + self:CreateBarTree(bar) + AceConfigDialog:SelectGroup(editorName, barOptMap[tmp.barName]) + tmp.barName = nil + tmp.creating = false + end + end + + ReAction.RegisterCallback(editor,"OnCreateBar") + ReAction.RegisterCallback(editor,"OnDestroyBar") + ReAction.RegisterCallback(editor,"OnEraseBar") + ReAction.RegisterCallback(editor,"OnRenameBar") + ReAction.RegisterCallback(editor,"OnBarOptionGeneratorRegistered") + + for name, bar in ReAction:IterateBars() do + editor:CreateBarTree(bar) + end + + return editor +end + + +function module:LaunchBarEditor(bar, ...) + if InCombatLockdown() then + ReAction:UserError(L["ReAction config mode disabled during combat."]) + else + if not self.editor then + self.editor = NewEditor() + end + self.editor:Open(bar, ...) + ReAction:SetConfigMode(true) + end +end +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/modules/HideBlizzard.lua Thu Jan 08 00:57:27 2009 +0000 @@ -0,0 +1,133 @@ +--[[ + ReAction 'Hide Blizzard' module + + Hides Blizzard action bars. This hides the extra action bars, stance bar, pet bar, and + main menu bar, which in turn hides the experience bar, bag bar, micro menu bar, and lag meter. + +--]] + +-- local imports +local ReAction = ReAction +local L = ReAction.L + +ReAction:UpdateRevision("$Revision$") + +-- module declaration +local moduleID = "HideBlizzard" +local module = ReAction:NewModule( moduleID ) + + +-- module methods +function module:OnInitialize() + self.db = ReAction.db:RegisterNamespace( moduleID, + { + profile = { + hide = false + } + } + ) + self.db.RegisterCallback(self,"OnProfileChanged") + self.db.RegisterCallback(self,"OnProfileCopied", "OnProfileChanged") + self.db.RegisterCallback(self,"OnProfileReset", "OnProfileChanged") + + self.hiddenFrame = CreateFrame("Frame") + self.hiddenFrame:Hide() + + ReAction:RegisterOptions(self, { + hideBlizzard = { + type = "toggle", + handler = self, + name = L["Hide Blizzard Action Bars"], + desc = L["Hide the default main bar and extra action bars"], + get = "IsHidden", + set = function(info,value) self:SetHidden(value) end, + disabled = InCombatLockdown + }, + }, true) -- global + +end + +function module:OnEnable() + if self.db.profile.hide then + self:HideAll(true) + end + + -- reroute blizzard action bar config to ReAction config window + InterfaceOptionsActionBarsPanel:HookScript("OnShow", + function() + if module:IsEnabled() and module:IsHidden() then + ReAction:ShowConfig() + end + end ) +end + +function module:OnDisable() + self:ShowAll(true) +end + +function module:OnProfileChanged() + if self.db.profile.hide then + module:HideAll(true) + else + module:ShowAll(true) + end +end + +local frames = { + MainMenuBar, + MultiBarLeft, + MultiBarRight, + MultiBarBottomLeft, + MultiBarBottomRight, + -- possess bar frame needs to be pulled out separately: stash its children away + PossessBarLeft, + PossessBarRight, + PossessButton1, + PossessButton2 +} + +local hidden = { } + +function module:HideAll( force ) + if not(self.db.profile.hide) or force then + self.db.profile.hide = true + for _, f in pairs(frames) do + hidden[f] = hidden[f] or { parent = f:GetParent(), wasShown = f:IsShown() } + f:SetParent(self.hiddenFrame) + f:Hide() + end + end + PossessBarFrame:SetParent(UIParent) + PossessBarFrame:EnableMouse(false) +end + +function module:ShowAll( force ) + PossessBarFrame:EnableMouse(true) + PossessBarFrame:SetParent(MainMenuBar) + if self.db.profile.hide or force then + self.db.profile.hide = false + + for _, f in pairs(frames) do + local h = hidden[f] + if h then + f:SetParent(h.parent) + if h.wasShown then + f:Show() + end + end + end + end +end + +function module:IsHidden() + return self.db.profile.hide +end + +function module:SetHidden(h) + if h then + self:HideAll() + else + self:ShowAll() + end +end +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/modules/LBF.lua Thu Jan 08 00:57:27 2009 +0000 @@ -0,0 +1,115 @@ +-- local imports +local ReAction = ReAction +local L = ReAction.L +local _G = _G + +-- module declaration +local moduleID = "ButtonFacade" +local module = ReAction:NewModule( moduleID ) + +-- handlers +function module:OnInitialize() + self.db = ReAction.db:RegisterNamespace( moduleID, + { + profile = { + -- default profile goes here + } + } + ) + + local LBF = LibStub("LibButtonFacade",true) + + if not LBF then -- no more initialization + return + end + + self.LBF = LBF + self.groups = { } + + -- override a method of ReAction.Bar + -- note that 'self' in this context refers to the bar + function ReAction.Bar:SkinButton( button, data ) + module:GetGroup(self:GetName()):AddButton(button:GetFrame(), data) + end + + -- register some common events + ReAction.RegisterCallback(self, "OnCreateBar") + ReAction.RegisterCallback(self, "OnDestroyBar") + ReAction.RegisterCallback(self, "OnRefreshBar") + ReAction.RegisterCallback(self, "OnEraseBar") + ReAction.RegisterCallback(self, "OnRenameBar") + + self.LBF:RegisterSkinCallback("ReAction", self.OnSkinChanged, self) +end + +function module:OnEnable() + +end + +function module:OnDisable() + +end + +function module:OnCreateBar(event, bar, name) + local c = self.db.profile[name] + if not c then + c = { + skinID = "Blizzard", + backdrop = true, + gloss = 0, + colors = {}, + } + self.db.profile[name] = c + end + + local g = self:GetGroup(name) + g.SkinID = c.skinID or "Blizzard" + g.Backdrop = c.backdrop + g.Gloss = c.gloss + g.Colors = c.colors +end + +function module:OnDestroyBar(event, bar, name) + if self.groups[name] then + self.groups[name]:Delete() + self.groups[name] = nil + end +end + +function module:OnRefreshBar(event, bar, name) + local c = self.db.profile[name] + local g = self.groups[name] + if c and g then + g:Skin(c.skinID, c.gloss, c.backdrop, c.colors) + end +end + +function module:OnEraseBar(event, bar, name) + self:OnDestroyBar(event, bar, name) + self.db.profile[name] = nil +end + +function module:OnRenameBar(event, bar, oldName, newName) + if self.groups[name] then + self.groups[name]:Delete(true) + self.db.profile[oldName], self.db.profile[newName] = nil, self.db.profile[oldName] + self:OnCreateBar(event, bar, newName) + end +end + +function module:OnSkinChanged( skinID, gloss, backdrop, group, button, colors ) + local c = self.db.profile[group] + if c then + c.skinID = skinID + c.gloss = gloss + c.backdrop = backdrop + c.colors = colors + end +end + +function module:GetGroup( name ) + if not self.groups[name] then + self.groups[name] = self.LBF:Group("ReAction", name) + end + return self.groups[name] +end
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/modules/LDB.lua Thu Jan 08 00:57:27 2009 +0000 @@ -0,0 +1,36 @@ +local ReAction = ReAction +local L = ReAction.L +local format = string.format + +-- Export ReAction launcher to LibDataBroker-aware displays +LibStub:GetLibrary("LibDataBroker-1.1"):NewDataObject( "ReAction", + { + type = "launcher", + icon = "Interface\\Icons\\INV_Qiraj_JewelEncased", + + OnClick = function( frame, button ) + if not InCombatLockdown() then + if IsAltKeyDown() then + ReAction:SetKeybindMode( not ReAction:GetKeybindMode() ) + elseif IsShiftKeyDown() then + ReAction:SetConfigMode( not ReAction:GetConfigMode() ) + elseif button == "RightButton" then + ReAction:ShowEditor() + else + ReAction:ShowConfig() + end + else + ReAction:UserError(L["ReAction: can't configure in combat"]) + end + end, + + -- this isn't included in the 'launcher' type LDB spec but it seems all launcher displays use it + OnTooltipShow = function( tooltip ) + tooltip:AddLine(format("|cffffffff%s|r %s",L["Click"],L["for global configuration"])) + tooltip:AddLine(format("|cffffd200%s|r %s",L["Right-click"],L["for bar editor dialog"])) + tooltip:AddLine(format("|cff00ff00%s|r %s",L["Shift-click"],L["to unlock bars"])) + tooltip:AddLine(format("|cff00cccc%s|r %s",L["Alt-click"],L["for keybind mode"])) + end, + + } +)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/modules/ModuleTemplate.lua Thu Jan 08 00:57:27 2009 +0000 @@ -0,0 +1,73 @@ +--[[ + ReAction module template + +--]] + +-- local imports +local ReAction = ReAction +local L = ReAction.L +local _G = _G + +-- module declaration +local moduleID = "MyModuleName" +local module = ReAction:NewModule( moduleID, + -- mixins go here +) + +-- handlers +function module:OnInitialize() + self.db = ReAction.db:RegisterNamespace( moduleID + { + profile = { + -- default profile goes here + } + } + ) + + -- register some common events + ReAction.RegisterCallback(self, "OnCreateBar") + ReAction.RegisterCallback(self, "OnDestroyBar") + ReAction.RegisterCallback(self, "OnRefreshBar") + ReAction.RegisterCallback(self, "OnEraseBar") + ReAction.RegisterCallback(self, "OnRenameBar") + ReAction.RegisterCallback(self, "OnConfigModeChanged") +end + +function module:OnEnable() + +end + +function module:OnDisable() + +end + +-- apply module features and settings to a bar object (see Bar.lua for Bar API) +function module:OnCreateBar(event, bar, name) + +end + +-- remove module features and settings from a bar object +function module:OnDestroyBar(event, bar, name) + +end + +-- refresh module features and settings on a bar object +function module:OnRefreshBar(event, bar, name) + +end + +-- erase any local configuration entries for the supplied bar name +function module:OnEraseBar(event, bar, name) + +end + +-- update any local configuration/option entries with the new bar name index +function module:OnRenameBar(event, bar, oldName, newName) + +end + +-- update any local display/options based on config mode (true/false) +function module:OnConfigModeChanged(event, mode) + +end +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/modules/Overlay.lua Thu Jan 08 00:57:27 2009 +0000 @@ -0,0 +1,769 @@ +local ReAction = ReAction +local L = ReAction.L +local CreateFrame = CreateFrame +local InCombatLockdown = InCombatLockdown +local floor = math.floor +local min = math.min +local format = string.format +local GameTooltip = GameTooltip +local Bar = ReAction.Bar +local GetSize = Bar.GetSize +local SetSize = Bar.SetSize +local GetButtonSize = Bar.GetButtonSize +local GetButtonGrid = Bar.GetButtonGrid +local SetButtonSize = Bar.SetButtonSize +local SetButtonGrid = Bar.SetButtonGrid +local ApplyAnchor = Bar.ApplyAnchor +local GameTooltipTextRight1 = GameTooltipTextRight1 +local GameTooltipTextRight2 = GameTooltipTextRight2 +local GameTooltipTextRight3 = GameTooltipTextRight3 + +local KB = LibStub("LibKeyBound-1.0") + +ReAction:UpdateRevision("$Revision$") + + +-- +-- Wrap some of the bar manipulators to make them state-aware +-- +local function SetAnchor( bar, point, frame, relPoint, x, y ) + local state = bar:GetState() + if state then + local anchorstate = bar:GetStateProperty(state, "anchorEnable") + if anchorstate then + bar:SetStateProperty(state, "anchorFrame", frame) + bar:SetStateProperty(state, "anchorPoint", point) + bar:SetStateProperty(state, "anchorRelPoint", relPoint) + bar:SetStateProperty(state, "anchorX", x or 0) + bar:SetStateProperty(state, "anchorY", y or 0) + bar:SetAnchor(bar:GetAnchor()) + return + end + end + bar:SetAnchor(point, frame, relPoint, x, y) +end + +local function GetStateScale( bar ) + local state = bar:GetState() + if state and bar:GetStateProperty(state, "enableScale") then + return bar:GetStateProperty(state, "scale") + end +end + +local function SetStateScale( bar, scale ) + local state = bar:GetState() + if state and bar:GetStateProperty(state, "enableScale") then + bar:SetStateProperty(state, "scale", scale) + end +end + + +-- +-- Bar config overlay +-- + +local function GetNormalTextColor() + return 1.0, 1.0, 1.0, 1.0 +end + +local function GetAnchoredTextColor() + return 1.0, 1.0, 1.0, 1.0 +end + +local function GetNormalBgColor() + return 0.7, 0.7, 1.0, 0.3 +end + +local function GetAnchoredBgColor() + return 0.9, 0.2, 0.7, 0.3 +end + +local function StoreSize(bar) + local f = bar:GetFrame() + SetSize( bar, f:GetWidth(), f:GetHeight() ) +end + +local function StoreExtents(bar) + local f = bar:GetFrame() + local p, fr, rp, x, y = f:GetPoint(1) + fr = fr and fr:GetName() or "UIParent" + SetAnchor( bar, p, fr, rp, x, y ) + SetSize( bar, f:GetWidth(), f:GetHeight() ) +end + +local function RecomputeButtonSize(bar) + local w, h = GetSize(bar) + local bw, bh = GetButtonSize(bar) + local r, c, s = GetButtonGrid(bar) + + local scaleW = (floor(w/c) - s) / bw + local scaleH = (floor(h/r) - s) / bh + local scale = min(scaleW, scaleH) + + SetButtonSize(bar, scale * bw, scale * bh, s) +end + +local function ComputeBarScale(bar) + local w, h = bar.controlFrame:GetWidth() - 8, bar.controlFrame:GetHeight() - 8 + local bw, bh = GetButtonSize(bar) + local r, c, s = GetButtonGrid(bar) + + local scaleW = w / (c*(bw+s)) + local scaleH = h / (r*(bh+s)) + local scale = min(scaleW, scaleH) + + if scale > 2.5 then + scale = 2.5 + elseif scale < 0.25 then + scale = 0.25 + end + + return scale +end + +local function RecomputeButtonSpacing(bar) + local w, h = GetSize(bar) + local bw, bh = GetButtonSize(bar) + local r, c, s = GetButtonGrid(bar) + + SetButtonGrid(bar,r,c,min(floor(w/c) - bw, floor(h/r) - bh)) +end + +local function RecomputeGrid(bar) + local w, h = GetSize(bar) + local bw, bh = GetButtonSize(bar) + local r, c, s = GetButtonGrid(bar) + + SetButtonGrid(bar, floor(h/(bh+s)), floor(w/(bw+s)), s) +end + +local function ClampToButtons(bar) + local bw, bh = GetButtonSize(bar) + local r, c, s = GetButtonGrid(bar) + SetSize(bar, (bw+s)*c + 1, (bh+s)*r + 1) +end + +local function HideGameTooltip() + GameTooltip:Hide() +end + +local anchorInside = { inside = true } +local anchorOutside = { outside = true } +local edges = { "BOTTOM", "TOP", "LEFT", "RIGHT" } +local oppositeEdges = { + TOP = "BOTTOM", + BOTTOM = "TOP", + LEFT = "RIGHT", + RIGHT = "LEFT" +} +local pointsOnEdge = { + BOTTOM = { "BOTTOM", "BOTTOMLEFT", "BOTTOMRIGHT", }, + TOP = { "TOP", "TOPLEFT", "TOPRIGHT", }, + RIGHT = { "RIGHT", "BOTTOMRIGHT", "TOPRIGHT", }, + LEFT = { "LEFT", "BOTTOMLEFT", "TOPLEFT", }, +} +local edgeSelector = { + BOTTOM = 1, -- select x of x,y + TOP = 1, -- select x of x,y + LEFT = 2, -- select y of x,y + RIGHT = 2, -- select y of x,y +} +local snapPoints = { + [anchorOutside] = { + BOTTOMLEFT = {"BOTTOMRIGHT","TOPLEFT","TOPRIGHT"}, + BOTTOM = {"TOP"}, + BOTTOMRIGHT = {"BOTTOMLEFT","TOPRIGHT","TOPLEFT"}, + RIGHT = {"LEFT"}, + TOPRIGHT = {"TOPLEFT","BOTTOMRIGHT","BOTTOMLEFT"}, + TOP = {"BOTTOM"}, + TOPLEFT = {"TOPRIGHT","BOTTOMLEFT","BOTTOMRIGHT"}, + LEFT = {"RIGHT"}, + CENTER = {"CENTER"} + }, + [anchorInside] = { + BOTTOMLEFT = {"BOTTOMLEFT"}, + BOTTOM = {"BOTTOM"}, + BOTTOMRIGHT = {"BOTTOMRIGHT"}, + RIGHT = {"RIGHT"}, + TOPRIGHT = {"TOPRIGHT"}, + TOP = {"TOP"}, + TOPLEFT = {"TOPLEFT"}, + LEFT = {"LEFT"}, + CENTER = {"CENTER"} + } +} +local insidePointOffsetFuncs = { + BOTTOMLEFT = function(x, y) return x, y end, + BOTTOM = function(x, y) return 0, y end, + BOTTOMRIGHT = function(x, y) return -x, y end, + RIGHT = function(x, y) return -x, 0 end, + TOPRIGHT = function(x, y) return -x, -y end, + TOP = function(x, y) return 0, -y end, + TOPLEFT = function(x, y) return x, -y end, + LEFT = function(x, y) return x, 0 end, + CENTER = function(x, y) return x, y end, +} +local pointCoordFuncs = { + BOTTOMLEFT = function(f) return f:GetLeft(), f:GetBottom() end, + BOTTOM = function(f) return nil, f:GetBottom() end, + BOTTOMRIGHT = function(f) return f:GetRight(), f:GetBottom() end, + RIGHT = function(f) return f:GetRight(), nil end, + TOPRIGHT = function(f) return f:GetRight(), f:GetTop() end, + TOP = function(f) return nil, f:GetTop() end, + TOPLEFT = function(f) return f:GetLeft(), f:GetTop() end, + LEFT = function(f) return f:GetLeft(), nil end, + CENTER = function(f) return f:GetCenter() end, +} +local edgeBoundsFuncs = { + BOTTOM = function(f) return f:GetLeft(), f:GetRight() end, + LEFT = function(f) return f:GetBottom(), f:GetTop() end +} +edgeBoundsFuncs.TOP = edgeBoundsFuncs.BOTTOM +edgeBoundsFuncs.RIGHT = edgeBoundsFuncs.LEFT +local cornerTexCoords = { + -- ULx, ULy, LLx, LLy, URx, URy, LRx, LRy + TOPLEFT = { 1, 1, 1, 0, 0, 1, 0, 0 }, + TOPRIGHT = { 1, 0, 0, 0, 1, 1, 0, 1 }, + BOTTOMLEFT = { 0, 1, 1, 1, 0, 0, 1, 0 }, + BOTTOMRIGHT = { 0, 0, 0, 1, 1, 0, 1, 1 }, +} + +-- Returns absolute coordinates x,y of the named point 'p' of frame 'f' +local function GetPointCoords( f, p ) + local x, y = pointCoordFuncs[p](f) + if not(x and y) then + local cx, cy = f:GetCenter() + x = x or cx + y = y or cy + end + return x, y +end + + +-- Returns true if frame 'f1' can be anchored to frame 'f2' +local function CheckAnchorable( f1, f2 ) + -- can't anchor a frame to itself or to nil + if f1 == f2 or f2 == nil then + return false + end + + -- can always anchor to UIParent + if f2 == UIParent then + return true + end + + -- also can't do circular anchoring of frames + -- walk the anchor chain, which generally shouldn't be that expensive + -- (who nests draggables that deep anyway?) + for i = 1, f2:GetNumPoints() do + local _, f = f2:GetPoint(i) + if not f then f = f2:GetParent() end + return CheckAnchorable(f1,f) + end + + return true +end + +-- Returns true if frames f1 and f2 specified edges overlap +local function CheckEdgeOverlap( f1, f2, e ) + local l1, u1 = edgeBoundsFuncs[e](f1) + local l2, u2 = edgeBoundsFuncs[e](f2) + return l1 <= l2 and l2 <= u1 or l2 <= l1 and l1 <= u2 +end + +-- Returns true if point p1 on frame f1 overlaps edge e2 on frame f2 +local function CheckPointEdgeOverlap( f1, p1, f2, e2 ) + local l, u = edgeBoundsFuncs[e2](f2) + local x, y = GetPointCoords(f1,p1) + x = select(edgeSelector[e2], x, y) + return l <= x and x <= u +end + +-- Returns the distance between corresponding edges. It is +-- assumed that the passed in edges e1 and e2 are the same or opposites +local function GetEdgeDistance( f1, f2, e1, e2 ) + local x1, y1 = pointCoordFuncs[e1](f1) + local x2, y2 = pointCoordFuncs[e2](f2) + return math.abs((x1 or y1) - (x2 or y2)) +end + +local globalSnapTargets = { [UIParent] = anchorInside } + +local function GetClosestFrameEdge(f1,f2,a) + local dist, edge, opp + if f2:IsVisible() and CheckAnchorable(f1,f2) then + for _, e in pairs(edges) do + local o = a.inside and e or oppositeEdges[e] + if CheckEdgeOverlap(f1,f2,e) then + local d = GetEdgeDistance(f1, f2, e, o) + if not dist or (d < dist) then + dist, edge, opp = d, e, o + end + end + end + end + return dist, edge, opp +end + +local function GetClosestVisibleEdge( f ) + local r, o, e1, e2 + local a = anchorOutside + for _, b in ReAction:IterateBars() do + local d, e, opp = GetClosestFrameEdge(f,b:GetFrame(),a) + if d and (not r or d < r) then + r, o, e1, e2 = d, b:GetFrame(), e, opp + end + end + for f2, a2 in pairs(globalSnapTargets) do + local d, e, opp = GetClosestFrameEdge(f,f2,a2) + if d and (not r or d < r) then + r, o, e1, e2, a = d, f2, e, opp, a2 + end + end + return o, e1, e2, a +end + +local function GetClosestVisiblePoint(f1) + local f2, e1, e2, a = GetClosestVisibleEdge(f1) + if f2 then + local rsq, p, rp, x, y + -- iterate pointsOnEdge in order and use < to prefer edge centers to corners + for _, p1 in ipairs(pointsOnEdge[e1]) do + if CheckPointEdgeOverlap(f1,p1,f2,e2) then + for _, p2 in pairs(snapPoints[a][p1]) do + local x1, y1 = GetPointCoords(f1,p1) + local x2, y2 = GetPointCoords(f2,p2) + local dx = x1 - x2 + local dy = y1 - y2 + local rsq2 = dx*dx + dy*dy + if not rsq or rsq2 < rsq then + rsq, p, rp, x, y = rsq2, p1, p2, dx, dy + end + end + end + end + return f2, p, rp, x, y + end +end + +local function GetClosestPointSnapped(f1, rx, ry, xOff, yOff) + local o, p, rp, x, y = GetClosestVisiblePoint(f1) + local s = false + + local insideOffsetFunc = p and insidePointOffsetFuncs[p] + local coordFunc = p and pointCoordFuncs[p] + if not insideOffsetFunc or not coordFunc then + return + end + + local sx, sy = insideOffsetFunc(xOff or 0, yOff or 0) + local xx, yy = coordFunc(f1) + if xx and yy then + if math.abs(x) <= rx then + if math.abs(y) <= ry then + x = sx + y = sy + s = true + elseif CheckEdgeOverlap(f1,o,"LEFT") then + x = sx + s = true + end + elseif math.abs(y) <= ry and CheckEdgeOverlap(f1,o,"TOP") then + y = sy + s = true + end + elseif xx then + if math.abs(x) <= rx then + x = sx + s = true + if math.abs(y) <= ry then + y = sy + end + end + elseif yy then + if math.abs(y) <= ry then + y = sy + s = true + if math.abs(x) <= rx then + x = sx + end + end + end + + -- correct for some Lua oddities with doubles + if x == -0 then x = 0 end + if y == -0 then y = 0 end + + if s then + return o, p, rp, math.floor(x), math.floor(y) + end +end + +local function CreateSnapIndicator() + local si = CreateFrame("Frame",nil,UIParent) + si:SetFrameStrata("HIGH") + si:SetHeight(16) + si:SetWidth(16) + local tex = si:CreateTexture() + tex:SetAllPoints() + tex:SetTexture("Interface\\AddOns\\ReAction\\img\\lock") + tex:SetBlendMode("ADD") + tex:SetDrawLayer("OVERLAY") + return si +end + +local si1 = CreateSnapIndicator() +local si2 = CreateSnapIndicator() + +local function DisplaySnapIndicator( f, rx, ry, xOff, yOff ) + local o, p, rp, x, y, snap = GetClosestPointSnapped(f, rx, ry, xOff, yOff) + if o then + si1:ClearAllPoints() + si2:ClearAllPoints() + si1:SetPoint("CENTER", f, p, 0, 0) + local xx, yy = pointCoordFuncs[rp](o) + x = math.abs(x) <=rx and xx and 0 or x + y = math.abs(y) <=ry and yy and 0 or y + si2:SetPoint("CENTER", o, rp, x, y) + si1:Show() + si2:Show() + else + if si1:IsVisible() then + si1:Hide() + si2:Hide() + end + end + return o, p +end + +local function HideSnapIndicator() + if si1:IsVisible() then + si1:Hide() + si2:Hide() + end +end + +local function UpdateLabelString(bar) + local label = bar.controlLabelString + if label then + local name = bar.labelName + if name and bar.labelSubtext then + name = format("%s (%s)", name, bar.labelSubtext) + end + label:SetText(name or "") + end +end + +local function CreateControls(bar) + local f = bar:GetFrame() + + f:SetMovable(true) + f:SetResizable(true) + + -- child of UIParent so that alpha and scale doesn't propagate to it + local overlay = CreateFrame("Button", nil, UIParent) + overlay:EnableMouse(true) + overlay:SetFrameLevel(3) -- set it above the buttons + overlay:SetPoint("TOPLEFT", f, "TOPLEFT", -4, 4) + overlay:SetPoint("BOTTOMRIGHT", f, "BOTTOMRIGHT", 4, -4) + overlay:SetBackdrop({ + edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border", + tile = true, + tileSize = 16, + edgeSize = 16, + insets = { left = 0, right = 0, top = 0, bottom = 0 }, + }) + + -- textures + local bgTex = overlay:CreateTexture(nil,"BACKGROUND") + bgTex:SetTexture(0.7,0.7,1.0,0.2) + bgTex:SetPoint("TOPLEFT",4,-4) + bgTex:SetPoint("BOTTOMRIGHT",-4,4) + local hTex = overlay:CreateTexture(nil,"HIGHLIGHT") + hTex:SetTexture(0.7,0.7,1.0,0.2) + hTex:SetPoint("TOPLEFT",4,-4) + hTex:SetPoint("BOTTOMRIGHT",-4,4) + hTex:SetBlendMode("ADD") + local aTex = overlay:CreateTexture(nil,"ARTWORK") + aTex:SetTexture("Interface\\AddOns\\ReAction\\img\\lock") + aTex:SetWidth(16) + aTex:SetHeight(16) + aTex:Hide() + + -- label + local label = overlay:CreateFontString(nil,"OVERLAY","GameFontNormalLarge") + label:SetAllPoints() + label:SetJustifyH("CENTER") + label:SetShadowColor(0,0,0,1) + label:SetShadowOffset(3,-3) + label:SetTextColor(GetNormalTextColor()) + label:SetText(bar:GetName()) + label:Show() + bar.controlLabelString = label -- so that bar:SetLabel() can update it + + local function UpdateAnchorDecoration() + local point, anchor, relPoint, x, y = f:GetPoint(1) + if point then + local ofsx, ofsy = insidePointOffsetFuncs[point](x,y) + if (anchor and anchor ~= UIParent) or (ofsx == 0 and ofsy == 0) then + bgTex:SetTexture( GetAnchoredBgColor() ) + hTex:SetTexture( GetAnchoredBgColor() ) + label:SetTextColor( GetAnchoredTextColor() ) + aTex:ClearAllPoints() + aTex:SetPoint(point) + aTex:Show() + return + end + end + bgTex:SetTexture( GetNormalBgColor() ) + hTex:SetTexture( GetNormalBgColor() ) + label:SetTextColor( GetNormalTextColor() ) + aTex:Hide() + end + + local function StopResize() + f:StopMovingOrSizing() + f.isMoving = false + f:SetScript("OnUpdate",nil) + StoreSize(bar) + ClampToButtons(bar) + ReAction:RefreshOptions() + end + + local function CornerUpdate() + local bw, bh = GetButtonSize(bar) + local r, c, s = GetButtonGrid(bar) + local ss = GetStateScale(bar) + if IsShiftKeyDown() then + if ss then + f:SetMinResize( ((s+bw)*c*0.25)/ss, ((s+bh)*r*0.25)/ss ) + f:SetMaxResize( ((s+bw)*c*2.5 + 1)/ss, ((s+bh)*r*2.5 + 1)/ss ) + scale = ComputeBarScale(bar) + else + f:SetMinResize( (s+12)*c+1, (s+12)*r+1 ) + f:SetMaxResize( (s+128)*c+1, (s+128)*r+1 ) + RecomputeButtonSize(bar) + end + elseif not ss and IsAltKeyDown() then + f:SetMinResize( bw*c, bh*r ) + f:SetMaxResize( 2*bw*c, 2*bh*r ) + RecomputeButtonSpacing(bar) + else + f:SetMinResize( bw+s+1, bh+s+1 ) + f:SetMaxResize( 50*(bw+s)+1, 50*(bh+s)+1 ) + RecomputeGrid(bar) + end + GameTooltipTextRight2:SetText(format("%d x %d",r,c)) + + local ss = GetStateScale(bar) + local state = bar:GetState() + if ss then + GameTooltipTextRight4:SetText(format("%d%%", scale*100)) + else + local size = (bw == bh) and tostring(bw) or format("%d x %d",bw,bh) + GameTooltipTextRight3:SetText(size) + GameTooltipTextRight4:SetText(tostring(s)) + end + end + + -- corner drag handles + for _, point in pairs({"BOTTOMLEFT","TOPLEFT","BOTTOMRIGHT","TOPRIGHT"}) do + local corner = CreateFrame("Frame",nil,overlay) + corner:EnableMouse(true) + corner:SetWidth(16) + corner:SetHeight(16) + corner:SetPoint(point) + + local tex = corner:CreateTexture(nil,"HIGHLIGHT") + tex:SetTexture("Interface\\AddOns\\ReAction\\img\\corner") + tex:SetTexCoord(unpack(cornerTexCoords[point])) + tex:SetBlendMode("ADD") + tex:SetAlpha(0.6) + tex:SetAllPoints() + + corner:SetScript("OnMouseDown", + function(_,btn) + f:SetScript("OnUpdate", CornerUpdate) + f:StartSizing(point) + end + ) + corner:SetScript("OnMouseUp", + function() + local ss = GetStateScale(bar) + if ss then + local state = bar:GetState() + SetStateScale(bar, ComputeBarScale(bar)) + end + StopResize() + end) + corner:SetScript("OnEnter", + function() + local bw, bh = GetButtonSize(bar) + local r, c, s = bar:GetButtonGrid() + local size = (bw == bh) and tostring(bw) or format("%d x %d",bw,bh) + local ss = GetStateScale(bar) + local state = bar:GetState() + GameTooltip:SetOwner(f, "ANCHOR_"..point) + if ss then + GameTooltip:AddLine(format("%s (%s: %s)", bar:GetName(), L["State"], state)) + else + GameTooltip:AddLine(bar:GetName()) + end + GameTooltip:AddDoubleLine(format("|cffcccccc%s|r %s",L["Drag"],L["to add/remove buttons:"]), format("%d x %d",r,c)) + if ss then + GameTooltip:AddLine(L["State Scale Override"]) + GameTooltip:AddDoubleLine(format("|cff00ff00%s|r %s",L["Hold Shift"],L["to change scale:"]), format("%d%%", bar:GetStateProperty(state,"scale")*100)) + else + GameTooltip:AddDoubleLine(format("|cff00ff00%s|r %s",L["Hold Shift"],L["to resize buttons:"]), tostring(floor(size))) + GameTooltip:AddDoubleLine(format("|cff0033cc%s|r %s",L["Hold Alt"], L["to change spacing:"]), tostring(floor(s))) + end + GameTooltip:Show() + end + ) + corner:SetScript("OnLeave", + function() + GameTooltip:Hide() + f:SetScript("OnUpdate",nil) + end + ) + end + + overlay:RegisterForDrag("LeftButton") + overlay:RegisterForClicks("RightButtonUp") + + overlay:SetScript("OnDragStart", + function() + f:StartMoving() + f.isMoving = true + local w,h = bar:GetButtonSize() + f:ClearAllPoints() + UpdateAnchorDecoration() + f:SetScript("OnUpdate", function() + if IsShiftKeyDown() then + local f, p = DisplaySnapIndicator(f,w,h) + else + HideSnapIndicator() + end + end) + end + ) + + local function UpdateDragTooltip() + GameTooltip:SetOwner(f, "ANCHOR_TOPRIGHT") + local ss = GetStateScale(bar) + local state = bar:GetState() + if ss then + GameTooltip:AddLine(format("%s (%s: %s)", bar:GetName(), L["State"], state)) + else + GameTooltip:AddLine(bar:GetName()) + end + GameTooltip:AddLine(format("|cffcccccc%s|r %s",L["Drag"],L["to move"])) + GameTooltip:AddLine(format("|cff00ff00%s|r %s",L["Hold Shift"],L["to anchor to nearby frames"])) + GameTooltip:AddLine(format("|cff00cccc%s|r %s",L["Right-click"],L["for options..."])) + local point, frame, relpoint, x, y = bar:GetFrame():GetPoint(1) + if point then + local ofsx, ofsy = insidePointOffsetFuncs[point](x,y) + if (frame and frame ~= UIParent) or (ofsx == 0 and ofsy == 0) then + frame = frame or UIParent + GameTooltip:AddLine(format("%s <%s>",L["Currently anchored to"],frame:GetName())) + end + end + GameTooltip:Show() + end + + overlay:SetScript("OnDragStop", + function() + f:StopMovingOrSizing() + f.isMoving = false + f:SetScript("OnUpdate",nil) + + if IsShiftKeyDown() then + local w, h = bar:GetButtonSize() + local a, p, rp, x, y = GetClosestPointSnapped(f,w,h) + if a then + f:ClearAllPoints() + f:SetPoint(p,a,rp,x,y) + end + HideSnapIndicator() + end + + StoreExtents(bar) + ReAction:RefreshOptions() + UpdateDragTooltip() + UpdateAnchorDecoration() + end + ) + + overlay:SetScript("OnEnter", + function() + UpdateDragTooltip() + end + ) + + overlay:SetScript("OnLeave", HideGameTooltip) + + overlay:SetScript("OnClick", + function() + ReAction:ShowEditor(bar) + end + ) + + function overlay:LIBKEYBOUND_ENABLED(evt) + self:SetFrameLevel(1) + end + + function overlay:LIBKEYBOUND_DISABLED(evt) + self:SetFrameLevel(3) + end + + function overlay:RefreshControls() + UpdateAnchorDecoration() + end + + overlay:SetScript("OnShow", overlay.RefreshControls) + + KB.RegisterCallback(overlay,"LIBKEYBOUND_ENABLED") + KB.RegisterCallback(overlay,"LIBKEYBOUND_DISABLED") + + if ReAction:GetKeybindMode() then + overlay:SetFrameLevel(1) + end + + bar:SetLabel(bar:GetName()) + UpdateLabelString(bar) + UpdateAnchorDecoration() + + return overlay +end + + +-- export methods to the Bar prototype + +function Bar:ShowControls(show) + local f = self.controlFrame + if show then + if not f then + f = CreateControls(self) + self.controlFrame = f + end + f:Show() + elseif f then + f:Hide() + end +end + +function Bar:RefreshControls() + if self.controlFrame and self.controlFrame:IsShown() then + self.controlFrame:RefreshControls() + end +end + +function Bar:SetLabel(name) + self.labelName = name + UpdateLabelString(self) +end + +function Bar:SetLabelSubtext(text) + self.labelSubtext = text + UpdateLabelString(self) +end
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/modules/PetAction.lua Thu Jan 08 00:57:27 2009 +0000 @@ -0,0 +1,637 @@ +--[[ + ReAction Pet Action button module + + The button module implements standard action button functionality by wrapping Blizzard's + PetActionButton frame and associated functions. + +--]] + +-- local imports +local ReAction = ReAction +local L = ReAction.L +local _G = _G +local CreateFrame = CreateFrame +local format = string.format + +ReAction:UpdateRevision("$Revision$") + +-- libraries +local KB = LibStub("LibKeyBound-1.0") + +-- module declaration +local moduleID = "PetAction" +local module = ReAction:NewModule( moduleID ) + +-- Button class declaration +local Button = { } + +-- private +local function UpdateButtonLock(bar) + local f = bar:GetFrame() + f:SetAttribute("lockbuttons",bar.config.lockButtons) + f:SetAttribute("lockbuttonscombat",bar.config.lockButtonsCombat) + f:Execute( + [[ + lockButtons = self:GetAttribute("lockbuttons") + lockButtonsCombat = self:GetAttribute("lockbuttonscombat") + ]]) +end + +-- module methods +function module:OnInitialize() + self.db = ReAction.db:RegisterNamespace( moduleID, + { + profile = { + buttons = { } + } + } + ) + self.buttons = { } + + ReAction:RegisterBarOptionGenerator(self, "GetBarOptions") + + ReAction.RegisterCallback(self, "OnCreateBar") + ReAction.RegisterCallback(self, "OnDestroyBar") + ReAction.RegisterCallback(self, "OnRefreshBar") + ReAction.RegisterCallback(self, "OnEraseBar") + ReAction.RegisterCallback(self, "OnRenameBar") + ReAction.RegisterCallback(self, "OnConfigModeChanged") + + KB.RegisterCallback(self, "LIBKEYBOUND_ENABLED") + KB.RegisterCallback(self, "LIBKEYBOUND_DISABLED") + KB.RegisterCallback(self, "LIBKEYBOUND_MODE_COLOR_CHANGED","LIBKEYBOUND_ENABLED") +end + +function module:OnEnable() + ReAction:RegisterBarType(L["Pet Action Bar"], + { + type = moduleID , + defaultButtonSize = 30, + defaultBarRows = 1, + defaultBarCols = 10, + defaultBarSpacing = 8 + }) +end + +function module:OnDisable() + ReAction:UnregisterBarType(L["Pet Action Bar"]) +end + +function module:OnCreateBar(event, bar, name) + if bar.config.type == moduleID then + -- auto show/hide when pet exists + bar:GetFrame():SetAttribute("unit","pet") + if not ReAction:GetConfigMode() then + RegisterUnitWatch(bar:GetFrame()) + end + self:OnRefreshBar(event, bar, name) + end +end + +function module:OnRefreshBar(event, bar, name) + if bar.config.type == moduleID then + if self.buttons[bar] == nil then + self.buttons[bar] = { } + end + local btns = self.buttons[bar] + local profile = self.db.profile + if profile.buttons[name] == nil then + profile.buttons[name] = {} + end + local btnCfg = profile.buttons[name] + + local r, c = bar:GetButtonGrid() + local n = r*c + for i = 1, n do + if btnCfg[i] == nil then + btnCfg[i] = {} + end + if btns[i] == nil then + local success, r = pcall(Button.New,Button,bar,i,btnCfg[i]) + if success and r then + btns[i] = r + bar:AddButton(i,r) + else + n = i - 1 + bar:ClipNButtons(n) + break + end + end + btns[i]:Refresh() + end + for i = n+1, #btns do + if btns[i] then + bar:RemoveButton(btns[i]) + btns[i] = btns[i]:Destroy() + if btnCfg[i] then + btnCfg[i] = nil + end + end + end + UpdateButtonLock(bar) + end +end + +function module:OnDestroyBar(event, bar, name) + if self.buttons[bar] then + local btns = self.buttons[bar] + for _,b in pairs(btns) do + if b then + b:Destroy() + end + end + self.buttons[bar] = nil + end +end + +function module:OnEraseBar(event, bar, name) + self.db.profile.buttons[name] = nil +end + +function module:OnRenameBar(event, bar, oldname, newname) + local b = self.db.profile.buttons + b[newname], b[oldname] = b[oldname], nil +end + + +function module:OnConfigModeChanged(event, mode) + for _, buttons in pairs(self.buttons) do + for _, b in pairs(buttons) do + b:ShowActionIDLabel(mode) + end + end + for _, bar in ReAction:IterateBars() do + if bar and self.buttons[bar] then + local f = bar:GetFrame() + if mode then + UnregisterUnitWatch(f) + f:Show() + else + RegisterUnitWatch(f) + end + end + end +end + +function module:LIBKEYBOUND_ENABLED(evt) + for _, buttons in pairs(self.buttons) do + for _, b in pairs(buttons) do + b:SetKeybindMode(true) + end + end +end + +function module:LIBKEYBOUND_DISABLED(evt) + for _, buttons in pairs(self.buttons) do + for _, b in pairs(buttons) do + b:SetKeybindMode(false) + end + end +end + + +---- Options ---- +local Handler = { } +local meta = { __index = Handler } + +function Handler:New(bar) + return setmetatable( + { + bar = bar, + config = bar.config + }, meta) +end + +function Handler:GetLockButtons() + return LOCK_ACTIONBAR == "1" or self.config.lockButtons +end + +function Handler:SetLockButtons(info, value) + self.config.lockButtons = value + UpdateButtonLock(self.bar) +end + +function Handler:LockButtonsDisabled() + return LOCK_ACTIONBAR == "1" +end + +function Handler:GetLockButtonsCombat() + return self.config.lockButtonsCombat +end + +function Handler:SetLockButtonsCombat(info, value) + self.config.lockButtonsCombat = value + UpdateButtonLock(self.bar) +end + +function Handler:LockButtonsCombatDisabled() + return LOCK_ACTIONBAR == "1" or not self.config.lockButtons +end + + +function module:GetBarOptions(bar) + if bar.config.type == moduleID then + return { + type = "group", + name = L["Pet Buttons"], + handler = Handler:New(bar), + args = { + lockButtons = { + name = L["Lock Buttons"], + desc = L["Prevents picking up/dragging actions.|nNOTE: This setting is overridden by the global setting in Blizzard's Action Buttons tab"], + order = 2, + type = "toggle", + disabled = "LockButtonsDisabled", + get = "GetLockButtons", + set = "SetLockButtons", + }, + lockOnlyCombat = { + name = L["Only in Combat"], + desc = L["Only lock the buttons when in combat"], + order = 3, + type = "toggle", + disabled = "LockButtonsCombatDisabled", + get = "GetLockButtonsCombat", + set = "SetLockButtonsCombat", + }, + } + } + end +end + + + +------ Button class ------ + +-- use-count of action IDs +local nActionIDs = NUM_PET_ACTION_SLOTS +local ActionIDList = setmetatable( {}, { + __index = function(self, idx) + if idx == nil then + for i = 1, nActionIDs do + if rawget(self,i) == nil then + rawset(self,i,1) + return i + end + end + error("ran out of pet action IDs") + else + local c = rawget(self,idx) or 0 + rawset(self,idx,c+1) + return idx + end + end, + __newindex = function(self,idx,value) + if value == nil then + value = rawget(self,idx) + if value == 1 then + value = nil + elseif value then + value = value - 1 + end + end + rawset(self,idx,value) + end +}) + +local frameRecycler = {} +local trash = CreateFrame("Frame") +local KBAttach, GetActionName, GetHotkey, SetKey, FreeKey, ClearBindings, GetBindings, OnEnter, OnLeave +do + local buttonLookup = setmetatable({},{__mode="kv"}) + + -- Use KeyBound-1.0 for binding, but use Override bindings instead of + -- regular bindings to support multiple profile use. This is a little + -- weird with the KeyBound dialog box (which has per-char selector as well + -- as an OK/Cancel box) but it's the least amount of effort to implement. + function GetActionName(f) + local b = buttonLookup[f] + if b then + return format("%s:%s", b.bar:GetName(), b.idx) + end + end + + function GetHotkey(f) + local b = buttonLookup[f] + if b then + return KB:ToShortKey(b:GetConfig().hotkey) + end + end + + function SetKey(f, key) + local b = buttonLookup[f] + if b then + local c = b:GetConfig() + if c.hotkey then + SetOverrideBinding(f, false, c.hotkey, nil) + end + if key then + SetOverrideBindingClick(f, false, key, f:GetName(), nil) + end + c.hotkey = key + b:DisplayHotkey(GetHotkey(f)) + end + end + + function FreeKey(f, key) + local b = buttonLookup[f] + if b then + local c = b:GetConfig() + if c.hotkey == key then + local action = f:GetActionName() + SetOverrideBinding(f, false, c.hotkey, nil) + c.hotkey = nil + b:DisplayHotkey(nil) + return action + end + end + return ReAction:FreeOverrideHotkey(key) + end + + function ClearBindings(f) + SetKey(f, nil) + end + + function GetBindings(f) + local b = buttonLookup[f] + if b then + return b:GetConfig().hotkey + end + end + + function KBAttach( button ) + local f = button:GetFrame() + f.GetActionName = GetActionName + f.GetHotkey = GetHotkey + f.SetKey = SetKey + f.FreeKey = FreeKey + f.ClearBindings = ClearBindings + f.GetBindings = GetBindings + buttonLookup[f] = button + f:SetKey(button:GetConfig().hotkey) + ReAction:RegisterKeybindFrame(f) + if ReAction:GetKeybindMode() then + button.border:SetVertexColor(KB:GetColorKeyBoundMode()) + button.border:Show() + end + end + + function OnEnter( self ) + if not self.tooltipName then + return; + end + local uber = GetCVar("UberTooltips") + if self.isToken or (uber == "0") then + if uber == "0" then + GameTooltip:SetOwner(self, "ANCHOR_RIGHT") + else + GameTooltip_SetDefaultAnchor(GameTooltip, self) + end + local tooltip = self.tooltipName + local k = GetBindings(self) + if k then + tooltip = tooltip .. format(" %s(%s)%s", NORMAL_FONT_COLOR_CODE, k, FONT_COLOR_CODE_CLOSE) + end + GameTooltip:SetText(tooltip) + if self.tooltipSubtext then + GameTooltip:AddLine(self.tooltipSubtext, "", 0.5, 0.5, 0.5) + end + GameTooltip:Show() + else + GameTooltip_SetDefaultAnchor(GameTooltip, self) + GameTooltip:SetPetAction(self:GetID()) + end + end + + function OnLeave() + GameTooltip:Hide() + end + +end + +local meta = { __index = Button } + +function Button:New( bar, idx, config ) + -- create new self + self = setmetatable( + { + bar = bar, + idx = idx, + config = config, + }, meta ) + + local name = config.name or ("ReAction_%s_%s_%d"):format(bar:GetName(),moduleID,idx) + config.name = name + self.name = name + config.actionID = ActionIDList[config.actionID] -- gets a free one if none configured + + -- have to recycle frames with the same name: + -- otherwise you either get references to old textures because named CreateFrame() + -- doesn't overwrite existing globals. Can't set them to nil in the global table, + -- as it causes taint. + local parent = bar:GetFrame() + local f = frameRecycler[name] + if f then + f:SetParent(parent) + else + f = CreateFrame("CheckButton", name, parent, "PetActionButtonTemplate") + -- ditch the old hotkey text because it's tied in ActionButton_Update() to the + -- standard binding. We use override bindings. + local hotkey = _G[name.."HotKey"] + hotkey:SetParent(trash) + hotkey = f:CreateFontString(nil, "ARTWORK", "NumberFontNormalSmallGray") + hotkey:SetWidth(36) + hotkey:SetHeight(18) + hotkey:SetJustifyH("RIGHT") + hotkey:SetJustifyV("TOP") + hotkey:SetPoint("TOPLEFT",f,"TOPLEFT",-2,-2) + f.hotkey = hotkey + f:HookScript("OnDragStart", function() self:Update() end) + f:HookScript("OnReceiveDrag", function() self:Update() end) + f:SetScript("OnEnter", OnEnter) + f:SetScript("OnLeave", OnLeave) + end + if config.actionID then + f:SetID(config.actionID) -- PetActionButtonTemplate isn't a proper SecureActionButton + end + f:SetFrameStrata("MEDIUM") + self.frame = f + self.icon = _G[("%sIcon"):format(name)] + self.acTex = _G[("%sAutoCastable"):format(name)] + self.acModel = _G[("%sShine"):format(name)] + self.cooldown = _G[("%sCooldown"):format(name)] + self.hotkey = f.hotkey + self.border = _G[("%sBorder"):format(name)] + + + f:RegisterEvent("PLAYER_CONTROL_LOST"); + f:RegisterEvent("PLAYER_CONTROL_GAINED"); + f:RegisterEvent("PLAYER_FARSIGHT_FOCUS_CHANGED"); + f:RegisterEvent("UNIT_PET"); + f:RegisterEvent("UNIT_FLAGS"); + f:RegisterEvent("UNIT_AURA"); + f:RegisterEvent("PET_BAR_UPDATE"); + f:RegisterEvent("PET_BAR_UPDATE_COOLDOWN"); + + f:SetScript("OnEvent", + function(event,arg1) + if event =="PET_BAR_UPDATE_COOLDOWN" then + self:UpdateCooldown() + elseif event == "UPDATE_BINDINGS" then + self:UpdateHotkey() + else + self:Update() + end + end) + + -- install drag wrappers to lock buttons + bar:GetFrame():WrapScript(f, "OnDragStart", + -- OnDragStart(self, button, kind, value, ...) + [[ + if lockButtons and (PlayerInCombat() or not lockButtonsCombat) and not IsModifiedClick("PICKUPACTION") then + return "clear" + end + ]]) + + KBAttach(self) + + -- attach to skinner + bar:SkinButton(self, + { + HotKey = self.hotkey, + } + ) + + self:Refresh() + self:SetKeybindMode(ReAction:GetKeybindMode()) + + return self +end + +function Button:Destroy() + local f = self.frame + f:UnregisterAllEvents() + f:Hide() + f:SetParent(UIParent) + f:ClearAllPoints() + if self.name then + frameRecycler[self.name] = f + _G[self.name] = nil + end + if self.config.actionID then + ActionIDList[self.config.actionID] = nil + end + self.frame = nil + self.config = nil + self.bar = nil +end + +function Button:Refresh() + self.bar:PlaceButton(self, 30, 30) + self:Update() + self:UpdateHotkey() + self.frame:Show() +end + +function Button:GetFrame() + return self.frame +end + +function Button:GetName() + return self.name +end + +function Button:GetConfig() + return self.config +end + +function Button:GetActionID() + return self.config.actionID +end + +function Button:Update() + local id = self.frame:GetID() + local name, subtext, texture, isToken, isActive, autoCastAllowed, autoCastEnabled = GetPetActionInfo(id); + local f = self.frame + --ReAction:Print(("id %d: '%s', '%s', '%s', '%s', '%s', '%s', '%s'"):format(tostring(id), tostring(name),tostring(subtext),tostring(texture),tostring(isToken),tostring(isActive),tostring(autoCastAllowed),tostring(autoCastEnabled))) + + if isToken then + self.icon:SetTexture(_G[texture]); + f.tooltipName = _G[name]; + else + self.icon:SetTexture(texture); + f.tooltipName = name; + end + + f.isToken = isToken; + f.tooltipSubtext = subtext; + f:SetChecked( isActive and 1 or 0); + + if autoCastAllowed then + self.acTex:Show(); + else + self.acTex:Hide(); + end + + if autoCastEnabled then + AutoCastShine_AutoCastStart(self.acModel) + else + AutoCastShine_AutoCastStop(self.acModel) + end + + if texture then + if GetPetActionSlotUsable(id) then + SetDesaturation(self.icon,nil) + else + SetDesaturation(self.icon,1) + end + self.icon:Show(); + f:SetNormalTexture("Interface\\Buttons\\UI-Quickslot2"); + else + self.icon:Hide(); + f:SetNormalTexture("Interface\\Buttons\\UI-Quickslot"); + end + + self:UpdateCooldown() +end + +function Button:UpdateCooldown() + local start, duration, enable = GetPetActionCooldown(self.frame:GetID()); + CooldownFrame_SetTimer(self.cooldown, start, duration, enable); +end + +function Button:UpdateHotkey() + self:DisplayHotkey(GetHotkey(self.frame)) +end + +function Button:ShowActionIDLabel(show) + if show then + -- store the action ID label in the frame due to frame recycling + if not self.actionIDLabel and self:GetActionID() then + local label = self.frame:CreateFontString(nil,"OVERLAY","GameFontNormalLarge") + label:SetAllPoints() + label:SetJustifyH("CENTER") + label:SetShadowColor(0,0,0,1) + label:SetShadowOffset(2,-2) + label:SetText(tostring(self:GetActionID())) + self.actionIDLabel = label + end + self.actionIDLabel:Show() + elseif self.actionIDLabel then + self.actionIDLabel:Hide() + end +end + + +function Button:SetKeybindMode(mode) + if mode then + self.border:SetVertexColor(KB:GetColorKeyBoundMode()) + self.border:Show() + else + self.border:Hide() + end +end + +function Button:DisplayHotkey( key ) + self.hotkey:SetText(key or "") +end
--- a/modules/ReAction_Action/ReAction_Action.lua Wed Jan 07 00:57:02 2009 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1157 +0,0 @@ ---[[ - ReAction Action button module. - - The button module implements standard action button functionality by wrapping Blizzard's - ActionBarButtonTemplate frame and associated functions. - - It also provides action remapping support for multiple pages and possessed targets - (Mind Control, Eyes of the Beast, Karazhan Chess event, various quests, etc). ---]] - --- local imports -local ReAction = ReAction -local L = ReAction.L -local _G = _G -local CreateFrame = CreateFrame -local format = string.format -local wipe = wipe - -ReAction:UpdateRevision("$Revision$") - --- libraries -local KB = LibStub("LibKeyBound-1.0") -local LBF -- initialized later - --- module declaration -local moduleID = "Action" -local module = ReAction:NewModule( moduleID ) - --- Class declarations -local Button = { } -local Handle = { } -local PropHandler = { } - --- Event handlers -function module:OnInitialize() - self.db = ReAction.db:RegisterNamespace( moduleID, - { - profile = { - bars = { }, - } - } - ) - self.handles = setmetatable({ }, weak) - - ReAction:RegisterBarOptionGenerator(self, "GetBarOptions") - - ReAction.RegisterCallback(self, "OnCreateBar") - ReAction.RegisterCallback(self, "OnRefreshBar") - ReAction.RegisterCallback(self, "OnDestroyBar") - ReAction.RegisterCallback(self, "OnEraseBar") - ReAction.RegisterCallback(self, "OnRenameBar") - ReAction.RegisterCallback(self, "OnConfigModeChanged") - - LBF = LibStub("LibButtonFacade",true) - - KB.RegisterCallback(self, "LIBKEYBOUND_ENABLED") - KB.RegisterCallback(self, "LIBKEYBOUND_DISABLED") - KB.RegisterCallback(self, "LIBKEYBOUND_MODE_COLOR_CHANGED","LIBKEYBOUND_ENABLED") -end - -function module:OnEnable() - ReAction:RegisterBarType(L["Action Bar"], - { - type = moduleID, - defaultButtonSize = 36, - defaultBarRows = 1, - defaultBarCols = 12, - defaultBarSpacing = 3 - }, true) - ReAction:GetModule("State"):RegisterStateProperty("page", nil, PropHandler.GetOptions(), PropHandler) -end - -function module:OnDisable() - ReAction:UnregisterBarType(L["Action Bar"]) - ReAction:GetModule("State"):UnregisterStateProperty("page") -end - -function module:OnCreateBar(event, bar, name) - if bar.config.type == moduleID then - local profile = self.db.profile - if profile.bars[name] == nil then - profile.bars[name] = { - buttons = { } - } - end - if self.handles[bar] == nil then - self.handles[bar] = Handle:New(bar, profile.bars[name]) - end - end -end - -function module:OnRefreshBar(event, bar, name) - if self.handles[bar] then - self.handles[bar]:Refresh() - end -end - -function module:OnDestroyBar(event, bar, name) - if self.handles[bar] then - self.handles[bar]:Destroy() - self.handles[bar] = nil - end -end - -function module:OnEraseBar(event, bar, name) - self.db.profile.bars[name] = nil -end - -function module:OnRenameBar(event, bar, oldname, newname) - b = self.db.profile.bars - b[newname], b[oldname] = b[oldname], nil -end - -function module:OnConfigModeChanged(event, mode) - for _, h in pairs(self.handles) do - h:SetConfigMode(mode) - end -end - -function module:LIBKEYBOUND_ENABLED(evt) - for _, h in pairs(self.handles) do - h:ShowGrid(true) - h:SetKeybindMode(true) - end -end - -function module:LIBKEYBOUND_DISABLED(evt) - for _, h in pairs(self.handles) do - h:ShowGrid(false) - h:SetKeybindMode(false) - end -end - - ----- Interface ---- -function module:GetBarOptions(bar) - local h = self.handles[bar] - if h then - return h:GetOptions() - end -end - - ----- Bar Handle ---- - -do - local options = { - hideEmpty = { - name = L["Hide Empty Buttons"], - order = 1, - type = "toggle", - width = "double", - get = "GetHideEmpty", - set = "SetHideEmpty", - }, - lockButtons = { - name = L["Lock Buttons"], - desc = L["Prevents picking up/dragging actions.|nNOTE: This setting is overridden by the global setting in Blizzard's Action Buttons tab"], - order = 2, - type = "toggle", - disabled = "LockButtonsDisabled", - get = "GetLockButtons", - set = "SetLockButtons", - }, - lockOnlyCombat = { - name = L["Only in Combat"], - desc = L["Only lock the buttons when in combat"], - order = 3, - type = "toggle", - disabled = "LockButtonsCombatDisabled", - get = "GetLockButtonsCombat", - set = "SetLockButtonsCombat", - }, - pages = { - name = L["# Pages"], - desc = L["Use the Dynamic State tab to specify page transitions"], - order = 4, - type = "range", - min = 1, - max = 10, - step = 1, - get = "GetNumPages", - set = "SetNumPages", - }, - mindcontrol = { - name = L["Mind Control Support"], - desc = L["When possessing a target (e.g. via Mind Control), map the first 12 buttons of this bar to the possessed target's actions."], - order = 5, - type = "toggle", - width = "double", - set = "SetMindControl", - get = "GetMindControl", - }, - actions = { - name = L["Edit Action IDs"], - order = 6, - type = "group", - inline = true, - args = { - method = { - name = L["Assign"], - order = 1, - type = "select", - width = "double", - values = { [0] = L["Choose Method..."], - [1] = L["Individually"], - [2] = L["All at Once"], }, - get = "GetActionEditMethod", - set = "SetActionEditMethod", - }, - rowSelect = { - name = L["Row"], - desc = L["Rows are numbered top to bottom"], - order = 2, - type = "select", - width = "half", - hidden = "IsButtonSelectHidden", - values = "GetRowList", - get = "GetSelectedRow", - set = "SetSelectedRow", - }, - colSelect = { - name = L["Col"], - desc = L["Columns are numbered left to right"], - order = 3, - type = "select", - width = "half", - hidden = "IsButtonSelectHidden", - values = "GetColumnList", - get = "GetSelectedColumn", - set = "SetSelectedColumn", - }, - pageSelect = { - name = L["Page"], - order = 4, - type = "select", - width = "half", - hidden = "IsPageSelectHidden", - values = "GetPageList", - get = "GetSelectedPage", - set = "SetSelectedPage", - }, - single = { - name = L["Action ID"], - usage = L["Specify ID 1-120"], - order = 5, - type = "input", - width = "half", - hidden = "IsButtonSelectHidden", - get = "GetActionID", - set = "SetActionID", - validate = "ValidateActionID", - }, - multi = { - name = L["ID List"], - usage = L["Specify a comma-separated list of IDs for each button in the bar (in order). Separate multiple pages with semicolons (;)"], - order = 6, - type = "input", - multiline = true, - width = "double", - hidden = "IsMultiIDHidden", - get = "GetMultiID", - set = "SetMultiID", - validate = "ValidateMultiID", - }, - }, - }, - } - - local weak = { __mode="k" } - local meta = { __index = Handle } - - function Handle:New( bar, config ) - local self = setmetatable( - { - bar = bar, - config = config, - btns = { } - }, - meta) - - if self.config.buttons == nil then - self.config.buttons = { } - end - self:Refresh() - self:SetKeybindMode(ReAction:GetKeybindMode()) - return self - end - - function Handle:Refresh() - local r, c = self.bar:GetButtonGrid() - local n = r*c - local btnCfg = self.config.buttons - if n ~= #self.btns then - for i = 1, n do - if btnCfg[i] == nil then - btnCfg[i] = {} - end - if self.btns[i] == nil then - local b = Button:New(self, i, btnCfg[i], self.config) - self.btns[i] = b - self.bar:AddButton(i,b) - end - end - for i = n+1, #self.btns do - if self.btns[i] then - self.bar:RemoveButton(self.btns[i]) - self.btns[i]:Destroy() - self.btns[i] = nil - btnCfg[i] = nil - end - end - end - local f = self.bar:GetFrame() - for _, b in ipairs(self.btns) do - b:Refresh() - end - f:SetAttribute("mindcontrol",self.config.mindcontrol) - f:Execute( - [[ - doMindControl = self:GetAttribute("mindcontrol") - control:ChildUpdate() - ]]) - - f:SetAttribute("_onstate-mindcontrol", - -- function _onstate-mindcontrol(self, stateid, newstate) - [[ - control:ChildUpdate() - ]]) - RegisterStateDriver(f, "mindcontrol", "[bonusbar:5] mc; none") - self:UpdateButtonLock() - end - - function Handle:Destroy() - for _,b in pairs(self.btns) do - if b then - b:Destroy() - end - end - end - - function Handle:SetConfigMode(mode) - for _, b in pairs(self.btns) do - b:ShowGrid(mode) - b:ShowActionIDLabel(mode) - end - end - - function Handle:ShowGrid(show) - for _, b in pairs(self.btns) do - b:ShowGrid(show) - end - end - - function Handle:UpdateButtonLock() - local f = self.bar:GetFrame() - f:SetAttribute("lockbuttons",self.config.lockButtons) - f:SetAttribute("lockbuttonscombat",self.config.lockButtonsCombat) - f:Execute( - [[ - lockButtons = self:GetAttribute("lockbuttons") - lockButtonsCombat = self:GetAttribute("lockbuttonscombat") - ]]) - end - - function Handle:SetKeybindMode(mode) - for _, b in pairs(self.btns) do - if mode then - -- set the border for all buttons to the keybind-enable color - b.border:SetVertexColor(KB:GetColorKeyBoundMode()) - b.border:Show() - elseif IsEquippedAction(b:GetActionID()) then - b.border:SetVertexColor(0, 1.0, 0, 0.35) -- from ActionButton.lua - else - b.border:Hide() - end - end - end - - function Handle:GetLastButton() - return self.btns[#self.btns] - end - - -- options handlers - function Handle:GetOptions() - return { - type = "group", - name = L["Action Buttons"], - handler = self, - args = options - } - end - - function Handle:SetHideEmpty(info, value) - if value ~= self.config.hideEmpty then - self.config.hideEmpty = value - self:ShowGrid(not value) - end - end - - function Handle:GetHideEmpty() - return self.config.hideEmpty - end - - function Handle:GetLockButtons() - return LOCK_ACTIONBAR == "1" or self.config.lockButtons - end - - function Handle:SetLockButtons(info, value) - self.config.lockButtons = value - self:UpdateButtonLock() - end - - function Handle:LockButtonsDisabled() - return LOCK_ACTIONBAR == "1" - end - - function Handle:GetLockButtonsCombat() - return self.config.lockButtonsCombat - end - - function Handle:SetLockButtonsCombat(info, value) - self.config.lockButtonsCombat = value - self:UpdateButtonLock() - end - - function Handle:LockButtonsCombatDisabled() - return LOCK_ACTIONBAR == "1" or not self.config.lockButtons - end - - function Handle:GetNumPages() - return self.config.nPages - end - - function Handle:SetNumPages(info, value) - self.config.nPages = value - self:Refresh() - end - - function Handle:GetMindControl() - return self.config.mindcontrol - end - - function Handle:SetMindControl(info, value) - self.config.mindcontrol = value - self:Refresh() - end - - function Handle:GetActionEditMethod() - return self.editMethod or 0 - end - - function Handle:SetActionEditMethod(info, value) - self.editMethod = value - end - - function Handle:IsButtonSelectHidden() - return self.editMethod ~= 1 - end - - function Handle:GetRowList() - local r,c = self.bar:GetButtonGrid() - if self.rowList == nil or #self.rowList ~= r then - local list = { } - for i = 1, r do - table.insert(list,i) - end - self.rowList = list - end - return self.rowList - end - - function Handle:GetSelectedRow() - local r, c = self.bar:GetButtonGrid() - local row = self.selectedRow or 1 - if row > r then - row = 1 - end - self.selectedRow = row - return row - end - - function Handle:SetSelectedRow(info, value) - self.selectedRow = value - end - - function Handle:GetColumnList() - local r,c = self.bar:GetButtonGrid() - if self.columnList == nil or #self.columnList ~= c then - local list = { } - for i = 1, c do - table.insert(list,i) - end - self.columnList = list - end - return self.columnList - end - - function Handle:GetSelectedColumn() - local r, c = self.bar:GetButtonGrid() - local col = self.selectedColumn or 1 - if col > c then - col = 1 - end - self.selectedColumn = col - return col - end - - function Handle:SetSelectedColumn(info, value) - self.selectedColumn = value - end - - function Handle:IsPageSelectHidden() - return self.editMethod ~= 1 or (self.config.nPages or 1) < 2 - end - - function Handle:GetPageList() - local n = self.config.nPages or 1 - if self.pageList == nil or #self.pageList ~= n then - local p = { } - for i = 1, n do - table.insert(p,i) - end - self.pageList = p - end - return self.pageList - end - - function Handle:GetSelectedPage() - local p = self.selectedPage or 1 - if p > (self.config.nPages or 1) then - p = 1 - end - self.selectedPage = p - return p - end - - function Handle:SetSelectedPage(info, value) - self.selectedPage = value - end - - function Handle:GetActionID() - local row = self.selectedRow or 1 - local col = self.selectedColumn or 1 - local r, c = self.bar:GetButtonGrid() - local n = (row-1) * c + col - local btn = self.btns[n] - if btn then - return tostring(btn:GetActionID(self.selectedPage or 1)) - end - end - - function Handle:SetActionID(info, value) - local row = self.selectedRow or 1 - local col = self.selectedColumn or 1 - local r, c = self.bar:GetButtonGrid() - local n = (row-1) * c + col - local btn = self.btns[n] - if btn then - btn:SetActionID(tonumber(value), self.selectedPage or 1) - end - end - - function Handle:ValidateActionID(info, value) - value = tonumber(value) - if value == nil or value < 1 or value > 120 then - return L["Specify ID 1-120"] - end - return true - end - - function Handle:IsMultiIDHidden() - return self.editMethod ~= 2 - end - - function Handle:GetMultiID() - local p = { } - for i = 1, self.config.nPages or 1 do - local b = { } - for _, btn in ipairs(self.btns) do - table.insert(b, btn:GetActionID(i)) - end - table.insert(p, table.concat(b,",")) - end - return table.concat(p,";\n") - end - - - local function ParseMultiID(nBtns, nPages, s) - if s:match("[^%d%s,;]") then - ReAction:Print("items other than digits, spaces, commas, and semicolons in string",s) - return nil - end - local p = { } - for list in s:gmatch("[^;]+") do - local pattern = ("^%s?$"):format(("%s*(%d+)%s*,"):rep(nBtns)) - local ids = { list:match(pattern) } - if #ids ~= nBtns then - ReAction:Print("found",#ids,"buttons instead of",nBtns) - return nil - end - table.insert(p,ids) - end - if #p ~= nPages then - ReAction:Print("found",#p,"pages instead of",nPages) - return nil - end - return p - end - - function Handle:SetMultiID(info, value) - local p = ParseMultiID(#self.btns, self.config.nPages or 1, value) - for page, b in ipairs(p) do - for button, id in ipairs(b) do - self.btns[button]:SetActionID(id, page) - end - end - end - - function Handle:ValidateMultiID(info, value) - local bad = L["Invalid action ID list string"] - if value == nil or ParseMultiID(#self.btns, self.config.nPages or 1, value) == nil then - return bad - end - return true - end -end - - ------- State property options ------ -do - local pageOptions = { - page = { - name = L["Show Page #"], - order = 11, - type = "select", - width = "half", - disabled = "IsPageDisabled", - hidden = "IsPageHidden", - values = "GetPageValues", - set = "SetProp", - get = "GetPage", - }, - } - - local function GetBarConfig(bar) - return module.db.profile.bars[bar:GetName()] - end - - function PropHandler.GetOptions() - return pageOptions - end - - function PropHandler:IsPageDisabled() - local c = GetBarConfig(self.bar) - local n = c and c.nPages or 1 - return not (n > 1) - end - - function PropHandler:IsPageHidden() - return not GetBarConfig(self.bar) - end - - function PropHandler:GetPageValues() - if not self._pagevalues then - self._pagevalues = { } - end - local c = GetBarConfig(self.bar) - if c then - local n = c.nPages - -- cache the results - if self._npages ~= n then - self._npages = n - wipe(self._pagevalues) - for i = 1, n do - self._pagevalues["page"..i] = i - end - end - end - return self._pagevalues - end - - function PropHandler:GetPage(info) - return self:GetProp(info) or 1 - end - -end - ------- ActionID allocation ------ --- this needs to be high performance when requesting new IDs, --- or certain controls will become sluggish. However, the new-request --- infrastructure can be built lazily the first time that a new request --- comes in (which will only happen at user config time: at static startup --- config time all actionIDs should already have been assigned and stored --- in the config file) - -local IDAlloc -do - local n = 120 - - IDAlloc = setmetatable({ wrap = 1, freecount = n }, {__index = function() return 0 end}) - - function IDAlloc:Acquire(id, hint) - id = tonumber(id) - hint = tonumber(hint) - if id and (id < 1 or id > n) then - id = nil - end - if hint and (hint < 1 or hint > n) then - hint = nil - end - if id == nil then - -- get a free ID - if hint and self[hint] == 0 then - -- use the hint if it's free - id = hint - elseif self.freecount > 0 then - -- if neither the id nor the hint are defined or free, but - -- the list is known to have free IDs, then start searching - -- at the hint for a free one - for i = hint or 1, n do - if self[i] == 0 then - id = i - break - end - end - -- self.wrap the search - if id == nil and hint and hint > 1 then - for i = 1, hint - 1 do - if self[i] == 0 then - id = i - break - end - end - end - end - if id == nil then - -- if there are no free IDs, start wrapping at 1 - id = self.wrap - self.wrap = id + 1 - if self.wrap > n then - self.wrap = 1 - end - end - end - if self[id] == 0 then - self.freecount = self.freecount - 1 - end - self[id] = self[id] + 1 - return id - end - - function IDAlloc:Release(id) - id = tonumber(id) - if id and (id >= 1 or id <= n) then - self[id] = self[id] - 1 - if self[id] == 0 then - self.freecount = self.freecount + 1 - self.wrap = 1 - end - end - end -end - ------- Button class ------ - -do - local frameRecycler = { } - local trash = CreateFrame("Frame") - local OnUpdate, KBAttach, GetActionName, GetHotkey, SetKey, FreeKey, ClearBindings, GetBindings - do - local ATTACK_BUTTON_FLASH_TIME = ATTACK_BUTTON_FLASH_TIME - local IsActionInRange = IsActionInRange - - local buttonLookup = setmetatable({},{__mode="kv"}) - - function OnUpdate(frame, elapsed) - -- note: This function taints frame.flashtime and frame.rangeTimer. Both of these - -- are only read by ActionButton_OnUpdate (which this function replaces). In - -- all other places they're just written, so it doesn't taint any secure code. - if frame.flashing == 1 then - frame.flashtime = frame.flashtime - elapsed - if frame.flashtime <= 0 then - local overtime = -frame.flashtime - if overtime >= ATTACK_BUTTON_FLASH_TIME then - overtime = 0 - end - frame.flashtime = ATTACK_BUTTON_FLASH_TIME - overtime - - local flashTexture = frame.flash - if flashTexture:IsShown() then - flashTexture:Hide() - else - flashTexture:Show() - end - end - end - - if frame.rangeTimer then - frame.rangeTimer = frame.rangeTimer - elapsed; - - if frame.rangeTimer <= 0 then - if IsActionInRange(frame.action) == 0 then - frame.icon:SetVertexColor(1.0,0.1,0.1) - else - ActionButton_UpdateUsable(frame) - end - frame.rangeTimer = 0.1 - end - end - end - - -- Use KeyBound-1.0 for binding, but use Override bindings instead of - -- regular bindings to support multiple profile use. This is a little - -- weird with the KeyBound dialog box (which has per-char selector as well - -- as an OK/Cancel box) but it's the least amount of effort to implement. - function GetActionName(f) - local b = buttonLookup[f] - if b then - return format("%s:%s", b.bar:GetName(), b.idx) - end - end - - function GetHotkey(f) - local b = buttonLookup[f] - if b then - return KB:ToShortKey(b:GetConfig().hotkey) - end - end - - function SetKey(f, key) - local b = buttonLookup[f] - if b then - local c = b:GetConfig() - if c.hotkey then - SetOverrideBinding(f, false, c.hotkey, nil) - end - if key then - SetOverrideBindingClick(f, false, key, f:GetName(), nil) - end - c.hotkey = key - b:DisplayHotkey(GetHotkey(f)) - end - end - - function FreeKey(f, key) - local b = buttonLookup[f] - if b then - local c = b:GetConfig() - if c.hotkey == key then - local action = f:GetActionName() - SetOverrideBinding(f, false, c.hotkey, nil) - c.hotkey = nil - b:DisplayHotkey(nil) - return action - end - end - return ReAction:FreeOverrideHotkey(key) - end - - function ClearBindings(f) - SetKey(f, nil) - end - - function GetBindings(f) - local b = buttonLookup[f] - if b then - return b:GetConfig().hotkey - end - end - - function KBAttach( button ) - local f = button:GetFrame() - f.GetActionName = GetActionName - f.GetHotkey = GetHotkey - f.SetKey = SetKey - f.FreeKey = FreeKey - f.ClearBindings = ClearBindings - f.GetBindings = GetBindings - buttonLookup[f] = button - f:SetKey(button:GetConfig().hotkey) - ReAction:RegisterKeybindFrame(f) - if ReAction:GetKeybindMode() then - button.border:SetVertexColor(KB:GetColorKeyBoundMode()) - button.border:Show() - end - end - end - - local meta = {__index = Button} - - function Button:New( handle, idx, config, barConfig ) - local bar = handle.bar - - -- create new self - self = setmetatable( - { - bar = bar, - idx = idx, - config = config, - barConfig = barConfig, - }, meta ) - - local name = config.name or ("ReAction_%s_%s_%d"):format(bar:GetName(),moduleID,idx) - self.name = name - config.name = name - local lastButton = handle:GetLastButton() - config.actionID = IDAlloc:Acquire(config.actionID, lastButton and lastButton.config.actionID) -- gets a free one if none configured - self.nPages = 1 - - -- have to recycle frames with the same name: CreateFrame() doesn't overwrite - -- existing globals. Can't set to nil in the global because it's then tainted. - local parent = bar:GetFrame() - local f = frameRecycler[name] - if f then - f:SetParent(parent) - else - f = CreateFrame("CheckButton", name, parent, "ActionBarButtonTemplate") - -- ditch the old hotkey text because it's tied in ActionButton_Update() to the - -- standard binding. We use override bindings. - local hotkey = _G[name.."HotKey"] - hotkey:SetParent(trash) - hotkey = f:CreateFontString(nil, "ARTWORK", "NumberFontNormalSmallGray") - hotkey:SetWidth(36) - hotkey:SetHeight(18) - hotkey:SetJustifyH("RIGHT") - hotkey:SetJustifyV("TOP") - hotkey:SetPoint("TOPLEFT",f,"TOPLEFT",-2,-2) - f.hotkey = hotkey - f.icon = _G[name.."Icon"] - f.flash = _G[name.."Flash"] - f:SetScript("OnUpdate",OnUpdate) - end - - self.hotkey = f.hotkey - self.border = _G[name.."Border"] - - f:SetAttribute("action", config.actionID) - -- install mind control actions for all buttons just for simplicity - if self.idx <= 12 then - f:SetAttribute("*action-mc", 120 + self.idx) - end - - -- set a _childupdate handler, called within the header's context - f:SetAttribute("_childupdate", - -- function _childupdate(self, snippetid, message) - [[ - local action = "action" - if doMindControl and GetBonusBarOffset() == 5 then - action = "*action-mc" - elseif page and state and page[state] then - action = "*action-"..page[state] - end - local value = self:GetAttribute(action) - self:SetAttribute("action",value) - ]]) - - -- install drag wrappers to lock buttons - bar:GetFrame():WrapScript(f, "OnDragStart", - -- OnDragStart(self, button, kind, value, ...) - [[ - if lockButtons and (PlayerInCombat() or not lockButtonsCombat) and not IsModifiedClick("PICKUPACTION") then - return "clear" - end - ]]) - - self.frame = f - - - -- initialize the hide state - f:SetAttribute("showgrid",0) - self:ShowGrid(not barConfig.hideEmpty) - if ReAction:GetConfigMode() then - self:ShowGrid(true) - end - - -- show the ID label if applicable - self:ShowActionIDLabel(ReAction:GetConfigMode()) - - -- attach the keybinder - KBAttach(self) - - -- attach to skinner - bar:SkinButton(self, - { - HotKey = self.hotkey, - } - ) - - self:Refresh() - return self - end - - function Button:Destroy() - local f = self.frame - f:UnregisterAllEvents() - f:Hide() - f:SetParent(UIParent) - f:ClearAllPoints() - if self.name then - frameRecycler[self.name] = f - end - if self.config.actionID then - IDAlloc:Release(self.config.actionID) - end - if self.config.pageactions then - for _, id in ipairs(self.config.pageactions) do - IDAlloc:Release(id) - end - end - self.frame = nil - self.config = nil - self.bar = nil - end - - function Button:Refresh() - local f = self.frame - self.bar:PlaceButton(self, 36, 36) - self:RefreshPages() - end - - function Button:GetFrame() - return self.frame - end - - function Button:GetName() - return self.name - end - - function Button:GetConfig() - return self.config - end - - function Button:GetActionID(page) - if page == nil then - -- get the effective ID - return self.frame.action -- kept up-to-date by Blizzard's ActionButton_CalculateAction() - else - if page == 1 then - return self.config.actionID - else - return self.config.pageactions and self.config.pageactions[page] or self.config.actionID - end - end - end - - function Button:SetActionID( id, page ) - id = tonumber(id) - page = tonumber(page) - if id == nil or id < 1 or id > 120 then - error("Button:SetActionID - invalid action ID") - end - if page and page ~= 1 then - if not self.config.pageactions then - self.config.pageactions = { } - end - if self.config.pageactions[page] then - IDAlloc:Release(self.config.pageactions[page]) - end - self.config.pageactions[page] = id - IDAlloc:Acquire(self.config.pageactions[page]) - self.frame:SetAttribute(("*action-page%d"):format(page),id) - else - IDAlloc:Release(self.config.actionID) - self.config.actionID = id - IDAlloc:Acquire(self.config.actionID) - self.frame:SetAttribute("action",id) - if self.config.pageactions then - self.config.pageactions[1] = id - self.frame:SetAttribute("*action-page1",id) - end - end - end - - function Button:RefreshPages( force ) - local nPages = self.barConfig.nPages - if nPages and (nPages ~= self.nPages or force) then - local f = self:GetFrame() - local c = self.config.pageactions - if nPages > 1 and not c then - c = { } - self.config.pageactions = c - end - for i = 1, nPages do - if i > 1 then - c[i] = IDAlloc:Acquire(c[i], self.config.actionID + (i-1)*self.bar:GetNumButtons()) - else - c[i] = self.config.actionID -- page 1 is the same as the base actionID - end - f:SetAttribute(("*action-page%d"):format(i),c[i]) - end - for i = nPages+1, #c do - IDAlloc:Release(c[i]) - c[i] = nil - f:SetAttribute(("*action-page%d"):format(i),nil) - end - self.nPages = nPages - end - end - - function Button:ShowGrid( show ) - if not InCombatLockdown() then - local f = self.frame - local count = f:GetAttribute("showgrid") - if show then - count = count + 1 - else - count = count - 1 - end - if count < 0 then - count = 0 - end - f:SetAttribute("showgrid",count) - - if count >= 1 and not f:GetAttribute("statehidden") then - if LBF then - LBF:SetNormalVertexColor(self.frame, 1.0, 1.0, 1.0, 0.5) - else - self.frame:GetNormalTexture():SetVertexColor(1.0, 1.0, 1.0, 0.5); - end - f:Show() - elseif count < 1 and not HasAction(self:GetActionID()) then - f:Hide() - end - end - end - - function Button:ShowActionIDLabel( show ) - local f = self:GetFrame() - if show then - local id = self:GetActionID() - if not f.actionIDLabel then - local label = f:CreateFontString(nil,"OVERLAY","GameFontNormalLarge") - label:SetAllPoints() - label:SetJustifyH("CENTER") - label:SetShadowColor(0,0,0,1) - label:SetShadowOffset(2,-2) - f.actionIDLabel = label -- store the label with the frame for recycling - - f:HookScript("OnAttributeChanged", - function(frame, attr, value) - if label:IsVisible() and attr:match("action") then - label:SetText(tostring(frame.action)) - end - end) - end - f.actionIDLabel:SetText(tostring(id)) - f.actionIDLabel:Show() - elseif f.actionIDLabel then - f.actionIDLabel:Hide() - end - end - - function Button:DisplayHotkey( key ) - self.hotkey:SetText(key or "") - end -end
--- a/modules/ReAction_Action/ReAction_Action.toc Wed Jan 07 00:57:02 2009 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,11 +0,0 @@ -## Interface: 30000 -## Title: ReAction: Action -## Notes: Action button module for ReAction -## DefaultState: enabled -## LoadOnDemand: 1 -## Author: Flick -## Version: 1.0 -## X-Category: Action Bars -## Dependencies: ReAction - -ReAction_Action.xml
--- a/modules/ReAction_Action/ReAction_Action.xml Wed Jan 07 00:57:02 2009 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,7 +0,0 @@ -<Ui xmlns="http://www.blizzard.com/wow/ui/" - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:schemaLocation="http://www.blizzard.com/wow/ui/..\FrameXML\UI.xsd"> - - <Script file="ReAction_Action.lua"/> - -</Ui> \ No newline at end of file
--- a/modules/ReAction_ButtonFacade/ReAction_ButtonFacade.lua Wed Jan 07 00:57:02 2009 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,115 +0,0 @@ --- local imports -local ReAction = ReAction -local L = ReAction.L -local _G = _G - --- module declaration -local moduleID = "ButtonFacade" -local module = ReAction:NewModule( moduleID ) - --- handlers -function module:OnInitialize() - self.db = ReAction.db:RegisterNamespace( moduleID, - { - profile = { - -- default profile goes here - } - } - ) - - local LBF = LibStub("LibButtonFacade",true) - - if not LBF then -- no more initialization - return - end - - self.LBF = LBF - self.groups = { } - - -- override a method of ReAction.Bar - -- note that 'self' in this context refers to the bar - function ReAction.Bar:SkinButton( button, data ) - module:GetGroup(self:GetName()):AddButton(button:GetFrame(), data) - end - - -- register some common events - ReAction.RegisterCallback(self, "OnCreateBar") - ReAction.RegisterCallback(self, "OnDestroyBar") - ReAction.RegisterCallback(self, "OnRefreshBar") - ReAction.RegisterCallback(self, "OnEraseBar") - ReAction.RegisterCallback(self, "OnRenameBar") - - self.LBF:RegisterSkinCallback("ReAction", self.OnSkinChanged, self) -end - -function module:OnEnable() - -end - -function module:OnDisable() - -end - -function module:OnCreateBar(event, bar, name) - local c = self.db.profile[name] - if not c then - c = { - skinID = "Blizzard", - backdrop = true, - gloss = 0, - colors = {}, - } - self.db.profile[name] = c - end - - local g = self:GetGroup(name) - g.SkinID = c.skinID or "Blizzard" - g.Backdrop = c.backdrop - g.Gloss = c.gloss - g.Colors = c.colors -end - -function module:OnDestroyBar(event, bar, name) - if self.groups[name] then - self.groups[name]:Delete() - self.groups[name] = nil - end -end - -function module:OnRefreshBar(event, bar, name) - local c = self.db.profile[name] - local g = self.groups[name] - if c and g then - g:Skin(c.skinID, c.gloss, c.backdrop, c.colors) - end -end - -function module:OnEraseBar(event, bar, name) - self:OnDestroyBar(event, bar, name) - self.db.profile[name] = nil -end - -function module:OnRenameBar(event, bar, oldName, newName) - if self.groups[name] then - self.groups[name]:Delete(true) - self.db.profile[oldName], self.db.profile[newName] = nil, self.db.profile[oldName] - self:OnCreateBar(event, bar, newName) - end -end - -function module:OnSkinChanged( skinID, gloss, backdrop, group, button, colors ) - local c = self.db.profile[group] - if c then - c.skinID = skinID - c.gloss = gloss - c.backdrop = backdrop - c.colors = colors - end -end - -function module:GetGroup( name ) - if not self.groups[name] then - self.groups[name] = self.LBF:Group("ReAction", name) - end - return self.groups[name] -end
--- a/modules/ReAction_ButtonFacade/ReAction_ButtonFacade.toc Wed Jan 07 00:57:02 2009 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,11 +0,0 @@ -## Interface: 30000 -## Title: ReAction: ButtonFacade -## Notes: ButtonFacade support for ReAction -## DefaultState: enabled -## LoadOnDemand: 1 -## Author: Flick -## Version: 1.0 -## X-Category: Action Bars -## Dependencies: ReAction - -ReAction_ButtonFacade.xml
--- a/modules/ReAction_ButtonFacade/ReAction_ButtonFacade.xml Wed Jan 07 00:57:02 2009 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,7 +0,0 @@ -<Ui xmlns="http://www.blizzard.com/wow/ui/" - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:schemaLocation="http://www.blizzard.com/wow/ui/..\FrameXML\UI.xsd"> - -<Script file="ReAction_ButtonFacade.lua"/> - -</Ui> \ No newline at end of file
--- a/modules/ReAction_HideBlizzard/ReAction_HideBlizzard.lua Wed Jan 07 00:57:02 2009 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,133 +0,0 @@ ---[[ - ReAction 'Hide Blizzard' module - - Hides Blizzard action bars. This hides the extra action bars, stance bar, pet bar, and - main menu bar, which in turn hides the experience bar, bag bar, micro menu bar, and lag meter. - ---]] - --- local imports -local ReAction = ReAction -local L = ReAction.L - -ReAction:UpdateRevision("$Revision$") - --- module declaration -local moduleID = "HideBlizzard" -local module = ReAction:NewModule( moduleID ) - - --- module methods -function module:OnInitialize() - self.db = ReAction.db:RegisterNamespace( moduleID, - { - profile = { - hide = false - } - } - ) - self.db.RegisterCallback(self,"OnProfileChanged") - self.db.RegisterCallback(self,"OnProfileCopied", "OnProfileChanged") - self.db.RegisterCallback(self,"OnProfileReset", "OnProfileChanged") - - self.hiddenFrame = CreateFrame("Frame") - self.hiddenFrame:Hide() - - ReAction:RegisterOptions(self, { - hideBlizzard = { - type = "toggle", - handler = self, - name = L["Hide Blizzard Action Bars"], - desc = L["Hide the default main bar and extra action bars"], - get = "IsHidden", - set = function(info,value) self:SetHidden(value) end, - disabled = InCombatLockdown - }, - }, true) -- global - -end - -function module:OnEnable() - if self.db.profile.hide then - self:HideAll(true) - end - - -- reroute blizzard action bar config to ReAction config window - InterfaceOptionsActionBarsPanel:HookScript("OnShow", - function() - if module:IsEnabled() and module:IsHidden() then - ReAction:ShowConfig() - end - end ) -end - -function module:OnDisable() - self:ShowAll(true) -end - -function module:OnProfileChanged() - if self.db.profile.hide then - module:HideAll(true) - else - module:ShowAll(true) - end -end - -local frames = { - MainMenuBar, - MultiBarLeft, - MultiBarRight, - MultiBarBottomLeft, - MultiBarBottomRight, - -- possess bar frame needs to be pulled out separately: stash its children away - PossessBarLeft, - PossessBarRight, - PossessButton1, - PossessButton2 -} - -local hidden = { } - -function module:HideAll( force ) - if not(self.db.profile.hide) or force then - self.db.profile.hide = true - for _, f in pairs(frames) do - hidden[f] = hidden[f] or { parent = f:GetParent(), wasShown = f:IsShown() } - f:SetParent(self.hiddenFrame) - f:Hide() - end - end - PossessBarFrame:SetParent(UIParent) - PossessBarFrame:EnableMouse(false) -end - -function module:ShowAll( force ) - PossessBarFrame:EnableMouse(true) - PossessBarFrame:SetParent(MainMenuBar) - if self.db.profile.hide or force then - self.db.profile.hide = false - - for _, f in pairs(frames) do - local h = hidden[f] - if h then - f:SetParent(h.parent) - if h.wasShown then - f:Show() - end - end - end - end -end - -function module:IsHidden() - return self.db.profile.hide -end - -function module:SetHidden(h) - if h then - self:HideAll() - else - self:ShowAll() - end -end -
--- a/modules/ReAction_HideBlizzard/ReAction_HideBlizzard.toc Wed Jan 07 00:57:02 2009 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,11 +0,0 @@ -## Interface: 30000 -## Title: ReAction: Hide Blizzard Frames -## Notes: Hides blizzard action bars -## DefaultState: enabled -## LoadOnDemand: 1 -## Author: Flick -## Version: 1.0 -## X-Category: Action Bars -## Dependencies: ReAction - -ReAction_HideBlizzard.xml
--- a/modules/ReAction_HideBlizzard/ReAction_HideBlizzard.xml Wed Jan 07 00:57:02 2009 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,7 +0,0 @@ -<Ui xmlns="http://www.blizzard.com/wow/ui/" - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:schemaLocation="http://www.blizzard.com/wow/ui/..\FrameXML\UI.xsd"> - -<Script file="ReAction_HideBlizzard.lua"/> - -</Ui> \ No newline at end of file
--- a/modules/ReAction_ModuleTemplate/ReAction_ModuleName.lua Wed Jan 07 00:57:02 2009 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,73 +0,0 @@ ---[[ - ReAction module template - ---]] - --- local imports -local ReAction = ReAction -local L = ReAction.L -local _G = _G - --- module declaration -local moduleID = "MyModuleName" -local module = ReAction:NewModule( moduleID, - -- mixins go here -) - --- handlers -function module:OnInitialize() - self.db = ReAction.db:RegisterNamespace( moduleID - { - profile = { - -- default profile goes here - } - } - ) - - -- register some common events - ReAction.RegisterCallback(self, "OnCreateBar") - ReAction.RegisterCallback(self, "OnDestroyBar") - ReAction.RegisterCallback(self, "OnRefreshBar") - ReAction.RegisterCallback(self, "OnEraseBar") - ReAction.RegisterCallback(self, "OnRenameBar") - ReAction.RegisterCallback(self, "OnConfigModeChanged") -end - -function module:OnEnable() - -end - -function module:OnDisable() - -end - --- apply module features and settings to a bar object (see Bar.lua for Bar API) -function module:OnCreateBar(event, bar, name) - -end - --- remove module features and settings from a bar object -function module:OnDestroyBar(event, bar, name) - -end - --- refresh module features and settings on a bar object -function module:OnRefreshBar(event, bar, name) - -end - --- erase any local configuration entries for the supplied bar name -function module:OnEraseBar(event, bar, name) - -end - --- update any local configuration/option entries with the new bar name index -function module:OnRenameBar(event, bar, oldName, newName) - -end - --- update any local display/options based on config mode (true/false) -function module:OnConfigModeChanged(event, mode) - -end -
--- a/modules/ReAction_ModuleTemplate/ReAction_ModuleName.toc Wed Jan 07 00:57:02 2009 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,11 +0,0 @@ -## Interface: 30000 -## Title: ReAction: <ModuleName> -## Notes: <description here> -## DefaultState: enabled -## LoadOnDemand: 1 -## Author: Flick -## Version: 1.0 -## X-Category: Action Bars -## Dependencies: ReAction - -ReAction_<ModuleName>.xml
--- a/modules/ReAction_ModuleTemplate/ReAction_ModuleName.xml Wed Jan 07 00:57:02 2009 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,7 +0,0 @@ -<Ui xmlns="http://www.blizzard.com/wow/ui/" - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:schemaLocation="http://www.blizzard.com/wow/ui/..\FrameXML\UI.xsd"> - -<Script file="ReAction_ModuleName.lua"/> - -</Ui> \ No newline at end of file
--- a/modules/ReAction_PetAction/ReAction_PetAction.lua Wed Jan 07 00:57:02 2009 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,637 +0,0 @@ ---[[ - ReAction Pet Action button module - - The button module implements standard action button functionality by wrapping Blizzard's - PetActionButton frame and associated functions. - ---]] - --- local imports -local ReAction = ReAction -local L = ReAction.L -local _G = _G -local CreateFrame = CreateFrame -local format = string.format - -ReAction:UpdateRevision("$Revision$") - --- libraries -local KB = LibStub("LibKeyBound-1.0") - --- module declaration -local moduleID = "PetAction" -local module = ReAction:NewModule( moduleID ) - --- Button class declaration -local Button = { } - --- private -local function UpdateButtonLock(bar) - local f = bar:GetFrame() - f:SetAttribute("lockbuttons",bar.config.lockButtons) - f:SetAttribute("lockbuttonscombat",bar.config.lockButtonsCombat) - f:Execute( - [[ - lockButtons = self:GetAttribute("lockbuttons") - lockButtonsCombat = self:GetAttribute("lockbuttonscombat") - ]]) -end - --- module methods -function module:OnInitialize() - self.db = ReAction.db:RegisterNamespace( moduleID, - { - profile = { - buttons = { } - } - } - ) - self.buttons = { } - - ReAction:RegisterBarOptionGenerator(self, "GetBarOptions") - - ReAction.RegisterCallback(self, "OnCreateBar") - ReAction.RegisterCallback(self, "OnDestroyBar") - ReAction.RegisterCallback(self, "OnRefreshBar") - ReAction.RegisterCallback(self, "OnEraseBar") - ReAction.RegisterCallback(self, "OnRenameBar") - ReAction.RegisterCallback(self, "OnConfigModeChanged") - - KB.RegisterCallback(self, "LIBKEYBOUND_ENABLED") - KB.RegisterCallback(self, "LIBKEYBOUND_DISABLED") - KB.RegisterCallback(self, "LIBKEYBOUND_MODE_COLOR_CHANGED","LIBKEYBOUND_ENABLED") -end - -function module:OnEnable() - ReAction:RegisterBarType(L["Pet Action Bar"], - { - type = moduleID , - defaultButtonSize = 30, - defaultBarRows = 1, - defaultBarCols = 10, - defaultBarSpacing = 8 - }) -end - -function module:OnDisable() - ReAction:UnregisterBarType(L["Pet Action Bar"]) -end - -function module:OnCreateBar(event, bar, name) - if bar.config.type == moduleID then - -- auto show/hide when pet exists - bar:GetFrame():SetAttribute("unit","pet") - if not ReAction:GetConfigMode() then - RegisterUnitWatch(bar:GetFrame()) - end - self:OnRefreshBar(event, bar, name) - end -end - -function module:OnRefreshBar(event, bar, name) - if bar.config.type == moduleID then - if self.buttons[bar] == nil then - self.buttons[bar] = { } - end - local btns = self.buttons[bar] - local profile = self.db.profile - if profile.buttons[name] == nil then - profile.buttons[name] = {} - end - local btnCfg = profile.buttons[name] - - local r, c = bar:GetButtonGrid() - local n = r*c - for i = 1, n do - if btnCfg[i] == nil then - btnCfg[i] = {} - end - if btns[i] == nil then - local success, r = pcall(Button.New,Button,bar,i,btnCfg[i]) - if success and r then - btns[i] = r - bar:AddButton(i,r) - else - n = i - 1 - bar:ClipNButtons(n) - break - end - end - btns[i]:Refresh() - end - for i = n+1, #btns do - if btns[i] then - bar:RemoveButton(btns[i]) - btns[i] = btns[i]:Destroy() - if btnCfg[i] then - btnCfg[i] = nil - end - end - end - UpdateButtonLock(bar) - end -end - -function module:OnDestroyBar(event, bar, name) - if self.buttons[bar] then - local btns = self.buttons[bar] - for _,b in pairs(btns) do - if b then - b:Destroy() - end - end - self.buttons[bar] = nil - end -end - -function module:OnEraseBar(event, bar, name) - self.db.profile.buttons[name] = nil -end - -function module:OnRenameBar(event, bar, oldname, newname) - local b = self.db.profile.buttons - b[newname], b[oldname] = b[oldname], nil -end - - -function module:OnConfigModeChanged(event, mode) - for _, buttons in pairs(self.buttons) do - for _, b in pairs(buttons) do - b:ShowActionIDLabel(mode) - end - end - for _, bar in ReAction:IterateBars() do - if bar and self.buttons[bar] then - local f = bar:GetFrame() - if mode then - UnregisterUnitWatch(f) - f:Show() - else - RegisterUnitWatch(f) - end - end - end -end - -function module:LIBKEYBOUND_ENABLED(evt) - for _, buttons in pairs(self.buttons) do - for _, b in pairs(buttons) do - b:SetKeybindMode(true) - end - end -end - -function module:LIBKEYBOUND_DISABLED(evt) - for _, buttons in pairs(self.buttons) do - for _, b in pairs(buttons) do - b:SetKeybindMode(false) - end - end -end - - ----- Options ---- -local Handler = { } -local meta = { __index = Handler } - -function Handler:New(bar) - return setmetatable( - { - bar = bar, - config = bar.config - }, meta) -end - -function Handler:GetLockButtons() - return LOCK_ACTIONBAR == "1" or self.config.lockButtons -end - -function Handler:SetLockButtons(info, value) - self.config.lockButtons = value - UpdateButtonLock(self.bar) -end - -function Handler:LockButtonsDisabled() - return LOCK_ACTIONBAR == "1" -end - -function Handler:GetLockButtonsCombat() - return self.config.lockButtonsCombat -end - -function Handler:SetLockButtonsCombat(info, value) - self.config.lockButtonsCombat = value - UpdateButtonLock(self.bar) -end - -function Handler:LockButtonsCombatDisabled() - return LOCK_ACTIONBAR == "1" or not self.config.lockButtons -end - - -function module:GetBarOptions(bar) - if bar.config.type == moduleID then - return { - type = "group", - name = L["Pet Buttons"], - handler = Handler:New(bar), - args = { - lockButtons = { - name = L["Lock Buttons"], - desc = L["Prevents picking up/dragging actions.|nNOTE: This setting is overridden by the global setting in Blizzard's Action Buttons tab"], - order = 2, - type = "toggle", - disabled = "LockButtonsDisabled", - get = "GetLockButtons", - set = "SetLockButtons", - }, - lockOnlyCombat = { - name = L["Only in Combat"], - desc = L["Only lock the buttons when in combat"], - order = 3, - type = "toggle", - disabled = "LockButtonsCombatDisabled", - get = "GetLockButtonsCombat", - set = "SetLockButtonsCombat", - }, - } - } - end -end - - - ------- Button class ------ - --- use-count of action IDs -local nActionIDs = NUM_PET_ACTION_SLOTS -local ActionIDList = setmetatable( {}, { - __index = function(self, idx) - if idx == nil then - for i = 1, nActionIDs do - if rawget(self,i) == nil then - rawset(self,i,1) - return i - end - end - error("ran out of pet action IDs") - else - local c = rawget(self,idx) or 0 - rawset(self,idx,c+1) - return idx - end - end, - __newindex = function(self,idx,value) - if value == nil then - value = rawget(self,idx) - if value == 1 then - value = nil - elseif value then - value = value - 1 - end - end - rawset(self,idx,value) - end -}) - -local frameRecycler = {} -local trash = CreateFrame("Frame") -local KBAttach, GetActionName, GetHotkey, SetKey, FreeKey, ClearBindings, GetBindings, OnEnter, OnLeave -do - local buttonLookup = setmetatable({},{__mode="kv"}) - - -- Use KeyBound-1.0 for binding, but use Override bindings instead of - -- regular bindings to support multiple profile use. This is a little - -- weird with the KeyBound dialog box (which has per-char selector as well - -- as an OK/Cancel box) but it's the least amount of effort to implement. - function GetActionName(f) - local b = buttonLookup[f] - if b then - return format("%s:%s", b.bar:GetName(), b.idx) - end - end - - function GetHotkey(f) - local b = buttonLookup[f] - if b then - return KB:ToShortKey(b:GetConfig().hotkey) - end - end - - function SetKey(f, key) - local b = buttonLookup[f] - if b then - local c = b:GetConfig() - if c.hotkey then - SetOverrideBinding(f, false, c.hotkey, nil) - end - if key then - SetOverrideBindingClick(f, false, key, f:GetName(), nil) - end - c.hotkey = key - b:DisplayHotkey(GetHotkey(f)) - end - end - - function FreeKey(f, key) - local b = buttonLookup[f] - if b then - local c = b:GetConfig() - if c.hotkey == key then - local action = f:GetActionName() - SetOverrideBinding(f, false, c.hotkey, nil) - c.hotkey = nil - b:DisplayHotkey(nil) - return action - end - end - return ReAction:FreeOverrideHotkey(key) - end - - function ClearBindings(f) - SetKey(f, nil) - end - - function GetBindings(f) - local b = buttonLookup[f] - if b then - return b:GetConfig().hotkey - end - end - - function KBAttach( button ) - local f = button:GetFrame() - f.GetActionName = GetActionName - f.GetHotkey = GetHotkey - f.SetKey = SetKey - f.FreeKey = FreeKey - f.ClearBindings = ClearBindings - f.GetBindings = GetBindings - buttonLookup[f] = button - f:SetKey(button:GetConfig().hotkey) - ReAction:RegisterKeybindFrame(f) - if ReAction:GetKeybindMode() then - button.border:SetVertexColor(KB:GetColorKeyBoundMode()) - button.border:Show() - end - end - - function OnEnter( self ) - if not self.tooltipName then - return; - end - local uber = GetCVar("UberTooltips") - if self.isToken or (uber == "0") then - if uber == "0" then - GameTooltip:SetOwner(self, "ANCHOR_RIGHT") - else - GameTooltip_SetDefaultAnchor(GameTooltip, self) - end - local tooltip = self.tooltipName - local k = GetBindings(self) - if k then - tooltip = tooltip .. format(" %s(%s)%s", NORMAL_FONT_COLOR_CODE, k, FONT_COLOR_CODE_CLOSE) - end - GameTooltip:SetText(tooltip) - if self.tooltipSubtext then - GameTooltip:AddLine(self.tooltipSubtext, "", 0.5, 0.5, 0.5) - end - GameTooltip:Show() - else - GameTooltip_SetDefaultAnchor(GameTooltip, self) - GameTooltip:SetPetAction(self:GetID()) - end - end - - function OnLeave() - GameTooltip:Hide() - end - -end - -local meta = { __index = Button } - -function Button:New( bar, idx, config ) - -- create new self - self = setmetatable( - { - bar = bar, - idx = idx, - config = config, - }, meta ) - - local name = config.name or ("ReAction_%s_%s_%d"):format(bar:GetName(),moduleID,idx) - config.name = name - self.name = name - config.actionID = ActionIDList[config.actionID] -- gets a free one if none configured - - -- have to recycle frames with the same name: - -- otherwise you either get references to old textures because named CreateFrame() - -- doesn't overwrite existing globals. Can't set them to nil in the global table, - -- as it causes taint. - local parent = bar:GetFrame() - local f = frameRecycler[name] - if f then - f:SetParent(parent) - else - f = CreateFrame("CheckButton", name, parent, "PetActionButtonTemplate") - -- ditch the old hotkey text because it's tied in ActionButton_Update() to the - -- standard binding. We use override bindings. - local hotkey = _G[name.."HotKey"] - hotkey:SetParent(trash) - hotkey = f:CreateFontString(nil, "ARTWORK", "NumberFontNormalSmallGray") - hotkey:SetWidth(36) - hotkey:SetHeight(18) - hotkey:SetJustifyH("RIGHT") - hotkey:SetJustifyV("TOP") - hotkey:SetPoint("TOPLEFT",f,"TOPLEFT",-2,-2) - f.hotkey = hotkey - f:HookScript("OnDragStart", function() self:Update() end) - f:HookScript("OnReceiveDrag", function() self:Update() end) - f:SetScript("OnEnter", OnEnter) - f:SetScript("OnLeave", OnLeave) - end - if config.actionID then - f:SetID(config.actionID) -- PetActionButtonTemplate isn't a proper SecureActionButton - end - f:SetFrameStrata("MEDIUM") - self.frame = f - self.icon = _G[("%sIcon"):format(name)] - self.acTex = _G[("%sAutoCastable"):format(name)] - self.acModel = _G[("%sShine"):format(name)] - self.cooldown = _G[("%sCooldown"):format(name)] - self.hotkey = f.hotkey - self.border = _G[("%sBorder"):format(name)] - - - f:RegisterEvent("PLAYER_CONTROL_LOST"); - f:RegisterEvent("PLAYER_CONTROL_GAINED"); - f:RegisterEvent("PLAYER_FARSIGHT_FOCUS_CHANGED"); - f:RegisterEvent("UNIT_PET"); - f:RegisterEvent("UNIT_FLAGS"); - f:RegisterEvent("UNIT_AURA"); - f:RegisterEvent("PET_BAR_UPDATE"); - f:RegisterEvent("PET_BAR_UPDATE_COOLDOWN"); - - f:SetScript("OnEvent", - function(event,arg1) - if event =="PET_BAR_UPDATE_COOLDOWN" then - self:UpdateCooldown() - elseif event == "UPDATE_BINDINGS" then - self:UpdateHotkey() - else - self:Update() - end - end) - - -- install drag wrappers to lock buttons - bar:GetFrame():WrapScript(f, "OnDragStart", - -- OnDragStart(self, button, kind, value, ...) - [[ - if lockButtons and (PlayerInCombat() or not lockButtonsCombat) and not IsModifiedClick("PICKUPACTION") then - return "clear" - end - ]]) - - KBAttach(self) - - -- attach to skinner - bar:SkinButton(self, - { - HotKey = self.hotkey, - } - ) - - self:Refresh() - self:SetKeybindMode(ReAction:GetKeybindMode()) - - return self -end - -function Button:Destroy() - local f = self.frame - f:UnregisterAllEvents() - f:Hide() - f:SetParent(UIParent) - f:ClearAllPoints() - if self.name then - frameRecycler[self.name] = f - _G[self.name] = nil - end - if self.config.actionID then - ActionIDList[self.config.actionID] = nil - end - self.frame = nil - self.config = nil - self.bar = nil -end - -function Button:Refresh() - self.bar:PlaceButton(self, 30, 30) - self:Update() - self:UpdateHotkey() - self.frame:Show() -end - -function Button:GetFrame() - return self.frame -end - -function Button:GetName() - return self.name -end - -function Button:GetConfig() - return self.config -end - -function Button:GetActionID() - return self.config.actionID -end - -function Button:Update() - local id = self.frame:GetID() - local name, subtext, texture, isToken, isActive, autoCastAllowed, autoCastEnabled = GetPetActionInfo(id); - local f = self.frame - --ReAction:Print(("id %d: '%s', '%s', '%s', '%s', '%s', '%s', '%s'"):format(tostring(id), tostring(name),tostring(subtext),tostring(texture),tostring(isToken),tostring(isActive),tostring(autoCastAllowed),tostring(autoCastEnabled))) - - if isToken then - self.icon:SetTexture(_G[texture]); - f.tooltipName = _G[name]; - else - self.icon:SetTexture(texture); - f.tooltipName = name; - end - - f.isToken = isToken; - f.tooltipSubtext = subtext; - f:SetChecked( isActive and 1 or 0); - - if autoCastAllowed then - self.acTex:Show(); - else - self.acTex:Hide(); - end - - if autoCastEnabled then - AutoCastShine_AutoCastStart(self.acModel) - else - AutoCastShine_AutoCastStop(self.acModel) - end - - if texture then - if GetPetActionSlotUsable(id) then - SetDesaturation(self.icon,nil) - else - SetDesaturation(self.icon,1) - end - self.icon:Show(); - f:SetNormalTexture("Interface\\Buttons\\UI-Quickslot2"); - else - self.icon:Hide(); - f:SetNormalTexture("Interface\\Buttons\\UI-Quickslot"); - end - - self:UpdateCooldown() -end - -function Button:UpdateCooldown() - local start, duration, enable = GetPetActionCooldown(self.frame:GetID()); - CooldownFrame_SetTimer(self.cooldown, start, duration, enable); -end - -function Button:UpdateHotkey() - self:DisplayHotkey(GetHotkey(self.frame)) -end - -function Button:ShowActionIDLabel(show) - if show then - -- store the action ID label in the frame due to frame recycling - if not self.actionIDLabel and self:GetActionID() then - local label = self.frame:CreateFontString(nil,"OVERLAY","GameFontNormalLarge") - label:SetAllPoints() - label:SetJustifyH("CENTER") - label:SetShadowColor(0,0,0,1) - label:SetShadowOffset(2,-2) - label:SetText(tostring(self:GetActionID())) - self.actionIDLabel = label - end - self.actionIDLabel:Show() - elseif self.actionIDLabel then - self.actionIDLabel:Hide() - end -end - - -function Button:SetKeybindMode(mode) - if mode then - self.border:SetVertexColor(KB:GetColorKeyBoundMode()) - self.border:Show() - else - self.border:Hide() - end -end - -function Button:DisplayHotkey( key ) - self.hotkey:SetText(key or "") -end
--- a/modules/ReAction_PetAction/ReAction_PetAction.toc Wed Jan 07 00:57:02 2009 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,11 +0,0 @@ -## Interface: 30000 -## Title: ReAction: Pet Actions -## Notes: Pet Action Bar support for ReAction -## DefaultState: enabled -## LoadOnDemand: 1 -## Author: Flick -## Version: 1.0 -## X-Category: Action Bars -## Dependencies: ReAction - -ReAction_PetAction.xml
--- a/modules/ReAction_PetAction/ReAction_PetAction.xml Wed Jan 07 00:57:02 2009 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,7 +0,0 @@ -<Ui xmlns="http://www.blizzard.com/wow/ui/" - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:schemaLocation="http://www.blizzard.com/wow/ui/..\FrameXML\UI.xsd"> - -<Script file="ReAction_PetAction.lua"/> - -</Ui> \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/modules/State.lua Thu Jan 08 00:57:27 2009 +0000 @@ -0,0 +1,1338 @@ +--[[ + ReAction bar state driver interface + +--]] + +-- local imports +local ReAction = ReAction +local L = ReAction.L +local _G = _G +local format = string.format +local InCombatLockdown = InCombatLockdown +local RegisterStateDriver = RegisterStateDriver + +ReAction:UpdateRevision("$Revision$") + +-- module declaration +local moduleID = "State" +local module = ReAction:NewModule( moduleID, "AceEvent-3.0" ) + +-- Utility -- + +-- traverse a table tree by key list and fetch the result or first nil +local function tfetch(t, ...) + for i = 1, select('#', ...) do + t = t and t[select(i, ...)] + end + return t +end + +-- traverse a table tree by key list and build tree as necessary +local function tbuild(t, ...) + for i = 1, select('#', ...) do + local key = select(i, ...) + if not t[key] then t[key] = { } end + t = t[key] + end + return t +end + +-- return a new array of keys of table 't', sorted by comparing +-- sub-fields (obtained via tfetch) of the table values +local function fieldsort( t, ... ) + local r = { } + for k in pairs(t) do + table.insert(r,k) + end + local path = { ... } + table.sort(r, function(lhs, rhs) + local olhs = tfetch(t[lhs], unpack(path)) or 0 + local orhs = tfetch(t[rhs], unpack(path)) or 0 + return olhs < orhs + end) + return r +end + +-- set a frame-ref, if the frame is valid, or set nil to the +-- corresponding attribute +local function SetFrameRef(frame, name, refFrame) + if refFrame then + local _, explicit = refFrame:IsProtected() + if not explicit then + refFrame = nil + end + end + if refFrame then + frame:SetFrameRef(name,refFrame) + else + frame:SetAttribute("frameref-"..name,nil) + end +end + + +local InitRules, ApplyStates, CleanupStates, SetProperty, GetProperty, RegisterProperty, ShowAll + +-- PRIVATE -- +do + + -- the field names must match the field names of the options table, below + -- the field values are secure snippets or 'true' to skip the snippet for that property. + local properties = { + hide = + [[ + local h = hide and hide[state] and not showAll + if h ~= hidden then + if h then + self:Hide() + else + self:Show() + end + hidden = h + end + if showAll then + control:CallMethod("UpdateHiddenLabel", hide and hide[state]) + end + ]], + + --keybindState TODO: broken + + anchorEnable = + [[ + local old_anchor = anchorstate + anchorstate = (anchorEnable and anchorEnable[state]) and state + if old_anchor ~= anchorstate or not set_state then + if anchorstate and anchorPoint then + if anchorPoint[state] then + self:ClearAllPoints() + local f = self:GetAttribute("frameref-anchor-"..anchorstate) + if f then + self:SetPoint(anchorPoint[state], f, anchorRelPoint[state], anchorX[state], anchorY[state]) + end + end + elseif defaultAnchor and defaultAnchor.point then + self:ClearAllPoints() + self:SetPoint(defaultAnchor.point, defaultAnchor.frame, + defaultAnchor.relPoint, defaultAnchor.x, defaultAnchor.y) + end + end + ]], + -- anchorEnable handles all the other bits + anchorFrame = true, + anchorPoint = true, + anchorRelPoint = true, + anchorX = true, + anchorY = true, + + + enableScale = + [[ + local old_scale = scalestate + scalestate = (enableScale and enableScale[state]) and state + if old_scale ~= scalestate or not set_state then + if scalestate and scale then + if scale[state] then + self:SetScale(scale[state]) + end + else + self:SetScale(1.0) + end + end + ]], + -- enableScale handles scale + scale = true, + + enableAlpha = + [[ + local old_alpha = alphastate + alphastate = (enableAlpha and enableAlpha[state]) and state + if old_alpha ~= alphastate or not set_state then + control:CallMethod("UpdateAlpha", alphastate and alpha[state] or defaultAlpha) + end + ]], + -- enableAlpha handles alpha + alpha = true, + } + + local weak = { __mode = "k" } + local statedrivers = setmetatable( { }, weak ) + local keybinds = setmetatable( { }, weak ) + + -- + -- Secure Handler Snippets + -- + local SetHandlerData, SetStateDriver, SetStateKeybind, RefreshState + do + local stateHandler_propInit = + [[ + propfuncs = table.new() + local proplist = self:GetAttribute("prop-func-list") + for s in string.gmatch(proplist, "(%w+)") do + table.insert(propfuncs, s) + end + ]] + + local onStateHandler = + -- function _onstate-reaction( self, stateid, newstate ) + [[ + set_state = newstate + + local oldState = state + state = state_override or set_state or state + for i = 1, #propfuncs do + control:RunAttribute("func-"..propfuncs[i]) + end + + control:ChildUpdate() + + if oldState ~= state then + control:CallMethod("StateRefresh", state) + end + ]] + + local onClickHandler = + -- function OnClick( self, button, down ) + [[ + if state_override == button then + state_override = nil -- toggle + else + state_override = button + end + ]] .. onStateHandler + + local function UpdateAlpha( frame, alpha ) + if alpha then + frame:SetAlpha(alpha) + end + end + + -- Construct a lua assignment as a code string and execute it within the header + -- frame's sandbox. 'value' must be a string, boolean, number, or nil. If called + -- with four arguments, then it treats 'varname' as an existing global table and + -- sets a key-value pair. For a slight efficiency boost, pass the values in as + -- attributes and fetch them as attributes from the snippet code, to leverage snippet + -- caching. + function SetHandlerData( bar, varname, value, key ) + local f = bar:GetFrame() + f:SetAttribute("data-varname",varname) + f:SetAttribute("data-value", value) + f:SetAttribute("data-key", key) + f:Execute( + [[ + local name = self:GetAttribute("data-varname") + local value = self:GetAttribute("data-value") + local key = self:GetAttribute("data-key") + if name then + if key then + if not _G[name] then + _G[name] = table.new() + end + _G[name][key] = value + else + _G[name] = value + end + end + ]]) + end + + function SetDefaultAnchor( bar ) + local point, frame, relPoint, x, y = bar:GetAnchor() + SetHandlerData(bar, "defaultAnchor", point, "point") + SetHandlerData(bar, "defaultAnchor", relPoint, "relPoint") + SetHandlerData(bar, "defaultAnchor", x, "x") + SetHandlerData(bar, "defaultAnchor", y, "y") + SetHandlerData(bar, "defaultAlpha", bar:GetAlpha()) + + local f = bar:GetFrame() + f.UpdateAlpha = UpdateAlpha + SetFrameRef(f, "defaultAnchor", _G[frame or "UIParent"]) + f:Execute( + [[ + defaultAnchor.frame = self:GetAttribute("frameref-defaultAnchor") + ]]) + end + + function RefreshState( bar ) + SetDefaultAnchor(bar) + bar:GetFrame():Execute( + [[ + if self:GetAttribute("reaction-refresh") then + control:RunAttribute("reaction-refresh") + end + ]]) + end + + function SetStateDriver( bar, rule ) + local f = bar:GetFrame() + + if not f.UpdateHiddenLabel then + function f:UpdateHiddenLabel(hide) + bar:SetLabelSubtext( hide and L["Hidden"] ) + end + end + + function f:StateRefresh( state ) + bar:RefreshControls() + end + + local props = { } + for p, h in pairs(properties) do + if type(h) == "string" then + table.insert(props,p) + f:SetAttribute("func-"..p, h) + end + end + f:SetAttribute("prop-func-list", table.concat(props," ")) + f:Execute(stateHandler_propInit) + f:SetAttribute("reaction-refresh", onStateHandler) + + if rule and #rule > 0 then + f:SetAttribute( "_onstate-reaction", onStateHandler ) + RegisterStateDriver(f, "reaction", rule) + statedrivers[bar] = rule + elseif statedrivers[bar] then + UnregisterStateDriver(f, "reaction") + f:SetAttribute( "_onstate-reaction", nil ) + statedrivers[bar] = nil + end + end + + function SetStateKeybind( bar, key, state ) + local f = bar:GetFrame() + + local kb = keybinds[bar] + if kb == nil then + if key == nil then + -- nothing to do + return + end + kb = { } + keybinds[bar] = kb + end + + -- clear the old binding, if any + if kb[state] then + SetOverrideBinding(f, false, kb[state], nil) + end + kb[state] = key + + if key then + f:SetAttribute("_onclick", onClickHandler) + SetOverrideBindingClick(f, false, key, state, nil) -- state name is the virtual mouse button + end + end + end + + -- As far as I can tell the macro clauses are NOT locale-specific. + local ruleformats = { + stealth = "stealth", + nostealth = "nostealth", + shadowform = "form:1", + noshadowform = "noform", + pet = "pet", + nopet = "nopet", + harm = "target=target,harm", + help = "target=target,help", + notarget = "target=target,noexists", + focusharm = "target=focus,harm", + focushelp = "target=focus,help", + nofocus = "target=focus,noexists", + raid = "group:raid", + party = "group:party", + solo = "nogroup", + combat = "combat", + nocombat = "nocombat", + possess = "bonusbar:5", + } + + -- Have to do these shenanigans instead of hardcoding the stances/forms because the + -- ordering varies if the character is missing a form. For warriors this is rarely + -- a problem (c'mon, who actually skips the level 10 def stance quest?) but for druids + -- it can be. Some people never bother to do the aquatic form quest until well past + -- when they get cat form, and stance 5/6 can be flight, tree, or moonkin depending + -- on talents. + function InitRules() + local forms = { } + -- sort by icon since it's locale-independent + for i = 1, GetNumShapeshiftForms() do + local icon, name, active = GetShapeshiftFormInfo(i) + -- if it's the current form, the icon is wrong (Ability_Spell_WispSplode) + -- so capture it from the spell info directly + if active then + local _1, _2 + _1, _2, icon = GetSpellInfo(name) + end + forms[icon] = i; + end + -- use 9 if not found since 9 is never a valid stance/form + local defensive = forms["Interface\\Icons\\Ability_Warrior_DefensiveStance"] or 9 + local berserker = forms["Interface\\Icons\\Ability_Racial_Avatar"] or 9 + local bear = forms["Interface\\Icons\\Ability_Racial_BearForm"] or 9 -- bear and dire bear share the same icon + local aquatic = forms["Interface\\Icons\\Ability_Druid_AquaticForm"] or 9 + local cat = forms["Interface\\Icons\\Ability_Druid_CatForm"] or 9 + local travel = forms["Interface\\Icons\\Ability_Druid_TravelForm"] or 9 + local tree = forms["Interface\\Icons\\Ability_Druid_TreeofLife"] or 9 + local moonkin = forms["Interface\\Icons\\Spell_Nature_ForceOfNature"] or 9 + local flight = forms["Interface\\Icons\\Ability_Druid_FlightForm"] or 9 -- flight and swift flight share the same icon + + ruleformats.battle = "stance:1" + ruleformats.defensive = format("stance:%d",defensive) + ruleformats.berserker = format("stance:%d",berserker) + ruleformats.caster = format("form:0/%d/%d/%d",aquatic, travel, flight) + ruleformats.bear = format("form:%d",bear) + ruleformats.cat = format("form:%d",cat) + ruleformats.tree = format("form:%d",tree) + ruleformats.moonkin = format("form:%d",moonkin) + end + + local function BuildRule(states) + local rules = { } + local default + + for idx, state in ipairs(fieldsort(states, "rule", "order")) do + local c = states[state].rule + local type = c.type + if type == "default" then + default = default or state + elseif type == "custom" then + if c.custom then + -- strip out all spaces from the custom rule + table.insert(rules, format("%s %s", c.custom:gsub("%s",""), state)) + end + elseif type == "any" or type == "all" then + if c.values then + local clauses = { } + for key, value in pairs(c.values) do + table.insert(clauses, ruleformats[key]) + end + if #clauses > 0 then + local sep = (type == "any") and "][" or "," + table.insert(rules, format("[%s] %s", table.concat(clauses,sep), state)) + end + end + end + end + -- make sure that the default, if any, is last + if default then + table.insert(rules, default) + end + return table.concat(rules,";") + end + + local function BuildKeybinds( bar, states ) + for name, state in pairs(states) do + local type = tfetch(state, "rule", "type") + if type == "keybind" then + local key = tfetch(state, "rule", "keybind") + SetStateKeybind(bar, key, name) + else + SetStateKeybind(bar, nil, name) -- this clears an existing keybind + end + end + end + + function GetProperty( bar, state, propname ) + return tfetch(module.db.profile.bars, bar:GetName(), "states", state, propname) + end + + function SetProperty( bar, state, propname, value ) + local s = tbuild(module.db.profile.bars, bar:GetName(), "states", state) + s[propname] = value + SetHandlerData(bar, propname, value, state) + RefreshState(bar) + end + + function RegisterProperty( propname, snippet ) + properties[propname] = snippet or true + for _, bar in ReAction:IterateBars() do + local states = tfetch(module.db.profile.bars, bar:GetName(), "states") + if states then + for name, s in pairs(states) do + SetHandlerData(bar, propname, s[propname], name) + end + SetStateDriver(bar, BuildRule(states)) + RefreshState(bar) + end + end + end + + function UnregisterProperty( propname ) + properties[propname] = nil + for _, bar in ReAction:IterateBars() do + SetHandlerData(bar, propname, nil) + SetStateDriver(bar, BuildRule(states)) + RefreshState(bar) + end + end + + function ApplyStates( bar ) + local states = tfetch(module.db.profile.bars, bar:GetName(), "states") + if states then + for propname in pairs(properties) do + for name, s in pairs(states) do + if propname == "anchorFrame" then + SetFrameRef(bar:GetFrame(), "anchor-"..name, _G[s.anchorFrame]) + else + SetHandlerData(bar, propname, s[propname], name) + end + end + end + BuildKeybinds(bar, states) + SetHandlerData(bar, "showAll", ReAction:GetConfigMode()) + SetStateDriver(bar, BuildRule(states)) + RefreshState(bar) + end + end + + function CleanupStates( bar ) + SetStateDriver(bar, nil) + end + + function ShowAll( bar, show ) + if statedrivers[bar] then + SetHandlerData(bar, "showAll", show) + RefreshState(bar) + end + end +end + + + +-- module event handlers -- + +function module:OnInitialize() + self.db = ReAction.db:RegisterNamespace( moduleID, + { + profile = { + bars = { }, + } + } + ) + + self:RegisterEvent("UPDATE_SHAPESHIFT_FORMS") + + ReAction:RegisterBarOptionGenerator(self, "GetBarOptions") + + ReAction.RegisterCallback(self, "OnCreateBar","OnRefreshBar") + ReAction.RegisterCallback(self, "OnDestroyBar") + ReAction.RegisterCallback(self, "OnRefreshBar") + ReAction.RegisterCallback(self, "OnEraseBar") + ReAction.RegisterCallback(self, "OnRenameBar") + ReAction.RegisterCallback(self, "OnConfigModeChanged") +end + +function module:OnEnable() + self:UPDATE_SHAPESHIFT_FORMS() -- it doesn't fire on a /reloadui +end + +function module:UPDATE_SHAPESHIFT_FORMS() + -- Re-parse the rules table according to the new form list. + -- This happens both at initial login (after PLAYER_ENTERING_WORLD) + -- as well as when gaining new abilities. + InitRules() + for name, bar in ReAction:IterateBars() do + self:OnRefreshBar(nil,bar,name) + end +end + +function module:OnRefreshBar(event, bar, name) + local c = self.db.profile.bars[name] + if c then + ApplyStates(bar) + end +end + +function module:OnDestroyBar(event, bar, name) + CleanupStates(bar) +end + +function module:OnEraseBar(event, bar, name) + self.db.profile.bars[name] = nil +end + +function module:OnRenameBar(event, bar, oldname, newname) + local bars = self.db.profile.bars + bars[newname], bars[oldname] = bars[oldname], nil +end + +function module:OnConfigModeChanged(event, mode) + for name, bar in ReAction:IterateBars() do + if self.db.profile.bars[name] then + ShowAll(bar, mode) + end + end +end + + + +-- Options -- + +local CreateBarOptions, RegisterPropertyOptions +do + local playerClass = select(2, UnitClass("player")) + local function ClassCheck(...) + for i = 1, select('#',...) do + if playerClass == select(i,...) then + return false + end + end + return true + end + + -- pre-sorted by the order they should appear in + local rules = { + -- rule hidden fields + { "stance", ClassCheck("WARRIOR"), { {battle = L["Battle Stance"]}, {defensive = L["Defensive Stance"]}, {berserker = L["Berserker Stance"]} } }, + { "form", ClassCheck("DRUID"), { {caster = L["Caster Form"]}, {bear = L["Bear Form"]}, {cat = L["Cat Form"]}, {tree = L["Tree of Life"]}, {moonkin = L["Moonkin Form"]} } }, + { "stealth", ClassCheck("ROGUE","DRUID"), { {stealth = L["Stealth"]}, {nostealth = L["No Stealth"]} } }, + { "shadow", ClassCheck("PRIEST"), { {shadowform = L["Shadowform"]}, {noshadowform = L["No Shadowform"]} } }, + { "pet", ClassCheck("HUNTER","WARLOCK"), { {pet = L["With Pet"]}, {nopet = L["Without Pet"]} } }, + { "target", false, { {harm = L["Hostile Target"]}, {help = L["Friendly Target"]}, {notarget = L["No Target"]} } }, + { "focus", false, { {focusharm = L["Hostile Focus"]}, {focushelp = L["Friendly Focus"]}, {nofocus = L["No Focus"]} } }, + { "possess", false, { {possess = L["Mind Control"]} } }, + { "group", false, { {raid = L["Raid"]}, {party = L["Party"]}, {solo = L["Solo"]} } }, + { "combat", false, { {combat = L["In Combat"]}, {nocombat = L["Out of Combat"]} } }, + } + + local ruleSelect = { } + local ruleMap = { } + local optionMap = setmetatable({},{__mode="k"}) + + local pointTable = { + NONE = " ", + CENTER = L["Center"], + LEFT = L["Left"], + RIGHT = L["Right"], + TOP = L["Top"], + BOTTOM = L["Bottom"], + TOPLEFT = L["Top Left"], + TOPRIGHT = L["Top Right"], + BOTTOMLEFT = L["Bottom Left"], + BOTTOMRIGHT = L["Bottom Right"], + } + + -- unpack rules table into ruleSelect and ruleMap + for _, c in ipairs(rules) do + local rule, hidden, fields = unpack(c) + if not hidden then + for _, field in ipairs(fields) do + local key, label = next(field) + table.insert(ruleSelect, label) + table.insert(ruleMap, key) + end + end + end + + local stateOptions = { + ordering = { + name = L["Info"], + order = 1, + type = "group", + args = { + delete = { + name = L["Delete this State"], + order = -1, + type = "execute", + func = "DeleteState", + }, + rename = { + name = L["Name"], + order = 1, + type = "input", + get = "GetName", + set = "SetStateName", + pattern = "^%w*$", + usage = L["State names must be alphanumeric without spaces"], + }, + ordering = { + name = L["Evaluation Order"], + desc = L["State transitions are evaluated in the order listed:\nMove a state up or down to change the order"], + order = 2, + type = "group", + inline = true, + args = { + up = { + name = L["Up"], + order = 1, + type = "execute", + width = "half", + func = "MoveStateUp", + }, + down = { + name = L["Down"], + order = 2, + type = "execute", + width = "half", + func = "MoveStateDown", + } + } + } + } + }, + properties = { + name = L["Properties"], + order = 2, + type = "group", + args = { + desc = { + name = L["Set the properties for the bar when in this state"], + order = 1, + type = "description" + }, + hide = { + name = L["Hide Bar"], + order = 90, + type = "toggle", + set = "SetProp", + get = "GetProp", + }, + --[[ BROKEN + keybindState = { + name = L["Override Keybinds"], + desc = L["Set this state to maintain its own set of keybinds which override the defaults when active"], + order = 91, + type = "toggle", + set = "SetProp", + get = "GetProp", + }, ]] + position = { + name = L["Position"], + order = 92, + type = "group", + inline = true, + args = { + anchorEnable = { + name = L["Reposition"], + order = 1, + type = "toggle", + set = "SetProp", + get = "GetProp", + }, + anchorFrame = { + name = L["Anchor Frame"], + order = 2, + type = "select", + values = "GetAnchorFrames", + set = "SetAnchorFrame", + get = "GetAnchorFrame", + disabled = "GetAnchorDisabled", + hidden = "GetAnchorDisabled", + }, + anchorPoint = { + name = L["Point"], + order = 3, + type = "select", + values = pointTable, + set = "SetAnchorPointProp", + get = "GetAnchorPointProp", + disabled = "GetAnchorDisabled", + hidden = "GetAnchorDisabled", + }, + anchorRelPoint = { + name = L["Relative Point"], + order = 4, + type = "select", + values = pointTable, + set = "SetAnchorPointProp", + get = "GetAnchorPointProp", + disabled = "GetAnchorDisabled", + hidden = "GetAnchorDisabled", + }, + anchorX = { + name = L["X Offset"], + order = 5, + type = "range", + min = -100, + max = 100, + step = 1, + set = "SetProp", + get = "GetProp", + disabled = "GetAnchorDisabled", + hidden = "GetAnchorDisabled", + }, + anchorY = { + name = L["Y Offset"], + order = 6, + type = "range", + min = -100, + max = 100, + step = 1, + set = "SetProp", + get = "GetProp", + disabled = "GetAnchorDisabled", + hidden = "GetAnchorDisabled", + }, + }, + }, + scale = { + name = L["Scale"], + order = 93, + type = "group", + inline = true, + args = { + enableScale = { + name = L["Set New Scale"], + order = 1, + type = "toggle", + set = "SetProp", + get = "GetProp", + }, + scale = { + name = L["Scale"], + order = 2, + type = "range", + min = 0.25, + max = 2.5, + step = 0.05, + isPercent = true, + set = "SetProp", + get = "GetScale", + disabled = "GetScaleDisabled", + hidden = "GetScaleDisabled", + }, + }, + }, + alpha = { + name = L["Transparency"], + order = 94, + type = "group", + inline = true, + args = { + enableAlpha = { + name = L["Set Transparency"], + order = 1, + type = "toggle", + set = "SetProp", + get = "GetProp", + }, + alpha = { + name = L["Transparency"], + order = 2, + type = "range", + min = 0, + max = 1, + step = 0.01, + bigStep = 0.05, + isPercent = true, + set = "SetProp", + get = "GetAlpha", + disabled = "GetAlphaDisabled", + hidden = "GetAlphaDisabled", + }, + }, + }, + }, + plugins = { } + }, + rules = { + name = L["Rule"], + order = 3, + type = "group", + args = { + mode = { + name = L["Select this state"], + order = 2, + type = "select", + style = "radio", + values = { + default = L["by default"], + any = L["when ANY of these"], + all = L["when ALL of these"], + custom = L["via custom rule"], + keybind = L["via keybinding"], + }, + set = "SetType", + get = "GetType", + }, + clear = { + name = L["Clear All"], + order = 3, + type = "execute", + hidden = "GetClearAllDisabled", + disabled = "GetClearAllDisabled", + func = "ClearAllConditions", + }, + inputs = { + name = L["Conditions"], + order = 4, + type = "multiselect", + hidden = "GetConditionsDisabled", + disabled = "GetConditionsDisabled", + values = ruleSelect, + set = "SetCondition", + get = "GetCondition", + }, + custom = { + name = L["Custom Rule"], + order = 5, + type = "input", + multiline = true, + hidden = "GetCustomDisabled", + disabled = "GetCustomDisabled", + desc = L["Syntax like macro rules: see preset rules for examples"], + set = "SetCustomRule", + get = "GetCustomRule", + validate = "ValidateCustomRule", + }, + keybind = { + name = L["Keybinding"], + order = 6, + inline = true, + hidden = "GetKeybindDisabled", + disabled = "GetKeybindDisabled", + type = "group", + args = { + desc = { + name = L["Invoking a state keybind toggles an override of all other transition rules."], + order = 1, + type = "description", + }, + keybind = { + name = L["State Hotkey"], + desc = L["Define an override toggle keybind"], + order = 2, + type = "keybinding", + set = "SetKeybind", + get = "GetKeybind", + }, + }, + }, + }, + }, + } + + local handlers = { } + local meta = { + __index = function(self, key) + for _, h in pairs(handlers) do + if h[key] then + return h[key] + end + end + end, + } + local StateHandler = setmetatable({ }, meta) + local proto = { __index = StateHandler } + + function RegisterPropertyOptions( field, options, handler ) + stateOptions.properties.plugins[field] = options + handlers[field] = handler + end + + function UnregisterPropertyOptions( field ) + stateOptions.properties.plugins[field] = nil + handlers[field] = nil + end + + function StateHandler:New( bar, opts ) + local self = setmetatable( + { + bar = bar + }, + proto ) + + function self:GetName() + return opts.name + end + + function self:SetName(name) + opts.name = name + end + + function self:GetOrder() + return opts.order + end + + -- get reference to states table: even if the bar + -- name changes the states table ref won't + self.states = tbuild(module.db.profile.bars, bar:GetName(), "states") + self.state = tbuild(self.states, opts.name) + + opts.order = self:GetRuleField("order") + if opts.order == nil then + -- add after the highest + opts.order = 100 + for _, state in pairs(self.states) do + local x = tonumber(tfetch(state, "rule", "order")) + if x and x >= opts.order then + opts.order = x + 1 + end + end + self:SetRuleField("order",opts.order) + end + + return self + end + + -- helper methods + + function StateHandler:SetRuleField( key, value, ... ) + tbuild(self.state, "rule", ...)[key] = value + end + + function StateHandler:GetRuleField( ... ) + return tfetch(self.state, "rule", ...) + end + + function StateHandler:FixAll( setkey ) + -- if multiple selections in the same group are chosen when 'all' is selected, + -- keep only one of them. If changing the mode, the first in the fields list will + -- be chosen arbitrarily. Otherwise, if selecting a new checkbox from the field-set, + -- it will be retained. + local notified = false + if self:GetRuleField("type") == "all" then + for _, c in ipairs(rules) do + local rule, hidden, fields = unpack(c) + local once = false + if setkey then + for idx, field in ipairs(fields) do + if next(field) == setkey then + once = true + end + end + end + for idx, field in ipairs(fields) do + local key = next(field) + if self:GetRuleField("values",key) then + if once and key ~= setkey then + self:SetRuleField(key,false,"values") + if not setkey and not notified then + ReAction:UserError(L["Warning: one or more incompatible rules were turned off"]) + notified = true + end + end + once = true + end + end + end + end + end + + function StateHandler:GetNeighbors() + local before, after + for k, v in pairs(self.states) do + local o = tonumber(tfetch(v, "rule", "order")) + if o and k ~= self:GetName() then + local obefore = tfetch(self.states,before,"rule","order") + local oafter = tfetch(self.states,after,"rule","order") + if o < self:GetOrder() and (not obefore or obefore < o) then + before = k + end + if o > self:GetOrder() and (not oafter or oafter > o) then + after = k + end + end + end + return before, after + end + + function StateHandler:SwapOrder( a, b ) + -- do options table + local args = optionMap[self.bar].args + args[a].order, args[b].order = args[b].order, args[a].order + -- do profile + a = tbuild(self.states, a, "rule") + b = tbuild(self.states, b, "rule") + a.order, b.order = b.order, a.order + end + + -- handler methods + + function StateHandler:GetProp( info ) + -- gets property of the same name as the options arg + return GetProperty(self.bar, self:GetName(), info[#info]) + end + + function StateHandler:SetProp( info, value ) + -- sets property of the same name as the options arg + SetProperty(self.bar, self:GetName(), info[#info], value) + end + + function StateHandler:DeleteState() + if self.states[self:GetName()] then + self.states[self:GetName()] = nil + ApplyStates(self.bar) + end + optionMap[self.bar].args[self:GetName()] = nil + end + + function StateHandler:SetStateName(info, value) + -- check for existing state name + if self.states[value] then + ReAction:UserError(format(L["State named '%s' already exists"],value)) + return + end + local args = optionMap[self.bar].args + local name = self:GetName() + self.states[value], args[value], self.states[name], args[name] = self.states[name], args[name], nil, nil + self:SetName(value) + ApplyStates(self.bar) + ReAction:ShowEditor(self.bar, moduleID, value) + end + + function StateHandler:MoveStateUp() + local before, after = self:GetNeighbors() + if before then + self:SwapOrder(before, self:GetName()) + ApplyStates(self.bar) + end + end + + function StateHandler:MoveStateDown() + local before, after = self:GetNeighbors() + if after then + self:SwapOrder(self:GetName(), after) + ApplyStates(self.bar) + end + end + + function StateHandler:GetAnchorDisabled() + return not GetProperty(self.bar, self:GetName(), "anchorEnable") + end + + function StateHandler:GetAnchorFrames(info) + self._anchorframes = self._anchorframes or { } + table.wipe(self._anchorframes) + + table.insert(self._anchorframes, "UIParent") + for name, bar in ReAction:IterateBars() do + table.insert(self._anchorframes, bar:GetFrame():GetName()) + end + return self._anchorframes + end + + function StateHandler:GetAnchorFrame(info) + local value = self:GetProp(info) + for k,v in pairs(self._anchorframes) do + if v == value then + return k + end + end + end + + function StateHandler:SetAnchorFrame(info, value) + local f = _G[self._anchorframes[value]] + if f then + SetFrameRef(self.bar:GetFrame(), "anchor-"..self:GetName(), f) + self:SetProp(info, f:GetName()) + end + end + + function StateHandler:SetAnchorPointProp(info, value) + self:SetProp(info, value ~= "NONE" and value or nil) + end + + function StateHandler:GetAnchorPointProp(info) + return self:GetProp(info) or "NONE" + end + + function StateHandler:GetScale(info) + return self:GetProp(info) or 1.0 + end + + function StateHandler:GetScaleDisabled() + return not GetProperty(self.bar, self:GetName(), "enableScale") + end + + function StateHandler:GetAlpha(info) + return self:GetProp(info) or 1.0 + end + + function StateHandler:GetAlphaDisabled() + return not GetProperty(self.bar, self:GetName(), "enableAlpha") + end + + function StateHandler:SetType(info, value) + self:SetRuleField("type", value) + self:FixAll() + ApplyStates(self.bar) + end + + function StateHandler:GetType() + return self:GetRuleField("type") + end + + function StateHandler:GetClearAllDisabled() + local t = self:GetRuleField("type") + return not( t == "any" or t == "all" or t == "custom") + end + + function StateHandler:ClearAllConditions() + local t = self:GetRuleField("type") + if t == "custom" then + self:SetRuleField("custom","") + elseif t == "any" or t == "all" then + self:SetRuleField("values", {}) + end + ApplyStates(self.bar) + end + + function StateHandler:GetConditionsDisabled() + local t = self:GetRuleField("type") + return not( t == "any" or t == "all") + end + + function StateHandler:SetCondition(info, key, value) + self:SetRuleField(ruleMap[key], value or nil, "values") + if value then + self:FixAll(ruleMap[key]) + end + ApplyStates(self.bar) + end + + function StateHandler:GetCondition(info, key) + return self:GetRuleField("values", ruleMap[key]) or false + end + + function StateHandler:GetCustomDisabled() + return self:GetRuleField("type") ~= "custom" + end + + function StateHandler:SetCustomRule(info, value) + self:SetRuleField("custom",value) + ApplyStates(self.bar) + end + + function StateHandler:GetCustomRule() + return self:GetRuleField("custom") or "" + end + + function StateHandler:ValidateCustomRule(info, value) + local s = value:gsub("%s","") -- remove all spaces + -- unfortunately %b and captures don't support the '+' notation, or this would be considerably simpler + repeat + if s == "" then + return true + end + local c, r = s:match("(%b[])(.*)") + if c == nil and s and #s > 0 then + return format(L["Invalid custom rule '%s': each clause must appear within [brackets]"],value or "") + end + s = r + until c == nil + return true + end + + function StateHandler:GetKeybindDisabled() + return self:GetRuleField("type") ~= "keybind" + end + + function StateHandler:GetKeybind() + return self:GetRuleField("keybind") + end + + function StateHandler:SetKeybind(info, value) + if value and #value == 0 then + value = nil + end + self:SetRuleField("keybind",value) + ApplyStates(self.bar) + end + + local function CreateStateOptions(bar, name) + local opts = { + type = "group", + name = name, + childGroups = "tab", + args = stateOptions + } + + opts.handler = StateHandler:New(bar,opts) + + return opts + end + + function module:GetBarOptions(bar) + local private = { } + local states = tbuild(module.db.profile.bars, bar:GetName(), "states") + local options = { + name = L["Dynamic State"], + type = "group", + order = -1, + childGroups = "tree", + disabled = InCombatLockdown, + args = { + __desc__ = { + name = L["States are evaluated in the order they are listed"], + order = 1, + type = "description", + }, + __new__ = { + name = L["New State..."], + order = 2, + type = "group", + args = { + name = { + name = L["State Name"], + desc = L["Set a name for the new state"], + order = 1, + type = "input", + get = function() return private.newstatename or "" end, + set = function(info,value) private.newstatename = value end, + pattern = "^%w*$", + usage = L["State names must be alphanumeric without spaces"], + }, + create = { + name = L["Create State"], + order = 2, + type = "execute", + func = function () + local name = private.newstatename + if states[name] then + ReAction:UserError(format(L["State named '%s' already exists"],name)) + else + -- TODO: select default state options and pass as final argument + states[name] = { } + optionMap[bar].args[name] = CreateStateOptions(bar,name) + ReAction:ShowEditor(bar, moduleID, name) + private.newstatename = "" + end + end, + disabled = function() + local name = private.newstatename or "" + return #name == 0 or name:find("%W") + end, + } + } + } + } + } + for name, config in pairs(states) do + options.args[name] = CreateStateOptions(bar,name) + end + optionMap[bar] = options + return options + end +end + +-- Module API -- + +-- Pass in a property field-name, an implementation secure snippet, a static options table, and an +-- optional options handler method-table +-- +-- The options table is static, i.e. not bar-specific and should only reference handler method +-- strings (either existing ones or those added via optHandler). The existing options are ordered +-- 90-99. Order #1 is reserved for the heading. +-- +-- The contents of optHandler, if provided, will be added to the existing StateHandler options metatable. +-- See above, for existing API. In particular see the properties set up in the New method: self.bar, +-- self.states, and self:GetName(), and the generic property handlers self:GetProp() and self:SetProp(). +-- +function module:RegisterStateProperty( field, snippetHandler, options, optHandler ) + RegisterProperty(field, snippetHandler) + RegisterPropertyOptions(field, options, optHandler) +end + +function module:UnregisterStateProperty( field ) + UnregisterProperty(field) + UnregisterPropertyOptions(field) +end + + +-- Export methods to Bar class -- + +function ReAction.Bar:GetState() + return GetManagedEnvironment(self:GetFrame()).state +end + +ReAction.Bar.GetStateProperty = GetProperty +ReAction.Bar.SetStateProperty = SetProperty
--- a/modules/modules.xml Wed Jan 07 00:57:02 2009 +0000 +++ b/modules/modules.xml Thu Jan 08 00:57:27 2009 +0000 @@ -2,17 +2,15 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.blizzard.com/wow/ui/..\FrameXML\UI.xsd"> -<!-- config/layout modules --> -<Include file="ReAction_ConfigUI\ReAction_ConfigUI.xml"/> -<Include file="ReAction_HideBlizzard\ReAction_HideBlizzard.xml"/> - - -<!-- button modules --> -<Include file="ReAction_Action\ReAction_Action.xml"/> -<Include file="ReAction_PetAction\ReAction_PetAction.xml"/> - - -<!-- other modules --> -<Include file="ReAction_ButtonFacade\ReAction_ButtonFacade.xml"/> +<Script file="bindings.lua"/> +<Script file="Bar.lua"/> +<Script file="Overlay.lua"/> +<Script file="Config.lua"/> +<Script file="State.lua"/> +<Script file="LDB.lua"/> +<Script file="LBF.lua"/> +<Script file="HideBlizzard.lua"/> +<Script file="Action.lua"/> +<Script file="PetAction.lua"/> </Ui> \ No newline at end of file