changeset 305:0211bcdfab70 stable

Merge 1.1 beta 12 to stable
author Flick
date Wed, 14 Nov 2012 16:47:27 -0800
parents 30c9bdaad7a3 (current diff) c63d74f26c22 (diff)
children 08bd2dec1f01
files .hgtags MultiCastButton.lua
diffstat 15 files changed, 227 insertions(+), 928 deletions(-) [+]
line wrap: on
line diff
--- a/.hgtags	Fri Aug 05 16:28:13 2011 -0700
+++ b/.hgtags	Wed Nov 14 16:47:27 2012 -0800
@@ -8,3 +8,5 @@
 499ca4edf033da3ec4fc52fd1058bf27b2679bbe 1.1 beta 8
 d913dd4878af0e4fa0380c777be74088b5a49ffc 1.1 beta 9
 276165a0e860616f13c042f93115f48a03ffc117 1.1 beta 10
+0cb6a99444974f7f09b50c1e3db40764f84b051d 1.1 alpha 11 (5.04 bandaid)
+86f99ec6d3e56b161a68a757b41b3794624da8fc 1.1 beta 12
--- a/ActionButton.lua	Fri Aug 05 16:28:13 2011 -0700
+++ b/ActionButton.lua	Wed Nov 14 16:47:27 2012 -0800
@@ -46,8 +46,9 @@
      (doMindControl and mcVehicleState == "mc") then
     local idx = self:GetAttribute("bar-idx")
     local maxN = (doVehicle and mcVehicleState == "vehicle") and 7 or 12
+    local pageIndex = (doVehicle and mcVehicleState == "vehicle") and 12 or 14
     if idx and idx <= maxN then
-      action = 120 + idx
+      action = 12*(pageIndex-1) + idx
     else
       action = 0
     end
@@ -146,12 +147,9 @@
 --
 local eventList = {
   "PLAYER_ENTERING_WORLD",
-  "ACTIONBAR_PAGE_CHANGED",
   "ACTIONBAR_SLOT_CHANGED",
   "UPDATE_BINDINGS",
-  "ACTIONBAR_UPDATE_STATE",
   "ACTIONBAR_UPDATE_USABLE",
-  "ACTIONBAR_UPDATE_COOLDOWN",
   "UNIT_INVENTORY_CHANGED",
   "LEARNED_SPELL_IN_TAB",
   "UPDATE_INVENTORY_ALERTS",
@@ -166,6 +164,9 @@
   "UNIT_ENTERED_VEHICLE",
   "UNIT_EXITED_VEHICLE",
   "COMPANION_UPDATE",
+  "UPDATE_SHAPESHIFT_FORM",
+  "SPELL_UPDATE_CHARGES",
+  "UPDATE_SUMMONPETS_ACTION",
 }
 
 --
@@ -186,7 +187,8 @@
     },
 
     barType = L["Action Bar"],
-    buttonTypeID = buttonTypeID
+    buttonTypeID = buttonTypeID,
+    eventList = eventList
   },
   { __index = Super } )
 
@@ -344,7 +346,6 @@
   self:UpdateTooltip()
   self:UpdateCheckedState()
   self:UpdateUsable()
-  self:UpdateCooldown()
   self:UpdateFlash()
 end
 
@@ -352,6 +353,11 @@
   local action = self:GetActionID()
   if action ~= self.actionID then
     self.actionID = action
+
+    -- update checked state and cooldown in C-code
+    local f = self:GetFrame()
+    SetActionUIButton(f, action, f.cooldown)
+
     self:UpdateAll()
   end
 end
@@ -394,12 +400,10 @@
     end
     icon:Show()
     self.rangeTimer = -1
-    f:SetNormalTexture("Interface\\Buttons\\UI-Quickslot2")
   else
     icon:Hide()
     self.frames.cooldown:Hide()
     self.rangeTimer = nil
-    f:SetNormalTexture("Interface\\Buttons\\UI-Quickslot")
   end
 end
 
@@ -421,7 +425,9 @@
 
 function Action:UpdateMacroText()
   local action = self.actionID
-  if not IsConsumableAction(action) and not IsStackableAction(action) then
+  if not IsConsumableAction(action) and 
+     not IsStackableAction(action) and 
+     (IsItemAction(action) or GetActionCount(action) == 0) then
     self.frames.name:SetText(GetActionText(action))
   else
     self.frames.name:SetText("")
@@ -430,10 +436,22 @@
 
 function Action:UpdateCount()
   local action = self.actionID
-  if IsConsumableAction(action) or IsStackableAction(action) then
-    self.frames.count:SetText(GetActionCount(action))
+  local count = GetActionCount(action) or 0
+  if IsConsumableAction(action) or IsStackableAction(action) or (not IsItemAction(action) and count > 0) then
+    if count > 9999 then
+      self.frames.count:SetText("*")
+    else
+      self.frames.count:SetText(count)
+    end
   else
-    self.frames.count:SetText("")
+    local charges, maxCharges, chargeStart, chargeDuration = GetActionCharges(action)
+    charges = tonumber(charges) or 0
+    maxCharges = tonumber(maxCharges) or 0
+    if maxCharges > 1 then
+      self.frames.count:SetText(charges)
+    else
+      self.frames.count:SetText("")
+    end
   end
 end
 
@@ -504,10 +522,6 @@
   return IsActionInRange(self.actionID) == 0
 end
 
-function Action:UpdateCooldown()
-  CooldownFrame_SetTimer(self.frames.cooldown, self:GetCooldown())
-end
-
 function Action:GetCooldown()
   return GetActionCooldown(self.actionID)
 end
@@ -642,7 +656,7 @@
     ]])
 
   f:SetAttribute("_onstate-mc", _onstate_mc)
-  RegisterStateDriver(f, "mc", "[vehicleui] vehicle; [bonusbar:5] mc; none")
+  RegisterStateDriver(f, "mc", "[vehicleui] vehicle; [overridebar] mc; none")
 
   f:SetAttribute("lockbuttons",config.lockButtons)
   f:SetAttribute("lockbuttonscombat",config.lockButtonsCombat)
@@ -722,14 +736,6 @@
   self:UpdateAction()
 end
 
-function Action:ACTIONBAR_PAGE_CHANGED()
-  self:UpdateAction()
-end
-
-function Action:UPDATE_BONUS_ACTIONBAR()
-  self:UpdateAction()
-end
-
 function Action:UPDATE_BINDINGS()
   self:UpdateHotkey()
 end
@@ -738,10 +744,6 @@
   self.rangeTimer = -1
 end
 
-function Action:ACTIONBAR_UPDATE_STATE()
-  self:UpdateCheckedState()
-end
-
 function Action:TRADE_SKILL_SHOW()
   self:UpdateCheckedState()
 end
@@ -765,8 +767,8 @@
   self:UpdateUsable()
 end
 
-function Action:ACTIONBAR_UPDATE_COOLDOWN()
-  self:UpdateCooldown()
+function Action:SPELL_UPDATE_CHARGES()
+  self:UpdateCount()
 end
 
 function Action:PLAYER_ENTER_COMBAT()
@@ -793,6 +795,10 @@
   end
 end
 
+function Action:UPDATE_SHAPESHIFT_FORM()
+  self:UpdateIcon()  
+end
+
 function Action:UNIT_INVENTORY_CHANGED(unit)
   if unit == "player" then
     self:UpdateTooltip()
@@ -802,3 +808,7 @@
 function Action:LEARNED_SPELL_IN_TAB()
   self:UpdateTooltip()
 end
+
+function Action:UPDATE_SUMMONPETS_ACTION()
+  self:UpdateIcon()
+end
--- a/BagButton.lua	Fri Aug 05 16:28:13 2011 -0700
+++ b/BagButton.lua	Wed Nov 14 16:47:27 2012 -0800
@@ -25,7 +25,7 @@
       btnWidth = 30,
       btnHeight = 30,
       btnRows = 1,
-      btnColumns = 6,
+      btnColumns = 5,
       spacing = 4,
       buttons = { }
     },
