changeset 63:768be7eb22a0

Converted several ReAction APIs to event-driven model instead of 'call-method-on-all-modules' model. Cleaned up a number of other architectural issues.
author Flick <flickerstreak@gmail.com>
date Thu, 22 May 2008 22:02:08 +0000
parents f9cdb920470a
children 2000f4f4c6af
files Bar.lua ReAction.lua modules/ReAction_Action/ReAction_Action.lua modules/ReAction_ConfigUI/ReAction_ConfigUI.lua modules/ReAction_ModuleTemplate/ReAction_ModuleName.lua modules/ReAction_PetAction/ReAction_PetAction.lua modules/ReAction_PossessBar/ReAction_PossessBar.lua
diffstat 7 files changed, 391 insertions(+), 300 deletions(-) [+]
line wrap: on
line diff
--- a/Bar.lua	Tue May 13 16:42:52 2008 +0000
+++ b/Bar.lua	Thu May 22 22:02:08 2008 +0000
@@ -34,6 +34,8 @@
   f:SetWidth(config.width)
   f:SetWidth(config.height)
 
+  ReAction.RegisterCallback(self, "OnConfigModeChanged")
+
   self.frame = f
   self:RefreshLayout()
   self:ApplyAnchor()
@@ -46,14 +48,19 @@
   f:Hide()
   f:SetParent(UIParent)
   f:ClearAllPoints()
+  ReAction.UnregisterAllCallbacks(self)
   self.labelString = nil
   self.controlFrame = nil
   self.frame = nil
   self.config = nil
 end
 
+function Bar:OnConfigModeChanged(event, mode)
+  self:ShowControls(mode)
+end
+
 function Bar:RefreshLayout()
-  ReAction:CallMethodOnAllModules("RefreshBar", self)
+  ReAction:RefreshBar(self)
 end
 
 function Bar:ApplyAnchor()
@@ -186,7 +193,7 @@
     local point, relativeTo, relativePoint, x, y = f:GetPoint(1)
     relativeTo = relativeTo or f:GetParent()
     local anchorTo
-    for name, b in pairs(ReAction.bars) do
+    for name, b in ReAction:IterateBars() do
       if b and b:GetFrame() == relativeTo then
         anchorTo = name
         break
@@ -401,7 +408,7 @@
   local function GetClosestVisibleEdge( f )
     local r, o, e1, e2
     local a = anchorOutside
-    for _, b in pairs(ReAction.bars) do
+    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
@@ -523,10 +530,6 @@
     end
   end
 
-  local function RefreshBarEditor()
-    ReAction:CallModuleMethod("ConfigUI","RefreshBarEditor")
-  end
-
   CreateControls = function(bar)
     local f = bar.frame
 
@@ -584,7 +587,7 @@
       StoreSize(bar)
       ClampToButtons(bar)
       ApplyAnchor(bar)
-      RefreshBarEditor()
+      ReAction:RefreshOptions()
     end
 
     -- edge drag handles
@@ -751,23 +754,23 @@
         end
 
         StoreExtents(bar)
-        RefreshBarEditor()
+        ReAction:RefreshOptions()
         updateDragTooltip()
       end
     )
 
     control:SetScript("OnEnter",
       function()
-        -- add bar type and status information to name
+        -- TODO: add bar type and status information to name
+        --[[
         local name = bar.name
         for _, m in ReAction:IterateModules() do
-          --[[
           local suffix = safecall(m,"GetBarNameModifier",bar)
           if suffix then
             name = ("%s %s"):format(name,suffix)
           end
-          --]]
         end
+        ]]--
 
         updateDragTooltip()
       end
@@ -825,7 +828,7 @@
           type = "execute",
           name = L["Settings..."],
           desc = L["Open the editor for this bar"],
-          func = function() CloseMenu(self.controlFrame); ReAction:CallModuleMethod("ConfigUI","LaunchBarEditor",self) end,
+          func = function() CloseMenu(self.controlFrame); ReAction:ShowEditor(self) end,
           disabled = InCombatLockdown,
           order = 1
         },
