Quantcast

- LDB rewrite, including new registry attrib for the LDB .tooltip

urnati [04-05-26 - 12:42]
- LDB rewrite, including new registry attrib for the LDB .tooltip
- Profile : Allow profile delete if profile is not sync source
- Gold : Add back 'DB delete'
Filename
Titan/Titan.lua
Titan/TitanConfig.lua
Titan/TitanLDB.lua
Titan/TitanMenu.lua
Titan/TitanMovable.lua
Titan/TitanTemplate.lua
Titan/TitanUtils.lua
Titan/TitanVariables.lua
Titan/_TitanIDE.lua
TitanBag/TitanBag.lua
TitanGold/TitanGold.lua
diff --git a/Titan/Titan.lua b/Titan/Titan.lua
index 9410675..763b8d3 100644
--- a/Titan/Titan.lua
+++ b/Titan/Titan.lua
@@ -220,6 +220,10 @@ local function RegisterForEvents()
 	_G[TITAN_PANEL_CONTROL]:RegisterEvent("ZONE_CHANGED");
 	_G[TITAN_PANEL_CONTROL]:RegisterEvent("ZONE_CHANGED_INDOORS");
 	_G[TITAN_PANEL_CONTROL]:RegisterEvent("ZONE_CHANGED_NEW_AREA");
+
+	-- 2026 Apr Add more info for Alts
+	_G[TITAN_PANEL_CONTROL]:RegisterEvent("PLAYER_MONEY");
+	_G[TITAN_PANEL_CONTROL]:RegisterEvent("PLAYER_EQUIPMENT_CHANGED");
 end

 --------------------------------------------------------------
@@ -268,15 +272,15 @@ local function SetToonInfo(toon)
 	toon_info.name = p
 	toon_info.server = s

-	local classFilename, classID = UnitClassBase(unit) -- regardless of comsetic changes
+	local classFilename, classID = UnitClassBase(unit) -- regardless of cosmetic changes
 	local classInfo = C_CreatureInfo.GetClassInfo(classID)
 	if classInfo == nil then
 		toon_info.class = "??"
 		toon_info.className = "??"
 		toon_info.classId = 0
 	else
-		toon_info.class = classInfo.className
-		toon_info.className = classInfo.classFile
+		toon_info.class = classInfo.className -- localized
+		toon_info.className = classInfo.classFile -- for comparison
 		toon_info.classId = classInfo.classID
 	end

@@ -292,6 +296,15 @@ local function SetToonInfo(toon)
 	toon_info.race = localizedRaceName
 	toon_info.raceName = englishRaceName
 	toon_info.raceId = raceID
+
+	-- 2026 Apr Add more info for Alts
+	-- Set initially, updates via events
+	toon_info.gold_toon = GetMoney() -- NO Warband
+
+	local avgItemLevel, avgItemLevelEquipped, avgItemLevelPvp = GetAverageItemLevel()
+	toon_info.itemLevelAve = avgItemLevel -- using ony equp change event, this may not be accurate...
+	toon_info.itemLevelEquipped = avgItemLevelEquipped -- this is the one we are tracking
+	toon_info.itemLevelPvp = avgItemLevelPvp
 end

 local function SetToonLogout(toon)
@@ -470,7 +483,7 @@ end
 ---@param arg1 boolean isLogin
 ---@param arg2 boolean isReload
 function TitanPanelBarButton:PLAYER_ENTERING_WORLD(arg1, arg2, arg3, arg4)
-	local call_success = nil
+	local call_ok = nil
 	local ret_val = nil

 	Titan_Debug.Out('titan', 'p_e_w', "Titan PLAYER_ENTERING_WORLD pcall setup routine")
@@ -501,22 +514,22 @@ function TitanPanelBarButton:PLAYER_ENTERING_WORLD(arg1, arg2, arg3, arg4)
 	then
 		-- StopTitan will force error and end this routine

-		call_success, ret_val = pcall(SetupTitan)
-		if call_success then
+		call_ok, ret_val = pcall(SetupTitan)
+		if call_ok then
 			-- Titan initialized properly
 		else
 			StopTitan("Could not initialize", ret_val) -- something really bad occured...
 		end

-		call_success, ret_val = pcall(SetupUser)
-		if call_success then
+		call_ok, ret_val = pcall(SetupUser)
+		if call_ok then
 			-- Titan initialized properly
 		else
 			StopTitan("Setup error", ret_val) -- something really bad occured...
 		end

-		call_success, ret_val = pcall(ShowTitan)
-		if call_success then
+		call_ok, ret_val = pcall(ShowTitan)
+		if call_ok then
 			-- Titan initialized properly
 		else
 			StopTitan("Could not show Bars", ret_val) -- something really bad occured...
@@ -555,6 +568,7 @@ end

 ---Titan Handle PLAYER_LOGOUT On logout, set some debug data in saved variables.
 function TitanPanelBarButton:PLAYER_LOGOUT()
+	-- many API calls will not work; data is not caught before the actual logout...
 	--[[
 	if not IsTitanPanelReset then
 		-- for debug
@@ -564,9 +578,9 @@ function TitanPanelBarButton:PLAYER_LOGOUT()
 			TitanPanelRegister.TitanPlugins = TitanPlugins
 		end
 	end
-	--]]
 	Titan__InitializedPEW = false

+	--]]
 	SetToonLogout(TitanSettings.Player)
 end

@@ -618,6 +632,27 @@ function TitanPanelBarButton:PLAYER_REGEN_DISABLED()
 	TitanPanelBarButton_DisplayBarsWanted("PLAYER_REGEN_DISABLED")
 end

+---Titan Handle ZONE_CHANGED_INDOORS Hide Titan top bars if user requested to hide Top bar(s) in BG or arena
+function TitanPanelBarButton:PLAYER_MONEY()
+	local toon_info = TitanSettings.Players[TitanSettings.Player].Info
+	-- 2026 Mar Add more info for Alts
+	toon_info.gold_toon = GetMoney() -- NO Warband
+end
+
+---Titan Handle ZONE_CHANGED_INDOORS Hide Titan top bars if user requested to hide Top bar(s) in BG or arena
+function TitanPanelBarButton:PLAYER_EQUIPMENT_CHANGED()
+	local toon_info = TitanSettings.Players[TitanSettings.Player].Info
+	-- 2026 Mar Add more info for Alts
+	local avgItemLevel, avgItemLevelEquipped, avgItemLevelPvp = GetAverageItemLevel()
+	toon_info.itemLevelAve = avgItemLevel
+	toon_info.itemLevelEquipped = avgItemLevelEquipped
+	toon_info.itemLevelPvp = avgItemLevelPvp
+	-- Poss info to save for Alts
+	-- professions, XP (Rested), level %, bags (free/total), connected realms (server click)
+	-- raid (locked list on click), num quests, currencies (on click - virtual)
+
+end
+
 if Titan_Global.switch.can_edit_ui then
 	-- Do not need to adjust frames
 else
@@ -693,7 +728,6 @@ local function handle_slash_help(cmd)

 	--	Give the user the general help if we can not figure out what they want
 	TitanPrint("", "header")
-	-- Cannot count registered plugins after initial registration  TitanUtils_RegisterPluginList()

 	if cmd == "reset" then
 		TitanPrint(L["TITAN_PANEL_SLASH_RESET_0"], "plain")
diff --git a/Titan/TitanConfig.lua b/Titan/TitanConfig.lua
index 9fe47f1..a0fdb48 100644
--- a/Titan/TitanConfig.lua
+++ b/Titan/TitanConfig.lua
@@ -38,7 +38,7 @@ local Config_locale = {
 		slash      = L["TITAN_PANEL_MENU_SLASH_COMMAND"],
 		help_list  = L["TITAN_PANEL_MENU_HELP_LIST"],
 		im_ex_port = L["TITAN_PANEL_MENU_IMPEXP_LABEL"],
-		adjust     = L["TITAN_PANEL_MENU_ADJUST_LABEL"] ,
+		adjust     = L["TITAN_PANEL_MENU_ADJUST_LABEL"],
 	}
 }

@@ -428,7 +428,7 @@ local function CreateBarsList(pos)
 				TitanPanelButton_Justify();
 			end,
 		}
---]]
+		--]]

 		position = position + 1 -- background
 		args[v.name].args.background = {
@@ -632,7 +632,7 @@ end
 ---@param pos number Options order start
 ---@return table Config options
 local function CreateBarsAll(pos)
---	AceConfigRegistry:NotifyChange("Titan Panel Globals")
+	--	AceConfigRegistry:NotifyChange("Titan Panel Globals")
 	local args = {}
 	local position = 1000

@@ -1215,7 +1215,7 @@ local function CreateProfiles(pos)
 	local p_sync = {} -- profiles used as Sync

 	-- Rip through the players (with server name) to sort them
-	for index, id in pairs(TitanSettings.Players) do
+	for index, id in TitanUtils_PlayerIter() do
 		-- collect some info on THIS toon for the config
 		local this_toon = {}

@@ -1235,17 +1235,20 @@ local function CreateProfiles(pos)
 		this_toon.profile = TitanVariables_GetProfile(index)
 		local res = ""

-		if this_toon.profile.ptype == Titan_Global.profile.GLOBAL then
-			res = L["TITAN_PANEL_GLOBAL"] .. " : " .. this_toon.profile.cname
+		-- Create string after Profile name in header
+		if is_custom then
+			res = ""
+		elseif this_toon.profile.ptype == Titan_Global.profile.GLOBAL then
+			res = " < " .. L["TITAN_PANEL_GLOBAL"] .. " : " .. this_toon.profile.cname
 		elseif this_toon.profile.ptype == Titan_Global.profile.SYNC then
-			res = L["TITAN_PANEL_MENU_PROFILE_SYNC"] .. " : " .. this_toon.profile.cname
+			res = " < " .. L["TITAN_PANEL_MENU_PROFILE_SYNC"] .. " : " .. this_toon.profile.cname
 		elseif this_toon.profile.ptype == Titan_Global.profile.TOON then
-			res = L["TITAN_PANEL_MENU_PROFILE_CHARS"] .. " : " .. Titan_Global.profile.NONE
+			res = " < " .. L["TITAN_PANEL_MENU_PROFILE_CHARS"] .. " : " .. Titan_Global.profile.NONE
 		else
-			res = "?" .. " : " .. Titan_Global.profile.NONE
+			res = " < " .. "?" .. " : " .. Titan_Global.profile.NONE
 		end

-	this_toon.toon_header = this_toon.fancy_name .. res --EndProfileText(this_toon.profile)
+		this_toon.toon_header = this_toon.fancy_name .. " "..res
 		this_toon.is_player = (index == TitanUtils_GetPlayer())
 		this_toon.sync_set = not (id.Panel["SyncWithProfile"] == Titan_Global.profile.NONE)
 		this_toon.sync_name = id.Panel["SyncWithProfile"]
@@ -1310,66 +1313,75 @@ local function CreateProfiles(pos)
 		}

 		do -- profile toon info
-			local custom_toon, toon_info = TitanUtils_GetPlayerInfo(this_toon.name)
-			if custom_toon then
+			local result, toon_info = TitanUtils_GetProfileInfo(this_toon.name, "Info", false)
+			-- is_custom | found | not_found | created
+			if result == "is_custom" then
 				-- Cannot login; There is no info to show.
-			elseif toon_info == nil then
+			elseif result == "not_found" then
 				-- For now show nothing
-			else -- display
-				local itoon = toon_info
-				local logout = (itoon.logoutStr == nil) and L["TITAN_PANEL_NA"] or itoon.logoutStr
-				position = position + 1
-				p_args[tostring(position)] = {
-					type = "description",
-					name = L["TITAN_PANEL_MENU_PROFILE_LOGOUT"].." : " .. logout,
-					--width = "0.5",
-					cmdHidden = true,
-					order = position,
-				}
-				if itoon.zoneText == nil or itoon.subZoneText == nil then
-					-- not filled in
+			elseif result == "found" then
+				if toon_info == nil then
+					-- satisfy IDE OR routine failed miserably
+					-- Show nothing
 				else
