BibleTime
btbookshelfworkspage.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 "btbookshelfworkspage.h"
14 
15 #include <algorithm>
16 #include <QAbstractItemView>
17 #include <QApplication>
18 #include <QByteArray>
19 #include <QComboBox>
20 #include <QDir>
21 #include <QFileInfo>
22 #include <QHBoxLayout>
23 #include <QHeaderView>
24 #include <QLabel>
25 #include <QLineEdit>
26 #include <QMenu>
27 #include <QNonConstOverload>
28 #include <QSet>
29 #include <QSizePolicy>
30 #include <QStringList>
31 #include <QStringLiteral>
32 #include <Qt>
33 #include <QToolButton>
34 #include <QVBoxLayout>
35 #include <QWizard>
36 #include <utility>
37 #include "../../backend/btinstallbackend.h"
38 #include "../../backend/bookshelfmodel/btbookshelfmodel.h"
39 #include "../../backend/bookshelfmodel/btbookshelffiltermodel.h"
40 #include "../../backend/bookshelfmodel/btbookshelftreemodel.h"
41 #include "../../backend/config/btconfig.h"
42 #include "../../backend/drivers/btmoduleset.h"
43 #include "../../backend/drivers/cswordmoduleinfo.h"
44 #include "../../backend/language.h"
45 #include "../../backend/managers/cswordbackend.h"
46 #include "../../util/btassert.h"
47 #include "../../util/btconnect.h"
48 #include "../btbookshelfgroupingmenu.h"
49 #include "../btbookshelfview.h"
50 #include "btbookshelfwizard.h"
51 #include "btinstallpagemodel.h"
52 
53 // Sword includes:
54 #pragma GCC diagnostic push
55 #pragma GCC diagnostic ignored "-Wzero-as-null-pointer-constant"
56 #include <installmgr.h>
57 #include <swbuf.h>
58 #include <swversion.h>
59 #pragma GCC diagnostic pop
60 
61 
62 namespace {
63 QString const groupingOrderKey("GUI/BookshelfWizard/InstallPage/grouping");
64 QString const installPathKey(
65  "GUI/BookshelfWizard/InstallPage/installPathIndex");
66 
67 inline bool filter(WizardTaskType const taskType,
68  QStringList const & languages,
69  CSwordModuleInfo const * const mInfo)
70 {
71  if (taskType == WizardTaskType::installWorks) {
73  && languages.contains(mInfo->language()->translatedName());
74  } else if (taskType == WizardTaskType::updateWorks) {
75  using CSMI = CSwordModuleInfo;
76  using CSV = sword::SWVersion const;
77  CSMI const * const installedModule =
79  return installedModule
80  && (CSV(installedModule->config(CSMI::ModuleVersion).toLatin1())
81  < CSV(mInfo->config(CSMI::ModuleVersion).toLatin1()));
82  } else {
85  }
86 }
87 
88 } // anonymous namespace
89 
91  QWidget * parent)
92  : BtBookshelfWizardPage(parent)
93  , m_taskType(iType)
94  , m_groupingOrder(btConfig(), groupingOrderKey)
95 {
96  // Initialize menus:
98  m_contextMenu = new QMenu(this);
99  m_contextMenu->addMenu(m_groupingMenu);
101 
102  // Setup UI:
103  QVBoxLayout * const verticalLayout = new QVBoxLayout(this);
104 
105  QHBoxLayout * horizontalLayout = new QHBoxLayout;
106  m_nameFilterLabel = new QLabel(this);
107  horizontalLayout->addWidget(m_nameFilterLabel);
108  m_nameFilterEdit = new QLineEdit(this);
110  horizontalLayout->addWidget(m_nameFilterEdit);
111  verticalLayout->addLayout(horizontalLayout);
112 
113  m_bookshelfView = new BtBookshelfView(this);
114  m_bookshelfView->setHeaderHidden(false);
115  m_bookshelfView->header()->setSectionResizeMode(
116  QHeaderView::ResizeToContents);
117  verticalLayout->addWidget(m_bookshelfView);
118 
119  m_msgLabel = new QLabel(this);
120  m_msgLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
121  m_msgLabel->setWordWrap(true);
122  verticalLayout->addWidget(m_msgLabel);
123 
124  QHBoxLayout * const pathLayout = new QHBoxLayout();
126  m_pathCombo = new QComboBox(this);
127  m_pathCombo->setMinimumContentsLength(20);
128  m_pathCombo->setSizeAdjustPolicy(
129  QComboBox::AdjustToMinimumContentsLengthWithIcon);
130  m_pathCombo->setSizePolicy(QSizePolicy::Expanding,
131  QSizePolicy::Minimum);
132  m_pathCombo->view()->setTextElideMode(Qt::ElideMiddle);
133 
134  m_pathLabel = new QLabel(this);
135  m_pathLabel->setBuddy(m_pathCombo);
137 
138  pathLayout->setContentsMargins(0, 8, 0, 0);
139  pathLayout->addWidget(m_pathLabel);
140  pathLayout->addWidget(m_pathCombo);
141  }
142  pathLayout->addStretch();
143 
144  m_groupingLabel = new QLabel(this);
145  pathLayout->addWidget(m_groupingLabel);
146 
147  m_groupingButton = new QToolButton(this);
148  m_groupingButton->setPopupMode(QToolButton::InstantPopup);
150  m_groupingButton->setIcon(m_groupingMenu->icon());
151  m_groupingButton->setAutoRaise(true);
152  pathLayout->addWidget(m_groupingButton);
153 
154  verticalLayout->addLayout(pathLayout);
155 
156  // Setup models:
157  BtBookshelfFilterModel * const filterModel =
158  new BtBookshelfFilterModel(this);
159  m_bookshelfView->setModel(filterModel);
160 
164  filterModel->setSourceModel(m_installPageModel);
165 
166  /// \todo is this useless if m_taskType == WizardTaskType::removeWorks?
170  } else {
172  }
174  m_bookshelfView->setColumnHidden(1, true);
175 
176  // Initialize connections:
179  this,
182  BT_CONNECT(m_pathCombo, qOverload<int>(&QComboBox::currentIndexChanged),
184  BT_CONNECT(
187  }
189  this, &BtBookshelfWorksPage::completeChanged);
192  BT_CONNECT(m_nameFilterEdit, &QLineEdit::textEdited,
194 
195  retranslateUi();
196 }
197 
199  m_nameFilterLabel->setText(tr("Fi&lter:"));
200  if (m_taskType == installWorks) {
201  setTitle(QApplication::translate("BookshelfWizard", "Install Works"));
202  setSubTitle(QApplication::translate("BookshelfWizard",
203  "Choose one or more works to "
204  "install."));
205  setButtonText(QWizard::NextButton,tr("Install Works >"));
206  } else if (m_taskType == WizardTaskType::updateWorks) {
207  setTitle(QApplication::translate("BookshelfWizard", "Update Works"));
208  setSubTitle(QApplication::translate("BookshelfWizard",
209  "Choose one or more works to "
210  "update."));
211  setButtonText(QWizard::NextButton,tr("Update Works >"));
212  } else {
214  setTitle(QApplication::translate("BookshelfWizard", "Remove Works"));
215  setSubTitle(QApplication::translate("BookshelfWizard",
216  "Choose one or more works to "
217  "remove."));
218  setButtonText(QWizard::NextButton,tr("Remove Works >"));
219  }
220 
222  m_pathLabel->setText(tr("Install &folder:"));
223  m_pathCombo->setToolTip(tr("The folder where the new works will be "
224  "installed"));
225  }
226 
228  m_msgLabel->setText(tr("There are no works to update."));
229  } else if (m_taskType == WizardTaskType::removeWorks) {
230  m_msgLabel->setText(tr("No works are currently installed so they "
231  "cannot be removed."));
232  } else {
234  m_msgLabel->setText(
235  tr("No works can be installed with the current selection of "
236  "remote libraries and languages. Please go back and make a "
237  "different selection."));
238  }
239 
240  m_groupingLabel->setText(tr("Grouping:"));
241  m_groupingButton->setText(tr("Grouping"));
242  m_groupingButton->setToolTip(
243  tr("Change the grouping of items in the bookshelf."));
244 }
245 
247  if (btWizard().taskType() == WizardTaskType::removeWorks)
250 }
251 
253  // Update models:
254  QStringList sources;
255  QStringList languages;
256  if (m_taskType == installWorks) {
257  sources = btWizard().selectedSources();
258  languages = btWizard().selectedLanguages();
259  } else {
261  }
262 
263  {
264  QSet<QString> addedModuleNames;
265  m_bookshelfModel->clear();
266  m_usedBackends.clear();
267  for (auto const & sourceName : sources) {
268  sword::InstallSource const source =
269  BtInstallBackend::source(sourceName);
270  std::unique_ptr<CSwordBackend const> backend(
272  bool backendUsed = false;
273  for (auto * const module : backend->moduleList()) {
274  if (filter(m_taskType, languages, module)) {
275  QString const & moduleName = module->name();
276  if (addedModuleNames.contains(moduleName))
277  continue;
278  addedModuleNames.insert(moduleName);
279  m_bookshelfModel->addModule(module);
280  module->setProperty("installSourceName",
281  QString(source.caption.c_str()));
282  backendUsed = true;
283  }
284  }
285  if (backendUsed)
286  m_usedBackends.emplace_back(std::move(backend));
287  }
289  m_bookshelfView->expandAll();
290 
291  bool const noWorks = (addedModuleNames.count() == 0);
292  m_msgLabel->setVisible(noWorks);
293  m_bookshelfView->setVisible(!noWorks);
294  m_groupingButton->setVisible(!noWorks);
295  m_groupingLabel->setVisible(!noWorks);
296  }
297 
298  // Set grouping:
299  static BtBookshelfTreeModel::Grouping const cat(
301  static BtBookshelfTreeModel::Grouping const catLang; // No grouping
302  if (languages.count() == 1) {
304  } else if (languages.count() > 1 && m_groupingOrder == cat) {
306  }
307 }
308 
310  BtBookshelfTreeModel::Grouping const & grouping)
311 {
313  m_bookshelfView->setRootIsDecorated(!grouping.isEmpty());
314 }
315 
317 { return checkedModules().count() > 0; }
318 
321 {
322  m_groupingOrder = g;
324 }
325 
327 { return m_installPageModel->checkedModules(); }
328 
330 { return m_pathCombo->currentText(); }
331 
333 { btConfig().setValue(installPathKey, index); }
334 
335 static bool installPathIsUsable(QString const & path) {
336  if (path.isEmpty())
337  return false;
338  QDir const dir(path);
339  if (!dir.exists() || !dir.isReadable())
340  return false;
341  return QFileInfo(dir.canonicalPath()).isWritable();
342 }
343 
345  m_pathCombo->clear();
346  bool haveUsableTargets = false;
347  for (auto const & target : CSwordBackend::instance().swordDirList()) {
348  if (installPathIsUsable(target)) {
349  m_pathCombo->addItem(QDir::toNativeSeparators(target));
350  haveUsableTargets = true;
351  }
352  }
353 
354  m_pathCombo->setVisible(haveUsableTargets);
355  m_pathLabel->setVisible(haveUsableTargets);
356  if (haveUsableTargets) {
357  /* Choose the current value from config but check whether we have so
358  many items: */
359  int const cfgValue = btConfig().value<int>(installPathKey, 0);
360  int const lastIdx = m_pathCombo->count() - 1;
361  m_pathCombo->setCurrentIndex(cfgValue > lastIdx ? lastIdx : cfgValue);
362  }
363 }
#define BT_ASSERT(...)
Definition: btassert.h:17
@ installWorks
@ removeFinalPage
@ installFinalPage
static bool installPathIsUsable(QString const &path)
BtConfig & btConfig()
This is a shortchand for BtConfig::getInstance().
Definition: btconfig.h:305
#define BT_CONNECT(...)
Definition: btconnect.h:20
void setNameFilterFixedString(QString const &nameFilter)
void signalGroupingOrderChanged(const BtBookshelfTreeModel::Grouping &)
static std::shared_ptr< BtBookshelfModel > newInstance()
void saveTo(BtConfigCore &config, QString const &key) const
void groupingOrderChanged(BtBookshelfTreeModel::Grouping newGrouping)
void setDefaultChecked(CheckedBehavior b)
void setGroupingOrder(const BtBookshelfTreeModel::Grouping &groupingOrder, bool emitSignal=true)
BtModuleSet const & checkedModules() const
void setSourceModel(std::shared_ptr< QAbstractItemModel > sourceModel)
void moduleChecked(CSwordModuleInfo *module, bool checked)
BtBookshelfWizard & btWizard() const noexcept
QStringList selectedSources() const
QStringList selectedLanguages() const
QToolButton * m_groupingButton
bool isComplete() const final override
void initializePage() final override
std::shared_ptr< BtBookshelfModel > m_bookshelfModel
std::vector< std::unique_ptr< CSwordBackend const > > m_usedBackends
void slotGroupingActionTriggered(BtBookshelfTreeModel::Grouping const &grouping)
BtModuleSet const & checkedModules() const
int nextId() const final override
void slotGroupingOrderChanged(BtBookshelfTreeModel::Grouping const &g)
WizardTaskType const m_taskType
BtBookshelfTreeModel::Grouping m_groupingOrder
void slotPathChanged(int const index)
BtBookshelfGroupingMenu * m_groupingMenu
BtBookshelfWorksPage(WizardTaskType iType, QWidget *parent=nullptr)
BtBookshelfView * m_bookshelfView
BtInstallPageModel * m_installPageModel
T value(QString const &key, T const &defaultValue=T()) const
Returns the settings value for the given global key.
Definition: btconfigcore.h:50
void setValue(QString const &key, T const &value)
Sets a value for a key.
Definition: btconfigcore.h:73
CSwordModuleInfo * findModuleByName(const QString &name) const
Searches for a module with the given name.
static CSwordBackend & instance() noexcept
Definition: cswordbackend.h:98
void sigSwordSetupChanged()
QString config(const CSwordModuleInfo::ConfigEntry entry) const
std::shared_ptr< Language const > language() const
QString const & name() const
sword::InstallSource source(const QString &name)
QStringList sourceNameList()
std::unique_ptr< CSwordBackend > backend(sword::InstallSource const &is)
QString const groupingOrderKey("GUI/BookshelfWizard/InstallPage/grouping")
QString const installPathKey("GUI/BookshelfWizard/InstallPage/installPathIndex")
bool filter(WizardTaskType const taskType, QStringList const &languages, CSwordModuleInfo const *const mInfo)