← Back to team overview

linuxdcpp-team team mailing list archive

[Branch ~dcplusplus-team/dcplusplus/trunk] Rev 2877: switch to Rich Edit 4.1, handle friendly name links

 

------------------------------------------------------------
revno: 2877
committer: poy <poy@xxxxxxxxxx>
branch nick: trunk
timestamp: Sun 2012-03-11 17:34:55 +0100
message:
  switch to Rich Edit 4.1, handle friendly name links
modified:
  dwt/include/dwt/widgets/RichTextBox.h
  dwt/src/widgets/RichTextBox.cpp
  win32/HtmlToRtf.cpp
  win32/RichTextBox.cpp
  win32/RichTextBox.h
  win32/WinUtil.cpp
  win32/WinUtil.h


--
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 'dwt/include/dwt/widgets/RichTextBox.h'
--- dwt/include/dwt/widgets/RichTextBox.h	2012-01-29 18:05:27 +0000
+++ dwt/include/dwt/widgets/RichTextBox.h	2012-03-11 16:34:55 +0000
@@ -81,6 +81,7 @@
 		FontPtr font;
 		bool scrollBarHorizontallyFlag;
 		bool scrollBarVerticallyFlag;
+		int events; /// additional events caught by this control; see the EM_SETEVENTMASK doc.
 
 		/// Fills with default parameters
 		Seed();