+					local itoon = toon_info
+					local logout = (itoon.logoutStr == nil) and L["TITAN_PANEL_NA"] or itoon.logoutStr
 					position = position + 1
 					p_args[tostring(position)] = {
 						type = "description",
-						name = L["TITAN_PANEL_MENU_PROFILE_LOC"].." : " .. itoon.zoneText .. " " .. itoon.subZoneText,
+						name = L["TITAN_PANEL_MENU_PROFILE_LOGOUT"] .. " : " .. logout,
 						--width = "0.5",
 						cmdHidden = true,
 						order = position,
 					}
+					if itoon.zoneText == nil or itoon.subZoneText == nil then
+						-- not filled in
+					else
+						position = position + 1
+						p_args[tostring(position)] = {
+							type = "description",
+							name = L["TITAN_PANEL_MENU_PROFILE_LOC"] ..
+							" : " .. itoon.zoneText .. " " .. itoon.subZoneText,
+							--width = "0.5",
+							cmdHidden = true,
+							order = position,
+						}
+					end
+					position = position + 1
+					p_args[tostring(position)] = {
+						type = "description",
+						name = L["TITAN_PANEL_MENU_PROFILE_LEVEL"] .. " : " .. itoon.levelText,
+						width = "0.5",
+						cmdHidden = true,
+						order = position,
+					}
+					position = position + 1
+					p_args[tostring(position)] = {
+						type = "description",
+						name = L["TITAN_PANEL_MENU_PROFILE_FACTION"] .. " : " .. itoon.faction,
+						width = "0.5",
+						cmdHidden = true,
+						order = position,
+					}
+					position = position + 1
+					p_args[tostring(position)] = {
+						type = "description",
+						name = L["TITAN_PANEL_MENU_PROFILE_CLASS"] .. " : " .. itoon.class,
+						width = "0.5",
+						cmdHidden = true,
+						order = position,
+					}
+					position = position + 1
+					p_args[tostring(position)] = {
+						type = "description",
+						name = L["TITAN_PANEL_MENU_PROFILE_RACE"] .. " : " .. itoon.race,
+						width = "0.5",
+						cmdHidden = true,
+						order = position,
+					}
 				end
-				position = position + 1
-				p_args[tostring(position)] = {
-					type = "description",
-					name = L["TITAN_PANEL_MENU_PROFILE_LEVEL"] .." : " .. itoon.levelText,
-					width = "0.5",
-					cmdHidden = true,
-					order = position,
-				}
-				position = position + 1
-				p_args[tostring(position)] = {
-					type = "description",
-					name = L["TITAN_PANEL_MENU_PROFILE_FACTION"].." : " .. itoon.faction,
-					width = "0.5",
-					cmdHidden = true,
-					order = position,
-				}
-				position = position + 1
-				p_args[tostring(position)] = {
-					type = "description",
-					name = L["TITAN_PANEL_MENU_PROFILE_CLASS"].." : " .. itoon.class,
-					width = "0.5",
-					cmdHidden = true,
-					order = position,
-				}
-				position = position + 1
-				p_args[tostring(position)] = {
-					type = "description",
-					name = L["TITAN_PANEL_MENU_PROFILE_RACE"].." : " .. itoon.race,
-					width = "0.5",
-					cmdHidden = true,
-					order = position,
-				}
+			else
+				-- not sure what happened, show nothing for now
 			end
 		end

@@ -1487,6 +1499,7 @@ local function CreateProfiles(pos)
 						L["TITAN_PANEL_MENU_LOAD_SETTINGS"]
 						.. " > " .. this_toon.name .. ""
 						, "info")
+					AceConfigRegistry:NotifyChange("Titan Panel Addon Chars")
 				end,
 				-- does not make sense to load current character profile
 				disabled = (this_toon.is_player or g_sync),
@@ -1522,7 +1535,7 @@ local function CreateProfiles(pos)
 				end,
 				-- can not delete current character profile
 				disabled = (this_toon.is_player
-					or g_sync
+					--					or g_sync
 					or p_sync[this_toon.name]),
 			}
 			position = position + 1
@@ -1797,7 +1810,7 @@ local function CreateProfiles(pos)
 				}
 			else
 				local has_sync = false
-				for index, id in pairs(TitanSettings.Players) do
+				for index, id in TitanUtils_PlayerIter() do
 					if id.Panel["SyncWithProfile"] == this_toon.name then
 						-- This profile uses this toon as a Sync
 						position = position + 1
@@ -1845,7 +1858,7 @@ local function CreateImportExportList(pos)
 	local p_info  = {} -- used to hold info about each toon in players

 	-- Rip through the players (with server name) to sort them
-	for index, id in pairs(TitanSettings.Players) do
+	for index, id in TitanUtils_PlayerIter() do
 		--		table.insert(players, {index = index});
 		--		table.insert(export, {index = false});
 		players[index] = index
@@ -1902,7 +1915,7 @@ local function CreateImportExportList(pos)
 	args["export_header"] = {
 		order = position,
 		type = "header",
-		name = L["TITAN_PANEL_MENU_IMPEXP_IMPORT"].." \n",
+		name = L["TITAN_PANEL_MENU_IMPEXP_IMPORT"] .. " \n",
 		cmdHidden = true
 	}
 	local ex_desc = ""
@@ -2411,7 +2424,7 @@ local function CreateUIOptions(pos)
 				width = ".5",
 				order = 42,
 				type = "description",
-				name = L["TITAN_PANEL_MENU_FRAME_STRATA_ORDER"].."\n"
+				name = L["TITAN_PANEL_MENU_FRAME_STRATA_ORDER"] .. "\n"
 					.. "- BACKGROUND\n"
 					.. "- LOW - default\n"
 					.. "- MEDIUM\n"
@@ -3648,7 +3661,7 @@ local function BuildAll()
 		AceConfig:RegisterOptionsTable("Titan Panel Addon Changes", titan_options.args.changeHistory)
 		AceConfig:RegisterOptionsTable("Titan Panel Help List", titan_options.args.helplist)

---		AceConfig:RegisterOptionsTable("Titan", titan_options)
+		--		AceConfig:RegisterOptionsTable("Titan", titan_options)
 		AceConfig:RegisterOptionsTable(config_parent, titan_options)
 		--	AceConfigRegistry:RegisterOptionsTable("Titan", titan_options)
 	end
diff --git a/Titan/TitanLDB.lua b/Titan/TitanLDB.lua
index 683b936..df430fd 100644
--- a/Titan/TitanLDB.lua
+++ b/Titan/TitanLDB.lua
@@ -2,55 +2,50 @@
 A "bridge" module to ensure proper registration and communication of LDB plugins with Titan Panel
 --]===]