--- a/ReAction.lua	Tue May 13 16:42:52 2008 +0000
+++ b/ReAction.lua	Thu May 22 22:02:08 2008 +0000
@@ -1,13 +1,37 @@
--- ReAction.lua
--- See modules/ReAction_ModuleTemplate for Module API listing
--- See Bar.lua for Bar object listing
+--[[
+  ReAction.lua
+
+  The ReAction core manages 4 collections:
+    - modules (via AceAddon)
+    - bars
+    - options
+    - bar-type constructors
+    
+  and publishes events when those collections change. It also implements a single property, 'config mode',
+  and has a couple convenience methods which drill down to particular modules.
+  
+  Most of the "real work" of the addon happens in Bar.lua and the various modules.
+
+  Events (with handler arguments):
+  --------------------------------
+  "OnCreateBar" (bar, name)             : after a bar object is created
+  "OnDestroyBar" (bar, name)            : before a bar object is destroyed
+  "OnEraseBar" (bar, name)              : before a bar config is removed from the profile db
+  "OnRenameBar" (bar, oldname, newname) : after a bar is renamed
+  "OnRefreshBar" (bar, name)            : after a bar's state has been updated
+  "OnOptionsRefreshed" ()               : after the global options tree is refreshed
+  "OnConfigModeChanged" (mode)          : after the config mode is changed
+  "OnBarOptionGeneratorRegistered" (module, function) : after an options generator function is registered
+
+  ReAction is also an AceAddon-3.0 and contains an AceDB-3.0, which in turn publish more events.
+]]--
+local version = GetAddOnMetadata("ReAction","Version")
 
 ------ CORE ------
 local ReAction = LibStub("AceAddon-3.0"):NewAddon( "ReAction",
   "AceConsole-3.0",
   "AceEvent-3.0"
 )
-ReAction.version = GetAddOnMetadata("ReAction","Version")
 ReAction.revision = tonumber(("$Revision$"):match("%d+"))
 
 ------ GLOBALS ------
@@ -20,29 +44,78 @@
   dbprint = function(msg)
     DEFAULT_CHAT_FRAME:AddMessage(msg)
   end
-  --seterrorhandler(dbprint)
 else
   dbprint = function() end
 end
 ReAction.dbprint = dbprint
 
 ------ LIBRARIES ------
+local callbacks = LibStub("CallbackHandler-1.0"):New(ReAction)
 local L = LibStub("AceLocale-3.0"):GetLocale("ReAction")
 ReAction.L = L
 
 ------ PRIVATE ------
-local SelectBar, DestroyBar, InitializeBars, TearDownBars, DeepCopy, SafeCall, CheckMethod, SlashHandler
+local private = { }
+local bars = {}
+local defaultBarConfig = {}
+local barOptionGenerators = { }
+local options = {
+  type = "group",
+  name = "ReAction",
+  childGroups = "tab",
+  args = {
+    _desc = {
+      type = "description",
+      name = L["Customizable replacement for Blizzard's Action Bars"],
+      order = 1,
+    },
+    global = {
+      type = "group",
+      name = L["Global Settings"],
+      desc = L["Global configuration settings"],
+      args = { 
+        unlock = {
+          type     = "toggle",
+          name     = L["Unlock Bars"],
+          desc     = L["Unlock bars for dragging and resizing with the mouse"],
+          handler  = ReAction,
+          get      = "GetConfigMode",
+          set      = function(info, value) ReAction:SetConfigMode(value) end,
+          disabled = InCombatLockdown,
+          order    = 1
+        },
+      },
+      plugins = { },
+      order = 2,
+    },
+    module = {
+      type = "group",
+      childGroups = "select",
+      name = L["Module Settings"],
+      desc = L["Configuration settings for each module"],
+      args = { },
+      plugins = { },
+      order = 3,
+    },
+  },
+  plugins = { }
+}
+ReAction.options = options
+
+local SelectBar, DestroyBar, InitializeBars, TearDownBars, DeepCopy, CallModuleMethod, SlashHandler
 do
   local pcall = pcall
   local geterrorhandler = geterrorhandler
+  local self = ReAction
+  local inited = false
 
-  SelectBar = function(x)
+  function SelectBar(x)
     local bar, name
     if type(x) == "string" then
       name = x
-      bar = ReAction:GetBar(name)
+      bar = self:GetBar(name)
     else
-      for k,v in pairs(ReAction.bars) do
+      for k,v in pairs(bars) do
         if v == x then
           name = k
           bar = x
@@ -52,37 +125,40 @@
     return bar, name
   end
 
-  DestroyBar = function(x)
+  function DestroyBar(x)
     local bar, name = SelectBar(x)
-    if name and bar then
-      ReAction.bars[name] = nil
-      ReAction:CallMethodOnAllModules("RemoveFromBar", bar)
+    if bar and name then
+      bars[name] = nil
+      callbacks:Fire("OnDestroyBar", bar, name)
       bar:Destroy()
     end
   end
 
-  InitializeBars = function ()
-    if not(ReAction.inited) then
-      for name, config in pairs(ReAction.db.profile.bars) do
+  function InitializeBars()
+    if not inited then
+      for name, config in pairs(self.db.profile.bars) do
         if config then