=== modified file 'dwt/src/widgets/RichTextBox.cpp'
--- dwt/src/widgets/RichTextBox.cpp	2012-02-09 19:45:38 +0000
+++ dwt/src/widgets/RichTextBox.cpp	2012-03-11 16:34:55 +0000
@@ -44,19 +44,20 @@
 
 namespace dwt {
 
-const TCHAR RichTextBox::windowClass[] = RICHEDIT_CLASS;
+const TCHAR RichTextBox::windowClass[] = MSFTEDIT_CLASS;
 
 RichTextBox::Seed::Seed() :
 	BaseType::Seed(WS_CHILD | WS_TABSTOP | WS_VSCROLL | ES_LEFT | ES_AUTOVSCROLL | ES_MULTILINE | ES_NOHIDESEL),
 	font(0),
 	scrollBarHorizontallyFlag(false),
-	scrollBarVerticallyFlag(false)
+	scrollBarVerticallyFlag(false),
+	events(ENM_LINK)
 {
 }
 
 Dispatcher& RichTextBox::makeDispatcher() {
-	// Need to load up RichEdit library!
-	static LibraryLoader richEditLibrary(_T("riched20.dll"));
+	// msftedit is the DLL containing Rich Edit 4.1, available from XP SP1 onwards.
+	static LibraryLoader richEditLibrary(_T("msftedit.dll"));
 	return ChainingDispatcher::superClass<RichTextBox>();
 }
 
@@ -68,7 +69,7 @@
 
 	setScrollBarHorizontally(cs.scrollBarHorizontallyFlag);
 	setScrollBarVertically(cs.scrollBarVerticallyFlag);
-
+	sendMessage(EM_SETEVENTMASK, 0, cs.events);
 	sendMessage(EM_AUTOURLDETECT, FALSE);
 
 	/* unlike other common controls, Rich Edits ignore WM_PRINTCLIENT messages. as per

=== modified file 'win32/HtmlToRtf.cpp'
--- win32/HtmlToRtf.cpp	2012-03-05 20:48:24 +0000
+++ win32/HtmlToRtf.cpp	2012-03-11 16:34:55 +0000
@@ -22,6 +22,7 @@
 #include "HtmlToRtf.h"
 
 #include <boost/algorithm/string/trim.hpp>
+#include <boost/scoped_array.hpp>
 
 #include <dcpp/debug.h>
 #include <dcpp/Flags.h>
@@ -47,11 +48,14 @@
 		int fontSize;
 		size_t textColor; // index in the "colors" table
 		size_t bgColor; // index in the "colors" table
+		string link;
+
 		Context(dwt::RichTextBox* box, Parser& parser);
+
+		tstring getBegin() const;
+		tstring getEnd() const;
 	};
 
-	void write(Context& context);
-
 	size_t addFont(string&& font);
 	static int rtfFontSize(float px);
 	size_t addColor(COLORREF color);
@@ -78,7 +82,6 @@
 Parser::Parser(dwt::RichTextBox* box) {
 	// create a default context with the Rich Edit control's current formatting.
 	contexts.emplace_back(box, *this);
-	write(contexts.back());
 }
 
 void Parser::startTag(const string& name_, StringPairList& attribs, bool simple) {
@@ -93,7 +96,7 @@
 	}
 
 	contexts.push_back(contexts.back());
-	ScopedFunctor([this] { write(contexts.back()); });
+	ScopedFunctor([this] { ret += contexts.back().getBegin(); });
 
 	if(name == "b") {
 		contexts.back().setFlag(Context::Bold);
@@ -107,6 +110,16 @@
 		return;
 	}
 
+	if(name == "a") {
+		const auto& link = getAttrib(attribs, "href", 0);
+		if(!link.empty()) {
+			auto& context = contexts.back();
+			context.link = link;
+			context.setFlag(Context::Underlined);
+			/// @todo custom color
+		}
+	}
+
 	const auto& style = getAttrib(attribs, "style", 0);
 
 	enum { Declaration, Font, Decoration, TextColor, BgColor, Unknown } state = Declaration;
@@ -172,7 +185,7 @@
 }
 
 void Parser::endTag(const string& name) {
-	ret += _T("}");
+	ret += contexts.back().getEnd();
 	contexts.pop_back();
 }
 
@@ -193,14 +206,25 @@
 	bgColor = parser.addColor(box->getBgColor());
 }
 
-void Parser::write(Context& context) {
-	string header;
-	if(context.isSet(Context::Bold)) { header += "\\b"; }
-	if(context.isSet(Context::Italic)) { header += "\\i"; }
-	if(context.isSet(Context::Underlined)) { header += "\\ul"; }
-	ret += Text::toT("{\\f" + Util::toString(context.font) + "\\fs" + Util::toString(context.fontSize) +
-		"\\cf" + Util::toString(context.textColor) + "\\highlight" + Util::toString(context.bgColor) +
-		header + " ");
+tstring Parser::Context::getBegin() const {
+	string ret = "{";
+
+	if(!link.empty()) {
+		ret += "\\field{\\*\\fldinst HYPERLINK \"" + link + "\"}{\\fldrslt";
+	}
+
+	ret += "\\f" + Util::toString(font) + "\\fs" + Util::toString(fontSize) +
+		"\\cf" + Util::toString(textColor) + "\\highlight" + Util::toString(bgColor);
+	if(isSet(Bold)) { ret += "\\b"; }
+	if(isSet(Italic)) { ret += "\\i"; }
+	if(isSet(Underlined)) { ret += "\\ul"; }
+
+	ret += " ";
+	return Text::toT(ret);
+}
+
+tstring Parser::Context::getEnd() const {
+	return link.empty() ? _T("}") : _T("}}");
 }
 
 size_t Parser::addFont(string&& font) {

=== modified file 'win32/RichTextBox.cpp'
--- win32/RichTextBox.cpp	2012-01-22 20:27:14 +0000
+++ win32/RichTextBox.cpp	2012-03-11 16:34:55 +0000
@@ -21,13 +21,23 @@
 #include "RichTextBox.h"
 
 #include "ParamDlg.h"
+#include "WinUtil.h"
 
 RichTextBox::Seed::Seed() : 
 BaseType::Seed()
 {
 }
 
-RichTextBox::RichTextBox(dwt::Widget* parent) : BaseType(parent) {
+RichTextBox::RichTextBox(dwt::Widget* parent) : BaseType(parent), linkF(0) {
+}
+
+void RichTextBox::create(const Seed& seed) {
+	BaseType::create(seed);
+
+	if((seed.events & ENM_LINK) == ENM_LINK) {
+		onRaw([this](WPARAM, LPARAM lParam) { return handleLink(*reinterpret_cast<ENLINK*>(lParam)); },
+			dwt::Message(WM_NOTIFY, EN_LINK));
+	}
 }
 
 bool RichTextBox::handleMessage(const MSG& msg, LRESULT& retVal) {
@@ -69,12 +79,11 @@
 }
 
 tstring RichTextBox::findTextPopup() {
-	tstring param = Util::emptyStringT;
-	ParamDlg lineFind(this, T_("Search"), T_("Specify search string"), Util::emptyStringT, false);
+	ParamDlg lineFind(this, T_("Search"), T_("Specify search string"));
 	if(lineFind.run() == IDOK) {
-		param = lineFind.getValue();
+		return lineFind.getValue();
 	}
-	return param;
+	return Util::emptyStringT;
 }
 
 void RichTextBox::findTextNew() {
@@ -98,3 +107,39 @@
 	}
 	return false;
 }
+
+void RichTextBox::onLink(LinkF f) {
+	linkF = f;
+}
+
+LRESULT RichTextBox::handleLink(ENLINK& link) {
+	/* the control doesn't handle click events, just "mouse down" & "mouse up". so we have to make
+	sure the mouse hasn't moved between "down" & "up". */
+	static LPARAM clickPos = 0;
+
+	switch(link.msg) {
+	case WM_LBUTTONDOWN:
+		{
+			clickPos = link.lParam;
+			break;
+		}
+
+	case WM_LBUTTONUP:
+		{
+			if(link.lParam != clickPos)
+				break;
+
+			boost::scoped_array<TCHAR> buf(new TCHAR[link.chrg.cpMax - link.chrg.cpMin + 1]);
+			TEXTRANGE text = { link.chrg, buf.get() };
+			sendMessage(EM_GETTEXTRANGE, 0, reinterpret_cast<LPARAM>(&text));
+			if(!linkF || !linkF(buf.get())) {
+				WinUtil::parseLink(buf.get());
+			}
+			break;
+		}
+
+		/// @todo context menu on rbuttonup
+		/// @todo tooltip on mouseover
+	}
+	return 0;
+}

