changeset 25:cb9635999171

- Reassigning loot, and marking loot as disenchanted/vault, will update the history entries also. - Tooltip help on dropdown menus appears at mouse now, instead of depending on Blizzard's "Beginner Tooltips" setting. - Forum BBcode output includes an option for MMO-Champion/Wowstead formatting. - Smarter cleanup functions for expiring caches. - Properly prefer locally-generated loot events, even when other users can see and rebroadcast the events back to you faster than you see them.
author Farmbuyer of US-Kilrogg <farmbuyer@gmail.com>
date Wed, 05 Oct 2011 02:14:07 +0000
parents 61d932f0e8f2
children f866daadcdf6
files core.lua gui.lua verbage.lua
diffstat 3 files changed, 294 insertions(+), 105 deletions(-) [+]
line wrap: on
line diff
--- a/core.lua	Wed Sep 21 06:21:48 2011 +0000
+++ b/core.lua	Wed Oct 05 02:14:07 2011 +0000
@@ -59,7 +59,8 @@
 	['bossmod'] = "DBM",
 	['keybinding_text'] = 'CTRL-SHIFT-O',
 	['forum'] = {
-		['[url]'] = '[url=http://www.wowhead.com/?item=$I]$N[/url]$X - $T',
+		['[url] Wowhead'] = '[url=http://www.wowhead.com/?item=$I]$N[/url]$X - $T',
+		['[url] MMO/Wowstead'] = '[http://db.mmo-champion.com/i/$I]$X - $T',
 		['[item] by name'] = '[item]$N[/item]$X - $T',
 		['[item] by ID'] = '[item]$I[/item]$X - $T',
 		['Custom...'] = '',
@@ -261,7 +262,7 @@
 ]]
 do
 	local caches = {}
