diff --git a/retroshare-gui/src/RetroShare.pro b/retroshare-gui/src/RetroShare.pro
index 77f8d612f..2d94d0b22 100644
--- a/retroshare-gui/src/RetroShare.pro
+++ b/retroshare-gui/src/RetroShare.pro
@@ -11,7 +11,7 @@ MOC_DIR = temp/moc
#CONFIG += debug
debug {
- QMAKE_CXXFLAGS *= -g
+ QMAKE_CFLAGS += -g
}
################################# Linux ##########################################
@@ -19,8 +19,15 @@ debug {
linux-* {
#CONFIG += version_detail_bash_script
QMAKE_CXXFLAGS *= -D_FILE_OFFSET_BITS=64
+
+ system(which gpgme-config >/dev/null 2>&1) {
+ INCLUDEPATH += $$system(gpgme-config --cflags | sed -e "s/-I//g")
+ } else {
+ message(Could not find gpgme-config on your system, assuming gpgme.h is in /usr/include)
+ }
+
LIBS += ../../libretroshare/src/lib/libretroshare.a
- LIBS += -lssl -lgpgme -lupnp -lXss
+ LIBS += -lssl -lgpgme -lupnp -lXss
DEFINES *= HAVE_XSS # for idle time, libx screensaver extensions
}
@@ -208,6 +215,7 @@ HEADERS += rshare.h \
gui/profile/ProfileWidget.h \
gui/profile/StatusMessage.h \
gui/chat/PopupChatDialog.h \
+ gui/chat/HandleRichText.h \
gui/channels/CreateChannel.h \
gui/channels/ChannelDetails.h \
gui/channels/CreateChannelMsg.h \
@@ -418,6 +426,7 @@ SOURCES += main.cpp \
gui/channels/CreateChannelMsg.cpp \
gui/channels/ChannelDetails.cpp \
gui/chat/PopupChatDialog.cpp \
+ gui/chat/HandleRichText.cpp \
gui/connect/ConnectDialog.cpp \
gui/connect/ConfCertDialog.cpp \
gui/msgs/ChanMsgDialog.cpp \
diff --git a/retroshare-gui/src/gui/PeersDialog.cpp b/retroshare-gui/src/gui/PeersDialog.cpp
index c89c06262..73682d0e6 100644
--- a/retroshare-gui/src/gui/PeersDialog.cpp
+++ b/retroshare-gui/src/gui/PeersDialog.cpp
@@ -998,14 +998,13 @@ void PeersDialog::insertChat()
QString name = QString::fromStdString(it->name);
QString line = "" + timestamp + "" +
"" + " " + name + "";
+ QString msgContents = QString::fromStdWString(it->msg);
//std::cerr << "PeersDialog::insertChat(): 1.11\n";
- historyKeeper.addMessage(name, "THIS", QString::fromStdWString(it->msg));
+ historyKeeper.addMessage(name, "THIS", msgContents);
//std::cerr << "PeersDialog::insertChat(): 1.12\n";
extraTxt += line;
- extraTxt += QString::fromStdWString(it->msg);
-
// notify with a systray icon msg
if(it->rsid != rsPeers->getOwnId())
{
@@ -1020,31 +1019,20 @@ void PeersDialog::insertChat()
emit notifyGroupChat(QString("New group chat"), notifyMsg);
}
- //replace http://, https:// and www. with links
- QRegExp rx("(retroshare://[^ <>]*)|(https?://[^ <>]*)|(www\\.[^ <>]*)");
- int count = 0;
- int pos = 200; //ignore the first 200 char because of the standard DTD ref
- while ( (pos = rx.indexIn(extraTxt, pos)) != -1 ) {
- //we need to look ahead to see if it's already a well formed link
- if (extraTxt.mid(pos - 6, 6) != "href=\"" && extraTxt.mid(pos - 6, 6) != "href='" && extraTxt.mid(pos - 6, 6) != "ttp://" ) {
- QString tempMessg = extraTxt.left(pos) + "" + rx.cap(count) + "" + extraTxt.mid(pos + rx.matchedLength(), -1);
- extraTxt = tempMessg;
- }
- pos += rx.matchedLength() + 15;
- count ++;
- }
+ // create a DOM tree object from the message and embed contents with HTML tags
+ QDomDocument doc;
+ doc.setContent(msgContents);
- if (settings.value(QString::fromUtf8("Emoteicons_GroupChat"), true).toBool())
- {
- QHashIterator i(smileys);
- while(i.hasNext())
- {
- i.next();
- foreach(QString code, i.key().split("|"))
- extraTxt.replace(code, "");
- }
- }
+ // embed links
+ QDomElement body = doc.documentElement();
+ RsChat::embedHtml(doc, body, defEmbedAhref);
+ // embed smileys
+ if (settings.value(QString::fromUtf8("Emoteicons_GroupChat"), true).toBool())
+ RsChat::embedHtml(doc, body, defEmbedImg);
+
+ msgContents = doc.toString(-1); // -1 removes any annoying carriage return misinterpreted by QTextEdit
+ extraTxt += msgContents;
if ((msgWidget->verticalScrollBar()->maximum() - 30) < msgWidget->verticalScrollBar()->value() ) {
msgWidget->append(extraTxt);
@@ -1357,7 +1345,7 @@ void PeersDialog::loadEmoticonsgroupchat()
#endif
if(!sm_file.open(QIODevice::ReadOnly))
{
- std::cerr << "Could not open resouce file :/emoticons/emotes.acs" << endl ;
+ std::cerr << "Could not open resouce file :/emoticons/emotes.acs" << std::endl ;
return ;
}
sm_codes = sm_file.readAll();
@@ -1407,6 +1395,9 @@ void PeersDialog::loadEmoticonsgroupchat()
smileys.insert(smcode, ":/"+smfile);
#endif
}
+
+ // init embedder
+ defEmbedImg.InitFromAwkwardHash(smileys);
}
void PeersDialog::smileyWidgetgroupchat()
@@ -1661,13 +1652,13 @@ void PeersDialog::fileHashingFinished(AttachFileItem* file) {
}
//convert fileSize from uint_64 to string for html link
- char fileSizeChar [100];
- sprintf(fileSizeChar, "%lld", file->FileSize());
- std::string fileSize = *(&fileSizeChar);
+// char fileSizeChar [100];
+// sprintf(fileSizeChar, "%lld", file->FileSize());
+// std::string fileSize = *(&fileSizeChar);
std::string mesgString = RetroShareLink(QString::fromStdString(file->FileName()),
- file->FileSize(),
- QString::fromStdString(file->FileHash())).toHtml().toStdString() ;
+ file->FileSize(),
+ QString::fromStdString(file->FileHash())).toHtml().toStdString() ;
// std::string mesgString = ""
// + "retroshare://file|" + (file->FileName()) + "|" + fileSize + "|" + (file->FileHash()) + "";
@@ -1679,13 +1670,13 @@ void PeersDialog::fileHashingFinished(AttachFileItem* file) {
//convert char massageString to w_char
wchar_t* message;
- int requiredSize = mbstowcs(NULL, messageString, 0); // C4996
+ size_t requiredSize = mbstowcs(NULL, messageString, 0); // C4996
/* Add one to leave room for the NULL terminator */
message = (wchar_t *)malloc( (requiredSize + 1) * sizeof( wchar_t ));
if (! message) {
std::cerr << ("Memory allocation failure.\n");
}
- int size = mbstowcs( message, messageString, requiredSize + 1); // C4996
+ size_t size = mbstowcs( message, messageString, requiredSize + 1); // C4996
if (size == (size_t) (-1)) {
printf("Couldn't convert string--invalid multibyte character.\n");
}
diff --git a/retroshare-gui/src/gui/PeersDialog.h b/retroshare-gui/src/gui/PeersDialog.h
index d701ae34e..aca4dbc7c 100644
--- a/retroshare-gui/src/gui/PeersDialog.h
+++ b/retroshare-gui/src/gui/PeersDialog.h
@@ -25,6 +25,7 @@
#include
#include "chat/PopupChatDialog.h"
+#include "chat/HandleRichText.h"
#include "RsAutoUpdatePage.h"
#include "mainpage.h"
@@ -159,8 +160,12 @@ private:
///play the sound when recv a message
void playsound();
-
- QString fileName;
+
+ QString fileName;
+
+ /** store default information for embedding HTML */
+ RsChat::EmbedInHtmlAhref defEmbedAhref;
+ RsChat::EmbedInHtmlImg defEmbedImg;
/* Worker Functions */
/* (1) Update Display */
@@ -187,7 +192,7 @@ private:
QColor _currentColor;
bool _underline;
time_t last_status_send_time ;
-
+
QHash smileys;
std::map chatDialogs;
@@ -198,5 +203,5 @@ private:
Ui::PeersDialog ui;
};
-#endif
+#endif
diff --git a/retroshare-gui/src/gui/chat/HandleRichText.cpp b/retroshare-gui/src/gui/chat/HandleRichText.cpp
new file mode 100644
index 000000000..c3e5a4271
--- /dev/null
+++ b/retroshare-gui/src/gui/chat/HandleRichText.cpp
@@ -0,0 +1,124 @@
+/****************************************************************
+ * This file is distributed under the following license:
+ *
+ * Copyright (c) 2010, Thomas Kister
+ *
+ * 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., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ ****************************************************************/
+
+#include "HandleRichText.h"
+
+
+namespace RsChat {
+
+
+void EmbedInHtmlImg::InitFromAwkwardHash(const QHash< QString, QString >& hash)
+{
+ QString newRE;
+ for(QHash::const_iterator it = hash.begin(); it != hash.end(); ++it)
+ foreach(QString smile, it.key().split("|")) {
+ smileys.insert(smile, it.value());
+ newRE += "(" + QRegExp::escape(smile) + ")|";
+ }
+ newRE.chop(1); // remove last |
+ myRE.setPattern(newRE);
+}
+
+
+/**
+ * Parses a DOM tree and replaces text by HTML tags.
+ * The tree is traversed depth-first, but only through children of Element type
+ * nodes. Any other kind of node is terminal.
+ *
+ * If the node is of type Text, its data is checked against the user-provided
+ * regular expression. If there is a match, the text is cut in three parts: the
+ * preceding part that will be inserted before, the part to be replaced, and the
+ * following part which will be itself checked against the regular expression.
+ *
+ * The part to be replaced is sent to a user-provided functor that will create
+ * the necessary embedding and return a new Element node to be inserted.
+ *
+ * @param[in] doc The whole DOM tree, necessary to create new nodes
+ * @param[in,out] currentElement The current node (which is of type Element)
+ * @param[in] embedInfos The regular expression and the type of embedding to use
+ */
+void embedHtml(QDomDocument& doc, QDomElement& currentElement, const EmbedInHtml& embedInfos)
+{
+ if(embedInfos.myRE.pattern().length() == 0) // we'll get stuck with an empty regexp
+ return;
+
+ QDomNodeList children = currentElement.childNodes();
+ for(uint index = 0; index < children.length(); index++) {
+ QDomNode tempNode = children.item(index);
+ if(tempNode.isElement()) {
+ // child is an element, we skip it if it's an tag
+ QDomElement tempElement = tempNode.toElement();
+ if(tempElement.tagName().toLower() != "head" && tempElement.tagName().toLower() != "a")
+ embedHtml(doc, tempElement, embedInfos);
+ }
+ else if(tempNode.isText()) {
+ // child is a text, we parse it
+ QString tempText = tempNode.toText().data();
+ if(embedInfos.myRE.indexIn(tempText) == -1)
+ continue;
+
+ // there is at least one link inside, we start replacing
+ int currentPos = 0;
+ int nextPos = 0;
+ while((nextPos = embedInfos.myRE.indexIn(tempText, currentPos)) != -1) {
+ // if nextPos == 0 it means the text begins by a link
+ if(nextPos > 0) {
+ QDomText textPart = doc.createTextNode(tempText.mid(currentPos, nextPos - currentPos));
+ currentElement.insertBefore(textPart, tempNode);
+ index++;
+ }
+
+ // inserted tag
+ QDomElement insertedTag;
+ switch(embedInfos.myType) {
+ case Ahref: insertedTag = doc.createElement("a");
+ insertedTag.setAttribute("href", embedInfos.myRE.cap(0));
+ insertedTag.appendChild(doc.createTextNode(embedInfos.myRE.cap(0)));
+ break;
+ case Img: insertedTag = doc.createElement("img");
+ {
+ const EmbedInHtmlImg& embedImg = static_cast(embedInfos);
+ insertedTag.setAttribute("src", embedImg.smileys[embedInfos.myRE.cap(0)]);
+ }
+ break;
+ }
+ currentElement.insertBefore(insertedTag, tempNode);
+
+ currentPos = nextPos + embedInfos.myRE.matchedLength();
+ index++;
+ }
+
+ // text after the last link, only if there's one, don't touch the index
+ // otherwise decrement the index because we're going to remove tempNode
+ if(currentPos < tempText.length()) {
+ QDomText textPart = doc.createTextNode(tempText.mid(currentPos));
+ currentElement.insertBefore(textPart, tempNode);
+ }
+ else
+ index--;
+
+ currentElement.removeChild(tempNode);
+ }
+ }
+}
+
+
+} // namespace RsChat
diff --git a/retroshare-gui/src/gui/chat/HandleRichText.h b/retroshare-gui/src/gui/chat/HandleRichText.h
new file mode 100644
index 000000000..458a11100
--- /dev/null
+++ b/retroshare-gui/src/gui/chat/HandleRichText.h
@@ -0,0 +1,112 @@
+/****************************************************************
+ * This file is distributed under the following license:
+ *
+ * Copyright (c) 2010, Thomas Kister
+ *
+ * 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., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ ****************************************************************/
+
+/**
+ * This file provides helper functions and functors for translating data from/to
+ * rich text format and HTML. Its main goal is to facilitate decoding of chat
+ * messages, particularly embedding special information into HTML tags.
+ */
+
+
+#ifndef HANDLE_RICH_TEXT_H_
+#define HANDLE_RICH_TEXT_H_
+
+
+#include
+#include
+
+
+namespace RsChat {
+
+
+/**
+ * The type of embedding we'd like to do
+ */
+enum EmbeddedType
+{
+ Ahref, ///< into
+ Img, ///< into
+};
+
+
+/**
+ * Base class for storing information about a given kind of embedding.
+ *
+ * Its only constructor is protected so it is impossible to instantiate it, and
+ * at the same time derived classes have to provide a type.
+ */
+class EmbedInHtml
+{
+protected:
+ EmbedInHtml(EmbeddedType newType) :
+ myType(newType)
+ {}
+
+public:
+ const EmbeddedType myType;
+ QRegExp myRE;
+};
+
+
+/**
+ * This class is used to store information for embedding links into tags.
+ */
+class EmbedInHtmlAhref : public EmbedInHtml
+{
+public:
+ EmbedInHtmlAhref() :
+ EmbedInHtml(Ahref)
+ {
+ myRE.setPattern("(\\bretroshare://[^\\s]*)|(\\bhttps?://[^\\s]*)|(\\bwww\\.[^\\s]*)");
+ }
+};
+
+
+/**
+ * This class is used to store information for embedding smileys into tags.
+ *
+ * By default the QRegExp the variables are empty, which means it must be
+ * filled at runtime, typically when the smileys set is loaded. It can be
+ * either done by hand or by using one of the helper methods available.
+ *
+ * Note: The QHash uses only *one* smiley per key (unlike soon-to-be-upgraded
+ * code out there).
+ */
+class EmbedInHtmlImg : public EmbedInHtml
+{
+public:
+ EmbedInHtmlImg() :
+ EmbedInHtml(Img)
+ {}
+
+ void InitFromAwkwardHash(const QHash& hash);
+
+ QHash smileys;
+};
+
+
+void embedHtml(QDomDocument& doc, QDomElement& currentElement, const EmbedInHtml& embedInfos);
+
+
+} // namespace RsChat
+
+
+#endif // HANDLE_RICH_TEXT_H_