bug-fix: fix images when cutting files

1. De-duplicate the images occur multiple times in the note;
2. Refresh the read mode and edit mode preview after the directory path
change;
3. Update init images and inserted images after the directory path
change;
This commit is contained in:
Le Tan 2017-12-20 20:48:16 +08:00
parent 7fd5ec26e5
commit 03122a24db
22 changed files with 223 additions and 36 deletions

View File

@ -51,7 +51,8 @@ var mdit = window.markdownit({
return hljs.highlightAuto(str).value;
}
} else {
return str;
// Use external default escaping.
return '';
}
}
});
@ -67,7 +68,7 @@ mdit = mdit.use(window.markdownitHeadingAnchor, {
toc.push({
level: getHeadingLevel(openToken.tag),
anchor: anchor,
title: escapeHtml(inlineToken.content)
title: mdit.utils.escapeHtml(inlineToken.content)
});
}
});

View File

@ -216,6 +216,9 @@ QVector<ImageLink> VUtils::fetchImagesFromMarkdownFile(VFile *p_file,
return images;
}
// Used to de-duplicate the links. Url as the key.
QSet<QString> fetchedLinks;
QVector<VElementRegion> regions = fetchImageRegionsUsingParser(text);
QRegExp regExp(c_imageLinkRegExp);
QString basePath = p_file->fetchBasePath();
@ -231,6 +234,7 @@ QVector<ImageLink> VUtils::fetchImagesFromMarkdownFile(VFile *p_file,
QString imageUrl = regExp.capturedTexts()[2].trimmed();
ImageLink link;
link.m_url = imageUrl;
QFileInfo info(basePath, imageUrl);
if (info.exists()) {
if (info.isNativePath()) {
@ -254,8 +258,11 @@ QVector<ImageLink> VUtils::fetchImagesFromMarkdownFile(VFile *p_file,
}
if (link.m_type & p_type) {
images.push_back(link);
qDebug() << "fetch one image:" << link.m_type << link.m_path;
if (!fetchedLinks.contains(link.m_url)) {
fetchedLinks.insert(link.m_url);
images.push_back(link);
qDebug() << "fetch one image:" << link.m_type << link.m_path << link.m_url;
}
}
}
@ -266,6 +273,24 @@ QVector<ImageLink> VUtils::fetchImagesFromMarkdownFile(VFile *p_file,
return images;
}
QString VUtils::imageLinkUrlToPath(const QString &p_basePath, const QString &p_url)
{
QString path;
QFileInfo info(p_basePath, p_url);
if (info.exists()) {
if (info.isNativePath()) {
// Local file.
path = QDir::cleanPath(info.absoluteFilePath());
} else {
path = p_url;
}
} else {
path = QUrl(p_url).toString();
}
return path;
}
bool VUtils::makePath(const QString &p_path)
{
if (p_path.isEmpty()) {

View File

@ -67,6 +67,10 @@ struct ImageLink
};
QString m_path;
// The url text in the link.
QString m_url;
ImageLinkType m_type;
};
@ -121,6 +125,9 @@ public:
static QVector<ImageLink> fetchImagesFromMarkdownFile(VFile *p_file,
ImageLink::ImageLinkType p_type = ImageLink::All);
// Return the absolute path of @p_url according to @p_basePath.
static QString imageLinkUrlToPath(const QString &p_basePath, const QString &p_url);
// Create directories along the @p_path.
// @p_path could be /home/tamlok/abc, /home/tamlok/abc/.
static bool makePath(const QString &p_path);

View File

@ -128,4 +128,13 @@ enum class CursorBlock
LeftSide
};
enum class UpdateAction
{
// The info of a file/directory has been changed.
InfoChanged = 0,
// The file/directory has been moved.
Moved
};
#endif

View File

@ -602,7 +602,7 @@ void VDirectoryTree::editDirectoryInfo()
fillTreeItem(curItem, curDir);
emit directoryUpdated(curDir);
emit directoryUpdated(curDir, UpdateAction::InfoChanged);
}
}
@ -856,7 +856,7 @@ void VDirectoryTree::pasteDirectories(VDirectory *p_destDir,
}
// Broadcast this update
emit directoryUpdated(destDir);
emit directoryUpdated(destDir, p_isCut ? UpdateAction::Moved : UpdateAction::InfoChanged);
}
}

