diff classes/ActionButton.lua @ 122:a2d2f23137c8

- Rearranged and consolidated some files in modules directory - Added 'classes' directory, moved Bar and Overlay there - Added Button, ActionButton, and GridProxy classes, not in use yet
author Flick <flickerstreak@gmail.com>
date Mon, 23 Feb 2009 18:56:57 +0000
parents
children 943eed2c7def
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/classes/ActionButton.lua	Mon Feb 23 18:56:57 2009 +0000
@@ -0,0 +1,693 @@
+local ReAction = ReAction
+local L = ReAction.L
+local _G = _G
+local CreateFrame = CreateFrame
+local GetBindingKey = GetBindingKey
+local format = string.format
+local IsUsableAction = IsUsableAction
+local IsEquippedAction = IsEquippedAction
+local IsConsumableAction = IsConsumableAction
+local IsStackableAction = IsStackableAction
+local GetActionText = GetActionText
+local GetCVar = GetCVar
+local GameTooltip_SetDefaultAnchor = GameTooltip_SetDefaultAnchor
+local IsCurrentAction = IsCurrentAction
+local IsAutoRepeatAction = IsAutoRepeatAction
+local IsUsableAction = IsUsableAction
+local IsAttackAction = IsAttackAction
+local CooldownFrame_SetTimer = CooldownFrame_SetTimer
+local GetActionCooldown = GetActionCooldown
+local GetActionTexture = GetActionTexture
+local ATTACK_BUTTON_FLASH_TIME = ATTACK_BUTTON_FLASH_TIME
+local TOOLTIP_UPDATE_TIME = TOOLTIP_UPDATE_TIME
+local IsActionInRange = IsActionInRange
+local InCombatLockdown = InCombatLockdown
+local HasAction = HasAction
+
+ReAction:UpdateRevision("$Revision: 154 $")
+
+--
+-- Secure snippets
+-- These are run within the context of the bar's sandbox, as the
+-- buttons themselves do not have their own sandbox.
+--
+local _onstate_mc = -- function(self, stateid, newstate)
+[[
+  local oldMcVehicleState = mcVehicleState
+  mcVehicleState = newstate
+  control:ChildUpdate()
+  if oldMcVehicleState == "vehicle" or mcVehicleState == "vehicle" then
+    control:ChildUpdate("vehicle")
+  end
+]]
+
+local _childupdate = -- function(self, snippetid, message)
+[[
+  local action = nil
+  if (doVehicle and mcVehicleState == "vehicle") or
+     (doMindControl and mcVehicleState == "mc") then
+    local idx = self:GetAttribute("bar-idx")
+    if idx and idx <= 12 then
+      action = 120 + idx
+    else
+      action = 0
+    end
+  elseif page and state and page[state] then
+    action = self:GetAttribute("action-page"..page[state])
+  else
+    action = self:GetAttribute("default-action")
+  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()
+  end
+]]
+
+local _childupdate_vehicleExit =  -- function(self, snippetid, message)
+[[
+  local show = (mcVehicleState == "vehicle")
+  if show then
+    self:SetAttribute("type","macro")
+    self:SetAttribute("macrotext","/run VehicleExit()")
+    self:Show()
+  else
+    self:SetAttribute("type","action")
+  end
+  control:CallMethod("ShowVehicleExit",show)
+]]
+
+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:Show()
+    else
+      self:Hide()
+    end
+  else
+    self:Show()
+  end
+]]
+
+local _onDragStart = -- function(self, button, kind, value, ...)
+[[
+  if lockButtons and (PlayerInCombat() or not lockButtonsCombat) and not IsModifiedClick("PICKUPACTION") then
+    return kind, value, ...
+  else
+    -- don't make any assumptions about hiding on grid show here, as we don't know if the 
+    -- drag gets cancelled later. 
+    return "action", self:GetAttribute("action")
+  end
+]]
+
+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")
+  end
+]]
+
+--
+-- private
+--
+local eventList = {
+  "PLAYER_REGEN_ENABLED",
+  "PLAYER_ENTERING_WORLD",
+  "ACTIONBAR_PAGE_CHANGED",
+  "ACTIONBAR_SLOT_CHANGED",
+  "UPDATE_BINDINGS",
+  "ACTIONBAR_UPDATE_STATE",
+  "ACTIONBAR_UPDATE_USABLE",
+  "ACTIONBAR_UPDATE_COOLDOWN",
+  "UPDATE_INVENTORY_ALERTS",
+  "PLAYER_TARGET_CHANGED",
+  "TRADE_SKILL_SHOW",
+  "TRADE_SKILL_CLOSE",
+  "PLAYER_ENTER_COMBAT",
+  "PLAYER_LEAVE_COMBAT",
+  "START_AUTOREPEAT_SPELL",
+  "STOP_AUTOREPEAT_SPELL",
+  "UNIT_ENTERED_VEHICLE",
+  "UNIT_EXITED_VEHICLE",
+  "COMPANION_UPDATE",
+}
+
+--
+-- Action Button class
+--
+local Super = ReAction.Button
+local Action = setmetatable( { }, { __index = Super } )
+ReAction.Button.Action = Action
+
+function Action:New( idx, config, bar, idHint )
+  self = Super.New(
+    self,
+    format("ReAction_%s_Action_%d",bar:GetName(),idx), 
+    config, 
+    bar, 
+    idx, 
+    "ActionButtonTemplate, SecureActionButtonTemplate" )
+
+  local f = self:GetFrame()
+  local barFrame = bar:GetFrame()
+
+  local frames = { }
+  self.frames = frames
+  frames.icon          = _G[name.."Icon"]
+  frames.flash         = _G[name.."Flash"]
+  frames.hotkey        = _G[name.."HotKey"]
+  frames.count         = _G[name.."Count"]
+  frames.name          = _G[name.."Name"]
+  frames.border        = _G[name.."Border"]
+  frames.cooldown      = _G[name.."Cooldown"]
+  frames.normalTexture = _G[name.."NormalTexture"]
+
+  self.hotkey = frames.hotkey -- alias for Button methods
+  self.border = frames.border -- alias for Button methods
+
+  self.rangeTimer = TOOLTIP_UPDATE_TIME
+
+  -- set up the base action ID
+  self:SetActionIDPool("action",120)
+  config.actionID = self:AcquireActionID(config.actionID, idHint)
+  self.actionID = config.actionID
+  self.nPages = 1
+
+  -- attribute setup
+  f:SetAttribute("type","action")
+  f:SetAttribute("checkselfcast", true)
+  f:SetAttribute("checkfocuscast", true)
+  f:SetAttribute("useparent-unit", true)
+  f:SetAttribute("action", config.actionID)
+  f:SetAttribute("default-action", config.actionID)
+  f:SetAttribute("showgrid",0)
+  f:SetAttribute("showgrid-temp",0)
+  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)
+  f:SetScript("OnDragStart", function(frame) self:OnDragStart() end)
+  f:SetScript("OnReceiveDrag", function(frame), self:OnReceiveDrag() end)
+
+  -- secure handlers
+  f:SetAttribute("_childupate", _childupdate)
+  f:SetAttribute("_childupdate-showgrid",_childupdate_showgrid)
+  barFrame:WrapScript(f, "OnDragStart", _onDragStart)
+  barFrame:WrapScript(f, "OnReceiveDrag", _onReceiveDrag)
+  if idx == 7 then
+    -- install vehicle-exit button on 7th button (only)
+    f:SetAttribute("_childupdate-vehicle", _childupdate_vehicleExit)
+    local button = self
+    function barFrame:ShowVehicleExit(show)
+      button:ShowVehicleExit(show)
+    end
+  end
+
+  -- event registration
+  f:RegisterForDrag("LeftButton", "RightButton")
+  f:RegisterForClicks("AnyUp")
+  for _, evt in pairs(eventList) do
+    f:RegisterEvent(evt)
+  end
+
+  -- attach to skinner
+  bar:SkinButton(self)
+
+  -- initial display
+  self:ShowGrid(not barFrame:GetConfig().hideEmpty)
+  if ReAction:GetConfigMode() then
+    self:ShowGrid(true)
+  end
+
+  self:Refresh()
+end
+
+function Action:Destroy()
+  local f = self:GetFrame()
+
+  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(id)
+    end
+  end
+  
+  Super:Destroy()
+end
+
+function Action:Refresh()
+  self.bar:PlaceButton(self, 36, 36)
+  self:RefreshPages()
+  self:UpdateAction()
+end
+
+function Action:UpdateAll()
+  self:UpdateActionIDLabel(ReAction:GetConfigMode())
+  self:UpdateHotkey()
+  self:UpdateShowGrid()
+  self:UpdateIcon()
+  self:UpdateBorder()
+  self:UpdateMacroText()
+  self:UpdateCount()
+  self:UpdateTooltip()
+  self:UpdateCheckedState()
+  self:UpdateUsable()
+  self:UpdateCooldown()
+  self:UpdateFlash()
+end
+
+function Action:UpdateAction()
+  local action = self:GetActionID()
+  if action ~= self.actionID then
+    self.actionID = action
+    self:UpdateAll()
+  end
+end
+
+function Action:UpdateShowGrid()
+  -- this is a little bit complicated because there's no
+  -- secure driver to handle show/hide grid events.
+  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))
+    for i = 1, self.nPages do
+      f:SetAttribute("hasaction-"..self.config.pageactions[i], HasAction(self.config.pageactions[i])
+    end
+    -- 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
+      f:Show()
+    else
+      f:Hide()
+    end
+  end
+end
+
+function Action:UpdateIcon()
+  local action = self.actionID
+  local texture = GetActionTexture(action)
+  local icon = self.frames.icon
+  local hotkey = self.frames.hotkey
+  local f = self:GetFrame()
+  if texture then
+    icon:SetTexture(texture)
+    icon:Show()
+    self.rangeTimer = -1
+    f:SetNormalTexture("Interface\\Buttons\\UI-Quickslot2")
+    hotkey:SetVertexColor(1.0, 1.0, 1.0)
+  else
+    icon:Hide()
+    self.frames.cooldown:Hide()
+    self.rangeTimer = nil
+    f:SetNormalTexture("Interface\\Buttons\\UI-Quickslot")
+    hotkey:SetVertexColor(0.6, 0.6, 0.6)
+    end
+  end
+end
+
+function Action:UpdateBorder()
+  local action = self.actionID
+  if ReAction:GetKeybindMode() then
+    self:UpdateKeybindModeDisplay(true)
+  elseif IsEquippedAction(action) then
+    self.frames.border:SetVertexColor(0, 1.0, 0, 0.35)
+    self.frames.border:Show()
+  else
+    self.frames.border:Hide()
+  end
+end
+
+function Action:UpdateMacroText()
+  local action = self.actionID
+  if not IsConsumableAction(action) and not IsStackableAction(action) then
+    self.frames.name:SetText(GetActionText(action))
+  else
+    self.frames.name:SetText("")
+  end
+end
+
+function Action:UpdateCount()
+  local action = self.actionID
+  if IsConsumableAction(action) or IsStackableAction(action) then
+    self.frames.count:SetText(GetActionCount(action))
+  else
+    self.frames.count:SetText("")
+  end
+end
+
+function Action:UpdateTooltip()
+  local f = self:GetFrame()
+  if GameTooltip:GetOwner() == f then
+    self:SetTooltip()
+  end
+end
+
+function Action:SetTooltip()
+  local f = self:GetFrame()
+  if GetCVar("UberTooltips") == "1" then
+    GameTooltip_SetDefaultAnchor(GameTooltip, f)
+  else
+    GameTooltip:SetOwner(f)
+  end
+  GameTooltip:SetAction(self.actionID)
+end
+
+function Action:UpdateCheckedState()
+  local action = self.actionID
+  if IsCurrentAction(action) or IsAutoRepeatAction(action) then
+    self:GetFrame():SetChecked(1)
+  else
+    self:GetFrame():SetChecked(0)
+  end
+end
+
+function Action:UpdateUsable()
+  local isUsable, notEnoughMana = IsUsableAction(self.actionID)
+  if isUsable then
+    self.frames.icon:SetVertexColor(1.0, 1.0, 1.0)
+    self.frames.normalTexture:SetVertexColor(1.0, 1.0, 1.0)
+  elseif notEnoughMana then
+    self.frames.icon:SetVertexColor(0.5, 0.5, 1.0)
+    self.frames.normalTexture:SetVertexColor(0.5, 0.5, 1.0)
+  else
+    self.frames.icon:SetVertexColor(0.4, 0.4, 0.4)
+    self.frames.normalTexture:SetVertexColor(1.0, 1.0, 1.0)
+  end
+end
+
+function Action:UpdateCooldown()
+  CooldownFrame_SetTimer(self.frames.cooldown, GetActionCooldown(self.actionID))
+end
+
+function Action:UpdateFlash()
+  local action = self.actionID
+  self:SetFlash( (IsAttackAction(action) and IsCurrentAction(action)) or IsAutoRepeatAction(action) )
+end
+
+function Action:SetFlash(flash)
+  if self.flashing ~= flash then
+    self.flashing = flash
+    self.flashtime = 0
+    if not flash then
+      self.frames.flash:Hide()
+    end
+    self:UpdateCheckedState()
+  end
+end
+
+function Action:RunFlash(elapsed)
+  if self.flashing then
+    local flashtime = self.flashtime - elapsed
+    self.flashtime = flashtime
+    if flashtime <= 0 then
+      local overtime = -flashtime
+      if overtime >= ATTACK_BUTTON_FLASH_TIME then
+        overtime = 0
+      end
+      flashtime = ATTACK_BUTTON_FLASH_TIME - overtime
+      local flash = self.frames.flash
+      if flash:IsShown() then
+        flash:Hide()
+      else
+        flash:Show()
+      end
+    end
+  end
+end
+
+function Action:RunRangeFinder(elapsed)
+  local rangeTimer = self.rangeTimer
+  if rangeTimer then
+    rangeTimer = rangeTimer - elapsed
+    self.rangeTimer = rangeTimer
+    if rangeTimer <= 0 then
+      if IsActionInRange(self.actionID) == 0 then
+        self.frames.icon:SetVertexColor(1.0,0.1,0.1)
+      else
+        self:UpdateUsable()
+      end
+      frame.rangeTimer = TOOLTIP_UPDATE_TIME
+    end
+  end
+end
+
+function Action:GetActionID(page)
+  if page == nil then
+    -- get the effective ID
+    return self:GetFrame():GetAttribute("action")
+  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 Action:SetActionID( id, page )
+  id = tonumber(id)
+  page = tonumber(page)
+  if id == nil or id < 1 or id > 120 then
+    error("Action:SetActionID - invalid action ID")
+  end
+  if page and page ~= 1 then
+    if not self.config.pageactions then
+      self.config.pageactions = { }
+    end
+    self:ReleaseActionID(self.config.pageactions[page])
+    self.config.pageactions[page] = id
+    self:AcquireActionID(self.config.pageactions[page])
+    self.frame:SetAttribute("action-page"..page,id)
+  else
+    self:ReleaseActionID(self.config.actionID)
+    self.config.actionID = id
+    self:AcquireActionID(self.config.actionID)
+    self.frame:SetAttribute("action",id)
+    self.frame:SetAttribute("default-action",id)
+    if self.config.pageactions then
+      self.config.pageactions[1] = id
+      self.frame:SetAttribute("action-page1",id)
+    end
+  end
+end
+
+function Action:RefreshPages( force )
+  local nPages = self.bar:GetConfig().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] = self:AcquireActionID(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
+      self:ReleaseActionID(c[i])
+      c[i] = nil
+      f:SetAttribute(("action-page%d"):format(i),nil)
+    end
+    self.nPages = nPages
+  end
+end
+
+function Action:SetupBarHeader( bar ) -- 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:Execute(
+    [[
+    doMindControl = self:GetAttribute("mindcontrol")
+    doVehicle = self:GetAttribute("vehicle")
+    control:ChildUpdate()
+    ]])
+
+  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:Execute(
+    [[
+      lockButtons = self:GetAttribute("lockbuttons")
+      lockButtonsCombat = self:GetAttribute("lockbuttonscombat")
+    ]])
+end
+
+
+function Action:ShowVehicleExit(show)
+  local f = self:GetFrame()
+  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
+
+function Action:OnEnter( )
+  self:SetTooltip()
+end
+
+function Action:OnLeave( )
+  GameTooltip:Hide()
+end
+
+function Action:OnAttributeChanged( attr, value )
+  self:UpdateAction()
+end
+
+function Action:PostClick( )
+  self:UpdateCheckedState()
+end
+
+function Action:OnUpdate( elapsed )
+  self:RunFlash(elapsed)
+  self:RunRangeFinder(elapsed)
+end
+
+function Action:OnDragStart()
+  self:UpdateCheckedState()
+  self:UpdateFlash()
+end
+
+function Action:OnReceiveDrag()
+  self:UpdateCheckedState()
+  self:UpdateFlash()
+end
+
+function Action:OnEvent(event, ...)
+  if self[event] then
+    self[event](self, event, ...)
+  end
+end
+
+function Action:ACTIONBAR_SLOT_CHANGED(event, action)
+  if action == 0 or action == self.actionID then
+    self:UpdateAction()
+  end
+end
+
+function Action:PLAYER_ENTERING_WORLD()
+  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
+
+function Action:PLAYER_TARGET_CHANGED()
+  self.rangeTimer = -1
+end
+
+function Action:ACTIONBAR_UPDATE_STATE()
+  self:UpdateCheckedState()
+end
+Action.TRADE_SKILL_SHOW = Action.ACTIONBAR_UPDATE_STATE
+Action.TRADE_SKILL_CLOSE = Action.ACTIONBAR_UPDATE_STATE
+
+function Action:UNIT_ENTERED_VEHICLE(event,unit)
+  if unit == "player" then
+    self:UpdateCheckedState()
+  end
+end
+Action.UNIT_EXITED_VEHICLE = Action.UNIT_ENTERED_VEHICLE
+
+function Action:COMPANION_UPDATE(event,unit)
+  if unit == "mount" then
+    self:UpdateCheckedState()
+  end
+end
+
+function Action:ACTIONBAR_UPDATE_USABLE()
+  self:UpdateUsable()
+end
+
+function Action:ACTIONBAR_UPDATE_COOLDOWN()
+  self:UpdateCooldown()
+end
+
+function Action:PLAYER_ENTER_COMBAT()
+  if IsAttackAction(self.actionID) then
+    self:SetFlash(true)
+  end
+end
+
+function Action:PLAYER_LEAVE_COMBAT()
+  if IsAttackAction(self.actionID) then
+    self:SetFlash(false)
+  end
+end
+
+function Action:START_AUTOREPEAT_SPELL()
+  if IsAutoRepeatAction(self.actionID) then
+    self:SetFlash(true)
+  end
+end
+
+function Action:STOP_AUTOREPEAT_SPELL()
+  if not IsAttackAction(self.actionID) then
+    self:SetFlash(false)
+  end
+end
+
+function Action:PLAYER_REGEN_ENABLED()
+  if self.showgridPending then
+    self:UpdateShowGrid()
+  end
+end