mirror of
https://github.com/RetroShare/RetroShare.git
synced 2024-12-28 00:49:28 -05:00
Merge pull request #189 from hunbernd/feature/chat-enhancements
Chat enhancements
This commit is contained in:
commit
8e9fe6d800
@ -267,10 +267,12 @@ static QString getStyle(const QDir &styleDir, const QString &styleVariant, enumG
|
||||
|
||||
QString ChatStyle::formatMessage(enumFormatMessage type, const QString &name, const QDateTime ×tamp, const QString &message, unsigned int flag)
|
||||
{
|
||||
bool me = false;
|
||||
QDomDocument doc ;
|
||||
QString styleOptimized ;
|
||||
QString errorMsg ; int errorLine ; int errorColumn ;
|
||||
QString messageBody = message ;
|
||||
me = me || message.trimmed().startsWith("/me ");
|
||||
if (doc.setContent(messageBody, &errorMsg, &errorLine, &errorColumn)) {
|
||||
QDomElement body = doc.documentElement();
|
||||
if (!body.isNull()){
|
||||
@ -279,6 +281,12 @@ QString ChatStyle::formatMessage(enumFormatMessage type, const QString &name, co
|
||||
for (int curs = 0; curs < count; ++curs){
|
||||
QDomNode it = body.childNodes().item(curs);
|
||||
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;
|
||||
QTextStream stream(&str);
|
||||
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 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>"));
|
||||
|
||||
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)
|
||||
.replace("%shortname%", strShortName)
|
||||
.replace("%date%", strDate)
|
||||
.replace("%time%", strTime)
|
||||
#ifdef COLORED_NICKNAMES
|
||||
|
@ -51,6 +51,7 @@
|
||||
#include "util/HandleRichText.h"
|
||||
#include "gui/chat/ChatUserNotify.h"//For BradCast
|
||||
#include "util/DateTime.h"
|
||||
#include "util/imageutil.h"
|
||||
|
||||
#include <retroshare/rsstatus.h>
|
||||
#include <retroshare/rsidentity.h>
|
||||
@ -125,6 +126,8 @@ ChatWidget::ChatWidget(QWidget *parent) :
|
||||
connect(ui->actionChooseFont, SIGNAL(triggered()), this, SLOT(chooseFont()));
|
||||
connect(ui->actionChooseColor, SIGNAL(triggered()), this, SLOT(chooseColor()));
|
||||
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>)));
|
||||
|
||||
@ -975,6 +978,14 @@ void ChatWidget::contextMenuTextBrowser(QPoint point)
|
||||
|
||||
contextMnu->addSeparator();
|
||||
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));
|
||||
delete(contextMnu);
|
||||
@ -1668,3 +1679,21 @@ bool ChatWidget::setStyle()
|
||||
|
||||
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);
|
||||
}
|
||||
|
@ -185,6 +185,9 @@ private slots:
|
||||
bool fileSave();
|
||||
bool fileSaveAs();
|
||||
|
||||
void quote();
|
||||
void saveImage();
|
||||
|
||||
private:
|
||||
bool findText(const QString& qsStringToFind);
|
||||
bool findText(const QString& qsStringToFind, bool bBackWard, bool bForceMove);
|
||||
|
@ -972,6 +972,23 @@ border-image: url(:/images/closepressed.png)
|
||||
<string>Search Box</string>
|
||||
</property>
|
||||
</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>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
|
@ -41,6 +41,7 @@ MimeTextEdit::MimeTextEdit(QWidget *parent)
|
||||
mCompleterKeyModifiers = Qt::ControlModifier;
|
||||
mCompleterKey = Qt::Key_Space;
|
||||
mForceCompleterShowNextKeyEvent = false;
|
||||
highliter = new RsSyntaxHighlighter(this);
|
||||
}
|
||||
|
||||
bool MimeTextEdit::canInsertFromMimeData(const QMimeData* source) const
|
||||
@ -231,6 +232,8 @@ void MimeTextEdit::contextMenuEvent(QContextMenuEvent *e)
|
||||
|
||||
/* Add actions for pasting links */
|
||||
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();
|
||||
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()));
|
||||
@ -268,3 +271,8 @@ void MimeTextEdit::pastePlainText()
|
||||
{
|
||||
insertPlainText(QApplication::clipboard()->text());
|
||||
}
|
||||
|
||||
void MimeTextEdit::spoiler()
|
||||
{
|
||||
RsHtml::insertSpoilerText(this->textCursor());
|
||||
}
|
||||
|
@ -24,11 +24,14 @@
|
||||
|
||||
#include <QCompleter>
|
||||
#include "RSTextEdit.h"
|
||||
#include "util/RsSyntaxHighlighter.h"
|
||||
|
||||
class MimeTextEdit : public RSTextEdit
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
Q_PROPERTY(QColor textColorQuote READ textColorQuote WRITE setTextColorQuote)
|
||||
|
||||
public:
|
||||
MimeTextEdit(QWidget *parent = 0);
|
||||
|
||||
@ -44,6 +47,11 @@ public:
|
||||
// Add QAction to context menu (action won't be deleted)
|
||||
void addContextMenuAction(QAction *action);
|
||||
|
||||
QColor textColorQuote() const { return highliter->textColorQuote();}
|
||||
|
||||
public slots:
|
||||
void setTextColorQuote(QColor textColorQuote) { highliter->setTextColorQuote(textColorQuote);}
|
||||
|
||||
signals:
|
||||
void calculateContextMenuActions();
|
||||
|
||||
@ -59,6 +67,8 @@ private slots:
|
||||
void pasteLink();
|
||||
void pasteOwnCertificateLink();
|
||||
void pastePlainText();
|
||||
void spoiler();
|
||||
|
||||
private:
|
||||
QString textUnderCursor() const;
|
||||
|
||||
@ -69,6 +79,7 @@ private:
|
||||
bool mForceCompleterShowNextKeyEvent;
|
||||
QString mCompleterStartString;
|
||||
QList<QAction*> mContextMenuActions;
|
||||
RsSyntaxHighlighter *highliter;
|
||||
};
|
||||
|
||||
#endif // MIMETEXTEDIT_H
|
||||
|
@ -14,6 +14,8 @@ RSTextBrowser::RSTextBrowser(QWidget *parent) :
|
||||
mImageBlockWidget = NULL;
|
||||
mLinkClickActive = true;
|
||||
|
||||
highliter = new RsSyntaxHighlighter(this);
|
||||
|
||||
connect(this, SIGNAL(anchorClicked(QUrl)), this, SLOT(linkClicked(QUrl)));
|
||||
}
|
||||
|
||||
|
@ -2,6 +2,7 @@
|
||||
#define RSTEXTBROWSER_H
|
||||
|
||||
#include <QTextBrowser>
|
||||
#include "util/RsSyntaxHighlighter.h"
|
||||
|
||||
class RSImageBlockWidget;
|
||||
|
||||
@ -9,6 +10,8 @@ class RSTextBrowser : public QTextBrowser
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
Q_PROPERTY(QColor textColorQuote READ textColorQuote WRITE setTextColorQuote)
|
||||
|
||||
public:
|
||||
explicit RSTextBrowser(QWidget *parent = 0);
|
||||
|
||||
@ -20,8 +23,11 @@ public:
|
||||
|
||||
virtual QVariant loadResource(int type, const QUrl &name);
|
||||
|
||||
QColor textColorQuote() const { return highliter->textColorQuote();}
|
||||
|
||||
public slots:
|
||||
void showImages();
|
||||
void setTextColorQuote(QColor textColorQuote) { highliter->setTextColorQuote(textColorQuote);}
|
||||
|
||||
private slots:
|
||||
void linkClicked(const QUrl &url);
|
||||
@ -35,6 +41,7 @@ private:
|
||||
bool mShowImages;
|
||||
RSImageBlockWidget *mImageBlockWidget;
|
||||
bool mLinkClickActive;
|
||||
RsSyntaxHighlighter *highliter;
|
||||
};
|
||||
|
||||
#endif // RSTEXTBROWSER_H
|
||||
|
@ -22,6 +22,7 @@
|
||||
#include <QDateTime>
|
||||
#include <QMessageBox>
|
||||
#include <QKeyEvent>
|
||||
#include <QScrollBar>
|
||||
|
||||
#include "GxsForumThreadWidget.h"
|
||||
#include "ui_GxsForumThreadWidget.h"
|
||||
@ -39,6 +40,7 @@
|
||||
#include "util/DateTime.h"
|
||||
#include "gui/common/UIStateHelper.h"
|
||||
#include "util/QtVersion.h"
|
||||
#include "util/imageutil.h"
|
||||
|
||||
#include <retroshare/rsgxsforums.h>
|
||||
#include <retroshare/rsgrouter.h>
|
||||
@ -135,6 +137,7 @@ GxsForumThreadWidget::GxsForumThreadWidget(const RsGxsGroupId &forumId, QWidget
|
||||
mThreadCompareRole->setRole(COLUMN_THREAD_DATE, ROLE_THREAD_SORT);
|
||||
|
||||
connect(ui->threadTreeWidget, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(threadListCustomPopupMenu(QPoint)));
|
||||
connect(ui->postText, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(contextMenuTextBrowser(QPoint)));
|
||||
|
||||
ui->subscribeToolButton->hide() ;
|
||||
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(filterChanged(int)), this, SLOT(filterColumnChanged(int)));
|
||||
|
||||
connect(ui->actionSave_image, SIGNAL(triggered()), this, SLOT(saveImage()));
|
||||
|
||||
/* Set own item delegate */
|
||||
RSItemDelegate *itemDelegate = new RSItemDelegate(this);
|
||||
itemDelegate->setSpacing(QSize(0, 2));
|
||||
@ -500,6 +505,26 @@ void GxsForumThreadWidget::threadListCustomPopupMenu(QPoint /*point*/)
|
||||
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)
|
||||
{
|
||||
if (obj == ui->threadTreeWidget) {
|
||||
@ -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()
|
||||
{
|
||||
if (mInProcessSettings) {
|
||||
|
@ -70,6 +70,7 @@ protected:
|
||||
private slots:
|
||||
/** Create the context popup menu and it's submenus */
|
||||
void threadListCustomPopupMenu(QPoint point);
|
||||
void contextMenuTextBrowser(QPoint point);
|
||||
|
||||
void changedThread();
|
||||
void clickedThread (QTreeWidgetItem *item, int column);
|
||||
@ -80,7 +81,7 @@ private slots:
|
||||
void replyMessageData(const RsGxsForumMsg &msg);
|
||||
void replyForumMessageData(const RsGxsForumMsg &msg);
|
||||
|
||||
|
||||
void saveImage();
|
||||
|
||||
|
||||
//void print();
|
||||
|
@ -453,10 +453,22 @@
|
||||
<verstretch>10</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="contextMenuPolicy">
|
||||
<enum>Qt::CustomContextMenu</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
</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>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
|
@ -27,8 +27,8 @@
|
||||
<table class='bubbleFooter' width='100%'>
|
||||
<tr>
|
||||
<td align='right'>
|
||||
<span width='130' align='right' class='time'>%time% - </span>
|
||||
<span class='name'>%name%</span>
|
||||
<span width='130' align='right' class='time'>%time%%nome% - %/nome%</span>
|
||||
<span class='name'>%nome%%name%%/nome%</span>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
@ -28,7 +28,7 @@
|
||||
<table class='bubbleFooter' width='100%'>
|
||||
<tr>
|
||||
<td align='left'>
|
||||
<span class='name'> %name% - </span>
|
||||
<span class='name'> %nome%%name% - %/nome%</span>
|
||||
<span width='130' align='right' class='time'>%time%</span>
|
||||
</td>
|
||||
</tr>
|
||||
|
@ -28,8 +28,8 @@
|
||||
<table class='bubbleFooter' width='100%'>
|
||||
<tr>
|
||||
<td align='right'>
|
||||
<span width='130' align='right' class='time'>%time% - </span>
|
||||
<span class='name'>%name%</span>
|
||||
<span width='130' align='right' class='time'>%time%%nome% - %/nome%</span>
|
||||
<span class='name'>%nome%%name%%/nome%</span>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
@ -28,7 +28,7 @@
|
||||
<table class='bubbleFooter' width='100%'>
|
||||
<tr>
|
||||
<td align='left'>
|
||||
<span class='name'> %name% - </span>
|
||||
<span class='name'> %nome%%name% - %/nome%</span>
|
||||
<span width='130' align='right' class='time'>%time%</span>
|
||||
</td>
|
||||
</tr>
|
||||
|
@ -28,7 +28,7 @@
|
||||
<table class='bubbleFooter' width='100%'>
|
||||
<tr>
|
||||
<td align='left'>
|
||||
<span class='name'> %name% - </span>
|
||||
<span class='name'> %nome%%name% - %/nome%</span>
|
||||
<span width='130' align='right' class='time'>%time%</span>
|
||||
</td>
|
||||
</tr>
|
||||
|
@ -3,5 +3,5 @@
|
||||
</style>
|
||||
|
||||
<span class='hincomingTime'>%date% %time%</span>
|
||||
<span class='name hincomingName'>%name%:</span>
|
||||
<span class='name hincomingName'>%nome%%name%:%/nome%</span>
|
||||
%message%
|
@ -3,5 +3,5 @@
|
||||
</style>
|
||||
|
||||
<span class='houtgoingTime'>%date% %time%</span>
|
||||
<span class='name houtgoingName'>%name%:</span>
|
||||
<span class='name houtgoingName'>%nome%%name%:%/nome%</span>
|
||||
%message%
|
@ -3,5 +3,5 @@
|
||||
</style>
|
||||
|
||||
<span class='incomingTime'>%time%</span>
|
||||
<span class='name incomingName'>%name%:</span>
|
||||
<span class='name incomingName'>%nome%%name%:%/nome%</span>
|
||||
%message%
|
@ -25,3 +25,7 @@
|
||||
.systemTime {
|
||||
color:#808080;
|
||||
}
|
||||
|
||||
.meName {
|
||||
font-weight: bold;
|
||||
}
|
@ -3,5 +3,5 @@
|
||||
</style>
|
||||
|
||||
<span class='ooutgoingTime'>%time%</span>
|
||||
<span class='name ooutgoingName'>%name%:</span>
|
||||
<span class='name ooutgoingName'>%nome%%name%:%/nome%</span>
|
||||
%message%
|
@ -3,5 +3,5 @@
|
||||
</style>
|
||||
|
||||
<span class='outgoingTime'>%time%</span>
|
||||
<span class='name outgoingName'>%name%:</span>
|
||||
<span class='name outgoingName'>%nome%%name%:%/nome%</span>
|
||||
%message%
|
@ -3,5 +3,5 @@
|
||||
</style>
|
||||
|
||||
<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%
|
||||
|
@ -3,5 +3,5 @@
|
||||
</style>
|
||||
|
||||
<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%
|
||||
|
@ -3,5 +3,5 @@
|
||||
</style>
|
||||
|
||||
<span class='incomingTime'>%time%</span>
|
||||
<span class='name incomingName'>%name%</span><br>
|
||||
<span class='name incomingName'>%nome%%name%%/nome%%me%*%/me%</span><br>
|
||||
%message%
|
||||
|
@ -47,3 +47,7 @@
|
||||
|
||||
.systemName {
|
||||
}
|
||||
|
||||
.meName {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
@ -3,5 +3,5 @@
|
||||
</style>
|
||||
|
||||
<span class='ooutgoingTime'>%time%</span>
|
||||
<span class='name ooutgoingName'>%name%</span><br>
|
||||
<span class='name ooutgoingName'>%nome%%name%%/nome%%me%*%/me%</span><br>
|
||||
%message%
|
||||
|
@ -3,5 +3,5 @@
|
||||
</style>
|
||||
|
||||
<span class='outgoingTime'>%time%</span>
|
||||
<span class='name outgoingName'>%name%</span><br>
|
||||
<span class='name outgoingName'>%nome%%name%%/nome%%me%*%/me%</span><br>
|
||||
%message%
|
||||
|
@ -188,6 +188,11 @@ NetworkDialog
|
||||
qproperty-backgroundColorDenied: lightGray;
|
||||
}
|
||||
|
||||
RSTextBrowser, MimeTextEdit
|
||||
{
|
||||
qproperty-textColorQuote: rgb(120, 153, 34);
|
||||
}
|
||||
|
||||
QLabel#headerTextLabel
|
||||
{
|
||||
qproperty-fontSizeFactor: 225;
|
||||
|
@ -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_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_OUTGOING, tr("UserName"), timestmp, tr("/me is sending a message with /me")));
|
||||
}
|
||||
|
||||
void ChatPage::fillPreview(QListWidget *listWidget, QComboBox *comboBox, QTextBrowser *textBrowser)
|
||||
|
@ -523,7 +523,9 @@ HEADERS += rshare.h \
|
||||
gui/groups/CreateGroup.h \
|
||||
gui/GetStartedDialog.h \
|
||||
gui/settings/WebuiPage.h \
|
||||
gui/statistics/BWGraph.h
|
||||
gui/statistics/BWGraph.h \
|
||||
util/RsSyntaxHighlighter.h \
|
||||
util/imageutil.h
|
||||
|
||||
# gui/ForumsDialog.h \
|
||||
# gui/forums/ForumDetails.h \
|
||||
@ -871,7 +873,9 @@ SOURCES += main.cpp \
|
||||
gui/statistics/StatisticsWindow.cpp \
|
||||
gui/statistics/BwCtrlWindow.cpp \
|
||||
gui/statistics/RttStatistics.cpp \
|
||||
gui/statistics/BWGraph.cpp
|
||||
gui/statistics/BWGraph.cpp \
|
||||
util/RsSyntaxHighlighter.cpp \
|
||||
util/imageutil.cpp
|
||||
|
||||
# gui/ForumsDialog.cpp \
|
||||
# gui/forums/ForumDetails.cpp \
|
||||
|
@ -26,6 +26,7 @@
|
||||
#include <QMessageBox>
|
||||
#include <QTextDocumentFragment>
|
||||
#include <qmath.h>
|
||||
#include <QUrl>
|
||||
|
||||
#include "HandleRichText.h"
|
||||
#include "gui/RetroShareLink.h"
|
||||
@ -569,7 +570,6 @@ static void optimizeHtml(QDomDocument& doc
|
||||
, QHash<QString, QStringList*> &stylesList
|
||||
, QHash<QString, QString> &knownStyle)
|
||||
{
|
||||
bool bFirstP=true;
|
||||
if (doc.documentElement().namedItem("style").toElement().attributeNode("RSOptimized").isAttr()) {
|
||||
//Already optimized only get StyleList
|
||||
QDomElement styleElem = doc.documentElement().namedItem("style").toElement();
|
||||
@ -611,9 +611,92 @@ static void optimizeHtml(QDomDocument& doc
|
||||
QDomNodeList children = currentElement.childNodes();
|
||||
for (uint index = 0; index < children.length(); ) {
|
||||
QDomNode node = children.item(index);
|
||||
|
||||
// Compress style attribute
|
||||
if (node.isElement()) {
|
||||
QDomElement element = node.toElement();
|
||||
styleNode = node.attributes().namedItem("style");
|
||||
|
||||
// not <p>
|
||||
if (addBR && element.tagName().toLower() != "p") {
|
||||
// add <br> after a removed <p> but not before a <p>
|
||||
QDomElement elementBr = doc.createElement("br");
|
||||
currentElement.insertBefore(elementBr, element);
|
||||
addBR = false;
|
||||
++index;
|
||||
}
|
||||
|
||||
// <body>
|
||||
if (element.tagName().toLower() == "body") {
|
||||
if (element.attributes().length() == 0) {
|
||||
// remove <body> without attributes
|
||||
removeElement(currentElement, element);
|
||||
// no ++index;
|
||||
continue;
|
||||
}
|
||||
// change <body> to <span>
|
||||
element.setTagName("span");
|
||||
}
|
||||
|
||||
// <head>
|
||||
if (element.tagName().toLower() == "head") {
|
||||
// remove <head>
|
||||
currentElement.removeChild(node);
|
||||
// no ++index;
|
||||
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
|
||||
optimizeHtml(doc, element, stylesList, knownStyle);
|
||||
|
||||
// <p>
|
||||
if (element.tagName().toLower() == "p") {
|
||||
// <p style="...">
|
||||
if (styleNode.isAttr()) {
|
||||
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;"
|
||||
|| style.startsWith("-qt-paragraph-type:empty;margin:0px 0px 0px 0px;-qt-block-indent:0;text-indent:0px;")) {
|
||||
|
||||
if (addBR) {
|
||||
// add <br> after a removed <p> but not before a removed <p>
|
||||
QDomElement elementBr = doc.createElement("br");
|
||||
currentElement.insertBefore(elementBr, element);
|
||||
++index;
|
||||
}
|
||||
// remove Qt standard <p> or empty <p>
|
||||
index += element.childNodes().length();
|
||||
removeElement(currentElement, element);
|
||||
// do not add extra <br> after empty paragraph, the paragraph already contains one
|
||||
addBR = ! style.startsWith("-qt-paragraph-type:empty");
|
||||
continue;
|
||||
}
|
||||
|
||||
}
|
||||
addBR = false;
|
||||
}
|
||||
// Compress style attribute
|
||||
if (styleNode.isAttr()) {
|
||||
QDomAttr styleAttr = styleNode.toAttr();
|
||||
QString style = styleAttr.value().simplified().trimmed();
|
||||
@ -653,72 +736,6 @@ static void optimizeHtml(QDomDocument& doc
|
||||
node.attributes().setNamedItem(classNode);
|
||||
}
|
||||
}
|
||||
|
||||
if (node.isElement()) {
|
||||
QDomElement element = node.toElement();
|
||||
|
||||
// not <p>
|
||||
if (addBR && element.tagName().toLower() != "p") {
|
||||
// add <br> after a removed <p> but not before a <p>
|
||||
QDomElement elementBr = doc.createElement("br");
|
||||
currentElement.insertBefore(elementBr, element);
|
||||
addBR = false;
|
||||
++index;
|
||||
}
|
||||
|
||||
// <body>
|
||||
if (element.tagName().toLower() == "body") {
|
||||
if (element.attributes().length() == 0) {
|
||||
// remove <body> without attributes
|
||||
removeElement(currentElement, element);
|
||||
// no ++index;
|
||||
continue;
|
||||
}
|
||||
// change <body> to <span>
|
||||
element.setTagName("span");
|
||||
}
|
||||
|
||||
// <head>
|
||||
if (element.tagName().toLower() == "head") {
|
||||
// remove <head>
|
||||
currentElement.removeChild(node);
|
||||
// no ++index;
|
||||
continue;
|
||||
}
|
||||
|
||||
// iterate children
|
||||
optimizeHtml(doc, element, stylesList, knownStyle);
|
||||
|
||||
// <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="...">
|
||||
if (element.attributes().size() == 1 && styleNode.isAttr()) {
|
||||
QString style = styleNode.toAttr().value().simplified();
|
||||
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;")) {
|
||||
|
||||
if (addBR) {
|
||||
// add <br> after a removed <p> but not before a removed <p>
|
||||
QDomElement elementBr = doc.createElement("br");
|
||||
currentElement.insertBefore(elementBr, element);
|
||||
++index;
|
||||
}
|
||||
// remove Qt standard <p> or empty <p>
|
||||
index += element.childNodes().length();
|
||||
removeElement(currentElement, element);
|
||||
// do not add extra <br> after empty paragraph, the paragraph already contains one
|
||||
addBR = ! style.startsWith("-qt-paragraph-type:empty");
|
||||
continue;
|
||||
}
|
||||
|
||||
}
|
||||
addBR = false;
|
||||
}
|
||||
}
|
||||
++index;
|
||||
}
|
||||
@ -1010,3 +1027,20 @@ QString RsHtml::makeQuotedText(RSTextBrowser *browser)
|
||||
text = sl.join("\n>");
|
||||
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("""));
|
||||
encoded = encoded.replace(QChar('\''), QString("'"));
|
||||
encoded = encoded.replace(QChar('<'), QString("<"));
|
||||
encoded = encoded.replace(QChar('>'), QString(">"));
|
||||
encoded = encoded.replace(QChar('&'), QString("&"));
|
||||
|
||||
QString html = QString("<a href=\"hidden:%1\" title=\"%1\">%2</a>").arg(encoded, publictext);
|
||||
cursor.insertHtml(html);
|
||||
}
|
||||
|
@ -53,6 +53,7 @@ class QDomDocument;
|
||||
class QDomElement;
|
||||
class EmbedInHtml;
|
||||
class RetroShareLink;
|
||||
class QTextCursor;
|
||||
|
||||
class RsHtml
|
||||
{
|
||||
@ -75,6 +76,7 @@ public:
|
||||
static QString plainText(const std::string &text);
|
||||
|
||||
static QString makeQuotedText(RSTextBrowser* browser);
|
||||
static void insertSpoilerText(QTextCursor cursor);
|
||||
|
||||
protected:
|
||||
void embedHtml(QTextDocument *textDocument, QDomDocument &doc, QDomElement ¤tElement, EmbedInHtml& embedInfos, ulong flag);
|
||||
|
45
retroshare-gui/src/util/RsSyntaxHighlighter.cpp
Normal file
45
retroshare-gui/src/util/RsSyntaxHighlighter.cpp
Normal 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;
|
||||
*/
|
31
retroshare-gui/src/util/RsSyntaxHighlighter.h
Normal file
31
retroshare-gui/src/util/RsSyntaxHighlighter.h
Normal 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
|
51
retroshare-gui/src/util/imageutil.cpp
Normal file
51
retroshare-gui/src/util/imageutil.cpp
Normal 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;
|
||||
}
|
17
retroshare-gui/src/util/imageutil.h
Normal file
17
retroshare-gui/src/util/imageutil.h
Normal 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
|
Loading…
Reference in New Issue
Block a user