WoWInterface SVN PhanxConfigWidgets

Compare Revisions

  • This comparison shows the changes necessary to convert path
    /
    from Rev 57 to Rev 56
    Reverse comparison

Rev 57 → Rev 56

branches/LibDualSpec-1.0/LibDualSpec-1.0.lua New file
0,0 → 1,330
--[[
LibDualSpec-1.0 - Adds dual spec support to individual AceDB-3.0 databases
Copyright (C) 2009-2011 Adirelle
 
All rights reserved.
 
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
 
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Redistribution of a stand alone version is strictly prohibited without
prior written authorization from the LibDualSpec project manager.
* Neither the name of the LibDualSpec authors nor the names of its contributors
may be used to endorse or promote products derived from this software without
specific prior written permission.
 
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
--]]
 
local MAJOR, MINOR = "LibDualSpec-1.0", 8
assert(LibStub, MAJOR.." requires LibStub")
local lib = LibStub:NewLibrary(MAJOR, MINOR)
if not lib then return end
 
-- ----------------------------------------------------------------------------
-- Library data
-- ----------------------------------------------------------------------------
 
lib.eventFrame = lib.eventFrame or CreateFrame("Frame")
 
lib.registry = lib.registry or {}
lib.options = lib.options or {}
lib.mixin = lib.mixin or {}
 
-- ----------------------------------------------------------------------------
-- Locals
-- ----------------------------------------------------------------------------
 
local registry = lib.registry
local options = lib.options
local mixin = lib.mixin
 
-- "Externals"
local AceDB3 = LibStub('AceDB-3.0', true)
local AceDBOptions3 = LibStub('AceDBOptions-3.0', true)
 
-- ----------------------------------------------------------------------------
-- Localization
-- ----------------------------------------------------------------------------
 
local L_DUALSPEC_DESC, L_ENABLED, L_ENABLED_DESC, L_DUAL_PROFILE, L_DUAL_PROFILE_DESC
 
do
L_DUALSPEC_DESC = "When enabled, this feature allow you to select a different "..
"profile for each talent spec. The dual profile will be swapped with the "..
"current profile each time you switch from a talent spec to the other."
L_ENABLED = 'Enable dual profile'
L_ENABLED_DESC = 'Check this box to automatically swap profiles on talent switch.'
L_DUAL_PROFILE = 'Dual profile'
L_DUAL_PROFILE_DESC = 'Select the profile to swap with on talent switch.'
 
local locale = GetLocale()
if locale == "frFR" then
L_DUALSPEC_DESC = "Lorsqu'elle est activée, cette fonctionnalité vous permet de choisir un profil différent pour chaque spécialisation de talents. Le second profil sera échangé avec le profil courant chaque fois que vous passerez d'une spécialisation à l'autre."
L_DUAL_PROFILE = "Second profil"
L_DUAL_PROFILE_DESC = "Sélectionnez le profil à échanger avec le profil courant lors du changement de spécialisation."
L_ENABLED = "Activez le second profil"
L_ENABLED_DESC = "Cochez cette case pour échanger automatiquement les profils lors d'un changement de spécialisation."
elseif locale == "deDE" then
L_DUALSPEC_DESC = "Wenn aktiv, wechselt dieses Feature bei jedem Wechsel der dualen Talentspezialisierung das Profil. Das duale Profil wird beim Wechsel automatisch mit dem derzeit aktiven Profil getauscht."
L_DUAL_PROFILE = "Duales Profil"
L_DUAL_PROFILE_DESC = "Wähle das Profil, das beim Wechsel der Talente aktiviert wird."
L_ENABLED = "Aktiviere Duale Profile"
L_ENABLED_DESC = "Aktiviere diese Option, um beim Talentwechsel automatisch zwischen den Profilen zu wechseln."
elseif locale == "koKR" then
L_DUALSPEC_DESC = "가능하면 사용합니다. 이중 특성에 의하여 다른 프로필을 선택할 수 있게 하니다. 이중 프로필은 현재 프로필과 번갈아서 특성이 변경될 때 같이 적용됩니다."
L_DUAL_PROFILE = "이중 프로필"
L_DUAL_PROFILE_DESC = "특성이 바뀔 때 프로필을 선택합니다."
L_ENABLED = "이중 프로필 사용"
L_ENABLED_DESC = "특성이 변경 될때 자동으로 프로필을 변경하도록 선택합니다."
elseif locale == "ruRU" then
L_DUALSPEC_DESC = "Двойной профиль позволяет вам выбрать различные профили для каждой раскладки талантов. Профили будут переключаться каждый раз, когда вы переключаете раскладку талантов."
L_DUAL_PROFILE = "Второй профиль"
L_DUAL_PROFILE_DESC = "Выберите профиль, который необходимо активировать при переключениии талантов."
L_ENABLED = "Включить двойной профиль"
L_ENABLED_DESC = "Включите эту опцию для автоматического переключения между профилями при переключении раскладки талантов."
elseif locale == "zhCN" then
L_DUALSPEC_DESC = "启时,你可以为你的双天赋设定另一组配置文件,你的双重配置文件将在你转换天赋时自动与目前使用配置文件交换。"
L_DUAL_PROFILE = "双重配置文件"
L_DUAL_PROFILE_DESC = "选择转换天赋时所要使用的配置文件"
L_ENABLED = "开启双重配置文件"
L_ENABLED_DESC = "勾选以便转换天赋时自动交换配置文件。"
elseif locale == "zhTW" then
L_DUALSPEC_DESC = "啟用時,你可以為你的雙天賦設定另一組設定檔。你的雙設定檔將在你轉換天賦時自動與目前使用設定檔交換。"
L_DUAL_PROFILE = "雙設定檔"
L_DUAL_PROFILE_DESC = "選擇轉換天賦後所要使用的設定檔"
L_ENABLED = "啟用雙設定檔"
L_ENABLED_DESC = "勾選以在轉換天賦時自動交換設定檔"
elseif locale == "esES" then
L_DUALSPEC_DESC = "Si está activa, esta característica te permite seleccionar un perfil distinto para cada configuración de talentos. El perfil secundario será intercambiado por el activo cada vez que cambies de una configuración de talentos a otra."
L_DUAL_PROFILE = "Perfil secundario"
L_DUAL_PROFILE_DESC = "Elige el perfil secundario que se usará cuando cambies de talentos."
L_ENABLED = "Activar perfil secundario"
L_ENABLED_DESC = "Activa esta casilla para alternar automáticamente entre prefiles cuando cambies de talentos."
end
end
 
-- ----------------------------------------------------------------------------
-- Mixin
-- ----------------------------------------------------------------------------
 
--- Get dual spec feature status.
-- @return (boolean) true is dual spec feature enabled.
-- @name enhancedDB:IsDualSpecEnabled
function mixin:IsDualSpecEnabled()
return registry[self].db.char.enabled
end
 
--- Enable/disabled dual spec feature.
-- @param enabled (boolean) true to enable dual spec feature, false to disable it.
-- @name enhancedDB:SetDualSpecEnabled
function mixin:SetDualSpecEnabled(enabled)
local db = registry[self].db
if enabled and not db.char.talentGroup then
db.char.talentGroup = lib.talentGroup
db.char.profile = self:GetCurrentProfile()
db.char.enabled = true
else
db.char.enabled = enabled
self:CheckDualSpecState()
end
end
 
--- Get the alternate profile name.
-- Defaults to the current profile.
-- @return (string) Alternate profile name.
-- @name enhancedDB:GetDualSpecProfile
function mixin:GetDualSpecProfile()
return registry[self].db.char.profile or self:GetCurrentProfile()
end
 
--- Set the alternate profile name.
-- No validation are done to ensure the profile is valid.
-- @param profileName (string) the profile name to use.
-- @name enhancedDB:SetDualSpecProfile
function mixin:SetDualSpecProfile(profileName)
registry[self].db.char.profile = profileName
end
 
--- Check if a profile swap should occur.
-- Do nothing if the dual spec feature is disabled. In the other
-- case, if the internally stored talent spec is different from the
-- actual active talent spec, the database swaps to the alternate profile.
-- There is normally no reason to call this method directly as LibDualSpec
-- takes care of calling it at appropriate times.
-- @name enhancedDB:CheckDualSpecState
function mixin:CheckDualSpecState()
local db = registry[self].db
if lib.talentsLoaded and db.char.enabled and db.char.talentGroup ~= lib.talentGroup then
local currentProfile = self:GetCurrentProfile()
local newProfile = db.char.profile
db.char.talentGroup = lib.talentGroup
if newProfile ~= currentProfile then
self:SetProfile(newProfile)
db.char.profile = currentProfile
end
end
end
 
-- ----------------------------------------------------------------------------
-- AceDB-3.0 support
-- ----------------------------------------------------------------------------
 
local function EmbedMixin(target)
for k,v in pairs(mixin) do
rawset(target, k, v)
end
end
 
-- Upgrade existing mixins
for target in pairs(registry) do
EmbedMixin(target)
end
 
-- Actually enhance the database
-- This is used on first initialization and everytime the database is reset using :ResetDB
function lib:_EnhanceDatabase(event, target)
registry[target].db = target:GetNamespace(MAJOR, true) or target:RegisterNamespace(MAJOR)
EmbedMixin(target)
target:CheckDualSpecState()
end
 
--- Embed dual spec feature into an existing AceDB-3.0 database.
-- LibDualSpec specific methods are added to the instance.
-- @name LibDualSpec:EnhanceDatabase
-- @param target (table) the AceDB-3.0 instance.
-- @param name (string) a user-friendly name of the database (best bet is the addon name).
function lib:EnhanceDatabase(target, name)
AceDB3 = AceDB3 or LibStub('AceDB-3.0', true)
if type(target) ~= "table" then
error("Usage: LibDualSpec:EnhanceDatabase(target, name): target should be a table.", 2)
elseif type(name) ~= "string" then
error("Usage: LibDualSpec:EnhanceDatabase(target, name): name should be a string.", 2)
elseif not AceDB3 or not AceDB3.db_registry[target] then
error("Usage: LibDualSpec:EnhanceDatabase(target, name): target should be an AceDB-3.0 database.", 2)
elseif target.parent then
error("Usage: LibDualSpec:EnhanceDatabase(target, name): cannot enhance a namespace.", 2)
elseif registry[target] then
return
end
registry[target] = { name = name }
lib:_EnhanceDatabase("EnhanceDatabase", target)
target.RegisterCallback(lib, "OnDatabaseReset", "_EnhanceDatabase")
end
 