---[===[
-Originally by Tristanian aka "TristTitan" as a Titan member
-Created and initially commited on : July 29th, 2008
---]===]
-
 --[===[ Var Titan LDB overview
-The spec: https://github.com/tekkub/libdatabroker-1-1
+Titan implements the LDB spec: https://github.com/tekkub/libdatabroker-1-1
 LDB (libdatabroker) is a small library that enables an addon to hook into a 'display' addon such as Titan.

 --- Creation
 The addon dev creates an LDB object which the lib places in storage accessible by lib:DataObjectIterator().
 It also fires a "LibDataBroker_DataObjectCreated" callback.

-LDB objects work by callbacks.
-When an LDB addon changes one of its values, the lib fires a callback for the display addon.
-
 The LDB addon may declare scripts (tooltip, mouse clicks, etc.) per the spec for the display addon to use.

 --- Starting from Titan view
-On PLAYER_ENTERING_WORLD, Titan will use the iterator to wrap each LDB type addon into a Titan plugin.
-Once done processing the known LDB objects,
-Titan registers for the callback to handle LDB objects created later or on demand.
-
+On PLAYER_ENTERING_WORLD, Titan will use the iterator to transform each LDB type addon into a Titan plugin.
 Titan registers for callbacks on text and icon updates - depending on the LDB type.

+Once done, Titan registers for the callback to handle LDB objects created later or on demand.
+
 --- Running from Titan view
 The LDB addon is responsible for setting and changing its text and icon.
+When an LDB addon changes one of its values, the lib fires a callback for the display addon.
 Titan is responsible for updating the Titan plugin in response.

-The Titan plugin will use the LDB addon scripts IF declared, again depending on the LDB type.
-
+The Titan plugin will use the LDB addon scripts IF declared.

 --- Supported
 Only LDB types listed in the LDB 1.1 spec are supported by Titan.

 - "launcher" become "icon" plugins - TitanPanelIconTemplate
+	type* - "launcher"
 	icon* - always shown
 	OnClick* -
 	label^ -
 	right side^ - default
-	tooltip
+	tocname
 - "data source" become "combo" plugins - TitanPanelComboTemplate
+	type* - "data source"
 	icon^ -
 	OnClick -
 	text*^ - or value & suffix
+	value -
+	suffix -
 	label^ -
 	OnEnter -
 	OnLeave -
-	tooltip
+	tooltip - Frame should NOT be type GameTooltip!!!!
 	OnTooltipShow -

 * required by LDB spec
@@ -112,322 +107,150 @@ local SupportedDOTypes = { DATA_SOURCE, LAUNCHER } -- in the 1.1 spec
 -- constants & variables
 local CALLBACK_PREFIX = "LibDataBroker_AttributeChanged_"
 local _G = getfenv(0);
-local InCombatLockdown = _G.InCombatLockdown;
+
 -- Create control frame so we can get events
+-- RegisterCallback assumes the method called is ON the frame (self) passed
+-- The IDE assumes RegisterCallback method is a string <sigh>
 local LDBToTitan = CreateFrame("Frame", "LDBTitan")
+
 local ldb = LibStub:GetLibrary("LibDataBroker-1.1")
 local LibQTip = nil
-local media = LibStub("LibSharedMedia-3.0")
 -- generic icon in case the DO does not provide one
-local iconTitanDefault = "Interface\\PVPFrame\\PVP-ArenaPoints-Icon";
+local iconTitanDefault = "Interface\\PVPFrame\\PVP-ArenaPoints-Icon"

--- Events we want for LDBToTitan
+-- Events we want
 LDBToTitan:RegisterEvent("PLAYER_LOGIN")
---LDBToTitan:RegisterEvent("PLAYER_ENTERING_WORLD")
-
----local OK to show tooltip?
----@return boolean
-local function If_Show_Tooltip()
-	local use_mod = TitanAllGetVar("UseTooltipModifer")
-	local use_alt = TitanAllGetVar("TooltipModiferAlt")
-	local use_ctrl = TitanAllGetVar("TooltipModiferCtrl")
-	local use_shift = TitanAllGetVar("TooltipModiferShift")
-	local ok = false
-	local tmp_txt = ""
-	if use_mod then
-		if (use_alt and IsAltKeyDown())
-			or (use_ctrl and IsControlKeyDown())
-			or (use_shift and IsShiftKeyDown())
-		then
-			ok = true
-		end
-	else
-		ok = true
-	end
-	return ok
-end
-
----Titan Properly anchor tooltips of the Titan (LDB) plugin
----@param parent table Parent frame
----@param anchorPoint string
----@param relativeToFrame table|string
----@param relativePoint string
----@param xOffset number
----@param yOffset number
----@param frame table|string Tooltip frame
---- relativeToFrame and frame are really ScriptRegion|string for GameTooltip
-function LDBToTitan:TitanLDBSetOwnerPosition(parent, anchorPoint, relativeToFrame, relativePoint, xOffset, yOffset, frame)
---	if frame:GetName() == "GameTooltip" then
-		-- Changes for 9.1.5 Removed the background template from the GameTooltip
-		-- Making changes to it difficult and possibly changing the tooltip globally.
-
-		frame:SetOwner(parent, "ANCHOR_NONE");
-
-		-- set font size for the Game Tooltip
-		if not TitanPanelGetVar("DisableTooltipFont") then
-			if TitanTooltipScaleSet < 1 then
-				TitanTooltipOrigScale = GameTooltip:GetScale();
-				TitanTooltipScaleSet = TitanTooltipScaleSet + 1;
-			end
-			frame:SetScale(TitanPanelGetVar("TooltipFont"));
-		end
---	end
-	frame:ClearAllPoints();
-	frame:SetPoint(anchorPoint, relativeToFrame, relativePoint, xOffset, yOffset);
-end
-
----Titan Fill in the tooltip for the Titan (LDB) plugin
----@param name string Plugin id name for LDB
----@param frame table Tooltip frame
----@param tt_func function? Tooltip function to be run
-function LDBToTitan:TitanLDBSetTooltip(name, frame, tt_func)
-	-- Check to see if we allow tooltips to be shown
-	if not TitanPanelGetVar("ToolTipsShown")
-		or (TitanPanelGetVar("HideTipsInCombat") and InCombatLockdown()) then
-		return
-	end

-	local button = TitanUtils_GetButton(name);
-	local scale = TitanPanelGetVar("Scale");
-	local offscreenX, offscreenY;
-	local i = TitanPanel_GetButtonNumber(name);
-	local bar = TITAN_PANEL_DISPLAY_PREFIX .. TitanUtils_GetWhichBar(name)
-	local vert = TitanBarData[bar].vert
-	-- Get TOP or BOTTOM for the anchor and relative anchor
-	local rel_pt, pt
-	if vert == TITAN_TOP then
-		pt = "TOP"
-		rel_pt = "BOTTOM"
+---local Add scripts and info to plugin frame for tooltip processsing.
+---@param frame table Titan frame created for LDB object
+local function GenTooltipScripts(frame)
+	local dbg_msg = "LDB-TT "
+	dbg_msg = dbg_msg .. tostring(frame.registry.id)
+	-- Technically the spec states only a data source can have .tooltip and .OnTooltipShow
+	-- but the original development allowed them to exist if the LDB dev added them.
+	if frame.ldb_obj.tooltip then
+		dbg_msg = dbg_msg .. " | tooltip"
+		frame.registry.tooltipDisplayFrame = frame.ldb_obj.tooltip
 	else
-		pt = "BOTTOM"
-		rel_pt = "TOP"
+		-- not on LDB
 	end
-
-	if _G[bar] and button then
-		self:TitanLDBSetOwnerPosition(button, pt .. "LEFT", button:GetName(),
-			rel_pt .. "LEFT", -10, 0, frame); -- y 4 * scale
-		-- Adjust frame position if it's off the screen
-		offscreenX, offscreenY = TitanUtils_GetOffscreen(frame);
-		if (offscreenX == -1) then
-			self:TitanLDBSetOwnerPosition(button, pt .. "LEFT", bar,
-				rel_pt .. "LEFT", 0, 0, frame);
-		elseif (offscreenX == 1) then
-			self:TitanLDBSetOwnerPosition(button, pt .. "RIGHT", bar,
-				rel_pt .. "RIGHT", 0, 0, frame);
-		end
+	if frame.ldb_obj.OnTooltipShow then
+		dbg_msg = dbg_msg .. " | OnTooltipShow"
+		frame.registry.tooltipTemplateFunction = frame.ldb_obj.OnTooltipShow
 	else
+		-- not on LDB
 	end

-	if tt_func and If_Show_Tooltip() then
-		frame:ClearLines()
-		tt_func(frame) -- TODO: use pcall??
-	end
-	frame:Show();
-end
-
----Titan Script Handler for the Titan (LDB) plugin
---- This implementation will work fine for a static tooltip but may have implications for dynamic ones so for now,
---- we'll only set it once (no callback) and see what happens
----@param event string Event name
----@param name string Plugin id name for LDB
----@param _ any not used
----@param func function LDB data object
----@param obj table LDB data object
-function LDBToTitan:TitanLDBHandleScripts(event, name, _, func, obj)
-	local TitanPluginframe = _G["TitanPanel" .. name .. "Button"];
-
-	-- tooltip
-	if event:find("tooltip") and not event:find("OnTooltipShow") then
-		local pluginframe = _G[obj.tooltip] or obj.tooltip
-		if pluginframe then
-			TitanPluginframe:SetScript("OnEnter", function(self)
-				TitanPanelButton_OnEnter(self);
-				LDBToTitan:TitanLDBSetTooltip(name, pluginframe, nil)
-			end
-			)
+	-- 2026 Mar :
+	-- This was rewritten to share tooltip processes with Titan
+	-- by setting a registry attribute for the type of tooltip processing needed.
+	--
+	-- Another change was to always create the On* scripts on the Titan plugin
+	-- then call the LDB routine, if it exists.
+	-- This may use a few more cycle but removes the need for callbacks and still
+	-- allows the LDB to update the scripts. Titan will use them on the next On* call.
+
+	-- OnEnter
+	-- Technically a launcher does not have OnEnter / Onleave but the original developer
+	-- allowed them to exist if the LDB dev added them.
+	-- If they do not exist, Titan takes no action.
+	frame:SetScript("OnEnter", function(self)
+		-- Per the spec, tooltip preferred order is:
+		-- tooltip > OnEnter/OnLeave > OnTooltipShow.
+		if self.registry.tooltipDisplayFrame then
+			TitanPanelButton_OnEnter(self) -- Use the .tooltip as is
+		elseif self.ldb_obj.OnEnter then
+			self.ldb_obj.OnEnter(self) -- Plugin will control all aspects
+		elseif self.registry.tooltipTemplateFunction then
+			TitanPanelButton_OnEnter(self) -- Pass a Titan tooltip to be filled by plugin
+		else
+			-- No recognized tooltip method, move on...
+		end

-			TitanPluginframe:SetScript("OnMouseDown", function(self)
-				pluginframe:Hide();
-			end
-			)
-
-			if pluginframe:GetScript("OnLeave") then
-				-- do nothing
-			else
-				TitanPluginframe:SetScript("OnLeave", function(self)
-					if obj.OnLeave then
-						obj.OnLeave(self)
+		-- Dropped Ace Tablet-2.0 lib as of 2025 Sep; last updated 2008 Sep
+
+		-- LibQTip-1.0 support code
+		LibQTip = LibStub("LibQTip-1.0", true)
+		if LibQTip then
+			local tt = nil
+			--local key, tip
+			for key, tip in LibQTip:IterateTooltips() do
+				if tip then
+					local _, relativeTo = tip:GetPoint()
+					if relativeTo
+						and relativeTo:GetName() == self:GetName() then
+						tt = tip
+						break
 					end
-					pluginframe:Hide();
-					TitanPanelButton_OnLeave(self);
 				end
-				)
 			end
-
-			if pluginframe:GetName() ~= "GameTooltip" then
-				if pluginframe:GetScript("OnShow") then
-					-- do nothing
-				else
-					pluginframe:SetScript("OnShow", function(self)
-						LDBToTitan:TitanLDBSetTooltip(name, pluginframe, nil)
-					end
-					)
-				end
+			if tt then
+				-- set transparency
+				local red, green, blue, _ = tt:GetBackdropColor()
+				local red2, green2, blue2, _ = tt:GetBackdropBorderColor()
+				tt:SetBackdropColor(red, green, blue,
+					TitanPanelGetVar("TooltipTrans"))
+				tt:SetBackdropBorderColor(red2, green2, blue2,
+					TitanPanelGetVar("TooltipTrans"))
 			end
 		end
+		-- /LibQTip-1.0 support code
+	end
+	)

-		-- OnTooltipShow
-	elseif event:find("OnTooltipShow") then
-		TitanPluginframe:SetScript("OnEnter", function(self)
-			if TITAN_PANEL_MOVING == 0 and func then
-				TitanPanelTooltip.TitanAddonName = name
-				LDBToTitan:TitanLDBSetTooltip(name, TitanPanelTooltip, func);
-			end
-			TitanPanelButton_OnEnter(self);
-		end
-		)
-		TitanPluginframe:SetScript("OnLeave", function(self)
-			TitanPanelTooltip:Hide();
-			TitanPanelButton_OnLeave(self);
+	-- OnLeave
+	frame:SetScript("OnLeave", function(self)
+		if self.ldb_obj.OnLeave then
+			self.ldb_obj.OnLeave(self)
+		else
+			TitanPanelButton_OnLeave(self)
 		end
-		)
+	end
+	)

-		-- OnDoubleClick
-	elseif event:find("OnDoubleClick") and not event:find("OnClick") then
-		TitanPluginframe:SetScript("OnDoubleClick", function(self, button)
-			if TITAN_PANEL_MOVING == 0 then
-				func(self, button)
+	-- Use the OnClick given, if exists
+	frame:SetScript("OnClick", function(self, button)
+		if TITAN_PANEL_MOVING == 0 then -- no move in progress
+			if self.ldb_obj.OnClick then
+				self.ldb_obj.OnClick(self, button)
 			end
 		end
-		)

-		-- OnClick
-	elseif event:find("OnClick") then
-		TitanPluginframe:SetScript("OnClick", function(self, button)
-			if TITAN_PANEL_MOVING == 0 then -- no move in progress
-				func(self, button)
-			end
-
-			--[[ 2026 Mar
-			Discovered that the Blizzard_Menu system, when used by an LDB,
+		--[[ 2026 Mar
+			Discovered when LDB uses Blizzard_Menu system
 			is set up and shown before we get here.
 			So... make assumption that menus will be closed on a mouse click.
-			--]]
-
-			--[===[
-			-- implement a safeguard, since the DO may actually use
-			-- Blizzy dropdowns !
-			if not TitanPanelRightClickMenu_IsVisible() then
-				TitanPanelButton_OnClick(self, button);
-			else
-				TitanUtils_CloseAllControlFrames();
-			end
-			--]===]
-		end
-		)
-	else -- OnEnter / OnLeave
-		TitanPluginframe:SetScript("OnEnter", function(self)
-			-- Check for tooltip libs without embedding them
-
-			-- Dropped Ace Tablet-2.0 lib as of 2025 Sep; last updated 2008 Sep
-			LibQTip = LibStub("LibQTip-1.0", true)
-			-- Check to see if we allow tooltips to be shown
-			if not TitanPanelGetVar("ToolTipsShown")
-				or (TitanPanelGetVar("HideTipsInCombat") and InCombatLockdown()) then
-				-- if a plugin is using tablet, then detach and close the tooltip
-				return;
-			else
-				-- if a plugin is using tablet, then re-attach the tooltip
-				-- (it will auto-open on mouseover)
-			end

-			-- set original tooltip scale for GameTooltip
-			if not TitanPanelGetVar("DisableTooltipFont") then
-				TitanTooltipOrigScale = GameTooltip:GetScale();
-			end
-			-- call OnEnter on LDB Object
-			if TITAN_PANEL_MOVING == 0 and func and If_Show_Tooltip() then
-				func(self)
-			end
-
-			TitanPanelButton_OnEnter(self);
-			-- LibQTip-1.0 support code
-			if LibQTip then
-				local tt = nil
-				local key, tip
-				for key, tip in LibQTip:IterateTooltips() do
-					if tip then
-						local _, relativeTo = tip:GetPoint()
-						if relativeTo
-							and relativeTo:GetName() == TitanPluginframe:GetName() then
-							tt = tip
-							break
-						end
-					end
-				end
-				if tt then
-					-- set transparency
-					local red, green, blue, _ = tt:GetBackdropColor()
-					local red2, green2, blue2, _ = tt:GetBackdropBorderColor()
-					tt:SetBackdropColor(red, green, blue,
-						TitanPanelGetVar("TooltipTrans"))
-					tt:SetBackdropBorderColor(red2, green2, blue2,
-						TitanPanelGetVar("TooltipTrans"))
-				end
-			end
-			-- /LibQTip-1.0 support code
-		end
-		)
+			It is conceivable that two menus could be open at the same time hopefully never ...
+			--]]
+	end
+	)

-		-- OnLeave
-		TitanPluginframe:SetScript("OnLeave", function(self)
-			if obj.OnLeave then
-				obj.OnLeave(self)
+	--
+	-- OnDoubleClick is UNDOCUMENTED in the 1.1 spec
+	-- but was implemented by the original developer
+	--
+	-- Use the OnDoubleClick, if exists
+	frame:SetScript("OnDoubleClick", function(self, button)
+		if TITAN_PANEL_MOVING == 0 then
+			if self.ldb_obj.OnDoubleClick then
+				self.ldb_obj.OnDoubleClick(self, button)
 			end
-			TitanPanelButton_OnLeave(self);
 		end
-		)
-	end
-end
-
----Titan Text callback for the Titan (LDB) plugin when the LDB addon changes display text of the LDB object
----@param _ any not used
----@param name string Plugin id name for LDB
----@param attr string "value" or  "suffix" or "text" or "label"
----@param value any Should be string
----@param dataobj table LDB data object
-function LDBToTitan:TitanLDBTextUpdate(_, name, attr, value, dataobj)
-	-- just in case the LDB is active before Titan can register it...
-	if not Titan__InitializedPEW then
-		-- plugins have not been registered yet.
-		return
-	end
-	-- This check is overkill but just in case...
-	local plugin = TitanUtils_GetPlugin(name)
-	local ldb = plugin and plugin.LDBVariables
-	if not ldb then
-		-- This plugin has not been registered
-		return
 	end
+	)

-	-- Accept the various display elements and update the Titan plugin
-	if attr == "value" then ldb.value = value end
-	if attr == "suffix" then ldb.suffix = value end
-	if attr == "text" then ldb.text = value end
-	if attr == "label" then ldb.label = value end
-
-	-- Now update the button with the change
-	TitanPanelButton_UpdateButton(name)
+	Titan_Debug.Out('titan', 'ldb_setup', dbg_msg);
 end

----Titan Text callback when the LDB addon changes display text
+---Titan Update Titan button
 ---@param name string Plugin id name for LDB
 ---@return string label
 ---@return string value
-function TitanLDBShowText(name)
+local function UpdateButton(name)
 	-- Set 'label1' and 'value1' for the Titan button display
-	local nametrim = string.gsub(name, "LDBT_", "");
-	local fontstring = _G[TitanUtils_ButtonName(nametrim) .. TITAN_PANEL_TEXT];
+--	local nametrim = string.gsub(name, "LDBT_", "");
+--	local fontstring = _G[TitanUtils_ButtonName(nametrim) .. TITAN_PANEL_TEXT];
 	local separator = ": "
 	local lab1, val1 = "", ""
 	local plugin = TitanUtils_GetPlugin(name)
@@ -448,10 +271,11 @@ function TitanLDBShowText(name)
 		end

 		-- Check for display text
-		-- Check for display text
 		-- .text is required to show
 		-- .value is the text of the value - 100.0 in 100.0 FPS
 		-- .suffix is the text after the value - FPS in 100.0 FPS
+		-- This has been 'broken' for ages...
+		-- not sure value / suffix were ever used...
 		if TitanGetVar(name, "ShowRegularText") then
 			val1 = (ldb.text or "")
 		else
@@ -475,65 +299,13 @@ function TitanLDBShowText(name)
 	return lab1, val1
 end

----Titan Icon callback for the Titan (LDB) plugin when the LDB addon changes the icon of the LDB object
----@param _ any not used
----@param name string Plugin id name for LDB
----@param attr string "icon" or  "iconCoords" or "iconR" "iconB" "iconR"
----@param value any icon : Path to icon file; iconCoords : coords
----@param dataobj table LDB data object
-function LDBToTitan:TitanLDBIconUpdate(_, name, attr, value, dataobj)
-	-- just in case the LDB is active before Titan can register it...
-	if not Titan__InitializedPEW then
-		-- no plugins are registered yet
-		return
-	end
-	-- This check is overkill but just in case...
-	local plugin = TitanUtils_GetPlugin(name)
-	local ldb = plugin and plugin.LDBVariables
-	if ldb then
-		if attr == "icon" then
-			TitanPlugins[name].icon = value;
-			TitanPanelButton_SetButtonIcon(name);
-		end
-
-		-- support for iconCoords, iconR, iconG, iconB attributes
-		if attr == "iconCoords" then
-			TitanPanelButton_SetButtonIcon(name, value);
-		end
-
-		if attr == "iconR" or attr == "iconB" or attr == "iconG" then
-			TitanPanelButton_SetButtonIcon(name, nil,
-				dataobj.iconR, dataobj.iconG, dataobj.iconB);
-		end
-	else
-		-- This plugin is not registered yet
-		return
-	end
-end
-
----Titan Refresh all text & icon for LDB addons that were successfully registered
---- Ensure all the LDB buttons are updated.
---- This is called once x seconds after PEW. This helps close the gap where LDB addons set their text on their PEW event
-function TitanLDBRefreshButton()
-	--	TitanDebug("LDB: RefreshButton")
-	for name, obj in ldb:DataObjectIterator() do
-		if obj then
-			local unused = nil
-			LDBToTitan:TitanLDBTextUpdate(unused, name, "text", (obj.text or ""), obj)
-			LDBToTitan:TitanLDBIconUpdate(unused, name, "icon", (obj.icon or iconTitanDefault), obj)
-		else
-			--	TitanDebug("LDB: '"..name.."' no refresh")
-		end
-	end
-end
-
----Titan Create a Titan plugin from the DO (Data Object)
+---local Create a Titan plugin from the LDB DO (Data Object)
 --- This is the heart of the LDB to Titan. It reads the LDB DO (Data Object) and creates a Titan plugin.
 --- This takes a stricter interpretation of the LDB 1.1 spec rather than guessing what LDB addon developers intended.
 ---@param self table LDB frame
 ---@param name_str string LDB id name
 ---@param obj table LDB data object
-function TitanLDBCreateObject(self, name_str, obj)
+local function TitanLDBCreateObject(self, name_str, obj)
 	local name = name_str
 	Titan_Debug.Out('titan', 'ldb_setup', tostring(name) .. " : Attempting to register ");

@@ -676,7 +448,7 @@ function TitanLDBCreateObject(self, name_str, obj)
 		ldb = tostring(obj.type),
 		-- per 1.1 spec if .label exists use it else use data object's name
 		menuText = obj.label or name,
-		buttonTextFunction = "TitanLDBShowText",
+		buttonTextFunction = UpdateButton,
 		icon = ldb__icon,
 		iconWidth = 16,
 		controlVariables = {
@@ -759,37 +531,16 @@ function TitanLDBCreateObject(self, name_str, obj)
 	-- Create the Titan frame for this LDB addon
 	-- Titan _OnLoad will be used to request the plugin be registered by Titan (Template)
 	local newTitanFrame -- a frame
-	--[===[
-	if obj.type == "macro" then  -- custom
-		newTitanFrame = CreateFrame("Button",
-			TitanUtils_ButtonName(name),
-			UIParent, "SecureActionButtonTemplate, TitanPanelComboTemplate")
---			UIParent, "TitanPanelComboTemplate")
-		newTitanFrame:RegisterForClicks("AnyUp", "AnyDown")
-		newTitanFrame:SetMouseClickEnabled(true)
-		newTitanFrame:SetAttribute("type", "macro")
---		newTitanFrame:SetAttribute("macro", obj.commandtext)
-		newTitanFrame:SetAttribute("macrotext", obj.commandtext)
-		newTitanFrame:SetScript("OnClick", function(self, button, down)
-						SecureUnitButton_OnClick(self, button, down)
-						--TitanPanelBarButton_OnClick(self, button)
-						end)
-	else
-		newTitanFrame = CreateFrame("Button",
-			TitanUtils_ButtonName(name),
-			UIParent, "TitanPanelComboTemplate")
-	end
---]===]
 	newTitanFrame = CreateFrame("Button",
 		TitanUtils_ButtonName(name),
 		UIParent, "TitanPanelComboTemplate")

 	newTitanFrame.TitanCreatedBy = "LDB"
-	--	newTitanFrame.TitanType = "macro"
 	newTitanFrame.TitanName = (name or "?")
 	newTitanFrame.TitanAction = (obj.commandtext or "None")

 	newTitanFrame.registry = registry
+	newTitanFrame.ldb_obj = obj -- attach for processing within scripts and Titan
 	newTitanFrame:SetFrameStrata("FULLSCREEN");
 	newTitanFrame:SetToplevel(true);
 	newTitanFrame:RegisterForClicks("LeftButtonUp", "RightButtonUp");
@@ -797,41 +548,10 @@ function TitanLDBCreateObject(self, name_str, obj)
 	-- Use the routines given by the DO in this precedence
 	-- tooltip > OnEnter > OnTooltipShow >
 	-- or register a callback in case it is created later. Per the 1.1 LDB spec
-	if obj.tooltip then
-		self:TitanLDBHandleScripts("tooltip", name, nil, obj.tooltip, obj)
-	elseif obj.OnEnter then
-		self:TitanLDBHandleScripts("OnEnter", name, nil, obj.OnEnter, obj)
-	elseif obj.OnTooltipShow then
-		self:TitanLDBHandleScripts("OnTooltipShow", name, nil, obj.OnTooltipShow, obj)
-	else
-		self:TitanLDBHandleScripts("OnEnter", name, nil, nil, obj)
-		ldb.RegisterCallback(self,
-			CALLBACK_PREFIX .. name .. "_OnEnter", "TitanLDBHandleScripts")
-		ldb.RegisterCallback(self,
-			CALLBACK_PREFIX .. name .. "_OnTooltipShow", "TitanLDBHandleScripts")
-	end
-
-	-- Use the OnClick given by the DO
-	-- or register a callback in case it is created later.
-	if obj.OnClick then
-		self:TitanLDBHandleScripts("OnClick", name, nil, obj.OnClick)
-	else
-		ldb.RegisterCallback(self,
-			CALLBACK_PREFIX .. name .. "_OnClick", "TitanLDBHandleScripts")
-	end
-
-	--
-	-- OnDoubleClick is UNDOCUMENTED in the 1.1 spec
-	-- but was implemented by the original developer
-	--
-	-- Use the OnDoubleClick given by the DO
-	-- or register a callback in case it is created later.
-	if obj.OnDoubleClick then
-		self:TitanLDBHandleScripts("OnDoubleClick", name, nil, obj.OnDoubleClick)
-	else
-		ldb.RegisterCallback(self,
-			CALLBACK_PREFIX .. name .. "_OnDoubleClick", "TitanLDBHandleScripts")
-	end
+	-- 2026 Mar Major rewrite to move tooltip logic into TitanTemplate.
+	-- The two LDB schemes (tooltip and OnTooltipShow) will be captured in the registry
+	-- which makes the schemes available to Titan plugins.
+	GenTooltipScripts(newTitanFrame)

 	local pew = "event"
 	if Titan__InitializedPEW then
@@ -850,10 +570,78 @@ function TitanLDBCreateObject(self, name_str, obj)
 		.. "\n...'" .. tostring(newTitanFrame:GetName()) .. "'"
 	)
 	return "Success"
+
+end
+
+---Titan Text callback for the Titan (LDB) plugin when the LDB addon changes display text of the LDB object
+---@param _ any not used
+---@param name string Plugin id name for LDB
+---@param attr string "value" or  "suffix" or "text" or "label"
+---@param value any Should be string
+---@param dataobj table LDB data object
+function LDBToTitan:TitanLDBTextUpdate(_, name, attr, value, dataobj)
+	-- just in case the LDB is active before Titan can register it...
+	if not Titan__InitializedPEW then
+		-- plugins have not been registered yet.
+		return
+	end
+	-- This check is overkill but just in case...
+	local plugin = TitanUtils_GetPlugin(name)
+	local ldb = plugin and plugin.LDBVariables
+	if not ldb then
+		-- This plugin has not been registered
+		return
+	end
+
+	-- Accept the various display elements and update the Titan plugin
+	local val = tostring(value) -- be paranoid :)
+	if attr == "value" then ldb.value = val end
+	if attr == "suffix" then ldb.suffix = val end
+	if attr == "text" then ldb.text = val end
+	if attr == "label" then ldb.label = val end
+
+	-- Now update the button with the change
+	TitanPanelButton_UpdateButton(name)
+end
+
+---Titan Callback when the LDB addon changes the icon of the LDB object
+---@param _ any not used
+---@param name string Plugin id name for LDB
+---@param attr string "icon" or  "iconCoords" or "iconR" "iconB" "iconR"
+---@param value any icon : Path to icon file; iconCoords : coords
+---@param dataobj table LDB data object
+function LDBToTitan:TitanLDBIconUpdate(_, name, attr, value, dataobj)
+	-- just in case the LDB is active before Titan can register it...
+	if not Titan__InitializedPEW then
+		-- no plugins are registered yet
+		return
+	end
+	-- This check is overkill but just in case...
+	local plugin = TitanUtils_GetPlugin(name)
+	local ldb = plugin and plugin.LDBVariables
+	if ldb then
+		if attr == "icon" then
+			TitanPlugins[name].icon = value;
+			TitanPanelButton_SetButtonIcon(name);
+		end
+
+		-- support for iconCoords, iconR, iconG, iconB attributes
+		if attr == "iconCoords" then
+			TitanPanelButton_SetButtonIcon(name, value);
+		end
+
+		if attr == "iconR" or attr == "iconB" or attr == "iconG" then
+			TitanPanelButton_SetButtonIcon(name, nil,
+				dataobj.iconR, dataobj.iconG, dataobj.iconB);
+		end
+	else
+		-- This plugin is not registered yet
+		return
+	end
 end

 ---Titan OnEvent handler for LDBToTitan
