vanilla-wow-addons – Blame information for rev 1

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 office 1 --[[
2 Name: AceDB-2.0
3 Revision: $Rev: 8838 $
4 Author(s): ckknight (ckknight@gmail.com)
5 Inspired By: AceDB 1.x by Turan (<email here>)
6 Website: http://www.wowace.com/
7 Documentation: http://wiki.wowace.com/index.php/AceDB-2.0
8 SVN: http://svn.wowace.com/root/trunk/Ace2/AceDB-2.0
9 Description: Mixin to allow for fast, clean, and featureful saved variable
10 access.
11 Dependencies: AceLibrary, AceOO-2.0, AceEvent-2.0
12 ]]
13  
14 local MAJOR_VERSION = "AceDB-2.0"
15 local MINOR_VERSION = "$Revision: 8838 $"
16  
17 if not AceLibrary then error(MAJOR_VERSION .. " requires AceLibrary") end
18 if not AceLibrary:IsNewVersion(MAJOR_VERSION, MINOR_VERSION) then return end
19  
20 if not AceLibrary:HasInstance("AceOO-2.0") then error(MAJOR_VERSION .. " requires AceOO-2.0") end
21  
22 -- localize --
23 local ACTIVE = "Active"
24 local STATE = "State"
25 local TOGGLE_ACTIVE = "Suspend/resume this addon"
26 local MAP_ACTIVESUSPENDED = { [true] = "|cff00ff00Active|r", [false] = "|cffff0000Suspended|r" }
27 local SET_PROFILE = "Set profile for this addon"
28 local SET_PROFILE_USAGE = "{char || class || realm || <profile name>}"
29 local PROFILE = "Profile"
30 local PLAYER_OF_REALM = "%s of %s"
31 -- localize --
32  
33 local AceOO = AceLibrary("AceOO-2.0")
34 local AceEvent
35 local Mixin = AceOO.Mixin
36 local AceDB = Mixin {
37 "RegisterDB",
38 "RegisterDefaults",
39 "ResetDB",
40 "SetProfile",
41 "GetProfile",
42 "ToggleActive",
43 "IsActive",
44 "AcquireDBNamespace",
45 }
46 local Dewdrop = AceLibrary:HasInstance("Dewdrop-2.0") and AceLibrary("Dewdrop-2.0")
47  
48 local _G = getfenv(0)
49  
50 local function inheritDefaults(t, defaults)
51 if not defaults then
52 return t
53 end
54 for k,v in pairs(defaults) do
55 if k == "*" then
56 local v = v
57 if type(v) == "table" then
58 setmetatable(t, {
59 __index = function(self, key)
60 if key == nil then
61 return nil
62 end
63 self[key] = {}
64 inheritDefaults(self[key], v)
65 return self[key]
66 end
67 } )
68 else
69 setmetatable(t, {
70 __index = function(self, key)
71 if key == nil then
72 return nil
73 end
74 self[key] = v
75 return self[key]
76 end
77 } )
78 end
79 for key in pairs(t) do
80 if (defaults[key] == nil or key == "*") and type(t[key]) == "table" then
81 inheritDefaults(t[key], v)
82 end
83 end
84 else
85 if type(v) == "table" then
86 if type(t[k]) ~= "table" then
87 t[k] = {}
88 end
89 inheritDefaults(t[k], v)
90 elseif t[k] == nil then
91 t[k] = v
92 end
93 end
94 end
95 return t
96 end
97  
98 local _,race = UnitRace("player")
99 local faction
100 if race == "Orc" or race == "Scourge" or race == "Troll" or race == "Tauren" then
101 faction = FACTION_HORDE
102 else
103 faction = FACTION_ALLIANCE
104 end
105 local charID = string.format(PLAYER_OF_REALM, UnitName("player"), (string.gsub(GetRealmName(), "^%s*(.-)%s*$", "%1")))
106 local realm = string.gsub(GetRealmName(), "^%s*(.-)%s*$", "%1")
107 local realmID = realm .. " - " .. faction
108 local classID = UnitClass("player")
109  
110 AceDB.CHAR_ID = charID
111 AceDB.REALM_ID = realmID
112 AceDB.CLASS_ID = classID
113  
114 AceDB.FACTION = faction
115 AceDB.REALM = realm
116 AceDB.NAME = UnitName("player")
117  
118 local new, del
119 do
120 local list = setmetatable({}, {__mode="k"})
121 function new()
122 local t = next(list)
123 if t then
124 list[t] = nil
125 return t
126 else
127 return {}
128 end
129 end
130  
131 function del(t)
132 setmetatable(t, nil)
133 for k in pairs(t) do
134 t[k] = nil
135 end
136 table.setn(t, 0)
137 list[t] = true
138 end
139 end
140  
141 local caseInsensitive_mt = {
142 __index = function(self, key)
143 if type(key) ~= "string" then
144 return nil
145 end
146 local lowerKey = string.lower(key)
147 for k,v in pairs(self) do
148 if string.lower(k) == lowerKey then
149 return self[k]
150 end
151 end
152 end,
153 __newindex = function(self, key, value)
154 if type(key) ~= "string" then
155 return error("table index is nil", 2)
156 end
157 local lowerKey = string.lower(key)
158 for k in pairs(self) do
159 if string.lower(k) == lowerKey then
160 rawset(self, k, nil)
161 rawset(self, key, value)
162 return
163 end
164 end
165 rawset(self, key, value)
166 end
167 }
168  
169 local db_mt = { __index = function(db, key)
170 if key == "char" then
171 if db.charName then
172 if type(_G[db.charName]) ~= "table" then
173 _G[db.charName] = {}
174 end
175 if type(_G[db.charName].global) ~= "table" then
176 _G[db.charName].global = {}
177 end
178 rawset(db, 'char', _G[db.charName].global)
179 else
180 if type(db.raw.chars) ~= "table" then
181 db.raw.chars = {}
182 end
183 local id = charID
184 if type(db.raw.chars[id]) ~= "table" then
185 db.raw.chars[id] = {}
186 end
187 rawset(db, 'char', db.raw.chars[id])
188 end
189 if db.defaults and db.defaults.char then
190 inheritDefaults(db.char, db.defaults.char)
191 end
192 return db.char
193 elseif key == "realm" then
194 if type(db.raw.realms) ~= "table" then
195 db.raw.realms = {}
196 end
197 local id = realmID
198 if type(db.raw.realms[id]) ~= "table" then
199 db.raw.realms[id] = {}
200 end
201 rawset(db, 'realm', db.raw.realms[id])
202 if db.defaults and db.defaults.realm then
203 inheritDefaults(db.realm, db.defaults.realm)
204 end
205 return db.realm
206 elseif key == "account" then
207 if type(db.raw.account) ~= "table" then
208 db.raw.account = {}
209 end
210 rawset(db, 'account', db.raw.account)
211 if db.defaults and db.defaults.account then
212 inheritDefaults(db.account, db.defaults.account)
213 end
214 return db.account
215 elseif key == "class" then
216 if type(db.raw.classes) ~= "table" then
217 db.raw.classes = {}
218 end
219 local id = classID
220 if type(db.raw.classes[id]) ~= "table" then
221 db.raw.classes[id] = {}
222 end
223 rawset(db, 'class', db.raw.classes[id])
224 if db.defaults and db.defaults.class then
225 inheritDefaults(db.class, db.defaults.class)
226 end
227 return db.class
228 elseif key == "profile" then
229 if type(db.raw.profiles) ~= "table" then
230 db.raw.profiles = setmetatable({}, caseInsensitive_mt)
231 else
232 setmetatable(db.raw.profiles, caseInsensitive_mt)
233 end
234 local id = db.raw.currentProfile[charID]
235 if id == "char" then
236 id = "char/" .. charID
237 elseif id == "class" then
238 id = "class/" .. classID
239 elseif id == "realm" then
240 id = "realm/" .. realmID
241 end
242 if type(db.raw.profiles[id]) ~= "table" then
243 db.raw.profiles[id] = {}
244 end
245 rawset(db, 'profile', db.raw.profiles[id])
246 if db.defaults and db.defaults.profile then
247 inheritDefaults(db.profile, db.defaults.profile)
248 end
249 return db.profile
250 elseif key == "raw" or key == "defaults" or key == "name" or key == "charName" or key == "namespaces" then
251 return nil
252 end
253 error(string.format('Cannot access key %q in db table. You may want to use db.profile[%q]', tostring(key), tostring(key)), 2)
254 end, __newindex = function(db, key, value)
255 error(string.format('Cannot access key %q in db table. You may want to use db.profile[%q]', tostring(key), tostring(key)), 2)
256 end }
257  
258 local CrawlForSerialization
259 local CrawlForDeserialization
260  
261 local function SerializeObject(o)
262 local t = { o:Serialize() }
263 t[0] = o.class:GetLibraryVersion()
264 CrawlForSerialization(t)
265 return t
266 end
267  
268 local function DeserializeObject(t)
269 CrawlForDeserialization(t)
270 local className = t[0]
271 for i = 20, 1, -1 do
272 if t[i] then
273 t.n = i
274 break
275 end
276 end
277 local o = AceLibrary(className):Deserialize(unpack(t))
278 t.n = 0
279 return o
280 end
281  
282 local function IsSerializable(t)
283 return AceOO.inherits(t, AceOO.Class) and t.class and type(t.class.Deserialize) == "function" and type(t.Serialize) == "function" and type(t.class.GetLibraryVersion) == "function"
284 end
285  
286 function CrawlForSerialization(t)
287 local tmp = new()
288 for k,v in pairs(t) do
289 tmp[k] = v
290 end
291 for k,v in pairs(tmp) do
292 if type(v) == "table" and type(v[0]) ~= "userdata" then
293 if IsSerializable(v) then
294 v = SerializeObject(v)
295 t[k] = v
296 else
297 CrawlForSerialization(v)
298 end
299 end
300 if type(k) == "table" and type(k[0]) ~= "userdata" then
301 if IsSerializable(k) then
302 t[k] = nil
303 t[SerializeObject(k)] = v
304 else
305 CrawlForSerialization(k)
306 end
307 end
308 tmp[k] = nil
309 k = nil
310 end
311 tmp = del(tmp)
312 end
313  
314 local function IsDeserializable(t)
315 return type(t[0]) == "string" and AceLibrary:HasInstance(t[0])
316 end
317  
318 function CrawlForDeserialization(t)
319 local tmp = new()
320 for k,v in pairs(t) do
321 tmp[k] = v
322 end
323 for k,v in pairs(tmp) do
324 if type(v) == "table" then
325 if IsDeserializable(v) then
326 t[k] = DeserializeObject(v)
327 del(v)
328 v = t[k]
329 elseif type(v[0]) ~= "userdata" then
330 CrawlForDeserialization(v)
331 end
332 end
333 if type(k) == "table" then
334 if IsDeserializable(k) then
335 t[k] = nil
336 t[DeserializeObject(k)] = v
337 del(k)
338 elseif type(k[0]) ~= "userdata" then
339 CrawlForDeserialization(k)
340 end
341 end
342 tmp[k] = nil
343 k = nil
344 end
345 tmp = del(tmp)
346 end
347  
348 local namespace_mt = { __index = function(namespace, key)
349 local db = namespace.db
350 local name = namespace.name
351 if key == "char" then
352 if db.charName then
353 if type(_G[db.charName]) ~= "table" then
354 _G[db.charName] = {}
355 end
356 if type(_G[db.charName].namespaces) ~= "table" then
357 _G[db.charName].namespaces = {}
358 end
359 if type(_G[db.charName].namespaces[name]) ~= "table" then
360 _G[db.charName].namespaces[name] = {}
361 end
362 rawset(namespace, 'char', _G[db.charName].namespaces[name])
363 else
364 if type(db.raw.namespaces) ~= "table" then
365 db.raw.namespaces = {}
366 end
367 if type(db.raw.namespaces[name]) ~= "table" then
368 db.raw.namespaces[name] = {}
369 end
370 if type(db.raw.namespaces[name].chars) ~= "table" then
371 db.raw.namespaces[name].chars = {}
372 end
373 local id = charID
374 if type(db.raw.namespaces[name].chars[id]) ~= "table" then
375 db.raw.namespaces[name].chars[id] = {}
376 end
377 rawset(namespace, 'char', db.raw.namespaces[name].chars[id])
378 end
379 if namespace.defaults and namespace.defaults.char then
380 inheritDefaults(namespace.char, namespace.defaults.char)
381 end
382 return namespace.char
383 elseif key == "realm" then
384 if type(db.raw.namespaces) ~= "table" then
385 db.raw.namespaces = {}
386 end
387 if type(db.raw.namespaces[name]) ~= "table" then
388 db.raw.namespaces[name] = {}
389 end
390 if type(db.raw.namespaces[name].realms) ~= "table" then
391 db.raw.namespaces[name].realms = {}
392 end
393 local id = realmID
394 if type(db.raw.namespaces[name].realms[id]) ~= "table" then
395 db.raw.namespaces[name].realms[id] = {}
396 end
397 rawset(namespace, 'realm', db.raw.namespaces[name].realms[id])
398 if namespace.defaults and namespace.defaults.realm then
399 inheritDefaults(namespace.realm, namespace.defaults.realm)
400 end
401 return namespace.realm
402 elseif key == "account" then
403 if type(db.raw.namespaces) ~= "table" then
404 db.raw.namespaces = {}
405 end
406 if type(db.raw.namespaces[name]) ~= "table" then
407 db.raw.namespaces[name] = {}
408 end
409 if type(db.raw.namespaces[name].account) ~= "table" then
410 db.raw.namespaces[name].account = {}
411 end
412 rawset(namespace, 'account', db.raw.namespaces[name].account)
413 if namespace.defaults and namespace.defaults.account then
414 inheritDefaults(namespace.account, namespace.defaults.account)
415 end
416 return namespace.account
417 elseif key == "class" then
418 if type(db.raw.namespaces) ~= "table" then
419 db.raw.namespaces = {}
420 end
421 if type(db.raw.namespaces[name]) ~= "table" then
422 db.raw.namespaces[name] = {}
423 end
424 if type(db.raw.namespaces[name].classes) ~= "table" then
425 db.raw.namespaces[name].classes = {}
426 end
427 local id = classID
428 if type(db.raw.namespaces[name].classes[id]) ~= "table" then
429 db.raw.namespaces[name].classes[id] = {}
430 end
431 rawset(namespace, 'class', db.raw.namespaces[name].classes[id])
432 if namespace.defaults and namespace.defaults.class then
433 inheritDefaults(namespace.class, namespace.defaults.class)
434 end
435 return namespace.class
436 elseif key == "profile" then
437 if type(db.raw.namespaces) ~= "table" then
438 db.raw.namespaces = {}
439 end
440 if type(db.raw.namespaces[name]) ~= "table" then
441 db.raw.namespaces[name] = {}
442 end
443 if type(db.raw.namespaces[name].profiles) ~= "table" then
444 db.raw.namespaces[name].profiles = setmetatable({}, caseInsensitive_mt)
445 else
446 setmetatable(db.raw.namespaces[name].profiles, caseInsensitive_mt)
447 end
448 local id = db.raw.currentProfile[charID]
449 if id == "char" then
450 id = "char/" .. charID
451 elseif id == "class" then
452 id = "class/" .. classID
453 elseif id == "realm" then
454 id = "realm/" .. realmID
455 end
456 if type(db.raw.namespaces[name].profiles[id]) ~= "table" then
457 db.raw.namespaces[name].profiles[id] = {}
458 end
459 rawset(namespace, 'profile', db.raw.namespaces[name].profiles[id])
460 if namespace.defaults and namespace.defaults.profile then
461 inheritDefaults(namespace.profile, namespace.defaults.profile)
462 end
463 return namespace.profile
464 elseif key == "defaults" or key == "name" or key == "db" then
465 return nil
466 end
467 error(string.format('Cannot access key %q in db table. You may want to use db.profile[%q]', tostring(key), tostring(key)), 2)
468 end, __newindex = function(db, key, value)
469 error(string.format('Cannot access key %q in db table. You may want to use db.profile[%q]', tostring(key), tostring(key)), 2)
470 end }
471  
472 function AceDB:InitializeDB(addonName)
473 local db = self.db
474  
475 if not db then
476 if addonName then
477 AceDB.addonsLoaded[addonName] = true
478 end
479 return
480 end
481  
482 if db.raw then
483 -- someone manually initialized
484 return
485 end
486  
487 if type(_G[db.name]) ~= "table" then
488 _G[db.name] = {}
489 else
490 CrawlForDeserialization(_G[db.name])
491 end
492 if type(_G[db.charName]) == "table" then
493 CrawlForDeserialization(_G[db.charName])
494 end
495 rawset(db, 'raw', _G[db.name])
496 if not db.raw.currentProfile then
497 db.raw.currentProfile = {}
498 end
499 if not db.raw.currentProfile[charID] then
500 db.raw.currentProfile[charID] = "Default"
501 end
502 if db.raw.disabled then
503 setmetatable(db.raw.disabled, caseInsensitive_mt)
504 end
505 if self['acedb-profile-copylist'] then
506 local t = self['acedb-profile-copylist']
507 for k,v in pairs(t) do
508 t[k] = nil
509 end
510 if db.raw.profiles then
511 for k in pairs(db.raw.profiles) do
512 if string.find(k, '^char/') then
513 local name = string.sub(k, 6)
514 if name ~= charID then
515 t[k] = 'Character: ' .. name
516 end
517 elseif string.find(k, '^realm/') then
518 local name = string.sub(k, 7)
519 if name ~= realmID then
520 t[k] = 'Realm: ' .. name
521 end
522 elseif string.find(k, '^class/') then
523 local name = string.sub(k, 7)
524 if name ~= classID then
525 t[k] = 'Class: ' .. name
526 end
527 end
528 end
529 end
530 end
531 if self['acedb-profile-list'] then
532 local t = self['acedb-profile-list']
533 for k,v in pairs(t) do
534 t[k] = nil
535 end
536 t.char = 'Character: ' .. charID
537 t.realm = 'Realm: ' .. realmID
538 t.class = 'Class: ' .. classID
539 t.Default = "Default"
540 if db.raw.profiles then
541 for k in pairs(db.raw.profiles) do
542 if not string.find(k, '^char/') and not string.find(k, '^realm/') and not string.find(k, '^class/') then
543 t[k] = k
544 end
545 end
546 end
547 end
548 setmetatable(db, db_mt)
549 end
550  
551 function AceDB:OnEmbedInitialize(target, name)
552 if name then
553 self:ADDON_LOADED(name)
554 end
555 self.InitializeDB(target, name)
556 end
557  
558 function AceDB:RegisterDB(name, charName)
559 AceDB:argCheck(name, 2, "string")
560 AceDB:argCheck(charName, 3, "string", "nil")
561 if self.db then
562 AceDB:error("Cannot call \"RegisterDB\" if self.db is set.")
563 end
564 local stack = debugstack()
565 local addonName = string.gsub(stack, ".-\n.-\\AddOns\\(.-)\\.*", "%1")
566 self.db = {
567 name = name,
568 charName = charName
569 }
570 if AceDB.addonsLoaded[addonName] then
571 AceDB.InitializeDB(self, addonName)
572 else
573 AceDB.addonsToBeInitialized[self] = addonName
574 end
575 AceDB.registry[self] = true
576 end
577  
578 function AceDB:RegisterDefaults(kind, defaults, a3)
579 local name
580 if a3 then
581 name, kind, defaults = kind, defaults, a3
582 AceDB:argCheck(name, 2, "string")
583 AceDB:argCheck(kind, 3, "string")
584 AceDB:argCheck(defaults, 4, "table")
585 if kind ~= "char" and kind ~= "class" and kind ~= "profile" and kind ~= "account" and kind ~= "realm" then
586 AceDB:error("Bad argument #3 to `RegisterDefaults' (\"char\", \"class\", \"profile\", \"account\", or \"realm\" expected, got %q)", kind)
587 end
588 else
589 AceDB:argCheck(kind, 2, "string")
590 AceDB:argCheck(defaults, 3, "table")
591 if kind ~= "char" and kind ~= "class" and kind ~= "profile" and kind ~= "account" and kind ~= "realm" then
592 AceDB:error("Bad argument #2 to `RegisterDefaults' (\"char\", \"class\", \"profile\", \"account\", or \"realm\" expected, got %q)", kind)
593 end
594 end
595 if type(self.db) ~= "table" or type(self.db.name) ~= "string" then
596 AceDB:error("Cannot call \"RegisterDefaults\" unless \"RegisterDB\" has been previously called.")
597 end
598 local db
599 if name then
600 local namespace = self:AcquireDBNamespace(name)
601 if namespace.defaults and namespace.defaults[kind] then
602 AceDB:error("\"RegisterDefaults\" has already been called for %q::%q.", name, kind)
603 end
604 db = namespace
605 else
606 if self.db.defaults and self.db.defaults[kind] then
607 AceDB:error("\"RegisterDefaults\" has already been called for %q.", kind)
608 end
609 db = self.db
610 end
611 if not db.defaults then
612 rawset(db, 'defaults', {})
613 end
614 db.defaults[kind] = defaults
615 if rawget(db, kind) then
616 inheritDefaults(db[kind], defaults)
617 end
618 end
619  
620 function AceDB:ResetDB(kind)
621 AceDB:argCheck(kind, 2, "nil", "string")
622 if not self.db or not self.db.raw then
623 AceDB:error("Cannot call \"ResetDB\" before \"RegisterDB\" has been called and before \"ADDON_LOADED\" has been fired.")
624 end
625 local db = self.db
626 if kind == nil then
627 if db.charName then
628 _G[db.charName] = nil
629 end
630 _G[db.name] = nil
631 rawset(db, 'raw', nil)
632 AceDB.InitializeDB(self)
633 if db.namespaces then
634 for name,v in pairs(db.namespaces) do
635 rawset(v, 'account', nil)
636 rawset(v, 'char', nil)
637 rawset(v, 'class', nil)
638 rawset(v, 'profile', nil)
639 rawset(v, 'realm', nil)
640 end
641 end
642 elseif kind == "account" then
643 db.raw.account = nil
644 rawset(db, 'account', nil)
645 if db.namespaces then
646 for name,v in pairs(db.namespaces) do
647 rawset(v, 'account', nil)
648 end
649 end
650 elseif kind == "char" then
651 if db.charName then
652 _G[db.charName] = nil
653 else
654 if db.raw.chars then
655 db.raw.chars[charID] = nil
656 end
657 if db.raw.namespaces then
658 for name,v in pairs(db.raw.namespaces) do
659 if v.chars then
660 v.chars[charID] = nil
661 end
662 end
663 end
664 end
665 rawset(db, 'char', nil)
666 if db.namespaces then
667 for name,v in pairs(db.namespaces) do
668 rawset(v, 'char', nil)
669 end
670 end
671 elseif kind == "realm" then
672 if db.raw.realms then
673 db.raw.realms[realmID] = nil
674 end
675 rawset(db, 'realm', nil)
676 if db.raw.namespaces then
677 for name,v in pairs(db.raw.namespaces) do
678 if v.realms then
679 v.realms[realmID] = nil
680 end
681 end
682 end
683 if db.namespaces then
684 for name,v in pairs(db.namespaces) do
685 rawset(v, 'realm', nil)
686 end
687 end
688 elseif kind == "class" then
689 if db.raw.realms then
690 db.raw.realms[classID] = nil
691 end
692 rawset(db, 'class', nil)
693 if db.raw.namespaces then
694 for name,v in pairs(db.raw.namespaces) do
695 if v.classes then
696 v.classes[classID] = nil
697 end
698 end
699 end
700 if db.namespaces then
701 for name,v in pairs(db.namespaces) do
702 rawset(v, 'class', nil)
703 end
704 end
705 elseif kind == "profile" then
706 local id = db.raw.currentProfile and db.raw.currentProfile[charID] or "Default"
707 if id == "char" then
708 id = "char/" .. charID
709 elseif id == "class" then
710 id = "class/" .. classID
711 elseif id == "realm" then
712 id = "realm/" .. realmID
713 end
714 if db.raw.profiles then
715 db.raw.profiles[id] = nil
716 end
717 rawset(db, 'profile', nil)
718 if db.raw.namespaces then
719 for name,v in pairs(db.raw.namespaces) do
720 if v.profiles then
721 v.profiles[id] = nil
722 end
723 end
724 end
725 if db.namespaces then
726 for name,v in pairs(db.namespaces) do
727 rawset(v, 'profile', nil)
728 end
729 end
730 end
731 end
732  
733 local function cleanDefaults(t, defaults)
734 if defaults then
735 for k,v in pairs(defaults) do
736 if k == "*" then
737 if type(v) == "table" then
738 for k in pairs(t) do
739 if (defaults[k] == nil or k == "*") and type(t[k]) == "table" then
740 if cleanDefaults(t[k], v) then
741 t[k] = nil
742 end
743 end
744 end
745 else
746 for k in pairs(t) do
747 if (defaults[k] == nil or k == "*") and t[k] == v then
748 t[k] = nil
749 end
750 end
751 end
752 else
753 if type(v) == "table" then
754 if type(t[k]) == "table" then
755 if cleanDefaults(t[k], v) then
756 t[k] = nil
757 end
758 end
759 elseif t[k] == v then
760 t[k] = nil
761 end
762 end
763 end
764 end
765 return t and not next(t)
766 end
767  
768 function AceDB:GetProfile()
769 if not self.db or not self.db.raw then
770 return nil
771 end
772 if not self.db.raw.currentProfile then
773 self.db.raw.currentProfile = {}
774 end
775 if not self.db.raw.currentProfile[charID] then
776 self.db.raw.currentProfile[charID] = "Default"
777 end
778 local profile = self.db.raw.currentProfile[charID]
779 if profile == "char" then
780 return "char", "char/" .. charID
781 elseif profile == "class" then
782 return "class", "class/" .. classID
783 elseif profile == "realm" then
784 return "realm", "realm/" .. realmID
785 end
786 return profile, profile
787 end
788  
789 local function copyTable(to, from)
790 setmetatable(to, nil)
791 for k,v in pairs(from) do
792 if type(k) == "table" then
793 k = copyTable({}, k)
794 end
795 if type(v) == "table" then
796 v = copyTable({}, v)
797 end
798 to[k] = v
799 end
800 table.setn(to, table.getn(from))
801 setmetatable(to, from)
802 return to
803 end
804  
805 function AceDB:SetProfile(name, copyFrom)
806 AceDB:argCheck(name, 2, "string")
807 AceDB:argCheck(copyFrom, 3, "string", "nil")
808 if not self.db or not self.db.raw then
809 AceDB:error("Cannot call \"SetProfile\" before \"RegisterDB\" has been called and before \"ADDON_LOADED\" has been fired.")
810 end
811 local db = self.db
812 local copy = false
813 local lowerName = string.lower(name)
814 local lowerCopyFrom = copyFrom and string.lower(copyFrom)
815 if string.sub(lowerName, 1, 5) == "char/" or string.sub(lowerName, 1, 6) == "realm/" or string.sub(lowerName, 1, 6) == "class/" then
816 if string.sub(lowerName, 1, 5) == "char/" then
817 name, copyFrom = "char", name
818 else
819 name, copyFrom = string.sub(lowerName, 1, 5), name
820 end
821 lowerName = string.lower(name)
822 lowerCopyFrom = string.lower(copyFrom)
823 end
824 if copyFrom then
825 if string.sub(lowerCopyFrom, 1, 5) == "char/" then
826 AceDB:assert(lowerName == "char", "If argument #3 starts with `char/', argument #2 must be `char'")
827 elseif string.sub(lowerCopyFrom, 1, 6) == "realm/" then
828 AceDB:assert(lowerName == "realm", "If argument #3 starts with `realm/', argument #2 must be `realm'")
829 elseif string.sub(lowerCopyFrom, 1, 6) == "class/" then
830 AceDB:assert(lowerName == "class", "If argument #3 starts with `class/', argument #2 must be `class'")
831 else
832 AceDB:assert(lowerName ~= "char" and lowerName ~= "realm" and lowerName ~= "class", "If argument #3 does not start with a special prefix, that prefix cannot be copied to.")
833 end
834 if not db.raw.profiles or not db.raw.profiles[copyFrom] then
835 AceDB:error("Cannot copy profile %q, it does not exist.", copyFrom)
836 elseif (string.sub(lowerName, 1, 5) == "char/" and string.sub(lowerName, 6) == string.lower(charID)) or (string.sub(lowerName, 1, 6) == "realm/" and string.sub(lowerName, 7) == string.lower(realmID)) or (string.sub(lowerName, 1, 6) == "class/" and string.sub(lowerName, 7) == string.lower(classID)) then
837 AceDB:error("Cannot copy profile %q, it is currently in use.", name)
838 end
839 end
840 local oldName = db.raw.currentProfile[charID]
841 if string.lower(oldName) == string.lower(name) then
842 return
843 end
844 local current = self.class
845 while current and current ~= AceOO.Class do
846 if current.mixins then
847 for mixin in pairs(current.mixins) do
848 if type(mixin.OnEmbedProfileDisable) == "function" then
849 mixin:OnEmbedProfileDisable(self)
850 end
851 end
852 end
853 current = current.super
854 end
855 if type(self.OnProfileDisable) == "function" then
856 self:OnProfileDisable()
857 end
858 local oldProfileData = db.profile
859 local realName = name
860 if lowerName == "char" then
861 realName = name .. "/" .. charID
862 elseif lowerName == "realm/" then
863 realName = name .. "/" .. realmID
864 elseif lowerName == "class/" then
865 realName = name .. "/" .. classID
866 end
867 local active = self:IsActive()
868 db.raw.currentProfile[charID] = name
869 rawset(db, 'profile', nil)
870 if copyFrom then
871 for k,v in pairs(db.profile) do
872 db.profile[k] = nil
873 end
874 copyTable(db.profile, db.raw.profiles[copyFrom])
875 inheritDefaults(db.profile, db.defaults and db.defaults.profile)
876 end
877 local current = self.class
878 while current and current ~= AceOO.Class do
879 if current.mixins then
880 for mixin in pairs(current.mixins) do
881 if type(mixin.OnEmbedProfileEnable) == "function" then
882 mixin:OnEmbedProfileEnable(self, oldName, oldProfileData, copyFrom)
883 end
884 end
885 end
886 current = current.super
887 end
888 if type(self.OnProfileEnable) == "function" then
889 self:OnProfileEnable(oldName, oldProfileData, copyFrom)
890 end
891 if cleanDefaults(oldProfileData, db.defaults and db.defaults.profile) then
892 db.raw.profiles[oldName] = nil
893 if not next(db.raw.profiles) then
894 db.raw.profiles = nil
895 end
896 end
897 local newactive = self:IsActive()
898 if active ~= newactive then
899 if AceOO.inherits(self, "AceAddon-2.0") then
900 local AceAddon = AceLibrary("AceAddon-2.0")
901 if not AceAddon.addonsStarted[self] then
902 return
903 end
904 end
905 if newactive then
906 local current = self.class
907 while current and current ~= AceOO.Class do
908 if current.mixins then
909 for mixin in pairs(current.mixins) do
910 if type(mixin.OnEmbedEnable) == "function" then
911 mixin:OnEmbedEnable(self)
912 end
913 end
914 end
915 current = current.super
916 end
917 if type(self.OnEnable) == "function" then
918 self:OnEnable()
919 end
920 else
921 local current = self.class
922 while current and current ~= AceOO.Class do
923 if current.mixins then
924 for mixin in pairs(current.mixins) do
925 if type(mixin.OnEmbedDisable) == "function" then
926 mixin:OnEmbedDisable(self)
927 end
928 end
929 end
930 current = current.super
931 end
932 if type(self.OnDisable) == "function" then
933 self:OnDisable()
934 end
935 end
936 end
937 if self['acedb-profile-list'] then
938 if not self['acedb-profile-list'][name] then
939 self['acedb-profile-list'][name] = name
940 end
941 end
942 if Dewdrop then
943 Dewdrop:Refresh(1)
944 Dewdrop:Refresh(2)
945 Dewdrop:Refresh(3)
946 Dewdrop:Refresh(4)
947 Dewdrop:Refresh(5)
948 end
949 end
950  
951 function AceDB:IsActive()
952 return not self.db or not self.db.raw or not self.db.raw.disabled or not self.db.raw.disabled[self.db.raw.currentProfile[charID]]
953 end
954  
955 function AceDB:ToggleActive(state)
956 AceDB:argCheck(state, 2, "boolean", "nil")
957 if not self.db or not self.db.raw then
958 AceDB:error("Cannot call \"ToggleActive\" before \"RegisterDB\" has been called and before \"ADDON_LOADED\" has been fired.")
959 end
960 local db = self.db
961 if not db.raw.disabled then
962 db.raw.disabled = setmetatable({}, caseInsensitive_mt)
963 end
964 local profile = db.raw.currentProfile[charID]
965 local disable
966 if state == nil then
967 disable = not db.raw.disabled[profile]
968 else
969 disable = not state
970 if disable == db.raw.disabled[profile] then
971 return
972 end
973 end
974 db.raw.disabled[profile] = disable or nil
975 if AceOO.inherits(self, "AceAddon-2.0") then
976 local AceAddon = AceLibrary("AceAddon-2.0")
977 if not AceAddon.addonsStarted[self] then
978 return
979 end
980 end
981 if not disable then
982 local current = self.class
983 while current and current ~= AceOO.Class do
984 if current.mixins then
985 for mixin in pairs(current.mixins) do
986 if type(mixin.OnEmbedEnable) == "function" then
987 mixin:OnEmbedEnable(self)
988 end
989 end
990 end
991 current = current.super
992 end
993 if type(self.OnEnable) == "function" then
994 self:OnEnable()
995 end
996 else
997 local current = self.class
998 while current and current ~= AceOO.Class do
999 if current.mixins then
1000 for mixin in pairs(current.mixins) do
1001 if type(mixin.OnEmbedDisable) == "function" then
1002 mixin:OnEmbedDisable(self)
1003 end
1004 end
1005 end
1006 current = current.super
1007 end
1008 if type(self.OnDisable) == "function" then
1009 self:OnDisable()
1010 end
1011 end
1012 return not disable
1013 end
1014  
1015 function AceDB:embed(target)
1016 self.super.embed(self, target)
1017 if not AceEvent then
1018 AceDB:error(MAJOR_VERSION .. " requires AceEvent-2.0")
1019 end
1020 end
1021  
1022 function AceDB:ADDON_LOADED(name)
1023 AceDB.addonsLoaded[name] = true
1024 for addon, addonName in pairs(AceDB.addonsToBeInitialized) do
1025 if name == addonName then
1026 AceDB.InitializeDB(addon, name)
1027 AceDB.addonsToBeInitialized[addon] = nil
1028 end
1029 end
1030 end
1031  
1032 function AceDB:PLAYER_LOGOUT()
1033 for addon in pairs(AceDB.registry) do
1034 local db = addon.db
1035 if db then
1036 setmetatable(db, nil)
1037 CrawlForSerialization(db.raw)
1038 if type(_G[db.charName]) == "table" then
1039 CrawlForSerialization(_G[db.charName])
1040 end
1041 if db.char and cleanDefaults(db.char, db.defaults and db.defaults.char) then
1042 if db.charName and _G[db.charName] and _G[db.charName].global == db.char then
1043 _G[db.charName].global = nil
1044 if not next(_G[db.charName]) then
1045 _G[db.charName] = nil
1046 end
1047 else
1048 if db.raw.chars then
1049 db.raw.chars[charID] = nil
1050 if not next(db.raw.chars) then
1051 db.raw.chars = nil
1052 end
1053 end
1054 end
1055 end
1056 if db.realm and cleanDefaults(db.realm, db.defaults and db.defaults.realm) then
1057 if db.raw.realms then
1058 db.raw.realms[realmID] = nil
1059 if not next(db.raw.realms) then
1060 db.raw.realms = nil
1061 end
1062 end
1063 end
1064 if db.class and cleanDefaults(db.class, db.defaults and db.defaults.class) then
1065 if db.raw.classes then
1066 db.raw.classes[classID] = nil
1067 if not next(db.raw.classes) then
1068 db.raw.classes = nil
1069 end
1070 end
1071 end
1072 if db.account and cleanDefaults(db.account, db.defaults and db.defaults.account) then
1073 db.raw.account = nil
1074 end
1075 if db.profile and cleanDefaults(db.profile, db.defaults and db.defaults.profile) then
1076 if db.raw.profiles then
1077 db.raw.profiles[db.raw.currentProfile and db.raw.currentProfile[charID] or "Default"] = nil
1078 if not next(db.raw.profiles) then
1079 db.raw.profiles = nil
1080 end
1081 end
1082 end
1083 if db.namespaces and db.raw.namespaces then
1084 for name,v in pairs(db.namespaces) do
1085 if db.raw.namespaces[name] then
1086 setmetatable(v, nil)
1087 if v.char and cleanDefaults(v.char, v.defaults and v.defaults.char) then
1088 if db.charName and _G[db.charName] and _G[db.charName].namespaces and _G[db.charName].namespaces[name] == v then
1089 _G[db.charName].namespaces[name] = nil
1090 if not next(_G[db.charName].namespaces) then
1091 _G[db.charName].namespaces = nil
1092 if not next(_G[db.charName]) then
1093 _G[db.charName] = nil
1094 end
1095 end
1096 else
1097 if db.raw.namespaces[name].chars then
1098 db.raw.namespaces[name].chars[charID] = nil
1099 if not next(db.raw.namespaces[name].chars) then
1100 db.raw.namespaces[name].chars = nil
1101 end
1102 end
1103 end
1104 end
1105 if v.realm and cleanDefaults(v.realm, v.defaults and v.defaults.realm) then
1106 if db.raw.namespaces[name].realms then
1107 db.raw.namespaces[name].realms[realmID] = nil
1108 if not next(db.raw.namespaces[name].realms) then
1109 db.raw.namespaces[name].realms = nil
1110 end
1111 end
1112 end
1113 if v.class and cleanDefaults(v.class, v.defaults and v.defaults.class) then
1114 if db.raw.namespaces[name].classes then
1115 db.raw.namespaces[name].classes[classID] = nil
1116 if not next(db.raw.namespaces[name].classes) then
1117 db.raw.namespaces[name].classes = nil
1118 end
1119 end
1120 end
1121 if v.account and cleanDefaults(v.account, v.defaults and v.defaults.account) then
1122 db.raw.namespaces[name].account = nil
1123 end
1124 if v.profile and cleanDefaults(v.profile, v.defaults and v.defaults.profile) then
1125 if db.raw.namespaces[name].profiles then
1126 db.raw.namespaces[name].profiles[db.raw.currentProfile[charID] or "Default"] = nil
1127 if not next(db.raw.namespaces[name].profiles) then
1128 db.raw.namespaces[name].profiles = nil
1129 end
1130 end
1131 end
1132 if not next(db.raw.namespaces[name]) then
1133 db.raw.namespaces[name] = nil
1134 end
1135 end
1136 end
1137 if not next(db.raw.namespaces) then
1138 db.raw.namespaces = nil
1139 end
1140 end
1141 if db.raw.disabled and not next(db.raw.disabled) then
1142 db.raw.disabled = nil
1143 end
1144 if db.raw.currentProfile then
1145 for k,v in pairs(db.raw.currentProfile) do
1146 if string.lower(v) == "default" then
1147 db.raw.currentProfile[k] = nil
1148 end
1149 end
1150 if not next(db.raw.currentProfile) then
1151 db.raw.currentProfile = nil
1152 end
1153 end
1154 if _G[db.name] and not next(_G[db.name]) then
1155 _G[db.name] = nil
1156 end
1157 end
1158 end
1159 end
1160  
1161 function AceDB:AcquireDBNamespace(name)
1162 AceDB:argCheck(name, 2, "string")
1163 local db = self.db
1164 if not db then
1165 AceDB:error("Cannot call `AcquireDBNamespace' before `RegisterDB' has been called.", 2)
1166 end
1167 if not db.namespaces then
1168 rawset(db, 'namespaces', {})
1169 end
1170 if not db.namespaces[name] then
1171 local namespace = {}
1172 db.namespaces[name] = namespace
1173 namespace.db = db
1174 namespace.name = name
1175 setmetatable(namespace, namespace_mt)
1176 end
1177 return db.namespaces[name]
1178 end
1179  
1180 local options
1181 function AceDB:GetAceOptionsDataTable(target)
1182 if not target['acedb-profile-list'] then
1183 target['acedb-profile-list'] = setmetatable({}, caseInsensitive_mt)
1184 local t = target['acedb-profile-list']
1185 for k,v in pairs(t) do
1186 t[k] = nil
1187 end
1188 t.char = 'Character: ' .. charID
1189 t.realm = 'Realm: ' .. realmID
1190 t.class = 'Class: ' .. classID
1191 t.Default = "Default"
1192 if target.db and target.db.raw then
1193 local db = target.db
1194 if db.raw.profiles then
1195 for k in pairs(db.raw.profiles) do
1196 if not string.find(k, '^char/') and not string.find(k, '^realm/') and not string.find(k, '^class/') then
1197 t[k] = k
1198 end
1199 end
1200 end
1201 end
1202 end
1203 if not target['acedb-profile-copylist'] then
1204 target['acedb-profile-copylist'] = setmetatable({}, caseInsensitive_mt)
1205 if target.db and target.db.raw then
1206 local t = target['acedb-profile-copylist']
1207 local db = target.db
1208  
1209 if db.raw.profiles then
1210 for k in pairs(db.raw.profiles) do
1211 if string.find(k, '^char/') then
1212 local name = string.sub(k, 6)
1213 if name ~= charID then
1214 t[k] = 'Character: ' .. name
1215 end
1216 elseif string.find(k, '^realm/') then
1217 local name = string.sub(k, 7)
1218 if name ~= realmID then
1219 t[k] = 'Realm: ' .. name
1220 end
1221 elseif string.find(k, '^class/') then
1222 local name = string.sub(k, 7)
1223 if name ~= classID then
1224 t[k] = 'Class: ' .. name
1225 end
1226 end
1227 end
1228 end
1229 end
1230 end
1231 if not options then
1232 options = {
1233 standby = {
1234 cmdName = STATE,
1235 guiName = ACTIVE,
1236 name = ACTIVE,
1237 desc = TOGGLE_ACTIVE,
1238 type = "toggle",
1239 get = "IsActive",
1240 set = "ToggleActive",
1241 map = MAP_ACTIVESUSPENDED,
1242 order = -3,
1243 },
1244 profile = {
1245 type = 'group',
1246 name = PROFILE,
1247 desc = SET_PROFILE,
1248 order = -3.5,
1249 get = "GetProfile",
1250 args = {
1251 choose = {
1252 guiName = "Choose",
1253 cmdName = PROFILE,
1254 desc = "Choose a profile",
1255 type = 'text',
1256 get = "GetProfile",
1257 set = "SetProfile",
1258 validate = target['acedb-profile-list']
1259 },
1260 copy = {
1261 guiName = "Copy from",
1262 cmdName = PROFILE,
1263 desc = "Copy settings from another profile",
1264 type = 'text',
1265 get = "GetProfile",
1266 set = "SetProfile",
1267 validate = target['acedb-profile-copylist'],
1268 disabled = function()
1269 return not next(target['acedb-profile-copylist'])
1270 end,
1271 },
1272 other = {
1273 guiName = "Other",
1274 cmdName = PROFILE,
1275 desc = "Choose another profile",
1276 usage = "<profile name>",
1277 type = 'text',
1278 get = "GetProfile",
1279 set = "SetProfile",
1280 }
1281 }
1282 },
1283 }
1284 end
1285 return options
1286 end
1287  
1288 local function activate(self, oldLib, oldDeactivate)
1289 AceDB = self
1290 AceEvent = AceLibrary:HasInstance("AceEvent-2.0") and AceLibrary("AceEvent-2.0")
1291  
1292 self.super.activate(self, oldLib, oldDeactivate)
1293  
1294 for t in pairs(self.embedList) do
1295 if t.db then
1296 rawset(t.db, 'char', nil)
1297 rawset(t.db, 'realm', nil)
1298 rawset(t.db, 'class', nil)
1299 rawset(t.db, 'account', nil)
1300 rawset(t.db, 'profile', nil)
1301 setmetatable(t.db, db_mt)
1302 end
1303 end
1304  
1305 if oldLib then
1306 self.addonsToBeInitialized = oldLib.addonsToBeInitialized
1307 self.addonsLoaded = oldLib.addonsLoaded
1308 self.registry = oldLib.registry
1309 end
1310 if not self.addonsToBeInitialized then
1311 self.addonsToBeInitialized = {}
1312 end
1313 if not self.addonsLoaded then
1314 self.addonsLoaded = {}
1315 end
1316 if not self.registry then
1317 self.registry = {}
1318 end
1319  
1320 if oldLib then
1321 oldDeactivate(oldLib)
1322 end
1323 end
1324  
1325 local function external(self, major, instance)
1326 if major == "AceEvent-2.0" then
1327 AceEvent = instance
1328  
1329 AceEvent:embed(self)
1330  
1331 self:RegisterEvent("ADDON_LOADED")
1332 self:RegisterEvent("PLAYER_LOGOUT")
1333 elseif major == "Dewdrop-2.0" then
1334 Dewdrop = instance
1335 end
1336 end
1337  
1338 AceLibrary:Register(AceDB, MAJOR_VERSION, MINOR_VERSION, activate, nil, external)
1339 AceDB = AceLibrary(MAJOR_VERSION)