diff --git a/core/networkaccess.cpp b/core/networkaccess.cpp index 69fe812..c2cd65d 100644 --- a/core/networkaccess.cpp +++ b/core/networkaccess.cpp @@ -378,7 +378,32 @@ void Core::NetworkAccess::onUploadFinished() qDebug() << "upload success for" << url; storage.addFile(upl->messages, upl->url, upl->path); - emit uploadFileComplete(upl->messages, upl->url); + emit uploadFileComplete(upl->messages, upl->url, upl->path); + + // Copy file to Download folder if it is a temp file. See Conversation::onImagePasted. + if (upl->path.startsWith(QDir::tempPath() + QStringLiteral("/squawk_img_attach_")) && upl->path.endsWith(".png")) { + QString err = ""; + QString downloadDirPath = prepareDirectory(upl->messages.front().jid); + if (downloadDirPath.size() > 0) { + QString newPath = downloadDirPath + "/" + upl->path.mid(QDir::tempPath().length() + 1); + + // Copy {TEMPDIR}/squawk_img_attach_XXXXXX.png to Download folder + bool copyResult = QFile::copy(upl->path, newPath); + + if (copyResult) { + // Change storage + storage.setPath(upl->url, newPath); + } else { + err = "copying to " + newPath + " failed"; + } + } else { + err = "Couldn't prepare a directory for file"; + } + + if (err.size() != 0) { + qDebug() << "failed to copy temporary upload file " << upl->path << " to download folder:" << err; + } + } } upl->reply->deleteLater(); diff --git a/core/networkaccess.h b/core/networkaccess.h index 75c189c..89d0633 100644 --- a/core/networkaccess.h +++ b/core/networkaccess.h @@ -58,7 +58,7 @@ public: signals: void loadFileProgress(const std::list& msgs, qreal value, bool up); void loadFileError(const std::list& msgs, const QString& text, bool up); - void uploadFileComplete(const std::list& msgs, const QString& url); + void uploadFileComplete(const std::list& msgs, const QString& url, const QString& path); void downloadFileComplete(const std::list& msgs, const QString& path); public slots: diff --git a/core/squawk.h b/core/squawk.h index 338eb40..3715afe 100644 --- a/core/squawk.h +++ b/core/squawk.h @@ -82,7 +82,7 @@ signals: void fileError(const std::list msgs, const QString& error, bool up); void fileProgress(const std::list msgs, qreal value, bool up); void fileDownloadComplete(const std::list msgs, const QString& path); - void fileUploadComplete(const std::list msgs, const QString& path); + void fileUploadComplete(const std::list msgs, const QString& url, const QString& path); void responseVCard(const QString& jid, const Shared::VCard& card); void changeMessage(const QString& account, const QString& jid, const QString& id, const QMap& data); diff --git a/ui/squawk.cpp b/ui/squawk.cpp index 6a0a676..406ee45 100644 --- a/ui/squawk.cpp +++ b/ui/squawk.cpp @@ -405,7 +405,7 @@ void Squawk::fileError(const std::list msgs, const QString& rosterModel.fileError(msgs, error, up); } -void Squawk::fileUploadComplete(const std::list msgs, const QString& path) +void Squawk::fileUploadComplete(const std::list msgs, const QString& url, const QString& path) { rosterModel.fileComplete(msgs, true); } diff --git a/ui/squawk.h b/ui/squawk.h index 28389fa..cb93259 100644 --- a/ui/squawk.h +++ b/ui/squawk.h @@ -107,7 +107,7 @@ public slots: void fileError(const std::list msgs, const QString& error, bool up); void fileProgress(const std::list msgs, qreal value, bool up); void fileDownloadComplete(const std::list msgs, const QString& path); - void fileUploadComplete(const std::list msgs, const QString& path); + void fileUploadComplete(const std::list msgs, const QString& url, const QString& path); void responseVCard(const QString& jid, const Shared::VCard& card); void changeMessage(const QString& account, const QString& jid, const QString& id, const QMap& data); void requestPassword(const QString& account); diff --git a/ui/widgets/conversation.cpp b/ui/widgets/conversation.cpp index d003551..fcf28c3 100644 --- a/ui/widgets/conversation.cpp +++ b/ui/widgets/conversation.cpp @@ -20,6 +20,7 @@ #include "ui_conversation.h" #include +#include #include #include #include @@ -27,6 +28,9 @@ #include #include #include +#include +#include +#include Conversation::Conversation(bool muc, Models::Account* acc, Models::Element* el, const QString pJid, const QString pRes, QWidget* parent): QWidget(parent), @@ -47,6 +51,7 @@ Conversation::Conversation(bool muc, Models::Account* acc, Models::Element* el, delegate(new MessageDelegate(this)), manualSliderChange(false), tsb(QApplication::style()->styleHint(QStyle::SH_ScrollBar_Transient) == 1), + pasteImageAction(new QAction(tr("Paste Image"), this)), shadow(10, 1, Qt::black, this), contextMenu(new QMenu()) { @@ -75,6 +80,7 @@ Conversation::Conversation(bool muc, Models::Account* acc, Models::Element* el, statusLabel = m_ui->statusLabel; connect(&ker, &KeyEnterReceiver::enterPressed, this, &Conversation::onEnterPressed); + connect(&ker, &KeyEnterReceiver::imagePasted, this, &Conversation::onImagePasted); connect(m_ui->sendButton, &QPushButton::clicked, this, &Conversation::onEnterPressed); connect(m_ui->attachButton, &QPushButton::clicked, this, &Conversation::onAttach); connect(m_ui->clearButton, &QPushButton::clicked, this, &Conversation::onClearButton); @@ -82,8 +88,11 @@ Conversation::Conversation(bool muc, Models::Account* acc, Models::Element* el, this, &Conversation::onTextEditDocSizeChanged); m_ui->messageEditor->installEventFilter(&ker); - - + m_ui->messageEditor->setContextMenuPolicy(Qt::CustomContextMenu); + + connect(m_ui->messageEditor, &QTextEdit::customContextMenuRequested, this, &Conversation::onMessageEditorContext); + connect(pasteImageAction, &QAction::triggered, this, &Conversation::onImagePasted); + //line->setAutoFillBackground(false); //if (testAttribute(Qt::WA_TranslucentBackground)) { //m_ui->scrollArea->setAutoFillBackground(false); @@ -183,10 +192,20 @@ bool KeyEnterReceiver::eventFilter(QObject* obj, QEvent* event) } } } + if (k == Qt::Key_V && key->modifiers() & Qt::CTRL) { + if (Conversation::checkClipboardImage()) { + emit imagePasted(); + return true; + } + } } return QObject::eventFilter(obj, event); } +bool Conversation::checkClipboardImage() { + return !QApplication::clipboard()->image().isNull(); +} + QString Conversation::getPalResource() const { return activePalResource; @@ -218,6 +237,24 @@ void Conversation::onEnterPressed() } } +void Conversation::onImagePasted() +{ + QImage image = QApplication::clipboard()->image(); + if (image.isNull()) { + return; + } + QTemporaryFile *tempFile = new QTemporaryFile(QDir::tempPath() + QStringLiteral("/squawk_img_attach_XXXXXX.png"), QApplication::instance()); + tempFile->open(); + image.save(tempFile, "PNG"); + tempFile->close(); + qDebug() << "image on paste temp file: " << tempFile->fileName(); + addAttachedFile(tempFile->fileName()); + + // The file, if successfully uploaded, will be copied to Download folder. + // On application closing, this temporary file will be automatically removed by Qt. + // See Core::NetworkAccess::onUploadFinished. +} + void Conversation::onAttach() { QFileDialog* d = new QFileDialog(this, tr("Chose a file to send")); @@ -443,3 +480,14 @@ void Conversation::onFeedContext(const QPoint& pos) } } } + +void Conversation::onMessageEditorContext(const QPoint& pos) +{ + pasteImageAction->setEnabled(Conversation::checkClipboardImage()); + + QMenu *editorMenu = m_ui->messageEditor->createStandardContextMenu(); + editorMenu->addSeparator(); + editorMenu->addAction(pasteImageAction); + + editorMenu->exec(this->m_ui->messageEditor->mapToGlobal(pos)); +} diff --git a/ui/widgets/conversation.h b/ui/widgets/conversation.h index b0eb745..6b5b4bb 100644 --- a/ui/widgets/conversation.h +++ b/ui/widgets/conversation.h @@ -60,6 +60,7 @@ protected: signals: void enterPressed(); + void imagePasted(); }; class Conversation : public QWidget @@ -77,6 +78,7 @@ public: void setPalResource(const QString& res); virtual void setAvatar(const QString& path); void setFeedFrames(bool top, bool right, bool bottom, bool left); + static bool checkClipboardImage(); signals: void sendMessage(const Shared::Message& message); @@ -102,6 +104,7 @@ protected: protected slots: void onEnterPressed(); + void onImagePasted(); void onAttach(); void onFileSelected(); void onBadgeClose(); @@ -111,6 +114,7 @@ protected slots: void onFeedMessage(const Shared::Message& msg); void positionShadow(); void onFeedContext(const QPoint &pos); + void onMessageEditorContext(const QPoint &pos); public: const bool isMuc; @@ -133,6 +137,8 @@ protected: bool manualSliderChange; bool tsb; //transient scroll bars + QAction* pasteImageAction; + ShadowOverlay shadow; QMenu* contextMenu; };