Merge branch 'RetroShare:master' into browse_image

This commit is contained in:
Tushar Garg 2023-05-05 16:08:01 +05:30 committed by GitHub
commit 9c950affcf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
50 changed files with 984 additions and 589 deletions

View File

@ -1120,8 +1120,7 @@ void TransfersDialog::handleEvent_main_thread(std::shared_ptr<const RsEvent> eve
break;
SoundManager::play(SOUND_DOWNLOAD_COMPLETE);
if (Settings->getNotifyFlags() & RS_POPUP_DOWNLOAD)
NotifyQt::getInstance()->addToaster(RS_POPUP_DOWNLOAD, fe->mHash.toStdString(), nfo.fname.c_str(),"");
NotifyQt::getInstance()->addToaster(RS_POPUP_DOWNLOAD, fe->mHash.toStdString(), nfo.fname.c_str(),"");
}
[[fallthrough]];

View File

@ -301,8 +301,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>634</width>
<height>538</height>
<width>456</width>
<height>731</height>
</rect>
</property>
<layout class="QVBoxLayout" name="scrollAreaWidgetContentsVLayout">
@ -338,21 +338,14 @@
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>518</width>
<height>17</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QFrame" name="info_Frame_Invite">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="palette">
<palette>
<active>
@ -490,6 +483,9 @@
<property name="text">
<string notr="true">Invite messages stay into your Outbox until an acknowledgement of receipt has been received.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
@ -895,9 +891,12 @@ border-image: url(:/images/closepressed.png)
</item>
<item row="11" column="1">
<widget class="QCheckBox" name="autoBanIdentities_CB">
<property name="text">
<property name="toolTip">
<string>Auto-Ban all identities signed by the same node</string>
</property>
<property name="text">
<string>Auto-Ban profile</string>
</property>
</widget>
</item>
<item row="4" column="1">

View File

@ -441,8 +441,7 @@ void NewsFeed::handleConnectionEvent(std::shared_ptr<const RsEvent> event)
{
case RsConnectionEventCode::PEER_CONNECTED:
addFeedItemIfUnique(new PeerItem(this, NEWSFEED_PEERLIST, e.mSslId, PEER_TYPE_CONNECT, false), true);
if (Settings->getNotifyFlags() & RS_POPUP_CONNECT)
NotifyQt::getInstance()->addToaster(RS_POPUP_CONNECT, e.mSslId.toStdString().c_str(), "", "");
NotifyQt::getInstance()->addToaster(RS_POPUP_CONNECT, e.mSslId.toStdString().c_str(), "", "");
break;
case RsConnectionEventCode::PEER_DISCONNECTED: // not handled yet
break;
@ -508,8 +507,7 @@ void NewsFeed::handleSecurityEvent(std::shared_ptr<const RsEvent> event)
if (Settings->getMessageFlags() & RS_MESSAGE_CONNECT_ATTEMPT)
MessageComposer::addConnectAttemptMsg(e.mPgpId, e.mSslId, QString::fromStdString(det.name + "(" + det.location + ")"));
if (Settings->getNotifyFlags() & RS_POPUP_CONNECT_ATTEMPT)
NotifyQt::getInstance()->addToaster(RS_POPUP_CONNECT_ATTEMPT, e.mPgpId.toStdString().c_str(), det.location, e.mSslId.toStdString().c_str());
NotifyQt::getInstance()->addToaster(RS_POPUP_CONNECT_ATTEMPT, e.mPgpId.toStdString().c_str(), det.location, e.mSslId.toStdString().c_str());
}
void NewsFeed::testFeeds(uint /*notifyFlags*/)

View File

