changeset 125:a9cf9b2fbf9b

- Fix interactions with AddonLoader. - Plugins get a :Print with a clickable prefix, with overrideable behaviors. - 'Deactivate' event gets a [wrapper around] current raiders table. - Fix sorting bug in player history data. - Smarter status line text when viewing unfiltered history. - Do not disable history regeneration button when current history is empty. - :BuildMainDisplay can handle the CLI args now.
author Farmbuyer of US-Kilrogg <farmbuyer@gmail.com>
date Sun, 19 Aug 2012 21:08:59 -0400
parents 1a248faf1a3b
children 9232cacc9136
files core.lua gui.lua options.lua verbage.lua
diffstat 4 files changed, 163 insertions(+), 85 deletions(-) [+]
line wrap: on
line diff
--- a/core.lua	Fri Aug 17 02:48:13 2012 -0400
+++ b/core.lua	Sun Aug 19 21:08:59 2012 -0400
@@ -21,6 +21,8 @@
 -    join       time player joined the raid (or first time we've seen them)
 -    leave      time player left the raid (or time we've left the raid, if
                 'online' is not 'no_longer')
+-    needinfo   true if haven't yet gotten close enough to player to request
+                unit data; SHOULD be missing
 
 Common g_loot entry indices:
 - kind          time/boss/loot
@@ -444,8 +446,10 @@
 	--
 	-- Returns an opaque token and a matching number.  Calling tostring() on
 	-- the token will yield a formatted clickable string that can be displayed
-	-- in chat.  The MethodName and raw function callbacks will both be
-	-- passed the addon table and the same matching number.
+	-- in local chat.  Clicking a tab_title hyperlink opens the GUI to that
+	-- tab; the MethodName and raw function callbacks will be passed the addon
+	-- table, the same matching number, and the mouse button (ala OnClick) as
+	-- arguments.
 	--
 	-- This is largely an excuse to fool around with Lua data constructs.
 	function addon.format_hypertext (text, color, func)
@@ -473,14 +477,15 @@
 		if ltype ~= "OuroLoot" then return end
 		arg = tonumber(arg)
 		local f = func_map[text_map[arg]]
-		if type(f) == 'function' then
-			f (addon, arg)
-		elseif type(f) == 'string' then
+		local t = type(f)
+		if t == 'string' then
 			if type(addon[f]) == 'function' then
-				addon[f](addon,arg)         -- method name
+				addon[f](addon,arg,mousebutton)   -- method
 			else
-				addon:BuildMainDisplay(f)   -- tab title fragment
+				addon:BuildMainDisplay(f)         -- tab title fragment
 			end
+		elseif t == 'function' then
+			f (addon, arg, mousebutton)
 		end
 	end)
 
@@ -754,7 +759,7 @@
 	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...
+	-- Primarily for plugins, but can be of use to me also...  LCALLBACK
 	self.callbacks = LibStub("CallbackHandler-1.0"):New(self)
 	--function self.callbacks:OnUsed (target_aka_self, eventname)  end
 	--function self.callbacks:OnUnused (target_aka_self, eventname)  end
@@ -870,11 +875,12 @@
 	self:RegisterEvent("PLAYER_LOGOUT")
 	self:RegisterEvent(RAID_ROSTER_UPDATE_EVENT,"RAID_ROSTER_UPDATE")
 
-	-- Cribbed from Talented.  I like the way jerry thinks: the first string
-	-- argument can be a format spec for the remainder of the arguments.
-	-- AceConsole:Printf isn't used because we can't specify a prefix without
-	-- jumping through ridonkulous hoops.  The part about overriding :Print
-	-- with a version using prefix hyperlinks is my fault.
+	-- This Print cribbed from Talented.  I like the way jerry thinks: the
+	-- first string argument can be a format spec for the remainder of the
+	-- arguments.  AceConsole:Printf isn't used because we can't specify a
+	-- prefix without jumping through ridonkulous hoops.
+	--
+	-- Everything dealing with a prefix hyperlink is my fault.
 	--
 	-- CFPrint added instead of the usual Print testing of the first arg for
 	-- frame-ness, which would slow down all printing and only rarely be useful.
@@ -1012,7 +1018,10 @@
 	frame creation.
 	]]
 	local bliz = CreateFrame("Frame")