-- ----------------------------------------------------------------------------
-- AceDBOptions-3.0 support
-- ----------------------------------------------------------------------------
 
local function NoDualSpec()
return GetNumTalentGroups() == 1
end
 
options.dualSpecDesc = {
name = L_DUALSPEC_DESC,
type = 'description',
order = 40.1,
hidden = NoDualSpec,
}
 
options.enabled = {
name = L_ENABLED,
desc = L_ENABLED_DESC,
type = 'toggle',
order = 40.2,
get = function(info) return info.handler.db:IsDualSpecEnabled() end,
set = function(info, value) info.handler.db:SetDualSpecEnabled(value) end,
hidden = NoDualSpec,
}
 
options.dualProfile = {
name = L_DUAL_PROFILE,
desc = L_DUAL_PROFILE_DESC,
type = 'select',
order = 40.3,
get = function(info) return info.handler.db:GetDualSpecProfile() end,
set = function(info, value) info.handler.db:SetDualSpecProfile(value) end,
values = "ListProfiles",
arg = "common",
hidden = NoDualSpec,
disabled = function(info) return not info.handler.db:IsDualSpecEnabled() end,
}
 
--- Embed dual spec options into an existing AceDBOptions-3.0 option table.
-- @name LibDualSpec:EnhanceOptions
-- @param optionTable (table) The option table returned by AceDBOptions-3.0.
-- @param target (table) The AceDB-3.0 the options operate on.
function lib:EnhanceOptions(optionTable, target)
AceDBOptions3 = AceDBOptions3 or LibStub('AceDBOptions-3.0', true)
if type(optionTable) ~= "table" then
error("Usage: LibDualSpec:EnhanceOptions(optionTable, target): optionTable should be a table.", 2)
elseif type(target) ~= "table" then
error("Usage: LibDualSpec:EnhanceOptions(optionTable, target): target should be a table.", 2)
elseif not (AceDBOptions3 and AceDBOptions3.optionTables[target]) then
error("Usage: LibDualSpec:EnhanceOptions(optionTable, target): optionTable is not an AceDBOptions-3.0 table.", 2)
elseif optionTable.handler.db ~= target then
error("Usage: LibDualSpec:EnhanceOptions(optionTable, target): optionTable must be the option table of target.", 2)
elseif not registry[target] then
error("Usage: LibDualSpec:EnhanceOptions(optionTable, target): EnhanceDatabase should be called before EnhanceOptions(optionTable, target).", 2)
elseif optionTable.plugins and optionTable.plugins[MAJOR] then
return
end
if not optionTable.plugins then
optionTable.plugins = {}
end
optionTable.plugins[MAJOR] = options
end
 
-- ----------------------------------------------------------------------------
-- Inspection
-- ----------------------------------------------------------------------------
 
local function iterator(registry, key)
local data
key, data = next(registry, key)
if key then
return key, data.name
end
end
 
--- Iterate through enhanced AceDB3.0 instances.
-- The iterator returns (instance, name) pairs where instance and name are the
-- arguments that were provided to lib:EnhanceDatabase.
-- @name LibDualSpec:IterateDatabases
-- @return Values to be used in a for .. in .. do statement.
function lib:IterateDatabases()
return iterator, lib.registry
end
 
-- ----------------------------------------------------------------------------
-- Switching logic
-- ----------------------------------------------------------------------------
 
lib.eventFrame:RegisterEvent('PLAYER_TALENT_UPDATE')
lib.eventFrame:SetScript('OnEvent', function()
lib.talentsLoaded = true
local newTalentGroup = GetActiveTalentGroup()
if lib.talentGroup ~= newTalentGroup then
lib.talentGroup = newTalentGroup
for target in pairs(registry) do
target:CheckDualSpecState()
end
end
end)
 
 
Property changes : Added: svn:eol-style + native
branches/AceGUI-3.0-Spell-EditBox/Spells-Editbox.lua New file
0,0 → 1,12
local AceGUI = LibStub("AceGUI-3.0")
do
local Type = "Spell_EditBox"
local Version = 3
 
-- I know theres a better way of doing this than this, but not sure for the time being, works fine though!
local function Constructor()
return AceGUI:Create("Predictor_Base")
end
 
AceGUI:RegisterWidgetType(Type, Constructor, Version)
end
Property changes : Added: svn:eol-style + native
branches/AceGUI-3.0-Spell-EditBox/AceGUI-3.0-Spell-EditBox.xml New file
0,0 → 1,8
<Ui xmlns="http://www.blizzard.com/wow/ui/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.blizzard.com/wow/ui/">
<Script file="SpellLoader.lua"/>
<Script file="EditBox-Core.lua"/>
<Script file="Spells-EditBox.lua"/>
<Script file="Casts-EditBox.lua"/>
<Script file="Auras-EditBox.lua"/>
<Script file="Player-EditBox.lua"/>
</Ui>
Property changes : Added: svn:eol-style + native
branches/AceGUI-3.0-Spell-EditBox/AceGUI-3.0-Spell-EditBox.lua New file
0,0 → 1,475
-- Based off of AceGUI-3.0 EditBox
local AceGUI = LibStub("AceGUI-3.0")
do
local Type = "Spell_EditBox"
local Version = 2
local PREDICTION_ROWS = 10
local totalSpellsLoaded, spellLoader = 0
local spells, indexedSpells, visiblePredicters = {}, {}, {}
 
-- Defined blew
local searchSpells
 
-- Spells have to gradually be loaded in to prevent the client from lagging, this starts as soon as one widget is shown
-- as of 3.1 the spellID goes up to ~66,000 which means it'll take around 5 - 10 seconds for it to load them all
-- Given users have to actually move the mouse, type what they want etc
-- it should result in them not noticing it does not have all the spell data yet
local function startLoading()
if( spellLoader ) then return end
 
spellLoader = CreateFrame("Frame")
spellLoader.timeElapsed = 0
spellLoader.totalInvalid = 0
spellLoader.index = 0
spellLoader:SetScript("OnUpdate", function(self, elapsed)
self.timeElapsed = self.timeElapsed + elapsed
if( self.timeElapsed < 0.10 ) then return end
self.timeElapsed = self.timeElapsed - 0.10
 
-- Too many invalid spells found will assume we found all there is that we can
if( self.totalInvalid >= 5000 ) then
self:Hide()
return
end
 
-- Load as many spells in
local spellsLoaded = totalSpellsLoaded
for i=spellLoader.index + 1, spellLoader.index + 500 do
local name, _, icon = GetSpellInfo(i)
 
-- The majority of spells that use the engineer gear icon are actually invalid spells that we can easily ignore
-- since there are ~12000 not counting duplicate names that use this icon it's worthwhile to filter out these spells
self.totalInvalid = self.totalInvalid + 1
if( name and icon ~= "Interface\\Icons\\Trade_Engineering" ) then
name = string.lower(name)
 
if( not spells[name] ) then
spells[string.lower(name)] = i
table.insert(indexedSpells, name)
 
totalSpellsLoaded = totalSpellsLoaded + 1
self.totalInvalid = 0
end
end
end
 
-- Every ~1 second it will update any visible predicters to make up for the fact that the data is delay loaded
if( spellLoader.index % 5000 == 0 ) then
for predicter in pairs(visiblePredicters) do
searchSpells(predicter, predicter.lastQuery)
end
end
 
-- Increment and do it all over!
spellLoader.index = spellLoader.index + 500
end)
end
 
-- Search for spells quickly
searchSpells = function(self, query)
for _, button in pairs(self.buttons) do button:Hide() end
 
local usedButtons = 0
for i=1, totalSpellsLoaded do
local name = indexedSpells[i]
if( string.match(name, query) ) then
usedButtons = usedButtons + 1
 
local spellName, _, spellIcon = GetSpellInfo(spells[name])
local button = self.buttons[usedButtons]
button.spellID = spells[name]
button:SetFormattedText("|T%s:20:20:2:11|t %s", spellIcon, spellName)
button:Show()
 
if( usedButtons ~= self.selectedButton ) then
button:UnlockHighlight()
 
if( GameTooltip:IsOwned(button) ) then
GameTooltip:Hide()
end
end
 
-- Ran out of text to suggest :<
if( usedButtons >= PREDICTION_ROWS ) then break end
end
end
 
if( usedButtons > 0 ) then
self:SetHeight(15 + usedButtons * 17)
self:Show()
else
self:Hide()
end
 
self.lastQuery = query
self.usedButtons = usedButtons
end
 
local function OnAcquire(self)
self:SetHeight(26)
self:SetWidth(200)
self:SetDisabled(false)
self:SetLabel()
self.showbutton = true
end
 
local function OnRelease(self)
self.frame:ClearAllPoints()
self.frame:Hide()
self.predictFrame:Hide()
 
self:SetDisabled(false)
end
 
local function Control_OnEnter(this)
this.obj:Fire("OnEnter")
end
 
local function Control_OnLeave(this)
this.obj:Fire("OnLeave")
end
 
local function EditBox_OnEscapePressed(this)
this:ClearFocus()
end
 
local function ShowButton(self)
if( self.lasttext ~= "" ) then
self.editbox.predictFrame.selectedButton = nil
searchSpells(self.editbox.predictFrame, "^" .. string.lower(self.lasttext))
else
self.editbox.predictFrame:Hide()
end
 
if( self.showbutton ) then
self.button:Show()
self.editbox:SetTextInsets(0,20,3,3)
end
end
 
local function HideButton(self)
self.button:Hide()
self.editbox:SetTextInsets(0,0,3,3)
self.editbox.predictFrame:Hide()
end
 
local function EditBox_OnEnterPressed(this)
if( this.predictFrame.selectedButton ) then
this.predictFrame.buttons[this.predictFrame.selectedButton]:Click()
this.predictFrame.selectedButton = nil
return
end
 
local self = this.obj
local value = this:GetText()
local cancel = self:Fire("OnEnterPressed", value)
if( not cancel ) then
HideButton(self)
end
 
-- Reactivate the cursor, odds are if you're adding auras you're adding multiple auras
self.editbox:SetFocus()
end
 
local function Button_OnClick(this)
local editbox = this.obj.editbox
 
editbox:ClearFocus()
EditBox_OnEnterPressed(editbox)
end
 
local function Predicter_OnHide(self)
-- Allow users to use arrows to go back and forth again without the fix
self.editbox:SetAltArrowKeyMode(false)
 
