Merge pull request #189 from hunbernd/feature/chat-enhancements

Chat enhancements
This commit is contained in:
defnax 2015-12-22 01:07:02 +01:00
commit 8e9fe6d800
37 changed files with 415 additions and 72 deletions

View File

@ -267,10 +267,12 @@ static QString getStyle(const QDir &styleDir, const QString &styleVariant, enumG
QString ChatStyle::formatMessage(enumFormatMessage type, const QString &name, const QDateTime &timestamp, const QString &message, unsigned int flag) QString ChatStyle::formatMessage(enumFormatMessage type, const QString &name, const QDateTime &timestamp, const QString &message, unsigned int flag)
{ {
bool me = false;
QDomDocument doc ; QDomDocument doc ;
QString styleOptimized ; QString styleOptimized ;
QString errorMsg ; int errorLine ; int errorColumn ; QString errorMsg ; int errorLine ; int errorColumn ;
QString messageBody = message ; QString messageBody = message ;
me = me || message.trimmed().startsWith("/me ");
if (doc.setContent(messageBody, &errorMsg, &errorLine, &errorColumn)) { if (doc.setContent(messageBody, &errorMsg, &errorLine, &errorColumn)) {
QDomElement body = doc.documentElement(); QDomElement body = doc.documentElement();
if (!body.isNull()){ if (!body.isNull()){
@ -279,6 +281,12 @@ QString ChatStyle::formatMessage(enumFormatMessage type, const QString &name, co
for (int curs = 0; curs < count; ++curs){ for (int curs = 0; curs < count; ++curs){
QDomNode it = body.childNodes().item(curs); QDomNode it = body.childNodes().item(curs);
if (it.nodeName().toLower() != "style") { if (it.nodeName().toLower() != "style") {
//find out if the message starts with /me
if(it.isText()){
me = me || it.toText().data().trimmed().startsWith("/me ");
}else if(it.isElement()){
me = me || it.toElement().text().trimmed().startsWith("/me ");
}
QString str; QString str;
QTextStream stream(&str); QTextStream stream(&str);
it.toElement().save(stream, -1); it.toElement().save(stream, -1);
@ -354,7 +362,22 @@ QString ChatStyle::formatMessage(enumFormatMessage type, const QString &name, co
QString strName = RsHtml::plainText(name).prepend(QString("<a name=\"name\">")).append(QString("</a>")); QString strName = RsHtml::plainText(name).prepend(QString("<a name=\"name\">")).append(QString("</a>"));
QString strDate = DateTime::formatDate(timestamp.date()).prepend(QString("<a name=\"date\">")).append(QString("</a>")); QString strDate = DateTime::formatDate(timestamp.date()).prepend(QString("<a name=\"date\">")).append(QString("</a>"));
QString strTime = DateTime::formatTime(timestamp.time()).prepend(QString("<a name=\"time\">")).append(QString("</a>")); QString strTime = DateTime::formatTime(timestamp.time()).prepend(QString("<a name=\"time\">")).append(QString("</a>"));
int bi = name.lastIndexOf(QRegExp(" \\(.*\\)")); //trim location from the end
QString strShortName = RsHtml::plainText(name.left(bi)).prepend(QString("<a name=\"name\">")).append(QString("</a>"));
//handle /me
//%nome% and %me% for including formatting conditionally
//meName class for modifying the style of the name in the palce of /me
if(me){
messageBody = messageBody.replace(messageBody.indexOf("/me "), 3, strShortName.prepend(QString("<span class=\"meName\">")).append(QString("</span>"))); //replace only the first /me
style = style.remove(QRegExp("%nome%.*%/nome%")).remove("%me%").remove("%/me%");
} else {
style = style.remove(QRegExp("%me%.*%/me%")).remove("%nome%").remove("%/nome%");
}
QString formatMsg = style.replace("%name%", strName) QString formatMsg = style.replace("%name%", strName)
.replace("%shortname%", strShortName)
.replace("%date%", strDate) .replace("%date%", strDate)
.replace("%time%", strTime) .replace("%time%", strTime)
#ifdef COLORED_NICKNAMES #ifdef COLORED_NICKNAMES

View File

@ -51,6 +51,7 @@
#include "util/HandleRichText.h" #include "util/HandleRichText.h"
#include "gui/chat/ChatUserNotify.h"//For BradCast #include "gui/chat/ChatUserNotify.h"//For BradCast
#include "util/DateTime.h" #include "util/DateTime.h"
#include "util/imageutil.h"
#include <retroshare/rsstatus.h> #include <retroshare/rsstatus.h>
#include <retroshare/rsidentity.h> #include <retroshare/rsidentity.h>
@ -125,6 +126,8 @@ ChatWidget::ChatWidget(QWidget *parent) :
connect(ui->actionChooseFont, SIGNAL(triggered()), this, SLOT(chooseFont())); connect(ui->actionChooseFont, SIGNAL(triggered()), this, SLOT(chooseFont()));
connect(ui->actionChooseColor, SIGNAL(triggered()), this, SLOT(chooseColor())); connect(ui->actionChooseColor, SIGNAL(triggered()), this, SLOT(chooseColor()));
connect(ui->actionResetFont, SIGNAL(triggered()), this, SLOT(resetFont())); connect(ui->actionResetFont, SIGNAL(triggered()), this, SLOT(resetFont()));
connect(ui->actionQuote, SIGNAL(triggered()), this, SLOT(quote()));
connect(ui->actionSave_image, SIGNAL(triggered()), this, SLOT(saveImage()));
connect(ui->hashBox, SIGNAL(fileHashingFinished(QList<HashedFile>)), this, SLOT(fileHashingFinished(QList<HashedFile>))); connect(ui->hashBox, SIGNAL(fileHashingFinished(QList<HashedFile>)), this, SLOT(fileHashingFinished(QList<HashedFile>)));
@ -975,6 +978,14 @@ void ChatWidget::contextMenuTextBrowser(QPoint point)
contextMnu->addSeparator(); contextMnu->addSeparator();
contextMnu->addAction(ui->actionClearChatHistory); contextMnu->addAction(ui->actionClearChatHistory);
contextMnu->addAction(ui->actionQuote);
QTextCursor cursor = ui->textBrowser->cursorForPosition(point);
if(ImageUtil::checkImage(cursor))
{
ui->actionSave_image->setData(point);
contextMnu->addAction(ui->actionSave_image);
}
contextMnu->exec(ui->textBrowser->viewport()->mapToGlobal(point)); contextMnu->exec(ui->textBrowser->viewport()->mapToGlobal(point));
delete(contextMnu); delete(contextMnu);
@ -1668,3 +1679,21 @@ bool ChatWidget::setStyle()
return false; return false;
} }
void ChatWidget::quote()
{
QString text = ui->textBrowser->textCursor().selection().toPlainText();
if(text.length() > 0)
{
QStringList sl = text.split(QRegExp("[\r\n]"),QString::SkipEmptyParts);
text = sl.join("\n>");
emit ui->chatTextEdit->append(QString(">") + text);
}
}
void ChatWidget::saveImage()
{
QPoint point = ui->actionSave_image->data().toPoint();
QTextCursor cursor = ui->textBrowser->cursorForPosition(point);
ImageUtil::extractImage(window(), cursor);
}

View File

@ -185,6 +185,9 @@ private slots:
bool fileSave(); bool fileSave();
bool fileSaveAs(); bool fileSaveAs();
void quote();
void saveImage();
private: private:
bool findText(const QString& qsStringToFind); bool findText(const QString& qsStringToFind);
bool findText(const QString& qsStringToFind, bool bBackWard, bool bForceMove); bool findText(const QString& qsStringToFind, bool bBackWard, bool bForceMove);

View File

@ -972,6 +972,23 @@ border-image: url(:/images/closepressed.png)
<string>Search Box</string> <string>Search Box</string>
</property> </property>
</action> </action>
<action name="actionQuote">
<property name="text">
<string>Quote</string>
</property>
<property name="toolTip">
<string>Quotes the selected text</string>
</property>
</action>
<action name="actionSave_image">
<property name="icon">
<iconset resource="../images.qrc">
<normaloff>:/images/document_save.png</normaloff>:/images/document_save.png</iconset>
</property>
<property name="text">
<string>Save image</string>
</property>
</action>
</widget> </widget>
<customwidgets> <customwidgets>
<customwidget> <customwidget>

View File

@ -41,6 +41,7 @@ MimeTextEdit::MimeTextEdit(QWidget *parent)
mCompleterKeyModifiers = Qt::ControlModifier; mCompleterKeyModifiers = Qt::ControlModifier;
mCompleterKey = Qt::Key_Space; mCompleterKey = Qt::Key_Space;
mForceCompleterShowNextKeyEvent = false; mForceCompleterShowNextKeyEvent = false;
highliter = new RsSyntaxHighlighter(this);
} }
bool MimeTextEdit::canInsertFromMimeData(const QMimeData* source) const bool MimeTextEdit::canInsertFromMimeData(const QMimeData* source) const
@ -231,6 +232,8 @@ void MimeTextEdit::contextMenuEvent(QContextMenuEvent *e)
/* Add actions for pasting links */ /* Add actions for pasting links */
contextMenu->addAction( tr("Paste as plain text"), this, SLOT(pastePlainText())); contextMenu->addAction( tr("Paste as plain text"), this, SLOT(pastePlainText()));
QAction *spoilerAction = contextMenu->addAction(tr("Spoiler"), this, SLOT(spoiler()));
spoilerAction->setToolTip(tr("Select text to hide, then push this button"));
contextMenu->addSeparator(); contextMenu->addSeparator();
QAction *pasteLinkAction = contextMenu->addAction(QIcon(":/images/pasterslink.png"), tr("Paste RetroShare Link"), this, SLOT(pasteLink())); QAction *pasteLinkAction = contextMenu->addAction(QIcon(":/images/pasterslink.png"), tr("Paste RetroShare Link"), this, SLOT(pasteLink()));
contextMenu->addAction(QIcon(":/images/pasterslink.png"), tr("Paste my certificate link"), this, SLOT(pasteOwnCertificateLink())); contextMenu->addAction(QIcon(":/images/pasterslink.png"), tr("Paste my certificate link"), this, SLOT(pasteOwnCertificateLink()));
@ -268,3 +271,8 @@ void MimeTextEdit::pastePlainText()
{ {
insertPlainText(QApplication::clipboard()->text()); insertPlainText(QApplication::clipboard()->text());
} }
void MimeTextEdit::spoiler()
{
RsHtml::insertSpoilerText(this->textCursor());
}

View File

@ -24,11 +24,14 @@
#include <QCompleter> #include <QCompleter>
#include "RSTextEdit.h" #include "RSTextEdit.h"
#include "util/RsSyntaxHighlighter.h"
class MimeTextEdit : public RSTextEdit class MimeTextEdit : public RSTextEdit
{ {
Q_OBJECT Q_OBJECT
Q_PROPERTY(QColor textColorQuote READ textColorQuote WRITE setTextColorQuote)
public: public:
MimeTextEdit(QWidget *parent = 0); MimeTextEdit(QWidget *parent = 0);
@ -44,6 +47,11 @@ public:
// Add QAction to context menu (action won't be deleted) // Add QAction to context menu (action won't be deleted)
void addContextMenuAction(QAction *action); void addContextMenuAction(QAction *action);
QColor textColorQuote() const { return highliter->textColorQuote();}
public slots:
void setTextColorQuote(QColor textColorQuote) { highliter->setTextColorQuote(textColorQuote);}
signals: signals:
void calculateContextMenuActions(); void calculateContextMenuActions();
@ -59,6 +67,8 @@ private slots:
void pasteLink(); void pasteLink();
void pasteOwnCertificateLink(); void pasteOwnCertificateLink();
void pastePlainText(); void pastePlainText();
void spoiler();
private: private:
QString textUnderCursor() const; QString textUnderCursor() const;
@ -69,6 +79,7 @@ private:
bool mForceCompleterShowNextKeyEvent; bool mForceCompleterShowNextKeyEvent;
QString mCompleterStartString; QString mCompleterStartString;
QList<QAction*> mContextMenuActions; QList<QAction*> mContextMenuActions;
RsSyntaxHighlighter *highliter;
}; };
#endif // MIMETEXTEDIT_H #endif // MIMETEXTEDIT_H

View File

@ -14,6 +14,8 @@ RSTextBrowser::RSTextBrowser(QWidget *parent) :
mImageBlockWidget = NULL; mImageBlockWidget = NULL;
mLinkClickActive = true; mLinkClickActive = true;
highliter = new RsSyntaxHighlighter(this);
connect(this, SIGNAL(anchorClicked(QUrl)), this, SLOT(linkClicked(QUrl))); connect(this, SIGNAL(anchorClicked(QUrl)), this, SLOT(linkClicked(QUrl)));
} }

View File

@ -2,6 +2,7 @@
#define RSTEXTBROWSER_H #define RSTEXTBROWSER_H
#include <QTextBrowser> #include <QTextBrowser>
#include "util/RsSyntaxHighlighter.h"
class RSImageBlockWidget; class RSImageBlockWidget;
@ -9,6 +10,8 @@ class RSTextBrowser : public QTextBrowser
{ {
Q_OBJECT Q_OBJECT
Q_PROPERTY(QColor textColorQuote READ textColorQuote WRITE setTextColorQuote)
public: public:
explicit RSTextBrowser(QWidget *parent = 0); explicit RSTextBrowser(QWidget *parent = 0);
@ -20,8 +23,11 @@ public:
virtual QVariant loadResource(int type, const QUrl &name); virtual QVariant loadResource(int type, const QUrl &name);
QColor textColorQuote() const { return highliter->textColorQuote();}
public slots: public slots:
void showImages(); void showImages();
void setTextColorQuote(QColor textColorQuote) { highliter->setTextColorQuote(textColorQuote);}
private slots: private slots:
void linkClicked(const QUrl &url); void linkClicked(const QUrl &url);
@ -35,6 +41,7 @@ private:
bool mShowImages; bool mShowImages;
RSImageBlockWidget *mImageBlockWidget; RSImageBlockWidget *mImageBlockWidget;
bool mLinkClickActive; bool mLinkClickActive;
RsSyntaxHighlighter *highliter;
}; };
#endif // RSTEXTBROWSER_H #endif // RSTEXTBROWSER_H

View File

@ -22,6 +22,7 @@
#include <QDateTime> #include <QDateTime>
#include <QMessageBox> #include <QMessageBox>
#include <QKeyEvent> #include <QKeyEvent>
#include <QScrollBar>
#include "GxsForumThreadWidget.h" #include "GxsForumThreadWidget.h"
#include "ui_GxsForumThreadWidget.h" #include "ui_GxsForumThreadWidget.h"
@ -39,6 +40,7 @@
#include "util/DateTime.h" #include "util/DateTime.h"
#include "gui/common/UIStateHelper.h" #include "gui/common/UIStateHelper.h"
#include "util/QtVersion.h" #include "util/QtVersion.h"
#include "util/imageutil.h"
#include <retroshare/rsgxsforums.h> #include <retroshare/rsgxsforums.h>
#include <retroshare/rsgrouter.h> #include <retroshare/rsgrouter.h>
@ -135,6 +137,7 @@ GxsForumThreadWidget::GxsForumThreadWidget(const RsGxsGroupId &forumId, QWidget
mThreadCompareRole->setRole(COLUMN_THREAD_DATE, ROLE_THREAD_SORT); mThreadCompareRole->setRole(COLUMN_THREAD_DATE, ROLE_THREAD_SORT);
connect(ui->threadTreeWidget, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(threadListCustomPopupMenu(QPoint))); connect(ui->threadTreeWidget, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(threadListCustomPopupMenu(QPoint)));
connect(ui->postText, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(contextMenuTextBrowser(QPoint)));
ui->subscribeToolButton->hide() ; ui->subscribeToolButton->hide() ;
connect(ui->subscribeToolButton, SIGNAL(subscribe(bool)), this, SLOT(subscribeGroup(bool))); connect(ui->subscribeToolButton, SIGNAL(subscribe(bool)), this, SLOT(subscribeGroup(bool)));
@ -154,6 +157,8 @@ GxsForumThreadWidget::GxsForumThreadWidget(const RsGxsGroupId &forumId, QWidget
connect(ui->filterLineEdit, SIGNAL(textChanged(QString)), this, SLOT(filterItems(QString))); connect(ui->filterLineEdit, SIGNAL(textChanged(QString)), this, SLOT(filterItems(QString)));
connect(ui->filterLineEdit, SIGNAL(filterChanged(int)), this, SLOT(filterColumnChanged(int))); connect(ui->filterLineEdit, SIGNAL(filterChanged(int)), this, SLOT(filterColumnChanged(int)));
connect(ui->actionSave_image, SIGNAL(triggered()), this, SLOT(saveImage()));
/* Set own item delegate */ /* Set own item delegate */
RSItemDelegate *itemDelegate = new RSItemDelegate(this); RSItemDelegate *itemDelegate = new RSItemDelegate(this);
itemDelegate->setSpacing(QSize(0, 2)); itemDelegate->setSpacing(QSize(0, 2));
@ -497,7 +502,27 @@ void GxsForumThreadWidget::threadListCustomPopupMenu(QPoint /*point*/)
contextMnu.addSeparator(); contextMnu.addSeparator();
contextMnu.addAction(replyauthorAct); contextMnu.addAction(replyauthorAct);
contextMnu.exec(QCursor::pos()); contextMnu.exec(QCursor::pos());
}
void GxsForumThreadWidget::contextMenuTextBrowser(QPoint point)
{
QMatrix matrix;
matrix.translate(ui->postText->horizontalScrollBar()->value(), ui->postText->verticalScrollBar()->value());
QMenu *contextMnu = ui->postText->createStandardContextMenu(matrix.map(point));
contextMnu->addSeparator();
QTextCursor cursor = ui->postText->cursorForPosition(point);
if(ImageUtil::checkImage(cursor))
{
ui->actionSave_image->setData(point);
contextMnu->addAction(ui->actionSave_image);
}
contextMnu->exec(ui->postText->viewport()->mapToGlobal(point));
delete(contextMnu);
} }
bool GxsForumThreadWidget::eventFilter(QObject *obj, QEvent *event) bool GxsForumThreadWidget::eventFilter(QObject *obj, QEvent *event)
@ -1856,6 +1881,13 @@ void GxsForumThreadWidget::replyForumMessageData(const RsGxsForumMsg &msg)
} }
} }
void GxsForumThreadWidget::saveImage()
{
QPoint point = ui->actionSave_image->data().toPoint();
QTextCursor cursor = ui->postText->cursorForPosition(point);
ImageUtil::extractImage(window(), cursor);
}
void GxsForumThreadWidget::changedViewBox() void GxsForumThreadWidget::changedViewBox()
{ {
if (mInProcessSettings) { if (mInProcessSettings) {

View File

@ -70,6 +70,7 @@ protected:
private slots: private slots:
/** Create the context popup menu and it's submenus */ /** Create the context popup menu and it's submenus */
void threadListCustomPopupMenu(QPoint point); void threadListCustomPopupMenu(QPoint point);
void contextMenuTextBrowser(QPoint point);
void changedThread(); void changedThread();
void clickedThread (QTreeWidgetItem *item, int column); void clickedThread (QTreeWidgetItem *item, int column);
@ -80,7 +81,7 @@ private slots:
void replyMessageData(const RsGxsForumMsg &msg); void replyMessageData(const RsGxsForumMsg &msg);
void replyForumMessageData(const RsGxsForumMsg &msg); void replyForumMessageData(const RsGxsForumMsg &msg);
void saveImage();
//void print(); //void print();

View File

@ -453,10 +453,22 @@
<verstretch>10</verstretch> <verstretch>10</verstretch>
</sizepolicy> </sizepolicy>
</property> </property>
<property name="contextMenuPolicy">
<enum>Qt::CustomContextMenu</enum>
</property>
</widget> </widget>
</widget> </widget>
</item> </item>
</layout> </layout>
<action name="actionSave_image">
<property name="icon">
<iconset resource="../images.qrc">
<normaloff>:/images/document_save.png</normaloff>:/images/document_save.png</iconset>
</property>
<property name="text">
<string>Save image</string>
</property>
</action>
</widget> </widget>
<customwidgets> <customwidgets>
<customwidget> <customwidget>

View File

@ -27,8 +27,8 @@
<table class='bubbleFooter' width='100%'> <table class='bubbleFooter' width='100%'>
<tr> <tr>
<td align='right'> <td align='right'>
<span width='130' align='right' class='time'>%time% - </span> <span width='130' align='right' class='time'>%time%%nome% - %/nome%</span>
<span class='name'>%name%</span> <span class='name'>%nome%%name%%/nome%</span>
</td> </td>
</tr> </tr>
</table> </table>

View File

@ -28,7 +28,7 @@
<table class='bubbleFooter' width='100%'> <table class='bubbleFooter' width='100%'>
<tr> <tr>
<td align='left'> <td align='left'>
<span class='name'>&nbsp;&nbsp;%name% - </span> <span class='name'>&nbsp;&nbsp;%nome%%name% - %/nome%</span>
<span width='130' align='right' class='time'>%time%</span> <span width='130' align='right' class='time'>%time%</span>
</td> </td>
</tr> </tr>

View File

@ -28,8 +28,8 @@
<table class='bubbleFooter' width='100%'> <table class='bubbleFooter' width='100%'>
<tr> <tr>
<td align='right'> <td align='right'>
<span width='130' align='right' class='time'>%time% - </span> <span width='130' align='right' class='time'>%time%%nome% - %/nome%</span>
<span class='name'>%name%</span> <span class='name'>%nome%%name%%/nome%</span>
</td> </td>
</tr> </tr>
</table> </table>

View File

@ -28,7 +28,7 @@
<table class='bubbleFooter' width='100%'> <table class='bubbleFooter' width='100%'>
<tr> <tr>
<td align='left'> <td align='left'>
<span class='name'>&nbsp;&nbsp;%name% - </span> <span class='name'>&nbsp;&nbsp;%nome%%name% - %/nome%</span>
<span width='130' align='right' class='time'>%time%</span> <span width='130' align='right' class='time'>%time%</span>
</td> </td>
</tr> </tr>

View File

@ -28,7 +28,7 @@
<table class='bubbleFooter' width='100%'> <table class='bubbleFooter' width='100%'>
<tr> <tr>
<td align='left'> <td align='left'>
<span class='name'>&nbsp;&nbsp;%name% - </span> <span class='name'>&nbsp;&nbsp;%nome%%name% - %/nome%</span>
<span width='130' align='right' class='time'>%time%</span> <span width='130' align='right' class='time'>%time%</span>
</td> </td>
</tr> </tr>

View File

@ -3,5 +3,5 @@
</style> </style>
<span class='hincomingTime'>%date% %time%</span> <span class='hincomingTime'>%date% %time%</span>
<span class='name hincomingName'>%name%:</span> <span class='name hincomingName'>%nome%%name%:%/nome%</span>
%message% %message%

View File

@ -3,5 +3,5 @@
</style> </style>
<span class='houtgoingTime'>%date% %time%</span> <span class='houtgoingTime'>%date% %time%</span>
<span class='name houtgoingName'>%name%:</span> <span class='name houtgoingName'>%nome%%name%:%/nome%</span>
%message% %message%

View File

@ -3,5 +3,5 @@
</style> </style>
<span class='incomingTime'>%time%</span> <span class='incomingTime'>%time%</span>
<span class='name incomingName'>%name%:</span> <span class='name incomingName'>%nome%%name%:%/nome%</span>
%message% %message%

View File

@ -25,3 +25,7 @@
.systemTime { .systemTime {
color:#808080; color:#808080;
} }
.meName {
font-weight: bold;
}

View File

@ -3,5 +3,5 @@
</style> </style>
<span class='ooutgoingTime'>%time%</span> <span class='ooutgoingTime'>%time%</span>
<span class='name ooutgoingName'>%name%:</span> <span class='name ooutgoingName'>%nome%%name%:%/nome%</span>
%message% %message%

View File

@ -3,5 +3,5 @@
</style> </style>
<span class='outgoingTime'>%time%</span> <span class='outgoingTime'>%time%</span>
<span class='name outgoingName'>%name%:</span> <span class='name outgoingName'>%nome%%name%:%/nome%</span>
%message% %message%

View File

@ -3,5 +3,5 @@
</style> </style>
<span class='hincomingTime'>%date% %time%</span> <span class='hincomingTime'>%date% %time%</span>
<span class='name hincomingName'>%name%</span><br> <span class='name hincomingName'>%nome%%name%%/nome%%me%*%/me%</span><br>
%message% %message%

View File

@ -3,5 +3,5 @@
</style> </style>
<span class='houtgoingTime'>%date% %time%</span> <span class='houtgoingTime'>%date% %time%</span>
<span class='name houtgoingName'>%name%</span><br> <span class='name houtgoingName'>%nome%%name%%/nome%%me%*%/me%</span><br>
%message% %message%

View File

@ -3,5 +3,5 @@
</style> </style>
<span class='incomingTime'>%time%</span> <span class='incomingTime'>%time%</span>
<span class='name incomingName'>%name%</span><br> <span class='name incomingName'>%nome%%name%%/nome%%me%*%/me%</span><br>
%message% %message%

View File

@ -47,3 +47,7 @@
.systemName { .systemName {
} }
.meName {
font-weight: bold;
}

View File

@ -3,5 +3,5 @@
</style> </style>
<span class='ooutgoingTime'>%time%</span> <span class='ooutgoingTime'>%time%</span>
<span class='name ooutgoingName'>%name%</span><br> <span class='name ooutgoingName'>%nome%%name%%/nome%%me%*%/me%</span><br>
%message% %message%

View File

@ -3,5 +3,5 @@
</style> </style>
<span class='outgoingTime'>%time%</span> <span class='outgoingTime'>%time%</span>
<span class='name outgoingName'>%name%</span><br> <span class='name outgoingName'>%nome%%name%%/nome%%me%*%/me%</span><br>
%message% %message%

View File

@ -188,6 +188,11 @@ NetworkDialog
qproperty-backgroundColorDenied: lightGray; qproperty-backgroundColorDenied: lightGray;
} }
RSTextBrowser, MimeTextEdit
{
qproperty-textColorQuote: rgb(120, 153, 34);
}
QLabel#headerTextLabel QLabel#headerTextLabel
{ {
qproperty-fontSizeFactor: 225; qproperty-fontSizeFactor: 225;

View File

@ -338,6 +338,7 @@ void ChatPage::setPreviewMessages(QString &stylePath, QString styleVariant, QTex
textBrowser->append(style.formatMessage(ChatStyle::FORMATMSG_OUTGOING, nameOutgoing, timestmp, tr("Outgoing message"))); textBrowser->append(style.formatMessage(ChatStyle::FORMATMSG_OUTGOING, nameOutgoing, timestmp, tr("Outgoing message")));
textBrowser->append(style.formatMessage(ChatStyle::FORMATMSG_OOUTGOING, nameOutgoing, timestmp, tr("Outgoing offline message"))); textBrowser->append(style.formatMessage(ChatStyle::FORMATMSG_OOUTGOING, nameOutgoing, timestmp, tr("Outgoing offline message")));
textBrowser->append(style.formatMessage(ChatStyle::FORMATMSG_SYSTEM, tr("System"), timestmp, tr("System message"))); textBrowser->append(style.formatMessage(ChatStyle::FORMATMSG_SYSTEM, tr("System"), timestmp, tr("System message")));
textBrowser->append(style.formatMessage(ChatStyle::FORMATMSG_OUTGOING, tr("UserName"), timestmp, tr("/me is sending a message with /me")));
} }
void ChatPage::fillPreview(QListWidget *listWidget, QComboBox *comboBox, QTextBrowser *textBrowser) void ChatPage::fillPreview(QListWidget *listWidget, QComboBox *comboBox, QTextBrowser *textBrowser)

View File

@ -523,7 +523,9 @@ HEADERS += rshare.h \
gui/groups/CreateGroup.h \ gui/groups/CreateGroup.h \
gui/GetStartedDialog.h \ gui/GetStartedDialog.h \
gui/settings/WebuiPage.h \ gui/settings/WebuiPage.h \
gui/statistics/BWGraph.h gui/statistics/BWGraph.h \
util/RsSyntaxHighlighter.h \
util/imageutil.h
# gui/ForumsDialog.h \ # gui/ForumsDialog.h \
# gui/forums/ForumDetails.h \ # gui/forums/ForumDetails.h \
@ -871,7 +873,9 @@ SOURCES += main.cpp \
gui/statistics/StatisticsWindow.cpp \ gui/statistics/StatisticsWindow.cpp \
gui/statistics/BwCtrlWindow.cpp \ gui/statistics/BwCtrlWindow.cpp \
gui/statistics/RttStatistics.cpp \ gui/statistics/RttStatistics.cpp \
gui/statistics/BWGraph.cpp gui/statistics/BWGraph.cpp \
util/RsSyntaxHighlighter.cpp \
util/imageutil.cpp
# gui/ForumsDialog.cpp \ # gui/ForumsDialog.cpp \
# gui/forums/ForumDetails.cpp \ # gui/forums/ForumDetails.cpp \

View File

@ -26,6 +26,7 @@
#include <QMessageBox> #include <QMessageBox>
#include <QTextDocumentFragment> #include <QTextDocumentFragment>
#include <qmath.h> #include <qmath.h>
#include <QUrl>
#include "HandleRichText.h" #include "HandleRichText.h"
#include "gui/RetroShareLink.h" #include "gui/RetroShareLink.h"
@ -569,7 +570,6 @@ static void optimizeHtml(QDomDocument& doc
, QHash<QString, QStringList*> &stylesList , QHash<QString, QStringList*> &stylesList
, QHash<QString, QString> &knownStyle) , QHash<QString, QString> &knownStyle)
{ {
bool bFirstP=true;
if (doc.documentElement().namedItem("style").toElement().attributeNode("RSOptimized").isAttr()) { if (doc.documentElement().namedItem("style").toElement().attributeNode("RSOptimized").isAttr()) {
//Already optimized only get StyleList //Already optimized only get StyleList
QDomElement styleElem = doc.documentElement().namedItem("style").toElement(); QDomElement styleElem = doc.documentElement().namedItem("style").toElement();
@ -611,51 +611,9 @@ static void optimizeHtml(QDomDocument& doc
QDomNodeList children = currentElement.childNodes(); QDomNodeList children = currentElement.childNodes();
for (uint index = 0; index < children.length(); ) { for (uint index = 0; index < children.length(); ) {
QDomNode node = children.item(index); QDomNode node = children.item(index);
// Compress style attribute
styleNode = node.attributes().namedItem("style");
if (styleNode.isAttr()) {
QDomAttr styleAttr = styleNode.toAttr();
QString style = styleAttr.value().simplified().trimmed();
style.replace("margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px;", "margin:0px 0px 0px 0px;");
style.replace("; ", ";");
QString className = knownStyle.value(style);
if (className.isEmpty()) {
// Create a new class
className = QString("S%1").arg(knownStyle.count());
knownStyle.insert(style, className);
// Now add this for each attribute values
QStringList styles = style.split(';');
foreach (QString pair, styles) {
pair.replace(" ","");
if (!pair.isEmpty()) {
QStringList* stylesListItem = stylesList.value(pair);
if(!stylesListItem){
// If value doesn't exist create it
stylesListItem = new QStringList();
stylesList.insert(pair, stylesListItem);
}
//Add the new class to this value
stylesListItem->push_back(className);
}
}
}
style.clear();
node.attributes().removeNamedItem("style");
styleNode.clear();
if (!className.isEmpty()) {
QDomNode classNode = doc.createAttribute("class");
classNode.setNodeValue(className);
node.attributes().setNamedItem(classNode);
}
}
if (node.isElement()) { if (node.isElement()) {
QDomElement element = node.toElement(); QDomElement element = node.toElement();
styleNode = node.attributes().namedItem("style");
// not <p> // not <p>
if (addBR && element.tagName().toLower() != "p") { if (addBR && element.tagName().toLower() != "p") {
@ -686,19 +644,38 @@ static void optimizeHtml(QDomDocument& doc
continue; continue;
} }
//hidden text in a
if (element.tagName().toLower() == "a") {
if(element.hasAttribute("href")){
QString href = element.attribute("href", "");
if(href.startsWith("hidden:")){
//this text should be hidden and appear in title
//we need this trick, because QTextEdit doesn't export the title attribute
QString title = href.remove(0, QString("hidden:").length());
QString text = element.text();
element.setTagName("span");
element.removeAttribute("href");
QDomNodeList c = element.childNodes();
for(int i = 0; i < c.count(); i++){
element.removeChild(c.at(i));
};
element.setAttribute(QString("title"), title);
QDomText textnode = doc.createTextNode(text);
element.appendChild(textnode);
}
}
}
// iterate children // iterate children
optimizeHtml(doc, element, stylesList, knownStyle); optimizeHtml(doc, element, stylesList, knownStyle);
// <p> // <p>
if (element.tagName().toLower() == "p") { if (element.tagName().toLower() == "p") {
//If it's the first <p>, replace it as <span> otherwise make "\n" before first line
if (bFirstP) {
element.setTagName("span");
bFirstP = false;
}
// <p style="..."> // <p style="...">
if (element.attributes().size() == 1 && styleNode.isAttr()) { if (styleNode.isAttr()) {
QString style = styleNode.toAttr().value().simplified(); QString style = styleNode.toAttr().value().simplified().trimmed();
style.replace("margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px;", "margin:0px 0px 0px 0px;");
style.replace("; ", ";");
if (style == "margin:0px 0px 0px 0px;-qt-block-indent:0;text-indent:0px;" if (style == "margin:0px 0px 0px 0px;-qt-block-indent:0;text-indent:0px;"
|| style.startsWith("-qt-paragraph-type:empty;margin:0px 0px 0px 0px;-qt-block-indent:0;text-indent:0px;")) { || style.startsWith("-qt-paragraph-type:empty;margin:0px 0px 0px 0px;-qt-block-indent:0;text-indent:0px;")) {
@ -719,6 +696,46 @@ static void optimizeHtml(QDomDocument& doc
} }
addBR = false; addBR = false;
} }
// Compress style attribute
if (styleNode.isAttr()) {
QDomAttr styleAttr = styleNode.toAttr();
QString style = styleAttr.value().simplified().trimmed();
style.replace("margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px;", "margin:0px 0px 0px 0px;");
style.replace("; ", ";");
QString className = knownStyle.value(style);
if (className.isEmpty()) {
// Create a new class
className = QString("S%1").arg(knownStyle.count());
knownStyle.insert(style, className);
// Now add this for each attribute values
QStringList styles = style.split(';');
foreach (QString pair, styles) {
pair.replace(" ","");
if (!pair.isEmpty()) {
QStringList* stylesListItem = stylesList.value(pair);
if(!stylesListItem){
// If value doesn't exist create it
stylesListItem = new QStringList();
stylesList.insert(pair, stylesListItem);
}
//Add the new class to this value
stylesListItem->push_back(className);
}
}
}
style.clear();
node.attributes().removeNamedItem("style");
styleNode.clear();
if (!className.isEmpty()) {
QDomNode classNode = doc.createAttribute("class");
classNode.setNodeValue(className);
node.attributes().setNamedItem(classNode);
}
}
} }
++index; ++index;
} }
@ -1010,3 +1027,20 @@ QString RsHtml::makeQuotedText(RSTextBrowser *browser)
text = sl.join("\n>"); text = sl.join("\n>");
return QString(">") + text; return QString(">") + text;
} }
void RsHtml::insertSpoilerText(QTextCursor cursor)
{
QString hiddentext = cursor.selection().toPlainText();
if(hiddentext.isEmpty()) return;
QString publictext = "*SPOILER*";
QString encoded = hiddentext;
encoded = encoded.replace(QChar('\"'), QString("&quot;"));
encoded = encoded.replace(QChar('\''), QString("&apos;"));
encoded = encoded.replace(QChar('<'), QString("&lt;"));
encoded = encoded.replace(QChar('>'), QString("&gt;"));
encoded = encoded.replace(QChar('&'), QString("&amp;"));
QString html = QString("<a href=\"hidden:%1\" title=\"%1\">%2</a>").arg(encoded, publictext);
cursor.insertHtml(html);
}

View File

@ -53,6 +53,7 @@ class QDomDocument;
class QDomElement; class QDomElement;
class EmbedInHtml; class EmbedInHtml;
class RetroShareLink; class RetroShareLink;
class QTextCursor;
class RsHtml class RsHtml
{ {
@ -75,6 +76,7 @@ public:
static QString plainText(const std::string &text); static QString plainText(const std::string &text);
static QString makeQuotedText(RSTextBrowser* browser); static QString makeQuotedText(RSTextBrowser* browser);
static void insertSpoilerText(QTextCursor cursor);
protected: protected:
void embedHtml(QTextDocument *textDocument, QDomDocument &doc, QDomElement &currentElement, EmbedInHtml& embedInfos, ulong flag); void embedHtml(QTextDocument *textDocument, QDomDocument &doc, QDomElement &currentElement, EmbedInHtml& embedInfos, ulong flag);

View File

@ -0,0 +1,45 @@
#include "RsSyntaxHighlighter.h"
RsSyntaxHighlighter::RsSyntaxHighlighter(QTextEdit *parent)
: QSyntaxHighlighter(parent)
{
}
void RsSyntaxHighlighter::highlightBlock(const QString &text)
{
QRegExp endl("[\\r\\n\\x2028]"); //Usually 0x2028 cahracter is used for newline, no idea why
int index = 0;
QStringList lines = text.split(endl);
foreach (const QString &line, lines) {
if(line.trimmed().startsWith('>')) {
setFormat(index, line.length(), quotationFormat);
}
index += line.length() + 1;
}
//Make it work with the compact chat style
if(lines.length() > 0){
int i = lines[0].indexOf(": >");
if(i != -1) {
setFormat(i+2, lines[0].length()-i-2, quotationFormat);
}
}
}
void RsSyntaxHighlighter::setTextColorQuote(QColor textColorQuote)
{
quotationFormat.setForeground(textColorQuote);
this->rehighlight();
}
//Dumping the raw unicode string into the console in Base64 encoding
/*
QByteArray uniline;
const QChar* qca = line.unicode();
for(int i=0; qca[i]!='\0' ;++i)
{
uniline.append(qca[i].row());
uniline.append(qca[i].cell());
}
std::cout << "Line: " << uniline.toBase64().toStdString() << std::endl;
*/

View File

@ -0,0 +1,31 @@
#ifndef RSSYNTAXHIGHLIGHTER_H
#define RSSYNTAXHIGHLIGHTER_H
#include <QObject>
#include <QSyntaxHighlighter>
#include <QTextEdit>
class RsSyntaxHighlighter : public QSyntaxHighlighter
{
Q_OBJECT
Q_PROPERTY(QColor textColorQuote READ textColorQuote WRITE setTextColorQuote)
public:
RsSyntaxHighlighter(QTextEdit *parent = 0);
QColor textColorQuote() const { return quotationFormat.foreground().color(); };
protected:
void highlightBlock(const QString &text);
private:
QTextCharFormat quotationFormat;
signals:
public slots:
void setTextColorQuote(QColor textColorQuote);
};
#endif // RSSYNTAXHIGHLIGHTER_H

View File

@ -0,0 +1,51 @@
#include "imageutil.h"
#include "util/misc.h"
#include <QMessageBox>
#include <QTextCursor>
#include <QByteArray>
#include <QString>
#include <QImage>
#include <QTextDocumentFragment>
#include <QApplication>
ImageUtil::ImageUtil() {}
void ImageUtil::extractImage(QWidget *window, QTextCursor cursor)
{
cursor.movePosition(QTextCursor::Left, QTextCursor::MoveAnchor, 1);
cursor.movePosition(QTextCursor::Right, QTextCursor::KeepAnchor, 2);
QString imagestr = cursor.selection().toHtml();
bool success = false;
int start = imagestr.indexOf("base64,") + 7;
int stop = imagestr.indexOf("\"", start);
int length = stop - start;
if((start >= 0) && (length > 0))
{
QByteArray ba = QByteArray::fromBase64(imagestr.mid(start, length).toLatin1());
QImage image = QImage::fromData(ba);
if(!image.isNull())
{
QString file;
success = true;
if(misc::getSaveFileName(window, RshareSettings::LASTDIR_IMAGES, "Save Picture File", "Pictures (*.png *.xpm *.jpg)", file))
{
if(!image.save(file, 0, 100))
if(!image.save(file + ".png", 0, 100))
QMessageBox::warning(window, QApplication::translate("ImageUtil", "Save image"), QApplication::translate("ImageUtil", "Cannot save the image, invalid filename"));
}
}
}
if(!success)
{
QMessageBox::warning(window, QApplication::translate("ImageUtil", "Save image"), QApplication::translate("ImageUtil", "Not an image"));
}
}
bool ImageUtil::checkImage(QTextCursor cursor)
{
cursor.movePosition(QTextCursor::Left, QTextCursor::MoveAnchor, 1);
cursor.movePosition(QTextCursor::Right, QTextCursor::KeepAnchor, 2);
QString imagestr = cursor.selection().toHtml();
return imagestr.indexOf("base64,") != -1;
}

View File

@ -0,0 +1,17 @@
#ifndef IMAGEUTIL_H
#define IMAGEUTIL_H
#include <QTextCursor>
#include <QWidget>
class ImageUtil
{
public:
ImageUtil();
static void extractImage(QWidget *window, QTextCursor cursor);
static bool checkImage(QTextCursor cursor);
};
#endif // IMAGEUTIL_H