BibleTime
cbookmarkindex.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 <memory>
14 #include <QAction>
15 #include <QApplication>
16 #include <QCursor>
17 #include <QDrag>
18 #include <QDragLeaveEvent>
19 #include <QDragMoveEvent>
20 #include <QDropEvent>
21 #include <QFileDialog>
22 #include <QMenu>
23 #include <QMouseEvent>
24 #include <QPainter>
25 #include <QPaintEvent>
26 #include <QScopeGuard>
27 #include <QTimer>
28 #include <QToolTip>
29 #include "../../backend/btbookmarksmodel.h"
30 #include "../../backend/config/btconfig.h"
31 #include "../../backend/drivers/cswordmoduleinfo.h"
32 #include "../../backend/managers/cswordbackend.h"
33 #include "../../util/btassert.h"
34 #include "../../util/btconnect.h"
35 #include "../../util/bticons.h"
36 #include "../../util/cresmgr.h"
37 #include "../../util/tool.h"
38 #include "../bookmarks/bteditbookmarkdialog.h"
39 #include "../bookmarks/cbookmarkindex.h"
40 #include "../btprinter.h"
41 #include "../BtMimeData.h"
42 #include "../messagedialog.h"
43 
44 
45 namespace {
46 
47 QString fileDialogFilter() {
48  return QStringLiteral("%1 (*.btb);;%2 (*)")
49  .arg(QObject::tr("BibleTime bookmark files"),
50  QObject::tr("All files"));
51 }
52 
53 } // anonymous namespace
54 
56  : QTreeView{parent}
57  , m_magTimer{this}
58  , m_bookmarksModel{nullptr}
59 {
60  setMouseTracking(true);
61  m_magTimer.setSingleShot(true);
62  m_magTimer.setInterval(
63  btConfig().value<int>(QStringLiteral("GUI/magDelay"), 400));
64  setContextMenuPolicy(Qt::CustomContextMenu);
65  setHeaderHidden(true);
66 
67  //--------------------------------------------------------------------------
68  // Initialize view:
69 
70  setHeaderHidden(true);
71 
72  setFocusPolicy(Qt::WheelFocus);
73 
74  //d'n'd related settings
75  setDragEnabled(true);
76  setAcceptDrops(true);
77  setDragDropMode(QAbstractItemView::DragDrop);
78  viewport()->setAcceptDrops(true);
79  setAutoScroll(true);
80  setAutoExpandDelay(800);
81 
82  setItemsExpandable(true);
83  setRootIsDecorated(true);
84  setAllColumnsShowFocus(true);
85  setSelectionMode(QAbstractItemView::ExtendedSelection);
86 
87  //setExpandsOnDoubleClick(true);
88  setEditTriggers(editTriggers() ^ QAbstractItemView::DoubleClicked);
89 
90  //setup the popup menu
91  m_popup = new QMenu{viewport()};
92  m_popup->setTitle(tr("Bookmarks"));
93  auto const addMenuAction =
94  [this](MenuAction const menuAction,
95  QString const & text,
96  QIcon const & pix,
97  auto && slot)
98  {
99  auto * const action = new QAction(pix, text, this);
100  BT_CONNECT(action, &QAction::triggered,
101  std::forward<decltype(slot)>(slot));
102  m_actions[menuAction] = action;
103  m_popup->addAction(action);
104  };
105  namespace MI = CResMgr::mainIndex;
106  addMenuAction(NewFolder, tr("New folder"), MI::newFolder::icon(),
107  [this]{
108  if (!selectedIndexes().empty()) {
109  if (m_bookmarksModel->isFolder(currentIndex()))
110  setCurrentIndex(
113  currentIndex()),
114  currentIndex()));
115  } else { // create a top level folder
116  setCurrentIndex(
118  m_bookmarksModel->rowCount() - 1,
119  QModelIndex()));
120  }
121  });
122  addMenuAction(ChangeFolder, tr("Rename folder"), MI::changeFolder::icon(),
123  [this]{
124  BT_ASSERT(m_bookmarksModel->isFolder(currentIndex()));
125  edit(currentIndex());
126  });
127  m_popup->addSeparator();
128  addMenuAction(EditBookmark, tr("Edit bookmark..."),
129  MI::editBookmark::icon(),
130  [this]{
131  QModelIndex const index = currentIndex();
133  auto * const module = m_bookmarksModel->module(index);
135  QStringLiteral("%1 (%2)")
136  .arg(m_bookmarksModel->key(index))
137  .arg(module
138  ? module->name()
139  : QObject::tr("unknown")),
140  index.data().toString(),
142  this);
143  if (d.exec() == QDialog::Accepted) {
144  m_bookmarksModel->setData(index, d.titleText());
146  d.descriptionText());
147  }
148  });
149  addMenuAction(SortFolderBookmarks, tr("Sort folder bookmarks..."),
150  MI::sortFolderBookmarks::icon(),
151  [this]{
152  BT_ASSERT(m_bookmarksModel->isFolder(currentIndex()));
153  m_bookmarksModel->sortItems(currentIndex());
154  });
155  addMenuAction(SortAllBookmarks, tr("Sort all bookmarks..."),
156  MI::sortAllBookmarks::icon(),
157  [this]{
159  auto const numRows = m_bookmarksModel->rowCount();
160  if (m_extraItem.row() != numRows - 1) {
161  m_bookmarksModel->removeRow(m_extraItem.row(),
162  m_extraItem.parent());
163  if (m_bookmarksModel->insertRows(numRows - 1, 1))
164  m_extraItem =
165  m_bookmarksModel->index(numRows - 1, 0);
166  }
167  });
168  addMenuAction(ImportBookmarks, tr("Import to folder..."),
169  MI::importBookmarks::icon(),
170  [this]{
171  BT_ASSERT(m_bookmarksModel->isFolder(currentIndex()));
172  QString const fileName =
173  QFileDialog::getOpenFileName(
174  nullptr,
175  QObject::tr("Import bookmarks"),
176  QString(),
177  fileDialogFilter());
178  if (!fileName.isEmpty())
179  m_bookmarksModel->load(fileName, currentIndex());
180  });
181  addMenuAction(ExportBookmarks, tr("Export from folder..."),
182  MI::exportBookmarks::icon(),
183  [this]{
184  BT_ASSERT(m_bookmarksModel->isFolder(currentIndex()));
185  QString const fileName =
186  QFileDialog::getSaveFileName(
187  nullptr,
188  QObject::tr("Export Bookmarks"),
189  QString(),
190  fileDialogFilter());
191  if (!fileName.isEmpty())
192  m_bookmarksModel->save(fileName, currentIndex());
193  });
194  addMenuAction(PrintBookmarks, tr("Print bookmarks..."),
195  MI::printBookmarks::icon(),
196  [this]{
197  BT_ASSERT(hasBookmarksRecursively(selectedIndexes()));
198  BtPrinter::KeyTree tree;
199  {
200  BtPrinter::KeyTreeItem::Settings const settings{
201  false,
202  BtPrinter::KeyTreeItem::Settings::CompleteShort};
203  QModelIndexList items(selectedIndexes());
204  while (!items.empty()) {
205  QModelIndex const index(items.takeFirst());
206  if (m_bookmarksModel->isBookmark(index)) {
207  tree.emplace_back(
208  m_bookmarksModel->key(index),
209  m_bookmarksModel->module(index),
210  settings);
211  } else if (m_bookmarksModel->isFolder(index)) {
212  int const numChildren =
213  m_bookmarksModel->rowCount(index);
214  for (int i = 0; i < numChildren; i++)
215  items.append(index.model()->index(i,
216  0,
217  index));
218  }
219  }
220  }
221  BT_ASSERT(!tree.empty());
222 
225  this}.printKeyTree(tree);
226  });
227  m_popup->addSeparator();
228  addMenuAction(DeleteEntries, tr("Remove selected items..."),
229  MI::deleteItems::icon(),
230  [this]{
232  this,
233  tr("Delete Items"),
234  tr("Do you really want to delete the selected "
235  "items and folders?"),
236  QMessageBox::Yes | QMessageBox::No,
237  QMessageBox::No) == QMessageBox::Yes)
238  deleteEntries();
239  });
240 
241  //--------------------------------------------------------------------------
242  // Initialize connections:
243 
244  BT_CONNECT(this, &CBookmarkIndex::activated,
245  [this](QModelIndex const & index) {
246  /** \note HACK: checking the modifier keys from the last
247  mouseReleaseEvent depends on executing order:
248  mouseReleaseEvent first, then itemClicked signal.*/
249  auto const modifiers = m_mouseReleaseEventModifiers;
250  m_mouseReleaseEventModifiers = Qt::NoModifier;
251  if (modifiers != Qt::NoModifier || !index.isValid())
252  return;
253 
254  // Clicked on a bookmark:
255  if (m_bookmarksModel->isBookmark(index))
256  if (auto * const mod = m_bookmarksModel->module(index))
258  QList<CSwordModuleInfo *>() << mod,
259  m_bookmarksModel->key(index));
260  });
261  BT_CONNECT(this, &CBookmarkIndex::customContextMenuRequested,
262  [this](QPoint const & p) {
263  // Enable actions based on the selected items (if any):
264  QModelIndex const i(indexAt(p));
265  QModelIndexList const items(selectedIndexes());
266  if (items.isEmpty()) { // Special handling for no selection:
267  for (int index = ActionBegin; index < ActionEnd; ++index)
268  m_actions[index]->setEnabled(
269  (index == NewFolder)
270  || (index == SortAllBookmarks));
271  } else if (items.count() == 1) {
272  // Special handling for one selected item:
273  for (int index = ActionBegin; index < ActionEnd; ++index)
274  m_actions[index]->setEnabled(
275  enableAction(items.at(0),
276  static_cast<MenuAction>(index)));
277  } else if (!i.isValid()) {
278  // Disable all actions for invalid index:
279  for (int index = ActionBegin; index < ActionEnd; ++index)
280  m_actions[index]->setEnabled(false);
281  } else {
282  // Enable actions depending on the the selected items:
283  for (int index = ActionBegin; index < ActionEnd; ++index)
284  m_actions[index]->setEnabled(
285  (index == DeleteEntries)
286  || ((index == PrintBookmarks)
287  && hasBookmarksRecursively(items)));
288  }
289  m_popup->exec(mapToGlobal(p));
290  });
291  BT_CONNECT(&m_magTimer, &QTimer::timeout,
292  [this]{
293  if (!underMouse())
294  return;
295 
296  /* Update the Mag only if the mouse pointer have been over
297  the same item since the timer was started. */
298  QModelIndex const itemUnderPointer(
299  indexAt(mapFromGlobal(QCursor::pos())));
300  if (itemUnderPointer.isValid()
301  && m_previousEventItem == itemUnderPointer
302  && m_bookmarksModel->isBookmark(itemUnderPointer))
303  {
304  if (CSwordModuleInfo const * const module =
305  m_bookmarksModel->module(itemUnderPointer))
306  {
307  Q_EMIT magInfoProvided(
309  QStringLiteral("%1:%2")
310  .arg(module->name(),
312  itemUnderPointer)));
313  } else {
314  Q_EMIT magInfoProvided(
316  tr("The work to which the bookmark "
317  "points to is not installed."));
318  }
319  }
320  });
321 
322  //--------------------------------------------------------------------------
323  // Initialize tree:
324 
326  setModel(m_bookmarksModel);
327 
328  // add the invisible extra item at the end
331  showExtraItem();
332 }
333 
334 /** \note Hack to get single click and selection working. See slotExecuted. */
335 void CBookmarkIndex::mouseReleaseEvent(QMouseEvent* event) {
336  m_mouseReleaseEventModifiers = event->modifiers();
337  QTreeView::mouseReleaseEvent(event);
338 }
339 
340 /** Creates a drag mime data object for the current selection. */
342  BTMimeData::ItemList bookmarks;
343  for (auto const & widgetItem : selectedIndexes()) {
344  if (!widgetItem.isValid())
345  break;
346  if (m_bookmarksModel->isBookmark(widgetItem)) {
347  /* Take care of bookmarks which have no valid module any more, e.g.
348  if these were uninstalled: */
349  CSwordModuleInfo * const module =
350  m_bookmarksModel->module(widgetItem);
351  const QString moduleName = module ? module->name() : QString();
352  bookmarks.append({moduleName,
353  m_bookmarksModel->key(widgetItem),
354  m_bookmarksModel->description(widgetItem)});
355  }
356  }
357  return new BTMimeData(std::move(bookmarks));
358 }
359 
360 void CBookmarkIndex::dragEnterEvent(QDragEnterEvent * event) {
361  if (event->mimeData()->hasFormat(QStringLiteral("BibleTime/Bookmark"))) {
362  event->acceptProposedAction();
363  setState(DraggingState);
364  } else {
365  QAbstractItemView::dragEnterEvent(event);
366  }
367 }
368 
369 void CBookmarkIndex::dragMoveEvent(QDragMoveEvent * event) {
370  // Do this first, otherwise the event may be ignored:
371  QTreeView::dragMoveEvent(event);
372 
373  event->acceptProposedAction();
374  event->accept();
375 
376  // Do this to paint the arrow:
377  #if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
378  m_dragMovementPosition = event->pos();
379  #else
380  m_dragMovementPosition = event->position().toPoint();
381  #endif
382  viewport()->update();
383 }
384 
385 void CBookmarkIndex::dragLeaveEvent(QDragLeaveEvent *) {
386  setState(QAbstractItemView::NoState); // Not dragging anymore
387  viewport()->update(); // Clear the arrow
388 }
389 
390 void CBookmarkIndex::paintEvent(QPaintEvent * event) {
391  // Do the normal painting first
392  QTreeView::paintEvent(event);
393 
394  // Don't paint the arrow if not dragging:
395  if (state() != QAbstractItemView::DraggingState)
396  return;
397 
398  static QPixmap pix;
399  static int halfPixHeight;
400  static bool arrowInitialized = false;
401 
402  // Initialize the static variables, including the arrow pixmap
403  if (!arrowInitialized) {
404  arrowInitialized = true;
405  pix =
407  util::tool::mWidth(*this, 1));
408  halfPixHeight = pix.height() / 2;
409  }
410 
411  // Find the place for the arrow:
412  QModelIndex const index = indexAt(m_dragMovementPosition);
413  int xCoord, yCoord;
414  if (m_bookmarksModel->isBookmark(index)) {
415  QRect const rect = visualRect(index);
416  xCoord = QApplication::isRightToLeft() ? rect.right() : rect.left();
417  if (m_dragMovementPosition.y() > rect.bottom() - rect.height() / 2) {
418  yCoord = rect.bottom() - halfPixHeight; // bottom
419  } else {
420  yCoord = rect.top() - halfPixHeight - 1; // top
421  }
422  } else {
423  if (m_bookmarksModel->isFolder(index)) {
424  QRect const rect = visualRect(index);
425  if (m_dragMovementPosition.y()
426  > rect.bottom() - (2 * rect.height() / 3))
427  {
428  yCoord = rect.bottom() - halfPixHeight; // bottom
429  xCoord = QApplication::isRightToLeft()
430  ? (rect.right() - indentation())
431  : (rect.left() + indentation());
432  } else {
433  yCoord = rect.top() - halfPixHeight - 1; // top
434  xCoord = QApplication::isRightToLeft()
435  ? rect.right()
436  : rect.left();
437  }
438  } else if (index.isValid()) { // the extra item
439  QRect const rect = visualRect(index);
440  xCoord = QApplication::isRightToLeft() ? rect.right() : rect.left();
441  yCoord = rect.top() - halfPixHeight - 1;
442  } else { // empty area
443  QRect const rect = visualRect(m_extraItem);
444  yCoord = rect.top() - halfPixHeight - 1;
445  xCoord = QApplication::isRightToLeft() ? rect.right() : rect.left();
446  }
447  }
448  QPainter{this->viewport()}.drawPixmap(xCoord, yCoord, pix);
449 }
450 
451 
452 void CBookmarkIndex::dropEvent(QDropEvent * event) {
453  // Try to prevent annoying timed autocollapsing:
454  auto const connection =
455  BT_CONNECT(this, &CBookmarkIndex::collapsed,
456  [this](QModelIndex const & index) { expand(index); });
457  auto cleanup = qScopeGuard([&connection]() noexcept
458  { disconnect(connection); });
459  #if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
460  auto const pos = event->pos();
461  #else
462  auto const pos = event->position().toPoint();
463  #endif
464  auto const index = indexAt(pos);
465  QModelIndex parentIndex;
466  int indexUnderParent = 0;
467 
468  // Find the place where the drag is dropped
469  if (index.isValid()) {
470  if (m_bookmarksModel->isFolder(index)) {
471  QRect const rect = visualRect(index);
472  if (pos.y() > rect.bottom() - (2 * rect.height() / 3)) {
473  parentIndex = index;
474  } else {
475  parentIndex = index.parent();
476  indexUnderParent = index.row(); // before the current folder
477  }
478  } else {
479  if (m_bookmarksModel->isBookmark(index)) {
480  parentIndex = index.parent();
481  indexUnderParent = index.row(); // before the current bookmark
482  QRect const rect = visualRect(index);
483  if (pos.y() > rect.bottom() - rect.height() / 2)
484  indexUnderParent++; // after the current bookmark
485  } else { // item is the extra item
486  parentIndex = index.parent();
487  indexUnderParent = index.row(); // before the current bookmark
488  }
489  }
490  } else { // no item under event point: drop to the end
491  indexUnderParent = m_bookmarksModel->rowCount() - 1;
492  }
493 
494  if (event->source() != this) {
495  // Take the bookmark data from the mime source
496  if (BTMimeData const * const mdata =
497  dynamic_cast<BTMimeData const *>(event->mimeData()))
498  {
499  //create the new bookmark
500  auto const & bookmark = mdata->bookmarks().first();
501  QString moduleName(bookmark.module());
502  QString keyText(bookmark.key());
503  QString description(bookmark.description());
504  auto * const minfo =
506  std::move(moduleName));
507  BT_ASSERT(minfo);
508 
509  /// \todo add title
510  m_bookmarksModel->addBookmark(indexUnderParent,
511  parentIndex,
512  *minfo,
513  std::move(keyText),
514  std::move(description));
515  }
516  } else {
517  event->accept();
518 
519  bool bookmarksOnly = true;
520  bool targetIncluded = false;
521  bool moreThanOneFolder = false;
522 
523  QModelIndexList const list = selectedIndexes();
524  QModelIndexList newList;
525 
526  for (auto const & index : list) {
527  if (m_bookmarksModel->isFolder(index)) {
528  bookmarksOnly = false;
529  // Only one item allowed if a folder is selected:
530  if (list.count() > 1) {
531  moreThanOneFolder = true;
532  break;
533  }
534  // Dropping to self or descendand not allowed:
535  if (m_bookmarksModel->hasDescendant(index, parentIndex)) {
536  targetIncluded = true;
537  break;
538  }
539  } else {
540  newList.append(index);
541  }
542  }
543 
544  if (!bookmarksOnly && list.count() == 1) {
545  newList.append(list[0]); // list contain only one folder item
546  } else if (!bookmarksOnly && list.count() > 1) {
547  moreThanOneFolder = true; // wrong amount of items
548  }
549 
550  if (moreThanOneFolder) {
551  QToolTip::showText(QCursor::pos(), tr("Can drop only bookmarks or one folder"));
552  return;
553  }
554  if (targetIncluded) {
555  QToolTip::showText(QCursor::pos(), tr("Can't drop folder into the folder itself or into its subfolder"));
556  return;
557  }
558 
559  // Ask whether to copy or move with a popup menu
560  std::unique_ptr<QMenu> dropPopupMenu{new QMenu{this}};
561  dropPopupMenu->setEnabled(!newList.empty());
562  QAction * const copy = dropPopupMenu->addAction(tr("Copy"));
563  QAction * const move = dropPopupMenu->addAction(tr("Move"));
564  QAction * const dropAction = dropPopupMenu->exec(QCursor::pos());
565  dropPopupMenu.reset();
566 
567  if (dropAction == copy) {
568  m_bookmarksModel->copyItems(indexUnderParent, parentIndex, newList);
569  } else if (dropAction == move) {
570  m_bookmarksModel->copyItems(indexUnderParent, parentIndex, newList);
571  deleteEntries();
572  } else { // user canceled
573  return;
574  }
575  }
576  setState(QAbstractItemView::NoState);
577 }
578 
579 bool CBookmarkIndex::enableAction(QModelIndex const & index,
580  CBookmarkIndex::MenuAction const type) const
581 {
582  switch (type) {
583  case NewFolder:
584  case ChangeFolder:
585  case SortFolderBookmarks:
586  case ImportBookmarks:
587  case ExportBookmarks:
588  return m_bookmarksModel->isFolder(index);
589  case DeleteEntries:
590  return true;
591  case PrintBookmarks:
592  return hasBookmarksRecursively(QModelIndexList{} << index);
593  case EditBookmark:
594  return m_bookmarksModel->isBookmark(index);
595  default:
596  return false;
597  }
598 }
599 
600 bool CBookmarkIndex::hasBookmarksRecursively(QModelIndexList items) const {
601  while (!items.empty()) {
602  QModelIndex const index = items.takeFirst();
603  if (m_bookmarksModel->isBookmark(index))
604  return true;
605  if (m_bookmarksModel->isFolder(index)) {
606  int const numChildren = m_bookmarksModel->rowCount(index);
607  for (int i = 0; i < numChildren; i++)
608  items.append(index.model()->index(i, 0, index));
609  }
610  }
611  return false;
612 }
613 
615  model()->setData(m_extraItem,
616  tr("Drag references from text views to this view"));
617 }
618 
620 { model()->setData(m_extraItem, QVariant()); }
621 
622 void CBookmarkIndex::leaveEvent(QEvent * event) {
623  showExtraItem();
624  update();
625  QTreeView::leaveEvent(event);
626 }
627 
629  /* We need to use QPersistentModelIndex because after removeRows QModelIndex
630  will be invalidated. Need to delete per index because selected indexes
631  might be under different parents. */
633  for (auto const & i : selectedIndexes())
634  list.append(i);
635 
636  for (auto const & i : list)
637  model()->removeRows(i.row(), 1, i.parent());
638 }
639 
640 
641 /*
642 Reimplementation from QAbstractItemView/QTreeWidget. Takes care of movable items.
643 It's easier to use this than to start drag with mouse event handlers.
644 The default implementation would drag items, but we don't call it. Instead we create
645 a BibleTime mimedata object. It can be dragged and dropped to a text view or somewhere else.
646 The internal drag is handled differently, it doesn't use the mimedata (see dropEvent()).
647 */
648 void CBookmarkIndex::startDrag(Qt::DropActions) {
649  // Create the data which can be used in other widgets:
650  QMimeData * const mData = dragObject();
651  QDrag * const drag = new QDrag{this};
652  drag->setMimeData(mData);
653  drag->exec();
654 
655  viewport()->update(); // because of the arrow
656 }
657 
658 void CBookmarkIndex::mouseMoveEvent(QMouseEvent * event) {
659  /* Restart the mag timer if we have moved to another item and shift was not
660  pressed: */
661  QModelIndex const itemUnderPointer = indexAt(event->pos());
662  if (itemUnderPointer.isValid()
663  && (itemUnderPointer != m_previousEventItem)
664  && !(event->modifiers() & Qt::ShiftModifier))
665  m_magTimer.start();
666  m_previousEventItem = itemUnderPointer;
667 
668  if (!itemUnderPointer.isValid() || itemUnderPointer == m_extraItem) {
669  showExtraItem();
670  } else {
671  hideExtraItem();
672  }
673  update();
674 
675  QTreeView::mouseMoveEvent(event);
676 }
#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< BookmarkItem > ItemList
Definition: BtMimeData.h:38
int rowCount(const QModelIndex &parent=QModelIndex()) const override
void copyItems(int row, const QModelIndex &parent, const QModelIndexList &toCopy)
Copies item to target position.
bool isBookmark(const QModelIndex &index) const
bool hasDescendant(const QModelIndex &baseIndex, const QModelIndex &testIndex) const
bool isFolder(const QModelIndex &index) const
void sortItems(QModelIndex const &parent=QModelIndex(), Qt::SortOrder const order=Qt::AscendingOrder)
QModelIndex index(int row, int column, const QModelIndex &parent=QModelIndex()) const override
QString description(const QModelIndex &index) const
bool setData(const QModelIndex &index, const QVariant &value, int role=Qt::EditRole) override
void setDescription(const QModelIndex &index, const QString &description)
set descritpion for index.
bool load(QString fileName=QString(), const QModelIndex &rootItem=QModelIndex())
Import bookmarks from file.
CSwordModuleInfo * module(const QModelIndex &index) const
bool insertRows(int row, int count, const QModelIndex &parent=QModelIndex()) override
QModelIndex addFolder(int row, const QModelIndex &parent, const QString &name=QString())
add new folder.
bool save(QString fileName=QString(), const QModelIndex &rootItem=QModelIndex())
Save bookmarks or specified branch to file.
QModelIndex addBookmark(int const row, QModelIndex const &parent, CSwordModuleInfo const &module, QString const &key, QString const &description=QString(), QString const &title=QString())
add new item with given parameters
QString key(const QModelIndex &index) const
DisplayOptions getDisplayOptions() const
Definition: btconfig.h:176
FilterOptions getFilterOptions() const
Definition: btconfig.h:160
A dialog box for editing bookmarks.
RegularIcon const icon_pointing_arrow
Definition: bticons.h:99
static BtIcons & instance()
Definition: bticons.h:44
Manages the print item queue and printing.
Definition: btprinter.h:20
void createReadDisplayWindow(QList< CSwordModuleInfo * >, QString const &)
Emitted when a module should be opened.
void startDrag(Qt::DropActions supportedActions) override
QPoint m_dragMovementPosition
BtBookmarksModel * m_bookmarksModel
void mouseReleaseEvent(QMouseEvent *event) override
void leaveEvent(QEvent *event) override
QMimeData * dragObject()
int m_mouseReleaseEventModifiers
void dragEnterEvent(QDragEnterEvent *event) override
void mouseMoveEvent(QMouseEvent *event) override
void magInfoProvided(Rendering::InfoType const, QString const &data)
QModelIndex m_previousEventItem
void paintEvent(QPaintEvent *event) override
CBookmarkIndex(QWidget *const parent=nullptr)
QAction * m_actions[ActionCount]
void dragLeaveEvent(QDragLeaveEvent *event) override
void dragMoveEvent(QDragMoveEvent *event) override
bool hasBookmarksRecursively(QModelIndexList selected) const
QPersistentModelIndex m_extraItem
void dropEvent(QDropEvent *event) override
bool enableAction(QModelIndex const &index, MenuAction const type) const
CSwordModuleInfo * findModuleByName(const QString &name) const
Searches for a module with the given name.
static CSwordBackend & instance() noexcept
Definition: cswordbackend.h:98
QString const & name() const
std::list< KeyTreeItem > KeyTree
if(plainSearchedText)
QMessageBox::StandardButton showQuestion(QWidget *parent, const QString &title, const QString &text, QMessageBox::StandardButtons buttons, QMessageBox::StandardButton defaultButton)
int mWidth(QWidget const &widget, int const mCount)
Calculates a maximum rendered text width for a widget and a string with the a given length.
Definition: tool.cpp:155