visiblePredicters[self] = nil
 
ClearOverrideBindings(self)
end
 
local function Predicter_OnShow(self)
-- I'm pretty sure this is completely against what you are supposed to actually do :>
visiblePredicters[self] = true
 
-- User doesn't need arrow keys, and by doing this the override binding for up/down arrows will work properly
self.editbox:SetAltArrowKeyMode(true)
 
SetOverrideBindingClick(self, true, "DOWN", self:GetName(), 1)
SetOverrideBindingClick(self, true, "UP", self:GetName(), -1)
SetOverrideBindingClick(self, true, "LEFT", self:GetName(), "LEFT")
SetOverrideBindingClick(self, true, "RIGHT", self:GetName(), "RIGHT")
end
 
-- When using SetAltArrowKeyMode the ability to move the cursor with left and right is disabled, this reenables that
-- since the cursor position automatically can't go below 0, this is a quick and easy fix
local function EditBox_FixCursorPosition(self, direction)
self:SetCursorPosition(self:GetCursorPosition() + (direction == "RIGHT" and 1 or -1))
end
 
local function EditBox_OnReceiveDrag(this)
local self = this.obj
local type, id, info = GetCursorInfo()
if( type == "spell" ) then
local name = GetSpellName(id, info)
self:SetText(name)
self:Fire("OnEnterPressed" ,name)
ClearCursor()
end
HideButton(self)
AceGUI:ClearFocus()
end
 
local function EditBox_OnTextChanged(this)
local self = this.obj
local value = this:GetText()
if( value ~= self.lasttext ) then
self:Fire("OnTextChanged", value)
self.lasttext = value
ShowButton(self)
end
end
 
local function EditBox_OnEditFocusLost(self)
Predicter_OnHide(self.predictFrame)
end
 
local function EditBox_OnEditFocusGained(self)
if( self.predictFrame:IsVisible() ) then
Predicter_OnShow(self.predictFrame)
end
end
 
local function SetDisabled(self, disabled)
self.disabled = disabled
if( disabled ) then
self.editbox:EnableMouse(false)
self.editbox:ClearFocus()
self.editbox:SetTextColor(0.5, 0.5, 0.5)
self.label:SetTextColor(0.5, 0.5, 0.5)
else
self.editbox:EnableMouse(true)
self.editbox:SetTextColor(1, 1, 1)
self.label:SetTextColor(1, 0.82, 0)
end
end
 
local function SetText(self, text, cursor)
self.lasttext = text or ""
self.editbox:SetText(self.lasttext)
self.editbox:SetCursorPosition(cursor or 0)
 
HideButton(self)
end
 
local function SetLabel(self, text)
if( text and text ~= "" ) then
self.label:SetText(text)
self.label:Show()
self.editbox:SetPoint("TOPLEFT", self.frame, "TOPLEFT", 7, -18)
self:SetHeight(44)
self.alignoffset = 30
else
self.label:SetText("")
self.label:Hide()
self.editbox:SetPoint("TOPLEFT", self.frame, "TOPLEFT", 7, 0)
self:SetHeight(26)
self.alignoffset = 12
end
end
 
local function Predicter_OnMouseDown(self, direction)
if( direction == "LEFT" or direction == "RIGHT" ) then
EditBox_FixCursorPosition(self.editbox, direction)
return
end
 
self.selectedButton = (self.selectedButton or 0) + direction
if( self.selectedButton > self.usedButtons ) then
self.selectedButton = 1
elseif( self.selectedButton <= 0 ) then
self.selectedButton = self.usedButtons
end
 
for i=1, self.usedButtons do
local button = self.buttons[i]
if( i == self.selectedButton ) then
button:LockHighlight()
 
GameTooltip:SetOwner(button, "ANCHOR_BOTTOMRIGHT")
GameTooltip:SetHyperlink("spell:" .. button.spellID)
else
button:UnlockHighlight()
 
if( GameTooltip:IsOwned(button) ) then
GameTooltip:Hide()
end
end
end
end
 
local function Spell_OnClick(self)
local name = GetSpellInfo(self.spellID)
 
SetText(self.parent.obj, name, string.len(name))
self.parent.obj:Fire("OnEnterPressed", name)
end
 
local function Spell_OnEnter(self)
self.parent.selectedButton = nil
self:LockHighlight()
 
GameTooltip:SetOwner(self, "ANCHOR_BOTTOMRIGHT")
GameTooltip:SetHyperlink("spell:" .. self.spellID)
end
 
local function Spell_OnLeave(self)
self:UnlockHighlight()
GameTooltip:Hide()
end
 
local predicterBackdrop = {
bgFile = "Interface\\ChatFrame\\ChatFrameBackground",
edgeFile = "Interface\\DialogFrame\\UI-DialogBox-Border",
edgeSize = 26,
insets = {left = 9, right = 9, top = 9, bottom = 9},
}
 
local function Constructor()
local num = AceGUI:GetNextWidgetNum(Type)
local frame = CreateFrame("Frame", nil, UIParent)
local editbox = CreateFrame("EditBox", "AceGUI30SpellEditBox" .. num, frame, "InputBoxTemplate")
 
-- Don't feel like looking up the specific callbacks for when a widget resizes, so going to be creative with SetPoint instead!
local predictFrame = CreateFrame("Frame", "AceGUI30SpellEditBox" .. num .. "Predicter", UIParent)
predictFrame:SetBackdrop(predicterBackdrop)
predictFrame:SetBackdropColor(0, 0, 0, 0.85)
predictFrame:SetWidth(1)
predictFrame:SetHeight(150)
predictFrame:SetPoint("TOPLEFT", editbox, "BOTTOMLEFT", -6, 0)
predictFrame:SetPoint("TOPRIGHT", editbox, "BOTTOMRIGHT", 0, 0)
predictFrame:SetFrameStrata("TOOLTIP")
predictFrame:Hide()
 
predictFrame.buttons = {}
 
for i=1, PREDICTION_ROWS do
local button = CreateFrame("Button", nil, predictFrame)
button:SetHeight(17)
button:SetWidth(1)
button:SetPushedTextOffset(-2, 0)
button:SetScript("OnClick", Spell_OnClick)
button:SetScript("OnEnter", Spell_OnEnter)
button:SetScript("OnLeave", Spell_OnLeave)
button.parent = predictFrame
button.editbox = editbox
button:Hide()
 
if( i > 1 ) then
button:SetPoint("TOPLEFT", predictFrame.buttons[i - 1], "BOTTOMLEFT", 0, 0)
button:SetPoint("TOPRIGHT", predictFrame.buttons[i - 1], "BOTTOMRIGHT", 0, 0)
else
button:SetPoint("TOPLEFT", predictFrame, 8, -8)
button:SetPoint("TOPRIGHT", predictFrame, -7, 0)
end
 
-- Create the actual text
local text = button:CreateFontString(nil, "ARTWORK", "GameFontNormal")
text:SetHeight(1)
text:SetWidth(1)
text:SetJustifyH("LEFT")
text:SetAllPoints(button)
button:SetFontString(text)
 
-- Setup the highlighting
local texture = button:CreateTexture(nil, "ARTWORK")
texture:SetTexture("Interface\\QuestFrame\\UI-QuestTitleHighlight")
texture:ClearAllPoints()
texture:SetPoint("TOPLEFT", button, 0, -2)
texture:SetPoint("BOTTOMRIGHT", button, 5, 2)
texture:SetAlpha(0.70)
 
button:SetHighlightTexture(texture)
button:SetHighlightFontObject(GameFontHighlight)
button:SetNormalFontObject(GameFontNormal)
 
table.insert(predictFrame.buttons, button)
end
 
local self = {}
self.type = Type
self.num = num
 
self.OnRelease = OnRelease
self.OnAcquire = OnAcquire
 
self.SetDisabled = SetDisabled
self.SetText = SetText
self.SetLabel = SetLabel
 
frame.obj = self
self.frame = frame
 
editbox.obj = self
editbox.predictFrame = predictFrame
self.editbox = editbox
 
self.predictFrame = predictFrame
predictFrame.editbox = editbox
predictFrame.obj = self
 
self.alignoffset = 30
 
frame:SetHeight(44)
frame:SetWidth(200)
 
-- Despite the fact that wowprogramming/wowwiki say EditBoxes have OnKeyUp/OnKeyDown thats not actually true
-- so doing some trickery with bindings and such to make navigation work
predictFrame:SetScript("OnMouseDown", Predicter_OnMouseDown)
predictFrame:SetScript("OnHide", Predicter_OnHide)
predictFrame:SetScript("OnShow", Predicter_OnShow)
 
editbox:SetScript("OnShow", startLoading)
editbox:SetScript("OnEnter", Control_OnEnter)
editbox:SetScript("OnLeave", Control_OnLeave)
 
editbox:SetAutoFocus(false)
editbox:SetFontObject(ChatFontNormal)
editbox:SetScript("OnEscapePressed", EditBox_OnEscapePressed)
editbox:SetScript("OnEnterPressed", EditBox_OnEnterPressed)
editbox:SetScript("OnTextChanged", EditBox_OnTextChanged)
editbox:SetScript("OnReceiveDrag", EditBox_OnReceiveDrag)
editbox:SetScript("OnMouseDown", EditBox_OnReceiveDrag)
editbox:SetScript("OnEditFocusGained", EditBox_OnEditFocusGained)
editbox:SetScript("OnEditFocusLost", EditBox_OnEditFocusLost)
 
editbox:SetTextInsets(0, 0, 3, 3)
editbox:SetMaxLetters(256)
 
editbox:SetPoint("BOTTOMLEFT", frame, "BOTTOMLEFT", 6, 0)
editbox:SetPoint("BOTTOMRIGHT", frame, "BOTTOMRIGHT", 0, 0)
editbox:SetHeight(19)
 
local label = frame:CreateFontString(nil,"OVERLAY","GameFontNormalSmall")
label:SetPoint("TOPLEFT", frame, "TOPLEFT", 0, -2)
label:SetPoint("TOPRIGHT", frame, "TOPRIGHT", 0, -2)
label:SetJustifyH("LEFT")
label:SetHeight(18)
self.label = label
 
local button = CreateFrame("Button",nil,editbox,"UIPanelButtonTemplate")
button:SetWidth(40)
button:SetHeight(20)
button:SetPoint("RIGHT", editbox, "RIGHT", -2, 0)
button:SetText(OKAY)
button:SetScript("OnClick", Button_OnClick)
button:Hide()
 