-	local cleanup_group = AnimTimerFrame:CreateAnimationGroup()
+	local cleanup_group = _G.AnimTimerFrame:CreateAnimationGroup()
 	local time = _G.time
 	cleanup_group:SetLooping("REPEAT")
 	cleanup_group:SetScript("OnLoop", function(cg)
@@ -269,25 +270,30 @@
 		local now = time()
 		local alldone = true
 		-- this is ass-ugly
-		for _,c in ipairs(caches) do
-			while (#c > 0) and (now - c[1].t > c.ttl) do
-				addon.dprint('cache', c.name, "cache removing",c[1].t, c[1].m)
-				tremove(c,1)
+		for name,c in pairs(caches) do
+			local fifo = c.fifo
+			local active = #fifo > 0
+			while (#fifo > 0) and (now - fifo[1].t > c.ttl) do
+				addon.dprint('cache', name, "cache removing",fifo[1].t, fifo[1].m)
+				tremove(fifo,1)
 			end
-			alldone = alldone and (#c == 0)
+			if active and #fifo == 0 and c.func then
+				addon.dprint('cache', name, "empty, firing cleanup")
+				c:func()
+			end
+			alldone = alldone and (#fifo == 0)
 		end
 		if alldone then
 			addon.dprint('cache',"OnLoop finishing animation group")
 			cleanup_group:Finish()
-			for _,c in ipairs(caches) do
-				if c.func then c:func() end
-			end
 		end
 		addon.dprint('cache',"OnLoop done")
 	end)
 
 	local function _add (cache, x)
-		tinsert(cache, {t=time(),m=x})
+		local datum = { t=time(), m=x }
+		cache.hash[x] = datum
+		tinsert (cache.fifo, datum)
 		if not cleanup_group:IsPlaying() then
 			addon.dprint('cache', cache.name, "STARTING animation group")
 			cache.cleanup:SetDuration(2)  -- hmmm
@@ -295,11 +301,15 @@
 		end
 	end
 	local function _test (cache, x)
-		for _,v in ipairs(cache) do
+		return cache.hash[x] ~= nil
+		--[[for _,v in ipairs(cache) do
 			if v.m == x then return true end
-		end
+		end]]
 	end
+
 	function create_new_cache (name, ttl, on_alldone)
+		-- setting OnFinished for cleanup fires at the end of each inner loop,
+		-- with no 'requested' argument to distinguish cases.  thus, on_alldone.
 		local c = {
 			ttl = ttl,
 			name = name,
@@ -307,11 +317,11 @@
 			test = _test,
 			cleanup = cleanup_group:CreateAnimation("Animation"),
 			func = on_alldone,
+			fifo = {},
+			hash = setmetatable({}, {__mode='kv'}),
 		}
 		c.cleanup:SetOrder(1)
-		-- setting OnFinished for cleanup fires at the end of each inner loop,
-		-- with no 'requested' argument to distinguish cases.  thus, on_alldone.
-		tinsert (caches, c)
+		caches[name] = c
 		return c
 	end
 end
@@ -337,13 +347,21 @@
 			opts[opt] = default
 		end
 	end
+	-- transition&remove old options
+	opts['forum_use_itemid'] = nil
+	if opts['forum_format'] then
+		opts.forum['Custom...'] = opts['forum_format']
+		opts['forum_format'] = nil
+	end
+	if opts.forum['[url]'] then
+		opts.forum['[url] Wowhead'] = opts.forum['[url]']
+		opts.forum['[url]'] = nil
+		opts.forum['[url] MMO/Wowstead'] = option_defaults.forum['[url] MMO/Wowstead']
+		if opts['forum_current'] == '[url]' then
+			opts['forum_current'] = '[url] Wowhead'
+		end
+	end
 	option_defaults = nil
-	-- transition&remove old options
-	opts["forum_use_itemid"] = nil
-	if opts["forum_format"] then
-		opts.forum["Custom..."] = opts["forum_format"]
-		opts["forum_format"] = nil
-	end
 	if OuroLootSV then  -- may not be the same as testing g_restore_p soon
 		if OuroLootSV.saved then
 			OuroLootSV_saved = OuroLootSV.saved; OuroLootSV.saved = nil
@@ -617,7 +635,33 @@
 -- helper for CHAT_MSG_LOOT handler
 do
 	-- Recent loot cache
-	addon.recent_loot = create_new_cache ('loot', comm_cleanup_ttl)
+	local candidates = {}
+	local function prefer_local_loots (cache)
+		-- The function name is a bit of a misnomer, as local entries overwrite
+		-- remote entries as the candidate table is populated.  This routine is
+		-- to extract the results once the cache timers have expired.
+		for i,sig in ipairs(candidates) do
+			addon.dprint('loot', "processing candidate entry", i, sig)
+			local loot = candidates[sig]
+			if loot then
+				addon.dprint('loot', i, "was found")
+				candidates[sig] = nil
+				local looti = addon._addLootEntry(loot)
+				if (loot.disposition ~= 'shard')
+				   and (loot.disposition ~= 'gvault')
+				   and (not addon.history_suppress)
+				then
+					addon:_addHistoryEntry(looti)
+				end
+			end
+		end
+
+		if addon.display then
+			addon:redisplay()
+		end
+		table.wipe(candidates)
+	end
+	addon.recent_loot = create_new_cache ('loot', comm_cleanup_ttl, prefer_local_loots)
 
 	local GetItemInfo, GetItemIcon = GetItemInfo, GetItemIcon
 
@@ -634,47 +678,39 @@
 		self.dprint('loot',">>_do_loot, R:", recipient, "I:", itemid, "C:", count, "frm:", from, "ex:", extratext, "q:", iquality)
 
 		itemid = tonumber(ilink:match("item:(%d+)") or 0)
-		if local_override or ((iquality >= self.threshold) and not opts.itemfilter[itemid]) then
+		-- This is only a loop to make jumping out of it easy, and still do cleanup below.
+		while local_override or ((iquality >= self.threshold) and not opts.itemfilter[itemid]) do
 			if (self.rebroadcast and (not from)) and not local_override then
 				self:broadcast('loot', recipient, itemid, count)
 			end
-			if self.enabled or local_override then
-				local signature = recipient .. iname .. (count or "")
-				if self.recent_loot:test(signature) then
-					self.dprint('cache', "loot <",signature,"> already in cache, skipping")
-				else
-					self.recent_loot:add(signature)
-					i = self._addLootEntry{   -- There is some redundancy here...
-						kind		= 'loot',
-						person		= recipient,
-						person_class= select(2,UnitClass(recipient)),
-						cache_miss	= i and true or nil,
-						quality		= iquality,
-						itemname	= iname,
-						id			= itemid,
-						itemlink	= ilink,
-						itexture	= itexture,
-						disposition	= (recipient == self.sharder) and 'shard' or nil,
-						count		= count,
-						bcast_from	= from,
-						extratext	= extratext,
-						is_heroic	= self:is_heroic_item(ilink),
-					}
-					self.dprint('loot', "added loot entry", i)
-					if not self.history_suppress then
-						self:_addHistoryEntry(i)
-					end
-					if self.display then
-						self:redisplay()
-						--[[
-						local st = self.display:GetUserData("eoiST")
-						if st and st.frame:IsVisible() then
-							st:OuroLoot_Refresh()
-						end
-						]]
-					end
-				end
+			if (not self.enabled) and (not local_override) then break end
+			local signature = recipient .. iname .. (count or "")
+			if from and self.recent_loot:test(signature) then
+				self.dprint('cache', "loot <",signature,"> already in cache, skipping")
+			else
+				self.recent_loot:add(signature)
+				-- There is some redundancy in all this, in the interests of ease-of-coding
+				i = {
+					kind		= 'loot',
+					person		= recipient,
+					person_class= select(2,UnitClass(recipient)),
+					cache_miss	= i and true or nil,
+					quality		= iquality,
+					itemname	= iname,
+					id			= itemid,
+					itemlink	= ilink,
+					itexture	= itexture,
+					disposition	= (recipient == self.sharder) and 'shard' or nil,
+					count		= count,
+					bcast_from	= from,
+					extratext	= extratext,
+					is_heroic	= self:is_heroic_item(ilink),
+				}
+				candidates[signature] = i
+				tinsert (candidates, signature)
+				self.dprint('cache', "loot <",signature,"> added to cache, candidate", #candidates)
 			end
+			break
 		end
 		self.dprint('loot',"<<_do_loot out")
 		return i
@@ -1067,11 +1103,11 @@
 	end
 end
 
--- Tie-ins with Deadly Boss Mods
+-- Tie-in with Deadly Boss Mods (or other such addons)
 do
-	local candidates, location
+	local candidates = {}
+	local location
 	local function fixup_durations (cache)
-		if candidates == nil then return end  -- this is called for *all* cache expirations, including non-boss
 		local boss, bossi
 		boss = candidates[1]
 		if #candidates == 1 then
@@ -1108,7 +1144,7 @@
 				addon:Print("Registered kill for '%s' in %s!", boss.bosskill, boss.instance)
 			end
 		end
-		candidates = nil
+		table.wipe(candidates)
 	end
 	addon.recent_boss = create_new_cache ('boss', 10, fixup_durations)
 
@@ -1145,7 +1181,6 @@
 					duration	= duration,      -- these two deliberately may be nil
 					raiderlist	= raiders and table.concat(raiders, ", ")
 				}
-				candidates = candidates or {}
 				tinsert(candidates,c)
 			end
 			break
@@ -1531,6 +1566,7 @@
 		if e.kind ~= 'loot' then return end
 
 		local i,h = self:get_loot_history(e.person)
+		-- If any of these change, update the end of history_handle_disposition.
 		local n = {
 			id = e.id,
 			when = self:format_timestamp (g_today, e),
@@ -1574,48 +1610,127 @@
 		end
 	end
 
-	function addon:reassign_loot (index, name_to)
-		local e = assert(g_loot[index], "trying to reassign nonexistant entry")
-		assert(e.kind=='loot', "trying to reassign something that isn't loot")
-		assert(type(name_to)=='string' and name_to:len()>0)
+	-- Given an entry in a g_loot table, looks up the corresponding history
+	-- entry.  Returns the player's index and history table (as in get_loot_history)
+	-- and the index into that table of the loot entry.  On failure, returns nil
+	-- and an error message ready to be formatted with the loot's name/itemlink.
+	function addon:_history_by_loot_id (loot, operation_text)
+		-- Using assert() here would be concatenating error strings that probably
+		-- wouldn't be used.  Do more verbose testing instead.
+		if type(loot) ~= 'table' then
+			error("trying to "..operation_text.." nonexistant entry")
+		end
+		if loot.kind ~= 'loot' then
+			error("trying to "..operation_text.." something that isn't loot")
+		end
 
-		local name_from = e.person
-		local tag = e.history_unique
+		local player = loot.person
+		local tag = loot.history_unique
 		local errtxt
+		local player_i, player_h, hist_i
 
 		if not tag then
 			errtxt = "Entry for %s is missing a history tag!"
 		else
-			local from_i,from_h = self:get_loot_history(name_from)
-			local to_i,to_h = self:get_loot_history(name_to)
-
-			local hi
-			for i,h in ipairs(from_h) do
+			player_i,player_h = self:get_loot_history(player)
+			for i,h in ipairs(player_h) do
 				local unique = h.id .. ' ' .. h.when
 				if unique == tag then
-					hi = i
+					hist_i = i
 					break
 				end
 			end
-			if not hi then
+			if not hist_i then
 				-- 1) loot an item, 2) clear old history, 3) reassign from current loot
 				-- Bah.  Anybody that tricky is already recoding the tables directly anyhow.
 				errtxt = "There is no record of %s ever having been assigned!"
-			else
-				hi = tremove (from_h, hi)
-				tinsert (to_h, 1, hi)
-				tsort (from_h, comp)
-				tsort (to_h, comp)
 			end
 		end
 
 		if errtxt then
-			self:Print(errtxt .. "  Loot will be reassigned but history will NOT be updated.", e.itemlink)
+			return nil, errtxt
 		end
-		e.person = name_to
-		e.person_class = select(2,UnitClass(name_to))
+		return player_i, player_h, hist_i
+	end
 
-		self:Print("Reassigned entry %d from '%s' to '%s'.", index, name_from, name_to)
+	function addon:reassign_loot (index, to_name)
+		assert(type(to_name)=='string' and to_name:len()>0)
+		local e = g_loot[index]
+		local from_i, from_h, hist_i = self:_history_by_loot_id (e, "reassign")
+		local from_name = e.person
+		local to_i,to_h = self:get_loot_history(to_name)
+
+		if not from_i then
+			-- from_h is the formatted error text
+			self:Print(from_h .. "  Loot will be reassigned, but history will NOT be updated.", e.itemlink)
+		else
+			local hist_h = tremove (from_h, hist_i)
+			tinsert (to_h, 1, hist_h)
+			tsort (from_h, comp)
+			tsort (to_h, comp)
+		end
+		e.person = to_name
+		e.person_class = select(2,UnitClass(to_name))
+		self.hist_clean = nil
+
+		self:Print("Reassigned entry %d/%s from '%s' to '%s'.", index, e.itemlink, from_name, to_name)
+	end
+
+	-- Any extra work for the "Mark as <x>" dropdown actions.  The
+	-- corresponding <x> will already have been assigned in the loot entry.
+	local deleted_cache = {} --setmetatable({}, {__mode='k'})
+	function addon:history_handle_disposition (index, olddisp)
+		local e = g_loot[index]
+		-- Standard disposition has a nil entry, but that's tedious in debug
+		-- output, so force to a string instead.
+		olddisp = olddisp or 'normal'
+		local newdisp = e.disposition or 'normal'
+		-- Ignore misclicks and the like
+		if olddisp == newdisp then return end
+
+		local name = e.person
+
+		if (newdisp == 'shard' or newdisp == 'gvault') then
+			local name_i, name_h, hist_i = self:_history_by_loot_id (e, "mark")
+			-- remove history entry
+			if hist_i then
+				local hist_h = tremove (name_h, hist_i)
+				deleted_cache[e.history_unique] = hist_h
+				self.hist_clean = nil
+			elseif (olddisp == 'shard' or olddisp == 'gvault') then
+				-- Sharding a vault item, or giving the auto-sharder something to bank,
+				-- etc, wouldn't necessarily have had a history entry to begin with.
+			else
+				self:Print(name_h .. "  Loot has been marked, but history will NOT be updated.", e.itemlink)
+			end
+			return
+		end
+
+		if (olddisp == 'shard' or olddisp == 'gvault')
+		   and (newdisp == 'normal' or newdisp == 'offspec')
+		then
+			local name_i, name_h = self:get_loot_history(name)
+
+			-- Must create a new history entry.  Could call '_addHistoryEntry(index)'
+			-- but that would duplicate a lot of effort.  To start with, check the
+			-- cache of stuff we've already deleted; if it's not there then just do
+			-- the same steps as _addHistoryEntry.
+			local entry
+			if e.history_unique and deleted_cache[e.history_unique] then
+				entry = deleted_cache[e.history_unique]
+				deleted_cache[e.history_unique] = nil
+			end
+			local when = g_today and self:format_timestamp (g_today, e) or tostring(e.stamp)
+			entry = entry or {
+				id = e.id,
+				when = when,
+				count = e.count,
+			}
+			tinsert (name_h, 1, entry)
+			e.history_unique = e.history_unique or (entry.id .. ' ' .. entry.when)
+			self.hist_clean = nil
+			return
+		end
 	end
 end
 
--- a/gui.lua	Wed Sep 21 06:21:48 2011 +0000
+++ b/gui.lua	Wed Oct 05 02:14:07 2011 +0000
@@ -401,6 +401,8 @@
 				arg1 = funcs[name],
 				arg2 = arg,
 				notCheckable = true,
+				tooltipOnButton = true,
+				tooltipWhileDisabled = true,
 				tooltipTitle = tiptext and name or nil,
 				tooltipText = tiptext,
 			})
