← Back to team overview

linuxdcpp-team team mailing list archive

[Branch ~dcplusplus-team/dcplusplus/trunk] Rev 2881: Format chat links

 

------------------------------------------------------------
revno: 2881
committer: poy <poy@xxxxxxxxxx>
branch nick: trunk
timestamp: Sun 2012-03-18 16:21:56 +0100
message:
  Format chat links
modified:
  changelog.txt
  dcpp/ChatMessage.cpp
  win32/AspectChat.h
  win32/HtmlToRtf.cpp
  win32/HubFrame.cpp


--
lp:dcplusplus
https://code.launchpad.net/~dcplusplus-team/dcplusplus/trunk

Your team Dcplusplus-team is subscribed to branch lp:dcplusplus.
To unsubscribe from this branch go to https://code.launchpad.net/~dcplusplus-team/dcplusplus/trunk/+edit-subscription
=== modified file 'changelog.txt'
--- changelog.txt	2012-03-11 16:36:44 +0000
+++ changelog.txt	2012-03-18 15:21:56 +0000
@@ -25,6 +25,7 @@
 * Fix discrepancies in the /conn chat command (poy)
 * Update boost to version 1.49
 * [L#947895] Move the "follow redirect" command to inline chat links (poy)
+* Format chat links (poy)
 
 -- 0.791 2012-01-14 --
 * Update translations

=== modified file 'dcpp/ChatMessage.cpp'
--- dcpp/ChatMessage.cpp	2012-03-11 18:06:18 +0000
+++ dcpp/ChatMessage.cpp	2012-03-18 15:21:56 +0000
@@ -26,8 +26,14 @@
 #include "SimpleXML.h"
 #include "Util.h"
 
+#include <boost/range/adaptor/reversed.hpp>
+
 namespace dcpp {
 
+using std::make_pair;
+using std::map;
+using std::pair;
+
 ChatMessage::ChatMessage(const string& text, const OnlineUser* from,
 	const OnlineUser* to, const OnlineUser* replyTo,
 	bool thirdPerson, time_t messageTimestamp) :
@@ -91,7 +97,101 @@
 	}
 
 	message += tmp;
-	htmlMessage += addSpan("text", tmp, Util::emptyString) + "</span>";
+
+	/* format the message; this will involve adding custom tags. this table holds the tags to be
+	added along with their position. */
+	struct Tag { string s; bool opening; size_t otherTag; };
+	map<size_t, Tag> tags;
+
+	/* link formatting - optimize the lookup a bit by using the fact that every link identifier
+	(except www ones) contains a colon. */
+	/// @todo add support for spaces within links enclosed by brackets / quotes (see URI RFC)
+	/// @todo friendly magnet links
+	auto addLink = [&tmp, &xmlTmp, &tags](size_t begin, size_t end) {
+		Tag openingTag = { "<a href=\"" + SimpleXML::escape(tmp.substr(begin, end - begin), xmlTmp, true) + "\">", true, end },
+			closingTag = { "</a>", false, begin };
+		tags.emplace(begin, std::move(openingTag));
+		tags.emplace(end, std::move(closingTag));
+	};
+
+	static const string delimiters = " \t\r\n<>\"";
+
+	i = 0;
+	size_t begin, end;
+	const auto n = tmp.size();
+	while((i = tmp.find(':', i)) != string::npos) {
+
+		if((begin = tmp.find_last_of(delimiters, i)) == string::npos) begin = 0; else ++begin;
+		if((end = tmp.find_first_of(delimiters, i + 1)) == string::npos) end = n;
+
+		if(i > 0 &&
+			(i + 4 < n && tmp[i + 1] == '/' && tmp[i + 2] == '/') || // "http://";, etc
+			(i >= begin + 6 && !tmp.compare(begin, 6, "magnet")) ||
+			(i >= begin + 6 && !tmp.compare(begin, 6, "mailto")))
+		{
+			addLink(begin, end);
+			i = end;
+
+		} else {
+			++i;
+		}
+	}
+
+	// check for www links.
+	i = 0;
+	while((i = tmp.find("www.", i)) != string::npos) {
+		if(i + 5 <= n && (i == 0 || delimiters.find(tmp[i - 1]) != string::npos)) {
+			if((end = tmp.find_first_of(delimiters, i + 4)) == string::npos) end = n;
+			if(i + 5 <= end) {
+				addLink(i, end);
+				i = end;
+			}
+		}
+		i += 5;
+	}
+
+	htmlMessage += "<span id=\"text\">";
+
+	/* write tags and the message data. support entangled tags, such as:
+	<a> <b> </a> </b> -> <a> <b> </b></a><b> </b> */
+
+	size_t pos = 0;
+	vector<size_t> openTags;
+
+	for(auto& tag: tags) {
+		htmlMessage += SimpleXML::escape(tmp.substr(pos, tag.first - pos), xmlTmp, false);
+		pos = tag.first;
+
+		if(tag.second.opening) {
+			htmlMessage += tag.second.s;
+			openTags.push_back(tag.first);
+
+		} else {
+			if(openTags.back() == tag.second.otherTag) {
+				// common case: no entangled tag; just write the closing tag.
+				htmlMessage += tag.second.s;
+				openTags.pop_back();
+
+			} else {
+				// there are entangled tags: write closing & opening tags of currently open tags.
+				for(auto key: openTags | boost::adaptors::reversed) { if(key >= tag.second.otherTag) {
+					htmlMessage += tags[tags[key].otherTag].s;
+				} }
+				openTags.erase(remove(openTags.begin(), openTags.end(), tag.second.otherTag), openTags.end());
+				for(auto key: openTags) { if(key >= tag.second.otherTag) {
+					htmlMessage += tags[key].s;
+				} }
+			}
+		}
+	}
+
+	if(pos != n) {
+		htmlMessage += SimpleXML::escape(tmp.substr(pos), xmlTmp, false);
+	}
+
+	htmlMessage += "</span></span>";
+
+	dcdebug("html: %s\n", htmlMessage.c_str());
 
 	/// @todo send this to plugins
 }