---- Read through all the LDB objects requesting creation so far. Try to create cooresponding Titan plugins.
+--- Accept the LDB object to create a cooresponding Titan plugin.
 ---@param sender any !! Not Used !!
 ---@param name string LDB id name
 ---@param obj table LDB data object
@@ -892,52 +680,67 @@ function LDBToTitan:TitanLDBCreateObject(sender, name, obj)
 	)
 end

---- OnEvent - PLAYER_LOGIN - handler for LDBToTitan
----@param self table Plugin frame
----@param event string Event name
----@param ... any Event args
-LDBToTitan:SetScript("OnEvent", function(self, event, ...)
-	if (event == "PLAYER_LOGIN") then
-		self:UnregisterEvent("PLAYER_LOGIN")
-		-- Register the LDB plugins that have been created so far
-		for name, obj in ldb:DataObjectIterator() do
-			local call_success = true
-			local ret_val = ""
-
-			-- Just in case, catch any errors
-			call_success, -- needed for pcall
-			ret_val = -- actual return values
-				pcall(TitanLDBCreateObject, self, name, obj)
-
-			if call_success then
-				-- Registration request created
-			else
-				-- Create enough of a plugin to tell the user / developer
-				-- that this plugin failed
-				local plugin =
-				{
-					self = nil,
-					button = nil,
-					name = tostring(name),
-					issue = ret_val,
-					notes = "",
-					status = TITAN_REGISTER_FAILED,
-					category = "",
-					plugin_type = tostring(obj.type or "LDB"),
-				}
-				TitanUtils_PluginFail(plugin)
+---Titan OnEvent handler for LDBToTitan
+local function InitLoad()
+	LDBToTitan:SetScript("OnEvent", function(self, event, ...)
+		if (event == "PLAYER_LOGIN") then
+			self:UnregisterEvent("PLAYER_LOGIN")
+			-- Register the LDB plugins that have been created so far
+			for name, obj in ldb:DataObjectIterator() do
+				local call_success = true
+				local ret_val = ""
+
+				-- Just in case, catch any errors
+				call_success, ret_val = pcall(TitanLDBCreateObject, self, name, obj)
+
+				if call_success then
+					-- Registration request created
+				else
+					-- Create enough of a plugin to tell the user / developer
+					-- that this plugin failed
+					local plugin =
+					{
+						self = nil,
+						button = nil,
+						name = tostring(name),
+						issue = ret_val,
+						notes = "",
+						status = TITAN_REGISTER_FAILED,
+						category = "",
+						plugin_type = tostring(obj.type or "LDB"),
+					}
+					TitanUtils_PluginFail(plugin)
+				end
+
+				Titan_Debug.Out('titan', 'ldb_setup', "LDB"
+					.. " " .. tostring(name) .. ""
+					.. " " .. tostring(call_success) .. ""
+					.. " " .. tostring(ret_val) .. ""
+				)
 			end

-			Titan_Debug.Out('titan', 'ldb_setup', "LDB"
-				.. " " .. tostring(name) .. ""
-				.. " " .. tostring(call_success) .. ""
-				.. " " .. tostring(ret_val) .. ""
-			)
+			-- In case a LDB plugin is created later...
+			ldb.RegisterCallback(self, "LibDataBroker_DataObjectCreated", "TitanLDBCreateObject")
 		end
+	end
+	)
+end

-		-- In case a LDB plugin is created later...
-		ldb.RegisterCallback(self,
-			"LibDataBroker_DataObjectCreated", "TitanLDBCreateObject")
+---Titan Refresh all text & icon for LDB addons that were successfully registered
+--- Ensure all the LDB buttons are updated.
+--- This is called once x seconds after PEW. This helps close the gap where LDB addons set their text on their PEW event
+function TitanLDBRefreshButton()
+	--	TitanDebug("LDB: RefreshButton")
+	for name, obj in ldb:DataObjectIterator() do
+		if obj then
+			local unused = nil
+			LDBToTitan:TitanLDBTextUpdate(unused, name, "text", (obj.text or ""), obj)
+			LDBToTitan:TitanLDBIconUpdate(unused, name, "icon", (obj.icon or iconTitanDefault), obj)
+		else
+			--	TitanDebug("LDB: '"..name.."' no refresh")
+		end
 	end
 end
-)
+
+InitLoad()
+--
\ No newline at end of file
diff --git a/Titan/TitanMenu.lua b/Titan/TitanMenu.lua
index 8c22ddc..61ecb6d 100644
--- a/Titan/TitanMenu.lua
+++ b/Titan/TitanMenu.lua
@@ -956,7 +956,7 @@ In order these are :
 On right click, Titan will, in order :
 1) Create the context menu
 2) Add a menu title at top using .registry.id