@@ -468,9 +470,11 @@
 	end,
 
 	["Mark as normal"] = function(rowi,disp) -- broadcast the change?  ugh
+		local olddisp = g_loot[rowi].disposition
 		g_loot[rowi].disposition = disp
 		g_loot[rowi].bcast_from = nil
 		g_loot[rowi].extratext = nil
+		addon:history_handle_disposition (rowi, olddisp)
 	end,
 
 	["Show only this player"] = function(rowi)
@@ -495,6 +499,7 @@
 		CloseDropDownMenus()  -- also need to close parent menu
 	end,
 	["Enter name..."] = function(rowi)
+		CloseDropDownMenus()  -- also need to close parent menu
 		local dialog = StaticPopup_Show "OUROL_REASSIGN_ENTER"
 		dialog.data = {index=rowi, display=_d}
 	end,
@@ -518,10 +523,10 @@
 		notCheckable = true,
 	}},
 	{
-		"Rebroadcast this day%time|Broadcasts everything from here down until a new day",
-		"Delete remaining entries for this day%time|Erases everything from here down until a new day",
-		"Insert new loot entry%loot|Inserts new loot above this one, prompting you for information",
-		"Insert new boss kill event%boss|Inserts new event above this one, prompting you for information",
+		"Rebroadcast this day%time|Broadcasts everything from here down until a new day.",
+		"Delete remaining entries for this day%time|Erases everything from here down until a new day.",
+		"Insert new loot entry%loot|Inserts new loot above this one, prompting you for information.",
+		"Insert new boss kill event%boss|Inserts new event above this one, prompting you for information.",
 		CLOSE
 	}, dropdownfuncs)
 local eoi_loot_dropdown = gen_easymenu_table(
@@ -534,14 +539,14 @@
 		"Mark as disenchanted%shard",
 		"Mark as offspec%offspec",
 		"Mark as guild vault%gvault",
-		"Mark as normal|This is the default. Selecting any 'Mark as <x>' action blanks out extra notes about who broadcast this entry, etc.",
+		"Mark as normal|This is the default.  Selecting any 'Mark as <x>' action blanks out extra notes about who broadcast this entry, etc.",
 		"--",
 		"Rebroadcast this loot entry|Sends this loot event, including special notes, as if it just happened.",
 		"Delete this loot event|Permanent, no going back!",
-		"Delete remaining entries for this boss%boss|Erases everything from here down until a new boss/day",
-		"Insert new loot entry%loot|Inserts new loot above this one, prompting you for information",
-		"Insert new boss kill event%boss|Inserts new event above this one, prompting you for information",
-		"Edit note|Same as double-clicking in the notes column",
+		"Delete remaining entries for this boss%boss|Erases everything from here down until a new boss/day.",
+		"Insert new loot entry%loot|Inserts new loot above this one, prompting you for information.",
+		"Insert new boss kill event%boss|Inserts new event above this one, prompting you for information.",
+		"Edit note|Same as double-clicking in the notes column.",
 		"--",
 		CLOSE
 	}, dropdownfuncs)