=== modified file 'win32/AspectChat.h'
--- win32/AspectChat.h	2012-03-11 18:06:18 +0000
+++ win32/AspectChat.h	2012-03-18 15:21:56 +0000
@@ -52,7 +52,6 @@
 			cs.style |= ES_READONLY;
 			chat = dwt::WidgetCreator<RichTextBox>::create(parent, cs);
 			chat->setTextLimit(65536);
-			WinUtil::handleDblClicks(chat);
 		}
 
 		{

=== modified file 'win32/HtmlToRtf.cpp'
--- win32/HtmlToRtf.cpp	2012-03-11 18:06:18 +0000
+++ win32/HtmlToRtf.cpp	2012-03-18 15:21:56 +0000
@@ -26,6 +26,7 @@
 #include <dcpp/debug.h>
 #include <dcpp/Flags.h>
 #include <dcpp/ScopedFunctor.h>
+#include <dcpp/SettingsManager.h>
 #include <dcpp/SimpleXML.h>
 #include <dcpp/StringTokenizer.h>
 #include <dcpp/Text.h>
@@ -115,6 +116,7 @@
 			auto& context = contexts.back();
 			context.link = link;
 			context.setFlag(Context::Underlined);
+			context.textColor = addColor(SETTING(LINK_COLOR)); /// @todo move to styles
 		}
 	}
 
@@ -218,6 +220,10 @@
 	if(isSet(Underlined)) { ret += "\\ul"; }
 
 	ret += " ";
+
+	// add an invisible space; otherwise link formatting may get lost...
+	if(!link.empty()) { ret += "{\\v  }"; }
+
 	return Text::toT(ret);
 }
 

=== modified file 'win32/HubFrame.cpp'
--- win32/HubFrame.cpp	2012-03-11 18:06:18 +0000
+++ win32/HubFrame.cpp	2012-03-18 15:21:56 +0000
@@ -903,8 +903,7 @@
 			string tmp;
 			addStatus(msgT, false);
 			/// @todo change to "javascript: external.redirect" when switching to an HTML control
-			addChatHTML("<span>*** </span><a href=\"redirect: " + SimpleXML::escape(line, tmp, true) +
-				"\" style=\"color: #" + Util::cssColor(SETTING(LINK_COLOR)) + ";\">" +
+			addChatHTML("<span>*** </span><a href=\"redirect: " + SimpleXML::escape(line, tmp, true) + "\">" +
 				SimpleXML::escape(msg, tmp, false) + "</a>");
 			addedChat(_T("*** ") + msgT);
 		}