@@ -39,7 +39,6 @@
 
 local Bag      = setmetatable( { }, { __index = BagBase } )
 local Backpack = setmetatable( { }, { __index = BagBase } )
-local Keyring  = setmetatable( { }, { __index = BagBase } )
 
 ReAction.Button.Bag = BagBase
 ReAction:RegisterBarType(BagBase)
@@ -55,8 +54,6 @@
   local class = Bag
   if idx == 1 then
     class = Backpack
-  elseif idx == 6 then
-    class = Keyring
   end
   self = class:New(btnCfg, bar, idx)
 
@@ -64,7 +61,7 @@
   local config = self:GetConfig()
 
   -- set up the bag ID pool
-  self:SetActionIDPool("bag",6)
+  self:SetActionIDPool("bag",5)
   config.bagID = self:AcquireActionID(config.bagID, idHint, true)
 
   -- non secure scripts
@@ -84,7 +81,7 @@
   f:SetID(self:GetBagID())
 
   if not f.hotkey then
-    local h = f:CreateFontString(name.."HotKey","ARTWORK","NumberFontNormalSmallGray")
+    local h = f:CreateFontString(self:GetName().."HotKey","ARTWORK","NumberFontNormalSmallGray")
     h:SetWidth(30)
     h:SetHeight(10)
     h:SetJustifyH("RIGHT")
@@ -93,14 +90,14 @@
     f.hotkey = h
   end
 
-  if not _G[name.."ItemAnim"] then
-    local anim = CreateFrame("Model",name.."ItemAnim",f,"ItemAnimTemplate")
+  if not _G[self:GetName().."ItemAnim"] then
+    local anim = CreateFrame("Model",self:GetName().."ItemAnim",f,"ItemAnimTemplate")
     anim:SetPoint("BOTTOMRIGHT",f,"BOTTOMRIGHT",-10,0)
     anim:Hide()
   end
 
   if not f.border then
-    local b = f:CreateTexture(name.."Border","OVERLAY")
+    local b = f:CreateTexture(self:GetName().."Border","OVERLAY")
     b:SetAllPoints()
     b:SetWidth(f:GetWidth()*(62/36))
     b:SetHeight(f:GetHeight()*(62/36))
@@ -113,9 +110,9 @@
   self.frames.count:SetDrawLayer("ARTWORK")
 
   self.frames.hotkey = f.hotkey
-  self.frames.border = _G[name.."Border"]
-  self.frames.icon = _G[name.."IconTexture"]
-  self.frames.anim = _G[name.."ItemAnim"]
+  self.frames.border = _G[self:GetName().."Border"]
+  self.frames.icon = _G[self:GetName().."IconTexture"]
+  self.frames.anim = _G[self:GetName().."ItemAnim"]
 
   -- initial display
   if ReAction:GetConfigMode() then
@@ -193,8 +190,8 @@
 --
 -- Bag Button class
 --
-function Bag:New(name, cfg, bar, idx)
-  self = Super.New(self, name, cfg, bar, idx, "ItemButtonTemplate" )
+function Bag:New(cfg, bar, idx)
+  self = Super.New(self, cfg, bar, idx, "ItemButtonTemplate" )
 
   local f = self:GetFrame()
 
@@ -209,7 +206,7 @@
   -- attach to skinner
   bar:SkinButton(self,
     {
-      Icon = _G[name.."IconTexture"]
+      Icon = _G[self:GetName().."IconTexture"]
     }
   )
 
@@ -291,11 +288,11 @@
 --
 -- Backpack Button class
 --
-function Backpack:New(name, cfg, bar, idx)
-  self = Super.New(self, name, cfg, bar, idx, "ItemButtonTemplate" )
+function Backpack:New(cfg, bar, idx)
+  self = Super.New(self, cfg, bar, idx, "ItemButtonTemplate" )
 
   local f = self:GetFrame()
-  local icon = _G[name.."IconTexture"]
+  local icon = _G[self:GetName().."IconTexture"]
   icon:SetTexture("Interface\\Buttons\\Button-Backpack-Up")
   icon:Show()
   f:SetCheckedTexture("Interface\\Buttons\\CheckButtonHilight")
@@ -306,7 +303,7 @@
   -- attach to skinner
   bar:SkinButton(self,
     {
-      Icon = _G[name.."IconTexture"]
+      Icon = _G[self:GetName().."IconTexture"]
     }
   )
 
@@ -386,78 +383,6 @@
 end
 
 
---
--- Keyring Button class
---
-function Keyring:New(name, cfg, bar, idx)
-  self = Super.New(self, name, cfg, bar, idx, "ItemButtonTemplate" )
-
-  local f = self:GetFrame()
-
-  f:SetWidth(18)
-  f:SetHeight(39)
-
-  local tex = f:GetNormalTexture()
-  tex:ClearAllPoints()
-  tex:SetAllPoints()
-  
-  f:SetNormalTexture("Interface\\Buttons\\UI-Button-KeyRing")
-  f:SetHighlightTexture("Interface\\Buttons\\UI-Button-KeyRing-Highlight")
-  f:SetPushedTexture("Interface\\Buttons\\UI-Button-KeyRing-Down")
-  f:GetNormalTexture():SetTexCoord(0,0.5625,0,0.609375)
-  f:GetHighlightTexture():SetTexCoord(0,0.5625,0,0.609375)
-  f:GetPushedTexture():SetTexCoord(0,0.5625,0,0.609375)
-
-  if not HasKey() then
-    f:Hide()
-  end
-
-  -- DO NOT attach to skinner
-
-  return self
-end
-
-function Keyring:GetBagID()
-  return KEYRING_CONTAINER
-end
-
-function Keyring:Refresh()
-  local f = self:GetFrame()
-  self.bar:PlaceButton( self, f:GetHeight(), f:GetHeight() ) -- use height x height since it's an odd size
-  self:UpdateHotkey()
-  self:Update()
-end
-
-function Keyring:SetTooltip()
-  GameTooltip:SetOwner(self:GetFrame(), "ANCHOR_RIGHT");
-  GameTooltip:SetText(KEYRING, HIGHLIGHT_FONT_COLOR.r, HIGHLIGHT_FONT_COLOR.g, HIGHLIGHT_FONT_COLOR.b);
-  GameTooltip:AddLine();
-end
-
-function Keyring:OnReceiveDrag()
-  if CursorHasItem() then
-    PutKeyInKeyRing()
-  end
-end
-
-function Keyring:OnClick()
-  if CursorHasItem() then
-    PutKeyInKeyRing()
-  else
-    ToggleKeyRing()
-  end
-  self:UpdateChecked()
-end
-
-function Keyring:ShowGridTemp(show)
-  if not HasKey() then
-    if show then 
-      self:GetFrame():Show()
-    else
-      self:GetFrame():Hide()
-    end
-  end
-end
 
 
 
--- a/Bar.lua	Fri Aug 05 16:28:13 2011 -0700
+++ b/Bar.lua	Wed Nov 14 16:47:27 2012 -0800
@@ -750,7 +750,7 @@
 local ruleformats = { 
   stealth       = { format = "stealth",        filter = ClassFilter("ROGUE","DRUID") },
   nostealth     = { format = "nostealth",      filter = ClassFilter("ROGUE","DRUID") },
-  shadowdance   = { format = "bonusbar:2",     filter = ClassFilter("ROGUE") },
+  shadowdance   = { format = "stance:3",       filter = ClassFilter("ROGUE") },
   shadowform    = { format = "form:1",         filter = ClassFilter("PRIEST") },
   noshadowform  = { format = "noform",         filter = ClassFilter("PRIEST") },
   battle        = { format = "stance:1",       filter = ClassFilter("WARRIOR") },
@@ -761,7 +761,7 @@
   cat           = { format = "form:3",         filter = ClassFilter("DRUID") },
   tree          = { format = "form:5",         filter = ClassFilter("DRUID") },
   moonkin       = { format = "form:5",         filter = ClassFilter("DRUID") },
-  demon         = { format = "form:2",         filter = ClassFilter("WARLOCK") },
+  demon         = { format = "form:1",         filter = ClassFilter("WARLOCK") },
   nodemon       = { format = "noform",         filter = ClassFilter("WARLOCK") },
   pet           = { format = "pet" },
   nopet         = { format = "nopet" },
@@ -776,8 +776,9 @@
   solo          = { format = "nogroup" },
   combat        = { format = "combat" },
   nocombat      = { format = "nocombat" },
-  possess       = { format = "@vehicle,noexists,bonusbar:5" },
-  vehicle       = { format = "@vehicle,exists,bonusbar:5" },
+  possess       = { format = "overridebar" },
+  extrabar      = { format = "extrabar" },
+  vehicle       = { format = "vehicleui" },
 }
 
 function Bar.InitRuleFormats()