View File

@ -11,6 +11,7 @@
#include "vdirectory.h"
#include "vnotebook.h"
#include "vnavigationmode.h"
#include "vconstants.h"
class VEditArea;
class QLabel;
@ -35,7 +36,7 @@ public:
signals:
void currentDirectoryChanged(VDirectory *p_directory);
void directoryUpdated(const VDirectory *p_directory);
void directoryUpdated(const VDirectory *p_directory, UpdateAction p_act);
public slots:
// Set directory tree to display a given notebook @p_notebook.

View File

@ -521,19 +521,19 @@ bool VEditArea::isFileOpened(const VFile *p_file)
return !findTabsByFile(p_file).isEmpty();
}
void VEditArea::handleFileUpdated(const VFile *p_file)
void VEditArea::handleFileUpdated(const VFile *p_file, UpdateAction p_act)
{
int nrWin = splitter->count();
for (int i = 0; i < nrWin; ++i) {
getWindow(i)->updateFileInfo(p_file);
getWindow(i)->updateFileInfo(p_file, p_act);
}
}
void VEditArea::handleDirectoryUpdated(const VDirectory *p_dir)
void VEditArea::handleDirectoryUpdated(const VDirectory *p_dir, UpdateAction p_act)
{
int nrWin = splitter->count();
for (int i = 0; i < nrWin; ++i) {
getWindow(i)->updateDirectoryInfo(p_dir);
getWindow(i)->updateDirectoryInfo(p_dir, p_act);
}
}

View File

@ -115,8 +115,10 @@ public slots:
// Scroll current tab to @p_header.
void scrollToHeader(const VHeaderPointer &p_header);
void handleFileUpdated(const VFile *p_file);
void handleDirectoryUpdated(const VDirectory *p_dir);
void handleFileUpdated(const VFile *p_file, UpdateAction p_act);
void handleDirectoryUpdated(const VDirectory *p_dir, UpdateAction p_act);
void handleNotebookUpdated(const VNotebook *p_notebook);
private slots:

View File

@ -188,3 +188,9 @@ void VEditTab::reloadFromDisk()
void VEditTab::writeBackupFile()
{
}
void VEditTab::handleFileOrDirectoryChange(bool p_isFile, UpdateAction p_act)
{
Q_UNUSED(p_isFile);
Q_UNUSED(p_act);
}

View File

@ -111,6 +111,9 @@ public:
// Reload file from disk and reload the editor.
void reloadFromDisk();
// Handle the change of file or directory, such as the file has been moved.
virtual void handleFileOrDirectoryChange(bool p_isFile, UpdateAction p_act);
public slots:
// Enter edit mode
virtual void editFile() = 0;

View File

@ -793,18 +793,20 @@ void VEditWindow::handleTabVimStatusUpdated(const VVim *p_vim)
}
}
void VEditWindow::updateFileInfo(const VFile *p_file)
void VEditWindow::updateFileInfo(const VFile *p_file, UpdateAction p_act)
{
if (!p_file) {
return;
}
int idx = findTabByFile(p_file);
if (idx > -1) {
updateTabStatus(idx);
getTab(idx)->handleFileOrDirectoryChange(true, p_act);
}
}
void VEditWindow::updateDirectoryInfo(const VDirectory *p_dir)
void VEditWindow::updateDirectoryInfo(const VDirectory *p_dir, UpdateAction p_act)
{
if (!p_dir) {
return;
@ -816,6 +818,7 @@ void VEditWindow::updateDirectoryInfo(const VDirectory *p_dir)
QPointer<VFile> file = editor->getFile();
if (p_dir->containsFile(file)) {
updateTabStatus(i);
editor->handleFileOrDirectoryChange(false, p_act);
}
}
}

