From 0b02b7baf03828e9df01fa9ec6cbb1e74ef66afe Mon Sep 17 00:00:00 2001
From: Rye Mutt <rye@alchemyviewer.org>
Date: Sat, 6 Jan 2024 02:57:51 -0500
Subject: [PATCH] Add experimental new chat history formatting

---
 indra/llui/llstyle.cpp                        |   3 +-
 indra/llui/llstyle.h                          |   1 +
 indra/llui/lltextbase.cpp                     |   5 +-
 .../newview/app_settings/settings_alchemy.xml |  44 ++++++
 indra/newview/llchathistory.cpp               | 127 +++++++++++++++---
 .../newview/llfloaterconversationpreview.cpp  |   4 +-
 indra/newview/llfloaterimsessiontab.cpp       |  28 ++--
 indra/newview/skins/alchemy/colors.xml        |   3 +
 indra/newview/skins/default/colors.xml        |   9 ++
 .../xui/en/menu_im_session_showmodes.xml      |  10 ++
 10 files changed, 205 insertions(+), 29 deletions(-)

diff --git a/indra/llui/llstyle.cpp b/indra/llui/llstyle.cpp
index bb731f4f7e2..ee372f94432 100644
--- a/indra/llui/llstyle.cpp
+++ b/indra/llui/llstyle.cpp
@@ -41,7 +41,8 @@ LLStyle::Params::Params()
 	font("font", LLFontGL::getFontMonospace()),
 	image("image"),
 	link_href("href"),
-	is_link("is_link")
+	is_link("is_link"),
+	use_default_link_style("use_default_link_style", true)
 {}
 
 
diff --git a/indra/llui/llstyle.h b/indra/llui/llstyle.h
index f978d3a2217..4466b0e0ddf 100644
--- a/indra/llui/llstyle.h
+++ b/indra/llui/llstyle.h
@@ -48,6 +48,7 @@ class LLStyle : public LLRefCount
 		Optional<LLUIImage*>			image;
 		Optional<std::string>			link_href;
 		Optional<bool>					is_link;
+		Optional<bool>					use_default_link_style;
 		Params();
 	};
 	LLStyle(const Params& p = Params());