-3) Call the plugin to fill it as before - using pcall, placing error in the menu
+3) Call the plugin to fill it as before - using protected call, placing error in the menu
 4) Add the plugin designated control vars + right side + Hide on bottom of menu
 The top and bottom of the menu are common so Titan adds:
 - a title
diff --git a/Titan/TitanMovable.lua b/Titan/TitanMovable.lua
index 4fc313f..6cd58f0 100755
--- a/Titan/TitanMovable.lua
+++ b/Titan/TitanMovable.lua
@@ -216,15 +216,6 @@ else
 local hooks_done = false;

 local move_count = 0
---[[
-Declare the Ace routines
- local AceTimer = LibStub("AceTimer-3.0")
- i.e. TitanPanelAce.ScheduleTimer("LDBToTitanSetText", TitanLDBRefreshButton, 2);
- or
- i.e. TitanPanelAce:ScheduleTimer(TitanLDBRefreshButton, 2);
-
- Be careful that the 'self' is proper to cancel timers!!!
---]]

 --local TitanPanelAce = LibStub("AceAddon-3.0"):NewAddon("TitanPanel", "AceHook-3.0", "AceTimer-3.0")

diff --git a/Titan/TitanTemplate.lua b/Titan/TitanTemplate.lua
index e56d4fc..5179633 100644
--- a/Titan/TitanTemplate.lua
+++ b/Titan/TitanTemplate.lua
@@ -104,6 +104,19 @@ function TitanPanel_SetScale()
 end
 --]]

+local function GetTooltipLines(tooltip)
+  local textLines = {}
+  local regions = {tooltip:GetRegions()}
+  local cnt = 1
+  for _, r in ipairs(regions) do
+    if r:IsObjectType("FontString") then
+      print(cnt..": "..tostring(r:GetText()))
+	  cnt = cnt + 1
+    end
+  end
+  return textLines
+end
+
 ---local Helper to add a line of tooltip text to the tooltip.
 ---@param text string To add
 ---@param frame table Tooltip frame
@@ -141,11 +154,17 @@ end
 ---@param xOffset number X offset
 ---@param yOffset number Y offset
 ---@param frame table Tooltip frame
-local function TitanTooltip_SetOwnerPosition(parent, anchorPoint, relativeToFrame, relativePoint, xOffset, yOffset, frame)
+---@param custom boolean If custom / not tooltip frame
+local function SetOwnerPosition(parent, anchorPoint, relativeToFrame, relativePoint, xOffset, yOffset, frame, custom)
 	-- Changes for 9.1.5 Removed the background template from the Tooltip
 	-- Making changes to it difficult and possibly changing the tooltip globally.

-	frame:SetOwner(parent, "ANCHOR_NONE");
+	if custom then
+		-- do NOT set owner - it clears the contents!
+	else
+		frame:SetOwner(parent, "ANCHOR_NONE")
+	end
+
 	frame:SetPoint(anchorPoint, relativeToFrame, relativePoint, xOffset, yOffset);

 	-- set font size for the Game Tooltip
@@ -177,7 +196,9 @@ end
 ---@param self table Plugin frame
 ---@param id string Plugin id name
 ---@param frame table Tooltip frame to use
-local function TitanTooltip_SetPanelTooltip(self, id, frame)
+---@param custom? boolean If custom / not tooltip frame
+local function SetPanelTooltip(self, id, frame, custom)
+	local is_custom = custom or false
 	local button = TitanUtils_GetButton(id)

 	if button then
@@ -207,7 +228,7 @@ local function TitanTooltip_SetPanelTooltip(self, id, frame)
 			rel_pt = rel_pt .. "RIGHT";
 		end

-		TitanTooltip_SetOwnerPosition(button, pt, button:GetName(), rel_pt, 0, 0, frame)
+		SetOwnerPosition(button, pt, button:GetName(), rel_pt, 0, 0, frame, is_custom)
 	end
 end

@@ -287,13 +308,14 @@ local function TitanPanelButton_SetTooltip(self)

 	ok = AllowTooltip(frame)
 	if ok then
-		local call_success = nil
+		local call_ok = nil
 		local tmp_txt = ""

 		self.tooltipCustomFunction = nil;
 		self.titan_tt_func = ""
 		self.titan_tt_err = ""

+		dbg_msg = dbg_msg .." "..tostring(id)..""
 		if (id and TitanUtils_IsPluginRegistered(id)) then
 			local plugin = TitanUtils_GetPlugin(id)
 			-- 2024 Jun Add id and frame name to 'pass' to tooltip routine.
@@ -301,8 +323,20 @@ local function TitanPanelButton_SetTooltip(self)
 			-- Used by Titan auto hide to better determine which bar the 'pin' / icon is on.
 			self.plugin_id = id
 			self.plugin_frame = TitanUtils_ButtonName(id)
-			if (plugin and plugin.ldb) then
-				-- assume the TitanLDB processing will handle the tooltip.
+			if (plugin and plugin.tooltipDisplayFrame) then
+				-- 2026 Mar : Added from LDB to take advantage of Titan processing.
+				-- PLugin is expected to handle its frame!
+				-- Titan will position and show only!
+				-- Plugin must handle any timeout and any other features.
+
+				-- Hide the Titan Tooltip in case it is open.
+				frame:Hide()
+
+				dbg_msg = dbg_msg .. " | tooltipDisplayFrame"
+					.." '"..tostring(plugin.tooltipDisplayFrame:GetName()).."'"
+				SetPanelTooltip(self, id, plugin.tooltipDisplayFrame, true)
+
+				plugin.tooltipDisplayFrame:Show() -- now show it
 			elseif (plugin and plugin.tooltipTemplateFunction) then
 				-- 2026 Mar Added to pass a tooltip frame to a plugin as an explicit agreement.
 				-- This acts as Blizz GameTooltip so plugin can 'add line' etc.