self.button = button
button.obj = self
 
AceGUI:RegisterAsWidget(self)
return self
end
 
AceGUI:RegisterWidgetType(Type, Constructor, Version)
end
\ No newline at end of file Property changes : Added: svn:eol-style + native
branches/AceGUI-3.0-Spell-EditBox/EditBox-Core.lua New file
0,0 → 1,468
-- This is basically the main portion of the predictor, the other files handle
local AceGUI = LibStub("AceGUI-3.0")
do
local Type = "Predictor_Base"
local Version = 1
local PREDICTOR_ROWS = 10
local SpellData = LibStub("AceGUI-3.0-SpellLoader")
local tooltip
local alreadyAdded = {}
local predictorBackdrop = {
bgFile = "Interface\\ChatFrame\\ChatFrameBackground",
edgeFile = "Interface\\DialogFrame\\UI-DialogBox-Border",
edgeSize = 26,
insets = {left = 9, right = 9, top = 9, bottom = 9},
}
 
local function OnAcquire(self)
self:SetHeight(26)
self:SetWidth(200)
self:SetDisabled(false)
self:SetLabel()
self.showButton = true
 
SpellData:RegisterPredictor(self.predictFrame)
SpellData:StartLoading()
end
 
local function OnRelease(self)
self.frame:ClearAllPoints()
self.frame:Hide()
self.predictFrame:Hide()
self.spellFilter = nil
 
self:SetDisabled(false)
 
SpellData:UnregisterPredictor(self.predictFrame)
end
 
local function Control_OnEnter(this)
this.obj:Fire("OnEnter")
end
 
local function Control_OnLeave(this)
this.obj:Fire("OnLeave")
end
 
local function Predictor_Query(self)
for _, button in pairs(self.buttons) do button:Hide() end
for k in pairs(alreadyAdded) do alreadyAdded[k] = nil end
 
local query = "^" .. string.lower(self.obj.editBox:GetText())
 
local activeButtons = 0
for spellID, name in pairs(SpellData.spellList) do
if( not alreadyAdded[name] and string.match(name, query) and ( not self.obj.spellFilter or self.obj.spellFilter(self.obj, spellID) ) ) then
activeButtons = activeButtons + 1
 
local button = self.buttons[activeButtons]
local spellName, spellRank, spellIcon = GetSpellInfo(spellID)
if( self.obj.useRanks and spellRank and spellRank ~= "" ) then
button:SetFormattedText("|T%s:20:20:2:11|t %s (%s)", spellIcon, spellName, spellRank)
else
button:SetFormattedText("|T%s:20:20:2:11|t %s", spellIcon, spellName)
end
 
if( not self.obj.useRanks ) then
alreadyAdded[name] = true
end
 
button.spellID = spellID
button:Show()
 
-- Highlight if needed
if( activeButtons ~= self.selectedButton ) then
button:UnlockHighlight()
 
if( GameTooltip:IsOwned(button) ) then
GameTooltip:Hide()
end
end
 
-- Ran out of text to suggest :<
if( activeButtons >= PREDICTOR_ROWS ) then break end
end
end
 
if( activeButtons > 0 ) then
self:SetHeight(15 + activeButtons * 17)
self:Show()
else
self:Hide()
end
 
self.activeButtons = activeButtons
end
 
local function ShowButton(self)
if( self.lastText ~= "" ) then
self.predictFrame.selectedButton = nil
Predictor_Query(self.predictFrame)
else
self.predictFrame:Hide()
end
 
if( self.showButton ) then
self.button:Show()
self.editBox:SetTextInsets(0, 20, 3, 3)
end
end
 
local function HideButton(self)
self.button:Hide()
self.editBox:SetTextInsets(0, 0, 3, 3)
 
self.predictFrame.selectedButton = nil
self.predictFrame:Hide()
end
 
local function Predictor_OnHide(self)
-- Allow users to use arrows to go back and forth again without the fix
self.obj.editBox:SetAltArrowKeyMode(false)
 
-- Make sure the tooltip isn't kept open if one of the buttons was using it
for _, button in pairs(self.buttons) do
if( GameTooltip:IsOwned(button) ) then
GameTooltip:Hide()
end
end
 
-- Reset all bindings set on this predictor
ClearOverrideBindings(self)
end
 
local function Predictor_OnShow(self)
-- If the user is using an edit box in a configuration, they will live without arrow keys while the predictor
-- is opened, this also is the only way of getting up/down arrow for browsing the predictor to work.
self.obj.editBox:SetAltArrowKeyMode(true)
 
local name = self:GetName()
SetOverrideBindingClick(self, true, "DOWN", name, 1)
SetOverrideBindingClick(self, true, "UP", name, -1)
SetOverrideBindingClick(self, true, "LEFT", name, "LEFT")
SetOverrideBindingClick(self, true, "RIGHT", name, "RIGHT")
end
 
local function EditBox_OnEnterPressed(this)
local self = this.obj
 
-- Something is selected in the predictor, use that value instead of whatever is in the input box
if( self.predictFrame.selectedButton ) then
self.predictFrame.buttons[self.predictFrame.selectedButton]:Click()
return
end
 
local cancel = self:Fire("OnEnterPressed", this:GetText())
if( not cancel ) then
HideButton(self)
end
 
-- Reactive the cursor, odds are if someone is adding spells they are adding more than one
-- and if they aren't, it can't hurt anyway.
self.editBox:SetFocus()
end
 
local function EditBox_OnEscapePressed(this)
this:ClearFocus()
end
 
-- When using SetAltArrowKeyMode the ability to move the cursor with left and right arrows is disabled
-- this reenables that so the user doesn't notice anything wrong
local function EditBox_FixCursorPosition(self, direction)
self:SetCursorPosition(self:GetCursorPosition() + (direction == "RIGHT" and 1 or -1))
end
 
local function EditBox_OnReceiveDrag(this)
local self = this.obj
local type, id, info = GetCursorInfo()
if( type == "spell" ) then
local name, rank = GetSpellName(id, info)
if( self.useRanks and rank and rank ~= "" ) then
name = string.format("%s (%s)", name, rank)
end
 
self:SetText(name)
self:Fire("OnEnterPressed", name)
ClearCursor()
end
 
HideButton(self)
AceGUI:ClearFocus()
end
 
local function EditBox_OnTextChanged(this)
local self = this.obj
local value = this:GetText()
if( value ~= self.lastText ) then
self:Fire("OnTextChanged", value)
self.lastText = value
 
ShowButton(self)
end
end
 
local function EditBox_OnEditFocusLost(self)
Predictor_OnHide(self.obj.predictFrame)
end
 
local function EditBox_OnEditFocusGained(self)
if( self.obj.predictFrame:IsVisible() ) then
Predictor_OnShow(self.obj.predictFrame)
end
end
 
local function Button_OnClick(this)
EditBox_OnEnterPressed(this.obj.editBox)
end
 
-- API calls
local function SetUseRanks(self, enabled)
self.useRanks = enabled
end
 
local function SetDisabled(self, disabled)
self.disabled = disabled
if( disabled ) then
self.editBox:EnableMouse(false)
self.editBox:ClearFocus()
self.editBox:SetTextColor(0.5, 0.5, 0.5)
self.label:SetTextColor(0.5, 0.5, 0.5)
else
self.editBox:EnableMouse(true)
self.editBox:SetTextColor(1, 1, 1)
self.label:SetTextColor(1, 0.82, 0)
end
end
 
local function SetText(self, text, cursor)
self.lastText = text or ""
self.editBox:SetText(self.lastText)
self.editBox:SetCursorPosition(cursor or 0)
 
HideButton(self)
end
 
local function SetLabel(self, text)
if( text and text ~= "" ) then
self.label:SetText(text)
self.label:Show()
self.editBox:SetPoint("TOPLEFT", self.frame, "TOPLEFT", 7, -18)
self:SetHeight(44)
self.alignoffset = 30
else
self.label:SetText("")
self.label:Hide()
self.editBox:SetPoint("TOPLEFT", self.frame, "TOPLEFT", 7, 0)
self:SetHeight(26)
self.alignoffset = 12
end
end
 
local function Predictor_OnMouseDown(self, direction)
-- Fix the cursor positioning if left or right arrow key was used
if( direction == "LEFT" or direction == "RIGHT" ) then
EditBox_FixCursorPosition(self.editBox, direction)
return
end
 
self.selectedButton = (self.selectedButton or 0) + direction
if( self.selectedButton > self.activeButtons ) then
self.selectedButton = 1
elseif( self.selectedButton <= 0 ) then
self.selectedButton = self.activeButtons
end
 
-- Figure out what to highlight and show the spell tooltip while we're at it
for i=1, self.activeButtons do
local button = self.buttons[i]
if( i == self.selectedButton ) then
button:LockHighlight()
 
GameTooltip:SetOwner(button, "ANCHOR_BOTTOMRIGHT", 3)
GameTooltip:SetHyperlink("spell:" .. button.spellID)
else
button:UnlockHighlight()
 
if( GameTooltip:IsOwned(button) ) then
GameTooltip:Hide()
end
end
end
end
 
local function Spell_OnClick(self)
local name, rank = GetSpellInfo(self.spellID)
if( self.useRanks and rank and rank ~= "" ) then
name = string.format("%s (%s)", name, rank)
end
 
SetText(self.parent.obj, name, string.len(name))
 
self.parent.selectedButton = nil
self.parent.obj:Fire("OnEnterPressed", name)
end
 
local function Spell_OnEnter(self)
self.parent.selectedButton = nil
self:LockHighlight()
 
GameTooltip:SetOwner(self, "ANCHOR_BOTTOMRIGHT", 3)
GameTooltip:SetHyperlink("spell:" .. self.spellID)
end
 
local function Spell_OnLeave(self)
self:UnlockHighlight()
GameTooltip:Hide()
end
 
local function Constructor()
local num = AceGUI:GetNextWidgetNum(Type)
local frame = CreateFrame("Frame", nil, UIParent)
local editBox = CreateFrame("EditBox", "AceGUI30SpellEditBox" .. num, frame, "InputBoxTemplate")
 
