diff --git a/AtlasLootFu/AtlasLootFu.lua b/AtlasLootFu/AtlasLootFu.lua new file mode 100644 index 0000000..5237c65 --- /dev/null +++ b/AtlasLootFu/AtlasLootFu.lua @@ -0,0 +1,57 @@ +--[[ +Name : AtlasLootFu +Version : 1.1 +Author : Daviesh (oma_daviesh@hotmail.com) +Website : http://www.atlasloot.net +Description : Adds AtlasLoot to FuBar. +]] + +--Invoke libraries +local tablet = AceLibrary("Tablet-2.0"); + +--Define the plugin +AtlasLootFu = AceLibrary("AceAddon-2.0"):new("AceEvent-2.0", "AceDB-2.0", "FuBarPlugin-2.0"); +AtlasLootFu.title = "AtlasLootFu"; +AtlasLootFu.hasIcon = "Interface\\Icons\\INV_Box_01"; +AtlasLootFu.defaultPosition = "LEFT"; +AtlasLootFu.defaultMinimapPosition = 180; +AtlasLootFu.cannotDetachTooltip = true; + +-- Activate menu options to hide icon/text (no point in having the colour option) +AtlasLootFu.hasNoColor = true; +AtlasLootFu:RegisterDB("AtlasLootFuDB"); + +--Make sure the plugin is the rightt format when activated +function AtlasLootFu:OnEnable() + self:Update(); +end + +--Define text to display when the cursor mouses over the plugin +function AtlasLootFu:OnTooltipUpdate() + local cat = tablet:AddCategory() + cat:AddLine( + "text", ATLASLOOTFU_LEFTCLICK + ) + cat:AddLine( + "text", ATLASLOOTFU_SHIFTCLICK + ) + cat:AddLine( + "text", ATLASLOOTFU_LEFTDRAG + ) +end + +--Define what to do when the plugin is clicked +function AtlasLootFu:OnClick(button) + --Left click -> open loot browser + --Shift Left Click -> show options menu + --Right click -> standard FuBar options + if IsShiftKeyDown() then + AtlasLootOptions_Toggle(); + else + if AtlasLootDefaultFrame:IsVisible() then + AtlasLootDefaultFrame:Hide(); + else + AtlasLootDefaultFrame:Show(); + end + end +end diff --git a/AtlasLootFu/AtlasLootFu.toc b/AtlasLootFu/AtlasLootFu.toc new file mode 100644 index 0000000..efa8175 --- /dev/null +++ b/AtlasLootFu/AtlasLootFu.toc @@ -0,0 +1,24 @@ +## Interface: 30300 +## Title: AtlasLootFu +## Notes: Minimap button for AtlasLoot +## Title-zhTW: |r|cFF0099FF[地圖]|rAL 小地圖按鍵 +## Notes-zhTW: AtlasLoot 小地圖按鍵 +## Author: Hegarol +## Version: v5.11.04 +## X-eMail: manager@atlasloot.net +## X-Category: Map +## X-License: GPL v2 +## X-Website: http://www.atlasloot.net +## X-Embeds: Ace2, FuBarPlugin-2.0, TabletLib +## SavedVariables: AtlasLootFuDB +## Dependencies: AtlasLoot +## OptionalDeps: Ace2, FuBarPlugin-2.0, TabletLib, FuBar +## X-Curse-Packaged-Version: v5.11.04 +## X-Curse-Project-Name: Atlasloot Enhanced +## X-Curse-Project-ID: atlasloot-enhanced +## X-Curse-Repository-ID: wow/atlasloot-enhanced/mainline + +embeds.xml + +Locales.lua +AtlasLootFu.lua \ No newline at end of file diff --git a/AtlasLootFu/Libs/AceAddon-2.0/AceAddon-2.0.lua b/AtlasLootFu/Libs/AceAddon-2.0/AceAddon-2.0.lua new file mode 100644 index 0000000..8ad9c00 --- /dev/null +++ b/AtlasLootFu/Libs/AceAddon-2.0/AceAddon-2.0.lua @@ -0,0 +1,1445 @@ +--[[ +Name: AceAddon-2.0 +Revision: $Rev: 1100 $ +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/wiki/AceAddon-2.0 +SVN: http://svn.wowace.com/wowace/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 +License: LGPL v2.1 +]] + +local MAJOR_VERSION = "AceAddon-2.0" +local MINOR_VERSION = 90000 + tonumber(("$Revision: 1100 $"):match("(%d+)")) + +-- 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 not AceLibrary:HasInstance("AceOO-2.0") then error(MAJOR_VERSION .. " requires AceOO-2.0.") end + +local function safecall(func,...) + local success, err = pcall(func,...) + if not success then geterrorhandler()(err:find("%.lua:%d+:") and err or (debugstack():match("\n(.-: )in.-\n") or "") .. err) end +end +-- Localization +local STANDBY, TITLE, NOTES, VERSION, AUTHOR, DATE, CATEGORY, EMAIL, CREDITS, WEBSITE, CATEGORIES, ABOUT, LICENSE, PRINT_ADDON_INFO, DONATE, DONATE_DESC, HOWTO_DONATE_WINDOWS, HOWTO_DONATE_MAC +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 + LICENSE = "License" -- fix + + ABOUT = "Über" + PRINT_ADDON_INFO = "Gibt Addondaten aus" + DONATE = "Donate" -- fix + DONATE_DESC = "Give a much-needed donation to the author of this addon." -- fix + HOWTO_DONATE_WINDOWS = "Press Ctrl-A to select the link, then Ctrl-C to copy, then Alt-Tab out of the game, open your favorite web browser, and paste the link into the address bar." -- fix + HOWTO_DONATE_MAC = "Press Cmd-A to select the link, then Cmd-C to copy, then Cmd-Tab out of the game, open your favorite web browser, and paste the link into the address bar." -- fix + + CATEGORIES = { + ["Action Bars"] = "Aktionsleisten", + ["Auction"] = "Auktion", + ["Audio"] = "Audio", + ["Battlegrounds/PvP"] = "Schlachtfeld/PvP", + ["Buffs"] = "Stärkungszauber", + ["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", + ["Caster"] = "Zauberer", + ["Combat"] = "Kampf", + ["Compilations"] = "Zusammenstellungen", + ["Data Export"] = "Datenexport", + ["Development Tools"] = "Entwicklungs Tools", + ["Guild"] = "Gilde", + ["Frame Modification"] = "Frame Veränderungen", + ["Interface Enhancements"] = "Interface Verbesserungen", + ["Inventory"] = "Inventar", + ["Library"] = "Bibliotheken", + ["Map"] = "Karte", + ["Mail"] = "Post", + ["Miscellaneous"] = "Diverses", + ["Quest"] = "Quest", + ["Raid"] = "Schlachtzug", + ["Tradeskill"] = "Beruf", + ["UnitFrame"] = "Einheiten-Fenster", + } +elseif GetLocale() == "frFR" then + STANDBY = "|cffff5050(attente)|r" + + TITLE = "Titre" + NOTES = "Notes" + VERSION = "Version" + AUTHOR = "Auteur" + DATE = "Date" + CATEGORY = "Catégorie" + EMAIL = "E-mail" + WEBSITE = "Site web" + CREDITS = "Credits" -- fix + LICENSE = "License" -- fix + + ABOUT = "A propos" + PRINT_ADDON_INFO = "Afficher les informations sur l'addon" + DONATE = "Donate" -- fix + DONATE_DESC = "Give a much-needed donation to the author of this addon." -- fix + HOWTO_DONATE_WINDOWS = "Press Ctrl-A to select the link, then Ctrl-C to copy, then Alt-Tab out of the game, open your favorite web browser, and paste the link into the address bar." -- fix + HOWTO_DONATE_MAC = "Press Cmd-A to select the link, then Cmd-C to copy, then Cmd-Tab out of the game, open your favorite web browser, and paste the link into the address bar." -- fix + + CATEGORIES = { + ["Action Bars"] = "Barres d'action", + ["Auction"] = "Hôtel 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être", + ["Rogue"] = "Voleur", + ["Shaman"] = "Chaman", + ["Warlock"] = "Démoniste", + ["Warrior"] = "Guerrier", + ["Healer"] = "Soigneur", + ["Tank"] = "Tank", + ["Caster"] = "Casteur", + ["Combat"] = "Combat", + ["Compilations"] = "Compilations", + ["Data Export"] = "Exportation de données", + ["Development Tools"] = "Outils de développement", + ["Guild"] = "Guilde", + ["Frame Modification"] = "Modification des fenêtres", + ["Interface Enhancements"] = "Améliorations de l'interface", + ["Inventory"] = "Inventaire", + ["Library"] = "Bibliothèques", + ["Map"] = "Carte", + ["Mail"] = "Courrier", + ["Miscellaneous"] = "Divers", + ["Quest"] = "Quêtes", + ["Raid"] = "Raid", + ["Tradeskill"] = "Métiers", + ["UnitFrame"] = "Fenêtres d'unité", + } +elseif GetLocale() == "koKR" then + STANDBY = "|cffff5050(사용가능)|r" + + TITLE = "제목" + NOTES = "노트" + VERSION = "버전" + AUTHOR = "저작자" + DATE = "날짜" + CATEGORY = "분류" + EMAIL = "전자 우편" + WEBSITE = "웹 사이트" + CREDITS = "공로자" + LICENSE = "라이센스" + + ABOUT = "정보" + PRINT_ADDON_INFO = "애드온에 대한 정보를 출력합니다." + DONATE = "기부" + DONATE_DESC = "이 애드온의 저작자에게 기부를 합니다." + HOWTO_DONATE_WINDOWS = "Ctrl-A를 눌려 링크를 선택후, Ctrl-C로 복사합니다. Alt-Tab 눌려 게임으로 부터 나간후 웹 브라우저를 엽니다. 복사된 링크를 주소 창에 붙여넣기 합니다." + HOWTO_DONATE_MAC = "Cmd-A를 눌려 링크를 선택후, Cmd-C로 복사합니다. Cmd-Tab 눌려 게임으로 부터 나간후 웹 브라우저를 엽니다. 복사된 링크를 주소 창에 붙여넣기 합니다." + + 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 = "電子郵件" + WEBSITE = "網站" + CREDITS = "特別感謝" + LICENSE = "版權" + + ABOUT = "關於" + PRINT_ADDON_INFO = "顯示插件資訊。" + DONATE = "捐贈" + DONATE_DESC = "捐贈金錢給插件作者。" + HOWTO_DONATE_WINDOWS = "請按Ctrl-A選擇網站連結,Ctrl-C複製網址,Alt-Tab切換到電腦桌面,打開瀏覽器,在網址列貼上網址。" + HOWTO_DONATE_MAC = "請按Cmd-A選擇網站連結,Cmd-C複製網址,Cmd-Tab切換到電腦桌面,打開瀏覽器,在網址列貼上網址。" + + 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(暂挂)|r" + + TITLE = "标题" + NOTES = "附注" + VERSION = "版本" + AUTHOR = "作者" + DATE = "日期" + CATEGORY = "分类" + EMAIL = "电子邮件" + WEBSITE = "网站" + CREDITS = "Credits" -- fix + LICENSE = "License" -- fix + + ABOUT = "关于" + PRINT_ADDON_INFO = "印列出插件信息" + DONATE = "Donate" -- fix + DONATE_DESC = "Give a much-needed donation to the author of this addon." -- fix + HOWTO_DONATE_WINDOWS = "Press Ctrl-A to select the link, then Ctrl-C to copy, then Alt-Tab out of the game, open your favorite web browser, and paste the link into the address bar." -- fix + HOWTO_DONATE_MAC = "Press Cmd-A to select the link, then Cmd-C to copy, then Cmd-Tab out of the game, open your favorite web browser, and paste the link into the address bar." -- fix + + CATEGORIES = { + ["Action Bars"] = "动作条", + ["Auction"] = "拍卖", + ["Audio"] = "音频", + ["Battlegrounds/PvP"] = "战场/PvP", + ["Buffs"] = "增益魔法", + ["Chat/Communication"] = "聊天/交流", + ["Druid"] = "德鲁伊", + ["Hunter"] = "猎人", + ["Mage"] = "法师", + ["Paladin"] = "圣骑士", + ["Priest"] = "牧师", + ["Rogue"] = "盗贼", + ["Shaman"] = "萨满祭司", + ["Warlock"] = "术士", + ["Warrior"] = "战士", + ["Healer"] = "Healer", + ["Tank"] = "Tank", + ["Caster"] = "Caster", + ["Combat"] = "战斗", + ["Compilations"] = "编译", + ["Data Export"] = "数据导出", + ["Development Tools"] = "开发工具", + ["Guild"] = "公会", + ["Frame Modification"] = "框架修改", + ["Interface Enhancements"] = "界面增强", + ["Inventory"] = "背包", + ["Library"] = "库", + ["Map"] = "地图", + ["Mail"] = "邮件", + ["Miscellaneous"] = "杂项", + ["Quest"] = "任务", + ["Raid"] = "团队", + ["Tradeskill"] = "商业技能", + ["UnitFrame"] = "头像框架", + } +elseif GetLocale() == "esES" then + STANDBY = "|cffff5050(espera)|r" + + TITLE = "Título" + NOTES = "Notas" + VERSION = "Versión" + AUTHOR = "Autor" + DATE = "Fecha" + CATEGORY = "Categoría" + EMAIL = "E-mail" + WEBSITE = "Web" + CREDITS = "Créditos" + LICENSE = "License" -- fix + + ABOUT = "Acerca de" + PRINT_ADDON_INFO = "Muestra información acerca del accesorio." + DONATE = "Donate" -- fix + DONATE_DESC = "Give a much-needed donation to the author of this addon." -- fix + HOWTO_DONATE_WINDOWS = "Press Ctrl-A to select the link, then Ctrl-C to copy, then Alt-Tab out of the game, open your favorite web browser, and paste the link into the address bar." -- fix + HOWTO_DONATE_MAC = "Press Cmd-A to select the link, then Cmd-C to copy, then Cmd-Tab out of the game, open your favorite web browser, and paste the link into the address bar." -- fix + + CATEGORIES = { + ["Action Bars"] = "Barras de Acción", + ["Auction"] = "Subasta", + ["Audio"] = "Audio", + ["Battlegrounds/PvP"] = "Campos de Batalla/JcJ", + ["Buffs"] = "Buffs", + ["Chat/Communication"] = "Chat/Comunicación", + ["Druid"] = "Druida", + ["Hunter"] = "Cazador", + ["Mage"] = "Mago", + ["Paladin"] = "Paladín", + ["Priest"] = "Sacerdote", + ["Rogue"] = "Pícaro", + ["Shaman"] = "Chamán", + ["Warlock"] = "Brujo", + ["Warrior"] = "Guerrero", + ["Healer"] = "Sanador", + ["Tank"] = "Tanque", + ["Caster"] = "Conjurador", + ["Combat"] = "Combate", + ["Compilations"] = "Compilaciones", + ["Data Export"] = "Exportar Datos", + ["Development Tools"] = "Herramientas de Desarrollo", + ["Guild"] = "Hermandad", + ["Frame Modification"] = "Modificación de Marcos", + ["Interface Enhancements"] = "Mejoras de la Interfaz", + ["Inventory"] = "Inventario", + ["Library"] = "Biblioteca", + ["Map"] = "Mapa", + ["Mail"] = "Correo", + ["Miscellaneous"] = "Misceláneo", + ["Quest"] = "Misión", + ["Raid"] = "Banda", + ["Tradeskill"] = "Habilidad de Comercio", + ["UnitFrame"] = "Marco de Unidades", + } +elseif GetLocale() == "ruRU" then + STANDBY = "|cffff5050(в режиме ожидания)|r" + + TITLE = "Название" + NOTES = "Описание" + VERSION = "Версия" + AUTHOR = "Автор" + DATE = "Дата" + CATEGORY = "Категория" + EMAIL = "E-mail" + WEBSITE = "Сайт" + CREDITS = "Титры" + LICENSE = "Лицензия" + + ABOUT = "Об аддоне" + PRINT_ADDON_INFO = "Показать информацию об аддоне." + DONATE = "Пожертвовать" + DONATE_DESC = "Отблагодарить автора за разработку аддона." + HOWTO_DONATE_WINDOWS = "Для выделения всей ссылки нажмите Ctrl-A, потом для её копирования Ctrl-C, чтобы свернуть игру - Alt-Tab, откройте ваш браузер и вставьте ссылку в адресную строку - Ctrl-V" + HOWTO_DONATE_MAC = "Для выделения всей ссылки нажмите Cmd-A, потом для её копирования Ctrl-C, чтобы свернуть игру - Cmd-Tab, откройте ваш браузер и вставьте ссылку в адресную строку Cmd-V" + + 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" + LICENSE = "License" + + ABOUT = "About" + PRINT_ADDON_INFO = "Show information about the addon." + DONATE = "Donate" + DONATE_DESC = "Give a much-needed donation to the author of this addon." + HOWTO_DONATE_WINDOWS = "Press Ctrl-A to select the link, then Ctrl-C to copy, then Alt-Tab out of the game, open your favorite web browser, and paste the link into the address bar." + HOWTO_DONATE_MAC = "Press Cmd-A to select the link, then Cmd-C to copy, then Cmd-Tab out of the game, open your favorite web browser, and paste the link into the address bar." + + 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 = key:lower() + for k,v in pairs(CATEGORIES) do + if k:lower() == lowerKey then + self[lowerKey] = v + 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:GetLocalizedCategory(name) + self:argCheck(name, 2, "string") + return CATEGORIES[name] or UNKNOWN +end + +function AceAddon:ToString() + return "AceAddon" +end + +local function print(text) + DEFAULT_CHAT_FRAME:AddMessage(text) +end + +function AceAddon:ADDON_LOADED(name) + local unregister = true + local initAddon = {} + while #self.nextAddon > 0 do + local addon = table.remove(self.nextAddon, 1) + if addon.possibleNames[name] then + table.insert(initAddon, addon) + else + unregister = nil + table.insert(self.skipAddon, addon) + end + end + self.nextAddon, self.skipAddon = self.skipAddon, self.nextAddon + if unregister then + AceAddon:UnregisterEvent("ADDON_LOADED") + end + while #initAddon > 0 do + local addon = table.remove(initAddon, 1) + table.insert(self.addons, addon) + if not self.addons[name] then + self.addons[name] = addon + end + addon.possibleNames = nil + self:InitializeAddon(addon, name) + end +end + +local function RegisterOnEnable(self) + if IsLoggedIn() 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 + AceAddon:ManualEnable(self) + end + else + if not AceAddon.addonsToOnEnable then + AceAddon.addonsToOnEnable = {} + end + table.insert(AceAddon.addonsToOnEnable, self) + end +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 type(addon.title) == "string" then + local num = addon.title:find(" |cff7fff7f %-Ace2%-|r$") + if num then + addon.title = addon.title:sub(1, num - 1) + end + addon.title = addon.title:trim() + end + if addon.notes == nil then + addon.notes = GetAddOnMetadata(name, "Notes") + end + if type(addon.notes) == "string" then + addon.notes = addon.notes:trim() + end + if addon.version == nil then + addon.version = GetAddOnMetadata(name, "Version") + end + if type(addon.version) == "string" then + if addon.version:find("%$Revision: (%d+) %$") then + addon.version = addon.version:gsub("%$Revision: (%d+) %$", "%1") + elseif addon.version:find("%$Rev: (%d+) %$") then + addon.version = addon.version:gsub("%$Rev: (%d+) %$", "%1") + elseif addon.version:find("%$LastChangedRevision: (%d+) %$") then + addon.version = addon.version:gsub("%$LastChangedRevision: (%d+) %$", "%1") + end + addon.version = addon.version:trim() + end + if addon.author == nil then + addon.author = GetAddOnMetadata(name, "Author") + end + if type(addon.author) == "string" then + addon.author = addon.author:trim() + end + if addon.credits == nil then + addon.credits = GetAddOnMetadata(name, "X-Credits") + end + if type(addon.credits) == "string" then + addon.credits = addon.credits:trim() + end + if addon.donate == nil then + addon.donate = GetAddOnMetadata(name, "X-Donate") + end + if type(addon.donate) == "string" then + addon.donate = addon.donate:trim() + end + if addon.date == nil then + addon.date = GetAddOnMetadata(name, "X-Date") or GetAddOnMetadata(name, "X-ReleaseDate") + end + if type(addon.date) == "string" then + if addon.date:find("%$Date: (.-) %$") then + addon.date = addon.date:gsub("%$Date: (.-) %$", "%1") + elseif addon.date:find("%$LastChangedDate: (.-) %$") then + addon.date = addon.date:gsub("%$LastChangedDate: (.-) %$", "%1") + end + addon.date = addon.date:trim() + end + + if addon.category == nil then + addon.category = GetAddOnMetadata(name, "X-Category") + end + if type(addon.category) == "string" then + addon.category = addon.category:trim() + end + if addon.email == nil then + addon.email = GetAddOnMetadata(name, "X-eMail") or GetAddOnMetadata(name, "X-Email") + end + if type(addon.email) == "string" then + addon.email = addon.email:trim() + end + if addon.license == nil then + addon.license = GetAddOnMetadata(name, "X-License") + end + if type(addon.license) == "string" then + addon.license = addon.license:trim() + end + if addon.website == nil then + addon.website = GetAddOnMetadata(name, "X-Website") + end + if type(addon.website) == "string" then + addon.website = addon.website:trim() + end + end + local current = addon.class + while true do + if current == AceOO.Class or not current 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 + local n = AceAddon.addonsToOnEnable and #AceAddon.addonsToOnEnable or 0 + + if type(addon.OnInitialize) == "function" then + safecall(addon.OnInitialize, addon, name) + end + if AceEvent then + AceEvent:TriggerEvent("Ace2_AddonInitialized", addon) + end + RegisterOnEnable(addon) + local n2 = AceAddon.addonsToOnEnable and #AceAddon.addonsToOnEnable or 0 + if n2 - n > 1 then + local mine = table.remove(AceAddon.addonsToOnEnable) + table.insert(AceAddon.addonsToOnEnable, n+1, mine) + end +end + +local aboutFrame +local function createAboutFrame() + aboutFrame = CreateFrame("Frame", "AceAddon20AboutFrame", UIParent, "DialogBoxFrame") + aboutFrame:SetWidth(500) + aboutFrame:SetHeight(400) + aboutFrame:SetPoint("CENTER") + aboutFrame:SetBackdrop({ + bgFile = [[Interface\DialogFrame\UI-DialogBox-Background]], + edgeFile = [[Interface\Tooltips\UI-Tooltip-Border]], + tile = true, tileSize = 16, edgeSize = 16, + insets = { left = 5, right = 5, top = 5, bottom = 5 } + }) + aboutFrame:SetBackdropColor(0,0,0,1) + + local donateButton = CreateFrame("Button", "AceAddon20AboutFrameDonateButton", aboutFrame, "UIPanelButtonTemplate2") + aboutFrame.donateButton = donateButton + donateButton:SetPoint("BOTTOMRIGHT", -20, 20) + _G.AceAddon20AboutFrameDonateButtonText:SetText(DONATE) + donateButton:SetWidth(_G.AceAddon20AboutFrameDonateButtonText:GetWidth()+20) + donateButton:SetScript("OnClick", function() + aboutFrame.currentAddon:OpenDonationFrame() + end) + + local text = aboutFrame:CreateFontString(nil, "OVERLAY", "GameFontHighlightLarge") + aboutFrame.title = text + text:SetPoint("TOP", 0, -5) + + aboutFrame:Hide() + + aboutFrame.lefts = {} + aboutFrame.rights = {} + aboutFrame.textLefts = {} + aboutFrame.textRights = {} + function aboutFrame:Clear() + self.title:SetText("") + for i = 1, #self.lefts do + self.lefts[i] = nil + self.rights[i] = nil + end + end + + function aboutFrame:AddLine(left, right) + aboutFrame.lefts[#aboutFrame.lefts+1] = left + aboutFrame.rights[#aboutFrame.rights+1] = right + end + + local aboutFrame_Show = aboutFrame.Show + function aboutFrame:Show(...) + local maxLeftWidth = 0 + local maxRightWidth = 0 + local textHeight = 0 + for i = 1, #self.lefts do + if not self.textLefts[i] then + local left = aboutFrame:CreateFontString(nil, "OVERLAY", "GameFontNormal") + self.textLefts[i] = left + local right = aboutFrame:CreateFontString(nil, "OVERLAY", "GameFontHighlight") + self.textRights[i] = right + if i == 1 then + left:SetPoint("TOPRIGHT", aboutFrame, "TOPLEFT", 75, -35) + else + left:SetPoint("TOPRIGHT", self.textLefts[i-1], "BOTTOMRIGHT", 0, -5) + end + right:SetPoint("LEFT", left, "RIGHT", 5, 0) + end + self.textLefts[i]:SetText(self.lefts[i] .. ":") + self.textRights[i]:SetText(self.rights[i]) + local leftWidth = self.textLefts[i]:GetWidth() + local rightWidth = self.textRights[i]:GetWidth() + textHeight = self.textLefts[i]:GetHeight() + if maxLeftWidth < leftWidth then + maxLeftWidth = leftWidth + end + if maxRightWidth < rightWidth then + maxRightWidth = rightWidth + end + end + for i = #self.lefts+1, #self.textLefts do + self.textLefts[i]:SetText('') + self.textRights[i]:SetText('') + end + aboutFrame:SetWidth(75 + maxRightWidth + 20) + aboutFrame:SetHeight(#self.lefts * (textHeight + 5) + 100) + + aboutFrame_Show(self, ...) + end + aboutFrame:Hide() + + createAboutFrame = nil +end +local donateFrame + +local function unobfuscateEmail(email) + return email:gsub(" AT ", "@"):gsub(" DOT ", ".") +end + +local function isGoodVariable(var) + return type(var) == "string" or type(var) == "number" +end +function AceAddon.prototype:PrintAddonInfo() + if createAboutFrame then + createAboutFrame() + end + aboutFrame:Clear() + local x + if isGoodVariable(self.title) then + x = tostring(self.title) + elseif isGoodVariable(self.name) then + x = tostring(self.name) + else + x = "<" .. tostring(self.class) .. " instance>" + end + if type(self.IsActive) == "function" then + if not self:IsActive() then + x = x .. " " .. STANDBY + end + end + aboutFrame.title:SetText(x) + + if isGoodVariable(self.version) then + aboutFrame:AddLine(VERSION, tostring(self.version)) + end + if isGoodVariable(self.notes) then + aboutFrame:AddLine(NOTES, tostring(self.notes)) + end + if isGoodVariable(self.author) then + aboutFrame:AddLine(AUTHOR, tostring(self.author)) + end + if isGoodVariable(self.credits) then + aboutFrame:AddLine(CREDITS, tostring(self.credits)) + end + if isGoodVariable(self.date) then + aboutFrame:AddLine(DATE, tostring(self.date)) + end + if isGoodVariable(self.category) then + local category = CATEGORIES[self.category] + aboutFrame:AddLine(CATEGORY, category or tostring(self.category)) + end + if isGoodVariable(self.email) then + aboutFrame:AddLine(EMAIL, unobfuscateEmail(tostring(self.email))) + end + if isGoodVariable(self.website) then + aboutFrame:AddLine(WEBSITE, tostring(self.website)) + end + if isGoodVariable(self.license) then + aboutFrame:AddLine(LICENSE, tostring(self.license)) + end + + if donateFrame and donateFrame:IsShown() then + donateFrame:Hide() + end + + aboutFrame.currentAddon = self + + aboutFrame:Show() + + if self.donate then + aboutFrame.donateButton:Show() + else + aboutFrame.donateButton:Hide() + end +end + +local function createDonateFrame() + donateFrame = CreateFrame("Frame", "AceAddon20Frame", UIParent, "DialogBoxFrame") + + donateFrame:SetWidth(500) + donateFrame:SetHeight(200) + donateFrame:SetPoint("CENTER") + donateFrame:SetBackdrop({ + bgFile = [[Interface\DialogFrame\UI-DialogBox-Background]], + edgeFile = [[Interface\Tooltips\UI-Tooltip-Border]], + tile = true, tileSize = 16, edgeSize = 16, + insets = { left = 5, right = 5, top = 5, bottom = 5 } + }) + donateFrame:SetBackdropColor(0,0,0,1) + + local text = donateFrame:CreateFontString(nil, "OVERLAY", "GameFontHighlightLarge") + text:SetPoint("TOP", 0, -5) + text:SetText(DONATE) + + local howto = donateFrame:CreateFontString(nil, "OVERLAY", "GameFontHighlight") + howto:SetPoint("TOP", text, "BOTTOM", 0, -5) + howto:SetPoint("LEFT", 16, 0) + howto:SetPoint("RIGHT", -16, 0) + if not IsMacClient() then + -- Windows or Linux + howto:SetText(HOWTO_DONATE_WINDOWS) + else + howto:SetText(HOWTO_DONATE_MAC) + end + + local scrollFrame = CreateFrame("ScrollFrame", "AceAddon20FrameScrollFrame", donateFrame, "UIPanelScrollFrameTemplate") + scrollFrame:SetToplevel(true) + scrollFrame:SetPoint("TOP", -10, -76) + scrollFrame:SetWidth(455) + scrollFrame:SetHeight(70) + howto:SetPoint("BOTTOM", scrollFrame, "TOP") + + local editBox = CreateFrame("EditBox", nil, scrollFrame) + donateFrame.editBox = editBox + scrollFrame:SetScrollChild(editBox) + editBox:SetFontObject(ChatFontNormal) + editBox:SetMultiLine(true) + editBox:SetMaxLetters(99999) + editBox:SetWidth(450) + editBox:SetHeight(54) + editBox:SetPoint("BOTTOM", 5, 0) + editBox:SetJustifyH("LEFT") + editBox:SetJustifyV("TOP") + editBox:SetAutoFocus(false) + editBox:SetScript("OnTextChanged", function(this) + if this:GetText() ~= this.text then + this:SetText(this.text) + end + end) + editBox:SetScript("OnEscapePressed", function(this) + this:ClearFocus() + end) + createDonateFrame = nil +end + +local function fix(char) + return ("%%%02x"):format(char:byte()) +end + +local function urlencode(text) + return text:gsub("[^0-9A-Za-z]", fix) +end + +function AceAddon.prototype:OpenDonationFrame() + if createDonateFrame then + createDonateFrame() + end + local donate = self.donate + if type(donate) ~= "string" then + donate = "Wowace" + end + local style, data = (":"):split(donate, 2) + style = style:lower() + if style ~= "website" and style ~= "paypal" then + style = "wowace" + end + if style == "wowace" then + donateFrame.editBox.text = "http://www.wowace.com/wiki/Donations" + elseif style == "website" then + donateFrame.editBox.text = data + else -- PayPal + local text = "https://www.paypal.com/cgi-bin/webscr?cmd=_xclick&business=" .. urlencode(unobfuscateEmail(data)) + local name + if type(self.title) == "string" then + name = self.title + elseif type(self.name) == "string" then + name = self.name + end + if name then + name = name:gsub("|c%x%x%x%x%x%x%x%x", ""):gsub("|r", "") + text = text .. "&item_name=" .. urlencode(name) + end + donateFrame.editBox.text = text + end + donateFrame.editBox:SetText(donateFrame.editBox.text) + + if aboutFrame and aboutFrame:IsShown() then + aboutFrame:Hide() + end + + donateFrame:Show() + + donateFrame.editBox:SetFocus() +end + +local options +function AceAddon:GetAceOptionsDataTable(target) + return { + about = { + name = ABOUT, + desc = PRINT_ADDON_INFO, + type = "execute", + func = "PrintAddonInfo", + order = -1, + }, + donate = { + name = DONATE, + desc = DONATE_DESC, + type = "execute", + func = "OpenDonationFrame", + order = -1, + hidden = function() + return not target.donate + end + } + } +end + +function AceAddon:PLAYER_LOGIN() + if self.addonsToOnEnable then + while #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 + AceAddon:ManualEnable(addon) + 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") + local names = {} + for i = 1, GetNumAddOns() do + if IsAddOnLoaded(i) then names[GetAddOnInfo(i)] = true end + end + self.possibleNames = names + 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, ...) + local class = AceAddon:pcall(AceOO.Classpool, self, ...) + return class:new() +end + +function AceAddon:ManualEnable(addon) + AceAddon:argCheck(addon, 2, "table") + local first = nil + if AceOO.inherits(addon, "AceAddon-2.0") then + if AceAddon.addonsEnabled and not AceAddon.addonsEnabled[addon] then + first = true + AceAddon.addonsEnabled[addon] = true + end + end + local current = addon.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, addon, first) + end + end + end + current = current.super + end + if type(addon.OnEnable) == "function" then + safecall(addon.OnEnable, addon, first) + end + if AceEvent then + AceEvent:TriggerEvent("Ace2_AddonEnabled", addon, first) + end +end + +function AceAddon:ManualDisable(addon) + AceAddon:argCheck(addon, 2, "table") + local current = addon.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, addon) + end + end + end + current = current.super + end + if type(module.OnDisable) == "function" then + safecall(module.OnDisable, addon) + end + if AceEvent then + AceEvent:TriggerEvent("Ace2_AddonDisabled", addon) + end +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 = (" "):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((" "):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 + listAddon(self.addons[name]) + else + local s = " - " .. tostring(GetAddOnMetadata(i, "Title") or name) + local version = GetAddOnMetadata(i, "Version") + if version then + if version:find("%$Revision: (%d+) %$") then + version = version:gsub("%$Revision: (%d+) %$", "%1") + elseif version:find("%$Rev: (%d+) %$") then + version = version:gsub("%$Rev: (%d+) %$", "%1") + elseif version:find("%$LastChangedRevision: (%d+) %$") then + version = version:gsub("%$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." .. MINOR_VERSION:gsub("%$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") + table.sort(self.addons, mySort) + for _,v in ipairs(self.addons) do + listAddon(v) + end + end + }, + all = { + desc = "List all addons", + name = "All", + type = "execute", + func = function() + print("|cffffff7fAddon list:|r") + 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 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 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 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 rawget(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(...) + local arg = { ... } + for i,v in ipairs(arg) do + arg[i] = v:gsub('%*', '.*'):gsub('%%', '%%%%'):lower() + end + local count = GetNumAddOns() + for i = 1, count do + local name = GetAddOnInfo(i) + local good = true + for _,v in ipairs(arg) do + if not name:lower():find(v) then + good = false + break + end + end + if good then + listNormalAddon(i) + end + end + end + } + }, + }, + enable = { + desc = "Enable addon(s).", + name = "Enable", + type = "text", + usage = " ...", + get = false, + input = true, + set = function(...) + for i = 1, select("#", ...) do + local addon = select(i, ...) + local name, title, _, enabled, _, reason = GetAddOnInfo(addon) + if reason == "MISSING" then + print(("|cffffff7fAce2:|r AddOn %q does not exist."):format(addon)) + elseif not enabled then + EnableAddOn(addon) + print(("|cffffff7fAce2:|r %s is now enabled."):format(addon or name)) + else + print(("|cffffff7fAce2:|r %s is already enabled."):format(addon or name)) + end + end + end, + }, + disable = { + desc = "Disable addon(s).", + name = "Disable", + type = "text", + usage = " ...", + get = false, + input = true, + set = function(...) + for i = 1, select("#", ...) do + local addon = select(i, ...) + local name, title, _, enabled, _, reason = GetAddOnInfo(addon) + if reason == "MISSING" then + print(("|cffffff7fAce2:|r AddOn %q does not exist."):format(addon)) + elseif enabled then + DisableAddOn(addon) + print(("|cffffff7fAce2:|r %s is now disabled."):format(addon or name)) + else + print(("|cffffff7fAce2:|r %s is already disabled."):format(addon or name)) + end + end + end, + }, + load = { + desc = "Load addon(s).", + name = "Load", + type = "text", + usage = " ...", + get = false, + input = true, + set = function(...) + for i = 1, select("#", ...) do + local addon = select(i, ...) + local name, title, _, _, loadable, reason = GetAddOnInfo(addon) + if reason == "MISSING" then + print(("|cffffff7fAce2:|r AddOn %q does not exist."):format(addon)) + elseif not loadable then + print(("|cffffff7fAce2:|r AddOn %q is not loadable. Reason: %s."):format(addon, reason)) + else + LoadAddOn(addon) + print(("|cffffff7fAce2:|r %s is now loaded."):format(addon or name)) + end + end + end + }, + info = { + desc = "Display information", + name = "Information", + type = "execute", + func = function() + local mem, threshold = gcinfo() + print((" - |cffffff7fMemory usage [|r%.3f MiB|cffffff7f]|r"):format(mem / 1024)) + if threshold then + print((" - |cffffff7fThreshold [|r%.3f MiB|cffffff7f]|r"):format(threshold / 1024)) + end + print((" - |cffffff7fFramerate [|r%.0f fps|cffffff7f]|r"):format(GetFramerate())) + local bandwidthIn, bandwidthOut, latency = GetNetStats() + bandwidthIn, bandwidthOut = floor(bandwidthIn * 1024), floor(bandwidthOut * 1024) + print((" - |cffffff7fLatency [|r%.0f ms|cffffff7f]|r"):format(latency)) + print((" - |cffffff7fBandwidth in [|r%.0f B/s|cffffff7f]|r"):format(bandwidthIn)) + print((" - |cffffff7fBandwidth out [|r%.0f B/s|cffffff7f]|r"):format(bandwidthOut)) + print((" - |cffffff7fTotal addons [|r%d|cffffff7f]|r"):format(GetNumAddOns())) + print((" - |cffffff7fAce2 addons [|r%d|cffffff7f]|r"):format(#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 = select(4, GetAddOnInfo(i)) + if not isActive or not loadable then + disabled = disabled + 1 + else + enabled = enabled + 1 + end + end + print((" - |cffffff7fAce 1.x addons [|r%d|cffffff7f]|r"):format(ace)) + print((" - |cffffff7fLoadOnDemand addons [|r%d|cffffff7f]|r"):format(lod)) + print((" - |cffffff7fenabled addons [|r%d|cffffff7f]|r"):format(enabled)) + print((" - |cffffff7fdisabled addons [|r%d|cffffff7f]|r"):format(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((" - |cffffff7fAceLibrary instances [|r%d|cffffff7f]|r"):format(libs)) + end + } + } + }) + elseif major == "AceModuleCore-2.0" then + AceModuleCore = instance + end +end + +local function activate(self, oldLib, oldDeactivate) + AceAddon = self + + self.addonsToOnEnable = oldLib and oldLib.addonsToOnEnable + self.addons = oldLib and oldLib.addons or {} + self.nextAddon = oldLib and oldLib.nextAddon or {} + self.skipAddon = oldLib and oldLib.skipAddon or {} + self.addonsStarted = oldLib and oldLib.addonsStarted or {} + self.addonsEnabled = oldLib and oldLib.addonsEnabled or {} + + if oldDeactivate then + oldDeactivate(oldLib) + end +end + +AceLibrary:Register(AceAddon, MAJOR_VERSION, MINOR_VERSION, activate, nil, external) diff --git a/AtlasLootFu/Libs/AceAddon-2.0/AceAddon-2.0.toc b/AtlasLootFu/Libs/AceAddon-2.0/AceAddon-2.0.toc new file mode 100644 index 0000000..c58a8d1 --- /dev/null +++ b/AtlasLootFu/Libs/AceAddon-2.0/AceAddon-2.0.toc @@ -0,0 +1,17 @@ +## Interface: 30300 +## X-Curse-Packaged-Version: r1101 +## X-Curse-Project-Name: Ace2 +## X-Curse-Project-ID: ace2 +## X-Curse-Repository-ID: wow/ace2/mainline + +## Title: Lib: AceAddon-2.0 +## Notes: AddOn development framework +## Author: Ace Development Team +## LoadOnDemand: 1 +## X-Website: http://www.wowace.com +## X-Category: Library +## X-License: LGPL v2.1 + MIT for AceOO-2.0 +## Dependencies: AceLibrary, AceOO-2.0 + +AceAddon-2.0.lua + diff --git a/AtlasLootFu/Libs/AceDB-2.0/AceDB-2.0.lua b/AtlasLootFu/Libs/AceDB-2.0/AceDB-2.0.lua new file mode 100644 index 0000000..edddf36 --- /dev/null +++ b/AtlasLootFu/Libs/AceDB-2.0/AceDB-2.0.lua @@ -0,0 +1,2221 @@ +--[[ +Name: AceDB-2.0 +Revision: $Rev: 1094 $ +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/wowace/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 +License: LGPL v2.1 +]] + +local MAJOR_VERSION = "AceDB-2.0" +local MINOR_VERSION = 90000 + tonumber(("$Revision: 1094 $"):match("(%d+)")) + +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 function safecall(func,...) + local success, err = pcall(func,...) + if not success then geterrorhandler()(err) end +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, RESET_PROFILE, RESET_PROFILE_DESC, CHARACTER_COLON, REALM_COLON, CLASS_COLON, DEFAULT, ALTERNATIVE + +-- Move these into "enUS" when they've been translated in all other locales +local DELETE_PROFILE = "Delete" +local DELETE_PROFILE_DESC = "Deletes a profile. Note that no check is made whether this profile is in use by other characters or not." +local DELETE_PROFILE_USAGE = "" + +if GetLocale() == "deDE" then + DELETE_PROFILE = "L\195\182schen" + DELETE_PROFILE_DESC = "L\195\182scht ein Profil. Beachte das nicht \195\188berpr\195\188ft wird ob das zu l\195\182schende Profil von anderen Charakteren genutzt wird oder nicht." + DELETE_PROFILE_USAGE = "" + + 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 = "" + RESET_PROFILE = "Resette das Profil" + RESET_PROFILE_DESC = "Entfernt alle Einstellungen des gegenw\195\164rtigen Profils." + + CHARACTER_COLON = "Charakter: " + REALM_COLON = "Realm: " + CLASS_COLON = "Klasse: " + + DEFAULT = "Vorgabe" + ALTERNATIVE = "Alternativ" +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 = "" + RESET_PROFILE = "Reset profile" -- fix + RESET_PROFILE_DESC = "Clear all settings of the current profile." -- fix + + CHARACTER_COLON = "Personnage: " + REALM_COLON = "Royaume: " + CLASS_COLON = "Classe: " + + DEFAULT = "Default" -- fix + ALTERNATIVE = "Alternative" -- fix +elseif GetLocale() == "koKR" then + DELETE_PROFILE = "삭제" + DELETE_PROFILE_DESC = "프로필을 삭제합니다." + DELETE_PROFILE_USAGE = "<프로필명>" + + 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 = "<프로필명>" + RESET_PROFILE = "프로필 초기화" + RESET_PROFILE_DESC = "모든 세팅에서 현재 프로필을 초기화 합니다." + + CHARACTER_COLON = "캐릭터: " + REALM_COLON = "서버: " + CLASS_COLON = "직업: " + + DEFAULT = "기본값" + ALTERNATIVE = "대체" +elseif GetLocale() == "zhTW" then + DELETE_PROFILE = "刪除" + DELETE_PROFILE_DESC = "刪除記錄檔。注意,有可能別的角色也使用這個記錄檔。" + DELETE_PROFILE_USAGE = "<記錄檔名稱>" + + 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 = "<記錄檔名稱>" + RESET_PROFILE = "重設記錄檔" + RESET_PROFILE_DESC = "清除目前的記錄檔上的所有設定。" + + CHARACTER_COLON = "角色: " + REALM_COLON = "伺服器: " + CLASS_COLON = "職業: " + + DEFAULT = "預設" + ALTERNATIVE = "替代" +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>" + RESET_PROFILE = "Reset profile" -- fix + RESET_PROFILE_DESC = "Clear all settings of the current profile." -- fix + + CHARACTER_COLON = "\229\173\151\231\172\166: " + REALM_COLON = "\229\159\159: " + CLASS_COLON = "\233\128\137\228\187\182\231\177\187: " + + DEFAULT = "Default" -- fix + ALTERNATIVE = "Alternative" -- fix +elseif GetLocale() == "esES" then + ACTIVE = "Activo" + ENABLED = "Activado" + STATE = "Estado" + TOGGLE_ACTIVE = "Parar/Continuar este accesorio" + MAP_ACTIVESUSPENDED = { [true] = "|cff00ff00Activo|r", [false] = "|cffff0000Parado|r" } + SET_PROFILE = "Selecciona el perfil para este accesorio." + SET_PROFILE_USAGE = "{perso || clase || reino || }" + PROFILE = "Perfil" + PLAYER_OF_REALM = "%s de %s" + CHOOSE_PROFILE_DESC = "Elige un perfil." + CHOOSE_PROFILE_GUI = "Elige" + COPY_PROFILE_DESC = "Copiar de un perfil a otro" + COPY_PROFILE_GUI = "Copiar desde" + OTHER_PROFILE_DESC = "Elige otro perfil." + OTHER_PROFILE_GUI = "Otro" + OTHER_PROFILE_USAGE = "" + RESET_PROFILE = "Reset profile" -- fix + RESET_PROFILE_DESC = "Clear all settings of the current profile." -- fix + + CHARACTER_COLON = "Personaje: " + REALM_COLON = "Reino: " + CLASS_COLON = "Clase: " + + DEFAULT = "Por defecto" + ALTERNATIVE = "Alternativo" +elseif GetLocale() == "ruRU" then + DELETE_PROFILE = "Удалить" + DELETE_PROFILE_DESC = "Удалить профиль. Изначально проверьте не используется ли этот профиль другими персонажами, чтобы не натворить себе неудобств." + DELETE_PROFILE_USAGE = "<название профиля>" + + ACTIVE = "Активный" + ENABLED = "Включён" + STATE = "Состояние" + TOGGLE_ACTIVE = "Отключить/Запустить аддон." + MAP_ACTIVESUSPENDED = { [true] = "|cff00ff00Активный|r", [false] = "|cffff0000Suspended|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 = "<название профиля>" + RESET_PROFILE = "Сброс профиля" + RESET_PROFILE_DESC = "Очистить все настройки для текущего профиля." + + CHARACTER_COLON = "Персонаж: " + REALM_COLON = "Сервер: " + CLASS_COLON = "Класс: " + + DEFAULT = "По-умолчанию" + ALTERNATIVE = "Альтернатива" +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 = "" + RESET_PROFILE = "Reset profile" + RESET_PROFILE_DESC = "Clear all settings of the current profile." + + CHARACTER_COLON = "Character: " + REALM_COLON = "Realm: " + CLASS_COLON = "Class: " + + DEFAULT = "Default" + ALTERNATIVE = "Alternative" +end +local convertFromOldCharID +do + local matchStr = "^" .. PLAYER_OF_REALM:gsub("([%(%)%.%*%+%-%[%]%?%^%$%%])", "%%%1"):gsub("%%s", "(.+)") .. "$" + function convertFromOldCharID(str) + local player, realm = str:match(matchStr) + if not player then + return str + end + return player .. " - " .. realm + end +end + +local AceOO = AceLibrary("AceOO-2.0") +local AceEvent +local Mixin = AceOO.Mixin +local AceDB = Mixin { + "RegisterDB", + "RegisterDefaults", + "ResetDB", + "SetProfile", + "GetProfile", + "CopyProfileFrom", + "DeleteProfile", + "ToggleActive", + "IsActive", + "AcquireDBNamespace", + } +local Dewdrop = AceLibrary:HasInstance("Dewdrop-2.0") and AceLibrary("Dewdrop-2.0") + +local _G = getfenv(0) + +local function inheritDefaults(t, defaults) + if not defaults then + return t + end + for k,v in pairs(defaults) do + if k == "*" or 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 == k) and type(t[key]) == "table" then + inheritDefaults(t[key], v) + end + end + else + if type(v) == "table" then + if type(rawget(t, k)) ~= "table" then + t[k] = {} + end + inheritDefaults(t[k], v) + if defaults["**"] then + inheritDefaults(t[k], defaults["**"]) + end + elseif rawget(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" or race == "BloodElf" then + faction = FACTION_HORDE +else + faction = FACTION_ALLIANCE +end +local server = GetRealmName():trim() +local charID = UnitName("player") .. " - " .. server +local realmID = server .. " - " .. faction +local classID = UnitClass("player") + +AceDB.CHAR_ID = charID +AceDB.REALM_ID = realmID +AceDB.CLASS_ID = classID + +AceDB.FACTION = faction +AceDB.REALM = server +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 + list[t] = true + end +end + +local caseInsensitive_mt = { + __index = function(self, key) + if type(key) ~= "string" then + return nil + end + local lowerKey = key:lower() + for k,v in pairs(self) do + if k:lower() == 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 = key:lower() + for k in pairs(self) do + if k:lower() == 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 == "server" then + if type(db.raw.servers) ~= "table" then + db.raw.servers = {} + end + local id = server + if type(db.raw.servers[id]) ~= "table" then + db.raw.servers[id] = {} + end + rawset(db, 'server', db.raw.servers[id]) + if db.defaults and db.defaults.server then + inheritDefaults(db.server, db.defaults.server) + end + return db.server + 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 == "faction" then + if type(db.raw.factions) ~= "table" then + db.raw.factions = {} + end + local id = faction + if type(db.raw.factions[id]) ~= "table" then + db.raw.factions[id] = {} + end + rawset(db, 'faction', db.raw.factions[id]) + if db.defaults and db.defaults.faction then + inheritDefaults(db.faction, db.defaults.faction) + end + return db.faction + 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(("Cannot access key %q in db table. You may want to use db.profile[%q]"):format(tostring(key), tostring(key)), 2) +end, __newindex = function(db, key, value) + error(("Cannot access key %q in db table. You may want to use db.profile[%q]"):format(tostring(key), tostring(key)), 2) +end } + +local function RecalculateAceDBCopyFromList(target) + local db = target.db + local t = target['acedb-profile-copylist'] + for k,v in pairs(t) do + t[k] = nil + end + local _,currentProfile = AceDB.GetProfile(target) + if db and db.raw then + if db.raw.profiles then + for k in pairs(db.raw.profiles) do + if currentProfile ~= k then + if k:find("^char/") then + local name = k:sub(6) + local player, realm = name:match("^(.*) %- (.*)$") + if player then + name = PLAYER_OF_REALM:format(player, realm) + end + t[k] = CHARACTER_COLON .. name + elseif k:find("^realm/") then + local name = k:sub(7) + t[k] = REALM_COLON .. name + elseif k:find("^class/") then + local name = k:sub(7) + t[k] = CLASS_COLON .. name + else + t[k] = k + end + end + end + end + if db.raw.namespaces then + for _,n in pairs(db.raw.namespaces) do + if n.profiles then + for k in pairs(n.profiles) do + if currentProfile ~= k then + if k:find('^char/') then + local name = k:sub(6) + local player, realm = name:match("^(.*) %- (.*)$") + if player then + name = PLAYER_OF_REALM:format(player, realm) + end + t[k] = CHARACTER_COLON .. name + elseif k:find('^realm/') then + local name = k:sub(7) + t[k] = REALM_COLON .. name + elseif k:find('^class/') then + local name = k:sub(7) + t[k] = CLASS_COLON .. name + else + t[k] = k + end + end + end + end + end + end + end + if t.Default then + t.Default = DEFAULT + end + if t.Alternative then + t.Alternative = ALTERNATIVE + end +end + +local function RecalculateAceDBProfileList(target) + local t = target['acedb-profile-list'] + for k,v in pairs(t) do + t[k] = nil + end + t.char = CHARACTER_COLON .. PLAYER_OF_REALM:format(UnitName("player"), server) + t.realm = REALM_COLON .. realmID + t.class = CLASS_COLON .. classID + t.Default = DEFAULT + local db = target.db + if db and db.raw then + if db.raw.profiles then + for k in pairs(db.raw.profiles) do + if not k:find("^char/") and not k:find("^realm/") and not k:find("^class/") then + t[k] = k + end + end + end + if db.raw.namespaces then + for _,n in pairs(db.raw.namespaces) do + if n.profiles then + for k in pairs(n.profiles) do + if not k:find("^char/") and not k:find("^realm/") and not k:find("^class/") then + t[k] = k + end + end + end + end + end + local curr = db.raw.currentProfile and db.raw.currentProfile[charID] + if curr and not t[curr] then + t[curr] = curr + end + end + if t.Alternative then + t.Alternative = ALTERNATIVE + end +end + +local CrawlForSerialization +local CrawlForDeserialization + +local function SerializeObject(o) + local t = { o:Serialize() } + CrawlForSerialization(t) + t[0] = o.class:GetLibraryVersion() + return t +end + +local function DeserializeObject(t) + CrawlForDeserialization(t) + local className = t[0] + t[0] = nil + return AceLibrary(className):Deserialize(unpack(t)) +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(rawget(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(rawget(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(rawget(t, 0)) == "string" and AceLibrary:HasInstance(rawget(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(rawget(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(rawget(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 == "server" 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].servers) ~= "table" then + db.raw.namespaces[name].servers = {} + end + local id = server + if type(db.raw.namespaces[name].servers[id]) ~= "table" then + db.raw.namespaces[name].servers[id] = {} + end + rawset(namespace, 'server', db.raw.namespaces[name].servers[id]) + if namespace.defaults and namespace.defaults.server then + inheritDefaults(namespace.server, namespace.defaults.server) + end + return namespace.server + 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 == "faction" 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].factions) ~= "table" then + db.raw.namespaces[name].factions = {} + end + local id = faction + if type(db.raw.namespaces[name].factions[id]) ~= "table" then + db.raw.namespaces[name].factions[id] = {} + end + rawset(namespace, 'faction', db.raw.namespaces[name].factions[id]) + if namespace.defaults and namespace.defaults.faction then + inheritDefaults(namespace.faction, namespace.defaults.faction) + end + return namespace.faction + 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(("Cannot access key %q in db table. You may want to use db.profile[%q]"):format(tostring(key), tostring(key)), 2) +end, __newindex = function(db, key, value) + error(("Cannot access key %q in db table. You may want to use db.profile[%q]"):format(tostring(key), tostring(key)), 2) +end } + +local tmp = {} +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 db.charName then + if type(_G[db.charName]) ~= "table" then + _G[db.charName] = {} + else + CrawlForDeserialization(_G[db.charName]) + end + end + rawset(db, 'raw', _G[db.name]) + if not db.raw.currentProfile then + db.raw.currentProfile = {} + else + for k,v in pairs(db.raw.currentProfile) do + tmp[convertFromOldCharID(k)] = v + db.raw.currentProfile[k] = nil + end + for k,v in pairs(tmp) do + db.raw.currentProfile[k] = v + tmp[k] = nil + end + end + if not db.raw.currentProfile[charID] then + db.raw.currentProfile[charID] = AceDB.registry[self] or "Default" + end + if db.raw.profiles then + for k,v in pairs(db.raw.profiles) do + local new_k = k + if k:find("^char/") then + new_k = "char/" .. convertFromOldCharID(k:sub(6)) + end + tmp[new_k] = v + db.raw.profiles[k] = nil + end + for k,v in pairs(tmp) do + db.raw.profiles[k] = v + tmp[k] = nil + end + end + if db.raw.disabledModules then -- AceModuleCore-2.0 + for k,v in pairs(db.raw.disabledModules) do + local new_k = k + if k:find("^char/") then + new_k = "char/" .. convertFromOldCharID(k:sub(6)) + end + tmp[new_k] = v + db.raw.disabledModules[k] = nil + end + for k,v in pairs(tmp) do + db.raw.disabledModules[k] = v + tmp[k] = nil + end + end + if db.raw.chars then + for k,v in pairs(db.raw.chars) do + tmp[convertFromOldCharID(k)] = v + db.raw.chars[k] = nil + end + for k,v in pairs(tmp) do + db.raw.chars[k] = v + tmp[k] = nil + end + end + if db.raw.namespaces then + for l,u in pairs(db.raw.namespaces) do + if u.chars then + for k,v in pairs(u.chars) do + tmp[convertFromOldCharID(k)] = v + u.chars[k] = nil + end + for k,v in pairs(tmp) do + u.chars[k] = v + tmp[k] = nil + end + end + end + end + if db.raw.disabled then + setmetatable(db.raw.disabled, caseInsensitive_mt) + end + if self['acedb-profile-copylist'] then + RecalculateAceDBCopyFromList(self) + end + if self['acedb-profile-list'] then + RecalculateAceDBProfileList(self) + 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, defaultProfile) + AceDB:argCheck(name, 2, "string") + AceDB:argCheck(charName, 3, "string", "nil") + AceDB:argCheck(defaultProfile, 4, "string", "nil") + if self.db then + AceDB:error("Cannot call \"RegisterDB\" if self.db is set.") + end + local stack = debugstack() + local addonName = stack:gsub(".-\n.-\\AddOns\\(.-)\\.*", "%1") + self.db = { + name = name, + charName = charName + } + AceDB.registry[self] = defaultProfile or "Default" + if AceDB.addonsLoaded[addonName] then + AceDB.InitializeDB(self, addonName) + else + AceDB.addonsToBeInitialized[self] = addonName + end +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") + else + AceDB:argCheck(kind, 2, "string") + AceDB:argCheck(defaults, 3, "table") + end + if kind ~= "char" and kind ~= "class" and kind ~= "profile" and kind ~= "account" and kind ~= "realm" and kind ~= "faction" and kind ~= "server" then + AceDB:error("Bad argument #%d to `RegisterDefaults' (\"char\", \"class\", \"profile\", \"account\", \"realm\", \"server\", or \"faction\" expected, got %q)", a3 and 3 or 2, kind) + 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, a2) + local name + if a2 then + name, kind = kind, a2 + AceDB:argCheck(name, 2, "nil", "string") + AceDB:argCheck(kind, 3, "nil", "string") + else + AceDB:argCheck(kind, 2, "nil", "string") + if kind ~= "char" and kind ~= "class" and kind ~= "profile" and kind ~= "account" and kind ~= "realm" and kind ~= "faction" and kind ~= "server" then + name, kind = kind, nil + end + end + 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 not kind then + if not name 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) + rawset(v, 'server', nil) + rawset(v, 'faction', nil) + end + end + else + if db.raw.namespaces then + db.raw.namespaces[name] = nil + end + if db.namespaces then + local v = db.namespaces[name] + if v then + rawset(v, 'account', nil) + rawset(v, 'char', nil) + rawset(v, 'class', nil) + rawset(v, 'profile', nil) + rawset(v, 'realm', nil) + rawset(v, 'server', nil) + rawset(v, 'faction', nil) + end + end + end + elseif kind == "account" then + if name then + db.raw.account = nil + rawset(db, 'account', nil) + if db.raw.namespaces then + for name,v in pairs(db.raw.namespaces) do + v.account = nil + end + end + if db.namespaces then + for name,v in pairs(db.namespaces) do + rawset(v, 'account', nil) + end + end + else + if db.raw.namespaces and db.raw.namespaces[name] then + db.raw.namespaces[name].account = nil + end + if db.namespaces then + local v = db.namespaces[name] + if v then + rawset(v, 'account', nil) + end + end + end + elseif kind == "char" then + if name 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 + else + if db.charName then + local x = _G[db.charName] + if x.namespaces then + x.namespaces[name] = nil + end + else + if db.raw.namespaces then + local v = db.namespaces[name] + if v and v.chars then + v.chars[charID] = nil + end + end + end + if db.namespaces then + local v = db.namespaces[name] + if v then + rawset(v, 'char', nil) + end + end + end + elseif kind == "realm" then + if not name 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 + else + if db.raw.namespaces then + local v = db.raw.namespaces[name] + if v and v.realms then + v.realms[realmID] = nil + end + end + if db.namespaces then + local v = db.namespaces[name] + if v then + rawset(v, 'realm', nil) + end + end + end + elseif kind == "server" then + if not name then + if db.raw.servers then + db.raw.servers[server] = nil + end + rawset(db, 'server', nil) + if db.raw.namespaces then + for name,v in pairs(db.raw.namespaces) do + if v.servers then + v.servers[server] = nil + end + end + end + if db.namespaces then + for name,v in pairs(db.namespaces) do + rawset(v, 'server', nil) + end + end + else + if db.raw.namespaces then + local v = db.raw.namespaces[name] + if v and v.servers then + v.servers[server] = nil + end + end + if db.namespaces then + local v = db.namespaces[name] + if v then + rawset(v, 'server', nil) + end + end + end + elseif kind == "faction" then + if not name then + if db.raw.factions then + db.raw.factions[faction] = nil + end + rawset(db, 'faction', nil) + if db.raw.namespaces then + for name,v in pairs(db.raw.namespaces) do + if v.factions then + v.factions[faction] = nil + end + end + end + if db.namespaces then + for name,v in pairs(db.namespaces) do + rawset(v, 'faction', nil) + end + end + else + if db.raw.namespaces then + local v = db.raw.namespaces[name] + if v and v.factions then + v.factions[faction] = nil + end + end + if db.namespaces then + local v = db.namespaces[name] + if v then + rawset(v, 'faction', nil) + end + end + end + elseif kind == "class" then + if not name 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 + else + if db.raw.namespaces then + local v = db.raw.namespaces[name] + if v and v.classes then + v.classes[classID] = nil + end + end + if db.namespaces then + local v = db.namespaces[name] + if v then + rawset(v, 'class', nil) + end + end + end + elseif kind == "profile" then + local id = db.raw.currentProfile and db.raw.currentProfile[charID] or AceDB.registry[self] or "Default" + if id == "char" then + id = "char/" .. charID + elseif id == "class" then + id = "class/" .. classID + elseif id == "realm" then + id = "realm/" .. realmID + 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, id) + end + end + end + current = current.super + end + if type(self.OnProfileDisable) == "function" then + safecall(self.OnProfileDisable, self, id) + end + local active = self:IsActive() + + if not name then + 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 + else + if db.raw.namespaces then + local v = db.raw.namespaces[name] + if v and v.profiles then + v.profiles[id] = nil + end + end + if db.namespaces then + local v = db.namespaces[name] + if v then + rawset(v, 'profile', nil) + 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, id) + end + end + end + current = current.super + end + if type(self.OnProfileEnable) == "function" then + safecall(self.OnProfileEnable, self, id) + end + local newactive = self:IsActive() + if active ~= newactive then + if newactive then + local first = nil + if AceOO.inherits(self, "AceAddon-2.0") then + local AceAddon = AceLibrary("AceAddon-2.0") + if not AceAddon.addonsStarted[self] then + return + end + if AceAddon.addonsEnabled and not AceAddon.addonsEnabled[self] then + AceAddon.addonsEnabled[self] = true + first = true + 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.OnEmbedEnable) == "function" then + safecall(mixin.OnEmbedEnable, mixin, self, first) + end + end + end + current = current.super + end + if type(self.OnEnable) == "function" then + safecall(self.OnEnable, self, first) + end + if AceEvent then + AceEvent:TriggerEvent("Ace2_AddonEnabled", self, first) + 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 + else + return -- skip event + end + if AceEvent then + AceEvent:TriggerEvent("AceDB20_ResetDB", self, self.db.name, kind) + end +end + +local function cleanDefaults(t, defaults, blocker) + if defaults then + for k,v in pairs(t) do + if (not blocker or (blocker[k] == nil and blocker['*'] == nil and blocker['**'] == nil)) and (defaults[k] ~= nil or defaults['*'] ~= nil or defaults['**'] ~= nil) then + local u = defaults[k] + if u == nil then + u = defaults['*'] + if u == nil then + u = defaults['**'] + end + end + if v == u then + t[k] = nil + elseif type(v) == "table" and type(u) == "table" then + if cleanDefaults(v, u) then + t[k] = nil + else + local w = defaults['**'] + if w ~= u then + if cleanDefaults(v, w, u) then + t[k] = nil + end + end + end + end + end + end + end + return t and next(t) == nil +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] = AceDB.registry[self] or "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 + setmetatable(to, from) + return to +end + +function AceDB:SetProfile(name) + AceDB:argCheck(name, 2, "string") + 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 lowerName = name:lower() + if lowerName:find("^char/") or lowerName:find("^realm/") or lowerName:find("^class/") then + if lowerName:find("^char/") then + name = "char" + else + name = lowerName:sub(1, 5) + end + lowerName = name:lower() + end + local oldName = db.raw.currentProfile[charID] + if oldName:lower() == name:lower() 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 + 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) + end + end + end + current = current.super + end + if type(self.OnProfileEnable) == "function" then + safecall(self.OnProfileEnable, self, oldName, oldProfileData) + 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 + local first = nil + if AceOO.inherits(self, "AceAddon-2.0") then + local AceAddon = AceLibrary("AceAddon-2.0") + if not AceAddon.addonsStarted[self] then + return + end + if AceAddon.addonsEnabled and not AceAddon.addonsEnabled[self] then + first = true + 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, first) + end + end + end + current = current.super + end + if type(self.OnEnable) == "function" then + safecall(self.OnEnable, self, first) + end + if AceEvent then + AceEvent:TriggerEvent("Ace2_AddonEnabled", self, first) + 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 + RecalculateAceDBProfileList(self) + end + if self['acedb-profile-copylist'] then + RecalculateAceDBCopyFromList(self) + end + if Dewdrop then + Dewdrop:Refresh() + end +end + +function AceDB:CopyProfileFrom(copyFrom) + AceDB:argCheck(copyFrom, 2, "string") + if not self.db or not self.db.raw then + AceDB:error("Cannot call \"CopyProfileFrom\" before \"RegisterDB\" has been called and before \"ADDON_LOADED\" has been fired.") + end + local db = self.db + local lowerCopyFrom = copyFrom:lower() + if not db.raw.profiles or not db.raw.profiles[copyFrom] then + local good = false + if db.raw.namespaces then + for _,n in pairs(db.raw.namespaces) do + if n.profiles and n.profiles[copyFrom] then + good = true + break + end + end + end + if not good then + AceDB:error("Cannot copy from profile %q, it does not exist.", copyFrom) + end + end + local currentProfile = db.raw.currentProfile[charID] + if currentProfile:lower() == lowerCopyFrom then + AceDB:error("Cannot copy from profile %q, it is currently in use.", copyFrom) + end + local oldProfileData = db.profile + 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, currentProfile) + end + end + end + current = current.super + end + if type(self.OnProfileDisable) == "function" then + safecall(self.OnProfileDisable, self, realName) + end + local active = self:IsActive() + for k,v in pairs(db.profile) do + db.profile[k] = nil + end + if db.raw.profiles[copyFrom] then + copyTable(db.profile, db.raw.profiles[copyFrom]) + end + 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 + if db.raw.namespaces[l].profiles[copyFrom] then + copyTable(u.profile, db.raw.namespaces[l].profiles[copyFrom]) + end + inheritDefaults(u.profile, u.defaults and u.defaults.profile) + 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, copyFrom, oldProfileData, copyFrom) + end + end + end + current = current.super + end + if type(self.OnProfileEnable) == "function" then + safecall(self.OnProfileEnable, self, copyFrom, oldProfileData, copyFrom) + 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 + RecalculateAceDBProfileList(self) + end + if self['acedb-profile-copylist'] then + RecalculateAceDBCopyFromList(self) + end + if Dewdrop then + Dewdrop:Refresh() + end +end + +function AceDB:DeleteProfile(profile, noconfirm) + AceDB:argCheck(profile , 2, "string") + if not self.db or not self.db.raw then + AceDB:error("Cannot call \"DeleteProfile\" before \"RegisterDB\" has been called and before \"ADDON_LOADED\" has been fired.") + end + local db = self.db + + local currentProfile = db.raw.currentProfile[charID] + if currentProfile:lower() == profile:lower() then + AceDB:error("Cannot delete profile %q, it is currently in use.", profile) + end + + if not (noconfirm or IsShiftKeyDown()) then + if not StaticPopupDialogs["ACEDB20_CONFIRM_DELETE_DIALOG"] then + StaticPopupDialogs["ACEDB20_CONFIRM_DELETE_DIALOG"] = {} + end + local t = StaticPopupDialogs["ACEDB20_CONFIRM_DELETE_DIALOG"] + t.text = format("%s: %s?", DELETE_PROFILE, profile) + t.button1 = DELETE_PROFILE + t.button2 = CANCEL or "Cancel" + t.OnAccept = function() + self:DeleteProfile(profile, true) + end + t.timeout = 0 + t.whileDead = 1 + t.hideOnEscape = 1 + + StaticPopup_Show("ACEDB20_CONFIRM_DELETE_DIALOG") + return; + end + + local good = false + if db.raw.profiles and db.raw.profiles[profile] then + good = true; + db.raw.profiles[profile] = nil; + end + + if db.raw.namespaces then + for _,n in pairs(db.raw.namespaces) do + if n.profiles and n.profiles[profile] then + n.profiles[profile] = nil; + good = true + end + end + end + + if not good then + AceDB:error("Cannot delete profile %q, it does not exist.", profile) + end + + if self['acedb-profile-list'] then + RecalculateAceDBProfileList(self) + end + if self['acedb-profile-copylist'] then + RecalculateAceDBCopyFromList(self) + end + + if Dewdrop then + Dewdrop:Refresh() + 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, defaultProfile in pairs(AceDB.registry) do + local db = addon.db + if db then + if type(addon.OnDatabaseCleanup) == "function" then + safecall(addon.OnDatabaseCleanup, addon) + end + 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.server and cleanDefaults(db.server, db.defaults and db.defaults.server) then + if db.raw.servers then + db.raw.servers[server] = nil + if not next(db.raw.servers) then + db.raw.servers = nil + end + end + end + if db.faction and cleanDefaults(db.faction, db.defaults and db.defaults.faction) then + if db.raw.factions then + db.raw.factions[faction] = nil + if not next(db.raw.factions) then + db.raw.factions = 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 defaultProfile 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.server and cleanDefaults(v.server, v.defaults and v.defaults.server) then + if db.raw.namespaces[name].servers then + db.raw.namespaces[name].servers[server] = nil + if not next(db.raw.namespaces[name].servers) then + db.raw.namespaces[name].servers = nil + end + end + end + if v.faction and cleanDefaults(v.faction, v.defaults and v.defaults.faction) then + if db.raw.namespaces[name].factions then + db.raw.namespaces[name].factions[faction] = nil + if not next(db.raw.namespaces[name].factions) then + db.raw.namespaces[name].factions = 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 defaultProfile 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 v:lower() == (defaultProfile or "Default"):lower() 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) + RecalculateAceDBProfileList(target) + end + if not target['acedb-profile-copylist'] then + target['acedb-profile-copylist'] = setmetatable({}, caseInsensitive_mt) + RecalculateAceDBCopyFromList(target) + 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 = false, + set = "CopyProfileFrom", + 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", + }, + delete = { + name = DELETE_PROFILE, + desc = DELETE_PROFILE_DESC, + usage = DELETE_PROFILE_USAGE, + type = 'text', + set = "DeleteProfile", + get = false, + validate = target['acedb-profile-copylist'], + disabled = function() + return not next(target['acedb-profile-copylist']) + end, + }, + reset = { + name = RESET_PROFILE, + desc = RESET_PROFILE_DESC, + type = 'execute', + func = function() + target:ResetDB('profile') + end, + confirm = true, + } + } + }, + } +end + +local function activate(self, oldLib, oldDeactivate) + AceDB = self + AceEvent = AceLibrary:HasInstance("AceEvent-2.0") and AceLibrary("AceEvent-2.0") + + self.addonsToBeInitialized = oldLib and oldLib.addonsToBeInitialized or {} + self.addonsLoaded = oldLib and oldLib.addonsLoaded or {} + self.registry = oldLib and oldLib.registry or {} + for k, v in pairs(self.registry) do + if v == true then + self.registry[k] = "Default" + end + end + + self:activate(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, 'server', nil) + rawset(t.db, 'faction', nil) + rawset(t.db, 'profile', nil) + setmetatable(t.db, db_mt) + end + 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/AtlasLootFu/Libs/AceDB-2.0/AceDB-2.0.toc b/AtlasLootFu/Libs/AceDB-2.0/AceDB-2.0.toc new file mode 100644 index 0000000..0e88718 --- /dev/null +++ b/AtlasLootFu/Libs/AceDB-2.0/AceDB-2.0.toc @@ -0,0 +1,17 @@ +## Interface: 30300 +## X-Curse-Packaged-Version: r1101 +## X-Curse-Project-Name: Ace2 +## X-Curse-Project-ID: ace2 +## X-Curse-Repository-ID: wow/ace2/mainline + +## Title: Lib: AceDB-2.0 +## Notes: AddOn development framework +## Author: Ace Development Team +## LoadOnDemand: 1 +## X-Website: http://www.wowace.com +## X-Category: Library +## X-License: LGPL v2.1 + MIT for AceOO-2.0 +## Dependencies: AceLibrary, AceEvent-2.0, AceOO-2.0 + +AceDB-2.0.lua + diff --git a/AtlasLootFu/Libs/AceEvent-2.0/AceEvent-2.0.lua b/AtlasLootFu/Libs/AceEvent-2.0/AceEvent-2.0.lua new file mode 100644 index 0000000..df62cf3 --- /dev/null +++ b/AtlasLootFu/Libs/AceEvent-2.0/AceEvent-2.0.lua @@ -0,0 +1,998 @@ +--[[ +Name: AceEvent-2.0 +Revision: $Rev: 1097 $ +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/wowace/trunk/Ace2/AceEvent-2.0 +Description: Mixin to allow for event handling, scheduling, and inter-addon + communication. +Dependencies: AceLibrary, AceOO-2.0 +License: LGPL v2.1 +]] + +local MAJOR_VERSION = "AceEvent-2.0" +local MINOR_VERSION = 90000 + tonumber(("$Revision: 1097 $"):match("(%d+)")) + +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: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", + "ScheduleLeaveCombatAction", + "CancelAllCombatSchedules", +} + +local weakKey = {__mode="k"} + +local FAKE_NIL +local RATE + +local eventsWhichHappenOnce = { + PLAYER_LOGIN = true, + AceEvent_FullyInitialized = true, + VARIABLES_LOADED = true, + PLAYER_LOGOUT = true, +} +local next = next +local pairs = pairs +local pcall = pcall +local type = type +local GetTime = GetTime +local gcinfo = gcinfo +local unpack = unpack +local geterrorhandler = geterrorhandler + +local new, del +do + local cache = setmetatable({}, {__mode='k'}) + function new(...) + local t = next(cache) + if t then + cache[t] = nil + for i = 1, select('#', ...) do + t[i] = select(i, ...) + end + return t + else + return { ... } + end + end + function del(t) + for k in pairs(t) do + t[k] = nil + end + cache[t] = true + return nil + end +end + +local registeringFromAceEvent +--[[---------------------------------------------------------------------------------- +Notes: + * Registers the addon with a Blizzard event or a custom AceEvent, which will cause the given method to be called when that is triggered. +Arguments: + string - name of the event to register + [optional] string or function - name of the method or function to call. Default: same name as "event". + [optional] boolean - whether to have method called only once. Default: false +------------------------------------------------------------------------------------]] +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 = {} + 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 = {} + 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] = nil + 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] = nil + 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 + +--[[---------------------------------------------------------------------------------- +Notes: + * Registers all events to the given method + * To access the current event, check AceEvent.currentEvent + * To access the current event's unique identifier, check AceEvent.currentEventUID + * This is only for debugging purposes. +Arguments: + [optional] string or function - name of the method or function to call. Default: same name as "event". +------------------------------------------------------------------------------------]] +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 + + local remember = not AceEvent_registry[ALL_EVENTS][self] + AceEvent_registry[ALL_EVENTS][self] = method + if remember then + AceEvent:TriggerEvent("AceEvent_EventRegistered", self, "all") + end +end + +--[[---------------------------------------------------------------------------------- +Notes: + * Trigger a custom AceEvent. + * This should never be called to simulate fake Blizzard events. + * Custom events should be in the form of AddonName_SpecificEvent +Arguments: + string - name of the event + tuple - list of arguments to pass along +------------------------------------------------------------------------------------]] +function AceEvent:TriggerEvent(event, ...) + 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 lastEvent = AceEvent.currentEvent + AceEvent.currentEvent = event + local lastEventUID = AceEvent.currentEventUID + local uid = AceEvent.UID_NUM + 1 + AceEvent.UID_NUM = uid + AceEvent.currentEventUID = uid + + local tmp = new() + + local AceEvent_onceRegistry = AceEvent.onceRegistry + if AceEvent_onceRegistry and AceEvent_onceRegistry[event] then + 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 method = tmp[obj] + AceEvent.UnregisterEvent(obj, event) + if type(method) == "string" then + local obj_method = obj[method] + if obj_method then + local success, err = pcall(obj_method, obj, ...) + if not success then geterrorhandler()(err:find("%.lua:%d+:") and err or (debugstack():match("(.-: )in.-\n") or "") .. err) end + end + elseif method then -- function + local success, err = pcall(method, ...) + if not success then geterrorhandler()(err:find("%.lua:%d+:") and err or (debugstack():match("(.-: )in.-\n") or "") .. err) end + end + tmp[obj] = nil + obj = next(tmp) + end + end + + local AceEvent_throttleRegistry = AceEvent.throttleRegistry + local throttleTable = AceEvent_throttleRegistry and AceEvent_throttleRegistry[event] + if AceEvent_registry[event] then + for obj, method in pairs(AceEvent_registry[event]) do + tmp[obj] = method + end + local obj = next(tmp) + while obj do + local cont = nil + if throttleTable and throttleTable[obj] then + local 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 + cont = true + end + end + if not cont then + local method = tmp[obj] + local t = type(method) + if t == "string" then + local obj_method = obj[method] + if obj_method then + local success, err = pcall(obj_method, obj, ...) + if not success then geterrorhandler()(err:find("%.lua:%d+:") and err or (debugstack():match("(.-: )in.-\n") or "") .. err) end + end + elseif t == "function" then -- function + local success, err = pcall(method, ...) + if not success then geterrorhandler()(err:find("%.lua:%d+:") and err or (debugstack():match("(.-: )in.-\n") or "") .. err) end + else + AceEvent:error("Cannot trigger event %q. %q's handler, %q, is not a method or function (%s).", event, obj, method, t) + end + end + tmp[obj] = nil + obj = next(tmp) + end + end + if AceEvent_registry[ALL_EVENTS] then + 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 t = type(method) + if t == "string" then + local obj_method = obj[method] + if obj_method then + local success, err = pcall(obj_method, obj, ...) + if not success then geterrorhandler()(err:find("%.lua:%d+:") and err or (debugstack():match("(.-: )in.-\n") or "") .. err) end + end + elseif t == "function" then + local success, err = pcall(method, ...) + if not success then geterrorhandler()(err:find("%.lua:%d+:") and err or (debugstack():match("(.-: )in.-\n") or "") .. err) end + else + AceEvent:error("Cannot trigger event %q. %q's handler, %q, is not a method or function (%s).", event, obj, method, t) + end + tmp[obj] = nil + obj = next(tmp) + end + end + tmp = del(tmp) + AceEvent.currentEvent = lastEvent + AceEvent.currentEventUID = lastEventUID +end + +local delayRegistry +local OnUpdate +do + local tmp = {} + OnUpdate = function() + 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] = nil + 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 t = type(event) + if t == "function" then + local uid = AceEvent.UID_NUM + 1 + AceEvent.UID_NUM = uid + AceEvent.currentEventUID = uid + local success, err = pcall(event, unpack(v, 1, v.n)) + if not success then geterrorhandler()(err:find("%.lua:%d+:") and err or (debugstack():match("(.-: )in.-\n") or "") .. err) end + AceEvent.currentEventUID = nil + elseif t == "string" then + AceEvent:TriggerEvent(event, unpack(v, 1, v.n)) + else + AceEvent:error("Cannot trigger event %q, it's not a method or function (%s).", event, t) + end + if not v_repeatDelay then + local x = delayRegistry[k] + if x and x.time == v_time then -- check if it was manually reset + if type(k) == "string" then + del(delayRegistry[k]) + end + delayRegistry[k] = nil + 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 +end + +local function ScheduleEvent(self, repeating, event, delay, ...) + local id + if type(event) == "string" and type(delay) ~= "number" then + id, event, delay = event, delay, ... + AceEvent:argCheck(event, 3, "string", "function", --[[ so message is right ]] "number") + AceEvent:argCheck(delay, 4, "number") + self:CancelScheduledEvent(id) + else + AceEvent:argCheck(event, 2, "string", "function") + AceEvent:argCheck(delay, 3, "number") + end + + if not delayRegistry then + AceEvent.delayRegistry = {} + delayRegistry = AceEvent.delayRegistry + AceEvent.frame:SetScript("OnUpdate", OnUpdate) + end + local t + if id then + t = new(select(2, ...)) + t.n = select('#', ...) - 1 + else + t = new(...) + t.n = select('#', ...) + end + t.event = event + t.time = GetTime() + delay + t.self = self + t.id = id or t + t.repeatDelay = repeating and delay + delayRegistry[t.id] = t + AceEvent.frame:Show() +end + +--[[---------------------------------------------------------------------------------- +Notes: + * Schedule an event to fire. + * To fire on the next frame, specify a delay of 0. +Arguments: + string or function - name of the event to fire, or a function to call. + number - the amount of time to wait until calling. + tuple - a list of arguments to pass along. +------------------------------------------------------------------------------------]] +function AceEvent:ScheduleEvent(event, delay, ...) + if type(event) == "string" and type(delay) ~= "number" then + AceEvent:argCheck(delay, 3, "string", "function", --[[ so message is right ]] "number") + AceEvent:argCheck(..., 4, "number") + else + AceEvent:argCheck(event, 2, "string", "function") + AceEvent:argCheck(delay, 3, "number") + end + + return ScheduleEvent(self, false, event, delay, ...) +end + +function AceEvent:ScheduleRepeatingEvent(event, delay, ...) + if type(event) == "string" and type(delay) ~= "number" then + AceEvent:argCheck(delay, 3, "string", "function", --[[ so message is right ]] "number") + AceEvent:argCheck(..., 4, "number") + else + AceEvent:argCheck(event, 2, "string", "function") + AceEvent:argCheck(delay, 3, "number") + end + + return ScheduleEvent(self, true, event, delay, ...) +end + +function AceEvent:CancelScheduledEvent(t) + AceEvent:argCheck(t, 2, "string") + if delayRegistry then + local v = delayRegistry[t] + if v then + if type(t) == "string" then + del(delayRegistry[t]) + end + delayRegistry[t] = nil + 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") + 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] = nil + 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(("Cannot unregister event %q. Improperly unregistering from AceEvent-2.0."):format(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 + AceEvent_registry[ALL_EVENTS] = del(AceEvent_registry[ALL_EVENTS]) + AceEvent.frame:UnregisterAllEvents() + for k,v in pairs(AceEvent_registry) do + AceEvent.frame:RegisterEvent(k) + end + end + end + if AceEvent_registry.AceEvent_EventUnregistered then + local event, data = "AceEvent_EventUnregistered", AceEvent_registry.AceEvent_EventUnregistered + local x = data[self] + data[self] = nil + if x then + if not next(data) then + if not AceEvent_registry[ALL_EVENTS] then + AceEvent.frame:UnregisterEvent(event) + end + AceEvent_registry[event] = del(AceEvent_registry[event]) + end + AceEvent:TriggerEvent("AceEvent_EventUnregistered", self, event) + end + end + for event, data in pairs(AceEvent_registry) do + local x = data[self] + data[self] = nil + if x and event ~= ALL_EVENTS then + if not next(data) then + if not AceEvent_registry[ALL_EVENTS] then + AceEvent.frame:UnregisterEvent(event) + end + AceEvent_registry[event] = del(AceEvent_registry[event]) + end + AceEvent:TriggerEvent("AceEvent_EventUnregistered", self, event) + 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 + if type(k) == "string" then + del(delayRegistry[k]) + end + delayRegistry[k] = nil + 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]) or AceEvent_registry[ALL_EVENTS] and next(AceEvent_registry[ALL_EVENTS]) and true or false + end + if AceEvent_registry[event] and AceEvent_registry[event][self] then + return true, AceEvent_registry[event][self] + end + if AceEvent_registry[ALL_EVENTS] and AceEvent_registry[ALL_EVENTS][self] then + return true, AceEvent_registry[ALL_EVENTS][self] + end + return false, nil +end + +local UnitExists = UnitExists +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 + local buckets = AceEvent.buckets + if not buckets[event] then + buckets[event] = new() + end + if not buckets[event][self] then + local t = {} + t.current = {} + t.self = self + buckets[event][self] = t + else + AceEvent.CancelScheduledEvent(self, buckets[event][self].id) + end + local bucket = buckets[event][self] + bucket.method = method + + local n = select('#', ...) + if n > 0 then + for i = 1, n do + bucket[i] = select(i, ...) + end + end + bucket.n = n + + local func = function(arg1) + bucket.run = true + if arg1 then + bucket.current[arg1] = true + end + end + buckets[event][self].func = func + local isUnitBucket = true + if type(event) == "string" then + AceEvent.RegisterEvent(self, event, func) + if not event:find("^UNIT_") then + isUnitBucket = nil + end + else + for _,v in ipairs(event) do + AceEvent.RegisterEvent(self, v, func) + if isUnitBucket and not v:find("^UNIT_") then + isUnitBucket = nil + end + end + end + bucket.unit = isUnitBucket + if not bucketfunc then + bucketfunc = function(bucket) + if bucket.run then + local current = bucket.current + local method = bucket.method + local self = bucket.self + if bucket.unit then + for unit in pairs(current) do + if not UnitExists(unit) then + current[unit] = nil + end + end + end + if type(method) == "string" then + self[method](self, current, unpack(bucket, 1, bucket.n)) + elseif method then -- function + method(current, unpack(bucket, 1, bucket.n)) + end + for k in pairs(current) do + current[k] = nil + k = nil + end + bucket.run = nil + end + end + end + bucket.id = "AceEvent-Bucket-" .. tostring(bucket) + AceEvent.ScheduleRepeatingEvent(self, bucket.id, 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) + + bucket.current = nil + AceEvent.buckets[event][self] = nil + 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 + +local combatSchedules +function AceEvent:CancelAllCombatSchedules() + local i = 0 + while true do + i = i + 1 + if not combatSchedules[i] then + break + end + local v = combatSchedules[i] + if v.self == self then + v = del(v) + table.remove(combatSchedules, i) + i = i - 1 + end + end +end + +local inCombat = false + +function AceEvent:PLAYER_REGEN_DISABLED() + inCombat = true +end + +do + local tmp = {} + function AceEvent:PLAYER_REGEN_ENABLED() + inCombat = false + for i, v in ipairs(combatSchedules) do + tmp[i] = v + combatSchedules[i] = nil + end + for i, v in ipairs(tmp) do + local func = v.func + if func then + local success, err = pcall(func, unpack(v, 1, v.n)) + if not success then geterrorhandler()(err:find("%.lua:%d+:") and err or (debugstack():match("(.-: )in.-\n") or "") .. err) end + else + local obj = v.obj or v.self + local method = v.method + local obj_method = obj[method] + if obj_method then + local success, err = pcall(obj_method, obj, unpack(v, 1, v.n)) + if not success then geterrorhandler()(err:find("%.lua:%d+:") and err or (debugstack():match("(.-: )in.-\n") or "") .. err) end + end + end + tmp[i] = del(v) + end + end +end + +function AceEvent:ScheduleLeaveCombatAction(method, ...) + local style = type(method) + if self == AceEvent then + if style == "table" then + local func = (...) + AceEvent:argCheck(func, 3, "string") + if type(method[func]) ~= "function" then + AceEvent:error("Cannot schedule a combat action to method %q, it does not exist", func) + end + else + AceEvent:argCheck(method, 2, "function", --[[so message is right]] "table") + end + self = method + else + AceEvent:argCheck(method, 2, "function", "string", "table") + if style == "string" and type(self[method]) ~= "function" then + AceEvent:error("Cannot schedule a combat action to method %q, it does not exist", method) + elseif style == "table" then + local func = (...) + AceEvent:argCheck(func, 3, "string") + if type(method[func]) ~= "function" then + AceEvent:error("Cannot schedule a combat action to method %q, it does not exist", func) + end + end + end + + if not inCombat then + local success, err + if type(method) == "function" then + success, err = pcall(method, ...) + elseif type(method) == "table" then + local func = (...) + success, err = pcall(method[func], method, select(2, ...)) + else + success, err = pcall(self[method], self, ...) + end + if not success then geterrorhandler()(err:find("%.lua:%d+:") and err or (debugstack():match("(.-: )in.-\n") or "") .. err) end + return + end + local t + local n = select('#', ...) + if style == "table" then + t = new(select(2, ...)) + t.obj = method + t.method = (...) + t.n = n-1 + else + t = new(...) + t.n = n + if style == "function" then + t.func = method + else + t.method = method + end + end + t.self = self + table.insert(combatSchedules, t) +end + +function AceEvent:OnEmbedDisable(target) + self.UnregisterAllEvents(target) + + self.CancelAllScheduledEvents(target) + + self.UnregisterAllBucketEvents(target) + + self.CancelAllCombatSchedules(target) +end + +function AceEvent:IsFullyInitialized() + return self.postInit or false +end + +local function activate(self, oldLib, oldDeactivate) + AceEvent = self + + self.onceRegistry = oldLib and oldLib.onceRegistry or {} + self.throttleRegistry = oldLib and oldLib.throttleRegistry or {} + self.delayRegistry = oldLib and oldLib.delayRegistry or {} + self.buckets = oldLib and oldLib.buckets or {} + self.registry = oldLib and oldLib.registry or {} + self.frame = oldLib and oldLib.frame or CreateFrame("Frame", "AceEvent20Frame") + self.playerLogin = IsLoggedIn() and true + self.postInit = oldLib and oldLib.postInit or self.playerLogin and ChatTypeInfo and ChatTypeInfo.WHISPER and ChatTypeInfo.WHISPER.r and true + self.ALL_EVENTS = oldLib and oldLib.ALL_EVENTS or _G.newproxy() + self.FAKE_NIL = oldLib and oldLib.FAKE_NIL or _G.newproxy() + self.RATE = oldLib and oldLib.RATE or _G.newproxy() + self.combatSchedules = oldLib and oldLib.combatSchedules or {} + self.UID_NUM = oldLib and oldLib.UID_NUM or 0 + + combatSchedules = self.combatSchedules + 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(_, 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, ...) + end + end) + if self.delayRegistry then + delayRegistry = self.delayRegistry + self.frame:SetScript("OnUpdate", OnUpdate) + end + + self:UnregisterAllEvents() + self:CancelAllScheduledEvents() + + local function handleFullInit() + if not self.postInit then + 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 + end + registeringFromAceEvent = true + self:RegisterEvent("MEETINGSTONE_CHANGED", func, true) + self:RegisterEvent("CHAT_MSG_CHANNEL_NOTICE", func, true) + + self:ScheduleEvent("AceEvent_FullyInitialized", func, 10) + registeringFromAceEvent = nil + end + end + + if not self.playerLogin then + registeringFromAceEvent = true + self:RegisterEvent("PLAYER_LOGIN", function() + self.playerLogin = true + handleFullInit() + handleFullInit = nil + end, true) + registeringFromAceEvent = nil + else + handleFullInit() + handleFullInit = nil + end + + if not AceEvent20EditBox then + CreateFrame("Editbox", "AceEvent20EditBox") + end + local editbox = AceEvent20EditBox + function editbox:Execute(line) + local defaulteditbox = DEFAULT_CHAT_FRAME.editBox + self:SetAttribute("chatType", defaulteditbox:GetAttribute("chatType")) + self:SetAttribute("tellTarget", defaulteditbox:GetAttribute("tellTarget")) + self:SetAttribute("channelTarget", defaulteditbox:GetAttribute("channelTarget")) + self:SetText(line) + ChatEdit_SendText(self) + end + editbox:Hide() + _G["SLASH_IN1"] = "/in" + SlashCmdList["IN"] = function(msg) + local seconds, command, rest = msg:match("^([^%s]+)%s+(/[^%s]+)(.*)$") + seconds = tonumber(seconds) + if not seconds then + DEFAULT_CHAT_FRAME:AddMessage("Error, bad arguments to /in. Must be in the form of `/in 5 /say hi'") + return + end + if IsSecureCmd(command) then + DEFAULT_CHAT_FRAME:AddMessage(("Error, /in cannot call secure command: %s"):format(command)) + return + end + self:ScheduleEvent("AceEventSlashIn-" .. math.random(1, 1000000000), editbox.Execute, seconds, editbox, command .. rest) + end + + registeringFromAceEvent = true + self:RegisterEvent("PLAYER_REGEN_ENABLED") + self:RegisterEvent("PLAYER_REGEN_DISABLED") + self:RegisterEvent("LOOT_OPENED", function() + if GetRealNumRaidMembers() > 0 or GetRealNumPartyMembers() > 0 then SendAddonMessage("LOOT_OPENED", "", "RAID") end + end) + inCombat = InCombatLockdown() + registeringFromAceEvent = nil + + self:activate(oldLib, oldDeactivate) + if oldLib then + oldDeactivate(oldLib) + end +end + +AceLibrary:Register(AceEvent, MAJOR_VERSION, MINOR_VERSION, activate) diff --git a/AtlasLootFu/Libs/AceEvent-2.0/AceEvent-2.0.toc b/AtlasLootFu/Libs/AceEvent-2.0/AceEvent-2.0.toc new file mode 100644 index 0000000..b73cd6a --- /dev/null +++ b/AtlasLootFu/Libs/AceEvent-2.0/AceEvent-2.0.toc @@ -0,0 +1,17 @@ +## Interface: 30300 +## X-Curse-Packaged-Version: r1101 +## X-Curse-Project-Name: Ace2 +## X-Curse-Project-ID: ace2 +## X-Curse-Repository-ID: wow/ace2/mainline + +## Title: Lib: AceEvent-2.0 +## Notes: AddOn development framework +## Author: Ace Development Team +## LoadOnDemand: 1 +## X-Website: http://www.wowace.com +## X-Category: Library +## X-License: LGPL v2.1 + MIT for AceOO-2.0 +## Dependencies: AceLibrary, AceOO-2.0 + +AceEvent-2.0.lua + diff --git a/AtlasLootFu/Libs/AceLibrary/AceLibrary.lua b/AtlasLootFu/Libs/AceLibrary/AceLibrary.lua new file mode 100644 index 0000000..8ff1742 --- /dev/null +++ b/AtlasLootFu/Libs/AceLibrary/AceLibrary.lua @@ -0,0 +1,799 @@ +--[[ +Name: AceLibrary +Revision: $Rev: 1091 $ +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: 1091 $ +Website: http://www.wowace.com/ +Documentation: http://www.wowace.com/index.php/AceLibrary +SVN: http://svn.wowace.com/wowace/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 +License: LGPL v2.1 +]] + +local ACELIBRARY_MAJOR = "AceLibrary" +local ACELIBRARY_MINOR = 90000 + tonumber(("$Revision: 1091 $"):match("(%d+)")) + +local _G = getfenv(0) +local previous = _G[ACELIBRARY_MAJOR] +if previous and not previous:IsNewVersion(ACELIBRARY_MAJOR, ACELIBRARY_MINOR) then return end + +do + -- LibStub is a simple versioning stub meant for use in Libraries. http://www.wowace.com/wiki/LibStub for more info + -- LibStub is hereby placed in the Public Domain -- Credits: Kaelten, Cladhaire, ckknight, Mikk, Ammo, Nevcairiel, joshborke + local LIBSTUB_MAJOR, LIBSTUB_MINOR = "LibStub", 2 -- NEVER MAKE THIS AN SVN REVISION! IT NEEDS TO BE USABLE IN ALL REPOS! + local LibStub = _G[LIBSTUB_MAJOR] + + if not LibStub or LibStub.minor < LIBSTUB_MINOR then + LibStub = LibStub or {libs = {}, minors = {} } + _G[LIBSTUB_MAJOR] = LibStub + LibStub.minor = LIBSTUB_MINOR + + function LibStub:NewLibrary(major, minor) + assert(type(major) == "string", "Bad argument #2 to `NewLibrary' (string expected)") + minor = assert(tonumber(strmatch(minor, "%d+")), "Minor version must either be a number or contain a number.") + local oldminor = self.minors[major] + if oldminor and oldminor >= minor then return nil end + self.minors[major], self.libs[major] = minor, self.libs[major] or {} + return self.libs[major], oldminor + end + + function LibStub:GetLibrary(major, silent) + if not self.libs[major] and not silent then + error(("Cannot find a library instance of %q."):format(tostring(major)), 2) + end + return self.libs[major], self.minors[major] + end + + function LibStub:IterateLibraries() return pairs(self.libs) end + setmetatable(LibStub, { __call = LibStub.GetLibrary }) + end +end +local LibStub = _G.LibStub + +-- If you don't want AceLibrary to enable libraries that are LoadOnDemand but +-- disabled in the addon screen, set this to true. +local DONT_ENABLE_LIBRARIES = nil + +local function safecall(func,...) + local success, err = pcall(func,...) + if not success then geterrorhandler()(err:find("%.lua:%d+:") and err or (debugstack():match("\n(.-: )in.-\n") or "") .. err) end +end + +-- @table AceLibrary +-- @brief System to handle all versioning of libraries. +local AceLibrary = {} +local AceLibrary_mt = {} +setmetatable(AceLibrary, AceLibrary_mt) + +local function error(self, message, ...) + if type(self) ~= "table" then + return _G.error(("Bad argument #1 to `error' (table expected, got %s)"):format(type(self)), 2) + end + + local stack = debugstack() + if not message then + local second = stack:match("\n(.-)\n") + message = "error raised! " .. second + else + local arg = { ... } -- not worried about table creation, as errors don't happen often + + for i = 1, #arg do + arg[i] = tostring(arg[i]) + end + for i = 1, 10 do + table.insert(arg, "nil") + end + message = message:format(unpack(arg)) + end + + if getmetatable(self) and getmetatable(self).__tostring then + message = ("%s: %s"):format(tostring(self), message) + elseif type(rawget(self, 'GetLibraryVersion')) == "function" and AceLibrary:HasInstance(self:GetLibraryVersion()) then + message = ("%s: %s"):format(self:GetLibraryVersion(), message) + elseif type(rawget(self, 'class')) == "table" and type(rawget(self.class, 'GetLibraryVersion')) == "function" and AceLibrary:HasInstance(self.class:GetLibraryVersion()) then + message = ("%s: %s"):format(self.class:GetLibraryVersion(), message) + end + + local first = stack:gsub("\n.*", "") + local file = first:gsub(".*\\(.*).lua:%d+: .*", "%1") + file = file:gsub("([%(%)%.%*%+%-%[%]%?%^%$%%])", "%%%1") + + + local i = 0 + for s in stack:gmatch("\n([^\n]*)") do + i = i + 1 + if not s:find(file .. "%.lua:%d+:") and not s:find("%(tail call%)") then + file = s:gsub("^.*\\(.*).lua:%d+: .*", "%1") + file = file:gsub("([%(%)%.%*%+%-%[%]%?%^%$%%])", "%%%1") + break + end + end + local j = 0 + for s in stack:gmatch("\n([^\n]*)") do + j = j + 1 + if j > i and not s:find(file .. "%.lua:%d+:") and not s:find("%(tail call%)") then + return _G.error(message, j+1) + end + end + return _G.error(message, 2) +end + +local type = type +local function argCheck(self, arg, num, kind, kind2, kind3, kind4, kind5) + if type(num) ~= "number" then + return error(self, "Bad argument #3 to `argCheck' (number expected, got %s)", type(num)) + elseif type(kind) ~= "string" then + return error(self, "Bad argument #4 to `argCheck' (string expected, got %s)", type(kind)) + end + arg = type(arg) + if arg ~= kind and arg ~= kind2 and arg ~= kind3 and arg ~= kind4 and arg ~= kind5 then + local stack = debugstack() + local func = stack:match("`argCheck'.-([`<].-['>])") + if not func then + func = stack:match("([`<].-['>])") + end + if kind5 then + return 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 + return 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 + return 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 + return error(self, "Bad argument #%s to %s (%s or %s expected, got %s)", tonumber(num) or 0/0, func, kind, kind2, arg) + else + return error(self, "Bad argument #%s to %s (%s expected, got %s)", tonumber(num) or 0/0, func, kind, arg) + end + end +end + +local pcall +do + local function check(self, ret, ...) + if not ret then + local s = ... + return error(self, (s:gsub(".-%.lua:%d-: ", ""))) + else + return ... + end + end + + function pcall(self, func, ...) + return check(self, _G.pcall(func, ...)) + end +end + +local recurse = {} +local 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 + +local function svnRevisionToNumber(text) + local kind = type(text) + if kind == "number" or tonumber(text) then + return tonumber(text) + elseif kind == "string" then + if text:find("^%$Revision: (%d+) %$$") then + return tonumber((text:match("^%$Revision: (%d+) %$$"))) + elseif text:find("^%$Rev: (%d+) %$$") then + return tonumber((text:match("^%$Rev: (%d+) %$$"))) + elseif text:find("^%$LastChangedRevision: (%d+) %$$") then + return tonumber((text:match("^%$LastChangedRevision: (%d+) %$$"))) + end + end + return nil +end + +local crawlReplace +do + local recurse = {} + local function func(t, to, from) + if recurse[t] then + return + end + 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) + func(t, to, from) + for k in pairs(recurse) do + recurse[k] = nil + end + 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 +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 + +-- @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, to) + if not to then + to = {} + end + for k,v in pairs(from) do + to[k] = v + end + 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) + if not list then + list = {} + list2 = {} + 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 + 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) + end +end + +local function TryToEnable(addon) + if DONT_ENABLE_LIBRARIES then return end + local isondemand = IsAddOnLoadOnDemand(addon) + if isondemand then + local _, _, _, enabled = GetAddOnInfo(addon) + EnableAddOn(addon) + local _, _, _, _, loadable = GetAddOnInfo(addon) + if not loadable and not enabled then + DisableAddOn(addon) + end + + return loadable + 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 and loaded, true is return. If not loadable, false is 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) + + loadable = (enabled and loadable) or TryToEnable(name) + + local loaded = false + if loadable then + loaded = true + LoadAddOn(name) + end + + local field = "X-AceLibrary-" .. major + for i = 1, GetNumAddOns() do + if GetAddOnMetadata(i, field) then + name, _, _, enabled, loadable = GetAddOnInfo(i) + + loadable = (enabled and loadable) or TryToEnable(name) + if loadable then + loaded = true + LoadAddOn(name) + end + end + end + return loaded +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) + + if type(minor) == "string" then + local m = svnRevisionToNumber(minor) + if m then + minor = m + else + _G.error(("Bad argument #3 to `IsNewVersion'. Must be a number or SVN revision string. %q is not appropriate"):format(minor), 2) + end + end + argCheck(self, minor, 3, "number") + local lib, oldMinor = LibStub:GetLibrary(major, true) + if lib then + return oldMinor < minor + end + 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") + if minor ~= false then + TryToLoadStandalone(major) + end + + local lib, ver = LibStub:GetLibrary(major, true) + if not lib and self.libs[major] then + lib, ver = self.libs[major].instance, self.libs[major].minor + end + if minor then + if type(minor) == "string" then + local m = svnRevisionToNumber(minor) + if m then + minor = m + else + _G.error(("Bad argument #3 to `HasInstance'. Must be a number or SVN revision string. %q is not appropriate"):format(minor), 2) + end + end + argCheck(self, minor, 3, "number") + if not lib then + return false + end + return ver == minor + end + return not not lib +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") + if minor ~= false then + TryToLoadStandalone(major) + end + + local data, ver = LibStub:GetLibrary(major, true) + if not data then + if self.libs[major] then + data, ver = self.libs[major].instance, self.libs[major].minor + else + _G.error(("Cannot find a library instance of %s."):format(major), 2) + return + end + end + if minor then + if type(minor) == "string" then + local m = svnRevisionToNumber(minor) + if m then + minor = m + else + _G.error(("Bad argument #3 to `GetInstance'. Must be a number or SVN revision string. %q is not appropriate"):format(minor), 2) + end + end + argCheck(self, minor, 2, "number") + if ver ~= minor then + _G.error(("Cannot find a library instance of %s, minor version %d."):format(major, minor), 2) + end + end + return data +end + +-- Syntax sugar. AceLibrary("FooBar-1.0") +AceLibrary_mt.__call = AceLibrary.GetInstance + +local donothing = function() end + +local AceEvent + +local tmp = {} + +-- @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. +-- @param externalFunc (optional) A function to be called whenever a new +-- library is registered. +function AceLibrary:Register(newInstance, major, minor, activateFunc, deactivateFunc, externalFunc) + argCheck(self, newInstance, 2, "table") + argCheck(self, major, 3, "string") + if major ~= ACELIBRARY_MAJOR then + for k,v in pairs(_G) do + if v == newInstance then + geterrorhandler()((debugstack():match("(.-: )in.-\n") or "") .. ("Cannot register library %q. It is part of the global table in _G[%q]."):format(major, k)) + end + end + end + if major ~= ACELIBRARY_MAJOR and not major:find("^[%a%-][%a%d%-]*%-%d+%.%d+$") then + _G.error(string.format("Bad argument #3 to `Register'. Must be in the form of \"Name-1.0\". %q is not appropriate", major), 2) + end + if type(minor) == "string" then + local m = svnRevisionToNumber(minor) + if m then + minor = m + else + _G.error(("Bad argument #4 to `Register'. Must be a number or SVN revision string. %q is not appropriate"):format(minor), 2) + end + 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] + if not data then + -- This is new + if LibStub:GetLibrary(major, true) then + error(self, "Cannot register library %q. It is already registered with LibStub.", major) + end + local instance = LibStub:NewLibrary(major, minor) + copyTable(newInstance, instance) + crawlReplace(instance, instance, newInstance) + destroyTable(newInstance) + if AceLibrary == newInstance 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, '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_instance in LibStub:IterateLibraries() do -- all libraries + tmp[k] = data_instance + end + for k, data in pairs(self.libs) do -- Ace libraries which may not have been registered with LibStub + tmp[k] = data.instance + end + for k, data_instance in pairs(tmp) do + if k ~= major then + safecall(externalFunc, instance, k, data_instance) + end + tmp[k] = nil + end + end + + for k,data in pairs(self.libs) do -- only Ace libraries + 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 + if minor <= data.minor then + -- This one is already obsolete, raise an error. + _G.error(("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"):format(major, data.minor, minor, major, minor), 2) + return + end + local instance = data.instance + -- This is an update + local oldInstance = {} + + local libStubInstance = LibStub:GetLibrary(major, true) + if not libStubInstance then -- non-LibStub AceLibrary registered the library + -- pass + elseif libStubInstance ~= instance then + error(self, "Cannot register library %q. It is already registered with LibStub.", major) + else + LibStub:NewLibrary(major, minor) -- upgrade the minor version + end + + addToPositions(newInstance, major) + local isAceLibrary = (AceLibrary == newInstance) + local old_error, old_argCheck, old_pcall + if isAceLibrary then + self = instance + AceLibrary = instance + + old_error = instance.error + old_argCheck = instance.argCheck + old_pcall = instance.pcall + + self.error = error + 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() + return major, minor + end) + if not rawget(instance, 'error') then + rawset(instance, 'error', error) + 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, '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 + oldInstance = nil + + if externalFunc then + for k, data_instance in LibStub:IterateLibraries() do -- all libraries + tmp[k] = data_instance + end + for k, data in pairs(self.libs) do -- Ace libraries which may not have been registered with LibStub + tmp[k] = data.instance + end + for k, data_instance in pairs(tmp) do + if k ~= major then + safecall(externalFunc, instance, k, data_instance) + end + tmp[k] = nil + end + end + + return instance +end + +function AceLibrary:IterateLibraries() + local t = {} + for major, instance in LibStub:IterateLibraries() do + t[major] = instance + end + for major, data in pairs(self.libs) do + t[major] = data.instance + end + return pairs(t) +end + +local function manuallyFinalize(major, instance) + if AceLibrary.libs[major] then + -- don't work on Ace libraries + return + end + local finalizedExternalLibs = AceLibrary.finalizedExternalLibs + if finalizedExternalLibs[major] then + return + end + finalizedExternalLibs[major] = true + + for k,data in pairs(AceLibrary.libs) do -- only Ace libraries + if k ~= major and data.externalFunc then + safecall(data.externalFunc, data.instance, major, instance) + end + 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) + AceLibrary = self + if not self.libs then + self.libs = oldLib and oldLib.libs or {} + self.scannedlibs = oldLib and oldLib.scannedlibs or {} + end + if not self.positions then + self.positions = oldLib and oldLib.positions or setmetatable({}, { __mode = "k" }) + end + self.finalizedExternalLibs = oldLib and oldLib.finalizedExternalLibs or {} + self.frame = oldLib and oldLib.frame or CreateFrame("Frame") + self.frame:UnregisterAllEvents() + self.frame:RegisterEvent("ADDON_LOADED") + self.frame:SetScript("OnEvent", function() + for major, instance in LibStub:IterateLibraries() do + manuallyFinalize(major, instance) + end + end) + for major, instance in LibStub:IterateLibraries() do + manuallyFinalize(major, instance) + 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, nil) diff --git a/AtlasLootFu/Libs/AceLibrary/AceLibrary.toc b/AtlasLootFu/Libs/AceLibrary/AceLibrary.toc new file mode 100644 index 0000000..77f0cf3 --- /dev/null +++ b/AtlasLootFu/Libs/AceLibrary/AceLibrary.toc @@ -0,0 +1,15 @@ +## Interface: 30300 +## X-Curse-Packaged-Version: r1101 +## X-Curse-Project-Name: Ace2 +## X-Curse-Project-ID: ace2 +## X-Curse-Repository-ID: wow/ace2/mainline + +## Title: Lib: AceLibrary +## Notes: AddOn development framework +## Author: Ace Development Team +## X-Website: http://www.wowace.com +## X-Category: Library +## X-License: LGPL v2.1 + MIT for AceOO-2.0 + +AceLibrary.lua + diff --git a/AtlasLootFu/Libs/AceOO-2.0/AceOO-2.0.lua b/AtlasLootFu/Libs/AceOO-2.0/AceOO-2.0.lua new file mode 100644 index 0000000..4a540f8 --- /dev/null +++ b/AtlasLootFu/Libs/AceOO-2.0/AceOO-2.0.lua @@ -0,0 +1,980 @@ +--[[ +Name: AceOO-2.0 +Revision: $Rev: 1091 $ +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/wowace/trunk/Ace2/AceOO-2.0 +Description: Library to provide an object-orientation framework. +Dependencies: AceLibrary +License: MIT +]] + +local MAJOR_VERSION = "AceOO-2.0" +local MINOR_VERSION = 90000 + tonumber(("$Revision: 1091 $"):match("(%d+)")) + +-- 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 + +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 getuid(t) + local mt = getmetatable(t) + setmetatable(t, nil) + local str = tostring(t) + setmetatable(t, mt) + local cap = str:match("[^:]*: 0x(.*)$") or str:match("[^:]*: (.*)$") + if cap then + return ("0"):rep(8 - #cap) .. 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 + +local function deeprawget(self, k) + while true do + local v = rawget(self, k) + if v ~= nil then + return v + end + local mt = getmetatable(self) + if not mt or type(mt.__index) ~= "table" then + return nil + end + self = mt.__index + 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 (...) 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 getlibraries(...) + if select('#', ...) == 0 then + return + end + return getlibrary((select(1, ...))), getlibraries(select(2, ...)) + end + local arg = {} + local function new(obj, ...) + local t = {} + local uid = getuid(t) + obj:init(t, getlibraries(...)) + t.uid = uid + return t + end + + local function createnew(self, ...) + local o = self.prototype + local x = new(o, getlibraries(...)) + return x + end + + function Factory(obj, newobj, ...) + local t = new(obj, ...) + 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 + local class = deeprawget(object, 'class') + if class then + current = 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 = deeprawget(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 = deeprawget(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 = deeprawget(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 (...) Arguments to pass to the object init function. +-- @return The new object. +-- +-- @method Class init +-- @brief Initialize a new class. +-- @param parent Superclass. +-- @param (...) 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, ...) + 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(...) + 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, ...) + return self:new(...) + end, + } + function Class:init(newclass, parent, ...) + parent = parent or self + + local total + + if parent.class then + total = { parent, ... } + parent = self + else + total = { ... } + 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 v.__deprecated then + AceOO:error(v.__deprecated) + end + 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 not newclass.interfaces then + newclass.interfaces = {} + end + for interface in pairs(parent.interfaces) do + newclass.interfaces[interface] = true + end + end + for k in pairs(total) do + total[k] = nil + end + + 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(...) + local x = ClassFactory:new(...) + 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) + if self.__deprecated then + AceOO:error(self.__deprecated) + end + 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, ...) + 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 = #export + for i = 1, num do + local v = export[i] + export[i] = nil + export[v] = true + end + + local interfaces + if select('#', ...) >= 1 then + interfaces = { ... } + for i,v in ipairs(interfaces) do + v = getlibrary(v) + interfaces[i] = v + if not v.class or not inherits(v, Interface) then + AceOO:error("Mixins can inherit only from interfaces") + end + end + local num = #interfaces + for i = 1, num do + local v = interfaces[i] + interfaces[i] = nil + interfaces[v] = true + end + 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 (...) Superinterfaces +do + Interface = Class() + function Interface:ToString() + if self.GetLibraryVersion then + return (self:GetLibraryVersion()) + else + return 'Instance' + end + end + function Interface.prototype:init(interface, ...) + 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 + if select('#', ...) >= 1 then + self.superinterfaces = { ... } + for i,v in ipairs(self.superinterfaces) do + v = getlibrary(v) + self.superinterfaces[i] = v + if not inherits(v, Interface) or not v.class then + AceOO:error('Cannot provide a non-Interface to inherit from') + end + end + local num = #self.superinterfaces + for i = 1, num do + local v = self.superinterfaces[i] + self.superinterfaces[i] = nil + self.superinterfaces[v] = true + end + 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, ...) + 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 + end + for i = 1, select('#', ...) do + local m = select(i, ...) + if m.uid then + table.insert(t, m.uid) + else + AceOO:error("%s is not an appropriate mixin", ts(m)) + end + end + table.sort(t) + local uid = table.concat(t, '') + local num = #t + for i = 1, num do + t[i] = nil + end + return uid + end + local classmeta + local arg = {} + function Classpool(superclass, ...) + local l = getlibrary + superclass = getlibrary(superclass) + arg = { ... } + for i, v in ipairs(arg) do + arg[i] = getlibrary(v) + end + if superclass then + if superclass.class then -- mixin + table.insert(arg, 1, superclass) + superclass = Class + end + else + superclass = Class + end + local key = getcomplexuid(superclass, unpack(arg)) + if not pool[key] then + local class = Class(superclass, unpack(arg)) + 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/AtlasLootFu/Libs/AceOO-2.0/AceOO-2.0.toc b/AtlasLootFu/Libs/AceOO-2.0/AceOO-2.0.toc new file mode 100644 index 0000000..b01fec5 --- /dev/null +++ b/AtlasLootFu/Libs/AceOO-2.0/AceOO-2.0.toc @@ -0,0 +1,16 @@ +## Interface: 30300 +## X-Curse-Packaged-Version: r1101 +## X-Curse-Project-Name: Ace2 +## X-Curse-Project-ID: ace2 +## X-Curse-Repository-ID: wow/ace2/mainline + +## Title: Lib: AceOO-2.0 +## Notes: AddOn development framework +## Author: Ace Development Team +## LoadOnDemand: 1 +## X-Website: http://www.wowace.com +## X-Category: Library +## X-License: LGPL v2.1 + MIT for AceOO-2.0 +## Dependencies: AceLibrary + +AceOO-2.0.lua diff --git a/AtlasLootFu/Libs/FuBarPlugin-2.0/FuBarPlugin-2.0.lua b/AtlasLootFu/Libs/FuBarPlugin-2.0/FuBarPlugin-2.0.lua new file mode 100644 index 0000000..e2ae1e3 --- /dev/null +++ b/AtlasLootFu/Libs/FuBarPlugin-2.0/FuBarPlugin-2.0.lua @@ -0,0 +1,1849 @@ +--[[ +Name: FuBarPlugin-2.0 +Revision: $Rev: 9 $ +Author: Cameron Kenneth Knight (ckknight@gmail.com) +Website: http://wiki.wowace.com/index.php/FuBarPlugin-2.0 +Documentation: http://wiki.wowace.com/index.php/FuBarPlugin-2.0 +SVN: svn://svn.wowace.com/wowace/trunk/FuBarPlugin-2.0/FuBarPlugin-2.0/ +Description: Plugin for FuBar. +Dependencies: AceLibrary, AceOO-2.0, AceEvent-2.0, (optional) Tablet-2.0, Dewdrop-2.0 +License: LGPL v2.1 + +Notes: When embeding this library, FuBar should be set as an optional dependency. +]] + +local MAJOR_VERSION = "FuBarPlugin-2.0" +local MINIMAPCONTAINER_MAJOR_VERSION = "FuBarPlugin-MinimapContainer-2.0" +local MINOR_VERSION = 90000 + tonumber(("$Revision: 9 $"):match("(%d+)")) + +-- 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 not AceLibrary:HasInstance("AceOO-2.0") then error(MAJOR_VERSION .. " requires AceOO-2.0.") end + +local AceEvent = AceLibrary:HasInstance("AceEvent-2.0") and AceLibrary("AceEvent-2.0") +local Tablet = AceLibrary:HasInstance("Tablet-2.0") and AceLibrary("Tablet-2.0") +local Dewdrop = AceLibrary:HasInstance("Dewdrop-2.0") and AceLibrary("Dewdrop-2.0") +local AceAddon + +local epsilon = 1e-5 +local _G = getfenv(0) + +local SHOW_ICON = "Show icon" +local SHOW_ICON_DESC = "Show the plugins icon on the panel." +local SHOW_TEXT = "Show text" +local SHOW_TEXT_DESC = "Show the plugins text on the panel." +local SHOW_COLORED_TEXT = "Show colored text" +local SHOW_COLORED_TEXT_DESC = "Allow the plugin to color its text." +local DETACH_TOOLTIP = "Detach tooltip" +local DETACH_TOOLTIP_DESC = "Detach the tooltip from the panel." +local LOCK_TOOLTIP = "Lock tooltip" +local LOCK_TOOLTIP_DESC = "Lock the tooltips position. When the tooltip is locked, you must use Alt to access it with your mouse." +local POSITION = "Position" +local POSITION_DESC = "Position the plugin on the panel." +local POSITION_LEFT = "Left" +local POSITION_RIGHT = "Right" +local POSITION_CENTER = "Center" +local ATTACH_TO_MINIMAP = "Attach to minimap" +local ATTACH_TO_MINIMAP_DESC = "Attach the plugin to the minimap instead of the panel." +local HIDE_FUBAR_PLUGIN = "Hide plugin" +local HIDE_FUBAR_PLUGIN_CMD = "Hidden" +local HIDE_FUBAR_PLUGIN_DESC = "Hide the plugin from the panel or minimap, leaving the addon running." +local OTHER = "Other" +local CLOSE = "Close" +local CLOSE_DESC = "Close the menu." + +if GetLocale() == "koKR" then + SHOW_ICON = "아이콘 표시" + SHOW_ICON_DESC = "패널에 플러그인 아이콘을 표시합니다." + SHOW_TEXT = "텍스트 표시" + SHOW_TEXT_DESC = "페널에 플러그인 텍스트를 표시합니다." + SHOW_COLORED_TEXT = "색상화된 텍스트 표시" + SHOW_COLORED_TEXT_DESC = "플러그인의 텍스트 색상을 허용합니다." + DETACH_TOOLTIP = "툴팁 분리" + DETACH_TOOLTIP_DESC = "패널에서 툴팁을 분리 합니다." + LOCK_TOOLTIP = "툴팁 고정" + LOCK_TOOLTIP_DESC = "툴팁 위치를 고정합니다." + POSITION = "위치" + POSITION_DESC = "패널에서 플러그인의 위치를 설정합니다." + POSITION_LEFT = "왼쪽" + POSITION_RIGHT = "오른쪽" + POSITION_CENTER = "가운데" + ATTACH_TO_MINIMAP = "미니맵에 표시" + ATTACH_TO_MINIMAP_DESC = "플러그인을 패널 대신 미니맵에 표시합니다." + HIDE_FUBAR_PLUGIN = "FuBar 플러그인 숨기기" + HIDE_FUBAR_PLUGIN_CMD = "숨겨짐" + HIDE_FUBAR_PLUGIN_DESC = "패널에서 플러그인을 숨깁니다." + OTHER = "기타" + CLOSE = "닫기" + CLOSE_DESC = "메뉴 닫기." +elseif GetLocale() == "deDE" then + SHOW_ICON = "Zeige Icon" + SHOW_ICON_DESC = "Zeige das Plugin-Icon auf der Leiste." + SHOW_TEXT = "Zeige Text" + SHOW_TEXT_DESC = "Zeige den Plugin-Text auf der Leiste." + SHOW_COLORED_TEXT = "Zeige gef\195\164rbten Text" + SHOW_COLORED_TEXT_DESC = "Dem Plugin erlauben sein Text zu f\195\164rben." + DETACH_TOOLTIP = "Tooltip l\195\182sen" + DETACH_TOOLTIP_DESC = "Tooltip von der Leiste l\195\182sen." + LOCK_TOOLTIP = "Tooltip sperren" + LOCK_TOOLTIP_DESC = "Tooltip an der Position sperren." + POSITION = "Position" + POSITION_DESC = "Positioniert das Plugin auf der Leiste." + POSITION_LEFT = "Links" + POSITION_RIGHT = "Rechts" + POSITION_CENTER = "Mitte" + ATTACH_TO_MINIMAP = "An der Minimap anbringen" + ATTACH_TO_MINIMAP_DESC = "Bringt das Plugin an der Minimap anstelle der Leiste an." + HIDE_FUBAR_PLUGIN = "Versteckt das FuBar Plugin" + HIDE_FUBAR_PLUGIN_CMD = "Verstecken" + HIDE_FUBAR_PLUGIN_DESC = "Versteckt das Plugin von der Leiste." + CLOSE = "Schlie\195\159en" + CLOSE_DESC = "Men\195\188 schlie\195\159en." +elseif GetLocale() == "frFR" then + SHOW_ICON = "Afficher l'ic\195\180ne" + SHOW_ICON_DESC = "Afficher l'ic\195\180ne du plugin sur le panneau." + SHOW_TEXT = "Afficher le texte" + SHOW_TEXT_DESC = "Afficher le texte du plugin sur le panneau." + SHOW_COLORED_TEXT = "Afficher la couleur du texte" + SHOW_COLORED_TEXT_DESC = "Permet au plugin de colorer le texte." + DETACH_TOOLTIP = "D\195\169tacher le tooltip" + DETACH_TOOLTIP_DESC = "Permet de d\195\169tacher le tooltip du panneau." + LOCK_TOOLTIP = "Bloquer le tooltip" + LOCK_TOOLTIP_DESC = "Permet de bloquer le tooltip \195\160 sa position actuelle. Une fois le tooltip bloqu\195\169, vous devez utiliser la touche Alt pour le d\195\169placer avec votre souris." + POSITION = "Position" + POSITION_DESC = "Permet de changer la position du plugin dans le panneau." + POSITION_LEFT = "Gauche" + POSITION_RIGHT = "Droite" + POSITION_CENTER = "Centre" + ATTACH_TO_MINIMAP = "Attacher \195\160 la minicarte" + ATTACH_TO_MINIMAP_DESC = "Attache l'ic\195\180ne du plugin \195\160 la minicarte." + HIDE_FUBAR_PLUGIN = "Masquer le plugin" + HIDE_FUBAR_PLUGIN_CMD = "Masqu\195\169" + HIDE_FUBAR_PLUGIN_DESC = "Permet de masquer compl\195\168tement le plugin du panneau, mais laisse l'addon fonctionner." + OTHER = "Autre" + CLOSE = "Fermer" + CLOSE_DESC = "Ferme le menu." +elseif GetLocale() == "zhCN" then + SHOW_ICON = "显示图标" + SHOW_ICON_DESC = "在面板上显示插件图标。" + SHOW_TEXT = "显示文字" + SHOW_TEXT_DESC = "在面板上显示文字标题。" + SHOW_COLORED_TEXT = "显示彩色文字" + SHOW_COLORED_TEXT_DESC = "允许插件显示彩色文字。" + DETACH_TOOLTIP = "独立提示信息" + DETACH_TOOLTIP_DESC = "从面板上独立提示信息。" + LOCK_TOOLTIP = "锁定提示信息" + LOCK_TOOLTIP_DESC = "锁定提示信息位置。" + POSITION = "位置" + POSITION_DESC = "插件在面板上的位置。" + POSITION_LEFT = "居左" + POSITION_RIGHT = "居右" + POSITION_CENTER = "居中" + ATTACH_TO_MINIMAP = "依附在小地图" + ATTACH_TO_MINIMAP_DESC = "插件图标依附在小地图而不显示在面板上。" + HIDE_FUBAR_PLUGIN = "隐藏 FuBar 插件" + HIDE_FUBAR_PLUGIN_CMD = "隐藏" + HIDE_FUBAR_PLUGIN_DESC = "在面板上隐藏该插件。" + OTHER = "其他" + CLOSE = "关闭" + CLOSE_DESC = "关闭菜单" +elseif GetLocale() == "zhTW" then + SHOW_ICON = "顯示圖示" + SHOW_ICON_DESC = "在面板上顯示插件圖示。" + SHOW_TEXT = "顯示文字" + SHOW_TEXT_DESC = "在面板上顯示插件文字。" + SHOW_COLORED_TEXT = "允許彩色文字" + SHOW_COLORED_TEXT_DESC = "允許插件在面板上使用彩色文字。" + DETACH_TOOLTIP = "獨立提示訊息" + DETACH_TOOLTIP_DESC = "從面板上獨立提示訊息。" + LOCK_TOOLTIP = "鎖定提示訊息" + LOCK_TOOLTIP_DESC = "鎖定提示訊息位置。當提示訊息鎖定時,需要用Alt鍵使用提示訊息的功能。" + POSITION = "位置" + POSITION_DESC = "插件在面板上的位置。" + POSITION_LEFT = "靠左" + POSITION_RIGHT = "靠右" + POSITION_CENTER = "置中" + ATTACH_TO_MINIMAP = "依附在小地圖" + ATTACH_TO_MINIMAP_DESC = "插件圖標依附在小地圖而不顯示在面板上。" + HIDE_FUBAR_PLUGIN = "隱藏插件" + HIDE_FUBAR_PLUGIN_CMD = "隱藏" + HIDE_FUBAR_PLUGIN_DESC = "在面板或小地圖上隱藏該插件,但保持執行狀態。" + OTHER = "其他" + CLOSE = "關閉" + CLOSE_DESC = "關閉選單。" +elseif GetLocale() == "esES" then + SHOW_ICON = "Mostrar icono" + SHOW_ICON_DESC = "Muestra el icono del plugin en el panel" + SHOW_TEXT = "Mostrar texto" + SHOW_TEXT_DESC = "Muestra el texto del plugin en el panel" + SHOW_COLORED_TEXT = "Mostrar el texto en color" + SHOW_COLORED_TEXT_DESC = "Permite al plugin colorear su texto" + DETACH_TOOLTIP = "Separar tooltip" + DETACH_TOOLTIP_DESC = "Separa el tooltip del panel" + LOCK_TOOLTIP = "Bloquear tooltip" + LOCK_TOOLTIP_DESC = "Bloquea la posici\195\179n de los tooltips. Cuando el tooltip est\195\161 bloqueado debes usar Alt para acceder a \195\169l con el rat\195\179n" + POSITION = "Posici\195\179n" + POSITION_DESC = "Posici\195\179n del plugin en el panel" + POSITION_LEFT = "Izquierda" + POSITION_RIGHT = "Derecha" + POSITION_CENTER = "Centro" + ATTACH_TO_MINIMAP = "Adjuntar al minimapa" + ATTACH_TO_MINIMAP_DESC = "Adjunta el plugin al minimapa en vez de al panel." + HIDE_FUBAR_PLUGIN = "Ocultar plugin" + HIDE_FUBAR_PLUGIN_CMD = "Oculto" + HIDE_FUBAR_PLUGIN_DESC = "Oculta el plugin del panel o minimapa, dejando el accesorio funcionando." + OTHER = "Otros" + CLOSE = "Cerrar" + CLOSE_DESC = "Cierra el men\195\186." +elseif GetLocale() == "ruRU" then + SHOW_ICON = "Показывать иконку" + SHOW_ICON_DESC = "Показывать иконку плагина на панели." + SHOW_TEXT = "Показывать текст" + SHOW_TEXT_DESC = "Показывать текст плагина на панели." + SHOW_COLORED_TEXT = "Показывать цветной текст" + SHOW_COLORED_TEXT_DESC = "Позволить плагину использовать его цвета в тексте." + DETACH_TOOLTIP = "Отделить подсказку" + DETACH_TOOLTIP_DESC = "Отделить всплывающую подсказку от панели." + LOCK_TOOLTIP = "Закрепить подсказку" + LOCK_TOOLTIP_DESC = "Закрепить позицию всплывающей подсказки. Когда всплывающая подсказка закреплена, используйте Alt для отображения ее у мыши." + POSITION = "Позиция" + POSITION_DESC = "Позиция плагина на панели." + POSITION_LEFT = "Слева" + POSITION_RIGHT = "Справа" + POSITION_CENTER = "По центру" + ATTACH_TO_MINIMAP = "Закрепить у мини-карты" + ATTACH_TO_MINIMAP_DESC = "Закрепить плагин у мини-карты, вместо панели." + HIDE_FUBAR_PLUGIN = "Скрыть плагин" + HIDE_FUBAR_PLUGIN_CMD = "Скрыть" + HIDE_FUBAR_PLUGIN_DESC = "Скрыть плагин с панели или мини-карты, но оставить аддон в рабочем состоянии." + OTHER = "Другое" + CLOSE = "Закрыть" + CLOSE_DESC = "Закрыть меню." +end + +local AceOO = AceLibrary("AceOO-2.0") +local FuBarPlugin = AceOO.Mixin { + "GetTitle", + "GetName", + "GetCategory", + "SetFontSize", + "GetFrame", + "Show", + "Hide", + "GetPanel", + "IsTextColored", + "ToggleTextColored", + "IsMinimapAttached", + "ToggleMinimapAttached", + "Update", + "UpdateDisplay", + "UpdateData", + "UpdateText", + "UpdateTooltip", + "SetIcon", + "GetIcon", + "CheckWidth", + "SetText", + "GetText", + "IsIconShown", + "ToggleIconShown", + "ShowIcon", + "HideIcon", + "IsTextShown", + "ToggleTextShown", + "ShowText", + "HideText", + "IsTooltipDetached", + "ToggleTooltipDetached", + "DetachTooltip", + "ReattachTooltip", + "GetDefaultPosition", + "SetPanel", + "IsLoadOnDemand", + "IsDisabled", + "CreateBasicPluginFrame", + "CreatePluginChildFrame", + "OpenMenu", + "AddImpliedMenuOptions", +} +local MinimapContainer + +local good = nil +local function CheckFuBar() + if not good then + good = FuBar and tonumber(FuBar.version:sub(1, 3)) and tonumber(FuBar.version:sub(1, 3)) >= 2 and true + end + return good +end + +function FuBarPlugin:GetTitle() + local name = self.title or self.name + if not name then + FuBarPlugin:error("You must provide self.title or self.name") + end + local title = select(3, name:find("FuBar %- (.-)%s*$")) + if not title then + title = name + end + return title:gsub("|c%x%x%x%x%x%x%x%x", ""):gsub("|r", "") +end + +function FuBarPlugin:GetName() + return self.name +end + +function FuBarPlugin:GetCategory() + return self.category or OTHER +end + +function FuBarPlugin:GetFrame() + return self.frame +end + +function FuBarPlugin:GetPanel() + return self.panel +end + +function FuBarPlugin:IsTextColored() + return not self.db or not self.db.profile or not self.db.profile.uncolored +end + +function FuBarPlugin:ToggleTextColored() + if not self.db then + FuBarPlugin:error("Cannot change text color if self.db is not available. (" .. self:GetTitle() .. ")") + end + self.db.profile.uncolored = not self.db.profile.uncolored or nil + self:UpdateText() +end + +function FuBarPlugin:ToggleMinimapAttached() + if CheckFuBar() and not self.cannotAttachToMinimap then + local value = self:IsMinimapAttached() + if value then + if self.panel then + self.panel:RemovePlugin(self) + end + if self.defaultPosition == "MINIMAP" then + FuBar:GetPanel(1):AddPlugin(self, nil, "LEFT") + else + FuBar:GetPanel(1):AddPlugin(self, nil, self.defaultPosition) + end + else + if self.panel then + self.panel:RemovePlugin(self) + end + MinimapContainer:AddPlugin(self) + end + end + Dewdrop:Close() +end + +function FuBarPlugin:IsMinimapAttached() + if not CheckFuBar() then + return true + end + return self.panel == MinimapContainer +end + +function FuBarPlugin:Update() + self:UpdateData() + self:UpdateText() + self:UpdateTooltip() +end + +function FuBarPlugin:UpdateDisplay() + self:UpdateText() + self:UpdateTooltip() +end + +function FuBarPlugin:UpdateData() + if type(self.OnDataUpdate) == "function" then + if not self:IsDisabled() then + self:OnDataUpdate() + end + end +end + +function FuBarPlugin:UpdateText() + if type(self.OnTextUpdate) == "function" then + if not self:IsDisabled() then + self:OnTextUpdate() + end + elseif self:IsTextShown() then + self:SetText(self:GetTitle()) + end +end + +function FuBarPlugin:RegisterTablet() + if self.blizzardTooltip or self.overrideTooltip or not Tablet then + return + end + + if not Tablet:IsRegistered(self.frame) then + if self.db and self.db.profile and not self.db.profile.detachedTooltip then + self.db.profile.detachedTooltip = {} + end + Tablet:Register(self.frame, + 'children', function() + Tablet:SetTitle(self:GetTitle()) + if type(self.OnTooltipUpdate) == "function" then + if not self:IsDisabled() then + self:OnTooltipUpdate() + end + end + end, + 'clickable', self.clickableTooltip, + 'data', CheckFuBar() and FuBar.db.profile.tooltip or self.db and self.db.profile.detachedTooltip or {}, + 'detachedData', self.db and self.db.profile.detachedTooltip or {}, + 'point', function(frame) + if frame:GetTop() > GetScreenHeight() / 2 then + local x = frame:GetCenter() + if x < GetScreenWidth() / 3 then + return "TOPLEFT", "BOTTOMLEFT" + elseif x < GetScreenWidth() * 2 / 3 then + return "TOP", "BOTTOM" + else + return "TOPRIGHT", "BOTTOMRIGHT" + end + else + local x = frame:GetCenter() + if x < GetScreenWidth() / 3 then + return "BOTTOMLEFT", "TOPLEFT" + elseif x < GetScreenWidth() * 2 / 3 then + return "BOTTOM", "TOP" + else + return "BOTTOMRIGHT", "TOPRIGHT" + end + end + end, + 'menu', self.OnMenuRequest and function(level, value, valueN_1, valueN_2, valueN_3, valueN_4) + if level == 1 then + local name = tostring(self) + if not name:find('^table:') then + name = name:gsub("|c%x%x%x%x%x%x%x%x(.-)|r", "%1") + Dewdrop:AddLine( + 'text', name, + 'isTitle', true + ) + end + end + if type(self.OnMenuRequest) == "function" then + self:OnMenuRequest(level, value, true, valueN_1, valueN_2, valueN_3, valueN_4) + elseif type(self.OnMenuRequest) == "table" then + Dewdrop:FeedAceOptionsTable(self.OnMenuRequest) + end + end, + 'hideWhenEmpty', self.tooltipHiddenWhenEmpty + ) + local func = self.frame:GetScript("OnEnter") + local function newFunc(this, ...) + func(this, ...) + + if FuBar and FuBar.IsHidingTooltipsInCombat and FuBar:IsHidingTooltipsInCombat() and InCombatLockdown() then + local frame = this.self.frame + if self.blizzardTooltip then + if GameTooltip:IsOwned(self:IsMinimapAttached() and self.minimapFrame or self.frame) then + GameTooltip:Hide() + end + elseif self.overrideTooltip and type(self.CloseTooltip) == "function" then + self:CloseTooltip() + elseif not self.overrideTooltip and Tablet and Tablet:IsAttached(frame) then + Tablet:Close(frame) + end + end + end + self.frame:SetScript("OnEnter", newFunc) + end +end + +function FuBarPlugin:UpdateTooltip() + if self.blizzardTooltip then + if GameTooltip:IsOwned(self:IsMinimapAttached() and self.minimapFrame or self.frame) then + GameTooltip:Hide() + + local frame = self:IsMinimapAttached() and self.minimapFrame or self.frame + local anchor + if frame:GetTop() > GetScreenHeight() / 2 then + local x = frame:GetCenter() + if x < GetScreenWidth() / 2 then + anchor = "ANCHOR_BOTTOMRIGHT" + else + anchor = "ANCHOR_BOTTOMLEFT" + end + else + local x = frame:GetCenter() + if x < GetScreenWidth() / 2 then + anchor = "ANCHOR_TOPLEFT" + else + anchor = "ANCHOR_TOPRIGHT" + end + end + GameTooltip:SetOwner(frame, anchor) + if type(self.OnTooltipUpdate) == "function" and not self:IsDisabled() then + self:OnTooltipUpdate() + end + GameTooltip:Show() + end + return + elseif self.overrideTooltip then + if type(self.OnTooltipUpdate) == "function" and not self:IsDisabled() then + self:OnTooltipUpdate() + end + return + end + if not Tablet then return end + + FuBarPlugin.RegisterTablet(self) + if self:IsMinimapAttached() and not self:IsTooltipDetached() and self.minimapFrame then + Tablet:Refresh(self.minimapFrame) + else + Tablet:Refresh(self.frame) + end +end + +function FuBarPlugin:OnProfileEnable() + self:Update() +end + +function FuBarPlugin:Show(panelId) + if self.frame:IsShown() or (self.minimapFrame and self.minimapFrame:IsShown()) then + return + end + if panelId ~= false then + if self.db then + self.db.profile.hidden = nil + end + end + if self.IsActive and not self:IsActive() then + self.panelIdTmp = panelId + self:ToggleActive() + self.panelIdTmp = nil + if self.db then + self.db.profile.disabled = nil + end + elseif not self.db or not self.db.profile.hidden then + if panelId == 0 or not CheckFuBar() then + MinimapContainer:AddPlugin(self) + else + FuBar:ShowPlugin(self, panelId or self.panelIdTmp) + end + if not self.userDefinedFrame then + if not self:IsTextShown() then + self.textFrame:SetText("") + self.textFrame:SetWidth(epsilon) + self.textFrame:Hide() + end + if not self:IsIconShown() then + self.iconFrame:SetWidth(epsilon) + self.iconFrame:Hide() + end + end + if AceOO.inherits(self, "AceAddon-2.0") then + if not AceAddon then + AceAddon = AceLibrary("AceAddon-2.0") + end + if AceAddon.addonsEnabled and not AceAddon.addonsEnabled[self] then + return + end + end + self:Update() + end +end + +function FuBarPlugin:Hide(check) + if not self.frame:IsShown() and (not self.minimapFrame or not self.minimapFrame:IsShown()) then + return + end + if self.hideWithoutStandby and self.db and check ~= false then + self.db.profile.hidden = true + end + if not self.hideWithoutStandby then + if self.db and not self.overrideTooltip and not self.blizzardTooltip and not self.cannotDetachTooltip and self:IsTooltipDetached() and self.db.profile.detachedTooltip and self.db.profile.detachedTooltip.detached then + self:ReattachTooltip() + self.db.profile.detachedTooltip.detached = true + end + if self.IsActive and self:IsActive() and self.ToggleActive and (not CheckFuBar() or not FuBar:IsChangingProfile()) then + self:ToggleActive() + end + end + if self.panel then + self.panel:RemovePlugin(self) + end + self.frame:Hide() + if self.minimapFrame then + self.minimapFrame:Hide() + end + + if Dewdrop:IsOpen(self.frame) or (self.minimapFrame and Dewdrop:IsOpen(self.minimapFrame)) then + Dewdrop:Close() + end +end + +function FuBarPlugin:SetIcon(path) + if not path then + return + end + FuBarPlugin:argCheck(path, 2, "string", "boolean") + if not self.hasIcon then + FuBarPlugin:error("Cannot set icon unless self.hasIcon is set. (" .. self:GetTitle() .. ")") + end + if not self.iconFrame then + return + end + if type(path) ~= "string" then + path = format("Interface\\AddOns\\%s\\icon", FuBarPlugin.folderNames[self] or self.folderName) + elseif not path:find('^Interface[\\/]') then + path = format("Interface\\AddOns\\%s\\%s", FuBarPlugin.folderNames[self] or self.folderName, path) + end + if path:sub(1, 16) == "Interface\\Icons\\" then + self.iconFrame:SetTexCoord(0.05, 0.95, 0.05, 0.95) + else + self.iconFrame:SetTexCoord(0, 1, 0, 1) + end + self.iconFrame:SetTexture(path) + if self.minimapIcon then + if path:sub(1, 16) == "Interface\\Icons\\" then + self.minimapIcon:SetTexCoord(0.05, 0.95, 0.05, 0.95) + else + self.minimapIcon:SetTexCoord(0, 1, 0, 1) + end + self.minimapIcon:SetTexture(path) + end +end + +function FuBarPlugin:GetIcon() + if self.hasIcon then + return self.iconFrame:GetTexture() + end +end + +function FuBarPlugin:CheckWidth(force) + FuBarPlugin:argCheck(force, 2, "boolean", "nil") + if (self.iconFrame and self.iconFrame:IsShown()) or (self.textFrame and self.textFrame:IsShown()) then + if (self.db and self.db.profile and not self:IsIconShown()) or not self.hasIcon then + self.iconFrame:SetWidth(epsilon) + end + local width + if not self.hasNoText then + self.textFrame:SetHeight(0) + self.textFrame:SetWidth(500) + width = self.textFrame:GetStringWidth() + 1 + self.textFrame:SetWidth(width) + self.textFrame:SetHeight(self.textFrame:GetHeight()) + end + if self.hasNoText or not self.textFrame:IsShown() then + self.frame:SetWidth(self.iconFrame:GetWidth()) + if self.panel and self.panel:GetPluginSide(self) == "CENTER" then + self.panel:UpdateCenteredPosition() + end + elseif force or not self.textWidth or self.textWidth < width or self.textWidth - 8 > width then + self.textWidth = width + self.textFrame:SetWidth(width) + if self.iconFrame and self.iconFrame:IsShown() then + self.frame:SetWidth(width + self.iconFrame:GetWidth()) + else + self.frame:SetWidth(width) + end + if self.panel and self.panel:GetPluginSide(self) == "CENTER" then + self.panel:UpdateCenteredPosition() + end + end + end +end + +function FuBarPlugin:SetText(text) + if not self.textFrame then + return + end + if self.hasNoText then + FuBarPlugin:error("Cannot set text if self.hasNoText has been set. (" .. self:GetTitle() .. ")") + end + FuBarPlugin:argCheck(text, 2, "string", "number") + if text == "" then + if self.hasIcon then + self:ShowIcon() + else + text = self:GetTitle() + end + end + if not self:IsTextColored() then + text = text:gsub("|c%x%x%x%x%x%x%x%x", ""):gsub("|r", "") + end + self.textFrame:SetText(text) + self:CheckWidth() +end + +function FuBarPlugin:GetText() + if not self.textFrame then + FuBarPlugin:error("Cannot get text without a self.textFrame (" .. self:GetTitle() .. ")") + end + if not self.hasNoText then + return self.textFrame:GetText() or "" + end +end + +function FuBarPlugin:IsIconShown() + if not self.hasIcon then + return false + elseif self.hasNoText then + return true + elseif not self.db then + return true + elseif self.db and self.db.profile.showIcon == nil then + return true + else + return (self.db and (self.db.profile.showIcon == 1 or self.db.profile.showIcon == true)) and true or false + end +end + +function FuBarPlugin:ToggleIconShown() + if not self.iconFrame then + FuBarPlugin:error("Cannot toggle icon without a self.iconFrame (" .. self:GetTitle() .. ")") + end + if not self.hasIcon then + FuBarPlugin:error("Cannot show icon unless self.hasIcon is set. (" .. self:GetTitle() .. ")") + end + if self.hasNoText then + FuBarPlugin:error("Cannot hide icon if self.hasNoText is set. (" .. self:GetTitle() .. ")") + end + if not self.textFrame then + FuBarPlugin:error("Cannot hide icon if self.textFrame is not set. (" .. self:GetTitle() .. ")") + end + if not self.iconFrame then + FuBarPlugin:error("Cannot hide icon if self.iconFrame is not set. (" .. self:GetTitle() .. ")") + end + if not self.db then + FuBarPlugin:error("Cannot hide icon if self.db is not available. (" .. self:GetTitle() .. ")") + end + local value = not self:IsIconShown() + self.db.profile.showIcon = value + if value then + if not self:IsTextShown() and self.textFrame:IsShown() and self.textFrame:GetText() == self:GetTitle() then + self.textFrame:Hide() + self.textFrame:SetText("") + end + self.iconFrame:Show() + self.iconFrame:SetWidth(self.iconFrame:GetHeight()) + else + if not self.textFrame:IsShown() or not self.textFrame:GetText() then + self.textFrame:Show() + self.textFrame:SetText(self:GetTitle()) + end + self.iconFrame:Hide() + self.iconFrame:SetWidth(epsilon) + end + self:CheckWidth(true) + return value +end + +function FuBarPlugin:ShowIcon() + if not self:IsIconShown() then + self:ToggleIconShown() + end +end + +function FuBarPlugin:HideIcon() + if self:IsIconShown() then + self:ToggleIconShown() + end +end + +function FuBarPlugin:IsTextShown() + if self.hasNoText then + return false + elseif not self.hasIcon then + return true + elseif not self.db then + return true + elseif self.db and self.db.profile.showText == nil then + return true + else + return (self.db and (self.db.profile.showText == 1 or self.db.profile.showText == true)) and true or false + end +end + +function FuBarPlugin:ToggleTextShown() + if self.cannotHideText then + FuBarPlugin:error("Cannot hide text unless self.cannotHideText is unset. (" .. self:GetTitle() .. ")") + end + if not self.hasIcon then + FuBarPlugin:error("Cannot show text unless self.hasIcon is set. (" .. self:GetTitle() .. ")") + end + if self.hasNoText then + FuBarPlugin:error("Cannot hide text if self.hasNoText is set. (" .. self:GetTitle() .. ")") + end + if not self.textFrame then + FuBarPlugin:error("Cannot hide text if self.textFrame is not set. (" .. self:GetTitle() .. ")") + end + if not self.iconFrame then + FuBarPlugin:error("Cannot hide text if self.iconFrame is not set. (" .. self:GetTitle() .. ")") + end + if not self.db then + FuBarPlugin:error("Cannot hide text if self.db is not available. (" .. self:GetTitle() .. ")") + end + local value = not self:IsTextShown() + self.db.profile.showText = value + if value then + self.textFrame:Show() + self:UpdateText() + else + self.textFrame:SetText("") + self.textFrame:SetWidth(epsilon) + self.textFrame:Hide() + if not self:IsIconShown() then + DropDownList1:Hide() + end + self:ShowIcon() + end + self:CheckWidth(true) + return value +end + +function FuBarPlugin:ShowText() + if not self:IsTextShown() then + self:ToggleTextShown() + end +end + +function FuBarPlugin:HideText() + if self:IsTextShown() then + self:ToggleTextShown() + end +end + +function FuBarPlugin:IsTooltipDetached() + if self.blizzardTooltip or self.overrideTooltip or not Tablet then return end + + FuBarPlugin.RegisterTablet(self) + return not Tablet:IsAttached(self.frame) +end + +function FuBarPlugin:ToggleTooltipDetached() + if self.blizzardTooltip or self.overrideTooltip or not Tablet then return end + + FuBarPlugin.RegisterTablet(self) + if self:IsTooltipDetached() then + Tablet:Attach(self.frame) + else + Tablet:Detach(self.frame) + end + if Dewdrop then Dewdrop:Close() end +end + +function FuBarPlugin:DetachTooltip() + if self.blizzardTooltip or self.overrideTooltip or not Tablet then return end + + FuBarPlugin.RegisterTablet(self) + Tablet:Detach(self.frame) +end + +function FuBarPlugin:ReattachTooltip() + if self.blizzardTooltip or self.overrideTooltip or not Tablet then return end + + FuBarPlugin.RegisterTablet(self) + Tablet:Attach(self.frame) +end + +function FuBarPlugin:GetDefaultPosition() + return self.defaultPosition or "LEFT" +end + +local function IsCorrectPanel(panel) + if type(panel) ~= "table" then + return false + elseif type(panel.AddPlugin) ~= "function" then + return false + elseif type(panel.RemovePlugin) ~= "function" then + return false + elseif type(panel.GetNumPlugins) ~= "function" then + return false + elseif type(panel:GetNumPlugins()) ~= "number" then + return false + elseif type(panel.GetPlugin) ~= "function" then + return false + elseif type(panel.HasPlugin) ~= "function" then + return false + elseif type(panel.GetPluginSide) ~= "function" then + return false + end + return true +end + +function FuBarPlugin:SetPanel(panel) + if panel and not IsCorrectPanel(panel) then + FuBarPlugin:error("Bad argument #2 to `SetPanel'. Panel does not have the correct API.") + end + self.panel = panel +end + +function FuBarPlugin:SetFontSize(size) + if self.userDefinedFrame then + FuBarPlugin:error((self.name and self.name .. ": " or "") .. "You must provide a SetFontSize(size) method if you provide your own frame.") + end + if self.hasIcon then + if not self.iconFrame then + FuBarPlugin:error((self.name and self.name .. ": " or "") .. "No iconFrame found") + end + self.iconFrame:SetWidth(size + 3) + self.iconFrame:SetHeight(size + 3) + end + if not self.hasNoText then + if not self.textFrame then + FuBarPlugin:error((self.name and self.name .. ": " or "") .. "No textFrame found") + end + local font, _, flags = self.textFrame:GetFont() + self.textFrame:SetFont(font, size, flags) + end + self:CheckWidth() +end + +function FuBarPlugin:IsLoadOnDemand() + local addon = FuBarPlugin.folderNames[self] or self.folderName + if not addon then + return + end + return IsAddOnLoadOnDemand(addon) +end + +function FuBarPlugin:IsDisabled() + return self.IsActive and not self:IsActive() or false +end + +function FuBarPlugin:OnInstanceInit(target) + if not AceEvent then + self:error(MAJOR_VERSION .. " requires AceEvent-2.0.") + elseif not Dewdrop then + self:error(MAJOR_VERSION .. " requires Dewdrop-2.0.") + end + self.registry[target] = true + + local folderName + for i = 6, 3, -1 do + folderName = debugstack(i, 1, 0):match("\\AddOns\\(.*)\\") + if folderName then + break + end + end + target.folderName = folderName + self.folderNames[target] = folderName +end +FuBarPlugin.OnManualEmbed = FuBarPlugin.OnInstanceInit + +local frame_OnClick, frame_OnDoubleClick, frame_OnMouseDown, frame_OnMouseUp, frame_OnReceiveDrag, frame_OnEnter, frame_OnLeave +function FuBarPlugin:CreateBasicPluginFrame(name) + local frame = CreateFrame("Button", name, UIParent) + frame:SetFrameStrata("HIGH") + frame:SetFrameLevel(7) + frame:EnableMouse(true) + frame:EnableMouseWheel(true) + frame:SetMovable(true) + frame:SetWidth(150) + frame:SetHeight(24) + frame:SetPoint("CENTER", UIParent, "CENTER") + frame.self = self + if not frame_OnEnter then + function frame_OnEnter(this, ...) + local self = this.self + if self.blizzardTooltip then + GameTooltip:SetOwner(self:IsMinimapAttached() and self.minimapFrame or self.frame, "ANCHOR_CURSOR") + self:UpdateTooltip() + end + if type(self.OnEnter) == "function" then + self:OnEnter(...) + end + end + end + frame:SetScript("OnEnter", frame_OnEnter) + if not frame_OnLeave then + function frame_OnLeave(this, ...) + local self = this.self + if type(self.OnLeave) == "function" then + self:OnLeave(...) + end + if self.blizzardTooltip and GameTooltip:IsOwned(self:IsMinimapAttached() and self.minimapFrame or self.frame) then + GameTooltip:Hide() + end + end + end + frame:SetScript("OnLeave", frame_OnLeave) + if not frame_OnClick then + function frame_OnClick(this, ...) + if this.self:IsMinimapAttached() and this.dragged then return end + if type(this.self.OnClick) == "function" then + this.self:OnClick(...) + end + end + end + frame:SetScript("OnClick", frame_OnClick) + if not frame_OnDoubleClick then + function frame_OnDoubleClick(this, ...) + if type(this.self.OnDoubleClick) == "function" then + this.self:OnDoubleClick(...) + end + end + end + frame:SetScript("OnDoubleClick", frame_OnDoubleClick) + if not frame_OnMouseDown then + function frame_OnMouseDown(this, ...) + if (...) == "RightButton" and not IsModifierKeyDown() then + this.self:OpenMenu() + return + else + HideDropDownMenu(1) + if type(this.self.OnMouseDown) == "function" then + this.self:OnMouseDown(...) + end + end + end + end + frame:SetScript("OnMouseDown", frame_OnMouseDown) + if not frame_OnMouseUp then + function frame_OnMouseUp(this, ...) + if type(this.self.OnMouseUp) == "function" then + this.self:OnMouseUp(...) + end + end + end + frame:SetScript("OnMouseUp", frame_OnMouseUp) + if not frame_OnReceiveDrag then + function frame_OnReceiveDrag(this, ...) + if (this.self:IsMinimapAttached() and not this.dragged) and type(this.self.OnReceiveDrag) == "function" then + this.self:OnReceiveDrag(...) + end + end + end + frame:SetScript("OnReceiveDrag", frame_OnReceiveDrag) + return frame +end + +local child_OnEnter, child_OnLeave, child_OnClick, child_OnDoubleClick, child_OnMouseDown, child_OnMouseUp, child_OnReceiveDrag +function FuBarPlugin:CreatePluginChildFrame(frameType, name, parent) + if not self.frame then + FuBarPlugin:error((self.name and self.name .. ": " or "") .. "You must have self.frame declared in order to add child frames") + end + FuBarPlugin:argCheck(frameType, 1, "string") + local child = CreateFrame(frameType, name, parent) + if parent then + child:SetFrameLevel(parent:GetFrameLevel() + 2) + end + child.self = self + if not child_OnEnter then + function child_OnEnter(this, ...) + if this.self.frame:GetScript("OnEnter") then + this.self.frame:GetScript("OnEnter")(this, ...) + end + end + end + child:SetScript("OnEnter", child_OnEnter) + if not child_OnLeave then + function child_OnLeave(this, ...) + if this.self.frame:GetScript("OnLeave") then + this.self.frame:GetScript("OnLeave")(this, ...) + end + end + end + child:SetScript("OnLeave", child_OnLeave) + if child:HasScript("OnClick") then + if not child_OnClick then + function child_OnClick(this, ...) + if this.self.frame:HasScript("OnClick") and this.self.frame:GetScript("OnClick") then + this.self.frame:GetScript("OnClick")(this, ...) + end + end + end + child:SetScript("OnClick", child_OnClick) + end + if child:HasScript("OnDoubleClick") then + if not child_OnDoubleClick then + function child_OnDoubleClick(this, ...) + if this.self.frame:HasScript("OnDoubleClick") and this.self.frame:GetScript("OnDoubleClick") then + this.self.frame:GetScript("OnDoubleClick")(this, ...) + end + end + end + child:SetScript("OnDoubleClick", child_OnDoubleClick) + end + if not child_OnMouseDown then + function child_OnMouseDown(this, ...) + if this.self.frame:HasScript("OnMouseDown") and this.self.frame:GetScript("OnMouseDown") then + this.self.frame:GetScript("OnMouseDown")(this, ...) + end + end + end + child:SetScript("OnMouseDown", child_OnMouseDown) + if not child_OnMouseUp then + function child_OnMouseUp(this, ...) + if this.self.frame:HasScript("OnMouseUp") and this.self.frame:GetScript("OnMouseUp") then + this.self.frame:GetScript("OnMouseUp")(this, ...) + end + end + end + child:SetScript("OnMouseUp", child_OnMouseUp) + if not child_OnReceiveDrag then + function child_OnReceiveDrag(this, ...) + if this.self.frame:HasScript("OnReceiveDrag") and this.self.frame:GetScript("OnReceiveDrag") then + this.self.frame:GetScript("OnReceiveDrag")(this, ...) + end + end + end + child:SetScript("OnReceiveDrag", child_OnReceiveDrag) + return child +end + +function FuBarPlugin:OpenMenu(frame) + if not frame then + frame = self:IsMinimapAttached() and self.minimapFrame or self.frame + end + if not frame:IsVisible() then + frame = UIParent + end + if not frame or not self:GetFrame() or Dewdrop:IsOpen(frame) then + Dewdrop:Close() + return + end + if self.blizzardTooltip then + if GameTooltip:IsOwned(frame) then + GameTooltip:Hide() + end + elseif self.overrideTooltip and type(self.CloseTooltip) == "function" then + self:CloseTooltip() + elseif not self.overrideTooltip and Tablet then + Tablet:Close() + end + + if not Dewdrop:IsRegistered(self:GetFrame()) then + if type(self.OnMenuRequest) == "table" and (not self.OnMenuRequest.handler or self.OnMenuRequest.handler == self) and self.OnMenuRequest.type == "group" then + Dewdrop:InjectAceOptionsTable(self, self.OnMenuRequest) + if self.OnMenuRequest.args and CheckFuBar() and not self.independentProfile then + self.OnMenuRequest.args.profile = nil + end + end + Dewdrop:Register(self:GetFrame(), + 'children', type(self.OnMenuRequest) == "table" and self.OnMenuRequest or function(level, value, valueN_1, valueN_2, valueN_3, valueN_4) + if level == 1 then + if not self.hideMenuTitle then + Dewdrop:AddLine( + 'text', self:GetTitle(), + 'isTitle', true + ) + end + + if self.OnMenuRequest then + self:OnMenuRequest(level, value, false, valueN_1, valueN_2, valueN_3, valueN_4) + end + + if not self.overrideMenu then + if self.MenuSettings and not self.hideMenuTitle then + Dewdrop:AddLine() + end + self:AddImpliedMenuOptions() + end + else + if not self.overrideMenu and self:AddImpliedMenuOptions() then + else + if self.OnMenuRequest then + self:OnMenuRequest(level, value, false, valueN_1, valueN_2, valueN_3, valueN_4) + end + end + end + if level == 1 then + Dewdrop:AddLine( + 'text', CLOSE, + 'tooltipTitle', CLOSE, + 'tooltipText', CLOSE_DESC, + 'func', Dewdrop.Close, + 'arg1', Dewdrop + ) + end + end, + 'point', function(frame) + local x, y = frame:GetCenter() + local leftRight + if x < GetScreenWidth() / 2 then + leftRight = "LEFT" + else + leftRight = "RIGHT" + end + if y < GetScreenHeight() / 2 then + return "BOTTOM" .. leftRight, "TOP" .. leftRight + else + return "TOP" .. leftRight, "BOTTOM" .. leftRight + end + end, + 'dontHook', true + ) + end + if frame == self:GetFrame() then + Dewdrop:Open(self:GetFrame()) + elseif frame ~= UIParent then + Dewdrop:Open(frame, self:GetFrame()) + else + Dewdrop:Open(frame, self:GetFrame(), 'cursorX', true, 'cursorY', true) + end +end + +local impliedMenuOptions +function FuBarPlugin:AddImpliedMenuOptions(level) + FuBarPlugin:argCheck(level, 2, "number", "nil") + if not impliedMenuOptions then + impliedMenuOptions = {} + end + if not impliedMenuOptions[self] then + impliedMenuOptions[self] = { type = 'group', args = {} } + Dewdrop:InjectAceOptionsTable(self, impliedMenuOptions[self]) + if impliedMenuOptions[self].args and CheckFuBar() and not self.independentProfile then + impliedMenuOptions[self].args.profile = nil + end + end + return Dewdrop:FeedAceOptionsTable(impliedMenuOptions[self], level and level - 1) +end + +function FuBarPlugin.OnEmbedInitialize(FuBarPlugin, self) + if not self.frame then + local name = "FuBarPlugin" .. self:GetTitle() .. "Frame" + local frame = _G[name] + if not frame or not _G[name .. "Text"] or not _G[name .. "Icon"] then + frame = self:CreateBasicPluginFrame(name) + + local icon = frame:CreateTexture(name .. "Icon", "ARTWORK") + icon:SetWidth(16) + icon:SetHeight(16) + icon:SetPoint("LEFT", frame, "LEFT") + + local text = frame:CreateFontString(name .. "Text", "ARTWORK") + text:SetWidth(134) + text:SetHeight(24) + text:SetPoint("LEFT", icon, "RIGHT", 0, 1) + text:SetFontObject(GameFontNormal) + end + self.frame = frame + self.textFrame = _G[name .. "Text"] + self.iconFrame = _G[name .. "Icon"] + else + self.userDefinedFrame = true + end + + self.frame.plugin = self + self.frame:SetParent(UIParent) + self.frame:SetPoint("RIGHT", UIParent, "LEFT", -5, 0) + self.frame:Hide() + + if self.hasIcon then + self:SetIcon(self.hasIcon) + end + + if CheckFuBar() then + FuBar:RegisterPlugin(self) + end +end + +local CheckShow = function(self, panelId) + if not self.frame:IsShown() and (not self.minimapFrame or not self.minimapFrame:IsShown()) then + self:Show(panelId) + Dewdrop:Refresh(2) + end +end + +local recheckPlugins +local AceConsole +function FuBarPlugin.OnEmbedEnable(FuBarPlugin, self) + if not self.userDefinedFrame then + if self:IsIconShown() then + self.iconFrame:Show() + else + self.iconFrame:Hide() + end + end + self:CheckWidth(true) + + if not self.hideWithoutStandby or (self.db and not self.db.profile.hidden) then + if FuBarPlugin.enabledPlugins[self] then + CheckShow(self, self.panelIdTmp) + else + FuBarPlugin:ScheduleEvent("FuBarPlugin-CheckShow-" .. tostring(self), CheckShow, 0, self, self.panelIdTmp) + end + end + FuBarPlugin.enabledPlugins[self] = true + + if not self.blizzardTooltip and not self.overrideTooltip and not self.cannotDetachTooltip and self.db and self.db.profile.detachedTooltip and self.db.profile.detachedTooltip.detached then + FuBarPlugin:ScheduleEvent("FuBarPlugin-DetachTooltip-" .. tostring(self), self.DetachTooltip, 0, self) + end + + if self:IsLoadOnDemand() and CheckFuBar() then + if not FuBar.db.profile.loadOnDemand then + FuBar.db.profile.loadOnDemand = {} + end + if not FuBar.db.profile.loadOnDemand[FuBarPlugin.folderNames[self] or self.folderName] then + FuBar.db.profile.loadOnDemand[FuBarPlugin.folderNames[self] or self.folderName] = {} + end + FuBar.db.profile.loadOnDemand[FuBarPlugin.folderNames[self] or self.folderName].disabled = nil + end + + if CheckFuBar() and AceLibrary:HasInstance("AceConsole-2.0") then + if not recheckPlugins then + if not AceConsole then + AceConsole = AceLibrary("AceConsole-2.0") + end + recheckPlugins = function() + for k,v in pairs(AceConsole.registry) do + if type(v) == "table" and v.args and AceOO.inherits(v.handler, FuBarPlugin) and not v.handler.independentProfile then + v.args.profile = nil + end + end + end + end + FuBarPlugin:ScheduleEvent("FuBarPlugin-recheckPlugins", recheckPlugins, 0) + end +end + +function FuBarPlugin.OnEmbedDisable(FuBarPlugin, self) + self:Hide(false) + + if self:IsLoadOnDemand() and CheckFuBar() then + if not FuBar.db.profile.loadOnDemand then + FuBar.db.profile.loadOnDemand = {} + end + if not FuBar.db.profile.loadOnDemand[FuBarPlugin.folderNames[self] or self.folderName] then + FuBar.db.profile.loadOnDemand[FuBarPlugin.folderNames[self] or self.folderName] = {} + end + FuBar.db.profile.loadOnDemand[FuBarPlugin.folderNames[self] or self.folderName].disabled = true + end +end + +function FuBarPlugin.OnEmbedProfileEnable(FuBarPlugin, self) + self:Update() + if self.db and self.db.profile then + if not self.db.profile.detachedTooltip then + self.db.profile.detachedTooltip = {} + end + if not self.blizzardTooltip and not self.overrideTooltip and Tablet then + if Tablet.registry[self.frame] then + Tablet:UpdateDetachedData(self.frame, self.db.profile.detachedTooltip) + else + FuBarPlugin.RegisterTablet(self) + end + end + if MinimapContainer:HasPlugin(self) then + MinimapContainer:ReadjustLocation(self) + end + end +end + +function FuBarPlugin.GetAceOptionsDataTable(FuBarPlugin, self) + return { + icon = { + type = "toggle", + name = SHOW_ICON, + desc = SHOW_ICON_DESC, + set = "ToggleIconShown", + get = "IsIconShown", + hidden = function() + return not self.hasIcon or self.hasNoText or self:IsDisabled() or self:IsMinimapAttached() or not self.db + end, + order = -13.7, + handler = self, + }, + text = { + type = "toggle", + name = SHOW_TEXT, + desc = SHOW_TEXT_DESC, + set = "ToggleTextShown", + get = "IsTextShown", + hidden = function() + return self.cannotHideText or not self.hasIcon or self.hasNoText or self:IsDisabled() or self:IsMinimapAttached() or not self.db + end, + order = -13.6, + handler = self, + }, + colorText = { + type = "toggle", + name = SHOW_COLORED_TEXT, + desc = SHOW_COLORED_TEXT_DESC, + set = "ToggleTextColored", + get = "IsTextColored", + hidden = function() + return self.userDefinedFrame or self.hasNoText or self.hasNoColor or self:IsDisabled() or self:IsMinimapAttached() or not self.db + end, + order = -13.5, + handler = self, + }, + detachTooltip = { + type = "toggle", + name = DETACH_TOOLTIP, + desc = DETACH_TOOLTIP_DESC, + get = "IsTooltipDetached", + set = "ToggleTooltipDetached", + hidden = function() + return not Tablet or self.blizzardTooltip or self.overrideTooltip or self.cannotDetachTooltip or self:IsDisabled() + end, + order = -13.4, + handler = self, + }, + lockTooltip = { + type = "toggle", + name = LOCK_TOOLTIP, + desc = LOCK_TOOLTIP_DESC, + get = function() + return Tablet:IsLocked(self.frame) + end, + set = function() + return Tablet:ToggleLocked(self.frame) + end, + disabled = function() + return not self:IsTooltipDetached() + end, + hidden = function() + return not Tablet or self.blizzardTooltip or self.overrideTooltip or self.cannotDetachTooltip or self:IsDisabled() + end, + order = -13.3, + handler = self, + }, + position = { + type = "text", + name = POSITION, + desc = POSITION_DESC, + validate = { + LEFT = POSITION_LEFT, + CENTER = POSITION_CENTER, + RIGHT = POSITION_RIGHT + }, + get = function() + return self.panel and self.panel:GetPluginSide(self) + end, + set = function(value) + if self.panel then + self.panel:SetPluginSide(self, value) + end + end, + hidden = function() + return self:IsMinimapAttached() or self:IsDisabled() or not self.panel + end, + order = -13.2, + handler = self, + }, + minimapAttach = { + type = "toggle", + name = ATTACH_TO_MINIMAP, + desc = ATTACH_TO_MINIMAP_DESC, + get = "IsMinimapAttached", + set = "ToggleMinimapAttached", + hidden = function() + return (self.cannotAttachToMinimap and not self:IsMinimapAttached()) or not CheckFuBar() or self:IsDisabled() + end, + order = -13.1, + handler = self, + }, + hide = { + type = "toggle", + cmdName = HIDE_FUBAR_PLUGIN_CMD, + guiName = HIDE_FUBAR_PLUGIN, + desc = HIDE_FUBAR_PLUGIN_DESC, + get = function() + return not self.frame:IsShown() and (not self.minimapFrame or not self.minimapFrame:IsShown()) + end, + set = function() + if not self.frame:IsShown() and (not self.minimapFrame or not self.minimapFrame:IsShown()) then + self:Show() + else + self:Hide() + end + end, + hidden = function() + return not self.hideWithoutStandby or self:IsDisabled() + end, + order = -13, + handler = self, + }, + } +end + +local function activate(self, oldLib, oldDeactivate) + FuBarPlugin = self + + if oldLib then + self.registry = oldLib.registry + self.folderNames = oldLib.folderNames + self.enabledPlugins = oldLib.enabledPlugins + end + + if not self.registry then + self.registry = {} + end + if not self.folderNames then + self.folderNames = {} + end + if not self.enabledPlugins then + self.enabledPlugins = {} + end + + FuBarPlugin.activate(self, oldLib, oldDeactivate) + + if oldDeactivate then + oldDeactivate(oldLib) + end +end + +local function external(self, major, instance) + if major == "AceEvent-2.0" then + AceEvent = instance + AceEvent:embed(self) + elseif major == "Tablet-2.0" then + Tablet = instance + elseif major == "Dewdrop-2.0" then + Dewdrop = instance + end +end + +AceLibrary:Register(FuBarPlugin, MAJOR_VERSION, MINOR_VERSION, activate, nil, external) + +MinimapContainer = {} + +local minimap_OnMouseDown, minimap_OnMouseUp +function MinimapContainer:AddPlugin(plugin) + if CheckFuBar() and FuBar:IsChangingProfile() then + return + end + if plugin.panel ~= nil then + plugin.panel:RemovePlugin(plugin) + end + plugin.panel = self + if not plugin.minimapFrame then + local frame = CreateFrame("Button", plugin.frame:GetName() .. "MinimapButton", Minimap) + plugin.minimapFrame = frame + frame.plugin = plugin + frame:SetWidth(31) + frame:SetHeight(31) + frame:SetFrameStrata("BACKGROUND") + frame:SetFrameLevel(4) + frame:SetHighlightTexture("Interface\\Minimap\\UI-Minimap-ZoomButton-Highlight") + local icon = frame:CreateTexture(frame:GetName() .. "Icon", "BACKGROUND") + plugin.minimapIcon = icon + local path = plugin:GetIcon() or (plugin.iconFrame and plugin.iconFrame:GetTexture()) or "Interface\\Icons\\INV_Misc_QuestionMark" + icon:SetTexture(path) + if path:sub(1, 16) == "Interface\\Icons\\" then + icon:SetTexCoord(0.05, 0.95, 0.05, 0.95) + else + icon:SetTexCoord(0, 1, 0, 1) + end + icon:SetWidth(20) + icon:SetHeight(20) + icon:SetPoint("TOPLEFT", frame, "TOPLEFT", 7, -5) + local overlay = frame:CreateTexture(frame:GetName() .. "Overlay","OVERLAY") + overlay:SetTexture("Interface\\Minimap\\MiniMap-TrackingBorder") + overlay:SetWidth(53) + overlay:SetHeight(53) + overlay:SetPoint("TOPLEFT",frame,"TOPLEFT") + frame:EnableMouse(true) + frame:RegisterForClicks("LeftButtonUp") + + frame.self = plugin + if not frame_OnEnter then + function frame_OnEnter(this, ...) + if type(this.self.OnEnter) == "function" then + this.self:OnEnter(...) + end + end + end + frame:SetScript("OnEnter", frame_OnEnter) + if not frame_OnLeave then + function frame_OnLeave(this, ...) + if type(this.self.OnLeave) == "function" then + this.self:OnLeave(...) + end + end + end + frame:SetScript("OnLeave", frame_OnLeave) + if not frame_OnClick then + function frame_OnClick(this, ...) + if this.self:IsMinimapAttached() and this.dragged then return end + if type(this.self.OnClick) == "function" then + this.self:OnClick(...) + end + end + end + frame:SetScript("OnClick", frame_OnClick) + if not frame_OnDoubleClick then + function frame_OnDoubleClick(this, ...) + if type(this.self.OnDoubleClick) == "function" then + this.self:OnDoubleClick(...) + end + end + end + frame:SetScript("OnDoubleClick", frame_OnDoubleClick) + if not frame_OnReceiveDrag then + function frame_OnReceiveDrag(this, ...) + if (this.self:IsMinimapAttached() and not this.dragged) and type(this.self.OnReceiveDrag) == "function" then + this.self:OnReceiveDrag(...) + end + end + end + frame:SetScript("OnReceiveDrag", frame_OnReceiveDrag) + if not minimap_OnMouseDown then + function minimap_OnMouseDown(this, ...) + this.dragged = false + if (...) == "RightButton" and not IsModifierKeyDown() then + this.self:OpenMenu(this) + else + HideDropDownMenu(1) + if type(this.self.OnMouseDown) == "function" then + this.self:OnMouseDown(...) + end + end + if this.self.OnClick or this.self.OnMouseDown or this.self.OnMouseUp or this.self.OnDoubleClick then + if this.self.minimapIcon:GetTexture():sub(1, 16) == "Interface\\Icons\\" then + this.self.minimapIcon:SetTexCoord(0.14, 0.86, 0.14, 0.86) + else + this.self.minimapIcon:SetTexCoord(0.1, 0.9, 0.1, 0.9) + end + end + end + end + frame:SetScript("OnMouseDown", minimap_OnMouseDown) + if not minimap_OnMouseUp then + function minimap_OnMouseUp(this, ...) + if not this.dragged and type(this.self.OnMouseUp) == "function" then + this.self:OnMouseUp(...) + end + if this.self.minimapIcon:GetTexture():sub(1, 16) == "Interface\\Icons\\" then + this.self.minimapIcon:SetTexCoord(0.05, 0.95, 0.05, 0.95) + else + this.self.minimapIcon:SetTexCoord(0, 1, 0, 1) + end + end + end + frame:SetScript("OnMouseUp", minimap_OnMouseUp) + frame:RegisterForDrag("LeftButton") + frame:SetScript("OnDragStart", self.OnDragStart) + frame:SetScript("OnDragStop", self.OnDragStop) + + if not plugin.blizzardTooltip and not plugin.overrideTooltip and Tablet then + -- Note that we have to do this after :SetScript("OnEnter"), etc, + -- so that Tablet-2.0 can override it properly. + FuBarPlugin.RegisterTablet(plugin) + Tablet:Register(frame, plugin.frame) + end + end + plugin.frame:Hide() + plugin.minimapFrame:Show() + self:ReadjustLocation(plugin) + table.insert(self.plugins, plugin) + local exists = false + return true +end + +function MinimapContainer:RemovePlugin(index) + if CheckFuBar() and FuBar:IsChangingProfile() then + return + end + if type(index) == "table" then + index = self:IndexOfPlugin(index) + if not index then + return + end + end + local t = self.plugins + local plugin = t[index] + assert(plugin.panel == self, "Plugin has improper panel field") + plugin:SetPanel(nil) + table.remove(t, index) + return true +end + +function MinimapContainer:ReadjustLocation(plugin) + local frame = plugin.minimapFrame + if plugin.db and plugin.db.profile.minimapPositionWild then + frame:SetPoint("CENTER", UIParent, "BOTTOMLEFT", plugin.db.profile.minimapPositionX, plugin.db.profile.minimapPositionY) + elseif not plugin.db and plugin.minimapPositionWild then + frame:SetPoint("CENTER", UIParent, "BOTTOMLEFT", plugin.minimapPositionX, plugin.minimapPositionY) + else + local position + if plugin.db then + position = plugin.db.profile.minimapPosition or plugin.defaultMinimapPosition or math.random(1, 360) + else + position = plugin.minimapPosition or plugin.defaultMinimapPosition or math.random(1, 360) + end + local angle = math.rad(position or 0) + local x,y + local minimapShape = GetMinimapShape and GetMinimapShape() or "ROUND" + local cos = math.cos(angle) + local sin = math.sin(angle) + + local round = true + if minimapShape == "ROUND" then + -- do nothing + elseif minimapShape == "SQUARE" then + round = false + elseif minimapShape == "CORNER-TOPRIGHT" then + if cos < 0 or sin < 0 then + round = false + end + elseif minimapShape == "CORNER-TOPLEFT" then + if cos > 0 or sin < 0 then + round = false + end + elseif minimapShape == "CORNER-BOTTOMRIGHT" then + if cos < 0 or sin > 0 then + round = false + end + elseif minimapShape == "CORNER-BOTTOMLEFT" then + if cos > 0 or sin > 0 then + round = false + end + elseif minimapShape == "SIDE-LEFT" then + if cos > 0 then + round = false + end + elseif minimapShape == "SIDE-RIGHT" then + if cos < 0 then + round = false + end + elseif minimapShape == "SIDE-TOP" then + if sin < 0 then + round = false + end + elseif minimapShape == "SIDE-BOTTOM" then + if sin > 0 then + round = false + end + elseif minimapShape == "TRICORNER-TOPRIGHT" then + if cos < 0 and sin < 0 then + round = false + end + elseif minimapShape == "TRICORNER-TOPLEFT" then + if cos > 0 and sin < 0 then + round = false + end + elseif minimapShape == "TRICORNER-BOTTOMRIGHT" then + if cos < 0 and sin > 0 then + round = false + end + elseif minimapShape == "TRICORNER-BOTTOMLEFT" then + if cos > 0 and sin > 0 then + round = false + end + end + + if round then + x = cos * 80 + y = sin * 80 + else + x = 80 * 2^0.5 * cos + y = 80 * 2^0.5 * sin + if x < -80 then + x = -80 + elseif x > 80 then + x = 80 + end + if y < -80 then + y = -80 + elseif y > 80 then + y = 80 + end + end + frame:SetPoint("CENTER", Minimap, "CENTER", x, y) + end +end + +function MinimapContainer:GetPlugin(index) + return self.plugins[index] +end + +function MinimapContainer:GetNumPlugins() + return #self.plugins +end + +function MinimapContainer:IndexOfPlugin(plugin) + for i,p in ipairs(self.plugins) do + if p == plugin then + return i, "MINIMAP" + end + end +end + +function MinimapContainer:HasPlugin(plugin) + return self:IndexOfPlugin(plugin) ~= nil +end + +function MinimapContainer:GetPluginSide(plugin) + local index = self:IndexOfPlugin(plugin) + assert(index, "Plugin not in panel") + return "MINIMAP" +end + +function MinimapContainer.OnDragStart(this) + this.dragged = true + this:LockHighlight() + this:SetScript("OnUpdate", MinimapContainer.OnUpdate) + if this.self.minimapIcon:GetTexture():sub(1, 16) == "Interface\\Icons\\" then + this.self.minimapIcon:SetTexCoord(0.05, 0.95, 0.05, 0.95) + else + this.self.minimapIcon:SetTexCoord(0, 1, 0, 1) + end +end + +function MinimapContainer.OnDragStop(this) + this:SetScript("OnUpdate", nil) + this:UnlockHighlight() +end + +function MinimapContainer.OnUpdate(this) + if not IsAltKeyDown() then + local mx, my = Minimap:GetCenter() + local px, py = GetCursorPosition() + local scale = UIParent:GetEffectiveScale() + px, py = px / scale, py / scale + local position = math.deg(math.atan2(py - my, px - mx)) + if position <= 0 then + position = position + 360 + elseif position > 360 then + position = position - 360 + end + if this.self.db then + this.self.db.profile.minimapPosition = position + this.self.db.profile.minimapPositionX = nil + this.self.db.profile.minimapPositionY = nil + this.self.db.profile.minimapPositionWild = nil + else + this.self.minimapPosition = position + this.self.minimapPositionX = nil + this.self.minimapPositionY = nil + this.self.minimapPositionWild = nil + end + else + local px, py = GetCursorPosition() + local scale = UIParent:GetEffectiveScale() + px, py = px / scale, py / scale + if this.self.db then + this.self.db.profile.minimapPositionX = px + this.self.db.profile.minimapPositionY = py + this.self.db.profile.minimapPosition = nil + this.self.db.profile.minimapPositionWild = true + else + this.self.minimapPositionX = px + this.self.minimapPositionY = py + this.self.minimapPosition = nil + this.self.minimapPositionWild = true + end + end + MinimapContainer:ReadjustLocation(this.self) +end + +local function activate(self, oldLib, oldDeactivate) + MinimapContainer = self + + if oldLib then + self.plugins = oldLib.plugins + end + + if not self.plugins then + self.plugins = {} + end + + if oldDeactivate then + oldDeactivate(oldLib) + end +end + +AceLibrary:Register(MinimapContainer, MINIMAPCONTAINER_MAJOR_VERSION, MINOR_VERSION, activate) diff --git a/AtlasLootFu/Libs/Tablet-2.0/AceLibrary/AceLibrary.lua b/AtlasLootFu/Libs/Tablet-2.0/AceLibrary/AceLibrary.lua new file mode 100644 index 0000000..8ff1742 --- /dev/null +++ b/AtlasLootFu/Libs/Tablet-2.0/AceLibrary/AceLibrary.lua @@ -0,0 +1,799 @@ +--[[ +Name: AceLibrary +Revision: $Rev: 1091 $ +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: 1091 $ +Website: http://www.wowace.com/ +Documentation: http://www.wowace.com/index.php/AceLibrary +SVN: http://svn.wowace.com/wowace/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 +License: LGPL v2.1 +]] + +local ACELIBRARY_MAJOR = "AceLibrary" +local ACELIBRARY_MINOR = 90000 + tonumber(("$Revision: 1091 $"):match("(%d+)")) + +local _G = getfenv(0) +local previous = _G[ACELIBRARY_MAJOR] +if previous and not previous:IsNewVersion(ACELIBRARY_MAJOR, ACELIBRARY_MINOR) then return end + +do + -- LibStub is a simple versioning stub meant for use in Libraries. http://www.wowace.com/wiki/LibStub for more info + -- LibStub is hereby placed in the Public Domain -- Credits: Kaelten, Cladhaire, ckknight, Mikk, Ammo, Nevcairiel, joshborke + local LIBSTUB_MAJOR, LIBSTUB_MINOR = "LibStub", 2 -- NEVER MAKE THIS AN SVN REVISION! IT NEEDS TO BE USABLE IN ALL REPOS! + local LibStub = _G[LIBSTUB_MAJOR] + + if not LibStub or LibStub.minor < LIBSTUB_MINOR then + LibStub = LibStub or {libs = {}, minors = {} } + _G[LIBSTUB_MAJOR] = LibStub + LibStub.minor = LIBSTUB_MINOR + + function LibStub:NewLibrary(major, minor) + assert(type(major) == "string", "Bad argument #2 to `NewLibrary' (string expected)") + minor = assert(tonumber(strmatch(minor, "%d+")), "Minor version must either be a number or contain a number.") + local oldminor = self.minors[major] + if oldminor and oldminor >= minor then return nil end + self.minors[major], self.libs[major] = minor, self.libs[major] or {} + return self.libs[major], oldminor + end + + function LibStub:GetLibrary(major, silent) + if not self.libs[major] and not silent then + error(("Cannot find a library instance of %q."):format(tostring(major)), 2) + end + return self.libs[major], self.minors[major] + end + + function LibStub:IterateLibraries() return pairs(self.libs) end + setmetatable(LibStub, { __call = LibStub.GetLibrary }) + end +end +local LibStub = _G.LibStub + +-- If you don't want AceLibrary to enable libraries that are LoadOnDemand but +-- disabled in the addon screen, set this to true. +local DONT_ENABLE_LIBRARIES = nil + +local function safecall(func,...) + local success, err = pcall(func,...) + if not success then geterrorhandler()(err:find("%.lua:%d+:") and err or (debugstack():match("\n(.-: )in.-\n") or "") .. err) end +end + +-- @table AceLibrary +-- @brief System to handle all versioning of libraries. +local AceLibrary = {} +local AceLibrary_mt = {} +setmetatable(AceLibrary, AceLibrary_mt) + +local function error(self, message, ...) + if type(self) ~= "table" then + return _G.error(("Bad argument #1 to `error' (table expected, got %s)"):format(type(self)), 2) + end + + local stack = debugstack() + if not message then + local second = stack:match("\n(.-)\n") + message = "error raised! " .. second + else + local arg = { ... } -- not worried about table creation, as errors don't happen often + + for i = 1, #arg do + arg[i] = tostring(arg[i]) + end + for i = 1, 10 do + table.insert(arg, "nil") + end + message = message:format(unpack(arg)) + end + + if getmetatable(self) and getmetatable(self).__tostring then + message = ("%s: %s"):format(tostring(self), message) + elseif type(rawget(self, 'GetLibraryVersion')) == "function" and AceLibrary:HasInstance(self:GetLibraryVersion()) then + message = ("%s: %s"):format(self:GetLibraryVersion(), message) + elseif type(rawget(self, 'class')) == "table" and type(rawget(self.class, 'GetLibraryVersion')) == "function" and AceLibrary:HasInstance(self.class:GetLibraryVersion()) then + message = ("%s: %s"):format(self.class:GetLibraryVersion(), message) + end + + local first = stack:gsub("\n.*", "") + local file = first:gsub(".*\\(.*).lua:%d+: .*", "%1") + file = file:gsub("([%(%)%.%*%+%-%[%]%?%^%$%%])", "%%%1") + + + local i = 0 + for s in stack:gmatch("\n([^\n]*)") do + i = i + 1 + if not s:find(file .. "%.lua:%d+:") and not s:find("%(tail call%)") then + file = s:gsub("^.*\\(.*).lua:%d+: .*", "%1") + file = file:gsub("([%(%)%.%*%+%-%[%]%?%^%$%%])", "%%%1") + break + end + end + local j = 0 + for s in stack:gmatch("\n([^\n]*)") do + j = j + 1 + if j > i and not s:find(file .. "%.lua:%d+:") and not s:find("%(tail call%)") then + return _G.error(message, j+1) + end + end + return _G.error(message, 2) +end + +local type = type +local function argCheck(self, arg, num, kind, kind2, kind3, kind4, kind5) + if type(num) ~= "number" then + return error(self, "Bad argument #3 to `argCheck' (number expected, got %s)", type(num)) + elseif type(kind) ~= "string" then + return error(self, "Bad argument #4 to `argCheck' (string expected, got %s)", type(kind)) + end + arg = type(arg) + if arg ~= kind and arg ~= kind2 and arg ~= kind3 and arg ~= kind4 and arg ~= kind5 then + local stack = debugstack() + local func = stack:match("`argCheck'.-([`<].-['>])") + if not func then + func = stack:match("([`<].-['>])") + end + if kind5 then + return 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 + return 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 + return 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 + return error(self, "Bad argument #%s to %s (%s or %s expected, got %s)", tonumber(num) or 0/0, func, kind, kind2, arg) + else + return error(self, "Bad argument #%s to %s (%s expected, got %s)", tonumber(num) or 0/0, func, kind, arg) + end + end +end + +local pcall +do + local function check(self, ret, ...) + if not ret then + local s = ... + return error(self, (s:gsub(".-%.lua:%d-: ", ""))) + else + return ... + end + end + + function pcall(self, func, ...) + return check(self, _G.pcall(func, ...)) + end +end + +local recurse = {} +local 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 + +local function svnRevisionToNumber(text) + local kind = type(text) + if kind == "number" or tonumber(text) then + return tonumber(text) + elseif kind == "string" then + if text:find("^%$Revision: (%d+) %$$") then + return tonumber((text:match("^%$Revision: (%d+) %$$"))) + elseif text:find("^%$Rev: (%d+) %$$") then + return tonumber((text:match("^%$Rev: (%d+) %$$"))) + elseif text:find("^%$LastChangedRevision: (%d+) %$$") then + return tonumber((text:match("^%$LastChangedRevision: (%d+) %$$"))) + end + end + return nil +end + +local crawlReplace +do + local recurse = {} + local function func(t, to, from) + if recurse[t] then + return + end + 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) + func(t, to, from) + for k in pairs(recurse) do + recurse[k] = nil + end + 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 +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 + +-- @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, to) + if not to then + to = {} + end + for k,v in pairs(from) do + to[k] = v + end + 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) + if not list then + list = {} + list2 = {} + 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 + 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) + end +end + +local function TryToEnable(addon) + if DONT_ENABLE_LIBRARIES then return end + local isondemand = IsAddOnLoadOnDemand(addon) + if isondemand then + local _, _, _, enabled = GetAddOnInfo(addon) + EnableAddOn(addon) + local _, _, _, _, loadable = GetAddOnInfo(addon) + if not loadable and not enabled then + DisableAddOn(addon) + end + + return loadable + 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 and loaded, true is return. If not loadable, false is 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) + + loadable = (enabled and loadable) or TryToEnable(name) + + local loaded = false + if loadable then + loaded = true + LoadAddOn(name) + end + + local field = "X-AceLibrary-" .. major + for i = 1, GetNumAddOns() do + if GetAddOnMetadata(i, field) then + name, _, _, enabled, loadable = GetAddOnInfo(i) + + loadable = (enabled and loadable) or TryToEnable(name) + if loadable then + loaded = true + LoadAddOn(name) + end + end + end + return loaded +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) + + if type(minor) == "string" then + local m = svnRevisionToNumber(minor) + if m then + minor = m + else + _G.error(("Bad argument #3 to `IsNewVersion'. Must be a number or SVN revision string. %q is not appropriate"):format(minor), 2) + end + end + argCheck(self, minor, 3, "number") + local lib, oldMinor = LibStub:GetLibrary(major, true) + if lib then + return oldMinor < minor + end + 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") + if minor ~= false then + TryToLoadStandalone(major) + end + + local lib, ver = LibStub:GetLibrary(major, true) + if not lib and self.libs[major] then + lib, ver = self.libs[major].instance, self.libs[major].minor + end + if minor then + if type(minor) == "string" then + local m = svnRevisionToNumber(minor) + if m then + minor = m + else + _G.error(("Bad argument #3 to `HasInstance'. Must be a number or SVN revision string. %q is not appropriate"):format(minor), 2) + end + end + argCheck(self, minor, 3, "number") + if not lib then + return false + end + return ver == minor + end + return not not lib +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") + if minor ~= false then + TryToLoadStandalone(major) + end + + local data, ver = LibStub:GetLibrary(major, true) + if not data then + if self.libs[major] then + data, ver = self.libs[major].instance, self.libs[major].minor + else + _G.error(("Cannot find a library instance of %s."):format(major), 2) + return + end + end + if minor then + if type(minor) == "string" then + local m = svnRevisionToNumber(minor) + if m then + minor = m + else + _G.error(("Bad argument #3 to `GetInstance'. Must be a number or SVN revision string. %q is not appropriate"):format(minor), 2) + end + end + argCheck(self, minor, 2, "number") + if ver ~= minor then + _G.error(("Cannot find a library instance of %s, minor version %d."):format(major, minor), 2) + end + end + return data +end + +-- Syntax sugar. AceLibrary("FooBar-1.0") +AceLibrary_mt.__call = AceLibrary.GetInstance + +local donothing = function() end + +local AceEvent + +local tmp = {} + +-- @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. +-- @param externalFunc (optional) A function to be called whenever a new +-- library is registered. +function AceLibrary:Register(newInstance, major, minor, activateFunc, deactivateFunc, externalFunc) + argCheck(self, newInstance, 2, "table") + argCheck(self, major, 3, "string") + if major ~= ACELIBRARY_MAJOR then + for k,v in pairs(_G) do + if v == newInstance then + geterrorhandler()((debugstack():match("(.-: )in.-\n") or "") .. ("Cannot register library %q. It is part of the global table in _G[%q]."):format(major, k)) + end + end + end + if major ~= ACELIBRARY_MAJOR and not major:find("^[%a%-][%a%d%-]*%-%d+%.%d+$") then + _G.error(string.format("Bad argument #3 to `Register'. Must be in the form of \"Name-1.0\". %q is not appropriate", major), 2) + end + if type(minor) == "string" then + local m = svnRevisionToNumber(minor) + if m then + minor = m + else + _G.error(("Bad argument #4 to `Register'. Must be a number or SVN revision string. %q is not appropriate"):format(minor), 2) + end + 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] + if not data then + -- This is new + if LibStub:GetLibrary(major, true) then + error(self, "Cannot register library %q. It is already registered with LibStub.", major) + end + local instance = LibStub:NewLibrary(major, minor) + copyTable(newInstance, instance) + crawlReplace(instance, instance, newInstance) + destroyTable(newInstance) + if AceLibrary == newInstance 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, '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_instance in LibStub:IterateLibraries() do -- all libraries + tmp[k] = data_instance + end + for k, data in pairs(self.libs) do -- Ace libraries which may not have been registered with LibStub + tmp[k] = data.instance + end + for k, data_instance in pairs(tmp) do + if k ~= major then + safecall(externalFunc, instance, k, data_instance) + end + tmp[k] = nil + end + end + + for k,data in pairs(self.libs) do -- only Ace libraries + 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 + if minor <= data.minor then + -- This one is already obsolete, raise an error. + _G.error(("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"):format(major, data.minor, minor, major, minor), 2) + return + end + local instance = data.instance + -- This is an update + local oldInstance = {} + + local libStubInstance = LibStub:GetLibrary(major, true) + if not libStubInstance then -- non-LibStub AceLibrary registered the library + -- pass + elseif libStubInstance ~= instance then + error(self, "Cannot register library %q. It is already registered with LibStub.", major) + else + LibStub:NewLibrary(major, minor) -- upgrade the minor version + end + + addToPositions(newInstance, major) + local isAceLibrary = (AceLibrary == newInstance) + local old_error, old_argCheck, old_pcall + if isAceLibrary then + self = instance + AceLibrary = instance + + old_error = instance.error + old_argCheck = instance.argCheck + old_pcall = instance.pcall + + self.error = error + 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() + return major, minor + end) + if not rawget(instance, 'error') then + rawset(instance, 'error', error) + 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, '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 + oldInstance = nil + + if externalFunc then + for k, data_instance in LibStub:IterateLibraries() do -- all libraries + tmp[k] = data_instance + end + for k, data in pairs(self.libs) do -- Ace libraries which may not have been registered with LibStub + tmp[k] = data.instance + end + for k, data_instance in pairs(tmp) do + if k ~= major then + safecall(externalFunc, instance, k, data_instance) + end + tmp[k] = nil + end + end + + return instance +end + +function AceLibrary:IterateLibraries() + local t = {} + for major, instance in LibStub:IterateLibraries() do + t[major] = instance + end + for major, data in pairs(self.libs) do + t[major] = data.instance + end + return pairs(t) +end + +local function manuallyFinalize(major, instance) + if AceLibrary.libs[major] then + -- don't work on Ace libraries + return + end + local finalizedExternalLibs = AceLibrary.finalizedExternalLibs + if finalizedExternalLibs[major] then + return + end + finalizedExternalLibs[major] = true + + for k,data in pairs(AceLibrary.libs) do -- only Ace libraries + if k ~= major and data.externalFunc then + safecall(data.externalFunc, data.instance, major, instance) + end + 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) + AceLibrary = self + if not self.libs then + self.libs = oldLib and oldLib.libs or {} + self.scannedlibs = oldLib and oldLib.scannedlibs or {} + end + if not self.positions then + self.positions = oldLib and oldLib.positions or setmetatable({}, { __mode = "k" }) + end + self.finalizedExternalLibs = oldLib and oldLib.finalizedExternalLibs or {} + self.frame = oldLib and oldLib.frame or CreateFrame("Frame") + self.frame:UnregisterAllEvents() + self.frame:RegisterEvent("ADDON_LOADED") + self.frame:SetScript("OnEvent", function() + for major, instance in LibStub:IterateLibraries() do + manuallyFinalize(major, instance) + end + end) + for major, instance in LibStub:IterateLibraries() do + manuallyFinalize(major, instance) + 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, nil) diff --git a/AtlasLootFu/Libs/Tablet-2.0/AceLibrary/AceLibrary.toc b/AtlasLootFu/Libs/Tablet-2.0/AceLibrary/AceLibrary.toc new file mode 100644 index 0000000..77f0cf3 --- /dev/null +++ b/AtlasLootFu/Libs/Tablet-2.0/AceLibrary/AceLibrary.toc @@ -0,0 +1,15 @@ +## Interface: 30300 +## X-Curse-Packaged-Version: r1101 +## X-Curse-Project-Name: Ace2 +## X-Curse-Project-ID: ace2 +## X-Curse-Repository-ID: wow/ace2/mainline + +## Title: Lib: AceLibrary +## Notes: AddOn development framework +## Author: Ace Development Team +## X-Website: http://www.wowace.com +## X-Category: Library +## X-License: LGPL v2.1 + MIT for AceOO-2.0 + +AceLibrary.lua + diff --git a/AtlasLootFu/Libs/Tablet-2.0/Dewdrop-2.0/Dewdrop-2.0.lua b/AtlasLootFu/Libs/Tablet-2.0/Dewdrop-2.0/Dewdrop-2.0.lua new file mode 100644 index 0000000..76da9e7 --- /dev/null +++ b/AtlasLootFu/Libs/Tablet-2.0/Dewdrop-2.0/Dewdrop-2.0.lua @@ -0,0 +1,3498 @@ +--[[ +Name: Dewdrop-2.0 +Revision: $Rev: 321 $ +Author(s): ckknight (ckknight@gmail.com) +Website: http://ckknight.wowinterface.com/ +Documentation: http://wiki.wowace.com/index.php/Dewdrop-2.0 +SVN: http://svn.wowace.com/root/trunk/DewdropLib/Dewdrop-2.0 +Description: A library to provide a clean dropdown menu interface. +Dependencies: AceLibrary +License: LGPL v2.1 +]] + +local MAJOR_VERSION = "Dewdrop-2.0" +local MINOR_VERSION = tonumber(strmatch("$Revision: 321 $", "%d+")) + 90000 + +if not AceLibrary then error(MAJOR_VERSION .. " requires AceLibrary") end +if not AceLibrary:IsNewVersion(MAJOR_VERSION, MINOR_VERSION) then return end + +local Dewdrop = {} + +local SharedMedia + +local CLOSE = "Close" +local CLOSE_DESC = "Close the menu." +local VALIDATION_ERROR = "Validation error." +local USAGE_TOOLTIP = "Usage: %s." +local RANGE_TOOLTIP = "Note that you can scroll your mouse wheel while over the slider to step by one." +local RESET_KEYBINDING_DESC = "Hit escape to clear the keybinding." +local KEY_BUTTON1 = "Left Mouse" +local KEY_BUTTON2 = "Right Mouse" +local DISABLED = "Disabled" +local DEFAULT_CONFIRM_MESSAGE = "Are you sure you want to perform `%s'?" + +if GetLocale() == "deDE" then + CLOSE = "Schlie\195\159en" + CLOSE_DESC = "Men\195\188 schlie\195\159en." + VALIDATION_ERROR = "Validierungsfehler." + USAGE_TOOLTIP = "Benutzung: %s." + RANGE_TOOLTIP = "Beachte das du mit dem Mausrad scrollen kannst solange du mit dem Mauszeiger \195\188ber dem Schieberegler bist, um feinere Spr\195\188nge zu machen." + RESET_KEYBINDING_DESC = "Escape dr\195\188cken, um die Tastenbelegung zu l\195\182schen." + KEY_BUTTON1 = "Linke Maustaste" + KEY_BUTTON2 = "Rechte Maustaste" + DISABLED = "Deaktiviert" + DEFAULT_CONFIRM_MESSAGE = "Bist du sicher das du `%s' machen willst?" +elseif GetLocale() == "koKR" then + CLOSE = "닫기" + CLOSE_DESC = "메뉴를 닫습니다." + VALIDATION_ERROR = "오류 확인." + USAGE_TOOLTIP = "사용법: %s." + RANGE_TOOLTIP = "알림 : 슬라이더 위에서 마우스 휠을 사용하면 한단계씩 조절할 수 있습니다." + RESET_KEYBINDING_DESC = "단축키를 해제하려면 ESC키를 누르세요." + KEY_BUTTON1 = "왼쪽 마우스" + KEY_BUTTON2 = "오른쪽 마우스" + DISABLED = "비활성화됨" + DEFAULT_CONFIRM_MESSAGE = "정말로 `%s' 실행을 하시겠습니까 ?" +elseif GetLocale() == "frFR" then + CLOSE = "Fermer" + CLOSE_DESC = "Ferme le menu." + VALIDATION_ERROR = "Erreur de validation." + USAGE_TOOLTIP = "Utilisation : %s." + RANGE_TOOLTIP = "Vous pouvez aussi utiliser la molette de la souris pour pour modifier progressivement." + RESET_KEYBINDING_DESC = "Appuyez sur la touche Echappement pour effacer le raccourci." + KEY_BUTTON1 = "Clic gauche" + KEY_BUTTON2 = "Clic droit" + DISABLED = "D\195\169sactiv\195\169" + DEFAULT_CONFIRM_MESSAGE = "\195\138tes-vous s\195\187r de vouloir effectuer '%s' ?" +elseif GetLocale() == "esES" then + CLOSE = "Cerrar" + CLOSE_DESC = "Cierra el menú." + VALIDATION_ERROR = "Error de validación." + USAGE_TOOLTIP = "Uso: %s." + RANGE_TOOLTIP = "Puedes desplazarte verticalmente con la rueda del ratón sobre el desplazador." + RESET_KEYBINDING_DESC = "Pulsa Escape para borrar la asignación de tecla." + KEY_BUTTON1 = "Clic Izquierdo" + KEY_BUTTON2 = "Clic Derecho" + DISABLED = "Desactivado" + DEFAULT_CONFIRM_MESSAGE = "¿Estás seguro de querer realizar `%s'?" +elseif GetLocale() == "zhTW" then + CLOSE = "關閉" + CLOSE_DESC = "關閉選單。" + VALIDATION_ERROR = "驗證錯誤。" + USAGE_TOOLTIP = "用法: %s。" + RANGE_TOOLTIP = "你可以在捲動條上使用滑鼠滾輪來捲動。" + RESET_KEYBINDING_DESC = "按Esc鍵清除快捷鍵。" + KEY_BUTTON1 = "滑鼠左鍵" + KEY_BUTTON2 = "滑鼠右鍵" + DISABLED = "停用" + DEFAULT_CONFIRM_MESSAGE = "是否執行「%s」?" +elseif GetLocale() == "zhCN" then + CLOSE = "关闭" + CLOSE_DESC = "关闭菜单" + VALIDATION_ERROR = "验证错误." + USAGE_TOOLTIP = "用法: %s." + RANGE_TOOLTIP = "你可以在滚动条上使用鼠标滚轮来翻页." + RESET_KEYBINDING_DESC = "按ESC键清除按键绑定" + KEY_BUTTON1 = "鼠标左键" + KEY_BUTTON2 = "鼠标右键" + DISABLED = "禁用" + DEFAULT_CONFIRM_MESSAGE = "是否执行'%s'?" +elseif GetLocale() == "ruRU" then + CLOSE = "Закрыть" + CLOSE_DESC = "Закрыть меню." + VALIDATION_ERROR = "Ошибка проверки данных." + USAGE_TOOLTIP = "Используйте: %s." + RANGE_TOOLTIP = "Используйте колесо мыши для прокрутки ползунка." + RESET_KEYBINDING_DESC = "Нажмите клавишу Escape для очистки клавиши." + KEY_BUTTON1 = "ЛКМ" + KEY_BUTTON2 = "ПКМ" + DISABLED = "Отключено" + DEFAULT_CONFIRM_MESSAGE = "Вы уверены что вы хотите выполнять `%s'?" +end + +Dewdrop.KEY_BUTTON1 = KEY_BUTTON1 +Dewdrop.KEY_BUTTON2 = KEY_BUTTON2 + +local function new(...) + local t = {} + for i = 1, select('#', ...), 2 do + local k = select(i, ...) + if k then + t[k] = select(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 + for i = 1, select('#', ...), 2 do + local k = select(i, ...) + if k then + t[k] = select(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 + for i = 1, select('#', ...), 2 do + local k = select(i, ...) + if k then + t[k] = select(i+1, ...) + else + break + end + end + return t + end +end +local levels +local buttons + + +-- Secure frame handling: +-- Rather than using secure buttons in the menu (has problems), we have one +-- master secureframe that we pop onto menu items on mouseover. This requires +-- some dark magic with OnLeave etc, but it's not too bad. + +local secureFrame = CreateFrame("Button", nil, nil, "SecureActionButtonTemplate") +secureFrame:Hide() + +local function secureFrame_Show(self) + local owner = self.owner + + if self.secure then -- Leftovers from previos owner, clean up! ("Shouldn't" happen but does..) + for k,v in pairs(self.secure) do + self:SetAttribute(k, nil) + end + end + self.secure = owner.secure; -- Grab hold of new secure data + + local scale = owner:GetEffectiveScale() + + self:SetPoint("TOPLEFT", nil, "BOTTOMLEFT", owner:GetLeft() * scale, owner:GetTop() * scale) + self:SetPoint("BOTTOMRIGHT", nil, "BOTTOMLEFT", owner:GetRight() * scale, owner:GetBottom() * scale) + self:EnableMouse(true) + for k,v in pairs(self.secure) do + self:SetAttribute(k, v) + end + + secureFrame:SetFrameStrata(owner:GetFrameStrata()) + secureFrame:SetFrameLevel(owner:GetFrameLevel()+1) + + self:Show() +end + +local function secureFrame_Hide(self) + self:Hide() + if self.secure then + for k,v in pairs(self.secure) do + self:SetAttribute(k, nil) + end + end + self.secure = nil +end + +secureFrame:SetScript("OnEvent", + function() + if event=="PLAYER_REGEN_ENABLED" then + this.combat = false + if not this:IsShown() and this.owner then + secureFrame_Show(this) + end + elseif event=="PLAYER_REGEN_DISABLED" then + this.combat = true + if this:IsShown() then + secureFrame_Hide(this) + end + end + end +) +secureFrame:RegisterEvent("PLAYER_REGEN_ENABLED") +secureFrame:RegisterEvent("PLAYER_REGEN_DISABLED") + +secureFrame:SetScript("OnLeave", + function() + local owner=this.owner + this:Deactivate() + owner:GetScript("OnLeave")() + end +) + +secureFrame:HookScript("OnClick", + function() + local realthis = this + this = this.owner + this:GetScript("OnClick")() + end +) + +function secureFrame:IsOwnedBy(frame) + return self.owner == frame +end + +function secureFrame:Activate(owner) + if self.owner then -- "Shouldn't" happen but apparently it does and I cba to troubleshoot... + if not self.combat then + secureFrame_Hide(self) + end + end + self.owner = owner + if not self.combat then + secureFrame_Show(self) + end +end + +function secureFrame:Deactivate() + if not self.combat then + secureFrame_Hide(self) + end + self.owner = nil +end + +-- END secure frame utilities + + +-- Underline on mouseover - use a single global underline that we move around, no point in creating lots of copies +local underlineFrame = CreateFrame("Frame", nil) +underlineFrame.tx = underlineFrame:CreateTexture() +underlineFrame.tx:SetTexture(1,1,0.5,0.75) +underlineFrame:SetScript("OnHide", function(this) this:Hide(); end) +underlineFrame:SetScript("OnShow", function(this) -- change sizing on the fly to catch runtime uiscale changes + underlineFrame.tx:SetPoint("TOPLEFT", -1, -2/this:GetEffectiveScale()) + underlineFrame.tx:SetPoint("RIGHT", 1,0) + underlineFrame.tx:SetHeight(0.6 / this:GetEffectiveScale()); +end) +underlineFrame:SetHeight(1) + +-- END underline on mouseover + + +local function GetScaledCursorPosition() + local x, y = GetCursorPosition() + local scale = UIParent:GetEffectiveScale() + return x / scale, y / scale +end + +local function StartCounting(self, level) + for i = level, 1, -1 do + if levels[i] then + levels[i].count = 3 + end + end +end + +local function StopCounting(self, level) + for i = level, 1, -1 do + if levels[i] then + levels[i].count = nil + end + end +end + +local function OnUpdate(self, elapsed) + for _,level in ipairs(levels) do + local count = level.count + if count then + count = count - elapsed + if count < 0 then + level.count = nil + self:Close(level.num) + else + level.count = count + end + end + end +end + +local function CheckDualMonitor(self, frame) + local ratio = GetScreenWidth() / GetScreenHeight() + if ratio >= 2.4 and frame:GetRight() > GetScreenWidth() / 2 and frame:GetLeft() < GetScreenWidth() / 2 then + local offsetx + if GetCursorPosition() / GetScreenHeight() * 768 < GetScreenWidth() / 2 then + offsetx = GetScreenWidth() / 2 - frame:GetRight() + else + offsetx = GetScreenWidth() / 2 - frame:GetLeft() + end + local point, parent, relativePoint, x, y = frame:GetPoint(1) + frame:SetPoint(point, parent, relativePoint, (x or 0) + offsetx, y or 0) + end +end + +local function CheckSize(self, level) + if not level.buttons then + return + end + local height = 20 + for _, button in ipairs(level.buttons) do + height = height + button:GetHeight() + end + level:SetHeight(height) + local width = 160 + for _, button in ipairs(level.buttons) do + local extra = 1 + if button.hasArrow or button.hasColorSwatch then + extra = extra + 16 + end + if not button.notCheckable then + extra = extra + 24 + end + button.text:SetFont(STANDARD_TEXT_FONT, button.textHeight) + if button.text:GetWidth() + extra > width then + width = button.text:GetWidth() + extra + end + end + level:SetWidth(width + 20) + if level:GetLeft() and level:GetRight() and level:GetTop() and level:GetBottom() and (level:GetLeft() < 0 or level:GetRight() > GetScreenWidth() or level:GetTop() > GetScreenHeight() or level:GetBottom() < 0) then + level:ClearAllPoints() + local parent = level.parent or level:GetParent() + if type(parent) ~= "table" then + parent = UIParent + end + if level.lastDirection == "RIGHT" then + if level.lastVDirection == "DOWN" then + level:SetPoint("TOPLEFT", parent, "TOPRIGHT", 5, 10) + else + level:SetPoint("BOTTOMLEFT", parent, "BOTTOMRIGHT", 5, -10) + end + else + if level.lastVDirection == "DOWN" then + level:SetPoint("TOPRIGHT", parent, "TOPLEFT", -5, 10) + else + level:SetPoint("BOTTOMRIGHT", parent, "BOTTOMLEFT", -5, -10) + end + end + end + local dirty = false + if not level:GetRight() then + self:Close() + return + end + if level:GetRight() > GetScreenWidth() and level.lastDirection == "RIGHT" then + level.lastDirection = "LEFT" + dirty = true + elseif level:GetLeft() < 0 and level.lastDirection == "LEFT" then + level.lastDirection = "RIGHT" + dirty = true + end + if level:GetTop() > GetScreenHeight() and level.lastVDirection == "UP" then + level.lastVDirection = "DOWN" + dirty = true + elseif level:GetBottom() < 0 and level.lastVDirection == "DOWN" then + level.lastVDirection = "UP" + dirty = true + end + if dirty then + level:ClearAllPoints() + local parent = level.parent or level:GetParent() + if type(parent) ~= "table" then + parent = UIParent + end + if level.lastDirection == "RIGHT" then + if level.lastVDirection == "DOWN" then + level:SetPoint("TOPLEFT", parent, "TOPRIGHT", 5, 10) + else + level:SetPoint("BOTTOMLEFT", parent, "BOTTOMRIGHT", 5, -10) + end + else + if level.lastVDirection == "DOWN" then + level:SetPoint("TOPRIGHT", parent, "TOPLEFT", -5, 10) + else + level:SetPoint("BOTTOMRIGHT", parent, "BOTTOMLEFT", -5, -10) + end + end + end + if level:GetTop() > GetScreenHeight() then + local top = level:GetTop() + local point, parent, relativePoint, x, y = level:GetPoint(1) + level:ClearAllPoints() + level:SetPoint(point, parent, relativePoint, x or 0, (y or 0) + GetScreenHeight() - top) + elseif level:GetBottom() < 0 then + local bottom = level:GetBottom() + local point, parent, relativePoint, x, y = level:GetPoint(1) + level:ClearAllPoints() + level:SetPoint(point, parent, relativePoint, x or 0, (y or 0) - bottom) + end + CheckDualMonitor(self, level) + if mod(level.num, 5) == 0 then + local left, bottom = level:GetLeft(), level:GetBottom() + level:ClearAllPoints() + level:SetPoint("BOTTOMLEFT", UIParent, "BOTTOMLEFT", left, bottom) + end +end + +local Open +local OpenSlider +local OpenEditBox +local Refresh +local Clear +local function ReleaseButton(self, level, index) + if not level.buttons then + return + end + if not level.buttons[index] then + return + end + local button = level.buttons[index] + button:Hide() + if button.highlight then + button.highlight:Hide() + end +-- button.arrow:SetVertexColor(1, 1, 1) +-- button.arrow:SetHeight(16) +-- button.arrow:SetWidth(16) + table.remove(level.buttons, index) + table.insert(buttons, button) + for k in pairs(button) do + if k ~= 0 and k ~= "text" and k ~= "check" and k ~= "arrow" and k ~= "colorSwatch" and k ~= "highlight" and k ~= "radioHighlight" then + button[k] = nil + end + end + return true +end + +local function Scroll(self, level, down) + if down then + if level:GetBottom() < 0 then + local point, parent, relativePoint, x, y = level:GetPoint(1) + level:SetPoint(point, parent, relativePoint, x, y + 50) + if level:GetBottom() > 0 then + level:SetPoint(point, parent, relativePoint, x, y + 50 - level:GetBottom()) + end + end + else + if level:GetTop() > GetScreenHeight() then + local point, parent, relativePoint, x, y = level:GetPoint(1) + level:SetPoint(point, parent, relativePoint, x, y - 50) + if level:GetTop() < GetScreenHeight() then + level:SetPoint(point, parent, relativePoint, x, y - 50 + GetScreenHeight() - level:GetTop()) + end + end + end +end + +local function getArgs(t, str, num, ...) + local x = t[str .. num] + if x == nil then + return ... + else + return x, getArgs(t, str, num + 1, ...) + end +end + +local sliderFrame +local editBoxFrame + +local normalFont +local lastSetFont +local justSetFont = false +local regionTmp = {} +local function fillRegionTmp(...) + for i = 1, select('#', ...) do + regionTmp[i] = select(i, ...) + end +end + +local function showGameTooltip(this) + if this.tooltipTitle or this.tooltipText then + GameTooltip_SetDefaultAnchor(GameTooltip, this) + local disabled = not this.isTitle and this.disabled + local font + if this.tooltipTitle then + if SharedMedia and SharedMedia:IsValid("font", this.tooltipTitle) then + font = SharedMedia:Fetch("font", this.tooltipTitle) + end + if disabled then + GameTooltip:SetText(this.tooltipTitle, 0.5, 0.5, 0.5, 1) + else + GameTooltip:SetText(this.tooltipTitle, 1, 1, 1, 1) + end + if this.tooltipText then + if not font and SharedMedia and SharedMedia:IsValid("font", this.tooltipText) then + font = SharedMedia:Fetch("font", this.tooltipText) + end + if disabled then + GameTooltip:AddLine(this.tooltipText, (NORMAL_FONT_COLOR.r + 0.5) / 2, (NORMAL_FONT_COLOR.g + 0.5) / 2, (NORMAL_FONT_COLOR.b + 0.5) / 2, 1) + else + GameTooltip:AddLine(this.tooltipText, NORMAL_FONT_COLOR.r, NORMAL_FONT_COLOR.g, NORMAL_FONT_COLOR.b, 1) + end + end + else + if SharedMedia and SharedMedia:IsValid("font", this.tooltipText) then + font = SharedMedia:Fetch("font", this.tooltipText) + end + if disabled then + GameTooltip:SetText(this.tooltipText, 0.5, 0.5, 0.5, 1) + else + GameTooltip:SetText(this.tooltipText, 1, 1, 1, 1) + end + end + if font then + fillRegionTmp(GameTooltip:GetRegions()) + lastSetFont = font + justSetFont = true + for i,v in ipairs(regionTmp) do + if v.SetFont then + local norm,size,outline = v:GetFont() + v:SetFont(font, size, outline) + if not normalFont then + normalFont = norm + end + end + regionTmp[i] = nil + end + elseif not normalFont then + fillRegionTmp(GameTooltip:GetRegions()) + for i,v in ipairs(regionTmp) do + if v.GetFont and not normalFont then + normalFont = v:GetFont() + end + regionTmp[i] = nil + end + end + GameTooltip:Show() + end + if this.tooltipFunc then + GameTooltip:SetOwner(this, "ANCHOR_NONE") + GameTooltip:SetPoint("TOPLEFT", this, "TOPRIGHT", 5, 0) + this.tooltipFunc(getArgs(this, 'tooltipArg', 1)) + GameTooltip:Show() + end +end + +local tmpt = setmetatable({}, {mode='v'}) +local numButtons = 0 +local function AcquireButton(self, level) + if not levels[level] then + return + end + level = levels[level] + if not level.buttons then + level.buttons = {} + end + local button + if #buttons == 0 then + numButtons = numButtons + 1 + button = CreateFrame("Button", "Dewdrop20Button" .. numButtons, nil) + button:SetFrameStrata("FULLSCREEN_DIALOG") + button:SetHeight(16) + local highlight = button:CreateTexture(nil, "BACKGROUND") + highlight:SetTexture("Interface\\QuestFrame\\UI-QuestTitleHighlight") + button.highlight = highlight + highlight:SetBlendMode("ADD") + highlight:SetAllPoints(button) + highlight:Hide() + local check = button:CreateTexture(nil, "ARTWORK") + button.check = check + check:SetTexture("Interface\\Buttons\\UI-CheckBox-Check") + check:SetPoint("CENTER", button, "LEFT", 12, 0) + check:SetWidth(24) + check:SetHeight(24) + local radioHighlight = button:CreateTexture(nil, "ARTWORK") + button.radioHighlight = radioHighlight + radioHighlight:SetTexture("Interface\\Buttons\\UI-RadioButton") + radioHighlight:SetAllPoints(check) + radioHighlight:SetBlendMode("ADD") + radioHighlight:SetTexCoord(0.5, 0.75, 0, 1) + radioHighlight:Hide() + button:SetScript("OnEnter", function() + if (sliderFrame and sliderFrame:IsShown() and sliderFrame.mouseDown and sliderFrame.level == this.level.num + 1) or (editBoxFrame and editBoxFrame:IsShown() and editBoxFrame.mouseDown and editBoxFrame.level == this.level.num + 1) then + for i = 1, this.level.num do + Refresh(self, levels[i]) + end + return + end + self:Close(this.level.num + 1) + if not this.disabled then + if this.secure then + secureFrame:Activate(this) + elseif this.hasSlider then + OpenSlider(self, this) + elseif this.hasEditBox then + OpenEditBox(self, this) + elseif this.hasArrow then + Open(self, this, nil, this.level.num + 1, this.value) + end + end + if not this.level then -- button reclaimed + return + end + StopCounting(self, this.level.num + 1) + if not this.disabled then + highlight:Show() + if this.isRadio then + button.radioHighlight:Show() + end + if this.mouseoverUnderline then + underlineFrame:SetParent(this) + underlineFrame:SetPoint("BOTTOMLEFT",this.text,0,0) + underlineFrame:SetWidth(this.text:GetWidth()) + underlineFrame:Show() + end + end + showGameTooltip(this) + end) + button:SetScript("OnHide", function() + if this.secure and secureFrame:IsOwnedBy(this) then + secureFrame:Deactivate() + end + end) + button:SetScript("OnLeave", function() + if this.secure and secureFrame:IsShown() then + return; -- it's ok, we didn't actually mouse out of the button, only onto the secure frame on top of it + end + underlineFrame:Hide() + if not this.selected then + highlight:Hide() + end + button.radioHighlight:Hide() + if this.level then + StartCounting(self, this.level.num) + end + GameTooltip:Hide() + end) + local first = true + button:SetScript("OnClick", function() + if not this.disabled then + if this.hasColorSwatch then + local func = button.colorFunc + local hasOpacity = this.hasOpacity + local this = this + for k in pairs(tmpt) do + tmpt[k] = nil + end + for i = 1, 1000 do + local x = this['colorArg'..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 = #tmpt + tmpt[n+1] = r + tmpt[n+2] = g + tmpt[n+3] = b + tmpt[n+4] = a + func(unpack(tmpt)) + tmpt[n+1] = nil + tmpt[n+2] = nil + tmpt[n+3] = nil + tmpt[n+4] = nil + end + end + ColorPickerFrame.hasOpacity = this.hasOpacity + ColorPickerFrame.opacityFunc = ColorPickerFrame.func + ColorPickerFrame.opacity = 1 - this.opacity + ColorPickerFrame:SetColorRGB(this.r, this.g, this.b) + local r, g, b, a = this.r, this.g, this.b, this.opacity + ColorPickerFrame.cancelFunc = function() + if func then + local n = #tmpt + tmpt[n+1] = r + tmpt[n+2] = g + tmpt[n+3] = b + tmpt[n+4] = a + func(unpack(tmpt)) + for i = 1, n+4 do + tmpt[i] = nil + end + end + end + self:Close(1) + ShowUIPanel(ColorPickerFrame) + elseif this.func then + local level = this.level + if type(this.func) == "string" then + if type(this.arg1[this.func]) ~= "function" then + self:error("Cannot call method %q", this.func) + end + this.arg1[this.func](this.arg1, getArgs(this, 'arg', 2)) + else + this.func(getArgs(this, 'arg', 1)) + end + if this.closeWhenClicked then + self:Close() + elseif level:IsShown() then + for i = 1, level.num do + Refresh(self, levels[i]) + end + local value = levels[level.num].value + for i = level.num-1, 1, -1 do + local level = levels[i] + local good = false + for _,button in ipairs(level.buttons) do + if button.value == value then + good = true + break + end + end + if not good then + Dewdrop:Close(i+1) + end + value = levels[i].value + end + end + elseif this.closeWhenClicked then + self:Close() + end + end + end) + local text = button:CreateFontString(nil, "ARTWORK") + button.text = text + text:SetFontObject(GameFontHighlightSmall) + button.text:SetFont(STANDARD_TEXT_FONT, UIDROPDOWNMENU_DEFAULT_TEXT_HEIGHT) + button:SetScript("OnMouseDown", function() + if not this.disabled and (this.func or this.colorFunc or this.closeWhenClicked) then + text:SetPoint("LEFT", button, "LEFT", this.notCheckable and 1 or 25, -1) + end + end) + button:SetScript("OnMouseUp", function() + if not this.disabled and (this.func or this.colorFunc or this.closeWhenClicked) then + text:SetPoint("LEFT", button, "LEFT", this.notCheckable and 0 or 24, 0) + end + end) + local arrow = button:CreateTexture(nil, "ARTWORK") + button.arrow = arrow + arrow:SetPoint("LEFT", button, "RIGHT", -16, 0) + arrow:SetWidth(16) + arrow:SetHeight(16) + arrow:SetTexture("Interface\\ChatFrame\\ChatFrameExpandArrow") + local colorSwatch = button:CreateTexture(nil, "ARTWORK") + button.colorSwatch = colorSwatch + colorSwatch:SetWidth(20) + colorSwatch:SetHeight(20) + colorSwatch:SetTexture("Interface\\ChatFrame\\ChatFrameColorSwatch") + local texture = button:CreateTexture(nil, "OVERLAY") + colorSwatch.texture = texture + texture:SetTexture("Interface\\Buttons\\WHITE8X8") + texture:SetWidth(11.5) + texture:SetHeight(11.5) + texture:Show() + texture:SetPoint("CENTER", colorSwatch, "CENTER") + colorSwatch:SetPoint("RIGHT", button, "RIGHT", 0, 0) + else + button = table.remove(buttons) + end + button:ClearAllPoints() + button:SetParent(level) + button:SetFrameStrata(level:GetFrameStrata()) + button:SetFrameLevel(level:GetFrameLevel() + 1) + button:SetPoint("LEFT", level, "LEFT", 10, 0) + button:SetPoint("RIGHT", level, "RIGHT", -10, 0) + if #level.buttons == 0 then + button:SetPoint("TOP", level, "TOP", 0, -10) + else + button:SetPoint("TOP", level.buttons[#level.buttons], "BOTTOM", 0, 0) + end + button.text:SetPoint("LEFT", button, "LEFT", 24, 0) + button:Show() + button.level = level + table.insert(level.buttons, button) + if not level.parented then + level.parented = true + level:ClearAllPoints() + if level.num == 1 then + if level.parent ~= UIParent and type(level.parent) == "table" then + level:SetPoint("TOPRIGHT", level.parent, "TOPLEFT") + else + level:SetPoint("CENTER", UIParent, "CENTER") + end + else + if level.lastDirection == "RIGHT" then + if level.lastVDirection == "DOWN" then + level:SetPoint("TOPLEFT", level.parent, "TOPRIGHT", 5, 10) + else + level:SetPoint("BOTTOMLEFT", level.parent, "BOTTOMRIGHT", 5, -10) + end + else + if level.lastVDirection == "DOWN" then + level:SetPoint("TOPRIGHT", level.parent, "TOPLEFT", -5, 10) + else + level:SetPoint("BOTTOMRIGHT", level.parent, "BOTTOMLEFT", -5, -10) + end + end + end + level:SetFrameStrata("FULLSCREEN_DIALOG") + end + button:SetAlpha(1) + return button +end + +local numLevels = 0 +local function AcquireLevel(self, level) + if not levels[level] then + for i = #levels + 1, level, -1 do + local i = i + numLevels = numLevels + 1 + local frame = CreateFrame("Button", "Dewdrop20Level" .. numLevels, nil) + if i == 1 then + local old_CloseSpecialWindows = CloseSpecialWindows + function CloseSpecialWindows() + local found = old_CloseSpecialWindows() + if levels[1]:IsShown() then + self:Close() + return 1 + end + return found + end + end + levels[i] = frame + frame.num = i + frame:SetParent(UIParent) + frame:SetFrameStrata("FULLSCREEN_DIALOG") + frame:Hide() + frame:SetWidth(180) + frame:SetHeight(10) + frame:SetFrameLevel(i * 3) + frame:SetScript("OnHide", function() + self:Close(level + 1) + end) + if frame.SetTopLevel then + frame:SetTopLevel(true) + end + frame:EnableMouse(true) + frame:EnableMouseWheel(true) + local backdrop = CreateFrame("Frame", nil, frame) + backdrop:SetAllPoints(frame) + backdrop:SetBackdrop(tmp( + 'bgFile', "Interface\\Tooltips\\UI-Tooltip-Background", + 'edgeFile', "Interface\\Tooltips\\UI-Tooltip-Border", + 'tile', true, + 'insets', tmp2( + 'left', 5, + 'right', 5, + 'top', 5, + 'bottom', 5 + ), + 'tileSize', 16, + 'edgeSize', 16 + )) + backdrop:SetBackdropBorderColor(TOOLTIP_DEFAULT_COLOR.r, TOOLTIP_DEFAULT_COLOR.g, TOOLTIP_DEFAULT_COLOR.b) + backdrop:SetBackdropColor(TOOLTIP_DEFAULT_BACKGROUND_COLOR.r, TOOLTIP_DEFAULT_BACKGROUND_COLOR.g, TOOLTIP_DEFAULT_BACKGROUND_COLOR.b) + frame:SetScript("OnClick", function() + self:Close(i) + end) + frame:SetScript("OnEnter", function() + StopCounting(self, i) + end) + frame:SetScript("OnLeave", function() + StartCounting(self, i) + end) + frame:SetScript("OnMouseWheel", function() + Scroll(self, frame, arg1 < 0) + end) + if i == 1 then + frame:SetScript("OnUpdate", function(this, arg1) + OnUpdate(self, arg1) + end) + levels[1].lastDirection = "RIGHT" + levels[1].lastVDirection = "DOWN" + else + levels[i].lastDirection = levels[i - 1].lastDirection + levels[i].lastVDirection = levels[i - 1].lastVDirection + end + end + end + local fullscreenFrame = GetUIPanel("fullscreen") + local l = levels[level] + local strata, framelevel = l:GetFrameStrata(), l:GetFrameLevel() + if fullscreenFrame then + l:SetParent(fullscreenFrame) + else + l:SetParent(UIParent) + end + l:SetFrameStrata(strata) + l:SetFrameLevel(framelevel) + l:SetAlpha(1) + return l +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 ~= "dragLink" and kind ~= "header" then + return '"type" must either be "range", "text", "group", "toggle", "execute", "color", "dragLink", 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 + end + if options ~= baseOptions then + if kind == "header" then + elseif type(options.desc) ~= "string" then + return '"desc" must be a string', position + elseif options.desc:len() == 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 options.cmdName:len() == 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 options.guiName:len() == 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 options.name:len() == 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 > #t then + return '"validate" numeric keys must be indexed properly. >= 1 and <= #t', 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 + if options.multiToggle and options.multiToggle ~= true then + return '"multiToggle" must be a boolean or nil if "validate" is a table', position + end + elseif options.validate == "keybinding" then + -- no other checks + 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 + if options.multiToggle and type(options.validate) ~= "table" then + return '"validate" must be a table if "multiToggle" is true', position + 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.bigStep then + if type(options.bigStep) ~= "number" then + return '"bigStep" must be a number', position + elseif options.bigStep < 0 then + return '"bigStep" 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) ~= "number" then + if type(k) ~= "string" then + return '"args" keys must be strings or numbers', position + elseif k:len() == 0 then + return '"args" keys must not be 0-length strings.', position + end + 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 + elseif kind == "execute" then + if type(options.confirm) ~= "string" and type(options.confirm) ~= "boolean" and type(options.confirm) ~= "nil" then + return '"confirm" must be a string, boolean, or nil', position + end + end + if options.icon and type(options.icon) ~= "string" then + return'"icon" must be a string', position + end + if options.iconWidth or options.iconHeight then + if type(options.iconWidth) ~= "number" or type(options.iconHeight) ~= "number" then + return '"iconHeight" and "iconWidth" must be numbers', position + end + end + if options.iconCoordLeft or options.iconCoordRight or options.iconCoordTop or options.iconCoordBottom then + if type(options.iconCoordLeft) ~= "number" or type(options.iconCoordRight) ~= "number" or type(options.iconCoordTop) ~= "number" or type(options.iconCoordBottom) ~= "number" then + return '"iconCoordLeft", "iconCoordRight", "iconCoordTop", and "iconCoordBottom" must be numbers', position + end + end +end + +local validatedOptions + +local values +local mysort_args +local mysort +local othersort +local othersort_validate + +local baseFunc, currentLevel + +local function confirmPopup(message, func, ...) + if not StaticPopupDialogs["DEWDROP20_CONFIRM_DIALOG"] then + StaticPopupDialogs["DEWDROP20_CONFIRM_DIALOG"] = {} + end + local t = StaticPopupDialogs["DEWDROP20_CONFIRM_DIALOG"] + for k in pairs(t) do + t[k] = nil + end + t.text = message + t.button1 = ACCEPT or "Accept" + t.button2 = CANCEL or "Cancel" + t.OnAccept = function() + func(unpack(t)) + end + for i = 1, select('#', ...) do + t[i] = select(i, ...) + end + t.timeout = 0 + t.whileDead = 1 + t.hideOnEscape = 1 + + Dewdrop:Close() + StaticPopup_Show("DEWDROP20_CONFIRM_DIALOG") +end + + +local function getMethod(settingname, handler, v, methodName, ...) -- "..." is simply returned straight out cause you can't do "a,b,c = 111,f(),222" + assert(v and type(v)=="table") + assert(methodName and type(methodName)=="string") + + local method = v[methodName] + if type(method)=="function" then + return method, ... + elseif type(method)=="string" then + if not handler then + Dewdrop:error("[%s] 'handler' is required if providing a method name: %q", tostring(settingname), method) + elseif not handler[method] then + Dewdrop:error("[%s] 'handler' method %q not defined", tostring(settingname), method) + end + return handler[method], handler, ... + end + + Dewdrop:error("[%s] Missing %q directive", tostring(settingname), methodName) +end + +local function callMethod(settingname, handler, v, methodName, ...) + assert(v and type(v)=="table") + assert(methodName and type(methodName)=="string") + + local method = v[methodName] + if type(method)=="function" then + local success, ret,ret2,ret3,ret4 = pcall(v[methodName], ...) + if not success then + geterrorhandler()(ret) + return nil + end + return ret,ret2,ret3,ret4 + + elseif type(method)=="string" then + + local neg = method:match("^~(.-)$") + if neg then + method = neg + end + if not handler then + Dewdrop:error("[%s] 'handler' is required if providing a method name: %q", tostring(settingname), method) + elseif not handler[method] then + Dewdrop:error("[%s] 'handler' (%q) method %q not defined", tostring(settingname), handler.name or "(unnamed)", method) + end + local success, ret,ret2,ret3,ret4 = pcall(handler[method], handler, ...) + if not success then + geterrorhandler()(ret) + return nil + end + if neg then + return not ret + end + return ret,ret2,ret3,ret4 + elseif method == false then + return nil + end + + Dewdrop:error("[%s] Missing %q directive in %q", tostring(settingname), methodName, v.name or "(unnamed)") +end + +local function skip1Nil(...) + if select(1,...)==nil then + return select(2,...) + end + return ... +end + +function Dewdrop:FeedAceOptionsTable(options, difference) + self:argCheck(options, 2, "table") + self:argCheck(difference, 3, "nil", "number") + if not currentLevel then + self:error("Cannot call `FeedAceOptionsTable' outside of a Dewdrop declaration") + end + if not difference then + difference = 0 + end + if not validatedOptions then + validatedOptions = {} + end + if not validatedOptions[options] then + local err, position = validateOptions(options) + + if err then + if position then + Dewdrop:error(position .. ": " .. err) + else + Dewdrop:error(err) + end + end + + validatedOptions[options] = true + end + local level = levels[currentLevel] + if not level then + self:error("Improper level given") + end + if not values then + values = {} + else + for k,v in pairs(values) do + values[k] = nil + end + end + + local current = level + while current do -- this traverses from higher level numbers to lower, building "values" with leaf nodes first and trunk nodes later + if current.num == difference + 1 then + break + end + table.insert(values, current.value) + current = levels[current.num - 1] + end + + local realOptions = options + local handler = options.handler + local passTable + local passValue + while #values > 0 do -- This loop traverses values from the END (trunk nodes first, then onto leaf nodes) + if options.pass then + if options.get and options.set then + passTable = options + elseif not passTable then + passTable = options + end + else + passTable = nil + end + local value = table.remove(values) + options = options.args and options.args[value] + if not options then + return + end + handler = options.handler or handler + passValue = passTable and value or nil + end + + if options.type == "group" then + local hidden = options.hidden + if type(hidden) == "function" or type(hidden) == "string" then + hidden = callMethod(options.name or "(options root)", handler, options, "hidden", options.passValue) or false + end + if hidden then + return + end + local disabled = options.disabled + if type(disabled) == "function" or type(disabled) == "string" then + disabled = callMethod(options.name or "(options root)", handler, options, "disabled", options.passValue) or false + end + if disabled then + self:AddLine( + 'text', DISABLED, + 'disabled', true + ) + return + end + for k in pairs(options.args) do + table.insert(values, k) + end + if options.pass then + if options.get and options.set then + passTable = options + elseif not passTable then + passTable = options + end + else + passTable = nil + end + if not mysort then + mysort = function(a, b) + local alpha, bravo = mysort_args[a], mysort_args[b] + local alpha_order = alpha.order or 100 + local bravo_order = bravo.order or 100 + local alpha_name = alpha.guiName or alpha.name + local bravo_name = 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 alpha_name:gsub("|c%x%x%x%x%x%x%x%x", ""):gsub("|r", ""):upper() < bravo_name:gsub("|c%x%x%x%x%x%x%x%x", ""):gsub("|r", ""):upper() + 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 + end + mysort_args = options.args + table.sort(values, mysort) + mysort_args = nil + local hasBoth = #values >= 1 and (options.args[values[1]].order or 100) > 0 and (options.args[values[#values]].order or 100) < 0 + local last_order = 1 + for _,k in ipairs(values) do + local v = options.args[k] + local handler = v.handler or handler + if hasBoth and last_order > 0 and (v.order or 100) < 0 then + hasBoth = false + self:AddLine() + end + local hidden, disabled = v.guiHidden or v.hidden, v.disabled + + if type(hidden) == "function" or type(hidden) == "string" then + hidden = callMethod(k, handler, v, "hidden", v.passValue) or false + end + if not hidden then + if type(disabled) == "function" or type(disabled) == "string" then + disabled = callMethod(k, handler, v, "disabled", v.passValue) or false + end + local name = (v.guiIconOnly and v.icon) and "" or (v.guiName or v.name) + local desc = v.guiDesc or v.desc + local iconHeight = v.iconHeight or 16 + local iconWidth = v.iconWidth or 16 + local iconCoordLeft = v.iconCoordLeft + local iconCoordRight = v.iconCoordRight + local iconCoordBottom = v.iconCoordBottom + local iconCoordTop = v.iconCoordTop + local tooltipTitle, tooltipText + tooltipTitle = name + if name ~= desc then + tooltipText = desc + end + if type(v.usage) == "string" and v.usage:trim():len() > 0 then + if tooltipText then + tooltipText = tooltipText .. "\n\n" .. USAGE_TOOLTIP:format(v.usage) + else + tooltipText = USAGE_TOOLTIP:format(v.usage) + end + end + local v_p = passTable + if not v_p or (v.type ~= "execute" and v.get and v.set) or (v.type == "execute" and v.func) then + v_p = v + end + local passValue = v.passValue or (v_p~=v and k) or nil + if v.type == "toggle" then + local checked = callMethod(name, handler, v_p, "get", passValue) or false + local checked_arg = checked + if type(v_p.get)=="string" and v_p.get:match("^~") then + checked_arg = not checked + end + local func, arg1, arg2, arg3 = getMethod(name, handler, v_p, "set", skip1Nil(passValue, not checked_arg)) + if v.guiNameIsMap then + checked = checked and true or false + name = tostring(v.map and v.map[checked]):gsub("|c%x%x%x%x%x%x%x%x(.-)|r", "%1") + tooltipTitle = name + checked = true--nil + end + self:AddLine( + 'text', name, + 'checked', checked, + 'isRadio', v.isRadio, + 'func', func, + 'arg1', arg1, + 'arg2', arg2, + 'arg3', arg3, + 'disabled', disabled, + 'tooltipTitle', tooltipTitle, + 'tooltipText', tooltipText + ) + elseif v.type == "execute" then + local func, arg1, arg2, arg3, arg4 + local confirm = v.confirm + if confirm == true then + confirm = DEFAULT_CONFIRM_MESSAGE:format(tooltipText or tooltipTitle) + func,arg1,arg2,arg3,arg4 = confirmPopup, confirm, getMethod(name, handler, v_p, "func", passValue) + elseif type(confirm) == "string" then + func,arg1,arg2,arg3,arg4 = confirmPopup, confirm, getMethod(name, handler, v_p, "func", passValue) + else + func,arg1,arg2 = getMethod(name, handler, v_p, "func", passValue) + end + self:AddLine( + 'text', name, + 'checked', checked, + 'func', func, + 'arg1', arg1, + 'arg2', arg2, + 'arg3', arg3, + 'arg4', arg4, + 'disabled', disabled, + 'tooltipTitle', tooltipTitle, + 'tooltipText', tooltipText, + 'icon', v.icon, + 'iconHeight', iconHeight, + 'iconWidth', iconWidth, + 'iconCoordLeft', iconCoordLeft, + 'iconCoordRight', iconCoordRight, + 'iconCoordTop', iconCoordTop, + 'iconCoordBottom', iconCoordBottom + ) + elseif v.type == "range" then + local sliderValue + sliderValue = callMethod(name, handler, v_p, "get", passValue) or 0 + local sliderFunc, sliderArg1, sliderArg2 = getMethod(name, handler, v_p, "set", passValue) + if tooltipText then + tooltipText = format("%s\n\n%s", tooltipText, RANGE_TOOLTIP) + else + tooltipText = RANGE_TOOLTIP + end + self:AddLine( + 'text', name, + 'hasArrow', true, + 'hasSlider', true, + 'sliderMin', v.min or 0, + 'sliderMax', v.max or 1, + 'sliderStep', v.step or 0, + 'sliderBigStep', v.bigStep or nil, + 'sliderIsPercent', v.isPercent or false, + 'sliderValue', sliderValue, + 'sliderFunc', sliderFunc, + 'sliderArg1', sliderArg1, + 'sliderArg2', sliderArg2, + 'fromAceOptions', true, + 'disabled', disabled, + 'tooltipTitle', tooltipTitle, + 'tooltipText', tooltipText, + 'icon', v.icon, + 'iconHeight', iconHeight, + 'iconWidth', iconWidth, + 'iconCoordLeft', iconCoordLeft, + 'iconCoordRight', iconCoordRight, + 'iconCoordTop', iconCoordTop, + 'iconCoordBottom', iconCoordBottom + ) + elseif v.type == "color" then + local r,g,b,a = callMethod(name, handler, v_p, "get", passValue) + if not r then + r,g,b,a = 0,0,0,0 + end + local colorFunc, colorArg1, colorArg2 = getMethod(name, handler, v_p, "set", passValue) + self:AddLine( + 'text', name, + 'hasArrow', true, + 'hasColorSwatch', true, + 'r', r, + 'g', g, + 'b', b, + 'opacity', v.hasAlpha and a or nil, + 'hasOpacity', v.hasAlpha, + 'colorFunc', colorFunc, + 'colorArg1', colorArg1, + 'colorArg2', colorArg2, + 'disabled', disabled, + 'tooltipTitle', tooltipTitle, + 'tooltipText', tooltipText + ) + elseif v.type == "text" then + if type(v.validate) == "table" then + local func,arg1,arg2 + if v.onClick then + func,arg1,arg2 = getMethod(name, handler, v, "onClick", passValue) + end + local checked + if v.isChecked then + checked = callMethod(name, handler, v, "isChecked", passValue) or false + end + self:AddLine( + 'text', name, + 'hasArrow', true, + 'value', k, + 'func', func, + 'arg1', arg1, + 'arg2', arg2, + 'mouseoverUnderline', func and true or nil, + 'disabled', disabled, + 'checked', checked, + 'tooltipTitle', tooltipTitle, + 'tooltipText', tooltipText, + 'icon', v.icon, + 'iconHeight', iconHeight, + 'iconWidth', iconWidth, + 'iconCoordLeft', iconCoordLeft, + 'iconCoordRight', iconCoordRight, + 'iconCoordTop', iconCoordTop, + 'iconCoordBottom', iconCoordBottom + ) + else + local editBoxText + editBoxText = callMethod(name, handler, v_p, "get", passValue) or "" + local editBoxFunc, editBoxArg1, editBoxArg2 = getMethod(name, handler, v_p, "set", passValue) + + local editBoxValidateFunc, editBoxValidateArg1 + + if v.validate and v.validate ~= "keybinding" then + if v.validate == "keybinding" then + if tooltipText then + tooltipText = format("%s\n\n%s", tooltipText, RESET_KEYBINDING_DESC) + else + tooltipText = RESET_KEYBINDING_DESC + end + else + editBoxValidateFunc, editBoxValidateArg1 = getMethod(name, handler, v, "validate") -- no passvalue! + end + end + + self:AddLine( + 'text', name, + 'hasArrow', true, + 'icon', v.icon, + 'iconHeight', iconHeight, + 'iconWidth', iconWidth, + 'iconCoordLeft', iconCoordLeft, + 'iconCoordRight', iconCoordRight, + 'iconCoordTop', iconCoordTop, + 'iconCoordBottom', iconCoordBottom, + 'hasEditBox', true, + 'editBoxText', editBoxText, + 'editBoxFunc', editBoxFunc, + 'editBoxArg1', editBoxArg1, + 'editBoxArg2', editBoxArg2, + 'editBoxValidateFunc', editBoxValidateFunc, + 'editBoxValidateArg1', editBoxValidateArg1, + 'editBoxIsKeybinding', v.validate == "keybinding", + 'editBoxKeybindingOnly', v.keybindingOnly, + 'editBoxKeybindingExcept', v.keybindingExcept, + 'disabled', disabled, + 'tooltipTitle', tooltipTitle, + 'tooltipText', tooltipText + ) + end + elseif v.type == "group" then + local func,arg1,arg2 + if v.onClick then + func,arg1,arg2 = getMethod(name, handler, v, "onClick", passValue) + end + local checked + if v.isChecked then + checked = callMethod(name, handler, v, "isChecked", passValue) or false + end + self:AddLine( + 'text', name, + 'hasArrow', true, + 'value', k, + 'func', func, + 'arg1', arg1, + 'arg2', arg2, + 'mouseoverUnderline', func and true or nil, + 'disabled', disabled, + 'checked', checked, + 'tooltipTitle', tooltipTitle, + 'tooltipText', tooltipText, + 'icon', v.icon, + 'iconHeight', iconHeight, + 'iconWidth', iconWidth, + 'iconCoordLeft', iconCoordLeft, + 'iconCoordRight', iconCoordRight, + 'iconCoordTop', iconCoordTop, + 'iconCoordBottom', iconCoordBottom + ) + elseif v.type == "header" then + if name == "" or not name then + self:AddLine( + 'isTitle', true, + 'icon', v.icon, + 'iconHeight', iconHeight, + 'iconWidth', iconWidth, + 'iconCoordLeft', iconCoordLeft, + 'iconCoordRight', iconCoordRight, + 'iconCoordTop', iconCoordTop, + 'iconCoordBottom', iconCoordBottom + ) + else + self:AddLine( + 'text', name, + 'isTitle', true, + 'icon', v.icon, + 'iconHeight', iconHeight, + 'iconWidth', iconWidth, + 'iconCoordLeft', iconCoordLeft, + 'iconCoordRight', iconCoordRight, + 'iconCoordTop', iconCoordTop, + 'iconCoordBottom', iconCoordBottom + ) + end + end + end + last_order = v.order or 100 + end + elseif options.type == "text" and type(options.validate) == "table" then + local current + local options_p = passTable + if not options_p or (options.get and options.set) then + options_p = options + passTable = nil + passValue = nil + end + local multiToggle = options.multiToggle + local passValue = options.passValue or passValue + if not multiToggle then + current = callMethod(k, handler, options_p, "get", passValue) + end + local indexed = true + for k,v in pairs(options.validate) do + if type(k) ~= "number" then + indexed = false + end + table.insert(values, k) + end + if not indexed then + if not othersort then + othersort = function(alpha, bravo) + return othersort_validate[alpha]:gsub("|c%x%x%x%x%x%x%x%x", ""):gsub("|r", ""):upper() < othersort_validate[bravo]:gsub("|c%x%x%x%x%x%x%x%x", ""):gsub("|r", ""):upper() + end + end + othersort_validate = options.validate + table.sort(values, othersort) + othersort_validate = nil + end + for _,k in ipairs(values) do + local v = options.validate[k] + if type(k) == "number" then + k = v + end + local func, arg1, arg2, arg3, arg4 = getMethod(k, handler, options_p, "set", skip1Nil(passValue, k)) + local checked + if multiToggle then + checked = callMethod(k, handler, options_p, "get", skip1Nil(passValue, k)) or false + if arg2 == nil then + arg2 = not checked + elseif arg3 == nil then + arg3 = not checked + else + arg4 = not checked + end + else + checked = (k == current or (type(k) == "string" and type(current) == "string" and k:lower() == current:lower())) + if checked then + func, arg1, arg2, arg3, arg4 = nil, nil, nil, nil, nil + end + end + local tooltipTitle + local tooltipText + if options.validateDesc then + tooltipTitle = v + tooltipText = options.validateDesc[k] + else + tooltipTitle = options.guiName or options.name + tooltipText = v + end + self:AddLine( + 'text', v, + 'func', func, + 'arg1', arg1, + 'arg2', arg2, + 'arg3', arg3, + 'arg4', arg4, + 'isRadio', not multiToggle, + 'checked', checked, + 'tooltipTitle', tooltipTitle, + 'tooltipText', tooltipText + ) + end + for k in pairs(values) do + values[k] = nil + end + else + return false + end + return true +end + +function Dewdrop:FeedTable(s, difference) + self:argCheck(s, 2, "table") + self:argCheck(difference, 3, "nil", "number") + if not currentLevel then + self:error("Cannot call `FeedTable' outside of a Dewdrop declaration") + end + if not difference then + difference = 0 + end + local level = levels[currentLevel] + if not level then + self:error("Improper level given") + end + if not values then + values = {} + else + for k,v in pairs(values) do + values[k] = nil + end + end + local t = s.subMenu and s or {subMenu = s} + local current = level + while current do + if current.num == difference + 1 then + break + end + table.insert(values, current.value) + current = levels[current.num - 1] + end + + while #values > 0 do + local value = table.remove(values) + t = t.subMenu and t.subMenu[value] + if not t then + return + end + end + + if t.subMenu or current.num == 1 then + for k in pairs(t.subMenu) do + table.insert(values, k) + end + table.sort(values) + for _,k in ipairs(values) do + local argTable = {"value", k} + for key, val in pairs(t.subMenu[k]) do + table.insert(argTable, key) + table.insert(argTable, val) + end + self:AddLine(unpack(argTable)) + end + for k in pairs(values) do + values[k] = nil + end + return false + end + return true +end + +function Refresh(self, level) + if type(level) == "number" then + level = levels[level] + end + if not level then + return + end + if baseFunc then + Clear(self, level) + currentLevel = level.num + if type(baseFunc) == "table" then + if currentLevel == 1 then + local handler = baseFunc.handler + if handler then + local name = tostring(handler) + if not name:find('^table:') and not handler.hideMenuTitle then + name = name:gsub("|c%x%x%x%x%x%x%x%x(.-)|r", "%1") + self:AddLine( + 'text', name, + 'isTitle', true + ) + end + end +-- elseif level.parentText then +-- self:AddLine( +-- 'text', level.parentText, +-- 'tooltipTitle', level.parentTooltipTitle, +-- 'tooltipText', level.parentTooltipText, +-- 'tooltipFunc', level.parentTooltipFunc, +-- 'isTitle', true +-- ) + end + self:FeedAceOptionsTable(baseFunc) + if currentLevel == 1 then + self:AddLine( + 'text', CLOSE, + 'tooltipTitle', CLOSE, + 'tooltipText', CLOSE_DESC, + 'closeWhenClicked', true + ) + end + else +-- if level.parentText then +-- self:AddLine( +-- 'text', level.parentText, +-- 'tooltipTitle', level.parentTooltipTitle, +-- 'tooltipText', level.parentTooltipText, +-- 'tooltipFunc', level.parentTooltipFunc, +-- 'isTitle', true +-- ) +-- end + baseFunc(currentLevel, level.value, levels[level.num - 1] and levels[level.num - 1].value, levels[level.num - 2] and levels[level.num - 2].value, levels[level.num - 3] and levels[level.num - 3].value, levels[level.num - 4] and levels[level.num - 4].value) + end + currentLevel = nil + CheckSize(self, level) + end +end + +function Dewdrop:Refresh(level) + self:argCheck(level, 2, "number", "nil") + if not level then + for k,v in pairs(levels) do + Refresh(self, v) + end + else + Refresh(self, levels[level]) + end +end + +function OpenSlider(self, parent) + if not sliderFrame then + sliderFrame = CreateFrame("Frame", nil, nil) + sliderFrame:SetWidth(100) + sliderFrame:SetHeight(170) + sliderFrame:SetScale(UIParent:GetScale()) + sliderFrame:SetBackdrop(tmp( + 'bgFile', "Interface\\Tooltips\\UI-Tooltip-Background", + 'edgeFile', "Interface\\Tooltips\\UI-Tooltip-Border", + 'tile', true, + 'insets', tmp2( + 'left', 5, + 'right', 5, + 'top', 5, + 'bottom', 5 + ), + 'tileSize', 16, + 'edgeSize', 16 + )) + sliderFrame:SetFrameStrata("FULLSCREEN_DIALOG") + if sliderFrame.SetTopLevel then + sliderFrame:SetTopLevel(true) + end + sliderFrame:SetBackdropBorderColor(TOOLTIP_DEFAULT_COLOR.r, TOOLTIP_DEFAULT_COLOR.g, TOOLTIP_DEFAULT_COLOR.b) + sliderFrame:SetBackdropColor(TOOLTIP_DEFAULT_BACKGROUND_COLOR.r, TOOLTIP_DEFAULT_BACKGROUND_COLOR.g, TOOLTIP_DEFAULT_BACKGROUND_COLOR.b) + sliderFrame:EnableMouse(true) + sliderFrame:EnableMouseWheel(true) + sliderFrame:Hide() + sliderFrame:SetPoint("CENTER", UIParent, "CENTER") + local slider = CreateFrame("Slider", nil, sliderFrame) + sliderFrame.slider = slider + slider:SetOrientation("VERTICAL") + slider:SetMinMaxValues(0, 1) + slider:SetValueStep(0.000000001) + slider:SetValue(0.5) + slider:SetWidth(16) + slider:SetHeight(128) + slider:SetPoint("LEFT", sliderFrame, "LEFT", 15, 0) + slider:SetBackdrop(tmp( + 'bgFile', "Interface\\Buttons\\UI-SliderBar-Background", + 'edgeFile', "Interface\\Buttons\\UI-SliderBar-Border", + 'tile', true, + 'edgeSize', 8, + 'tileSize', 8, + 'insets', tmp2( + 'left', 3, + 'right', 3, + 'top', 3, + 'bottom', 3 + ) + )) + local texture = slider:CreateTexture() + slider:SetThumbTexture("Interface\\Buttons\\UI-SliderBar-Button-Vertical") + local text = slider:CreateFontString(nil, "ARTWORK") + sliderFrame.topText = text + text:SetFontObject(GameFontGreenSmall) + text:SetText("100%") + text:SetPoint("BOTTOM", slider, "TOP") + local text = slider:CreateFontString(nil, "ARTWORK") + sliderFrame.bottomText = text + text:SetFontObject(GameFontGreenSmall) + text:SetText("0%") + text:SetPoint("TOP", slider, "BOTTOM") + local editBox = CreateFrame("EditBox", nil, sliderFrame) + sliderFrame.currentText = editBox + editBox:SetFontObject(ChatFontNormal) + editBox:SetHeight(13) + editBox:SetPoint("RIGHT", sliderFrame, "RIGHT", -16, 0) + editBox:SetPoint("LEFT", slider, "RIGHT", 12, 0) + editBox:SetText("50%") + editBox:SetJustifyH("CENTER") + + local width = editBox:GetWidth()/2 + 10 + local left = editBox:CreateTexture(nil, "BACKGROUND") + left:SetTexture("Interface\\ChatFrame\\UI-ChatInputBorder-Left") + left:SetTexCoord(0, width / 256, 0, 1) + left:SetWidth(width) + left:SetHeight(32) + left:SetPoint("LEFT", editBox, "LEFT", -10, 0) + local right = editBox:CreateTexture(nil, "BACKGROUND") + right:SetTexture("Interface\\ChatFrame\\UI-ChatInputBorder-Right") + right:SetTexCoord(1 - width / 256, 1, 0, 1) + right:SetWidth(width) + right:SetHeight(32) + right:SetPoint("RIGHT", editBox, "RIGHT", 10, 0) + + local changed = false + local inside = false + slider:SetScript("OnValueChanged", function() + if sliderFrame.changing then + return + end + changed = true + local done = false + if sliderFrame.parent and sliderFrame.parent.sliderFunc then + local min = sliderFrame.parent.sliderMin or 0 + local max = sliderFrame.parent.sliderMax or 1 + local step + if sliderFrame.fineStep then + step = sliderFrame.parent.sliderStep or (max - min) / 100 + else + step = sliderFrame.parent.sliderBigStep or sliderFrame.parent.sliderStep or (max - min) / 100 + end + local value = (1 - slider:GetValue()) * (max - min) + min + if step > 0 then + value = math.floor((value - min) / step + 0.5) * step + min + if value > max then + value = max + elseif value < min then + value = min + end + end + if value == sliderFrame.lastValue then + return + end + sliderFrame.lastValue = value + local text = sliderFrame.parent.sliderFunc(getArgs(sliderFrame.parent, 'sliderArg', 1, value)) + if sliderFrame.parent.fromAceOptions then + text = nil + elseif type(text) == "string" or type(text) == "number" then + sliderFrame.currentText:SetText(text) + done = true + end + end + if not done then + local min = sliderFrame.parent.sliderMin or 0 + local max = sliderFrame.parent.sliderMax or 1 + local step + if sliderFrame.fineStep then + step = sliderFrame.parent.sliderStep or (max - min) / 100 + else + step = sliderFrame.parent.sliderBigStep or sliderFrame.parent.sliderStep or (max - min) / 100 + end + local value = (1 - slider:GetValue()) * (max - min) + min + if step > 0 then + value = math.floor((value - min) / step + 0.5) * step + min + if value > max then + value = max + elseif value < min then + value = min + end + end + if sliderFrame.parent.sliderIsPercent then + sliderFrame.currentText:SetText(string.format("%.0f%%", value * 100)) + else + if step < 0.1 then + sliderFrame.currentText:SetText(string.format("%.2f", value)) + elseif step < 1 then + sliderFrame.currentText:SetText(string.format("%.1f", value)) + else + sliderFrame.currentText:SetText(string.format("%.0f", value)) + end + end + end + end) + local function onEnter() + StopCounting(self, sliderFrame.level) + showGameTooltip(sliderFrame.parent) + end + local function onLeave() + GameTooltip:Hide() + end + sliderFrame:SetScript("OnEnter", onEnter) + sliderFrame:SetScript("OnLeave", function() + GameTooltip:Hide() + if changed then + local parent = sliderFrame.parent + local sliderFunc = parent.sliderFunc + for i = 1, sliderFrame.level - 1 do + Refresh(self, levels[i]) + end + local newParent + for _,button in ipairs(levels[sliderFrame.level-1].buttons) do + if button.sliderFunc == sliderFunc then + newParent = button + break + end + end + if newParent then + OpenSlider(self, newParent) + else + sliderFrame:Hide() + end + end + end) + editBox:SetScript("OnEnter", onEnter) + editBox:SetScript("OnLeave", onLeave) + slider:SetScript("OnMouseDown", function() + sliderFrame.mouseDown = true + GameTooltip:Hide() + end) + slider:SetScript("OnMouseUp", function() + sliderFrame.mouseDown = false + if changed--[[ and not inside]] then + local parent = sliderFrame.parent + local sliderFunc = parent.sliderFunc + for i = 1, sliderFrame.level - 1 do + Refresh(self, levels[i]) + end + local newParent + for _,button in ipairs(levels[sliderFrame.level-1].buttons) do + if button.sliderFunc == sliderFunc then + newParent = button + break + end + end + if newParent then + OpenSlider(self, newParent) + else + sliderFrame:Hide() + end + end + if inside then + showGameTooltip(sliderFrame.parent) + end + end) + slider:SetScript("OnEnter", function() + inside = true + StopCounting(self, sliderFrame.level) + showGameTooltip(sliderFrame.parent) + end) + slider:SetScript("OnLeave", function() + inside = false + GameTooltip:Hide() + if changed and not sliderFrame.mouseDown then + local parent = sliderFrame.parent + local sliderFunc = parent.sliderFunc + for i = 1, sliderFrame.level - 1 do + Refresh(self, levels[i]) + end + local newParent + for _,button in ipairs(levels[sliderFrame.level-1].buttons) do + if button.sliderFunc == sliderFunc then + newParent = button + break + end + end + if newParent then + OpenSlider(self, newParent) + else + sliderFrame:Hide() + end + + changed = false + end + end) + sliderFrame:SetScript("OnMouseWheel", function(t, a1) + local arg1 = a1 or arg1 + local up = arg1 > 0 + + local min = sliderFrame.parent.sliderMin or 0 + local max = sliderFrame.parent.sliderMax or 1 + local step = sliderFrame.parent.sliderStep or (max - min) / 100 + if step <= 0 then + step = (max - min) / 100 + end + + local value = (1 - slider:GetValue()) * (max - min) + min + if up then + value = value + step + else + value = value - step + end + if value > max then + value = max + elseif value < min then + value = min + end + sliderFrame.fineStep = true + if max<=min then + slider:SetValue(0) + else + slider:SetValue(1 - (value - min) / (max - min)) + end + sliderFrame.fineStep = nil + end) + slider:SetScript("OnMouseWheel", sliderFrame:GetScript("OnMouseWheel")) + editBox:SetScript("OnEnterPressed", function(t, a1) + local value = editBox:GetNumber() + + if sliderFrame.parent.sliderIsPercent then + value = value / 100 + end + + local min = sliderFrame.parent.sliderMin or 0 + local max = sliderFrame.parent.sliderMax or 1 + + if value > max then + value = max + elseif value < min then + value = min + end + sliderFrame.fineStep = true + if max <= min then + slider:SetValue(0) + else + slider:SetValue(1 - (value - min) / (max - min)) + end + sliderFrame.fineStep = nil + + StartCounting(self, sliderFrame.level) + end) + editBox:SetScript("OnEscapePressed", function() + self:Close(sliderFrame.level) + StartCounting(self, sliderFrame.level) + end) + editBox:SetAutoFocus(false) + end + sliderFrame.parent = parent + sliderFrame.level = parent.level.num + 1 + sliderFrame.parentValue = parent.level.value + sliderFrame:SetFrameLevel(parent.level:GetFrameLevel() + 3) + sliderFrame.slider:SetFrameLevel(sliderFrame:GetFrameLevel() + 1) + sliderFrame.currentText:SetFrameLevel(sliderFrame:GetFrameLevel() + 1) + sliderFrame.currentText:ClearFocus() + sliderFrame.changing = true + if not parent.sliderMin or not parent.sliderMax then + return + end + + if parent.arrow then +-- parent.arrow:SetVertexColor(0.2, 0.6, 0) +-- parent.arrow:SetHeight(24) +-- parent.arrow:SetWidth(24) + parent.selected = true + parent.highlight:Show() + end + + sliderFrame:SetClampedToScreen(false) + if not parent.sliderValue then + parent.sliderValue = (parent.sliderMin + parent.sliderMax) / 2 + end + if parent.sliderMax <= parent.sliderMin then + sliderFrame.slider:SetValue(0) + else + sliderFrame.slider:SetValue(1 - (parent.sliderValue - parent.sliderMin) / (parent.sliderMax - parent.sliderMin)) + end + sliderFrame.changing = false + sliderFrame.bottomText:SetText(parent.sliderMinText or "0") + sliderFrame.topText:SetText(parent.sliderMaxText or "1") + local text + if parent.sliderFunc and not parent.fromAceOptions then + text = parent.sliderFunc(getArgs(parent, 'sliderArg', 1, parent.sliderValue)) + end + if type(text) == "number" or type(text) == "string" then + sliderFrame.currentText:SetText(text) + elseif parent.sliderIsPercent then + sliderFrame.currentText:SetText(string.format("%.0f%%", parent.sliderValue * 100)) + else + if parent.sliderStep < 0.1 then + sliderFrame.currentText:SetText(string.format("%.2f", parent.sliderValue)) + elseif parent.sliderStep < 1 then + sliderFrame.currentText:SetText(string.format("%.1f", parent.sliderValue)) + else + sliderFrame.currentText:SetText(string.format("%.0f", parent.sliderValue)) + end + end + + + sliderFrame.lastValue = parent.sliderValue + + local level = parent.level + sliderFrame:Show() + sliderFrame:ClearAllPoints() + if level.lastDirection == "RIGHT" then + if level.lastVDirection == "DOWN" then + sliderFrame:SetPoint("TOPLEFT", parent, "TOPRIGHT", 5, 10) + else + sliderFrame:SetPoint("BOTTOMLEFT", parent, "BOTTOMRIGHT", 5, -10) + end + else + if level.lastVDirection == "DOWN" then + sliderFrame:SetPoint("TOPRIGHT", parent, "TOPLEFT", -5, 10) + else + sliderFrame:SetPoint("BOTTOMRIGHT", parent, "BOTTOMLEFT", -5, -10) + end + end + local dirty + if level.lastDirection == "RIGHT" then + if sliderFrame:GetRight() > GetScreenWidth() then + level.lastDirection = "LEFT" + dirty = true + end + elseif sliderFrame:GetLeft() < 0 then + level.lastDirection = "RIGHT" + dirty = true + end + if level.lastVDirection == "DOWN" then + if sliderFrame:GetBottom() < 0 then + level.lastVDirection = "UP" + dirty = true + end + elseif sliderFrame:GetTop() > GetScreenWidth() then + level.lastVDirection = "DOWN" + dirty = true + end + if dirty then + sliderFrame:ClearAllPoints() + if level.lastDirection == "RIGHT" then + if level.lastVDirection == "DOWN" then + sliderFrame:SetPoint("TOPLEFT", parent, "TOPRIGHT", 5, 10) + else + sliderFrame:SetPoint("BOTTOMLEFT", parent, "BOTTOMRIGHT", 5, -10) + end + else + if level.lastVDirection == "DOWN" then + sliderFrame:SetPoint("TOPRIGHT", parent, "TOPLEFT", -5, 10) + else + sliderFrame:SetPoint("BOTTOMRIGHT", parent, "BOTTOMLEFT", -5, -10) + end + end + end + local left, bottom = sliderFrame:GetLeft(), sliderFrame:GetBottom() + sliderFrame:ClearAllPoints() + sliderFrame:SetPoint("BOTTOMLEFT", UIParent, "BOTTOMLEFT", left, bottom) + if mod(level.num, 5) == 0 then + local left, bottom = level:GetLeft(), level:GetBottom() + level:ClearAllPoints() + level:SetPoint("BOTTOMLEFT", UIParent, "BOTTOMLEFT", left, bottom) + end + sliderFrame:SetClampedToScreen(true) +end + +function OpenEditBox(self, parent) + if not editBoxFrame then + editBoxFrame = CreateFrame("Frame", nil, nil) + editBoxFrame:SetWidth(200) + editBoxFrame:SetHeight(40) + editBoxFrame:SetScale(UIParent:GetScale()) + editBoxFrame:SetBackdrop(tmp( + 'bgFile', "Interface\\Tooltips\\UI-Tooltip-Background", + 'edgeFile', "Interface\\Tooltips\\UI-Tooltip-Border", + 'tile', true, + 'insets', tmp2( + 'left', 5, + 'right', 5, + 'top', 5, + 'bottom', 5 + ), + 'tileSize', 16, + 'edgeSize', 16 + )) + editBoxFrame:SetFrameStrata("FULLSCREEN_DIALOG") + if editBoxFrame.SetTopLevel then + editBoxFrame:SetTopLevel(true) + end + editBoxFrame:SetBackdropBorderColor(TOOLTIP_DEFAULT_COLOR.r, TOOLTIP_DEFAULT_COLOR.g, TOOLTIP_DEFAULT_COLOR.b) + editBoxFrame:SetBackdropColor(TOOLTIP_DEFAULT_BACKGROUND_COLOR.r, TOOLTIP_DEFAULT_BACKGROUND_COLOR.g, TOOLTIP_DEFAULT_BACKGROUND_COLOR.b) + editBoxFrame:EnableMouse(true) + editBoxFrame:EnableMouseWheel(true) + editBoxFrame:Hide() + editBoxFrame:SetPoint("CENTER", UIParent, "CENTER") + + local editBox = CreateFrame("EditBox", nil, editBoxFrame) + editBoxFrame.editBox = editBox + editBox:SetFontObject(ChatFontNormal) + editBox:SetWidth(160) + editBox:SetHeight(13) + editBox:SetPoint("CENTER", editBoxFrame, "CENTER", 0, 0) + + local left = editBox:CreateTexture(nil, "BACKGROUND") + left:SetTexture("Interface\\ChatFrame\\UI-ChatInputBorder-Left") + left:SetTexCoord(0, 100 / 256, 0, 1) + left:SetWidth(100) + left:SetHeight(32) + left:SetPoint("LEFT", editBox, "LEFT", -10, 0) + local right = editBox:CreateTexture(nil, "BACKGROUND") + right:SetTexture("Interface\\ChatFrame\\UI-ChatInputBorder-Right") + right:SetTexCoord(156/256, 1, 0, 1) + right:SetWidth(100) + right:SetHeight(32) + right:SetPoint("RIGHT", editBox, "RIGHT", 10, 0) + + editBox:SetScript("OnEnterPressed", function() + if editBoxFrame.parent and editBoxFrame.parent.editBoxValidateFunc then + local t = editBox.realText or editBox:GetText() or "" + local result = editBoxFrame.parent.editBoxValidateFunc(getArgs(editBoxFrame.parent, 'editBoxValidateArg', 1, t)) + if not result then + UIErrorsFrame:AddMessage(VALIDATION_ERROR, 1, 0, 0) + return + end + end + if editBoxFrame.parent and editBoxFrame.parent.editBoxFunc then + local t + if editBox.realText ~= "NONE" then + t = editBox.realText or editBox:GetText() or "" + end + editBoxFrame.parent.editBoxFunc(getArgs(editBoxFrame.parent, 'editBoxArg', 1, t)) + end + self:Close(editBoxFrame.level) + for i = 1, editBoxFrame.level - 1 do + Refresh(self, levels[i]) + end + StartCounting(self, editBoxFrame.level-1) + end) + editBox:SetScript("OnEscapePressed", function() + self:Close(editBoxFrame.level) + StartCounting(self, editBoxFrame.level-1) + end) + editBox:SetScript("OnReceiveDrag", function(this) + if GetCursorInfo then + local type, alpha, bravo = GetCursorInfo() + local text + if type == "spell" then + text = GetSpellName(alpha, bravo) + elseif type == "item" then + text = bravo + end + if not text then + return + end + ClearCursor() + editBox:SetText(text) + end + end) + local changing = false + local skipNext = false + + function editBox:SpecialSetText(text) + local oldText = editBox:GetText() or "" + if not text then + text = "" + end + if text ~= oldText then + changing = true + self:SetText(tostring(text)) + changing = false + skipNext = true + end + end + + editBox:SetScript("OnTextChanged", function() + if skipNext then + skipNext = false + elseif not changing and editBoxFrame.parent and editBoxFrame.parent.editBoxChangeFunc then + local t + if editBox.realText ~= "NONE" then + t = editBox.realText or editBox:GetText() or "" + end + local text = editBoxFrame.parent.editBoxChangeFunc(getArgs(editBoxFrame.parent, 'editBoxChangeArg', 1, t)) + if text then + editBox:SpecialSetText(text) + end + end + end) + editBoxFrame:SetScript("OnEnter", function() + StopCounting(self, editBoxFrame.level) + showGameTooltip(editBoxFrame.parent) + end) + editBoxFrame:SetScript("OnLeave", function() + GameTooltip:Hide() + end) + editBox:SetScript("OnEnter", function() + StopCounting(self, editBoxFrame.level) + showGameTooltip(editBoxFrame.parent) + end) + editBox:SetScript("OnLeave", function() + GameTooltip:Hide() + end) + editBoxFrame:SetScript("OnKeyDown", function(this, a1) + if not editBox.keybinding then + return + end + local arg1 = a1 or arg1 + local screenshotKey = GetBindingKey("SCREENSHOT") + if screenshotKey and arg1 == screenshotKey then + Screenshot() + return + end + + if arg1 == "LeftButton" then + arg1 = "BUTTON1" + elseif arg1 == "RightButton" then + arg1 = "BUTTON2" + elseif arg1 == "MiddleButton" then + arg1 = "BUTTON3" + elseif arg1 == "Button4" then + arg1 = "BUTTON4" + elseif arg1 == "Button5" then + arg1 = "BUTTON5" + end + if arg1 == "UNKNOWN" then + return + elseif arg1 == "SHIFT" or arg1 == "CTRL" or arg1 == "ALT" then + return + elseif arg1 == "ENTER" then + if editBox.keybindingOnly and not editBox.keybindingOnly[editBox.realText] then + return editBox:GetScript("OnEscapePressed")() + elseif editBox.keybindingExcept and editBox.keybindingExcept[editBox.realText] then + return editBox:GetScript("OnEscapePressed")() + else + return editBox:GetScript("OnEnterPressed")() + end + elseif arg1 == "ESCAPE" then + if editBox.realText == "NONE" then + return editBox:GetScript("OnEscapePressed")() + else + editBox:SpecialSetText(NONE or "NONE") + editBox.realText = "NONE" + return + end + elseif editBox.keybindingOnly and not editBox.keybindingOnly[arg1] then + return + elseif editBox.keybindingExcept and editBox.keybindingExcept[arg1] then + return + end + local s = GetBindingText(arg1, "KEY_") + if s == "BUTTON1" then + s = KEY_BUTTON1 + elseif s == "BUTTON2" then + s = KEY_BUTTON2 + end + local real = arg1 + if IsShiftKeyDown() then + s = "Shift-" .. s + real = "SHIFT-" .. real + end + if IsControlKeyDown() then + s = "Ctrl-" .. s + real = "CTRL-" .. real + end + if IsAltKeyDown() then + s = "Alt-" .. s + real = "ALT-" .. real + end + if editBox:GetText() ~= s then + editBox:SpecialSetText("-") + editBox:SpecialSetText(s) + editBox.realText = real + return editBox:GetScript("OnTextChanged")() + end + end) + editBoxFrame:SetScript("OnMouseDown", editBoxFrame:GetScript("OnKeyDown")) + editBox:SetScript("OnMouseDown", function(this, ...) + if GetCursorInfo and (CursorHasItem() or CursorHasSpell()) then + return editBox:GetScript("OnReceiveDrag")(this, ...) + end + return editBoxFrame:GetScript("OnKeyDown")(this, ...) + end) + editBoxFrame:SetScript("OnMouseWheel", function(t, a1) + local arg1 = a1 or arg1 + local up = arg1 > 0 + arg1 = up and "MOUSEWHEELUP" or "MOUSEWHEELDOWN" + return editBoxFrame:GetScript("OnKeyDown")(t or this, arg1) + end) + editBox:SetScript("OnMouseWheel", editBoxFrame:GetScript("OnMouseWheel")) + end + editBoxFrame.parent = parent + editBoxFrame.level = parent.level.num + 1 + editBoxFrame.parentValue = parent.level.value + editBoxFrame:SetFrameLevel(parent.level:GetFrameLevel() + 3) + editBoxFrame.editBox:SetFrameLevel(editBoxFrame:GetFrameLevel() + 1) + editBoxFrame.editBox.realText = nil + editBoxFrame:SetClampedToScreen(false) + + editBoxFrame.editBox:SpecialSetText("") + if parent.editBoxIsKeybinding then + local s = parent.editBoxText + if s == "" then + s = "NONE" + end + editBoxFrame.editBox.realText = s + if s and s ~= "NONE" then + local alpha,bravo = s:match("^(.+)%-(.+)$") + if not bravo then + alpha = nil + bravo = s + end + bravo = GetBindingText(bravo, "KEY_") + if alpha then + editBoxFrame.editBox:SpecialSetText(alpha:upper() .. "-" .. bravo) + else + editBoxFrame.editBox:SpecialSetText(bravo) + end + else + editBoxFrame.editBox:SpecialSetText(NONE or "NONE") + end + else + editBoxFrame.editBox:SpecialSetText(parent.editBoxText) + end + + editBoxFrame.editBox.keybinding = parent.editBoxIsKeybinding + editBoxFrame.editBox.keybindingOnly = parent.editBoxKeybindingOnly + editBoxFrame.editBox.keybindingExcept = parent.editBoxKeybindingExcept + editBoxFrame.editBox:EnableKeyboard(not parent.editBoxIsKeybinding) + editBoxFrame:EnableKeyboard(parent.editBoxIsKeybinding) + + if parent.arrow then +-- parent.arrow:SetVertexColor(0.2, 0.6, 0) +-- parent.arrow:SetHeight(24) +-- parent.arrow:SetWidth(24) + parent.selected = true + parent.highlight:Show() + end + + local level = parent.level + editBoxFrame:Show() + editBoxFrame:ClearAllPoints() + if level.lastDirection == "RIGHT" then + if level.lastVDirection == "DOWN" then + editBoxFrame:SetPoint("TOPLEFT", parent, "TOPRIGHT", 5, 10) + else + editBoxFrame:SetPoint("BOTTOMLEFT", parent, "BOTTOMRIGHT", 5, -10) + end + else + if level.lastVDirection == "DOWN" then + editBoxFrame:SetPoint("TOPRIGHT", parent, "TOPLEFT", -5, 10) + else + editBoxFrame:SetPoint("BOTTOMRIGHT", parent, "BOTTOMLEFT", -5, -10) + end + end + local dirty + if level.lastDirection == "RIGHT" then + if editBoxFrame:GetRight() > GetScreenWidth() then + level.lastDirection = "LEFT" + dirty = true + end + elseif editBoxFrame:GetLeft() < 0 then + level.lastDirection = "RIGHT" + dirty = true + end + if level.lastVDirection == "DOWN" then + if editBoxFrame:GetBottom() < 0 then + level.lastVDirection = "UP" + dirty = true + end + elseif editBoxFrame:GetTop() > GetScreenWidth() then + level.lastVDirection = "DOWN" + dirty = true + end + if dirty then + editBoxFrame:ClearAllPoints() + if level.lastDirection == "RIGHT" then + if level.lastVDirection == "DOWN" then + editBoxFrame:SetPoint("TOPLEFT", parent, "TOPRIGHT", 5, 10) + else + editBoxFrame:SetPoint("BOTTOMLEFT", parent, "BOTTOMRIGHT", 5, -10) + end + else + if level.lastVDirection == "DOWN" then + editBoxFrame:SetPoint("TOPRIGHT", parent, "TOPLEFT", -5, 10) + else + editBoxFrame:SetPoint("BOTTOMRIGHT", parent, "BOTTOMLEFT", -5, -10) + end + end + end + local left, bottom = editBoxFrame:GetLeft(), editBoxFrame:GetBottom() + editBoxFrame:ClearAllPoints() + editBoxFrame:SetPoint("BOTTOMLEFT", UIParent, "BOTTOMLEFT", left, bottom) + if mod(level.num, 5) == 0 then + local left, bottom = level:GetLeft(), level:GetBottom() + level:ClearAllPoints() + level:SetPoint("BOTTOMLEFT", UIParent, "BOTTOMLEFT", left, bottom) + end + editBoxFrame:SetClampedToScreen(true) +end + +function Dewdrop:EncodeKeybinding(text) + if text == nil or text == "NONE" then + return nil + end + text = tostring(text):upper() + local shift, ctrl, alt + local modifier + while true do + if text == "-" then + break + end + modifier, text = strsplit('-', text, 2) + if text then + if modifier ~= "SHIFT" and modifier ~= "CTRL" and modifier ~= "ALT" then + return false + end + if modifier == "SHIFT" then + if shift then + return false + end + shift = true + end + if modifier == "CTRL" then + if ctrl then + return false + end + ctrl = true + end + if modifier == "ALT" then + if alt then + return false + end + alt = true + end + else + text = modifier + break + end + end + if not text:find("^F%d+$") and text ~= "CAPSLOCK" and text:len() ~= 1 and (text:len() == 0 or text:byte() < 128 or text:len() > 4) and not _G["KEY_" .. text] and text ~= "BUTTON1" and text ~= "BUTTON2" then + return false + end + local s = GetBindingText(text, "KEY_") + if s == "BUTTON1" then + s = KEY_BUTTON1 + elseif s == "BUTTON2" then + s = KEY_BUTTON2 + end + if shift then + s = "Shift-" .. s + end + if ctrl then + s = "Ctrl-" .. s + end + if alt then + s = "Alt-" .. s + end + return s +end + +function Dewdrop:IsOpen(parent) + self:argCheck(parent, 2, "table", "string", "nil") + return levels[1] and levels[1]:IsShown() and (not parent or parent == levels[1].parent or parent == levels[1]:GetParent()) +end + +function Dewdrop:GetOpenedParent() + return (levels[1] and levels[1]:IsShown()) and (levels[1].parent or levels[1]:GetParent()) +end + +function Open(self, parent, func, level, value, point, relativePoint, cursorX, cursorY) + self:Close(level) + if DewdropLib then + local d = DewdropLib:GetInstance('1.0') + local ret, val = pcall(d, IsOpen, d) + if ret and val then + DewdropLib:GetInstance('1.0'):Close() + end + end + if type(parent) == "table" then + parent:GetCenter() + end + local frame = AcquireLevel(self, level) + if level == 1 then + frame.lastDirection = "RIGHT" + frame.lastVDirection = "DOWN" + else + frame.lastDirection = levels[level - 1].lastDirection + frame.lastVDirection = levels[level - 1].lastVDirection + end + frame:SetFrameStrata("FULLSCREEN_DIALOG") + frame:ClearAllPoints() + frame.parent = parent + frame:SetPoint("LEFT", UIParent, "RIGHT", 10000, 0) + frame:Show() + if level == 1 then + baseFunc = func + end + levels[level].value = value +-- levels[level].parentText = parent.text and parent.text:GetText() or nil +-- levels[level].parentTooltipTitle = parent.tooltipTitle +-- levels[level].parentTooltipText = parent.tooltipText +-- levels[level].parentTooltipFunc = parent.tooltipFunc + if type(parent) == "table" and parent.arrow then +-- parent.arrow:SetVertexColor(0.2, 0.6, 0) +-- parent.arrow:SetHeight(24) +-- parent.arrow:SetWidth(24) + parent.selected = true + parent.highlight:Show() + end + relativePoint = relativePoint or point + Refresh(self, levels[level]) + if point or (cursorX and cursorY) then + frame:ClearAllPoints() + if cursorX and cursorY then + local curX, curY = GetScaledCursorPosition() + if curY < GetScreenHeight() / 2 then + point, relativePoint = "BOTTOM", "BOTTOM" + else + point, relativePoint = "TOP", "TOP" + end + if curX < GetScreenWidth() / 2 then + point, relativePoint = point .. "LEFT", relativePoint .. "RIGHT" + else + point, relativePoint = point .. "RIGHT", relativePoint .. "LEFT" + end + end + frame:SetPoint(point, type(parent) == "table" and parent or UIParent, relativePoint) + if cursorX and cursorY then + local left = frame:GetLeft() + local width = frame:GetWidth() + local bottom = frame:GetBottom() + local height = frame:GetHeight() + local curX, curY = GetScaledCursorPosition() + frame:ClearAllPoints() + relativePoint = relativePoint or point + if point == "BOTTOM" or point == "TOP" then + if curX < GetScreenWidth() / 2 then + point = point .. "LEFT" + else + point = point .. "RIGHT" + end + elseif point == "CENTER" then + if curX < GetScreenWidth() / 2 then + point = "LEFT" + else + point = "RIGHT" + end + end + local xOffset, yOffset = 0, 0 + if curY > GetScreenHeight() / 2 then + yOffset = -height + end + if curX > GetScreenWidth() / 2 then + xOffset = -width + end + frame:SetPoint(point, type(parent) == "table" and parent or UIParent, relativePoint, curX - left + xOffset, curY - bottom + yOffset) + if level == 1 then + frame.lastDirection = "RIGHT" + end + elseif cursorX then + local left = frame:GetLeft() + local width = frame:GetWidth() + local curX, curY = GetScaledCursorPosition() + frame:ClearAllPoints() + relativePoint = relativePoint or point + if point == "BOTTOM" or point == "TOP" then + if curX < GetScreenWidth() / 2 then + point = point .. "LEFT" + else + point = point .. "RIGHT" + end + elseif point == "CENTER" then + if curX < GetScreenWidth() / 2 then + point = "LEFT" + else + point = "RIGHT" + end + end + frame:SetPoint(point, type(parent) == "table" and parent or UIParent, relativePoint, curX - left - width / 2, 0) + if level == 1 then + frame.lastDirection = "RIGHT" + end + elseif cursorY then + local bottom = frame:GetBottom() + local height = frame:GetHeight() + local curX, curY = GetScaledCursorPosition() + frame:ClearAllPoints() + relativePoint = relativePoint or point + if point == "LEFT" or point == "RIGHT" then + if curX < GetScreenHeight() / 2 then + point = point .. "BOTTOM" + else + point = point .. "TOP" + end + elseif point == "CENTER" then + if curX < GetScreenHeight() / 2 then + point = "BOTTOM" + else + point = "TOP" + end + end + frame:SetPoint(point, type(parent) == "table" and parent or UIParent, relativePoint, 0, curY - bottom - height / 2) + if level == 1 then + frame.lastDirection = "DOWN" + end + end + if (strsub(point, 1, 3) ~= strsub(relativePoint, 1, 3)) then + if frame:GetBottom() < 0 then + local point, parent, relativePoint, x, y = frame:GetPoint(1) + local change = GetScreenHeight() - frame:GetTop() + local otherChange = -frame:GetBottom() + if otherChange < change then + change = otherChange + end + frame:SetPoint(point, parent, relativePoint, x, y + change) + elseif frame:GetTop() > GetScreenHeight() then + local point, parent, relativePoint, x, y = frame:GetPoint(1) + local change = GetScreenHeight() - frame:GetTop() + local otherChange = -frame:GetBottom() + if otherChange < change then + change = otherChange + end + frame:SetPoint(point, parent, relativePoint, x, y + change) + end + end + end + CheckDualMonitor(self, frame) + frame:SetClampedToScreen(true) + frame:SetClampedToScreen(false) + StartCounting(self, level) +end + +function Dewdrop:IsRegistered(parent) + self:argCheck(parent, 2, "table", "string") + return not not self.registry[parent] +end + +function Dewdrop:Register(parent, ...) + self:argCheck(parent, 2, "table", "string") + if self.registry[parent] then + self:Unregister(parent) + end + local info = new(...) + if type(info.children) == "table" then + local err, position = validateOptions(info.children) + + if err then + if position then + Dewdrop:error(position .. ": " .. err) + else + Dewdrop:error(err) + end + end + end + self.registry[parent] = info + if not info.dontHook and not self.onceRegistered[parent] and type(parent) == "table" then + if parent:HasScript("OnMouseUp") then + local script = parent:GetScript("OnMouseUp") + parent:SetScript("OnMouseUp", function(this, ...) + if script then + script(this, ...) + end + if arg1 == "RightButton" and self.registry[parent] then + if self:IsOpen(parent) then + self:Close() + else + self:Open(parent) + end + end + end) + end + if parent:HasScript("OnMouseDown") then + local script = parent:GetScript("OnMouseDown") + parent:SetScript("OnMouseDown", function(this, ...) + if script then + script(this, ...) + end + if self.registry[parent] then + self:Close() + end + end) + end + end + self.onceRegistered[parent] = true +end + +function Dewdrop:Unregister(parent) + self:argCheck(parent, 2, "table", "string") + self.registry[parent] = nil +end + +function Dewdrop:Open(parent, ...) + self:argCheck(parent, 2, "table", "string") + local info + local k1 = ... + if type(k1) == "table" and k1[0] and k1.IsObjectType and self.registry[k1] then + info = tmp(select(2, ...)) + for k,v in pairs(self.registry[k1]) do + if info[k] == nil then + info[k] = v + end + end + else + info = tmp(...) + if self.registry[parent] then + for k,v in pairs(self.registry[parent]) do + if info[k] == nil then + info[k] = v + end + end + end + end + local point = info.point + local relativePoint = info.relativePoint + local cursorX = info.cursorX + local cursorY = info.cursorY + if type(point) == "function" then + local b + point, b = point(parent) + if b then + relativePoint = b + end + end + if type(relativePoint) == "function" then + relativePoint = relativePoint(parent) + end + Open(self, parent, info.children, 1, nil, point, relativePoint, cursorX, cursorY) +end + +function Clear(self, level) + if level then + if level.buttons then + for i = #level.buttons, 1, -1 do + ReleaseButton(self, level, i) + end + end + end +end + +function Dewdrop:Close(level) + if DropDownList1:IsShown() then + DropDownList1:Hide() + end + if DewdropLib then + local d = DewdropLib:GetInstance('1.0') + local ret, val = pcall(d, IsOpen, d) + if ret and val then + DewdropLib:GetInstance('1.0'):Close() + end + end + self:argCheck(level, 2, "number", "nil") + if not level then + level = 1 + end + if level == 1 and levels[level] then + levels[level].parented = false + end + if level > 1 and levels[level-1].buttons then + local buttons = levels[level-1].buttons + for _,button in ipairs(buttons) do +-- button.arrow:SetWidth(16) +-- button.arrow:SetHeight(16) + button.selected = nil + button.highlight:Hide() +-- button.arrow:SetVertexColor(1, 1, 1) + end + end + if sliderFrame and sliderFrame.level >= level then + sliderFrame:Hide() + end + if editBoxFrame and editBoxFrame.level >= level then + editBoxFrame:Hide() + end + for i = level, #levels do + Clear(self, levels[level]) + levels[i]:Hide() + levels[i]:ClearAllPoints() + levels[i]:SetPoint("CENTER", UIParent, "CENTER") + levels[i].value = nil + end +end + +function Dewdrop:AddSeparator(level) + level = levels[level or currentLevel] + if not level or not level.buttons then return; end + + local prevbutton = level.buttons[#level.buttons] + if not prevbutton then return; end + + if prevbutton.disabled and prevbutton.text:GetText() == "" then + return + end + self:AddLine("text", "", "disabled", true) +end + +function Dewdrop:AddLine(...) + local info = tmp(...) + local level = info.level or currentLevel + info.level = nil + local button = AcquireButton(self, level) + if not next(info) then + info.disabled = true + end + button.disabled = info.isTitle or info.notClickable or info.disabled or (self.combat and info.secure) + button.isTitle = info.isTitle + button.notClickable = info.notClickable + if button.isTitle then + button.text:SetFontObject(GameFontNormalSmall) + elseif button.notClickable then + button.text:SetFontObject(GameFontHighlightSmall) + elseif button.disabled then + button.text:SetFontObject(GameFontDisableSmall) + else + button.text:SetFontObject(GameFontHighlightSmall) + end + if info.disabled then + button.arrow:SetDesaturated(true) + button.check:SetDesaturated(true) + else + button.arrow:SetDesaturated(false) + button.check:SetDesaturated(false) + end + if info.textR and info.textG and info.textB then + button.textR = info.textR + button.textG = info.textG + button.textB = info.textB + button.text:SetTextColor(button.textR, button.textG, button.textB) + else + button.text:SetTextColor(button.text:GetFontObject():GetTextColor()) + end + button.notCheckable = info.notCheckable + button.text:SetPoint("LEFT", button, "LEFT", button.notCheckable and 0 or 24, 0) + button.checked = not info.notCheckable and info.checked + button.mouseoverUnderline = info.mouseoverUnderline + button.isRadio = not info.notCheckable and info.isRadio + if info.isRadio then + button.check:Show() + button.check:SetTexture(info.checkIcon or "Interface\\Buttons\\UI-RadioButton") + if button.checked then + button.check:SetTexCoord(0.25, 0.5, 0, 1) + button.check:SetVertexColor(1, 1, 1, 1) + else + button.check:SetTexCoord(0, 0.25, 0, 1) + button.check:SetVertexColor(1, 1, 1, 0.5) + end + button.radioHighlight:SetTexture(info.checkIcon or "Interface\\Buttons\\UI-RadioButton") + button.check:SetWidth(16) + button.check:SetHeight(16) + elseif info.icon then + button.check:Show() + button.check:SetTexture(info.icon) + if info.iconWidth and info.iconHeight then + button.check:SetWidth(info.iconWidth) + button.check:SetHeight(info.iconHeight) + else + button.check:SetWidth(16) + button.check:SetHeight(16) + end + if info.iconCoordLeft and info.iconCoordRight and info.iconCoordTop and info.iconCoordBottom then + button.check:SetTexCoord(info.iconCoordLeft, info.iconCoordRight, info.iconCoordTop, info.iconCoordBottom) + elseif info.icon:find("^Interface\\Icons\\") then + button.check:SetTexCoord(0.05, 0.95, 0.05, 0.95) + else + button.check:SetTexCoord(0, 1, 0, 1) + end + button.check:SetVertexColor(1, 1, 1, 1) + else + if button.checked then + if info.checkIcon then + button.check:SetWidth(16) + button.check:SetHeight(16) + button.check:SetTexture(info.checkIcon) + if info.checkIcon:find("^Interface\\Icons\\") then + button.check:SetTexCoord(0.05, 0.95, 0.05, 0.95) + else + button.check:SetTexCoord(0, 1, 0, 1) + end + else + button.check:SetWidth(24) + button.check:SetHeight(24) + button.check:SetTexture("Interface\\Buttons\\UI-CheckBox-Check") + button.check:SetTexCoord(0, 1, 0, 1) + end + button.check:SetVertexColor(1, 1, 1, 1) + else + button.check:SetVertexColor(1, 1, 1, 0) + end + end + if not button.disabled then + button.func = info.func + button.secure = info.secure + end + button.hasColorSwatch = info.hasColorSwatch + if button.hasColorSwatch then + button.colorSwatch:Show() + button.colorSwatch.texture:Show() + button.r = info.r or 1 + button.g = info.g or 1 + button.b = info.b or 1 + button.colorSwatch.texture:SetVertexColor(button.r, button.g, button.b) + button.checked = false + button.func = nil + button.colorFunc = info.colorFunc + local i = 1 + while true do + local k = "colorArg" .. i + local x = info[k] + if x == nil then + break + end + button[k] = x + i = i + 1 + end + button.hasOpacity = info.hasOpacity + button.opacity = info.opacity or 1 + else + button.colorSwatch:Hide() + button.colorSwatch.texture:Hide() + end + button.hasArrow = not button.hasColorSwatch and (info.value or info.hasSlider or info.hasEditBox) and info.hasArrow + if button.hasArrow then + button.arrow:SetAlpha(1) + if info.hasSlider then + button.hasSlider = true + button.sliderMin = info.sliderMin or 0 + button.sliderMax = info.sliderMax or 1 + button.sliderStep = info.sliderStep or 0 + button.sliderBigStep = info.sliderBigStep or button.sliderStep + if button.sliderBigStep < button.sliderStep then + button.sliderBigStep = button.sliderStep + end + button.sliderIsPercent = info.sliderIsPercent and true or false + button.sliderMinText = info.sliderMinText or button.sliderIsPercent and string.format("%.0f%%", button.sliderMin * 100) or button.sliderMin + button.sliderMaxText = info.sliderMaxText or button.sliderIsPercent and string.format("%.0f%%", button.sliderMax * 100) or button.sliderMax + button.sliderFunc = info.sliderFunc + button.sliderValue = info.sliderValue + button.fromAceOptions = info.fromAceOptions + local i = 1 + while true do + local k = "sliderArg" .. i + local x = info[k] + if x == nil then + break + end + button[k] = x + i = i + 1 + end + elseif info.hasEditBox then + button.hasEditBox = true + button.editBoxText = info.editBoxText or "" + button.editBoxFunc = info.editBoxFunc + local i = 1 + while true do + local k = "editBoxArg" .. i + local x = info[k] + if x == nil then + break + end + button[k] = x + i = i + 1 + end + button.editBoxChangeFunc = info.editBoxChangeFunc + local i = 1 + while true do + local k = "editBoxChangeArg" .. i + local x = info[k] + if x == nil then + break + end + button[k] = x + i = i + 1 + end + button.editBoxValidateFunc = info.editBoxValidateFunc + local i = 1 + while true do + local k = "editBoxValidateArg" .. i + local x = info[k] + if x == nil then + break + end + button[k] = x + i = i + 1 + end + button.editBoxIsKeybinding = info.editBoxIsKeybinding + button.editBoxKeybindingOnly = info.editBoxKeybindingOnly + button.editBoxKeybindingExcept = info.editBoxKeybindingExcept + else + button.value = info.value + local l = levels[level+1] + if l and info.value == l.value then +-- button.arrow:SetWidth(24) +-- button.arrow:SetHeight(24) + button.selected = true + button.highlight:Show() + end + end + else + button.arrow:SetAlpha(0) + end + local i = 1 + while true do + local k = "arg" .. i + local x = info[k] + if x == nil then + break + end + button[k] = x + i = i + 1 + end + button.closeWhenClicked = info.closeWhenClicked + button.textHeight = info.textHeight or UIDROPDOWNMENU_DEFAULT_TEXT_HEIGHT or 10 + local font,_ = button.text:GetFont() + button.text:SetFont(STANDARD_TEXT_FONT or "Fonts\\FRIZQT__.TTF", button.textHeight) + button:SetHeight(button.textHeight + 6) + button.text:SetPoint("RIGHT", button.arrow, (button.hasColorSwatch or button.hasArrow) and "LEFT" or "RIGHT") + button.text:SetJustifyH(info.justifyH or "LEFT") + button.text:SetText(info.text) + button.tooltipTitle = info.tooltipTitle + button.tooltipText = info.tooltipText + button.tooltipFunc = info.tooltipFunc + local i = 1 + while true do + local k = "tooltipArg" .. i + local x = info[k] + if x == nil then + break + end + button[k] = x + i = i + 1 + end + if not button.tooltipTitle and not button.tooltipText and not button.tooltipFunc and not info.isTitle then + button.tooltipTitle = info.text + end + if type(button.func) == "string" then + if type(button.arg1) ~= "table" then + self:error("Cannot call method %q on a non-table", button.func) + end + if type(button.arg1[button.func]) ~= "function" then + self:error("Method %q nonexistant.", button.func) + end + end +end + +function Dewdrop:InjectAceOptionsTable(handler, options) + self:argCheck(handler, 2, "table") + self:argCheck(options, 3, "table") + if tostring(options.type):lower() ~= "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 AceLibrary:HasInstance("AceOO-2.0") or not class then + if Rock then + -- possible Rock object + for mixin in Rock:IterateObjectMixins(handler) 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 + else + -- Ace2 object + while class and class ~= AceLibrary("AceOO-2.0").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 + end + return options +end + +function Dewdrop:OnTooltipHide() + if lastSetFont then + if lastSetFont == normalFont then + lastSetFont = nil + return + end + fillRegionTmp(GameTooltip:GetRegions()) + for i,v in ipairs(regionTmp) do + if v.GetFont then + local font,size,outline = v:GetFont() + if font == lastSetFont then + v:SetFont(normalFont, size, outline) + end + end + regionTmp[i] = nil + end + lastSetFont = nil + end +end + +local function activate(self, oldLib, oldDeactivate) + Dewdrop = self + if oldLib and oldLib.registry then + self.registry = oldLib.registry + self.onceRegistered = oldLib.onceRegistered + else + self.registry = {} + self.onceRegistered = {} + + local WorldFrame_OnMouseDown = WorldFrame:GetScript("OnMouseDown") + local WorldFrame_OnMouseUp = WorldFrame:GetScript("OnMouseUp") + local oldX, oldY, clickTime + WorldFrame:SetScript("OnMouseDown", function(this, ...) + oldX,oldY = GetCursorPosition() + clickTime = GetTime() + if WorldFrame_OnMouseDown then + WorldFrame_OnMouseDown(this, ...) + end + end) + + WorldFrame:SetScript("OnMouseUp", function(this, ...) + local x,y = GetCursorPosition() + if not oldX or not oldY or not x or not y or not clickTime then + self:Close() + if WorldFrame_OnMouseUp then + WorldFrame_OnMouseUp(this, ...) + end + return + end + local d = math.abs(x - oldX) + math.abs(y - oldY) + if d <= 5 and GetTime() - clickTime < 0.5 then + self:Close() + end + if WorldFrame_OnMouseUp then + WorldFrame_OnMouseUp(this, ...) + end + end) + + hooksecurefunc(DropDownList1, "Show", function() + if levels[1] and levels[1]:IsVisible() then + self:Close() + end + end) + + hooksecurefunc("HideDropDownMenu", function() + if levels[1] and levels[1]:IsVisible() then + self:Close() + end + end) + + hooksecurefunc("CloseDropDownMenus", function() + if levels[1] and levels[1]:IsVisible() then + local stack = debugstack() + if not stack:find("`TargetFrame_OnHide'") then + self:Close() + end + end + end) + end + self.frame = oldLib and oldLib.frame or CreateFrame("Frame") + self.frame:UnregisterAllEvents() + self.frame:RegisterEvent("PLAYER_REGEN_ENABLED") + self.frame:RegisterEvent("PLAYER_REGEN_DISABLED") + self.frame:Hide() + self.frame:SetScript("OnEvent", function(this, event) + this:Show() + if event=="PLAYER_REGEN_ENABLED" then -- track combat state for secure frame operations + self.combat = false + elseif event=="PLAYER_REGEN_DISABLED" then + self.combat = true + end + end) + self.frame:SetScript("OnUpdate", function(this) + this:Hide() + self:Refresh(1) + end) + self.hookedTooltip = true + if not oldLib or not oldLib.hookedTooltip then + local OnTooltipHide = GameTooltip:GetScript("OnHide") + GameTooltip:SetScript("OnHide", function(this, ...) + if OnTooltipHide then + OnTooltipHide(this, ...) + end + if type(self.OnTooltipHide) == "function" then + self:OnTooltipHide() + end + end) + end + levels = {} + buttons = {} + + if oldDeactivate then + oldDeactivate(oldLib) + end +end + +local function external(lib, major, instance) + if major == "LibSharedMedia-3.0" then + SharedMedia = instance + end +end + +AceLibrary:Register(Dewdrop, MAJOR_VERSION, MINOR_VERSION, activate, nil, external) diff --git a/AtlasLootFu/Libs/Tablet-2.0/Tablet-2.0.lua b/AtlasLootFu/Libs/Tablet-2.0/Tablet-2.0.lua new file mode 100644 index 0000000..f800ae5 --- /dev/null +++ b/AtlasLootFu/Libs/Tablet-2.0/Tablet-2.0.lua @@ -0,0 +1,2973 @@ +--[[ +Name: Tablet-2.0 +Revision: $Rev: 216 $ +Author(s): ckknight (ckknight@gmail.com) +Website: http://ckknight.wowinterface.com/ +Documentation: http://www.wowace.com/index.php/Tablet-2.0 +SVN: http://svn.wowace.com/wowace/trunk/TabletLib/Tablet-2.0 +Description: A library to provide an efficient, featureful tooltip-style display. +Dependencies: AceLibrary, (optional) Dewdrop-2.0 +License: LGPL v2.1 +]] + +local MAJOR_VERSION = "Tablet-2.0" +local MINOR_VERSION = 90000 + tonumber(("$Revision: 216 $"):match("(%d+)")) + +if not AceLibrary then error(MAJOR_VERSION .. " requires AceLibrary") end +if not AceLibrary:IsNewVersion(MAJOR_VERSION, MINOR_VERSION) then return end + +local DEBUG = false + +local SCROLL_UP = "Scroll up" +local SCROLL_DOWN = "Scroll down" +local HINT = "Hint" +local DETACH = "Detach" +local DETACH_DESC = "Detach the tablet from its source." +local SIZE = "Size" +local SIZE_DESC = "Scale the tablet." +local CLOSE_MENU = "Close menu" +local CLOSE_MENU_DESC = "Close the menu." +local COLOR = "Background color" +local COLOR_DESC = "Set the background color." +local LOCK = "Lock" +local LOCK_DESC = "Lock the tablet in its current position. Alt+Right-click for menu or Alt+drag to drag it when locked." + +if GetLocale() == "deDE" then + SCROLL_UP = "Hochscrollen" + SCROLL_DOWN = "Runterscrollen" + HINT = "Hinweis" + DETACH = "L\195\182sen" + DETACH_DESC = "L\195\182st den Tooltip aus seiner Verankerung." + SIZE = "Gr\195\182\195\159e" + SIZE_DESC = "Gr\195\182\195\159e des Tooltips \195\164ndern." + CLOSE_MENU = "Menu schlie\195\159en" + CLOSE_MENU_DESC = "Schlie\195\159t das Menu." + COLOR = "Hintergrundfarbe" + COLOR_DESC = "Hintergrundfarbe setzen." + LOCK = "Sperren" + LOCK_DESC = "Sperrt die aktuelle Position vom Tooltip. Alt+Rechts-klick f\195\188rs Men\195\188 oder Alt+Verschieben f\195\188rs verschieben wenn es gesperrt ist." +elseif GetLocale() == "koKR" then + SCROLL_UP = "위로 스크롤" + SCROLL_DOWN = "아래로 스크롤" + HINT = "힌트" + DETACH = "분리" + DETACH_DESC = "테이블을 분리합니다." + SIZE = "크기" + SIZE_DESC = "테이블의 크기입니다." + CLOSE_MENU = "메뉴 닫기" + CLOSE_MENU_DESC = "메뉴를 닫습니다." + COLOR = "배경 색상" + COLOR_DESC = "배경 색상을 설정합니다." + LOCK = "고정" + LOCK_DESC = "현재 위치에 테이블을 고정합니다. 알트+우클릭 : 메뉴열기, 알트+드래그 : 고정된것을 드래그합니다." +elseif GetLocale() == "zhCN" then + SCROLL_UP = "向上翻转" + SCROLL_DOWN = "向上翻转" + HINT = "提示" + DETACH = "分离" + DETACH_DESC = "分离菜单为独立提示." + SIZE = "尺寸" + SIZE_DESC = "缩放菜单显示尺寸." + CLOSE_MENU = "关闭菜单" + CLOSE_MENU_DESC = "关闭菜单" + COLOR = "背景颜色" + COLOR_DESC = "设置菜单背景颜色." + LOCK = "锁定" + LOCK_DESC = "锁定菜单当前位置. alt+右键 将显示选项, alt+拖动 可以移动已锁定的菜单." +elseif GetLocale() == "zhTW" then + SCROLL_UP = "向上翻捲" + SCROLL_DOWN = "向上翻捲" + HINT = "提示" + DETACH = "分離" + DETACH_DESC = "分離選單為獨立提示。" + SIZE = "尺寸" + SIZE_DESC = "縮放選單顯示尺寸。" + CLOSE_MENU = "關閉選單" + CLOSE_MENU_DESC = "關閉選單。" + COLOR = "背景顏色" + COLOR_DESC = "設定選單背景顏色。" + LOCK = "鎖定" + LOCK_DESC = "鎖定選單目前位置設定。Alt-右鍵將顯示選項,Alt-拖動可以移動已鎖定的選單。" +elseif GetLocale() == "frFR" then + SCROLL_UP = "Parcourir vers le haut" + SCROLL_DOWN = "Parcourir vers le bas" + HINT = "Astuce" + DETACH = "D\195\169tacher" + DETACH_DESC = "Permet de d\195\169tacher le tableau de sa source." + SIZE = "Taille" + SIZE_DESC = "Permet de changer l'\195\169chelle du tableau." + CLOSE_MENU = "Fermer le menu" + CLOSE_MENU_DESC = "Ferme ce menu." + COLOR = "Couleur du fond" + COLOR_DESC = "Permet de d\195\169finir la couleur du fond." + LOCK = "Bloquer" + LOCK_DESC = "Bloque le tableau \195\160 sa position actuelle. Alt+clic-droit pour le menu ou Alt+glisser pour le d\195\169placer quand il est bloqu\195\169." +elseif GetLocale() == "esES" then + SCROLL_UP = "Desplazar hacia arriba" + SCROLL_DOWN = "Desplazar hacia abajo" + HINT = "Consejo" + DETACH = "Separar" + DETACH_DESC = "Separa el tooltip de su fuente." + SIZE = "Tama\195\177o" + SIZE_DESC = "Escala el tooltip" + CLOSE_MENU = "Cerrar men\195\186" + CLOSE_MENU_DESC = "Cierra el men\195\186" + COLOR = "Color de fondo" + COLOR_DESC = "Establece el color de fondo" + LOCK = "Bloquear" + LOCK_DESC = "Bloquea el tooltip en su posici\195\179n actual. Clic+Alt para el men\195\186 y arrastra+Alt para arrastrarlo cuando est\195\161 bloqueado" +elseif GetLocale() == "ruRU" then + SCROLL_UP = "Прокрутка вверх" + SCROLL_DOWN = "Прокрутка вниз" + HINT = "Совет" + DETACH = "Отделить" + DETACH_DESC = "Отделить планшет от его источника." + SIZE = "Размер" + SIZE_DESC = "Масштаб планшета." + CLOSE_MENU = "Закрыть меню" + CLOSE_MENU_DESC = "Закрыть меню." + COLOR = "Цвет фона" + COLOR_DESC = "Установить цвет фона." + LOCK = "Зафиксировать" + LOCK_DESC = "Зафиксировать планшет в его текущем позиции. Alt+ПКМ для меню или Alt+перетаскивание для перетаскивания когда планшет зафиксирован." +end + +local start = GetTime() +local wrap +local GetProfileInfo +if DEBUG then + local tree = {} + local treeMemories = {} + local treeTimes = {} + local memories = {} + local times = {} + function wrap(value, name) + if type(value) == "function" then + local oldFunction = value + memories[name] = 0 + times[name] = 0 + return function(self, ...) + local pos = #tree + tree[#tree+1] = name + treeMemories[#treeMemories+1] = 0 + treeTimes[#treeTimes+1] = 0 + local t, mem = GetTime(), gcinfo() + local r1, r2, r3, r4, r5, r6, r7, r8 = oldFunction(self, ...) + mem, t = gcinfo() - mem, GetTime() - t + if pos > 0 then + treeMemories[pos] = treeMemories[pos] + mem + treeTimes[pos] = treeTimes[pos] + t + end + local otherMem = table.remove(treeMemories) + if mem - otherMem > 0 then + memories[name] = memories[name] + mem - otherMem + end + times[name] = times[name] + t - table.remove(treeTimes) + table.remove(tree) + return r1, r2, r3, r4, r5, r6, r7, r8 + end + end + end + + function GetProfileInfo() + return GetTime() - start, times, memories + end +else + function wrap(value) + return value + end +end + +local function GetMainFrame() + if UIParent:IsShown() then + return UIParent + end + local f = GetUIPanel("fullscreen") + if f and f:IsShown() then + return f + end + return nil +end +GetMainFrame = wrap(GetMainFrame, "GetMainFrame") + +local MIN_TOOLTIP_SIZE = 200 +local TESTSTRING_EXTRA_WIDTH = 8 +local Tablet = {} +local Dewdrop = nil +local CleanCategoryPool +local pool = {} + +local function del(t) + setmetatable(t, nil) + for k in pairs(t) do + t[k] = nil + end + t[''] = true + t[''] = nil + pool[t] = true + return nil +end + +local function copy(parent) + local t = next(pool) + if not t then + t = {} + else + pool[t] = nil + end + if parent then + for k,v in pairs(parent) do + t[k] = v + end + setmetatable(t, getmetatable(parent)) + end + return t +end + +local function new(...) + local t = next(pool) + if not t then + t = {} + else + pool[t] = nil + end + + for i = 1, select('#', ...), 2 do + local k = select(i, ...) + if k then + t[k] = select(i+1, ...) + else + break + end + end + return t +end + +local tmp +tmp = setmetatable({}, {__index = function(self, key) + local t = {} + tmp[key] = function(...) + for k in pairs(t) do + t[k] = nil + end + for i = 1, select('#', ...), 2 do + local k = select(i, ...) + if k then + t[k] = select(i+1, ...) + else + break + end + end + return t + end + return tmp[key] +end}) + +local headerSize, normalSize +if GameTooltipHeaderText then + headerSize = select(2,GameTooltipHeaderText:GetFont()) +else + headerSize = 14 +end +if GameTooltipText then + normalSize = select(2,GameTooltipText:GetFont()) +else + normalSize = 12 +end +local tooltip +local testString +local TabletData = {} +local Category = {} +local Line = {} +local function getTestWidth(font, size, text) + if not testString then + return MIN_TOOLTIP_SIZE + 40 + end + testString:SetWidth(0) + testString:SetFontObject(font) + local a,_,b = font:GetFont() + testString:SetFont(a, size, b) + testString:SetText(text) + return testString:GetStringWidth()-- + TESTSTRING_EXTRA_WIDTH +end +getTestWidth = wrap(getTestWidth, "getTestWidth") +do + local TabletData_mt = { __index = TabletData } + function TabletData:new(tablet) + local self = new() + self.categories = new() + self.id = 0 + self.width = 0 -- (MIN_TOOLTIP_SIZE - 20)*tablet.fontSizePercent + self.tablet = tablet + self.title = nil + self.titleR, self.titleG, self.titleB = nil, nil, nil + self.num_lines = 0 + setmetatable(self, TabletData_mt) + return self + end + TabletData.new = wrap(TabletData.new, "TabletData:new") + + function TabletData:checkMinWidth() + local min = self.minWidth or MIN_TOOLTIP_SIZE + local width = (min - 20)*self.tablet.fontSizePercent + if self.width < width then + self.width = width + end + end + TabletData.checkMinWidth = wrap(TabletData.checkMinWidth, "TabletData:checkMinWidth") + + function TabletData:del() + for k, v in ipairs(self.categories) do + v:del() + end + del(self.categories) + del(self) + end + TabletData.del = wrap(TabletData.del, "TabletData:del") + + function TabletData:Display() + if self.title and (self.tablet == tooltip or self.tablet.registration.showTitleWhenDetached) then + local info = new( + 'hideBlankLine', true, + 'text', self.title, + 'justify', "CENTER", + 'font', GameTooltipHeaderText, + 'isTitle', true + ) + if self.titleR then + info.textR = self.titleR + info.textG = self.titleG + info.textB = self.titleB + end + self:AddCategory(info, 1) + del(info) + end + if self.tablet == tooltip or self.tablet.registration.showHintWhenDetached then + if self.hint then + self:AddCategory(nil):AddLine( + 'text', HINT .. ": " .. self.hint, + 'textR', 0, + 'textG', 1, + 'textB', 0, + 'wrap', true + ) + end + end + + local tabletData = self.tabletData + for k, v in ipairs(self.categories) do + local width + if v.columns <= 2 then + width = v.x1 + else + width = (v.columns - 1)*20 + for i = 1, v.columns do + width = width + v['x' .. i] + end + end + if self.width < width then + self.width = width + end + end + + local good = false + local lastTitle = true + for k, v in ipairs(self.categories) do + if lastTitle then + v.hideBlankLine = true + lastTitle = false + end + if v:Display(self.tablet) and (not v.isTitle or not self.tablet.registration.hideWhenEmpty or next(self.categories, k)) then + good = true + end + if v.isTitle then + lastTitle = true + end + end + if not good then + if self.tablet == tooltip or not self.tablet.registration.hideWhenEmpty then + local width + local info = new( + 'hideBlankLine', true, + 'text', self.title, + 'justify', "CENTER", + 'font', GameTooltipHeaderText, + 'isTitle', true + ) + local cat = self:AddCategory(info) + del(info) + self.width = self.categories[#self.categories].x1 + cat:Display(self.tablet) + else + self.tablet:__Hide() + self.tablet.tmpHidden = true + end + else + self.tablet:__Show() + self.tablet.tmpHidden = nil + end + end + TabletData.Display = wrap(TabletData.Display, "TabletData:Display") + + function TabletData:AddCategory(info, index) + local made = false + if not info then + made = true + info = new() + end + local cat = Category:new(self, info) + if index then + table.insert(self.categories, index, cat) + else + self.categories[#self.categories+1] = cat + end + if made then + del(info) + end + return cat + end + TabletData.AddCategory = wrap(TabletData.AddCategory, "TabletData:AddCategory") + + function TabletData:SetHint(hint) + self.hint = hint + end + TabletData.SetHint = wrap(TabletData.SetHint, "TabletData:SetHint") + + function TabletData:SetTitle(title) + self.title = title or "Title" + end + TabletData.SetTitle = wrap(TabletData.SetTitle, "TabletData:SetTitle") + + function TabletData:SetTitleColor(r, g, b) + self.titleR = r + self.titleG = g + self.titleB = b + end + TabletData.SetTitleColor = wrap(TabletData.SetTitleColor, "TabletData:SetTitleColor") +end +do + local Category_mt = { __index = Category } + function Category:new(tabletData, info, superCategory) + local self = copy(info) + if superCategory and not self.noInherit then + self.superCategory = superCategory.superCategory + for k, v in pairs(superCategory) do + if k:find("^child_") then + local k = strsub(k, 7) + if self[k] == nil then + self[k] = v + end + end + end + self.columns = superCategory.columns + else + self.superCategory = self + end + self.tabletData = tabletData + self.lines = new() + if not self.columns then + self.columns = 1 + end + for i = 1, self.columns do + self['x' .. i] = 0 + end + setmetatable(self, Category_mt) + self.lastWasTitle = nil + local good = self.text + if not good then + for i = 2, self.columns do + if self['text' .. i] then + good = true + break + end + end + end + if good then + local x = new( + 'category', category, + 'text', self.text, + 'fakeChild', true, + 'func', self.func, + 'onEnterFunc', self.onEnterFunc, + 'onLeaveFunc', self.onLeaveFunc, + 'hasCheck', self.hasCheck, + 'checked', self.checked, + 'checkIcon', self.checkIcon, + 'isRadio', self.isRadio, + 'font', self.font, + 'size', self.size, + 'wrap', self.wrap, + 'catStart', true, + 'indentation', self.indentation, + 'noInherit', true, + 'justify', self.justify, + 'isTitle', self.isTitle + ) + local i = 1 + while true do + local k = 'arg' .. i + local v = self[k] + if v == nil then + break + end + x[k] = v + i = i + 1 + end + i = 1 + while true do + local k = 'onEnterArg' .. i + local v = self[k] + if v == nil then + break + end + x[k] = v + i = i + 1 + end + i = 1 + while true do + local k = 'onLeaveArg' .. i + local v = self[k] + if v == nil then + break + end + x[k] = v + i = i + 1 + end + if self.isTitle then + x.textR = self.textR or 1 + x.textG = self.textG or 0.823529 + x.textB = self.textB or 0 + else + x.textR = self.textR or 1 + x.textG = self.textG or 1 + x.textB = self.textB or 1 + end + for i = 2, self.columns do + x['text' .. i] = self['text' .. i] + x['text' .. i .. 'R'] = self['text' .. i .. 'R'] or self['textR' .. i] or 1 + x['text' .. i .. 'G'] = self['text' .. i .. 'G'] or self['textG' .. i] or 1 + x['text' .. i .. 'B'] = self['text' .. i .. 'B'] or self['textB' .. i] or 1 + x['font' .. i] = self['font' .. i] + x['size' .. i] = self['size' .. i] + x['justify' .. i] = self['justify' .. i] + end + if self.checkIcon and self.checkIcon:find("^Interface\\Icons\\") then + x.checkCoordLeft = self.checkCoordLeft or 0.05 + x.checkCoordRight = self.checkCoordRight or 0.95 + x.checkCoordTop = self.checkCoordTop or 0.05 + x.checkCoordBottom = self.checkCoordBottom or 0.95 + else + x.checkCoordLeft = self.checkCoordLeft or 0 + x.checkCoordRight = self.checkCoordRight or 1 + x.checkCoordTop = self.checkCoordTop or 0 + x.checkCoordBottom = self.checkCoordBottom or 1 + end + x.checkColorR = self.checkColorR or 1 + x.checkColorG = self.checkColorG or 1 + x.checkColorB = self.checkColorB or 1 + self:AddLine(x) + del(x) + self.lastWasTitle = true + end + return self + end + Category.new = wrap(Category.new, "Category:new") + + function Category:del() + local prev = garbageLine + for k, v in pairs(self.lines) do + v:del() + end + del(self.lines) + del(self) + end + Category.del = wrap(Category.del, "Category:del") + + function Category:AddLine(...) + self.lastWasTitle = nil + local line + local k1 = ... + if type(k1) == "table" then + local k2 = select(2, ...) + Line:new(self, k1, k2) + else + local info = new(...) + Line:new(self, info) + info = del(info) + end + end + Category.AddLine = wrap(Category.AddLine, "Category:AddLine") + + function Category:AddCategory(...) + local lastWasTitle = self.lastWasTitle + self.lastWasTitle = nil + local info + local k1 = ... + if type(k1) == "table" then + info = k1 + else + info = new(...) + end + if lastWasTitle or #self.lines == 0 then + info.hideBlankLine = true + end + local cat = Category:new(self.tabletData, info, self) + self.lines[#self.lines+1] = cat + if info ~= k1 then + info = del(info) + end + return cat + end + Category.AddCategory = wrap(Category.AddCategory, "Category:AddCategory") + + function Category:HasChildren() + local hasChildren = false + for k, v in ipairs(self.lines) do + if v.HasChildren then + if v:HasChildren() then + return true + end + end + if not v.fakeChild then + return true + end + end + return false + end + Category.HasChildren = wrap(Category.HasChildren, "Category:HasChildren") + + local lastWasTitle = false + function Category:Display(tablet) + if not self.isTitle and not self.showWithoutChildren and not self:HasChildren() then + return false + end + if not self.hideBlankLine and not lastWasTitle then + local info = new( + 'blank', true, + 'fakeChild', true, + 'noInherit', true + ) + self:AddLine(info, 1) + del(info) + end + local good = false + if #self.lines > 0 then + self.tabletData.id = self.tabletData.id + 1 + self.id = self.tabletData.id + for k, v in ipairs(self.lines) do + if v:Display(tablet) then + good = true + end + end + end + lastWasTitle = self.isTitle + return good + end + Category.Display = wrap(Category.Display, "Category:Display") +end +do + local Line_mt = { __index = Line } + function Line:new(category, info, position) + local self = copy(info) + if not info.noInherit then + for k, v in pairs(category) do + if k:find("^child_") then + local k = strsub(k, 7) + if self[k] == nil then + self[k] = v + end + end + end + end + self.category = category + if position then + table.insert(category.lines, position, self) + else + category.lines[#category.lines+1] = self + end + setmetatable(self, Line_mt) + local n = category.tabletData.num_lines + 1 + category.tabletData.num_lines = n + if n == 10 then + category.tabletData:checkMinWidth() + end + local columns = category.columns + if columns == 1 then + if not self.justify then + self.justify = "LEFT" + end + elseif columns == 2 then + self.justify = "LEFT" + self.justify2 = "RIGHT" + if self.wrap then + self.wrap2 = false + end + else + for i = 2, columns-1 do + if not self['justify' .. i] then + self['justify' .. i] = "CENTER" + end + end + if not self.justify then + self.justify = "LEFT" + end + if not self['justify' .. columns] then + self['justify' .. columns] = "RIGHT" + end + if self.wrap then + for i = 2, columns do + self['wrap' .. i] = false + end + else + for i = 2, columns do + if self['wrap' .. i] then + for j = i+1, columns do + self['wrap' .. i] = false + end + break + end + end + end + end + if not self.indentation or self.indentation < 0 then + self.indentation = 0 + end + if not self.font then + self.font = GameTooltipText + end + for i = 2, columns do + if not self['font' .. i] then + self['font' .. i] = self.font + end + end + if not self.size then + self.size = select(2,self.font:GetFont()) + end + for i = 2, columns do + if not self['size' .. i] then + self['size' .. i] = select(2,self['font' .. i]:GetFont()) + end + end + if self.checkIcon and self.checkIcon:find("^Interface\\Icons\\") then + if not self.checkCoordLeft then + self.checkCoordLeft = 0.05 + end + if not self.checkCoordRight then + self.checkCoordRight = 0.95 + end + if not self.checkCoordTop then + self.checkCoordTop = 0.05 + end + if not self.checkCoordBottom then + self.checkCoordBottom = 0.95 + end + else + if not self.checkCoordLeft then + self.checkCoordLeft = 0 + end + if not self.checkCoordRight then + self.checkCoordRight = 1 + end + if not self.checkCoordTop then + self.checkCoordTop = 0 + end + if not self.checkCoordBottom then + self.checkCoordBottom = 1 + end + end + if not self.checkColorR then + self.checkColorR = 1 + end + if not self.checkColorG then + self.checkColorG = 1 + end + if not self.checkColorB then + self.checkColorB = 1 + end + + local fontSizePercent = category.tabletData.tablet.fontSizePercent + local w = 0 + self.checkWidth = 0 + testString = category.tabletData.tablet.buttons[1].col1 + if self.text then + if not self.wrap then + local testWidth = getTestWidth(self.font, self.size * fontSizePercent, self.text) + local checkWidth = self.hasCheck and self.size * fontSizePercent or 0 + self.checkWidth = checkWidth + w = testWidth + self.indentation * fontSizePercent + checkWidth + if category.superCategory.x1 < w then + category.superCategory.x1 = w + end + else + if columns == 1 then + local testWidth = getTestWidth(self.font, self.size * fontSizePercent, self.text) + local checkWidth = self.hasCheck and self.size * fontSizePercent or 0 + self.checkWidth = checkWidth + w = testWidth + self.indentation * fontSizePercent + checkWidth + if w > (MIN_TOOLTIP_SIZE - 20) * fontSizePercent then + w = (MIN_TOOLTIP_SIZE - 20) * fontSizePercent + end + else + w = MIN_TOOLTIP_SIZE * fontSizePercent / 2 + end + if category.superCategory.x1 < w then + category.superCategory.x1 = w + end + end + end + if columns == 2 and self.text2 then + if not self.wrap2 then + local testWidth = getTestWidth(self.font2, self.size2 * fontSizePercent, self.text2) + w = w + 40 * fontSizePercent + testWidth + if category.superCategory.x1 < w then + category.superCategory.x1 = w + end + else + w = w + 40 * fontSizePercent + MIN_TOOLTIP_SIZE * fontSizePercent / 2 + if category.superCategory.x1 < w then + category.superCategory.x1 = w + end + end + elseif columns >= 3 then + if self.text2 then + if not self.wrap2 then + local testWidth = getTestWidth(self.font2, self.size2 * fontSizePercent, self.text2) + local w = testWidth + if category.superCategory.x2 < w then + category.superCategory.x2 = w + end + else + local w = MIN_TOOLTIP_SIZE / 2 + if category.superCategory.x2 < w then + category.superCategory.x2 = w + end + end + end + + for i = 3, columns do + local text = self['text' .. i] + if text then + local x_i = 'x' .. i + if not self['wrap' .. i] then + local testWidth = getTestWidth(self['font' .. i], self['size' .. i] * fontSizePercent, text) + local w = testWidth + if category.superCategory[x_i] < w then + category.superCategory[x_i] = w + end + else + local w = MIN_TOOLTIP_SIZE / 2 + if category.superCategory[x_i] < w then + category.superCategory[x_i] = w + end + end + end + end + end + return self + end + Line.new = wrap(Line.new, "Line:new") + + function Line:del() + del(self) + end + Line.del = wrap(Line.del, "Line:del") + + function Line:Display(tablet) + tablet:AddLine(self) + return true + end + Line.Display = wrap(Line.Display, "Line:Display") +end + +local fake_ipairs +do + local function iter(tmp, i) + i = i + 1 + local x = tmp[i] + tmp[i] = nil + if x then + return i, x + end + end + + local tmp = {} + function fake_ipairs(...) + for i = 1, select('#', ...) do + tmp[i] = select(i, ...) + end + return iter, tmp, 0 + end + fake_ipairs = wrap(fake_ipairs, "fake_ipairs") +end + +local function argunpack(t, key, i) + if not i then + i = 1 + end + local k = key .. i + local v = t[k] + if v then + return v, argunpack(t, key, i+1) + end +end +argunpack = wrap(argunpack, "argunpack") + + +local delstring, newstring +do + local cache = {} + function delstring(t) + cache[#cache+1] = t + t:SetText(nil) + t:ClearAllPoints() + t:Hide() + t:SetParent(UIParent) + return nil + end + delstring = wrap(delstring, "delstring") + function newstring(parent) + if #cache ~= 0 then + local t = cache[#cache] + cache[#cache] = nil + t:Show() + t:SetParent(parent) + return t + end + local t = parent:CreateFontString(nil, "ARTWORK") + return t + end + newstring = wrap(newstring, "newstring") +end + +local function button_OnEnter(this, ...) + if type(this.self:GetScript("OnEnter")) == "function" then + this.self:GetScript("OnEnter")(this.self, ...) + end + this.highlight:Show() + if this.onEnterFunc then + local success, ret = pcall(this.onEnterFunc, argunpack(this, 'onEnterArg')) + if not success then + geterrorhandler()(ret) + end + end +end +button_OnEnter = wrap(button_OnEnter, "button_OnEnter") + +local function button_OnLeave(this, ...) + if type(this.self:GetScript("OnLeave")) == "function" then + this.self:GetScript("OnLeave")(this.self, ...) + end + this.highlight:Hide() + if this.onLeaveFunc then + local success, ret = pcall(this.onLeaveFunc, argunpack(this, 'onLeaveArg')) + if not success then + geterrorhandler()(ret) + end + end +end +button_OnLeave = wrap(button_OnLeave, "button_OnLeave") +local lastMouseDown +local function button_OnClick(this, arg1, ...) + if this.self:HasScript("OnClick") and type(this.self:GetScript("OnClick")) == "function" then + this.self:GetScript("OnClick")(this.self, arg1, ...) + end + if arg1 == "RightButton" then + if this.self:HasScript("OnClick") and type(this.self:GetScript("OnClick")) == "function" then + this.self:GetScript("OnClick")(this.self, arg1, ...) + end + elseif arg1 == "LeftButton" then + if this.self.preventClick == nil or GetTime() > this.self.preventClick and GetTime() < lastMouseDown + 0.5 then + this.self.preventClick = nil + this.self.updating = true + this.self.preventRefresh = true + local success, ret = pcall(this.func, argunpack(this, 'arg')) + if not success then + geterrorhandler()(ret) + end + if this.self and this.self.registration then + this.self.preventRefresh = false + this.self:children() + this.self.updating = false + end + end + end +end +button_OnClick = wrap(button_OnClick, "button_OnClick") +local function button_OnMouseUp(this, arg1, ...) + if this.self:HasScript("OnMouseUp") and type(this.self:GetScript("OnMouseUp")) == "function" then + this.self:GetScript("OnMouseUp")(this.self, arg1, ...) + end + if arg1 ~= "RightButton" then + if this.clicked then + local a,b,c,d,e = this.check:GetPoint(1) + this.check:SetPoint(a,b,c,d-1,e+1) + this.clicked = false + end + end +end +button_OnMouseUp = wrap(button_OnMouseUp, "button_OnMouseUp") +local function button_OnMouseDown(this, arg1, ...) + if this.self:HasScript("OnMouseDown") and type(this.self:GetScript("OnMouseDown")) == "function" then + this.self:GetScript("OnMouseDown")(this.self, arg1, ...) + end + lastMouseDown = GetTime() + if arg1 ~= "RightButton" then + local a,b,c,d,e = this.check:GetPoint(1) + this.check:SetPoint(a,b,c,d+1,e-1) + this.clicked = true + end +end +button_OnMouseDown = wrap(button_OnMouseDown, "button_OnMouseDown") +local function button_OnDragStart(this, ...) + local parent = this:GetParent() and this:GetParent().tablet + if parent:GetScript("OnDragStart") then + return parent:GetScript("OnDragStart")(parent, ...) + end +end +button_OnDragStart = wrap(button_OnDragStart, "button_OnDragStart") +local function button_OnDragStop(this, ...) + local parent = this:GetParent() and this:GetParent().tablet + if parent:GetScript("OnDragStop") then + return parent:GetScript("OnDragStop")(parent, ...) + end +end +button_OnDragStop = wrap(button_OnDragStop, "button_OnDragStop") + +local num_buttons = 0 +local function NewLine(self) + if self.maxLines <= self.numLines then + self.maxLines = self.maxLines + 1 + num_buttons = num_buttons + 1 + local button = CreateFrame("Button", "Tablet20Button" .. num_buttons, self.scrollChild) + button:SetFrameLevel(12) + button.indentation = 0 + local check = button:CreateTexture(nil, "ARTWORK") + local col1 = newstring(button) + testString = col1 + local highlight = button:CreateTexture(nil, "BACKGROUND") + highlight:SetTexture("Interface\\QuestFrame\\UI-QuestTitleHighlight") + button.highlight = highlight + highlight:SetBlendMode("ADD") + highlight:SetAllPoints(button) + highlight:Hide() + self.buttons[#self.buttons+1] = button + button.check = check + button.col1 = col1 + col1:SetWidth(0) + if self.maxLines == 1 then + col1:SetFontObject(GameTooltipHeaderText) + col1:SetJustifyH("CENTER") + button:SetPoint("TOPLEFT", self.scrollFrame, "TOPLEFT", 3, -5) + else + col1:SetFontObject(GameTooltipText) + button:SetPoint("TOPLEFT", self.buttons[self.maxLines - 1], "BOTTOMLEFT", 0, -2) + end + button:SetScript("OnEnter", button_OnEnter) + button:SetScript("OnLeave", button_OnLeave) + button.check = check + button.self = self + button:SetPoint("RIGHT", self.scrollFrame, "RIGHT", -7, 0) + check.shown = false + check:SetPoint("TOPLEFT", button, "TOPLEFT") + col1:SetPoint("TOPLEFT", check, "TOPLEFT") + local size = select(2,GameTooltipText:GetFont()) + check:SetHeight(size * 1.5) + check:SetWidth(size * 1.5) + check:SetTexture("Interface\\Buttons\\UI-CheckBox-Check") + check:SetAlpha(0) + if not button.clicked then + button:SetScript("OnMouseWheel", self:GetScript("OnMouseWheel")) + button:EnableMouseWheel(true) + button:Hide() + end + check:Show() + col1:Hide() + end +end +NewLine = wrap(NewLine, "NewLine") + +local function RecalculateTabletHeight(detached) + detached.height_ = nil + if detached.registration and detached.registration.positionFunc then + local height = detached:GetHeight() + if height > 0 then + detached.height_ = height + else + local top, bottom + for i = 1, detached:GetNumPoints() do + local a,b,c,d,e = detached:GetPoint(i) + + if a:find("^TOP") then + if c:find("^TOP") then + top = b:GetTop() + elseif c:find("^BOTTOM") then + top = b:GetBottom() + else + top = select(2,b:GetCenter()) + end + if top then + top = top + e + end + elseif a:find("^BOTTOM") then + if c:find("^TOP") then + bottom = b:GetTop() + elseif c:find("^BOTTOM") then + bottom = b:GetBottom() + else + bottom = select(2,b:GetCenter()) + end + if bottom then + bottom = bottom + e + end + end + end + if top and bottom then + detached.height_ = top - bottom + end + end + end +end +RecalculateTabletHeight = wrap(RecalculateTabletHeight, "RecalculateTabletHeight") + +local function GetTooltipHeight(self) + RecalculateTabletHeight(self) + if self.height_ then + local height = self:GetTop() and self:GetBottom() and self:GetTop() - self:GetBottom() or self:GetHeight() + if height == 0 then + height = self.height_ + end + return height + end + if self.registration.maxHeight then + return self.registration.maxHeight + end + if self == tooltip then + return GetScreenHeight()*3/4 + else + return GetScreenHeight()*2/3 + end +end +GetTooltipHeight = wrap(GetTooltipHeight, "GetTooltipHeight") + +local overFrame = nil +local detachedTooltips = {} +local AcquireDetachedFrame, ReleaseDetachedFrame +local function AcquireFrame(self, registration, data, detachedData) + if not detachedData then + detachedData = data + end + if tooltip then + tooltip.data = data + tooltip.detachedData = detachedData + local fontSizePercent = tooltip.data and tooltip.data.fontSizePercent or 1 + local transparency = tooltip.data and tooltip.data.transparency or 0.75 + local r = tooltip.data and tooltip.data.r or 0 + local g = tooltip.data and tooltip.data.g or 0 + local b = tooltip.data and tooltip.data.b or 0 + tooltip:SetFontSizePercent(fontSizePercent) + tooltip:SetTransparency(transparency) + tooltip:SetColor(r, g, b) + tooltip:SetParent(GetMainFrame()) + tooltip:SetFrameStrata(registration.strata or "TOOLTIP") + tooltip:SetFrameLevel(10) + for _,frame in fake_ipairs(tooltip:GetChildren()) do + frame:SetFrameLevel(12) + end + else + tooltip = CreateFrame("Frame", "Tablet20Frame", UIParent) + tooltip:SetParent(GetMainFrame()) + self.tooltip = tooltip + tooltip.data = data + tooltip.detachedData = detachedData + tooltip:EnableMouse(true) + tooltip:EnableMouseWheel(true) + tooltip:SetFrameStrata(registration.strata or "TOOLTIP") + tooltip:SetFrameLevel(10) + local backdrop = new( + 'bgFile', "Interface\\Buttons\\WHITE8X8", + 'edgeFile', "Interface\\Tooltips\\UI-Tooltip-Border", + 'tile', true, + 'tileSize', 16, + 'edgeSize', 16, + 'insets', new( + 'left', 5, + 'right', 5, + 'top', 5, + 'bottom', 5 + ) + ) + tooltip:SetBackdrop(backdrop) + del(backdrop.insets) + del(backdrop) + tooltip:SetBackdropColor(0, 0, 0, 1) + + tooltip.numLines = 0 + tooltip.owner = nil + tooltip.fontSizePercent = tooltip.data and tooltip.data.fontSizePercent or 1 + tooltip.maxLines = 0 + tooltip.buttons = {} + tooltip.transparency = tooltip.data and tooltip.data.transparency or 0.75 + tooltip:SetBackdropColor(0, 0, 0, tooltip.transparency) + tooltip:SetBackdropBorderColor(1, 1, 1, tooltip.transparency) + + tooltip:SetScript("OnUpdate", function(this, elapsed) + if not tooltip.updating and (not tooltip.enteredFrame or (overFrame and not MouseIsOver(overFrame))) then + tooltip.scrollFrame:SetVerticalScroll(0) + tooltip.slider:SetValue(0) + tooltip:Hide() + tooltip.registration.tooltip = nil + tooltip.registration = nil + overFrame = nil + end + end) + + tooltip:SetScript("OnEnter", function(this) + if tooltip.clickable then + tooltip.enteredFrame = true + overFrame = nil + end + end) + + tooltip:SetScript("OnLeave", function(this) + if not tooltip.updating then + tooltip.enteredFrame = false + end + end) + + tooltip:SetScript("OnMouseWheel", function(this, arg1) + tooltip.updating = true + tooltip:Scroll(arg1 < 0) + tooltip.updating = false + end) + + local scrollFrame = CreateFrame("ScrollFrame", "Tablet20FrameScrollFrame", tooltip) + scrollFrame:SetFrameLevel(11) + local scrollChild = CreateFrame("Frame", "Tablet20FrameScrollChild", scrollFrame) + scrollChild.tablet = tooltip + scrollFrame:SetScrollChild(scrollChild) + tooltip.scrollFrame = scrollFrame + tooltip.scrollChild = scrollChild + scrollFrame:SetPoint("TOPLEFT", 5, -5) + scrollFrame:SetPoint("TOPRIGHT", -5, -5) + scrollFrame:SetPoint("BOTTOMLEFT", 5, 5) + scrollFrame:SetPoint("BOTTOMRIGHT", -5, 5) + scrollChild:SetWidth(1) + scrollChild:SetHeight(1) + local slider = CreateFrame("Slider", "Tablet20FrameSlider", scrollFrame) + tooltip.slider = slider + slider:SetOrientation("VERTICAL") + slider:SetMinMaxValues(0, 1) + slider:SetValueStep(0.001) + slider:SetValue(0) + slider:SetWidth(8) + slider:SetPoint("TOPRIGHT", 0, 0) + slider:SetPoint("BOTTOMRIGHT", 0, 0) + slider:SetBackdrop(new( + 'bgFile', "Interface\\Buttons\\UI-SliderBar-Background", + 'edgeFile', "Interface\\Buttons\\UI-SliderBar-Border", + 'tile', true, + 'edgeSize', 8, + 'tileSize', 8, + 'insets', new( + 'left', 3, + 'right', 3, + 'top', 3, + 'bottom', 3 + ) + )) + slider:SetThumbTexture("Interface\\Buttons\\UI-SliderBar-Button-Vertical") + slider:SetScript("OnEnter", tooltip:GetScript("OnEnter")) + slider:SetScript("OnLeave", tooltip:GetScript("OnLeave")) + slider.tablet = tooltip + slider:SetScript("OnValueChanged", function(this) + local max = this.tablet.scrollChild:GetHeight() - this.tablet:GetHeight() + + local val = this:GetValue() * max + + if math.abs(this.tablet.scrollFrame:GetVerticalScroll() - val) < 1 then + return + end + + this.tablet.scrollFrame:SetVerticalScroll(val) + end) + + NewLine(tooltip) + + function tooltip:SetOwner(o) + self:Hide(o) + self.owner = o + end + tooltip.SetOwner = wrap(tooltip.SetOwner, "tooltip:SetOwner") + + function tooltip:IsOwned(o) + return self.owner == o + end + tooltip.IsOwned = wrap(tooltip.IsOwned, "tooltip:IsOwned") + + function tooltip:ClearLines(hide) + CleanCategoryPool(self) + for i = 1, self.numLines do + local button = self.buttons[i] + local check = button.check + if not button.clicked or hide then + button:Hide() + end + check.shown = false + check:SetAlpha(0) + end + self.numLines = 0 + end + tooltip.ClearLines = wrap(tooltip.ClearLines, "tooltip:ClearLines") + + function tooltip:NumLines() + return self.numLines + end + + local lastWidth + local old_tooltip_Hide = tooltip.Hide + tooltip.__Hide = old_tooltip_Hide + function tooltip:Hide(newOwner) + if self == tooltip or newOwner == nil then + old_tooltip_Hide(self) + end + self:ClearLines(true) + self.owner = nil + self.lastWidth = nil + self.tmpHidden = nil + end + tooltip.Hide = wrap(tooltip.Hide, "tooltip:Hide") + + local old_tooltip_Show = tooltip.Show + tooltip.__Show = old_tooltip_Show + function tooltip:Show(tabletData) + if self.owner == nil or self.notInUse then + return + end + if not self.tmpHidden then + old_tooltip_Show(self) + end + + testString = self.buttons[1].col1 + + local maxWidth = tabletData and tabletData.width or self:GetWidth() - 20 + local hasWrap = false + local numColumns + + local height = 20 + self:SetWidth(maxWidth + 20) + + for i = 1, self.numLines do + local button = self.buttons[i] + local col1 = button.col1 + local col2 = button.col2 + local check = button.check + button:SetWidth(maxWidth) + button:SetHeight(col2 and math.max(col1:GetHeight(), col2:GetHeight()) or col1:GetHeight()) + height = height + button:GetHeight() + 2 + if i == 1 then + button:SetPoint("TOPLEFT", self.scrollChild, "TOPLEFT", 5, -5) + else + button:SetPoint("TOPLEFT", self.buttons[i - 1], "BOTTOMLEFT", 0, -2) + end + if button.clicked then + check:SetPoint("TOPLEFT", button, "TOPLEFT", button.indentation * self.fontSizePercent + (check.width - check:GetWidth()) / 2 + 1, -1) + else + check:SetPoint("TOPLEFT", button, "TOPLEFT", button.indentation * self.fontSizePercent + (check.width - check:GetWidth()) / 2, 0) + end + button:Show() + end + self.scrollFrame:SetFrameLevel(11) + self.scrollChild:SetWidth(maxWidth) + self.scrollChild:SetHeight(height) + local maxHeight = GetTooltipHeight(self) + if height > maxHeight then + height = maxHeight + self.slider:Show() + else + self.slider:Hide() + end + self:SetHeight(height) + self.scrollFrame:SetScrollChild(self.scrollChild) + local val = self.scrollFrame:GetVerticalScroll() + local max = self.scrollChild:GetHeight() - self:GetHeight() + if val > max then + val = max + end + if val < 0 then + val = 0 + end + self.scrollFrame:SetVerticalScroll(val) + self.slider:SetValue(val/max) + end + tooltip.Show = wrap(tooltip.Show, "tooltip:Show") + + function tooltip:AddLine(info) + local category = info.category.superCategory + local maxWidth = category.tabletData.width + local text = info.blank and "\n" or info.text + local id = info.id + local func = info.func + local checked = info.checked + local isRadio = info.isRadio + local checkTexture = info.checkTexture + local fontSizePercent = self.fontSizePercent + if not info.font then + info.font = GameTooltipText + end + if not info.size then + info.size = select(2,info.font:GetFont()) + end + local catStart = false + local columns = category and category.columns or 1 + local x_total = 0 + local x1, x2 + if category then + for i = 1, category.columns do + x_total = x_total + category['x' .. i] + end + x1, x2 = category.x1, category.x2 + else + x1, x2 = 0, 0 + end + + self.numLines = self.numLines + 1 + NewLine(self) + local num = self.numLines + + local button = self.buttons[num] + button:Show() + button.col1:Show() + button.indentation = info.indentation + local col1 = button.col1 + local check = button.check + do -- if columns >= 1 then + col1:SetWidth(0) + col1:SetFontObject(info.font) + local font,_,flags = info.font:GetFont() + col1:SetFont(font, info.size * fontSizePercent, flags) + col1:SetText(text) + col1:SetJustifyH(info.justify) + col1:Show() + + if info.textR and info.textG and info.textB then + col1:SetTextColor(info.textR, info.textG, info.textB) + else + col1:SetTextColor(1, 0.823529, 0) + end + if columns < 2 then + local i = 2 + while true do + local col = button['col' .. i] + if col then + button['col' .. i] = delstring(col) + else + break + end + i = i + 1 + end + else + local i = 2 + while true do + local col = button['col' .. i] + if not col then + button['col' .. i] = newstring(button) + col = button['col' .. i] + end + col:SetFontObject(info['font' .. i]) + col:SetText(info['text' .. i]) + col:Show() + local r,g,b = info['text' .. i .. 'R'] + if r then + g = info['text' .. i .. 'G'] + if g then + b = info['text' .. i .. 'B'] + end + end + if b then + col:SetTextColor(r, g, b) + else + col:SetTextColor(1, 0.823529, 0) + end + local a,_,b = info.font2:GetFont() + col:SetFont(a, info['size' .. i] * fontSizePercent, b) + col:SetJustifyH(info['justify' .. i]) + if columns == i then + if i == 2 then + col:SetPoint("TOPLEFT", col1, "TOPRIGHT", 40 * fontSizePercent, 0) + col:SetPoint("TOPRIGHT", button, "TOPRIGHT", -5, 0) + else + local col2 = button.col2 + col2:ClearAllPoints() + col2:SetPoint("TOPLEFT", col1, "TOPRIGHT", (20 - info.indentation) * fontSizePercent, 0) + end + i = i + 1 + while true do + local col = button['col' .. i] + if col then + button['col' .. i] = delstring(col) + else + break + end + i = i + 1 + end + break + end + i = i + 1 + end + end + end + + check:SetWidth(info.size * fontSizePercent) + check:SetHeight(info.size * fontSizePercent) + check.width = info.size * fontSizePercent + if info.hasCheck then + check.shown = true + check:Show() + if isRadio then + check:SetTexture(info.checkIcon or "Interface\\Buttons\\UI-RadioButton") + if info.checked then + check:SetAlpha(1) + check:SetTexCoord(0.25, 0.5, 0, 1) + else + check:SetAlpha(self.transparency) + check:SetTexCoord(0, 0.25, 0, 1) + end + check:SetVertexColor(1, 1, 1) + else + if info.checkIcon then + check:SetTexture(info.checkIcon) + check:SetTexCoord(info.checkCoordLeft, info.checkCoordRight, info.checkCoordTop, info.checkCoordBottom) + check:SetVertexColor(info.checkColorR, info.checkColorG, info.checkColorB) + else + check:SetTexture("Interface\\Buttons\\UI-CheckBox-Check") + check:SetWidth(info.size * fontSizePercent * 1.5) + check:SetHeight(info.size * fontSizePercent * 1.5) + check.width = info.size * fontSizePercent * 1.2 + check:SetTexCoord(0, 1, 0, 1) + check:SetVertexColor(1, 1, 1) + end + check:SetAlpha(info.checked and 1 or 0) + end + col1:SetPoint("TOPLEFT", check, "TOPLEFT", check.width, 0) + else + col1:SetPoint("TOPLEFT", check, "TOPLEFT") + end + local col2 = button.col2 + if columns == 1 then + col1:SetWidth(maxWidth) + elseif columns == 2 then + if info.wrap then + col1:SetWidth(maxWidth - col2:GetWidth() - 40 * fontSizePercent) + col2:SetWidth(0) + elseif info.wrap2 then + col1:SetWidth(0) + col2:SetWidth(maxWidth - col1:GetWidth() - 40 * fontSizePercent) + else + col1:SetWidth(0) + col2:SetWidth(0) + end + col2:ClearAllPoints() + col2:SetPoint("TOPRIGHT", button, "TOPRIGHT", 0, 0) + if not info.text2 then + col1:SetJustifyH(info.justify or "LEFT") + end + else + col1:SetWidth(x1 - info.checkWidth) + col2:SetWidth(x2) + local num = (category.tabletData.width - x_total) / (columns - 1) + col2:SetPoint("TOPLEFT", col1, "TOPRIGHT", num - info.indentation * fontSizePercent, 0) + local last = col2 + for i = 3, category.columns do + local col = button['col' .. i] + col:SetWidth(category['x' .. i]) + col:SetPoint("TOPLEFT", last, "TOPRIGHT", num, 0) + last = col + end + end + button.func = nil + button.onEnterFunc = nil + button.onLeaveFunc = nil + button:SetFrameLevel(12) -- hack suggested on forum. Added 06/17/2007. (hC) + if not self.locked or IsAltKeyDown() then + local func = info.func + if func then + if type(func) == "string" then + if type(info.arg1) ~= "table" then + Tablet:error("Cannot call method " .. info.func .. " on a non-table") + end + func = info.arg1[func] + if type(func) ~= "function" then + Tablet:error("Method " .. info.func .. " nonexistant") + end + else + if type(func) ~= "function" then + Tablet:error("func must be a function or method") + end + end + button.func = func + local i = 1 + while true do + local k = 'arg' .. i + if button[k] ~= nil then + button[k] = nil + else + break + end + i = i + 1 + end + i = 1 + while true do + local k = 'arg' .. i + local v = info[k] + if v == nil then + break + end + button[k] = v + i = i + 1 + end + local onEnterFunc = info.onEnterFunc + if onEnterFunc then + if type(onEnterFunc) == "string" then + if type(info.onEnterArg1) ~= "table" then + Tablet:error("Cannot call method " .. info.onEnterFunc .. " on a non-table") + end + onEventFunc = info.onEnterArg1[onEnterFunc] + if type(onEnterFunc) ~= "function" then + Tablet:error("Method " .. info.onEnterFunc .. " nonexistant") + end + else + if type(onEnterFunc) ~= "function" then + Tablet:error("func must be a function or method") + end + end + button.onEnterFunc = onEnterFunc + local i = 1 + while true do + local k = 'onEnterArg' .. i + if button[k] ~= nil then + button[k] = nil + else + break + end + i = i + 1 + end + i = 1 + while true do + local k = 'onEnterArg' .. i + local v = info[k] + if v == nil then + break + end + button[k] = v + i = i + 1 + end + end + local onLeaveFunc = info.onLeaveFunc + if onLeaveFunc then + if type(onLeaveFunc) == "string" then + if type(info.onLeaveArg1) ~= "table" then + Tablet:error("Cannot call method " .. info.onLeaveFunc .. " on a non-table") + end + onLeaveFunc = info.onLeaveArg1[onLeaveFunc] + if type(onLeaveFunc) ~= "function" then + Tablet:error("Method " .. info.onLeaveFunc .. " nonexistant") + end + else + if type(onLeaveFunc) ~= "function" then + Tablet:error("func must be a function or method") + end + end + button.onLeaveFunc = onLeaveFunc + local i = 1 + while true do + local k = 'onLeaveArg' .. i + if button[k] ~= nil then + button[k] = nil + else + break + end + i = i + 1 + end + i = 1 + while true do + local k = 'onLeaveArg' .. i + local v = info[k] + if v == nil then + break + end + button[k] = v + i = i + 1 + end + end + button.self = self + button:SetScript("OnMouseUp", button_OnMouseUp) + button:SetScript("OnMouseDown", button_OnMouseDown) + button:RegisterForDrag("LeftButton") + button:SetScript("OnDragStart", button_OnDragStart) + button:SetScript("OnDragStop", button_OnDragStop) + button:SetScript("OnClick", button_OnClick) + if button.clicked then + button:SetButtonState("PUSHED") + end + button:EnableMouse(true) + else + button:SetScript("OnMouseDown", nil) + button:SetScript("OnMouseUp", nil) + button:RegisterForDrag() + button:SetScript("OnDragStart", nil) + button:SetScript("OnDragStop", nil) + button:SetScript("OnClick", nil) + button:EnableMouse(false) + end + else + button:SetScript("OnMouseDown", nil) + button:SetScript("OnMouseUp", nil) + button:RegisterForDrag() + button:SetScript("OnDragStart", nil) + button:SetScript("OnDragStop", nil) + button:SetScript("OnClick", nil) + button:EnableMouse(false) + end + end + tooltip.AddLine = wrap(tooltip.AddLine, "tooltip:AddLine") + + function tooltip:SetFontSizePercent(percent) + local data, detachedData = self.data, self.detachedData + if detachedData and detachedData.detached then + data = detachedData + end + local lastSize = self.fontSizePercent + percent = tonumber(percent) or 1 + if percent < 0.25 then + percent = 0.25 + elseif percent > 4 then + percent = 4 + end + self.fontSizePercent = percent + if data then + data.fontSizePercent = percent + end + local ratio = self.fontSizePercent / lastSize + for i = 1, self.numLines do + local button = self.buttons[i] + local j = 1 + while true do + local col = button['col' .. j] + if not col then + break + end + local font, size, flags = col:GetFont() + col:SetFont(font, size * ratio, flags) + j = j + 1 + end + local check = button.check + check.width = check.width * ratio + check:SetWidth(check:GetWidth() * ratio) + check:SetHeight(check:GetHeight() * ratio) + end + self:SetWidth((self:GetWidth() - 51) * ratio + 51) + self:SetHeight((self:GetHeight() - 51) * ratio + 51) + if self:IsShown() and self.children then + self:children() + self:Show() + end + end + tooltip.SetFontSizePercent = wrap(tooltip.SetFontSizePercent, "tooltip:SetFontSizePercent") + + function tooltip:GetFontSizePercent() + return self.fontSizePercent + end + + function tooltip:SetTransparency(alpha) + local data, detachedData = self.data, self.detachedData + if detachedData and detachedData.detached then + data = detachedData + end + self.transparency = alpha + if data then + data.transparency = alpha ~= 0.75 and alpha or nil + end + self:SetBackdropColor(self.r or 0, self.g or 0, self.b or 0, alpha) + self:SetBackdropBorderColor(1, 1, 1, alpha) + self.slider:SetBackdropColor(self.r or 0, self.g or 0, self.b or 0, alpha) + self.slider:SetBackdropBorderColor(1, 1, 1, alpha) + end + tooltip.SetTransparency = wrap(tooltip.SetTransparency, "tooltip:SetTransparency") + + function tooltip:GetTransparency() + return self.transparency + end + + function tooltip:SetColor(r, g, b) + local data, detachedData = self.data, self.detachedData + if detachedData and detachedData.detached then + data = detachedData + end + self.r = r + self.g = g + self.b = b + if data then + data.r = r ~= 0 and r or nil + data.g = g ~= 0 and g or nil + data.b = b ~= 0 and b or nil + end + self:SetBackdropColor(r or 0, g or 0, b or 0, self.transparency) + self:SetBackdropBorderColor(1, 1, 1, self.transparency) + end + tooltip.SetColor = wrap(tooltip.SetColor, "tooltip:SetColor") + + function tooltip:GetColor() + return self.r, self.g, self.b + end + + function tooltip:Scroll(down) + local val + local max = self.scrollChild:GetHeight() - self:GetHeight() + if down then + if IsShiftKeyDown() then + val = max + else + val = self.scrollFrame:GetVerticalScroll() + 36 + if val > max then + val = max + end + end + else + if IsShiftKeyDown() then + val = 0 + else + val = self.scrollFrame:GetVerticalScroll() - 36 + if val < 0 then + val = 0 + end + end + end + self.scrollFrame:SetVerticalScroll(val) + self.slider:SetValue(val/max) + end + tooltip.Scroll = wrap(tooltip.Scroll, "tooltip:Scroll") + + function tooltip.Detach(tooltip) + local owner = tooltip.owner + tooltip:Hide() + if not tooltip.detachedData then + self:error("You cannot detach if detachedData is not present") + end + tooltip.detachedData.detached = true + local detached = AcquireDetachedFrame(self, tooltip.registration, tooltip.data, tooltip.detachedData) + + detached.menu, tooltip.menu = tooltip.menu, nil + detached.runChildren = tooltip.runChildren + detached.children = tooltip.children + detached.minWidth = tooltip.minWidth + tooltip.runChildren = nil + tooltip.children = nil + tooltip.minWidth = nil + detached:SetOwner(owner) + detached:children() + detached:Show() + end + tooltip.Detach = wrap(tooltip.Detach, "tooltip:Detach") + + end + + tooltip.registration = registration + registration.tooltip = tooltip + return tooltip +end +AcquireFrame = wrap(AcquireFrame, "AcquireFrame") + +function ReleaseDetachedFrame(self, data, detachedData) + if not detachedData then + detachedData = data + end + for _, detached in ipairs(detachedTooltips) do + if detached.detachedData == detachedData then + detached.notInUse = true + detached:Hide() + detached.registration.tooltip = nil + detached.registration = nil + detached.detachedData = nil + end + end +end +ReleaseDetachedFrame = wrap(ReleaseDetachedFrame, "ReleaseDetachedFrame") + +local StartCheckingAlt, StopCheckingAlt +do + local frame + function StartCheckingAlt(func) + if not frame then + frame = CreateFrame("Frame") + frame:SetScript("OnEvent", function(this, _, modifier) + if modifier == "LALT" or modifier == "RALT" then + this.func() + end + end) + end + frame:RegisterEvent("MODIFIER_STATE_CHANGED") + frame.func = func + end + StartCheckingAlt = wrap(StartCheckingAlt, "StartCheckingAlt") + function StopCheckingAlt() + if frame then + frame:UnregisterEvent("MODIFIER_STATE_CHANGED") + end + end + StopCheckingAlt = wrap(StopCheckingAlt, "StopCheckingAlt") +end + +function AcquireDetachedFrame(self, registration, data, detachedData) + if not detachedData then + detachedData = data + end + for _, detached in ipairs(detachedTooltips) do + if detached.notInUse then + detached.data = data + detached.detachedData = detachedData + detached.notInUse = nil + local fontSizePercent = detachedData.fontSizePercent or 1 + local transparency = detachedData.transparency or 0.75 + local r = detachedData.r or 0 + local g = detachedData.g or 0 + local b = detachedData.b or 0 + detached:SetFontSizePercent(fontSizePercent) + detached:SetTransparency(transparency) + detached:SetColor(r, g, b) + detached:ClearAllPoints() + detached:SetWidth(0) + detached:SetHeight(0) + if not registration.strata then + detached:SetFrameStrata("BACKGROUND") + end + if not registration.frameLevel then + detached:SetFrameLevel(10) + for _,frame in fake_ipairs(detached:GetChildren()) do + frame:SetFrameLevel(12) + end + end + detached:SetParent(registration.parent or GetMainFrame()) + if registration.strata then + detached:SetFrameStrata(registration.strata) + end + if registration.frameLevel then + detached:SetFrameLevel(registration.frameLevel) + for _,frame in fake_ipairs(detached:GetChildren()) do + frame:SetFrameLevel(registration.frameLevel+2) + end + end + detached.height_ = nil + if registration.positionFunc then + registration.positionFunc(detached) + RecalculateTabletHeight(detached) + else + detached:SetPoint(detachedData.anchor or "CENTER", GetMainFrame(), detachedData.anchor or "CENTER", detachedData.offsetx or 0, detachedData.offsety or 0) + end + detached.registration = registration + registration.tooltip = detached + if registration.movable == false then + detached:RegisterForDrag() + else + detached:RegisterForDrag("LeftButton") + end + return detached + end + end + + if not Dewdrop and AceLibrary:HasInstance("Dewdrop-2.0") then + Dewdrop = AceLibrary("Dewdrop-2.0") + end + StartCheckingAlt(function() + for _, detached in ipairs(detachedTooltips) do + if detached:IsShown() and detached.locked then + detached:EnableMouse(IsAltKeyDown()) + detached:children() + if detached.moving then + local a1 = arg1 + arg1 = "LeftButton" + if type(detached:GetScript("OnMouseUp")) == "function" then + detached:GetScript("OnMouseUp")(detached, arg1) + end + arg1 = a1 + end + end + end + end) + if not tooltip then + AcquireFrame(self, {}) + end + local detached = CreateFrame("Frame", "Tablet20DetachedFrame" .. (#detachedTooltips + 1), GetMainFrame()) + detachedTooltips[#detachedTooltips+1] = detached + detached.notInUse = true + detached:EnableMouse(not data.locked) + detached:EnableMouseWheel(true) + detached:SetMovable(true) + detached:SetPoint(data.anchor or "CENTER", GetMainFrame(), data.anchor or "CENTER", data.offsetx or 0, data.offsety or 0) + + detached.numLines = 0 + detached.owner = nil + detached.fontSizePercent = 1 + detached.maxLines = 0 + detached.buttons = {} + detached.transparency = 0.75 + detached.r = 0 + detached.g = 0 + detached.b = 0 + detached:SetFrameStrata(registration and registration.strata or "BACKGROUND") + detached:SetBackdrop(tmp.a( + 'bgFile', "Interface\\Buttons\\WHITE8X8", + 'edgeFile', "Interface\\Tooltips\\UI-Tooltip-Border", + 'tile', true, + 'tileSize', 16, + 'edgeSize', 16, + 'insets', tmp.b( + 'left', 5, + 'right', 5, + 'top', 5, + 'bottom', 5 + ) + )) + detached.locked = detachedData.locked + detached:EnableMouse(not detached.locked) + + local width = GetScreenWidth() + local height = GetScreenHeight() + if registration and registration.movable == false then + detached:RegisterForDrag() + else + detached:RegisterForDrag("LeftButton") + end + detached:SetScript("OnDragStart", function(this) + detached:StartMoving() + detached.moving = true + end) + + detached:SetScript("OnDragStop", function(this) + detached:StopMovingOrSizing() + detached.moving = nil + detached:SetClampedToScreen(1) + detached:SetClampedToScreen(nil) + local anchor + local offsetx + local offsety + if detached:GetTop() + detached:GetBottom() < height then + anchor = "BOTTOM" + offsety = detached:GetBottom() + if offsety < 0 then + offsety = 0 + end + if offsety < MainMenuBar:GetTop() and MainMenuBar:IsVisible() then + offsety = MainMenuBar:GetTop() + end + local top = 0 + if FuBar then + for i = 1, FuBar:GetNumPanels() do + local panel = FuBar:GetPanel(i) + if panel:GetAttachPoint() == "BOTTOM" then + if panel.frame:GetTop() > top then + top = panel.frame:GetTop() + break + end + end + end + end + if offsety < top then + offsety = top + end + else + anchor = "TOP" + offsety = detached:GetTop() - height + if offsety > 0 then + offsety = 0 + end + local bottom = GetScreenHeight() + if FuBar then + for i = 1, FuBar:GetNumPanels() do + local panel = FuBar:GetPanel(i) + if panel:GetAttachPoint() == "TOP" then + if panel.frame:GetBottom() < bottom then + bottom = panel.frame:GetBottom() + break + end + end + end + end + bottom = bottom - GetScreenHeight() + if offsety > bottom then + offsety = bottom + end + end + if detached:GetLeft() + detached:GetRight() < width * 2 / 3 then + anchor = anchor .. "LEFT" + offsetx = detached:GetLeft() + if offsetx < 0 then + offsetx = 0 + end + elseif detached:GetLeft() + detached:GetRight() < width * 4 / 3 then + if anchor == "" then + anchor = "CENTER" + end + offsetx = (detached:GetLeft() + detached:GetRight() - GetScreenWidth()) / 2 + else + anchor = anchor .. "RIGHT" + offsetx = detached:GetRight() - width + if offsetx > 0 then + offsetx = 0 + end + end + detached:ClearAllPoints() + detached:SetPoint(anchor, GetMainFrame(), anchor, offsetx, offsety) + local t = detached.detachedData + if t.anchor ~= anchor or math.abs(t.offsetx - offsetx) > 8 or math.abs(t.offsety - offsety) > 8 then + detached.preventClick = GetTime() + 0.05 + end + t.anchor = anchor + t.offsetx = offsetx + t.offsety = offsety + detached:Show() + end) + + if Dewdrop then + Dewdrop:Register(detached, + 'children', function(level, value) + if not detached.registration then + return + end + if detached.menu then + if type(detached.menu) == "function" then + detached.menu(level, value) + else + Dewdrop:FeedAceOptionsTable(detached.menu) + end + if level == 1 then + Dewdrop:AddLine() + end + end + if level == 1 then + if not detached.registration.cantAttach then + Dewdrop:AddLine( + 'text', DETACH, + 'tooltipTitle', DETACH, + 'tooltipText', DETACH_DESC, + 'checked', true, + 'arg1', detached, + 'func', "Attach", + 'closeWhenClicked', true + ) + end + if not detached.registration.positionFunc then + Dewdrop:AddLine( + 'text', LOCK, + 'tooltipTitle', LOCK, + 'tooltipText', LOCK_DESC, + 'checked', detached:IsLocked(), + 'arg1', detached, + 'func', "Lock", + 'closeWhenClicked', not detached:IsLocked() + ) + end + Dewdrop:AddLine( + 'text', COLOR, + 'tooltipTitle', COLOR, + 'tooltipText', COLOR_DESC, + 'hasColorSwatch', true, + 'r', detached.r, + 'g', detached.g, + 'b', detached.b, + 'hasOpacity', true, + 'opacity', detached.transparency, + 'colorFunc', function(r, g, b, a) + detached:SetColor(r, g, b) + detached:SetTransparency(a) + end + ) + Dewdrop:AddLine( + 'text', SIZE, + 'tooltipTitle', SIZE, + 'tooltipText', SIZE_DESC, + 'hasArrow', true, + 'hasSlider', true, + 'sliderFunc', function(value) + detached:SetFontSizePercent(value) + end, + 'sliderMax', 2, + 'sliderMin', 0.5, + 'sliderStep', 0.05, + 'sliderIsPercent', true, + 'sliderValue', detached:GetFontSizePercent() + ) + Dewdrop:AddLine( + 'text', CLOSE_MENU, + 'tooltipTitle', CLOSE_MENU, + 'tooltipText', CLOSE_MENU_DESC, + 'func', function() + Dewdrop:Close() + end + ) + end + end, + 'point', function() + local x, y = detached:GetCenter() + if x < GetScreenWidth() / 2 then + if y < GetScreenHeight() / 2 then + return "BOTTOMLEFT", "BOTTOMRIGHT" + else + return "TOPLEFT", "TOPRIGHT" + end + else + if y < GetScreenHeight() / 2 then + return "BOTTOMRIGHT", "BOTTOMLEFT" + else + return "TOPRIGHT", "TOPLEFT" + end + end + end + ) + end + + local scrollFrame = CreateFrame("ScrollFrame", detached:GetName() .. "ScrollFrame", detached) + local scrollChild = CreateFrame("Frame", detached:GetName() .. "ScrollChild", scrollFrame) + scrollFrame:SetFrameLevel(11) + scrollFrame:SetScrollChild(scrollChild) + scrollChild.tablet = detached + detached.scrollFrame = scrollFrame + detached.scrollChild = scrollChild + scrollFrame:SetPoint("TOPLEFT", 5, -5) + scrollFrame:SetPoint("BOTTOMRIGHT", -5, 5) + scrollChild:SetWidth(1) + scrollChild:SetHeight(1) + local slider = CreateFrame("Slider", detached:GetName() .. "Slider", scrollFrame) + detached.slider = slider + slider:SetOrientation("VERTICAL") + slider:SetMinMaxValues(0, 1) + slider:SetValueStep(0.001) + slider:SetValue(0) + slider:SetWidth(8) + slider:SetPoint("TOPRIGHT", 0, 0) + slider:SetPoint("BOTTOMRIGHT", 0, 0) + slider:SetBackdrop(new( + 'bgFile', "Interface\\Buttons\\UI-SliderBar-Background", + 'edgeFile', "Interface\\Buttons\\UI-SliderBar-Border", + 'tile', true, + 'edgeSize', 8, + 'tileSize', 8, + 'insets', new( + 'left', 3, + 'right', 3, + 'top', 3, + 'bottom', 3 + ) + )) + slider:SetThumbTexture("Interface\\Buttons\\UI-SliderBar-Button-Vertical") + slider:SetScript("OnEnter", detached:GetScript("OnEnter")) + slider:SetScript("OnLeave", detached:GetScript("OnLeave")) + slider.tablet = detached + slider:SetScript("OnValueChanged", Tablet20FrameSlider:GetScript("OnValueChanged")) + + NewLine(detached) + + detached:SetScript("OnMouseWheel", function(this, arg1) + detached:Scroll(arg1 < 0) + end) + + detached.SetTransparency = tooltip.SetTransparency + detached.GetTransparency = tooltip.GetTransparency + detached.SetColor = tooltip.SetColor + detached.GetColor = tooltip.GetColor + detached.SetFontSizePercent = tooltip.SetFontSizePercent + detached.GetFontSizePercent = tooltip.GetFontSizePercent + detached.SetOwner = tooltip.SetOwner + detached.IsOwned = tooltip.IsOwned + detached.ClearLines = tooltip.ClearLines + detached.NumLines = tooltip.NumLines + detached.__Hide = detached.Hide + detached.__Show = detached.Show + detached.Hide = tooltip.Hide + detached.Show = tooltip.Show + local old_IsShown = detached.IsShown + function detached:IsShown() + if self.tmpHidden then + return true + else + return old_IsShown(self) + end + end + detached.AddLine = tooltip.AddLine + detached.Scroll = tooltip.Scroll + function detached:IsLocked() + return self.locked + end + function detached:Lock() + self:EnableMouse(self.locked) + self.locked = not self.locked + if self.detachedData then + self.detachedData.locked = self.locked or nil + end + self:children() + end + + function detached.Attach(detached) + if not detached then + self:error("Detached tooltip not given.") + end + if not detached.AddLine then + self:error("detached argument not a Tooltip.") + end + if not detached.owner then + self:error("Detached tooltip has no owner.") + end + if detached.notInUse then + self:error("Detached tooltip not in use.") + end + detached.menu = nil + detached.detachedData.detached = nil + detached:SetOwner(nil) + detached.notInUse = true + end + + return AcquireDetachedFrame(self, registration, data, detachedData) +end +AcquireDetachedFrame = wrap(AcquireDetachedFrame, "AcquireDetachedFrame") + +function Tablet:Close(parent) + if not parent then + if tooltip and tooltip:IsShown() then + tooltip:Hide() + tooltip.registration.tooltip = nil + tooltip.registration = nil + tooltip.enteredFrame = false + end + return + else + self:argCheck(parent, 2, "table", "string") + end + local info = self.registry[parent] + if not info then + self:error("You cannot close a tablet with an unregistered parent frame.") + end + local data = info.data + local detachedData = info.detachedData + if detachedData and detachedData.detached then + ReleaseDetachedFrame(self, data, detachedData) + elseif tooltip and tooltip.data == data then + tooltip:Hide() + if tooltip.registration then + tooltip.registration.tooltip = nil + tooltip.registration = nil + end + end + if tooltip then tooltip.enteredFrame = false end +end +Tablet.Close = wrap(Tablet.Close, "Tablet:Close") + +local function frame_children(self) + if not self.preventRefresh and self:GetParent() and self:GetParent():IsShown() then + Tablet.currentFrame = self + Tablet.currentTabletData = TabletData:new(self) + Tablet.currentTabletData.minWidth = self.minWidth + self:ClearLines() + if self.runChildren then + self.runChildren() + end + Tablet.currentTabletData:Display(Tablet.currentFrame) + self:Show(Tablet.currentTabletData) + Tablet.currentTabletData:del() + Tablet.currentTabletData = nil + Tablet.currentFrame = nil + end +end +frame_children = wrap(frame_children, "frame_children") + +function Tablet:Open(fakeParent, parent) + self:argCheck(fakeParent, 2, "table", "string") + self:argCheck(parent, 3, "nil", "table", "string") + if not parent then + parent = fakeParent + end + local info = self.registry[parent] + if not info then + self:error("You cannot open a tablet with an unregistered parent frame.") + end + local detachedData = info.detachedData + if detachedData then + for _, detached in ipairs(detachedTooltips) do + if not detached.notInUse and detached.detachedData == detachedData then + return + end + end + end + local data = info.data + local children = info.children + if not children then + return + end + local frame = AcquireFrame(self, info, data, detachedData) + frame.clickable = info.clickable + frame.menu = info.menu + frame.runChildren = info.children + frame.minWidth = info.minWidth + if not frame.children or not frame.childrenVer or frame.childrenVer < MINOR_VERSION then + frame.childrenVer = MINOR_VERSION + frame.children = frame_children + end + frame:SetOwner(fakeParent) + frame:children() + local point = info.point + local relativePoint = info.relativePoint + if type(point) == "function" then + local b + point, b = point(fakeParent) + if b then + relativePoint = b + end + end + if type(relativePoint) == "function" then + relativePoint = relativePoint(fakeParent) + end + if not point then + point = "CENTER" + end + if not relativePoint then + relativePoint = point + end + frame:ClearAllPoints() + if type(parent) ~= "string" then + frame:SetPoint(point, fakeParent, relativePoint) + end + local offsetx = 0 + local offsety = 0 + frame:SetClampedToScreen(1) + frame:SetClampedToScreen(nil) + if frame:GetBottom() and frame:GetLeft() then + if frame:GetRight() > GetScreenWidth() then + offsetx = frame:GetRight() - GetScreenWidth() + elseif frame:GetLeft() < 0 then + offsetx = -frame:GetLeft() + end + local ratio = GetScreenWidth() / GetScreenHeight() + if ratio >= 2.4 and frame:GetRight() > GetScreenWidth() / 2 and frame:GetLeft() < GetScreenWidth() / 2 then + if frame:GetCenter() < GetScreenWidth() / 2 then + offsetx = frame:GetRight() - GetScreenWidth() / 2 + else + offsetx = frame:GetLeft() - GetScreenWidth() / 2 + end + end + if frame:GetBottom() < 0 then + offsety = frame:GetBottom() + elseif frame:GetTop() and frame:GetTop() > GetScreenHeight() then + offsety = frame:GetTop() - GetScreenHeight() + end + if MainMenuBar:IsVisible() and frame:GetBottom() < MainMenuBar:GetTop() and offsety < frame:GetBottom() - MainMenuBar:GetTop() then + offsety = frame:GetBottom() - MainMenuBar:GetTop() + end + + if FuBar then + local top = 0 + if FuBar then + for i = 1, FuBar:GetNumPanels() do + local panel = FuBar:GetPanel(i) + if panel:GetAttachPoint() == "BOTTOM" then + if panel.frame:GetTop() and panel.frame:GetTop() > top then + top = panel.frame:GetTop() + break + end + end + end + end + if frame:GetBottom() < top and offsety < frame:GetBottom() - top then + offsety = frame:GetBottom() - top + end + local bottom = GetScreenHeight() + if FuBar then + for i = 1, FuBar:GetNumPanels() do + local panel = FuBar:GetPanel(i) + if panel:GetAttachPoint() == "TOP" then + if panel.frame:GetBottom() and panel.frame:GetBottom() < bottom then + bottom = panel.frame:GetBottom() + break + end + end + end + end + if frame:GetTop() > bottom and offsety < frame:GetTop() - bottom then + offsety = frame:GetTop() - bottom + end + end + end + if type(fakeParent) ~= "string" then + frame:SetPoint(point, fakeParent, relativePoint, -offsetx, -offsety) + end + + if detachedData and (info.cantAttach or detachedData.detached) and frame == tooltip then + detachedData.detached = false + frame:Detach() + end + if (not detachedData or not detachedData.detached) and GetMouseFocus() == fakeParent then + self.tooltip.enteredFrame = true + end + overFrame = type(fakeParent) == "table" and MouseIsOver(fakeParent) and fakeParent +end +Tablet.Open = wrap(Tablet.Open, "Tablet:Open") + +function Tablet:Register(parent, ...) + self:argCheck(parent, 2, "table", "string") + if self.registry[parent] then + self:Unregister(parent) + end + local info + local k1 = ... + if type(k1) == "table" and k1[0] then + if type(self.registry[k1]) ~= "table" then + self:error("Other parent not registered") + end + info = copy(self.registry[k1]) + local v1 = select(2, ...) + if type(v1) == "function" then + info.point = v1 + info.relativePoint = nil + end + else + info = new(...) + end + self.registry[parent] = info + info.data = info.data or info.detachedData or new() + info.detachedData = info.detachedData or info.data + local data = info.data + local detachedData = info.detachedData + if not self.onceRegistered[parent] and type(parent) == "table" and type(parent.SetScript) == "function" and not info.dontHook then + if not Dewdrop and AceLibrary:HasInstance("Dewdrop-2.0") then + Dewdrop = AceLibrary("Dewdrop-2.0") + end + local script = parent:GetScript("OnEnter") + parent:SetScript("OnEnter", function(...) + if script then + script(...) + end + if self.registry[parent] then + if (not data or not detachedData.detached) and (Dewdrop and not Dewdrop:IsOpen(parent)) then + self:Open(parent) + end + end + end) + if parent:HasScript("OnMouseDown") then + local script = parent:GetScript("OnMouseDown") + parent:SetScript("OnMouseDown", function(...) + if script then + script(...) + end + if self.registry[parent] and self.registry[parent].tooltip and self.registry[parent].tooltip == self.tooltip then + self.tooltip:Hide() + end + end) + end + if parent:HasScript("OnMouseWheel") then + local script = parent:GetScript("OnMouseWheel") + parent:SetScript("OnMouseWheel", function(...) + if script then + script(...) + end + if self.registry[parent] and self.registry[parent].tooltip then + self.registry[parent].tooltip:Scroll(arg1 < 0) + end + end) + end + end + self.onceRegistered[parent] = true + if GetMouseFocus() == parent then + self:Open(parent) + end +end +Tablet.Register = wrap(Tablet.Register, "Tablet:Register") + +function Tablet:Unregister(parent) + self:argCheck(parent, 2, "table", "string") + if not self.registry[parent] then + self:error("You cannot unregister a parent frame if it has not been registered already.") + end + self.registry[parent] = nil +end +Tablet.Unregister = wrap(Tablet.Unregister, "Tablet:Unregister") + +function Tablet:IsRegistered(parent) + self:argCheck(parent, 2, "table", "string") + return self.registry[parent] and true +end +Tablet.IsRegistered = wrap(Tablet.IsRegistered, "Tablet:IsRegistered") + +local _id = 0 +local addedCategory +local depth = 0 +local categoryPool = {} +function CleanCategoryPool(self) + for k,v in pairs(categoryPool) do + del(v) + categoryPool[k] = nil + end + _id = 0 +end +CleanCategoryPool = wrap(CleanCategoryPool, "CleanCategoryPool") + +function Tablet:AddCategory(...) + if not self.currentFrame then + self:error("You must add categories in within a registration.") + end + local info = new(...) + local cat = self.currentTabletData:AddCategory(info) + info = del(info) + return cat +end +Tablet.AddCategory = wrap(Tablet.AddCategory, "Tablet:AddCategory") + +function Tablet:SetHint(text) + if not self.currentFrame then + self:error("You must set hint within a registration.") + end + self.currentTabletData:SetHint(text) +end +Tablet.SetHint = wrap(Tablet.SetHint, "Tablet:SetHint") + +function Tablet:SetTitle(text) + if not self.currentFrame then + self:error("You must set title within a registration.") + end + self.currentTabletData:SetTitle(text) +end +Tablet.SetTitle = wrap(Tablet.SetTitle, "Tablet:SetTitle") + +function Tablet:SetTitleColor(r, g, b) + if not self.currentFrame then + self:error("You must set title color within a registration.") + end + self:argCheck(r, 2, "number") + self:argCheck(g, 3, "number") + self:argCheck(b, 4, "number") + self.currentTabletData:SetTitleColor(r, g, b) +end +Tablet.SetTitleColor = wrap(Tablet.SetTitleColor, "Tablet:SetTitleColor") + +function Tablet:GetNormalFontSize() + return normalSize +end +Tablet.GetNormalFontSize = wrap(Tablet.GetNormalFontSize, "Tablet:GetNormalFontSize") + +function Tablet:GetHeaderFontSize() + return headerSize +end +Tablet.GetHeaderFontSize = wrap(Tablet.GetHeaderFontSize, "Tablet:GetHeaderFontSize") + +function Tablet:GetNormalFontObject() + return GameTooltipText +end +Tablet.GetNormalFontObject = wrap(Tablet.GetNormalFontObject, "Tablet:GetNormalFontObject") + +function Tablet:GetHeaderFontObject() + return GameTooltipHeaderText +end +Tablet.GetHeaderFontObject = wrap(Tablet.GetHeaderFontObject, "Tablet:GetHeaderFontObject") + +function Tablet:SetFontSizePercent(parent, percent) + self:argCheck(parent, 2, "table", "string") + local info = self.registry[parent] + if info then + if info.tooltip then + info.tooltip:SetFontSizePercent(percent) + else + local data = info.data + local detachedData = info.detachedData + if detachedData.detached then + detachedData.fontSizePercent = percent + else + data.fontSizePercent = percent + end + end + elseif type(parent) == "table" then + parent.fontSizePercent = percent + else + self:error("You cannot change font size with an unregistered parent frame.") + end +end +Tablet.SetFontSizePercent = wrap(Tablet.SetFontSizePercent, "Tablet:SetFontSizePercent") + +function Tablet:GetFontSizePercent(parent) + self:argCheck(parent, 2, "table", "string") + local info = self.registry[parent] + if info then + local data = info.data + local detachedData = info.detachedData + if detachedData.detached then + return detachedData.fontSizePercent or 1 + else + return data.fontSizePercent or 1 + end + elseif type(parent) == "table" then + return parent.fontSizePercent or 1 + else + self:error("You cannot check font size with an unregistered parent frame.") + end +end +Tablet.GetFontSizePercent = wrap(Tablet.GetFontSizePercent, "Tablet:GetFontSizePercent") + +function Tablet:SetTransparency(parent, percent) + self:argCheck(parent, 2, "table", "string") + local info = self.registry[parent] + if info then + if info.tooltip then + info.tooltip:SetTransparency(percent) + else + local data = info.data + local detachedData = info.detachedData + if detachedData.detached then + detachedData.transparency = percent + elseif data then + data.transparency = percent + end + end + elseif type(parent) == "table" then + parent.transparency = percent + else + self:error("You cannot change transparency with an unregistered parent frame.") + end +end +Tablet.SetTransparency = wrap(Tablet.SetTransparency, "Tablet:SetTransparency") + +function Tablet:GetTransparency(parent) + self:argCheck(parent, 2, "table", "string") + local info = self.registry[parent] + if info then + local data = info.data + local detachedData = info.detachedData + if detachedData.detached then + return detachedData.transparency or 0.75 + else + return data.transparency or 0.75 + end + elseif type(parent) == "table" then + return parent.transparency or 0.75 + else + self:error("You cannot get transparency with an unregistered parent frame.") + end +end +Tablet.GetTransparency = wrap(Tablet.GetTransparency, "Tablet:GetTransparency") + +function Tablet:SetColor(parent, r, g, b) + self:argCheck(parent, 2, "table", "string") + local info = self.registry[parent] + if info then + if info.tooltip then + info.tooltip:SetColor(r, g, b) + else + local data = info.data + local detachedData = info.detachedData + if detachedData.detached then + detachedData.r = r + detachedData.g = g + detachedData.b = b + else + data.r = r + data.g = g + data.b = b + end + end + elseif type(parent) == "table" then + parent.r = r + parent.g = g + parent.b = b + else + self:error("You cannot change color with an unregistered parent frame.") + end +end +Tablet.SetColor = wrap(Tablet.SetColor, "Tablet:SetColor") + +function Tablet:GetColor(parent) + self:argCheck(parent, 2, "table", "string") + local info = self.registry[parent] + if info then + local data = info.data + local detachedData = info.detachedData + if detachedData.detached then + return detachedData.r or 0, detachedData.g or 0, detachedData.b or 0 + else + return data.r or 0, data.g or 0, data.b or 0 + end + elseif type(parent) == "table" then + return parent.r or 0, parent.g or 0, parent.b or 0 + else + self:error("You must provide a registered parent frame to check color") + end +end +Tablet.GetColor = wrap(Tablet.GetColor, "Tablet:GetColor") + +function Tablet:Detach(parent) + self:argCheck(parent, 2, "table", "string") + local info = self.registry[parent] + if not info then + self:error("You cannot detach tablet with an unregistered parent frame.") + end + if not info.detachedData then + self:error("You cannot detach tablet without a data field.") + end + if info.tooltip and info.tooltip == tooltip and tooltip.registration then + tooltip:Detach() + else + info.detachedData.detached = true + local detached = AcquireDetachedFrame(self, info, info.data, info.detachedData) + + detached.menu = info.menu + detached.runChildren = info.children + detached.minWidth = info.minWidth + if not detached.children or not detached.childrenVer or detached.childrenVer < MINOR_VERSION then + detached.childrenVer = MINOR_VERSION + detached.children = frame_children + end + detached:SetOwner(parent) + detached:children() + end +end +Tablet.Detach = wrap(Tablet.Detach, "Tablet:Detach") + +function Tablet:Attach(parent) + self:argCheck(parent, 2, "table", "string") + local info = self.registry[parent] + if not info then + self:error("You cannot detach tablet with an unregistered parent frame.") + end + if not info.detachedData then + self:error("You cannot attach tablet without a data field.") + end + if info.tooltip and info.tooltip ~= tooltip then + info.tooltip:Attach() + else + info.detachedData.detached = false + end +end +Tablet.Attach = wrap(Tablet.Attach, "Tablet:Attach") + +function Tablet:IsAttached(parent) + self:argCheck(parent, 2, "table", "string") + local info = self.registry[parent] + if not info then + self:error("You cannot check tablet with an unregistered parent frame.") + end + return not info.detachedData or not info.detachedData.detached +end +Tablet.IsAttached = wrap(Tablet.IsAttached, "Tablet:IsAttached") + +function Tablet:Refresh(parent) + self:argCheck(parent, 2, "table", "string") + local info = self.registry[parent] + if not info then + self:error("You cannot refresh tablet with an unregistered parent frame.") + end + local tt = info.tooltip + if tt and not tt.preventRefresh and tt:IsShown() then + tt.updating = true + tt:children() + tt.updating = false + end +end +Tablet.Refresh = wrap(Tablet.Refresh, "Tablet:Refresh") + +function Tablet:IsLocked(parent) + self:argCheck(parent, 2, "table", "string") + local info = self.registry[parent] + if not info then + self:error("You cannot check tablet with an unregistered parent frame.") + end + return info.detachedData and info.detachedData.locked +end +Tablet.IsLocked = wrap(Tablet.IsLocked, "Tablet:IsLocked") + +function Tablet:ToggleLocked(parent) + self:argCheck(parent, 2, "table", "string") + local info = self.registry[parent] + if not info then + self:error("You cannot lock tablet with an unregistered parent frame.") + end + if info.tooltip and info.tooltip ~= tooltip then + info.tooltip:Lock() + elseif info.detachedData then + info.detachedData.locked = info.detachedData.locked + end +end +Tablet.ToggleLocked = wrap(Tablet.ToggleLocked, "Tablet:ToggleLocked") + +function Tablet:UpdateDetachedData(parent, detachedData) + self:argCheck(parent, 2, "table", "string") + local info = self.registry[parent] + if not info then + self:error("You cannot update tablet with an unregistered parent frame.") + end + self:argCheck(detachedData, 3, "table") + if info.data == info.detachedData then + info.data = detachedData + end + info.detachedData = detachedData + if info.detachedData.detached then + self:Detach(parent) + elseif info.tooltip and info.tooltip.owner then + self:Attach(parent) + end +end +Tablet.UpdateDetachedData = wrap(Tablet.UpdateDetachedData, "Tablet:UpdateDetachedData") + +if DEBUG then + function Tablet:ListProfileInfo() + local duration, times, memories = GetProfileInfo() + if not duration or not time or not memories then + self:error("Problems") + end + local t = new() + for method in pairs(memories) do + t[#t+1] = method + end + table.sort(t, function(alpha, bravo) + if memories[alpha] ~= memories[bravo] then + return memories[alpha] < memories[bravo] + elseif times[alpha] ~= times[bravo] then + return times[alpha] < times[bravo] + else + return alpha < bravo + end + end) + local memory = 0 + local time = 0 + for _,method in ipairs(t) do + DEFAULT_CHAT_FRAME:AddMessage(format("%s || %.3f s || %.3f%% || %d KiB", method, times[method], times[method] / duration * 100, memories[method])) + memory = memory + memories[method] + time = time + times[method] + end + DEFAULT_CHAT_FRAME:AddMessage(format("%s || %.3f s || %.3f%% || %d KiB", "Total", time, time / duration * 100, memory)) + del(t) + end + SLASH_TABLET1 = "/tablet" + SLASH_TABLET2 = "/tabletlib" + SlashCmdList["TABLET"] = function(msg) + AceLibrary(MAJOR_VERSION):ListProfileInfo() + end +end + +local function activate(self, oldLib, oldDeactivate) + Tablet = self + if oldLib then + self.registry = oldLib.registry + self.onceRegistered = oldLib.onceRegistered + self.tooltip = oldLib.tooltip + self.currentFrame = oldLib.currentFrame + self.currentTabletData = oldLib.currentTabletData + else + self.registry = {} + self.onceRegistered = {} + end + + tooltip = self.tooltip + + if oldDeactivate then + oldDeactivate(oldLib) + end +end + +local function deactivate(self) + StopCheckingAlt() +end + +AceLibrary:Register(Tablet, MAJOR_VERSION, MINOR_VERSION, activate, deactivate) diff --git a/AtlasLootFu/Locales.lua b/AtlasLootFu/Locales.lua new file mode 100644 index 0000000..9f057a8 --- /dev/null +++ b/AtlasLootFu/Locales.lua @@ -0,0 +1,34 @@ +ATLASLOOTFU_LEFTCLICK = "|cff1eff00Left-Click|r Browse Loot Tables"; +ATLASLOOTFU_SHIFTCLICK = "|cffff0000Shift-Click|r View Options"; +ATLASLOOTFU_LEFTDRAG = "|cffccccccLeft-Click + Drag|r Move Minimap Button"; + +if (GetLocale() == "deDE") then + ATLASLOOTFU_LEFTCLICK = "|cff1eff00Linksklick|r Loot-Tabellen durchsuchen"; + ATLASLOOTFU_SHIFTCLICK = "|cffff0000Shift-Klick|r Optionen anzeigen"; + ATLASLOOTFU_LEFTDRAG = "|cffccccccLinksklick + Ziehen|r Minimap-Button bewegen"; +end + +if (GetLocale() == "frFR") then + ATLASLOOTFU_LEFTCLICK = "|cff1eff00Clic-Gauche|r Parcourir les tables de butin"; + ATLASLOOTFU_SHIFTCLICK = "|cffff0000Shift-Clic|r Affiche les Options"; + ATLASLOOTFU_LEFTDRAG = "|cffccccccClic-Gauche + Maintenir|r Déplacer l'icone de la minimap"; +end + +if (GetLocale() == "ruRU") then +ATLASLOOTFU_LEFTCLICK = "|cff1eff00Левый-Клик|r обзор таблици добычи"; +ATLASLOOTFU_SHIFTCLICK = "|cffff0000Shift-Клик|r настройки"; +ATLASLOOTFU_LEFTDRAG = "|cffccccccЛевый-Клик + тащить|r перемещение кнопки"; +end + +if (GetLocale() == "zhTW") then +ATLASLOOTFU_LEFTCLICK = "|cff1eff00左鍵|r 瀏覽物品掉落表格"; +ATLASLOOTFU_SHIFTCLICK = "|cffff0000Shift-點擊|r 查看選項"; +ATLASLOOTFU_LEFTDRAG = "|cffcccccc右鍵 + 拖曳|r 移動小地圖按鈕"; +end + +if (GetLocale() == "zhCN") then +ATLASLOOTFU_LEFTCLICK = "|cff1eff00左键|r 浏览物品掉落表格"; +ATLASLOOTFU_SHIFTCLICK = "|cffff0000Shift-点击|r 查看选项"; +ATLASLOOTFU_LEFTDRAG = "|cffcccccc右键 + 拖曳|r 移动小地图按钮"; +end + diff --git a/AtlasLootFu/embeds.xml b/AtlasLootFu/embeds.xml new file mode 100644 index 0000000..901ce09 --- /dev/null +++ b/AtlasLootFu/embeds.xml @@ -0,0 +1,14 @@ + + +