diff --git a/src/dialog/vexportdialog.cpp b/src/dialog/vexportdialog.cpp new file mode 100644 index 00000000..e88a045f --- /dev/null +++ b/src/dialog/vexportdialog.cpp @@ -0,0 +1,630 @@ +#include "vexportdialog.h" + +#include +#include + +#ifndef QT_NO_PRINTER +#include +#include +#endif + +#include "utils/vutils.h" +#include "vlineedit.h" +#include "vnotebook.h" +#include "vfile.h" +#include "vdirectory.h" +#include "vcart.h" +#include "vconfigmanager.h" +#include "vnotefile.h" +#include "vnote.h" +#include "vexporter.h" + +extern VConfigManager *g_config; + +extern VNote *g_vnote; + +QString VExportDialog::s_lastOutputFolder; + +#define LOGERR(x) do { QString msg = (x); \ + VUtils::addErrMsg(p_errMsg, msg); \ + appendLogLine(msg); \ + } while (0) + +VExportDialog::VExportDialog(VNotebook *p_notebook, + VDirectory *p_directory, + VFile *p_file, + VCart *p_cart, + MarkdownConverterType p_renderer, + QWidget *p_parent) + : QDialog(p_parent), + m_notebook(p_notebook), + m_directory(p_directory), + m_file(p_file), + m_cart(p_cart), + m_pageLayout(QPageLayout(QPageSize(QPageSize::A4), + QPageLayout::Portrait, + QMarginsF(0.3, 0.3, 0.3, 0.3))), + m_inExport(false), + m_askedToStop(false) +{ + if (s_lastOutputFolder.isEmpty()) { + s_lastOutputFolder = g_config->getExportFolderPath(); + } + + setupUI(); + + m_exporter = new VExporter(this); + + initUIFields(p_renderer); + + handleInputChanged(); +} + +void VExportDialog::setupUI() +{ + // Notes to export. + m_srcCB = VUtils::getComboBox(); + m_srcCB->setToolTip(tr("Choose notes to export")); + m_srcCB->setSizeAdjustPolicy(QComboBox::AdjustToMinimumContentsLengthWithIcon); + + // Target format. + m_formatCB = VUtils::getComboBox(); + m_formatCB->setToolTip(tr("Choose target format to export as")); + m_formatCB->setSizeAdjustPolicy(QComboBox::AdjustToContents); + connect(m_formatCB, SIGNAL(currentIndexChanged(int)), + this, SLOT(handleCurrentFormatChanged(int))); + + // Markdown renderer. + m_rendererCB = VUtils::getComboBox(); + m_rendererCB->setToolTip(tr("Choose converter to render Markdown")); + m_rendererCB->setSizeAdjustPolicy(QComboBox::AdjustToContents); + + // Output directory. + m_outputEdit = new VLineEdit(s_lastOutputFolder); + connect(m_outputEdit, &QLineEdit::textChanged, + this, &VExportDialog::handleInputChanged); + m_browseBtn = new QPushButton(tr("&Browse")); + connect(m_browseBtn, &QPushButton::clicked, + this, &VExportDialog::handleBrowseBtnClicked); + QHBoxLayout *outputLayout = new QHBoxLayout(); + outputLayout->addWidget(m_outputEdit); + outputLayout->addWidget(m_browseBtn); + + m_basicBox = new QGroupBox(tr("Information")); + + m_settingBox = new QGroupBox(tr("Advanced Settings")); + + m_consoleEdit = new QPlainTextEdit(); + m_consoleEdit->setReadOnly(true); + m_consoleEdit->setLineWrapMode(QPlainTextEdit::NoWrap); + m_consoleEdit->setProperty("LineEdit", true); + m_consoleEdit->setPlaceholderText(tr("Output logs will be shown here")); + + // Ok is the default button. + m_btnBox = new QDialogButtonBox(QDialogButtonBox::Close); + m_exportBtn = m_btnBox->addButton(tr("Export"), QDialogButtonBox::ActionRole); + m_openBtn = m_btnBox->addButton(tr("Open Output Directory"), QDialogButtonBox::ActionRole); + connect(m_btnBox, &QDialogButtonBox::rejected, + this, [this]() { + if (m_inExport) { + // Just cancel the export. Do not exit. + m_askedToStop = true; + } else { + QDialog::reject(); + } + }); + + m_exportBtn->setProperty("SpecialBtn", true); + connect(m_exportBtn, &QPushButton::clicked, + this, &VExportDialog::startExport); + + connect(m_openBtn, &QPushButton::clicked, + this, [this]() { + QUrl url = QUrl::fromLocalFile(getOutputDirectory()); + QDesktopServices::openUrl(url); + }); + + QFormLayout *basicLayout = new QFormLayout(); + basicLayout->addRow(tr("Notes to export:"), m_srcCB); + basicLayout->addRow(tr("Target format:"), m_formatCB); + basicLayout->addRow(tr("Markdown renderer:"), m_rendererCB); + basicLayout->addRow(tr("Output directory:"), outputLayout); + + m_basicBox->setLayout(basicLayout); + + // Settings box. + m_pdfSettings = setupPDFAdvancedSettings(); + + QVBoxLayout *advLayout = new QVBoxLayout(); + advLayout->addWidget(m_pdfSettings); + + m_settingBox->setLayout(advLayout); + + QVBoxLayout *mainLayout = new QVBoxLayout(); + mainLayout->addWidget(m_basicBox); + mainLayout->addWidget(m_settingBox); + mainLayout->addWidget(m_consoleEdit); + mainLayout->addWidget(m_btnBox); + + setLayout(mainLayout); + + setWindowTitle(tr("Export")); +} + +QWidget *VExportDialog::setupPDFAdvancedSettings() +{ + // Page layout settings. + m_layoutLabel = new QLabel(); + m_layoutBtn = new QPushButton(tr("Settings")); + +#ifndef QT_NO_PRINTER + connect(m_layoutBtn, &QPushButton::clicked, + this, &VExportDialog::handleLayoutBtnClicked); +#else + m_layoutBtn->hide(); +#endif + + updatePageLayoutLabel(); + + QHBoxLayout *layoutLayout = new QHBoxLayout(); + layoutLayout->addWidget(m_layoutLabel); + layoutLayout->addWidget(m_layoutBtn); + layoutLayout->addStretch(); + + QFormLayout *advLayout = new QFormLayout(); + advLayout->addRow(tr("Page layout:"), layoutLayout); + + advLayout->setContentsMargins(0, 0, 0, 0); + + QWidget *wid = new QWidget(); + wid->setLayout(advLayout); + + return wid; +} + +void VExportDialog::initUIFields(MarkdownConverterType p_renderer) +{ + // Notes to export. + if (m_file) { + m_srcCB->addItem(tr("Current Note (%1)").arg(m_file->getName()), + (int)ExportSource::CurrentNote); + } + + if (m_directory) { + m_srcCB->addItem(tr("Current Directory (%1)").arg(m_directory->getName()), + (int)ExportSource::CurrentDirectory); + } + + if (m_notebook) { + m_srcCB->addItem(tr("Current Notebook (%1)").arg(m_notebook->getName()), + (int)ExportSource::CurrentNotebook); + } + + if (m_cart && m_cart->count() > 0) { + m_srcCB->addItem(tr("Cart (%1)").arg(m_cart->count()), + (int)ExportSource::Cart); + } + + // Export format. + m_formatCB->addItem(tr("Markdown"), (int)ExportFormat::Markdown); + m_formatCB->addItem(tr("HTML"), (int)ExportFormat::HTML); + m_formatCB->addItem(tr("PDF"), (int)ExportFormat::PDF); + + // Markdown renderer. + m_rendererCB->addItem(tr("Hoedown"), MarkdownConverterType::Hoedown); + m_rendererCB->addItem(tr("Marked"), MarkdownConverterType::Marked); + m_rendererCB->addItem(tr("Markdown-it"), MarkdownConverterType::MarkdownIt); + m_rendererCB->addItem(tr("Showdown"), MarkdownConverterType::Showdown); + m_rendererCB->setCurrentIndex(m_rendererCB->findData(p_renderer)); +} + +void VExportDialog::startExport() +{ + if (m_inExport) { + return; + } + + m_exportBtn->setEnabled(false); + m_askedToStop = false; + m_inExport = true; + + QString outputFolder = QDir::cleanPath(QDir(getOutputDirectory()).absolutePath()); + + ExportOption opt((ExportSource)m_srcCB->currentData().toInt(), + (ExportFormat)m_formatCB->currentData().toInt(), + (MarkdownConverterType)m_rendererCB->currentData().toInt(), + &m_pageLayout); + + m_consoleEdit->clear(); + appendLogLine(tr("Export to %1.").arg(outputFolder)); + + if (opt.m_format == ExportFormat::PDF) { + m_exporter->prepareExport(opt); + } + + int ret = 0; + QString msg; + + switch ((int)opt.m_source) { + case (int)ExportSource::CurrentNote: + ret = doExport(m_file, opt, outputFolder, &msg); + break; + + case (int)ExportSource::CurrentDirectory: + ret = doExport(m_directory, opt, outputFolder, &msg); + break; + + case (int)ExportSource::CurrentNotebook: + ret = doExport(m_notebook, opt, outputFolder, &msg); + break; + + case (int)ExportSource::Cart: + ret = doExport(m_cart, opt, outputFolder, &msg); + break; + + default: + break; + } + + if (m_askedToStop) { + appendLogLine(tr("User cancelled the export. Aborted!")); + m_askedToStop = false; + } + + if (!msg.isEmpty()) { + VUtils::showMessage(QMessageBox::Warning, + tr("Warning"), + tr("Errors found during export."), + msg, + QMessageBox::Ok, + QMessageBox::Ok, + this); + } + + appendLogLine(tr("%1 notes exported.").arg(ret)); + + m_inExport = false; + m_exportBtn->setEnabled(true); +} + +QString VExportDialog::getOutputDirectory() const +{ + return m_outputEdit->text(); +} + +void VExportDialog::handleBrowseBtnClicked() +{ + QString initPath = getOutputDirectory(); + if (!QFileInfo::exists(initPath)) { + initPath = g_config->getDocumentPathOrHomePath(); + } + + QString dirPath = QFileDialog::getExistingDirectory(this, + tr("Select Output Directory To Export To"), + initPath, + QFileDialog::ShowDirsOnly + | QFileDialog::DontResolveSymlinks); + + if (!dirPath.isEmpty()) { + m_outputEdit->setText(dirPath); + s_lastOutputFolder = dirPath; + } +} + +void VExportDialog::handleInputChanged() +{ + // Source. + bool sourceOk = true; + if (m_srcCB->count() == 0) { + sourceOk = false; + } + + // Output folder. + bool pathOk = true; + QString path = getOutputDirectory(); + if (path.isEmpty() || !VUtils::checkPathLegal(path)) { + pathOk = false; + } + + m_exportBtn->setEnabled(sourceOk && pathOk); + m_openBtn->setEnabled(pathOk); +} + +void VExportDialog::appendLogLine(const QString &p_text) +{ + m_consoleEdit->appendPlainText(p_text); + QCoreApplication::sendPostedEvents(); +} + +int VExportDialog::doExport(VFile *p_file, + const ExportOption &p_opt, + const QString &p_outputFolder, + QString *p_errMsg) +{ + Q_ASSERT(p_file); + + appendLogLine(tr("Exporting note %1.").arg(p_file->fetchPath())); + + int ret = 0; + switch ((int)p_opt.m_format) { + case (int)ExportFormat::Markdown: + ret = doExportMarkdown(p_file, p_opt, p_outputFolder, p_errMsg); + break; + + case (int)ExportFormat::PDF: + ret = doExportPDF(p_file, p_opt, p_outputFolder, p_errMsg); + break; + + default: + break; + } + + return ret; +} + +int VExportDialog::doExport(VDirectory *p_directory, + const ExportOption &p_opt, + const QString &p_outputFolder, + QString *p_errMsg) +{ + Q_ASSERT(p_directory); + + bool opened = p_directory->isOpened(); + if (!opened && !p_directory->open()) { + LOGERR(tr("Fail to open folder %1.").arg(p_directory->fetchRelativePath())); + return 0; + } + + int ret = 0; + + QString folderName = VUtils::getDirNameWithSequence(p_outputFolder, + p_directory->getName()); + QString outputPath = QDir(p_outputFolder).filePath(folderName); + if (!VUtils::makePath(outputPath)) { + LOGERR(tr("Fail to create directory %1.").arg(outputPath)); + goto exit; + } + + // Export child notes. + for (auto const & file : p_directory->getFiles()) { + if (!checkUserAction()) { + goto exit; + } + + ret += doExport(file, p_opt, outputPath, p_errMsg); + } + + // Export subfolder. + for (auto const & dir : p_directory->getSubDirs()) { + if (!checkUserAction()) { + goto exit; + } + + ret += doExport(dir, p_opt, outputPath, p_errMsg); + } + +exit: + if (!opened) { + p_directory->close(); + } + + return ret; +} + +int VExportDialog::doExport(VNotebook *p_notebook, + const ExportOption &p_opt, + const QString &p_outputFolder, + QString *p_errMsg) +{ + Q_ASSERT(p_notebook); + + bool opened = p_notebook->isOpened(); + if (!opened && !p_notebook->open()) { + LOGERR(tr("Fail to open notebook %1.").arg(p_notebook->getName())); + return 0; + } + + int ret = 0; + + QString folderName = VUtils::getDirNameWithSequence(p_outputFolder, + p_notebook->getName()); + QString outputPath = QDir(p_outputFolder).filePath(folderName); + if (!VUtils::makePath(outputPath)) { + LOGERR(tr("Fail to create directory %1.").arg(outputPath)); + goto exit; + } + + // Export subfolder. + for (auto const & dir : p_notebook->getRootDir()->getSubDirs()) { + if (!checkUserAction()) { + goto exit; + } + + ret += doExport(dir, p_opt, outputPath, p_errMsg); + } + +exit: + if (!opened) { + p_notebook->close(); + } + + return ret; +} + +int VExportDialog::doExport(VCart *p_cart, + const ExportOption &p_opt, + const QString &p_outputFolder, + QString *p_errMsg) +{ + Q_ASSERT(p_cart); + + int ret = 0; + + QVector files = m_cart->getFiles(); + for (auto const & it : files) { + VFile *file = g_vnote->getFile(it); + if (!file) { + LOGERR(tr("Fail to open file %1.").arg(it)); + continue; + } + + ret += doExport(file, p_opt, p_outputFolder, p_errMsg); + } + + return ret; +} + +int VExportDialog::doExportMarkdown(VFile *p_file, + const ExportOption &p_opt, + const QString &p_outputFolder, + QString *p_errMsg) +{ + Q_UNUSED(p_opt); + + QString srcFilePath(p_file->fetchPath()); + + if (p_file->getDocType() != DocType::Markdown) { + LOGERR(tr("Skip exporting non-Markdown file %1 as Markdown.").arg(srcFilePath)); + return 0; + } + + // Export it to a folder with the same name. + QString name = VUtils::getDirNameWithSequence(p_outputFolder, p_file->getName()); + QString outputPath = QDir(p_outputFolder).filePath(name); + if (!VUtils::makePath(outputPath)) { + LOGERR(tr("Fail to create directory %1.").arg(outputPath)); + return 0; + } + + // Copy the note file. + QString destPath = QDir(outputPath).filePath(p_file->getName()); + if (!VUtils::copyFile(srcFilePath, destPath, false)) { + LOGERR(tr("Fail to copy the note file %1.").arg(srcFilePath)); + return 0; + } + + // Copy images. + int ret = 1; + int nrImageCopied = 0; + QVector images = VUtils::fetchImagesFromMarkdownFile(p_file, + ImageLink::LocalRelativeInternal); + if (!VNoteFile::copyInternalImages(images, + outputPath, + false, + &nrImageCopied, + p_errMsg)) { + ret = 0; + appendLogLine(tr("Fail to copy images of note %1.").arg(srcFilePath)); + } + + // Copy attachments. + if (p_file->getType() == FileType::Note) { + VNoteFile *noteFile = static_cast(p_file); + QString attaFolder = noteFile->getAttachmentFolder(); + if (!attaFolder.isEmpty()) { + QString attaFolderPath; + attaFolderPath = noteFile->fetchAttachmentFolderPath(); + attaFolder = VUtils::getDirNameWithSequence(outputPath, attaFolder); + QString folderPath = QDir(outputPath).filePath(attaFolder); + + // Copy attaFolder to folderPath. + if (!VUtils::copyDirectory(attaFolderPath, folderPath, false)) { + LOGERR(tr("Fail to copy attachments folder %1 to %2.") + .arg(attaFolderPath).arg(folderPath)); + ret = 0; + } + } + } + + if (ret) { + appendLogLine(tr("Note %1 exported to %2.").arg(srcFilePath).arg(outputPath)); + } + + return ret; +} + +int VExportDialog::doExportPDF(VFile *p_file, + const ExportOption &p_opt, + const QString &p_outputFolder, + QString *p_errMsg) +{ + Q_UNUSED(p_opt); + + QString srcFilePath(p_file->fetchPath()); + + if (p_file->getDocType() != DocType::Markdown) { + LOGERR(tr("Skip exporting non-Markdown file %1 as PDF.").arg(srcFilePath)); + return 0; + } + + // Get output file. + QString suffix = ".pdf"; + QString name = VUtils::getFileNameWithSequence(p_outputFolder, + QFileInfo(p_file->getName()).completeBaseName() + suffix); + QString outputPath = QDir(p_outputFolder).filePath(name); + + if (m_exporter->exportPDF(p_file, p_opt, outputPath, p_errMsg)) { + appendLogLine(tr("Note %1 exported to %2.").arg(srcFilePath).arg(outputPath)); + return 1; + } else { + appendLogLine(tr("Fail to export note %1.").arg(srcFilePath)); + return 0; + } +} + +bool VExportDialog::checkUserAction() +{ + if (m_askedToStop) { + return false; + } + + QCoreApplication::processEvents(); + + return true; +} + +void VExportDialog::handleLayoutBtnClicked() +{ +#ifndef QT_NO_PRINTER + QPrinter printer; + printer.setPageLayout(m_pageLayout); + + QPageSetupDialog dlg(&printer, this); + if (dlg.exec() != QDialog::Accepted) { + return; + } + + m_pageLayout.setPageSize(printer.pageLayout().pageSize()); + m_pageLayout.setMargins(printer.pageLayout().margins()); + m_pageLayout.setOrientation(printer.pageLayout().orientation()); + + updatePageLayoutLabel(); +#endif +} + +void VExportDialog::updatePageLayoutLabel() +{ + qDebug() << "page layout margins:" << m_pageLayout.margins(); + + m_layoutLabel->setText(QString("%1, %2").arg(m_pageLayout.pageSize().name()) + .arg(m_pageLayout.orientation() == QPageLayout::Portrait ? + tr("Portrait") : tr("Landscape"))); +} + +void VExportDialog::handleCurrentFormatChanged(int p_index) +{ + bool pdfEnabled = false; + + if (p_index >= 0) { + switch (m_formatCB->currentData().toInt()) { + case (int)ExportFormat::PDF: + pdfEnabled = true; + break; + + default: + break; + } + } + + m_pdfSettings->setVisible(pdfEnabled); +} diff --git a/src/dialog/vexportdialog.h b/src/dialog/vexportdialog.h new file mode 100644 index 00000000..f42fffae --- /dev/null +++ b/src/dialog/vexportdialog.h @@ -0,0 +1,180 @@ +#ifndef VEXPORTDIALOG_H +#define VEXPORTDIALOG_H + +#include +#include + +#include "vconstants.h" + +class QLabel; +class VLineEdit; +class QDialogButtonBox; +class QComboBox; +class QPushButton; +class QGroupBox; +class QPlainTextEdit; +class VNotebook; +class VDirectory; +class VFile; +class VCart; +class VExporter; + + +enum class ExportSource +{ + CurrentNote = 0, + CurrentDirectory, + CurrentNotebook, + Cart +}; + + +enum class ExportFormat +{ + Markdown = 0, + HTML, + PDF +}; + + +struct ExportOption +{ + ExportOption(ExportSource p_source, + ExportFormat p_format, + MarkdownConverterType p_renderer, + QPageLayout *p_layout) + : m_source(p_source), + m_format(p_format), + m_renderer(p_renderer), + m_layout(p_layout) + { + } + + ExportSource m_source; + ExportFormat m_format; + MarkdownConverterType m_renderer; + QPageLayout *m_layout; +}; + + +class VExportDialog : public QDialog +{ + Q_OBJECT +public: + VExportDialog(VNotebook *p_notebook, + VDirectory *p_directory, + VFile *p_file, + VCart *p_cart, + MarkdownConverterType p_renderer, + QWidget *p_parent = nullptr); + +private slots: + void startExport(); + + void handleBrowseBtnClicked(); + + void handleInputChanged(); + + void handleLayoutBtnClicked(); + + void handleCurrentFormatChanged(int p_index); + +private: + void setupUI(); + + QWidget *setupPDFAdvancedSettings(); + + void initUIFields(MarkdownConverterType p_renderer); + + QString getOutputDirectory() const; + + void appendLogLine(const QString &p_text); + + // Return number of files exported. + int doExport(VFile *p_file, + const ExportOption &p_opt, + const QString &p_outputFolder, + QString *p_errMsg = NULL); + + int doExport(VDirectory *p_directory, + const ExportOption &p_opt, + const QString &p_outputFolder, + QString *p_errMsg = NULL); + + int doExport(VNotebook *p_notebook, + const ExportOption &p_opt, + const QString &p_outputFolder, + QString *p_errMsg = NULL); + + int doExport(VCart *p_cart, + const ExportOption &p_opt, + const QString &p_outputFolder, + QString *p_errMsg = NULL); + + int doExportMarkdown(VFile *p_file, + const ExportOption &p_opt, + const QString &p_outputFolder, + QString *p_errMsg = NULL); + + int doExportPDF(VFile *p_file, + const ExportOption &p_opt, + const QString &p_outputFolder, + QString *p_errMsg = NULL); + + // Return false if we could not continue. + bool checkUserAction(); + + void updatePageLayoutLabel(); + + QComboBox *m_srcCB; + + QComboBox *m_formatCB; + + QComboBox *m_rendererCB; + + VLineEdit *m_outputEdit; + + QPushButton *m_browseBtn; + + QGroupBox *m_basicBox; + + QGroupBox *m_settingBox; + + QWidget *m_pdfSettings; + + QPlainTextEdit *m_consoleEdit; + + QDialogButtonBox *m_btnBox; + + QPushButton *m_openBtn; + + QPushButton *m_exportBtn; + + QLabel *m_layoutLabel; + + QPushButton *m_layoutBtn; + + VNotebook *m_notebook; + + VDirectory *m_directory; + + VFile *m_file; + + VCart *m_cart; + + QPageLayout m_pageLayout; + + // Whether we are exporting files. + bool m_inExport; + + // Asked to stop exporting by user. + bool m_askedToStop; + + // Exporter used to export PDF and HTML. + VExporter *m_exporter; + + // Last output folder path. + static QString s_lastOutputFolder; +}; + +#endif // VEXPORTDIALOG_H diff --git a/src/dialog/vnewnotebookdialog.cpp b/src/dialog/vnewnotebookdialog.cpp index d70a7b45..6ff575c7 100644 --- a/src/dialog/vnewnotebookdialog.cpp +++ b/src/dialog/vnewnotebookdialog.cpp @@ -143,7 +143,7 @@ void VNewNotebookDialog::handleBrowseBtnClicked() if (defaultPath.isEmpty()) { defaultPath = g_config->getVnoteNotebookFolderPath(); if (!QFileInfo::exists(defaultPath)) { - defaultPath = QDir::homePath(); + defaultPath = g_config->getDocumentPathOrHomePath(); } } diff --git a/src/dialog/vnotebookinfodialog.h b/src/dialog/vnotebookinfodialog.h index e7e98a85..b016cd76 100644 --- a/src/dialog/vnotebookinfodialog.h +++ b/src/dialog/vnotebookinfodialog.h @@ -8,7 +8,6 @@ class QLabel; class VLineEdit; class VMetaWordLineEdit; class QDialogButtonBox; -class QString; class VNotebook; class VNotebookInfoDialog : public QDialog diff --git a/src/src.pro b/src/src.pro index e29affcf..65595726 100644 --- a/src/src.pro +++ b/src/src.pro @@ -60,7 +60,6 @@ SOURCES += main.cpp\ vorphanfile.cpp \ vcodeblockhighlighthelper.cpp \ vwebview.cpp \ - vexporter.cpp \ vmdtab.cpp \ vhtmltab.cpp \ utils/vvim.cpp \ @@ -112,7 +111,9 @@ SOURCES += main.cpp\ vlistwidget.cpp \ vsimplesearchinput.cpp \ vstyleditemdelegate.cpp \ - vtreewidget.cpp + vtreewidget.cpp \ + dialog/vexportdialog.cpp \ + vexporter.cpp HEADERS += vmainwindow.h \ vdirectorytree.h \ @@ -158,7 +159,6 @@ HEADERS += vmainwindow.h \ vorphanfile.h \ vcodeblockhighlighthelper.h \ vwebview.h \ - vexporter.h \ vmdtab.h \ vhtmltab.h \ utils/vvim.h \ @@ -211,7 +211,9 @@ HEADERS += vmainwindow.h \ vlistwidget.h \ vsimplesearchinput.h \ vstyleditemdelegate.h \ - vtreewidget.h + vtreewidget.h \ + dialog/vexportdialog.h \ + vexporter.h RESOURCES += \ vnote.qrc \ diff --git a/src/vcart.cpp b/src/vcart.cpp index ac1ffadb..753ea8ca 100644 --- a/src/vcart.cpp +++ b/src/vcart.cpp @@ -191,3 +191,19 @@ QString VCart::getFilePath(const QListWidgetItem *p_item) const { return p_item->data(Qt::UserRole).toString(); } + +int VCart::count() const +{ + return m_itemList->count(); +} + +QVector VCart::getFiles() const +{ + QVector files; + int cnt = m_itemList->count(); + for (int i = 0; i < cnt; ++i) { + files.append(getFilePath(m_itemList->item(i))); + } + + return files; +} diff --git a/src/vcart.h b/src/vcart.h index 42bc4c27..f2ef8313 100644 --- a/src/vcart.h +++ b/src/vcart.h @@ -22,6 +22,10 @@ public: void addFile(const QString &p_filePath); + int count() const; + + QVector getFiles() const; + private slots: void handleContextMenuRequested(QPoint p_pos); diff --git a/src/vconfigmanager.cpp b/src/vconfigmanager.cpp index 4add3315..eaf51ed8 100644 --- a/src/vconfigmanager.cpp +++ b/src/vconfigmanager.cpp @@ -47,6 +47,8 @@ const QString VConfigManager::c_dataTextStyle = QString("font: bold"); const QString VConfigManager::c_vnoteNotebookFolderName = QString("vnote_notebooks"); +const QString VConfigManager::c_exportFolderName = QString("vnote_exports"); + VConfigManager::VConfigManager(QObject *p_parent) : QObject(p_parent), m_hasReset(false), @@ -930,6 +932,26 @@ QString VConfigManager::getVnoteNotebookFolderPath() } } +QString VConfigManager::getExportFolderPath() +{ + QStringList folders = QStandardPaths::standardLocations(QStandardPaths::DocumentsLocation); + if (folders.isEmpty()) { + return QDir::home().filePath(c_exportFolderName); + } else { + return QDir(folders[0]).filePath(c_exportFolderName); + } +} + +QString VConfigManager::getDocumentPathOrHomePath() +{ + QStringList folders = QStandardPaths::standardLocations(QStandardPaths::DocumentsLocation); + if (folders.isEmpty()) { + return QDir::homePath(); + } else { + return folders[0]; + } +} + QHash VConfigManager::readShortcutsFromSettings(QSettings *p_settings, const QString &p_group) { diff --git a/src/vconfigmanager.h b/src/vconfigmanager.h index 5aaec2d3..ee72e3d6 100644 --- a/src/vconfigmanager.h +++ b/src/vconfigmanager.h @@ -14,20 +14,9 @@ #include "vfilesessioninfo.h" #include "utils/vmetawordmanager.h" - class QJsonObject; class QString; - -enum MarkdownConverterType -{ - Hoedown = 0, - Marked, - MarkdownIt, - Showdown -}; - - struct VColor { QString m_name; @@ -57,6 +46,7 @@ struct MarkdownitOption bool m_linkify; }; + // Type of heading sequence. enum class HeadingSequenceType { @@ -69,6 +59,7 @@ enum class HeadingSequenceType Invalid }; + class VConfigManager : public QObject { public: @@ -89,6 +80,11 @@ public: // Get the path of the folder used to store default notebook. static QString getVnoteNotebookFolderPath(); + // Get the path of the default export folder. + static QString getExportFolderPath(); + + static QString getDocumentPathOrHomePath(); + // Constants static const QString orgName; static const QString appName; @@ -892,6 +888,9 @@ private: // The folder name to store all notebooks if user does not specify one. static const QString c_vnoteNotebookFolderName; + + // The default export output folder name. + static const QString c_exportFolderName; }; diff --git a/src/vconstants.h b/src/vconstants.h index 76283e48..1a034d27 100644 --- a/src/vconstants.h +++ b/src/vconstants.h @@ -141,4 +141,12 @@ enum class UpdateAction Moved }; +enum MarkdownConverterType +{ + Hoedown = 0, + Marked, + MarkdownIt, + Showdown +}; + #endif diff --git a/src/vdirectorytree.cpp b/src/vdirectorytree.cpp index 0898e472..e0a2320a 100644 --- a/src/vdirectorytree.cpp +++ b/src/vdirectorytree.cpp @@ -1207,3 +1207,13 @@ void VDirectoryTree::sortItems(VDirectory *p_dir) updateItemDirectChildren(findVDirectory(p_dir)); } } + +VDirectory *VDirectoryTree::currentDirectory() const +{ + QTreeWidgetItem *item = currentItem(); + if (item) { + return getVDirectory(item); + } + + return NULL; +} diff --git a/src/vdirectorytree.h b/src/vdirectorytree.h index a0a6b341..8453620e 100644 --- a/src/vdirectorytree.h +++ b/src/vdirectorytree.h @@ -30,6 +30,8 @@ public: const VNotebook *currentNotebook() const; + VDirectory *currentDirectory() const; + // Implementations for VNavigationMode. void showNavigation() Q_DECL_OVERRIDE; bool handleKeyNavigation(int p_key, bool &p_succeed) Q_DECL_OVERRIDE; diff --git a/src/vexporter.cpp b/src/vexporter.cpp index 5523bc63..9a747dbf 100644 --- a/src/vexporter.cpp +++ b/src/vexporter.cpp @@ -1,206 +1,111 @@ #include "vexporter.h" -#include -#include -#include -#include #include -#include -#include - -#ifndef QT_NO_PRINTER -#include -#include -#endif +#include +#include #include "vconfigmanager.h" -#include "utils/vutils.h" #include "vfile.h" #include "vwebview.h" +#include "utils/vutils.h" #include "vpreviewpage.h" #include "vconstants.h" -#include "vnote.h" #include "vmarkdownconverter.h" #include "vdocument.h" -#include "vlineedit.h" extern VConfigManager *g_config; -QString VExporter::s_defaultPathDir = QDir::homePath(); - -VExporter::VExporter(MarkdownConverterType p_mdType, QWidget *p_parent) - : QDialog(p_parent), m_webViewer(NULL), m_mdType(p_mdType), - m_file(NULL), m_type(ExportType::PDF), m_source(ExportSource::Invalid), - m_noteState(NoteState::NotReady), m_state(ExportState::Idle), - m_pageLayout(QPageLayout(QPageSize(QPageSize::A4), QPageLayout::Portrait, QMarginsF(0.0, 0.0, 0.0, 0.0))), - m_exported(false) +VExporter::VExporter(QWidget *p_parent) + : QObject(p_parent), + m_webViewer(NULL), + m_state(ExportState::Idle) { - initMarkdownTemplate(); - - setupUI(); } -void VExporter::initMarkdownTemplate() +void VExporter::prepareExport(const ExportOption &p_opt) { - m_htmlTemplate = VUtils::generateHtmlTemplate(m_mdType, true); + m_htmlTemplate = VUtils::generateHtmlTemplate(p_opt.m_renderer, true); + m_pageLayout = *(p_opt.m_layout); } -void VExporter::setupUI() +bool VExporter::exportPDF(VFile *p_file, + const ExportOption &p_opt, + const QString &p_outputFile, + QString *p_errMsg) { - m_infoLabel = new QLabel(); - m_infoLabel->setWordWrap(true); + Q_UNUSED(p_errMsg); - // Target file path. - QLabel *pathLabel = new QLabel(tr("Target &path:")); - m_pathEdit = new VLineEdit(); - pathLabel->setBuddy(m_pathEdit); - m_browseBtn = new QPushButton(tr("&Browse")); - connect(m_browseBtn, &QPushButton::clicked, - this, &VExporter::handleBrowseBtnClicked); + bool ret = false; - // Page layout. - QLabel *layoutLabel = new QLabel(tr("Page layout:")); - m_layoutLabel = new QLabel(); - m_layoutBtn = new QPushButton(tr("&Settings")); + bool isOpened = p_file->isOpened(); + if (!isOpened && !p_file->open()) { + goto exit; + } -#ifndef QT_NO_PRINTER - connect(m_layoutBtn, &QPushButton::clicked, - this, &VExporter::handleLayoutBtnClicked); -#else - m_layoutBtn->hide(); -#endif + Q_ASSERT(m_state == ExportState::Idle); + m_state = ExportState::Busy; - // Progress. - m_proLabel = new QLabel(this); - m_proBar = new QProgressBar(this); + clearNoteState(); - // Ok is the default button. - m_btnBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); - m_openBtn = m_btnBox->addButton(tr("Open File Location"), QDialogButtonBox::ActionRole); - connect(m_btnBox, &QDialogButtonBox::accepted, this, &VExporter::startExport); - connect(m_btnBox, &QDialogButtonBox::rejected, this, &VExporter::cancelExport); - connect(m_openBtn, &QPushButton::clicked, this, &VExporter::openTargetPath); + initWebViewer(p_file, p_opt); - QPushButton *okBtn = m_btnBox->button(QDialogButtonBox::Ok); - okBtn->setProperty("SpecialBtn", true); - m_pathEdit->setMinimumWidth(okBtn->sizeHint().width() * 3); + while (!isNoteStateReady()) { + VUtils::sleepWait(100); - QGridLayout *mainLayout = new QGridLayout(); - mainLayout->addWidget(m_infoLabel, 0, 0, 1, 3); - mainLayout->addWidget(pathLabel, 1, 0); - mainLayout->addWidget(m_pathEdit, 1, 1); - mainLayout->addWidget(m_browseBtn, 1, 2); - mainLayout->addWidget(layoutLabel, 2, 0); - mainLayout->addWidget(m_layoutLabel, 2, 1); - mainLayout->addWidget(m_layoutBtn, 2, 2); - mainLayout->addWidget(m_proLabel, 3, 1, 1, 2); - mainLayout->addWidget(m_proBar, 4, 1, 1, 2); - mainLayout->addWidget(m_btnBox, 5, 1, 1, 2); + if (m_state == ExportState::Cancelled) { + goto exit; + } - m_proLabel->hide(); - m_proBar->hide(); + if (isNoteStateFailed()) { + m_state = ExportState::Failed; + goto exit; + } + } - setLayout(mainLayout); - mainLayout->setSizeConstraint(QLayout::SetFixedSize); - setWindowTitle(tr("Export Note")); + // Wait to ensure Web side is really ready. + VUtils::sleepWait(200); - m_openBtn->hide(); + if (m_state == ExportState::Cancelled) { + goto exit; + } - updatePageLayoutLabel(); -} + { + bool exportRet = exportToPDF(m_webViewer, + p_outputFile, + m_pageLayout); -static QString exportTypeStr(ExportType p_type) -{ - if (p_type == ExportType::PDF) { - return "PDF"; + clearNoteState(); + + if (!isOpened) { + p_file->close(); + } + + if (exportRet) { + m_state = ExportState::Successful; } else { - return "HTML"; + m_state = ExportState::Failed; } -} - -void VExporter::handleBrowseBtnClicked() -{ - QFileInfo fi(getFilePath()); - QString fileType = m_type == ExportType::PDF ? - tr("Portable Document Format (*.pdf)") : - tr("WebPage, Complete (*.html)"); - QString path = QFileDialog::getSaveFileName(this, tr("Export As"), - fi.absoluteFilePath(), - fileType); - if (path.isEmpty()) { - return; } - setFilePath(path); - s_defaultPathDir = VUtils::basePathFromPath(path); +exit: + clearWebViewer(); - m_openBtn->hide(); -} - -void VExporter::handleLayoutBtnClicked() -{ -#ifndef QT_NO_PRINTER - QPrinter printer; - printer.setPageLayout(m_pageLayout); - - QPageSetupDialog dlg(&printer, this); - if (dlg.exec() != QDialog::Accepted) { - return; + if (m_state == ExportState::Successful) { + ret = true; } - m_pageLayout.setPageSize(printer.pageLayout().pageSize()); - m_pageLayout.setOrientation(printer.pageLayout().orientation()); + m_state = ExportState::Idle; - updatePageLayoutLabel(); -#endif + return ret; } -void VExporter::updatePageLayoutLabel() +void VExporter::initWebViewer(VFile *p_file, const ExportOption &p_opt) { - m_layoutLabel->setText(QString("%1, %2").arg(m_pageLayout.pageSize().name()) - .arg(m_pageLayout.orientation() == QPageLayout::Portrait ? - tr("Portrait") : tr("Landscape"))); -} + Q_ASSERT(!m_webViewer); -QString VExporter::getFilePath() const -{ - return QDir::cleanPath(m_pathEdit->text()); -} - -void VExporter::setFilePath(const QString &p_path) -{ - m_pathEdit->setText(QDir::toNativeSeparators(p_path)); -} - -void VExporter::exportNote(VFile *p_file, ExportType p_type) -{ - m_file = p_file; - m_type = p_type; - m_source = ExportSource::Note; - - if (!m_file || m_file->getDocType() != DocType::Markdown) { - // Do not support non-Markdown note now. - m_btnBox->button(QDialogButtonBox::Ok)->setEnabled(false); - return; - } - - m_infoLabel->setText(tr("Export note %2 as %3.") - .arg(g_config->c_dataTextStyle) - .arg(m_file->getName()) - .arg(exportTypeStr(p_type))); - - setWindowTitle(tr("Export As %1").arg(exportTypeStr(p_type))); - - setFilePath(QDir(s_defaultPathDir).filePath(QFileInfo(p_file->fetchPath()).baseName() + - "." + exportTypeStr(p_type).toLower())); -} - -void VExporter::initWebViewer(VFile *p_file) -{ - V_ASSERT(!m_webViewer); - - m_webViewer = new VWebView(p_file, this); + m_webViewer = new VWebView(p_file, static_cast(parent())); m_webViewer->hide(); + VPreviewPage *page = new VPreviewPage(m_webViewer); m_webViewer->setPage(page); @@ -216,7 +121,7 @@ void VExporter::initWebViewer(VFile *p_file) page->setWebChannel(channel); // Need to generate HTML using Hoedown. - if (m_mdType == MarkdownConverterType::Hoedown) { + if (p_opt.m_renderer == MarkdownConverterType::Hoedown) { VMarkdownConverter mdConverter; QString toc; QString html = mdConverter.generateHtml(p_file->getContent(), @@ -228,14 +133,6 @@ void VExporter::initWebViewer(VFile *p_file) m_webViewer->setHtml(m_htmlTemplate, p_file->getBaseUrl()); } -void VExporter::clearWebViewer() -{ - if (m_webViewer) { - delete m_webViewer; - m_webViewer = NULL; - } -} - void VExporter::handleLogicsFinished() { Q_ASSERT(!(m_noteState & NoteState::WebLogicsReady)); @@ -252,130 +149,16 @@ void VExporter::handleLoadFinished(bool p_ok) } } -void VExporter::clearNoteState() +void VExporter::clearWebViewer() { - m_noteState = NoteState::NotReady; -} - -bool VExporter::isNoteStateReady() const -{ - return m_noteState == NoteState::Ready; -} - -bool VExporter::isNoteStateFailed() const -{ - return m_noteState & NoteState::Failed; -} - -void VExporter::startExport() -{ - QPushButton *cancelBtn = m_btnBox->button(QDialogButtonBox::Cancel); - - if (m_exported) { - cancelBtn->show(); - m_exported = false; - accept(); - } - - int exportedNum = 0; - enableUserInput(false); - V_ASSERT(m_state == ExportState::Idle); - m_state = ExportState::Busy; - - m_openBtn->hide(); - - if (m_source == ExportSource::Note) { - V_ASSERT(m_file); - bool isOpened = m_file->isOpened(); - if (!isOpened && !m_file->open()) { - goto exit; - } - - clearNoteState(); - initWebViewer(m_file); - - // Update progress info. - m_proLabel->setText(tr("Exporting %1").arg(m_file->getName())); - m_proBar->setEnabled(true); - m_proBar->setMinimum(0); - m_proBar->setMaximum(100); - m_proBar->reset(); - m_proLabel->show(); - m_proBar->show(); - - while (!isNoteStateReady()) { - VUtils::sleepWait(100); - if (m_proBar->value() < 70) { - m_proBar->setValue(m_proBar->value() + 1); - } - - if (m_state == ExportState::Cancelled) { - goto exit; - } - - if (isNoteStateFailed()) { - m_state = ExportState::Failed; - goto exit; - } - } - - // Wait to ensure Web side is really ready. - VUtils::sleepWait(200); - - if (m_state == ExportState::Cancelled) { - goto exit; - } - - m_proBar->setValue(80); - - bool exportRet = exportToPDF(m_webViewer, getFilePath(), m_pageLayout); - - clearNoteState(); - - if (!isOpened) { - m_file->close(); - } - - if (exportRet) { - m_proBar->setValue(100); - m_state = ExportState::Successful; - exportedNum++; - } else { - m_proBar->setEnabled(false); - m_state = ExportState::Failed; - } - } - -exit: - clearWebViewer(); - - m_proLabel->setText(""); - m_proLabel->hide(); - enableUserInput(true); - - if (m_state == ExportState::Cancelled) { - reject(); - } - - if (exportedNum) { - m_exported = true; - m_openBtn->show(); - cancelBtn->hide(); - } - - m_state = ExportState::Idle; -} - -void VExporter::cancelExport() -{ - if (m_state == ExportState::Idle) { - reject(); - } else { - m_state = ExportState::Cancelled; + if (m_webViewer) { + delete m_webViewer; + m_webViewer = NULL; } } -bool VExporter::exportToPDF(VWebView *p_webViewer, const QString &p_filePath, +bool VExporter::exportToPDF(VWebView *p_webViewer, + const QString &p_filePath, const QPageLayout &p_layout) { int pdfPrinted = 0; @@ -411,16 +194,3 @@ bool VExporter::exportToPDF(VWebView *p_webViewer, const QString &p_filePath, return pdfPrinted == 1; } -void VExporter::enableUserInput(bool p_enabled) -{ - m_btnBox->button(QDialogButtonBox::Ok)->setEnabled(p_enabled); - m_pathEdit->setEnabled(p_enabled); - m_browseBtn->setEnabled(p_enabled); - m_layoutBtn->setEnabled(p_enabled); -} - -void VExporter::openTargetPath() const -{ - QUrl url = QUrl::fromLocalFile(VUtils::basePathFromPath(getFilePath())); - QDesktopServices::openUrl(url); -} diff --git a/src/vexporter.h b/src/vexporter.h index 88534820..0bf81b93 100644 --- a/src/vexporter.h +++ b/src/vexporter.h @@ -1,51 +1,32 @@ #ifndef VEXPORTER_H #define VEXPORTER_H -#include +#include #include -#include -#include "vconfigmanager.h" +#include "dialog/vexportdialog.h" + +class QWidget; class VWebView; -class VFile; -class VLineEdit; -class QLabel; -class QDialogButtonBox; -class QPushButton; -class QProgressBar; -enum class ExportType +class VExporter : public QObject { - PDF = 0, - HTML -}; - -class VExporter : public QDialog -{ - Q_OBJECT public: - explicit VExporter(MarkdownConverterType p_mdType = MarkdownIt, QWidget *p_parent = 0); + explicit VExporter(QWidget *p_parent = nullptr); - void exportNote(VFile *p_file, ExportType p_type); + void prepareExport(const ExportOption &p_opt); + + bool exportPDF(VFile *p_file, + const ExportOption &p_opt, + const QString &p_outputFile, + QString *p_errMsg = NULL); private slots: - void handleBrowseBtnClicked(); - void handleLayoutBtnClicked(); - void startExport(); - void cancelExport(); void handleLogicsFinished(); + void handleLoadFinished(bool p_ok); - void openTargetPath() const; private: - enum class ExportSource - { - Note = 0, - Directory, - Notebook, - Invalid - }; - enum class ExportState { Idle = 0, @@ -55,6 +36,7 @@ private: Successful }; + enum NoteState { NotReady = 0, @@ -64,59 +46,46 @@ private: Failed = 0x4 }; - void setupUI(); - void initMarkdownTemplate(); - - void updatePageLayoutLabel(); - - void setFilePath(const QString &p_path); - - QString getFilePath() const; - - void initWebViewer(VFile *p_file); + void initWebViewer(VFile *p_file, const ExportOption &p_opt); void clearWebViewer(); - void enableUserInput(bool p_enabled); - - bool exportToPDF(VWebView *p_webViewer, const QString &p_filePath, const QPageLayout &p_layout); - void clearNoteState(); + bool isNoteStateReady() const; + bool isNoteStateFailed() const; + bool exportToPDF(VWebView *p_webViewer, + const QString &p_filePath, + const QPageLayout &p_layout); + + QPageLayout m_pageLayout; + // Will be allocated and free for each conversion. VWebView *m_webViewer; - MarkdownConverterType m_mdType; QString m_htmlTemplate; - VFile *m_file; - ExportType m_type; - ExportSource m_source; + NoteState m_noteState; ExportState m_state; - - QLabel *m_infoLabel; - VLineEdit *m_pathEdit; - QPushButton *m_browseBtn; - QLabel *m_layoutLabel; - QPushButton *m_layoutBtn; - QDialogButtonBox *m_btnBox; - QPushButton *m_openBtn; - - // Progress label and bar. - QLabel *m_proLabel; - QProgressBar *m_proBar; - - QPageLayout m_pageLayout; - - // Whether a PDF has been exported. - bool m_exported; - - // The default directory. - static QString s_defaultPathDir; }; +inline void VExporter::clearNoteState() +{ + m_noteState = NoteState::NotReady; +} + +inline bool VExporter::isNoteStateReady() const +{ + return m_noteState == NoteState::Ready; +} + +inline bool VExporter::isNoteStateFailed() const +{ + return m_noteState & NoteState::Failed; +} + #endif // VEXPORTER_H diff --git a/src/vmainwindow.cpp b/src/vmainwindow.cpp index 19b7985f..5fe90cfe 100644 --- a/src/vmainwindow.cpp +++ b/src/vmainwindow.cpp @@ -37,6 +37,7 @@ #include "utils/viconutils.h" #include "dialog/vtipsdialog.h" #include "vcart.h" +#include "dialog/vexportdialog.h" extern VConfigManager *g_config; @@ -994,13 +995,12 @@ void VMainWindow::initFileMenu() fileMenu->addSeparator(); // Export as PDF. - m_exportAsPDFAct = new QAction(tr("Export As &PDF"), this); - m_exportAsPDFAct->setToolTip(tr("Export current note as PDF file")); - connect(m_exportAsPDFAct, &QAction::triggered, - this, &VMainWindow::exportAsPDF); - m_exportAsPDFAct->setEnabled(false); + m_exportAct = new QAction(tr("E&xport"), this); + m_exportAct->setToolTip(tr("Export notes")); + connect(m_exportAct, &QAction::triggered, + this, &VMainWindow::handleExportAct); - fileMenu->addAction(m_exportAsPDFAct); + fileMenu->addAction(m_exportAct); // Print. m_printAct = new QAction(VIconUtils::menuIcon(":/resources/icons/print.svg"), @@ -1349,8 +1349,6 @@ void VMainWindow::changeMarkdownConverter(QAction *action) MarkdownConverterType type = (MarkdownConverterType)action->data().toInt(); - qDebug() << "switch to converter" << type; - g_config->setMarkdownConverterType(type); } @@ -1418,7 +1416,7 @@ void VMainWindow::setEditorBackgroundColor(QAction *action) void VMainWindow::initConverterMenu(QMenu *p_menu) { - QMenu *converterMenu = p_menu->addMenu(tr("&Converter")); + QMenu *converterMenu = p_menu->addMenu(tr("&Renderer")); converterMenu->setToolTipsVisible(true); QActionGroup *converterAct = new QActionGroup(this); @@ -1828,7 +1826,6 @@ void VMainWindow::updateActionsStateFromTab(const VEditTab *p_tab) && dynamic_cast(file)->isSystemFile(); m_printAct->setEnabled(file && file->getDocType() == DocType::Markdown); - m_exportAsPDFAct->setEnabled(file && file->getDocType() == DocType::Markdown); updateEditReadAct(p_tab); @@ -2380,19 +2377,6 @@ void VMainWindow::printNote() } } -void VMainWindow::exportAsPDF() -{ - V_ASSERT(m_curTab); - V_ASSERT(m_curFile); - - if (m_curFile->getDocType() == DocType::Markdown) { - VMdTab *mdTab = dynamic_cast((VEditTab *)m_curTab); - VExporter exporter(mdTab->getMarkdownConverterType(), this); - exporter.exportNote(m_curFile, ExportType::PDF); - exporter.exec(); - } -} - QAction *VMainWindow::newAction(const QIcon &p_icon, const QString &p_text, QObject *p_parent) @@ -3015,3 +2999,19 @@ void VMainWindow::updateEditReadAct(const VEditTab *p_tab) m_editReadAct->setEnabled(p_tab); } + +void VMainWindow::handleExportAct() +{ + VExportDialog dialog(notebookSelector->currentNotebook(), + directoryTree->currentDirectory(), + m_curFile, + m_cart, + g_config->getMdConverterType(), + this); + dialog.exec(); +} + +VNotebook *VMainWindow::getCurrentNotebook() const +{ + return notebookSelector->currentNotebook(); +} diff --git a/src/vmainwindow.h b/src/vmainwindow.h index edfc021d..19f0ff2b 100644 --- a/src/vmainwindow.h +++ b/src/vmainwindow.h @@ -97,6 +97,8 @@ public: VEditTab *getCurrentTab() const; + VNotebook *getCurrentNotebook() const; + signals: // Emit when editor related configurations were changed by user. void editorConfigUpdated(); @@ -139,7 +141,9 @@ private slots: void enableImageConstraint(bool p_checked); void enableImageCaption(bool p_checked); void printNote(); - void exportAsPDF(); + + // Open export dialog. + void handleExportAct(); // Set the panel view properly. void enableCompactMode(bool p_enabled); @@ -349,7 +353,7 @@ private: QAction *m_printAct; - QAction *m_exportAsPDFAct; + QAction *m_exportAct; QAction *m_findReplaceAct; diff --git a/src/vnote.cpp b/src/vnote.cpp index 8a5059bf..8b74dcd6 100644 --- a/src/vnote.cpp +++ b/src/vnote.cpp @@ -10,7 +10,6 @@ #include "vnote.h" #include "utils/vutils.h" #include "vconfigmanager.h" -#include "vmainwindow.h" #include "vorphanfile.h" #include "vnotefile.h" #include "vpalette.h" @@ -238,15 +237,7 @@ VOrphanFile *VNote::getOrphanFile(const QString &p_path, bool p_modifiable, bool } } - for (int i = 0; i < m_externalFiles.size(); ++i) { - VOrphanFile *file = m_externalFiles[i]; - if (!file->isOpened()) { - qDebug() << "release orphan file" << file; - m_externalFiles.removeAt(i); - delete file; - --i; - } - } + freeOrphanFiles(); // Create a VOrphanFile for path. VOrphanFile *file = new VOrphanFile(this, path, p_modifiable, p_systemFile); @@ -296,3 +287,17 @@ VDirectory *VNote::getInternalDirectory(const QString &p_path) return dir; } + +void VNote::freeOrphanFiles() +{ + for (int i = 0; i < m_externalFiles.size();) { + VOrphanFile *file = m_externalFiles[i]; + if (!file->isOpened()) { + qDebug() << "release orphan file" << file; + m_externalFiles.removeAt(i); + delete file; + } else { + ++i; + } + } +} diff --git a/src/vnote.h b/src/vnote.h index 5be9d5e6..6c15194c 100644 --- a/src/vnote.h +++ b/src/vnote.h @@ -99,6 +99,8 @@ public: // Otherwise, returns NULL. VDirectory *getInternalDirectory(const QString &p_path); + void freeOrphanFiles(); + public slots: void updateTemplate(); diff --git a/src/vnotebookselector.cpp b/src/vnotebookselector.cpp index fc935912..c85bfaab 100644 --- a/src/vnotebookselector.cpp +++ b/src/vnotebookselector.cpp @@ -621,3 +621,8 @@ VNotebook *VNotebookSelector::getNotebook(const QListWidgetItem *p_item) const return NULL; } + +VNotebook *VNotebookSelector::currentNotebook() const +{ + return getNotebook(currentIndex()); +} diff --git a/src/vnotebookselector.h b/src/vnotebookselector.h index cfb875d4..846c9c3e 100644 --- a/src/vnotebookselector.h +++ b/src/vnotebookselector.h @@ -27,6 +27,8 @@ public: // Add notebook on popup if no notebooks currently. void showPopup() Q_DECL_OVERRIDE; + VNotebook *currentNotebook() const; + // Implementations for VNavigationMode. void registerNavigation(QChar p_majorKey) Q_DECL_OVERRIDE; void showNavigation() Q_DECL_OVERRIDE; diff --git a/src/vnotefile.cpp b/src/vnotefile.cpp index 09f96a02..cde82c56 100644 --- a/src/vnotefile.cpp +++ b/src/vnotefile.cpp @@ -8,7 +8,6 @@ #include #include -#include "utils/vutils.h" #include "vdirectory.h" VNoteFile::VNoteFile(VDirectory *p_directory, @@ -510,42 +509,12 @@ bool VNoteFile::copyFile(VDirectory *p_destDir, } // Copy images. - QDir parentDir(destFile->fetchBasePath()); - QSet 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)); - ret = false; - continue; - } - - QString imageFolder = VUtils::directoryNameFromPath(VUtils::basePathFromPath(link.m_path)); - QString destImagePath = QDir(parentDir.filePath(imageFolder)).filePath(VUtils::fileNameFromPath(link.m_path)); - - if (VUtils::equalPath(link.m_path, destImagePath)) { - VUtils::addErrMsg(p_errMsg, tr("Skip image with the same source and target path %1.") - .arg(link.m_path)); - ret = false; - continue; - } - - if (!VUtils::copyFile(link.m_path, destImagePath, p_isCut)) { - VUtils::addErrMsg(p_errMsg, tr("Fail to %1 image %2 to %3. " - "Please manually %1 it and modify the note.") - .arg(opStr).arg(link.m_path).arg(destImagePath)); - ret = false; - } else { - ++nrImageCopied; - qDebug() << opStr << "image" << link.m_path << "to" << destImagePath; - } + if (!copyInternalImages(images, + destFile->fetchBasePath(), + p_isCut, + &nrImageCopied, + p_errMsg)) { + ret = false; } // Copy attachment folder. @@ -587,3 +556,53 @@ bool VNoteFile::copyFile(VDirectory *p_destDir, return ret; } +bool VNoteFile::copyInternalImages(const QVector &p_images, + const QString &p_destDirPath, + bool p_isCut, + int *p_nrImageCopied, + QString *p_errMsg) +{ + bool ret = true; + QDir parentDir(p_destDirPath); + QSet processedImages; + QString opStr = p_isCut ? tr("cut") : tr("copy"); + int nrImageCopied = 0; + for (int i = 0; i < p_images.size(); ++i) { + const ImageLink &link = p_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)); + ret = false; + continue; + } + + QString imageFolder = VUtils::directoryNameFromPath(VUtils::basePathFromPath(link.m_path)); + QString destImagePath = QDir(parentDir.filePath(imageFolder)).filePath(VUtils::fileNameFromPath(link.m_path)); + + if (VUtils::equalPath(link.m_path, destImagePath)) { + VUtils::addErrMsg(p_errMsg, tr("Skip image with the same source and target path %1.") + .arg(link.m_path)); + ret = false; + continue; + } + + if (!VUtils::copyFile(link.m_path, destImagePath, p_isCut)) { + VUtils::addErrMsg(p_errMsg, tr("Fail to %1 image %2 to %3. " + "Please manually %1 it and modify the note.") + .arg(opStr).arg(link.m_path).arg(destImagePath)); + ret = false; + } else { + ++nrImageCopied; + qDebug() << opStr << "image" << link.m_path << "to" << destImagePath; + } + } + + *p_nrImageCopied = nrImageCopied; + return ret; +} diff --git a/src/vnotefile.h b/src/vnotefile.h index 96d3a344..0f615217 100644 --- a/src/vnotefile.h +++ b/src/vnotefile.h @@ -5,6 +5,7 @@ #include #include "vfile.h" +#include "utils/vutils.h" class VDirectory; class VNotebook; @@ -129,6 +130,13 @@ public: VNoteFile **p_targetFile, QString *p_errMsg = NULL); + // Copy images @p_images of a file to @p_destDirPath. + static bool copyInternalImages(const QVector &p_images, + const QString &p_destDirPath, + bool p_isCut, + int *p_nrImageCopied, + QString *p_errMsg = NULL); + private: // Delete internal images of this file. // Return true only when all internal images were deleted successfully.