--[[------------------------------------------------------------------------- -- BindConfig.lua -- -- This file contains the definitions of the binding configuration panel. -- -- Events registered: -- None -------------------------------------------------------------------------]]-- local addonName, addon = ... local L = addon.L local MAX_ROWS = 12 function CliqueConfig:ShowWithSpellBook() self:ClearAllPoints() self:SetParent(SpellBookFrame) self:SetPoint("LEFT", SpellBookFrame, "RIGHT", 55, 0) self:Show() end function CliqueConfig:OnShow() if not self.initialized then self:SetupGUI() self:HijackSpellbook() self.initialized = true end -- Hide the alertTab if the spellbook isn't shown if SpellButton1:IsVisible() then self.bindAlert:Show() else self.bindAlert:Hide() end CliqueSpellTab:SetChecked(true) self:UpdateList() self:EnableSpellbookButtons() self:UpdateAlert() end function CliqueConfig:OnHide() self:ClearAllPoints() self:SetParent(UIParent) HideUIPanel(self) CliqueSpellTab:SetChecked(false) self:UpdateAlert() end -- compat:classic function CliqueConfig:SetupTalents() if not GetNumSpecializations then self.talents = {L["Default"]} else for i = 1, GetNumSpecializations() do local id, name = GetSpecializationInfo(i) self.talents[i] = name end end end function CliqueConfig:SetupGUI() self:SetupTalents() self.rows = {} for i = 1, MAX_ROWS do self.rows[i] = CreateFrame("Button", "CliqueRow" .. i, self.page1, "CliqueRowTemplate") end self.rows[1]:ClearAllPoints() self.rows[1]:SetPoint("TOPLEFT", "CliqueConfigPage1Column1", "BOTTOMLEFT", 0, -3) self.rows[1]:SetPoint("RIGHT", CliqueConfigPage1Column2, "RIGHT", 0, 0) for i = 2, MAX_ROWS do self.rows[i]:ClearAllPoints() self.rows[i]:SetPoint("TOPLEFT", self.rows[i - 1], "BOTTOMLEFT") self.rows[i]:SetPoint("RIGHT", CliqueConfigPage1Column2, "RIGHT", 0, 0) end self.TitleText:SetText(L["Clique Binding Configuration"]) self.dialog = _G["CliqueDialog"] self.dialog.title = self.dialog.TitleText self.dialog:SetUserPlaced(false) self.dialog:ClearAllPoints() self.dialog:SetPoint("CENTER", self, "CENTER", 30, 0) self.dialog.title:SetText(L["Set binding"]) self.dialog.button_accept:SetText(L["Accept"]) self.dialog.button_binding:SetText(L["Set binding"]) local desc = L["In order to specify a binding, move your mouse over the button labelled 'Set binding' and either click with your mouse or press a key on your keyboard. You can modify the binding by holding down a combination of the alt, control and shift keys on your keyboard."] self.dialog.desc:SetText(desc) self.alert = _G["CliqueTabAlert"] self.bindAlert.text:SetText(L["You are in Clique binding mode"]) self.close = _G[self:GetName() .. "CloseButton"] self.close:SetScript("OnClick", function() HideUIPanel(CliqueConfig) end) self.page1.column1:SetText(L["Action"]) self.page1.column2:SetText(L["Binding"]) -- Set columns up to handle sorting self.page1.column1.sortType = "name" self.page1.column2.sortType = "key" self.page1.sortType = self.page1.column2.sortType self.page1.button_spell:SetText(L["Bind spell"]) self.page1.button_other:SetText(L["Bind other"]) self.page1.button_options:SetText(L["Options"]) self.page2.button_binding:SetText(L["Set binding"]) self.page2.button_save:SetText(L["Save"]) self.page2.button_cancel:SetText(L["Cancel"]) local desc = L["You can use this page to create a custom macro to be run when activating a binding on a unit. When creating this macro you should keep in mind that you will need to specify the target of any actions in the macro by using the 'mouseover' unit, which is the unit you are clicking on. For example, you can do any of the following:\n\n/cast [target=mouseover] Regrowth\n/cast [@mouseover] Regrowth\n/cast [@mouseovertarget] Taunt\n\nHover over the 'Set binding' button below and either click or press a key with any modifiers you would like included. Then edit the box below to contain the macro you would like to have run when this binding is activated."] self.page2.desc:SetText(desc) self.page2.editbox = CliqueScrollFrameEditBox self.page1:Show() end function CliqueConfig:Column_OnClick(frame, button) self.page1.sortType = frame.sortType self:UpdateList() end function CliqueConfig:HijackSpellbook() self.spellbookButtons = {} for idx = 1, 12 do local parent = _G["SpellButton" .. idx] local button = CreateFrame("Button", "CliqueSpellbookButton" .. idx, parent, "CliqueSpellbookButtonTemplate") button.spellbutton = parent button:EnableKeyboard(false) button:EnableMouseWheel(true) button:RegisterForClicks("AnyDown") button:SetID(parent:GetID()) self.spellbookButtons[idx] = button end SpellBookFrame:HookScript("OnShow", function(frame) self:EnableSpellbookButtons() end) SpellBookFrame:HookScript("OnHide", function(frame) self:EnableSpellbookButtons() end) self:EnableSpellbookButtons() end function CliqueConfig:EnableSpellbookButtons() local enabled; if self.page1:IsVisible() and SpellBookFrame:IsVisible() then enabled = true end if self.spellbookButtons then for idx, button in ipairs(self.spellbookButtons) do if enabled and addon:APIIsTrue(button.spellbutton:IsEnabled()) then button:Show() else button:Hide() end end end end -- Spellbook button functions function CliqueConfig:Spellbook_EnableKeyboard(button, motion) button:EnableKeyboard(true) end function CliqueConfig:Spellbook_DisableKeyboard(button, motion) button:EnableKeyboard(false) end function CliqueConfig:Spellbook_OnBinding(button, key) if key == "ESCAPE" then HideUIPanel(CliqueConfig) return end local slot = SpellBook_GetSpellBookSlot(button:GetParent()); local name, spellSubName, subtype = GetSpellBookItemName(slot, SpellBookFrame.bookType) local texture = GetSpellBookItemTexture(slot, SpellBookFrame.bookType) if spellSubName == "" then spellSubName = nil end local key = addon:GetCapturedKey(key) if not key then return end local succ, err = addon:AddBinding{ key = key, type = "spell", spell = name, spellSubName = spellSubName, icon = texture } CliqueConfig:UpdateList() end function CliqueConfig:Button_OnClick(button) -- Click handler for "Bind spell" button if button == self.page1.button_spell then ShowUIPanel(SpellBookFrame) CliqueConfig:ShowWithSpellBook() -- Click handler for "Bind other" button elseif button == self.page1.button_other then local config = CliqueConfig local menu = { { text = L["Select a binding type"], isTitle = true, notCheckable = true, }, { text = L["Target clicked unit"], func = function() self:SetupCaptureDialog("target") end, notCheckable = true, }, { text = L["Open unit menu"], func = function() self:SetupCaptureDialog("menu") end, notCheckable = true, }, { text = L["Run custom macro"], func = function() config.page1:Hide() config.page2.bindType = "macro" -- Clear out the entries config.page2.bindText:SetText(L["No binding set"]) config.page2.editbox:SetText("") config.page2.button_save:Disable() config.page2:Show() end, notCheckable = true, }, } UIDropDownMenu_SetAnchor(self.dropdown, 0, 0, "BOTTOMLEFT", self.page1.button_other, "TOP") EasyMenu(menu, self.dropdown, nil, 0, 0, nil, nil) -- Click handler for "Edit" button elseif button == self.page1.button_options then local menu = { { text = L["Select an options category"], isTitle = true, notCheckable = true, }, { text = L["Clique general options"], func = function() HideUIPanel(SpellBookFrame) HideUIPanel(CliqueConfig) InterfaceOptionsFrame_OpenToCategory(addon.optpanels["GENERAL"]) end, notCheckable = true, }, { text = L["Frame blacklist"], func = function() HideUIPanel(SpellBookFrame) HideUIPanel(CliqueConfig) InterfaceOptionsFrame_OpenToCategory(addon.optpanels["BLACKLIST"]) end, notCheckable = true, }, { text = L["Blizzard frame integration options"], func = function() HideUIPanel(SpellBookFrame) HideUIPanel(CliqueConfig) InterfaceOptionsFrame_OpenToCategory(addon.optpanels["BLIZZFRAMES"]) end, notCheckable = true, }, } UIDropDownMenu_SetAnchor(self.dropdown, 0, 0, "BOTTOMLEFT", self.page1.button_options, "TOP") EasyMenu(menu, self.dropdown, nil, 0, 0, nil, nil) elseif button == self.page2.button_save then -- Check the input local key = self.page2.key local macrotext = self.page2.editbox:GetText() if self.page2.binding then self.page2.binding.key = key self.page2.binding.macrotext = macrotext self.page2.binding = nil addon:FireMessage("BINDINGS_CHANGED") else local succ, err = addon:AddBinding{ key = key, type = "macro", macrotext = macrotext, } end self:UpdateList() self.page2:Hide() self.page1:Show() elseif button == self.page2.button_cancel then self.page2.binding = nil self.page2:Hide() self.page1:Show() end end local memoizeBindings = setmetatable({}, {__index = function(t, k, v) local binbits = addon:GetBinaryBindingKey(k) rawset(t, k, binbits) return binbits end}) local compareFunctions; compareFunctions = { name = function(a, b) local texta = addon:GetBindingActionText(a.type, a) local textb = addon:GetBindingActionText(b.type, b) if texta == textb then return compareFunctions.key(a, b) end return texta < textb end, key = function(a, b) local keya = addon:GetBindingKey(a) local keyb = addon:GetBindingKey(b) if keya == keyb then return memoizeBindings[a] < memoizeBindings[b] elseif not keya or not keyb then return false else return keya < keyb end end, binding = function(a, b) local mem = memoizeBindings if mem[a] == mem[b] then return compareFunctions.name(a, b) else return mem[a] < mem[b] end end, } -- Mapping between binding entry and index in profile function CliqueConfig:UpdateList() local page = self.page1 local binds = addon.bindings -- GUI not created yet if not self.initialized then return elseif not self:IsVisible() then return end -- Sort the bindings local sort = {} for idx, entry in pairs(binds) do sort[#sort + 1] = entry end if page.sortType then table.sort(sort, compareFunctions[page.sortType]) else table.sort(sort, compareFunctions.key) end -- Enable or disable the scroll bar if #sort > MAX_ROWS - 1 then -- Set up the scrollbar for the item list page.slider:SetMinMaxValues(0, #sort - MAX_ROWS) -- Adjust and show if not page.slider:IsShown() then -- Adjust column positions for idx, row in ipairs(self.rows) do row.bind:SetWidth(90) end page.slider:SetValue(0) page.slider:Show() end elseif page.slider:IsShown() then -- Move column positions back and hide the slider for idx, row in ipairs(self.rows) do row.bind:SetWidth(105) end page.slider:Hide() end -- Update the rows in the list local offset = page.slider:GetValue() or 0 for idx, row in ipairs(self.rows) do local offsetIndex = offset + idx if sort[offsetIndex] then local bind = sort[offsetIndex] row.icon:SetTexture(addon:GetBindingIcon(bind)) row.name:SetText(addon:GetBindingActionText(bind.type, bind)) row.info:SetText(addon:GetBindingInfoText(bind)) row.bind:SetText(addon:GetBindingKeyComboText(bind)) row.binding = bind row:Show() else row:Hide() end end end function CliqueConfig:ClearEditPage() end function CliqueConfig:ShowEditPage() self:ClearEditPage() self.page1:Hide() self.page3:Show() end function CliqueConfig:Save_OnClick(button, down) end function CliqueConfig:Cancel_OnClick(button, down) self:ClearEditPage() self.page3:Hide() self.page1:Show() end function CliqueConfig:SetupCaptureDialog(type, binding) self.dialog.bindType = type self.dialog.binding = binding if not binding then local actionText = addon:GetBindingActionText(type, binding) self.dialog.title:SetText(L["Set binding: %s"]:format(actionText)) else -- This is a change to an existing binding local actionText = addon:GetBindingActionText(type, binding) self.dialog.title:SetText(L["Change binding: %s"]:format(actionText)) end self.dialog.bindText:SetText("") self.dialog:Show() end function CliqueConfig:BindingButton_OnClick(button, key) local dialog = CliqueDialog dialog.key = addon:GetCapturedKey(key) if dialog.key then CliqueDialog.bindText:SetText(addon:GetBindingKeyComboText(dialog.key)) end end function CliqueConfig:MacroBindingButton_OnClick(button, key) local key = addon:GetCapturedKey(key) if key then self.page2.key = key self.page2.bindText:SetText(addon:GetBindingKeyComboText(key)) self.page2.button_save:Enable() else self.page2.bindText:SetText(L["No binding set"]) self.page2.button_save:Disable() end end function CliqueConfig:AcceptSetBinding() local dialog = CliqueDialog local key = dialog.key if dialog.binding then -- This was a CHANGE binding instead of a SET binding dialog.binding.key = key dialog.binding = nil -- Do not forget to update the attributes as well self:UpdateList() addon:FireMessage("BINDINGS_CHANGED") else local succ, err = addon:AddBinding{ key = key, type = dialog.bindType, } if succ then self:UpdateList() end end dialog:Hide() end local function toggleSet(binding, set, ...) local exclude = {} for i = 1, select("#", ...) do local item = select(i, ...) table.insert(exclude, item) end return function() if not binding.sets then binding.sets = {} end if binding.sets[set] then binding.sets[set] = nil else binding.sets[set] = true end for idx, exclset in ipairs(exclude) do binding.sets[exclset] = nil end UIDropDownMenu_Refresh(UIDROPDOWNMENU_OPEN_MENU, nil, UIDROPDOWNMENU_MENU_LEVEL) CliqueConfig:UpdateList() addon:FireMessage("BINDINGS_CHANGED") end end function CliqueConfig:Row_OnClick(frame, button) local binding = frame.binding local actionText = addon:GetBindingActionText(binding.type, binding) local menu = { { text = L["Configure binding: '%s'"]:format(actionText:sub(1,15)), notCheckable = true, isTitle = true, }, { text = L["Change binding"], func = function() local binding = frame.binding self:SetupCaptureDialog(binding.type, binding) end, notCheckable = true, }, { text = L["Delete binding"], func = function() addon:DeleteBinding(frame.binding) self:UpdateList() end, notCheckable = true, }, } if binding.type == "spell" and binding.spellSubName then -- Enable a 'Remove Rank' option table.insert(menu, { text = L["Remove spell rank"], func = function() local binding = frame.binding binding.spellSubName = null self:UpdateList() addon:FireMessage("BINDINGS_CHANGED") end, notCheckable = true, }) end if binding.type == "macro" then -- Replace 'Change Binding' with 'Edit macro' menu[2] = { text = L["Edit macro"], func = function() self.page2.bindType = "macro" local bindText = addon:GetBindingKeyComboText(binding) self.page2.bindText:SetText(bindText) self.page2.binding = binding self.page2.key = binding.key self.page2.editbox:SetText(binding.macrotext) self.page2.button_save:Enable() self.page1:Hide() self.page2:Show() end, notCheckable = true, } end local submenu = { text = L["Enable/Disable binding-sets"], hasArrow = true, notCheckable = true, menuList = {}, } table.insert(menu, submenu) table.insert(submenu.menuList, { text = L["Default"], checked = function() return binding.sets["default"] end, func = toggleSet(binding, "default"), tooltipTitle = L["Clique: 'default' binding-set"], tooltipText = L["A binding that belongs to the 'default' binding-set will always be active on your unit frames, unless you override it with another binding."], keepShownOnClick = true, }) table.insert(submenu.menuList, { text = L["Friend"], checked = function() return binding.sets["friend"] end, func = toggleSet(binding, "friend"), tooltipTitle = L["Clique: 'friend' binding-set"], tooltipText = L["A binding that belongs to the 'frield' binding-set will only be active when clicking on unit frames that display friendly units, i.e. those you can heal and assist. If you click on a unit that you cannot heal or assist, nothing will happen."], keepShownOnClick = true, }) table.insert(submenu.menuList, { text = L["Enemy"], checked = function() return binding.sets["enemy"] end, func = toggleSet(binding, "enemy"), tooltipTitle = L["Clique: 'enemy' binding-set"], tooltipText = L["A binding that belongs to the 'enemy' binding-set will always be active when clicking on unit frames that display enemy units, i.e. those you can attack. If you click on a unit that you cannot attack, nothing will happen."], keepShownOnClick = true, }) table.insert(submenu.menuList, { text = L["Out-of-combat (ONLY)"], checked = function() return binding.sets["ooc"] end, func = toggleSet(binding, "ooc"), tooltipTitle = L["Clique: 'ooc' binding-set"], tooltipText = L["A binding that belongs to the 'ooc' binding-set will only be active when the player is out-of-combat, regardless of the other binding-sets this binding belongs to. As soon as the player enters combat, these bindings will no longer be active, so be careful when choosing this binding-set for any spells you use frequently."], keepShownOnClick = true, }) -- Add some buttons for the talent specs for i = 1, #self.talents do local key = "spec" .. i table.insert(submenu.menuList, { text = L["Talents: %s"]:format(self.talents[i]), checked = function() return binding.sets[key] end, func = toggleSet(binding, key), tooltipTitle = L["Clique: talent set %d binding-set"].format(i), tooltipText = L["A binding that belongs to this binding-set is only active when the player has the given talent specialization active"], keepShownOnClick = true, }) end table.insert(submenu.menuList, { text = L["Hovercast bindings (target required)"], checked = function() return binding.sets["hovercast"] end, func = toggleSet(binding, "hovercast", "global"), tooltipTitle = L["Clique: 'hovercast' binding-set"], tooltipText = L["A binding that belongs to the 'hovercast' binding-set is active whenever the mouse is over a unit frame, or a character in the 3D world. This allows you to use 'hovercasting', where you hover over a unit in the world and press a key to cast a spell on them. THese bindings are also active over unit frames."], keepShownOnClick = true, }) table.insert(submenu.menuList, { text = L["Global bindings (no target)"], checked = function() return binding.sets["global"] end, func = toggleSet(binding, "global", "hovercast"), tooltipTitle = L["Clique: 'global' binding-set"], tooltipText = L["A binding that belongs to the 'global' binding-set is always active. If the spell requires a target, you will be given the 'casting hand', otherwise the spell will be cast. If the spell is an AOE spell, then you will be given the ground targeting circle."], keepShownOnClick = true, }) EasyMenu(menu, self.dropdown, "cursor", 0, 0, nil, nil) end function CliqueConfig:SpellTab_OnClick(frame) if self:IsVisible() then HideUIPanel(CliqueConfig) elseif SpellBookFrame:IsVisible() then self:ShowWithSpellBook() else ShowUIPanel(CliqueConfig) end end function CliqueConfig:UpdateAlert(type) local alert = CliqueTabAlert if not addon.settings.alerthidden and SpellBookFrame:IsVisible() and CliqueConfig:IsVisible() then alert.type = type alert.text:SetText(L["When both the Clique binding configuration window and the spellbook are open, you can set new bindings simply by performing them on the spell icon in your spellbook. Simply move your mouse over a spell and then click or press a key on your keyboard along with any combination of the alt, control, and shift keys. The new binding will be added to your binding configuration."]) alert:Show() else alert:Hide() end end