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