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