flickerstreak@25: --[[ flickerstreak@25: ReAction bar module. flickerstreak@25: flickerstreak@25: This is the heart of ReAction, the bar management module. Many of its flickerstreak@25: functions turn around and iterate all modules registered to the ReAction flickerstreak@25: parent core. flickerstreak@25: flickerstreak@25: Also defined in this file is the Bar class implementation, which can be flickerstreak@25: used by other modules to manipulate the action bars. Many of the module flickerstreak@25: iterated calls pass a bar or list of bars as an argument. flickerstreak@25: flickerstreak@25: Module methods called by the Bar module: flickerstreak@25: module:ApplyToBar(bar) flickerstreak@25: module:RemoveFromBar(bar) flickerstreak@25: module:RefreshBar(bar) flickerstreak@25: module:ApplyConfigMode(mode,listOfBars) flickerstreak@25: module:GetBarNameModifier(bar) flickerstreak@25: module:EraseBarConfig(barName) flickerstreak@25: flickerstreak@25: flickerstreak@25: useful Bar object API (partial list): flickerstreak@25: f = bar:GetFrame() -- f is derived from SecureStateDriver flickerstreak@25: bar:RefreshLayout() flickerstreak@25: w,h = bar:GetSize() flickerstreak@25: r,c,s = bar:GetButtonGrid() -- #rows, #columns, inter-button spacing flickerstreak@25: name = bar:GetName() flickerstreak@25: bar:PlaceButton(frame, idx, baseSizeX, baseSizeY) -- idx is 1-based flickerstreak@25: flickerstreak@25: --]] flickerstreak@25: flickerstreak@25: -- local imports flickerstreak@25: local ReAction = ReAction flickerstreak@25: local L = ReAction.L flickerstreak@25: local _G = _G flickerstreak@25: local AceOO = AceLibrary("AceOO-2.0") flickerstreak@25: local CreateFrame = CreateFrame flickerstreak@25: local geterrorhandler = geterrorhandler flickerstreak@25: local pcall = pcall flickerstreak@25: local print = ReAction.print flickerstreak@25: flickerstreak@25: -- update ReAction revision if this file is newer flickerstreak@25: local revision = tonumber(("$Revision: 1 $"):match("%d+")) flickerstreak@25: if revision > ReAction.revision then flickerstreak@25: Reaction.revision = revision flickerstreak@25: end flickerstreak@25: flickerstreak@25: local moduleID = "Bar" flickerstreak@25: flickerstreak@25: -- flickerstreak@25: -- Bar module declaration flickerstreak@25: -- flickerstreak@25: local module = ReAction:NewModule( moduleID ) flickerstreak@25: flickerstreak@25: -- flickerstreak@25: -- Bar class declaration flickerstreak@25: -- flickerstreak@25: local BarClass = AceOO.Class() flickerstreak@25: local Bar = BarClass.prototype flickerstreak@25: module.BarClass = BarClass flickerstreak@25: flickerstreak@27: -- flickerstreak@27: -- local utility flickerstreak@27: -- flickerstreak@27: local function deepCopy(x) flickerstreak@27: if type(x) ~= "table" then flickerstreak@27: return x flickerstreak@27: end flickerstreak@27: local r = {} flickerstreak@27: for k,v in pairs(x) do flickerstreak@27: r[k] = deepCopy(v) flickerstreak@27: end flickerstreak@27: return r flickerstreak@27: end flickerstreak@25: flickerstreak@25: -- flickerstreak@25: -- Bar module implementation flickerstreak@25: -- flickerstreak@25: function module:OnInitialize() flickerstreak@25: self.db = ReAction:AcquireDBNamespace(moduleID) flickerstreak@25: ReAction:RegisterDefaults(moduleID,"profile", flickerstreak@25: { flickerstreak@25: bars = { }, flickerstreak@25: defaultBar = { } flickerstreak@25: } flickerstreak@25: ) flickerstreak@25: self.bars = {} flickerstreak@25: end flickerstreak@25: flickerstreak@25: function module:OnEnable() flickerstreak@25: self:InitializeBars() flickerstreak@25: end flickerstreak@25: flickerstreak@25: function module:OnDisable() flickerstreak@25: self:TearDownBars() flickerstreak@25: end flickerstreak@25: flickerstreak@25: function module:OnProfileEnable() flickerstreak@25: self:InitializeBars() flickerstreak@25: end flickerstreak@25: flickerstreak@25: function module:OnProfileDisable() flickerstreak@25: self:TearDownBars() flickerstreak@25: end flickerstreak@25: flickerstreak@25: function module:InitializeBars() flickerstreak@25: if not(self.inited) then flickerstreak@25: for name, config in pairs(self.db.profile.bars) do flickerstreak@25: if config then flickerstreak@25: self:CreateBar(name, config) flickerstreak@25: end flickerstreak@25: end flickerstreak@25: self:CallMethodOnAllBars("ApplyAnchor") -- re-anchor in the case of oddball ordering flickerstreak@25: self.inited = true flickerstreak@25: end flickerstreak@25: end flickerstreak@25: flickerstreak@25: function module:TearDownBars() flickerstreak@25: for name, bar in pairs(self.bars) do flickerstreak@25: if bar then flickerstreak@25: self.bars[name] = self:DeleteBar(bar) flickerstreak@25: end flickerstreak@25: end flickerstreak@25: self.inited = false flickerstreak@25: end flickerstreak@25: flickerstreak@25: -- Gets config from existing DB name or default if not supplied flickerstreak@25: -- Saves to DB if name not known flickerstreak@25: function module:CreateBar(name, config) flickerstreak@25: local profile = self.db.profile flickerstreak@25: if not name then flickerstreak@25: i = 1 flickerstreak@25: repeat flickerstreak@25: name = L["Bar "]..i flickerstreak@25: i = i + 1 flickerstreak@25: until self.bars[name] == nil flickerstreak@25: end flickerstreak@25: config = config or profile.bars[name] or deepCopy(profile.defaultBar) flickerstreak@25: if not profile.bars[name] then flickerstreak@25: profile.bars[name] = config flickerstreak@25: end flickerstreak@25: local bar = self.BarClass:new( name, config ) flickerstreak@25: ReAction:CallMethodOnAllModules("ApplyToBar", bar) flickerstreak@25: self.bars[name] = bar flickerstreak@25: return bar flickerstreak@25: end flickerstreak@25: flickerstreak@25: flickerstreak@25: local SelectBar flickerstreak@25: do flickerstreak@25: SelectBar = function(x) flickerstreak@25: local bar, name flickerstreak@25: if type(x) == "string" then flickerstreak@25: name = x flickerstreak@25: bar = module:GetBar(name) flickerstreak@25: elseif AceOO.inherits(x,BarClass) then flickerstreak@25: bar = x flickerstreak@25: for k,v in pairs(module.bars) do flickerstreak@25: if v == bar then flickerstreak@25: name = k flickerstreak@25: end flickerstreak@25: end flickerstreak@25: else flickerstreak@25: error("bad argument to SelectBar") flickerstreak@25: end flickerstreak@25: return bar, name flickerstreak@25: end flickerstreak@25: end flickerstreak@25: flickerstreak@25: -- Takes either a bar name string or a bar object. flickerstreak@25: -- Does NOT destroy the DB entry, this function is only used for flickerstreak@25: -- enable/disable and profile switching. To remove a bar permanently, flickerstreak@25: -- use EraseBar() instead. flickerstreak@25: function module:DeleteBar(x) flickerstreak@25: local bar, name = SelectBar(x) flickerstreak@25: if name and bar then flickerstreak@25: self.bars[name] = nil flickerstreak@25: ReAction:CallMethodOnAllModules("RemoveFromBar", bar) flickerstreak@25: bar:Destroy() flickerstreak@25: end flickerstreak@25: end flickerstreak@25: flickerstreak@25: function module:EraseBar(x) flickerstreak@25: local bar, name = SelectBar(x) flickerstreak@25: if name and bar then flickerstreak@25: self:DeleteBar(bar) flickerstreak@25: self.db.profile.bars[name] = nil flickerstreak@25: ReAction:CallMethodOnAllModules("EraseBarConfig", name) flickerstreak@25: end flickerstreak@25: end flickerstreak@25: flickerstreak@25: function module:GetBar(name) flickerstreak@25: return self.bars[name] flickerstreak@25: end flickerstreak@25: flickerstreak@25: function module:RenameBar(x, newname) flickerstreak@25: local bar, name = SelectBar(x) flickerstreak@25: if bar and name and newname then flickerstreak@25: if self.bars[newname] then flickerstreak@25: error(L["ReAction: name already in use"]) flickerstreak@25: end flickerstreak@25: self.bars[newname] = self.bars[name] flickerstreak@25: self.bars[name] = nil flickerstreak@25: bar:SetName(newname) flickerstreak@25: local cfg = self.db.profile.bars flickerstreak@25: cfg[newname], cfg[name] = cfg[name], nil flickerstreak@25: end flickerstreak@25: end flickerstreak@25: flickerstreak@25: function module:CallMethodOnAllBars(method,...) flickerstreak@25: local m flickerstreak@25: if type(method) == "function" then flickerstreak@25: m = method flickerstreak@25: elseif type(method) ~= "string" then flickerstreak@25: error("Invalid method passed to ReAction_Bar:CallMethodOnAllBars()") flickerstreak@25: end flickerstreak@25: for _, bar in pairs(self.bars) do flickerstreak@25: if bar then flickerstreak@25: local m = m or bar[method] flickerstreak@25: if m then flickerstreak@25: local success,err = pcall(m,bar,...) flickerstreak@25: if not success then flickerstreak@25: geterrorhandler()(err) flickerstreak@25: end flickerstreak@25: end flickerstreak@25: end flickerstreak@25: end flickerstreak@25: end flickerstreak@25: flickerstreak@25: flickerstreak@25: flickerstreak@25: function module:GetBarMenuOptions(bar) flickerstreak@25: if not(bar.modMenuOpts[moduleID]) then flickerstreak@25: bar.modMenuOpts[moduleID] = { flickerstreak@25: delete = { flickerstreak@25: type = "execute", flickerstreak@25: name = L["Delete Bar"], flickerstreak@25: desc = L["Remove the bar from the current profile"], flickerstreak@25: func = function() self:EraseBar(bar) end, flickerstreak@25: order = 1 flickerstreak@25: }, flickerstreak@25: } flickerstreak@25: end flickerstreak@25: return bar.modMenuOpts[moduleID] flickerstreak@25: end flickerstreak@25: flickerstreak@25: function module:GetBarConfigOptions(bar, cfgModule) flickerstreak@25: if not(bar.modConfigOpts[moduleID]) then flickerstreak@25: bar.modConfigOpts[moduleID] = { flickerstreak@25: delete = { flickerstreak@25: type = "execute", flickerstreak@25: name = L["Delete Bar"], flickerstreak@25: desc = L["Remove the bar from the current profile"], flickerstreak@25: func = function() self:EraseBar(bar); cfgModule:RefreshConfig() end flickerstreak@25: }, flickerstreak@25: rename = { flickerstreak@25: type = "text", flickerstreak@25: name = L["Rename Bar"], flickerstreak@25: desc = L["Set a name for the bar"], flickerstreak@25: get = "GetName", flickerstreak@25: set = function(name) self:RenameBar(bar,name); cfgModule:RefreshConfig() end flickerstreak@25: } flickerstreak@25: } flickerstreak@25: end flickerstreak@25: return bar.modConfigOpts[moduleID] flickerstreak@25: end flickerstreak@25: flickerstreak@25: flickerstreak@25: flickerstreak@25: -- flickerstreak@25: -- Bar class implementation flickerstreak@25: -- flickerstreak@25: function Bar:init( name, config ) flickerstreak@25: BarClass.super.prototype.init(self) flickerstreak@25: self.name, self.config = name, config flickerstreak@25: flickerstreak@25: if type(config) ~= "table" then flickerstreak@25: error("ReAction:Bar: config table required") flickerstreak@25: end flickerstreak@25: flickerstreak@25: local f = CreateFrame("Frame",nil,config.parent or UIParent,"SecureStateDriverTemplate") flickerstreak@25: f:SetFrameStrata("MEDIUM") flickerstreak@25: config.width = config.width or 400 flickerstreak@25: config.height = config.height or 80 flickerstreak@25: f:SetWidth(config.width) flickerstreak@25: f:SetWidth(config.height) flickerstreak@25: flickerstreak@25: self.frame = f flickerstreak@25: self:RefreshLayout() flickerstreak@25: self:ApplyAnchor() flickerstreak@25: f:Show() flickerstreak@25: end flickerstreak@25: flickerstreak@25: function Bar:Destroy() flickerstreak@25: local f = self.frame flickerstreak@25: f:UnregisterAllEvents() flickerstreak@25: f:Hide() flickerstreak@25: f:SetParent(UIParent) flickerstreak@25: f:ClearAllPoints() flickerstreak@25: self.labelString = nil flickerstreak@25: self.controlFrame = nil flickerstreak@25: self.frame = nil flickerstreak@25: self.config = nil flickerstreak@25: end flickerstreak@25: flickerstreak@25: function Bar:RefreshLayout() flickerstreak@25: ReAction:CallMethodOnAllModules("RefreshBar", self) flickerstreak@25: end flickerstreak@25: flickerstreak@25: function Bar:ApplyAnchor() flickerstreak@25: local f, config = self.frame, self.config flickerstreak@25: f:SetWidth(config.width) flickerstreak@25: f:SetHeight(config.height) flickerstreak@25: local anchor = config.anchor flickerstreak@25: if anchor then flickerstreak@25: local anchorTo flickerstreak@25: if config.anchorTo then flickerstreak@25: anchorTo = module:GetBar(config.anchorTo) or _G[config.anchorTo] flickerstreak@25: end flickerstreak@25: f:SetPoint(anchor, anchorTo, config.relativePoint, config.x or 0, config.y or 0) flickerstreak@25: else flickerstreak@25: f:SetPoint("CENTER") flickerstreak@25: end flickerstreak@25: end flickerstreak@25: flickerstreak@25: function Bar:GetFrame() flickerstreak@25: return self.frame flickerstreak@25: end flickerstreak@25: flickerstreak@25: function Bar:GetSize() flickerstreak@25: return self.frame:GetWidth() or 200, self.frame:GetHeight() or 200 flickerstreak@25: end flickerstreak@25: flickerstreak@25: function Bar:SetSize(w,h) flickerstreak@25: self.config.width = w flickerstreak@25: self.config.height = h flickerstreak@25: end flickerstreak@25: flickerstreak@25: function Bar:GetButtonSize() flickerstreak@25: local w = self.config.btnWidth or 32 flickerstreak@25: local h = self.config.btnHeight or 32 flickerstreak@25: -- TODO: get from modules? flickerstreak@25: return w,h flickerstreak@25: end flickerstreak@25: flickerstreak@25: function Bar:SetButtonSize(w,h) flickerstreak@25: if w > 0 and h > 0 then flickerstreak@25: self.config.btnWidth = w flickerstreak@25: self.config.btnHeight = h flickerstreak@25: end flickerstreak@25: end flickerstreak@25: flickerstreak@25: function Bar:GetButtonGrid() flickerstreak@25: local cfg = self.config flickerstreak@25: local r = cfg.btnRows or 1 flickerstreak@25: local c = cfg.btnColumns or 1 flickerstreak@25: local s = cfg.spacing or 4 flickerstreak@25: return r,c,s flickerstreak@25: end flickerstreak@25: flickerstreak@25: function Bar:SetButtonGrid(r,c,s) flickerstreak@25: if r > 0 and c > 0 and s > 0 then flickerstreak@25: local cfg = self.config flickerstreak@25: cfg.btnRows = r flickerstreak@25: cfg.btnColumns = c flickerstreak@25: cfg.spacing = s flickerstreak@25: end flickerstreak@25: end flickerstreak@25: flickerstreak@25: -- This should only be called from module:RenameBar(), otherwise flickerstreak@25: -- the bar's internal name and the module's list of bars by name flickerstreak@25: -- can get out of sync. flickerstreak@25: function Bar:SetName( name ) flickerstreak@25: name = name or "" flickerstreak@25: self.name = name flickerstreak@25: end flickerstreak@25: flickerstreak@25: function Bar:GetName() flickerstreak@25: return self.name flickerstreak@25: end flickerstreak@25: flickerstreak@25: function Bar:PlaceButton(f, idx, baseW, baseH) flickerstreak@25: local r, c, s = self:GetButtonGrid() flickerstreak@25: local bh, bw = self:GetButtonSize() flickerstreak@25: local row, col = floor((idx-1)/c), mod((idx-1),c) -- zero-based flickerstreak@25: local x, y = col*bw + (col+0.5)*s, row*bh + (row+0.5)*s flickerstreak@25: local scale = bw/baseW flickerstreak@25: flickerstreak@25: f:ClearAllPoints() flickerstreak@25: f:SetPoint("TOPLEFT",x/scale,-y/scale) flickerstreak@25: f:SetScale(scale) flickerstreak@25: -- f:Show() flickerstreak@25: end flickerstreak@25: