changeset 128:729232aeeb5e

Action Button rewrite. (note: pet actions are probably slightly broken right now, they haven't been updated yet)
author Flick <flickerstreak@gmail.com>
date Thu, 05 Mar 2009 01:28:48 +0000
parents 29dacbecdb52
children 28b430de5875
files classes/ActionButton.lua classes/Bar.lua classes/Button.lua classes/GridProxy.lua modules/Action.lua
diffstat 5 files changed, 155 insertions(+), 631 deletions(-) [+]
line wrap: on
line diff
--- a/classes/ActionButton.lua	Wed Mar 04 21:19:32 2009 +0000
+++ b/classes/ActionButton.lua	Thu Mar 05 01:28:48 2009 +0000
@@ -59,12 +59,19 @@
   end
 
   self:SetAttribute("action",action)
-  local hasaction = (action > 120) or self:GetAttribute("hasaction-"..action)
 
-  if (self:GetAttribute("showgrid") + self:GetAttribute("showgrid-temp") == 0) and not hasaction then
-    self:Hide()
-  else
-    self:Show()
+  if not self:GetAttribute("showgrid") then
+    local hasaction = (action > 120) or self:GetAttribute("hasaction-"..action)
+    local tempShow = self:GetAttribute("showgrid-temp")
+    local evtShow = self:GetAttribute("showgrid-event")
+    if tempShow then tempShow = (tempShow > 0) end
+    if evtShow then evtShow = (evtShow > 0) end
+
+    if tempShow or evtShow or hasaction then
+      self:Show()
+    else
+      self:Hide()
+    end
   end
 ]]
 
@@ -83,18 +90,20 @@
 
 local _childupdate_showgrid = -- function(self, snippetid, message)
 [[
-  self:SetAttribute("showgrid-temp",message or 0)
-  local count = (message or 0) + (self:GetAttribute("showgrid") or 0)
-  if count == 0 then
-    local action = self:GetAttribute("action")
-    local hasaction = (action > 120) or self:GetAttribute("hasaction-"..action)
-    if hasaction then
+  self:SetAttribute("showgrid-event",message)
+  if not self:GetAttribute("showgrid") then
+    local count = message + (self:GetAttribute("showgrid-temp") or 0)
+    if count <= 0 then
+      local action = self:GetAttribute("action")
+      local hasaction = (action > 120) or self:GetAttribute("hasaction-"..action)
+      if hasaction then
+        self:Show()
+      else
+        self:Hide()
+      end
+    else
       self:Show()
-    else
-      self:Hide()
     end
-  else
-    self:Show()
   end
 ]]
 
@@ -111,15 +120,11 @@
 
 local _onReceiveDrag = -- function(self, button, kind, value, ...)
 [[
-  if lockButtons and (PlayerInCombat() or not lockButtonsCombat) and not IsModifiedClick("PICKUPACTION") then
-    return kind, value, ...
-  else
-    if kind == "spell" or kind == "item" or kind == "macro" then
-      -- assume it's a valid action
-      self:SetAttribute("hasaction-"..self:GetAttribute("action"),true)
-    end
-    return "action", self:GetAttribute("action")
+  if kind == "spell" or kind == "item" or kind == "macro" then
+    -- assume it's a valid action
+    self:SetAttribute("hasaction-"..self:GetAttribute("action"),true)
   end
+  return "action", self:GetAttribute("action")
 ]]
 
 --
@@ -154,13 +159,14 @@
 local Action = setmetatable( { }, { __index = Super } )
 ReAction.Button.Action = Action
 
-function Action:New( idx, config, bar, idHint )
+function Action:New( idx, barConfig, bar, idHint )
   local name = format("ReAction_%s_Action_%d",bar:GetName(),idx)
  
-  self = Super.New(self, name, config, bar, idx, "SecureActionButtonTemplate, ActionButtonTemplate" )
+  self = Super.New(self, name, barConfig, bar, idx, "SecureActionButtonTemplate, ActionButtonTemplate" )
 
   local f = self:GetFrame()
   local barFrame = bar:GetFrame()
+  local config = self:GetConfig()
 
   local frames = { }
   self.frames = frames
@@ -190,9 +196,12 @@
   f:SetAttribute("useparent-unit", true)
   f:SetAttribute("action", config.actionID)
   f:SetAttribute("default-action", config.actionID)
-  f:SetAttribute("showgrid",0)
+  f:SetAttribute("bar-idx",idx)
   f:SetAttribute("showgrid-temp",0)
-  f:SetAttribute("bar-idx",idx)
+  f:SetAttribute("showgrid-event",0)
+  f:SetAttribute("showgrid",not self:GetBarConfig().hideEmpty)
+
+  self:RefreshHasActionAttributes()
 
   -- non secure scripts
   f:SetScript("OnEvent", function(frame, ...) self:OnEvent(...) end)
