Mercurial > wow > reaction
changeset 33:c54c481ad0ed
- Moved bar control frame from ConfigUI to Bar
- Added LICENSE.txt
- added profile management options
- other minor cleanup
author | Flick <flickerstreak@gmail.com> |
---|---|
date | Thu, 03 Apr 2008 20:25:40 +0000 |
parents | 821b2b7edff1 |
children | b5ae1bae02ff |
files | Bar.lua LICENSE.txt ReAction.lua ReAction.toc lib/AceLibrary/AceLibrary.lua lib/AceLibrary/AceLibrary.toc lib/Dewdrop-2.0/Dewdrop-2.0.lua lib/embeds.xml modules/ReAction_ConfigUI/ReAction_ConfigUI.lua modules/ReAction_ConfigUI/lib/AceLibrary/AceLibrary.lua modules/ReAction_ConfigUI/lib/AceLibrary/AceLibrary.toc modules/ReAction_ConfigUI/lib/AceOO-2.0/AceOO-2.0.lua modules/ReAction_ConfigUI/lib/AceOO-2.0/AceOO-2.0.toc modules/ReAction_ConfigUI/lib/Dewdrop-2.0/Dewdrop-2.0.lua modules/ReAction_ConfigUI/lib/embeds.xml |
diffstat | 15 files changed, 4862 insertions(+), 5679 deletions(-) [+] |
line wrap: on
line diff
--- a/Bar.lua Thu Apr 03 16:59:16 2008 +0000 +++ b/Bar.lua Thu Apr 03 20:25:40 2008 +0000 @@ -2,9 +2,16 @@ local L = ReAction.L local _G = _G local CreateFrame = CreateFrame +local InCombatLockdown = InCombatLockdown +local floor = math.floor +local min = math.min +local format = string.format +local GameTooltip = GameTooltip + + -- update ReAction revision if this file is newer -local revision = tonumber(("$Revision: 1 $"):match("%d+")) +local revision = tonumber(("$Revision$"):match("%d+")) if revision > ReAction.revision then Reaction.revision = revision end @@ -112,6 +119,13 @@ return self.name end +function Bar:SetName(name) + self.name = name + if self.controlLabelString then + self.controlLabelString:SetText(self.name) + end +end + function Bar:PlaceButton(f, idx, baseW, baseH) local r, c, s = self:GetButtonGrid() local bh, bw = self:GetButtonSize() @@ -127,6 +141,378 @@ + + + + +-- +-- Bar config overlay +-- +local StoreExtents, RecomputeButtonSize, RecomputeButtonSpacing, RecomputeGrid, ClampToButtons, HideGameTooltip, CreateControls + +do + -- upvalue some of these for small OnUpdate performance boost + local GetSize = Bar.GetSize + local GetButtonSize = Bar.GetButtonSize + local GetButtonGrid = Bar.GetButtonGrid + local SetSize = Bar.SetSize + local SetButtonSize = Bar.SetButtonSize + local SetButtonGrid = Bar.SetButtonGrid + local ApplyAnchor = Bar.ApplyAnchor + + StoreExtents = function(bar) + local f = bar.frame + local point, relativeTo, relativePoint, x, y = f:GetPoint(1) + relativeTo = relativeTo or f:GetParent() + local anchorTo + for name, b in pairs(ReAction.bars) do + if b then + if b:GetFrame() == relativeTo then + anchorTo = name + break + end + end + end + anchorTo = anchorTo or relativeTo:GetName() + local c = bar.config + c.anchor = point + c.anchorTo = anchorTo + c.relativePoint = relativePoint + c.x = x + c.y = y + c.width, c.height = f:GetWidth(), f:GetHeight() + end + + RecomputeButtonSize = function(bar) + local w, h = GetSize(bar) + local bw, bh = GetButtonSize(bar) + local r, c, s = GetButtonGrid(bar) + + local scaleW = (floor(w/c) - s) / bw + local scaleH = (floor(h/r) - s) / bh + local scale = min(scaleW, scaleH) + + SetButtonSize(bar, scale * bw, scale * bh, s) + end + + RecomputeButtonSpacing = function(bar) + local w, h = GetSize(bar) + local bw, bh = GetButtonSize(bar) + local r, c, s = GetButtonGrid(bar) + + SetButtonGrid(bar,r,c,min(floor(w/c) - bw, floor(h/r) - bh)) + end + + RecomputeGrid = function(bar) + local w, h = GetSize(bar) + local bw, bh = GetButtonSize(bar) + local r, c, s = GetButtonGrid(bar) + + SetButtonGrid(bar, floor(h/(bh+s)), floor(w/(bw+s)), s) + end + + ClampToButtons = function(bar) + local bw, bh = GetButtonSize(bar) + local r, c, s = GetButtonGrid(bar) + SetSize(bar, (bw+s)*c, (bh+s)*r ) + end + + HideGameTooltip = function() + GameTooltip:Hide() + end + + CreateControls = function(bar) + local f = bar.frame + + f:SetMovable(true) + f:SetResizable(true) + f:SetClampedToScreen(true) + + -- buttons on the bar should be direct children of the bar frame. + -- The control elements need to float on top of this, which we could + -- do with SetFrameLevel() or Raise(), but it's more reliable to do it + -- via frame nesting, hence good old foo's appearance here. + local foo = CreateFrame("Frame",nil,f) + foo:SetAllPoints() + + local control = CreateFrame("Button", nil, foo) + control:EnableMouse(true) + control:SetToplevel(true) + control:SetPoint("TOPLEFT", -4, 4) + control:SetPoint("BOTTOMRIGHT", 4, -4) + control:SetBackdrop({ + edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border", + tile = true, + tileSize = 16, + edgeSize = 16, + insets = { left = 0, right = 0, top = 0, bottom = 0 }, + }) + + -- textures + local bgTex = control:CreateTexture(nil,"BACKGROUND") + bgTex:SetTexture(0.7,0.7,1.0,0.2) + bgTex:SetPoint("TOPLEFT",4,-4) + bgTex:SetPoint("BOTTOMRIGHT",-4,4) + local hTex = control:CreateTexture(nil,"HIGHLIGHT") + hTex:SetTexture(0.7,0.7,1.0,0.2) + hTex:SetPoint("TOPLEFT",4,-4) + hTex:SetPoint("BOTTOMRIGHT",-4,4) + hTex:SetBlendMode("ADD") + + -- label + local label = control:CreateFontString(nil,"OVERLAY","GameFontNormalLarge") + label:SetAllPoints() + label:SetJustifyH("CENTER") + label:SetShadowColor(0,0,0,1) + label:SetShadowOffset(2,-2) + label:SetTextColor(1,1,1,1) + label:SetText(bar:GetName()) + label:Show() + bar.controlLabelString = label -- so that bar:SetName() can update it + + local StopResize = function() + f:StopMovingOrSizing() + f.isMoving = false + f:SetScript("OnUpdate",nil) + StoreExtents(bar) + ClampToButtons(bar) + ApplyAnchor(bar) + end + + -- edge drag handles + for _, point in pairs({"LEFT","TOP","RIGHT","BOTTOM"}) do + local edge = CreateFrame("Frame",nil,control) + edge:EnableMouse(true) + edge:SetWidth(8) + edge:SetHeight(8) + if point == "TOP" or point == "BOTTOM" then + edge:SetPoint(point.."LEFT") + edge:SetPoint(point.."RIGHT") + else + edge:SetPoint("TOP"..point) + edge:SetPoint("BOTTOM"..point) + end + local tex = edge:CreateTexture(nil,"HIGHLIGHT") + tex:SetTexture(1.0,0.82,0,0.7) + tex:SetBlendMode("ADD") + tex:SetAllPoints() + edge:RegisterForDrag("LeftButton") + edge:SetScript("OnMouseDown", + function() + local bw, bh = GetButtonSize(bar) + local r, c, s = GetButtonGrid(bar) + f:SetMinResize( bw+s+1, bh+s+1 ) + f:StartSizing(point) + f:SetScript("OnUpdate", + function() + RecomputeGrid(bar) + bar:RefreshLayout() + end + ) + end + ) + edge:SetScript("OnMouseUp", StopResize) + edge:SetScript("OnEnter", + function() + GameTooltip:SetOwner(f, "ANCHOR_"..point) + GameTooltip:AddLine(L["Drag to add/remove buttons"]) + GameTooltip:Show() + end + ) + edge:SetScript("OnLeave", HideGameTooltip) + edge:Show() + end + + -- corner drag handles, again nested in an anonymous frame so that they are on top + local foo2 = CreateFrame("Frame",nil,control) + foo2:SetAllPoints(true) + for _, point in pairs({"BOTTOMLEFT","TOPLEFT","BOTTOMRIGHT","TOPRIGHT"}) do + local corner = CreateFrame("Frame",nil,foo2) + corner:EnableMouse(true) + corner:SetWidth(12) + corner:SetHeight(12) + corner:SetPoint(point) + local tex = corner:CreateTexture(nil,"HIGHLIGHT") + tex:SetTexture(1.0,0.82,0,0.7) + tex:SetBlendMode("ADD") + tex:SetAllPoints() + corner:RegisterForDrag("LeftButton","RightButton") + local updateTooltip = function() + local size, size2 = bar:GetButtonSize() + local rows, cols, spacing = bar:GetButtonGrid() + size = (size == size2) and tostring(size) or format("%dx%d",size,size2) + GameTooltipTextRight4:SetText(size) + GameTooltipTextRight5:SetText(tostring(spacing)) + end + corner:SetScript("OnMouseDown", + function(_,btn) + local bw, bh = GetButtonSize(bar) + local r, c, s = GetButtonGrid(bar) + if btn == "LeftButton" then -- button resize + f:SetMinResize( (s+12)*c+1, (s+12)*r+1 ) + f:SetScript("OnUpdate", + function() + RecomputeButtonSize(bar) + bar:RefreshLayout() + updateTooltip() + end + ) + elseif btn == "RightButton" then -- spacing resize + f:SetMinResize( bw*c, bh*r ) + f:SetScript("OnUpdate", + function() + RecomputeButtonSpacing(bar) + bar:RefreshLayout() + updateTooltip() + end + ) + end + f:StartSizing(point) + end + ) + corner:SetScript("OnMouseUp",StopResize) + corner:SetScript("OnEnter", + function() + GameTooltip:SetOwner(f, "ANCHOR_"..point) + GameTooltip:AddLine(L["Drag to resize buttons"]) + GameTooltip:AddLine(L["Right-click-drag"]) + GameTooltip:AddLine(L["to change spacing"]) + local size, size2 = bar:GetButtonSize() + local rows, cols, spacing = bar:GetButtonGrid() + size = (size == size2) and tostring(size) or format("%dx%d",size,size2) + GameTooltip:AddDoubleLine(L["Size:"], size) + GameTooltip:AddDoubleLine(L["Spacing:"], tostring(spacing)) + GameTooltip:Show() + end + ) + corner:SetScript("OnLeave", + function() + GameTooltip:Hide() + f:SetScript("OnUpdate",nil) + end + ) + + end + + control:RegisterForDrag("LeftButton") + control:RegisterForClicks("RightButtonDown") + + control:SetScript("OnDragStart", + function() + f:StartMoving() + f.isMoving = true + -- TODO: snap indicator update install + end + ) + + control:SetScript("OnDragStop", + function() + f:StopMovingOrSizing() + f.isMoving = false + f:SetScript("OnUpdate",nil) + -- TODO: snap frame here + StoreExtents(bar) + end + ) + + control:SetScript("OnEnter", + function() + -- add bar type and status information to name + local name = bar.name + for _, m in ReAction:IterateModules() do + --[[ + local suffix = safecall(m,"GetBarNameModifier",bar) + if suffix then + name = ("%s %s"):format(name,suffix) + end + --]] + end + + GameTooltip:SetOwner(f, "ANCHOR_TOPRIGHT") + GameTooltip:AddLine(name) + GameTooltip:AddLine(L["Drag to move"]) + --GameTooltip:AddLine(L["Shift-drag for sticky mode"]) + GameTooltip:AddLine(L["Right-click for options"]) + GameTooltip:Show() + end + ) + + control:SetScript("OnLeave", HideGameTooltip) + + control:SetScript("OnClick", + function() + bar:ShowMenu() + end + ) + + return control + end +end + + +local OpenMenu, CloseMenu +do + -- Looking for a lightweight AceConfig3-struct-compatible + -- replacement for Dewdrop, encapsulate here + -- Considering Blizzard's EasyMenu/UIDropDownMenu, but that's + -- a bit tricky to convert from AceConfig3-struct + local Dewdrop = AceLibrary("Dewdrop-2.0") + OpenMenu = function(frame, opts) + Dewdrop:Open(frame, "children", opts, "cursorX", true, "cursorY", true) + end + CloseMenu = function(frame) + if Dewdrop:GetOpenedParent() == frame then + Dewdrop:Close() + end + end +end + + +function Bar:ShowControls(show) + if show then + if not self.controlFrame then + self.controlFrame = CreateControls(self) + end + self.controlFrame:Show() + elseif self.controlFrame then + CloseMenu(self.controlFrame) + self.controlFrame:Hide() + end +end + +function Bar:ShowMenu() + if not self.menuOpts then + self.menuOpts = { + type = "group", + args = { + --[[ + openConfig = { + type = "execute", + name = L["Configure..."], + desc = L["Open the configuration dialogue for this bar"], + func = function() CloseMenu(self.controlFrame); module:OpenConfig(self) end, + disabled = InCombatLockdown, + order = 1 + }, + --]] + delete = { + type = "execute", + name = L["Delete Bar"], + desc = L["Remove the bar from the current profile"], + func = function() ReAction:EraseBar(self) end, + order = 2 + }, + } + } + end + if self.modMenuOpts == nil then + self.modMenuOpts = { } + end + OpenMenu(self.controlFrame, self.menuOpts) +end + + + ------ Export as a class-factory ------ ReAction.Bar = { prototype = Bar,
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/LICENSE.txt Thu Apr 03 20:25:40 2008 +0000 @@ -0,0 +1,59 @@ +Copyright (c) 2008 Ryan Findley + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + + + +This distribution includes source code provided by +Ace3 Development Team, license of which is reproduced below: + +------------------------------------------- + +Copyright (c) 2007, Ace3 Development Team + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * Redistribution of a stand alone version is strictly prohibited without + prior written authorization from the Lead of the Ace3 Development Team. + * Neither the name of the Ace3 Development Team nor the names of its contributors + may be used to endorse or promote products derived from this software without + specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +------------------------------------------ \ No newline at end of file
--- a/ReAction.lua Thu Apr 03 16:59:16 2008 +0000 +++ b/ReAction.lua Thu Apr 03 20:25:40 2008 +0000 @@ -1,18 +1,14 @@ -- ReAction.lua -- See modules/ReAction_ModuleTemplate for Module API listing -- See Bar.lua for Bar object listing -local MAJOR_VERSION = GetAddOnMetadata("ReAction","Version") - ------- LIBRARIES ------ -local L = LibStub("AceLocale-3.0"):GetLocale("ReAction") ------ CORE ------ local ReAction = LibStub("AceAddon-3.0"):NewAddon( "ReAction", - "AceConsole-3.0" + "AceConsole-3.0", + "AceEvent-3.0" ) -ReAction.revision = tonumber(("$Revision: 1 $"):match("%d+")) -ReAction.version = MAJOR_VERSION -ReAction.L = L +ReAction.version = GetAddOnMetadata("ReAction","Version") +ReAction.revision = tonumber(("$Revision$"):match("%d+")) ------ GLOBALS ------ _G["ReAction"] = ReAction @@ -28,6 +24,10 @@ ReAction.print = function() end end +------ LIBRARIES ------ +local L = LibStub("AceLocale-3.0"):GetLocale("ReAction") +ReAction.L = L + ------ PRIVATE ------ local SelectBar, DestroyBar, InitializeBars, TearDownBars, DeepCopy, SafeCall, CheckMethod, SlashHandler do @@ -112,25 +112,25 @@ end local function CallOptsModule(method,...) - local m = ReAction:GetModule("ConfigUI",true) - if not m then - LoadAddOn("ReAction_ConfigUI") - m = ReAction:GetModule("ConfigUI") - end - if m and type(m) == "table" and type(m[method]) == "function" then - m[method](m,...) - else - ReAction:Print("Options module not found") - end + local m = ReAction:GetModule("ConfigUI",true) + if not m then + LoadAddOn("ReAction_ConfigUI") + m = ReAction:GetModule("ConfigUI") + end + if m and type(m) == "table" and type(m[method]) == "function" then + m[method](m,...) + else + ReAction:Print("Options module not found") + end end SlashHandler = function(option) if option == "config" then CallOptsModule("OpenConfig",true) elseif option == "unlock" then - CallOptsModule("SetConfigMode",true) + ReAction:SetConfigMode(true) elseif option == "lock" then - CallOptsModule("SetConfigMode",false) + ReAction:SetConfigMode(false) else ReAction:Print(ReAction.version) ReAction:Print("/reaction config") @@ -156,6 +156,7 @@ self.callbacks = LibStub("CallbackHandler-1.0"):New(self) self:RegisterChatCommand("reaction", SlashHandler) self:RegisterChatCommand("rxn", SlashHandler) + self:RegisterEvent("PLAYER_REGEN_DISABLED") self.bars = {} end @@ -169,8 +170,8 @@ end function ReAction:OnProfileChanged() - self.TearDownBars() - self.InitializeBars() + TearDownBars() + InitializeBars() end function ReAction:OnModuleEnable(module) @@ -193,6 +194,14 @@ end end +function ReAction:PLAYER_REGEN_DISABLED() + if self.configMode == true then + UIErrorsFrame:AddMessage(L["ReAction config mode disabled during combat."]) + self:SetConfigMode(false) + end +end + + ------ API ------ function ReAction:CallMethodOnAllModules(method, ...) @@ -229,6 +238,10 @@ self:CallMethodOnAllModules("ApplyToBar", bar) self.bars[name] = bar self.callbacks:Fire("OnCreateBar", bar) + if self.configMode then + bar:ShowControls(true) + end + return bar end @@ -295,3 +308,9 @@ function ReAction:RefreshOptions() self.callbacks:Fire("OnOptionsRefreshed") end + +function ReAction:SetConfigMode( mode ) + self:CallMethodOnAllBars("ShowControls",mode) + self:CallMethodOnAllModules("ApplyConfigMode",mode,self.bars) + self.configMode = mode +end
--- a/ReAction.toc Thu Apr 03 16:59:16 2008 +0000 +++ b/ReAction.toc Thu Apr 03 20:25:40 2008 +0000 @@ -7,5 +7,6 @@ ## Version: 1.0 ## SavedVariables: ReAction_DB ## X-Category: Action Bars +## X-License: MIT ReAction.xml
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/AceLibrary/AceLibrary.lua Thu Apr 03 20:25:40 2008 +0000 @@ -0,0 +1,856 @@ +--[[ +Name: AceLibrary +Revision: $Rev: 49421 $ +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: 49421 $ +Website: http://www.wowace.com/ +Documentation: http://www.wowace.com/index.php/AceLibrary +SVN: http://svn.wowace.com/root/trunk/Ace2/AceLibrary +Description: Versioning library to handle other library instances, upgrading, + and proper access. + It also provides a base for libraries to work off of, providing + proper error tools. It is handy because all the errors occur in the + file that called it, not in the library file itself. +Dependencies: None +License: LGPL v2.1 +]] + +local ACELIBRARY_MAJOR = "AceLibrary" +local ACELIBRARY_MINOR = "$Revision: 49421 $" + +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 + +local WoW22 = false +if type(GetBuildInfo) == "function" then + local success, buildinfo = pcall(GetBuildInfo) + if success and type(buildinfo) == "string" then + local num = tonumber(buildinfo:match("^(%d+%.%d+)")) + if num and num >= 2.2 then + WoW22 = true + end + 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 assert +if not WoW22 then + function assert(self, condition, message, ...) + if not condition then + if not message then + local stack = debugstack() + local second = stack:match("\n(.-)\n") + message = "assertion failed! " .. second + end + return error(self, message, ...) + end + return condition + end +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 WoW22 and not rawget(instance, 'assert') then + rawset(instance, 'assert', assert) + end + if not rawget(instance, 'argCheck') then + rawset(instance, 'argCheck', argCheck) + end + if not rawget(instance, 'pcall') then + rawset(instance, 'pcall', pcall) + end + addToPositions(instance, major) + if activateFunc then + safecall(activateFunc, instance, nil, nil) -- no old version, so explicit nil + +--[[ if major ~= ACELIBRARY_MAJOR then + for k,v in pairs(_G) do + if v == instance 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]] + 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_assert, old_argCheck, old_pcall + if isAceLibrary then + self = instance + AceLibrary = instance + + old_error = instance.error + if not WoW22 then + old_assert = instance.assert + end + old_argCheck = instance.argCheck + old_pcall = instance.pcall + + self.error = error + if not WoW22 then + self.assert = assert + end + 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 WoW22 and not rawget(instance, 'assert') then + rawset(instance, 'assert', assert) + end + if not rawget(instance, 'argCheck') then + rawset(instance, 'argCheck', argCheck) + end + if not rawget(instance, 'pcall') then + rawset(instance, 'pcall', pcall) + end + if isAceLibrary then + for _,v in pairs(self.libs) do + local i = type(v) == "table" and v.instance + if type(i) == "table" then + if not rawget(i, 'error') or i.error == old_error then + rawset(i, 'error', error) + end + if not WoW22 and (not rawget(i, 'assert') or i.assert == old_assert) then + rawset(i, 'assert', assert) + end + if not rawget(i, 'argCheck') or i.argCheck == old_argCheck then + rawset(i, 'argCheck', argCheck) + end + if not rawget(i, 'pcall') or i.pcall == old_pcall then + rawset(i, 'pcall', pcall) + end + end + end + end + if activateFunc then + safecall(activateFunc, instance, oldInstance, oldDeactivateFunc) + +--[[ if major ~= ACELIBRARY_MAJOR then + for k,v in pairs(_G) do + if v == instance 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]] + 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)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/AceLibrary/AceLibrary.toc Thu Apr 03 20:25:40 2008 +0000 @@ -0,0 +1,10 @@ +## Interface: 20200 + +## 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
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/Dewdrop-2.0/Dewdrop-2.0.lua Thu Apr 03 20:25:40 2008 +0000 @@ -0,0 +1,3487 @@ +--[[ +Name: Dewdrop-2.0 +Revision: $Rev: 48630 $ +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 = "$Revision: 48630 $" + +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 \195\188ber dem Schieberegler bist, um 10er 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'?" +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.IsFrameType 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 == "SharedMedia-1.0" then + SharedMedia = instance + end +end + +AceLibrary:Register(Dewdrop, MAJOR_VERSION, MINOR_VERSION, activate, nil, external)
--- a/lib/embeds.xml Thu Apr 03 16:59:16 2008 +0000 +++ b/lib/embeds.xml Thu Apr 03 20:25:40 2008 +0000 @@ -7,8 +7,11 @@ <Include file="AceAddon-3.0\AceAddon-3.0.xml"/> <Include file="AceLocale-3.0\AceLocale-3.0.xml"/> <Include file="AceDB-3.0\AceDB-3.0.xml"/> - <Include file="AceDBOptions-3.0\AceDBOptions-3.0.xml"/> <Include file="AceConsole-3.0\AceConsole-3.0.xml"/> <Include file="AceEvent-3.0\AceEvent-3.0.xml"/> + <!-- for now --> + <Script file="AceLibrary\AceLibrary.lua"/> + <Script file="Dewdrop-2.0\Dewdrop-2.0.lua"/> + </Ui>
--- a/modules/ReAction_ConfigUI/ReAction_ConfigUI.lua Thu Apr 03 16:59:16 2008 +0000 +++ b/modules/ReAction_ConfigUI/ReAction_ConfigUI.lua Thu Apr 03 20:25:40 2008 +0000 @@ -1,38 +1,18 @@ --[[ ReAction Configuration UI module - This modules creates and manages ReAction configuration - elements, including: - - - Interface Options panel - - bar dragging and resizing control overlays - - contextual menus - - Individual modules are responsible for populating these - configuration elements via ReAction:RegisterOptions(). The - valid values of 'context' are: - - - 'global' : added to the Global Settings tab - - 'module' : added to the Module Settings tab - - 'bar' : added to the Bar Settings tab - - 'barMenu' : shown on the bar contextual menu - + Hooks into Blizzard Interface Options AddOns panel --]] -- local imports local ReAction = ReAction local L = ReAction.L -local _G = _G -local InCombatLockdown = InCombatLockdown - -local Dewdrop = AceLibrary("Dewdrop-2.0") -- module declaration local moduleID = "ConfigUI" -local module = ReAction:NewModule( moduleID, - "AceEvent-3.0" -) +local module = ReAction:NewModule( moduleID ) +-- options table basic layer module.configOptions = { type = "group", childGroups = "tab", @@ -94,19 +74,20 @@ profile = { } } ) - self:InitializeOptions() - LibStub("AceConfig-3.0"):RegisterOptionsTable("ReAction",self.configOptions) - LibStub("AceConfigDialog-3.0"):AddToBlizOptions("ReAction", "ReAction") - self:RegisterEvent("PLAYER_REGEN_DISABLED") -end -function module:InitializeOptions() for _, m in pairs(ReAction:GetOptions("global")) do for k, v in pairs(m) do self.configOptions.args.global.args[k] = v end end + + self.configOptions.args.profile = LibStub("AceDBOptions-3.0"):GetOptionsTable(ReAction.db) + self.configOptions.args.profile.order = -2 + + LibStub("AceConfigRegistry-3.0"):RegisterOptionsTable("ReAction",self.configOptions) + LibStub("AceConfigDialog-3.0"):AddToBlizOptions("ReAction", "ReAction") ReAction.RegisterCallback(self,"OnOptionsRegistered") + ReAction.RegisterCallback(self,"OnOptionsRefreshed") end function module:OnOptionsRegistered(evt, context, module, opts) @@ -125,420 +106,13 @@ end end -function module:PLAYER_REGEN_DISABLED() - if self.configMode == true then - UIErrorsFrame:AddMessage(L["ReAction config mode disabled during combat."]) - self:SetConfigMode(false) - end -end - -function module:SetConfigMode( mode ) - ReAction:CallMethodOnAllBars("ShowControls",mode) - ReAction:CallMethodOnAllModules("ApplyConfigMode",mode,ReAction.bars) - self.configMode = mode -end - -function module:ApplyConfigMode( mode, bars ) - if not(mode) then - -- close open dewdrop menu - local p = Dewdrop:GetOpenedParent() - if p then - for _, bar in pairs(bars) do - if bar then - if p == bar.controlFrame then - Dewdrop:Close() - end - end - end - end - end -end - -local function safecall(module, method, ...) - if module and type(module[method]) == "function" then - return module[method](method, ...) - end +function module:OnOptionsRefreshed(evt) + -- TODO: refresh options frame (just OpenConfig again?) end function module:OpenConfig(bar) - Dewdrop:Close() InterfaceOptionsFrame_OpenToFrame("ReAction") -end - -function module:ApplyToBar(bar) - if self.configMode then - bar:ShowControls(self.configMode) + if bar then + -- TODO: select the correct bar pane end end - -function module:RemoveFromBar(bar) - if bar.controlFrame then - bar.controlFrame:SetParent(UIParent) - bar.controlFrame:ClearAllPoints() - bar.controlFrame:Hide() - bar.controlFrame = nil - end -end - - - - - --- --- Bar config overlay --- --- import some of these for small OnUpdate performance boost -local Bar = ReAction.Bar.prototype -local GetSize = Bar.GetSize -local GetButtonSize = Bar.GetButtonSize -local GetButtonGrid = Bar.GetButtonGrid -local SetSize = Bar.SetSize -local SetButtonSize = Bar.SetButtonSize -local SetButtonGrid = Bar.SetButtonGrid -local ApplyAnchor = Bar.ApplyAnchor -local floor = math.floor -local min = math.min -local format = string.format -local GameTooltip = GameTooltip - -local function StoreExtents(bar) - local f = bar.frame - local point, relativeTo, relativePoint, x, y = f:GetPoint(1) - relativeTo = relativeTo or f:GetParent() - local anchorTo - for name, b in pairs(ReAction.bars) do - if b then - if b:GetFrame() == relativeTo then - anchorTo = name - break - end - end - end - anchorTo = anchorTo or relativeTo:GetName() - local c = bar.config - c.anchor = point - c.anchorTo = anchorTo - c.relativePoint = relativePoint - c.x = x - c.y = y - c.width, c.height = f:GetWidth(), f:GetHeight() -end - -local function RecomputeButtonSize(bar) - local w, h = GetSize(bar) - local bw, bh = GetButtonSize(bar) - local r, c, s = GetButtonGrid(bar) - - local scaleW = (floor(w/c) - s) / bw - local scaleH = (floor(h/r) - s) / bh - local scale = min(scaleW, scaleH) - - SetButtonSize(bar, scale * bw, scale * bh, s) -end - -local function RecomputeButtonSpacing(bar) - local w, h = GetSize(bar) - local bw, bh = GetButtonSize(bar) - local r, c, s = GetButtonGrid(bar) - - SetButtonGrid(bar,r,c,min(floor(w/c) - bw, floor(h/r) - bh)) -end - -local function RecomputeGrid(bar) - local w, h = GetSize(bar) - local bw, bh = GetButtonSize(bar) - local r, c, s = GetButtonGrid(bar) - - SetButtonGrid(bar, floor(h/(bh+s)), floor(w/(bw+s)), s) -end - -local function ClampToButtons(bar) - local bw, bh = GetButtonSize(bar) - local r, c, s = GetButtonGrid(bar) - SetSize(bar, (bw+s)*c, (bh+s)*r ) -end - -local function HideGameTooltip() - GameTooltip:Hide() -end - -local function CreateControls(bar) - local f = bar.frame - - f:SetMovable(true) - f:SetResizable(true) - f:SetClampedToScreen(true) - - -- buttons on the bar should be direct children of the bar frame. - -- The control elements need to float on top of this, which we could - -- do with SetFrameLevel() or Raise(), but it's more reliable to do it - -- via frame nesting, hence good old foo's appearance here. - local foo = CreateFrame("Frame",nil,f) - foo:SetAllPoints() - - local control = CreateFrame("Button", nil, foo) - control:EnableMouse(true) - control:SetToplevel(true) - control:SetPoint("TOPLEFT", -4, 4) - control:SetPoint("BOTTOMRIGHT", 4, -4) - control:SetBackdrop({ - edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border", - tile = true, - tileSize = 16, - edgeSize = 16, - insets = { left = 0, right = 0, top = 0, bottom = 0 }, - }) - - -- textures - local bgTex = control:CreateTexture(nil,"BACKGROUND") - bgTex:SetTexture(0.7,0.7,1.0,0.2) - bgTex:SetPoint("TOPLEFT",4,-4) - bgTex:SetPoint("BOTTOMRIGHT",-4,4) - local hTex = control:CreateTexture(nil,"HIGHLIGHT") - hTex:SetTexture(0.7,0.7,1.0,0.2) - hTex:SetPoint("TOPLEFT",4,-4) - hTex:SetPoint("BOTTOMRIGHT",-4,4) - hTex:SetBlendMode("ADD") - - -- label - local label = control:CreateFontString(nil,"OVERLAY","GameFontNormalLarge") - label:SetAllPoints() - label:SetJustifyH("CENTER") - label:SetShadowColor(0,0,0,1) - label:SetShadowOffset(2,-2) - label:SetTextColor(1,1,1,1) - label:SetText(bar:GetName()) - label:Show() - bar.controlLabelString = label -- so that bar:SetName() can update it - - local StopResize = function() - f:StopMovingOrSizing() - f.isMoving = false - f:SetScript("OnUpdate",nil) - StoreExtents(bar) - ClampToButtons(bar) - ApplyAnchor(bar) - end - - -- edge drag handles - for _, point in pairs({"LEFT","TOP","RIGHT","BOTTOM"}) do - local edge = CreateFrame("Frame",nil,control) - edge:EnableMouse(true) - edge:SetWidth(8) - edge:SetHeight(8) - if point == "TOP" or point == "BOTTOM" then - edge:SetPoint(point.."LEFT") - edge:SetPoint(point.."RIGHT") - else - edge:SetPoint("TOP"..point) - edge:SetPoint("BOTTOM"..point) - end - local tex = edge:CreateTexture(nil,"HIGHLIGHT") - tex:SetTexture(1.0,0.82,0,0.7) - tex:SetBlendMode("ADD") - tex:SetAllPoints() - edge:RegisterForDrag("LeftButton") - edge:SetScript("OnMouseDown", - function() - local bw, bh = GetButtonSize(bar) - local r, c, s = GetButtonGrid(bar) - f:SetMinResize( bw+s+1, bh+s+1 ) - f:StartSizing(point) - f:SetScript("OnUpdate", - function() - RecomputeGrid(bar) - bar:RefreshLayout() - end - ) - end - ) - edge:SetScript("OnMouseUp", StopResize) - edge:SetScript("OnEnter", - function() - GameTooltip:SetOwner(f, "ANCHOR_"..point) - GameTooltip:AddLine(L["Drag to add/remove buttons"]) - GameTooltip:Show() - end - ) - edge:SetScript("OnLeave", HideGameTooltip) - edge:Show() - end - - -- corner drag handles, again nested in an anonymous frame so that they are on top - local foo2 = CreateFrame("Frame",nil,control) - foo2:SetAllPoints(true) - for _, point in pairs({"BOTTOMLEFT","TOPLEFT","BOTTOMRIGHT","TOPRIGHT"}) do - local corner = CreateFrame("Frame",nil,foo2) - corner:EnableMouse(true) - corner:SetWidth(12) - corner:SetHeight(12) - corner:SetPoint(point) - local tex = corner:CreateTexture(nil,"HIGHLIGHT") - tex:SetTexture(1.0,0.82,0,0.7) - tex:SetBlendMode("ADD") - tex:SetAllPoints() - corner:RegisterForDrag("LeftButton","RightButton") - local updateTooltip = function() - local size, size2 = bar:GetButtonSize() - local rows, cols, spacing = bar:GetButtonGrid() - size = (size == size2) and tostring(size) or format("%dx%d",size,size2) - GameTooltipTextRight4:SetText(size) - GameTooltipTextRight5:SetText(tostring(spacing)) - end - corner:SetScript("OnMouseDown", - function(_,btn) - local bw, bh = GetButtonSize(bar) - local r, c, s = GetButtonGrid(bar) - if btn == "LeftButton" then -- button resize - f:SetMinResize( (s+12)*c+1, (s+12)*r+1 ) - f:SetScript("OnUpdate", - function() - RecomputeButtonSize(bar) - bar:RefreshLayout() - updateTooltip() - end - ) - elseif btn == "RightButton" then -- spacing resize - f:SetMinResize( bw*c, bh*r ) - f:SetScript("OnUpdate", - function() - RecomputeButtonSpacing(bar) - bar:RefreshLayout() - updateTooltip() - end - ) - end - f:StartSizing(point) - end - ) - corner:SetScript("OnMouseUp",StopResize) - corner:SetScript("OnEnter", - function() - GameTooltip:SetOwner(f, "ANCHOR_"..point) - GameTooltip:AddLine(L["Drag to resize buttons"]) - GameTooltip:AddLine(L["Right-click-drag"]) - GameTooltip:AddLine(L["to change spacing"]) - local size, size2 = bar:GetButtonSize() - local rows, cols, spacing = bar:GetButtonGrid() - size = (size == size2) and tostring(size) or format("%dx%d",size,size2) - GameTooltip:AddDoubleLine(L["Size:"], size) - GameTooltip:AddDoubleLine(L["Spacing:"], tostring(spacing)) - GameTooltip:Show() - end - ) - corner:SetScript("OnLeave", - function() - GameTooltip:Hide() - f:SetScript("OnUpdate",nil) - end - ) - - end - - control:RegisterForDrag("LeftButton") - control:RegisterForClicks("RightButtonDown") - - control:SetScript("OnDragStart", - function() - f:StartMoving() - f.isMoving = true - -- TODO: snap indicator update install - end - ) - - control:SetScript("OnDragStop", - function() - f:StopMovingOrSizing() - f.isMoving = false - f:SetScript("OnUpdate",nil) - -- TODO: snap frame here - StoreExtents(bar) - end - ) - - control:SetScript("OnEnter", - function() - -- add bar type and status information to name - local name = bar.name - for _, m in ReAction:IterateModules() do - local suffix = safecall(m,"GetBarNameModifier",bar) - if suffix then - name = ("%s %s"):format(name,suffix) - end - end - - GameTooltip:SetOwner(f, "ANCHOR_TOPRIGHT") - GameTooltip:AddLine(name) - GameTooltip:AddLine(L["Drag to move"]) - --GameTooltip:AddLine(L["Shift-drag for sticky mode"]) - GameTooltip:AddLine(L["Right-click for options"]) - GameTooltip:Show() - end - ) - - control:SetScript("OnLeave", HideGameTooltip) - - control:SetScript("OnClick", - function() - bar:ShowMenu() - end - ) - - return control -end - -function Bar:ShowControls(show) - if show then - if not self.controlFrame then - self.controlFrame = CreateControls(self) - end - self.controlFrame:Show() - elseif self.controlFrame then - self.controlFrame:Hide() - end -end - -function Bar:ShowMenu() - if not self.menuOpts then - self.menuOpts = { - type = "group", - args = { - openConfig = { - type = "execute", - name = L["Configure..."], - desc = L["Open the configuration dialogue for this bar"], - func = function() module:OpenConfig(self) end, - disabled = InCombatLockdown, - order = 1 - }, - delete = { - type = "execute", - name = L["Delete Bar"], - desc = L["Remove the bar from the current profile"], - func = function() ReAction:EraseBar(self) end, - order = 2 - }, - } - } - end - if self.modMenuOpts == nil then - self.modMenuOpts = { } - end - for _, m in ReAction:IterateModules() do - local opts = safecall(m,"GetBarMenuOptions",self,module) - if opts then - for k, v in pairs(opts) do - self.menuOpts.args[k] = v - end - end - end - Dewdrop:Open(self.controlFrame, "children", self.menuOpts, "cursorX", true, "cursorY", true) -end - -local Bar_SuperSetName = Bar.SetName -function Bar:SetName(name) - Bar_SuperSetName(self,name) - if self.controlLabelString then - self.controlLabelString:SetText(self.name) - end -end
--- a/modules/ReAction_ConfigUI/lib/AceLibrary/AceLibrary.lua Thu Apr 03 16:59:16 2008 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,856 +0,0 @@ ---[[ -Name: AceLibrary -Revision: $Rev: 49421 $ -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: 49421 $ -Website: http://www.wowace.com/ -Documentation: http://www.wowace.com/index.php/AceLibrary -SVN: http://svn.wowace.com/root/trunk/Ace2/AceLibrary -Description: Versioning library to handle other library instances, upgrading, - and proper access. - It also provides a base for libraries to work off of, providing - proper error tools. It is handy because all the errors occur in the - file that called it, not in the library file itself. -Dependencies: None -License: LGPL v2.1 -]] - -local ACELIBRARY_MAJOR = "AceLibrary" -local ACELIBRARY_MINOR = "$Revision: 49421 $" - -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 - -local WoW22 = false -if type(GetBuildInfo) == "function" then - local success, buildinfo = pcall(GetBuildInfo) - if success and type(buildinfo) == "string" then - local num = tonumber(buildinfo:match("^(%d+%.%d+)")) - if num and num >= 2.2 then - WoW22 = true - end - 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 assert -if not WoW22 then - function assert(self, condition, message, ...) - if not condition then - if not message then - local stack = debugstack() - local second = stack:match("\n(.-)\n") - message = "assertion failed! " .. second - end - return error(self, message, ...) - end - return condition - end -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 WoW22 and not rawget(instance, 'assert') then - rawset(instance, 'assert', assert) - end - if not rawget(instance, 'argCheck') then - rawset(instance, 'argCheck', argCheck) - end - if not rawget(instance, 'pcall') then - rawset(instance, 'pcall', pcall) - end - addToPositions(instance, major) - if activateFunc then - safecall(activateFunc, instance, nil, nil) -- no old version, so explicit nil - ---[[ if major ~= ACELIBRARY_MAJOR then - for k,v in pairs(_G) do - if v == instance 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]] - 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_assert, old_argCheck, old_pcall - if isAceLibrary then - self = instance - AceLibrary = instance - - old_error = instance.error - if not WoW22 then - old_assert = instance.assert - end - old_argCheck = instance.argCheck - old_pcall = instance.pcall - - self.error = error - if not WoW22 then - self.assert = assert - end - 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 WoW22 and not rawget(instance, 'assert') then - rawset(instance, 'assert', assert) - end - if not rawget(instance, 'argCheck') then - rawset(instance, 'argCheck', argCheck) - end - if not rawget(instance, 'pcall') then - rawset(instance, 'pcall', pcall) - end - if isAceLibrary then - for _,v in pairs(self.libs) do - local i = type(v) == "table" and v.instance - if type(i) == "table" then - if not rawget(i, 'error') or i.error == old_error then - rawset(i, 'error', error) - end - if not WoW22 and (not rawget(i, 'assert') or i.assert == old_assert) then - rawset(i, 'assert', assert) - end - if not rawget(i, 'argCheck') or i.argCheck == old_argCheck then - rawset(i, 'argCheck', argCheck) - end - if not rawget(i, 'pcall') or i.pcall == old_pcall then - rawset(i, 'pcall', pcall) - end - end - end - end - if activateFunc then - safecall(activateFunc, instance, oldInstance, oldDeactivateFunc) - ---[[ if major ~= ACELIBRARY_MAJOR then - for k,v in pairs(_G) do - if v == instance 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]] - 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)
--- a/modules/ReAction_ConfigUI/lib/AceLibrary/AceLibrary.toc Thu Apr 03 16:59:16 2008 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,10 +0,0 @@ -## Interface: 20200 - -## 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
--- a/modules/ReAction_ConfigUI/lib/AceOO-2.0/AceOO-2.0.lua Thu Apr 03 16:59:16 2008 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,980 +0,0 @@ ---[[ -Name: AceOO-2.0 -Revision: $Rev: 38641 $ -Developed by: The Ace Development Team (http://www.wowace.com/index.php/The_Ace_Development_Team) -Inspired By: Ace 1.x by Turan (turan@gryphon.com) -Website: http://www.wowace.com/ -Documentation: http://www.wowace.com/index.php/AceOO-2.0 -SVN: http://svn.wowace.com/root/trunk/Ace2/AceOO-2.0 -Description: Library to provide an object-orientation framework. -Dependencies: AceLibrary -License: MIT -]] - -local MAJOR_VERSION = "AceOO-2.0" -local MINOR_VERSION = "$Revision: 38641 $" - --- 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)
--- a/modules/ReAction_ConfigUI/lib/AceOO-2.0/AceOO-2.0.toc Thu Apr 03 16:59:16 2008 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,12 +0,0 @@ -## Interface: 20200 - -## 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
--- a/modules/ReAction_ConfigUI/lib/Dewdrop-2.0/Dewdrop-2.0.lua Thu Apr 03 16:59:16 2008 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,3487 +0,0 @@ ---[[ -Name: Dewdrop-2.0 -Revision: $Rev: 48630 $ -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 = "$Revision: 48630 $" - -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 \195\188ber dem Schieberegler bist, um 10er 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'?" -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.IsFrameType 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 == "SharedMedia-1.0" then - SharedMedia = instance - end -end - -AceLibrary:Register(Dewdrop, MAJOR_VERSION, MINOR_VERSION, activate, nil, external)
--- a/modules/ReAction_ConfigUI/lib/embeds.xml Thu Apr 03 16:59:16 2008 +0000 +++ b/modules/ReAction_ConfigUI/lib/embeds.xml Thu Apr 03 20:25:40 2008 +0000 @@ -2,13 +2,10 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.blizzard.com/wow/ui/..\FrameXML\UI.xsd"> - <Script file="AceLibrary\AceLibrary.lua"/> - <Script file="AceOO-2.0\AceOO-2.0.lua"/> - <Script file="Waterfall-1.0\Waterfall-1.0.lua"/> - <Script file="Dewdrop-2.0\Dewdrop-2.0.lua"/> - <Include file="AceGUI-3.0\AceGUI-3.0.xml"/> - <Include file="AceConfig-3.0\AceConfig-3.0.xml"/> + <Include file="AceConfigRegistry-3.0\AceConfigRegistry-3.0.xml"/> + <Include file="AceConfigDialog-3.0\AceConfigDialog-3.0.xml"/> + <Include file="AceDBOptions-3.0\AceDBOptions-3.0.xml"/> </Ui> \ No newline at end of file