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 <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 */
56void 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
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
217void 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
#define ID_PROPERTY_NAME
FontSettingsPair getFontForLanguage(Language const &language)
Get font for a language.
Definition btconfig.cpp:355
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)