-          ReAction:CreateBar(name, config)
+          self:CreateBar(name, config)
         end
       end
-      ReAction:CallMethodOnAllBars("ApplyAnchor") -- re-anchor in the case of oddball ordering
-      ReAction.inited = true
+      -- re-anchor in case anchor order does not match init order
+      for name, bar in pairs(bars) do
+        bar:ApplyAnchor()
+      end
+      inited = true
     end
   end
 
-  TearDownBars = function()
-    for name, bar in pairs(ReAction.bars) do
+  function TearDownBars()
+    for name, bar in pairs(bars) do
       if bar then
-        ReAction.bars[name] = DestroyBar(bar)
+        bars[name] = DestroyBar(bar)
       end
     end
-    ReAction.inited = false
+    inited = false
   end
 
-  DeepCopy = function(x)
+  function DeepCopy(x)
     if type(x) ~= "table" then
       return x
     end
@@ -93,39 +169,41 @@
     return r
   end
 
-  SafeCall = function(f, ...)
-    if f then
-      local success, err = pcall(f,...)
-      if not success then
-        geterrorhandler()(err)
+  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,...)
+      else
+        dbprint(("Bad call '%s' to %s module"):format(tostring(method),modulename));
+      end
+    else
+      self:Print(("Module '%s' not found"):format(tostring(modulename)))
+    end
   end
 
-  CheckMethod = function(m)
-    if type(m) == "function" then
-      return m
-    end
-    if type(m) ~= "string" then
-      error("Invalid method")
-    end
-  end
-
-  SlashHandler = function(option)
+  function SlashHandler(option)
     if option == "config" then
-      ReAction:ShowConfig()
+      self:ShowConfig()
     elseif option == "edit" then
-      ReAction:ShowEditor()
+      self:ShowEditor()
     elseif option == "unlock" then
-      ReAction:SetConfigMode(true)
+      self:SetConfigMode(true)
     elseif option == "lock" then
-      ReAction:SetConfigMode(false)
+      self:SetConfigMode(false)
     else
-      ReAction:Print(("%3.1f.%d"):format(ReAction.version,ReAction.revision))
-      ReAction:Print("/reaction config")
-      ReAction:Print("/reaction edit")
-      ReAction:Print("/reaction lock")
-      ReAction:Print("/reaction unlock")
+      self:Print(("%3.1f.%d"):format(version,self.revision))
+      self:Print("/rxn config")
+      self:Print("/rxn edit")
+      self:Print("/rxn lock")
+      self:Print("/rxn unlock")
     end
   end
 end
@@ -144,57 +222,12 @@
   )
   self.db.RegisterCallback(self,"OnProfileChanged")
   self.db.RegisterCallback(self,"OnProfileReset","OnProfileChanged")
-  self.callbacks = LibStub("CallbackHandler-1.0"):New(self)
+
+  options.args.profile = LibStub("AceDBOptions-3.0"):GetOptionsTable(self.db)
+
   self:RegisterChatCommand("reaction", SlashHandler)
   self:RegisterChatCommand("rxn", SlashHandler)
   self:RegisterEvent("PLAYER_REGEN_DISABLED")
-
-  self.bars = {}
-  self.defaultBarConfig = {}
-
-  self.options = {
-    type = "group",
-    name = "ReAction",
-    childGroups = "tab",
-    args = {
-      _desc = {
-        type = "description",
-        name = L["Customizable replacement for Blizzard's Action Bars"],
-        order = 1,
-      },
-      global = {
-        type = "group",
-        name = L["Global Settings"],
-        desc = L["Global configuration settings"],
-        args = { 
-          unlock = {
-            type     = "toggle",
-            handler  = module,
-            name     = L["Unlock Bars"],
-            desc     = L["Unlock bars for dragging and resizing with the mouse"],
-            get      = function() return self.configMode end,
-            set      = function(info, value) self:SetConfigMode(value) end,
-            disabled = InCombatLockdown,
-            order    = 1
-          },
-        },
-        plugins = { },
-        order = 2,
-      },
-      module = {
-        type = "group",
-        childGroups = "select",
-        name = L["Module Settings"],
-        desc = L["Configuration settings for each module"],
-        args = { },
-        plugins = { },
-        order = 3,
-      },
-      profile = LibStub("AceDBOptions-3.0"):GetOptionsTable(self.db)
-    },
-    plugins = { }
-  }
-
 end
 
 function ReAction:OnEnable()
@@ -210,29 +243,9 @@
   InitializeBars()
 end
 
