feature: paste image in chat #51
@ -378,7 +378,32 @@ void Core::NetworkAccess::onUploadFinished()
|
|||||||
qDebug() << "upload success for" << url;
|
qDebug() << "upload success for" << url;
|
||||||
|
|
||||||
storage.addFile(upl->messages, upl->url, upl->path);
|
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();
|
upl->reply->deleteLater();
|
||||||
|
@ -58,7 +58,7 @@ public:
|
|||||||
signals:
|
signals:
|
||||||
void loadFileProgress(const std::list<Shared::MessageInfo>& msgs, qreal value, bool up);
|
void loadFileProgress(const std::list<Shared::MessageInfo>& msgs, qreal value, bool up);
|
||||||
void loadFileError(const std::list<Shared::MessageInfo>& msgs, const QString& text, bool up);
|
void loadFileError(const std::list<Shared::MessageInfo>& msgs, const QString& text, bool up);
|
||||||
void uploadFileComplete(const std::list<Shared::MessageInfo>& msgs, const QString& url);
|
void uploadFileComplete(const std::list<Shared::MessageInfo>& msgs, const QString& url, const QString& path);
|
||||||
void downloadFileComplete(const std::list<Shared::MessageInfo>& msgs, const QString& path);
|
void downloadFileComplete(const std::list<Shared::MessageInfo>& msgs, const QString& path);
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
|
@ -82,7 +82,7 @@ signals:
|
|||||||
void fileError(const std::list<Shared::MessageInfo> msgs, const QString& error, bool up);
|
void fileError(const std::list<Shared::MessageInfo> msgs, const QString& error, bool up);
|
||||||
void fileProgress(const std::list<Shared::MessageInfo> msgs, qreal value, bool up);
|
void fileProgress(const std::list<Shared::MessageInfo> msgs, qreal value, bool up);
|
||||||
void fileDownloadComplete(const std::list<Shared::MessageInfo> msgs, const QString& path);
|
void fileDownloadComplete(const std::list<Shared::MessageInfo> msgs, const QString& path);
|
||||||
void fileUploadComplete(const std::list<Shared::MessageInfo> msgs, const QString& path);
|
void fileUploadComplete(const std::list<Shared::MessageInfo> msgs, const QString& url, const QString& path);
|
||||||
|
|
||||||
void responseVCard(const QString& jid, const Shared::VCard& card);
|
void responseVCard(const QString& jid, const Shared::VCard& card);
|
||||||
void changeMessage(const QString& account, const QString& jid, const QString& id, const QMap<QString, QVariant>& data);
|
void changeMessage(const QString& account, const QString& jid, const QString& id, const QMap<QString, QVariant>& data);
|
||||||
|
@ -405,7 +405,7 @@ void Squawk::fileError(const std::list<Shared::MessageInfo> msgs, const QString&
|
|||||||
rosterModel.fileError(msgs, error, up);
|
rosterModel.fileError(msgs, error, up);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Squawk::fileUploadComplete(const std::list<Shared::MessageInfo> msgs, const QString& path)
|
void Squawk::fileUploadComplete(const std::list<Shared::MessageInfo> msgs, const QString& url, const QString& path)
|
||||||
{
|
{
|
||||||
rosterModel.fileComplete(msgs, true);
|
rosterModel.fileComplete(msgs, true);
|
||||||
}
|
}
|
||||||
|
@ -107,7 +107,7 @@ public slots:
|
|||||||
void fileError(const std::list<Shared::MessageInfo> msgs, const QString& error, bool up);
|
void fileError(const std::list<Shared::MessageInfo> msgs, const QString& error, bool up);
|
||||||
void fileProgress(const std::list<Shared::MessageInfo> msgs, qreal value, bool up);
|
void fileProgress(const std::list<Shared::MessageInfo> msgs, qreal value, bool up);
|
||||||
void fileDownloadComplete(const std::list<Shared::MessageInfo> msgs, const QString& path);
|
void fileDownloadComplete(const std::list<Shared::MessageInfo> msgs, const QString& path);
|
||||||
void fileUploadComplete(const std::list<Shared::MessageInfo> msgs, const QString& path);
|
void fileUploadComplete(const std::list<Shared::MessageInfo> msgs, const QString& url, const QString& path);
|
||||||
void responseVCard(const QString& jid, const Shared::VCard& card);
|
void responseVCard(const QString& jid, const Shared::VCard& card);
|
||||||
void changeMessage(const QString& account, const QString& jid, const QString& id, const QMap<QString, QVariant>& data);
|
void changeMessage(const QString& account, const QString& jid, const QString& id, const QMap<QString, QVariant>& data);
|
||||||
void requestPassword(const QString& account);
|
void requestPassword(const QString& account);
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
#include "ui_conversation.h"
|
#include "ui_conversation.h"
|
||||||
|
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
|
#include <QClipboard>
|
||||||
#include <QScrollBar>
|
#include <QScrollBar>
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
#include <QFileDialog>
|
#include <QFileDialog>
|
||||||
@ -27,6 +28,9 @@
|
|||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <QAbstractTextDocumentLayout>
|
#include <QAbstractTextDocumentLayout>
|
||||||
#include <QCoreApplication>
|
#include <QCoreApplication>
|
||||||
|
#include <QTemporaryFile>
|
||||||
|
#include <QDir>
|
||||||
|
#include <QMenu>
|
||||||
|
|
||||||
Conversation::Conversation(bool muc, Models::Account* acc, Models::Element* el, const QString pJid, const QString pRes, QWidget* parent):
|
Conversation::Conversation(bool muc, Models::Account* acc, Models::Element* el, const QString pJid, const QString pRes, QWidget* parent):
|
||||||
QWidget(parent),
|
QWidget(parent),
|
||||||
@ -47,6 +51,7 @@ Conversation::Conversation(bool muc, Models::Account* acc, Models::Element* el,
|
|||||||
delegate(new MessageDelegate(this)),
|
delegate(new MessageDelegate(this)),
|
||||||
manualSliderChange(false),
|
manualSliderChange(false),
|
||||||
tsb(QApplication::style()->styleHint(QStyle::SH_ScrollBar_Transient) == 1),
|
tsb(QApplication::style()->styleHint(QStyle::SH_ScrollBar_Transient) == 1),
|
||||||
|
pasteImageAction(new QAction(tr("Paste Image"), this)),
|
||||||
shadow(10, 1, Qt::black, this),
|
shadow(10, 1, Qt::black, this),
|
||||||
contextMenu(new QMenu())
|
contextMenu(new QMenu())
|
||||||
{
|
{
|
||||||
@ -75,6 +80,7 @@ Conversation::Conversation(bool muc, Models::Account* acc, Models::Element* el,
|
|||||||
statusLabel = m_ui->statusLabel;
|
statusLabel = m_ui->statusLabel;
|
||||||
|
|
||||||
connect(&ker, &KeyEnterReceiver::enterPressed, this, &Conversation::onEnterPressed);
|
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->sendButton, &QPushButton::clicked, this, &Conversation::onEnterPressed);
|
||||||
connect(m_ui->attachButton, &QPushButton::clicked, this, &Conversation::onAttach);
|
connect(m_ui->attachButton, &QPushButton::clicked, this, &Conversation::onAttach);
|
||||||
connect(m_ui->clearButton, &QPushButton::clicked, this, &Conversation::onClearButton);
|
connect(m_ui->clearButton, &QPushButton::clicked, this, &Conversation::onClearButton);
|
||||||
@ -82,7 +88,10 @@ Conversation::Conversation(bool muc, Models::Account* acc, Models::Element* el,
|
|||||||
this, &Conversation::onTextEditDocSizeChanged);
|
this, &Conversation::onTextEditDocSizeChanged);
|
||||||
|
|
||||||
m_ui->messageEditor->installEventFilter(&ker);
|
m_ui->messageEditor->installEventFilter(&ker);
|
||||||
|
m_ui->messageEditor->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||||
|
|
||||||
blue marked this conversation as resolved
|
|||||||
|
connect(m_ui->messageEditor, &QTextEdit::customContextMenuRequested, this, &Conversation::onMessageEditorContext);
|
||||||
|
connect(pasteImageAction, &QAction::triggered, this, &Conversation::onImagePasted);
|
||||||
|
|
||||||
//line->setAutoFillBackground(false);
|
//line->setAutoFillBackground(false);
|
||||||
//if (testAttribute(Qt::WA_TranslucentBackground)) {
|
//if (testAttribute(Qt::WA_TranslucentBackground)) {
|
||||||
@ -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);
|
return QObject::eventFilter(obj, event);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Conversation::checkClipboardImage() {
|
||||||
|
return !QApplication::clipboard()->image().isNull();
|
||||||
|
}
|
||||||
|
|
||||||
QString Conversation::getPalResource() const
|
QString Conversation::getPalResource() const
|
||||||
{
|
{
|
||||||
return activePalResource;
|
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()
|
void Conversation::onAttach()
|
||||||
{
|
{
|
||||||
QFileDialog* d = new QFileDialog(this, tr("Chose a file to send"));
|
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));
|
||||||
|
}
|
||||||
|
@ -60,6 +60,7 @@ protected:
|
|||||||
|
|
||||||
signals:
|
signals:
|
||||||
void enterPressed();
|
void enterPressed();
|
||||||
|
void imagePasted();
|
||||||
};
|
};
|
||||||
|
|
||||||
class Conversation : public QWidget
|
class Conversation : public QWidget
|
||||||
@ -77,6 +78,7 @@ public:
|
|||||||
void setPalResource(const QString& res);
|
void setPalResource(const QString& res);
|
||||||
virtual void setAvatar(const QString& path);
|
virtual void setAvatar(const QString& path);
|
||||||
void setFeedFrames(bool top, bool right, bool bottom, bool left);
|
void setFeedFrames(bool top, bool right, bool bottom, bool left);
|
||||||
|
static bool checkClipboardImage();
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void sendMessage(const Shared::Message& message);
|
void sendMessage(const Shared::Message& message);
|
||||||
@ -102,6 +104,7 @@ protected:
|
|||||||
|
|
||||||
protected slots:
|
protected slots:
|
||||||
void onEnterPressed();
|
void onEnterPressed();
|
||||||
|
void onImagePasted();
|
||||||
void onAttach();
|
void onAttach();
|
||||||
void onFileSelected();
|
void onFileSelected();
|
||||||
void onBadgeClose();
|
void onBadgeClose();
|
||||||
@ -111,6 +114,7 @@ protected slots:
|
|||||||
void onFeedMessage(const Shared::Message& msg);
|
void onFeedMessage(const Shared::Message& msg);
|
||||||
void positionShadow();
|
void positionShadow();
|
||||||
void onFeedContext(const QPoint &pos);
|
void onFeedContext(const QPoint &pos);
|
||||||
|
void onMessageEditorContext(const QPoint &pos);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
const bool isMuc;
|
const bool isMuc;
|
||||||
@ -133,6 +137,8 @@ protected:
|
|||||||
bool manualSliderChange;
|
bool manualSliderChange;
|
||||||
bool tsb; //transient scroll bars
|
bool tsb; //transient scroll bars
|
||||||
|
|
||||||
|
QAction* pasteImageAction;
|
||||||
|
|
||||||
ShadowOverlay shadow;
|
ShadowOverlay shadow;
|
||||||
QMenu* contextMenu;
|
QMenu* contextMenu;
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user
Do you mind if we better move it to initialization block? Cuz, since you declare here that variable again as QAction* it might be even considered like another variable rather than that one declared as a property...
How about this change?
52551c1ce0
That's good, thank you!