@@ -310,14 +344,13 @@ local function TitanPanelButton_SetTooltip(self)
 				-- Hide the Titan Tooltip in case it is open.
 				frame:Hide()
 				frame:ClearLines() -- Hand off a blank tooltip
-				TitanTooltip_SetPanelTooltip(self, id, frame)
+				SetPanelTooltip(self, id, frame)

-				-- The plugin is in full control of contents.
+				-- The plugin is in full control of contents; pass it the tooltip frame to fill
 				self.tooltipTemplateFunction = plugin.tooltipTemplateFunction;
-				dbg_msg = dbg_msg .. " | custom"
-				call_success, -- for pcall
-				tmp_txt = pcall(self.tooltipTemplateFunction, frame)
-				if call_success then
+				dbg_msg = dbg_msg .. " | tooltipTemplateFunction"
+				call_ok, tmp_txt = pcall(self.tooltipTemplateFunction, frame)
+				if call_ok then
 					-- all is good
 					dbg_msg = dbg_msg .. " | ok"
 				else
@@ -334,14 +367,13 @@ local function TitanPanelButton_SetTooltip(self)
 				-- It is left so older plugins will work - but they error as of Midnight (12.0.0)
 				-- Use tooltipTemplateFunction instead!
 				local custom_f = GameTooltip
-				TitanTooltip_SetPanelTooltip(self, id, custom_f);
+				SetPanelTooltip(self, id, custom_f);

 				-- Fill the tooltip
 				self.tooltipCustomFunction = plugin.tooltipCustomFunction;
 				dbg_msg = dbg_msg .. " | custom"
-				call_success, -- for pcall
-				tmp_txt = pcall(self.tooltipCustomFunction, self)
-				if call_success then
+				call_ok, tmp_txt = pcall(self.tooltipCustomFunction, self)
+				if call_ok then
 					-- all is good
 					dbg_msg = dbg_msg .. " | ok"
 				else
@@ -354,6 +386,7 @@ local function TitanPanelButton_SetTooltip(self)
 				-- From Lingkan dev of Titan Rep Continued
 				local tooltipTextFunc = {} ---@type function
 				local tt_func = plugin.tooltipTextFunction
+				local func_ok = true

 				if type(tt_func) == 'string' then
 					-- Function MUST be in global namespace
@@ -366,14 +399,15 @@ local function TitanPanelButton_SetTooltip(self)
 				else
 					-- silently leave...
 					dbg_msg = dbg_msg .. " | none found"
+					func_ok = false
 				end

-				if (tooltipTextFunc) then
+				if func_ok then
 					-- Hide the Tooltip while being updated, to avoid race conditions.
 					frame:Hide()
 					GameTooltip:Hide() -- Also hide in case. Cannot hide completly custom tool tips
 					-- Prep the tooltip frame
-					TitanTooltip_SetPanelTooltip(self, id, frame);
+					SetPanelTooltip(self, id, frame);
 					if plugin.tooltipTitle then
 						self.tooltipTitle = plugin.tooltipTitle;
 						frame:SetText(self.tooltipTitle,
@@ -381,11 +415,10 @@ local function TitanPanelButton_SetTooltip(self)
 					else
 						-- assume dev is doing their own thing
 					end
-					call_success, -- for pcall
-					tmp_txt = pcall(tooltipTextFunc, self)
+					call_ok, tmp_txt = pcall(tooltipTextFunc, self)

 					-- Fill the tooltip
-					-- If pcall errors, the error will be in the tooltip
+					-- Any error will be in the tooltip
 					self.tooltipText = tmp_txt
 					if (self.tooltipText) then
 						TitanTooltip_AddTooltipText(self.tooltipText, frame)
@@ -768,7 +801,7 @@ end
 ---@param id string Plugin id
 --- The plugin is expected to tell Titan what routine is to be called in <self>.registry.buttonTextFunction.
 --- Note: Titan handles up to 4 label-value pairs. User may customize (override) the plugin labels.
---- The text routine is called in protected mode (pcall) to ensure the Titan main routines still run.
+--- The text routine is called in protected mode to ensure the Titan main routines still run.
 local function TitanPanelButton_SetButtonText(id)
 	local dbg_msg = "ptxt : '" .. tostring(id) .. "'"
 	local ok = false
@@ -805,7 +838,7 @@ local function TitanPanelButton_SetButtonText(id)

 	if ok and buttonTextFunction then
 		local label1, value1, label2, value2, label3, value3, label4, value4
-		local call_success = false
+		local call_ok = false
 		local button = TitanUtils_GetButton(id) -- get plugin frame
 		local buttonText = {}

@@ -821,11 +854,11 @@ local function TitanPanelButton_SetButtonText(id)

 			-- We'll be paranoid here and call the button text function in protected mode.
 			-- In case the function fails it will not take Titan with it...
-			call_success, -- for pcall
+			call_ok,
 			label1, value1, label2, value2, label3, value3, label4, value4 =
 				pcall(buttonTextFunction, id)

-			if call_success then
+			if call_ok then
 				-- All is good
 				text = true
 			else
diff --git a/Titan/TitanUtils.lua b/Titan/TitanUtils.lua
index 3d47b3e..2544029 100644
--- a/Titan/TitanUtils.lua
+++ b/Titan/TitanUtils.lua
@@ -1257,13 +1257,11 @@ function TitanUtils_GetAddOnMetadata(name, field)
 	---@diagnostic disable-next-line: deprecated, undefined-global
 	local GetMeta = C_AddOns and C_AddOns.GetAddOnMetadata or GetAddOnMetadata

-	local call_success, ret_val
+	local call_ok, ret_val

 	-- Just in case, catch any errors
-	call_success, -- needed for pcall
-	ret_val =  -- actual return values
-		pcall(GetMeta, name, field)
-	if call_success then
+	call_ok, ret_val = pcall(GetMeta, name, field)
+	if call_ok then
 		-- all is good
 		return ret_val
 	else
@@ -1365,7 +1363,7 @@ local function NoColor(name)
 	return no_color
 end

----local This routine is a protected manner (pcall) by Titan when it attempts to register a plugin.
+---local This routine is a protected manner by Titan when it attempts to register a plugin.
 ---@param plugin table Plugin frame - Titan template
 ---@return table Results of the registration - pass (TitanPlugins) or fail
 --- See routine for output table values
@@ -1512,20 +1510,16 @@ end
 --- Lets be extremely paranoid here because registering plugins that do not play nice can cause real headaches...
 ---@param plugin table Plugin frame - Titan template
 function TitanUtils_RegisterPlugin(plugin)
-	local call_success, ret_val
+	local call_ok, ret_val
 	-- Ensure we have a glimmer of a plugin and that the plugin has not
 	-- already been registered.
 	if plugin and plugin.status == TITAN_NOT_REGISTERED then
 		-- See if the request to register has a shot at success
 		if plugin.self then
 			-- Just in case, catch any errors
-			call_success, -- needed for pcall
-			ret_val = -- actual return values
-				pcall(TitanUtils_RegisterPluginProtected, plugin)
-			-- pcall does not allow errors to propagate out. Any error
-			-- is returned as text with the success / fail.
-			-- Think of it as a try - catch block
-			if call_success then
+			call_ok, ret_val = pcall(TitanUtils_RegisterPluginProtected, plugin)
+			-- Any error is returned as text with the success / fail.
+			if call_ok then
 				-- all is good so write the return values to the plugin
 				plugin.status = ret_val.result
 				plugin.issue = ret_val.issue
@@ -1679,6 +1673,90 @@ function TitanUtils_GetPlayerInfo(toon)
 	return is_custom, p_info
 end

+---Titan Get the data table from player Titan settings.
+---Intent is encapsulate plugins from where data is stored.
+---@param profile string A player name to look up
+---@param attrib string Specific data to look up
+---@param create boolean If true and no data table, create an empty table
+---@return string result is_custom | found | not_found | created
+---@return table? data nil or table of data found/created
+function TitanUtils_GetProfileInfo(profile, attrib, create)
+	local _, server, is_custom = TitanUtils_ParseName(profile)
+	-- Design intent is have a place to store data that is accessed across toons.
+	-- This routine abstracts ;where; the data table exists.
+	-- This returns a pointer to the data table so a plugin can manage the data elements
+	-- and Titan manages where the data table resides.
+	--
+	-- Base : TitanSettings.Players[profile] - Titan managed
+	--
+	-- [attrib] assigned by plugin by create = true - plugin managed
+	-- - Info- toon data for profiles; Alts, poss Config
+	-- - Gold- Gold plugin
+	--
+	-- [attrib].* managed by plugin
+	--
+	-- returns :
+	-- custom - true if the profile is custom else false
+	--
+	-- data -
+	-- if custom always return nil
+	-- if create = false : a plugin wants to know if data exists - return table or nil
+	-- if create = true  : a plugin needs to create to store - return table or {} (empty table)
+
+	local p_info = nil
+	local action = ""
+	if is_custom then
+		-- there is no Info table... cannnot log into a custom profile
+		-- NEVER create...
+		action = "is_custom"
+	elseif TitanSettings.Players[profile]
+	and TitanSettings.Players[profile][attrib]
+	then
+		p_info = TitanSettings.Players[profile][attrib] -- return the pointer
+		action = "found"
+	else
+		-- New or may not have logged into this toon in ages :)
+		if create then
+			TitanSettings.Players[profile][attrib] = {} -- create for caller
+			p_info = TitanSettings.Players[profile][attrib] -- return the pointer
+			action = "created"
+		else
+			p_info = nil -- tell caller no Info exists
+			action = "not_found"
+		end
+	end
+
+	return action, p_info
+end
+
+---Titan Allow iteration over the player list without the caller knowing where the list is.
+--- Usage : for player, data in TitanUtils_PlayerIter() end
+---@return function
+function TitanUtils_PlayerIter()
+   local t = TitanSettings.Players
+   -- Create a list of keys that match the filter pattern
+   local keys = {}
+   for k, v in pairs(t) do
+         table.insert(keys, k)
+   end
+
+   -- Define the iterator function that steps through the 'keys' list
+   local index = 0
+   local function iterator()
+      index = index + 1
+      local key = keys[index]
+      if key then
+         return key, t[key] -- Return key and value from original table
+      end
+      return nil -- End of iteration
+   end
+
+   -- Return the triplet: iterator, state (nil here, as state is internal), initial (0 handled in closure)
+   -- Note: We return the iterator function and the 'keys' table as state if we wanted to expose state,
+   -- but a closure over 'keys' is sufficient for hiding the original table.
+   return iterator
+end
+
 ---Titan Return the screen size after scaling
 ---@return table screenXY { x | y | scaled_x | scaled_y } all numbers
 function TitanUtils_ScreenSize()
@@ -2030,6 +2108,19 @@ function TitanFindIndex(tb, val)
 	end
 end

+local function GetTooltipLines(tooltip)
+  local textLines = {}
+  local regions = {tooltip:GetRegions()}
+  local cnt = 1
+  for _, r in ipairs(regions) do
+    if r:IsObjectType("FontString") then
+      print(cnt..": "..tostring(r:GetText()))
+	  cnt = cnt + 1
+    end
+  end
+  return textLines
+end
+
 --====== Deprecated routines
 -- These routines will be commented out for a couple releases then deleted.
 --
diff --git a/Titan/TitanVariables.lua b/Titan/TitanVariables.lua
index daded03..8312258 100644
--- a/Titan/TitanVariables.lua
+++ b/Titan/TitanVariables.lua
@@ -1219,6 +1219,30 @@ local function Check_toon_settings(toon, toon_table)
 	-- Note: the sync routine only ensures first level, NOT recursive
 	TitanVariables_SyncRegisterSavedVariables(TITAN_PANEL_SAVED_VARIABLES, v["Panel"])