@@ -557,6 +562,8 @@
 			text = "Reassign to...",
 			hasArrow = true,
 			--menuList = filled in in the fly,
+			tooltipOnButton = true,
+			tooltipWhileDisabled = true,
 		},
 	},
 	{
@@ -571,12 +578,12 @@
 		notCheckable = true,
 	}},
 	{
-		"Change from 'wipe' to 'kill'|Also collapses other wipe entries",
-		"Rebroadcast this boss|Broadcasts the kill event and all subsequent loot until next boss",
+		"Change from 'wipe' to 'kill'|Also collapses other wipe entries.",
+		"Rebroadcast this boss|Broadcasts the kill event and all subsequent loot until next boss.",
 		"Delete this boss event|Permanent, no going back!",
-		"Delete remaining entries for this boss%boss|Erases everything from here down until a new boss/day",
-		"Insert new loot entry%loot|Inserts new loot above this one, prompting you for information",
-		"Insert new boss kill event%boss|Inserts new event above this one, prompting you for information",
+		"Delete remaining entries for this boss%boss|Erases everything from here down until a new boss/day.",
+		"Insert new loot entry%loot|Inserts new loot above this one, prompting you for information.",
+		"Insert new boss kill event%boss|Inserts new event above this one, prompting you for information.",
 		"--",
 		CLOSE
 	}, dropdownfuncs)
