15 #include <QApplication>
18 #include <QDragLeaveEvent>
19 #include <QDragMoveEvent>
21 #include <QFileDialog>
23 #include <QMouseEvent>
25 #include <QPaintEvent>
26 #include <QScopeGuard>
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"
48 return QStringLiteral(
"%1 (*.btb);;%2 (*)")
49 .arg(QObject::tr(
"BibleTime bookmark files"),
50 QObject::tr(
"All files"));
58 , m_bookmarksModel{nullptr}
60 setMouseTracking(
true);
63 btConfig().value<int>(QStringLiteral(
"GUI/magDelay"), 400));
64 setContextMenuPolicy(Qt::CustomContextMenu);
65 setHeaderHidden(
true);
70 setHeaderHidden(
true);
72 setFocusPolicy(Qt::WheelFocus);
77 setDragDropMode(QAbstractItemView::DragDrop);
78 viewport()->setAcceptDrops(
true);
80 setAutoExpandDelay(800);
82 setItemsExpandable(
true);
83 setRootIsDecorated(
true);
84 setAllColumnsShowFocus(
true);
85 setSelectionMode(QAbstractItemView::ExtendedSelection);
88 setEditTriggers(editTriggers() ^ QAbstractItemView::DoubleClicked);
92 m_popup->setTitle(tr(
"Bookmarks"));
93 auto const addMenuAction =
99 auto *
const action =
new QAction(pix, text,
this);
101 std::forward<decltype(slot)>(slot));
106 addMenuAction(
NewFolder, tr(
"New folder"), MI::newFolder::icon(),
108 if (!selectedIndexes().empty()) {
122 addMenuAction(
ChangeFolder, tr(
"Rename folder"), MI::changeFolder::icon(),
125 edit(currentIndex());
129 MI::editBookmark::icon(),
131 QModelIndex
const index = currentIndex();
135 QStringLiteral(
"%1 (%2)")
139 : QObject::tr(
"unknown")),
140 index.data().toString(),
143 if (d.exec() == QDialog::Accepted) {
150 MI::sortFolderBookmarks::icon(),
156 MI::sortAllBookmarks::icon(),
161 m_bookmarksModel->removeRow(m_extraItem.row(),
162 m_extraItem.parent());
163 if (m_bookmarksModel->insertRows(numRows - 1, 1))
165 m_bookmarksModel->index(numRows - 1, 0);
169 MI::importBookmarks::icon(),
172 QString
const fileName =
173 QFileDialog::getOpenFileName(
175 QObject::tr(
"Import bookmarks"),
178 if (!fileName.isEmpty())
182 MI::exportBookmarks::icon(),
185 QString
const fileName =
186 QFileDialog::getSaveFileName(
188 QObject::tr(
"Export Bookmarks"),
191 if (!fileName.isEmpty())
195 MI::printBookmarks::icon(),
202 BtPrinter::KeyTreeItem::Settings::CompleteShort};
203 QModelIndexList items(selectedIndexes());
204 while (!items.empty()) {
205 QModelIndex
const index(items.takeFirst());
212 int const numChildren =
214 for (
int i = 0; i < numChildren; i++)
215 items.append(index.model()->index(i,
225 this}.printKeyTree(tree);
229 MI::deleteItems::icon(),
234 tr(
"Do you really want to delete the selected "
235 "items and folders?"),
236 QMessageBox::Yes | QMessageBox::No,
237 QMessageBox::No) == QMessageBox::Yes)
245 [
this](QModelIndex
const & index) {
251 if (modifiers != Qt::NoModifier || !index.isValid())
261 BT_CONNECT(
this, &CBookmarkIndex::customContextMenuRequested,
262 [
this](QPoint
const & p) {
264 QModelIndex
const i(indexAt(p));
265 QModelIndexList
const items(selectedIndexes());
266 if (items.isEmpty()) {
271 }
else if (items.count() == 1) {
277 }
else if (!i.isValid()) {
298 QModelIndex
const itemUnderPointer(
299 indexAt(mapFromGlobal(QCursor::pos())));
300 if (itemUnderPointer.isValid()
309 QStringLiteral(
"%1:%2")
316 tr(
"The work to which the bookmark "
317 "points to is not installed."));
337 QTreeView::mouseReleaseEvent(event);
343 for (
auto const & widgetItem : selectedIndexes()) {
344 if (!widgetItem.isValid())
351 const QString moduleName = module ? module->
name() : QString();
352 bookmarks.append({moduleName,
361 if (event->mimeData()->hasFormat(QStringLiteral(
"BibleTime/Bookmark"))) {
362 event->acceptProposedAction();
363 setState(DraggingState);
365 QAbstractItemView::dragEnterEvent(event);
371 QTreeView::dragMoveEvent(event);
373 event->acceptProposedAction();
377 #if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
382 viewport()->update();
386 setState(QAbstractItemView::NoState);
387 viewport()->update();
392 QTreeView::paintEvent(event);
395 if (state() != QAbstractItemView::DraggingState)
399 static int halfPixHeight;
400 static bool arrowInitialized =
false;
403 if (!arrowInitialized) {
404 arrowInitialized =
true;
408 halfPixHeight = pix.height() / 2;
415 QRect
const rect = visualRect(index);
416 xCoord = QApplication::isRightToLeft() ? rect.right() : rect.left();
418 yCoord = rect.bottom() - halfPixHeight;
420 yCoord = rect.top() - halfPixHeight - 1;
424 QRect
const rect = visualRect(index);
426 > rect.bottom() - (2 * rect.height() / 3))
428 yCoord = rect.bottom() - halfPixHeight;
429 xCoord = QApplication::isRightToLeft()
430 ? (rect.right() - indentation())
431 : (rect.left() + indentation());
433 yCoord = rect.top() - halfPixHeight - 1;
434 xCoord = QApplication::isRightToLeft()
438 }
else if (index.isValid()) {
439 QRect
const rect = visualRect(index);
440 xCoord = QApplication::isRightToLeft() ? rect.right() : rect.left();
441 yCoord = rect.top() - halfPixHeight - 1;
444 yCoord = rect.top() - halfPixHeight - 1;
445 xCoord = QApplication::isRightToLeft() ? rect.right() : rect.left();
448 QPainter{this->viewport()}.drawPixmap(xCoord, yCoord, pix);
454 auto const connection =
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();
462 auto const pos =
event->position().toPoint();
464 auto const index = indexAt(pos);
465 QModelIndex parentIndex;
466 int indexUnderParent = 0;
469 if (index.isValid()) {
471 QRect
const rect = visualRect(index);
472 if (pos.y() > rect.bottom() - (2 * rect.height() / 3)) {
475 parentIndex = index.parent();
476 indexUnderParent = index.row();
480 parentIndex = index.parent();
481 indexUnderParent = index.row();
482 QRect
const rect = visualRect(index);
483 if (pos.y() > rect.bottom() - rect.height() / 2)
486 parentIndex = index.parent();
487 indexUnderParent = index.row();
494 if (event->source() !=
this) {
497 dynamic_cast<BTMimeData const *
>(event->mimeData()))
500 auto const & bookmark = mdata->bookmarks().first();
501 QString moduleName(bookmark.module());
502 QString keyText(bookmark.key());
503 QString description(bookmark.description());
506 std::move(moduleName));
514 std::move(description));
519 bool bookmarksOnly =
true;
520 bool targetIncluded =
false;
521 bool moreThanOneFolder =
false;
523 QModelIndexList
const list = selectedIndexes();
524 QModelIndexList newList;
526 for (
auto const & index : list) {
528 bookmarksOnly =
false;
530 if (list.count() > 1) {
531 moreThanOneFolder =
true;
536 targetIncluded =
true;
540 newList.append(index);
544 if (!bookmarksOnly && list.count() == 1) {
545 newList.append(list[0]);
546 }
else if (!bookmarksOnly && list.count() > 1) {
547 moreThanOneFolder =
true;
550 if (moreThanOneFolder) {
551 QToolTip::showText(QCursor::pos(), tr(
"Can drop only bookmarks or one folder"));
554 if (targetIncluded) {
555 QToolTip::showText(QCursor::pos(), tr(
"Can't drop folder into the folder itself or into its subfolder"));
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();
567 if (dropAction == copy) {
569 }
else if (dropAction == move) {
576 setState(QAbstractItemView::NoState);
601 while (!items.empty()) {
602 QModelIndex
const index = items.takeFirst();
607 for (
int i = 0; i < numChildren; i++)
608 items.append(index.model()->index(i, 0, index));
616 tr(
"Drag references from text views to this view"));
625 QTreeView::leaveEvent(event);
633 for (
auto const & i : selectedIndexes())
636 for (
auto const & i : list)
637 model()->removeRows(i.row(), 1, i.parent());
651 QDrag *
const drag =
new QDrag{
this};
652 drag->setMimeData(mData);
655 viewport()->update();
661 QModelIndex
const itemUnderPointer = indexAt(event->pos());
662 if (itemUnderPointer.isValid()
664 && !(event->modifiers() & Qt::ShiftModifier))
668 if (!itemUnderPointer.isValid() || itemUnderPointer ==
m_extraItem) {
675 QTreeView::mouseMoveEvent(event);
BtConfig & btConfig()
This is a shortchand for BtConfig::getInstance().
QList< BookmarkItem > ItemList
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
FilterOptions getFilterOptions() const
A dialog box for editing bookmarks.
QString descriptionText()
RegularIcon const icon_pointing_arrow
static BtIcons & instance()
Manages the print item queue and printing.
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
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
QString const & name() const
std::list< KeyTreeItem > KeyTree
QString fileDialogFilter()
QMessageBox::StandardButton showQuestion(QWidget *parent, const QString &title, const QString &text, QMessageBox::StandardButtons buttons, QMessageBox::StandardButton defaultButton)