vanilla-wow-addons – Blame information for rev 1

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 office 1 --[[
2 Name: Dewdrop-2.0
3 Revision: $Rev: 7265 $
4 Author(s): ckknight (ckknight@gmail.com)
5 Website: http://ckknight.wowinterface.com/
6 Documentation: http://wiki.wowace.com/index.php/Dewdrop-2.0
7 SVN: http://svn.wowace.com/root/trunk/DewdropLib/Dewdrop-2.0
8 Description: A library to provide a clean dropdown menu interface.
9 Dependencies: AceLibrary
10 ]]
11  
12 local MAJOR_VERSION = "Dewdrop-2.0"
13 local MINOR_VERSION = "$Revision: 7265 $"
14  
15 if not AceLibrary then error(MAJOR_VERSION .. " requires AceLibrary") end
16 if not AceLibrary:IsNewVersion(MAJOR_VERSION, MINOR_VERSION) then return end
17  
18 local Dewdrop = {}
19  
20 local function new(k1, v1, k2, v2, k3, v3, k4, v4, k5, v5, k6, v6, k7, v7, k8, v8, k9, v9, k10, v10, k11, v11, k12, v12, k13, v13, k14, v14, k15, v15, k16, v16, k17, v17, k18, v18, k19, v19, k20, v20)
21 local t = {}
22 if k1 then t[k1] = v1
23 if k2 then t[k2] = v2
24 if k3 then t[k3] = v3
25 if k4 then t[k4] = v4
26 if k5 then t[k5] = v5
27 if k6 then t[k6] = v6
28 if k7 then t[k7] = v7
29 if k8 then t[k8] = v8
30 if k9 then t[k9] = v9
31 if k10 then t[k10] = v10
32 if k11 then t[k11] = v11
33 if k12 then t[k12] = v12
34 if k13 then t[k13] = v13
35 if k14 then t[k14] = v14
36 if k15 then t[k15] = v15
37 if k16 then t[k16] = v16
38 if k17 then t[k17] = v17
39 if k18 then t[k18] = v18
40 if k19 then t[k19] = v19
41 if k20 then t[k20] = v20
42 end end end end end end end end end end end end end end end end end end end end
43 return t
44 end
45 local tmp
46 do
47 local t
48 function tmp(k1, v1, k2, v2, k3, v3, k4, v4, k5, v5, k6, v6, k7, v7, k8, v8, k9, v9, k10, v10, k11, v11, k12, v12, k13, v13, k14, v14, k15, v15, k16, v16, k17, v17, k18, v18, k19, v19, k20, v20)
49 for k in pairs(t) do
50 t[k] = nil
51 end
52 if type(k1) == "table" then
53 for k,v in pairs(k1) do
54 t[k] = v
55 end
56 else
57 if k1 then t[k1] = v1
58 if k2 then t[k2] = v2
59 if k3 then t[k3] = v3
60 if k4 then t[k4] = v4
61 if k5 then t[k5] = v5
62 if k6 then t[k6] = v6
63 if k7 then t[k7] = v7
64 if k8 then t[k8] = v8
65 if k9 then t[k9] = v9
66 if k10 then t[k10] = v10
67 if k11 then t[k11] = v11
68 if k12 then t[k12] = v12
69 if k13 then t[k13] = v13
70 if k14 then t[k14] = v14
71 if k15 then t[k15] = v15
72 if k16 then t[k16] = v16
73 if k17 then t[k17] = v17
74 if k18 then t[k18] = v18
75 if k19 then t[k19] = v19
76 if k20 then t[k20] = v20
77 end end end end end end end end end end end end end end end end end end end end
78 end
79 return t
80 end
81 local x = tmp
82 function tmp(k1, v1, k2, v2, k3, v3, k4, v4, k5, v5, k6, v6, k7, v7, k8, v8, k9, v9, k10, v10, k11, v11, k12, v12, k13, v13, k14, v14, k15, v15, k16, v16, k17, v17, k18, v18, k19, v19, k20, v20)
83 t = {}
84 tmp = x
85 x = nil
86 return tmp(k1, v1, k2, v2, k3, v3, k4, v4, k5, v5, k6, v6, k7, v7, k8, v8, k9, v9, k10, v10, k11, v11, k12, v12, k13, v13, k14, v14, k15, v15, k16, v16, k17, v17, k18, v18, k19, v19, k20, v20)
87 end
88 end
89 local tmp2
90 do
91 local t
92 function tmp2(k1, v1, k2, v2, k3, v3, k4, v4, k5, v5, k6, v6, k7, v7, k8, v8, k9, v9, k10, v10, k11, v11, k12, v12, k13, v13, k14, v14, k15, v15, k16, v16, k17, v17, k18, v18, k19, v19, k20, v20)
93 for k in pairs(t) do
94 t[k] = nil
95 end
96 if k1 then t[k1] = v1
97 if k2 then t[k2] = v2
98 if k3 then t[k3] = v3
99 if k4 then t[k4] = v4
100 if k5 then t[k5] = v5
101 if k6 then t[k6] = v6
102 if k7 then t[k7] = v7
103 if k8 then t[k8] = v8
104 if k9 then t[k9] = v9
105 if k10 then t[k10] = v10
106 if k11 then t[k11] = v11
107 if k12 then t[k12] = v12
108 if k13 then t[k13] = v13
109 if k14 then t[k14] = v14
110 if k15 then t[k15] = v15
111 if k16 then t[k16] = v16
112 if k17 then t[k17] = v17
113 if k18 then t[k18] = v18
114 if k19 then t[k19] = v19
115 if k20 then t[k20] = v20
116 end end end end end end end end end end end end end end end end end end end end
117 return t
118 end
119 local x = tmp2
120 function tmp2(k1, v1, k2, v2, k3, v3, k4, v4, k5, v5, k6, v6, k7, v7, k8, v8, k9, v9, k10, v10, k11, v11, k12, v12, k13, v13, k14, v14, k15, v15, k16, v16, k17, v17, k18, v18, k19, v19, k20, v20)
121 t = {}
122 tmp2 = x
123 x = nil
124 return tmp2(k1, v1, k2, v2, k3, v3, k4, v4, k5, v5, k6, v6, k7, v7, k8, v8, k9, v9, k10, v10, k11, v11, k12, v12, k13, v13, k14, v14, k15, v15, k16, v16, k17, v17, k18, v18, k19, v19, k20, v20)
125 end
126 end
127 local levels
128 local buttons
129  
130 local function GetScaledCursorPosition()
131 local x, y = GetCursorPosition()
132 local scale = UIParent:GetEffectiveScale()
133 return x / scale, y / scale
134 end
135  
136 local function StartCounting(self, levelNum)
137 for i = levelNum, table.getn(levels) do
138 if levels[i] then
139 levels[i].count = 3
140 end
141 end
142 end
143  
144 local function StopCounting(self, level)
145 for i = level, 1, -1 do
146 if levels[i] then
147 levels[i].count = nil
148 end
149 end
150 end
151  
152 local function OnUpdate(self, arg1)
153 for _,level in ipairs(levels) do
154 if level.count then
155 level.count = level.count - arg1
156 if level.count < 0 then
157 level.count = nil
158 self:Close(level.num)
159 end
160 end
161 end
162 end
163  
164 local function CheckDualMonitor(self, frame)
165 local ratio = GetScreenWidth() / GetScreenHeight()
166 if ratio >= 2.4 and frame:GetRight() > GetScreenWidth() / 2 and frame:GetLeft() < GetScreenWidth() / 2 then
167 local offsetx
168 if GetCursorPosition() / GetScreenHeight() * 768 < GetScreenWidth() / 2 then
169 offsetx = GetScreenWidth() / 2 - frame:GetRight()
170 else
171 offsetx = GetScreenWidth() / 2 - frame:GetLeft()
172 end
173 local point, parent, relativePoint, x, y = frame:GetPoint(1)
174 frame:SetPoint(point, parent, relativePoint, (x or 0) + offsetx, y or 0)
175 end
176 end
177  
178 local function CheckSize(self, level)
179 if not level.buttons then
180 return
181 end
182 local height = 20
183 for _, button in ipairs(level.buttons) do
184 height = height + button:GetHeight()
185 end
186 level:SetHeight(height)
187 local width = 160
188 for _, button in ipairs(level.buttons) do
189 local extra = 1
190 if button.hasArrow or button.hasColorSwatch then
191 extra = extra + 16
192 end
193 if not button.notCheckable then
194 extra = extra + 24
195 end
196 button.text:SetFont(STANDARD_TEXT_FONT, button.textHeight)
197 if button.text:GetWidth() + extra > width then
198 width = button.text:GetWidth() + extra
199 end
200 end
201 level:SetWidth(width + 20)
202 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
203 level:ClearAllPoints()
204 if level.lastDirection == "RIGHT" then
205 if level.lastVDirection == "DOWN" then
206 level:SetPoint("TOPLEFT", level.parent or level:GetParent(), "TOPRIGHT", 5, 10)
207 else
208 level:SetPoint("BOTTOMLEFT", level.parent or level:GetParent(), "BOTTOMRIGHT", 5, -10)
209 end
210 else
211 if level.lastVDirection == "DOWN" then
212 level:SetPoint("TOPRIGHT", level.parent or level:GetParent(), "TOPLEFT", -5, 10)
213 else
214 level:SetPoint("BOTTOMRIGHT", level.parent or level:GetParent(), "BOTTOMLEFT", -5, -10)
215 end
216 end
217 end
218 local dirty = false
219 if not level:GetRight() then
220 self:Close()
221 return
222 end
223 if level:GetRight() > GetScreenWidth() and level.lastDirection == "RIGHT" then
224 level.lastDirection = "LEFT"
225 dirty = true
226 elseif level:GetLeft() < 0 and level.lastDirection == "LEFT" then
227 level.lastDirection = "RIGHT"
228 dirty = true
229 end
230 if level:GetTop() > GetScreenHeight() and level.lastVDirection == "UP" then
231 level.lastVDirection = "DOWN"
232 dirty = true
233 elseif level:GetBottom() < 0 and level.lastVDirection == "DOWN" then
234 level.lastVDirection = "UP"
235 dirty = true
236 end
237 if dirty then
238 level:ClearAllPoints()
239 if level.lastDirection == "RIGHT" then
240 if level.lastVDirection == "DOWN" then
241 level:SetPoint("TOPLEFT", level.parent or level:GetParent(), "TOPRIGHT", 5, 10)
242 else
243 level:SetPoint("BOTTOMLEFT", level.parent or level:GetParent(), "BOTTOMRIGHT", 5, -10)
244 end
245 else
246 if level.lastVDirection == "DOWN" then
247 level:SetPoint("TOPRIGHT", level.parent or level:GetParent(), "TOPLEFT", -5, 10)
248 else
249 level:SetPoint("BOTTOMRIGHT", level.parent or level:GetParent(), "BOTTOMLEFT", -5, -10)
250 end
251 end
252 end
253 if level:GetTop() > GetScreenHeight() then
254 local top = level:GetTop()
255 local point, parent, relativePoint, x, y = level:GetPoint(1)
256 level:ClearAllPoints()
257 level:SetPoint(point, parent, relativePoint, x or 0, (y or 0) + GetScreenHeight() - top)
258 elseif level:GetBottom() < 0 then
259 local bottom = level:GetBottom()
260 local point, parent, relativePoint, x, y = level:GetPoint(1)
261 level:ClearAllPoints()
262 level:SetPoint(point, parent, relativePoint, x or 0, (y or 0) - bottom)
263 end
264 CheckDualMonitor(self, level)
265 if mod(level.num, 5) == 0 then
266 local left, bottom = level:GetLeft(), level:GetBottom()
267 level:ClearAllPoints()
268 level:SetPoint("BOTTOMLEFT", UIParent, "BOTTOMLEFT", left, bottom)
269 end
270 end
271  
272 local Open
273 local OpenSlider
274 local OpenEditBox
275 local Refresh
276 local Clear
277 local function ReleaseButton(self, level, index)
278 if not level.buttons then
279 return
280 end
281 if not level.buttons[index] then
282 return
283 end
284 local button = level.buttons[index]
285 button:Hide()
286 if button.highlight then
287 button.highlight:Hide()
288 end
289 table.remove(level.buttons, index)
290 table.insert(buttons, button)
291 for k in pairs(button) do
292 if k ~= 0 and k ~= "text" and k ~= "check" and k ~= "arrow" and k ~= "colorSwatch" and k ~= "highlight" and k ~= "radioHighlight" then
293 button[k] = nil
294 end
295 end
296 return true
297 end
298  
299 local function Scroll(self, level, down)
300 if down then
301 if level:GetBottom() < 0 then
302 local point, parent, relativePoint, x, y = level:GetPoint(1)
303 level:SetPoint(point, parent, relativePoint, x, y + 50)
304 if level:GetBottom() > 0 then
305 level:SetPoint(point, parent, relativePoint, x, y + 50 - level:GetBottom())
306 end
307 end
308 else
309 if level:GetTop() > GetScreenHeight() then
310 local point, parent, relativePoint, x, y = level:GetPoint(1)
311 level:SetPoint(point, parent, relativePoint, x, y - 50)
312 if level:GetTop() < GetScreenHeight() then
313 level:SetPoint(point, parent, relativePoint, x, y - 50 + GetScreenHeight() - level:GetTop())
314 end
315 end
316 end
317 end
318  
319 local sliderFrame
320 local editBoxFrame
321  
322 local function AcquireButton(self, level)
323 if not levels[level] then
324 return
325 end
326 level = levels[level]
327 if not level.buttons then
328 level.buttons = {}
329 end
330 local button
331 if table.getn(buttons) == 0 then
332 button = CreateFrame("Button")
333 button:SetFrameStrata("FULLSCREEN_DIALOG")
334 button:SetHeight(16)
335 local highlight = button:CreateTexture(nil, "BACKGROUND")
336 highlight:SetTexture("Interface\\QuestFrame\\UI-QuestTitleHighlight")
337 button.highlight = highlight
338 highlight:SetBlendMode("ADD")
339 highlight:SetAllPoints(button)
340 highlight:Hide()
341 local check = button:CreateTexture(nil, "ARTWORK")
342 button.check = check
343 check:SetTexture("Interface\\Buttons\\UI-CheckBox-Check")
344 check:SetPoint("CENTER", button, "LEFT", 12, 0)
345 check:SetWidth(24)
346 check:SetHeight(24)
347 local radioHighlight = button:CreateTexture(nil, "ARTWORK")
348 button.radioHighlight = radioHighlight
349 radioHighlight:SetTexture("Interface\\Buttons\\UI-RadioButton")
350 radioHighlight:SetAllPoints(check)
351 radioHighlight:SetBlendMode("ADD")
352 radioHighlight:SetTexCoord(0.5, 0.75, 0, 1)
353 radioHighlight:Hide()
354 button:SetScript("OnEnter", function()
355 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
356 for i = 1, this.level.num do
357 Refresh(self, levels[i])
358 end
359 return
360 end
361 self:Close(this.level.num + 1)
362 if not this.disabled then
363 if this.hasSlider then
364 OpenSlider(self, this)
365 elseif this.hasEditBox then
366 OpenEditBox(self, this)
367 elseif this.hasArrow then
368 Open(self, this, nil, this.level.num + 1, this.value)
369 end
370 end
371 if not this.level then -- button reclaimed
372 return
373 end
374 StopCounting(self, this.level.num + 1)
375 if not this.disabled then
376 highlight:Show()
377 if this.isRadio then
378 button.radioHighlight:Show()
379 end
380 end
381 if this.tooltipTitle or this.tooltipText then
382 GameTooltip_SetDefaultAnchor(GameTooltip, this)
383 local disabled = this.disabled
384 if this.tooltipTitle then
385 if disabled then
386 GameTooltip:SetText(this.tooltipTitle, 0.5, 0.5, 0.5, 1)
387 else
388 GameTooltip:SetText(this.tooltipTitle, 1, 1, 1, 1)
389 end
390 if this.tooltipText then
391 if disabled then
392 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)
393 else
394 GameTooltip:AddLine(this.tooltipText, NORMAL_FONT_COLOR.r, NORMAL_FONT_COLOR.g, NORMAL_FONT_COLOR.b, 1)
395 end
396 end
397 else
398 if disabled then
399 GameTooltip:SetText(this.tooltipText, 0.5, 0.5, 0.5, 1)
400 else
401 GameTooltip:SetText(this.tooltipText, 1, 1, 1, 1)
402 end
403 end
404 GameTooltip:Show()
405 end
406 if this.tooltipFunc then
407 GameTooltip:SetOwner(this, "ANCHOR_NONE")
408 GameTooltip:SetPoint("TOPLEFT", this, "TOPRIGHT", 5, 0)
409 this.tooltipFunc(this.tooltipArg1, this.tooltipArg2, this.tooltipArg3, this.tooltipArg4)
410 GameTooltip:Show()
411 end
412 end)
413 button:SetScript("OnLeave", function()
414 highlight:Hide()
415 button.radioHighlight:Hide()
416 if this.level then
417 StartCounting(self, this.level.num)
418 end
419 GameTooltip:Hide()
420 end)
421 button:SetScript("OnClick", function()
422 if not this.disabled then
423 if this.hasColorSwatch then
424 local func = button.colorFunc
425 local a1,a2,a3,a4 = button.colorArg1, button.colorArg2, button.colorArg3, button.colorArg4
426 local hasOpacity = this.hasOpacity
427 ColorPickerFrame.func = function()
428 if func then
429 local r,g,b = ColorPickerFrame:GetColorRGB()
430 local a = hasOpacity and 1 - OpacitySliderFrame:GetValue() or nil
431 if a1 == nil then
432 func(r, g, b, a)
433 elseif a2 == nil then
434 func(a1, r, g, b, a)
435 elseif a3 == nil then
436 func(a1, a2, r, g, b, a)
437 elseif a4 == nil then
438 func(a1, a2, a3, r, g, b, a)
439 else
440 func(a1, a2, a3, a4, r, g, b, a)
441 end
442 end
443 end
444 ColorPickerFrame.hasOpacity = this.hasOpacity
445 ColorPickerFrame.opacityFunc = ColorPickerFrame.func
446 ColorPickerFrame.opacity = 1 - this.opacity
447 ColorPickerFrame:SetColorRGB(this.r, this.g, this.b)
448 local r, g, b, a = this.r, this.g, this.b, this.opacity
449 ColorPickerFrame.cancelFunc = function()
450 if a1 == nil then
451 func(r, g, b, a)
452 elseif a2 == nil then
453 func(a1, r, g, b, a)
454 elseif a3 == nil then
455 func(a1, a2, r, g, b, a)
456 else
457 func(a1, a2, a3, r, g, b, a)
458 end
459 end
460 self:Close(1)
461 ShowUIPanel(ColorPickerFrame)
462 elseif this.func then
463 local level = button.level
464 if type(this.func) == "string" then
465 self:assert(type(this.arg1[this.func]) == "function", "Cannot call method " .. this.func)
466 this.arg1[this.func](this.arg1, this.arg2, this.arg3, this.arg4)
467 else
468 this.func(this.arg1, this.arg2, this.arg3, this.arg4)
469 end
470 if this.closeWhenClicked then
471 self:Close()
472 elseif level:IsShown() then
473 for i = 1, level.num do
474 Refresh(self, levels[i])
475 end
476 end
477 elseif this.closeWhenClicked then
478 self:Close()
479 end
480 end
481 end)
482 local text = button:CreateFontString(nil, "ARTWORK")
483 button.text = text
484 text:SetFontObject(GameFontHighlightSmall)
485 button.text:SetFont(STANDARD_TEXT_FONT, UIDROPDOWNMENU_DEFAULT_TEXT_HEIGHT)
486 button:SetScript("OnMouseDown", function()
487 if not this.disabled and (this.func or this.colorFunc or this.closeWhenClicked) then
488 text:SetPoint("LEFT", button, "LEFT", this.notCheckable and 1 or 25, -1)
489 end
490 end)
491 button:SetScript("OnMouseUp", function()
492 if not this.disabled and (this.func or this.colorFunc or this.closeWhenClicked) then
493 text:SetPoint("LEFT", button, "LEFT", this.notCheckable and 0 or 24, 0)
494 end
495 end)
496 local arrow = button:CreateTexture(nil, "ARTWORK")
497 button.arrow = arrow
498 arrow:SetPoint("RIGHT", button, "RIGHT", 0, 0)
499 arrow:SetWidth(16)
500 arrow:SetHeight(16)
501 arrow:SetTexture("Interface\\ChatFrame\\ChatFrameExpandArrow")
502 local colorSwatch = button:CreateTexture(nil, "OVERLAY")
503 button.colorSwatch = colorSwatch
504 colorSwatch:SetWidth(20)
505 colorSwatch:SetHeight(20)
506 colorSwatch:SetTexture("Interface\\ChatFrame\\ChatFrameColorSwatch")
507 local texture = button:CreateTexture(nil, "OVERLAY")
508 colorSwatch.texture = texture
509 texture:SetTexture(1, 1, 1)
510 texture:SetWidth(11.5)
511 texture:SetHeight(11.5)
512 texture:Show()
513 texture:SetPoint("CENTER", colorSwatch, "CENTER")
514 colorSwatch:SetPoint("RIGHT", button, "RIGHT", 0, 0)
515 else
516 button = buttons[table.getn(buttons)]
517 table.remove(buttons, table.getn(buttons))
518 end
519 button:ClearAllPoints()
520 button:SetParent(level)
521 button:SetFrameStrata(level:GetFrameStrata())
522 button:SetFrameLevel(level:GetFrameLevel() + 1)
523 button:SetPoint("LEFT", level, "LEFT", 10, 0)
524 button:SetPoint("RIGHT", level, "RIGHT", -10, 0)
525 if table.getn(level.buttons) == 0 then
526 button:SetPoint("TOP", level, "TOP", 0, -10)
527 else
528 button:SetPoint("TOP", level.buttons[table.getn(level.buttons)], "BOTTOM", 0, 0)
529 end
530 button.text:SetPoint("LEFT", button, "LEFT", 24, 0)
531 button:Show()
532 button.level = level
533 table.insert(level.buttons, button)
534 if not level.parented then
535 level.parented = true
536 level:ClearAllPoints()
537 if level.num == 1 then
538 if level.parent ~= UIParent then
539 level:SetPoint("TOPRIGHT", level.parent, "TOPLEFT")
540 else
541 level:SetPoint("CENTER", level.parent, "CENTER")
542 end
543 else
544 if level.lastDirection == "RIGHT" then
545 if level.lastVDirection == "DOWN" then
546 level:SetPoint("TOPLEFT", level.parent, "TOPRIGHT", 5, 10)
547 else
548 level:SetPoint("BOTTOMLEFT", level.parent, "BOTTOMRIGHT", 5, -10)
549 end
550 else
551 if level.lastVDirection == "DOWN" then
552 level:SetPoint("TOPRIGHT", level.parent, "TOPLEFT", -5, 10)
553 else
554 level:SetPoint("BOTTOMRIGHT", level.parent, "BOTTOMLEFT", -5, -10)
555 end
556 end
557 end
558 level:SetFrameStrata("FULLSCREEN_DIALOG")
559 end
560 return button
561 end
562  
563 local function AcquireLevel(self, level)
564 if not levels[level] then
565 for i = table.getn(levels) + 1, level, -1 do
566 local i = i
567 local frame = CreateFrame("Button")
568 if i == 1 then
569 local old_CloseWindows = CloseWindows
570 function CloseWindows(ignoreCenter)
571 local found = old_CloseWindows(ignoreCenter)
572 if levels[1]:IsShown() then
573 self:Close()
574 return 1
575 end
576 return found
577 end
578 end
579 levels[i] = frame
580 frame.num = i
581 frame:SetParent(UIParent)
582 frame:SetFrameStrata("FULLSCREEN_DIALOG")
583 frame:Hide()
584 frame:SetWidth(180)
585 frame:SetHeight(10)
586 frame:SetFrameLevel(i * 3)
587 frame:SetScript("OnHide", function()
588 self:Close(level + 1)
589 end)
590 frame:SetFrameStrata("FULLSCREEN_DIALOG")
591 if frame.SetTopLevel then
592 frame:SetTopLevel(true)
593 end
594 frame:EnableMouse(true)
595 frame:EnableMouseWheel(true)
596 local backdrop = CreateFrame("Frame", nil, frame)
597 backdrop:SetAllPoints(frame)
598 backdrop:SetBackdrop(tmp(
599 'bgFile', "Interface\\Tooltips\\UI-Tooltip-Background",
600 'edgeFile', "Interface\\Tooltips\\UI-Tooltip-Border",
601 'tile', true,
602 'insets', tmp2(
603 'left', 5,
604 'right', 5,
605 'top', 5,
606 'bottom', 5
607 ),
608 'tileSize', 16,
609 'edgeSize', 16
610 ))
611 backdrop:SetBackdropBorderColor(TOOLTIP_DEFAULT_COLOR.r, TOOLTIP_DEFAULT_COLOR.g, TOOLTIP_DEFAULT_COLOR.b)
612 backdrop:SetBackdropColor(TOOLTIP_DEFAULT_BACKGROUND_COLOR.r, TOOLTIP_DEFAULT_BACKGROUND_COLOR.g, TOOLTIP_DEFAULT_BACKGROUND_COLOR.b)
613 frame:SetScript("OnClick", function()
614 self:Close(i)
615 end)
616 frame:SetScript("OnEnter", function()
617 StopCounting(self, i)
618 end)
619 frame:SetScript("OnLeave", function()
620 StartCounting(self, i)
621 end)
622 frame:SetScript("OnMouseWheel", function()
623 Scroll(self, frame, arg1 < 0)
624 end)
625 if i == 1 then
626 frame:SetScript("OnUpdate", function()
627 OnUpdate(self, arg1)
628 end)
629 levels[1].lastDirection = "RIGHT"
630 levels[1].lastVDirection = "DOWN"
631 else
632 levels[i].lastDirection = levels[i - 1].lastDirection
633 levels[i].lastVDirection = levels[i - 1].lastVDirection
634 end
635 end
636 end
637 return levels[level]
638 end
639  
640 local function checkValidate(validateFunc, func, arg1, arg2, arg3)
641 local text
642 if arg3 ~= nil then
643 text = arg3
644 elseif arg2 ~= nil then
645 text = arg2
646 else
647 text = arg1
648 end
649 if not validateFunc(text) then
650 DEFAULT_CHAT_FRAME:AddMessage("|cffffff7fValidation error: [|r" .. tostring(text) .. "|cffffff7f]|r")
651 else
652 func(arg1, arg2, arg3)
653 end
654 end
655  
656 local function validateOptions(options, position, baseOptions, fromPass)
657 if not baseOptions then
658 baseOptions = options
659 end
660 if type(options) ~= "table" then
661 return "Options must be a table.", position
662 end
663 local kind = options.type
664 if type(kind) ~= "string" then
665 return '"type" must be a string.', position
666 elseif kind ~= "group" and kind ~= "range" and kind ~= "text" and kind ~= "execute" and kind ~= "toggle" and kind ~= "color" and kind ~= "header" then
667 return '"type" must either be "range", "text", "group", "toggle", "execute", "color", or "header".', position
668 end
669 if options.aliases then
670 if type(options.aliases) ~= "table" and type(options.aliases) ~= "string" then
671 return '"alias" must be a table or string', position
672 end
673 end
674 if not fromPass then
675 if kind == "execute" then
676 if type(options.func) ~= "string" and type(options.func) ~= "function" then
677 return '"func" must be a string or function', position
678 end
679 elseif kind == "range" or kind == "text" or kind == "toggle" then
680 if type(options.set) ~= "string" and type(options.set) ~= "function" then
681 return '"set" must be a string or function', position
682 end
683 if kind == "text" and options.get == false then
684 elseif type(options.get) ~= "string" and type(options.get) ~= "function" then
685 return '"get" must be a string or function', position
686 end
687 elseif kind == "group" and options.pass then
688 if options.pass ~= true then
689 return '"pass" must be either nil, true, or false', position
690 end
691 if not options.func then
692 if type(options.set) ~= "string" and type(options.set) ~= "function" then
693 return '"set" must be a string or function', position
694 end
695 if type(options.get) ~= "string" and type(options.get) ~= "function" then
696 return '"get" must be a string or function', position
697 end
698 elseif type(options.func) ~= "string" and type(options.func) ~= "function" then
699 return '"func" must be a string or function', position
700 end
701 end
702 else
703 if kind == "group" then
704 return 'cannot have "type" = "group" as a subgroup of a passing group', position
705 end
706 end
707 if options ~= baseOptions then
708 if kind == "header" then
709 elseif type(options.desc) ~= "string" then
710 return '"desc" must be a string', position
711 elseif string.len(options.desc) == 0 then
712 return '"desc" cannot be a 0-length string', position
713 end
714 end
715 if options ~= baseOptions or kind == "range" or kind == "text" or kind == "toggle" or kind == "color" then
716 if options.type == "header" and not options.cmdName and not options.name then
717 elseif options.cmdName then
718 if type(options.cmdName) ~= "string" then
719 return '"cmdName" must be a string or nil', position
720 elseif string.len(options.cmdName) == 0 then
721 return '"cmdName" cannot be a 0-length string', position
722 end
723 if type(options.guiName) ~= "string" then
724 if not options.guiNameIsMap then
725 return '"guiName" must be a string or nil', position
726 end
727 elseif string.len(options.guiName) == 0 then
728 return '"guiName" cannot be a 0-length string', position
729 end
730 else
731 if type(options.name) ~= "string" then
732 return '"name" must be a string', position
733 elseif string.len(options.name) == 0 then
734 return '"name" cannot be a 0-length string', position
735 end
736 end
737 end
738 if options.guiNameIsMap then
739 if type(options.guiNameIsMap) ~= "boolean" then
740 return '"guiNameIsMap" must be a boolean or nil', position
741 elseif options.type ~= "toggle" then
742 return 'if "guiNameIsMap" is true, then "type" must be set to \'toggle\'', position
743 elseif type(options.map) ~= "table" then
744 return '"map" must be a table', position
745 end
746 end
747 if options.message and type(options.message) ~= "string" then
748 return '"message" must be a string or nil', position
749 end
750 if options.error and type(options.error) ~= "string" then
751 return '"error" must be a string or nil', position
752 end
753 if options.current and type(options.current) ~= "string" then
754 return '"current" must be a string or nil', position
755 end
756 if options.order then
757 if type(options.order) ~= "number" or (-1 < options.order and options.order < 0.999) then
758 return '"order" must be a non-zero number or nil', position
759 end
760 end
761 if options.disabled then
762 if type(options.disabled) ~= "function" and type(options.disabled) ~= "string" and options.disabled ~= true then
763 return '"disabled" must be a function, string, or boolean', position
764 end
765 end
766 if options.cmdHidden then
767 if type(options.cmdHidden) ~= "function" and type(options.cmdHidden) ~= "string" and options.cmdHidden ~= true then
768 return '"cmdHidden" must be a function, string, or boolean', position
769 end
770 end
771 if options.guiHidden then
772 if type(options.guiHidden) ~= "function" and type(options.guiHidden) ~= "string" and options.guiHidden ~= true then
773 return '"guiHidden" must be a function, string, or boolean', position
774 end
775 end
776 if options.hidden then
777 if type(options.hidden) ~= "function" and type(options.hidden) ~= "string" and options.hidden ~= true then
778 return '"hidden" must be a function, string, or boolean', position
779 end
780 end
781 if kind == "text" then
782 if type(options.validate) == "table" then
783 local t = options.validate
784 local iTable = nil
785 for k,v in pairs(t) do
786 if type(k) == "number" then
787 if iTable == nil then
788 iTable = true
789 elseif not iTable then
790 return '"validate" must either have all keys be indexed numbers or strings', position
791 elseif k < 1 or k > table.getn(t) then
792 return '"validate" numeric keys must be indexed properly. >= 1 and <= table.getn', position
793 end
794 else
795 if iTable == nil then
796 iTable = false
797 elseif iTable then
798 return '"validate" must either have all keys be indexed numbers or strings', position
799 end
800 end
801 if type(v) ~= "string" then
802 return '"validate" values must all be strings', position
803 end
804 end
805 else
806 if type(options.usage) ~= "string" then
807 return '"usage" must be a string', position
808 elseif options.validate and type(options.validate) ~= "string" and type(options.validate) ~= "function" then
809 return '"validate" must be a string, function, or table', position
810 end
811 end
812 elseif kind == "range" then
813 if options.min or options.max then
814 if type(options.min) ~= "number" then
815 return '"min" must be a number', position
816 elseif type(options.max) ~= "number" then
817 return '"max" must be a number', position
818 elseif options.min >= options.max then
819 return '"min" must be less than "max"', position
820 end
821 end
822 if options.step then
823 if type(options.step) ~= "number" then
824 return '"step" must be a number', position
825 elseif options.step < 0 then
826 return '"step" must be nonnegative', position
827 end
828 end
829 if options.isPercent and options.isPercent ~= true then
830 return '"isPercent" must either be nil, true, or false', position
831 end
832 elseif kind == "toggle" then
833 if options.map then
834 if type(options.map) ~= "table" then
835 return '"map" must be a table', position
836 elseif type(options.map[true]) ~= "string" then
837 return '"map[true]" must be a string', position
838 elseif type(options.map[false]) ~= "string" then
839 return '"map[false]" must be a string', position
840 end
841 end
842 elseif kind == "color" then
843 if options.hasAlpha and options.hasAlpha ~= true then
844 return '"hasAlpha" must be nil, true, or false', position
845 end
846 elseif kind == "group" then
847 if options.pass and options.pass ~= true then
848 return '"pass" must be nil, true, or false', position
849 end
850 if type(options.args) ~= "table" then
851 return '"args" must be a table', position
852 end
853 for k,v in pairs(options.args) do
854 if type(k) ~= "string" then
855 return '"args" keys must be strings', position
856 elseif string.find(k, "%s") then
857 return string.format('"args" keys must not include spaces. %q is not appropriate.', k), position
858 elseif string.len(k) == 0 then
859 return '"args" keys must not be 0-length strings.', position
860 end
861 if type(v) ~= "table" then
862 return '"args" values must be tables', position and position .. "." .. k or k
863 end
864 local newposition
865 if position then
866 newposition = position .. ".args." .. k
867 else
868 newposition = "args." .. k
869 end
870 local err, pos = validateOptions(v, newposition, baseOptions, options.pass)
871 if err then
872 return err, pos
873 end
874 end
875 end
876 end
877  
878 local validatedOptions
879  
880 local values
881 local mysort_args
882 local mysort
883 local othersort
884 local othersort_validate
885  
886 local baseFunc, currentLevel
887  
888 function Dewdrop:FeedAceOptionsTable(options, difference)
889 self:argCheck(options, 2, "table")
890 self:argCheck(difference, 3, "nil", "number")
891 self:assert(currentLevel, "Cannot call `FeedAceOptionsTable' outside of a Dewdrop declaration")
892 if not difference then
893 difference = 0
894 end
895 if not validatedOptions then
896 validatedOptions = {}
897 end
898 if not validatedOptions[options] then
899 local err, position = validateOptions(options)
900  
901 if err then
902 if position then
903 Dewdrop:error(position .. ": " .. err)
904 else
905 Dewdrop:error(err)
906 end
907 end
908  
909 validatedOptions[options] = true
910 end
911 local level = levels[currentLevel]
912 self:assert(level, "Improper level given")
913 if not values then
914 values = {}
915 else
916 for k,v in pairs(values) do
917 values[k] = nil
918 end
919 table.setn(values, 0)
920 end
921  
922 local current = level
923 while current do
924 if current.num == difference + 1 then
925 break
926 end
927 table.insert(values, current.value)
928 current = levels[current.num - 1]
929 end
930  
931 local realOptions = options
932 local handler = options.handler
933 local passTable
934 local passValue
935 while table.getn(values) > 0 do
936 passTable = options.pass and current or nil
937 local value = table.remove(values)
938 options = options.args and options.args[value]
939 if not options then
940 return
941 end
942 handler = options.handler or handler
943 passValue = passTable and value or nil
944 end
945  
946 if options.type == "group" then
947 for k in pairs(options.args) do
948 table.insert(values, k)
949 end
950 if not mysort then
951 mysort = function(a, b)
952 local alpha, bravo = mysort_args[a], mysort_args[b]
953 local alpha_order = alpha.order or 100
954 local bravo_order = bravo.order or 100
955 local alpha_name = alpha.guiName or alpha.name
956 local bravo_name = bravo.guiName or bravo.name
957 if alpha_order == bravo_order then
958 if not alpha_name then
959 return true
960 elseif not bravo_name then
961 return false
962 else
963 return alpha_name < bravo_name
964 end
965 else
966 if alpha_order < 0 then
967 if bravo_order > 0 then
968 return false
969 end
970 else
971 if bravo_order < 0 then
972 return true
973 end
974 end
975 return alpha_order < bravo_order
976 end
977 end
978 end
979 mysort_args = options.args
980 table.sort(values, mysort)
981 mysort_args = nil
982 local hasBoth = table.getn(values) >= 1 and (options.args[values[1]].order or 100) > 0 and (options.args[values[table.getn(values)]].order or 100) < 0
983 local last_order = 1
984 for _,k in ipairs(values) do
985 local v = options.args[k]
986 local handler = v.handler or handler
987 if hasBoth and last_order > 0 and (v.order or 100) < 0 then
988 hasBoth = false
989 self:AddLine()
990 end
991 local hidden, disabled = v.guiHidden or v.hidden, v.disabled
992 if type(hidden) == "function" then
993 hidden = hidden()
994 elseif type(hidden) == "string" then
995 hidden = handler[hidden](handler)
996 end
997 if not hidden then
998 if type(disabled) == "function" then
999 disabled = disabled()
1000 elseif type(disabled) == "string" then
1001 disabled = handler[disabled](handler)
1002 end
1003 local name = v.guiName or v.name
1004 local desc = v.desc
1005 local tooltipTitle, tooltipText
1006 tooltipTitle = name
1007 if name ~= desc then
1008 tooltipText = desc
1009 end
1010 if v.type == "toggle" then
1011 local checked
1012 if type(v.get) == "function" then
1013 checked = v.get(passValue)
1014 else
1015 if not handler[v.get] then
1016 Dewdrop:error("Handler %q not available", v.get)
1017 end
1018 checked = handler[v.get](handler, passValue)
1019 end
1020 local func, arg1, arg2, arg3
1021 if type(v.set) == "function" then
1022 func = v.set
1023 if passValue ~= nil then
1024 arg1 = passValue
1025 arg2 = not checked
1026 else
1027 arg1 = not checked
1028 end
1029 else
1030 if not handler[v.set] then
1031 Dewdrop:error("Handler %q not available", v.set)
1032 end
1033 func = handler[v.set]
1034 arg1 = handler
1035 if passValue ~= nil then
1036 arg2 = passValue
1037 arg3 = not checked
1038 else
1039 arg2 = not checked
1040 end
1041 end
1042 if v.guiNameIsMap then
1043 checked = checked and true or false
1044 name = string.gsub(tostring(v.map and v.map[checked]), "|c%x%x%x%x%x%x%x%x(.-)|r", "%1")
1045 checked = nil
1046 end
1047 self:AddLine(
1048 'text', name,
1049 'checked', checked,
1050 'func', func,
1051 'arg1', arg1,
1052 'arg2', arg2,
1053 'arg3', arg3,
1054 'disabled', disabled,
1055 'tooltipTitle', tooltipTitle,
1056 'tooltipText', tooltipText
1057 )
1058 elseif v.type == "execute" then
1059 local func, arg1, arg2
1060 if type(v.func) == "function" then
1061 func = v.func
1062 arg1 = passValue
1063 else
1064 if not handler[v.func] then
1065 Dewdrop:error("Handler %q not available", v.func)
1066 end
1067 func = handler[v.func]
1068 arg1 = handler
1069 arg2 = passValue
1070 end
1071 self:AddLine(
1072 'text', name,
1073 'checked', checked,
1074 'func', func,
1075 'arg1', arg1,
1076 'arg2', arg2,
1077 'disabled', disabled,
1078 'tooltipTitle', tooltipTitle,
1079 'tooltipText', tooltipText
1080 )
1081 elseif v.type == "range" then
1082 local sliderValue
1083 if type(v.get) == "function" then
1084 sliderValue = v.get(passValue)
1085 else
1086 if not handler[v.get] then
1087 Dewdrop:error("Handler %q not available", v.get)
1088 end
1089 sliderValue = handler[v.get](handler, passValue)
1090 end
1091 local sliderFunc, sliderArg1, sliderArg2
1092 if type(v.set) == "function" then
1093 sliderFunc = v.set
1094 sliderArg1 = passValue
1095 else
1096 if not handler[v.set] then
1097 Dewdrop:error("Handler %q not available", v.set)
1098 end
1099 sliderFunc = handler[v.set]
1100 sliderArg1 = handler
1101 sliderArg2 = passValue
1102 end
1103 self:AddLine(
1104 'text', name,
1105 'hasArrow', true,
1106 'hasSlider', true,
1107 'sliderMin', v.min or 0,
1108 'sliderMax', v.max or 1,
1109 'sliderStep', v.step or 0,
1110 'sliderIsPercent', v.isPercent or false,
1111 'sliderValue', sliderValue,
1112 'sliderFunc', sliderFunc,
1113 'sliderArg1', sliderArg1,
1114 'sliderArg2', sliderArg2,
1115 'disabled', disabled,
1116 'tooltipTitle', tooltipTitle,
1117 'tooltipText', tooltipText
1118 )
1119 elseif v.type == "color" then
1120 local r,g,b,a
1121 if type(v.get) == "function" then
1122 r,g,b,a = v.get(passValue)
1123 else
1124 if not handler[v.get] then
1125 Dewdrop:error("Handler %q not available", v.get)
1126 end
1127 r,g,b,a = handler[v.get](handler, passValue)
1128 end
1129 local colorFunc, colorArg1, colorArg2
1130 if type(v.set) == "function" then
1131 colorFunc = v.set
1132 colorArg1 = passValue
1133 else
1134 if not handler[v.set] then
1135 Dewdrop:error("Handler %q not available", v.set)
1136 end
1137 colorFunc = handler[v.set]
1138 colorArg1 = handler
1139 colorArg2 = passValue
1140 end
1141 self:AddLine(
1142 'text', name,
1143 'hasArrow', true,
1144 'hasColorSwatch', true,
1145 'r', r,
1146 'g', g,
1147 'b', b,
1148 'opacity', v.hasAlpha and a or nil,
1149 'hasOpacity', v.hasAlpha,
1150 'colorFunc', colorFunc,
1151 'colorArg1', colorArg1,
1152 'colorArg2', colorArg2,
1153 'disabled', disabled,
1154 'tooltipTitle', tooltipTitle,
1155 'tooltipText', tooltipText
1156 )
1157 elseif v.type == "text" then
1158 if type(v.validate) == "table" then
1159 self:AddLine(
1160 'text', name,
1161 'hasArrow', true,
1162 'value', k,
1163 'disabled', disabled,
1164 'tooltipTitle', tooltipTitle,
1165 'tooltipText', tooltipText
1166 )
1167 else
1168 local editBoxText
1169 if type(v.get) == "function" then
1170 editBoxText = v.get(passValue)
1171 elseif v.get == false then
1172 editBoxText = nil
1173 else
1174 if not handler[v.get] then
1175 Dewdrop:error("Handler %q not available", v.get)
1176 end
1177 editBoxText = handler[v.get](handler, passValue)
1178 end
1179 local editBoxFunc, editBoxArg1, editBoxArg2
1180 if type(v.set) == "function" then
1181 editBoxFunc = v.set
1182 editBoxArg1 = passValue
1183 else
1184 if not handler[v.set] then
1185 Dewdrop:error("Handler %q not available", v.set)
1186 end
1187 editBoxFunc = handler[v.set]
1188 editBoxArg1 = handler
1189 editBoxArg2 = passValue
1190 end
1191  
1192 local editBoxValidateFunc, editBoxValidateArg1
1193  
1194 if v.validate then
1195 if type(v.validate) == "function" then
1196 editBoxValidateFunc = v.validate
1197 else
1198 if not handler[v.validate] then
1199 Dewdrop:error("Handler %q not available", v.validate)
1200 end
1201 editBoxValidateFunc = handler[v.validate]
1202 editBoxValidateArg1 = handler
1203 end
1204 end
1205  
1206 self:AddLine(
1207 'text', name,
1208 'hasArrow', true,
1209 'hasEditBox', true,
1210 'editBoxText', editBoxText,
1211 'editBoxFunc', editBoxFunc,
1212 'editBoxArg1', editBoxArg1,
1213 'editBoxArg2', editBoxArg2,
1214 'editBoxValidateFunc', editBoxValidateFunc,
1215 'editBoxValidateArg1', editBoxValidateArg1,
1216 'disabled', disabled,
1217 'tooltipTitle', tooltipTitle,
1218 'tooltipText', tooltipText
1219 )
1220 end
1221 elseif v.type == "group" then
1222 self:AddLine(
1223 'text', name,
1224 'hasArrow', true,
1225 'value', k,
1226 'disabled', disabled,
1227 'tooltipTitle', tooltipTitle,
1228 'tooltipText', tooltipText
1229 )
1230 elseif v.type == "header" then
1231 if name == "" or not name then
1232 self:AddLine()
1233 else
1234 self:AddLine(
1235 'text', name,
1236 'isTitle', true
1237 )
1238 end
1239 end
1240 end
1241 last_order = v.order or 100
1242 end
1243 elseif options.type == "text" and type(options.validate) == "table" then
1244 local current
1245 if type(options.get) == "function" then
1246 current = options.get(passValue)
1247 else
1248 if not handler[options.get] then
1249 Dewdrop:error("Handler %q not available", options.get)
1250 end
1251 current = handler[options.get](handler, passValue)
1252 end
1253 local indexed = true
1254 for k,v in pairs(options.validate) do
1255 if type(k) ~= "number" then
1256 indexed = false
1257 end
1258 table.insert(values, k)
1259 end
1260 if not indexed then
1261 if not othersort then
1262 othersort = function(alpha, bravo)
1263 return othersort_validate[alpha] < othersort_validate[bravo]
1264 end
1265 end
1266 othersort_validate = options.validate
1267 table.sort(values, othersort)
1268 othersort_validate = nil
1269 end
1270 for _,k in ipairs(values) do
1271 local v = options.validate[k]
1272 if type(k) == "number" then
1273 k = v
1274 end
1275 local func, arg1, arg2
1276 if type(options.set) == "function" then
1277 func = options.set
1278 if passValue ~= nil then
1279 arg1 = passValue
1280 arg2 = k
1281 else
1282 arg1 = k
1283 end
1284 else
1285 if not handler[options.set] then
1286 Dewdrop:error("Handler %q not available", options.set)
1287 end
1288 func = handler[options.set]
1289 arg1 = handler
1290 if passValue ~= nil then
1291 arg2 = passValue
1292 arg3 = k
1293 else
1294 arg2 = k
1295 end
1296 end
1297 local checked = (k == current or (type(k) == "string" and type(current) == "string" and string.lower(k) == string.lower(current)))
1298 self:AddLine(
1299 'text', v,
1300 'func', not checked and func or nil,
1301 'arg1', not checked and arg1 or nil,
1302 'arg2', not checked and arg2 or nil,
1303 'arg3', not checked and arg3 or nil,
1304 'isRadio', true,
1305 'checked', checked,
1306 'tooltipTitle', options.guiName or options.name,
1307 'tooltipText', v
1308 )
1309 end
1310 for k in pairs(values) do
1311 values[k] = nil
1312 end
1313 table.setn(values, 0)
1314 else
1315 self:assert(false)
1316 end
1317 return true
1318 end
1319  
1320 function Refresh(self, level)
1321 if type(level) == "number" then
1322 level = levels[level]
1323 end
1324 if not level then
1325 return
1326 end
1327 if baseFunc then
1328 Clear(self, level)
1329 currentLevel = level.num
1330 if type(baseFunc) == "table" then
1331 if currentLevel == 1 then
1332 local handler = baseFunc.handler
1333 if handler then
1334 local name = tostring(handler)
1335 if not string.find(name, '^table:') then
1336 name = string.gsub(name, "|c%x%x%x%x%x%x%x%x(.-)|r", "%1")
1337 self:AddLine(
1338 'text', name,
1339 'isTitle', true
1340 )
1341 end
1342 end
1343 end
1344 self:FeedAceOptionsTable(baseFunc)
1345 if currentLevel == 1 then
1346 self:AddLine(
1347 'text', CLOSE,
1348 'closeWhenClicked', true
1349 )
1350 end
1351 else
1352 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)
1353 end
1354 currentLevel = nil
1355 CheckSize(self, level)
1356 end
1357 end
1358  
1359 function Dewdrop:Refresh(level)
1360 self:argCheck(level, 2, "number")
1361 Refresh(self, levels[level])
1362 end
1363  
1364 function OpenSlider(self, parent)
1365 if not sliderFrame then
1366 sliderFrame = CreateFrame("Frame", nil, UIParent)
1367 sliderFrame:SetWidth(80)
1368 sliderFrame:SetHeight(170)
1369 sliderFrame:SetBackdrop(tmp(
1370 'bgFile', "Interface\\Tooltips\\UI-Tooltip-Background",
1371 'edgeFile', "Interface\\Tooltips\\UI-Tooltip-Border",
1372 'tile', true,
1373 'insets', tmp2(
1374 'left', 5,
1375 'right', 5,
1376 'top', 5,
1377 'bottom', 5
1378 ),
1379 'tileSize', 16,
1380 'edgeSize', 16
1381 ))
1382 sliderFrame:SetFrameStrata("FULLSCREEN_DIALOG")
1383 if sliderFrame.SetTopLevel then
1384 sliderFrame:SetTopLevel(true)
1385 end
1386 sliderFrame:SetBackdropBorderColor(TOOLTIP_DEFAULT_COLOR.r, TOOLTIP_DEFAULT_COLOR.g, TOOLTIP_DEFAULT_COLOR.b)
1387 sliderFrame:SetBackdropColor(TOOLTIP_DEFAULT_BACKGROUND_COLOR.r, TOOLTIP_DEFAULT_BACKGROUND_COLOR.g, TOOLTIP_DEFAULT_BACKGROUND_COLOR.b)
1388 sliderFrame:EnableMouse(true)
1389 sliderFrame:Hide()
1390 sliderFrame:SetPoint("CENTER", UIParent, "CENTER")
1391 local slider = CreateFrame("Slider", nil, sliderFrame)
1392 sliderFrame.slider = slider
1393 slider:SetOrientation("VERTICAL")
1394 slider:SetMinMaxValues(0, 1)
1395 slider:SetValueStep(0.01)
1396 slider:SetValue(0.5)
1397 slider:SetWidth(16)
1398 slider:SetHeight(128)
1399 slider:SetPoint("LEFT", sliderFrame, "LEFT", 15, 0)
1400 slider:SetBackdrop(tmp(
1401 'bgFile', "Interface\\Buttons\\UI-SliderBar-Background",
1402 'edgeFile', "Interface\\Buttons\\UI-SliderBar-Border",
1403 'tile', true,
1404 'edgeSize', 8,
1405 'tileSize', 8,
1406 'insets', tmp2(
1407 'left', 3,
1408 'right', 3,
1409 'top', 3,
1410 'bottom', 3
1411 )
1412 ))
1413 local texture = slider:CreateTexture()
1414 slider:SetThumbTexture("Interface\\Buttons\\UI-SliderBar-Button-Vertical")
1415 local text = slider:CreateFontString(nil, "ARTWORK")
1416 sliderFrame.topText = text
1417 text:SetFontObject(GameFontGreenSmall)
1418 text:SetText("100%")
1419 text:SetPoint("BOTTOM", slider, "TOP")
1420 local text = slider:CreateFontString(nil, "ARTWORK")
1421 sliderFrame.bottomText = text
1422 text:SetFontObject(GameFontGreenSmall)
1423 text:SetText("0%")
1424 text:SetPoint("TOP", slider, "BOTTOM")
1425 local text = slider:CreateFontString(nil, "ARTWORK")
1426 sliderFrame.currentText = text
1427 text:SetFontObject(GameFontHighlightSmall)
1428 text:SetText("50%")
1429 text:SetPoint("LEFT", slider, "RIGHT")
1430 text:SetPoint("RIGHT", sliderFrame, "RIGHT", -6, 0)
1431 text:SetJustifyH("CENTER")
1432 local changed = false
1433 local inside = false
1434 slider:SetScript("OnValueChanged", function()
1435 if sliderFrame.changing then
1436 return
1437 end
1438 changed = true
1439 local done = false
1440 if sliderFrame.parent then
1441 if sliderFrame.parent.sliderFunc then
1442 local min = sliderFrame.parent.sliderMin or 0
1443 local max = sliderFrame.parent.sliderMax or 1
1444 local step = sliderFrame.parent.sliderStep or (max - min) / 100
1445 local a1,a2,a3,a4 = sliderFrame.parent.sliderArg1, sliderFrame.parent.sliderArg2, sliderFrame.parent.sliderArg3, sliderFrame.parent.sliderArg4
1446 local value = (1 - slider:GetValue()) * (max - min) + min
1447 if step > 0 then
1448 value = math.floor((value - min) / step + 0.5) * step + min
1449 if value > max then
1450 value = max
1451 elseif value < min then
1452 value = min
1453 end
1454 end
1455 local text
1456 if a1 == nil then
1457 text = sliderFrame.parent.sliderFunc(value)
1458 elseif a2 == nil then
1459 text = sliderFrame.parent.sliderFunc(a1, value)
1460 elseif a3 == nil then
1461 text = sliderFrame.parent.sliderFunc(a1, a2, value)
1462 elseif a4 == nil then
1463 text = sliderFrame.parent.sliderFunc(a1, a2, a3, value)
1464 else
1465 text = sliderFrame.parent.sliderFunc(a1, a2, a3, a4, value)
1466 end
1467 if text then
1468 sliderFrame.currentText:SetText(text)
1469 done = true
1470 end
1471 end
1472 end
1473 if not done then
1474 local min = sliderFrame.parent.sliderMin or 0
1475 local max = sliderFrame.parent.sliderMax or 1
1476 local step = sliderFrame.parent.sliderStep or (max - min) / 100
1477 local value = (1 - slider:GetValue()) * (max - min) + min
1478 if step > 0 then
1479 value = math.floor((value - min) / step + 0.5) * step + min
1480 if value > max then
1481 value = max
1482 elseif value < min then
1483 value = min
1484 end
1485 end
1486 if sliderFrame.parent.sliderIsPercent then
1487 sliderFrame.currentText:SetText(string.format("%.0f%%", value * 100))
1488 else
1489 if step < 0.1 then
1490 sliderFrame.currentText:SetText(string.format("%.2f", value))
1491 elseif step < 1 then
1492 sliderFrame.currentText:SetText(string.format("%.1f", value))
1493 else
1494 sliderFrame.currentText:SetText(string.format("%.0f", value))
1495 end
1496 end
1497 end
1498 end)
1499 sliderFrame:SetScript("OnEnter", function()
1500 StopCounting(self, sliderFrame.level)
1501 end)
1502 sliderFrame:SetScript("OnLeave", function()
1503 StartCounting(self, sliderFrame.level)
1504 end)
1505 slider:SetScript("OnMouseDown", function()
1506 sliderFrame.mouseDown = true
1507 end)
1508 slider:SetScript("OnMouseUp", function()
1509 sliderFrame.mouseDown = false
1510 if changed and not inside then
1511 local parent = sliderFrame.parent
1512 for i = 1, sliderFrame.level - 1 do
1513 Refresh(self, levels[i])
1514 end
1515 OpenSlider(self, parent)
1516 end
1517 end)
1518 slider:SetScript("OnEnter", function()
1519 inside = true
1520 StopCounting(self, sliderFrame.level)
1521 end)
1522 slider:SetScript("OnLeave", function()
1523 inside = false
1524 StartCounting(self, sliderFrame.level)
1525 if changed and not sliderFrame.mouseDown then
1526 local parent = sliderFrame.parent
1527 for i = 1, sliderFrame.level - 1 do
1528 Refresh(self, levels[i])
1529 end
1530 OpenSlider(self, parent)
1531 end
1532 end)
1533 end
1534 sliderFrame.parent = parent
1535 sliderFrame.level = parent.level.num + 1
1536 sliderFrame.parentValue = parent.level.value
1537 sliderFrame:SetFrameLevel(parent.level:GetFrameLevel() + 3)
1538 sliderFrame.slider:SetFrameLevel(sliderFrame:GetFrameLevel() + 1)
1539 sliderFrame.changing = true
1540 if not parent.sliderMin or not parent.sliderMax then
1541 return
1542 end
1543 if not parent.sliderValue then
1544 parent.sliderValue = (parent.sliderMin + parent.sliderMax) / 2
1545 end
1546 sliderFrame.slider:SetValue(1 - (parent.sliderValue - parent.sliderMin) / (parent.sliderMax - parent.sliderMin))
1547 sliderFrame.changing = false
1548 sliderFrame.bottomText:SetText(parent.sliderMinText or "0")
1549 sliderFrame.topText:SetText(parent.sliderMaxText or "1")
1550 local text
1551 if parent.sliderFunc then
1552 local a1,a2,a3,a4 = parent.sliderArg1, parent.sliderArg2, parent.sliderArg3, parent.sliderArg4
1553 if a1 == nil then
1554 text = parent.sliderFunc(parent.sliderValue)
1555 elseif a2 == nil then
1556 text = parent.sliderFunc(a1, parent.sliderValue)
1557 elseif a3 == nil then
1558 text = parent.sliderFunc(a1, a2, parent.sliderValue)
1559 elseif a4 == nil then
1560 text = parent.sliderFunc(a1, a2, a3, parent.sliderValue)
1561 else
1562 text = parent.sliderFunc(a1, a2, a3, a4, parent.sliderValue)
1563 end
1564 end
1565 if text then
1566 sliderFrame.currentText:SetText(text)
1567 elseif parent.sliderIsPercent then
1568 sliderFrame.currentText:SetText(string.format("%.0f%%", parent.sliderValue * 100))
1569 else
1570 sliderFrame.currentText:SetText(parent.sliderValue)
1571 end
1572  
1573 local level = parent.level
1574 sliderFrame:Show()
1575 sliderFrame:ClearAllPoints()
1576 if level.lastDirection == "RIGHT" then
1577 if level.lastVDirection == "DOWN" then
1578 sliderFrame:SetPoint("TOPLEFT", parent, "TOPRIGHT", 5, 10)
1579 else
1580 sliderFrame:SetPoint("BOTTOMLEFT", parent, "BOTTOMRIGHT", 5, -10)
1581 end
1582 else
1583 if level.lastVDirection == "DOWN" then
1584 sliderFrame:SetPoint("TOPRIGHT", parent, "TOPLEFT", -5, 10)
1585 else
1586 sliderFrame:SetPoint("BOTTOMRIGHT", parent, "BOTTOMLEFT", -5, -10)
1587 end
1588 end
1589 local dirty
1590 if level.lastDirection == "RIGHT" then
1591 if sliderFrame:GetRight() > GetScreenWidth() then
1592 level.lastDirection = "LEFT"
1593 dirty = true
1594 end
1595 elseif sliderFrame:GetLeft() < 0 then
1596 level.lastDirection = "RIGHT"
1597 dirty = true
1598 end
1599 if level.lastVDirection == "DOWN" then
1600 if sliderFrame:GetBottom() < 0 then
1601 level.lastVDirection = "UP"
1602 dirty = true
1603 end
1604 elseif sliderFrame:GetTop() > GetScreenWidth() then
1605 level.lastVDirection = "DOWN"
1606 dirty = true
1607 end
1608 if dirty then
1609 sliderFrame:ClearAllPoints()
1610 if level.lastDirection == "RIGHT" then
1611 if level.lastVDirection == "DOWN" then
1612 sliderFrame:SetPoint("TOPLEFT", parent, "TOPRIGHT", 5, 10)
1613 else
1614 sliderFrame:SetPoint("BOTTOMLEFT", parent, "BOTTOMRIGHT", 5, -10)
1615 end
1616 else
1617 if level.lastVDirection == "DOWN" then
1618 sliderFrame:SetPoint("TOPRIGHT", parent, "TOPLEFT", -5, 10)
1619 else
1620 sliderFrame:SetPoint("BOTTOMRIGHT", parent, "BOTTOMLEFT", -5, -10)
1621 end
1622 end
1623 end
1624 local left, bottom = sliderFrame:GetLeft(), sliderFrame:GetBottom()
1625 sliderFrame:ClearAllPoints()
1626 sliderFrame:SetPoint("BOTTOMLEFT", UIParent, "BOTTOMLEFT", left, bottom)
1627 if mod(level.num, 5) == 0 then
1628 local left, bottom = level:GetLeft(), level:GetBottom()
1629 level:ClearAllPoints()
1630 level:SetPoint("BOTTOMLEFT", UIParent, "BOTTOMLEFT", left, bottom)
1631 end
1632 end
1633  
1634 function OpenEditBox(self, parent)
1635 if not editBoxFrame then
1636 editBoxFrame = CreateFrame("Frame", nil, UIParent)
1637 editBoxFrame:SetWidth(200)
1638 editBoxFrame:SetHeight(40)
1639 editBoxFrame:SetBackdrop(tmp(
1640 'bgFile', "Interface\\Tooltips\\UI-Tooltip-Background",
1641 'edgeFile', "Interface\\Tooltips\\UI-Tooltip-Border",
1642 'tile', true,
1643 'insets', tmp2(
1644 'left', 5,
1645 'right', 5,
1646 'top', 5,
1647 'bottom', 5
1648 ),
1649 'tileSize', 16,
1650 'edgeSize', 16
1651 ))
1652 editBoxFrame:SetFrameStrata("FULLSCREEN_DIALOG")
1653 if editBoxFrame.SetTopLevel then
1654 editBoxFrame:SetTopLevel(true)
1655 end
1656 editBoxFrame:SetBackdropBorderColor(TOOLTIP_DEFAULT_COLOR.r, TOOLTIP_DEFAULT_COLOR.g, TOOLTIP_DEFAULT_COLOR.b)
1657 editBoxFrame:SetBackdropColor(TOOLTIP_DEFAULT_BACKGROUND_COLOR.r, TOOLTIP_DEFAULT_BACKGROUND_COLOR.g, TOOLTIP_DEFAULT_BACKGROUND_COLOR.b)
1658 editBoxFrame:EnableMouse(true)
1659 editBoxFrame:Hide()
1660 editBoxFrame:SetPoint("CENTER", UIParent, "CENTER")
1661  
1662 local editBox = CreateFrame("EditBox", nil, editBoxFrame)
1663 editBoxFrame.editBox = editBox
1664 editBox:SetFontObject(ChatFontNormal)
1665 editBox:SetWidth(160)
1666 editBox:SetHeight(13)
1667 editBox:SetPoint("CENTER", editBoxFrame, "CENTER", 0, 0)
1668  
1669 local left = editBox:CreateTexture(nil, "BACKGROUND")
1670 left:SetTexture("Interface\\ChatFrame\\UI-ChatInputBorder-Left")
1671 left:SetTexCoord(0, 100 / 256, 0, 1)
1672 left:SetWidth(100)
1673 left:SetHeight(32)
1674 left:SetPoint("LEFT", editBox, "LEFT", -10, 0)
1675 local right = editBox:CreateTexture(nil, "BACKGROUND")
1676 right:SetTexture("Interface\\ChatFrame\\UI-ChatInputBorder-Right")
1677 right:SetTexCoord(156/256, 1, 0, 1)
1678 right:SetWidth(100)
1679 right:SetHeight(32)
1680 right:SetPoint("RIGHT", editBox, "RIGHT", 10, 0)
1681  
1682 editBox:SetScript("OnEnterPressed", function()
1683 if editBoxFrame.parent and editBoxFrame.parent.editBoxValidateFunc then
1684 local a1,a2,a3,a4 = editBoxFrame.parent.editBoxValidateArg1, editBoxFrame.parent.editBoxValidateArg2, editBoxFrame.parent.editBoxValidateArg3, editBoxFrame.parent.editBoxValidateArg4
1685  
1686 local result
1687 if a1 == nil then
1688 result = editBoxFrame.parent.editBoxValidateFunc(editBox:GetText() or "")
1689 elseif a2 == nil then
1690 result = editBoxFrame.parent.editBoxValidateFunc(a1, editBox:GetText() or "")
1691 elseif a3 == nil then
1692 result = editBoxFrame.parent.editBoxValidateFunc(a1, a2, editBox:GetText() or "")
1693 elseif a4 == nil then
1694 result = editBoxFrame.parent.editBoxValidateFunc(a1, a2, a3, editBox:GetText() or "")
1695 else
1696 result = editBoxFrame.parent.editBoxValidateFunc(a1, a2, a3, a4, editBox:GetText() or "")
1697 end
1698 if not result then
1699 message("Validation error: [" .. tostring(text) .. "]")
1700 return
1701 end
1702 end
1703 if editBoxFrame.parent and editBoxFrame.parent.editBoxFunc then
1704 local a1,a2,a3,a4 = editBoxFrame.parent.editBoxArg1, editBoxFrame.parent.editBoxArg2, editBoxFrame.parent.editBoxArg3, editBoxFrame.parent.editBoxArg4
1705 if a1 == nil then
1706 editBoxFrame.parent.editBoxFunc(editBox:GetText() or "")
1707 elseif a2 == nil then
1708 editBoxFrame.parent.editBoxFunc(a1, editBox:GetText() or "")
1709 elseif a3 == nil then
1710 editBoxFrame.parent.editBoxFunc(a1, a2, editBox:GetText() or "")
1711 elseif a4 == nil then
1712 editBoxFrame.parent.editBoxFunc(a1, a2, a3, editBox:GetText() or "")
1713 else
1714 editBoxFrame.parent.editBoxFunc(a1, a2, a3, a4, editBox:GetText() or "")
1715 end
1716 end
1717 self:Close(editBoxFrame.level)
1718 for i = 1, editBoxFrame.level - 1 do
1719 Refresh(self, levels[i])
1720 end
1721 end)
1722 editBox:SetScript("OnEscapePressed", function()
1723 self:Close(editBoxFrame.level)
1724 end)
1725 local changing = false
1726 local skipNext = false
1727  
1728 function editBox:SpecialSetText(text)
1729 local oldText = editBox:GetText() or ""
1730 if not text then
1731 text = ""
1732 end
1733 if text ~= oldText then
1734 changing = true
1735 self:SetText(text)
1736 changing = false
1737 skipNext = true
1738 end
1739 end
1740  
1741 editBox:SetScript("OnTextChanged", function()
1742 if skipNext then
1743 skipNext = false
1744 elseif not changing and editBoxFrame.parent and editBoxFrame.parent.editBoxChangeFunc then
1745 local a1,a2,a3,a4 = editBoxFrame.parent.editBoxChangeArg1, editBoxFrame.parent.editBoxChangeArg2, editBoxFrame.parent.editBoxChangeArg3, editBoxFrame.parent.editBoxChangeArg4
1746 local text
1747 if a1 == nil then
1748 text = editBoxFrame.parent.editBoxChangeFunc(editBox:GetText() or "")
1749 elseif a2 == nil then
1750 text = editBoxFrame.parent.editBoxChangeFunc(a1, editBox:GetText() or "")
1751 elseif a3 == nil then
1752 text = editBoxFrame.parent.editBoxChangeFunc(a1, a2, editBox:GetText() or "")
1753 elseif a4 == nil then
1754 text = editBoxFrame.parent.editBoxChangeFunc(a1, a2, a3, editBox:GetText() or "")
1755 else
1756 text = editBoxFrame.parent.editBoxChangeFunc(a1, a2, a3, a4, editBox:GetText() or "")
1757 end
1758 if text then
1759 editBox:SpecialSetText(text)
1760 end
1761 end
1762 end)
1763 editBoxFrame:SetScript("OnEnter", function()
1764 StopCounting(self, editBoxFrame.level)
1765 end)
1766 editBoxFrame:SetScript("OnLeave", function()
1767 StartCounting(self, editBoxFrame.level)
1768 end)
1769 editBox:SetScript("OnEnter", function()
1770 StopCounting(self, editBoxFrame.level)
1771 end)
1772 editBox:SetScript("OnLeave", function()
1773 StartCounting(self, editBoxFrame.level)
1774 end)
1775 end
1776 editBoxFrame.parent = parent
1777 editBoxFrame.level = parent.level.num + 1
1778 editBoxFrame.parentValue = parent.level.value
1779 editBoxFrame:SetFrameLevel(parent.level:GetFrameLevel() + 3)
1780 editBoxFrame.editBox:SetFrameLevel(editBoxFrame:GetFrameLevel() + 1)
1781 editBoxFrame.editBox:SpecialSetText(parent.editBoxText)
1782  
1783 local level = parent.level
1784 editBoxFrame:Show()
1785 editBoxFrame:ClearAllPoints()
1786 if level.lastDirection == "RIGHT" then
1787 if level.lastVDirection == "DOWN" then
1788 editBoxFrame:SetPoint("TOPLEFT", parent, "TOPRIGHT", 5, 10)
1789 else
1790 editBoxFrame:SetPoint("BOTTOMLEFT", parent, "BOTTOMRIGHT", 5, -10)
1791 end
1792 else
1793 if level.lastVDirection == "DOWN" then
1794 editBoxFrame:SetPoint("TOPRIGHT", parent, "TOPLEFT", -5, 10)
1795 else
1796 editBoxFrame:SetPoint("BOTTOMRIGHT", parent, "BOTTOMLEFT", -5, -10)
1797 end
1798 end
1799 local dirty
1800 if level.lastDirection == "RIGHT" then
1801 if editBoxFrame:GetRight() > GetScreenWidth() then
1802 level.lastDirection = "LEFT"
1803 dirty = true
1804 end
1805 elseif editBoxFrame:GetLeft() < 0 then
1806 level.lastDirection = "RIGHT"
1807 dirty = true
1808 end
1809 if level.lastVDirection == "DOWN" then
1810 if editBoxFrame:GetBottom() < 0 then
1811 level.lastVDirection = "UP"
1812 dirty = true
1813 end
1814 elseif editBoxFrame:GetTop() > GetScreenWidth() then
1815 level.lastVDirection = "DOWN"
1816 dirty = true
1817 end
1818 if dirty then
1819 editBoxFrame:ClearAllPoints()
1820 if level.lastDirection == "RIGHT" then
1821 if level.lastVDirection == "DOWN" then
1822 editBoxFrame:SetPoint("TOPLEFT", parent, "TOPRIGHT", 5, 10)
1823 else
1824 editBoxFrame:SetPoint("BOTTOMLEFT", parent, "BOTTOMRIGHT", 5, -10)
1825 end
1826 else
1827 if level.lastVDirection == "DOWN" then
1828 editBoxFrame:SetPoint("TOPRIGHT", parent, "TOPLEFT", -5, 10)
1829 else
1830 editBoxFrame:SetPoint("BOTTOMRIGHT", parent, "BOTTOMLEFT", -5, -10)
1831 end
1832 end
1833 end
1834 local left, bottom = editBoxFrame:GetLeft(), editBoxFrame:GetBottom()
1835 editBoxFrame:ClearAllPoints()
1836 editBoxFrame:SetPoint("BOTTOMLEFT", UIParent, "BOTTOMLEFT", left, bottom)
1837 if mod(level.num, 5) == 0 then
1838 local left, bottom = level:GetLeft(), level:GetBottom()
1839 level:ClearAllPoints()
1840 level:SetPoint("BOTTOMLEFT", UIParent, "BOTTOMLEFT", left, bottom)
1841 end
1842 end
1843  
1844 function Dewdrop:IsOpen(parent)
1845 self:argCheck(parent, 2, "table", "nil")
1846 return levels[1] and levels[1]:IsShown() and (not parent or parent == levels[1].parent or parent == levels[1]:GetParent())
1847 end
1848  
1849 function Dewdrop:GetOpenedParent()
1850 return (levels[1] and levels[1]:IsShown()) and (levels[1].parent or levels[1]:GetParent())
1851 end
1852  
1853 function Open(self, parent, func, level, value, point, relativePoint, cursorX, cursorY)
1854 self:Close(level)
1855 if DewdropLib then
1856 local d = DewdropLib:GetInstance('1.0')
1857 local ret, val = pcall(d, IsOpen, d)
1858 if ret and val then
1859 DewdropLib:GetInstance('1.0'):Close()
1860 end
1861 end
1862 parent:GetCenter()
1863 local frame = AcquireLevel(self, level)
1864 if level == 1 then
1865 frame.lastDirection = "RIGHT"
1866 frame.lastVDirection = "DOWN"
1867 else
1868 frame.lastDirection = levels[level - 1].lastDirection
1869 frame.lastVDirection = levels[level - 1].lastVDirection
1870 end
1871 frame:SetFrameStrata("FULLSCREEN_DIALOG")
1872 frame:ClearAllPoints()
1873 frame.parent = parent
1874 frame:SetPoint("LEFT", UIParent, "RIGHT", 10000, 0)
1875 frame:Show()
1876 if level == 1 then
1877 baseFunc = func
1878 end
1879 levels[level].value = value
1880 relativePoint = relativePoint or point
1881 Refresh(self, levels[level])
1882 if point or (cursorX and cursorY) then
1883 frame:ClearAllPoints()
1884 if cursorX and cursorY then
1885 local curX, curY = GetScaledCursorPosition()
1886 if curY < GetScreenHeight() / 2 then
1887 point, relativePoint = "BOTTOM", "BOTTOM"
1888 else
1889 point, relativePoint = "TOP", "TOP"
1890 end
1891 if curX < GetScreenWidth() / 2 then
1892 point, relativePoint = point .. "LEFT", relativePoint .. "RIGHT"
1893 else
1894 point, relativePoint = point .. "RIGHT", relativePoint .. "LEFT"
1895 end
1896 end
1897 frame:SetPoint(point, parent, relativePoint)
1898 if cursorX and cursorY then
1899 local left = frame:GetLeft()
1900 local width = frame:GetWidth()
1901 local bottom = frame:GetBottom()
1902 local height = frame:GetHeight()
1903 local curX, curY = GetScaledCursorPosition()
1904 frame:ClearAllPoints()
1905 relativePoint = relativePoint or point
1906 if point == "BOTTOM" or point == "TOP" then
1907 if curX < GetScreenWidth() / 2 then
1908 point = point .. "LEFT"
1909 else
1910 point = point .. "RIGHT"
1911 end
1912 elseif point == "CENTER" then
1913 if curX < GetScreenWidth() / 2 then
1914 point = "LEFT"
1915 else
1916 point = "RIGHT"
1917 end
1918 end
1919 local xOffset, yOffset = 0, 0
1920 if curY > GetScreenHeight() / 2 then
1921 yOffset = -height
1922 end
1923 if curX > GetScreenWidth() / 2 then
1924 xOffset = -width
1925 end
1926 frame:SetPoint(point, parent, relativePoint, curX - left + xOffset, curY - bottom + yOffset)
1927 if level == 1 then
1928 frame.lastDirection = "RIGHT"
1929 end
1930 elseif cursorX then
1931 local left = frame:GetLeft()
1932 local width = frame:GetWidth()
1933 local curX, curY = GetScaledCursorPosition()
1934 frame:ClearAllPoints()
1935 relativePoint = relativePoint or point
1936 if point == "BOTTOM" or point == "TOP" then
1937 if curX < GetScreenWidth() / 2 then
1938 point = point .. "LEFT"
1939 else
1940 point = point .. "RIGHT"
1941 end
1942 elseif point == "CENTER" then
1943 if curX < GetScreenWidth() / 2 then
1944 point = "LEFT"
1945 else
1946 point = "RIGHT"
1947 end
1948 end
1949 frame:SetPoint(point, parent, relativePoint, curX - left - width / 2, 0)
1950 if level == 1 then
1951 frame.lastDirection = "RIGHT"
1952 end
1953 elseif cursorY then
1954 local bottom = frame:GetBottom()
1955 local height = frame:GetHeight()
1956 local curX, curY = GetScaledCursorPosition()
1957 frame:ClearAllPoints()
1958 relativePoint = relativePoint or point
1959 if point == "LEFT" or point == "RIGHT" then
1960 if curX < GetScreenHeight() / 2 then
1961 point = point .. "BOTTOM"
1962 else
1963 point = point .. "TOP"
1964 end
1965 elseif point == "CENTER" then
1966 if curX < GetScreenHeight() / 2 then
1967 point = "BOTTOM"
1968 else
1969 point = "TOP"
1970 end
1971 end
1972 frame:SetPoint(point, parent, relativePoint, 0, curY - bottom - height / 2)
1973 if level == 1 then
1974 frame.lastDirection = "DOWN"
1975 end
1976 end
1977 if (strsub(point, 1, 3) ~= strsub(relativePoint, 1, 3)) then
1978 if frame:GetBottom() < 0 then
1979 local point, parent, relativePoint, x, y = frame:GetPoint(1)
1980 local change = GetScreenHeight() - frame:GetTop()
1981 local otherChange = -frame:GetBottom()
1982 if otherChange < change then
1983 change = otherChange
1984 end
1985 frame:SetPoint(point, parent, relativePoint, x, y + change)
1986 elseif frame:GetTop() > GetScreenHeight() then
1987 local point, parent, relativePoint, x, y = frame:GetPoint(1)
1988 local change = GetScreenHeight() - frame:GetTop()
1989 local otherChange = -frame:GetBottom()
1990 if otherChange < change then
1991 change = otherChange
1992 end
1993 frame:SetPoint(point, parent, relativePoint, x, y + change)
1994 end
1995 end
1996 end
1997 CheckDualMonitor(self, frame)
1998 StartCounting(self, level)
1999 end
2000  
2001 function Dewdrop:IsRegistered(parent)
2002 self:argCheck(parent, 2, "table")
2003 return not not self.registry[parent]
2004 end
2005  
2006 function Dewdrop:Register(parent, k1, v1, k2, v2, k3, v3, k4, v4, k5, v5, k6, v6, k7, v7, k8, v8, k9, v9, k10, v10, k11, v11, k12, v12, k13, v13, k14, v14, k15, v15, k16, v16, k17, v17, k18, v18, k19, v19, k20, v20)
2007 self:argCheck(parent, 2, "table")
2008 if self.registry[parent] then
2009 self:Unregister(parent)
2010 end
2011 local info = new(k1, v1, k2, v2, k3, v3, k4, v4, k5, v5, k6, v6, k7, v7, k8, v8, k9, v9, k10, v10, k11, v11, k12, v12, k13, v13, k14, v14, k15, v15, k16, v16, k17, v17, k18, v18, k19, v19, k20, v20)
2012 if type(info.children) == "table" then
2013 local err, position = validateOptions(info.children)
2014  
2015 if err then
2016 if position then
2017 Dewdrop:error(position .. ": " .. err)
2018 else
2019 Dewdrop:error(err)
2020 end
2021 end
2022 end
2023 self.registry[parent] = info
2024 if not info.dontHook and not self.onceRegistered[parent] then
2025 if parent:HasScript("OnMouseUp") then
2026 local script = parent:GetScript("OnMouseUp")
2027 parent:SetScript("OnMouseUp", function()
2028 if script then
2029 script()
2030 end
2031 if arg1 == "RightButton" and self.registry[parent] then
2032 if self:IsOpen(parent) then
2033 self:Close()
2034 else
2035 self:Open(parent)
2036 end
2037 end
2038 end)
2039 end
2040 if parent:HasScript("OnMouseDown") then
2041 local script = parent:GetScript("OnMouseDown")
2042 parent:SetScript("OnMouseDown", function()
2043 if script then
2044 script()
2045 end
2046 if self.registry[parent] then
2047 self:Close()
2048 end
2049 end)
2050 end
2051 end
2052 self.onceRegistered[parent] = true
2053 end
2054  
2055 function Dewdrop:Unregister(parent)
2056 self:argCheck(parent, 2, "table")
2057 self.registry[parent] = nil
2058 end
2059  
2060 function Dewdrop:Open(parent, k1, v1, k2, v2, k3, v3, k4, v4, k5, v5, k6, v6, k7, v7, k8, v8, k9, v9, k10, v10, k11, v11, k12, v12, k13, v13, k14, v14, k15, v15, k16, v16, k17, v17, k18, v18, k19, v19, k20, v20)
2061 self:argCheck(parent, 2, "table")
2062 local info
2063 if type(k1) == "table" and k1[0] and k1.IsFrameType and self.registry[k1] then
2064 info = tmp()
2065 for k,v in pairs(self.registry[k1]) do
2066 info[k] = v
2067 end
2068 else
2069 info = tmp(k1, v1, k2, v2, k3, v3, k4, v4, k5, v5, k6, v6, k7, v7, k8, v8, k9, v9, k10, v10, k11, v11, k12, v12, k13, v13, k14, v14, k15, v15, k16, v16, k17, v17, k18, v18, k19, v19, k20, v20)
2070 if self.registry[parent] then
2071 for k,v in pairs(self.registry[parent]) do
2072 if info[k] == nil then
2073 info[k] = v
2074 end
2075 end
2076 end
2077 end
2078 local point = info.point
2079 local relativePoint = info.relativePoint
2080 local cursorX = info.cursorX
2081 local cursorY = info.cursorY
2082 if type(point) == "function" then
2083 local b
2084 point, b = point(parent)
2085 if b then
2086 relativePoint = b
2087 end
2088 end
2089 if type(relativePoint) == "function" then
2090 relativePoint = relativePoint(parent)
2091 end
2092 Open(self, parent, info.children, 1, nil, point, relativePoint, cursorX, cursorY)
2093 end
2094  
2095 function Clear(self, level)
2096 if level then
2097 if level.buttons then
2098 for i = table.getn(level.buttons), 1, -1 do
2099 ReleaseButton(self, level, i)
2100 end
2101 end
2102 end
2103 end
2104  
2105 function Dewdrop:Close(level)
2106 if DropDownList1:IsShown() then
2107 DropDownList1:Hide()
2108 end
2109 if DewdropLib then
2110 local d = DewdropLib:GetInstance('1.0')
2111 local ret, val = pcall(d, IsOpen, d)
2112 if ret and val then
2113 DewdropLib:GetInstance('1.0'):Close()
2114 end
2115 end
2116 self:argCheck(level, 2, "number", "nil")
2117 if not level then
2118 level = 1
2119 end
2120 if level == 1 and levels[level] then
2121 levels[level].parented = false
2122 end
2123 if sliderFrame and sliderFrame.level >= level then
2124 sliderFrame:Hide()
2125 end
2126 if editBoxFrame and editBoxFrame.level >= level then
2127 editBoxFrame:Hide()
2128 end
2129 for i = level, table.getn(levels) do
2130 Clear(self, levels[level])
2131 levels[level]:Hide()
2132 levels[i]:ClearAllPoints()
2133 levels[i]:SetPoint("CENTER", UIParent, "CENTER")
2134 end
2135 end
2136  
2137 function Dewdrop:AddLine(k1, v1, k2, v2, k3, v3, k4, v4, k5, v5, k6, v6, k7, v7, k8, v8, k9, v9, k10, v10, k11, v11, k12, v12, k13, v13, k14, v14, k15, v15, k16, v16, k17, v17, k18, v18, k19, v19, k20, v20)
2138 local info = tmp(k1, v1, k2, v2, k3, v3, k4, v4, k5, v5, k6, v6, k7, v7, k8, v8, k9, v9, k10, v10, k11, v11, k12, v12, k13, v13, k14, v14, k15, v15, k16, v16, k17, v17, k18, v18, k19, v19, k20, v20)
2139 local level = info.level or currentLevel
2140 info.level = nil
2141 local button = AcquireButton(self, level)
2142 if not next(info) then
2143 info.disabled = true
2144 end
2145 button.disabled = info.isTitle or info.notClickable or info.disabled
2146 button.isTitle = info.isTitle
2147 button.notClickable = info.notClickable
2148 if button.isTitle then
2149 button.text:SetFontObject(GameFontNormalSmall)
2150 elseif button.notClickable then
2151 button.text:SetFontObject(GameFontHighlightSmall)
2152 elseif button.disabled then
2153 button.text:SetFontObject(GameFontDisableSmall)
2154 else
2155 button.text:SetFontObject(GameFontHighlightSmall)
2156 end
2157 if info.disabled then
2158 button.arrow:SetDesaturated(true)
2159 button.check:SetDesaturated(true)
2160 else
2161 button.arrow:SetDesaturated(false)
2162 button.check:SetDesaturated(false)
2163 end
2164 if info.textR and info.textG and info.textB then
2165 button.textR = info.textR
2166 button.textG = info.textG
2167 button.textB = info.textB
2168 button.text:SetTextColor(button.textR, button.textG, button.textB)
2169 else
2170 button.text:SetTextColor(button.text:GetFontObject():GetTextColor())
2171 end
2172 button.notCheckable = info.notCheckable
2173 button.text:SetPoint("LEFT", button, "LEFT", button.notCheckable and 0 or 24, 0)
2174 button.checked = not info.notCheckable and info.checked
2175 button.isRadio = not info.notCheckable and info.isRadio
2176 if info.isRadio then
2177 button.check:Show()
2178 button.check:SetTexture(info.checkIcon or "Interface\\Buttons\\UI-RadioButton")
2179 if button.checked then
2180 button.check:SetTexCoord(0.25, 0.5, 0, 1)
2181 button.check:SetVertexColor(1, 1, 1, 1)
2182 else
2183 button.check:SetTexCoord(0, 0.25, 0, 1)
2184 button.check:SetVertexColor(1, 1, 1, 0.5)
2185 end
2186 button.radioHighlight:SetTexture(info.checkIcon or "Interface\\Buttons\\UI-RadioButton")
2187 button.check:SetWidth(16)
2188 button.check:SetHeight(16)
2189 else
2190 if button.checked then
2191 if info.checkIcon then
2192 button.check:SetWidth(16)
2193 button.check:SetHeight(16)
2194 button.check:SetTexture(info.checkIcon)
2195 if string.sub(info.checkIcon, 1, 16) == "Interface\\Icons\\" then
2196 button.check:SetTexCoord(0.05, 0.95, 0.05, 0.95)
2197 else
2198 button.check:SetTexCoord(0, 1, 0, 1)
2199 end
2200 else
2201 button.check:SetWidth(24)
2202 button.check:SetHeight(24)
2203 button.check:SetTexture("Interface\\Buttons\\UI-CheckBox-Check")
2204 button.check:SetTexCoord(0, 1, 0, 1)
2205 end
2206 button.check:SetVertexColor(1, 1, 1, 1)
2207 else
2208 button.check:SetVertexColor(1, 1, 1, 0)
2209 end
2210 end
2211 if not button.disabled then
2212 button.func = info.func
2213 end
2214 button.hasColorSwatch = info.hasColorSwatch
2215 if button.hasColorSwatch then
2216 button.colorSwatch:Show()
2217 button.colorSwatch.texture:Show()
2218 button.r = info.r or 1
2219 button.g = info.g or 1
2220 button.b = info.b or 1
2221 button.colorSwatch.texture:SetTexture(button.r, button.g, button.b)
2222 button.checked = false
2223 button.func = nil
2224 button.colorFunc = info.colorFunc
2225 button.colorArg1 = info.colorArg1
2226 button.colorArg2 = info.colorArg2
2227 button.colorArg3 = info.colorArg3
2228 button.colorArg4 = info.colorArg4
2229 button.hasOpacity = info.hasOpacity
2230 button.opacity = info.opacity or 1
2231 else
2232 button.colorSwatch:Hide()
2233 button.colorSwatch.texture:Hide()
2234 end
2235 button.hasArrow = not button.hasColorSwatch and (info.value or info.hasSlider or info.hasEditBox) and info.hasArrow
2236 if button.hasArrow then
2237 button.arrow:SetAlpha(1)
2238 if info.hasSlider then
2239 button.hasSlider = true
2240 button.sliderMin = info.sliderMin or 0
2241 button.sliderMax = info.sliderMax or 1
2242 button.sliderStep = info.sliderStep or 0
2243 button.sliderIsPercent = info.sliderIsPercent and true or false
2244 button.sliderMinText = info.sliderMinText or button.sliderIsPercent and string.format("%.0f%%", button.sliderMin * 100) or button.sliderMin
2245 button.sliderMaxText = info.sliderMaxText or button.sliderIsPercent and string.format("%.0f%%", button.sliderMax * 100) or button.sliderMax
2246 button.sliderFunc = info.sliderFunc
2247 button.sliderValue = info.sliderValue
2248 button.sliderArg1 = info.sliderArg1
2249 button.sliderArg2 = info.sliderArg2
2250 button.sliderArg3 = info.sliderArg3
2251 button.sliderArg4 = info.sliderArg4
2252 elseif info.hasEditBox then
2253 button.hasEditBox = true
2254 button.editBoxText = info.editBoxText or ""
2255 button.editBoxFunc = info.editBoxFunc
2256 button.editBoxArg1 = info.editBoxArg1
2257 button.editBoxArg2 = info.editBoxArg2
2258 button.editBoxArg3 = info.editBoxArg3
2259 button.editBoxArg4 = info.editBoxArg4
2260 button.editBoxChangeFunc = info.editBoxChangeFunc
2261 button.editBoxChangeArg1 = info.editBoxChangeArg1
2262 button.editBoxChangeArg2 = info.editBoxChangeArg2
2263 button.editBoxChangeArg3 = info.editBoxChangeArg3
2264 button.editBoxChangeArg4 = info.editBoxChangeArg4
2265 button.editBoxValidateFunc = info.editBoxValidateFunc
2266 button.editBoxValidateArg1 = info.editBoxValidateArg1
2267 button.editBoxValidateArg2 = info.editBoxValidateArg2
2268 button.editBoxValidateArg3 = info.editBoxValidateArg3
2269 button.editBoxValidateArg4 = info.editBoxValidateArg4
2270 else
2271 button.value = info.value
2272 end
2273 else
2274 button.arrow:SetAlpha(0)
2275 end
2276 button.arg1 = info.arg1
2277 button.arg2 = info.arg2
2278 button.arg3 = info.arg3
2279 button.arg4 = info.arg4
2280 button.closeWhenClicked = info.closeWhenClicked
2281 button.textHeight = info.textHeight or UIDROPDOWNMENU_DEFAULT_TEXT_HEIGHT or 10
2282 local font,_ = button.text:GetFont()
2283 button.text:SetFont(STANDARD_TEXT_FONT or "Fonts\\FRIZQT__.TTF", button.textHeight)
2284 button:SetHeight(button.textHeight + 6)
2285 button.text:SetPoint("RIGHT", button.arrow, (button.hasColorSwatch or button.hasArrow) and "LEFT" or "RIGHT")
2286 button.text:SetJustifyH(info.justifyH or "LEFT")
2287 button.text:SetText(info.text)
2288 button.tooltipTitle = info.tooltipTitle
2289 button.tooltipText = info.tooltipText
2290 button.tooltipFunc = info.tooltipFunc
2291 button.tooltipArg1 = info.tooltipArg1
2292 button.tooltipArg2 = info.tooltipArg2
2293 button.tooltipArg3 = info.tooltipArg3
2294 button.tooltipArg4 = info.tooltipArg4
2295 if not button.tooltipTitle and not button.tooltipText and not button.tooltipFunc and not info.isTitle then
2296 button.tooltipTitle = info.text
2297 end
2298 if type(button.func) == "string" then
2299 self:assert(type(button.arg1) == "table", "Cannot call method " .. button.func .. " on a non-table")
2300 self:assert(type(button.arg1[button.func]) == "function", "Method " .. button.func .. " nonexistant.")
2301 end
2302 end
2303  
2304 function Dewdrop:InjectAceOptionsTable(handler, options)
2305 self:argCheck(handler, 2, "table")
2306 self:argCheck(options, 3, "table")
2307 if string.lower(tostring(options.type)) ~= "group" then
2308 self:error('Cannot inject into options table argument #3 if its type is not "group"')
2309 end
2310 if options.handler ~= nil and options.handler ~= handler then
2311 self:error("Cannot inject into options table argument #3 if it has a different handler than argument #2")
2312 end
2313 options.handler = handler
2314 local class = handler.class
2315 if not AceLibrary:HasInstance("AceOO-2.0") or not class then
2316 self:error("Cannot retrieve AceOptions tables from a non-object argument #2")
2317 end
2318 while class and class ~= AceLibrary("AceOO-2.0").Class do
2319 if type(class.GetAceOptionsDataTable) == "function" then
2320 local t = class:GetAceOptionsDataTable(handler)
2321 for k,v in pairs(t) do
2322 if type(options.args) ~= "table" then
2323 options.args = {}
2324 end
2325 if options.args[k] == nil then
2326 options.args[k] = v
2327 end
2328 end
2329 end
2330 local mixins = class.mixins
2331 if mixins then
2332 for mixin in pairs(mixins) do
2333 if type(mixin.GetAceOptionsDataTable) == "function" then
2334 local t = mixin:GetAceOptionsDataTable(handler)
2335 for k,v in pairs(t) do
2336 if type(options.args) ~= "table" then
2337 options.args = {}
2338 end
2339 if options.args[k] == nil then
2340 options.args[k] = v
2341 end
2342 end
2343 end
2344 end
2345 end
2346 class = class.super
2347 end
2348 return options
2349 end
2350  
2351 local function activate(self, oldLib, oldDeactivate)
2352 Dewdrop = self
2353 if oldLib and oldLib.registry then
2354 self.registry = oldLib.registry
2355 self.onceRegistered = oldLib.onceRegistered
2356 else
2357 self.registry = {}
2358 self.onceRegistered = {}
2359  
2360 local WorldFrame_OnMouseDown = WorldFrame:GetScript("OnMouseDown")
2361 local WorldFrame_OnMouseUp = WorldFrame:GetScript("OnMouseUp")
2362 local oldX, oldY, clickTime
2363 WorldFrame:SetScript("OnMouseDown", function()
2364 oldX,oldY = GetCursorPosition()
2365 clickTime = GetTime()
2366 if WorldFrame_OnMouseDown then
2367 WorldFrame_OnMouseDown()
2368 end
2369 end)
2370  
2371 WorldFrame:SetScript("OnMouseUp", function()
2372 local x,y = GetCursorPosition()
2373 if not oldX or not oldY or not x or not y or not clickTime then
2374 self:Close()
2375 if WorldFrame_OnMouseUp then
2376 WorldFrame_OnMouseUp()
2377 end
2378 return
2379 end
2380 local d = math.abs(x - oldX) + math.abs(y - oldY)
2381 if d <= 5 and GetTime() - clickTime < 0.5 then
2382 self:Close()
2383 end
2384 if WorldFrame_OnMouseUp then
2385 WorldFrame_OnMouseUp()
2386 end
2387 end)
2388  
2389 local DropDownList1_Show = DropDownList1.Show
2390 function DropDownList1.Show(DropDownList1)
2391 if levels[1] and levels[1]:IsVisible() then
2392 self:Close()
2393 end
2394 DropDownList1_Show(DropDownList1)
2395 end
2396  
2397 local old_HideDropDownMenu = HideDropDownMenu
2398 function HideDropDownMenu(num)
2399 if levels[1] and levels[1]:IsVisible() then
2400 self:Close()
2401 end
2402 old_HideDropDownMenu(num)
2403 end
2404  
2405 local old_CloseDropDownMenus = CloseDropDownMenus
2406 function CloseDropDownMenus(num)
2407 if levels[1] and levels[1]:IsVisible() then
2408 self:Close()
2409 end
2410 old_CloseDropDownMenus(num)
2411 end
2412 end
2413 levels = {}
2414 buttons = {}
2415  
2416 if oldDeactivate then
2417 oldDeactivate(oldLib)
2418 end
2419 end
2420  
2421 AceLibrary:Register(Dewdrop, MAJOR_VERSION, MINOR_VERSION, activate)