vanilla-wow-addons – Blame information for rev 1

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 office 1 --[[ Stubby
2  
3 $Id: Stubby.lua 941 2006-07-10 17:34:32Z mentalpower $
4 Version: 3.8.0 (Kangaroo)
5  
6 Stubby is an addon that allows you to register boot code for
7 your addon.
8  
9 This bootcode will be run whenever your addon does not demand
10 load on startup so that you can setup your own conditions for
11 loading.
12  
13 A quick example of this is:
14 -------------------------------------------
15 Stubby.RegisterBootCode("myAddOn", "CommandHandler", [[
16 local function cmdHandler(msg)
17 LoadAddOn("myAddOn")
18 MyAddOn_Command(msg)
19 end
20 SLASH_MYADDON1 = "/myaddon"
21 SlashCmdList['MYADDON'] = cmdHandler
22 ]]);
23 -------------------------------------------
24 So, what did this just do? It registered some boot code
25 (called "CommandHandler") with Stubby that Stubby will
26 (in the case you are not demand loaded) execute on your
27 behalf.
28  
29 In the above example, your boot code sets up a command handler
30 which causes your addon to load and process the command.
31  
32 Another example:
33 -------------------------------------------
34 Stubby.CreateAddOnLoadBootCode("myAddOn", "Blizzard_AuctionUI")
35 -------------------------------------------
36 Ok, what was that? Well you just setup some boot code
37 for your addon that will register an addon hook when
38 Stubby loads and your addon doesn't. This addon hook
39 will cause your addon to load when the AuctionUI does.
40  
41  
42 The primary functions that you will be interested in are:
43 CreateAddOnLoadBootCode(ownerAddOn, triggerAddOn)
44 CreateEventLoadBootCode(ownerAddOn, triggerEvent)
45 CreateFunctionLoadBootCode(ownerAddOn, triggerFunction)
46 And the manual, but vastly more powerful:
47 RegisterBootCode(ownerAddOn, bootName, bootCode)
48  
49  
50 Stubby can also save variables for you if you wish to retain
51 stateful information in your boot code. (maybe you have
52 recieved notification from your user that they wish always
53 to have your addon load for the current toon?)
54  
55 These are the variable functions:
56 SetConfig(ownerAddOn, variable, value, isGlobal)
57 GetConfig(ownerAddOn, variable)
58 ClearConfig(ownerAddOn, variable)
59  
60 The SetConfig function sets the configuration variable
61 "variable" for ownerAddOn to value. The variable is
62 per-toon unless isGlobal is set.
63  
64 The GetConfig function gets "variable" for ownerAddOn
65 it will return per-toon values before global ones.
66  
67 The ClearConfig function clears the toon specific and
68 global "variable" for ownerAddOn.
69  
70  
71 The following functions are also available for you to use
72 if you need to use some manual boot code and want to
73 hook into some function, addon or event within your boot
74 code:
75 Stubby.RegisterFunctionHook(triggerFunction, position, hookFunction, ...)
76 Stubby.RegisterAddOnHook(triggerAddOn, ownerAddOn, hookFunction, ...)
77 Stubby.RegisterEventHook(triggerEvent, ownerAddOn, hookFunction, ...)
78  
79 RegisterFunctionHook allows you to hook into a function.
80 * The triggerFunction is a string that names the function you
81 want to hook into. eg: "GameTooltip.SetOwner"
82 * The position is a negative or positive number that defines
83 the actual calling order of the addon. The smaller or more
84 negative the number, the earlier in the call sequence your
85 hookFunction will be called, the larger the number, the
86 later your hook will be called. The actual original (hooked)
87 function is called at position 0, so if your addon is hooked
88 at a negative position, you will not have access to any
89 return values.
90 * You pass (by reference) your function that you wish called
91 as hookFunction. This function will be called with the
92 following parameters:
93 hookFunction(hookParams, returnValue, hook1, hook2 .. hookN)
94 - hookParams is a table containing the additional parameters
95 passed to the RegisterFunctionHook function (the "..." params)
96 - returnValue is an array of the returned values of the function
97 or nil if none.
98 - hook1..hookN are the original parameters of the hooked
99 function in the original order.
100  
101 RegisterAddOnHook is very much like the register function hook
102 call except that there is no positioning (you may get notified in
103 any order with respect to any other addons which may be hooked)
104 * The triggerAddOn specifies the name of the addon of which you
105 want to be notified of it's loading.
106 * The ownerAddOn is your addon's name (used for removing hooks)
107 * The hookFunction is a function that gets called when the
108 triggerAddOn loads or if it is already loaded straight away.
109 This function will be called with the following parameters
110 hookFunction(hookParams)
111 - hookParams is a table containing the additional parameters
112 passed to the RegisterAddOnHook function (the "..." params)
113  
114 RegisterEventHook allows you to hook an event in much the same
115 way as the above functions.
116 * The triggerEvent is an event which causes your hookFunction to
117 be executed.
118 * The ownerAddOn is your addon's name (used for removing hooks)
119 * The hookFunction is a function that gets called whenever the
120 triggerEvent fires (until canceled with UnregisterEventHook)
121 This function will be called with the following parameters:
122 hookFunction(hookParams, event, hook1, hook2 .. hookN)
123 - hookParams is a table containing the additional parameters
124 passed to the RegisterEventHook function (the "..." params)
125 - event is the event string that has just been fired
126 - hook1..hookN are the original parameters of the event
127 function in the original order.
128  
129 Other functions which may be of interest are:
130 UnregisterFunctionHook(triggerFunction, hookFunc)
131 UnregisterAddOnHook(triggerAddOn, ownerAddOn)
132 UnregisterEventHook(triggerEvent, ownerAddOn)
133 UnregisterBootCode(ownerAddOn, bootName)
134  
135 There is also a single exposed 'constant' allowing you to do
136 some basic version checking for compatibility:
137 Stubby.VERSION (introduced in revision 507)
138 This constant is Stubby's revision number, a simple positive
139 integer that will increase by an arbitrary amount with each
140 new version of Stubby.
141 Current $Revision: 941 $
142  
143 Example:
144 -------------------------------------------
145 if (Stubby.VERSION and Stubby.VERSION >= 507) then
146 -- Register boot code
147 else
148 Stubby.Print("You need to update your version of Stubby!")
149 end
150 -------------------------------------------
151  
152 License:
153 This program is free software; you can redistribute it and/or
154 modify it under the terms of the GNU General Public License
155 as published by the Free Software Foundation; either version 2
156 of the License, or (at your option) any later version.
157  
158 This program is distributed in the hope that it will be useful,
159 but WITHOUT ANY WARRANTY; without even the implied warranty of
160 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
161 GNU General Public License for more details.
162  
163 You should have received a copy of the GNU General Public License
164 along with this program(see GLP.txt); if not, write to the Free Software
165 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
166 --]]
167  
168 local cleanList
169 local config = {
170 hooks = { functions={}, origFuncs={} },
171 calls = { functions={}, callList={} },
172 loads = {},
173 events = {},
174 }
175  
176 local StubbyConfig = {}
177  
178  
179 -- Function prototypes
180 local chatPrint -- chatPrint(...)
181 local checkAddOns -- checkAddOns()
182 local clearConfig -- clearConfig(ownerAddOn, variable)
183 local createAddOnLoadBootCode -- createAddOnLoadBootCode(ownerAddOn, triggerAddOn)
184 local createEventLoadBootCode -- createEventLoadBootCode(ownerAddOn, triggerEvent)
185 local createFunctionLoadBootCode -- createFunctionLoadBootCode(ownerAddOn, triggerFunction)
186 local eventWatcher -- eventWatcher(event)
187 local events -- events(event, param)
188 local getConfig -- getConfig(ownerAddOn, variable)
189 local getOrigFunc -- getOrigFunc(triggerFunction)
190 local hookCall -- hookCall(funcName, a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
191 local hookInto -- hookInto(triggerFunction)
192 local inspectAddOn -- inspectAddOn(addonName, title, info)
193 local loadWatcher -- loadWatcher(loadedAddOn)
194 local onLoaded -- onLoaded()
195 local onWorldStart -- onWorldStart()
196 local rebuildNotifications -- rebuildNotifications(notifyItems)
197 local registerAddOnHook -- registerAddOnHook(triggerAddOn, ownerAddOn, hookFunction, ...)
198 local registerBootCode -- registerBootCode(ownerAddOn, bootName, bootCode)
199 local registerEventHook -- registerEventHook(triggerEvent, ownerAddOn, hookFunction, ...)
200 local registerFunctionHook -- registerFunctionHook(triggerFunction, position, hookFunc, ...)
201 local runBootCodes -- runBootCodes()
202 local searchForNewAddOns -- searchForNewAddOns()
203 local cleanUpAddOnData -- cleanUpAddOnData()
204 local cleanUpAddOnConfigs -- cleanUpAddOnConfigs()
205 local setConfig -- setConfig(ownerAddOn, variable, value, isGlobal)
206 local shouldInspectAddOn -- shouldInspectAddOn(addonName)
207 local unregisterAddOnHook -- unregisterAddOnHook(triggerAddOn, ownerAddOn)
208 local unregisterBootCode -- unregisterBootCode(ownerAddOn, bootName)
209 local unregisterEventHook -- unregisterEventHook(triggerEvent, ownerAddOn)
210 local unregisterFunctionHook -- unregisterFunctionHook(triggerFunction, hookFunc)
211  
212  
213 -- Function definitions
214  
215 -- This function takes all the items and their requested orders
216 -- and assigns an actual ordering to them.
217 function rebuildNotifications(notifyItems)
218 local notifyFuncs = {}
219 for hookType, hData in notifyItems do
220 notifyFuncs[hookType] = {}
221  
222 -- Sort all hooks for this type in ascending numerical order.
223 local sortedPositions = {}
224 for requestedPos,_ in hData do
225 table.insert(sortedPositions, requestedPos)
226 end
227 table.sort(sortedPositions)
228  
229 -- Process the sorted request list and insert in correct
230 -- order into the call list.
231 for _,requestedPos in sortedPositions do
232 local func = hData[requestedPos]
233 table.insert(notifyFuncs[hookType], func)
234 end
235 end
236 return notifyFuncs
237 end
238  
239 -- This function's purpose is to execute all the attached
240 -- functions in order and the original call at just before
241 -- position 0.
242 function hookCall(funcName, a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
243 local result, r1,r2,r3,r4,r5,r6,r7,r8,r9,r10,r11,r12,r13,r14,r15,r16,r17,r18,r19,r20
244 local orig = Stubby.GetOrigFunc(funcName)
245 if (not orig) then return end
246  
247 local res;
248 local retVal = nil
249 local returns = false
250  
251 local callees
252 if config.calls and config.calls.callList and config.calls.callList[funcName] then
253 callees = config.calls.callList[funcName]
254 end
255  
256 if (callees) then
257 for _,func in ipairs(callees) do
258 if (orig and func.p >= 0) then
259 result, r1,r2,r3,r4,r5,r6,r7,r8,r9,r10,r11,r12,r13,r14,r15,r16,r17,r18,r19,r20
260 = pcall(orig, a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
261 if (result) then
262 if r1 or r2 or r3 or r4 or r5 or r6 or r7 or r8 or r9 or r10 or r11 or r12 or r13 or r14 or r15 or r16 or r17 or r18 or r19 or r20 then
263 retVal = { r1,r2,r3,r4,r5,r6,r7,r8,r9,r10,r11,r12,r13,r14,r15,r16,r17,r18,r19,r20 }
264 returns = true
265 end
266 else
267 Stubby.Print("Error occured while running hooks for: ", tostring(funcName), "\n", r1, "\nCall Chain:\n", debugstack(2, 3, 6))
268 end
269 orig = nil
270 end
271 local result, res, addit = pcall(func.f, func.a, retVal, a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20);
272 if (result) then
273 if (type(res) == 'string') then
274 if (res == 'abort') then return end
275 if (res == 'killorig') then orig = nil end
276 if (res == 'setreturn') then
277 retVal = addit
278 returns = true
279 end
280 if (res == 'setparams') then
281 a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20 = unpack(addit)
282 end
283 end
284 else
285 Stubby.Print("Error occured while running hooks for: ", tostring(funcName), "\n", res, "\nCall Chain:\n", debugstack(2, 3, 6))
286 end
287 end
288 end
289 if (orig) then
290 result, r1,r2,r3,r4,r5,r6,r7,r8,r9,r10,r11,r12,r13,r14,r15,r16,r17,r18,r19,r20
291 = pcall(orig, a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
292 if (result) then
293 if r1 or r2 or r3 or r4 or r5 or r6 or r7 or r8 or r9 or r10 or r11 or r12 or r13 or r14 or r15 or r16 or r17 or r18 or r19 or r20 then
294 returns = true
295 end
296 else
297 Stubby.Print("Error occured while running hooks for: ", tostring(funcName), "\n", r1, "\nCall Chain:\n", debugstack(2, 3, 6))
298 end
299 end
300 if (returns) then
301 return r1,r2,r3,r4,r5,r6,r7,r8,r9,r10,r11,r12,r13,r14,r15,r16,r17,r18,r19,r20
302 end
303 end
304  
305 -- This function automatically hooks Stubby in place of the
306 -- original function, dynamically.
307 Stubby_OldFunction = nil
308 Stubby_NewFunction = nil
309 function hookInto(triggerFunction)
310 if (config.hooks.origFuncs[triggerFunction]) then return end
311 RunScript("Stubby_OldFunction = "..triggerFunction)
312 RunScript("Stubby_NewFunction = function(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20) return Stubby.HookCall('"..triggerFunction.."', a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20) end")
313 RunScript(triggerFunction.." = Stubby_NewFunction")
314 config.hooks.functions[triggerFunction] = Stubby_NewFunction;
315 config.hooks.origFuncs[triggerFunction] = Stubby_OldFunction;
316 Stubby_NewFunction = nil
317 Stubby_OldFunction = nil
318 end
319  
320 function getOrigFunc(triggerFunction)
321 if (config.hooks) and (config.hooks.origFuncs) then
322 return config.hooks.origFuncs[triggerFunction]
323 end
324 end
325  
326  
327 -- This function causes a given function to be hooked by stubby and
328 -- configures the hook function to be called at the given position.
329 -- The original function gets executed a position 0. Use a negative
330 -- number to get called before the original function, and positive
331 -- number to get called after the original function. Default position
332 -- is 200. If someone else is already using your number, you will get
333 -- automatically moved up for after or down for before. Please also
334 -- leave space for other people who may need to position their hooks
335 -- in between your hook and the original.
336 function registerFunctionHook(triggerFunction, position, hookFunc, ...)
337 local insertPos = tonumber(position) or 200
338 local funcObj = { f=hookFunc, a=arg, p=position }
339 if (table.getn(arg) == 0) then funcObj.a = nil; end
340  
341 if (not config.calls) then config.calls = {} end
342 if (not config.calls.functions) then config.calls.functions = {} end
343 if (config.calls.functions[triggerFunction]) then
344 while (config.calls.functions[triggerFunction][insertPos]) do
345 if (position >= 0) then
346 insertPos = insertPos + 1
347 else
348 insertPos = insertPos - 1
349 end
350 end
351 config.calls.functions[triggerFunction][insertPos] = funcObj
352 else
353 config.calls.functions[triggerFunction] = {}
354 config.calls.functions[triggerFunction][insertPos] = funcObj
355 end
356 config.calls.callList = rebuildNotifications(config.calls.functions);
357 hookInto(triggerFunction)
358 end
359  
360 function unregisterFunctionHook(triggerFunction, hookFunc)
361 if not (config.calls and config.calls.functions and config.calls.functions[triggerFunction]) then return end
362 for pos, funcObj in config.calls.functions[triggerFunction] do
363 if (funcObj and funcObj.f == hookFunc) then
364 config.calls.functions[triggerFunction][pos] = nil
365 end
366 end
367 end
368  
369 -- This function registers a given function to be called when a given
370 -- addon is loaded, or immediatly if it is already loaded (this can be
371 -- used to setup a hooking function to execute when an addon is loaded
372 -- but not before)
373 function registerAddOnHook(triggerAddOn, ownerAddOn, hookFunction, ...)
374 if (IsAddOnLoaded(triggerAddOn)) then
375 hookFunction(unpack(arg))
376 else
377 local addon = string.lower(triggerAddOn)
378 if (not config.loads[addon]) then config.loads[addon] = {} end
379 config.loads[addon][ownerAddOn] = nil
380 if (hookFunction) then
381 config.loads[addon][ownerAddOn] = { f=hookFunction, a=arg }
382 end
383 end
384 end
385  
386 function unregisterAddOnHook(triggerAddOn, ownerAddOn)
387 local addon = string.lower(triggerAddOn)
388 if (config.loads and config.loads[addon] and config.loads[addon][ownerAddOn]) then
389 config.loads[addon][ownerAddOn] = nil
390 end
391 end
392  
393  
394 function loadWatcher(loadedAddOn)
395 local addon = string.lower(loadedAddOn)
396 if (config.loads[addon]) then
397 local ownerAddOn, hookDetail
398 for ownerAddOn, hookDetail in config.loads[addon] do
399 hookDetail.f(hookDetail.a)
400 end
401 end
402 end
403  
404 -- This function registers a given function to be called when a given
405 -- event is fired (this can be used to activate an addon upon receipt
406 -- of a given event etc)
407 function registerEventHook(triggerEvent, ownerAddOn, hookFunction, ...)
408 if (not config.events[triggerEvent]) then
409 config.events[triggerEvent] = {}
410 StubbyFrame:RegisterEvent(triggerEvent)
411 end
412 config.events[triggerEvent][ownerAddOn] = nil
413 if (hookFunction) then
414 config.events[triggerEvent][ownerAddOn] = { f=hookFunction, a=arg }
415 end
416 end
417  
418 function unregisterEventHook(triggerEvent, ownerAddOn)
419 if (config.events and config.events[triggerEvent] and config.events[triggerEvent][ownerAddOn]) then
420 config.events[triggerEvent][ownerAddOn] = nil
421 end
422 end
423  
424 function eventWatcher(event)
425 if (config.events[event]) then
426 local ownerAddOn, hookDetail
427 for ownerAddOn, hookDetail in config.events[event] do
428 hookDetail.f(hookDetail.a, event, arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8,arg9,arg10,arg11,arg12,arg13,arg14,arg15,arg16,arg17,arg18,arg19,arg20);
429 end
430 end
431 end
432  
433 -- This function registers boot code. This is a piece of code
434 -- specified as a string, which Stubby will execute on your behalf
435 -- when we are first loaded. This code can do anything a normal
436 -- lua script can, such as create global functions, register a
437 -- command handler, hook into functions, load your addon etc.
438 -- Leaving bootCode nil will remove your boot.
439 function registerBootCode(ownerAddOn, bootName, bootCode)
440 local ownerIndex = string.lower(ownerAddOn)
441 local bootIndex = string.lower(bootName)
442 if (not StubbyConfig.boots) then StubbyConfig.boots = {} end
443 if (not StubbyConfig.boots[ownerIndex]) then StubbyConfig.boots[ownerIndex] = {} end
444 StubbyConfig.boots[ownerIndex][bootIndex] = nil
445 if (bootCode) then
446 StubbyConfig.boots[ownerIndex][bootIndex] = bootCode
447 end
448 end
449  
450 function unregisterBootCode(ownerAddOn, bootName)
451 local ownerIndex = string.lower(ownerAddOn)
452 local bootIndex = string.lower(bootName)
453 if not (StubbyConfig.boots) then return end
454 if not (ownerIndex and StubbyConfig.boots[ownerIndex]) then return end
455 if (bootIndex == nil) then
456 StubbyConfig.boots[ownerIndex] = nil
457 else
458 StubbyConfig.boots[ownerIndex][bootIndex] = nil
459 end
460 end
461  
462 function createAddOnLoadBootCode(ownerAddOn, triggerAddOn)
463 registerBootCode(ownerAddOn, triggerAddOn.."AddOnLoader",
464 'local function hookFunction() '..
465 'LoadAddOn("'..ownerAddOn..'") '..
466 'Stubby.UnregisterAddOnHook("'..triggerAddOn..'", "'..ownerAddOn..'") '..
467 'end '..
468 'Stubby.RegisterAddOnHook("'..triggerAddOn..'", "'..ownerAddOn..'", hookFunction)'
469 );
470 end
471  
472 function createFunctionLoadBootCode(ownerAddOn, triggerFunction)
473 registerBootCode(ownerAddOn, triggerFunction.."FunctionLoader",
474 'local function hookFunction() '..
475 'LoadAddOn("'..ownerAddOn..'") '..
476 'Stubby.UnregisterFunctionHook("'..triggerFunction..'", hookFunction) '..
477 'end '..
478 'Stubby.RegisterFunctionHook("'..triggerFunction..'", 200, hookFunction)'
479 );
480 end
481  
482 function createEventLoadBootCode(ownerAddOn, triggerEvent)
483 registerBootCode(ownerAddOn, triggerEvent.."FunctionLoader",
484 'local function hookFunction() '..
485 'LoadAddOn("'..ownerAddOn..'") '..
486 'Stubby.UnregisterEventHook("'..triggerEvent..'", "'..ownerAddOn..'") '..
487 'end '..
488 'Stubby.RegisterEventHook("'..triggerEvent..'", "'..ownerAddOn..'", hookFunction)'
489 );
490 end
491  
492 -- Functions to check through all addons for dependants.
493 -- If any exist that we don't know about, and have a dependancy of us, then we will load them
494 -- once to give them a chance to register themselves with us.
495 function checkAddOns()
496 if not StubbyConfig.inspected then return end
497 local goodList = {}
498 local addonCount = GetNumAddOns()
499 local name, title, notes
500 for i=1, addonCount do
501 name, title, notes = GetAddOnInfo(i)
502 if (StubbyConfig.inspected and StubbyConfig.inspected[name]) then
503 local infoCompare = title.."|"..(notes or "")
504 if (infoCompare == StubbyConfig.addinfo[name]) then
505 goodList[name] = true
506 end
507 end
508 end
509 for name,_ in StubbyConfig.inspected do
510 if (not goodList[name]) then
511 StubbyConfig.inspected[name] = nil
512 StubbyConfig.addinfo[name] = nil
513 end
514 end
515 end
516  
517 -- Cleans up boot codes for removed addons and prompts for deletion of their
518 -- configurations.
519 function cleanUpAddOnData()
520 if (not StubbyConfig.boots) then return; end
521  
522 for b in pairs(StubbyConfig.boots) do
523 local _,title = GetAddOnInfo(b)
524 if (not title) then
525 StubbyConfig.boots[b] = nil
526  
527 if (StubbyConfig.configs) then
528 if (cleanList == nil) then cleanList = {}; end
529 table.insert(cleanList, b)
530 end
531 end
532 end
533  
534 if (cleanList) then cleanUpAddOnConfigs(); end
535 end
536  
537 -- Shows confirmation dialogs to clean configuration for addons that have
538 -- just been removed. Warning: Calls itself recursively until done.
539 function cleanUpAddOnConfigs()
540 if (not cleanList) then return; end
541  
542 local addonIndex = table.getn(cleanList)
543 local addonName = cleanList[addonIndex]
544  
545 if (addonIndex == 1) then
546 cleanList = nil
547 else
548 table.remove(cleanList, addonIndex)
549 end
550  
551 StaticPopupDialogs["CLEANUP_STUBBY" .. addonIndex] = {
552 text = "The AddOn \"" .. addonName .. "\" is no longer available. Do you wish to delete it's loading preferences?",
553 button1 = "Delete",
554 button2 = "Keep",
555 OnAccept = function()
556 StubbyConfig.configs[addonName] = nil
557 cleanUpAddOnConfigs();
558 end,
559 OnCancel = function()
560 cleanUpAddOnConfigs();
561 end,
562 timeout = 0,
563 whileDead = 1,
564 };
565 StaticPopup_Show("CLEANUP_STUBBY" .. addonIndex, "","");
566 end
567  
568 function shouldInspectAddOn(addonName)
569 if not StubbyConfig.inspected[addonName] then return true end
570 return false
571 end
572  
573 function inspectAddOn(addonName, title, info)
574 LoadAddOn(addonName)
575 StubbyConfig.inspected[addonName] = true
576 StubbyConfig.addinfo[addonName] = title.."|"..(info or "")
577 end
578  
579 function searchForNewAddOns()
580 local addonCount = GetNumAddOns()
581 local name, title, notes, enabled, loadable, reason, security, requiresLoad
582 for i=1, addonCount do
583 requiresLoad = false
584 name, title, notes, enabled, loadable, reason, security = GetAddOnInfo(i)
585 if (IsAddOnLoadOnDemand(i) and shouldInspectAddOn(name) and loadable) then
586 local addonDeps = { GetAddOnDependencies(i) }
587 for _, dependancy in pairs(addonDeps) do
588 if (string.lower(dependancy) == "stubby") then
589 requiresLoad = true
590 end
591 end
592 end
593  
594 if (requiresLoad) then inspectAddOn(name, title, notes) end
595 end
596 end
597  
598 -- This function runs through the boot scripts we have, and if the
599 -- related addon is not loaded yet, runs the boot script.
600 function runBootCodes()
601 if (not StubbyConfig.boots) then return end
602 for addon, boots in StubbyConfig.boots do
603 if (not IsAddOnLoaded(addon) and IsAddOnLoadOnDemand(addon)) then
604 local _, _, _, _, loadable = GetAddOnInfo(addon)
605 if (loadable) then
606 for bootname, boot in pairs(boots) do
607 RunScript(boot)
608 end
609 end
610 end
611 end
612 end
613  
614  
615 function onWorldStart()
616 -- Check for expired or updated addons and remove their boot codes.
617 checkAddOns()
618  
619 -- Run all of our boots to setup the respective addons functions.
620 runBootCodes()
621  
622 -- The search for new life and new civilizations... or just addons maybe.
623 searchForNewAddOns()
624  
625 -- Delete data for removed addons
626 cleanUpAddOnData()
627 end
628  
629 function onLoaded()
630 if not StubbyConfig.inspected then StubbyConfig.inspected = {} end
631 if not StubbyConfig.addinfo then StubbyConfig.addinfo = {} end
632 Stubby.RegisterEventHook("PLAYER_LOGIN", "Stubby", onWorldStart)
633 end
634  
635 function events(event, param)
636 if (not event) then event = "" end
637 if (not param) then param = "" end
638 if (event == "ADDON_LOADED") then
639 if (string.lower(param) == "stubby") then onLoaded() end
640 Stubby.LoadWatcher(param)
641 end
642 Stubby.EventWatcher(event)
643 end
644  
645 function chatPrint(...)
646 if ( DEFAULT_CHAT_FRAME ) then
647 local msg = ""
648 for i=1, table.getn(arg) do
649 if i==1 then msg = arg[i]
650 else msg = msg.." "..arg[i]
651 end
652 end
653 DEFAULT_CHAT_FRAME:AddMessage(msg, 1.0, 0.35, 0.15)
654 end
655 end
656  
657 -- This function allows boot code to store a configuration variable
658 -- by default the variable is per character unless isGlobal is set.
659 function setConfig(ownerAddOn, variable, value, isGlobal)
660 local ownerIndex = string.lower(ownerAddOn)
661 local varIndex = string.lower(variable)
662 if (not isGlobal) then
663 varIndex = string.lower(UnitName("player")) .. ":" .. varIndex
664 end
665  
666 if (not StubbyConfig.configs) then StubbyConfig.configs = {} end
667 if (not StubbyConfig.configs[ownerIndex]) then StubbyConfig.configs[ownerIndex] = {} end
668 StubbyConfig.configs[ownerIndex][varIndex] = value
669 end
670  
671 -- This function gets a config variable stored by the above function
672 -- it will prefer a player specific variable over a global with the
673 -- same name
674 function getConfig(ownerAddOn, variable)
675 local ownerIndex = string.lower(ownerAddOn)
676 local globalIndex = string.lower(variable)
677 local playerIndex = string.lower(UnitName("player")) .. ":" .. globalIndex
678  
679 if (not StubbyConfig.configs) then return end
680 if (not StubbyConfig.configs[ownerIndex]) then return end
681 local curValue = StubbyConfig.configs[ownerIndex][playerIndex]
682 if (curValue == nil) then
683 curValue = StubbyConfig.configs[ownerIndex][globalIndex]
684 end
685 return curValue
686 end
687  
688 -- This function clears the config variable specified (both the
689 -- global and player specific) or all config variables for the
690 -- ownerAddOn if no variable is specified
691 function clearConfig(ownerAddOn, variable)
692 local ownerIndex = string.lower(ownerAddOn)
693 if (not StubbyConfig.configs) then return end
694 if (not StubbyConfig.configs[ownerIndex]) then return end
695 if (variable) then
696 local globalIndex = string.lower(variable)
697 local playerIndex = string.lower(UnitName("player")) .. ":" .. globalIndex
698 StubbyConfig.configs[ownerIndex][globalIndex] = nil
699 StubbyConfig.configs[ownerIndex][playerIndex] = nil
700 else
701 StubbyConfig.configs[ownerIndex] = nil
702 end
703 end
704  
705 -- Extract the revision number from SVN keyword string
706 local function getRevision()
707 local found, _, rev = string.find("$Revision: 941 $", "(%d+)")
708 if (found ~= nil) then return tonumber(rev); end
709 return nil
710 end
711  
712 -- Setup our Stubby global object. All interaction is done
713 -- via the methods exposed here.
714 Stubby = {
715 VERSION = getRevision(),
716 Print = chatPrint,
717 Events = events,
718 HookCall = hookCall,
719 SetConfig = setConfig,
720 GetConfig = getConfig,
721 ClearConfig = clearConfig,
722 GetOrigFunc = getOrigFunc,
723 LoadWatcher = loadWatcher,
724 EventWatcher = eventWatcher,
725 RegisterBootCode = registerBootCode,
726 RegisterEventHook = registerEventHook,
727 RegisterAddOnHook = registerAddOnHook,
728 RegisterFunctionHook = registerFunctionHook,
729 UnregisterBootCode = unregisterBootCode,
730 UnregisterEventHook = unregisterEventHook,
731 UnregisterAddOnHook = unregisterAddOnHook,
732 UnregisterFunctionHook = unregisterFunctionHook,
733 CreateAddOnLoadBootCode = createAddOnLoadBootCode,
734 CreateEventLoadBootCode = createEventLoadBootCode,
735 CreateFunctionLoadBootCode = createFunctionLoadBootCode,
736 }