changeset 100:a57133ee3c9b

- Allow event callbacks using the standard CallbackHandler scheme. Add a debug.callback flag with usual semantics. - Fire an initial set of events. This will take experimentation. - If restoring g_loot, set metatables on previous entries also.
author Farmbuyer of US-Kilrogg <farmbuyer@gmail.com>
date Mon, 30 Jul 2012 19:25:46 +0000
parents 966d06c8d9c9
children f7162a1cadc7
files LibFarmbuyer.lua core.lua gui.lua options.lua verbage.lua
diffstat 5 files changed, 108 insertions(+), 24 deletions(-) [+]
line wrap: on
line diff
--- a/LibFarmbuyer.lua	Sun Jul 29 23:12:18 2012 +0000
+++ b/LibFarmbuyer.lua	Mon Jul 30 19:25:46 2012 +0000
@@ -8,11 +8,11 @@
   Evaluates to true if I'm hacking on something.
 
 - tableprint(t[,f])
-  A single print() call to the contents of T, including nils; strings are
-  cleaned up with respect to embedded '|'/control chars.  A single space
-  is used during concatenation of T.  If a function F is passed, calls that
-  instead of print().  Returns the accumulated string and either T or the
-  returned values of F, depending on which was used.
+  A single print() call to the tostring'd contents of T, including nils;
+  strings are cleaned up with respect to embedded '|'/control chars.  A
+  single space is used during concatenation of T.  If a function F is passed,
+  calls that instead of print().  Returns the accumulated string and either
+  T or the returned values of F, depending on which was used.
 
 - safeprint(...)
   Same as tableprint() on the argument list.  Returns the results of tableprint.
--- a/core.lua	Sun Jul 29 23:12:18 2012 +0000
+++ b/core.lua	Mon Jul 30 19:25:46 2012 +0000
@@ -205,12 +205,14 @@
 		flow		= false,
 		notraid		= false,
 		cache		= false,
+		callback	= false,
 		alsolog		= false,
 	}
 	--@debug@
 	DEBUG_PRINT		= true
 	debug.loot		= true
 	debug.comm		= true
+	is_guilded		= _G.IsInGuild()
 	--@end-debug@
 
 	-- This looks ugly, but it factors out the load-time decisions from
@@ -388,7 +390,7 @@
 local GetNumRaidMembers = _G.GetNumGroupMembers or _G.GetNumRaidMembers
 local IsInRaid = _G.IsInRaid or (function() return GetNumRaidMembers() > 0 end)
 -- En masse forward decls of symbols defined inside local blocks
-local _register_bossmod, makedate, create_new_cache, _init, _log
+local _register_bossmod, makedate, create_new_cache, _init, _log, _do_loot_metas
 local _history_by_loot_id, _setup_unique_replace, _unavoidable_collision
 local _notify_about_change, _notify_about_remote
 
@@ -611,7 +613,7 @@
 	-- this into a weakly-keyed table.
 	mt = { __metatable = 'Should be using setmode.' }
 
-	g_uniques = setmetatable (m_reset{}, mt)
+	g_uniques = _G.setmetatable (m_reset{}, mt)
 end
 
 
@@ -689,7 +691,7 @@
 			cleanup = cleanup_group:CreateAnimation("Animation"),
 			func = on_alldone,
 			fifo = {},
-			hash = setmetatable({}, {__mode='kv'}),
+			hash = _G.setmetatable({}, {__mode='kv'}),
 		}
 		c.cleanup:SetOrder(1)
 		caches[name] = c
@@ -716,6 +718,11 @@
 	g_restore_p = OuroLootSV ~= nil
 	self.dprint('flow', "oninit sets restore as", g_restore_p)
 