@@ -702,7 +709,15 @@
 		end
 		eoi_player_dropdown[2].menuList =
 			gen_easymenu_table (raiders, {"Enter name...",CLOSE}, dropdownfuncs)
-		--tabledump(eoi_player_dropdown)
+		if e.disposition == 'shard' or e.disposition == 'gvault' then
+			eoi_player_dropdown[2].disabled = true
+			eoi_player_dropdown[2].tooltipTitle = "Cannot Reassign"
+			eoi_player_dropdown[2].tooltipText = "You must first mark this item as 'normal' or 'offspec' before reassignment."
+		else
+			eoi_player_dropdown[2].disabled = nil
+			eoi_player_dropdown[2].tooltipTitle = nil
+			eoi_player_dropdown[2].tooltipText = nil
+		end
 		EasyMenu (eoi_player_dropdown, dropdownmenuframe, cellFrame, 0, 0, "MENU")
 
 	elseif kind == 'boss' then
@@ -1129,6 +1144,7 @@
 
 	tabs_OnGroupSelected["hist"] = function(container,specials)
 		histST = LibStub("ScrollingTable"):CreateST(hist_st_cols,eoi_st_displayed_rows,eoi_st_rowheight)
+		_d:SetUserData("histST",histST)
 		if addon.author_debug then
 			_G.OLHST = histST
 		end