-	bliz.name = "Ouro Loot"
+	bliz.name = "Ouro Loot"  -- must match X-LoadOn-InterfaceOptions
+	if AddonLoader then
+		AddonLoader:RemoveInterfaceOptions(bliz.name)
+	end
 	bliz:SetScript("OnShow", function(_b)
 		local button = CreateFrame("Button",nil,_b,"UIPanelButtonTemplate")
 		button:SetWidth(150)
@@ -1020,9 +1029,9 @@
 		button:SetScript("OnClick", function()
 			InterfaceOptionsFrameCancel:Click()
 			HideUIPanel(GameMenuFrame)
-			addon:OpenMainDisplayToTab"Options"
+			addon:BuildMainDisplay('opt')
 		end)
-		button:SetText('"/ouroloot opt"')
+		button:SetText('"/ouroloot options"')
 		button:SetPoint("TOPLEFT",20,-20)
 		_b:SetScript("OnShow",nil)
 	end)
@@ -1048,32 +1057,67 @@
 	- db:  (OUT) AceDB object, set during init.
 	- opts:  (OUT) Pointer to plugin's "db.profile" subtable.
 
-	OnInitialize, [default_]OnEnable, register_text_generator, register_tab_control
-	are all inherited.
+	Inherited unchanged:
+
+	Inherited module variants:
+	- OnInitialize, OnEnable
+	- register_text_generator, register_tab_control:  also flag plugin as
+		a text-generating module in main addon
 	]]
 	local prototype = {}
-	local registry
+	local textgen_registry, chat_prefixes, chat_codes
 
 	-- By default, no plugins.  First one in sets up code for any after.
-	addon.get_plugin = flib.nullfunc
-
-	-- Fires before the plugin's own OnEnable (inherited or otherwise).
+	addon.get_textgen_plugin = flib.nullfunc
+
+	-- Called as part of NewModule, after embedding and metas are done.
+	-- Mostly this is one-time setup stuff that we don't need until a plugin
+	-- is actually built.
 	function addon:OnModuleCreated (plugin)
-		if not registry then
-			registry = {}
-			addon.get_plugin = function(a,t) return registry[t] end
-			prototype.register_text_generator = function(p,t,...)
-				registry[t] = p
-				return addon:register_text_generator(t,...)
-			end
-			prototype.register_tab_control = function(p,t,...)
-				registry[t] = p
-				return addon:register_tab_control(t,...)
+		textgen_registry, chat_prefixes, chat_codes = {}, {}, {}
+		addon.get_textgen_plugin = function(a,t)
+			return textgen_registry[t]
+		end
+		prototype.register_text_generator = function(p,t,...)
+			textgen_registry[t] = p
+			return addon:register_text_generator(t,...)
+		end
+		prototype.register_tab_control = function(p,t,...)
+			textgen_registry[t] = p
+			return addon:register_tab_control(t,...)
+		end
+
+		function addon:ModulePrefixClick (codenum, mousebutton)
+			local plugin = assert(chat_codes[codenum])
+			if not plugin:IsEnabled() then return end
+			if mousebutton == 'LeftButton' then
+				if plugin.PrefixLeftClick then
+					plugin:PrefixLeftClick(codenum)
+				else
+					self:BuildMainDisplay()
+				end
+			elseif mousebutton == 'RightButton' then
+				local uniqueval = plugin.name
+				if plugin.PrefixRightClick then
+					uniqueval = plugin:PrefixRightClick(codenum)
+				end
+				self:BuildMainDisplay('opt',uniqueval)
 			end
 		end
+		function addon:OnModuleCreated (plugin)
+			local token, code = self.format_hypertext (plugin.moduleName,
+				--[[heirloom]]7, "ModulePrefixClick")
+			chat_prefixes[plugin] = token
+			chat_codes[code] = plugin
+			-- remove the libraries' embedded pointers so that the prototype
+			-- can be inherited
+			plugin.Print = nil
+		end
+
+		return self:OnModuleCreated(plugin)
 	end
 
-	local function module_OnInit (plugin)
+	function prototype.OnInitialize (plugin)
 		if plugin.option_defaults then
 			plugin.db = addon.db:RegisterNamespace (plugin.moduleName, plugin.option_defaults)
 			plugin.opts = plugin.db.profile
@@ -1081,14 +1125,14 @@
 		end
 	end
 