-function ReAction:OnModuleEnable(module)
-  if module.ApplyToBar then
-    for _, b in pairs(bars) do
-      if b then
-        module:ApplyToBar(b)
-      end
-    end
-  end
-end
-
-function ReAction:OnModuleDisable(module)
-  if module.RemoveFromBar then
-    for _, b in pairs(bars) do
-      if b then
-        module:RemoveFromBar(b)
-      end
-    end
-  end
-end
-
 function ReAction:PLAYER_REGEN_DISABLED()
-  if self.configMode == true then
-    UIErrorsFrame:AddMessage(L["ReAction config mode disabled during combat."])
+  if private.configMode == true then
+    self:UserError(L["ReAction config mode disabled during combat."])
     self:SetConfigMode(false)
   end
 end
@@ -245,45 +258,9 @@
   UIErrorsFrame:AddMessage(msg)
 end
 
-function ReAction:CallMethodOnAllModules(method, ...)
-  local m = CheckMethod(method)
-  for _, x in self:IterateModules() do
-    if x then
-      SafeCall(m or x[method], x, ...)
-    end
-  end
-end
-
-function ReAction:CallMethodOnAllBars(method,...)
-  local m = CheckMethod(method)
-  for _, x in pairs(self.bars) do
-    if x then
-      SafeCall(m or x[method], x, ...)
-    end
-  end
-end
-
-function ReAction: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,...)
-    else
-      dbprint(("Bad call '%s' to %s module"):format(tostring(method),modulename));
-    end
-  else
-    self:Print(("Module '%s' not found"):format(tostring(modulename)))
-  end
-end
-
-
+-- usage:
+--  (1) ReAction:CreateBar(name, cfgTable)
+--  (2) ReAction:CreateBar(name, "barType", [nRows], [nCols], [btnSize], [btnSpacing])
 function ReAction:CreateBar(name, ...)
   local config = select(1,...)
   if config and type(config) ~= "table" then
@@ -291,7 +268,7 @@
     if type(bartype) ~= "string" then
       error("ReAction:CreateBar() - first argument must be a config table or a default config type string")
     end
-    config = self.defaultBarConfig[bartype]
+    config = defaultBarConfig[bartype]
     if not config then
       error(("ReAction:CreateBar() - unknown bar type '%s'"):format(bartype))
     end
@@ -317,14 +294,13 @@
     repeat
       name = prefix..i
       i = i + 1
-    until self.bars[name] == nil
+    until bars[name] == nil
   end
   profile.bars[name] = profile.bars[name] or config
   local bar = self.Bar:new( name, profile.bars[name] )  -- ReAction.Bar defined in Bar.lua
-  self:CallMethodOnAllModules("ApplyToBar", bar)
-  self.bars[name] = bar
-  self.callbacks:Fire("OnCreateBar", bar)
-  if self.configMode then
+  bars[name] = bar
+  callbacks:Fire("OnCreateBar", bar, name)
+  if private.configMode then
     bar:ShowControls(true)
   end
 
@@ -333,69 +309,150 @@
 
 function ReAction:EraseBar(x)
   local bar, name = SelectBar(x)
-  if name and bar then
+  if bar and name then
+    callbacks:Fire("OnEraseBar", bar, name)
     DestroyBar(bar)
     self.db.profile.bars[name] = nil
-    self:CallMethodOnAllModules("EraseBarConfig", name)
-    self.callbacks:Fire("OnEraseBar",name)
   end
 end
 
 function ReAction:GetBar(name)
-  return self.bars[name]
+  return bars[name]
+end
+
+function ReAction:IterateBars()
+  return pairs(bars)
 end
 
 function ReAction:RenameBar(x, newname)
   local bar, name = SelectBar(x)
-  if bar and name and newname then
-    if self.bars[newname] then
-      UIErrorsFrame:AddMessage(("%s ('%s')"):format(L["ReAction: name already in use"],newname))
+  if type(newname) ~= "string" then
+    error("ReAction:RenameBar() - second argument must be a string")
+  end
+  if bar and name and #newname > 0 then
+    if bars[newname] then
+      self:UserError(("%s ('%s')"):format(L["ReAction: name already in use"],newname))
     else
-      self.bars[newname] = self.bars[name]
-      self.bars[name] = nil
+      bars[newname], bars[name] = bars[name], nil
       bar:SetName(newname or "")
       local cfg = self.db.profile.bars
       cfg[newname], cfg[name] = cfg[name], nil
-      self:CallMethodOnAllModules("RenameBarConfig", name, newname)
-      self.callbacks:Fire("OnRenameBar", name, newname)
+      callbacks:Fire("OnRenameBar", bar, name, newname)
     end
   end
 end
 