@@ -1176,6 +1192,7 @@
 
 		tabs_OnGroupSelected["hist"] = function(container,specials)
 			local st_widget = GUI:Create("lib-st")
+			-- don't need _d:GetUserData("histST") here, as it's already a local
 			histST:OuroLoot_Refresh()
 			st_widget:WrapST(histST)
 			st_widget.head_offset = 15
@@ -1745,6 +1762,10 @@
 		self.display:Hide()
 	end
 
+	-- This probably causes taint... hm.
+	local prev_fade_time = UIDROPDOWNMENU_SHOW_TIME
+	UIDROPDOWNMENU_SHOW_TIME = 4
+
 	local display = GUI:Create("Frame")
 	if _d then
 		display:SetUserData("eoiST",_d)   -- warning! warning! kludge detected!
@@ -1766,6 +1787,7 @@
 	display.sizer_e:SetScript("OnMouseUp",nil)
 	]]
 	display:SetCallback("OnClose", function(_display)
+		UIDROPDOWNMENU_SHOW_TIME = prev_fade_time 
 		_d = _display:GetUserData("eoiST")
 		self.display = nil
 		GUI:Release(_display)
@@ -2218,4 +2240,48 @@
 	end,]]
 }
 
+
+-- Workaround this bug:  http://us.battle.net/wow/en/forum/topic/3278901991
+if true then
+	-- Verbatim copy of UIDropDownMenuTemplates.xml:155 or so, except as
+	-- tagged with CHANGE.
+	local function onenter (self, motion)
+		if ( self.hasArrow ) then
+			local level =  self:GetParent():GetID() + 1;
+			local listFrame = _G["DropDownList"..level];
+			if ( not listFrame or not listFrame:IsShown() or select(2, listFrame:GetPoint()) ~= self ) then
+				ToggleDropDownMenu(self:GetParent():GetID() + 1, self.value, nil, nil, nil, nil, self.menuList, self);
+			end
+		else
+			CloseDropDownMenus(self:GetParent():GetID() + 1);
+		end
+		_G[self:GetName().."Highlight"]:Show();
+		UIDropDownMenu_StopCounting(self:GetParent());
+		if ( self.tooltipTitle ) then
+			if ( self.tooltipOnButton ) then
+				GameTooltip:SetOwner(self, "ANCHOR_RIGHT");
+				GameTooltip:AddLine(self.tooltipTitle, 1.0, 1.0, 1.0);
+				GameTooltip:AddLine(self.tooltipText, nil,nil,nil,1);  -- CHANGE added nil->1 arguments
+				GameTooltip:Show();
+			else
+				GameTooltip_AddNewbieTip(self, self.tooltipTitle, 1.0, 1.0, 1.0, self.tooltipText, 1);
+			end
+		end
+	end
+	-- end verbatime copy
+
+	for i = 1, UIDROPDOWNMENU_MAXLEVELS do
+		local list = _G["DropDownList"..i]
+		if list then
+			for j = 1, UIDROPDOWNMENU_MAXBUTTONS do
+				local button = _G["DropDownList"..i.."Button"..j]
+				if button then
+					--print("button fixup",i,j)
+					button:SetScript("OnEnter",onenter)
+				end
+			end
+		end
+	end
+end
+
 -- vim:noet