diff --git a/indra/llui/lltextbase.cpp b/indra/llui/lltextbase.cpp
index 26a0e1b74b2..f70f26f880f 100644
--- a/indra/llui/lltextbase.cpp
+++ b/indra/llui/lltextbase.cpp
@@ -2291,7 +2291,10 @@ void LLTextBase::appendTextImpl(const std::string &new_text, const LLStyle::Para
 			end = match.getEnd()+1;
 
 			LLStyle::Params link_params(style_params);
-			link_params.overwriteFrom(match.getStyle());
+			if (link_params.use_default_link_style)
+			{
+				link_params.overwriteFrom(match.getStyle());
+			}
 
 			// output the text before the Url
 			if (start > 0)
diff --git a/indra/newview/app_settings/settings_alchemy.xml b/indra/newview/app_settings/settings_alchemy.xml
index fb1e0524e3a..b63c1825087 100644
--- a/indra/newview/app_settings/settings_alchemy.xml
+++ b/indra/newview/app_settings/settings_alchemy.xml
@@ -1756,5 +1756,49 @@
 			<key>Value</key>
 			<integer>0</integer>
 		</map>
+		<key>AlchemyFancyChatDivider</key>
+		<map>
+			<key>Comment</key>
+			<string>Divider style for fancy chat</string>
+			<key>Persist</key>
+			<integer>1</integer>
+			<key>Type</key>
+			<string>String</string>
+			<key>Value</key>
+			<string> │ </string>
+		</map>
+		<key>AlchemyFancyChatNameWidth</key>
+		<map>
+			<key>Comment</key>
+			<string>Width of name field in fancy chat</string>
+			<key>Persist</key>
+			<integer>1</integer>
+			<key>Type</key>
+			<string>S32</string>
+			<key>Value</key>
+			<integer>18</integer>
+		</map>
+		<key>AlchemyPlainChatNameBold</key>
+		<map>
+			<key>Comment</key>
+			<string>If true, names will be bold in plain text chat</string>
+			<key>Persist</key>
+			<integer>1</integer>
+			<key>Type</key>
+			<string>Boolean</string>
+			<key>Value</key>
+			<integer>0</integer>
+		</map>
+		<key>AlchemyChatHistoryStyle</key>
+		<map>
+			<key>Comment</key>
+			<string>0 - Expanded 1 - Plain 2 - Fancy Plain</string>
+			<key>Persist</key>
+			<integer>1</integer>
+			<key>Type</key>
+			<string>S32</string>
+			<key>Value</key>
+			<integer>0</integer>
+		</map>
 	</map>
 </llsd>
diff --git a/indra/newview/llchathistory.cpp b/indra/newview/llchathistory.cpp
index f771b0c9aee..dfaaa8f2c65 100644
--- a/indra/newview/llchathistory.cpp
+++ b/indra/newview/llchathistory.cpp
@@ -1055,7 +1055,10 @@ class LLChatHistoryHeader: public LLPanel
 private:
 	void setTimeField(const LLChat& chat)
 	{
-		LLTextBox* time_box = getChild<LLTextBox>("time_box");
+		LLTextBox* time_box = mTimeBoxTextBox;
+
+		static LLUIColor timestamp_color = LLUIColorTable::instance().getColor("ChatHeaderTimestampColor"); // <alchemy/>
+		time_box->setColor(timestamp_color); // <alchemy/>
 
 		LLRect rect_before = time_box->getRect();
 
@@ -1168,7 +1171,7 @@ LLChatHistory::LLChatHistory(const LLChatHistory::Params& p)
 	editor_params.rect = getLocalRect();
 	editor_params.follows.flags = FOLLOWS_ALL;
 	editor_params.enabled = false; // read only
-	editor_params.show_context_menu = "true";
+	editor_params.show_context_menu = true;
 	editor_params.trusted_content = false;
 	mEditor = LLUICtrlFactory::create<LLTextEditor>(editor_params, this);
 	mEditor->setIsFriendCallback(LLAvatarActions::isFriend);
@@ -1277,6 +1280,7 @@ void LLChatHistory::clear()
 {
 	mLastFromName.clear();
 	mEditor->clear();
+	mEditor->blockUndo(); // AL:LL:WTF: why is chat history a text editor with an undo stack...
 	mLastFromID = LLUUID::null;
 }
 
@@ -1285,7 +1289,8 @@ static LLTrace::BlockTimerStatHandle FTM_APPEND_MESSAGE("Append Chat Message");
 void LLChatHistory::appendMessage(const LLChat& chat, const LLSD &args, const LLStyle::Params& input_append_params)
 {
 	LL_RECORD_BLOCK_TIME(FTM_APPEND_MESSAGE);
-	bool use_plain_text_chat_history = args["use_plain_text_chat_history"].asBoolean();
+	bool use_plain_text_chat_history = args["chat_history_style"].asInteger() >= 1;
+	bool use_irssi_text_chat_history = args["chat_history_style"].asInteger() >= 2;
 	bool square_brackets = false; // square brackets necessary for a system messages
 
 	llassert(mEditor);
@@ -1327,10 +1332,10 @@ void LLChatHistory::appendMessage(const LLChat& chat, const LLSD &args, const LL
 	}
 
 	LLColor4 txt_color = LLUIColorTable::instance().getColor("White");
-	LLColor4 name_color(txt_color);
+	LLColor4 name_color = LLUIColorTable::instance().getColor("ChatHeaderDisplayNameColor"); // <alchemy/>
 
 	LLViewerChat::getChatColor(chat,txt_color);
-	LLFontGL* fontp = LLViewerChat::getChatFont();	
+	LLFontGL* fontp = LLViewerChat::getChatFont();
 	std::string font_name = LLFontGL::nameFromFont(fontp);
 	std::string font_size = LLFontGL::sizeFromFont(fontp);	
 
@@ -1391,17 +1396,23 @@ void LLChatHistory::appendMessage(const LLChat& chat, const LLSD &args, const LL
 	}
 
 	bool prependNewLineState = mEditor->getText().size() != 0;
+	static LLCachedControl<S32> name_column(gSavedSettings, "AlchemyFancyChatNameWidth", 18);
+	static LLCachedControl<bool> alchemyPlainChatNameBold(gSavedSettings, "AlchemyPlainChatNameBold", false);
+	static LLCachedControl<std::string> alchemyFancyChatDivider(gSavedSettings, "AlchemyFancyChatDivider", " | ");
+	static LLUIColor fancy_chat_divider_color = LLUIColorTable::instance().getColor("AlchemyFancyChatDividerColor");
 
 	// compact mode: show a timestamp and name
 	if (use_plain_text_chat_history)
 	{
-		square_brackets = chat.mSourceType == CHAT_SOURCE_SYSTEM;
+		square_brackets = chat.mSourceType == CHAT_SOURCE_SYSTEM && !use_irssi_text_chat_history;
 
-		LLStyle::Params timestamp_style(body_message_params);
+		name_params.color(fancy_chat_divider_color);
+		name_params.readonly_color(fancy_chat_divider_color);
 
 		// out of the timestamp
 		if (args["show_time"].asBoolean())
 		{
+			LLStyle::Params timestamp_style(body_message_params);
 			if (!message_from_log)
 			{
 				LLColor4 timestamp_color = LLUIColorTable::instance().getColor("ChatTimestampColor");
@@ -1441,14 +1452,38 @@ void LLChatHistory::appendMessage(const LLChat& chat, const LLSD &args, const LL
 				// set the link for the object name to be the objectim SLapp
 				// (don't let object names with hyperlinks override our objectim Url)
 				LLStyle::Params link_params(body_message_params);
-				LLColor4 link_color = LLUIColorTable::instance().getColor("HTMLLinkColor");
+				static LLUIColor link_color = LLUIColorTable::instance().getColor("HTMLLinkColor");
 				link_params.color = link_color;
 				link_params.readonly_color = link_color;
 				link_params.is_link = true;
 				link_params.link_href = url;
 
-				mEditor->appendText(chat.mFromName + delimiter, prependNewLineState, link_params);
-				prependNewLineState = false;
+				if (use_irssi_text_chat_history)
+				{
+					if (irc_me)
+					{
+						mEditor->appendText("<" + std::string(name_column - 1, ' ') + "*>", prependNewLineState, link_params);
+						prependNewLineState = false;
+						mEditor->appendText(alchemyFancyChatDivider, prependNewLineState, name_params);
+						mEditor->appendText(chat.mFromName + delimiter, prependNewLineState, link_params);
+					}
+					else
+					{
+						LLWString from_text = utf8string_to_wstring(chat.mFromName);
+						S32 i = from_text.length();
+						if (i > name_column) from_text.erase(name_column);
+						else if (i < name_column) from_text = LLWString(name_column - i, ' ') + from_text;
+
+						mEditor->appendText("<" + wstring_to_utf8str(from_text) + ">", prependNewLineState, link_params);
+						prependNewLineState = false;
+						mEditor->appendText(alchemyFancyChatDivider, prependNewLineState, name_params);
+					}
+				}
+				else
+				{
+					mEditor->appendText(chat.mFromName + delimiter, prependNewLineState, link_params);
+					prependNewLineState = false;
+				}
 			}
 //			else if ( chat.mFromName != SYSTEM_FROM && chat.mFromID.notNull() && !message_from_log && chat.mSourceType != CHAT_SOURCE_REGION)
 // [RLVa:KB] - Checked: 2010-04-22 (RLVa-1.2.0f) | Added: RLVa-1.2.0f
@@ -1458,13 +1493,47 @@ void LLChatHistory::appendMessage(const LLChat& chat, const LLSD &args, const LL
 				LLStyle::Params link_params(body_message_params);
 				link_params.overwriteFrom(LLStyleMap::instance().lookupAgent(chat.mFromID));
 
-				// Add link to avatar's inspector and delimiter to message.
-				mEditor->appendText(std::string(link_params.link_href), prependNewLineState, link_params);
-				prependNewLineState = false;
-				mEditor->appendText(delimiter, prependNewLineState, body_message_params);
+				if (use_irssi_text_chat_history)
+				{
+					if (irc_me)
+					{
+						mEditor->appendText("<" + std::string(name_column - 1, ' ') + "*>", prependNewLineState, link_params);
+						prependNewLineState = false;
+						mEditor->appendText(alchemyFancyChatDivider, prependNewLineState, name_params);
+						LLStyle::Params link_params2(body_message_params);
+						link_params2.use_default_link_style = false;
+						link_params2.link_href = link_params.link_href;
+						mEditor->appendText(std::string(link_params.link_href) + delimiter, prependNewLineState, link_params2);
+					}
+					else 
+					{
+						LLWString from_text = utf8string_to_wstring(chat.mFromName);
+						std::string text_padding;
+						S32 i = from_text.length();
+						if (i >= name_column) from_text = from_text.substr(0, name_column);
+						else if (i < name_column) text_padding = std::string(name_column - i, ' ');
+
+						mEditor->appendText("<" + text_padding + "[" + std::string(link_params.link_href) + " " + wstring_to_utf8str(from_text) + "]>", prependNewLineState, link_params);
+						prependNewLineState = false;
+						mEditor->appendText(alchemyFancyChatDivider, false, name_params);
+					}
+				}
+				else
+				{
+					// Add link to avatar's inspector and delimiter to message.
+					mEditor->appendText(std::string(link_params.link_href), prependNewLineState, link_params);
+					prependNewLineState = false;
+					mEditor->appendText(delimiter, prependNewLineState, body_message_params);
+				}
 			}
             else if (teleport_separator)
             {
+				if(use_irssi_text_chat_history)
+				{
+					mEditor->appendText("<" + std::string(name_column - 1, ' ') + "*>", prependNewLineState, body_message_params);
+					prependNewLineState = false;
+					mEditor->appendText(alchemyFancyChatDivider, prependNewLineState, name_params);
+				}
                 std::string tp_text = LLTrans::getString("teleport_preamble_compact_chat");
                 mEditor->appendText(tp_text + " <nolink>" + chat.mFromName + "</nolink>",
                     prependNewLineState, body_message_params);
@@ -1472,9 +1541,33 @@ void LLChatHistory::appendMessage(const LLChat& chat, const LLSD &args, const LL
             }
 			else
 			{
-				mEditor->appendText("<nolink>" + chat.mFromName + "</nolink>" + delimiter,
-						prependNewLineState, body_message_params);
-				prependNewLineState = false;
+				if (use_irssi_text_chat_history)
+				{
+					if (irc_me)
+					{
+						mEditor->appendText("<" + std::string(name_column - 1, ' ') + "*>", prependNewLineState, body_message_params);
+						prependNewLineState = false;
+						mEditor->appendText(alchemyFancyChatDivider, prependNewLineState, name_params);
+						mEditor->appendText(std::string("<nolink>").append(chat.mFromName).append("</nolink>").append(delimiter), prependNewLineState, body_message_params);
+					}
+					else
+					{
+						LLWString from_text = utf8string_to_wstring(chat.mFromName);
+						S32 i = from_text.length();
+						if (i >= name_column) from_text = from_text.substr(0, name_column);
+						else if (i < name_column) from_text = LLWString(name_column - i, ' ') + from_text;
+
+						mEditor->appendText("<<nolink>" + wstring_to_utf8str(from_text) + "</nolink>>", prependNewLineState, body_message_params);
+						prependNewLineState = false;
+						mEditor->appendText(alchemyFancyChatDivider, prependNewLineState, name_params);
+					}
+				}
+				else
+				{
+					mEditor->appendText("<nolink>" + chat.mFromName + "</nolink>" + delimiter,
+							prependNewLineState, body_message_params);
+					prependNewLineState = false;
+				}
 			}
 		}
 	}
diff --git a/indra/newview/llfloaterconversationpreview.cpp b/indra/newview/llfloaterconversationpreview.cpp
index d76793bfd29..74e553db748 100644
--- a/indra/newview/llfloaterconversationpreview.cpp
+++ b/indra/newview/llfloaterconversationpreview.cpp
@@ -251,8 +251,8 @@ void LLFloaterConversationPreview::showHistory()
 		}
 
 		LLSD chat_args;
-		chat_args["use_plain_text_chat_history"] =
-						gSavedSettings.getBOOL("PlainTextChatHistory");
+		chat_args["chat_history_style"] =
+						gSavedSettings.getS32("AlchemyChatHistoryStyle");
 		chat_args["show_time"] = gSavedSettings.getBOOL("IMShowTime");
 		chat_args["show_names_for_p2p_conv"] = gSavedSettings.getBOOL("IMShowNamesForP2PConv");
 
diff --git a/indra/newview/llfloaterimsessiontab.cpp b/indra/newview/llfloaterimsessiontab.cpp
index db94f163ce6..b6c1481d324 100644
--- a/indra/newview/llfloaterimsessiontab.cpp
+++ b/indra/newview/llfloaterimsessiontab.cpp
@@ -463,8 +463,7 @@ void LLFloaterIMSessionTab::appendMessage(const LLChat& chat, const LLSD &args)
 		tmp_chat.mFromName = chat.mFromName;
 		LLSD chat_args;
 		if (args) chat_args = args;
-		chat_args["use_plain_text_chat_history"] =
-				gSavedSettings.getBOOL("PlainTextChatHistory");
+		chat_args["chat_history_style"] = gSavedSettings.getS32("AlchemyChatHistoryStyle");
 		chat_args["show_time"] = gSavedSettings.getBOOL("IMShowTime");
 		chat_args["show_names_for_p2p_conv"] =
 				!mIsP2PChat || gSavedSettings.getBOOL("IMShowNamesForP2PConv");
@@ -663,9 +662,14 @@ void LLFloaterIMSessionTab::onIMSessionMenuItemClicked(const LLSD& userdata)
 {
 	std::string item = userdata.asString();
 
-	if (item == "compact_view" || item == "expanded_view")
+	if (item == "fancy_compact_view" || item == "compact_view" || item == "expanded_view")
 	{
-		gSavedSettings.setBOOL("PlainTextChatHistory", item == "compact_view");
+		S32 newset = 0;
+		if (item == "fancy_compact_view")
+			newset = 2;
+		else if (item == "compact_view")
+			newset = 1;
+		gSavedSettings.setS32("AlchemyChatHistoryStyle", newset);
 	}
 	else
 	{
@@ -679,9 +683,17 @@ void LLFloaterIMSessionTab::onIMSessionMenuItemClicked(const LLSD& userdata)
 bool LLFloaterIMSessionTab::onIMCompactExpandedMenuItemCheck(const LLSD& userdata)
 {
 	std::string item = userdata.asString();
-	bool is_plain_text_mode = gSavedSettings.getBOOL("PlainTextChatHistory");
-
-	return is_plain_text_mode? item == "compact_view" : item == "expanded_view";
+	S32 chat_history_mode = gSavedSettings.getS32("AlchemyChatHistoryStyle");
+	switch (chat_history_mode)
+	{
+	default:
+	case 0:
+		return item == "expanded_view";
+	case 1:
+		return item == "compact_view";
+	case 2:
+		return item == "fancy_compact_view";
+	}
 }
 
 
@@ -694,7 +706,7 @@ bool LLFloaterIMSessionTab::onIMShowModesMenuItemCheck(const LLSD& userdata)
 bool LLFloaterIMSessionTab::onIMShowModesMenuItemEnable(const LLSD& userdata)
 {
 	std::string item = userdata.asString();
-	bool plain_text = gSavedSettings.getBOOL("PlainTextChatHistory");
+	bool plain_text = gSavedSettings.getS32("AlchemyChatHistoryStyle") >= 1;
 	bool is_not_names = (item != "IMShowNamesForP2PConv");
 	return (plain_text && (is_not_names || mIsP2PChat));
 }
diff --git a/indra/newview/skins/alchemy/colors.xml b/indra/newview/skins/alchemy/colors.xml
index c6f81aba602..7dd8d75a9a4 100644
--- a/indra/newview/skins/alchemy/colors.xml
+++ b/indra/newview/skins/alchemy/colors.xml
@@ -1157,4 +1157,7 @@
   <color
     name="MusicTickerEmphasisColor" 
     reference="AlBlue"/>
+  <color
+     name="AlchemyFancyChatDividerColor"
+     reference="AlchemyText" />
 </colors>
diff --git a/indra/newview/skins/default/colors.xml b/indra/newview/skins/default/colors.xml
index 9ea7722dc21..4fa41878515 100644
--- a/indra/newview/skins/default/colors.xml
+++ b/indra/newview/skins/default/colors.xml
@@ -1164,4 +1164,13 @@
    <color
     name="MusicTickerEmphasisColor" 
     reference="EmphasisColor"/>
+  <color
+   name="ChatHeaderDisplayNameColor"
+   reference="White" />
+  <color
+   name="ChatHeaderTimestampColor"
+   reference="White" />
+    <color
+     name="AlchemyFancyChatDividerColor"
+     reference="White" />
 </colors>
diff --git a/indra/newview/skins/default/xui/en/menu_im_session_showmodes.xml b/indra/newview/skins/default/xui/en/menu_im_session_showmodes.xml
index 722a927fd79..880791c5e79 100644
--- a/indra/newview/skins/default/xui/en/menu_im_session_showmodes.xml
+++ b/indra/newview/skins/default/xui/en/menu_im_session_showmodes.xml
@@ -3,6 +3,16 @@
  name="menu_modes"
  left="0" bottom="0" visible="false"
  mouse_opaque="false">
+	<menu_item_check
+       label="IRC view"
+       name="fancy_compact_view">
+		<menu_item_check.on_click
+		   function="IMSession.Menu.Action"
+		   parameter="fancy_compact_view"/>
+		<menu_item_check.on_check
+		   function="IMSession.Menu.CompactExpandedModes.CheckItem"
+		   parameter="fancy_compact_view"/>
+	</menu_item_check>
     <menu_item_check
        label="Compact view"
        name="compact_view">
-- 
GitLab