view Editor.lua @ 207:c24ac8ee1e45

kb mode and config mode now mutually exclusive
author Flick <flickerstreak@gmail.com>
date Tue, 16 Nov 2010 21:49:54 -0800
parents 85213d045acb
children 376c424900f0
line wrap: on
line source
local addonName, addonTable = ...
local ReAction = addonTable.ReAction
local L = ReAction.L
local _G = _G
local wipe = wipe

local AceConfigReg = LibStub("AceConfigRegistry-3.0")
local AceConfigDialog = LibStub("AceConfigDialog-3.0")


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"],
}

local Editor = { }

function Editor:New()
  -- create new self
  self = setmetatable( { }, { __index = self } )

  self.barOptMap = setmetatable({},{__mode="v"})
  self.tmp = { }
  self.configID = "ReAction-Editor"

  self.gui = LibStub("AceGUI-3.0"):Create("Frame")
  
  local frame = self.gui.frame
  frame:SetClampedToScreen(true)
  frame:Hide()

  frame:SetScript("OnHide", 
    function(...)
      ReAction:SetConfigMode(false)
    end)

  self.title = ("%s - %s"):format(L["ReAction"],L["Bar Editor"])
  self.gui:SetTitle(self.title)

  self.options = {
    type = "group",
    name = self.title,
    handler = self,
    childGroups = "select",
    args = {
      launchConfig = {
        type = "execute",
        name = L["Global Config"],
        desc = L["Opens ReAction global configuration settings panel"],
        func = function() ReAction:ShowOptions(); self:Close() end,
        order = 1
      },
      desc = {
        type = "description",
        name = L["Use the mouse to arrange and resize the bars on screen. Tooltips on bars indicate additional functionality."],
        order = 2,
      },
      hdr = {
        type = "header",
        name = L["Select Bar"],
        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 self.tmp.barName or "" end,
            set  = function(info, val) self.tmp.barName = val end,
            order = 2,
          },
          type = {
            type = "select",
            name = L["Button Type"],
            get  = function() return self.tmp.barType or ReAction:GetDefaultBarType() or "" end,
            set  = function(info, val) 
                     local c = ReAction:GetBarTypeConfig(val)
                     self.tmp.barType = val 
                     self.tmp.barSize = c.defaultButtonSize or self.tmp.barSize
                     self.tmp.barRows = c.defaultBarRows or self.tmp.barRows
                     self.tmp.barCols = c.defaultBarCols or self.tmp.barCols
                     self.tmp.barSpacing = c.defaultBarSpacing or self.tmp.barSpacing
                   end,
            values = "GetBarTypes",
            order = 3,
          },
          go = {
            type = "execute",
            name = L["Create Bar"],
            func = "CreateBar",
            order = 4,
          },
          grid = {
            type = "group",
            name = L["Button Grid"],
            inline = true,
            order = 5,
            args = {
              rows = {
                type = "range",
                name = L["Rows"],
                get  = function() return self.tmp.barRows or 1 end,
                set  = function(info, val) self.tmp.barRows = val end,
                width = "full",
                min = 1,
                max = 32,
                step = 1,
                order = 2,
              },
              cols = {
                type = "range",
                name = L["Columns"],
                get  = function() return self.tmp.barCols or 12 end,
                set  = function(info, val) self.tmp.barCols = val end,
                width = "full",
                min = 1, 
                max = 32,
                step = 1,
                order = 3,
              },
              sz = {
                type = "range",
                name = L["Size"],
                get  = function() return self.tmp.barSize or 36 end,
                set  = function(info, val) self.tmp.barSize = val end,
                width = "full",
                min = 10,
                max = 72,
                step = 1,
                order = 4,
              },
              spacing = {
                type = "range",
                name = L["Spacing"],
                get  = function() return self.tmp.barSpacing or 3 end,
                set  = function(info, val) self.tmp.barSpacing = val end,
                width = "full",
                min = 0,
                max = 24,
                step = 1,
                order = 5,
              }
            }
          }
        }
      }
    }
  }

  self.gui:SetCallback("OnClose", 
    function() 
      ReAction:SetConfigMode(false)
    end )

  AceConfigReg:RegisterOptionsTable(self.configID, self.options)
  AceConfigDialog:SetDefaultSize(self.configID, 700, 540)

  ReAction.RegisterCallback(self,"OnCreateBar")
  ReAction.RegisterCallback(self,"OnDestroyBar")
  ReAction.RegisterCallback(self,"OnEraseBar")
  ReAction.RegisterCallback(self,"OnRenameBar")

  for name, bar in ReAction:IterateBars() do
    self:CreateBarTree(bar)
  end

  return self
end


function Editor:Open(bar, ...)
  if bar then
    AceConfigDialog:SelectGroup(self.configID, self.barOptMap[bar:GetName()], ...)
  end
  AceConfigDialog:Open(self.configID,self.gui)
  self.gui:SetTitle(self.title)
end

function Editor:Close()
  if self.gui then
    self.gui:ReleaseChildren()
    self.gui:Hide()
  end
end