+function ReAction:RefreshBar(x)
+  local bar, name = SelectBar(x)
+  if bar and name then
+    callbacks:Fire("OnRefreshBar", bar, name)
+  end
+end
+
 function ReAction:RegisterBarType( name, config, isDefaultChoice )
-  self.defaultBarConfig[name] = config
+  defaultBarConfig[name] = config
   if isDefaultChoice then
-    self.defaultBarConfigChoice = name
+    defaultBarConfigChoice = name
   end
   self:RefreshOptions()
 end
 
 function ReAction:UnregisterBarType( name )
-  self.defaultBarConfig[name] = nil
-  if self.defaultBarConfigChoice == name then
-    self.defaultBarConfigChoice = nil
+  defaultBarConfig[name] = nil
+  if private.defaultBarConfigChoice == name then
+    private.defaultBarConfigChoice = nil
   end
   self:RefreshOptions()
 end
 
-function ReAction:RegisterOptions(module, options, global)
-  self.options.args[global and "global" or "module"].plugins[module:GetName()] = options
+function ReAction:IterateBarTypes()
+  return pairs(defaultBarConfig)
+end
+
+function ReAction:GetBarTypeConfig(name)
+  if name then
+    return defaultBarConfig[name]
+  end
+end
+
+function ReAction:GetBarTypeOptions( fill )
+  fill = fill or { }
+  for k in self:IterateBarTypes() do
+    fill[k] = k
+  end
+  return fill
+end
+
+function ReAction:GetDefaultBarType()
+  return private.defaultBarConfigChoice
+end
+
+function ReAction:RegisterOptions(module, opts, global)
+  options.args[global and "global" or "module"].plugins[module:GetName()] = opts
+  self:RefreshOptions()
 end
 
 function ReAction:RefreshOptions()
-  self.callbacks:Fire("OnOptionsRefreshed")
+  callbacks:Fire("OnOptionsRefreshed")
+end
+
+-- 
+-- In addition to global and general module options, options tables 
+-- must be generated dynamically for each bar.
+--
+-- 'func' should be a function or a method string.
+-- The function or method will be passed the bar as its parameter.
+-- (methods will of course get the module as the first 'self' parameter)
+-- 
+-- A generator can be unregistered by passing a nil func.
+--
+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
+  barOptionGenerators[module] = func
+  callbacks:Fire("OnBarOptionGeneratorRegistered", module, func)
+end
+
+-- builds a table suitable for use as an AceConfig3 group 'plugins' sub-table
+function ReAction:GenerateBarOptionsTable( bar )
+  local opts = { }
+  for module, func in pairs(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
+      opts[module:GetName()] = { [module:GetName()] = r }
+    else
+      geterrorhandler()(r)
+    end
+  end
+  return opts
 end
 
 function ReAction:SetConfigMode( mode )
-  self:CallMethodOnAllBars("ShowControls",mode)
-  self:CallMethodOnAllModules("ApplyConfigMode",mode,self.bars)
-  self.configMode = mode
+  private.configMode = mode
+  callbacks:Fire("OnConfigModeChanged", mode)
+end
+
+function ReAction:GetConfigMode()
+  return private.configMode
 end
 
 function ReAction:ShowConfig()
-  self:CallModuleMethod("ConfigUI","OpenConfig")
+  CallModuleMethod("ConfigUI","OpenConfig")
 end
 
-function ReAction:ShowEditor()
-  self:CallModuleMethod("ConfigUI","LaunchBarEditor")
+function ReAction:ShowEditor(bar)
+  CallModuleMethod("ConfigUI","LaunchBarEditor",bar)
 end
--- a/modules/ReAction_Action/ReAction_Action.lua	Tue May 13 16:42:52 2008 +0000
+++ b/modules/ReAction_Action/ReAction_Action.lua	Thu May 22 22:02:08 2008 +0000
@@ -27,7 +27,6 @@
     }
   )
   self.buttons = { }
-  self.options = setmetatable({},{__mode="k"})
 
   ReAction:RegisterOptions(self, {
       [moduleID] = {
@@ -43,6 +42,16 @@
         }
       }
     })
+
+  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()
@@ -60,22 +69,17 @@
   ReAction:UnregisterBarType(L["Action Bar"])
 end
 
-function module:ApplyToBar(bar)
-  self:RefreshBar(bar)
-end
-
-function module:RefreshBar(bar)
+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
-    local barName = bar:GetName()
-    if profile.buttons[barName] == nil then
-      profile.buttons[barName] = {}
+    if profile.buttons[name] == nil then
+      profile.buttons[name] = {}
     end
