view 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 source
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