--- a/Button.lua	Fri Aug 05 16:28:13 2011 -0700
+++ b/Button.lua	Wed Nov 14 16:47:27 2012 -0800
@@ -204,6 +204,10 @@
   if not poolID or not maxID then
     error("AcquireActionID: must setup pool first with SetActionIDPool")
   end
+  hint = tonumber(hint)
+  if hint and (hint < 1 or hint > maxID) then
+    hint = nil
+  end
   local pool = idPools[poolID]
   if not pool then
     pool = { nWraps = 0, useCount = { } }
@@ -216,19 +220,19 @@
   if id == nil then
     repeat
       local nWraps = pool.nWraps or 0
-      if hint and (useCount[hint] == nil or useCount[hint] == nWraps) then
+      if hint and (useCount[hint] == 0 or useCount[hint] == nWraps) then
         id = hint
       else
         local start = hint or 1
         for i = start, maxID do
-          if useCount[i] == nil or useCount[i] == nWraps then
+          if useCount[i] == 0 or useCount[i] == nWraps then
             id = i
             break
           end
         end
         if not id then
           for i = 1, start do
-            if useCount[i] == nil or useCount[i] == nWraps then
+            if useCount[i] == 0 or useCount[i] == nWraps then
               id = i
               break
             end
--- a/Editor.lua	Fri Aug 05 16:28:13 2011 -0700
+++ b/Editor.lua	Wed Nov 14 16:47:27 2012 -0800
@@ -494,12 +494,12 @@
         set   = "SetNumPages",
       },
       mindcontrol = {
-        name = L["Mind Control Support"],
-        desc = L["When possessing a target (e.g. via Mind Control), map the first 12 buttons of this bar to the possessed target's actions."],
+        name = L["Override Button Support"],
+        desc = L["Override the first 12 buttons on this bar with abilities gained from certain game quests and events."],
         order = 2,
         type = "toggle",
-        set = "SetMindControl",
-        get = "GetMindControl",
+        set = "SetMindControl", -- called 'mind control' for legacy reasons
+        get = "GetMindControl", -- called 'mind control' for legacy reasons
       },
       vehicle = {
         name = L["Vehicle Support"],
@@ -1037,8 +1037,9 @@
     { "pet",     { {pet = L["With Pet"]}, {nopet = L["Without Pet"]} } },
     { "target",  { {harm = L["Hostile Target"]}, {help = L["Friendly Target"]}, {notarget = L["No Target"]} } },
     { "focus",   { {focusharm = L["Hostile Focus"]}, {focushelp = L["Friendly Focus"]}, {nofocus = L["No Focus"]} } },
-    { "possess", { {possess = L["Mind Control"]} } },
     { "vehicle", { {vehicle = L["In a Vehicle"]} } },
+    { "possess", { {possess = L["Ability Override"]} } },
+    { "extrabar",{ {extrabar = L["Has Special Action"]} } },
     { "group",   { {raid = L["Raid"]}, {party = L["Party"]}, {solo = L["Solo"]} } },
     { "combat",  { {combat = L["In Combat"]}, {nocombat = L["Out of Combat"]} } },
   }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ExtraActionButton.lua	Wed Nov 14 16:47:27 2012 -0800
