changeset 75:06cd74bdc7da

- Cleaned up Bar interface - Move all attribute setting from Bar into State - Separated Moonkin and Tree of Life - Removed PossessBar module - Added some infrastructure for paged/mind control support to Action
author Flick <flickerstreak@gmail.com>
date Mon, 16 Jun 2008 18:46:08 +0000
parents 00e28094e1a3
children c8c8610fd864
files Bar.lua Overlay.lua ReAction.lua State.lua locale/enUS.lua modules/ReAction_Action/ReAction_Action.lua modules/ReAction_PetAction/ReAction_PetAction.lua modules/ReAction_PossessBar/ReAction_PossessBar.lua modules/ReAction_PossessBar/ReAction_PossessBar.toc modules/ReAction_PossessBar/ReAction_PossessBar.xml modules/modules.xml
diffstat 11 files changed, 462 insertions(+), 758 deletions(-) [+]
line wrap: on
line diff
--- a/Bar.lua	Tue Jun 10 22:25:15 2008 +0000
+++ b/Bar.lua	Mon Jun 16 18:46:08 2008 +0000
@@ -3,84 +3,91 @@
 local _G = _G
 local CreateFrame = CreateFrame
 local floor = math.floor
+local fmod = math.fmod
+local format = string.format
 local SecureStateHeader_Refresh = SecureStateHeader_Refresh
 
 
-
 -- update ReAction revision if this file is newer
 local revision = tonumber(("$Revision$"):match("%d+"))
 if revision > ReAction.revision then
   ReAction.revision = revision
 end
 
+
 ------ BAR CLASS ------
 local Bar = { _classID = {} }
 
 local function Constructor( self, name, config )
-  self.name, self.config = name, config
-  self.buttons = setmetatable({},{__mode="k"})
-
   if type(config) ~= "table" then
     error("ReAction.Bar: config table required")
   end
+  config.width = config.width or 480
+  config.height = config.height or 40
+
+  self.name, self.config = name, config
+  self.buttons = setmetatable({},{__mode="k"})
+  self.statedrivers = { }
+  self.keybinds = { }
 
   local parent = config.parent and (ReAction:GetBar(config.parent) or _G[config.parent]) or UIParent
-  local f = CreateFrame("Frame",nil,parent,"SecureStateHeaderTemplate")
+  local f = CreateFrame("Button",name and format("ReAction-%s",name),parent,"SecureStateHeaderTemplate, SecureActionButtonTemplate")
+
+  -- The frame itself is read-only
+  function self:GetFrame()
+    return f
+  end
+
+  -- The bar itself is also a Button derived from SecureActionButtonTemplate, so it has an OnClick handler
+  -- which we can use as a virtual button for keybinds, which will send attribute-value changes to itself.
+  -- However, we don't ever want the user to be able to click it directly.
+  f:EnableMouse(false)
+  f:SetAttribute("type","attribute")
   f:SetFrameStrata("MEDIUM")
-  config.width = config.width or 480
-  config.height = config.height or 40
   f:SetWidth(config.width)
   f:SetWidth(config.height)
+  f:Show()
 
+  self:ApplyAnchor()
   ReAction.RegisterCallback(self, "OnConfigModeChanged")
-
-  self.frame = f
-  self:ApplyAnchor()
-  f:Show()
-  self:RefreshLayout()
 end
 
 function Bar:Destroy()
-  local f = self.frame
+  local f = self:GetFrame()
   f:UnregisterAllEvents()
   f:Hide()
   f:SetParent(UIParent)
   f:ClearAllPoints()
   ReAction.UnregisterAllCallbacks(self)
-  if self.statedriver then
-    UnregisterStateDriver(f, "reaction")
+  for driver in pairs(self.statedrivers) do
+    UnregisterStateDriver(f, driver)
   end
   self.labelString = nil
   self.controlFrame = nil
-  self.frame = nil
   self.config = nil
 end
 
 function Bar:OnConfigModeChanged(event, mode)
-  self:ShowControls(mode) -- ShowControls() defined in Overlay.lua
-end
-
-function Bar:RefreshLayout()
-  ReAction:RefreshBar(self)
+  self:ShowControls(mode) -- Bar:ShowControls() defined in Overlay.lua
 end
 
 function Bar:ApplyAnchor()
-  local f, config = self.frame, self.config
+  local f, config = self:GetFrame(), self.config
   f:SetWidth(config.width)
   f:SetHeight(config.height)
-  local anchor = config.anchor
+  local point  = config.point
   f:ClearAllPoints()
-  if anchor then
-    local anchorTo = f:GetParent()
-    if config.anchorTo then
-      local bar = ReAction:GetBar(config.anchorTo)
+  if point then
+    local anchor = f:GetParent()
+    if config.anchor then
+      local bar = ReAction:GetBar(config.anchor)
       if bar then
-        anchorTo = bar:GetFrame()
+        anchor = bar:GetFrame()
       else
-        anchorTo = _G[config.anchorTo]
+        anchor = _G[config.anchor]
       end
     end
-    f:SetPoint(anchor, anchorTo or f:GetParent(), config.relativePoint, config.x or 0, config.y or 0)
+    f:SetPoint(point, anchor or f:GetParent(), config.relpoint, config.x or 0, config.y or 0)
   else
     f:SetPoint("CENTER")
   end
@@ -88,9 +95,9 @@
 
 function Bar:SetAnchor(point, frame, relativePoint, x, y)
   local c = self.config
-  c.anchor = point or c.anchor
-  c.anchorTo = frame and frame:GetName() or c.anchorTo
-  c.relativePoint = relativePoint or c.relativePoint
+  c.point = point or c.point
+  c.anchor = frame and frame:GetName() or c.anchor
+  c.relpoint = relativePoint or c.relpoint
   c.x = x or c.x
   c.y = y or c.y
   self:ApplyAnchor()
@@ -98,20 +105,20 @@
 
 function Bar:GetAnchor()
   local c = self.config
-  return (c.anchor or "CENTER"), (c.anchorTo or self.frame:GetParent():GetName()), (c.relativePoint or c.anchor or "CENTER"), (c.x or 0), (c.y or 0)
-end
-
-function Bar:GetFrame()
-  return self.frame
+  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()
-  return self.frame:GetWidth() or 200, self.frame:GetHeight() or 200
+  local f = self:GetFrame()
+  return f:GetWidth(), f:GetHeight()
 end
 
 function Bar:SetSize(w,h)
   self.config.width = w
   self.config.height = h
+  local f = self:GetFrame()
+  f:SetWidth(w)
+  f:SetHeight(h)
 end
 
 function Bar:GetButtonSize()