@ -43,6 +43,7 @@
#define LINK_IMAGE ":/images/thumb-link.png"
// #ifdef DEBUG_BOARDPOSTDISPLAYWIDGET 1
/** Constructor */
@ -58,6 +59,7 @@ BoardPostDisplayWidgetBase::BoardPostDisplayWidgetBase(const RsPostedPost& post,
{
}
void BoardPostDisplayWidgetBase::setCommentsSize(int comNb)
{
QString sComButText = tr("Comment");
@ -168,6 +170,7 @@ void BoardPostDisplayWidgetBase::baseSetup()
readButton()->setChecked(false);
#ifdef TO_REMOVE
QMenu *menu = new QMenu();
menu->addAction(CopyLinkAction);
menu->addSeparator();
@ -175,6 +178,7 @@ void BoardPostDisplayWidgetBase::baseSetup()
shareButton()->setPopupMode(QToolButton::InstantPopup);
connect(menu,SIGNAL(aboutToShow()),this,SLOT(handleShareButtonClicked()));
#endif
RsReputationLevel overall_reputation = rsReputations->overallReputationLevel(mPost.mMeta.mAuthorId);
bool redacted = (overall_reputation == RsReputationLevel::LOCALLY_NEGATIVE);
@ -182,7 +186,9 @@ void BoardPostDisplayWidgetBase::baseSetup()
if(redacted)
{
commentButton()->setDisabled(true);
#ifdef TO_REMOVE
shareButton()->setDisabled(true);
#endif
voteUpButton()->setDisabled(true);
voteDownButton()->setDisabled(true);
fromLabel()->setId(mPost.mMeta.mAuthorId);
@ -275,6 +281,7 @@ void BoardPostDisplayWidgetBase::baseSetup()
emit sizeChanged(this);
#endif
}
#ifdef TO_REMOVE
void BoardPostDisplayWidgetBase::handleShareButtonClicked()
{
emit shareButtonClicked();
@ -284,6 +291,8 @@ void BoardPostDisplayWidgetBase::handleCopyLinkClicked()
{
emit copylinkClicked();
}
#endif
//===================================================================================================================================
//== class BoardPostDisplayWidget ==
//===================================================================================================================================
@ -292,6 +301,7 @@ BoardPostDisplayWidget_compact::BoardPostDisplayWidget_compact(const RsPostedPos
: BoardPostDisplayWidgetBase(post,display_flags,parent), ui(new Ui::BoardPostDisplayWidget_compact())
{
ui->setupUi(this);
ui->shareButton->hide();
BoardPostDisplayWidget_compact::setup();
}
@ -400,10 +410,10 @@ QLabel *BoardPostDisplayWidget_compact::newLabel() { return ui->ne
QToolButton *BoardPostDisplayWidget_compact::readButton() { return ui->readButton; }
GxsIdLabel *BoardPostDisplayWidget_compact::fromLabel() { return ui->fromLabel; }
QLabel *BoardPostDisplayWidget_compact::dateLabel() { return ui->dateLabel; }
QLabel *BoardPostDisplayWidget_compact::titleLabel() { return ui->titleLabel; }
ElidedLabel *BoardPostDisplayWidget_compact::titleLabel() { return ui->titleLabel; }
QLabel *BoardPostDisplayWidget_compact::scoreLabel() { return ui->scoreLabel; }
QLabel *BoardPostDisplayWidget_compact::notes() { return ui->notes; }
QToolButton *BoardPostDisplayWidget_compact::shareButton() { return ui->shareButton; }
//QToolButton *BoardPostDisplayWidget_compact::shareButton() { return ui->shareButton; }
QLabel *BoardPostDisplayWidget_compact::pictureLabel() { return ui->pictureLabel; }
QFrame *BoardPostDisplayWidget_compact::feedFrame() { return ui->feedFrame; }
@ -415,7 +425,8 @@ BoardPostDisplayWidget_card::BoardPostDisplayWidget_card(const RsPostedPost& pos
: BoardPostDisplayWidgetBase(post,display_flags,parent), ui(new Ui::BoardPostDisplayWidget_card())
{
ui->setupUi(this);
BoardPostDisplayWidget_card::setup();
ui->shareButton->hide();
BoardPostDisplayWidget_card::setup();
}
BoardPostDisplayWidget_card::~BoardPostDisplayWidget_card()
@ -469,10 +480,10 @@ QLabel *BoardPostDisplayWidget_card::newLabel() { return ui->newLa
QToolButton *BoardPostDisplayWidget_card::readButton() { return ui->readButton; }
GxsIdLabel *BoardPostDisplayWidget_card::fromLabel() { return ui->fromLabel; }
QLabel *BoardPostDisplayWidget_card::dateLabel() { return ui->dateLabel; }
QLabel *BoardPostDisplayWidget_card::titleLabel() { return ui->titleLabel; }
ElidedLabel *BoardPostDisplayWidget_card::titleLabel() { return ui->titleLabel; }
QLabel *BoardPostDisplayWidget_card::scoreLabel() { return ui->scoreLabel; }
QLabel *BoardPostDisplayWidget_card::notes() { return ui->notes; }
QToolButton *BoardPostDisplayWidget_card::shareButton() { return ui->shareButton; }
//QToolButton *BoardPostDisplayWidget_card::shareButton() { return ui->shareButton; }
QLabel *BoardPostDisplayWidget_card::pictureLabel() { return ui->pictureLabel; }
QFrame *BoardPostDisplayWidget_card::feedFrame() { return ui->feedFrame; }

View File

@ -37,6 +37,7 @@ class QToolButton;
class QTextEdit;
class ClickableLabel;
class GxsIdLabel;
class ElidedLabel;
struct RsPostedPost;
@ -72,14 +73,14 @@ protected:
virtual QToolButton *commentButton() =0;
virtual QToolButton *voteDownButton() =0;
virtual QLabel *newLabel() =0;
virtual QLabel *titleLabel()=0;
virtual ElidedLabel *titleLabel()=0;
virtual GxsIdLabel *fromLabel()=0;
virtual QLabel *dateLabel()=0;
virtual QLabel *scoreLabel() =0;
virtual QLabel *notes() =0;
virtual QLabel *pictureLabel()=0;
virtual QToolButton *readButton() =0;
virtual QToolButton *shareButton() =0;
// virtual QToolButton *shareButton() =0;
virtual QFrame *feedFrame() =0;
protected slots:
@ -89,9 +90,10 @@ protected slots:
void makeUpVote() ;
void makeDownVote() ;
void setCommentsSize(int comNb) ;
#ifdef TO_REMOVE
void handleShareButtonClicked() ;
void handleCopyLinkClicked() ;
#endif
signals:
void changeReadStatusRequested(const RsGxsMessageId&,bool);
@ -99,8 +101,8 @@ signals:
void expand(RsGxsMessageId,bool);
void commentsRequested(const RsGxsMessageId&,bool);
void thumbnailOpenned();
void shareButtonClicked();
void copylinkClicked();
// void shareButtonClicked();
// void copylinkClicked();
protected:
RsPostedPost mPost;
@ -123,12 +125,14 @@ public:
QLabel *newLabel() override;
GxsIdLabel *fromLabel() override;
QLabel *dateLabel() override;
QLabel *titleLabel() override;
ElidedLabel *titleLabel() override;
QLabel *scoreLabel() override;
QLabel *notes() override;
QLabel *pictureLabel() override;
QToolButton *readButton() override;
#ifdef TO_REMOVE
QToolButton *shareButton() override;
#endif
QFrame *feedFrame() override;
public slots:
@ -162,11 +166,11 @@ public:
QLabel *newLabel() override;
GxsIdLabel *fromLabel() override;
QLabel *dateLabel() override;
QLabel *titleLabel() override;
ElidedLabel *titleLabel() override;
QLabel *scoreLabel() override;
QLabel *notes() override;
QToolButton *readButton() override;
QToolButton *shareButton() override;
// QToolButton *shareButton() override;
QLabel *pictureLabel() override;
QFrame *feedFrame() override;

View File

@ -305,7 +305,7 @@
</layout>
</item>
<item>
<widget class="QLabel" name="titleLabel">
<widget class="ElidedLabel" name="titleLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
@ -464,6 +464,12 @@
</layout>
</widget>
<customwidgets>
<customwidget>
<class>ElidedLabel</class>
<extends>QLabel</extends>
<header>gui/common/ElidedLabel.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>GxsIdLabel</class>
<extends>QLabel</extends>

View File

@ -211,7 +211,7 @@
<number>3</number>
</property>
<item>
<widget class="QLabel" name="titleLabel">
<widget class="ElidedLabel" name="titleLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
@ -515,6 +515,12 @@
</layout>
</widget>
<customwidgets>
<customwidget>
<class>ElidedLabel</class>
<extends>QLabel</extends>
<header>gui/common/ElidedLabel.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>GxsIdLabel</class>
<extends>QLabel</extends>

View File

@ -19,6 +19,7 @@
*******************************************************************************/
#include <QBuffer>
#include <QClipboard>
#include <QMessageBox>
#include <QByteArray>
#include <QStringList>
@ -59,6 +60,7 @@ PostedCreatePostDialog::PostedCreatePostDialog(RsPosted *posted, const RsGxsGrou
connect(ui->postButton, SIGNAL(clicked()), this, SLOT(createPost()));
connect(ui->addPicButton, SIGNAL(clicked() ), this , SLOT(addPicture()));
connect(ui->pasteButton, SIGNAL(clicked() ), this , SLOT(pastePicture()));
connect(ui->RichTextEditWidget, SIGNAL(textSizeOk(bool)),ui->postButton, SLOT(setEnabled(bool)));
ui->headerFrame->setHeaderImage(FilesDefs::getPixmapFromQtResourcePath(":/icons/png/postedlinks.png"));
@ -256,6 +258,35 @@ void PostedCreatePostDialog::addPicture()
}
void PostedCreatePostDialog::pastePicture()
{
imagefilename = "";
imagebytes.clear();
QPixmap empty;
ui->imageLabel->setPixmap(empty);
// paste picture from clipboard
const QClipboard *clipboard = QApplication::clipboard();
const QMimeData *mimeData = clipboard->mimeData();
QImage image;
if (mimeData->hasImage()) {
image = (qvariant_cast<QImage>(mimeData->imageData()));
QImage opt;
if(ImageUtil::optimizeSizeBytes(imagebytes, image, opt,"JPG", 640*480, MAXMESSAGESIZE - 2000)) { //Leave space for other stuff
ui->imageLabel->setPixmap(QPixmap::fromImage(opt));
ui->stackedWidgetPicture->setCurrentIndex(IMG_PICTURE);
ui->removeButton->show();
}
} else {
QMessageBox::information(nullptr,tr("No clipboard image found."),tr("There is no image data in the clipboard to paste"));
imagefilename = "";
imagebytes.clear();
return;
}
}
int PostedCreatePostDialog::viewMode()
{
if (ui->viewPostButton->isChecked()) {

View File

@ -24,7 +24,6 @@
#include <QDialog>
#include <gui/common/HashBox.h>
#include "retroshare/rsposted.h"
#include "util/RichTextEdit.h"
namespace Ui {
class PostedCreatePostDialog;
@ -50,6 +49,7 @@ private:
private slots:
void createPost();
void addPicture();
void pastePicture();
void on_removeButton_clicked();
void fileHashingFinished(QList<HashedFile> hashedFiles);
void reject() override; //QDialog

View File

@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>575</width>
<height>518</height>
<height>468</height>
</rect>
</property>
<property name="windowTitle">
@ -152,8 +152,8 @@
<property name="topMargin">
<number>9</number>
</property>
<item row="1" column="0">
<spacer name="addPicLeft_HSpacer">
<item row="1" column="2">
<spacer name="addPicRight_HSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
@ -182,15 +182,8 @@
</property>
</widget>
</item>
<item row="2" column="0" colspan="3">
<widget class="QLabel" name="sizeWarningLabel">
<property name="text">
<string>Post size is limited to 32 KB, pictures will be downscaled.</string>
</property>
</widget>
</item>
<item row="1" column="2">
<spacer name="addPicRight_HSpacer">
<item row="1" column="0">
<spacer name="addPicLeft_HSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
@ -203,6 +196,13 @@
</spacer>
</item>
<item row="3" column="0" colspan="3">
<widget class="QLabel" name="sizeWarningLabel">
<property name="text">
<string>Post size is limited to 32 KB, pictures will be downscaled.</string>
</property>
</widget>
</item>
<item row="4" column="0" colspan="3">
<spacer name="addPic_VSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
@ -215,6 +215,26 @@
</property>
</spacer>
</item>
<item row="2" column="1">
<widget class="QPushButton" name="pasteButton">
<property name="toolTip">
<string>Paste image from clipboard</string>
</property>
<property name="text">
<string>Paste Picture</string>
</property>
<property name="icon">
<iconset resource="../icons.qrc">
<normaloff>:/icons/svg/paste_image.svg</normaloff>:/icons/svg/paste_image.svg</iconset>
</property>
<property name="iconSize">
<size>
<width>32</width>
<height>32</height>
</size>
</property>
</widget>
</item>
</layout>
</item>
<item row="1" column="0">
@ -225,7 +245,7 @@
<property name="sizeHint" stdset="0">
<size>
<width>267</width>
<height>138</height>
<height>20</height>
</size>
</property>
</spacer>

View File

@ -253,7 +253,7 @@
<number>6</number>
</property>
<item>
<widget class="QLabel" name="titleLabel">
<widget class="ElidedLabel" name="titleLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Expanding">
<horstretch>0</horstretch>

View File

@ -219,7 +219,7 @@ QWidget *PostedPostDelegate::createEditor(QWidget *parent, const QStyleOptionVie
else
w = new BoardPostDisplayWidget_card(post,displayFlags(post.mMeta.mMsgId),parent);
QObject::connect(w,SIGNAL(vote(RsGxsGrpMsgIdPair,bool)),mPostListWidget,SLOT(voteMsg(RsGxsGrpMsgIdPair,bool)));
QObject::connect(w,SIGNAL(vote(RsGxsGrpMsgIdPair,bool)),mPostListWidget,SLOT(voteMsg(RsGxsGrpMsgIdPair,bool)));
QObject::connect(w,SIGNAL(expand(RsGxsMessageId,bool)),this,SLOT(expandItem(RsGxsMessageId,bool)));
QObject::connect(w,SIGNAL(commentsRequested(RsGxsMessageId,bool)),mPostListWidget,SLOT(openComments(RsGxsMessageId)));
QObject::connect(w,SIGNAL(changeReadStatusRequested(RsGxsMessageId,bool)),mPostListWidget,SLOT(changeReadStatus(RsGxsMessageId,bool)));
@ -230,7 +230,6 @@ QWidget *PostedPostDelegate::createEditor(QWidget *parent, const QStyleOptionVie
QObject::connect(w,SIGNAL(expand(RsGxsMessageId,bool)),this,SLOT(markCurrentPostAsRead()));
QObject::connect(w,SIGNAL(commentsRequested(RsGxsMessageId,bool)),mPostListWidget,SLOT(markCurrentPostAsRead()));
QObject::connect(w,SIGNAL(shareButtonClicked()),mPostListWidget,SLOT(markCurrentPostAsRead()));
QObject::connect(w,SIGNAL(copylinkClicked()),mPostListWidget,SLOT(copyMessageLink()));
w->setGeometry(option.rect);
w->adjustSize();
@ -277,7 +276,6 @@ PostedListWidgetWithModel::PostedListWidgetWithModel(const RsGxsGroupId& postedI
connect(ui->nextButton,SIGNAL(clicked()),this,SLOT(nextPosts()));
connect(ui->prevButton,SIGNAL(clicked()),this,SLOT(prevPosts()));
connect(ui->postsTree,SIGNAL(customContextMenuRequested(QPoint)),this,SLOT(postContextMenu(QPoint)));
connect(ui->viewModeButton,SIGNAL(clicked()),this,SLOT(switchDisplayMode()));
connect(mPostedPostsModel,SIGNAL(boardPostsLoaded()),this,SLOT(postPostLoad()));
@ -296,7 +294,11 @@ PostedListWidgetWithModel::PostedListWidgetWithModel(const RsGxsGroupId& postedI
connect(ui->postsTree,SIGNAL(sizeChanged(QSize)),this,SLOT(handlePostsTreeSizeChange(QSize)));
/* load settings */
ui->postsTree->setContextMenuPolicy(Qt::CustomContextMenu);
//QObject::connect(this,SIGNAL(copylinkClicked()),this,SLOT(copyMessageLink()));
connect(ui->postsTree,SIGNAL(customContextMenuRequested(const QPoint&)),this,SLOT(postContextMenu(const QPoint&)));
/* load settings */
processSettings(true);
/* Initialize subscribe button */
@ -325,6 +327,32 @@ PostedListWidgetWithModel::PostedListWidgetWithModel(const RsGxsGroupId& postedI
}, mEventHandlerId, RsEventType::GXS_POSTED );
}
void PostedListWidgetWithModel::postContextMenu(const QPoint& point)
{
QMenu menu(this);
// 1 - check that we are clicking on a post
QModelIndex index = ui->postsTree->indexAt(point);
if(!index.isValid())
return;
// 2 - generate the menu for that post.
menu.addAction(FilesDefs::getIconFromQtResourcePath(IMAGE_COPYLINK), tr("Copy RetroShare Link"), this, SLOT(copyMessageLink()))->setData(index);
menu.addAction(FilesDefs::getIconFromQtResourcePath(IMAGE_AUTHOR), tr("Show author in People tab"), this, SLOT(showAuthorInPeople()))->setData(index);
#ifdef TODO
// This feature is not implemented yet in libretroshare.
if(IS_GROUP_PUBLISHER(mGroup.mMeta.mSubscribeFlags))
menu.addAction(FilesDefs::getIconFromQtResourcePath(":/images/edit_16.png"), tr("Edit"), this, SLOT(editPost()));
#endif
menu.exec(QCursor::pos());
}
void PostedListWidgetWithModel::switchDisplayMode()
{
if(mPostedPostsDelegate->getDisplayMode() == BoardPostDisplayWidget_compact::DISPLAY_MODE_CARD)
@ -403,7 +431,7 @@ void PostedListWidgetWithModel::prevPosts()
void PostedListWidgetWithModel::showAuthorInPeople()
{
QModelIndex index = ui->postsTree->selectionModel()->currentIndex();
QModelIndex index = qobject_cast<QAction*>(QObject::sender())->data().toModelIndex();
if(!index.isValid())
throw std::runtime_error("No post under mouse!");
@ -428,23 +456,6 @@ void PostedListWidgetWithModel::showAuthorInPeople()
MainWindow::showWindow(MainWindow::People);
idDialog->navigate(RsGxsId(post.mMeta.mAuthorId));
}
void PostedListWidgetWithModel::postContextMenu(const QPoint&)
{
QMenu menu(this);
menu.addAction(FilesDefs::getIconFromQtResourcePath(IMAGE_COPYLINK), tr("Copy RetroShare Link"), this, SLOT(copyMessageLink()));
menu.addAction(FilesDefs::getIconFromQtResourcePath(IMAGE_AUTHOR), tr("Show author in People tab"), this, SLOT(showAuthorInPeople()));
#ifdef TODO
// This feature is not implemented yet in libretroshare.
if(IS_GROUP_PUBLISHER(mGroup.mMeta.mSubscribeFlags))
menu.addAction(FilesDefs::getIconFromQtResourcePath(":/images/edit_16.png"), tr("Edit"), this, SLOT(editPost()));
#endif
menu.exec(QCursor::pos());
}
void PostedListWidgetWithModel::copyMessageLink()
{
try
@ -452,7 +463,7 @@ void PostedListWidgetWithModel::copyMessageLink()
if (groupId().isNull())
throw std::runtime_error("No channel currently selected!");
QModelIndex index = ui->postsTree->selectionModel()->currentIndex();
QModelIndex index = qobject_cast<QAction*>(QObject::sender())->data().toModelIndex();
if(!index.isValid())
throw std::runtime_error("No post under mouse!");
@ -528,87 +539,6 @@ void PostedListWidgetWithModel::handleEvent_main_thread(std::shared_ptr<const Rs
}
}
#ifdef TO_REMOVE
void PostedListWidgetWithModel::showPostDetails()
{
QModelIndex index = ui->postsTree->selectionModel()->currentIndex();
RsPostedPost post = index.data(Qt::UserRole).value<RsPostedPost>() ;
QTextDocument doc;
doc.setHtml(post.mMsg.c_str());
if(post.mMeta.mPublishTs == 0)
{
ui->postDetails_TE->clear();
ui->postLogo_LB->hide();
ui->postName_LB->hide();
ui->postTime_LB->hide();
mChannelPostFilesModel->clear();
return;
}
ui->postLogo_LB->show();
ui->postName_LB->show();
ui->postTime_LB->show();
if(index.row()==0 && index.column()==0)
std::cerr << "here" << std::endl;
std::cerr << "showPostDetails: setting mLastSelectedPosts[groupId()] to current post Id " << post.mMeta.mMsgId << ". Previous value: " << mLastSelectedPosts[groupId()] << std::endl;
mLastSelectedPosts[groupId()] = post.mMeta.mMsgId;
std::list<ChannelPostFileInfo> files;
for(auto& file:post.mFiles)
files.push_back(ChannelPostFileInfo(file,post.mMeta.mPublishTs));
mChannelPostFilesModel->setFiles(files);
auto all_msgs_versions(post.mOlderVersions);
all_msgs_versions.insert(post.mMeta.mMsgId);
ui->commentsDialog->commentLoad(post.mMeta.mGroupId, all_msgs_versions, post.mMeta.mMsgId);
std::cerr << "Showing details about selected index : "<< index.row() << "," << index.column() << std::endl;
ui->postDetails_TE->setText(RsHtml().formatText(NULL, QString::fromUtf8(post.mMsg.c_str()), /* RSHTML_FORMATTEXT_EMBED_SMILEYS |*/ RSHTML_FORMATTEXT_EMBED_LINKS));
QPixmap postImage;
if (post.mThumbnail.mData != NULL)
GxsIdDetails::loadPixmapFromData(post.mThumbnail.mData, post.mThumbnail.mSize, postImage,GxsIdDetails::ORIGINAL);
else
postImage = FilesDefs::getPixmapFromQtResourcePath(ChannelPostThumbnailView::CHAN_DEFAULT_IMAGE);
int W = QFontMetricsF(font()).height() * 8;
// Using fixed width so that the post will not displace the text when we browse.
ui->postLogo_LB->setPixmap(postImage);
ui->postLogo_LB->setFixedSize(W,postImage.height()/(float)postImage.width()*W);
ui->postName_LB->setText(QString::fromUtf8(post.mMeta.mMsgName.c_str()));
ui->postName_LB->setFixedWidth(W);
ui->postTime_LB->setText(QDateTime::fromMSecsSinceEpoch(post.mMeta.mPublishTs*1000).toString("MM/dd/yyyy, hh:mm"));
ui->postTime_LB->setFixedWidth(W);
//ui->channelPostFiles_TV->resizeColumnToContents(RsGxsChannelPostFilesModel::COLUMN_FILES_FILE);
//ui->channelPostFiles_TV->resizeColumnToContents(RsGxsChannelPostFilesModel::COLUMN_FILES_SIZE);
ui->channelPostFiles_TV->setAutoSelect(true);
// Now also set the post as read
if(IS_MSG_UNREAD(post.mMeta.mMsgStatus) || IS_MSG_NEW(post.mMeta.mMsgStatus))
{
RsGxsGrpMsgIdPair postId;
postId.second = post.mMeta.mMsgId;
postId.first = post.mMeta.mGroupId;
RsThread::async([postId]() { rsGxsChannels->markRead(postId, true) ; } );
}
}
#endif
void PostedListWidgetWithModel::updateGroupData()
{
if(groupId().isNull())

View File

@ -136,6 +136,7 @@ private slots:
#ifdef TO_REMOVE
void showPostDetails();
#endif
void postContextMenu(const QPoint&);
void showAuthorInPeople();
void tabCloseRequested(int index);
void updateSorting(int);
@ -145,7 +146,6 @@ private slots:
void subscribeGroup(bool subscribe);
void settingsChanged();
void postPostLoad();
void postContextMenu(const QPoint&);
void copyMessageLink();
void nextPosts();
void prevPosts();

View File

@ -454,22 +454,6 @@ p, li { white-space: pre-wrap; }
</item>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Minimum</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="LineEditClear" name="filter_LE">
<property name="placeholderText">

View File

@ -42,15 +42,26 @@ PulseAddDialog::PulseAddDialog(QWidget *parent)
connect(ui.pushButton_Cancel, SIGNAL( clicked( void ) ), this, SLOT( cancelPulse( void ) ) );
connect(ui.textEdit_Pulse, SIGNAL( textChanged( void ) ), this, SLOT( pulseTextChanged( void ) ) );
connect(ui.pushButton_picture, SIGNAL(clicked()), this, SLOT( toggle()));
connect(ui.pushButton_remove, SIGNAL(clicked()), this, SLOT( removePictures()));
// this connection is from browse push button to the slot function onBrowseButtonClicked()
connect(ui.pushButton_Browse, SIGNAL(clicked()), this, SLOT( onBrowseButtonClicked()));
ui.pushButton_picture->setIcon(FilesDefs::getIconFromQtResourcePath(QString(":/icons/png/photo.png")));
ui.pushButton_Browse->setIcon(FilesDefs::getIconFromQtResourcePath(QString(":/icons/png/add-image.png")));
ui.pushButton_remove->setIcon(FilesDefs::getIconFromQtResourcePath(QString(":/icons/mail/delete.png")));
ui.frame_picture->hide();
ui.pushButton_picture->hide();
// initially hiding the browse button as the attach image button is not pressed
ui.frame_PictureBrowse->hide();
//ui.frame_PictureBrowse->hide();
ui.label_image1->hide();
ui.label_image2->hide();
ui.label_image3->hide();
ui.label_image4->hide();
setAcceptDrops(true);
}
@ -160,11 +171,10 @@ void PulseAddDialog::cleanup()
ui.label_image4->clear();
ui.label_image4->setText(tr("Drag and Drop Image"));
ui.lineEdit_FilePath->clear();
// Hide Drag & Drop Frame and the browse frame
ui.frame_picture->hide();
ui.frame_PictureBrowse->hide();
//ui.frame_PictureBrowse->hide();
ui.pushButton_picture->hide();
ui.pushButton_picture->setChecked(false);
}
@ -489,24 +499,29 @@ void PulseAddDialog::addImage(const QString &path)
std::cerr << "PulseAddDialog::addImage() Installing in Image1";
std::cerr << std::endl;
ui.label_image1->setPixmap(icon);
ui.label_image1->show();
ui.frame_picture->show();
mImage1.copy((uint8_t *) ba.data(), ba.size());
std::cerr << "PulseAddDialog::addImage() Installing in Image1 Size: " << mImage1.mSize;
std::cerr << std::endl;
}
else if (mImage2.empty()) {
ui.label_image2->setPixmap(icon);
ui.label_image2->show();
mImage2.copy((uint8_t *) ba.data(), ba.size());
std::cerr << "PulseAddDialog::addImage() Installing in Image2 Size: " << mImage2.mSize;
std::cerr << std::endl;
}
else if (mImage3.empty()) {
ui.label_image3->setPixmap(icon);
ui.label_image3->show();
mImage3.copy((uint8_t *) ba.data(), ba.size());
std::cerr << "PulseAddDialog::addImage() Installing in Image3 Size: " << mImage3.mSize;
std::cerr << std::endl;
}
else if (mImage4.empty()) {
ui.label_image4->setPixmap(icon);
ui.label_image4->show();
mImage4.copy((uint8_t *) ba.data(), ba.size());
std::cerr << "PulseAddDialog::addImage() Installing in Image4 Size: " << mImage4.mSize;
std::cerr << std::endl;
@ -523,7 +538,6 @@ void PulseAddDialog::toggle()
{
// Show the input methods (drag and drop field and the browse button)
ui.frame_picture->show();
ui.frame_PictureBrowse->show();
ui.pushButton_picture->setToolTip(tr("Hide Pictures"));
}
@ -531,7 +545,6 @@ void PulseAddDialog::toggle()
{
// Hide the input methods (drag and drop field and the browse button)
ui.frame_picture->hide();
ui.frame_PictureBrowse->hide();
ui.pushButton_picture->setToolTip(tr("Add Pictures"));
}
@ -543,8 +556,27 @@ void PulseAddDialog::onBrowseButtonClicked()
QString filePath;
misc::getOpenFileName(this, RshareSettings::LASTDIR_IMAGES, tr("Load Picture File"), "Pictures (*.png *.xpm *.jpg *.jpeg *.gif *.webp )", filePath);
if (!filePath.isEmpty()) {
ui.lineEdit_FilePath->setText(filePath);
//ui.lineEdit_FilePath->setText(filePath);
addImage(filePath);
}
}
void PulseAddDialog::removePictures()
{
mImage1.clear();
ui.label_image1->clear();
mImage2.clear();
ui.label_image2->clear();
mImage3.clear();
ui.label_image3->clear();
mImage4.clear();
ui.label_image4->clear();
ui.label_image1->hide();
ui.label_image2->hide();
ui.label_image3->hide();
ui.label_image4->hide();
ui.frame_picture->hide();
}

View File

@ -53,7 +53,8 @@ private slots:
void clearDialog();
void pulseTextChanged();
void toggle();
void onBrowseButtonClicked();
void onBrowseButtonClicked();
void removePictures();
private:
// OLD VERSIONs, private now.

View File

@ -6,7 +6,7 @@
<rect>
<x>0</x>
<y>0</y>
<width>720</width>
<width>735</width>
<height>513</height>
</rect>
</property>
@ -52,6 +52,7 @@
<property name="font">
<font>
<pointsize>12</pointsize>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
@ -249,6 +250,12 @@
</item>
<item>
<widget class="QFrame" name="frame_picture">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
@ -278,6 +285,22 @@
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLabel" name="label_image4">
<property name="minimumSize">
<size>
<width>40</width>
<height>40</height>
</size>
</property>
<property name="text">
<string>Drag and Drop Image</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_image3">
<property name="minimumSize">
@ -310,63 +333,22 @@
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLabel" name="label_image4">
<property name="minimumSize">
<size>
<width>40</width>
<height>40</height>
</size>
<item row="0" column="2" rowspan="2">
<widget class="QToolButton" name="pushButton_remove">
<property name="toolTip">
<string>Remove all images</string>
</property>
<property name="text">
<string>Drag and Drop Image</string>
<string/>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QFrame" name="frame_PictureBrowse">
<property name="minimumSize">
<size>
<width>0</width>
<height>50</height>
</size>
</property>
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLineEdit" name="lineEdit_FilePath">
<property name="minimumSize">
<property name="iconSize">
<size>
<width>541</width>
<height>25</height>
<width>24</width>
<height>24</height>
</size>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pushButton_Browse">
<property name="minimumSize">
<size>
<width>101</width>
<height>25</height>
</size>
</property>
<property name="text">
<string>Browse...</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
@ -413,7 +395,6 @@
</layout>
<zorder>frame_URL</zorder>
<zorder>frame_picture</zorder>
<zorder>frame_PictureBrowse</zorder>
<zorder>textEdit_Pulse</zorder>
</widget>
</item>
@ -423,7 +404,7 @@
<number>6</number>
</property>
<item>
<widget class="QPushButton" name="pushButton_picture">
<widget class="QToolButton" name="pushButton_picture">
<property name="text">
<string/>
</property>
@ -438,6 +419,22 @@
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="pushButton_Browse">
<property name="toolTip">
<string>Add Picture</string>
</property>
<property name="text">
<string/>
</property>
<property name="iconSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
</widget>
</item>
<item>
<spacer name="buttonHSpacer">
<property name="orientation">
@ -456,6 +453,7 @@
<property name="font">
<font>
<pointsize>12</pointsize>
<weight>75</weight>
<bold>true</bold>
</font>
</property>

View File

@ -23,6 +23,8 @@
#include "WireGroupDialog.h"
#include "WireGroupItem.h"
#include "gui/settings/rsharesettings.h"
#include "gui/gxs/GxsIdDetails.h"
#include "gui/common/FilesDefs.h"
#include "PulseViewGroup.h"
#include "PulseReplySeperator.h"
@ -345,8 +347,20 @@ void WireDialog::updateGroups(std::vector<RsWireGroup>& groups)
{
// grab own groups.
// setup Chooser too.
mOwnGroups.push_back(it);
ui.groupChooser->addItem(QString::fromStdString(it.mMeta.mGroupName));
mOwnGroups.push_back(it);
QPixmap pixmap;
if (it.mHeadshot.mData)
{
if (GxsIdDetails::loadPixmapFromData( it.mHeadshot.mData,it.mHeadshot.mSize,pixmap,GxsIdDetails::ORIGINAL))
pixmap = pixmap.scaled(32,32);
}
else
{
// default.
pixmap = FilesDefs::getPixmapFromQtResourcePath(":/icons/wire.png").scaled(32,32);
}
ui.groupChooser->addItem(QPixmap(pixmap),QString::fromStdString(it.mMeta.mGroupName));
}
}
}

View File

@ -165,7 +165,6 @@ ChatWidget::ChatWidget(QWidget *parent)
connect(ui->actionResetFont, SIGNAL(triggered()), this, SLOT(resetFont()));
connect(ui->actionQuote, SIGNAL(triggered()), this, SLOT(quote()));
connect(ui->actionDropPlacemark, SIGNAL(triggered()), this, SLOT(dropPlacemark()));
connect(ui->actionSave_image, SIGNAL(triggered()), this, SLOT(saveImage()));
connect(ui->actionImport_sticker, SIGNAL(triggered()), this, SLOT(saveSticker()));
connect(ui->actionShow_Hidden_Images, SIGNAL(triggered()), ui->textBrowser, SLOT(showImages()));
ui->actionShow_Hidden_Images->setIcon(ui->textBrowser->getBlockedImage());
@ -628,7 +627,7 @@ bool ChatWidget::eventFilter(QObject *obj, QEvent *event)
QString toolTipText = ui->textBrowser->anchorForPosition(helpEvent->pos());
if (toolTipText.isEmpty() && !ui->textBrowser->getShowImages()){
QString imageStr;
if (ui->textBrowser->checkImage(helpEvent->pos(), imageStr)) {
if (ImageUtil::checkImage(ui->textBrowser, helpEvent->pos(), imageStr)) {
toolTipText = imageStr;
}
} else if (toolTipText.startsWith(PERSONID)){
@ -1151,10 +1150,7 @@ void ChatWidget::pasteText(const QString& S)
void ChatWidget::contextMenuTextBrowser(QPoint point)
{
QMatrix matrix;
matrix.translate(ui->textBrowser->horizontalScrollBar()->value(), ui->textBrowser->verticalScrollBar()->value());
QMenu *contextMnu = ui->textBrowser->createStandardContextMenu(matrix.map(point));
QMenu *contextMnu = ui->textBrowser->createStandardContextMenuFromPoint(point);
contextMnu->addSeparator();
contextMnu->addAction(ui->actionClearChatHistory);
@ -1162,14 +1158,12 @@ void ChatWidget::contextMenuTextBrowser(QPoint point)
contextMnu->addAction(ui->actionQuote);
contextMnu->addAction(ui->actionDropPlacemark);
if(ui->textBrowser->checkImage(point))
if(ImageUtil::checkImage(ui->textBrowser, point))
{
if (! ui->textBrowser->getShowImages())
contextMnu->addAction(ui->actionShow_Hidden_Images);
ui->actionSave_image->setData(point);
ui->actionImport_sticker->setData(point);
contextMnu->addAction(ui->actionSave_image);
contextMnu->addAction(ui->actionImport_sticker);
}
@ -1995,13 +1989,6 @@ void ChatWidget::dropPlacemark()
// or not.
}
void ChatWidget::saveImage()
{
QPoint point = ui->actionSave_image->data().toPoint();
QTextCursor cursor = ui->textBrowser->cursorForPosition(point);
ImageUtil::extractImage(window(), cursor);
}
void ChatWidget::saveSticker()
{
QPoint point = ui->actionImport_sticker->data().toPoint();

View File

@ -196,7 +196,6 @@ private slots:
void quote();
void dropPlacemark();
void saveImage();
void saveSticker();
private:

View File

@ -1049,15 +1049,6 @@ border-image: url(:/images/closepressed.png)
<string>Insert horizontal rule</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>
<action name="actionImport_sticker">
<property name="icon">
<iconset resource="../icons.qrc">

View File

@ -32,6 +32,7 @@
#include "MimeTextEdit.h"
#include "util/HandleRichText.h"
#include "gui/RetroShareLink.h"
#include "util/imageutil.h"
#include <retroshare/rspeers.h>
@ -246,6 +247,18 @@ void MimeTextEdit::contextMenuEvent(QContextMenuEvent *e)
QMenu *contextMenu = createStandardContextMenu(e->pos());
if (ImageUtil::checkImage(this, e->pos())) {
contextMenu->addSeparator();
QAction *a = contextMenu->addAction(FilesDefs::getIconFromQtResourcePath(":/images/document_save.png"), tr("Save image"), this, SLOT(saveImage()));
a->setData(e->pos());
a = contextMenu->addAction( tr("Copy image"), this, SLOT(copyImage()));
a->setData(e->pos());
contextMenu->addSeparator();
}
/* Add actions for pasting links */
contextMenu->addAction( tr("Paste as plain text"), this, SLOT(pastePlainText()));
QAction *spoilerAction = contextMenu->addAction(tr("Spoiler"), this, SLOT(spoiler()));
@ -292,3 +305,27 @@ void MimeTextEdit::spoiler()
{
RsHtml::insertSpoilerText(this->textCursor());
}
void MimeTextEdit::saveImage()
{
QAction *action = dynamic_cast<QAction*>(sender()) ;
if (!action) {
return;
}
QPoint point = action->data().toPoint();
QTextCursor cursor = cursorForPosition(point);
ImageUtil::extractImage(window(), cursor);
}
void MimeTextEdit::copyImage()
{
QAction *action = dynamic_cast<QAction*>(sender()) ;
if (!action) {
return;
}
QPoint point = action->data().toPoint();
QTextCursor cursor = cursorForPosition(point);
ImageUtil::copyImage(window(), cursor);
}

View File

@ -75,6 +75,8 @@ private slots:
void pasteOwnCertificateLink();
void pastePlainText();
void spoiler();
void saveImage();
void copyImage();
private:
QString textUnderCursor() const;

View File

@ -22,6 +22,7 @@
#include "RSImageBlockWidget.h"
#include "gui/common/FilesDefs.h"
#include "util/imageutil.h"
#include <retroshare/rsinit.h> //To get RsAccounts
@ -33,6 +34,7 @@
#include <QPainter>
#include <QPlainTextEdit>
#include <QTextDocumentFragment>
#include <QScrollBar>
#include <iostream>
@ -232,64 +234,6 @@ void RSTextBrowser::activateLinkClick(bool active)
mLinkClickActive = active;
}
/**
* @brief RSTextBrowser::checkImage
* @param pos where to check if image is shown in viewport coordinate
* @param imageStr return html source of cursor
* @return True if an image is under cursor
*/
bool RSTextBrowser::checkImage(QPoint pos, QString &imageStr)
{
//Get text cursor under pos. But if pos is under text browser end line this return last cursor.
QTextCursor cursor = cursorForPosition(pos);
//First get rect of cursor (could be at left or right of image)
QRect cursorRectStart = cursorRect(cursor);
//Second get text
cursor.movePosition(QTextCursor::Left, QTextCursor::MoveAnchor, 1);//To get character just before
QRect cursorRectLeft = cursorRect(cursor);
cursor.movePosition(QTextCursor::Right, QTextCursor::KeepAnchor, 2);
QRect cursorRectRight = cursorRect(cursor);
imageStr = cursor.selection().toHtml();
#ifdef RSTEXTBROWSER_CHECKIMAGE_DEBUG
mCursorRectStart = cursorRectStart;
mCursorRectLeft = cursorRectLeft;
mCursorRectRight = cursorRectRight;
std::cerr << "cursorRect LTRB :" << cursorRectStart.left() << ";" << cursorRectStart.top() << ";" << cursorRectStart.right() << ";" << cursorRectStart.bottom() << std::endl;
std::cerr << "cursorRectLeft :" << cursorRectLeft.left() << ";" << cursorRectLeft.top() << ";" << cursorRectLeft.right() << ";" << cursorRectLeft.bottom() << std::endl;
std::cerr << "cursorRectRight :" << cursorRectRight.left() << ";" << cursorRectRight.top() << ";" << cursorRectRight.right() << ";" << cursorRectRight.bottom() << std::endl;
std::cerr << "pos XY :" << pos.x() << ";" << pos.y() << std::endl;
#endif
QRect cursorRectEnd = cursorRectStart;
//Finally set left with right of precedent character.
if (cursorRectEnd.top() < cursorRectLeft.bottom())
{
cursorRectEnd.setLeft(cursorRectLeft.right());
} else {
//Image on new line
cursorRectEnd.setLeft(0);
}
//And set Right with left of next character.
if (cursorRectEnd.bottom() > cursorRectRight.top())
{
cursorRectEnd.setRight(cursorRectRight.left());
} else {
//New line after Image.
}
#ifdef RSTEXTBROWSER_CHECKIMAGE_DEBUG
mCursorRectEnd = cursorRectEnd;
std::cerr << "final cursorRect:" << cursorRectEnd.left() << ";" << cursorRectEnd.top() << ";" << cursorRectEnd.right() << ";" << cursorRectEnd.bottom() << std::endl;
viewport()->update();
#endif
//If pos is on text rect
if (cursorRectEnd.contains(pos))
{
return imageStr.indexOf("base64,") != -1;
}
return false;
}
/**
* @brief RSTextBrowser::anchorForPosition Replace anchorAt that doesn't works as expected.
* @param pos Where to get anchor from text
@ -317,18 +261,59 @@ QString RSTextBrowser::anchorForPosition(const QPoint &pos) const
return anchor;
}
QMenu *RSTextBrowser::createStandardContextMenu()
void RSTextBrowser::addContextMenuAction(QAction *action)
{
return createStandardContextMenu(QPoint());
mContextMenuActions.push_back(action);
}
QMenu *RSTextBrowser::createStandardContextMenu(const QPoint &position)
void RSTextBrowser::contextMenuEvent(QContextMenuEvent *event)
{
QMenu *menu = QTextBrowser::createStandardContextMenu(position);
emit calculateContextMenuActions();
QMenu *contextMenu = createStandardContextMenuFromPoint(event->pos());
QList<QAction*>::iterator it;
for (it = mContextMenuActions.begin(); it != mContextMenuActions.end(); ++it) {
contextMenu->addAction(*it);
}
contextMenu->exec(QCursor::pos());
delete(contextMenu);
}
QMenu *RSTextBrowser::createStandardContextMenuFromPoint(const QPoint &widgetPos)
{
QMatrix matrix;
matrix.translate(horizontalScrollBar()->value(), verticalScrollBar()->value());
QMenu *menu = QTextBrowser::createStandardContextMenu(matrix.map(widgetPos));
menu->addSeparator();
QAction *a = menu->addAction(FilesDefs::getIconFromQtResourcePath("://icons/textedit/code.png"), tr("View &Source"), this, SLOT(viewSource()));
a->setEnabled(!this->document()->isEmpty());
if (ImageUtil::checkImage(this, widgetPos
#ifdef RSTEXTBROWSER_CHECKIMAGE_DEBUG
, &mCursorRectStart, &mCursorRectLeft, &mCursorRectRight, &mCursorRectEnd
#endif
)) {
a = menu->addAction(FilesDefs::getIconFromQtResourcePath(":/images/document_save.png"), tr("Save image"), this, SLOT(saveImage()));
a->setData(widgetPos);
a = menu->addAction( tr("Copy image"), this, SLOT(copyImage()));
a->setData(widgetPos);
}
#ifdef RSTEXTBROWSER_CHECKIMAGE_DEBUG
std::cerr << "cursorRect LTRB :" << mCursorRectStart.left() << ";" << mCursorRectStart.top() << ";" << mCursorRectStart.right() << ";" << mCursorRectStart.bottom() << std::endl;
std::cerr << "cursorRectLeft :" << mCursorRectLeft.left() << ";" << mCursorRectLeft.top() << ";" << mCursorRectLeft.right() << ";" << mCursorRectLeft.bottom() << std::endl;
std::cerr << "cursorRectRight :" << mCursorRectRight.left() << ";" << mCursorRectRight.top() << ";" << mCursorRectRight.right() << ";" << mCursorRectRight.bottom() << std::endl;
std::cerr << "pos XY :" << widgetPos.x() << ";" << widgetPos.y() << std::endl;
std::cerr << "final cursorRect:" << mCursorRectEnd.left() << ";" << mCursorRectEnd.top() << ";" << mCursorRectEnd.right() << ";" << mCursorRectEnd.bottom() << std::endl;
viewport()->update();
#endif
return menu;
}
@ -350,3 +335,27 @@ void RSTextBrowser::viewSource()
delete dialog;
}
void RSTextBrowser::saveImage()
{
QAction *action = dynamic_cast<QAction*>(sender()) ;
if (!action) {
return;
}
QPoint point = action->data().toPoint();
QTextCursor cursor = cursorForPosition(point);
ImageUtil::extractImage(window(), cursor);
}
void RSTextBrowser::copyImage()
{
QAction *action = dynamic_cast<QAction*>(sender()) ;
if (!action) {
return;
}
QPoint point = action->data().toPoint();
QTextCursor cursor = cursorForPosition(point);
ImageUtil::copyImage(window(), cursor);
}

View File

@ -45,10 +45,10 @@ public:
void setImageBlockWidget(RSImageBlockWidget *widget);
void resetImagesStatus(bool load);
QPixmap getBlockedImage();
bool checkImage(QPoint pos, QString &imageStr);
bool checkImage(QPoint pos) {QString imageStr; return checkImage(pos, imageStr); }
QString anchorForPosition(const QPoint &pos) const;
// Add QAction to context menu (action won't be deleted)
void addContextMenuAction(QAction *action);
void activateLinkClick(bool active);
@ -58,8 +58,10 @@ public:
QVariant textColorQuotes() const { return highlighter->textColorQuotes();}
bool getShowImages() const { return mShowImages; }
QMenu *createStandardContextMenu();
QMenu *createStandardContextMenu(const QPoint &position);
QMenu *createStandardContextMenuFromPoint(const QPoint &widgetPos);
Q_SIGNALS:
void calculateContextMenuActions();
public slots:
void showImages();
@ -70,9 +72,16 @@ private slots:
void linkClicked(const QUrl &url);
void destroyImageBlockWidget();
void viewSource();
void saveImage();
void copyImage();
protected:
void paintEvent(QPaintEvent *event);
virtual void contextMenuEvent(QContextMenuEvent *event);
private:
// Hide method from QTextBrowser
using QTextBrowser::createStandardContextMenu;
private:
QString mPlaceholderText;
@ -80,6 +89,7 @@ private:
RSImageBlockWidget *mImageBlockWidget;
bool mLinkClickActive;
RsSyntaxHighlighter *highlighter;
QList<QAction*> mContextMenuActions;
#ifdef RSTEXTBROWSER_CHECKIMAGE_DEBUG
QRect mCursorRectStart;
QRect mCursorRectLeft;

View File

@ -186,13 +186,13 @@ void BaseBoardsCommentsItem::loadMessage()
if (posts.size() == 1)
{
std::cerr << (void*)this << ": Obtained post, with msgId = " << posts[0].mMeta.mMsgId << std::endl;
const RsPostedPost& post(posts[0]);
RsPostedPost post(posts[0]); // no reference to temporary because it's passed to a thread!
RsQThreadUtils::postToObject( [post,this]() { setPost(post,true); mIsLoadingMessage = false;}, this );
}
else if(comments.size() == 1)
{
const RsGxsComment& cmt = comments[0];
RsGxsComment cmt(comments[0]);
std::cerr << (void*)this << ": Obtained comment, setting messageId to threadID = " << cmt.mMeta.mThreadId << std::endl;
RsQThreadUtils::postToObject( [cmt,this]()
@ -287,14 +287,13 @@ void BaseBoardsCommentsItem::readToggled(bool checked)
return;
}
setReadStatus(false, checked); // Can't call this inside an async call since the widget may be destroyed afterwards!
// So we do it right away.
RsThread::async( [this,checked]() {
RsGxsGrpMsgIdPair msgPair = std::make_pair(groupId(), messageId());
rsPosted->setCommentReadStatus(msgPair, !checked);
RsQThreadUtils::postToObject( [this,checked]() {
setReadStatus(false, checked);
} );
});
}

View File

@ -292,13 +292,13 @@ void ChannelsCommentsItem::loadMessage()
#ifdef DEBUG_ITEM
std::cerr << (void*)this << ": Obtained post, with msgId = " << posts[0].mMeta.mMsgId << std::endl;
#endif
const RsGxsChannelPost& post(posts[0]);
RsGxsChannelPost post(posts[0]); // no reference to temporary here, because we pass this to a thread
RsQThreadUtils::postToObject( [post,this]() { setPost(post); }, this );
}
else if(comments.size() == 1)
{
const RsGxsComment& cmt = comments[0];
RsGxsComment cmt(comments[0]);
#ifdef DEBUG_ITEM
std::cerr << (void*)this << ": Obtained comment, setting messageId to threadID = " << cmt.mMeta.mThreadId << std::endl;
#endif

View File

@ -125,7 +125,7 @@ void GxsForumGroupItem::loadGroup()
std::cerr << std::endl;
return;
}
const RsGxsForumGroup& group(groups[0]);
RsGxsForumGroup group(groups[0]);// no reference to teporary accross threads!
RsQThreadUtils::postToObject( [group,this]()
{

View File

@ -235,7 +235,7 @@ void GxsForumMsgItem::loadMessage()
std::cerr << std::endl;
return;
}
const RsGxsForumMsg& msg(msgs[0]);
RsGxsForumMsg msg(msgs[0]);
RsQThreadUtils::postToObject( [msg,this]()
{
@ -280,7 +280,7 @@ void GxsForumMsgItem::loadParentMessage(const RsGxsMessageId& parent_msg)
std::cerr << std::endl;
return;
}
const RsGxsForumMsg& msg(msgs[0]);
RsGxsForumMsg msg(msgs[0]);
RsQThreadUtils::postToObject( [msg,this]()
{

View File

@ -794,56 +794,25 @@ void CreateGxsChannelMsg::sendMessage(const std::string &subject, const std::str
/* rsGxsChannels */
if (rsGxsChannels)
{
RsGxsChannelPost post;
post.mMeta.mGroupId = mChannelId;
post.mMeta.mParentId.clear() ;
post.mMeta.mThreadId.clear() ;
post.mMeta.mMsgId.clear() ;
post.mMeta.mOrigMsgId = mOrigPostId;
post.mMeta.mMsgName = subject;
post.mMsg = msg;
post.mFiles = files;
QByteArray ba;
QBuffer buffer(&ba);
RsGxsImage image;
if(!picture.isNull())
{
// send chan image
buffer.open(QIODevice::WriteOnly);
preview_W->getCroppedScaledPicture().save(&buffer, "JPG"); // writes image into ba in PNG format
post.mThumbnail.copy((uint8_t *) ba.data(), ba.size());
image.copy((uint8_t *) ba.data(), ba.size());
}
std::string error_string;
RsGxsMessageId post_id;
#ifdef ENABLE_GENERATE
int generateCount = 0;
if (generateCheckBox->isChecked()) {
generateCount = generateSpinBox->value();
if (QMessageBox::question(this, tr("Generate mass data"), tr("Do you really want to generate %1 messages ?").arg(generateCount), QMessageBox::Yes|QMessageBox::No, QMessageBox::No) == QMessageBox::No) {
return;
}
}
#endif
uint32_t token;
#ifdef ENABLE_GENERATE
if (generateCount) {
for (int count = 0; count < generateCount; ++count) {
RsGxsChannelPost generatePost = post;
generatePost.mMeta.mMsgName = QString("%1 %2").arg(QString::fromUtf8(post.mMeta.mMsgName.c_str())).arg(count + 1, 3, 10, QChar('0')).toUtf8().constData();
rsGxsChannels->createPost(token, generatePost);
}
} else {
#endif
rsGxsChannels->createPost(token, post);
#ifdef ENABLE_GENERATE
}
#endif
if(!rsGxsChannels->createPostV2(mChannelId,subject,msg,files,image,mOrigPostId,post_id,error_string))
QMessageBox::critical(nullptr,tr("Cannot publish post"),QString::fromStdString(error_string));
}
accept();

View File

@ -27,6 +27,8 @@
#include "retroshare/rsgxschannels.h"
#include "retroshare/rsexpr.h"
#include "gui/MainWindow.h"
#include "gui/mainpagestack.h"
#include "gui/common/FilesDefs.h"
#include "util/qtthreadsutils.h"
#include "util/HandleRichText.h"
@ -36,7 +38,7 @@
#include "GxsChannelPostFilesModel.h"
//#define DEBUG_CHANNEL_MODEL_DATA
//#define DEBUG_CHANNEL_MODEL
#define DEBUG_CHANNEL_MODEL
Q_DECLARE_METATYPE(RsMsgMetaData)
Q_DECLARE_METATYPE(RsGxsChannelPost)
@ -61,7 +63,7 @@ void RsGxsChannelPostsModel::setMode(TreeMode mode)
if(mode == TREE_MODE_LIST)
setNumColumns(2);
triggerViewUpdate();
triggerViewUpdate(true,true);
}
void RsGxsChannelPostsModel::computeCommentCounts( std::vector<RsGxsChannelPost>& posts, std::vector<RsGxsComment>& comments)
@ -116,12 +118,15 @@ void RsGxsChannelPostsModel::preMods()
}
void RsGxsChannelPostsModel::postMods()
{
triggerViewUpdate();
emit layoutChanged();
}
void RsGxsChannelPostsModel::triggerViewUpdate()
void RsGxsChannelPostsModel::triggerViewUpdate(bool data_changed, bool layout_changed)
{
emit dataChanged(createIndex(0,0,(void*)NULL), createIndex(rowCount()-1,mColumns-1,(void*)NULL));
if(data_changed)
emit dataChanged(createIndex(0,0,(void*)NULL), createIndex(rowCount()-1,mColumns-1,(void*)NULL));
if(layout_changed)
emit layoutChanged();
}
void RsGxsChannelPostsModel::getFilesList(std::list<ChannelPostFileInfo>& files)
@ -168,11 +173,7 @@ void RsGxsChannelPostsModel::updateFilter(uint32_t& count)
{
preMods();
beginResetModel();
mFilteredPosts.clear();
//mFilteredPosts.push_back(0);
endResetModel();
for(size_t i=0;i<mPosts.size();++i)
if(postPassesFilter(mPosts[i],mFilteredStrings,mFilterUnread))
@ -314,17 +315,8 @@ bool RsGxsChannelPostsModel::setNumColumns(int n)
preMods();
beginResetModel();
endResetModel();
mColumns = n;
if (rowCount()>0)
{
beginInsertRows(QModelIndex(),0,rowCount()-1);
endInsertRows();
}
postMods();
return true;
@ -453,7 +445,6 @@ const RsGxsGroupId& RsGxsChannelPostsModel::currentGroupId() const
{
return mChannelGroup.mMeta.mGroupId;
}
void RsGxsChannelPostsModel::updateChannel(const RsGxsGroupId& channel_group_id)
{
if(channel_group_id.isNull())
@ -547,7 +538,7 @@ void RsGxsChannelPostsModel::updateSinglePost(const RsGxsChannelPost& post,std::
uint32_t count;
updateFilter(count);
triggerViewUpdate();
triggerViewUpdate(true,false);
}
void RsGxsChannelPostsModel::setPosts(const RsGxsChannelGroup& group, std::vector<RsGxsChannelPost>& posts)
@ -557,7 +548,7 @@ void RsGxsChannelPostsModel::setPosts(const RsGxsChannelGroup& group, std::vecto
initEmptyHierarchy();
mChannelGroup = group;
createPostsArray(posts);
createPostsArray(posts);
std::sort(mPosts.begin(),mPosts.end());
@ -586,7 +577,9 @@ void RsGxsChannelPostsModel::update_posts(const RsGxsGroupId& group_id)
if(group_id.isNull())
return;
RsThread::async([this, group_id]()
MainWindow::getPage(MainWindow::Channels)->setCursor(Qt::WaitCursor) ; // Maybe we should pass that widget when calling update_posts
RsThread::async([this, group_id]()
{
// 1 - get message data from p3GxsChannels
@ -641,12 +634,14 @@ void RsGxsChannelPostsModel::update_posts(const RsGxsGroupId& group_id)
delete comments;
delete votes;
}, this );
MainWindow::getPage(MainWindow::Channels)->setCursor(Qt::ArrowCursor) ;
}, this );
});
}
void RsGxsChannelPostsModel::createPostsArray(std::vector<RsGxsChannelPost>& posts)
void RsGxsChannelPostsModel::old_createPostsArray(std::vector<RsGxsChannelPost>& posts)
{
// collect new versions of posts if any
@ -692,9 +687,6 @@ void RsGxsChannelPostsModel::createPostsArray(std::vector<RsGxsChannelPost>& pos
uint32_t current_index = new_versions[i] ;
uint32_t source_index = new_versions[i] ;
#ifdef DEBUG_CHANNEL_MODEL
RsGxsMessageId source_msg_id = posts[source_index].mMeta.mMsgId ;
#endif
// What we do is everytime we find a replacement post, we climb up the replacement graph until we find the original post
// (or the most recent version of it). When we reach this post, we replace it with the data of the source post.
@ -764,6 +756,153 @@ void RsGxsChannelPostsModel::createPostsArray(std::vector<RsGxsChannelPost>& pos
}
}
void RsGxsChannelPostsModel::createPostsArray(std::vector<RsGxsChannelPost>& posts)
{
// The hierarchy of posts may contain edited posts. In the new model (03/2023), mOrigMsgId points to the original
// top-level post in the hierarchy of edited posts. However, in the old model, mOrigMsgId points to the edited post.
// Therefore the algorithm below is made to cope with both models at once.
//
// In the future, using the new model, it will be possible to delete old versions from the db, and detect new versions
// because they all share the same mOrigMsgId.
//
// We proceed as follows:
//
// 1 - create a search map to convert post IDs into their index in the posts tab
// 2 - recursively climb up the post mOrigMsgId until no parent is found. At top level, create the original post, and add all previous elements as newer versions.
// 3 - go through the list of original posts, select among them the most recent version, and set all others as older versions.
//
// The algorithm handles the case where some parent has been deleted.
#ifdef DEBUG_CHANNEL_MODEL
std::cerr << "Inserting channel posts" << std::endl;
#endif
// 1 - create a search map to convert post IDs into their index in the posts tab
#ifdef DEBUG_CHANNEL_MODEL
std::cerr << " Given list: " << std::endl;
#endif
std::map<RsGxsMessageId,uint32_t> search_map ;
for (uint32_t i=0;i<posts.size();++i)
{
#ifdef DEBUG_CHANNEL_MODEL
std::cerr << " " << i << ": " << posts[i].mMeta.mMsgId << " orig=" << posts[i].mMeta.mOrigMsgId << " publish TS =" << posts[i].mMeta.mPublishTs << std::endl;
#endif
search_map[posts[i].mMeta.mMsgId] = i ;
}
// 2 - recursively climb up the post mOrigMsgId until no parent is found. At top level, create the original post, and add all previous elements as newer versions.
#ifdef DEBUG_CHANNEL_MODEL
std::cerr << " Searching for top-level posts..." << std::endl;
#endif
std::map<RsGxsMessageId,std::pair<uint32_t,std::set<RsGxsMessageId> > > original_versions ;
for (uint32_t i=0;i<posts.size();++i)
{
#ifdef DEBUG_CHANNEL_MODEL
std::cerr << " Post " << i;
#endif
// We use a recursive function here, so as to collect versions when climbing up to the top level post, and
// set the top level as the orig for all visited posts on the way back.
std::function<RsGxsMessageId (uint32_t,std::set<RsGxsMessageId>& versions,rstime_t newest_time,uint32_t newest_index,int depth)> recurs_find_top_level
= [&posts,&search_map,&recurs_find_top_level,&original_versions](uint32_t index,
std::set<RsGxsMessageId>& collected_versions,
rstime_t newest_time,
uint32_t newest_index,
int depth)
-> RsGxsMessageId
{
const auto& m(posts[index].mMeta);
if(m.mPublishTs > newest_time)
{
newest_index = index;
newest_time = m.mPublishTs;
}
collected_versions.insert(m.mMsgId);
RsGxsMessageId top_level_id;
std::map<RsGxsMessageId,uint32_t>::const_iterator it;
if(m.mOrigMsgId.isNull() || m.mOrigMsgId==m.mMsgId) // we have a top-level post.
top_level_id = m.mMsgId;
else if( (it = search_map.find(m.mOrigMsgId)) == search_map.end()) // we don't have the post. Never mind, we store the
{
top_level_id = m.mOrigMsgId;
collected_versions.insert(m.mOrigMsgId); // this one will never be added to the set by the previous call
}
else
{
top_level_id = recurs_find_top_level(it->second,collected_versions,newest_time,newest_index,depth+1);
posts[index].mMeta.mOrigMsgId = top_level_id; // this fastens calculation because it will skip already seen posts.
return top_level_id;
}
#ifdef DEBUG_CHANNEL_MODEL
std::cerr << std::string(2*depth,' ') << " top level = " << top_level_id ;
#endif
auto vit = original_versions.find(top_level_id);
if(vit != original_versions.end())
{
if(posts[vit->second.first].mMeta.mPublishTs < newest_time)
vit->second.first = newest_index;
#ifdef DEBUG_CHANNEL_MODEL
std::cerr << " already existing. " << std::endl;
#endif
}
else
{
original_versions[top_level_id].first = newest_index;
#ifdef DEBUG_CHANNEL_MODEL
std::cerr << " new. " << std::endl;
#endif
}
original_versions[top_level_id].second.insert(collected_versions.begin(),collected_versions.end());
return top_level_id;
};
auto versions_set = std::set<RsGxsMessageId>();
recurs_find_top_level(i,versions_set,posts[i].mMeta.mPublishTs,i,0);
}
mPosts.clear();
#ifdef DEBUG_CHANNEL_MODEL
std::cerr << " Total top_level posts: " << original_versions.size() << std::endl;
for(auto it:original_versions)
{
std::cerr << " Post " << it.first << ". Total versions = " << it.second.second.size() << " latest: " << posts[it.second.first].mMeta.mMsgId << std::endl;
for(auto m:it.second.second)
if(m != it.first)
std::cerr << " other (newer version): " << m << std::endl;
}
#endif
// make sure the posts are delivered in the same order they appears in the posts[] tab.
std::vector<uint32_t> ids;
for(auto id:original_versions)
ids.push_back(id.second.first);
std::sort(ids.begin(),ids.end());
for(uint32_t i=0;i<ids.size();++i)
{
mPosts.push_back(posts[ids[i]]);
mPosts.back().mOlderVersions = original_versions[posts[ids[i]].mMeta.mMsgId].second;
}
}
void RsGxsChannelPostsModel::setAllMsgReadStatus(bool read_status)
{
// No need to call preMods()/postMods() here because we're not changing the model

View File

@ -115,7 +115,7 @@ public:
void updateChannel(const RsGxsGroupId& channel_group_id);
const RsGxsGroupId& currentGroupId() const;
void triggerViewUpdate();
void triggerViewUpdate(bool data_changed,bool layout_changed);
// sets the number of columns. Returns 0 if nothing changes.
bool setNumColumns(int n);
@ -235,8 +235,9 @@ private:
//static void convertMsgToPostEntry(const RsGxsChannelGroup &mChannelGroup, const RsMsgMetaData &msg, bool useChildTS, ChannelModelPostEntry& fentry);
//void computeMessagesHierarchy(const RsGxsChannelGroup& forum_group, const std::vector<RsMsgMetaData> &msgs_array, std::vector<ChannelPostsModelPostEntry> &posts, std::map<RsGxsMessageId, std::vector<std::pair<time_t, RsGxsMessageId> > > &mPostVersions);
void createPostsArray(std::vector<RsGxsChannelPost> &posts);
void setPosts(const RsGxsChannelGroup& group, std::vector<RsGxsChannelPost> &posts);
void old_createPostsArray(std::vector<RsGxsChannelPost> &posts);
void createPostsArray(std::vector<RsGxsChannelPost>& posts);
void setPosts(const RsGxsChannelGroup& group, std::vector<RsGxsChannelPost> &posts);
public:
void updateSinglePost(const RsGxsChannelPost& post, std::set<RsGxsFile>& added_files, std::set<RsGxsFile>& removed_files);
private:

View File

@ -92,9 +92,9 @@ Q_DECLARE_METATYPE(ChannelPostFileInfo)
int ChannelPostDelegate::cellSize(int col,const QFont& font,uint32_t parent_width) const
{
if(mUseGrid || col==0)
return mZoom*COLUMN_SIZE_FONT_FACTOR_W*QFontMetricsF(font).height();
return (int)floor(mZoom*COLUMN_SIZE_FONT_FACTOR_W*QFontMetricsF(font).height());
else
return 0.8*parent_width - mZoom*COLUMN_SIZE_FONT_FACTOR_W*QFontMetricsF(font).height();
return (int)floor(0.8*parent_width - mZoom*COLUMN_SIZE_FONT_FACTOR_W*QFontMetricsF(font).height());
}
void ChannelPostDelegate::zoom(bool zoom_or_unzoom)
@ -279,18 +279,18 @@ void ChannelPostDelegate::setWidgetGrid(bool use_grid)
QWidget *ChannelPostFilesDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &/*option*/, const QModelIndex& index) const
{
ChannelPostFileInfo file = index.data(Qt::UserRole).value<ChannelPostFileInfo>() ;
ChannelPostFileInfo file = index.data(Qt::UserRole).value<ChannelPostFileInfo>() ;
if(index.column() == RsGxsChannelPostFilesModel::COLUMN_FILES_FILE)
{
if(index.column() == RsGxsChannelPostFilesModel::COLUMN_FILES_FILE)
{
GxsChannelFilesStatusWidget* w = new GxsChannelFilesStatusWidget(file,parent,true);
w->setFocusPolicy(Qt::StrongFocus);
connect(w,SIGNAL(onButtonClick()),this->parent(),SLOT(updateDAll_PB()));
return w;
}
else
return NULL;
return w;
}
else
return NULL;
}
void ChannelPostFilesDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &/* index */) const
{
@ -379,7 +379,7 @@ GxsChannelPostsWidgetWithModel::GxsChannelPostsWidgetWithModel(const RsGxsGroupI
connect(ui->viewType_TB,SIGNAL(clicked()),this,SLOT(switchView()));
ui->showUnread_TB->setIcon(FilesDefs::getIconFromQtResourcePath(":/images/message-state-unread.png"));
ui->showUnread_TB->setChecked(false);
whileBlocking(ui->showUnread_TB)->setChecked(false);
ui->showUnread_TB->setToolTip(tr("Show unread posts only"));
connect(ui->showUnread_TB,SIGNAL(toggled(bool)),this,SLOT(switchOnlyUnread(bool)));
@ -392,7 +392,7 @@ GxsChannelPostsWidgetWithModel::GxsChannelPostsWidgetWithModel(const RsGxsGroupI
mChannelPostsDelegate->setAspectRatio(ChannelPostThumbnailView::ASPECT_RATIO_16_9);
connect(ui->postsTree,SIGNAL(zoomRequested(bool)),this,SLOT(updateZoomFactor(bool)));
connect(ui->postsTree,SIGNAL(zoomRequested(bool)),this,SLOT(onUpdateZoomFactor(bool)));
connect(ui->commentsDialog,SIGNAL(commentsLoaded(int)),this,SLOT(updateCommentsCount(int)));
ui->channelPostFiles_TV->setModel(mChannelPostFilesModel = new RsGxsChannelPostFilesModel(this));
@ -517,23 +517,33 @@ void GxsChannelPostsWidgetWithModel::currentTabChanged(int t)
case CHANNEL_TABS_POSTS:
ui->showUnread_TB->setHidden(false);
ui->viewType_TB->setHidden(false);
updateZoomFactor(0); // fixes a bug due to the widget now knowing its size when not displayed.
break;
}
}
void GxsChannelPostsWidgetWithModel::updateZoomFactor(bool zoom_or_unzoom)
void GxsChannelPostsWidgetWithModel::onUpdateZoomFactor(bool zoom_or_unzoom)
{
mChannelPostsDelegate->zoom(zoom_or_unzoom);
if(zoom_or_unzoom)
updateZoomFactor(1);
else
updateZoomFactor(-1);
}
void GxsChannelPostsWidgetWithModel::updateZoomFactor(int what_to_do)
{
if(what_to_do != 0)
mChannelPostsDelegate->zoom(what_to_do > 0);
QSize s = ui->postsTree->size();
int n_columns = std::max(1,(int)floor(s.width() / (float)(mChannelPostsDelegate->cellSize(0,font(),s.width()))));
mChannelPostsModel->setNumColumns(n_columns); // forces the update
for(int i=0;i<mChannelPostsModel->columnCount();++i)
ui->postsTree->setColumnWidth(i,mChannelPostsDelegate->cellSize(i,font(),ui->postsTree->width()));
QSize s = ui->postsTree->size();
int n_columns = std::max(1,(int)floor(s.width() / (mChannelPostsDelegate->cellSize(0,font(),s.width()))));
mChannelPostsModel->setNumColumns(n_columns); // forces the update
ui->postsTree->dataChanged(QModelIndex(),QModelIndex());
if(what_to_do)
mChannelPostsModel->triggerViewUpdate(true,false);
else
mChannelPostsModel->triggerViewUpdate(false,true);
}
void GxsChannelPostsWidgetWithModel::sortColumnPostFiles(int col,Qt::SortOrder so)
@ -639,7 +649,7 @@ void GxsChannelPostsWidgetWithModel::switchView()
selectItem(msg_id);
ui->postsTree->setFocus();
mChannelPostsModel->triggerViewUpdate(); // This is already called by setMode(), but the model cannot know how many
mChannelPostsModel->triggerViewUpdate(false,true); // This is already called by setMode(), but the model cannot know how many
// columns is actually has until we call handlePostsTreeSizeChange(), so
// we have to call it again here.
}
@ -1067,8 +1077,9 @@ void GxsChannelPostsWidgetWithModel::postChannelPostLoad()
best = i;
mChannelPostsDelegate->setAspectRatio(static_cast<ChannelPostThumbnailView::AspectRatio>(best));
mChannelPostsModel->triggerViewUpdate();
handlePostsTreeSizeChange(ui->postsTree->size(),true); // force the update
updateZoomFactor(0);
}
void GxsChannelPostsWidgetWithModel::updateDisplay(bool update_group_data,bool update_posts)

View File

@ -154,7 +154,7 @@ private slots:
void editPost();
void postContextMenu(const QPoint&);
void copyMessageLink();
void updateZoomFactor(bool zoom_or_unzoom);
void onUpdateZoomFactor(bool zoom_or_unzoom);
void switchView();
void switchOnlyUnread(bool b);
void markMessageUnread();
@ -168,7 +168,8 @@ public slots:
void copyChannelFilesLink();
private:
void processSettings(bool load);
void updateZoomFactor(int what_to_do); // -1=unzoom, 0=nothing, 1=zoom
void processSettings(bool load);
RsGxsMessageId getCurrentItemId() const;
void selectItem(const RsGxsMessageId& msg_id);

View File

@ -402,7 +402,7 @@
<string notr="true">&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Ubuntu'; font-size:11pt; font-weight:400; font-style:normal;&quot;&gt;
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Sans Serif'; font-size:11pt; font-weight:400; font-style:normal;&quot;&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2'; font-size:8pt;&quot;&gt;Description&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="textInteractionFlags">
@ -534,7 +534,7 @@ p, li { white-space: pre-wrap; }
<item>
<layout class="QVBoxLayout" name="verticalLayout_6">
<item>
<widget class="QLabel" name="postName_LB">
<widget class="ElidedLabel" name="postName_LB">
<property name="font">
<font>
<weight>75</weight>

View File

@ -19,6 +19,7 @@
*******************************************************************************/
#include "CreateGxsForumMsg.h"
#include "ui_CreateGxsForumMsg.h"
#include <QMessageBox>
#include <QFile>
@ -54,63 +55,64 @@
/** Constructor */
CreateGxsForumMsg::CreateGxsForumMsg(const RsGxsGroupId &fId, const RsGxsMessageId &pId, const RsGxsMessageId& mOId, const RsGxsId& posterId, bool isModerating)
: QDialog(NULL, Qt::WindowSystemMenuHint | Qt::WindowTitleHint | Qt::WindowMinMaxButtonsHint | Qt::WindowCloseButtonHint),
mForumId(fId), mParentId(pId), mOrigMsgId(mOId),mPosterId(posterId),mIsModerating(isModerating)
mForumId(fId), mParentId(pId), mOrigMsgId(mOId),mPosterId(posterId),mIsModerating(isModerating),
ui(new Ui::CreateGxsForumMsg)
{
/* Invoke the Qt Designer generated object setup routine */
ui.setupUi(this);
ui->setupUi(this);
setAttribute(Qt::WA_DeleteOnClose, true);
/* Setup UI helper */
mStateHelper = new UIStateHelper(this);
mStateHelper->addWidget(CREATEGXSFORUMMSG_FORUMINFO, ui.postButton);
mStateHelper->addWidget(CREATEGXSFORUMMSG_FORUMINFO, ui.innerFrame);
mStateHelper->addLoadPlaceholder(CREATEGXSFORUMMSG_FORUMINFO, ui.forumName);
mStateHelper->addLoadPlaceholder(CREATEGXSFORUMMSG_FORUMINFO, ui.forumSubject);
mStateHelper->addClear(CREATEGXSFORUMMSG_FORUMINFO, ui.forumName);
mStateHelper->addWidget(CREATEGXSFORUMMSG_FORUMINFO, ui->postButton);
mStateHelper->addWidget(CREATEGXSFORUMMSG_FORUMINFO, ui->innerFrame);
mStateHelper->addLoadPlaceholder(CREATEGXSFORUMMSG_FORUMINFO, ui->forumName);
mStateHelper->addLoadPlaceholder(CREATEGXSFORUMMSG_FORUMINFO, ui->forumSubject);
mStateHelper->addClear(CREATEGXSFORUMMSG_FORUMINFO, ui->forumName);
mStateHelper->addWidget(CREATEGXSFORUMMSG_PARENTMSG, ui.postButton);
mStateHelper->addWidget(CREATEGXSFORUMMSG_PARENTMSG, ui.innerFrame);
mStateHelper->addLoadPlaceholder(CREATEGXSFORUMMSG_PARENTMSG, ui.forumName);
mStateHelper->addLoadPlaceholder(CREATEGXSFORUMMSG_PARENTMSG, ui.forumSubject);
mStateHelper->addClear(CREATEGXSFORUMMSG_PARENTMSG, ui.forumName);
mStateHelper->addWidget(CREATEGXSFORUMMSG_PARENTMSG, ui->postButton);
mStateHelper->addWidget(CREATEGXSFORUMMSG_PARENTMSG, ui->innerFrame);
mStateHelper->addLoadPlaceholder(CREATEGXSFORUMMSG_PARENTMSG, ui->forumName);
mStateHelper->addLoadPlaceholder(CREATEGXSFORUMMSG_PARENTMSG, ui->forumSubject);
mStateHelper->addClear(CREATEGXSFORUMMSG_PARENTMSG, ui->forumName);
mStateHelper->addWidget(CREATEGXSFORUMMSG_ORIGMSG, ui.postButton);
mStateHelper->addWidget(CREATEGXSFORUMMSG_ORIGMSG, ui.innerFrame);
mStateHelper->addLoadPlaceholder(CREATEGXSFORUMMSG_ORIGMSG, ui.forumName);
mStateHelper->addLoadPlaceholder(CREATEGXSFORUMMSG_ORIGMSG, ui.forumSubject);
mStateHelper->addClear(CREATEGXSFORUMMSG_ORIGMSG, ui.forumName);
mStateHelper->addWidget(CREATEGXSFORUMMSG_ORIGMSG, ui->postButton);
mStateHelper->addWidget(CREATEGXSFORUMMSG_ORIGMSG, ui->innerFrame);
mStateHelper->addLoadPlaceholder(CREATEGXSFORUMMSG_ORIGMSG, ui->forumName);
mStateHelper->addLoadPlaceholder(CREATEGXSFORUMMSG_ORIGMSG, ui->forumSubject);
mStateHelper->addClear(CREATEGXSFORUMMSG_ORIGMSG, ui->forumName);
QString text = mOId.isNull()?(pId.isNull() ? tr("Start New Thread") : tr("Post Forum Message")):tr("Edit Message");
setWindowTitle(text);
if (!mOId.isNull())
ui.postButton->setText(tr ("Update"));
ui->postButton->setText(tr ("Update"));
ui.forumMessage->setPlaceholderText(tr ("Text"));
ui->forumMessage->setPlaceholderText(tr ("Text"));
ui.headerFrame->setHeaderImage(FilesDefs::getPixmapFromQtResourcePath(":/icons/png/forums.png"));
ui.headerFrame->setHeaderText(text);
ui->headerFrame->setHeaderImage(FilesDefs::getPixmapFromQtResourcePath(":/icons/png/forums.png"));
ui->headerFrame->setHeaderText(text);
ui.generateSpinBox->setEnabled(false);
ui->generateSpinBox->setEnabled(false);
Settings->loadWidgetInformation(this);
connect(ui.hashBox, SIGNAL(fileHashingFinished(QList<HashedFile>)), this, SLOT(fileHashingFinished(QList<HashedFile>)));
connect(ui->hashBox, SIGNAL(fileHashingFinished(QList<HashedFile>)), this, SLOT(fileHashingFinished(QList<HashedFile>)));
// connect up the buttons.
connect(ui.postButton, SIGNAL(clicked()), this, SLOT(createMsg()));
connect(ui.cancelButton, SIGNAL(clicked()), this, SLOT(reject()));
connect(ui.emoticonButton, SIGNAL(clicked()), this, SLOT(smileyWidgetForums()));
connect(ui.attachFileButton, SIGNAL(clicked()), this, SLOT(addFile()));
connect(ui.attachPictureButton, SIGNAL(clicked()), this, SLOT(addPicture()));
connect(ui.forumMessage, SIGNAL(textChanged()), this, SLOT(checkLength()));
connect(ui.generateCheckBox, SIGNAL(toggled(bool)), ui.generateSpinBox, SLOT(setEnabled(bool)));
connect(ui->postButton, SIGNAL(clicked()), this, SLOT(createMsg()));
connect(ui->cancelButton, SIGNAL(clicked()), this, SLOT(reject()));
connect(ui->emoticonButton, SIGNAL(clicked()), this, SLOT(smileyWidgetForums()));
connect(ui->attachFileButton, SIGNAL(clicked()), this, SLOT(addFile()));
connect(ui->attachPictureButton, SIGNAL(clicked()), this, SLOT(addPicture()));
connect(ui->forumMessage, SIGNAL(textChanged()), this, SLOT(checkLength()));
connect(ui->generateCheckBox, SIGNAL(toggled(bool)), ui->generateSpinBox, SLOT(setEnabled(bool)));
setAcceptDrops(true);
ui.hashBox->setDropWidget(this);
ui.hashBox->setAutoHide(false);
ui->hashBox->setDropWidget(this);
ui->hashBox->setAutoHide(false);
mParentMsgLoaded = false;
mForumMetaLoaded = false;
@ -118,11 +120,11 @@ CreateGxsForumMsg::CreateGxsForumMsg(const RsGxsGroupId &fId, const RsGxsMessage
newMsg();
ui.hashGroupBox->hide();
ui->hashGroupBox->hide();
#ifndef ENABLE_GENERATE
ui.generateCheckBox->hide();
ui.generateSpinBox->hide();
ui->generateCheckBox->hide();
ui->generateSpinBox->hide();
#endif
processSettings(true);
}
@ -130,6 +132,9 @@ CreateGxsForumMsg::CreateGxsForumMsg(const RsGxsGroupId &fId, const RsGxsMessage
CreateGxsForumMsg::~CreateGxsForumMsg()
{
processSettings(false);
delete ui;
}
void CreateGxsForumMsg::processSettings(bool load)
@ -142,14 +147,14 @@ void CreateGxsForumMsg::processSettings(bool load)
RsGxsId gxs_id(Settings->value("IDChooser", QString::fromStdString(RsGxsId().toStdString())).toString().toStdString());
if(!gxs_id.isNull() && rsIdentity->isOwnId(gxs_id))
ui.idChooser->setChosenId(gxs_id);
ui->idChooser->setChosenId(gxs_id);
}
else
{
// state of ID Chooser combobox
RsGxsId id;
if(ui.idChooser->getChosenId(id))
if(ui->idChooser->getChosenId(id))
Settings->setValue("IDChooser", QString::fromStdString(id.toStdString()));
}
@ -171,11 +176,11 @@ void CreateGxsForumMsg::newMsg()
std::set<RsGxsId> id_set ;
id_set.insert(mPosterId) ;
ui.idChooser->loadIds(IDCHOOSER_ID_REQUIRED | IDCHOOSER_NO_CREATE, mPosterId);
ui.idChooser->setIdConstraintSet(id_set);
ui->idChooser->loadIds(IDCHOOSER_ID_REQUIRED | IDCHOOSER_NO_CREATE, mPosterId);
ui->idChooser->setIdConstraintSet(id_set);
}
else
ui.idChooser->loadIds(IDCHOOSER_ID_REQUIRED, mPosterId);
ui->idChooser->loadIds(IDCHOOSER_ID_REQUIRED, mPosterId);
if (mForumId.isNull()) {
mStateHelper->setActive(CREATEGXSFORUMMSG_FORUMINFO, false);
@ -185,7 +190,7 @@ void CreateGxsForumMsg::newMsg()
mStateHelper->clear(CREATEGXSFORUMMSG_FORUMINFO);
mStateHelper->clear(CREATEGXSFORUMMSG_PARENTMSG);
mStateHelper->clear(CREATEGXSFORUMMSG_ORIGMSG);
ui.forumName->setText(tr("No Forum"));
ui->forumName->setText(tr("No Forum"));
return;
}
@ -306,7 +311,7 @@ void CreateGxsForumMsg::loadFormInformation()
if(!mPosterId.isNull())
fl |= IDCHOOSER_NO_CREATE;
ui.idChooser->setFlags(fl) ;
ui->idChooser->setFlags(fl) ;
QString name = QString::fromUtf8(mForumMeta.mGroupName.c_str());
QString subj;
@ -327,19 +332,19 @@ void CreateGxsForumMsg::loadFormInformation()
subj = "Re: " + title;
}
ui.forumName->setText(misc::removeNewLine(name));
ui->forumName->setText(misc::removeNewLine(name));
std::cerr << "Setting name to \"" << misc::removeNewLine(name).toStdString() << std::endl;
if(!subj.isNull())
ui.forumSubject->setText(misc::removeNewLine(subj));
ui->forumSubject->setText(misc::removeNewLine(subj));
if (ui.forumSubject->text().isEmpty())
if (ui->forumSubject->text().isEmpty())
{
ui.forumSubject->setFocus();
ui.forumSubject->setPlaceholderText(tr ("Title"));
ui->forumSubject->setFocus();
ui->forumSubject->setPlaceholderText(tr ("Title"));
}
else
ui.forumMessage->setFocus();
ui->forumMessage->setFocus();
#ifdef TOGXS
if (mForumMeta.mGroupFlags & RS_DISTRIB_AUTHEN_REQ)
@ -347,18 +352,18 @@ void CreateGxsForumMsg::loadFormInformation()
if (1)
#endif
{
ui.signBox->setChecked(true);
ui.signBox->setEnabled(false);
ui.signBox->hide();
ui->signBox->setChecked(true);
ui->signBox->setEnabled(false);
ui->signBox->hide();
}
else
{
/* Uncheck sign box by default for anonymous forums */
ui.signBox->setChecked(false);
ui.signBox->setEnabled(true);
ui->signBox->setChecked(false);
ui->signBox->setEnabled(true);
}
//ui.forumMessage->setText("");
//ui->forumMessage->setText("");
}
static const uint32_t MAX_ALLOWED_GXS_MESSAGE_SIZE = 199000;
@ -366,27 +371,27 @@ static const uint32_t MAX_ALLOWED_GXS_MESSAGE_SIZE = 199000;
void CreateGxsForumMsg::checkLength()
{
QString text;
RsHtml::optimizeHtml(ui.forumMessage, text);
RsHtml::optimizeHtml(ui->forumMessage, text);
std::wstring msg = text.toStdWString();
int charRemains = MAX_ALLOWED_GXS_MESSAGE_SIZE - msg.length();
if(charRemains >= 0) {
text = tr("It remains %1 characters after HTML conversion.").arg(charRemains);
ui.info_Label->setStyleSheet("QLabel#info_Label { }");
ui->info_Label->setStyleSheet("QLabel#info_Label { }");
}else{
text = tr("Warning: This message is too big of %1 characters after HTML conversion.").arg((0-charRemains));
ui.info_Label->setStyleSheet("QLabel#info_Label {color: red; font: bold; }");
ui->info_Label->setStyleSheet("QLabel#info_Label {color: red; font: bold; }");
}
ui.postButton->setToolTip(text);
ui.postButton->setEnabled(charRemains>=0);
ui.info_Label->setText(text);
ui->postButton->setToolTip(text);
ui->postButton->setEnabled(charRemains>=0);
ui->info_Label->setText(text);
}
void CreateGxsForumMsg::createMsg()
{
QString name = misc::removeNewLine(ui.forumSubject->text());
QString name = misc::removeNewLine(ui->forumSubject->text());
QString desc;
RsHtml::optimizeHtml(ui.forumMessage, desc);
RsHtml::optimizeHtml(ui->forumMessage, desc);
if(name.isEmpty() | desc.isEmpty()) {
/* error message */
@ -415,9 +420,9 @@ void CreateGxsForumMsg::createMsg()
if ((msg.mMsg == "") && (msg.mMeta.mMsgName == ""))
return; /* do nothing */
if (ui.signBox->isChecked()) {
if (ui->signBox->isChecked()) {
RsGxsId authorId;
switch (ui.idChooser->getChosenId(authorId)) {
switch (ui->idChooser->getChosenId(authorId)) {
case GxsIdChooser::KnowId:
case GxsIdChooser::UnKnowId:
msg.mMeta.mAuthorId = authorId;
@ -447,23 +452,23 @@ void CreateGxsForumMsg::createMsg()
QMessageBox::warning(this, tr("RetroShare"),tr("Congrats, you found a bug!")+" "+QString(__FILE__)+":"+QString(__LINE__), QMessageBox::Ok, QMessageBox::Ok);
return;
}//switch (ui.idChooser->getChosenId(authorId))
}//switch (ui->idChooser->getChosenId(authorId))
} else {
//std::cerr << "CreateGxsForumMsg::createMsg() No Signature (for now :)";
//std::cerr << std::endl;
QMessageBox::warning(this, tr("RetroShare"),tr("Please choose Signing Id, it is required"), QMessageBox::Ok, QMessageBox::Ok);
return;
}//if (ui.signBox->isChecked())
}//if (ui->signBox->isChecked())
int generateCount = 0;
#ifdef ENABLE_GENERATE
if (ui.generateCheckBox->isChecked()) {
generateCount = ui.generateSpinBox->value();
if (ui->generateCheckBox->isChecked()) {
generateCount = ui->generateSpinBox->value();
if (QMessageBox::question(this, tr("Generate mass data"), tr("Do you really want to generate %1 messages ?").arg(generateCount), QMessageBox::Yes|QMessageBox::No, QMessageBox::No) == QMessageBox::No) {
return;
}//if (QMessageBox::question(this,
}//if (ui.generateCheckBox->isChecked())
}//if (ui->generateCheckBox->isChecked())
#endif
uint32_t token;
@ -490,7 +495,7 @@ void CreateGxsForumMsg::closeEvent (QCloseEvent * /*event*/)
void CreateGxsForumMsg::reject()
{
if (ui.forumMessage->document()->isModified()) {
if (ui->forumMessage->document()->isModified()) {
QMessageBox::StandardButton ret;
ret = QMessageBox::warning(this, tr("Cancel Forum Message"),
tr("Forum Message has not been sent yet!\n"
@ -511,7 +516,7 @@ void CreateGxsForumMsg::reject()
void CreateGxsForumMsg::smileyWidgetForums()
{
Emoticons::showSmileyWidget(this, ui.emoticonButton, SLOT(addSmileys()), false);
Emoticons::showSmileyWidget(this, ui->emoticonButton, SLOT(addSmileys()), false);
}
void CreateGxsForumMsg::addSmileys()
@ -520,17 +525,17 @@ void CreateGxsForumMsg::addSmileys()
// add trailing space
smiley += QString(" ");
// add preceding space when needed (not at start of text or preceding space already exists)
if(!ui.forumMessage->textCursor().atStart() && ui.forumMessage->toPlainText()[ui.forumMessage->textCursor().position() - 1] != QChar(' '))
if(!ui->forumMessage->textCursor().atStart() && ui->forumMessage->toPlainText()[ui->forumMessage->textCursor().position() - 1] != QChar(' '))
smiley = QString(" ") + smiley;
ui.forumMessage->textCursor().insertText(smiley);
ui->forumMessage->textCursor().insertText(smiley);
}
void CreateGxsForumMsg::addFile()
{
QStringList files;
if (misc::getOpenFileNames(this, RshareSettings::LASTDIR_EXTRAFILE, tr("Add Extra File"), "", files)) {
ui.hashBox->addAttachments(files,RS_FILE_REQ_ANONYMOUS_ROUTING);
ui.hashGroupBox->show();
ui->hashBox->addAttachments(files,RS_FILE_REQ_ANONYMOUS_ROUTING);
ui->hashGroupBox->show();
}
}
@ -541,7 +546,7 @@ void CreateGxsForumMsg::addPicture()
QString encodedImage;
if (RsHtml::makeEmbeddedImage(file, encodedImage, 640*480, MAX_ALLOWED_GXS_MESSAGE_SIZE - 200)) {
QTextDocumentFragment fragment = QTextDocumentFragment::fromHtml(encodedImage);
ui.forumMessage->textCursor().insertFragment(fragment);
ui->forumMessage->textCursor().insertFragment(fragment);
}
}
}
@ -563,11 +568,11 @@ void CreateGxsForumMsg::fileHashingFinished(QList<HashedFile> hashedFiles)
}
if (!mesgString.isEmpty()) {
ui.forumMessage->textCursor().insertHtml(mesgString);
ui->forumMessage->textCursor().insertHtml(mesgString);
}
ui.forumMessage->setFocus( Qt::OtherFocusReason );
ui.hashGroupBox->hide();
ui->forumMessage->setFocus( Qt::OtherFocusReason );
ui->hashGroupBox->hide();
}
void CreateGxsForumMsg::loadCircleInfo(const RsGxsGroupId& circle_id)
@ -603,11 +608,11 @@ void CreateGxsForumMsg::loadCircleInfo(const RsGxsGroupId& circle_id)
//for(std::set<RsGxsId>::const_iterator it(cg.mInvitedMembers.begin());it!=cg.mInvitedMembers.end();++it)
// std::cerr << " added constraint to circle element " << *it << std::endl;
ui.idChooser->setIdConstraintSet(cg.mInvitedMembers) ;
ui.idChooser->setFlags(IDCHOOSER_NO_CREATE | ui.idChooser->flags()) ; // since there's a circle involved, no ID creation can be needed
ui->idChooser->setIdConstraintSet(cg.mInvitedMembers) ;
ui->idChooser->setFlags(IDCHOOSER_NO_CREATE | ui->idChooser->flags()) ; // since there's a circle involved, no ID creation can be needed
RsGxsId tmpid ;
if(ui.idChooser->countEnabledEntries() == 0)
if(ui->idChooser->countEnabledEntries() == 0)
{
QMessageBox::information(NULL,tr("No compatible ID for this forum"),tr("None of your identities is allowed to post in this forum. This could be due to the forum being limited to a circle that contains none of your identities, or forum flags requiring a PGP-signed identity.")) ;
close() ;
@ -618,10 +623,10 @@ void CreateGxsForumMsg::loadCircleInfo(const RsGxsGroupId& circle_id)
void CreateGxsForumMsg::setSubject(const QString& msg)
{
ui.forumSubject->setText(msg);
ui->forumSubject->setText(msg);
}
void CreateGxsForumMsg::insertPastedText(const QString& msg)
{
ui.forumMessage->append(msg);
ui->forumMessage->append(msg);
}

View File

@ -21,10 +21,15 @@
#ifndef _CREATE_GXSFORUM_MSG_DIALOG_H
#define _CREATE_GXSFORUM_MSG_DIALOG_H
#include "ui_CreateGxsForumMsg.h"
#include <QDialog>
#include <retroshare/rsgxsforums.h>
#include <retroshare/rsgxscircles.h>
#include "gui/common/HashBox.h"
namespace Ui {
class CreateGxsForumMsg;
}
class UIStateHelper;
@ -80,7 +85,7 @@ private:
UIStateHelper *mStateHelper;
/** Qt Designer generated object */
Ui::CreateGxsForumMsg ui;
Ui::CreateGxsForumMsg *ui;
};
#endif

View File

@ -1278,8 +1278,8 @@ void RsGxsForumModel::recursSetMsgReadStatus(ForumModelIndex i,bool read_status,
void *ref ;
convertTabEntryToRefPointer(i,ref); // we dont use i+1 here because i is not a row, but an index in the mPosts tab
QModelIndex itemIndex = createIndex(i - 1, 0, ref);
emit dataChanged(itemIndex, itemIndex);
QModelIndex itemIndex = (mTreeMode == TREE_MODE_FLAT)?createIndex(i - 1, 0, ref):createIndex(mPosts[i].prow,0,ref);
emit dataChanged(itemIndex, itemIndex);
}
if(!with_children)

View File

@ -276,7 +276,6 @@ GxsForumThreadWidget::GxsForumThreadWidget(const RsGxsGroupId &forumId, QWidget
connect(ui->versions_CB, SIGNAL(currentIndexChanged(int)), this, SLOT(changedVersion()));
connect(ui->threadTreeWidget, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(threadListCustomPopupMenu(QPoint)));
connect(ui->postText, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(contextMenuTextBrowser(QPoint)));
connect(ui->forumName, SIGNAL(clicked(QPoint)), this, SLOT(showForumInfo()));
ui->subscribeToolButton->hide() ;
@ -303,8 +302,6 @@ 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()));
connect(ui->threadedView_TB, SIGNAL(toggled(bool)), this, SLOT(toggleThreadedView(bool)));
connect(ui->flatView_TB, SIGNAL(toggled(bool)), this, SLOT(toggleFlatView(bool)));
connect(ui->latestPostInThreadView_TB, SIGNAL(toggled(bool)), this, SLOT(toggleLstPostInThreadView(bool)));
@ -793,25 +790,6 @@ 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();
if(ui->postText->checkImage(point))
{
ui->actionSave_image->setData(point);
contextMnu->addAction(ui->actionSave_image);
}
contextMnu->exec(ui->postText->viewport()->mapToGlobal(point));
delete(contextMnu);
}
void GxsForumThreadWidget::headerContextMenuRequested(const QPoint &pos)
{
QMenu* header_context_menu = new QMenu(tr("Show column"), this);
@ -1823,13 +1801,6 @@ 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::toggleThreadedView(bool b) { if(b) changedViewBox(VIEW_THREADED); }
void GxsForumThreadWidget::toggleFlatView(bool b) { if(b) changedViewBox(VIEW_FLAT); }
void GxsForumThreadWidget::toggleLstPostInThreadView(bool b) { if(b) changedViewBox(VIEW_LAST_POST); }

View File

@ -108,7 +108,6 @@ protected:
private slots:
/** Create the context popup menu and it's submenus */
void threadListCustomPopupMenu(QPoint point);
void contextMenuTextBrowser(QPoint point);
void headerContextMenuRequested(const QPoint& pos);
void showForumInfo();
@ -134,8 +133,6 @@ private slots:
// This method is used to perform an asynchroneous action on the message data. Any of the methods above can be used as parameter.
void async_msg_action(const MsgMethod& method);
void saveImage();
void markMsgAsRead();
void markMsgAsReadChildren();
void markMsgAsUnread();

View File

@ -568,9 +568,6 @@
<family>MS Sans Serif</family>
</font>
</property>
<property name="contextMenuPolicy">
<enum>Qt::CustomContextMenu</enum>
</property>
</widget>
</item>
</layout>
@ -578,15 +575,6 @@
</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>

View File

@ -208,6 +208,7 @@
<file>icons/svg/newsfeed.svg</file>
<file>icons/svg/options.svg</file>
<file>icons/svg/paste.svg</file>
<file>icons/svg/paste_image.svg</file>
<file>icons/svg/people-notify.svg</file>
<file>icons/svg/people.svg</file>
<file>icons/svg/person.svg</file>

View File

@ -0,0 +1,89 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
width="800px"
height="800px"
viewBox="0 0 24 24"
version="1.1"
id="svg9"
sodipodi:docname="paste_image.svg"
inkscape:version="1.1.1 (3bf5ae0d25, 2021-09-20)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/">
<defs
id="defs13" />
<sodipodi:namedview
id="namedview11"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
showgrid="false"
inkscape:zoom="0.65"
inkscape:cx="400.76923"
inkscape:cy="400.76923"
inkscape:window-width="1366"
inkscape:window-height="705"
inkscape:window-x="-8"
inkscape:window-y="-8"
inkscape:window-maximized="1"
inkscape:current-layer="svg9" />
<g
id=""
stroke="none"
stroke-width="1"
fill="none"
fill-rule="evenodd"
style="fill:#039bd5;fill-opacity:1"
transform="matrix(1.0392218,0,0,1.0392218,-1.9428799,-0.49382824)">
<g
id=""
fill="#212121"
fill-rule="nonzero"
style="fill:#039bd5;fill-opacity:1">
<path
id="🎨-Color"
style="fill:#039bd5;fill-opacity:1;stroke-width:34.6407"
transform="matrix(0.02886775,0,0,0.02886775,1.4076686,0.47519042)"
d="m 306.18945,52.820312 c -40.0879,10e-7 -73.09816,30.310784 -77.36133,69.263668 l -61.08593,0.0176 -5.33594,0.17969 c -40.5569,2.74231 -72.605469,36.50942 -72.605469,77.76172 v 467.80859 l 0.179688,5.33594 c 2.742299,40.55688 36.509321,72.60547 77.761721,72.60547 h 52.125 l 3.52539,-0.23633 c 12.68112,-1.72033 22.45508,-12.58924 22.45508,-25.74219 0,-14.34857 -11.63181,-25.98242 -25.98047,-25.98242 h -52.125 l -3.52539,-0.23633 C 151.53568,691.8754 141.76172,681.00453 141.76172,667.85158 V 200.04297 l 0.23633,-3.52539 c 1.72034,-12.68112 12.5912,-22.45508 25.74414,-22.45508 l 73.84375,-0.006 c 13.97689,20.75897 37.69587,34.41602 64.60351,34.41602 h 121.47461 c 26.90774,0 50.62476,-13.65705 64.60156,-34.41602 l 73.84376,0.006 3.52539,0.23828 c 12.681,1.72034 22.45507,12.58925 22.45507,25.74219 v 51.87305 l 0.23828,3.52539 c 1.72033,12.68102 12.5912,22.45507 25.74415,22.45507 14.34857,0 25.98046,-11.6319 25.98046,-25.98046 v -51.87305 l -0.18164,-5.33594 c -2.74229,-40.5569 -36.50942,-72.60547 -77.76171,-72.60547 l -61.08594,-0.0176 C 500.76027,83.131296 467.75208,52.820313 427.66406,52.820312 Z m 0,51.960938 h 121.47461 c 14.28463,0 25.86328,11.58073 25.86328,25.86523 0,14.28461 -11.57865,25.86329 -25.86328,25.86329 H 306.18945 c -14.2845,0 -25.86523,-11.57868 -25.86523,-25.86329 0,-14.2845 11.58073,-25.86523 25.86523,-25.86523 z m 104.03711,225.16602 c -0.33514,0 -0.66945,0.003 -1.0039,0.006 0.0913,0.10704 0.17843,0.24832 0.26953,0.35743 0.6531,-0.11675 1.30303,-0.25154 1.95703,-0.36328 z m 171.90039,104.82031 c 0.14437,0.15262 0.28921,0.30435 0.4336,0.45703 -0.005,-0.10958 -0.0139,-0.21866 -0.0195,-0.32813 -0.14235,-0.0328 -0.27106,-0.0979 -0.41407,-0.1289 z M 409.08203,623.94336 c -0.34384,0.18716 -0.46718,0.25312 -0.68555,0.37109 8.7e-4,0.001 8e-4,0.003 0.002,0.004 0.16412,-0.0872 0.10477,-0.0549 0.64453,-0.3418 0.0129,-0.011 0.0261,-0.0221 0.0391,-0.0332 z m 209.33789,74.14844 c -0.31273,0.21286 -0.62794,0.42235 -0.94336,0.63086 0.55318,0.008 1.09907,-0.028 1.63477,-0.14258 -0.2314,-0.16229 -0.46355,-0.32441 -0.69141,-0.48828 z" />
</g>
</g>
<g
transform="matrix(0.03347274,0,0,0.03347274,6.3883953,6.7648694)"
id="g14">
<g
id="g6">
<g
id="g4">
<path
d="M 426.667,68.267 H 51.2 C 22.923,68.267 0,91.19 0,119.467 V 358.4 c 0,28.277 22.923,51.2 51.2,51.2 h 375.467 c 28.277,0 51.2,-22.923 51.2,-51.2 V 119.467 c 0,-28.277 -22.923,-51.2 -51.2,-51.2 z m 17.066,197.734 -107.4,-107.4 c -6.664,-6.663 -17.468,-6.663 -24.132,0 L 170.667,300.134 114.466,243.933 c -6.664,-6.663 -17.468,-6.663 -24.132,0 L 34.133,300.134 V 119.467 c 0,-9.426 7.641,-17.067 17.067,-17.067 h 375.467 c 9.426,0 17.067,7.641 17.067,17.067 v 146.534 z"
data-original="#000000"
class="active-path"
data-old_color="#000000"
fill="#039bd5"
id="path2-3" />
</g>
</g>
<g
id="g12">
<g
id="g10">
<circle
cx="153.60001"
cy="187.733"
r="51.200001"
data-original="#000000"
class="active-path"
data-old_color="#000000"
fill="#039bd5"
id="circle8" />
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.8 KiB

View File

@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>1215</width>
<height>825</height>
<width>800</width>
<height>650</height>
</rect>
</property>
<property name="windowTitle">

View File

@ -96,13 +96,12 @@ void MessageUserNotify::handleEvent_main_thread(std::shared_ptr<const RsEvent> e
switch (fe->mMailStatusEventCode) {
case RsMailStatusEventCode::NEW_MESSAGE:
if (Settings->getNotifyFlags() & RS_POPUP_MSG)
for (it = fe->mChangedMsgIds.begin(); it != fe->mChangedMsgIds.end(); ++it) {
MessageInfo msgInfo;
if (rsMail->getMessage(*it, msgInfo)) {
NotifyQt::getInstance()->addToaster(RS_POPUP_MSG, msgInfo.msgId.c_str(), msgInfo.title.c_str(), msgInfo.msg.c_str() );
}
for (it = fe->mChangedMsgIds.begin(); it != fe->mChangedMsgIds.end(); ++it) {
MessageInfo msgInfo;
if (rsMail->getMessage(*it, msgInfo)) {
NotifyQt::getInstance()->addToaster(RS_POPUP_MSG, msgInfo.msgId.c_str(), msgInfo.title.c_str(), msgInfo.msg.c_str() );
}
}
break;
case RsMailStatusEventCode::MESSAGE_CHANGED:
case RsMailStatusEventCode::MESSAGE_REMOVED:

View File

@ -1173,37 +1173,73 @@ void NotifyQt::addToaster(uint notifyFlags, const std::string& id, const std::st
ToasterItem *toaster = NULL;
uint popupflags = Settings->getNotifyFlags();
switch(type)
{
case RS_POPUP_ENCRYPTED_MSG:
SoundManager::play(SOUND_MESSAGE_ARRIVED);
toaster = new ToasterItem(new MessageToaster(std::string(), tr("Unknown title"), QString("[%1]").arg(tr("Encrypted message"))));
if ((popupflags & RS_POPUP_MSG) && !_disableAllToaster)
{
toaster = new ToasterItem(new MessageToaster(std::string(), tr("Unknown title"), QString("[%1]").arg(tr("Encrypted message"))));
}
break;
case RS_POPUP_MSG:
SoundManager::play(SOUND_MESSAGE_ARRIVED);
toaster = new ToasterItem(new MessageToaster(id, QString::fromUtf8(title.c_str()), QString::fromUtf8(msg.c_str())));
if ((popupflags & RS_POPUP_MSG) && !_disableAllToaster)
{
toaster = new ToasterItem(new MessageToaster(id, QString::fromUtf8(title.c_str()), QString::fromUtf8(msg.c_str())));
}
break;
case RS_POPUP_CONNECT:
SoundManager::play(SOUND_USER_ONLINE);
toaster = new ToasterItem(new OnlineToaster(RsPeerId(id)));
if ((popupflags & RS_POPUP_CONNECT) && !_disableAllToaster)
{
toaster = new ToasterItem(new OnlineToaster(RsPeerId(id)));
}
break;
case RS_POPUP_DOWNLOAD:
SoundManager::play(SOUND_DOWNLOAD_COMPLETE);
toaster = new ToasterItem(new DownloadToaster(RsFileHash(id), QString::fromUtf8(title.c_str())));
if ((popupflags & RS_POPUP_DOWNLOAD) && !_disableAllToaster)
{
toaster = new ToasterItem(new DownloadToaster(RsFileHash(id), QString::fromUtf8(title.c_str())));
}
break;
case RS_POPUP_CHAT:
toaster = new ToasterItem(new ChatToaster(RsPeerId(id), QString::fromUtf8(msg.c_str())));
break;
if ((popupflags & RS_POPUP_CHAT) && !_disableAllToaster)
{
// TODO: fix for distant chat, look up if dstant chat uses RS_POPUP_CHAT
ChatDialog *chatDialog = ChatDialog::getChat(ChatId(RsPeerId(id)));
ChatWidget *chatWidget;
if (chatDialog && (chatWidget = chatDialog->getChatWidget()) && chatWidget->isActive()) {
// do not show when active
break;
}
toaster = new ToasterItem(new ChatToaster(RsPeerId(id), QString::fromUtf8(msg.c_str())));
}
case RS_POPUP_GROUPCHAT:
#ifdef RS_DIRECT_CHAT
toaster = new ToasterItem(new GroupChatToaster(RsPeerId(id), QString::fromUtf8(msg.c_str())));
if ((popupflags & RS_POPUP_GROUPCHAT) && !_disableAllToaster)
{
MainWindow *mainWindow = MainWindow::getInstance();
if (mainWindow && mainWindow->isActiveWindow() && !mainWindow->isMinimized()) {
if (MainWindow::getActivatePage() == MainWindow::Friends) {
if (FriendsDialog::isGroupChatActive()) {
// do not show when active
break;
}
}
}
toaster = new ToasterItem(new GroupChatToaster(RsPeerId(id), QString::fromUtf8(msg.c_str())));
}
#endif // RS_DIRECT_CHAT
break;
case RS_POPUP_CHATLOBBY:
if ((popupflags & RS_POPUP_CHATLOBBY) && !_disableAllToaster)
{
ChatId chat_id(id);
@ -1221,13 +1257,16 @@ void NotifyQt::addToaster(uint notifyFlags, const std::string& id, const std::st
break; // participant is muted
toaster = new ToasterItem(new ChatLobbyToaster(chat_id.toLobbyId(), sender, QString::fromUtf8(msg.c_str())));
}
}
break;
case RS_POPUP_CONNECT_ATTEMPT:
if ((popupflags & RS_POPUP_CONNECT_ATTEMPT) && !_disableAllToaster)
{
// id = gpgid
// title = ssl name
// msg = peer id
toaster = new ToasterItem(new FriendRequestToaster(RsPgpId(id), QString::fromUtf8(title.c_str()), RsPeerId(msg)));
}
break;
}
@ -1240,4 +1279,6 @@ void NotifyQt::addToaster(uint notifyFlags, const std::string& id, const std::st
waitingToasterList.push_back(toaster);
}
}
/* Now start the waiting toasters */
startWaitingToasters();
}

View File

@ -125,9 +125,6 @@ void ChatPage::updateFontsAndEmotes()
/** Saves the changes on this page */
void ChatPage::updateChatParams()
{
// state of distant Chat combobox
Settings->setValue("DistantChat", ui.distantChatComboBox->currentIndex());
Settings->setChatScreenFont(fontTempChat.toString());
NotifyQt::getInstance()->notifyChatFontChanged();
@ -395,9 +392,23 @@ ChatPage::load()
whileBlocking(ui.minimumContrast)->setValue(Settings->value("MinimumContrast", 4.5).toDouble());
Settings->endGroup();
// state of distant Chat combobox
int index = Settings->value("DistantChat", 0).toInt();
whileBlocking(ui.distantChatComboBox)->setCurrentIndex(index);
// state of distant Chat combobox
switch(rsMsgs->getDistantChatPermissionFlags())
{
default:
case RS_DISTANT_CHAT_CONTACT_PERMISSION_FLAG_FILTER_NONE:
whileBlocking(ui.distantChatComboBox)->setCurrentIndex(0);
break ;
case RS_DISTANT_CHAT_CONTACT_PERMISSION_FLAG_FILTER_NON_CONTACTS:
whileBlocking(ui.distantChatComboBox)->setCurrentIndex(1);
break ;
case RS_DISTANT_CHAT_CONTACT_PERMISSION_FLAG_FILTER_EVERYBODY:
whileBlocking(ui.distantChatComboBox)->setCurrentIndex(2);
break ;
}
fontTempChat.fromString(Settings->getChatScreenFont());

View File

@ -19289,7 +19289,12 @@ p, li { white-space: pre-wrap; }
<translation type="unfinished"></translation>
</message>
<message>
<location line="+17"/>
<location line="+4"/>
<source>Save image</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+19"/>
<source>Document source</source>
<translation type="unfinished"></translation>
</message>

View File

@ -23,8 +23,12 @@
#include "util/rstime.h"
#include <QApplication>
#include <QWidget>
#include <QTextEdit>
#include <QByteArray>
#include <QImage>
#include <QClipboard>
#include <QMimeData>
#include <QMessageBox>
#include <QString>
#include <QTextCursor>
@ -37,6 +41,64 @@
ImageUtil::ImageUtil() {}
bool ImageUtil::checkImage(const QTextEdit *edit, const QPoint &pos, QRect *cursorRectStartOut, QRect *cursorRectLeftOut, QRect *cursorRectRightOut, QRect *cursorRectEndOut)
{
QString imageStr;
return checkImage(edit, pos, imageStr, cursorRectStartOut, cursorRectLeftOut, cursorRectRightOut, cursorRectEndOut);
}
bool ImageUtil::checkImage(const QTextEdit *edit, const QPoint &pos, QString &imageStr, QRect *cursorRectStartOut, QRect *cursorRectLeftOut, QRect *cursorRectRightOut, QRect *cursorRectEndOut)
{
//Get text cursor under pos. But if pos is under text browser end line this return last cursor.
QTextCursor cursor = edit->cursorForPosition(pos);
//First get rect of cursor (could be at left or right of image)
QRect cursorRectStart = edit->cursorRect(cursor);
//Second get text
cursor.movePosition(QTextCursor::Left, QTextCursor::MoveAnchor, 1);//To get character just before
QRect cursorRectLeft = edit->cursorRect(cursor);
cursor.movePosition(QTextCursor::Right, QTextCursor::KeepAnchor, 2);
QRect cursorRectRight = edit->cursorRect(cursor);
imageStr = cursor.selection().toHtml();
if (cursorRectStartOut) {
*cursorRectStartOut = cursorRectStart;
}
if (cursorRectLeftOut) {
*cursorRectLeftOut = cursorRectLeft;
}
if (cursorRectRightOut) {
*cursorRectRightOut = cursorRectRight;
}
QRect cursorRectEnd = cursorRectStart;
//Finally set left with right of precedent character.
if (cursorRectEnd.top() < cursorRectLeft.bottom())
{
cursorRectEnd.setLeft(cursorRectLeft.right());
} else {
//Image on new line
cursorRectEnd.setLeft(0);
}
//And set Right with left of next character.
if (cursorRectEnd.bottom() > cursorRectRight.top())
{
cursorRectEnd.setRight(cursorRectRight.left());
} else {
//New line after Image.
}
if (cursorRectEndOut) {
*cursorRectEndOut = cursorRectEnd;
}
//If pos is on text rect
if (cursorRectEnd.contains(pos))
{
return imageStr.indexOf("base64,") != -1;
}
return false;
}
void ImageUtil::extractImage(QWidget *window, QTextCursor cursor, QString file)
{
cursor.movePosition(QTextCursor::Left, QTextCursor::MoveAnchor, 1);
@ -68,6 +130,34 @@ void ImageUtil::extractImage(QWidget *window, QTextCursor cursor, QString file)
}
}
void ImageUtil::copyImage(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())
{
success = true;
QClipboard *clipboard = QApplication::clipboard();
QMimeData *data = new QMimeData;
data->setImageData(image);
clipboard->setMimeData(data, QClipboard::Clipboard);
}
}
if(!success)
{
QMessageBox::warning(window, QApplication::translate("ImageUtil", "Copy image"), QApplication::translate("ImageUtil", "Not an image"));
}
}
bool ImageUtil::optimizeSizeBytes(QByteArray &bytearray, const QImage &original, QImage &optimized, const char *format, int maxPixels, int maxBytes)
{
//nothing to do if it fits into the limits

View File

@ -22,16 +22,21 @@
#define IMAGEUTIL_H
#include <QTextCursor>
#include <QWidget>
#include <QByteArray>
#include <qiterator.h>
class QWidget;
class QTextEdit;
class QByteArray;
class ImageUtil
{
public:
ImageUtil();
static bool checkImage(const QTextEdit *edit, const QPoint &pos, QRect *cursorRectStartOut = NULL, QRect *cursorRectLeftOut = NULL, QRect *cursorRectRightOut = NULL, QRect *cursorRectEndOut = NULL);
static bool checkImage(const QTextEdit *edit, const QPoint &pos, QString &imageStr, QRect *cursorRectStartOut = NULL, QRect *cursorRectLeftOut = NULL, QRect *cursorRectRightOut = NULL, QRect *cursorRectEndOut = NULL);
static void extractImage(QWidget *window, QTextCursor cursor, QString file = "");
static void copyImage(QWidget *window, QTextCursor cursor);
static bool optimizeSizeHtml(QString &html, const QImage& original, QImage &optimized, int maxPixels = -1, int maxBytes = -1);
static bool optimizeSizeBytes(QByteArray &bytearray, const QImage &original, QImage &optimized, const char *format, int maxPixels, int maxBytes);
static bool hasAlphaContent(const QImage& image);