-    local btnCfg = profile.buttons[barName]
+    local btnCfg = profile.buttons[name]
 
     local r, c = bar:GetButtonGrid()
     local n = r*c
@@ -103,7 +107,7 @@
   end
 end
 
-function module:RemoveFromBar(bar)
+function module:OnDestroyBar(event, bar, name)
   if self.buttons[bar] then
     local btns = self.buttons[bar]
     for _,b in pairs(btns) do
@@ -115,11 +119,11 @@
   end
 end
 
-function module:EraseBarConfig(barName)
-  self.db.profile.buttons[barName] = nil
+function module:OnEraseBar(event, bar, name)
+  self.db.profile.buttons[name] = nil
 end
 
-function module:RenameBarConfig(oldname, newname)
+function module:OnRenameBar(event, bar, oldname, newname)
   local b = self.db.profile.buttons
   b[newname], b[oldname] = b[oldname], nil
 end
@@ -139,8 +143,8 @@
   end
 end
 
-function module:ApplyConfigMode(mode,bars)
-  for _, bar in pairs(bars) do
+function module:OnConfigModeChanged(event, mode)
+  for _, bar in ReAction:IterateBars() do
     if bar and self.buttons[bar] then
       for _, b in pairs(self.buttons[bar]) do
         if b then
@@ -179,16 +183,13 @@
 
 ---- Options ----
 function module:GetBarOptions(bar)