+---[[
+	-- 2026 Mar - transfer Gold and Post to same level as Info
+	-- Remove the old one.
+	if  TitanSettings.Players[toon]
+	and TitanSettings.Players[toon].Info
+	and TitanSettings.Players[toon].Info.Gold then
+		TitanSettings.Players[toon].Gold = {}
+		TitanVariables_SyncRegisterSavedVariables(
+			TitanSettings.Players[toon].Info.Gold, TitanSettings.Players[toon].Gold)
+		TitanSettings.Players[toon].Info.Gold = nil
+	else
+		-- already transfered
+	end
+	if  TitanSettings.Players[toon]
+	and TitanSettings.Players[toon].Info
+	and TitanSettings.Players[toon].Info.Post then
+		TitanSettings.Players[toon].Post = {}
+		TitanVariables_SyncRegisterSavedVariables(
+			TitanSettings.Players[toon].Info.Post, TitanSettings.Players[toon].Post)
+		TitanSettings.Players[toon].Info.Post = nil
+	else
+		-- already transfered
+	end
+--]]

 	-- ====== New Mar 2023 : TitanSettings.Players[player].BarData to hold Short bar data
 	Set_bar_vars(toon)
diff --git a/Titan/_TitanIDE.lua b/Titan/_TitanIDE.lua
index e8c6003..2aaa335 100644
--- a/Titan/_TitanIDE.lua
+++ b/Titan/_TitanIDE.lua
@@ -212,6 +212,10 @@ C_Bank = {} -- 11.0.0 New Warbank - Hopefully WoW API extension will catch up so
 ---@field race string
 ---@field raceName string
 ---@field raceId number
+---@field gold_toon number
+---@field itemLevelAve number
+---@field itemLevelEquipped number
+---@field itemLevelPvp number

 --====== Profile output from Utils
 ---@class Get_Profile_Result
diff --git a/TitanBag/TitanBag.lua b/TitanBag/TitanBag.lua
index 8606855..9da20bb 100644
--- a/TitanBag/TitanBag.lua
+++ b/TitanBag/TitanBag.lua
@@ -1071,15 +1071,23 @@ Titan_Menu expects an object approach. The older scheme uses a table driven whic
 If Titan finds a tooltip function, Titan will assume it needs to position, show, and hide the tooltip.

 Titan looks for a function to create a tooltip, in order:
-1) .tooltipTemplateFunction : New 2026 Mar
+1) .tooltipDisplayFrame : New 2026 Mar
+   2026 Mar : Added from LDB (.tooltip) to take advantage of Titan processing.
+   It allows the plugin full control BUT the plugin must take nearly full responsibility.
+   Titan will position and show only!
+   Plugin must handle timeout and any other features.
+   Note: If using a frame of type GameTooltip, the plugin MUST set owner.
+   If Titan does a set oener on display, it wipes the contents...
+
+2) .tooltipTemplateFunction : New 2026 Mar
 A game tooltip template is passed to plugin as an explicit agreement
    pcall(self.tooltipTemplateFunction, self, frame)

-2) .tooltipCustomFunction : Deprecated Midnight (12.0.0) / 2026 Mar :
+3) .tooltipCustomFunction : Deprecated Midnight (12.0.0) / 2026 Mar :
 Assumes GameTooltip as implicit agreement
    tmp_txt = pcall(self.tooltipCustomFunction, self)

-3) .tooltipTextFunction : Titan adds plugin name as Title; expects text in return to fill the tooltip.
+4) .tooltipTextFunction : Titan adds plugin name as Title; expects text in return to fill the tooltip.

 The tooltip function is called when the mouse enters the plugin frame - OnEnter.
 Titan templates set the OnEnter script for the plugin frame.
diff --git a/TitanGold/TitanGold.lua b/TitanGold/TitanGold.lua
index 16b1a7a..de43f35 100644
--- a/TitanGold/TitanGold.lua
+++ b/TitanGold/TitanGold.lua
@@ -55,9 +55,8 @@ Titan_Debug.gold.eval = false
 ---@class GoldData
 ---@field gold number
 ---@field show boolean
-local GoldData = nil -- pointer to this player plugin data
+local GoldData = nil ---@class GoldInfo
 local GoldInfo = nil ---@class CharInfo
---TitanSettings.Players[toon].Info ---@type CharInfo

 ---@class IndexInfo Index flags
 ---@field valid boolean Saved toon is valid
@@ -145,17 +144,13 @@ function Warband.SetSum()
 		-- Really just prevents errors if not implemented in the WoW version

 		-- There *may* have been instances of failure reported as Titan errors
-		-- Wrap in pcall for safety
-		--Warband.bank_sum = C_Bank.FetchDepositedMoney(Enum.BankType.Account)
 		local sum = 0
-		local call_success = false
+		local call_ok = false
 		local ret_val = nil

-		call_success, -- needed for pcall
-		ret_val = -- actual return values
-			pcall(C_Bank.FetchDepositedMoney, Enum.BankType.Account)
+		call_ok, ret_val = pcall(C_Bank.FetchDepositedMoney, Enum.BankType.Account)

-		if call_success then
+		if call_ok then
 			-- Assume a valid Warband cash amount (WOWMONEY)
 			sum = ret_val
 		else
@@ -216,19 +211,6 @@ local function GetConnectedRealms()
 	return realms
 end

----local Use index to get toon info from Titan
----@param info string
----@return string Character name - no server
----@return string Server name
----@return string Faction internal, not localized
-local function GetIndexInfo(info)
-	local t_info = TitanSettings.Players[info].Info ---@type CharInfo
-	local character = t_info.name
-	local charserver = t_info.server
-	local char_faction = t_info.factionName
-	return character, charserver, char_faction
-end
-
 ---local Take Gold index and return parts plus various flags
 ---@param index string
 ---@return IndexInfo
@@ -236,77 +218,86 @@ local function EvalIndexInfo(index)
 	local str = ""
 	str = str .. tostring(index)

-	local res = { valid = false } -- The return table will be built as needed.
+	local res = { valid = false }
+	-- The return table will be built as needed.
 	local character, charserver, is_custom = TitanUtils_ParseName(index)
-	local toon_info = TitanSettings.Players[index].Info ---@class CharInfo
-	if is_custom then
+	local result = ""
+	local toon_info ---@class CharInfo
+	result, toon_info = TitanUtils_GetProfileInfo(index, "Info", false)
+	if result == "is_custom" then
 		-- do not fill in
 		res.valid = false

 		str = str .. " ignored : is_custom"
 		Titan_Debug.Out('gold', 'eval', str)
-	elseif toon_info == nil then
+	elseif result == "not_found" then
 		-- do not fill in
 		res.valid = false

 		str = str .. " ignored : no data yet"
-	else
-		local toon_gold = toon_info[TITAN_GOLD_ID] ---@class GoldData
-		if toon_gold == nil then
-			res.valid = false
-
-			str = str .. " ignored : info but no gold data yet"
+	elseif result == "found" then
+		if toon_info == nil then
 		else
-			res.valid = true
+			local toon_gold ---@class GoldData
+			result, toon_gold = TitanUtils_GetProfileInfo(index, "Gold", false)
+			if toon_gold == nil then
+				res.valid = false

-			res.char_name = character -- set in Info 9.1
-			res.server = charserver -- set in Info 9.1
-			res.faction = toon_info.faction
+				str = str .. " ignored : info but no gold data yet"
+			else
+				res.valid = true

-			res.ignore_faction = TitanGetVar(TITAN_GOLD_ID, "IgnoreFaction")
+				res.char_name = character -- set in Info 9.1
+				res.server = charserver -- set in Info 9.1
+				res.faction = toon_info.faction

-			if (res.faction == GoldInfo.faction) then
-				res.same_faction = true
-			else
-				res.same_faction = false
-			end
+				res.ignore_faction = TitanGetVar(TITAN_GOLD_ID, "IgnoreFaction")

-			if (res.server == GoldInfo.server) then
-				res.same_realm = true
-			else
-				res.same_realm = false
-			end
+				if (res.faction == GoldInfo.faction) then
+					res.same_faction = true
+				else
+					res.same_faction = false
+				end

-			local saved_server = string.gsub(res.server, "%s", "") -- GetAutoCompleteRealms removes spaces, idk why...
-			if merged_realms[saved_server] then
-				res.merge_realm = true
-			else
-				res.merge_realm = false
-			end
+				if (res.server == GoldInfo.server) then
+					res.same_realm = true
+				else
+					res.same_realm = false
+				end

-			-- Assume server option is satisfied; check other options
-			if (res.ignore_faction or res.same_faction)
-			and toon_gold.show
-			then
-				res.show_toon = true
-			else
-				res.show_toon = false
-			end
+				local saved_server = string.gsub(res.server, "%s", "") -- GetAutoCompleteRealms removes spaces, idk why...
+				if merged_realms[saved_server] then
+					res.merge_realm = true
+				else
+					res.merge_realm = false
+				end

-			res.gold = toon_gold.gold
-			res.show = toon_gold.show -- user option
-
-			str = str
-				.. " n:" .. tostring(res.char_name) .. ""
-				.. " s:" .. tostring(res.server) .. ""
-				.. " ss:" .. tostring(res.same_realm) .. ""
-				.. " ms:" .. tostring(res.merge_realm) .. ""
-				.. " f:" .. tostring(res.faction) .. ""
-				.. " if:" .. tostring(res.ignore_faction) .. ""
-				.. " sf:" .. tostring(res.same_faction) .. ""
-				.. " show:" .. tostring(res.show_toon) .. ""
-				.. " gold:" .. tostring(res.gold) .. ""
+				-- Assume server option is satisfied; check other options
+				if (res.ignore_faction or res.same_faction)
+					and toon_gold.show
+				then
+					res.show_toon = true
+				else
+					res.show_toon = false
+				end
+
+				res.gold = toon_gold.gold
+				res.show = toon_gold.show -- user option
+
+				str = str
+					.. " n:" .. tostring(res.char_name) .. ""
+					.. " s:" .. tostring(res.server) .. ""
+					.. " ss:" .. tostring(res.same_realm) .. ""
+					.. " ms:" .. tostring(res.merge_realm) .. ""
+					.. " f:" .. tostring(res.faction) .. ""
+					.. " if:" .. tostring(res.ignore_faction) .. ""
+					.. " sf:" .. tostring(res.same_faction) .. ""
+					.. " show:" .. tostring(res.show_toon) .. ""
+					.. " gold:" .. tostring(res.gold) .. ""
+			end
 		end
+	else
+		str = str .. " ignored : no data yet"
 	end

 	Titan_Debug.Out('gold', 'eval', str)
@@ -339,7 +330,7 @@ local function TotalGold()
 	if TitanGetVar(TITAN_GOLD_ID, "SeparateServers") then
 		Titan_Debug.Out('gold', 'total_gold', "=== SeparateServers")
 		-- Parse the database and display all characters on this server
-		for index, money in pairs(TitanSettings.Players) do
+		for index, money in TitanUtils_PlayerIter() do
 			local char = EvalIndexInfo(index)
 			if char.valid then
 				if char.same_realm and char.show_toon then
@@ -356,7 +347,7 @@ local function TotalGold()
 	elseif TitanGetVar(TITAN_GOLD_ID, "MergeServers") then
 		Titan_Debug.Out('gold', 'total_gold', "=== MergeServers")
 		-- Parse the database and display characters on merged / connected servers
-		for index, money in pairs(TitanSettings.Players) do
+		for index, money in TitanUtils_PlayerIter() do
 			local char = EvalIndexInfo(index)
 			if char.valid then
 				if char.merge_realm and char.show_toon then
@@ -373,7 +364,7 @@ local function TotalGold()
 	elseif TitanGetVar(TITAN_GOLD_ID, "AllServers") then
 		Titan_Debug.Out('gold', 'total_gold', "=== AllServers")
 		-- Parse the database and display characters on all servers
