diff options.lua @ 96:780b7e0eeeeb

Break the options panel out of gui.lua into new options.lua. Move default item lists from verbage.lua there also. Redo options panel as a tree instead of a massive scrolling thing, and prepare data structures to let plugins/etc add their own options code.
author Farmbuyer of US-Kilrogg <farmbuyer@gmail.com>
date Thu, 26 Jul 2012 20:46:00 +0000
parents
children ba5ff82dcf19
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/options.lua	Thu Jul 26 20:46:00 2012 +0000
@@ -0,0 +1,695 @@
+local addon = select(2,...)
+if addon.NOLOAD then return end
+
+-- Don't bother recording any of this loot:
+addon.default_itemfilter = {
+	-- could probably remove most of this now
+--	[29434]		= true, -- Badge of Justice
+--	[40752]		= true, -- Emblem of Heroism
+--	[40753]		= true, -- Emblem of Valor
+--	[45624]		= true, -- Emblem of Conquest
+--	[43228]		= true, -- Stone Keeper's Shard
+--	[47241]		= true, -- Emblem of Triumph
+--	[49426]		= true, -- Emblem of Frost
+}
+
+-- Mark these as straight to guild vault:
+addon.default_itemvault = {
+	[52078]		= true, -- Chaos Orb
+	[69237]		= true, -- Living Ember
+	[71998]		= true, -- Essence of Destruction
+}
+
+local options_tree = {
+	{
+		value = "pong",
+		text = "Pongs",
+	},
+	{
+		value = "basic",
+		text = "Options",
+		children = {
+			{
+				value = "filter",
+				text = "Item Filters",
+			},
+		},
+	},
+	{
+		value = "adv",
+		text = "Advanced/Debugging",
+	},
+}
+
+
+--[[
+mkbutton ("WidgetType", 'display key', "Text On Widget", "mouseover status text")
+mkbutton ( [Button]     'display key', "Text On Widget", "mouseover status text")
+mkbutton ( [Button]      [text]        "Text On Widget", "mouseover status text")
+]]
+local mkbutton = addon.gui_state_pointer.mkbutton
+local gui = addon.gui_state_pointer
+local AceGUI = LibStub("AceGUI-3.0")
+local flib = LibStub("LibFarmbuyer")
+-- Local ref to OuroLootSV_opts, which may be reassigned after load.
+-- So instead this is updated when the tab is displayed.
+local opts   
+
+local function mktoggle (opt, label, width, desc, opt_func)
+	local w = mkbutton("CheckBoxSmallLabel", nil, "", desc)
+	w:SetRelativeWidth(width)
+	w:SetType("checkbox")
+	w:SetLabel(label)
+	if opt then
+		w:SetValue(opts[opt])
+		w:SetCallback("OnValueChanged", opt_func or (function(_w,event,value)
+			opts[opt] = value
+		end))
+	end
+	return w
+end
+
+local function mktitle (txt)
+	local t = AceGUI:Create("Label")
+	t:SetFullWidth(true)
+	t:SetColor (0.19, 0.68, 1)   -- cff30adff
+	t:SetFontObject(GameFontHighlightLarge)
+	t:SetText(txt)
+	local s = AceGUI:Create("Spacer")
+	s:SetFullWidth(true)
+	s:SetHeight(20)
+	return t, s
+end
+
+local function adv_careful_OnTextChanged (ebox,event,value)
+	-- The EditBox widget's code will call an internal ShowButton routine
+	-- after this callback returns.  ShowButton will test for this flag:
+	ebox:DisableButton (value == "")
+end
+
+
+---------------
+-- All controls take a scrollframe container set to Flow layout.
+local controls = {}
+controls.pong = function (container)
+	container:AddChildren(mktitle[[Echoes from latest ping:]])
+	addon.sender_list.sort()
+	if #addon.sender_list.namesI > 0 then
+		local w = AceGUI:Create("Label")
+		w:SetFullWidth(true)
+		w:SetText(table.concat(addon.sender_list.namesI,'\n')) -- sigh
+		container:AddChild(w)
+	end
+end
+
+
+---------------
+controls.basic = function (container)
+	container:AddChildren(mktitle[[Account Options (saved across sessions)]])
+
+	container:PauseLayout()
+	local w
+	local stdw = 0.99    -- standard width
+
+	-- the nubtoggle!
+	w = mktoggle('gui_noob', [[Show UI Tips]], stdw,
+		[[Toggles display of the "helpful tips" box hanging off the right side.  Useful if you've just installed/upgraded.]])
+	container:AddChild(w)
+
+	-- reminder popup
+	w = mktoggle('popup_on_join', "Show reminder popup on new raid", stdw,
+		[[When joining a raid and not already tracking, display a dialog asking for instructions.]])
+	container:AddChild(w)
+
+	-- toggle scroll-to-bottom on first tab
+	w = mktoggle('scroll_to_bottom', "Scroll to bottom when opening display", stdw,
+		[[Scroll to the bottom of the loot window (most recent entries) when displaying the GUI.]])
+	container:AddChild(w)
+
+	-- chatty boss mode
+	w = mktoggle('chatty_on_kill', "Be chatty on boss kill", stdw,
+		[[Print something to chat output when the boss mod tells Ouro Loot about a successful boss kill.]])
+	container:AddChild(w)
+
+	-- less noise in main panel
+	w = mktoggle('no_tracking_wipes', "Do not track wipes", stdw,
+		[[Do not add 'wipe' entries on the main loot grid, or generate any text for them.]])
+	container:AddChild(w)
+
+	-- cutesy abbrevs
+	w = mktoggle('snarky_boss', "Use snarky boss names", stdw,
+		[[Irreverent replacement names for boss events.  See abbreviations.lua for details.]])
+	container:AddChild(w)
+
+	-- LOD plugins in all cases
+	w = mktoggle('display_disabled_LODs', "Include disabled plugins", stdw,
+		[[Show loadable plugins even if they've been disabled (and offer to enable them).  Relog to take effect.]])
+	container:AddChild(w)
+
+	-- showing the "(from Rebroadcasterdude)" in the notes column
+	w = mktoggle('display_bcast_from', "Show rebroadcasting player", stdw,
+		[[Include "from PlayerName" in the Notes column for loot that was broadcast to you.  (Not included in forum output).]],
+		function(_w,_e,value)
+			opts.display_bcast_from = value
+			addon.loot_clean = nil
+		end)
+	container:AddChild(w)
+
+	-- prefilling g_uniques with history
+	w = mktoggle('precache_history_uniques', "Prescan for faster handling", stdw,
+		[[See description under +Help -- Handy Tips -- Prescanning> for instructions.]])
+	container:AddChild(w)
+
+	w = AceGUI:Create("Spacer") w:SetFullWidth(true) w:SetHeight(10) container:AddChild(w)
+	-- possible keybindings
+	do
+		local pair = AceGUI:Create("InlineGroup")
+		pair:SetLayout("List")
+		pair:SetRelativeWidth(0.49)
+		pair:SetTitle("Keybinding for '/ouroloot'")
+		local editbox, checkbox
+		editbox = mkbutton("EditBox", nil, opts.keybinding_text,
+			[[Keybinding text format is fragile!  (All caps, ALT then CTRL then SHIFT.)  Relog to take effect.]])
+		editbox:SetFullWidth(true)
+		editbox:SetLabel("Keybinding text")
+		editbox:SetCallback("OnEnterPressed", function(_w,event,value)
+			opts.keybinding_text = value
+		end)
+		editbox:SetDisabled(not opts.keybinding)
+		checkbox = mktoggle('keybinding', "Register keybinding", 1,
+			[[Register a keybinding to toggle the loot display.  Relog to take effect.]],
+			function (_w,_,value)
+				opts.keybinding = value
+				editbox:SetDisabled(not opts.keybinding)
+			end)
+		checkbox:SetFullWidth(true)
+		pair:AddChild(checkbox)
+		pair:AddChild(editbox)
+		container:AddChild(pair)
+	end
+
+	-- replacement for slashloot
+	do
+		local pair = AceGUI:Create("InlineGroup")
+		pair:SetLayout("List")
+		pair:SetRelativeWidth(0.49)
+		pair:SetTitle('Synonyms for "/ouroloot"')
+		local editbox, checkbox
+		editbox = mkbutton("EditBox", nil, opts.slash_synonyms,
+			[[Separate multiple synonyms with a comma.  Relog to take effect.]])
+		editbox:SetFullWidth(true)
+		editbox:SetLabel("Slash commands")
+		editbox:SetCallback("OnEnterPressed", function(_e,event,value)
+			-- Do the sanity checking here rather than at each login.
+			-- This is not foolproof.  That's okay.
+			local t = { strsplit(',', tostring(value)) }
+			for k,v in ipairs(t) do
+				v = v:trim()
+				if v:sub(1,1) ~= "/" then
+					v = "/" .. v
+				end
+				t[k] = v
+			end
+			value = table.concat(t,',')
+			_e:SetText(value)
+			opts.slash_synonyms = value
+		end)
+		editbox:SetDisabled(not opts.register_slash_synonyms)
+		checkbox = mktoggle('register_slash_synonyms', "Register slash commands", 1,
+			[[Register these slash commands as synonyms for "/ouroloot".  Relog to take effect.]],
+			function (_w,_,value)
+				opts.register_slash_synonyms = value
+				editbox:SetDisabled(not opts.register_slash_synonyms)
+			end)
+		checkbox:SetFullWidth(true)
+		pair:AddChild(checkbox)
+		pair:AddChild(editbox)
+		container:AddChild(pair)
+	end
+
+	-- chatty disposition/assignment changes
+	w = AceGUI:Create("Spacer") w:SetFullWidth(true) w:SetHeight(10) container:AddChild(w)
+	do
+		local chatgroup = AceGUI:Create("InlineGroup")
+		chatgroup:SetLayout("List")
+		chatgroup:SetRelativeWidth(0.75)
+		chatgroup:SetTitle("Remote Changes Chat")
+		local toggle, editbox
+		toggle = mktoggle('chatty_on_remote_changes', "Be chatty on remote changes", 1,
+			[[Print something to chat when other users change recorded loot.]],
+			function (_w,_,value)
+				opts.chatty_on_remote_changes = value
+				editbox:SetDisabled(not opts.chatty_on_remote_changes)
+			end)
+		toggle:SetFullWidth(true)
+		chatgroup:AddChild(toggle)
+		w = AceGUI:Create("Label")
+		w:SetFullWidth(true)
+		w:SetText("This controls the output of the |cff00ffff'Be chatty on remote changes'|r option.  If this field is a number, it designates which chat frame to use.  Otherwise it is the Lua variable name of a frame with AddMessage capability.")
+		chatgroup:AddChild(w)
+		editbox = mkbutton("EditBox", nil, opts.chatty_on_remote_changes_frame,
+			[[1 = default chat frame, 2 = combat log, etc]])
+		editbox:SetFullWidth(true)
+		editbox:SetLabel("Output Chatframe")
+		editbox:SetCallback("OnTextChanged", adv_careful_OnTextChanged)
+		editbox:SetCallback("OnEnterPressed", function(_w,event,value)
+			local prev = opts.chatty_on_remote_changes_frame
+			value = value:trim()
+			value = tonumber(value) or value
+			if addon:_set_remote_change_chatframe (value) then
+				opts.chatty_on_remote_changes_frame = value
+				_w:SetText(tostring(value))
+				_w.editbox:ClearFocus()
+			else
+				_w:SetText(tostring(prev))
+			end
+		end)
+		editbox:SetDisabled(not opts.chatty_on_remote_changes)
+		chatgroup:AddChild(editbox)
+		w = mkbutton("Chat Frame Numbers",
+			[[Print each chat window number in its own frame, for easy reference in the editing field.]])
+		w:SetFullWidth(true)
+		w:SetCallback("OnClick", function()
+			for i = 1, NUM_CHAT_WINDOWS do
+				local cf = _G['ChatFrame'..i]
+				if not cf then break end
+				addon:CFPrint (cf, "This is frame number |cffff0000%d|r.", i)
+			end
+		end)
+		chatgroup:AddChild(w)
+		container:AddChild(chatgroup)
+	end
+
+	-- boss mod selection
+	w = AceGUI:Create("Spacer") w:SetFullWidth(true) w:SetHeight(2) container:AddChild(w)
+	do
+		local list = {}
+		local current
+		for k,v in ipairs(addon.bossmods) do
+			list[k] = v.n
+			if v.n == opts.bossmod then
+				current = k
+			end
+		end
+		w = mkbutton("Dropdown", nil, "", [[Which 'boss mod' to use.]])
+		w:SetRelativeWidth(0.3)
+		w:SetLabel("Boss Mod:")
+		w:SetList(list)
+		w:SetValue(current)
+		w:SetCallback("OnValueChanged", function(_w,event,choice)
+			opts.bossmod = list[choice]
+		end)
+		container:AddChild(w)
+	end
+
+	container:ResumeLayout()
+	container:DoLayout()
+	AceGUI:ClearFocus()
+end
+
+
+---------------
+do
+	local warntext = [[At least one of the items in the filter list was not in your game client's cache.  This is okay.  Just wait a few seconds, display some other Ouro Loot tab or panel, and then display the Item Filters again.]]
+	local cache_warn, cache_warned = false, false
+	local function do_warning (cnt)
+		if cache_warn and not cache_warned then
+			cache_warned = true
+			addon:Print(warntext)
+			local t = AceGUI:Create("Label")
+			t:SetFullWidth(true)
+			t:SetText(warntext)
+			cnt:AddChild(t)
+		end
+	end
+
+	controls.basic_filter = function (container)
+		container:AddChildren(mktitle[[Item-Specific Special Handling]])
+
+		cache_warn, cache_warned = false, false
+		local filterlist, vaultlist = {}, {}
+		for id in pairs(opts.itemfilter) do
+			local iname, _, iquality = GetItemInfo(id)
+			if iname then
+				filterlist[id] = ITEM_QUALITY_COLORS[iquality].hex .. iname .. "|r"
+			else
+				filterlist[id] = id
+				cache_warn = true
+			end
+		end
+		for id in pairs(opts.itemvault) do
+			local iname, _, iquality = GetItemInfo(id)
+			if iname then
+				vaultlist[id] = ITEM_QUALITY_COLORS[iquality].hex .. iname .. "|r"
+			else
+				vaultlist[id] = id
+				cache_warn = true
+			end
+		end
+
+		local w = AceGUI:Create("EditBoxDropDown")
+		w:SetRelativeWidth(0.4)
+		w:SetText("Item filter")
+		w:SetEditBoxTooltip("Link items which should no longer be tracked.")
+		w:SetList(filterlist)
+		w:SetCallback("OnTextEnterPressed", function(_w, _, text)
+			local iname, ilink, iquality = GetItemInfo(text:trim())
+			if not iname then
+				return addon:Print("Error:  %s is not a valid item name/link!", text)
+			end
+			local id = tonumber(ilink:match("item:(%d+)"))
+			filterlist[id] = ITEM_QUALITY_COLORS[iquality].hex .. iname .. "|r"
+			opts.itemfilter[id] = true
+			addon:Print("Now filtering out", ilink)
+		end)
+		w:SetCallback("OnListItemClicked", function(_w, _, key_id, val_name)
+			--local ilink = select(2,GetItemInfo(key_id))
+			opts.itemfilter[tonumber(key_id)] = nil
+			--addon:Print("No longer filtering out", ilink)
+			addon:Print("No longer filtering out", val_name)
+		end)
+		--w:SetCallback("OnDropdownShown",do_warning)
+		w:SetCallback("OnDropdownShown", function()
+			do_warning(container)
+		end)
+		container:AddChild(w)
+
+		w = AceGUI:Create("Spacer")
+		w:SetRelativeWidth(0.1)
+		w:SetHeight(2)
+		container:AddChild(w)
+
+		w = AceGUI:Create("EditBoxDropDown")
+		w:SetRelativeWidth(0.4)
+		w:SetText("Vault items")
+		w:SetEditBoxTooltip("Link items which should be automatically marked as guild vault.")
+		w:SetList(vaultlist)
+		w:SetCallback("OnTextEnterPressed", function(_w, _, text)
+			local iname, ilink, iquality = GetItemInfo(text:trim())
+			if not iname then
+				return addon:Print("Error:  %s is not a valid item name/link!", text)
+			end
+			local id = tonumber(ilink:match("item:(%d+)"))
+			vaultlist[id] = ITEM_QUALITY_COLORS[iquality].hex .. iname .. "|r"
+			opts.itemvault[id] = true
+			addon:Print("Now auto-vaulting", ilink)
+		end)
+		w:SetCallback("OnListItemClicked", function(_w, _, key_id, val_name)
+			--local ilink = select(2,GetItemInfo(key_id))
+			opts.itemfilter[tonumber(key_id)] = nil
+			--addon:Print("No longer filtering out", ilink)
+			addon:Print("No longer auto-vaulting", val_name)
+		end)
+		w:SetCallback("OnDropdownShown",do_warning)
+		container:AddChild(w)
+	end
+end
+
+
+---------------
+local adv_real = function (container)
+	container:AddChildren(mktitle[[Debugging Options (not saved across sessions)]])
+
+	container:PauseLayout()
+	local w
+
+	do
+		local grp = AceGUI:Create("InlineGroup")
+		grp:SetLayout("List")
+		grp:SetRelativeWidth(0.60)
+		grp:SetTitle("Output of debugging messages")
+
+		w = AceGUI:Create("CheckBoxSmallLabel")
+		w:SetFullWidth(true)
+		w:SetType("checkbox")
+		w:SetLabel("master toggle")
+		w:SetValue(addon.DEBUG_PRINT)
+		w:SetCallback("OnValueChanged", function(_w,event,value)
+			addon.DEBUG_PRINT = value
+			addon:redisplay()
+		end)
+		grp:AddChild(w)
+		for d,v in pairs(addon.debug) do
+			w = AceGUI:Create("CheckBoxSmallLabel")
+			w:SetFullWidth(true)
+			w:SetType("checkbox")
+			w:SetLabel(d)
+			if d == "notraid" then
+				w:SetDescription[[Tick this before enabling to make the addon work outside of raid groups]]
+			else
+				if d == "alsolog" then
+					w:SetDescription[[Also log all debug messages to disk. See print_log.lua in the addon folder for later viewing.]]
+				end
+				w:SetDisabled(not addon.DEBUG_PRINT)
+			end
+			w:SetValue(v)
+			w:SetCallback("OnValueChanged", function(_w,event,value) addon.debug[d] = value end)
+			grp:AddChild(w)
+		end
+		container:AddChild(grp)
+	end
+
+	do
+		local simple = AceGUI:Create("SimpleGroup")
+		simple:SetLayout("List")
+		simple:SetRelativeWidth(0.35)
+		w = AceGUI:Create("CheckBoxSmallLabel")
+		--w:SetRelativeWidth(0.35)
+		w:SetFullWidth(true)
+		w:SetType("checkbox")
+		w:SetLabel("GOP history mode")
+		w:SetValue(addon.history_suppress)
+		w:SetCallback("OnValueChanged", function(_w,event,value) addon.history_suppress = value end)
+		simple:AddChild(w)
+		w = mkbutton("Dropdown", nil, "",
+			[[if active, tooltip shown when hovering over Item column only]])
+		--w:SetRelativeWidth(0.4)
+		w:SetFullWidth(true)
+		w:SetLabel("loot debugging tooltip")
+		w:SetList{
+			[1] = "Off",
+			[2] = "/dump into tooltip",
+			[3] = "small fixed fields",
+		}
+		w:SetValue(gui._do_debugging_tooltip or 1)
+		w:SetCallback("OnValueChanged", function(_w,event,choice)
+			gui._do_debugging_tooltip = choice > 1 and choice or nil
+		end)
+		simple:AddChild(w)
+		container:AddChild(simple)
+	end
+
+	w = AceGUI:Create("Spacer") w:SetFullWidth(true) w:SetHeight(10) container:AddChild(w)
+
+	w = mkbutton("EditBox", 'comm_ident', addon.ident,
+		[[Set tracking to 'Disabled' in the top-right dropdown, then change this field (click Okay or press Enter).]])
+	w:SetRelativeWidth(0.25)
+	w:SetLabel("Addon channel ID")
+	w:SetCallback("OnTextChanged", adv_careful_OnTextChanged)
+	w:SetCallback("OnEnterPressed", function(_w,event,value)
+		-- if they set it to blank spaces, they're boned.  oh well.
+		-- Re-enabling will take care of propogating this new value.
+		addon.ident = (value == "") and "OuroLoot2" or value
+		_w:SetText(addon.ident)
+		addon:Print("Addon channel ID set to '".. addon.ident.. "' for rebroadcasting and listening.")
+	end)
+	w:SetDisabled(addon.enabled or addon.rebroadcast)
+	container:AddChild(w)
+
+	w = mkbutton("EditBox", nil, addon.recent_messages.ttl, [[comm cache TTL]])
+	w:SetRelativeWidth(0.1)
+	w:SetLabel("ttl")
+	w:SetCallback("OnTextChanged", adv_careful_OnTextChanged)
+	w:SetCallback("OnEnterPressed", function(_w,event,value)
+		value = tonumber(value) or addon.recent_messages.ttl
+		addon.recent_messages.ttl = value
+		_w:SetText(tostring(value))
+	end)
+	container:AddChild(w)
+
+	w = mkbutton("load nsaab1548", [[Cursed Darkhound]])
+	w:SetRelativeWidth(0.25)
+	w:SetCallback("OnClick", function()
+		for i, v in ipairs(DBM.AddOns) do
+			if v.modId == "DBM-NotScaryAtAll" then
+				DBM:LoadMod(v)
+				break
+			end
+		end
+		local mod = DBM:GetModByName("NotScaryAtAll")
+		if mod then
+			mod:EnableMod()
+			addon:Print("Now tracking ID",mod.creatureId)
+		else
+			addon:Print("Can do nothing; DBM testing mod wasn't loaded.")
+		end
+	end)
+	w:SetDisabled(addon.bossmod_registered ~= 'DBM')
+	container:AddChild(w)
+
+	w = mkbutton("GC", [[full GC cycle]])
+	w:SetRelativeWidth(0.2)
+	w:SetCallback("OnClick", function()
+		local before = collectgarbage('count')
+		collectgarbage('collect')
+		local after = collectgarbage('count')
+		addon:Print("Collected %d KB, %d KB still in use by Lua universe.", before-after, after)
+	end)
+	container:AddChild(w)
+
+	w = AceGUI:Create("Spacer") w:SetFullWidth(true) w:SetHeight(3) container:AddChild(w)
+	do
+		local simple = AceGUI:Create("SimpleGroup")
+		simple:SetLayout("Flow")
+		--simple:SetRelativeWidth(0.95)
+		simple:SetFullWidth(true)
+		w = mkbutton("MidS-H", [[not exactly an Easter egg, with sound]])
+		w:SetRelativeWidth(0.2)
+		w:SetCallback("OnClick", function() 
+			PlaySoundFile ([[Sound\Music\WorldEvents\HordeFirepole.mp3]], "Master")
+		end)
+		simple:AddChild(w)
+		w = mkbutton("MidS-A", [[not exactly an Easter egg, with sound]])
+		w:SetRelativeWidth(0.2)
+		w:SetCallback("OnClick", function() 
+			PlaySoundFile ([[Sound\Music\WorldEvents\AllianceFirepole.mp3]], "Master")
+		end)
+		simple:AddChild(w)
+		w = mkbutton("SFRR", [[test]])
+		w:SetRelativeWidth(0.15)
+		w:SetCallback("OnClick", function() 
+			PlaySoundFile ([[Interface\AddOns\Ouro_Loot\sfrr.ogg]], "Master")
+		end)
+		simple:AddChild(w)
+
+		container:AddChild(simple)
+	end
+
+	w = AceGUI:Create("Spacer") w:SetRelativeWidth(0.65) w:SetHeight(15) container:AddChild(w)
+	w = mkbutton("Clear All & Reload",
+		[[No confirmation!  |cffff1010Erases absolutely all> Ouro Loot saved variables and reloads the UI.]])
+	w:SetRelativeWidth(0.3)
+	w:SetCallback("OnClick", function()
+		addon:_clear_SVs()  -- reloads
+	end)
+	container:AddChild(w)
+
+	container:ResumeLayout()
+	container:DoLayout()
+	AceGUI:ClearFocus()
+	--container:SetScroll(1000)   -- scrollframe widget's max value
+end
+
+-- Initial advanced panel function (unless debug mode is on during load, which
+-- means it was almost certainly hardcoded that way, which means it's probably
+-- me testing).
+if false and addon.DEBUG_PRINT then
+	controls.adv = adv_real
+else
+	controls.adv = function (container)
+		local speedbump = AceGUI:Create("InteractiveLabel")
+		speedbump:SetFullWidth(true)
+		speedbump:SetFontObject(GameFontHighlightLarge)
+		speedbump:SetImage[[Interface\DialogFrame\DialogAlertIcon]]
+		speedbump:SetImageSize(50,50)
+		speedbump:SetText[[The debugging/testing settings on the advanced panel can seriously bork up the addon if you make a mistake.  If you're okay with the possibility of losing data, click this warning to load the panel.]]
+		speedbump:SetCallback("OnClick", function (_sb)
+			controls.adv = { adv_real }
+			return addon:redisplay()
+		end)
+		container:AddChild(speedbump)
+	end
+end
+
+
+---------------
+-- Tab 6:  Options
+do
+	local funkified = {}
+	for key,f in pairs(controls) do
+		-- this is how TreeGroup makes unique keys
+		local funkykey = key:gsub('_','\001')
+		funkified[funkykey] = { f }
+	end
+	controls = funkified
+end
+
+-- widget container status tables (will have things magically appear
+-- inside them that we wish to preserve)
+local status_for_scroll = {}
+local status_for_select = { treewidth = 160 }
+
+-- Clicking an entry on the left tree column.
+local opt_OnGroupSelected_func = function (treeg,event,category)
+	local catfuncs = controls[category]
+	if not catfuncs then
+		addon:horrible_horrible_error(("Category '%s' has no handler function!"):format(category:gsub('\001','_')))
+	end
+	treeg:ReleaseChildren()
+	local sf = AceGUI:Create("ScrollFrame")
+	sf:SetStatusTable(status_for_scroll)
+	sf:SetLayout("Flow")
+	-- This forces the scrolling area to be bigger than the visible area; else
+	-- some of the text gets cut off without ever triggering the scrollbar.
+	sf.content:SetHeight(700)
+	for _,func in ipairs(catfuncs) do
+		if func(sf) then break end
+	end
+	treeg:AddChild(sf)
+	if treeg:GetUserData("options restore scroll") then
+		if status_for_scroll.scrollvalue then
+			sf:SetScroll(status_for_scroll.scrollvalue)
+		end
+		treeg:SetUserData("options restore scroll", false)
+	else
+		sf:SetScroll(0)
+	end
+end
+
+-- Clicking the Options tab as a whole (tabs_OnGroupSelected["opt"]).
+local tabs_OGS = function (container, specials)
+	opts = OuroLootSV_opts
+
+	container:SetLayout("Fill")
+	local left = AceGUI:Create("TreeGroup")
+	left:SetStatusTable(status_for_select)
+	left:SetLayout("Fill")
+	left:SetFullWidth(true)
+	left:SetFullHeight(true)
+	left:EnableButtonTooltips(false)
+	left:SetTree(options_tree)
+	left:SetCallback("OnGroupSelected", opt_OnGroupSelected_func)
+	container:AddChild(left)
+	if status_for_select.selected then
+		left:SetUserData("options restore scroll", true)
+		left:SelectByValue(status_for_select.selected)
+	else
+		left:SelectByValue("basic")
+	end
+
+	local w = mkbutton("ReloadUI",
+		[[Does what you think it does.  Loot information is written out and restored.]])
+	w:SetFullWidth(true)
+	w:SetCallback("OnClick", ReloadUI)
+	specials:AddChild(w)
+
+	w = mkbutton("Ping!",
+		[[Queries other raid users for their addon version and current status.  Results displayed on Pongs panel after five seconds.]])
+	w:SetFullWidth(true)
+	w:SetCallback("OnClick", function(_w)
+		_w:SetText("5... 4... 3...")
+		_w:SetDisabled(true)
+		addon:DoPing()
+		addon:ScheduleTimer(function(b)
+			if b:IsVisible() then
+				return addon:redisplay()
+			end
+		end, 5, _w)
+	end)
+	specials:AddChild(w)
+end
+
+addon:register_tab_control_AT_END ("opt", [[Options]],
+	[[Options for fine-tuning behavior]], tabs_OGS)
+
+-- vim:noet