@@ -126,6 +133,7 @@
     self.config.btnWidth = w
     self.config.btnHeight = h
   end
+  ReAction:RefreshBar(self)
 end
 
 function Bar:GetButtonGrid()
@@ -143,188 +151,128 @@
     cfg.btnColumns = c
     cfg.spacing = s
   end
+  ReAction:RefreshBar(self)
 end
 
 function Bar:GetName()
   return self.name
 end
 
+-- only ReAction:RenameBar() should call this function
 function Bar:SetName(name)
   self.name = name
+  -- controlLabelString is defined in Overlay.lua
   if self.controlLabelString then
     self.controlLabelString:SetText(self.name)
   end
 end
 
-function Bar:PlaceButton(f, idx, baseW, baseH)
+function Bar:AddButton(idx, button)
+  self.buttons[button] = idx
+  SecureStateHeader_Refresh(self:GetFrame())
+end
+
+function Bar:RemoveButton(button)
+  self.buttons[button] = nil
+end
+
+function Bar:IterateButtons() -- iterator returns button, idx
+  return pairs(self.buttons)
+end
+
+function Bar:PlaceButton(button, baseW, baseH)
+  local idx = self.buttons[button]
+  if not idx then return end
   local r, c, s = self:GetButtonGrid()
   local bh, bw = self:GetButtonSize()
-  local row, col = floor((idx-1)/c), mod((idx-1),c) -- zero-based
+  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 f = button:GetFrame()
 
   f:ClearAllPoints()
   f:SetPoint("TOPLEFT",x/scale,-y/scale)
   f:SetScale(scale)
-  self.buttons[f] = true
 end
 
+-- Creates (or updates) a named binding which binds a key press to a call to SetAttribute()
+-- pass a nil key to unbind
+function Bar:SetAttributeBinding( name, key, attribute, value )
+  if not name then
+    error("usage - Bar:SetAttributeBinding(name [, key, attribute, value]")
+  end
+  local f = self:GetFrame()
 
--- multi-state functions --
-function Bar:GetNumPages()
-  return self.config.nPages or 1
+  -- clear the old binding, if any
+  if self.keybinds[name] then
+    SetOverrideBinding(f, false, self.keybinds[name], nil)
+  end
+  if key then
+    f:SetAttribute(format("attribute-name-%s",name), attribute)
+    f:SetAttribute(format("attribute-value-%s",name), value)
+    SetOverrideBindingClick(f, false, key, f:GetName(), name) -- binding name is the virtual mouse button
+  end
+  self.keybinds[name] = key
 end
 