-	local function module_OnEnable (plugin)
-	end
-
-	local function module_GetOption (plugin, info)
+	--function prototype.OnEnable (plugin)
+	--end
+
+	function prototype.GetOption (plugin, info)
 		local name = info[#info]
 		return plugin.db.profile[name]
 	end
-	local function module_SetOption (plugin, info, value)
+	function prototype.SetOption (plugin, info, value)
 		local name = info[#info]
 		plugin.db.profile[name] = value
 		local arg = info.arg
@@ -1097,11 +1141,16 @@
 		end
 	end
 
-	prototype.OnInitialize = module_OnInit
-	prototype.OnEnable = module_OnEnable
-	prototype.default_OnEnable = module_OnEnable
-	prototype.GetOption = module_GetOption
-	prototype.SetOption = module_SetOption
+	-- may eventually just rework the main print routines and inherit all 3
+	function prototype.Print (plugin, str, ...)
+		local AC = LibStub("AceConsole-3.0")
+		local ps = tostring(chat_prefixes[plugin])
+		if type(str) == 'string' and str:find("%", nil, --[[plainmatch=]]true) then
+			return AC:Print (ps, str:format(...))
+		else
+			return AC:Print (ps, str, ...)
+		end
+	end
 
 	addon:SetDefaultModuleLibraries("AceConsole-3.0")
 	addon:SetDefaultModulePrototype(prototype)
@@ -1139,8 +1188,9 @@
     The two boolean predicates are self-explanatory.  The threshold is an
     ITEM_QUALITY_* constant integer.
 
-'Deactivate'
-    After all system events have been unregistered.
+'Deactivate', raiderdata
+    After all system events have been unregistered.  Argument is a holder of
+	the current g_loot.raiders table (in 'raidersnap').
 
 'Reset'
     Clicking "Clear Loot", after all data manipulation is finished.
@@ -1930,7 +1980,10 @@
 	self:UnregisterEvent("PLAYER_ENTERING_WORLD")
 	self:UnregisterEvent("CHAT_MSG_LOOT")
 	_LFR_suppressing = nil
-	self:Fire ('Deactivate')
+	-- Passing .raiders directly doesn't work with a proxy (again, WTB Lua
+	-- 5.2 and its __pairs iterators).  Give it the same structure as a boss
+	-- entry instead.
+	self:Fire ('Deactivate', { raidersnap = g_loot.raiders })
 	self:Print("Deactivated.")
 end
 
@@ -3057,7 +3110,7 @@
 		return L > R    -- reverse of normal order, newest first
 	end
 	local function sort_player (p)
-		local new_uniques, uniques_bywhen, when_array = new(), new(), new()
+		local new_uniques, uniques_bywhen, when_array = {}, new(), new()
 		for u,tstamp in pairs(p.when) do
 			uniques_bywhen[tstamp] = u
 			when_array[#when_array+1] = tstamp
@@ -3067,7 +3120,8 @@
 			new_uniques[i] = uniques_bywhen[tstamp]
 		end
 		p.unique = new_uniques
-		del(new_uniques)  del(uniques_bywhen)  del(when_array)
+		del(uniques_bywhen)
+		del(when_array)
 	end
 
 	function addon:repair_history_integrity()
--- a/gui.lua	Fri Aug 17 02:48:13 2012 -0400
+++ b/gui.lua	Sun Aug 19 21:08:59 2012 -0400
@@ -458,6 +458,10 @@
 	function addon:_fill_out_hist_data (opt_starting_index)
 		local new, del = flib.new, flib.del
 
+		if not self.history.st then
+			self.history.st = {}
+			self.hist_clean = nil
+		end
 		-- Clearing history finishes this function with #hist==0 and hist_clean==0.
 		-- The next call typically detects this (#<1) and handles it.  If loot is
 		-- recorded before then, it results in hist_clean==0 and #hist==1, which
@@ -466,9 +470,6 @@
 			self.hist_clean = nil
 			opt_starting_index = nil
 		end
-		if not self.history.st then
-			self.history.st = {}
-		end
 
 		-- for now
 		if self.hist_clean == #self.history then return end
@@ -1865,12 +1866,12 @@
 		container:AddChild(st_widget)
 		-- If we're focused on one player, but have deleted all entries for
 		-- that player, don't sit there stuck on a blank grid.
-		if history_filter_who and #histST.filtered < 1 then
+		if history_filter_who and #histST.filtered > 0 then
+			setstatus(hist_name_status)
+		else
 			history_filter_who = nil
 			histST:SetFilter(history_filter_by_recent)
 			setstatus(hist_normal_status)
-		else
-			setstatus(hist_name_status)
 		end
 
 		local b
@@ -1907,7 +1908,6 @@
 		b = mkbutton("Regenerate",
 			[[Erases all history entries from the displayed realm, and regenerates it from current loot information.]])
 		b:SetFullWidth(true)
-		b:SetDisabled (#addon.history == 0)
 		b:SetCallback("OnClick", function(_b)
 			local dialog = StaticPopup_Show("OUROL_HIST_REGEN", addon.history.realm)
 			dialog.data = addon
@@ -2070,9 +2070,10 @@
 gui.mkbutton = mkbutton
 
 --[[
-Creates the main window.
+Creates the main window.  Can jump directly to a tab (and feed it CLI
+routine arguments), if given the appropriate tab code.
 ]]
-function addon:BuildMainDisplay (opt_tabselect)
+function addon:BuildMainDisplay (opt_tabselect, opt_tabselect_CLI_arg)
 	if self.display then
 		-- try to get everything to update, rebuild, refresh... ugh, no
 		self.display:Hide()
@@ -2284,26 +2285,30 @@
 	end)
 	tabs:SetCallback("OnTabLeave", statusy_OnLeave)
 	tabs:SetUserData("special buttons group",tab_specials)
-	tabs:SelectTab((opt_tabselect and #opt_tabselect>0)
-		and opt_tabselect or "eoi")
+
+	local whichtab, tabargs = "eoi"
+	if opt_tabselect and #opt_tabselect > 0 then
+		whichtab, tabargs = opt_tabselect, opt_tabselect_CLI_arg
+	end
+	tabs:SelectTab(whichtab)
 
 	display:AddChildren (tabs, control)
 	display:ApplyStatus()
+	display:Show() -- without this, only appears every *other* function call
 
-	display:Show() -- without this, only appears every *other* function call
+	if tabargs and tabs_CLI_special[whichtab] then
+		tabs_CLI_special[whichtab](tabargs)
+	end
 	return display
 end
 
--- Searches tab titles from left to right.
+-- Searches tab title texts from left to right.
 function addon:OpenMainDisplayToTab (text, opt_arg)
 	text = '^'..text:lower()
 	for _,tab in ipairs(gui.taborder) do
 		local v = gui.tabtexts[tab]
 		if v and v.title:lower():find(text) then
-			self:BuildMainDisplay(tab)
-			if opt_arg and tabs_CLI_special[tab] then
-				tabs_CLI_special[tab](opt_arg)
-			end
+			self:BuildMainDisplay (tab, opt_arg)
 			return true
 		end
 	end
@@ -2384,7 +2389,7 @@
 StaticPopupDialogs["OUROL_HIST_REGEN"] = flib.StaticPopup{
 	-- Concatenate this once at load time.  There is no ITEM_QUALITY_LEGENDARY constant.
 	text = "Erase all history entries from " .. ITEM_QUALITY_COLORS[5].hex
-		.. "%s|r, and generate it anew from current loot?",
+		.. "%s|r, and generate it anew from current loot?|n|nNote this obeys the current setting of the 'Suppress history for cross-realm players' option.",
 	button1 = YES,
 	button2 = NO,
 	OnAccept = function (dialog, addon, data2)
--- a/options.lua	Fri Aug 17 02:48:13 2012 -0400
+++ b/options.lua	Sun Aug 19 21:08:59 2012 -0400
@@ -750,6 +750,7 @@
 
 -- widget container status tables (will have things magically appear
 -- inside them that we wish to preserve)
+local options_treegroup -- careful, this is deliberately not preserved
 local status_for_scroll = {}
 local status_for_select = { treewidth = 160 }
 
@@ -800,6 +801,7 @@
 local tabs_OGS = function (container, specials)
 	container:SetLayout("Fill")
 	local left = AceGUI:Create("TreeGroup")
+	options_treegroup = left
 	left:SetStatusTable(status_for_select)
 	left:SetLayout("Fill")
 	left:SetFullWidth(true)
@@ -846,12 +848,18 @@
 	if preload then preload() end
 end
 
+local tabs_cli = function (unique)
+	if not controls[unique] then return end
+	options_treegroup:SetSelected(unique)
+	options_treegroup:SetUserData("options restore scroll", false)
+end
+
 addon:register_tab_control_AT_END ("opt", [[Options]],
 	[[Options for fine-tuning behavior]], tabs_OGS, [[
 The "be chatty" options can be noisy, but they make the
 |cffff8000[Ouro Loot]|r link much more useful.  See
 <Help -- Handy Tips> for details!
-]])
+]], tabs_cli)
 
 
 ---------------
@@ -873,14 +881,18 @@
 --[[
 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
+  'name' field and
   - TEXT is passed
   - if OPTIONS is a table, then OPTIONS.args.name exists
-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()).
+UNIQUE and PARENT control placement in the tree list.  UNIQUE is a unique
+  string, or nil to use the module's unique name (this can only be used once
+  obviously, or it's not "unique").  PARENT is nil to display at the main
+  level of options, or a previously-registered return value to display as a
+  child of that option.
+TEXT is either the text to display in the tree list, or nil to use
+  PLUGIN:GetName().
 OPTIONS is either
-  I)  an aceconfig-style options table
+  I)  an aceconfig-style options table (uses PLUGIN:GetName())
   II) a function to call directly
 
 (I) Will augment options.args.name with coloring/sizing, if it exists.
@@ -893,13 +905,16 @@
 	  the "button" clicked in the lefthand tree list.
 
 Will append a reset button IFF an options database has been registered already.
+
+XXX - the PARENT functionality hasn't been implmented yet cuz I'm lazy
 ]]
