From e5021f4501b06419d19468a1c1bd675c6ea51d29 Mon Sep 17 00:00:00 2001 From: Le Tan Date: Tue, 9 May 2017 19:37:17 +0800 Subject: [PATCH] refactor local image folder Treat those images which have relative path and locate in directories that have the same parent directory as the file as internal images. VNote will only manage the internal images. --- src/utils/vutils.cpp | 121 ++++++++++++++++++++++++++------------ src/utils/vutils.h | 53 ++++++++++++++--- src/vdirectory.cpp | 73 +++++++++++++++++------ src/vdirectory.h | 2 + src/vfile.cpp | 28 ++++++--- src/vfile.h | 9 ++- src/vimagepreviewer.cpp | 2 +- src/vmdedit.cpp | 61 +++++++++++-------- src/vmdedit.h | 15 +++-- src/vmdeditoperations.cpp | 69 ++++++++++++++++------ src/vmdeditoperations.h | 5 ++ src/vnotebook.cpp | 10 +++- src/vnotebook.h | 10 ++++ src/vorphanfile.cpp | 5 ++ src/vorphanfile.h | 27 +++++---- 15 files changed, 357 insertions(+), 133 deletions(-) diff --git a/src/utils/vutils.cpp b/src/utils/vutils.cpp index bd1e420a..0c3a87f8 100644 --- a/src/utils/vutils.cpp +++ b/src/utils/vutils.cpp @@ -17,14 +17,14 @@ #include #include -#include "vconfigmanager.h" +#include "vfile.h" extern VConfigManager vconfig; const QVector> VUtils::c_availableLanguages = {QPair("en_US", "Englisth(US)"), QPair("zh_CN", "Chinese")}; -const QString VUtils::c_imageLinkRegExp = QString("\\!\\[([^\\]]*)\\]\\(([^\\)\"]+)\\s*(\".*\")?\\s*\\)"); +const QString VUtils::c_imageLinkRegExp = QString("\\!\\[([^\\]]*)\\]\\(([^\\)\"]+)\\s*(\"(\\\\.|[^\"\\)])*\")?\\s*\\)"); VUtils::VUtils() { @@ -113,71 +113,120 @@ void VUtils::processStyle(QString &style, const QVector } } -bool VUtils::isMarkdown(const QString &name) +bool VUtils::isMarkdown(const QString &p_fileName) { const QVector mdPostfix({"md", "markdown", "mkd"}); - QStringList list = name.split('.', QString::SkipEmptyParts); - if (list.isEmpty()) { - return false; - } - const QString &postfix = list.last(); + QFileInfo info(p_fileName); + QString suffix = info.suffix(); + for (int i = 0; i < mdPostfix.size(); ++i) { - if (postfix == mdPostfix[i]) { + if (suffix == mdPostfix[i]) { return true; } } + return false; } -QString VUtils::fileNameFromPath(const QString &path) +QString VUtils::fileNameFromPath(const QString &p_path) { - if (path.isEmpty()) { - return path; + if (p_path.isEmpty()) { + return p_path; } - return QFileInfo(QDir::cleanPath(path)).fileName(); + + return QFileInfo(QDir::cleanPath(p_path)).fileName(); } -QString VUtils::basePathFromPath(const QString &path) +QString VUtils::basePathFromPath(const QString &p_path) { - return QFileInfo(path).path(); + if (p_path.isEmpty()) { + return p_path; + } + + return QFileInfo(QDir::cleanPath(p_path)).path(); } -// Collect image links like ![](images/xx.jpg) -QVector VUtils::imagesFromMarkdownFile(const QString &filePath) +QVector VUtils::fetchImagesFromMarkdownFile(VFile *p_file, + ImageLink::ImageLinkType p_type) { - Q_ASSERT(isMarkdown(filePath)); - QVector images; - if (filePath.isEmpty()) { + V_ASSERT(p_file->getDocType() == DocType::Markdown); + QVector images; + + bool isOpened = p_file->isOpened(); + if (!isOpened && !p_file->open()) { return images; } - QString basePath = basePathFromPath(filePath); - QString text = readFileFromDisk(filePath); - QRegExp regExp("\\!\\[[^\\]]*\\]\\((images/[^/\\)]+)\\)"); - int pos = 0; + const QString &text = p_file->getContent(); + if (text.isEmpty()) { + if (!isOpened) { + p_file->close(); + } + + return images; + } + + QRegExp regExp(c_imageLinkRegExp); + QString basePath = p_file->retriveBasePath(); + int pos = 0; while (pos < text.size() && (pos = regExp.indexIn(text, pos)) != -1) { - Q_ASSERT(regExp.captureCount() == 1); - qDebug() << regExp.capturedTexts()[0] << regExp.capturedTexts()[1]; - images.append(QDir(basePath).filePath(regExp.capturedTexts()[1])); + QString imageUrl = regExp.capturedTexts()[2].trimmed(); + + ImageLink link; + QFileInfo info(basePath, imageUrl); + if (info.exists()) { + if (info.isNativePath()) { + // Local file. + link.m_path = QDir::cleanPath(info.absoluteFilePath()); + + if (QDir::isRelativePath(imageUrl)) { + link.m_type = p_file->isInternalImageFolder(VUtils::basePathFromPath(link.m_path)) ? + ImageLink::LocalRelativeInternal : ImageLink::LocalRelativeExternal; + } else { + link.m_type = ImageLink::LocalAbsolute; + } + } else { + link.m_type = ImageLink::Resource; + link.m_path = imageUrl; + } + } else { + QUrl url(imageUrl); + link.m_path = url.toString(); + link.m_type = ImageLink::Remote; + } + + if (link.m_type & p_type) { + images.push_back(link); + qDebug() << "fetch one image:" << link.m_type << link.m_path; + } + pos += regExp.matchedLength(); } + + if (!isOpened) { + p_file->close(); + } + return images; } -void VUtils::makeDirectory(const QString &path) +bool VUtils::makePath(const QString &p_path) { - if (path.isEmpty()) { - return; + if (p_path.isEmpty()) { + return true; } - // mkdir will return false if it already exists - QString basePath = basePathFromPath(path); - QString dirName = directoryNameFromPath(path); - QDir dir(basePath); - if (dir.mkdir(dirName)) { - qDebug() << "mkdir" << path; + bool ret = true; + QDir dir; + if (dir.mkpath(p_path)) { + qDebug() << "make path" << p_path; + } else { + qWarning() << "fail to make path" << p_path; + ret = false; } + + return ret; } ClipboardOpType VUtils::opTypeInClipboard() diff --git a/src/utils/vutils.h b/src/utils/vutils.h index 46f93ab6..ea6b967c 100644 --- a/src/utils/vutils.h +++ b/src/utils/vutils.h @@ -11,6 +11,7 @@ #include "vconstants.h" class QKeyEvent; +class VFile; #if !defined(V_ASSERT) #define V_ASSERT(cond) ((!(cond)) ? qt_assert(#cond, __FILE__, __LINE__) : qt_noop()) @@ -22,6 +23,22 @@ enum class MessageBoxType Danger = 1 }; +struct ImageLink +{ + enum ImageLinkType + { + LocalRelativeInternal = 0x1, + LocalRelativeExternal = 0x2, + LocalAbsolute = 0x4, + Resource = 0x8, + Remote = 0x10, + All = 0xffff + }; + + QString m_path; + ImageLinkType m_type; +}; + class VUtils { public: @@ -36,12 +53,29 @@ public: static QString generateCopiedFileName(const QString &p_dirPath, const QString &p_fileName); static QString generateCopiedDirName(const QString &p_parentDirPath, const QString &p_dirName); static void processStyle(QString &style, const QVector > &varMap); - static bool isMarkdown(const QString &fileName); - static inline QString directoryNameFromPath(const QString& path); - static QString fileNameFromPath(const QString &path); - static QString basePathFromPath(const QString &path); - static QVector imagesFromMarkdownFile(const QString &filePath); - static void makeDirectory(const QString &path); + static bool isMarkdown(const QString &p_fileName); + + // Return the last directory name of @p_path. + static inline QString directoryNameFromPath(const QString& p_path); + + // Return the file name of @p_path. + // /home/tamlok/abc, /home/tamlok/abc/ will both return abc. + static QString fileNameFromPath(const QString &p_path); + + // Return the base path of @p_path. + // /home/tamlok/abc, /home/tamlok/abc/ will both return /home/tamlok. + static QString basePathFromPath(const QString &p_path); + + // Fetch all the image links (including those in code blocks) in markdown file p_file. + // @p_type to filter the links returned. + // Need to open p_file and will close it if it is originally closed. + static QVector fetchImagesFromMarkdownFile(VFile *p_file, + ImageLink::ImageLinkType p_type = ImageLink::All); + + // Create directories along the @p_path. + // @p_path could be /home/tamlok/abc, /home/tamlok/abc/. + static bool makePath(const QString &p_path); + static ClipboardOpType opTypeInClipboard(); static bool copyFile(const QString &p_srcFilePath, const QString &p_destFilePath, bool p_isCut); static bool copyDirectory(const QString &p_srcDirPath, const QString &p_destDirPath, bool p_isCut); @@ -59,11 +93,12 @@ public: static QString getLocale(); // Regular expression for image link. - // ![image title]( http://github.com/tamlok/vnote.jpg "alt text" ) + // ![image title]( http://github.com/tamlok/vnote.jpg "alt \" text" ) // Captured texts (need to be trimmed): // 1. Image Alt Text (Title); // 2. Image URL; // 3. Image Optional Title with double quotes; + // 4. Unused; static const QString c_imageLinkRegExp; private: @@ -71,9 +106,9 @@ private: static const QVector> c_availableLanguages; }; -inline QString VUtils::directoryNameFromPath(const QString &path) +inline QString VUtils::directoryNameFromPath(const QString &p_path) { - return fileNameFromPath(path); + return fileNameFromPath(p_path); } #endif // VUTILS_H diff --git a/src/vdirectory.cpp b/src/vdirectory.cpp index 09dd097f..64f4c37f 100644 --- a/src/vdirectory.cpp +++ b/src/vdirectory.cpp @@ -7,6 +7,8 @@ #include "vfile.h" #include "utils/vutils.h" +extern VConfigManager vconfig; + VDirectory::VDirectory(VNotebook *p_notebook, const QString &p_name, QObject *p_parent) : QObject(p_parent), m_notebook(p_notebook), m_name(p_name), m_opened(false), @@ -472,9 +474,10 @@ VFile *VDirectory::copyFile(VDirectory *p_destDir, const QString &p_destName, DocType docType = p_srcFile->getDocType(); DocType newDocType = VUtils::isMarkdown(destPath) ? DocType::Markdown : DocType::Html; - QVector images; + QVector images; if (docType == DocType::Markdown) { - images = VUtils::imagesFromMarkdownFile(srcPath); + images = VUtils::fetchImagesFromMarkdownFile(p_srcFile, + ImageLink::LocalRelativeInternal); } // Copy the file @@ -505,35 +508,71 @@ VFile *VDirectory::copyFile(VDirectory *p_destDir, const QString &p_destName, destFile->convert(docType, newDocType); } - // We need to copy images when it is still markdown + // We need to copy internal images when it is still markdown. if (!images.isEmpty()) { if (newDocType == DocType::Markdown) { - QString dirPath = destFile->retriveImagePath(); - VUtils::makeDirectory(dirPath); + QString parentPath = destFile->retriveBasePath(); int nrPasted = 0; for (int i = 0; i < images.size(); ++i) { - if (!QFile(images[i]).exists()) { + const ImageLink &link = images[i]; + if (!QFileInfo::exists(link.m_path)) { continue; } - QString destImagePath = QDir(dirPath).filePath(VUtils::fileNameFromPath(images[i])); - // Copy or Cut the images accordingly. - if (VUtils::copyFile(images[i], destImagePath, p_cut)) { - nrPasted++; + QString errStr; + bool ret = true; + + QString imageFolder = VUtils::directoryNameFromPath(VUtils::basePathFromPath(link.m_path)); + QString destImagePath = QDir(parentPath).filePath(imageFolder); + ret = VUtils::makePath(destImagePath); + if (!ret) { + errStr = tr("Fail to create image folder %2.") + .arg(vconfig.c_dataTextStyle).arg(destImagePath); } else { + destImagePath = QDir(destImagePath).filePath(VUtils::fileNameFromPath(link.m_path)); + + // Copy or Cut the images accordingly. + if (destImagePath == link.m_path) { + ret = false; + } else { + ret = VUtils::copyFile(link.m_path, destImagePath, p_cut); + } + + if (ret) { + qDebug() << (p_cut ? "Cut" : "Copy") << "image" + << link.m_path << "->" << destImagePath; + + nrPasted++; + } else { + errStr = tr("Please check if there already exists a file %2 " + "and then manually copy it and modify the note accordingly.") + .arg(vconfig.c_dataTextStyle).arg(destImagePath); + } + } + + if (!ret) { VUtils::showMessage(QMessageBox::Warning, tr("Warning"), - tr("Fail to copy image %1.").arg(images[i]), - tr("Please check if there already exists a file with the same name and then manually copy it."), - QMessageBox::Ok, QMessageBox::Ok, NULL); + tr("Fail to copy image %2 while " + "%5 note %4.") + .arg(vconfig.c_dataTextStyle).arg(link.m_path) + .arg(vconfig.c_dataTextStyle).arg(srcPath) + .arg(p_cut ? tr("moving") : tr("copying")), + errStr, QMessageBox::Ok, QMessageBox::Ok, NULL); } } - qDebug() << "pasted" << nrPasted << "images sucessfully"; + + qDebug() << "pasted" << nrPasted << "images"; } else { - // Delete the images + // Delete the images. + int deleted = 0; for (int i = 0; i < images.size(); ++i) { - QFile file(images[i]); - file.remove(); + QFile file(images[i].m_path); + if (file.remove()) { + ++deleted; + } } + + qDebug() << "delete" << deleted << "images since it is not Markdown any more for" << srcPath; } } diff --git a/src/vdirectory.h b/src/vdirectory.h index a6ccb220..4375a7b1 100644 --- a/src/vdirectory.h +++ b/src/vdirectory.h @@ -38,10 +38,12 @@ public: VDirectory *addSubDirectory(const QString &p_name, int p_index); void deleteFile(VFile *p_file); bool rename(const QString &p_name); + // Copy @p_srcFile to @p_destDir, setting new name to @p_destName. // @p_cut: copy or cut. Returns the dest VFile. static VFile *copyFile(VDirectory *p_destDir, const QString &p_destName, VFile *p_srcFile, bool p_cut); + static VDirectory *copyDirectory(VDirectory *p_destDir, const QString &p_destName, VDirectory *p_srcDir, bool p_cut); diff --git a/src/vfile.cpp b/src/vfile.cpp index 1e853e61..3e0cc1ed 100644 --- a/src/vfile.cpp +++ b/src/vfile.cpp @@ -45,9 +45,9 @@ void VFile::close() void VFile::deleteDiskFile() { - Q_ASSERT(parent()); + V_ASSERT(parent()); - // Delete local images in ./images if it is Markdown + // Delete local images if it is Markdown. if (m_docType == DocType::Markdown) { deleteLocalImages(); } @@ -97,17 +97,19 @@ void VFile::setModified(bool p_modified) void VFile::deleteLocalImages() { - Q_ASSERT(m_docType == DocType::Markdown); - QString filePath = retrivePath(); - QVector images = VUtils::imagesFromMarkdownFile(filePath); + V_ASSERT(m_docType == DocType::Markdown); + + QVector images = VUtils::fetchImagesFromMarkdownFile(this, + ImageLink::LocalRelativeInternal); int deleted = 0; for (int i = 0; i < images.size(); ++i) { - QFile file(images[i]); + QFile file(images[i].m_path); if (file.remove()) { ++deleted; } } - qDebug() << "delete" << deleted << "images for" << filePath; + + qDebug() << "delete" << deleted << "images for" << retrivePath(); } void VFile::setName(const QString &p_name) @@ -151,6 +153,11 @@ QString VFile::getNotebookName() const return getDirectory()->getNotebookName(); } +const VNotebook *VFile::getNotebook() const +{ + return getDirectory()->getNotebook(); +} + VNotebook *VFile::getNotebook() { return getDirectory()->getNotebook(); @@ -175,7 +182,7 @@ QString VFile::retriveBasePath() const QString VFile::retriveImagePath() const { - return QDir(retriveBasePath()).filePath("images"); + return QDir(retriveBasePath()).filePath(getNotebook()->getImageFolder()); } void VFile::setContent(const QString &p_content) @@ -202,3 +209,8 @@ FileType VFile::getType() const { return m_type; } + +bool VFile::isInternalImageFolder(const QString &p_path) const +{ + return VUtils::basePathFromPath(p_path) == getDirectory()->retrivePath(); +} diff --git a/src/vfile.h b/src/vfile.h index 58cb93f2..280cab0e 100644 --- a/src/vfile.h +++ b/src/vfile.h @@ -28,6 +28,7 @@ public: DocType getDocType() const; const QString &getContent() const; virtual void setContent(const QString &p_content); + virtual const VNotebook *getNotebook() const; virtual VNotebook *getNotebook(); virtual QString getNotebookName() const; virtual QString retrivePath() const; @@ -39,13 +40,19 @@ public: bool isOpened() const; FileType getType() const; + // Whether the directory @p_path is an internal image folder of this file. + // It is true only when the folder is in the same directory as the parent + // directory of this file. + virtual bool isInternalImageFolder(const QString &p_path) const; + public slots: void setModified(bool p_modified); protected: // Delete the file and corresponding images void deleteDiskFile(); - // Delete local images in ./images of DocType::Markdown + + // Delete local images of DocType::Markdown. void deleteLocalImages(); QString m_name; diff --git a/src/vimagepreviewer.cpp b/src/vimagepreviewer.cpp index db612679..6aa1b618 100644 --- a/src/vimagepreviewer.cpp +++ b/src/vimagepreviewer.cpp @@ -195,7 +195,7 @@ QString VImagePreviewer::fetchImagePathToPreview(const QString &p_text) if (info.exists()) { if (info.isNativePath()) { // Local file. - imagePath = info.absoluteFilePath(); + imagePath = QDir::cleanPath(info.absoluteFilePath()); } else { imagePath = imageUrl; } diff --git a/src/vmdedit.cpp b/src/vmdedit.cpp index 3101ebea..837e6bd1 100644 --- a/src/vmdedit.cpp +++ b/src/vmdedit.cpp @@ -90,7 +90,7 @@ void VMdEdit::saveFile() void VMdEdit::reloadFile() { const QString &content = m_file->getContent(); - Q_ASSERT(content.indexOf(QChar::ObjectReplacementCharacter) == -1); + V_ASSERT(content.indexOf(QChar::ObjectReplacementCharacter) == -1); setPlainText(content); setModified(false); } @@ -171,61 +171,74 @@ void VMdEdit::insertFromMimeData(const QMimeData *source) VEdit::insertFromMimeData(source); } -void VMdEdit::imageInserted(const QString &p_name) +void VMdEdit::imageInserted(const QString &p_path) { - m_insertedImages.append(p_name); + ImageLink link; + link.m_path = p_path; + link.m_type = ImageLink::LocalRelativeInternal; + + m_insertedImages.append(link); } void VMdEdit::initInitImages() { - m_initImages = VUtils::imagesFromMarkdownFile(m_file->retrivePath()); + m_initImages = VUtils::fetchImagesFromMarkdownFile(m_file, + ImageLink::LocalRelativeInternal); } void VMdEdit::clearUnusedImages() { - QVector images = VUtils::imagesFromMarkdownFile(m_file->retrivePath()); + QVector images = VUtils::fetchImagesFromMarkdownFile(m_file, + ImageLink::LocalRelativeInternal); if (!m_insertedImages.isEmpty()) { - QVector imageNames(images.size()); - for (int i = 0; i < imageNames.size(); ++i) { - imageNames[i] = VUtils::fileNameFromPath(images[i]); - } - - QDir dir = QDir(m_file->retriveImagePath()); for (int i = 0; i < m_insertedImages.size(); ++i) { - QString name = m_insertedImages[i]; + const ImageLink &link = m_insertedImages[i]; + + V_ASSERT(link.m_type == ImageLink::LocalRelativeInternal); + int j; - for (j = 0; j < imageNames.size(); ++j) { - if (name == imageNames[j]) { + for (j = 0; j < images.size(); ++j) { + if (link.m_path == images[j].m_path) { break; } } - // Delete it - if (j == imageNames.size()) { - QString imagePath = dir.filePath(name); - QFile(imagePath).remove(); - qDebug() << "delete inserted image" << imagePath; + // This inserted image is no longer in the file. + if (j == images.size()) { + if (!QFile(link.m_path).remove()) { + qWarning() << "fail to delete unused inserted image" << link.m_path; + } else { + qDebug() << "delete unused inserted image" << link.m_path; + } } } + m_insertedImages.clear(); } for (int i = 0; i < m_initImages.size(); ++i) { - QString imagePath = m_initImages[i]; + const ImageLink &link = m_initImages[i]; + + V_ASSERT(link.m_type == ImageLink::LocalRelativeInternal); + int j; for (j = 0; j < images.size(); ++j) { - if (imagePath == images[j]) { + if (link.m_path == images[j].m_path) { break; } } - // Delete it + // Original local relative image is no longer in the file. if (j == images.size()) { - QFile(imagePath).remove(); - qDebug() << "delete existing image" << imagePath; + if (!QFile(link.m_path).remove()) { + qWarning() << "fail to delete unused original image" << link.m_path; + } else { + qDebug() << "delete unused original image" << link.m_path; + } } } + m_initImages.clear(); } diff --git a/src/vmdedit.h b/src/vmdedit.h index 7358acaf..f5c61f1b 100644 --- a/src/vmdedit.h +++ b/src/vmdedit.h @@ -10,6 +10,7 @@ #include "vtoc.h" #include "veditoperations.h" #include "vconfigmanager.h" +#include "utils/vutils.h" class HGMarkdownHighlighter; class VCodeBlockHighlightHelper; @@ -27,8 +28,9 @@ public: void saveFile() Q_DECL_OVERRIDE; void reloadFile() Q_DECL_OVERRIDE; - // An image has been inserted. - void imageInserted(const QString &p_name); + // 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); // Scroll to m_headers[p_headerIndex]. void scrollToHeader(int p_headerIndex); @@ -67,8 +69,13 @@ private: HGMarkdownHighlighter *m_mdHighlighter; VCodeBlockHighlightHelper *m_cbHighlighter; VImagePreviewer *m_imagePreviewer; - QVector m_insertedImages; - QVector m_initImages; + + // Image links inserted while editing. + QVector m_insertedImages; + + // Image links right at the beginning of the edit. + QVector m_initImages; + QVector m_headers; }; diff --git a/src/vmdeditoperations.cpp b/src/vmdeditoperations.cpp index 6b94596f..1316926a 100644 --- a/src/vmdeditoperations.cpp +++ b/src/vmdeditoperations.cpp @@ -55,49 +55,80 @@ void VMdEditOperations::insertImageFromQImage(const QString &title, const QStrin const QImage &image) { QString fileName = VUtils::generateImageFileName(path, title); - qDebug() << "insert image" << path << title << fileName; QString filePath = QDir(path).filePath(fileName); - Q_ASSERT(!QFile(filePath).exists()); - VUtils::makeDirectory(path); - bool ret = image.save(filePath); + V_ASSERT(!QFile(filePath).exists()); + + QString errStr; + bool ret = VUtils::makePath(path); if (!ret) { - QMessageBox msgBox(QMessageBox::Warning, tr("Warning"), tr("Fail to save image %1.").arg(filePath), - QMessageBox::Ok, (QWidget *)m_editor); - msgBox.exec(); + errStr = tr("Fail to create image folder %2.") + .arg(vconfig.c_dataTextStyle).arg(path); + } else { + ret = image.save(filePath); + if (!ret) { + errStr = tr("Fail to save image %2.") + .arg(vconfig.c_dataTextStyle).arg(filePath); + } + } + + if (!ret) { + VUtils::showMessage(QMessageBox::Warning, tr("Warning"), + tr("Fail to insert image %2.").arg(vconfig.c_dataTextStyle).arg(title), + errStr, + QMessageBox::Ok, + QMessageBox::Ok, + (QWidget *)m_editor); return; } - QString md = QString("![%1](images/%2)").arg(title).arg(fileName); + QString md = QString("![%1](%2/%3)").arg(title).arg(VUtils::directoryNameFromPath(path)).arg(fileName); insertTextAtCurPos(md); + qDebug() << "insert image" << title << filePath; + VMdEdit *mdEditor = dynamic_cast(m_editor); Q_ASSERT(mdEditor); - mdEditor->imageInserted(fileName); + mdEditor->imageInserted(filePath); } void VMdEditOperations::insertImageFromPath(const QString &title, const QString &path, const QString &oriImagePath) { QString fileName = VUtils::generateImageFileName(path, title, QFileInfo(oriImagePath).suffix()); - qDebug() << "insert image" << path << title << fileName << oriImagePath; QString filePath = QDir(path).filePath(fileName); - Q_ASSERT(!QFile(filePath).exists()); - VUtils::makeDirectory(path); - bool ret = QFile::copy(oriImagePath, filePath); + V_ASSERT(!QFile(filePath).exists()); + + QString errStr; + bool ret = VUtils::makePath(path); if (!ret) { - qWarning() << "fail to copy" << oriImagePath << "to" << filePath; - QMessageBox msgBox(QMessageBox::Warning, tr("Warning"), tr("Fail to save image %1.").arg(filePath), - QMessageBox::Ok, (QWidget *)m_editor); - msgBox.exec(); + errStr = tr("Fail to create image folder %2.") + .arg(vconfig.c_dataTextStyle).arg(path); + } else { + ret = QFile::copy(oriImagePath, filePath); + if (!ret) { + errStr = tr("Fail to copy image %2.") + .arg(vconfig.c_dataTextStyle).arg(filePath); + } + } + + if (!ret) { + VUtils::showMessage(QMessageBox::Warning, tr("Warning"), + tr("Fail to insert image %2.").arg(vconfig.c_dataTextStyle).arg(title), + errStr, + QMessageBox::Ok, + QMessageBox::Ok, + (QWidget *)m_editor); return; } - QString md = QString("![%1](images/%2)").arg(title).arg(fileName); + QString md = QString("![%1](%2/%3)").arg(title).arg(VUtils::directoryNameFromPath(path)).arg(fileName); insertTextAtCurPos(md); + qDebug() << "insert image" << title << filePath; + VMdEdit *mdEditor = dynamic_cast(m_editor); Q_ASSERT(mdEditor); - mdEditor->imageInserted(fileName); + mdEditor->imageInserted(filePath); } bool VMdEditOperations::insertImageFromURL(const QUrl &imageUrl) diff --git a/src/vmdeditoperations.h b/src/vmdeditoperations.h index d83b1038..68530550 100644 --- a/src/vmdeditoperations.h +++ b/src/vmdeditoperations.h @@ -26,7 +26,12 @@ private slots: private: void insertImageFromPath(const QString &title, const QString &path, const QString &oriImagePath); + + // @title: title of the inserted image; + // @path: the image folder path to insert the image in; + // @image: the image to be inserted; void insertImageFromQImage(const QString &title, const QString &path, const QImage &image); + void setKeyState(KeyState p_state); // Key press handlers. diff --git a/src/vnotebook.cpp b/src/vnotebook.cpp index 9b52afbd..e16e7705 100644 --- a/src/vnotebook.cpp +++ b/src/vnotebook.cpp @@ -6,9 +6,12 @@ #include "vconfigmanager.h" #include "vfile.h" +const QString VNotebook::c_defaultImageFolder = "_v_images"; + VNotebook::VNotebook(const QString &name, const QString &path, QObject *parent) - : QObject(parent), m_name(name), m_path(path) + : QObject(parent), m_name(name), m_imageFolder(c_defaultImageFolder) { + m_path = QDir::cleanPath(path); m_rootDir = new VDirectory(this, VUtils::directoryNameFromPath(path)); } @@ -116,3 +119,8 @@ bool VNotebook::containsFile(const VFile *p_file) const { return m_rootDir->containsFile(p_file); } + +const QString &VNotebook::getImageFolder() const +{ + return m_imageFolder; +} diff --git a/src/vnotebook.h b/src/vnotebook.h index 51a8ddd4..0115d03e 100644 --- a/src/vnotebook.h +++ b/src/vnotebook.h @@ -30,12 +30,22 @@ public: QObject *p_parent = 0); static bool deleteNotebook(VNotebook *p_notebook, bool p_deleteFiles); + const QString &getImageFolder() const; + signals: void contentChanged(); private: QString m_name; QString m_path; + + // Folder name to store images. + // VNote will store images in this folder within the same directory of the note. + QString m_imageFolder; + + // Default folder name to store images of all the notes within this notebook. + static const QString c_defaultImageFolder; + // Parent is NULL for root directory VDirectory *m_rootDir; }; diff --git a/src/vorphanfile.cpp b/src/vorphanfile.cpp index bdc3b67d..ba6363aa 100644 --- a/src/vorphanfile.cpp +++ b/src/vorphanfile.cpp @@ -87,3 +87,8 @@ void VOrphanFile::setContent(const QString & /* p_content */) { V_ASSERT(false); } + +bool VOrphanFile::isInternalImageFolder(const QString &p_path) const +{ + return VUtils::basePathFromPath(p_path) == VUtils::basePathFromPath(m_path); +} diff --git a/src/vorphanfile.h b/src/vorphanfile.h index 87218ccb..7848880d 100644 --- a/src/vorphanfile.h +++ b/src/vorphanfile.h @@ -10,21 +10,22 @@ class VOrphanFile : public VFile Q_OBJECT public: VOrphanFile(const QString &p_path, QObject *p_parent); - bool open(); - QString retrivePath() const; - QString retriveRelativePath() const; - QString retriveBasePath() const; - VDirectory *getDirectory(); - const VDirectory *getDirectory() const; - QString getNotebookName() const; - VNotebook *getNotebook(); + bool open() Q_DECL_OVERRIDE; + QString retrivePath() const Q_DECL_OVERRIDE; + QString retriveRelativePath() const Q_DECL_OVERRIDE; + QString retriveBasePath() const Q_DECL_OVERRIDE; + VDirectory *getDirectory() Q_DECL_OVERRIDE; + const VDirectory *getDirectory() const Q_DECL_OVERRIDE; + QString getNotebookName() const Q_DECL_OVERRIDE; + VNotebook *getNotebook() Q_DECL_OVERRIDE; private: - bool save(); - void convert(DocType p_curType, DocType p_targetType); - void setName(const QString &p_name); - QString retriveImagePath() const; - void setContent(const QString &p_content); + bool save() Q_DECL_OVERRIDE; + void convert(DocType p_curType, DocType p_targetType) Q_DECL_OVERRIDE; + void setName(const QString &p_name) Q_DECL_OVERRIDE; + QString retriveImagePath() const Q_DECL_OVERRIDE; + void setContent(const QString &p_content) Q_DECL_OVERRIDE; + bool isInternalImageFolder(const QString &p_path) const Q_DECL_OVERRIDE; QString m_path; friend class VDirectory;