-  --
-  -- 'rule' is a rule-string to pass to RegisterStateDriver
-  -- 'states' is a { ["statename"] = <don't care> } table of all state names
-  -- 'keybinds' is a { ["statename"] = keybind } table of all keybound states
-  --
-function Bar:SetStateDriver( rule, states, keybinds )
-  local f = self.frame
-  local kbprefix = ""
-  do
-    local tmp = { }
-    for s, k in pairs(keybinds) do
-      if k and #k > 0 then -- filter out false table entries
-        -- if in a keybound state, set the stack to the new state but stay in the keybound state.
-        -- use $s as a placeholder for the current state, it will be gsub()'d in later
-        table.insert(tmp,("%s:$s set() %s"):format(s,s))
+-- Sets up a state driver 'name' for the bar, using the provided 'rule'
+-- Also sets attributes 'statemap-<name>-<key>'=<value> for each entry in the passed map
+-- if 'rule' is nil or an empty string, the driver is unregistered.
+function Bar:SetStateDriver( name, rule, map )
+  local f = self:GetFrame()
+  if rule and #rule > 0 then
+    if map then
+      for key, value in pairs(map) do
+        f:SetAttribute( format("statemap-%s-%s",name,key), value )
       end
     end
-    table.insert(tmp,kbprefix)  -- to get a trailing ';' if the table is not empty
-    kbprefix = table.concat(tmp,";")
-  end
-  for state in pairs(states) do
-    -- For all states: if in a keybound state, stay there (with stack manipulation, see above).
-    -- Otherwise, go to the state
-    f:SetAttribute(("statemap-reaction-%s"):format(state),("%s%s"):format(kbprefix:gsub("%$s",state),state))
-
-    local binding = keybinds[state]
-    self:SetStateKeybind(binding, state) -- set the binding even if nil, to clear it unconditionally
-    if binding then
-      -- for key bindings, use the state-stack to toggle between the last state and the keybound state
-      -- use a different 'virtual state' passed to attribute 'reaction-state' for key bindings, "<state>_binding"
-      f:SetAttribute(("statemap-reaction-%s_binding"):format(state), ("%s:pop();*:set(%s)"):format(state,state))
-    end
-  end
-
-  if rule and #rule > 0 then
-    self.stateDriver = true
-    RegisterStateDriver(f, "reaction", rule)
-  elseif self.statedriver then
-    self.statedriver = false
-    UnregisterStateDriver(f, "reaction")
+    RegisterStateDriver(f, name, rule)
+    self.statedrivers[name] = true
+  elseif self.statedrivers[name] then
+    UnregisterStateDriver(f, name)
+    self.statedrivers[name] = nil
   end
 end
 
-function Bar:SetHideStates(s)
-  for f in pairs(self.buttons) do
-    if f:GetParent() == self.frame then
-      f:SetAttribute("hidestates",s)
+-- Set an attribute on the frame (or its buttons if 'doButtons' = true)
+-- Either or both 'map' and 'default' can be passed:
+--   - If 'map' is omitted, then 'default' is set to the attribute.
+--   - If 'map' is provided, then it is interpreted as an unordered
+--     table of the form { ["statename"] = ["value"] }, and will be
+--     converted into a SecureStateHeaderTemplate style state-parsed
+--     string, e.g. "<state1>:<value1>;<state2>:<value2>". If 'default'
+--     is also provided, then its value will be converted to a string
+--     and appended.
+function Bar:SetStateAttribute( attribute, map, default, doButtons )
+  local value = default
+  if map then
+    local tmp = { }
+    for state, value in pairs(map) do
+      table.insert(tmp, format("%s:%s",tostring(state),tostring(value)))
     end
+    if default then
+      table.insert(tmp, tostring(default))
+    end
+    value = table.concat(tmp,";")
   end
-  SecureStateHeader_Refresh(self.frame)
-end
-
-function Bar:SetStateKeybind(key, state, defaultstate)
-  -- Lazily create a tiny offscreen button which sends "<state>_binding" values to the
-  -- bar frame's state-reaction attribute, by using an override binding to generate a 
-  -- click on the button with a virtual mouse button "state". 
-  -- This gets around making the bar itself a clickable button, which is not desirable
-  local f = self.statebuttonframe
-  if key then
-    if not f then
-      f = CreateFrame("Button",self:GetName().."_statebutton",self.frame,"SecureActionButtonTemplate")
-      f:SetPoint("BOTTOMRIGHT",UIParent,"TOPLEFT")
-      f:SetWidth(1)
-      f:SetHeight(1)
-      f:SetAttribute("type*","attribute")
-      f:SetAttribute("attribute-name*","state-reaction")
-      f:SetAttribute("attribute-frame*",self.frame)
-      f:Show()
-      f.bindings = { }
-      self.statebuttonframe = f
-    end
-    f:SetAttribute(("attribute-value-%s"):format(state),("%s_binding"):format(state))
-      -- clear the old binding, if any, for this state
-    if f.bindings[state] then
-      SetOverrideBinding(self.frame, false, f.bindings[state], nil)
-    end
-    SetOverrideBindingClick(self.frame, false, key, f:GetName(), state) -- the state name is used as the virtual button
-    f.bindings[state] = key
-  elseif f then
-    key = f.bindings[state]
-    if key then
-      SetOverrideBinding(self.frame, false, key, nil)
-      f.bindings[state] = nil
-    end
-  end
-end
-
-function Bar:SetStatePageMap(state, map)  -- map is a { ["statename"] = pagenumber } table
-  local f = self.frame
-  local tmp = { }
-  for s, p in pairs(map) do
-    table.insert(tmp, ("%s:page%d"):format(s,p))
-  end
-  local spec = table.concat(tmp,";")
-  local current = f:GetAttribute("statebutton")
-  if spec ~= f:GetAttribute("statebutton") then
-    f:SetAttribute("statebutton", spec)
-  end
-  SecureStateHeader_Refresh(f)
-end
-
-function Bar:SetStateKeybindOverrideMap(states) -- 'states' is an array of state-names that should have keybind overrides enabled
-  local f = self.frame
-  for i = 1, #states do
-    local s = states[i]
-    states[i] = ("%s:%s"):format(s,s)
-  end
-  table.insert(states,"_defaultbindings")
-  f:SetAttribute("statebindings",table.concat(states,";"))
-  SecureStateHeader_Refresh(f)
-  for b in pairs(self.buttons) do
-    -- TODO: signal child frames that they should maintain multiple bindings
-  end
-end
-
-local _ofskeys = { "point", "relpoint", "x", "y" }
-function Bar:SetStateAnchorMap( map ) -- 'map' is a { ["statename"] = { point=point, relpoint=relpoint, x=x, y=y } } table
-  local f = self.frame
-  local c = self.config
-  local default = { point = c.anchor, relpoint = c.relativePoint, x = c.x, y = c.y }
-  for _, key in pairs(_ofskeys) do
-    local t = { }
-    for state, info in pairs(map) do
-      if info[key] then
-        table.insert(t, ("%s:%s"):format(state, info[key]))
+  if doButtons then
+    for b in pairs(self.buttons) do
+      local f = b.GetFrame and b:GetFrame()
+      if f then
+        f:SetAttribute(attribute, value)
       end
     end
-    if #t > 0 and default[key] then table.insert(t, tostring(default[key])) end
-    f:SetAttribute(("headofs%s"):format(key), table.concat(t,";") or "")
+  else
+    self:GetFrame():SetAttribute(attribute, value)
   end
-  SecureStateHeader_Refresh(f)
+  SecureStateHeader_Refresh(self:GetFrame())
 end
 
-function Bar:SetStateScaleMap( map ) -- 'map' is a { ["statename"] = scalevalue } table
-  local f = self.frame
-  local t = { }
-  for state, scale in pairs(map) do
-    table.insert( t, ("%s:%s"):format(state,scale) )
-  end
-  if #t > 0 then table.insert(t, "1.0") end
-  f:SetAttribute("headscale",table.concat(t,";") or "")
-  SecureStateHeader_Refresh(f)
-end
-
-
 
 ------ Export as a class-factory ------
 ReAction.Bar = {
   prototype = Bar,
-  new = function(self, ...)
+  New = function(self, ...)
     local x = { }
     for k,v in pairs(Bar) do
       x[k] = v
--- a/Overlay.lua	Tue Jun 10 22:25:15 2008 +0000
+++ b/Overlay.lua	Mon Jun 16 18:46:08 2008 +0000
@@ -65,7 +65,7 @@
 local ApplyAnchor   = Bar.ApplyAnchor
 
 local function StoreExtents(bar)
-  local f = bar.frame
+  local f = bar:GetFrame()
   local point, relativeTo, relativePoint, x, y = f:GetPoint(1)
   relativeTo = relativeTo or f:GetParent()
   local anchorTo
@@ -86,7 +86,7 @@
 end
 
 local function StoreSize(bar)
-  local f = bar.frame
+  local f = bar:GetFrame()
   local c = bar.config
   c.width, c.height = f:GetWidth(), f:GetHeight()
 end
@@ -407,7 +407,7 @@
 end
 
 local function CreateControls(bar)
-  local f = bar.frame
+  local f = bar:GetFrame()
 
   f:SetMovable(true)
   f:SetResizable(true)
@@ -493,7 +493,6 @@
         f:SetScript("OnUpdate", 
           function()
             RecomputeGrid(bar)
-            bar:RefreshLayout()
           end
         )
       end
@@ -540,7 +539,6 @@
           f:SetScript("OnUpdate", 
             function()
               RecomputeButtonSize(bar)
-              bar:RefreshLayout()
               updateTooltip()
             end
           )
@@ -549,7 +547,6 @@
           f:SetScript("OnUpdate", 
             function()
               RecomputeButtonSpacing(bar)
-              bar:RefreshLayout()
               updateTooltip()
             end
           )
--- a/ReAction.lua	Tue Jun 10 22:25:15 2008 +0000
+++ b/ReAction.lua	Mon Jun 16 18:46:08 2008 +0000
@@ -297,7 +297,7 @@
     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
+  local bar = self.Bar:New( name, profile.bars[name] )  -- ReAction.Bar defined in Bar.lua
   bars[name] = bar
   callbacks:Fire("OnCreateBar", bar, name)
   if private.configMode then
--- a/State.lua	Tue Jun 10 22:25:15 2008 +0000
+++ b/State.lua	Mon Jun 16 18:46:08 2008 +0000
@@ -8,6 +8,7 @@
 local L = ReAction.L
 local _G = _G
 local InCombatLockdown = InCombatLockdown
+local format = string.format
 
 -- module declaration
 local moduleID = "State"
@@ -33,9 +34,26 @@
   return t
 end
 
--- PRIVATE --
+-- 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
+
 
 local InitRules, ApplyStates, SetProperty, GetProperty
+
+-- PRIVATE --
 do
   -- As far as I can tell the macro clauses are NOT locale-specific.
   local ruleformats = { 
@@ -56,13 +74,14 @@
     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 can be flight, tree, or moonkin
+  -- 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 = { }
@@ -78,91 +97,109 @@
     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 treekin   = forms["Interface\\Icons\\Ability_Druid_TreeofLife"] or forms["Interface\\Icons\\Spell_Nature_ForceOfNature"] 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     = ("stance:%d"):format(defensive)
-    ruleformats.berserker     = ("stance:%d"):format(berserker)
-    ruleformats.caster        = ("form:0/%d/%d/%d"):format(aquatic, travel, flight)
-    ruleformats.bear          = ("form:%d"):format(bear)
-    ruleformats.cat           = ("form:%d"):format(cat)
-    ruleformats.treeOrMoonkin = ("form:%d"):format(treekin)
+    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
 
-  -- 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)
+
+  -- state property functions
+  local ofskeys = {
+    anchorPoint = "point",
+    anchorRelPoint = "relpoint",
+    anchorX = "x",
+    anchorY = "y" 
+  }
+
+  local barofsidx = {
+    anchorPoint = 1,
+    anchorRelPoint = 3,
+    anchorX = 4,
+    anchorY = 5
+  }
+
+  local function UpdatePartialAnchor(bar, states, ckey)
+    local map = { }
+    local bc = bar.config
+    for state, c in pairs(states) do
+      if c.enableAnchor then
+        map[state] = c[ckey]
+      end
     end
-    local dotdotdot = { ... }
-    table.sort(r, function(lhs, rhs)
-       local olhs = tfetch(t[lhs], unpack(dotdotdot)) or 0
-       local orhs = tfetch(t[rhs], unpack(dotdotdot)) or 0
-       return olhs < orhs
-      end)
-    return r
+    local ofskey = ofskeys[ckey]
+    local default = select(barofsidx[ckey], bar:GetAnchor())
+    bar:SetStateAttribute(format("headofs%s",ofskeys[ckey]), map, default)
+  end
+  
+  -- the name of the function maps to the name of the config element
+  local propertyFuncs = { 
+    hide = function( bar, states )
+      local hs = { }
+      for state, config in pairs(states) do
+        if config.hide then
+          table.insert(hs, state)
+        end
+      end
+      bar:SetStateAttribute("hidestates", nil, table.concat(hs,","), true) -- pass to buttons
+    end,
+
+    page = function( bar, states )
+      local map = { }
+      for state, config in pairs(states) do
+        if config.page then
+          map[state] = format("page%d",config.page)
+        end
+      end
+      bar:SetStateAttribute("statebutton", map)
+    end,
+
+    keybindstate = function( bar, states )
+      local map = { }
+      for state, config in pairs(states) do
+        local kbset = config.keybindstate and state
+        map[state] = kbset
+        for button in bar:IterateButtons() do
+          -- TODO: inform children they should maintain multiple binding sets
+          -- ?? button:UpdateBindingSet(kbset)
+        end
+      end
+      bar:SetStateAttribute("statebindings", map)
+    end,
+
+    enableAnchor = function( bar, states )
+      for ckey in pairs(ofskeys) do
+        UpdatePartialAnchor(bar, states, ckey)
+      end
+    end,
+
+    enableScale = function( bar, states )
+      local map = { }
+      for state, c in pairs(states) do
+        if c.enableScale then
+          map[state] = c.scale
+        end
+      end
+      bar:SetStateAttribute("headscale", map, 1.0)
+    end,
+  }
+
+  -- generate some table entries
+  propertyFuncs.scale = propertyFuncs.enableScale
+  for ckey in pairs(ofskeys) do
+    propertyFuncs[ckey] = function( bar, states )
+      UpdatePartialAnchor(bar, states, ckey)
+    end
   end
 
-  local function BuildRules(states)
-    local rules = { }
-    local keybinds = { }
-    local default
-    local fmt = "%s %s"
-    for idx, state in ipairs(fieldsort(states, "rule", "order")) do
-      local c = states[state].rule
-      local type = tfetch(c,"type")
-      if type == "default" then
-        default = default or state
-      elseif type == "keybind" then
-        keybinds[state] = c.keybind or false
-      elseif type == "custom" then
-        if c.custom then
-          -- strip out all spaces from the custom rule
-          table.insert(rules, fmt:format(c.custom:gsub("%s",""), state))
-        end
-      elseif type == "any" then
-        if c.values then
-          local clauses = { }
-          for key, value in pairs(c.values) do
-            table.insert(clauses, ("[%s]"):format(ruleformats[key]))
-          end
-          if #clauses > 0 then
-            table.insert(rules, fmt:format(table.concat(clauses), state))
-          end
-        end
-      elseif 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
-            table.insert(rules, fmt:format(("[%s]"):format(table.concat(clauses, ",")), state))
-          end
-        end
-      end
-    end
-    if default then
-      table.insert(rules, default)
-    end
-    return rules, keybinds
-  end
-
-  local propertyFuncs = { }
-
-  function ApplyStates( bar )
-    local states = tfetch(module.db.profile.bars, bar:GetName(), "states")
-    if states then
-      local rules, keybinds = BuildRules(states)
-      bar:SetStateDriver(table.concat(rules,";"), states, keybinds)
-      for k, f in pairs(propertyFuncs) do
-        f(bar, states)
-      end
-    end
-  end
 
   function GetProperty( bar, state, propname )
     return tfetch(module.db.profile.bars, bar:GetName(), "states", state, propname)
@@ -177,65 +214,111 @@
     end
   end
 
-  -- state property functions
-  function propertyFuncs.hide( bar, states )
-    local tmp = { }
-    for state, config in pairs(states) do
-      if config.hide then
-        table.insert(tmp, state)
+
+
+  --
+  -- Build a state-transition spec string and statemap to be passed to
+  -- Bar:SetStateDriver().
+  --
+  -- The statemap building is complex: keybound states override all 
+  -- other transitions, so must remain in their current state, but must
+  -- also remember other transitions that happen while they're stuck there
+  -- so that when the binding is toggled off it can return to the proper state
+  --
+  local function BuildStateMap(states)
+    local rules = { }
+    local statemap = { }
+    local keybinds = { }
+    local default
+
+    -- first grab all the keybind override states
+    -- and construct an override template
+    local override
+    do
+      local overrides = { }
+      for name, state in pairs(states) do
+        local type = tfetch(state, "rule", "type")
+        if type == "keybind" then
+          -- use the state-stack to remember the current transition
+          -- use $s as a marker for a later call to gsub()
+          table.insert(overrides, format("%s:$s set() %s", name, name))
+        end
+      end
+      if #overrides > 0 then
+        table.insert(overrides, "") -- for a trailing ';'
+      end
+      override = table.concat(overrides, ";") or ""
+    end
+
+    -- now iterate the rules in order
+    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" then
+        if c.values then
+          local clauses = { }
+          for key, value in pairs(c.values) do
+            table.insert(clauses, format("[%s]", ruleformats[key]))
+          end
+          if #clauses > 0 then
+            table.insert(rules, format("%s %s", table.concat(clauses), state))
+          end
+        end
+      elseif 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
+            table.insert(rules, format("%s %s", format("[%s]", table.concat(clauses, ",")), state))
+          end
+        end
+      end
+      
+      -- use a different virtual button for the actual keybind transition,
+      -- to implement a toggle. You have to clear it regardless of the type
+      -- (which is usually a no-op) to unbind state transitions when switching
+      -- transition types.
+      local bindbutton = format("%s_binding",state)
+      if type == "keybind" then
+        keybinds[bindbutton] = c.keybind or false
+        statemap[bindbutton] = format("%s:pop();*:set(%s)", state, state)
+      else
+        keybinds[bindbutton] = false
+      end
+
+      -- construct the statemap. gsub() the state name into the override template.
+      statemap[state] = format("%s%s", override:gsub("%$s",state), state)
+    end
+    -- make sure that the default, if any, is last
+    if default then
+      table.insert(rules, default)
+    end
+    return table.concat(rules,";"), statemap, keybinds
+  end
+
+  function ApplyStates( bar )
+    local states = tfetch(module.db.profile.bars, bar:GetName(), "states")
+    if states then
+      local rule, statemap, keybinds = BuildStateMap(states)
+      bar:SetStateDriver("reaction", rule, statemap)
+      for state, key in pairs(keybinds) do
+        bar:SetAttributeBinding(state, key, "state-reaction", state)
+      end
+      for k, f in pairs(propertyFuncs) do
+        f(bar, states)
       end
     end
-    local s = table.concat(tmp,",")
-    bar:SetHideStates(s)
   end
 
-  function propertyFuncs.page( bar, states )
-    local map = { }
-    for state, config in pairs(states) do
-      map[state] = config.page
-    end
-    bar:SetStatePageMap(state, map)
-  end
-
-  function propertyFuncs.keybindstate( bar, states )
-    local map = { }
-    for state, config in pairs(states) do
-      if config.keybindstate then
-        table.insert(map,state)
-      end
-    end
-    bar:SetStateKeybindOverrideMap(map)
-  end
-
-  local function updateAnchor(bar, states)
-    local map = { }
-    for state, c in pairs(states) do
-      if c.enableAnchor then
-        map[state] = { point = c.anchorPoint, relpoint = c.anchorRelPoint, x = c.anchorX, y = c.anchorY }
-      end
-    end
-    bar:SetStateAnchorMap(map)
-  end
-
-  propertyFuncs.enableAnchor   = updateAnchor
-  propertyFuncs.anchorPoint    = updateAnchor
-  propertyFuncs.anchorRelPoint = updateAnchor
-  propertyFuncs.anchorX        = updateAnchor
-  propertyFuncs.anchorY        = updateAnchor
-
-  local function updateScale( bar, states )
-    local map = { }
-    for state, c in pairs(states) do
-      if c.enablescale then
-        map[state] = c.scale
-      end
-    end
-    bar:SetStateScaleMap(map)
-  end
-
-  propertyFuncs.enablescale = updateScale
-  propertyFuncs.scale       = updateScale
-
 end
 
 
@@ -289,7 +372,7 @@
 end
 
 function module:OnRenameBar(event, bar, oldname, newname)
-  local b = self.db.profile.bars
+  local bars = self.db.profile.bars
   bars[newname], bars[oldname] = bars[oldname], nil
 end
 
@@ -317,12 +400,13 @@
   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"]}, {treeOrMoonkin = L["Tree/Moonkin"]}  } },
+    { "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"]} } },
   }