-  if not self.options[bar] then
-    self.options[bar] = {
-      type = "group",
-      name = L["Action Buttons"],
-      hidden = function() return bar.config.type ~= moduleID end,
-      args = {
-      }
+  return {
+    type = "group",
+    name = L["Action Buttons"],
+    hidden = function() return bar.config.type ~= moduleID end,
+    args = {
     }
-  end
-  return self.options[bar]
+  }
 end
 
 
--- a/modules/ReAction_ConfigUI/ReAction_ConfigUI.lua	Tue May 13 16:42:52 2008 +0000
+++ b/modules/ReAction_ConfigUI/ReAction_ConfigUI.lua	Thu May 22 22:02:08 2008 +0000
@@ -85,7 +85,7 @@
   self.frame.obj:SetCallback("default", 
     function() 
       ReAction.db:ResetProfile()
-      self:OnOptionsRefreshed()
+      ReAction:RefreshOptions()
     end )
 end
 
@@ -96,7 +96,7 @@
 local function NewEditor()
   -- private variables
   local editorName = "ReAction-Editor"
-  local barOptMap = { }
+  local barOptMap = setmetatable({},{__mode="v"})
   local tmp = { }
   local pointTable = {
     CENTER      = L["Center"], 
@@ -126,7 +126,7 @@
         editor.closePending = false
       end
       if editor.selfClosePending then
-        ed:Hide()
+        editor:Hide()
         AceConfigReg:NotifyChange(configName)
         editor.selfClosePending = false
       end
@@ -193,9 +193,9 @@
           type = {
             type = "select",
             name = L["Button Type"],
-            get  = function() return tmp.barType or ReAction.defaultBarConfigChoice or "" end,
+            get  = function() return tmp.barType or ReAction:GetDefaultBarType() or "" end,
             set  = function(info, val) 
-                     local c = ReAction.defaultBarConfig[val]
+                     local c = ReAction:GetBarTypeConfig(val)
                      tmp.barType = val 
                      tmp.barSize = c.defaultButtonSize or tmp.barSize
                      tmp.barRows = c.defaultBarRows or tmp.barRows
@@ -403,12 +403,10 @@
   end
 
   function editor:RefreshBarTree(bar)
-    local opts = options.args[barOptMap[bar:GetName()]]
-    opts.plugins = { }
-    for name, module in ReAction:IterateModules() do
-      if module.GetBarOptions then
-        opts.plugins[module:GetName()] = { [module:GetName()] = module:GetBarOptions(bar) }
-      end
+    local key = barOptMap[bar:GetName()]
+    if key and options.args[key] then
+      options.args[key].plugins = ReAction:GenerateBarOptionsTable(bar)
+      AceConfigReg:NotifyChange(editorName)
     end
   end
 
@@ -416,6 +414,14 @@
     self:CreateBarTree(bar)
   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
@@ -434,29 +440,37 @@
     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
-    for k in pairs(ReAction.defaultBarConfig) do
-      _scratch[k] = k
-    end
-    return _scratch
+    return ReAction:GetBarTypeOptions(_scratch)
   end
 
   function editor:CreateBar()
     if tmp.barName and tmp.barName ~= "" then
-      ReAction:CreateBar(tmp.barName, tmp.barType or ReAction.defaultBarConfigChoice, tmp.barRows, tmp.barCols, tmp.barSize, tmp.barSpacing)
+      ReAction:CreateBar(tmp.barName, tmp.barType or ReAction:GetDefaultBarType(), tmp.barRows, tmp.barCols, tmp.barSize, tmp.barSpacing)
       tmp.barName = nil
     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 pairs(ReAction.bars) do
+  for name, bar in ReAction:IterateBars() do
     editor:CreateBarTree(bar)
   end
 
@@ -471,6 +485,7 @@
     if not self.editor then
       self.editor = NewEditor()
     end
+    -- TODO: figure out how to open to a particular bar: currently AceConfigDialogue doesn't support this
     self.editor:Open()
     ReAction:SetConfigMode(true)
   end
--- a/modules/ReAction_ModuleTemplate/ReAction_ModuleName.lua	Tue May 13 16:42:52 2008 +0000
+++ b/modules/ReAction_ModuleTemplate/ReAction_ModuleName.lua	Thu May 22 22:02:08 2008 +0000
@@ -14,7 +14,7 @@
   -- mixins go here
 )
 
--- module methods
+-- handlers
 function module:OnInitialize()
   self.db = ReAction.db:RegisterNamespace( moduleID
     {
@@ -24,7 +24,13 @@
     }
   )
 
-  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")
+  ReAction.RegisterCallback(self, "OnConfigModeChanged")
 end
 
 function module:OnEnable()
@@ -35,40 +41,33 @@
 
 end
 
-
----- ReAction module API ----
-
 -- apply module features and settings to a bar object (see Bar.lua for Bar API)
-function module:ApplyToBar(bar)
+function module:OnCreateBar(event, bar, name)
 
 end
 
 -- remove module features and settings from a bar object
-function module:RemoveFromBar(bar)
+function module:OnDestroyBar(event, bar, name)
 
 end
 
 -- refresh module features and settings on a bar object
-function module:RefreshBar(bar)
+function module:OnRefreshBar(event, bar, name)
 
 end
 
--- notification of config mode (true/false) on the list of bars
-function module:ApplyConfigMode(mode,listOfBars)
+-- erase any local configuration entries for the supplied bar name
+function module:OnEraseBar(event, bar, name)
 
 end
 
--- return a name-modifier (suffix) for the bar name display. This can reflect a dynamic state.
-function module:GetBarNameModifier(bar)
-  return nil
-end
-
--- erase any local configuration entries for the supplied bar name
-function module:EraseBarConfig(barName)
+-- update any local configuration/option entries with the new bar name index
+function module:OnRenameBar(event, bar, oldName, newName)
 
 end
 
--- update any local configuration entries with the new bar name index
-function module:RenameBarConfig(oldName, newName)
+-- update any local display/options based on config mode (true/false)
+function module:OnConfigModeChanged(event, mode)
 
 end
+
--- a/modules/ReAction_PetAction/ReAction_PetAction.lua	Tue May 13 16:42:52 2008 +0000
+++ b/modules/ReAction_PetAction/ReAction_PetAction.lua	Thu May 22 22:02:08 2008 +0000
@@ -27,7 +27,15 @@
     }
   )
   self.buttons = { }
-  self.options = setmetatable({},{__mode="k"})
+
+  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")
 end
 
 function module:OnEnable()
@@ -45,27 +53,26 @@
   ReAction:UnregisterBarType(L["Pet Action Bar"])
 end
 
-function module:ApplyToBar(bar)
+function module:OnCreateBar(event, bar, name)
   if bar.config.type == moduleID then
     -- auto show/hide when pet exists
     bar:GetFrame():SetAttribute("unit","pet")
     RegisterUnitWatch(bar:GetFrame())
-    self:RefreshBar(bar)
+    self:OnRefreshBar(event, bar, name)
   end
 end
 
-function module:RefreshBar(bar)
+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
-    local barName = bar:GetName()
-    if profile.buttons[barName] == nil then
-      profile.buttons[barName] = {}
+    if profile.buttons[name] == nil then
+      profile.buttons[name] = {}
     end
-    local btnCfg = profile.buttons[barName]
+    local btnCfg = profile.buttons[name]
 
     local r, c = bar:GetButtonGrid()
     local n = r*c
@@ -93,7 +100,7 @@
   end
 end
 
-function module:RemoveFromBar(bar)
+function module:OnDestroyBar(event, bar, name)
   if self.buttons[bar] then
     local btns = self.buttons[bar]
     for _,b in pairs(btns) do