@@ -0,0 +1,114 @@
+local _, ns = ...
+local ReAction = ns.ReAction
+local L = ReAction.L
+local format = string.format
+
+--
+-- ExtraAction Button class
+--
+local buttonTypeID = "ExtraAction"
+local Super = ReAction.Button.Action
+local ExtraAction = setmetatable(
+  { 
+    defaultBarConfig = { 
+      type = buttonTypeID ,
+      btnWidth = 52,
+      btnHeight = 52,
+      btnRows = 1,
+      btnColumns = 1,
+      spacing = 3,
+      buttons = { }
+    },
+
+    barType = L["Special Action Button"], 
+    buttonTypeID = buttonTypeID
+  }, 
+  { __index = Super } )
+
+ReAction.Button.ExtraAction = ExtraAction
+ReAction:RegisterBarType(ExtraAction)
+
+function ExtraAction:New( config, bar, idx, idHint )
+  -- don't invoke the base ActionButton constructor, since it does a bunch of stuff that
+  -- we don't need. Instead, call the Button base constructor directly instead and
+  -- re-implement the bits of the ActionButton constructor that we actually need.
+  self = ReAction.Button.New(self, config, bar, idx, "SecureActionButtonTemplate, ActionButtonTemplate" )
+
+  self.barConfig = bar:GetConfig()
+
+  local f = self:GetFrame()
+  local barFrame = bar:GetFrame()
+
+  self.rangeTimer = TOOLTIP_UPDATE_TIME
+
+  self:SetActionIDPool("special-action",1)
+  config.actionID = self:AcquireActionID(config.actionID, idHint, true)
+  self.nPages = 1
+
+  -- compute the actual game action-ID from the extra bar offset page
+  local actionID = config.actionID + (GetExtraBarIndex() - 1)*NUM_ACTIONBAR_BUTTONS
+  self.actionID = actionID
+  f.action = actionID
+
+  -- attribute setup
+  f:SetAttribute("type","action")
+  f:SetAttribute("checkselfcast", true)
+  f:SetAttribute("checkfocuscast", true)
+  f:SetAttribute("action", actionID)
+  f:SetAttribute("default-action", actionID)
+  f:SetAttribute("bar-idx",idx)
+
+  -- non secure scripts
+  f:SetScript("OnEvent", function(frame, ...) self:OnEvent(...) end)
+  f:SetScript("OnEnter", function(frame) self:OnEnter() end)
+  f:SetScript("OnLeave", function(frame) self:OnLeave() end)
+  f:SetScript("OnAttributeChanged", function(frame, attr, value) self:OnAttributeChanged(attr, value) end)
+  f:SetScript("PostClick", function(frame, ...) self:PostClick(...) end)
+  f:SetScript("OnUpdate", function(frame, elapsed) self:OnUpdate(elapsed) end)
+
+  -- event registration
+  for _, evt in pairs(self.eventList) do
+    f:RegisterEvent(evt)
+  end
+
+  -- register to use the C cooldown implementation
+  SetActionUIButton(f, actionID, f.cooldown)
+
+  -- attach to skinner
+  bar:SkinButton(self)
+
+  self:Refresh()
+
+  return self
+end
+
+function ExtraAction:Destroy()
+  -- similarly, don't call the ActionButton destructor function, call the Button destructor function
+  ReAction.Button.Destroy(self)
+end
+
+function ExtraAction:SetupBar(bar)
+  -- again don't call the ActionButton:SetupBar method, because it does a lot of things we don't need/want.
+  -- however the Button base class SetupBar method does need to be called.
+  ReAction.Button.SetupBar(self,bar)
+
+  -- show/hide the bar based on whether we have an extrabar. Hook into the unitexists mechanism that the pet bar uses.
+  RegisterStateDriver(bar:GetFrame(), "unitexists", "[extrabar] show ; hide")
+end
+
+function ExtraAction:ShowGrid()
+  -- override: do nothing
+end
+
+function ExtraAction:ShowGridTemp( show )
+  -- override: do nothing
+end
+
+function ExtraAction:SetActionID()
+  -- override: action ID never changes
+end
+
+function ExtraAction:RefreshPages()
+  -- override: action ID never changes
+end
+
--- a/MultiCastButton.lua	Fri Aug 05 16:28:13 2011 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,785 +0,0 @@
-local _, ns = ...
-local ReAction = ns.ReAction
-local L = ReAction.L
-local _G = _G
-local CreateFrame = CreateFrame
-local format = string.format
-local unpack = unpack
-local GetCVar = GetCVar
-local GameTooltip_SetDefaultAnchor = GameTooltip_SetDefaultAnchor
-local InCombatLockdown = InCombatLockdown
-local IsUsableSpell = IsUsableSpell
-local IsUsableAction = IsUsableAction
-local IsSpellKnown = IsSpellKnown
-local IsSpellInRange = IsSpellInRange
-local IsActionInRange = IsActionInRange
-local GetSpellInfo = GetSpellInfo
-local GetSpellCooldown = GetSpellCooldown
-local GetActionCooldown = GetActionCooldown
-local GetSpellTexture = GetSpellTexture
-local GetActionTexture = GetActionTexture
-local GetMultiCastTotemSpells = GetMultiCastTotemSpells
-
---@do-not-package@
---[[
-  Blizzard Constants:
-    - NUM_MULTI_CAST_BUTTONS_PER_PAGE = 4
-    - NUM_MULTI_CAST_PAGES = 3
-    - SHAMAN_TOTEM_PRIORITIES = { } -- sets the order of the totems
-    - TOTEM_MULTI_CAST_SUMMON_SPELLS = { } -- list of summon spellIDs
-    - TOTEM_MULTI_CAST_RECALL_SPELLS = { } -- list of recall spellIDs
-
-  Blizzard Events:
-    - UPDATE_MULTI_CAST_ACTIONBAR
-
-  Blizzard APIs:
-    - GetMultiCastBarOffset() : returns 6
-
-    - SetMultiCastSpell(actionID, spellID) (protected) OR
-         SetAttribute("type","multispell")
-         SetAttribute("action",actionID)
-         SetAttribute("spell",spellID)
-
-         note: multicast actionID page is NUM_ACTIONBAR_PAGES + GetMultiCastBarOffset(),
-               so that's action ID 132-144.
-
-    - spell1, spell2, spell3, ... = GetMultiCastTotemSpells(slot)
-        returns spellIDs for all known totems that fit that slot. This function is available in
-        the secure environment.
-
-  Blizzard textures:
-    All the textures for the multicast bar (arrows, empty-slot icons, etc) are part of a single
-    texture: each texture uses SetTexCoord() to display only a slice of the textures. I suppose
-    this is to slightly optimize texture load performance, but it makes the UI code more clumsy.
-
-    Each totem button and arrow has a colored border indicating its elemental type.
-
-    TODO: 
-      - make whether to show the colored border configurable (looks really bad with ButtonFacade:Zoomed)
-      - apply ButtonFacade to the flyout buttons? Or at least zoom the textures slightly?
-      - use a multiplier with SetTexCoord on totem bar texture?
-
-  Design Notes:
-    - Only the header has a secure context. All other frames execute in its context.
-
-    - Each button is either type "spell" (summon/recall) or type "action" (totem action IDs are 
-      GetBonusBarOffset()=6, 132-144) with 3 pages of 4 buttons. The paging is controlled by
-      the summon flyout, which is also paged with the summon spells (the recall button is not paged)
-    
-    - A spell list is updated in the secure context at setup time (TODO: redo setup when learning new 
-      spells) with the list of spells known for each slot.
-    
-    - Each button (except recall) has an arrow button which appears on mouseover and when clicked
-      opens the flyout via a wrapped OnClick handler. When the flyout is open, the arrow does not
-      appear.
-    
-    - A single flyout with N+1 (1 slot is to select no totem for the set) flyout-buttons is a child
-      of the bar. Each time the flyout panel is opened, the individual  buttons grab their corresponding
-      spell/type from the list, according to the slot which opened the flyout. Each button either sets
-      the current page (summon) or sets a multispell to an actionID via type="multispell". None of them
-      actually cast any spells (though, I suppose we could modify this so that e.g. configurable 
-      right-click casts the spell). The flyout also has a close button which closes the flyout: the
-      flyout-open code positions the close button anchored to the last button in the flyout (which 
-      changes dynamically because each slot has a different number of items in the list).
-
-    - Multicast sets are not stances, there's no need (or ability) to handle swapping sets if one of 
-      the summon spells is cast from elsewhere.
-
-    - The default UI has Call of the Elements always selected on UI load. This module remembers the last
-      selected one and restores it.
-
-
-]]--
---@end-do-not-package@
-
---
--- Secure snippets
---
-
--- bar
-local _bar_init = -- function(self)
-[[
-  -- set up some globals in the secure environment
-  flyout               = self:GetFrameRef("flyout")
-  flyoutSlot           = nil
-  summonSlot           = self:GetAttribute("summonSlot")
-  recallSlot           = self:GetAttribute("recallSlot")
-  baseActionID         = self:GetAttribute("baseActionID")
-  slotsPerPage         = self:GetAttribute("slotsPerPage")
-  currentPage          = currentPage or self:GetAttribute("lastSummon") or 1
-
-  totemIDsBySlot = newtable()
-  for i = 1, slotsPerPage do
-    totemIDsBySlot[i] = self:GetAttribute("TOTEM_PRIORITY_"..i)
-  end
-
-  -- these are set up in bar:SetupBar()
-  flyoutChildren = flyoutChildren or newtable()
-  summonSpells = summonSpells or newtable()
-]]
-
-local _onstate_multispellpage = -- function(self, stateid, newstate)
-[[
-  currentPage = tonumber(newstate)
-  control:CallMethod("UpdateLastSummon",currentPage)
-  control:ChildUpdate()
-]]
-
-
--- buttons
-local _childupdate = -- function(self, snippetid, message)
-[[
-  local t = self:GetAttribute("type")
-  self:SetAttribute(t, self:GetAttribute(t.."-page"..currentPage))
-]]
-
-local _onEnter = -- function(self)
-  -- for whatever reason, RegisterAutoHide is unreliable
-  -- unless you re-anchor the frame prior to calling it.
-  -- Even then, it's still not terribly reliable.
-[[
-  local slot = self:GetAttribute("bar-idx")
-  local arrow = owner:GetFrameRef("arrow-"..slot)
-  if arrow and not arrow:IsShown() and not (flyout:IsVisible() and flyoutSlot == slot) then
-    arrow:ClearAllPoints()
-    arrow:SetPoint("BOTTOM",self,"TOP",0,0)
-    arrow:Show()
-    arrow:RegisterAutoHide(0)
-    arrow:AddToAutoHide(self)
-  end
-]]
-
-
--- flyout arrow
-local _arrow_openFlyout = -- function(self)
-[[
-  local slot = self:GetAttribute("bar-idx")
-  local totemID = totemIDsBySlot[slot - (summonSlot or 0)]
-  if totemID == 0 then
-    totemID = "summon"
-  end
-
-  local lastButton, lastPage
-  for page, b in ipairs(flyoutChildren) do
-    b:Hide()
-    b:SetAttribute("totemSlot",totemID)
-    if slot == summonSlot then
-      local spellID = self:GetParent():GetAttribute("spell-page"..page)
-      if spellID then
-        b:SetAttribute("type","changePage")
-        b:SetAttribute("spell",spellID)
-        b:Show()
-        lastButton = b
-        lastPage = page
-      end
-    else
-      local spell = select(page, 0, GetMultiCastTotemSpells(totemID) )
-      if spell then
-        b:SetAttribute("type","multispell")
-        b:SetAttribute("action", baseActionID + (currentPage - 1)*slotsPerPage + totemID)
-        b:SetAttribute("spell", spell)
-        b:Show()
-        lastButton = b
-        lastPage = page
-      end
-    end
-  end
-
-  local close = owner:GetFrameRef("close")
-  if lastButton and close then
-    close:ClearAllPoints()
-    close:SetPoint("BOTTOM",lastButton,"TOP",0,0) -- TODO: better anchoring
-    close:Show()
-    control:CallMethod("UpdateFlyoutTextures",totemID)
-  end
-
-  flyout:ClearAllPoints()
-  flyout:SetPoint("BOTTOM",self,"BOTTOM",0,0)  -- TODO: better anchoring
-  if lastPage then
-    flyout:SetHeight(lastPage * 27 + (close and close:GetHeight() or 0))
-  end
-  flyout:Show()
-  flyout:RegisterAutoHide(1) -- TODO: configurable
-  flyout:AddToAutoHide(owner)
-  flyoutSlot = slot
-  self:Hide()
-]]
-
-local _closeFlyout = -- function(self)
-[[
-  flyout:Hide()
-]]
-
-
--- flyout child buttons
-local _flyout_child_preClick = -- function(self, button, down)
-[[
-  local button = button
-  if self:GetAttribute("type") == "changePage" then
-    owner:SetAttribute("state-multispellpage",self:GetAttribute("index"))
-    self:GetParent():Hide()
-    return false
-  else
-    return nil, "close"
-  end
-]]
-
-local _flyout_child_postClick = -- function(self, message, button, down)
-[[
-  if message == "close" then
-    self:GetParent():Hide() -- hide flyout after selecting
-  end
-]]
-
-
---
--- The Blizzard totem bar textures are all actually one big texture,
--- with texcoord offsets. Shamelessly stolen from FrameXML/MultiCastActionBarFrame.lua
---
-local TOTEM_TEXTURE = "Interface\\Buttons\\UI-TotemBar"
-local FLYOUT_UP_BUTTON_HL_TCOORDS   = { 72/128,  92/128, 88/256,  98/256 }
-local FLYOUT_DOWN_BUTTON_HL_TCOORDS = { 72/128,  92/128, 69/256,  79/256 }
-
-local SLOT_EMPTY_TCOORDS = {
-  [EARTH_TOTEM_SLOT] = {  66/128,  96/128,   3/256,  33/256 },
-	[FIRE_TOTEM_SLOT]  = {  67/128,  97/128, 100/256, 130/256 },
-	[WATER_TOTEM_SLOT] = {  39/128,  69/128, 209/256, 239/256 },
-	[AIR_TOTEM_SLOT]   = {  66/128,  96/128,  36/256,  66/256 },
-}
-
-local SLOT_OVERLAY_TCOORDS = {
-	[EARTH_TOTEM_SLOT] = {   1/128,  35/128, 172/256, 206/256 },
-	[FIRE_TOTEM_SLOT]  = {  36/128,  70/128, 172/256, 206/256 },
-	[WATER_TOTEM_SLOT] = {   1/128,  35/128, 207/256, 240/256 },
-	[AIR_TOTEM_SLOT]   = {  36/128,  70/128, 137/256, 171/256 },
-}
-
-local FLYOUT_UP_BUTTON_TCOORDS = {
-	["summon"]         = {  99/128, 127/128,  84/256, 102/256 },
-	[EARTH_TOTEM_SLOT] = {  99/128, 127/128, 160/256, 178/256 },
-	[FIRE_TOTEM_SLOT]  = {  99/128, 127/128, 122/256, 140/256 },
-	[WATER_TOTEM_SLOT] = {  99/128, 127/128, 199/256, 217/256 },
-	[AIR_TOTEM_SLOT]   = {  99/128, 127/128, 237/256, 255/256 },
-}
-
-local FLYOUT_DOWN_BUTTON_TCOORDS = {
-	["summon"]         = {  99/128, 127/128,  65/256,  83/256 },
-	[EARTH_TOTEM_SLOT] = {  99/128, 127/128, 141/256, 159/256 },
-	[FIRE_TOTEM_SLOT]  = {  99/128, 127/128, 103/256, 121/256 },
-	[WATER_TOTEM_SLOT] = {  99/128, 127/128, 180/256, 198/256 },
-	[AIR_TOTEM_SLOT]   = {  99/128, 127/128, 218/256, 236/256 },
-}
-
-local FLYOUT_TOP_TCOORDS = {
-	["summon"]         = {  33/128,  65/128,   1/256,  23/256 },
-	[EARTH_TOTEM_SLOT] = {   0/128,  32/128,  46/256,  68/256 },
-	[FIRE_TOTEM_SLOT]  = {  33/128,  65/128,  46/256,  68/256 },
-	[WATER_TOTEM_SLOT] = {   0/128,  32/128,   1/256,  23/256 },
-	[AIR_TOTEM_SLOT]   = {   0/128,  32/128,  91/256, 113/256 },
-}
-
-local FLYOUT_MIDDLE_TCOORDS = {
-	["summon"]         = {  33/128,  65/128,  23/256,  43/256 },
-	[EARTH_TOTEM_SLOT] = {   0/128,  32/128,  68/256,  88/256 },
-	[FIRE_TOTEM_SLOT]  = {  33/128,  65/128,  68/256,  88/256 },
-	[WATER_TOTEM_SLOT] = {   0/128,  32/128,  23/256,  43/256 },
-	[AIR_TOTEM_SLOT]   = {   0/128,  32/128, 113/256, 133/256 },
-}
-
-local eventList = { 
-  "ACTIONBAR_SLOT_CHANGED",
-  "ACTIONBAR_UPDATE_STATE",
-  "ACTIONBAR_UPDATE_USABLE",
-  "ACTIONBAR_UPDATE_COOLDOWN",
-  "UPDATE_BINDINGS",
-  "UPDATE_MULTI_CAST_ACTIONBAR",
-}
-
---
--- MultiCast Button class
--- Inherits implementation methods from Action button class, but circumvents the constructor
--- and redefines/removes some methods.
---
-local buttonTypeID = "Totem"
-local Super = ReAction.Button
-local Action = ReAction.Button.Action
-local MultiCast = setmetatable( 
-  { 
-    defaultBarConfig = { 
-      type = buttonTypeID,
-      btnWidth = 36,
-      btnHeight = 36,
-      btnRows = 1,
-      btnColumns = 6,
-      spacing = 3,
-      buttons = { }
-    },
-
-    barType = L["Totem Bar"], 
-    buttonTypeID = buttonTypeID
-  },
-  { __index = Action } )
-
-ReAction.Button.MultiCast = MultiCast
-ReAction:RegisterBarType(MultiCast)
-
-function MultiCast:New( btnConfig, bar, idx )
-  if idx < 1 or idx > NUM_MULTI_CAST_BUTTONS_PER_PAGE + 2 then
-    ReAction:UserError(L["All %s buttons are in use for this bar, cannot create any more buttons"]:format(self.barType))
-    error(nil)
-  end
-
-  self = Super.New(self, btnConfig, bar, idx, "SecureActionButtonTemplate, ActionButtonTemplate" )
-
-  if not bar.hasMulticast or idx > bar.maxIndex then
-    -- Not enough multicast capability to use this button
-    self:Refresh()
-    return self
-  end
-
-  local barFrame = bar:GetFrame()
-  local f = self:GetFrame()
-
-  -- attributes
-  local page = (idx == bar.recallSlot) and 1 or bar:GetConfig().lastSummon or 1
-  if idx == bar.recallSlot or idx == bar.summonSlot then
-    f:SetAttribute("type","spell")
-    local spells = (idx == bar.summonSlot) and TOTEM_MULTI_CAST_SUMMON_SPELLS or TOTEM_MULTI_CAST_RECALL_SPELLS
-    f:SetAttribute("spell",spells[page])
-    for i, spell in ipairs(spells) do 
-      if spell and IsSpellKnown(spell) then
-        f:SetAttribute("spell-page"..i, spell)
-      end
-    end
-  else
-    local offset = bar.summonSlot and 1 or 0
-    local slot = SHAMAN_TOTEM_PRIORITIES[idx - offset]
-    local baseAction = barFrame:GetAttribute("baseActionID") + slot
-    self.totemSlot = slot
-    f:SetAttribute("type","action")
-    f:SetAttribute("action", baseAction + (page - 1) * NUM_MULTI_CAST_BUTTONS_PER_PAGE)
-    for i = 1, NUM_MULTI_CAST_PAGES do
-      f:SetAttribute("action-page"..i, baseAction + (i-1) * NUM_MULTI_CAST_BUTTONS_PER_PAGE)
-    end
-    if not f.overlayTex then
-      local tx = f:CreateTexture("OVERLAY")
-      tx:SetTexture(TOTEM_TEXTURE)
-      tx:SetTexCoord(unpack(SLOT_OVERLAY_TCOORDS[self.totemSlot]))
-      tx:SetWidth(34)
-      tx:SetHeight(34)
-      tx:SetPoint("CENTER")
-      tx:Show()
-      f.overlayTex = tx
-    end
-  end
-  f:SetAttribute("bar-idx",idx)
-
-  -- non secure scripts
-  f:SetScript("OnEvent", function(frame, ...) self:OnEvent(...) end)
-  f:SetScript("OnEnter", function(frame) self:OnEnter() end)
-  f:SetScript("OnLeave", function(frame) self:OnLeave() end)
-  f:SetScript("OnAttributeChanged", function(frame, attr, value) self:OnAttributeChanged(attr, value) end)
-  f:SetScript("PostClick", function(frame, ...) self:PostClick(...) end)
-
-  -- secure handlers
-  if idx ~= bar.recallSlot then
-    f:SetAttribute("_childupdate",_childupdate)
-  end
-  barFrame:WrapScript(f, "OnEnter", _onEnter)
-
-  -- event registration
-  for _, evt in pairs(eventList) do
-    f:RegisterEvent(evt)
-  end
-
-  -- Set up a proxy for the icon texture for use with ButtonFacade
-  local SetTexCoordRaw = self.frames.icon.SetTexCoord
-  self.frames.icon.SetTexCoord = function( tx, ... )
-    if self:GetIconTexture() == TOTEM_TEXTURE then
-      SetTexCoordRaw(tx,select(2,self:GetIconTexture()))
-    else
-      SetTexCoordRaw(tx,...)
-    end
-  end
-
-  -- attach to skinner
-  bar:SkinButton(self)
-
-  f:Show()
-
-  -- open arrow and flyout background textures
-  if idx ~= bar.recallSlot then
-    local arrow = f._arrowFrame or CreateFrame("Button", nil, f, "SecureFrameTemplate")
-    f._arrowFrame = arrow
-    arrow:SetWidth(28)
-    arrow:SetHeight(18)
-    arrow:SetPoint("BOTTOM",self:GetFrame(),"TOP",0,0) -- TODO: better anchoring
-    arrow:SetNormalTexture(TOTEM_TEXTURE)
-    local slot = self.totemSlot or "summon"
-    arrow:GetNormalTexture():SetTexCoord( unpack(FLYOUT_UP_BUTTON_TCOORDS[slot]) )
-    arrow:SetHighlightTexture(TOTEM_TEXTURE)
-    arrow:GetHighlightTexture():SetTexCoord( unpack(FLYOUT_UP_BUTTON_HL_TCOORDS) )
-    arrow:SetAttribute("bar-idx",idx)
-    arrow:Hide()
-    barFrame:WrapScript(arrow, "OnClick", _arrow_openFlyout)
-    barFrame:SetFrameRef("arrow-"..idx,arrow)
-  end
-
-  self:Refresh()
-
-  return self
-end
-
-function MultiCast:Destroy()
-  local barFrame = self.bar:GetFrame()
-  local f = self:GetFrame()
-  pcall( barFrame.UnwrapScript, barFrame, f, "OnEnter" ) -- ignore errors
-  if f._arrowFrame then
-    pcall( barFrame.UnwrapScript, barFrame, f._arrowFrame,"OnClick" ) -- ignore errors
-  end
-  Super.Destroy(self)
-end
-
-function MultiCast:Refresh()
-  Super.Refresh(self)
-  self:UpdateAction()
-
-  local bar = self.bar
-  if bar.hasMulticast == true and self.idx <= bar.maxIndex or ReAction:GetConfigMode() then
-    self:GetFrame():Show()
-  else
-    self:GetFrame():Hide()
-  end
-end
-
-function MultiCast:ShowGrid( show )
-end
-
-function MultiCast:ShowGridTemp( show )
-end
-
-function MultiCast:AcquireActionID()
-end
-
-function MultiCast:ReleaseActionID()
-end
-
-function MultiCast:UpdateShowGrid()
-end
-
-function MultiCast:UpdateBorder()
-end
-
-function MultiCast:UpdateMacroText()
-end
-
-function MultiCast:UpdateCount()
-end
-
-function MultiCast:UpdateCheckedState()
-  local action = self:GetActionID()
-  if action and IsCurrentAction(action) then
-    self:GetFrame():SetChecked(1)
-  else
-    self:GetFrame():SetChecked(0)
-  end
-end
-
-function MultiCast:RefreshHasActionAttributes()
-end
-
-function MultiCast:UpdateFlash()
-end
-
-function MultiCast:GetIconTexture()
-  local tx
-  if self.spellID then
-    tx = GetSpellTexture(GetSpellInfo(self.spellID))
-  elseif self.actionID then
-    tx = GetActionTexture(self.actionID)
-  end
-  if tx then
-    return tx
-  else
-    return TOTEM_TEXTURE, unpack(SLOT_EMPTY_TCOORDS[self.totemSlot or 1])
-  end
-end
-
-function MultiCast:UpdateAction()
-  local action = self:GetActionID()
-  if action then
-    if action ~= self.actionID then
-      self.actionID = action
-      self:UpdateAll()
-    end
-  else
-    local spellID = self:GetSpellID()
-    if spellID ~= self.spellID then
-      self.spellID = spellID
-      self:UpdateAll()
-    end
-  end
-end
-
-function MultiCast:GetActionID(page)
-  return self:GetFrame():GetAttribute("action")
-end
-
-function MultiCast:GetSpellID(page)
-  return self:GetFrame():GetAttribute("spell")
-end
-
-function MultiCast:SetActionID( id  )
-  error("Can not set action ID of multicast buttons")
-end
-
-function MultiCast:SetTooltip()
-  local barFrame = self:GetFrame()
-  if GetCVar("UberTooltips") == "1" then
-    GameTooltip_SetDefaultAnchor(GameTooltip, barFrame)
-  else
-    GameTooltip:SetOwner(barFrame)
-  end
-  if self.spellID then
-    GameTooltip:SetSpellByID(self.spellID,false,true)
-  elseif self.actionID then
-    GameTooltip:SetAction(self.actionID)
-  end
-end
-
-function MultiCast:GetUsable()
-  if self.spellID then
-    return IsUsableSpell((GetSpellInfo(self.spellID)))
-  elseif self.actionID then
-    return IsUsableAction(self.actionID)
-  end
-end
-
-function MultiCast:GetInRange()
-  if self.spellID then
-    return IsSpellInRange((GetSpellInfo(self.spellID))) == 0
-  elseif self.actionID then
-    return IsActionInRange(self.actionID) == 0
-  end
-end
-
-function MultiCast:GetCooldown()
-  if self.spellID then
-    return GetSpellCooldown((GetSpellInfo(self.spellID)))
-  elseif self.actionID then
-    return GetActionCooldown(self.actionID)
-  else
-    return 0, 0, 0
-  end
-end
-
-function MultiCast:UPDATE_MULTI_CAST_ACTIONBAR()
-  self:UpdateAll()
-end
-
-
---
--- flyout setup
---
-local function ShowFlyoutTooltip(frame)
-  if GetCVar("UberTooltips") == "1" then
-    GameTooltip_SetDefaultAnchor(GameTooltip, frame)
-  else
-    GameTooltip:SetOwner(frame)
-  end
-  local spell = frame:GetAttribute("spell")
-  if spell == nil or spell == 0 then
-    GameTooltip:SetText(MULTI_CAST_TOOLTIP_NO_TOTEM, HIGHLIGHT_FONT_COLOR.r, HIGHLIGHT_FONT_COLOR.g, HIGHLIGHT_FONT_COLOR.b)
-  else
-    GameTooltip:SetSpellByID(spell,false,true)
-  end
-end
-
-local function HideFlyoutTooltip()
-  GameTooltip:Hide()
-end
-
-local function UpdateFlyoutIcon(frame)
-  local spellID = frame:GetAttribute("spell")
-  if spellID == 0 or spellID == nil then
-    frame.icon:SetTexture(TOTEM_TEXTURE)
-    local slot = tonumber(frame:GetAttribute("totemSlot")) or 1
-    frame.icon:SetTexCoord( unpack(SLOT_EMPTY_TCOORDS[slot]) )
-  else
-    frame.icon:SetTexture(GetSpellTexture(GetSpellInfo(spellID)))
-    frame.icon:SetTexCoord(0,1,0,1)
-  end
-end
-
-function MultiCast:SetupBar( bar )
-  local slot = 0
-  local nTotemSlots = 0
-  local summonSlot = nil
-  local recallSlot = nil
-
-  -- figure out the capabilities of the character
-	for i, spell in ipairs(TOTEM_MULTI_CAST_SUMMON_SPELLS) do 
-    if spell and IsSpellKnown(spell) then
-      slot = 1
-      summonSlot = 1
-    end
-	end
-
-  for i = 1, NUM_MULTI_CAST_BUTTONS_PER_PAGE do
-		local totem = SHAMAN_TOTEM_PRIORITIES[i];
-		if GetTotemInfo(totem) and GetMultiCastTotemSpells(totem) then
-      nTotemSlots = nTotemSlots + 1
-      slot = slot + 1
-    end
-  end
-
-  slot = slot + 1
-	for i, spell in ipairs(TOTEM_MULTI_CAST_RECALL_SPELLS) do 
-    if spell and IsSpellKnown(spell) then
-      recallSlot = slot
-    end
-	end
-
-  local maxIndex = nTotemSlots
-  if summonSlot then
-    maxIndex = maxIndex + 1
-  end
-  if recallSlot then
-    maxIndex = maxIndex + 1
-  end
-
-  bar.hasMulticast = nTotemSlots > 0
-  bar.summonSlot   = summonSlot
-  bar.recallSlot   = recallSlot
-  bar.nTotemSlots  = nTotemSlots
-  bar.maxIndex     = maxIndex
-
-  if bar.hasMulticast == false then
-    Super.SetupBar(self,bar)
-    return -- no multicast capability
-  end
-
-  local f = bar:GetFrame()
-
-  -- init bar secure environment
-  f:SetAttribute("lastSummon", bar:GetConfig().lastSummon)
-  f:SetAttribute("summonSlot", summonSlot)
-  f:SetAttribute("recallSlot", recallSlot)
-  f:SetAttribute("slotsPerPage", NUM_MULTI_CAST_BUTTONS_PER_PAGE)
-  f:SetAttribute("baseActionID", (NUM_ACTIONBAR_PAGES + GetMultiCastBarOffset() - 1)*NUM_ACTIONBAR_BUTTONS)
-  for i, p in ipairs(SHAMAN_TOTEM_PRIORITIES) do
-    f:SetAttribute("TOTEM_PRIORITY_"..i,p)
-  end
-  f:SetAttribute("_onstate-multispellpage", _onstate_multispellpage)
-
-  function f:UpdateLastSummon(value)
-    bar:GetConfig().lastSummon = value
-  end
-
-  -- create flyout container frame and close arrow
-  local flyout = bar._flyoutFrame
-  if not flyout then
-    flyout = CreateFrame("Frame", nil, f, "SecureFrameTemplate")
-    bar._flyoutFrame = flyout
-    f:SetFrameRef("flyout",flyout)
-    flyout.buttons = { }
-    flyout:Hide()
-    flyout:SetWidth(24)
-    flyout:SetHeight(1)
-    flyout:SetPoint("BOTTOM",f,"TOP",0,0)
-
-    local close = CreateFrame("Button", nil, flyout, "SecureFrameTemplate")
-    close:SetWidth(28)
-    close:SetHeight(18)
-    close:SetPoint("BOTTOM",flyout,"TOP")
-    close:SetNormalTexture(TOTEM_TEXTURE)
-    close:GetNormalTexture():SetTexCoord(unpack(FLYOUT_DOWN_BUTTON_TCOORDS["summon"]))
-    close:SetHighlightTexture(TOTEM_TEXTURE)
-    close:GetHighlightTexture():SetTexCoord( unpack(FLYOUT_DOWN_BUTTON_HL_TCOORDS) )
-    f:SetFrameRef("close",close)
-    f:WrapScript(close, "OnClick", _closeFlyout)
-    close:Show()
-
-    local midTx = flyout:CreateTexture("BACKGROUND")
-    midTx:SetWidth(32)
-    midTx:SetHeight(20)
-    midTx:SetPoint("BOTTOM")
-    midTx:SetTexture(TOTEM_TEXTURE)
-    midTx:SetTexCoord(unpack(FLYOUT_MIDDLE_TCOORDS["summon"]))
-    midTx:Show()
-
-    local topTx = flyout:CreateTexture("BACKGROUND")
-    topTx:SetWidth(32)
-    topTx:SetHeight(20)
-    topTx:SetTexture(TOTEM_TEXTURE)
-    midTx:SetTexCoord(unpack(FLYOUT_TOP_TCOORDS["summon"]))
-    topTx:SetPoint("BOTTOM",midTx,"TOP",0,-10)
-    topTx:Show()
-
-    function flyout:UpdateTextures(slot)
-      slot = slot or "summon"
-      close:GetNormalTexture():SetTexCoord(unpack(FLYOUT_DOWN_BUTTON_TCOORDS[slot]))
-      midTx:ClearAllPoints()
-      midTx:SetPoint("BOTTOM")
-      midTx:SetPoint("TOP",close,"BOTTOM",0,0)
-      midTx:SetTexCoord(unpack(FLYOUT_MIDDLE_TCOORDS[slot]))
-      topTx:SetTexCoord(unpack(FLYOUT_TOP_TCOORDS[slot]))
-    end
-
-    -- create flyout buttons
-    for i = 1, 10 do -- maximum 9 spells + 1 empty slot
-      local b = CreateFrame("Button",nil,flyout,"SecureActionButtonTemplate")
-      b:SetWidth(24)
-      b:SetHeight(24)
-      local prev = flyout.buttons[i-1]
-      b:SetPoint("BOTTOM", prev or flyout, prev and "TOP" or "BOTTOM", 0, 3) -- TODO: better anchoring
-      b.icon = b:CreateTexture("BACKGROUND")
-      b.icon:SetAllPoints()
-      b.icon:Show()
-      b:SetHighlightTexture("Interface\\Buttons\\ButtonHilight-Square")
-      b:GetHighlightTexture():SetBlendMode("ADD")
-      b:EnableMouse(true)
-      b:RegisterForClicks(bar:GetConfig().clickDown and "AnyDown" or "AnyUp")
-      b:SetScript("OnShow",UpdateFlyoutIcon)
-      b:SetScript("OnEnter",ShowFlyoutTooltip)
-      b:SetScript("OnLeave",HideFlyoutTooltip)
-      b:SetAttribute("index",i)
-      f:SetAttribute("flyout-child-idx",i)
-      f:SetFrameRef("flyout-child",b)
-      f:Execute([[
-          flyoutChildren = flyoutChildren or newtable()
-          flyoutChildren[self:GetAttribute("flyout-child-idx")] = self:GetFrameRef("flyout-child")
-        ]])
-      f:WrapScript(b, "OnClick", _flyout_child_preClick, _flyout_child_postClick)
-      b:Show()
-      flyout.buttons[i] = b
-    end
-  end
-
-  -- scale flyout frame
-  local scale = bar:GetButtonSize() / 36
-  flyout:SetScale(scale)
-
-  function f:UpdateFlyoutTextures(slot)
-    flyout:UpdateTextures(slot)
-  end
-
-  -- re-execute setup when new spells are loaded
-  if not f.events_registered then
-    f:RegisterEvent("UPDATE_MULTI_CAST_ACTIONBAR")
-    f:RegisterEvent("PLAYER_ENTERING_WORLD")
-      -- Bar.frame does not use OnEvent
-    f:SetScript("OnEvent", 
-      function()
-        if not InCombatLockdown() then
-          self:SetupBar(bar)
-        end
-      end)
-    f.events_registered = true
-  end
-
-  f:Execute(_bar_init)
-
-  Super.SetupBar(self,bar) -- create buttons after this is done
-end
-
--- a/PetActionButton.lua	Fri Aug 05 16:28:13 2011 -0700
+++ b/PetActionButton.lua	Wed Nov 14 16:47:27 2012 -0800
@@ -47,7 +47,6 @@
 "PLAYER_FARSIGHT_FOCUS_CHANGED",
 "UNIT_PET",
 "UNIT_FLAGS",
