BibleTime
cexportmanager.cpp
Go to the documentation of this file.
1 /*********
2 *
3 * In the name of the Father, and of the Son, and of the Holy Spirit.
4 *
5 * This file is part of BibleTime's source code, https://bibletime.info/
6 *
7 * Copyright 1999-2021 by the BibleTime developers.
8 * The BibleTime source code is licensed under the GNU General Public License
9 * version 2.0.
10 *
11 **********/
12 
13 #include "cexportmanager.h"
14 
15 #include <QApplication>
16 #include <QClipboard>
17 #include <QFileDialog>
18 #include <QList>
19 #include <QProgressDialog>
20 #include <QTextStream>
21 #include "../backend/drivers/cswordmoduleinfo.h"
22 #include "../backend/keys/cswordkey.h"
23 #include "../backend/keys/cswordversekey.h"
24 #include "../backend/managers/referencemanager.h"
25 #include "../backend/rendering/cplaintextexportrendering.h"
26 #include "../backend/rendering/ctextrendering.h"
27 #include "../util/btassert.h"
28 #include "../util/tool.h"
29 #include "btprinter.h"
30 
31 // Sword includes:
32 #include <swkey.h>
33 #include <listkey.h>
34 
35 
36 using namespace Rendering;
37 
39 
40 CExportManager::CExportManager(bool const showProgress,
41  QString const & progressLabel,
42  FilterOptions const & filterOptions,
43  DisplayOptions const & displayOptions)
44  : m_filterOptions(filterOptions)
45  , m_displayOptions(displayOptions)
46  , m_progressDialog(
47  showProgress
48  ? [&progressLabel]{
49  auto dialog =
50  std::make_unique<QProgressDialog>(nullptr, Qt::Dialog);
51  dialog->setWindowTitle(QStringLiteral("BibleTime"));
52  dialog->setLabelText(progressLabel);
53  return dialog;
54  }()
55  : nullptr)
56 {}
57 
59 
60 bool CExportManager::saveKey(CSwordKey const * const key,
61  Format const format,
62  bool const addText,
63  const BtConstModuleList& modules)
64 {
65  if (!key || !key->module())
66  return false;
67  QString const filename = getSaveFileName(format);
68  if (filename.isEmpty())
69  return false;
70 
71  QString text;
72  {
73  CSwordVerseKey const * const vk =
74  dynamic_cast<CSwordVerseKey const *>(key);
75  auto const render = newRenderer(format, addText);
76  if (vk && vk->isBoundSet()) {
77  text = render->renderKeyRange(vk->lowerBound(),
78  vk->upperBound(),
79  modules);
80  text.replace(QStringLiteral("#CHAPTERTITLE#"),
81  QStringLiteral("%1 %2")
82  .arg(vk->bookName())
83  .arg(QString::number(vk->chapter())));
84  text.replace(QStringLiteral("#TEXT_ALIGN#"),
85  QStringLiteral("right"));
86  } else { // no range supported
87  text = render->renderSingleKey(key->key(), modules);
88  }
89  }
90  util::tool::savePlainFile(filename, text);
91  return true;
92 }
93 
95  CSwordModuleInfo const * module,
96  Format const format,
97  bool const addText)
98 {
99  if (l.empty())
100  return false;
101 
102  QString const filename = getSaveFileName(format);
103  if (filename.isEmpty())
104  return false;
105 
106  CTextRendering::KeyTree tree; /// \todo Verify that items in tree are properly freed.
107 
108  setProgressRange(l.size()); /// \todo check size
109  KTI::Settings itemSettings;
110  itemSettings.highlight = false;
111 
112  for (auto const & keyPtr : l) {
113  if (progressWasCancelled())
114  return false;
115  tree.emplace_back(QString::fromLocal8Bit(keyPtr->getText()),
116  module,
117  itemSettings);
118  incProgress();
119  }
120 
121  QString const text = newRenderer(format, addText)->renderKeyTree(tree);
122  util::tool::savePlainFile(filename, text);
124  return true;
125 }
126 
128  Format const format,
129  bool const addText)
130 {
131  if (list.empty())
132  return false;
133 
134  const QString filename = getSaveFileName(format);
135  if (filename.isEmpty())
136  return false;
137 
138  CTextRendering::KeyTree tree; /// \todo Verify that items in tree are properly freed.
139 
140  KTI::Settings itemSettings;
141  itemSettings.highlight = false;
142 
143  setProgressRange(list.count());
144  for (CSwordKey const * const k : list) {
145  if (progressWasCancelled())
146  return false;
147  tree.emplace_back(k->key(), k->module(), itemSettings);
148  incProgress();
149  }
150 
151  QString const text = newRenderer(format, addText)->renderKeyTree(tree);
152  util::tool::savePlainFile(filename, text);
154  return true;
155 }
156 
157 namespace {
158 
159 template <typename Arg> inline void copyToClipboard(Arg && arg)
160 { QApplication::clipboard()->setText(std::forward<Arg>(arg)); }
161 
162 } // anonymous namespace
163 
164 bool CExportManager::copyKey(CSwordKey const * const key,
165  Format const format,
166  bool const addText)
167 {
168  if (!key || !key->module())
169  return false;
170 
171  QString text;
172  BtConstModuleList modules;
173  modules.append(key->module());
174 
175  {
176  auto const render = newRenderer(format, addText);
177  CSwordVerseKey const * const vk =
178  dynamic_cast<CSwordVerseKey const *>(key);
179  if (vk && vk->isBoundSet()) {
180  text = render->renderKeyRange(vk->lowerBound(),
181  vk->upperBound(),
182  modules);
183  } else { // no range supported
184  text = render->renderSingleKey(key->key(), modules);
185  }
186  }
187 
188  copyToClipboard(text);
189  return true;
190 }
191 
193  CSwordModuleInfo const * const module,
194  Format const format,
195  bool const addText)
196 {
197  if (l.empty())
198  return false;
199 
200  CTextRendering::KeyTree tree; /// \todo Verify that items in tree are properly freed.
201  KTI::Settings itemSettings;
202  itemSettings.highlight = false;
203 
204  for (auto const & keyPtr : l) {
205  if (progressWasCancelled())
206  return false;
207  tree.emplace_back(QString::fromLocal8Bit(keyPtr->getText()),
208  module,
209  itemSettings);
210  }
211 
212  copyToClipboard(newRenderer(format, addText)->renderKeyTree(tree));
214  return true;
215 }
216 
217 
219  Format const format,
220  bool const addText)
221 {
222  if (list.empty())
223  return false;
224 
225  CTextRendering::KeyTree tree; /// \todo Verify that items in tree are properly freed.
226  KTI::Settings itemSettings;
227  itemSettings.highlight = false;
228 
229  setProgressRange(list.count());
230  for (CSwordKey const * const k : list) {
231  if (progressWasCancelled())
232  return false;
233  tree.emplace_back(k->key(), k->module(), itemSettings);
234  incProgress();
235  }
236 
237  copyToClipboard(newRenderer(format, addText)->renderKeyTree(tree));
239  return true;
240 }
241 
242 namespace {
243 
245 
246  PrintSettings(DisplayOptions const & displayOptions)
247  : BtPrinter::KeyTreeItem::Settings{
248  false,
249  displayOptions.verseNumbers ? Settings::SimpleKey : Settings::NoKey}
250  {}
251 
252 };
253 
254 } // anonymous namespace
255 
256 bool CExportManager::printKey(CSwordKey const * const key,
257  DisplayOptions const & displayOptions,
258  FilterOptions const & filterOptions)
259 {
260  PrintSettings settings{displayOptions};
261  BtPrinter::KeyTree tree; /// \todo Verify that items in tree are properly freed.
262  tree.emplace_back(key->key(), key->module(), settings);
263  BtPrinter{displayOptions, filterOptions}.printKeyTree(tree);
264  return true;
265 }
266 
267 bool CExportManager::printKey(CSwordModuleInfo const * const module,
268  QString const & startKey,
269  QString const & stopKey,
270  DisplayOptions const & displayOptions,
271  FilterOptions const & filterOptions)
272 {
273  PrintSettings settings{displayOptions};
274  BtPrinter::KeyTree tree;
275  if (startKey != stopKey) {
276  tree.emplace_back(startKey, stopKey, module, settings);
277  } else {
278  tree.emplace_back(startKey, module, settings);
279  }
280  BtPrinter{displayOptions, filterOptions}.printKeyTree(tree);
281  return true;
282 }
283 
284 bool CExportManager::printByHyperlink(QString const & hyperlink,
285  DisplayOptions const & displayOptions,
286  FilterOptions const & filterOptions)
287 {
288  auto const decodedLink(ReferenceManager::decodeHyperlink(hyperlink));
289  if (!decodedLink && !decodedLink->module)
290  return false;
291  auto const * const module = decodedLink->module;
292  auto const & keyName = decodedLink->key;
293 
294  BtPrinter::KeyTree tree; /// \todo Verify that items in tree are properly freed.
295  PrintSettings settings{displayOptions};
296  //check if we have a range of entries or a single one
297  if ((module->type() == CSwordModuleInfo::Bible)
298  || (module->type() == CSwordModuleInfo::Commentary))
299  {
300  sword::ListKey const verses =
301  sword::VerseKey().parseVerseList(
302  keyName.toUtf8().constData(),
303  "Genesis 1:1",
304  true);
305 
306  for (int i = 0; i < verses.getCount(); i++) {
307  if (sword::VerseKey const * const element =
308  dynamic_cast<sword::VerseKey const *>(verses.getElement(i)))
309  {
310  tree.emplace_back(
311  QString::fromUtf8(
312  element->getLowerBound().getText()),
313  QString::fromUtf8(
314  element->getUpperBound().getText()),
315  module,
316  settings);
317  } else if (verses.getElement(i)) {
318  tree.emplace_back(
319  QString::fromUtf8(verses.getElement(i)->getText()),
320  module,
321  settings);
322  }
323  }
324  } else {
325  tree.emplace_back(keyName, module, settings);
326  }
327  BtPrinter{displayOptions, filterOptions}.printKeyTree(tree);
328  return true;
329 }
330 
333  CSwordModuleInfo const * const module,
334  DisplayOptions const & displayOptions,
335  FilterOptions const & filterOptions)
336 {
337  if (list.empty())
338  return false;
339  PrintSettings settings{displayOptions};
340  BtPrinter::KeyTree tree; /// \todo Verify that items in tree are properly freed.
341 
342  setProgressRange(list.size());
343  for (auto const & keyPtr : list) {
344  if (progressWasCancelled())
345  return false;
346  QString const key = keyPtr->getText();
347  tree.emplace_back(key, key, module, settings);
348  incProgress();
349  }
350  BtPrinter{displayOptions, filterOptions}.printKeyTree(tree);
352  return true;
353 }
354 
355 bool CExportManager::printKeyList(QStringList const & list,
356  CSwordModuleInfo const * const module,
357  DisplayOptions const & displayOptions,
358  FilterOptions const & filterOptions)
359 {
360  if (list.empty())
361  return false;
362 
363  PrintSettings settings{displayOptions};
364  BtPrinter::KeyTree tree; /// \todo Verify that items in tree are properly freed.
365 
366  setProgressRange(list.count());
367  for (QString const & key: list) {
368  if (progressWasCancelled())
369  return false;
370  tree.emplace_back(key, module, settings);
371  incProgress();
372  }
373  BtPrinter{displayOptions, filterOptions}.printKeyTree(tree);
375  return true;
376 }
377 
378 /** Returns a filename to save a file. */
379 const QString CExportManager::getSaveFileName(const Format format) {
380  QString filter;
381  switch (format) {
382  case HTML:
383  filter = QObject::tr("HTML files") + " (*.html *.htm);;";
384  break;
385  case Text:
386  filter = QObject::tr("Text files") + " (*.txt);;";
387  break;
388  }
389  filter += QObject::tr("All files") + QStringLiteral(" (*)");
390 
391  return QFileDialog::getSaveFileName(nullptr,
392  QObject::tr("Save file"),
393  QString(),
394  filter,
395  nullptr);
396 }
397 
398 std::unique_ptr<CTextRendering> CExportManager::newRenderer(Format const format,
399  bool const addText)
400 {
401  FilterOptions filterOptions = m_filterOptions;
402  filterOptions.footnotes = false;
403  filterOptions.strongNumbers = false;
404  filterOptions.morphTags = false;
405  filterOptions.lemmas = false;
406  filterOptions.scriptureReferences = false;
407  filterOptions.textualVariants = false;
408 
409  using R = std::unique_ptr<CTextRendering>;
410  BT_ASSERT((format == Text) || (format == HTML));
411  if (format == HTML)
412  return R{new CTextRendering(addText,
414  filterOptions)};
415  return R{new CPlainTextExportRendering(addText,
417  filterOptions)};
418 }
419 
420 void CExportManager::setProgressRange(int const items) {
421  if (!m_progressDialog)
422  return;
423 
424  m_progressDialog->setMaximum(items);
425  m_progressDialog->setValue(0);
426  m_progressDialog->setMinimumDuration(0);
427  m_progressDialog->show();
428  // m_progressDialog->repaint();
429  qApp->processEvents(); //do not lock the GUI!
430 }
431 
432 /** Increments the progress by one item. */
434  if (m_progressDialog)
435  m_progressDialog->setValue(m_progressDialog->value() + 1);
436 }
437 
439  return m_progressDialog ? m_progressDialog->wasCanceled() : false;
440 }
441 
442 /** Closes the progress dialog immediatly. */
444  if (m_progressDialog) {
445  m_progressDialog->close();
446  m_progressDialog->reset();
447  }
448  qApp->processEvents(); //do not lock the GUI!
449 }
#define BT_ASSERT(...)
Definition: btassert.h:17
QList< CSwordModuleInfo const * > BtConstModuleList
Definition: btmodulelist.h:21
Manages the print item queue and printing.
Definition: btprinter.h:20
std::unique_ptr< QProgressDialog > const m_progressDialog
bool progressWasCancelled()
bool printKey(CSwordKey const *const key, DisplayOptions const &displayOptions, FilterOptions const &filterOptions)
std::unique_ptr< Rendering::CTextRendering > newRenderer(Format const format, bool const addText)
void setProgressRange(int const items)
FilterOptions const m_filterOptions
bool printByHyperlink(QString const &hyperlink, DisplayOptions const &displayOptions, FilterOptions const &filterOptions)
Prints a key using the hyperlink created by CReferenceManager.
bool printKeyList(CSwordModuleSearch::ModuleResultList const &list, CSwordModuleInfo const *const module, DisplayOptions const &displayOptions, FilterOptions const &filterOptions)
bool copyKeyList(CSwordModuleSearch::ModuleResultList const &list, CSwordModuleInfo const *const module, Format const format, bool const addText)
void incProgress()
Increments the progress by one item.
const QString getSaveFileName(Format const format)
bool saveKey(CSwordKey const *const key, Format const format, bool const addText, const BtConstModuleList &modules)
CExportManager(const bool showProgress=true, const QString &progressLabel=QString(), const FilterOptions &filterOptions=btConfig().getFilterOptions(), const DisplayOptions &displayOptions=btConfig().getDisplayOptions())
bool saveKeyList(CSwordModuleSearch::ModuleResultList const &list, CSwordModuleInfo const *const module, Format const format, bool const addText)
void closeProgressDialog()
Closes the progress dialog immediately.
bool copyKey(CSwordKey const *const key, Format const format, bool const addText)
DisplayOptions const m_displayOptions
CSwordModuleInfo const * module() const
Definition: cswordkey.h:68
virtual QString key() const =0
CSwordKey implementation for Sword's VerseKey.
CSwordVerseKey upperBound() const
QString bookName() const
bool isBoundSet() const
int chapter() const
CSwordVerseKey lowerBound() const
Text rendering based on trees.
std::list< KeyTreeItem > KeyTree
std::vector< std::shared_ptr< sword::SWKey const > > ModuleResultList
std::optional< DecodedHyperlink > decodeHyperlink(QString const &hyperlink)
bool filter(WizardTaskType const taskType, QStringList const &languages, CSwordModuleInfo const *const mInfo)
bool savePlainFile(const QString &filename, void(&writer)(QTextStream &, void *), void *userPtr)
Definition: tool.cpp:35
int morphTags
Definition: btglobal.h:29
int textualVariants
Definition: btglobal.h:34
int footnotes
Definition: btglobal.h:26
int scriptureReferences
Definition: btglobal.h:36
int strongNumbers
Definition: btglobal.h:27
PrintSettings(DisplayOptions const &displayOptions)