@@ -105,18 +112,18 @@
   end
 end
 
-function module:EraseBarConfig(barName)
-  self.db.profile.buttons[barName] = nil
+function module:OnEraseBar(event, bar, name)
+  self.db.profile.buttons[name] = nil
 end
 
-function module:RenameBarConfig(oldname, newname)
+function module:OnRenameBar(event, bar, oldname, newname)
   local b = self.db.profile.buttons
   b[newname], b[oldname] = b[oldname], nil
 end
 
 
-function module:ApplyConfigMode(mode,bars)
-  for _, bar in pairs(bars) do
+function module:OnConfigModeChanged(event, mode)
+  for _, bar in ReAction:IterateBars() do
     if bar and self.buttons[bar] then
       for _, b in pairs(self.buttons[bar]) do
         if b then
@@ -160,16 +167,13 @@
 
 ---- Options ----
 function module:GetBarOptions(bar)
-  if not self.options[bar] then
-    self.options[bar] = {
-      type = "group",
-      name = L["Pet Buttons"],
-      hidden = function() return bar.config.type ~= moduleID end,
-      args = {
-      }
+  return {
+    type = "group",
+    name = L["Pet Buttons"],
+    hidden = function() return bar.config.type ~= moduleID end,
+    args = {
     }
-  end
-  return self.options[bar]
+  }
 end
 
 
--- a/modules/ReAction_PossessBar/ReAction_PossessBar.lua	Tue May 13 16:42:52 2008 +0000
+++ b/modules/ReAction_PossessBar/ReAction_PossessBar.lua	Thu May 22 22:02:08 2008 +0000
@@ -26,7 +26,6 @@
     }
   )
   self.buttons = { }
-  self.options = setmetatable({},{__mode="k"})
 
   ReAction:RegisterOptions(self, {
       [moduleID] = {
@@ -42,6 +41,15 @@
         }
       }
     })
+
+  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")
 end
 
 function module:OnEnable()
@@ -59,27 +67,26 @@
   ReAction:UnregisterBarType(L["Possess Bar"])
 end
 
-function module:ApplyToBar(bar)
+function module:OnCreateBar(event, bar, name)
   if bar.config.type == moduleID then
     bar:GetFrame():SetParent(PossessBarFrame)
     bar.config.parent = "PossessBarFrame"
     self:CreatePossessControlButtons(bar)
   end
-  self:RefreshBar(bar)
+  self:OnRefreshBar(event, bar, name)
 end
 
-function module:RefreshBar(bar)
+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
-    local barName = bar:GetName()
-    if profile.buttons[barName] == nil then
-      profile.buttons[barName] = {}
+    if profile.buttons[name] == nil then
+      profile.buttons[name] = {}
     end
-    local btnCfg = profile.buttons[barName]
+    local btnCfg = profile.buttons[name]
 
     local r, c = bar:GetButtonGrid()
     local n = r*c
@@ -107,7 +114,7 @@
   end
 end
 
-function module:RemoveFromBar(bar)
+function module:OnDestroyBar(event, bar, name)
   if self.buttons[bar] then
     local btns = self.buttons[bar]
     for _,b in pairs(btns) do
@@ -119,11 +126,11 @@
   end
 end
 
-function module:EraseBarConfig(barName)
-  self.db.profile.buttons[barName] = nil
+function module:OnEraseBar(event, bar, name)
+  self.db.profile.buttons[name] = nil
 end
 
-function module:RenameBarConfig(oldname, newname)
+function module:OnRenameBar(event, bar, oldname, newname)
   local b = self.db.profile.buttons
   b[newname], b[oldname] = b[oldname], nil
 end
@@ -143,8 +150,8 @@
   end
 end
 
-function module:ApplyConfigMode(mode,bars)
-  for _, bar in pairs(bars) do
+function module:OnConfigModeChanged(event, mode)
+  for _, bar in ReAction:IterateBars() do
     if bar and self.buttons[bar] then
       for _, b in pairs(self.buttons[bar]) do
         if b then
@@ -257,16 +264,13 @@
 
 ---- Options ----
 function module:GetBarOptions(bar)
-  if not self.options[bar] then
-    self.options[bar] = {
-      type = "group",
-      name = L["Possess Buttons"],
-      hidden = function() return bar.config.type ~= moduleID end,
-      args = {
-      }
+  return {
+    type = "group",
+    name = L["Possess Buttons"],
+    hidden = function() return bar.config.type ~= moduleID end,
+    args = {
     }
-  end
-  return self.options[bar]
+  }
 end