view options.lua @ 156:1e2ee3f52bc8 beta-l10n-2

Stub files to test server-side localization generator. These are NOT LOADED YET.
author Farmbuyer of US-Kilrogg <farmbuyer@gmail.com>
date Sat, 21 Feb 2015 17:04:00 -0500
parents 823a987ac492
children
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 = {
	[52190] = true, -- Inferno Ruby
	[52191] = true, -- Ocean Sapphire
	[52192] = true, -- Dream Emerald
	[52193] = true, -- Ember Topaz
	[52194] = true, -- Demonseye
	[52195] = true, -- Amberjewel
	[52722] = true, -- Maelstrom Crystal
	[71716] = true, -- Soothsayer's Runes
	[74247] = true, -- Ethereal Shard
	[74248] = true, -- Sha Crystal
	[113588] = true, -- Temporal Crystal
	[115504] = true, -- Fractured Temporal Crystal

	-- 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  (Cata heroics)
	[69237] = true, -- Living Ember  (Cata Firelands)
	[71998] = true, -- Essence of Destruction  (Cata Dragon Soul)
}

local options_tree = {
	{
		value = "pong",
		text = "Pongs",
	},
	{
		value = "basic",
		text = "Main Options",
		children = {
			{
				value = "filter",
				text = "Item Filters",
			},
			{
				value = "profiles",
				text = "Profiles",
			},
		},
	},
	'NEW INSERTIONS HERE',  -- commenting this puts them all at the bottom
	{
		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 opts

local error, assert = addon.error, addon.assert

local function mktoggle (opt, label, width, desc, opt_func)
	local w = mkbutton("CheckBox", 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
			addon.loot_clean = nil
			addon.hist_clean = nil
		end))
	end
	return w
end

local function spacer (height)
	local s = AceGUI:Create("Spacer")
	s:SetFullWidth(true)
	s:SetHeight(height)
	return s
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)
	return t, spacer(20)
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
	local experimental =
		[[|TInterface\Scenarios\ScenarioIcon-Boss:0|t]]
		.. '|cff00ff00Experimental!  '

	-- 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.]])
	w:SetImage[[Interface\OptionsFrame\UI-OptionsFrame-NewFeatureIcon]]
	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)

	-- (try to) track bonus loot
	-- For the texture coordinates, see EncounterJournal_SetFlagIcon() and
	-- http://wow.gamepedia.com/API_EJ_GetSectionInfo
	w = mktoggle('track_bonusrolls', "Track loot from bonus rolls", stdw,
		[[Track items received from spending roll tokens.]]
		.. experimental
		.. [[Might only see your own bonus loot, unless rebroadcasted.|r]])
	--w:SetImage([[Interface\EncounterJournal\UI-EJ-Icons]],
		-- This is "ID 6", the interruptible lightning icon
		--0.75, (0.75 + (1/8)), 0, 0.5)
		-- This is "ID 7", the heroic/skull
		--3/8, 0.5, 0, 0.5)
	w:SetImage([[Interface\Scenarios\ScenarioIcon-Boss]])
	container:AddChild(w)

	-- track receiving arbitrary items
	w = mktoggle('track_nonloot', "Track non-loot items", stdw,
		[[Track items received from the mailbox, salvage bags, quests, lockboxes...]])
	container:AddChild(w)

	-- auto-GOP mode when in LFR
	w = mktoggle('history_suppress_LFR', "Suppress history in LFR", stdw,
		[[Do not record anything at all in the History tab while in an LFR raid.  Changes only take effect outside of LFR.]])
	container:AddChild(w)

	-- ignore cross realm players in history
	w = mktoggle('history_ignore_xrealm', "Suppress history for cross-realm players",
		stdw,
		[[Do not record anything in the History tab for players from other realms.]])
	container:AddChild(w)

	do
		-- LOD plugins unloadable for reasons other than disabling
		local w_un = mktoggle('display_unusable_LODs', "Include unusable plugins", stdw,
			[[Show plugins even if they are flagged for reasons besides simply being disabled.  Relog to take effect.]])

		-- LOD plugins disabled in the addons menu
		w = mktoggle('display_disabled_LODs', "Include disabled plugins", stdw,
			[[Show loadable plugins even if they've been disabled (and offer to enable them if clicked).]],
			function (_w,_,value)
				opts.display_disabled_LODs = value
				w_un:SetDisabled(not opts.display_disabled_LODs)
				addon.loot_clean = nil
				addon.hist_clean = nil
				addon:_gui_add_disabled_LOD_tabs()
			end)

		w_un:SetDisabled(not opts.display_disabled_LODs)
		container:AddChild(w)
		container:AddChild(w_un)
	end

	-- 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)

	container:AddChild(spacer(10))
	-- possible keybindings
	do
		local pair = AceGUI:Create("InlineGroup")
		pair:SetLayout("List")
		pair:SetRelativeWidth(0.49)
		pair:SetTitle('Keybinding for "/ouroloot toggle"')
		local checkbox, button
		checkbox = mktoggle('keybinding', "Register keybinding", 1,
			[[Whether to register a keybinding to toggle the loot display.  Relog to take effect.]],
			function (_w,_,value)
				opts.keybinding = value
				button:SetDisabled(not opts.keybinding)
			end)
		checkbox:SetFullWidth(true)
		button = mkbutton("Keybinding", nil, "",
			-- This wording is not quite accurate, but auto-l10n is nice.
			_G.BIND_KEY_TO_COMMAND:format([['/ouroloot toggle']]))
		button:SetFullWidth(true)
		--button:SetLabel("this is label text")
		button:SetKey(opts.keybinding_text)
		button:SetCallback("OnKeyChanged", function(_b,event,value)
			local possible_prev = opts.keybinding_text
			opts.keybinding_text = value
			-- If they were already binding something at login, try updating.
			if not _G.OuroLootBindingOpen then return end
			if possible_prev and #possible_prev > 0 then
				addon:Print("Trying to unbind previous %s key.", possible_prev)
				SetBinding (possible_prev, nil)
			end
			if value and #value > 0 then
				addon:Print("Trying to bind new %s key.", value)
				if SetBindingClick (value, "OuroLootBindingOpen") then
					local c = GetCurrentBindingSet()
					if c == ACCOUNT_BINDINGS or c == CHARACTER_BINDINGS then
						SaveBindings(c)
					end
				end
			end
		end)
		pair:AddChildren (checkbox, button)
		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
	container:AddChild(spacer(10))
	do
		local chatgroup = AceGUI:Create("InlineGroup")
		chatgroup:SetLayout("List")
		chatgroup:SetRelativeWidth(0.75)
		chatgroup:SetTitle("Notify on 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) and
					(not opts.chatty_on_local_changes))
			end)
		toggle:SetFullWidth(true)
		chatgroup:AddChild(toggle)
		toggle = mktoggle('chatty_on_local_changes', "Be chatty on your own changes", 1,
			[[Print something to chat when you change recorded loot (as a reminder).]],
			function (_w,_,value)
				opts.chatty_on_local_changes = value
				editbox:SetDisabled((not opts.chatty_on_remote_changes) and
					(not opts.chatty_on_local_changes))
			end)
		toggle:SetFullWidth(true)
		chatgroup:AddChild(toggle)

		w = AceGUI:Create("Label")
		w:SetFullWidth(true)
		w:SetText("This controls the output of the two |cff00ffff'Be chatty on <X> changes'|r options.  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_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_changes_frame
			value = value:trim()
			value = tonumber(value) or value
			if addon:_set_chatty_change_chatframe (value) then
				opts.chatty_on_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) and
			(not opts.chatty_on_local_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
	container:AddChild(spacer(2))
	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 description = [[These control automatic handling of specific items when they are looted.

"Item filter" is a list of items to ignore.  "Vault items" is a list of items to mark as going to the guild vault (as if you had chosen "Mark as guild vault" from the Loot tab).

Proper use of these lists can avoid a lot of noise and tedious clicking during or after a raid.  You will probably need to add/remove entries as you advance between expansions and tiers.]]
	local defaultstext = [[Clicking this button adds some reasonable defaults to your item lists.  (This was done once automatically, the first time you loaded Ouro Loot, but you can delete them at any time.)]]

	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]])

		local w = AceGUI:Create("Label")
		w:SetFullWidth(true)
		w:SetText(description)
		container:AddChild(w)
		container:AddChild(spacer(20))

		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
		-- This is so that the "add all defaults" button will never, ever,
		-- trip over cache misses.
		for id in pairs(addon.default_itemfilter) do GetItemInfo(id) end
		for id in pairs(addon.default_itemvault) do GetItemInfo(id) end

		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", 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)

		local function MAYBE_ADD (destname, dest, id)
			local name, link = GetItemInfo(id)
			if not name then
				addon:Print("> No data on ID %d, this may be a bug.",id)
				return
			end
			if dest[id] then
				addon:Print("> Skipping %s as it's already in '%s' list.",
					link, destname)
			else
				dest[id] = true
				addon:Print("> Added %s to '%s' list.", link, destname)
			end
		end

		container:AddChild(spacer(40))
		w = AceGUI:Create("Label")
		w:SetFullWidth(true)
		w:SetText(defaultstext)
		container:AddChild(w)
		w = AceGUI:Create("Button")
		w:SetRelativeWidth(0.4)
		w:SetText[[Add Default Entries]]
		w:SetCallback("OnClick", function()
			for id in pairs(addon.default_itemfilter) do
				MAYBE_ADD ("filter", opts.itemfilter, id)
			end
			for id in pairs(addon.default_itemvault) do
				MAYBE_ADD ("vault", opts.itemvault, id)
			end
			addon:redisplay()
		end)
		container:AddChild(w)
	end