-function addon:register_options_entry (plugin, parent, text, options)
-	-- if this changes at all, fix up plugin_reset_button accordingly
-	local code = plugin:GetName()
+function addon:register_options_entry (plugin, unique, parent, text, options)
+	unique = (unique and tostring(unique))
+		or assert(plugin.name, "plugin has no 'name' field")
+
 	--if parent 
 
-	text = text or plugin.moduleName
+	text = text or plugin:GetName()
 
 	local handler
 	local pdb = plugin.moduleName and
@@ -907,10 +922,10 @@
 
 	if type(options) == 'table' then
 		-- AceConfig-style options table
-		aceconfig_list[code] = true
+		aceconfig_list[unique] = true
 		if not options.args.name then
 			options.args.name = {
-				name = plugin.moduleName,
+				name = plugin:GetName(),
 				type = 'description',
 			}
 		end
@@ -948,7 +963,7 @@
 		if pdb then
 			handler = function (sf)
 				sf:AddChildren(mktitle(text))
-				local ret = options (sf, plugin, code)
+				local ret = options (sf, plugin, unique)
 				local w = mkbutton("Reset",
 					([[Reset settings for <%s> plugin back to defaults.  Shift-click to also trigger a UI reload.]]):format(text))
 				w:SetRelativeWidth(0.3)