@@ -205,7 +214,7 @@
   f:SetScript("OnReceiveDrag", function(frame) self:OnReceiveDrag() end)
 
   -- secure handlers
-  f:SetAttribute("_childupate", _childupdate)
+  f:SetAttribute("_childupdate", _childupdate)
   f:SetAttribute("_childupdate-showgrid",_childupdate_showgrid)
   barFrame:WrapScript(f, "OnDragStart", _onDragStart)
   barFrame:WrapScript(f, "OnReceiveDrag", _onReceiveDrag)
@@ -222,13 +231,10 @@
   bar:SkinButton(self)
 
   -- initial display
-  self:ShowGrid(not bar:GetConfig().hideEmpty)
   if ReAction:GetConfigMode() then
-    self:ShowGrid(true)
+    self:ShowGridTemp(true)
   end
 
-  f:Show()
-
   self:Refresh()
 
   return self
@@ -236,37 +242,64 @@
 
 function Action:Destroy()
   local f = self:GetFrame()
+  local c = self:GetConfig()
 
   f:UnregisterAllEvents()
 
   f:SetAttribute("_childupdate-vehicle",nil)
 
-  self:ReleaseActionID(config.actionID)
-  if self.config.pageactions then
-    for _, id in ipairs(self.config.pageactions) do
+  self:ReleaseActionID(c.actionID)
+  if c.pageactions then
+    for _, id in ipairs(c.pageactions) do
       self:ReleaseActionID(id)
     end
   end
   
-  Super:Destroy()
+  Super.Destroy(self)
 end
 
 function Action:Refresh()
   self.bar:PlaceButton(self, 36, 36)
   self:RefreshPages()
   self:InstallVehicle()
+  self:ShowGrid(not self:GetBarConfig().hideEmpty)
   self:UpdateAction()
 end
 
 function Action:InstallVehicle()
-  if self.idx == 7 and self.bar:GetConfig().vehicle then
+  if self.idx == 7 and self:GetBarConfig().vehicle then
     -- install vehicle-exit button on 7th button (only)
-    f:SetAttribute("_childupdate-vehicle", _childupdate_vehicleExit)
-    barFrame.ShowVehicleExit = function(bar,show)
+    self:GetFrame():SetAttribute("_childupdate-vehicle", _childupdate_vehicleExit)
+    self:GetBar():GetFrame().ShowVehicleExit = function(bar,show)
       self:ShowVehicleExit(show)
     end
   else
-    f:SetAttribute("_childupdate-vehicle",nil)
+    self:GetFrame():SetAttribute("_childupdate-vehicle",nil)
+  end
+end
+
+function Action:ShowGrid( show )
+  if not InCombatLockdown() then
+    self.frame:SetAttribute("showgrid", show)
+    self:UpdateShowGrid()
+  end
+end
+
+function Action:ShowGridTemp( show )
+  -- This function only modifies the show-grid when out
+  -- of combat, and is ignored by the secure handler. Use 
+  -- it for configuration modes.
+  if not InCombatLockdown() then
+    local count = self.showGridTempCount or 0
+    if show then
+      count = count + 1
+    else
+      count = count - 1
+    end
+    if count < 0 then count = 0 end
+    self.showGridTempCount = count
+    self:GetFrame():SetAttribute("showgrid-temp",count)
+    self:UpdateShowGrid()
   end
 end
 
@@ -293,28 +326,48 @@
   end
 end
 
+function Action:RefreshHasActionAttributes()
+  -- check if each action has an action or not, and flag an attribute
+  -- so that the showgrid secure handler can make decisions accordingly
+  local f = self:GetFrame()
+  local attr = "hasaction-"..self.config.actionID
+  local hasAction = HasAction(self.config.actionID)
+  if f:GetAttribute(attr) ~= hasAction then
+    f:SetAttribute(attr,hasAction) -- avoid setting attribute and triggering script handler unnecessarily
+  end
+  if self.config.pageactions then
+    for i = 1, self.nPages do
+      attr = "hasaction-"..self.config.pageactions[i]
+      hasAction = HasAction(self.config.pageactions[i])
+      if f:GetAttribute(attr) ~= hasAction then
+        f:SetAttribute(attr,hasAction)
+      end
+    end
+  end
+end
+
 function Action:UpdateShowGrid()
   -- this is a little bit complicated because there's no
-  -- secure driver to handle show/hide grid events.
+  -- secure access to HasAction.
   if InCombatLockdown() then
     self.showgridPending = true  -- handle after combat
   else
     self.showgridPending = false
-    -- check if each action has an action or not, and flag an attribute
-    -- so that the showgrid secure handler can make decisions accordingly
-    local f = self:GetFrame()
-    f:SetAttribute("hasaction-"..self.config.actionID, HasAction(self.config.actionID))
-    if self.config.pageactions then
-      for i = 1, self.nPages do
-        f:SetAttribute("hasaction-"..self.config.pageactions[i], HasAction(self.config.pageactions[i]))
-      end
-    end
+    self:RefreshHasActionAttributes()
+
     -- the following is an out-of-combat show/hide to supplement the secure
     -- handling and clean up after it when it guesses