end


---------------
do
	local profiles
	controls.basic_profiles = function (container)
		if not profiles then
			profiles = LibStub("AceDBOptions-3.0"):GetOptionsTable(addon.db)
			LibStub("AceConfig-3.0"):RegisterOptionsTable("OuroLootProfiles", profiles)
		end
		LibStub("AceConfigDialog-3.0"):Open ("OuroLootProfiles", container)
	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("CheckBox")
		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("CheckBox")
			w:SetFullWidth(true)
			w:SetType("checkbox")
			w:SetLabel(d)
			if d == "notraid" then
				w:SetDescription[[Tick this before manually 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
				if d == "comm" then
					addon.is_guilded = _G.IsInGuild()
				end
			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("CheckBox")
		--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

	container:AddChild(spacer(10))

	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')  -- set by :Activate
	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)

	container:AddChild(spacer(3))
	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)
		w = mkbutton("Peppers!", [[test 3]])
		w:SetRelativeWidth(0.20)
		w:SetCallback("OnClick", function()
			PlaySoundFile ([[Sound\CREATURE\UncleGao\VO_SB_GAO_EVENT_04.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 immediately 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 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 out of nested entries
		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 options_treegroup -- careful, this is deliberately not preserved
local status_for_scroll = {}
local status_for_select = { treewidth = 160 }

local function preload()
	for id in pairs(opts.itemfilter) do GetItemInfo(id) end
	for id in pairs(opts.itemvault) do GetItemInfo(id) end
	preload = nil
end

local aceconfig_list = {}

-- Selecting an entry on the left tree column.
local opt_OnGroupSelected_func = function (treeg,event,category)
	opts = addon.db.profile
	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()
	if aceconfig_list[category] then
		-- aceconfigdialog makes too many assumptions about scrolling
		for _,func in ipairs(catfuncs) do
			if func(treeg,category) then break end
		end
	else
		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,category) 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
end

-- Clicking the Options tab as a whole (tabs_OnGroupSelected["opt"]).
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)
	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)

	if #OuroLootSV_log > 0 then
		w = AceGUI:Create("Label")
		w:SetFullWidth(true)
		w:SetText(("Currently %d lines in disk log."):format(#OuroLootSV_log))
		specials:AddChild(w)
	end

	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)


---------------
-- Registering new options entries
local insertion_index = #options_tree
for i = #options_tree, 1, -1 do
	if options_tree[i] == 'NEW INSERTIONS HERE' then
		insertion_index = i
		break
	end
end
if insertion_index then
	table.remove (options_tree, insertion_index)
else
	insertion_index = #options_tree + 1
end


--[[
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
  'name' field and
  - TEXT is passed
  - if OPTIONS is a table, then OPTIONS.args.name exists
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 (uses PLUGIN:GetName())
  II) a function to call directly

(I) Will augment options.args.name with coloring/sizing, if it exists.
    (Set options.args.name.name before registering.)  Will create it if not
	present.

(II)  Title added unconditionally.  Callback is
        options (scrollframe_container, plugin, unique_code)
	  where UNIQUE_CODE is what's used by the TreeGroup widget in identifying
	  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, unique, parent, text, options)
	unique = (unique and tostring(unique))
		or assert(plugin.name, "plugin has no 'name' field")

	--if parent

	text = text or plugin:GetName()

	local handler
	local pdb = plugin.moduleName and
	            self.db:GetNamespace (plugin.moduleName, --[[silent=]]true)

	if type(options) == 'table' then
		-- AceConfig-style options table
		aceconfig_list[unique] = true
		if not options.args.name then
			options.args.name = {
				name = plugin:GetName(),
				type = 'description',
			}
		end
		options.args.name.name = '|cff30adff' .. options.args.name.name .. '|r'
		options.args.name.fontSize = 'large'
		options.args.name.width = 'full'
		options.args.name.cmdHidden = true
		options.args.name.order = 1
		if pdb then
			options.args.INSERTED_SPACER = {
				name = '',
				type = 'description',
				cmdHidden = true,
				width = 'full',
				order = 9000,
			}
			options.args.RESET = {
				name = "Reset",
				desc = ([[Reset settings for <%s> plugin back to defaults.  Shift-click to also trigger a UI reload.]]):format(plugin:GetName()),
				type = 'execute',
				func = function()
					pdb:ResetProfile()
					if IsShiftKeyDown() then ReloadUI() end
				end,
				order = 9001,
			}
		end
		LibStub("AceConfig-3.0"):RegisterOptionsTable(plugin:GetName(), options)
		handler = function (sf)
			LibStub("AceConfigDialog-3.0"):Open (plugin:GetName(), sf)
		end

	elseif type(options) == 'function' then
		-- AceGUI callback
		if pdb then
			handler = function (sf)
				sf:AddChildren(mktitle(text))
				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)
				w:SetCallback("OnClick", function(_w)
					pdb:ResetProfile()
					if IsShiftKeyDown() then ReloadUI() end
				end)
				sf:AddChildren(spacer(10),w)
				return ret
			end
		else
			handler = function (sf)
				sf:AddChildren(mktitle(text))
				return options (sf, plugin, unique)
			end
		end

	else
		error(("Error: 'options' parameter for plugin '%s' is neither table nor function"):format(plugin:GetName()))
	end

	if not controls[unique] then
		controls[unique] = {}
	end
	table.insert (controls[unique], handler)

	table.insert (options_tree, insertion_index, {
		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 unique -- something that can be used as 'parent'?
end


addon.FILES_LOADED = addon.FILES_LOADED + 1
-- vim:noet