Thread Tools Display Modes
12-22-18, 02:45 PM   #1
Brellison94
An Aku'mai Servant
AddOn Author - Click to view addons
Join Date: Mar 2013
Posts: 36
Memory-economic mode removal with this message

I'm testing something to get this fixed and I get this lua error. Any idea to work around this?

Lua Code:
  1. Message: Interface\AddOns\BossTalk\Core.lua:975: Dewdrop-2.0: args.bosses.args.1: "desc" must be a string
  2. Time: Sat Dec 22 14:43:28 2018
  3. Count: 5
  4. Stack: Interface\AddOns\BossTalk\Core.lua:975: Dewdrop-2.0: args.bosses.args.1: "desc" must be a string
  5. [C]: in function `error'
  6. ...rface\AddOns\BossTalk\Libs\AceLibrary\AceLibrary.lua:122: in function `error'
  7. ...ace\AddOns\BossTalk\Libs\Dewdrop-2.0\Dewdrop-2.0.lua:1309: in function `FeedAceOptionsTable'
  8. Interface\AddOns\BossTalk\Core.lua:975: in function <Interface\AddOns\BossTalk\Core.lua:974>
  9. ...ace\AddOns\BossTalk\Libs\Dewdrop-2.0\Dewdrop-2.0.lua:1903: in function <...ace\AddOns\BossTalk\Libs\Dewdrop-2.0\Dewdrop-2.0.lua:1852>
  10. ...ace\AddOns\BossTalk\Libs\Dewdrop-2.0\Dewdrop-2.0.lua:2788: in function <...ace\AddOns\BossTalk\Libs\Dewdrop-2.0\Dewdrop-2.0.lua:2747>
  11. ...ace\AddOns\BossTalk\Libs\Dewdrop-2.0\Dewdrop-2.0.lua:3005: in function `Open'
  12. Interface\AddOns\BossTalk\Core.lua:977: in function `OnFuBarClick'
  13. Interface\AddOns\BossTalk\Core.lua:910: in function `OnClick'
  14. ...ace\AddOns\Grid\Libs\LibDBIcon-1.0\LibDBIcon-1.0.lua:144: in function <...ace\AddOns\Grid\Libs\LibDBIcon-1.0\LibDBIcon-1.0.lua:142>
  15.  
  16. Locals: (*temporary) = "Dewdrop-2.0: args.bosses.args.1: "desc" must be a string"
  Reply With Quote
12-22-18, 03:02 PM   #2
Fizzlemizz
I did that?
 
Fizzlemizz's Avatar
Premium Member
AddOn Author - Click to view addons
Join Date: Dec 2011
Posts: 1,871
Is it meant to be:
Code:
args.bosses.args[1]
It's a bit hard to tell without any code to associate the error with.
__________________
Fizzlemizz
Maintainer of Discord Unit Frames and Discord Art.
Author of FauxMazzle, FauxMazzleHUD and Move Pad Plus.
  Reply With Quote
12-22-18, 04:09 PM   #3
Brellison94
An Aku'mai Servant
AddOn Author - Click to view addons
Join Date: Mar 2013
Posts: 36
Originally Posted by Fizzlemizz View Post
Is it meant to be:
Code:
args.bosses.args[1]
It's a bit hard to tell without any code to associate the error with.
Here's where its at in the Options file.

Lua Code:
  1. bosses = {
  2. type = "group",
  3. name = L["Bossquotes"],
  4. desc = L["A list of bossquotes."],
  5. args = {{ type = 'execute',
  6. name = L["Generate menu!"],
  7. func = function() BossTalk:GenerateMenu() end,
  8. order = 7,
  9. icon = "Interface\\Icons\\Spell_ChargePositive",
  10. }},
  11. order = 8,
  12. },
  Reply With Quote
12-22-18, 04:33 PM   #4
Fizzlemizz
I did that?
 
Fizzlemizz's Avatar
Premium Member
AddOn Author - Click to view addons
Join Date: Dec 2011
Posts: 1,871
Once again, not a lot to go on.

It would appear that the table is being passed to DewDrop which is returning the '"desc" must be a string' message.

Possibly try putting a print statement in the DewDrop lua to find out what "type" is being passed for desc. and maybe some information from other keys to make sure it's using the table you think it is.
__________________
Fizzlemizz
Maintainer of Discord Unit Frames and Discord Art.
Author of FauxMazzle, FauxMazzleHUD and Move Pad Plus.
  Reply With Quote
12-22-18, 05:40 PM   #5
Brellison94
An Aku'mai Servant
AddOn Author - Click to view addons
Join Date: Mar 2013
Posts: 36
Originally Posted by Fizzlemizz View Post
Once again, not a lot to go on.

It would appear that the table is being passed to DewDrop which is returning the '"desc" must be a string' message.

Possibly try putting a print statement in the DewDrop lua to find out what "type" is being passed for desc. and maybe some information from other keys to make sure it's using the table you think it is.
This lib hasn't been updated since 3-7 years ago. Had this problem various times each expansion launch. I'm seeing something but not what I expect. Here's the DewDrop file to present the situation.

