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: 7846 $
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: 7846 $"
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 self.InitializeDB(target, name)
553 end
554  
555 function AceDB:RegisterDB(name, charName)
556 AceDB:argCheck(name, 2, "string")
557 AceDB:argCheck(charName, 3, "string", "nil")
558 if self.db then
559 AceDB:error("Cannot call \"RegisterDB\" if self.db is set.")
560 end
561 local stack = debugstack()
562 local addonName = string.gsub(stack, ".-\n.-\\AddOns\\(.-)\\.*", "%1")
563 self.db = {
564 name = name,
565 charName = charName
566 }
567 if AceDB.addonsLoaded[addonName] then
568 AceDB.InitializeDB(self, addonName)
569 else
570 AceDB.addonsToBeInitialized[self] = addonName
571 end
572 AceDB.registry[self] = true
573 end
574  
575 function AceDB:RegisterDefaults(kind, defaults, a3)
576 local name
577 if a3 then
578 name, kind, defaults = kind, defaults, a3
579 AceDB:argCheck(name, 2, "string")
580 AceDB:argCheck(kind, 3, "string")
581 AceDB:argCheck(defaults, 4, "table")
582 if kind ~= "char" and kind ~= "class" and kind ~= "profile" and kind ~= "account" and kind ~= "realm" then
583 AceDB:error("Bad argument #3 to `RegisterDefaults' (\"char\", \"class\", \"profile\", \"account\", or \"realm\" expected, got %q)", kind)
584 end
585 else
586 AceDB:argCheck(kind, 2, "string")
587 AceDB:argCheck(defaults, 3, "table")
588 if kind ~= "char" and kind ~= "class" and kind ~= "profile" and kind ~= "account" and kind ~= "realm" then
589 AceDB:error("Bad argument #2 to `RegisterDefaults' (\"char\", \"class\", \"profile\", \"account\", or \"realm\" expected, got %q)", kind)
590 end
591 end
592 if type(self.db) ~= "table" or type(self.db.name) ~= "string" then
593 AceDB:error("Cannot call \"RegisterDefaults\" unless \"RegisterDB\" has been previously called.")
594 end
595 local db
596 if name then
597 local namespace = self:AcquireDBNamespace(name)
598 if namespace.defaults and namespace.defaults[kind] then
599 AceDB:error("\"RegisterDefaults\" has already been called for %q::%q.", name, kind)
600 end
601 db = namespace
602 else
603 if self.db.defaults and self.db.defaults[kind] then
604 AceDB:error("\"RegisterDefaults\" has already been called for %q.", kind)
605 end
606 db = self.db
607 end
608 if not db.defaults then
609 rawset(db, 'defaults', {})
610 end
611 db.defaults[kind] = defaults
612 if rawget(db, kind) then
613 inheritDefaults(db[kind], defaults)
614 end
615 end
616  
617 function AceDB:ResetDB(kind)
618 AceDB:argCheck(kind, 2, "nil", "string")
619 if not self.db or not self.db.raw then
620 AceDB:error("Cannot call \"ResetDB\" before \"RegisterDB\" has been called and before \"ADDON_LOADED\" has been fired.")
621 end
622 local db = self.db
623 if kind == nil then
624 if db.charName then
625 _G[db.charName] = nil
626 end
627 _G[db.name] = nil
628 rawset(db, 'raw', nil)
629 AceDB.InitializeDB(self)
630 if db.namespaces then
631 for name,v in pairs(db.namespaces) do
632 rawset(v, 'account', nil)
633 rawset(v, 'char', nil)
634 rawset(v, 'class', nil)
635 rawset(v, 'profile', nil)
636 rawset(v, 'realm', nil)
637 end
638 end
639 elseif kind == "account" then
640 db.raw.account = nil
641 rawset(db, 'account', nil)
642 if db.namespaces then
643 for name,v in pairs(db.namespaces) do
644 rawset(v, 'account', nil)
645 end
646 end
647 elseif kind == "char" then
648 if db.charName then
649 _G[db.charName] = nil
650 else
651 if db.raw.chars then
652 db.raw.chars[charID] = nil
653 end
654 if db.raw.namespaces then
655 for name,v in pairs(db.raw.namespaces) do
656 if v.chars then
657 v.chars[charID] = nil
658 end
659 end
660 end
661 end
662 rawset(db, 'char', nil)
663 if db.namespaces then
664 for name,v in pairs(db.namespaces) do
665 rawset(v, 'char', nil)
666 end
667 end
668 elseif kind == "realm" then
669 if db.raw.realms then
670 db.raw.realms[realmID] = nil
671 end
672 rawset(db, 'realm', nil)
673 if db.raw.namespaces then
674 for name,v in pairs(db.raw.namespaces) do
675 if v.realms then
676 v.realms[realmID] = nil
677 end
678 end
679 end
680 if db.namespaces then
681 for name,v in pairs(db.namespaces) do
682 rawset(v, 'realm', nil)
683 end
684 end
685 elseif kind == "class" then
686 if db.raw.realms then
687 db.raw.realms[classID] = nil
688 end
689 rawset(db, 'class', nil)
690 if db.raw.namespaces then
691 for name,v in pairs(db.raw.namespaces) do
692 if v.classes then
693 v.classes[classID] = nil
694 end
695 end
696 end
697 if db.namespaces then
698 for name,v in pairs(db.namespaces) do
699 rawset(v, 'class', nil)
700 end
701 end
702 elseif kind == "profile" then
703 local id = db.raw.currentProfile and db.raw.currentProfile[charID] or "Default"
704 if id == "char" then
705 id = "char/" .. charID
706 elseif id == "class" then
707 id = "class/" .. classID
708 elseif id == "realm" then
709 id = "realm/" .. realmID
710 end
711 if db.raw.profiles then
712 db.raw.profiles[id] = nil
713 end
714 rawset(db, 'profile', nil)
715 if db.raw.namespaces then
716 for name,v in pairs(db.raw.namespaces) do
717 if v.profiles then
718 v.profiles[id] = nil
719 end
720 end
721 end
722 if db.namespaces then
723 for name,v in pairs(db.namespaces) do
724 rawset(v, 'profile', nil)
725 end
726 end
727 end
728 end
729  
730 local function cleanDefaults(t, defaults)
731 if defaults then
732 for k,v in pairs(defaults) do
733 if k == "*" then
734 if type(v) == "table" then
735 for k in pairs(t) do
736 if (defaults[k] == nil or k == "*") and type(t[k]) == "table" then
737 if cleanDefaults(t[k], v) then
738 t[k] = nil
739 end
740 end
741 end
742 else
743 for k in pairs(t) do
744 if (defaults[k] == nil or k == "*") and t[k] == v then
745 t[k] = nil
746 end
747 end
748 end
749 else
750 if type(v) == "table" then
751 if type(t[k]) == "table" then
752 if cleanDefaults(t[k], v) then
753 t[k] = nil
754 end
755 end
756 elseif t[k] == v then
757 t[k] = nil
758 end
759 end
760 end
761 end
762 return t and not next(t)
763 end
764  
765 function AceDB:GetProfile()
766 if not self.db or not self.db.raw then
767 return nil
768 end
769 if not self.db.raw.currentProfile then
770 self.db.raw.currentProfile = {}
771 end
772 if not self.db.raw.currentProfile[charID] then
773 self.db.raw.currentProfile[charID] = "Default"
774 end
775 local profile = self.db.raw.currentProfile[charID]
776 if profile == "char" then
777 return "char", "char/" .. charID
778 elseif profile == "class" then
779 return "class", "class/" .. classID
780 elseif profile == "realm" then
781 return "realm", "realm/" .. realmID
782 end
783 return profile, profile
784 end
785  
786 local function copyTable(to, from)
787 setmetatable(to, nil)
788 for k,v in pairs(from) do
789 if type(k) == "table" then
790 k = copyTable({}, k)
791 end
792 if type(v) == "table" then
793 v = copyTable({}, v)
794 end
795 to[k] = v
796 end
797 table.setn(to, table.getn(from))
798 setmetatable(to, from)
799 return to
800 end
801  
802 function AceDB:SetProfile(name, copyFrom)
803 AceDB:argCheck(name, 2, "string")
804 AceDB:argCheck(copyFrom, 3, "string", "nil")
805 if not self.db or not self.db.raw then
806 AceDB:error("Cannot call \"SetProfile\" before \"RegisterDB\" has been called and before \"ADDON_LOADED\" has been fired.")
807 end
808 local db = self.db
809 local copy = false
810 local lowerName = string.lower(name)
811 local lowerCopyFrom = copyFrom and string.lower(copyFrom)
812 if string.sub(lowerName, 1, 5) == "char/" or string.sub(lowerName, 1, 6) == "realm/" or string.sub(lowerName, 1, 6) == "class/" then
813 if string.sub(lowerName, 1, 5) == "char/" then
814 name, copyFrom = "char", name
815 else
816 name, copyFrom = string.sub(lowerName, 1, 5), name
817 end
818 lowerName = string.lower(name)
819 lowerCopyFrom = string.lower(copyFrom)
820 end
821 if copyFrom then
822 if string.sub(lowerCopyFrom, 1, 5) == "char/" then
823 AceDB:assert(lowerName == "char", "If argument #3 starts with `char/', argument #2 must be `char'")
824 elseif string.sub(lowerCopyFrom, 1, 6) == "realm/" then
825 AceDB:assert(lowerName == "realm", "If argument #3 starts with `realm/', argument #2 must be `realm'")
826 elseif string.sub(lowerCopyFrom, 1, 6) == "class/" then
827 AceDB:assert(lowerName == "class", "If argument #3 starts with `class/', argument #2 must be `class'")
828 else
829 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.")
830 end
831 if not db.raw.profiles or not db.raw.profiles[copyFrom] then
832 AceDB:error("Cannot copy profile %q, it does not exist.", copyFrom)
833 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
834 AceDB:error("Cannot copy profile %q, it is currently in use.", name)
835 end
836 end
837 local oldName = db.raw.currentProfile[charID]
838 if string.lower(oldName) == string.lower(name) then
839 return
840 end
841 local current = self.class
842 while true do
843 if current == AceOO.Class then
844 break
845 end
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 true do
879 if current == AceOO.Class then
880 break
881 end
882 if current.mixins then
883 for mixin in pairs(current.mixins) do
884 if type(mixin.OnEmbedProfileEnable) == "function" then
885 mixin:OnEmbedProfileEnable(self, oldName, oldProfileData, copyFrom)
886 end
887 end
888 end
889 current = current.super
890 end
891 if type(self.OnProfileEnable) == "function" then
892 self:OnProfileEnable(oldName, oldProfileData, copyFrom)
893 end
894 if cleanDefaults(oldProfileData, db.defaults and db.defaults.profile) then
895 db.raw.profiles[oldName] = nil
896 if not next(db.raw.profiles) then
897 db.raw.profiles = nil
898 end
899 end
900 local newactive = self:IsActive()
901 if active ~= newactive then
902 if AceOO.inherits(self, "AceAddon-2.0") then
903 local AceAddon = AceLibrary("AceAddon-2.0")
904 if not AceAddon.addonsStarted[self] then
905 return
906 end
907 end
908 if newactive then
909 local current = self.class
910 while true do
911 if current == AceOO.Class then
912 break
913 end
914 if current.mixins then
915 for mixin in pairs(current.mixins) do
916 if type(mixin.OnEmbedEnable) == "function" then
917 mixin:OnEmbedEnable(self)
918 end
919 end
920 end
921 current = current.super
922 end
923 if type(self.OnEnable) == "function" then
924 self:OnEnable()
925 end
926 else
927 local current = self.class
928 while true do
929 if current == AceOO.Class then
930 break
931 end
932 if current.mixins then
933 for mixin in pairs(current.mixins) do
934 if type(mixin.OnEmbedDisable) == "function" then
935 mixin:OnEmbedDisable(self)
936 end
937 end
938 end
939 current = current.super
940 end
941 if type(self.OnDisable) == "function" then
942 self:OnDisable()
943 end
944 end
945 end
946 if self['acedb-profile-list'] then
947 if not self['acedb-profile-list'][name] then
948 self['acedb-profile-list'][name] = name
949 end
950 end
951 if Dewdrop then
952 Dewdrop:Refresh(1)
953 Dewdrop:Refresh(2)
954 Dewdrop:Refresh(3)
955 Dewdrop:Refresh(4)
956 Dewdrop:Refresh(5)
957 end
958 end
959  
960 function AceDB:IsActive()
961 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]]
962 end
963  
964 function AceDB:ToggleActive(state)
965 AceDB:argCheck(state, 2, "boolean", "nil")
966 if not self.db or not self.db.raw then
967 AceDB:error("Cannot call \"ToggleActive\" before \"RegisterDB\" has been called and before \"ADDON_LOADED\" has been fired.")
968 end
969 local db = self.db
970 if not db.raw.disabled then
971 db.raw.disabled = setmetatable({}, caseInsensitive_mt)
972 end
973 local profile = db.raw.currentProfile[charID]
974 local disable
975 if state == nil then
976 disable = not db.raw.disabled[profile]
977 else
978 disable = not state
979 if disable == db.raw.disabled[profile] then
980 return
981 end
982 end
983 db.raw.disabled[profile] = disable or nil
984 if AceOO.inherits(self, "AceAddon-2.0") then
985 local AceAddon = AceLibrary("AceAddon-2.0")
986 if not AceAddon.addonsStarted[self] then
987 return
988 end
989 end
990 if not disable then
991 local current = self.class
992 while true do
993 if current == AceOO.Class then
994 break
995 end
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 true do
1011 if current == AceOO.Class then
1012 break
1013 end
1014 if current.mixins then
1015 for mixin in pairs(current.mixins) do
1016 if type(mixin.OnEmbedDisable) == "function" then
1017 mixin:OnEmbedDisable(self)
1018 end
1019 end
1020 end
1021 current = current.super
1022 end
1023 if type(self.OnDisable) == "function" then
1024 self:OnDisable()
1025 end
1026 end
1027 return not disable
1028 end
1029  
1030 function AceDB:embed(target)
1031 self.super.embed(self, target)
1032 if not AceEvent then
1033 AceDB:error(MAJOR_VERSION .. " requires AceEvent-2.0")
1034 end
1035 end
1036  
1037 function AceDB:ADDON_LOADED(name)
1038 AceDB.addonsLoaded[name] = true
1039 for addon, addonName in pairs(AceDB.addonsToBeInitialized) do
1040 if name == addonName then
1041 AceDB.InitializeDB(addon, name)
1042 AceDB.addonsToBeInitialized[addon] = nil
1043 end
1044 end
1045 end
1046  
1047 function AceDB:PLAYER_LOGOUT()
1048 for addon in pairs(AceDB.registry) do
1049 local db = addon.db
1050 if db then
1051 setmetatable(db, nil)
1052 CrawlForSerialization(db.raw)
1053 if type(_G[db.charName]) == "table" then
1054 CrawlForSerialization(_G[db.charName])
1055 end
1056 if db.char and cleanDefaults(db.char, db.defaults and db.defaults.char) then
1057 if db.charName and _G[db.charName] and _G[db.charName].global == db.char then
1058 _G[db.charName].global = nil
1059 if not next(_G[db.charName]) then
1060 _G[db.charName] = nil
1061 end
1062 else
1063 if db.raw.chars then
1064 db.raw.chars[charID] = nil
1065 if not next(db.raw.chars) then
1066 db.raw.chars = nil
1067 end
1068 end
1069 end
1070 end
1071 if db.realm and cleanDefaults(db.realm, db.defaults and db.defaults.realm) then
1072 if db.raw.realms then
1073 db.raw.realms[realmID] = nil
1074 if not next(db.raw.realms) then
1075 db.raw.realms = nil
1076 end
1077 end
1078 end
1079 if db.class and cleanDefaults(db.class, db.defaults and db.defaults.class) then
1080 if db.raw.classes then
1081 db.raw.classes[classID] = nil
1082 if not next(db.raw.classes) then
1083 db.raw.classes = nil
1084 end
1085 end
1086 end
1087 if db.account and cleanDefaults(db.account, db.defaults and db.defaults.account) then
1088 db.raw.account = nil
1089 end
1090 if db.profile and cleanDefaults(db.profile, db.defaults and db.defaults.profile) then
1091 if db.raw.profiles then
1092 db.raw.profiles[db.raw.currentProfile and db.raw.currentProfile[charID] or "Default"] = nil
1093 if not next(db.raw.profiles) then
1094 db.raw.profiles = nil
1095 end
1096 end
1097 end
1098 if db.namespaces and db.raw.namespaces then
1099 for name,v in pairs(db.namespaces) do
1100 if db.raw.namespaces[name] then
1101 setmetatable(v, nil)
1102 if v.char and cleanDefaults(v.char, v.defaults and v.defaults.char) then
1103 if db.charName and _G[db.charName] and _G[db.charName].namespaces and _G[db.charName].namespaces[name] == v then
1104 _G[db.charName].namespaces[name] = nil
1105 if not next(_G[db.charName].namespaces) then
1106 _G[db.charName].namespaces = nil
1107 if not next(_G[db.charName]) then
1108 _G[db.charName] = nil
1109 end
1110 end
1111 else
1112 if db.raw.namespaces[name].chars then
1113 db.raw.namespaces[name].chars[charID] = nil
1114 if not next(db.raw.namespaces[name].chars) then
1115 db.raw.namespaces[name].chars = nil
1116 end
1117 end
1118 end
1119 end
1120 if v.realm and cleanDefaults(v.realm, v.defaults and v.defaults.realm) then
1121 if db.raw.namespaces[name].realms then
1122 db.raw.namespaces[name].realms[realmID] = nil
1123 if not next(db.raw.namespaces[name].realms) then
1124 db.raw.namespaces[name].realms = nil
1125 end
1126 end
1127 end
1128 if v.class and cleanDefaults(v.class, v.defaults and v.defaults.class) then
1129 if db.raw.namespaces[name].classes then
1130 db.raw.namespaces[name].classes[classID] = nil
1131 if not next(db.raw.namespaces[name].classes) then
1132 db.raw.namespaces[name].classes = nil
1133 end
1134 end
1135 end
1136 if v.account and cleanDefaults(v.account, v.defaults and v.defaults.account) then
1137 db.raw.namespaces[name].account = nil
1138 end
1139 if v.profile and cleanDefaults(v.profile, v.defaults and v.defaults.profile) then
1140 if db.raw.namespaces[name].profiles then
1141 db.raw.namespaces[name].profiles[db.raw.currentProfile[charID] or "Default"] = nil
1142 if not next(db.raw.namespaces[name].profiles) then
1143 db.raw.namespaces[name].profiles = nil
1144 end
1145 end
1146 end
1147 if not next(db.raw.namespaces[name]) then
1148 db.raw.namespaces[name] = nil
1149 end
1150 end
1151 end
1152 if not next(db.raw.namespaces) then
1153 db.raw.namespaces = nil
1154 end
1155 end
1156 if db.raw.disabled and not next(db.raw.disabled) then
1157 db.raw.disabled = nil
1158 end
1159 if db.raw.currentProfile then
1160 for k,v in pairs(db.raw.currentProfile) do
1161 if string.lower(v) == "default" then
1162 db.raw.currentProfile[k] = nil
1163 end
1164 end
1165 if not next(db.raw.currentProfile) then
1166 db.raw.currentProfile = nil
1167 end
1168 end
1169 if _G[db.name] and not next(_G[db.name]) then
1170 _G[db.name] = nil
1171 end
1172 end
1173 end
1174 end
1175  
1176 function AceDB:AcquireDBNamespace(name)
1177 AceDB:argCheck(name, 2, "string")
1178 local db = self.db
1179 if not db then
1180 AceDB:error("Cannot call `AcquireDBNamespace' before `RegisterDB' has been called.", 2)
1181 end
1182 if not db.namespaces then
1183 rawset(db, 'namespaces', {})
1184 end
1185 if not db.namespaces[name] then
1186 local namespace = {}
1187 db.namespaces[name] = namespace
1188 namespace.db = db
1189 namespace.name = name
1190 setmetatable(namespace, namespace_mt)
1191 end
1192 return db.namespaces[name]
1193 end
1194  
1195 local options
1196 function AceDB:GetAceOptionsDataTable(target)
1197 if not target['acedb-profile-list'] then
1198 target['acedb-profile-list'] = setmetatable({}, caseInsensitive_mt)
1199 local t = target['acedb-profile-list']
1200 for k,v in pairs(t) do
1201 t[k] = nil
1202 end
1203 t.char = 'Character: ' .. charID
1204 t.realm = 'Realm: ' .. realmID
1205 t.class = 'Class: ' .. classID
1206 t.Default = "Default"
1207 if target.db and target.db.raw then
1208 local db = target.db
1209 if db.raw.profiles then
1210 for k in pairs(db.raw.profiles) do
1211 if not string.find(k, '^char/') and not string.find(k, '^realm/') and not string.find(k, '^class/') then
1212 t[k] = k
1213 end
1214 end
1215 end
1216 end
1217 end
1218 if not target['acedb-profile-copylist'] then
1219 target['acedb-profile-copylist'] = setmetatable({}, caseInsensitive_mt)
1220 if target.db and target.db.raw then
1221 local t = target['acedb-profile-copylist']
1222 local db = target.db
1223  
1224 if db.raw.profiles then
1225 for k in pairs(db.raw.profiles) do
1226 if string.find(k, '^char/') then
1227 local name = string.sub(k, 6)
1228 if name ~= charID then
1229 t[k] = 'Character: ' .. name
1230 end
1231 elseif string.find(k, '^realm/') then
1232 local name = string.sub(k, 7)
1233 if name ~= realmID then
1234 t[k] = 'Realm: ' .. name
1235 end
1236 elseif string.find(k, '^class/') then
1237 local name = string.sub(k, 7)
1238 if name ~= classID then
1239 t[k] = 'Class: ' .. name
1240 end
1241 end
1242 end
1243 end
1244 end
1245 end
1246 if not options then
1247 options = {
1248 standby = {
1249 cmdName = STATE,
1250 guiName = ACTIVE,
1251 name = ACTIVE,
1252 desc = TOGGLE_ACTIVE,
1253 type = "toggle",
1254 get = "IsActive",
1255 set = "ToggleActive",
1256 map = MAP_ACTIVESUSPENDED,
1257 order = -3,
1258 },
1259 profile = {
1260 type = 'group',
1261 name = PROFILE,
1262 desc = SET_PROFILE,
1263 order = -3.5,
1264 get = "GetProfile",
1265 args = {
1266 choose = {
1267 guiName = "Choose",
1268 cmdName = PROFILE,
1269 desc = "Choose a profile",
1270 type = 'text',
1271 get = "GetProfile",
1272 set = "SetProfile",
1273 validate = target['acedb-profile-list']
1274 },
1275 copy = {
1276 guiName = "Copy from",
1277 cmdName = PROFILE,
1278 desc = "Copy settings from another profile",
1279 type = 'text',
1280 get = "GetProfile",
1281 set = "SetProfile",
1282 validate = target['acedb-profile-copylist'],
1283 disabled = function()
1284 return not next(target['acedb-profile-copylist'])
1285 end,
1286 },
1287 other = {
1288 guiName = "Other",
1289 cmdName = PROFILE,
1290 desc = "Choose another profile",
1291 usage = "<profile name>",
1292 type = 'text',
1293 get = "GetProfile",
1294 set = "SetProfile",
1295 }
1296 }
1297 },
1298 }
1299 end
1300 return options
1301 end
1302  
1303 local function activate(self, oldLib, oldDeactivate)
1304 AceDB = self
1305 AceEvent = AceLibrary:HasInstance("AceEvent-2.0") and AceLibrary("AceEvent-2.0")
1306  
1307 self.super.activate(self, oldLib, oldDeactivate)
1308  
1309 for t in pairs(self.embedList) do
1310 if t.db then
1311 rawset(t.db, 'char', nil)
1312 rawset(t.db, 'realm', nil)
1313 rawset(t.db, 'class', nil)
1314 rawset(t.db, 'account', nil)
1315 rawset(t.db, 'profile', nil)
1316 setmetatable(t.db, db_mt)
1317 end
1318 end
1319  
1320 if oldLib then
1321 self.addonsToBeInitialized = oldLib.addonsToBeInitialized
1322 self.addonsLoaded = oldLib.addonsLoaded
1323 self.registry = oldLib.registry
1324 end
1325 if not self.addonsToBeInitialized then
1326 self.addonsToBeInitialized = {}
1327 end
1328 if not self.addonsLoaded then
1329 self.addonsLoaded = {}
1330 end
1331 if not self.registry then
1332 self.registry = {}
1333 end
1334  
1335 if oldLib then
1336 oldDeactivate(oldLib)
1337 end
1338 end
1339  
1340 local function external(self, major, instance)
1341 if major == "AceEvent-2.0" then
1342 AceEvent = instance
1343  
1344 AceEvent:embed(self)
1345  
1346 self:RegisterEvent("ADDON_LOADED")
1347 self:RegisterEvent("PLAYER_LOGOUT")
1348 elseif major == "Dewdrop-2.0" then
1349 Dewdrop = instance
1350 end
1351 end
1352  
1353 AceLibrary:Register(AceDB, MAJOR_VERSION, MINOR_VERSION, activate, nil, external)
1354 AceDB = AceLibrary(MAJOR_VERSION)