function Editor:Refresh()
  AceConfigReg:NotifyChange(self.configID)
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 = self.options.args
  local key
  local i = 1
  repeat
    key = ("bar%s"):format(i)
    i = i+1
  until args[key] == nil
  self.barOptMap[name] = key
  args[key] = { 
    type = "group",
    name = name,
    childGroups = "tab",
    order = i+100,
    args = {
      general = {
        type = "group",
        name = L["General"],
        order = 1,
        args = {
          name = {
            type = "input",
            name = L["Rename Bar"],
            get  = function() return bar:GetName() end,
            set  = function(info, value) return ReAction:RenameBar(bar, value) end,
            order = 1,
          },
          delete = {
            type = "execute",
            name = L["Delete Bar"],
            desc = function() return bar:GetName() end,
            confirm = true,
            func = function() ReAction:EraseBar(bar) end,
            order = 2
          },
          anchor = {
            type = "group",
            name = L["Anchor"],
            inline = true,
            args = {
              frame = {
                type = "input",
                name = L["Frame"],
                desc = L["The frame that the bar is anchored to"],
                get  = function() local _, f = bar:GetAnchor(); return f end,
                set  = function(info, val) bar:SetAnchor(nil,val) end,
                validate = function(info, name) 
                    if name then
                      local f = ReAction:GetBar(name)
                      if f then
                        return true
                      else
                        f = _G[name]
                        if f and type(f) == "table" and f.IsObjectType and f:IsObjectType("Frame") then
                          local _, explicit = f:IsProtected()
                          return explicit
                        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:RefreshBarOptions()
end

function Editor:RefreshBarOptions()
  for name, key in pairs(self.barOptMap) do
    local bar = ReAction:GetBar(name)
    if bar and key and self.options.args[key] then
      self.options.args[key].plugins = self:GenerateBarOptionsTable(bar)
    end
  end
  AceConfigReg:NotifyChange(self.configID)
end

function Editor:OnCreateBar(evt, bar)
  if not self.tmp.creating then
    -- a bit of hack to work around OnCreateBar event handler ordering
    self:CreateBarTree(bar)
  end
end

function Editor:OnDestroyBar(evt, bar, name)
  local key = self.barOptMap[name]
  if key then
    self.options.args[key] = nil
  end
  self:Refresh()
end

function Editor:OnEraseBar(evt, name)
  local key = self.barOptMap[name]
  self.barOptMap[name] = nil
  if key then
    self.options.args[key] = nil
    self:Refresh()
  end
end

function Editor:OnRenameBar(evt, bar, oldname, newname)
  local key = self.barOptMap[oldname]
  self.barOptMap[oldname], self.barOptMap[newname] = nil, key
  if key then
    self.options.args[key].name = newname
    self:Refresh()
  end
end

local _scratch = { }
function Editor:GetBarTypes()
  wipe(_scratch)
  return ReAction:GetBarTypeOptions(_scratch)
end

function Editor:CreateBar()
  if self.tmp.barName and self.tmp.barName ~= "" then
    self.tmp.creating = true
    local bar = ReAction:CreateBar(self.tmp.barName, self.tmp.barType or ReAction:GetDefaultBarType(), self.tmp.barRows, self.tmp.barCols, self.tmp.barSize, self.tmp.barSpacing)
    if bar then
      self:CreateBarTree(bar)
      AceConfigDialog:SelectGroup(self.configID, self.barOptMap[self.tmp.barName])
      self.tmp.barName = nil
    end
    self.tmp.creating = false
  end
end

function Editor:GenerateBarOptionsTable( bar )
  local opts = { }
  if not ReAction.barOptionGenerators then
    return
  end

  for module, func in pairs(ReAction.barOptionGenerators) do
    local success, r
    if type(func) == "string" then
      success, r = pcall(module[func], module, bar)
    else
      success, r = pcall(func, bar)
    end
    if success then
      if r then
        opts[module:GetName()] = { [module:GetName()] = r }
      end
    else
      geterrorhandler()(r)
    end
  end
  return opts
end



---- Export to ReAction ----
function ReAction:ShowEditor(bar, ...)
  if InCombatLockdown() then
    self:UserError(L["ReAction config mode disabled during combat."])
  else
    self.editor = self.editor or Editor:New()
    self.editor:Open(bar, ...)
    self:SetConfigMode(true)
  end
end

function ReAction:CloseEditor()
  if self.editor then
    self.editor:Close()
  end
end

function ReAction:RefreshEditor()
  if self.editor then
    self.editor:RefreshBarOptions()
  end
end

function ReAction:RegisterBarOptionGenerator( module, func )
  if not module or type(module) ~= "table" then -- doesn't need to be a proper module, strictly
    error("ReAction:RegisterBarOptionGenerator() : Invalid module")
  end
  if type(func) == "string" then
    if not module[func] then
      error(("ReAction:RegisterBarOptionGenerator() : Invalid method '%s'"):format(func))
    end
  elseif func and type(func) ~= "function" then
    error("ReAction:RegisterBarOptionGenerator() : Invalid function")
  end
  self.barOptionGenerators = self.barOptionGenerators or { }
  self.barOptionGenerators[module] = func

  if self.editor then
    self.editor:RefreshBarOptions()
  end
end