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