Lua Code:
  1. local MAJOR_VERSION = "Dewdrop-2.0"
  2. local MINOR_VERSION = tonumber(strmatch("$Revision: 326 $", "%d+")) + 90000
  3.  
  4. if not AceLibrary then error(MAJOR_VERSION .. " requires AceLibrary") end
  5. if not AceLibrary:IsNewVersion(MAJOR_VERSION, MINOR_VERSION) then return end
  6.  
  7. local Dewdrop = {}
  8.  
  9. local SharedMedia
  10.  
  11. local CLOSE = "Close"
  12. local CLOSE_DESC = "Close the menu."
  13. local VALIDATION_ERROR = "Validation error."
  14. local USAGE_TOOLTIP = "Usage: %s."
  15. local RANGE_TOOLTIP = "Note that you can scroll your mouse wheel while over the slider to step by one."
  16. local RESET_KEYBINDING_DESC = "Hit escape to clear the keybinding."
  17. local KEY_BUTTON1 = "Left Mouse"
  18. local KEY_BUTTON2 = "Right Mouse"
  19. local DISABLED = "Disabled"
  20. local DEFAULT_CONFIRM_MESSAGE = "Are you sure you want to perform `%s'?"
  21.  
  22. if GetLocale() == "deDE" then
  23.     CLOSE = "Schlie\195\159en"
  24.     CLOSE_DESC = "Men\195\188 schlie\195\159en."
  25.     VALIDATION_ERROR = "Validierungsfehler."
  26.     USAGE_TOOLTIP = "Benutzung: %s."
  27.     RANGE_TOOLTIP = "Beachte das du mit dem Mausrad scrollen kannst solange du mit dem Mauszeiger \195\188ber dem Schieberegler bist, um feinere Spr\195\188nge zu machen."
  28.     RESET_KEYBINDING_DESC = "Escape dr\195\188cken, um die Tastenbelegung zu l\195\182schen."
  29.     KEY_BUTTON1 = "Linke Maustaste"
  30.     KEY_BUTTON2 = "Rechte Maustaste"
  31.     DISABLED = "Deaktiviert"
  32.     DEFAULT_CONFIRM_MESSAGE = "Bist du sicher das du `%s' machen willst?"
  33. elseif GetLocale() == "koKR" then
  34.     CLOSE = "닫기"
  35.     CLOSE_DESC = "메뉴를 닫습니다."
  36.     VALIDATION_ERROR = "오류 확인."
  37.     USAGE_TOOLTIP = "사용법: %s."
  38.     RANGE_TOOLTIP = "알림 : 슬라이더 위에서 마우스 휠을 사용하면 한단계씩 조절할 수 있습니다."
  39.     RESET_KEYBINDING_DESC = "단축키를 해제하려면 ESC키를 누르세요."
  40.     KEY_BUTTON1 = "왼쪽 마우스"
  41.     KEY_BUTTON2 = "오른쪽 마우스"
  42.     DISABLED = "비활성화됨"
  43.     DEFAULT_CONFIRM_MESSAGE = "정말로 `%s' 실행을 하시겠습니까 ?"
  44. elseif GetLocale() == "frFR" then
  45.     CLOSE = "Fermer"
  46.     CLOSE_DESC = "Ferme le menu."
  47.     VALIDATION_ERROR = "Erreur de validation."
  48.     USAGE_TOOLTIP = "Utilisation : %s."
  49.     RANGE_TOOLTIP = "Vous pouvez aussi utiliser la molette de la souris pour pour modifier progressivement."
  50.     RESET_KEYBINDING_DESC = "Appuyez sur la touche Echappement pour effacer le raccourci."
  51.     KEY_BUTTON1 = "Clic gauche"
  52.     KEY_BUTTON2 = "Clic droit"
  53.     DISABLED = "D\195\169sactiv\195\169"
  54.     DEFAULT_CONFIRM_MESSAGE = "\195\138tes-vous s\195\187r de vouloir effectuer '%s' ?"
  55. elseif GetLocale() == "esES" then
  56.     CLOSE = "Cerrar"
  57.     CLOSE_DESC = "Cierra el menú."
  58.     VALIDATION_ERROR = "Error de validación."
  59.     USAGE_TOOLTIP = "Uso: %s."
  60.     RANGE_TOOLTIP = "Puedes desplazarte verticalmente con la rueda del ratón sobre el desplazador."
  61.     RESET_KEYBINDING_DESC = "Pulsa Escape para borrar la asignación de tecla."
  62.     KEY_BUTTON1 = "Clic Izquierdo"
  63.     KEY_BUTTON2 = "Clic Derecho"
  64.     DISABLED = "Desactivado"
  65.     DEFAULT_CONFIRM_MESSAGE = "¿Estás seguro de querer realizar `%s'?"
  66. elseif GetLocale() == "zhTW" then
  67.     CLOSE = "關閉"
  68.     CLOSE_DESC = "關閉選單。"
  69.     VALIDATION_ERROR = "驗證錯誤。"
  70.     USAGE_TOOLTIP = "用法: %s。"
  71.     RANGE_TOOLTIP = "你可以在捲動條上使用滑鼠滾輪來捲動。"
  72.     RESET_KEYBINDING_DESC = "按Esc鍵清除快捷鍵。"
  73.     KEY_BUTTON1 = "滑鼠左鍵"
  74.     KEY_BUTTON2 = "滑鼠右鍵"
  75.     DISABLED = "停用"
  76.     DEFAULT_CONFIRM_MESSAGE = "是否執行「%s」?"
  77. elseif GetLocale() == "zhCN" then
  78.     CLOSE = "关闭"
  79.     CLOSE_DESC = "关闭菜单"
  80.     VALIDATION_ERROR = "验证错误."
  81.     USAGE_TOOLTIP = "用法: %s."
  82.     RANGE_TOOLTIP = "你可以在滚动条上使用鼠标滚轮来翻页."
  83.     RESET_KEYBINDING_DESC = "按ESC键清除按键绑定"
  84.     KEY_BUTTON1 = "鼠标左键"
  85.     KEY_BUTTON2 = "鼠标右键"
  86.     DISABLED = "禁用"
  87.     DEFAULT_CONFIRM_MESSAGE = "是否执行'%s'?"
  88. elseif GetLocale() == "ruRU" then
  89.     CLOSE = "Закрыть"
  90.     CLOSE_DESC = "Закрыть меню."
  91.     VALIDATION_ERROR = "Ошибка проверки данных."
  92.     USAGE_TOOLTIP = "Используйте: %s."
  93.     RANGE_TOOLTIP = "Используйте колесо мыши для прокрутки ползунка."
  94.     RESET_KEYBINDING_DESC = "Нажмите клавишу Escape для очистки клавиши."
  95.     KEY_BUTTON1 = "ЛКМ"
  96.     KEY_BUTTON2 = "ПКМ"
  97.     DISABLED = "Отключено"
  98.     DEFAULT_CONFIRM_MESSAGE = "Вы уверены что вы хотите выполнять `%s'?"
  99. end
  100.  
  101. Dewdrop.KEY_BUTTON1 = KEY_BUTTON1
  102. Dewdrop.KEY_BUTTON2 = KEY_BUTTON2
  103.  
  104. local function new(...)
  105.     local t = {}
  106.     for i = 1, select('#', ...), 2 do
  107.         local k = select(i, ...)
  108.         if k then
  109.             t[k] = select(i+1, ...)
  110.         else
  111.             break
  112.         end
  113.     end
  114.     return t
  115. end
  116.  
  117. local tmp
  118. do
  119.     local t = {}
  120.     function tmp(...)
  121.         for k in pairs(t) do
  122.             t[k] = nil
  123.         end
  124.         for i = 1, select('#', ...), 2 do
  125.             local k = select(i, ...)
  126.             if k then
  127.                 t[k] = select(i+1, ...)
  128.             else
  129.                 break
  130.             end
  131.         end
  132.         return t
  133.     end
  134. end
  135. local tmp2
  136. do
  137.     local t = {}
  138.     function tmp2(...)
  139.         for k in pairs(t) do
  140.             t[k] = nil
  141.         end
  142.         for i = 1, select('#', ...), 2 do
  143.             local k = select(i, ...)
  144.             if k then
  145.                 t[k] = select(i+1, ...)
  146.             else
  147.                 break
  148.             end
  149.         end
  150.         return t
  151.     end
  152. end
  153. local levels
  154. local buttons
  155.  
  156.  
  157. -- Secure frame handling:
  158. -- Rather than using secure buttons in the menu (has problems), we have one
  159. -- master secureframe that we pop onto menu items on mouseover. This requires
  160. -- some dark magic with OnLeave etc, but it's not too bad.
  161.  
  162. local eventFrame = CreateFrame("Button")
  163. local secureFrame
  164. local createSecureFrame
  165.  
  166. local function secureFrame_Show(self)
  167.   local owner = self.owner
  168.  
  169.   if self.secure then   -- Leftovers from previos owner, clean up! ("Shouldn't" happen but does..)
  170.       for k,v in pairs(self.secure) do
  171.         self:SetAttribute(k, nil)
  172.       end
  173.   end
  174.   self.secure = owner.secure;   -- Grab hold of new secure data
  175.  
  176.   local scale = owner:GetEffectiveScale()
  177.  
  178.   self:SetPoint("TOPLEFT", nil, "BOTTOMLEFT", owner:GetLeft() * scale, owner:GetTop() * scale)
  179.   self:SetPoint("BOTTOMRIGHT", nil, "BOTTOMLEFT", owner:GetRight() * scale, owner:GetBottom() * scale)
  180.   self:EnableMouse(true)
  181.   for k,v in pairs(self.secure) do
  182.     self:SetAttribute(k, v)
  183.   end
  184.  
  185.     secureFrame:SetFrameStrata(owner:GetFrameStrata())
  186.     secureFrame:SetFrameLevel(owner:GetFrameLevel()+1)
  187.  
  188.   self:Show()
  189. end
  190.  
  191. local function secureFrame_Hide(self)
  192.   self:Hide()
  193.   if self.secure then
  194.       for k,v in pairs(self.secure) do
  195.         self:SetAttribute(k, nil)
  196.       end
  197.     end
  198.   self.secure = nil
  199. end
  200.  
  201. eventFrame:SetScript("OnEvent",
  202.     function(this, event)
  203.         if event=="PLAYER_REGEN_ENABLED" then
  204.             createSecureFrame()
  205.             secureFrame.combat = false
  206.             if not secureFrame:IsShown() and secureFrame.owner then
  207.                 secureFrame_Show(secureFrame)
  208.             end
  209.         elseif event=="PLAYER_REGEN_DISABLED" and secureFrame then
  210.             secureFrame.combat = true
  211.             if secureFrame:IsShown() then
  212.                 secureFrame_Hide(secureFrame)
  213.             end
  214.         end
  215.     end
  216. )
  217. eventFrame:RegisterEvent("PLAYER_REGEN_ENABLED")
  218. eventFrame:RegisterEvent("PLAYER_REGEN_DISABLED")
  219.  
  220. function createSecureFrame()
  221.   if secureFrame or InCombatLockdown() then return end
  222.   secureFrame = CreateFrame("Button", nil, nil, "SecureActionButtonTemplate")
  223.   secureFrame:Hide()
  224.  
  225. secureFrame:SetScript("OnLeave",
  226.     function(this)
  227.         local owner=this.owner
  228.         this:Deactivate()
  229.         owner:GetScript("OnLeave")(owner)
  230.     end
  231. )
  232.  
  233. secureFrame:HookScript("OnClick",
  234.     function(this)
  235.         local realthis = this
  236.         this = this.owner
  237.         this:GetScript("OnClick")(this)
  238.     end
  239. )
  240.  
  241. function secureFrame:IsOwnedBy(frame)
  242.     return self.owner == frame
  243. end
  244.  
  245. function secureFrame:Activate(owner)
  246.     if self.owner then      -- "Shouldn't" happen but apparently it does and I cba to troubleshoot...
  247.         if not self.combat then
  248.             secureFrame_Hide(self)
  249.         end
  250.     end
  251.     self.owner = owner
  252.     if not self.combat then
  253.         secureFrame_Show(self)
  254.     end
  255. end
  256.  
  257. function secureFrame:Deactivate()
  258.     if not self.combat then
  259.         secureFrame_Hide(self)
  260.     end
  261.     self.owner = nil
  262. end
  263.  
  264. end
  265. createSecureFrame()
  266. -- END secure frame utilities
  267.  
  268.  
  269. -- Underline on mouseover - use a single global underline that we move around, no point in creating lots of copies
  270. local underlineFrame = CreateFrame("Frame", nil)
  271. underlineFrame.tx = underlineFrame:CreateTexture()
  272. underlineFrame.tx:SetTexture(1,1,0.5,0.75)
  273. underlineFrame:SetScript("OnHide", function(this) this:Hide(); end)
  274. underlineFrame:SetScript("OnShow", function(this)   -- change sizing on the fly to catch runtime uiscale changes
  275.     underlineFrame.tx:SetPoint("TOPLEFT", -1, -2/this:GetEffectiveScale())
  276.     underlineFrame.tx:SetPoint("RIGHT", 1,0)
  277.     underlineFrame.tx:SetHeight(0.6 / this:GetEffectiveScale());
  278. end)
  279. underlineFrame:SetHeight(1)
  280.  
  281. -- END underline on mouseover
  282.  
  283.  
  284. local function GetScaledCursorPosition()
  285.     local x, y = GetCursorPosition()
  286.     local scale = UIParent:GetEffectiveScale()
  287.     return x / scale, y / scale
  288. end
  289.  
  290. local function StartCounting(self, level)
  291.     for i = level, 1, -1 do
  292.         if levels[i] then
  293.             levels[i].count = 3
  294.         end
  295.     end
  296. end
  297.  
  298. local function StopCounting(self, level)
  299.     for i = level, 1, -1 do
  300.         if levels[i] then
  301.             levels[i].count = nil
  302.         end
  303.     end
  304. end
  305.  
  306. local function OnUpdate(self, elapsed)
  307.     for _,level in ipairs(levels) do
  308.         local count = level.count
  309.         if count then
  310.             count = count - elapsed
  311.             if count < 0 then
  312.                 level.count = nil
  313.                 self:Close(level.num)
  314.             else
  315.                 level.count = count
  316.             end
  317.         end
  318.     end
  319. end
  320.  
  321. local function CheckDualMonitor(self, frame)
  322.     local ratio = GetScreenWidth() / GetScreenHeight()
  323.     if ratio >= 2.4 and frame:GetRight() > GetScreenWidth() / 2 and frame:GetLeft() < GetScreenWidth() / 2 then
  324.         local offsetx
  325.         if GetCursorPosition() / GetScreenHeight() * 768 < GetScreenWidth() / 2 then
  326.             offsetx = GetScreenWidth() / 2 - frame:GetRight()
  327.         else
  328.             offsetx = GetScreenWidth() / 2 - frame:GetLeft()
  329.         end
  330.         local point, parent, relativePoint, x, y = frame:GetPoint(1)
  331.         frame:SetPoint(point, parent, relativePoint, (x or 0) + offsetx, y or 0)
  332.     end
  333. end
  334.  
  335. local function CheckSize(self, level)
  336.     if not level.buttons then
  337.         return
  338.     end
  339.     local height = 20
  340.     for _, button in ipairs(level.buttons) do
  341.         height = height + button:GetHeight()
  342.     end
  343.     level:SetHeight(height)
  344.     local width = 160
  345.     for _, button in ipairs(level.buttons) do
  346.         local extra = 1
  347.         if button.hasArrow or button.hasColorSwatch then
  348.             extra = extra + 16
  349.         end
  350.         if not button.notCheckable then
  351.             extra = extra + 24
  352.         end
  353.         button.text:SetFont(STANDARD_TEXT_FONT, button.textHeight)
  354.         if button.text:GetStringWidth() + extra > width then
  355.             width = button.text:GetStringWidth() + extra
  356.         end
  357.     end
  358.     level:SetWidth(width + 20)
  359.     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
  360.         level:ClearAllPoints()
  361.         local parent = level.parent or level:GetParent()
  362.         if type(parent) ~= "table" then
  363.             parent = UIParent
  364.         end
  365.         if level.lastDirection == "RIGHT" then
  366.             if level.lastVDirection == "DOWN" then
  367.                 level:SetPoint("TOPLEFT", parent, "TOPRIGHT", 5, 10)
  368.             else
  369.                 level:SetPoint("BOTTOMLEFT", parent, "BOTTOMRIGHT", 5, -10)
  370.             end
  371.         else
  372.             if level.lastVDirection == "DOWN" then
  373.                 level:SetPoint("TOPRIGHT", parent, "TOPLEFT", -5, 10)
  374.             else
  375.                 level:SetPoint("BOTTOMRIGHT", parent, "BOTTOMLEFT", -5, -10)
  376.             end
  377.         end
  378.     end
  379.     local dirty = false
  380.     if not level:GetRight() then
  381.         self:Close()
  382.         return
  383.     end
  384.     if level:GetRight() > GetScreenWidth() and level.lastDirection == "RIGHT" then
  385.         level.lastDirection = "LEFT"
  386.         dirty = true
  387.     elseif level:GetLeft() < 0 and level.lastDirection == "LEFT" then
  388.         level.lastDirection = "RIGHT"
  389.         dirty = true
  390.     end
  391.     if level:GetTop() > GetScreenHeight() and level.lastVDirection == "UP" then
  392.         level.lastVDirection = "DOWN"
  393.         dirty = true
  394.     elseif level:GetBottom() < 0 and level.lastVDirection == "DOWN" then
  395.         level.lastVDirection = "UP"
  396.         dirty = true
  397.     end
  398.     if dirty then
  399.         level:ClearAllPoints()
  400.         local parent = level.parent or level:GetParent()
  401.         if type(parent) ~= "table" then
  402.             parent = UIParent
  403.         end
  404.         if level.lastDirection == "RIGHT" then
  405.             if level.lastVDirection == "DOWN" then
  406.                 level:SetPoint("TOPLEFT", parent, "TOPRIGHT", 5, 10)
  407.             else
  408.                 level:SetPoint("BOTTOMLEFT", parent, "BOTTOMRIGHT", 5, -10)
  409.             end
  410.         else
  411.             if level.lastVDirection == "DOWN" then
  412.                 level:SetPoint("TOPRIGHT", parent, "TOPLEFT", -5, 10)
  413.             else
  414.                 level:SetPoint("BOTTOMRIGHT", parent, "BOTTOMLEFT", -5, -10)
  415.             end
  416.         end
  417.     end
  418.     if level:GetTop() > GetScreenHeight() then
  419.         local top = level:GetTop()
  420.         local point, parent, relativePoint, x, y = level:GetPoint(1)
  421.         level:ClearAllPoints()
  422.         level:SetPoint(point, parent, relativePoint, x or 0, (y or 0) + GetScreenHeight() - top)
  423.     elseif level:GetBottom() < 0 then
  424.         local bottom = level:GetBottom()
  425.         local point, parent, relativePoint, x, y = level:GetPoint(1)
  426.         level:ClearAllPoints()
  427.         level:SetPoint(point, parent, relativePoint, x or 0, (y or 0) - bottom)
  428.     end
  429.     CheckDualMonitor(self, level)
  430.     if mod(level.num, 5) == 0 then
  431.         local left, bottom = level:GetLeft(), level:GetBottom()
  432.         level:ClearAllPoints()
  433.         level:SetPoint("BOTTOMLEFT", UIParent, "BOTTOMLEFT", left, bottom)
  434.     end
  435. end
  436.  
  437. local Open
  438. local OpenSlider
  439. local OpenEditBox
  440. local Refresh
  441. local Clear
  442. local function ReleaseButton(self, level, index)
  443.     if not level.buttons then
  444.         return
  445.     end
  446.     if not level.buttons[index] then
  447.         return
  448.     end
  449.     local button = level.buttons[index]
  450.     button:Hide()
  451.     if button.highlight then
  452.         button.highlight:Hide()
  453.     end
  454. --  button.arrow:SetVertexColor(1, 1, 1)
  455. --  button.arrow:SetHeight(16)
  456. --  button.arrow:SetWidth(16)
  457.     table.remove(level.buttons, index)
  458.     table.insert(buttons, button)
  459.     for k in pairs(button) do
  460.         if k ~= 0 and k ~= "text" and k ~= "check" and k ~= "arrow" and k ~= "colorSwatch" and k ~= "highlight" and k ~= "radioHighlight" then
  461.             button[k] = nil
  462.         end
  463.     end
  464.     return true
  465. end
  466.  
  467. local function Scroll(self, level, down)
  468.     if down then
  469.         if level:GetBottom() < 0 then
  470.             local point, parent, relativePoint, x, y = level:GetPoint(1)
  471.             level:SetPoint(point, parent, relativePoint, x, y + 50)
  472.             if level:GetBottom() > 0 then
  473.                 level:SetPoint(point, parent, relativePoint, x, y + 50 - level:GetBottom())
  474.             end
  475.         end
  476.     else
  477.         if level:GetTop() > GetScreenHeight() then
  478.             local point, parent, relativePoint, x, y = level:GetPoint(1)
  479.             level:SetPoint(point, parent, relativePoint, x, y - 50)
  480.             if level:GetTop() < GetScreenHeight() then
  481.                 level:SetPoint(point, parent, relativePoint, x, y - 50 + GetScreenHeight() - level:GetTop())
  482.             end
  483.         end
  484.     end
  485. end
  486.  
  487. local function getArgs(t, str, num, ...)
  488.     local x = t[str .. num]
  489.     if x == nil then
  490.         return ...
  491.     else
  492.         return x, getArgs(t, str, num + 1, ...)
  493.     end
  494. end
  495.  
  496. local sliderFrame
  497. local editBoxFrame
  498.  
  499. local normalFont
  500. local lastSetFont
  501. local justSetFont = false
  502. local regionTmp = {}
  503. local function fillRegionTmp(...)
  504.     for i = 1, select('#', ...) do
  505.         regionTmp[i] = select(i, ...)
  506.     end
  507. end
  508.  
  509. local function showGameTooltip(this)
  510.     if this.tooltipTitle or this.tooltipText then
  511.         GameTooltip_SetDefaultAnchor(GameTooltip, this)
  512.         local disabled = not this.isTitle and this.disabled
  513.         local font
  514.         if this.tooltipTitle then
  515.             if SharedMedia and SharedMedia:IsValid("font", this.tooltipTitle) then
  516.                 font = SharedMedia:Fetch("font", this.tooltipTitle)
  517.             end
  518.             if disabled then
  519.                 GameTooltip:SetText(this.tooltipTitle, 0.5, 0.5, 0.5, 1)
  520.             else
  521.                 GameTooltip:SetText(this.tooltipTitle, 1, 1, 1, 1)
  522.             end
  523.             if this.tooltipText then
  524.                 if not font and SharedMedia and SharedMedia:IsValid("font", this.tooltipText) then
  525.                     font = SharedMedia:Fetch("font", this.tooltipText)
  526.                 end
  527.                 if disabled then
  528.                     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)
  529.                 else
  530.                     GameTooltip:AddLine(this.tooltipText, NORMAL_FONT_COLOR.r, NORMAL_FONT_COLOR.g, NORMAL_FONT_COLOR.b, 1)
  531.                 end
  532.             end
  533.         else
  534.             if SharedMedia and SharedMedia:IsValid("font", this.tooltipText) then
  535.                 font = SharedMedia:Fetch("font", this.tooltipText)
  536.             end
  537.             if disabled then
  538.                 GameTooltip:SetText(this.tooltipText, 0.5, 0.5, 0.5, 1)
  539.             else
  540.                 GameTooltip:SetText(this.tooltipText, 1, 1, 1, 1)
  541.             end
  542.         end
  543.         if font then
  544.             fillRegionTmp(GameTooltip:GetRegions())
  545.             lastSetFont = font
  546.             justSetFont = true
  547.             for i,v in ipairs(regionTmp) do
  548.                 if v.SetFont then
  549.                     local norm,size,outline = v:GetFont()
  550.                     v:SetFont(font, size, outline)
  551.                     if not normalFont then
  552.                         normalFont = norm
  553.                     end
  554.                 end
  555.                 regionTmp[i] = nil
  556.             end
  557.         elseif not normalFont then
  558.             fillRegionTmp(GameTooltip:GetRegions())
  559.             for i,v in ipairs(regionTmp) do
  560.                 if v.GetFont and not normalFont then
  561.                     normalFont = v:GetFont()
  562.                 end
  563.                 regionTmp[i] = nil
  564.             end
  565.         end
  566.         GameTooltip:Show()
  567.     end
  568.     if this.tooltipFunc then
  569.         GameTooltip:SetOwner(this, "ANCHOR_NONE")
  570.         GameTooltip:SetPoint("TOPLEFT", this, "TOPRIGHT", 5, 0)
  571.         this.tooltipFunc(getArgs(this, 'tooltipArg', 1))
  572.         GameTooltip:Show()
  573.     end
  574. end
  575.  
  576. local tmpt = setmetatable({}, {mode='v'})
  577. local numButtons = 0
  578. local function AcquireButton(self, level)
  579.     if not levels[level] then
  580.         return
  581.     end
  582.     level = levels[level]
  583.     if not level.buttons then
  584.         level.buttons = {}
  585.     end
  586.     local button
  587.     if #buttons == 0 then
  588.         numButtons = numButtons + 1
  589.         button = CreateFrame("Button", "Dewdrop20Button" .. numButtons, nil)
  590.         button:SetFrameStrata("FULLSCREEN_DIALOG")
  591.         button:SetHeight(16)
  592.         local highlight = button:CreateTexture(nil, "BACKGROUND")
  593.         highlight:SetTexture("Interface\\QuestFrame\\UI-QuestTitleHighlight")
  594.         button.highlight = highlight
  595.         highlight:SetBlendMode("ADD")
  596.         highlight:SetAllPoints(button)
  597.         highlight:Hide()
  598.         local check = button:CreateTexture(nil, "ARTWORK")
  599.         button.check = check
  600.         check:SetTexture("Interface\\Buttons\\UI-CheckBox-Check")
  601.         check:SetPoint("CENTER", button, "LEFT", 12, 0)
  602.         check:SetWidth(24)
  603.         check:SetHeight(24)
  604.         local radioHighlight = button:CreateTexture(nil, "ARTWORK")
  605.         button.radioHighlight = radioHighlight
  606.         radioHighlight:SetTexture("Interface\\Buttons\\UI-RadioButton")
  607.         radioHighlight:SetAllPoints(check)
  608.         radioHighlight:SetBlendMode("ADD")
  609.         radioHighlight:SetTexCoord(0.5, 0.75, 0, 1)
  610.         radioHighlight:Hide()
  611.         button:SetScript("OnEnter", function(this)
  612.             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
  613.                 for i = 1, this.level.num do
  614.                     Refresh(self, levels[i])
  615.                 end
  616.                 return
  617.             end
  618.             self:Close(this.level.num + 1)
  619.             if not this.disabled then
  620.                 if this.secure then
  621.                     secureFrame:Activate(this)
  622.                 elseif this.hasSlider then
  623.                     OpenSlider(self, this)
  624.                 elseif this.hasEditBox then
  625.                     OpenEditBox(self, this)
  626.                 elseif this.hasArrow then
  627.                     Open(self, this, nil, this.level.num + 1, this.value)
  628.                 end
  629.             end
  630.             if not this.level then -- button reclaimed
  631.                 return
  632.             end
  633.             StopCounting(self, this.level.num + 1)
  634.             if not this.disabled then
  635.                 highlight:Show()
  636.                 if this.isRadio then
  637.                     button.radioHighlight:Show()
  638.                 end
  639.                 if this.mouseoverUnderline then
  640.                     underlineFrame:SetParent(this)
  641.                     underlineFrame:SetPoint("BOTTOMLEFT",this.text,0,0)
  642.                     underlineFrame:SetWidth(this.text:GetWidth())
  643.                     underlineFrame:Show()
  644.                 end
  645.             end
  646.             showGameTooltip(this)
  647.         end)
  648.         button:SetScript("OnHide", function(this)
  649.             if this.secure and secureFrame:IsOwnedBy(this) then
  650.                 secureFrame:Deactivate()
  651.             end
  652.         end)
  653.         button:SetScript("OnLeave", function(this)
  654.             if this.secure and secureFrame:IsShown() then
  655.                 return; -- it's ok, we didn't actually mouse out of the button, only onto the secure frame on top of it
  656.             end
  657.             underlineFrame:Hide()
  658.             if not this.selected then
  659.                 highlight:Hide()
  660.             end
  661.             button.radioHighlight:Hide()
  662.             if this.level then
  663.                 StartCounting(self, this.level.num)
  664.             end
  665.             GameTooltip:Hide()
  666.         end)
  667.         local first = true
  668.         button:SetScript("OnClick", function(this)
  669.             if not this.disabled then
  670.                 if this.hasColorSwatch then
  671.                     local func = button.colorFunc
  672.                     local hasOpacity = this.hasOpacity
  673.                     local this = this
  674.                     for k in pairs(tmpt) do
  675.                         tmpt[k] = nil
  676.                     end
  677.                     for i = 1, 1000 do
  678.                         local x = this['colorArg'..i]
  679.                         if x == nil then
  680.                             break
  681.                         else
  682.                             tmpt[i] = x
  683.                         end
  684.                     end
  685.                     ColorPickerFrame.func = function()
  686.                         if func then
  687.                             local r,g,b = ColorPickerFrame:GetColorRGB()
  688.                             local a = hasOpacity and 1 - OpacitySliderFrame:GetValue() or nil
  689.                             local n = #tmpt
  690.                             tmpt[n+1] = r
  691.                             tmpt[n+2] = g
  692.                             tmpt[n+3] = b
  693.                             tmpt[n+4] = a
  694.                             func(unpack(tmpt))
  695.                             tmpt[n+1] = nil
  696.                             tmpt[n+2] = nil
  697.                             tmpt[n+3] = nil
  698.                             tmpt[n+4] = nil
  699.                         end
  700.                     end
  701.                     ColorPickerFrame.hasOpacity = this.hasOpacity
  702.                     ColorPickerFrame.opacityFunc = ColorPickerFrame.func
  703.                     ColorPickerFrame.opacity = 1 - this.opacity
  704.                     ColorPickerFrame:SetColorRGB(this.r, this.g, this.b)
  705.                     local r, g, b, a = this.r, this.g, this.b, this.opacity
  706.                     ColorPickerFrame.cancelFunc = function()
  707.                         if func then
  708.                             local n = #tmpt
  709.                             tmpt[n+1] = r
  710.                             tmpt[n+2] = g
  711.                             tmpt[n+3] = b
  712.                             tmpt[n+4] = a
  713.                             func(unpack(tmpt))
  714.                             for i = 1, n+4 do
  715.                                 tmpt[i] = nil
  716.                             end
  717.                         end
  718.                     end
  719.                     self:Close(1)
  720.                     ShowUIPanel(ColorPickerFrame)
  721.                 elseif this.func then
  722.                     local level = this.level
  723.                     if type(this.func) == "string" then
  724.                         if type(this.arg1[this.func]) ~= "function" then
  725.                             self:error("Cannot call method %q", this.func)
  726.                         end
  727.                         this.arg1[this.func](this.arg1, getArgs(this, 'arg', 2))
  728.                     else
  729.                         this.func(getArgs(this, 'arg', 1))
  730.                     end
  731.                     if this.closeWhenClicked then
  732.                         self:Close()
  733.                     elseif level:IsShown() then
  734.                         for i = 1, level.num do
  735.                             Refresh(self, levels[i])
  736.                         end
  737.                         local value = levels[level.num].value
  738.                         for i = level.num-1, 1, -1 do
  739.                             local level = levels[i]
  740.                             local good = false
  741.                             for _,button in ipairs(level.buttons) do
  742.                                 if button.value == value then
  743.                                     good = true
  744.                                     break
  745.                                 end
  746.                             end
  747.                             if not good then
  748.                                 Dewdrop:Close(i+1)
  749.                             end
  750.                             value = levels[i].value
  751.                         end
  752.                     end
  753.                 elseif this.closeWhenClicked then
  754.                     self:Close()
  755.                 end
  756.             end
  757.         end)
  758.         local text = button:CreateFontString(nil, "ARTWORK")
  759.         button.text = text
  760.         text:SetFontObject(GameFontHighlightSmall)
  761.         button.text:SetFont(STANDARD_TEXT_FONT, UIDROPDOWNMENU_DEFAULT_TEXT_HEIGHT)
  762.         button:SetScript("OnMouseDown", function(this)
  763.             if not this.disabled and (this.func or this.colorFunc or this.closeWhenClicked) then
  764.                 text:SetPoint("LEFT", button, "LEFT", this.notCheckable and 1 or 25, -1)
  765.             end
  766.         end)
  767.         button:SetScript("OnMouseUp", function(this)
  768.             if not this.disabled and (this.func or this.colorFunc or this.closeWhenClicked) then
  769.                 text:SetPoint("LEFT", button, "LEFT", this.notCheckable and 0 or 24, 0)
  770.             end
  771.         end)
  772.         local arrow = button:CreateTexture(nil, "ARTWORK")
  773.         button.arrow = arrow
  774.         arrow:SetPoint("LEFT", button, "RIGHT", -16, 0)
  775.         arrow:SetWidth(16)
  776.         arrow:SetHeight(16)
  777.         arrow:SetTexture("Interface\\ChatFrame\\ChatFrameExpandArrow")
  778.         local colorSwatch = button:CreateTexture(nil, "ARTWORK")
  779.         button.colorSwatch = colorSwatch
  780.         colorSwatch:SetWidth(20)
  781.         colorSwatch:SetHeight(20)
  782.         colorSwatch:SetTexture("Interface\\ChatFrame\\ChatFrameColorSwatch")
  783.         local texture = button:CreateTexture(nil, "OVERLAY")
  784.         colorSwatch.texture = texture
  785.         texture:SetTexture("Interface\\Buttons\\WHITE8X8")
  786.         texture:SetWidth(11.5)
  787.         texture:SetHeight(11.5)
  788.         texture:Show()
  789.         texture:SetPoint("CENTER", colorSwatch, "CENTER")
  790.         colorSwatch:SetPoint("RIGHT", button, "RIGHT", 0, 0)
  791.     else
  792.         button = table.remove(buttons)
  793.     end
  794.     button:ClearAllPoints()
  795.     button:SetParent(level)
  796.     button:SetFrameStrata(level:GetFrameStrata())
  797.     button:SetFrameLevel(level:GetFrameLevel() + 1)
  798.     button:SetPoint("LEFT", level, "LEFT", 10, 0)
  799.     button:SetPoint("RIGHT", level, "RIGHT", -10, 0)
  800.     if #level.buttons == 0 then
  801.         button:SetPoint("TOP", level, "TOP", 0, -10)
  802.     else
  803.         button:SetPoint("TOP", level.buttons[#level.buttons], "BOTTOM", 0, 0)
  804.     end
  805.     button.text:SetPoint("LEFT", button, "LEFT", 24, 0)
  806.     button:Show()
  807.     button.level = level
  808.     table.insert(level.buttons, button)
  809.     if not level.parented then
  810.         level.parented = true
  811.         level:ClearAllPoints()
  812.         if level.num == 1 then
  813.             if level.parent ~= UIParent and type(level.parent) == "table" then
  814.                 level:SetPoint("TOPRIGHT", level.parent, "TOPLEFT")
  815.             else
  816.                 level:SetPoint("CENTER", UIParent, "CENTER")
  817.             end
  818.         else
  819.             if level.lastDirection == "RIGHT" then
  820.                 if level.lastVDirection == "DOWN" then
  821.                     level:SetPoint("TOPLEFT", level.parent, "TOPRIGHT", 5, 10)
  822.                 else
  823.                     level:SetPoint("BOTTOMLEFT", level.parent, "BOTTOMRIGHT", 5, -10)
  824.                 end
  825.             else
  826.                 if level.lastVDirection == "DOWN" then
  827.                     level:SetPoint("TOPRIGHT", level.parent, "TOPLEFT", -5, 10)
  828.                 else
  829.                     level:SetPoint("BOTTOMRIGHT", level.parent, "BOTTOMLEFT", -5, -10)
  830.                 end
  831.             end
  832.         end
  833.         level:SetFrameStrata("FULLSCREEN_DIALOG")
  834.     end
  835.     button:SetAlpha(1)
  836.     return button
  837. end
  838.  
  839. local numLevels = 0
  840. local function AcquireLevel(self, level)
  841.     if not levels[level] then
  842.         for i = #levels + 1, level, -1 do
  843.             local i = i
  844.             numLevels = numLevels + 1
  845.             local frame = CreateFrame("Button", "Dewdrop20Level" .. numLevels, nil)
  846.             if i == 1 then
  847.                 local old_CloseSpecialWindows = CloseSpecialWindows
  848.                 function CloseSpecialWindows()
  849.                     local found = old_CloseSpecialWindows()
  850.                     if levels[1]:IsShown() then
  851.                         self:Close()
  852.                         return 1
  853.                     end
  854.                     return found
  855.                 end
  856.             end
  857.             levels[i] = frame
  858.             frame.num = i
  859.             frame:SetParent(UIParent)
  860.             frame:SetFrameStrata("FULLSCREEN_DIALOG")
  861.             frame:Hide()
  862.             frame:SetWidth(180)
  863.             frame:SetHeight(10)
  864.             frame:SetFrameLevel(i * 3)
  865.             frame:SetScript("OnHide", function()
  866.                 self:Close(level + 1)
  867.             end)
  868.             if frame.SetTopLevel then
  869.                 frame:SetTopLevel(true)
  870.             end
  871.             frame:EnableMouse(true)
  872.             frame:EnableMouseWheel(true)
  873.             local backdrop = CreateFrame("Frame", nil, frame)
  874.             backdrop:SetAllPoints(frame)
  875.             backdrop:SetBackdrop(tmp(
  876.                 'bgFile', "Interface\\Tooltips\\UI-Tooltip-Background",
  877.                 'edgeFile', "Interface\\Tooltips\\UI-Tooltip-Border",
  878.                 'tile', true,
  879.                 'insets', tmp2(
  880.                     'left', 5,
  881.                     'right', 5,
  882.                     'top', 5,
  883.                     'bottom', 5
  884.                 ),
  885.                 'tileSize', 16,
  886.                 'edgeSize', 16
  887.             ))
  888.             backdrop:SetBackdropBorderColor(TOOLTIP_DEFAULT_COLOR.r, TOOLTIP_DEFAULT_COLOR.g, TOOLTIP_DEFAULT_COLOR.b)
  889.             backdrop:SetBackdropColor(TOOLTIP_DEFAULT_BACKGROUND_COLOR.r, TOOLTIP_DEFAULT_BACKGROUND_COLOR.g, TOOLTIP_DEFAULT_BACKGROUND_COLOR.b)
  890.             frame:SetScript("OnClick", function()
  891.                 self:Close(i)
  892.             end)
  893.             frame:SetScript("OnEnter", function()
  894.                 StopCounting(self, i)
  895.             end)
  896.             frame:SetScript("OnLeave", function()
  897.                 StartCounting(self, i)
  898.             end)
  899.             frame:SetScript("OnMouseWheel", function(this, arg1)
  900.                 Scroll(self, frame, arg1 < 0)
  901.             end)
  902.             if i == 1 then
  903.                 frame:SetScript("OnUpdate", function(this, arg1)
  904.                     OnUpdate(self, arg1)
  905.                 end)
  906.                 levels[1].lastDirection = "RIGHT"
  907.                 levels[1].lastVDirection = "DOWN"
  908.             else
  909.                 levels[i].lastDirection = levels[i - 1].lastDirection
  910.                 levels[i].lastVDirection = levels[i - 1].lastVDirection
  911.             end
  912.         end
  913.     end
  914.     local fullscreenFrame = GetUIPanel("fullscreen")
  915.     local l = levels[level]
  916.     local strata, framelevel = l:GetFrameStrata(), l:GetFrameLevel()
  917.     if fullscreenFrame then
  918.         l:SetParent(fullscreenFrame)
  919.     else
  920.         l:SetParent(UIParent)
  921.     end
  922.     l:SetFrameStrata(strata)
  923.     l:SetFrameLevel(framelevel)
  924.     l:SetAlpha(1)
  925.     return l
  926. end
  927.  
  928. local function validateOptions(options, position, baseOptions, fromPass)
  929.     if not baseOptions then
  930.         baseOptions = options
  931.     end
  932.     if type(options) ~= "table" then
  933.         return "Options must be a table.", position
  934.     end
  935.     local kind = options.type
  936.     if type(kind) ~= "string" then
  937.         return '"type" must be a string.', position
  938.     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
  939.         return '"type" must either be "range", "text", "group", "toggle", "execute", "color", "dragLink", or "header".', position
  940.     end
  941.     if options.aliases then
  942.         if type(options.aliases) ~= "table" and type(options.aliases) ~= "string" then
  943.             return '"alias" must be a table or string', position
  944.         end
  945.     end
  946.     if not fromPass then
  947.         if kind == "execute" then
  948.             if type(options.func) ~= "string" and type(options.func) ~= "function" then
  949.                 return '"func" must be a string or function', position
  950.             end
  951.         elseif kind == "range" or kind == "text" or kind == "toggle" then
  952.             if type(options.set) ~= "string" and type(options.set) ~= "function" then
  953.                 return '"set" must be a string or function', position
  954.             end
  955.             if kind == "text" and options.get == false then
  956.             elseif type(options.get) ~= "string" and type(options.get) ~= "function" then
  957.                 return '"get" must be a string or function', position
  958.             end
  959.         elseif kind == "group" and options.pass then
  960.             if options.pass ~= true then
  961.                 return '"pass" must be either nil, true, or false', position
  962.             end
  963.             if not options.func then
  964.                 if type(options.set) ~= "string" and type(options.set) ~= "function" then
  965.                     return '"set" must be a string or function', position
  966.                 end
  967.                 if type(options.get) ~= "string" and type(options.get) ~= "function" then
  968.                     return '"get" must be a string or function', position
  969.                 end
  970.             elseif type(options.func) ~= "string" and type(options.func) ~= "function" then
  971.                 return '"func" must be a string or function', position
  972.             end
  973.         end
  974.     end
  975.     if options ~= baseOptions then
  976.         if kind == "header" then
  977.         elseif type(options.desc) ~= "string" then
  978.             return '"desc" must be a string', position
  979.         elseif options.desc:len() == 0 then
  980.             return '"desc" cannot be a 0-length string', position
  981.         end
  982.     end
  983.     if options ~= baseOptions or kind == "range" or kind == "text" or kind == "toggle" or kind == "color" then
  984.         if options.type == "header" and not options.cmdName and not options.name then
  985.         elseif options.cmdName then
  986.             if type(options.cmdName) ~= "string" then
  987.                 return '"cmdName" must be a string or nil', position
  988.             elseif options.cmdName:len() == 0 then
  989.                 return '"cmdName" cannot be a 0-length string', position
  990.             end
  991.             if type(options.guiName) ~= "string" then
  992.                 if not options.guiNameIsMap then
  993.                     return '"guiName" must be a string or nil', position
  994.                 end
  995.             elseif options.guiName:len() == 0 then
  996.                 return '"guiName" cannot be a 0-length string', position
  997.             end
  998.         else
  999.             if type(options.name) ~= "string" then
  1000.                 return '"name" must be a string', position
  1001.             elseif options.name:len() == 0 then
  1002.                 return '"name" cannot be a 0-length string', position
  1003.             end
  1004.         end
  1005.     end
  1006.     if options.guiNameIsMap then
  1007.         if type(options.guiNameIsMap) ~= "boolean" then
  1008.             return '"guiNameIsMap" must be a boolean or nil', position
  1009.         elseif options.type ~= "toggle" then
  1010.             return 'if "guiNameIsMap" is true, then "type" must be set to \'toggle\'', position
  1011.         elseif type(options.map) ~= "table" then
  1012.             return '"map" must be a table', position
  1013.         end
  1014.     end
  1015.     if options.message and type(options.message) ~= "string" then
  1016.         return '"message" must be a string or nil', position
  1017.     end
  1018.     if options.error and type(options.error) ~= "string" then
  1019.         return '"error" must be a string or nil', position
  1020.     end
  1021.     if options.current and type(options.current) ~= "string" then
  1022.         return '"current" must be a string or nil', position
  1023.     end
  1024.     if options.order then
  1025.         if type(options.order) ~= "number" or (-1 < options.order and options.order < 0.999) then
  1026.             return '"order" must be a non-zero number or nil', position
  1027.         end
  1028.     end
  1029.     if options.disabled then
  1030.         if type(options.disabled) ~= "function" and type(options.disabled) ~= "string" and options.disabled ~= true then
  1031.             return '"disabled" must be a function, string, or boolean', position
  1032.         end
  1033.     end
  1034.     if options.cmdHidden then
  1035.         if type(options.cmdHidden) ~= "function" and type(options.cmdHidden) ~= "string" and options.cmdHidden ~= true then
  1036.             return '"cmdHidden" must be a function, string, or boolean', position
  1037.         end
  1038.     end
  1039.     if options.guiHidden then
  1040.         if type(options.guiHidden) ~= "function" and type(options.guiHidden) ~= "string" and options.guiHidden ~= true then
  1041.             return '"guiHidden" must be a function, string, or boolean', position
  1042.         end
  1043.     end
  1044.     if options.hidden then
  1045.         if type(options.hidden) ~= "function" and type(options.hidden) ~= "string" and options.hidden ~= true then
  1046.             return '"hidden" must be a function, string, or boolean', position
  1047.         end
  1048.     end
  1049.     if kind == "text" then
  1050.         if type(options.validate) == "table" then
  1051.             local t = options.validate
  1052.             local iTable = nil
  1053.             for k,v in pairs(t) do
  1054.                 if type(k) == "number" then
  1055.                     if iTable == nil then
  1056.                         iTable = true
  1057.                     elseif not iTable then
  1058.                         return '"validate" must either have all keys be indexed numbers or strings', position
  1059.                     elseif k < 1 or k > #t then
  1060.                         return '"validate" numeric keys must be indexed properly. >= 1 and <= #t', position
  1061.                     end
  1062.                 else
  1063.                     if iTable == nil then
  1064.                         iTable = false
  1065.                     elseif iTable then
  1066.                         return '"validate" must either have all keys be indexed numbers or strings', position
  1067.                     end
  1068.                 end
  1069.                 if type(v) ~= "string" then
  1070.                     return '"validate" values must all be strings', position
  1071.                 end
  1072.             end
  1073.             if options.multiToggle and options.multiToggle ~= true then
  1074.                 return '"multiToggle" must be a boolean or nil if "validate" is a table', position
  1075.             end
  1076.         elseif options.validate == "keybinding" then
  1077.             -- no other checks
  1078.         else
  1079.             if type(options.usage) ~= "string" then
  1080.                 return '"usage" must be a string', position
  1081.             elseif options.validate and type(options.validate) ~= "string" and type(options.validate) ~= "function" then
  1082.                 return '"validate" must be a string, function, or table', position
  1083.             end
  1084.         end
  1085.         if options.multiToggle and type(options.validate) ~= "table" then
  1086.             return '"validate" must be a table if "multiToggle" is true', position
  1087.         end
  1088.     elseif kind == "range" then
  1089.         if options.min or options.max then
  1090.             if type(options.min) ~= "number" then
  1091.                 return '"min" must be a number', position
  1092.             elseif type(options.max) ~= "number" then
  1093.                 return '"max" must be a number', position
  1094.             elseif options.min >= options.max then
  1095.                 return '"min" must be less than "max"', position
  1096.             end
  1097.         end
  1098.         if options.step then
  1099.             if type(options.step) ~= "number" then
  1100.                 return '"step" must be a number', position
  1101.             elseif options.step < 0 then
  1102.                 return '"step" must be nonnegative', position
  1103.             end
  1104.         end
  1105.         if options.bigStep then
  1106.             if type(options.bigStep) ~= "number" then
  1107.                 return '"bigStep" must be a number', position
  1108.             elseif options.bigStep < 0 then
  1109.                 return '"bigStep" must be nonnegative', position
  1110.             end
  1111.         end
  1112.         if options.isPercent and options.isPercent ~= true then
  1113.             return '"isPercent" must either be nil, true, or false', position
  1114.         end
  1115.     elseif kind == "toggle" then
  1116.         if options.map then
  1117.             if type(options.map) ~= "table" then
  1118.                 return '"map" must be a table', position
  1119.             elseif type(options.map[true]) ~= "string" then
  1120.                 return '"map[true]" must be a string', position
  1121.             elseif type(options.map[false]) ~= "string" then
  1122.                 return '"map[false]" must be a string', position
  1123.             end
  1124.         end
  1125.     elseif kind == "color" then
  1126.         if options.hasAlpha and options.hasAlpha ~= true then
  1127.             return '"hasAlpha" must be nil, true, or false', position
  1128.         end
  1129.     elseif kind == "group" then
  1130.         if options.pass and options.pass ~= true then
  1131.             return '"pass" must be nil, true, or false', position
  1132.         end
  1133.         if type(options.args) ~= "table" then
  1134.             return '"args" must be a table', position
  1135.         end
  1136.         for k,v in pairs(options.args) do
  1137.             if type(k) ~= "number" then
  1138.                 if type(k) ~= "string" then
  1139.                     return '"args" keys must be strings or numbers', position
  1140.                 elseif k:len() == 0 then
  1141.                     return '"args" keys must not be 0-length strings.', position
  1142.                 end
  1143.             end
  1144.             if type(v) ~= "table" then
  1145.                 return '"args" values must be tables', position and position .. "." .. k or k
  1146.             end
  1147.             local newposition
  1148.             if position then
  1149.                 newposition = position .. ".args." .. k
  1150.             else
  1151.                 newposition = "args." .. k
  1152.             end
  1153.             local err, pos = validateOptions(v, newposition, baseOptions, options.pass)
  1154.             if err then
  1155.                 return err, pos
  1156.             end
  1157.         end
  1158.     elseif kind == "execute" then
  1159.         if type(options.confirm) ~= "string" and type(options.confirm) ~= "boolean" and type(options.confirm) ~= "nil" then
  1160.             return '"confirm" must be a string, boolean, or nil', position
  1161.         end
  1162.     end
  1163.     if options.icon and type(options.icon) ~= "string" then
  1164.         return'"icon" must be a string', position
  1165.     end
  1166.     if options.iconWidth or options.iconHeight then
  1167.         if type(options.iconWidth) ~= "number" or type(options.iconHeight) ~= "number" then
  1168.             return '"iconHeight" and "iconWidth" must be numbers', position
  1169.         end
  1170.     end
  1171.     if options.iconCoordLeft or options.iconCoordRight or options.iconCoordTop or options.iconCoordBottom then
  1172.         if type(options.iconCoordLeft) ~= "number" or type(options.iconCoordRight) ~= "number" or type(options.iconCoordTop) ~= "number" or type(options.iconCoordBottom) ~= "number" then
  1173.             return '"iconCoordLeft", "iconCoordRight", "iconCoordTop", and "iconCoordBottom" must be numbers', position
  1174.         end
  1175.     end
  1176. end
  1177.  
  1178. local validatedOptions
  1179.  
  1180. local values
  1181. local mysort_args
  1182. local mysort
  1183. local othersort
  1184. local othersort_validate
  1185.  
  1186. local baseFunc, currentLevel
  1187.  
  1188. local function confirmPopup(message, func, ...)
  1189.     if not StaticPopupDialogs["DEWDROP20_CONFIRM_DIALOG"] then
  1190.         StaticPopupDialogs["DEWDROP20_CONFIRM_DIALOG"] = {}
  1191.     end
  1192.     local t = StaticPopupDialogs["DEWDROP20_CONFIRM_DIALOG"]
  1193.     for k in pairs(t) do
  1194.         t[k] = nil
  1195.     end
  1196.     t.text = message
  1197.     t.button1 = ACCEPT or "Accept"
  1198.     t.button2 = CANCEL or "Cancel"
  1199.     t.OnAccept = function()
  1200.         func(unpack(t))
  1201.     end
  1202.     for i = 1, select('#', ...) do
  1203.         t[i] = select(i, ...)
  1204.     end
  1205.     t.timeout = 0
  1206.     t.whileDead = 1
  1207.     t.hideOnEscape = 1
  1208.  
  1209.     Dewdrop:Close()
  1210.     StaticPopup_Show("DEWDROP20_CONFIRM_DIALOG")
  1211. end
  1212.  
  1213.  
  1214. local function getMethod(settingname, handler, v, methodName, ...)  -- "..." is simply returned straight out cause you can't do "a,b,c = 111,f(),222"
  1215.     assert(v and type(v)=="table")
  1216.     assert(methodName and type(methodName)=="string")
  1217.  
  1218.     local method = v[methodName]
  1219.     if type(method)=="function" then
  1220.         return method, ...
  1221.     elseif type(method)=="string" then
  1222.         if not handler then
  1223.             Dewdrop:error("[%s] 'handler' is required if providing a method name: %q", tostring(settingname), method)
  1224.         elseif not handler[method] then
  1225.             Dewdrop:error("[%s] 'handler' method %q not defined", tostring(settingname), method)
  1226.         end
  1227.         return handler[method], handler, ...
  1228.     end
  1229.  
  1230.     Dewdrop:error("[%s] Missing %q directive", tostring(settingname), methodName)
  1231. end
  1232.  
  1233. local function callMethod(settingname, handler, v, methodName, ...)
  1234.     assert(v and type(v)=="table")
  1235.     assert(methodName and type(methodName)=="string")
  1236.  
  1237.     local method = v[methodName]
  1238.     if type(method)=="function" then
  1239.         local success, ret,ret2,ret3,ret4 = pcall(v[methodName], ...)
  1240.         if not success then
  1241.             geterrorhandler()(ret)
  1242.             return nil
  1243.         end
  1244.         return ret,ret2,ret3,ret4
  1245.  
  1246.     elseif type(method)=="string" then
  1247.  
  1248.         local neg = method:match("^~(.-)$")
  1249.         if neg then
  1250.             method = neg
  1251.         end
  1252.         if not handler then
  1253.             Dewdrop:error("[%s] 'handler' is required if providing a method name: %q", tostring(settingname), method)
  1254.         elseif not handler[method] then
  1255.             Dewdrop:error("[%s] 'handler' (%q) method %q not defined", tostring(settingname), handler.name or "(unnamed)", method)
  1256.         end
  1257.         local success, ret,ret2,ret3,ret4 = pcall(handler[method], handler, ...)
  1258.         if not success then
  1259.             geterrorhandler()(ret)
  1260.             return nil
  1261.         end
  1262.         if neg then
  1263.             return not ret
  1264.         end
  1265.         return ret,ret2,ret3,ret4
  1266.     elseif method == false then
  1267.         return nil
  1268.     end
  1269.  
  1270.     Dewdrop:error("[%s] Missing %q directive in %q", tostring(settingname), methodName, v.name or "(unnamed)")
  1271. end
  1272.  
  1273. local function skip1Nil(...)
  1274.     if select(1,...)==nil then
  1275.         return select(2,...)
  1276.     end
  1277.     return ...
  1278. end
  1279.  
  1280. function Dewdrop:FeedAceOptionsTable(options, difference)
  1281.     self:argCheck(options, 2, "table")
  1282.     self:argCheck(difference, 3, "nil", "number")
  1283.     if not currentLevel then
  1284.         self:error("Cannot call `FeedAceOptionsTable' outside of a Dewdrop declaration")
  1285.     end
  1286.     if not difference then
  1287.         difference = 0
  1288.     end
  1289.     if not validatedOptions then
  1290.         validatedOptions = {}
  1291.     end
  1292.     if not validatedOptions[options] then
  1293.         local err, position = validateOptions(options)
  1294.  
  1295.         if err then
  1296.             if position then
  1297.                 Dewdrop:error(position .. ": " .. err)
  1298.             else
  1299.                 Dewdrop:error(err)
  1300.             end
  1301.         end
  1302.  
  1303.         validatedOptions[options] = true
  1304.     end
  1305.     local level = levels[currentLevel]
  1306.     if not level then
  1307.         self:error("Improper level given")
  1308.     end
  1309.     if not values then
  1310.         values = {}
  1311.     else
  1312.         for k,v in pairs(values) do
  1313.             values[k] = nil
  1314.         end
  1315.     end
  1316.  
  1317.     local current = level
  1318.     while current do        -- this traverses from higher level numbers to lower, building "values" with leaf nodes first and trunk nodes later
  1319.         if current.num == difference + 1 then
  1320.             break
  1321.         end
  1322.         table.insert(values, current.value)
  1323.         current = levels[current.num - 1]
  1324.     end
  1325.  
  1326.     local realOptions = options
  1327.     local handler = options.handler
  1328.     local passTable
  1329.     local passValue
  1330.     while #values > 0 do    -- This loop traverses values from the END (trunk nodes first, then onto leaf nodes)
  1331.         if options.pass then
  1332.             if options.get and options.set then
  1333.                 passTable = options
  1334.             elseif not passTable then
  1335.                 passTable = options
  1336.             end
  1337.         else
  1338.             passTable = nil
  1339.         end
  1340.         local value = table.remove(values)
  1341.         options = options.args and options.args[value]
  1342.         if not options then
  1343.             return
  1344.         end
  1345.         handler = options.handler or handler
  1346.         passValue = passTable and value or nil
  1347.     end
  1348.  
  1349.     if options.type == "group" then
  1350.         local hidden = options.hidden
  1351.         if type(hidden) == "function" or type(hidden) == "string" then
  1352.             hidden = callMethod(options.name or "(options root)", handler, options, "hidden", options.passValue) or false
  1353.         end
  1354.         if hidden then
  1355.             return
  1356.         end
  1357.         local disabled = options.disabled
  1358.         if type(disabled) == "function" or type(disabled) == "string" then
  1359.             disabled = callMethod(options.name or "(options root)", handler, options, "disabled", options.passValue) or false
  1360.         end
  1361.         if disabled then
  1362.             self:AddLine(
  1363.                 'text', DISABLED,
  1364.                 'disabled', true
  1365.             )
  1366.             return
  1367.         end
  1368.         for k in pairs(options.args) do
  1369.             table.insert(values, k)
  1370.         end
  1371.         if options.pass then
  1372.             if options.get and options.set then
  1373.                 passTable = options
  1374.             elseif not passTable then
  1375.                 passTable = options
  1376.             end
  1377.         else
  1378.             passTable = nil
  1379.         end
  1380.         if not mysort then
  1381.             mysort = function(a, b)
  1382.                 local alpha, bravo = mysort_args[a], mysort_args[b]
  1383.                 local alpha_order = alpha.order or 100
  1384.                 local bravo_order = bravo.order or 100
  1385.                 local alpha_name = alpha.guiName or alpha.name
  1386.                 local bravo_name = bravo.guiName or bravo.name
  1387.                 if alpha_order == bravo_order then
  1388.                     if not alpha_name then
  1389.                         return bravo_name
  1390.                     elseif not bravo_name then
  1391.                         return false
  1392.                     else
  1393.                         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()
  1394.                     end
  1395.                 else
  1396.                     if alpha_order < 0 then
  1397.                         if bravo_order > 0 then
  1398.                             return false
  1399.                         end
  1400.                     else
  1401.                         if bravo_order < 0 then
  1402.                             return true
  1403.                         end
  1404.                     end
  1405.                     return alpha_order < bravo_order
  1406.                 end
  1407.             end
  1408.         end
  1409.         mysort_args = options.args
  1410.         table.sort(values, mysort)
  1411.         mysort_args = nil
  1412.         local hasBoth = #values >= 1 and (options.args[values[1]].order or 100) > 0 and (options.args[values[#values]].order or 100) < 0
  1413.         local last_order = 1
  1414.         for _,k in ipairs(values) do
  1415.             local v = options.args[k]
  1416.             local handler = v.handler or handler
  1417.             if hasBoth and last_order > 0 and (v.order or 100) < 0 then
  1418.                 hasBoth = false
  1419.                 self:AddLine()
  1420.             end
  1421.             local hidden, disabled = v.guiHidden or v.hidden, v.disabled
  1422.  
  1423.             if type(hidden) == "function" or type(hidden) == "string" then
  1424.                 hidden = callMethod(k, handler, v, "hidden", v.passValue) or false
  1425.             end
  1426.             if not hidden then
  1427.                 if type(disabled) == "function" or type(disabled) == "string" then
  1428.                     disabled = callMethod(k, handler, v, "disabled", v.passValue) or false
  1429.                 end
  1430.                 local name = (v.guiIconOnly and v.icon) and "" or (v.guiName or v.name)
  1431.                 local desc = v.guiDesc or v.desc
  1432.                 local iconHeight = v.iconHeight or 16
  1433.                 local iconWidth = v.iconWidth or 16
  1434.                 local iconCoordLeft = v.iconCoordLeft
  1435.                 local iconCoordRight = v.iconCoordRight
  1436.                 local iconCoordBottom = v.iconCoordBottom
  1437.                 local iconCoordTop = v.iconCoordTop
  1438.                 local tooltipTitle, tooltipText
  1439.                 tooltipTitle = name
  1440.                 if name ~= desc then
  1441.                     tooltipText = desc
  1442.                 end
  1443.                 if type(v.usage) == "string" and v.usage:trim():len() > 0 then
  1444.                     if tooltipText then
  1445.                         tooltipText = tooltipText .. "\n\n" .. USAGE_TOOLTIP:format(v.usage)
  1446.                     else
  1447.                         tooltipText = USAGE_TOOLTIP:format(v.usage)
  1448.                     end
  1449.                 end
  1450.                 local v_p = passTable
  1451.                 if not v_p or (v.type ~= "execute" and v.get and v.set) or (v.type == "execute" and v.func) then
  1452.                     v_p = v
  1453.                 end
  1454.                 local passValue = v.passValue or (v_p~=v and k) or nil
  1455.                 if v.type == "toggle" then
  1456.                     local checked = callMethod(name, handler, v_p, "get", passValue) or false
  1457.                     local checked_arg = checked
  1458.                     if type(v_p.get)=="string" and v_p.get:match("^~") then
  1459.                         checked_arg = not checked
  1460.                     end
  1461.                     local func, arg1, arg2, arg3 = getMethod(name, handler, v_p, "set",   skip1Nil(passValue, not checked_arg))
  1462.                     if v.guiNameIsMap then
  1463.                         checked = checked and true or false
  1464.                         name = tostring(v.map and v.map[checked]):gsub("|c%x%x%x%x%x%x%x%x(.-)|r", "%1")
  1465.                         tooltipTitle = name
  1466.                         checked = true--nil
  1467.                     end
  1468.                     self:AddLine(
  1469.                         'text', name,
  1470.                         'checked', checked,
  1471.                         'isRadio', v.isRadio,
  1472.                         'func', func,
  1473.                         'arg1', arg1,
  1474.                         'arg2', arg2,
  1475.                         'arg3', arg3,
  1476.                         'disabled', disabled,
  1477.                         'tooltipTitle', tooltipTitle,
  1478.                         'tooltipText', tooltipText
  1479.                     )
  1480.                 elseif v.type == "execute" then
  1481.                     local func, arg1, arg2, arg3, arg4
  1482.                     local confirm = v.confirm
  1483.                     if confirm == true then
  1484.                         confirm = DEFAULT_CONFIRM_MESSAGE:format(tooltipText or tooltipTitle)
  1485.                         func,arg1,arg2,arg3,arg4 = confirmPopup, confirm, getMethod(name, handler, v_p, "func", passValue)
  1486.                     elseif type(confirm) == "string" then
  1487.                         func,arg1,arg2,arg3,arg4 = confirmPopup, confirm, getMethod(name, handler, v_p, "func", passValue)
  1488.                     else
  1489.                         func,arg1,arg2 = getMethod(name, handler, v_p, "func", passValue)
  1490.                     end
  1491.                     self:AddLine(
  1492.                         'text', name,
  1493.                         'checked', checked,
  1494.                         'func', func,
  1495.                         'arg1', arg1,
  1496.                         'arg2', arg2,
  1497.                         'arg3', arg3,
  1498.                         'arg4', arg4,
  1499.                         'disabled', disabled,
  1500.                         'tooltipTitle', tooltipTitle,
  1501.                         'tooltipText', tooltipText,
  1502.                         'icon', v.icon,
  1503.                         'iconHeight', iconHeight,
  1504.                         'iconWidth', iconWidth,
  1505.                         'iconCoordLeft', iconCoordLeft,
  1506.                         'iconCoordRight', iconCoordRight,
  1507.                         'iconCoordTop', iconCoordTop,
  1508.                         'iconCoordBottom', iconCoordBottom
  1509.                     )
  1510.                 elseif v.type == "range" then
  1511.                     local sliderValue
  1512.                     sliderValue = callMethod(name, handler, v_p, "get", passValue) or 0
  1513.                     local sliderFunc, sliderArg1, sliderArg2 = getMethod(name, handler, v_p, "set",    passValue)
  1514.                     if tooltipText then
  1515.                         tooltipText = format("%s\n\n%s", tooltipText, RANGE_TOOLTIP)
  1516.                     else
  1517.                         tooltipText = RANGE_TOOLTIP
  1518.                     end
  1519.                     self:AddLine(
  1520.                         'text', name,
  1521.                         'hasArrow', true,
  1522.                         'hasSlider', true,
  1523.                         'sliderMin', v.min or 0,
  1524.                         'sliderMax', v.max or 1,
  1525.                         'sliderStep', v.step or 0,
  1526.                         'sliderBigStep', v.bigStep or nil,
  1527.                         'sliderIsPercent', v.isPercent or false,
  1528.                         'sliderValue', sliderValue,
  1529.                         'sliderFunc', sliderFunc,
  1530.                         'sliderArg1', sliderArg1,
  1531.                         'sliderArg2', sliderArg2,
  1532.                         'fromAceOptions', true,
  1533.                         'disabled', disabled,
  1534.                         'tooltipTitle', tooltipTitle,
  1535.                         'tooltipText', tooltipText,
  1536.                         'icon', v.icon,
  1537.                         'iconHeight', iconHeight,
  1538.                         'iconWidth', iconWidth,
  1539.                         'iconCoordLeft', iconCoordLeft,
  1540.                         'iconCoordRight', iconCoordRight,
  1541.                         'iconCoordTop', iconCoordTop,
  1542.                         'iconCoordBottom', iconCoordBottom
  1543.                     )
  1544.                 elseif v.type == "color" then
  1545.                     local r,g,b,a = callMethod(name, handler, v_p, "get", passValue)
  1546.                     if not r then
  1547.                         r,g,b,a = 0,0,0,0
  1548.                     end
  1549.                     local colorFunc, colorArg1, colorArg2 = getMethod(name, handler, v_p, "set",    passValue)
  1550.                     self:AddLine(
  1551.                         'text', name,
  1552.                         'hasArrow', true,
  1553.                         'hasColorSwatch', true,
  1554.                         'r', r,
  1555.                         'g', g,
  1556.                         'b', b,
  1557.                         'opacity', v.hasAlpha and a or nil,
  1558.                         'hasOpacity', v.hasAlpha,
  1559.                         'colorFunc', colorFunc,
  1560.                         'colorArg1', colorArg1,
  1561.                         'colorArg2', colorArg2,
  1562.                         'disabled', disabled,
  1563.                         'tooltipTitle', tooltipTitle,
  1564.                         'tooltipText', tooltipText
  1565.                     )
  1566.                 elseif v.type == "text" then
  1567.                         if type(v.validate) == "table" then
  1568.                         local func,arg1,arg2
  1569.                         if v.onClick then
  1570.                             func,arg1,arg2 = getMethod(name, handler, v, "onClick",    passValue)
  1571.                         end
  1572.                         local checked
  1573.                         if v.isChecked then
  1574.                             checked = callMethod(name, handler, v, "isChecked",    passValue) or false
  1575.                         end
  1576.                         self:AddLine(
  1577.                             'text', name,
  1578.                             'hasArrow', true,
  1579.                             'value', k,
  1580.                             'func', func,
  1581.                             'arg1', arg1,
  1582.                             'arg2', arg2,
  1583.                             'mouseoverUnderline', func and true or nil,
  1584.                             'disabled', disabled,
  1585.                             'checked', checked,
  1586.                             'tooltipTitle', tooltipTitle,
  1587.                             'tooltipText', tooltipText,
  1588.                             'icon', v.icon,
  1589.                             'iconHeight', iconHeight,
  1590.                             'iconWidth', iconWidth,
  1591.                             'iconCoordLeft', iconCoordLeft,
  1592.                             'iconCoordRight', iconCoordRight,
  1593.                             'iconCoordTop', iconCoordTop,
  1594.                             'iconCoordBottom', iconCoordBottom
  1595.                         )
  1596.                     else
  1597.                         local editBoxText
  1598.                         editBoxText = callMethod(name, handler, v_p, "get", passValue) or ""
  1599.                         local editBoxFunc, editBoxArg1, editBoxArg2 = getMethod(name, handler, v_p, "set",    passValue)
  1600.  
  1601.                         local editBoxValidateFunc, editBoxValidateArg1
  1602.  
  1603.                         if v.validate and v.validate ~= "keybinding" then
  1604.                             if v.validate == "keybinding" then
  1605.                                 if tooltipText then
  1606.                                     tooltipText = format("%s\n\n%s", tooltipText, RESET_KEYBINDING_DESC)
  1607.                                 else
  1608.                                     tooltipText = RESET_KEYBINDING_DESC
  1609.                                 end
  1610.                             else
  1611.                                 editBoxValidateFunc, editBoxValidateArg1 = getMethod(name, handler, v, "validate") -- no passvalue!
  1612.                             end
  1613.                         end
  1614.  
  1615.                         self:AddLine(
  1616.                             'text', name,
  1617.                             'hasArrow', true,
  1618.                             'icon', v.icon,
  1619.                             'iconHeight', iconHeight,
  1620.                             'iconWidth', iconWidth,
  1621.                             'iconCoordLeft', iconCoordLeft,
  1622.                             'iconCoordRight', iconCoordRight,
  1623.                             'iconCoordTop', iconCoordTop,
  1624.                             'iconCoordBottom', iconCoordBottom,
  1625.                             'hasEditBox', true,
  1626.                             'editBoxText', editBoxText,
  1627.                             'editBoxFunc', editBoxFunc,
  1628.                             'editBoxArg1', editBoxArg1,
  1629.                             'editBoxArg2', editBoxArg2,
  1630.                             'editBoxValidateFunc', editBoxValidateFunc,
  1631.                             'editBoxValidateArg1', editBoxValidateArg1,
  1632.                             'editBoxIsKeybinding', v.validate == "keybinding",
  1633.                             'editBoxKeybindingOnly', v.keybindingOnly,
  1634.                             'editBoxKeybindingExcept', v.keybindingExcept,
  1635.                             'disabled', disabled,
  1636.                             'tooltipTitle', tooltipTitle,
  1637.                             'tooltipText', tooltipText
  1638.                         )
  1639.                     end
  1640.                 elseif v.type == "group" then
  1641.                     local func,arg1,arg2
  1642.                     if v.onClick then
  1643.                         func,arg1,arg2 = getMethod(name, handler, v, "onClick",    passValue)
  1644.                     end
  1645.                     local checked
  1646.                     if v.isChecked then
  1647.                         checked = callMethod(name, handler, v, "isChecked",    passValue) or false
  1648.                     end
  1649.                     self:AddLine(
  1650.                         'text', name,
  1651.                         'hasArrow', true,
  1652.                         'value', k,
  1653.                         'func', func,
  1654.                         'arg1', arg1,
  1655.                         'arg2', arg2,
  1656.                         'mouseoverUnderline', func and true or nil,
  1657.                         'disabled', disabled,
  1658.                         'checked', checked,
  1659.                         'tooltipTitle', tooltipTitle,
  1660.                         'tooltipText', tooltipText,
  1661.                         'icon', v.icon,
  1662.                         'iconHeight', iconHeight,
  1663.                         'iconWidth', iconWidth,
  1664.                         'iconCoordLeft', iconCoordLeft,
  1665.                         'iconCoordRight', iconCoordRight,
  1666.                         'iconCoordTop', iconCoordTop,
  1667.                         'iconCoordBottom', iconCoordBottom
  1668.                     )
  1669.                 elseif v.type == "header" then
  1670.                     if name == "" or not name then
  1671.                         self:AddLine(
  1672.                             'isTitle', true,
  1673.                             'icon', v.icon,
  1674.                             'iconHeight', iconHeight,
  1675.                             'iconWidth', iconWidth,
  1676.                             'iconCoordLeft', iconCoordLeft,
  1677.                             'iconCoordRight', iconCoordRight,
  1678.                             'iconCoordTop', iconCoordTop,
  1679.                             'iconCoordBottom', iconCoordBottom
  1680.                         )
  1681.                     else
  1682.                         self:AddLine(
  1683.                             'text', name,
  1684.                             'isTitle', true,
  1685.                             'icon', v.icon,
  1686.                             'iconHeight', iconHeight,
  1687.                             'iconWidth', iconWidth,
  1688.                             'iconCoordLeft', iconCoordLeft,
  1689.                             'iconCoordRight', iconCoordRight,
  1690.                             'iconCoordTop', iconCoordTop,
  1691.                             'iconCoordBottom', iconCoordBottom
  1692.                         )
  1693.                     end
  1694.                 end
  1695.             end
  1696.             last_order = v.order or 100
  1697.         end
  1698.     elseif options.type == "text" and type(options.validate) == "table" then
  1699.         local current
  1700.         local options_p = passTable
  1701.         if not options_p or (options.get and options.set) then
  1702.             options_p = options
  1703.             passTable = nil
  1704.             passValue = nil
  1705.         end
  1706.         local multiToggle = options.multiToggle
  1707.         local passValue = options.passValue or passValue
  1708.         if not multiToggle then
  1709.             current = callMethod(k, handler, options_p, "get", passValue)
  1710.         end
  1711.         local indexed = true
  1712.         for k,v in pairs(options.validate) do
  1713.             if type(k) ~= "number" then
  1714.                 indexed = false
  1715.             end
  1716.             table.insert(values, k)
  1717.         end
  1718.         if not indexed then
  1719.             if not othersort then
  1720.                 othersort = function(alpha, bravo)
  1721.                     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()
  1722.                 end
  1723.             end
  1724.             othersort_validate = options.validate
  1725.             table.sort(values, othersort)
  1726.             othersort_validate = nil
  1727.         end
  1728.         for _,k in ipairs(values) do
  1729.             local v = options.validate[k]
  1730.             if type(k) == "number" then
  1731.                 k = v
  1732.             end
  1733.             local func, arg1, arg2, arg3, arg4 = getMethod(k, handler, options_p, "set",    skip1Nil(passValue, k))
  1734.             local checked
  1735.             if multiToggle then
  1736.                 checked = callMethod(k, handler, options_p, "get", skip1Nil(passValue, k)) or false
  1737.                 if arg2 == nil then
  1738.                     arg2 = not checked
  1739.                 elseif arg3 == nil then
  1740.                     arg3 = not checked
  1741.                 else
  1742.                     arg4 = not checked
  1743.                 end
  1744.             else
  1745.                 checked = (k == current or (type(k) == "string" and type(current) == "string" and k:lower() == current:lower()))
  1746.                 if checked then
  1747.                     func, arg1, arg2, arg3, arg4 = nil, nil, nil, nil, nil
  1748.                 end
  1749.             end
  1750.             local tooltipTitle
  1751.             local tooltipText
  1752.             if options.validateDesc then
  1753.                 tooltipTitle = v
  1754.                 tooltipText = options.validateDesc[k]
  1755.             else
  1756.                 tooltipTitle = options.guiName or options.name
  1757.                 tooltipText = v
  1758.             end
  1759.             self:AddLine(
  1760.                 'text', v,
  1761.                 'func', func,
  1762.                 'arg1', arg1,
  1763.                 'arg2', arg2,
  1764.                 'arg3', arg3,
  1765.                 'arg4', arg4,
  1766.                 'isRadio', not multiToggle,
  1767.                 'checked',  checked,
  1768.                 'tooltipTitle', tooltipTitle,
  1769.                 'tooltipText', tooltipText
  1770.             )
  1771.         end
  1772.         for k in pairs(values) do
  1773.             values[k] = nil
  1774.         end
  1775.     else
  1776.         return false
  1777.     end
  1778.     return true
  1779. end
  1780.  
  1781. function Dewdrop:FeedTable(s, difference)
  1782.     self:argCheck(s, 2, "table")
  1783.     self:argCheck(difference, 3, "nil", "number")
  1784.     if not currentLevel then
  1785.         self:error("Cannot call `FeedTable' outside of a Dewdrop declaration")
  1786.     end
  1787.     if not difference then
  1788.         difference = 0
  1789.     end
  1790.     local level = levels[currentLevel]
  1791.     if not level then
  1792.         self:error("Improper level given")
  1793.     end
  1794.     if not values then
  1795.         values = {}
  1796.     else
  1797.         for k,v in pairs(values) do
  1798.             values[k] = nil
  1799.         end
  1800.     end
  1801.     local t = s.subMenu and s or {subMenu = s}
  1802.     local current = level
  1803.     while current do
  1804.         if current.num == difference + 1 then
  1805.             break
  1806.         end
  1807.         table.insert(values, current.value)
  1808.         current = levels[current.num - 1]
  1809.     end
  1810.  
  1811.     while #values > 0 do
  1812.         local value = table.remove(values)
  1813.         t = t.subMenu and t.subMenu[value]
  1814.         if not t then
  1815.             return
  1816.         end
  1817.     end
  1818.  
  1819.     if t.subMenu or current.num == 1 then
  1820.         for k in pairs(t.subMenu) do
  1821.             table.insert(values, k)
  1822.         end
  1823.         table.sort(values)
  1824.         for _,k in ipairs(values) do
  1825.             local argTable = {"value", k}
  1826.             for key, val in pairs(t.subMenu[k]) do
  1827.                 table.insert(argTable, key)
  1828.                 table.insert(argTable, val)
  1829.             end
  1830.             self:AddLine(unpack(argTable))
  1831.         end
  1832.         for k in pairs(values) do
  1833.             values[k] = nil
  1834.         end
  1835.         return false
  1836.     end
  1837.     return true
  1838. end
  1839.  
  1840. function Refresh(self, level)
  1841.     if type(level) == "number" then
  1842.         level = levels[level]
  1843.     end
  1844.     if not level then
  1845.         return
  1846.     end
  1847.     if baseFunc then
  1848.         Clear(self, level)
  1849.         currentLevel = level.num
  1850.         if type(baseFunc) == "table" then
  1851.             if currentLevel == 1 then
  1852.                 local handler = baseFunc.handler
  1853.                 if handler then
  1854.                     local name = tostring(handler)
  1855.                     if not name:find('^table:') and not handler.hideMenuTitle then
  1856.                         name = name:gsub("|c%x%x%x%x%x%x%x%x(.-)|r", "%1")
  1857.                         self:AddLine(
  1858.                             'text', name,
  1859.                             'isTitle', true
  1860.                         )
  1861.                     end
  1862.                 end
  1863. --          elseif level.parentText then
  1864. --              self:AddLine(
  1865. --                  'text', level.parentText,
  1866. --                  'tooltipTitle', level.parentTooltipTitle,
  1867. --                  'tooltipText', level.parentTooltipText,
  1868. --                  'tooltipFunc', level.parentTooltipFunc,
  1869. --                  'isTitle', true
  1870. --              )
  1871.             end
  1872.             self:FeedAceOptionsTable(baseFunc)
  1873.             if currentLevel == 1 then
  1874.                 self:AddLine(
  1875.                     'text', CLOSE,
  1876.                     'tooltipTitle', CLOSE,
  1877.                     'tooltipText', CLOSE_DESC,
  1878.                     'closeWhenClicked', true
  1879.                 )
  1880.             end
  1881.         else
  1882. --          if level.parentText then
  1883. --              self:AddLine(
  1884. --                  'text', level.parentText,
  1885. --                  'tooltipTitle', level.parentTooltipTitle,
  1886. --                  'tooltipText', level.parentTooltipText,
  1887. --                  'tooltipFunc', level.parentTooltipFunc,
  1888. --                  'isTitle', true
  1889. --              )
  1890. --          end
  1891.             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)
  1892.         end
  1893.         currentLevel = nil
  1894.         CheckSize(self, level)
  1895.     end
  1896. end
  1897.  
  1898. function Dewdrop:Refresh(level)
  1899.     self:argCheck(level, 2, "number", "nil")
  1900.     if not level then
  1901.         for k,v in pairs(levels) do
  1902.             Refresh(self, v)
  1903.         end
  1904.     else
  1905.         Refresh(self, levels[level])
  1906.     end
  1907. end
  1908.  
  1909. function OpenSlider(self, parent)
  1910.     if not sliderFrame then
  1911.         sliderFrame = CreateFrame("Frame", nil, nil)
  1912.         sliderFrame:SetWidth(100)
  1913.         sliderFrame:SetHeight(170)
  1914.         sliderFrame:SetScale(UIParent:GetScale())
  1915.         sliderFrame:SetBackdrop(tmp(
  1916.             'bgFile', "Interface\\Tooltips\\UI-Tooltip-Background",
  1917.             'edgeFile', "Interface\\Tooltips\\UI-Tooltip-Border",
  1918.             'tile', true,
  1919.             'insets', tmp2(
  1920.                 'left', 5,
  1921.                 'right', 5,
  1922.                 'top', 5,
  1923.                 'bottom', 5
  1924.             ),
  1925.             'tileSize', 16,
  1926.             'edgeSize', 16
  1927.         ))
  1928.         sliderFrame:SetFrameStrata("FULLSCREEN_DIALOG")
  1929.         if sliderFrame.SetTopLevel then
  1930.             sliderFrame:SetTopLevel(true)
  1931.         end
  1932.         sliderFrame:SetBackdropBorderColor(TOOLTIP_DEFAULT_COLOR.r, TOOLTIP_DEFAULT_COLOR.g, TOOLTIP_DEFAULT_COLOR.b)
  1933.         sliderFrame:SetBackdropColor(TOOLTIP_DEFAULT_BACKGROUND_COLOR.r, TOOLTIP_DEFAULT_BACKGROUND_COLOR.g, TOOLTIP_DEFAULT_BACKGROUND_COLOR.b)
  1934.         sliderFrame:EnableMouse(true)
  1935.         sliderFrame:EnableMouseWheel(true)
  1936.         sliderFrame:Hide()
  1937.         sliderFrame:SetPoint("CENTER", UIParent, "CENTER")
  1938.         local slider = CreateFrame("Slider", nil, sliderFrame)
  1939.         sliderFrame.slider = slider
  1940.         slider:SetOrientation("VERTICAL")
  1941.         slider:SetMinMaxValues(0, 1)
  1942.         slider:SetValueStep(0.000000001)
  1943.         slider:SetValue(0.5)
  1944.         slider:SetWidth(16)
  1945.         slider:SetHeight(128)
  1946.         slider:SetPoint("LEFT", sliderFrame, "LEFT", 15, 0)
  1947.         slider:SetBackdrop(tmp(
  1948.             'bgFile', "Interface\\Buttons\\UI-SliderBar-Background",
  1949.             'edgeFile', "Interface\\Buttons\\UI-SliderBar-Border",
  1950.             'tile', true,
  1951.             'edgeSize', 8,
  1952.             'tileSize', 8,
  1953.             'insets', tmp2(
  1954.                 'left', 3,
  1955.                 'right', 3,
  1956.                 'top', 3,
  1957.                 'bottom', 3
  1958.             )
  1959.         ))
  1960.         local texture = slider:CreateTexture()
  1961.         slider:SetThumbTexture("Interface\\Buttons\\UI-SliderBar-Button-Vertical")
  1962.         local text = slider:CreateFontString(nil, "ARTWORK")
  1963.         sliderFrame.topText = text
  1964.         text:SetFontObject(GameFontGreenSmall)
  1965.         text:SetText("100%")
  1966.         text:SetPoint("BOTTOM", slider, "TOP")
  1967.         local text = slider:CreateFontString(nil, "ARTWORK")
  1968.         sliderFrame.bottomText = text
  1969.         text:SetFontObject(GameFontGreenSmall)
  1970.         text:SetText("0%")
  1971.         text:SetPoint("TOP", slider, "BOTTOM")
  1972.         local editBox = CreateFrame("EditBox", nil, sliderFrame)
  1973.         sliderFrame.currentText = editBox
  1974.         editBox:SetFontObject(ChatFontNormal)
  1975.         editBox:SetHeight(13)
  1976.         editBox:SetPoint("RIGHT", sliderFrame, "RIGHT", -16, 0)
  1977.         editBox:SetPoint("LEFT", slider, "RIGHT", 12, 0)
  1978.         editBox:SetText("50%")
  1979.         editBox:SetJustifyH("CENTER")
  1980.  
  1981.         local width = editBox:GetWidth()/2 + 10
  1982.         local left = editBox:CreateTexture(nil, "BACKGROUND")
  1983.         left:SetTexture("Interface\\ChatFrame\\UI-ChatInputBorder-Left")
  1984.         left:SetTexCoord(0, width / 256, 0, 1)
  1985.         left:SetWidth(width)
  1986.         left:SetHeight(32)
  1987.         left:SetPoint("LEFT", editBox, "LEFT", -10, 0)
  1988.         local right = editBox:CreateTexture(nil, "BACKGROUND")
  1989.         right:SetTexture("Interface\\ChatFrame\\UI-ChatInputBorder-Right")
  1990.         right:SetTexCoord(1 - width / 256, 1, 0, 1)
  1991.         right:SetWidth(width)
  1992.         right:SetHeight(32)
  1993.         right:SetPoint("RIGHT", editBox, "RIGHT", 10, 0)
  1994.  
  1995.         local changed = false
  1996.         local inside = false
  1997.         slider:SetScript("OnValueChanged", function()
  1998.             if sliderFrame.changing then
  1999.                 return
  2000.             end
  2001.             changed = true
  2002.             local done = false
  2003.             if sliderFrame.parent and sliderFrame.parent.sliderFunc then
  2004.                 local min = sliderFrame.parent.sliderMin or 0
  2005.                 local max = sliderFrame.parent.sliderMax or 1
  2006.                 local step
  2007.                 if sliderFrame.fineStep then
  2008.                     step = sliderFrame.parent.sliderStep or (max - min) / 100
  2009.                 else
  2010.                     step = sliderFrame.parent.sliderBigStep or sliderFrame.parent.sliderStep or (max - min) / 100
  2011.                 end
  2012.                 local value = (1 - slider:GetValue()) * (max - min) + min
  2013.                 if step > 0 then
  2014.                     value = math.floor((value - min) / step + 0.5) * step + min
  2015.                     if value > max then
  2016.                         value = max
  2017.                     elseif value < min then
  2018.                         value = min
  2019.                     end
  2020.                 end
  2021.                 if value == sliderFrame.lastValue then
  2022.                     return
  2023.                 end
  2024.                 sliderFrame.lastValue = value
  2025.                 local text = sliderFrame.parent.sliderFunc(getArgs(sliderFrame.parent, 'sliderArg', 1, value))
  2026.                 if sliderFrame.parent.fromAceOptions then
  2027.                     text = nil
  2028.                 elseif type(text) == "string" or type(text) == "number" then
  2029.                     sliderFrame.currentText:SetText(text)
  2030.                     done = true
  2031.                 end
  2032.             end
  2033.             if not done then
  2034.                 local min = sliderFrame.parent.sliderMin or 0
  2035.                 local max = sliderFrame.parent.sliderMax or 1
  2036.                 local step
  2037.                 if sliderFrame.fineStep then
  2038.                     step = sliderFrame.parent.sliderStep or (max - min) / 100
  2039.                 else
  2040.                     step = sliderFrame.parent.sliderBigStep or sliderFrame.parent.sliderStep or (max - min) / 100
  2041.                 end
  2042.                 local value = (1 - slider:GetValue()) * (max - min) + min
  2043.                 if step > 0 then
  2044.                     value = math.floor((value - min) / step + 0.5) * step + min
  2045.                     if value > max then
  2046.                         value = max
  2047.                     elseif value < min then
  2048.                         value = min
  2049.                     end
  2050.                 end
  2051.                 if sliderFrame.parent.sliderIsPercent then
  2052.                     sliderFrame.currentText:SetText(string.format("%.0f%%", value * 100))
  2053.                 else
  2054.                     if step < 0.1 then
  2055.                         sliderFrame.currentText:SetText(string.format("%.2f", value))
  2056.                     elseif step < 1 then
  2057.                         sliderFrame.currentText:SetText(string.format("%.1f", value))
  2058.                     else
  2059.                         sliderFrame.currentText:SetText(string.format("%.0f", value))
  2060.                     end
  2061.                 end
  2062.             end
  2063.         end)
  2064.         local function onEnter()
  2065.             StopCounting(self, sliderFrame.level)
  2066.             showGameTooltip(sliderFrame.parent)
  2067.         end
  2068.         local function onLeave()
  2069.             GameTooltip:Hide()
  2070.         end
  2071.         sliderFrame:SetScript("OnEnter", onEnter)
  2072.         sliderFrame:SetScript("OnLeave", function()
  2073.             GameTooltip:Hide()
  2074.             if changed then
  2075.                 local parent = sliderFrame.parent
  2076.                 local sliderFunc = parent.sliderFunc
  2077.                 for i = 1, sliderFrame.level - 1 do
  2078.                     Refresh(self, levels[i])
  2079.                 end
  2080.                 local newParent
  2081.                 for _,button in ipairs(levels[sliderFrame.level-1].buttons) do
  2082.                     if button.sliderFunc == sliderFunc then
  2083.                         newParent = button
  2084.                         break
  2085.                     end
  2086.                 end
  2087.                 if newParent then
  2088.                     OpenSlider(self, newParent)
  2089.                 else
  2090.                     sliderFrame:Hide()
  2091.                 end
  2092.             end
  2093.         end)
  2094.         editBox:SetScript("OnEnter", onEnter)
  2095.         editBox:SetScript("OnLeave", onLeave)
  2096.         slider:SetScript("OnMouseDown", function()
  2097.             sliderFrame.mouseDown = true
  2098.             GameTooltip:Hide()
  2099.         end)
  2100.         slider:SetScript("OnMouseUp", function()
  2101.             sliderFrame.mouseDown = false
  2102.             if changed--[[ and not inside]] then
  2103.                 local parent = sliderFrame.parent
  2104.                 local sliderFunc = parent.sliderFunc
  2105.                 for i = 1, sliderFrame.level - 1 do
  2106.                     Refresh(self, levels[i])
  2107.                 end
  2108.                 local newParent
  2109.                 for _,button in ipairs(levels[sliderFrame.level-1].buttons) do
  2110.                     if button.sliderFunc == sliderFunc then
  2111.                         newParent = button
  2112.                         break
  2113.                     end
  2114.                 end
  2115.                 if newParent then
  2116.                     OpenSlider(self, newParent)
  2117.                 else
  2118.                     sliderFrame:Hide()
  2119.                 end
  2120.             end
  2121.             if inside then
  2122.                 showGameTooltip(sliderFrame.parent)
  2123.             end
  2124.         end)
  2125.         slider:SetScript("OnEnter", function()
  2126.             inside = true
  2127.             StopCounting(self, sliderFrame.level)
  2128.             showGameTooltip(sliderFrame.parent)
  2129.         end)
  2130.         slider:SetScript("OnLeave", function()
  2131.             inside = false
  2132.             GameTooltip:Hide()
  2133.             if changed and not sliderFrame.mouseDown then
  2134.                 local parent = sliderFrame.parent
  2135.                 local sliderFunc = parent.sliderFunc
  2136.                 for i = 1, sliderFrame.level - 1 do
  2137.                     Refresh(self, levels[i])
  2138.                 end
  2139.                 local newParent
  2140.                 for _,button in ipairs(levels[sliderFrame.level-1].buttons) do
  2141.                     if button.sliderFunc == sliderFunc then
  2142.                         newParent = button
  2143.                         break
  2144.                     end
  2145.                 end
  2146.                 if newParent then
  2147.                     OpenSlider(self, newParent)
  2148.                 else
  2149.                     sliderFrame:Hide()
  2150.                 end
  2151.  
  2152.                 changed = false
  2153.             end
  2154.         end)
  2155.         sliderFrame:SetScript("OnMouseWheel", function(t, a1)
  2156.             local arg1 = a1 or arg1
  2157.             local up = arg1 > 0
  2158.  
  2159.             local min = sliderFrame.parent.sliderMin or 0
  2160.             local max = sliderFrame.parent.sliderMax or 1
  2161.             local step = sliderFrame.parent.sliderStep or (max - min) / 100
  2162.             if step <= 0 then
  2163.                 step = (max - min) / 100
  2164.             end
  2165.  
  2166.             local value = (1 - slider:GetValue()) * (max - min) + min
  2167.             if up then
  2168.                 value = value + step
  2169.             else
  2170.                 value = value - step
  2171.             end
  2172.             if value > max then
  2173.                 value = max
  2174.             elseif value < min then
  2175.                 value = min
  2176.             end
  2177.             sliderFrame.fineStep = true
  2178.             if max<=min then
  2179.                 slider:SetValue(0)
  2180.             else
  2181.                 slider:SetValue(1 - (value - min) / (max - min))
  2182.             end
  2183.             sliderFrame.fineStep = nil
  2184.         end)
  2185.         slider:SetScript("OnMouseWheel", sliderFrame:GetScript("OnMouseWheel"))
  2186.         editBox:SetScript("OnEnterPressed", function(t, a1)
  2187.             local value = editBox:GetNumber()
  2188.  
  2189.             if sliderFrame.parent.sliderIsPercent then
  2190.                 value = value / 100
  2191.             end
  2192.  
  2193.             local min = sliderFrame.parent.sliderMin or 0
  2194.             local max = sliderFrame.parent.sliderMax or 1
  2195.  
  2196.             if value > max then
  2197.                 value = max
  2198.             elseif value < min then
  2199.                 value = min
  2200.             end
  2201.             sliderFrame.fineStep = true
  2202.             if max <= min then
  2203.                 slider:SetValue(0)
  2204.             else
  2205.                 slider:SetValue(1 - (value - min) / (max - min))
  2206.             end
  2207.             sliderFrame.fineStep = nil
  2208.  
  2209.             StartCounting(self, sliderFrame.level)
  2210.         end)
  2211.         editBox:SetScript("OnEscapePressed", function()
  2212.             self:Close(sliderFrame.level)
  2213.             StartCounting(self, sliderFrame.level)
  2214.         end)
  2215.         editBox:SetAutoFocus(false)
  2216.     end
  2217.     sliderFrame.parent = parent
  2218.     sliderFrame.level = parent.level.num + 1
  2219.     sliderFrame.parentValue = parent.level.value
  2220.     sliderFrame:SetFrameLevel(parent.level:GetFrameLevel() + 3)
  2221.     sliderFrame.slider:SetFrameLevel(sliderFrame:GetFrameLevel() + 1)
  2222.     sliderFrame.currentText:SetFrameLevel(sliderFrame:GetFrameLevel() + 1)
  2223.     sliderFrame.currentText:ClearFocus()
  2224.     sliderFrame.changing = true
  2225.     if not parent.sliderMin or not parent.sliderMax then
  2226.         return
  2227.     end
  2228.  
  2229.     if parent.arrow then
  2230. --      parent.arrow:SetVertexColor(0.2, 0.6, 0)
  2231. --      parent.arrow:SetHeight(24)
  2232. --      parent.arrow:SetWidth(24)
  2233.         parent.selected = true
  2234.         parent.highlight:Show()
  2235.     end
  2236.  
  2237.     sliderFrame:SetClampedToScreen(false)
  2238.     if not parent.sliderValue then
  2239.         parent.sliderValue = (parent.sliderMin + parent.sliderMax) / 2
  2240.     end
  2241.     if parent.sliderMax <= parent.sliderMin then
  2242.         sliderFrame.slider:SetValue(0)
  2243.     else
  2244.         sliderFrame.slider:SetValue(1 - (parent.sliderValue - parent.sliderMin) / (parent.sliderMax - parent.sliderMin))
  2245.     end
  2246.     sliderFrame.changing = false
  2247.     sliderFrame.bottomText:SetText(parent.sliderMinText or "0")
  2248.     sliderFrame.topText:SetText(parent.sliderMaxText or "1")
  2249.     local text
  2250.     if parent.sliderFunc and not parent.fromAceOptions then
  2251.         text = parent.sliderFunc(getArgs(parent, 'sliderArg', 1, parent.sliderValue))
  2252.     end
  2253.     if type(text) == "number" or type(text) == "string" then
  2254.         sliderFrame.currentText:SetText(text)
  2255.     elseif parent.sliderIsPercent then
  2256.         sliderFrame.currentText:SetText(string.format("%.0f%%", parent.sliderValue * 100))
  2257.     else
  2258.         if parent.sliderStep < 0.1 then
  2259.             sliderFrame.currentText:SetText(string.format("%.2f", parent.sliderValue))
  2260.         elseif parent.sliderStep < 1 then
  2261.             sliderFrame.currentText:SetText(string.format("%.1f", parent.sliderValue))
  2262.         else
  2263.             sliderFrame.currentText:SetText(string.format("%.0f", parent.sliderValue))
  2264.         end
  2265.     end
  2266.  
  2267.  
  2268.     sliderFrame.lastValue = parent.sliderValue
  2269.  
  2270.     local level = parent.level
  2271.     sliderFrame:Show()
  2272.     sliderFrame:ClearAllPoints()
  2273.     if level.lastDirection == "RIGHT" then
  2274.         if level.lastVDirection == "DOWN" then
  2275.             sliderFrame:SetPoint("TOPLEFT", parent, "TOPRIGHT", 5, 10)
  2276.         else
  2277.             sliderFrame:SetPoint("BOTTOMLEFT", parent, "BOTTOMRIGHT", 5, -10)
  2278.         end
  2279.     else
  2280.         if level.lastVDirection == "DOWN" then
  2281.             sliderFrame:SetPoint("TOPRIGHT", parent, "TOPLEFT", -5, 10)
  2282.         else
  2283.             sliderFrame:SetPoint("BOTTOMRIGHT", parent, "BOTTOMLEFT", -5, -10)
  2284.         end
  2285.     end
  2286.     local dirty
  2287.     if level.lastDirection == "RIGHT" then
  2288.         if sliderFrame:GetRight() > GetScreenWidth() then
  2289.             level.lastDirection = "LEFT"
  2290.             dirty = true
  2291.         end
  2292.     elseif sliderFrame:GetLeft() < 0 then
  2293.         level.lastDirection = "RIGHT"
  2294.         dirty = true
  2295.     end
  2296.     if level.lastVDirection == "DOWN" then
  2297.         if sliderFrame:GetBottom() < 0 then
  2298.             level.lastVDirection = "UP"
  2299.             dirty = true
  2300.         end
  2301.     elseif sliderFrame:GetTop() > GetScreenWidth() then
  2302.         level.lastVDirection = "DOWN"
  2303.         dirty = true
  2304.     end
  2305.     if dirty then
  2306.         sliderFrame:ClearAllPoints()
  2307.         if level.lastDirection == "RIGHT" then
  2308.             if level.lastVDirection == "DOWN" then
  2309.                 sliderFrame:SetPoint("TOPLEFT", parent, "TOPRIGHT", 5, 10)
  2310.             else
  2311.                 sliderFrame:SetPoint("BOTTOMLEFT", parent, "BOTTOMRIGHT", 5, -10)
  2312.             end
  2313.         else
  2314.             if level.lastVDirection == "DOWN" then
  2315.                 sliderFrame:SetPoint("TOPRIGHT", parent, "TOPLEFT", -5, 10)
  2316.             else
  2317.                 sliderFrame:SetPoint("BOTTOMRIGHT", parent, "BOTTOMLEFT", -5, -10)
  2318.             end
  2319.         end
  2320.     end
  2321.     local left, bottom = sliderFrame:GetLeft(), sliderFrame:GetBottom()
  2322.     sliderFrame:ClearAllPoints()
  2323.     sliderFrame:SetPoint("BOTTOMLEFT", UIParent, "BOTTOMLEFT", left, bottom)
  2324.     if mod(level.num, 5) == 0 then
  2325.         local left, bottom = level:GetLeft(), level:GetBottom()
  2326.         level:ClearAllPoints()
  2327.         level:SetPoint("BOTTOMLEFT", UIParent, "BOTTOMLEFT", left, bottom)
  2328.     end
  2329.     sliderFrame:SetClampedToScreen(true)
  2330. end
  2331.  
  2332. function OpenEditBox(self, parent)
  2333.     if not editBoxFrame then
  2334.         editBoxFrame = CreateFrame("Frame", nil, nil)
  2335.         editBoxFrame:SetWidth(200)
  2336.         editBoxFrame:SetHeight(40)
  2337.         editBoxFrame:SetScale(UIParent:GetScale())
  2338.         editBoxFrame:SetBackdrop(tmp(
  2339.             'bgFile', "Interface\\Tooltips\\UI-Tooltip-Background",
  2340.             'edgeFile', "Interface\\Tooltips\\UI-Tooltip-Border",
  2341.             'tile', true,
  2342.             'insets', tmp2(
  2343.                 'left', 5,
  2344.                 'right', 5,
  2345.                 'top', 5,
  2346.                 'bottom', 5
  2347.             ),
  2348.             'tileSize', 16,
  2349.             'edgeSize', 16
  2350.         ))
  2351.         editBoxFrame:SetFrameStrata("FULLSCREEN_DIALOG")
  2352.         if editBoxFrame.SetTopLevel then
  2353.             editBoxFrame:SetTopLevel(true)
  2354.         end
  2355.         editBoxFrame:SetBackdropBorderColor(TOOLTIP_DEFAULT_COLOR.r, TOOLTIP_DEFAULT_COLOR.g, TOOLTIP_DEFAULT_COLOR.b)
  2356.         editBoxFrame:SetBackdropColor(TOOLTIP_DEFAULT_BACKGROUND_COLOR.r, TOOLTIP_DEFAULT_BACKGROUND_COLOR.g, TOOLTIP_DEFAULT_BACKGROUND_COLOR.b)
  2357.         editBoxFrame:EnableMouse(true)
  2358.         editBoxFrame:EnableMouseWheel(true)
  2359.         editBoxFrame:Hide()
  2360.         editBoxFrame:SetPoint("CENTER", UIParent, "CENTER")
  2361.  
  2362.         local editBox = CreateFrame("EditBox", nil, editBoxFrame)
  2363.         editBoxFrame.editBox = editBox
  2364.         editBox:SetFontObject(ChatFontNormal)
  2365.         editBox:SetWidth(160)
  2366.         editBox:SetHeight(13)
  2367.         editBox:SetPoint("CENTER", editBoxFrame, "CENTER", 0, 0)
  2368.  
  2369.         local left = editBox:CreateTexture(nil, "BACKGROUND")
  2370.         left:SetTexture("Interface\\ChatFrame\\UI-ChatInputBorder-Left")
  2371.         left:SetTexCoord(0, 100 / 256, 0, 1)
  2372.         left:SetWidth(100)
  2373.         left:SetHeight(32)
  2374.         left:SetPoint("LEFT", editBox, "LEFT", -10, 0)
  2375.         local right = editBox:CreateTexture(nil, "BACKGROUND")
  2376.         right:SetTexture("Interface\\ChatFrame\\UI-ChatInputBorder-Right")
  2377.         right:SetTexCoord(156/256, 1, 0, 1)
  2378.         right:SetWidth(100)
  2379.         right:SetHeight(32)
  2380.         right:SetPoint("RIGHT", editBox, "RIGHT", 10, 0)
  2381.  
  2382.         editBox:SetScript("OnEnterPressed", function()
  2383.             if editBoxFrame.parent and editBoxFrame.parent.editBoxValidateFunc then
  2384.                 local t = editBox.realText or editBox:GetText() or ""
  2385.                 local result = editBoxFrame.parent.editBoxValidateFunc(getArgs(editBoxFrame.parent, 'editBoxValidateArg', 1, t))
  2386.                 if not result then
  2387.                     UIErrorsFrame:AddMessage(VALIDATION_ERROR, 1, 0, 0)
  2388.                     return
  2389.                 end
  2390.             end
  2391.             if editBoxFrame.parent and editBoxFrame.parent.editBoxFunc then
  2392.                 local t
  2393.                 if editBox.realText ~= "NONE" then
  2394.                     t = editBox.realText or editBox:GetText() or ""
  2395.                 end
  2396.                 editBoxFrame.parent.editBoxFunc(getArgs(editBoxFrame.parent, 'editBoxArg', 1, t))
  2397.             end
  2398.             self:Close(editBoxFrame.level)
  2399.             for i = 1, editBoxFrame.level - 1 do
  2400.                 Refresh(self, levels[i])
  2401.             end
  2402.             StartCounting(self, editBoxFrame.level-1)
  2403.         end)
  2404.         editBox:SetScript("OnEscapePressed", function()
  2405.             self:Close(editBoxFrame.level)
  2406.             StartCounting(self, editBoxFrame.level-1)
  2407.         end)
  2408.         editBox:SetScript("OnReceiveDrag", function(this)
  2409.             if GetCursorInfo then
  2410.                 local type, alpha, bravo = GetCursorInfo()
  2411.                 local text
  2412.                 if type == "spell" then
  2413.                     text = GetSpellName(alpha, bravo)
  2414.                 elseif type == "item" then
  2415.                     text = bravo
  2416.                 end
  2417.                 if not text then
  2418.                     return
  2419.                 end
  2420.                 ClearCursor()
  2421.                 editBox:SetText(text)
  2422.             end
  2423.         end)
  2424.         local changing = false
  2425.         local skipNext = false
  2426.  
  2427.         function editBox:SpecialSetText(text)
  2428.             local oldText = editBox:GetText() or ""
  2429.             if not text then
  2430.                 text = ""
  2431.             end
  2432.             if text ~= oldText then
  2433.                 changing = true
  2434.                 self:SetText(tostring(text))
  2435.                 changing = false
  2436.                 skipNext = true
  2437.             end
  2438.         end
  2439.  
  2440.         editBox:SetScript("OnTextChanged", function()
  2441.             if skipNext then
  2442.                 skipNext = false
  2443.             elseif not changing and editBoxFrame.parent and editBoxFrame.parent.editBoxChangeFunc then
  2444.                 local t
  2445.                 if editBox.realText ~= "NONE" then
  2446.                     t = editBox.realText or editBox:GetText() or ""
  2447.                 end
  2448.                 local text = editBoxFrame.parent.editBoxChangeFunc(getArgs(editBoxFrame.parent, 'editBoxChangeArg', 1, t))
  2449.                 if text then
  2450.                     editBox:SpecialSetText(text)
  2451.                 end
  2452.             end
  2453.         end)
  2454.         editBoxFrame:SetScript("OnEnter", function()
  2455.             StopCounting(self, editBoxFrame.level)
  2456.             showGameTooltip(editBoxFrame.parent)
  2457.         end)
  2458.         editBoxFrame:SetScript("OnLeave", function()
  2459.             GameTooltip:Hide()
  2460.         end)
  2461.         editBox:SetScript("OnEnter", function()
  2462.             StopCounting(self, editBoxFrame.level)
  2463.             showGameTooltip(editBoxFrame.parent)
  2464.         end)
  2465.         editBox:SetScript("OnLeave", function()
  2466.             GameTooltip:Hide()
  2467.         end)
  2468.         editBoxFrame:SetScript("OnKeyDown", function(this, a1)
  2469.             if not editBox.keybinding then
  2470.                 return
  2471.             end
  2472.             local arg1 = a1 or arg1
  2473.             local screenshotKey = GetBindingKey("SCREENSHOT")
  2474.             if screenshotKey and arg1 == screenshotKey then
  2475.                 Screenshot()
  2476.                 return
  2477.             end
  2478.  
  2479.             if arg1 == "LeftButton" then
  2480.                 arg1 = "BUTTON1"
  2481.             elseif arg1 == "RightButton" then
  2482.                 arg1 = "BUTTON2"
  2483.             elseif arg1 == "MiddleButton" then
  2484.                 arg1 = "BUTTON3"
  2485.             elseif arg1 == "Button4" then
  2486.                 arg1 = "BUTTON4"
  2487.             elseif arg1 == "Button5" then
  2488.                 arg1 = "BUTTON5"
  2489.             end
  2490.             if arg1 == "UNKNOWN" then
  2491.                 return
  2492.             elseif arg1 == "SHIFT" or arg1 == "CTRL" or arg1 == "ALT" then
  2493.                 return
  2494.             elseif arg1 == "ENTER" then
  2495.                 if editBox.keybindingOnly and not editBox.keybindingOnly[editBox.realText] then
  2496.                     return editBox:GetScript("OnEscapePressed")()
  2497.                 elseif editBox.keybindingExcept and editBox.keybindingExcept[editBox.realText] then
  2498.                     return editBox:GetScript("OnEscapePressed")()
  2499.                 else
  2500.                     return editBox:GetScript("OnEnterPressed")()
  2501.                 end
  2502.             elseif arg1 == "ESCAPE" then
  2503.                 if editBox.realText == "NONE" then
  2504.                     return editBox:GetScript("OnEscapePressed")()
  2505.                 else
  2506.                     editBox:SpecialSetText(NONE or "NONE")
  2507.                     editBox.realText = "NONE"
  2508.                     return
  2509.                 end
  2510.             elseif editBox.keybindingOnly and not editBox.keybindingOnly[arg1] then
  2511.                 return
  2512.             elseif editBox.keybindingExcept and editBox.keybindingExcept[arg1] then
  2513.                 return
  2514.             end
  2515.             local s = GetBindingText(arg1, "KEY_")
  2516.             if s == "BUTTON1" then
  2517.                 s = KEY_BUTTON1
  2518.             elseif s == "BUTTON2" then
  2519.                 s = KEY_BUTTON2
  2520.             end
  2521.             local real = arg1
  2522.             if IsShiftKeyDown() then
  2523.                 s = "Shift-" .. s
  2524.                 real = "SHIFT-" .. real
  2525.             end
  2526.             if IsControlKeyDown() then
  2527.                 s = "Ctrl-" .. s
  2528.                 real = "CTRL-" .. real
  2529.             end
  2530.             if IsAltKeyDown() then
  2531.                 s = "Alt-" .. s
  2532.                 real = "ALT-" .. real
  2533.             end
  2534.             if editBox:GetText() ~= s then
  2535.                 editBox:SpecialSetText("-")
  2536.                 editBox:SpecialSetText(s)
  2537.                 editBox.realText = real
  2538.                 return editBox:GetScript("OnTextChanged")()
  2539.             end
  2540.         end)
  2541.         editBoxFrame:SetScript("OnMouseDown", editBoxFrame:GetScript("OnKeyDown"))
  2542.         editBox:SetScript("OnMouseDown", function(this, ...)
  2543.             if GetCursorInfo and (CursorHasItem() or CursorHasSpell()) then
  2544.                 return editBox:GetScript("OnReceiveDrag")(this, ...)
  2545.             end
  2546.             return editBoxFrame:GetScript("OnKeyDown")(this, ...)
  2547.         end)
  2548.         editBoxFrame:SetScript("OnMouseWheel", function(t, a1)
  2549.             local arg1 = a1 or arg1
  2550.             local up = arg1 > 0
  2551.             arg1 = up and "MOUSEWHEELUP" or "MOUSEWHEELDOWN"
  2552.             return editBoxFrame:GetScript("OnKeyDown")(t or this, arg1)
  2553.         end)
  2554.         editBox:SetScript("OnMouseWheel", editBoxFrame:GetScript("OnMouseWheel"))
  2555.     end
  2556.     editBoxFrame.parent = parent
  2557.     editBoxFrame.level = parent.level.num + 1
  2558.     editBoxFrame.parentValue = parent.level.value
  2559.     editBoxFrame:SetFrameLevel(parent.level:GetFrameLevel() + 3)
  2560.     editBoxFrame.editBox:SetFrameLevel(editBoxFrame:GetFrameLevel() + 1)
  2561.     editBoxFrame.editBox.realText = nil
  2562.     editBoxFrame:SetClampedToScreen(false)
  2563.  
  2564.     editBoxFrame.editBox:SpecialSetText("")
  2565.     if parent.editBoxIsKeybinding then
  2566.         local s = parent.editBoxText
  2567.         if s == "" then
  2568.             s =  "NONE"
  2569.         end
  2570.         editBoxFrame.editBox.realText = s
  2571.         if s and s ~= "NONE" then
  2572.             local alpha,bravo = s:match("^(.+)%-(.+)$")
  2573.             if not bravo then
  2574.                 alpha = nil
  2575.                 bravo = s
  2576.             end
  2577.             bravo = GetBindingText(bravo, "KEY_")
  2578.             if alpha then
  2579.                 editBoxFrame.editBox:SpecialSetText(alpha:upper() .. "-" .. bravo)
  2580.             else
  2581.                 editBoxFrame.editBox:SpecialSetText(bravo)
  2582.             end
  2583.         else
  2584.             editBoxFrame.editBox:SpecialSetText(NONE or "NONE")
  2585.         end
  2586.     else
  2587.         editBoxFrame.editBox:SpecialSetText(parent.editBoxText)
  2588.     end
  2589.  
  2590.     editBoxFrame.editBox.keybinding = parent.editBoxIsKeybinding
  2591.     editBoxFrame.editBox.keybindingOnly = parent.editBoxKeybindingOnly
  2592.     editBoxFrame.editBox.keybindingExcept = parent.editBoxKeybindingExcept
  2593.     editBoxFrame.editBox:EnableKeyboard(not parent.editBoxIsKeybinding)
  2594.     editBoxFrame:EnableKeyboard(parent.editBoxIsKeybinding)
  2595.  
  2596.     if parent.arrow then
  2597. --      parent.arrow:SetVertexColor(0.2, 0.6, 0)
  2598. --      parent.arrow:SetHeight(24)
  2599. --      parent.arrow:SetWidth(24)
  2600.         parent.selected = true
  2601.         parent.highlight:Show()
  2602.     end
  2603.  
  2604.     local level = parent.level
  2605.     editBoxFrame:Show()
  2606.     editBoxFrame:ClearAllPoints()
  2607.     if level.lastDirection == "RIGHT" then
  2608.         if level.lastVDirection == "DOWN" then
  2609.             editBoxFrame:SetPoint("TOPLEFT", parent, "TOPRIGHT", 5, 10)
  2610.         else
  2611.             editBoxFrame:SetPoint("BOTTOMLEFT", parent, "BOTTOMRIGHT", 5, -10)
  2612.         end
  2613.     else
  2614.         if level.lastVDirection == "DOWN" then
  2615.             editBoxFrame:SetPoint("TOPRIGHT", parent, "TOPLEFT", -5, 10)
  2616.         else
  2617.             editBoxFrame:SetPoint("BOTTOMRIGHT", parent, "BOTTOMLEFT", -5, -10)
  2618.         end
  2619.     end
  2620.     local dirty
  2621.     if level.lastDirection == "RIGHT" then
  2622.         if editBoxFrame:GetRight() > GetScreenWidth() then
  2623.             level.lastDirection = "LEFT"
  2624.             dirty = true
  2625.         end
  2626.     elseif editBoxFrame:GetLeft() < 0 then
  2627.         level.lastDirection = "RIGHT"
  2628.         dirty = true
  2629.     end
  2630.     if level.lastVDirection == "DOWN" then
  2631.         if editBoxFrame:GetBottom() < 0 then
  2632.             level.lastVDirection = "UP"
  2633.             dirty = true
  2634.         end
  2635.     elseif editBoxFrame:GetTop() > GetScreenWidth() then
  2636.         level.lastVDirection = "DOWN"
  2637.         dirty = true
  2638.     end
  2639.     if dirty then
  2640.         editBoxFrame:ClearAllPoints()
  2641.         if level.lastDirection == "RIGHT" then
  2642.             if level.lastVDirection == "DOWN" then
  2643.                 editBoxFrame:SetPoint("TOPLEFT", parent, "TOPRIGHT", 5, 10)
  2644.             else
  2645.                 editBoxFrame:SetPoint("BOTTOMLEFT", parent, "BOTTOMRIGHT", 5, -10)
  2646.             end
  2647.         else
  2648.             if level.lastVDirection == "DOWN" then
  2649.                 editBoxFrame:SetPoint("TOPRIGHT", parent, "TOPLEFT", -5, 10)
  2650.             else
  2651.                 editBoxFrame:SetPoint("BOTTOMRIGHT", parent, "BOTTOMLEFT", -5, -10)
  2652.             end
  2653.         end
  2654.     end
  2655.     local left, bottom = editBoxFrame:GetLeft(), editBoxFrame:GetBottom()
  2656.     editBoxFrame:ClearAllPoints()
  2657.     editBoxFrame:SetPoint("BOTTOMLEFT", UIParent, "BOTTOMLEFT", left, bottom)
  2658.     if mod(level.num, 5) == 0 then
  2659.         local left, bottom = level:GetLeft(), level:GetBottom()
  2660.         level:ClearAllPoints()
  2661.         level:SetPoint("BOTTOMLEFT", UIParent, "BOTTOMLEFT", left, bottom)
  2662.     end
  2663.     editBoxFrame:SetClampedToScreen(true)
  2664. end
  2665.  
  2666. function Dewdrop:EncodeKeybinding(text)
  2667.     if text == nil or text == "NONE" then
  2668.         return nil
  2669.     end
  2670.     text = tostring(text):upper()
  2671.     local shift, ctrl, alt
  2672.     local modifier
  2673.     while true do
  2674.         if text == "-" then
  2675.             break
  2676.         end
  2677.         modifier, text = strsplit('-', text, 2)
  2678.         if text then
  2679.             if modifier ~= "SHIFT" and modifier ~= "CTRL" and modifier ~= "ALT" then
  2680.                 return false
  2681.             end
  2682.             if modifier == "SHIFT" then
  2683.                 if shift then
  2684.                     return false
  2685.                 end
  2686.                 shift = true
  2687.             end
  2688.             if modifier == "CTRL" then
  2689.                 if ctrl then
  2690.                     return false
  2691.                 end
  2692.                 ctrl = true
  2693.             end
  2694.             if modifier == "ALT" then
  2695.                 if alt then
  2696.                     return false
  2697.                 end
  2698.                 alt = true
  2699.             end
  2700.         else
  2701.             text = modifier
  2702.             break
  2703.         end
  2704.     end
  2705.     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
  2706.         return false
  2707.     end
  2708.     local s = GetBindingText(text, "KEY_")
  2709.     if s == "BUTTON1" then
  2710.         s = KEY_BUTTON1
  2711.     elseif s == "BUTTON2" then
  2712.         s = KEY_BUTTON2
  2713.     end
  2714.     if shift then
  2715.         s = "Shift-" .. s
  2716.     end
  2717.     if ctrl then
  2718.         s = "Ctrl-" .. s
  2719.     end
  2720.     if alt then
  2721.         s = "Alt-" .. s
  2722.     end
  2723.     return s
  2724. end
  2725.  
  2726. function Dewdrop:IsOpen(parent)
  2727.     self:argCheck(parent, 2, "table", "string", "nil")
  2728.     return levels[1] and levels[1]:IsShown() and (not parent or parent == levels[1].parent or parent == levels[1]:GetParent())
  2729. end
  2730.  
  2731. function Dewdrop:GetOpenedParent()
  2732.     return (levels[1] and levels[1]:IsShown()) and (levels[1].parent or levels[1]:GetParent())
  2733. end
  2734.  
  2735. function Open(self, parent, func, level, value, point, relativePoint, cursorX, cursorY)
  2736.     self:Close(level)
  2737.     if DewdropLib then
  2738.         local d = DewdropLib:GetInstance('1.0')
  2739.         local ret, val = pcall(d, IsOpen, d)
  2740.         if ret and val then
  2741.             DewdropLib:GetInstance('1.0'):Close()
  2742.         end
  2743.     end
  2744.     if type(parent) == "table" then
  2745.         parent:GetCenter()
  2746.     end
  2747.     local frame = AcquireLevel(self, level)
  2748.     if level == 1 then
  2749.         frame.lastDirection = "RIGHT"
  2750.         frame.lastVDirection = "DOWN"
  2751.     else
  2752.         frame.lastDirection = levels[level - 1].lastDirection
  2753.         frame.lastVDirection = levels[level - 1].lastVDirection
  2754.     end
  2755.     frame:SetFrameStrata("FULLSCREEN_DIALOG")
  2756.     frame:ClearAllPoints()
  2757.     frame.parent = parent
  2758.     frame:SetPoint("LEFT", UIParent, "RIGHT", 10000, 0)
  2759.     frame:Show()
  2760.     if level == 1 then
  2761.         baseFunc = func
  2762.     end
  2763.     levels[level].value = value
  2764. --  levels[level].parentText = parent.text and parent.text:GetText() or nil
  2765. --  levels[level].parentTooltipTitle = parent.tooltipTitle
  2766. --  levels[level].parentTooltipText = parent.tooltipText
  2767. --  levels[level].parentTooltipFunc = parent.tooltipFunc
  2768.     if type(parent) == "table" and parent.arrow then
  2769. --      parent.arrow:SetVertexColor(0.2, 0.6, 0)
  2770. --      parent.arrow:SetHeight(24)
  2771. --      parent.arrow:SetWidth(24)
  2772.         parent.selected = true
  2773.         parent.highlight:Show()
  2774.     end
  2775.     relativePoint = relativePoint or point
  2776.     Refresh(self, levels[level])
  2777.     if point or (cursorX and cursorY) then
  2778.         frame:ClearAllPoints()
  2779.         if cursorX and cursorY then
  2780.             local curX, curY = GetScaledCursorPosition()
  2781.             if curY < GetScreenHeight() / 2 then
  2782.                 point, relativePoint = "BOTTOM", "BOTTOM"
  2783.             else
  2784.                 point, relativePoint = "TOP", "TOP"
  2785.             end
  2786.             if curX < GetScreenWidth() / 2 then
  2787.                 point, relativePoint = point .. "LEFT", relativePoint .. "RIGHT"
  2788.             else
  2789.                 point, relativePoint = point .. "RIGHT", relativePoint .. "LEFT"
  2790.             end
  2791.         end
  2792.         frame:SetPoint(point, type(parent) == "table" and parent or UIParent, relativePoint)
  2793.         if cursorX and cursorY then
  2794.             local left = frame:GetLeft()
  2795.             local width = frame:GetWidth()
  2796.             local bottom = frame:GetBottom()
  2797.             local height = frame:GetHeight()
  2798.             local curX, curY = GetScaledCursorPosition()
  2799.             frame:ClearAllPoints()
  2800.             relativePoint = relativePoint or point
  2801.             if point == "BOTTOM" or point == "TOP" then
  2802.                 if curX < GetScreenWidth() / 2 then
  2803.                     point = point .. "LEFT"
  2804.                 else
  2805.                     point = point .. "RIGHT"
  2806.                 end
  2807.             elseif point == "CENTER" then
  2808.                 if curX < GetScreenWidth() / 2 then
  2809.                     point = "LEFT"
  2810.                 else
  2811.                     point = "RIGHT"
  2812.                 end
  2813.             end
  2814.             local xOffset, yOffset = 0, 0
  2815.             if curY > GetScreenHeight() / 2 then
  2816.                 yOffset = -height
  2817.             end
  2818.             if curX > GetScreenWidth() / 2 then
  2819.                 xOffset = -width
  2820.             end
  2821.             frame:SetPoint(point, type(parent) == "table" and parent or UIParent, relativePoint, curX - left + xOffset, curY - bottom + yOffset)
  2822.             if level == 1 then
  2823.                 frame.lastDirection = "RIGHT"
  2824.             end
  2825.         elseif cursorX then
  2826.             local left = frame:GetLeft()
  2827.             local width = frame:GetWidth()
  2828.             local curX, curY = GetScaledCursorPosition()
  2829.             frame:ClearAllPoints()
  2830.             relativePoint = relativePoint or point
  2831.             if point == "BOTTOM" or point == "TOP" then
  2832.                 if curX < GetScreenWidth() / 2 then
  2833.                     point = point .. "LEFT"
  2834.                 else
  2835.                     point = point .. "RIGHT"
  2836.                 end
  2837.             elseif point == "CENTER" then
  2838.                 if curX < GetScreenWidth() / 2 then
  2839.                     point = "LEFT"
  2840.                 else
  2841.                     point = "RIGHT"
  2842.                 end
  2843.             end
  2844.             frame:SetPoint(point, type(parent) == "table" and parent or UIParent, relativePoint, curX - left - width / 2, 0)
  2845.             if level == 1 then
  2846.                 frame.lastDirection = "RIGHT"
  2847.             end
  2848.         elseif cursorY then
  2849.             local bottom = frame:GetBottom()
  2850.             local height = frame:GetHeight()
  2851.             local curX, curY = GetScaledCursorPosition()
  2852.             frame:ClearAllPoints()
  2853.             relativePoint = relativePoint or point
  2854.             if point == "LEFT" or point == "RIGHT" then
  2855.                 if curX < GetScreenHeight() / 2 then
  2856.                     point = point .. "BOTTOM"
  2857.                 else
  2858.                     point = point .. "TOP"
  2859.                 end
  2860.             elseif point == "CENTER" then
  2861.                 if curX < GetScreenHeight() / 2 then
  2862.                     point = "BOTTOM"
  2863.                 else
  2864.                     point = "TOP"
  2865.                 end
  2866.             end
  2867.             frame:SetPoint(point, type(parent) == "table" and parent or UIParent, relativePoint, 0, curY - bottom - height / 2)
  2868.             if level == 1 then
  2869.                 frame.lastDirection = "DOWN"
  2870.             end
  2871.         end
  2872.         if (strsub(point, 1, 3) ~= strsub(relativePoint, 1, 3)) then
  2873.             if frame:GetBottom() < 0 then
  2874.                 local point, parent, relativePoint, x, y = frame:GetPoint(1)
  2875.                 local change = GetScreenHeight() - frame:GetTop()
  2876.                 local otherChange = -frame:GetBottom()
  2877.                 if otherChange < change then
  2878.                     change = otherChange
  2879.                 end
  2880.                 frame:SetPoint(point, parent, relativePoint, x, y + change)
  2881.             elseif frame:GetTop() > GetScreenHeight() then
  2882.                 local point, parent, relativePoint, x, y = frame:GetPoint(1)
  2883.                 local change = GetScreenHeight() - frame:GetTop()
  2884.                 local otherChange = -frame:GetBottom()
  2885.                 if otherChange < change then
  2886.                     change = otherChange
  2887.                 end
  2888.                 frame:SetPoint(point, parent, relativePoint, x, y + change)
  2889.             end
  2890.         end
  2891.     end
  2892.     CheckDualMonitor(self, frame)
  2893.     frame:SetClampedToScreen(true)
  2894.     frame:SetClampedToScreen(false)
  2895.     StartCounting(self, level)
  2896. end
  2897.  
  2898. function Dewdrop:IsRegistered(parent)
  2899.     self:argCheck(parent, 2, "table", "string")
  2900.     return not not self.registry[parent]
  2901. end
  2902.  
  2903. function Dewdrop:Register(parent, ...)
  2904.     self:argCheck(parent, 2, "table", "string")
  2905.     if self.registry[parent] then
  2906.         self:Unregister(parent)
  2907.     end
  2908.     local info = new(...)
  2909.     if type(info.children) == "table" then
  2910.         local err, position = validateOptions(info.children)
  2911.  
  2912.         if err then
  2913.             if position then
  2914.                 Dewdrop:error(position .. ": " .. err)
  2915.             else
  2916.                 Dewdrop:error(err)
  2917.             end
  2918.         end
  2919.     end
  2920.     self.registry[parent] = info
  2921.     if not info.dontHook and not self.onceRegistered[parent] and type(parent) == "table" then
  2922.         if parent:HasScript("OnMouseUp") then
  2923.             local script = parent:GetScript("OnMouseUp")
  2924.             parent:SetScript("OnMouseUp", function(this, ...)
  2925.                 local arg1 = ...
  2926.                 if script then
  2927.                     script(this, ...)
  2928.                 end
  2929.                 if arg1 == "RightButton" and self.registry[parent] then
  2930.                     if self:IsOpen(parent) then
  2931.                         self:Close()
  2932.                     else
  2933.                         self:Open(parent)
  2934.                     end
  2935.                 end
  2936.             end)
  2937.         end
  2938.         if parent:HasScript("OnMouseDown") then
  2939.             local script = parent:GetScript("OnMouseDown")
  2940.             parent:SetScript("OnMouseDown", function(this, ...)
  2941.                 if script then
  2942.                     script(this, ...)
  2943.                 end
  2944.                 if self.registry[parent] then
  2945.                     self:Close()
  2946.                 end
  2947.             end)
  2948.         end
  2949.     end
  2950.     self.onceRegistered[parent] = true
  2951. end
  2952.  
  2953. function Dewdrop:Unregister(parent)
  2954.     self:argCheck(parent, 2, "table", "string")
  2955.     self.registry[parent] = nil
  2956. end
  2957.  
  2958. function Dewdrop:Open(parent, ...)
  2959.     self:argCheck(parent, 2, "table", "string")
  2960.     local info
  2961.     local k1 = ...
  2962.     if type(k1) == "table" and k1[0] and k1.IsObjectType and self.registry[k1] then
  2963.         info = tmp(select(2, ...))
  2964.         for k,v in pairs(self.registry[k1]) do
  2965.             if info[k] == nil then
  2966.                 info[k] = v
  2967.             end
  2968.         end
  2969.     else
  2970.         info = tmp(...)
  2971.         if self.registry[parent] then
  2972.             for k,v in pairs(self.registry[parent]) do
  2973.                 if info[k] == nil then
  2974.                     info[k] = v
  2975.                 end
  2976.             end
  2977.         end
  2978.     end
  2979.     local point = info.point
  2980.     local relativePoint = info.relativePoint
  2981.     local cursorX = info.cursorX
  2982.     local cursorY = info.cursorY
  2983.     if type(point) == "function" then
  2984.         local b
  2985.         point, b = point(parent)
  2986.         if b then
  2987.             relativePoint = b
  2988.         end
  2989.     end
  2990.     if type(relativePoint) == "function" then
  2991.         relativePoint = relativePoint(parent)
  2992.     end
  2993.     Open(self, parent, info.children, 1, nil, point, relativePoint, cursorX, cursorY)
  2994. end
  2995.  
  2996. function Clear(self, level)
  2997.     if level then
  2998.         if level.buttons then
  2999.             for i = #level.buttons, 1, -1 do
  3000.                 ReleaseButton(self, level, i)
  3001.             end
  3002.         end
  3003.     end
  3004. end
  3005.  
  3006. function Dewdrop:Close(level)
  3007.     if DropDownList1:IsShown() then
  3008.         DropDownList1:Hide()
  3009.     end
  3010.     if DewdropLib then
  3011.         local d = DewdropLib:GetInstance('1.0')
  3012.         local ret, val = pcall(d, IsOpen, d)
  3013.         if ret and val then
  3014.             DewdropLib:GetInstance('1.0'):Close()
  3015.         end
  3016.     end
  3017.     self:argCheck(level, 2, "number", "nil")
  3018.     if not level then
  3019.         level = 1
  3020.     end
  3021.     if level == 1 and levels[level] then
  3022.         levels[level].parented = false
  3023.     end
  3024.     if level > 1 and levels[level-1].buttons then
  3025.         local buttons = levels[level-1].buttons
  3026.         for _,button in ipairs(buttons) do
  3027. --          button.arrow:SetWidth(16)
  3028. --          button.arrow:SetHeight(16)
  3029.             button.selected = nil
  3030.             button.highlight:Hide()
  3031. --          button.arrow:SetVertexColor(1, 1, 1)
  3032.         end
  3033.     end
  3034.     if sliderFrame and sliderFrame.level >= level then
  3035.         sliderFrame:Hide()
  3036.     end
  3037.     if editBoxFrame and editBoxFrame.level >= level then
  3038.         editBoxFrame:Hide()
  3039.     end
  3040.     for i = level, #levels do
  3041.         Clear(self, levels[level])
  3042.         levels[i]:Hide()
  3043.         levels[i]:ClearAllPoints()
  3044.         levels[i]:SetPoint("CENTER", UIParent, "CENTER")
  3045.         levels[i].value = nil
  3046.     end
  3047. end
  3048.  
  3049. function Dewdrop:AddSeparator(level)
  3050.     level = levels[level or currentLevel]
  3051.     if not level or not level.buttons then return; end
  3052.  
  3053.     local prevbutton = level.buttons[#level.buttons]
  3054.     if not prevbutton then return; end
  3055.  
  3056.     if prevbutton.disabled and prevbutton.text:GetText() == "" then
  3057.         return
  3058.     end
  3059.     self:AddLine("text", "", "disabled", true)
  3060. end
  3061.  
  3062. function Dewdrop:AddLine(...)
  3063.     local info = tmp(...)
  3064.     local level = info.level or currentLevel
  3065.     info.level = nil
  3066.     local button = AcquireButton(self, level)
  3067.     if not next(info) then
  3068.         info.disabled = true
  3069.     end
  3070.     button.disabled = info.isTitle or info.notClickable or info.disabled or (self.combat and info.secure)
  3071.     button.isTitle = info.isTitle
  3072.     button.notClickable = info.notClickable
  3073.     if button.isTitle then
  3074.         button.text:SetFontObject(GameFontNormalSmall)
  3075.     elseif button.notClickable then
  3076.         button.text:SetFontObject(GameFontHighlightSmall)
  3077.     elseif button.disabled then
  3078.         button.text:SetFontObject(GameFontDisableSmall)
  3079.     else
  3080.         button.text:SetFontObject(GameFontHighlightSmall)
  3081.     end
  3082.     if info.disabled then
  3083.         button.arrow:SetDesaturated(true)
  3084.         button.check:SetDesaturated(true)
  3085.     else
  3086.         button.arrow:SetDesaturated(false)
  3087.         button.check:SetDesaturated(false)
  3088.     end
  3089.     if info.textR and info.textG and info.textB then
  3090.         button.textR = info.textR
  3091.         button.textG = info.textG
  3092.         button.textB = info.textB
  3093.         button.text:SetTextColor(button.textR, button.textG, button.textB)
  3094.     else
  3095.         button.text:SetTextColor(button.text:GetFontObject():GetTextColor())
  3096.     end
  3097.     button.notCheckable = info.notCheckable
  3098.     button.text:SetPoint("LEFT", button, "LEFT", button.notCheckable and 0 or 24, 0)
  3099.     button.checked = not info.notCheckable and info.checked
  3100.     button.mouseoverUnderline = info.mouseoverUnderline
  3101.     button.isRadio = not info.notCheckable and info.isRadio
  3102.     if info.isRadio then
  3103.         button.check:Show()
  3104.         button.check:SetTexture(info.checkIcon or "Interface\\Buttons\\UI-RadioButton")
  3105.         if button.checked then
  3106.             button.check:SetTexCoord(0.25, 0.5, 0, 1)
  3107.             button.check:SetVertexColor(1, 1, 1, 1)
  3108.         else
  3109.             button.check:SetTexCoord(0, 0.25, 0, 1)
  3110.             button.check:SetVertexColor(1, 1, 1, 0.5)
  3111.         end
  3112.         button.radioHighlight:SetTexture(info.checkIcon or "Interface\\Buttons\\UI-RadioButton")
  3113.         button.check:SetWidth(16)
  3114.         button.check:SetHeight(16)
  3115.     elseif info.icon then
  3116.         button.check:Show()
  3117.         button.check:SetTexture(info.icon)
  3118.         if info.iconWidth and info.iconHeight then
  3119.             button.check:SetWidth(info.iconWidth)
  3120.             button.check:SetHeight(info.iconHeight)
  3121.         else
  3122.             button.check:SetWidth(16)
  3123.             button.check:SetHeight(16)
  3124.         end
  3125.         if info.iconCoordLeft and info.iconCoordRight and info.iconCoordTop and info.iconCoordBottom then
  3126.             button.check:SetTexCoord(info.iconCoordLeft, info.iconCoordRight, info.iconCoordTop, info.iconCoordBottom)
  3127.         elseif info.icon:find("^Interface\\Icons\\") then
  3128.             button.check:SetTexCoord(0.05, 0.95, 0.05, 0.95)
  3129.         else
  3130.             button.check:SetTexCoord(0, 1, 0, 1)
  3131.         end
  3132.         button.check:SetVertexColor(1, 1, 1, 1)
  3133.     else
  3134.         if button.checked then
  3135.             if info.checkIcon then
  3136.                 button.check:SetWidth(16)
  3137.                 button.check:SetHeight(16)
  3138.                 button.check:SetTexture(info.checkIcon)
  3139.                 if info.checkIcon:find("^Interface\\Icons\\") then
  3140.                     button.check:SetTexCoord(0.05, 0.95, 0.05, 0.95)
  3141.                 else
  3142.                     button.check:SetTexCoord(0, 1, 0, 1)
  3143.                 end
  3144.             else
  3145.                 button.check:SetWidth(24)
  3146.                 button.check:SetHeight(24)
  3147.                 button.check:SetTexture("Interface\\Buttons\\UI-CheckBox-Check")
  3148.                 button.check:SetTexCoord(0, 1, 0, 1)
  3149.             end
  3150.             button.check:SetVertexColor(1, 1, 1, 1)
  3151.         else
  3152.             button.check:SetVertexColor(1, 1, 1, 0)
  3153.         end
  3154.     end
  3155.     if not button.disabled then
  3156.         button.func = info.func
  3157.         button.secure = info.secure
  3158.     end
  3159.     button.hasColorSwatch = info.hasColorSwatch
  3160.     if button.hasColorSwatch then
  3161.         button.colorSwatch:Show()
  3162.         button.colorSwatch.texture:Show()
  3163.         button.r = info.r or 1
  3164.         button.g = info.g or 1
  3165.         button.b = info.b or 1
  3166.         button.colorSwatch.texture:SetVertexColor(button.r, button.g, button.b)
  3167.         button.checked = false
  3168.         button.func = nil
  3169.         button.colorFunc = info.colorFunc
  3170.         local i = 1
  3171.         while true do
  3172.             local k = "colorArg" .. i
  3173.             local x = info[k]
  3174.             if x == nil then
  3175.                 break
  3176.             end
  3177.             button[k] = x
  3178.             i = i + 1
  3179.         end
  3180.         button.hasOpacity = info.hasOpacity
  3181.         button.opacity = info.opacity or 1
  3182.     else
  3183.         button.colorSwatch:Hide()
  3184.         button.colorSwatch.texture:Hide()
  3185.     end
  3186.     button.hasArrow = not button.hasColorSwatch and (info.value or info.hasSlider or info.hasEditBox) and info.hasArrow
  3187.     if button.hasArrow then
  3188.         button.arrow:SetAlpha(1)
  3189.         if info.hasSlider then
  3190.             button.hasSlider = true
  3191.             button.sliderMin = info.sliderMin or 0
  3192.             button.sliderMax = info.sliderMax or 1
  3193.             button.sliderStep = info.sliderStep or 0
  3194.             button.sliderBigStep = info.sliderBigStep or button.sliderStep
  3195.             if button.sliderBigStep < button.sliderStep then
  3196.                 button.sliderBigStep = button.sliderStep
  3197.             end
  3198.             button.sliderIsPercent = info.sliderIsPercent and true or false
  3199.             button.sliderMinText = info.sliderMinText or button.sliderIsPercent and string.format("%.0f%%", button.sliderMin * 100) or button.sliderMin
  3200.             button.sliderMaxText = info.sliderMaxText or button.sliderIsPercent and string.format("%.0f%%", button.sliderMax * 100) or button.sliderMax
  3201.             button.sliderFunc = info.sliderFunc
  3202.             button.sliderValue = info.sliderValue
  3203.             button.fromAceOptions = info.fromAceOptions
  3204.             local i = 1
  3205.             while true do
  3206.                 local k = "sliderArg" .. i
  3207.                 local x = info[k]
  3208.                 if x == nil then
  3209.                     break
  3210.                 end
  3211.                 button[k] = x
  3212.                 i = i + 1
  3213.             end
  3214.         elseif info.hasEditBox then
  3215.             button.hasEditBox = true
  3216.             button.editBoxText = info.editBoxText or ""
  3217.             button.editBoxFunc = info.editBoxFunc
  3218.             local i = 1
  3219.             while true do
  3220.                 local k = "editBoxArg" .. i
  3221.                 local x = info[k]
  3222.                 if x == nil then
  3223.                     break
  3224.                 end
  3225.                 button[k] = x
  3226.                 i = i + 1
  3227.             end
  3228.             button.editBoxChangeFunc = info.editBoxChangeFunc
  3229.             local i = 1
  3230.             while true do
  3231.                 local k = "editBoxChangeArg" .. i
  3232.                 local x = info[k]
  3233.                 if x == nil then
  3234.                     break
  3235.                 end
  3236.                 button[k] = x
  3237.                 i = i + 1
  3238.             end
  3239.             button.editBoxValidateFunc = info.editBoxValidateFunc
  3240.             local i = 1
  3241.             while true do
  3242.                 local k = "editBoxValidateArg" .. i
  3243.                 local x = info[k]
  3244.                 if x == nil then
  3245.                     break
  3246.                 end
  3247.                 button[k] = x
  3248.                 i = i + 1
  3249.             end
  3250.             button.editBoxIsKeybinding = info.editBoxIsKeybinding
  3251.             button.editBoxKeybindingOnly = info.editBoxKeybindingOnly
  3252.             button.editBoxKeybindingExcept = info.editBoxKeybindingExcept
  3253.         else
  3254.             button.value = info.value
  3255.             local l = levels[level+1]
  3256.             if l and info.value == l.value then
  3257. --              button.arrow:SetWidth(24)
  3258. --              button.arrow:SetHeight(24)
  3259.                 button.selected = true
  3260.                 button.highlight:Show()
  3261.             end
  3262.         end
  3263.     else
  3264.         button.arrow:SetAlpha(0)
  3265.     end
  3266.     local i = 1
  3267.     while true do
  3268.         local k = "arg" .. i
  3269.         local x = info[k]
  3270.         if x == nil then
  3271.             break
  3272.         end
  3273.         button[k] = x
  3274.         i = i + 1
  3275.     end
  3276.     button.closeWhenClicked = info.closeWhenClicked
  3277.     button.textHeight = info.textHeight or UIDROPDOWNMENU_DEFAULT_TEXT_HEIGHT or 10
  3278.     local font,_ = button.text:GetFont()
  3279.     button.text:SetFont(STANDARD_TEXT_FONT or "Fonts\\FRIZQT__.TTF", button.textHeight)
  3280.     button:SetHeight(button.textHeight + 6)
  3281.     button.text:SetPoint("RIGHT", button.arrow, (button.hasColorSwatch or button.hasArrow) and "LEFT" or "RIGHT")
  3282.     button.text:SetJustifyH(info.justifyH or "LEFT")
  3283.     button.text:SetText(info.text)
  3284.     button.tooltipTitle = info.tooltipTitle
  3285.     button.tooltipText = info.tooltipText
  3286.     button.tooltipFunc = info.tooltipFunc
  3287.     local i = 1
  3288.     while true do
  3289.         local k = "tooltipArg" .. i
  3290.         local x = info[k]
  3291.         if x == nil then
  3292.             break
  3293.         end
  3294.         button[k] = x
  3295.         i = i + 1
  3296.     end
  3297.     if not button.tooltipTitle and not button.tooltipText and not button.tooltipFunc and not info.isTitle then
  3298.         button.tooltipTitle = info.text
  3299.     end
  3300.     if type(button.func) == "string" then
  3301.         if type(button.arg1) ~= "table" then
  3302.             self:error("Cannot call method %q on a non-table", button.func)
  3303.         end
  3304.         if type(button.arg1[button.func]) ~= "function" then
  3305.             self:error("Method %q nonexistant.", button.func)
  3306.         end
  3307.     end
  3308. end
  3309.  
  3310. function Dewdrop:InjectAceOptionsTable(handler, options)
  3311.     self:argCheck(handler, 2, "table")
  3312.     self:argCheck(options, 3, "table")
  3313.     if tostring(options.type):lower() ~= "group" then
  3314.         self:error('Cannot inject into options table argument #3 if its type is not "group"')
  3315.     end
  3316.     if options.handler ~= nil and options.handler ~= handler then
  3317.         self:error("Cannot inject into options table argument #3 if it has a different handler than argument #2")
  3318.     end
  3319.     options.handler = handler
  3320.     local class = handler.class
  3321.     if not AceLibrary:HasInstance("AceOO-2.0") or not class then
  3322.         if Rock then
  3323.             -- possible Rock object
  3324.             for mixin in Rock:IterateObjectMixins(handler) do
  3325.                 if type(mixin.GetAceOptionsDataTable) == "function" then
  3326.                     local t = mixin:GetAceOptionsDataTable(handler)
  3327.                     for k,v in pairs(t) do
  3328.                         if type(options.args) ~= "table" then
  3329.                             options.args = {}
  3330.                         end
  3331.                         if options.args[k] == nil then
  3332.                             options.args[k] = v
  3333.                         end
  3334.                     end
  3335.                 end
  3336.             end
  3337.         end
  3338.     else
  3339.         -- Ace2 object
  3340.         while class and class ~= AceLibrary("AceOO-2.0").Class do
  3341.             if type(class.GetAceOptionsDataTable) == "function" then
  3342.                 local t = class:GetAceOptionsDataTable(handler)
  3343.                 for k,v in pairs(t) do
  3344.                     if type(options.args) ~= "table" then
  3345.                         options.args = {}
  3346.                     end
  3347.                     if options.args[k] == nil then
  3348.                         options.args[k] = v
  3349.                     end
  3350.                 end
  3351.             end
  3352.             local mixins = class.mixins
  3353.             if mixins then
  3354.                 for mixin in pairs(mixins) do
  3355.                     if type(mixin.GetAceOptionsDataTable) == "function" then
  3356.                         local t = mixin:GetAceOptionsDataTable(handler)
  3357.                         for k,v in pairs(t) do
  3358.                             if type(options.args) ~= "table" then
  3359.                                 options.args = {}
  3360.                             end
  3361.                             if options.args[k] == nil then
  3362.                                 options.args[k] = v
  3363.                             end
  3364.                         end
  3365.                     end
  3366.                 end
  3367.             end
  3368.             class = class.super
  3369.         end
  3370.     end
  3371.     return options
  3372. end
  3373.  
  3374. function Dewdrop:OnTooltipHide()
  3375.     if lastSetFont then
  3376.         if lastSetFont == normalFont then
  3377.             lastSetFont = nil
  3378.             return
  3379.         end
  3380.         fillRegionTmp(GameTooltip:GetRegions())
  3381.         for i,v in ipairs(regionTmp) do
  3382.             if v.GetFont then
  3383.                 local font,size,outline = v:GetFont()
  3384.                 if font == lastSetFont then
  3385.                     v:SetFont(normalFont, size, outline)
  3386.                 end
  3387.             end
  3388.             regionTmp[i] = nil
  3389.         end
  3390.         lastSetFont = nil
  3391.     end
  3392. end
  3393.  
  3394. local function activate(self, oldLib, oldDeactivate)
  3395.     Dewdrop = self
  3396.     if oldLib and oldLib.registry then
  3397.         self.registry = oldLib.registry
  3398.         self.onceRegistered = oldLib.onceRegistered
  3399.     else
  3400.         self.registry = {}
  3401.         self.onceRegistered = {}
  3402.  
  3403.         local WorldFrame_OnMouseDown = WorldFrame:GetScript("OnMouseDown")
  3404.         local WorldFrame_OnMouseUp = WorldFrame:GetScript("OnMouseUp")
  3405.         local oldX, oldY, clickTime
  3406.         WorldFrame:SetScript("OnMouseDown", function(this, ...)
  3407.             oldX,oldY = GetCursorPosition()
  3408.             clickTime = GetTime()
  3409.             if WorldFrame_OnMouseDown then
  3410.                 WorldFrame_OnMouseDown(this, ...)
  3411.             end
  3412.         end)
  3413.  
  3414.         WorldFrame:SetScript("OnMouseUp", function(this, ...)
  3415.             local x,y = GetCursorPosition()
  3416.             if not oldX or not oldY or not x or not y or not clickTime then
  3417.                 self:Close()
  3418.                 if WorldFrame_OnMouseUp then
  3419.                     WorldFrame_OnMouseUp(this, ...)
  3420.                 end
  3421.                 return
  3422.             end
  3423.             local d = math.abs(x - oldX) + math.abs(y - oldY)
  3424.             if d <= 5 and GetTime() - clickTime < 0.5 then
  3425.                 self:Close()
  3426.             end
  3427.             if WorldFrame_OnMouseUp then
  3428.                 WorldFrame_OnMouseUp(this, ...)
  3429.             end
  3430.         end)
  3431.  
  3432.         hooksecurefunc(DropDownList1, "Show", function()
  3433.             if levels[1] and levels[1]:IsVisible() then
  3434.                 self:Close()
  3435.             end
  3436.         end)
  3437.  
  3438.         hooksecurefunc("HideDropDownMenu", function()
  3439.             if levels[1] and levels[1]:IsVisible() then
  3440.                 self:Close()
  3441.             end
  3442.         end)
  3443.  
  3444.         hooksecurefunc("CloseDropDownMenus", function()
  3445.             if levels[1] and levels[1]:IsVisible() then
  3446.                 local stack = debugstack()
  3447.                 if not stack:find("`TargetFrame_OnHide'") then
  3448.                     self:Close()
  3449.                 end
  3450.             end
  3451.         end)
  3452.     end
  3453.     self.frame = oldLib and oldLib.frame or CreateFrame("Frame")
  3454.     self.frame:UnregisterAllEvents()
  3455.     self.frame:RegisterEvent("PLAYER_REGEN_ENABLED")
  3456.     self.frame:RegisterEvent("PLAYER_REGEN_DISABLED")
  3457.     self.frame:Hide()
  3458.     self.frame:SetScript("OnEvent", function(this, event)
  3459.         this:Show()
  3460.         if event=="PLAYER_REGEN_ENABLED" then           -- track combat state for secure frame operations
  3461.             self.combat = false
  3462.         elseif event=="PLAYER_REGEN_DISABLED" then
  3463.             self.combat = true
  3464.         end
  3465.     end)
  3466.     self.frame:SetScript("OnUpdate", function(this)
  3467.         this:Hide()
  3468.         self:Refresh(1)
  3469.     end)
  3470.     self.hookedTooltip = true
  3471.     if not oldLib or not oldLib.hookedTooltip then
  3472.         local OnTooltipHide = GameTooltip:GetScript("OnHide")
  3473.         GameTooltip:SetScript("OnHide", function(this, ...)
  3474.             if OnTooltipHide then
  3475.                 OnTooltipHide(this, ...)
  3476.             end
  3477.             if type(self.OnTooltipHide) == "function" then
  3478.                 self:OnTooltipHide()
  3479.             end
  3480.         end)
  3481.     end
  3482.     levels = {}
  3483.     buttons = {}
  3484.  
  3485.     if oldDeactivate then
  3486.         oldDeactivate(oldLib)
  3487.     end
  3488. end
  3489.  
  3490. local function external(lib, major, instance)
  3491.     if major == "LibSharedMedia-3.0" then
  3492.         SharedMedia = instance
  3493.     end
  3494. end
  3495.  
  3496. AceLibrary:Register(Dewdrop, MAJOR_VERSION, MINOR_VERSION, activate, nil, external)
  Reply With Quote
12-22-18, 06:07 PM   #6
Fizzlemizz
I did that?
 
Fizzlemizz's Avatar
Premium Member
AddOn Author - Click to view addons
Join Date: Dec 2011
Posts: 1,871
Around line 975
Lua Code:
  1. if options ~= baseOptions then
  2.     if kind == "header" then
  3.     elseif type(options.desc) ~= "string" then
  4. -- This is where I would check the table is what is expected.
  5.         return '"desc" must be a string', position
  6.     elseif options.desc:len() == 0 then
  7.         return '"desc" cannot be a 0-length string', position
  8.     end
  9. end
I'm not saying DewDrop is wrong but this seems to be where it thinks it's not getting the correct table format/information and posting the error.

If you think it should be get the table:
Code:
bosses = {...}
add print(bosses, type(bosses.desc)) after the table is created and print(options, type(options.desc)) in DewDrop before the return. They should be the same table and desc should be type "string" in both cases. If they're not then you need to track back to where the table changed.
__________________
Fizzlemizz
Maintainer of Discord Unit Frames and Discord Art.
Author of FauxMazzle, FauxMazzleHUD and Move Pad Plus.

Last edited by Fizzlemizz : 12-22-18 at 06:09 PM.
  Reply With Quote
12-22-18, 07:21 PM   #7
Brellison94
An Aku'mai Servant
AddOn Author - Click to view addons
Join Date: Mar 2013
Posts: 36
Originally Posted by Fizzlemizz View Post
Around line 975
Lua Code:
  1. if options ~= baseOptions then
  2.     if kind == "header" then
  3.     elseif type(options.desc) ~= "string" then
  4. -- This is where I would check the table is what is expected.
  5.         return '"desc" must be a string', position
  6.     elseif options.desc:len() == 0 then
  7.         return '"desc" cannot be a 0-length string', position
  8.     end
  9. end
I'm not saying DewDrop is wrong but this seems to be where it thinks it's not getting the correct table format/information and posting the error.

If you think it should be get the table:
Code:
bosses = {...}
add print(bosses, type(bosses.desc)) after the table is created and print(options, type(options.desc)) in DewDrop before the return. They should be the same table and desc should be type "string" in both cases. If they're not then you need to track back to where the table changed.
All of the code is in Dewdrop file?
  Reply With Quote
12-22-18, 08:12 PM   #8
Fizzlemizz
I did that?
 
Fizzlemizz's Avatar
Premium Member
AddOn Author - Click to view addons
Join Date: Dec 2011
Posts: 1,871
Code:
print(options, type(options.desc))
Goes in DewDrop before the line "return '"desc" must be a string', position".

Code:
print(bosses, type(bosses.desc))
Goes in your code initially after declaring the bosses table (it can be copied/moved to track what happens to the table like like a copy at the line where the error is designated).
__________________
Fizzlemizz
Maintainer of Discord Unit Frames and Discord Art.
Author of FauxMazzle, FauxMazzleHUD and Move Pad Plus.
  Reply With Quote
12-22-18, 08:54 PM   #9
Fizzlemizz
I did that?
 
Fizzlemizz's Avatar
Premium Member
AddOn Author - Click to view addons
Join Date: Dec 2011
Posts: 1,871
If I have it right, it seems to be complaining that
Code:
BossTalk.cmdtable.desc
is not a string.

BossTalk.cmdtable seems to contain
Code:
type = 'group'
and
Code:
args = { ... }
but no "desc" key meaning its type is nil and not string.
__________________
Fizzlemizz
Maintainer of Discord Unit Frames and Discord Art.
Author of FauxMazzle, FauxMazzleHUD and Move Pad Plus.

Last edited by Fizzlemizz : 12-23-18 at 12:47 AM.
  Reply With Quote
12-23-18, 10:15 AM   #10
Brellison94
An Aku'mai Servant
AddOn Author - Click to view addons
Join Date: Mar 2013
Posts: 36
Originally Posted by Fizzlemizz View Post
If I have it right, it seems to be complaining that
Code:
BossTalk.cmdtable.desc
is not a string.

BossTalk.cmdtable seems to contain
Code:
type = 'group'
and
Code:
args = { ... }
but no "desc" key meaning its type is nil and not string.
That's aggravating. Where did you put this that the code above displayed?

Code:
print(bosses, type(bosses.desc))
  Reply With Quote
12-23-18, 11:05 AM   #11
Fizzlemizz
I did that?
 
Fizzlemizz's Avatar
Premium Member
AddOn Author - Click to view addons
Join Date: Dec 2011
Posts: 1,871
I didn't. I downloaded your addon and followed the code.

From what you posted and looking at DewDrop code I knew DewDrop expected a desc key in the table it was examinning.

From the error I knew the table being examined is BossTalk.cmdtable (the error being the line BTchat:FeedAceOptionsTable(BossTalk.cmdtable),

From looking at where BossTalk.cmdtable is defined I saw it had no desc key.
__________________
Fizzlemizz
Maintainer of Discord Unit Frames and Discord Art.
Author of FauxMazzle, FauxMazzleHUD and Move Pad Plus.

Last edited by Fizzlemizz : 12-23-18 at 11:18 AM.
  Reply With Quote

WoWInterface » AddOns, Compilations, Macros » AddOn Help/Support » Memory-economic mode removal with this message

Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

vB code is On
Smilies are On
[IMG] code is On
HTML code is Off