-- Don't feel like looking up the specific callbacks for when a widget resizes, so going to be creative with SetPoint instead!
local predictFrame = CreateFrame("Frame", "AceGUI30SpellEditBox" .. num .. "Predictor", UIParent)
predictFrame:SetBackdrop(predictorBackdrop)
predictFrame:SetBackdropColor(0, 0, 0, 0.85)
predictFrame:SetWidth(1)
predictFrame:SetHeight(150)
predictFrame:SetPoint("TOPLEFT", editBox, "BOTTOMLEFT", -6, 0)
predictFrame:SetPoint("TOPRIGHT", editBox, "BOTTOMRIGHT", 0, 0)
predictFrame:SetFrameStrata("TOOLTIP")
predictFrame.buttons = {}
predictFrame.Query = Predictor_Query
predictFrame:Hide()
 
-- Create the mass of predictor rows
for i=1, PREDICTOR_ROWS do
local button = CreateFrame("Button", nil, predictFrame)
button:SetHeight(17)
button:SetWidth(1)
button:SetPushedTextOffset(-2, 0)
button:SetScript("OnClick", Spell_OnClick)
button:SetScript("OnEnter", Spell_OnEnter)
button:SetScript("OnLeave", Spell_OnLeave)
button.parent = predictFrame
button.editBox = editBox
button:Hide()
 
if( i > 1 ) then
button:SetPoint("TOPLEFT", predictFrame.buttons[i - 1], "BOTTOMLEFT", 0, 0)
button:SetPoint("TOPRIGHT", predictFrame.buttons[i - 1], "BOTTOMRIGHT", 0, 0)
else
button:SetPoint("TOPLEFT", predictFrame, 8, -8)
button:SetPoint("TOPRIGHT", predictFrame, -7, 0)
end
 
-- Create the actual text
local text = button:CreateFontString(nil, "ARTWORK", "GameFontNormal")
text:SetHeight(1)
text:SetWidth(1)
text:SetJustifyH("LEFT")
text:SetAllPoints(button)
button:SetFontString(text)
 
-- Setup the highlighting
local texture = button:CreateTexture(nil, "ARTWORK")
texture:SetTexture("Interface\\QuestFrame\\UI-QuestTitleHighlight")
texture:ClearAllPoints()
texture:SetPoint("TOPLEFT", button, 0, -2)
texture:SetPoint("BOTTOMRIGHT", button, 5, 2)
texture:SetAlpha(0.70)
 
button:SetHighlightTexture(texture)
button:SetHighlightFontObject(GameFontHighlight)
button:SetNormalFontObject(GameFontNormal)
 
table.insert(predictFrame.buttons, button)
end
 
-- Set the main info things for this thingy
local self = {}
self.type = Type
self.num = num
 
self.OnRelease = OnRelease
self.OnAcquire = OnAcquire
 
self.SetDisabled = SetDisabled
self.SetText = SetText
self.SetLabel = SetLabel
 
self.frame = frame
self.predictFrame = predictFrame
self.editBox = editBox
 
self.alignoffset = 30
 
frame:SetHeight(44)
frame:SetWidth(200)
 
frame.obj = self
editBox.obj = self
predictFrame.obj = self
 
-- Purely meant for a single tooltip for doing scanning
if( not tooltip ) then
tooltip = CreateFrame("GameTooltip")
tooltip:SetOwner(UIParent, "ANCHOR_NONE")
for i=1, 6 do
tooltip["TextLeft" .. i] = tooltip:CreateFontString()
tooltip["TextRight" .. i] = tooltip:CreateFontString()
tooltip:AddFontStrings(tooltip["TextLeft" .. i], tooltip["TextRight" .. i])
end
end
 
self.tooltip = tooltip
 
-- EditBoxes override the OnKeyUp/OnKeyDown events so that they can function, meaning in order to make up and down
-- arrow navigation of the menu work, I have to do some trickery with temporary bindings.
predictFrame:SetScript("OnMouseDown", Predictor_OnMouseDown)
predictFrame:SetScript("OnHide", Predictor_OnHide)
predictFrame:SetScript("OnShow", Predictor_OnShow)
 
editBox:SetScript("OnEnter", Control_OnEnter)
editBox:SetScript("OnLeave", Control_OnLeave)
 
editBox:SetAutoFocus(false)
editBox:SetFontObject(ChatFontNormal)
editBox:SetScript("OnEscapePressed", EditBox_OnEscapePressed)
editBox:SetScript("OnEnterPressed", EditBox_OnEnterPressed)
editBox:SetScript("OnTextChanged", EditBox_OnTextChanged)
editBox:SetScript("OnReceiveDrag", EditBox_OnReceiveDrag)
editBox:SetScript("OnMouseDown", EditBox_OnReceiveDrag)
editBox:SetScript("OnEditFocusGained", EditBox_OnEditFocusGained)
editBox:SetScript("OnEditFocusLost", EditBox_OnEditFocusLost)
 
editBox:SetTextInsets(0, 0, 3, 3)
editBox:SetMaxLetters(256)
 
editBox:SetPoint("BOTTOMLEFT", frame, "BOTTOMLEFT", 6, 0)
editBox:SetPoint("BOTTOMRIGHT", frame, "BOTTOMRIGHT", 0, 0)
editBox:SetHeight(19)
 
local label = frame:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
label:SetPoint("TOPLEFT", frame, "TOPLEFT", 0, -2)
label:SetPoint("TOPRIGHT", frame, "TOPRIGHT", 0, -2)
label:SetJustifyH("LEFT")
label:SetHeight(18)
 
self.label = label
 
local button = CreateFrame("Button", nil, editBox, "UIPanelButtonTemplate")
button:SetPoint("RIGHT", editBox, "RIGHT", -2, 0)
button:SetScript("OnClick", Button_OnClick)
button:SetWidth(40)
button:SetHeight(20)
button:SetText(OKAY)
button:Hide()
 
self.button = button
button.obj = self
 
AceGUI:RegisterAsWidget(self)
return self
end
 
AceGUI:RegisterWidgetType(Type, Constructor, Version)
end
Property changes : Added: svn:eol-style + native
branches/AceGUI-3.0-Spell-EditBox/SpellLoader.lua New file
0,0 → 1,86
local major = "AceGUI-3.0-SpellLoader"
local minor = 1
 
local SpellLoader = LibStub:NewLibrary(major, minor)
if( not SpellLoader ) then return end
 
SpellLoader.predictors = SpellLoader.predictors or {}
SpellLoader.spellList = SpellLoader.spellList or {}
SpellLoader.spellsLoaded = SpellLoader.spellsLoaded or 0
 
local SPELLS_PER_RUN = 500
local TIMER_THROTTLE = 0.10
local spells, predictors = SpellLoader.spellList, SpellLoader.predictors
 
function SpellLoader:RegisterPredictor(frame)
self.predictors[frame] = true
end
 
function SpellLoader:UnregisterPredictor(frame)
self.predictors[frame] = nil
end
 
function SpellLoader:StartLoading()
if( self.loader ) then return end
 
local blacklist = {
["Interface\\Icons\\Trade_Alchemy"] = true,
["Interface\\Icons\\Trade_BlackSmithing"] = true,
["Interface\\Icons\\Trade_BrewPoison"] = true,
["Interface\\Icons\\Trade_Engineering"] = true,
["Interface\\Icons\\Trade_Engraving"] = true,
["Interface\\Icons\\Trade_Fishing"] = true,
["Interface\\Icons\\Trade_Herbalism"] = true,
["Interface\\Icons\\Trade_LeatherWorking"] = true,
["Interface\\Icons\\Trade_Mining"] = true,
["Interface\\Icons\\Trade_Tailoring"] = true,
["Interface\\Icons\\Temp"] = true,
}
 
local timeElapsed, totalInvalid, currentIndex = 0, 0, 0
self.loader = CreateFrame("Frame")
self.loader:SetScript("OnUpdate", function(self, elapsed)
timeElapsed = timeElapsed + elapsed
if( timeElapsed < TIMER_THROTTLE ) then return end
timeElapsed = timeElapsed - TIMER_THROTTLE
 
-- 5,000 invalid spells in a row means it's a safe assumption that there are no more spells to query
if( totalInvalid >= 5000 ) then
self:Hide()
return
end
 
-- Load as many spells in
for spellID=currentIndex + 1, currentIndex + SPELLS_PER_RUN do
local name, rank, icon = GetSpellInfo(spellID)
 
-- Pretty much every profession spell uses Trade_* and 99% of the random spells use the Trade_Engineering icon
-- we can safely blacklist any of these spells as they are not needed. Can get away with this because things like
-- Alchemy use two icons, the Trade_* for the actual crafted spell and a different icon for the actual buff
-- Passive spells have no use as well, since they are well passive and can't actually be used
if( name and not blacklist[icon] and rank ~= SPELL_PASSIVE ) then
name = string.lower(name)
 
SpellLoader.spellsLoaded = SpellLoader.spellsLoaded + 1
spells[spellID] = string.lower(name)
 
totalInvalid = 0
else
totalInvalid = totalInvalid + 1
end
end
 
-- Every ~1 second it will update any visible predictors to make up for the fact that the data is delay loaded
if( currentIndex % 5000 == 0 ) then
for predictor in pairs(predictors) do
if( predictor:IsVisible() ) then
predictor:Query()
end
end
end
 
 
-- Increment and do it all over!
currentIndex = currentIndex + SPELLS_PER_RUN
end)
end
\ No newline at end of file Property changes : Added: svn:eol-style + native
branches/AceGUI-3.0-Spell-EditBox/Auras-EditBox.lua New file
0,0 → 1,53
local AceGUI = LibStub("AceGUI-3.0")
do
local Type = "Aura_EditBox"
local Version = 1
local filterCache = {}
 
-- Tooltip scanning every single spell while it's loading would be not be that fun. Scanning spells as we search them (and get valid results)
-- is a better solution since the results can be cached too.
local function spellFilter(self, spellID)
if( filterCache[spellID] ~= nil ) then return filterCache[spellID] end
 
-- Very very few auras are over 100 yard range, and those are generally boss spells should be able to get away with this
if( select(9, GetSpellInfo(spellID)) > 100 ) then
filterCache[spellID] = true
return false
end
 
-- We look for a description tag, 99% of auras have a description tag indicating what they are
-- so we don't find one, then it's likely a safe assumption that it is not an aura
self.tooltip:SetHyperlink("spell:" .. spellID)
for i=1, self.tooltip:NumLines() do
local text = self.tooltip["TextLeft" .. i]
if( text ) then
local r, g, b = text:GetTextColor()
r = math.floor(r + 0.10)
g = math.floor(g + 0.10)
b = math.floor(b + 0.10)
 
