BibleTime
crangechooserdialog.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 "crangechooserdialog.h"
14 
15 #include <QByteArray>
16 #include <QDialogButtonBox>
17 #include <QFlags>
18 #include <QFrame>
19 #include <QHBoxLayout>
20 #include <QLabel>
21 #include <QLineEdit>
22 #include <QList>
23 #include <QListWidget>
24 #include <QListWidgetItem>
25 #include <QMap>
26 #include <QPushButton>
27 #include <Qt>
28 #include <QTextEdit>
29 #include <QVBoxLayout>
30 #include "../../backend/config/btconfig.h"
31 #include "../../backend/drivers/cswordmoduleinfo.h"
32 #include "../../backend/managers/cswordbackend.h"
33 #include "../../util/btassert.h"
34 #include "../../util/btconnect.h"
35 #include "../messagedialog.h"
36 
37 // Sword includes:
38 #pragma GCC diagnostic push
39 #pragma GCC diagnostic ignored "-Wextra-semi"
40 #pragma GCC diagnostic ignored "-Wsuggest-override"
41 #pragma GCC diagnostic ignored "-Wzero-as-null-pointer-constant"
42 #ifdef __clang__
43 #pragma clang diagnostic push
44 #pragma clang diagnostic ignored "-Wsuggest-destructor-override"
45 #endif
46 #include <versekey.h>
47 #include <listkey.h>
48 #include <swkey.h>
49 #include <swmodule.h>
50 #ifdef __clang__
51 #pragma clang diagnostic pop
52 #endif
53 #pragma GCC diagnostic pop
54 
55 namespace Search {
56 
57 CRangeChooserDialog::CRangeChooserDialog(const QStringList& scopeModules, QWidget *parentDialog)
58  : QDialog(parentDialog),
59  m_scopeModules(scopeModules)
60 {
61  initView();
63 
64  retranslateUi();
65 
66  // Add the existing scopes
68  for (auto it = map.begin(); it != map.end(); ++it)
69  new RangeItem(it.key(), it.value(), m_rangeList);
71 }
72 
74  m_rangeList = new QListWidget(this);
75  m_rangeListLabel = new QLabel(this);
76  m_rangeListLabel->setBuddy(m_rangeList);
77 
78  m_newRangeButton = new QPushButton(this);
79  m_deleteRangeButton = new QPushButton(this);
80 
81  m_nameEdit = new QLineEdit(this);
82  m_nameEditLabel = new QLabel(this);
83  m_nameEditLabel->setBuddy(m_nameEdit);
84 
85  m_rangeEdit = new QTextEdit(this);
86  m_rangeEditLabel = new QLabel(this);
87  m_rangeEditLabel->setBuddy(m_rangeEdit);
88 
89  m_resultList = new QListWidget(this);
90  m_resultListLabel = new QLabel(this);
92 
93  QFrame *hSeparatorLine = new QFrame(this);
94  hSeparatorLine->setFrameShape(QFrame::HLine);
95  hSeparatorLine->setFrameShadow(QFrame::Sunken);
96 
97  m_buttonBox = new QDialogButtonBox(this);
98  m_buttonBox->setOrientation(Qt::Horizontal);
99  m_buttonBox->setStandardButtons(QDialogButtonBox::Ok
100  | QDialogButtonBox::Cancel
101  | QDialogButtonBox::RestoreDefaults);
103 
104  QHBoxLayout *rangeButtonsLayout = new QHBoxLayout();
105  rangeButtonsLayout->addWidget(m_newRangeButton);
106  rangeButtonsLayout->addWidget(m_deleteRangeButton);
107 
108  QVBoxLayout* rangeListLayout = new QVBoxLayout;
109  rangeListLayout->addWidget(m_rangeListLabel);
110  rangeListLayout->addWidget(m_rangeList);
111  rangeListLayout->addLayout(rangeButtonsLayout);
112 
113  QHBoxLayout* nameEditLayout = new QHBoxLayout();
114  nameEditLayout->addWidget(m_nameEditLabel);
115  nameEditLayout->addWidget(m_nameEdit);
116 
117  QVBoxLayout* rangeEditLayout = new QVBoxLayout();
118  rangeEditLayout->addLayout(nameEditLayout);
119  rangeEditLayout->addWidget(m_rangeEditLabel);
120  rangeEditLayout->addWidget(m_rangeEdit);
121  rangeEditLayout->addWidget(m_resultListLabel);
122  rangeEditLayout->addWidget(m_resultList);
123 
124  QHBoxLayout *topLayout = new QHBoxLayout;
125  topLayout->addLayout(rangeListLayout);
126  topLayout->addLayout(rangeEditLayout);
127 
128  QVBoxLayout *vboxLayout = new QVBoxLayout(this);
129  vboxLayout->addLayout(topLayout);
130  vboxLayout->addWidget(hSeparatorLine);
131  vboxLayout->addWidget(m_buttonBox);
132 }
133 
135  BT_CONNECT(m_rangeList, &QListWidget::currentItemChanged,
136  [this](QListWidgetItem *, QListWidgetItem * const previous) {
137  if (previous) {
138  BT_ASSERT(dynamic_cast<RangeItem *>(previous));
139  saveCurrentToRange(static_cast<RangeItem*>(previous));
140  }
142  });
143  BT_CONNECT(m_nameEdit, &QLineEdit::textEdited,
145  BT_CONNECT(m_rangeEdit, &QTextEdit::textChanged,
146  [this]{
147  m_resultList->clear();
148  static QRegularExpression const re(
149  QStringLiteral(R"PCRE(\s*-\s*)PCRE"));
150  auto const range =
151  m_rangeEdit->toPlainText().replace(
152  re,
153  QStringLiteral("-"));
154 
155  auto const & backend = CSwordBackend::instance();
156  for (auto const & moduleName : m_scopeModules) {
157  auto * const module =
158  backend.findModuleByName(moduleName);
159  if (!module)
160  continue;
161  auto const verses(
162  sword::VerseKey(
163  module->swordModule().getKey())
164  .parseVerseList(
165  range.toUtf8().constData(),
166  "Genesis 1:1",
167  true));
168  if (verses.getCount() > 0) {
169  for (int i = 0; i < verses.getCount(); i++) {
170  auto const * const elementText =
171  verses.getElement(i)->getRangeText();
172  new QListWidgetItem(
173  QString::fromUtf8(elementText),
174  m_resultList);
175  }
176  break;
177  }
178  }
179  });
180 
181  // Connect buttons:
182  BT_CONNECT(m_buttonBox, &QDialogButtonBox::accepted,
184  BT_CONNECT(m_buttonBox, &QDialogButtonBox::rejected,
185  this, &CRangeChooserDialog::reject);
186  BT_CONNECT(m_newRangeButton, &QPushButton::clicked,
187  [this]{
188  m_rangeList->setCurrentItem(
189  new RangeItem(tr("New range"),
190  QString(),
191  m_rangeList));
193  });
194  BT_CONNECT(m_deleteRangeButton, &QPushButton::clicked,
195  [this]{
196  BT_ASSERT(dynamic_cast<RangeItem *>(
197  m_rangeList->currentItem()));
198  QListWidgetItem * const i = m_rangeList->currentItem();
199  m_rangeList->removeItemWidget(i);
200  delete i;
201 
203  });
204  QPushButton * defaultsButton = m_buttonBox->button(QDialogButtonBox::RestoreDefaults);
205  BT_CONNECT(defaultsButton, &QPushButton::clicked,
206  [this]{
207  m_rangeList->clear();
209  auto const map(
210  btConfig().getSearchScopesForCurrentLocale(
211  m_scopeModules));
212  for (auto it = map.begin(); it != map.end(); ++it)
213  new RangeItem(it.key(), it.value(), m_rangeList);
214  m_rangeList->setCurrentItem(nullptr);
216  });
217 }
218 
220  setWindowTitle(tr("Setup Search Scopes"));
221 
222  m_rangeListLabel->setText(tr("S&earch range:"));
223  m_rangeList->setToolTip(tr("Select a scope from the list to edit the search"
224  "ranges"));
225 
226  m_newRangeButton->setText(tr("&Add new scope"));
227  m_newRangeButton->setToolTip(tr("Add a new search scope. First enter an "
228  "appropriate name, then edit the search "
229  "ranges."));
230 
231  m_deleteRangeButton->setText(tr("Delete current &scope"));
232  m_deleteRangeButton->setToolTip(tr("Delete the selected search scope"));
233 
234  m_nameEditLabel->setText(tr("&Name:"));
235  m_nameEdit->setToolTip(tr("Change the name of the selected search scope"));
236 
237  m_rangeEditLabel->setText(tr("Edi&t current range:"));
238  m_rangeEdit->setToolTip(tr("Change the search ranges of the selected search"
239  "scope item. Have a look at the predefined "
240  "search scopes to see how search ranges are "
241  "constructed."));
242 
243  m_resultListLabel->setText(tr("Parsed search range:"));
244  m_resultList->setToolTip(tr("The search ranges which will be used for the "
245  "search, parsed to the canonical form"));
246 }
247 
249  if (!m_nameEdit->text().isEmpty())
250  i->setCaption(m_nameEdit->text());
251 
252  i->setRange(m_rangeEdit->toPlainText());
253 }
254 
256  const QListWidgetItem * const item = m_rangeList->currentItem();
257  BT_ASSERT(!item || dynamic_cast<RangeItem const *>(item));
258  const RangeItem * rangeItem = static_cast<const RangeItem *>(item);
259 
260  m_nameEdit->setEnabled(item != nullptr);
261  m_rangeEdit->setEnabled(item != nullptr);
262  m_resultList->setEnabled(item != nullptr);
263  m_deleteRangeButton->setEnabled(item != nullptr);
264  m_nameEdit->setText(item != nullptr ? rangeItem->caption() : QString());
265  m_rangeEdit->setText(item != nullptr ? rangeItem->range() : QString());
266 
267  if (item != nullptr)
268  m_nameEdit->setFocus();
269 
270  nameEditTextChanged(item != nullptr ? rangeItem->caption() : QString());
271 }
272 
274  // Update the active item:
275  QListWidgetItem *currentItem = m_rangeList->currentItem();
276  if (currentItem != nullptr) {
277  BT_ASSERT(dynamic_cast<RangeItem *>(currentItem));
278  saveCurrentToRange(static_cast<RangeItem*>(currentItem));
279  }
280 
281  // Save the new sorted map of search scopes:
282  m_rangeList->sortItems();
284  for (int i = 0; i < m_rangeList->count(); i++) {
285  BT_ASSERT(dynamic_cast<RangeItem *>(m_rangeList->item(i)));
286  const RangeItem * item = static_cast<RangeItem*>(m_rangeList->item(i));
287  map[item->caption()] = item->range();
288  }
290 
291  QDialog::accept();
292 }
293 
294 void CRangeChooserDialog::nameEditTextChanged(const QString &newText) {
295  const bool e = !newText.isEmpty() || m_rangeList->count() <= 0;
296  m_buttonBox->button(QDialogButtonBox::Ok)->setEnabled(e);
297 }
298 
299 } //end of namespace Search
#define BT_ASSERT(...)
Definition: btassert.h:17
BtConfig & btConfig()
This is a shortchand for BtConfig::getInstance().
Definition: btconfig.h:305
#define BT_CONNECT(...)
Definition: btconnect.h:20
QMap< QString, QString > StringMap
Definition: btconfig.h:48
void setSearchScopesWithCurrentLocale(const QStringList &scopeModules, StringMap searchScopes)
Definition: btconfig.cpp:441
void deleteSearchScopesWithCurrentLocale()
Definition: btconfig.cpp:499
StringMap getSearchScopesForCurrentLocale(const QStringList &scopeModules)
Definition: btconfig.cpp:412
static CSwordBackend & instance() noexcept
Definition: cswordbackend.h:98
void setCaption(QString const &caption)
CRangeChooserDialog(const QStringList &rangeScopeModule, QWidget *parentDialog=nullptr)
void nameEditTextChanged(const QString &newText)
std::unique_ptr< CSwordBackend > backend(sword::InstallSource const &is)
void prepareDialogBox(QDialogButtonBox *box)