linuxdcpp-team team mailing list archive
-
linuxdcpp-team team
-
Mailing list archive
-
Message #05277
[Branch ~dcplusplus-team/dcplusplus/trunk] Rev 2845: preparations for HTML chat formatting by plugins
------------------------------------------------------------
revno: 2845
committer: poy <poy@xxxxxxxxxx>
branch nick: trunk
timestamp: Wed 2012-02-01 17:26:37 +0100
message:
preparations for HTML chat formatting by plugins
added:
dcpp/ChatMessage.cpp
win32/HtmlToRtf.cpp
win32/HtmlToRtf.h
modified:
dcpp/AdcHub.cpp
dcpp/ChatMessage.h
dcpp/ClientListener.h
dcpp/NmdcHub.cpp
dcpp/SimpleXML.cpp
dcpp/SimpleXMLReader.cpp
dcpp/SimpleXMLReader.h
help/chat_commands.html
help/settings_appearance.html
test/testxml.cpp
win32/AspectChat.h
win32/HubFrame.cpp
win32/HubFrame.h
win32/PrivateFrame.cpp
win32/PrivateFrame.h
win32/WinUtil.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 'dcpp/AdcHub.cpp'
--- dcpp/AdcHub.cpp 2012-01-27 22:19:56 +0000
+++ dcpp/AdcHub.cpp 2012-02-01 16:26:37 +0000
@@ -255,27 +255,22 @@
if(!from || from->getIdentity().noChat())
return;
- ChatMessage message = { c.getParam(0), from->getUser() };
+ decltype(from) to = nullptr, replyTo = nullptr;
string temp;
if(c.getParam("PM", 1, temp)) { // add PM<group-cid> as well
- auto ou = findUser(c.getTo());
- if(!ou)
- return;
- message.to = ou->getUser();
-
- ou = findUser(AdcCommand::toSID(temp));
- if(!ou)
- return;
- message.replyTo = ou->getUser();
+
+ to = findUser(c.getTo());
+ if(!to)
+ return;
+
+ replyTo = findUser(AdcCommand::toSID(temp));
+ if(!replyTo)
+ return;
}
- message.thirdPerson = c.hasFlag("ME", 1);
-
- if(c.getParam("TS", 1, temp))
- message.timestamp = Util::toInt64(temp);
-
- fire(ClientListener::Message(), this, std::move(message));
+ fire(ClientListener::Message(), this, ChatMessage(c.getParam(0), from, to, replyTo, c.hasFlag("ME", 1),
+ c.getParam("TS", 1, temp) ? Util::toInt64(temp) : 0));
}
void AdcHub::handle(AdcCommand::GPA, AdcCommand& c) noexcept {
@@ -500,8 +495,7 @@
}
}
- ChatMessage message = { c.getParam(1), u->getUser() };
- fire(ClientListener::Message(), this, std::move(message));
+ fire(ClientListener::Message(), this, ChatMessage(c.getParam(1), u));
}
void AdcHub::handle(AdcCommand::SCH, AdcCommand& c) noexcept {
=== added file 'dcpp/ChatMessage.cpp'
--- dcpp/ChatMessage.cpp 1970-01-01 00:00:00 +0000
+++ dcpp/ChatMessage.cpp 2012-02-01 16:26:37 +0000
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2001-2012 Jacek Sieka, arnetheduck on gmail point com
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "stdinc.h"
+#include "ChatMessage.h"
+
+#include "Client.h"
+#include "format.h"
+#include "OnlineUser.h"
+#include "SettingsManager.h"
+#include "SimpleXML.h"
+#include "StringTokenizer.h"
+#include "Util.h"
+
+namespace dcpp {
+
+namespace {
+
+// helpers that convert style settings to CSS declarations (implementation dependant).
+
+string cssColor(int color) {
+#ifdef _WIN32
+ // assume it's a COLORREF.
+ char buf[8];
+ snprintf(buf, sizeof(buf), "%.2X%.2X%.2X", GetRValue(color), GetGValue(color), GetBValue(color));
+ return buf;
+#else
+ ///@todo
+ return string();
+#endif
+}
+
+string cssFont(const string& font) {
+#ifdef _WIN32
+ StringTokenizer<string> st(font, ',');
+ auto& l = st.getTokens();
+ if(l.size() >= 4) {
+ std::stringstream stream;
+ stream << (Util::toInt(l[3]) ? "italic" : "normal") << " " << l[2] << " " <<
+ abs(Util::toFloat(l[1])) * 72.0 / 96.0 << "px " << l[0];
+ return stream.str();
+ }
+ return string();
+#else
+ ///@todo
+ return string();
+#endif
+}
+
+} // unnamed namespace
+
+ChatMessage::ChatMessage(const string& text, const OnlineUser* from,
+ const OnlineUser* to, const OnlineUser* replyTo,
+ bool thirdPerson, time_t timestamp) :
+from(from->getUser()),
+to(to ? to->getUser() : nullptr),
+replyTo(replyTo ? replyTo->getUser() : nullptr),
+timestamp(time(0)),
+thirdPerson(thirdPerson),
+messageTimestamp(timestamp)
+{
+ /* format the message to plain text and HTML strings before handing them over to plugins for
+ further processing. */
+
+ string tmp;
+ string xmlTmp;
+
+ auto addSpan = [&xmlTmp](char* id, const string& content, const string& style) -> string {
+ std::stringstream stream;
+ stream << "<span id=\"" << id << "\"";
+ if(!style.empty()) { stream << " style=\"" << SimpleXML::escape(style, xmlTmp, true) << "\""; }
+ stream << ">" << SimpleXML::escape(content, xmlTmp, false) << "</span>";
+ return stream.str();
+ };
+
+ if(BOOLSETTING(TIME_STAMPS)) {
+ tmp = "[" + Util::getShortTimeString(timestamp) + "] ";
+ message += tmp;
+ htmlMessage += addSpan("timestamp", tmp, Util::emptyString);
+ }
+
+ if(messageTimestamp) {
+ tmp = "["; tmp += str(F_("Sent %1%") % Util::getShortTimeString(messageTimestamp)); tmp += "] ";
+ message += tmp;
+ htmlMessage += addSpan("messageTimestamp", tmp, Util::emptyString);
+ }
+
+ tmp = from->getIdentity().getNick();
+
+ // let's *not* obey the spec here and add a space after the star. :P
+ message += thirdPerson ? "* " + tmp + " " : "<" + tmp + "> ";
+
+ auto style = from->getIdentity().getStyle();
+ string styleAttr;
+ if(!style.font.empty()) { styleAttr += "font: " + cssFont(style.font) + ";"; }
+ if(style.textColor != -1) { styleAttr += "color: #" + cssColor(style.textColor) + ";"; }
+ if(style.bgColor != -1) { styleAttr += "background-color: #" + cssColor(style.bgColor) + ";"; }
+ tmp = addSpan("nick", tmp, styleAttr);
+ htmlMessage += thirdPerson ? "* " + tmp + " " : "<" + tmp + "> ";
+
+ // Check all '<' and '[' after newlines as they're probably pastes...
+ tmp = text;
+ size_t i = 0;
+ while((i = tmp.find('\n', i)) != string::npos) {
+ ++i;
+ if(i < tmp.size()) {
+ if(tmp[i] == '[' || tmp[i] == '<') {
+ tmp.insert(i, "- ");
+ i += 2;
+ }
+ }
+ }
+
+ message += tmp;
+ htmlMessage += addSpan("message", tmp, Util::emptyString);
+
+ /// @todo send this to plugins
+}
+
+} // namespace dcpp
=== modified file 'dcpp/ChatMessage.h'
--- dcpp/ChatMessage.h 2012-01-27 22:19:56 +0000
+++ dcpp/ChatMessage.h 2012-02-01 16:26:37 +0000
@@ -28,14 +28,21 @@
using std::string;
struct ChatMessage {
- string text;
+ ChatMessage(const string& text, const OnlineUser* from,
+ const OnlineUser* to = nullptr, const OnlineUser* replyTo = nullptr,
+ bool thirdPerson = false, time_t timestamp = 0);
+
+ string message;
+ string htmlMessage;
UserPtr from;
UserPtr to;
UserPtr replyTo;
+ time_t timestamp;
+
bool thirdPerson;
- time_t timestamp;
+ time_t messageTimestamp;
};
} // namespace dcpp
=== modified file 'dcpp/ClientListener.h'
--- dcpp/ClientListener.h 2012-01-27 22:19:56 +0000
+++ dcpp/ClientListener.h 2012-02-01 16:26:37 +0000
@@ -61,7 +61,7 @@
virtual void on(Failed, Client*, const string&) noexcept { }
virtual void on(GetPassword, Client*) noexcept { }
virtual void on(HubUpdated, Client*) noexcept { }
- virtual void on(Message, Client*, ChatMessage&&) noexcept { }
+ virtual void on(Message, Client*, const ChatMessage&) noexcept { }
virtual void on(StatusMessage, Client*, const string&, int = FLAG_NORMAL) noexcept { }
virtual void on(HubUserCommand, Client*, int, int, const string&, const string&) noexcept { }
virtual void on(HubFull, Client*) noexcept { }
=== modified file 'dcpp/NmdcHub.cpp'
--- dcpp/NmdcHub.cpp 2012-01-27 22:19:56 +0000
+++ dcpp/NmdcHub.cpp 2012-02-01 16:26:37 +0000
@@ -224,8 +224,7 @@
from = &o;
}
- ChatMessage chatMessage = { unescape(message), from->getUser() };
- fire(ClientListener::Message(), this, std::move(chatMessage));
+ fire(ClientListener::Message(), this, ChatMessage(unescape(message), from));
return;
}
@@ -730,8 +729,7 @@
}
}
- ChatMessage message = { unescape(param.substr(j + 2)), from->getUser(), getUser(getMyNick()).getUser(), replyTo->getUser() };
- fire(ClientListener::Message(), this, std::move(message));
+ fire(ClientListener::Message(), this, ChatMessage(unescape(param.substr(j + 2)), from, &getUser(getMyNick()), replyTo));
} else if(cmd == "$GetPass") {
OnlineUser& ou = getUser(getMyNick());
@@ -912,10 +910,9 @@
privateMessage(aUser.getIdentity().getNick(), aMessage);
// Emulate a returning message...
Lock l(cs);
- OnlineUser* ou = findUser(getMyNick());
+ auto ou = findUser(getMyNick());
if(ou) {
- ChatMessage message = { aMessage, ou->getUser(), aUser.getUser(), ou->getUser() };
- fire(ClientListener::Message(), this, std::move(message));
+ fire(ClientListener::Message(), this, ChatMessage(aMessage, ou, &aUser, ou));
}
}
=== modified file 'dcpp/SimpleXML.cpp'
--- dcpp/SimpleXML.cpp 2012-01-23 20:18:58 +0000
+++ dcpp/SimpleXML.cpp 2012-02-01 16:26:37 +0000
@@ -189,7 +189,7 @@
}
TagReader t(&root);
- SimpleXMLReader(&t).parse(aXML.c_str(), aXML.size(), false);
+ SimpleXMLReader(&t).parse(aXML);
if(root.children.size() != 1) {
throw SimpleXMLException("Invalid XML file, missing or multiple root tags");
=== modified file 'dcpp/SimpleXMLReader.cpp'
--- dcpp/SimpleXMLReader.cpp 2012-01-23 20:18:58 +0000
+++ dcpp/SimpleXMLReader.cpp 2012-02-01 16:26:37 +0000
@@ -571,10 +571,15 @@
} while(process());
}
-bool SimpleXMLReader::parse(const char* data, size_t len, bool more) {
+bool SimpleXMLReader::parse(const char* data, size_t len) {
buf.append(data, len);
return process();
}
+
+bool SimpleXMLReader::parse(const string& str) {
+ return parse(str.c_str(), str.size());
+}
+
bool SimpleXMLReader::spaceOrError(const char* message) {
if(!skipSpace()) {
error(message);
=== modified file 'dcpp/SimpleXMLReader.h'
--- dcpp/SimpleXMLReader.h 2012-01-13 20:55:20 +0000
+++ dcpp/SimpleXMLReader.h 2012-02-01 16:26:37 +0000
@@ -40,7 +40,9 @@
virtual ~SimpleXMLReader() { }
void parse(InputStream& is, size_t maxSize = 0);
- bool parse(const char* data, size_t len, bool more);
+ bool parse(const char* data, size_t len);
+ bool parse(const string& str);
+
private:
static const size_t MAX_NAME_SIZE = 256;
=== modified file 'help/chat_commands.html'
--- help/chat_commands.html 2012-01-15 16:08:29 +0000
+++ help/chat_commands.html 2012-02-01 16:26:37 +0000
@@ -116,9 +116,6 @@
<dd>Displays available commands. (The ones listed on this page.)</dd>
<dt><untranslated>/u <url></untranslated></dt>
<dd>Launches your default web browser with the given URL.</dd>
- <dt id="ts"><untranslated>/ts</untranslated></dt>
- <dd>Toggles the showing of time stamps on new chat messages. It
-does not add time stamps to already received chat lines.</dd>
<dt><untranslated>/f <search string></untranslated></dt>
<dd>Highlights the last occourrence of the specified search string in
the chat window.</dd>
=== modified file 'help/settings_appearance.html'
--- help/settings_appearance.html 2012-01-15 16:08:29 +0000
+++ help/settings_appearance.html 2012-02-01 16:26:37 +0000
@@ -22,10 +22,9 @@
<dd cshelp="IDH_SETTINGS_APPEARANCE_ALWAYS_TRAY">When enabled, the tray icon will be always shown, regardless of
the visibility state of DC++ program window.</dd>
<dt>Show timestamps in chat by default</dt>
- <dd cshelp="IDH_SETTINGS_APPEARANCE_TIME_STAMPS">This option will show time stamps on chat lined in newly opened
-hubs. In order to show them in an already open hub, use the <a
- href="chat_commands.html#ts">/ts chat command</a>. To customize the
-format of the time stamps, see the <a href="#timestamp_format">Timestamp format</a> option below.</dd>
+ <dd cshelp="IDH_SETTINGS_APPEARANCE_TIME_STAMPS">When enabled, a string representing the current
+ time (called "timestamp") will be prepended to every chat message. To customize the timestamp
+ format, use the <a href="#timestamp_format">Timestamp format</a> option below.</dd>
<dt id="viewstatus">View status messages in main chat</dt>
<dd cshelp="IDH_SETTINGS_APPEARANCE_STATUS_IN_CHAT">Show some messages destined for the Status Bar in main chat as
well. <i>It's generally a good idea to leave this enabled.</i></dd>
=== modified file 'test/testxml.cpp'
--- test/testxml.cpp 2011-04-27 19:57:37 +0000
+++ test/testxml.cpp 2012-02-01 16:26:37 +0000
@@ -48,7 +48,7 @@
const char xml[] = "<?xml version='1.0' encoding='utf-8' ?><complex a='1'> <simple b=\"2\"/><complex2> data </complex2></complex>";
for(size_t i = 0, iend = sizeof(xml); i < iend; ++i) {
- reader.parse(xml + i, 1, true);
+ reader.parse(xml + i, 1);
}
ASSERT_EQ(collector.simpleTags["simple"], 1);
@@ -70,7 +70,7 @@
const char xml[] = "<root ab=''&"'><></root>";
for(size_t i = 0, iend = sizeof(xml); i < iend; ++i) {
- reader.parse(xml + i, 1, true);
+ reader.parse(xml + i, 1);
}
ASSERT_EQ(collector.startTags["root"], 1);
@@ -87,7 +87,7 @@
const char xml[] = "<root><!-- comment <i>,;&--></root>";
for(size_t i = 0, iend = sizeof(xml); i < iend; ++i) {
- reader.parse(xml + i, 1, true);
+ reader.parse(xml + i, 1);
}
ASSERT_EQ(collector.startTags["root"], 1);
=== modified file 'win32/AspectChat.h'
--- win32/AspectChat.h 2012-01-27 22:19:56 +0000
+++ win32/AspectChat.h 2012-02-01 16:26:37 +0000
@@ -19,11 +19,13 @@
#ifndef DCPLUSPLUS_WIN32_ASPECT_CHAT_H
#define DCPLUSPLUS_WIN32_ASPECT_CHAT_H
+#include <dcpp/ChatMessage.h>
#include <dcpp/File.h>
#include <dwt/WidgetCreator.h>
#include "HoldRedraw.h"
+#include "HtmlToRtf.h"
#include "RichTextBox.h"
#include "WinUtil.h"
@@ -39,7 +41,6 @@
chat(0),
message(0),
messageLines(1),
- timeStamps(BOOLSETTING(TIME_STAMPS)),
curCommandPosition(0)
{
}
@@ -70,28 +71,25 @@
virtual ~AspectChat() { }
- /* the first tstring is a plain formatted chat message (no style); the second tstring is a
- formatted message with style codes. "formatted" means that the message has been prepended with
- the sender's nick and possibly a timestamp, new lines have been converted to CR-LF and other
- smoothing may have been applied. */
- typedef std::pair<tstring, tstring> FormattedChatMessage;
-
- tstring formatText(const tstring& aLine) {
+private:
+ tstring formatText(const tstring& message) {
/// @todo factor out to dwt
/// @todo Text::toT works but _T doesn't, verify this.
- return Text::toT("{\\urtf1\n") + aLine + Text::toT("}\n");
- }
-
- /** Add a chat message. The message must have already been formatted; its special RTF
- characters must have been escaped with chat->rtfEscape. There is one exception: this method
- handles encapsulating the message within {\urtf1...} and prepending timestamps. */
- void addChat(const tstring& text) {
tstring pre;
if(chat->length() > 0)
pre += _T("\r\n");
- if(timeStamps)
- pre += Text::toT("[" + Util::getShortTimeString() + "] ");
- chat->addTextSteady(formatText(chat->rtfEscape(pre) + text));
+ return Text::toT("{\\urtf1\n") + chat->rtfEscape(pre + message) + Text::toT("}\n");
+ }
+
+public:
+ void addChat(const tstring& message) {
+ chat->addTextSteady(formatText(message));
+ t().addedChat(message);
+ }
+
+ void addChat(const ChatMessage& message) {
+ chat->addTextSteady(formatText(Text::toT(HtmlToRtf::convert(message.htmlMessage))));
+ t().addedChat(Text::toT(message.message));
}
void readLog(const string& logPath, const unsigned setting) {
@@ -146,14 +144,6 @@
} else if(Util::stricmp(cmd.c_str(), _T("f")) == 0) {
chat->findText(param.empty() ? chat->findTextPopup() : param);
- } else if(Util::stricmp(cmd.c_str(), _T("ts")) == 0) {
- timeStamps = !timeStamps;
- if(timeStamps) {
- status = T_("Timestamps enabled");
- } else {
- status = T_("Timestamps disabled");
- }
-
} else {
return false;
}
@@ -259,8 +249,6 @@
unsigned messageLines;
private:
- bool timeStamps;
-
TStringList prevCommands;
tstring currentCommand;
TStringList::size_type curCommandPosition; //can't use an iterator because StringList is a vector, and vector iterators become invalid after resizing
=== added file 'win32/HtmlToRtf.cpp'
--- win32/HtmlToRtf.cpp 1970-01-01 00:00:00 +0000
+++ win32/HtmlToRtf.cpp 2012-02-01 16:26:37 +0000
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2001-2012 Jacek Sieka, arnetheduck on gmail point com
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "stdafx.h"
+#include "HtmlToRtf.h"
+
+#include <dcpp/SimpleXML.h>
+
+#ifdef TODO
+namespace { tstring toRTF(COLORREF color) {
+ return Text::toT("\\red" + Util::toString(GetRValue(color)) +
+ "\\green" + Util::toString(GetGValue(color)) +
+ "\\blue" + Util::toString(GetBValue(color)) + ";");
+} }
+
+HubFrame::FormattedChatMessage HubFrame::format(const ChatMessage& message, int* pmInfo) const {
+ if(!nick.empty()) {
+ nick = chat->rtfEscape(nick);
+ tstring rtfHeader;
+ tstring rtfFormat;
+ if(!style.font.empty()) {
+ auto cached = WinUtil::getUserMatchFont(style.font);
+ if(cached.get()) {
+ auto lf = cached->getLogFont();
+ rtfHeader += _T("{\\fonttbl{\\f0\\fnil\\fcharset") + Text::toT(Util::toString(lf.lfCharSet)) + _T(" ") + lf.lfFaceName + _T(";}}");
+ rtfFormat += _T("\\f0\\fs") + Text::toT(Util::toString(lf.lfHeight * 2));
+ if(lf.lfWeight >= FW_BOLD) { rtfFormat += _T("\\b"); }
+ if(lf.lfItalic) { rtfFormat += _T("\\i"); }
+ }
+ }
+ if(!rtfFormat.empty() || style.textColor != -1 || style.bgColor != -1) {
+ /* when creating a new context (say for a font table), always redefine colors as the Rich Edit
+ control seems to randomly reset them like a boss. */
+ if(style.textColor == -1) { style.textColor = chat->getTextColor(); }
+ if(style.bgColor == -1) { style.bgColor = chat->getBgColor(); }
+ rtfHeader += _T("{\\colortbl") + toRTF(style.textColor) + toRTF(style.bgColor) + _T("}");
+ rtfFormat += _T("\\cf0\\highlight1");
+ }
+ if(!rtfFormat.empty()) {
+ nick = _T("{") + rtfHeader + rtfFormat + _T(" ") + nick + _T("}");
+ }
+ ret.second += message.thirdPerson ? _T("* ") + nick + _T(" ") : _T("<") + nick + _T("> ");
+ }
+
+ auto tmp = Text::toT(Text::toDOS(text));
+ ret.first += tmp;
+ ret.second += chat->rtfEscape(tmp);
+}
+#endif
+
+struct Parser : SimpleXMLReader::CallBack {
+ Parser() { }
+ void startTag(const string& name, StringPairList& attribs, bool simple);
+ void endTag(const string& name, const string& data);
+ string finalize();
+private:
+ string ret;
+};
+
+string HtmlToRtf::convert(const string& html) {
+ Parser parser;
+ try { SimpleXMLReader(&parser).parse(html); }
+ catch(const SimpleXMLException& e) { return e.getError(); }
+ return parser.finalize();
+}
+
+void Parser::startTag(const string& name, StringPairList& attribs, bool simple) {
+ ret += name;
+}
+
+void Parser::endTag(const string& name, const string& data) {
+ ret += data;
+}
+
+string Parser::finalize() {
+ return ret;
+}
=== added file 'win32/HtmlToRtf.h'
--- win32/HtmlToRtf.h 1970-01-01 00:00:00 +0000
+++ win32/HtmlToRtf.h 2012-02-01 16:26:37 +0000
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2001-2012 Jacek Sieka, arnetheduck on gmail point com
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef DCPLUSPLUS_WIN32_HTML_TO_RTF_H
+#define DCPLUSPLUS_WIN32_HTML_TO_RTF_H
+
+/** Convert an HTML string to an RTF string, suitable for insertion within a Rich Edit control.
+Only simple HTML tags (those that are marked as "phrasing content" in the HTML5 spec) are
+supported. */
+class HtmlToRtf {
+public:
+ static string convert(const string& html);
+};
+
+#endif
=== modified file 'win32/HubFrame.cpp'
--- win32/HubFrame.cpp 2012-01-29 18:05:27 +0000
+++ win32/HubFrame.cpp 2012-02-01 16:26:37 +0000
@@ -518,115 +518,16 @@
tasks.clear();
}
-namespace { tstring toRTF(COLORREF color) {
- return Text::toT("\\red" + Util::toString(GetRValue(color)) +
- "\\green" + Util::toString(GetGValue(color)) +
- "\\blue" + Util::toString(GetBValue(color)) + ";");
-} }
-
-HubFrame::FormattedChatMessage HubFrame::format(const ChatMessage& message, int* pmInfo) const {
- FormattedChatMessage ret;
-
- if(message.timestamp) {
- tstring tmp = _T("["); tmp += T_("Sent ") + Text::toT(Util::getShortTimeString(message.timestamp)) + _T("] ");
- ret.first += tmp;
- ret.second += chat->rtfEscape(tmp);
- }
-
- tstring nick;
- Style style;
- {
- auto lock = ClientManager::getInstance()->lock();
- if(message.from.get()) {
- auto ou = ClientManager::getInstance()->findOnlineUser(message.from->getCID(), url, true);
- if(ou) {
- nick = Text::toT(ou->getIdentity().getNick());
- style = ou->getIdentity().getStyle();
- }
- }
-
- if(pmInfo) {
- // gather info used by the PM dispatcher here to only use 1 CM lock.
- auto& flags = *pmInfo;
- auto ou = ClientManager::getInstance()->findOnlineUser(message.replyTo->getCID(), url, true);
- if(ou && ou->getIdentity().isHub())
- flags |= FROM_HUB;
- if(ou && ou->getIdentity().isBot())
- flags |= FROM_BOT;
- }
- }
-
- if(!nick.empty()) {
- // let's *not* obey the spec here and add a space after the star. :P
- ret.first += message.thirdPerson ? _T("* ") + nick + _T(" ") : _T("<") + nick + _T("> ");
-
- nick = chat->rtfEscape(nick);
- tstring rtfHeader;
- tstring rtfFormat;
- if(!style.font.empty()) {
- auto cached = WinUtil::getUserMatchFont(style.font);
- if(cached.get()) {
- auto lf = cached->getLogFont();
- rtfHeader += _T("{\\fonttbl{\\f0\\fnil\\fcharset") + Text::toT(Util::toString(lf.lfCharSet)) + _T(" ") + lf.lfFaceName + _T(";}}");
- rtfFormat += _T("\\f0\\fs") + Text::toT(Util::toString(lf.lfHeight * 2));
- if(lf.lfWeight >= FW_BOLD) { rtfFormat += _T("\\b"); }
- if(lf.lfItalic) { rtfFormat += _T("\\i"); }
- }
- }
- if(!rtfFormat.empty() || style.textColor != -1 || style.bgColor != -1) {
- /* when creating a new context (say for a font table), always redefine colors as the Rich Edit
- control seems to randomly reset them like a boss. */
- if(style.textColor == -1) { style.textColor = chat->getTextColor(); }
- if(style.bgColor == -1) { style.bgColor = chat->getBgColor(); }
- rtfHeader += _T("{\\colortbl") + toRTF(style.textColor) + toRTF(style.bgColor) + _T("}");
- rtfFormat += _T("\\cf0\\highlight1");
- }
- if(!rtfFormat.empty()) {
- nick = _T("{") + rtfHeader + rtfFormat + _T(" ") + nick + _T("}");
- }
- ret.second += message.thirdPerson ? _T("* ") + nick + _T(" ") : _T("<") + nick + _T("> ");
- }
-
- // Check all '<' and '[' after newlines as they're probably pastes...
- auto text = message.text;
- size_t i = 0;
- while((i = text.find('\n', i)) != string::npos) {
- ++i;
- if(i < text.size()) {
- if(text[i] == '[' || text[i] == '<') {
- text.insert(i, "- ");
- i += 2;
- }
- }
- }
-
- auto tmp = Text::toT(Text::toDOS(text));
- ret.first += tmp;
- ret.second += chat->rtfEscape(tmp);
-
- return ret;
-}
-
-void HubFrame::addChat(const ChatMessage& message) {
- addChat(format(message));
-}
-
-void HubFrame::addChat(const tstring& text) {
- addChat(make_pair(text, chat->rtfEscape(text)));
-}
-
-void HubFrame::addChat(const FormattedChatMessage& message) {
- ChatType::addChat(message.second);
-
+void HubFrame::addedChat(const tstring& message) {
{
auto u = url;
- WinUtil::notify(WinUtil::NOTIFICATION_MAIN_CHAT, message.first, [this, u] { activateWindow(u); });
+ WinUtil::notify(WinUtil::NOTIFICATION_MAIN_CHAT, message, [this, u] { activateWindow(u); });
}
setDirty(SettingsManager::BOLD_HUB);
if(BOOLSETTING(LOG_MAIN_CHAT)) {
ParamMap params;
- params["message"] = [&message] { return Text::fromT(message.first); };
+ params["message"] = [&message] { return Text::fromT(message); };
client->getHubIdentity().getParams(params, "hub", false);
params["hubURL"] = [this] { return client->getHubUrl(); };
client->getMyIdentity().getParams(params, "my", true);
@@ -728,17 +629,24 @@
}
void HubFrame::onPrivateMessage(const ChatMessage& message) {
- int pmInfo = 0;
- auto text = format(message, &pmInfo);
+ bool fromHub = false, fromBot = false;
+ {
+ auto lock = ClientManager::getInstance()->lock();
+ auto ou = ClientManager::getInstance()->findOnlineUser(message.replyTo->getCID(), url, true);
+ if(ou && ou->getIdentity().isHub())
+ fromHub = true;
+ if(ou && ou->getIdentity().isBot())
+ fromBot = true;
+ }
bool ignore = false, window = false;
- if((pmInfo & FROM_HUB) == FROM_HUB) {
+ if(fromHub) {
if(BOOLSETTING(IGNORE_HUB_PMS)) {
ignore = true;
} else if(BOOLSETTING(POPUP_HUB_PMS) || PrivateFrame::isOpen(message.replyTo)) {
window = true;
}
- } else if((pmInfo & FROM_BOT) == FROM_BOT) {
+ } else if(fromBot) {
if(BOOLSETTING(IGNORE_BOT_PMS)) {
ignore = true;
} else if(BOOLSETTING(POPUP_BOT_PMS) || PrivateFrame::isOpen(message.replyTo)) {
@@ -749,13 +657,13 @@
}
if(ignore) {
- addStatus(str(TF_("Ignored message: %1%") % text.first), false);
+ addStatus(str(TF_("Ignored message: %1%") % Text::toT(message.message)), false);
} else {
if(window) {
- PrivateFrame::gotMessage(getParent(), message.from, message.to, message.replyTo, text, url);
+ PrivateFrame::gotMessage(getParent(), message.from, message.to, message.replyTo, message, url);
} else {
/// @todo add formatting here (for PMs in main chat)
- addChat(str(TF_("Private message from %1%: %2%") % getNick(message.from) % text.first));
+ addChat(str(TF_("Private message from %1%: %2%") % getNick(message.from) % Text::toT(message.message)));
}
WinUtil::mainWindow->TrayPM();
}
@@ -1014,20 +922,14 @@
callAsync([this, hubNameT] { setText(hubNameT); });
}
-void HubFrame::on(Message, Client*, ChatMessage&& message) noexcept {
- struct ChatMessageFunctor {
- ChatMessageFunctor(HubFrame* h, ChatMessage&& message) : h(h), message(std::forward<ChatMessage>(message)) { }
- void operator()() const {
- if(message.to.get() && message.replyTo.get()) {
- h->onPrivateMessage(message);
- } else {
- h->addChat(message);
- }
+void HubFrame::on(Message, Client*, const ChatMessage& message) noexcept {
+ callAsync([this, message] {
+ if(message.to.get() && message.replyTo.get()) {
+ onPrivateMessage(message);
+ } else {
+ addChat(message);
}
- HubFrame* h;
- ChatMessage message;
- };
- callAsync(ChatMessageFunctor(this, std::forward<ChatMessage>(message)));
+ });
}
void HubFrame::on(StatusMessage, Client*, const string& line, int statusFlags) noexcept {
=== modified file 'win32/HubFrame.h'
--- win32/HubFrame.h 2012-01-28 11:36:54 +0000
+++ win32/HubFrame.h 2012-02-01 16:26:37 +0000
@@ -197,11 +197,7 @@
bool tab();
- enum { FROM_HUB = 1 << 0, FROM_BOT = 1 << 1 };
- FormattedChatMessage format(const ChatMessage& message, int* pmInfo = nullptr) const;
- void addChat(const ChatMessage& message);
- void addChat(const tstring& text);
- void addChat(const FormattedChatMessage& message);
+ void addedChat(const tstring& message);
void addStatus(const tstring& text, bool legitimate = true);
pair<size_t, tstring> getStatusUsers() const;
@@ -284,7 +280,7 @@
virtual void on(Failed, Client*, const string&) noexcept;
virtual void on(GetPassword, Client*) noexcept;
virtual void on(HubUpdated, Client*) noexcept;
- virtual void on(Message, Client*, ChatMessage&&) noexcept;
+ virtual void on(Message, Client*, const ChatMessage&) noexcept;
virtual void on(StatusMessage, Client*, const string&, int = ClientListener::FLAG_NORMAL) noexcept;
virtual void on(NickTaken, Client*) noexcept;
virtual void on(SearchFlood, Client*, const string&) noexcept;
=== modified file 'win32/PrivateFrame.cpp'
--- win32/PrivateFrame.cpp 2012-01-27 22:19:56 +0000
+++ win32/PrivateFrame.cpp 2012-02-01 16:26:37 +0000
@@ -27,6 +27,7 @@
#include "MainWindow.h"
#include "resource.h"
+#include <dcpp/ChatMessage.h>
#include <dcpp/ClientManager.h>
#include <dcpp/Client.h>
#include <dcpp/LogManager.h>
@@ -56,7 +57,7 @@
}
void PrivateFrame::gotMessage(TabViewPtr parent, const UserPtr& from, const UserPtr& to, const UserPtr& replyTo,
- const FormattedChatMessage& message, const string& hubHint)
+ const ChatMessage& message, const string& hubHint)
{
const UserPtr& user = (replyTo == ClientManager::getInstance()->getMe()) ? to : replyTo;
auto i = frames.find(user);
@@ -74,13 +75,13 @@
}
}
- WinUtil::notify(WinUtil::NOTIFICATION_PM_WINDOW, message.first, [user] { activateWindow(user); });
+ WinUtil::notify(WinUtil::NOTIFICATION_PM_WINDOW, Text::toT(message.message), [user] { activateWindow(user); });
} else {
i->second->addChat(message);
}
- WinUtil::notify(WinUtil::NOTIFICATION_PM, message.first, [user] { activateWindow(user); });
+ WinUtil::notify(WinUtil::NOTIFICATION_PM, Text::toT(message.message), [user] { activateWindow(user); });
}
void PrivateFrame::activateWindow(const UserPtr& u) {
@@ -187,28 +188,22 @@
PrivateFrame::~PrivateFrame() {
}
-void PrivateFrame::addChat(const FormattedChatMessage& message, bool log) {
- ChatType::addChat(message.second);
-
+void PrivateFrame::addedChat(const tstring& message) {
setDirty(SettingsManager::BOLD_PM);
- if(log && BOOLSETTING(LOG_PRIVATE_CHAT)) {
+ if(BOOLSETTING(LOG_PRIVATE_CHAT)) {
ParamMap params;
- params["message"] = [&message] { return Text::fromT(message.first); };
+ params["message"] = [&message] { return Text::fromT(message); };
fillLogParams(params);
LOG(LogManager::PM, params);
}
}
-void PrivateFrame::addChat(const tstring& text, bool log) {
- addChat(make_pair(text, chat->rtfEscape(text)), log);
-}
-
-void PrivateFrame::addStatus(const tstring& text, bool log) {
+void PrivateFrame::addStatus(const tstring& text) {
status->setText(STATUS_STATUS, Text::toT("[" + Util::getShortTimeString() + "] ") + text);
if(BOOLSETTING(STATUS_IN_CHAT))
- addChat(_T("*** ") + text, log);
+ addChat(_T("*** ") + text);
}
bool PrivateFrame::preClosing() {
=== modified file 'win32/PrivateFrame.h'
--- win32/PrivateFrame.h 2012-01-27 22:19:56 +0000
+++ win32/PrivateFrame.h 2012-02-01 16:26:37 +0000
@@ -56,7 +56,7 @@
const string& getId() const;
static void gotMessage(TabViewPtr parent, const UserPtr& from, const UserPtr& to, const UserPtr& replyTo,
- const FormattedChatMessage& message, const string& hubHint);
+ const ChatMessage& message, const string& hubHint);
static void openWindow(TabViewPtr parent, const HintedUser& replyTo, const tstring& msg = Util::emptyStringT,
const string& logPath = Util::emptyString, bool activate = true);
static void activateWindow(const UserPtr& u);
@@ -93,9 +93,8 @@
string getLogPath() const;
void openLog();
void fillLogParams(ParamMap& params) const;
- void addChat(const FormattedChatMessage& message, bool log = true);
- void addChat(const tstring& text, bool log = true);
- void addStatus(const tstring& text, bool log = true);
+ void addedChat(const tstring& message);
+ void addStatus(const tstring& text);
void updateOnlineStatus();
bool handleChatContextMenu(dwt::ScreenCoordinate pt);
=== modified file 'win32/WinUtil.cpp'
--- win32/WinUtil.cpp 2012-01-29 18:05:27 +0000
+++ win32/WinUtil.cpp 2012-02-01 16:26:37 +0000
@@ -551,7 +551,7 @@
tstring
WinUtil::commands =
- _T("/refresh, /me <msg>, /clear [lines to keep], /slots #, /dslots #, /search <string>, /f <string>, /dc++, /away <msg>, /back, /g <searchstring>, /imdb <imdbquery>, /u <url>, /rebuild, /ts, /download, /upload");
+ _T("/refresh, /me <msg>, /clear [lines to keep], /slots #, /dslots #, /search <string>, /f <string>, /dc++, /away <msg>, /back, /g <searchstring>, /imdb <imdbquery>, /u <url>, /rebuild, /download, /upload");
bool WinUtil::checkCommand(tstring& cmd, tstring& param, tstring& message, tstring& status, bool& thirdPerson) {
string::size_type i = cmd.find(' ');