commit 120bb38b3772a4fffb5a3ff15b1d604101a1b016 Author: buhfur-ubuntu Date: Sun May 31 14:10:07 2026 -0400 Initial commit diff --git a/Bindings.xml b/Bindings.xml new file mode 100755 index 0000000..dbb6689 --- /dev/null +++ b/Bindings.xml @@ -0,0 +1,76 @@ + + + + + + ChatFrame_OpenChat("/o"); + + + ChatFrame_OpenChat("/g"); + + + ChatFrame_OpenChat("/p"); + + + ChatFrame_OpenChat("/raid"); + + + ChatFrame_OpenChat("/rw"); + + + ChatFrame_OpenChat("/bg"); + + + ChatFrame_OpenChat("/say"); + + + ChatFrame_OpenChat("/yell"); + + + ChatFrame_OpenChat("/w"); + + + ChatFrame_OpenChat("/1"); + + + ChatFrame_OpenChat("/2"); + + + ChatFrame_OpenChat("/3"); + + + ChatFrame_OpenChat("/4"); + + + ChatFrame_OpenChat("/5"); + + + ChatFrame_OpenChat("/6"); + + + ChatFrame_OpenChat("/7"); + + + ChatFrame_OpenChat("/8"); + + + ChatFrame_OpenChat("/9"); + + + ChatFrame_OpenChat("/10"); + + + + Prat_TellTarget:TellTarget(); + + diff --git a/Prat.lua b/Prat.lua new file mode 100755 index 0000000..f6c6ebb --- /dev/null +++ b/Prat.lua @@ -0,0 +1,50 @@ +--[[ +Name: Prat +Author(s): Curney (asml8ed@gmail.com) + Krtek (krtek4@gmail.com) + laytya (laytya at github) +Inspired By: idChat2 by Industrial +Website: https://github.com/laytya/Prat +Description: A framework for chat frame modules. +Dependencies: Ace2 +]] + +local L = AceLibrary("AceLocale-2.2"):new("Prat") +local waterfall = AceLibrary("Waterfall-1.0") + +L:RegisterTranslations("enUS", function() return { + ["Prat"] = true, + ["A framework for chat frame modules."] = true, +} end) + +L:RegisterTranslations("ruRU", function() return { + ["Prat"] = "Prat", + ["A framework for chat frame modules."] = "Фреймворк для чата.", +} end) + +L:RegisterTranslations("zhCN", function() return { + ["Prat"] = "Prat", + ["A framework for chat frame modules."] = "聊天窗口增加插件。", +} end) + +L:RegisterTranslations("koKR", function() return { + ["Prat"] = "Prat", + ["A framework for chat frame modules."] = "대화창 모듈에 대한 프레임워크", +} end) + +Prat = AceLibrary("AceAddon-2.0"):new("AceConsole-2.0", "AceDB-2.0", "AceDebug-2.0", "AceEvent-2.0", "AceHook-2.1", "AceModuleCore-2.0") + +Prat:RegisterDB("PratDB") +Prat:SetModuleMixins("AceEvent-2.0", "AceHook-2.1") +Prat.Options = { + name = L["Prat"], + desc = L["A framework for chat frame modules."], + type = "group", + args = {} +} + +local version = GetAddOnMetadata("Prat", "Version"); + +Prat:RegisterChatCommand({"/pratcl"}, Prat.Options) +Prat:RegisterChatCommand({"/prat"}, function() waterfall:Open('Prat') end) +waterfall:Register('Prat', 'aceOptions', Prat.Options, 'title','Prat ' .. version,'colorR', 0.6, 'colorG', 0.7, 'colorB', 1) \ No newline at end of file diff --git a/Prat.toc b/Prat.toc new file mode 100755 index 0000000..9c5091d --- /dev/null +++ b/Prat.toc @@ -0,0 +1,58 @@ +## Interface: 11200 + +## Title: Prat |cff7fff7f -Ace2-|r +## Title-zhCN: |cff7fff7f-Ace2-|r Prat +## Notes-ruRU: Фреймворк для чата. +## Notes: A framework for chat frame modules. +## Notes-frFR: Framework pour des modules concernant les fentres de discussion +## Version: v1.0.8 +## Author: Prat Development Team, laytya, Qxcl + +## X-Category: Chat/Communication +## X-Website: https://github.com/Qxcl/Prat-turtle +## X-Credits: Industrial, Ammo, Cirk, Yris, Leuchtturm, AnduinLothar, Satrina, 昏睡墨鱼&月色狼影, Prat Development Team, RuWOW team + +## OptionalDeps: Ace2 + +## SavedVariables: PratDB + +libs\AceLibrary\AceLibrary.lua +libs\AceOO-2.0\AceOO-2.0.lua +libs\AceAddon-2.0\AceAddon-2.0.lua +libs\AceConsole-2.0\AceConsole-2.0.lua +libs\AceDB-2.0\AceDB-2.0.lua +libs\AceDebug-2.0\AceDebug-2.0.lua +libs\AceEvent-2.0\AceEvent-2.0.lua +libs\AceHook-2.1\AceHook-2.1.lua +libs\AceTab-2.0\AceTab-2.0.lua +libs\AceModuleCore-2.0\AceModuleCore-2.0.lua +libs\AceLocale-2.2\AceLocale-2.2.lua +libs\Babble-Class-2.2\Babble-Class-2.2.lua +libs\Waterfall-1.0\Waterfall-1.0.lua + +Prat.lua + +modules\Buttons.lua +modules\ChannelNames.lua +modules\ChannelSeparator.lua +modules\ChannelColorMemory.lua +modules\ChatTabs.lua +modules\Fading.lua +modules\FontSize.lua +modules\Editbox.lua +modules\Highlight.lua +modules\History.lua +modules\Justify.lua +modules\KeyBindings.lua +modules\Notification.lua +modules\PlayerNames.lua +modules\PopupMessage.lua +modules\Scroll.lua +modules\StickyChannels.lua +modules\TellTarget.lua +modules\Timestamps.lua +modules\UrlCopy.lua +modules\Who.lua + +frames\who.xml +frames\popup.xml diff --git a/font/WarSansTT-Bliz-500.ttf b/font/WarSansTT-Bliz-500.ttf new file mode 100755 index 0000000..06607c8 Binary files /dev/null and b/font/WarSansTT-Bliz-500.ttf differ diff --git a/frames/popup.xml b/frames/popup.xml new file mode 100755 index 0000000..dc6d346 --- /dev/null +++ b/frames/popup.xml @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Prat_PopupMessage:PopupUpdated(arg1); + + + + + \ No newline at end of file diff --git a/frames/who.xml b/frames/who.xml new file mode 100755 index 0000000..faaee9c --- /dev/null +++ b/frames/who.xml @@ -0,0 +1,416 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + FauxScrollFrame_OnVerticalScroll(PRAT_WHO_FRAME_HEIGHT, Prat_Who_UpdateFrame); + + + + + + + + + PratWhoFrame:StartMoving(); + + + PratWhoFrame:StopMovingOrSizing(); + + + PratWhoFrame:StopMovingOrSizing(); + + + + diff --git a/libs/AceAddon-2.0/AceAddon-2.0.lua b/libs/AceAddon-2.0/AceAddon-2.0.lua new file mode 100755 index 0000000..7d8c4dd --- /dev/null +++ b/libs/AceAddon-2.0/AceAddon-2.0.lua @@ -0,0 +1,1078 @@ +--[[ +Name: AceAddon-2.0 +Revision: $Rev: 17957 $ +Developed by: The Ace Development Team (http://www.wowace.com/index.php/The_Ace_Development_Team) +Inspired By: Ace 1.x by Turan (turan@gryphon.com) +Website: http://www.wowace.com/ +Documentation: http://www.wowace.com/index.php/AceAddon-2.0 +SVN: http://svn.wowace.com/root/trunk/Ace2/AceAddon-2.0 +Description: Base for all Ace addons to inherit from. +Dependencies: AceLibrary, AceOO-2.0, AceEvent-2.0, (optional) AceConsole-2.0 +]] + +local MAJOR_VERSION = "AceAddon-2.0" +local MINOR_VERSION = "$Revision: 17957 $" + +-- This ensures the code is only executed if the libary doesn't already exist, or is a newer version +if not AceLibrary then error(MAJOR_VERSION .. " requires AceLibrary.") end +if not AceLibrary:IsNewVersion(MAJOR_VERSION, MINOR_VERSION) then return end + +if loadstring("return function(...) return ... end") and AceLibrary:HasInstance(MAJOR_VERSION) then return end -- lua51 check +if not AceLibrary:HasInstance("AceOO-2.0") then error(MAJOR_VERSION .. " requires AceOO-2.0.") end + +local function safecall(func,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10) + local success, err = pcall(func,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10) + if not success then geterrorhandler()(err) end +end + +-- Localization +local STANDBY, TITLE, NOTES, VERSION, AUTHOR, DATE, CATEGORY, EMAIL, CREDITS, WEBSITE, COMMANDS, CATEGORIES, ABOUT, PRINT_ADDON_INFO +if GetLocale() == "deDE" then + STANDBY = "|cffff5050(Standby)|r" -- capitalized + + TITLE = "Titel" + NOTES = "Anmerkung" + VERSION = "Version" + AUTHOR = "Autor" + DATE = "Datum" + CATEGORY = "Kategorie" + EMAIL = "E-mail" + WEBSITE = "Webseite" + CREDITS = "Credits" -- fix + COMMANDS = "Commands" + + ABOUT = "\195\188ber" + PRINT_ADDON_INFO = "Gibt Addondaten aus" + + CATEGORIES = { + ["Action Bars"] = "Aktionsleisten", + ["Auction"] = "Auktion", + ["Audio"] = "Audio", + ["Battlegrounds/PvP"] = "Schlachtfeld/PvP", + ["Buffs"] = "Buffs", + ["Chat/Communication"] = "Chat/Kommunikation", + ["Druid"] = "Druide", + ["Hunter"] = "Jäger", + ["Mage"] = "Magier", + ["Paladin"] = "Paladin", + ["Priest"] = "Priester", + ["Rogue"] = "Schurke", + ["Shaman"] = "Schamane", + ["Warlock"] = "Hexenmeister", + ["Warrior"] = "Krieger", + ["Healer"] = "Heiler", + ["Tank"] = "Tank", -- noone use "Brecher"... + ["Caster"] = "Caster", + ["Combat"] = "Kampf", + ["Compilations"] = "Compilations", -- whats that o_O + ["Data Export"] = "Datenexport", + ["Development Tools"] = "Entwicklungs Tools", + ["Guild"] = "Gilde", + ["Frame Modification"] = "Frame Modifikation", + ["Interface Enhancements"] = "Interface Verbesserungen", + ["Inventory"] = "Inventar", + ["Library"] = "Library", + ["Map"] = "Map", + ["Mail"] = "Mail", + ["Miscellaneous"] = "Diverses", + ["Quest"] = "Quest", + ["Raid"] = "Schlachtzug", + ["Tradeskill"] = "Handelsf\195\164higkeit", + ["UnitFrame"] = "UnitFrame", + } +elseif GetLocale() == "frFR" then + STANDBY = "|cffff5050(attente)|r" + + TITLE = "Titre" + NOTES = "Notes" + VERSION = "Version" + AUTHOR = "Auteur" + DATE = "Date" + CATEGORY = "Cat\195\169gorie" + EMAIL = "E-mail" + WEBSITE = "Site web" + CREDITS = "Credits" -- fix + COMMANDS = "Commands" + + ABOUT = "A propos" + PRINT_ADDON_INFO = "Afficher les informations sur l'addon" + + CATEGORIES = { + ["Action Bars"] = "Barres d'action", + ["Auction"] = "H\195\180tel des ventes", + ["Audio"] = "Audio", + ["Battlegrounds/PvP"] = "Champs de bataille/JcJ", + ["Buffs"] = "Buffs", + ["Chat/Communication"] = "Chat/Communication", + ["Druid"] = "Druide", + ["Hunter"] = "Chasseur", + ["Mage"] = "Mage", + ["Paladin"] = "Paladin", + ["Priest"] = "Pr\195\170tre", + ["Rogue"] = "Voleur", + ["Shaman"] = "Chaman", + ["Warlock"] = "D\195\169moniste", + ["Warrior"] = "Guerrier", + ["Healer"] = "Soigneur", + ["Tank"] = "Tank", + ["Caster"] = "Casteur", + ["Combat"] = "Combat", + ["Compilations"] = "Compilations", + ["Data Export"] = "Exportation de donn\195\169es", + ["Development Tools"] = "Outils de d\195\169veloppement", + ["Guild"] = "Guilde", + ["Frame Modification"] = "Modification des fen\195\170tres", + ["Interface Enhancements"] = "Am\195\169liorations de l'interface", + ["Inventory"] = "Inventaire", + ["Library"] = "Biblioth\195\168ques", + ["Map"] = "Carte", + ["Mail"] = "Courrier", + ["Miscellaneous"] = "Divers", + ["Quest"] = "Qu\195\170tes", + ["Raid"] = "Raid", + ["Tradeskill"] = "M\195\169tiers", + ["UnitFrame"] = "Fen\195\170tres d'unit\195\169", + } +elseif GetLocale() == "koKR" then + STANDBY = "|cffff5050(사용가능)|r" + + TITLE = "제목" + NOTES = "노트" + VERSION = "버전" + AUTHOR = "저작자" + DATE = "날짜" + CATEGORY = "분류" + EMAIL = "E-mail" + WEBSITE = "웹사이트" + CREDITS = "Credits" -- fix + COMMANDS = "Commands" + + ABOUT = "정보" + PRINT_ADDON_INFO = "애드온 정보 출력" + + CATEGORIES = { + ["Action Bars"] = "액션바", + ["Auction"] = "경매", + ["Audio"] = "음향", + ["Battlegrounds/PvP"] = "전장/PvP", + ["Buffs"] = "버프", + ["Chat/Communication"] = "대화/의사소통", + ["Druid"] = "드루이드", + ["Hunter"] = "사냥꾼", + ["Mage"] = "마법사", + ["Paladin"] = "성기사", + ["Priest"] = "사제", + ["Rogue"] = "도적", + ["Shaman"] = "주술사", + ["Warlock"] = "흑마법사", + ["Warrior"] = "전사", + ["Healer"] = "힐러", + ["Tank"] = "탱커", + ["Caster"] = "캐스터", + ["Combat"] = "전투", + ["Compilations"] = "복합", + ["Data Export"] = "자료 출력", + ["Development Tools"] = "개발 도구", + ["Guild"] = "길드", + ["Frame Modification"] = "구조 변경", + ["Interface Enhancements"] = "인터페이스 강화", + ["Inventory"] = "인벤토리", + ["Library"] = "라이브러리", + ["Map"] = "지도", + ["Mail"] = "우편", + ["Miscellaneous"] = "기타", + ["Quest"] = "퀘스트", + ["Raid"] = "공격대", + ["Tradeskill"] = "전문기술", + ["UnitFrame"] = "유닛 프레임", + } +elseif GetLocale() == "zhTW" then + STANDBY = "|cffff5050(待命)|r" + + TITLE = "標題" + NOTES = "註記" + VERSION = "版本" + AUTHOR = "作者" + DATE = "日期" + CATEGORY = "類別" + EMAIL = "E-mail" + WEBSITE = "網站" + CREDITS = "Credits" -- fix + COMMANDS = "Commands" + + ABOUT = "關於" + PRINT_ADDON_INFO = "顯示插件資訊" + + CATEGORIES = { + ["Action Bars"] = "動作列", + ["Auction"] = "拍賣", + ["Audio"] = "音樂", + ["Battlegrounds/PvP"] = "戰場/PvP", + ["Buffs"] = "增益", + ["Chat/Communication"] = "聊天/通訊", + ["Druid"] = "德魯伊", + ["Hunter"] = "獵人", + ["Mage"] = "法師", + ["Paladin"] = "聖騎士", + ["Priest"] = "牧師", + ["Rogue"] = "盜賊", + ["Shaman"] = "薩滿", + ["Warlock"] = "術士", + ["Warrior"] = "戰士", + ["Healer"] = "治療者", + ["Tank"] = "坦克", + ["Caster"] = "施法者", + ["Combat"] = "戰鬥", + ["Compilations"] = "編輯", + ["Data Export"] = "資料匯出", + ["Development Tools"] = "開發工具", + ["Guild"] = "公會", + ["Frame Modification"] = "框架修改", + ["Interface Enhancements"] = "介面增強", + ["Inventory"] = "背包", + ["Library"] = "資料庫", + ["Map"] = "地圖", + ["Mail"] = "郵件", + ["Miscellaneous"] = "綜合", + ["Quest"] = "任務", + ["Raid"] = "團隊", + ["Tradeskill"] = "商業技能", + ["UnitFrame"] = "單位框架", + } +elseif GetLocale() == "zhCN" then + STANDBY = "|cffff5050(\230\154\130\230\140\130)|r" + + TITLE = "\230\160\135\233\162\152" + NOTES = "\233\153\132\230\179\168" + VERSION = "\231\137\136\230\156\172" + AUTHOR = "\228\189\156\232\128\133" + DATE = "\230\151\165\230\156\159" + CATEGORY = "\229\136\134\231\177\187" + EMAIL = "\231\148\181\229\173\144\233\130\174\228\187\182" + WEBSITE = "\231\189\145\231\171\153" + CREDITS = "Credits" -- fix + COMMANDS = "Commands" + + ABOUT = "\229\133\179\228\186\142" + PRINT_ADDON_INFO = "\229\141\176\229\136\151\229\135\186\230\143\146\228\187\182\228\191\161\230\129\175" + + CATEGORIES = { + ["Action Bars"] = "\229\138\168\228\189\156\230\157\161", + ["Auction"] = "\230\139\141\229\141\150", + ["Audio"] = "\233\159\179\233\162\145", + ["Battlegrounds/PvP"] = "\230\136\152\229\156\186/PvP", + ["Buffs"] = "\229\162\158\231\155\138\233\173\148\230\179\149", + ["Chat/Communication"] = "\232\129\138\229\164\169/\228\186\164\230\181\129", + ["Druid"] = "\229\190\183\233\178\129\228\188\138", + ["Hunter"] = "\231\140\142\228\186\186", + ["Mage"] = "\230\179\149\229\184\136", + ["Paladin"] = "\229\156\163\233\170\145\229\163\171", + ["Priest"] = "\231\137\167\229\184\136", + ["Rogue"] = "\231\155\151\232\180\188", + ["Shaman"] = "\232\144\168\230\187\161\231\165\173\229\143\184", + ["Warlock"] = "\230\156\175\229\163\171", + ["Warrior"] = "\230\136\152\229\163\171", +-- ["Healer"] = "\230\178\187\231\150\151\228\191\157\233\154\156", +-- ["Tank"] = "\232\191\145\230\136\152\230\142\167\229\136\182", +-- ["Caster"] = "\232\191\156\231\168\139\232\190\147\229\135\186", + ["Combat"] = "\230\136\152\230\150\151", + ["Compilations"] = "\231\188\150\232\175\145", + ["Data Export"] = "\230\149\176\230\141\174\229\175\188\229\135\186", + ["Development Tools"] = "\229\188\128\229\143\145\229\183\165\229\133\183", + ["Guild"] = "\229\133\172\228\188\154", + ["Frame Modification"] = "\230\161\134\230\158\182\228\191\174\230\148\185", + ["Interface Enhancements"] = "\231\149\140\233\157\162\229\162\158\229\188\186", + ["Inventory"] = "\232\131\140\229\140\133", + ["Library"] = "\229\186\147", + ["Map"] = "\229\156\176\229\155\190", + ["Mail"] = "\233\130\174\228\187\182", + ["Miscellaneous"] = "\230\157\130\233\161\185", + ["Quest"] = "\228\187\187\229\138\161", + ["Raid"] = "\229\155\162\233\152\159", + ["Tradeskill"] = "\229\149\134\228\184\154\230\138\128\232\131\189", + ["UnitFrame"] = "\229\164\180\229\131\143\230\161\134\230\158\182", + } +elseif GetLocale() == "ruRU" then + STANDBY = "|cffff5050(в режиме ожидания)|r" + + TITLE = "Название" + NOTES = "Описание" + VERSION = "Версия" + AUTHOR = "Автор" + DATE = "Дата" + CATEGORY = "Категория" + EMAIL = "E-mail" + WEBSITE = "Сайт" + CREDITS = "Титры" + COMMANDS = "Команды" + + ABOUT = "Об аддоне" + PRINT_ADDON_INFO = "Показать информацию об аддоне." + + CATEGORIES = { + ["Action Bars"] = "Панели команд", + ["Auction"] = "Аукцион", + ["Audio"] = "Аудио", + ["Battlegrounds/PvP"] = "Поля сражений/PvP", + ["Buffs"] = "Баффы", + ["Chat/Communication"] = "Чат/Коммуникация", + ["Druid"] = "Друид", + ["Hunter"] = "Охотник", + ["Mage"] = "Маг", + ["Paladin"] = "Паладин", + ["Priest"] = "Жрец", + ["Rogue"] = "Разбойник", + ["Shaman"] = "Шаман", + ["Warlock"] = "Чернокнижник", + ["Warrior"] = "Воин", + ["Healer"] = "Лекарь", + ["Tank"] = "Танк", + ["Caster"] = "Кастер", + ["Combat"] = "Сражения", + ["Compilations"] = "Компиляция", + ["Data Export"] = "Экспорт данных", + ["Development Tools"] = "Инструменты разработчика", + ["Guild"] = "Гильдия", + ["Frame Modification"] = "Модификация фреймов", + ["Interface Enhancements"] = "Улучшение интерфейса", + ["Inventory"] = "Инвентарь", + ["Library"] = "Библиотеки", + ["Map"] = "Карта", + ["Mail"] = "Почта", + ["Miscellaneous"] = "Разное", + ["Quest"] = "Задания", + ["Raid"] = "Рейд", + ["Tradeskill"] = "Умения", + ["UnitFrame"] = "Фреймы персонажей", + } +else -- enUS + STANDBY = "|cffff5050(standby)|r" + + TITLE = "Title" + NOTES = "Notes" + VERSION = "Version" + AUTHOR = "Author" + DATE = "Date" + CATEGORY = "Category" + EMAIL = "E-mail" + WEBSITE = "Website" + CREDITS = "Credits" + COMMANDS = "Commands" + + ABOUT = "About" + PRINT_ADDON_INFO = "Print out addon info" + + CATEGORIES = { + ["Action Bars"] = "Action Bars", + ["Auction"] = "Auction", + ["Audio"] = "Audio", + ["Battlegrounds/PvP"] = "Battlegrounds/PvP", + ["Buffs"] = "Buffs", + ["Chat/Communication"] = "Chat/Communication", + ["Druid"] = "Druid", + ["Hunter"] = "Hunter", + ["Mage"] = "Mage", + ["Paladin"] = "Paladin", + ["Priest"] = "Priest", + ["Rogue"] = "Rogue", + ["Shaman"] = "Shaman", + ["Warlock"] = "Warlock", + ["Warrior"] = "Warrior", + ["Healer"] = "Healer", + ["Tank"] = "Tank", + ["Caster"] = "Caster", + ["Combat"] = "Combat", + ["Compilations"] = "Compilations", + ["Data Export"] = "Data Export", + ["Development Tools"] = "Development Tools", + ["Guild"] = "Guild", + ["Frame Modification"] = "Frame Modification", + ["Interface Enhancements"] = "Interface Enhancements", + ["Inventory"] = "Inventory", + ["Library"] = "Library", + ["Map"] = "Map", + ["Mail"] = "Mail", + ["Miscellaneous"] = "Miscellaneous", + ["Quest"] = "Quest", + ["Raid"] = "Raid", + ["Tradeskill"] = "Tradeskill", + ["UnitFrame"] = "UnitFrame", + } +end + +setmetatable(CATEGORIES, { __index = function(self, key) -- case-insensitive + local lowerKey = string.lower(key) + for k,v in pairs(CATEGORIES) do + if string.lower(k) == lowerKey then + return v + end + end +end }) + +-- Create the library object + +local AceOO = AceLibrary("AceOO-2.0") +local AceAddon = AceOO.Class() +local AceEvent +local AceConsole +local AceModuleCore + +function AceAddon:ToString() + return "AceAddon" +end + +local function print(text) + DEFAULT_CHAT_FRAME:AddMessage(text) +end + +function AceAddon:ADDON_LOADED(name) + while table.getn(self.nextAddon) > 0 do + local addon = table.remove(self.nextAddon, 1) + table.insert(self.addons, addon) + if not self.addons[name] then + self.addons[name] = addon + end + self:InitializeAddon(addon, name) + end +end + +local function RegisterOnEnable(self) + if DEFAULT_CHAT_FRAME and DEFAULT_CHAT_FRAME.defaultLanguage then -- HACK + AceAddon.playerLoginFired = true + end + if AceAddon.playerLoginFired then + AceAddon.addonsStarted[self] = true + if (type(self.IsActive) ~= "function" or self:IsActive()) and (not AceModuleCore or not AceModuleCore:IsModule(self) or AceModuleCore:IsModuleActive(self)) then + local current = self.class + while true do + if current == AceOO.Class then + break + end + if current.mixins then + for mixin in pairs(current.mixins) do + if type(mixin.OnEmbedEnable) == "function" then + safecall(mixin.OnEmbedEnable,mixin,self) + end + end + end + current = current.super + end + if type(self.OnEnable) == "function" then + safecall(self.OnEnable,self) + end + if AceEvent then + AceEvent:TriggerEvent("Ace2_AddonEnabled", self) + end + end + else + if not AceAddon.addonsToOnEnable then + AceAddon.addonsToOnEnable = {} + end + table.insert(AceAddon.addonsToOnEnable, self) + end +end + +local function stripSpaces(text) + if type(text) == "string" then + return (string.gsub(string.gsub(text, "^%s*(.-)%s*$", "%1"), "%s%s+", " ")) + end + return text +end + +function AceAddon:InitializeAddon(addon, name) + if addon.name == nil then + addon.name = name + end + if GetAddOnMetadata then + -- TOC checks + if addon.title == nil then + addon.title = GetAddOnMetadata(name, "Title") + end + if addon.title then + local num = string.find(addon.title, " |cff7fff7f %-Ace2%-|r$") + if num then + addon.title = string.sub(addon.title, 1, num - 1) + end + addon.title = stripSpaces(addon.title) + end + + if addon.notes == nil then + addon.notes = GetAddOnMetadata(name, "Notes") + addon.notes = stripSpaces(addon.notes) + end + if addon.version == nil then + addon.version = GetAddOnMetadata(name, "Version") + end + if addon.version then + if string.find(addon.version, "%$Revision: (%d+) %$") then + addon.version = string.gsub(addon.version, "%$Revision: (%d+) %$", "%1") + elseif string.find(addon.version, "%$Rev: (%d+) %$") then + addon.version = string.gsub(addon.version, "%$Rev: (%d+) %$", "%1") + elseif string.find(addon.version, "%$LastChangedRevision: (%d+) %$") then + addon.version = string.gsub(addon.version, "%$LastChangedRevision: (%d+) %$", "%1") + end + end + addon.version = stripSpaces(addon.version) + if addon.author == nil then + addon.author = GetAddOnMetadata(name, "Author") + addon.author = stripSpaces(addon.author) + end + if addon.credits == nil then + addon.credits = GetAddOnMetadata(name, "X-Credits") + addon.credits = stripSpaces(addon.credits) + end + if addon.date == nil then + addon.date = GetAddOnMetadata(name, "X-Date") or GetAddOnMetadata(name, "X-ReleaseDate") + end + if addon.date then + if string.find(addon.date, "%$Date: (.-) %$") then + addon.date = string.gsub(addon.date, "%$Date: (.-) %$", "%1") + elseif string.find(addon.date, "%$LastChangedDate: (.-) %$") then + addon.date = string.gsub(addon.date, "%$LastChangedDate: (.-) %$", "%1") + end + end + addon.date = stripSpaces(addon.date) + + if addon.category == nil then + addon.category = GetAddOnMetadata(name, "X-Category") + addon.category = stripSpaces(addon.category) + end + if addon.email == nil then + addon.email = GetAddOnMetadata(name, "X-eMail") or GetAddOnMetadata(name, "X-Email") + addon.email = stripSpaces(addon.email) + end + if addon.website == nil then + addon.website = GetAddOnMetadata(name, "X-Website") + addon.website = stripSpaces(addon.website) + end + if addon.commands == nil then + addon.commands = GetAddOnMetadata(name, "X-Commands") + addon.commands = stripSpaces(addon.commands) + end + end + local current = addon.class + while true do + if current == AceOO.Class then + break + end + if current.mixins then + for mixin in pairs(current.mixins) do + if type(mixin.OnEmbedInitialize) == "function" then + mixin:OnEmbedInitialize(addon, name) + end + end + end + current = current.super + end + if type(addon.OnInitialize) == "function" then + safecall(addon.OnInitialize, addon, name) + end + if AceEvent then + AceEvent:TriggerEvent("Ace2_AddonInitialized", addon) + end + RegisterOnEnable(addon) +end + +function AceAddon.prototype:PrintAddonInfo() + local x + if self.title then + x = "|cffffff7f" .. tostring(self.title) .. "|r" + elseif self.name then + x = "|cffffff7f" .. tostring(self.name) .. "|r" + else + x = "|cffffff7f<" .. tostring(self.class) .. " instance>|r" + end + if type(self.IsActive) == "function" then + if not self:IsActive() then + x = x .. " " .. STANDBY + end + end + if self.version then + x = x .. " - |cffffff7f" .. tostring(self.version) .. "|r" + end + if self.notes then + x = x .. " - " .. tostring(self.notes) + end + print(x) + if self.author then + print(" - |cffffff7f" .. AUTHOR .. ":|r " .. tostring(self.author)) + end + if self.credits then + print(" - |cffffff7f" .. CREDITS .. ":|r " .. tostring(self.credits)) + end + if self.date then + print(" - |cffffff7f" .. DATE .. ":|r " .. tostring(self.date)) + end + if self.category then + local category = CATEGORIES[self.category] + if category then + print(" - |cffffff7f" .. CATEGORY .. ":|r " .. category) + end + end + if self.email then + print(" - |cffffff7f" .. EMAIL .. ":|r " .. tostring(self.email)) + end + if self.website then + print(" - |cffffff7f" .. WEBSITE .. ":|r " .. tostring(self.website)) + end + if self.commands then + print(" - |cffffff7f" .. COMMANDS .. ":|r " .. tostring(self.commands)) + end +end + +local options +function AceAddon:GetAceOptionsDataTable(target) + if not options then + options = { + about = { + name = ABOUT, + desc = PRINT_ADDON_INFO, + type = "execute", + func = "PrintAddonInfo", + order = -1, + } + } + end + return options +end + +function AceAddon:PLAYER_LOGIN() + self.playerLoginFired = true + if self.addonsToOnEnable then + while table.getn(self.addonsToOnEnable) > 0 do + local addon = table.remove(self.addonsToOnEnable, 1) + self.addonsStarted[addon] = true + if (type(addon.IsActive) ~= "function" or addon:IsActive()) and (not AceModuleCore or not AceModuleCore:IsModule(addon) or AceModuleCore:IsModuleActive(addon)) then + local current = addon.class + while true do + if current == AceOO.Class then + break + end + if current.mixins then + for mixin in pairs(current.mixins) do + if type(mixin.OnEmbedEnable) == "function" then + safecall(mixin.OnEmbedEnable,mixin,addon) + end + end + end + current = current.super + end + if type(addon.OnEnable) == "function" then + safecall(addon.OnEnable,addon) + end + if AceEvent then + AceEvent:TriggerEvent("Ace2_AddonEnabled", addon) + end + end + end + self.addonsToOnEnable = nil + end +end + +function AceAddon.prototype:Inject(t) + AceAddon:argCheck(t, 2, "table") + for k,v in pairs(t) do + self[k] = v + end +end + +function AceAddon.prototype:init() + if not AceEvent then + error(MAJOR_VERSION .. " requires AceEvent-2.0", 4) + end + AceAddon.super.prototype.init(self) + + self.super = self.class.prototype + + AceAddon:RegisterEvent("ADDON_LOADED", "ADDON_LOADED", true) + table.insert(AceAddon.nextAddon, self) +end + +function AceAddon.prototype:ToString() + local x + if type(self.title) == "string" then + x = self.title + elseif type(self.name) == "string" then + x = self.name + else + x = "<" .. tostring(self.class) .. " instance>" + end + if (type(self.IsActive) == "function" and not self:IsActive()) or (AceModuleCore and AceModuleCore:IsModule(addon) and AceModuleCore:IsModuleActive(addon)) then + x = x .. " " .. STANDBY + end + return x +end + +AceAddon.new = function(self, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11, m12, m13, m14, m15, m16, m17, m18, m19, m20) + local class = AceAddon:pcall(AceOO.Classpool, self, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11, m12, m13, m14, m15, m16, m17, m18, m19, m20) + return class:new() +end + +local function external(self, major, instance) + if major == "AceEvent-2.0" then + AceEvent = instance + + AceEvent:embed(self) + + self:RegisterEvent("PLAYER_LOGIN", "PLAYER_LOGIN", true) + elseif major == "AceConsole-2.0" then + AceConsole = instance + + local slashCommands = { "/ace2" } + local _,_,_,enabled,loadable = GetAddOnInfo("Ace") + if not enabled or not loadable then + table.insert(slashCommands, "/ace") + end + local function listAddon(addon, depth) + if not depth then + depth = 0 + end + + local s = string.rep(" ", depth) .. " - " .. tostring(addon) + if rawget(addon, 'version') then + s = s .. " - |cffffff7f" .. tostring(addon.version) .. "|r" + end + if rawget(addon, 'slashCommand') then + s = s .. " |cffffff7f(" .. tostring(addon.slashCommand) .. ")|r" + end + print(s) + if type(rawget(addon, 'modules')) == "table" then + local i = 0 + for k,v in pairs(addon.modules) do + i = i + 1 + if i == 6 then + print(string.rep(" ", depth + 1) .. " - more...") + break + else + listAddon(v, depth + 1) + end + end + end + end + local function listNormalAddon(i) + local name,_,_,enabled,loadable = GetAddOnInfo(i) + if not loadable then + enabled = false + end + if self.addons[name] then + local addon = self.addons[name] + if not AceCoreAddon or not AceCoreAddon:IsModule(addon) then + listAddon(addon) + end + else + local s = " - " .. tostring(GetAddOnMetadata(i, "Title") or name) + local version = GetAddOnMetadata(i, "Version") + if version then + if string.find(version, "%$Revision: (%d+) %$") then + version = string.gsub(version, "%$Revision: (%d+) %$", "%1") + elseif string.find(version, "%$Rev: (%d+) %$") then + version = string.gsub(version, "%$Rev: (%d+) %$", "%1") + elseif string.find(version, "%$LastChangedRevision: (%d+) %$") then + version = string.gsub(version, "%$LastChangedRevision: (%d+) %$", "%1") + end + s = s .. " - |cffffff7f" .. version .. "|r" + end + if not enabled then + s = s .. " |cffff0000(disabled)|r" + end + if IsAddOnLoadOnDemand(i) then + s = s .. " |cff00ff00[LoD]|r" + end + print(s) + end + end + local function mySort(alpha, bravo) + return tostring(alpha) < tostring(bravo) + end + AceConsole.RegisterChatCommand(self, slashCommands, { + desc = "AddOn development framework", + name = "Ace2", + type = "group", + args = { + about = { + desc = "Get information about Ace2", + name = "About", + type = "execute", + func = function() + print("|cffffff7fAce2|r - |cffffff7f2.0." .. string.gsub(MINOR_VERSION, "%$Revision: (%d+) %$", "%1") .. "|r - AddOn development framework") + print(" - |cffffff7f" .. AUTHOR .. ":|r Ace Development Team") + print(" - |cffffff7f" .. WEBSITE .. ":|r http://www.wowace.com/") + end + }, + list = { + desc = "List addons", + name = "List", + type = "group", + args = { + ace2 = { + desc = "List addons using Ace2", + name = "Ace2", + type = "execute", + func = function() + print("|cffffff7fAddon list:|r") + local AceCoreAddon = AceLibrary:HasInstance("AceCoreAddon-2.0") and AceLibrary("AceCoreAddon-2.0") + table.sort(self.addons, mySort) + for _,v in ipairs(self.addons) do + if not AceCoreAddon or not AceCoreAddon:IsModule(v) then + listAddon(v) + end + end + end + }, + all = { + desc = "List all addons", + name = "All", + type = "execute", + func = function() + print("|cffffff7fAddon list:|r") + local AceCoreAddon = AceLibrary:HasInstance("AceCoreAddon-2.0") and AceLibrary("AceCoreAddon-2.0") + local count = GetNumAddOns() + for i = 1, count do + listNormalAddon(i) + end + end + }, + enabled = { + desc = "List all enabled addons", + name = "Enabled", + type = "execute", + func = function() + print("|cffffff7fAddon list:|r") + local AceCoreAddon = AceLibrary:HasInstance("AceCoreAddon-2.0") and AceLibrary("AceCoreAddon-2.0") + local count = GetNumAddOns() + for i = 1, count do + local _,_,_,enabled,loadable = GetAddOnInfo(i) + if enabled and loadable then + listNormalAddon(i) + end + end + end + }, + disabled = { + desc = "List all disabled addons", + name = "Disabled", + type = "execute", + func = function() + print("|cffffff7fAddon list:|r") + local AceCoreAddon = AceLibrary:HasInstance("AceCoreAddon-2.0") and AceLibrary("AceCoreAddon-2.0") + local count = GetNumAddOns() + for i = 1, count do + local _,_,_,enabled,loadable = GetAddOnInfo(i) + if not enabled or not loadable then + listNormalAddon(i) + end + end + end + }, + lod = { + desc = "List all LoadOnDemand addons", + name = "LoadOnDemand", + type = "execute", + func = function() + print("|cffffff7fAddon list:|r") + local AceCoreAddon = AceLibrary:HasInstance("AceCoreAddon-2.0") and AceLibrary("AceCoreAddon-2.0") + local count = GetNumAddOns() + for i = 1, count do + if IsAddOnLoadOnDemand(i) then + listNormalAddon(i) + end + end + end + }, + ace1 = { + desc = "List all addons using Ace1", + name = "Ace 1.x", + type = "execute", + func = function() + print("|cffffff7fAddon list:|r") + local count = GetNumAddOns() + for i = 1, count do + local dep1, dep2, dep3, dep4 = GetAddOnDependencies(i) + if dep1 == "Ace" or dep2 == "Ace" or dep3 == "Ace" or dep4 == "Ace" then + listNormalAddon(i) + end + end + end + }, + libs = { + desc = "List all libraries using AceLibrary", + name = "Libraries", + type = "execute", + func = function() + if type(AceLibrary) == "table" and type(AceLibrary.libs) == "table" then + print("|cffffff7fLibrary list:|r") + for name, data in pairs(AceLibrary.libs) do + local s + if data.minor then + s = " - " .. tostring(name) .. "." .. tostring(data.minor) + else + s = " - " .. tostring(name) + end + if AceLibrary(name).slashCommand then + s = s .. " |cffffff7f(" .. tostring(AceLibrary(name).slashCommand) .. "|cffffff7f)" + end + print(s) + end + end + end + }, + search = { + desc = "Search by name", + name = "Search", + type = "text", + usage = "", + input = true, + get = false, + set = function(...) + for i,v in ipairs(arg) do + arg[i] = string.lower(string.gsub(string.gsub(v, '%*', '.*'), '%%', '%%%%')) + end + local count = GetNumAddOns() + for i = 1, count do + local name = GetAddOnInfo(i) + local good = true + for _,v in ipairs(arg) do + if not string.find(string.lower(name), v) then + good = false + break + end + end + if good then + listNormalAddon(i) + end + end + end + } + }, + }, + enable = { + desc = "Enable addon", + name = "Enable", + type = "text", + usage = "", + get = false, + set = function(text) + local name,title,_,_,_,reason = GetAddOnInfo(text) + if reason == "MISSING" then + print(string.format("|cffffff7fAce2:|r AddOn %q does not exist", text)) + else + EnableAddOn(text) + print(string.format("|cffffff7fAce2:|r %s is now enabled", title or name)) + end + end, + }, + disable = { + desc = "Disable addon", + name = "Disable", + type = "text", + usage = "", + get = false, + set = function(text) + local name,title,_,_,_,reason = GetAddOnInfo(text) + if reason == "MISSING" then + print(string.format("|cffffff7fAce2:|r AddOn %q does not exist", text)) + else + DisableAddOn(text) + print(string.format("|cffffff7fAce2:|r %s is now disabled", title or name)) + end + end, + }, + load = { + desc = "Load addon", + name = "Load", + type = "text", + usage = "", + get = false, + set = function(text) + local name,title,_,_,loadable,reason = GetAddOnInfo(text) + if reason == "MISSING" then + print(string.format("|cffffff7fAce2:|r AddOn %q does not exist.", text)) + elseif not loadable then + print(string.format("|cffffff7fAce2:|r AddOn %q is not loadable. Reason: %s", text, reason)) + else + LoadAddOn(text) + print(string.format("|cffffff7fAce2:|r %s is now loaded", title or name)) + end + end + }, + info = { + desc = "Display information", + name = "Information", + type = "execute", + func = function() + local mem, threshold = gcinfo() + print(string.format(" - |cffffff7fMemory usage [|r%.3f MiB|cffffff7f]|r", mem / 1024)) + print(string.format(" - |cffffff7fThreshold [|r%.3f MiB|cffffff7f]|r", threshold / 1024)) + print(string.format(" - |cffffff7fFramerate [|r%.0f fps|cffffff7f]|r", GetFramerate())) + local bandwidthIn, bandwidthOut, latency = GetNetStats() + bandwidthIn, bandwidthOut = floor(bandwidthIn * 1024), floor(bandwidthOut * 1024) + print(string.format(" - |cffffff7fLatency [|r%.0f ms|cffffff7f]|r", latency)) + print(string.format(" - |cffffff7fBandwidth in [|r%.0f B/s|cffffff7f]|r", bandwidthIn)) + print(string.format(" - |cffffff7fBandwidth out [|r%.0f B/s|cffffff7f]|r", bandwidthOut)) + print(string.format(" - |cffffff7fTotal addons [|r%d|cffffff7f]|r", GetNumAddOns())) + print(string.format(" - |cffffff7fAce2 addons [|r%d|cffffff7f]|r", table.getn(self.addons))) + local ace = 0 + local enabled = 0 + local disabled = 0 + local lod = 0 + for i = 1, GetNumAddOns() do + local dep1, dep2, dep3, dep4 = GetAddOnDependencies(i) + if dep1 == "Ace" or dep2 == "Ace" or dep3 == "Ace" or dep4 == "Ace" then + ace = ace + 1 + end + if IsAddOnLoadOnDemand(i) then + lod = lod + 1 + end + local _,_,_,IsActive,loadable = GetAddOnInfo(i) + if not IsActive or not loadable then + disabled = disabled + 1 + else + enabled = enabled + 1 + end + end + print(string.format(" - |cffffff7fAce 1.x addons [|r%d|cffffff7f]|r", ace)) + print(string.format(" - |cffffff7fLoadOnDemand addons [|r%d|cffffff7f]|r", lod)) + print(string.format(" - |cffffff7fenabled addons [|r%d|cffffff7f]|r", enabled)) + print(string.format(" - |cffffff7fdisabled addons [|r%d|cffffff7f]|r", disabled)) + local libs = 0 + if type(AceLibrary) == "table" and type(AceLibrary.libs) == "table" then + for _ in pairs(AceLibrary.libs) do + libs = libs + 1 + end + end + print(string.format(" - |cffffff7fAceLibrary instances [|r%d|cffffff7f]|r", libs)) + end + } + } + }) + elseif major == "AceModuleCore-2.0" then + AceModuleCore = instance + end +end + +local function activate(self, oldLib, oldDeactivate) + AceAddon = self + + if oldLib then + self.playerLoginFired = oldLib.playerLoginFired or DEFAULT_CHAT_FRAME and DEFAULT_CHAT_FRAME.defaultLanguage + self.addonsToOnEnable = oldLib.addonsToOnEnable + self.addons = oldLib.addons + self.nextAddon = oldLib.nextAddon + self.addonsStarted = oldLib.addonsStarted + end + if not self.addons then + self.addons = {} + end + if not self.nextAddon then + self.nextAddon = {} + end + if not self.addonsStarted then + self.addonsStarted = {} + end + if oldDeactivate then + oldDeactivate(oldLib) + end +end + +AceLibrary:Register(AceAddon, MAJOR_VERSION, MINOR_VERSION, activate, nil, external) +AceAddon = AceLibrary(MAJOR_VERSION) diff --git a/libs/AceConsole-2.0/AceConsole-2.0.lua b/libs/AceConsole-2.0/AceConsole-2.0.lua new file mode 100755 index 0000000..f6d2028 --- /dev/null +++ b/libs/AceConsole-2.0/AceConsole-2.0.lua @@ -0,0 +1,1909 @@ +--[[ +Name: AceConsole-2.0 +Revision: $Rev: 17638 $ +Developed by: The Ace Development Team (http://www.wowace.com/index.php/The_Ace_Development_Team) +Inspired By: Ace 1.x by Turan (turan@gryphon.com) +Website: http://www.wowace.com/ +Documentation: http://www.wowace.com/index.php/AceConsole-2.0 +SVN: http://svn.wowace.com/root/trunk/Ace2/AceConsole-2.0 +Description: Mixin to allow for input/output capabilities. This uses the + AceOptions data table format to determine input. + http://wiki.wowace.com/index.php/AceOptions_data_table +Dependencies: AceLibrary, AceOO-2.0 +]] + +local MAJOR_VERSION = "AceConsole-2.0" +local MINOR_VERSION = "$Revision: 17638 $" + +if not AceLibrary then error(MAJOR_VERSION .. " requires AceLibrary.") end +if not AceLibrary:IsNewVersion(MAJOR_VERSION, MINOR_VERSION) then return end + +if loadstring("return function(...) return ... end") and AceLibrary:HasInstance(MAJOR_VERSION) then return end -- lua51 check +if not AceLibrary:HasInstance("AceOO-2.0") then error(MAJOR_VERSION .. " requires AceOO-2.0.") end + +local MAP_ONOFF, USAGE, IS_CURRENTLY_SET_TO, IS_NOW_SET_TO, IS_NOT_A_VALID_OPTION_FOR, IS_NOT_A_VALID_VALUE_FOR, NO_OPTIONS_AVAILABLE, OPTION_HANDLER_NOT_FOUND, OPTION_HANDLER_NOT_VALID, OPTION_IS_DISABLED +if GetLocale() == "deDE" then + MAP_ONOFF = { [false] = "|cffff0000Aus|r", [true] = "|cff00ff00An|r" } + USAGE = "Benutzung" + IS_CURRENTLY_SET_TO = "|cffffff7f%s|r steht momentan auf |cffffff7f[|r%s|cffffff7f]|r" + IS_NOW_SET_TO = "|cffffff7f%s|r ist nun auf |cffffff7f[|r%s|cffffff7f]|r gesetzt" + IS_NOT_A_VALID_OPTION_FOR = "[|cffffff7f%s|r] ist keine g\195\188ltige Option f\195\188r |cffffff7f%s|r" + IS_NOT_A_VALID_VALUE_FOR = "[|cffffff7f%s|r] ist kein g\195\188ltiger Wert f\195\188r |cffffff7f%s|r" + NO_OPTIONS_AVAILABLE = "Keine Optionen verfügbar" + OPTION_HANDLER_NOT_FOUND = "Optionen handler |cffffff7f%q|r nicht gefunden." + OPTION_HANDLER_NOT_VALID = "Optionen handler nicht g\195\188ltig." + OPTION_IS_DISABLED = "Option |cffffff7f%s|r deaktiviert." +elseif GetLocale() == "frFR" then + MAP_ONOFF = { [false] = "|cffff0000Inactif|r", [true] = "|cff00ff00Actif|r" } + USAGE = "Utilisation" + IS_CURRENTLY_SET_TO = "|cffffff7f%s|r est actuellement positionn\195\169 sur |cffffff7f[|r%s|cffffff7f]|r" + IS_NOW_SET_TO = "|cffffff7f%s|r est maintenant positionn\195\169 sur |cffffff7f[|r%s|cffffff7f]|r" + IS_NOT_A_VALID_OPTION_FOR = "[|cffffff7f%s|r] n'est pas une option valide pour |cffffff7f%s|r" + IS_NOT_A_VALID_VALUE_FOR = "[|cffffff7f%s|r] n'est pas une valeur valide pour |cffffff7f%s|r" + NO_OPTIONS_AVAILABLE = "Pas d'options disponibles" + OPTION_HANDLER_NOT_FOUND = "Le gestionnaire d'option |cffffff7f%q|r n'a pas \195\169t\195\169 trouv\195\169." + OPTION_HANDLER_NOT_VALID = "Le gestionnaire d'option n'est pas valide." + OPTION_IS_DISABLED = "L'option |cffffff7f%s|r est d\195\169sactiv\195\169e." +elseif GetLocale() == "koKR" then + MAP_ONOFF = { [false] = "|cffff0000끔|r", [true] = "|cff00ff00켬|r" } + USAGE = "사용법" + IS_CURRENTLY_SET_TO = "|cffffff7f%s|r|1은;는; 현재 상태는 |cffffff7f[|r%s|cffffff7f]|r|1으로;로; 설정되어 있습니다" + IS_NOW_SET_TO = "|cffffff7f%s|r|1을;를; |cffffff7f[|r%s|cffffff7f]|r 상태로 변경합니다" + IS_NOT_A_VALID_OPTION_FOR = "[|cffffff7f%s|r]|1은;는; |cffffff7f%s|r에서 사용불가능한 설정입니다" + IS_NOT_A_VALID_VALUE_FOR = "[|cffffff7f%s|r]|1은;는; |cffffff7f%s|r에서 사용불가능한 설정값입니다" + NO_OPTIONS_AVAILABLE = "가능한 설정이 없습니다" + OPTION_HANDLER_NOT_FOUND = "설정 조정값인 |cffffff7f%q|r|1을;를; 찾지 못했습니다." + OPTION_HANDLER_NOT_VALID = "설정 조정값이 올바르지 않습니다." + OPTION_IS_DISABLED = "|cffffff7f%s|r 설정은 사용할 수 없습니다." +elseif GetLocale() == "zhCN" then + MAP_ONOFF = { [false] = "|cffff0000\229\133\179\233\151\173|r", [true] = "|cff00ff00\229\188\128\229\144\175|r" } + USAGE = "\231\148\168\230\179\149" + IS_CURRENTLY_SET_TO = "|cffffff7f%s|r \229\189\147\229\137\141\232\162\171\232\174\190\231\189\174 |cffffff7f[|r%s|cffffff7f]|r" + IS_NOW_SET_TO = "|cffffff7f%s|r \231\142\176\229\156\168\232\162\171\232\174\190\231\189\174\228\184\186 |cffffff7f[|r%s|cffffff7f]|r" + IS_NOT_A_VALID_OPTION_FOR = "[|cffffff7f%s|r] \228\184\141\230\152\175\228\184\128\228\184\170\230\156\137\230\149\136\231\154\132\233\128\137\233\161\185 \228\184\186 |cffffff7f%s|r" + IS_NOT_A_VALID_VALUE_FOR = "[|cffffff7f%s|r] \228\184\141\230\152\175\228\184\128\228\184\170\230\156\137\230\149\136\229\128\188 \228\184\186 |cffffff7f%s|r" + NO_OPTIONS_AVAILABLE = "\230\178\161\230\156\137\233\128\137\233\161\185\229\143\175\231\148\168" + OPTION_HANDLER_NOT_FOUND = "\233\128\137\233\161\185\229\164\132\231\144\134\231\168\139\229\186\143 |cffffff7f%q|r \230\178\161\230\159\165\230\137\190." + OPTION_HANDLER_NOT_VALID = "\233\128\137\233\161\185\229\164\132\231\144\134\231\168\139\229\186\143 \230\151\160\230\149\136." + OPTION_IS_DISABLED = "\233\128\137\233\161\185 |cffffff7f%s|r \228\184\141\229\174\140\230\149\180." +elseif GetLocale() == "zhTW" then + MAP_ONOFF = { [false] = "|cffff0000關閉|r", [true] = "|cff00ff00開啟|r" } + USAGE = "用法" + IS_CURRENTLY_SET_TO = "|cffffff7f%s|r 目前的設定為 |cffffff7f[|r%s|cffffff7f]|r" + IS_NOW_SET_TO = "|cffffff7f%s|r 現在被設定為 |cffffff7f[|r%s|cffffff7f]|r" + IS_NOT_A_VALID_OPTION_FOR = "[|cffffff7f%s|r] 是一個不符合規定的選項,對 |cffffff7f%s|r" + IS_NOT_A_VALID_VALUE_FOR = "[|cffffff7f%s|r] 是一個不符合規定的數值,對 |cffffff7f%s|r" + NO_OPTIONS_AVAILABLE = "沒有可用的選項處理器。" + OPTION_HANDLER_NOT_FOUND = "找不到 |cffffff7f%q|r 選項處理器。" + OPTION_HANDLER_NOT_VALID = "選項處理器不符合規定。" + OPTION_IS_DISABLED = "|cffffff7f%s|r 已被停用。" +elseif GetLocale() == "ruRU" then + MAP_ONOFF = { [false] = "|cffff0000Выкл|r", [true] = "|cff00ff00Вкл|r" } + USAGE = "Использование" + IS_CURRENTLY_SET_TO = "|cffffff7f%s|r в настоящее время установлен на |cffffff7f[|r%s|cffffff7f]|r" + IS_NOW_SET_TO = "|cffffff7f%s|r теперь установлен |cffffff7f[|r%s|cffffff7f]|r" + IS_NOT_A_VALID_OPTION_FOR = "[|cffffff7f%s|r] - недействительная опция для |cffffff7f%s|r" + IS_NOT_A_VALID_VALUE_FOR = "[|cffffff7f%s|r] - недействительное значение для |cffffff7f%s|r" + NO_OPTIONS_AVAILABLE = "Нет доступных опций" + OPTION_HANDLER_NOT_FOUND = "Оператор опции |cffffff7f%q|r не найден." + OPTION_HANDLER_NOT_VALID = "Оператор опции недействителен." + OPTION_IS_DISABLED = "Опция |cffffff7f%s|r отключена." +else -- enUS + MAP_ONOFF = { [false] = "|cffff0000Off|r", [true] = "|cff00ff00On|r" } + USAGE = "Usage" + IS_CURRENTLY_SET_TO = "|cffffff7f%s|r is currently set to |cffffff7f[|r%s|cffffff7f]|r" + IS_NOW_SET_TO = "|cffffff7f%s|r is now set to |cffffff7f[|r%s|cffffff7f]|r" + IS_NOT_A_VALID_OPTION_FOR = "[|cffffff7f%s|r] is not a valid option for |cffffff7f%s|r" + IS_NOT_A_VALID_VALUE_FOR = "[|cffffff7f%s|r] is not a valid value for |cffffff7f%s|r" + NO_OPTIONS_AVAILABLE = "No options available" + OPTION_HANDLER_NOT_FOUND = "Option handler |cffffff7f%q|r not found." + OPTION_HANDLER_NOT_VALID = "Option handler not valid." + OPTION_IS_DISABLED = "Option |cffffff7f%s|r is disabled." +end + +local NONE = NONE or "None" + +local AceOO = AceLibrary("AceOO-2.0") +local AceEvent + +local AceConsole = AceOO.Mixin { "Print", "PrintComma", "CustomPrint", "RegisterChatCommand" } +local Dewdrop + +local _G = getfenv(0) + +local table_setn +do + local version = GetBuildInfo() + if string.find(version, "^2%.") then + -- 2.0.0 + table_setn = function() end + else + table_setn = table.setn + end +end + +local function print(text, name, r, g, b, frame, delay) + if not text or string.len(text) == 0 then + text = " " + end + if not name or name == AceConsole then + (frame or DEFAULT_CHAT_FRAME):AddMessage(text, r, g, b, nil, delay or 5) + else + (frame or DEFAULT_CHAT_FRAME):AddMessage("|cffffff78" .. tostring(name) .. ":|r " .. text, r, g, b, nil, delay or 5) + end +end + +local tmp +function AceConsole:CustomPrint(r, g, b, frame, delay, connector, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) + a1 = tostring(a1) + if string.find(a1, "%%") then + print(string.format(a1, tostring(a2), tostring(a3), tostring(a4), tostring(a5), tostring(a6), tostring(a7), tostring(a8), tostring(a9), tostring(a10), tostring(a11), tostring(a12), tostring(a13), tostring(a14), tostring(a15), tostring(a16), tostring(a17), tostring(a18), tostring(a19), tostring(a20)), self, r, g, b, frame or self.printFrame, delay) + else + if not tmp then + tmp = {} + end + tmp[1] = a1 + tmp[2] = a2 + tmp[3] = a3 + tmp[4] = a4 + tmp[5] = a5 + tmp[6] = a6 + tmp[7] = a7 + tmp[8] = a8 + tmp[9] = a9 + tmp[10] = a10 + tmp[11] = a11 + tmp[12] = a12 + tmp[13] = a13 + tmp[14] = a14 + tmp[15] = a15 + tmp[16] = a16 + tmp[17] = a17 + tmp[18] = a18 + tmp[19] = a19 + tmp[20] = a20 + local n = 20 + while tmp[n] == nil do + n = n - 1 + end + table_setn(tmp, n) + for k = 1, n do + tmp[k] = tostring(tmp[k]) + end + print(table.concat(tmp, connector or " "), self, r, g, b, frame or self.printFrame, delay) + for k,v in pairs(tmp) do + tmp[k] = nil + end + table_setn(tmp, 0) + end +end + +function AceConsole:Print(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) + return AceConsole.CustomPrint(self, nil, nil, nil, nil, nil, " ", a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) +end + +function AceConsole:PrintComma(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) + return AceConsole.CustomPrint(self, nil, nil, nil, nil, nil, ", ", a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) +end + +local work +local argwork + +local string_gfind = string.gmatch or string.gfind + +local function findTableLevel(self, options, chat, text, index, passTable) + if not index then + index = 1 + if work then + for k,v in pairs(work) do + work[k] = nil + end + table_setn(work, 0) + for k,v in pairs(argwork) do + argwork[k] = nil + end + table_setn(argwork, 0) + else + work = {} + argwork = {} + end + local len = string.len(text) + local count + repeat + text, count = string.gsub(text, "(|cff%x%x%x%x%x%x|Hitem:%d-:%d-:%d-:%d-|h%[[^%]]-) (.-%]|h|r)", "%1\001%2") + until count == 0 + text = string.gsub(text, "(%]|h|r)(|cff%x%x%x%x%x%x|Hitem:%d-:%d-:%d-:%d-|h%[)", "%1 %2") + for token in string_gfind(text, "([^%s]+)") do + local token = token + local num = tonumber(token) + if num then + token = num + else + token = string.gsub(token, "\001", " ") + end + table.insert(work, token) + end + end + + local path = chat + for i = 1, index - 1 do + path = path .. " " .. tostring(work[i]) + end + + if type(options.args) == "table" then + local disabled, hidden = options.disabled, options.cmdHidden or options.hidden + if hidden then + if type(hidden) == "function" then + hidden = hidden() + elseif type(hidden) == "string" then + local handler = options.handler or self + if type(handler[hidden]) ~= "function" then + AceConsole:error("%s: %s", tostring(handler), string.format(OPTION_HANDLER_NOT_FOUND, hidden)) + end + hidden = handler[hidden](handler) + end + end + if hidden then + disabled = true + elseif disabled then + if type(disabled) == "function" then + disabled = disabled() + elseif type(disabled) == "string" then + local handler = options.handler or self + if type(handler[disabled]) ~= "function" then + AceConsole:error("%s: %s", tostring(handler), string.format(OPTION_HANDLER_NOT_FOUND, disabled)) + end + disabled = handler[disabled](handler) + end + end + if not disabled then + local next = work[index] and string.lower(work[index]) + if next then + for k,v in pairs(options.args) do + local good = false + if string.lower(k) == next then + good = true + elseif type(v.aliases) == "table" then + for _,alias in ipairs(v.aliases) do + if string.lower(alias) == next then + good = true + break + end + end + elseif type(v.aliases) == "string" and string.lower(v.aliases) == next then + good = true + end + if good then + return findTableLevel(options.handler or self, v, chat, text, index + 1, options.pass and options or nil) + end + end + end + end + end + for i = index, table.getn(work) do + table.insert(argwork, work[i]) + end + return options, path, argwork, options.handler or self, passTable, passTable and work[index - 1] +end + +local function validateOptionsMethods(self, options, position) + if type(options) ~= "table" then + return "Options must be a table.", position + end + self = options.handler or self + if options.type == "execute" then + if options.func and type(options.func) ~= "string" and type(options.func) ~= "function" then + return "func must be a string or function", position + end + if options.func and type(options.func) == "string" and type(self[options.func]) ~= "function" then + return string.format("%q is not a proper function", options.func), position + end + else + if options.get then + if type(options.get) ~= "string" and type(options.get) ~= "function" then + return "get must be a string or function", position + end + if type(options.get) == "string" and type(self[options.get]) ~= "function" then + return string.format("%q is not a proper function", options.get), position + end + end + if options.set then + if type(options.set) ~= "string" and type(options.set) ~= "function" then + return "set must be a string or function", position + end + if type(options.set) == "string" and type(self[options.set]) ~= "function" then + return string.format("%q is not a proper function", options.set), position + end + end + if options.validate and type(options.validate) ~= "table" then + if type(options.validate) ~= "string" and type(options.validate) ~= "function" then + return "validate must be a string or function", position + end + if type(options.validate) == "string" and type(self[options.validate]) ~= "function" then + return string.format("%q is not a proper function", options.validate), position + end + end + end + if options.disabled and type(options.disabled) == "string" and type(self[options.disabled]) ~= "function" then + return string.format("%q is not a proper function", options.disabled), position + end + if options.cmdHidden and type(options.cmdHidden) == "string" and type(self[options.cmdHidden]) ~= "function" then + return string.format("%q is not a proper function", options.cmdHidden), position + end + if options.guiHidden and type(options.guiHidden) == "string" and type(self[options.guiHidden]) ~= "function" then + return string.format("%q is not a proper function", options.guiHidden), position + end + if options.hidden and type(options.hidden) == "string" and type(self[options.hidden]) ~= "function" then + return string.format("%q is not a proper function", options.hidden), position + end + if options.type == "group" and type(options.args) == "table" then + for k,v in pairs(options.args) do + if type(v) == "table" then + local newposition + if position then + newposition = position .. ".args." .. k + else + newposition = "args." .. k + end + local err, pos = validateOptionsMethods(self, v, newposition) + if err then + return err, pos + end + end + end + end +end + +local function validateOptions(options, position, baseOptions, fromPass) + if not baseOptions then + baseOptions = options + end + if type(options) ~= "table" then + return "Options must be a table.", position + end + local kind = options.type + if type(kind) ~= "string" then + return '"type" must be a string.', position + elseif kind ~= "group" and kind ~= "range" and kind ~= "text" and kind ~= "execute" and kind ~= "toggle" and kind ~= "color" and kind ~= "header" then + return '"type" must either be "range", "text", "group", "toggle", "execute", "color", or "header".', position + end + if options.aliases then + if type(options.aliases) ~= "table" and type(options.aliases) ~= "string" then + return '"alias" must be a table or string', position + end + end + if not fromPass then + if kind == "execute" then + if type(options.func) ~= "string" and type(options.func) ~= "function" then + return '"func" must be a string or function', position + end + elseif kind == "range" or kind == "text" or kind == "toggle" then + if type(options.set) ~= "string" and type(options.set) ~= "function" then + return '"set" must be a string or function', position + end + if kind == "text" and options.get == false then + elseif type(options.get) ~= "string" and type(options.get) ~= "function" then + return '"get" must be a string or function', position + end + elseif kind == "group" and options.pass then + if options.pass ~= true then + return '"pass" must be either nil, true, or false', position + end + if not options.func then + if type(options.set) ~= "string" and type(options.set) ~= "function" then + return '"set" must be a string or function', position + end + if type(options.get) ~= "string" and type(options.get) ~= "function" then + return '"get" must be a string or function', position + end + elseif type(options.func) ~= "string" and type(options.func) ~= "function" then + return '"func" must be a string or function', position + end + end + else + if kind == "group" then + return 'cannot have "type" = "group" as a subgroup of a passing group', position + end + end + if options ~= baseOptions then + if kind == "header" then + elseif type(options.desc) ~= "string" then + return '"desc" must be a string', position + elseif string.len(options.desc) == 0 then + return '"desc" cannot be a 0-length string', position + end + end + + if options ~= baseOptions or kind == "range" or kind == "text" or kind == "toggle" or kind == "color" then + if options.type == "header" and not options.cmdName and not options.name then + elseif options.cmdName then + if type(options.cmdName) ~= "string" then + return '"cmdName" must be a string or nil', position + elseif string.len(options.cmdName) == 0 then + return '"cmdName" cannot be a 0-length string', position + end + if type(options.guiName) ~= "string" then + if not options.guiNameIsMap then + return '"guiName" must be a string or nil', position + end + elseif string.len(options.guiName) == 0 then + return '"guiName" cannot be a 0-length string', position + end + else + if type(options.name) ~= "string" then + return '"name" must be a string', position + elseif string.len(options.name) == 0 then + return '"name" cannot be a 0-length string', position + end + end + end + if options.guiNameIsMap then + if type(options.guiNameIsMap) ~= "boolean" then + return '"guiNameIsMap" must be a boolean or nil', position + elseif options.type ~= "toggle" then + return 'if "guiNameIsMap" is true, then "type" must be set to \'toggle\'', position + elseif type(options.map) ~= "table" then + return '"map" must be a table', position + end + end + if options.message and type(options.message) ~= "string" then + return '"message" must be a string or nil', position + end + if options.error and type(options.error) ~= "string" then + return '"error" must be a string or nil', position + end + if options.current and type(options.current) ~= "string" then + return '"current" must be a string or nil', position + end + if options.order then + if type(options.order) ~= "number" or (-1 < options.order and options.order < 0.999) then + return '"order" must be a non-zero number or nil', position + end + end + if options.disabled then + if type(options.disabled) ~= "function" and type(options.disabled) ~= "string" and options.disabled ~= true then + return '"disabled" must be a function, string, or boolean', position + end + end + if options.cmdHidden then + if type(options.cmdHidden) ~= "function" and type(options.cmdHidden) ~= "string" and options.cmdHidden ~= true then + return '"cmdHidden" must be a function, string, or boolean', position + end + end + if options.guiHidden then + if type(options.guiHidden) ~= "function" and type(options.guiHidden) ~= "string" and options.guiHidden ~= true then + return '"guiHidden" must be a function, string, or boolean', position + end + end + if options.hidden then + if type(options.hidden) ~= "function" and type(options.hidden) ~= "string" and options.hidden ~= true then + return '"hidden" must be a function, string, or boolean', position + end + end + if kind == "text" then + if type(options.validate) == "table" then + local t = options.validate + local iTable = nil + for k,v in pairs(t) do + if type(k) == "number" then + if iTable == nil then + iTable = true + elseif not iTable then + return '"validate" must either have all keys be indexed numbers or strings', position + elseif k < 1 or k > table.getn(t) then + return '"validate" numeric keys must be indexed properly. >= 1 and <= table.getn', position + end + else + if iTable == nil then + iTable = false + elseif iTable then + return '"validate" must either have all keys be indexed numbers or strings', position + end + end + if type(v) ~= "string" then + return '"validate" values must all be strings', position + end + end + else + if type(options.usage) ~= "string" then + return '"usage" must be a string', position + elseif options.validate and type(options.validate) ~= "string" and type(options.validate) ~= "function" then + return '"validate" must be a string, function, or table', position + end + end + elseif kind == "range" then + if options.min or options.max then + if type(options.min) ~= "number" then + return '"min" must be a number', position + elseif type(options.max) ~= "number" then + return '"max" must be a number', position + elseif options.min >= options.max then + return '"min" must be less than "max"', position + end + end + if options.step then + if type(options.step) ~= "number" then + return '"step" must be a number', position + elseif options.step < 0 then + return '"step" must be nonnegative', position + end + end + if options.isPercent and options.isPercent ~= true then + return '"isPercent" must either be nil, true, or false', position + end + elseif kind == "toggle" then + if options.map then + if type(options.map) ~= "table" then + return '"map" must be a table', position + elseif type(options.map[true]) ~= "string" then + return '"map[true]" must be a string', position + elseif type(options.map[false]) ~= "string" then + return '"map[false]" must be a string', position + end + end + elseif kind == "color" then + if options.hasAlpha and options.hasAlpha ~= true then + return '"hasAlpha" must be nil, true, or false', position + end + elseif kind == "group" then + if options.pass and options.pass ~= true then + return '"pass" must be nil, true, or false', position + end + if type(options.args) ~= "table" then + return '"args" must be a table', position + end + for k,v in pairs(options.args) do + if type(k) ~= "string" then + return '"args" keys must be strings', position + elseif string.find(k, "%s") then + return string.format('"args" keys must not include spaces. %q is not appropriate.', k), position + elseif string.len(k) == 0 then + return '"args" keys must not be 0-length strings.', position + end + if type(v) ~= "table" then + return '"args" values must be tables', position and position .. "." .. k or k + end + local newposition + if position then + newposition = position .. ".args." .. k + else + newposition = "args." .. k + end + local err, pos = validateOptions(v, newposition, baseOptions, options.pass) + if err then + return err, pos + end + end + end +end + +local colorTable +local colorFunc +local colorCancelFunc + +local order + +local mysort_args +local mysort + +local function printUsage(self, handler, realOptions, options, path, args, quiet, filter) + if filter then + filter = "^" .. string.gsub(filter, "([%(%)%.%*%+%-%[%]%?%^%$%%])", "%%%1") + end + local hidden, disabled = options.cmdHidden or options.hidden, options.disabled + if hidden then + if type(hidden) == "function" then + hidden = hidden() + elseif type(hidden) == "string" then + if type(handler[hidden]) ~= "function" then + AceConsole:error("%s: %s", tostring(handler), string.format(OPTION_HANDLER_NOT_FOUND, hidden)) + end + hidden = handler[hidden](handler) + end + end + if hidden then + disabled = true + elseif disabled then + if type(disabled) == "function" then + disabled = disabled() + elseif type(disabled) == "string" then + if type(handler[disabled]) ~= "function" then + AceConsole:error("%s: %s", tostring(handler), string.format(OPTION_HANDLER_NOT_FOUND, disabled)) + end + disabled = handler[disabled](handler) + end + end + local kind = string.lower(options.type or "group") + if disabled then + print(string.format(OPTION_IS_DISABLED, path), realOptions.cmdName or realOptions.name or self) + elseif kind == "text" then + local var + if passTable then + if not passTable.get then + elseif type(passTable.get) == "function" then + var = passTable.get(passValue) + else + local handler = passTable.handler or handler + if type(handler[passTable.get]) ~= "function" then + AceConsole:error("%s: %s", tostring(handler), string.format(OPTION_HANDLER_NOT_FOUND, passTable.get)) + end + var = handler[passTable.get](handler, passValue) + end + else + if not options.get then + elseif type(options.get) == "function" then + var = options.get() + else + local handler = options.handler or handler + if type(handler[options.get]) ~= "function" then + AceConsole:error("%s: %s", tostring(handler), string.format(OPTION_HANDLER_NOT_FOUND, options.get)) + end + var = handler[options.get](handler) + end + end + + local usage + if type(options.validate) == "table" then + if filter then + if not order then + order = {} + end + for k,v in pairs(options.validate) do + if string.find(v, filter) then + table.insert(order, v) + end + end + usage = "{" .. table.concat(order, " || ") .. "}" + for k in pairs(order) do + order[k] = nil + end + table_setn(order, 0) + else + if not order then + order = {} + end + for k,v in pairs(options.validate) do + table.insert(order, v) + end + usage = "{" .. table.concat(order, " || ") .. "}" + for k in pairs(order) do + order[k] = nil + end + table_setn(order, 0) + end + var = options.validate[var] or var + else + usage = options.usage or "" + end + if not quiet then + print(string.format("|cffffff7f%s:|r %s %s", USAGE, path, usage), realOptions.cmdName or realOptions.name or self) + end + if (passTable and passTable.get) or options.get then + print(string.format(options.current or IS_CURRENTLY_SET_TO, tostring(options.cmdName or options.name), tostring(var or NONE))) + end + elseif kind == "range" then + local var + if passTable then + if type(passTable.get) == "function" then + var = passTable.get(passValue) + else + local handler = passTable.handler or handler + if type(handler[passTable.get]) ~= "function" then + AceConsole:error("%s: %s", tostring(handler), string.format(OPTION_HANDLER_NOT_FOUND, passTable.get)) + end + var = handler[passTable.get](handler, passValue) + end + else + if type(options.get) == "function" then + var = options.get() + else + local handler = options.handler or handler + if type(handler[options.get]) ~= "function" then + AceConsole:error("%s: %s", tostring(handler), string.format(OPTION_HANDLER_NOT_FOUND, options.get)) + end + var = handler[options.get](handler) + end + end + + local usage + local min = options.min or 0 + local max = options.max or 1 + if options.isPercent then + min, max = min * 100, max * 100 + var = tostring(var * 100) .. "%" + end + local bit = "-" + if min < 0 or max < 0 then + bit = " - " + end + usage = string.format("(%s%s%s)", min, bit, max) + if not quiet then + print(string.format("|cffffff7f%s:|r %s %s", USAGE, path, usage), realOptions.cmdName or realOptions.name or self) + end + print(string.format(options.current or IS_CURRENTLY_SET_TO, tostring(options.cmdName or options.name), tostring(var or NONE))) + elseif kind == "group" then + local usage + if next(options.args) then + if not order then + order = {} + end + for k,v in pairs(options.args) do + if v.type ~= "header" then + local hidden = v.cmdHidden or v.hidden + if hidden then + if type(hidden) == "function" then + hidden = hidden() + elseif type(hidden) == "string" then + local handler = v.handler or handler + if type(handler[hidden]) ~= "function" then + AceConsole:error("%s: %s", tostring(handler), string.format(OPTION_HANDLER_NOT_FOUND, hidden)) + end + hidden = handler[hidden](handler) + end + end + if not hidden then + if filter then + if string.find(k, filter) then + table.insert(order, k) + elseif type(v.aliases) == "table" then + for _,bit in ipairs(v.aliases) do + if string.find(v.aliases, filter) then + table.insert(order, k) + break + end + end + elseif type(v.aliases) == "string" then + if string.find(v.aliases, filter) then + table.insert(order, k) + end + end + else + table.insert(order, k) + end + end + end + end + if not mysort then + mysort = function(a, b) + local alpha, bravo = mysort_args[a], mysort_args[b] + local alpha_order = alpha and alpha.order or 100 + local bravo_order = bravo and bravo.order or 100 + if alpha_order == bravo_order then + return tostring(a) < tostring(b) + else + if alpha_order < 0 then + if bravo_order > 0 then + return false + end + else + if bravo_order < 0 then + return true + end + end + if alpha_order > 0 and bravo_order > 0 then + return tostring(a) < tostring(b) + end + return alpha_order < bravo_order + end + end + end + mysort_args = options.args + table.sort(order, mysort) + mysort_args = nil + if not quiet then + if options == realOptions then + if options.desc then + print(tostring(options.desc), realOptions.cmdName or realOptions.name or self) + print(string.format("|cffffff7f%s:|r %s %s", USAGE, path, "{" .. table.concat(order, " || ") .. "}")) + elseif self.description or self.notes then + print(tostring(self.description or self.notes), realOptions.cmdName or realOptions.name or self) + print(string.format("|cffffff7f%s:|r %s %s", USAGE, path, "{" .. table.concat(order, " || ") .. "}")) + else + print(string.format("|cffffff7f%s:|r %s %s", USAGE, path, "{" .. table.concat(order, " || ") .. "}"), realOptions.cmdName or realOptions.name or self) + end + else + if options.desc then + print(string.format("|cffffff7f%s:|r %s %s", USAGE, path, "{" .. table.concat(order, " || ") .. "}"), realOptions.cmdName or realOptions.name or self) + print(tostring(options.desc)) + else + print(string.format("|cffffff7f%s:|r %s %s", USAGE, path, "{" .. table.concat(order, " || ") .. "}"), realOptions.cmdName or realOptions.name or self) + end + end + end + for _,k in ipairs(order) do + local v = options.args[k] + if v then + local disabled = v.disabled + if disabled then + if type(disabled) == "function" then + disabled = disabled() + elseif type(disabled) == "string" then + local handler = v.handler or handler + if type(handler[disabled]) ~= "function" then + AceConsole:error("%s: %s", tostring(handler), string.format(OPTION_HANDLER_NOT_FOUND, disabled)) + end + disabled = handler[disabled](handler) + end + end + if type(v.aliases) == "table" then + k = k .. " || " .. table.concat(v.aliases, " || ") + elseif type(v.aliases) == "string" then + k = k .. " || " .. v.aliases + end + if v.get then + local a1,a2,a3,a4 + if type(v.get) == "function" then + if options.pass then + a1,a2,a3,a4 = v.get(k) + else + a1,a2,a3,a4 = v.get() + end + else + local handler = v.handler or handler + if type(handler[v.get]) ~= "function" then + AceConsole:error("%s: %s", tostring(handler), string.format(OPTION_HANDLER_NOT_FOUND, v.get)) + end + if options.pass then + a1,a2,a3,a4 = handler[v.get](handler, k) + else + a1,a2,a3,a4 = handler[v.get](handler) + end + end + if v.type == "color" then + if v.hasAlpha then + if not a1 or not a2 or not a3 or not a4 then + s = NONE + else + s = string.format("|c%02x%02x%02x%02x%02x%02x%02x%02x|r", a4*255, a1*255, a2*255, a3*255, a4*255, a1*255, a2*255, a3*255) + end + else + if not a1 or not a2 or not a3 then + s = NONE + else + s = string.format("|cff%02x%02x%02x%02x%02x%02x|r", a1*255, a2*255, a3*255, a1*255, a2*255, a3*255) + end + end + elseif v.type == "toggle" then + if v.map then + s = tostring(v.map[a1 and true or false] or NONE) + else + s = tostring(MAP_ONOFF[a1 and true or false] or NONE) + end + elseif v.type == "range" then + if v.isPercent then + s = tostring(a1 * 100) .. "%" + else + s = tostring(a1) + end + elseif v.type == "text" and type(v.validate) == "table" then + s = tostring(v.validate[a1] or a1) + else + s = tostring(a1 or NONE) + end + if disabled then + local s = string.gsub(s, "|cff%x%x%x%x%x%x(.-)|r", "%1") + local desc = string.gsub(v.desc or NONE, "|cff%x%x%x%x%x%x(.-)|r", "%1") + print(string.format("|cffcfcfcf - %s: [%s] %s|r", k, s, desc)) + else + print(string.format(" - |cffffff7f%s: [|r%s|cffffff7f]|r %s", k, s, v.desc or NONE)) + end + else + if disabled then + local desc = string.gsub(v.desc or NONE, "|cff%x%x%x%x%x%x(.-)|r", "%1") + print(string.format("|cffcfcfcf - %s: %s", k, desc)) + else + print(string.format(" - |cffffff7f%s:|r %s", k, v.desc or NONE)) + end + end + end + end + for k in pairs(order) do + order[k] = nil + end + table_setn(order, 0) + else + if options.desc then + desc = options.desc + print(string.format("|cffffff7f%s:|r %s", USAGE, path), realOptions.cmdName or realOptions.name or self) + print(tostring(options.desc)) + elseif options == realOptions and (self.description or self.notes) then + print(tostring(self.description or self.notes), realOptions.cmdName or realOptions.name or self) + print(string.format("|cffffff7f%s:|r %s", USAGE, path)) + else + print(string.format("|cffffff7f%s:|r %s", USAGE, path), realOptions.cmdName or realOptions.name or self) + end + print(NO_OPTIONS_AVAILABLE) + end + end +end + +local function handlerFunc(self, chat, msg, options) + if not msg then + msg = "" + else + msg = string.gsub(msg, "^%s*(.-)%s*$", "%1") + msg = string.gsub(msg, "%s+", " ") + end + + local realOptions = options + local options, path, args, handler, passTable, passValue = findTableLevel(self, options, chat, msg) + + local hidden, disabled = options.cmdHidden or options.hidden, options.disabled + if hidden then + if type(hidden) == "function" then + hidden = hidden() + elseif type(hidden) == "string" then + if type(handler[hidden]) ~= "function" then + AceConsole:error("%s: %s", tostring(handler), string.format(OPTION_HANDLER_NOT_FOUND, hidden)) + end + hidden = handler[hidden](handler) + end + end + if hidden then + disabled = true + elseif disabled then + if type(disabled) == "function" then + disabled = disabled() + elseif type(disabled) == "string" then + if type(handler[disabled]) ~= "function" then + AceConsole:error("%s: %s", tostring(handler), string.format(OPTION_HANDLER_NOT_FOUND, disabled)) + end + disabled = handler[disabled](handler) + end + end + local _G_this = this + local kind = string.lower(options.type or "group") + if disabled then + print(string.format(OPTION_IS_DISABLED, path), realOptions.cmdName or realOptions.name or self) + elseif kind == "text" then + if table.getn(args) > 0 then + if (type(options.validate) == "table" and table.getn(args) > 1) or (type(options.validate) ~= "table" and not options.input) then + local arg = table.concat(args, " ") + for k,v in pairs(args) do + args[k] = nil + end + table_setn(args, 0) + table.insert(args, arg) + end + if options.validate then + local good + if type(options.validate) == "function" then + good = options.validate(unpack(args)) + elseif type(options.validate) == "table" then + local arg = args[1] + arg = string.lower(tostring(arg)) + for k,v in pairs(options.validate) do + if string.lower(v) == arg then + args[1] = type(k) == "string" and k or v + good = true + break + end + end + if not good and type((next(options.validate))) == "string" then + for k,v in pairs(options.validate) do + if type(k) == "string" and string.lower(k) == arg then + args[1] = k + good = true + break + end + end + end + else + if type(handler[options.validate]) ~= "function" then + AceConsole:error("%s: %s", tostring(handler), string.format(OPTION_HANDLER_NOT_FOUND, options.validate)) + end + good = handler[options.validate](handler, unpack(args)) + end + if not good then + local usage + if type(options.validate) == "table" then + if not order then + order = {} + end + for k,v in pairs(options.validate) do + table.insert(order, v) + end + usage = "{" .. table.concat(order, " || ") .. "}" + for k in pairs(order) do + order[k] = nil + end + table_setn(order, 0) + else + usage = options.usage or "" + end + print(string.format(options.error or IS_NOT_A_VALID_OPTION_FOR, tostring(table.concat(args, " ")), path), realOptions.cmdName or realOptions.name or self) + print(string.format("|cffffff7f%s:|r %s %s", USAGE, path, usage)) + return + end + end + + local var + if passTable then + if not passTable.get then + elseif type(passTable.get) == "function" then + var = passTable.get(passValue) + else + if type(handler[passTable.get]) ~= "function" then + AceConsole:error("%s: %s", tostring(handler), string.format(OPTION_HANDLER_NOT_FOUND, passTable.get)) + end + var = handler[passTable.get](handler, passValue) + end + else + if not options.get then + elseif type(options.get) == "function" then + var = options.get() + else + if type(handler[options.get]) ~= "function" then + AceConsole:error("%s: %s", tostring(handler), string.format(OPTION_HANDLER_NOT_FOUND, options.get)) + end + var = handler[options.get](handler) + end + end + + if var ~= args[1] then + if passTable then + if type(passTable.set) == "function" then + passTable.set(passValue, unpack(args)) + else + if type(handler[passTable.set]) ~= "function" then + AceConsole:error("%s: %s", tostring(handler), string.format(OPTION_HANDLER_NOT_FOUND, passTable.set)) + end + handler[passTable.set](handler, passTable.set, unpack(args)) + end + else + if type(options.set) == "function" then + options.set(unpack(args)) + else + if type(handler[options.set]) ~= "function" then + AceConsole:error("%s: %s", tostring(handler), string.format(OPTION_HANDLER_NOT_FOUND, options.set)) + end + handler[options.set](handler, unpack(args)) + end + end + end + end + + if table.getn(args) > 0 then + local var + if passTable then + if not passTable.get then + elseif type(passTable.get) == "function" then + var = passTable.get(passValue) + else + if type(handler[passTable.get]) ~= "function" then + AceConsole:error("%s: %s", tostring(handler), string.format(OPTION_HANDLER_NOT_FOUND, passTable.get)) + end + var = handler[passTable.get](handler, passValue) + end + else + if not options.get then + elseif type(options.get) == "function" then + var = options.get() + else + if type(handler[options.get]) ~= "function" then + AceConsole:error("%s: %s", tostring(handler), string.format(OPTION_HANDLER_NOT_FOUND, options.get)) + end + var = handler[options.get](handler) + end + end + if type(options.validate) == "table" then + var = options.validate[var] or var + end + if (passTable and passTable.get) or options.get then + print(string.format(options.message or IS_NOW_SET_TO, tostring(options.cmdName or options.name), tostring(var or NONE)), realOptions.cmdName or realOptions.name or self) + end + if var == args[1] then + return + end + else + printUsage(self, handler, realOptions, options, path, args) + return + end + elseif kind == "execute" then + if passTable then + if type(passFunc) == "function" then + set(passValue) + else + if type(handler[passFunc]) ~= "function" then + AceConsole:error("%s: %s", tostring(handler), string.format(OPTION_HANDLER_NOT_FOUND, passFunc)) + end + handler[passFunc](handler, passValue) + end + else + local ret, msg + if type(options.func) == "function" then + options.func() + else + local handler = options.handler or self + if type(handler[options.func]) ~= "function" then + AceConsole:error("%s: %s", tostring(handler), string.format(OPTION_HANDLER_NOT_FOUND, options.func)) + end + handler[options.func](handler) + end + end + elseif kind == "toggle" then + local var + if passTable then + if type(passTable.get) == "function" then + var = passTable.get(passValue) + else + if type(handler[passTable.get]) ~= "function" then + AceConsole:error("%s: %s", tostring(handler), string.format(OPTION_HANDLER_NOT_FOUND, passTable.get)) + end + var = handler[passTable.get](handler, passValue) + end + if type(passTable.set) == "function" then + passTable.set(passValue, not var) + else + if type(handler[passTable.set]) ~= "function" then + AceConsole:error("%s: %s", tostring(handler), string.format(OPTION_HANDLER_NOT_FOUND, passTable.set)) + end + handler[passTable.set](handler, passValue, not var) + end + if type(passTable.get) == "function" then + var = passTable.get(passValue) + else + var = handler[passTable.get](handler, passValue) + end + else + local handler = options.handler or self + if type(options.get) == "function" then + var = options.get() + else + if type(handler[options.get]) ~= "function" then + AceConsole:error("%s: %s", tostring(handler), string.format(OPTION_HANDLER_NOT_FOUND, options.get)) + end + var = handler[options.get](handler) + end + if type(options.set) == "function" then + options.set(not var) + else + if type(handler[options.set]) ~= "function" then + AceConsole:error("%s: %s", tostring(handler), string.format(OPTION_HANDLER_NOT_FOUND, options.set)) + end + handler[options.set](handler, not var) + end + if type(options.get) == "function" then + var = options.get() + else + var = handler[options.get](handler) + end + end + + print(string.format(options.message or IS_NOW_SET_TO, tostring(options.cmdName or options.name), (options.map or MAP_ONOFF)[var and true or false] or NONE), realOptions.cmdName or realOptions.name or self) + elseif kind == "range" then + local arg + if table.getn(args) <= 1 then + arg = args[1] + else + arg = table.concat(args, " ") + end + + if arg then + local min = options.min or 0 + local max = options.max or 1 + local good = false + if type(arg) == "number" then + if options.isPercent then + arg = arg / 100 + end + + if arg >= min and arg <= max then + good = true + end + + if good and type(options.step) == "number" and options.step > 0 then + local step = options.step + arg = math.floor((arg - min) / step + 0.5) * step + min + if arg > max then + arg = max + elseif arg < min then + arg = min + end + end + end + if not good then + local usage + local min = options.min or 0 + local max = options.max or 1 + if options.isPercent then + min, max = min * 100, max * 100 + end + local bit = "-" + if min < 0 or max < 0 then + bit = " - " + end + usage = string.format("(%s%s%s)", min, bit, max) + print(string.format(options.error or IS_NOT_A_VALID_VALUE_FOR, tostring(arg), path), realOptions.cmdName or realOptions.name or self) + print(string.format("|cffffff7f%s:|r %s %s", USAGE, path, usage)) + return + end + + local var + if passTable then + if type(passTable.get) == "function" then + var = passTable.get(passValue) + else + if type(handler[passTable.get]) ~= "function" then + AceConsole:error("%s: %s", tostring(handler), string.format(OPTION_HANDLER_NOT_FOUND, passTable.get)) + end + var = handler[passTable.get](handler, passValue) + end + else + if type(options.get) == "function" then + var = options.get() + else + local handler = options.handler or self + if type(handler[options.get]) ~= "function" then + AceConsole:error("%s: %s", tostring(handler), string.format(OPTION_HANDLER_NOT_FOUND, options.get)) + end + var = handler[options.get](handler) + end + end + + if var ~= arg then + if passTable then + if type(passTable.set) == "function" then + passTable.set(passValue, arg) + else + if type(handler[passTable.set]) ~= "function" then + AceConsole:error("%s: %s", tostring(handler), string.format(OPTION_HANDLER_NOT_FOUND, passTable.set)) + end + handler[passTable.set](handler, passValue, arg) + end + else + if type(options.set) == "function" then + options.set(arg) + else + local handler = options.handler or self + if type(handler[options.set]) ~= "function" then + AceConsole:error("%s: %s", tostring(handler), string.format(OPTION_HANDLER_NOT_FOUND, options.set)) + end + handler[options.set](handler, arg) + end + end + end + end + + if arg then + local var + if passTable then + if type(passTable.get) == "function" then + var = passTable.get(passValue) + else + if type(handler[passTable.get]) ~= "function" then + AceConsole:error("%s: %s", tostring(handler), string.format(OPTION_HANDLER_NOT_FOUND, passTable.get)) + end + var = handler[passTable.get](handler, passValue) + end + else + if type(options.get) == "function" then + var = options.get() + else + local handler = options.handler or self + if type(handler[options.get]) ~= "function" then + AceConsole:error("%s: %s", tostring(handler), string.format(OPTION_HANDLER_NOT_FOUND, options.get)) + end + var = handler[options.get](handler) + end + end + + if var and options.isPercent then + var = tostring(var * 100) .. "%" + end + print(string.format(options.message or IS_NOW_SET_TO, tostring(options.cmdName or options.name), tostring(var or NONE)), realOptions.cmdName or realOptions.name or self) + if var == arg then + return + end + else + printUsage(self, handler, realOptions, options, path, args) + return + end + elseif kind == "color" then + if table.getn(args) > 0 then + local r,g,b,a + if table.getn(args) == 1 then + local arg = tostring(args[1]) + if options.hasAlpha then + if string.len(arg) == 8 and string.find(arg, "^%x*$") then + r,g,b,a = tonumber(string.sub(arg, 1, 2), 16) / 255, tonumber(string.sub(arg, 3, 4), 16) / 255, tonumber(string.sub(arg, 5, 6), 16) / 255, tonumber(string.sub(arg, 7, 8), 16) / 255 + end + else + if string.len(arg) == 6 and string.find(arg, "^%x*$") then + r,g,b = tonumber(string.sub(arg, 1, 2), 16) / 255, tonumber(string.sub(arg, 3, 4), 16) / 255, tonumber(string.sub(arg, 5, 6), 16) / 255 + end + end + elseif table.getn(args) == 4 and options.hasAlpha then + local a1,a2,a3,a4 = args[1], args[2], args[3], args[4] + if type(a1) == "number" and type(a2) == "number" and type(a3) == "number" and type(a4) == "number" and a1 <= 1 and a2 <= 1 and a3 <= 1 and a4 <= 1 then + r,g,b,a = a1,a2,a3,a4 + elseif (type(a1) == "number" or string.len(a1) == 2) and string.find(a1, "^%x*$") and (type(a2) == "number" or string.len(a2) == 2) and string.find(a2, "^%x*$") and (type(a3) == "number" or string.len(a3) == 2) and string.find(a3, "^%x*$") and (type(a4) == "number" or string.len(a4) == 2) and string.find(a4, "^%x*$") then + r,g,b,a = tonumber(a1, 16) / 255, tonumber(a2, 16) / 255, tonumber(a3, 16) / 255, tonumber(a4, 16) / 255 + end + elseif table.getn(args) == 3 and not options.hasAlpha then + local a1,a2,a3 = args[1], args[2], args[3] + if type(a1) == "number" and type(a2) == "number" and type(a3) == "number" and a1 <= 1 and a2 <= 1 and a3 <= 1 then + r,g,b = a1,a2,a3 + elseif (type(a1) == "number" or string.len(a1) == 2) and string.find(a1, "^%x*$") and (type(a2) == "number" or string.len(a2) == 2) and string.find(a2, "^%x*$") and (type(a3) == "number" or string.len(a3) == 2) and string.find(a3, "^%x*$") then + r,g,b = tonumber(a1, 16) / 255, tonumber(a2, 16) / 255, tonumber(a3, 16) / 255 + end + end + if not r then + print(string.format(options.error or IS_NOT_A_VALID_OPTION_FOR, table.concat(args, ' '), path), realOptions.cmdName or realOptions.name or self) + print(string.format("|cffffff7f%s:|r %s {0-1} {0-1} {0-1}%s", USAGE, path, options.hasAlpha and " {0-1}" or "")) + return + end + if passTable then + if type(passTable.set) == "function" then + passTable.set(passValue, r,g,b,a) + else + if type(handler[passTable.set]) ~= "function" then + AceConsole:error("%s: %s", tostring(handler), string.format(OPTION_HANDLER_NOT_FOUND, passTable.set)) + end + handler[passTable.set](handler, passValue, r,g,b,a) + end + else + if type(options.set) == "function" then + options.set(r,g,b,a) + else + if type(handler[options.set]) ~= "function" then + AceConsole:error("%s: %s", tostring(handler), string.format(OPTION_HANDLER_NOT_FOUND, options.set)) + end + handler[options.set](handler, r,g,b,a) + end + end + + local r,g,b,a + if passTable then + if type(passTable.get) == "function" then + r,g,b,a = passTable.get(passValue) + else + if type(handler[passTable.get]) ~= "function" then + AceConsole:error("%s: %s", tostring(handler), string.format(OPTION_HANDLER_NOT_FOUND, passTable.get)) + end + r,g,b,a = handler[passTable.get](handler, passValue) + end + else + if type(options.get) == "function" then + r,g,b,a = options.get() + else + if type(handler[options.get]) ~= "function" then + AceConsole:error("%s: %s", tostring(handler), string.format(OPTION_HANDLER_NOT_FOUND, options.get)) + end + r,g,b,a = handler[options.get](handler) + end + end + + local s + if type(r) == "number" and type(g) == "number" and type(b) == "number" then + if options.hasAlpha and type(a) == "number" then + s = string.format("|c%02x%02x%02x%02x%02x%02x%02x%02x|r", a*255, r*255, g*255, b*255, r*255, g*255, b*255, a*255) + else + s = string.format("|cff%02x%02x%02x%02x%02x%02x|r", r*255, g*255, b*255, r*255, g*255, b*255) + end + else + s = NONE + end + print(string.format(options.message or IS_NOW_SET_TO, tostring(options.cmdName or options.name), s), realOptions.cmdName or realOptions.name or self) + else + local r,g,b,a + if passTable then + if type(passTable.get) == "function" then + r,g,b,a = passTable.get(passValue) + else + if type(handler[passTable.get]) ~= "function" then + AceConsole:error("%s: %s", tostring(handler), string.format(OPTION_HANDLER_NOT_FOUND, passTable.get)) + end + r,g,b,a = handler[passTable.get](handler, passValue) + end + else + if type(options.get) == "function" then + r,g,b,a = options.get() + else + if type(handler[options.get]) ~= "function" then + AceConsole:error("%s: %s", tostring(handler), string.format(OPTION_HANDLER_NOT_FOUND, options.get)) + end + r,g,b,a = handler[options.get](handler) + end + end + + if not colorTable then + colorTable = {} + local t = colorTable + + if ColorPickerOkayButton then + local ColorPickerOkayButton_OnClick = ColorPickerOkayButton:GetScript("OnClick") + ColorPickerOkayButton:SetScript("OnClick", function() + if ColorPickerOkayButton_OnClick then + ColorPickerOkayButton_OnClick() + end + if t.active then + ColorPickerFrame.cancelFunc = nil + ColorPickerFrame.func = nil + ColorPickerFrame.opacityFunc = nil + local r,g,b,a + if t.passValue then + if type(t.get) == "function" then + r,g,b,a = t.get(t.passValue) + else + if type(t.handler[t.get]) ~= "function" then + AceConsole:error("%s: %s", tostring(handler), string.format(OPTION_HANDLER_NOT_FOUND, t.get)) + end + r,g,b,a = t.handler[t.get](t.handler, t.passValue) + end + else + if type(t.get) == "function" then + r,g,b,a = t.get() + else + if type(t.handler[t.get]) ~= "function" then + AceConsole:error("%s: %s", tostring(handler), string.format(OPTION_HANDLER_NOT_FOUND, t.get)) + end + r,g,b,a = t.handler[t.get](t.handler) + end + end + if r ~= t.r or g ~= t.g or b ~= t.b or (t.hasAlpha and a ~= t.a) then + local s + if type(r) == "number" and type(g) == "number" and type(b) == "number" then + if t.hasAlpha and type(a) == "number" then + s = string.format("|c%02x%02x%02x%02x%02x%02x%02x%02x|r", a*255, r*255, g*255, b*255, r*255, g*255, b*255, a*255) + else + s = string.format("|cff%02x%02x%02x%02x%02x%02x|r", r*255, g*255, b*255, r*255, g*255, b*255) + end + else + s = NONE + end + print(string.format(t.message, tostring(t.name), s), t.realOptions.cmdName or t.realOptions.name or self) + end + for k,v in pairs(t) do + t[k] = nil + end + end + end) + end + else + for k,v in pairs(colorTable) do + colorTable[k] = nil + end + end + + if type(r) ~= "number" or type(g) ~= "number" or type(b) ~= "number" then + r,g,b = 1, 1, 1 + end + if type(a) ~= "number" then + a = 1 + end + local t = colorTable + t.r = r + t.g = g + t.b = b + if hasAlpha then + t.a = a + end + t.realOptions = realOptions + t.hasAlpha = options.hasAlpha + t.handler = handler + t.set = passTable and passTable.set or options.set + t.get = passTable and passTable.get or options.get + t.name = options.cmdName or options.name + t.message = options.message or IS_NOW_SET_TO + t.passValue = passValue + t.active = true + + if not colorFunc then + colorFunc = function() + local r,g,b = ColorPickerFrame:GetColorRGB() + if t.hasAlpha then + local a = 1 - OpacitySliderFrame:GetValue() + if type(t.set) == "function" then + if t.passValue then + t.set(t.passValue, r,g,b,a) + else + t.set(r,g,b,a) + end + else + if type(t.handler[t.set]) ~= "function" then + AceConsole:error("%s: %s", tostring(handler), string.format(OPTION_HANDLER_NOT_FOUND, t.set)) + end + if t.passValue then + t.handler[t.set](t.handler, t.passValue, r,g,b,a) + else + t.handler[t.set](t.handler, r,g,b,a) + end + end + else + if type(t.set) == "function" then + if t.passValue then + t.set(t.passValue, r,g,b) + else + t.set(r,g,b) + end + else + if type(t.handler[t.set]) ~= "function" then + AceConsole:error("%s: %s", tostring(handler), string.format(OPTION_HANDLER_NOT_FOUND, t.set)) + end + if t.passValue then + t.handler[t.set](t.handler, t.passValue, r,g,b) + else + t.handler[t.set](t.handler, r,g,b) + end + end + end + end + end + + ColorPickerFrame.func = colorFunc + ColorPickerFrame.hasOpacity = options.hasAlpha + if options.hasAlpha then + ColorPickerFrame.opacityFunc = ColorPickerFrame.func + ColorPickerFrame.opacity = 1 - a + end + ColorPickerFrame:SetColorRGB(r,g,b) + + if not colorCancelFunc then + colorCancelFunc = function() + if t.hasAlpha then + if type(t.set) == "function" then + if t.passValue then + t.set(t.passValue, t.r,t.g,t.b,t.a) + else + t.set(t.r,t.g,t.b,t.a) + end + else + if type(t.handler[t.get]) ~= "function" then + AceConsole:error("%s: %s", tostring(handler), string.format(OPTION_HANDLER_NOT_FOUND, t.get)) + end + if t.passValue then + t.handler[t.set](t.handler, t.passValue, t.r,t.g,t.b,t.a) + else + t.handler[t.set](t.handler, t.r,t.g,t.b,t.a) + end + end + else + if type(t.set) == "function" then + if t.passValue then + t.set(t.passValue, t.r,t.g,t.b) + else + t.set(t.r,t.g,t.b) + end + else + if type(t.handler[t.set]) ~= "function" then + AceConsole:error("%s: %s", tostring(handler), string.format(OPTION_HANDLER_NOT_FOUND, t.set)) + end + if t.passValue then + t.handler[t.set](t.handler, t.passValue, t.r,t.g,t.b) + else + t.handler[t.set](t.handler, t.r,t.g,t.b) + end + end + end + for k,v in pairs(t) do + t[k] = nil + end + ColorPickerFrame.cancelFunc = nil + ColorPickerFrame.func = nil + ColorPickerFrame.opacityFunc = nil + end + end + + ColorPickerFrame.cancelFunc = colorCancelFunc + + ShowUIPanel(ColorPickerFrame) + end + return + elseif kind == "group" then + if table.getn(args) == 0 then + printUsage(self, handler, realOptions, options, path, args) + else + -- invalid argument + print(string.format(options.error or IS_NOT_A_VALID_OPTION_FOR, args[1], path), realOptions.cmdName or realOptions.name or self) + end + return + end + this = _G_this + if Dewdrop then + Dewdrop:Refresh(1) + Dewdrop:Refresh(2) + Dewdrop:Refresh(3) + Dewdrop:Refresh(4) + Dewdrop:Refresh(5) + end +end + +local external +function AceConsole:RegisterChatCommand(slashCommands, options, name) + if type(slashCommands) ~= "table" and slashCommands ~= false then + AceConsole:error("Bad argument #2 to `RegisterChatCommand' (expected table, got %s)", type(slashCommands)) + end + if not slashCommands and type(name) ~= "string" then + AceConsole:error("Bad argument #4 to `RegisterChatCommand' (expected string, got %s)", type(name)) + end + if type(options) ~= "table" and type(options) ~= "function" and options ~= nil then + AceConsole:error("Bad argument #3 to `RegisterChatCommand' (expected table, function, or nil, got %s)", type(options)) + end + if name then + if type(name) ~= "string" then + AceConsole:error("Bad argument #4 to `RegisterChatCommand' (expected string or nil, got %s)", type(name)) + elseif not string.find(name, "^%w+$") or string.upper(name) ~= name or string.len(name) == 0 then + AceConsole:error("Argument #4 must be an uppercase, letters-only string with at least 1 character") + end + end + if slashCommands then + if table.getn(slashCommands) == 0 then + AceConsole:error("Argument #2 to `RegisterChatCommand' must include at least one string") + end + + for k,v in pairs(slashCommands) do + if type(k) ~= "number" then + AceConsole:error("All keys in argument #2 to `RegisterChatCommand' must be numbers") + end + if type(v) ~= "string" then + AceConsole:error("All values in argument #2 to `RegisterChatCommand' must be strings") + elseif not string.find(v, "^/[A-Za-z][A-Za-z0-9_]*$") then + AceConsole:error("All values in argument #2 to `RegisterChatCommand' must be in the form of \"/word\"") + end + end + end + + if not options then + options = { + type = 'group', + args = {}, + handler = self + } + end + + if type(options) == "table" then + local err, position = validateOptions(options) + if err then + if position then + AceConsole:error(position .. ": " .. err) + else + AceConsole:error(err) + end + end + + if not options.handler then + options.handler = self + end + + if options.handler == self and string.lower(options.type) == "group" and self.class then + AceConsole:InjectAceOptionsTable(self, options) + end + end + + local chat + if slashCommands then + chat = slashCommands[1] + else + chat = _G["SLASH_"..name..1] + end + + local handler + if type(options) == "function" then + handler = options + for k,v in pairs(_G) do + if handler == v then + local k = k + handler = function(msg) + return _G[k](msg) + end + end + end + else + function handler(msg) + handlerFunc(self, chat, msg, options) + end + end + + if not _G.SlashCmdList then + _G.SlashCmdList = {} + end + + if not name then + repeat + name = string.char(math.random(26) + string.byte('A') - 1) .. string.char(math.random(26) + string.byte('A') - 1) .. string.char(math.random(26) + string.byte('A') - 1) .. string.char(math.random(26) + string.byte('A') - 1) .. string.char(math.random(26) + string.byte('A') - 1) .. string.char(math.random(26) + string.byte('A') - 1) .. string.char(math.random(26) + string.byte('A') - 1) .. string.char(math.random(26) + string.byte('A') - 1) + until not _G.SlashCmdList[name] + end + + if slashCommands then + if _G.SlashCmdList[name] then + local i = 0 + while true do + i = i + 1 + if _G["SLASH_"..name..i] then + _G["SLASH_"..name..i] = nil + else + break + end + end + end + + local i = 0 + for _,command in ipairs(slashCommands) do + i = i + 1 + _G["SLASH_"..name..i] = command + if string.lower(command) ~= command then + i = i + 1 + _G["SLASH_"..name..i] = string.lower(command) + end + end + end + _G.SlashCmdList[name] = handler + if self ~= AceConsole and self.slashCommand == nil then + self.slashCommand = chat + end + + if not AceEvent and AceLibrary:HasInstance("AceEvent-2.0") then + external(AceConsole, "AceEvent-2.0", AceLibrary("AceEvent-2.0")) + end + if AceEvent then + if not AceConsole.nextAddon then + AceConsole.nextAddon = {} + end + if type(options) == "table" then + AceConsole.nextAddon[self] = options + if not self.playerLogin then + AceConsole:RegisterEvent("PLAYER_LOGIN", "PLAYER_LOGIN", true) + end + end + end + + AceConsole.registry[name] = options +end + +function AceConsole:InjectAceOptionsTable(handler, options) + self:argCheck(handler, 2, "table") + self:argCheck(options, 3, "table") + if string.lower(options.type) ~= "group" then + self:error('Cannot inject into options table argument #3 if its type is not "group"') + end + if options.handler ~= nil and options.handler ~= handler then + self:error("Cannot inject into options table argument #3 if it has a different handler than argument #2") + end + options.handler = handler + local class = handler.class + if not class then + self:error("Cannot retrieve AceOptions tables from a non-object argument #2") + end + while class and class ~= AceOO.Class do + if type(class.GetAceOptionsDataTable) == "function" then + local t = class:GetAceOptionsDataTable(handler) + for k,v in pairs(t) do + if type(options.args) ~= "table" then + options.args = {} + end + if options.args[k] == nil then + options.args[k] = v + end + end + end + local mixins = class.mixins + if mixins then + for mixin in pairs(mixins) do + if type(mixin.GetAceOptionsDataTable) == "function" then + local t = mixin:GetAceOptionsDataTable(handler) + for k,v in pairs(t) do + if type(options.args) ~= "table" then + options.args = {} + end + if options.args[k] == nil then + options.args[k] = v + end + end + end + end + end + class = class.super + end + return options +end + +function AceConsole:PLAYER_LOGIN() + self.playerLogin = true + for addon, options in pairs(self.nextAddon) do + local err, position = validateOptionsMethods(addon, options) + if err then + if position then + error(tostring(addon) .. ": AceConsole: " .. position .. ": " .. err) + else + error(tostring(addon) .. ": AceConsole: " .. err) + end + end + self.nextAddon[addon] = nil + end + + self:RegisterChatCommand({ "/reload", "/rl", "/reloadui" }, ReloadUI, "RELOAD") + + local version = GetBuildInfo() + if string.find(version, "^2%.") then + -- 2.0.0 + self:RegisterChatCommand({ "/print" }, function(text) + RunScript("local function func(...) local arg = {...}; for k = 1,select('#', ...) do arg[k] = tostring(arg[k]) end DEFAULT_CHAT_FRAME:AddMessage(table.concat(arg, ' ')) end func(" .. text .. ")") + end, "PRINT") + else + self:RegisterChatCommand({ "/print" }, function(text) + RunScript("local function func(...) for k = 1,table.getn(arg) do arg[k] = tostring(arg[k]) end DEFAULT_CHAT_FRAME:AddMessage(table.concat(arg, ' ')) end func(" .. text .. ")") + end, "PRINT") + end +end + +function AceConsole:TabCompleteInfo(cmdpath) + local _, _, cmd = string.find(cmdpath, "(/%S+)") + if not cmd then + return + end + local path = string.sub(cmdpath, string.len(cmd) + 2) + for name in pairs(SlashCmdList) do --global + if AceConsole.registry[name] then + local i = 0 + while true do + i = i + 1 + local scmd = _G["SLASH_"..name..i] + if not scmd then break end + if cmd == scmd then + return name, cmd, path + end + end + end + end +end + +function external(self, major, instance) + if major == "AceEvent-2.0" then + if not AceEvent then + AceEvent = instance + + AceEvent:embed(self) + end + elseif major == "AceTab-2.0" then + instance:RegisterTabCompletion("AceConsole", "%/.*", function(t, cmdpath, pos) + local ac = AceLibrary("AceConsole-2.0") + local name, cmd, path = ac:TabCompleteInfo(string.sub(cmdpath, 1, pos)) + + if not ac.registry[name] then + return false + else + local validArgs = findTableLevel(ac, ac.registry[name], cmd, path or "") + if validArgs.args then + for arg in pairs(validArgs.args) do + table.insert(t, arg) + end + end + end + end, function(u, matches, gcs, cmdpath) + local ac = AceLibrary("AceConsole-2.0") + local name, cmd, path = ac:TabCompleteInfo(cmdpath) + if ac.registry[name] then + local validArgs, path2, argwork = findTableLevel(ac, ac.registry[name], cmd, path) + printUsage(ac, validArgs.handler, ac.registry[name], validArgs, path2, argwork, not gcs or gcs ~= "", gcs) + end + end) + elseif major == "Dewdrop-2.0" then + Dewdrop = instance + end +end + +local function activate(self, oldLib, oldDeactivate) + AceConsole = self + + self.super.activate(self, oldLib, oldDeactivate) + + if oldLib then + self.registry = oldLib.registry + self.nextAddon = oldLib.nextAddon + end + if not self.registry then + self.registry = {} + else + for name,options in pairs(self.registry) do + self:RegisterChatCommand(false, options, name) + end + end + + if oldDeactivate then + oldDeactivate(oldLib) + end +end + +AceLibrary:Register(AceConsole, MAJOR_VERSION, MINOR_VERSION, activate, nil, external) +AceConsole = AceLibrary(MAJOR_VERSION) diff --git a/libs/AceDB-2.0/AceDB-2.0.lua b/libs/AceDB-2.0/AceDB-2.0.lua new file mode 100755 index 0000000..1fb6663 --- /dev/null +++ b/libs/AceDB-2.0/AceDB-2.0.lua @@ -0,0 +1,1518 @@ +--[[ +Name: AceDB-2.0 +Revision: $Rev: 17797 $ +Developed by: The Ace Development Team (http://www.wowace.com/index.php/The_Ace_Development_Team) +Inspired By: Ace 1.x by Turan (turan@gryphon.com) +Website: http://www.wowace.com/ +Documentation: http://www.wowace.com/index.php/AceDB-2.0 +SVN: http://svn.wowace.com/root/trunk/Ace2/AceDB-2.0 +Description: Mixin to allow for fast, clean, and featureful saved variable + access. +Dependencies: AceLibrary, AceOO-2.0, AceEvent-2.0 +]] + +local MAJOR_VERSION = "AceDB-2.0" +local MINOR_VERSION = "$Revision: 17797 $" + +if not AceLibrary then error(MAJOR_VERSION .. " requires AceLibrary") end +if not AceLibrary:IsNewVersion(MAJOR_VERSION, MINOR_VERSION) then return end + +if loadstring("return function(...) return ... end") and AceLibrary:HasInstance(MAJOR_VERSION) then return end -- lua51 check +if not AceLibrary:HasInstance("AceOO-2.0") then error(MAJOR_VERSION .. " requires AceOO-2.0") end + +local ACTIVE, ENABLED, STATE, TOGGLE_ACTIVE, MAP_ACTIVESUSPENDED, SET_PROFILE, SET_PROFILE_USAGE, PROFILE, PLAYER_OF_REALM, CHOOSE_PROFILE_DESC, CHOOSE_PROFILE_GUI, COPY_PROFILE_DESC, COPY_PROFILE_GUI, OTHER_PROFILE_DESC, OTHER_PROFILE_GUI, OTHER_PROFILE_USAGE, CHARACTER, REALM, CLASS + +local function safecall(func,a,b,c,d,e,f,g,h,i) + local success, err = pcall(func,a,b,c,d,e,f,g,h,i) + if not success then geterrorhandler()(err) end +end + +if GetLocale() == "deDE" then + ACTIVE = "Aktiv" + ENABLED = "Aktiviert" + STATE = "Status" + TOGGLE_ACTIVE = "Stoppt/Aktiviert dieses Addon." + MAP_ACTIVESUSPENDED = { [true] = "|cff00ff00Aktiv|r", [false] = "|cffff0000Gestoppt|r" } + SET_PROFILE = "Setzt das Profil f\195\188r dieses Addon." + SET_PROFILE_USAGE = "{Charakter || Klasse || Realm || }" + PROFILE = "Profil" + PLAYER_OF_REALM = "%s von %s" + CHOOSE_PROFILE_DESC = "W\195\164hle ein Profil." + CHOOSE_PROFILE_GUI = "W\195\164hle" + COPY_PROFILE_DESC = "Kopiert Einstellungen von einem anderem Profil." + COPY_PROFILE_GUI = "Kopiere von" + OTHER_PROFILE_DESC = "W\195\164hle ein anderes Profil." + OTHER_PROFILE_GUI = "Anderes" + OTHER_PROFILE_USAGE = "" + + CHARACTER = "Charakter: " + REALM = "Realm: " + CLASS = "Klasse: " +elseif GetLocale() == "frFR" then + ACTIVE = "Actif" + ENABLED = "Activ\195\169" + STATE = "Etat" + TOGGLE_ACTIVE = "Suspend/active cet addon." + MAP_ACTIVESUSPENDED = { [true] = "|cff00ff00Actif|r", [false] = "|cffff0000Suspendu|r" } + SET_PROFILE = "S\195\169lectionne le profil pour cet addon." + SET_PROFILE_USAGE = "{perso || classe || royaume || }" + PROFILE = "Profil" + PLAYER_OF_REALM = "%s de %s" + CHOOSE_PROFILE_DESC = "Choisissez un profil." + CHOOSE_PROFILE_GUI = "Choix" + COPY_PROFILE_DESC = "Copier les param\195\168tres d'un autre profil." + COPY_PROFILE_GUI = "Copier \195\160 partir de" + OTHER_PROFILE_DESC = "Choisissez un autre profil." + OTHER_PROFILE_GUI = "Autre" + OTHER_PROFILE_USAGE = "" + + CHARACTER = "Personnage: " + REALM = "Royaume: " + CLASS = "Classe: " +elseif GetLocale() == "koKR" then + ACTIVE = "활성화" + ENABLED = "활성화" + STATE = "상태" + TOGGLE_ACTIVE = "이 애드온 중지/계속 실행" + MAP_ACTIVESUSPENDED = { [true] = "|cff00ff00활성화|r", [false] = "|cffff0000중지됨|r" } + SET_PROFILE = "이 애드온에 프로필 설정" + SET_PROFILE_USAGE = "{캐릭터명 || 직업 || 서버명 || <프로필명>}" + PROFILE = "프로필" + PLAYER_OF_REALM = "%s (%s 서버)" + CHOOSE_PROFILE_DESC = "프로파일을 선택합니다." + CHOOSE_PROFILE_GUI = "선택" + COPY_PROFILE_DESC = "다른 프로파일에서 설정을 복사합니다." + COPY_PROFILE_GUI = "복사" + OTHER_PROFILE_DESC = "다른 프로파일을 선택합니다." + OTHER_PROFILE_GUI = "기타" + OTHER_PROFILE_USAGE = "<프로파일명>" + + CHARACTER = "캐릭터: " + REALM = "서버: " + CLASS = "직업: " +elseif GetLocale() == "zhTW" then + ACTIVE = "啟動" + ENABLED = "啟用" + STATE = "狀態" + TOGGLE_ACTIVE = "暫停/重啟這個插件。" + MAP_ACTIVESUSPENDED = { [true] = "|cff00ff00啟動|r", [false] = "|cffff0000已暫停|r" } + SET_PROFILE = "設定這插件的記錄檔。" + SET_PROFILE_USAGE = "{角色 || 聯業 || 伺服器 || <記錄檔名稱>}" + PROFILE = "記錄檔" + PLAYER_OF_REALM = "%s 於 %s" + CHOOSE_PROFILE_DESC = "選擇一個記錄檔" + CHOOSE_PROFILE_GUI = "選擇" + COPY_PROFILE_DESC = "由其他記錄檔複製設定。" + COPY_PROFILE_GUI = "複製由" + OTHER_PROFILE_DESC = "選擇其他記錄檔。" + OTHER_PROFILE_GUI = "其他" + OTHER_PROFILE_USAGE = "<記錄檔名稱>" + + CHARACTER = "角色:" + REALM = "伺服器:" + CLASS = "聯業:" +elseif GetLocale() == "zhCN" then + ACTIVE = "\230\156\137\230\149\136" + ENABLED = "\229\144\175\231\148\168" + STATE = "\231\138\182\230\128\129" + TOGGLE_ACTIVE = "\230\154\130\229\129\156/\230\129\162\229\164\141 \230\173\164\230\143\146\228\187\182." + MAP_ACTIVESUSPENDED = { [true] = "|cff00ff00\230\156\137\230\149\136|r", [false] = "|cffff0000\230\154\130\229\129\156|r" } + SET_PROFILE = "\232\174\190\231\189\174\233\133\141\231\189\174\230\150\135\228\187\182\228\184\186\232\191\153\230\143\146\228\187\182." + SET_PROFILE_USAGE = "{\229\173\151\231\172\166 || \233\128\137\228\187\182\231\177\187 || \229\159\159 || <\233\133\141\231\189\174\230\150\135\228\187\182\229\144\141\229\173\151>}" + PROFILE = "\233\133\141\231\189\174\230\150\135\228\187\182" + PLAYER_OF_REALM = "%s \231\154\132 %s" + CHOOSE_PROFILE_DESC = "\233\128\137\230\139\169\233\133\141\231\189\174\230\150\135\228\187\182." + CHOOSE_PROFILE_GUI = "\233\128\137\230\139\169" + COPY_PROFILE_DESC = "\229\164\141\229\136\182\232\174\190\231\189\174\228\187\142\229\143\166\228\184\128\228\184\170\233\133\141\231\189\174\230\150\135\228\187\182." + COPY_PROFILE_GUI = "\229\164\141\229\136\182\228\187\142" + OTHER_PROFILE_DESC = "\233\128\137\230\139\169\229\143\166\228\184\128\228\184\170\233\133\141\231\189\174\230\150\135\228\187\182." + OTHER_PROFILE_GUI = "\229\133\182\228\187\150" + OTHER_PROFILE_USAGE = "<\233\133\141\231\189\174\230\150\135\228\187\182\229\144\141\229\173\151>" + + CHARACTER = "\229\173\151\231\172\166: " + REALM = "\229\159\159: " + CLASS = "\233\128\137\228\187\182\231\177\187: " +elseif GetLocale() == "ruRU" then + ACTIVE = "Активный" + ENABLED = "Включён" + STATE = "Состояние" + TOGGLE_ACTIVE = "Отключить/Запустить аддон." + MAP_ACTIVESUSPENDED = { [true] = "|cff00ff00Активный|r", [false] = "|cffff0000Приостановленный|r" } + SET_PROFILE = "Установить профиль для этого аддона." + SET_PROFILE_USAGE = "{чар || класс || сервер || <название профиля>}" + PROFILE = "Профиль" + PLAYER_OF_REALM = "%s из %s" + CHOOSE_PROFILE_DESC = "Выберите профиль." + CHOOSE_PROFILE_GUI = "Выбор" + COPY_PROFILE_DESC = "Cкопировать настройки из другого профиля." + COPY_PROFILE_GUI = "Скопировать из" + OTHER_PROFILE_DESC = "Выбрать другой профиль." + OTHER_PROFILE_GUI = "Другое" + OTHER_PROFILE_USAGE = "<название профиля>" + + CHARACTER = "Персонаж: " + REALM = "Сервер: " + CLASS = "Класс: " +else -- enUS + ACTIVE = "Active" + ENABLED = "Enabled" + STATE = "State" + TOGGLE_ACTIVE = "Suspend/resume this addon." + MAP_ACTIVESUSPENDED = { [true] = "|cff00ff00Active|r", [false] = "|cffff0000Suspended|r" } + SET_PROFILE = "Set profile for this addon." + SET_PROFILE_USAGE = "{char || class || realm || }" + PROFILE = "Profile" + PLAYER_OF_REALM = "%s of %s" + CHOOSE_PROFILE_DESC = "Choose a profile." + CHOOSE_PROFILE_GUI = "Choose" + COPY_PROFILE_DESC = "Copy settings from another profile." + COPY_PROFILE_GUI = "Copy from" + OTHER_PROFILE_DESC = "Choose another profile." + OTHER_PROFILE_GUI = "Other" + OTHER_PROFILE_USAGE = "" + + CHARACTER = "Character: " + REALM = "Realm: " + CLASS = "Class: " +end + +local AceOO = AceLibrary("AceOO-2.0") +local AceEvent +local Mixin = AceOO.Mixin +local AceDB = Mixin { + "RegisterDB", + "RegisterDefaults", + "ResetDB", + "SetProfile", + "GetProfile", + "ToggleActive", + "IsActive", + "AcquireDBNamespace", + } +local Dewdrop = AceLibrary:HasInstance("Dewdrop-2.0") and AceLibrary("Dewdrop-2.0") + +local _G = getfenv(0) + +local table_setn +do + local version = GetBuildInfo() + if string.find(version, "^2%.") then + -- 2.0.0 + table_setn = function() end + else + table_setn = table.setn + end +end + +local function inheritDefaults(t, defaults) + if not defaults then + return t + end + for k,v in pairs(defaults) do + if k == "*" then + local v = v + if type(v) == "table" then + setmetatable(t, { + __index = function(self, key) + if key == nil then + return nil + end + self[key] = {} + inheritDefaults(self[key], v) + return self[key] + end + } ) + else + setmetatable(t, { + __index = function(self, key) + if key == nil then + return nil + end + self[key] = v + return self[key] + end + } ) + end + for key in pairs(t) do + if (defaults[key] == nil or key == "*") and type(t[key]) == "table" then + inheritDefaults(t[key], v) + end + end + else + if type(v) == "table" then + if type(t[k]) ~= "table" then + t[k] = {} + end + inheritDefaults(t[k], v) + elseif t[k] == nil then + t[k] = v + end + end + end + return t +end + +local _,race = UnitRace("player") +local faction +if race == "Orc" or race == "Scourge" or race == "Troll" or race == "Tauren" then + faction = FACTION_HORDE +else + faction = FACTION_ALLIANCE +end +local charID = string.format(PLAYER_OF_REALM, UnitName("player"), (string.gsub(GetRealmName(), "^%s*(.-)%s*$", "%1"))) +local realm = string.gsub(GetRealmName(), "^%s*(.-)%s*$", "%1") +local realmID = realm .. " - " .. faction +local classID = UnitClass("player") + +AceDB.CHAR_ID = charID +AceDB.REALM_ID = realmID +AceDB.CLASS_ID = classID + +AceDB.FACTION = faction +AceDB.REALM = realm +AceDB.NAME = UnitName("player") + +local new, del +do + local list = setmetatable({}, {__mode="k"}) + function new() + local t = next(list) + if t then + list[t] = nil + return t + else + return {} + end + end + + function del(t) + setmetatable(t, nil) + for k in pairs(t) do + t[k] = nil + end + table_setn(t, 0) + list[t] = true + end +end + +local caseInsensitive_mt = { + __index = function(self, key) + if type(key) ~= "string" then + return nil + end + local lowerKey = string.lower(key) + for k,v in pairs(self) do + if string.lower(k) == lowerKey then + return self[k] + end + end + end, + __newindex = function(self, key, value) + if type(key) ~= "string" then + return error("table index is nil", 2) + end + local lowerKey = string.lower(key) + for k in pairs(self) do + if string.lower(k) == lowerKey then + rawset(self, k, nil) + rawset(self, key, value) + return + end + end + rawset(self, key, value) + end +} + +local db_mt = { __index = function(db, key) + if key == "char" then + if db.charName then + if type(_G[db.charName]) ~= "table" then + _G[db.charName] = {} + end + if type(_G[db.charName].global) ~= "table" then + _G[db.charName].global = {} + end + rawset(db, 'char', _G[db.charName].global) + else + if type(db.raw.chars) ~= "table" then + db.raw.chars = {} + end + local id = charID + if type(db.raw.chars[id]) ~= "table" then + db.raw.chars[id] = {} + end + rawset(db, 'char', db.raw.chars[id]) + end + if db.defaults and db.defaults.char then + inheritDefaults(db.char, db.defaults.char) + end + return db.char + elseif key == "realm" then + if type(db.raw.realms) ~= "table" then + db.raw.realms = {} + end + local id = realmID + if type(db.raw.realms[id]) ~= "table" then + db.raw.realms[id] = {} + end + rawset(db, 'realm', db.raw.realms[id]) + if db.defaults and db.defaults.realm then + inheritDefaults(db.realm, db.defaults.realm) + end + return db.realm + elseif key == "account" then + if type(db.raw.account) ~= "table" then + db.raw.account = {} + end + rawset(db, 'account', db.raw.account) + if db.defaults and db.defaults.account then + inheritDefaults(db.account, db.defaults.account) + end + return db.account + elseif key == "class" then + if type(db.raw.classes) ~= "table" then + db.raw.classes = {} + end + local id = classID + if type(db.raw.classes[id]) ~= "table" then + db.raw.classes[id] = {} + end + rawset(db, 'class', db.raw.classes[id]) + if db.defaults and db.defaults.class then + inheritDefaults(db.class, db.defaults.class) + end + return db.class + elseif key == "profile" then + if type(db.raw.profiles) ~= "table" then + db.raw.profiles = setmetatable({}, caseInsensitive_mt) + else + setmetatable(db.raw.profiles, caseInsensitive_mt) + end + local id = db.raw.currentProfile[charID] + if id == "char" then + id = "char/" .. charID + elseif id == "class" then + id = "class/" .. classID + elseif id == "realm" then + id = "realm/" .. realmID + end + if type(db.raw.profiles[id]) ~= "table" then + db.raw.profiles[id] = {} + end + rawset(db, 'profile', db.raw.profiles[id]) + if db.defaults and db.defaults.profile then + inheritDefaults(db.profile, db.defaults.profile) + end + return db.profile + elseif key == "raw" or key == "defaults" or key == "name" or key == "charName" or key == "namespaces" then + return nil + end + error(string.format('Cannot access key %q in db table. You may want to use db.profile[%q]', tostring(key), tostring(key)), 2) +end, __newindex = function(db, key, value) + error(string.format('Cannot access key %q in db table. You may want to use db.profile[%q]', tostring(key), tostring(key)), 2) +end } + +local CrawlForSerialization +local CrawlForDeserialization + +local function SerializeObject(o) + local t = { o:Serialize() } + t[0] = o.class:GetLibraryVersion() + CrawlForSerialization(t) + return t +end + +local function DeserializeObject(t) + CrawlForDeserialization(t) + local className = t[0] + for i = 20, 1, -1 do + if t[i] then + table_setn(t, i) + break + end + end + local o = AceLibrary(className):Deserialize(unpack(t)) + table_setn(t, 0) + return o +end + +local function IsSerializable(t) + return AceOO.inherits(t, AceOO.Class) and t.class and type(t.class.Deserialize) == "function" and type(t.Serialize) == "function" and type(t.class.GetLibraryVersion) == "function" +end + +function CrawlForSerialization(t) + local tmp = new() + for k,v in pairs(t) do + tmp[k] = v + end + for k,v in pairs(tmp) do + if type(v) == "table" and type(v[0]) ~= "userdata" then + if IsSerializable(v) then + v = SerializeObject(v) + t[k] = v + else + CrawlForSerialization(v) + end + end + if type(k) == "table" and type(k[0]) ~= "userdata" then + if IsSerializable(k) then + t[k] = nil + t[SerializeObject(k)] = v + else + CrawlForSerialization(k) + end + end + tmp[k] = nil + k = nil + end + tmp = del(tmp) +end + +local function IsDeserializable(t) + return type(t[0]) == "string" and AceLibrary:HasInstance(t[0]) +end + +function CrawlForDeserialization(t) + local tmp = new() + for k,v in pairs(t) do + tmp[k] = v + end + for k,v in pairs(tmp) do + if type(v) == "table" then + if IsDeserializable(v) then + t[k] = DeserializeObject(v) + del(v) + v = t[k] + elseif type(v[0]) ~= "userdata" then + CrawlForDeserialization(v) + end + end + if type(k) == "table" then + if IsDeserializable(k) then + t[k] = nil + t[DeserializeObject(k)] = v + del(k) + elseif type(k[0]) ~= "userdata" then + CrawlForDeserialization(k) + end + end + tmp[k] = nil + k = nil + end + tmp = del(tmp) +end + +local namespace_mt = { __index = function(namespace, key) + local db = namespace.db + local name = namespace.name + if key == "char" then + if db.charName then + if type(_G[db.charName]) ~= "table" then + _G[db.charName] = {} + end + if type(_G[db.charName].namespaces) ~= "table" then + _G[db.charName].namespaces = {} + end + if type(_G[db.charName].namespaces[name]) ~= "table" then + _G[db.charName].namespaces[name] = {} + end + rawset(namespace, 'char', _G[db.charName].namespaces[name]) + else + if type(db.raw.namespaces) ~= "table" then + db.raw.namespaces = {} + end + if type(db.raw.namespaces[name]) ~= "table" then + db.raw.namespaces[name] = {} + end + if type(db.raw.namespaces[name].chars) ~= "table" then + db.raw.namespaces[name].chars = {} + end + local id = charID + if type(db.raw.namespaces[name].chars[id]) ~= "table" then + db.raw.namespaces[name].chars[id] = {} + end + rawset(namespace, 'char', db.raw.namespaces[name].chars[id]) + end + if namespace.defaults and namespace.defaults.char then + inheritDefaults(namespace.char, namespace.defaults.char) + end + return namespace.char + elseif key == "realm" then + if type(db.raw.namespaces) ~= "table" then + db.raw.namespaces = {} + end + if type(db.raw.namespaces[name]) ~= "table" then + db.raw.namespaces[name] = {} + end + if type(db.raw.namespaces[name].realms) ~= "table" then + db.raw.namespaces[name].realms = {} + end + local id = realmID + if type(db.raw.namespaces[name].realms[id]) ~= "table" then + db.raw.namespaces[name].realms[id] = {} + end + rawset(namespace, 'realm', db.raw.namespaces[name].realms[id]) + if namespace.defaults and namespace.defaults.realm then + inheritDefaults(namespace.realm, namespace.defaults.realm) + end + return namespace.realm + elseif key == "account" then + if type(db.raw.namespaces) ~= "table" then + db.raw.namespaces = {} + end + if type(db.raw.namespaces[name]) ~= "table" then + db.raw.namespaces[name] = {} + end + if type(db.raw.namespaces[name].account) ~= "table" then + db.raw.namespaces[name].account = {} + end + rawset(namespace, 'account', db.raw.namespaces[name].account) + if namespace.defaults and namespace.defaults.account then + inheritDefaults(namespace.account, namespace.defaults.account) + end + return namespace.account + elseif key == "class" then + if type(db.raw.namespaces) ~= "table" then + db.raw.namespaces = {} + end + if type(db.raw.namespaces[name]) ~= "table" then + db.raw.namespaces[name] = {} + end + if type(db.raw.namespaces[name].classes) ~= "table" then + db.raw.namespaces[name].classes = {} + end + local id = classID + if type(db.raw.namespaces[name].classes[id]) ~= "table" then + db.raw.namespaces[name].classes[id] = {} + end + rawset(namespace, 'class', db.raw.namespaces[name].classes[id]) + if namespace.defaults and namespace.defaults.class then + inheritDefaults(namespace.class, namespace.defaults.class) + end + return namespace.class + elseif key == "profile" then + if type(db.raw.namespaces) ~= "table" then + db.raw.namespaces = {} + end + if type(db.raw.namespaces[name]) ~= "table" then + db.raw.namespaces[name] = {} + end + if type(db.raw.namespaces[name].profiles) ~= "table" then + db.raw.namespaces[name].profiles = setmetatable({}, caseInsensitive_mt) + else + setmetatable(db.raw.namespaces[name].profiles, caseInsensitive_mt) + end + local id = db.raw.currentProfile[charID] + if id == "char" then + id = "char/" .. charID + elseif id == "class" then + id = "class/" .. classID + elseif id == "realm" then + id = "realm/" .. realmID + end + if type(db.raw.namespaces[name].profiles[id]) ~= "table" then + db.raw.namespaces[name].profiles[id] = {} + end + rawset(namespace, 'profile', db.raw.namespaces[name].profiles[id]) + if namespace.defaults and namespace.defaults.profile then + inheritDefaults(namespace.profile, namespace.defaults.profile) + end + return namespace.profile + elseif key == "defaults" or key == "name" or key == "db" then + return nil + end + error(string.format('Cannot access key %q in db table. You may want to use db.profile[%q]', tostring(key), tostring(key)), 2) +end, __newindex = function(db, key, value) + error(string.format('Cannot access key %q in db table. You may want to use db.profile[%q]', tostring(key), tostring(key)), 2) +end } + +function AceDB:InitializeDB(addonName) + local db = self.db + + if not db then + if addonName then + AceDB.addonsLoaded[addonName] = true + end + return + end + + if db.raw then + -- someone manually initialized + return + end + + if type(_G[db.name]) ~= "table" then + _G[db.name] = {} + else + CrawlForDeserialization(_G[db.name]) + end + if type(_G[db.charName]) == "table" then + CrawlForDeserialization(_G[db.charName]) + end + rawset(db, 'raw', _G[db.name]) + if not db.raw.currentProfile then + db.raw.currentProfile = {} + end + if not db.raw.currentProfile[charID] then + db.raw.currentProfile[charID] = "Default" + end + if db.raw.disabled then + setmetatable(db.raw.disabled, caseInsensitive_mt) + end + if self['acedb-profile-copylist'] then + local t = self['acedb-profile-copylist'] + for k,v in pairs(t) do + t[k] = nil + end + if db.raw.profiles then + for k in pairs(db.raw.profiles) do + if string.find(k, '^char/') then + local name = string.sub(k, 6) + if name ~= charID then + t[k] = CHARACTER .. name + end + elseif string.find(k, '^realm/') then + local name = string.sub(k, 7) + if name ~= realmID then + t[k] = REALM .. name + end + elseif string.find(k, '^class/') then + local name = string.sub(k, 7) + if name ~= classID then + t[k] = CLASS .. name + end + end + end + end + end + if self['acedb-profile-list'] then + local t = self['acedb-profile-list'] + for k,v in pairs(t) do + t[k] = nil + end + t.char = CHARACTER .. charID + t.realm = REALM .. realmID + t.class = CLASS .. classID + t.Default = "Default" + if db.raw.profiles then + for k in pairs(db.raw.profiles) do + if not string.find(k, '^char/') and not string.find(k, '^realm/') and not string.find(k, '^class/') then + t[k] = k + end + end + end + end + setmetatable(db, db_mt) +end + +function AceDB:OnEmbedInitialize(target, name) + if name then + self:ADDON_LOADED(name) + end + self.InitializeDB(target, name) +end + +function AceDB:RegisterDB(name, charName) + AceDB:argCheck(name, 2, "string") + AceDB:argCheck(charName, 3, "string", "nil") + if self.db then + AceDB:error("Cannot call \"RegisterDB\" if self.db is set.") + end + local stack = debugstack() + local addonName = string.gsub(stack, ".-\n.-\\AddOns\\(.-)\\.*", "%1") + self.db = { + name = name, + charName = charName + } + if AceDB.addonsLoaded[addonName] then + AceDB.InitializeDB(self, addonName) + else + AceDB.addonsToBeInitialized[self] = addonName + end + AceDB.registry[self] = true +end + +function AceDB:RegisterDefaults(kind, defaults, a3) + local name + if a3 then + name, kind, defaults = kind, defaults, a3 + AceDB:argCheck(name, 2, "string") + AceDB:argCheck(kind, 3, "string") + AceDB:argCheck(defaults, 4, "table") + if kind ~= "char" and kind ~= "class" and kind ~= "profile" and kind ~= "account" and kind ~= "realm" then + AceDB:error("Bad argument #3 to `RegisterDefaults' (\"char\", \"class\", \"profile\", \"account\", or \"realm\" expected, got %q)", kind) + end + else + AceDB:argCheck(kind, 2, "string") + AceDB:argCheck(defaults, 3, "table") + if kind ~= "char" and kind ~= "class" and kind ~= "profile" and kind ~= "account" and kind ~= "realm" then + AceDB:error("Bad argument #2 to `RegisterDefaults' (\"char\", \"class\", \"profile\", \"account\", or \"realm\" expected, got %q)", kind) + end + end + if type(self.db) ~= "table" or type(self.db.name) ~= "string" then + AceDB:error("Cannot call \"RegisterDefaults\" unless \"RegisterDB\" has been previously called.") + end + local db + if name then + local namespace = self:AcquireDBNamespace(name) + if namespace.defaults and namespace.defaults[kind] then + AceDB:error("\"RegisterDefaults\" has already been called for %q::%q.", name, kind) + end + db = namespace + else + if self.db.defaults and self.db.defaults[kind] then + AceDB:error("\"RegisterDefaults\" has already been called for %q.", kind) + end + db = self.db + end + if not db.defaults then + rawset(db, 'defaults', {}) + end + db.defaults[kind] = defaults + if rawget(db, kind) then + inheritDefaults(db[kind], defaults) + end +end + +function AceDB:ResetDB(kind) + AceDB:argCheck(kind, 2, "nil", "string") + if not self.db or not self.db.raw then + AceDB:error("Cannot call \"ResetDB\" before \"RegisterDB\" has been called and before \"ADDON_LOADED\" has been fired.") + end + local db = self.db + if kind == nil then + if db.charName then + _G[db.charName] = nil + end + _G[db.name] = nil + rawset(db, 'raw', nil) + AceDB.InitializeDB(self) + if db.namespaces then + for name,v in pairs(db.namespaces) do + rawset(v, 'account', nil) + rawset(v, 'char', nil) + rawset(v, 'class', nil) + rawset(v, 'profile', nil) + rawset(v, 'realm', nil) + end + end + elseif kind == "account" then + db.raw.account = nil + rawset(db, 'account', nil) + if db.namespaces then + for name,v in pairs(db.namespaces) do + rawset(v, 'account', nil) + end + end + elseif kind == "char" then + if db.charName then + _G[db.charName] = nil + else + if db.raw.chars then + db.raw.chars[charID] = nil + end + if db.raw.namespaces then + for name,v in pairs(db.raw.namespaces) do + if v.chars then + v.chars[charID] = nil + end + end + end + end + rawset(db, 'char', nil) + if db.namespaces then + for name,v in pairs(db.namespaces) do + rawset(v, 'char', nil) + end + end + elseif kind == "realm" then + if db.raw.realms then + db.raw.realms[realmID] = nil + end + rawset(db, 'realm', nil) + if db.raw.namespaces then + for name,v in pairs(db.raw.namespaces) do + if v.realms then + v.realms[realmID] = nil + end + end + end + if db.namespaces then + for name,v in pairs(db.namespaces) do + rawset(v, 'realm', nil) + end + end + elseif kind == "class" then + if db.raw.realms then + db.raw.realms[classID] = nil + end + rawset(db, 'class', nil) + if db.raw.namespaces then + for name,v in pairs(db.raw.namespaces) do + if v.classes then + v.classes[classID] = nil + end + end + end + if db.namespaces then + for name,v in pairs(db.namespaces) do + rawset(v, 'class', nil) + end + end + elseif kind == "profile" then + local id = db.raw.currentProfile and db.raw.currentProfile[charID] or "Default" + if id == "char" then + id = "char/" .. charID + elseif id == "class" then + id = "class/" .. classID + elseif id == "realm" then + id = "realm/" .. realmID + end + if db.raw.profiles then + db.raw.profiles[id] = nil + end + rawset(db, 'profile', nil) + if db.raw.namespaces then + for name,v in pairs(db.raw.namespaces) do + if v.profiles then + v.profiles[id] = nil + end + end + end + if db.namespaces then + for name,v in pairs(db.namespaces) do + rawset(v, 'profile', nil) + end + end + end +end + +local function cleanDefaults(t, defaults) + if defaults then + for k,v in pairs(defaults) do + if k == "*" then + if type(v) == "table" then + for k in pairs(t) do + if (defaults[k] == nil or k == "*") and type(t[k]) == "table" then + if cleanDefaults(t[k], v) then + t[k] = nil + end + end + end + else + for k in pairs(t) do + if (defaults[k] == nil or k == "*") and t[k] == v then + t[k] = nil + end + end + end + else + if type(v) == "table" then + if type(t[k]) == "table" then + if cleanDefaults(t[k], v) then + t[k] = nil + end + end + elseif t[k] == v then + t[k] = nil + end + end + end + end + return t and not next(t) +end + +function AceDB:GetProfile() + if not self.db or not self.db.raw then + return nil + end + if not self.db.raw.currentProfile then + self.db.raw.currentProfile = {} + end + if not self.db.raw.currentProfile[charID] then + self.db.raw.currentProfile[charID] = "Default" + end + local profile = self.db.raw.currentProfile[charID] + if profile == "char" then + return "char", "char/" .. charID + elseif profile == "class" then + return "class", "class/" .. classID + elseif profile == "realm" then + return "realm", "realm/" .. realmID + end + return profile, profile +end + +local function copyTable(to, from) + setmetatable(to, nil) + for k,v in pairs(from) do + if type(k) == "table" then + k = copyTable({}, k) + end + if type(v) == "table" then + v = copyTable({}, v) + end + to[k] = v + end + table_setn(to, table.getn(from)) + setmetatable(to, from) + return to +end + +function AceDB:SetProfile(name, copyFrom) + AceDB:argCheck(name, 2, "string") + AceDB:argCheck(copyFrom, 3, "string", "nil") + if not self.db or not self.db.raw then + AceDB:error("Cannot call \"SetProfile\" before \"RegisterDB\" has been called and before \"ADDON_LOADED\" has been fired.") + end + local db = self.db + local copy = false + local lowerName = string.lower(name) + local lowerCopyFrom = copyFrom and string.lower(copyFrom) + if string.sub(lowerName, 1, 5) == "char/" or string.sub(lowerName, 1, 6) == "realm/" or string.sub(lowerName, 1, 6) == "class/" then + if string.sub(lowerName, 1, 5) == "char/" then + name, copyFrom = "char", name + else + name, copyFrom = string.sub(lowerName, 1, 5), name + end + lowerName = string.lower(name) + lowerCopyFrom = string.lower(copyFrom) + end + if copyFrom then + if string.sub(lowerCopyFrom, 1, 5) == "char/" then + AceDB:assert(lowerName == "char", "If argument #3 starts with `char/', argument #2 must be `char'") + elseif string.sub(lowerCopyFrom, 1, 6) == "realm/" then + AceDB:assert(lowerName == "realm", "If argument #3 starts with `realm/', argument #2 must be `realm'") + elseif string.sub(lowerCopyFrom, 1, 6) == "class/" then + AceDB:assert(lowerName == "class", "If argument #3 starts with `class/', argument #2 must be `class'") + else + AceDB:assert(lowerName ~= "char" and lowerName ~= "realm" and lowerName ~= "class", "If argument #3 does not start with a special prefix, that prefix cannot be copied to.") + end + if not db.raw.profiles or not db.raw.profiles[copyFrom] then + AceDB:error("Cannot copy profile %q, it does not exist.", copyFrom) + elseif (string.sub(lowerName, 1, 5) == "char/" and string.sub(lowerName, 6) == string.lower(charID)) or (string.sub(lowerName, 1, 6) == "realm/" and string.sub(lowerName, 7) == string.lower(realmID)) or (string.sub(lowerName, 1, 6) == "class/" and string.sub(lowerName, 7) == string.lower(classID)) then + AceDB:error("Cannot copy profile %q, it is currently in use.", name) + end + end + local oldName = db.raw.currentProfile[charID] + if string.lower(oldName) == string.lower(name) then + return + end + local oldProfileData = db.profile + local realName = name + if lowerName == "char" then + realName = name .. "/" .. charID + elseif lowerName == "realm" then + realName = name .. "/" .. realmID + elseif lowerName == "class" then + realName = name .. "/" .. classID + end + local current = self.class + while current and current ~= AceOO.Class do + if current.mixins then + for mixin in pairs(current.mixins) do + if type(mixin.OnEmbedProfileDisable) == "function" then + safecall(mixin.OnEmbedProfileDisable, mixin, self, realName) + end + end + end + current = current.super + end + if type(self.OnProfileDisable) == "function" then + safecall(self.OnProfileDisable, self, realName) + end + local active = self:IsActive() + db.raw.currentProfile[charID] = name + rawset(db, 'profile', nil) + if db.namespaces then + for k,v in pairs(db.namespaces) do + rawset(v, 'profile', nil) + end + end + if copyFrom then + for k,v in pairs(db.profile) do + db.profile[k] = nil + end + copyTable(db.profile, db.raw.profiles[copyFrom]) + inheritDefaults(db.profile, db.defaults and db.defaults.profile) + if db.namespaces then + for l,u in pairs(db.namespaces) do + for k,v in pairs(u.profile) do + u.profile[k] = nil + end + copyTable(u.profile, db.raw.namespaces[l].profiles[copyFrom]) + inheritDefaults(u.profile, u.defaults and u.defaults.profile) + end + end + end + local current = self.class + while current and current ~= AceOO.Class do + if current.mixins then + for mixin in pairs(current.mixins) do + if type(mixin.OnEmbedProfileEnable) == "function" then + safecall(mixin.OnEmbedProfileEnable, mixin, self, oldName, oldProfileData, copyFrom) + end + end + end + current = current.super + end + if type(self.OnProfileEnable) == "function" then + safecall(self.OnProfileEnable, self, oldName, oldProfileData, copyFrom) + end + if cleanDefaults(oldProfileData, db.defaults and db.defaults.profile) then + db.raw.profiles[oldName] = nil + if not next(db.raw.profiles) then + db.raw.profiles = nil + end + end + local newactive = self:IsActive() + if active ~= newactive then + if AceOO.inherits(self, "AceAddon-2.0") then + local AceAddon = AceLibrary("AceAddon-2.0") + if not AceAddon.addonsStarted[self] then + return + end + end + if newactive then + local current = self.class + while current and current ~= AceOO.Class do + if current.mixins then + for mixin in pairs(current.mixins) do + if type(mixin.OnEmbedEnable) == "function" then + safecall(mixin.OnEmbedEnable, mixin, self) + end + end + end + current = current.super + end + if type(self.OnEnable) == "function" then + safecall(self.OnEnable, self) + end + if AceEvent then + AceEvent:TriggerEvent("Ace2_AddonEnabled", self) + end + else + local current = self.class + while current and current ~= AceOO.Class do + if current.mixins then + for mixin in pairs(current.mixins) do + if type(mixin.OnEmbedDisable) == "function" then + safecall(mixin.OnEmbedDisable, mixin, self) + end + end + end + current = current.super + end + if type(self.OnDisable) == "function" then + safecall(self.OnDisable, self) + end + if AceEvent then + AceEvent:TriggerEvent("Ace2_AddonDisabled", self) + end + end + end + if self['acedb-profile-list'] then + if not self['acedb-profile-list'][name] then + self['acedb-profile-list'][name] = name + end + end + if Dewdrop then + Dewdrop:Refresh(1) + Dewdrop:Refresh(2) + Dewdrop:Refresh(3) + Dewdrop:Refresh(4) + Dewdrop:Refresh(5) + end +end + +function AceDB:IsActive() + return not self.db or not self.db.raw or not self.db.raw.disabled or not self.db.raw.disabled[self.db.raw.currentProfile[charID]] +end + +function AceDB:ToggleActive(state) + AceDB:argCheck(state, 2, "boolean", "nil") + if not self.db or not self.db.raw then + AceDB:error("Cannot call \"ToggleActive\" before \"RegisterDB\" has been called and before \"ADDON_LOADED\" has been fired.") + end + local db = self.db + if not db.raw.disabled then + db.raw.disabled = setmetatable({}, caseInsensitive_mt) + end + local profile = db.raw.currentProfile[charID] + local disable + if state == nil then + disable = not db.raw.disabled[profile] + else + disable = not state + if disable == db.raw.disabled[profile] then + return + end + end + db.raw.disabled[profile] = disable or nil + if AceOO.inherits(self, "AceAddon-2.0") then + local AceAddon = AceLibrary("AceAddon-2.0") + if not AceAddon.addonsStarted[self] then + return + end + end + if not disable then + local current = self.class + while current and current ~= AceOO.Class do + if current.mixins then + for mixin in pairs(current.mixins) do + if type(mixin.OnEmbedEnable) == "function" then + safecall(mixin.OnEmbedEnable, mixin, self) + end + end + end + current = current.super + end + if type(self.OnEnable) == "function" then + safecall(self.OnEnable, self) + end + if AceEvent then + AceEvent:TriggerEvent("Ace2_AddonEnabled", self) + end + else + local current = self.class + while current and current ~= AceOO.Class do + if current.mixins then + for mixin in pairs(current.mixins) do + if type(mixin.OnEmbedDisable) == "function" then + safecall(mixin.OnEmbedDisable, mixin, self) + end + end + end + current = current.super + end + if type(self.OnDisable) == "function" then + safecall(self.OnDisable, self) + end + if AceEvent then + AceEvent:TriggerEvent("Ace2_AddonDisabled", self) + end + end + return not disable +end + +function AceDB:embed(target) + self.super.embed(self, target) + if not AceEvent then + AceDB:error(MAJOR_VERSION .. " requires AceEvent-2.0") + end +end + +function AceDB:ADDON_LOADED(name) + AceDB.addonsLoaded[name] = true + for addon, addonName in pairs(AceDB.addonsToBeInitialized) do + if name == addonName then + AceDB.InitializeDB(addon, name) + AceDB.addonsToBeInitialized[addon] = nil + end + end +end + +function AceDB:PLAYER_LOGOUT() + for addon in pairs(AceDB.registry) do + local db = addon.db + if db then + setmetatable(db, nil) + CrawlForSerialization(db.raw) + if type(_G[db.charName]) == "table" then + CrawlForSerialization(_G[db.charName]) + end + if db.char and cleanDefaults(db.char, db.defaults and db.defaults.char) then + if db.charName and _G[db.charName] and _G[db.charName].global == db.char then + _G[db.charName].global = nil + if not next(_G[db.charName]) then + _G[db.charName] = nil + end + else + if db.raw.chars then + db.raw.chars[charID] = nil + if not next(db.raw.chars) then + db.raw.chars = nil + end + end + end + end + if db.realm and cleanDefaults(db.realm, db.defaults and db.defaults.realm) then + if db.raw.realms then + db.raw.realms[realmID] = nil + if not next(db.raw.realms) then + db.raw.realms = nil + end + end + end + if db.class and cleanDefaults(db.class, db.defaults and db.defaults.class) then + if db.raw.classes then + db.raw.classes[classID] = nil + if not next(db.raw.classes) then + db.raw.classes = nil + end + end + end + if db.account and cleanDefaults(db.account, db.defaults and db.defaults.account) then + db.raw.account = nil + end + if db.profile and cleanDefaults(db.profile, db.defaults and db.defaults.profile) then + if db.raw.profiles then + db.raw.profiles[db.raw.currentProfile and db.raw.currentProfile[charID] or "Default"] = nil + if not next(db.raw.profiles) then + db.raw.profiles = nil + end + end + end + if db.namespaces and db.raw.namespaces then + for name,v in pairs(db.namespaces) do + if db.raw.namespaces[name] then + setmetatable(v, nil) + if v.char and cleanDefaults(v.char, v.defaults and v.defaults.char) then + if db.charName and _G[db.charName] and _G[db.charName].namespaces and _G[db.charName].namespaces[name] == v then + _G[db.charName].namespaces[name] = nil + if not next(_G[db.charName].namespaces) then + _G[db.charName].namespaces = nil + if not next(_G[db.charName]) then + _G[db.charName] = nil + end + end + else + if db.raw.namespaces[name].chars then + db.raw.namespaces[name].chars[charID] = nil + if not next(db.raw.namespaces[name].chars) then + db.raw.namespaces[name].chars = nil + end + end + end + end + if v.realm and cleanDefaults(v.realm, v.defaults and v.defaults.realm) then + if db.raw.namespaces[name].realms then + db.raw.namespaces[name].realms[realmID] = nil + if not next(db.raw.namespaces[name].realms) then + db.raw.namespaces[name].realms = nil + end + end + end + if v.class and cleanDefaults(v.class, v.defaults and v.defaults.class) then + if db.raw.namespaces[name].classes then + db.raw.namespaces[name].classes[classID] = nil + if not next(db.raw.namespaces[name].classes) then + db.raw.namespaces[name].classes = nil + end + end + end + if v.account and cleanDefaults(v.account, v.defaults and v.defaults.account) then + db.raw.namespaces[name].account = nil + end + if v.profile and cleanDefaults(v.profile, v.defaults and v.defaults.profile) then + if db.raw.namespaces[name].profiles then + db.raw.namespaces[name].profiles[db.raw.currentProfile and db.raw.currentProfile[charID] or "Default"] = nil + if not next(db.raw.namespaces[name].profiles) then + db.raw.namespaces[name].profiles = nil + end + end + end + if not next(db.raw.namespaces[name]) then + db.raw.namespaces[name] = nil + end + end + end + if not next(db.raw.namespaces) then + db.raw.namespaces = nil + end + end + if db.raw.disabled and not next(db.raw.disabled) then + db.raw.disabled = nil + end + if db.raw.currentProfile then + for k,v in pairs(db.raw.currentProfile) do + if string.lower(v) == "default" then + db.raw.currentProfile[k] = nil + end + end + if not next(db.raw.currentProfile) then + db.raw.currentProfile = nil + end + end + if _G[db.name] and not next(_G[db.name]) then + _G[db.name] = nil + end + end + end +end + +function AceDB:AcquireDBNamespace(name) + AceDB:argCheck(name, 2, "string") + local db = self.db + if not db then + AceDB:error("Cannot call `AcquireDBNamespace' before `RegisterDB' has been called.", 2) + end + if not db.namespaces then + rawset(db, 'namespaces', {}) + end + if not db.namespaces[name] then + local namespace = {} + db.namespaces[name] = namespace + namespace.db = db + namespace.name = name + setmetatable(namespace, namespace_mt) + end + return db.namespaces[name] +end + +function AceDB:GetAceOptionsDataTable(target) + if not target['acedb-profile-list'] then + target['acedb-profile-list'] = setmetatable({}, caseInsensitive_mt) + local t = target['acedb-profile-list'] + for k,v in pairs(t) do + t[k] = nil + end + t.char = CHARACTER .. charID + t.realm = REALM .. realmID + t.class = CLASS .. classID + t.Default = "Default" + if target.db and target.db.raw then + local db = target.db + if db.raw.profiles then + for k in pairs(db.raw.profiles) do + if not string.find(k, '^char/') and not string.find(k, '^realm/') and not string.find(k, '^class/') then + t[k] = k + end + end + end + end + end + if not target['acedb-profile-copylist'] then + target['acedb-profile-copylist'] = setmetatable({}, caseInsensitive_mt) + if target.db and target.db.raw then + local t = target['acedb-profile-copylist'] + local db = target.db + + if db.raw.profiles then + for k in pairs(db.raw.profiles) do + if string.find(k, '^char/') then + local name = string.sub(k, 6) + if name ~= charID then + t[k] = CHARACTER .. name + end + elseif string.find(k, '^realm/') then + local name = string.sub(k, 7) + if name ~= realmID then + t[k] = REALM .. name + end + elseif string.find(k, '^class/') then + local name = string.sub(k, 7) + if name ~= classID then + t[k] = CLASS .. name + end + end + end + end + end + end + return { + standby = { + cmdName = STATE, + guiName = ENABLED, + name = ACTIVE, + desc = TOGGLE_ACTIVE, + type = "toggle", + get = "IsActive", + set = "ToggleActive", + map = MAP_ACTIVESUSPENDED, + order = -3, + }, + profile = { + type = 'group', + name = PROFILE, + desc = SET_PROFILE, + order = -3.5, + get = "GetProfile", + args = { + choose = { + guiName = CHOOSE_PROFILE_GUI, + cmdName = PROFILE, + desc = CHOOSE_PROFILE_DESC, + type = 'text', + get = "GetProfile", + set = "SetProfile", + validate = target['acedb-profile-list'] + }, + copy = { + guiName = COPY_PROFILE_GUI, + cmdName = PROFILE, + desc = COPY_PROFILE_DESC, + type = 'text', + get = "GetProfile", + set = "SetProfile", + validate = target['acedb-profile-copylist'], + disabled = function() + return not next(target['acedb-profile-copylist']) + end, + }, + other = { + guiName = OTHER_PROFILE_GUI, + cmdName = PROFILE, + desc = OTHER_PROFILE_DESC, + usage = OTHER_PROFILE_USAGE, + type = 'text', + get = "GetProfile", + set = "SetProfile", + } + } + }, + } +end + +local function activate(self, oldLib, oldDeactivate) + AceDB = self + AceEvent = AceLibrary:HasInstance("AceEvent-2.0") and AceLibrary("AceEvent-2.0") + + self.super.activate(self, oldLib, oldDeactivate) + + for t in pairs(self.embedList) do + if t.db then + rawset(t.db, 'char', nil) + rawset(t.db, 'realm', nil) + rawset(t.db, 'class', nil) + rawset(t.db, 'account', nil) + rawset(t.db, 'profile', nil) + setmetatable(t.db, db_mt) + end + end + + if oldLib then + self.addonsToBeInitialized = oldLib.addonsToBeInitialized + self.addonsLoaded = oldLib.addonsLoaded + self.registry = oldLib.registry + end + if not self.addonsToBeInitialized then + self.addonsToBeInitialized = {} + end + if not self.addonsLoaded then + self.addonsLoaded = {} + end + if not self.registry then + self.registry = {} + end + + if oldLib then + oldDeactivate(oldLib) + end +end + +local function external(self, major, instance) + if major == "AceEvent-2.0" then + AceEvent = instance + + AceEvent:embed(self) + + self:RegisterEvent("ADDON_LOADED") + self:RegisterEvent("PLAYER_LOGOUT") + elseif major == "Dewdrop-2.0" then + Dewdrop = instance + end +end + +AceLibrary:Register(AceDB, MAJOR_VERSION, MINOR_VERSION, activate, nil, external) +AceDB = AceLibrary(MAJOR_VERSION) diff --git a/libs/AceDebug-2.0/AceDebug-2.0.lua b/libs/AceDebug-2.0/AceDebug-2.0.lua new file mode 100755 index 0000000..1161fac --- /dev/null +++ b/libs/AceDebug-2.0/AceDebug-2.0.lua @@ -0,0 +1,242 @@ +--[[ +Name: AceDebug-2.0 +Revision: $Rev: 17638 $ +Developed by: The Ace Development Team (http://www.wowace.com/index.php/The_Ace_Development_Team) +Inspired By: Ace 1.x by Turan (turan@gryphon.com) +Website: http://www.wowace.com/ +Documentation: http://www.wowace.com/index.php/AceDebug-2.0 +SVN: http://svn.wowace.com/root/trunk/Ace2/AceDebug-2.0 +Description: Mixin to allow for simple debugging capabilities. +Dependencies: AceLibrary, AceOO-2.0 +]] + +local MAJOR_VERSION = "AceDebug-2.0" +local MINOR_VERSION = "$Revision: 17638 $" + +if not AceLibrary then error(MAJOR_VERSION .. " requires AceLibrary") end +if not AceLibrary:IsNewVersion(MAJOR_VERSION, MINOR_VERSION) then return end + +if loadstring("return function(...) return ... end") and AceLibrary:HasInstance(MAJOR_VERSION) then return end -- lua51 check +if not AceLibrary:HasInstance("AceOO-2.0") then error(MAJOR_VERSION .. " requires AceOO-2.0") end + +local DEBUGGING, TOGGLE_DEBUGGING + +if GetLocale() == "frFR" then + DEBUGGING = "D\195\169boguage" + TOGGLE_DEBUGGING = "Activer/d\195\169sactiver le d\195\169boguage" +elseif GetLocale() == "deDE" then + DEBUGGING = "Debuggen" + TOGGLE_DEBUGGING = "Aktiviert/Deaktiviert Debugging" +elseif GetLocale() == "koKR" then + DEBUGGING = "디버깅" + TOGGLE_DEBUGGING = "디버깅 기능 사용함/사용안함" +elseif GetLocale() == "zhTW" then + DEBUGGING = "除錯" + TOGGLE_DEBUGGING = "啟用/停用除錯功能" +elseif GetLocale() == "zhCN" then + DEBUGGING = "\232\176\131\232\175\149" + TOGGLE_DEBUGGING = "\229\144\175\231\148\168/\231\166\129\231\148\168 \232\176\131\232\175\149" +elseif GetLocale() == "ruRU" then + DEBUGGING = "Отладка" + TOGGLE_DEBUGGING = "Вкл/Выкл отладку для этого аддона." +else -- enUS + DEBUGGING = "Debugging" + TOGGLE_DEBUGGING = "Enable/disable debugging" +end + +local table_setn +do + local version = GetBuildInfo() + if string.find(version, "^2%.") then + -- 2.0.0 + table_setn = function() end + else + table_setn = table.setn + end +end + +local math_mod = math.mod or math.fmod + +local AceOO = AceLibrary:GetInstance("AceOO-2.0") +local AceDebug = AceOO.Mixin {"Debug", "CustomDebug", "IsDebugging", "SetDebugging", "SetDebugLevel", "LevelDebug", "CustomLevelDebug", "GetDebugLevel"} + +local function print(text, r, g, b, frame, delay) + (frame or DEFAULT_CHAT_FRAME):AddMessage(text, r, g, b, 1, delay or 5) +end + +local tmp + +function AceDebug:CustomDebug(r, g, b, frame, delay, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) + if not self.debugging then return end + + local output = string.format("|cff7fff7f(DEBUG) %s:[%s%3d]|r", tostring(self), date("%H:%M:%S"), math_mod(GetTime(), 1) * 1000) + + if string.find(tostring(a1), "%%") then + output = output .. " " .. string.format(tostring(a1), tostring(a2), tostring(a3), tostring(a4), tostring(a5), tostring(a6), tostring(a7), tostring(a8), tostring(a9), tostring(a10), tostring(a11), tostring(a12), tostring(a13), tostring(a14), tostring(a15), tostring(a16), tostring(a17), tostring(a18), tostring(a19), tostring(a20)) + else + if not tmp then + tmp = {} + end + + -- This block dynamically rebuilds the tmp array stopping on the first nil. + table.insert(tmp, output) + + table.insert(tmp, tostring(a1)) + table.insert(tmp, a2) + table.insert(tmp, a3) + table.insert(tmp, a4) + table.insert(tmp, a5) + table.insert(tmp, a6) + table.insert(tmp, a7) + table.insert(tmp, a8) + table.insert(tmp, a9) + table.insert(tmp, a10) + table.insert(tmp, a11) + table.insert(tmp, a12) + table.insert(tmp, a13) + table.insert(tmp, a14) + table.insert(tmp, a15) + table.insert(tmp, a16) + table.insert(tmp, a17) + table.insert(tmp, a18) + table.insert(tmp, a19) + table.insert(tmp, a20) + while tmp[table.getn(tmp)] == nil do + table.remove(tmp) + end + for k = 1, table.getn(tmp) do + tmp[k] = tostring(tmp[k]) + end + + output = table.concat(tmp, " ") + + for k,v in pairs(tmp) do + tmp[k] = nil + end + table_setn(tmp, 0) + end + + print(output, r, g, b, frame or self.debugFrame, delay) +end + +function AceDebug:Debug(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) + AceDebug.CustomDebug(self, nil, nil, nil, nil, nil, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) +end + +function AceDebug:IsDebugging() + return self.debugging +end + +function AceDebug:SetDebugging(debugging) + self.debugging = debugging +end + +-- Takes a number 1-3 +-- Level 1: Critical messages that every user should receive +-- Level 2: Should be used for local debugging (function calls, etc) +-- Level 3: Very verbose debugging, will dump everything and anything +-- If set to nil, you will receive no debug information +function AceDebug:SetDebugLevel(level) + AceDebug:argCheck(level, 1, "number", "nil") + if not level then + self.debuglevel = nil + return + end + if level < 1 or level > 3 then + AceDebug:error("Bad argument #1 to `SetDebugLevel`, must be a number 1-3") + end + self.debuglevel = level +end + +function AceDebug:GetDebugLevel() + return self.debuglevel +end + +function AceDebug:CustomLevelDebug(level, r, g, b, frame, delay, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) + if not self.debugging or not self.debuglevel then return end + AceDebug:argCheck(level, 1, "number") + if level < 1 or level > 3 then + AceDebug:error("Bad argument #1 to `LevelDebug`, must be a number 1-3") + end + if level > self.debuglevel then return end + + local output = string.format("|cff7fff7f(DEBUG) %s:[%s.%3d]|r", tostring(self), date("%H:%M:%S"), math_mod(GetTime(), 1) * 1000) + + if string.find(tostring(a1), "%%") then + output = output .. " " .. string.format(tostring(a1), tostring(a2), tostring(a3), tostring(a4), tostring(a5), tostring(a6), tostring(a7), tostring(a8), tostring(a9), tostring(a10), tostring(a11), tostring(a12), tostring(a13), tostring(a14), tostring(a15), tostring(a16), tostring(a17), tostring(a18), tostring(a19), tostring(a20)) + else + if not tmp then + tmp = {} + end + + -- This block dynamically rebuilds the tmp array stopping on the first nil. + table.insert(tmp, output) + + table.insert(tmp, tostring(a1)) + table.insert(tmp, a2) + table.insert(tmp, a3) + table.insert(tmp, a4) + table.insert(tmp, a5) + table.insert(tmp, a6) + table.insert(tmp, a7) + table.insert(tmp, a8) + table.insert(tmp, a9) + table.insert(tmp, a10) + table.insert(tmp, a11) + table.insert(tmp, a12) + table.insert(tmp, a13) + table.insert(tmp, a14) + table.insert(tmp, a15) + table.insert(tmp, a16) + table.insert(tmp, a17) + table.insert(tmp, a18) + table.insert(tmp, a19) + table.insert(tmp, a20) + while tmp[table.getn(tmp)] == nil do + table.remove(tmp) + end + for k = 1, table.getn(tmp) do + tmp[k] = tostring(tmp[k]) + end + + output = table.concat(tmp, " ") + + for k,v in pairs(tmp) do + tmp[k] = nil + end + table_setn(tmp, 0) + end + + print(output, r, g, b, frame or self.debugFrame, delay) +end + +function AceDebug:LevelDebug(level, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) + if not self.debugging or not self.debuglevel then return end + AceDebug:argCheck(level, 1, "number") + if level < 1 or level > 3 then + AceDebug:error("Bad argument #1 to `LevelDebug`, must be a number 1-3") + end + if level > self.debuglevel then return end + + AceDebug.CustomLevelDebug(self, level, nil, nil, nil, nil, nil, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) +end + + +local options +function AceDebug:GetAceOptionsDataTable(target) + if not options then + options = { + debug = { + name = DEBUGGING, + desc = TOGGLE_DEBUGGING, + type = "toggle", + get = "IsDebugging", + set = "SetDebugging", + order = -2, + } + } + end + return options +end +AceLibrary:Register(AceDebug, MAJOR_VERSION, MINOR_VERSION, AceDebug.activate) +AceDebug = AceLibrary(MAJOR_VERSION) diff --git a/libs/AceEvent-2.0/AceEvent-2.0.lua b/libs/AceEvent-2.0/AceEvent-2.0.lua new file mode 100755 index 0000000..326ee5b --- /dev/null +++ b/libs/AceEvent-2.0/AceEvent-2.0.lua @@ -0,0 +1,973 @@ +--[[ +Name: AceEvent-2.0 +Revision: $Rev: 17803 $ +Developed by: The Ace Development Team (http://www.wowace.com/index.php/The_Ace_Development_Team) +Inspired By: Ace 1.x by Turan (turan@gryphon.com) +Website: http://www.wowace.com/ +Documentation: http://www.wowace.com/index.php/AceEvent-2.0 +SVN: http://svn.wowace.com/root/trunk/Ace2/AceEvent-2.0 +Description: Mixin to allow for event handling, scheduling, and inter-addon + communication. +Dependencies: AceLibrary, AceOO-2.0 +]] + +local MAJOR_VERSION = "AceEvent-2.0" +local MINOR_VERSION = "$Revision: 17803 $" + +if not AceLibrary then error(MAJOR_VERSION .. " requires AceLibrary") end +if not AceLibrary:IsNewVersion(MAJOR_VERSION, MINOR_VERSION) then return end + +if loadstring("return function(...) return ... end") and AceLibrary:HasInstance(MAJOR_VERSION) then return end -- lua51 check +if not AceLibrary:HasInstance("AceOO-2.0") then error(MAJOR_VERSION .. " requires AceOO-2.0") end + +local AceOO = AceLibrary:GetInstance("AceOO-2.0") +local Mixin = AceOO.Mixin +local AceEvent = Mixin { + "RegisterEvent", + "RegisterAllEvents", + "UnregisterEvent", + "UnregisterAllEvents", + "TriggerEvent", + "ScheduleEvent", + "ScheduleRepeatingEvent", + "CancelScheduledEvent", + "CancelAllScheduledEvents", + "IsEventRegistered", + "IsEventScheduled", + "RegisterBucketEvent", + "UnregisterBucketEvent", + "UnregisterAllBucketEvents", + "IsBucketEventRegistered", + } + +local table_setn +do + local version = GetBuildInfo() + if string.find(version, "^2%.") then + -- 2.0.0 + table_setn = function() end + else + table_setn = table.setn + end +end + +local weakKey = {__mode="k"} +local new, del +do + local list = setmetatable({}, weakKey) + function new() + local t = next(list) + if t then + list[t] = nil + return t + else + return {} + end + end + + function del(t) + setmetatable(t, nil) + for k in pairs(t) do + t[k] = nil + end + list[t] = true + end +end + +local FAKE_NIL +local RATE + +local eventsWhichHappenOnce = { + PLAYER_LOGIN = true, + AceEvent_FullyInitialized = true, + VARIABLES_LOADED = true, + PLAYER_LOGOUT = true, +} + +local registeringFromAceEvent +function AceEvent:RegisterEvent(event, method, once) + AceEvent:argCheck(event, 2, "string") + if self == AceEvent and not registeringFromAceEvent then + AceEvent:argCheck(method, 3, "function") + self = method + else + AceEvent:argCheck(method, 3, "string", "function", "nil", "boolean", "number") + if type(method) == "boolean" or type(method) == "number" then + AceEvent:argCheck(once, 4, "nil") + once, method = method, event + end + end + AceEvent:argCheck(once, 4, "number", "boolean", "nil") + if eventsWhichHappenOnce[event] then + once = true + end + local throttleRate + if type(once) == "number" then + throttleRate, once = once + end + if not method then + method = event + end + if type(method) == "string" and type(self[method]) ~= "function" then + AceEvent:error("Cannot register event %q to method %q, it does not exist", event, method) + else + assert(type(method) == "function" or type(method) == "string") + end + + local AceEvent_registry = AceEvent.registry + if not AceEvent_registry[event] then + AceEvent_registry[event] = new() + AceEvent.frame:RegisterEvent(event) + end + + local remember = true + if AceEvent_registry[event][self] then + remember = false + end + AceEvent_registry[event][self] = method + + local AceEvent_onceRegistry = AceEvent.onceRegistry + if once then + if not AceEvent_onceRegistry then + AceEvent.onceRegistry = new() + AceEvent_onceRegistry = AceEvent.onceRegistry + end + if not AceEvent_onceRegistry[event] then + AceEvent_onceRegistry[event] = new() + end + AceEvent_onceRegistry[event][self] = true + else + if AceEvent_onceRegistry and AceEvent_onceRegistry[event] then + AceEvent_onceRegistry[event][self] = nil + if not next(AceEvent_onceRegistry[event]) then + AceEvent_onceRegistry[event] = del(AceEvent_onceRegistry[event]) + end + end + end + + local AceEvent_throttleRegistry = AceEvent.throttleRegistry + if throttleRate then + if not AceEvent_throttleRegistry then + AceEvent.throttleRegistry = new() + AceEvent_throttleRegistry = AceEvent.throttleRegistry + end + if not AceEvent_throttleRegistry[event] then + AceEvent_throttleRegistry[event] = new() + end + if AceEvent_throttleRegistry[event][self] then + AceEvent_throttleRegistry[event][self] = del(AceEvent_throttleRegistry[event][self]) + end + AceEvent_throttleRegistry[event][self] = setmetatable(new(), weakKey) + local t = AceEvent_throttleRegistry[event][self] + t[RATE] = throttleRate + else + if AceEvent_throttleRegistry and AceEvent_throttleRegistry[event] then + if AceEvent_throttleRegistry[event][self] then + AceEvent_throttleRegistry[event][self] = del(AceEvent_throttleRegistry[event][self]) + end + if not next(AceEvent_throttleRegistry[event]) then + AceEvent_throttleRegistry[event] = del(AceEvent_throttleRegistry[event]) + end + end + end + + if remember then + AceEvent:TriggerEvent("AceEvent_EventRegistered", self, event) + end +end + +local ALL_EVENTS + +function AceEvent:RegisterAllEvents(method) + if self == AceEvent then + AceEvent:argCheck(method, 1, "function") + self = method + else + AceEvent:argCheck(method, 1, "string", "function") + if type(method) == "string" and type(self[method]) ~= "function" then + AceEvent:error("Cannot register all events to method %q, it does not exist", method) + end + end + + local AceEvent_registry = AceEvent.registry + if not AceEvent_registry[ALL_EVENTS] then + AceEvent_registry[ALL_EVENTS] = new() + AceEvent.frame:RegisterAllEvents() + end + + AceEvent_registry[ALL_EVENTS][self] = method +end + +local _G = getfenv(0) +local memstack, timestack = {}, {} +local memdiff, timediff +function AceEvent:TriggerEvent(event, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) + AceEvent:argCheck(event, 2, "string") + local AceEvent_registry = AceEvent.registry + if (not AceEvent_registry[event] or not next(AceEvent_registry[event])) and (not AceEvent_registry[ALL_EVENTS] or not next(AceEvent_registry[ALL_EVENTS])) then + return + end + local _G_event = _G.event + _G.event = event + local lastEvent = AceEvent.currentEvent + AceEvent.currentEvent = event + + local AceEvent_onceRegistry = AceEvent.onceRegistry + local AceEvent_debugTable = AceEvent.debugTable + if AceEvent_onceRegistry and AceEvent_onceRegistry[event] then + local tmp = new() + for obj, method in pairs(AceEvent_onceRegistry[event]) do + tmp[obj] = AceEvent_registry[event] and AceEvent_registry[event][obj] or nil + end + local obj = next(tmp) + while obj do + local mem, time + if AceEvent_debugTable then + if not AceEvent_debugTable[event] then + AceEvent_debugTable[event] = new() + end + if not AceEvent_debugTable[event][obj] then + AceEvent_debugTable[event][obj] = new() + AceEvent_debugTable[event][obj].mem = 0 + AceEvent_debugTable[event][obj].time = 0 + AceEvent_debugTable[event][obj].count = 0 + end + if memdiff then + table.insert(memstack, memdiff) + table.insert(timestack, timediff) + end + memdiff, timediff = 0, 0 + mem, time = gcinfo(), GetTime() + end + local method = tmp[obj] + AceEvent.UnregisterEvent(obj, event) + if type(method) == "string" then + local obj_method = obj[method] + if obj_method then + obj_method(obj, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) + end + elseif method then -- function + method(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) + end + if AceEvent_debugTable then + local dmem, dtime = memdiff, timediff + mem, time = gcinfo() - mem - memdiff, GetTime() - time - timediff + AceEvent_debugTable[event][obj].mem = AceEvent_debugTable[event][obj].mem + mem + AceEvent_debugTable[event][obj].time = AceEvent_debugTable[event][obj].time + time + AceEvent_debugTable[event][obj].count = AceEvent_debugTable[event][obj].count + 1 + + memdiff, timediff = table.remove(memstack), table.remove(timestack) + if memdiff then + memdiff = memdiff + mem + dmem + timediff = timediff + time + dtime + end + end + tmp[obj] = nil + obj = next(tmp) + end + del(tmp) + end + + local AceEvent_throttleRegistry = AceEvent.throttleRegistry + local throttleTable = AceEvent_throttleRegistry and AceEvent_throttleRegistry[event] + if AceEvent_registry[event] then + local tmp = new() + for obj, method in pairs(AceEvent_registry[event]) do + tmp[obj] = method + end + local obj = next(tmp) + while obj do + local method = tmp[obj] + local continue = false + if throttleTable and throttleTable[obj] then + local a1 = a1 + if a1 == nil then + a1 = FAKE_NIL + end + if not throttleTable[obj][a1] or GetTime() - throttleTable[obj][a1] >= throttleTable[obj][RATE] then + throttleTable[obj][a1] = GetTime() + else + continue = true + end + end + if not continue then + local mem, time + if AceEvent_debugTable then + if not AceEvent_debugTable[event] then + AceEvent_debugTable[event] = new() + end + if not AceEvent_debugTable[event][obj] then + AceEvent_debugTable[event][obj] = new() + AceEvent_debugTable[event][obj].mem = 0 + AceEvent_debugTable[event][obj].time = 0 + AceEvent_debugTable[event][obj].count = 0 + end + if memdiff then + table.insert(memstack, memdiff) + table.insert(timestack, timediff) + end + memdiff, timediff = 0, 0 + mem, time = gcinfo(), GetTime() + end + if type(method) == "string" then + local obj_method = obj[method] + if obj_method then + obj_method(obj, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) + end + elseif method then -- function + method(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) + end + if AceEvent_debugTable then + local dmem, dtime = memdiff, timediff + mem, time = gcinfo() - mem - memdiff, GetTime() - time - timediff + AceEvent_debugTable[event][obj].mem = AceEvent_debugTable[event][obj].mem + mem + AceEvent_debugTable[event][obj].time = AceEvent_debugTable[event][obj].time + time + AceEvent_debugTable[event][obj].count = AceEvent_debugTable[event][obj].count + 1 + + memdiff, timediff = table.remove(memstack), table.remove(timestack) + if memdiff then + memdiff = memdiff + mem + dmem + timediff = timediff + time + dtime + end + end + end + tmp[obj] = nil + obj = next(tmp) + end + del(tmp) + end + if AceEvent_registry[ALL_EVENTS] then + local tmp = new() + for obj, method in pairs(AceEvent_registry[ALL_EVENTS]) do + tmp[obj] = method + end + local obj = next(tmp) + while obj do + local method = tmp[obj] + local mem, time + if AceEvent_debugTable then + if not AceEvent_debugTable[event] then + AceEvent_debugTable[event] = new() + end + if not AceEvent_debugTable[event][obj] then + AceEvent_debugTable[event][obj] = new() + AceEvent_debugTable[event][obj].mem = 0 + AceEvent_debugTable[event][obj].time = 0 + AceEvent_debugTable[event][obj].count = 0 + end + if memdiff then + table.insert(memstack, memdiff) + table.insert(timestack, timediff) + end + memdiff, timediff = 0, 0 + mem, time = gcinfo(), GetTime() + end + if type(method) == "string" then + local obj_method = obj[method] + if obj_method then + obj_method(obj, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) + end + elseif method then -- function + method(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) + end + if AceEvent_debugTable then + local dmem, dtime = memdiff, timediff + mem, time = gcinfo() - mem - memdiff, GetTime() - time - timediff + AceEvent_debugTable[event][obj].mem = AceEvent_debugTable[event][obj].mem + mem + AceEvent_debugTable[event][obj].time = AceEvent_debugTable[event][obj].time + time + AceEvent_debugTable[event][obj].count = AceEvent_debugTable[event][obj].count + 1 + + memdiff, timediff = table.remove(memstack), table.remove(timestack) + if memdiff then + memdiff = memdiff + mem + dmem + timediff = timediff + time + dtime + end + end + tmp[obj] = nil + obj = next(tmp) + end + del(tmp) + end + _G.event = _G_event + AceEvent.currentEvent = lastEvent +end + +-- local accessors +local getn = table.getn +local tinsert = table.insert +local tremove = table.remove +local floor = math.floor +local GetTime = GetTime +local next = next +local pairs = pairs +local unpack = unpack + +local delayRegistry +local tmp = {} +local function OnUpdate() + local t = GetTime() + for k,v in pairs(delayRegistry) do + tmp[k] = true + end + for k in pairs(tmp) do + local v = delayRegistry[k] + if v then + local v_time = v.time + if not v_time then + delayRegistry[k] = del(v) + elseif v_time <= t then + local v_repeatDelay = v.repeatDelay + if v_repeatDelay then + -- use the event time, not the current time, else timing inaccuracies add up over time + v.time = v_time + v_repeatDelay + end + local event = v.event + local mem, time + if AceEvent_debugTable then + mem, time = gcinfo(), GetTime() + end + if type(event) == "function" then + event(unpack(v)) + else + AceEvent:TriggerEvent(event, unpack(v)) + end + if AceEvent_debugTable then + mem, time = gcinfo() - mem, GetTime() - time + v.mem = v.mem + mem + v.timeSpent = v.timeSpent + time + v.count = v.count + 1 + end + if not v_repeatDelay then + local x = delayRegistry[k] + if x and x.time == v_time then -- check if it was manually reset + delayRegistry[k] = del(v) + end + end + end + end + end + for k in pairs(tmp) do + tmp[k] = nil + end + if not next(delayRegistry) then + AceEvent.frame:Hide() + end +end + +local function ScheduleEvent(self, repeating, event, delay, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) + local id + if type(event) == "string" or type(event) == "table" then + if type(event) == "table" then + if not delayRegistry or not delayRegistry[event] then + AceEvent:error("Bad argument #2 to `ScheduleEvent'. Improper id table fed in.") + end + end + if type(delay) ~= "number" then + id, event, delay, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20 = event, delay, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20 + AceEvent:argCheck(event, 3, "string", "function", --[[ so message is right ]] "number") + AceEvent:argCheck(delay, 4, "number") + self:CancelScheduledEvent(id) + end + else + AceEvent:argCheck(event, 2, "string", "function") + AceEvent:argCheck(delay, 3, "number") + end + + if not delayRegistry then + AceEvent.delayRegistry = new() + delayRegistry = AceEvent.delayRegistry + AceEvent.frame:SetScript("OnUpdate", OnUpdate) + end + local t + if type(id) == "table" then + for k in pairs(id) do + id[k] = nil + end + t = id + else + t = new() + end + t[1] = a1 + t[2] = a2 + t[3] = a3 + t[4] = a4 + t[5] = a5 + t[6] = a6 + t[7] = a7 + t[8] = a8 + t[9] = a9 + t[10] = a10 + t[11] = a11 + t[12] = a12 + t[13] = a13 + t[14] = a14 + t[15] = a15 + t[16] = a16 + t[17] = a17 + t[18] = a18 + t[19] = a19 + t[20] = a20 + table_setn(t, 20) + t.event = event + t.time = GetTime() + delay + t.self = self + t.id = id or t + t.repeatDelay = repeating and delay + if AceEvent_debugTable then + t.mem = 0 + t.count = 0 + t.timeSpent = 0 + end + delayRegistry[t.id] = t + AceEvent.frame:Show() + return t.id +end + +function AceEvent:ScheduleEvent(event, delay, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) + if type(event) == "string" or type(event) == "table" then + if type(event) == "table" then + if not delayRegistry or not delayRegistry[event] then + AceEvent:error("Bad argument #2 to `ScheduleEvent'. Improper id table fed in.") + end + end + if type(delay) ~= "number" then + AceEvent:argCheck(delay, 3, "string", "function", --[[ so message is right ]] "number") + AceEvent:argCheck(a1, 4, "number") + end + else + AceEvent:argCheck(event, 2, "string", "function") + AceEvent:argCheck(delay, 3, "number") + end + + return ScheduleEvent(self, false, event, delay, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) +end + +function AceEvent:ScheduleRepeatingEvent(event, delay, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) + if type(event) == "string" or type(event) == "table" then + if type(event) == "table" then + if not delayRegistry or not delayRegistry[event] then + AceEvent:error("Bad argument #2 to `ScheduleEvent'. Improper id table fed in.") + end + end + if type(delay) ~= "number" then + AceEvent:argCheck(delay, 3, "string", "function", --[[ so message is right ]] "number") + AceEvent:argCheck(a1, 4, "number") + end + else + AceEvent:argCheck(event, 2, "string", "function") + AceEvent:argCheck(delay, 3, "number") + end + + return ScheduleEvent(self, true, event, delay, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) +end + +function AceEvent:CancelScheduledEvent(t) + AceEvent:argCheck(t, 2, "string", "table") + if delayRegistry then + local v = delayRegistry[t] + if v then + delayRegistry[t] = del(v) + if not next(delayRegistry) then + AceEvent.frame:Hide() + end + return true + end + end + return false +end + +function AceEvent:IsEventScheduled(t) + AceEvent:argCheck(t, 2, "string", "table") + if delayRegistry then + local v = delayRegistry[t] + if v then + return true, v.time - GetTime() + end + end + return false, nil +end + +function AceEvent:UnregisterEvent(event) + AceEvent:argCheck(event, 2, "string") + local AceEvent_registry = AceEvent.registry + if AceEvent_registry[event] and AceEvent_registry[event][self] then + AceEvent_registry[event][self] = nil + local AceEvent_onceRegistry = AceEvent.onceRegistry + if AceEvent_onceRegistry and AceEvent_onceRegistry[event] and AceEvent_onceRegistry[event][self] then + AceEvent_onceRegistry[event][self] = nil + if not next(AceEvent_onceRegistry[event]) then + AceEvent_onceRegistry[event] = del(AceEvent_onceRegistry[event]) + end + end + local AceEvent_throttleRegistry = AceEvent.throttleRegistry + if AceEvent_throttleRegistry and AceEvent_throttleRegistry[event] and AceEvent_throttleRegistry[event][self] then + AceEvent_throttleRegistry[event][self] = del(AceEvent_throttleRegistry[event][self]) + if not next(AceEvent_throttleRegistry[event]) then + AceEvent_throttleRegistry[event] = del(AceEvent_throttleRegistry[event]) + end + end + if not next(AceEvent_registry[event]) then + AceEvent_registry[event] = del(AceEvent_registry[event]) + if not AceEvent_registry[ALL_EVENTS] or not next(AceEvent_registry[ALL_EVENTS]) then + AceEvent.frame:UnregisterEvent(event) + end + end + else + if self == AceEvent then + error(string.format("Cannot unregister event %q. Improperly unregistering from AceEvent-2.0.", event), 2) + else + AceEvent:error("Cannot unregister event %q. %q is not registered with it.", event, self) + end + end + AceEvent:TriggerEvent("AceEvent_EventUnregistered", self, event) +end + +function AceEvent:UnregisterAllEvents() + local AceEvent_registry = AceEvent.registry + if AceEvent_registry[ALL_EVENTS] and AceEvent_registry[ALL_EVENTS][self] then + AceEvent_registry[ALL_EVENTS][self] = nil + if not next(AceEvent_registry[ALL_EVENTS]) then + del(AceEvent_registry[ALL_EVENTS]) + AceEvent.frame:UnregisterAllEvents() + for k,v in pairs(AceEvent_registry) do + if k ~= ALL_EVENTS then + AceEvent.frame:RegisterEvent(k) + end + end + AceEvent_registry[event] = nil + end + end + local first = true + for event, data in pairs(AceEvent_registry) do + if first then + if AceEvent_registry.AceEvent_EventUnregistered then + event = "AceEvent_EventUnregistered" + else + first = false + end + end + local x = data[self] + data[self] = nil + if x and event ~= ALL_EVENTS then + if not next(data) then + del(data) + if not AceEvent_registry[ALL_EVENTS] or not next(AceEvent_registry[ALL_EVENTS]) then + AceEvent.frame:UnregisterEvent(event) + end + AceEvent_registry[event] = nil + end + AceEvent:TriggerEvent("AceEvent_EventUnregistered", self, event) + end + if first then + event = nil + end + end + if AceEvent.onceRegistry then + for event, data in pairs(AceEvent.onceRegistry) do + data[self] = nil + end + end +end + +function AceEvent:CancelAllScheduledEvents() + if delayRegistry then + for k,v in pairs(delayRegistry) do + if v.self == self then + delayRegistry[k] = del(v) + end + end + if not next(delayRegistry) then + AceEvent.frame:Hide() + end + end +end + +function AceEvent:IsEventRegistered(event) + AceEvent:argCheck(event, 2, "string") + local AceEvent_registry = AceEvent.registry + if self == AceEvent then + return AceEvent_registry[event] and next(AceEvent_registry[event]) and true or false + end + if AceEvent_registry[event] and AceEvent_registry[event][self] then + return true, AceEvent_registry[event][self] + end + return false, nil +end + +local bucketfunc +function AceEvent:RegisterBucketEvent(event, delay, method) + AceEvent:argCheck(event, 2, "string", "table") + if type(event) == "table" then + for k,v in pairs(event) do + if type(k) ~= "number" then + AceEvent:error("All keys to argument #2 to `RegisterBucketEvent' must be numbers.") + elseif type(v) ~= "string" then + AceEvent:error("All values to argument #2 to `RegisterBucketEvent' must be strings.") + end + end + end + AceEvent:argCheck(delay, 3, "number") + if AceEvent == self then + AceEvent:argCheck(method, 4, "function") + self = method + else + if type(event) == "string" then + AceEvent:argCheck(method, 4, "string", "function", "nil") + if not method then + method = event + end + else + AceEvent:argCheck(method, 4, "string", "function") + end + + if type(method) == "string" and type(self[method]) ~= "function" then + AceEvent:error("Cannot register event %q to method %q, it does not exist", event, method) + end + end + if not AceEvent.buckets then + AceEvent.buckets = new() + end + if not AceEvent.buckets[event] then + AceEvent.buckets[event] = new() + end + if not AceEvent.buckets[event][self] then + AceEvent.buckets[event][self] = new() + AceEvent.buckets[event][self].current = new() + AceEvent.buckets[event][self].self = self + else + AceEvent.CancelScheduledEvent(self, AceEvent.buckets[event][self].id) + end + local bucket = AceEvent.buckets[event][self] + bucket.method = method + + local func = function(arg1) + bucket.run = true + if arg1 then + bucket.current[arg1] = true + end + end + AceEvent.buckets[event][self].func = func + if type(event) == "string" then + AceEvent.RegisterEvent(self, event, func) + else + for _,v in ipairs(event) do + AceEvent.RegisterEvent(self, v, func) + end + end + if not bucketfunc then + bucketfunc = function(bucket) + local current = bucket.current + local method = bucket.method + local self = bucket.self + if bucket.run then + if type(method) == "string" then + self[method](self, current) + elseif method then -- function + method(current) + end + for k in pairs(current) do + current[k] = nil + k = nil + end + bucket.run = false + end + end + end + bucket.id = AceEvent.ScheduleRepeatingEvent(self, bucketfunc, delay, bucket) +end + +function AceEvent:IsBucketEventRegistered(event) + AceEvent:argCheck(event, 2, "string", "table") + return AceEvent.buckets and AceEvent.buckets[event] and AceEvent.buckets[event][self] +end + +function AceEvent:UnregisterBucketEvent(event) + AceEvent:argCheck(event, 2, "string", "table") + if not AceEvent.buckets or not AceEvent.buckets[event] or not AceEvent.buckets[event][self] then + AceEvent:error("Cannot unregister bucket event %q. %q is not registered with it.", event, self) + end + + local bucket = AceEvent.buckets[event][self] + + if type(event) == "string" then + AceEvent.UnregisterEvent(self, event) + else + for _,v in ipairs(event) do + AceEvent.UnregisterEvent(self, v) + end + end + AceEvent:CancelScheduledEvent(bucket.id) + + del(bucket.current) + AceEvent.buckets[event][self] = del(AceEvent.buckets[event][self]) + if not next(AceEvent.buckets[event]) then + AceEvent.buckets[event] = del(AceEvent.buckets[event]) + end +end + +function AceEvent:UnregisterAllBucketEvents() + if not AceEvent.buckets or not next(AceEvent.buckets) then + return + end + for k,v in pairs(AceEvent.buckets) do + if v == self then + AceEvent.UnregisterBucketEvent(self, k) + k = nil + end + end +end + +function AceEvent:OnEmbedDisable(target) + self.UnregisterAllEvents(target) + + self.CancelAllScheduledEvents(target) + + self.UnregisterAllBucketEvents(target) +end + +function AceEvent:EnableDebugging() + if not self.debugTable then + self.debugTable = new() + + if delayRegistry then + for k,v in pairs(self.delayRegistry) do + if not v.mem then + v.mem = 0 + v.count = 0 + v.timeSpent = 0 + end + end + end + end +end + +function AceEvent:IsFullyInitialized() + return self.postInit or false +end + +function AceEvent:IsPostPlayerLogin() + return self.playerLogin or false +end + +function AceEvent:activate(oldLib, oldDeactivate) + AceEvent = self + + if oldLib then + self.onceRegistry = oldLib.onceRegistry + self.throttleRegistry = oldLib.throttleRegistry + self.delayRegistry = oldLib.delayRegistry + self.buckets = oldLib.buckets + self.registry = oldLib.registry + self.frame = oldLib.frame + self.debugTable = oldLib.debugTable + self.playerLogin = oldLib.pew or DEFAULT_CHAT_FRAME and DEFAULT_CHAT_FRAME.defaultLanguage and true + self.postInit = oldLib.postInit or self.playerLogin and ChatTypeInfo and ChatTypeInfo.WHISPER and ChatTypeInfo.WHISPER.r and true + self.ALL_EVENTS = oldLib.ALL_EVENTS + self.FAKE_NIL = oldLib.FAKE_NIL + self.RATE = oldLib.RATE + end + if not self.registry then + self.registry = {} + end + if not self.frame then + self.frame = CreateFrame("Frame", "AceEvent20Frame") + end + if not self.ALL_EVENTS then + self.ALL_EVENTS = {} + end + if not self.FAKE_NIL then + self.FAKE_NIL = {} + end + if not self.RATE then + self.RATE = {} + end + ALL_EVENTS = self.ALL_EVENTS + FAKE_NIL = self.FAKE_NIL + RATE = self.RATE + local inPlw = false + local blacklist = { + UNIT_INVENTORY_CHANGED = true, + BAG_UPDATE = true, + ITEM_LOCK_CHANGED = true, + ACTIONBAR_SLOT_CHANGED = true, + } + self.frame:SetScript("OnEvent", function() + local event = event + if event == "PLAYER_ENTERING_WORLD" then + inPlw = false + elseif event == "PLAYER_LEAVING_WORLD" then + inPlw = true + end + if event and (not inPlw or not blacklist[event]) then + self:TriggerEvent(event, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9) + end + end) + if self.delayRegistry then + delayRegistry = self.delayRegistry + self.frame:SetScript("OnUpdate", OnUpdate) + end + + self:UnregisterAllEvents() + self:CancelAllScheduledEvents() + + registeringFromAceEvent = true + self:RegisterEvent("LOOT_OPENED", function() + SendAddonMessage("LOOT_OPENED", "", "RAID") + end) + registeringFromAceEvent = nil + + if not self.playerLogin then + registeringFromAceEvent = true + self:RegisterEvent("PLAYER_LOGIN", function() + self.playerLogin = true + end, true) + registeringFromAceEvent = nil + end + + if not self.postInit then + local isReload = true + local function func() + self.postInit = true + self:TriggerEvent("AceEvent_FullyInitialized") + if self.registry["CHAT_MSG_CHANNEL_NOTICE"] and self.registry["CHAT_MSG_CHANNEL_NOTICE"][self] then + self:UnregisterEvent("CHAT_MSG_CHANNEL_NOTICE") + end + if self.registry["MEETINGSTONE_CHANGED"] and self.registry["MEETINGSTONE_CHANGED"][self] then + self:UnregisterEvent("MEETINGSTONE_CHANGED") + end + if self.registry["MINIMAP_ZONE_CHANGED"] and self.registry["MINIMAP_ZONE_CHANGED"][self] then + self:UnregisterEvent("MINIMAP_ZONE_CHANGED") + end + if self.registry["LANGUAGE_LIST_CHANGED"] and self.registry["LANGUAGE_LIST_CHANGED"][self] then + self:UnregisterEvent("LANGUAGE_LIST_CHANGED") + end + end + registeringFromAceEvent = true + local f = function() + self.playerLogin = true + self:ScheduleEvent("AceEvent_FullyInitialized", func, 1) + end + self:RegisterEvent("MEETINGSTONE_CHANGED", f, true) + self:RegisterEvent("CHAT_MSG_CHANNEL_NOTICE", function() + self:ScheduleEvent("AceEvent_FullyInitialized", func, 0.05) + end) + self:RegisterEvent("LANGUAGE_LIST_CHANGED", function() + if self.registry["MEETINGSTONE_CHANGED"] and self.registry["MEETINGSTONE_CHANGED"][self] then + registeringFromAceEvent = true + self:UnregisterEvent("MEETINGSTONE_CHANGED") + self:RegisterEvent("MINIMAP_ZONE_CHANGED", f, true) + registeringFromAceEvent = nil + end + end) + self:ScheduleEvent("AceEvent_FullyInitialized", func, 10) + registeringFromAceEvent = nil + end + + self.super.activate(self, oldLib, oldDeactivate) + if oldLib then + oldDeactivate(oldLib) + end +end + +AceLibrary:Register(AceEvent, MAJOR_VERSION, MINOR_VERSION, AceEvent.activate) +AceEvent = AceLibrary(MAJOR_VERSION) diff --git a/libs/AceHook-2.1/AceHook-2.1.lua b/libs/AceHook-2.1/AceHook-2.1.lua new file mode 100755 index 0000000..8dc7bb0 --- /dev/null +++ b/libs/AceHook-2.1/AceHook-2.1.lua @@ -0,0 +1,569 @@ +--[[ +Name: AceHook-2.1 +Revision: $Rev: 17638 $ +Developed by: The Ace Development Team (http://www.wowace.com/index.php/The_Ace_Development_Team) +Inspired By: Ace 1.x by Turan (turan@gryphon.com) +Website: http://www.wowace.com/ +Documentation: http://www.wowace.com/index.php/AceHook-2.1 +SVN: http://svn.wowace.com/root/trunk/Ace2/AceHook-2.1 +Description: Mixin to allow for safe hooking of functions, methods, and scripts. +Dependencies: AceLibrary, AceOO-2.0 +]] + +local MAJOR_VERSION = "AceHook-2.1" +local MINOR_VERSION = "$Revision: 17638 $" + +-- This ensures the code is only executed if the libary doesn't already exist, or is a newer version +if not AceLibrary then error(MAJOR_VERSION .. " requires AceLibrary.") end +if not AceLibrary:IsNewVersion(MAJOR_VERSION, MINOR_VERSION) then return end + +if loadstring("return function(...) return ... end") and AceLibrary:HasInstance(MAJOR_VERSION) then return end -- lua51 check +if not AceLibrary:HasInstance("AceOO-2.0") then error(MAJOR_VERSION .. " requires AceOO-2.0") end + +--[[--------------------------------------------------------------------------------- + Create the library object +----------------------------------------------------------------------------------]] + +local AceOO = AceLibrary:GetInstance("AceOO-2.0") +local AceHook = AceOO.Mixin { + "Hook", + "HookScript", + "SecureHook", + "Unhook", + "UnhookAll", + "HookReport", + "IsHooked", + } + +--[[--------------------------------------------------------------------------------- + Library Definitions +----------------------------------------------------------------------------------]] + +local protFuncs = { + CameraOrSelectOrMoveStart = true, CameraOrSelectOrMoveStop = true, + TurnOrActionStart = true, TurnOrActionStop = true, + PitchUpStart = true, PitchUpStop = true, + PitchDownStart = true, PitchDownStop = true, + MoveBackwardStart = true, MoveBackwardStop = true, + MoveForwardStart = true, MoveForwardStop = true, + Jump = true, StrafeLeftStart = true, + StrafeLeftStop = true, StrafeRightStart = true, + StrafeRightStop = true, ToggleMouseMove = true, + ToggleRun = true, TurnLeftStart = true, + TurnLeftStop = true, TurnRightStart = true, + TurnRightStop = true, +} + +local function issecurevariable(x) + if protFuncs[x] then + return 1 + else + return nil + end +end + +local _G = getfenv(0) + +local function hooksecurefunc(arg1, arg2, arg3) + if type(arg1) == "string" then + arg1, arg2, arg3 = _G, arg1, arg2 + end + local orig = arg1[arg2] + arg1[arg2] = function(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20) + local x1,x2,x3,x4,x5,x6,x7,x8,x9,x10,x11,x12,x13,x14,x15,x16,x17,x18,x19,x20 = orig(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20) + + arg3(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20) + + return x1,x2,x3,x4,x5,x6,x7,x8,x9,x10,x11,x12,x13,x14,x15,x16,x17,x18,x19,x20 + end +end + +local protectedScripts = { + OnClick = true, +} + + +local handlers, scripts, actives, registry + +--[[--------------------------------------------------------------------------------- + Private definitions (Not exposed) +----------------------------------------------------------------------------------]] + +local new, del +do + local list = setmetatable({}, {__mode = "k"}) + function new() + local t = next(list) + if not t then + return {} + end + list[t] = nil + return t + end + + function del(t) + setmetatable(t, nil) + for k in pairs(t) do + t[k] = nil + end + list[t] = true + end +end + +local function createFunctionHook(self, func, handler, orig, secure) + if not secure then + if type(handler) == "string" then + -- The handler is a method, need to self it + local uid + uid = function(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20) + if actives[uid] then + return self[handler](self, a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20) + else + return orig(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20) + end + end + return uid + else + -- The handler is a function, just call it + local uid + uid = function(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20) + if actives[uid] then + return handler(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20) + else + return orig(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20) + end + end + return uid + end + else + -- secure hooks don't call the original method + if type(handler) == "string" then + -- The handler is a method, need to self it + local uid + uid = function(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20) + if actives[uid] then + return self[handler](self, a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20) + end + end + return uid + else + -- The handler is a function, just call it + local uid + uid = function(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20) + if actives[uid] then + return handler(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20) + end + end + return uid + end + end +end + +local function createMethodHook(self, object, method, handler, orig, secure, script) + if script then + if type(handler) == "string" then + local uid + uid = function() + if actives[uid] then + return self[handler](self, object) + else + return orig() + end + end + return uid + else + -- The handler is a function, just call it + local uid + uid = function() + if actives[uid] then + return handler(object) + else + return orig() + end + end + return uid + end + elseif not secure then + if type(handler) == "string" then + local uid + uid = function(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20) + if actives[uid] then + return self[handler](self, a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20) + else + return orig(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20) + end + end + return uid + else + -- The handler is a function, just call it + local uid + uid = function(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20) + if actives[uid] then + return handler(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20) + else + return orig(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20) + end + end + return uid + end + else + -- secure hooks don't call the original method + if type(handler) == "string" then + local uid + uid = function(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20) + if actives[uid] then + return self[handler](self, a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20) + end + end + return uid + else + -- The handler is a function, just call it + local uid + uid = function(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20) + if actives[uid] then + return handler(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20) + end + end + return uid + end + end +end + +local function hookFunction(self, func, handler, secure) + local orig = _G[func] + + if not orig or type(orig) ~= "function" then + AceHook:error("Attempt to hook a non-existant function %q", func) + end + + if not handler then + handler = func + end + + if registry[self][func] then + local uid = registry[self][func] + + if actives[uid] then + -- We have an active hook from this source. Don't multi-hook + AceHook:error("%q already has an active hook from this source.", func) + end + + if handlers[uid] == handler then + -- The hook is inactive, so reactivate it + actives[uid] = true + return + else + AceHook:error("There is a stale hook for %q can't hook or reactivate.", func) + end + end + + if type(handler) == "string" then + if type(self[handler]) ~= "function" then + AceHook:error("Could not find the the handler %q when hooking function %q", handler, func) + end + elseif type(handler) ~= "function" then + AceHook:error("Could not find the handler you supplied when hooking %q", func) + end + + local uid = createFunctionHook(self, func, handler, orig, secure) + registry[self][func] = uid + actives[uid] = true + handlers[uid] = handler + + if not secure then + _G[func] = uid + self.hooks[func] = orig + else + hooksecurefunc(func, uid) + end +end + +local function unhookFunction(self, func) + if not registry[self][func] then + AceHook:error("Tried to unhook %q which is not currently hooked.", func) + end + + local uid = registry[self][func] + + if actives[uid] then + -- See if we own the global function + if self.hooks[func] and _G[func] == uid then + _G[func] = self.hooks[func] + self.hooks[func] = nil + registry[self][func] = nil + handlers[uid] = nil + scripts[uid] = nil + actives[uid] = nil + -- Magically all-done + else + actives[uid] = nil + end + end +end + +local function hookMethod(self, obj, method, handler, script, secure) + if not handler then + handler = method + end + + if not obj or type(obj) ~= "table" then + AceHook:error("The object you supplied could not be found, or isn't a table.") + end + + local uid = registry[self][obj] and registry[self][obj][method] + if uid then + if actives[uid] then + -- We have an active hook from this source. Don't multi-hook + AceHook:error("%q already has an active hook from this source.", method) + end + + if handlers[uid] == handler then + -- The hook is inactive, reactivate it. + actives[uid] = true + return + else + AceHook:error("There is a stale hook for %q can't hook or reactivate.", method) + end + end + + if type(handler) == "string" then + if type(self[handler]) ~= "function" then + AceHook:error("Could not find the handler %q you supplied when hooking method %q", handler, method) + end + elseif type(handler) ~= "function" then + AceHook:error("Could not find the handler you supplied when hooking method %q", method) + end + + local orig + if script then + if not obj.GetScript then + AceHook:error("The object you supplied does not have a GetScript method.") + end + if not obj:HasScript(method) then + AceHook:error("The object you supplied doesn't allow the %q method.", method) + end + + orig = obj:GetScript(method) + if type(orig) ~= "function" then + -- Sometimes there is not a original function for a script. + orig = function() end + end + else + orig = obj[method] + end + if not orig then + AceHook:error("Could not find the method or script %q you are trying to hook.", method) + end + + if not registry[self][obj] then + registry[self][obj] = new() + end + + if not self.hooks[obj] then + self.hooks[obj] = new() + end + + local uid = createMethodHook(self, obj, method, handler, orig, secure, script) + registry[self][obj][method] = uid + actives[uid] = true + handlers[uid] = handler + scripts[uid] = script and true or nil + + if script then + obj:SetScript(method, uid) + self.hooks[obj][method] = orig + elseif not secure then + obj[method] = uid + self.hooks[obj][method] = orig + else + hooksecurefunc(obj, method, uid) + end +end + +local function unhookMethod(self, obj, method) + if not registry[self][obj] or not registry[self][obj][method] then + AceHook:error("Attempt to unhook a method %q that is not currently hooked.", method) + return + end + + local uid = registry[self][obj][method] + + if actives[uid] then + if scripts[uid] then -- If this is a script + if obj:GetScript(method) == uid then + -- We own the script. Revert to normal. + obj:SetScript(method, self.hooks[obj][method]) + self.hooks[obj][method] = nil + registry[self][obj][method] = nil + handlers[uid] = nil + scripts[uid] = nil + actives[uid] = nil + else + actives[uid] = nil + end + else + if self.hooks[obj] and self.hooks[obj][method] and obj[method] == uid then + -- We own the method. Revert to normal. + obj[method] = self.hooks[obj][method] + self.hooks[obj][method] = nil + registry[self][obj][method] = nil + handlers[uid] = nil + actives[uid] = nil + else + actives[uid] = nil + end + end + end + if self.hooks[obj] and not next(self.hooks[obj]) then + self.hooks[obj] = del(self.hooks[obj]) + end + if not next(registry[self][obj]) then + registry[self][obj] = del(registry[self][obj]) + end +end + +-- ("function" [, handler] [, hookSecure]) or (object, "method" [, handler] [, hookSecure]) +function AceHook:Hook(object, method, handler, hookSecure) + if type(object) == "string" then + method, handler, hookSecure = object, method, handler + if handler == true then + handler, hookSecure = nil, true + end + if not hookSecure and issecurevariable(method) then + AceHook:error("Attempt to hook secure function %q. Use `SecureHook' or add `true' to the argument list to override.", method) + end + AceHook:argCheck(handler, 3, "function", "string", "nil") + AceHook:argCheck(hookSecure, 4, "boolean", "nil") + hookFunction(self, method, handler, false) + else + if handler == true then + handler, hookSecure = nil, true + end + if not hookSecure and issecurevariable(object, method) then + AceHook:error("Attempt to hook secure method %q. Use `SecureHook' or add `true' to the argument list to override.", method) + end + AceHook:argCheck(object, 2, "table") + AceHook:argCheck(method, 3, "string") + AceHook:argCheck(handler, 4, "function", "string", "nil") + AceHook:argCheck(hookSecure, 5, "boolean", "nil") + hookMethod(self, object, method, handler, false, false) + end +end + +-- ("function", handler) or (object, "method", handler) +function AceHook:SecureHook(object, method, handler) + if type(object) == "string" then + method, handler = object, method + AceHook:argCheck(handler, 3, "function", "string", "nil") + hookFunction(self, method, handler, true) + else + AceHook:argCheck(object, 2, "table") + AceHook:argCheck(method, 3, "string") + AceHook:argCheck(handler, 4, "function", "string", "nil") + hookMethod(self, object, method, handler, false, true) + end +end + +function AceHook:HookScript(frame, script, handler) + AceHook:argCheck(frame, 2, "table") + if not frame[0] or type(frame.IsFrameType) ~= "function" then + AceHook:error("Bad argument #2 to `HookScript'. Expected frame.") + end + AceHook:argCheck(script, 3, "string") + AceHook:argCheck(handler, 4, "function", "string", "nil") + hookMethod(self, frame, script, handler, true, false) +end + +-- ("function") or (object, "method") +function AceHook:IsHooked(obj, method) + if type(obj) == "string" then + if registry[self][obj] and actives[registry[self][obj]] then + return true, handlers[registry[self][obj]] + end + else + AceHook:argCheck(obj, 2, "string", "table") + AceHook:argCheck(method, 3, "string") + if registry[self][obj] and registry[self][obj][method] and actives[registry[self][obj][method]] then + return true, handlers[registry[self][obj][method]] + end + end + + return false, nil +end + +-- ("function") or (object, "method") +function AceHook:Unhook(obj, method) + if type(obj) == "string" then + unhookFunction(self, obj) + else + AceHook:argCheck(obj, 2, "string", "table") + AceHook:argCheck(method, 3, "string") + unhookMethod(self, obj, method) + end +end + +function AceHook:UnhookAll() + for key, value in pairs(registry[self]) do + if type(key) == "table" then + for method in pairs(value) do + self:Unhook(key, method) + end + else + self:Unhook(key) + end + end +end + +function AceHook:HookReport() + DEFAULT_CHAT_FRAME:AddMessage("This is a list of all active hooks for this object:") + if not next(registry[self]) then + DEFAULT_CHAT_FRAME:AddMessage("No hooks") + end + + for key, value in pairs(registry[self]) do + if type(value) == "table" then + for method, uid in pairs(value) do + DEFAULT_CHAT_FRAME:AddMessage(string.format("object: %s method: %q |cff%s|r%s", tostring(key), method, actives[uid] and "00ff00Active" or "ffff00Inactive", not self.hooks[key][method] and " |cff7f7fff-Secure-|r" or "")) + end + else + DEFAULT_CHAT_FRAME:AddMessage(string.format("function: %q |cff%s|r%s", tostring(key), actives[value] and "00ff00Active" or "ffff00Inactive", not self.hooks[key] and " |cff7f7fff-Secure-|r" or "")) + end + end +end + +function AceHook:OnInstanceInit(object) + if not object.hooks then + object.hooks = new() + end + if not registry[object] then + registry[object] = new() + end +end + +AceHook.OnManualEmbed = AceHook.OnInstanceInit + +function AceHook:OnEmbedDisable(target) + self.UnhookAll(target) +end + +local function activate(self, oldLib, oldDeactivate) + AceHook = self + + self.handlers = oldLib and oldLib.handlers or {} + self.registry = oldLib and oldLib.registry or {} + self.scripts = oldLib and oldLib.scripts or {} + self.actives = oldLib and oldLib.actives or {} + + handlers = self.handlers + registry = self.registry + scripts = self.scripts + actives = self.actives + + AceHook.super.activate(self, oldLib, oldDeactivate) + + if oldDeactivate then + oldDeactivate(oldLib) + end +end + +AceLibrary:Register(AceHook, MAJOR_VERSION, MINOR_VERSION, activate) diff --git a/libs/AceLibrary/AceLibrary.lua b/libs/AceLibrary/AceLibrary.lua new file mode 100755 index 0000000..b5ec3fa --- /dev/null +++ b/libs/AceLibrary/AceLibrary.lua @@ -0,0 +1,751 @@ +--[[ +Name: AceLibrary +Revision: $Rev: 17722 $ +Developed by: The Ace Development Team (http://www.wowace.com/index.php/The_Ace_Development_Team) +Inspired By: Iriel (iriel@vigilance-committee.org) + Tekkub (tekkub@gmail.com) + Revision: $Rev: 17722 $ +Website: http://www.wowace.com/ +Documentation: http://www.wowace.com/index.php/AceLibrary +SVN: http://svn.wowace.com/root/trunk/Ace2/AceLibrary +Description: Versioning library to handle other library instances, upgrading, + and proper access. + It also provides a base for libraries to work off of, providing + proper error tools. It is handy because all the errors occur in the + file that called it, not in the library file itself. +Dependencies: None +]] + +local ACELIBRARY_MAJOR = "AceLibrary" +local ACELIBRARY_MINOR = "$Revision: 17723 $" + +if loadstring("return function(...) return ... end") and AceLibrary and AceLibrary:HasInstance(ACELIBRARY_MAJOR) then return end -- lua51 check + +local _G = getfenv(0) +local previous = _G[ACELIBRARY_MAJOR] +if previous and not previous:IsNewVersion(ACELIBRARY_MAJOR, ACELIBRARY_MINOR) then return end + +local function donothing() end + +local table_setn = string.find(GetBuildInfo(), "^2%.") and donothing or table.setn + +local string_gfind = string.gmatch or string.gfind + +-- The a1,a2,a3,a4,a5,a6,a7,a8,a9,a10 are used here instead of ... (vararg) +-- It seems ugly but in Lua 5.1, Vararg functions create a table `arg' each +-- time the function is called +local function safecall(func,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10) + local success, err = pcall(func,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10) + if not success then geterrorhandler()(err) end +end + +-- @function destroyTable +-- @brief remove all the contents of a table +-- @param t table to destroy +local function destroyTable(t) + setmetatable(t, nil) + for k,v in pairs(t) do t[k] = nil end + table_setn(t, 0) +end + +-- @table AceLibrary +-- @brief System to handle all versioning of libraries. +local AceLibrary = {} +local AceLibrary_mt = {} +setmetatable(AceLibrary, AceLibrary_mt) + +local error +do + local tmp = {} + function error(self, message, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) + if type(self) ~= "table" then + _G.error(string.format("Bad argument #1 to `error' (table expected, got %s)", type(self)), 2) + end + + local stack = debugstack() + if not message then + local _,_,second = string.find(stack, "\n(.-)\n") + message = "error raised! " .. second + else + destroyTable(tmp) + table.insert(tmp, a1) + table.insert(tmp, a2) + table.insert(tmp, a3) + table.insert(tmp, a4) + table.insert(tmp, a5) + table.insert(tmp, a6) + table.insert(tmp, a7) + table.insert(tmp, a8) + table.insert(tmp, a9) + table.insert(tmp, a10) + table.insert(tmp, a11) + table.insert(tmp, a12) + table.insert(tmp, a13) + table.insert(tmp, a14) + table.insert(tmp, a15) + table.insert(tmp, a16) + table.insert(tmp, a17) + table.insert(tmp, a18) + table.insert(tmp, a19) + table.insert(tmp, a20) + for i = 1,table.getn(tmp) do + tmp[i] = tostring(tmp[i]) + end + for i = 1,10 do + table.insert(tmp, "nil") + end + message = string.format(message, unpack(tmp)) + end + + if getmetatable(self) and getmetatable(self).__tostring then + message = string.format("%s: %s", tostring(self), message) + elseif type(rawget(self, 'GetLibraryVersion')) == "function" and AceLibrary:HasInstance(self:GetLibraryVersion()) then + message = string.format("%s: %s", self:GetLibraryVersion(), message) + elseif type(rawget(self, 'class')) == "table" and type(rawget(self.class, 'GetLibraryVersion')) == "function" and AceLibrary:HasInstance(self.class:GetLibraryVersion()) then + message = string.format("%s: %s", self.class:GetLibraryVersion(), message) + end + + local first = string.gsub(stack, "\n.*", "") + local file = string.gsub(first, ".*\\(.*).lua:%d+: .*", "%1") + file = string.gsub(file, "([%(%)%.%*%+%-%[%]%?%^%$%%])", "%%%1") + + local i = 0 + for s in string_gfind(stack, "\n([^\n]*)") do + i = i + 1 + if not string.find(s, file .. "%.lua:%d+:") then + file = string.gsub(s, "^.*\\(.*).lua:%d+: .*", "%1") + file = string.gsub(file, "([%(%)%.%*%+%-%[%]%?%^%$%%])", "%%%1") + break + end + end + local j = 0 + for s in string_gfind(stack, "\n([^\n]*)") do + j = j + 1 + if j > i and not string.find(s, file .. "%.lua:%d+:") then + _G.error(message, j + 1) + return + end + end + _G.error(message, 2) + return + end +end + +local function assert(self, condition, message, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) + if not condition then + if not message then + local stack = debugstack() + local _,_,second = string.find(stack, "\n(.-)\n") + message = "assertion failed! " .. second + end + error(self, message, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) + return + end + return condition +end + +local function argCheck(self, arg, num, kind, kind2, kind3, kind4, kind5) + if type(num) ~= "number" then + error(self, "Bad argument #3 to `argCheck' (number expected, got %s)", type(num)) + elseif type(kind) ~= "string" then + error(self, "Bad argument #4 to `argCheck' (string expected, got %s)", type(kind)) + end + local errored = false + arg = type(arg) + if arg ~= kind and arg ~= kind2 and arg ~= kind3 and arg ~= kind4 and arg ~= kind5 then + local _,_,func = string.find(debugstack(), "`argCheck'.-([`<].-['>])") + if not func then + _,_,func = string.find(debugstack(), "([`<].-['>])") + end + if kind5 then + error(self, "Bad argument #%s to %s (%s, %s, %s, %s, or %s expected, got %s)", tonumber(num) or 0/0, func, kind, kind2, kind3, kind4, kind5, arg) + elseif kind4 then + error(self, "Bad argument #%s to %s (%s, %s, %s, or %s expected, got %s)", tonumber(num) or 0/0, func, kind, kind2, kind3, kind4, arg) + elseif kind3 then + error(self, "Bad argument #%s to %s (%s, %s, or %s expected, got %s)", tonumber(num) or 0/0, func, kind, kind2, kind3, arg) + elseif kind2 then + error(self, "Bad argument #%s to %s (%s or %s expected, got %s)", tonumber(num) or 0/0, func, kind, kind2, arg) + else + error(self, "Bad argument #%s to %s (%s expected, got %s)", tonumber(num) or 0/0, func, kind, arg) + end + end +end + +local function pcall(self, func, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) + a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20 = _G.pcall(func, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) + if not a1 then + error(self, string.gsub(a2, ".-%.lua:%d-: ", "")) + else + return a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20 + end +end + +local addToPositions +do + local recurse = {} + function addToPositions(t, major) + if not AceLibrary.positions[t] or AceLibrary.positions[t] == major then + rawset(t, recurse, true) + AceLibrary.positions[t] = major + for k,v in pairs(t) do + if type(v) == "table" and not rawget(v, recurse) then + addToPositions(v, major) + end + if type(k) == "table" and not rawget(k, recurse) then + addToPositions(k, major) + end + end + local mt = getmetatable(t) + if mt and not rawget(mt, recurse) then + addToPositions(mt, major) + end + rawset(t, recurse, nil) + end + end +end + +local function svnRevisionToNumber(text) + if type(text) == "string" then + if string.find(text, "^%$Revision: (%d+) %$$") then + return tonumber((string.gsub(text, "^%$Revision: (%d+) %$$", "%1"))) + elseif string.find(text, "^%$Rev: (%d+) %$$") then + return tonumber((string.gsub(text, "^%$Rev: (%d+) %$$", "%1"))) + elseif string.find(text, "^%$LastChangedRevision: (%d+) %$$") then + return tonumber((string.gsub(text, "^%$LastChangedRevision: (%d+) %$$", "%1"))) + end + elseif type(text) == "number" then + return text + end + return nil +end + +-- @function crawlReplace +-- @brief replace all keys/values of one value to another +-- @param t table to modify +-- @param to the value to replace +-- @param from the value to be replaced +-- @example crawlReplace({a="b",b="c"}, "d", "b") => {a="d",d="c"} +local crawlReplace +do + local recurse = {} + local function func(t, to, from) + recurse[t] = true + local mt = getmetatable(t) + setmetatable(t, nil) + rawset(t, to, rawget(t, from)) + rawset(t, from, nil) + for k,v in pairs(t) do + if v == from then + t[k] = to + elseif type(v) == "table" then + if not recurse[v] then + func(v, to, from) + end + end + + if type(k) == "table" then + if not recurse[k] then + func(k, to, from) + end + end + end + setmetatable(t, mt) + if mt then + if mt == from then + setmetatable(t, to) + elseif not recurse[mt] then + func(mt, to, from) + end + end + end + function crawlReplace(t, to, from) + destroyTable(recurse) + func(t, to, from) + end +end + +local function isFrame(frame) + return type(frame) == "table" and type(rawget(frame, 0)) == "userdata" and type(rawget(frame, 'IsFrameType')) == "function" and getmetatable(frame) and type(rawget(getmetatable(frame), '__index')) == "function" +end + +-- operator new/del with memory management +local new, del +do + -- this table contains the created tables which are not in use + local tables = setmetatable({}, {__mode = "k"}) + + function new() + local t = next(tables) + if t then + tables[t] = nil + return t + else + return {} + end + end + + -- recursively destruct the table + function del(t, depth) + if depth and depth > 0 then + for k,v in pairs(t) do + if type(v) == "table" and not isFrame(v) then + del(v, depth - 1) + end + end + end + destroyTable(t) + tables[t] = true + end +end + +-- @function copyTable +-- @brief Create a shallow copy of a table and return it. +-- @param from The table to copy from +-- @return A shallow copy of the table +local function copyTable(from) + local to = new() + for k,v in pairs(from) do to[k] = v end + table_setn(to, table.getn(from)) + setmetatable(to, getmetatable(from)) + return to +end + +-- @function deepTransfer +-- @brief Fully transfer all data, keeping proper previous table +-- backreferences stable. +-- @param to The table with which data is to be injected into +-- @param from The table whose data will be injected into the first +-- @param saveFields If available, a shallow copy of the basic data is saved +-- in here. +-- @param list The account of table references +-- @param list2 The current status on which tables have been traversed. +local deepTransfer +do + -- @function examine + -- @brief Take account of all the table references to be shared + -- between the to and from tables. + -- @param to The table with which data is to be injected into + -- @param from The table whose data will be injected into the first + -- @param list An account of the table references + local function examine(to, from, list, major) + list[from] = to + for k,v in pairs(from) do + if rawget(to, k) and type(from[k]) == "table" and type(to[k]) == "table" and not list[from[k]] then + if from[k] == to[k] then + list[from[k]] = to[k] + elseif AceLibrary.positions[from[v]] ~= major and AceLibrary.positions[from[v]] then + list[from[k]] = from[k] + elseif not list[from[k]] then + examine(to[k], from[k], list, major) + end + end + end + return list + end + + function deepTransfer(to, from, saveFields, major, list, list2) + setmetatable(to, nil) + local createdList + if not list then + createdList = true + list = new() + list2 = new() + examine(to, from, list, major) + end + list2[to] = to + for k,v in pairs(to) do + if type(rawget(from, k)) ~= "table" or type(v) ~= "table" or isFrame(v) then + if saveFields then + saveFields[k] = v + end + to[k] = nil + elseif v ~= _G then + if saveFields then + saveFields[k] = copyTable(v) + end + end + end + for k in pairs(from) do + if rawget(to, k) and to[k] ~= from[k] and AceLibrary.positions[to[k]] == major and from[k] ~= _G then + if not list2[to[k]] then + deepTransfer(to[k], from[k], nil, major, list, list2) + end + to[k] = list[to[k]] or list2[to[k]] + else + rawset(to, k, from[k]) + end + end + table_setn(to, table.getn(from)) + setmetatable(to, getmetatable(from)) + local mt = getmetatable(to) + if mt then + if list[mt] then + setmetatable(to, list[mt]) + elseif mt.__index and list[mt.__index] then + mt.__index = list[mt.__index] + end + end + destroyTable(from) + if createdList then + del(list) + del(list2) + end + end +end + +-- @method TryToLoadStandalone +-- @brief Attempt to find and load a standalone version of the requested library +-- @param major A string representing the major version +-- @return If library is found, return values from the call to LoadAddOn are returned +-- If the library has been requested previously, nil is returned. +local function TryToLoadStandalone(major) + if not AceLibrary.scannedlibs then AceLibrary.scannedlibs = {} end + if AceLibrary.scannedlibs[major] then return end + + AceLibrary.scannedlibs[major] = true + + local name, _, _, enabled, loadable = GetAddOnInfo(major) + if loadable then + return LoadAddOn(name) + end + + for i=1,GetNumAddOns() do + if GetAddOnMetadata(i, "X-AceLibrary-"..major) then + local name, _, _, enabled, loadable = GetAddOnInfo(i) + if loadable then + return LoadAddOn(name) + end + end + end +end + +local function minorCheck(minor, func) + if type(minor) == "string" then + local m = svnRevisionToNumber(minor) + if m then + return m + else + _G.error(string.format("Bad argument #3 to `%s'. Must be a number or SVN revision string. %q is not appropriate", func, minor), 3) + end + else + return minor + end +end + +-- @method IsNewVersion +-- @brief Obtain whether the supplied version would be an upgrade to the +-- current version. This allows for bypass code in library +-- declaration. +-- @param major A string representing the major version +-- @param minor An integer or an svn revision string representing the minor version +-- @return whether the supplied version would be newer than what is +-- currently available. +function AceLibrary:IsNewVersion(major, minor) + argCheck(self, major, 2, "string") + TryToLoadStandalone(major) + minor = minorCheck(minor, "IsNewVersion") + argCheck(self, minor, 3, "number") + local data = self.libs[major] + if not data then + return true + end + return data.minor < minor +end + +-- @method HasInstance +-- @brief Returns whether an instance exists. This allows for optional support of a library. +-- @param major A string representing the major version. +-- @param minor (optional) An integer or an svn revision string representing the minor version. +-- @return Whether an instance exists. +function AceLibrary:HasInstance(major, minor) + argCheck(self, major, 2, "string") + TryToLoadStandalone(major) + + if minor then + minor = minorCheck(minor, "HasInstance") + argCheck(self, minor, 3, "number") + if not self.libs[major] then + return + end + return self.libs[major].minor == minor + end + return self.libs[major] and true +end + +-- @method GetInstance +-- @brief Returns the library with the given major/minor version. +-- @param major A string representing the major version. +-- @param minor (optional) An integer or an svn revision string representing the minor version. +-- @return The library with the given major/minor version. +function AceLibrary:GetInstance(major, minor) + argCheck(self, major, 2, "string") + TryToLoadStandalone(major) + + local data = self.libs[major] + if not data then + _G.error(string.format("Cannot find a library instance of %s.", major), 2) + return + end + if minor then + minor = minorCheck(minor, "GetInstance") + argCheck(self, minor, 2, "number") + if data.minor ~= minor then + _G.error(string.format("Cannot find a library instance of %s, minor version %d.", major, minor), 2) + return + end + end + return data.instance +end + +-- Syntax sugar. AceLibrary("FooBar-1.0") +AceLibrary_mt.__call = AceLibrary.GetInstance + +local AceEvent + +-- @method Register +-- @brief Registers a new version of a given library. +-- @param newInstance the library to register +-- @param major the major version of the library +-- @param minor the minor version of the library +-- @param activateFunc (optional) A function to be called when the library is +-- fully activated. Takes the arguments +-- (newInstance [, oldInstance, oldDeactivateFunc]). If +-- oldInstance is given, you should probably call +-- oldDeactivateFunc(oldInstance). +-- @param deactivateFunc (optional) A function to be called by a newer library's +-- activateFunc. +-- Take the arguments +-- (oldInstance) +-- @param externalFunc (optional) A function to be called whenever a new +-- library is registered. +-- Takes the arguments +-- (instance, otherInstanceName, otherInstance) +-- This function will be called on two sides between the new +-- library and old ones +function AceLibrary:Register(newInstance, major, minor, activateFunc, deactivateFunc, externalFunc) + argCheck(self, newInstance, 2, "table") + argCheck(self, major, 3, "string") + if type(minor) == "string" then + minor = minorCheck(minor, "Register") + end + argCheck(self, minor, 4, "number") + if math.floor(minor) ~= minor or minor < 0 then + error(self, "Bad argument #4 to `Register' (integer >= 0 expected, got %s)", minor) + end + argCheck(self, activateFunc, 5, "function", "nil") + argCheck(self, deactivateFunc, 6, "function", "nil") + argCheck(self, externalFunc, 7, "function", "nil") + if not deactivateFunc then + deactivateFunc = donothing + end + local data = self.libs[major] + local isAceLibrary = (AceLibrary == newInstance) + + if not data then + -- This is new + local instance = copyTable(newInstance) + crawlReplace(instance, instance, newInstance) + destroyTable(newInstance) + if isAceLibrary then + self = instance + AceLibrary = instance + end + self.libs[major] = { + instance = instance, + minor = minor, + deactivateFunc = deactivateFunc, + externalFunc = externalFunc, + } + rawset(instance, 'GetLibraryVersion', function(self) + return major, minor + end) + if not rawget(instance, 'error') then + rawset(instance, 'error', error) + end + if not rawget(instance, 'assert') then + rawset(instance, 'assert', assert) + end + if not rawget(instance, 'argCheck') then + rawset(instance, 'argCheck', argCheck) + end + if not rawget(instance, 'pcall') then + rawset(instance, 'pcall', pcall) + end + addToPositions(instance, major) + if activateFunc then + safecall(activateFunc, instance, nil, nil) -- no old version, so explicit nil + end + + if externalFunc then + for k,data in pairs(self.libs) do + if k ~= major then + safecall(externalFunc, instance, k, data.instance) + end + end + end + + for k,data in pairs(self.libs) do + if k ~= major and data.externalFunc then + safecall(data.externalFunc, data.instance, major, instance) + end + end + if major == "AceEvent-2.0" then + AceEvent = instance + end + if AceEvent then + AceEvent.TriggerEvent(self, "AceLibrary_Register", major, instance) + end + + return instance + end + local instance = data.instance + if minor <= data.minor then + -- This one is already obsolete, raise an error. + _G.error(string.format("Obsolete library registered. %s is already registered at version %d. You are trying to register version %d. Hint: if not AceLibrary:IsNewVersion(%q, %d) then return end", major, data.minor, minor, major, minor), 2) + return + end + -- This is an update + local oldInstance = new() + + addToPositions(newInstance, major) + + local old_error, old_assert, old_argCheck, old_pcall + if isAceLibrary then + self = instance + AceLibrary = instance + + old_error = instance.error + old_assert = instance.assert + old_argCheck = instance.argCheck + old_pcall = instance.pcall + + self.error = error + self.assert = assert + self.argCheck = argCheck + self.pcall = pcall + end + deepTransfer(instance, newInstance, oldInstance, major) + crawlReplace(instance, instance, newInstance) + local oldDeactivateFunc = data.deactivateFunc + data.minor = minor + data.deactivateFunc = deactivateFunc + data.externalFunc = externalFunc + rawset(instance, 'GetLibraryVersion', function(self) + return major, minor + end) + if not rawget(instance, 'error') then + rawset(instance, 'error', error) + end + if not rawget(instance, 'assert') then + rawset(instance, 'assert', assert) + end + if not rawget(instance, 'argCheck') then + rawset(instance, 'argCheck', argCheck) + end + if not rawget(instance, 'pcall') then + rawset(instance, 'pcall', pcall) + end + if isAceLibrary then + for _,v in pairs(self.libs) do + local i = type(v) == "table" and v.instance + if type(i) == "table" then + if not rawget(i, 'error') or i.error == old_error then + rawset(i, 'error', error) + end + if not rawget(i, 'assert') or i.assert == old_assert then + rawset(i, 'assert', assert) + end + if not rawget(i, 'argCheck') or i.argCheck == old_argCheck then + rawset(i, 'argCheck', argCheck) + end + if not rawget(i, 'pcall') or i.pcall == old_pcall then + rawset(i, 'pcall', pcall) + end + end + end + end + if activateFunc then + safecall(activateFunc, instance, oldInstance, oldDeactivateFunc) + else + safecall(oldDeactivateFunc, oldInstance) + end + del(oldInstance) + + if externalFunc then + for k,data in pairs(self.libs) do + if k ~= major then + safecall(externalFunc, instance, k, data.instance) + end + end + end + + return instance +end + +do + local function iter(t, k) + k = next(t, k) + if not k then + return nil + else + return k, t[k].instance + end + end + + -- Example + -- for major, instance in AceLibrary:IterateLibraries() do + -- ... + -- end + function AceLibrary:IterateLibraries() + return iter, self.libs, nil + end +end + +-- @function Activate +-- @brief The activateFunc for AceLibrary itself. Called when +-- AceLibrary properly registers. +-- @param self Reference to AceLibrary +-- @param oldLib (optional) Reference to an old version of AceLibrary +-- @param oldDeactivate (optional) Function to deactivate the old lib +local function activate(self, oldLib, oldDeactivate) + if not self.libs then + if oldLib then + self.libs = oldLib.libs + self.scannedlibs = oldLib.scannedlibs + end + if not self.libs then + self.libs = {} + end + if not self.scannedlibs then + self.scannedlibs = {} + end + end + if not self.positions then + if oldLib then + self.positions = oldLib.positions + end + if not self.positions then + self.positions = setmetatable({}, { __mode = "k" }) + end + end + + -- Expose the library in the global environment + _G[ACELIBRARY_MAJOR] = self + + if oldDeactivate then + oldDeactivate(oldLib) + end +end + +if not previous then + previous = AceLibrary +end +if not previous.libs then + previous.libs = {} +end +AceLibrary.libs = previous.libs +if not previous.positions then + previous.positions = setmetatable({}, { __mode = "k" }) +end +AceLibrary.positions = previous.positions +AceLibrary:Register(AceLibrary, ACELIBRARY_MAJOR, ACELIBRARY_MINOR, activate) diff --git a/libs/AceLocale-2.2/AceLocale-2.2.lua b/libs/AceLocale-2.2/AceLocale-2.2.lua new file mode 100755 index 0000000..27ebe52 --- /dev/null +++ b/libs/AceLocale-2.2/AceLocale-2.2.lua @@ -0,0 +1,538 @@ +--[[ +Name: AceLocale-2.2 +Revision: $Rev: 17638 $ +Developed by: The Ace Development Team (http://www.wowace.com/index.php/The_Ace_Development_Team) +Inspired By: Ace 1.x by Turan (turan@gryphon.com) +Website: http://www.wowace.com/ +Documentation: http://www.wowace.com/index.php/AceLocale-2.2 +SVN: http://svn.wowace.com/root/trunk/Ace2/AceLocale-2.2 +Description: Localization library for addons to use to handle proper + localization and internationalization. +Dependencies: AceLibrary +]] + +local MAJOR_VERSION = "AceLocale-2.2" +local MINOR_VERSION = "$Revision: 17638 $" + +if not AceLibrary then error(MAJOR_VERSION .. " requires AceLibrary.") end +if not AceLibrary:IsNewVersion(MAJOR_VERSION, MINOR_VERSION) then return end + +if loadstring("return function(...) return ... end") and AceLibrary:HasInstance(MAJOR_VERSION) then return end -- lua51 check +local AceLocale = {} + +local DEFAULT_LOCALE = "enUS" +local _G = getfenv(0) + +local BASE_TRANSLATIONS, DEBUGGING, TRANSLATIONS, BASE_LOCALE, TRANSLATION_TABLES, REVERSE_TRANSLATIONS, STRICTNESS, DYNAMIC_LOCALES, CURRENT_LOCALE, NAME + +local rawget = rawget +local rawset = rawset +local type = type + +local newRegistries = {} +local scheduleClear + +local lastSelf +local __index = function(self, key) + lastSelf = self + local value = (rawget(self, TRANSLATIONS) or AceLocale.prototype)[key] + rawset(self, key, value) + return value +end + +local __newindex = function(self, k, v) + if type(v) ~= "function" and type(k) ~= "table" then + AceLocale.error(self, "Cannot change the values of an AceLocale instance.") + end + rawset(self, k, v) +end + +local __tostring = function(self) + if type(rawget(self, 'GetLibraryVersion')) == "function" then + return self:GetLibraryVersion() + else + return "AceLocale(" .. self[NAME] .. ")" + end +end + +local function clearCache(self) + if not rawget(self, BASE_TRANSLATIONS) then + return + end + + local cache = self[BASE_TRANSLATIONS] + rawset(self, REVERSE_TRANSLATIONS, nil) + + for k in pairs(self) do + if rawget(cache, k) ~= nil then + self[k] = nil + end + end + rawset(self, 'tmp', true) + self.tmp = nil +end + +local function refixInstance(instance) + if getmetatable(instance) then + setmetatable(instance, nil) + end + local translations = instance[TRANSLATIONS] + if translations then + if getmetatable(translations) then + setmetatable(translations, nil) + end + local baseTranslations = instance[BASE_TRANSLATIONS] + if getmetatable(baseTranslations) then + setmetatable(baseTranslations, nil) + end + if translations == baseTranslations or instance[STRICTNESS] then + setmetatable(instance, { + __index = __index, + __newindex = __newindex, + __tostring = __tostring + }) + + setmetatable(translations, { + __index = AceLocale.prototype + }) + else + setmetatable(instance, { + __index = __index, + __newindex = __newindex, + __tostring = __tostring + }) + + setmetatable(translations, { + __index = baseTranslations, + }) + + setmetatable(baseTranslations, { + __index = AceLocale.prototype, + }) + end + else + setmetatable(instance, { + __index = __index, + __newindex = __newindex, + __tostring = __tostring, + }) + end + clearCache(instance) + newRegistries[instance] = true + scheduleClear() + return instance +end + +function AceLocale:new(name) + self:argCheck(name, 2, "string") + + if self.registry[name] and type(rawget(self.registry[name], 'GetLibraryVersion')) ~= "function" then + return self.registry[name] + end + + AceLocale.registry[name] = refixInstance({ + [STRICTNESS] = false, + [NAME] = name, + }) + newRegistries[AceLocale.registry[name]] = true + return AceLocale.registry[name] +end + +AceLocale.prototype = { class = AceLocale } + +function AceLocale.prototype:EnableDebugging() + if rawget(self, BASE_TRANSLATIONS) then + AceLocale.error(self, "Cannot enable debugging after a translation has been registered.") + end + rawset(self, DEBUGGING, true) +end + +function AceLocale.prototype:EnableDynamicLocales(override) + AceLocale.argCheck(self, override, 2, "boolean", "nil") + if not override and rawget(self, BASE_TRANSLATIONS) then + AceLocale.error(self, "Cannot enable dynamic locales after a translation has been registered.") + end + if not rawget(self, DYNAMIC_LOCALES) then + rawset(self, DYNAMIC_LOCALES, true) + if rawget(self, BASE_LOCALE) then + if not rawget(self, TRANSLATION_TABLES) then + rawset(self, TRANSLATION_TABLES, {}) + end + self[TRANSLATION_TABLES][self[BASE_LOCALE]] = self[BASE_TRANSLATIONS] + self[TRANSLATION_TABLES][self[CURRENT_LOCALE]] = self[TRANSLATIONS] + end + end +end + +function AceLocale.prototype:RegisterTranslations(locale, func) + AceLocale.argCheck(self, locale, 2, "string") + AceLocale.argCheck(self, func, 3, "function") + + if locale == rawget(self, BASE_LOCALE) then + AceLocale.error(self, "Cannot provide the same locale more than once. %q provided twice.", locale) + end + + if rawget(self, BASE_TRANSLATIONS) and GetLocale() ~= locale then + if rawget(self, DEBUGGING) or rawget(self, DYNAMIC_LOCALES) then + if not rawget(self, TRANSLATION_TABLES) then + rawset(self, TRANSLATION_TABLES, {}) + end + if self[TRANSLATION_TABLES][locale] then + AceLocale.error(self, "Cannot provide the same locale more than once. %q provided twice.", locale) + end + local t = func() + func = nil + if type(t) ~= "table" then + AceLocale.error(self, "Bad argument #3 to `RegisterTranslations'. function did not return a table. (expected table, got %s)", type(t)) + end + self[TRANSLATION_TABLES][locale] = t + t = nil + end + func = nil + return + end + local t = func() + func = nil + if type(t) ~= "table" then + AceLocale.error(self, "Bad argument #3 to `RegisterTranslations'. function did not return a table. (expected table, got %s)", type(t)) + end + + rawset(self, TRANSLATIONS, t) + if not rawget(self, BASE_TRANSLATIONS) then + rawset(self, BASE_TRANSLATIONS, t) + rawset(self, BASE_LOCALE, locale) + for key,value in pairs(t) do + if value == true then + t[key] = key + end + end + else + for key, value in pairs(self[TRANSLATIONS]) do + if not rawget(self[BASE_TRANSLATIONS], key) then + AceLocale.error(self, "Improper translation exists. %q is likely misspelled for locale %s.", key, locale) + end + if value == true then + AceLocale.error(self, "Can only accept true as a value on the base locale. %q is the base locale, %q is not.", rawget(self, BASE_LOCALE), locale) + end + end + end + rawset(self, CURRENT_LOCALE, locale) + refixInstance(self) + if rawget(self, DEBUGGING) or rawget(self, DYNAMIC_LOCALES) then + if not rawget(self, TRANSLATION_TABLES) then + rawset(self, TRANSLATION_TABLES, {}) + end + self[TRANSLATION_TABLES][locale] = t + end + t = nil +end + +function AceLocale.prototype:SetLocale(locale) + AceLocale.argCheck(self, locale, 2, "string", "boolean") + if not rawget(self, DYNAMIC_LOCALES) then + AceLocale.error(self, "Cannot call `SetLocale' without first calling `EnableDynamicLocales'.") + end + if not rawget(self, TRANSLATION_TABLES) then + AceLocale.error(self, "Cannot call `SetLocale' without first calling `RegisterTranslations'.") + end + if locale == true then + locale = GetLocale() + if not self[TRANSLATION_TABLES][locale] then + locale = self[BASE_LOCALE] + end + end + + if self[CURRENT_LOCALE] == locale then + return + end + + if not self[TRANSLATION_TABLES][locale] then + AceLocale.error(self, "Locale %q not registered.", locale) + end + + self[TRANSLATIONS] = self[TRANSLATION_TABLES][locale] + self[CURRENT_LOCALE] = locale + refixInstance(self) +end + +function AceLocale.prototype:GetLocale() + if not rawget(self, TRANSLATION_TABLES) then + AceLocale.error(self, "Cannot call `GetLocale' without first calling `RegisterTranslations'.") + end + return self[CURRENT_LOCALE] +end + +local function iter(t, position) + return (next(t, position)) +end + +function AceLocale.prototype:IterateAvailableLocales() + if not rawget(self, DYNAMIC_LOCALES) then + AceLocale.error(self, "Cannot call `IterateAvailableLocales' without first calling `EnableDynamicLocales'.") + end + if not rawget(self, TRANSLATION_TABLES) then + AceLocale.error(self, "Cannot call `IterateAvailableLocales' without first calling `RegisterTranslations'.") + end + return iter, self[TRANSLATION_TABLES], nil +end + +function AceLocale.prototype:HasLocale(locale) + if not rawget(self, DYNAMIC_LOCALES) then + AceLocale.error(self, "Cannot call `HasLocale' without first calling `EnableDynamicLocales'.") + end + AceLocale.argCheck(self, locale, 2, "string") + return rawget(self, TRANSLATION_TABLES) and self[TRANSLATION_TABLES][locale] ~= nil +end + +function AceLocale.prototype:SetStrictness(strict) + AceLocale.argCheck(self, strict, 2, "boolean") + local mt = getmetatable(self) + if not mt then + AceLocale.error(self, "Cannot call `SetStrictness' without a metatable.") + end + if not rawget(self, TRANSLATIONS) then + AceLocale.error(self, "No translations registered.") + end + rawset(self, STRICTNESS, strict) + refixInstance(self) +end + +local function initReverse(self) + rawset(self, REVERSE_TRANSLATIONS, {}) + local alpha = self[TRANSLATIONS] + local bravo = self[REVERSE_TRANSLATIONS] + for base, localized in pairs(alpha) do + bravo[localized] = base + end +end + +function AceLocale.prototype:GetTranslation(text) + AceLocale.argCheck(self, text, 1, "string", "number") + if not rawget(self, TRANSLATIONS) then + AceLocale.error(self, "No translations registered") + end + return self[text] +end + +function AceLocale.prototype:GetStrictTranslation(text) + AceLocale.argCheck(self, text, 1, "string", "number") + local x = rawget(self, TRANSLATIONS) + if not x then + AceLocale.error(self, "No translations registered") + end + local value = rawget(x, text) + if value == nil then + AceLocale.error(self, "Translation %q does not exist for locale %s", text, self[CURRENT_LOCALE]) + end + return value +end + +function AceLocale.prototype:GetReverseTranslation(text) + local x = rawget(self, REVERSE_TRANSLATIONS) + if not x then + if not rawget(self, TRANSLATIONS) then + AceLocale.error(self, "No translations registered") + end + initReverse(self) + x = self[REVERSE_TRANSLATIONS] + end + local translation = x[text] + if not translation then + AceLocale.error(self, "Reverse translation for %q does not exist", text) + end + return translation +end + +function AceLocale.prototype:GetIterator() + local x = rawget(self, TRANSLATIONS) + if not x then + AceLocale.error(self, "No translations registered") + end + return next, x, nil +end + +function AceLocale.prototype:GetReverseIterator() + local x = rawget(self, REVERSE_TRANSLATIONS) + if not x then + if not rawget(self, TRANSLATIONS) then + AceLocale.error(self, "No translations registered") + end + initReverse(self) + x = self[REVERSE_TRANSLATIONS] + end + return next, x, nil +end + +function AceLocale.prototype:HasTranslation(text) + AceLocale.argCheck(self, text, 1, "string", "number") + local x = rawget(self, TRANSLATIONS) + if not x then + AceLocale.error(self, "No translations registered") + end + return rawget(x, text) and true +end + +function AceLocale.prototype:HasReverseTranslation(text) + local x = rawget(self, REVERSE_TRANSLATIONS) + if not x then + if not rawget(self, TRANSLATIONS) then + AceLocale.error(self, "No translations registered") + end + initReverse(self) + x = self[REVERSE_TRANSLATIONS] + end + return x[text] and true +end + +function AceLocale.prototype:Debug() + if not rawget(self, DEBUGGING) then + return + end + local words = {} + local locales = {"enUS", "ruRU", "deDE", "frFR", "koKR", "zhCN", "zhTW", "esES"} + local localizations = {} + DEFAULT_CHAT_FRAME:AddMessage("--- AceLocale Debug ---") + for _,locale in ipairs(locales) do + if not self[TRANSLATION_TABLES][locale] then + DEFAULT_CHAT_FRAME:AddMessage(string.format("Locale %q not found", locale)) + else + localizations[locale] = self[TRANSLATION_TABLES][locale] + end + end + local localeDebug = {} + for locale, localization in pairs(localizations) do + localeDebug[locale] = {} + for word in pairs(localization) do + if type(localization[word]) == "table" then + if type(words[word]) ~= "table" then + words[word] = {} + end + for bit in pairs(localization[word]) do + if type(localization[word][bit]) == "string" then + words[word][bit] = true + end + end + elseif type(localization[word]) == "string" then + words[word] = true + end + end + end + for word in pairs(words) do + if type(words[word]) == "table" then + for bit in pairs(words[word]) do + for locale, localization in pairs(localizations) do + if not rawget(localization, word) or not localization[word][bit] then + localeDebug[locale][word .. "::" .. bit] = true + end + end + end + else + for locale, localization in pairs(localizations) do + if not rawget(localization, word) then + localeDebug[locale][word] = true + end + end + end + end + for locale, t in pairs(localeDebug) do + if not next(t) then + DEFAULT_CHAT_FRAME:AddMessage(string.format("Locale %q complete", locale)) + else + DEFAULT_CHAT_FRAME:AddMessage(string.format("Locale %q missing:", locale)) + for word in pairs(t) do + DEFAULT_CHAT_FRAME:AddMessage(string.format(" %q", word)) + end + end + end + DEFAULT_CHAT_FRAME:AddMessage("--- End AceLocale Debug ---") +end + +setmetatable(AceLocale.prototype, { + __index = function(self, k) + if type(k) ~= "table" and k ~= 0 and k ~= "GetLibraryVersion" and k ~= "error" and k ~= "assert" and k ~= "argCheck" and k ~= "pcall" then -- HACK: remove "GetLibraryVersion" and such later. + AceLocale.error(lastSelf or self, "Translation %q does not exist.", k) + end + return nil + end +}) + +local function activate(self, oldLib, oldDeactivate) + AceLocale = self + + self.frame = oldLib and oldLib.frame or CreateFrame("Frame") + self.registry = oldLib and oldLib.registry or {} + self.BASE_TRANSLATIONS = oldLib and oldLib.BASE_TRANSLATIONS or {} + self.DEBUGGING = oldLib and oldLib.DEBUGGING or {} + self.TRANSLATIONS = oldLib and oldLib.TRANSLATIONS or {} + self.BASE_LOCALE = oldLib and oldLib.BASE_LOCALE or {} + self.TRANSLATION_TABLES = oldLib and oldLib.TRANSLATION_TABLES or {} + self.REVERSE_TRANSLATIONS = oldLib and oldLib.REVERSE_TRANSLATIONS or {} + self.STRICTNESS = oldLib and oldLib.STRICTNESS or {} + self.NAME = oldLib and oldLib.NAME or {} + self.DYNAMIC_LOCALES = oldLib and oldLib.DYNAMIC_LOCALES or {} + self.CURRENT_LOCALE = oldLib and oldLib.CURRENT_LOCALE or {} + + BASE_TRANSLATIONS = self.BASE_TRANSLATIONS + DEBUGGING = self.DEBUGGING + TRANSLATIONS = self.TRANSLATIONS + BASE_LOCALE = self.BASE_LOCALE + TRANSLATION_TABLES = self.TRANSLATION_TABLES + REVERSE_TRANSLATIONS = self.REVERSE_TRANSLATIONS + STRICTNESS = self.STRICTNESS + NAME = self.NAME + DYNAMIC_LOCALES = self.DYNAMIC_LOCALES + CURRENT_LOCALE = self.CURRENT_LOCALE + + + local GetTime = GetTime + local timeUntilClear = GetTime() + 5 + scheduleClear = function() + if next(newRegistries) then + self.frame:Show() + timeUntilClear = GetTime() + 5 + end + end + + if not self.registry then + self.registry = {} + else + for name, instance in pairs(self.registry) do + local name = name + local mt = getmetatable(instance) + setmetatable(instance, nil) + instance[NAME] = name + local strict + if instance[STRICTNESS] ~= nil then + strict = instance[STRICTNESS] + elseif instance[TRANSLATIONS] ~= instance[BASE_TRANSLATIONS] then + if getmetatable(instance[TRANSLATIONS]).__index == oldLib.prototype then + strict = true + end + end + instance[STRICTNESS] = strict and true or false + refixInstance(instance) + end + end + + self.frame:SetScript("OnEvent", scheduleClear) + self.frame:SetScript("OnUpdate", function() -- (this, elapsed) + if timeUntilClear - GetTime() <= 0 then + self.frame:Hide() + for k in pairs(newRegistries) do + clearCache(k) + newRegistries[k] = nil + k = nil + end + end + end) + self.frame:UnregisterAllEvents() + self.frame:RegisterEvent("ADDON_LOADED") + self.frame:RegisterEvent("PLAYER_ENTERING_WORLD") + self.frame:Show() + + if oldDeactivate then + oldDeactivate(oldLib) + end +end + +AceLibrary:Register(AceLocale, MAJOR_VERSION, MINOR_VERSION, activate) diff --git a/libs/AceModuleCore-2.0/AceModuleCore-2.0.lua b/libs/AceModuleCore-2.0/AceModuleCore-2.0.lua new file mode 100755 index 0000000..42fcc16 --- /dev/null +++ b/libs/AceModuleCore-2.0/AceModuleCore-2.0.lua @@ -0,0 +1,462 @@ +--[[ +Name: AceModuleCore-2.0 +Revision: $Rev: 17998 $ +Developed by: The Ace Development Team (http://www.wowace.com/index.php/The_Ace_Development_Team) +Inspired By: Ace 1.x by Turan (turan@gryphon.com) +Website: http://www.wowace.com/ +Documentation: http://www.wowace.com/index.php/AceModuleCore-2.0 +SVN: http://svn.wowace.com/root/trunk/Ace2/AceModuleCore-2.0 +Description: Mixin to provide a module system so that modules or plugins can + use an addon as its core. +Dependencies: AceLibrary, AceOO-2.0, AceAddon-2.0, AceEvent-2.0 (optional), Compost-2.0 (optional) +]] + +local MAJOR_VERSION = "AceModuleCore-2.0" +local MINOR_VERSION = "$Revision: 17998 $" + +if not AceLibrary then error(MAJOR_VERSION .. " requires AceLibrary") end +if not AceLibrary:IsNewVersion(MAJOR_VERSION, MINOR_VERSION) then return end + +if loadstring("return function(...) return ... end") and AceLibrary:HasInstance(MAJOR_VERSION) then return end -- lua51 check +if not AceLibrary:HasInstance("AceOO-2.0") then error(MAJOR_VERSION .. " requires AceOO-2.0") end + +local function safecall(func,a,b,c,d,e,f,g) + local success, err = pcall(func,a,b,c,d,e,f,g) + if not success then geterrorhandler()(err) end +end + +local table_setn +do + local version = GetBuildInfo() + if string.find(version, "^2%.") then + -- 2.0.0 + table_setn = function() end + else + table_setn = table.setn + end +end + +local new, del +do + local list = setmetatable({}, {__mode = 'k'}) + function new() + local t = next(list) + if t then + list[t] = nil + return t + else + return {} + end + end + function del(t) + for k in pairs(t) do + t[k] = nil + end + table_setn(t, 0) + list[t] = true + return nil + end +end + +local AceOO = AceLibrary:GetInstance("AceOO-2.0") +local AceModuleCore = AceOO.Mixin { + "NewModule", + "HasModule", + "GetModule", + "IsModule", + "IterateModules", + "SetModuleMixins", + "SetModuleClass", + "IsModuleActive", + "ToggleModuleActive" + } +local AceEvent + +local Compost = AceLibrary:HasInstance("Compost-2.0") and AceLibrary("Compost-2.0") + +local function getlibrary(lib) + if type(lib) == "string" then + return AceLibrary(lib) + else + return lib + end +end + +local tmp +function AceModuleCore:NewModule(name, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) + if not self.modules then + AceModuleCore:error("CreatePrototype() must be called before attempting to create a new module.", 2) + end + AceModuleCore:argCheck(name, 2, "string") + if string.len(name) == 0 then + AceModuleCore:error("Bad argument #2 to `NewModule`, string must not be empty") + end + if self.modules[name] then + AceModuleCore:error("The module %q has already been registered", name) + end + + if not tmp then + tmp = {} + end + if a1 then table.insert(tmp, a1) + if a2 then table.insert(tmp, a2) + if a3 then table.insert(tmp, a3) + if a4 then table.insert(tmp, a4) + if a5 then table.insert(tmp, a5) + if a6 then table.insert(tmp, a6) + if a7 then table.insert(tmp, a7) + if a8 then table.insert(tmp, a8) + if a9 then table.insert(tmp, a9) + if a10 then table.insert(tmp, a10) + if a11 then table.insert(tmp, a11) + if a12 then table.insert(tmp, a12) + if a13 then table.insert(tmp, a13) + if a14 then table.insert(tmp, a14) + if a15 then table.insert(tmp, a15) + if a16 then table.insert(tmp, a16) + if a17 then table.insert(tmp, a17) + if a18 then table.insert(tmp, a18) + if a19 then table.insert(tmp, a19) + if a20 then table.insert(tmp, a20) + end end end end end end end end end end end end end end end end end end end end + for k,v in ipairs(tmp) do + tmp[k] = getlibrary(v) + end + + if self.moduleMixins then + for _,mixin in ipairs(self.moduleMixins) do + local exists = false + for _,v in ipairs(tmp) do + if mixin == v then + exists = true + break + end + end + if not exists then + table.insert(tmp, mixin) + end + end + end + + local module = AceOO.Classpool(self.moduleClass, unpack(tmp)):new(name) + self.modules[name] = module + module.name = name + module.title = name + + AceModuleCore.totalModules[module] = self + + if AceEvent then + AceEvent:TriggerEvent("Ace2_ModuleCreated", module) + end + + for k in pairs(tmp) do + tmp[k] = nil + end + table_setn(tmp, 0) + return module +end + +function AceModuleCore:HasModule(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) + if a1 then if not self.modules[a1] then return false end + if a2 then if not self.modules[a2] then return false end + if a3 then if not self.modules[a3] then return false end + if a4 then if not self.modules[a4] then return false end + if a5 then if not self.modules[a5] then return false end + if a6 then if not self.modules[a6] then return false end + if a7 then if not self.modules[a7] then return false end + if a8 then if not self.modules[a8] then return false end + if a9 then if not self.modules[a9] then return false end + if a10 then if not self.modules[a10] then return false end + if a11 then if not self.modules[a11] then return false end + if a12 then if not self.modules[a12] then return false end + if a13 then if not self.modules[a13] then return false end + if a14 then if not self.modules[a14] then return false end + if a15 then if not self.modules[a15] then return false end + if a16 then if not self.modules[a16] then return false end + if a17 then if not self.modules[a17] then return false end + if a18 then if not self.modules[a18] then return false end + if a19 then if not self.modules[a19] then return false end + if a20 then if not self.modules[a20] then return false end + end end end end end end end end end end end end end end end end end end end end + + return true +end + +function AceModuleCore:GetModule(name) + if not self.modules then + AceModuleCore:error("Error initializing class. Please report error.") + end + if not self.modules[name] then + AceModuleCore:error("Cannot find module %q.", name) + end + return self.modules[name] +end + +function AceModuleCore:IsModule(module) + if self == AceModuleCore then + return AceModuleCore.totalModules[module] + else + for k,v in pairs(self.modules) do + if v == module then + return true + end + end + return false + end +end + +function AceModuleCore:IterateModules() + local t = new() + for k in pairs(self.modules) do + table.insert(t, k) + end + table.sort(t) + local i = 0 + return function() + i = i + 1 + local x = t[i] + if x then + return x, self.modules[x] + else + t = del(t) + return nil + end + end, nil, nil +end + +function AceModuleCore:SetModuleMixins(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) + if self.moduleMixins then + AceModuleCore:error('Cannot call "SetModuleMixins" twice') + elseif not self.modules then + AceModuleCore:error("Error initializing class. Please report error.") + elseif next(self.modules) then + AceModuleCore:error('Cannot call "SetModuleMixins" after "NewModule" has been called.') + end + + self.moduleMixins = Compost and Compost:Acquire(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) or {a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20} + for k,v in ipairs(self.moduleMixins) do + self.moduleMixins[k] = getlibrary(v) + end +end + +function AceModuleCore:SetModuleClass(class) + class = getlibrary(class) + AceModuleCore:assert(AceOO.inherits(class, AceOO.Class), "Bad argument #2 to `SetModuleClass' (Class expected)") + if not self.modules then + AceModuleCore:error("Error initializing class. Please report error.") + end + if self.customModuleClass then + AceModuleCore:error("Cannot call `SetModuleClass' twice.") + end + self.customModuleClass = true + self.moduleClass = class + self.modulePrototype = class.prototype +end + +function AceModuleCore:ToggleModuleActive(module, state) + AceModuleCore:argCheck(module, 2, "table", "string") + AceModuleCore:argCheck(state, 3, "nil", "boolean") + + if type(module) == "string" then + if not self:HasModule(module) then + AceModuleCore:error("Cannot find module %q", module) + end + module = self:GetModule(module) + else + if not self:IsModule(module) then + AceModuleCore:error("%q is not a module", module) + end + end + + local disable + if state == nil then + disable = self:IsModuleActive(module) + else + disable = not state + if disable ~= self:IsModuleActive(module) then + return + end + end + + if type(module.ToggleActive) == "function" then + return module:ToggleActive(not disable) + elseif AceOO.inherits(self, "AceDB-2.0") then + if not self.db or not self.db.raw then + AceModuleCore:error("Cannot toggle a module until `RegisterDB' has been called and `ADDON_LOADED' has been fireed.") + end + if type(self.db.raw.disabledModules) ~= "table" then + self.db.raw.disabledModules = Compost and Compost:Acquire() or {} + end + local _,profile = self:GetProfile() + if type(self.db.raw.disabledModules[profile]) ~= "table" then + self.db.raw.disabledModules[profile] = Compost and Compost:Acquire() or {} + end + if type(self.db.raw.disabledModules[profile][module.name]) ~= "table" then + self.db.raw.disabledModules[profile][module.name] = disable or nil + end + if not disable then + if not next(self.db.raw.disabledModules[profile]) then + if Compost then + Compost:Reclaim(self.db.raw.disabledModules[profile]) + end + self.db.raw.disabledModules[profile] = nil + end + if not next(self.db.raw.disabledModules) then + if Compost then + Compost:Reclaim(self.db.raw.disabledModules) + end + self.db.raw.disabledModules = nil + end + end + else + if type(self.disabledModules) ~= "table" then + self.disabledModules = Compost and Compost:Acquire() or {} + end + self.disabledModules[module.name] = disable or nil + end + if AceOO.inherits(module, "AceAddon-2.0") then + local AceAddon = AceLibrary("AceAddon-2.0") + if not AceAddon.addonsStarted[module] then + return + end + end + if not disable then + local current = module.class + while true do + if current == AceOO.Class then + break + end + if current.mixins then + for mixin in pairs(current.mixins) do + if type(mixin.OnEmbedEnable) == "function" then + safecall(mixin.OnEmbedEnable, mixin, module) + end + end + end + current = current.super + end + if type(module.OnEnable) == "function" then + safecall(module.OnEnable, module) + end + if AceEvent then + AceEvent:TriggerEvent("Ace2_AddonEnabled", module) + end + else + local current = module.class + while true do + if current == AceOO.Class then + break + end + if current.mixins then + for mixin in pairs(current.mixins) do + if type(mixin.OnEmbedDisable) == "function" then + safecall(mixin.OnEmbedDisable, mixin, module) + end + end + end + current = current.super + end + if type(module.OnDisable) == "function" then + safecall(module.OnDisable, module) + end + if AceEvent then + AceEvent:TriggerEvent("Ace2_AddonDisabled", module) + end + end + return not disable +end + +function AceModuleCore:IsModuleActive(module) + AceModuleCore:argCheck(module, 2, "table", "string") + + if AceModuleCore == self then + self:argCheck(module, 2, "table") + + local core = AceModuleCore.totalModules[module] + if not core then + self:error("Bad argument #2 to `IsModuleActive'. Not a module") + end + return core:IsModuleActive(module) + end + + if type(module) == "string" then + if not self:HasModule(module) then + AceModuleCore:error("Cannot find module %q", module) + end + module = self:GetModule(module) + else + if not self:IsModule(module) then + AceModuleCore:error("%q is not a module", module) + end + end + + if type(module.IsActive) == "function" then + return module:IsActive() + elseif AceOO.inherits(self, "AceDB-2.0") then + local _,profile = self:GetProfile() + return not self.db or not self.db.raw or not self.db.raw.disabledModules or not self.db.raw.disabledModules[profile] or not self.db.raw.disabledModules[profile][module.name] + else + return not self.disabledModules or not self.disabledModules[module.name] + end +end + +function AceModuleCore:OnInstanceInit(target) + if target.modules then + AceModuleCore:error("OnInstanceInit cannot be called twice") + end + target.modules = Compost and Compost:Acquire() or {} + + target.moduleClass = AceOO.Class("AceAddon-2.0") + target.modulePrototype = target.moduleClass.prototype +end + +AceModuleCore.OnManualEmbed = AceModuleCore.OnInstanceInit + +function AceModuleCore.OnEmbedProfileDisable(AceModuleCore, self, newProfile) + if not AceOO.inherits(self, "AceDB-2.0") then + return + end + local _,currentProfile = self:GetProfile() + for k, module in pairs(self.modules) do + if type(module.IsActive) == "function" or type(module.ToggleActive) == "function" then + -- continue + else + local currentActive = not self.db or not self.db.raw or not self.db.raw.disabledModules or not self.db.raw.disabledModules[currentProfile] or not self.db.raw.disabledModules[currentProfile][module.name] + local newActive = not self.db or not self.db.raw or not self.db.raw.disabledModules or not self.db.raw.disabledModules[newProfile] or not self.db.raw.disabledModules[newProfile][module.name] + if currentActive ~= newActive then + self:ToggleModuleActive(module) + if not self.db.raw.disabledModules then + self.db.raw.disabledModules = {} + end + if not self.db.raw.disabledModules[currentProfile] then + self.db.raw.disabledModules[currentProfile] = {} + end + self.db.raw.disabledModules[currentProfile][module.name] = not currentActive or nil + end + end + end +end + +local function activate(self, oldLib, oldDeactivate) + AceModuleCore = self + + if oldLib then + self.totalModules = oldLib.totalModules + end + if not self.totalModules then + self.totalModules = {} + end + + self.super.activate(self, oldLib, oldDeactivate) +end + +local function external(self, major, instance) + if major == "Compost-2.0" then + Compost = instance + elseif major == "AceEvent-2.0" then + AceEvent = instance + end +end + +AceLibrary:Register(AceModuleCore, MAJOR_VERSION, MINOR_VERSION, activate, nil, external) +AceModuleCore = AceLibrary(MAJOR_VERSION) diff --git a/libs/AceOO-2.0/AceOO-2.0.lua b/libs/AceOO-2.0/AceOO-2.0.lua new file mode 100755 index 0000000..7c4f3bc --- /dev/null +++ b/libs/AceOO-2.0/AceOO-2.0.lua @@ -0,0 +1,1004 @@ +--[[ +Name: AceOO-2.0 +Revision: $Rev: 17638 $ +Developed by: The Ace Development Team (http://www.wowace.com/index.php/The_Ace_Development_Team) +Inspired By: Ace 1.x by Turan (turan@gryphon.com) +Website: http://www.wowace.com/ +Documentation: http://www.wowace.com/index.php/AceOO-2.0 +SVN: http://svn.wowace.com/root/trunk/Ace2/AceOO-2.0 +Description: Library to provide an object-orientation framework. +Dependencies: AceLibrary +]] + +local MAJOR_VERSION = "AceOO-2.0" +local MINOR_VERSION = "$Revision: 17638 $" + +-- This ensures the code is only executed if the libary doesn't already exist, or is a newer version +if not AceLibrary then error(MAJOR_VERSION .. " requires AceLibrary.") end +if not AceLibrary:IsNewVersion(MAJOR_VERSION, MINOR_VERSION) then return end + +if loadstring("return function(...) return ... end") and AceLibrary:HasInstance(MAJOR_VERSION) then return end -- lua51 check +local table_setn +do + local version = GetBuildInfo() + if string.find(version, "^2%.") then + -- 2.0.0 + table_setn = function() end + else + table_setn = table.setn + end +end + +local AceOO = { + error = AceLibrary.error, + argCheck = AceLibrary.argCheck +} + +-- @function getuid +-- @brief Obtain a unique string identifier for the object in question. +-- @param t The object to obtain the uid for. +-- @return The uid string. +local function pad(cap) + return string.rep('0', 8 - string.len(cap)) .. cap +end +local function getuid(t) + local mt = getmetatable(t) + setmetatable(t, nil) + local str = tostring(t) + setmetatable(t, mt) + local _,_,cap = string.find(str, '[^:]*: 0x(.*)$') + if cap then return pad(cap) end + _,_,cap = string.find(str, '[^:]*: (.*)$') + if cap then return pad(cap) end +end + +local function getlibrary(o) + if type(o) == "table" then + return o + elseif type(o) == "string" then + if not AceLibrary:HasInstance(o) then + AceOO:error("Library %q does not exist.", o) + end + return AceLibrary(o) + end +end + +-- @function Factory +-- @brief Construct a factory for the creation of objects. +-- @param obj The object whose init method will be called on the new factory +-- object. +-- @param newobj The object whose init method will be called on the new +-- objects that the Factory creates, to initialize them. +-- @param (a1..a20) Arguments which will be passed to obj.init() in addition +-- to the Factory object. +-- @return The new factory which creates a newobj when its new method is called, +-- or when it is called directly (__call metamethod). +local Factory +do + local function new(obj, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, + a13, a14, a15, a16, a17, a18, a19, a20) + local t = {} + local uid = getuid(t) + local l = getlibrary + obj:init(t, l(a1), l(a2), l(a3), l(a4), l(a5), l(a6), l(a7), + l(a8), l(a9), l(a10), l(a11), l(a12), l(a13), + l(a14), l(a15), l(a16), l(a17), l(a18), l(a19), + l(a20)) + t.uid = uid + return t + end + + local function createnew(self, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, + a11, a12, a13, a14, a15, a16, a17, a18, + a19, a20) + local o = self.prototype + local l = getlibrary + return new(o, l(a1), l(a2), l(a3), l(a4), l(a5), l(a6), l(a7), + l(a8), l(a9), l(a10), l(a11), l(a12), l(a13), + l(a14), l(a15), l(a16), l(a17), l(a18), l(a19), + l(a20)) + end + + function Factory(obj, newobj, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, + a11, a12, a13, a14, a15, a16, a17, a18, + a19, a20) + local t = new(obj, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, + a13, a14, a15, a16, a17, a18, a19, a20) + t.prototype = newobj + t.new = createnew + getmetatable(t).__call = t.new + return t + end +end + + +local function objtostring(self) + if self.ToString then + return self:ToString() + elseif self.GetLibraryVersion then + return (self:GetLibraryVersion()) + elseif self.super then + local s = "Sub-" .. tostring(self.super) + local first = true + if self.interfaces then + for interface in pairs(self.interfaces) do + if first then + s = s .. "(" .. tostring(interface) + first = false + else + s = s .. ", " .. tostring(interface) + end + end + end + if self.mixins then + for mixin in pairs(self.mixins) do + if first then + s = s .. tostring(mixin) + first = false + else + s = s .. ", " .. tostring(mixin) + end + end + end + if first then + if self.uid then + return s .. ":" .. self.uid + else + return s + end + else + return s .. ")" + end + else + return self.uid and 'Subclass:' .. self.uid or 'Subclass' + end +end + +-- @table Object +-- @brief Base of all objects, including Class. +-- +-- @method init +-- @brief Initialize a new object. +-- @param newobject The object to initialize +-- @param class The class to make newobject inherit from +local Object +do + Object = {} + function Object:init(newobject, class) + local parent = class or self + if not rawget(newobject, 'uid') then + newobject.uid = getuid(newobject) + end + local mt = { + __index = parent, + __tostring = objtostring, + } + setmetatable(newobject, mt) + end + Object.uid = getuid(Object) + setmetatable(Object, { __tostring = function() return 'Object' end }) +end + +local Interface + +local function validateInterface(object, interface) + if not object.class and object.prototype then + object = object.prototype + end + for k,v in pairs(interface.interface) do + if tostring(type(object[k])) ~= v then + return false + end + end + if interface.superinterfaces then + for superinterface in pairs(interface.superinterfaces) do + if not validateInterface(object, superinterface) then + return false + end + end + end + if type(object.class) == "table" and rawequal(object.class.prototype, object) then + if not object.class.interfaces then + rawset(object.class, 'interfaces', {}) + end + object.class.interfaces[interface] = true + elseif type(object.class) == "table" and type(object.class.prototype) == "table" then + validateInterface(object.class.prototype, interface) + -- check if class is proper, thus preventing future checks. + end + return true +end + +-- @function inherits +-- @brief Return whether an Object or Class inherits from a given +-- parent. +-- @param object Object or Class to check +-- @param parent Parent to test inheritance from +-- @return whether an Object or Class inherits from a given +-- parent. +local function inherits(object, parent) + object = getlibrary(object) + if type(parent) == "string" then + if not AceLibrary:HasInstance(parent) then + return false + else + parent = AceLibrary(parent) + end + end + AceOO:argCheck(parent, 2, "table") + if type(object) ~= "table" then + return false + end + local current + if object.class then + current = object.class + else + current = object + end + if type(current) ~= "table" then + return false + end + if rawequal(current, parent) then + return true + end + if parent.class then + while true do + if rawequal(current, Object) then + break + end + if current.mixins then + for mixin in pairs(current.mixins) do + if rawequal(mixin, parent) then + return true + end + end + end + if current.interfaces then + for interface in pairs(current.interfaces) do + if rawequal(interface, parent) then + return true + end + end + end + current = current.super + if type(current) ~= "table" then + break + end + end + + local isInterface = false + local curr = parent.class + while true do + if rawequal(curr, Object) then + break + elseif rawequal(curr, Interface) then + isInterface = true + break + end + curr = curr.super + if type(curr) ~= "table" then + break + end + end + return isInterface and validateInterface(object, parent) + else + while true do + if rawequal(current, parent) then + return true + elseif rawequal(current, Object) then + return false + end + current = current.super + if type(current) ~= "table" then + return false + end + end + end +end + +-- @table Class +-- @brief An object factory which sets up inheritence and supports +-- 'mixins'. +-- +-- @metamethod Class call +-- @brief Call ClassFactory:new() to create a new class. +-- +-- @method Class new +-- @brief Construct a new object. +-- @param (a1..a20) Arguments to pass to the object init function. +-- @return The new object. +-- +-- @method Class init +-- @brief Initialize a new class. +-- @param parent Superclass. +-- @param (a1..a20) Mixins. +-- +-- @method Class ToString +-- @return A string representing the object, in this case 'Class'. +local initStatus +local Class +local Mixin +local autoEmbed = false +local function traverseInterfaces(bit, total) + if bit.superinterfaces then + for interface in pairs(bit.superinterfaces) do + if not total[interface] then + total[interface] = true + traverseInterfaces(interface, total) + end + end + end +end +local class_new +do + Class = Factory(Object, setmetatable({}, {__index = Object}), Object) + Class.super = Object + + local function protostring(t) + return '<' .. tostring(t.class) .. ' prototype>' + end + local function classobjectstring(t) + if t.ToString then + return t:ToString() + elseif t.GetLibraryVersion then + return (t:GetLibraryVersion()) + else + return '<' .. tostring(t.class) .. ' instance>' + end + end + local function classobjectequal(self, other) + if type(self) == "table" and self.Equals then + return self:Equals(other) + elseif type(other) == "table" and other.Equals then + return other:Equals(self) + elseif type(self) == "table" and self.CompareTo then + return self:CompareTo(other) == 0 + elseif type(other) == "table" and other.CompareTo then + return other:CompareTo(self) == 0 + else + return rawequal(self, other) + end + end + local function classobjectlessthan(self, other) + if type(self) == "table" and self.IsLessThan then + return self:IsLessThan(other) + elseif type(other) == "table" and other.IsLessThanOrEqualTo then + return not other:IsLessThanOrEqualTo(self) + elseif type(self) == "table" and self.CompareTo then + return self:CompareTo(other) < 0 + elseif type(other) == "table" and other.CompareTo then + return other:CompareTo(self) > 0 + elseif type(other) == "table" and other.IsLessThan and other.Equals then + return other:Equals(self) or other:IsLessThan(self) + else + AceOO:error("cannot compare two objects") + end + end + local function classobjectlessthanequal(self, other) + if type(self) == "table" and self.IsLessThanOrEqualTo then + return self:IsLessThanOrEqualTo(other) + elseif type(other) == "table" and other.IsLessThan then + return not other:IsLessThan(self) + elseif type(self) == "table" and self.CompareTo then + return self:CompareTo(other) <= 0 + elseif type(other) == "table" and other.CompareTo then + return other:CompareTo(self) >= 0 + elseif type(self) == "table" and self.IsLessThan and self.Equals then + return self:Equals(other) or self:IsLessThan(other) + else + AceOO:error("cannot compare two incompatible objects") + end + end + local function classobjectadd(self, other) + if type(self) == "table" and self.Add then + return self:Add(other) + else + AceOO:error("cannot add two incompatible objects") + end + end + local function classobjectsub(self, other) + if type(self) == "table" and self.Subtract then + return self:Subtract(other) + else + AceOO:error("cannot subtract two incompatible objects") + end + end + local function classobjectunm(self, other) + if type(self) == "table" and self.UnaryNegation then + return self:UnaryNegation(other) + else + AceOO:error("attempt to negate an incompatible object") + end + end + local function classobjectmul(self, other) + if type(self) == "table" and self.Multiply then + return self:Multiply(other) + else + AceOO:error("cannot multiply two incompatible objects") + end + end + local function classobjectdiv(self, other) + if type(self) == "table" and self.Divide then + return self:Divide(other) + else + AceOO:error("cannot divide two incompatible objects") + end + end + local function classobjectpow(self, other) + if type(self) == "table" and self.Exponent then + return self:Exponent(other) + else + AceOO:error("cannot exponentiate two incompatible objects") + end + end + local function classobjectconcat(self, other) + if type(self) == "table" and self.Concatenate then + return self:Concatenate(other) + else + AceOO:error("cannot concatenate two incompatible objects") + end + end + function class_new(self, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, + a13, a14, a15, a16, a17, a18, a19, a20) + if self.virtual then + AceOO:error("Cannot instantiate a virtual class.") + end + + local o = self.prototype + local newobj = {} + if o.class and o.class.instancemeta then + setmetatable(newobj, o.class.instancemeta) + else + Object:init(newobj, o) + end + + if self.interfaces and not self.interfacesVerified then + -- Verify the interfaces + + for interface in pairs(self.interfaces) do + for field,kind in pairs(interface.interface) do + if tostring(type(newobj[field])) ~= kind then + AceOO:error("Class did not satisfy all interfaces. %q is required to be a %s. It is a %s", field, kind, tostring(type(newobj[field]))) + end + end + end + self.interfacesVerified = true + end + local tmp = initStatus + initStatus = newobj + newobj:init(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, + a13, a14, a15, a16, a17, a18, a19, a20) + if initStatus then + initStatus = tmp + AceOO:error("Initialization not completed, be sure to call the superclass's init method.") + return + end + initStatus = tmp + return newobj + end + local classmeta = { + __tostring = objtostring, + __call = function(self, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, + a13, a14, a15, a16, a17, a18, a19, a20) + return self:new(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, + a13, a14, a15, a16, a17, a18, a19, a20) + end, + } + function Class:init(newclass, parent, a1, a2, a3, a4, a5, a6, a7, a8, a9, + a10, a11, a12, a13, a14, a15, a16, + a17, a18, a19, a20) + parent = parent or self + + local total + + if parent.class then + total = { + parent, a1, a2, a3, a4, a5, a6, a7, a8, a9, + a10, a11, a12, a13, a14, a15, a16, + a17, a18, a19, a20 + } + parent = self + else + total = { + a1, a2, a3, a4, a5, a6, a7, a8, a9, + a10, a11, a12, a13, a14, a15, a16, + a17, a18, a19, a20 + } + end + if not inherits(parent, Class) then + AceOO:error("Classes must inherit from a proper class") + end + if parent.sealed then + AceOO:error("Cannot inherit from a sealed class") + end + for i,v in ipairs(total) do + if inherits(v, Mixin) and v.class then + if not newclass.mixins then + newclass.mixins = {} + end + if newclass.mixins[v] then + AceOO:error("Cannot explicitly inherit from the same mixin twice") + end + newclass.mixins[v] = true + elseif inherits(v, Interface) and v.class then + if not newclass.interfaces then + newclass.interfaces = {} + end + if newclass.interfaces[v] then + AceOO:error("Cannot explicitly inherit from the same interface twice") + end + newclass.interfaces[v] = true + else + AceOO:error("Classes can only inherit from one or zero classes and any number of mixins or interfaces") + end + end + if parent.interfaces then + if newclass.interfaces then + for interface in pairs(parent.interfaces) do + newclass.interfaces[interface] = true + end + else + newclass.interfaces = parent.interfaces + end + end + for k in pairs(total) do + total[k] = nil + end + table_setn(total, 0) + + newclass.super = parent + + newclass.prototype = setmetatable(total, { + __index = parent.prototype, + __tostring = protostring, + }) + total = nil + + newclass.instancemeta = { + __index = newclass.prototype, + __tostring = classobjectstring, + __eq = classobjectequal, + __lt = classobjectlessthan, + __le = classobjectlessthanequal, + __add = classobjectadd, + __sub = classobjectsub, + __unm = classobjectunm, + __mul = classobjectmul, + __div = classobjectdiv, + __pow = classobjectpow, + __concat = classobjectconcat, + } + + setmetatable(newclass, classmeta) + + newclass.new = class_new + + if newclass.mixins then + -- Fold in the mixins + local err, msg + for mixin in pairs(newclass.mixins) do + local ret + autoEmbed = true + ret, msg = pcall(mixin.embed, mixin, newclass.prototype) + autoEmbed = false + if not ret then + err = true + break + end + end + + if err then + local pt = newclass.prototype + for k,v in pairs(pt) do + pt[k] = nil + end + + -- method conflict + AceOO:error(msg) + end + end + + newclass.prototype.class = newclass + + if newclass.interfaces then + for interface in pairs(newclass.interfaces) do + traverseInterfaces(interface, newclass.interfaces) + end + end + if newclass.mixins then + for mixin in pairs(newclass.mixins) do + if mixin.interfaces then + if not newclass.interfaces then + newclass.interfaces = {} + end + for interface in pairs(mixin.interfaces) do + newclass.interfaces[interface] = true + end + end + end + end + end + function Class:ToString() + if type(self.GetLibraryVersion) == "function" then + return (self:GetLibraryVersion()) + else + return "Class" + end + end + + local tmp + function Class.prototype:init() + if rawequal(self, initStatus) then + initStatus = nil + else + AceOO:error("Improper self passed to init. You must do MyClass.super.prototype.init(self, ...)", 2) + end + self.uid = getuid(self) + local current = self.class + while true do + if current == Class then + break + end + if current.mixins then + for mixin in pairs(current.mixins) do + if type(mixin.OnInstanceInit) == "function" then + mixin:OnInstanceInit(self) + end + end + end + current = current.super + end + end +end + + +-- @object ClassFactory +-- @brief A factory for creating classes. Rarely used directly. +local ClassFactory = Factory(Object, Class, Object) + +function Class:new(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, + a12, a13, a14, a15, a16, a17, a18, a19, a20) + local x = ClassFactory:new(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, + a12, a13, a14, a15, a16, a17, a18, a19, a20) + if AceOO.classes then + AceOO.classes[x] = true + end + return x +end +getmetatable(Class).__call = Class.new + +-- @class Mixin +-- @brief A class to create mixin objects, which contain methods that get +-- "mixed in" to class prototypes. +-- +-- @object Mixin prototype +-- @brief The prototype that mixin objects inherit their methods from. +-- +-- @method Mixin prototype embed +-- @brief Mix in the methods of our object which are listed in our interface +-- to the supplied target table. +-- +-- @method Mixin prototype init +-- @brief Initialize the mixin object. +-- @param newobj The new object we're initializing. +-- @param interface The interface we implement (the list of methods our +-- prototype provides which should be mixed into the target +-- table by embed). +do + Mixin = Class() + function Mixin:ToString() + if self.GetLibraryVersion then + return (self:GetLibraryVersion()) + else + return 'Mixin' + end + end + local function _Embed(state, field, target) + field = next(state.export, field) + if field == nil then + return + end + + if rawget(target, field) or (target[field] and target[field] ~= state[field]) then + AceOO:error("Method conflict in attempt to mixin. Field %q", field) + end + + target[field] = state[field] + + local ret,msg = pcall(_Embed, state, field, target) + if not ret then + -- Mix in the next method according to the defined interface. If that + -- fails due to a conflict, re-raise to back out the previous mixed + -- methods. + + target[field] = nil + AceOO:error(msg) + end + end + function Mixin.prototype:embed(target) + local mt = getmetatable(target) + setmetatable(target, nil) + local err, msg = pcall(_Embed, self, nil, target) + if not err then + setmetatable(target, mt) + AceOO:error(msg) + return + end + if type(self.embedList) == "table" then + self.embedList[target] = true + end + if type(target.class) ~= "table" then + target[self] = true + end + if not autoEmbed and type(self.OnManualEmbed) == "function" then + self:OnManualEmbed(target) + end + setmetatable(target, mt) + end + + function Mixin.prototype:activate(oldLib, oldDeactivate) + if oldLib and oldLib.embedList then + for target in pairs(oldLib.embedList) do + local mt = getmetatable(target) + setmetatable(target, nil) + for field in pairs(oldLib.export) do + target[field] = nil + end + setmetatable(target, mt) + end + self.embedList = oldLib.embedList + for target in pairs(self.embedList) do + self:embed(target) + end + else + self.embedList = setmetatable({}, {__mode="k"}) + end + end + + function Mixin.prototype:init(export, a1, a2, a3, a4, a5, a6, a7, a8, a9, + a10, a11, a12, a13, a14, a15, a16, + a17, a18, a19, a20) + AceOO:argCheck(export, 2, "table") + for k,v in pairs(export) do + if type(k) ~= "number" then + AceOO:error("All keys to argument #2 must be numbers.") + elseif type(v) ~= "string" then + AceOO:error("All values to argument #2 must be strings.") + end + end + local num = table.getn(export) + for i = 1, num do + local v = export[i] + export[i] = nil + export[v] = true + end + table_setn(export, 0) + local interfaces + if a1 then + local l = getlibrary + interfaces = { l(a1), l(a2), l(a3), l(a4), l(a5), l(a6), l(a7), l(a8), + l(a9), l(a10), l(a11), l(a12), l(a13), l(a14), l(a15), l(a16), + l(a17), l(a18), l(a19), l(a20) } + for _,v in ipairs(interfaces) do + if not v.class or not inherits(v, Interface) then + AceOO:error("Mixins can inherit only from interfaces") + end + end + local num = table.getn(interfaces) + for i = 1, num do + local v = interfaces[i] + interfaces[i] = nil + interfaces[v] = true + end + table_setn(interfaces, 0) + for interface in pairs(interfaces) do + traverseInterfaces(interface, interfaces) + end + for interface in pairs(interfaces) do + for field,kind in pairs(interface.interface) do + if kind ~= "nil" then + local good = false + for bit in pairs(export) do + if bit == field then + good = true + break + end + end + if not good then + AceOO:error("Mixin does not fully accommodate field %q", field) + end + end + end + end + end + self.super = Mixin.prototype + Mixin.super.prototype.init(self) + self.export = export + self.interfaces = interfaces + end +end + +-- @class Interface +-- @brief A class to create interfaces, which contain contracts that classes +-- which inherit from this must comply with. +-- +-- @object Interface prototype +-- @brief The prototype that interface objects must adhere to. +-- +-- @method Interface prototype init +-- @brief Initialize the mixin object. +-- @param interface The interface we contract (the hash of fields forced). +-- @param (a1..a20) Superinterfaces +do + Interface = Class() + function Interface:ToString() + if self.GetLibraryVersion then + return (self:GetLibraryVersion()) + else + return 'Instance' + end + end + function Interface.prototype:init(interface, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) + Interface.super.prototype.init(self) + AceOO:argCheck(interface, 2, "table") + for k,v in pairs(interface) do + if type(k) ~= "string" then + AceOO:error("All keys to argument #2 must be numbers.") + elseif type(v) ~= "string" then + AceOO:error("All values to argument #2 must be strings.") + elseif v ~= "nil" and v ~= "string" and v ~= "number" and v ~= "table" and v ~= "function" then + AceOO:error('All values to argument #2 must either be "nil", "string", "number", "table", or "function".') + end + end + local l = getlibrary + a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20 = l(a1), l(a2), l(a3), l(a4), l(a5), l(a6), l(a7), l(a8), l(a9), l(a10), l(a11), l(a12), l(a13), l(a14), l(a15), l(a16), l(a17), l(a18), l(a19), l(a20) + if a1 then + self.superinterfaces = {a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20} + for k,v in ipairs(self.superinterfaces) do + if not inherits(v, Interface) or not v.class then + AceOO:error('Cannot provide a non-Interface to inherit from') + end + end + local num = table.getn(self.superinterfaces) + for i = 1, num do + local v = self.superinterfaces[i] + self.superinterfaces[i] = nil + self.superinterfaces[v] = true + end + table_setn(self.superinterfaces, 0) + end + self.interface = interface + end +end + +-- @function Classpool +-- @brief Obtain a read only class from our pool of classes, indexed by the +-- superclass and mixins. +-- @param sc The superclass of the class we want. +-- @param (m1..m20) Mixins of the class we want's objects. +-- @return A read only class from the class pool. +local Classpool +do + local pool = setmetatable({}, {__mode = 'v'}) + local function newindex(k, v) + AceOO:error('Attempt to modify a read-only class.') + end + local function protonewindex(k, v) + AceOO:error('Attempt to modify a read-only class prototype.') + end + local function ts(bit) + if type(bit) ~= "table" then + return tostring(bit) + elseif getmetatable(bit) and bit.__tostring then + return tostring(bit) + elseif type(bit.GetLibraryVersion) == "function" then + return bit:GetLibraryVersion() + else + return tostring(bit) + end + end + local t + local function getcomplexuid(sc, m1, m2, m3, m4, m5, m6, m7, m8, m9, + m10, m11, m12, m13, m14, m15, m16, m17, m18, m19, m20) + if not t then t = {} end + if sc then if sc.uid then table.insert(t, sc.uid) else AceOO:error("%s is not an appropriate class/mixin", ts(sc)) end + if m1 then if m1.uid then table.insert(t, m1.uid) else AceOO:error("%s is not an appropriate mixin", ts(m1)) end + if m2 then if m2.uid then table.insert(t, m2.uid) else AceOO:error("%s is not an appropriate mixin", ts(m2)) end + if m3 then if m3.uid then table.insert(t, m3.uid) else AceOO:error("%s is not an appropriate mixin", ts(m3)) end + if m4 then if m4.uid then table.insert(t, m4.uid) else AceOO:error("%s is not an appropriate mixin", ts(m4)) end + if m5 then if m5.uid then table.insert(t, m5.uid) else AceOO:error("%s is not an appropriate mixin", ts(m5)) end + if m6 then if m6.uid then table.insert(t, m6.uid) else AceOO:error("%s is not an appropriate mixin", ts(m6)) end + if m7 then if m7.uid then table.insert(t, m7.uid) else AceOO:error("%s is not an appropriate mixin", ts(m7)) end + if m8 then if m8.uid then table.insert(t, m8.uid) else AceOO:error("%s is not an appropriate mixin", ts(m8)) end + if m9 then if m9.uid then table.insert(t, m9.uid) else AceOO:error("%s is not an appropriate mixin", ts(m9)) end + if m10 then if m10.uid then table.insert(t, m10.uid) else AceOO:error("%s is not an appropriate mixin", ts(m10)) end + if m11 then if m11.uid then table.insert(t, m11.uid) else AceOO:error("%s is not an appropriate mixin", ts(m11)) end + if m12 then if m12.uid then table.insert(t, m12.uid) else AceOO:error("%s is not an appropriate mixin", ts(m12)) end + if m13 then if m13.uid then table.insert(t, m13.uid) else AceOO:error("%s is not an appropriate mixin", ts(m13)) end + if m14 then if m14.uid then table.insert(t, m14.uid) else AceOO:error("%s is not an appropriate mixin", ts(m14)) end + if m15 then if m15.uid then table.insert(t, m15.uid) else AceOO:error("%s is not an appropriate mixin", ts(m15)) end + if m16 then if m16.uid then table.insert(t, m16.uid) else AceOO:error("%s is not an appropriate mixin", ts(m16)) end + if m17 then if m17.uid then table.insert(t, m17.uid) else AceOO:error("%s is not an appropriate mixin", ts(m17)) end + if m18 then if m18.uid then table.insert(t, m18.uid) else AceOO:error("%s is not an appropriate mixin", ts(m18)) end + if m19 then if m19.uid then table.insert(t, m19.uid) else AceOO:error("%s is not an appropriate mixin", ts(m19)) end + if m20 then if m20.uid then table.insert(t, m20.uid) else AceOO:error("%s is not an appropriate mixin", ts(m20)) end + end end end end end end end end end end end end end end end end end end end end end + table.sort(t) + local uid = table.concat(t, '') + for k in pairs(t) do t[k] = nil end + table_setn(t, 0) + return uid + end + local classmeta + function Classpool(sc, m1, m2, m3, m4, m5, m6, m7, m8, m9, + m10, m11, m12, m13, m14, m15, m16, + m17, m18, m19, m20) + local l = getlibrary + sc, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11, m12, m13, m14, m15, m16, m17, m18, m19, m20 = l(sc), l(m1), l(m2), l(m3), l(m4), l(m5), l(m6), l(m7), l(m8), l(m9), l(m10), l(m11), l(m12), l(m13), l(m14), l(m15), l(m16), l(m17), l(m18), l(m19), l(m20) + if sc and sc.class then + sc, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11, m12, m13, m14, m15, m16, m17, m18, m19, m20 = Class, sc, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11, m12, m13, m14, m15, m16, m17, m18, m19 + end + sc = sc or Class + local key = getcomplexuid(sc, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11, m12, m13, m14, m15, m16, m17, m18, m19, m20) + if not pool[key] then + local class = Class(sc, m1, m2, m3, m4, m5, m6, m7, m8, m9, + m10, m11, m12, m13, m14, m15, m16, m17, + m18, m19, m20) + if not classmeta then + classmeta = {} + local mt = getmetatable(class) + for k,v in pairs(mt) do + classmeta[k] = v + end + classmeta.__newindex = newindex + end + -- Prevent the user from adding methods to this class. + -- NOTE: I'm not preventing modifications of existing class members, + -- but it's likely that only a truly malicious user will be doing so. + class.sealed = true + setmetatable(class, classmeta) + getmetatable(class.prototype).__newindex = protonewindex + pool[key] = class + end + return pool[key] + end +end + +AceOO.Factory = Factory +AceOO.Object = Object +AceOO.Class = Class +AceOO.Mixin = Mixin +AceOO.Interface = Interface +AceOO.Classpool = Classpool +AceOO.inherits = inherits + +-- Library handling bits + +local function activate(self, oldLib, oldDeactivate) + AceOO = self + Factory = self.Factory + Object = self.Object + Class = self.Class + ClassFactory.prototype = Class + Mixin = self.Mixin + Interface = self.Interface + Classpool = self.Classpool + + if oldLib then + self.classes = oldLib.classes + end + if not self.classes then + self.classes = setmetatable({}, {__mode="k"}) + else + for class in pairs(self.classes) do + class.new = class_new + end + end + + if oldDeactivate then + oldDeactivate(oldLib) + end +end + +AceLibrary:Register(AceOO, MAJOR_VERSION, MINOR_VERSION, activate) +AceOO = AceLibrary(MAJOR_VERSION) diff --git a/libs/AceTab-2.0/AceTab-2.0.lua b/libs/AceTab-2.0/AceTab-2.0.lua new file mode 100755 index 0000000..cb11f93 --- /dev/null +++ b/libs/AceTab-2.0/AceTab-2.0.lua @@ -0,0 +1,328 @@ +--[[ +Name: AceTab-2.0 +Revision: $Rev: 17638 $ +Developed by: The Ace Development Team (http://www.wowace.com/index.php/The_Ace_Development_Team) +Website: http://www.wowace.com/ +Documentation: http://www..wowace.com/index.php/AceTab-2.0 +SVN: http://svn.wowace.com/root/trunk/Ace2/AceTab-2.0 +Description: A tab-completion library +Dependencies: AceLibrary, AceEvent-2.0 +]] + +local MAJOR_VERSION = "AceTab-2.0" +local MINOR_VERSION = "$Revision: 17638 $" + +if not AceLibrary then error(MAJOR_VERSION .. " requires AceLibrary.") end +if not AceLibrary:IsNewVersion(MAJOR_VERSION, MINOR_VERSION) then return end + +if loadstring("return function(...) return ... end") and AceLibrary:HasInstance(MAJOR_VERSION) then return end -- lua51 check +local AceEvent +local AceTab = {} +local _G = getfenv() + +local hookedFrames = {} +local framesHooked = {} + +function AceTab:RegisterTabCompletion(descriptor, regex, wlfunc, usage, editframes) + self:argCheck(descriptor, 2, "string") + self:argCheck(regex, 3, "string", "table") + self:argCheck(wlfunc, 4, "string", "function", "nil") + self:argCheck(usage, 5, "string", "function", "boolean", "nil") + self:argCheck(editframe, 6, "string", "table", "nil") + + if type(regex) == "string" then regex = {regex} end + + if type(wlfunc) == "string" and type(self[wlfunc]) ~= "function" then + self:error("Cannot register function %q; it does not exist", wlfunc) + end + + if type(usage) == "string" and type(self[usage]) ~= "function" then + self:error("Cannot register usage function %q; it does not exist", usage) + end + + if not editframes then editframes = {"ChatFrameEditBox"} end + + if type(editframes) == "table" and editframes.Show then editframes = {editframes:GetName()} end + + for _, frame in pairs(editframes) do + local Gframe + if type(frame) == "table" then + Gframe = frame + frame = frame:GetName() + else + Gframe = _G[frame] + end + + if type(Gframe) ~= "table" or not Gframe.Show then + self:error("Cannot register frame %q; it does not exist", frame) + frame = nil + end + + if frame then + if Gframe:GetFrameType() ~= "EditBox" then + self:error("Cannot register frame %q; it is not an EditBox", frame) + frame = nil + else + if AceEvent and AceEvent:IsFullyInitialized() then + if not framesHooked[Gframe] then + framesHooked[Gframe] = true + local orig = Gframe:GetScript("OnTabPressed") + if type(orig) ~= "function" then + orig = function() end + end + Gframe:SetScript("OnTabPressed", function() + if self:OnTabPressed(Gframe) then + return orig() + end + end) + Gframe.curMatch = 0 + Gframe.matches = {} + Gframe.pMatchLen = 0 + end + else + hookedFrames[frame] = true + end + end + end + end + + if not self.registry[descriptor] then + self.registry[descriptor] = {} + end + + if not self.registry[descriptor][self] then + self.registry[descriptor][self] = {} + end + self.registry[descriptor][self] = {patterns = regex, wlfunc = wlfunc, usage = usage, frames = editframes} + + + if not AceEvent and AceLibrary:HasInstance("AceEvent-2.0") then + external(AceTab, "AceEvent-2.0", AceLibrary("AceEvent-2.0")) + end + if AceEvent then + if not self:IsEventRegistered("AceEvent_FullyInitialized") then + self:RegisterEvent("AceEvent_FullyInitialized", "AceEvent_FullyInitialized", true) + end + end +end + +function AceTab:IsTabCompletionRegistered(descriptor) + self:argCheck(descriptor, 2, "string") + return self.registry[descriptor] and self.registry[descriptor][self] +end + +function AceTab:UnregisterTabCompletion(descriptor) + self:argCheck(descriptor, 2, "string") + if self.registry[descriptor] and self.registry[descriptor][self] then + self.registry[descriptor][self] = nil + else + self:error("Cannot unregister a tab completion (%s) that you have not registered.", descriptor) + end +end + +local GCS +GCS = function(s1, s2) + if not s1 and not s2 then return end + if not s1 then s1 = s2 end + if not s2 then s2 = s1 end + local s1len, s2len = string.len(s1), string.len(s2) + if s2len < s1len then + s1, s2 = s2, s1 + end + if string.find(string.lower(s2), string.lower(s1)) then + return s1 + else + return GCS(string.sub(s1, 1, -2), s2) + end +end +local pos +local function CycleTab() + this.pMatchLen = string.len(this.lMatch) + local cMatch = 0 + local matched = false + for desc, mList in pairs(this.matches) do + if not matched then + for _, m in ipairs(mList) do + cMatch = cMatch + 1 + if cMatch == this.curMatch then + this.lMatch = m + this.curMatch = this.curMatch + 1 + matched = true + break + end + end + end + end + if not matched then + this.curMatch = 1 + this.lMatch = this.origWord + end + this:HighlightText(pos - this.pMatchLen, pos) + this:Insert(this.lMatch) +end + +function AceTab:OnTabPressed() + local ost = this:GetScript("OnTextSet") + if type(ost) ~= "function" then + ost = nil + end + if ost then this:SetScript("OnTextSet", nil) end + if this:GetText() == "" then return true end + this:Insert("\255") + pos = string.find(this:GetText(), "\255", 1) - 1 + this:HighlightText(pos, pos+1) + this:Insert("\0") + if ost then this:SetScript("OnTextSet", ost) end + local fulltext = this:GetText() + local text = string.sub(fulltext, 0, pos) or "" + + local left = string.find(string.sub(text, 1, pos), "%w+$") + left = left and left-1 or pos + if not left or left == 1 and string.sub(text, 1, 1) == "/" then return true end + + local _, _, word = string.find(string.sub(text, left, pos), "(%w+)") + word = word or "" + this.lMatch = this.curMatch > 0 and (this.lMatch or this.origWord) + + if this.lMatch and this.lMatch ~= "" and string.find(string.sub(text, 1, pos), this.lMatch.."$") then + return CycleTab() + else + this.matches = {} + this.curMatch = 0 + this.lMatch = nil + end + + local completions = {} + local numMatches = 0 + local firstMatch, hasNonFallback + + for desc, entry in pairs(AceTab.registry) do + for _, s in pairs(entry) do + for _, f in pairs(s.frames) do + if _G[f] == this then + for _, regex in ipairs(s.patterns) do + local cands = {} + if string.find(string.sub(text, 1, left), regex.."$") then + local c = s.wlfunc(cands, fulltext, left) + if c ~= false then + local mtemp = {} + this.matches[desc] = this.matches[desc] or {} + for _, cand in ipairs(cands) do + if string.find(string.lower(cand), string.lower(word), 1, 1) == 1 then + mtemp[cand] = true + numMatches = numMatches + 1 + if numMatches == 1 then firstMatch = cand end + end + end + for i in pairs(mtemp) do + table.insert(this.matches[desc], i) + end + this.matches[desc].usage = s.usage + if regex ~= "" and this.matches[desc][1] then + hasNonFallback = true + this.matches[desc].notFallback = true + end + end + end + end + end + end + end + end + + local _, set = next(this.matches) + if not set or numMatches == 0 and not hasNonFallback then return true end + + this:HighlightText(left, left + string.len(word)) + if numMatches == 1 then + this:Insert(firstMatch) + this:Insert(" ") + else + if this.curMatch == 0 then + this.curMatch = 1 + this.origWord = word + this.lMatch = word + CycleTab() + end + local gcs + for h, c in pairs(this.matches) do + if hasNonFallback and not c.notFallback then break end + local u = c.usage + c.usage = nil + local candUsage = u and {} + local gcs2 + if next(c) then + if not u then DEFAULT_CHAT_FRAME:AddMessage(h..":") end + for _, m in ipairs(c) do + if not u then DEFAULT_CHAT_FRAME:AddMessage(m) end + gcs2 = GCS(gcs2, m) + end + end + gcs = GCS(gcs, gcs2) + if u then + if type(u) == "function" then + local us = u(candUsage, c, gcs2, string.sub(text, 1, left)) + if candUsage and next(candUsage) then us = candUsage end + if type(us) == "string" then + DEFAULT_CHAT_FRAME:AddMessage(us) + elseif type(us) == "table" and numMatches > 0 then + for _, v in ipairs(c) do + if us[v] then DEFAULT_CHAT_FRAME:AddMessage(string.format("%s - %s", v, us[v])) end + end + end + end + end + end + if curMatch == 0 then + this:Insert(gcs or word) + end + end +end + +function AceTab:AceEvent_FullyInitialized() + for frame in pairs(hookedFrames) do + local Gframe = _G[frame] + if not framesHooked[Gframe] then + framesHooked[Gframe] = true + local orig = Gframe:GetScript("OnTabPressed") + if type(orig) ~= "function" then + orig = function() end + end + Gframe:SetScript("OnTabPressed", function() + if self:OnTabPressed(Gframe) then + return orig() + end + end) + Gframe.curMatch = 0 + Gframe.matches = {} + Gframe.pMatchLen = 0 + end + end +end + +local function external(self, major, instance) + if major == "AceEvent-2.0" then + if not AceEvent then + AceEvent = instance + + AceEvent:embed(self) + end + end +end + +local function activate(self, oldLib, oldDeactivate) + if oldLib then + self.registry = oldLib.registry + end + + if not self.registry then + self.registry = {} + end + + if oldDeactivate then + oldDeactivate(oldLib) + end +end + +AceLibrary:Register(AceTab, MAJOR_VERSION, MINOR_VERSION, activate, nil, external) +AceTab = AceLibrary(MAJOR_VERSION) diff --git a/libs/Babble-Class-2.2/Babble-Class-2.2.lua b/libs/Babble-Class-2.2/Babble-Class-2.2.lua new file mode 100755 index 0000000..6fc8baa --- /dev/null +++ b/libs/Babble-Class-2.2/Babble-Class-2.2.lua @@ -0,0 +1,150 @@ +--[[ +Name: Babble-Class-2.2 +Revision: $Rev: 14410 $ +Author(s): ckknight (ckknight@gmail.com) +Website: http://ckknight.wowinterface.com/ +Documentation: http://wiki.wowace.com/index.php/Babble-Class-2.2 +SVN: http://svn.wowace.com/root/trunk/Babble-2.2/Babble-Class-2.2 +Description: A library to provide localizations for classes. +Dependencies: AceLibrary, AceLocale-2.2 +]] + +local MAJOR_VERSION = "Babble-Class-2.2" +local MINOR_VERSION = tonumber(string.sub("$Revision: 14410 $", 12, -3)) + +if not AceLibrary then error(MAJOR_VERSION .. " requires AceLibrary") end + +if not AceLibrary:HasInstance("AceLocale-2.2") then error(MAJOR_VERSION .. " requires AceLocale-2.2") end + +local _, x = AceLibrary("AceLocale-2.2"):GetLibraryVersion() +MINOR_VERSION = MINOR_VERSION * 100000 + x + +if not AceLibrary:IsNewVersion(MAJOR_VERSION, MINOR_VERSION) then return end + +local BabbleClass = AceLibrary("AceLocale-2.2"):new(MAJOR_VERSION) + +-- uncomment below for debug information +-- BabbleClass:EnableDebugging() + +BabbleClass:RegisterTranslations("enUS", function() + return { + ["Warlock"] = true, + ["Warrior"] = true, + ["Hunter"] = true, + ["Mage"] = true, + ["Priest"] = true, + ["Druid"] = true, + ["Paladin"] = true, + ["Shaman"] = true, + ["Rogue"] = true, + } +end) + +BabbleClass:RegisterTranslations("ruRU", function() + return { + ["Warlock"] = "Чернокнижник", + ["Warrior"] = "Воин", + ["Hunter"] = "Охотник", + ["Mage"] = "Маг", + ["Priest"] = "Жрец", + ["Druid"] = "Друид", + ["Paladin"] = "Паладин", + ["Shaman"] = "Шаман", + ["Rogue"] = "Разбойник", + } +end) + +BabbleClass:RegisterTranslations("deDE", function() + return { + ["Warlock"] = "Hexenmeister", + ["Warrior"] = "Krieger", + ["Hunter"] = "Jäger", + ["Mage"] = "Magier", + ["Priest"] = "Priester", + ["Druid"] = "Druide", + ["Paladin"] = "Paladin", + ["Shaman"] = "Schamane", + ["Rogue"] = "Schurke", + } +end) + +BabbleClass:RegisterTranslations("frFR", function() + return { + ["Warlock"] = "D\195\169moniste", + ["Warrior"] = "Guerrier", + ["Hunter"] = "Chasseur", + ["Mage"] = "Mage", + ["Priest"] = "Pr\195\170tre", + ["Druid"] = "Druide", + ["Paladin"] = "Paladin", + ["Shaman"] = "Chaman", + ["Rogue"] = "Voleur", + } +end) + +BabbleClass:RegisterTranslations("zhCN", function() + return { + ["Warlock"] = "术士", + ["Warrior"] = "战士", + ["Hunter"] = "猎人", + ["Mage"] = "法师", + ["Priest"] = "牧师", + ["Druid"] = "德鲁伊", + ["Paladin"] = "圣骑士", + ["Shaman"] = "萨满祭司", + ["Rogue"] = "盗贼", + } +end) + +BabbleClass:RegisterTranslations("zhTW", function() + return { + ["Warlock"] = "術士", + ["Warrior"] = "戰士", + ["Hunter"] = "獵人", + ["Mage"] = "法師", + ["Priest"] = "牧師", + ["Druid"] = "德魯伊", + ["Paladin"] = "聖騎士", + ["Shaman"] = "薩滿", + ["Rogue"] = "盜賊", + } +end) + +BabbleClass:RegisterTranslations("koKR", function() + return { + ["Warlock"] = "흑마법사", + ["Warrior"] = "전사", + ["Hunter"] = "사냥꾼", + ["Mage"] = "마법사", + ["Priest"] = "사제", + ["Druid"] = "드루이드", + ["Paladin"] = "성기사", + ["Shaman"] = "주술사", + ["Rogue"] = "도적", + } +end) + +BabbleClass:Debug() +BabbleClass:SetStrictness(true) + +function BabbleClass:GetColor(class) + self:argCheck(class, 2, "string") + if self:HasReverseTranslation(class) then + class = string.upper(self:GetReverseTranslation(class)) + else + class = string.upper(class) + end + if RAID_CLASS_COLORS and RAID_CLASS_COLORS[class] then + return RAID_CLASS_COLORS[class].r, RAID_CLASS_COLORS[class].g, RAID_CLASS_COLORS[class].b + end + return 0.63, 0.63, 0.63 +end + +function BabbleClass:GetHexColor(class) + self:argCheck(class, 2, "string") + local r, g, b = self:GetColor(class) + return string.format("%02x%02x%02x", r * 255, g * 255, b * 255) +end + +AceLibrary:Register(BabbleClass, MAJOR_VERSION, MINOR_VERSION) +BabbleClass = nil diff --git a/libs/Waterfall-1.0/Waterfall-1.0.lua b/libs/Waterfall-1.0/Waterfall-1.0.lua new file mode 100755 index 0000000..6b0461d --- /dev/null +++ b/libs/Waterfall-1.0/Waterfall-1.0.lua @@ -0,0 +1,4863 @@ +--[[ +Name: Waterfall-1.0 +Revision: $Revision: 78661 $ +Author(s): Nargiddley (nargiddley@gmail.com) +Inspired By: Dewdrop by ckknight +Website: http://www.wowace.com/wiki/Waterfall-1.0 +Documentation: http://www.wowace.com/wiki/Waterfall-1.0 +SVN: http://svn.wowace.com/wowace/trunk/Waterfall-1.0 +Description: Gui Configuration Library +License: LGPL 2.1 +Dependencies: AceOO-2.0 +]] + +local MAJOR_VERSION = "Waterfall-1.0" +local MINOR_VERSION = "$Revision: 78663 $" + +local CONTROL_LIMIT = 250 +local _ + +local tinsert = table.insert +local tremove = table.remove +local table_setn = table.setn + +if not AceLibrary then error(MAJOR_VERSION .. " requires AceLibrary") end +if not AceLibrary:IsNewVersion(MAJOR_VERSION, MINOR_VERSION) then return end + +if not AceLibrary:HasInstance("AceOO-2.0") then error(MAJOR_VERSION .. " requires AceOO-2.0.") end + +local AceOO = AceLibrary("AceOO-2.0") + +local Waterfall = {} + +local currentframe + +local OPTIONS = "Options" +local ARROW = " -> " +local ARE_YOU_SURE = "Are you sure you want to %s?" + +if GetLocale() == "zhCN" then + OPTIONS = "选项" + ARROW = " -> " + ARE_YOU_SURE = "你确定要 %s么?" +elseif GetLocale() == "zhTW" then + OPTIONS = "選項" + ARROW = " > " + ARE_YOU_SURE = "你確定要「%s」嗎?" +elseif GetLocale() == "koKR" then + OPTIONS = "설정" + ARE_YOU_SURE = "정말 당신은 `%s'|1을;를; 하시겠습니까?" +elseif GetLocale() == "ruRU" then + OPTIONS = "Опции" + ARE_YOU_SURE = "Вы уверены что вы хотите %s?" +end + +local DEFAULT_CONTROL_WIDTH = 180 +--[[ +local print = Sea.io.print +local function printTable(table,rowname,level) + if ( level == nil ) then level = 1; end + + if ( type(rowname) == "nil" ) then rowname = "ROOT"; + elseif ( type(rowname) == "string" ) then + rowname = "\""..rowname.."\""; + elseif ( type(rowname) ~= "number" ) then + rowname = "*"..type(rowname).."*"; + end + + local msg = ""; + + + + if ( table == nil ) then + Sea.io.print(msg,"[",rowname,"] := nil "); return + end + if ( type(table) == "table" and level > 0 ) then + Sea.io.print (msg,rowname," { "); + for k,v in table do + if v == nil then Sea.io.print(msg,"[",rowname,"] := nil "); end + printTable(v,k,level-1); + end + Sea.io.print(msg,"}"); + elseif (type(table) == "function" ) then + Sea.io.print(msg,"[",rowname,"] => {{FunctionPtr*}}"); + elseif (type(table) == "userdata" ) then + Sea.io.print(msg,"[",rowname,"] => {{UserData}}"); + elseif (type(table) == "boolean" ) then + local value = "true"; + if ( not table ) then + value = "false"; + end + Sea.io.print(msg,"[",rowname,"] => ",value); + else + Sea.io.print(msg,"[",rowname,"] => ",table); + end +end +]] + +--[[ + Passing an AceOO class will get an object of that class from the pool + You dont need to specify the class when releasing it + Passing a string will get a table from a named pool + the string must be passed when releasing tables like this +--]] +local getObj, releaseObj +do + local objPools = {} + function getObj(class,...) + if not objPools[class] then + objPools[class] = {} + end + local newObj = tremove(objPools[class]) + if not newObj then + if type(class) ~= "string" and type(class.new) == "function" then + newObj = class:new(unpack(arg)) + else + newObj = {} + end + end + return newObj + end + function releaseObj(obj,class) + if not class then + class = obj.class + end + if not class then error("You must supply a class to release non AceOO objects") end + if not objPools[class] then + objPools[class] = {} + end + if type(obj.CleanUp) == "function" then + obj:CleanUp() + end + tinsert(objPools[class],obj) + end +end +--[[ +--borrowed from Dewdrop-2.0 +local function new(k1, v1, k2, v2, k3, v3, k4, v4, k5, v5, k6, v6, k7, v7, k8, v8, k9, v9, k10, v10, k11, v11, k12, v12, k13, v13, k14, v14, k15, v15, k16, v16, k17, v17, k18, v18, k19, v19, k20, v20) + local t = {} + if k1 then t[k1] = v1 + if k2 then t[k2] = v2 + if k3 then t[k3] = v3 + if k4 then t[k4] = v4 + if k5 then t[k5] = v5 + if k6 then t[k6] = v6 + if k7 then t[k7] = v7 + if k8 then t[k8] = v8 + if k9 then t[k9] = v9 + if k10 then t[k10] = v10 + if k11 then t[k11] = v11 + if k12 then t[k12] = v12 + if k13 then t[k13] = v13 + if k14 then t[k14] = v14 + if k15 then t[k15] = v15 + if k16 then t[k16] = v16 + if k17 then t[k17] = v17 + if k18 then t[k18] = v18 + if k19 then t[k19] = v19 + if k20 then t[k20] = v20 + end end end end end end end end end end end end end end end end end end end end + return t +end +local tmp +do + local t + function tmp(k1, v1, k2, v2, k3, v3, k4, v4, k5, v5, k6, v6, k7, v7, k8, v8, k9, v9, k10, v10, k11, v11, k12, v12, k13, v13, k14, v14, k15, v15, k16, v16, k17, v17, k18, v18, k19, v19, k20, v20) + for k in pairs(t) do + t[k] = nil + end + if type(k1) == "table" then + for k,v in pairs(k1) do + t[k] = v + end + else + if k1 then t[k1] = v1 + if k2 then t[k2] = v2 + if k3 then t[k3] = v3 + if k4 then t[k4] = v4 + if k5 then t[k5] = v5 + if k6 then t[k6] = v6 + if k7 then t[k7] = v7 + if k8 then t[k8] = v8 + if k9 then t[k9] = v9 + if k10 then t[k10] = v10 + if k11 then t[k11] = v11 + if k12 then t[k12] = v12 + if k13 then t[k13] = v13 + if k14 then t[k14] = v14 + if k15 then t[k15] = v15 + if k16 then t[k16] = v16 + if k17 then t[k17] = v17 + if k18 then t[k18] = v18 + if k19 then t[k19] = v19 + if k20 then t[k20] = v20 + end end end end end end end end end end end end end end end end end end end end + end + return t + end + local x = tmp + function tmp(k1, v1, k2, v2, k3, v3, k4, v4, k5, v5, k6, v6, k7, v7, k8, v8, k9, v9, k10, v10, k11, v11, k12, v12, k13, v13, k14, v14, k15, v15, k16, v16, k17, v17, k18, v18, k19, v19, k20, v20) + t = {} + tmp = x + x = nil + return tmp(k1, v1, k2, v2, k3, v3, k4, v4, k5, v5, k6, v6, k7, v7, k8, v8, k9, v9, k10, v10, k11, v11, k12, v12, k13, v13, k14, v14, k15, v15, k16, v16, k17, v17, k18, v18, k19, v19, k20, v20) + end +end +local tmp2 +do + local t + function tmp2(k1, v1, k2, v2, k3, v3, k4, v4, k5, v5, k6, v6, k7, v7, k8, v8, k9, v9, k10, v10, k11, v11, k12, v12, k13, v13, k14, v14, k15, v15, k16, v16, k17, v17, k18, v18, k19, v19, k20, v20) + for k in pairs(t) do + t[k] = nil + end + if k1 then t[k1] = v1 + if k2 then t[k2] = v2 + if k3 then t[k3] = v3 + if k4 then t[k4] = v4 + if k5 then t[k5] = v5 + if k6 then t[k6] = v6 + if k7 then t[k7] = v7 + if k8 then t[k8] = v8 + if k9 then t[k9] = v9 + if k10 then t[k10] = v10 + if k11 then t[k11] = v11 + if k12 then t[k12] = v12 + if k13 then t[k13] = v13 + if k14 then t[k14] = v14 + if k15 then t[k15] = v15 + if k16 then t[k16] = v16 + if k17 then t[k17] = v17 + if k18 then t[k18] = v18 + if k19 then t[k19] = v19 + if k20 then t[k20] = v20 + end end end end end end end end end end end end end end end end end end end end + return t + end + local x = tmp2 + function tmp2(k1, v1, k2, v2, k3, v3, k4, v4, k5, v5, k6, v6, k7, v7, k8, v8, k9, v9, k10, v10, k11, v11, k12, v12, k13, v13, k14, v14, k15, v15, k16, v16, k17, v17, k18, v18, k19, v19, k20, v20) + t = {} + tmp2 = x + x = nil + return tmp2(k1, v1, k2, v2, k3, v3, k4, v4, k5, v5, k6, v6, k7, v7, k8, v8, k9, v9, k10, v10, k11, v11, k12, v12, k13, v13, k14, v14, k15, v15, k16, v16, k17, v17, k18, v18, k19, v19, k20, v20) + end +end +]] + +local function new(...) + local t = {} + for i = 1, table.getn(arg), 2 do + local k = arg[i] + if k then + t[k] = arg[i+1] + else + break + end + end + return t +end + +local tmp +do + local t = {} + function tmp(...) + for k in pairs(t) do + t[k] = nil + end + table_setn(t,0) + for i = 1, table.getn(arg), 2 do + local k = arg[i] + if k then + t[k] = arg[i+1] + else + break + end + end + return t + end +end +local tmp2 +do + local t = {} + function tmp2(...) + for k in pairs(t) do + t[k] = nil + end + table_setn(t,0) + for i = 1, table.getn(arg), 2 do + local k = arg[i] + if k then + t[k] = arg[i+1] + else + break + end + end + return t + end +end + + +local function getArgs(t, str, num, ...) + local x = t[str .. num] + if x == nil then + return unpack(arg) + else + return x, getArgs(t, str, num + 1, unpack(arg)) + end +end + +local function confirmPopup(message, func, ...) + if not StaticPopupDialogs["WATERFALL01_CONFIRM_DIALOG"] then + StaticPopupDialogs["WATERFALL01_CONFIRM_DIALOG"] = {} + end + local t = StaticPopupDialogs["WATERFALL01_CONFIRM_DIALOG"] + for k in pairs(t) do + t[k] = nil + end + table_setn(t,0) + t.text = message + t.button1 = ACCEPT + t.button2 = CANCEL + t.OnAccept = function() + func(unpack(t)) + end + for i = 1, table.getn(arg) do + t[i] = arg[i] + end + t.timeout = 0 + t.whileDead = 1 + t.hideOnEscape = 1 + + StaticPopup_Show("WATERFALL01_CONFIRM_DIALOG") +end + +local function showGameTooltip(this) + if this.tooltipTitle or this.tooltipText then + GameTooltip_SetDefaultAnchor(GameTooltip, this.frame or this) + if this.tooltipTitle then + GameTooltip:SetText(this.tooltipTitle, 1, 1, 1, 1) + if this.tooltipText then + GameTooltip:AddLine(this.tooltipText, NORMAL_FONT_COLOR.r, NORMAL_FONT_COLOR.g, NORMAL_FONT_COLOR.b, 1) + end + else + GameTooltip:SetText(this.tooltipText, 1, 1, 1, 1) + end + GameTooltip:Show() + end +end +--end of borrowing + +local function parseList(list) + local token, rest = string.match(list,"([^%s]+)%s+(.+)") + if not rest then + return (tonumber(list) or list) + else + return (tonumber(token) or token), parseList(rest) + end +end + +local function compareOptions(a,b) + if not a then + return true + end + if not b then + return false + end + local A, B = a.order or 100, b.order or 100 + if A == B then + local NameA = a.guiName or a.name or "" + local NameB = b.guiName or b.name or "" + return string.upper(NameA) < string.upper(NameB) + end + if A < 0 then + if B > 0 then + return false + end + else + if B < 0 then + return true + end + end + return A < B +end + +function Waterfall:Register(id,...) + local settings = new(unpack(arg)) + + if settings.aceOptions then + assert(type(settings.aceOptions) == "table","\"aceOptions\" must be a table") + else + assert(type(settings.tree) == "table","\"tree\" must be a table") + assert(type(settings.children) == "function","\"children\" must be a function") + end + if not settings.title then + settings.title = id + end + + if settings.treeType == "SECTIONS" then + settings.hideTreeRoot = true + settings.treeLevels = 3 + end + + if self.registry[id] then + self:UnRegister(id) + end + self.registry[id] = settings +end + +function Waterfall:UnRegister(id) + self.registry[id] = nil +end + +function Waterfall:IsRegistered(id) + return not not self.registry[id] +end + +local function releaseTree(tree) + for i, v in ipairs(tree) do + tree[i] = nil + releaseTree(v) + releaseObj(v,"table") + end + for k, v in pairs(tree) do + tree[k] = nil + end + table_setn(tree,0) +end + +local function checkNegatable(attr,handler,param) + local ret + if type(attr) == "function" then + ret = attr(param) + elseif type(attr) == "string" and handler then + local neg = string.match(attr,"^~(.+)$") + if neg then + attr = neg + end + ret = handler[attr](handler,param) + if neg then + ret = not ret + end + else + ret = attr + end + return ret +end + +local mysort = function(a, b) + + local alpha, bravo = a, b + local alpha_order = alpha and alpha.order or 100 + local bravo_order = bravo and bravo.order or 100 + local alpha_name = alpha and (alpha.guiName or alpha.name) + local bravo_name = bravo and (bravo.guiName or bravo.name) + if alpha_order == bravo_order then + if not alpha_name then + return bravo_name + elseif not bravo_name then + return false + else + return string.upper(string.gsub(string.gsub(alpha_name,"|c%x%x%x%x%x%x%x%x", ""), "|r", "")) < string.upper(string.gsub(string.gsub(bravo_name,"|c%x%x%x%x%x%x%x%x", ""),"|r", "")) + end + else + if alpha_order < 0 then + if bravo_order > 0 then + return false + end + else + if bravo_order < 0 then + return true + end + end + return alpha_order < bravo_order + end +end + +local function buildAceOptionsTree(tree, status, options, numLevels, path, rootHandler) + local settings = Waterfall.registry[currentframe.id] + local handler = options.handler or rootHandler + local bargs = options.args + + assert(type(bargs) == "table",string.format("args must be a table: Current Path:%s",(path or "."))) + local root + + if path then + tree.id = path + tree.text = options.guiName or options.name or (options.handler and options.handler.name) or path + tree.name = tree.text + tree.tooltipTitle = tree.text + tree.tooltipText = options.desc + tree.order = options.order + tree.disabled = checkNegatable(options.disabled, handler, options.passValue) + tree.handler = handler + tree.icon = options.icon + tree.hidden = checkNegatable(options.wfHidden or options.guiHidden or options.hidden,handler, options.passValue) + local hasOptions + for k, item in pairs(bargs) do + if item.type ~= "group" and item.type ~= "heading" then + if not checkNegatable(item.wfHidden or item.guiHidden or item.hidden,handler, item.passValue) then + hasOptions = true + end + end + end + tree.hasOptions = hasOptions + else + releaseTree(tree) + local hasOptions + for k, item in pairs(bargs) do + if item.type ~= "group" and item.type ~= "heading" then + hasOptions = true + end + end + if hasOptions then + root = getObj("table") + root.text = options.guiName or options.name or (options.handler and options.handler.name) or path + root.name = tree.text + root.tooltipTitle = tree.text + root.tooltipText = options.desc + root.order = options.order + root.disabled = checkNegatable(options.disabled, handler, options.passValue) + root.handler = handler + root.icon = options.icon + root.hidden = checkNegatable(options.wfHidden or options.guiHidden or options.hidden,handler, options.passValue) + root.id = "." + root.hasOptions = true + if status['.'] == nil then + status['.'] = true + end + tinsert(tree,root) + if not settings.hideTreeRoot then + tree = root + end + end + settings.treeRootShown = hasOptions and not settings.hideTreeRoot + end + + if not numLevels or numLevels > 1 then + for k, item in pairs(bargs) do + if item.type == "group" then + local child = getObj("table") + local nextPath + if path then + nextPath = path.."."..k + else + nextPath = k + end + if numLevels then + buildAceOptionsTree(child, status, item,numLevels - 1,nextPath, handler) + else + buildAceOptionsTree(child, status, item,nil,nextPath, handler) + end + tinsert(tree,child) + end + end + end + + + table.sort(tree, mysort) + +-- table.sort(tree,compareOptions) +end + +local function checkAceOptionsPath(path, options, frame) + local t = options + local validpath + for k in string.gfind(path,"[^\.]+") do + if tonumber(k) and t.args[tonumber(k)] then + k = tonumber(k) + end + if t.args and t.args[k] then + t = t.args[k] + if validpath then + validpath = validpath..'.'..k + else + validpath = k + end + else + if frame then + frame:SetSelected(validpath, true) + end + break + end + end + return validpath or '.' +end + +local function feedFromOptionsTree(paneid, options) + if not paneid then return end + local settings = Waterfall.registry[currentframe.id] + paneid = checkAceOptionsPath(paneid, options, settings.frame) + local curlevel = 1 + local levels = 1 + for x in string.gfind(paneid,"[^%.]+") do + curlevel = curlevel + 1 + end + if settings.treeLevels == curlevel then + levels = nil + end + if paneid == "." then + Waterfall:FeedAceOptionsTable(options, nil, levels) + return options.name or (options.handler and options.handler.name) or OPTIONS + else + local crumbs + if settings.treeRootShown then + crumbs = options.name or (options.handler and options.handler.name) or OPTIONS + end + local t = options + Waterfall:FeedAceOptionsTable(options,paneid,levels) + for k in string.gfind(paneid,"[^\.]+") do + if tonumber(k) and t.args[tonumber(k)] then + k = tonumber(k) + end + if t.args and t.args[k] then + t = t.args[k] + if crumbs then + crumbs = crumbs..ARROW..(t.guiName or t.name or "") + else + crumbs = (t.guiName or t.name or "") + end + else + Waterfall:error("Invalid path \"%s\" for Aceoptions table",paneid) + end + end + return crumbs + end +end + +local old_CloseSpecialWindows + +function Waterfall:Open(id, pane) + if not old_CloseSpecialWindows then + old_CloseSpecialWindows = CloseSpecialWindows + CloseSpecialWindows = function() + local found = old_CloseSpecialWindows() + return self:CloseAll() or found + end + end + local info = self.registry[id] + + if not info then + error("You cannot open a waterfall without registering it first") + end + --already open + if info.frame then + if pane then + info.frame:SetSelected(pane) + end + return + end + local frame = getObj(WaterfallFrame) --WaterfallFrame:new() + frame:SetID(id) + frame.lib = self + info.frame = frame + if info.title then + frame:SetTitle(info.title) + end + local r,g,b = info.colorR or 1, info.colorG or 0.6, info.colorB or 0 + frame:SetColor(r,g,b) + + if info.aceOptions then + + if not info.tree then + info.tree = {} + end + if not info.treestatus then + info.treestatus = {} + end + currentframe = frame + frame:SetTree(info.tree, info.treestatus, buildAceOptionsTree, info.aceOptions, info.treeLevels) + currentframe = nil + frame:SetChildren(feedFromOptionsTree, info.aceOptions) + if pane then + frame.treeview:Collapse() + end + frame:SetSelected(pane or info.defaultPane or ".") + frame.treeview:SetType(info.treeType) + else + if not info.treestatus then + info.treestatus = {} + end + frame:SetTree(info.tree, info.treestatus) + frame:SetChildren(info.children) + if pane or info.defaultPane then + frame:SetSelected(pane or info.defaultPane) + end + frame.treeview:SetType(info.treeType) + end + + if info.height then + frame:SetHeight(info.height) + end + if info.width then + frame:SetWidth(info.width) + end + frame:Refresh() + frame:Show() + +end + +function Waterfall:IsOpen(id) + return not not self.registry[id].frame +end + +function Waterfall:Refresh(id) + local info = self.registry[id] + if not info then + error("You can't refresh a waterfall without registering it first") + end + if info.frame then + info.frame:Refresh() + end +end + +function Waterfall:SetSize(id, width, height) + local info = self.registry[id] + if not info then + error("You refresh open a waterfall without registering it first") + end + if info.frame then + info.frame:SetWidth(width) + info.frame:SetHeight(height) + end +end + +local function setCommonAttributes(control,info) + control.width = info.width + control.height = info.height + control.noNewLine = info.noNewLine + control.causesRefresh = info.causesRefresh + control.fullRefresh = info.fullRefresh + control.treeRefresh = info.treeRefresh + control.tooltipTitle = info.tooltipTitle + control.tooltipText = info.tooltipText + control:SetText(info.text or "") +end + +function Waterfall:AddControl(...) + currentframe.controlcount = currentframe.controlcount + 1 + if not currentframe then + error("AddControl must be called from within a children function") + end + local control + local info = self.registry[currentframe.id] + local limit = info.controlLimit or CONTROL_LIMIT + if currentframe.controlcount > limit then + if currentframe.controlcount == limit + 1 then + control = getObj(WaterfallLabel) + control.r = 1 + control.g = 0 + control.b = 0 + control:SetText(("More than %s Controls in current pane....."):format(limit)) + control:Refresh() + currentframe:AddControl(control) + end + return + end + + local info = tmp2(unpack(arg)) + + if info.type == "label" then + control = getObj(WaterfallLabel) + setCommonAttributes(control,info) + control.justifyH = info.justifyH + control.r = info.r + control.g = info.g + control.b = info.b + control:Refresh() + elseif info.type == "dragLink" then + control = getObj(WaterfallDragLink) + setCommonAttributes(control,info) + control:SetFunc("set",info.setFunc,getArgs(info,"setArg",1)) + control:SetFunc("get",info.getFunc,getArgs(info,"getArg",1)) + if type(info.disabled) == "function" then + control:SetFunc("disabled",info.disabled,getArgs(info,"disabledArg",1)) + control.inverseDisabled = info.inverseDisabled + else + control.disabled = info.disabled + end + + control:Refresh() + elseif info.type == "linklabel" then + control = getObj(WaterfallLinklabel) + setCommonAttributes(control,info) + control.justifyH = info.justifyH + control.r = info.r + control.g = info.g + control.b = info.b + + control:SetFunc("link",info.linkFunc,getArgs(info,"linkArg",1)) + if type(info.disabled) == "function" then + control:SetFunc("disabled",info.disabled,getArgs(info,"disabledArg",1)) + control.inverseDisabled = info.inverseDisabled + else + control.disabled = info.disabled + end + control.confirm = info.confirm + control:Refresh() + + elseif info.type == "button" then + control = getObj(WaterfallButton) + + setCommonAttributes(control,info) + control:SetFunc("exec",info.execFunc,getArgs(info,"execArg",1)) + if type(info.disabled) == "function" then + control:SetFunc("disabled",info.disabled,getArgs(info,"disabledArg",1)) + control.inverseDisabled = info.inverseDisabled + else + control.disabled = info.disabled + end + control.confirm = info.confirm + control.link = info.link + + control:Refresh() + + elseif info.type == "heading" then + control = getObj(WaterfallHeading) + control.justifyH = info.justifyH + setCommonAttributes(control,info) + control:Refresh() + elseif info.type == "textbox" then + control = getObj(WaterfallTextBox) + setCommonAttributes(control,info) + control:SetFunc("set",info.setFunc,getArgs(info,"setArg",1)) + control:SetFunc("get",info.getFunc,getArgs(info,"getArg",1)) + control:SetFunc("changed",info.changedFunc,getArgs(info,"changedArg",1)) + if type(info.disabled) == "function" then + control:SetFunc("disabled",info.disabled,getArgs(info,"disabledArg",1)) + control.inverseDisabled = info.inverseDisabled + else + control.disabled = info.disabled + end + control.toNumber = info.toNumber + control.parseList = info.parseList + control.setOnTextChanged = info.setOnTextChanged + control:SetFunc("validate",info.validateFunc,getArgs(info,"validateArg",1)) + control.validateModifies = info.validateModifies + control:Refresh() + + elseif info.type == "dropdown" then + control = getObj(WaterfallDropdown) + setCommonAttributes(control,info) + control:SetFunc("set",info.setFunc,getArgs(info,"setArg",1)) + control:SetFunc("get",info.getFunc,getArgs(info,"getArg",1)) + if type(info.disabled) == "function" then + control:SetFunc("disabled",info.disabled,getArgs(info,"disabledArg",1)) + control.inverseDisabled = info.inverseDisabled + else + control.disabled = info.disabled + end + control.parseList = info.parseList + control.list = info.list + control.columns = info.columns + control.strict = info.strict + + control:Refresh() + + elseif info.type == "checkbox" then + control = getObj(WaterfallCheckBox) + setCommonAttributes(control,info) + control:SetChecked(info.checked) + control.isRadio = info.isRadio + control:SetFunc("set",info.setFunc,getArgs(info,"setArg",1)) + control:SetFunc("get",info.getFunc,getArgs(info,"getArg",1)) + if type(info.disabled) == "function" then + control:SetFunc("disabled",info.disabled,getArgs(info,"disabledArg",1)) + control.inverseDisabled = info.inverseDisabled + else + control.disabled = info.disabled + end + + control:Refresh() + elseif info.type == "slider" then + control = getObj(WaterfallSlider) + setCommonAttributes(control,info) + control:SetFunc("set",info.setFunc,getArgs(info,"setArg",1)) + control:SetFunc("get",info.getFunc,getArgs(info,"getArg",1)) + control.min = info.min + control.max = info.max + control.step = info.step + control.bigStep = info.bigStep or info.step + control.finalSetOnly = info.finalSetOnly + if type(info.disabled) == "function" then + control:SetFunc("disabled",info.disabled,getArgs(info,"disabledArg",1)) + control.inverseDisabled = info.inverseDisabled + else + control.disabled = info.disabled + end + control:Refresh() + elseif info.type == "color" then + control = getObj(WaterfallColorSwatch) + setCommonAttributes(control,info) + control:SetFunc("set",info.setFunc,getArgs(info,"setArg",1)) + control:SetFunc("get",info.getFunc,getArgs(info,"getArg",1)) + if type(info.disabled) == "function" then + control:SetFunc("disabled",info.disabled,getArgs(info,"disabledArg",1)) + control.inverseDisabled = info.inverseDisabled + else + control.disabled = info.disabled + end + control.hasOpacity = info.hasOpacity + control.r = info.r + control.g = info.g + control.b = info.b + control.opacity = info.opacity + control.hasOpacity = info.hasOpacity + control:Refresh() + elseif info.type == "keybind" then + control = getObj(WaterfallKeybinding) + control:SetText("Binding") + setCommonAttributes(control,info) + control:SetFunc("set",info.setFunc,getArgs(info,"setArg",1)) + control:SetFunc("get",info.getFunc,getArgs(info,"getArg",1)) + if type(info.disabled) == "function" then + control:SetFunc("disabled",info.disabled,getArgs(info,"disabledArg",1)) + control.inverseDisabled = info.inverseDisabled + else + control.disabled = info.disabled + end + control.keybindingOnly = info.keybindingOnly + control.keybindingExcept = info.keybindingExcept + control:Refresh() + else + + error("Invalid or missing type") + end + currentframe:AddControl(control) +end + + +local function addDisabled(t,disabled,handler,param) + if disabled then + if type(disabled) == "function" or type(disabled) == "boolean" then + tinsert(t,"disabled") + tinsert(t,disabled) + if param then + tinsert(t,"disabledArg1") + tinsert(t,param) + end + elseif type(disabled) == "string" then + local neg = string.match(disabled,"^~(.+)$") + if neg then + disabled = neg + end + if handler and handler[disabled] then + tinsert(t,"disabled") + tinsert(t,handler[disabled]) + tinsert(t,"disabledArg1") + tinsert(t,handler) + if param then + tinsert(t,"disabledArg2") + tinsert(t,param) + end + end + if neg then + tinsert(t,"inverseDisabled") + tinsert(t,true) + end + end + end +end + +local function appendArgs(t,name,start,...) + for k = 1 , table.getn(arg) do + tinsert(t,name.."Arg"..(start + k - 1)) + tinsert(t,arg[k]) + end +end + +local function addFunction(t,name,func,handler,...) + if type(func) == "function" then + tinsert(t,name.."Func") + tinsert(t,func) + appendArgs(t,name,1,unpack(arg)) + elseif type(func) == "string" and handler and handler[func] then + tinsert(t,name.."Func") + tinsert(t,handler[func]) + tinsert(t,name.."Arg1") + tinsert(t,handler) + appendArgs(t,name,2,unpack(arg)) + end +end + +local function validateRange(min,max,step,value) + value = tonumber(value) + if step > 0 then + value = math.floor((value - min) / step + 0.5) * step + min + end + if value >= min and value <= max then + return value + else + return nil + end +end + +--[[ + Feeds an ace options table into the right pane, must be called within a "children" function + root is the root of your options table, this is important if you use any methods for resolving the handler + path is the path into the table seperated by "." e.g. "Items.Compress" to feed the table given by root.args.Items.args.Compress + maxLevels is how many sub groups to feed as well, 1 is only the current level 2 will feed sub groups etc. nil assumes no limit +]] +function Waterfall:FeedAceOptionsTable(root,path,maxLevels) + local feedtmp = getObj("table") + local feedkeys = getObj("table") + local tmpargs = getObj("table") + local t + local handler = root.handler + local groupDisabled = checkNegatable(root.disabled,root.handler,root.passValue) + local passSet + local passGet + if root.pass then + passSet = root.set + passGet = root.get + else + passSet = nil + passGet = nil + end + if path then + t = root + for k in string.gfind(path,"[^\.]+") do + if tonumber(k) and t.args[tonumber(k)] then + k = tonumber(k) + end + if t.args and t.args[k] then + t = t.args[k] + handler = t.handler or handler + groupDisabled = checkNegatable(t.disabled,handler,t.passValue) or groupDisabled + if t.pass then + passSet = t.set + passGet = t.get + else + passSet = nil + passGet = nil + end + else + self:error("Invalid path \"%s\" for Aceoptions table",path) + end + end + else + t = root + end + if t.type ~= "group" then + self:error("You must feed an ace options group") + end + for k, v in pairs(t.args) do + tinsert(feedtmp, v) + feedkeys[v] = k + end + + table.sort(feedtmp, mysort) + local lastheader + for i, v in ipairs(feedtmp) do + --only add headers if they arent immediately followed by a group + if lastheader then + if v.type ~= "group" then + --self:AddControl("type","label","text","","width",80) + --self:AddControl("type","heading","text",lastheader,"width",DEFAULT_CONTROL_WIDTH,"justifyH","CENTER","noNewLine",true) + self:AddControl("type","heading","text",lastheader,"width",DEFAULT_CONTROL_WIDTH,"justifyH","CENTER") + end + lastheader = nil + end + local hidden + if v.guiHidden ~= nil then + hidden = v.guiHidden + elseif v.wfHidden ~= nil then + hidden = v.wfHidden + else + hidden = v.hidden + end + hidden = checkNegatable(hidden,v.handler or handler,v.passValue) + + local name = v.guiName or v.name + if name and ( checkNegatable(v.disabled,v.handler or handler,v.passValue) or groupDisabled ) then + name = "|cFF808080"..name + end + if not hidden then + if v.type == "toggle" then + --self:AddControl("type","label","text","","width",80) + tinsert(tmpargs,"type") + tinsert(tmpargs,"checkbox") + --tinsert(tmpargs,"noNewLine") + --tinsert(tmpargs,true) + tinsert(tmpargs,"text") + tinsert(tmpargs,name) + tinsert(tmpargs,"tooltipTitle") + tinsert(tmpargs,name) + tinsert(tmpargs,"tooltipText") + tinsert(tmpargs,v.desc or false) + tinsert(tmpargs,"isRadio") + tinsert(tmpargs,not not v.isRadio) + if passGet and v.get == nil then + addFunction(tmpargs,"get",passGet,v.handler or handler,v.passValue or feedkeys[v]) + else + addFunction(tmpargs,"get",v.get,v.handler or handler,v.passValue) + end + if passSet and v.set == nil then + addFunction(tmpargs,"set",passSet,v.handler or handler,v.passValue or feedkeys[v]) + else + addFunction(tmpargs,"set",v.set,v.handler or handler,v.passValue) + end + + addDisabled(tmpargs,groupDisabled or v.disabled,v.handler or handler,v.passValue) + tinsert(tmpargs,"fullRefresh") + tinsert(tmpargs,true) + + self:AddControl(unpack(tmpargs)) + elseif v.type == "dragLink" then + tinsert(tmpargs,"type") + tinsert(tmpargs,"dragLink") + tinsert(tmpargs,"text") + tinsert(tmpargs,name) + tinsert(tmpargs,"tooltipText") + tinsert(tmpargs,v.desc or false) + tinsert(tmpargs,"tooltipTitle") + local tooltipTitle = "" .. tostring(v.itemId) + tinsert(tmpargs,tooltipTitle) + tinsert(tmpargs,"linkInfo") + tinsert(tmpargs,v.linkInfo or {}) + tinsert(tmpargs,"icon") + tinsert(tmpargs,v.icon or "") + tinsert(tmpargs,"iconWidth") + tinsert(tmpargs,v.iconWidth or WaterfallDragLink.defaultIconSize) + tinsert(tmpargs,"iconHeight") + tinsert(tmpargs,v.iconHeight or WaterfallDragLink.defaultIconSize) + if passGet and v.get == nil then + addFunction(tmpargs,"get",passGet,v.handler or handler,v.passValue or feedkeys[v]) + else + addFunction(tmpargs,"get",v.get,v.handler or handler,v.passValue) + end + if passSet and v.set == nil then + addFunction(tmpargs,"set",passSet,v.handler or handler,v.passValue or feedkeys[v]) + else + addFunction(tmpargs,"set",v.set,v.handler,v.passValue) + end + + addDisabled(tmpargs,groupDisabled or v.disabled,v.handler or handler,v.passValue) + + self:AddControl(unpack(tmpargs)) + elseif v.type == "group" then + if not maxLevels or maxLevels > 1 then + --self:AddControl("type","label","text","","width",80) + if checkNegatable(v.disabled,v.handler or handler,v.passValue) or groupDisabled then + name = "|cFF808080"..name + end + --self:AddControl("type","heading","text",name,"width",DEFAULT_CONTROL_WIDTH,"justifyH","CENTER","noNewLine",true) + self:AddControl("type","heading","text",name,"width",DEFAULT_CONTROL_WIDTH,"justifyH","CENTER") + local nextPath + local nextMaxLevels + if path then + nextPath = path.."."..feedkeys[v] + else + nextPath = feedkeys[v] + end + if maxLevels then + nextMaxLevels = maxLevels - 1 + end + self:FeedAceOptionsTable(root,nextPath,nextMaxLevels) + self:AddControl("type","heading") + end + elseif v.type == "text" then + if type(v.validate) == "table" then + if v.multiToggle then + self:AddControl("type","heading","text",name) + for key, value in pairs(v.validate) do + if type(key) == "number" then + key = value + end + self:AddControl("type","label","text","","width",12) + tinsert(tmpargs,"type") + tinsert(tmpargs,"checkbox") + tinsert(tmpargs,"text") + tinsert(tmpargs,value) + tinsert(tmpargs,"noNewLine") + tinsert(tmpargs,true) + tinsert(tmpargs,"tooltipTitle") + tinsert(tmpargs,name) + tinsert(tmpargs,"tooltipText") + tinsert(tmpargs,value) + if v.passValue then + if passGet and v.get == nil then + addFunction(tmpargs,"get",passGet,v.handler or handler,v.passValue or feedkeys[v],key) + else + addFunction(tmpargs,"get",v.get,v.handler or handler,v.passValue,key) + end + if passSet and v.set == nil then + addFunction(tmpargs,"set",passSet,v.handler or handler,v.passValue or feedkeys[v],key) + else + addFunction(tmpargs,"set",v.set,v.handler or handler,v.passValue,key) + end + else + if passGet and v.get == nil then + addFunction(tmpargs,"get",passGet,v.handler or handler,feedkeys[v],key) + else + addFunction(tmpargs,"get",v.get,v.handler or handler,key) + end + if passSet and v.set == nil then + addFunction(tmpargs,"set",passSet,v.handler or handler,feedkeys[v],key) + else + addFunction(tmpargs,"set",v.set,v.handler or handler,key) + end + end + + addDisabled(tmpargs,groupDisabled or v.disabled,v.handler or handler,v.passValue,key) + tinsert(tmpargs,"fullRefresh") + tinsert(tmpargs,true) + self:AddControl(unpack(tmpargs)) + for i = 0,table.getn(tmpargs) do + tmpargs[i] = nil + end + end + else + --self:AddControl("type","label","text",name,"width",80,"justifyH","RIGHT") + self:AddControl("type","label","text",name) + tinsert(tmpargs,"type") + tinsert(tmpargs,"dropdown") + tinsert(tmpargs,"strict") + tinsert(tmpargs,true) + --tinsert(tmpargs,"noNewLine") + --tinsert(tmpargs,true) + tinsert(tmpargs,"tooltipTitle") + tinsert(tmpargs,name) + tinsert(tmpargs,"tooltipText") + tinsert(tmpargs,v.desc or false) + if passGet and v.get == nil then + addFunction(tmpargs,"get",passGet,v.handler or handler,v.passValue or feedkeys[v]) + else + addFunction(tmpargs,"get",v.get,v.handler or handler,v.passValue) + end + if passSet and v.set == nil then + addFunction(tmpargs,"set",passSet,v.handler or handler,v.passValue or feedkeys[v]) + else + addFunction(tmpargs,"set",v.set,v.handler or handler,v.passValue) + end + + addDisabled(tmpargs,groupDisabled or v.disabled,v.handler or handler,v.passValue) + + tinsert(tmpargs,"list") + tinsert(tmpargs,v.validate) + tinsert(tmpargs,"columns") + tinsert(tmpargs,v.columns) + tinsert(tmpargs,"parseList") + tinsert(tmpargs, not not v.input) + tinsert(tmpargs,"fullRefresh") + tinsert(tmpargs,true) + self:AddControl(unpack(tmpargs)) + end + elseif v.validate == "keybinding" then + --self:AddControl("type","label","text",name,"width",80,"justifyH","RIGHT") + self:AddControl("type","label","text",name) + tinsert(tmpargs,"type") + tinsert(tmpargs,"keybind") + --tinsert(tmpargs,"noNewLine") + --tinsert(tmpargs,true) + if passGet and v.get == nil then + addFunction(tmpargs,"get",passGet,v.handler or handler,v.passValue or feedkeys[v]) + else + addFunction(tmpargs,"get",v.get,v.handler or handler,v.passValue) + end + if passSet and v.set == nil then + addFunction(tmpargs,"set",passSet,v.handler or handler,v.passValue or feedkeys[v]) + else + addFunction(tmpargs,"set",v.set,v.handler or handler,v.passValue) + end + tinsert(tmpargs,"keybindingOnly") + tinsert(tmpargs,v.keybindingOnly or false) + tinsert(tmpargs,"keybindingExcept") + tinsert(tmpargs,v.keybindingExcept or false) + tinsert(tmpargs,"tooltipTitle") + tinsert(tmpargs,name) + tinsert(tmpargs,"tooltipText") + tinsert(tmpargs,v.desc or false) + tinsert(tmpargs,"fullRefresh") + tinsert(tmpargs,true) + + self:AddControl(unpack(tmpargs)) + else + --self:AddControl("type","label","text",name,"width",80,"justifyH","RIGHT") + self:AddControl("type","label","text",name) + tinsert(tmpargs,"type") + tinsert(tmpargs,"textbox") + --tinsert(tmpargs,"noNewLine") + --tinsert(tmpargs,true) + tinsert(tmpargs,"tooltipTitle") + tinsert(tmpargs,name) + tinsert(tmpargs,"tooltipText") + tinsert(tmpargs,v.desc or false) + if passGet and v.get == nil then + addFunction(tmpargs,"get",passGet,v.handler or handler,v.passValue or feedkeys[v]) + else + addFunction(tmpargs,"get",v.get,v.handler or handler,v.passValue) + end + if passSet and v.set == nil then + addFunction(tmpargs,"set",passSet,v.handler or handler,v.passValue or feedkeys[v]) + else + addFunction(tmpargs,"set",v.set,v.handler or handler,v.passValue) + end + + addDisabled(tmpargs,groupDisabled or v.disabled,v.handler or handler,v.passValue) + tinsert(tmpargs,"parseList") + tinsert(tmpargs, not not v.input) + tinsert(tmpargs,"fullRefresh") + tinsert(tmpargs,true) + + addFunction(tmpargs,"validate",v.validate,v.handler or handler) + + self:AddControl(unpack(tmpargs)) + end + elseif v.type == "execute" then + --self:AddControl("type","label","text","","width",80) + tinsert(tmpargs,"type") + tinsert(tmpargs,"button") + --tinsert(tmpargs,"noNewLine") + --tinsert(tmpargs,true) + tinsert(tmpargs,"text") + tinsert(tmpargs,name) + tinsert(tmpargs,"confirm") + tinsert(tmpargs, v.confirm or false) + tinsert(tmpargs,"tooltipTitle") + tinsert(tmpargs,name) + tinsert(tmpargs,"tooltipText") + tinsert(tmpargs,v.desc or false) + tinsert(tmpargs, "link") + tinsert(tmpargs,v.link or false) + addFunction(tmpargs,"exec",v.func,v.handler or handler,v.passValue) + + addDisabled(tmpargs,groupDisabled or v.disabled,v.handler or handler,v.passValue) + + tinsert(tmpargs,"fullRefresh") + tinsert(tmpargs,true) + self:AddControl(unpack(tmpargs)) + elseif v.type == "header" then + if name and ( checkNegatable(v.disabled,v.handler or handler,v.passValue) or groupDisabled ) then + lastheader = "|cFF808080"..name + else + lastheader = name + end + elseif v.type == "range" then + --self:AddControl("type","label","text",name,"width",80,"justifyH","RIGHT") + self:AddControl("type","label","text",name) + tinsert(tmpargs,"type") + tinsert(tmpargs,"slider") + --tinsert(tmpargs,"noNewLine") + --tinsert(tmpargs,true) + local min,max,step + min = v.min or 0 + max = v.max or 1 + step = v.step or 0 + tinsert(tmpargs,"min") + tinsert(tmpargs,v.min or 0) + tinsert(tmpargs,"max") + tinsert(tmpargs,v.max or 1) + tinsert(tmpargs,"step") + tinsert(tmpargs,v.step or 0) + tinsert(tmpargs,"bigStep") + tinsert(tmpargs,v.bigStep or v.step or 0) + tinsert(tmpargs,"finalSetOnly") + tinsert(tmpargs,v.finalSetOnly or false) + --if no step is given or there are more than 20 steps possible + --local complex = step == 0 or ((max - min) / step) > 20 + local complex = true + tinsert(tmpargs,"text") + tinsert(tmpargs,"") + tinsert(tmpargs,"tooltipTitle") + tinsert(tmpargs,name) + tinsert(tmpargs,"tooltipText") + tinsert(tmpargs,v.desc or false) + if passGet and v.get == nil then + addFunction(tmpargs,"get",passGet,v.handler or handler,v.passValue or feedkeys[v]) + else + addFunction(tmpargs,"get",v.get,v.handler or handler,v.passValue) + end + if passSet and v.set == nil then + addFunction(tmpargs,"set",passSet,v.handler or handler,v.passValue or feedkeys[v]) + else + addFunction(tmpargs,"set",v.set,v.handler or handler,v.passValue) + end + addDisabled(tmpargs,groupDisabled or v.disabled,v.handler or handler,v.passValue) + + tinsert(tmpargs,"fullRefresh") + tinsert(tmpargs,true) + self:AddControl(unpack(tmpargs)) + if complex then + tinsert(tmpargs,"type") + tinsert(tmpargs,"textbox") + tinsert(tmpargs,"noNewLine") + tinsert(tmpargs,true) + tinsert(tmpargs,"toNumber") + tinsert(tmpargs,true) + tinsert(tmpargs,"validateModifies") + tinsert(tmpargs,true) + tinsert(tmpargs,"width") + tinsert(tmpargs,60) + tinsert(tmpargs,"tooltipTitle") + tinsert(tmpargs,name) + tinsert(tmpargs,"tooltipText") + tinsert(tmpargs,v.desc or false) + if passGet and v.get == nil then + addFunction(tmpargs,"get",passGet,v.handler or handler,v.passValue or feedkeys[v]) + else + addFunction(tmpargs,"get",v.get,v.handler or handler,v.passValue) + end + if passSet and v.set == nil then + addFunction(tmpargs,"set",passSet,v.handler or handler,v.passValue or feedkeys[v]) + else + addFunction(tmpargs,"set",v.set,v.handler or handler,v.passValue) + end + + addFunction(tmpargs,"validate",validateRange,v.handler or handler,min,max,step) + addDisabled(tmpargs,groupDisabled or v.disabled,v.handler or handler,v.passValue) + + tinsert(tmpargs,"fullRefresh") + tinsert(tmpargs,true) + self:AddControl(unpack(tmpargs)) + end + elseif v.type == "color" then + --self:AddControl("type","label","text","","width",80) + tinsert(tmpargs,"type") + tinsert(tmpargs,"color") + --tinsert(tmpargs,"noNewLine") + --tinsert(tmpargs,true) + tinsert(tmpargs,"text") + tinsert(tmpargs,name) + if passGet and v.get == nil then + addFunction(tmpargs,"get",passGet,v.handler or handler,v.passValue or feedkeys[v]) + else + addFunction(tmpargs,"get",v.get,v.handler or handler,v.passValue) + end + if passSet and v.set == nil then + addFunction(tmpargs,"set",passSet,v.handler or handler,v.passValue or feedkeys[v]) + else + addFunction(tmpargs,"set",v.set,v.handler or handler,v.passValue) + end + addDisabled(tmpargs,groupDisabled or v.disabled,v.handler or handler,v.passValue) + tinsert(tmpargs,"tooltipTitle") + tinsert(tmpargs,name) + tinsert(tmpargs,"tooltipText") + tinsert(tmpargs,v.desc or false) + tinsert(tmpargs,"hasOpacity") + tinsert(tmpargs, not not v.hasAlpha) + self:AddControl(unpack(tmpargs)) + end + + for i = 0,table.getn(tmpargs) do + tmpargs[i] = nil + end + table_setn(tmpargs, 0) + end + end + for i = 0,table.getn(feedtmp) do + feedtmp[i] = nil + end + table_setn(feedtmp, 0) + for k in pairs(feedkeys) do + feedkeys[k] = nil + end + table_setn(feedkeys, 0) + + releaseObj(feedtmp,"table") + releaseObj(feedkeys,"table") + releaseObj(tmpargs,"table") + +end + +function Waterfall:Close(id) + local info = self.registry[id] + if not info then + error("You cannot close a waterfall without registering it first") + end + local frame = info.frame + if frame then + local OnClose = info.OnClose + if type(OnClose) == "function" then + OnClose(id) + end + releaseObj(frame) + end + info.frame = nil +end + +function Waterfall:CloseAll() + local closed + for id, v in pairs(self.registry) do + if v.frame then + self:Close(id) + closed = true + end + end + return closed +end + +----------------- +-- Gui Classes -- +----------------- +local FrameBackdrop = { + bgFile = "Interface\\Tooltips\\UI-Tooltip-Background", + edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border", + tile = true, tileSize = 16, edgeSize = 16, + insets = { left = 3, right = 3, top = 30, bottom = 3 } +} + +local PaneBackdrop = { + bgFile = "Interface\\Tooltips\\UI-Tooltip-Background", + edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border", + tile = true, tileSize = 16, edgeSize = 16, + insets = { left = 3, right = 3, top = 23, bottom = 3 } +} + +local ControlBackdrop = { + bgFile = "Interface\\Tooltips\\UI-Tooltip-Background", + edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border", + tile = true, tileSize = 16, edgeSize = 16, + insets = { left = 3, right = 3, top = 3, bottom = 3 } +} + + + +-- virtual class to contain frame functions +local FramePassthrough = AceOO.Class() +FramePassthrough.virtual = true + +function FramePassthrough.prototype:SetPoint(point,relativeTo,relativePoint,xOffset,yOffset) + self.frame:SetPoint(point,(relativeTo and relativeTo.frame) or relativeTo,relativePoint,xOffset,yOffset) +end +function FramePassthrough.prototype:SetAllPoints(frame) + self.frame:SetAllPoints(frame.frame or frame) +end +function FramePassthrough.prototype:SetWidth(...) + self.frame:SetWidth(unpack(arg)) +end +function FramePassthrough.prototype:SetHeight(...) + self.frame:SetHeight(unpack(arg)) +end +function FramePassthrough.prototype:SetMinResize(...) + self.frame:SetMinResize(unpack(arg)) +end +function FramePassthrough.prototype:SetMaxResize(...) + self.frame:SetMaxResize(unpack(arg)) +end +function FramePassthrough.prototype:ClearAllPoints() + self.frame:ClearAllPoints() +end +function FramePassthrough.prototype:Show() + self.frame:Show() +end +function FramePassthrough.prototype:Hide() + self.frame:Hide() +end +function FramePassthrough.prototype:GetHeight() + return self.frame:GetHeight() +end +function FramePassthrough.prototype:GetWidth() + return self.frame:GetWidth() +end + +-- Base Class for Gui Controls +local WaterfallControl = AceOO.Class(FramePassthrough) +WaterfallControl.virtual = true + +function WaterfallControl.prototype:init(frametype,inherits) + WaterfallControl.super.prototype.init(self) + local frame = CreateFrame(frametype or "Frame",nil,nil,inherits) + self.frame = frame + frame.obj = self + frame:SetFrameLevel(10) + frame:SetFrameStrata("DIALOG") +end + +function WaterfallControl.prototype:SetParent(parent) + self.parent = parent + self.frame:SetParent(parent.scrollchild) +end + +function WaterfallControl.prototype:Refresh() + +end + +--basic cleanup function, usually overridden +function WaterfallControl.prototype:CleanUp() + self.frame:ClearAllPoints() + self.frame:Hide() + self.width = nil + self.height = nil + self.noNewLine = nil + self.causesRefresh = nil + self.fullRefresh = nil + self.treeRefresh = nil + self.disabled = nil + self:SetFunc("disabled",nil) +end + +function WaterfallControl.prototype:SetFunc(funcType,func,...) + self[funcType.."Func"] = func + local i = 1 + while self[funcType.."Arg"..i] do + self[funcType.."Arg"..i] = nil + i = i + 1 + end + for i = 1 , table.getn(arg) do + self[funcType.."Arg"..i] = arg[i] + end +end + +local function Control_OnEnter() + showGameTooltip(this.obj) +end + +local function Control_OnLeave() + GameTooltip:Hide() +end + +--the main waterfall frame +WaterfallFrame = AceOO.Class(FramePassthrough) +--the right side pane with gui components in it +WaterfallPane = AceOO.Class(FramePassthrough) + +--gui components to be placed on the pane +WaterfallLabel = AceOO.Class(WaterfallControl) +WaterfallLinklabel = AceOO.Class(WaterfallControl) +WaterfallCheckBox = AceOO.Class(WaterfallControl) +WaterfallDragLink = AceOO.Class(WaterfallControl) +WaterfallButton = AceOO.Class(WaterfallControl) +WaterfallHeading = AceOO.Class(WaterfallControl) +WaterfallTextBox = AceOO.Class(WaterfallControl) +WaterfallSlider = AceOO.Class(WaterfallControl) +WaterfallColorSwatch = AceOO.Class(WaterfallControl) +WaterfallDropdown = AceOO.Class(WaterfallControl) +WaterfallKeybinding = AceOO.Class(WaterfallControl) + +--the left side treeview pane +WaterfallTreeView = AceOO.Class(FramePassthrough) +WaterfallTreeLine = AceOO.Class(FramePassthrough) +WaterfallTreeSection = AceOO.Class(FramePassthrough) + +--------------------------- +-- Waterfall Frame Class -- +--------------------------- + +local function frameOnClose() + this.obj.lib:Close(this.obj.id) +end + +local function closeOnClick() + this.obj:Hide() +end + +local function frameOnMouseDown() + this:GetParent():StartMoving() +end + +local function frameOnMouseUp() + this:GetParent():StopMovingOrSizing() +end + +local function sizerseOnMouseDown() + this:GetParent():StartSizing("BOTTOMRIGHT") +end + +local function sizersOnMouseDown() + this:GetParent():StartSizing("BOTTOM") +end + +local function sizereOnMouseDown() + this:GetParent():StartSizing("RIGHT") +end + +local function sizerOnMouseUp() + this:GetParent():StopMovingOrSizing() +end + + +function WaterfallFrame.prototype:init() + WaterfallFrame.super.prototype.init(self) + local frame = CreateFrame("Frame",nil,UIParent) + self.frame = frame + frame.obj = self + frame:SetWidth(550) + frame:SetHeight(500) + frame:SetPoint("CENTER",UIParent,"CENTER",0,0) + frame:EnableMouse() + frame:SetMovable(true) + frame:SetResizable(true) + frame:SetMinResize(500,300) + frame:SetFrameLevel(1) + frame:SetFrameStrata("DIALOG") + + frame:SetBackdrop(FrameBackdrop) + frame:SetBackdropColor(0,0,0) + frame:SetScript("OnHide",frameOnClose) + + local titlebar = frame:CreateTexture(nil,"BACKGROUND") + self.titlebar = titlebar + local titlebar2 = frame:CreateTexture(nil,"BACKGROUND") + self.titlebar2 = titlebar2 + local r,g,b = 1, 0.6, 0 + self.colorR = r + self.colorG = g + self.colorB = b + + titlebar:SetPoint("TOPLEFT",frame,"TOPLEFT",3,-4) + titlebar:SetPoint("TOPRIGHT",frame,"TOPRIGHT",-3,-4) + titlebar:SetHeight(13) + + titlebar2:SetPoint("TOPLEFT",titlebar,"BOTTOMLEFT",0,0) + titlebar2:SetPoint("TOPRIGHT",titlebar,"BOTTOMRIGHT",0,0) + titlebar2:SetHeight(13) + + titlebar:SetGradientAlpha("VERTICAL",r*0.6,g*0.6,b*0.6,1,r,g,b,1) + titlebar:SetTexture(r,g,b,1) + titlebar2:SetGradientAlpha("VERTICAL",r*0.9,g*0.9,b*0.9,1,r*0.6,g*0.6,b*0.6,1) + titlebar2:SetTexture(r,g,b,1) + + local closebutton = CreateFrame("Button",nil,frame,"UIPanelCloseButton") + closebutton:SetScript("OnClick", closeOnClick) + closebutton:SetPoint("TOPRIGHT",frame,"TOPRIGHT",-2,-2) + + self.closebutton = closebutton + closebutton.obj = self + + local title = CreateFrame("Frame",nil,frame) + self.title = title + title:EnableMouse() + title:SetScript("OnMouseDown",frameOnMouseDown) + title:SetScript("OnMouseUp", frameOnMouseUp) + + + title:SetPoint("TOPLEFT",titlebar,"TOPLEFT",0,0) + title:SetPoint("BOTTOMRIGHT",titlebar2,"BOTTOMRIGHT",0,0) + + local titletext = title:CreateFontString(nil,"OVERLAY","QuestTitleFont") + titletext:SetPoint("TOPLEFT",title,"TOPLEFT",0,0) + titletext:SetPoint("TOPRIGHT",title,"TOPRIGHT",0,0) + titletext:SetHeight(26) + titletext:SetShadowColor(0,0,0) + titletext:SetShadowOffset(1,-1) + titletext:SetTextColor(1,1,1) + + self.titletext = titletext + + local sizer_se = CreateFrame("Frame",nil,frame) + sizer_se:SetPoint("BOTTOMRIGHT",frame,"BOTTOMRIGHT",0,0) + sizer_se:SetWidth(25) + sizer_se:SetHeight(25) + sizer_se:EnableMouse() + sizer_se:SetScript("OnMouseDown",sizerseOnMouseDown) + sizer_se:SetScript("OnMouseUp", sizerOnMouseUp) + self.sizer_se = sizer_se + + local sizer_s = CreateFrame("Frame",nil,frame) + sizer_s:SetPoint("BOTTOMRIGHT",frame,"BOTTOMRIGHT",-25,0) + sizer_s:SetPoint("BOTTOMLEFT",frame,"BOTTOMLEFT",0,0) + sizer_s:SetHeight(25) + sizer_s:EnableMouse() + sizer_s:SetScript("OnMouseDown",sizersOnMouseDown) + sizer_s:SetScript("OnMouseUp", sizerOnMouseUp) + self.sizer_s = sizer_s + + local sizer_e = CreateFrame("Frame",nil,frame) + sizer_e:SetPoint("BOTTOMRIGHT",frame,"BOTTOMRIGHT",0,25) + sizer_e:SetPoint("TOPRIGHT",frame,"TOPRIGHT",0,0) + sizer_e:SetWidth(25) + sizer_e:EnableMouse() + sizer_e:SetScript("OnMouseDown",sizereOnMouseDown) + sizer_e:SetScript("OnMouseUp", sizerOnMouseUp) + self.sizer_e = sizer_e + + local treeview = WaterfallTreeView:new(self) + self.treeview = treeview + treeview:SetPoint("TOPLEFT",frame,"TOPLEFT",11,-32) + treeview:SetPoint("BOTTOMLEFT",frame,"BOTTOMLEFT",11,10) + treeview:SetWidth(200) + treeview:SetMinResize(200,0) + treeview:SetMaxResize(400,10000) + + local mainpane = WaterfallPane:new(self) + + mainpane:SetPoint("TOPLEFT",treeview.frame,"TOPRIGHT",-3,0) + mainpane:SetPoint("BOTTOMRIGHT",frame,"BOTTOMRIGHT",-10,10) + self.mainpane = mainpane + +end + +function WaterfallFrame.prototype:SetColor(r,g,b) + self.colorR, self.colorG, self.colorB = r,g,b + local titlebar = self.titlebar + local titlebar2 = self.titlebar2 + titlebar:SetGradientAlpha("VERTICAL",r*0.6,g*0.6,b*0.6,1,r,g,b,1) + titlebar:SetTexture(r,g,b,1) + titlebar2:SetGradientAlpha("VERTICAL",r*0.9,g*0.9,b*0.9,1,r*0.6,g*0.6,b*0.6,1) + titlebar2:SetTexture(r,g,b,1) + self.mainpane:SetColor(r,g,b) +end + +function WaterfallFrame.prototype:GetColor() + return self.colorR, self.colorG, self.colorB +end + +function WaterfallFrame.prototype:Hide() + self.frame:Hide() +end + +function WaterfallFrame.prototype:Show() + self.frame:Show() +end + +function WaterfallFrame.prototype:SetID(id) + self.id = id +end + +function WaterfallFrame.prototype:ChangePane(newpane) + + self.mainpane:Clear(newpane) + self.controlcount = 0 + currentframe = self + if self.children then + local title = self.children(newpane, self.childrenArg) + self.mainpane.titletext:SetText(title) + end + currentframe = nil + self.mainpane:DoLayout() + self.mainpane.paneid = newpane +end + +function WaterfallFrame.prototype:SetSelected(id, noupdate) + + self.treeview:SetSelected(id, noupdate) +end + +function WaterfallFrame.prototype:SetTitle(title) + self.titletext:SetText(title) +end + +function WaterfallFrame.prototype:SetChildren(children, arg) + self.children = children + self.childrenArg = arg +end +function WaterfallFrame.prototype:SetTree(tree, treestatus, func, ...) + self.treeview:SetContent(tree, treestatus, func, unpack(arg)) +end + +function WaterfallFrame.prototype:AddControl(control) + self.mainpane:AddControl(control) +end + +function WaterfallFrame.prototype:Refresh() + self.treeview:Refresh() + self:ChangePane(self.treeview:GetSelected()) +end + +function WaterfallFrame.prototype:CleanUp() + self.children = nil + self.tree = nil + self.treeview:CleanUp() + self:ChangePane() + self:Hide() +end + +function WaterfallFrame.prototype:ReAnchorTree() + self.treeview:SetPoint("TOPLEFT",self.frame,"TOPLEFT",11,-32) + self.treeview:SetPoint("BOTTOMLEFT",self.frame,"BOTTOMLEFT",11,10) +end +------------------------- +-- WaterfallPane Class -- +------------------------- + +local function paneOnSizeChanged() + this.obj:SizeChanged() +end +local function paneOnMouseWheel() + this.obj:MoveScroll(arg1) +end +local function paneOnSizeChanged() + this.obj:DoLayout() +end +local function paneOnScrollValueChanged(self,value) + this.obj:SetScroll(arg1) +end +local numpanes = 0 +function WaterfallPane.prototype:init(parent) + WaterfallPane.super.prototype.init(self) + numpanes = numpanes + 1 + self.parent = parent + local name = "WaterfallPane"..numpanes + local frame = CreateFrame("Frame",name,parent.frame) + frame:SetBackdrop(PaneBackdrop) + frame:SetBackdropBorderColor(0.6, 0.6, 0.6) + frame:SetBackdropColor(0.1, 0.1, 0.1) + frame:SetFrameLevel(2) + frame:SetFrameStrata("DIALOG") + self.frame = frame + frame.obj = self + frame:SetScript("OnSizeChanged",paneOnSizeChanged) + + --local title = CreateFrame("Frame",nil,frame) + --title:SetPoint("TOPLEFT",frame,"TOPLEFT",4,-4) + --title:SetPoint("TOPRIGHT",frame,"TOPRIGHT",-4,-4) + --title:SetHeight(24) + --title:SetBackdrop(PaneBackdrop) + --title:SetBackdropColor(0,0,0,0.2) + --self.title = title + + local scrollframe = CreateFrame("ScrollFrame",name.."ScrollFrame",frame) + local scrollchild = CreateFrame("Frame",name.."ChildFrame",scrollframe) + local scrollbar = CreateFrame("Slider",name.."ScrollBar",scrollframe,"UIPanelScrollBarTemplate") + self.scrollframe = scrollframe + self.scrollchild = scrollchild + self.scrollbar = scrollbar + + scrollframe:SetScrollChild(scrollchild) + scrollframe:SetPoint("TOPLEFT",frame,"TOPLEFT",9,-29) + scrollframe:SetPoint("BOTTOMRIGHT",frame,"BOTTOMRIGHT",-28,12) + scrollframe:EnableMouseWheel(true) + scrollframe:SetScript("OnMouseWheel", paneOnMouseWheel) + scrollframe:SetScript("OnSizeChanged", paneOnSizeChanged) + scrollframe.obj = self + + --scrollchild:SetPoint("TOPLEFT",scrollframe,"TOPLEFT",0,0) + --scrollchild:SetPoint("BOTTOMRIGHT",scrollframe,"BOTTOMRIGHT",0,0) + scrollchild:ClearAllPoints() + scrollchild:SetPoint("TOPLEFT",scrollframe,"TOPLEFT",0,0) + scrollchild:SetWidth(400) + + scrollbar:SetPoint("TOPLEFT",scrollframe,"TOPRIGHT",0,-16) + scrollbar:SetPoint("BOTTOMLEFT",scrollframe,"BOTTOMRIGHT",0,16) + scrollbar:SetMinMaxValues(0,1000) + scrollbar:SetValueStep(1) + scrollbar:SetValue(0) + scrollbar:SetWidth(16) + scrollbar:SetScript("OnValueChanged", paneOnScrollValueChanged) + scrollbar.obj = self + self.scrollvalue = 0 + + local titletext = frame:CreateFontString(nil,"OVERLAY","GameFontNormal") + titletext:SetPoint("TOPLEFT",frame,"TOPLEFT",8,-5) + titletext:SetPoint("TOPRIGHT",frame,"TOPRIGHT",-8,-5) + titletext:SetHeight(16) + titletext:SetText("") + titletext:SetTextColor(1,1,1) + + local r,g,b = 1, 0.6, 0 + self.colorR = r + self.colorG = g + self.colorB = b + local titlebar = frame:CreateTexture(nil,"BACKGROUND") + self.titlebar = titlebar + local titlebar2 = frame:CreateTexture(nil,"BACKGROUND") + self.titlebar2 = titlebar2 + titlebar:SetPoint("TOPLEFT",frame,"TOPLEFT",4,-4) + titlebar:SetPoint("TOPRIGHT",frame,"TOPRIGHT",-4,-4) + titlebar:SetHeight(10) + titlebar2:SetPoint("TOPLEFT",titlebar,"BOTTOMLEFT",0,0) + titlebar2:SetPoint("TOPRIGHT",titlebar,"BOTTOMRIGHT",0,0) + titlebar2:SetHeight(10) + + titlebar:SetGradientAlpha("VERTICAL",r*0.6,g*0.6,b*0.6,1,r,g,b,1) + titlebar:SetTexture(r,g,b,1) + titlebar2:SetGradientAlpha("VERTICAL",r*0.9,g*0.9,b*0.9,1,r*0.6,g*0.6,b*0.6,1) + titlebar2:SetTexture(r,g,b,1) + + --titletext:SetTextColor(1,1,1) + self.titletext = titletext + + self.controls = {} +end + +function WaterfallPane.prototype:GetID() + return self.parent.id +end + +function WaterfallPane.prototype:SetScroll(value) + local frame, child = self.scrollframe, self.scrollchild + local viewheight = frame:GetHeight() + local height = child:GetHeight() + local width, viewwidth = frame:GetWidth(), child:GetWidth() + local offset + local xOffset = max(0, (width-viewwidth)/2 ) + if viewheight > height then + offset = 0 + else + offset = floor((height - viewheight) / 1000.0 * (value or 0)) + end + child:ClearAllPoints() + child:SetPoint("TOPLEFT",frame,"TOPLEFT",xOffset,offset) + child.offset = offset + + self.scrollvalue = value +end + +--[[ +horiz scrolling version +function WaterfallPane.prototype:SetScroll(value) + local frame, child = self.scrollframe, self.scrollchild + local viewheight = frame:GetWidth() + local height = child:GetWidth() + local offset + if viewheight > height then + offset = 0 + else + offset = floor((height - viewheight) / 1000.0 * value) + end + child:ClearAllPoints() + child:SetPoint("TOPLEFT",frame,"TOPLEFT",-offset,0) + child:SetPoint("BOTTOMLEFT",frame,"BOTTOMLEFT",-offset,0) + child.offset = offset + + self.scrollvalue = value +end +]] +function WaterfallPane.prototype:MoveScroll(value) + local frame, child = self.scrollframe, self.scrollchild + local height, viewheight = frame:GetHeight(), child:GetHeight() + if height > viewheight then + self.scrollbar:Hide() + else + self.scrollbar:Show() + local diff = height - viewheight + local delta = 1 + if value < 0 then + delta = -1 + end + self.scrollbar:SetValue(min(max(self.scrollvalue + delta*(1000/(diff/45)),0), 1000)) + end +end + +function WaterfallPane.prototype:SetColor(r,g,b) + self.colorR, self.colorG, self.colorB = r,g,b + local titlebar = self.titlebar + local titlebar2 = self.titlebar2 + titlebar:SetGradientAlpha("VERTICAL",r*0.6,g*0.6,b*0.6,1,r,g,b,1) + titlebar:SetTexture(r,g,b,1) + titlebar2:SetGradientAlpha("VERTICAL",r*0.9,g*0.9,b*0.9,1,r*0.6,g*0.6,b*0.6,1) + titlebar2:SetTexture(r,g,b,1) +end + +function WaterfallPane.prototype:Clear(newpane) + for i = 1, table.getn(self.controls) do + releaseObj(self.controls[i]) + self.controls[i] = nil + end + table_setn(self.controls,0) + if self.paneid ~= newpane then + self:SetScroll(0) + end +end + +function WaterfallPane.prototype:ClearFocus() + for i, v in ipairs(self.controls) do + if v.class == WaterfallTextBox then + v.frame:ClearFocus() + end + end +end + +function WaterfallPane.prototype:AddControl(control) + tinsert(self.controls, control) + control:SetParent(self) +end + +--simple one under the other layout for now +function WaterfallPane.prototype:DoLayout() + + local usedheight = 0 + local maxwidth = 0 + local rowwidth = 1 + local rowheight = 1 + for i, control in ipairs(self.controls) do + control:ClearAllPoints() + if i == 1 then + control:SetPoint("TOPLEFT",self.scrollchild,"TOPLEFT",10,0) + rowheight = control:GetHeight() + rowwidth = control:GetWidth() + 10 + else + if control.noNewLine then + control:SetPoint("TOPLEFT",self.controls[i-1],"TOPRIGHT",0,0) + rowheight = max(rowheight,control:GetHeight()) + rowwidth = rowwidth + control:GetWidth() + else + usedheight = usedheight + rowheight + maxwidth = max(rowwidth,maxwidth) + control:SetPoint("TOPLEFT",self.scrollchild,"TOPLEFT",10,-usedheight) + rowheight = control:GetHeight() + rowwidth = control:GetWidth() + end + end + control:Show() + end + maxwidth = max(rowwidth,maxwidth) + + self.scrollchild:SetHeight(usedheight+rowheight) + self.scrollchild:SetWidth(maxwidth+5) + + self:FixScroll() +end + +--[[ +function WaterfallPane.prototype:DoLayout() + local colheight = 0 + local sectionheight = 0 + local sectionwidth = 0 + local colwidth = 0 + local usedwidth = 0 + local frameheight = self.scrollchild:GetHeight() + for i, control in ipairs(self.controls) do + colheight = colheight + control:GetHeight() + colwidth = max(colwidth, control:GetWidth()) + control:ClearAllPoints() + if colheight <= (frameheight - 10) then + if i == 1 then + control:SetPoint("TOPLEFT",self.scrollchild,"TOPLEFT",4,0) + else + control:SetPoint("TOPLEFT",self.controls[i-1],"BOTTOMLEFT",0,0) + end + else + usedwidth = usedwidth + colwidth + 6 + colwidth = 0 + colheight = 0 + control:SetPoint("TOPLEFT",self.scrollchild,"TOPLEFT",4+usedwidth,0) + end + control:Show() + end + usedwidth = usedwidth + colwidth + + self.scrollchild:SetWidth(usedwidth) + self:FixScroll() +end +]] +function WaterfallPane.prototype:SizeChanged() + self:FixScroll() +end + +function WaterfallPane.prototype:FixScroll() + local frame, child = self.scrollframe, self.scrollchild + local height, viewheight = frame:GetHeight(), child:GetHeight() + local width, viewwidth = frame:GetWidth(), child:GetWidth() + local offset = self.scrollchild.offset + if not offset then + offset = 0 + end + local curvalue = self.scrollbar:GetValue() + local xOffset = max(0, (width-viewwidth)/2 ) + if viewheight < height then + self.scrollbar:Hide() + --self.scrollbar:SetValue(0) + child:ClearAllPoints() + child:SetPoint("TOPLEFT",frame,"TOPLEFT",xOffset,0) + child.offset = 0 + else + self.scrollbar:Show() + local value = (offset / (viewheight - height) * 1000) + if value > 1000 then value = 1000 end + self.scrollbar:SetValue(value) + self:SetScroll(value) + if value < 1000 then + child:ClearAllPoints() + child:SetPoint("TOPLEFT",frame,"TOPLEFT",xOffset,offset) + child.offset = offset + end + end +end +--[[ +horiz scrolling version +function WaterfallPane.prototype:FixScroll() + local frame, child = self.scrollframe, self.scrollchild + local height, viewheight = frame:GetWidth(), child:GetWidth() + local offset = self.scrollchild.offset + if not offset then + offset = 0 + end + local curvalue = self.scrollbar:GetValue() + + if viewheight < height then + self.scrollbar:EnableMouse(false) + self.scrollbar:SetValue(0) + else + self.scrollbar:EnableMouse(true) + local value = (offset / (viewheight - height) * 1000) + if value > 1000 then value = 1000 end + self.scrollbar:SetValue(value) + self:SetScroll(value) + if value < 1000 then + child:ClearAllPoints() + child:SetPoint("TOPLEFT",frame,"TOPLEFT",-offset,0) + child:SetPoint("BOTTOMLEFT",frame,"BOTTOMLEFT",-offset,0) + child.offset = offset + end + end +end +]] +function WaterfallPane.prototype:Refresh(fullRefresh) + if fullRefresh then + self.parent:Refresh() + else + for i, control in ipairs(self.controls) do + control:Refresh() + end + end + self:FixScroll() +end + +----------------- +-- Label Class -- +----------------- + +function WaterfallLabel.prototype:init() + WaterfallLabel.super.prototype.init(self) + local frame = self.frame + local text = frame:CreateFontString(nil,"OVERLAY","GameFontNormalSmall") + self.text = text + frame.text = text + + text:SetText("Waterfalllabel") + text:SetJustifyH("LEFT") + text:SetTextColor(1,1,1) + frame:SetHeight(24) + frame:SetWidth(DEFAULT_CONTROL_WIDTH) + text:SetHeight(16) + text:SetPoint("LEFT",frame,"LEFT") +end + +--fontstrings dont return the correct width untill they have been drawn, wait a frame to set the width +local function labelFixWidth() + if not this.obj.width then + this:SetWidth(this.text:GetWidth()+3) + end + this:SetScript("OnUpdate",nil) +end + +function WaterfallLabel.prototype:SetText(text) + self.text:SetText(text) + self:Refresh() +end + +function WaterfallLabel.prototype:CleanUp() + self.frame:ClearAllPoints() + self.frame:Hide() + self.width = nil + self.height = nil + self.noNewLine = nil + self.causesRefresh = nil + self.fullRefresh = nil + self.treeRefresh = nil + self:SetText("") +end + +function WaterfallLabel.prototype:Refresh() + self.frame:SetHeight(self.height or 16) + if self.width then + self.text:ClearAllPoints() + self.text:SetPoint("LEFT",self.frame,"LEFT") + self.text:SetPoint("RIGHT",self.frame,"RIGHT") + self:SetWidth(self.width) + else + self.text:ClearAllPoints() + self.text:SetPoint("LEFT",self.frame,"LEFT") + self.frame:SetScript("OnUpdate", labelFixWidth) + end + self.text:SetJustifyH(self.justifyH or "LEFT") + self.text:SetTextColor(self.r or 1, self.g or 1, self.b or 1) +end + +----------------- +-- Linklabel Class -- +----------------- +local function Linklabel_OnEnter() + local self = this.obj + if not self.disabled then + self.highlight:Show() + showGameTooltip(self) + end +end + +local function Linklabel_OnLeave() + this.obj.highlight:Hide() + GameTooltip:Hide() +end + +local function linkConfirmFunc(self, refresh, full, func, ...) + func(unpack(arg)) + + if refresh or full then + self.parent:Refresh(full) + end +end + +local function Linklabel_OnClick() + local self = this.obj + local func = self.linkFunc + local confirm = this.obj.confirm + + if func then + if confirm then + if type(confirm) == "string" then + confirmPopup(confirm,linkConfirmFunc,self,self.causesRefresh,self.fullRefresh,func,getArgs(this.obj,"linkArg",1)) + else + confirmPopup((ARE_YOU_SURE):format(this.obj.text:GetText()),linkConfirmFunc, + self,self.causesRefresh,self.fullRefresh,func,getArgs(this.obj,"linkArg",1)) + end + else + func(getArgs(this.obj,"linkArg",1)) + end + if self.causesRefresh or self.fullRefresh then + self.parent:Refresh(self.fullRefresh) + end + end +end + +function WaterfallLinklabel.prototype:init() + WaterfallLinklabel.super.prototype.init(self, "Button") + local frame = self.frame + local text = frame:CreateFontString(nil,"OVERLAY","GameFontNormalSmall") + self.text = text + frame.text = text + frame:SetScript("OnClick", Linklabel_OnClick) + frame:SetScript("OnEnter", Linklabel_OnEnter) + frame:SetScript("OnLeave", Linklabel_OnLeave) + + text:SetText("WaterfallLinklabel") + text:SetJustifyH("LEFT") + text:SetTextColor(1,1,1) + frame:SetHeight(24) + frame:SetWidth(DEFAULT_CONTROL_WIDTH) + text:SetHeight(16) + text:SetPoint("LEFT",frame,"LEFT") + + local highlight = frame:CreateTexture(nil, "BACKGROUND") + self.highlight = highlight + highlight:SetTexture("Interface\\QuestFrame\\UI-QuestTitleHighlight") + highlight:SetBlendMode("ADD") + highlight:SetAllPoints(frame) + highlight:Hide() + + frame:EnableMouse() +end + +function WaterfallLinklabel.prototype:SetText(text) + self.text:SetText(text) + self:Refresh() +end + +function WaterfallLinklabel.prototype:CleanUp() + self.frame:ClearAllPoints() + self.frame:Hide() + self.width = nil + self.height = nil + self.noNewLine = nil + self.causesRefresh = nil + self.fullRefresh = nil + self.treeRefresh = nil + self:SetText("") +end + +function WaterfallLinklabel.prototype:Refresh() + self.frame:SetHeight(self.height or 16) + + if self.width then + self.text:ClearAllPoints() + self.text:SetPoint("LEFT",self.frame,"LEFT") + self.text:SetPoint("RIGHT",self.frame,"RIGHT") + self:SetWidth(self.width) + else + self.text:ClearAllPoints() + self.text:SetPoint("LEFT") + self.frame:SetScript("OnUpdate", labelFixWidth) + end + self.text:SetJustifyH(self.justifyH or "LEFT") + self.text:SetTextColor(self.r or 1, self.g or 1, self.b or 1) +end + +function WaterfallLinklabel.prototype:SetColor(r,g,b) + self.r = r + self.g = g + self.b = b + self:Refresh() +end + +----------------- +-- ColorSwatch Class -- +----------------- +local tmpt = setmetatable({}, {mode='v'}) +local function ColorSwatch_OnClick() + local self = this.obj + if not self.disabled then + local func = self.setFunc + local hasOpacity = self.hasOpacity + + for k in pairs(tmpt) do + tmpt[k] = nil + end + for i = 1, 1000 do + local x = self['setArg'..i] + if x == nil then + break + else + tmpt[i] = x + end + end + ColorPickerFrame.func = function() + if func then + local r,g,b = ColorPickerFrame:GetColorRGB() + local a = hasOpacity and 1 - OpacitySliderFrame:GetValue() or nil + local n = table.getn(tmpt) + tmpt[n+1] = r + tmpt[n+2] = g + tmpt[n+3] = b + tmpt[n+4] = a + func(unpack(tmpt)) + self:SetColor(r,g,b,a) + tmpt[n+1] = nil + tmpt[n+2] = nil + tmpt[n+3] = nil + tmpt[n+4] = nil + if self.causesRefresh or self.fullRefresh then + self.parent:Refresh(self.fullRefresh) + end + end + end + ColorPickerFrame.hasOpacity = self.hasOpacity + ColorPickerFrame.opacityFunc = ColorPickerFrame.func + ColorPickerFrame.opacity = 1 - (self.opacity or 0) + ColorPickerFrame:SetColorRGB(self.r, self.g, self.b) + local r, g, b, a = self.r, self.g, self.b, self.opacity + ColorPickerFrame.cancelFunc = function() + if func then + local n = table.getn(tmpt) + tmpt[n+1] = r + tmpt[n+2] = g + tmpt[n+3] = b + tmpt[n+4] = a + func(unpack(tmpt)) + self:SetColor(r,g,b,a) + for i = 1, n+4 do + tmpt[i] = nil + end + end + end + + ColorPickerFrame:SetFrameStrata(self.frame:GetFrameStrata()) + ColorPickerFrame:SetFrameLevel(self.frame:GetParent():GetFrameLevel()+1) + for i, child in ipairs({ColorPickerFrame:GetChildren()}) do + -- child:SetFrameStrata(self.frame:GetFrameStrata()) + child:SetFrameLevel(self.frame:GetParent():GetFrameLevel()+2) + end + ShowUIPanel(ColorPickerFrame) + end +end + +local function ColorSwatch_OnEnter() + local self = this.obj + if not self.disabled then + self.highlight:Show() + showGameTooltip(self) + end +end + +local function ColorSwatch_OnLeave() + + this.obj.highlight:Hide() + GameTooltip:Hide() +end + +function WaterfallColorSwatch.prototype:init() + WaterfallColorSwatch.super.prototype.init(self,"Button") + local frame = self.frame + + local text = frame:CreateFontString(nil,"OVERLAY","GameFontNormalSmall") + self.text = text + text:SetJustifyH("LEFT") + text:SetTextColor(1,1,1) + frame:SetHeight(24) + frame:SetWidth(DEFAULT_CONTROL_WIDTH) + text:SetHeight(24) + frame:SetScript("OnClick", ColorSwatch_OnClick) + frame:SetScript("OnEnter",ColorSwatch_OnEnter) + frame:SetScript("OnLeave",ColorSwatch_OnLeave) + + local colorSwatch = frame:CreateTexture(nil, "OVERLAY") + self.colorSwatch = colorSwatch + colorSwatch:SetWidth(24) + colorSwatch:SetHeight(24) + colorSwatch:SetTexture("Interface\\ChatFrame\\ChatFrameColorSwatch") + local texture = frame:CreateTexture(nil, "OVERLAY") + colorSwatch.texture = texture + texture:SetTexture(1, 1, 1) + texture:SetWidth(13.8) + texture:SetHeight(13.8) + texture:Show() + + local highlight = frame:CreateTexture(nil, "BACKGROUND") + self.highlight = highlight + highlight:SetTexture("Interface\\QuestFrame\\UI-QuestTitleHighlight") + highlight:SetBlendMode("ADD") + highlight:SetAllPoints(frame) + highlight:Hide() + + texture:SetPoint("CENTER", colorSwatch, "CENTER") + colorSwatch:SetPoint("LEFT", frame, "LEFT", 0, 0) + text:SetPoint("LEFT",colorSwatch,"RIGHT",2,0) + text:SetPoint("RIGHT",frame,"RIGHT") +end + +function WaterfallColorSwatch.prototype:SetText(text) + self.text:SetText(text) +end + +function WaterfallColorSwatch.prototype:SetColor(r,g,b,a) + self.r = r + self.g = g + self.b = b + self.opacity = a + self:Refresh() +end + +function WaterfallColorSwatch.prototype:Refresh() + if self.getFunc then + self.r, self.g, self.b, self.opacity = self.getFunc(getArgs(self,"getArg",1)) + end + if self.disabledFunc then + self.disabled = self.disabledFunc(getArgs(self,"disabledArg",1)) + if self.inverseDisabled then + self.disabled = not self.disabled + end + end + self.colorSwatch.texture:SetTexture(self.r,self.g,self.b) + if self.disabled then + self.text:SetTextColor(0.5,0.5,0.5) + else + self.text:SetTextColor(1,1,1) + end + if self.width then + self:SetWidth(self.width) + else + self:SetWidth(DEFAULT_CONTROL_WIDTH) + end +end + +function WaterfallColorSwatch.prototype:CleanUp() + self.frame:ClearAllPoints() + self.frame:Hide() + self.width = nil + self.height = nil + self.noNewLine = nil + self.causesRefresh = nil + self.fullRefresh = nil + self.treeRefresh = nil + self:SetText("") + self:SetFunc("color",nil) + self.r = nil + self.g = nil + self.b = nil + self.hasOpacity = nil + self.opacity = nil + self.disabled = nil + self:SetFunc("disabled",nil) +end + + + +----------------- +-- Heading Class -- +----------------- + +function WaterfallHeading.prototype:init() + WaterfallHeading.super.prototype.init(self) + local frame = self.frame + local text = frame:CreateFontString(nil,"OVERLAY","GameFontNormal") + self.text = text + frame.text = text + + text:SetJustifyH("LEFT") + frame:SetHeight(24) + frame:SetWidth(DEFAULT_CONTROL_WIDTH) + text:SetHeight(24) + text:SetPoint("LEFT",frame,"LEFT") +end + +function WaterfallHeading.prototype:SetText(text) + self.text:SetText(text) + self.frame:SetScript("OnUpdate", labelFixWidth) +end + +function WaterfallHeading.prototype:CleanUp() + self.frame:ClearAllPoints() + self.frame:Hide() + self.width = nil + self.height = nil + self.noNewLine = nil + self.causesRefresh = nil + self.fullRefresh = nil + self.treeRefresh = nil + self:SetText("") +end + +function WaterfallHeading.prototype:Refresh() + self.frame:SetHeight(self.height or 16) + if self.width then + self.text:ClearAllPoints() + self.text:SetPoint("LEFT",self.frame,"LEFT") + self.text:SetPoint("RIGHT",self.frame,"RIGHT") + self:SetWidth(self.width) + else + self.text:ClearAllPoints() + self.text:SetPoint("LEFT",self.frame,"LEFT") + self.frame:SetScript("OnUpdate", labelFixWidth) + end + self.text:SetJustifyH(self.justifyH or "LEFT") +end +----------------- +-- CheckBox Class -- +----------------- + +local function CheckBox_OnEnter() + local self = this.obj + if not self.disabled then + self.highlight:Show() + showGameTooltip(self) + end +end + +local function CheckBox_OnLeave() + local self = this.obj + if not self.down then + self.highlight:Hide() + end + GameTooltip:Hide() +end + +local function CheckBox_OnMouseUp() + local self = this.obj + if not self.disabled then + self:ToggleChecked() + self.text:SetPoint("LEFT",self.check,"RIGHT",0,0) + end + self.down = nil +end + +local function CheckBox_OnMouseDown() + local self = this.obj + if not self.disabled then + self.text:SetPoint("LEFT",self.check,"RIGHT",1,-1) + self.down = true + end +end + +function WaterfallCheckBox.prototype:init() + WaterfallCheckBox.super.prototype.init(self,"Button") + + local frame = self.frame + local text = frame:CreateFontString(nil,"OVERLAY","GameFontNormalSmall") + self.text = text + + frame:SetScript("OnEnter",CheckBox_OnEnter) + frame:SetScript("OnLeave",CheckBox_OnLeave) + frame:SetScript("OnMouseUp",CheckBox_OnMouseUp) + frame:SetScript("OnMouseDown",CheckBox_OnMouseDown) + frame:EnableMouse() + local checkbg = frame:CreateTexture(nil,"ARTWORK") + self.checkbg = checkbg + checkbg:SetWidth(20) + checkbg:SetHeight(20) + checkbg:SetPoint("LEFT",frame,"LEFT",0,0) + checkbg:SetTexture("Interface\\Buttons\\UI-CheckBox-Up") + local check = frame:CreateTexture(nil,"OVERLAY") + self.check = check + check:SetWidth(20) + check:SetHeight(20) + check:SetPoint("LEFT",frame,"LEFT",0,0) + check:SetTexture("Interface\\Buttons\\UI-CheckBox-Check") + + self.checked = false + self:UpdateCheck() + + local highlight = frame:CreateTexture(nil, "BACKGROUND") + self.highlight = highlight + highlight:SetTexture("Interface\\QuestFrame\\UI-QuestTitleHighlight") + highlight:SetBlendMode("ADD") + highlight:SetAllPoints(frame) + highlight:Hide() + + + text:SetJustifyH("LEFT") + text:SetTextColor(1,1,1) + frame:SetHeight(24) + frame:SetWidth(DEFAULT_CONTROL_WIDTH) + text:SetHeight(24) + text:SetPoint("LEFT",check,"RIGHT",0,0) +end + +function WaterfallCheckBox.prototype:UpdateTexture() + local checkbg = self.checkbg + local check = self.check + local highlight = self.highlight + + if self.isRadio then + checkbg:SetTexture("Interface\\Buttons\\UI-RadioButton") + checkbg:SetTexCoord(0,0.25,0,1) + check:SetTexture("Interface\\Buttons\\UI-RadioButton") + check:SetTexCoord(0.5,0.75,0,1) + check:SetBlendMode("ADD") + else + checkbg:SetTexture("Interface\\Buttons\\UI-CheckBox-Up") + checkbg:SetTexCoord(0,1,0,1) + check:SetTexture("Interface\\Buttons\\UI-CheckBox-Check") + check:SetTexCoord(0,1,0,1) + check:SetBlendMode("BLEND") + end +end + +function WaterfallCheckBox.prototype:SetText(text) + self.text:SetText(text) +end + +function WaterfallCheckBox.prototype:UpdateCheck() + + if self.checked then + self.check:Show() + else + self.check:Hide() + end +end + +function WaterfallCheckBox.prototype:ToggleChecked() + self.checked = not self.checked + + self:UpdateCheck() + + if self.setFunc then + self.setFunc(getArgs(self,"setArg",1,self.checked)) + end + + if self.causesRefresh or self.fullRefresh then + self.parent:Refresh(self.fullRefresh) + else + self:Refresh() + end +end + +function WaterfallCheckBox.prototype:SetChecked(value) + self.checked = value + self:UpdateCheck() +end + +function WaterfallCheckBox.prototype:GetChecked() + return self.checked +end + +function WaterfallCheckBox.prototype:CleanUp() + self.frame:ClearAllPoints() + self.frame:Hide() + self.highlight:Hide() + self.width = nil + self.height = nil + self.noNewLine = nil + self.causesRefresh = nil + self.fullRefresh = nil + self.treeRefresh = nil + self:SetText("") + self:SetChecked(false) + self:SetFunc("disabled",nil) + self:SetFunc("get",nil) + self:SetFunc("set",nil) + self.isRadio = nil +end + +function WaterfallCheckBox.prototype:Refresh() + if self.getFunc then + local checked = self.getFunc(getArgs(self,"getArg",1)) + self:SetChecked(checked) + end + if self.disabledFunc then + self.disabled = self.disabledFunc(getArgs(self,"disabledArg",1)) + if self.inverseDisabled then + self.disabled = not self.disabled + end + end + if self.disabled then + self.text:SetTextColor(0.5,0.5,0.5) + SetDesaturation(self.check, true) + else + self.text:SetTextColor(1,1,1) + SetDesaturation(self.check, false) + end + if self.width then + self:SetWidth(self.width) + else + self:SetWidth(DEFAULT_CONTROL_WIDTH) + end + self:UpdateTexture() +end + +----------------- +-- DragLink Class -- +----------------- + +-- Provides an icon and description of a link based on on its linkInfo.itemType, itemId, itemInfo +-- Dragging onto the linkIcon sets linkInfo.itemType, linkInfo.itemId, linkInfo.itemInfo +-- If linkInfo.itemType == "spell" then linkInfo.spellName, linkInfo.spellRank, linkInfo.spellClass (current player class) are also set +-- Dragging between two DragLink objects will swap their contents + +function Waterfall:GetDraggingObject() + return Waterfall.fromObject +end + + +function Waterfall:SetDraggingObject(fromObject) + Waterfall.fromObject = fromObject +end + +local function DragLinkOnDragStart() + local fromObject = this.obj + Waterfall:SetDraggingObject(fromObject) +end + +local function DragLinkOnReceiveDrag() + local self = this.obj + local toObject = this.obj + local fromObject = Waterfall:GetDraggingObject() + local refreshNeeded = false + if (fromObject and fromObject ~= toObject) then +--DEFAULT_CHAT_FRAME:AddMessage("DragLinkOnReceiveDrag toObject " .. tostring(toObject)) + fromObject.itemType, toObject.itemType = toObject.itemType, fromObject.itemType + fromObject.itemId, toObject.itemId = toObject.itemId, fromObject.itemId + fromObject.itemInfo, toObject.itemInfo = toObject.itemInfo, fromObject.itemInfo + fromObject.spellName, toObject.spellName = toObject.spellName, fromObject.spellName + fromObject.spellRank, toObject.spellRank = toObject.spellRank, fromObject.spellRank + fromObject.spellClass, toObject.spellClass = toObject.spellClass, fromObject.spellClass + if fromObject.causesRefresh or fromObject.fullRefresh then + fromObject.parent:Refresh(fromObject.fullRefresh) + end + refreshNeeded = true + else + local itemType, itemId, itemInfo = GetCursorInfo() +--DEFAULT_CHAT_FRAME:AddMessage("DragLinkOnReceiveDrag itemType " .. tostring(itemType) .. " itemId " .. tostring(itemId) .. " itemInfo " .. tostring(itemInfo)) + if (itemType == "item" or itemType == "spell" or itemType == "macro") then + local linkInfo = self.linkInfo + linkInfo.itemType = itemType + linkInfo.itemId = itemId + linkInfo.itemInfo = itemInfo + if (itemType == "spell") then + local spellName, spellRank = GetSpellName(itemId, itemInfo) + linkInfo.spellName = spellName + linkInfo.spellRank = spellRank + linkInfo.spellClass = WaterfallDragLink.CLASS + else + linkInfo.spellName = nil + linkInfo.spellRank = nil + linkInfo.spellClass = nil + end +--DEFAULT_CHAT_FRAME:AddMessage("DragLinkOnReceiveDrag itemType " .. tostring(itemType) .. " itemId " .. tostring(itemId) .. " itemInfo " .. tostring(itemInfo)) +--DEFAULT_CHAT_FRAME:AddMessage("DragLinkOnReceiveDrag self.setFunc " .. tostring(self.setFunc) .. " self.disabled " .. tostring(self.disabled)) + if (self.setFunc and not self.disabled) then + self.setFunc(getArgs(self,"setArg",1,linkInfo)) + end + refreshNeeded = true + ClearCursor() + end + end + + if (refreshNeeded) then + if self.causesRefresh or self.fullRefresh then + self.parent:Refresh(self.fullRefresh) + else + self:Refresh() + end + end + Waterfall:SetDraggingObject(nil) +end + +local function DragLinkOnClick() + DragLinkOnReceiveDrag() --?? +end + +local function DragLinkOnEnter() + local self = this.obj + local linkInfo = self.linkInfo + if (linkInfo) then + if (linkInfo.itemType == "item") then + GameTooltip:SetOwner(this, "ANCHOR_PRESERVE") + if (linkInfo.itemId) then + GameTooltip:SetHyperlink("item:" .. linkInfo.itemId .. ":0:0:0:0:0:0:0") + else + GameTooltip:SetHyperlink(tostring(linkInfo.itemInfo)) + end + elseif (linkInfo.itemType == "spell") then + if (linkInfo.spellName) then +-- Cannot set this without preserving spell book & spell id. Even then may be from different class. So lame. Awaiting a Blizzard API that doesnt suck. +-- GameTooltip:SetSpell(linkInfo.spellId, linkInfo.spellTab) + GameTooltip:SetOwner(this, "ANCHOR_PRESERVE") + GameTooltip:AddLine(linkInfo.spellName, 1, 1, 1) + local name, rank, icon, cost, isFunnel, powerType, castTime, minRange, maxRange = GetSpellInfo(linkInfo.spellName) + if (name) then + if (powerType == 0) then + cost = tostring(cost) .. " " .. MANA + elseif (powerType == 1) then + cost = tostring(cost) .. " " .. RAGE + elseif (powerType == 2) then + cost = tostring(cost) .. " " .. FOCUS + elseif (powerType == 3) then + cost = tostring(cost) .. " " .. ENERGY + elseif (powerType == 4) then + cost = tostring(cost) .. " " .. HAPPINESS + end + local range = "" + if (maxRange) then + range = SPELL_RANGE:format(maxRange) + end + GameTooltip:AddDoubleLine(cost, range, 1, 1, 1, 1, 1, 1) + if (castTime and castTime > 0) then + GameTooltip:AddLine(SPELL_CAST_TIME_SEC:format(castTime), 1, 1, 1) + else + GameTooltip:AddLine(SPELL_CAST_TIME_INSTANT, 1, 1, 1) + end + if (rank) then + GameTooltip:AddLine(tostring(rank)) + end + GameTooltip:Show() + end + end + elseif (linkInfo.itemType == "macro") then + if (linkInfo.itemId) then + local name, icon, body = GetMacroInfo(linkInfo.itemId) + if (name) then + GameTooltip:SetOwner(this, "ANCHOR_PRESERVE") + GameTooltip:AddLine(tostring(name), 0.2, 0.8, 0.8, 1) + GameTooltip:AddTexture(icon) + GameTooltip:AddLine(tostring(body), 1, 1, 1, 1) + GameTooltip:Show() + end + end + end + end +end + +local function DragLinkOnLeave() + GameTooltip:Hide() +end + +local function DragLinkGetTexture(self) + local linkInfo = self.linkInfo + local texture + if (linkInfo) then + if (linkInfo.itemType == "item") then + if (linkInfo.itemId and tonumber(linkInfo.itemId)) then + _,_,_,_,_,_,_,_,_,texture = GetItemInfo(tonumber(linkInfo.itemId)) + if (texture) then + return texture + end + end + elseif (linkInfo.itemType == "spell") then + if (linkInfo.spellName) then + _, _, texture = GetSpellInfo(linkInfo.spellName) + if (texture) then + return texture + end + end + elseif (linkInfo.itemType == "macro") then + if (linkInfo.itemId) then + _, texture = GetMacroInfo(linkInfo.itemId) + if (texture) then + return texture + end + end + end + end + return "Interface\\Icons\\INV_Misc_Gift_01" +end + +WaterfallDragLink.defaultIconSize = 36 +_, WaterfallDragLink.CLASS = UnitClass("player") -- Use the capitalized english class name + +function WaterfallDragLink.prototype:init() + WaterfallDragLink.super.prototype.init(self, "Button") + + local frame = self.frame + local text = frame:CreateFontString(nil,"OVERLAY","GameFontNormalSmall") + self.text = text + + frame:SetScript("OnDragStart", DragLinkOnDragStart) + frame:SetScript("OnReceiveDrag", DragLinkOnReceiveDrag) + frame:SetScript("OnClick", DragLinkOnClick) + frame:SetScript("OnEnter", DragLinkOnEnter) + frame:SetScript("OnLeave", DragLinkOnLeave) + + frame:EnableMouse() + frame:RegisterForDrag("LeftButton") + frame:RegisterForClicks("LeftButtonUp", "RightButtonUp") + + local linkIcon = frame:CreateTexture(nil, "OVERLAY") + linkIcon:SetWidth(self.iconWidth or WaterfallDragLink.defaultIconSize) + linkIcon:SetHeight(self.iconHeight or WaterfallDragLink.defaultIconSize) + linkIcon:SetPoint("LEFT",frame,"LEFT",0,0) + linkIcon:SetTexture(DragLinkGetTexture(self)) + linkIcon:SetTexCoord(0,1,0,1) + linkIcon:Show() + self.linkIcon = linkIcon + + text:SetJustifyH("LEFT") + text:SetTextColor(1,1,1) + + frame:SetHeight(self.iconHeight or WaterfallDragLink.defaultIconSize + 4) + frame:SetWidth(DEFAULT_CONTROL_WIDTH) + + text:SetHeight(self.iconHeight or WaterfallDragLink.defaultIconSize + 4) + text:SetPoint("LEFT",check,"RIGHT",0,0) +end + +function WaterfallDragLink.prototype:SetText(text) + self.text:SetText(text) +end + +function WaterfallDragLink.prototype:CleanUp() + self.frame:ClearAllPoints() + self.frame:Hide() + self.width = nil + self.height = nil + self.noNewLine = nil + self.causesRefresh = nil + self.fullRefresh = nil + self.treeRefresh = nil + self:SetText("") + self:SetFunc("disabled", nil) + self.linkIcon:Hide() +end + +function WaterfallDragLink.prototype:Refresh() + if self.disabledFunc then + self.disabled = self.disabledFunc(getArgs(self,"disabledArg",1)) + if self.inverseDisabled then + self.disabled = not self.disabled + end + end + if self.disabled then + self.frame:Disable() + else + self.frame:Enable() + end + if self.width then + self:SetWidth(self.width) + else + self:SetWidth(DEFAULT_CONTROL_WIDTH) + end + if (not self.linkIcon) then + local linkIcon = self.frame:CreateTexture(nil, "OVERLAY") + linkIcon:SetWidth(self.iconWidth or WaterfallDragLink.defaultIconSize) + linkIcon:SetHeight(self.iconHeight or WaterfallDragLink.defaultIconSize) + linkIcon:SetPoint("TOP", self.frame, "TOP", 0, 0) + linkIcon:SetTexCoord(0,1,0,1) + linkIcon:Show() + self.linkIcon = linkIcon + end + if self.getFunc then + self.linkInfo = self.getFunc(getArgs(self,"getArg",1)) + else + self:assert(self.linkInfo, "wth?") + end + self.linkIcon:SetTexture(DragLinkGetTexture(self)) + self.linkIcon:Show() + if (self.linkInfo.itemId) then + self:SetText((self.linkInfo.itemInfo or "") .. " (" .. tostring(self.linkInfo.itemId) .. ")") + else + self:SetText("") + end +--DEFAULT_CHAT_FRAME:AddMessage("WaterfallDragLink.prototype:Refresh texture " .. tostring(DragLinkGetTexture(self)) .. " self.linkInfo.itemId " .. tostring(self.linkInfo.itemId) .. " self.linkInfo.itemInfo " .. tostring(self.linkInfo.itemInfo)) +end + +----------------- +-- Button Class -- +----------------- +local function buttonConfirmFunc(self, refresh, full, func, ...) + func(unpack(arg)) + + if refresh or full then + self.parent:Refresh(full) + end +end + +local function Button_OnClick() + local self = this.obj + local func = self.execFunc + local confirm = this.obj.confirm + if func then + if confirm then + if type(confirm) == "string" then + confirmPopup(confirm,buttonConfirmFunc,self,self.causesRefresh,self.fullRefresh,func,getArgs(this.obj,"execArg",1)) + else + confirmPopup((ARE_YOU_SURE):format(this.obj.frame:GetText()),buttonConfirmFunc, + self,self.causesRefresh,self.fullRefresh,func,getArgs(this.obj,"execArg",1)) + end + else + func(getArgs(this.obj,"execArg",1)) + end + if self.causesRefresh or self.fullRefresh then + self.parent:Refresh(self.fullRefresh) + end + end +end + +local function Control_OnEnter() + if this.obj.link then + GameTooltip:SetOwner(this, "ANCHOR_PRESERVE") + GameTooltip:SetHyperlink(this.obj.link) + else + showGameTooltip(this.obj) + end +end + +local function Control_OnLeave() + GameTooltip:Hide() +end + +function WaterfallButton.prototype:init() + WaterfallButton.super.prototype.init(self,"Button","UIPanelButtonTemplate") + local frame = self.frame + --local text = frame:CreateFontString(nil,"OVERLAY","GameFontNormalSmall") + local text = frame:GetFontString() + text:SetPoint("LEFT",frame,"LEFT",7,0) + text:SetPoint("RIGHT",frame,"RIGHT",-7,0) + + frame:SetScript("OnClick",Button_OnClick) + frame:SetScript("OnEnter",Control_OnEnter) + frame:SetScript("OnLeave",Control_OnLeave) + + frame:EnableMouse() + + frame:SetHeight(24) + frame:SetWidth(DEFAULT_CONTROL_WIDTH) + +end + +function WaterfallButton.prototype:SetText(text) + self.frame:SetText(text) +end + +function WaterfallButton.prototype:CleanUp() + self.frame:ClearAllPoints() + self.frame:Hide() + self.width = nil + self.height = nil + self.noNewLine = nil + self.causesRefresh = nil + self.fullRefresh = nil + self.treeRefresh = nil + self:SetText("") + self:SetFunc("disabled",nil) + self:SetFunc("exec",nil) +end + +function WaterfallButton.prototype:Refresh() + if self.disabledFunc then + self.disabled = self.disabledFunc(getArgs(self,"disabledArg",1)) + if self.inverseDisabled then + self.disabled = not self.disabled + end + end + if self.disabled then + self.frame:Disable() + else + self.frame:Enable() + end + if self.width then + self:SetWidth(self.width) + else + self:SetWidth(DEFAULT_CONTROL_WIDTH) + end +end + +----------------- +-- Keybinding Class -- +----------------- + +local function Keybinding_OnClick( button) + local self = this.obj + + if button == "LeftButton" or button == "RightButton" then + if self.waitingForKey then + this:EnableKeyboard(false) + self.msgframe:Hide() + this:UnlockHighlight() + self.waitingForKey = nil + else + this:EnableKeyboard(true) + self.parent:ClearFocus() + self.msgframe:Show() + this:LockHighlight() + self.waitingForKey = true + end + end +end + + +local function Keybinding_OnKeyDown( key) + local self = this.obj + if self.waitingForKey then + local keyPressed = key; + if keyPressed == "ESCAPE" then + keyPressed = "" + else + if ( keyPressed == "BUTTON1" or keyPressed == "BUTTON2" ) then + return; + end + if ( keyPressed == "UNKNOWN" ) then + return; + end + if ( keyPressed == "SHIFT" or keyPressed == "CTRL" or keyPressed == "ALT") then + return; + end + if ( keyPressed == "LSHIFT" or keyPressed == "LCTRL" or keyPressed == "LALT") then + return; + end + if ( keyPressed == "RSHIFT" or keyPressed == "RCTRL" or keyPressed == "RALT") then + return; + end + if ( IsShiftKeyDown() ) then + keyPressed = "SHIFT-"..keyPressed; + end + if ( IsControlKeyDown() ) then + keyPressed = "CTRL-"..keyPressed; + end + if ( IsAltKeyDown() ) then + keyPressed = "ALT-"..keyPressed; + end + end + + if self.setFunc and not self.disabled then + local validated = true + if keyPressed == "" then + keyPressed = nil + else + if self.keybindingOnly and not self.keybindingOnly[keyPressed] then + validated = false + end + if self.keybindingExcept and self.keybindingExcept[keyPressed] then + validated = false + end + end + if validated then + self.setFunc(getArgs(self,"setArg",1,keyPressed)) + end + if self.causesRefresh or self.fullRefresh then + self.parent:Refresh(self.fullRefresh) + end + end + self:Refresh() + + this:EnableKeyboard(false) + self.msgframe:Hide() + this:UnlockHighlight() + self.waitingForKey = nil + end +end + +local function Keybinding_OnMouseDown( button) + if ( button == "LeftButton" or button == "RightButton" ) then + return + elseif ( button == "MiddleButton" ) then + button = "BUTTON3"; + elseif ( button == "Button4" ) then + button = "BUTTON4" + elseif ( button == "Button5" ) then + button = "BUTTON5" + end + Keybinding_OnKeyDown( button) +end + +local function keybindingMsgFixWidth() + this:SetWidth(this.msg:GetWidth()+10) + this:SetScript("OnUpdate",nil) +end + +function WaterfallKeybinding.prototype:init() + WaterfallKeybinding.super.prototype.init(self,"Button","UIPanelButtonTemplate") + local frame = self.frame + --local text = frame:CreateFontString(nil,"OVERLAY","GameFontNormalSmall") + local text = frame:GetFontString() + text:SetPoint("LEFT",frame,"LEFT",7,0) + text:SetPoint("RIGHT",frame,"RIGHT",-7,0) + + frame:SetScript("OnClick",Keybinding_OnClick) + frame:SetScript("OnKeyDown",Keybinding_OnKeyDown) + frame:SetScript("OnEnter",Control_OnEnter) + frame:SetScript("OnLeave",Control_OnLeave) + frame:SetScript("OnMouseDown",Keybinding_OnMouseDown) + frame:RegisterForClicks("AnyDown") + frame:EnableMouse() + + frame:SetHeight(24) + frame:SetWidth(DEFAULT_CONTROL_WIDTH) + + local msgframe = CreateFrame("Frame",nil,UIParent) + msgframe:SetHeight(30) + msgframe:SetBackdrop(ControlBackdrop) + msgframe:SetBackdropColor(0,0,0) + msgframe:SetFrameStrata("DIALOG") + msgframe:SetFrameLevel(20) + self.msgframe = msgframe + local msg = msgframe:CreateFontString(nil,"OVERLAY","GameFontNormal") + msg:SetText("Press a key to bind, ESC to clear the binding or click the button again to cancel") + msgframe.msg = msg + msg:SetPoint("TOPLEFT",msgframe,"TOPLEFT",5,-5) + msgframe:SetScript("OnUpdate", keybindingMsgFixWidth) + msgframe:SetPoint("TOP",frame,"BOTTOM",0,-2) + msgframe:Hide() +end + +function WaterfallKeybinding.prototype:SetText(text) + self.frame:SetText(text) +end + +function WaterfallKeybinding.prototype:CleanUp() + self.frame:ClearAllPoints() + self.frame:Hide() + self.width = nil + self.height = nil + self.noNewLine = nil + self.causesRefresh = nil + self.fullRefresh = nil + self.treeRefresh = nil + self:SetText("") + self:SetFunc("disabled",nil) + self:SetFunc("exec",nil) + self.waitingForKey = nil + self.msgframe:Hide() +end + +function WaterfallKeybinding.prototype:Refresh() + if self.getFunc then + local text = self.getFunc(getArgs(self,"getArg",1)) + self:SetText(text or "") + end + if self.disabledFunc then + self.disabled = self.disabledFunc(getArgs(self,"disabledArg",1)) + if self.inverseDisabled then + self.disabled = not self.disabled + end + end + if self.disabled then + self.frame:Disable() + else + self.frame:Enable() + end + if self.width then + self:SetWidth(self.width) + else + self:SetWidth(DEFAULT_CONTROL_WIDTH) + end +end + +----------------- +-- Slider Class -- +----------------- + +local function Slider_OnValueChanged() + local self = this.obj + if not this.setup then + local func = self.setFunc + local newvalue + newvalue = this:GetValue() + if newvalue ~= self.value and not self.disabled then + if self.bigStep > 0 then + self.value = math.floor((newvalue - self.min) / self.bigStep + 0.5) * self.bigStep + self.min + else + self.value = newvalue + end + if func and not self.isInit then + if (self.finalSetOnly) then + self.finalSetValue = self.value + else + func(getArgs(this.obj,"setArg",1,self.value)) + end + end + end + if self.value then + this.obj.valuetext:SetText(math.floor(self.value*10)/10) + end + end +end + +local function Slider_OnMouseUp() + local self = this.obj + if (self.finalSetValue) then + local func = self.setFunc + if func then + func(getArgs(this.obj,"setArg",1,self.finalSetValue)) + end + end + if self.causesRefresh or self.fullRefresh then + self.parent:Refresh(self.fullRefresh) + end +end + +local SliderBackdrop = { + bgFile = "Interface\\Buttons\\UI-SliderBar-Background", + edgeFile = "Interface\\Buttons\\UI-SliderBar-Border", + tile = true, tileSize = 8, edgeSize = 8, + insets = { left = 3, right = 3, top = 6, bottom = 6 } +} + +function WaterfallSlider.prototype:init() + WaterfallSlider.super.prototype.init(self,"Frame") + local frame = self.frame + + self.slider = CreateFrame("Slider",nil,frame) + local slider = self.slider + slider:SetScript("OnEnter",Control_OnEnter) + slider:SetScript("OnLeave",Control_OnLeave) + slider:SetScript("OnMouseUp", Slider_OnMouseUp) + slider.obj = self + slider:SetOrientation("HORIZONTAL") + frame:SetWidth(DEFAULT_CONTROL_WIDTH) + slider:SetHeight(17) + slider:SetHitRectInsets(0,0,-10,-10) + slider:SetBackdrop(SliderBackdrop) + self.text = slider:CreateFontString(nil,"ARTWORK","GameFontNormalSmall") + self.text:SetPoint("TOP",frame,"TOP",0,-2) + + self.lowtext = slider:CreateFontString(nil,"ARTWORK","GameFontHighlightSmall") + self.lowtext:SetPoint("TOPLEFT",slider,"BOTTOMLEFT",2,3) + + self.hightext = slider:CreateFontString(nil,"ARTWORK","GameFontHighlightSmall") + self.hightext:SetPoint("TOPRIGHT",slider,"BOTTOMRIGHT",-2,3) + + self.valuetext = slider:CreateFontString(nil,"ARTWORK","GameFontHighlightSmall") + self.valuetext:SetPoint("TOP",slider,"BOTTOM",0,3) + + + slider:SetThumbTexture("Interface\\Buttons\\UI-SliderBar-Button-Horizontal") + + frame:SetWidth(DEFAULT_CONTROL_WIDTH) + frame:SetHeight(40) + slider:SetPoint("TOP",self.text,"BOTTOM",0,0) + slider:SetPoint("LEFT",frame,"LEFT",0,0) + slider:SetPoint("RIGHT",frame,"RIGHT",0,0) + + if self.getFunc then + self.isInit = true + self.value = self.getFunc(getArgs(self,"getArg",1)) + self.isInit = nil + end + slider:SetValue(self.value or 0) + slider:SetScript("OnValueChanged",Slider_OnValueChanged) + +end + +function WaterfallSlider.prototype:CleanUp() + self.frame:ClearAllPoints() + self.frame:Hide() + self.width = nil + self.height = nil + self.noNewLine = nil + self.causesRefresh = nil + self.fullRefresh = nil + self.treeRefresh = nil + self:SetText("") + self:SetFunc("disabled",nil) + self:SetFunc("get",nil) + self:SetFunc("set",nil) + self.min = nil + self.max = nil + self.step = nil + self.value = nil + self.parent = nil + self.disabled = nil +end + +function WaterfallSlider.prototype:SetText(text) + self.text:SetText(text) +end + +function WaterfallSlider.prototype:Refresh() + local frame = self.slider + frame.setup = true + frame:SetMinMaxValues(self.min or 0,self.max or 100) + self.lowtext:SetText(self.min or 0) + self.hightext:SetText(self.max or 100) + frame:SetValueStep(self.step or 1) + frame.setup = nil + if self.getFunc then + local newvalue + newvalue = self.getFunc(getArgs(self,"getArg",1)) + if newvalue ~= self.value then + self.isInit = true + frame:SetValue(newvalue) + self.isInit = nil + end + end + + if self.disabledFunc then + self.disabled = self.disabledFunc(getArgs(self,"disabledArg",1)) + if self.inverseDisabled then + self.disabled = not self.disabled + end + end + if self.disabled then + self.slider:EnableMouse(false) + self.text:SetTextColor(.5,.5,.5) + self.hightext:SetTextColor(.5,.5,.5) + self.lowtext:SetTextColor(.5,.5,.5) + self.valuetext:SetTextColor(.5,.5,.5) + else + self.slider:EnableMouse(true) + self.text:SetTextColor(1,1,1) + self.hightext:SetTextColor(1,1,1) + self.lowtext:SetTextColor(1,1,1) + self.valuetext:SetTextColor(1,1,1) + end + + if self.width then + self:SetWidth(self.width) + else + self:SetWidth(DEFAULT_CONTROL_WIDTH) + end +end + +------------------- +-- TextBox Class -- +------------------- + +local function TextBox_OnEscapePressed() + this:ClearFocus() +end + +local function TextBox_OnEnterPressed() + local self = this.obj + local value = this:GetText() + if self.toNumber then + value = tonumber(value) + --TODO: same message as a failed validation + if not value then return end + end + if self.setFunc and not self.disabled then + local validated = true + if type(self.validateFunc) == "function" then + validated = self.validateFunc(getArgs(self,"validateArg",1,value)) + if validated and self.validateModifies then + value = validated + local setOnTextChanged = self.setOnTextChanged + self.setOnTextChanged = nil + this:SetText(value) + self.setOnTextChanged = setOnTextChanged + end + elseif type(self.validateFunc) == "table" then + validated = self.validateFunc[value] + end + --TODO: message for failed validation + if validated then + if self.parseList then + self.setFunc(getArgs(self,"setArg",1,parseList(value))) + else + self.setFunc(getArgs(self,"setArg",1,value)) + end + end + if self.getFunc == false then + self:SetText("") + end + if self.causesRefresh or self.fullRefresh then + self.parent:Refresh(self.fullRefresh) + end + end +end + +local function TextBox_OnTextChanged() + local self = this.obj + local value = this:GetText() + if self.setOnTextChanged then + TextBox_OnEnterPressed() + end + if self.changedFunc and not self.disabled then + if self.parseList then + self.changedFunc(getArgs(self,"changedArg",1,parseList(value))) + else + self.changedFunc(getArgs(self,"changedArg",1,value)) + end + end +end + +function WaterfallTextBox.prototype:init() + WaterfallTextBox.super.prototype.init(self,"EditBox") + local frame = self.frame + --local text = frame:CreateFontString(nil,"OVERLAY","GameFontNormalSmall") + --self.text = text + frame:SetScript("OnEnter",Control_OnEnter) + frame:SetScript("OnLeave",Control_OnLeave) + frame:SetFontObject(ChatFontNormal) + frame:SetScript("OnEscapePressed",TextBox_OnEscapePressed) + frame:SetScript("OnEnterPressed",TextBox_OnEnterPressed) + frame:SetScript("OnTextChanged",TextBox_OnTextChanged) + frame:SetTextInsets(5,5,3,3) + frame:SetMaxLetters(256) + frame:SetAutoFocus(false) + frame:SetHeight(26) + frame:SetWidth(DEFAULT_CONTROL_WIDTH) + frame:SetBackdrop(ControlBackdrop) + frame:SetBackdropColor(0,0,0) +end + +local function UglyScrollLeft() + this:HighlightText(0,1); + this:Insert(" "..strsub(this:GetText(),1,1)); + this:HighlightText(0,1); + this:Insert(""); + this:SetScript("OnUpdate", nil); +end + +function WaterfallTextBox.prototype:SetText(text) + self.frame:SetText(text) + self.frame:SetScript("OnUpdate", UglyScrollLeft); +end + +function WaterfallTextBox.prototype:CleanUp() + self.frame:ClearAllPoints() + self.frame:Hide() + self.width = nil + self.height = nil + self.noNewLine = nil + self.causesRefresh = nil + self.fullRefresh = nil + self.treeRefresh = nil + self.setOnTextChanged = nil + self:SetText("") + self:SetFunc("disabled",nil) + self:SetFunc("get",nil) + self:SetFunc("set",nil) + self:SetFunc("changed",nil) +end + +function WaterfallTextBox.prototype:Refresh() + if self.getFunc then + local text = self.getFunc(getArgs(self,"getArg",1)) + if text then + self:SetText(text) + end + end + + if self.disabledFunc then + self.disabled = self.disabledFunc(getArgs(self,"disabledArg",1)) + if self.inverseDisabled then + self.disabled = not self.disabled + end + end + if self.disabled then + self.frame:EnableMouse(false) + self.frame:ClearFocus() + self.frame:SetTextColor(0.5,0.5,0.5) + else + self.frame:EnableMouse(true) + self.frame:SetTextColor(1,1,1) + end + if self.width then + self:SetWidth(self.width) + else + self:SetWidth(DEFAULT_CONTROL_WIDTH) + end +end + + +-------------------- +-- DropDown Class -- +-------------------- + +local function Dropdown_OnEnterPressed() + local self = this.obj + if self.setFunc and not self.disabled then + local ret + if self.strict and this.value then + ret = this.value + else + ret = this:GetText() + end + if self.parseList then + self.setFunc(getArgs(self,"setArg",1,parseList(ret))) + else + self.setFunc(getArgs(self,"setArg",1,ret)) + end + if self.causesRefresh or self.fullRefresh then + self.parent:Refresh(self.fullRefresh) + end + end +end + +local function Dropdown_TogglePullout() + local self = this.obj + if self.open then + self.open = nil + self.pullout:Hide() + else + self.open = true + self:BuildPullout() + if self.lines[1] and self.lines[1]:IsShown() then + self.pullout:Show() + end + end +end + +local function Dropdown_OnHide() + this.obj.pullout:Hide() +end + +local function Dropdown_LineClicked() + local self = this.obj + self.open = false + self.pullout:Hide() + self.editbox:SetText(this.text:GetText()) + self.editbox.value = this.value + Dropdown_OnEnterPressed(self.editbox) +end + +local function Dropdown_LineEnter() + this.highlight:Show() +end + +local function Dropdown_LineLeave() + this.highlight:Hide() +end + +function WaterfallDropdown.prototype:init() + WaterfallDropdown.super.prototype.init(self,"Frame") + local frame = self.frame + frame.obj = self + --local text = frame:CreateFontString(nil,"OVERLAY","GameFontNormalSmall") + --self.text = text + local editbox = CreateFrame("EditBox",nil,frame) + self.editbox = editbox + editbox.obj = self + editbox:SetFontObject(ChatFontNormal) + editbox:SetScript("OnEscapePressed",TextBox_OnEscapePressed) + editbox:SetScript("OnEnterPressed",Dropdown_OnEnterPressed) + frame:SetScript("OnEnter",Control_OnEnter) + frame:SetScript("OnLeave",Control_OnLeave) + editbox:SetScript("OnEnter",Control_OnEnter) + editbox:SetScript("OnLeave",Control_OnLeave) + editbox:SetTextInsets(5,5,3,3) + editbox:SetMaxLetters(256) + editbox:SetAutoFocus(false) + editbox:SetBackdrop(ControlBackdrop) + editbox:SetBackdropColor(0,0,0) + + editbox:SetPoint("TOPLEFT",frame,"TOPLEFT",0,0) + editbox:SetPoint("BOTTOMRIGHT",frame,"BOTTOMRIGHT",-20,0) + local button = CreateFrame("Button",nil,frame) + self.button = button + button.obj = self + button:SetWidth(24) + button:SetHeight(24) + button:SetScript("OnEnter",Control_OnEnter) + button:SetScript("OnLeave",Control_OnLeave) + button:SetNormalTexture("Interface\\ChatFrame\\UI-ChatIcon-ScrollDown-Up") + button:GetNormalTexture():SetTexCoord(.09,.91,.09,.91) + button:SetPushedTexture("Interface\\ChatFrame\\UI-ChatIcon-ScrollDown-Down") + button:GetPushedTexture():SetTexCoord(.09,.91,.09,.91) + button:SetDisabledTexture("Interface\\ChatFrame\\UI-ChatIcon-ScrollDown-Disabled") + button:GetDisabledTexture():SetTexCoord(.09,.91,.09,.91) + button:SetHighlightTexture("Interface\\Buttons\\UI-Common-MouseHilight", "ADD") + button:GetHighlightTexture():SetTexCoord(.09,.91,.09,.91) + button:SetPoint("RIGHT",frame,"RIGHT",0,0) + button:SetScript("OnClick",Dropdown_TogglePullout) + frame:SetHeight(26) + frame:SetWidth(DEFAULT_CONTROL_WIDTH) + frame:SetScript("OnHide",Dropdown_OnHide) + local pullout = CreateFrame("Frame",nil,UIParent) + self.pullout = pullout + frame:EnableMouse() + pullout:SetBackdrop(ControlBackdrop) + pullout:SetBackdropColor(0,0,0) + pullout:SetFrameStrata("DIALOG") + pullout:SetFrameLevel(20) + pullout:SetPoint("TOPLEFT",frame,"BOTTOMLEFT",0,0) + pullout:SetPoint("TOPRIGHT",frame,"BOTTOMRIGHT",-24,0) + pullout:SetClampedToScreen(true) + pullout:Hide() + + self.lines = {} +end + +function WaterfallDropdown.prototype:SetText(text) + self.editbox:SetText(text) +end + +function WaterfallDropdown.prototype:CleanUp() + self.frame:ClearAllPoints() + self.frame:Hide() + self.width = nil + self.height = nil + self.noNewLine = nil + self.causesRefresh = nil + self.fullRefresh = nil + self.treeRefresh = nil + self:SetText("") + self:SetFunc("disabled",nil) + self:SetFunc("get",nil) + self:SetFunc("set",nil) + self.editbox.value = nil + self.columns = nil +end + +function WaterfallDropdown.prototype:Refresh() + local list = self.list + if self.getFunc then + local newval = self.getFunc(getArgs(self,"getArg",1)) + if newval then + if self.strict then + if type(list) == "table" then + local value + for k, text in pairs(list) do + if type(k) == "string" then + value = k + else + value = text + end + if newval == value then + self:SetText(text) + self.editbox.value = value + break + end + end + elseif type(list) == "function" then + for i, text, value in list() do + if newval == value then + self:SetText(text) + self.editbox.value = value + break + end + end + end + else + self.editbox:SetText(newval) + self.editbox.value = newval + end + else + self.editbox:SetText("") + self.editbox.value = newval + end + end + + if self.disabledFunc then + self.disabled = self.disabledFunc(getArgs(self,"disabledArg",1)) + if self.inverseDisabled then + self.disabled = not self.disabled + end + end + if self.disabled then + self.editbox:EnableMouse(false) + self.editbox:ClearFocus() + self.editbox:SetTextColor(0.5,0.5,0.5) + self.button:Disable() + else + self.button:Enable() + if self.strict then + self.editbox:EnableMouse(false) + self.editbox:ClearFocus() + self.editbox:SetTextColor(1,1,1) + else + self.editbox:EnableMouse(true) + self.editbox:SetTextColor(1,1,1) + end + end + if self.width then + self:SetWidth(self.width) + else + self:SetWidth(DEFAULT_CONTROL_WIDTH) + end +end + +local function div(a,b) + return a - math.floor(a/b)*b +end + +local ddsort = {} +function WaterfallDropdown.prototype:BuildPullout() + local list = self.list + local lines = self.lines + local totalheight = 10 + self:ClearPullout() + if type(list) == "table" then + for k, v in pairs(list) do + tinsert(ddsort,k) + end + table.sort(ddsort) + local columns = tonumber(self.columns) + local columnHeight, columnWidth + local columnCount = 0 + self.pullout:ClearAllPoints() + if (columns) then + columnHeight = math.floor((table.getn(ddsort)) / columns) + 2 + columnWidth = self.pullout:GetWidth() + self.pullout:SetPoint("TOPLEFT",self.frame,"BOTTOMRIGHT", -1 * DEFAULT_CONTROL_WIDTH * columns - 24,0) + self.pullout:SetPoint("TOPRIGHT",self.frame,"BOTTOMRIGHT",-24,0) + else + self.pullout:SetPoint("TOPLEFT",self.frame,"BOTTOMRIGHT", -1 * DEFAULT_CONTROL_WIDTH - 24,0) + self.pullout:SetPoint("TOPRIGHT",self.frame,"BOTTOMRIGHT",-24,0) + columnHeight = table.getn(ddsort) + 1 + end + for i, value in pairs(ddsort) do + local text = list[value] + if (div((i - 1), (columnHeight - 1)) == 0) then + columnCount = columnCount + 1 + end + if (lines[i]) then + self:UpdateLine(lines[i], math.mod(i-1, (columnHeight-1)), columnCount) + else + lines[i] = self:CreateLine(math.mod(i-1, (columnHeight-1)), columnCount) + end + if i == 1 then + lines[i]:SetPoint("TOP",self.pullout,"TOP",0,-5) + else + if (columns and (div((i - 1), columnHeight) == 0)) then + lines[i]:SetPoint("TOP",self.pullout,"TOP",0,-5) + else + lines[i]:SetPoint("TOP",lines[i-1],"BOTTOM",0,0) + end + end + lines[i].text:SetText(text) + if type(value) == "string" then + lines[i].value = value + else + lines[i].value = text + end + if lines[i].value == self.editbox.value and self.getFunc then + lines[i].check:Show() + else + lines[i].check:Hide() + end + lines[i]:Show() + end + totalheight = totalheight + 17 * (columnHeight-1) + for k in pairs(ddsort) do + ddsort[k] = nil + end + table_setn(ddsort,0) + elseif type(list) == "function" then + for i, text, value in list() do + if not lines[i] then + lines[i] = self:CreateLine() + if i == 1 then + lines[i]:SetPoint("TOP",self.pullout,"TOP",0,-5) + else + lines[i]:SetPoint("TOP",lines[i-1],"BOTTOM",0,0) + end + end + lines[i].text:SetText(text) + lines[i].value = value + lines[i]:Show() + totalheight = totalheight + 17 + end + end + self.pullout:SetHeight(totalheight) +end + +function WaterfallDropdown.prototype:ClearPullout() + if self.lines then + for i, line in ipairs(self.lines) do + line.text:SetText("") + line:Hide() + end + end + self.pullout:SetHeight(10) + self.pullout:SetWidth(DEFAULT_CONTROL_WIDTH) +end + +-- For large list, if specified, 0 based row, 1 based column gives a grid. +function WaterfallDropdown.prototype:CreateLine(row, column) + local frame = CreateFrame("Button",nil,self.pullout) + frame.obj = self + + frame.text = frame:CreateFontString(nil,"OVERLAY","GameFontNormalSmall") + frame.text:SetTextColor(1,1,1) + frame.text:SetJustifyH("LEFT") + + frame:SetFrameLevel(25) + frame:SetFrameStrata("DIALOG") + + local highlight = frame:CreateTexture(nil, "OVERLAY") + frame.highlight = highlight + highlight:SetTexture("Interface\\QuestFrame\\UI-QuestTitleHighlight") + highlight:SetBlendMode("ADD") + + local check = frame:CreateTexture("OVERLAY") + frame.check = check + check:SetWidth(16) + check:SetHeight(16) + check:SetTexture("Interface\\Buttons\\UI-CheckBox-Check") + + frame:SetScript("OnClick",Dropdown_LineClicked) + frame:SetScript("OnEnter",Dropdown_LineEnter) + frame:SetScript("OnLeave",Dropdown_LineLeave) + return self:UpdateLine(frame, row, column) +end + + +function WaterfallDropdown.prototype:UpdateLine(frame, row, column) + if (column) then + frame:SetPoint("TOPLEFT",self.pullout,"TOPLEFT",6 + (column - 1) * DEFAULT_CONTROL_WIDTH,-5 - row*17) + frame:SetPoint("BOTTOMRIGHT",self.pullout,"TOPLEFT",-6 + column * DEFAULT_CONTROL_WIDTH,-5 - (row+1)*17) + else + frame:SetPoint("LEFT",self.pullout,"LEFT",6,0) + frame:SetPoint("RIGHT",self.pullout,"RIGHT",-6,0) + end + + frame.text:SetPoint("TOPLEFT",frame,"TOPLEFT",16,0) + frame.text:SetPoint("BOTTOMRIGHT",frame,"BOTTOMRIGHT",0,0) + + frame.highlight:SetAllPoints(frame) + frame.highlight:Hide() + + frame.check:SetPoint("LEFT",frame,"LEFT",0,0) + + return frame +end + +----------------------------- +-- WaterfallTreeView Class -- +----------------------------- + +local function treeviewOnSizeChanged() + this.obj:SizeChanged() +end +local function treeviewOnMouseWheel() + this.obj:MoveScroll(arg1) +end +local function treeviewOnScrollValueChanged() + this.obj:SetScroll(arg1) +end +local function treeviewSizerOnMouseUp() + this:GetParent():StopMovingOrSizing() this.obj.parent:ReAnchorTree() +end + +function WaterfallTreeView.prototype:init(parent) + WaterfallTreeView.super.prototype.init(self) + --Parent WaterfallFrame Object + self.parent = parent + local frame = CreateFrame("Frame",nil,parent.frame) + frame.obj = self + local scrollframe = CreateFrame("ScrollFrame",nil,frame) + local scrollchild = CreateFrame("Frame",nil,scrollframe) + local scrollbar = CreateFrame("Slider",nil,scrollframe,"UIPanelScrollBarTemplate") + frame:SetBackdrop(ControlBackdrop) + frame:SetBackdropBorderColor(0.6, 0.6, 0.6) + frame:SetBackdropColor(0.1, 0.1, 0.1) + frame:SetScript("OnSizeChanged", treeviewOnSizeChanged) + frame:SetFrameLevel(2) + frame:SetFrameStrata("DIALOG") + frame:SetResizable(true) + + self.scrollframe = scrollframe + self.scrollchild = scrollchild + self.frame = frame + self.scrollbar = scrollbar + + scrollframe:SetScrollChild(scrollchild) + scrollframe:SetPoint("TOPLEFT",frame,"TOPLEFT",8,-12) + scrollframe:SetPoint("BOTTOMRIGHT",frame,"BOTTOMRIGHT",-28,12) + scrollframe:EnableMouseWheel(true) + scrollframe:SetScript("OnMouseWheel", treeviewOnMouseWheel) + scrollframe.obj = self + + scrollchild:SetPoint("TOPLEFT",scrollframe,"TOPLEFT",0,0) + scrollchild:SetPoint("TOPRIGHT",scrollframe,"TOPRIGHT",0,0) + scrollchild:SetHeight(400) + + scrollbar:SetPoint("TOPLEFT",scrollframe,"TOPRIGHT",0,-16) + scrollbar:SetPoint("BOTTOMLEFT",scrollframe,"BOTTOMRIGHT",0,16) + scrollbar:SetMinMaxValues(0,1000) + scrollbar:SetValueStep(1) + scrollbar:SetValue(0) + scrollbar:SetWidth(16) + scrollbar:SetScript("OnValueChanged", treeviewOnScrollValueChanged) + self.scrollvalue = 0 + scrollchild.lines = {} + self.linetables = {} + self.sections = {} + scrollbar.obj = self + + local sizer = CreateFrame("Frame",nil,frame) + sizer:SetPoint("TOPRIGHT",frame,"TOPRIGHT",0,0) + sizer:SetPoint("BOTTOMRIGHT",frame,"BOTTOMRIGHT",0,0) + sizer:SetWidth(7) + sizer:EnableMouse() + sizer:SetScript("OnMouseDown",sizereOnMouseDown) + sizer:SetScript("OnMouseUp", treeviewSizerOnMouseUp) + self.sizer = sizer + sizer.obj = self + + self:Refresh() +end + +function WaterfallTreeView.prototype:SetScroll(value) + local frame, child = self.scrollframe, self.scrollchild + local viewheight = frame:GetHeight() + local height = child:GetHeight() + local offset + if viewheight > height then + offset = 0 + else + offset = floor((height - viewheight) / 1000.0 * value) + end + child:ClearAllPoints() + child:SetPoint("TOPLEFT",frame,"TOPLEFT",0,offset) + child:SetPoint("TOPRIGHT",frame,"TOPRIGHT",0,offset) + child.offset = offset + + self.scrollvalue = value +end + +function WaterfallTreeView.prototype:SizeChanged() + self:FixScroll() + self.parent:ReAnchorTree() +end + +function WaterfallTreeView.prototype:MoveScroll(value) + local frame, child = self.scrollframe, self.scrollchild + local height, viewheight = frame:GetHeight(), child:GetHeight() + if height > viewheight then + self.scrollbar:Hide() + else + self.scrollbar:Show() + local diff = height - viewheight + local delta = 1 + if value < 0 then + delta = -1 + end + self.scrollbar:SetValue(math.min(math.max(self.scrollvalue + delta*(1000/(diff/45)),0), 1000)) + end +end + +function WaterfallTreeView.prototype:FixScroll() + if self.noFixScroll then return end + local frame, child = self.scrollframe, self.scrollchild + local height, viewheight = frame:GetHeight(), child:GetHeight() + local offset = self.scrollchild.offset + if not offset then + offset = 0 + end + local curvalue = self.scrollbar:GetValue() + + if viewheight < height then + self.scrollbar:Hide() + self.scrollbar:SetValue(0) + else + self.scrollbar:Show() + local value = (offset / (viewheight - height) * 1000) + if value > 1000 then value = 1000 end + self.scrollbar:SetValue(value) + self:SetScroll(value) + if value < 1000 then + child:ClearAllPoints() + child:SetPoint("TOPLEFT",frame,"TOPLEFT",0,offset) + child:SetPoint("TOPRIGHT",frame,"TOPRIGHT",0,offset) + child.offset = offset + end + end +end + +function WaterfallTreeView.prototype:ToggleExpand(id) + self.status[id] = not self.status[id] + self:Refresh() +end + +function WaterfallTreeView.prototype:Refresh(noupdate) + self.noFixScroll = true + self:Clear() + if not self.contents then return end + if self.contentsFunc and not noupdate then + self.parent.controlcount = 0 + currentframe = self.parent + self.contentsFunc(self.contents, self.status, getArgs(self, "contentsArg", 1)) + currentframe = nil + end + if self.type == "SECTIONS" then + for i, section in ipairs(self.contents) do + if not section.hidden then + self:AddSection(i,section) + end + end + local height = 0 + for i, section in ipairs(self.sections) do + height = height + section:GetHeight() + end + self.scrollchild:SetHeight(height+3) + else + local level = 0 + local index = 1 + for i, line in ipairs(self.contents) do + if not line.hidden then + index = self:AddLine(index,line,level) + end + end + self.scrollchild:SetHeight(index*16) + end + self.noFixScroll = nil + self:FixScroll() +end + +local function isSectionSelected(section, selected) + if not selected then return end + if section.id == selected then + return true + end + for i,v in ipairs(section.lines) do + if i <= section.numlines then + if v.id == selected then + return true + end + end + end +end + +function WaterfallTreeView.prototype:Collapse() + for k in pairs(self.status) do + self.status[k] = false + end +end + +function WaterfallTreeView.prototype:AddSection(index,line) + local sections = self.sections + local id = line.id + if not sections[index] then + sections[index] = getObj(WaterfallTreeSection,self) + local section = sections[index] + section:SetParent(self) + if index == 1 then + section:SetPoint("TOPLEFT",self.scrollchild,"TOPLEFT",0,0) + section:SetPoint("TOPRIGHT",self.scrollchild,"TOPRIGHT",0,0) + else + section:SetPoint("TOPLEFT",sections[index-1],"BOTTOMLEFT",0,0) + section:SetPoint("TOPRIGHT",sections[index-1],"BOTTOMRIGHT",0,0) + end + end + local section = sections[index] + section:SetText(line.text) + section:SetID(line.id) + section:SetTooltipText(line.tooltipTitle,line.tooltipText) + if self.status[id] == nil then + self.status[id] = not not line.isOpen + end + + local handler = line.handler + local disabled = checkNegatable(line.disabled,handler) + section.disabled = disabled + section:SetColor(self.parent:GetColor()) + if line.hasOptions then + section:AddLine(line.text,line.id,line.id == self.selected,disabled,line.tooltipTitle,line.tooltipText) + end + local firstid + for i, item in ipairs(line) do + if not item.hidden then + if not firstid then + firstid = item.id + end + local itemdisabled = checkNegatable(item.disabled,handler) + section:AddLine(item.text,item.id,item.id == self.selected,itemdisabled or disabled,item.tooltipTitle,item.tooltipText) + end + end + if self.selected == section.id and not line.hasOptions then + self:SetSelected(firstid) + return + end + + if isSectionSelected(section, self.selected) then + self.status[section.id] = true + end + section:SetExpanded(self.status[line.id] and not disabled) + section:Show() +end + +function WaterfallTreeView.prototype:Clear() + for i, frame in ipairs(self.scrollchild.lines) do + releaseObj(frame) + self.scrollchild.lines[i] = nil + end + for k in pairs(self.linetables) do + self.linetables[k] = nil + end + for i, section in ipairs(self.sections) do + releaseObj(section) + self.sections[i] = nil + end + self.numlines = 0 + self.scrollchild:SetHeight(1) + self:FixScroll() +end + +function WaterfallTreeView.prototype:ClearSelection() + self.selected = nil +end + +function WaterfallTreeView.prototype:GetSelected() + return self.selected +end + +function WaterfallTreeView.prototype:SetSelected(lineid, noupdate) + self.selected = lineid + self:Refresh(noupdate) + if not noupdate then + self.parent:ChangePane(lineid) + end +end + +function WaterfallTreeView.prototype:AddLine(index,line,level,disabled) + local lines = self.scrollchild.lines + local text, hasChildren, isSelected, isOpen + text = line.text or "" + hasChildren = table.getn(line) > 0 + local id = line.id + if self.status[id] == nil then + self.status[id] = not not line.isOpen + end + isOpen = self.status[id] and not disabled + isSelected = self.selected == id + if not lines[index] then + lines[index] = getObj(WaterfallTreeLine) + lines[index]:SetParent(self) + end + local disabled + if type(line.disabled) == "function" then + disabled = line.disabled() + else + disabled = line.disabled + end + lines[index]:Setup(index,id,text,level,hasChildren,isSelected,isOpen,disabled) + lines[index]:SetTooltipText(line.tooltipTitle,line.tooltipText) + lines[index]:SetIcon(line.icon) + self.linetables[index] = line + index = index + 1 + if isOpen then + for i, childline in ipairs(line) do + if not childline.hidden then + index = self:AddLine(index,childline,level+1) + end + end + end + + return index +end + +function WaterfallTreeView.prototype:SetType(type) + if not type then + self.type = "TREE" + else + self.type = type + end +end + +function WaterfallTreeView.prototype:SetContent(contents, status, func, ...) + self.contents = contents + self.status = status + self.contentsFunc = func + local i = 1 + while self["contentsArg"..i] do + self["contentsArg"..i] = nil + i = i + 1 + end + for i = 1 , table.getn(arg) do + self["contentsArg"..i] = arg[i] + end + self:Refresh() + self:FixScroll() +end + +function WaterfallTreeView.prototype:CleanUp() + self.contents = nil + self.selected = nil + self.contentsFunc = nil + local i = 1 + while self["contentsArg"..i] do + self["contentsArg"..i] = nil + i = i + 1 + end + self.selected = nil + self:Refresh() +end + +-------------------- +-- WaterfallTreeLine Class -- +-------------------- + +local function TreeLine_OnEnter() + if not this.obj.disabled then + this.highlight:SetAlpha(1) + this.highlight:Show() + showGameTooltip(this.obj) + end +end + +local function TreeLine_OnLeave() + if not this.obj.isSelected then + this.highlight:Hide() + else + this.highlight:SetAlpha(0.7) + end + GameTooltip:Hide() +end + +local function TreeLine_OnClick() + + if not this.obj.disabled then + this.obj.parent:SetSelected(this.obj.id) + end +end + +local function TreeLine_OnExpandClick() + + if not this.obj.disabled then + this.obj.parent:ToggleExpand(this.obj.id) + end +end + +function WaterfallTreeLine.prototype:init() + WaterfallTreeLine.super.prototype.init(self) + local frame = CreateFrame("Button",nil,nil) + self.frame = frame + local text = frame:CreateFontString(nil,"OVERLAY","GameFontNormal") + + text:SetJustifyH("LEFT") + text:SetTextColor(1,1,1) + frame:SetHeight(16) + frame:EnableMouse(true) + frame:SetScript("OnEnter",TreeLine_OnEnter) + frame:SetScript("OnLeave",TreeLine_OnLeave) + frame:SetScript("OnClick",TreeLine_OnClick) + frame:SetFrameLevel(10) + frame:SetFrameStrata("DIALOG") + + local highlight = frame:CreateTexture(nil, "BACKGROUND") + highlight:SetTexture("Interface\\QuestFrame\\UI-QuestTitleHighlight") + highlight:SetBlendMode("ADD") + highlight:SetAllPoints(frame) + highlight:Hide() + + local expand = CreateFrame("Button",nil,frame) + expand.obj = self + expand:SetNormalTexture("Interface\\Buttons\\UI-PlusButton-Up") + expand:SetPushedTexture("Interface\\Buttons\\UI-PlusButton-Down") + expand:SetDisabledTexture("Interface\\Buttons\\UI-PlusButton-Disabled") + expand:SetHighlightTexture("Interface\\Buttons\\UI-PlusButton-Hilight") + expand:SetScript("OnClick",TreeLine_OnExpandClick) + expand:SetFrameLevel(21) + expand:SetWidth(16) + expand:SetHeight(16) + + local icon = frame:CreateTexture(nil,"OVERLAY") + icon:SetWidth(16) + icon:SetHeight(16) + icon:SetPoint("LEFT",expand,"RIGHT",1,0) + self.icon = icon + + + expand:SetPoint("LEFT",frame,"LEFT",0,0) + text:SetPoint("LEFT",icon,"RIGHT",2,0) + text:SetPoint("RIGHT",frame,"RIGHT",0,0) + + frame.obj = self + frame.text = text + frame.highlight = highlight + frame.expand = expand + self.expand = expand + self.highlight = highlight + self.text = text +end + +function WaterfallTreeLine.prototype:UpdateExpandButton() + local expand = self.expand + if self.hasChildren then + if self.isOpen then + expand:SetNormalTexture("Interface\\Buttons\\UI-MinusButton-Up") + expand:SetPushedTexture("Interface\\Buttons\\UI-MinusButton-Down") + expand:SetDisabledTexture("Interface\\Buttons\\UI-MinusButton-Disabled") + expand:Enable() + expand:SetWidth(16) + else + if self.disabled then + expand:Disable() + else + expand:Enable() + end + expand:SetNormalTexture("Interface\\Buttons\\UI-PlusButton-Up") + expand:SetPushedTexture("Interface\\Buttons\\UI-PlusButton-Down") + expand:SetDisabledTexture("Interface\\Buttons\\UI-PlusButton-Disabled") + + expand:SetWidth(16) + end + else + expand:SetNormalTexture(nil) + expand:SetPushedTexture(nil) + expand:SetDisabledTexture(nil) + expand:Disable() + expand:SetWidth(16) + end +end + +function WaterfallTreeLine.prototype:CleanUp() + self:ClearAllPoints() + self:Hide() + self:SetText("") + self.disabled = nil + self.handler = nil + self.name = nil +end + +function WaterfallTreeLine.prototype:Setup(index, id, text, level, hasChildren, isSelected, isOpen, disabled) + self.level = level + self.hasChildren = hasChildren + self.isSelected = isSelected + self.isOpen = isOpen and not disabled + self.index = index + self.id = id + self.disabled = disabled + + self:UpdateExpandButton() + + self.expand:SetPoint("LEFT",self.frame,"LEFT",level*8,0) + self:SetText(text) + if disabled then + self.text:SetTextColor(0.5,0.5,0.5) + else + self.text:SetTextColor(1,1,1) + end + self.frame:SetPoint("TOPLEFT",self.parent.scrollchild,"TOPLEFT",0,-(index-1)*16) + self.frame:SetPoint("TOPRIGHT",self.parent.scrollchild,"TOPRIGHT",0,-(index-1)*16) + if self.isSelected then + self.highlight:Show() + self.highlight:SetAlpha(0.7) + else + self.highlight:Hide() + end + self.frame:Show() +end + +function WaterfallTreeLine.prototype:SetIcon(iconpath) + local icon = self.icon + if iconpath then + icon:SetTexture(iconpath) + icon:SetWidth(16) + else + icon:SetTexture(nil) + icon:SetWidth(1) + end +end + +function WaterfallTreeLine.prototype:SetText(text) + self.text:SetText(text) +end + +function WaterfallTreeLine.prototype:SetTooltipText(title,text) + self.tooltipTitle = title + self.tooltipText = text +end + +function WaterfallTreeLine.prototype:SetParent(parent) + self.parent = parent + self.frame.parent = parent + self.frame:SetParent(parent.scrollchild) +end + +----------------------- +-- TreeSection Class -- +----------------------- + +local function SectionTitle_OnClick() + local self = this.obj + local parent = self.parent + if not self.disabled then + local selected = self.parent.selected + local lines = self.lines + if self.expanded then + --search before changing pane, since it will release and reinit this section + local found + for i = 1, self.numlines do + if lines[i].id == selected then + found = true + end + end + --if expanded close + + -- if one of our children is selected then clear the pane + if found then + parent.selected = nil + parent:ToggleExpand(self.id) + parent:SetSelected(nil) + else + parent:ToggleExpand(self.id) + end + else + --save before changing pane, since it will release and reinit this section + local newid + if lines[1] then + newid = lines[1].id + end + parent:ToggleExpand(self.id) + if newid and not parent.selected then + parent:SetSelected(newid) + end + end + end +end + +local function SectionTitle_OnEnter() + local self = this.obj + if not self.disabled then + self.hover = true + self:UpdateColor() + end + showGameTooltip(self) +end + +local function SectionTitle_OnLeave() + local self = this.obj + self.hover = false + self:UpdateColor() + GameTooltip:Hide() +end + +local function SectionItem_OnEnter() + if not this.disabled then + this.highlight:SetAlpha(1) + this.highlight:Show() + showGameTooltip() + end +end + +local function SectionItem_OnClick() + + if not this.disabled then + this.obj.parent:SetSelected(this.id) + end +end + +local function SectionItem_OnLeave() + if not this.isSelected then + this.highlight:Hide() + else + this.highlight:SetAlpha(0.7) + end + GameTooltip:Hide() +end + +function WaterfallTreeSection.prototype:init(parent) + WaterfallTreeSection.super.prototype.init(self) + + self.parent = parent + local frame = CreateFrame("Frame",nil,parent.scrollchild) + frame.obj = self + self.frame = frame + frame:SetBackdrop(PaneBackdrop) + frame:SetBackdropBorderColor(0.6, 0.6, 0.6) + frame:SetBackdropColor(0.1, 0.1, 0.1) + frame:SetFrameLevel(10) + frame:SetFrameStrata("DIALOG") + + local titletext = frame:CreateFontString(nil,"OVERLAY","GameFontNormal") + self.titletext = titletext + titletext:SetPoint("TOPLEFT",frame,"TOPLEFT",0,-5) + titletext:SetPoint("TOPRIGHT",frame,"TOPRIGHT",0,-5) + titletext:SetHeight(16) + titletext:SetText("Section") + titletext:SetTextColor(1,1,1) + + local arrow = frame:CreateTexture(nil,"OVERLAY") + self.arrow = arrow + arrow:SetPoint("TOPRIGHT",frame,"TOPRIGHT",-6,-4) + arrow:SetWidth(20) + arrow:SetHeight(20) + arrow:SetTexture("Interface\\MiniMap\\ROTATING-MINIMAPARROW") + + local r,g,b = 1, 0.6, 0 + self.colorR = r + self.colorG = g + self.colorB = b + local titlebar = frame:CreateTexture(nil,"BACKGROUND") + self.titlebar = titlebar + local titlebar2 = frame:CreateTexture(nil,"BACKGROUND") + self.titlebar2 = titlebar2 + titlebar:SetPoint("TOPLEFT",frame,"TOPLEFT",4,-4) + titlebar:SetPoint("TOPRIGHT",frame,"TOPRIGHT",-4,-4) + titlebar:SetHeight(10) + titlebar2:SetPoint("TOPLEFT",titlebar,"BOTTOMLEFT",0,0) + titlebar2:SetPoint("TOPRIGHT",titlebar,"BOTTOMRIGHT",0,0) + titlebar2:SetHeight(10) + + titlebar:SetGradientAlpha("VERTICAL",r*0.6,g*0.6,b*0.6,1,r,g,b,1) + titlebar:SetTexture(r,g,b,1) + titlebar2:SetGradientAlpha("VERTICAL",r*0.9,g*0.9,b*0.9,1,r*0.6,g*0.6,b*0.6,1) + titlebar2:SetTexture(r,g,b,1) + + local title = CreateFrame("Button",nil,frame) + self.title = title + title:EnableMouse() + title.obj = self + title:SetScript("OnClick", SectionTitle_OnClick) + title:SetScript("OnEnter", SectionTitle_OnEnter) + title:SetScript("OnLeave", SectionTitle_OnLeave) + title:SetPoint("TOPLEFT",titlebar,"TOPLEFT",0,0) + title:SetPoint("BOTTOMRIGHT",titlebar2,"BOTTOMRIGHT",0,0) + + self.lines = {} + self.numlines = 0 + + frame:SetWidth(100) + frame:SetHeight(100) + self.expanded = true + + + + frame:SetPoint("CENTER",UIParent,"CENTER") +end + +function WaterfallTreeSection.prototype:SetParent(parent) + self.parent = parent + self.frame:SetParent(parent.scrollchild) +end + +function WaterfallTreeSection.prototype:UpdateColor() + local r,g,b + self.arrow:Show() + if self.disabled then + r,g,b = 0.5,0.5,0.5 + self.arrow:Hide() + elseif self.hover then + r,g,b = self.colorR+((1-self.colorR)/2), self.colorG+((1-self.colorG)/2), self.colorB+((1-self.colorB)/2) + else + r,g,b = self.colorR, self.colorG, self.colorB + end + + local titlebar = self.titlebar + local titlebar2 = self.titlebar2 + titlebar:SetGradientAlpha("VERTICAL",r*0.6,g*0.6,b*0.6,1,r,g,b,1) + titlebar:SetTexture(r,g,b,1) + titlebar2:SetGradientAlpha("VERTICAL",r*0.9,g*0.9,b*0.9,1,r*0.6,g*0.6,b*0.6,1) + titlebar2:SetTexture(r,g,b,1) +end + +function WaterfallTreeSection.prototype:SetColor(r,g,b) + if r and g and b then + self.colorR, self.colorG, self.colorB = r,g,b + end + self:UpdateColor() +end + +function WaterfallTreeSection.prototype:ToggleExpand() + self:SetExpanded(not self.expanded) +end + +function WaterfallTreeSection.prototype:SetText(text) + self.titletext:SetText(text) +end + + +function WaterfallTreeSection.prototype:SetTooltipText(title,text) + self.tooltipTitle = title + self.tooltipText = text +end + +function WaterfallTreeSection.prototype:SetID(id) + self.id = id +end + +function WaterfallTreeSection.prototype:AddLine(text,id,selected,disabled,tooltipTitle,tooltipText) + local numlines = self.numlines + 1 + self.numlines = numlines + local lines = self.lines + if not lines[numlines] then + self:CreateLine(numlines) + end + local line = lines[numlines] + + line.text:SetText(text) + line.id = id + line.disabled = disabled + line.isSelected = selected + line.tooltipTitle = tooltipTitle + line.tooltipText = tooltipText + if selected then + line.highlight:SetAlpha(0.7) + line.highlight:Show() + else + line.highlight:Hide() + end + if disabled then + line.text:SetTextColor(0.5,0.5,0.5) + else + line.text:SetTextColor(1,1,1) + end + line:Show() + self:FixHeight() +end + +function WaterfallTreeSection.prototype:SetExpanded(expanded) + self.expanded = expanded + local lines = self.lines + if self.expanded then + for i = 1, self.numlines do + lines[i]:Show() + end + self.arrow:SetTexCoord(.18,.85,.15,.82) + else + for i = 1, table.getn(lines) do + lines[i]:Hide() + end + self.arrow:SetTexCoord(.85,.18,.82,.15) + end + self:FixHeight() +end + +function WaterfallTreeSection.prototype:FixHeight() + if self.expanded then + self.frame:SetHeight(33 + self.numlines * 18) + else + self.frame:SetHeight(28) + end +end + +function WaterfallTreeSection.prototype:CreateLine(index) + local frame = self.frame + self.lines[index] = CreateFrame("Button",nil,frame) + local line = self.lines[index] + line.obj = self + + line:SetScript("OnEnter",SectionItem_OnEnter) + line:SetScript("OnLeave",SectionItem_OnLeave) + line:SetScript("OnClick",SectionItem_OnClick) + + line:SetPoint("LEFT",frame,"LEFT",5,0) + line:SetPoint("RIGHT",frame,"RIGHT",-5,0) + line:SetHeight(16) + if index == 1 then + line:SetPoint("TOP",frame,"TOP",0,-28) + else + line:SetPoint("TOP",self.lines[index-1],"BOTTOM",0,-2) + end + + local text = line:CreateFontString(nil,"OVERLAY","GameFontNormal") + line.text = text + text:SetAllPoints(line) + text:SetTextColor(1,1,1) + + local highlight = line:CreateTexture(nil, "BACKGROUND") + line.highlight = highlight + highlight:SetTexture("Interface\\QuestFrame\\UI-QuestTitleHighlight") + highlight:SetBlendMode("ADD") + highlight:SetAllPoints(line) + highlight:Hide() +end + +function WaterfallTreeSection.prototype:CleanUp() + for i = 1, table.getn(self.lines) do + self.lines[i]:Hide() + end + self.numlines = 0 + self.frame:Hide() + self.frame:ClearAllPoints() + self.id = nil + self.disabled = nil + self.colorR = 1 + self.colorG = 0.6 + self.colorB = 0 +end + +local function activate(self, oldLib, oldDeactivate) + if oldLib and oldLib.registry then + self.registry = oldLib.registry + else + self.registry = {} + end + if oldDeactivate then + oldDeactivate(oldLib) + end +end + +AceLibrary:Register(Waterfall, MAJOR_VERSION, MINOR_VERSION, activate) +Waterfall = AceLibrary("Waterfall-1.0") diff --git a/modules/Buttons.lua b/modules/Buttons.lua new file mode 100755 index 0000000..795c38b --- /dev/null +++ b/modules/Buttons.lua @@ -0,0 +1,268 @@ +--[[ +Name: Prat_Buttons +Revision: $Revision: 16019 $ +Author(s): Curney (asml8ed@gmail.com) + Krtek (krtek4@gmail.com) +Inspired by: idChat2_Buttons by Industrial +Website: http://www.wowace.com/files/index.php?path=Prat/ +Documentation: http://www.wowace.com/wiki/Prat/Integrated_Modules#Buttons +SVN: http://svn.wowace.com/wowace/trunk/Prat/ +Description: Module for Prat that toggles the chat menu and chat window buttons on and off (default=off). +Dependencies: Prat +]] + +local L = AceLibrary("AceLocale-2.2"):new("PratButtons") + +L:RegisterTranslations("enUS", function() return { + ["Buttons"] = true, + ["Chat window menu and buttons options."] = true, + ["ChatMenu"] = true, + ["Toggles chat menu on and off."] = true, + ["ChatButtons"] = true, + ["Toggle chat windows buttons on and off."] = true, + ["Chat%dButtons"] = true, + ["Toggles buttons on and off for chat window %d."] = true, + ["Show Reminder Button"] = true, + ["Show reminder button when not at bottom of chat window."] = true, + ["Toggle"] = true, + ["Toggle the module on and off."] = true, +} end) + +L:RegisterTranslations("ruRU", function() return { + ["Buttons"] = "Кнопки", + ["Chat window menu and buttons options."] = "Настройки кнопок меню и окна чата.", + ["ChatMenu"] = "ЧатМеню", + ["Toggles chat menu on and off."] = "Вкл/выкл кнопки чата.", + ["ChatButtons"] = "ЧатКнопка", + ["Toggle chat windows buttons on and off."] = "Вкл/Выкл кнопок окна чата", + ["Chat%dButtons"] = "ЧатКнопка%d", + ["Toggles buttons on and off for chat window %d."] = "Вкл/Выкл кнопки окна чата %d.", + ["Show Reminder Button"] = "Показать кнопку напоминания", + ["Show reminder button when not at bottom of chat window."] = "Показать кнопку напоминания не в нижней части окна.", + ["Toggle"] = "Вкл/Выкл", + ["Toggle the module on and off."] = "Вкл/Выкл модуль", +} end) + +L:RegisterTranslations("zhCN", function() return { + ["Buttons"] = "按钮", + ["Chat window menu and buttons options."] = "聊天窗口菜单及按钮选项。", + ["ChatMenu"] = "聊天菜单", + ["Toggles chat menu on and off."] = "显示聊天菜单打开与关闭。", + ["ChatButtons"] = "聊天按钮", + ["Toggle chat windows buttons on and off."] = "切换显示聊天按钮打开与关闭。", + ["Chat%dButtons"] = "聊天%d按钮", + ["Toggles buttons on and off for chat window %d."] = "显示第 %d 聊天窗口按钮。", + ["Show Reminder Button"] = "显示提示按钮", + ["Show reminder button when not at bottom of chat window."] = "聊天窗口不在底部时显示提示按钮。", + ["Toggle"] = "切换", + ["Toggle the module on and off."] = "切换此模块打开与关闭。", +} end) + +L:RegisterTranslations("koKR", function() return { + ["Buttons"] = "버튼", + ["Chat window menu and buttons options."] = "대화창 메뉴와 버튼을 설정합니다.", + ["ChatMenu"] = "대화 메뉴", + ["Toggles chat menu on and off."] = "대화 메뉴 켜고 끄기를 전환합니다.", + ["ChatButtons"] = "대화 버튼", + ["Toggle chat windows buttons on and off."] = "대화창 버튼 켜고 끄기를 전환합니다.", + ["Chat%dButtons"] = "대화창%d 버튼", + ["Toggles buttons on and off for chat window %d."] = "대화창%d에 대한 버튼을 켜고 끄기를 전환합니다.", + ["Show Reminder Button"] = "Show Reminder Button", + ["Show reminder button when not at bottom of chat window."] = "Show reminder button when not at bottom of chat window.", + ["Toggle"] = "전환", + ["Toggle the module on and off."] = "모듈 켜고 끄기를 전환합니다.", +} end) + +Prat_Buttons = Prat:NewModule("buttons") + +function Prat_Buttons:OnInitialize() + self.db = Prat:AcquireDBNamespace("Buttons") + Prat:RegisterDefaults("Buttons", "profile", { + on = true, + chatmenu = false, + chatbutton = {false, false, false, false, false, false, false}, + showreminder = false + }) + Prat.Options.args.buttons = { + name = L["Buttons"], + desc = L["Chat window menu and buttons options."], + type = "group", + args = { + chatmenu = { + name = L["ChatMenu"], + desc = L["Toggles chat menu on and off."], + type = "toggle", + order = 150, + get = function() return self.db.profile.chatmenu end, + set = function(v) + self.db.profile.chatmenu = v + self:ChatMenu(v) + end + }, + chatbuttons = { + name = L["ChatButtons"], + desc = L["Toggle chat windows buttons on and off."], + type = "group", + order = 180, + args = { + chat1 = { + name = string.format(L["Chat%dButtons"], 1), + desc = string.format(L["Toggles buttons on and off for chat window %d."], 1), + type = "toggle", + get = function() return self.db.profile.chatbutton[1] end, + set = function(v) + self.db.profile.chatbutton[1] = v + self:ChatButton(1,v) + end + }, + chat2 = { + name = string.format(L["Chat%dButtons"], 2), + desc = string.format(L["Toggles buttons on and off for chat window %d."], 2), + type = "toggle", + get = function() return self.db.profile.chatbutton[2] end, + set = function(v) + self.db.profile.chatbutton[2] = v + self:ChatButton(2,v) + end + }, + chat3 = { + name = string.format(L["Chat%dButtons"], 3), + desc = string.format(L["Toggles buttons on and off for chat window %d."], 3), + type = "toggle", + get = function() return self.db.profile.chatbutton[3] end, + set = function(v) + self.db.profile.chatbutton[3] = v + self:ChatButton(3,v) + end + }, + chat4 = { + name = string.format(L["Chat%dButtons"], 4), + desc = string.format(L["Toggles buttons on and off for chat window %d."], 4), + type = "toggle", + get = function() return self.db.profile.chatbutton[4] end, + set = function(v) + self.db.profile.chatbutton[4] = v + self:ChatButton(4,v) + end + }, + chat5 = { + name = string.format(L["Chat%dButtons"], 5), + desc = string.format(L["Toggles buttons on and off for chat window %d."], 5), + type = "toggle", + get = function() return self.db.profile.chatbutton[5] end, + set = function(v) + self.db.profile.chatbutton[5] = v + self:ChatButton(5,v) + end + }, + chat6 = { + name = string.format(L["Chat%dButtons"], 6), + desc = string.format(L["Toggles buttons on and off for chat window %d."], 6), + type = "toggle", + get = function() return self.db.profile.chatbutton[6] end, + set = function(v) + self.db.profile.chatbutton[6] = v + self:ChatButton(6,v) + end + }, + chat7 = { + name = string.format(L["Chat%dButtons"], 7), + desc = string.format(L["Toggles buttons on and off for chat window %d."], 7), + type = "toggle", + get = function() return self.db.profile.chatbutton[7] end, + set = function(v) + self.db.profile.chatbutton[7] = v + self:ChatButton(7,v) + end + } + }, + }, + showreminder = { + name = L["Show Reminder Button"], + desc = L["Show reminder button when not at bottom of chat window."], + type = "toggle", + order = 190, + get = function() return self.db.profile.showreminder end, + set = function(v) self.db.profile.showreminder = v end, + }, + toggle = { + name = L["Toggle"], + desc = L["Toggle the module on and off."], + type = "toggle", + order = 200, + get = function() return self.db.profile.on end, + set = function() self.db.profile.on = Prat:ToggleModuleActive("buttons") end + } + } + } + -- create reminder buttons + for i = 1,NUM_CHAT_WINDOWS do + self:MakeReminder(i) + end +end + +function Prat_Buttons:OnEnable() + self:Hook("ChatFrame_OnUpdate") +end + +function Prat_Buttons:OnDisable() + self:ChatMenu(true) + for i = 1,NUM_CHAT_WINDOWS do + self:ChatButton(i,true) + end +end + +function Prat_Buttons:ChatFrame_OnUpdate(elapsed) + self.hooks["ChatFrame_OnUpdate"](elapsed) + self:ChatMenu(self.db.profile.chatmenu) + for i = 1,NUM_CHAT_WINDOWS do + self:ChatButton(i,self.db.profile.chatbutton[i]) + end +end + +function Prat_Buttons:ChatMenu(visible) + if visible then + ChatFrameMenuButton:Show() + else + ChatFrameMenuButton:Hide() + end +end + +function Prat_Buttons:ChatButton(id,visible) + local cf = getglobal("ChatFrame"..id) + local cfup = getglobal("ChatFrame"..id.."UpButton") + local cfdown = getglobal("ChatFrame"..id.."DownButton") + local cfbottom = getglobal("ChatFrame"..id.."BottomButton") + local cfreminder = getglobal("ChatFrame"..id.."ScrollDownReminder") + if visible then + cfup:Show() + cfdown:Show() + cfbottom:Show() + else + cfup:Hide() + cfdown:Hide() + cfbottom:Hide() + if ( (not cf:AtBottom() ) and self.db.profile.showreminder) then + cfreminder:Show() + else + cfreminder:Hide() + end + end +end + +function Prat_Buttons:MakeReminder(id) + local cf = getglobal("ChatFrame"..id) + local b = CreateFrame("Button","ChatFrame"..id.."ScrollDownReminder",cf) + b:SetFrameStrata("BACKGROUND") + b:SetWidth(24) + b:SetHeight(24) + b:SetNormalTexture("Interface\\Buttons\\UI-ScrollBar-ScrollDownButton-Up") + b:SetPushedTexture("Interface\\Buttons\\UI-ScrollBar-ScrollDownButton-Down") + b:SetHighlightTexture("Interface\\PaperDollInfoFrame\\UI-Character-Tab-Highlight") + b:SetPoint("RIGHT", cf, "RIGHT", 0, 0) + b:SetPoint("LEFT", cf, "RIGHT", -32, 0) + b:SetPoint("TOP", cf, "BOTTOM", 0, 28) + b:SetPoint("BOTTOM", cf, "BOTTOM", 0, 0) + b:SetScript("OnClick", function() this:GetParent():ScrollToBottom() end) + b:Hide() +end diff --git a/modules/ChannelColorMemory.lua b/modules/ChannelColorMemory.lua new file mode 100755 index 0000000..a6ca292 --- /dev/null +++ b/modules/ChannelColorMemory.lua @@ -0,0 +1,122 @@ +--[[ +Name: Prat_ChannelColorMemory +Revision: $Revision: 16019 $ +Author(s): Sylvanaar +Website: http://www.wowace.com/files/index.php?path=Prat/ +Documentation: http://www.wowace.com/wiki/Prat/Integrated_Modules#ChannelColorMemory +SVN: http://svn.wowace.com/wowace/trunk/Prat/ +Description: Module for Prat that remembers the colors of channels by channel name +Dependencies: Prat + +Credit goes to: ConsisTint By Karl Isenberg (AnduinLothar) +]] + +local L = AceLibrary("AceLocale-2.2"):new("ChannelColorMemory") + +L:RegisterTranslations("enUS", function() return { + ["ChannelColorMemory"] = true, + ["Remembers the colors of channels by channel name."] = true, + ["(%w+)%s?(.*)"] = true, + ["Toggle"] = true, + ["Toggle the module on and off."] = true, +} end) + +L:RegisterTranslations("ruRU", function() return { + ["ChannelColorMemory"] = "Запомнить цвет канала", + ["Remembers the colors of channels by channel name."] = "Запоминает цвета каналов по названию канала.", + ["(%w+)%s?(.*)"] = "(%w+)%s?(.*)", + ["Toggle"] = "Вкл/Выкл", + ["Toggle the module on and off."] = "Вкл/Выкл модуль.", +} end) + +L:RegisterTranslations("zhCN", function() return { + ["ChannelColorMemory"] = "频道颜色储存", + ["Remembers the colors of channels by channel name."] = "还原频道名称的颜色设置", + ["(%w+)%s?(.*)"] = "(%w+)%s?(.*)", + ["Toggle"] = "切换", + ["Toggle the module on and off."] = "切换此模块的打开与关闭。", +} end) + +L:RegisterTranslations("koKR", function() return { + ["ChannelColorMemory"] = "채널 색상 기억", + ["Remembers the colors of channels by channel name."] = "채널명에 따른 채널의 색상을 기억합니다.", + ["(%w+)%s?(.*)"] = "(.+)%s?(.*)", + ["Toggle"] = "전환", + ["Toggle the module on and off."] = "모듈 켜고 끄기 전환", +} end) + + +Prat_ChannelColorMemory = Prat:NewModule("ChannelColorMemory") + +function Prat_ChannelColorMemory:OnInitialize() + self.db = Prat:AcquireDBNamespace("ChannelColorMemory") + Prat:RegisterDefaults("ChannelColorMemory", "profile", { + on = true, + colors = {}, + }) + Prat.Options.args.ChannelColorMemory = { + name = L["ChannelColorMemory"], + desc = L["Remembers the colors of channels by channel name."], + type = "group", + args = { + Toggle = { + type = "toggle", + name = L["Toggle"], + desc = L["Toggle the module on and off."], + get = function() return self.db.profile.on end, + set = function(v) self.db.profile.on = Prat:ToggleModuleActive("ChannelColorMemory") end + }, + } + } +end + +function Prat_ChannelColorMemory:OnEnable() + self:RegisterEvent("UPDATE_CHAT_COLOR") + self:RegisterEvent("CHAT_MSG_CHANNEL_NOTICE") +end +function Prat_ChannelColorMemory:OnDisable() + self:UnregisterEvent("UPDATE_CHAT_COLOR") + self:UnregisterEvent("CHAT_MSG_CHANNEL_NOTICE") +end + + +function Prat_ChannelColorMemory:UPDATE_CHAT_COLOR(ChatType, cr,cg,cb) + if (ChatType) then + local number = string.gfind(arg1, "CHANNEL(%d+)")() + if ( number ) then + local _, name = GetChannelName(number); + if ( name ) then + local name, zoneSuffix = string.gfind(name, L["(%w+)%s?(.*)"])(); + + local color = self.db.profile.colors[name]; + if (not color) then + self.db.profile.colors[name] = {r=cr, g=cg, b=cb}; + else + color.r=cr + color.g=cg + color.b=cb + end + end + end + end +end + +function Prat_ChannelColorMemory:CHAT_MSG_CHANNEL_NOTICE(NoticeType,a2,a3,LongName,a5,a6,a7,number, cname) + if (not strfind(LongName, "%d+%. .*")) then + return; + elseif (arg1 == "YOU_JOINED") then + local name, zoneSuffix = string.gfind(cname, L["(%w+)%s?(.*)"])(); + local color = self.db.profile.colors[name]; + if (color) then + ChangeChatColor("CHANNEL"..number, color.r, color.g, color.b); + else + color = ChatTypeInfo["CHANNEL"..number]; + self.db.profile.colors[name] = {r=color.r, g=color.g, b=color.b}; + end + end +end + + + + + diff --git a/modules/ChannelNames.lua b/modules/ChannelNames.lua new file mode 100755 index 0000000..e2fb8da --- /dev/null +++ b/modules/ChannelNames.lua @@ -0,0 +1,999 @@ +--[[ +Name: Prat_ChannelNames +Revision: $Revision: 16019 $ +Author(s): Curney (asml8ed@gmail.com) + Krtek (krtek4@gmail.com) +Inspired by: idChat2_ChannelNames by Industrial +Website: http://www.wowace.com/files/index.php?path=Prat/ +Documentation: http://www.wowace.com/wiki/Prat/Integrated_Modules#ChannelNames +SVN: http://svn.wowace.com/wowace/trunk/Prat/ +Description: Module for Prat that adds options and keybindings for all chat channels. +Dependencies: Prat +]] + +local L = AceLibrary("AceLocale-2.2"):new("PratChannelNames") + +L:RegisterTranslations("enUS", function() return { + ["ChannelNames"] = true, + ["Channel name abbreviation options."] = true, + ["Replace"] = true, + ["Toggle replacing this channel."] = true, + ["Off"] = true, + ["Turn off this channel."] = true, + ["Set"] = true, + ["Channel %d"] = true, + ["Say"] = true, + ["Whisper"] = true, + ["Incoming Whisper"] = true, + ["Yell"] = true, + ["Guild"] = true, + ["Party"] = true, + ["Raid"] = true, + ["Raid Leader"] = true, + ["Raid Warning"] = true, + ["Officer"] = true, + ["Battleground"] = true, + ["Battleground Leader"] = true, + ["%s settings."] = true, + ["Use a custom replacement for the chat %s text."] = true, + ["Show Space"] = true, + ["Toggle adding space after channel replacement."] = true, + ["Show Colon"] = true, + ["Toggle adding colon after channel replacement."] = true, + ["Toggle"] = true, + ["Toggle the module on and off."] = true, +} end) + +L:RegisterTranslations("ruRU", function() return { + ["ChannelNames"] = "ChannelNames", + ["Channel name abbreviation options."] = "Настройки сокращения названий каналов.", + ["Replace"] = "Заменить", + ["Toggle replacing this channel."] = "Включить замену данного канала.", + ["Off"] = "Выкл", + ["Turn off this channel."] = "Выключить этот канал.", + ["Set"] = "Задать", + ["Channel %d"] = "Канал %d", + ["Say"] = "Сказать", + ["Whisper"] = "Шепот", + ["Incoming Whisper"] = "Шепот", + ["Yell"] = "Крикнуть", + ["Guild"] = "Гильдия", + ["Party"] = "Группа", + ["Raid"] = "Рейд", + ["Raid Leader"] = "Лидер рейда", + ["Raid Warning"] = "Объявление рейду", + ["Officer"] = "Офицер", + ["Battleground"] = "Поле боя", + ["Battleground Leader"] = "Лидер поля боя", + ["%s settings."] = "Настройки канала \"%s\".", + ["Use a custom replacement for the chat %s text."] = "Использовать заданную замену текста %s канала.", + ["Show Space"] = "Показывать пробел", + ["Toggle adding space after channel replacement."] = "Вкл/Выкл добавление пробела после замены канала.", + ["Show Colon"] = "Показывать двоеточие", + ["Toggle adding colon after channel replacement."] = "Вкл/Выкл добавление двоеточия после замены канала.", + ["Toggle"] = "Вкл/Выкл", + ["Toggle the module on and off."] = "Вкл/Выкл модуль", +} end) + +L:RegisterTranslations("zhCN", function() return { + ["ChannelNames"] = "频道名称", + ["Channel name abbreviation options."] = "频道名称缩写选项。", + ["Replace"] = "替换", + ["Toggle replacing this channel."] = "替换频道。", + ["Off"] = "关闭", + ["Turn off this channel."] = "关闭此频道。", + ["Set"] = "设置", + ["Channel %d"] = "频道 %d", + ["Say"] = "说", + ["Whisper"] = "密语", + ["Incoming Whisper"] = "密语回复", + ["Yell"] = "喊", + ["Guild"] = "公会", + ["Party"] = "队伍", + ["Raid"] = "团队", + ["Raid Leader"] = "团队领袖", + ["Raid Warning"] = "团队报警", + ["Officer"] = "官员", + ["Battleground"] = "战场", + ["Battleground Leader"] = "战场领袖", + ["%s settings."] = "%s 设置。", + ["Use a custom replacement for the chat %s text."] = "自定义替换此聊天 %s 文本。", + ["Show Space"] = "显示间隔", + ["Toggle adding space after channel replacement."] = "切换频道替换后增加间隔空间。", + ["Show Colon"] = "显示冒号", + ["Toggle adding colon after channel replacement."] = "切换频道替换后增加冒号。", + ["Toggle"] = "切换", + ["Toggle the module on and off."] = "切换此模块的打开与关闭。", +} end) + +L:RegisterTranslations("koKR", function() return { + ["ChannelNames"] = "채널명", + ["Channel name abbreviation options."] = "채널명 생략 설정입니다.", + ["Replace"] = "대체", + ["Toggle replacing this channel."] = "해당 채널 대체를 전환합니다.", + ["Off"] = "끔", + ["Turn off this channel."] = "해당 채널를 끕니다.", + ["Set"] = "설정", + ["Channel %d"] = "채널 %d", + ["Say"] = "일반", + ["Whisper"] = "귓속말", + ["Incoming Whisper"] = "보낸 귓속말", + ["Yell"] = "외침", + ["Guild"] = "길드", + ["Party"] = "파티", + ["Raid"] = "공격대", + ["Raid Leader"] = "공격대장", + ["Raid Warning"] = "공격대 경보", + ["Officer"] = "오피서", + ["Battleground"] = "전장", + ["Battleground Leader"] = "전투대장", + ["%s settings."] = "%s 설정입니다.", + ["Use a custom replacement for the chat %s text."] = "%s 대화에 사용자 대체를 사용합니다.", + ["Show Space"] = "간격 표시", + ["Toggle adding space after channel replacement."] = "채널 대체후 간격 추가를 전환합니다.", + ["Show Colon"] = "콜론 표시", + ["Toggle adding colon after channel replacement."] = "채널 대채 후 콜론 추가를 전환합니다.", + ["Toggle"] = "전환", + ["Toggle the module on and off."] = "모듈 켜고 끄기를 전환합니다.", +} end) + +local chanSave = {} + +Prat_ChannelNames = Prat:NewModule("channelnames") + +function Prat_ChannelNames:OnInitialize() + self.db = Prat:AcquireDBNamespace("ChannelNames") + Prat:RegisterDefaults("ChannelNames", "profile", { + on = true, + space = true, + colon = true, + replace = { + say = true, + whisper = true, + whisperincome = true, + yell = true, + party = true, + guild = true, + officer = true, + raid = true, + raidleader = true, + raidwarning = true, + battleground = true, + battlegroundleader = true, + channel1 = true, + channel2 = true, + channel3 = true, + channel4 = true, + channel5 = true, + channel6 = true, + channel7 = true, + channel8 = true, + channel9 = true, + channel10 = true, + }, + shortnames = { + say = "[S]", + whisper = "[W To]", + whisperincome = "[W From]", + yell = "[Y]", + party = "[P]", + guild = "[G]", + officer = "[O]", + raid = "[R]", + raidleader = "[RL]", + raidwarning = "[RW]", + battleground = "[B]", + battlegroundleader = "[BL]", + channel1 = "[1]", + channel2 = "[2]", + channel3 = "[3]", + channel4 = "[4]", + channel5 = "[5]", + channel6 = "[6]", + channel7 = "[7]", + channel8 = "[8]", + channel9 = "[9]", + channel10 = "[10]", + } + }) + Prat.Options.args.channelnames = { + name = L["ChannelNames"], + desc = L["Channel name abbreviation options."], + type = "group", + args = { + say = { + name = L["Say"], + desc = string.format(L["%s settings."], L["Say"]), + type = "group", + order = 80, + args = { + set = { + name = L["Say"], + desc = string.format(L["Use a custom replacement for the chat %s text."], L["Say"]), + type = "text", + usage = "", + get = function() return self.db.profile.shortnames.say end, + set = function(v) self.db.profile.shortnames.say = v end + }, + replace = { + name = L["Replace"], + desc = L["Toggle replacing this channel."], + type = "toggle", + order = 175, + get = function() return self.db.profile.replace.say end, + set = function(v) self.db.profile.replace.say = v end, + }, + off = { + name = L["Off"], + desc = L["Turn off this channel."], + type = "execute", + order = 200, + func = function() self.db.profile.shortnames.say = "" Prat:Print("Say text now turned off") end + } + } + }, + whisper = { + name = L["Whisper"], + desc = string.format(L["%s settings."], L["Whisper"]), + type = "group", + order = 90, + args = { + set = { + name = L["Whisper"], + desc = string.format(L["Use a custom replacement for the chat %s text."], L["Whisper"]), + type = "text", + usage = "", + get = function() return self.db.profile.shortnames.whisper end, + set = function(v) self.db.profile.shortnames.whisper = v end + }, + replace = { + name = L["Replace"], + desc = L["Toggle replacing this channel."], + type = "toggle", + order = 175, + get = function() return self.db.profile.replace.whisper end, + set = function(v) self.db.profile.replace.whisper = v end, + }, + off = { + name = L["Off"], + desc = L["Turn off this channel."], + type = "execute", + order = 200, + func = function() self.db.profile.shortnames.whisper = "" Prat:Print("Whisper text now turned off") end + } + } + }, + whisperincome = { + name = L["Incoming Whisper"], + desc = string.format(L["%s settings."], L["Incoming Whisper"]), + type = "group", + order = 100, + args = { + set = { + name = L["Incoming Whisper"], + desc = string.format(L["Use a custom replacement for the chat %s text."], L["Incoming Whisper"]), + type = "text", + usage = "", + get = function() return self.db.profile.shortnames.whisperincome end, + set = function(v) self.db.profile.shortnames.whisperincome = v end + }, + replace = { + name = L["Replace"], + desc = L["Toggle replacing this channel."], + type = "toggle", + order = 175, + get = function() return self.db.profile.replace.whisperincome end, + set = function(v) self.db.profile.replace.whisperincome = v end, + }, + off = { + name = L["Off"], + desc = L["Turn off this channel."], + type = "execute", + order = 200, + func = function() self.db.profile.shortnames.whisperincome = "" Prat:Print("Whisper text now turned off") end + } + } + }, + yell = { + name = L["Yell"], + desc = string.format(L["%s settings."], L["Yell"]), + type = "group", + order = 110, + args = { + set = { + name = L["Yell"], + desc = string.format(L["Use a custom replacement for the chat %s text."], L["Yell"]), + type = "text", + usage = "", + get = function() return self.db.profile.shortnames.yell end, + set = function(v) self.db.profile.shortnames.yell = v end + }, + replace = { + name = L["Replace"], + desc = L["Toggle replacing this channel."], + type = "toggle", + order = 175, + get = function() return self.db.profile.replace.yell end, + set = function(v) self.db.profile.replace.yell = v end, + }, + off = { + name = L["Off"], + desc = L["Turn off this channel."], + type = "execute", + order = 200, + func = function() self.db.profile.shortnames.yell = "" Prat:Print("Yell text now turned off") end + } + } + }, + guild = { + name = L["Guild"], + desc = string.format(L["%s settings."], L["Guild"]), + type = "group", + order = 120, + args = { + set = { + name = L["Guild"], + desc = string.format(L["Use a custom replacement for the chat %s text."], L["Guild"]), + type = "text", + usage = "", + get = function() return self.db.profile.shortnames.guild end, + set = function(v) self.db.profile.shortnames.guild = v end + }, + replace = { + name = L["Replace"], + desc = L["Toggle replacing this channel."], + type = "toggle", + order = 175, + get = function() return self.db.profile.replace.guild end, + set = function(v) self.db.profile.replace.guild = v end, + }, + off = { + name = L["Off"], + desc = L["Turn off this channel."], + type = "execute", + order = 200, + func = function() self.db.profile.shortnames.guild = "" Prat:Print("Guild text now turned off") end + } + } + }, + party = { + name = L["Party"], + desc = string.format(L["%s settings."], L["Party"]), + type = "group", + order = 130, + args = { + set = { + name = L["Party"], + desc = string.format(L["Use a custom replacement for the chat %s text."], L["Party"]), + type = "text", + usage = "", + get = function() return self.db.profile.shortnames.party end, + set = function(v) self.db.profile.shortnames.party = v end + }, + replace = { + name = L["Replace"], + desc = L["Toggle replacing this channel."], + type = "toggle", + order = 175, + get = function() return self.db.profile.replace.party end, + set = function(v) self.db.profile.replace.party = v end, + }, + off = { + name = L["Off"], + desc = L["Turn off this channel."], + type = "execute", + order = 200, + func = function() self.db.profile.shortnames.party = "" Prat:Print("Party text now turned off") end + } + } + }, + raid = { + name = L["Raid"], + desc = string.format(L["%s settings."], L["Raid"]), + type = "group", + order = 140, + args = { + set = { + name = L["Raid"], + desc = string.format(L["Use a custom replacement for the chat %s text."], L["Raid"]), + type = "text", + usage = "", + get = function() return self.db.profile.shortnames.raid end, + set = function(v) self.db.profile.shortnames.raid = v end + }, + replace = { + name = L["Replace"], + desc = L["Toggle replacing this channel."], + type = "toggle", + order = 175, + get = function() return self.db.profile.replace.raid end, + set = function(v) self.db.profile.replace.raid = v end, + }, + off = { + name = L["Off"], + desc = L["Turn off this channel."], + type = "execute", + order = 200, + func = function() self.db.profile.shortnames.raid = "" Prat:Print("Raid text now turned off") end + } + } + }, + raidleader = { + name = L["Raid Leader"], + desc = string.format(L["%s settings."], L["Raid Leader"]), + type = "group", + order = 150, + args = { + set = { + name = L["Raid Leader"], + desc = string.format(L["Use a custom replacement for the chat %s text."], L["Raid Leader"]), + type = "text", + usage = "", + get = function() return self.db.profile.shortnames.raidleader end, + set = function(v) self.db.profile.shortnames.raidleader = v end + }, + replace = { + name = L["Replace"], + desc = L["Toggle replacing this channel."], + type = "toggle", + order = 175, + get = function() return self.db.profile.replace.raidleader end, + set = function(v) self.db.profile.replace.raidleader = v end, + }, + off = { + name = L["Off"], + desc = L["Turn off this channel."], + type = "execute", + order = 200, + func = function() self.db.profile.shortnames.raidleader = "" Prat:Print("Raid Leader text now turned off") end + } + } + }, + raidwarning = { + name = L["Raid Warning"], + desc = string.format(L["%s settings."], L["Raid Warning"]), + type = "group", + order = 160, + args = { + set = { + name = L["Raid Warning"], + desc = string.format(L["Use a custom replacement for the chat %s text."], L["Raid Warning"]), + type = "text", + usage = "", + get = function() return self.db.profile.shortnames.raidwarning end, + set = function(v) self.db.profile.shortnames.raidwarning = v end + }, + replace = { + name = L["Replace"], + desc = L["Toggle replacing this channel."], + type = "toggle", + order = 175, + get = function() return self.db.profile.replace.raidwarning end, + set = function(v) self.db.profile.replace.raidwarning = v end, + }, + off = { + name = L["Off"], + desc = L["Turn off this channel."], + type = "execute", + order = 200, + func = function() self.db.profile.shortnames.raidwarning = "" Prat:Print("Raid Warning text now turned off") end + } + } + }, + officer = { + name = L["Officer"], + desc = string.format(L["%s settings."], L["Officer"]), + type = "group", + order = 170, + args = { + set = { + name = L["Officer"], + desc = string.format(L["Use a custom replacement for the chat %s text."], L["Officer"]), + type = "text", + usage = "", + get = function() return self.db.profile.shortnames.officer end, + set = function(v) self.db.profile.shortnames.officer = v end + }, + replace = { + name = L["Replace"], + desc = L["Toggle replacing this channel."], + type = "toggle", + order = 175, + get = function() return self.db.profile.replace.officer end, + set = function(v) self.db.profile.replace.officer = v end, + }, + off = { + name = L["Off"], + desc = L["Turn off this channel."], + type = "execute", + order = 200, + func = function() self.db.profile.shortnames.officer = "" Prat:Print("Officer text now turned off") end + } + } + }, + battleground = { + name = L["Battleground"], + desc = string.format(L["%s settings."], L["Battleground"]), + type = "group", + order = 180, + args = { + set = { + name = L["Battleground"], + desc = string.format(L["Use a custom replacement for the chat %s text."], L["Battleground"]), + type = "text", + usage = "", + get = function() return self.db.profile.shortnames.battleground end, + set = function(v) self.db.profile.shortnames.battleground = v end + }, + replace = { + name = L["Replace"], + desc = L["Toggle replacing this channel."], + type = "toggle", + order = 175, + get = function() return self.db.profile.replace.battleground end, + set = function(v) self.db.profile.replace.battleground = v end, + }, + off = { + name = L["Off"], + desc = L["Turn off this channel."], + type = "execute", + order = 200, + func = function() self.db.profile.shortnames.battleground = "" Prat:Print("Battleground text now turned off") end + } + } + }, + battlegroundleader = { + name = L["Battleground Leader"], + desc = string.format(L["%s settings."], L["Battleground Leader"]), + type = "group", + order = 190, + args = { + set = { + name = L["Battleground Leader"], + desc = string.format(L["Use a custom replacement for the chat %s text."], L["Battleground Leader"]), + type = "text", + usage = "", + get = function() return self.db.profile.shortnames.battlegroundleader end, + set = function(v) self.db.profile.shortnames.battlegroundleader = v end + }, + replace = { + name = L["Replace"], + desc = L["Toggle replacing this channel."], + type = "toggle", + order = 175, + get = function() return self.db.profile.replace.battlegroundleader end, + set = function(v) self.db.profile.replace.battlegroundleader = v end, + }, + off = { + name = L["Off"], + desc = L["Turn off this channel."], + type = "execute", + order = 200, + func = function() self.db.profile.shortnames.battlegroundleader = "" Prat:Print("Battleground Leader text now turned off") end + } + } + }, + channel1 = { + name = string.format(L["Channel %d"], 1), + desc = string.format(L["%s settings."],string.format(L["Channel %d"], 1)), + type = "group", + order = 201, + args = { + set = { + name = string.format(L["Channel %d"], 1), + desc = string.format(L["Use a custom replacement for the chat %s text."], string.format(L["Channel %d"], 1)), + type = "text", + usage = "", + get = function() return self.db.profile.shortnames.channel1 end, + set = function(v) self.db.profile.shortnames.channel1 = v end + }, + replace = { + name = L["Replace"], + desc = L["Toggle replacing this channel."], + type = "toggle", + order = 175, + get = function() return self.db.profile.replace.channel1 end, + set = function(v) self.db.profile.replace.channel1 = v end, + }, + off = { + name = L["Off"], + desc = L["Turn off this channel."], + type = "execute", + order = 200, + func = function() self.db.profile.shortnames.channel1 = "" Prat:Print("Channel 1 text now turned off") end + } + } + }, + channel2 = { + name = string.format(L["Channel %d"], 2), + desc = string.format(L["%s settings."],string.format(L["Channel %d"], 2)), + type = "group", + order = 202, + args = { + set = { + name = string.format(L["Channel %d"], 2), + desc = string.format(L["Use a custom replacement for the chat %s text."], string.format(L["Channel %d"], 2)), + type = "text", + usage = "", + get = function() return self.db.profile.shortnames.channel2 end, + set = function(v) self.db.profile.shortnames.channel2 = v end + }, + replace = { + name = L["Replace"], + desc = L["Toggle replacing this channel."], + type = "toggle", + order = 175, + get = function() return self.db.profile.replace.channel2 end, + set = function(v) self.db.profile.replace.channel2 = v end, + }, + off = { + name = L["Off"], + desc = L["Turn off this channel."], + type = "execute", + order = 200, + func = function() self.db.profile.shortnames.channel2 = "" Prat:Print("Channel 2 text now turned off") end + } + } + }, + channel3 = { + name = string.format(L["Channel %d"], 3), + desc = string.format(L["%s settings."],string.format(L["Channel %d"], 3)), + type = "group", + order = 203, + args = { + set = { + name = string.format(L["Channel %d"], 3), + desc = string.format(L["Use a custom replacement for the chat %s text."], string.format(L["Channel %d"], 3)), + type = "text", + usage = "", + get = function() return self.db.profile.shortnames.channel3 end, + set = function(v) self.db.profile.shortnames.channel3 = v end + }, + replace = { + name = L["Replace"], + desc = L["Toggle replacing this channel."], + type = "toggle", + order = 175, + get = function() return self.db.profile.replace.channel3 end, + set = function(v) self.db.profile.replace.channel3 = v end, + }, + off = { + name = L["Off"], + desc = L["Turn off this channel."], + type = "execute", + order = 200, + func = function() self.db.profile.shortnames.channel3 = "" Prat:Print("Channel 3 text now turned off") end + } + } + }, + channel4 = { + name = string.format(L["Channel %d"], 4), + desc = string.format(L["%s settings."],string.format(L["Channel %d"], 4)), + type = "group", + order = 204, + args = { + set = { + name = string.format(L["Channel %d"], 4), + desc = string.format(L["Use a custom replacement for the chat %s text."], string.format(L["Channel %d"], 4)), + type = "text", + usage = "", + get = function() return self.db.profile.shortnames.channel4 end, + set = function(v) self.db.profile.shortnames.channel4 = v end + }, + replace = { + name = L["Replace"], + desc = L["Toggle replacing this channel."], + type = "toggle", + order = 175, + get = function() return self.db.profile.replace.channel4 end, + set = function(v) self.db.profile.replace.channel4 = v end, + }, + off = { + name = L["Off"], + desc = L["Turn off this channel."], + type = "execute", + order = 200, + func = function() self.db.profile.shortnames.channel4 = "" Prat:Print("Channel 4 text now turned off") end + } + } + }, + channel5 = { + name = string.format(L["Channel %d"], 5), + desc = string.format(L["%s settings."],string.format(L["Channel %d"], 5)), + type = "group", + order = 205, + args = { + set = { + name = string.format(L["Channel %d"], 5), + desc = string.format(L["Use a custom replacement for the chat %s text."], string.format(L["Channel %d"], 5)), + type = "text", + usage = "", + get = function() return self.db.profile.shortnames.channel5 end, + set = function(v) self.db.profile.shortnames.channel5 = v end + }, + replace = { + name = L["Replace"], + desc = L["Toggle replacing this channel."], + type = "toggle", + order = 175, + get = function() return self.db.profile.replace.channel5 end, + set = function(v) self.db.profile.replace.channel5 = v end, + }, + off = { + name = L["Off"], + desc = L["Turn off this channel."], + type = "execute", + order = 200, + func = function() self.db.profile.shortnames.channel5 = "" Prat:Print("Channel 5 text now turned off") end + } + } + }, + channel6 = { + name = string.format(L["Channel %d"], 6), + desc = string.format(L["%s settings."],string.format(L["Channel %d"], 6)), + type = "group", + order = 206, + args = { + set = { + name = string.format(L["Channel %d"], 6), + desc = string.format(L["Use a custom replacement for the chat %s text."], string.format(L["Channel %d"], 6)), + type = "text", + usage = "", + get = function() return self.db.profile.shortnames.channel6 end, + set = function(v) self.db.profile.shortnames.channel6 = v end + }, + replace = { + name = L["Replace"], + desc = L["Toggle replacing this channel."], + type = "toggle", + order = 175, + get = function() return self.db.profile.replace.channel6 end, + set = function(v) self.db.profile.replace.channel6 = v end, + }, + off = { + name = L["Off"], + desc = L["Turn off this channel."], + type = "execute", + order = 200, + func = function() self.db.profile.shortnames.channel6 = "" Prat:Print("Channel 6 text now turned off") end + } + } + }, + channel7 = { + name = string.format(L["Channel %d"], 7), + desc = string.format(L["%s settings."],string.format(L["Channel %d"], 7)), + type = "group", + order = 207, + args = { + set = { + name = string.format(L["Channel %d"], 7), + desc = string.format(L["Use a custom replacement for the chat %s text."], string.format(L["Channel %d"], 7)), + type = "text", + usage = "", + get = function() return self.db.profile.shortnames.channel7 end, + set = function(v) self.db.profile.shortnames.channel7 = v end + }, + replace = { + name = L["Replace"], + desc = L["Toggle replacing this channel."], + type = "toggle", + order = 175, + get = function() return self.db.profile.replace.channel7 end, + set = function(v) self.db.profile.replace.channel7 = v end, + }, + off = { + name = L["Off"], + desc = L["Turn off this channel."], + type = "execute", + order = 200, + func = function() self.db.profile.shortnames.channel7 = "" Prat:Print("Channel 7 text now turned off") end + } + } + }, + channel8 = { + name = string.format(L["Channel %d"], 8), + desc = string.format(L["%s settings."],string.format(L["Channel %d"], 8)), + type = "group", + order = 208, + args = { + set = { + name = string.format(L["Channel %d"], 8), + desc = string.format(L["Use a custom replacement for the chat %s text."], string.format(L["Channel %d"], 8)), + type = "text", + usage = "", + get = function() return self.db.profile.shortnames.channel8 end, + set = function(v) self.db.profile.shortnames.channel8 = v end + }, + replace = { + name = L["Replace"], + desc = L["Toggle replacing this channel."], + type = "toggle", + order = 175, + get = function() return self.db.profile.replace.channel8 end, + set = function(v) self.db.profile.replace.channel8 = v end, + }, + off = { + name = L["Off"], + desc = L["Turn off this channel."], + type = "execute", + order = 200, + func = function() self.db.profile.shortnames.channel8 = "" Prat:Print("Channel 8 text now turned off") end + } + } + }, + channel9 = { + name = string.format(L["Channel %d"], 9), + desc = string.format(L["%s settings."],string.format(L["Channel %d"], 9)), + type = "group", + order = 209, + args = { + set = { + name = string.format(L["Channel %d"], 9), + desc = string.format(L["Use a custom replacement for the chat %s text."], string.format(L["Channel %d"], 9)), + type = "text", + usage = "", + get = function() return self.db.profile.shortnames.channel9 end, + set = function(v) self.db.profile.shortnames.channel9 = v end + }, + replace = { + name = L["Replace"], + desc = L["Toggle replacing this channel."], + type = "toggle", + order = 175, + get = function() return self.db.profile.replace.channel9 end, + set = function(v) self.db.profile.replace.channel9 = v end, + }, + off = { + name = L["Off"], + desc = L["Turn off this channel."], + type = "execute", + order = 200, + func = function() self.db.profile.shortnames.channel9 = "" Prat:Print("Channel 9 text now turned off") end + } + } + }, + channel10 = { + name = string.format(L["Channel %d"], 10), + desc = string.format(L["%s settings."],string.format(L["Channel %d"], 10)), + type = "group", + order = 210, + args = { + set = { + name = string.format(L["Channel %d"], 10), + desc = string.format(L["Use a custom replacement for the chat %s text."], string.format(L["Channel %d"], 10)), + type = "text", + usage = "", + get = function() return self.db.profile.shortnames.channel10 end, + set = function(v) self.db.profile.shortnames.channel10 = v end + }, + replace = { + name = L["Replace"], + desc = L["Toggle replacing this channel."], + type = "toggle", + order = 175, + get = function() return self.db.profile.replace.channel10 end, + set = function(v) self.db.profile.replace.channel10 = v end, + }, + off = { + name = L["Off"], + desc = L["Turn off this channel."], + type = "execute", + order = 200, + func = function() self.db.profile.shortnames.channel10 = "" Prat:Print("Channel 10 text now turned off") end + } + } + }, + spacetoggle = { + name = L["Show Space"], + desc = L["Toggle adding space after channel replacement."], + type = "toggle", + order = 230, + get = function() return self.db.profile.space end, + set = function(v) self.db.profile.space = v end, + }, + colontoggle = { + name = L["Show Colon"], + desc = L["Toggle adding colon after channel replacement."], + type = "toggle", + order = 240, + get = function() return self.db.profile.colon end, + set = function(v) self.db.profile.colon = v end, + }, + toggle = { + name = L["Toggle"], + desc = L["Toggle the module on and off."], + type = "toggle", + order = 250, + get = function() return self.db.profile.on end, + set = function(v) self.db.profile.on = Prat:ToggleModuleActive("channelnames"); end, + }, + } + } +end + +function Prat_ChannelNames:OnEnable() + for i=1,NUM_CHAT_WINDOWS do + self:Hook(getglobal("ChatFrame"..i), "AddMessage") + end + self:saveChanName() + self:doReplacement() +end + +function Prat_ChannelNames:saveChanName() + chanSave["guild"] = CHAT_GUILD_GET + chanSave["officer"] = CHAT_OFFICER_GET + chanSave["party"] = CHAT_PARTY_GET + chanSave["raid"] = CHAT_RAID_GET + chanSave["raidleader"] = CHAT_RAID_LEADER_GET + chanSave["raidwarning"] = CHAT_RAID_WARNING_GET + chanSave["battleground"] = CHAT_BATTLEGROUND_GET + chanSave["battlegroundleader"] = CHAT_BATTLEGROUND_LEADER_GET + chanSave["say"] = CHAT_SAY_GET + chanSave["whisperincome"] = CHAT_WHISPER_GET + chanSave["whisper"] = CHAT_WHISPER_INFORM_GET + chanSave["yell"] = CHAT_YELL_GET +end + +function Prat_ChannelNames:doReplacement() + local replace = self.db.profile.replace + local shortnames = self.db.profile.shortnames + local space = self.db.profile.space and " " or "" + local colon = self.db.profile.colon and ":" or "" + local default = space .. "%s" .. colon .. "\32" + + CHAT_CHANNEL_GET = "%s" .. colon .. "\32"; + + CHAT_GUILD_GET = replace["guild"] and (shortnames["guild"] .. default) or chanSave["guild"]; + CHAT_OFFICER_GET = replace["officer"] and (shortnames["officer"] .. default) or chanSave["officer"] + + CHAT_PARTY_GET = replace["party"] and (shortnames["party"] .. default) or chanSave["party"] + CHAT_RAID_GET = replace["raid"] and (shortnames["raid"] .. default) or chanSave["raid"] + CHAT_RAID_LEADER_GET = replace["raidleader"] and (shortnames["raidleader"] .. default) or chanSave["raidleader"] + CHAT_RAID_WARNING_GET = replace["raidwarning"] and (shortnames["raidwarning"] .. default) or chanSave["raidwarning"] + CHAT_BATTLEGROUND_GET = replace["battleground"] and (shortnames["battleground"] .. default) or chanSave["battleground"] + CHAT_BATTLEGROUND_LEADER_GET = replace["battlegroundleader"] and (shortnames["battlegroundleader"] .. default) or chanSave["battlegroundleader"] + + CHAT_SAY_GET = replace["say"] and (shortnames["say"] .. default) or chanSave["say"] + CHAT_WHISPER_GET = replace["whisperincome"] and (shortnames["whisperincome"] .. default) or chanSave["whisperincome"] + CHAT_WHISPER_INFORM_GET = replace["whisper"] and (shortnames["whisper"] .. default) or chanSave["whisper"] + CHAT_YELL_GET = replace["yell"] and (shortnames["yell"] .. default) or chanSave["yell"] + +-- CHAT_AFK_GET = replace[""] and (shortnames["] .. "%s is Away From Keyboard" .. colon .. "\32"; + +-- CHAT_CHANNEL_JOIN_GET = replace[""] and (shortnames["] .. "%s joined channel."; -- another player joined +-- CHAT_CHANNEL_LEAVE_GET = replace[""] and (shortnames["] .. "%s left channel."; -- another player left +-- CHAT_CHANNEL_LIST_GET = replace[""] and (shortnames["] .. "[%s]\32"; -- Channel user list, user list is the "message", this is the channel name. + +-- CHAT_CHANNEL_SEND = "[%d. %s]" .. colon .. "\32"; + +-- CHAT_GUILD_SEND = "Guild" .. colon .. "\32"; -- Chat message to your guild +-- CHAT_OFFICER_SEND = "Officer" .. colon .. "\32"; -- Chat message to officers in your guild +-- CHAT_PARTY_SEND = "Party" .. colon .. "\32"; -- Chat message to your party +-- CHAT_RAID_SEND = "Raid" .. colon .. "\32"; -- Chat message to your raid +-- CHAT_RAID_WARNING_SEND = "Raid Warning" .. colon .. "\32"; +-- CHAT_BATTLEGROUND_SEND = "Battleground" .. colon .. "\32"; + +-- CHAT_SAY_SEND = "Say" .. colon .. "\32"; -- Normal chat message +-- CHAT_WHISPER_SEND = "Tell %s" .. colon .. "\32"; -- Whisper to a particular player +-- CHAT_YELL_SEND = "Yell" .. colon .. "\32"; -- Yell a message +end + +function Prat_ChannelNames:AddMessage(frame, text, ...) + self:doReplacement() + if text then + local pattern = "%]%s+(.*|Hplayer)" + local channel = string.gsub(text, ".*%[(.-)" .. pattern ..".+", "%1") + if string.find(channel, "%d+%. ") then + channel = string.gsub(channel, "(%d+)%..*", "channel%1") + pattern = "%[%d+%..-" .. pattern + end + if ( self.db.profile.replace[channel] ) then + local replacement = self.db.profile.shortnames[channel] + if replacement then + if ( (not(replacement == "")) and self.db.profile.space ) then replacement = replacement .. " " end + text = string.gsub(text, pattern, replacement .. "%1") + end + end + end + self.hooks[frame].AddMessage(frame, text, unpack(arg)) +end diff --git a/modules/ChannelSeparator.lua b/modules/ChannelSeparator.lua new file mode 100755 index 0000000..9e356d0 --- /dev/null +++ b/modules/ChannelSeparator.lua @@ -0,0 +1,105 @@ +--[[ +Name: Prat_ChannelSeparator +Revision: $Revision: 16019 $ +Author(s): Krtek (krtek4@gmail.com) +Website: http://www.wowace.com/files/index.php?path=Prat/ +Documentation: http://www.wowace.com/wiki/Prat/Integrated_Modules#ChannelSeparator +SVN: http://svn.wowace.com/wowace/trunk/Prat/ +Description: Module for Prat that separates chat channels in the chat setting menu. +Dependencies: Prat +]] + +local L = AceLibrary("AceLocale-2.2"):new("PratChannelSeparator") + +L:RegisterTranslations("enUS", function() return { + ["ChannelSeparator"] = true, + ["Separates chat channels in chat setting menu."] = true, + ["Toggle"] = true, + ["Toggle the module on and off."] = true, +} end) + +L:RegisterTranslations("ruRU", function() return { + ["ChannelSeparator"] = "ChannelSeparator", + ["Separates chat channels in chat setting menu."] = "Разделять каналы чата в меню настроек чата.", + ["Toggle"] = "Вкл/Выкл", + ["Toggle the module on and off."] = "Вкл/Выкл модуль.", +} end) + +L:RegisterTranslations("zhCN", function() return { + ["ChannelSeparator"] = "频道分离", + ["Separates chat channels in chat setting menu."] = "分离聊天频道设置菜单。", + ["Toggle"] = "切换", + ["Toggle the module on and off."] = "切换此模块的打开与关闭。", +} end) + +L:RegisterTranslations("koKR", function() return { + ["ChannelSeparator"] = "채널구분", + ["Separates chat channels in chat setting menu."] = "대화 설정 메뉴에 대화 채널을 구분합니다.", + ["Toggle"] = "전환", + ["Toggle the module on and off."] = "모듈 켜고 끄기 전환", +} end) + +Prat_ChannelSeparator = Prat:NewModule("channelseparator") + +function Prat_ChannelSeparator:OnInitialize() + self.OriginalChatTypeGroup = {} + self.OriginalChannelMenuChatTypeGroups = {} + self.db = Prat:AcquireDBNamespace("ChannelSeparator") + Prat:RegisterDefaults("ChannelSeparator", "profile", { + on = true, + }) + Prat.Options.args.channelseparator = { + name = L["ChannelSeparator"], + desc = L["Separates chat channels in chat setting menu."], + type = "group", + args = { + Toggle = { + type = "toggle", + name = L["Toggle"], + desc = L["Toggle the module on and off."], + get = function() return self.db.profile.on end, + set = function(v) self.db.profile.on = Prat:ToggleModuleActive("channelseparator") end + }, + } + } +end + +function Prat_ChannelSeparator:OnEnable() + self.OriginalChatTypeGroup = ChatTypeGroup + ChatTypeGroup["GUILD"] = { + "CHAT_MSG_GUILD", + "GUILD_MOTD", + }; + ChatTypeGroup["OFFICER"] = { + "CHAT_MSG_OFFICER", + }; + ChatTypeGroup["PARTY"] = { + "CHAT_MSG_PARTY", + }; + ChatTypeGroup["RAID"] = { + "CHAT_MSG_RAID", + "CHAT_MSG_RAID_LEADER", + "CHAT_MSG_RAID_WARNING", + }; + ChatTypeGroup["BATTLEGROUND"] = { + "CHAT_MSG_BATTLEGROUND", + "CHAT_MSG_BATTLEGROUND_LEADER", + }; + self.OriginalChannelMenuChatTypeGroups = ChannelMenuChatTypeGroups + ChannelMenuChatTypeGroups = {}; + ChannelMenuChatTypeGroups[1] = "SAY"; + ChannelMenuChatTypeGroups[2] = "YELL"; + ChannelMenuChatTypeGroups[3] = "GUILD"; + ChannelMenuChatTypeGroups[4] = "OFFICER"; + ChannelMenuChatTypeGroups[5] = "WHISPER"; + ChannelMenuChatTypeGroups[6] = "PARTY"; + ChannelMenuChatTypeGroups[7] = "RAID"; + ChannelMenuChatTypeGroups[8] = "BATTLEGROUND"; +end + +function Prat_ChannelSeparator:OnDisable() + ChatTypeGroup = {} + ChatTypeGroup = self.OriginalChatTypeGroup + ChannelMenuChatTypeGroups = {} + ChannelMenuChatTypeGroups = self.OriginalChannelMenuChatTypeGroups +end diff --git a/modules/ChatTabs.lua b/modules/ChatTabs.lua new file mode 100755 index 0000000..2a6da43 --- /dev/null +++ b/modules/ChatTabs.lua @@ -0,0 +1,410 @@ +--------------------------------------------------------------------------------- +-- +-- Prat - A framework for World of Warcraft chat mods +-- +-- Copyright (C) 2006-2007 Prat Development Team +-- +-- This program is free software; you can redistribute it and/or +-- modify it under the terms of the GNU General Public License +-- as published by the Free Software Foundation; either version 2 +-- of the License, or (at your option) any later version. +-- +-- This program is distributed in the hope that it will be useful, +-- but WITHOUT ANY WARRANTY; without even the implied warranty of +-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +-- GNU General Public License for more details. +-- +-- You should have received a copy of the GNU General Public License +-- along with this program; if not, write to: +-- +-- Free Software Foundation, Inc., +-- 51 Franklin Street, Fifth Floor, +-- Boston, MA 02110-1301, USA. +-- +-- +------------------------------------------------------------------------------- + + + +--[[ +Name: PratChatTabs +Revision: $Revision: 25896 $ +Author(s): Sylvanaar (sylvanaar@mindspring.com) +Website: http://www.wowace.com/files/index.php?path=Prat/ +Documentation: http://www.wowace.com/wiki/Prat/Integrated_Modules#ChatTabs +Subversion: http://svn.wowace.com/wowace/trunk/Prat/ +Discussions: http://groups.google.com/group/wow-prat +Issues and feature requests: http://code.google.com/p/prat/issues/list +Description: Module for Prat that adds chat window tab options. +Dependencies: Prat +]] + +-- set prat module name + +-- define localized strings +local L = AceLibrary("AceLocale-2.2"):new("PratChatTabs") + +L:RegisterTranslations("enUS", function() return { + ["ChatTabs"] = true, + ["Chat window tab options."] = true, + ["SetMode"] = true, + ["Toggle mode"] = true, + ["SetAll"] = true, + ["Set chat tabs for all windows."] = true, + ["Individual"] = true, + ["Toggle chat tabs for each window."] = true, + ["Chat%dTab"] = true, + ["Toggles chat tab options for chat window %d."] = true, + ["Active Alpha"] = true, + ["Sets alpha of chat tab for active chat frame."] = true, + ["Not Active Alpha"] = true, + ["Sets alpha of chat tab for not active chat frame."] = true, + ["All"] = true, + ["Individual"] = true, + ["Always Visible"] = true, + ["Hidden"] = true, + ["Default"] = true, + ["Flash Selected Tab"] = true, + ["Toggle flashing the active chat tab on whispers."] = true, + ["Toggle"] = true, + ["Toggle the module on and off."] = true, + ["Set chattab on login"] = true, + ["Set number of chattab to show on login."] = true, +} end) + +L:RegisterTranslations("ruRU", function() return { + ["ChatTabs"] = "Закладки", + ["Chat window tab options."] = "Настройки закладок окошек чата", + ["SetMode"] = "Установка мода", + ["Toggle mode"] = "Переключение мода", + ["SetAll"] = "Включить все", + ["Set chat tabs for all windows."] = "Установить для всех закладок", + ["Individual"] = "Индивидуально", + ["Toggle chat tabs for each window."] = "Установить для каждой закладки", + ["Chat%dTab"] = "Закладка%d", + ["Toggles chat tab options for chat window %d."] = "Переключить настройки закладки для окна %d", + ["Active Alpha"] = "Прозрачность Активного", + ["Sets alpha of chat tab for active chat frame."] = "Установить прозрачность закладки активного окна", + ["Not Active Alpha"] = "Прозрачность НеАктивного", + ["Sets alpha of chat tab for not active chat frame."] = "Установить прозрачность закладки не активного окна", + ["All"] = "Все", + ["Individual"] = "Индивидуально", + ["Always Visible"] = "Всегда видимое", + ["Hidden"] = "Скрытое", + ["Default"] = "По умолчанию", + ["Flash Selected Tab"] = "Мерцание", + ["Toggle flashing the active chat tab on whispers."] = "Переключение мерцания закладки при личных сообщениях", + ["Toggle"] = "Вкл/Выкл", + ["Toggle the module on and off."] = "Вкл/Выкл модуль.", + ["Set chattab on login"] = "Показать чат после входа", + ["Set number of chattab to show on login."] = "Установить номер чата который показать после входа", +} end) + +L:RegisterTranslations("koKR", function() return { + ["ChatTabs"] = "대화탭", + ["Chat window tab options."] = "대화탭 설정", + ["SetMode"] = "모드설정", + ["Toggle mode"] = "모드 전환", + ["SetAll"] = "모두설정", + ["Set chat tabs for all windows."] = "모든 창에대한 대화탭을 설정합니다.", + ["Individual"] = "개별", + ["Toggle chat tabs for each window."] = "각 창에 대한 대화 탭을 전환합니다.", + ["Chat%dTab"] = "대화%d탭", + ["Toggles chat tab options for chat window %d."] = "%d 대화창에 대한 대화탭 설정을 전환합니다.", + ["Active Alpha"] = "활성화 투명도", + ["Sets alpha of chat tab for active chat frame."] = "활성화된 대화창에 대한 대화탭의 투명도를 설정합니다.", + ["Not Active Alpha"] = "비활성화 투명도", + ["Sets alpha of chat tab for not active chat frame."] = "비활성화 대화창에 대한 대화탭의 투명도를 설정합니다.", + ["All"] = "모두", + ["Individual"] = "개별", + ["Always Visible"] = "항상 표시", + ["Hidden"] = "숨김", + ["Default"] = "기본값", + ["Flash Selected Tab"] = "선택된 탭 강조", + ["Toggle flashing the active chat tab on whispers."] = "귓속말시 활성화된 대화탭 강조를 전환합니다.", + ["Toggle"] = "전환", + ["Toggle the module on and off."] = "모듈 켜고 끄기를 전환합니다.", + ["Set chattab on login"] = true, + ["Set number of chattab to show on login."] = true, +} end) + +-- create prat module +Prat_ChatTabs = Prat:NewModule("tabs") +--Prat_ChatTabs.revision = tonumber(string.sub("$Revision: 25896 $", 12, -3)) + + +-- define the default db values +function Prat_ChatTabs:OnInitialize() + self.db = Prat:AcquireDBNamespace("ChatTabs") + Prat:RegisterDefaults("ChatTabs", "profile", { + on = false, + notactivealpha = 0.5, + activealpha = 1, + mode = "ALL", + cf = {"ALWAYS", "ALWAYS", "ALWAYS", "ALWAYS", "ALWAYS", "ALWAYS", "ALWAYS"}, + cfall = "ALWAYS", + flashselected = false, + showonlogin = 1, + }) + Prat.Options.args.tabs = { + name = L["ChatTabs"], + desc = L["Chat window tab options."], + type = "group", + args = { + mode = { + name = L["SetMode"], + desc = L["Toggle mode"], + type = "text", + order = 100, + get = function() return self.db.profile.mode end, + set = function(v) self.db.profile.mode = v self:OnUpdate() end, + validate = {["ALL"] = L["All"], ["INDIVIDUAL"] = L["Individual"]}, + }, + all = { + name = L["SetAll"], + desc = L["Set chat tabs for all windows."], + type = "text", + disabled = function() if self.db.profile.mode == "INDIVIDUAL" then return true else return false end end, + order = 110, + get = function() return self.db.profile.cfall end, + set = function(v) self.db.profile.cfall = v self:OnUpdate() end, + validate = {["ALWAYS"] = L["Always Visible"], ["HIDDEN"] = L["Hidden"]}, + }, + individual = { + name = L["Individual"], + desc = L["Toggle chat tabs for each window."], + type = "group", + disabled = function() if self.db.profile.mode == "ALL" then return true else return false end end, + order = 120, + args = { + chat1 = { + name = string.format(L["Chat%dTab"], 1), + desc = string.format(L["Toggles chat tab options for chat window %d."], 1), + type = "text", + get = function() return self.db.profile.cf[1] end, + set = function(v) + self.db.profile.cf[1] = v + self:OnUpdate() + if self.db.profile.cf[1] == "DEFAULT" then + getglobal("ChatFrame1Tab"):Hide() + end + end, + validate = {["ALWAYS"] = L["Always Visible"], ["HIDDEN"] = L["Hidden"], ["DEFAULT"] = L["Default"]}, + }, + chat2 = { + name = string.format(L["Chat%dTab"], 2), + desc = string.format(L["Toggles chat tab options for chat window %d."], 2), + type = "text", + get = function() return self.db.profile.cf[2] end, + set = function(v) + self.db.profile.cf[2] = v + self:OnUpdate() + if self.db.profile.cf[2] == "DEFAULT" then + getglobal("ChatFrame2Tab"):Hide() + end + end, + validate = {["ALWAYS"] = L["Always Visible"], ["HIDDEN"] = L["Hidden"], ["DEFAULT"] = L["Default"]}, + }, + chat3 = { + name = string.format(L["Chat%dTab"], 3), + desc = string.format(L["Toggles chat tab options for chat window %d."], 3), + type = "text", + get = function() return self.db.profile.cf[3] end, + set = function(v) + self.db.profile.cf[3] = v + self:OnUpdate() + if self.db.profile.cf[3] == "DEFAULT" then + getglobal("ChatFrame3Tab"):Hide() + end + end, + validate = {["ALWAYS"] = L["Always Visible"], ["HIDDEN"] = L["Hidden"], ["DEFAULT"] = L["Default"]}, + }, + chat4 = { + name = string.format(L["Chat%dTab"], 4), + desc = string.format(L["Toggles chat tab options for chat window %d."], 4), + type = "text", + get = function() return self.db.profile.cf[4] end, + set = function(v) + self.db.profile.cf[4] = v + self:OnUpdate() + if self.db.profile.cf[4] == "DEFAULT" then + getglobal("ChatFrame4Tab"):Hide() + end + end, + validate = {["ALWAYS"] = L["Always Visible"], ["HIDDEN"] = L["Hidden"], ["DEFAULT"] = L["Default"]}, + }, + chat5 = { + name = string.format(L["Chat%dTab"], 5), + desc = string.format(L["Toggles chat tab options for chat window %d."], 5), + type = "text", + get = function() return self.db.profile.cf[5] end, + set = function(v) + self.db.profile.cf[5] = v + self:OnUpdate() + if self.db.profile.cf[5] == "DEFAULT" then + getglobal("ChatFrame5Tab"):Hide() + end + end, + validate = {["ALWAYS"] = L["Always Visible"], ["HIDDEN"] = L["Hidden"], ["DEFAULT"] = L["Default"]}, + }, + chat6 = { + name = string.format(L["Chat%dTab"], 6), + desc = string.format(L["Toggles chat tab options for chat window %d."], 6), + type = "text", + get = function() return self.db.profile.cf[6] end, + set = function(v) + self.db.profile.cf[6] = v + self:OnUpdate() + if self.db.profile.cf[6] == "DEFAULT" then + getglobal("ChatFrame6Tab"):Hide() + end + end, + validate = {["ALWAYS"] = L["Always Visible"], ["HIDDEN"] = L["Hidden"], ["DEFAULT"] = L["Default"]}, + }, + chat7 = { + name = string.format(L["Chat%dTab"], 7), + desc = string.format(L["Toggles chat tab options for chat window %d."], 7), + type = "text", + get = function() return self.db.profile.cf[7] end, + set = function(v) + self.db.profile.cf[7] = v + self:OnUpdate() + if self.db.profile.cf[7] == "DEFAULT" then + getglobal("ChatFrame7Tab"):Hide() + end + end, + validate = {["ALWAYS"] = L["Always Visible"], ["HIDDEN"] = L["Hidden"], ["DEFAULT"] = L["Default"]}, + }, + }, + }, + activealpha = { + name = L["Active Alpha"], + desc = L["Sets alpha of chat tab for active chat frame."], + type = "range", + get = function() return self.db.profile.activealpha end, + set = function(v) self.db.profile.activealpha = v self:OnUpdate() end, + min = 0.1, + max = 1, + step = 0.1, + order = 200, + }, + notactivealpha = { + name = L["Not Active Alpha"], + desc = L["Sets alpha of chat tab for not active chat frame."], + type = "range", + get = function() return self.db.profile.notactivealpha end, + set = function(v) self.db.profile.notactivealpha = v self:OnUpdate() end, + min = 0.1, + max = 1, + step = 0.1, + order = 210, + }, + flashselected = { + name = L["Flash Selected Tab"], + desc = L["Toggle flashing the active chat tab on whispers."], + type = "toggle", + get = function() return self.db.profile.flashselected end, + set = function(v) self.db.profile.flashselected = v Prat_ChatTabs:HookFlash() end, + order = 211 + }, + showonlogin = { + name = L["Set chattab on login"], + desc = L["Set number of chattab to show on login."], + type = "range", + get = function() return self.db.profile.showonlogin end, + set = function(v) self.db.profile.showonlogin = v end, + min = 1, + max = 7, + step = 1, + order = 212, + }, + toggle = { + name = L["Toggle"], + desc = L["Toggle the module on and off."], + type = "toggle", + order = 400, + get = function() return self.db.profile.on end, + set = function() self.db.profile.on = Prat:ToggleModuleActive("tabs") end + } + } + } +end + +--[[------------------------------------------------ + Module Event Functions +------------------------------------------------]]-- + +-- things to do when the module is enabled +function Prat_ChatTabs:OnEnable() + self:SecureHook("FCF_OnUpdate", "OnUpdate") + self:HookFlash() + local frame = getglobal("ChatFrame"..(self.db.profile.showonlogin or 1)); + SELECTED_CHAT_FRAME = frame + FCF_SelectDockFrame(frame) + self:OnUpdate() +end + +-- things to do when the module is enabled +function Prat_ChatTabs:OnDisable() + if self:IsHooked("FCF_OnUpdate") then + self:Unhook("FCF_OnUpdate") + end +end + +--[[------------------------------------------------ + Core Functions +------------------------------------------------]]-- + +function Prat_ChatTabs:HookFlash() + if self.db.profile.flashselected then + if not self:IsHooked("FCF_FlashTab") then + self:SecureHook("FCF_FlashTab") + end + else + if self:IsHooked("FCF_FlashTab") then + self:Unhook("FCF_FlashTab") + end + end +end + +function Prat_ChatTabs:OnUpdate(elapsed) + if self.db.profile.on then + for i=1,NUM_CHAT_WINDOWS do + local cftab = getglobal("ChatFrame"..i.."Tab") + if getglobal("ChatFrame"..i):IsVisible() or getglobal("ChatFrame"..i).isDocked then + if self.db.profile.mode == "ALL" then + if SELECTED_CHAT_FRAME:GetID() == i then + cftab:SetAlpha(self.db.profile.activealpha) + else + cftab:SetAlpha(self.db.profile.notactivealpha) + end + if self.db.profile.cfall == "ALWAYS" then + cftab:Show() + elseif self.db.profile.cfall == "HIDDEN" then + cftab:Hide() + end + else + if SELECTED_CHAT_FRAME:GetID() == i then + cftab:SetAlpha(self.db.profile.activealpha) + else + cftab:SetAlpha(self.db.profile.notactivealpha) + end + if self.db.profile.cf[i] == "ALWAYS" then + cftab:Show() + elseif self.db.profile.cf[i] == "HIDDEN" then + cftab:Hide() + end + end + end + end + end +end + +function Prat_ChatTabs:FCF_FlashTab() + local tabFlash = getglobal(this:GetName().."TabFlash"); + if ( not this.isDocked or UIFrameIsFlashing(tabFlash) ) then + return; + end + tabFlash:Show(); + UIFrameFlash(tabFlash, 0.25, 0.25, 60, nil, 0.5, 0.5); +end diff --git a/modules/Editbox.lua b/modules/Editbox.lua new file mode 100755 index 0000000..26eab4a --- /dev/null +++ b/modules/Editbox.lua @@ -0,0 +1,312 @@ +--[[ +Name: Prat_Editbox +Revision: $Revision: 16019 $ +Author(s): Curney (asml8ed@gmail.com) + Krtek (krtek4@gmail.com) +Inspired by: idChat2_Editbox by Industrial +Website: http://www.wowace.com/files/index.php?path=Prat/ +Documentation: http://www.wowace.com/wiki/Prat/Integrated_Modules#Editbox +SVN: http://svn.wowace.com/wowace/trunk/Prat/ +Description: Module for Prat that adds editbox options. +Dependencies: Prat +]] + +local L = AceLibrary("AceLocale-2.2"):new("PratEditbox") + +L:RegisterTranslations("enUS", function() return { + ["Editbox"] = true, + ["Editbox options."] = true, + ["Alpha"] = true, + ["Set the alpha of the editbox."] = true, + ["Position"] = true, + ["Set position of editbox."] = true, + ["Width"] = true, + ["Set the width of the editbox."] = true, + ["Locked"] = true, + ["Lock editbox position is undocked."] = true, + ["Arrowkeys"] = true, + ["Toggle using arrowkeys in editbox without the alt key."] = true, + ["Sticky"] = true, + ["Toggle text clearing when editbox is closed."] = true, + ["Toggle"] = true, + ["Toggle the module on and off."] = true, +} end) + +L:RegisterTranslations("ruRU", function() return { + ["Editbox"] = "Editbox", + ["Editbox options."] = "Настройки поля ввода.", + ["Alpha"] = "Альфа", + ["Set the alpha of the editbox."] = "Установить альфа в поле ввода.", + ["Position"] = "Позиция", + ["Set position of editbox."] = "Установить положение поля ввода.", + ["Width"] = "Ширина", + ["Set the width of the editbox."] = "Установить ширину поля ввода.", + ["Locked"] = "Заблокировать", + ["Lock editbox position is undocked."] = "Поле ввода будет отстыковано.", + ["Arrowkeys"] = "Стрелки", + ["Toggle using arrowkeys in editbox without the alt key."] = "Переключение поля ввода с помощью стрелок, без кнопки alt.", + ["Sticky"] = "Липкий", + ["Toggle text clearing when editbox is closed."] = "Вкл/Выкл очистку текста когда поле ввода закрыто.", + ["Toggle"] = "Вкл/Выкл", + ["Toggle the module on and off."] = "Вкл/Выкл модуль", +} end) + +L:RegisterTranslations("zhCN", function() return { + ["Editbox"] = "输入框", + ["Editbox options."] = "输入框选项。", + ["Alpha"] = "透明度", + ["Set the alpha of the editbox."] = "设置输入框透明度。", + ["Position"] = "位置", + ["Set position of editbox."] = "设置输入框位置。", + ["Width"] = "宽度", + ["Set the width of the editbox."] = "设置输入框宽度。", + ["Locked"] = "锁定", + ["Lock editbox position is undocked."] = "锁定输入框位置。", + ["Arrowkeys"] = "方向键", + ["Toggle using arrowkeys in editbox without the alt key."] = "激活输入框使用方向键不带Alt键功能。", + ["Sticky"] = "粘滞", + ["Toggle text clearing when editbox is closed."] = "激活当聊天框关闭时清除文本。", + ["Toggle"] = "切换", + ["Toggle the module on and off."] = "切换此模块的打开与关闭", +} end) + +L:RegisterTranslations("koKR", function() return { + ["Editbox"] = "입력창", + ["Editbox options."] = "입력창 설정입니다.", + ["Alpha"] = "투명도", + ["Set the alpha of the editbox."] = "입력창의 투명도를 설정합니다.", + ["Position"] = "위치", + ["Set position of editbox."] = "입력창의 위치를 설정합니다.", + ["Width"] = "넓이", + ["Set the width of the editbox."] = "입력창의 넓이를 설정합니다.", + ["Locked"] = "고정", + ["Lock editbox position is undocked."] = "입력창 위치를 고정시킵니다.", + ["Arrowkeys"] = "방향키", + ["Toggle using arrowkeys in editbox without the alt key."] = " ALT키를 누르지 않고 입력창에 방향키 사용을 전환합니다.", + ["Sticky"] = "고정", + ["Toggle text clearing when editbox is closed."] = "입력창을 닫았을 때 텍스트 초기화를 전환합니다.", + ["Toggle"] = "전환", + ["Toggle the module on and off."] = "모듈 켜고 끄기를 전환합니다.", +} end) + +Prat_Editbox = Prat:NewModule("editbox") + +function Prat_Editbox:OnInitialize() + self.db = Prat:AcquireDBNamespace("Editbox") + Prat:RegisterDefaults("Editbox", "profile", { + on = true, + alpha = 1, + position = "TOP", + width = 400, + undocked = {point = "CENTER", relativeTo="UIParent", relativePoint = "CENTER", xoff = 0, yoff = 0}, + locked = false, + sticky = false, + arrowkeys = true + }) + Prat.Options.args.editbox = { + name = L["Editbox"], + desc = L["Editbox options."], + type = "group", + args = { + alpha = { + name = L["Alpha"], + desc = L["Set the alpha of the editbox."], + type = "range", + get = function() return self.db.profile.alpha end, + set = function(v) + self.db.profile.alpha = v + self:Alpha(v) + end, + min = 0, + max = 1, + step = 0.05, + order = 195 + }, + position = { + name = L["Position"], + desc = L["Set position of editbox."], + type = "text", + get = function() return self.db.profile.position end, + set = function(v) + self.db.profile.position = v + self:Position(v) + end, + validate = {["TOP"] = "Top", ["BOTTOM"] = "Bottom", ["UNDOCKED"] = "Undocked"}, + order = 196 + }, + width = { + name = L["Width"], + desc = L["Set the width of the editbox."], + type = "range", + get = function() return self.db.profile.width end, + set = function(v) + self.db.profile.width = v + self:Position(self.db.profile.position) + end, + min = 267, + max = 800, + step = 1, + disabled = function() if self.db.profile.position == "UNDOCKED" then return false else return true end end, + order = 197 + }, + locked = { + name = L["Locked"], + desc = L["Lock editbox position is undocked."], + type = "toggle", + get = function() return self.db.profile.locked end, + set = function(v) + self.db.profile.locked = v + self:Position(self.db.profile.position) + end, + disabled = function() if self.db.profile.position == "UNDOCKED" then return false else return true end end, + order = 198 + }, + arrowkeys = { + name = L["Arrowkeys"], + desc = L["Toggle using arrowkeys in editbox without the alt key."], + type = "toggle", + get = function() return self.db.profile.arrowkeys end, + set = function(v) + self.db.profile.arrowkeys = v + self:ArrowKeys(v) + end, + order = 199 + }, + sticky = { + name = L["Sticky"], + desc = L["Toggle text clearing when editbox is closed."], + type = "toggle", + get = function() return self.db.profile.sticky end, + set = function(v) + self.db.profile.sticky = v + self:Sticky(v) + end, + order = 200 + }, + toggle = { + name = L["Toggle"], + desc = L["Toggle the module on and off."], + type = "toggle", + get = function() return self.db.profile.on end, + set = function() self.db.profile.on = Prat:ToggleModuleActive("editbox") end, + order = 201 + } + } + } +end + +function Prat_Editbox:OnEnable() + self:Position(self.db.profile.position) + self:Alpha(self.db.profile.alpha) + self:ArrowKeys(self.db.profile.arrowkeys) + self:Sticky(self.db.profile.sticky) +end + +function Prat_Editbox:OnDisable() + self:Position("BOTTOM") + self:Alpha(1) + self:ArrowKeys(false) + self:Sticky(false) +end + +function Prat_Editbox:Alpha(value) + local eb = VisorEditbox or ChatFrameEditBox + for i,v in ipairs({eb:GetRegions()}) do + if i==6 or i==7 or i==8 then + v:SetAlpha(value) + end + end +end + +function Prat_Editbox:ArrowKeys(enabled) + local eb = VisorEditBox or ChatFrameEditBox + if enabled then eb:SetAltArrowKeyMode(false) else eb:SetAltArrowKeyMode(true) end +end + +function Prat_Editbox:Position(option) + local eb = VisorEditBox or ChatFrameEditBox + eb:ClearAllPoints() + if option == "TOP" then + eb:SetPoint("BOTTOMLEFT", "ChatFrame1", "TOPLEFT", -5, 0) + eb:SetPoint("BOTTOMRIGHT", "ChatFrame1", "TOPRIGHT", 5, 0) + end + if option == "BOTTOM" then + eb:SetPoint("TOPLEFT", "ChatFrame1", "BOTTOMLEFT", -5, 0) + eb:SetPoint("TOPRIGHT", "ChatFrame1", "BOTTOMRIGHT", 5, 0) + end + if option == "UNDOCKED" then + eb:SetMovable(true) + eb:EnableMouse(true) + eb:RegisterForDrag("LeftButton") + eb:SetScript("OnDragStart", function() if not self.db.profile.locked then eb:StartMoving() end end) + eb:SetScript("OnDragStop", function() + eb:StopMovingOrSizing() + local point, relativeTo, relativePoint, xoff, yoff = eb:GetPoint() + self.db.profile.undocked.point = point + -- self.db.profile.undocked.relativeTo = relativeTo:GetName() -- this causes game crash, keep commented out + self.db.profile.undocked.relativePoint = relativePoint + self.db.profile.undocked.xoff = xoff + self.db.profile.undocked.yoff = yoff + end) + eb:SetPoint(self.db.profile.undocked.point, self.db.profile.undocked.relativeTo, self.db.profile.undocked.relativePoint, self.db.profile.undocked.xoff, self.db.profile.undocked.yoff) + eb:SetWidth(self.db.profile.width) + end +end + +function Prat_Editbox:Sticky(enabled) + if not enabled then + DEFAULT_CHAT_FRAME.editBox:SetScript("OnEscapePressed", + function() ChatEdit_OnEscapePressed(this) end + ) + DEFAULT_CHAT_FRAME.editBox:SetScript("OnEnterPressed", + function() ChatEdit_OnEnterPressed() end + ) + self:UnhookAll() + else + DEFAULT_CHAT_FRAME.editBox:SetScript("OnEscapePressed", + function() + this.oldText = this:GetText() + this.oldChatType = this.chatType + ChatEdit_OnEscapePressed(this) + end + ) + DEFAULT_CHAT_FRAME.editBox:SetScript("OnEnterPressed", + function() + this.oldText = nil + this.oldChatType = nil + ChatEdit_OnEnterPressed() + end + ) + self:Hook("ChatFrame_OpenChat", + function(text, chatFrame) + local editBox = DEFAULT_CHAT_FRAME.editBox + if (not chatFrame or chatFrame == DEFAULT_CHAT_FRAME) and text == "" and + editBox.oldText and editBox.oldText ~= "" then + editBox:SetText(editBox.oldText) + editBox.chatType = editBox.oldChatType + editBox:Show() + else + self.hooks["ChatFrame_OpenChat"](text, chatFrame) + end + end + ) + self:Hook("ChatFrame_ReplyTell", + function(chatFrame) + if ( not chatFrame ) then + chatFrame = DEFAULT_CHAT_FRAME; + end + chatFrame.editBox.oldText = nil + self.hooks["ChatFrame_ReplyTell"](chatFrame) + end + ) + self:Hook("ChatFrame_ReplyTell2", + function(chatFrame) + if ( not chatFrame ) then + chatFrame = DEFAULT_CHAT_FRAME; + end + chatFrame.editBox.oldText = nil + self.hooks["ChatFrame_ReplyTell2"](chatFrame) + end + ) + end +end diff --git a/modules/Fading.lua b/modules/Fading.lua new file mode 100755 index 0000000..5e3a13a --- /dev/null +++ b/modules/Fading.lua @@ -0,0 +1,182 @@ +--[[ +Name: Prat_Fading +Revision: $Revision: 16019 $ +Author(s): Curney (asml8ed@gmail.com) + Krtek (krtek4@gmail.com) +Inspired by: idChat2_DisableFade by Industrial +Website: http://www.wowace.com/files/index.php?path=Prat/ +Documentation: http://www.wowace.com/wiki/Prat/Integrated_Modules#Fading +SVN: http://svn.wowace.com/wowace/trunk/Prat/ +Description: Module for Prat that adds text fading options for chat windows. +Dependencies: Prat +]] + +local L = AceLibrary("AceLocale-2.2"):new("PratFading") + +L:RegisterTranslations("enUS", function() return { + ["Fading"] = true, + ["Chat window text fading options."] = true, + ["TextFade"] = true, + ["Set text fading in chat windows on and off."] = true, + ["Chat%dFading"] = true, + ["Toggles fading on and off for chat window %d."] = true, + ["FadeDuration"] = true, + ["Set duration to show text before fading."] = true, + ["Toggle"] = true, + ["Toggle the module on and off."] = true, +} end) + +L:RegisterTranslations("ruRU", function() return { + ["Fading"] = "Fading", + ["Chat window text fading options."] = "Настройки затухания текста в окне чата.", + ["TextFade"] = "ЗатуханиеТекста", + ["Set text fading in chat windows on and off."] = "Вкл/Выкл затухание текста в окне чата.", + ["Chat%dFading"] = "ЗатуханиеТекста%d", + ["Toggles fading on and off for chat window %d."] = "Вкл/Выкл затухание в окне чата %d.", + ["FadeDuration"] = "ПродолжительностьЗатухания", + ["Set duration to show text before fading."] = "Установить продолжительность отображение текста, прежде чем они исчезнет.", + ["Toggle"] = "Вкл/Выкл", + ["Toggle the module on and off."] = "Вкл/Выкл модуль", +} end) + +L:RegisterTranslations("zhCN", function() return { + ["Fading"] = "淡出", + ["Chat window text fading options."] = "聊天窗口文本淡入淡出选项。", + ["TextFade"] = "文本淡出", + ["Set text fading in chat windows on and off."] = "设置聊天窗口是否使用文本淡出。", + ["Chat%dFading"] = "窗口 %d 淡出", + ["Toggles fading on and off for chat window %d."] = "切换聊天窗口 %d 文本淡出打开与关闭。", + ["FadeDuration"] = "时间", + ["Set duration to show text before fading."] = "设置聊天窗口淡出时间。", + ["Toggle"] = "切换", + ["Toggle the module on and off."] = "切换此模块打开与关闭。", +} end) + +L:RegisterTranslations("koKR", function() return { + ["Fading"] = "사라짐", + ["Chat window text fading options."] = "대화창 글자 사라짐 설정입니다.", + ["TextFade"] = "글자 사라짐", + ["Set text fading in chat windows on and off."] = "대화창에 글자가 사라지는 것을 켜고 끄기를 전환합니다.", + ["Chat%dFading"] = "대화창%d 사라짐", + ["Toggles fading on and off for chat window %d."] = "대화창 %d에 대한 사라짐 켜고 끄기를 전환합니다.", + ["FadeDuration"] = "사라짐 시간", + ["Set duration to show text before fading."] = "사라지기 전까지 글자를 표시할 시간을 설정합니다.", + ["Toggle"] = "전환", + ["Toggle the module on and off."] = "모듈 켜고 끄기를 전환합니다.", +} end) + +Prat_Fading = Prat:NewModule("fading") + +function Prat_Fading:OnInitialize() + self.db = Prat:AcquireDBNamespace("Fading") + Prat:RegisterDefaults("Fading", "profile", { + on = true, + normal = true, + textfade = {false, false, false, false, false, false, false}, + duration = 5 + }) + Prat.Options.args.fading = { + name = L["Fading"], + desc = L["Chat window text fading options."], + type = "group", + args = { + textfade = { + name = L["TextFade"], + desc = L["Set text fading in chat windows on and off."], + type = "group", + args = { + chat1 = { + name = string.format(L["Chat%dFading"], 1), + desc = string.format(L["Toggles fading on and off for chat window %d."], 1), + type = "toggle", + get = function() return self.db.profile.textfade[1] end, + set = function(v) self.db.profile.textfade[1] = v; self:Fade(1,v) end + }, + chat2 = { + name = string.format(L["Chat%dFading"], 1), + desc = string.format(L["Toggles fading on and off for chat window %d."], 1), + type = "toggle", + get = function() return self.db.profile.textfade[2] end, + set = function(v) self.db.profile.textfade[2] = v; self:Fade(2,v) end + }, + chat3 = { + name = string.format(L["Chat%dFading"], 1), + desc = string.format(L["Toggles fading on and off for chat window %d."], 1), + type = "toggle", + get = function() return self.db.profile.textfade[3] end, + set = function(v) self.db.profile.textfade[3] = v; self:Fade(3,v) end + }, + chat4 = { + name = string.format(L["Chat%dFading"], 1), + desc = string.format(L["Toggles fading on and off for chat window %d."], 1), + type = "toggle", + get = function() return self.db.profile.textfade[4] end, + set = function(v) self.db.profile.textfade[4] = v; self:Fade(4,v) end + }, + chat5 = { + name = string.format(L["Chat%dFading"], 1), + desc = string.format(L["Toggles fading on and off for chat window %d."], 1), + type = "toggle", + get = function() return self.db.profile.textfade[5] end, + set = function(v) self.db.profile.textfade[5] = v; self:Fade(5,v) end + }, + chat6 = { + name = string.format(L["Chat%dFading"], 1), + desc = string.format(L["Toggles fading on and off for chat window %d."], 1), + type = "toggle", + get = function() return self.db.profile.textfade[6] end, + set = function(v) self.db.profile.textfade[6] = v; self:Fade(6,v) end + }, + chat7 = { + name = string.format(L["Chat%dFading"], 1), + desc = string.format(L["Toggles fading on and off for chat window %d."], 1), + type = "toggle", + get = function() return self.db.profile.textfade[7] end, + set = function(v) self.db.profile.textfade[7] = v; self:Fade(7,v) end + } + } + }, + fadeduration = { + name = L["FadeDuration"], + desc = L["Set duration to show text before fading."], + type = "range", + order = 190, + get = function() return self.db.profile.duration end, + set = function(v) self.db.profile.duration = v end, + min = 1, + max = 60, + step = 1 + }, + toggle = { + name = L["Toggle"], + desc = L["Toggle the module on and off."], + type = "toggle", + order = 200, + get = function() return self.db.profile.on end, + set = function() self.db.profile.on = Prat:ToggleModuleActive("fading") end + } + } + } +end + +function Prat_Fading:OnEnable() + for i=1,NUM_CHAT_WINDOWS do + self:Fade(i,self.db.profile.textfade[i]) + end +end + +function Prat_Fading:OnDisable() + for i=1,7 do + self:Fade(i,self.db.profile.normal) + end +end + +function Prat_Fading:Fade(id,textfade) + local cf = getglobal("ChatFrame"..id) + if textfade then + cf:SetFading(1) + cf:SetFadeDuration(self.db.profile.duration) + else + cf:SetFading(nil) + end +end diff --git a/modules/FontSize.lua b/modules/FontSize.lua new file mode 100755 index 0000000..cf821e3 --- /dev/null +++ b/modules/FontSize.lua @@ -0,0 +1,250 @@ +--[[ +Name: Prat_FontSize +Revision: $Revision: 16019 $ +Author(s): Curney (asml8ed@gmail.com) +Inspired by: ChatFrameExtender by Satrina +Website: http://www.wowace.com/files/index.php?path=Prat/ +Documentation: http://www.wowace.com/wiki/Prat/Integrated_Modules#FontSize +SVN: http://svn.wowace.com/wowace/trunk/Prat/ +Description: Module for Prat that sets the font size for chat windows (default=12). +Dependencies: Prat +]] + +local L = AceLibrary("AceLocale-2.2"):new("PratFontSize") + +UNICODEFONT = "Interface\\AddOns\\Prat\\font\\WarSansTT-Bliz-500.ttf" + +L:RegisterTranslations("enUS", function() return { + ["FontSize"] = true, + ["Chat window font size options."] = true, + ["SetMode"] = true, + ["Choose mode for setting font sizes."] = true, + ["Set"] = true, + ["Set the font size for each window."] = true, + ["Chat%dFontSize"] = true, + ["Sets font size for chat window %d."] = true, + ["SetAll"] = true, + ["Sets font size for all chat windows."] = true, + ["Toggle"] = true, + ["Toggle the module on and off."] = true, + ["ChatFont"] = true, + ["Choose Default WOW font or UNICODE."] = true, +} end) + +L:RegisterTranslations("ruRU", function() return { + ["FontSize"] = "FontSize", + ["Chat window font size options."] = "Опции размера шрифта окна чата.", + ["SetMode"] = "УстановитьРежим", + ["Choose mode for setting font sizes."] = "Выберите режим для настройки размера шрифта.", + ["Set"] = "Установить", + ["Set the font size for each window."] = "Установить размер шрифта для всех окон.", + ["Chat%dFontSize"] = "РазмерШрифтаЧата%d", + ["Sets font size for chat window %d."] = "Установить размер шрифта для окна чата %d.", + ["SetAll"] = "УстановитьВсе", + ["Sets font size for all chat windows."] = "Устанавливает размер шрифта для всех окон чата.", + ["Toggle"] = "Вкл/Выкл", + ["Toggle the module on and off."] = "Вкл/Выкл модуль", + ["ChatFont"] = "Шрифт чата", + ["Choose Default WOW font or UNICODE."] = "Выберите каой шрифт использовать для чата - поумолчанию или Юникод", +} end) + +L:RegisterTranslations("zhCN", function() return { + ["FontSize"] = "字体尺寸", + ["Chat window font size options."] = "聊天窗口字体尺寸选项。", + ["SetMode"] = "设置模式", + ["Choose mode for setting font sizes."] = "选择设置字体尺寸模式。", + ["Set"] = "设置", + ["Set the font size for each window."] = "为每一聊天窗口设置字体尺寸。", + ["Chat%dFontSize"] = "窗口 %d 字体尺寸", + ["Sets font size for chat window %d."] = "设置聊天窗口 %d 字体尺寸。", + ["SetAll"] = "所有尺寸", + ["Sets font size for all chat windows."] = "设置所有聊天窗口字体尺寸。", + ["Toggle"] = "切换", + ["Toggle the module on and off."] = "切换此模块打开与关闭。", + ["ChatFont"] = "Шрифт чата", + ["Choose Default WOW font or UNICODE."] = "Выберите каой шрифт использовать для чата - поумолчанию или Юникод", +} end) + +L:RegisterTranslations("koKR", function() return { + ["FontSize"] = "글자크기", + ["Chat window font size options."] = "대화창 글자 크기를 설정합니다.", + ["SetMode"] = "모드설정", + ["Choose mode for setting font sizes."] = "글자 크기 설정에 대한 모드를 선택합니다.", + ["Set"] = "설정", + ["Set the font size for each window."] = "각 창에 대한 글자 크기를 설정합니다.", + ["Chat%dFontSize"] = "대화창%d 글자 크기", + ["Sets font size for chat window %d."] = "대화창 %d에 대한 글자 크기를 설정합니다.", + ["SetAll"] = "모두 설정", + ["Sets font size for all chat windows."] = "모든 대화창에 대한 글자 크기를 설정합니다.", + ["Toggle"] = "전환", + ["Toggle the module on and off."] = "모듈 켜고 끄기를 전환합니다.", + ["ChatFont"] = "Шрифт чата", + ["Choose Default WOW font or UNICODE."] = "Выберите каой шрифт использовать для чата - поумолчанию или Юникод", +} end) + +Prat_FontSize = Prat:NewModule("fontsize") + +function Prat_FontSize:OnInitialize() + self.db = Prat:AcquireDBNamespace("FontSize") + Prat:RegisterDefaults("FontSize", "profile", { + on = true, + sizemode = "INDIVIDUAL", + size = {12, 12, 12, 12, 12, 12, 12}, + sizeall = 12, + chatfont = "Fonts\\ARIALN.ttf", + }) + if not self.db.profile.chatfont then self.db.profile.chatfont = "Fonts\\ARIALN.ttf" end + NAMEPLATE_FONT = self.db.profile.chatfont + ChatFontNormal:SetFont(self.db.profile.chatfont, self.db.profile.sizeall, "") + Prat.Options.args.fontsize = { + name = L["FontSize"], + desc = L["Chat window font size options."], + type = "group", + args = { + setmode = { + name = L["SetMode"], + desc = L["Choose mode for setting font sizes."], + type = "text", + get = function() return self.db.profile.sizemode end, + set = function(v) self.db.profile.sizemode = v end, + validate = {["ALL"] = "All", ["INDIVIDUAL"] = "Individual"}, + order = 197 + }, + chatfont = { + name = L["ChatFont"], + desc = L["Choose Default WOW font or UNICODE."], + type = "text", + get = function() return self.db.profile.chatfont end, + set = function(v) self.db.profile.chatfont = v + NAMEPLATE_FONT = self.db.profile.chatfont + ChatFontNormal:SetFont(self.db.profile.chatfont, self.db.profile.sizeall, "") + end, + validate = {["Fonts\\ARIALN.ttf"] = "Default", [UNICODEFONT] = "UNICODE"}, + order = 196 + }, + set = { + name = L["Set"], + desc = L["Set the font size for each window."], + type = "group", + args = { + chat1 = { + name = string.format(L["Chat%dFontSize"], 1), + desc = string.format(L["Sets font size for chat window %d."], 1), + type = "range", + get = function() return self.db.profile.size[1] end, + set = function(v) self.db.profile.size[1] = v; self:SetSize(1,v) end, + min = 4, + max = 22, + step = 1 + }, + chat2 = { + name = string.format(L["Chat%dFontSize"], 2), + desc = string.format(L["Sets font size for chat window %d."], 2), + type = "range", + get = function() return self.db.profile.size[2] end, + set = function(v) self.db.profile.size[2] = v; self:SetSize(2,v) end, + min = 4, + max = 22, + step = 1 + }, + chat3 = { + name = string.format(L["Chat%dFontSize"], 3), + desc = string.format(L["Sets font size for chat window %d."], 3), + type = "range", + get = function() return self.db.profile.size[3] end, + set = function(v) self.db.profile.size[3] = v; self:SetSize(3,v) end, + min = 4, + max = 22, + step = 1 + }, + chat4 = { + name = string.format(L["Chat%dFontSize"], 4), + desc = string.format(L["Sets font size for chat window %d."], 4), + type = "range", + get = function() return self.db.profile.size[4] end, + set = function(v) self.db.profile.size[4] = v; self:SetSize(4,v) end, + min = 4, + max = 22, + step = 1 + }, + chat5 = { + name = string.format(L["Chat%dFontSize"], 5), + desc = string.format(L["Sets font size for chat window %d."], 5), + type = "range", + get = function() return self.db.profile.size[5] end, + set = function(v) self.db.profile.size[5] = v; self:SetSize(5,v) end, + min = 4, + max = 22, + step = 1 + }, + chat6 = { + name = string.format(L["Chat%dFontSize"], 6), + desc = string.format(L["Sets font size for chat window %d."], 6), + type = "range", + get = function() return self.db.profile.size[6] end, + set = function(v) self.db.profile.size[6] = v; self:SetSize(6,v) end, + min = 4, + max = 22, + step = 1 + }, + chat7 = { + name = string.format(L["Chat%dFontSize"], 7), + desc = string.format(L["Sets font size for chat window %d."], 7), + type = "range", + get = function() return self.db.profile.size[7] end, + set = function(v) self.db.profile.size[7] = v; self:SetSize(7,v) end, + min = 4, + max = 22, + step = 1 + } + }, + disabled = function() if self.db.profile.sizemode == "ALL" then return true else return false end end, + order = 198 + }, + setall = { + name = L["SetAll"], + desc = L["Sets font size for all chat windows."], + type = "range", + get = function() return self.db.profile.sizeall end, + set = function(v) + self.db.profile.sizeall = v + for i=1,NUM_CHAT_WINDOWS do + self.db.profile.size[i] = v + self:SetSize(i,self.db.profile.size[i]) + end + end, + min = 4, + max = 22, + step = 1, + disabled = function() if self.db.profile.sizemode == "INDIVIDUAL" then return true else return false end end, + order = 199 + }, + toggle = { + name = L["Toggle"], + desc = L["Toggle the module on and off."], + type = "toggle", + get = function() return self.db.profile.on end, + set = function() self.db.profile.on = Prat:ToggleModuleActive("fontsize") end, + order = 200 + } + } + } +end + +function Prat_FontSize:OnEnable() + for i=1,NUM_CHAT_WINDOWS do + self:SetSize(i,self.db.profile.size[i]) + end +end + +function Prat_FontSize:OnDisable() + for i=1,NUM_CHAT_WINDOWS do + self:SetSize(i,12) + end +end + +function Prat_FontSize:SetSize(id, size) + local fontFile, unused, fontFlags = getglobal("ChatFrame"..id):GetFont(); + getglobal("ChatFrame"..id):SetFont(fontFile, size, fontFlags); + SetChatWindowSize(getglobal("ChatFrame"..id):GetID(), size); +end diff --git a/modules/Highlight.lua b/modules/Highlight.lua new file mode 100755 index 0000000..6e39085 --- /dev/null +++ b/modules/Highlight.lua @@ -0,0 +1,85 @@ +--[[ +Name: Prat_Highlight +Revision: $Revision: 16019 $ +Author(s): Curney (asml8ed@gmail.com) + Krtek (krtek4@gmail.com) +Website: http://www.wowace.com/files/index.php?path=Prat/ +Documentation: http://www.wowace.com/wiki/Prat/Integrated_Modules#Highlight +SVN: http://svn.wowace.com/wowace/trunk/Prat/ +Description: Module for Prat that adds options for highlighting text in chat windows. +Dependencies: Prat +]] + +local L = AceLibrary("AceLocale-2.2"):new("PratHighlight") + +L:RegisterTranslations("enUS", function() return { + ["Highlight"] = true, + ["Text highlighting options."] = true, + ["Toggle"] = true, + ["Toggle the module on and off."] = true, +} end) + +L:RegisterTranslations("ruRU", function() return { + ["Highlight"] = "Highlight", + ["Text highlighting options."] = "Опции подсветки текста.", + ["Toggle"] = "Вкл/Выкл", + ["Toggle the module on and off."] = "Вкл/Выкл модуль", +} end) + +L:RegisterTranslations("zhCN", function() return { + ["Highlight"] = "高亮", + ["Text highlighting options."] = "文本高亮选项。", + ["Toggle"] = "切换", + ["Toggle the module on and off."] = "切换此模块的打开与关闭。", +} end) + +L:RegisterTranslations("koKR", function() return { + ["Highlight"] = "강조", + ["Text highlighting options."] = "텍스트 강조 설정입니다.", + ["Toggle"] = "전환", + ["Toggle the module on and off."] = "모듈 켜고 끄기를 전환합니다.", +} end) + +Prat_Highlight = Prat:NewModule("highlight") + +function Prat_Highlight:OnInitialize() + self.db = Prat:AcquireDBNamespace("Highlight") + Prat:RegisterDefaults("Highlight", "profile", { + on = true + }) + Prat.Options.args.highlight = { + name = L["Highlight"], + desc = L["Text highlighting options."], + type = "group", + args = { + toggle = { + type = "toggle", + name = L["Toggle"], + desc = L["Toggle the module on and off."], + order = 200, + get = function() return self.db.profile.on end, + set = function(v) self.db.profile.on = Prat:ToggleModuleActive("highlight") end + }, + } + } +end + + +function Prat_Highlight:OnEnable() + for i=1,NUM_CHAT_WINDOWS do + self:Hook(getglobal("ChatFrame"..i), "AddMessage") + end +end + +-- Hook +function Prat_Highlight:AddMessage(frame, text, ...) + if text then + text = Prat_Highlight:ProcessText (text) + end + self.hooks[frame].AddMessage(frame, text, unpack(arg)) +end + +-- Utility Function +function Prat_Highlight:ProcessText(text) + return text +end diff --git a/modules/History.lua b/modules/History.lua new file mode 100755 index 0000000..5bd2849 --- /dev/null +++ b/modules/History.lua @@ -0,0 +1,177 @@ +--[[ +Name: Prat_History +Revision: $Revision: 16026 $ +Author(s): Krtek (krtek4@gmail.com) +Website: http://www.wowace.com/files/index.php?path=Prat/ +Documentation: http://www.wowace.com/wiki/Prat/Integrated_Modules#History +SVN: http://svn.wowace.com/wowace/trunk/Prat/ +Description: Module for Prat that adds chat history options. +Dependencies: Prat +]] + +local L = AceLibrary("AceLocale-2.2"):new("PratHistory") + +L:RegisterTranslations("enUS", function() return { + ["History"] = true, + ["Chat history options."] = true, + ["Set"] = true, + ["Set number of History lines for each window."] = true, + ["Chat%dHistory"] = true, + ["Sets history for chat window %d."] = true, + ["Toggle"] = true, + ["Toggle the module on and off."] = true, +} end) + +L:RegisterTranslations("ruRU", function() return { + ["History"] = "History", + ["Chat history options."] = "Настройки истории чата.", + ["Set"] = "Установить", + ["Set number of History lines for each window."] = "Установить количество строк истории для каждого окна.", + ["Chat%dHistory"] = "ИсторияЧата%d", + ["Sets history for chat window %d."] = "Установить историю для окна чата %d.", + ["Toggle"] = "Вкл/Выкл", + ["Toggle the module on and off."] = "Вкл/Выкл модуль.", +} end) + +L:RegisterTranslations("zhCN", function() return { + ["History"] = "历史", + ["Chat history options."] = "聊天历史选项。", + ["Set"] = "设置", + ["Set number of History lines for each window."] = "为每一聊天窗口设置历史记录行数.", + ["Chat%dHistory"] = "窗口%d历史", + ["Sets history for chat window %d."] = "设置聊天窗口%d聊天历史记录", + ["Toggle"] = "切换", + ["Toggle the module on and off."] = "切换此模块的打开与关闭。", +} end) + +L:RegisterTranslations("koKR", function() return { + ["History"] = "이력", + ["Chat history options."] = "대화 이력 설정입니다.", + ["Set"] = "설정", + ["Set number of History lines for each window."] = "각 창에 대한 이력 라인수를 설정합니다.", + ["Chat%dHistory"] = "대화창%d 이력", + ["Sets history for chat window %d."] = "대화창 %d에 대한 이력을 설정합니다.", + ["Toggle"] = "전환", + ["Toggle the module on and off."] = "모듈 켜고 끄기를 전환합니다.", +} end) + +Prat_History = Prat:NewModule("history") + +function Prat_History:OnInitialize() + self.db = Prat:AcquireDBNamespace("History") + Prat:RegisterDefaults("History", "profile", { + on = true, + chatlines = {128, 128, 128, 128, 128, 128, 128, 128}, + }) + Prat.Options.args.history = { + name = L["History"], + desc = L["Chat history options."], + type = "group", + args = { + set = { + name = L["Set"], + desc = L["Set number of History lines for each window."], + type = "group", + args = { + chat1 = { + name = string.format(L["Chat%dHistory"], 1), + desc = string.format(L["Sets history for chat window %d."], 1), + type = "range", + min = 50, + max = 500, + step = 1, + get = function() return self.db.profile.chatlines[1] end, + set = function(v) Prat_History:SetHistory(1, v) end, + }, + chat2 = { + name = string.format(L["Chat%dHistory"], 2), + desc = string.format(L["Sets history for chat window %d."], 2), + type = "range", + min = 50, + max = 500, + step = 1, + get = function() return self.db.profile.chatlines[2] end, + set = function(v) Prat_History:SetHistory(2, v) end, + }, + chat3 = { + name = string.format(L["Chat%dHistory"], 3), + desc = string.format(L["Sets history for chat window %d."], 3), + type = "range", + min = 50, + max = 500, + step = 1, + get = function() return self.db.profile.chatlines[3] end, + set = function(v) Prat_History:SetHistory(3, v) end, + }, + chat4 = { + name = string.format(L["Chat%dHistory"], 4), + desc = string.format(L["Sets history for chat window %d."], 4), + type = "range", + min = 50, + max = 500, + step = 1, + get = function() return self.db.profile.chatlines[4] end, + set = function(v) Prat_History:SetHistory(4, v) end, + }, + chat5 = { + name = string.format(L["Chat%dHistory"], 5), + desc = string.format(L["Sets history for chat window %d."], 5), + type = "range", + min = 50, + max = 500, + step = 1, + get = function() return self.db.profile.chatlines[5] end, + set = function(v) Prat_History:SetHistory(5, v) end, + }, + chat6 = { + name = string.format(L["Chat%dHistory"], 6), + desc = string.format(L["Sets history for chat window %d."], 6), + type = "range", + min = 50, + max = 500, + step = 1, + get = function() return self.db.profile.chatlines[6] end, + set = function(v) Prat_History:SetHistory(6, v) end, + }, + chat7 = { + name = string.format(L["Chat%dHistory"],7), + desc = string.format(L["Sets history for chat window %d."], 7), + type = "range", + min = 50, + max = 500, + step = 1, + get = function() return self.db.profile.chatlines[7] end, + set = function(v) Prat_History:SetHistory(7, v) end, + }, + } + }, + toggle = { + name = L["Toggle"], + desc = L["Toggle the module on and off."], + type = "toggle", + order = 200, + get = function() return self.db.profile.on end, + set = function(v) self.db.profile.on = Prat:ToggleModuleActive("history") end + } + } + } +end + +function Prat_History:OnEnable() + for i=1,NUM_CHAT_WINDOWS do + self:SetHistory(i,self.db.profile.chatlines[i]) + end +end + +function Prat_History:OnDisable() + for i=1,NUM_CHAT_WINDOWS do + self:SetHistory(i,128) + end +end + +function Prat_History:SetHistory(id, lines) + if lines ~= self.db.profile.chatlines[id] then + getglobal("ChatFrame"..id):SetMaxLines(lines) + self.db.profile.chatlines[id] = lines + end +end diff --git a/modules/Justify.lua b/modules/Justify.lua new file mode 100755 index 0000000..993d767 --- /dev/null +++ b/modules/Justify.lua @@ -0,0 +1,163 @@ +--[[ +Name: Prat_Justify +Revision: $Revision: 16019 $ +Author(s): Curney (asml8ed@gmail.com) +Thanks to: Wubin for helping me understand variables better +Website: http://www.wowace.com/files/index.php?path=Prat/ +Documentation: http://www.wowace.com/wiki/Prat/Integrated_Modules#Justify +SVN: http://svn.wowace.com/wowace/trunk/Prat/ +Description: Module for Prat that sets the justification for chat windows (default=Left). +Dependencies: Prat +]] + +local L = AceLibrary("AceLocale-2.2"):new("PratJustify") + +L:RegisterTranslations("enUS", function() return { + ["Justify"] = true, + ["Chat window justification options."] = true, + ["Set"] = true, + ["Set justify for each window."] = true, + ["Chat%dJustify"] = true, + ["Sets justification for chat window %d."] = true, + ["Toggle"] = true, + ["Toggle the module on and off."] = true, +} end) + +L:RegisterTranslations("ruRU", function() return { + ["Justify"] = "Justify", + ["Chat window justification options."] = "Опции выравнивания окна чата.", + ["Set"] = "Установить", + ["Set justify for each window."] = "Установить выравнивание для каждого окна.", + ["Chat%dJustify"] = "ВыравниваниеЧата%d", + ["Sets justification for chat window %d."] = "Установлено выравнивание окна чата %d.", + ["Toggle"] = "Вкл/Выкл", + ["Toggle the module on and off."] = "Вкл/Выкл модуль.", +} end) + +L:RegisterTranslations("zhCN", function() return { + ["Justify"] = "对齐", + ["Chat window justification options."] = "聊天窗口对齐模式.", + ["Set"] = "设置", + ["Set justify for each window."] = "为每一个聊天窗口设置对齐模式.", + ["Chat%dJustify"] = "窗口%d对齐", + ["Sets justification for chat window %d."] = "设置聊天窗口%d对齐模式.", + ["Toggle"] = "切换", + ["Toggle the module on and off."] = "切换此模块的打开与关闭。", +} end) + +L:RegisterTranslations("koKR", function() return { + ["Justify"] = "정렬", + ["Chat window justification options."] = "대화창 정렬 설정", + ["Set"] = "설정", + ["Set justify for each window."] = "각 창에 대한 정렬 설정", + ["Chat%dJustify"] = "대화창%d 정렬", + ["Sets justification for chat window %d."] = "대화창 %d에 대한 정렬을 설정합니다.", + ["Toggle"] = "전환", + ["Toggle the module on and off."] = "모듈 켜고 끄기 전환", +} end) + +Prat_Justify = Prat:NewModule("justify") + +function Prat_Justify:OnInitialize() + self.db = Prat:AcquireDBNamespace("Justify") + Prat:RegisterDefaults("Justify", "profile", { + on = true, + justification = {"LEFT", "LEFT", "LEFT", "LEFT", "LEFT", "LEFT", "LEFT"} + }) + Prat.Options.args.justify = { + name = L["Justify"], + desc = L["Chat window justification options."], + type = "group", + args = { + set = { + name = L["Set"], + desc = L["Set justify for each window."], + type = "group", + order = 150, + args = { + chat1 = { + name = string.format(L["Chat%dJustify"], 1), + desc = string.format(L["Sets justification for chat window %d."], 1), + type = "text", + get = function() return self.db.profile.justification[1] end, + set = function(v) self.db.profile.justification[1] = v; self:SetJustify(1,v) end, + validate = {["LEFT"] = "Left", ["RIGHT"] = "Right", ["CENTER"] = "Center"} + }, + chat2 = { + name = string.format(L["Chat%dJustify"], 2), + desc = string.format(L["Sets justification for chat window %d."], 2), + type = "text", + get = function() return self.db.profile.justification[2] end, + set = function(v) self.db.profile.justification[2] = v; self:SetJustify(2,v) end, + validate = {["LEFT"] = "Left", ["RIGHT"] = "Right", ["CENTER"] = "Center"} + }, + chat3 = { + name = string.format(L["Chat%dJustify"], 3), + desc = string.format(L["Sets justification for chat window %d."], 3), + type = "text", + get = function() return self.db.profile.justification[3] end, + set = function(v) self.db.profile.justification[3] = v; self:SetJustify(3,v) end, + validate = {["LEFT"] = "Left", ["RIGHT"] = "Right", ["CENTER"] = "Center"} + }, + chat4 = { + name = string.format(L["Chat%dJustify"], 4), + desc = string.format(L["Sets justification for chat window %d."], 4), + type = "text", + get = function() return self.db.profile.justification[4] end, + set = function(v) self.db.profile.justification[4] = v; self:SetJustify(4,v) end, + validate = {["LEFT"] = "Left", ["RIGHT"] = "Right", ["CENTER"] = "Center"} + }, + chat5 = { + name = string.format(L["Chat%dJustify"], 5), + desc = string.format(L["Sets justification for chat window %d."], 5), + type = "text", + get = function() return self.db.profile.justification[5] end, + set = function(v) self.db.profile.justification[5] = v; self:SetJustify(5,v) end, + validate = {["LEFT"] = "Left", ["RIGHT"] = "Right", ["CENTER"] = "Center"} + }, + chat6 = { + name = string.format(L["Chat%dJustify"], 6), + desc = string.format(L["Sets justification for chat window %d."], 6), + type = "text", + get = function() return self.db.profile.justification[6] end, + set = function(v) self.db.profile.justification[6] = v; self:SetJustify(6,v) end, + validate = {["LEFT"] = "Left", ["RIGHT"] = "Right", ["CENTER"] = "Center"} + }, + chat7 = { + name = string.format(L["Chat%dJustify"], 7), + desc = string.format(L["Sets justification for chat window %d."], 7), + type = "text", + get = function() return self.db.profile.justification[7] end, + set = function(v) self.db.profile.justification[7] = v; self:SetJustify(7,v) end, + validate = {["LEFT"] = "Left", ["RIGHT"] = "Right", ["CENTER"] = "Center"} + } + } + }, + toggle = { + name = L["Toggle"], + desc = L["Toggle the module on and off."], + type = "toggle", + order = 200, + get = function() return self.db.profile.on end, + set = function() self.db.profile.on = Prat:ToggleModuleActive("justify") end + } + } + } +end + +function Prat_Justify:OnEnable() + for i=1,NUM_CHAT_WINDOWS do + self:SetJustify(i,self.db.profile.justification[i]) + end +end + +function Prat_Justify:OnDisable() + for i=1,NUM_CHAT_WINDOWS do + self:SetJustify(i,"LEFT") + end +end + +function Prat_Justify:SetJustify(id, justification) + local cf = getglobal("ChatFrame"..id) + cf:SetJustifyH(justification) +end diff --git a/modules/Keybindings.lua b/modules/Keybindings.lua new file mode 100755 index 0000000..379dd3a --- /dev/null +++ b/modules/Keybindings.lua @@ -0,0 +1,104 @@ +--[[ +Name: Prat_KeyBindings +Revision: $Revision: 16019 $ +Author(s): Curney (asml8ed@gmail.com) + Krtek (krtek4@gmail.com) +Website: http://www.wowace.com/files/index.php?path=Prat/ +Documentation: http://www.wowace.com/wiki/Prat/Integrated_Modules#KeyBindings +SVN: http://svn.wowace.com/wowace/trunk/Prat/ +Description: Module for Prat that adds keybindings for modules. +Dependencies: Prat +]] + +local L = AceLibrary("AceLocale-2.2"):new("PratKeyBindings") + +L:RegisterTranslations("enUS", function() return { + ["Prat ChannelNames"] = true, + ["Officer Channel"] = true, + ["Guild Channel"] = true, + ["Party Channel"] = true, + ["Raid Channel"] = true, + ["Raid Warning Channel"] = true, + ["Battleground Channel"] = true, + ["Say"] = true, + ["Yell"] = true, + ["Whisper"] = true, + ["Channel %d"] = true, + ["Prat TellTarget"] = true, + ["TellTarget"] = true, +} end) + +L:RegisterTranslations("ruRU", function() return { + ["Prat ChannelNames"] = "Prat ChannelNames", + ["Officer Channel"] = "Офицерский Канал", + ["Guild Channel"] = "Канал Гильдии", + ["Party Channel"] = "Канал Группы", + ["Raid Channel"] = "Канал Рейда", + ["Raid Warning Channel"] = "Канал объявлений рейду", + ["Battleground Channel"] = "Канал поля боя", + ["Say"] = "Сказать", + ["Yell"] = "Крикнуть", + ["Whisper"] = "Шепнуть", + ["Channel %d"] = "Канал %d", + ["Prat TellTarget"] = "Prat Сказать цели", + ["TellTarget"] = "Сказать цели", +} end) + +L:RegisterTranslations("zhCN", function() return { + ["Prat ChannelNames"] = "Prat 频道名称", + ["Officer Channel"] = "官员频道", + ["Guild Channel"] = "公会频道", + ["Party Channel"] = "队伍频道", + ["Raid Channel"] = "团队频道", + ["Raid Warning Channel"] = "团队警报频道", + ["Battleground Channel"] = "战场频道", + ["Say"] = "说", + ["Yell"] = "喊", + ["Whisper"] = "密语", + ["Channel %d"] = "频道 %d", + ["Prat TellTarget"] = "Prat 通知目标", + ["TellTarget"] = "通知目标", +} end) + +L:RegisterTranslations("koKR", function() return { + ["Prat ChannelNames"] = "Prat 채널명", + ["Officer Channel"] = "오피서 채널", + ["Guild Channel"] = "길드 채널", + ["Party Channel"] = "파티 채널", + ["Raid Channel"] = "공격대 채널", + ["Raid Warning Channel"] = "공격대경보 채널", + ["Battleground Channel"] = "전장 채널", + ["Say"] = "일반대화", + ["Yell"] = "외치기", + ["Whisper"] = "귓속말", + ["Channel %d"] = "채널 %d", + ["Prat TellTarget"] = "Prat 대상대화", + ["TellTarget"] = "대상대화", +} end) + +Prat_Keybindings = Prat:NewModule("keybindings") + +function Prat_Keybindings:OnInitialize() + BINDING_HEADER_Prat_ChannelNames = L["Prat ChannelNames"] + BINDING_NAME_officer = L["Officer Channel"] + BINDING_NAME_guild = L["Guild Channel"] + BINDING_NAME_party = L["Party Channel"] + BINDING_NAME_raid = L["Raid Channel"] + BINDING_NAME_raidwarn = L["Raid Warning Channel"] + BINDING_NAME_battleground = L["Battleground Channel"] + BINDING_NAME_say = L["Say"] + BINDING_NAME_yell = L["Yell"] + BINDING_NAME_whisper = L["Whisper"] + BINDING_NAME_one = string.format(L["Channel %d"], 1) + BINDING_NAME_two = string.format(L["Channel %d"], 2) + BINDING_NAME_three = string.format(L["Channel %d"], 3) + BINDING_NAME_four = string.format(L["Channel %d"], 4) + BINDING_NAME_five = string.format(L["Channel %d"], 5) + BINDING_NAME_six = string.format(L["Channel %d"], 6) + BINDING_NAME_seven = string.format(L["Channel %d"], 7) + BINDING_NAME_eight = string.format(L["Channel %d"], 8) + BINDING_NAME_nine = string.format(L["Channel %d"], 9) + BINDING_NAME_ten = string.format(L["Channel %d"], 10) + BINDING_HEADER_Prat_TellTarget = L["Prat TellTarget"] + BINDING_NAME_TellTarget = L["TellTarget"] +end diff --git a/modules/Notification.lua b/modules/Notification.lua new file mode 100755 index 0000000..c01c778 --- /dev/null +++ b/modules/Notification.lua @@ -0,0 +1,71 @@ +--[[ +Name: Prat_Notification +Revision: $Revision: 16019 $ +Author(s): Curney (asml8ed@gmail.com) + Krtek (krtek4@gmail.com) +Website: http://www.wowace.com/files/index.php?path=Prat/ +Documentation: http://www.wowace.com/wiki/Prat/Integrated_Modules#Notification +SVN: http://svn.wowace.com/wowace/trunk/Prat/ +Description: Module for Prat that adds options for chat channel notification. +Dependencies: Prat +]] + +local L = AceLibrary("AceLocale-2.2"):new("PratNotification") + +L:RegisterTranslations("enUS", function() return { + ["Notification"] = true, + ["Chat channel notification options."] = true, + ["Toggle"] = true, + ["Toggle the module on and off."] = true, +} end) + +L:RegisterTranslations("ruRU", function() return { + ["Notification"] = "Notification", + ["Chat channel notification options."] = "Опции уведомления канала чата.", + ["Toggle"] = "Вкл/Выкл", + ["Toggle the module on and off."] = "Вкл/Выкл модуль", +} end) + +L:RegisterTranslations("zhCN", function() return { + ["Notification"] = "通告", + ["Chat channel notification options."] = "聊天频道通告选项。", + ["Toggle"] = "切换", + ["Toggle the module on and off."] = "切换此模块打开与关闭。", +} end) + +L:RegisterTranslations("koKR", function() return { + ["Notification"] = "알림", + ["Chat channel notification options."] = "대화 채널 알림 설정입니다.", + ["Toggle"] = "전환", + ["Toggle the module on and off."] = "모듈 켜고 끄기를 전환합니다.", +} end) + +Prat_Notification = Prat:NewModule("notification") + +function Prat_Notification:OnInitialize() + self.db = Prat:AcquireDBNamespace("Notification") + Prat:RegisterDefaults("Notification", "profile", { + on = true + }) + Prat.Options.args.highlight = { + name = L["Notification"], + desc = L["Chat channel notification options."], + type = "group", + args = { + toggle = { + type = "toggle", + name = L["Toggle"], + desc = L["Toggle the module on and off."], + order = 200, + get = function() return self.db.profile.on end, + set = function(v) self.db.profile.on = Prat:ToggleModuleActive("notification") end + }, + } + } +end + +function Prat_Notification:OnEnable() +end + +function Prat_Notification:OnDisable() +end diff --git a/modules/PlayerNames.lua b/modules/PlayerNames.lua new file mode 100755 index 0000000..53205ee --- /dev/null +++ b/modules/PlayerNames.lua @@ -0,0 +1,574 @@ +--[[ +Name: Prat_PlayerNames +Revision: $Revision: 16689 $ +Author(s): Curney (asml8ed@gmail.com) + Krtek (krtek4@gmail.com) +Inspired by: idChat2_PlayerNames by Industrial +Website: http://www.wowace.com/files/index.php?path=Prat/ +Documentation: http://www.wowace.com/wiki/Prat/Integrated_Modules#PlayerNames +SVN: http://svn.wowace.com/wowace/trunk/Prat/ +Description: Module for Prat that adds player name options. +Dependencies: Prat +]] + +local L = AceLibrary("AceLocale-2.2"):new("PratPlayerNames") + +L:RegisterTranslations("enUS", function() return { + ["PlayerNames"] = true, + ["Player name formating options."] = true, + ["Brackets"] = true, + ["Square"] = true, + ["Angled"] = true, + ["None"] = true, + ["Class"] = true, + ["Random"] = true, + ["Sets style of brackets to use around player names."] = true, + ["Use common color"] = true, + ["Toggle useing common color for unknown player names."] = true, + ["Set common color"] = true, + ["Change the common color of PlayerNames"] = true, + ["Use TabComplete"] = true, + ["Toggle tab completion of player names."] = true, + ["Show Level"] = true, + ["Toggle level showing."] = true, + ["Show Group"] = true, + ["Toggle raid group showing."] = true, + ["Keep Info"] = true, + ["Keep player information between session."] = true, + ["Color Mode"] = true, + ["How to color Players name."] = true, + ["Toggle"] = true, + ["Toggle the module on and off."] = true, + ["Use Census+"] = true, + ["Use Census+ database"] = true, + ["Mark self"] = true, + ["Toggle color marking of your nikname in chat."] = true, + ["Set mark color"] = true, + ["Change the mark color of your nikname"] = true, + ["Group acronim"] = true, + ["Simbol of raid group added to group number"] = true , + ["Enable group number at %s"] = true, +} end) + +L:RegisterTranslations("ruRU", function() return { + ["PlayerNames"] = "PlayerNames", + ["Player name formating options."] = "Настройки форматирования имени собеседника.", + ["Brackets"] = "Скобки", + ["Square"] = "Квадратные", + ["Angled"] = "Треугольные", + ["None"] = "Нет", + ["Class"] = "Класс", + ["Random"] = "Случайно", + ["Sets style of brackets to use around player names."] = "Установить стиль скобок вокруг имени собеседника.", + ["Use common color"] = "Использовать общий цвет", + ["Toggle useing common color for unknown player names."] = "Вкл/Выкл использование общего цвета для неизвестных имен игроков.", + ["Set common color"] = "Установить общий цвет", + ["Change the common color of PlayerNames"] = "Изменить общий цвет имен игроков", + ["Use TabComplete"] = "Использовать TabComplete", + ["Toggle tab completion of player names."] = "Вкл/Выкл TabComplete в именах игроков.", + ["Show Level"] = "Показывать уровень", + ["Toggle level showing."] = "Вкл/выкл отображения уровня собеседника.", + ["Show Group"] = "Показывать группу", + ["Toggle raid group showing."] = "Вкл/выкл отображения рейдовой группы.", + ["Keep Info"] = "Хранить информацию", + ["Keep player information between session."] = "Хранить информацию о игроках между сессиями.", + ["Color Mode"] = "Режим цвета", + ["How to color Players name."] = "Как окрасить имя игрока.", + ["Toggle"] = "Вкл/Выкл", + ["Toggle the module on and off."] = "Вкл/Выкл модуль", + ["Use Census+"] = "Использовать Census+", + ["Use Census+ database"] = "Использовать базу данных Census+", +} end) + +L:RegisterTranslations("zhCN", function() return { + ["PlayerNames"] = "玩家姓名", + ["Player name formating options."] = "玩家姓名格式选项。", + ["Brackets"] = "圆弧", + ["Square"] = "方形", + ["Angled"] = "折角", + ["None"] = "无", + ["Class"] = "职业", + ["Random"] = "随机", + ["Sets style of brackets to use around player names."] = "设置玩家名字的括号。", + ["Use TabComplete"] = "标签", + ["Toggle tab completion of player names."] = "为玩家姓名作标签。", + ["Show Level"] = "显示等级", + ["Toggle level showing."] = "人物等级显示。", + ["Show Group"] = "显示小组", + ["Toggle raid group showing."] = "团队小组显示。", + ["Keep Info"] = "持续", + ["Keep player information between session."] = "在进程间保持信息。", + ["Color Mode"] = "颜色模块", + ["How to color Players name."] = "玩家名字颜色。", + ["Toggle"] = "切换", + ["Toggle the module on and off."] = "切换此模块的打开与关闭。", +} end) + +L:RegisterTranslations("koKR", function() return { + ["PlayerNames"] = "플레이어명", + ["Player name formating options."] = "플레이어명 형식을 설정합니다.", + ["Brackets"] = "괄호", + ["Square"] = "사각형", + ["Angled"] = "각진형", + ["None"] = "없음", + ["Class"] = "직업", + ["Random"] = "무작위", + ["Sets style of brackets to use around player names."] = "플레이어명 주위 괄호의 스타일 설정", + ["Use common color"] = "공통 색상 사용", + ["Toggle useing common color for unknown player names."] = "알려지지 않은 플레이어명에 대한 공통 색상 사용을 전환합니다.", + ["Set common color"] = "공통 색상 설정", + ["Change the common color of PlayerNames"] = "플레이어명의 공통 색상을 설정합니다.", + ["Use TabComplete"] = "탭완성 사용", + ["Toggle tab completion of player names."] = "플레이어명의 탭 완성기능을 전환합니다.", + ["Show Level"] = "레벨 표시", + ["Toggle level showing."] = "레벨 표시를 전환합니다.", + ["Show Group"] = "파티 표시", + ["Toggle raid group showing."] = "공격대 파티 표시를 전환합니다.", + ["Keep Info"] = "정보 유지", + ["Keep player information between session."] = "연결 간 플레이어 정보를 유지합니다.", + ["Color Mode"] = "색상 모드", + ["How to color Players name."] = "플레이어명 색상을 변경합니다.", + ["Toggle"] = "전환", + ["Toggle the module on and off."] = "모듈 켜고 끄기를 전환합니다.", +} end) + +local BC = AceLibrary("Babble-Class-2.2") +local sfind, sformat, slower, sgsub = string.find, string.format , string.lower, string.gsub + +Prat_PlayerNames = Prat:NewModule("playernames") + +Prat_PlayerNames.Classes = {} +Prat_PlayerNames.Levels = {} +Prat_PlayerNames.Subgroups = {} + +local chatList = {[1] = {t = "SAY", n = "Say"}, + [4] = {t = "GUILD", n = "Guild"}, + [2] = {t = "PARTY", n = "Party"}, + [3] = {t = "RAID", n = "Raid"}, + [7] = {t = "WHISPER", n = "Whisper"}, + [6] = {t = "YELL", n = "Yell"}, + [5] = {t = "OFFICER", n = "Officer"}, + [93] = {t = "BATTLEGROUND", n = "Battlegground"} + } +Prat_chatList = chatList +function Prat_PlayerNames:OnInitialize() + Prat_PlayerNames.db = Prat:AcquireDBNamespace("PlayerNames") + + local defaults = { + on = true, + brackets = "Angled", + tabcomplete = true, + level = true, + classes = {}, + levels = {}, + subgroup = true, + groupchan = {}, + groupAcronim = "[%s]", + keep = true, + census = false, + colormode = "CLASS", + usecommoncolor = true, + color = { + r = 1, + g = 0, + b = 1, + }, + markself = true, + selfcolor = { + r = 1, + g = 0, + b = 1, + }, + } + + for id,chat in pairs( chatList) do defaults.groupchan[chat.t] = true end + + Prat:RegisterDefaults("PlayerNames", "profile", defaults) + Prat.Options.args.playernames = { + name = L["PlayerNames"], + desc = L["Player name formating options."], + type = "group", + args = { + brackets = { + name = L["Brackets"], + desc = L["Sets style of brackets to use around player names."], + type = "text", + order = 140, + get = function() return Prat_PlayerNames.db.profile.brackets end, + set = function(v) Prat_PlayerNames.db.profile.brackets = v end, + validate = {["Square"] = L["Square"], ["Angled"] = L["Angled"], ["None"] = L["None"]} + }, + colormode = { + name = L["Color Mode"], + desc = L["How to color Players name."], + type = "text", + order = 150, + get = function() return Prat_PlayerNames.db.profile.colormode end, + set = function(v) Prat_PlayerNames.db.profile.colormode = v end, + validate = {["RANDOM"] = L["Random"], ["CLASS"] = L["Class"], ["NONE"] = L["None"]} + }, + level = { + name = L["Show Level"], + desc = L["Toggle level showing."], + type = "toggle", + order = 160, + get = function() return Prat_PlayerNames.db.profile.level end, + set = function(v) Prat_PlayerNames.db.profile.level = v end + }, + subgroup = { + name = L["Show Group"], + desc = L["Toggle raid group showing."], + type = "group", + order = 170, + args = { + set = { + name = L["Group acronim"], + desc = L["Simbol of raid group added to group number"], + type = "text", + usage = "", + order = 90, + get = function() return Prat_PlayerNames.db.profile.groupAcronim end, + set = function(v) Prat_PlayerNames.db.profile.groupAcronim = v end + }, + }, + + }, + keep = { + name = L["Keep Info"], + desc = L["Keep player information between session."], + type = "toggle", + order = 180, + get = function() return Prat_PlayerNames.db.profile.keep end, + set = function(v) Prat_PlayerNames.db.profile.keep = v end + }, + census = { + name = L["Use Census+"], + desc = L["Use Census+ database"], + type = "toggle", + order = 185, + get = function() return Prat_PlayerNames.db.profile.census end, + set = function(v) Prat_PlayerNames.db.profile.census = v end + }, + tabcomplete = { + name = L["Use TabComplete"], + desc = L["Toggle tab completion of player names."], + type = "toggle", + order = 190, + get = function() return Prat_PlayerNames.db.profile.tabcomplete end, + set = function(v) Prat_PlayerNames.db.profile.tabcomplete = v; self:TabComplete(v) end + }, + usecommoncolor = { + name = L["Use common color"], + desc = L["Toggle useing common color for unknown player names."], + type = "toggle", + order = 200, + get = function() return Prat_PlayerNames.db.profile.usecommoncolor end, + set = function(v) Prat_PlayerNames.db.profile.usecommoncolor = v end + }, + setcolor = { + name = L["Set common color"], + desc = L["Change the common color of PlayerNames"], + type = "color", + order = 210, + get = function() return Prat_PlayerNames.db.profile.color.r, Prat_PlayerNames.db.profile.color.g, Prat_PlayerNames.db.profile.color.b end, + set = function(r, g, b, a) Prat_PlayerNames.db.profile.color.r, Prat_PlayerNames.db.profile.color.g, Prat_PlayerNames.db.profile.color.b = r, g, b end, + disabled = function() if not Prat_PlayerNames.db.profile.usecommoncolor then return true else return false end end, + }, + markself = { + name = L["Mark self"], + desc = L["Toggle color marking of your nikname in chat."], + type = "toggle", + order = 220, + get = function() return Prat_PlayerNames.db.profile.markself end, + set = function(v) Prat_PlayerNames.db.profile.markself = v end + }, + setselfcolor = { + name = L["Set mark color"], + desc = L["Change the mark color of your nikname"], + type = "color", + order = 230, + get = function() return Prat_PlayerNames.db.profile.selfcolor.r, Prat_PlayerNames.db.profile.selfcolor.g, Prat_PlayerNames.db.profile.selfcolor.b end, + set = function(r, g, b, a) Prat_PlayerNames.db.profile.selfcolor.r, Prat_PlayerNames.db.profile.selfcolor.g, Prat_PlayerNames.db.profile.selfcolor.b = r, g, b end, + disabled = function() if not Prat_PlayerNames.db.profile.markself then return true else return false end end, + }, + toggle = { + name = L["Toggle"], + desc = L["Toggle the module on and off."], + type = "toggle", + order = 500, + get = function() return Prat_PlayerNames.db.profile.on end, + set = function() Prat_PlayerNames.db.profile.on = Prat:ToggleModuleActive("playernames") end + } + } + } + local ord = 100 + local menu = Prat.Options.args.playernames.args.subgroup.args + for id,chat in pairs( chatList) do + menu[chat.t] = {} + menu[chat.t].name = chat.n + menu[chat.t].desc = sformat(L["Enable group number at %s"],chat.n) + menu[chat.t].type = "toggle" + menu[chat.t].order = ord + --menu[chat].get = function() return Prat_PlayerNames.db.profile.groupchan[chat] end + --menu[chat].set = function(v) Prat_PlayerNames.db.profile.groupchan[chat] = not Prat_PlayerNames.db.profile.groupchan[chat] end + ord = ord +10 + end + menu["SAY"].get = function() return Prat_PlayerNames.db.profile.groupchan["SAY"] end + menu["SAY"].set = function(v) Prat_PlayerNames.db.profile.groupchan["SAY"] = v end + menu["GUILD"].get = function() return Prat_PlayerNames.db.profile.groupchan["GUILD"] end + menu["GUILD"].set = function(v) Prat_PlayerNames.db.profile.groupchan["GUILD"] = v end + menu["PARTY"].get = function() return Prat_PlayerNames.db.profile.groupchan["PARTY"] end + menu["PARTY"].set = function(v) Prat_PlayerNames.db.profile.groupchan["PARTY"] = v end + menu["RAID"].get = function() return Prat_PlayerNames.db.profile.groupchan["RAID"] end + menu["RAID"].set = function(v) Prat_PlayerNames.db.profile.groupchan["RAID"] = v end + menu["WHISPER"].get = function() return Prat_PlayerNames.db.profile.groupchan["WHISPER"] end + menu["WHISPER"].set = function(v) Prat_PlayerNames.db.profile.groupchan["WHISPER"] = v end + menu["YELL"].get = function() return Prat_PlayerNames.db.profile.groupchan["YELL"] end + menu["YELL"].set = function(v) Prat_PlayerNames.db.profile.groupchan["YELL"] = v end + menu["OFFICER"].get = function() return Prat_PlayerNames.db.profile.groupchan["OFFICER"] end + menu["OFFICER"].set = function(v) Prat_PlayerNames.db.profile.groupchan["OFFICER"] = v end + menu["BATTLEGROUND"].get = function() return Prat_PlayerNames.db.profile.groupchan["BATTLEGROUND"] end + menu["BATTLEGROUND"].set = function(v) Prat_PlayerNames.db.profile.groupchan["BATTLEGROUND"] = v end +end + +function Prat_PlayerNames:OnEnable() + for i = 1, NUM_CHAT_WINDOWS do + self:Hook(getglobal("ChatFrame"..i), "AddMessage") + end + + self:RegisterEvent("FRIENDLIST_UPDATE", "updateFriends") + self:RegisterEvent("GUILD_ROSTER_UPDATE", "updateGuild") + self:RegisterEvent("RAID_ROSTER_UPDATE", "updateRaid") + self:RegisterEvent("PARTY_MEMBERS_CHANGED", "updateParty") + self:RegisterEvent("PLAYER_TARGET_CHANGED", "updateTarget") + self:RegisterEvent("WHO_LIST_UPDATE", "updateWho") + self:RegisterEvent("CHAT_MSG_SYSTEM", "updateWho") -- for short /who command + + for name, color in pairs(Prat_PlayerNames.db.profile.classes) do Prat_PlayerNames.Classes[name] = color end + for name, level in pairs(Prat_PlayerNames.db.profile.levels) do Prat_PlayerNames.Levels[name] = level end + + local PlayerClass + _, PlayerClass = UnitClass("player") + self.playerName = UnitName("player"); + self:addName(self.playerName, Playerclass, UnitLevel("player")) + + + self:updateParty() + self:updateRaid() + self:updateFriends() + self:updateGuild() + + self:TabComplete(Prat_PlayerNames.db.profile.tabcomplete) +end + +function Prat_PlayerNames:OnDisable() + self:TabComplete(false) +end + +function Prat_PlayerNames:updateFriends() + local Name, Class, Level + for i = 1, GetNumFriends() do + Name, Level, Class = GetFriendInfo(i) + self:addName(Name, Class, Level) + end +end + +function Prat_PlayerNames:updateGuild() + local Name, Class, Level + for i = 1, GetNumGuildMembers() do + Name, _, _, Level, Class = GetGuildRosterInfo(i) + self:addName(Name, Class, Level) + end +end + +function Prat_PlayerNames:updateRaid() + local Name, Class, SubGroup, Level + Prat_PlayerNames.Subgroups = {} + for i = 1, GetNumRaidMembers() do + Name, _, SubGroup, Level, Class = GetRaidRosterInfo(i) + self:addName(Name, Class, Level, SubGroup) + end +end + +function Prat_PlayerNames:updateParty() + local Class, Unit + for i = 1, GetNumPartyMembers() do + Unit = "party" .. i + _, Class = UnitClass(Unit) + self:addName(UnitName(Unit), Class, UnitLevel(Unit)) + end +end + +function Prat_PlayerNames:updateTarget() + local Class + if not UnitIsPlayer("target") or not UnitIsFriend("player", "target") then + return + end + _, Class = UnitClass("target") + self:addName(UnitName("target"), Class, UnitLevel("target")) +end + +function Prat_PlayerNames:updateWho() + local Name, Class, Level + for i = 1, GetNumWhoResults() do + Name, _, Level, _, Class = GetWhoInfo(i) + self:addName(Name, Class, Level) + end +end + +function Prat_PlayerNames:addName(Name, Class, Level, SubGroup) + if Name then + if Level and Level ~= 0 and ( not ( Prat_PlayerNames.Levels[Name] and Prat_PlayerNames.Levels[Name] > Level ) ) then + Prat_PlayerNames.Levels[Name] = Level + if ( Prat_PlayerNames.db.profile.keep) then Prat_PlayerNames.db.profile.levels[Name] = Level end + end + if Class then + Prat_PlayerNames.Classes[Name] = Class + if ( Prat_PlayerNames.db.profile.keep ) then Prat_PlayerNames.db.profile.classes[Name] = Class end + end + if SubGroup then + Prat_PlayerNames.Subgroups[Name] = SubGroup + end + end +end + +function Prat_PlayerNames:randomColor(Name) + local hash = 17 + for i=1,string.len(Name) do + hash = hash * 37 * string.byte(Name, i); + end + + local r = math.floor(math.mod(hash / 97, 255)); + local g = math.floor(math.mod(hash / 17, 255)); + local b = math.floor(math.mod(hash / 227, 255)); + + if ((r * 299 + g * 587 + b * 114) / 1000) < 105 then + r = math.abs(r - 255); + g = math.abs(g - 255); + b = math.abs(b - 255); + end + + return sformat("%02x%02x%02x", r, g, b) +end + +function Prat_PlayerNames:addInfo(Name, id) + if Name then + local returnName = Name + + if Prat_PlayerNames.Levels[Name] and Prat_PlayerNames.db.profile.level then + returnName = sformat("%s:%s", Prat_PlayerNames.Levels[Name], returnName) + elseif Prat_PlayerNames.db.profile.census and CensusPlus_ForAllCharacters then + local realmName = g_CensusPlusLocale .. GetCVar("realmName"); + CensusPlus_ForAllCharacters( realmName, UnitFactionGroup("player"), nil, nil, nil, nil, Prat_InternalWhoResult) + end + + if Prat_PlayerNames.Subgroups[Name] and (id == nil or (chatList[id] and Prat_PlayerNames.db.profile.groupchan[chatList[id].t])) then + local grp = sformat(Prat_PlayerNames.db.profile.groupAcronim,Prat_PlayerNames.Subgroups[Name] or "") + returnName = sformat("%s%s", returnName, grp ) + end + if Prat_PlayerNames.Classes[Name] then + if Prat_PlayerNames.db.profile.colormode == "CLASS" then + returnName = sformat("|cff%s%s|r", BC:GetHexColor(Prat_PlayerNames.Classes[Name]), returnName) + elseif Prat_PlayerNames.db.profile.colormode == "RANDOM" then + returnName = sformat("|cff%s%s|r", self:randomColor(Name), returnName) + end + else + if Prat_PlayerNames.db.profile.usecommoncolor then + local color = sformat("%02x%02x%02x", Prat_PlayerNames.db.profile.color.r*255, Prat_PlayerNames.db.profile.color.g*255, Prat_PlayerNames.db.profile.color.b*255) + returnName = sformat("|cff%s%s|r", color, returnName) + end + end + + return returnName + else + return "" + end +end + +function Prat_InternalWhoResult(Name, Level, guild, race, Class, lastSeen ) + if Name then + if Level and Level ~= 0 and ( not ( Prat_PlayerNames.Levels[Name] and Prat_PlayerNames.Levels[Name] > Level ) ) then + Prat_PlayerNames.Levels[Name] = Level + if ( Prat_PlayerNames.db.profile.keep) then Prat_PlayerNames.db.profile.levels[Name] = Level end + end + if Class then + Prat_PlayerNames.Classes[Name] = Class + if ( Prat_PlayerNames.db.profile.keep ) then Prat_PlayerNames.db.profile.classes[Name] = Class end + end + end +end +function gisub(s, pat, repl, n) + pat = sgsub(pat, '(%a)', + function (v) return '['..strupper(v)..strlower(v)..']' end) + if n then + return sgsub(s, pat, repl, n) + else + return sgsub(s, pat, repl) + end +end +function Prat_PlayerNames:AddMessage(frame, text, r, g, b, id) + if text and id then + -- local Name = sgsub(text, ".*|Hplayer:(.-)|h.*", "%1") + + -- Sender's name is always the first linked player name in the message + local _, _, Name = sfind(text, "|Hplayer:(.-)|h%[.-]|h") + + -- If self.playerName is a clickable link, don't color it + local _, _, linkedName = sfind(text, sformat("Hplayer:(%s)", self.playerName)) + if not linkedName then + _, _, linkedName = sfind(text, sformat("|h[(%s)]|h", self.playerName)) + end + if not linkedName and Name ~= self.playerName and Prat_PlayerNames.db.profile.markself then + local textL = slower(text) + local playerL = slower(self.playerName) + if sfind(textL, playerL) then + local color = sformat("%02x%02x%02x", Prat_PlayerNames.db.profile.selfcolor.r*255, Prat_PlayerNames.db.profile.selfcolor.g*255, Prat_PlayerNames.db.profile.selfcolor.b*255) + local returnName = sformat("|cff%s%s|r", color, self.playerName) + text = gisub(text, self.playerName, returnName) + PlaySound("FriendJoinGame"); + end + end + + local Brackets + Name = self:addInfo(Name, id) + + if Prat_PlayerNames.db.profile.brackets == "Angled" then + Brackets = "<|Hplayer:%1|h" .. Name .. "|h>" + elseif Prat_PlayerNames.db.profile.brackets == "None" then + Brackets = "|Hplayer:%1|h" .. Name .. "|h" + else + Brackets = "[|Hplayer:%1|h" .. Name .. "|h]" + end + + -- Only replace the first instance, and leave any linked names untouched + text = sgsub(text, "|Hplayer:(.-)|h%[.-%]|h(.-:-)", Brackets .. "%2", 1) + end + Prat_PlayerNames.hooks[frame].AddMessage(frame, text, r, g, b, id) +end + +function Prat_PlayerNames:TabComplete(enabled) + if enabled then + if not AceLibrary("AceTab-2.0"):IsTabCompletionRegistered("Prat_PlayerNames") then + AceLibrary("AceTab-2.0"):RegisterTabCompletion("Prat_PlayerNames", "", + function(t, text, pos) + for name in pairs(Prat_PlayerNames.Classes) do + table.insert(t, pos == 0 and name..":" or name) + end + end, + function(u, cands) + local text = "" + for _, cand in ipairs(cands) do + cand = sgsub(cand, ":", "") + cand = self:addInfo(cand, nil) + text = text .. " " .. cand + end + Prat:Print("Tab completion : " .. text) + end + ) + end + else + if AceLibrary("AceTab-2.0"):IsTabCompletionRegistered("Prat_PlayerNames") then + AceLibrary("AceTab-2.0"):UnregisterTabCompletion("Prat_PlayerNames") + end + end +end diff --git a/modules/PopupMessage.lua b/modules/PopupMessage.lua new file mode 100755 index 0000000..0f7263f --- /dev/null +++ b/modules/PopupMessage.lua @@ -0,0 +1,270 @@ +--[[ +Name: Prat_PopupMessage +Revision: $Revision: 16019 $ +Author(s): Sylvanaar +Website: http://www.wowace.com/files/index.php?path=Prat/ +Documentation: http://www.wowace.com/wiki/Prat/Integrated_Modules#PopupMessage +SVN: http://svn.wowace.com/wowace/trunk/Prat/ +Description: Module for Prat that displays chat with your name in a pop up window +Dependencies: Prat + +Credit goes to: CleanChat +]] + +local L = AceLibrary("AceLocale-2.2"):new("PopupMessage") + +L:RegisterTranslations("enUS", function() return { + ["PopupMessage"] = true, + ["Shows messages with your name in a popup."] = true, + ["Toggle"] = true, + ["Show"] = true, + ["ShowAll"] = true, + ["Toggle the module on and off."] = true, + ["Toggles popups on and off for chat window %d."] = true, + ["Toggle showing Popup settings for each window."] = true, + ["Chat%dPopupShow"] = true, + ["Toggles Popup on and off for all chat windows."] = true, + ["Choose mode for showing popups."] = true, + ["ShowMode"] = true, + ["All"] = true, + ["Individual"] = true, + ["An alternate name to show in popups."] = true, + ["Nickname"] = true, +} end) + +L:RegisterTranslations("ruRU", function() return { + ["PopupMessage"] = "PopupMessage", + ["Shows messages with your name in a popup."] = "Отображать сообщение с вашим именем в всплывающем окне.", + ["Toggle"] = "Вкл/Выкл", + ["Show"] = "Показать", + ["ShowAll"] = "Показать все", + ["Toggle the module on and off."] = "Вкл/Выкл модуль", + ["Toggles popups on and off for chat window %d."] = "Вкл/Выкл всплывающие окна для окна чата %d.", + ["Toggle showing Popup settings for each window."] = "Вкл/Выкл всплывающие настройки для каждого окна.", + ["Chat%dPopupShow"] = "ПоказыватьВысплывающийЧат%d", + ["Toggles Popup on and off for all chat windows."] = "Вкл/Выкл всплывание для всех окон чата.", + ["Choose mode for showing popups."] = "Выбрать режим для отображение всплывающих окон.", + ["ShowMode"] = "ПоказатьРежим", + ["All"] = "Все", + ["Individual"] = "Индивидуальный", + ["An alternate name to show in popups."] = "Альтернативное название чтобы показать всплывающие окна.", + ["Nickname"] = "Ник", +} end) + +L:RegisterTranslations("zhCN", function() return { + ["PopupMessage"] = "弹出消息", + ["Shows messages with your name in a popup."] = "在弹出窗口中显示包含自己名字的消息.", + ["Toggle"] = "切换", + ["Toggle the module on and off."] = "切换此模块的打开与关闭。", + ["Show"] = "显示", + ["ShowAll"] = "全部显示", + ["Toggles popups on and off for chat window %d."] = "切换窗口 %d 的弹出消息.", + ["Toggle showing Popup settings for each window."] = "切换每一窗口的弹出显示设置.", + ["Chat%dPopupShow"] = "显示窗口 %d 探出消息", + ["Toggles Popup on and off for all chat windows."] = "切换全部聊天窗口弹出消息.", + ["Choose mode for showing popups."] = "选择显示弹出消息方式.", + ["ShowMode"] = "显示模式", + ["All"] = "全部", + ["Individual"] = "个别", + ["An alternate name to show in popups."] = "显示弹出消息的替换名字.", + ["Nickname"] = "昵称", +} end) + +L:RegisterTranslations("koKR", function() return { + ["PopupMessage"] = "팝업 메세지", + ["Shows messages with your name in a popup."] = "당신의 이름을 포함한 메세지를 팝업으로 표시합니다.", + ["Show"] = "표시", + ["ShowAll"] = "모두 표시", + ["Toggles popups on and off for chat window %d."] = "대화창 %d에 대한 팝업을 켜거나 끕니다.", + ["Toggle showing Popup settings for each window."] = "각 창에 대한 팝업 설정을 표시하거나 끕니다.", + ["Chat%dPopupShow"] = "대화%d 팝업 표시", + ["Toggles Popup on and off for all chat windows."] = "모든 대화창에 대한 팝업을 켜거나 끕니다.", + ["Choose mode for showing popups."] = "팝업 표시에 대한 모드를 선택합니다.", + ["ShowMode"] = "표시모드", + ["All"] = "모두", + ["Individual"] = "각각", + ["An alternate name to show in popups."] = "팝업으로 표시할 다른 이름", + ["Nickname"] = "별명", + ["Toggle"] = "전환", + ["Toggle the module on and off."] = "모듈 켜고 끄기 전환", +} end) + +Prat_PopupMessage = Prat:NewModule("PopupMessage") + +function Prat_PopupMessage:OnInitialize() + self.db = Prat:AcquireDBNamespace("PopupMessage") + Prat:RegisterDefaults("PopupMessage", "profile", { + on = true, + showmode = "INDIVIDUAL", + show = {true, false, false, false, false, false, false}, + showall = false, + nickname = "", + }) + Prat.Options.args.PopupMessage = { + name = L["PopupMessage"], + desc = L["Shows messages with your name in a popup."], + type = "group", + args = { + Toggle = { + type = "toggle", + name = L["Toggle"], + desc = L["Toggle the module on and off."], + get = function() return self.db.profile.on end, + set = function(v) self.db.profile.on = Prat:ToggleModuleActive("PopupMessage") end + }, + showmode = { + name = L["ShowMode"], + desc = L["Choose mode for showing popups."], + type = "text", + order = 100, + get = function() return self.db.profile.showmode end, + set = function(v) self.db.profile.showmode = v end, + validate = {["ALL"] = L["All"], ["INDIVIDUAL"] = L["Individual"]} + }, + show = { + name = L["Show"], + desc = L["Toggle showing Popup settings for each window."], + type = "group", + order = 110, + args = { + chat1 = { + name = string.format(L["Chat%dPopupShow"], 1), + desc = string.format(L["Toggles popups on and off for chat window %d."], 1), + type = "toggle", + get = function() return self.db.profile.show[1] end, + set = function(v) self.db.profile.show[1] = v; self:SetFrameStatus(1) end + }, + chat2 = { + name = string.format(L["Chat%dPopupShow"], 2), + desc = string.format(L["Toggles popups on and off for chat window %d."], 2), + type = "toggle", + get = function() return self.db.profile.show[2] end, + set = function(v) self.db.profile.show[2] = v; self:SetFrameStatus(2) end + }, + chat3 = { + name = string.format(L["Chat%dPopupShow"], 3), + desc = string.format(L["Toggles popups on and off for chat window %d."], 3), + type = "toggle", + get = function() return self.db.profile.show[3] end, + set = function(v) self.db.profile.show[3] = v; self:SetFrameStatus(3) end + }, + chat4 = { + name = string.format(L["Chat%dPopupShow"], 4), + desc = string.format(L["Toggles popups on and off for chat window %d."], 4), + type = "toggle", + get = function() return self.db.profile.show[4] end, + set = function(v) self.db.profile.show[4] = v; self:SetFrameStatus(4) end + }, + chat5 = { + name = string.format(L["Chat%dPopupShow"], 5), + desc = string.format(L["Toggles popups on and off for chat window %d."], 5), + type = "toggle", + get = function() return self.db.profile.show[5] end, + set = function(v) self.db.profile.show[5] = v; self:SetFrameStatus(5) end + }, + chat6 = { + name = string.format(L["Chat%dPopupShow"], 6), + desc = string.format(L["Toggles popups on and off for chat window %d."], 6), + type = "toggle", + get = function() return self.db.profile.show[6] end, + set = function(v) self.db.profile.show[6] = v; self:SetFrameStatus(6) end + }, + chat7 = { + name = string.format(L["Chat%dPopupShow"], 7), + desc = string.format(L["Toggles popups on and off for chat window %d."], 7), + type = "toggle", + get = function() return self.db.profile.show[7] end, + set = function(v) self.db.profile.show[7] = v; self:SetFrameStatus(7) end + }, + }, + disabled = function() if self.db.profile.showmode == "ALL" then return true else return false end end, + }, + showall = { + name = L["ShowAll"], + desc = L["Toggles Popup on and off for all chat windows."], + type = "toggle", + order = 120, + get = function() return self.db.profile.showall end, + set = function(v) + self.db.profile.showall = v + for i=1,NUM_CHAT_WINDOWS do + self.db.profile.show[i] = v + self:SetFrameStatus(i) + end + end, + disabled = function() if self.db.profile.showmode == "INDIVIDUAL" then return true else return false end end, + }, + + nick = { + order = 130, + name = L["Nickname"], + desc = L["An alternate name to show in popups."], + type = "text", + usage = "", + get = function() return self.db.profile.nickname end, + set = function(v) self.db.profile.nickname = v end, + hidden = function() return true end, + }, + }, + } +end + +function Prat_PopupMessage:OnEnable() + for i=1,NUM_CHAT_WINDOWS do + self:SetFrameStatus(i) + end + + self.playerName = UnitName("player"); +end +function Prat_PopupMessage:OnDisable() + +end + +function Prat_PopupMessage:SetFrameStatus(id) + local frame = getglobal("ChatFrame"..id) + + if self.db.profile.show[id] then + if not self:IsHooked(frame, "AddMessage") then self:SecureHook(frame, "AddMessage") end + else + if self:IsHooked(frame, "AddMessage") then self:Unhook(frame, "AddMessage") end + end +end + + +function Prat_PopupMessage:AddMessage(frame, text, r, g, b, id) + + if not text then return end + local Name = string.gsub(text, ".*|Hplayer:(.-)|h.*", "%1") + + if Name ~= self.playerName then + local textL = string.lower(text) + local playerL = string.lower(self.playerName) +-- local nicknameL = string.lower(self.db.profile.nickname) + +-- if string.find(textL, playerL) or string.find(textL, nicknameL) then + if string.find(textL, playerL) then + Prat_PopupFrame.fadeOut = 10; + Prat_PopupFrame:SetAlpha(1); + Prat_PopupFrameText:SetText(text); + Prat_PopupFrame:Show(); + PlaySound("FriendJoinGame"); + end + end + + -- SecureHook no need to call original +end + + +function Prat_PopupMessage:PopupUpdated(elapsed) + Prat_PopupFrame.fadeOut = Prat_PopupFrame.fadeOut - elapsed; + if Prat_PopupFrame.fadeOut < -1 then + Prat_PopupFrame:Hide(); + elseif Prat_PopupFrame.fadeOut < 0 then + Prat_PopupFrame:SetAlpha(1 + Prat_PopupFrame.fadeOut); + end +end + + + + + diff --git a/modules/Scroll.lua b/modules/Scroll.lua new file mode 100755 index 0000000..5ad48f9 --- /dev/null +++ b/modules/Scroll.lua @@ -0,0 +1,232 @@ +--[[ +Name: Prat_Scroll +Revision: $Revision: 16019 $ +Author(s): Curney (asml8ed@gmail.com) + Krtek (krtek4@gmail.com) +Inspired by: idChat2_PlayerNames by Industrial +Website: http://www.wowace.com/files/index.php?path=Prat/ +Documentation: http://www.wowace.com/wiki/Prat/Integrated_Modules#Scroll +SVN: http://svn.wowace.com/wowace/trunk/Prat/ +Description: Module for Prat that enables mousewheel scrolling for chat windows (default=on). +Dependencies: Prat +]] + +local L = AceLibrary("AceLocale-2.2"):new("PratScrolling") + +L:RegisterTranslations("enUS", function() return { + ["Scroll"] = true, + ["Chat window scrolling options."] = true, + ["MouseScroll"] = true, + ["Toggle mousewheel scrolling in chat windows on and off."] = true, + ["Chat%dScrolling"] = true, + ["Toggles mouse scrolling on and off for chat window %d."] = true, + ["Normal ScrollSpeed"] = true, + ["Sets number of lines mousewheel will scroll."] = true, + ["Ctrl ScrollSpeed"] = true, + ["Sets number of lines mousewheel will scroll when ctrl is pressed."] = true, + ["Toggle"] = true, + ["Toggle the module on and off."] = true, +} end) + +L:RegisterTranslations("ruRU", function() return { + ["Scroll"] = "Scroll", + ["Chat window scrolling options."] = "Настройки прокрутки окна чата.", + ["MouseScroll"] = "Прокрутка мышкой", + ["Toggle mousewheel scrolling in chat windows on and off."] = "Вкл/Выкл прокрутку окна чата колесиком мышки.", + ["Chat%dScrolling"] = "ПрокруткаЧата%d", + ["Toggles mouse scrolling on and off for chat window %d."] = "Вкл/Выкл прокрутку мышкой окно чата %d.", + ["Normal ScrollSpeed"] = "Нормальная скорость прокрутки", + ["Sets number of lines mousewheel will scroll."] = "Установить кол-во строк, которое будет прокручиваться колесиком мышки.", + ["Ctrl ScrollSpeed"] = "Ctrl скорость прокрутки", + ["Sets number of lines mousewheel will scroll when ctrl is pressed."] = "Устанавливает кол-во прокрученных строки колесиком мышки при нажатии ctrl.", + ["Toggle"] = "Вкл/Выкл", + ["Toggle the module on and off."] = "Вкл/Выкл модуль.", +} end) + +L:RegisterTranslations("zhCN", function() return { + ["Scroll"] = "滚动", + ["Chat window scrolling options."] = "聊天窗口滚动选项。", + ["MouseScroll"] = "鼠标滚动", + ["Toggle mousewheel scrolling in chat windows on and off."] = "切换鼠标滚动聊天窗口功能打开与关闭。", + ["Chat%dScrolling"] = "窗口%d滚动", + ["Toggles mouse scrolling on and off for chat window %d."] = "切换聊天窗口 %d 鼠标滚动功能打开与关闭。", + ["Normal ScrollSpeed"] = "滚动速度", + ["Sets number of lines mousewheel will scroll."] = "鼠标滚动时卷动的行数。", + ["Ctrl ScrollSpeed"] = "滚动速度", + ["Sets number of lines mousewheel will scroll when ctrl is pressed."] = "设置按下时鼠标滚轮的滚动行数。", + ["Toggle"] = "切换", + ["Toggle the module on and off."] = "切换此模块的打开与关闭", +} end) + +L:RegisterTranslations("koKR", function() return { + ["Scroll"] = "스크롤", + ["Chat window scrolling options."] = "대화창 스크롤 설정입니다.", + ["MouseScroll"] = "마우스 스크롤", + ["Toggle mousewheel scrolling in chat windows on and off."] = "대화창을 마우스횔로 스크롤하는 것을 켜고 끄기를 전환합니다.", + ["Chat%dScrolling"] = "대화창%d 스크롤", + ["Toggles mouse scrolling on and off for chat window %d."] = "대화창 %d에 대한 마우스 스크롤을 켜고 끄기를 전환합니다.", + ["Normal ScrollSpeed"] = "일반 스크롤 속도", + ["Sets number of lines mousewheel will scroll."] = "설정한 라인 수 만큼 스크롤됩니다.", + ["Ctrl ScrollSpeed"] = "컨트롤 스크롤 속도", + ["Sets number of lines mousewheel will scroll when ctrl is pressed."] = "컨트롤키를 눌렀을 때 설정된 라인 수만큼 스크롤됩니다.", + ["Toggle"] = "전환", + ["Toggle the module on and off."] = "모듈 켜고 끄기를 전환합니다.", +} end) + +Prat_Scroll = Prat:NewModule("scroll") + +function Prat_Scroll:OnInitialize() + self.db = Prat:AcquireDBNamespace("Scrolling") + Prat:RegisterDefaults("Scrolling", "profile", { + on = true, + chatscrolls = {true, true, true, true, true, true, true}, + normscrollspeed = 1, + ctrlscrollspeed = 3, + }) + Prat.Options.args.scroll = { + name = L["Scroll"], + desc = L["Chat window scrolling options."], + type = "group", + args = { + mousescroll = { + name = L["MouseScroll"], + desc = L["Toggle mousewheel scrolling in chat windows on and off."], + type = "group", + args = { + chat1 = { + name = string.format(L["Chat%dScrolling"], 1), + desc = string.format(L["Toggles mouse scrolling on and off for chat window %d."], 1), + type = "toggle", + get = function() return self.db.profile.chatscrolls[1] end, + set = function(v) self.db.profile.chatscrolls[1] = v; self:MouseWheel(1,v) end + }, + chat2 = { + name = string.format(L["Chat%dScrolling"], 2), + desc = string.format(L["Toggles mouse scrolling on and off for chat window %d."], 2), + type = "toggle", + get = function() return self.db.profile.chatscrolls[2] end, + set = function(v) self.db.profile.chatscrolls[2] = v; self:MouseWheel(2,v) end + }, + chat3 = { + name = string.format(L["Chat%dScrolling"], 3), + desc = string.format(L["Toggles mouse scrolling on and off for chat window %d."], 3), + type = "toggle", + get = function() return self.db.profile.chatscrolls[3] end, + set = function(v) self.db.profile.chatscrolls[3] = v; self:MouseWheel(3,v) end + }, + chat4 = { + name = string.format(L["Chat%dScrolling"], 4), + desc = string.format(L["Toggles mouse scrolling on and off for chat window %d."], 4), + type = "toggle", + get = function() return self.db.profile.chatscrolls[4] end, + set = function(v) self.db.profile.chatscrolls[4] = v; self:MouseWheel(4,v) end + }, + chat5 = { + name = string.format(L["Chat%dScrolling"], 5), + desc = string.format(L["Toggles mouse scrolling on and off for chat window %d."], 5), + type = "toggle", + get = function() return self.db.profile.chatscrolls[5] end, + set = function(v) self.db.profile.chatscrolls[5] = v; self:MouseWheel(5,v) end + }, + chat6 = { + name = string.format(L["Chat%dScrolling"], 6), + desc = string.format(L["Toggles mouse scrolling on and off for chat window %d."], 6), + type = "toggle", + get = function() return self.db.profile.chatscrolls[6] end, + set = function(v) self.db.profile.chatscrolls[6] = v; self:MouseWheel(6,v) end + }, + chat7 = { + name = string.format(L["Chat%dScrolling"], 7), + desc = string.format(L["Toggles mouse scrolling on and off for chat window %d."], 7), + type = "toggle", + get = function() return self.db.profile.chatscrolls[7] end, + set = function(v) self.db.profile.chatscrolls[7] = v; self:MouseWheel(7,v) end + } + } + }, + normscrollspeed = { + name = L["Normal ScrollSpeed"], + desc = L["Sets number of lines mousewheel will scroll."], + type = "range", + get = function() return self.db.profile.normscrollspeed end, + set = function(v) self.db.profile.normscrollspeed = v end, + min = 1, + max = 21, + step = 1, + order = 180 + }, + ctrlscrollspeed = { + name = L["Ctrl ScrollSpeed"], + desc = L["Sets number of lines mousewheel will scroll when ctrl is pressed."], + type = "range", + get = function() return self.db.profile.ctrlscrollspeed end, + set = function(v) self.db.profile.ctrlscrollspeed = v end, + min = 3, + max = 21, + step = 3, + order = 190 + }, + toggle = { + name = L["Toggle"], + desc = L["Toggle the module on and off."], + type = "toggle", + order = 200, + get = function() return self.db.profile.on end, + set = function() self.db.profile.on = Prat:ToggleModuleActive("scroll") end + } + } + } +end + +function Prat_Scroll:OnEnable() + for i = 1,NUM_CHAT_WINDOWS do + self:MouseWheel(i,self.db.profile.chatscrolls[i]) + end +end + +function Prat_Scroll:OnDisable() + for i = 1,NUM_CHAT_WINDOWS do + self:MouseWheel(i,false) + end +end + +function Prat_Scroll:MouseWheel(id, enabled) + local cf = getglobal("ChatFrame"..id) + if enabled then + cf:SetScript("OnMouseWheel", function() + if (arg1 > 0) then + if IsShiftKeyDown() then + cf:ScrollToTop() + else + if IsControlKeyDown() then + for i = 1,self.db.profile.ctrlscrollspeed do + cf:ScrollUp() + end + else + for i = 1,self.db.profile.normscrollspeed do + cf:ScrollUp() + end + end + end + elseif (arg1 < 0) then + if IsShiftKeyDown() then + cf:ScrollToBottom() + else + if IsControlKeyDown() then + for i = 1,self.db.profile.ctrlscrollspeed do + cf:ScrollDown() + end + else + for i = 1,self.db.profile.normscrollspeed do + cf:ScrollDown() + end + end + end + end + end) + cf:EnableMouseWheel(true) + else + cf:SetScript("OnMouseWheel", nil) + cf:EnableMouseWheel(false) + end +end diff --git a/modules/StickyChannels.lua b/modules/StickyChannels.lua new file mode 100755 index 0000000..48ea23f --- /dev/null +++ b/modules/StickyChannels.lua @@ -0,0 +1,301 @@ +--[[ +Name: Prat_StickyChannels +Revision: $Revision: 16019 $ +Author(s): Curney (asml8ed@gmail.com) + Krtek (krtek4@gmail.com) +Inspired by: idChat2_StickyChannels by Industrial +Website: http://www.wowace.com/files/index.php?path=Prat/ +Documentation: http://www.wowace.com/wiki/Prat/Integrated_Modules#StickyChannels +SVN: http://svn.wowace.com/wowace/trunk/Prat/ +Description: Module for Prat that toggles sticky of different chat channel types on and off (default=on). +Dependencies: Prat +]] + +local L = AceLibrary("AceLocale-2.2"):new("PratStickyChannels") + +L:RegisterTranslations("enUS", function() return { + ["StickyChannels"] = true, + ["Chat channel sticky options."] = true, + ["ChatType"] = true, + ["Per chat type options."] = true, + ["Say"] = true, + ["Whisper"] = true, + ["Yell"] = true, + ["Party"] = true, + ["Guild"] = true, + ["Officer"] = true, + ["Raid"] = true, + ["RaidWarning"] = true, + ["Battleground"] = true, + ["Channel"] = true, + ["Emote"] = true, + ["%sSticky"] = true, + ["Toggles sticky on and off for %s."] = true, + ["Toggle"] = true, + ["Toggle the module on and off."] = true, +} end) + +L:RegisterTranslations("ruRU", function() return { + ["StickyChannels"] = "StickyChannels", + ["Chat channel sticky options."] = "Опции прилипания каналов чата.", + ["ChatType"] = "ТипЧата", + ["Per chat type options."] = "Опции чат типа.", + ["Say"] = "Сказать", + ["Whisper"] = "Шепот", + ["Yell"] = "Крикнуть", + ["Party"] = "Группа", + ["Guild"] = "Гильдия", + ["Officer"] = "Офицер", + ["Raid"] = "Рейд", + ["RaidWarning"] = "Объявление рейду", + ["Battleground"] = "Поле боя", + ["Channel"] = "Канал", + ["Emote"] = "Эмоция", + ["%sSticky"] = "Липкая \"%s\"", + ["Toggles sticky on and off for %s."] = "Вкл/Выкл липкие %s.", + ["Toggle"] = "Вкл/Выкл", + ["Toggle the module on and off."] = "Вкл/Выкл модуль.", +} end) + +L:RegisterTranslations("zhCN", function() return { + ["StickyChannels"] = "固定频道", + ["Chat channel sticky options."] = "固定聊天频道选项。", + ["ChatType"] = "聊天分类", + ["Per chat type options."] = "聊天类型选项。", + ["Say"] = "说", + ["Whisper"] = "密语", + ["Yell"] = "喊", + ["Party"] = "队伍", + ["Guild"] = "公会", + ["Officer"] = "官员", + ["Raid"] = "团队", + ["RaidWarning"] = "团队报警", + ["Battleground"] = "战场", + ["Channel"] = "频道", + ["Emote"] = "表情", + ["%sSticky"] = "固定%s", + ["Toggles sticky on and off for %s."] = "切换固定%s频道.", + ["Toggle"] = "切换", + ["Toggle the module on and off."] = "切换此模块的打开与关闭。", +} end) + +L:RegisterTranslations("koKR", function() return { + ["StickyChannels"] = "채널 고정", + ["Chat channel sticky options."] = "대화 채널 고정 설정입니다.", + ["ChatType"] = "대화 종류", + ["Per chat type options."] = "대화종류에 따른 설정입니다.", + ["Say"] = "일반", + ["Whisper"] = "귓속말", + ["Yell"] = "외치기", + ["Party"] = "파티", + ["Guild"] = "길드", + ["Officer"] = "오피서", + ["Raid"] = "공격대", + ["RaidWarning"] = "공대 경보", + ["Battleground"] = "전장", + ["Channel"] = "채널", + ["Emote"] = "감정표현", + ["%sSticky"] = "%s 고정", + ["Toggles sticky on and off for %s."] = "%s에 대한 고정 켜고 끄기를 전환합니다.", + ["Toggle"] = "전환", + ["Toggle the module on and off."] = "모듈 켜고 끄기를 전환합니다.", +} end) + +Prat_StickyChannels = Prat:NewModule("stickychannels") + +function Prat_StickyChannels:OnInitialize() + self.db = Prat:AcquireDBNamespace("StickyChannels") + Prat:RegisterDefaults("StickyChannels", "profile", { + on = true, + say = true, + whisper = true, + yell = true, + party = true, + guild = true, + officer = true, + raid = true, + raid_warning = true, + battleground = true, + channel = true, + emote = true, + }) + Prat.Options.args.stickychannels = { + name = L["StickyChannels"], + desc = L["Chat channel sticky options."], + type = "group", + args = { + chattype = { + name = L["ChatType"], + desc = L["Per chat type options."], + type = "group", + args = { + say = { + name = string.format(L["%sSticky"], L["Say"]), + desc = string.format(L["Toggles sticky on and off for %s."], L["Say"]), + type = "toggle", + order = 101, + get = function() return self.db.profile.say end, + set = function(v) + self.db.profile.say = v + self:Stickum("SAY",v) + end + }, + whisper = { + name = string.format(L["%sSticky"], L["Whisper"]), + desc = string.format(L["Toggles sticky on and off for %s."], L["Whisper"]), + type = "toggle", + order = 102, + get = function() return self.db.profile.whisper end, + set = function(v) + self.db.profile.whisper = v + self:Stickum("WHISPER",v) + end + }, + yell = { + name = string.format(L["%sSticky"], L["Yell"]), + desc = string.format(L["Toggles sticky on and off for %s."], L["Yell"]), + type = "toggle", + order = 103, + get = function() return self.db.profile.yell end, + set = function(v) + self.db.profile.yell = v + self:Stickum("YELL",v) + end + }, + party = { + name = string.format(L["%sSticky"], L["Party"]), + desc = string.format(L["Toggles sticky on and off for %s."], L["Party"]), + type = "toggle", + order = 104, + get = function() return self.db.profile.party end, + set = function(v) + self.db.profile.party = v + self:Stickum("PARTY",v) + end + }, + guild = { + name = string.format(L["%sSticky"], L["Guild"]), + desc = string.format(L["Toggles sticky on and off for %s."], L["Guild"]), + type = "toggle", + order = 105, + get = function() return self.db.profile.guild end, + set = function(v) + self.db.profile.guild = v + self:Stickum("GUILD",v) + end + }, + officer = { + name = string.format(L["%sSticky"], L["Officer"]), + desc = string.format(L["Toggles sticky on and off for %s."], L["Officer"]), + type = "toggle", + order = 106, + get = function() return self.db.profile.officer end, + set = function(v) + self.db.profile.officer = v + self:Stickum("OFFICER",v) + end + }, + raid = { + name = string.format(L["%sSticky"], L["Raid"]), + desc = string.format(L["Toggles sticky on and off for %s."], L["Raid"]), + type = "toggle", + order = 107, + get = function() return self.db.profile.raid end, + set = function(v) + self.db.profile.raid = v + self:Stickum("RAID",v) + end + }, + raidwarning = { + name = string.format(L["%sSticky"], L["RaidWarning"]), + desc = string.format(L["Toggles sticky on and off for %s."], L["RaidWarning"]), + type = "toggle", + order = 108, + get = function() return self.db.profile.raid_warning end, + set = function(v) + self.db.profile.raid_warning = v + self:Stickum("RAID_WARNING",v) + end + }, + battleground = { + name = string.format(L["%sSticky"], L["Battleground"]), + desc = string.format(L["Toggles sticky on and off for %s."], L["Battleground"]), + type = "toggle", + order = 109, + get = function() return self.db.profile.battleground end, + set = function(v) + self.db.profile.battleground = v + self:Stickum("BATTLEGROUND",v) + end + }, + channel = { + name = string.format(L["%sSticky"], L["Channel"]), + desc = string.format(L["Toggles sticky on and off for %s."], L["Channel"]), + type = "toggle", + order = 110, + get = function() return self.db.profile.channel end, + set = function(v) + self.db.profile.channel = v + self:Stickum("CHANNEL",v) + end + }, + emote = { + name = string.format(L["%sSticky"], L["Emote"]), + desc = string.format(L["Toggles sticky on and off for %s."], L["Emote"]), + type = "toggle", + order = 111, + get = function() return self.db.profile.emote end, + set = function(v) + self.db.profile.emote = v + self:Stickum("EMOTE",v) + end + } + } + }, + toggle = { + name = L["Toggle"], + desc = L["Toggle the module on and off."], + type = "toggle", + order = 200, + get = function() return self.db.profile.on end, + set = function() self.db.profile.on = Prat:ToggleModuleActive("stickychannels") end + } + } + } +end + +function Prat_StickyChannels:OnEnable() + self:Stickum("SAY",self.db.profile.say) + self:Stickum("WHISPER",self.db.profile.whisper) + self:Stickum("YELL",self.db.profile.yell) + self:Stickum("PARTY",self.db.profile.party) + self:Stickum("GUILD",self.db.profile.guild) + self:Stickum("OFFICER",self.db.profile.officer) + self:Stickum("RAID",self.db.profile.raid) + self:Stickum("RAID_WARNING",self.db.profile.raid_warning) + self:Stickum("BATTLEGROUND",self.db.profile.battleground) + self:Stickum("CHANNEL",self.db.profile.channel) + self:Stickum("EMOTE",self.db.profile.emote) +end + +function Prat_StickyChannels:OnDisable() + self:Stickum("SAY",false) + self:Stickum("WHISPER",false) + self:Stickum("YELL",false) + self:Stickum("PARTY",false) + self:Stickum("GUILD",false) + self:Stickum("OFFICER",false) + self:Stickum("RAID",false) + self:Stickum("RAID_WARNING",false) + self:Stickum("BATTLEGROUND",false) + self:Stickum("CHANNEL",false) + self:Stickum("EMOTE",false) +end + +function Prat_StickyChannels:Stickum(channel,stickied) + if stickied then + ChatTypeInfo[channel].sticky = 1 + else + ChatTypeInfo[channel].sticky = 0 + end +end diff --git a/modules/TellTarget.lua b/modules/TellTarget.lua new file mode 100755 index 0000000..9086073 --- /dev/null +++ b/modules/TellTarget.lua @@ -0,0 +1,122 @@ +--[[ +Name: Prat_TellTarget +Revision: $Revision: 16019 $ +Author(s): Curney (asml8ed@gmail.com) + Krtek (krtek4@gmail.com) +Inspired by: idChat2_TellTarget by Industrial + ChatFrameExtender by Satrina +Website: http://www.wowace.com/files/index.php?path=Prat/ +Documentation: http://www.wowace.com/wiki/Prat/Integrated_Modules#TellTarget +SVN: http://svn.wowace.com/wowace/trunk/Prat/ +Description: Module for Prat that adds a slash command (/tt) to send a message to your target (default=on). +Dependencies: Prat +]] + +local L = AceLibrary("AceLocale-2.2"):new("PratTellTarget") + +L:RegisterTranslations("enUS", function() return { + ["TellTarget"] = true, + ["Adds telltarget slash command (/tt)."] = true, + ["Toggle"] = true, + ["Toggle the module on and off."] = true, + ["Target does not exist."] = true, + ["Target is not a player."] = true, + ["No target selected."] = true, + ["NoTarget"] = true, +} end) + +L:RegisterTranslations("ruRU", function() return { + ["TellTarget"] = "TellTarget", + ["Adds telltarget slash command (/tt)."] = "Добавляет слеш-команду 'сказать о цели' (/tt).", + ["Toggle"] = "Вкл/Выкл", + ["Toggle the module on and off."] = "Вкл/Выкл модуль.", + ["Target does not exist."] = "Цель не существует.", + ["Target is not a player."] = "Выбранная цель не является игроком.", + ["No target selected."] = "Нет выбранной цели.", + ["NoTarget"] = "Нет цели", +} end) + +L:RegisterTranslations("zhCN", function() return { + ["TellTarget"] = "通知目标", + ["Adds telltarget slash command (/tt)."] = "添加通知目标命令(/tt)。", + ["Toggle"] = "切换", + ["Toggle the module on and off."] = "切换此模块的打开与关闭", + ["Target does not exist."] = "目标不存在。", + ["Target is not a player."] = "目标非玩家。", + ["No target selected."] = "无目标选定。", + ["NoTarget"] = "无目标", +} end) + +L:RegisterTranslations("koKR", function() return { + ["TellTarget"] = "대상대화", + ["Adds telltarget slash command (/tt)."] = "대상대화를 위한 (/tt) 명령 추가", + ["Toggle"] = "전환", + ["Toggle the module on and off."] = "모듈 켜고 끄기 전환", + ["Target does not exist."] = "대상이 없습니다.", + ["Target is not a player."] = "올바른 대상이 아닙니다.", + ["No target selected."] = "선택된 대상이 없습니다.", + ["NoTarget"] = "대상없음", +} end) + +Prat_TellTarget = Prat:NewModule("telltarget") + +function Prat_TellTarget:OnInitialize() + self.db = Prat:AcquireDBNamespace("TellTarget") + Prat:RegisterDefaults("TellTarget", "profile", { + on = true, + }) + Prat.Options.args.telltarget = { + name = L["TellTarget"], + desc = L["Adds telltarget slash command (/tt)."], + type = "group", + args = { + toggle = { + name = L["Toggle"], + desc = L["Toggle the module on and off."], + type = "toggle", + get = function() return self.db.profile.on end, + set = function() self.db.profile.on = Prat:ToggleModuleActive("telltarget") end + } + } + } +end + +function Prat_TellTarget:OnEnable() + self:Hook("ChatEdit_ExtractTellTarget") + self:Hook("ChatEdit_ParseText") +end + +function Prat_TellTarget:TellTarget(msg) + if not UnitExists("target") then return end + if not UnitIsPlayer("target") then Prat:Print(L["Target is not a player."]); return end + if not UnitName("target") then Prat:Print(L["No target selected."]); return end + local cf = DEFAULT_CHAT_FRAME + cf.editBox.chatType = "WHISPER" + cf.editBox.tellTarget = UnitName("target") + ChatEdit_UpdateHeader(cf.editBox) + if not cf.editBox:IsVisible() then + ChatFrame_OpenChat("", cf) + end +end + +function Prat_TellTarget:ChatEdit_ExtractTellTarget(editBox, msg) + self.hooks["ChatEdit_ExtractTellTarget"](editBox, msg) + if (editBox.tellTarget == "%t") then + editBox.tellTarget = UnitName("target") or L["NoTarget"] + ChatEdit_UpdateHeader(editBox) + end +end + +function Prat_TellTarget:ChatEdit_ParseText(editBox, send) + self.hooks["ChatEdit_ParseText"](editBox, send) + local text = editBox:GetText() + if not (strsub(text, 1, 1) ~= "/") then + local command = gsub(text, "/([^%s]+)%s(.*)", "/%1", 1) + if command == "/tt" then + editBox.chatType = "WHISPER" + editBox.tellTarget = UnitName("target") or L["NoTarget"] + ChatEdit_UpdateHeader(editBox) + editBox:SetText(strsub(text, strlen(command) + 2)) + end + end +end diff --git a/modules/Timestamps.lua b/modules/Timestamps.lua new file mode 100755 index 0000000..fd57188 --- /dev/null +++ b/modules/Timestamps.lua @@ -0,0 +1,468 @@ +--[[ +Name: Prat_Timestamps +Revision: $Revision: 16065 $ +Author(s): Curney (asml8ed@gmail.com) + Krtek (krtek4@gmail.com) +Inspired by: idChat2_Timestamps by Industrial +Website: http://www.wowace.com/files/index.php?path=Prat/ +Documentation: http://www.wowace.com/wiki/Prat/Integrated_Modules#Timestamps +SVN: http://svn.wowace.com/wowace/trunk/Prat/ +Description: Module for Prat that adds timestamps to chat windows (default=on). +Dependencies: Prat +]] + +local L = AceLibrary("AceLocale-2.2"):new("PratTimestamps") + +L:RegisterTranslations("enUS", function() return { + ["Timestamps"] = true, + ["Timestamp options."] = true, + ["ShowMode"] = true, + ["Choose mode for showing timestamps."] = true, + ["All"] = true, + ["Individual"] = true, + ["Show"] = true, + ["Toggle showing timestamp settings for each window."] = true, + ["Chat%dTimestampShow"] = true, + ["Toggles timestamp on and off for chat window %d."] = true, + ["ShowAll"] = true, + ["Toggles timestamp on and off for all chat windows."] = true, + ["FormatMode"] = true, + ["Choose mode for setting timestamps."] = true, + ["Format"] = true, + ["Format the timestamp format for each window."] = true, + ["Chat%dTimestampFormat"] = true, + ["Set timestamp format for chat window %d (strftime format)."] = true, + ["FormatAll"] = true, + ["Set timestamp format for all chat windows (strftime format)."] = true, + ["Show Space"] = true, + ["Toggle adding space after timestamp."] = true, + ["Color Timestamp"] = true, + ["Toggle the coloring the timestamp on and off."] = true, + ["Set Color"] = true, + ["Change the color of the timestamp."] = true, + ["Use Local Time"] = true, + ["Toggle using local time on and off."] = true, + ["Toggle"] = true, + ["Toggle the module on and off."] = true, +} end) + +L:RegisterTranslations("ruRU", function() return { + ["Timestamps"] = "Timestamps", + ["Timestamp options."] = "Настройки времени", + ["ShowMode"] = "Показать режим", + ["Choose mode for showing timestamps."] = "Выберите режим для показа времени.", + ["All"] = "Все", + ["Individual"] = "Индивидуальный ", + ["Show"] = "Показать", + ["Toggle showing timestamp settings for each window."] = "Вкл/Выкл показ настроек времени для каждого окна.", + ["Chat%dTimestampShow"] = "ПоказатьВремяЧата%d", + ["Toggles timestamp on and off for chat window %d."] = "Вкл/Выкл время для окна чата %d.", + ["ShowAll"] = "Показать все", + ["Toggles timestamp on and off for all chat windows."] = "Вкл/Выкл время для всех окон чата.", + ["FormatMode"] = "ФорматРежим", + ["Choose mode for setting timestamps."] = "Выберите режим для задания времени.", + ["Format"] = "Формат", + ["Format the timestamp format for each window."] = "Форматировать формат времени для каждого окна.", + ["Chat%dTimestampFormat"] = "ФорматВремениЧата%d", + ["Set timestamp format for chat window %d (strftime format)."] = "Установить формат времени для окна чата %d.", + ["FormatAll"] = "ФорматВсего", + ["Set timestamp format for all chat windows (strftime format)."] = "Установить формат времени для всех окон чата.", + ["Show Space"] = "Показать пробел", + ["Toggle adding space after timestamp."] = "Вкл/Выкл добавление пробела после времени.", + ["Color Timestamp"] = "Цвет времени", + ["Toggle the coloring the timestamp on and off."] = "Вкл/Выкл окраску времени.", + ["Set Color"] = "Установить цвет", + ["Change the color of the timestamp."] = "Изменить цвет времени.", + ["Use Local Time"] = "Использовать часовой пояс", + ["Toggle using local time on and off."] = "Вкл/Выкл использование локального времени.", + ["Toggle"] = "Вкл/Выкл", + ["Toggle the module on and off."] = "Вкл/Выкл модуль.", +} end) + +L:RegisterTranslations("zhCN", function() return { + ["Timestamps"] = "时间", + ["Timestamp options."] = "聊天时间显示选项。", + ["ShowMode"] = "显示模式", + ["Choose mode for showing timestamps."] = "选择聊天窗口显示时间模式。", + ["All"] = "全部", + ["Individual"] = "独立", + ["Show"] = "显示", + ["Toggle showing timestamp settings for each window."] = "切换每一聊天窗口切换显示时间标签设置。", + ["Chat%dTimestampShow"] = "窗口%d时间显示", + ["Toggles timestamp on and off for chat window %d."] = "切换聊天窗口%d时间显示功能打开与关闭。", + ["ShowAll"] = "全部显示", + ["Toggles timestamp on and off for all chat windows."] = "切换所有聊天窗口时间显示的打开与关闭。", + ["FormatMode"] = "格式模式", + ["Choose mode for setting timestamps."] = "选择时间标签的格式设置。", + ["Format"] = "格式化", + ["Format the timestamp format for each window."] = "格式化每一窗口的时间显示。", + ["Chat%dTimestampFormat"] = "窗口%d时间格式", + ["Set timestamp format for chat window %d (strftime format)."] = "设置窗口%d的时间格式 (strftime格式)。", + ["FormatAll"] = "全部格式", + ["Set timestamp format for all chat windows (strftime format)."] = "设置全部窗口的时间格式 (strftime 格式)。", + ["Show Space"] = "空格", + ["Toggle adding space after timestamp."] = "切换时间标签后的空格。", + ["Color Timestamp"] = "颜色", + ["Toggle the coloring the timestamp on and off."] = "切换时间标签颜色功能打开与关闭。", + ["Set Color"] = "设置颜色", + ["Change the color of the timestamp."] = "更改时间标签颜色.", + ["Use Local Time"] = "本地时间", + ["Toggle using local time on and off."] = "切换服务器时间和本地时间.", + ["Toggle"] = "切换", + ["Toggle the module on and off."] = "切换此模块的打开与关闭。", +} end) + +L:RegisterTranslations("koKR", function() return { + ["Timestamps"] = "타임스탬프", + ["Timestamp options."] = "타임스탬프 설정입니다.", + ["ShowMode"] = "표시 모드", + ["Choose mode for showing timestamps."] = "타임스탬프 표시에 대한 모드를 선택합니다.", + ["All"] = "모두", + ["Individual"] = "개별", + ["Show"] = "표시", + ["Toggle showing timestamp settings for each window."] = "각 창에 대한 타임스탬프 표시 전환", + ["Chat%dTimestampShow"] = "대화%d 타임스탬프 표시", + ["Toggles timestamp on and off for chat window %d."] = "대화창%d에 대한 타임스템프를 켜고 끄기를 전환합니다.", + ["ShowAll"] = "모두 표시", + ["Toggles timestamp on and off for all chat windows."] = "모든 대화창에 대한 타임스탬프 켜고 끄기를 전환합니다.", + ["FormatMode"] = "형식 모드", + ["Choose mode for setting timestamps."] = "타임스탬프 설정에 대한 모드를 선택합니다.", + ["Format"] = "형식", + ["Format the timestamp format for each window."] = "각 창에 대한 타임스탬프 형식을 설정합니다.", + ["Chat%dTimestampFormat"] = "대화%d 타임스탬프 형식", + ["Set timestamp format for chat window %d (strftime format)."] = "대화창 %d에 대한 타임스탬프 형식을 설정합니다(strftime format).", + ["FormatAll"] = "모든 형식", + ["Set timestamp format for all chat windows (strftime format)."] = "모든 대화창에 대한 타임스탬프 형식을 설정합니다(strftime format).", + ["Show Space"] = "빈칸 표시", + ["Toggle adding space after timestamp."] = "타임스탬프 다음에 빈 칸 추가를 전환합니다.", + ["Color Timestamp"] = "타임스탬프 색상", + ["Toggle the coloring the timestamp on and off."] = "타임스탬프 색상 켜고 끄기를 전환합니다.", + ["Set Color"] = "색상 설정", + ["Change the color of the timestamp."] = "타임스탬프의 색상을 변경합니다.", + ["Use Local Time"] = "로컬 시간 사용", + ["Toggle using local time on and off."] = "로컬 시간 사용 켜고 끄기를 전환합니다.", + ["Toggle"] = "전환", + ["Toggle the module on and off."] = "모듈 켜고 끄기를 전환합니다.", +} end) + +Prat_Timestamps = Prat:NewModule("timestamps") + +function Prat_Timestamps:OnInitialize() + self.db = Prat:AcquireDBNamespace("Timestamps") + Prat:RegisterDefaults("Timestamps", "profile", { + on = true, + showmode = "ALL", + show = {true, true, true, true, true, true, true}, + showall = true, + formatmode = "ALL", + format = {"%X", "%X", "%X", "%X", "%X", "%X", "%X"}, + formatall = "%X", + colortimestamp = true, + color = { + r = 1, + g = 1, + b = 1 + }, + space = true, + localtime = true + }) + Prat.Options.args.timestamps = { + name = L["Timestamps"], + desc = L["Timestamp options."], + type = "group", + args = { + showmode = { + name = L["ShowMode"], + desc = L["Choose mode for showing timestamps."], + type = "text", + order = 100, + get = function() return self.db.profile.showmode end, + set = function(v) self.db.profile.showmode = v end, + validate = {["ALL"] = L["All"], ["INDIVIDUAL"] = L["Individual"]} + }, + show = { + name = L["Show"], + desc = L["Toggle showing timestamp settings for each window."], + type = "group", + order = 110, + args = { + chat1 = { + name = string.format(L["Chat%dTimestampShow"], 1), + desc = string.format(L["Toggles timestamp on and off for chat window %d."], 1), + type = "toggle", + get = function() return self.db.profile.show[1] end, + set = function(v) self.db.profile.show[1] = v; self:SetFrameStatus(1) end + }, + chat2 = { + name = string.format(L["Chat%dTimestampShow"], 2), + desc = string.format(L["Toggles timestamp on and off for chat window %d."], 2), + type = "toggle", + get = function() return self.db.profile.show[2] end, + set = function(v) self.db.profile.show[2] = v; self:SetFrameStatus(2) end + }, + chat3 = { + name = string.format(L["Chat%dTimestampShow"], 3), + desc = string.format(L["Toggles timestamp on and off for chat window %d."], 3), + type = "toggle", + get = function() return self.db.profile.show[3] end, + set = function(v) self.db.profile.show[3] = v; self:SetFrameStatus(3) end + }, + chat4 = { + name = string.format(L["Chat%dTimestampShow"], 4), + desc = string.format(L["Toggles timestamp on and off for chat window %d."], 4), + type = "toggle", + get = function() return self.db.profile.show[4] end, + set = function(v) self.db.profile.show[4] = v; self:SetFrameStatus(4) end + }, + chat5 = { + name = string.format(L["Chat%dTimestampShow"], 5), + desc = string.format(L["Toggles timestamp on and off for chat window %d."], 5), + type = "toggle", + get = function() return self.db.profile.show[5] end, + set = function(v) self.db.profile.show[5] = v; self:SetFrameStatus(5) end + }, + chat6 = { + name = string.format(L["Chat%dTimestampShow"], 6), + desc = string.format(L["Toggles timestamp on and off for chat window %d."], 6), + type = "toggle", + get = function() return self.db.profile.show[6] end, + set = function(v) self.db.profile.show[6] = v; self:SetFrameStatus(6) end + }, + chat7 = { + name = string.format(L["Chat%dTimestampShow"], 7), + desc = string.format(L["Toggles timestamp on and off for chat window %d."], 7), + type = "toggle", + get = function() return self.db.profile.show[7] end, + set = function(v) self.db.profile.show[7] = v; self:SetFrameStatus(7) end + }, + }, + disabled = function() if self.db.profile.showmode == "ALL" then return true else return false end end, + }, + showall = { + name = L["ShowAll"], + desc = L["Toggles timestamp on and off for all chat windows."], + type = "toggle", + order = 120, + get = function() return self.db.profile.showall end, + set = function(v) + self.db.profile.showall = v + for i=1,NUM_CHAT_WINDOWS do + self.db.profile.show[i] = v + self:SetFrameStatus(i) + end + end, + disabled = function() if self.db.profile.showmode == "INDIVIDUAL" then return true else return false end end, + }, + formatmode = { + name = L["FormatMode"], + desc = L["Choose mode for setting timestamps."], + type = "text", + order = 130, + get = function() return self.db.profile.formatmode end, + set = function(v) self.db.profile.formatmode = v end, + validate = {["ALL"] = L["All"], ["INDIVIDUAL"] = L["Individual"]} + }, + format = { + name = L["Format"], + desc = L["Format the timestamp format for each window."], + type = "group", + order = 140, + args = { + chat1 = { + name = string.format(L["Chat%dTimestampFormat"], 1), + desc = string.format(L["Set timestamp format for chat window %d (strftime format)."], 1), + type = "text", + order = 150, + usage = "", + get = function() return self.db.profile.format[1] end, + set = function(v) self.db.profile.format[1] = v end + }, + chat2 = { + name = string.format(L["Chat%dTimestampFormat"], 2), + desc = string.format(L["Set timestamp format for chat window %d (strftime format)."], 2), + type = "text", + order = 150, + usage = "", + get = function() return self.db.profile.format[2] end, + set = function(v) self.db.profile.format[2] = v end + }, + chat3 = { + name = string.format(L["Chat%dTimestampFormat"], 3), + desc = string.format(L["Set timestamp format for chat window %d (strftime format)."], 3), + type = "text", + order = 150, + usage = "", + get = function() return self.db.profile.format[3] end, + set = function(v) self.db.profile.format[3] = v end + }, + chat4 = { + name = string.format(L["Chat%dTimestampFormat"], 4), + desc = string.format(L["Set timestamp format for chat window %d (strftime format)."], 4), + type = "text", + order = 150, + usage = "", + get = function() return self.db.profile.format[4] end, + set = function(v) self.db.profile.format[4] = v end + }, + chat5 = { + name = string.format(L["Chat%dTimestampFormat"], 5), + desc = string.format(L["Set timestamp format for chat window %d (strftime format)."], 5), + type = "text", + order = 150, + usage = "", + get = function() return self.db.profile.format[5] end, + set = function(v) self.db.profile.format[5] = v end + }, + chat6 = { + name = string.format(L["Chat%dTimestampFormat"], 6), + desc = string.format(L["Set timestamp format for chat window %d (strftime format)."], 6), + type = "text", + order = 150, + usage = "", + get = function() return self.db.profile.format[6] end, + set = function(v) self.db.profile.format[6] = v end + }, + chat7 = { + name = string.format(L["Chat%dTimestampFormat"], 7), + desc = string.format(L["Set timestamp format for chat window %d (strftime format)."], 7), + type = "text", + order = 150, + usage = "", + get = function() return self.db.profile.format[7] end, + set = function(v) self.db.profile.format[7] = v end + }, + }, + disabled = function() if self.db.profile.formatmode == "ALL" then return true else return false end end, + }, + formatall = { + name = L["FormatAll"], + desc = L["Set timestamp format for all chat windows (strftime format)."], + type = "text", + order = 150, + usage = "", + get = function() return self.db.profile.formatall end, + set = function(v) + self.db.profile.formatall = v + for i=1,NUM_CHAT_WINDOWS do + self.db.profile.format[i] = v + end + end, + disabled = function() if self.db.profile.formatmode == "INDIVIDUAL" then return true else return false end end, + }, + showspace = { + name = L["Show Space"], + desc = L["Toggle adding space after timestamp."], + type = "toggle", + order = 160, + get = function() return self.db.profile.space end, + set = function(v) self.db.profile.space = v end, + }, + colortimestamp = { + name = L["Color Timestamp"], + desc = L["Toggle the coloring the timestamp on and off."], + type = "toggle", + order = 170, + get = function() return self.db.profile.colortimestamp end, + set = function(v) self.db.profile.colortimestamp = v end + }, + setcolor = { + name = L["Set Color"], + desc = L["Change the color of the timestamp."], + type = "color", + order = 180, + get = function() return self.db.profile.color.r, self.db.profile.color.g, self.db.profile.color.b end, + set = function(r, g, b, a) self.db.profile.color.r, self.db.profile.color.g, self.db.profile.color.b = r, g, b end, + disabled = function() if not self.db.profile.colortimestamp then return true else return false end end, + }, + localtime = { + name = L["Use Local Time"], + desc = L["Toggle using local time on and off."], + type = "toggle", + order = 190, + get = function() return self.db.profile.localtime end, + set = function(v) self.db.profile.localtime = v end + }, + toggle = { + name = L["Toggle"], + desc = L["Toggle the module on and off."], + type = "toggle", + order = 200, + get = function() return self.db.profile.on end, + set = function() self.db.profile.on = Prat:ToggleModuleActive("timestamps") end + } + } + } +end + +function Prat_Timestamps:OnEnable() + for i=1,NUM_CHAT_WINDOWS do + Prat_Timestamps:SetFrameStatus(i) + end + + self.secondsDifference = 0 + _,self.lastMinute = GetGameTime() +end + +function Prat_Timestamps:SetFrameStatus(id) + if self.db.profile.show[id] then + if not self:IsHooked(getglobal("ChatFrame"..id), "AddMessage") then self:Hook(getglobal("ChatFrame"..id), "AddMessage", "AddMessage"..id) end + else + if self:IsHooked(getglobal("ChatFrame"..id), "AddMessage") then self:Unhook(getglobal("ChatFrame"..id), "AddMessage") end + end +end + +function Prat_Timestamps:AddMessage1(...) + Prat_Timestamps:AddMessage(1, unpack(arg)) +end +function Prat_Timestamps:AddMessage2(...) + Prat_Timestamps:AddMessage(2, unpack(arg)) +end +function Prat_Timestamps:AddMessage3(...) + Prat_Timestamps:AddMessage(3, unpack(arg)) +end +function Prat_Timestamps:AddMessage4(...) + Prat_Timestamps:AddMessage(4, unpack(arg)) +end +function Prat_Timestamps:AddMessage5(...) + Prat_Timestamps:AddMessage(5, unpack(arg)) +end +function Prat_Timestamps:AddMessage6(...) + Prat_Timestamps:AddMessage(6, unpack(arg)) +end +function Prat_Timestamps:AddMessage7(...) + Prat_Timestamps:AddMessage(7, unpack(arg)) +end + +function Prat_Timestamps:AddMessage(id, frame, text, ...) + if text then + -- get timestamp format + local dateFormat + dateFormat = self.db.profile.format[id] + -- get space character useage + if ( self.db.profile.space ) then space = " " else space = "" end + -- get color format + if self.db.profile.colortimestamp then + local color = string.format("%02x%02x%02x", self.db.profile.color.r*255, self.db.profile.color.g*255, self.db.profile.color.b*255) + text = string.format("|cff"..color.."%s|r" .. space .. "%s", self:GetTime(dateFormat), text) + else + text = string.format("%s".. space .. "%s", self:GetTime(dateFormat), text) + end + end + self.hooks[frame].AddMessage(frame, text, unpack(arg)) +end + +function Prat_Timestamps:GetTime(format) + if self.db.profile.localtime then + return date(format) + else + local tempDate = date("*t") + tempDate["hour"], tempDate["min"] = GetGameTime() + -- taken from FuBar_ClockFu + if self.lastMinute ~= tempDate["min"] then + _,self.lastMinute = GetGameTime() + self.secondsDifference = mod(time(), 60) + end + tempDate["sec"] = mod(time() - self.secondsDifference, 60) + return date(format, time(tempDate)) + end +end diff --git a/modules/UrlCopy.lua b/modules/UrlCopy.lua new file mode 100755 index 0000000..ddd8db9 --- /dev/null +++ b/modules/UrlCopy.lua @@ -0,0 +1,269 @@ +--[[ +Name: Prat_UrlCopy +Revision: $Revision: 16019 $ +Author(s): Krtek (krtek4@gmail.com) +Website: http://www.wowace.com/files/index.php?path=Prat/ +Documentation: http://www.wowace.com/wiki/Prat/Integrated_Modules#UrlCopy +SVN: http://svn.wowace.com/wowace/trunk/Prat/ +Description: Module for Prat that makes it easy to copy URL"s in chat windows. +Dependencies: Prat +]] + +local L = AceLibrary("AceLocale-2.2"):new("PratUrlCopy") + +L:RegisterTranslations("enUS", function() return { + ["UrlCopy"] = true, + ["URL formating options."] = true, + ["Show Brackets"] = true, + ["Toggle showing brackets on and off."] = true, + ["Use Popup"] = true, + ["Use popup window to show URL."] = true, + ["Color URL"] = true, + ["Toggle the URL color on and off."] = true, + ["Set Color"] = true, + ["Change the color of the URL."] = true, + ["Toggle"] = true, + ["Toggle the module on and off."] = true, +} end) + +L:RegisterTranslations("ruRU", function() return { + ["UrlCopy"] = "UrlCopy", + ["URL formating options."] = "Настройки формата URL-адреса.", + ["Show Brackets"] = "Показывать скобки", + ["Toggle showing brackets on and off."] = "Вкл/Выкл отображение скобок.", + ["Use Popup"] = "Всплывающее окно", + ["Use popup window to show URL."] = "Использовать всплывающее окно для отображение ссылки.", + ["Color URL"] = "Цвет ссылки", + ["Toggle the URL color on and off."] = "Вкл/Выкл окраску ссылок.", + ["Set Color"] = "Цвет", + ["Change the color of the URL."] = "Изменение цвета ссылок.", + ["Toggle"] = "Вкл/Выкл", + ["Toggle the module on and off."] = "Вкл/Выкл модуль.", +} end) + +L:RegisterTranslations("zhCN", function() return { + ["UrlCopy"] = "复制超链接", + ["URL formating options."] = "超链接格式选项。", + ["Show Brackets"] = "显示括号", + ["Toggle showing brackets on and off."] = "切换显示括号功能打开与关闭。", + ["Use Popup"] = "弹出", + ["Use popup window to show URL."] = "用弹出窗口显示超链接。", + ["Color URL"] = "颜色", + ["Toggle the URL color on and off."] = "切换超链接颜色打开与关闭。", + ["Set Color"] = "设置颜色", + ["Change the color of the URL."] = "更改超链接颜色。", + ["Toggle"] = "切换", + ["Toggle the module on and off."] = "切换此模块的打开与关闭。", +} end) + + +L:RegisterTranslations("koKR", function() return { + ["UrlCopy"] = "URL 복사", + ["URL formating options."] = "URL 형식 설정입니다.", + ["Show Brackets"] = "괄호 표시", + ["Toggle showing brackets on and off."] = "괄호 표시 켜고 끄기를 전환합니다.", + ["Use Popup"] = "팝업 사용", + ["Use popup window to show URL."] = "URL 표시를 위한 팝업창을 사용합니다.", + ["Color URL"] = "색상 URL", + ["Toggle the URL color on and off."] = "URL 색상 켜고 끄기를 전환합니다.", + ["Set Color"] = "색상 설정", + ["Change the color of the URL."] = "URL의 색상을 변경합니다.", + ["Toggle"] = "전환", + ["Toggle the module on and off."] = "모듈 켜고 끄기를 전환합니다.", +} end) + +Prat_UrlCopy = Prat:NewModule("urlcopy") + +function Prat_UrlCopy:OnInitialize() + self.db = Prat:AcquireDBNamespace("UrlCopy") + Prat:RegisterDefaults("UrlCopy", "profile", { + on = true, + bracket = true, + popup = true, + colorurl = true, + color = { + r = 1, + g = 1, + b = 1, + }, + }) + Prat.Options.args.urlcopy = { + name = L["UrlCopy"], + desc = L["URL formating options."], + type = "group", + args = { + bracket = { + name = L["Show Brackets"], + desc = L["Toggle showing brackets on and off."], + type = "toggle", + order = 160, + get = function() return self.db.profile.bracket end, + set = function(v) self.db.profile.bracket = v end, + }, + popup = { + name = L["Use Popup"], + desc = L["Use popup window to show URL."], + type = "toggle", + order = 170, + get = function() return self.db.profile.popup end, + set = function(v) self.db.profile.popup = v end, + }, + colorurl = { + name = L["Color URL"], + desc = L["Toggle the URL color on and off."], + type = "toggle", + order = 180, + get = function() return self.db.profile.colorurl end, + set = function(v) self.db.profile.colorurl = v end + }, + setcolor = { + name = L["Set Color"], + desc = L["Change the color of the URL."], + type = "color", + order = 190, + get = function() return self.db.profile.color.r, self.db.profile.color.g, self.db.profile.color.b end, + set = function(r, g, b, a) self.db.profile.color.r, self.db.profile.color.g, self.db.profile.color.b = r, g, b end, + disabled = function() if not self.db.profile.colorurl then return true else return false end end, + }, + toggle = { + name = L["Toggle"], + desc = L["Toggle the module on and off."], + type = "toggle", + order = 200, + get = function() return self.db.profile.on end, + set = function(v) self.db.profile.on = Prat:ToggleModuleActive("urlcopy") end + }, + } + } +end + + +function Prat_UrlCopy:OnEnable() + for i=1,NUM_CHAT_WINDOWS do + self:Hook(getglobal("ChatFrame"..i), "AddMessage") + end + + self:Hook("SetItemRef") +end + +-- Hook +function Prat_UrlCopy:AddMessage(frame, text, ...) + if text then + text = Prat_UrlCopy:ProcessText (text) + end + self.hooks[frame].AddMessage(frame, text, unpack(arg)) +end + +function Prat_UrlCopy:SetItemRef(link, text, button) + if ( strsub(link, 1, 3) == "url" ) then + Prat_UrlCopy:ShowUrl(link) + else + self.hooks.SetItemRef(link, text, button); + end +end + +-- Utility Function +function Prat_UrlCopy:Link (link) + local returnedLink = " " + + if self.db.profile.colorurl then + local color = string.format("%02x%02x%02x", self.db.profile.color.r*255, self.db.profile.color.g*255, self.db.profile.color.b*255) + returnedLink = " |cff" .. color + end + + returnedLink = returnedLink .. "|Hurl:" .. link .. "|h" + + if ( self.db.profile.bracket ) then + returnedLink = returnedLink .. "[" .. link .. "]" + else + returnedLink = returnedLink .. link + end + + returnedLink = returnedLink .. "|h|r " + + return returnedLink +end + +function Prat_UrlCopy:ProcessText(text) + text = Prat_UrlCopy:Decompose(text) + + return text +end + +function Prat_UrlCopy:Decompose (chatstring) + if chatstring ~= nil then + -- www.X.Y url + chatstring = string.gsub (chatstring, " www%.([_A-Za-z0-9-]+)%.(%S+)%s?", Prat_UrlCopy:Link ("www.%1.%2")) + + -- X://Y url + chatstring = string.gsub (chatstring, " (%a+)://(%S+)%s?", Prat_UrlCopy:Link ("%1://%2")) + + -- X@X.Y url (---> email) + chatstring = string.gsub (chatstring, " ([_A-Za-z0-9-%.]+)@([_A-Za-z0-9-]+)%.([_A-Za-z0-9-%.]+)%s?", Prat_UrlCopy:Link ("%1@%2.%3")) + + -- XXX.YYY.ZZZ.WWW:VVVV url (IP of ts server for example) + chatstring = string.gsub (chatstring, " (%d%d?%d?)%.(%d%d?%d?)%.(%d%d?%d?)%.(%d%d?%d?):(%d%d?%d?%d?%d?)%s?", Prat_UrlCopy:Link ("%1.%2.%3.%4:%5")) + + -- XXX.YYY.ZZZ.WWW url (---> IP) + chatstring = string.gsub (chatstring, " (%d%d?%d?)%.(%d%d?%d?)%.(%d%d?%d?)%.(%d%d?%d?)%s?", Prat_UrlCopy:Link ("%1.%2.%3.%4")) + + -- X.Y.Z url + chatstring = string.gsub (chatstring, " ([_A-Za-z0-9-]+)%.([_A-Za-z0-9-]+)%.(%S+)%s?", Prat_UrlCopy:Link ("%1.%2.%3")) + + -- X.Y.Z:WWWW url (ts server for example) + chatstring = string.gsub (chatstring, " ([_A-Za-z0-9-]+)%.([_A-Za-z0-9-]+)%.(%S+)%:([_0-9-]+)%s?", Prat_UrlCopy:Link ("%1.%2.%3:%4")) + + end + return chatstring +end + +function Prat_UrlCopy:StaticPopupUrl(link) + StaticPopupDialogs["SHOW_URL"] = { + text = "URL : %s", + button2 = TEXT(ACCEPT), + hasEditBox = 1, + hasWideEditBox = 1, + showAlert = 1, -- HACK : it"s the only way I found to make de StaticPopup have sufficient width to show WideEditBox :( + + OnShow = function() + local editBox = getglobal(this:GetName().."WideEditBox"); + editBox:SetText(format(link)); + editBox:SetFocus(); + editBox:HighlightText(0); + + local button = getglobal(this:GetName().."Button2"); + button:ClearAllPoints(); + button:SetWidth(200); + button:SetPoint("CENTER", editBox, "CENTER", 0, -30); + + getglobal(this:GetName().."AlertIcon"):Hide(); -- HACK : we hide the false AlertIcon + end, + + OnHide = function() end, + OnAccept = function() end, + OnCancel = function() end, + EditBoxOnEscapePressed = function() this:GetParent():Hide(); end, + timeout = 0, + whileDead = 1, + hideOnEscape = 1 + }; + + StaticPopup_Show ("SHOW_URL", link); +end + +function Prat_UrlCopy:EditBoxUrl(link) + if (not ChatFrameEditBox:IsShown()) then + ChatFrame_OpenChat(link, DEFAULT_CHAT_FRAME); + else + ChatFrameEditBox:Insert(link); + end +end + +function Prat_UrlCopy:ShowUrl(link) + link = strsub(link, 5) + if ( self.db.profile.popup ) then + Prat_UrlCopy:StaticPopupUrl(link) + else + Prat_UrlCopy:EditBoxUrl(link) + end +end diff --git a/modules/Who.lua b/modules/Who.lua new file mode 100755 index 0000000..6fc7d71 --- /dev/null +++ b/modules/Who.lua @@ -0,0 +1,417 @@ +--[[ +Name: Prat_Who +Revision: $Revision: 16019 $ +Author(s): Krtek (krtek4@gmail.com) +Website: http://www.wowace.com/files/index.php?path=Prat/ +Documentation: http://www.wowace.com/wiki/Prat/Integrated_Modules#Who +SVN: http://svn.wowace.com/wowace/trunk/Prat/ +Description: Module for Prat that makes it easy to copy URL"s in chat windows. +Dependencies: Prat +]] + +PRAT_WHO_FRAME_HEIGHT = 16 +LIST_TO_DISPLAY = 20 + +local L = AceLibrary("AceLocale-2.2"):new("PratWho") + +L:RegisterTranslations("enUS", function() return { + ["Who"] = true, + ["Adds actions for channels and /chatwho."] = true, + ["List user(s) on channel."] = true, + [" (see /PRAT who chanlist)"] = true, + ["Chanlist"] = true, + ["Channel List."] = true, + ["Join message"] = true, + ["Toggle showing of join message."] = true, + ["Leave message"] = true, + ["Toggle showing of leave message."] = true, + ["GUI"] = true, + ["Toggle showing information in a nice GUI or in default chat window."] = true, + ["Toggle"] = true, + ["Toggle the module on and off."] = true, + + ["Channels listing"] = true, + ["Who is on that chan"] = true, + ["Back to channel list"] = true, +} end) + +L:RegisterTranslations("ruRU", function() return { + ["Who"] = "Who", + ["Adds actions for channels and /chatwho."] = "Добавляет действия для каналов и /chatwho.", + ["List user(s) on channel."] = "Список пользователей канала.", + [" (see /PRAT who chanlist)"] = "<название канала> (см. /PRAT who chanlist)", + ["Chanlist"] = "Chanlist", + ["Channel List."] = "Список каналов.", + ["Join message"] = "Сообщение присоединения", + ["Toggle showing of join message."] = "Вкл/Выкл показ сообщения при присоед.", + ["Leave message"] = "Сообщение ухода", + ["Toggle showing of leave message."] = "Вкл/Выкл показ сообщения при уходе.", + ["GUI"] = "GUI", + ["Toggle showing information in a nice GUI or in default chat window."] = "Вкл/Выкл показать информации в хорошем GUI или в стандартном окне чата.", + ["Toggle"] = "Вкл/Выкл", + ["Toggle the module on and off."] = "Вкл/Выкл модуль.", + + ["Channels listing"] = "Список каналов", + ["Who is on that chan"] = "Кто сейчас в этом канале", + ["Back to channel list"] = "Вернуться к списку каналов", +} end) + +L:RegisterTranslations("zhCN", function() return { + ["Who"] = "查询", + ["Adds actions for channels and /chatwho."] = "输入/chatwho列出频道和聊天对象.", + ["List user(s) on channel."] = "列出频道中的用户。", + [" (see /PRAT who chanlist)"] = "<频道名称> (查看 /PRAT who chanlist)", + ["Chanlist"] = "频道列表", + ["Channel List."] = "频道列表。", + ["Join message"] = "加入信息", + ["Toggle showing of join message."] = "显示加入信息。", + ["Leave message"] = "离开信息", + ["Toggle showing of leave message."] = "显示离开信息。", + ["GUI"] = "图形界面", + ["Toggle showing information in a nice GUI or in default chat window."] = "在图形界面或默认聊天窗口显示信息。", + ["Toggle"] = "切换", + ["Toggle the module on and off."] = "切换此模块打开与关闭。", + + ["Channels listing"] = "频道列表", + ["Who is on that chan"] = "在此频道查询", + ["Back to channel list"] = "返回频道列表", +} end) + +L:RegisterTranslations("koKR", function() return { + ["Who"] = "누구", + ["Adds actions for channels and /chatwho."] = "채널 목록과 /chatwho에 대한 동작을 추가합니다.", + ["List user(s) on channel."] = "채널 사용자 목록입니다.", + [" (see /PRAT who chanlist)"] = "<채널명> (명령어: /PRAT who chanlist)", + ["Chanlist"] = "채널목록", + ["Channel List."] = "채널 목록입니다.", + ["Join message"] = "참여 메세지", + ["Toggle showing of join message."] = "참여 메세지 표시를 전환합니다.", + ["Leave message"] = "나감 메세지", + ["Toggle showing of leave message."] = "나감 메세지 표시를 전환합니다.", + ["GUI"] = "GUI", + ["Toggle showing information in a nice GUI or in default chat window."] = "기본대화창이나 GUI 에 정보 표시를 전환합니다.", + ["Toggle"] = "전환", + ["Toggle the module on and off."] = "모듈 켜고 끄기를 전환합니다.", + + ["Channels listing"] = "채널 목록", + ["Who is on that chan"] = "채널 접속 목록", + ["Back to channel list"] = "목록으로 돌아가기", +} end) + +Prat_Who = Prat:NewModule("who") + +function Prat_Who:OnInitialize() + self.db = Prat:AcquireDBNamespace("Who") + Prat:RegisterDefaults("Who", "profile", { + on = true, + join = false, + leave = false, + gui = true, + }) + PratWhoFrame:RegisterForDrag("LeftButton"); + self.wantInfo = false; + self.info = ""; + self.lastChannel = ""; + self.List = {}; + self.selectedInList = 1; + Prat.Options.args.who = { + name = L["Who"], + desc = L["Adds actions for channels and /chatwho."], + type = "group", + args = { + who = { + name = L["Who"], + desc = L["List user(s) on channel."], + type = "text", + usage = L[" (see /PRAT who chanlist)"], + get = false, + set = function(v) Prat_Who:Who(v); end, + validate = function(v) return self:CheckChan(v); end, + error = "%s is not a valid channel (see /PRAT who chanlist)", + }, + chanlist = { + name = L["Chanlist"], + desc = L["Channel List."], + type = "execute", + func = function(v) Prat_Who:ChannelList(v); end, + }, + join = { + name = L["Join message"], + desc = L["Toggle showing of join message."], + type = "toggle", + get = function() return self.db.profile.join end, + set = function(v)self.db.profile.join = v end, + }, + leave = { + name = L["Leave message"], + desc = L["Toggle showing of leave message."], + type = "toggle", + get = function() return self.db.profile.leave end, + set = function(v)self.db.profile.leave = v end, + }, + gui = { + name = L["GUI"], + desc = L["Toggle showing information in a nice GUI or in default chat window."], + type = "toggle", + get = function() return self.db.profile.gui end, + set = function(v)self.db.profile.gui = v end, + }, + toggle = { + name = L["Toggle"], + desc = L["Toggle the module on and off."], + type = "toggle", + order = 200, + get = function() return self.db.profile.on end, + set = function() self.db.profile.on = Prat:ToggleModuleActive("who") end + } + } + } +end + +function Prat_Who:OnEnable() + self:Hook("ChatFrame_OnEvent") +end + +function Prat_Who:Who(channel) + self.wantInfo = true; + self.List = {}; + self.lastChannel = channel; + + ListChannelByName(channel); +end + +function Prat_Who:ShowWho() + for name in string.gfind(self.info, "([%S]+)[%s]*") do + table.insert(self.List, Prat_Who:BeautifyName(name)) + end + + local function whoSort(a, b) + local aWeight = 100 + if string.find(a, ".+%(owner%)") then aWeight = 0 + elseif string.find(a, ".+%(moderator%)") then aWeight = 50 + elseif string.find(a, ".+%(muted%)") then aWeight = 200 + end + + local bWeight = 100 + if string.find(b, ".+%(owner%)") then bWeight = 0 + elseif string.find(b, ".+%(moderator%)") then bWeight = 50 + elseif string.find(b, ".+%(muted%)") then bWeight = 200 + end + + if ( aWeight == bWeight ) then return a < b + else return aWeight < bWeight + end + end + + table.sort(self.List, whoSort) + + if ( self.db.profile.gui ) then + Prat_Who:UpdateFrame() + else + local text = "" + for i,name in self.List do + text = text .. name .. " " + end + text = "This feature is buggy for time, please use the GUI mode :D" + Prat_Who:NotGuiShow(text) + end +end + +function Prat_Who:ChannelList() + local chanList = self:GetChannelList(); + + self.List = {} + for number, name in chanList do + table.insert(self.List, number .. ". " .. name); + end + + if ( self.db.profile.gui ) then + Prat_Who:UpdateFrame() + else + local text = "" + for i,name in self.List do + text = text .. name .. " " + end + Prat_Who:NotGuiShow(text) + end +end + +function Prat_Who:GetChannelList() + self.lastChannel = ""; + local chanList = {}; + + local id, name + for i=1, 10 do + id, name = GetChannelName(i) + if ( id > 0 and name ) then table.insert(chanList, id, name) end + end + + return chanList +end + +function Prat_Who:CheckChan(channel) + local id, name = GetChannelName(channel) + if ( id > 0 and name ) then return true + else return false end +end + +-- The Hooking method isn"t good if some other addons need the information that are kept :( +function Prat_Who:ChatFrame_OnEvent(event) + if ( ( event == "CHAT_MSG_CHANNEL_LIST" ) and self.wantInfo) then + self.info = arg1 + + if not ( strsub(self.info,1,1) == "[" ) then + self:ShowWho() + end + + return; + end; + + if ( event == "CHAT_MSG_CHANNEL_JOIN" and self.db.profile.join and not self.db.profile.leave ) then + return; + end + + if ( event == "CHAT_MSG_CHANNEL_LEAVE" and self.db.profile.leave ) then + return; + end + + self.hooks.ChatFrame_OnEvent(event); +end + +-- Utility function +local beautifyTable = { + { pattern = "%*(.+)", replacement = "%1 (owner)", patternB = "(.+) %(owner%)", replacementB = "%1" }, + { pattern = "@(.+)", replacement = "%1 (moderator)", patternB = "(.+) %(moderator%)", replacementB = "%1" }, + { pattern = "#(.+)", replacement = "%1 (muted)", patternB = "(.+) %(muted%)", replacementB = "%1" }, +} + +function Prat_Who:BeautifyName(name) +-- * channel owner, @ channel moderator, # muted/unvoiced. + if name then + name = string.gsub(name, ",", ""); + + for id, option in beautifyTable do + name = string.gsub(name, option["pattern"], option["replacement"]); + end + + return name +end +end + +function Prat_Who:DebeautifyName(name) + if name then + for id, option in beautifyTable do + name = string.gsub(name, option["patternB"] , option["replacementB"]); + end + + return name +end +end + +function Prat_Who:Invite() + InviteByName(Prat_Who:DebeautifyName(self.List[self.selectedInList])); +end + +function Prat_Who:Whisper() + local name = Prat_Who:DebeautifyName(self.List[self.selectedInList]); + if ( not ChatFrameEditBox:IsVisible() ) then + ChatFrame_OpenChat("/w "..name.." "); + else + ChatFrameEditBox:SetText("/w "..name.." "); + end + ChatEdit_ParseText(ChatFrame1.editBox, 0); +end + +-- Frame function +function Prat_Who:NotGuiShow(text) + Prat:Print(tostring(text)) +end + +function Prat_Who:HideFrame() + PratWhoFrame:Hide(); + Prat_Who.wantInfo = false; +end + +function Prat_Who:UpdateFrame() + local listLength = table.getn(self.List) + local listOffset = FauxScrollFrame_GetOffset(PratWhoScrollFrame); + local listIndex; + local listButton; + + if ( not PratWhoFrame:IsVisible() ) then + PratWhoFrame:Show(); + self.selectedInList = 1; + end + + if ( self.lastChannel == "" ) then + PratWhoTitle:SetText(L["Channels listing"]); + PratWhoInviteButton:Hide(); + PratWhoWhisperButton:Hide(); + PratWhoToggleButton:SetText(L["Who is on that chan"]); + else + local id, name = GetChannelName(self.lastChannel) + PratWhoTitle:SetText(listLength .. " users on " .. id .. ". " .. name); + PratWhoInviteButton:Show(); + PratWhoWhisperButton:Show(); + PratWhoToggleButton:SetText(L["Back to channel list"]); + end + + for i=1, LIST_TO_DISPLAY, 1 do + listIndex = i + listOffset + + listButton = getglobal("PratWhoButton"..i); + + if ( listIndex > listLength ) then + listButton:Hide(); + else + listButton:SetID(listIndex); + + if ( listIndex == self.selectedInList ) then + listButton:LockHighlight(); + else + listButton:UnlockHighlight(); + end + + local name = self.List[listIndex] + + if Prat_PlayerNames then + local nameBis = string.gsub(name, "(.+) %(.+%)", "%1") + local nameColored = Prat_PlayerNames:addInfo(nameBis) + name = string.gsub(name, nameBis, nameColored) + end + + getglobal("PratWhoButton"..i.."ButtonTextName"):SetText(name); + listButton:Show(); + end + + end + + FauxScrollFrame_Update(PratWhoScrollFrame, listLength, LIST_TO_DISPLAY, PRAT_WHO_FRAME_HEIGHT ); +end + +function Prat_Who:RefreshFrame() + if ( self.lastChannel == "" ) then + Prat_Who:ChannelList(); + else + Prat_Who:Who(self.lastChannel); + end +end + +function Prat_Who:Button_OnClick() + self.selectedInList = this:GetID(); + Prat_Who:UpdateFrame() +end + +function Prat_Who_UpdateFrame() -- FauxScrollFrame_OnVerticalScroll() seems to doesn"t accept member function, so I have to declare this one + Prat_Who:UpdateFrame() +end + +function Prat_Who:ToggleChanListWho() + if ( self.lastChannel ~= "" ) then + self.lastChannel = "" + else + self.lastChannel = string.gsub(self.List[self.selectedInList], "(%d+)%..+", "%1") + end + + Prat_Who:RefreshFrame() +end