View File

@ -43,8 +43,10 @@ public:
// Scroll current tab to header @p_header.
void scrollToHeader(const VHeaderPointer &p_header);
void updateFileInfo(const VFile *p_file);
void updateDirectoryInfo(const VDirectory *p_dir);
void updateFileInfo(const VFile *p_file, UpdateAction p_act);
void updateDirectoryInfo(const VDirectory *p_dir, UpdateAction p_act);
void updateNotebookInfo(const VNotebook *p_notebook);
VEditTab *getCurrentTab() const;

View File

@ -247,7 +247,7 @@ void VFileList::fileInfo(VNoteFile *p_file)
fillItem(item, p_file);
}
emit fileUpdated(p_file);
emit fileUpdated(p_file, UpdateAction::InfoChanged);
}
}
@ -800,7 +800,7 @@ void VFileList::pasteFiles(VDirectory *p_destDir,
if (destFile) {
++nrPasted;
emit fileUpdated(destFile);
emit fileUpdated(destFile, p_isCut ? UpdateAction::Moved : UpdateAction::InfoChanged);
}
}

View File

@ -69,7 +69,7 @@ signals:
OpenFileMode p_mode = OpenFileMode::Read,
bool p_forceMode = false);
void fileUpdated(const VNoteFile *p_file);
void fileUpdated(const VNoteFile *p_file, UpdateAction p_act);
private slots:
void contextMenuRequested(QPoint pos);

View File

@ -86,14 +86,15 @@ void VMdEditOperations::insertImageFromQImage(const QString &title, const QStrin
return;
}
QString md = QString("![%1](%2/%3)").arg(title).arg(folderInLink).arg(fileName);
QString url = QString("%1/%2").arg(folderInLink).arg(fileName);
QString md = QString("![%1](%2)").arg(title).arg(url);
insertTextAtCurPos(md);
qDebug() << "insert image" << title << filePath;
VMdEditor *mdEditor = dynamic_cast<VMdEditor *>(m_editor);
Q_ASSERT(mdEditor);
mdEditor->imageInserted(filePath);
mdEditor->imageInserted(filePath, url);
}
void VMdEditOperations::insertImageFromPath(const QString &title, const QString &path,
@ -126,14 +127,15 @@ void VMdEditOperations::insertImageFromPath(const QString &title, const QString
return;
}
QString md = QString("![%1](%2/%3)").arg(title).arg(folderInLink).arg(fileName);
QString url = QString("%1/%2").arg(folderInLink).arg(fileName);
QString md = QString("![%1](%2)").arg(title).arg(url);
insertTextAtCurPos(md);
qDebug() << "insert image" << title << filePath;
VMdEditor *mdEditor = dynamic_cast<VMdEditor *>(m_editor);
Q_ASSERT(mdEditor);
mdEditor->imageInserted(filePath);
mdEditor->imageInserted(filePath, url);
}
bool VMdEditOperations::insertImageFromURL(const QUrl &imageUrl)

View File

