BibleTime
btbiblekeywidget.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 "btbiblekeywidget.h"
14
15#include <QApplication>
16#include <QDebug>
17#include <QEvent>
18#include <QFocusEvent>
19#include <QHBoxLayout>
20#include <QLineEdit>
21#include <QMenu>
22#include <QMouseEvent>
23#include <QPixmap>
24#include <QString>
25#include <QStringList>
26#include <QtGlobal>
27#include <QToolButton>
28#include "../../../backend/keys/cswordversekey.h"
29#include "../../../util/btconnect.h"
30#include "../../../util/cresmgr.h"
31#include "../cscrollerwidgetset.h"
33
34
35class BtLineEdit : public QLineEdit {
36 public:
38 : QLineEdit(parent) {
39 }
40 protected:
41 void focusInEvent(QFocusEvent* event) override {
42 Qt::FocusReason reason = event->reason();
43 if (reason == Qt::OtherFocusReason) {
44 selectAll();
45 }
46
47 QWidget::focusInEvent(event);
48 }
49};
50
51
53 CSwordBibleModuleInfo const * mod,
54 CSwordVerseKey * key,
55 QWidget * parent)
56 : QWidget(parent)
57 , m_key(key)
58 , m_dropDownHoverTimer(this)
59 , m_module(mod)
60{
61 auto const slotStep =
62 [this](int offset, CSwordVerseKey::JumpType const jumpType) {
63 if (!offset)
64 return;
65 if (offset > 0) {
66 do {
67 m_key->next(jumpType);
68 } while (--offset);
69 } else {
70 do {
71 m_key->previous(jumpType);
72 } while (++offset);
73 }
74 if (!updatelock)
75 Q_EMIT changed(m_key);
76 };
77 auto const slotStepBook =
78 [slotStep](int offset)
79 { slotStep(offset, CSwordVerseKey::UseBook); };
80 auto const slotStepChapter =
81 [slotStep](int offset)
82 { slotStep(offset, CSwordVerseKey::UseChapter); };
83 auto const slotStepVerse =
84 [slotStep](int offset)
85 { slotStep(offset, CSwordVerseKey::UseVerse); };
86
87 updatelock = false;
88
89 setFocusPolicy(Qt::WheelFocus);
90
91 QToolButton* clearRef = new QToolButton(this);
92 clearRef->setIcon(CResMgr::icon_clearEdit());
93 clearRef->setAutoRaise(true);
94 clearRef->setStyleSheet(QStringLiteral("QToolButton{margin:0px;}"));
95 BT_CONNECT(clearRef, &QToolButton::clicked,
96 [this]{
97 m_textbox->setText(QString());
98 m_textbox->setFocus();
99 });
100
101 auto * const bookScroller = new CScrollerWidgetSet(this);
102
103 m_textbox = new BtLineEdit( this );
104 setFocusProxy(m_textbox);
105 m_textbox->setContentsMargins(0, 0, 0, 0);
106
107 auto * const chapterScroller = new CScrollerWidgetSet(this);
108 auto * const verseScroller = new CScrollerWidgetSet(this);
109
110 QHBoxLayout* m_mainLayout = new QHBoxLayout( this );
111 m_mainLayout->setContentsMargins(0, 0, 0, 0);
112 m_mainLayout->setSpacing(0);
113 m_mainLayout->addWidget(clearRef);
114 m_mainLayout->addWidget(bookScroller);
115 m_mainLayout->addWidget(m_textbox);
116 m_mainLayout->addWidget(chapterScroller);
117 m_mainLayout->addWidget(verseScroller);
118
119
120 setTabOrder(m_textbox, nullptr);
121
122 m_dropDownButtons = new QWidget(this);
123 m_dropDownButtons->setWindowFlags(Qt::Popup);
124 m_dropDownButtons->setAttribute(Qt::WA_WindowPropagation);
125 m_dropDownButtons->setCursor(Qt::ArrowCursor);
126 QHBoxLayout *dropDownButtonsLayout(new QHBoxLayout(m_dropDownButtons));
127
128 auto * const bookChooser =
130 *this);
131 bookChooser->setToolTip(tr("Select book"));
132 BT_CONNECT(bookChooser->menu(), &QMenu::triggered,
133 [this](QAction * const action) {
134 auto bookname = action->property("bookname").toString();
135 if (m_key->bookName() != bookname) {
136 m_key->setBookName(std::move(bookname));
137 updateText();
138 }
139 if (!updatelock)
140 Q_EMIT changed(m_key);
141 });
142 BT_CONNECT(bookChooser, &BtDropdownChooserButton::stepItem, slotStepBook);
143 dropDownButtonsLayout->addWidget(bookChooser, 2);
144
145 auto * const chapterChooser =
147 *this);
148 chapterChooser->setToolTip(tr("Select chapter"));
149 BT_CONNECT(chapterChooser->menu(), &QMenu::triggered,
150 [this](QAction * const action) {
151 int const n = action->property("chapter").toInt();
152 if (m_key->chapter() != n) {
153 m_key->setChapter(n);
154 updateText();
155 }
156 if (!updatelock)
157 Q_EMIT changed(m_key);
158 });
159 BT_CONNECT(chapterChooser,
161 slotStepChapter);
162 dropDownButtonsLayout->addWidget(chapterChooser, 1);
163
164 auto * const verseChooser =
166 *this);
167 verseChooser->setToolTip(tr("Select verse"));
168 BT_CONNECT(verseChooser->menu(), &QMenu::triggered,
169 [this](QAction * const action) {
170 int const n = action->property("verse").toInt();
171 if (m_key->verse() != n) {
172 m_key->setVerse(n);
173 updateText();
174 }
175 if (!updatelock)
176 Q_EMIT changed(m_key);
177 });
178 BT_CONNECT(verseChooser, &BtDropdownChooserButton::stepItem, slotStepVerse);
179 dropDownButtonsLayout->addWidget(verseChooser, 1);
180
181 dropDownButtonsLayout->setContentsMargins(0, 0, 0, 0);
182 dropDownButtonsLayout->setSpacing(0);
183 m_dropDownButtons->setLayout(dropDownButtonsLayout);
184 m_dropDownButtons->hide();
185
186 m_dropDownButtons->installEventFilter(this);
187
188 m_dropDownHoverTimer.setInterval(500);
189 m_dropDownHoverTimer.setSingleShot(true);
190 BT_CONNECT(&m_dropDownHoverTimer, &QTimer::timeout,
191 m_dropDownButtons, &QWidget::hide);
192
193 QString scrollButtonToolTip(tr("Scroll through the entries of the list. Press the button and move the mouse to increase or decrease the item."));
194 bookScroller->setToolTips(
195 tr("Next book"),
196 scrollButtonToolTip,
197 tr("Previous book")
198 );
199 chapterScroller->setToolTips(
200 tr("Next chapter"),
201 scrollButtonToolTip,
202 tr("Previous chapter")
203 );
204 verseScroller->setToolTips(
205 tr("Next verse"),
206 scrollButtonToolTip,
207 tr("Previous verse")
208 );
209
210 // signals and slots connections
211 auto const initScrollerConnections =
212 [this](CScrollerWidgetSet & scroller, auto stepFunction)
213 {
214 BT_CONNECT(&scroller,
216 stepFunction);
218 [this]{
219 updatelock = true;
220 oldKey = m_key->key();
221 });
223 [this]{
224 updatelock = false;
225 if (oldKey != m_key->key())
226 Q_EMIT changed(m_key);
227 });
228 };
229 initScrollerConnections(*bookScroller, slotStepBook);
230 initScrollerConnections(*chapterScroller, slotStepChapter);
231 initScrollerConnections(*verseScroller, slotStepVerse);
232
233 BT_CONNECT(m_textbox, &QLineEdit::returnPressed,
234 [this]{
235 m_key->setKey(m_textbox->text());
236 Q_EMIT changed(m_key);
237 });
238
239 BT_CONNECT(m_key->afterChangedSignaller(), &BtSignal::signal,
241
242 setKey(key); // The order of these two functions is important.
243 setModule();
244}
245
249
251 if (m) { //can be null
252 m_module = m;
253 m_key->setModule(m);
254 }
255}
256
258 if (o != m_dropDownButtons) return false;
259 switch (e->type()) {
260 case QEvent::Enter:
262 return true;
263 case QEvent::Leave:
264 m_dropDownHoverTimer.start();
265 return true;
266 default:
267 return false;
268 }
269}
270
271void BtBibleKeyWidget::enterEvent(QEnterEvent *) {
273
275
276 m_dropDownButtons->raise();
277 m_dropDownButtons->show();
278}
279
281 m_dropDownHoverTimer.start();
282}
283
284void BtBibleKeyWidget::resizeEvent(QResizeEvent *event) {
285 if (m_dropDownButtons->isVisible()) {
287 }
288 QWidget::resizeEvent(event);
289}
290
292 m_dropDownButtons->setParent(window());
293 int h(m_dropDownButtons->layout()->minimumSize().height());
294 QPoint topLeft(mapTo(window(),
295 QPoint(m_textbox->x(), m_textbox->y() + m_textbox->height())));
296 m_dropDownButtons->setGeometry(topLeft.x(), topLeft.y(),
297 m_textbox->width(), h);
298}
299
301 QString text(m_key->key());
302 m_textbox->setText(text);
303 QFontMetrics fm(m_textbox->font());
304 int nw(m_textbox->minimumSizeHint().width() + fm.horizontalAdvance(text));
305 if (nw > m_textbox->minimumWidth()) {
306 m_textbox->setMinimumWidth(nw);
307 m_textbox->updateGeometry();
308 }
309}
310
312 if (!key) return false;
313
314 m_key->setKey(key->key());
315 return true;
316}
317
319 for (auto const & bookname : m_module->books())
320 menu.addAction(bookname)->setProperty("bookname", bookname);
321}
322
324 int count = m_module->chapterCount(m_key->bibleBook());
325 for (int i = 1; i <= count; i++)
326 menu.addAction(QString::number(i))->setProperty("chapter", i);
327}
328
330 int count = m_module->verseCount(m_key->bookName(), m_key->chapter());
331 for (int i = 1; i <= count; i++)
332 menu.addAction(QString::number(i))->setProperty("verse", i);
333}
#define BT_CONNECT(...)
Definition btconnect.h:20
BtBibleKeyWidget(CSwordBibleModuleInfo const *module, CSwordVerseKey *key, QWidget *parent=nullptr)
const CSwordBibleModuleInfo * m_module
void enterEvent(QEnterEvent *event) override
CSwordVerseKey * m_key
bool eventFilter(QObject *o, QEvent *e) override
void resizeEvent(QResizeEvent *event) override
void populateBookMenu(QMenu &menu)
QLineEdit * m_textbox
void setModule(const CSwordBibleModuleInfo *m=nullptr)
void leaveEvent(QEvent *event) override
void populateChapterMenu(QMenu &menu)
void changed(CSwordVerseKey *key)
void populateVerseMenu(QMenu &menu)
bool setKey(CSwordVerseKey *key)
QWidget * m_dropDownButtons
void stepItem(int step)
BtLineEdit(QWidget *parent)
void focusInEvent(QFocusEvent *event) override
void signal()
void change(int count)
Implementation for Sword Bibles.
unsigned int verseCount(const unsigned int book, const unsigned int chapter) const
unsigned int chapterCount(const unsigned int book) const
QStringList const & books() const
CSwordKey implementation for Sword's VerseKey.
char bibleBook() const
QString bookName() const
bool next(const JumpType type=JumpType::UseVerse)
void setModule(const CSwordModuleInfo *newModule) final override
bool previous(const JumpType type=JumpType::UseVerse)
QString key() const final override
bool setKey(const QString &key) final override
int chapter() const
if(plainSearchedText)