BibleTime
cbookkeychooser.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 "cbookkeychooser.h"
14 
15 #include <algorithm>
16 #include <memory>
17 #include <QComboBox>
18 #include <QHBoxLayout>
19 #include <QStringList>
20 #include <Qt>
21 #include <QtAlgorithms>
22 #include <QtGlobal>
23 #include <QVariant>
24 #include <QWidget>
25 #include "../../backend/config/btconfig.h"
26 #include "../../backend/drivers/btmodulelist.h"
27 #include "../../backend/drivers/cswordbookmoduleinfo.h"
28 #include "../../backend/drivers/cswordmoduleinfo.h"
29 #include "../../backend/keys/cswordkey.h"
30 #include "../../backend/keys/cswordtreekey.h"
31 #include "../../util/btassert.h"
32 #include "../../util/btconnect.h"
33 #include "ckeychooserwidget.h"
34 
35 
36 #define ID_PROPERTY_NAME "CBookKeyChooser_ID"
37 
39  CSwordKey * key,
40  QWidget * parent)
41  : CKeyChooser(parent)
42  , m_layout(nullptr)
43 {
44  m_key = dynamic_cast<CSwordTreeKey * >(key);
45  setModules(modules, true);
46  setKey(key);
47 
48  adjustFont();
49 }
50 
52  setKey(newKey, true);
53 }
54 
55 /** Sets a new key to this keychooser */
56 void CBookKeyChooser::setKey(CSwordKey * newKey, const bool emitSignal) {
57  if (m_key != newKey) // Set the internal key to the new one
58  m_key = dynamic_cast<CSwordTreeKey*>(newKey);
59 
60  QString oldKey(m_key->key());
61 
62  if (oldKey.isEmpty()) { // Don't set keys equal to "/", always use a key which may have content
64  oldKey = m_key->key();
65  }
66 
67  auto const oldOffset = m_key->offset();
68 
69  QStringList siblings; // Split up key
70  if (m_key && !oldKey.isEmpty())
71  siblings = oldKey.split('/', Qt::SkipEmptyParts);
72 
73  int depth = 0;
74 
75  m_key->positionToRoot(); //start iteration at root node
76 
77  while (m_key->positionToFirstChild() && (depth < siblings.count())) {
78  QString key = m_key->key();
79  int index = (depth == 0) ? -1 : 0;
80 
81  bool found = false;
82 
83  do { //look for matching sibling
84  ++index;
85  found = (m_key->getLocalNameUnicode() == siblings[depth]);
86  } while (!found && m_key->positionToNextSibling());
87 
88  if (found) {
89  key = m_key->key(); //found: change key to this level
90  } else {
91  m_key->setKey(key); //not found: restore old key
92  }
93 
94  setupCombo(key, depth, index);
95 
96  //last iteration: check to see if another box can be filled with child entries
97  if (depth == siblings.count() - 1 && m_key->hasChildren()) {
99  setupCombo(m_key->key(), ++depth, 0);
100  }
101 
102  depth++;
103  }
104 
105  //clear the combos which were not filled
106  for (; depth < m_modules.first()->depth(); ++depth) {
107  CKeyChooserWidget * const chooser = m_chooserWidgets.at(depth);
108  if (chooser)
109  chooser->reset(0, 0, false);
110  }
111 
112  if (oldKey.isEmpty()) {
114  } else {
115  //m_key->key(oldKey);
116  m_key->setOffset(oldOffset);
117  }
118 
119  if (emitSignal)
120  Q_EMIT keyChanged(m_key);
121 }
122 
123 /** Returns the key of this kechooser. */
125  return m_key;
126 }
127 
128 /** Sets another module to this keychooser */
130  bool refresh)
131 {
132  m_modules.clear();
133 
134  // for (modules.first(); modules.current(); modules.next()) {
135  for (auto const * const m : modules)
136  if (m->type() == CSwordModuleInfo::GenericBook)
137  m_modules.append(static_cast<CSwordBookModuleInfo const *>(m));
138 
139  //refresh the number of combos
140  if (refresh && m_modules.count() && m_key) {
141  if (!m_layout) {
142  m_layout = new QHBoxLayout(this);
143  m_layout->setSpacing(0);
144  m_layout->setContentsMargins(0, 0, 0, 0);
145  }
146 
147  qDeleteAll(m_chooserWidgets);
148  m_chooserWidgets.clear();
149 
150  for (int i = 0; i < m_modules.first()->depth(); ++i) {
151  // Create an empty keychooser, don't handle next/prev signals
152  CKeyChooserWidget * const w = new CKeyChooserWidget(0, this);
153  m_chooserWidgets.append(w);
154 
155  //don't allow a too high width, try to keep as narrow as possible
156  //to aid users with smaller screen resolutions
157  int totalWidth = 200; //only 1 level
158  if (m_modules.first()->depth() > 1) {
159  if (m_modules.first()->depth() > 3) {
160  totalWidth = 400; //4+ levels
161  } else {
162  totalWidth = 300; //2-3 levels
163  }
164  }
165 
166  int maxWidth = static_cast<int>(static_cast<float>(totalWidth)
167  / m_modules.first()->depth());
168 
169  w->comboBox().setMaximumWidth(maxWidth);
170  w->comboBox().setCurrentIndex(0);
171 
176 
177  m_layout->addWidget(w);
178  w->setProperty(ID_PROPERTY_NAME, i+1);
179  w->show();
180  }
181 
182  //set the tab order of the key chooser widgets
183 
184  CKeyChooserWidget * chooser = nullptr;
185  CKeyChooserWidget * chooser_prev = nullptr;
186  const int count = m_chooserWidgets.count();
187  for (int i = 0; i < count; i++) {
188  chooser = m_chooserWidgets.at(i);
189  BT_ASSERT(chooser);
190  if (chooser_prev)
191  QWidget::setTabOrder(chooser_prev, chooser);
192 
193  chooser_prev = chooser;
194  }
195  QWidget::setTabOrder(chooser, nullptr);
196 
197  updateKey(m_key);
198  adjustFont(); // only when refresh is set.
199  }
200 }
201 
202 /** No descriptions */
204  //Make sure the entries are displayed correctly.
205  auto const & font =
206  btConfig().getFontForLanguage(*m_modules.first()->language()).second;
207  for (CKeyChooserWidget * const w : m_chooserWidgets)
208  w->comboBox().setFont(font);
209 }
210 
211 /** Refreshes the content. */
213  if (m_key)
214  updateKey(m_key); // Refresh with current key
215 }
216 
217 void CBookKeyChooser::setupCombo(const QString & key,
218  const int depth,
219  const int currentItem)
220 {
221  CKeyChooserWidget * const chooserWidget = m_chooserWidgets.at(depth);
222 
223  CSwordTreeKey tmpKey(*m_key);
224  tmpKey.setKey(key);
225  tmpKey.positionToParent();
226  tmpKey.positionToFirstChild();
227 
228  QStringList items;
229  if (depth > 0)
230  items.append(QString()); // Insert an empty item at the top
231 
232  do {
233  items.append(tmpKey.getLocalNameUnicode());
234  } while (tmpKey.positionToNextSibling());
235 
236  if (chooserWidget)
237  chooserWidget->reset(items, currentItem, false);
238 }
239 
240 /** A keychooser changed. Update and emit a signal if necessary. */
242  Q_UNUSED(newIndex)
243  QStringList items;
244 
245  const int max = std::min(static_cast<int>(m_chooserWidgets.size()),
246  sender()->property(ID_PROPERTY_NAME).toInt());
247  for (int i = 0; i < max; i++) {
248  CKeyChooserWidget * const chooser = m_chooserWidgets.at(i);
249  BT_ASSERT(chooser);
250  const QString currentText = chooser->comboBox().currentText();
251  if (currentText.isEmpty())
252  break;
253 
254  items.append(currentText);
255  }
256 
257  m_key->setKey('/' + items.join('/'));
258  setKey(m_key);
259 }
260 
261 /** Updates the keychoosers for the given key but emit no signal. */
263  setKey(key, false);
264 }
#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
QList< CSwordModuleInfo const * > BtConstModuleList
Definition: btmodulelist.h:21
#define ID_PROPERTY_NAME
FontSettingsPair getFontForLanguage(Language const &language)
Get font for a language.
Definition: btconfig.cpp:364
void refreshContent() final override
void setupCombo(QString const &key, int const depth, int const currentItem)
CSwordKey * key() final override
CBookKeyChooser(BtConstModuleList const &modules, CSwordKey *key=nullptr, QWidget *parent=nullptr)
QList< CKeyChooserWidget * > m_chooserWidgets
void keyChooserChanged(int)
A keychooser changed. Update and emit a signal if necessary.
CSwordTreeKey * m_key
void setModules(BtConstModuleList const &modules, bool refresh=false) final override
void setKey(CSwordKey *key) final override
QList< CSwordBookModuleInfo const * > m_modules
QHBoxLayout * m_layout
void updateKey(CSwordKey *key) final override
Updates the keychoosers for the given key but emit no signal.
void focusOut(int index)
QComboBox & comboBox() const
void changed(int index)
void reset(const int count, int index, bool do_emit)
void keyChanged(CSwordKey *newKey)
Class for generic book support.
CSwordKey implementation for Sword's TreeKey.
Definition: cswordtreekey.h:43
void positionToRoot()
Definition: cswordtreekey.h:99
bool positionToFirstChild()
bool hasChildren()
Definition: cswordtreekey.h:98
bool positionToNextSibling()
bool positionToParent()
QString getLocalNameUnicode()
QString key() const final override
Offset offset() const
bool setKey(const QString &key) final override
void setOffset(Offset value)