exportdialog.cpp 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988
  1. #include "exportdialog.h"
  2. #include <QGroupBox>
  3. #include <QFormLayout>
  4. #include <QVBoxLayout>
  5. #include <QHBoxLayout>
  6. #include <QLabel>
  7. #include <QPushButton>
  8. #include <QCheckBox>
  9. #include <QLineEdit>
  10. #include <QProgressBar>
  11. #include <QFileInfo>
  12. #include <QFileDialog>
  13. #include <QUrl>
  14. #include <QPlainTextEdit>
  15. #include <QCoreApplication>
  16. #include <QPrinter>
  17. #include <QPageSetupDialog>
  18. #include <QPageLayout>
  19. #include <QInputDialog>
  20. #include <notebook/notebook.h>
  21. #include <notebook/node.h>
  22. #include <buffer/buffer.h>
  23. #include <widgets/widgetsfactory.h>
  24. #include <core/thememgr.h>
  25. #include <core/configmgr.h>
  26. #include <core/sessionconfig.h>
  27. #include <core/vnotex.h>
  28. #include <utils/widgetutils.h>
  29. #include <utils/fileutils.h>
  30. #include <utils/pathutils.h>
  31. #include <utils/clipboardutils.h>
  32. #include <export/exporter.h>
  33. #include <widgets/locationinputwithbrowsebutton.h>
  34. #include <widgets/messageboxhelper.h>
  35. #include <widgets/combobox.h>
  36. using namespace vnotex;
  37. ExportDialog::ExportDialog(Notebook *p_notebook,
  38. Node *p_folder,
  39. Node *p_note,
  40. Buffer *p_buffer,
  41. QWidget *p_parent)
  42. : ScrollDialog(p_parent),
  43. m_notebook(p_notebook),
  44. m_folder(p_folder),
  45. m_note(p_note),
  46. m_buffer(p_buffer)
  47. {
  48. setupUI();
  49. initOptions();
  50. restoreFields(m_option);
  51. setWindowFlags(windowFlags() | Qt::WindowMinimizeButtonHint);
  52. connect(this, &QDialog::finished,
  53. this, [this]() {
  54. saveFields(m_option);
  55. ConfigMgr::getInst().getSessionConfig().setExportOption(m_option);
  56. });
  57. }
  58. void ExportDialog::setupUI()
  59. {
  60. auto widget = new QWidget(this);
  61. setCentralWidget(widget);
  62. auto mainLayout = new QVBoxLayout(widget);
  63. auto sourceBox = setupSourceGroup(widget);
  64. mainLayout->addWidget(sourceBox);
  65. auto targetBox = setupTargetGroup(widget);
  66. mainLayout->addWidget(targetBox);
  67. m_advancedGroupBox = setupAdvancedGroup(widget);
  68. mainLayout->addWidget(m_advancedGroupBox);
  69. m_progressBar = new QProgressBar(widget);
  70. m_progressBar->setRange(0, 0);
  71. m_progressBar->hide();
  72. addBottomWidget(m_progressBar);
  73. setupButtonBox();
  74. setWindowTitle(tr("Export"));
  75. }
  76. QGroupBox *ExportDialog::setupSourceGroup(QWidget *p_parent)
  77. {
  78. auto box = new QGroupBox(tr("Source"), p_parent);
  79. auto layout = WidgetsFactory::createFormLayout(box);
  80. {
  81. m_sourceComboBox = WidgetsFactory::createComboBox(box);
  82. if (m_buffer) {
  83. m_sourceComboBox->addItem(tr("Current Buffer (%1)").arg(m_buffer->getName()),
  84. static_cast<int>(ExportSource::CurrentBuffer));
  85. }
  86. if (m_note && m_note->hasContent()) {
  87. m_sourceComboBox->addItem(tr("Current Note (%1)").arg(m_note->getName()),
  88. static_cast<int>(ExportSource::CurrentNote));
  89. }
  90. if (m_folder && m_folder->isContainer()) {
  91. m_sourceComboBox->addItem(tr("Current Folder (%1)").arg(m_folder->getName()),
  92. static_cast<int>(ExportSource::CurrentFolder));
  93. }
  94. if (m_notebook) {
  95. m_sourceComboBox->addItem(tr("Current Notebook (%1)").arg(m_notebook->getName()),
  96. static_cast<int>(ExportSource::CurrentNotebook));
  97. }
  98. layout->addRow(tr("Source:"), m_sourceComboBox);
  99. }
  100. {
  101. // TODO: Source format filtering.
  102. }
  103. return box;
  104. }
  105. QString ExportDialog::getDefaultOutputDir() const
  106. {
  107. return PathUtils::concatenateFilePath(ConfigMgr::getDocumentOrHomePath(), tr("vnote_exports"));
  108. }
  109. QGroupBox *ExportDialog::setupTargetGroup(QWidget *p_parent)
  110. {
  111. auto box = new QGroupBox(tr("Target"), p_parent);
  112. auto layout = WidgetsFactory::createFormLayout(box);
  113. {
  114. m_targetFormatComboBox = WidgetsFactory::createComboBox(box);
  115. m_targetFormatComboBox->addItem(tr("Markdown"),
  116. static_cast<int>(ExportFormat::Markdown));
  117. m_targetFormatComboBox->addItem(tr("HTML"),
  118. static_cast<int>(ExportFormat::HTML));
  119. m_targetFormatComboBox->addItem(tr("PDF"),
  120. static_cast<int>(ExportFormat::PDF));
  121. m_targetFormatComboBox->addItem(tr("Custom"),
  122. static_cast<int>(ExportFormat::Custom));
  123. connect(m_targetFormatComboBox, QOverload<int>::of(&QComboBox::currentIndexChanged),
  124. this, [this]() {
  125. AdvancedSettings settings = AdvancedSettings::Max;
  126. int format = m_targetFormatComboBox->currentData().toInt();
  127. switch (format) {
  128. case static_cast<int>(ExportFormat::HTML):
  129. settings = AdvancedSettings::HTML;
  130. break;
  131. case static_cast<int>(ExportFormat::PDF):
  132. settings = AdvancedSettings::PDF;
  133. break;
  134. case static_cast<int>(ExportFormat::Custom):
  135. settings = AdvancedSettings::Custom;
  136. break;
  137. default:
  138. break;
  139. }
  140. showAdvancedSettings(settings);
  141. });
  142. layout->addRow(tr("Format:"), m_targetFormatComboBox);
  143. }
  144. {
  145. m_transparentBgCheckBox = WidgetsFactory::createCheckBox(tr("Use transparent background"), box);
  146. layout->addRow(m_transparentBgCheckBox);
  147. }
  148. {
  149. const auto webStyles = VNoteX::getInst().getThemeMgr().getWebStyles();
  150. m_renderingStyleComboBox = WidgetsFactory::createComboBox(box);
  151. layout->addRow(tr("Rendering style:"), m_renderingStyleComboBox);
  152. for (const auto &pa : webStyles) {
  153. m_renderingStyleComboBox->addItem(pa.first, pa.second);
  154. }
  155. m_syntaxHighlightStyleComboBox = WidgetsFactory::createComboBox(box);
  156. layout->addRow(tr("Syntax highlighting style:"), m_syntaxHighlightStyleComboBox);
  157. for (const auto &pa : webStyles) {
  158. m_syntaxHighlightStyleComboBox->addItem(pa.first, pa.second);
  159. }
  160. }
  161. {
  162. m_outputDirInput = new LocationInputWithBrowseButton(box);
  163. layout->addRow(tr("Output directory:"), m_outputDirInput);
  164. connect(m_outputDirInput, &LocationInputWithBrowseButton::clicked,
  165. this, [this]() {
  166. QString initPath = getOutputDir();
  167. if (!QFileInfo::exists(initPath)) {
  168. initPath = getDefaultOutputDir();
  169. }
  170. QString dirPath = QFileDialog::getExistingDirectory(this,
  171. tr("Select Export Output Directory"),
  172. initPath,
  173. QFileDialog::ShowDirsOnly
  174. | QFileDialog::DontResolveSymlinks);
  175. if (!dirPath.isEmpty()) {
  176. m_outputDirInput->setText(dirPath);
  177. }
  178. });
  179. }
  180. return box;
  181. }
  182. QGroupBox *ExportDialog::setupAdvancedGroup(QWidget *p_parent)
  183. {
  184. auto box = new QGroupBox(tr("Advanced"), p_parent);
  185. auto layout = new QVBoxLayout(box);
  186. m_advancedSettings.resize(AdvancedSettings::Max);
  187. m_advancedSettings[AdvancedSettings::General] = setupGeneralAdvancedSettings(box);
  188. layout->addWidget(m_advancedSettings[AdvancedSettings::General]);
  189. return box;
  190. }
  191. QWidget *ExportDialog::setupGeneralAdvancedSettings(QWidget *p_parent)
  192. {
  193. QWidget *widget = new QWidget(p_parent);
  194. auto layout = WidgetsFactory::createFormLayout(widget);
  195. layout->setContentsMargins(0, 0, 0, 0);
  196. {
  197. m_recursiveCheckBox = WidgetsFactory::createCheckBox(tr("Process sub-folders"), widget);
  198. layout->addRow(m_recursiveCheckBox);
  199. }
  200. {
  201. m_exportAttachmentsCheckBox = WidgetsFactory::createCheckBox(tr("Export attachments"), widget);
  202. layout->addRow(m_exportAttachmentsCheckBox);
  203. }
  204. return widget;
  205. }
  206. void ExportDialog::setupButtonBox()
  207. {
  208. setDialogButtonBox(QDialogButtonBox::Close);
  209. auto box = getDialogButtonBox();
  210. m_exportBtn = box->addButton(tr("Export"), QDialogButtonBox::ActionRole);
  211. connect(m_exportBtn, &QPushButton::clicked,
  212. this, &ExportDialog::startExport);
  213. m_openDirBtn = box->addButton(tr("Open Directory"), QDialogButtonBox::ActionRole);
  214. connect(m_openDirBtn, &QPushButton::clicked,
  215. this, [this]() {
  216. auto dir = getOutputDir();
  217. if (!dir.isEmpty()) {
  218. WidgetUtils::openUrlByDesktop(QUrl::fromLocalFile(dir));
  219. }
  220. });
  221. m_copyContentBtn = box->addButton(tr("Copy Content"), QDialogButtonBox::ActionRole);
  222. m_copyContentBtn->setToolTip(tr("Copy exported file content"));
  223. m_copyContentBtn->setEnabled(false);
  224. connect(m_copyContentBtn, &QPushButton::clicked,
  225. this, [this]() {
  226. if (m_exportedFile.isEmpty()) {
  227. return;
  228. }
  229. const auto content = FileUtils::readTextFile(m_exportedFile);
  230. if (!content.isEmpty()) {
  231. ClipboardUtils::setTextToClipboard(content);
  232. }
  233. });
  234. }
  235. QString ExportDialog::getOutputDir() const
  236. {
  237. return m_outputDirInput->text();
  238. }
  239. void ExportDialog::initOptions()
  240. {
  241. // Read it from config.
  242. const auto &sessionConfig = ConfigMgr::getInst().getSessionConfig();
  243. m_option = sessionConfig.getExportOption();
  244. m_customOptions = sessionConfig.getCustomExportOptions();
  245. const auto &theme = VNoteX::getInst().getThemeMgr().getCurrentTheme();
  246. m_option.m_renderingStyleFile = theme.getFile(Theme::File::WebStyleSheet);
  247. m_option.m_syntaxHighlightStyleFile = theme.getFile(Theme::File::HighlightStyleSheet);
  248. if (m_option.m_outputDir.isEmpty()) {
  249. m_option.m_outputDir = getDefaultOutputDir();
  250. }
  251. if (findCustomOption(m_option.m_customExport) == -1) {
  252. m_option.m_customExport = m_customOptions.isEmpty() ? QString() : m_customOptions[0].m_name;
  253. }
  254. }
  255. void ExportDialog::restoreFields(const ExportOption &p_option)
  256. {
  257. {
  258. int idx = m_sourceComboBox->findData(static_cast<int>(p_option.m_source));
  259. if (idx != -1) {
  260. m_sourceComboBox->setCurrentIndex(idx);
  261. }
  262. }
  263. {
  264. int idx = m_targetFormatComboBox->findData(static_cast<int>(p_option.m_targetFormat));
  265. if (idx != -1) {
  266. m_targetFormatComboBox->setCurrentIndex(idx);
  267. }
  268. }
  269. m_transparentBgCheckBox->setChecked(p_option.m_useTransparentBg);
  270. {
  271. int idx = m_renderingStyleComboBox->findData(p_option.m_renderingStyleFile);
  272. if (idx != -1) {
  273. m_renderingStyleComboBox->setCurrentIndex(idx);
  274. }
  275. }
  276. {
  277. int idx = m_syntaxHighlightStyleComboBox->findData(p_option.m_syntaxHighlightStyleFile);
  278. if (idx != -1) {
  279. m_syntaxHighlightStyleComboBox->setCurrentIndex(idx);
  280. }
  281. }
  282. m_outputDirInput->setText(p_option.m_outputDir);
  283. m_recursiveCheckBox->setChecked(p_option.m_recursive);
  284. m_exportAttachmentsCheckBox->setChecked(p_option.m_exportAttachments);
  285. }
  286. void ExportDialog::saveFields(ExportOption &p_option)
  287. {
  288. p_option.m_source = static_cast<ExportSource>(m_sourceComboBox->currentData().toInt());
  289. p_option.m_targetFormat = static_cast<ExportFormat>(m_targetFormatComboBox->currentData().toInt());
  290. p_option.m_useTransparentBg = m_transparentBgCheckBox->isChecked();
  291. p_option.m_renderingStyleFile = m_renderingStyleComboBox->currentData().toString();
  292. p_option.m_syntaxHighlightStyleFile = m_syntaxHighlightStyleComboBox->currentData().toString();
  293. p_option.m_outputDir = getOutputDir();
  294. p_option.m_recursive = m_recursiveCheckBox->isChecked();
  295. p_option.m_exportAttachments = m_exportAttachmentsCheckBox->isChecked();
  296. if (m_advancedSettings[AdvancedSettings::HTML]) {
  297. saveFields(p_option.m_htmlOption);
  298. }
  299. if (m_advancedSettings[AdvancedSettings::PDF]) {
  300. saveFields(p_option.m_pdfOption);
  301. }
  302. if (m_advancedSettings[AdvancedSettings::Custom]) {
  303. saveCustomFields(p_option);
  304. }
  305. }
  306. void ExportDialog::startExport()
  307. {
  308. if (m_exportOngoing) {
  309. return;
  310. }
  311. // On start.
  312. {
  313. clearInformationText();
  314. m_exportedFile.clear();
  315. m_exportOngoing = true;
  316. updateUIOnExport();
  317. }
  318. saveFields(m_option);
  319. int ret = doExport(m_option);
  320. appendLog(tr("%n file(s) exported", "", ret));
  321. // On end.
  322. {
  323. m_exportOngoing = false;
  324. updateUIOnExport();
  325. }
  326. }
  327. void ExportDialog::rejectedButtonClicked()
  328. {
  329. if (m_exportOngoing) {
  330. // Just cancel the export.
  331. appendLog(tr("Cancelling the export"));
  332. getExporter()->stop();
  333. } else {
  334. Dialog::rejectedButtonClicked();
  335. }
  336. }
  337. void ExportDialog::appendLog(const QString &p_log)
  338. {
  339. appendInformationText(">>> " + p_log);
  340. QCoreApplication::sendPostedEvents();
  341. }
  342. void ExportDialog::updateUIOnExport()
  343. {
  344. m_exportBtn->setEnabled(!m_exportOngoing);
  345. if (m_exportOngoing) {
  346. m_progressBar->setMaximum(0);
  347. m_progressBar->show();
  348. } else {
  349. m_progressBar->hide();
  350. }
  351. m_copyContentBtn->setEnabled(!m_exportedFile.isEmpty());
  352. }
  353. int ExportDialog::doExport(ExportOption p_option)
  354. {
  355. if (p_option.m_targetFormat == ExportFormat::PDF && p_option.m_pdfOption.m_useWkhtmltopdf) {
  356. // Check wkhtmltopdf executable.
  357. const auto &wkExePath = p_option.m_pdfOption.m_wkhtmltopdfExePath;
  358. if (wkExePath.isEmpty() || !QFileInfo::exists(wkExePath)) {
  359. appendLog(tr("Please specify a valid wkhtmltopdf executable file (%1)").arg(wkExePath));
  360. return 0;
  361. }
  362. p_option.m_transformSvgToPngEnabled = true;
  363. p_option.m_removeCodeToolBarEnabled = true;
  364. } else if (p_option.m_targetFormat == ExportFormat::Custom) {
  365. int optIdx = findCustomOption(p_option.m_customExport);
  366. if (optIdx == -1) {
  367. appendLog(tr("Please specify a valid scheme"));
  368. return 0;
  369. }
  370. p_option.m_customOption = &m_customOptions[optIdx];
  371. }
  372. int exportedFilesCount = 0;
  373. switch (p_option.m_source) {
  374. case ExportSource::CurrentBuffer:
  375. {
  376. Q_ASSERT(m_buffer);
  377. const auto outputFile = getExporter()->doExport(p_option, m_buffer);
  378. exportedFilesCount = outputFile.isEmpty() ? 0 : 1;
  379. if (exportedFilesCount == 1 && p_option.m_targetFormat == ExportFormat::HTML) {
  380. m_exportedFile = outputFile;
  381. }
  382. break;
  383. }
  384. case ExportSource::CurrentNote:
  385. {
  386. Q_ASSERT(m_note);
  387. const auto outputFile = getExporter()->doExport(p_option, m_note);
  388. exportedFilesCount = outputFile.isEmpty() ? 0 : 1;
  389. if (exportedFilesCount == 1 && p_option.m_targetFormat == ExportFormat::HTML) {
  390. m_exportedFile = outputFile;
  391. }
  392. break;
  393. }
  394. case ExportSource::CurrentFolder:
  395. {
  396. Q_ASSERT(m_folder);
  397. const auto outputFiles = getExporter()->doExportFolder(p_option, m_folder);
  398. exportedFilesCount = outputFiles.size();
  399. break;
  400. }
  401. case ExportSource::CurrentNotebook:
  402. {
  403. Q_ASSERT(m_notebook);
  404. const auto outputFiles = getExporter()->doExport(p_option, m_notebook);
  405. exportedFilesCount = outputFiles.size();
  406. break;
  407. }
  408. }
  409. return exportedFilesCount;
  410. }
  411. Exporter *ExportDialog::getExporter()
  412. {
  413. if (!m_exporter) {
  414. m_exporter = new Exporter(this);
  415. connect(m_exporter, &Exporter::progressUpdated,
  416. this, &ExportDialog::updateProgress);
  417. connect(m_exporter, &Exporter::logRequested,
  418. this, &ExportDialog::appendLog);
  419. }
  420. return m_exporter;
  421. }
  422. void ExportDialog::updateProgress(int p_val, int p_maximum)
  423. {
  424. if (p_maximum < m_progressBar->value()) {
  425. m_progressBar->setValue(p_val);
  426. m_progressBar->setMaximum(p_maximum);
  427. } else {
  428. m_progressBar->setMaximum(p_maximum);
  429. m_progressBar->setValue(p_val);
  430. }
  431. }
  432. QWidget *ExportDialog::getHtmlAdvancedSettings()
  433. {
  434. if (!m_advancedSettings[AdvancedSettings::HTML]) {
  435. // Setup HTML advanced settings.
  436. QWidget *widget = new QWidget(m_advancedGroupBox);
  437. auto layout = WidgetsFactory::createFormLayout(widget);
  438. layout->setContentsMargins(0, 0, 0, 0);
  439. {
  440. m_embedStylesCheckBox = WidgetsFactory::createCheckBox(tr("Embed styles"), widget);
  441. layout->addRow(m_embedStylesCheckBox);
  442. }
  443. {
  444. m_embedImagesCheckBox = WidgetsFactory::createCheckBox(tr("Embed images"), widget);
  445. layout->addRow(m_embedImagesCheckBox);
  446. }
  447. {
  448. m_completePageCheckBox = WidgetsFactory::createCheckBox(tr("Complete page"), widget);
  449. m_completePageCheckBox->setToolTip(tr("Export the whole page along with images which may change the links structure"));
  450. connect(m_completePageCheckBox, &QCheckBox::stateChanged,
  451. this, [this](int p_state) {
  452. bool checked = p_state == Qt::Checked;
  453. m_embedImagesCheckBox->setEnabled(checked);
  454. });
  455. layout->addRow(m_completePageCheckBox);
  456. }
  457. {
  458. m_useMimeHtmlFormatCheckBox = WidgetsFactory::createCheckBox(tr("Mime HTML format"), widget);
  459. connect(m_useMimeHtmlFormatCheckBox, &QCheckBox::stateChanged,
  460. this, [this](int p_state) {
  461. bool checked = p_state == Qt::Checked;
  462. m_embedStylesCheckBox->setEnabled(!checked);
  463. m_completePageCheckBox->setEnabled(!checked);
  464. });
  465. // TODO: do not support MHTML for now.
  466. m_useMimeHtmlFormatCheckBox->setEnabled(false);
  467. m_useMimeHtmlFormatCheckBox->hide();
  468. layout->addRow(m_useMimeHtmlFormatCheckBox);
  469. }
  470. {
  471. m_addOutlinePanelCheckBox = WidgetsFactory::createCheckBox(tr("Add outline panel"), widget);
  472. layout->addRow(m_addOutlinePanelCheckBox);
  473. }
  474. m_advancedGroupBox->layout()->addWidget(widget);
  475. m_advancedSettings[AdvancedSettings::HTML] = widget;
  476. restoreFields(m_option.m_htmlOption);
  477. }
  478. return m_advancedSettings[AdvancedSettings::HTML];
  479. }
  480. void ExportDialog::showAdvancedSettings(AdvancedSettings p_settings)
  481. {
  482. for (int i = AdvancedSettings::General + 1; i < m_advancedSettings.size(); ++i) {
  483. if (m_advancedSettings[i]) {
  484. m_advancedSettings[i]->hide();
  485. }
  486. }
  487. QWidget *widget = nullptr;
  488. switch (p_settings) {
  489. case AdvancedSettings::HTML:
  490. widget = getHtmlAdvancedSettings();
  491. break;
  492. case AdvancedSettings::PDF:
  493. widget = getPdfAdvancedSettings();
  494. break;
  495. case AdvancedSettings::Custom:
  496. widget = getCustomAdvancedSettings();
  497. break;
  498. default:
  499. break;
  500. }
  501. if (widget) {
  502. widget->show();
  503. }
  504. }
  505. void ExportDialog::restoreFields(const ExportHtmlOption &p_option)
  506. {
  507. m_embedStylesCheckBox->setChecked(p_option.m_embedStyles);
  508. m_embedImagesCheckBox->setChecked(p_option.m_embedImages);
  509. m_completePageCheckBox->setChecked(p_option.m_completePage);
  510. m_useMimeHtmlFormatCheckBox->setChecked(p_option.m_useMimeHtmlFormat);
  511. m_addOutlinePanelCheckBox->setChecked(p_option.m_addOutlinePanel);
  512. }
  513. void ExportDialog::saveFields(ExportHtmlOption &p_option)
  514. {
  515. p_option.m_embedStyles = m_embedStylesCheckBox->isChecked();
  516. p_option.m_embedImages = m_embedImagesCheckBox->isChecked();
  517. p_option.m_completePage = m_completePageCheckBox->isChecked();
  518. p_option.m_useMimeHtmlFormat = m_useMimeHtmlFormatCheckBox->isChecked();
  519. p_option.m_addOutlinePanel = m_addOutlinePanelCheckBox->isChecked();
  520. }
  521. QWidget *ExportDialog::getPdfAdvancedSettings()
  522. {
  523. if (!m_advancedSettings[AdvancedSettings::PDF]) {
  524. QWidget *widget = new QWidget(m_advancedGroupBox);
  525. auto layout = WidgetsFactory::createFormLayout(widget);
  526. layout->setContentsMargins(0, 0, 0, 0);
  527. {
  528. m_pageLayoutBtn = new QPushButton(tr("Settings"), widget);
  529. connect(m_pageLayoutBtn, &QPushButton::clicked,
  530. this, [this]() {
  531. QPrinter printer;
  532. printer.setPageLayout(*m_pageLayout);
  533. QPageSetupDialog dlg(&printer, this);
  534. if (dlg.exec() != QDialog::Accepted) {
  535. return;
  536. }
  537. m_pageLayout->setUnits(QPageLayout::Millimeter);
  538. m_pageLayout->setPageSize(printer.pageLayout().pageSize());
  539. m_pageLayout->setMargins(printer.pageLayout().margins(QPageLayout::Millimeter));
  540. m_pageLayout->setOrientation(printer.pageLayout().orientation());
  541. updatePageLayoutButtonLabel();
  542. });
  543. layout->addRow(tr("Page layout:"), m_pageLayoutBtn);
  544. }
  545. {
  546. m_addTableOfContentsCheckBox = WidgetsFactory::createCheckBox(tr("Add Table-of-Contents"), widget);
  547. layout->addRow(m_addTableOfContentsCheckBox);
  548. }
  549. {
  550. auto useLayout = new QHBoxLayout();
  551. m_useWkhtmltopdfCheckBox = WidgetsFactory::createCheckBox(tr("Use wkhtmltopdf (outline supported)"), widget);
  552. useLayout->addWidget(m_useWkhtmltopdfCheckBox);
  553. auto downloadBtn = new QPushButton(tr("Download"), widget);
  554. connect(downloadBtn, &QPushButton::clicked,
  555. this, []() {
  556. WidgetUtils::openUrlByDesktop(QUrl("https://wkhtmltopdf.org/downloads.html"));
  557. });
  558. useLayout->addWidget(downloadBtn);
  559. layout->addRow(useLayout);
  560. }
  561. {
  562. m_allInOneCheckBox = WidgetsFactory::createCheckBox(tr("All-in-One"), widget);
  563. m_allInOneCheckBox->setToolTip(tr("Export all source files into one file"));
  564. m_allInOneCheckBox->setEnabled(false);
  565. connect(m_useWkhtmltopdfCheckBox, &QCheckBox::stateChanged,
  566. this, [this](int p_state) {
  567. m_allInOneCheckBox->setEnabled(p_state == Qt::Checked);
  568. });
  569. layout->addRow(m_allInOneCheckBox);
  570. }
  571. {
  572. auto pathLayout = new QHBoxLayout();
  573. m_wkhtmltopdfExePathLineEdit = WidgetsFactory::createLineEdit(widget);
  574. pathLayout->addWidget(m_wkhtmltopdfExePathLineEdit);
  575. auto browseBtn = new QPushButton(tr("Browse"), widget);
  576. pathLayout->addWidget(browseBtn);
  577. connect(browseBtn, &QPushButton::clicked,
  578. this, [this]() {
  579. QString filePath = QFileDialog::getOpenFileName(this,
  580. tr("Select wkhtmltopdf Executable"),
  581. QCoreApplication::applicationDirPath());
  582. if (!filePath.isEmpty()) {
  583. m_wkhtmltopdfExePathLineEdit->setText(filePath);
  584. }
  585. });
  586. layout->addRow(tr("Wkhtmltopdf path:"), pathLayout);
  587. }
  588. {
  589. m_wkhtmltopdfArgsLineEdit = WidgetsFactory::createLineEdit(widget);
  590. layout->addRow(tr("Wkhtmltopdf arguments:"), m_wkhtmltopdfArgsLineEdit);
  591. }
  592. m_advancedGroupBox->layout()->addWidget(widget);
  593. m_advancedSettings[AdvancedSettings::PDF] = widget;
  594. restoreFields(m_option.m_pdfOption);
  595. }
  596. return m_advancedSettings[AdvancedSettings::PDF];
  597. }
  598. QWidget *ExportDialog::getCustomAdvancedSettings()
  599. {
  600. if (!m_advancedSettings[AdvancedSettings::Custom]) {
  601. QWidget *widget = new QWidget(m_advancedGroupBox);
  602. auto layout = WidgetsFactory::createFormLayout(widget);
  603. layout->setContentsMargins(0, 0, 0, 0);
  604. {
  605. auto schemeLayout = new QHBoxLayout();
  606. layout->addRow(tr("Scheme:"), schemeLayout);
  607. m_customExportComboBox = WidgetsFactory::createComboBox(widget);
  608. m_customExportComboBox->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
  609. schemeLayout->addWidget(m_customExportComboBox, 1);
  610. auto addBtn = new QPushButton(tr("New"), widget);
  611. addBtn->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
  612. connect(addBtn, &QPushButton::clicked,
  613. this, &ExportDialog::addCustomExportScheme);
  614. schemeLayout->addWidget(addBtn);
  615. auto delBtn = new QPushButton(tr("Delete"), widget);
  616. delBtn->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
  617. connect(delBtn, &QPushButton::clicked,
  618. this, &ExportDialog::removeCustomExportScheme);
  619. schemeLayout->addWidget(delBtn);
  620. }
  621. {
  622. m_targetSuffixLineEdit = WidgetsFactory::createLineEdit(widget);
  623. m_targetSuffixLineEdit->setToolTip(tr("Suffix of the target file like docs/pdf/epub"));
  624. m_targetSuffixLineEdit->setEnabled(false);
  625. layout->addRow(tr("Target file suffix:"), m_targetSuffixLineEdit);
  626. }
  627. {
  628. m_resourcePathSeparatorLineEdit = WidgetsFactory::createLineEdit(widget);
  629. m_resourcePathSeparatorLineEdit->setToolTip(tr("Separator used to concatenate resource folder paths"));
  630. m_resourcePathSeparatorLineEdit->setEnabled(false);
  631. layout->addRow(tr("Resource path separator:"), m_resourcePathSeparatorLineEdit);
  632. }
  633. {
  634. m_useHtmlInputCheckBox = WidgetsFactory::createCheckBox(tr("Use HTML format as input"), widget);
  635. m_useHtmlInputCheckBox->setToolTip(tr("Convert to HTMl format first as the input of the custom export command"));
  636. m_useHtmlInputCheckBox->setEnabled(false);
  637. layout->addRow(m_useHtmlInputCheckBox);
  638. }
  639. {
  640. m_allInOneCheckBox = WidgetsFactory::createCheckBox(tr("All-in-One"), widget);
  641. m_allInOneCheckBox->setToolTip(tr("Export all source files into one file"));
  642. m_allInOneCheckBox->setEnabled(false);
  643. layout->addRow(m_allInOneCheckBox);
  644. }
  645. {
  646. m_targetPageScrollableCheckBox = WidgetsFactory::createCheckBox(tr("Target page scrollable"), widget);
  647. m_targetPageScrollableCheckBox->setToolTip(tr("Whether the page of the target file is scrollable"));
  648. m_targetPageScrollableCheckBox->setEnabled(false);
  649. layout->addRow(m_targetPageScrollableCheckBox);
  650. }
  651. {
  652. auto usage = tr("Command:\n"
  653. "\t%1: List of input files.\n"
  654. "\t%2: List of paths to search for images and other resources.\n"
  655. "\t%3: Path of rendering CSS style sheet.\n"
  656. "\t%4: Path of syntax highlighting CSS style sheet.\n"
  657. "\t%5: Path of output file.");
  658. layout->addRow(new QLabel(usage, widget));
  659. }
  660. {
  661. m_commandTextEdit = WidgetsFactory::createPlainTextEdit(widget);
  662. #if defined(Q_OS_WIN)
  663. m_commandTextEdit->setPlaceholderText("pandoc.exe --resource-path=.;%2 --css=%3 --css=%4 -s -o %5 %1");
  664. #else
  665. m_commandTextEdit->setPlaceholderText("pandoc --resource-path=.:%2 --css=%3 --css=%4 -s -o %5 %1");
  666. #endif
  667. m_commandTextEdit->setMaximumHeight(m_commandTextEdit->minimumSizeHint().height());
  668. m_commandTextEdit->setEnabled(false);
  669. layout->addRow(m_commandTextEdit);
  670. }
  671. connect(m_customExportComboBox, QOverload<int>::of(&QComboBox::currentIndexChanged),
  672. this, &ExportDialog::customExportCurrentSchemeChanged);
  673. m_advancedGroupBox->layout()->addWidget(widget);
  674. m_advancedSettings[AdvancedSettings::Custom] = widget;
  675. restoreCustomFields(m_option);
  676. }
  677. return m_advancedSettings[AdvancedSettings::Custom];
  678. }
  679. void ExportDialog::restoreFields(const ExportPdfOption &p_option)
  680. {
  681. m_pageLayout = p_option.m_layout;
  682. updatePageLayoutButtonLabel();
  683. m_addTableOfContentsCheckBox->setChecked(p_option.m_addTableOfContents);
  684. m_useWkhtmltopdfCheckBox->setChecked(p_option.m_useWkhtmltopdf);
  685. m_allInOneCheckBox->setChecked(p_option.m_allInOne);
  686. m_wkhtmltopdfExePathLineEdit->setText(p_option.m_wkhtmltopdfExePath);
  687. m_wkhtmltopdfArgsLineEdit->setText(p_option.m_wkhtmltopdfArgs);
  688. }
  689. void ExportDialog::saveFields(ExportPdfOption &p_option)
  690. {
  691. p_option.m_layout = m_pageLayout;
  692. p_option.m_addTableOfContents = m_addTableOfContentsCheckBox->isChecked();
  693. p_option.m_useWkhtmltopdf = m_useWkhtmltopdfCheckBox->isChecked();
  694. p_option.m_allInOne = m_allInOneCheckBox->isChecked();
  695. p_option.m_wkhtmltopdfExePath = m_wkhtmltopdfExePathLineEdit->text();
  696. p_option.m_wkhtmltopdfArgs = m_wkhtmltopdfArgsLineEdit->text();
  697. }
  698. void ExportDialog::restoreCustomFields(const ExportOption &p_option)
  699. {
  700. m_customExportComboBox->clear();
  701. int curIndex = -1;
  702. for (int i = 0; i < m_customOptions.size(); ++i) {
  703. m_customExportComboBox->addItem(m_customOptions[i].m_name, m_customOptions[i].m_name);
  704. if (m_customOptions[i].m_name == p_option.m_customExport) {
  705. curIndex = i;
  706. }
  707. }
  708. m_customExportComboBox->setCurrentIndex(curIndex);
  709. }
  710. void ExportDialog::saveCustomFields(ExportOption &p_option)
  711. {
  712. p_option.m_customExport = m_customExportComboBox->currentData().toString();
  713. int idx = findCustomOption(p_option.m_customExport);
  714. if (idx > -1) {
  715. auto &opt = m_customOptions[idx];
  716. opt.m_targetSuffix = m_targetSuffixLineEdit->text();
  717. opt.m_resourcePathSeparator = m_resourcePathSeparatorLineEdit->text();
  718. opt.m_useHtmlInput = m_useHtmlInputCheckBox->isChecked();
  719. opt.m_allInOne = m_allInOneCheckBox->isChecked();
  720. opt.m_targetPageScrollable = m_targetPageScrollableCheckBox->isChecked();
  721. opt.m_command = m_commandTextEdit->toPlainText().trimmed();
  722. int lineIdx = opt.m_command.indexOf(QLatin1Char('\n'));
  723. if (lineIdx > -1) {
  724. opt.m_command = opt.m_command.left(lineIdx);
  725. }
  726. ConfigMgr::getInst().getSessionConfig().setCustomExportOptions(m_customOptions);
  727. }
  728. }
  729. void ExportDialog::updatePageLayoutButtonLabel()
  730. {
  731. Q_ASSERT(m_pageLayout);
  732. m_pageLayoutBtn->setText(
  733. QString("%1, %2").arg(m_pageLayout->pageSize().name(),
  734. m_pageLayout->orientation() == QPageLayout::Portrait ? tr("Portrait") : tr("Landscape")));
  735. }
  736. int ExportDialog::findCustomOption(const QString &p_name) const
  737. {
  738. if (p_name.isEmpty()) {
  739. return -1;
  740. }
  741. for (int i = 0; i < m_customOptions.size(); ++i) {
  742. if (m_customOptions[i].m_name == p_name) {
  743. return i;
  744. }
  745. }
  746. return -1;
  747. }
  748. void ExportDialog::addCustomExportScheme()
  749. {
  750. QString name;
  751. while (true) {
  752. name = QInputDialog::getText(this, tr("New Custom Export Scheme"), tr("Scheme name:"));
  753. if (name.isEmpty()) {
  754. return;
  755. }
  756. if (findCustomOption(name) != -1) {
  757. MessageBoxHelper::notify(MessageBoxHelper::Warning,
  758. tr("Name conflicts with existing scheme."),
  759. this);
  760. } else {
  761. break;
  762. }
  763. }
  764. // Based on current scheme.
  765. ExportCustomOption newOption;
  766. {
  767. int curIndex = findCustomOption(m_customExportComboBox->currentData().toString());
  768. if (curIndex > -1) {
  769. newOption = m_customOptions[curIndex];
  770. }
  771. }
  772. newOption.m_name = name;
  773. m_customOptions.append(newOption);
  774. ConfigMgr::getInst().getSessionConfig().setCustomExportOptions(m_customOptions);
  775. // Add it to combo box.
  776. m_customExportComboBox->addItem(name, name);
  777. m_customExportComboBox->setCurrentIndex(m_customExportComboBox->findData(name));
  778. }
  779. void ExportDialog::removeCustomExportScheme()
  780. {
  781. auto name = m_customExportComboBox->currentData().toString();
  782. if (name.isEmpty()) {
  783. return;
  784. }
  785. int ret = MessageBoxHelper::questionOkCancel(MessageBoxHelper::Warning,
  786. tr("Delete scheme (%1)?").arg(name),
  787. QString(),
  788. QString(),
  789. this);
  790. if (ret != QMessageBox::Ok) {
  791. return;
  792. }
  793. int idx = findCustomOption(name);
  794. Q_ASSERT(idx > -1);
  795. m_customOptions.remove(idx);
  796. ConfigMgr::getInst().getSessionConfig().setCustomExportOptions(m_customOptions);
  797. m_customExportComboBox->removeItem(m_customExportComboBox->currentIndex());
  798. }
  799. void ExportDialog::customExportCurrentSchemeChanged(int p_comboIdx)
  800. {
  801. const bool enabled = p_comboIdx >= 0;
  802. m_targetSuffixLineEdit->setEnabled(enabled);
  803. m_resourcePathSeparatorLineEdit->setEnabled(enabled);
  804. m_useHtmlInputCheckBox->setEnabled(enabled);
  805. m_allInOneCheckBox->setEnabled(enabled);
  806. m_targetPageScrollableCheckBox->setEnabled(enabled);
  807. m_commandTextEdit->setEnabled(enabled);
  808. if (p_comboIdx < 0) {
  809. m_option.m_customExport.clear();
  810. return;
  811. }
  812. auto name = m_customExportComboBox->currentData().toString();
  813. m_option.m_customExport = name;
  814. int curIndex = findCustomOption(name);
  815. Q_ASSERT(curIndex > -1);
  816. const auto &opt = m_customOptions[curIndex];
  817. m_targetSuffixLineEdit->setText(opt.m_targetSuffix);
  818. m_resourcePathSeparatorLineEdit->setText(opt.m_resourcePathSeparator);
  819. m_useHtmlInputCheckBox->setChecked(opt.m_useHtmlInput);
  820. m_allInOneCheckBox->setChecked(opt.m_allInOne);
  821. m_targetPageScrollableCheckBox->setChecked(opt.m_targetPageScrollable);
  822. m_commandTextEdit->setPlainText(opt.m_command);
  823. }