@ -125,6 +125,7 @@ void VMdEditor::beginEdit()
void VMdEditor::endEdit()
{
setReadOnlyAndHighlightCurrentLine(true);
clearUnusedImages();
}
@ -138,6 +139,10 @@ void VMdEditor::saveFile()
m_file->setContent(toPlainText());
setModified(false);
clearUnusedImages();
initInitImages();
}
void VMdEditor::reloadFile()
@ -544,7 +549,7 @@ void VMdEditor::clearUnusedImages()
QVector<ImageLink> images = VUtils::fetchImagesFromMarkdownFile(m_file,
ImageLink::LocalRelativeInternal);
QVector<QString> unusedImages;
QSet<QString> unusedImages;
if (!m_insertedImages.isEmpty()) {
for (int i = 0; i < m_insertedImages.size(); ++i) {
@ -563,7 +568,7 @@ void VMdEditor::clearUnusedImages()
// This inserted image is no longer in the file.
if (j == images.size()) {
unusedImages.push_back(link.m_path);
unusedImages.insert(link.m_path);
}
}
@ -584,7 +589,7 @@ void VMdEditor::clearUnusedImages()
// Original local relative image is no longer in the file.
if (j == images.size()) {
unusedImages.push_back(link.m_path);
unusedImages.insert(link.m_path);
}
}
@ -621,27 +626,27 @@ void VMdEditor::clearUnusedImages()
g_config->setConfirmImagesCleanUp(dialog.getAskAgainEnabled());
for (auto const & item : items) {
unusedImages.push_back(item.m_name);
unusedImages.insert(item.m_name);
}
}
}
for (int i = 0; i < unusedImages.size(); ++i) {
for (auto const & item : unusedImages) {
bool ret = false;
if (m_file->getType() == FileType::Note) {
const VNoteFile *tmpFile = dynamic_cast<const VNoteFile *>((VFile *)m_file);
ret = VUtils::deleteFile(tmpFile->getNotebook(), unusedImages[i], false);
ret = VUtils::deleteFile(tmpFile->getNotebook(), item, false);
} else if (m_file->getType() == FileType::Orphan) {
const VOrphanFile *tmpFile = dynamic_cast<const VOrphanFile *>((VFile *)m_file);
ret = VUtils::deleteFile(tmpFile, unusedImages[i], false);
ret = VUtils::deleteFile(tmpFile, item, false);
} else {
Q_ASSERT(false);
}
if (!ret) {
qWarning() << "fail to delete unused original image" << unusedImages[i];
qWarning() << "fail to delete unused original image" << item;
} else {
qDebug() << "delete unused image" << unusedImages[i];
qDebug() << "delete unused image" << item;
}
}
}
@ -731,10 +736,11 @@ void VMdEditor::insertFromMimeData(const QMimeData *p_source)
VTextEdit::insertFromMimeData(p_source);
}
void VMdEditor::imageInserted(const QString &p_path)
void VMdEditor::imageInserted(const QString &p_path, const QString &p_url)
{
ImageLink link;
link.m_path = p_path;
link.m_url = p_url;
if (m_file->useRelativeImageFolder()) {
link.m_type = ImageLink::LocalRelativeInternal;
} else {
@ -907,3 +913,80 @@ void VMdEditor::setContent(const QString &p_content, bool p_modified)
setPlainText(p_content);
}
}
void VMdEditor::refreshPreview()
{
m_previewMgr->refreshPreview();
}
void VMdEditor::updateInitAndInsertedImages(bool p_fileChanged, UpdateAction p_act)
{
if (p_fileChanged && p_act == UpdateAction::InfoChanged) {
return;
}
if (!isModified()) {
Q_ASSERT(m_insertedImages.isEmpty());
m_insertedImages.clear();
if (!m_initImages.isEmpty()) {
// Re-generate init images.
initInitImages();
}
return;
}
// Update init images.
QVector<ImageLink> tmp = m_initImages;
initInitImages();
Q_ASSERT(tmp.size() == m_initImages.size());
QDir dir(m_file->fetchBasePath());
// File has been moved.
if (p_fileChanged) {
// Since we clear unused images once user save the note, all images
// in m_initImages now are moved already.
// Update inserted images.
// Inserted images should be moved manually here. Then update all the
// paths.
for (auto & link : m_insertedImages) {
if (link.m_type == ImageLink::LocalAbsolute) {
continue;
}
QString newPath = QDir::cleanPath(dir.absoluteFilePath(link.m_url));
if (VUtils::equalPath(link.m_path, newPath)) {
continue;
}
if (!VUtils::copyFile(link.m_path, newPath, true)) {
VUtils::showMessage(QMessageBox::Warning,
tr("Warning"),
tr("Fail to move unsaved inserted image %1 to %2.")
.arg(link.m_path)
.arg(newPath),
tr("Please check it manually to avoid image loss."),
QMessageBox::Ok,
QMessageBox::Ok,
this);
continue;
}
link.m_path = newPath;
}
} else {
// Directory changed.
// Update inserted images.
for (auto & link : m_insertedImages) {
if (link.m_type == ImageLink::LocalAbsolute) {
continue;
}
QString newPath = QDir::cleanPath(dir.absoluteFilePath(link.m_url));
link.m_path = newPath;
}
}
}