-- Gold first text, it's a profession link
if( i == 1 and ( r ~= 1 or g ~= 1 or g ~= 1 ) ) then
filterCache[spellID] = false
return false
-- Gold for anything else and it should be a valid aura
elseif( r ~= 1 or g ~= 1 or b ~= 1 ) then
filterCache[spellID] = true
return true
end
end
end
 
filterCache[spellID] = false
return false
end
 
-- I know theres a better way of doing this than this, but not sure for the time being, works fine though!
local function Constructor()
local self = AceGUI:Create("Predictor_Base")
self.spellFilter = spellFilter
return self
end
 
AceGUI:RegisterWidgetType(Type, Constructor, Version)
end
Property changes : Added: svn:eol-style + native
branches/AceGUI-3.0-Spell-EditBox/AceGUI-3.0-Spell-EditBox.toc New file
0,0 → 1,13
## Interface: 30200
## Title: Lib: AceGUI-3.0-Spell-EditBox
## Notes: Adds predicted lists for spells while the user types
## Author: Shadowed
## X-Category: Library
## OptionalDeps: Ace3
## X-Curse-Packaged-Version: r20090807071246
## X-Curse-Project-Name: AceGUI-3.0-Spell-EditBox
## X-Curse-Project-ID: ace-gui-3-0-spell-editbox
## X-Curse-Repository-ID: wow/ace-gui-3-0-spell-editbox/mainline
 
AceGUI-3.0-Spell-EditBox.xml
## test.lua
\ No newline at end of file Property changes : Added: svn:eol-style + native
branches/AceGUI-3.0-Spell-EditBox/Player-EditBox.lua New file
0,0 → 1,46
local AceGUI = LibStub("AceGUI-3.0")
do
local Type = "Player_EditBox"
local Version = 1
local playerSpells = {}
local frame
 
local function spellFilter(self, spellID)
return playerSpells[spellID]
end
 
local function loadPlayerSpells(self)
table.wipe(playerSpells)
 
for tab=2, GetNumSpellTabs() do
local offset = select(3, GetSpellTabInfo(tab))
for i=1, offset do
self.tooltip:SetSpell(i + offset, tab)
 
local spellName, _, spellID = self.tooltip:GetSpell()
if( spellName ) then
playerSpells[spellID] = true
end
end
end
end
 
-- I know theres a better way of doing this than this, but not sure for the time being, works fine though!
local function Constructor()
local self = AceGUI:Create("Predictor_Base")
self.spellFilter = spellFilter
 
if( not frame ) then
frame = CreateFrame("Frame")
frame:RegisterEvent("SPELLS_CHANGED")
frame:SetScript("OnEvent", loadPlayerSpells)
frame.tooltip = self.tooltip
 
loadPlayerSpells(frame)
end
 
return self
end
 
AceGUI:RegisterWidgetType(Type, Constructor, Version)
end
Property changes : Added: svn:eol-style + native
branches/AceGUI-3.0-Spell-EditBox/Casts-EditBox.lua New file
0,0 → 1,35
local AceGUI = LibStub("AceGUI-3.0")
do
local Type = "Cast_EditBox"
local Version = 1
local filterCache = {}
 
local function spellFilter(self, spellID)
if( filterCache[spellID] ~= nil ) then return filterCache[spellID] end
 
-- Scan first line to figure out if it's a profession
self.tooltip:SetHyperlink("spell:" .. spellID)
local r, g, b = self.tooltip.TextLeft1:GetTextColor()
r = math.floor(r + 0.10)
g = math.floor(g + 0.10)
b = math.floor(b + 0.10)
 
if( r ~= 1 or g ~= 1 or b ~= 1 ) then
filterCache[spellID] = false
return false
end
 
-- Hide all spells without a cast time
filterCache[spellID] = select(7, GetSpellInfo(spellID)) > 0
return filterCache[spellID]
end
 
-- I know theres a better way of doing this than this, but not sure for the time being, works fine though!
local function Constructor()
local self = AceGUI:Create("Predictor_Base")
self.spellFilter = spellFilter
return self
end
 
AceGUI:RegisterWidgetType(Type, Constructor, Version)
end
Property changes : Added: svn:eol-style + native
branches/LibStub/LibStub.lua New file
0,0 → 1,30
-- LibStub is a simple versioning stub meant for use in Libraries. http://www.wowace.com/wiki/LibStub for more info
-- LibStub is hereby placed in the Public Domain Credits: Kaelten, Cladhaire, ckknight, Mikk, Ammo, Nevcairiel, joshborke
local LIBSTUB_MAJOR, LIBSTUB_MINOR = "LibStub", 2 -- NEVER MAKE THIS AN SVN REVISION! IT NEEDS TO BE USABLE IN ALL REPOS!
local LibStub = _G[LIBSTUB_MAJOR]
 
if not LibStub or LibStub.minor < LIBSTUB_MINOR then
LibStub = LibStub or {libs = {}, minors = {} }
_G[LIBSTUB_MAJOR] = LibStub
LibStub.minor = LIBSTUB_MINOR
 
function LibStub:NewLibrary(major, minor)
assert(type(major) == "string", "Bad argument #2 to `NewLibrary' (string expected)")
minor = assert(tonumber(strmatch(minor, "%d+")), "Minor version must either be a number or contain a number.")
 
local oldminor = self.minors[major]
if oldminor and oldminor >= minor then return nil end
self.minors[major], self.libs[major] = minor, self.libs[major] or {}
return self.libs[major], oldminor
end
 
function LibStub:GetLibrary(major, silent)
if not self.libs[major] and not silent then
error(("Cannot find a library instance of %q."):format(tostring(major)), 2)
end
return self.libs[major], self.minors[major]
end
 
function LibStub:IterateLibraries() return pairs(self.libs) end
setmetatable(LibStub, { __call = LibStub.GetLibrary })
end
Property changes : Added: svn:eol-style + native
branches/ChatThrottleLib/ChatThrottleLib.lua New file
0,0 → 1,503
--
-- ChatThrottleLib by Mikk
--
-- Manages AddOn chat output to keep player from getting kicked off.
--
-- ChatThrottleLib:SendChatMessage/:SendAddonMessage functions that accept
-- a Priority ("BULK", "NORMAL", "ALERT") as well as prefix for SendChatMessage.
--
-- Priorities get an equal share of available bandwidth when fully loaded.
-- Communication channels are separated on extension+chattype+destination and
-- get round-robinned. (Destination only matters for whispers and channels,
-- obviously)
--
-- Will install hooks for SendChatMessage and SendAddonMessage to measure
-- bandwidth bypassing the library and use less bandwidth itself.
--
--
-- Fully embeddable library. Just copy this file into your addon directory,
-- add it to the .toc, and it's done.
--
-- Can run as a standalone addon also, but, really, just embed it! :-)
--
 
local CTL_VERSION = 21
 
local _G = _G
 
if _G.ChatThrottleLib then
if _G.ChatThrottleLib.version >= CTL_VERSION then
-- There's already a newer (or same) version loaded. Buh-bye.
return
elseif not _G.ChatThrottleLib.securelyHooked then
print("ChatThrottleLib: Warning: There's an ANCIENT ChatThrottleLib.lua (pre-wow 2.0, <v16) in an addon somewhere. Get the addon updated or copy in a newer ChatThrottleLib.lua (>=v16) in it!")
-- ATTEMPT to unhook; this'll behave badly if someone else has hooked...
-- ... and if someone has securehooked, they can kiss that goodbye too... >.<
_G.SendChatMessage = _G.ChatThrottleLib.ORIG_SendChatMessage
if _G.ChatThrottleLib.ORIG_SendAddonMessage then
_G.SendAddonMessage = _G.ChatThrottleLib.ORIG_SendAddonMessage
end
end
_G.ChatThrottleLib.ORIG_SendChatMessage = nil
_G.ChatThrottleLib.ORIG_SendAddonMessage = nil
end
 
if not _G.ChatThrottleLib then
_G.ChatThrottleLib = {}
end
 
ChatThrottleLib = _G.ChatThrottleLib -- in case some addon does "local ChatThrottleLib" above us and we're copypasted (AceComm-2, sigh)
local ChatThrottleLib = _G.ChatThrottleLib
 
ChatThrottleLib.version = CTL_VERSION
 
 
 
------------------ TWEAKABLES -----------------
 
ChatThrottleLib.MAX_CPS = 800 -- 2000 seems to be safe if NOTHING ELSE is happening. let's call it 800.
ChatThrottleLib.MSG_OVERHEAD = 40 -- Guesstimate overhead for sending a message; source+dest+chattype+protocolstuff
 
ChatThrottleLib.BURST = 4000 -- WoW's server buffer seems to be about 32KB. 8KB should be safe, but seen disconnects on _some_ servers. Using 4KB now.
 