-		for index, money in pairs(TitanSettings.Players) do
+		for index, money in TitanUtils_PlayerIter() do
 			local char = EvalIndexInfo(index)
 			if char.valid then
 				if char.show_toon then
@@ -458,7 +449,7 @@ local function GetTooltipText()
 	if TitanGetVar(TITAN_GOLD_ID, "SeparateServers") then
 		-- Parse the database and display characters from this server
 		Titan_Debug.Out('gold', 'tool_tip', "=== SeparateServers")
-		for index, money in pairs(TitanSettings.Players) do
+		for index, money in TitanUtils_PlayerIter() do
 			local char = EvalIndexInfo(index)
 			if char.valid then
 				if char.same_realm and char.show_toon then
@@ -470,7 +461,7 @@ local function GetTooltipText()
 	elseif TitanGetVar(TITAN_GOLD_ID, "MergeServers") then
 		-- Parse the database and display characters from merged / connected servers
 		Titan_Debug.Out('gold', 'tool_tip', "=== MergeServers")
-		for index, money in pairs(TitanSettings.Players) do
+		for index, money in TitanUtils_PlayerIter() do
 			local char = EvalIndexInfo(index)
 			if char.valid then
 				if char.merge_realm and char.show_toon then
@@ -482,7 +473,7 @@ local function GetTooltipText()
 	elseif TitanGetVar(TITAN_GOLD_ID, "AllServers") then
 		-- Parse the database and display characters from all servers
 		Titan_Debug.Out('gold', 'tool_tip', "=== AllServers")
-		for index, money in pairs(TitanSettings.Players) do
+		for index, money in TitanUtils_PlayerIter() do
 			local char = EvalIndexInfo(index)
 			if char.valid then
 				if char.show_toon then
@@ -510,7 +501,7 @@ local function GetTooltipText()
 		charserver = toon.server
 		char_faction = toon.faction

-		local t_gold = toon.gold --TitanSettings.Players[toon].Info[TITAN_GOLD_ID].gold
+		local t_gold = toon.gold
 		coin_str = NiceCash(t_gold, false, false)
 		show_dash = false
 		show_realm = true
@@ -679,7 +670,7 @@ end

 ---local See if this toon is in saved vars AFTER PEW event.
 --- Get current total and session start time. Toon gold is available via API AFTER PEW event.
-local function Initialize_Array()
+local function Initialize_Array(action)
 	Titan_Debug.Out('gold', 'flow', "Init inititated")

 	local info = ""
@@ -690,52 +681,53 @@ local function Initialize_Array()

 		-- See if this is a new toon to Gold saved vars or reset
 		local gindex, _, _ = TitanUtils_GetPlayer()
-		-- TitanSettings.Players[toon].Info.[TITAN_GOLD_ID]
+		local result = ""
+		result, GoldInfo = TitanUtils_GetProfileInfo(gindex, "Info", false)

-		GoldInfo = TitanSettings.Players[gindex].Info
-
-		if GoldInfo[TITAN_GOLD_ID] then
-			-- use existing data
-		else
-			GoldInfo[TITAN_GOLD_ID] = {}
-			GoldData = GoldInfo[TITAN_GOLD_ID]
-			GoldData.gold = 0
+		-- Ensure the saved vars are what we need for valid toons
+		for index, money in TitanUtils_PlayerIter() do
+			local char = EvalIndexInfo(index)
+			if char.valid then
+				local toon_gold ---@class GoldData
+				result, toon_gold = TitanUtils_GetProfileInfo(index, "Gold", false)
+				if result == "found" then
+					if toon_gold == nil then
+						-- not sure !?
+					else
+						-- Added 2026 Feb
+						if toon_gold.show == nil then
+							toon_gold.show = true -- default
+						else
+							-- exists, use as is
+						end
+
+						if action == "reset" then
+							toon_gold.gold = 0
+						else
+							-- use as is
+						end
+					end
+				else
+					-- ignore
+				end
+			else
+				-- ignore custom profiles or toons not logged into yet
+			end
 		end

+		result, GoldData = TitanUtils_GetProfileInfo(gindex, "Gold", true)
+		GoldData.gold = Get_Money()
+
 		Warband.Init()

 		GOLD_STARTINGGOLD = Get_Money();
 		GOLD_SESSIONSTART = GetTime();
 		GOLD_INITIALIZED = true;

-		GoldData = GoldInfo[TITAN_GOLD_ID]
-		GoldData.gold = Get_Money()
-
 		info = ""
 			.. " " .. tostring(GOLD_SESSIONSTART) .. ""
 			.. " " .. tostring(GOLD_STARTINGGOLD) .. ""
 			.. " " .. tostring(Warband.GetSum()) .. ""
-
-		-- Ensure the saved vars are what we need for valid toons
-		for index, money in pairs(TitanSettings.Players) do
-			local char = EvalIndexInfo(index)
-			if char.valid then
-				-- Added 2026 Feb
-				if TitanSettings.Players[index].Info[TITAN_GOLD_ID].show == nil then
-					TitanSettings.Players[index].Info[TITAN_GOLD_ID].show = true -- default
-				else
-					-- exists, use as is
-				end
-
-				if TitanSettings.Players[index].Info[TITAN_GOLD_ID].show == nil then
-					TitanSettings.Players[index].Info[TITAN_GOLD_ID].show = true -- default
-				else
-					-- exists, use as is
-				end
-			else
-				-- ignore custom profiles or toons not logged into yet
-			end
-		end
 	end

 	-- 2026 Mar : Repurposed to add sort gold decsending
@@ -754,13 +746,12 @@ local function Initialize_Array()
 		.. " " .. info .. ""
 	Titan_Debug.Out('gold', 'flow', msg)
 end
-
 ---local Clear the gold array and rebuild
 ---@param self Button
 local function ClearData(self)
 	GOLD_INITIALIZED = false;

-	Initialize_Array();
+	Initialize_Array("reset");

 	TitanPanelButton_UpdateButton(TITAN_GOLD_ID)

@@ -792,7 +783,7 @@ end
 local function ShowMenuButtons(faction, level)
 	-- create the list and sort by alpha with server
 	local list_alpha = {}
-	for index, money in pairs(TitanSettings.Players) do
+	for index, money in TitanUtils_PlayerIter() do
 		local char = EvalIndexInfo(index)
 		if char.valid and char.faction == faction then
 			table.insert(list_alpha, index);
@@ -813,14 +804,26 @@ local function ShowMenuButtons(faction, level)

 		Titan_Menu.AddSelectorGeneric(level, toon,
 			function(data)
-				local toon_info = TitanSettings.Players[data.c_name].Info ---@class CharInfo
-				local toon_gold = toon_info[TITAN_GOLD_ID] ---@class GoldData
-				return toon_gold.show
+				local res = false
+				local result = ""
+				local toon_gold ---@class GoldData
+				result, toon_gold = TitanUtils_GetProfileInfo(data.c_name, "Gold", false)
+				if result == "found" and toon_gold ~= nil then
+					res = toon_gold.show
+				else
+					-- !? something bad happened...
+				end
+				return res
 			end,
 			function(data)
-				local toon_info = TitanSettings.Players[data.c_name].Info ---@class CharInfo
-				local toon_gold = toon_info[TITAN_GOLD_ID] ---@class GoldData
-				toon_gold.show = not toon_gold.show
+				local result = ""
+				local toon_gold ---@class GoldData
+				result, toon_gold = TitanUtils_GetProfileInfo(data.c_name, "Gold", false)
+				if result == "found" and toon_gold ~= nil then
+					toon_gold.show = not toon_gold.show
+				else
+					-- !? do nothing
+				end
 				TitanPanelButton_UpdateButton(TITAN_GOLD_ID);
 			end,
 			{ c_name = toon }
@@ -836,8 +839,6 @@ local function GeneratorFunction(owner, rootDescription)
 	do           -- next level options
 		-- NameAsc | GoldAsc | GoldDec [Ascend, Descend]
 		local disp = { -- selectors using the same option - label, value
---			{ L["TITAN_GOLD_TOGGLE_SORT_GOLD"], false },
---			{ L["TITAN_GOLD_TOGGLE_SORT_NAME"], "true" },
 			{ "Sort by Name", "NameAsc" },
 			{ "Sort by Gold Ascending", "GoldAsc" },
 			{ "Sort by Gold Descending", "GoldDec" },
@@ -896,7 +897,6 @@ local function GeneratorFunction(owner, rootDescription)
 	Titan_Menu.AddDivider(root)

 	local opts_show = Titan_Menu.AddButton(root, L["TITAN_GOLD_SHOW_PLAYER"])
---		.." : "..L["TITAN_GOLD_FACTION_PLAYER_ALLY"])
 	do
 	local opts_alliance = Titan_Menu.AddButton(opts_show, L["TITAN_GOLD_FACTION_PLAYER_ALLY"])
 		ShowMenuButtons(TITAN_ALLIANCE, opts_alliance)
@@ -906,7 +906,7 @@ local function GeneratorFunction(owner, rootDescription)
 		ShowMenuButtons(TITAN_HORDE, opts_horde)
 	end

---	Titan_Menu.AddCommand(root, id, L["TITAN_GOLD_CLEAR_DATA_TEXT"], TitanGold_ClearDB)
+	Titan_Menu.AddCommand(root, id, L["TITAN_GOLD_CLEAR_DATA_TEXT"], TitanGold_ClearDB)
 	Titan_Menu.AddCommand(root, id, L["TITAN_GOLD_RESET_SESS_TEXT"], ResetSession)
 end

@@ -994,7 +994,7 @@ end
 ---local When shown, register needed events and start timer for gold per hour
 ---@param self Button
 local function OnShow(self)
-	Initialize_Array()
+	Initialize_Array("set")
 	self:RegisterEvent("PLAYER_MONEY")

 	if TitanGetVar(TITAN_GOLD_ID, "DisplayGoldPerHour") then
@@ -1013,7 +1013,7 @@ local function OnShow(self)
 	Titan_Debug.Out('gold', 'flow', msg)
 end

----local When shown, unregister needed events and stop timer for gold per hour
+---local When hidden, unregister needed events and stop timer for gold per hour
 ---@param self Button
 local function OnHide(self)
 	self:UnregisterEvent("PLAYER_MONEY");
@@ -1083,25 +1083,29 @@ function TitanGold.GetInfo(player, add_label)
 		label = ""
 	end

+	local result = ""
+	local toon_gold ---@class GoldData
+	result, toon_gold = TitanUtils_GetProfileInfo(player, "Gold", false)
 	local character, charserver, is_custom = TitanUtils_ParseName(player)
-	if is_custom then
-		res = L["TITAN_PANEL_NA"]
+	if result == "is_custom" then
+		res = L["TITAN_PANEL_NA"] .. " - Custom profile."
 	elseif _G[TITAN_BUTTON]:IsShown() then
-		local toon_gold = nil
-		if TitanSettings.Players[player]
-			and TitanSettings.Players[player].Info
-			and TitanSettings.Players[player].Info[TITAN_GOLD_ID] then
-			toon_gold = TitanSettings.Players[player].Info[TITAN_GOLD_ID] ---@class GoldData
-			res = NiceCash(toon_gold.gold, true, false)
+		if result == "found" then
+			if toon_gold == nil then
+				res = L["TITAN_PANEL_NA"] .. " - Data empty!?."
+			else
+				res = NiceCash(toon_gold.gold, true, false)
+			end
 		else
-			res = L["TITAN_PANEL_NA"].." - Not logged in yet with Gold enabled."
+			res = L["TITAN_PANEL_NA"] .. " - Not logged in yet with Gold enabled."
 		end
 	else
 		res = L["TITAN_PANEL_MENU_DISABLED"]
 	end

-	return label..res
+	return label .. res
 end
+
 ---local Create required Gold frames
 local function Create_Frames()
 	if _G[TITAN_BUTTON] then