Mercurial > wow > hotcorners
comparison Libs/AceConfig-3.0/AceConfigDialog-3.0/AceConfigDialog-3.0.lua @ 0:fc346da3afd9
First commit Hot Corners standalone.
author | tercio |
---|---|
date | Fri, 08 Aug 2014 12:35:17 -0300 |
parents | |
children | c31ee4251181 |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:fc346da3afd9 |
---|---|
1 --- AceConfigDialog-3.0 generates AceGUI-3.0 based windows based on option tables. | |
2 -- @class file | |
3 -- @name AceConfigDialog-3.0 | |
4 -- @release $Id: AceConfigDialog-3.0.lua 1089 2013-09-13 14:32:35Z nevcairiel $ | |
5 | |
6 local LibStub = LibStub | |
7 local MAJOR, MINOR = "AceConfigDialog-3.0", 58 | |
8 local AceConfigDialog, oldminor = LibStub:NewLibrary(MAJOR, MINOR) | |
9 | |
10 if not AceConfigDialog then return end | |
11 | |
12 AceConfigDialog.OpenFrames = AceConfigDialog.OpenFrames or {} | |
13 AceConfigDialog.Status = AceConfigDialog.Status or {} | |
14 AceConfigDialog.frame = AceConfigDialog.frame or CreateFrame("Frame") | |
15 | |
16 AceConfigDialog.frame.apps = AceConfigDialog.frame.apps or {} | |
17 AceConfigDialog.frame.closing = AceConfigDialog.frame.closing or {} | |
18 AceConfigDialog.frame.closeAllOverride = AceConfigDialog.frame.closeAllOverride or {} | |
19 | |
20 local gui = LibStub("AceGUI-3.0") | |
21 local reg = LibStub("AceConfigRegistry-3.0") | |
22 | |
23 -- Lua APIs | |
24 local tconcat, tinsert, tsort, tremove, tsort = table.concat, table.insert, table.sort, table.remove, table.sort | |
25 local strmatch, format = string.match, string.format | |
26 local assert, loadstring, error = assert, loadstring, error | |
27 local pairs, next, select, type, unpack, wipe, ipairs = pairs, next, select, type, unpack, wipe, ipairs | |
28 local rawset, tostring, tonumber = rawset, tostring, tonumber | |
29 local math_min, math_max, math_floor = math.min, math.max, math.floor | |
30 | |
31 -- Global vars/functions that we don't upvalue since they might get hooked, or upgraded | |
32 -- List them here for Mikk's FindGlobals script | |
33 -- GLOBALS: NORMAL_FONT_COLOR, GameTooltip, StaticPopupDialogs, ACCEPT, CANCEL, StaticPopup_Show | |
34 -- GLOBALS: PlaySound, GameFontHighlight, GameFontHighlightSmall, GameFontHighlightLarge | |
35 -- GLOBALS: CloseSpecialWindows, InterfaceOptions_AddCategory, geterrorhandler | |
36 | |
37 local emptyTbl = {} | |
38 | |
39 --[[ | |
40 xpcall safecall implementation | |
41 ]] | |
42 local xpcall = xpcall | |
43 | |
44 local function errorhandler(err) | |
45 return geterrorhandler()(err) | |
46 end | |
47 | |
48 local function CreateDispatcher(argCount) | |
49 local code = [[ | |
50 local xpcall, eh = ... | |
51 local method, ARGS | |
52 local function call() return method(ARGS) end | |
53 | |
54 local function dispatch(func, ...) | |
55 method = func | |
56 if not method then return end | |
57 ARGS = ... | |
58 return xpcall(call, eh) | |
59 end | |
60 | |
61 return dispatch | |
62 ]] | |
63 | |
64 local ARGS = {} | |
65 for i = 1, argCount do ARGS[i] = "arg"..i end | |
66 code = code:gsub("ARGS", tconcat(ARGS, ", ")) | |
67 return assert(loadstring(code, "safecall Dispatcher["..argCount.."]"))(xpcall, errorhandler) | |
68 end | |
69 | |
70 local Dispatchers = setmetatable({}, {__index=function(self, argCount) | |
71 local dispatcher = CreateDispatcher(argCount) | |
72 rawset(self, argCount, dispatcher) | |
73 return dispatcher | |
74 end}) | |
75 Dispatchers[0] = function(func) | |
76 return xpcall(func, errorhandler) | |
77 end | |
78 | |
79 local function safecall(func, ...) | |
80 return Dispatchers[select("#", ...)](func, ...) | |
81 end | |
82 | |
83 local width_multiplier = 170 | |
84 | |
85 --[[ | |
86 Group Types | |
87 Tree - All Descendant Groups will all become nodes on the tree, direct child options will appear above the tree | |
88 - Descendant Groups with inline=true and thier children will not become nodes | |
89 | |
90 Tab - Direct Child Groups will become tabs, direct child options will appear above the tab control | |
91 - Grandchild groups will default to inline unless specified otherwise | |
92 | |
93 Select- Same as Tab but with entries in a dropdown rather than tabs | |
94 | |
95 | |
96 Inline Groups | |
97 - Will not become nodes of a select group, they will be effectivly part of thier parent group seperated by a border | |
98 - If declared on a direct child of a root node of a select group, they will appear above the group container control | |
99 - When a group is displayed inline, all descendants will also be inline members of the group | |
100 | |
101 ]] | |
102 | |
103 -- Recycling functions | |
104 local new, del, copy | |
105 --newcount, delcount,createdcount,cached = 0,0,0 | |
106 do | |
107 local pool = setmetatable({},{__mode="k"}) | |
108 function new() | |
109 --newcount = newcount + 1 | |
110 local t = next(pool) | |
111 if t then | |
112 pool[t] = nil | |
113 return t | |
114 else | |
115 --createdcount = createdcount + 1 | |
116 return {} | |
117 end | |
118 end | |
119 function copy(t) | |
120 local c = new() | |
121 for k, v in pairs(t) do | |
122 c[k] = v | |
123 end | |
124 return c | |
125 end | |
126 function del(t) | |
127 --delcount = delcount + 1 | |
128 wipe(t) | |
129 pool[t] = true | |
130 end | |
131 -- function cached() | |
132 -- local n = 0 | |
133 -- for k in pairs(pool) do | |
134 -- n = n + 1 | |
135 -- end | |
136 -- return n | |
137 -- end | |
138 end | |
139 | |
140 -- picks the first non-nil value and returns it | |
141 local function pickfirstset(...) | |
142 for i=1,select("#",...) do | |
143 if select(i,...)~=nil then | |
144 return select(i,...) | |
145 end | |
146 end | |
147 end | |
148 | |
149 --gets an option from a given group, checking plugins | |
150 local function GetSubOption(group, key) | |
151 if group.plugins then | |
152 for plugin, t in pairs(group.plugins) do | |
153 if t[key] then | |
154 return t[key] | |
155 end | |
156 end | |
157 end | |
158 | |
159 return group.args[key] | |
160 end | |
161 | |
162 --Option member type definitions, used to decide how to access it | |
163 | |
164 --Is the member Inherited from parent options | |
165 local isInherited = { | |
166 set = true, | |
167 get = true, | |
168 func = true, | |
169 confirm = true, | |
170 validate = true, | |
171 disabled = true, | |
172 hidden = true | |
173 } | |
174 | |
175 --Does a string type mean a literal value, instead of the default of a method of the handler | |
176 local stringIsLiteral = { | |
177 name = true, | |
178 desc = true, | |
179 icon = true, | |
180 usage = true, | |
181 width = true, | |
182 image = true, | |
183 fontSize = true, | |
184 } | |
185 | |
186 --Is Never a function or method | |
187 local allIsLiteral = { | |
188 type = true, | |
189 descStyle = true, | |
190 imageWidth = true, | |
191 imageHeight = true, | |
192 } | |
193 | |
194 --gets the value for a member that could be a function | |
195 --function refs are called with an info arg | |
196 --every other type is returned | |
197 local function GetOptionsMemberValue(membername, option, options, path, appName, ...) | |
198 --get definition for the member | |
199 local inherits = isInherited[membername] | |
200 | |
201 | |
202 --get the member of the option, traversing the tree if it can be inherited | |
203 local member | |
204 | |
205 if inherits then | |
206 local group = options | |
207 if group[membername] ~= nil then | |
208 member = group[membername] | |
209 end | |
210 for i = 1, #path do | |
211 group = GetSubOption(group, path[i]) | |
212 if group[membername] ~= nil then | |
213 member = group[membername] | |
214 end | |
215 end | |
216 else | |
217 member = option[membername] | |
218 end | |
219 | |
220 --check if we need to call a functon, or if we have a literal value | |
221 if ( not allIsLiteral[membername] ) and ( type(member) == "function" or ((not stringIsLiteral[membername]) and type(member) == "string") ) then | |
222 --We have a function to call | |
223 local info = new() | |
224 --traverse the options table, picking up the handler and filling the info with the path | |
225 local handler | |
226 local group = options | |
227 handler = group.handler or handler | |
228 | |
229 for i = 1, #path do | |
230 group = GetSubOption(group, path[i]) | |
231 info[i] = path[i] | |
232 handler = group.handler or handler | |
233 end | |
234 | |
235 info.options = options | |
236 info.appName = appName | |
237 info[0] = appName | |
238 info.arg = option.arg | |
239 info.handler = handler | |
240 info.option = option | |
241 info.type = option.type | |
242 info.uiType = "dialog" | |
243 info.uiName = MAJOR | |
244 | |
245 local a, b, c ,d | |
246 --using 4 returns for the get of a color type, increase if a type needs more | |
247 if type(member) == "function" then | |
248 --Call the function | |
249 a,b,c,d = member(info, ...) | |
250 else | |
251 --Call the method | |
252 if handler and handler[member] then | |
253 a,b,c,d = handler[member](handler, info, ...) | |
254 else | |
255 error(format("Method %s doesn't exist in handler for type %s", member, membername)) | |
256 end | |
257 end | |
258 del(info) | |
259 return a,b,c,d | |
260 else | |
261 --The value isnt a function to call, return it | |
262 return member | |
263 end | |
264 end | |
265 | |
266 --[[calls an options function that could be inherited, method name or function ref | |
267 local function CallOptionsFunction(funcname ,option, options, path, appName, ...) | |
268 local info = new() | |
269 | |
270 local func | |
271 local group = options | |
272 local handler | |
273 | |
274 --build the info table containing the path | |
275 -- pick up functions while traversing the tree | |
276 if group[funcname] ~= nil then | |
277 func = group[funcname] | |
278 end | |
279 handler = group.handler or handler | |
280 | |
281 for i, v in ipairs(path) do | |
282 group = GetSubOption(group, v) | |
283 info[i] = v | |
284 if group[funcname] ~= nil then | |
285 func = group[funcname] | |
286 end | |
287 handler = group.handler or handler | |
288 end | |
289 | |
290 info.options = options | |
291 info[0] = appName | |
292 info.arg = option.arg | |
293 | |
294 local a, b, c ,d | |
295 if type(func) == "string" then | |
296 if handler and handler[func] then | |
297 a,b,c,d = handler[func](handler, info, ...) | |
298 else | |
299 error(string.format("Method %s doesn't exist in handler for type func", func)) | |
300 end | |
301 elseif type(func) == "function" then | |
302 a,b,c,d = func(info, ...) | |
303 end | |
304 del(info) | |
305 return a,b,c,d | |
306 end | |
307 --]] | |
308 | |
309 --tables to hold orders and names for options being sorted, will be created with new() | |
310 --prevents needing to call functions repeatedly while sorting | |
311 local tempOrders | |
312 local tempNames | |
313 | |
314 local function compareOptions(a,b) | |
315 if not a then | |
316 return true | |
317 end | |
318 if not b then | |
319 return false | |
320 end | |
321 local OrderA, OrderB = tempOrders[a] or 100, tempOrders[b] or 100 | |
322 if OrderA == OrderB then | |
323 local NameA = (type(tempNames[a]) == "string") and tempNames[a] or "" | |
324 local NameB = (type(tempNames[b]) == "string") and tempNames[b] or "" | |
325 return NameA:upper() < NameB:upper() | |
326 end | |
327 if OrderA < 0 then | |
328 if OrderB > 0 then | |
329 return false | |
330 end | |
331 else | |
332 if OrderB < 0 then | |
333 return true | |
334 end | |
335 end | |
336 return OrderA < OrderB | |
337 end | |
338 | |
339 | |
340 | |
341 --builds 2 tables out of an options group | |
342 -- keySort, sorted keys | |
343 -- opts, combined options from .plugins and args | |
344 local function BuildSortedOptionsTable(group, keySort, opts, options, path, appName) | |
345 tempOrders = new() | |
346 tempNames = new() | |
347 | |
348 if group.plugins then | |
349 for plugin, t in pairs(group.plugins) do | |
350 for k, v in pairs(t) do | |
351 if not opts[k] then | |
352 tinsert(keySort, k) | |
353 opts[k] = v | |
354 | |
355 path[#path+1] = k | |
356 tempOrders[k] = GetOptionsMemberValue("order", v, options, path, appName) | |
357 tempNames[k] = GetOptionsMemberValue("name", v, options, path, appName) | |
358 path[#path] = nil | |
359 end | |
360 end | |
361 end | |
362 end | |
363 | |
364 for k, v in pairs(group.args) do | |
365 if not opts[k] then | |
366 tinsert(keySort, k) | |
367 opts[k] = v | |
368 | |
369 path[#path+1] = k | |
370 tempOrders[k] = GetOptionsMemberValue("order", v, options, path, appName) | |
371 tempNames[k] = GetOptionsMemberValue("name", v, options, path, appName) | |
372 path[#path] = nil | |
373 end | |
374 end | |
375 | |
376 tsort(keySort, compareOptions) | |
377 | |
378 del(tempOrders) | |
379 del(tempNames) | |
380 end | |
381 | |
382 local function DelTree(tree) | |
383 if tree.children then | |
384 local childs = tree.children | |
385 for i = 1, #childs do | |
386 DelTree(childs[i]) | |
387 del(childs[i]) | |
388 end | |
389 del(childs) | |
390 end | |
391 end | |
392 | |
393 local function CleanUserData(widget, event) | |
394 | |
395 local user = widget:GetUserDataTable() | |
396 | |
397 if user.path then | |
398 del(user.path) | |
399 end | |
400 | |
401 if widget.type == "TreeGroup" then | |
402 local tree = user.tree | |
403 widget:SetTree(nil) | |
404 if tree then | |
405 for i = 1, #tree do | |
406 DelTree(tree[i]) | |
407 del(tree[i]) | |
408 end | |
409 del(tree) | |
410 end | |
411 end | |
412 | |
413 if widget.type == "TabGroup" then | |
414 widget:SetTabs(nil) | |
415 if user.tablist then | |
416 del(user.tablist) | |
417 end | |
418 end | |
419 | |
420 if widget.type == "DropdownGroup" then | |
421 widget:SetGroupList(nil) | |
422 if user.grouplist then | |
423 del(user.grouplist) | |
424 end | |
425 if user.orderlist then | |
426 del(user.orderlist) | |
427 end | |
428 end | |
429 end | |
430 | |
431 -- - Gets a status table for the given appname and options path. | |
432 -- @param appName The application name as given to `:RegisterOptionsTable()` | |
433 -- @param path The path to the options (a table with all group keys) | |
434 -- @return | |
435 function AceConfigDialog:GetStatusTable(appName, path) | |
436 local status = self.Status | |
437 | |
438 if not status[appName] then | |
439 status[appName] = {} | |
440 status[appName].status = {} | |
441 status[appName].children = {} | |
442 end | |
443 | |
444 status = status[appName] | |
445 | |
446 if path then | |
447 for i = 1, #path do | |
448 local v = path[i] | |
449 if not status.children[v] then | |
450 status.children[v] = {} | |
451 status.children[v].status = {} | |
452 status.children[v].children = {} | |
453 end | |
454 status = status.children[v] | |
455 end | |
456 end | |
457 | |
458 return status.status | |
459 end | |
460 | |
461 --- Selects the specified path in the options window. | |
462 -- The path specified has to match the keys of the groups in the table. | |
463 -- @param appName The application name as given to `:RegisterOptionsTable()` | |
464 -- @param ... The path to the key that should be selected | |
465 function AceConfigDialog:SelectGroup(appName, ...) | |
466 local path = new() | |
467 | |
468 | |
469 local app = reg:GetOptionsTable(appName) | |
470 if not app then | |
471 error(("%s isn't registed with AceConfigRegistry, unable to open config"):format(appName), 2) | |
472 end | |
473 local options = app("dialog", MAJOR) | |
474 local group = options | |
475 local status = self:GetStatusTable(appName, path) | |
476 if not status.groups then | |
477 status.groups = {} | |
478 end | |
479 status = status.groups | |
480 local treevalue | |
481 local treestatus | |
482 | |
483 for n = 1, select("#",...) do | |
484 local key = select(n, ...) | |
485 | |
486 if group.childGroups == "tab" or group.childGroups == "select" then | |
487 --if this is a tab or select group, select the group | |
488 status.selected = key | |
489 --children of this group are no longer extra levels of a tree | |
490 treevalue = nil | |
491 else | |
492 --tree group by default | |
493 if treevalue then | |
494 --this is an extra level of a tree group, build a uniquevalue for it | |
495 treevalue = treevalue.."\001"..key | |
496 else | |
497 --this is the top level of a tree group, the uniquevalue is the same as the key | |
498 treevalue = key | |
499 if not status.groups then | |
500 status.groups = {} | |
501 end | |
502 --save this trees status table for any extra levels or groups | |
503 treestatus = status | |
504 end | |
505 --make sure that the tree entry is open, and select it. | |
506 --the selected group will be overwritten if a child is the final target but still needs to be open | |
507 treestatus.selected = treevalue | |
508 treestatus.groups[treevalue] = true | |
509 | |
510 end | |
511 | |
512 --move to the next group in the path | |
513 group = GetSubOption(group, key) | |
514 if not group then | |
515 break | |
516 end | |
517 tinsert(path, key) | |
518 status = self:GetStatusTable(appName, path) | |
519 if not status.groups then | |
520 status.groups = {} | |
521 end | |
522 status = status.groups | |
523 end | |
524 | |
525 del(path) | |
526 reg:NotifyChange(appName) | |
527 end | |
528 | |
529 local function OptionOnMouseOver(widget, event) | |
530 --show a tooltip/set the status bar to the desc text | |
531 local user = widget:GetUserDataTable() | |
532 local opt = user.option | |
533 local options = user.options | |
534 local path = user.path | |
535 local appName = user.appName | |
536 | |
537 GameTooltip:SetOwner(widget.frame, "ANCHOR_TOPRIGHT") | |
538 local name = GetOptionsMemberValue("name", opt, options, path, appName) | |
539 local desc = GetOptionsMemberValue("desc", opt, options, path, appName) | |
540 local usage = GetOptionsMemberValue("usage", opt, options, path, appName) | |
541 local descStyle = opt.descStyle | |
542 | |
543 if descStyle and descStyle ~= "tooltip" then return end | |
544 | |
545 GameTooltip:SetText(name, 1, .82, 0, 1) | |
546 | |
547 if opt.type == "multiselect" then | |
548 GameTooltip:AddLine(user.text,0.5, 0.5, 0.8, 1) | |
549 end | |
550 if type(desc) == "string" then | |
551 GameTooltip:AddLine(desc, 1, 1, 1, 1) | |
552 end | |
553 if type(usage) == "string" then | |
554 GameTooltip:AddLine("Usage: "..usage, NORMAL_FONT_COLOR.r, NORMAL_FONT_COLOR.g, NORMAL_FONT_COLOR.b, 1) | |
555 end | |
556 | |
557 GameTooltip:Show() | |
558 end | |
559 | |
560 local function OptionOnMouseLeave(widget, event) | |
561 GameTooltip:Hide() | |
562 end | |
563 | |
564 local function GetFuncName(option) | |
565 local type = option.type | |
566 if type == "execute" then | |
567 return "func" | |
568 else | |
569 return "set" | |
570 end | |
571 end | |
572 local function confirmPopup(appName, rootframe, basepath, info, message, func, ...) | |
573 if not StaticPopupDialogs["ACECONFIGDIALOG30_CONFIRM_DIALOG"] then | |
574 StaticPopupDialogs["ACECONFIGDIALOG30_CONFIRM_DIALOG"] = {} | |
575 end | |
576 local t = StaticPopupDialogs["ACECONFIGDIALOG30_CONFIRM_DIALOG"] | |
577 for k in pairs(t) do | |
578 t[k] = nil | |
579 end | |
580 t.text = message | |
581 t.button1 = ACCEPT | |
582 t.button2 = CANCEL | |
583 t.preferredIndex = STATICPOPUP_NUMDIALOGS | |
584 local dialog, oldstrata | |
585 t.OnAccept = function() | |
586 safecall(func, unpack(t)) | |
587 if dialog and oldstrata then | |
588 dialog:SetFrameStrata(oldstrata) | |
589 end | |
590 AceConfigDialog:Open(appName, rootframe, unpack(basepath or emptyTbl)) | |
591 del(info) | |
592 end | |
593 t.OnCancel = function() | |
594 if dialog and oldstrata then | |
595 dialog:SetFrameStrata(oldstrata) | |
596 end | |
597 AceConfigDialog:Open(appName, rootframe, unpack(basepath or emptyTbl)) | |
598 del(info) | |
599 end | |
600 for i = 1, select("#", ...) do | |
601 t[i] = select(i, ...) or false | |
602 end | |
603 t.timeout = 0 | |
604 t.whileDead = 1 | |
605 t.hideOnEscape = 1 | |
606 | |
607 dialog = StaticPopup_Show("ACECONFIGDIALOG30_CONFIRM_DIALOG") | |
608 if dialog then | |
609 oldstrata = dialog:GetFrameStrata() | |
610 dialog:SetFrameStrata("TOOLTIP") | |
611 end | |
612 end | |
613 | |
614 local function ActivateControl(widget, event, ...) | |
615 --This function will call the set / execute handler for the widget | |
616 --widget:GetUserDataTable() contains the needed info | |
617 local user = widget:GetUserDataTable() | |
618 local option = user.option | |
619 local options = user.options | |
620 local path = user.path | |
621 local info = new() | |
622 | |
623 local func | |
624 local group = options | |
625 local funcname = GetFuncName(option) | |
626 local handler | |
627 local confirm | |
628 local validate | |
629 --build the info table containing the path | |
630 -- pick up functions while traversing the tree | |
631 if group[funcname] ~= nil then | |
632 func = group[funcname] | |
633 end | |
634 handler = group.handler or handler | |
635 confirm = group.confirm | |
636 validate = group.validate | |
637 for i = 1, #path do | |
638 local v = path[i] | |
639 group = GetSubOption(group, v) | |
640 info[i] = v | |
641 if group[funcname] ~= nil then | |
642 func = group[funcname] | |
643 end | |
644 handler = group.handler or handler | |
645 if group.confirm ~= nil then | |
646 confirm = group.confirm | |
647 end | |
648 if group.validate ~= nil then | |
649 validate = group.validate | |
650 end | |
651 end | |
652 | |
653 info.options = options | |
654 info.appName = user.appName | |
655 info.arg = option.arg | |
656 info.handler = handler | |
657 info.option = option | |
658 info.type = option.type | |
659 info.uiType = "dialog" | |
660 info.uiName = MAJOR | |
661 | |
662 local name | |
663 if type(option.name) == "function" then | |
664 name = option.name(info) | |
665 elseif type(option.name) == "string" then | |
666 name = option.name | |
667 else | |
668 name = "" | |
669 end | |
670 local usage = option.usage | |
671 local pattern = option.pattern | |
672 | |
673 local validated = true | |
674 | |
675 if option.type == "input" then | |
676 if type(pattern)=="string" then | |
677 if not strmatch(..., pattern) then | |
678 validated = false | |
679 end | |
680 end | |
681 end | |
682 | |
683 local success | |
684 if validated and option.type ~= "execute" then | |
685 if type(validate) == "string" then | |
686 if handler and handler[validate] then | |
687 success, validated = safecall(handler[validate], handler, info, ...) | |
688 if not success then validated = false end | |
689 else | |
690 error(format("Method %s doesn't exist in handler for type execute", validate)) | |
691 end | |
692 elseif type(validate) == "function" then | |
693 success, validated = safecall(validate, info, ...) | |
694 if not success then validated = false end | |
695 end | |
696 end | |
697 | |
698 local rootframe = user.rootframe | |
699 if type(validated) == "string" then | |
700 --validate function returned a message to display | |
701 if rootframe.SetStatusText then | |
702 rootframe:SetStatusText(validated) | |
703 else | |
704 -- TODO: do something else. | |
705 end | |
706 PlaySound("igPlayerInviteDecline") | |
707 del(info) | |
708 return true | |
709 elseif not validated then | |
710 --validate returned false | |
711 if rootframe.SetStatusText then | |
712 if usage then | |
713 rootframe:SetStatusText(name..": "..usage) | |
714 else | |
715 if pattern then | |
716 rootframe:SetStatusText(name..": Expected "..pattern) | |
717 else | |
718 rootframe:SetStatusText(name..": Invalid Value") | |
719 end | |
720 end | |
721 else | |
722 -- TODO: do something else | |
723 end | |
724 PlaySound("igPlayerInviteDecline") | |
725 del(info) | |
726 return true | |
727 else | |
728 | |
729 local confirmText = option.confirmText | |
730 --call confirm func/method | |
731 if type(confirm) == "string" then | |
732 if handler and handler[confirm] then | |
733 success, confirm = safecall(handler[confirm], handler, info, ...) | |
734 if success and type(confirm) == "string" then | |
735 confirmText = confirm | |
736 confirm = true | |
737 elseif not success then | |
738 confirm = false | |
739 end | |
740 else | |
741 error(format("Method %s doesn't exist in handler for type confirm", confirm)) | |
742 end | |
743 elseif type(confirm) == "function" then | |
744 success, confirm = safecall(confirm, info, ...) | |
745 if success and type(confirm) == "string" then | |
746 confirmText = confirm | |
747 confirm = true | |
748 elseif not success then | |
749 confirm = false | |
750 end | |
751 end | |
752 | |
753 --confirm if needed | |
754 if type(confirm) == "boolean" then | |
755 if confirm then | |
756 if not confirmText then | |
757 local name, desc = option.name, option.desc | |
758 if type(name) == "function" then | |
759 name = name(info) | |
760 end | |
761 if type(desc) == "function" then | |
762 desc = desc(info) | |
763 end | |
764 confirmText = name | |
765 if desc then | |
766 confirmText = confirmText.." - "..desc | |
767 end | |
768 end | |
769 | |
770 local iscustom = user.rootframe:GetUserData("iscustom") | |
771 local rootframe | |
772 | |
773 if iscustom then | |
774 rootframe = user.rootframe | |
775 end | |
776 local basepath = user.rootframe:GetUserData("basepath") | |
777 if type(func) == "string" then | |
778 if handler and handler[func] then | |
779 confirmPopup(user.appName, rootframe, basepath, info, confirmText, handler[func], handler, info, ...) | |
780 else | |
781 error(format("Method %s doesn't exist in handler for type func", func)) | |
782 end | |
783 elseif type(func) == "function" then | |
784 confirmPopup(user.appName, rootframe, basepath, info, confirmText, func, info, ...) | |
785 end | |
786 --func will be called and info deleted when the confirm dialog is responded to | |
787 return | |
788 end | |
789 end | |
790 | |
791 --call the function | |
792 if type(func) == "string" then | |
793 if handler and handler[func] then | |
794 safecall(handler[func],handler, info, ...) | |
795 else | |
796 error(format("Method %s doesn't exist in handler for type func", func)) | |
797 end | |
798 elseif type(func) == "function" then | |
799 safecall(func,info, ...) | |
800 end | |
801 | |
802 | |
803 | |
804 local iscustom = user.rootframe:GetUserData("iscustom") | |
805 local basepath = user.rootframe:GetUserData("basepath") or emptyTbl | |
806 --full refresh of the frame, some controls dont cause this on all events | |
807 if option.type == "color" then | |
808 if event == "OnValueConfirmed" then | |
809 | |
810 if iscustom then | |
811 AceConfigDialog:Open(user.appName, user.rootframe, unpack(basepath)) | |
812 else | |
813 AceConfigDialog:Open(user.appName, unpack(basepath)) | |
814 end | |
815 end | |
816 elseif option.type == "range" then | |
817 if event == "OnMouseUp" then | |
818 if iscustom then | |
819 AceConfigDialog:Open(user.appName, user.rootframe, unpack(basepath)) | |
820 else | |
821 AceConfigDialog:Open(user.appName, unpack(basepath)) | |
822 end | |
823 end | |
824 --multiselects don't cause a refresh on 'OnValueChanged' only 'OnClosed' | |
825 elseif option.type == "multiselect" then | |
826 user.valuechanged = true | |
827 else | |
828 if iscustom then | |
829 AceConfigDialog:Open(user.appName, user.rootframe, unpack(basepath)) | |
830 else | |
831 AceConfigDialog:Open(user.appName, unpack(basepath)) | |
832 end | |
833 end | |
834 | |
835 end | |
836 del(info) | |
837 end | |
838 | |
839 local function ActivateSlider(widget, event, value) | |
840 local option = widget:GetUserData("option") | |
841 local min, max, step = option.min or (not option.softMin and 0 or nil), option.max or (not option.softMax and 100 or nil), option.step | |
842 if min then | |
843 if step then | |
844 value = math_floor((value - min) / step + 0.5) * step + min | |
845 end | |
846 value = math_max(value, min) | |
847 end | |
848 if max then | |
849 value = math_min(value, max) | |
850 end | |
851 ActivateControl(widget,event,value) | |
852 end | |
853 | |
854 --called from a checkbox that is part of an internally created multiselect group | |
855 --this type is safe to refresh on activation of one control | |
856 local function ActivateMultiControl(widget, event, ...) | |
857 ActivateControl(widget, event, widget:GetUserData("value"), ...) | |
858 local user = widget:GetUserDataTable() | |
859 local iscustom = user.rootframe:GetUserData("iscustom") | |
860 local basepath = user.rootframe:GetUserData("basepath") or emptyTbl | |
861 if iscustom then | |
862 AceConfigDialog:Open(user.appName, user.rootframe, unpack(basepath)) | |
863 else | |
864 AceConfigDialog:Open(user.appName, unpack(basepath)) | |
865 end | |
866 end | |
867 | |
868 local function MultiControlOnClosed(widget, event, ...) | |
869 local user = widget:GetUserDataTable() | |
870 if user.valuechanged then | |
871 local iscustom = user.rootframe:GetUserData("iscustom") | |
872 local basepath = user.rootframe:GetUserData("basepath") or emptyTbl | |
873 if iscustom then | |
874 AceConfigDialog:Open(user.appName, user.rootframe, unpack(basepath)) | |
875 else | |
876 AceConfigDialog:Open(user.appName, unpack(basepath)) | |
877 end | |
878 end | |
879 end | |
880 | |
881 local function FrameOnClose(widget, event) | |
882 local appName = widget:GetUserData("appName") | |
883 AceConfigDialog.OpenFrames[appName] = nil | |
884 gui:Release(widget) | |
885 end | |
886 | |
887 local function CheckOptionHidden(option, options, path, appName) | |
888 --check for a specific boolean option | |
889 local hidden = pickfirstset(option.dialogHidden,option.guiHidden) | |
890 if hidden ~= nil then | |
891 return hidden | |
892 end | |
893 | |
894 return GetOptionsMemberValue("hidden", option, options, path, appName) | |
895 end | |
896 | |
897 local function CheckOptionDisabled(option, options, path, appName) | |
898 --check for a specific boolean option | |
899 local disabled = pickfirstset(option.dialogDisabled,option.guiDisabled) | |
900 if disabled ~= nil then | |
901 return disabled | |
902 end | |
903 | |
904 return GetOptionsMemberValue("disabled", option, options, path, appName) | |
905 end | |
906 --[[ | |
907 local function BuildTabs(group, options, path, appName) | |
908 local tabs = new() | |
909 local text = new() | |
910 local keySort = new() | |
911 local opts = new() | |
912 | |
913 BuildSortedOptionsTable(group, keySort, opts, options, path, appName) | |
914 | |
915 for i = 1, #keySort do | |
916 local k = keySort[i] | |
917 local v = opts[k] | |
918 if v.type == "group" then | |
919 path[#path+1] = k | |
920 local inline = pickfirstset(v.dialogInline,v.guiInline,v.inline, false) | |
921 local hidden = CheckOptionHidden(v, options, path, appName) | |
922 if not inline and not hidden then | |
923 tinsert(tabs, k) | |
924 text[k] = GetOptionsMemberValue("name", v, options, path, appName) | |
925 end | |
926 path[#path] = nil | |
927 end | |
928 end | |
929 | |
930 del(keySort) | |
931 del(opts) | |
932 | |
933 return tabs, text | |
934 end | |
935 ]] | |
936 local function BuildSelect(group, options, path, appName) | |
937 local groups = new() | |
938 local order = new() | |
939 local keySort = new() | |
940 local opts = new() | |
941 | |
942 BuildSortedOptionsTable(group, keySort, opts, options, path, appName) | |
943 | |
944 for i = 1, #keySort do | |
945 local k = keySort[i] | |
946 local v = opts[k] | |
947 if v.type == "group" then | |
948 path[#path+1] = k | |
949 local inline = pickfirstset(v.dialogInline,v.guiInline,v.inline, false) | |
950 local hidden = CheckOptionHidden(v, options, path, appName) | |
951 if not inline and not hidden then | |
952 groups[k] = GetOptionsMemberValue("name", v, options, path, appName) | |
953 tinsert(order, k) | |
954 end | |
955 path[#path] = nil | |
956 end | |
957 end | |
958 | |
959 del(opts) | |
960 del(keySort) | |
961 | |
962 return groups, order | |
963 end | |
964 | |
965 local function BuildSubGroups(group, tree, options, path, appName) | |
966 local keySort = new() | |
967 local opts = new() | |
968 | |
969 BuildSortedOptionsTable(group, keySort, opts, options, path, appName) | |
970 | |
971 for i = 1, #keySort do | |
972 local k = keySort[i] | |
973 local v = opts[k] | |
974 if v.type == "group" then | |
975 path[#path+1] = k | |
976 local inline = pickfirstset(v.dialogInline,v.guiInline,v.inline, false) | |
977 local hidden = CheckOptionHidden(v, options, path, appName) | |
978 if not inline and not hidden then | |
979 local entry = new() | |
980 entry.value = k | |
981 entry.text = GetOptionsMemberValue("name", v, options, path, appName) | |
982 entry.icon = GetOptionsMemberValue("icon", v, options, path, appName) | |
983 entry.iconCoords = GetOptionsMemberValue("iconCoords", v, options, path, appName) | |
984 entry.disabled = CheckOptionDisabled(v, options, path, appName) | |
985 if not tree.children then tree.children = new() end | |
986 tinsert(tree.children,entry) | |
987 if (v.childGroups or "tree") == "tree" then | |
988 BuildSubGroups(v,entry, options, path, appName) | |
989 end | |
990 end | |
991 path[#path] = nil | |
992 end | |
993 end | |
994 | |
995 del(keySort) | |
996 del(opts) | |
997 end | |
998 | |
999 local function BuildGroups(group, options, path, appName, recurse) | |
1000 local tree = new() | |
1001 local keySort = new() | |
1002 local opts = new() | |
1003 | |
1004 BuildSortedOptionsTable(group, keySort, opts, options, path, appName) | |
1005 | |
1006 for i = 1, #keySort do | |
1007 local k = keySort[i] | |
1008 local v = opts[k] | |
1009 if v.type == "group" then | |
1010 path[#path+1] = k | |
1011 local inline = pickfirstset(v.dialogInline,v.guiInline,v.inline, false) | |
1012 local hidden = CheckOptionHidden(v, options, path, appName) | |
1013 if not inline and not hidden then | |
1014 local entry = new() | |
1015 entry.value = k | |
1016 entry.text = GetOptionsMemberValue("name", v, options, path, appName) | |
1017 entry.icon = GetOptionsMemberValue("icon", v, options, path, appName) | |
1018 entry.disabled = CheckOptionDisabled(v, options, path, appName) | |
1019 tinsert(tree,entry) | |
1020 if recurse and (v.childGroups or "tree") == "tree" then | |
1021 BuildSubGroups(v,entry, options, path, appName) | |
1022 end | |
1023 end | |
1024 path[#path] = nil | |
1025 end | |
1026 end | |
1027 del(keySort) | |
1028 del(opts) | |
1029 return tree | |
1030 end | |
1031 | |
1032 local function InjectInfo(control, options, option, path, rootframe, appName) | |
1033 local user = control:GetUserDataTable() | |
1034 for i = 1, #path do | |
1035 user[i] = path[i] | |
1036 end | |
1037 user.rootframe = rootframe | |
1038 user.option = option | |
1039 user.options = options | |
1040 user.path = copy(path) | |
1041 user.appName = appName | |
1042 control:SetCallback("OnRelease", CleanUserData) | |
1043 control:SetCallback("OnLeave", OptionOnMouseLeave) | |
1044 control:SetCallback("OnEnter", OptionOnMouseOver) | |
1045 end | |
1046 | |
1047 | |
1048 --[[ | |
1049 options - root of the options table being fed | |
1050 container - widget that controls will be placed in | |
1051 rootframe - Frame object the options are in | |
1052 path - table with the keys to get to the group being fed | |
1053 --]] | |
1054 | |
1055 local function FeedOptions(appName, options,container,rootframe,path,group,inline) | |
1056 local keySort = new() | |
1057 local opts = new() | |
1058 | |
1059 BuildSortedOptionsTable(group, keySort, opts, options, path, appName) | |
1060 | |
1061 for i = 1, #keySort do | |
1062 local k = keySort[i] | |
1063 local v = opts[k] | |
1064 tinsert(path, k) | |
1065 local hidden = CheckOptionHidden(v, options, path, appName) | |
1066 local name = GetOptionsMemberValue("name", v, options, path, appName) | |
1067 if not hidden then | |
1068 if v.type == "group" then | |
1069 if inline or pickfirstset(v.dialogInline,v.guiInline,v.inline, false) then | |
1070 --Inline group | |
1071 local GroupContainer | |
1072 if name and name ~= "" then | |
1073 GroupContainer = gui:Create("InlineGroup") | |
1074 GroupContainer:SetTitle(name or "") | |
1075 else | |
1076 GroupContainer = gui:Create("SimpleGroup") | |
1077 end | |
1078 | |
1079 GroupContainer.width = "fill" | |
1080 GroupContainer:SetLayout("flow") | |
1081 container:AddChild(GroupContainer) | |
1082 FeedOptions(appName,options,GroupContainer,rootframe,path,v,true) | |
1083 end | |
1084 else | |
1085 --Control to feed | |
1086 local control | |
1087 | |
1088 local name = GetOptionsMemberValue("name", v, options, path, appName) | |
1089 | |
1090 if v.type == "execute" then | |
1091 | |
1092 local imageCoords = GetOptionsMemberValue("imageCoords",v, options, path, appName) | |
1093 local image, width, height = GetOptionsMemberValue("image",v, options, path, appName) | |
1094 | |
1095 if type(image) == "string" then | |
1096 control = gui:Create("Icon") | |
1097 if not width then | |
1098 width = GetOptionsMemberValue("imageWidth",v, options, path, appName) | |
1099 end | |
1100 if not height then | |
1101 height = GetOptionsMemberValue("imageHeight",v, options, path, appName) | |
1102 end | |
1103 if type(imageCoords) == "table" then | |
1104 control:SetImage(image, unpack(imageCoords)) | |
1105 else | |
1106 control:SetImage(image) | |
1107 end | |
1108 if type(width) ~= "number" then | |
1109 width = 32 | |
1110 end | |
1111 if type(height) ~= "number" then | |
1112 height = 32 | |
1113 end | |
1114 control:SetImageSize(width, height) | |
1115 control:SetLabel(name) | |
1116 else | |
1117 control = gui:Create("Button") | |
1118 control:SetText(name) | |
1119 end | |
1120 control:SetCallback("OnClick",ActivateControl) | |
1121 | |
1122 elseif v.type == "input" then | |
1123 local controlType = v.dialogControl or v.control or (v.multiline and "MultiLineEditBox") or "EditBox" | |
1124 control = gui:Create(controlType) | |
1125 if not control then | |
1126 geterrorhandler()(("Invalid Custom Control Type - %s"):format(tostring(controlType))) | |
1127 control = gui:Create(v.multiline and "MultiLineEditBox" or "EditBox") | |
1128 end | |
1129 | |
1130 if v.multiline and control.SetNumLines then | |
1131 control:SetNumLines(tonumber(v.multiline) or 4) | |
1132 end | |
1133 control:SetLabel(name) | |
1134 control:SetCallback("OnEnterPressed",ActivateControl) | |
1135 local text = GetOptionsMemberValue("get",v, options, path, appName) | |
1136 if type(text) ~= "string" then | |
1137 text = "" | |
1138 end | |
1139 control:SetText(text) | |
1140 | |
1141 elseif v.type == "toggle" then | |
1142 control = gui:Create("CheckBox") | |
1143 control:SetLabel(name) | |
1144 control:SetTriState(v.tristate) | |
1145 local value = GetOptionsMemberValue("get",v, options, path, appName) | |
1146 control:SetValue(value) | |
1147 control:SetCallback("OnValueChanged",ActivateControl) | |
1148 | |
1149 if v.descStyle == "inline" then | |
1150 local desc = GetOptionsMemberValue("desc", v, options, path, appName) | |
1151 control:SetDescription(desc) | |
1152 end | |
1153 | |
1154 local image = GetOptionsMemberValue("image", v, options, path, appName) | |
1155 local imageCoords = GetOptionsMemberValue("imageCoords", v, options, path, appName) | |
1156 | |
1157 if type(image) == "string" then | |
1158 if type(imageCoords) == "table" then | |
1159 control:SetImage(image, unpack(imageCoords)) | |
1160 else | |
1161 control:SetImage(image) | |
1162 end | |
1163 end | |
1164 elseif v.type == "range" then | |
1165 control = gui:Create("Slider") | |
1166 control:SetLabel(name) | |
1167 control:SetSliderValues(v.softMin or v.min or 0, v.softMax or v.max or 100, v.bigStep or v.step or 0) | |
1168 control:SetIsPercent(v.isPercent) | |
1169 local value = GetOptionsMemberValue("get",v, options, path, appName) | |
1170 if type(value) ~= "number" then | |
1171 value = 0 | |
1172 end | |
1173 control:SetValue(value) | |
1174 control:SetCallback("OnValueChanged",ActivateSlider) | |
1175 control:SetCallback("OnMouseUp",ActivateSlider) | |
1176 | |
1177 elseif v.type == "select" then | |
1178 local values = GetOptionsMemberValue("values", v, options, path, appName) | |
1179 if v.style == "radio" then | |
1180 local disabled = CheckOptionDisabled(v, options, path, appName) | |
1181 local width = GetOptionsMemberValue("width",v,options,path,appName) | |
1182 control = gui:Create("InlineGroup") | |
1183 control:SetLayout("Flow") | |
1184 control:SetTitle(name) | |
1185 control.width = "fill" | |
1186 | |
1187 control:PauseLayout() | |
1188 local optionValue = GetOptionsMemberValue("get",v, options, path, appName) | |
1189 local t = {} | |
1190 for value, text in pairs(values) do | |
1191 t[#t+1]=value | |
1192 end | |
1193 tsort(t) | |
1194 for k, value in ipairs(t) do | |
1195 local text = values[value] | |
1196 local radio = gui:Create("CheckBox") | |
1197 radio:SetLabel(text) | |
1198 radio:SetUserData("value", value) | |
1199 radio:SetUserData("text", text) | |
1200 radio:SetDisabled(disabled) | |
1201 radio:SetType("radio") | |
1202 radio:SetValue(optionValue == value) | |
1203 radio:SetCallback("OnValueChanged", ActivateMultiControl) | |
1204 InjectInfo(radio, options, v, path, rootframe, appName) | |
1205 control:AddChild(radio) | |
1206 if width == "double" then | |
1207 radio:SetWidth(width_multiplier * 2) | |
1208 elseif width == "half" then | |
1209 radio:SetWidth(width_multiplier / 2) | |
1210 elseif width == "full" then | |
1211 radio.width = "fill" | |
1212 else | |
1213 radio:SetWidth(width_multiplier) | |
1214 end | |
1215 end | |
1216 control:ResumeLayout() | |
1217 control:DoLayout() | |
1218 else | |
1219 local controlType = v.dialogControl or v.control or "Dropdown" | |
1220 control = gui:Create(controlType) | |
1221 if not control then | |
1222 geterrorhandler()(("Invalid Custom Control Type - %s"):format(tostring(controlType))) | |
1223 control = gui:Create("Dropdown") | |
1224 end | |
1225 local itemType = v.itemControl | |
1226 if itemType and not gui:GetWidgetVersion(itemType) then | |
1227 geterrorhandler()(("Invalid Custom Item Type - %s"):format(tostring(itemType))) | |
1228 itemType = nil | |
1229 end | |
1230 control:SetLabel(name) | |
1231 control:SetList(values, nil, itemType) | |
1232 local value = GetOptionsMemberValue("get",v, options, path, appName) | |
1233 if not values[value] then | |
1234 value = nil | |
1235 end | |
1236 control:SetValue(value) | |
1237 control:SetCallback("OnValueChanged", ActivateControl) | |
1238 end | |
1239 | |
1240 elseif v.type == "multiselect" then | |
1241 local values = GetOptionsMemberValue("values", v, options, path, appName) | |
1242 local disabled = CheckOptionDisabled(v, options, path, appName) | |
1243 | |
1244 local controlType = v.dialogControl or v.control | |
1245 | |
1246 local valuesort = new() | |
1247 if values then | |
1248 for value, text in pairs(values) do | |
1249 tinsert(valuesort, value) | |
1250 end | |
1251 end | |
1252 tsort(valuesort) | |
1253 | |
1254 if controlType then | |
1255 control = gui:Create(controlType) | |
1256 if not control then | |
1257 geterrorhandler()(("Invalid Custom Control Type - %s"):format(tostring(controlType))) | |
1258 end | |
1259 end | |
1260 if control then | |
1261 control:SetMultiselect(true) | |
1262 control:SetLabel(name) | |
1263 control:SetList(values) | |
1264 control:SetDisabled(disabled) | |
1265 control:SetCallback("OnValueChanged",ActivateControl) | |
1266 control:SetCallback("OnClosed", MultiControlOnClosed) | |
1267 local width = GetOptionsMemberValue("width",v,options,path,appName) | |
1268 if width == "double" then | |
1269 control:SetWidth(width_multiplier * 2) | |
1270 elseif width == "half" then | |
1271 control:SetWidth(width_multiplier / 2) | |
1272 elseif width == "full" then | |
1273 control.width = "fill" | |
1274 else | |
1275 control:SetWidth(width_multiplier) | |
1276 end | |
1277 --check:SetTriState(v.tristate) | |
1278 for i = 1, #valuesort do | |
1279 local key = valuesort[i] | |
1280 local value = GetOptionsMemberValue("get",v, options, path, appName, key) | |
1281 control:SetItemValue(key,value) | |
1282 end | |
1283 else | |
1284 control = gui:Create("InlineGroup") | |
1285 control:SetLayout("Flow") | |
1286 control:SetTitle(name) | |
1287 control.width = "fill" | |
1288 | |
1289 control:PauseLayout() | |
1290 local width = GetOptionsMemberValue("width",v,options,path,appName) | |
1291 for i = 1, #valuesort do | |
1292 local value = valuesort[i] | |
1293 local text = values[value] | |
1294 local check = gui:Create("CheckBox") | |
1295 check:SetLabel(text) | |
1296 check:SetUserData("value", value) | |
1297 check:SetUserData("text", text) | |
1298 check:SetDisabled(disabled) | |
1299 check:SetTriState(v.tristate) | |
1300 check:SetValue(GetOptionsMemberValue("get",v, options, path, appName, value)) | |
1301 check:SetCallback("OnValueChanged",ActivateMultiControl) | |
1302 InjectInfo(check, options, v, path, rootframe, appName) | |
1303 control:AddChild(check) | |
1304 if width == "double" then | |
1305 check:SetWidth(width_multiplier * 2) | |
1306 elseif width == "half" then | |
1307 check:SetWidth(width_multiplier / 2) | |
1308 elseif width == "full" then | |
1309 check.width = "fill" | |
1310 else | |
1311 check:SetWidth(width_multiplier) | |
1312 end | |
1313 end | |
1314 control:ResumeLayout() | |
1315 control:DoLayout() | |
1316 | |
1317 | |
1318 end | |
1319 | |
1320 del(valuesort) | |
1321 | |
1322 elseif v.type == "color" then | |
1323 control = gui:Create("ColorPicker") | |
1324 control:SetLabel(name) | |
1325 control:SetHasAlpha(GetOptionsMemberValue("hasAlpha",v, options, path, appName)) | |
1326 control:SetColor(GetOptionsMemberValue("get",v, options, path, appName)) | |
1327 control:SetCallback("OnValueChanged",ActivateControl) | |
1328 control:SetCallback("OnValueConfirmed",ActivateControl) | |
1329 | |
1330 elseif v.type == "keybinding" then | |
1331 control = gui:Create("Keybinding") | |
1332 control:SetLabel(name) | |
1333 control:SetKey(GetOptionsMemberValue("get",v, options, path, appName)) | |
1334 control:SetCallback("OnKeyChanged",ActivateControl) | |
1335 | |
1336 elseif v.type == "header" then | |
1337 control = gui:Create("Heading") | |
1338 control:SetText(name) | |
1339 control.width = "fill" | |
1340 | |
1341 elseif v.type == "description" then | |
1342 control = gui:Create("Label") | |
1343 control:SetText(name) | |
1344 | |
1345 local fontSize = GetOptionsMemberValue("fontSize",v, options, path, appName) | |
1346 if fontSize == "medium" then | |
1347 control:SetFontObject(GameFontHighlight) | |
1348 elseif fontSize == "large" then | |
1349 control:SetFontObject(GameFontHighlightLarge) | |
1350 else -- small or invalid | |
1351 control:SetFontObject(GameFontHighlightSmall) | |
1352 end | |
1353 | |
1354 local imageCoords = GetOptionsMemberValue("imageCoords",v, options, path, appName) | |
1355 local image, width, height = GetOptionsMemberValue("image",v, options, path, appName) | |
1356 | |
1357 if type(image) == "string" then | |
1358 if not width then | |
1359 width = GetOptionsMemberValue("imageWidth",v, options, path, appName) | |
1360 end | |
1361 if not height then | |
1362 height = GetOptionsMemberValue("imageHeight",v, options, path, appName) | |
1363 end | |
1364 if type(imageCoords) == "table" then | |
1365 control:SetImage(image, unpack(imageCoords)) | |
1366 else | |
1367 control:SetImage(image) | |
1368 end | |
1369 if type(width) ~= "number" then | |
1370 width = 32 | |
1371 end | |
1372 if type(height) ~= "number" then | |
1373 height = 32 | |
1374 end | |
1375 control:SetImageSize(width, height) | |
1376 end | |
1377 local width = GetOptionsMemberValue("width",v,options,path,appName) | |
1378 control.width = not width and "fill" | |
1379 end | |
1380 | |
1381 --Common Init | |
1382 if control then | |
1383 if control.width ~= "fill" then | |
1384 local width = GetOptionsMemberValue("width",v,options,path,appName) | |
1385 if width == "double" then | |
1386 control:SetWidth(width_multiplier * 2) | |
1387 elseif width == "half" then | |
1388 control:SetWidth(width_multiplier / 2) | |
1389 elseif width == "full" then | |
1390 control.width = "fill" | |
1391 else | |
1392 control:SetWidth(width_multiplier) | |
1393 end | |
1394 end | |
1395 if control.SetDisabled then | |
1396 local disabled = CheckOptionDisabled(v, options, path, appName) | |
1397 control:SetDisabled(disabled) | |
1398 end | |
1399 | |
1400 InjectInfo(control, options, v, path, rootframe, appName) | |
1401 container:AddChild(control) | |
1402 end | |
1403 | |
1404 end | |
1405 end | |
1406 tremove(path) | |
1407 end | |
1408 container:ResumeLayout() | |
1409 container:DoLayout() | |
1410 del(keySort) | |
1411 del(opts) | |
1412 end | |
1413 | |
1414 local function BuildPath(path, ...) | |
1415 for i = 1, select("#",...) do | |
1416 tinsert(path, (select(i,...))) | |
1417 end | |
1418 end | |
1419 | |
1420 | |
1421 local function TreeOnButtonEnter(widget, event, uniquevalue, button) | |
1422 local user = widget:GetUserDataTable() | |
1423 if not user then return end | |
1424 local options = user.options | |
1425 local option = user.option | |
1426 local path = user.path | |
1427 local appName = user.appName | |
1428 | |
1429 local feedpath = new() | |
1430 for i = 1, #path do | |
1431 feedpath[i] = path[i] | |
1432 end | |
1433 | |
1434 BuildPath(feedpath, ("\001"):split(uniquevalue)) | |
1435 local group = options | |
1436 for i = 1, #feedpath do | |
1437 if not group then return end | |
1438 group = GetSubOption(group, feedpath[i]) | |
1439 end | |
1440 | |
1441 local name = GetOptionsMemberValue("name", group, options, feedpath, appName) | |
1442 local desc = GetOptionsMemberValue("desc", group, options, feedpath, appName) | |
1443 | |
1444 GameTooltip:SetOwner(button, "ANCHOR_NONE") | |
1445 if widget.type == "TabGroup" then | |
1446 GameTooltip:SetPoint("BOTTOM",button,"TOP") | |
1447 else | |
1448 GameTooltip:SetPoint("LEFT",button,"RIGHT") | |
1449 end | |
1450 | |
1451 GameTooltip:SetText(name, 1, .82, 0, 1) | |
1452 | |
1453 if type(desc) == "string" then | |
1454 GameTooltip:AddLine(desc, 1, 1, 1, 1) | |
1455 end | |
1456 | |
1457 GameTooltip:Show() | |
1458 end | |
1459 | |
1460 local function TreeOnButtonLeave(widget, event, value, button) | |
1461 GameTooltip:Hide() | |
1462 end | |
1463 | |
1464 | |
1465 local function GroupExists(appName, options, path, uniquevalue) | |
1466 if not uniquevalue then return false end | |
1467 | |
1468 local feedpath = new() | |
1469 local temppath = new() | |
1470 for i = 1, #path do | |
1471 feedpath[i] = path[i] | |
1472 end | |
1473 | |
1474 BuildPath(feedpath, ("\001"):split(uniquevalue)) | |
1475 | |
1476 local group = options | |
1477 for i = 1, #feedpath do | |
1478 local v = feedpath[i] | |
1479 temppath[i] = v | |
1480 group = GetSubOption(group, v) | |
1481 | |
1482 if not group or group.type ~= "group" or CheckOptionHidden(group, options, temppath, appName) then | |
1483 del(feedpath) | |
1484 del(temppath) | |
1485 return false | |
1486 end | |
1487 end | |
1488 del(feedpath) | |
1489 del(temppath) | |
1490 return true | |
1491 end | |
1492 | |
1493 local function GroupSelected(widget, event, uniquevalue) | |
1494 | |
1495 local user = widget:GetUserDataTable() | |
1496 | |
1497 local options = user.options | |
1498 local option = user.option | |
1499 local path = user.path | |
1500 local rootframe = user.rootframe | |
1501 | |
1502 local feedpath = new() | |
1503 for i = 1, #path do | |
1504 feedpath[i] = path[i] | |
1505 end | |
1506 | |
1507 BuildPath(feedpath, ("\001"):split(uniquevalue)) | |
1508 local group = options | |
1509 for i = 1, #feedpath do | |
1510 group = GetSubOption(group, feedpath[i]) | |
1511 end | |
1512 widget:ReleaseChildren() | |
1513 AceConfigDialog:FeedGroup(user.appName,options,widget,rootframe,feedpath) | |
1514 | |
1515 del(feedpath) | |
1516 end | |
1517 | |
1518 | |
1519 | |
1520 --[[ | |
1521 -- INTERNAL -- | |
1522 This function will feed one group, and any inline child groups into the given container | |
1523 Select Groups will only have the selection control (tree, tabs, dropdown) fed in | |
1524 and have a group selected, this event will trigger the feeding of child groups | |
1525 | |
1526 Rules: | |
1527 If the group is Inline, FeedOptions | |
1528 If the group has no child groups, FeedOptions | |
1529 | |
1530 If the group is a tab or select group, FeedOptions then add the Group Control | |
1531 If the group is a tree group FeedOptions then | |
1532 its parent isnt a tree group: then add the tree control containing this and all child tree groups | |
1533 if its parent is a tree group, its already a node on a tree | |
1534 --]] | |
1535 | |
1536 function AceConfigDialog:FeedGroup(appName,options,container,rootframe,path, isRoot) | |
1537 local group = options | |
1538 --follow the path to get to the curent group | |
1539 local inline | |
1540 local grouptype, parenttype = options.childGroups, "none" | |
1541 | |
1542 | |
1543 for i = 1, #path do | |
1544 local v = path[i] | |
1545 group = GetSubOption(group, v) | |
1546 inline = inline or pickfirstset(v.dialogInline,v.guiInline,v.inline, false) | |
1547 parenttype = grouptype | |
1548 grouptype = group.childGroups | |
1549 end | |
1550 | |
1551 if not parenttype then | |
1552 parenttype = "tree" | |
1553 end | |
1554 | |
1555 --check if the group has child groups | |
1556 local hasChildGroups | |
1557 for k, v in pairs(group.args) do | |
1558 if v.type == "group" and not pickfirstset(v.dialogInline,v.guiInline,v.inline, false) and not CheckOptionHidden(v, options, path, appName) then | |
1559 hasChildGroups = true | |
1560 end | |
1561 end | |
1562 if group.plugins then | |
1563 for plugin, t in pairs(group.plugins) do | |
1564 for k, v in pairs(t) do | |
1565 if v.type == "group" and not pickfirstset(v.dialogInline,v.guiInline,v.inline, false) and not CheckOptionHidden(v, options, path, appName) then | |
1566 hasChildGroups = true | |
1567 end | |
1568 end | |
1569 end | |
1570 end | |
1571 | |
1572 container:SetLayout("flow") | |
1573 local scroll | |
1574 | |
1575 --Add a scrollframe if we are not going to add a group control, this is the inverse of the conditions for that later on | |
1576 if (not (hasChildGroups and not inline)) or (grouptype ~= "tab" and grouptype ~= "select" and (parenttype == "tree" and not isRoot)) then | |
1577 if container.type ~= "InlineGroup" and container.type ~= "SimpleGroup" then | |
1578 scroll = gui:Create("ScrollFrame") | |
1579 scroll:SetLayout("flow") | |
1580 scroll.width = "fill" | |
1581 scroll.height = "fill" | |
1582 container:SetLayout("fill") | |
1583 container:AddChild(scroll) | |
1584 container = scroll | |
1585 end | |
1586 end | |
1587 | |
1588 FeedOptions(appName,options,container,rootframe,path,group,nil) | |
1589 | |
1590 if scroll then | |
1591 container:PerformLayout() | |
1592 local status = self:GetStatusTable(appName, path) | |
1593 if not status.scroll then | |
1594 status.scroll = {} | |
1595 end | |
1596 scroll:SetStatusTable(status.scroll) | |
1597 end | |
1598 | |
1599 if hasChildGroups and not inline then | |
1600 local name = GetOptionsMemberValue("name", group, options, path, appName) | |
1601 if grouptype == "tab" then | |
1602 | |
1603 local tab = gui:Create("TabGroup") | |
1604 InjectInfo(tab, options, group, path, rootframe, appName) | |
1605 tab:SetCallback("OnGroupSelected", GroupSelected) | |
1606 tab:SetCallback("OnTabEnter", TreeOnButtonEnter) | |
1607 tab:SetCallback("OnTabLeave", TreeOnButtonLeave) | |
1608 | |
1609 local status = AceConfigDialog:GetStatusTable(appName, path) | |
1610 if not status.groups then | |
1611 status.groups = {} | |
1612 end | |
1613 tab:SetStatusTable(status.groups) | |
1614 tab.width = "fill" | |
1615 tab.height = "fill" | |
1616 | |
1617 local tabs = BuildGroups(group, options, path, appName) | |
1618 tab:SetTabs(tabs) | |
1619 tab:SetUserData("tablist", tabs) | |
1620 | |
1621 for i = 1, #tabs do | |
1622 local entry = tabs[i] | |
1623 if not entry.disabled then | |
1624 tab:SelectTab((GroupExists(appName, options, path,status.groups.selected) and status.groups.selected) or entry.value) | |
1625 break | |
1626 end | |
1627 end | |
1628 | |
1629 container:AddChild(tab) | |
1630 | |
1631 elseif grouptype == "select" then | |
1632 | |
1633 local select = gui:Create("DropdownGroup") | |
1634 select:SetTitle(name) | |
1635 InjectInfo(select, options, group, path, rootframe, appName) | |
1636 select:SetCallback("OnGroupSelected", GroupSelected) | |
1637 local status = AceConfigDialog:GetStatusTable(appName, path) | |
1638 if not status.groups then | |
1639 status.groups = {} | |
1640 end | |
1641 select:SetStatusTable(status.groups) | |
1642 local grouplist, orderlist = BuildSelect(group, options, path, appName) | |
1643 select:SetGroupList(grouplist, orderlist) | |
1644 select:SetUserData("grouplist", grouplist) | |
1645 select:SetUserData("orderlist", orderlist) | |
1646 | |
1647 local firstgroup = orderlist[1] | |
1648 if firstgroup then | |
1649 select:SetGroup((GroupExists(appName, options, path,status.groups.selected) and status.groups.selected) or firstgroup) | |
1650 end | |
1651 | |
1652 select.width = "fill" | |
1653 select.height = "fill" | |
1654 | |
1655 container:AddChild(select) | |
1656 | |
1657 --assume tree group by default | |
1658 --if parenttype is tree then this group is already a node on that tree | |
1659 elseif (parenttype ~= "tree") or isRoot then | |
1660 local tree = gui:Create("TreeGroup") | |
1661 InjectInfo(tree, options, group, path, rootframe, appName) | |
1662 tree:EnableButtonTooltips(false) | |
1663 | |
1664 tree.width = "fill" | |
1665 tree.height = "fill" | |
1666 | |
1667 tree:SetCallback("OnGroupSelected", GroupSelected) | |
1668 tree:SetCallback("OnButtonEnter", TreeOnButtonEnter) | |
1669 tree:SetCallback("OnButtonLeave", TreeOnButtonLeave) | |
1670 | |
1671 local status = AceConfigDialog:GetStatusTable(appName, path) | |
1672 if not status.groups then | |
1673 status.groups = {} | |
1674 end | |
1675 local treedefinition = BuildGroups(group, options, path, appName, true) | |
1676 tree:SetStatusTable(status.groups) | |
1677 | |
1678 tree:SetTree(treedefinition) | |
1679 tree:SetUserData("tree",treedefinition) | |
1680 | |
1681 for i = 1, #treedefinition do | |
1682 local entry = treedefinition[i] | |
1683 if not entry.disabled then | |
1684 tree:SelectByValue((GroupExists(appName, options, path,status.groups.selected) and status.groups.selected) or entry.value) | |
1685 break | |
1686 end | |
1687 end | |
1688 | |
1689 container:AddChild(tree) | |
1690 end | |
1691 end | |
1692 end | |
1693 | |
1694 local old_CloseSpecialWindows | |
1695 | |
1696 | |
1697 local function RefreshOnUpdate(this) | |
1698 for appName in pairs(this.closing) do | |
1699 if AceConfigDialog.OpenFrames[appName] then | |
1700 AceConfigDialog.OpenFrames[appName]:Hide() | |
1701 end | |
1702 if AceConfigDialog.BlizOptions and AceConfigDialog.BlizOptions[appName] then | |
1703 for key, widget in pairs(AceConfigDialog.BlizOptions[appName]) do | |
1704 if not widget:IsVisible() then | |
1705 widget:ReleaseChildren() | |
1706 end | |
1707 end | |
1708 end | |
1709 this.closing[appName] = nil | |
1710 end | |
1711 | |
1712 if this.closeAll then | |
1713 for k, v in pairs(AceConfigDialog.OpenFrames) do | |
1714 if not this.closeAllOverride[k] then | |
1715 v:Hide() | |
1716 end | |
1717 end | |
1718 this.closeAll = nil | |
1719 wipe(this.closeAllOverride) | |
1720 end | |
1721 | |
1722 for appName in pairs(this.apps) do | |
1723 if AceConfigDialog.OpenFrames[appName] then | |
1724 local user = AceConfigDialog.OpenFrames[appName]:GetUserDataTable() | |
1725 AceConfigDialog:Open(appName, unpack(user.basepath or emptyTbl)) | |
1726 end | |
1727 if AceConfigDialog.BlizOptions and AceConfigDialog.BlizOptions[appName] then | |
1728 for key, widget in pairs(AceConfigDialog.BlizOptions[appName]) do | |
1729 local user = widget:GetUserDataTable() | |
1730 if widget:IsVisible() then | |
1731 AceConfigDialog:Open(widget:GetUserData("appName"), widget, unpack(user.basepath or emptyTbl)) | |
1732 end | |
1733 end | |
1734 end | |
1735 this.apps[appName] = nil | |
1736 end | |
1737 this:SetScript("OnUpdate", nil) | |
1738 end | |
1739 | |
1740 -- Upgrade the OnUpdate script as well, if needed. | |
1741 if AceConfigDialog.frame:GetScript("OnUpdate") then | |
1742 AceConfigDialog.frame:SetScript("OnUpdate", RefreshOnUpdate) | |
1743 end | |
1744 | |
1745 --- Close all open options windows | |
1746 function AceConfigDialog:CloseAll() | |
1747 AceConfigDialog.frame.closeAll = true | |
1748 AceConfigDialog.frame:SetScript("OnUpdate", RefreshOnUpdate) | |
1749 if next(self.OpenFrames) then | |
1750 return true | |
1751 end | |
1752 end | |
1753 | |
1754 --- Close a specific options window. | |
1755 -- @param appName The application name as given to `:RegisterOptionsTable()` | |
1756 function AceConfigDialog:Close(appName) | |
1757 if self.OpenFrames[appName] then | |
1758 AceConfigDialog.frame.closing[appName] = true | |
1759 AceConfigDialog.frame:SetScript("OnUpdate", RefreshOnUpdate) | |
1760 return true | |
1761 end | |
1762 end | |
1763 | |
1764 -- Internal -- Called by AceConfigRegistry | |
1765 function AceConfigDialog:ConfigTableChanged(event, appName) | |
1766 AceConfigDialog.frame.apps[appName] = true | |
1767 AceConfigDialog.frame:SetScript("OnUpdate", RefreshOnUpdate) | |
1768 end | |
1769 | |
1770 reg.RegisterCallback(AceConfigDialog, "ConfigTableChange", "ConfigTableChanged") | |
1771 | |
1772 --- Sets the default size of the options window for a specific application. | |
1773 -- @param appName The application name as given to `:RegisterOptionsTable()` | |
1774 -- @param width The default width | |
1775 -- @param height The default height | |
1776 function AceConfigDialog:SetDefaultSize(appName, width, height) | |
1777 local status = AceConfigDialog:GetStatusTable(appName) | |
1778 if type(width) == "number" and type(height) == "number" then | |
1779 status.width = width | |
1780 status.height = height | |
1781 end | |
1782 end | |
1783 | |
1784 --- Open an option window at the specified path (if any). | |
1785 -- This function can optionally feed the group into a pre-created container | |
1786 -- instead of creating a new container frame. | |
1787 -- @paramsig appName [, container][, ...] | |
1788 -- @param appName The application name as given to `:RegisterOptionsTable()` | |
1789 -- @param container An optional container frame to feed the options into | |
1790 -- @param ... The path to open after creating the options window (see `:SelectGroup` for details) | |
1791 function AceConfigDialog:Open(appName, container, ...) | |
1792 if not old_CloseSpecialWindows then | |
1793 old_CloseSpecialWindows = CloseSpecialWindows | |
1794 CloseSpecialWindows = function() | |
1795 local found = old_CloseSpecialWindows() | |
1796 return self:CloseAll() or found | |
1797 end | |
1798 end | |
1799 local app = reg:GetOptionsTable(appName) | |
1800 if not app then | |
1801 error(("%s isn't registed with AceConfigRegistry, unable to open config"):format(appName), 2) | |
1802 end | |
1803 local options = app("dialog", MAJOR) | |
1804 | |
1805 local f | |
1806 | |
1807 local path = new() | |
1808 local name = GetOptionsMemberValue("name", options, options, path, appName) | |
1809 | |
1810 --If an optional path is specified add it to the path table before feeding the options | |
1811 --as container is optional as well it may contain the first element of the path | |
1812 if type(container) == "string" then | |
1813 tinsert(path, container) | |
1814 container = nil | |
1815 end | |
1816 for n = 1, select("#",...) do | |
1817 tinsert(path, (select(n, ...))) | |
1818 end | |
1819 | |
1820 --if a container is given feed into that | |
1821 if container then | |
1822 f = container | |
1823 f:ReleaseChildren() | |
1824 f:SetUserData("appName", appName) | |
1825 f:SetUserData("iscustom", true) | |
1826 if #path > 0 then | |
1827 f:SetUserData("basepath", copy(path)) | |
1828 end | |
1829 local status = AceConfigDialog:GetStatusTable(appName) | |
1830 if not status.width then | |
1831 status.width = 700 | |
1832 end | |
1833 if not status.height then | |
1834 status.height = 500 | |
1835 end | |
1836 if f.SetStatusTable then | |
1837 f:SetStatusTable(status) | |
1838 end | |
1839 if f.SetTitle then | |
1840 f:SetTitle(name or "") | |
1841 end | |
1842 else | |
1843 if not self.OpenFrames[appName] then | |
1844 f = gui:Create("Frame") | |
1845 self.OpenFrames[appName] = f | |
1846 else | |
1847 f = self.OpenFrames[appName] | |
1848 end | |
1849 f:ReleaseChildren() | |
1850 f:SetCallback("OnClose", FrameOnClose) | |
1851 f:SetUserData("appName", appName) | |
1852 if #path > 0 then | |
1853 f:SetUserData("basepath", copy(path)) | |
1854 end | |
1855 f:SetTitle(name or "") | |
1856 local status = AceConfigDialog:GetStatusTable(appName) | |
1857 f:SetStatusTable(status) | |
1858 end | |
1859 | |
1860 self:FeedGroup(appName,options,f,f,path,true) | |
1861 if f.Show then | |
1862 f:Show() | |
1863 end | |
1864 del(path) | |
1865 | |
1866 if AceConfigDialog.frame.closeAll then | |
1867 -- close all is set, but thats not good, since we're just opening here, so force it | |
1868 AceConfigDialog.frame.closeAllOverride[appName] = true | |
1869 end | |
1870 end | |
1871 | |
1872 -- convert pre-39 BlizOptions structure to the new format | |
1873 if oldminor and oldminor < 39 and AceConfigDialog.BlizOptions then | |
1874 local old = AceConfigDialog.BlizOptions | |
1875 local new = {} | |
1876 for key, widget in pairs(old) do | |
1877 local appName = widget:GetUserData("appName") | |
1878 if not new[appName] then new[appName] = {} end | |
1879 new[appName][key] = widget | |
1880 end | |
1881 AceConfigDialog.BlizOptions = new | |
1882 else | |
1883 AceConfigDialog.BlizOptions = AceConfigDialog.BlizOptions or {} | |
1884 end | |
1885 | |
1886 local function FeedToBlizPanel(widget, event) | |
1887 local path = widget:GetUserData("path") | |
1888 AceConfigDialog:Open(widget:GetUserData("appName"), widget, unpack(path or emptyTbl)) | |
1889 end | |
1890 | |
1891 local function ClearBlizPanel(widget, event) | |
1892 local appName = widget:GetUserData("appName") | |
1893 AceConfigDialog.frame.closing[appName] = true | |
1894 AceConfigDialog.frame:SetScript("OnUpdate", RefreshOnUpdate) | |
1895 end | |
1896 | |
1897 --- Add an option table into the Blizzard Interface Options panel. | |
1898 -- You can optionally supply a descriptive name to use and a parent frame to use, | |
1899 -- as well as a path in the options table.\\ | |
1900 -- If no name is specified, the appName will be used instead. | |
1901 -- | |
1902 -- If you specify a proper `parent` (by name), the interface options will generate a | |
1903 -- tree layout. Note that only one level of children is supported, so the parent always | |
1904 -- has to be a head-level note. | |
1905 -- | |
1906 -- This function returns a reference to the container frame registered with the Interface | |
1907 -- Options. You can use this reference to open the options with the API function | |
1908 -- `InterfaceOptionsFrame_OpenToCategory`. | |
1909 -- @param appName The application name as given to `:RegisterOptionsTable()` | |
1910 -- @param name A descriptive name to display in the options tree (defaults to appName) | |
1911 -- @param parent The parent to use in the interface options tree. | |
1912 -- @param ... The path in the options table to feed into the interface options panel. | |
1913 -- @return The reference to the frame registered into the Interface Options. | |
1914 function AceConfigDialog:AddToBlizOptions(appName, name, parent, ...) | |
1915 local BlizOptions = AceConfigDialog.BlizOptions | |
1916 | |
1917 local key = appName | |
1918 for n = 1, select("#", ...) do | |
1919 key = key.."\001"..select(n, ...) | |
1920 end | |
1921 | |
1922 if not BlizOptions[appName] then | |
1923 BlizOptions[appName] = {} | |
1924 end | |
1925 | |
1926 if not BlizOptions[appName][key] then | |
1927 local group = gui:Create("BlizOptionsGroup") | |
1928 BlizOptions[appName][key] = group | |
1929 group:SetName(name or appName, parent) | |
1930 | |
1931 group:SetTitle(name or appName) | |
1932 group:SetUserData("appName", appName) | |
1933 if select("#", ...) > 0 then | |
1934 local path = {} | |
1935 for n = 1, select("#",...) do | |
1936 tinsert(path, (select(n, ...))) | |
1937 end | |
1938 group:SetUserData("path", path) | |
1939 end | |
1940 group:SetCallback("OnShow", FeedToBlizPanel) | |
1941 group:SetCallback("OnHide", ClearBlizPanel) | |
1942 InterfaceOptions_AddCategory(group.frame) | |
1943 return group.frame | |
1944 else | |
1945 error(("%s has already been added to the Blizzard Options Window with the given path"):format(appName), 2) | |
1946 end | |
1947 end |