--- a/verbage.lua	Wed Sep 21 06:21:48 2011 +0000
+++ b/verbage.lua	Wed Oct 05 02:14:07 2011 +0000
@@ -355,9 +355,9 @@
 The <History> tab maintains a list of all loot.  It is intended to help answer
 questions such as "When was the last time PlayerX won something?" and "How much stuff
 has PlayerY gotten lately?"  The history tab is, by design, not as configurable
-as the main <Loot> tab; entries cannot be edited and so forth.
+as the main <Loot> tab; entries cannot be manually edited and so forth.
 
-Loot history is the only "live" data tab which persists across the +Clear Loot> command.
+This loot history is the only "live" data tab which persists across the +Clear Loot> command.
 For this reason, very little information is stored:  only the recipient, the item,
 and a textual timestamp.  Boss names, offspecs, and other notes are not preserved.
 
@@ -367,8 +367,8 @@
 lead to odd display issues.
 
 Left-clicking a row will change the window to display all recorded loot for that
-player.  On that display, right-clicking any row will return to showing the most
-recent single item for all players.
+player.  While on that display, right-clicking any row will return to showing the
+most recent single item for all players.
 
 Histories are maintained per-realm.  This refers to the realm you are on when the
 loot drops, not the realm of the player receiving it (in the case of cross-realm
@@ -389,6 +389,13 @@
 loot" display.  It is another good periodic maintenance step, but does not discard
 as much data as the other actions.
 
+Using +Reassign to...> will also move the item between player histories.  The timestamp
+will not be changed; it will "always have been" received by the new recipient.
+
+Using +Mark as disenchanted> or +Mark as guild vault> will remove the item from
+history altogether.  Remarking such an item as +normal> or +offspec> will replace
+the item back into the player's history.
+
 Note:  the first time you display the histories during a game session, you will
 likely see several items listed as +UNKNOWN>.  This is not a bug; these items are
 simply not in your local game cache.  WoW will automatically retrieve the missing