+	-- Primarily for plugins, but can be of use to me also...
+	self.callbacks = _G.LibStub("CallbackHandler-1.0"):New(self)
+	--function self.callbacks:OnUsed (target_aka_self, eventname)  end
+	--function self.callbacks:OnUnused (target_aka_self, eventname)  end
+
 	if _G.OuroLootOptsDB == nil then
 		local vclick = self.format_hypertext ([[click here]], ITEM_QUALITY_UNCOMMON, 'help')
 		self:ScheduleTimer(function(s)
@@ -1044,6 +1051,25 @@
 	end
 end
 
+-- We don't want to trigger plugins and another addons as soon as something
+-- interesting happens, because a nontrivial amount of work happens "quickly"
+-- after the interesting event:  cleanups/fixups, improvs from network, etc.
+-- So firing a callback is delayed ever so briefly by human standards.
+--
+-- Can't *quite* use the expiring caches for this, but that's okay.
+do
+	local unpack = _G.unpack
+	local function F (t)
+		addon.callbacks:Fire (unpack(t))
+		flib.del(t)
+	end
+	function addon:Fire (...)
+		self.dprint('callback', ...)
+		local capture = flib.new(...)
+		self:ScheduleTimer (F, 1.2, capture)
+	end
+end
+
 
 ------ Event handlers
 function addon:_clear_SVs()
@@ -1129,6 +1155,8 @@
 			end
 		end
 
+		-- XXX somewhere in here, we could fire a useful callback event
+
 		if redo then
 			redo_count = redo_count + 1
 		end
@@ -1607,6 +1635,7 @@
 
 		elseif cmd == "debug" then
 			if arg then
+				self.is_guilded = _G.IsInGuild()
 				self.debug[arg] = not self.debug[arg]
 				_G.print(arg,self.debug[arg])
 				if self.debug[arg] then self.DEBUG_PRINT = true end
@@ -1705,6 +1734,7 @@
 	if opt_threshold then
 		self:SetThreshold (opt_threshold, --[[quiet_p=]]true)
 	end
+	self:Fire ('Activate', self.enabled, self.rebroadcast, self.threshold)
 	self:Print("Now %s; threshold currently %s.",
 		self.enabled and "tracking" or "only broadcasting",
 		self.thresholds[self.threshold])
@@ -1719,6 +1749,7 @@
 	self:UnregisterEvent(RAID_ROSTER_UPDATE_EVENT)
 	self:UnregisterEvent("PLAYER_ENTERING_WORLD")
 	self:UnregisterEvent("CHAT_MSG_LOOT")
+	self:Fire ('Deactivate')
 	self:Print("Deactivated.")
 end
 
@@ -1744,6 +1775,7 @@
 		end
 	end
 	_init(self,st)
+	self:Fire ('Reset')
 	if repopup then
 		addon:BuildMainDisplay()
 	end
@@ -1996,6 +2028,7 @@
 		g_loot = _G.OuroLootSV
 		self.popped = #g_loot > 0
 		self.dprint('flow', "restoring", #g_loot, "entries")
+		_do_loot_metas()
 		self:ScheduleTimer("Activate", 12, opts.threshold)
 		-- FIXME printed could be too large if entries were deleted, how much do we care?
 		self.sharder = opts.autoshard
@@ -2080,7 +2113,7 @@
 			end
 		end
 		bossi = addon._addBossEntry(boss)
-		-- addon.
+		addon:Fire ('NewBoss', boss)
 		bossi = addon._adjustBossOrder (bossi, g_boss_signpost) or bossi
 		g_boss_signpost = nil
 		addon.latest_instance = boss.instance
@@ -2230,17 +2263,45 @@
 do
 	local rawget, setmetatable = _G.rawget, _G.setmetatable
 
-	-- This shouldn't be required.  /sadface
+	--@debug@
+	local tos = {}
+	tos.time = function (e)
+		return e.startday.text
+	end
+	tos.boss = function (e)
+		return e.bossname .. '/' .. e.reason
+	end
+	tos.loot = function (e)
+		return e.itemname .. '/' .. e.person .. '/' .. e.unique .. '/'
+			.. tostring(e.disposition) .. (e.extratext and ('/'..e.extratext) or '')
+	end
+	--@end-debug@
 	local loot_entry_mt = {
 		__index = function (e,key)
+			-- This shouldn't be required, as the refresh should be picking
+			-- it up already.  Sigh.
 			if key == 'cols' then
 				pprint('mt', e.kind, "key is", key)
-				--tabledump(e)  -- not actually that useful
 				addon:_fill_out_eoi_data(1)
 			end
 			return rawget(e,key)
+		end,
+		--@debug@
+		__tostring = function (e)
+			local k = e.kind
+			if k then
+				return ("<%s/%s>"):format(k, tos[k] and tos[k](e) or "?")
+			end
+			return "<unknown loot entry type>"
+		end,
+		--@end-debug@
+	}
+	function _do_loot_metas()
+		for i,e in ipairs(g_loot) do
+			setmetatable(e,loot_entry_mt)
 		end
-	}
+		_do_loot_metas = nil
+	end
 
 	-- Given a loot index, searches backwards for a timestamp.  Returns that
 	-- index and the time entry, or nil if it falls off the beginning.  Pass an
@@ -2326,14 +2387,15 @@
 			if (not e.unique) or (#e.unique==0) then
 				e.unique = e.id .. (e.disposition or e.person) .. _G.date("%Y/%m/%d %H:%M",e.stamp)
 			end
+			addon:Fire ('NewLootEntry', e)
 		end
 		local index = #g_loot + 1
 		g_loot[index] = e
+		addon:Fire ('NewEOIEntry', e)
 		return index
 	end
 
-	-- Safety wrapper only.
-	-- XXX Maybe pprint something here.
+	-- Safety/convenience wrapper only.
 	function addon._addBossEntry (e)
 		local ret = addon._addLootEntry(e)
 		assert(e.kind=='boss')
@@ -2346,6 +2408,7 @@
 			if needSnap then e.raidersnap = ss end
 			if needInst then e.instance = inst end
 		end
+		addon:Fire ('NewBossEntry', e)
 		return ret
 	end
 
@@ -2552,6 +2615,8 @@
 		if self.display then
 			local d = self.display
 			if d then
+				-- Take this down a piece at a time, on the assumption that
+				-- the main window won't be able to do so.
 				local gui = d:GetUserData("GUI state")
 				local eoist = gui.eoiST
 				if eoist then eoist:Hide() end
@@ -2887,6 +2952,7 @@
 		h.count[U] = e.count
 
 		g_uniques[U] = { loot = lootindex, history = e.person }
+		self:Fire ('NewHistory', e.person, U)
 	end
 
 	-- Create new history table based on current loot.
@@ -3104,6 +3170,7 @@
 			self.display:GetUserData("GUI state").eoiST:OuroLoot_Refresh(index)
 			self:redisplay()
 		end
+		self:Fire ('Reassign', unique, id, e, from_name, to_name)
 		return index
 	end
 
@@ -3128,6 +3195,7 @@
 		player.when[u], player.id[u], player.count[u] = nil, nil, nil
 		g_uniques[u] = nil
 		addon.hist_clean = nil
+		addon:Fire ('DelHistory', player.name, u)
 		return #player.unique
 	end
 
@@ -3307,6 +3375,7 @@
 			unique = assert(e.unique)
 			self:vbroadcast('mark', unique, id, olddisp, newdisp)
 		end
+		self:Fire ('MarkAs', unique, id, e, olddisp or 'normal', newdisp or 'normal')
 		return index
 	end
 end
@@ -3347,7 +3416,7 @@
 		self.dprint('comm', "<broadcast>:", msg)
 		self:SendCommMessage(self.ident, msg, "RAID")
 		-- this is what lets us debug our own message traffic:
-		if self.debug.comm then
+		if self.debug.comm and self.is_guilded then
 			self:SendCommMessage(self.ident, msg, "GUILD")
 		end
 	end
--- a/gui.lua	Sun Jul 29 23:12:18 2012 +0000
+++ b/gui.lua	Mon Jul 30 19:25:46 2012 +0000
@@ -899,15 +899,19 @@
 
 	df_DELETE = function(rowi)
 		local gone = tremove (g_loot, rowi)
+		addon:Fire ('DelEOIEntry', gone)
 		addon:Print("Removed %s.",
 			gone.itemlink or gone.bossname or gone.startday.text)
-		if gone.kind == 'loot' and IsShiftKeyDown() then
-			local okay,err = addon:_delHistoryEntry (gone)
-			if okay then
-				addon:Print("Removed history entry %s from %s.",
-					gone.itemlink, addon:colorize(gone.person,gone.person_class))
-			else
-				addon:Print(err)
+		if gone.kind == 'loot' then
+			addon:Fire ('DelLootEntry', gone)
+			if IsShiftKeyDown() then
+				local okay,err = addon:_delHistoryEntry (gone)
+				if okay then
+					addon:Print("Removed history entry %s from %s.",
+						gone.itemlink, addon:colorize(gone.person,gone.person_class))
+				else
+					addon:Print(err)
+				end
 			end
 		end
 	end,
--- a/options.lua	Sun Jul 29 23:12:18 2012 +0000
+++ b/options.lua	Mon Jul 30 19:25:46 2012 +0000
@@ -522,7 +522,12 @@
 				w:SetDisabled(not addon.DEBUG_PRINT)
 			end
 			w:SetValue(v)
-			w:SetCallback("OnValueChanged", function(_w,event,value) addon.debug[d] = value end)
+			w:SetCallback("OnValueChanged", function(_w,event,value)
+				addon.debug[d] = value
+				if d == "comm" then
+					addon.is_guilded = _G.IsInGuild()
+				end
+			end)
 			grp:AddChild(w)
 		end
 		container:AddChild(grp)
@@ -805,7 +810,9 @@
 
 
 --[[
-PLUGIN is the module table itself.
+PLUGIN is the module table itself.  (This does not actually have to be
+  a plugin created with :[Constrained]NewModule, as long as it has a
+  GetName method and other parameters here are used appropriately.)
 PARENT is nil to register in the tree list directly.
 TEXT is either the text to display in the tree list, or nil to use the
   moduleName field out of PLUGIN (not :GetName()).
--- a/verbage.lua	Sun Jul 29 23:12:18 2012 +0000
+++ b/verbage.lua	Mon Jul 30 19:25:46 2012 +0000
@@ -5,6 +5,10 @@
 - Right-clicking the [ouroloot] chat link could do something different than
 the current left-clicking.  Popup small menu in place?
 
+- A fight duration of (0:00) happens when DBM gets confused.  If it happens
+"near" another boss entry with nonzero duration, can we just safely assume
+that's the real entry and delete the zero entry altogether?
+
 - Break more methods out into private routines.
 
 - implement ack, then fallback to recording if not ack'd