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-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 "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 
35 class 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  // Using "this" instead of "nullptr" works around a Qt 5.9 bug, QTBUG-61213
123  m_dropDownButtons = new QWidget(this);
124 
125  m_dropDownButtons->setWindowFlags(Qt::Popup);
126  m_dropDownButtons->setAttribute(Qt::WA_WindowPropagation);
127  m_dropDownButtons->setCursor(Qt::ArrowCursor);
128  QHBoxLayout *dropDownButtonsLayout(new QHBoxLayout(m_dropDownButtons));
129 
130  auto * const bookChooser =
132  *this);
133  bookChooser->setToolTip(tr("Select book"));
134  BT_CONNECT(bookChooser->menu(), &QMenu::triggered,
135  [this](QAction * const action) {
136  auto bookname = action->property("bookname").toString();
137  if (m_key->bookName() != bookname) {
138  m_key->setBookName(std::move(bookname));
139  updateText();
140  }
141  if (!updatelock)
142  Q_EMIT changed(m_key);
143  });
144  BT_CONNECT(bookChooser, &BtDropdownChooserButton::stepItem, slotStepBook);
145  dropDownButtonsLayout->addWidget(bookChooser, 2);
146 
147  auto * const chapterChooser =
149  *this);
150  chapterChooser->setToolTip(tr("Select chapter"));
151  BT_CONNECT(chapterChooser->menu(), &QMenu::triggered,
152  [this](QAction * const action) {
153  int const n = action->property("chapter").toInt();
154  if (m_key->chapter() != n) {
155  m_key->setChapter(n);
156  updateText();
157  }
158  if (!updatelock)
159  Q_EMIT changed(m_key);
160  });
161  BT_CONNECT(chapterChooser,
163  slotStepChapter);
164  dropDownButtonsLayout->addWidget(chapterChooser, 1);
165 
166  auto * const verseChooser =
168  *this);
169  verseChooser->setToolTip(tr("Select verse"));
170  BT_CONNECT(verseChooser->menu(), &QMenu::triggered,
171  [this](QAction * const action) {
172  int const n = action->property("verse").toInt();
173  if (m_key->verse() != n) {
174  m_key->setVerse(n);
175  updateText();
176  }
177  if (!updatelock)
178  Q_EMIT changed(m_key);
179  });
180  BT_CONNECT(verseChooser, &BtDropdownChooserButton::stepItem, slotStepVerse);
181  dropDownButtonsLayout->addWidget(verseChooser, 1);
182 
183  dropDownButtonsLayout->setContentsMargins(0, 0, 0, 0);
184  dropDownButtonsLayout->setSpacing(0);
185  m_dropDownButtons->setLayout(dropDownButtonsLayout);
186  m_dropDownButtons->hide();
187 
188  m_dropDownButtons->installEventFilter(this);
189 
190  m_dropDownHoverTimer.setInterval(500);
191  m_dropDownHoverTimer.setSingleShot(true);
192  BT_CONNECT(&m_dropDownHoverTimer, &QTimer::timeout,
193  m_dropDownButtons, &QWidget::hide);
194 
195  QString scrollButtonToolTip(tr("Scroll through the entries of the list. Press the button and move the mouse to increase or decrease the item."));
196  bookScroller->setToolTips(
197  tr("Next book"),
198  scrollButtonToolTip,
199  tr("Previous book")
200  );
201  chapterScroller->setToolTips(
202  tr("Next chapter"),
203  scrollButtonToolTip,
204  tr("Previous chapter")
205  );
206  verseScroller->setToolTips(
207  tr("Next verse"),
208  scrollButtonToolTip,
209  tr("Previous verse")
210  );
211 
212  // signals and slots connections
213  auto const initScrollerConnections =
214  [this](CScrollerWidgetSet & scroller, auto stepFunction)
215  {
216  BT_CONNECT(&scroller,
218  stepFunction);
220  [this]{
221  updatelock = true;
222  oldKey = m_key->key();
223  });
225  [this]{
226  updatelock = false;
227  if (oldKey != m_key->key())
228  Q_EMIT changed(m_key);
229  });
230  };
231  initScrollerConnections(*bookScroller, slotStepBook);
232  initScrollerConnections(*chapterScroller, slotStepChapter);
233  initScrollerConnections(*verseScroller, slotStepVerse);
234 
235  BT_CONNECT(m_textbox, &QLineEdit::returnPressed,
236  [this]{
237  m_key->setKey(m_textbox->text());
238  Q_EMIT changed(m_key);
239  });
240 
241  BT_CONNECT(m_key->afterChangedSignaller(), &BtSignal::signal,
243 
244  setKey(key); // The order of these two functions is important.
245  setModule();
246 }
247 
249  delete m_dropDownButtons;
250 }
251 
253  if (m) { //can be null
254  m_module = m;
255  m_key->setModule(m);
256  }
257 }
258 
260  if (o != m_dropDownButtons) return false;
261  switch (e->type()) {
262  case QEvent::Enter:
263  m_dropDownHoverTimer.stop();
264  return true;
265  case QEvent::Leave:
266  m_dropDownHoverTimer.start();
267  return true;
268  default:
269  return false;
270  }
271 }
272 
273 #if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
274 void BtBibleKeyWidget::enterEvent(QEvent *) {
275 #else
276 void BtBibleKeyWidget::enterEvent(QEnterEvent *) {
277 #endif
278  m_dropDownHoverTimer.stop();
279 
281 
282  m_dropDownButtons->raise();
283  m_dropDownButtons->show();
284 }
285 
287  m_dropDownHoverTimer.start();
288 }
289 
290 void BtBibleKeyWidget::resizeEvent(QResizeEvent *event) {
291  if (m_dropDownButtons->isVisible()) {
293  }
294  QWidget::resizeEvent(event);
295 }
296 
298  m_dropDownButtons->setParent(window());
299  int h(m_dropDownButtons->layout()->minimumSize().height());
300  QPoint topLeft(mapTo(window(),
301  QPoint(m_textbox->x(), m_textbox->y() + m_textbox->height())));
302  m_dropDownButtons->setGeometry(topLeft.x(), topLeft.y(),
303  m_textbox->width(), h);
304 }
305 
307  QString text(m_key->key());
308  m_textbox->setText(text);
309  QFontMetrics fm(m_textbox->font());
310  int nw(m_textbox->minimumSizeHint().width() + fm.horizontalAdvance(text));
311  if (nw > m_textbox->minimumWidth()) {
312  m_textbox->setMinimumWidth(nw);
313  m_textbox->updateGeometry();
314  }
315 }
316 
318  if (!key) return false;
319 
320  m_key->setKey(key->key());
321  return true;
322 }
323 
325  for (auto const & bookname : m_module->books())
326  menu.addAction(bookname)->setProperty("bookname", bookname);
327 }
328 
330  int count = m_module->chapterCount(m_key->bibleBook());
331  for (int i = 1; i <= count; i++)
332  menu.addAction(QString::number(i))->setProperty("chapter", i);
333 }
334 
336  int count = m_module->verseCount(m_key->bookName(), m_key->chapter());
337  for (int i = 1; i <= count; i++)
338  menu.addAction(QString::number(i))->setProperty("verse", i);
339 }
#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
~BtBibleKeyWidget() override
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)