@@ -482,7 +566,7 @@
             set  = function(info, value) 
                      -- check for existing state name
                      if states[value] then
-                       L["State named '%s' already exists"]:format(value)
+                       format(L["State named '%s' already exists"],value)
                      end
                      local args = optionMap[bar].args
                      states[value], args[value], states[opts.name], args[opts.name] = states[opts.name], args[opts.name], nil, nil
@@ -551,27 +635,27 @@
             order = 3,
             type  = "select",
             disabled = function()
-                         return bar:GetNumPages() < 2
+                         --return bar:GetNumPages() < 2
+                         return true
                        end,
             hidden   = function()
-                         return bar:GetNumPages() < 2
+                         --return bar:GetNumPages() < 2
+                         return true
                        end,
             values   = function()
-                         local pages = { none = " " }
-                         for i = 1, bar:GetNumPages() do
-                           pages[i] = i
-                         end
+                         -- use off-by-one ordering to put (none) first in the list
+                         local pages = { [1] = L["(none)"] }
+                         --for i = 1, bar:GetNumPages() do
+                         --  pages[i+1] = i
+                         --end
                          return pages
                        end,
             set      = function(info, value)
-                         if value == "none" then
-                           setprop(info, nil)
-                         else
-                           setprop(info, value)
-                         end
+                         value = value - 1
+                         setprop(info, value > 0 and value or nil)
                        end,
             get      = function(info)