-    if HasAction(self.actionID) then
+    local f = self:GetFrame()
+    local count = (f:GetAttribute("showgrid-event") or 0) +
+                  (self.showGridTempCount or 0) +
+                  (f:GetAttribute("showgrid") and 1 or 0)
+
+    if count <= 0 and not HasAction(self.actionID) then
+      if f:IsShown() then
+        f:Hide()
+      end
+    elseif not f:IsShown() then
       f:Show()
-    else
-      f:Hide()
     end
   end
 end
@@ -507,7 +560,7 @@
 end
 
 function Action:RefreshPages( force )
-  local nPages = self.bar:GetConfig().nPages
+  local nPages = self:GetBarConfig().nPages
   if nPages and (nPages ~= self.nPages or force) then
     local f = self:GetFrame()
     local c = self.config.pageactions
@@ -532,11 +585,10 @@
   end
 end
 
-function Action.SetupBarHeader( bar ) -- call this as a static method
+function Action.SetupBarHeader( bar, config ) -- call this as a static method
   local f = bar:GetFrame()
-  local c = bar:GetConfig()
-  f:SetAttribute("mindcontrol",c.mindcontrol)
-  f:SetAttribute("vehicle",c.vehicle)
+  f:SetAttribute("mindcontrol",config.mindcontrol)
+  f:SetAttribute("vehicle",config.vehicle)
   f:Execute(
     [[
     doMindControl = self:GetAttribute("mindcontrol")
@@ -547,8 +599,8 @@
   f:SetAttribute("_onstate-mc", _onstate_mc)
   RegisterStateDriver(f, "mc", "[target=vehicle,exists] vehicle; [bonusbar:5] mc; none")
 
-  f:SetAttribute("lockbuttons",c.lockButtons)
-  f:SetAttribute("lockbuttonscombat",c.lockButtonsCombat)
+  f:SetAttribute("lockbuttons",config.lockButtons)
+  f:SetAttribute("lockbuttonscombat",config.lockButtonsCombat)
   f:Execute(
     [[
       lockButtons = self:GetAttribute("lockbuttons")
@@ -572,7 +624,7 @@
 function Action:ShowVehicleExit(show)
   local f = self:GetFrame()
   local tx = f.vehicleExitTexture
-  if show and self.bar:GetConfig().vehicle then
+  if show and self:GetBarConfig().vehicle then
     if not tx then
       tx = f:CreateTexture(nil,"ARTWORK")
       tx:SetAllPoints()
@@ -629,7 +681,7 @@
 
 function Action:ACTIONBAR_SLOT_CHANGED(event, action)
   if action == 0 or action == self.actionID then
-    self:UpdateAction()
+    self:UpdateAll()
   end
 end
 
--- a/classes/Bar.lua	Wed Mar 04 21:19:32 2009 +0000
+++ b/classes/Bar.lua	Thu Mar 05 01:28:48 2009 +0000
@@ -84,6 +84,9 @@
 
 function Bar:OnConfigModeChanged(event, mode)
   self:ShowControls(mode) -- Bar:ShowControls() defined in Overlay.lua
+  for b in pairs(self.buttons) do
+    b:ShowGridTemp(mode)
+  end
 end
 
 function Bar:ApplyAnchor()
--- a/classes/Button.lua	Wed Mar 04 21:19:32 2009 +0000
+++ b/classes/Button.lua	Thu Mar 05 01:28:48 2009 +0000
@@ -30,7 +30,7 @@
 
 ReAction.Button = Button -- export to ReAction
 
-function Button:New( name, config, bar, idx, inherits, buttonType )
+function Button:New( name, barConfig, bar, idx, inherits, buttonType )
   buttonType = buttonType or "CheckButton"
 
   -- create new self
@@ -38,7 +38,8 @@
     { 
       bar = bar,
       idx = idx,
-      config = config,
+      barConfig = barConfig,
+      config = barConfig.buttons[idx],
       name = name,
     }, 
     { __index = self } )
@@ -84,6 +85,10 @@
   end
 end
 
+function Button:GetBar()
+  return self.bar
+end
+
 function Button:GetFrame()
   return self.frame
 end
@@ -96,6 +101,12 @@
   return self.config
 end
 
+function Button:GetBarConfig()
+  -- this is the per-bar Button config structure,
+  -- not the config structure of the bar itself
+  return self.barConfig
+end
+
 function Button:GetActionID()
   -- derived classes should override this
   return nil
@@ -180,6 +191,7 @@
     f:SetScript("OnEnter", self.oldOnEnter)
     self.oldOnEnter = nil
   end
+  self:ShowGridTemp(mode)
   self:UpdateKeybindModeDisplay( mode )
 end
 
@@ -256,23 +268,14 @@
   end
 end
 
-function Button:ShowGrid( show )
-  if not InCombatLockdown() then
-    local f = self.frame
-    local count = f:GetAttribute("showgrid")
-    if show then
-      count = count + 1
-    else
-      count = count - 1
-    end
-    if count < 0 then
-      count = 0
-    end
-    f:SetAttribute("showgrid",count)
-    self:UpdateShowGrid()
-  end
-end
-
 function Button:UpdateShowGrid()
  -- does nothing by default
 end
+
+function Button:ShowGridTemp(show)
+  -- does nothing by default
+end
+
+function Button:ShowGrid(show)
+  -- does nothing by default
+end
--- a/classes/GridProxy.lua	Wed Mar 04 21:19:32 2009 +0000
+++ b/classes/GridProxy.lua	Thu Mar 05 01:28:48 2009 +0000
@@ -4,7 +4,7 @@
 local gridProxy = { }
 ReAction.gridProxy = gridProxy
 
-local f = CreateFrame("CheckButton",nil,UIParent,"ActionBarButtonTemplate, SecureHandlerAttributeTemplate")
+local f = CreateFrame("CheckButton","ReActionShowGridProxy",UIParent,"ActionBarButtonTemplate, SecureHandlerAttributeTemplate")
   -- SecureHandlerAttributeTemplate overwrites the onAttributeChanged handler, as it's last in the list
 f:UnregisterAllEvents()
 f:SetScript("OnEnter",nil)
@@ -13,6 +13,9 @@
 f:SetScript("OnDragStart",nil)
 f:SetScript("OnReceiveDrag",nil)
 f:SetScript("OnUpdate",nil)
+f:SetAttribute("showgrid",0)
+f:SetAttribute("action",0)
+f.action = 0
 f:EnableMouse(false)
 for _, child in ipairs({f:GetChildren()}) do
   child:Hide()
--- a/modules/Action.lua	Wed Mar 04 21:19:32 2009 +0000
+++ b/modules/Action.lua	Thu Mar 05 01:28:48 2009 +0000
@@ -1,14 +1,3 @@
---[[
-  ReAction Action button module.
-
-  The button module implements standard action button functionality by wrapping Blizzard's 
-  ActionBarButtonTemplate frame and associated functions.
-
-  It also provides action remapping support for multiple pages and possessed targets
-  (Mind Control, Eyes of the Beast, Karazhan Chess event, various quests, etc).
---]]
-
--- local imports
 local ReAction = ReAction
 local L = ReAction.L
 local _G = _G
@@ -22,14 +11,13 @@
 
 -- libraries
 local KB = LibStub("LibKeyBound-1.0")
-local LBF -- initialized later
 
 -- module declaration
 local moduleID = "Action"
 local module = ReAction:NewModule( moduleID )
 
 -- Class declarations
-local Button = { }
+local Button = ReAction.Button.Action -- see /classes/ActionButton.lua
 local Handle = { }
 local PropHandler = { }
 
@@ -53,8 +41,6 @@
   ReAction.RegisterCallback(self, "OnRenameBar")
   ReAction.RegisterCallback(self, "OnConfigModeChanged")
 
-  LBF = LibStub("LibButtonFacade",true)
-
   KB.RegisterCallback(self, "LIBKEYBOUND_ENABLED")
   KB.RegisterCallback(self, "LIBKEYBOUND_DISABLED")
   KB.RegisterCallback(self, "LIBKEYBOUND_MODE_COLOR_CHANGED","LIBKEYBOUND_ENABLED")
@@ -121,14 +107,12 @@
 
 function module:LIBKEYBOUND_ENABLED(evt)
   for _, h in pairs(self.handles) do
-    h:ShowGrid(true)
     h:SetKeybindMode(true)
   end
 end
 
 function module:LIBKEYBOUND_DISABLED(evt)
   for _, h in pairs(self.handles) do
-    h:ShowGrid(false)
     h:SetKeybindMode(false)
   end
 end
@@ -160,7 +144,6 @@
       desc = L["Prevents picking up/dragging actions.|nNOTE: This setting is overridden by the global setting in Blizzard's Action Buttons tab"],
       order = 2,
       type = "toggle",
-      disabled = "LockButtonsDisabled",
       get = "GetLockButtons",
       set = "SetLockButtons",
     },
@@ -307,7 +290,9 @@
           btnCfg[i] = {}
         end
         if self.btns[i] == nil then
-          local b = Button:New(self, i, btnCfg[i], self.config)
+          local lastButton = self:GetLastButton()
+          local hint = lastButton and lastButton.config.actionID
+          local b = Button:New(i, self.config, self.bar, hint)
           self.btns[i] = b
           self.bar:AddButton(i,b)
         end
@@ -321,31 +306,10 @@
         end
       end
     end
-    local f = self.bar:GetFrame()
     for _, b in ipairs(self.btns) do
       b:Refresh()
     end
-    f:SetAttribute("mindcontrol",self.config.mindcontrol)
-    f:SetAttribute("vehicle",self.config.vehicle)
-    f:Execute(
-      [[
-      doMindControl = self:GetAttribute("mindcontrol")
-      doVehicle = self:GetAttribute("vehicle")
-      control:ChildUpdate()
-      ]])
-
-    f:SetAttribute("_onstate-mc",
-      -- function _onstate-mc(self, stateid, newstate)
-      [[
-        local oldMcVehicleState = mcVehicleState
-        mcVehicleState = newstate
-        control:ChildUpdate()
-        if oldMcVehicleState == "vehicle" or mcVehicleState == "vehicle" then
-          control:ChildUpdate("vehicle")
-        end
-      ]])
-    RegisterStateDriver(f, "mc", "[target=vehicle,exists] vehicle; [bonusbar:5] mc; none")
-
+    Button.SetupBarHeader(self.bar,self.config)
     self:UpdateButtonLock()
   end
 
@@ -359,26 +323,12 @@
 
   function Handle:SetConfigMode(mode)
     for _, b in pairs(self.btns) do
-      b:ShowGrid(mode)
-      b:ShowActionIDLabel(mode)
-    end
-  end
-
-  function Handle:ShowGrid(show)
-    for _, b in pairs(self.btns) do
-      b:ShowGrid(show)
+      b:UpdateActionIDLabel(mode)
     end
   end
 
   function Handle:UpdateButtonLock()
-    local f = self.bar:GetFrame()
-    f:SetAttribute("lockbuttons",self.config.lockButtons)
-    f:SetAttribute("lockbuttonscombat",self.config.lockButtonsCombat)
-    f:Execute(
-      [[
-        lockButtons = self:GetAttribute("lockbuttons")
-        lockButtonsCombat = self:GetAttribute("lockbuttonscombat")
-      ]])
+    Button.SetButtonLock(self.bar, self.config.lockButtons, self.config.lockButtonsCombat)
   end
 
   function Handle:SetKeybindMode(mode)
@@ -404,7 +354,9 @@
   function Handle:SetHideEmpty(info, value)
     if value ~= self.config.hideEmpty then
       self.config.hideEmpty = value
-      self:ShowGrid(not value)
+      for _, b in pairs(self.btns) do
+        b:ShowGrid(not value)
+      end
     end
   end
 
@@ -413,7 +365,7 @@
   end
 
   function Handle:GetLockButtons()
-    return LOCK_ACTIONBAR == "1" or self.config.lockButtons
+    return self.config.lockButtons
   end
 
   function Handle:SetLockButtons(info, value)
@@ -421,10 +373,6 @@
     self:UpdateButtonLock()
   end
 
-  function Handle:LockButtonsDisabled()
-    return LOCK_ACTIONBAR == "1"
-  end
-
   function Handle:GetLockButtonsCombat()
     return self.config.lockButtonsCombat
   end
@@ -435,7 +383,7 @@
   end
 
   function Handle:LockButtonsCombatDisabled()
-    return LOCK_ACTIONBAR == "1" or not self.config.lockButtons
+    return not self.config.lockButtons
   end
 
   function Handle:GetNumPages()
@@ -705,495 +653,3 @@
 
 end
 
------- ActionID allocation ------
--- this needs to be high performance when requesting new IDs,
--- or certain controls will become sluggish. However, the new-request
--- infrastructure can be built lazily the first time that a new request
--- comes in (which will only happen at user config time: at static startup
--- config time all actionIDs should already have been assigned and stored
--- in the config file)
-
-local IDAlloc
-do
-  local n = 120
-  
-  IDAlloc = setmetatable({ wrap = 1, freecount = n }, {__index = function() return 0 end})
-
-  function IDAlloc:Acquire(id, hint)
-    id = tonumber(id)
-    hint = tonumber(hint)
-    if id and (id < 1 or id > n) then
-      id = nil
-    end
-    if hint and (hint < 1 or hint > n) then
-      hint = nil
-    end
-    if id == nil then
-      -- get a free ID
-      if hint and self[hint] == 0 then
-        -- use the hint if it's free
-        id = hint
-      elseif self.freecount > 0 then
-        -- if neither the id nor the hint are defined or free, but
-        -- the list is known to have free IDs, then start searching
-        -- at the hint for a free one
-        for i = hint or 1, n do
-          if self[i] == 0 then
-            id = i
-            break
-          end
-        end
-        -- self.wrap the search
-        if id == nil and hint and hint > 1 then
-          for i = 1, hint - 1 do
-            if self[i] == 0 then
-              id = i
-              break
-            end
-          end
-        end
-      end
-      if id == nil then
-        -- if there are no free IDs, start wrapping at 1
-        id = self.wrap
-        self.wrap = id + 1
-        if self.wrap > n then
-          self.wrap = 1
-        end
-      end
-    end
-    if self[id] == 0 then
-      self.freecount = self.freecount - 1
-    end
-    self[id] = self[id] + 1
-    return id
-  end
-
-  function IDAlloc:Release(id)
-    id = tonumber(id)
-    if id and (id >= 1 or id <= n) then
-      self[id] = self[id] - 1
-      if self[id] == 0 then
-        self.freecount = self.freecount + 1
-        self.wrap = 1
-      end
-    end
-  end
-end
-
------- Button class ------
-local frameRecycler = { }
-local trash = CreateFrame("Frame")
-local OnUpdate, GetActionName, GetHotkey
-do
-  local ATTACK_BUTTON_FLASH_TIME = ATTACK_BUTTON_FLASH_TIME
-  local IsActionInRange = IsActionInRange
-
-  function OnUpdate(frame, elapsed)
-    -- note: This function taints frame.flashtime and frame.rangeTimer. Both of these
-    --       are only read by ActionButton_OnUpdate (which this function replaces). In
-    --       all other places they're just written, so it doesn't taint any secure code.
-    if frame.flashing == 1 then
-      frame.flashtime = frame.flashtime - elapsed
-      if frame.flashtime <= 0 then
-        local overtime = -frame.flashtime
-        if overtime >= ATTACK_BUTTON_FLASH_TIME then
-          overtime = 0
-        end
-        frame.flashtime = ATTACK_BUTTON_FLASH_TIME - overtime
-
-        local flashTexture = frame.flash
-        if flashTexture:IsShown() then
-          flashTexture:Hide()
-        else
-          flashTexture:Show()
-        end
-      end
-    end
-    
-    if frame.rangeTimer then
-      frame.rangeTimer = frame.rangeTimer - elapsed;
-
-      if frame.rangeTimer <= 0 then
-        if IsActionInRange(frame.action) == 0 then
-          frame.icon:SetVertexColor(1.0,0.1,0.1)
-        else
-          ActionButton_UpdateUsable(frame)
-        end
-        frame.rangeTimer = 0.1
-      end
-    end
-  end
-
-  function GetActionName(f)
-    local b = f and f._reactionButton
-    if b then
-      return format("%s:%s", b.bar:GetName(), b.idx)
-    end
-  end
-
-  function GetHotkey(f)
-    return KB:ToShortKey(GetBindingKey(format("CLICK %s:LeftButton",f:GetName())))
-  end
-
-  -- This is a bit hokey : install a bare hook on ActionButton_UpdateHotkey because
-  -- even though it's secure it's never called in a way that can cause taint. This is 
-  -- for performance reasons to avoid having to hook frame:OnEvent securely.
-  local UpdateHotkey_old = ActionButton_UpdateHotkeys
-  ActionButton_UpdateHotkeys = function( frame, ... )
-    local b = frame._reactionButton
-    if b then
-      b.hotkey:SetText( GetHotkey(frame) )
-    else
-      return UpdateHotkey_old(frame, ...)
-    end
-  end
-end
-
-local meta = {__index = Button}
-
-function Button:New( handle, idx, config, barConfig )
-  local bar = handle.bar
-
-  -- create new self
-  self = setmetatable( 
-    { 
-      bar = bar,
-      idx = idx,
-      config = config,
-      barConfig = barConfig,
-    }, meta )
-
-  local name = config.name or ("ReAction_%s_%s_%d"):format(bar:GetName(),moduleID,idx)
-  self.name = name
-  config.name = name
-  local lastButton = handle:GetLastButton()
-  config.actionID = IDAlloc:Acquire(config.actionID, lastButton and lastButton.config.actionID) -- gets a free one if none configured
-  self.nPages = 1
-  
-  -- have to recycle frames with the same name: CreateFrame() doesn't overwrite
-  -- existing globals. Can't set to nil in the global because it's then tainted.
-  local parent = bar:GetFrame()
-  local f = frameRecycler[name]
-  if f then
-    f:SetParent(parent)
-  else
-    f = CreateFrame("CheckButton", name, parent, "ActionBarButtonTemplate")
-    -- ditch the old hotkey text because it's tied in ActionButton_Update() to the
-    -- standard binding.
-    local hotkey = _G[name.."HotKey"]
-    hotkey:SetParent(trash)
-    hotkey = f:CreateFontString(nil, "ARTWORK", "NumberFontNormalSmallGray")
-    hotkey:SetWidth(36)
-    hotkey:SetHeight(18)
-    hotkey:SetJustifyH("RIGHT")
-    hotkey:SetJustifyV("TOP")
-    hotkey:SetPoint("TOPLEFT",f,"TOPLEFT",-2,-2)
-    f.hotkey = hotkey
-    f.icon = _G[name.."Icon"]
-    f.flash = _G[name.."Flash"]
-    f:SetScript("OnUpdate",OnUpdate)
-  end
-
-  f._reactionButton = self
-
-  self.hotkey = f.hotkey
-  self.border = _G[name.."Border"]
-
-  f:SetAttribute("action", config.actionID)
-  f:SetAttribute("default-action", config.actionID)
-  -- install mind control actions for all buttons just for simplicity
-  if self.idx <= 12 then
-    f:SetAttribute("mc-action", 120 + self.idx)
-  end
-
-  -- set a tooltip onEnter
-  f:SetScript("OnEnter", 
-    function(frame)
-      if ReAction:GetKeybindMode() then
-        KB:Set(frame)
-      elseif frame.vehicleExitMode then
-        GameTooltip_AddNewbieTip(frame, LEAVE_VEHICLE, 1.0, 1.0, 1.0, nil);
-      else
-        ActionButton_SetTooltip(frame)
-      end
-    end)
-  
-  -- set a _childupdate handler, called within the header's context
-  f:SetAttribute("_childupdate", 
-    -- function _childupdate(self, snippetid, message)
-    [[
-      local action = "default-action"
-      if (doVehicle and mcVehicleState == "vehicle") or
-         (doMindControl and mcVehicleState == "mc") then
-        action = "mc-action"
-      elseif page and state and page[state] then
-        action = "action-"..page[state]
-      end
-
-      local value = self:GetAttribute(action)
-      if value then
-        self:SetAttribute("action",value)
-      end
-    ]])
-
-  -- Install a handler for the 7th button (only) to show/hide a
-  -- vehicle exit button. This is more than a little bit hack-ish and
-  -- will be replaced in the next iteration with the reimplementation
-  -- of action button functionality.
-  if idx == 7 then
-    local barFrame = bar:GetFrame()
-    function barFrame:ShowVehicleExit(show)
-      local tx = f.vehicleExitTexture
-      if show then
-        if not tx then
-          tx = f:CreateTexture(nil,"ARTWORK")
-          tx:SetAllPoints()
-            -- copied from Blizzard/VehicleMenuBar.lua SkinsData
-          tx:SetTexture("Interface\\Vehicles\\UI-Vehicles-Button-Exit-Up")
-          tx:SetTexCoord(0.140625, 0.859375, 0.140625, 0.859375)
-          f.vehicleExitTexture = tx
-        end
-        tx:Show()
-        f.vehicleExitMode = true
-      elseif tx then
-        tx:SetTexCoord(0,1,0,1)
-        tx:Hide()
-        f.vehicleExitMode = false
-      end
-    end
-
-    f:SetAttribute("macrotext","/run VehicleExit()")
-    f:SetAttribute("_childupdate-vehicle",
-      -- function _childupdate-vehicle(self, snippetid, message)
-      [[
-        local show = (mcVehicleState == "vehicle")
-        if show then
-          self:SetAttribute("type","macro")
-          self:SetAttribute("showgrid",self:GetAttribute("showgrid")+1)
-          self:Show()
-        else
-          self:SetAttribute("type","action")
-          local showgrid = self:GetAttribute("showgrid")
-          showgrid = showgrid - 1
-          if showgrid < 0 then showgrid = 0 end
-          self:SetAttribute("showgrid",self:GetAttribute("showgrid")-1)
-          if showgrid <= 0 then
-            self:Hide()
-          end
-        end
-        control:CallMethod("ShowVehicleExit",show)
-      ]])
-  end
-
-  -- install drag wrappers to lock buttons 
-  bar:GetFrame():WrapScript(f, "OnDragStart",
-    -- OnDragStart(self, button, kind, value, ...)
-    [[
-      if lockButtons and (PlayerInCombat() or not lockButtonsCombat) and not IsModifiedClick("PICKUPACTION") then
-        return "clear"
-      end
-    ]])
-
-  self.frame = f
-
-  -- initialize the hide state
-  f:SetAttribute("showgrid",0)
-  self:ShowGrid(not barConfig.hideEmpty)
-  if ReAction:GetConfigMode() then
-    self:ShowGrid(true)
-  end
-
-  -- set the hotkey text
-  self.hotkey:SetText( GetHotkey(self.frame) )
-
-  -- show the ID label if applicable
-  self:ShowActionIDLabel(ReAction:GetConfigMode())
-
-  -- attach to skinner
-  bar:SkinButton(self,
-    {
-      HotKey = self.hotkey,
-    }
-  )
-
-  self:Refresh()
-  return self
-end
-
-function Button:Destroy()
-  local f = self.frame
-  f:UnregisterAllEvents()
-  f:Hide()
-  f:SetParent(UIParent)
-  f:ClearAllPoints()
-  f:SetAttribute("_childupdate",nil)
-  f:SetAttribute("_childupdate-vehicle",nil)
-  if self.name then
-    frameRecycler[self.name] = f
-  end
-  if self.config.actionID then
-    IDAlloc:Release(self.config.actionID)
-  end
-  if self.config.pageactions then
-    for _, id in ipairs(self.config.pageactions) do
-      IDAlloc:Release(id)
-    end
-  end
-  f._reactionButton = nil
-  self.frame = nil
-  self.config = nil
-  self.bar = nil
-end
-
-function Button:Refresh()
-  local f = self.frame
-  self.bar:PlaceButton(self, 36, 36)
-  self:RefreshPages()
-end
-
-function Button:GetFrame()
-  return self.frame
-end
-
-function Button:GetName()
-  return self.name
-end
-
-function Button:GetConfig()
-  return self.config
-end
-
-function Button:GetActionID(page)
-  if page == nil then
-    -- get the effective ID
-    return self.frame.action -- kept up-to-date by Blizzard's ActionButton_CalculateAction()
-  else
-    if page == 1 then
-      return self.config.actionID
-    else
-      return self.config.pageactions and self.config.pageactions[page] or self.config.actionID
-    end
-  end
-end
-
-function Button:SetActionID( id, page )
-  id = tonumber(id)
-  page = tonumber(page)
-  if id == nil or id < 1 or id > 120 then
-    error("Button:SetActionID - invalid action ID")
-  end
-  if page and page ~= 1 then
-    if not self.config.pageactions then
-      self.config.pageactions = { }
-    end
-    if self.config.pageactions[page] then
-      IDAlloc:Release(self.config.pageactions[page])
-    end
-    self.config.pageactions[page] = id
-    IDAlloc:Acquire(self.config.pageactions[page])
-    self.frame:SetAttribute(("action-page%d"):format(page),id)
-  else
-    IDAlloc:Release(self.config.actionID)
-    self.config.actionID = id
-    IDAlloc:Acquire(self.config.actionID)
-    self.frame:SetAttribute("action",id)
-    if self.config.pageactions then
-      self.config.pageactions[1] = id
-      self.frame:SetAttribute("action-page1",id)
-    end
-  end
-end
-
-function Button:RefreshPages( force )
-  local nPages = self.barConfig.nPages
-  if nPages and (nPages ~= self.nPages or force) then
-    local f = self:GetFrame()
-    local c = self.config.pageactions
-    if nPages > 1 and not c then
-      c = { }
-      self.config.pageactions = c
-    end
-    for i = 1, nPages do
-      if i > 1 then
-        c[i] = IDAlloc:Acquire(c[i], self.config.actionID + (i-1)*self.bar:GetNumButtons())
-      else
-        c[i] = self.config.actionID  -- page 1 is the same as the base actionID
-      end
-      f:SetAttribute(("action-page%d"):format(i),c[i])
-    end
-    for i = nPages+1, #c do
-      IDAlloc:Release(c[i])
-      c[i] = nil
-      f:SetAttribute(("action-page%d"):format(i),nil)
-    end
-    self.nPages = nPages
-  end
-end
-
-function Button:ShowGrid( show )
-  if not InCombatLockdown() then
-    local f = self.frame
-    local count = f:GetAttribute("showgrid")
-    if show then
-      count = count + 1
-    else
-      count = count - 1
-    end
-    if count < 0 then
-      count = 0
-    end
-    f:SetAttribute("showgrid",count)
-
-    if count >= 1 and not f:GetAttribute("statehidden") then
-      if LBF then
-        LBF:SetNormalVertexColor(self.frame, 1.0, 1.0, 1.0, 0.5)
-      else
-        self.frame:GetNormalTexture():SetVertexColor(1.0, 1.0, 1.0, 0.5);
-      end
-      f:Show()
-    elseif count < 1 and not HasAction(self:GetActionID()) then
-      f:Hide()
-    end
-  end
-end
-
-function Button:ShowActionIDLabel( show )
-  local f = self:GetFrame()
-  if show then
-    local id = self:GetActionID()
-    if not f.actionIDLabel then
-      local label = f:CreateFontString(nil,"OVERLAY","GameFontNormalLarge")
-      label:SetAllPoints()
-      label:SetJustifyH("CENTER")
-      label:SetShadowColor(0,0,0,1)
-      label:SetShadowOffset(2,-2)
-      f.actionIDLabel = label -- store the label with the frame for recycling
-
-      f:HookScript("OnAttributeChanged", 
-        function(frame, attr, value)
-          if label:IsVisible() and attr:match("action") then
-            label:SetText(tostring(frame.action))
-          end
-        end)
-    end
-    f.actionIDLabel:SetText(tostring(id))
-    f.actionIDLabel:Show()
-  elseif f.actionIDLabel then
-    f.actionIDLabel:Hide()
-  end
-end
-
-function Button:SetKeybindMode( mode )
-  if mode then
-    self.frame.GetActionName = GetActionName
-    self.frame.GetHotkey     = GetHotkey
-    -- set the border for all buttons to the keybind-enable color
-    self.border:SetVertexColor(KB:GetColorKeyBoundMode())
-    self.border:Show()
-  elseif IsEquippedAction(self:GetActionID()) then
-    self.border:SetVertexColor(0, 1.0, 0, 0.35) -- from ActionButton.lua
-  else
-    self.border:Hide()
-  end
-end
\ No newline at end of file