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: 8328 $
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: 8328 $"
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 true do
846 if current == AceOO.Class then
847 break
848 end
849 if current.mixins then
850 for mixin in pairs(current.mixins) do
851 if type(mixin.OnEmbedProfileDisable) == "function" then
852 mixin:OnEmbedProfileDisable(self)
853 end
854 end
855 end
856 current = current.super
857 end
858 if type(self.OnProfileDisable) == "function" then
859 self:OnProfileDisable()
860 end
861 local oldProfileData = db.profile
862 local realName = name
863 if lowerName == "char" then
864 realName = name .. "/" .. charID
865 elseif lowerName == "realm/" then
866 realName = name .. "/" .. realmID
867 elseif lowerName == "class/" then
868 realName = name .. "/" .. classID
869 end
870 local active = self:IsActive()
871 db.raw.currentProfile[charID] = name
872 rawset(db, 'profile', nil)
873 if copyFrom then
874 for k,v in pairs(db.profile) do
875 db.profile[k] = nil
876 end
877 copyTable(db.profile, db.raw.profiles[copyFrom])
878 inheritDefaults(db.profile, db.defaults and db.defaults.profile)
879 end
880 local current = self.class
881 while true do
882 if current == AceOO.Class then
883 break
884 end
885 if current.mixins then
886 for mixin in pairs(current.mixins) do
887 if type(mixin.OnEmbedProfileEnable) == "function" then
888 mixin:OnEmbedProfileEnable(self, oldName, oldProfileData, copyFrom)
889 end
890 end
891 end
892 current = current.super
893 end
894 if type(self.OnProfileEnable) == "function" then
895 self:OnProfileEnable(oldName, oldProfileData, copyFrom)
896 end
897 if cleanDefaults(oldProfileData, db.defaults and db.defaults.profile) then
898 db.raw.profiles[oldName] = nil
899 if not next(db.raw.profiles) then
900 db.raw.profiles = nil
901 end
902 end
903 local newactive = self:IsActive()
904 if active ~= newactive then
905 if AceOO.inherits(self, "AceAddon-2.0") then
906 local AceAddon = AceLibrary("AceAddon-2.0")
907 if not AceAddon.addonsStarted[self] then
908 return
909 end
910 end
911 if newactive then
912 local current = self.class
913 while true do
914 if current == AceOO.Class then
915 break
916 end
917 if current.mixins then
918 for mixin in pairs(current.mixins) do
919 if type(mixin.OnEmbedEnable) == "function" then
920 mixin:OnEmbedEnable(self)
921 end
922 end
923 end
924 current = current.super
925 end
926 if type(self.OnEnable) == "function" then
927 self:OnEnable()
928 end
929 else
930 local current = self.class
931 while true do
932 if current == AceOO.Class then
933 break
934 end
935 if current.mixins then
936 for mixin in pairs(current.mixins) do
937 if type(mixin.OnEmbedDisable) == "function" then
938 mixin:OnEmbedDisable(self)
939 end
940 end
941 end
942 current = current.super
943 end
944 if type(self.OnDisable) == "function" then
945 self:OnDisable()
946 end
947 end
948 end
949 if self['acedb-profile-list'] then
950 if not self['acedb-profile-list'][name] then
951 self['acedb-profile-list'][name] = name
952 end
953 end
954 if Dewdrop then
955 Dewdrop:Refresh(1)
956 Dewdrop:Refresh(2)
957 Dewdrop:Refresh(3)
958 Dewdrop:Refresh(4)
959 Dewdrop:Refresh(5)
960 end
961 end
962  
963 function AceDB:IsActive()
964 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]]
965 end
966  
967 function AceDB:ToggleActive(state)
968 AceDB:argCheck(state, 2, "boolean", "nil")
969 if not self.db or not self.db.raw then
970 AceDB:error("Cannot call \"ToggleActive\" before \"RegisterDB\" has been called and before \"ADDON_LOADED\" has been fired.")
971 end
972 local db = self.db
973 if not db.raw.disabled then
974 db.raw.disabled = setmetatable({}, caseInsensitive_mt)
975 end
976 local profile = db.raw.currentProfile[charID]
977 local disable
978 if state == nil then
979 disable = not db.raw.disabled[profile]
980 else
981 disable = not state
982 if disable == db.raw.disabled[profile] then
983 return
984 end
985 end
986 db.raw.disabled[profile] = disable or nil
987 if AceOO.inherits(self, "AceAddon-2.0") then
988 local AceAddon = AceLibrary("AceAddon-2.0")
989 if not AceAddon.addonsStarted[self] then
990 return
991 end
992 end
993 if not disable then
994 local current = self.class
995 while true do
996 if current == AceOO.Class then
997 break
998 end
999 if current.mixins then
1000 for mixin in pairs(current.mixins) do
1001 if type(mixin.OnEmbedEnable) == "function" then
1002 mixin:OnEmbedEnable(self)
1003 end
1004 end
1005 end
1006 current = current.super
1007 end
1008 if type(self.OnEnable) == "function" then
1009 self:OnEnable()
1010 end
1011 else
1012 local current = self.class
1013 while true do
1014 if current == AceOO.Class then
1015 break
1016 end
1017 if current.mixins then
1018 for mixin in pairs(current.mixins) do
1019 if type(mixin.OnEmbedDisable) == "function" then
1020 mixin:OnEmbedDisable(self)
1021 end
1022 end
1023 end
1024 current = current.super
1025 end
1026 if type(self.OnDisable) == "function" then
1027 self:OnDisable()
1028 end
1029 end
1030 return not disable
1031 end
1032  
1033 function AceDB:embed(target)
1034 self.super.embed(self, target)
1035 if not AceEvent then
1036 AceDB:error(MAJOR_VERSION .. " requires AceEvent-2.0")
1037 end
1038 end
1039  
1040 function AceDB:ADDON_LOADED(name)
1041 AceDB.addonsLoaded[name] = true
1042 for addon, addonName in pairs(AceDB.addonsToBeInitialized) do
1043 if name == addonName then
1044 AceDB.InitializeDB(addon, name)
1045 AceDB.addonsToBeInitialized[addon] = nil
1046 end
1047 end
1048 end
1049  
1050 function AceDB:PLAYER_LOGOUT()
1051 for addon in pairs(AceDB.registry) do
1052 local db = addon.db
1053 if db then
1054 setmetatable(db, nil)
1055 CrawlForSerialization(db.raw)
1056 if type(_G[db.charName]) == "table" then
1057 CrawlForSerialization(_G[db.charName])
1058 end
1059 if db.char and cleanDefaults(db.char, db.defaults and db.defaults.char) then
1060 if db.charName and _G[db.charName] and _G[db.charName].global == db.char then
1061 _G[db.charName].global = nil
1062 if not next(_G[db.charName]) then
1063 _G[db.charName] = nil
1064 end
1065 else
1066 if db.raw.chars then
1067 db.raw.chars[charID] = nil
1068 if not next(db.raw.chars) then
1069 db.raw.chars = nil
1070 end
1071 end
1072 end
1073 end
1074 if db.realm and cleanDefaults(db.realm, db.defaults and db.defaults.realm) then
1075 if db.raw.realms then
1076 db.raw.realms[realmID] = nil
1077 if not next(db.raw.realms) then
1078 db.raw.realms = nil
1079 end
1080 end
1081 end
1082 if db.class and cleanDefaults(db.class, db.defaults and db.defaults.class) then
1083 if db.raw.classes then
1084 db.raw.classes[classID] = nil
1085 if not next(db.raw.classes) then
1086 db.raw.classes = nil
1087 end
1088 end
1089 end
1090 if db.account and cleanDefaults(db.account, db.defaults and db.defaults.account) then
1091 db.raw.account = nil
1092 end
1093 if db.profile and cleanDefaults(db.profile, db.defaults and db.defaults.profile) then
1094 if db.raw.profiles then
1095 db.raw.profiles[db.raw.currentProfile and db.raw.currentProfile[charID] or "Default"] = nil
1096 if not next(db.raw.profiles) then
1097 db.raw.profiles = nil
1098 end
1099 end
1100 end
1101 if db.namespaces and db.raw.namespaces then
1102 for name,v in pairs(db.namespaces) do
1103 if db.raw.namespaces[name] then
1104 setmetatable(v, nil)
1105 if v.char and cleanDefaults(v.char, v.defaults and v.defaults.char) then
1106 if db.charName and _G[db.charName] and _G[db.charName].namespaces and _G[db.charName].namespaces[name] == v then
1107 _G[db.charName].namespaces[name] = nil
1108 if not next(_G[db.charName].namespaces) then
1109 _G[db.charName].namespaces = nil
1110 if not next(_G[db.charName]) then
1111 _G[db.charName] = nil
1112 end
1113 end
1114 else
1115 if db.raw.namespaces[name].chars then
1116 db.raw.namespaces[name].chars[charID] = nil
1117 if not next(db.raw.namespaces[name].chars) then
1118 db.raw.namespaces[name].chars = nil
1119 end
1120 end
1121 end
1122 end
1123 if v.realm and cleanDefaults(v.realm, v.defaults and v.defaults.realm) then
1124 if db.raw.namespaces[name].realms then
1125 db.raw.namespaces[name].realms[realmID] = nil
1126 if not next(db.raw.namespaces[name].realms) then
1127 db.raw.namespaces[name].realms = nil
1128 end
1129 end
1130 end
1131 if v.class and cleanDefaults(v.class, v.defaults and v.defaults.class) then
1132 if db.raw.namespaces[name].classes then
1133 db.raw.namespaces[name].classes[classID] = nil
1134 if not next(db.raw.namespaces[name].classes) then
1135 db.raw.namespaces[name].classes = nil
1136 end
1137 end
1138 end
1139 if v.account and cleanDefaults(v.account, v.defaults and v.defaults.account) then
1140 db.raw.namespaces[name].account = nil
1141 end
1142 if v.profile and cleanDefaults(v.profile, v.defaults and v.defaults.profile) then
1143 if db.raw.namespaces[name].profiles then
1144 db.raw.namespaces[name].profiles[db.raw.currentProfile[charID] or "Default"] = nil
1145 if not next(db.raw.namespaces[name].profiles) then
1146 db.raw.namespaces[name].profiles = nil
1147 end
1148 end
1149 end
1150 if not next(db.raw.namespaces[name]) then
1151 db.raw.namespaces[name] = nil
1152 end
1153 end
1154 end
1155 if not next(db.raw.namespaces) then
1156 db.raw.namespaces = nil
1157 end
1158 end
1159 if db.raw.disabled and not next(db.raw.disabled) then
1160 db.raw.disabled = nil
1161 end
1162 if db.raw.currentProfile then
1163 for k,v in pairs(db.raw.currentProfile) do
1164 if string.lower(v) == "default" then
1165 db.raw.currentProfile[k] = nil
1166 end
1167 end
1168 if not next(db.raw.currentProfile) then
1169 db.raw.currentProfile = nil
1170 end
1171 end
1172 if _G[db.name] and not next(_G[db.name]) then
1173 _G[db.name] = nil
1174 end
1175 end
1176 end
1177 end
1178  
1179 function AceDB:AcquireDBNamespace(name)
1180 AceDB:argCheck(name, 2, "string")
1181 local db = self.db
1182 if not db then
1183 AceDB:error("Cannot call `AcquireDBNamespace' before `RegisterDB' has been called.", 2)
1184 end
1185 if not db.namespaces then
1186 rawset(db, 'namespaces', {})
1187 end
1188 if not db.namespaces[name] then
1189 local namespace = {}
1190 db.namespaces[name] = namespace
1191 namespace.db = db
1192 namespace.name = name
1193 setmetatable(namespace, namespace_mt)
1194 end
1195 return db.namespaces[name]
1196 end
1197  
1198 local options
1199 function AceDB:GetAceOptionsDataTable(target)
1200 if not target['acedb-profile-list'] then
1201 target['acedb-profile-list'] = setmetatable({}, caseInsensitive_mt)
1202 local t = target['acedb-profile-list']
1203 for k,v in pairs(t) do
1204 t[k] = nil
1205 end
1206 t.char = 'Character: ' .. charID
1207 t.realm = 'Realm: ' .. realmID
1208 t.class = 'Class: ' .. classID
1209 t.Default = "Default"
1210 if target.db and target.db.raw then
1211 local db = target.db
1212 if db.raw.profiles then
1213 for k in pairs(db.raw.profiles) do
1214 if not string.find(k, '^char/') and not string.find(k, '^realm/') and not string.find(k, '^class/') then
1215 t[k] = k
1216 end
1217 end
1218 end
1219 end
1220 end
1221 if not target['acedb-profile-copylist'] then
1222 target['acedb-profile-copylist'] = setmetatable({}, caseInsensitive_mt)
1223 if target.db and target.db.raw then
1224 local t = target['acedb-profile-copylist']
1225 local db = target.db
1226  
1227 if db.raw.profiles then
1228 for k in pairs(db.raw.profiles) do
1229 if string.find(k, '^char/') then
1230 local name = string.sub(k, 6)
1231 if name ~= charID then
1232 t[k] = 'Character: ' .. name
1233 end
1234 elseif string.find(k, '^realm/') then
1235 local name = string.sub(k, 7)
1236 if name ~= realmID then
1237 t[k] = 'Realm: ' .. name
1238 end
1239 elseif string.find(k, '^class/') then
1240 local name = string.sub(k, 7)
1241 if name ~= classID then
1242 t[k] = 'Class: ' .. name
1243 end
1244 end
1245 end
1246 end
1247 end
1248 end
1249 if not options then
1250 options = {
1251 standby = {
1252 cmdName = STATE,
1253 guiName = ACTIVE,
1254 name = ACTIVE,
1255 desc = TOGGLE_ACTIVE,
1256 type = "toggle",
1257 get = "IsActive",
1258 set = "ToggleActive",
1259 map = MAP_ACTIVESUSPENDED,
1260 order = -3,
1261 },
1262 profile = {
1263 type = 'group',
1264 name = PROFILE,
1265 desc = SET_PROFILE,
1266 order = -3.5,
1267 get = "GetProfile",
1268 args = {
1269 choose = {
1270 guiName = "Choose",
1271 cmdName = PROFILE,
1272 desc = "Choose a profile",
1273 type = 'text',
1274 get = "GetProfile",
1275 set = "SetProfile",
1276 validate = target['acedb-profile-list']
1277 },
1278 copy = {
1279 guiName = "Copy from",
1280 cmdName = PROFILE,
1281 desc = "Copy settings from another profile",
1282 type = 'text',
1283 get = "GetProfile",
1284 set = "SetProfile",
1285 validate = target['acedb-profile-copylist'],
1286 disabled = function()
1287 return not next(target['acedb-profile-copylist'])
1288 end,
1289 },
1290 other = {
1291 guiName = "Other",
1292 cmdName = PROFILE,
1293 desc = "Choose another profile",
1294 usage = "<profile name>",
1295 type = 'text',
1296 get = "GetProfile",
1297 set = "SetProfile",
1298 }
1299 }
1300 },
1301 }
1302 end
1303 return options
1304 end
1305  
1306 local function activate(self, oldLib, oldDeactivate)
1307 AceDB = self
1308 AceEvent = AceLibrary:HasInstance("AceEvent-2.0") and AceLibrary("AceEvent-2.0")
1309  
1310 self.super.activate(self, oldLib, oldDeactivate)
1311  
1312 for t in pairs(self.embedList) do
1313 if t.db then
1314 rawset(t.db, 'char', nil)
1315 rawset(t.db, 'realm', nil)
1316 rawset(t.db, 'class', nil)
1317 rawset(t.db, 'account', nil)
1318 rawset(t.db, 'profile', nil)
1319 setmetatable(t.db, db_mt)
1320 end
1321 end
1322  
1323 if oldLib then
1324 self.addonsToBeInitialized = oldLib.addonsToBeInitialized
1325 self.addonsLoaded = oldLib.addonsLoaded
1326 self.registry = oldLib.registry
1327 end
1328 if not self.addonsToBeInitialized then
1329 self.addonsToBeInitialized = {}
1330 end
1331 if not self.addonsLoaded then
1332 self.addonsLoaded = {}
1333 end
1334 if not self.registry then
1335 self.registry = {}
1336 end
1337  
1338 if oldLib then
1339 oldDeactivate(oldLib)
1340 end
1341 end
1342  
1343 local function external(self, major, instance)
1344 if major == "AceEvent-2.0" then
1345 AceEvent = instance
1346  
1347 AceEvent:embed(self)
1348  
1349 self:RegisterEvent("ADDON_LOADED")
1350 self:RegisterEvent("PLAYER_LOGOUT")
1351 elseif major == "Dewdrop-2.0" then
1352 Dewdrop = instance
1353 end
1354 end
1355  
1356 AceLibrary:Register(AceDB, MAJOR_VERSION, MINOR_VERSION, activate, nil, external)
1357 AceDB = AceLibrary(MAJOR_VERSION)