-                         return getprop(info) or "none"
+                         return getprop(info) or L["(none)"]
                        end,
           },
           keybindstate = {
@@ -647,7 +731,7 @@
             type  = "group",
             inline = true,
             args = {
-              enablescale = {
+              enableScale = {
                 name  = L["Set New Scale"],
                 order = 1,
                 type  = "toggle",
@@ -664,8 +748,8 @@
                 isPercent = true,
                 set   = setprop,
                 get   = function(info) return getprop(info) or 1 end,
-                disabled = function() return not GetProperty(bar, opts.name, "enablescale") end,
-                hidden = function() return not GetProperty(bar, opts.name, "enablescale") end,
+                disabled = function() return not GetProperty(bar, opts.name, "enableScale") end,
+                hidden = function() return not GetProperty(bar, opts.name, "enableScale") end,
               },
             },
           },
@@ -771,7 +855,7 @@
                   end
                   local c, r = s:match("(%b[])(.*)")
                   if c == nil and s and #s > 0 then
-                    return L["Invalid custom rule '%s': each clause must appear within [brackets]"]:format(rule)
+                    return format(L["Invalid custom rule '%s': each clause must appear within [brackets]"],rule)
                   end
                   s = r
                 until c == nil
@@ -816,6 +900,7 @@
 
   CreateBarOptions = function(bar)
     local private = { }
+    local states = tbuild(module.db.profile.bars, bar:GetName(), "states")
     local options = {
       type = "group",
       name = L["Dynamic State"],
@@ -849,7 +934,7 @@
               func = function ()
                   local name = private.newstatename
                   if states[name] then
-                    ReAction:UserError(L["State named '%s' already exists"]:format(name))
+                    ReAction:UserError(format(L["State named '%s' already exists"],name))
                   else
                     -- TODO: select default state options and pass as final argument
                     states[name] = { }
--- a/locale/enUS.lua	Tue Jun 10 22:25:15 2008 +0000
+++ b/locale/enUS.lua	Mon Jun 16 18:46:08 2008 +0000
@@ -46,7 +46,8 @@
 "Caster Form",
 "Bear Form",
 "Cat Form",
-"Tree/Moonkin",
+"Tree of Life",
+"Moonkin Form",
 "Stealth",
 "No Stealth",
 "Shadowform",
@@ -59,6 +60,7 @@
 "Hostile Focus",
 "Friendly Focus",
 "No Focus",
+"Mind Control",
 "Raid",
 "Party",
 "Solo",
@@ -76,6 +78,7 @@
 "Set the properties for the bar when in this state",
 "Hide Bar",
 "Show Page #",
+"(none)",
 "Override Keybinds",
 "Set this state to maintain its own set of keybinds which override the defaults when active",
 "Position",
--- a/modules/ReAction_Action/ReAction_Action.lua	Tue Jun 10 22:25:15 2008 +0000
+++ b/modules/ReAction_Action/ReAction_Action.lua	Mon Jun 16 18:46:08 2008 +0000
@@ -17,12 +17,28 @@
 local moduleID = "Action"
 local module = ReAction:NewModule( moduleID )
 
+-- private --
+local function GetBarConfig(bar)
+  return module.db.profile.bars[bar:GetName()]
+end
+
+local function RefreshLite(bar)
+  local btns = module.buttons[bar]
+  if btns then
+    for _, b in ipairs(btns) do
+      b:Refresh()
+    end
+  end
+end
+
+
 -- module methods
 function module:OnInitialize()
   self.db = ReAction.db:RegisterNamespace( moduleID,
     { 
       profile = {
-        buttons = { }
+        buttons = { },
+        bars = { },
       }
     }
   )
@@ -79,7 +95,11 @@
     if profile.buttons[name] == nil then
       profile.buttons[name] = {}
     end
+    if profile.bars[name] == nil then
+      profile.bars[name] = {}
+    end
     local btnCfg = profile.buttons[name]
+    local barCfg = profile.bars[name]
 
     local r, c = bar:GetButtonGrid()
     local n = r*c
@@ -88,22 +108,23 @@
         btnCfg[i] = {}
       end
       if btns[i] == nil then
-        local ok, b = pcall(self.BtnClass.new, self.BtnClass, bar, i, btnCfg[i])
+        local ok, b = pcall(self.BtnClass.New, self.BtnClass, bar, i, btnCfg[i], barCfg)
         if ok and b then
           btns[i] = b
+          bar:AddButton(i,b)
         end
-      else
-        btns[i]:Refresh(bar,i)
       end
     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
+    RefreshLite(bar)
   end
 end
 
@@ -121,11 +142,15 @@
 
 function module:OnEraseBar(event, bar, name)
   self.db.profile.buttons[name] = nil
+  self.db.profile.bars[name] = nil
 end
 
 function module:OnRenameBar(event, bar, oldname, newname)
   local b = self.db.profile.buttons
   b[newname], b[oldname] = b[oldname], nil
+
+  b = self.db.profile.bars
+  b[newname], b[oldname] = b[oldname], nil
 end
 
 function module:SetHideEmptyButtons(hide)
@@ -163,13 +188,20 @@
 
 function module:showActionIDLabel(button)
   if not button.actionIDLabel and button:GetActionID() then
-    local label = button:GetFrame():CreateFontString(nil,"OVERLAY","GameFontNormalLarge")
+    local f = button:GetFrame()
+    local label = f:CreateFontString(nil,"OVERLAY","GameFontNormalLarge")
     label:SetAllPoints()
     label:SetJustifyH("CENTER")
     label:SetShadowColor(0,0,0,1)
     label:SetShadowOffset(2,-2)
     label:SetText(tostring(button:GetActionID()))
     button.actionIDLabel = label
+    f:HookScript("OnAttributeChanged", 
+      function(frame, attr, value)
+        if attr == "state-parent" then
+          label:SetText(tostring(button:GetActionID()))
+        end
+      end)
   end
   button.actionIDLabel:Show()
 end
@@ -230,27 +262,33 @@
 ------ Button class ------
 local Button = { }
 
-local function Constructor( self, bar, idx, config )
-  self.bar, self.idx, self.config = bar, idx, config
+local function Constructor( self, bar, idx, config, barConfig )
+  self.bar, self.idx, self.config, self.barConfig = bar, idx, config, barConfig
 
   local barFrame = bar:GetFrame()
 
   config.name = config.name or ("ReAction_%s_%d"):format(bar:GetName(),idx)
   self.name = config.name
   config.actionID = ActionIDList[config.actionID] -- gets a free one if none configured
+  self.nPages = 1
   
   local f = CreateFrame("CheckButton", self.name, barFrame, "ActionBarButtonTemplate")
 
   -- TODO: re-implement ActionButton event handlers that don't do secure stuff
 
-  -- this will probably cause taint, using right now for display/debugging purposes
+  -- this will probably cause taint and/or performance problems, using right now for display/debugging purposes
   f:SetScript("OnAttributeChanged", ActionButton_UpdateAction)
+
   f:SetAttribute("action", config.actionID)
+  -- install mind control action support for all buttons here just for simplicity
+  if self.idx <= 12 then
+    f:SetAttribute("action-mc", 120 + self.idx)
+  end
 
   barFrame:SetAttribute("addchild",f)
 
   self.frame = f
-  self:Refresh(bar,idx)
+  self:Refresh()
 
   if not module.db.profile.hideEmptyButtons then
     ActionButton_ShowGrid(self.frame)
@@ -274,13 +312,23 @@
   if self.config.actionID then
     ActionIDList[self.config.actionID] = nil
   end
+  if self.config.pages then
+    for _, id in ipairs(self.config.pages) do
+      ActionIDList[id] = nil
+    end
+  end
   self.frame = nil
   self.config = nil
   self.bar = nil
 end
 
-function Button:Refresh(bar,idx)
-  bar:PlaceButton(self.frame, idx, 36, 36)
+function Button:Refresh()
+  local f = self.frame
+  self.bar:PlaceButton(self, 36, 36)
+  if self.barConfig.mckeybinds then
+    f:SetAttribute("bindings-mc", self.barConfig.mckeybinds[self.idx])
+  end
+  self:RefreshPages()
 end
 
 function Button:GetFrame()
@@ -292,13 +340,36 @@
 end
 
 function Button:GetActionID()
-  return self.config.actionID
+  return SecureButton_GetModifiedAttribute(self.frame, "action")
 end
 
+function Button:RefreshPages()
+  local nPages = 1 --self.bar:GetNumPages()
+  if nPages ~= self.nPages then
+    local f = self:GetFrame()
+    local c = self.config.pages
+    if nPages > 1 and not c then
+      c = { }
+      self.config.pages = c
+    end
+    for i = 1, nPages do
+      c[i] = ActionIDList[c[i]] -- gets a free one if none configured
+      f:SetAttribute(("action-page%d"):format(i))
+    end
+    for i = nPages+1, #c do
+      ActionIDList[c[i]] = nil
+      c[i] = nil
+      f:SetAttribute(("action-page%d"):format(i))
+    end
+
+    -- TODO:
+    -- apply next-page, prev-page, and direct-page keybinds (via bar:SetStateKeybind abstraction)
+  end
+end
 
 -- export as a class-factory to module
 module.BtnClass = {
-  new = function(self, ...)
+  New = function(self, ...)
     local x = { }
     for k,v in pairs(Button) do
       x[k] = v
--- a/modules/ReAction_PetAction/ReAction_PetAction.lua	Tue Jun 10 22:25:15 2008 +0000
+++ b/modules/ReAction_PetAction/ReAction_PetAction.lua	Mon Jun 16 18:46:08 2008 +0000
@@ -84,6 +84,7 @@
         local ok, b = pcall(self.BtnClass.new, self.BtnClass, bar, i, btnCfg[i])
         if ok and b then
           btns[i] = b
+          bar:AddButton(i,b)
         end
       else
         btns[i]:Refresh(bar,i)
@@ -91,6 +92,7 @@
     end
     for i = n+1, #btns do
       if btns[i] then
+        bar:RemoveButton(b)
         btns[i] = btns[i]:Destroy()
         if btnCfg[i] then
           btnCfg[i] = nil
@@ -296,7 +298,7 @@
 end
 
 function Button:Refresh(bar,idx)
-  bar:PlaceButton(self.frame, idx, 30, 30)
+  bar:PlaceButton(self, 30, 30)
   self:Update()
   self:UpdateHotkey()
 end
--- a/modules/ReAction_PossessBar/ReAction_PossessBar.lua	Tue Jun 10 22:25:15 2008 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,391 +0,0 @@
---[[
-  ReAction Possess Bar (Mind Control/etc) button module.
-
-  Wraps the standard Action buttons 121-132 with an automatic show/hide
-  when mind control (etc) is active
-
---]]
-
--- local imports
-local ReAction = ReAction
-local L = ReAction.L
-local _G = _G
-local CreateFrame = CreateFrame
-
--- module declaration
-local moduleID = "PossessBar"
-local module = ReAction:NewModule( moduleID )
-
--- module methods
-function module:OnInitialize()
-  self.db = ReAction.db:RegisterNamespace( moduleID,
-    { 
-      profile = {
-        buttons = { }
-      }
-    }
-  )
-  self.buttons = { }
-
-  ReAction:RegisterOptions(self, {
-      [moduleID] = {
-        type = "group",
-        name = L["Possess Bar"],
-        args = {
-          hideEmptyPossess = {
-            type = "toggle",
-            name = L["Hide Empty Possess Bar Buttons"],
-            get  = function() return self.db.profile.hideEmptyButtons end,
-            set  = function(info, val) module:SetHideEmptyButtons(val) end,
-          }
-        }
-      }
-    })
-
-  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()
-  ReAction:RegisterBarType(L["Possess Bar"], 
-    { 
-      type = moduleID,
-      defaultButtonSize = 36,
-      defaultBarRows = 1,
-      defaultBarCols = 12,
-      defaultBarSpacing = 3
-    })
-end
-
-function module:OnDisable()
-  ReAction:UnregisterBarType(L["Possess Bar"])
-end
-
-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:OnRefreshBar(event, bar, name)
-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 ok, b = pcall(self.BtnClass.new, self.BtnClass, bar, i, btnCfg[i])
-        if ok and b then
-          btns[i] = b
-        end
-      else
-        btns[i]:Refresh(bar,i)
-      end
-    end
-    for i = n+1, #btns do
-      if btns[i] then
-        btns[i] = btns[i]:Destroy()
-        if btnCfg[i] then
-          btnCfg[i] = nil
-        end
-      end
-    end
-  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:SetHideEmptyButtons(hide)
-  if hide ~= self.db.profile.hideEmptyButtons then
-    for _, bar in pairs(self.buttons) do
-      for _, b in pairs(bar) do
-        if hide then
-          ActionButton_HideGrid(b.frame)
-        else
-          ActionButton_ShowGrid(b.frame)
-        end
-      end
-    end
-    self.db.profile.hideEmptyButtons = hide
-  end
-end
-
-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
-          if mode then
-            ActionButton_ShowGrid(b.frame)
-            self:showActionIDLabel(b)
-          else
-            ActionButton_HideGrid(b.frame)
-            self:hideActionIDLabel(b)
-          end
-        end
-      end
-      local f = bar:GetFrame()
-      if mode then
-        f:SetParent(UIParent)
-        f:Show()
-      else
-        f:SetParent(PossessBarFrame)
-      end
-    end
-  end
-end
-
-function module:showActionIDLabel(button)
-  if not button.actionIDLabel and button:GetActionID() then
-    local label = button:GetFrame():CreateFontString(nil,"OVERLAY","GameFontNormalLarge")
-    label:SetAllPoints()
-    label:SetJustifyH("CENTER")
-    label:SetShadowColor(0,0,0,1)
-    label:SetShadowOffset(2,-2)
-    label:SetText(tostring(button:GetActionID()))
-    button.actionIDLabel = label
-  end
-  button.actionIDLabel:Show()
-end
-
-function module:hideActionIDLabel(button)
-  if button.actionIDLabel then
-    button.actionIDLabel:Hide()
-  end
-end
-
-
--- possess-bar control buttons (shows buff, cancel buff)
-function module:CreatePossessControlButton(bar,id,name)
-  -- guard against taint by reusing global variable frames
-  -- instead of nilling them out (e.g. create bar, delete bar, create bar with same name)
-  name = name or ("ReAction_%s_PossessCtrlButton%d"):format(bar:GetName(),id)
-  local b = name and _G[name]
-  if b then 
-    b:SetParent(bar:GetFrame())
-  else
-    b = CreateFrame("CheckButton", name, bar:GetFrame(), "PossessButtonTemplate")
-  end
-  b:SetID(id)
-
-  b:RegisterEvent("PLAYER_AURAS_CHANGED");
-
-  local icon = _G[("%sIcon"):format(name)]
-  local cooldown = _G[("%sCooldown"):format(name)]
-  local nTex = _G[("%sNormalTexture"):format(name)]
-  nTex:SetWidth(54)
-  nTex:SetHeight(54)
-
-  local function update()
-    local texture = GetPossessInfo(id);
-    icon:SetTexture(texture);
-    icon:Show()
-    cooldown:Hide();
-    b:SetChecked(0);
-    icon:SetVertexColor(1.0, 1.0, 1.0);
-  end
-  update()
-
-  b:HookScript("OnClick", function() b:SetChecked(0) end)
-  b:SetScript("OnEvent", update)
-  b:SetScript("OnShow", update)
-
-  return b
-end
-
-function module:CreatePossessControlButtons(bar)
-  if not bar.possessButtons then
-    bar.possessButtons = { }
-    bar.config.possessFrameNames = bar.config.possessFrameNames or { }
-    local previous
-    local n = NUM_POSSESS_SLOTS
-    for i = 1, n do
-      local name = bar.config.possessFrameNames[i]
-      local f = self:CreatePossessControlButton(bar,i,name)
-      bar.possessButtons[i] = f
-      bar.config.possessFrameNames[i] = f:GetName()
-
-      local r, c, s = bar:GetButtonGrid()
-      local w, h = bar:GetButtonSize()
-
-      local scale = ((h - (n-1)*s)/n)/30
-      f:SetScale(scale)
-
-      if previous then
-        f:SetPoint("TOP", previous, "BOTTOM", 0, -s/scale)
-      else
-        f:SetPoint("TOPRIGHT", bar:GetFrame(), "TOPLEFT", -s/scale, 0)
-      end
-      f:Show()
-      previous = f
-    end
-  end
-end
-
----- Options ----
-function module:GetBarOptions(bar)
-  return {
-    type = "group",
-    name = L["Possess Buttons"],
-    hidden = function() return bar.config.type ~= moduleID end,
-    args = {
-    }
-  }
-end
-
-
--- use-count of action IDs
-local minActionID = 121
-local maxActionID = 132
-local ActionIDList = setmetatable( {}, {
-  __index = function(self, idx)
-    if idx == nil then
-      for i = minActionID, maxActionID do
-        if rawget(self,i) == nil then
-          rawset(self,i,1)
-          return i
-        end
-      end
-      error("ran out of 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
-})
-
-
-
-
------- Button class ------
-local Button = { }
-
-local function Constructor( self, bar, idx, config )
-  self.bar, self.idx, self.config = bar, idx, config
-
-  local barFrame = bar:GetFrame()
-
-  config.name = config.name or ("ReAction_%s_Possess_%d"):format(bar:GetName(),idx)
-  self.name = config.name
-  config.actionID = ActionIDList[config.actionID] -- gets a free one if none configured
-  
-  local f = CreateFrame("CheckButton", self.name, barFrame, "BonusActionButtonTemplate")
-
-  -- TODO: re-implement ActionButton event handlers that don't do secure stuff
-
-  -- this will probably cause taint, using right now for display/debugging purposes
-  f:SetScript("OnAttributeChanged", ActionButton_UpdateAction)
-  f:SetAttribute("action", config.actionID)
-
-  barFrame:SetAttribute("addchild",f)
-
-  self.frame = f
-  self:Refresh(bar,idx)
-
-  if not module.db.profile.hideEmptyButtons then
-    ActionButton_ShowGrid(self.frame)
-  end
-
-  if ReAction.configMode then
-    ActionButton_ShowGrid(self.frame)
-    module:showActionIDLabel(self)
-  end
-end
-
-function Button:Destroy()
-  local f = self.frame
-  f:UnregisterAllEvents()
-  f:Hide()
-  f:SetParent(UIParent)
-  f:ClearAllPoints()
-  if self.name then
-    _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(bar,idx)
-  bar:PlaceButton(self.frame, idx, 36, 36)
-end
-
-function Button:GetFrame()
-  return self.frame
-end
-
-function Button:GetName()
-  return self.name
-end
-
-function Button:GetActionID()
-  return self.config.actionID
-end
-
-
--- export as a class-factory to module
-module.BtnClass = {
-  new = function(self, ...)
-    local x = { }
-    for k,v in pairs(Button) do
-      x[k] = v
-    end
-    Constructor(x, ...)
-    return x
-  end
-}
--- a/modules/ReAction_PossessBar/ReAction_PossessBar.toc	Tue Jun 10 22:25:15 2008 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,11 +0,0 @@
-## Interface: 20300
-## Title: ReAction: Possess Bar
-## Notes: Possess Bar (Mind Control) module for ReAction
-## DefaultState: enabled
-## LoadOnDemand: 1
-## Author: Flick
-## Version: 1.0
-## X-Category: Action Bars
-## Dependencies: ReAction
-
-ReAction_PossessBar.xml
--- a/modules/ReAction_PossessBar/ReAction_PossessBar.xml	Tue Jun 10 22:25:15 2008 +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_PossessBar.lua"/>
-
-</Ui>
\ No newline at end of file
--- a/modules/modules.xml	Tue Jun 10 22:25:15 2008 +0000
+++ b/modules/modules.xml	Mon Jun 16 18:46:08 2008 +0000
@@ -10,7 +10,6 @@
 <!-- button modules -->
 <Include file="ReAction_Action\ReAction_Action.xml"/>
 <Include file="ReAction_PetAction\ReAction_PetAction.xml"/>
-<Include file="ReAction_PossessBar\Reaction_PossessBar.xml"/>
 
 <!-- not yet implemented
 <Include file="ReAction_BagBar\ReAction_BagBar.xml"/>