ChatThrottleLib.MIN_FPS = 20 -- Reduce output CPS to half (and don't burst) if FPS drops below this value
 
 
local setmetatable = setmetatable
local table_remove = table.remove
local tostring = tostring
local GetTime = GetTime
local math_min = math.min
local math_max = math.max
local next = next
local strlen = string.len
local GetFrameRate = GetFrameRate
 
 
 
-----------------------------------------------------------------------
-- Double-linked ring implementation
 
local Ring = {}
local RingMeta = { __index = Ring }
 
function Ring:New()
local ret = {}
setmetatable(ret, RingMeta)
return ret
end
 
function Ring:Add(obj) -- Append at the "far end" of the ring (aka just before the current position)
if self.pos then
obj.prev = self.pos.prev
obj.prev.next = obj
obj.next = self.pos
obj.next.prev = obj
else
obj.next = obj
obj.prev = obj
self.pos = obj
end
end
 
function Ring:Remove(obj)
obj.next.prev = obj.prev
obj.prev.next = obj.next
if self.pos == obj then
self.pos = obj.next
if self.pos == obj then
self.pos = nil
end
end
end
 
 
 
-----------------------------------------------------------------------
-- Recycling bin for pipes
-- A pipe is a plain integer-indexed queue, which also happens to be a ring member
 
ChatThrottleLib.PipeBin = nil -- pre-v19, drastically different
local PipeBin = setmetatable({}, {__mode="k"})
 
local function DelPipe(pipe)
for i = #pipe, 1, -1 do
pipe[i] = nil
end
pipe.prev = nil
pipe.next = nil
 
PipeBin[pipe] = true
end
 
local function NewPipe()
local pipe = next(PipeBin)
if pipe then
PipeBin[pipe] = nil
return pipe
end
return {}
end
 
 
 
 
-----------------------------------------------------------------------
-- Recycling bin for messages
 
ChatThrottleLib.MsgBin = nil -- pre-v19, drastically different
local MsgBin = setmetatable({}, {__mode="k"})
 
local function DelMsg(msg)
msg[1] = nil
-- there's more parameters, but they're very repetetive so the string pool doesn't suffer really, and it's faster to just not delete them.
MsgBin[msg] = true
end
 
local function NewMsg()
local msg = next(MsgBin)
if msg then
MsgBin[msg] = nil
return msg
end
return {}
end
 
 
-----------------------------------------------------------------------
-- ChatThrottleLib:Init
-- Initialize queues, set up frame for OnUpdate, etc
 
 
function ChatThrottleLib:Init()
 
-- Set up queues
if not self.Prio then
self.Prio = {}
self.Prio["ALERT"] = { ByName = {}, Ring = Ring:New(), avail = 0 }
self.Prio["NORMAL"] = { ByName = {}, Ring = Ring:New(), avail = 0 }
self.Prio["BULK"] = { ByName = {}, Ring = Ring:New(), avail = 0 }
end
 
-- v4: total send counters per priority
for _, Prio in pairs(self.Prio) do
Prio.nTotalSent = Prio.nTotalSent or 0
end
 
if not self.avail then
self.avail = 0 -- v5
end
if not self.nTotalSent then
self.nTotalSent = 0 -- v5
end
 
 
-- Set up a frame to get OnUpdate events
if not self.Frame then
self.Frame = CreateFrame("Frame")
self.Frame:Hide()
end
self.Frame:SetScript("OnUpdate", self.OnUpdate)
self.Frame:SetScript("OnEvent", self.OnEvent) -- v11: Monitor P_E_W so we can throttle hard for a few seconds
self.Frame:RegisterEvent("PLAYER_ENTERING_WORLD")
self.OnUpdateDelay = 0
self.LastAvailUpdate = GetTime()
self.HardThrottlingBeginTime = GetTime() -- v11: Throttle hard for a few seconds after startup
 
-- Hook SendChatMessage and SendAddonMessage so we can measure unpiped traffic and avoid overloads (v7)
if not self.securelyHooked then
-- Use secure hooks as of v16. Old regular hook support yanked out in v21.
self.securelyHooked = true
--SendChatMessage
hooksecurefunc("SendChatMessage", function(...)
return ChatThrottleLib.Hook_SendChatMessage(...)
end)
--SendAddonMessage
hooksecurefunc("SendAddonMessage", function(...)
return ChatThrottleLib.Hook_SendAddonMessage(...)
end)
end
self.nBypass = 0
end
 
 
-----------------------------------------------------------------------
-- ChatThrottleLib.Hook_SendChatMessage / .Hook_SendAddonMessage
 
local bMyTraffic = false
 
function ChatThrottleLib.Hook_SendChatMessage(text, chattype, language, destination, ...)
if bMyTraffic then
return
end
local self = ChatThrottleLib
local size = strlen(tostring(text or "")) + strlen(tostring(destination or "")) + self.MSG_OVERHEAD
self.avail = self.avail - size
self.nBypass = self.nBypass + size -- just a statistic
end
function ChatThrottleLib.Hook_SendAddonMessage(prefix, text, chattype, destination, ...)
if bMyTraffic then
return
end
local self = ChatThrottleLib
local size = tostring(text or ""):len() + tostring(prefix or ""):len();
size = size + tostring(destination or ""):len() + self.MSG_OVERHEAD
self.avail = self.avail - size
self.nBypass = self.nBypass + size -- just a statistic
end
 
 
 
-----------------------------------------------------------------------
-- ChatThrottleLib:UpdateAvail
-- Update self.avail with how much bandwidth is currently available
 
function ChatThrottleLib:UpdateAvail()
local now = GetTime()
local MAX_CPS = self.MAX_CPS;
local newavail = MAX_CPS * (now - self.LastAvailUpdate)
local avail = self.avail
 
if now - self.HardThrottlingBeginTime < 5 then
-- First 5 seconds after startup/zoning: VERY hard clamping to avoid irritating the server rate limiter, it seems very cranky then
avail = math_min(avail + (newavail*0.1), MAX_CPS*0.5)
self.bChoking = true
elseif GetFramerate() < self.MIN_FPS then -- GetFrameRate call takes ~0.002 secs
avail = math_min(MAX_CPS, avail + newavail*0.5)
self.bChoking = true -- just a statistic
else
avail = math_min(self.BURST, avail + newavail)
self.bChoking = false
end
 
avail = math_max(avail, 0-(MAX_CPS*2)) -- Can go negative when someone is eating bandwidth past the lib. but we refuse to stay silent for more than 2 seconds; if they can do it, we can.
 
self.avail = avail
self.LastAvailUpdate = now
 
return avail
end
 
 
-----------------------------------------------------------------------
-- Despooling logic
 
function ChatThrottleLib:Despool(Prio)
local ring = Prio.Ring
while ring.pos and Prio.avail > ring.pos[1].nSize do
local msg = table_remove(Prio.Ring.pos, 1)
if not Prio.Ring.pos[1] then
local pipe = Prio.Ring.pos
Prio.Ring:Remove(pipe)
Prio.ByName[pipe.name] = nil
DelPipe(pipe)
else
Prio.Ring.pos = Prio.Ring.pos.next
end
Prio.avail = Prio.avail - msg.nSize
bMyTraffic = true
msg.f(unpack(msg, 1, msg.n))
bMyTraffic = false
Prio.nTotalSent = Prio.nTotalSent + msg.nSize
DelMsg(msg)
if msg.callbackFn then
msg.callbackFn (msg.callbackArg)
end
end
end
 
 
function ChatThrottleLib.OnEvent(this,event)
-- v11: We know that the rate limiter is touchy after login. Assume that it's touchy after zoning, too.
local self = ChatThrottleLib
if event == "PLAYER_ENTERING_WORLD" then
self.HardThrottlingBeginTime = GetTime() -- Throttle hard for a few seconds after zoning
self.avail = 0
end
end
 
 
function ChatThrottleLib.OnUpdate(this,delay)
local self = ChatThrottleLib
 
self.OnUpdateDelay = self.OnUpdateDelay + delay
if self.OnUpdateDelay < 0.08 then
return
end
self.OnUpdateDelay = 0
 
self:UpdateAvail()
 
if self.avail < 0 then
return -- argh. some bastard is spewing stuff past the lib. just bail early to save cpu.
end
 
-- See how many of our priorities have queued messages (we only have 3, don't worry about the loop)
local n = 0
for prioname,Prio in pairs(self.Prio) do
if Prio.Ring.pos or Prio.avail < 0 then
n = n + 1
end
end
 
-- Anything queued still?
if n<1 then
-- Nope. Move spillover bandwidth to global availability gauge and clear self.bQueueing
for prioname, Prio in pairs(self.Prio) do
self.avail = self.avail + Prio.avail
Prio.avail = 0
end
self.bQueueing = false
self.Frame:Hide()
return
end
 
-- There's stuff queued. Hand out available bandwidth to priorities as needed and despool their queues
local avail = self.avail/n
self.avail = 0
 
for prioname, Prio in pairs(self.Prio) do
if Prio.Ring.pos or Prio.avail < 0 then
Prio.avail = Prio.avail + avail
if Prio.Ring.pos and Prio.avail > Prio.Ring.pos[1].nSize then
self:Despool(Prio)
-- Note: We might not get here if the user-supplied callback function errors out! Take care!
end
end
end
 
end
 
 
 
 
-----------------------------------------------------------------------
-- Spooling logic
 
 
function ChatThrottleLib:Enqueue(prioname, pipename, msg)
local Prio = self.Prio[prioname]
local pipe = Prio.ByName[pipename]
if not pipe then
self.Frame:Show()
pipe = NewPipe()
pipe.name = pipename
Prio.ByName[pipename] = pipe
Prio.Ring:Add(pipe)
end
 
pipe[#pipe + 1] = msg
 
self.bQueueing = true
end
 
 
 
function ChatThrottleLib:SendChatMessage(prio, prefix, text, chattype, language, destination, queueName, callbackFn, callbackArg)
if not self or not prio or not prefix or not text or not self.Prio[prio] then
error('Usage: ChatThrottleLib:SendChatMessage("{BULK||NORMAL||ALERT}", "prefix", "text"[, "chattype"[, "language"[, "destination"]]]', 2)
end
if callbackFn and type(callbackFn)~="function" then
error('ChatThrottleLib:ChatMessage(): callbackFn: expected function, got '..type(callbackFn), 2)
end
 
local nSize = text:len()
 
if nSize>255 then
error("ChatThrottleLib:SendChatMessage(): message length cannot exceed 255 bytes", 2)
end
 
nSize = nSize + self.MSG_OVERHEAD
 
-- Check if there's room in the global available bandwidth gauge to send directly
if not self.bQueueing and nSize < self:UpdateAvail() then
self.avail = self.avail - nSize
bMyTraffic = true
_G.SendChatMessage(text, chattype, language, destination)
bMyTraffic = false
self.Prio[prio].nTotalSent = self.Prio[prio].nTotalSent + nSize
if callbackFn then
callbackFn (callbackArg)
end
return
end
 
-- Message needs to be queued
local msg = NewMsg()
msg.f = _G.SendChatMessage
msg[1] = text
msg[2] = chattype or "SAY"
msg[3] = language
msg[4] = destination
msg.n = 4
msg.nSize = nSize
msg.callbackFn = callbackFn
msg.callbackArg = callbackArg
 
self:Enqueue(prio, queueName or (prefix..(chattype or "SAY")..(destination or "")), msg)
end
 
 
function ChatThrottleLib:SendAddonMessage(prio, prefix, text, chattype, target, queueName, callbackFn, callbackArg)
if not self or not prio or not prefix or not text or not chattype or not self.Prio[prio] then
error('Usage: ChatThrottleLib:SendAddonMessage("{BULK||NORMAL||ALERT}", "prefix", "text", "chattype"[, "target"])', 2)
end
if callbackFn and type(callbackFn)~="function" then
error('ChatThrottleLib:SendAddonMessage(): callbackFn: expected function, got '..type(callbackFn), 2)
end
 
local nSize = prefix:len() + 1 + text:len();
 
if nSize>255 then
error("ChatThrottleLib:SendAddonMessage(): prefix + message length cannot exceed 254 bytes", 2)
end
 
nSize = nSize + self.MSG_OVERHEAD;
 
-- Check if there's room in the global available bandwidth gauge to send directly
if not self.bQueueing and nSize < self:UpdateAvail() then
self.avail = self.avail - nSize
bMyTraffic = true
_G.SendAddonMessage(prefix, text, chattype, target)
bMyTraffic = false
self.Prio[prio].nTotalSent = self.Prio[prio].nTotalSent + nSize
if callbackFn then
callbackFn (callbackArg)
end
return
end
 
-- Message needs to be queued
local msg = NewMsg()
msg.f = _G.SendAddonMessage
msg[1] = prefix
msg[2] = text
msg[3] = chattype
msg[4] = target
msg.n = (target~=nil) and 4 or 3;
msg.nSize = nSize
msg.callbackFn = callbackFn
msg.callbackArg = callbackArg
 
self:Enqueue(prio, queueName or (prefix..chattype..(target or "")), msg)
end
 
 
 
 
-----------------------------------------------------------------------
-- Get the ball rolling!
 
ChatThrottleLib:Init()
 
--[[ WoWBench debugging snippet
if(WOWB_VER) then
local function SayTimer()
print("SAY: "..GetTime().." "..arg1)
end
ChatThrottleLib.Frame:SetScript("OnEvent", SayTimer)
ChatThrottleLib.Frame:RegisterEvent("CHAT_MSG_SAY")
end
]]
 
 
Property changes : Added: svn:eol-style + native
branches/LibAboutPanel/LibAboutPanel.lua New file
0,0 → 1,218
--[[
 
****************************************************************************************
LibAboutPanel
 
File date: 2010-12-01T19:31:23Z
Project version: v1.51
 
Author: Tekkub, Ackis
 
****************************************************************************************
 
]]--
 
local lib, oldminor = LibStub:NewLibrary("LibAboutPanel", 2)
if not lib then return end
 
function lib.new(parent, addonname)
local frame = CreateFrame("Frame", nil, UIParent)
frame.name, frame.parent, frame.addonname = not parent and gsub(addonname," ","") or "About", parent, gsub(addonname," ","") -- Remove spaces from addonname because GetMetadata doesn't like that
frame:Hide()
frame:SetScript("OnShow", lib.OnShow)
InterfaceOptions_AddCategory(frame)
return frame
end
 
local GAME_LOCALE = GetLocale()
 
local L = {}
 
-- frFR
if GAME_LOCALE == "frFR" then
L["About"] = "à propos de"
L["Click and press Ctrl-C to copy"] = "Click and press Ctrl-C to copy"
-- deDE
elseif GAME_LOCALE == "deDE" then
L["About"] = "Über"
L["Click and press Ctrl-C to copy"] = "Klicken und Strg-C drücken zum kopieren"
-- esES
elseif GAME_LOCALE == "esES" then
L["About"] = "Acerca de"
L["Click and press Ctrl-C to copy"] = "Click and press Ctrl-C to copy"
-- esMX
elseif GAME_LOCALE == "esMX" then
L["About"] = "Sobre"
L["Click and press Ctrl-C to copy"] = "Click and press Ctrl-C to copy"
-- koKR
elseif GAME_LOCALE == "koKR" then
L["About"] = "대하여"
L["Click and press Ctrl-C to copy"] = "클릭 후 Ctrl-C 복사"
-- ruRU
elseif GAME_LOCALE == "ruRU" then
L["About"] = "Об аддоне"
L["Click and press Ctrl-C to copy"] = "Click and press Ctrl-C to copy"
-- zhCN
elseif GAME_LOCALE == "zhCN" then
L["About"] = "关于"
L["Click and press Ctrl-C to copy"] = "点击并 Ctrl-C 复制"
-- zhTW
elseif GAME_LOCALE == "zhTW" then
L["About"] = "關於"
L["Click and press Ctrl-C to copy"] = "左鍵點擊並按下 Ctrl-C 以複製字串"
-- enUS and non-localized
else
L["About"] ="About"
L["Click and press Ctrl-C to copy"] = "Click and press Ctrl-C to copy"
end
 
local editbox = CreateFrame('EditBox', nil, UIParent)
editbox:Hide()
editbox:SetAutoFocus(true)
editbox:SetHeight(32)
editbox:SetFontObject('GameFontHighlightSmall')
lib.editbox = editbox
 
local left = editbox:CreateTexture(nil, "BACKGROUND")
left:SetWidth(8) left:SetHeight(20)
left:SetPoint("LEFT", -5, 0)
left:SetTexture("Interface\\Common\\Common-Input-Border")
left:SetTexCoord(0, 0.0625, 0, 0.625)
 
local right = editbox:CreateTexture(nil, "BACKGROUND")
right:SetWidth(8) right:SetHeight(20)
right:SetPoint("RIGHT", 0, 0)
right:SetTexture("Interface\\Common\\Common-Input-Border")
right:SetTexCoord(0.9375, 1, 0, 0.625)
 
local center = editbox:CreateTexture(nil, "BACKGROUND")
center:SetHeight(20)
center:SetPoint("RIGHT", right, "LEFT", 0, 0)
center:SetPoint("LEFT", left, "RIGHT", 0, 0)
center:SetTexture("Interface\\Common\\Common-Input-Border")
center:SetTexCoord(0.0625, 0.9375, 0, 0.625)
 
editbox:SetScript("OnEscapePressed", editbox.ClearFocus)
editbox:SetScript("OnEnterPressed", editbox.ClearFocus)
editbox:SetScript("OnEditFocusLost", editbox.Hide)
editbox:SetScript("OnEditFocusGained", editbox.HighlightText)
editbox:SetScript("OnTextChanged", function(self)
self:SetText(self:GetParent().val)
self:HighlightText()
end)
 
 
function lib.OpenEditbox(self)
editbox:SetText(self.val)
editbox:SetParent(self)
editbox:SetPoint("LEFT", self)
editbox:SetPoint("RIGHT", self)
editbox:Show()
end
 
 
local fields = {"Version", "Author", "X-Category", "X-License", "X-Email", "Email", "eMail", "X-Website", "X-Credits", "X-Localizations", "X-Donate"}
local haseditbox = {["X-Website"] = true, ["X-Email"] = true, ["X-Donate"] = true, ["Email"] = true, ["eMail"] = true}
 
local function HideTooltip() GameTooltip:Hide() end
 
local function ShowTooltip(self)
GameTooltip:SetOwner(self, "ANCHOR_TOPRIGHT")
GameTooltip:SetText(L["Click and press Ctrl-C to copy"])
--GameTooltip:SetText("Click and press Ctrl-C to copy")
end
 
function lib.OnShow(frame)
 
local notefield = "Notes"
 
if (GAME_LOCALE ~= "enUS") then
notefield = notefield .. "-" .. GAME_LOCALE
end
 
-- Get the localized version of notes if it exists or fall back to the english one.
local notes = GetAddOnMetadata(frame.addonname, notefield) or GetAddOnMetadata(frame.addonname, "Notes")
 
if not frame.about_title then
frame.about_title = frame:CreateFontString(nil, "ARTWORK", "GameFontNormalLarge")
end
local title = frame.about_title
 
title:SetPoint("TOPLEFT", 16, -16)
title:SetText(frame.parent and (frame.parent.." - " .. L["About"]) or frame.name)
 
if not frame.about_subtitle then
frame.about_subtitle = frame:CreateFontString(nil, "ARTWORK", "GameFontHighlightSmall")
end
local subtitle = frame.about_subtitle
subtitle:SetHeight(32)
subtitle:SetPoint("TOPLEFT", title, "BOTTOMLEFT", 0, -8)
subtitle:SetPoint("RIGHT", frame, -32, 0)
subtitle:SetNonSpaceWrap(true)
subtitle:SetJustifyH("LEFT")
subtitle:SetJustifyV("TOP")
subtitle:SetText(notes)
 
local anchor
for _,field in pairs(fields) do
local val = GetAddOnMetadata(frame.addonname, field)
if val then
if not frame[field .. "_title"] then
frame[field .. "_title"] = frame:CreateFontString(nil, "ARTWORK", "GameFontNormalSmall")
end
local title = frame[field .. "_title"]
title:SetWidth(75)
 
if not anchor then
title:SetPoint("TOPLEFT", subtitle, "BOTTOMLEFT", -2, -12)
else
title:SetPoint("TOPLEFT", anchor, "BOTTOMLEFT", 0, -10)
end
title:SetJustifyH("RIGHT")
title:SetText(field:gsub("X%-", ""))
 
if not frame[field .. "_detail"] then
frame[field .. "_detail"] = frame:CreateFontString(nil, "ARTWORK", "GameFontHighlightSmall")
end
local detail = frame[field .. "_detail"]
detail:SetHeight(32)
detail:SetPoint("LEFT", title, "RIGHT", 4, 0)
detail:SetPoint("RIGHT", frame, -16, 0)
detail:SetJustifyH("LEFT")
 
if (field == "Author") then
local authorservername = GetAddOnMetadata(frame.addonname, "X-Author-Server")
local authorfaction = GetAddOnMetadata(frame.addonname, "X-Author-Faction")
 
if authorservername and authorfaction then
detail:SetText((haseditbox[field] and "|cff9999ff" or "").. val .. " on " .. authorservername .. " (" .. authorfaction .. ")")
elseif authorservername and not authorfaction then
detail:SetText((haseditbox[field] and "|cff9999ff" or "").. val .. " on " .. authorservername)
elseif not authorservername and authorfaction then
detail:SetText((haseditbox[field] and "|cff9999ff" or "").. val .. " (" .. authorfaction .. ")")
else
detail:SetText((haseditbox[field] and "|cff9999ff" or "").. val)
end
elseif (field == "Version") then
local addonversion = GetAddOnMetadata(frame.addonname, field)
-- Remove @project-revision@ and replace it with Repository
addonversion = string.gsub(addonversion,"@project.revision@","Repository")
detail:SetText((haseditbox[field] and "|cff9999ff" or "").. addonversion)
else
detail:SetText((haseditbox[field] and "|cff9999ff" or "").. val)
end
 
if haseditbox[field] then
local button = CreateFrame("Button", nil, frame)
button:SetAllPoints(detail)
button.val = val
button:SetScript("OnClick", lib.OpenEditbox)
button:SetScript("OnEnter", ShowTooltip)
button:SetScript("OnLeave", HideTooltip)
end
 
anchor = title
end
end
 
end