/********* * * This file is part of BibleTime's source code, http://www.bibletime.info/. * * Copyright 1999-2011 by the BibleTime developers. * The BibleTime source code is licensed under the GNU General Public License version 2.0. * **********/ #include "frontend/bookmarks/cbookmarkindex.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "backend/config/cbtconfig.h" #include "backend/drivers/cswordmoduleinfo.h" #include "backend/managers/referencemanager.h" #include "frontend/cdragdrop.h" #include "frontend/cinfodisplay.h" #include "frontend/cprinter.h" #include "frontend/bookmarks/btbookmarkitembase.h" #include "frontend/bookmarks/btbookmarkitem.h" #include "frontend/bookmarks/btbookmarkfolder.h" #include "frontend/bookmarks/btbookmarkloader.h" #include "frontend/searchdialog/csearchdialog.h" #include "util/cresmgr.h" #include "util/tool.h" #include "util/directory.h" #include "util/dialogutil.h" #include "bibletime.h" CBookmarkIndex::CBookmarkIndex(QWidget *parent) : QTreeWidget(parent), m_magTimer(this), m_previousEventItem(0) { setMouseTracking(true); m_magTimer.setSingleShot(true); m_magTimer.setInterval(CBTConfig::get(CBTConfig::magDelay)); setContextMenuPolicy(Qt::CustomContextMenu); initView(); initConnections(); initTree(); } CBookmarkIndex::~CBookmarkIndex() { saveBookmarks(); } /** Initializes the view. */ void CBookmarkIndex::initView() { setHeaderHidden(true); setFocusPolicy(Qt::WheelFocus); //d'n'd related settings setDragEnabled( true ); setAcceptDrops( true ); setDragDropMode(QAbstractItemView::DragDrop); viewport()->setAcceptDrops(true); setAutoScroll(true); setAutoExpandDelay(800); setItemsExpandable(true); setRootIsDecorated(true); setAllColumnsShowFocus(true); setSelectionMode(QAbstractItemView::ExtendedSelection); //setup the popup menu m_popup = new QMenu(viewport()); m_popup->setTitle(tr("Bookmarks")); m_actions.newFolder = newQAction(tr("New folder"), CResMgr::mainIndex::newFolder::icon, 0, this, SLOT(createNewFolder()), this); m_actions.changeFolder = newQAction(tr("Rename folder"), CResMgr::mainIndex::changeFolder::icon, 0, this, SLOT(changeFolder()), this); m_actions.editBookmark = newQAction(tr("Edit bookmark..."), CResMgr::mainIndex::editBookmark::icon, 0, this, SLOT(editBookmark()), this); /// \todo Add icons for sorting bookmarks m_actions.sortFolderBookmarks = newQAction(tr("Sort folder bookmarks..."), QString::null, 0, this, SLOT(sortFolderBookmarks()), this); m_actions.sortAllBookmarks = newQAction(tr("Sort all bookmarks..."), QString::null, 0, this, SLOT(sortAllBookmarks()), this); m_actions.importBookmarks = newQAction(tr("Import to folder..."), CResMgr::mainIndex::importBookmarks::icon, 0, this, SLOT(importBookmarks()), this); m_actions.exportBookmarks = newQAction(tr("Export from folder..."), CResMgr::mainIndex::exportBookmarks::icon, 0, this, SLOT(exportBookmarks()), this); m_actions.printBookmarks = newQAction(tr("Print bookmarks..."), CResMgr::mainIndex::printBookmarks::icon, 0, this, SLOT(printBookmarks()), this); m_actions.deleteEntries = newQAction(tr("Remove selected items..."), CResMgr::mainIndex::deleteItems::icon, 0, this, SLOT(deleteEntries()), this); //fill the popup menu itself m_popup->addAction(m_actions.newFolder); m_popup->addAction(m_actions.changeFolder); QAction* separator = new QAction(this); separator->setSeparator(true); m_popup->addAction(separator); m_popup->addAction(m_actions.editBookmark); m_popup->addAction(m_actions.sortFolderBookmarks); m_popup->addAction(m_actions.sortAllBookmarks); m_popup->addAction(m_actions.importBookmarks); m_popup->addAction(m_actions.exportBookmarks); m_popup->addAction(m_actions.printBookmarks); separator = new QAction(this); separator->setSeparator(true); m_popup->addAction(separator); m_popup->addAction(m_actions.deleteEntries); m_bookmarksModified = false; } /** Convenience function for creating a new QAction. * Should be replaced with something better; it was easier to make a new function * than to modify all QAction constructors. */ QAction* CBookmarkIndex::newQAction(const QString& text, const QString& pix, const int /*shortcut*/, const QObject* receiver, const char* slot, QObject* parent) { namespace DU = util::directory; QAction *action; if (pix.isEmpty()) { action = new QAction(text, parent); } else { action = new QAction(DU::getIcon(pix), text, parent); } QObject::connect(action, SIGNAL(triggered()), receiver, slot); return action; } /** Initialize the SIGNAL<->SLOT connections */ void CBookmarkIndex::initConnections() { bool ok; ok = connect(this, SIGNAL(itemActivated(QTreeWidgetItem*, int)), this, SLOT(slotExecuted(QTreeWidgetItem*))); Q_ASSERT(ok); ok = connect(this, SIGNAL(customContextMenuRequested(const QPoint&)), SLOT(contextMenu(const QPoint&))); Q_ASSERT(ok); ok = connect(&m_magTimer, SIGNAL(timeout()), this, SLOT(magTimeout())); Q_ASSERT(ok); ok = connect(this, SIGNAL(itemEntered(QTreeWidgetItem*, int)), this, SLOT(slotItemEntered(QTreeWidgetItem*, int)) ); Q_ASSERT(ok); // Connection to detect changes in the items themselves (e.g. renames, // description changes) so that we can consider saving the bookmarks. connect(this, SIGNAL(itemChanged(QTreeWidgetItem*, int)), this, SLOT(needToSaveBookmarks(QTreeWidgetItem*)) ); // Connect the bookmark saving timer. bookmarkSaveTimer.setSingleShot(true); connect(&bookmarkSaveTimer, SIGNAL(timeout()), this, SLOT(considerSavingBookmarks()) ); } /** * Hack to get single click and selection working. See slotExecuted. */ void CBookmarkIndex::mouseReleaseEvent(QMouseEvent* event) { m_mouseReleaseEventModifiers = event->modifiers(); QTreeWidget::mouseReleaseEvent(event); } /** Called when an item is clicked with mouse or activated with keyboard. */ void CBookmarkIndex::slotExecuted( QTreeWidgetItem* i ) { //HACK: checking the modifier keys from the last mouseReleaseEvent //depends on executing order: mouseReleaseEvent first, then itemClicked signal int modifiers = m_mouseReleaseEventModifiers; m_mouseReleaseEventModifiers = Qt::NoModifier; if (modifiers != Qt::NoModifier) { return; } BtBookmarkItemBase* btItem = dynamic_cast(i); if (!btItem) { return; } BtBookmarkFolder* folderItem = 0; BtBookmarkItem* bookmarkItem = 0; if ((folderItem = dynamic_cast(btItem))) { i->setExpanded( !i->isExpanded() ); } else if (( bookmarkItem = dynamic_cast(btItem) )) { //clicked on a bookmark if (CSwordModuleInfo* mod = bookmarkItem->module()) { QList modules; modules.append(mod); emit createReadDisplayWindow(modules, bookmarkItem->key()); } } } /** Creates a drag mime data object for the current selection. */ QMimeData* CBookmarkIndex::dragObject() { BTMimeData::ItemList dndItems; BTMimeData* mimeData = new BTMimeData; foreach( QTreeWidgetItem* widgetItem, selectedItems() ) { if (!widgetItem) break; if (dynamic_cast(widgetItem)) { if (BtBookmarkItem* bookmark = dynamic_cast( widgetItem )) { //take care of bookmarks which have no valid module any more, e.g. if it was uninstalled const QString moduleName = bookmark->module() ? bookmark->module()->name() : QString::null; mimeData->appendBookmark(moduleName, bookmark->key(), bookmark->description()); } } } return mimeData; } void CBookmarkIndex::dragEnterEvent( QDragEnterEvent* event ) { setState(QAbstractItemView::DraggingState); QTreeWidget::dragEnterEvent(event); if (event->source() == this || event->mimeData()->hasFormat("BibleTime/Bookmark")) { event->acceptProposedAction(); } } void CBookmarkIndex::dragMoveEvent( QDragMoveEvent* event ) { // do this first, otherwise the event may be ignored QTreeWidget::dragMoveEvent(event); event->acceptProposedAction(); event->accept(); // do this to paint the arrow m_dragMovementPosition = event->pos(); viewport()->update(); } void CBookmarkIndex::dragLeaveEvent( QDragLeaveEvent* ) { setState(QAbstractItemView::NoState); // not dragging anymore viewport()->update(); // clear the arrow } void CBookmarkIndex::paintEvent(QPaintEvent* event) { namespace DU = util::directory; static QPixmap pix; static int halfPixHeight; static bool arrowInitialized = false; // Initialize the static variables, including the arrow pixmap if (!arrowInitialized) { arrowInitialized = true; int arrowSize = util::tool::mWidth(this, 1); QString fileName; if (DU::getIconDir().exists("pointing_arrow.svg")) { fileName = DU::getIconDir().filePath("pointing_arrow.svg"); } else { if (DU::getIconDir().exists("pointing_arrow.png")) { fileName = DU::getIconDir().filePath("pointing_arrow.png"); } else { qWarning() << "Picture file pointing_arrow.svg or .png not found!"; } } pix = QPixmap(fileName); pix = pix.scaled(arrowSize, arrowSize, Qt::KeepAspectRatioByExpanding); halfPixHeight = pix.height() / 2; } // Do the normal painting first QTreeWidget::paintEvent(event); // Paint the arrow if the drag is going on if (QAbstractItemView::DraggingState == state()) { bool rtol = QApplication::isRightToLeft(); QPainter painter(this->viewport()); QTreeWidgetItem* item = itemAt(m_dragMovementPosition); bool isFolder = dynamic_cast(item); bool isBookmark = dynamic_cast(item); // Find the place for the arrow QRect rect = visualItemRect(item); int xCoord = rtol ? rect.right() : rect.left(); int yCoord; if (isFolder) { if (m_dragMovementPosition.y() > rect.bottom() - (2* rect.height() / 3) ) { yCoord = rect.bottom() - halfPixHeight; // bottom xCoord = rtol ? (xCoord - indentation()) : (xCoord + indentation()); } else { yCoord = rect.top() - halfPixHeight - 1; // top } } else { if (isBookmark) { if (m_dragMovementPosition.y() > rect.bottom() - rect.height() / 2) { yCoord = rect.bottom() - halfPixHeight; // bottom } else { yCoord = rect.top() - halfPixHeight - 1; // top } } else { if (item) { // the extra item yCoord = rect.top() - halfPixHeight - 1; } else { // empty area rect = visualItemRect(m_extraItem); yCoord = rect.top() - halfPixHeight - 1; xCoord = rtol ? rect.right() : rect.left(); } } } painter.drawPixmap(xCoord, yCoord, pix); } } void CBookmarkIndex::dropEvent( QDropEvent* event ) { //setState(QAbstractItemView::NoState); // Try to prevent annoying timed autocollapsing. Remember to disconnect before return. QObject::connect(this, SIGNAL(itemCollapsed(QTreeWidgetItem*)), this, SLOT(expandAutoCollapsedItem(QTreeWidgetItem*))); QTreeWidgetItem* item = itemAt(event->pos()); QTreeWidgetItem* parentItem = 0; int indexUnderParent = 0; // Find the place where the drag is dropped if (item) { QRect rect = visualItemRect(item); bool isFolder = dynamic_cast(item); bool isBookmark = dynamic_cast(item); if (isFolder) { // item is a folder if (event->pos().y() > rect.bottom() - (2* rect.height() / 3) ) { parentItem = item; } else { parentItem = item->parent(); if (!parentItem) { parentItem = invisibleRootItem(); } indexUnderParent = parentItem->indexOfChild(item); // before the current folder } } else { if (isBookmark) { // item is a bookmark parentItem = item->parent(); if (!parentItem) { parentItem = invisibleRootItem(); } indexUnderParent = parentItem->indexOfChild(item); // before the current bookmark if (event->pos().y() > rect.bottom() - rect.height() / 2) { indexUnderParent++; // after the current bookmark } } else { // item is the extra item parentItem = item->parent(); if (!parentItem) { parentItem = invisibleRootItem(); } indexUnderParent = parentItem->indexOfChild(item); // before the current bookmark } } } else { // no item under event point: drop to the end parentItem = invisibleRootItem(); indexUnderParent = parentItem->childCount() - 1; } if ( event->source() == this ) { event->accept(); bool bookmarksOnly = true; bool targetIncluded = false; bool moreThanOneFolder = false; QList newItems = addItemsToDropTree(parentItem, bookmarksOnly, targetIncluded, moreThanOneFolder); if (moreThanOneFolder) { QToolTip::showText(QCursor::pos(), tr("Can drop only bookmarks or one folder")); QObject::disconnect(this, SIGNAL(itemCollapsed(QTreeWidgetItem*)), this, SLOT(expandAutoCollapsedItem(QTreeWidgetItem*))); return; } if (targetIncluded) { QToolTip::showText(QCursor::pos(), tr("Can't drop folder into the folder itself or into its subfolder")); QObject::disconnect(this, SIGNAL(itemCollapsed(QTreeWidgetItem*)), this, SLOT(expandAutoCollapsedItem(QTreeWidgetItem*))); return; } // Ask whether to copy or move with a popup menu QMenu* dropPopupMenu = new QMenu(this); QAction* copy = dropPopupMenu->addAction(tr("Copy")); QAction* move = dropPopupMenu->addAction(tr("Move")); QAction* dropAction = dropPopupMenu->exec(QCursor::pos()); if (dropAction == copy) { parentItem->insertChildren(indexUnderParent, newItems); // Need this here because the "move" case goes through // "deleteEntries" which has a save call. needToSaveBookmarks(); } else { if (dropAction == move) { parentItem->insertChildren(indexUnderParent, newItems); deleteEntries(false); } else { QObject::disconnect(this, SIGNAL(itemCollapsed(QTreeWidgetItem*)), this, SLOT(expandAutoCollapsedItem(QTreeWidgetItem*))); return; // user canceled } } } else { createBookmarkFromDrop(event, parentItem, indexUnderParent); } QObject::disconnect(this, SIGNAL(itemCollapsed(QTreeWidgetItem*)), this, SLOT(expandAutoCollapsedItem(QTreeWidgetItem*))); setState(QAbstractItemView::NoState); } void CBookmarkIndex::createBookmarkFromDrop(QDropEvent* event, QTreeWidgetItem* parentItem, int indexInParent) { //take the bookmark data from the mime source const BTMimeData* mdata = dynamic_cast(event->mimeData()); if (mdata) { //create the new bookmark QString moduleName = mdata->bookmark().module(); QString keyText = mdata->bookmark().key(); QString description = mdata->bookmark().description(); CSwordModuleInfo *minfo = CSwordBackend::instance()->findModuleByName(moduleName); QString title; /// \todo QTreeWidgetItem* newItem = new BtBookmarkItem(minfo, keyText, description, title); // connect(newItem, SIGNAL(bookmarkModified()), this, SLOT(needToSaveBookmarks()) ); parentItem->insertChild(indexInParent, newItem); needToSaveBookmarks(); } } /** Load the tree from file */ void CBookmarkIndex::initTree() { BtBookmarkLoader loader; addTopLevelItems(loader.loadTree()); // add the invisible extra item at the end m_extraItem = new QTreeWidgetItem(); m_extraItem->setFlags(Qt::ItemIsDropEnabled); addTopLevelItem(m_extraItem); } void CBookmarkIndex::slotItemEntered(QTreeWidgetItem* item, int) { if (item == m_extraItem) { m_extraItem->setText(0, tr("Drag references from text views to this view")); } else { m_extraItem->setText(0, QString::null); } } /** Returns the correct QAction object for the given type of action. */ QAction* CBookmarkIndex::action( BtBookmarkItemBase::MenuAction type ) const { switch (type) { case BtBookmarkItemBase::NewFolder: return m_actions.newFolder; case BtBookmarkItemBase::ChangeFolder: return m_actions.changeFolder; case BtBookmarkItemBase::EditBookmark: return m_actions.editBookmark; case BtBookmarkItemBase::SortFolderBookmarks: return m_actions.sortFolderBookmarks; case BtBookmarkItemBase::SortAllBookmarks: return m_actions.sortAllBookmarks; case BtBookmarkItemBase::ImportBookmarks: return m_actions.importBookmarks; case BtBookmarkItemBase::ExportBookmarks: return m_actions.exportBookmarks; case BtBookmarkItemBase::PrintBookmarks: return m_actions.printBookmarks; case BtBookmarkItemBase::DeleteEntries: return m_actions.deleteEntries; default: return 0; } } /** Shows the context menu at the given position. */ void CBookmarkIndex::contextMenu(const QPoint& p) { //setup menu entries depending on current selection QTreeWidgetItem* i = itemAt(p); QList items = selectedItems(); //The item which was clicked may not be selected if (i && !items.contains(i) && i != m_extraItem) items.append(i); if (items.isEmpty()) { //special handling for no selection BtBookmarkItemBase::MenuAction actionType; for (int index = BtBookmarkItemBase::ActionBegin; index <= BtBookmarkItemBase::ActionEnd; ++index) { actionType = static_cast(index); if (QAction* a = action(actionType)) { switch (index) { //case BtBookmarkItemBase::ExportBookmarks: //case BtBookmarkItemBase::ImportBookmarks: case BtBookmarkItemBase::NewFolder: case BtBookmarkItemBase::SortAllBookmarks: //case BtBookmarkItemBase::PrintBookmarks: a->setEnabled(true); break; default: a->setEnabled(false); } } } } else if (items.count() == 1) { //special handling for one selected item BtBookmarkItemBase* item = dynamic_cast(items.at(0)); BtBookmarkItemBase::MenuAction actionType; for (int index = BtBookmarkItemBase::ActionBegin; index <= BtBookmarkItemBase::ActionEnd; ++index) { actionType = static_cast(index); if (QAction* a = action(actionType)) a->setEnabled( item->enableAction(actionType) ); } } else { //first disable all actions BtBookmarkItemBase::MenuAction actionType; for (int index = BtBookmarkItemBase::ActionBegin; index <= BtBookmarkItemBase::ActionEnd; ++index) { actionType = static_cast(index); if (QAction* a = action(actionType)) a->setEnabled(false); } //enable the menu items depending on the types of the selected items. for (int index = BtBookmarkItemBase::ActionBegin; index <= BtBookmarkItemBase::ActionEnd; ++index) { actionType = static_cast(index); bool enableAction = isMultiAction(actionType); QListIterator it(items); while (it.hasNext()) { BtBookmarkItemBase* i = dynamic_cast(it.next()); enableAction = enableAction && i->enableAction(actionType); } if (enableAction) { QAction* a = action(actionType) ; if (i && a) a->setEnabled(enableAction); } } } //finally, open the popup m_popup->exec(mapToGlobal(p)); } /** Adds a new subfolder to the current item. */ void CBookmarkIndex::createNewFolder() { QList selected = selectedItems(); if (selected.count() > 0) { BtBookmarkFolder* i = dynamic_cast(currentItem()); if (i) { i->newSubFolder(); } } else { // create a top level folder BtBookmarkFolder* newFolder = new BtBookmarkFolder(tr("New folder")); //parentFolder->addChild(newFolder); insertTopLevelItem(topLevelItemCount() - 1, newFolder); newFolder->update(); newFolder->rename(); } needToSaveBookmarks(); } /** Opens a dialog to change the current folder. */ void CBookmarkIndex::changeFolder() { BtBookmarkFolder* i = dynamic_cast(currentItem()); Q_ASSERT(i); if (i) { i->rename(); } } /** Edits the current bookmark. */ void CBookmarkIndex::editBookmark() { BtBookmarkItem* i = dynamic_cast(currentItem()); Q_ASSERT(i); if (i) { i->rename(); } } /** Sorts the current folder bookmarks. */ void CBookmarkIndex::sortFolderBookmarks() { BtBookmarkFolder* i = dynamic_cast(currentItem()); Q_ASSERT(i); if (i) { i->sortChildren(0, Qt::AscendingOrder); } } /** Sorts all bookmarks. */ void CBookmarkIndex::sortAllBookmarks() { sortItems(0, Qt::AscendingOrder); int index = indexOfTopLevelItem(m_extraItem); if (index >= 0) { QTreeWidgetItem* item = takeTopLevelItem(index); if (item != 0) { addTopLevelItem(m_extraItem); } } } /** Exports the bookmarks being in the selected folder. */ void CBookmarkIndex::exportBookmarks() { BtBookmarkFolder* i = dynamic_cast(currentItem()); Q_ASSERT(i); if (i) { i->exportBookmarks(); } } /** Import bookmarks from a file and add them to the selected folder. */ void CBookmarkIndex::importBookmarks() { BtBookmarkFolder* i = dynamic_cast(currentItem()); Q_ASSERT(i); if (i) { i->importBookmarks(); } needToSaveBookmarks(); } /** Prints the selected bookmarks. */ void CBookmarkIndex::printBookmarks() { Printing::CPrinter::KeyTree tree; Printing::CPrinter::KeyTreeItem::Settings settings; settings.keyRenderingFace = Printing::CPrinter::KeyTreeItem::Settings::CompleteShort; QList items; BtBookmarkFolder* bf = dynamic_cast(currentItem()); if (bf) { items = bf->getChildList(); } else { items = selectedItems(); } //create a tree of keytreeitems using the bookmark hierarchy. QListIterator it(items); while (it.hasNext()) { BtBookmarkItem* i = dynamic_cast(it.next()); if (i) { qDebug() << "printBookmarks: add to list" << i->key(); tree.append( new Printing::CPrinter::KeyTreeItem( i->key(), i->module(), settings ) ); } } if (items.isEmpty()) { qWarning("Tried to print empty bookmark list."); return; } QSharedPointer printer( new Printing::CPrinter( this, CBTConfig::getDisplayOptionDefaults(), CBTConfig::getFilterOptionDefaults() ) ); printer->printKeyTree(tree); } /** Deletes the selected entries. */ void CBookmarkIndex::deleteEntries(bool confirm) { if (confirm) { if (!selectedItems().count()) { BtBookmarkItemBase* f = dynamic_cast(currentItem()); if (f) { currentItem()->setSelected(true); } else { return; } } if (util::showQuestion(this, tr("Delete Items"), tr("Do you really want to delete the selected items and child-items?"), QMessageBox::Yes | QMessageBox::No, QMessageBox::No ) != QMessageBox::Yes) { return; } } while (selectedItems().size() > 0) { delete selectedItems().at(0); // deleting all does not work because it may cause double deletion } // Save the bookmarks. One way would be to signal that the bookmarks have // changed emit a signal so that a number of changes may be saved at once. // Another way is to simply save the bookmarks after each change, which can // be inefficient. needToSaveBookmarks(); } /* Reimplementation from QAbstractItemView/QTreeWidget. Takes care of movable items. It's easier to use this than to start drag with mouse event handlers. The default implementation would drag items, but we don't call it. Instead we create a BibleTime mimedata object. It can be dragged and dropped to a text view or somewhere else. The internal drag is handled differently, it doesn't use the mimedata (see dropEvent()). */ void CBookmarkIndex::startDrag(Qt::DropActions) { QMimeData* mData = dragObject(); // create the data which can be used in other widgets QDrag* drag = new QDrag(this); drag->setMimeData(mData); drag->exec(); viewport()->update(); // because of the arrow } /* Returns true if more than one entry is supported by this action type. Returns false for actions which support only one entry, e.g. about module etc. */ bool CBookmarkIndex::isMultiAction( const BtBookmarkItemBase::MenuAction type ) const { switch (type) { case BtBookmarkItemBase::NewFolder: return false; case BtBookmarkItemBase::ChangeFolder: return false; case BtBookmarkItemBase::EditBookmark: return false; case BtBookmarkItemBase::SortFolderBookmarks: return false; case BtBookmarkItemBase::SortAllBookmarks: return false; case BtBookmarkItemBase::ImportBookmarks: return false; case BtBookmarkItemBase::ExportBookmarks: return false; case BtBookmarkItemBase::PrintBookmarks: return true; case BtBookmarkItemBase::DeleteEntries: return true; } return false; } /* Saves the bookmarks to the default bookmarks file. */ void CBookmarkIndex::saveBookmarks() { BtBookmarkLoader loader; loader.saveTreeFromRootItem(invisibleRootItem()); } void CBookmarkIndex::mouseMoveEvent(QMouseEvent* event) { // Restart the mag timer if we have moved to another item and shift was not pressed. QTreeWidgetItem* itemUnderPointer = itemAt(event->pos()); if (itemUnderPointer && (itemUnderPointer != m_previousEventItem) ) { if ( !(event->modifiers() & Qt::ShiftModifier)) { m_magTimer.start(); // see the ctor for the timer properties } } m_previousEventItem = itemUnderPointer; // Clear the extra item text unless we are on top of it if ( (itemUnderPointer != m_extraItem) && !m_extraItem->text(0).isNull()) { m_extraItem->setText(0, QString::null); } QTreeWidget::mouseMoveEvent(event); } void CBookmarkIndex::magTimeout() { QTreeWidgetItem* itemUnderPointer = 0; if (underMouse()) { itemUnderPointer = itemAt(mapFromGlobal(QCursor::pos())); } // if the mouse pointer have been over the same item since the timer was started if (itemUnderPointer && (m_previousEventItem == itemUnderPointer)) { BtBookmarkItem* bitem = dynamic_cast(itemUnderPointer); if (bitem) { // Update the mag if (bitem->module()) { (BibleTime::instance()->infoDisplay())->setInfo( InfoDisplay::CInfoDisplay::CrossReference, bitem->module()->name() + ":" + bitem->key() ); } else { (BibleTime::instance()->infoDisplay())->setInfo(InfoDisplay::CInfoDisplay::Text, tr("The work to which the bookmark points to is not installed.")); } } } } /* Creates a list of new items based on the current selection. If there are only bookmarks in the selection they are all included. If there is one folder it's included as a deep copy. Sets bookmarksOnly=false if it finds a folder. Sets targetIncluded=true if the target is in the list. Sets moreThanOneFolder=true if selection includes one folder and something more. If moreThanOneFolder or targetIncluded is detected the creation of list is stopped and the list is incomplete. */ QList CBookmarkIndex::addItemsToDropTree( QTreeWidgetItem* target, bool& bookmarksOnly, bool& targetIncluded, bool& moreThanOneFolder) { QList selectedList = selectedItems(); QList newList; foreach(QTreeWidgetItem* item, selectedList) { if ( BtBookmarkFolder* folder = dynamic_cast(item)) { bookmarksOnly = false; if (selectedList.count() > 1) { // only one item allowed if a folder is selected moreThanOneFolder = true; break; } if (folder->hasDescendant(target)) { // dropping to self or descendand not allowed targetIncluded = true; break; } } else { newList.append(new BtBookmarkItem( *(dynamic_cast(item)) )); } } if (!bookmarksOnly && selectedList.count() == 1) { BtBookmarkFolder* folder = dynamic_cast(selectedList.value(0)); BtBookmarkFolder* copy = folder->deepCopy(); newList.append(copy); } if (!bookmarksOnly && selectedList.count() > 1) { // wrong amount of items moreThanOneFolder = true; } return newList; } /// Bookmark saving code. To avoid many saves during a short period of time, /// bookmark modification is first noted. Then, after a wait (1.5s), if no more /// modifications are made, the bookmarks are saved. The timer is reset when a /// new modification is made. The timer bookmarkSaveTimer is set to be oneshot. void CBookmarkIndex::needToSaveBookmarks() { m_bookmarksModified = true; bookmarkSaveTimer.start(1500); // Only save after 1.5s. } void CBookmarkIndex::needToSaveBookmarks(QTreeWidgetItem* treeItem) { // Need to test whether the item that changed is not just a display item, // but actually a folder or bookmark. BtBookmarkItemBase* bookmark = dynamic_cast(treeItem); if (bookmark) { m_bookmarksModified = true; bookmarkSaveTimer.start(1500); // Only save after 1.5s. } } /// Considers saving bookmarks only if they have been modified. This procedure /// should be called by the qtimer bookmarkTimer. void CBookmarkIndex::considerSavingBookmarks() { if (m_bookmarksModified) { saveBookmarks(); m_bookmarksModified = false; } }