-"UNIT_AURA",
 "PET_BAR_UPDATE",
 "PET_BAR_UPDATE_COOLDOWN",
 "PET_BAR_UPDATE_USABLE",
@@ -145,6 +144,7 @@
   for _, evt in pairs(eventList) do
     f:RegisterEvent(evt)
   end
+  f:RegisterUnitEvent("UNIT_AURA","pet")
 
   -- attach to skinner
   bar:SkinButton(self,
--- a/Profile.lua	Fri Aug 05 16:28:13 2011 -0700
+++ b/Profile.lua	Wed Nov 14 16:47:27 2012 -0800
@@ -1,7 +1,7 @@
 local _, ns = ...
 local ReAction = ns.ReAction
 
-ReAction.PROFILEVERSION_LATEST = 2
+ReAction.PROFILEVERSION_LATEST = 3
 
 ReAction.defaultProfile = { 
   profile = {
@@ -139,6 +139,25 @@
     db.profile.dbversion = 2
   end
 
+
+  if db.profile.dbversion < 3 then
+    -- upgrade to v3
+
+    -- totem multicast bar no longer part of wow
+    local totem = db:GetNamespace("Totem",true)
+    if totem then
+      wipe(totem)
+    end
+
+    -- remove any bars set as totem bars
+    for name, bar in pairs(db.profile.bars) do
+      if bar.type == "Totem" then
+        db.profile.bars[name] = nil
+      end
+    end
+
+  end
+
   db.profile.dbversion = self.PROFILEVERSION_LATEST
 end
 
--- a/ReAction.lua	Fri Aug 05 16:28:13 2011 -0700
+++ b/ReAction.lua	Wed Nov 14 16:47:27 2012 -0800
@@ -95,7 +95,7 @@
   -- It's fairly normal to use the Blizzard vehicle bar, and to have
   -- your regular buttons in the same location. If you do this, and don't
   -- bother to hide your buttons, they'll obscure some parts of the vehicle bar.
-  VehicleMenuBar:SetFrameLevel(VehicleMenuBar:GetFrameLevel()+3)
+--  VehicleMenuBar:SetFrameLevel(VehicleMenuBar:GetFrameLevel()+3)
 
   self.callbacks = LibStub("CallbackHandler-1.0"):New(self)
   
@@ -354,7 +354,7 @@
   for _, f in pairs(blizzFrames) do
     ManageBlizzFrame(f, self.db.profile.options.hideBlizzardBars)
   end
-  ManageBlizzFrame(VehicleMenuBar, self.db.profile.options.hideBlizzardVehicleBar)
+  --ManageBlizzFrame(VehicleMenuBar, self.db.profile.options.hideBlizzardVehicleBar)
 end
 
 function ReAction:RegisterBarType( class, isDefault )
--- a/ReAction.toc	Fri Aug 05 16:28:13 2011 -0700
+++ b/ReAction.toc	Wed Nov 14 16:47:27 2012 -0800
@@ -1,4 +1,4 @@
-## Interface: 40200
+## Interface: 50400
 ## Title: ReAction
 ## Notes: Action button layout and configuration
 ## DefaultState: enabled
--- a/ReAction.xml	Fri Aug 05 16:28:13 2011 -0700
+++ b/ReAction.xml	Wed Nov 14 16:47:27 2012 -0800
@@ -15,7 +15,7 @@
   <Script file="StanceButton.lua"/>
   <Script file="BagButton.lua"/>
   <Script file="VehicleExitButton.lua"/>
-  <Script file="MultiCastButton.lua"/>
+  <Script file="ExtraActionButton.lua"/>
   <Script file="Options.lua"/>
   <Script file="Editor.lua"/>
 </Ui>
--- a/VehicleExitButton.lua	Fri Aug 05 16:28:13 2011 -0700
+++ b/VehicleExitButton.lua	Wed Nov 14 16:47:27 2012 -0800
@@ -28,7 +28,7 @@
 ReAction.Button.VehicleExit = VExitButton
 ReAction:RegisterBarType(VExitButton)
 
-function VExitButton:New( config, bar, idx )
+function VExitButton:New( config, bar, idx, idHint )
   self = Super.New(self, config, bar, idx, "SecureFrameTemplate, ActionButtonTemplate", "Button")
 
   -- frame setup
@@ -36,6 +36,9 @@
   self.frames.icon:SetTexture("Interface\\Vehicles\\UI-Vehicles-Button-Exit-Up")
   self.frames.icon:SetTexCoord(0.140625, 0.859375, 0.140625, 0.859375)
 
+  self:SetActionIDPool("vehicle-exit",1)
+  self:AcquireActionID(nil, idHint, true)
+
   -- attribute setup
   -- (none)
 
@@ -66,14 +69,6 @@
   return 1
 end
 
-function VExitButton:AcquireActionID()
-  -- don't use pool
-end
-
-function VExitButton:ReleaseActionID()
-  -- don't use pool
-end
-
 function VExitButton:Refresh()
   Super.Refresh(self)
   -- it seems that setscale kills the texcoord, have to refresh it
--- a/locale/enUS.lua	Fri Aug 05 16:28:13 2011 -0700
+++ b/locale/enUS.lua	Wed Nov 14 16:47:27 2012 -0800
@@ -103,8 +103,8 @@
 "ID List",
 "Specify a comma-separated list of IDs for each button in the bar (in order). Separate multiple pages with semicolons (;)",
 "Invalid action ID list string",
-"Mind Control Support",
-"When possessing a target (e.g. via Mind Control), map the first 12 buttons of this bar to the possessed target's actions.",
+"Override Button Support",
+"Override the first 12 buttons on this bar with abilities gained from certain game quests and events.",
 "Vehicle Support",
 "When on a vehicle, map the first 6 buttons of this bar to the vehicle actions. The vehicle-exit button is mapped to the 7th button. Pitch controls are not supported.",
 "Show Page #",
@@ -133,7 +133,8 @@
 "Hostile Focus",
 "Friendly Focus",
 "No Focus",
-"Mind Control",
+"Ability Override",
+"Has Special Action",
 "In a Vehicle",
 "Raid",
 "Party",
@@ -233,10 +234,6 @@
 "State",
 "State Scale Override",
 
--- MultiCastButton.lua
-"Totem Bar",
-"All %s buttons are in use for this bar, cannot create any more buttons",
-
 -- PetActionButton.lua
 "Pet Action Bar",
 "Pet action ID range is 1-10",
@@ -247,6 +244,9 @@
 -- VehicleExitButton.lua
 "Exit Vehicle Floater",
 
+-- ExtraActionButton.lua
+"Special Action Button",
+
 }) do
   L[s] = true
 end