=== modified file 'win32/RichTextBox.h'
--- win32/RichTextBox.h	2012-01-13 20:55:20 +0000
+++ win32/RichTextBox.h	2012-03-11 16:34:55 +0000
@@ -25,10 +25,13 @@
 
 #include "forward.h"
 
-/// our rich text boxes that provide find functions
+/// our rich text boxes that provide find functions and handle links
 class RichTextBox : public dwt::RichTextBox {
 	typedef dwt::RichTextBox BaseType;
 	friend class dwt::WidgetCreator<RichTextBox>;
+
+	typedef std::function<bool (const tstring&)> LinkF;
+
 public:
 	typedef RichTextBox ThisType;
 	
@@ -41,6 +44,7 @@
 	};
 
 	explicit RichTextBox(dwt::Widget* parent);
+	void create(const Seed& seed);
 
 	bool handleMessage(const MSG& msg, LRESULT& retVal);
 
@@ -50,8 +54,14 @@
 	void findTextNew();
 	void findTextNext();
 
+	/// provides a chance to handle links differently
+	void onLink(LinkF f);
+
 private:
 	bool handleKeyDown(int c);
+	LRESULT handleLink(ENLINK& link);
+
+	LinkF linkF;
 };
 
 typedef RichTextBox::ObjectType RichTextBoxPtr;

=== modified file 'win32/WinUtil.cpp'
--- win32/WinUtil.cpp	2012-03-03 19:33:45 +0000
+++ win32/WinUtil.cpp	2012-03-11 16:34:55 +0000
@@ -516,11 +516,7 @@
 }
 
 void WinUtil::handleDblClicks(dwt::TextBoxBase* box) {
-	box->onLeftMouseDblClick([box](const dwt::MouseEvent &me) { return WinUtil::handleBoxDblClick(box, me); });
-}
-
-bool WinUtil::handleBoxDblClick(dwt::TextBoxBase* box, const dwt::MouseEvent& ev) {
-	return parseDBLClick(box->textUnderCursor(ev.pos));
+	box->onLeftMouseDblClick([box](const dwt::MouseEvent& me) { return parseLink(box->textUnderCursor(me.pos)); });
 }
 
 #define LINE2 _T("-- http://dcplusplus.sourceforge.net <DC++ ") _T(VERSIONSTRING) _T(">")
@@ -977,6 +973,7 @@
 			seed.style |= ES_MULTILINE;
 		seed.exStyle = WS_EX_TOOLWINDOW | WS_EX_NOACTIVATE | WS_EX_CLIENTEDGE;
 		seed.location.size.x = std::min(getParent()->getDesktopSize().width(), static_cast<long>(maxWidth * dwt::util::dpiFactor()));
+		seed.events |= ENM_REQUESTRESIZE;
 		create(seed);
 
 		const auto margins = sendMessage(EM_GETMARGINS);
@@ -984,7 +981,6 @@
 
 		// let the control figure out what the best size is.
 		onRaw([this, pt](WPARAM, LPARAM l) { return this->resize(l, pt); }, dwt::Message(WM_NOTIFY, EN_REQUESTRESIZE));
-		sendMessage(EM_SETEVENTMASK, 0, ENM_REQUESTRESIZE); ///@todo move to dwt
 		setText(text);
 	}
 
@@ -1378,7 +1374,7 @@
 	::ShellExecute(NULL, NULL, url.c_str(), NULL, NULL, SW_SHOWNORMAL);
 }
 
-bool WinUtil::parseDBLClick(const tstring& str) {
+bool WinUtil::parseLink(const tstring& str) {
 	auto url = Text::fromT(str);
 	string proto, host, port, file, query, fragment;
 	Util::decodeUrl(url, proto, host, port, file, query, fragment);

=== modified file 'win32/WinUtil.h'
--- win32/WinUtil.h	2012-01-29 18:05:27 +0000
+++ win32/WinUtil.h	2012-03-11 16:34:55 +0000
@@ -292,7 +292,7 @@
 
 	static bool getUCParams(dwt::Widget* parent, const UserCommand& cmd, ParamMap& params) noexcept;
 
-	static bool parseDBLClick(const tstring& aString);
+	static bool parseLink(const tstring& aString);
 	static void parseMagnetUri(const tstring& /*aUrl*/, bool aOverride = false);
 
 	static void help(dwt::Control* widget);
@@ -322,8 +322,6 @@
 	static dwt::IconPtr toolbarIcon(unsigned id);
 
 private:
-	static bool handleBoxDblClick(dwt::TextBoxBase* box, const dwt::MouseEvent& ev);
-
 	static void initUserMatching();
 	static void initHelpPath();