MdEditor: insert link or content when dropping file in editor

This commit is contained in:
Le Tan 2018-09-12 20:35:15 +08:00
parent a055d6e935
commit 4d223d0397
7 changed files with 261 additions and 128 deletions

View File

@ -119,8 +119,12 @@ void VInsertLinkDialog::fetchLinkFromClipboard()
QUrl url = QUrl::fromUserInput(text); QUrl url = QUrl::fromUserInput(text);
if (url.isValid()) { if (url.isValid()) {
if (m_linkUrlEdit->text().isEmpty()) { if (m_linkUrlEdit->text().isEmpty()) {
if (url.isLocalFile()) {
m_linkUrlEdit->setText(url.toString(QUrl::EncodeSpaces));
} else {
m_linkUrlEdit->setText(text); m_linkUrlEdit->setText(text);
} }
}
} else if (m_linkTextEdit->text().isEmpty()) { } else if (m_linkTextEdit->text().isEmpty()) {
m_linkTextEdit->setText(text); m_linkTextEdit->setText(text);
} }

View File

@ -49,8 +49,10 @@ const QString VUtils::c_imageLinkRegExp = QString("\\!\\[([^\\]]*)\\]\\(\\s*([^\
const QString VUtils::c_imageTitleRegExp = QString("[^\\[\\]]*"); const QString VUtils::c_imageTitleRegExp = QString("[^\\[\\]]*");
const QString VUtils::c_linkRegExp = QString("\\[([^\\]]*)\\]\\(\\s*([^\\)\"'\\s]+)\\s*" const QString VUtils::c_linkRegExp = QString("\\[([^\\]]*)\\]"
"((\"[^\"\\)\\n]*\")|('[^'\\)\\n]*'))?\\s*" "\\(\\s*(\\S+)"
"(?:\\s+((\"[^\"\\n]*\")"
"|('[^'\\n]*')))?\\s*"
"\\)"); "\\)");
const QString VUtils::c_fileNameRegExp = QString("(?:[^\\\\/:\\*\\?\"<>\\|\\s]| )*"); const QString VUtils::c_fileNameRegExp = QString("(?:[^\\\\/:\\*\\?\"<>\\|\\s]| )*");

View File

@ -30,7 +30,7 @@ VEditOperations::VEditOperations(VEditor *p_editor, VFile *p_file)
} }
} }
void VEditOperations::insertTextAtCurPos(const QString &p_text) void VEditOperations::insertText(const QString &p_text)
{ {
m_editor->insertPlainTextW(p_text); m_editor->insertPlainTextW(p_text);
} }

View File

@ -30,6 +30,8 @@ public:
virtual bool insertLink(const QString &p_linkText, virtual bool insertLink(const QString &p_linkText,
const QString &p_linkUrl) = 0; const QString &p_linkUrl) = 0;
virtual void insertText(const QString &p_text);
// Return true if @p_event has been handled and no need to be further // Return true if @p_event has been handled and no need to be further
// processed. // processed.
virtual bool handleKeyPressEvent(QKeyEvent *p_event) = 0; virtual bool handleKeyPressEvent(QKeyEvent *p_event) = 0;
@ -68,8 +70,6 @@ private:
void updateCursorLineBg(); void updateCursorLineBg();
protected: protected:
void insertTextAtCurPos(const QString &p_text);
VEditor *m_editor; VEditor *m_editor;
QPointer<VFile> m_file; QPointer<VFile> m_file;
VEditConfig *m_editConfig; VEditConfig *m_editConfig;

View File

