diff modules/Action.lua @ 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 fb6c3a642ae3
children 901c91dc1bf2
line wrap: on
line diff
--- 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