diff libs/Dewdrop-2.0/Dewdrop-2.0.lua @ 1:c11ca1d8ed91

Version 0.1
author Flick <flickerstreak@gmail.com>
date Tue, 20 Mar 2007 21:03:57 +0000
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libs/Dewdrop-2.0/Dewdrop-2.0.lua	Tue Mar 20 21:03:57 2007 +0000
@@ -0,0 +1,2751 @@
+--[[
+Name: Dewdrop-2.0
+Revision: $Rev: 19976 $
+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
+]]
+
+local MAJOR_VERSION = "Dewdrop-2.0"
+local MINOR_VERSION = "$Revision: 19976 $"
+
+if not AceLibrary then error(MAJOR_VERSION .. " requires AceLibrary") end
+if not AceLibrary:IsNewVersion(MAJOR_VERSION, MINOR_VERSION) then return end
+
+local Dewdrop = {}
+
+local CLOSE = "Close"
+local CLOSE_DESC = "Close the menu."
+local VALIDATION_ERROR = "Validation error."
+local RESET_KEYBINDING_DESC = "Hit escape to clear the keybinding."
+
+if GetLocale() == "deDE" then
+--	VALIDATION_ERROR = "some message here..."
+end
+
+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
+
+local function GetScaledCursorPosition()
+	local x, y = GetCursorPosition()
+	local scale = UIParent:GetEffectiveScale()
+	return x / scale, y / scale
+end
+
+local function StartCounting(self, levelNum)
+	for i = levelNum, #levels 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, arg1)
+	for _,level in ipairs(levels) do
+		if level.count then
+			level.count = level.count - arg1
+			if level.count < 0 then
+				level.count = nil
+				self:Close(level.num)
+			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()
+		if level.lastDirection == "RIGHT" then
+			if level.lastVDirection == "DOWN" then
+				level:SetPoint("TOPLEFT", level.parent or level:GetParent(), "TOPRIGHT", 5, 10)
+			else
+				level:SetPoint("BOTTOMLEFT", level.parent or level:GetParent(), "BOTTOMRIGHT", 5, -10)
+			end
+		else
+			if level.lastVDirection == "DOWN" then
+				level:SetPoint("TOPRIGHT", level.parent or level:GetParent(), "TOPLEFT", -5, 10)
+			else
+				level:SetPoint("BOTTOMRIGHT", level.parent or level:GetParent(), "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()
+		if level.lastDirection == "RIGHT" then
+			if level.lastVDirection == "DOWN" then
+				level:SetPoint("TOPLEFT", level.parent or level:GetParent(), "TOPRIGHT", 5, 10)
+			else
+				level:SetPoint("BOTTOMLEFT", level.parent or level:GetParent(), "BOTTOMRIGHT", 5, -10)
+			end
+		else
+			if level.lastVDirection == "DOWN" then
+				level:SetPoint("TOPRIGHT", level.parent or level:GetParent(), "TOPLEFT", -5, 10)
+			else
+				level:SetPoint("BOTTOMRIGHT", level.parent or level:GetParent(), "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 sliderFrame
+local editBoxFrame
+
+local function showGameTooltip(this)
+	if this.tooltipTitle or this.tooltipText then
+		GameTooltip_SetDefaultAnchor(GameTooltip, this)
+		local disabled = not this.isTitle and this.disabled
+		if this.tooltipTitle then
+			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 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 disabled then
+				GameTooltip:SetText(this.tooltipText, 0.5, 0.5, 0.5, 1)
+			else
+				GameTooltip:SetText(this.tooltipText, 1, 1, 1, 1)
+			end
+		end
+		GameTooltip:Show()
+	end
+	if this.tooltipFunc then
+		GameTooltip:SetOwner(this, "ANCHOR_NONE")
+		GameTooltip:SetPoint("TOPLEFT", this, "TOPRIGHT", 5, 0)
+		this.tooltipFunc(this.tooltipArg1, this.tooltipArg2, this.tooltipArg3, this.tooltipArg4)
+		GameTooltip:Show()
+	end
+end
+
+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.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
+			end
+			showGameTooltip(this)
+		end)
+		button:SetScript("OnLeave", function()
+			if not this.selected then
+				highlight:Hide()
+			end
+			button.radioHighlight:Hide()
+			if this.level then
+				StartCounting(self, this.level.num)
+			end
+			GameTooltip:Hide()
+		end)
+		button:SetScript("OnClick", function()
+			if not this.disabled then
+				if this.hasColorSwatch then
+					local func = button.colorFunc
+					local a1,a2,a3,a4 = button.colorArg1, button.colorArg2, button.colorArg3, button.colorArg4
+					local hasOpacity = this.hasOpacity
+					ColorPickerFrame.func = function()
+						if func then
+							local r,g,b = ColorPickerFrame:GetColorRGB()
+							local a = hasOpacity and 1 - OpacitySliderFrame:GetValue() or nil
+							if a1 == nil then
+								func(r, g, b, a)
+							elseif a2 == nil then
+								func(a1, r, g, b, a)
+							elseif a3 == nil then
+								func(a1, a2, r, g, b, a)
+							elseif a4 == nil then
+								func(a1, a2, a3, r, g, b, a)
+							else
+								func(a1, a2, a3, a4, r, g, b, a)
+							end
+						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 a1 == nil then
+							func(r, g, b, a)
+						elseif a2 == nil then
+							func(a1, r, g, b, a)
+						elseif a3 == nil then
+							func(a1, a2, r, g, b, a)
+						else
+							func(a1, a2, a3, r, g, b, a)
+						end
+					end
+					self:Close(1)
+					ShowUIPanel(ColorPickerFrame)
+				elseif this.func then
+					local level = button.level
+					if type(this.func) == "string" then
+						self:assert(type(this.arg1[this.func]) == "function", "Cannot call method " .. this.func)
+						this.arg1[this.func](this.arg1, this.arg2, this.arg3, this.arg4)
+					else
+						this.func(this.arg1, this.arg2, this.arg3, this.arg4)
+					end
+					if this.closeWhenClicked then
+						self:Close()
+					elseif level:IsShown() then
+						for i = 1, level.num do
+							Refresh(self, levels[i])
+						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, "OVERLAY")
+		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(1, 1, 1)
+		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 then
+				level:SetPoint("TOPRIGHT", level.parent, "TOPLEFT")
+			else
+				level:SetPoint("CENTER", level.parent, "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()
+					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 = GetFullScreenFrame()
+	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 checkValidate(validateFunc, func, arg1, arg2, arg3)
+	local text
+	if arg3 ~= nil then
+		text = arg3
+	elseif arg2 ~= nil then
+		text = arg2
+	else
+		text = arg1
+	end
+	if not validateFunc(text) then
+		DEFAULT_CHAT_FRAME:AddMessage("|cffffff7fValidation error: [|r" .. tostring(text) .. "|cffffff7f]|r")
+	else
+		func(arg1, arg2, arg3)
+	end
+end
+
+local function validateOptions(options, position, baseOptions, fromPass)
+	if not baseOptions then
+		baseOptions = options
+	end
+	if type(options) ~= "table" then
+		return "Options must be a table.", position
+	end
+	local kind = options.type
+	if type(kind) ~= "string" then
+		return '"type" must be a string.', position
+	elseif kind ~= "group" and kind ~= "range" and kind ~= "text" and kind ~= "execute" and kind ~= "toggle" and kind ~= "color" and kind ~= "header" then
+		return '"type" must either be "range", "text", "group", "toggle", "execute", "color", or "header".', position
+	end
+	if options.aliases then
+		if type(options.aliases) ~= "table" and type(options.aliases) ~= "string" then
+			return '"alias" must be a table or string', position
+		end
+	end
+	if not fromPass then
+		if kind == "execute" then
+			if type(options.func) ~= "string" and type(options.func) ~= "function" then
+				return '"func" must be a string or function', position
+			end
+		elseif kind == "range" or kind == "text" or kind == "toggle" then
+			if type(options.set) ~= "string" and type(options.set) ~= "function" then
+				return '"set" must be a string or function', position
+			end
+			if kind == "text" and options.get == false then
+			elseif type(options.get) ~= "string" and type(options.get) ~= "function" then
+				return '"get" must be a string or function', position
+			end
+		elseif kind == "group" and options.pass then
+			if options.pass ~= true then
+				return '"pass" must be either nil, true, or false', position
+			end
+			if not options.func then
+				if type(options.set) ~= "string" and type(options.set) ~= "function" then
+					return '"set" must be a string or function', position
+				end
+				if type(options.get) ~= "string" and type(options.get) ~= "function" then
+					return '"get" must be a string or function', position
+				end
+			elseif type(options.func) ~= "string" and type(options.func) ~= "function" then
+				return '"func" must be a string or function', position
+			end
+		end
+	else
+		if kind == "group" then
+			return 'cannot have "type" = "group" as a subgroup of a passing group', position
+		end
+	end
+	if options ~= baseOptions then
+		if kind == "header" then
+		elseif type(options.desc) ~= "string" then
+			return '"desc" must be a string', position
+		elseif 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
+		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
+	elseif kind == "range" then
+		if options.min or options.max then
+			if type(options.min) ~= "number" then
+				return '"min" must be a number', position
+			elseif type(options.max) ~= "number" then
+				return '"max" must be a number', position
+			elseif options.min >= options.max then
+				return '"min" must be less than "max"', position
+			end
+		end
+		if options.step then
+			if type(options.step) ~= "number" then
+				return '"step" must be a number', position
+			elseif options.step < 0 then
+				return '"step" must be nonnegative', position
+			end
+		end
+		if options.isPercent and options.isPercent ~= true then
+			return '"isPercent" must either be nil, true, or false', position
+		end
+	elseif kind == "toggle" then
+		if options.map then
+			if type(options.map) ~= "table" then
+				return '"map" must be a table', position
+			elseif type(options.map[true]) ~= "string" then
+				return '"map[true]" must be a string', position
+			elseif type(options.map[false]) ~= "string" then
+				return '"map[false]" must be a string', position
+			end
+		end
+	elseif kind == "color" then
+		if options.hasAlpha and options.hasAlpha ~= true then
+			return '"hasAlpha" must be nil, true, or false', position
+		end
+	elseif kind == "group" then
+		if options.pass and options.pass ~= true then
+			return '"pass" must be nil, true, or false', position
+		end
+		if type(options.args) ~= "table" then
+			return '"args" must be a table', position
+		end
+		for k,v in pairs(options.args) do
+			if type(k) ~= "string" then
+				return '"args" keys must be strings', position
+			elseif k:find("%s") then
+				return string.format('"args" keys must not include spaces. %q is not appropriate.', k), position
+			elseif k:len() == 0 then
+				return '"args" keys must not be 0-length strings.', position
+			end
+			if type(v) ~= "table" then
+				return '"args" values must be tables', position and position .. "." .. k or k
+			end
+			local newposition
+			if position then
+				newposition = position .. ".args." .. k
+			else
+				newposition = "args." .. k
+			end
+			local err, pos = validateOptions(v, newposition, baseOptions, options.pass)
+			if err then
+				return err, pos
+			end
+		end
+	end
+	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
+
+function Dewdrop:FeedAceOptionsTable(options, difference)
+	self:argCheck(options, 2, "table")
+	self:argCheck(difference, 3, "nil", "number")
+	self:assert(currentLevel, "Cannot call `FeedAceOptionsTable' outside of a Dewdrop declaration")
+	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]
+	self:assert(level, "Improper level given")
+	if not values then
+		values = {}
+	else
+		for k,v in pairs(values) do
+			values[k] = nil
+		end
+	end
+
+	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
+
+	local realOptions = options
+	local handler = options.handler
+	local passTable
+	local passValue
+	while #values > 0 do
+		passTable = options.pass and current or nil
+		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
+		for k in pairs(options.args) do
+			table.insert(values, k)
+		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 true
+					elseif not bravo_name then
+						return false
+					else
+						return alpha_name:upper() < bravo_name: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" then
+				hidden = hidden()
+			elseif type(hidden) == "string" then
+				local f = hidden
+				local neg = f:match("^~(.-)$")
+				if neg then
+					f = neg
+				end
+				hidden = handler[f](handler)
+				if neg then
+					hidden = not hidden
+				end
+			end
+			if not hidden then
+				if type(disabled) == "function" then
+					disabled = disabled()
+				elseif type(disabled) == "string" then
+					local f = disabled
+					local neg = f:match("^~(.-)$")
+					if neg then
+						f = neg
+					end
+					disabled = handler[f](handler)
+					if neg then
+						disabled = not disabled
+					end
+				end
+				local name = (v.guiIconOnly and v.icon) and "" or (v.guiName or v.name)
+				local desc = 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 v.type == "toggle" then
+					local checked
+					local checked_arg
+					if type(v.get) == "function" then
+						checked = v.get(passValue)
+						checked_arg = checked
+					else
+						local f = v.get
+						local neg = f:match("^~(.-)$")
+						if neg then
+							f = neg
+						end
+						if not handler[f] then
+							Dewdrop:error("Handler %q not available", f)
+						end
+						checked = handler[f](handler, passValue)
+						checked_arg = checked
+						if neg then
+							checked = not checked
+						end
+					end
+					local func, arg1, arg2, arg3
+					if type(v.set) == "function" then
+						func = v.set
+						if passValue ~= nil then
+							arg1 = passValue
+							arg2 = not checked_arg
+						else
+							arg1 = not checked_arg
+						end
+					else
+						if not handler[v.set] then
+							Dewdrop:error("Handler %q not available", v.set)
+						end
+						func = handler[v.set]
+						arg1 = handler
+						if passValue ~= nil then
+							arg2 = passValue
+							arg3 = not checked_arg
+						else
+							arg2 = not checked_arg
+						end
+					end
+					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")
+						checked = 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
+					if type(v.func) == "function" then
+						func = v.func
+						arg1 = passValue
+					else
+						if not handler[v.func] then
+							Dewdrop:error("Handler %q not available", v.func)
+						end
+						func = handler[v.func]
+						arg1 = handler
+						arg2 = passValue
+					end
+					self:AddLine(
+						'text', name,
+						'checked', checked,
+						'func', func,
+						'arg1', arg1,
+						'arg2', arg2,
+						'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
+					if type(v.get) == "function" then
+						sliderValue = v.get(passValue)
+					else
+						if not handler[v.get] then
+							Dewdrop:error("Handler %q not available", v.get)
+						end
+						sliderValue = handler[v.get](handler, passValue)
+					end
+					local sliderFunc, sliderArg1, sliderArg2
+					if type(v.set) == "function" then
+						sliderFunc = v.set
+						sliderArg1 = passValue
+					else
+						if not handler[v.set] then
+							Dewdrop:error("Handler %q not available", v.set)
+						end
+						sliderFunc = handler[v.set]
+						sliderArg1 = handler
+						sliderArg2 = passValue
+					end
+					self:AddLine(
+						'text', name,
+						'hasArrow', true,
+						'hasSlider', true,
+						'sliderMin', v.min or 0,
+						'sliderMax', v.max or 1,
+						'sliderStep', v.step or 0,
+						'sliderIsPercent', v.isPercent or false,
+						'sliderValue', sliderValue,
+						'sliderFunc', sliderFunc,
+						'sliderArg1', sliderArg1,
+						'sliderArg2', sliderArg2,
+						'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
+					if type(v.get) == "function" then
+						r,g,b,a = v.get(passValue)
+					else
+						if not handler[v.get] then
+							Dewdrop:error("Handler %q not available", v.get)
+						end
+						r,g,b,a = handler[v.get](handler, passValue)
+					end
+					local colorFunc, colorArg1, colorArg2
+					if type(v.set) == "function" then
+						colorFunc = v.set
+						colorArg1 = passValue
+					else
+						if not handler[v.set] then
+							Dewdrop:error("Handler %q not available", v.set)
+						end
+						colorFunc = handler[v.set]
+						colorArg1 = handler
+						colorArg2 = passValue
+					end
+					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
+						self:AddLine(
+							'text', name,
+							'hasArrow', true,
+							'value', k,
+							'disabled', disabled,
+							'tooltipTitle', tooltipTitle,
+							'tooltipText', tooltipText,
+							'icon', v.icon,
+							'iconHeight', iconHeight,
+							'iconWidth', iconWidth,
+							'iconCoordLeft', iconCoordLeft,
+							'iconCoordRight', iconCoordRight,
+							'iconCoordTop', iconCoordTop,
+							'iconCoordBottom', iconCoordBottom
+						)
+					else
+						local editBoxText
+						if type(v.get) == "function" then
+							editBoxText = v.get(passValue)
+						elseif v.get == false then
+							editBoxText = nil
+						else
+							if not handler[v.get] then
+								Dewdrop:error("Handler %q not available", v.get)
+							end
+							editBoxText = handler[v.get](handler, passValue)
+						end
+						local editBoxFunc, editBoxArg1, editBoxArg2
+						if type(v.set) == "function" then
+							editBoxFunc = v.set
+							editBoxArg1 = passValue
+						else
+							if not handler[v.set] then
+								Dewdrop:error("Handler %q not available", v.set)
+							end
+							editBoxFunc = handler[v.set]
+							editBoxArg1 = handler
+							editBoxArg2 = passValue
+						end
+						
+						local editBoxValidateFunc, editBoxValidateArg1
+
+						if v.validate and v.validate ~= "keybinding" then
+							if type(v.validate) == "function" then
+								editBoxValidateFunc = v.validate
+							else
+								if not handler[v.validate] then
+									Dewdrop:error("Handler %q not available", v.validate)
+								end
+								editBoxValidateFunc = handler[v.validate]
+								editBoxValidateArg1 = handler
+							end
+						elseif v.validate then
+							if tooltipText then
+								tooltipText = tooltipText .. "\n\n" .. RESET_KEYBINDING_DESC
+							else
+								tooltipText = RESET_KEYBINDING_DESC
+							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",
+							'disabled', disabled,
+							'tooltipTitle', tooltipTitle,
+							'tooltipText', tooltipText
+						)
+					end
+				elseif v.type == "group" then
+					self:AddLine(
+						'text', name,
+						'hasArrow', true,
+						'value', k,
+						'disabled', disabled,
+						'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
+		if type(options.get) == "function" then
+			current = options.get(passValue)
+		elseif options.get ~= false then
+			if not handler[options.get] then
+				Dewdrop:error("Handler %q not available", options.get)
+			end
+			current = handler[options.get](handler, 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] < othersort_validate[bravo]
+				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
+			if type(options.set) == "function" then
+				func = options.set
+				if passValue ~= nil then
+					arg1 = passValue
+					arg2 = k
+				else
+					arg1 = k
+				end
+			else
+				if not handler[options.set] then
+					Dewdrop:error("Handler %q not available", options.set)
+				end
+				func = handler[options.set]
+				arg1 = handler
+				if passValue ~= nil then
+					arg2 = passValue
+					arg3 = k
+				else
+					arg2 = k
+				end
+			end
+			local checked = (k == current or (type(k) == "string" and type(current) == "string" and k:lower() == current:lower()))
+			self:AddLine(
+				'text', v,
+				'func', not checked and func or nil,
+				'arg1', not checked and arg1 or nil,
+				'arg2', not checked and arg2 or nil,
+				'arg3', not checked and arg3 or nil,
+				'isRadio', true,
+				'checked',  checked,
+				'tooltipTitle', options.guiName or options.name,
+				'tooltipText', v
+			)
+		end
+		for k in pairs(values) do
+			values[k] = nil
+		end
+	else
+		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:') 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")
+	Refresh(self, levels[level])
+end
+
+function OpenSlider(self, parent)
+	if not sliderFrame then
+		sliderFrame = CreateFrame("Frame", nil, nil)
+		sliderFrame:SetWidth(80)
+		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: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.01)
+		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 text = slider:CreateFontString(nil, "ARTWORK")
+		sliderFrame.currentText = text
+		text:SetFontObject(GameFontHighlightSmall)
+		text:SetText("50%")
+		text:SetPoint("LEFT", slider, "RIGHT")
+		text:SetPoint("RIGHT", sliderFrame, "RIGHT", -6, 0)
+		text:SetJustifyH("CENTER")
+		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 = sliderFrame.parent.sliderStep or (max - min) / 100
+				local a1,a2,a3,a4 = sliderFrame.parent.sliderArg1, sliderFrame.parent.sliderArg2, sliderFrame.parent.sliderArg3, sliderFrame.parent.sliderArg4
+				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
+				if a1 == nil then
+					text = sliderFrame.parent.sliderFunc(value)
+				elseif a2 == nil then
+					text = sliderFrame.parent.sliderFunc(a1, value)
+				elseif a3 == nil then
+					text = sliderFrame.parent.sliderFunc(a1, a2, value)
+				elseif a4 == nil then
+					text = sliderFrame.parent.sliderFunc(a1, a2, a3, value)
+				else
+					text = sliderFrame.parent.sliderFunc(a1, a2, a3, a4, value)
+				end
+				if text 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 = sliderFrame.parent.sliderStep or (max - min) / 100
+				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)
+		sliderFrame:SetScript("OnEnter", function()
+			StopCounting(self, sliderFrame.level)
+			showGameTooltip(sliderFrame.parent)
+		end)
+		sliderFrame:SetScript("OnLeave", function()
+			StartCounting(self, sliderFrame.level)
+			GameTooltip:Hide()
+		end)
+		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
+			StartCounting(self, sliderFrame.level)
+			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
+			end
+		end)
+	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.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
+	sliderFrame.slider:SetValue(1 - (parent.sliderValue - parent.sliderMin) / (parent.sliderMax - parent.sliderMin))
+	sliderFrame.changing = false
+	sliderFrame.bottomText:SetText(parent.sliderMinText or "0")
+	sliderFrame.topText:SetText(parent.sliderMaxText or "1")
+	local text
+	if parent.sliderFunc then
+		local a1,a2,a3,a4 = parent.sliderArg1, parent.sliderArg2, parent.sliderArg3, parent.sliderArg4
+		if a1 == nil then
+			text = parent.sliderFunc(parent.sliderValue)
+		elseif a2 == nil then
+			text = parent.sliderFunc(a1, parent.sliderValue)
+		elseif a3 == nil then
+			text = parent.sliderFunc(a1, a2, parent.sliderValue)
+		elseif a4 == nil then
+			text = parent.sliderFunc(a1, a2, a3, parent.sliderValue)
+		else
+			text = parent.sliderFunc(a1, a2, a3, a4, parent.sliderValue)
+		end
+	end
+	if text then
+		sliderFrame.currentText:SetText(text)
+	elseif parent.sliderIsPercent then
+		sliderFrame.currentText:SetText(string.format("%.0f%%", parent.sliderValue * 100))
+	else
+		sliderFrame.currentText:SetText(parent.sliderValue)
+	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: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 a1,a2,a3,a4 = editBoxFrame.parent.editBoxValidateArg1, editBoxFrame.parent.editBoxValidateArg2, editBoxFrame.parent.editBoxValidateArg3, editBoxFrame.parent.editBoxValidateArg4
+				
+				local t = editBox.realText or editBox:GetText() or ""
+				local result
+				if a1 == nil then
+					result = editBoxFrame.parent.editBoxValidateFunc(t)
+				elseif a2 == nil then
+					result = editBoxFrame.parent.editBoxValidateFunc(a1, t)
+				elseif a3 == nil then
+					result = editBoxFrame.parent.editBoxValidateFunc(a1, a2, t)
+				elseif a4 == nil then
+					result = editBoxFrame.parent.editBoxValidateFunc(a1, a2, a3, t)
+				else
+					result = editBoxFrame.parent.editBoxValidateFunc(a1, a2, a3, a4, t)
+				end
+				if not result then
+					UIErrorsFrame:AddMessage(VALIDATION_ERROR, 1, 0, 0)
+					return
+				end
+			end
+			if editBoxFrame.parent and editBoxFrame.parent.editBoxFunc then
+				local a1,a2,a3,a4 = editBoxFrame.parent.editBoxArg1, editBoxFrame.parent.editBoxArg2, editBoxFrame.parent.editBoxArg3, editBoxFrame.parent.editBoxArg4
+				local t
+				if editBox.realText ~= "NONE" then
+					t = editBox.realText or editBox:GetText() or ""
+				end
+				if a1 == nil then
+					editBoxFrame.parent.editBoxFunc(t)
+				elseif a2 == nil then
+					editBoxFrame.parent.editBoxFunc(a1, t)
+				elseif a3 == nil then
+					editBoxFrame.parent.editBoxFunc(a1, a2, t)
+				elseif a4 == nil then
+					editBoxFrame.parent.editBoxFunc(a1, a2, a3, t)
+				else
+					editBoxFrame.parent.editBoxFunc(a1, a2, a3, a4, t)
+				end
+			end
+			self:Close(editBoxFrame.level)
+			for i = 1, editBoxFrame.level - 1 do
+				Refresh(self, levels[i])
+			end
+		end)
+		editBox:SetScript("OnEscapePressed", function()
+			self:Close(editBoxFrame.level)
+		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(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 a1,a2,a3,a4 = editBoxFrame.parent.editBoxChangeArg1, editBoxFrame.parent.editBoxChangeArg2, editBoxFrame.parent.editBoxChangeArg3, editBoxFrame.parent.editBoxChangeArg4
+				local t
+				if editBox.realText ~= "NONE" then
+					t = editBox.realText or editBox:GetText() or ""
+				end
+				local text
+				if a1 == nil then
+					text = editBoxFrame.parent.editBoxChangeFunc(t)
+				elseif a2 == nil then
+					text = editBoxFrame.parent.editBoxChangeFunc(a1, t)
+				elseif a3 == nil then
+					text = editBoxFrame.parent.editBoxChangeFunc(a1, a2, t)
+				elseif a4 == nil then
+					text = editBoxFrame.parent.editBoxChangeFunc(a1, a2, a3, t)
+				else
+					text = editBoxFrame.parent.editBoxChangeFunc(a1, a2, a3, a4, t)
+				end
+				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()
+			StartCounting(self, editBoxFrame.level)
+			GameTooltip:Hide()
+		end)
+		editBox:SetScript("OnEnter", function()
+			StopCounting(self, editBoxFrame.level)
+			showGameTooltip(editBoxFrame.parent)
+		end)
+		editBox:SetScript("OnLeave", function()
+			StartCounting(self, editBoxFrame.level)
+			GameTooltip:Hide()
+		end)
+		editBoxFrame:SetScript("OnKeyDown", function()
+			if not editBox.keybinding then
+				return
+			end
+			local arg1 = 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 == "BUTTON1" or arg1 == "BUTTON2" or arg1 == "UNKNOWN" then
+				return
+			elseif arg1 == "SHIFT" or arg1 == "CTRL" or arg1 == "ALT" then
+				return
+			elseif arg1 == "ENTER" then
+				return editBox:GetScript("OnEnterPressed")()
+			elseif arg1 == "ESCAPE" then
+				if editBox.realText == "NONE" then
+					return editBox:GetScript("OnEscapePressed")()
+				else
+					editBox:SpecialSetText(NONE or "NONE")
+					editBox.realText = "NONE"
+					return
+				end
+			end
+			local s = GetBindingText(arg1, "KEY_")
+			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(s)
+				editBox.realText = real
+				return editBox:GetScript("OnTextChanged")()
+			end
+		end)
+		editBoxFrame:SetScript("OnMouseDown", editBoxFrame:GetScript("OnKeyDown"))
+		editBox:SetScript("OnMouseDown", editBoxFrame:GetScript("OnKeyDown"))
+	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)
+	
+	if parent.editBoxIsKeybinding then
+		local s = parent.editBoxText
+		editBoxFrame.editBox.realText = s
+		if s and s ~= "" 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: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:IsOpen(parent)
+	self:argCheck(parent, 2, "table", "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
+	parent:GetCenter()
+	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:SetClampedToScreen(false)
+	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 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, parent, 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, parent, 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, parent, 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, parent, 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)
+	StartCounting(self, level)
+end
+
+function Dewdrop:IsRegistered(parent)
+	self:argCheck(parent, 2, "table")
+	return not not self.registry[parent]
+end
+
+function Dewdrop:Register(parent, ...)
+	self:argCheck(parent, 2, "table")
+	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] then
+		if parent:HasScript("OnMouseUp") then
+			local script = parent:GetScript("OnMouseUp")
+			parent:SetScript("OnMouseUp", function()
+				if script then
+					script()
+				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()
+				if script then
+					script()
+				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")
+	self.registry[parent] = nil
+end
+
+function Dewdrop:Open(parent, ...)
+	self:argCheck(parent, 2, "table")
+	local info
+	local k1 = ...
+	if type(k1) == "table" and k1[0] and k1.IsFrameType and self.registry[k1] then
+		info = tmp()
+		for k,v in pairs(self.registry[k1]) do
+			info[k] = v
+		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: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
+	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.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
+	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:SetTexture(button.r, button.g, button.b)
+		button.checked = false
+		button.func = nil
+		button.colorFunc = info.colorFunc
+		button.colorArg1 = info.colorArg1
+		button.colorArg2 = info.colorArg2
+		button.colorArg3 = info.colorArg3
+		button.colorArg4 = info.colorArg4
+		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.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.sliderArg1 = info.sliderArg1
+			button.sliderArg2 = info.sliderArg2
+			button.sliderArg3 = info.sliderArg3
+			button.sliderArg4 = info.sliderArg4
+		elseif info.hasEditBox then
+			button.hasEditBox = true
+			button.editBoxText = info.editBoxText or ""
+			button.editBoxFunc = info.editBoxFunc
+			button.editBoxArg1 = info.editBoxArg1
+			button.editBoxArg2 = info.editBoxArg2
+			button.editBoxArg3 = info.editBoxArg3
+			button.editBoxArg4 = info.editBoxArg4
+			button.editBoxChangeFunc = info.editBoxChangeFunc
+			button.editBoxChangeArg1 = info.editBoxChangeArg1
+			button.editBoxChangeArg2 = info.editBoxChangeArg2
+			button.editBoxChangeArg3 = info.editBoxChangeArg3
+			button.editBoxChangeArg4 = info.editBoxChangeArg4
+			button.editBoxValidateFunc = info.editBoxValidateFunc
+			button.editBoxValidateArg1 = info.editBoxValidateArg1
+			button.editBoxValidateArg2 = info.editBoxValidateArg2
+			button.editBoxValidateArg3 = info.editBoxValidateArg3
+			button.editBoxValidateArg4 = info.editBoxValidateArg4
+			button.editBoxIsKeybinding = info.editBoxIsKeybinding
+		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
+	button.arg1 = info.arg1
+	button.arg2 = info.arg2
+	button.arg3 = info.arg3
+	button.arg4 = info.arg4
+	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
+	button.tooltipArg1 = info.tooltipArg1
+	button.tooltipArg2 = info.tooltipArg2
+	button.tooltipArg3 = info.tooltipArg3
+	button.tooltipArg4 = info.tooltipArg4
+	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
+		self:assert(type(button.arg1) == "table", "Cannot call method " .. button.func .. " on a non-table")
+		self:assert(type(button.arg1[button.func]) == "function", "Method " .. button.func .. " nonexistant.")
+	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
+		self:error("Cannot retrieve AceOptions tables from a non-object argument #2")
+	end
+	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
+	return options
+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()
+			oldX,oldY = GetCursorPosition()
+			clickTime = GetTime()
+			if WorldFrame_OnMouseDown then
+				WorldFrame_OnMouseDown()
+			end
+		end)
+
+		WorldFrame:SetScript("OnMouseUp", function()
+			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()
+				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()
+			end
+		end)
+
+		if hooksecurefunc then
+			hooksecurefunc(DropDownList1, "Show", function()
+				if levels[1] and levels[1]:IsVisible() then
+					self:Close()
+				end
+			end)
+		else
+			local DropDownList1_Show = DropDownList1.Show
+			function DropDownList1.Show(DropDownList1)
+				if levels[1] and levels[1]:IsVisible() then
+					self:Close()
+				end
+				DropDownList1_Show(DropDownList1)
+			end
+		end
+
+		if hooksecurefunc then
+			hooksecurefunc("HideDropDownMenu", function()
+				if levels[1] and levels[1]:IsVisible() then
+					self:Close()
+				end
+			end)
+		else
+			local old_HideDropDownMenu = HideDropDownMenu
+			function HideDropDownMenu(num)
+				if levels[1] and levels[1]:IsVisible() then
+					self:Close()
+				end
+				old_HideDropDownMenu(num)
+			end
+		end
+
+		if hooksecurefunc then
+			hooksecurefunc("CloseDropDownMenus", function()
+				if levels[1] and levels[1]:IsVisible() then
+					self:Close()
+				end
+			end)
+		else
+			local old_CloseDropDownMenus = CloseDropDownMenus
+			function CloseDropDownMenus(num)
+				if levels[1] and levels[1]:IsVisible() then
+					self:Close()
+				end
+				old_CloseDropDownMenus(num)
+			end
+		end
+	end
+	levels = {}
+	buttons = {}
+
+	if oldDeactivate then
+		oldDeactivate(oldLib)
+	end
+end
+
+AceLibrary:Register(Dewdrop, MAJOR_VERSION, MINOR_VERSION, activate)