@ -92,7 +92,7 @@ void VMdEditOperations::insertImageFromQImage(const QString &title,
QString url = QString("%1/%2").arg(folderInLink).arg(fileName); QString url = QString("%1/%2").arg(folderInLink).arg(fileName);
QString md = QString("![%1](%2)").arg(title).arg(url); QString md = QString("![%1](%2)").arg(title).arg(url);
insertTextAtCurPos(md); insertText(md);
qDebug() << "insert image" << title << filePath; qDebug() << "insert image" << title << filePath;
@ -133,7 +133,7 @@ void VMdEditOperations::insertImageFromPath(const QString &title, const QString
QString url = QString("%1/%2").arg(folderInLink).arg(fileName); QString url = QString("%1/%2").arg(folderInLink).arg(fileName);
QString md = QString("![%1](%2)").arg(title).arg(url); QString md = QString("![%1](%2)").arg(title).arg(url);
insertTextAtCurPos(md); insertText(md);
qDebug() << "insert image" << title << filePath; qDebug() << "insert image" << title << filePath;
@ -540,7 +540,7 @@ bool VMdEditOperations::handleKeyTab(QKeyEvent *p_event)
m_autoIndentPos = m_editor->textCursorW().position(); m_autoIndentPos = m_editor->textCursorW().position();
} else { } else {
// Just insert "tab". // Just insert "tab".
insertTextAtCurPos(text); insertText(text);
m_autoIndentPos = -1; m_autoIndentPos = -1;
} }
} }
@ -1128,9 +1128,7 @@ bool VMdEditOperations::insertLink(const QString &p_linkText,
const QString &p_linkUrl) const QString &p_linkUrl)
{ {
QString link = QString("[%1](%2)").arg(p_linkText).arg(p_linkUrl); QString link = QString("[%1](%2)").arg(p_linkText).arg(p_linkUrl);
QTextCursor cursor = m_editor->textCursorW(); insertText(link);
cursor.insertText(link);
m_editor->setTextCursorW(cursor);
setVimMode(VimMode::Insert); setVimMode(VimMode::Insert);

View File

@ -5,6 +5,7 @@
#include <QDebug> #include <QDebug>
#include <QScopedPointer> #include <QScopedPointer>
#include <QClipboard> #include <QClipboard>
#include <QMimeDatabase>
#include "vdocument.h" #include "vdocument.h"
#include "utils/veditutils.h" #include "utils/veditutils.h"
@ -850,125 +851,20 @@ bool VMdEditor::canInsertFromMimeData(const QMimeData *p_source) const
void VMdEditor::insertFromMimeData(const QMimeData *p_source) void VMdEditor::insertFromMimeData(const QMimeData *p_source)
{ {
if (p_source->hasHtml()) { if (processHtmlFromMimeData(p_source)) {
// Handle <img>.
QRegExp reg("<img ([^>]*)src=\"([^\"]+)\"([^>]*)>");
QString html(p_source->html());
if (reg.indexIn(html) != -1 && VUtils::onlyHasImgInHtml(html)) {
if (p_source->hasImage()) {
// Both image data and URL are embedded.
VSelectDialog dialog(tr("Insert From Clipboard"), this);
dialog.addSelection(tr("Insert From URL"), 0);
dialog.addSelection(tr("Insert From Image Data"), 1);
dialog.addSelection(tr("Insert As Image Link"), 2);
if (dialog.exec() == QDialog::Accepted) {
int selection = dialog.getSelection();
if (selection == 1) {
// Insert from image data.
m_editOps->insertImageFromMimeData(p_source);
return;
} else if (selection == 2) {
// Insert as link.
insertImageLink("", reg.cap(2));
return;
}
} else {
return;
}
}
m_editOps->insertImageFromURL(QUrl(reg.cap(2)));
return;
}
}
VSelectDialog dialog(tr("Insert From Clipboard"), this);
dialog.addSelection(tr("Insert As Image"), 0);
dialog.addSelection(tr("Insert As Text"), 1);
dialog.addSelection(tr("Insert As Image Link"), 2);
if (p_source->hasImage()) {
// Image data in the clipboard
if (p_source->hasText()) {
if (dialog.exec() == QDialog::Accepted) {
int selection = dialog.getSelection();
if (selection == 1) {
// Insert as text.
Q_ASSERT(p_source->hasText() && p_source->hasImage());
VTextEdit::insertFromMimeData(p_source);
return;
} else if (selection == 2) {
// Insert as link.
insertImageLink("", p_source->text());
return;
}
} else {
return;
}
}
m_editOps->insertImageFromMimeData(p_source);
return; return;
} }
if (p_source->hasUrls()) { if (processImageFromMimeData(p_source)) {
QList<QUrl> urls = p_source->urls();
if (urls.size() == 1 && VUtils::isImageURL(urls[0])) {
if (dialog.exec() == QDialog::Accepted) {
// FIXME: After calling dialog.exec(), p_source->hasUrl() returns false.
int selection = dialog.getSelection();
if (selection == 0) {
// Insert as image.
m_editOps->insertImageFromURL(urls[0]);
return;
} else if (selection == 2) {
// Insert as link.
insertImageLink("", urls[0].toString(QUrl::FullyEncoded));
return; return;
} }
QMimeData newSource; if (processUrlFromMimeData(p_source)) {
newSource.setUrls(urls);
VTextEdit::insertFromMimeData(&newSource);
return; return;
} else {
return;
}
}
}
if (p_source->hasText()) {
QString text = p_source->text();
if (VUtils::isImageURLText(text)) {
// The text is a URL to an image.
if (dialog.exec() == QDialog::Accepted) {
int selection = dialog.getSelection();
if (selection == 0) {
// Insert as image.
QUrl url;
if (QFileInfo::exists(text)) {
url = QUrl::fromLocalFile(text);
} else {
url = QUrl(text);
}
if (url.isValid()) {
m_editOps->insertImageFromURL(url);
} }
if (processTextFromMimeData(p_source)) {
return; return;
} else if (selection == 2) {
// Insert as link.
insertImageLink("", text);
return;
}
} else {
return;
}
}
Q_ASSERT(p_source->hasText());
} }
VTextEdit::insertFromMimeData(p_source); VTextEdit::insertFromMimeData(p_source);
@ -1266,9 +1162,7 @@ void VMdEditor::htmlToTextFinished(int p_id, int p_timeStamp, const QString &p_t
{ {
Q_UNUSED(p_id); Q_UNUSED(p_id);
if (m_copyTimeStamp == p_timeStamp && !p_text.isEmpty()) { if (m_copyTimeStamp == p_timeStamp && !p_text.isEmpty()) {
QTextCursor cursor = textCursor(); m_editOps->insertText(p_text);
cursor.insertText(p_text);
setTextCursor(cursor);
emit m_object->statusMessage(tr("Parsed Markdown text inserted")); emit m_object->statusMessage(tr("Parsed Markdown text inserted"));
} }
} }
@ -1849,3 +1743,230 @@ void VMdEditor::parseAndPaste()
emit requestHtmlToText(html, 0, m_copyTimeStamp); emit requestHtmlToText(html, 0, m_copyTimeStamp);
} }
} }
bool VMdEditor::processHtmlFromMimeData(const QMimeData *p_source)
{
if (!p_source->hasHtml()) {
return false;
}
// Handle <img>.
QRegExp reg("<img ([^>]*)src=\"([^\"]+)\"([^>]*)>");
QString html(p_source->html());
if (reg.indexIn(html) != -1 && VUtils::onlyHasImgInHtml(html)) {
if (p_source->hasImage()) {
// Both image data and URL are embedded.
VSelectDialog dialog(tr("Insert From Clipboard"), this);
dialog.addSelection(tr("Insert From URL"), 0);
dialog.addSelection(tr("Insert From Image Data"), 1);
dialog.addSelection(tr("Insert As Image Link"), 2);
if (dialog.exec() == QDialog::Accepted) {
int selection = dialog.getSelection();
if (selection == 1) {
// Insert from image data.
m_editOps->insertImageFromMimeData(p_source);
return true;
} else if (selection == 2) {
// Insert as link.
insertImageLink("", reg.cap(2));
return true;
}
} else {
return true;
}
}
m_editOps->insertImageFromURL(QUrl(reg.cap(2)));
return true;
}
return false;
}
bool VMdEditor::processImageFromMimeData(const QMimeData *p_source)
{
if (!p_source->hasImage()) {
return false;
}
// Image data in the clipboard
if (p_source->hasText()) {
VSelectDialog dialog(tr("Insert From Clipboard"), this);
dialog.addSelection(tr("Insert As Image"), 0);
dialog.addSelection(tr("Insert As Text"), 1);
dialog.addSelection(tr("Insert As Image Link"), 2);
if (dialog.exec() == QDialog::Accepted) {
int selection = dialog.getSelection();
if (selection == 1) {
// Insert as text.
Q_ASSERT(p_source->hasText() && p_source->hasImage());
VTextEdit::insertFromMimeData(p_source);
return true;
} else if (selection == 2) {
// Insert as link.
insertImageLink("", p_source->text());
return true;
}
} else {
return true;
}
}
m_editOps->insertImageFromMimeData(p_source);
return true;
}
bool VMdEditor::processUrlFromMimeData(const QMimeData *p_source)
{
if (!p_source->hasUrls()) {
return false;
}
QList<QUrl> urls = p_source->urls();
if (urls.size() == 1) {
if (VUtils::isImageURL(urls[0])) {
VSelectDialog dialog(tr("Insert From Clipboard"), this);
dialog.addSelection(tr("Insert As Image"), 0);
dialog.addSelection(tr("Insert As Text"), 1);
dialog.addSelection(tr("Insert As Image Link"), 2);
if (dialog.exec() == QDialog::Accepted) {
// FIXME: After calling dialog.exec(), p_source->hasUrl() returns false.
int selection = dialog.getSelection();
if (selection == 0) {
// Insert as image.
m_editOps->insertImageFromURL(urls[0]);
return true;
} else if (selection == 2) {
// Insert as link.
insertImageLink("", urls[0].toString(QUrl::FullyEncoded));
return true;
}
QMimeData newSource;
newSource.setUrls(urls);
VTextEdit::insertFromMimeData(&newSource);
}
return true;
} else {
QString localTextFilePath;
if (urls[0].isLocalFile()) {
localTextFilePath = urls[0].toLocalFile();
if (!QFileInfo::exists(localTextFilePath)) {
localTextFilePath.clear();
} else {
QMimeDatabase mimeDatabase;
const QMimeType mimeType = mimeDatabase.mimeTypeForFile(localTextFilePath);
if (mimeType.isValid() && !mimeType.inherits(QStringLiteral("text/plain"))) {
localTextFilePath.clear();
}
}
}
VSelectDialog dialog(tr("Insert From Clipboard"), this);
dialog.addSelection(tr("Insert As Link"), 0);
dialog.addSelection(tr("Insert As Text"), 1);
if (!localTextFilePath.isEmpty()) {
dialog.addSelection(tr("Insert File Content"), 2);
}
if (dialog.exec() == QDialog::Accepted) {
switch (dialog.getSelection()) {
case 0:
{
QString ut = urls[0].isLocalFile() ? urls[0].toString(QUrl::EncodeSpaces)
: urls[0].toString();
VInsertLinkDialog ld(QObject::tr("Insert Link"),
"",
"",
"",
ut,
false,
this);
if (ld.exec() == QDialog::Accepted) {
QString linkText = ld.getLinkText();
QString linkUrl = ld.getLinkUrl();
Q_ASSERT(!linkText.isEmpty() && !linkUrl.isEmpty());
m_editOps->insertLink(linkText, linkUrl);
}
break;
}
case 1:
if (p_source->hasText()) {
m_editOps->insertText(p_source->text());
} else {
m_editOps->insertText(urls[0].toString());
}
break;
case 2:
{
Q_ASSERT(!localTextFilePath.isEmpty());
m_editOps->insertText(VUtils::readFileFromDisk(localTextFilePath));
break;
}
default:
Q_ASSERT(false);
break;
}
return true;
} else {
return true;
}
}
}
return false;
}
bool VMdEditor::processTextFromMimeData(const QMimeData *p_source)
{
if (!p_source->hasText()) {
return false;
}
QString text = p_source->text();
if (VUtils::isImageURLText(text)) {
// The text is a URL to an image.
VSelectDialog dialog(tr("Insert From Clipboard"), this);
dialog.addSelection(tr("Insert As Image"), 0);
dialog.addSelection(tr("Insert As Text"), 1);
dialog.addSelection(tr("Insert As Image Link"), 2);
if (dialog.exec() == QDialog::Accepted) {
int selection = dialog.getSelection();
if (selection == 0) {
// Insert as image.
QUrl url;
if (QFileInfo::exists(text)) {
url = QUrl::fromLocalFile(text);
} else {
url = QUrl(text);
}
if (url.isValid()) {
m_editOps->insertImageFromURL(url);
}
return true;
} else if (selection == 2) {
// Insert as link.
insertImageLink("", text);
return true;
}
} else {
return true;
}
}
Q_ASSERT(p_source->hasText());
return false;
}

View File

@ -305,6 +305,14 @@ private:
const QString &p_text, const QString &p_text,
const QString &p_format); const QString &p_format);
bool processHtmlFromMimeData(const QMimeData *p_source);
bool processImageFromMimeData(const QMimeData *p_source);
bool processUrlFromMimeData(const QMimeData *p_source);
bool processTextFromMimeData(const QMimeData *p_source);
PegMarkdownHighlighter *m_pegHighlighter; PegMarkdownHighlighter *m_pegHighlighter;
VCodeBlockHighlightHelper *m_cbHighlighter; VCodeBlockHighlightHelper *m_cbHighlighter;