View File

@ -45,7 +45,8 @@ public:
// An image has been inserted. The image is relative.
// @p_path is the absolute path of the inserted image.
void imageInserted(const QString &p_path);
// @p_url is the URL text within ().
void imageInserted(const QString &p_path, const QString &p_url);
// Scroll to header @p_blockNumber.
// Return true if @p_blockNumber is valid to scroll to.
@ -59,6 +60,11 @@ public:
void setContent(const QString &p_content, bool p_modified = false) Q_DECL_OVERRIDE;
void refreshPreview();
// Update m_initImages and m_insertedImages to handle the change of the note path.
void updateInitAndInsertedImages(bool p_fileChanged, UpdateAction p_act);
public slots:
bool jumpTitle(bool p_forward, int p_relativeLevel, int p_repeat) Q_DECL_OVERRIDE;
@ -217,5 +223,4 @@ private:
bool m_freshEdit;
};
#endif // VMDEDITOR_H

View File

@ -982,3 +982,18 @@ void VMdTab::updateCursorStatus()
{
emit statusUpdated(fetchTabInfo(VEditTabInfo::InfoType::Cursor));
}
void VMdTab::handleFileOrDirectoryChange(bool p_isFile, UpdateAction p_act)
{
// Reload the web view with new base URL.
m_headerFromEditMode = m_currentHeader;
m_webViewer->setHtml(VUtils::generateHtmlTemplate(m_mdConType, false),
m_file->getBaseUrl());
if (m_editor) {
m_editor->updateInitAndInsertedImages(p_isFile, p_act);
// Refresh the previewed images in edit mode.
m_editor->refreshPreview();
}
}

View File

@ -85,6 +85,8 @@ public:
void reload() Q_DECL_OVERRIDE;
void handleFileOrDirectoryChange(bool p_isFile, UpdateAction p_act) Q_DECL_OVERRIDE;
public slots:
// Enter edit mode.
void editFile() Q_DECL_OVERRIDE;

View File

@ -511,8 +511,15 @@ bool VNoteFile::copyFile(VDirectory *p_destDir,
// Copy images.
QDir parentDir(destFile->fetchBasePath());
QSet<QString> processedImages;
for (int i = 0; i < images.size(); ++i) {
const ImageLink &link = images[i];
if (processedImages.contains(link.m_path)) {
continue;
}
processedImages.insert(link.m_path);
if (!QFileInfo::exists(link.m_path)) {
VUtils::addErrMsg(p_errMsg, tr("Source image %1 does not exist.")
.arg(link.m_path));

View File

@ -375,3 +375,14 @@ void VPreviewManager::clearBlockObsoletePreviewInfo(long long p_timeStamp,
m_editor->relayout(affectedBlocks);
}
void VPreviewManager::refreshPreview()
{
if (!m_previewEnabled) {
return;
}
clearPreview();
requestUpdateImageLinks();
}

View File

@ -26,6 +26,9 @@ public:
// Clear all the preview.
void clearPreview();
// Refresh all the preview.
void refreshPreview();
public slots:
// Image links were updated from the highlighter.
void imageLinksUpdated(const QVector<VElementRegion> &p_imageRegions);