@@ -962,7 +977,7 @@
 		else
 			handler = function (sf)
 				sf:AddChildren(mktitle(text))
-				return options (sf, plugin, code)
+				return options (sf, plugin, unique)
 			end
 		end
 
@@ -970,19 +985,19 @@
 		error(("Error: 'options' parameter for plugin '%s' is neither table nor function"):format(plugin:GetName()))
 	end
 
-	if not controls[code] then
-		controls[code] = {}
+	if not controls[unique] then
+		controls[unique] = {}
 	end
-	table.insert (controls[code], handler)
+	table.insert (controls[unique], handler)
 
 	table.insert (options_tree, insertion_index, {
-		value = code,
-		text = text,  -- maybe call markup on this?
+		value = unique,
+		text = '++ '..text,  -- maybe call markup on this?
 	})
 	insertion_index = insertion_index + 1
 	-- The treegroup will rescan options_tree (via RefreshTree) several times
 	-- before the player actually sees anything; no more work needs doing.
-	return -- something that can be used as 'parent'
+	return unique -- something that can be used as 'parent'?
 end
 
 
--- a/verbage.lua	Fri Aug 17 02:48:13 2012 -0400
+++ b/verbage.lua	Sun Aug 19 21:08:59 2012 -0400
@@ -9,6 +9,9 @@
 "near" another boss entry with nonzero duration, can we just safely assume
 that's the real entry and delete the zero entry altogether?
 
+- Loading enough plugin tabs to grow a second tab row causes the ST frames
+to be too large.
+
 - Rework candidate groups into the expiring caches.
 
 - Break more methods out into private routines.