vanilla-wow-addons – Blame information for rev 1
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
1 | office | 1 | ------------------------------------------------------ |
2 | -- GFWTooltip.lua |
||
3 | -- Generic tooltip hooking |
||
4 | -- Credit where due: mostly based on GDI's Reagent Info (which in turn uses code from ItemsMatrix, LootLink, and Auctioneer) |
||
5 | -- Additional inspiration from Reagent Watch and EnhTooltip |
||
6 | ------------------------------------------------------ |
||
7 | local GFWTOOLTIP_THIS_VERSION = 8; |
||
8 | ------------------------------------------------------ |
||
9 | |||
10 | ------------------------------------------------------ |
||
11 | -- External API |
||
12 | ------------------------------------------------------ |
||
13 | |||
14 | --[[ GFWTooltip_AddCallback(modName, callbackFunction) |
||
15 | |||
16 | Allows you to have a function called whenever a tooltip is displayed. |
||
17 | The function is called with arguments (frame, name, link, source): |
||
18 | |||
19 | frame: the instance of GameTooltip being shown, which you can modify via its API |
||
20 | name: the first line of the tooltip, typically an item name. |
||
21 | link: a hyperlink to the item being shown in the tooltip |
||
22 | source: a non-localized string identifying why this tooltip is being shown |
||
23 | (e.g. "MERCHANT" for an item seen in the MerchantFrame) |
||
24 | |||
25 | Your callback function should return true if it modifies the tooltip, so we know not to modify the same tooltip twice. |
||
26 | |||
27 | Example: |
||
28 | |||
29 | function MyTooltipCallback(frame, name, link, source) |
||
30 | frame:AddLine("some stuff"); |
||
31 | return true; |
||
32 | end |
||
33 | function MyMod_OnLoad() |
||
34 | GFWTooltip_AddCallback("MyMod", MyTooltipCallback); |
||
35 | end |
||
36 | ]] |
||
37 | |||
38 | local function addCallback(modName, callbackFunction) |
||
39 | GFWTooltip_Callbacks[modName] = callbackFunction; |
||
40 | end |
||
41 | |||
42 | ------------------------------------------------------ |
||
43 | -- Initial setup |
||
44 | ------------------------------------------------------ |
||
45 | |||
46 | if (GFWTooltip == nil) then |
||
47 | GFWTooltip = {}; |
||
48 | end |
||
49 | if (GFWTooltip_Callbacks == nil) then |
||
50 | GFWTooltip_Callbacks = {}; |
||
51 | end |
||
52 | if (GFWTooltip_OriginalFunctions == nil) then |
||
53 | GFWTooltip_OriginalFunctions = {}; |
||
54 | end |
||
55 | local hookTableName = "GFWTooltip_"..GFWTOOLTIP_THIS_VERSION.."_HookFunctions"; |
||
56 | if (getglobal(hookTableName) == nil) then |
||
57 | setglobal(hookTableName, {}); |
||
58 | end |
||
59 | local G = GFWTooltip; |
||
60 | local Orig = GFWTooltip_OriginalFunctions; |
||
61 | local Hook = getglobal(hookTableName); |
||
62 | |||
63 | ------------------------------------------------------ |
||
64 | -- Internal functions |
||
65 | ------------------------------------------------------ |
||
66 | |||
67 | local checkTimer; -- Timer for frequency of tooltip checks |
||
68 | local gameToolTipOwner; -- The current owner of the GameTooltip |
||
69 | local currentTooltip; -- Current Tooltip frame |
||
70 | local itemsMatrixEnabled; -- Boolean to watch if ItemsMatrix is running |
||
71 | |||
72 | local function hookFunction(functionName) |
||
73 | if (Orig[functionName] == nil) then |
||
74 | Orig[functionName] = getglobal(functionName); |
||
75 | end |
||
76 | setglobal(functionName, Hook[functionName]); |
||
77 | end |
||
78 | |||
79 | local function hookMethod(objectName, functionName) |
||
80 | local signature = objectName.."_"..functionName; |
||
81 | local object = getglobal(objectName); |
||
82 | if (Orig[signature] == nil) then |
||
83 | Orig[signature] = object[functionName]; |
||
84 | end |
||
85 | object[functionName] = Hook[signature]; |
||
86 | end |
||
87 | |||
88 | -- We call this at the bottom of this file to get things started. |
||
89 | local function setupHookFunctions() |
||
90 | |||
91 | -- Hooks for Blizzard's GameTooltip functions |
||
92 | hookMethod("GameTooltip", "SetHyperlink"); |
||
93 | hookMethod("GameTooltip", "SetLootItem"); |
||
94 | hookMethod("GameTooltip", "SetQuestItem"); |
||
95 | hookMethod("GameTooltip", "SetQuestLogItem"); |
||
96 | hookMethod("GameTooltip", "SetInventoryItem"); |
||
97 | hookMethod("GameTooltip", "SetMerchantItem"); |
||
98 | hookMethod("GameTooltip", "SetCraftItem"); |
||
99 | hookMethod("GameTooltip", "SetTradeSkillItem"); |
||
100 | hookMethod("GameTooltip", "SetAuctionSellItem"); |
||
101 | hookMethod("GameTooltip", "SetOwner"); |
||
102 | hookFunction("GameTooltip_OnHide"); |
||
103 | |||
104 | -- Hooks for other Blizzard functions |
||
105 | hookFunction("ContainerFrameItemButton_OnEnter"); |
||
106 | hookFunction("ContainerFrame_Update"); |
||
107 | hookFunction("SetItemRef"); |
||
108 | |||
109 | -- Dynamic-load Blizzard functions |
||
110 | hookFunction("AuctionFrame_LoadUI"); |
||
111 | |||
112 | -- Hook ItemsMatrix's functions if they're available |
||
113 | if (type(ItemsMatrix_AddExtraTooltipInfo) == "function") then |
||
114 | itemsMatrixEnabled = true; |
||
115 | end |
||
116 | |||
117 | -- Hook AIOI's stuff if it's available and we're not running ItemsMatrix |
||
118 | if(AllInOneInventory_Enabled and not itemsMatrixEnabled) then |
||
119 | hookFunction("AllInOneInventory_ModifyItemTooltip"); |
||
120 | end |
||
121 | |||
122 | -- Hook for MyInventory |
||
123 | if (MyInventoryProfile and not itemsMatrixEnabled) then |
||
124 | hookFunction("MyInventory_ContainerFrameItemButton_OnEnter"); |
||
125 | hookFunction("MyInventoryFrame_Update"); |
||
126 | end |
||
127 | |||
128 | -- Hook for the LootLink window's tooltip (not using LL's tooltip hook because we already cover everything else it does) |
||
129 | if (LootLinkItemButton_OnEnter) then |
||
130 | hookFunction("LootLinkItemButton_OnEnter"); |
||
131 | end |
||
132 | end |
||
133 | |||
134 | local function addTooltipInfo(frame, name, link, source) |
||
135 | if (frame.gfwDone == 1) then return; end -- we've already been here |
||
136 | |||
137 | local changedTooltip = false; |
||
138 | for modName, callback in GFWTooltip_Callbacks do |
||
139 | if (callback ~= nil and type(callback) == "function") then |
||
140 | local modifiedInCallback = callback(frame, name, link, source); |
||
141 | changedTooltip = changedTooltip or modifiedInCallback; |
||
142 | if (modifiedInCallback) then |
||
143 | --GFWUtils.DebugLog("Ran tooltip callback for ".. modName..", tooltip modified."); |
||
144 | else |
||
145 | --GFWUtils.DebugLog("Ran tooltip callback for ".. modName..", tooltip not modified."); |
||
146 | end |
||
147 | end |
||
148 | end |
||
149 | --GFWUtils.DebugLog("Done with tooltip callbacks."); |
||
150 | if (changedTooltip) then |
||
151 | frame.gfwDone = 1; |
||
152 | frame:Show(); |
||
153 | end |
||
154 | |||
155 | end |
||
156 | |||
157 | -- Checks the tooltip info for an item name. If one is found and we haven't updated the tip already, process it. |
||
158 | local function checkTooltipInfo(frame, link, source) |
||
159 | if (link and link ~= GFWTooltip_LastLink) then |
||
160 | frame.gfwDone = nil; |
||
161 | end |
||
162 | if (link == nil) then |
||
163 | link = GFWTooltip_LastLink; |
||
164 | end |
||
165 | |||
166 | -- If we've already added our information, no need to do it again |
||
167 | currentTooltip = frame; |
||
168 | if ( frame == nil ) then |
||
169 | return; |
||
170 | end |
||
171 | if (not frame:IsVisible()) then |
||
172 | frame.gfwDone = nil; |
||
173 | end |
||
174 | local _, _, itemLink = string.find(link, "(item:%d+:%d+:%d+:%d+)"); |
||
175 | if (itemLink) then |
||
176 | local name = GetItemInfo(itemLink); |
||
177 | if (name and name ~= GFWTooltip_LastName) then |
||
178 | frame.gfwDone = nil; |
||
179 | end |
||
180 | if ( not frame.gfwDone) then |
||
181 | addTooltipInfo(frame, name, link, source); |
||
182 | GFWTooltip_LastLink = link; |
||
183 | GFWTooltip_LastName = name; |
||
184 | return; |
||
185 | end |
||
186 | end |
||
187 | end |
||
188 | |||
189 | local function nameFromLink(link) |
||
190 | local name; |
||
191 | if ( not link ) then |
||
192 | return nil; |
||
193 | end |
||
194 | for name in string.gfind(link, "|c%x+|Hitem:%d+:%d+:%d+:%d+|h%[(.-)%]|h|r") do |
||
195 | return name; |
||
196 | end |
||
197 | return nil; |
||
198 | end |
||
199 | |||
200 | -- Calling this will allow us to automatically add information to tooltips when needed |
||
201 | local function autoInfoOn() |
||
202 | lSuppressInfoAdd = nil; |
||
203 | end |
||
204 | |||
205 | -- Calling this will prevent us from automatically adding information to tooltips |
||
206 | local function autoInfoOff() |
||
207 | lSuppressInfoAdd = 1; |
||
208 | end |
||
209 | |||
210 | local function findItemInBags(findName) |
||
211 | for bag = 0, 4, 1 do |
||
212 | size = GetContainerNumSlots(bag); |
||
213 | if (size) then |
||
214 | for slot = size, 1, -1 do |
||
215 | local link = GetContainerItemLink(bag, slot); |
||
216 | if (link) then |
||
217 | local itemName = nameFromLink(link); |
||
218 | if (itemName == findName) then |
||
219 | return bag, slot; |
||
220 | end |
||
221 | end |
||
222 | end |
||
223 | end |
||
224 | end |
||
225 | end |
||
226 | |||
227 | |||
228 | -------------------- |
||
229 | -- Hook Functions -- |
||
230 | -------------------- |
||
231 | |||
232 | function Hook.AuctionFrame_LoadUI() |
||
233 | Orig.AuctionFrame_LoadUI(); |
||
234 | |||
235 | if (Orig.AuctionFrameItem_OnEnter == nil) then |
||
236 | hookFunction("AuctionFrameItem_OnEnter"); |
||
237 | |||
238 | GFWUtils.DebugLog("GFWTooltip AuctionFrame hooks installed."); |
||
239 | end |
||
240 | end |
||
241 | |||
242 | function Hook.GameTooltip_SetHyperlink(tooltip, link) |
||
243 | Orig.GameTooltip_SetHyperlink(tooltip, link); |
||
244 | |||
245 | local name = GetItemInfo(link); |
||
246 | addTooltipInfo(tooltip, name, link, "LINK"); |
||
247 | end |
||
248 | |||
249 | function Hook.GameTooltip_SetLootItem(this, slot) |
||
250 | Orig.GameTooltip_SetLootItem(this, slot); |
||
251 | |||
252 | local link = GetLootSlotLink(slot); |
||
253 | local name = nameFromLink(link); |
||
254 | addTooltipInfo(this, name, link, "LOOT"); |
||
255 | end |
||
256 | |||
257 | function Hook.GameTooltip_SetQuestItem(this, qtype, slot) |
||
258 | Orig.GameTooltip_SetQuestItem(this, qtype, slot); |
||
259 | |||
260 | local link = GetQuestItemLink(qtype, slot); |
||
261 | local name = nameFromLink(link); |
||
262 | addTooltipInfo(this, name, link, "QUEST"); |
||
263 | end |
||
264 | |||
265 | function Hook.GameTooltip_SetQuestLogItem(this, qtype, slot) |
||
266 | Orig.GameTooltip_SetQuestLogItem(this, qtype, slot); |
||
267 | |||
268 | local link = GetQuestLogItemLink(qtype, slot); |
||
269 | local name = nameFromLink(link); |
||
270 | addTooltipInfo(this, name, link, "QUESTLOG"); |
||
271 | end |
||
272 | |||
273 | function Hook.GameTooltip_SetMerchantItem(this, slot) |
||
274 | Orig.GameTooltip_SetMerchantItem(this, slot); |
||
275 | |||
276 | local link = GetMerchantItemLink(slot); |
||
277 | local name = nameFromLink(link); |
||
278 | addTooltipInfo(this, name, link, "MERCHANT"); |
||
279 | end |
||
280 | |||
281 | function Hook.AuctionFrameItem_OnEnter(type, index) |
||
282 | Orig.AuctionFrameItem_OnEnter(type, index); |
||
283 | |||
284 | local link = GetAuctionItemLink(type, index); |
||
285 | local name = nameFromLink(link); |
||
286 | addTooltipInfo(GameTooltip, name, link, "AUCTION"); |
||
287 | end |
||
288 | |||
289 | function Hook.GameTooltip_SetInventoryItem(this, unit, slot) |
||
290 | local hasItem, hasCooldown, repairCost = Orig.GameTooltip_SetInventoryItem(this, unit, slot); |
||
291 | local link = GetInventoryItemLink(unit, slot); |
||
292 | local name = nameFromLink(link); |
||
293 | addTooltipInfo(this, name, link, "INVENTORY"); |
||
294 | |||
295 | return hasItem, hasCooldown, repairCost; |
||
296 | end |
||
297 | |||
298 | function Hook.GameTooltip_SetCraftItem(this, skill, slot) |
||
299 | Orig.GameTooltip_SetCraftItem(this, skill, slot); |
||
300 | local link; |
||
301 | if (slot) then |
||
302 | link = GetCraftReagentItemLink(skill, slot); |
||
303 | local name = nameFromLink(link); |
||
304 | addTooltipInfo(this, name, link, "CRAFT_REAGENT"); |
||
305 | else |
||
306 | link = GetCraftItemLink(skill); |
||
307 | local name = nameFromLink(link); |
||
308 | addTooltipInfo(this, name, link, "CRAFT_ITEM"); |
||
309 | end |
||
310 | end |
||
311 | |||
312 | function Hook.GameTooltip_SetTradeSkillItem(this, skill, slot) |
||
313 | Orig.GameTooltip_SetTradeSkillItem(this, skill, slot); |
||
314 | local link; |
||
315 | if (slot) then |
||
316 | link = GetTradeSkillReagentItemLink(skill, slot); |
||
317 | local name = nameFromLink(link); |
||
318 | addTooltipInfo(this, name, link, "TRADESKILL_REAGENT"); |
||
319 | else |
||
320 | link = GetTradeSkillItemLink(skill); |
||
321 | local name = nameFromLink(link); |
||
322 | addTooltipInfo(this, name, link, "TRADESKILL_ITEM"); |
||
323 | end |
||
324 | end |
||
325 | |||
326 | function Hook.GameTooltip_SetAuctionSellItem(this) |
||
327 | Orig.GameTooltip_SetAuctionSellItem(this); |
||
328 | local name, texture, quantity, quality, canUse, price = GetAuctionSellItemInfo(); |
||
329 | if (name) then |
||
330 | local bag, slot = findItemInBags(name); |
||
331 | if (bag) then |
||
332 | local link = GetContainerItemLink(bag, slot); |
||
333 | addTooltipInfo(this, name, link, "AUCTION_SELL"); |
||
334 | end |
||
335 | end |
||
336 | end |
||
337 | |||
338 | function Hook.ContainerFrameItemButton_OnEnter() |
||
339 | Orig.ContainerFrameItemButton_OnEnter(); |
||
340 | autoInfoOff(); |
||
341 | |||
342 | if (itemsMatrixEnabled) then |
||
343 | autoInfoOn(); |
||
344 | return; |
||
345 | end |
||
346 | |||
347 | if (not InRepairMode()) then |
||
348 | local frameID = this:GetParent():GetID(); |
||
349 | local buttonID = this:GetID(); |
||
350 | local link = GetContainerItemLink(frameID, buttonID); |
||
351 | local name = nameFromLink(link); |
||
352 | |||
353 | if( name ) then |
||
354 | local texture, itemCount, locked, quality, readable = GetContainerItemInfo(frameID, buttonID); |
||
355 | checkTooltipInfo(GameTooltip, link, "CONTAINER"); |
||
356 | GameTooltip:Show(); |
||
357 | end |
||
358 | end |
||
359 | autoInfoOn(); |
||
360 | end |
||
361 | |||
362 | function Hook.ContainerFrame_Update(frame) |
||
363 | Orig.ContainerFrame_Update(frame); |
||
364 | autoInfoOff(); |
||
365 | |||
366 | if (itemsMatrixEnabled) then |
||
367 | autoInfoOn(); |
||
368 | return; |
||
369 | end |
||
370 | |||
371 | if( not InRepairMode() and GameTooltip:IsVisible() ) then |
||
372 | local frameID = frame:GetID(); |
||
373 | local frameName = frame:GetName(); |
||
374 | local iButton; |
||
375 | for iButton = 1, frame.size do |
||
376 | local button = getglobal(frameName.."Item"..iButton); |
||
377 | if( GameTooltip:IsOwned(button) ) then |
||
378 | local buttonID = button:GetID(); |
||
379 | local link = GetContainerItemLink(frameID, buttonID); |
||
380 | local name = nameFromLink(link); |
||
381 | |||
382 | if( name ) then |
||
383 | local texture, itemCount, locked, quality, readable = GetContainerItemInfo(frameID, buttonID); |
||
384 | checkTooltipInfo(GameTooltip, link, "CONTAINER"); |
||
385 | GameTooltip:Show(); |
||
386 | end |
||
387 | end |
||
388 | end |
||
389 | end |
||
390 | autoInfoOn(); |
||
391 | end |
||
392 | |||
393 | function Hook.GameTooltip_OnHide() |
||
394 | Orig.GameTooltip_OnHide(); |
||
395 | GFWTooltip_LastLink = nil; |
||
396 | GFWTooltip_LastName = nil; |
||
397 | GameTooltip.gfwDone = nil; |
||
398 | if ( currentTooltip ) then |
||
399 | currentTooltip.gfwDone = nil; |
||
400 | currentTooltip = nil; |
||
401 | end |
||
402 | end |
||
403 | |||
404 | function Hook.GameTooltip_SetOwner(this, owner, anchor) |
||
405 | Orig.GameTooltip_SetOwner(this, owner, anchor); |
||
406 | gameToolTipOwner = owner; |
||
407 | end |
||
408 | |||
409 | function Hook.SetItemRef(link, text, button) |
||
410 | if (not ItemRefTooltip:IsVisible()) then |
||
411 | ItemRefTooltip.gfwDone = nil; |
||
412 | end |
||
413 | Orig.SetItemRef(link, text, button); |
||
414 | checkTooltipInfo(ItemRefTooltip, link, "ITEMREF"); |
||
415 | end |
||
416 | |||
417 | -- AIOI Hooks |
||
418 | function Hook.AllInOneInventory_ModifyItemTooltip(bag, slot, tooltipName) |
||
419 | Orig.AllInOneInventory_ModifyItemTooltip(bag, slot, tooltipName); |
||
420 | |||
421 | -- Verify AIOI is installed and running |
||
422 | if(not AllInOneInventory_Enabled or itemsMatrixEnabled) then |
||
423 | return; |
||
424 | end |
||
425 | |||
426 | local tooltip = getglobal(tooltipName); |
||
427 | if ( not tooltip ) then |
||
428 | tooltip = getglobal("GameTooltip"); |
||
429 | tooltipName = "GameTooltip"; |
||
430 | end |
||
431 | if ( not tooltip ) then |
||
432 | return false; |
||
433 | end |
||
434 | |||
435 | if ( not InRepairMode() ) then |
||
436 | local link = GetContainerItemLink(bag, slot); |
||
437 | local name = nameFromLink(link); |
||
438 | |||
439 | addTooltipInfo(GameTooltip, name, link, "AIOI"); |
||
440 | end |
||
441 | end |
||
442 | |||
443 | -- MyInventory Hooks |
||
444 | function Hook.MyInventory_ContainerFrameItemButton_OnEnter() |
||
445 | Orig.MyInventory_ContainerFrameItemButton_OnEnter(); |
||
446 | |||
447 | if (GameTooltip:IsVisible() and not InRepairMode()) then |
||
448 | local bag, slot = MyInventory_GetIdAsBagSlot(this:GetID()); |
||
449 | local _, stack = GetContainerItemInfo(bag, slot); |
||
450 | |||
451 | local link = GetContainerItemLink(bag, slot); |
||
452 | local name = nameFromLink(link); |
||
453 | |||
454 | if(link and name) then |
||
455 | addTooltipInfo(GameTooltip, name, link, "MYINV"); |
||
456 | end |
||
457 | end |
||
458 | end |
||
459 | |||
460 | function Hook.MyInventoryFrame_Update(frame) |
||
461 | Orig.MyInventoryFrame_Update(frame); |
||
462 | |||
463 | local name = frame:GetName(); |
||
464 | for j=1, frame.size, 1 do |
||
465 | local itemButton = getglobal(name.."Item"..j); |
||
466 | |||
467 | if (GameTooltip:IsVisible() and GameTooltip:IsOwned(itemButton)) then |
||
468 | tooltip = getglobal("GameTooltip"); |
||
469 | tooltipName = "GameTooltip"; |
||
470 | |||
471 | if ( not tooltip ) then |
||
472 | return false; |
||
473 | end |
||
474 | |||
475 | local bag, slot = MyInventory_GetIdAsBagSlot(itemButton:GetID()); |
||
476 | local link = GetContainerItemLink(bag, slot); |
||
477 | local name = nameFromLink(link); |
||
478 | |||
479 | if (name ~= nil) then |
||
480 | addTooltipInfo(GameTooltip, name, link, "MYINV"); |
||
481 | end |
||
482 | end |
||
483 | end |
||
484 | end |
||
485 | |||
486 | function Hook.LootLinkItemButton_OnEnter() |
||
487 | Orig.LootLinkItemButton_OnEnter(); |
||
488 | local name = this:GetText(); |
||
489 | local itemLink = ItemLinks[name]; |
||
490 | local link; |
||
491 | if (itemLink and itemLink.c and itemLink.i and LootLink_CheckItemServer(itemLink, LootLinkState.ServerNamesToIndices[GetCVar("realmName")])) then |
||
492 | local item = string.gsub(itemLink.i, "(%d+):(%d+):(%d+):(%d+)", "%1:0:%3:%4"); |
||
493 | link = "|c"..itemLink.c.."|Hitem:"..item.."|h["..name.."]|h|r"; |
||
494 | end |
||
495 | if (link) then |
||
496 | addTooltipInfo(LootLinkTooltip, name, link, "LOOTLINK"); |
||
497 | end |
||
498 | end |
||
499 | |||
500 | ------------------------------------------------------ |
||
501 | -- load only if not already loaded |
||
502 | ------------------------------------------------------ |
||
503 | |||
504 | if (G.Version == nil or (tonumber(G.Version) and G.Version < GFWTOOLTIP_THIS_VERSION)) then |
||
505 | |||
506 | -- Initialize state variables |
||
507 | checkTimer = 0; -- Timer for frequency of tooltip checks |
||
508 | itemsMatrixEnabled = false; -- Boolean to watch if ItemsMatrix is running |
||
509 | |||
510 | -- Export functions |
||
511 | setupHookFunctions(); |
||
512 | G.AddCallback = addCallback; |
||
513 | GFWTooltip_AddCallback = addCallback; |
||
514 | GFWTooltip.FindItemInBags = findItemInBags; |
||
515 | GFWTooltip.NameFromLink = nameFromLink; |
||
516 | |||
517 | -- Set version number |
||
518 | G.Version = GFWTOOLTIP_THIS_VERSION; |
||
519 | |||
520 | GFWUtils.Print("GFWTooltip v"..GFWTOOLTIP_THIS_VERSION.." loaded."); |
||
521 | |||
522 | end |
||
523 | |||
524 |