diff options
Diffstat (limited to 'src/frontend/bookmarks')
-rw-r--r-- | src/frontend/bookmarks/btbookmarkfolder.cpp | 140 | ||||
-rw-r--r-- | src/frontend/bookmarks/btbookmarkfolder.h | 50 | ||||
-rw-r--r-- | src/frontend/bookmarks/btbookmarkitem.cpp | 149 | ||||
-rw-r--r-- | src/frontend/bookmarks/btbookmarkitem.h | 76 | ||||
-rw-r--r-- | src/frontend/bookmarks/btbookmarkitembase.cpp | 21 | ||||
-rw-r--r-- | src/frontend/bookmarks/btbookmarkitembase.h | 62 | ||||
-rw-r--r-- | src/frontend/bookmarks/btbookmarkloader.cpp | 177 | ||||
-rw-r--r-- | src/frontend/bookmarks/btbookmarkloader.h | 46 | ||||
-rw-r--r-- | src/frontend/bookmarks/bteditbookmarkdialog.cpp | 78 | ||||
-rw-r--r-- | src/frontend/bookmarks/bteditbookmarkdialog.h | 62 | ||||
-rw-r--r-- | src/frontend/bookmarks/cbookmarkindex.cpp | 937 | ||||
-rw-r--r-- | src/frontend/bookmarks/cbookmarkindex.h | 214 |
12 files changed, 2012 insertions, 0 deletions
diff --git a/src/frontend/bookmarks/btbookmarkfolder.cpp b/src/frontend/bookmarks/btbookmarkfolder.cpp new file mode 100644 index 0000000..1cc2583 --- /dev/null +++ b/src/frontend/bookmarks/btbookmarkfolder.cpp @@ -0,0 +1,140 @@ +/********* +* +* 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/btbookmarkfolder.h" + +#include <QDebug> +#include <QFileDialog> +#include "frontend/bookmarks/btbookmarkitembase.h" +#include "frontend/bookmarks/btbookmarkitem.h" +#include "frontend/bookmarks/btbookmarkloader.h" +#include "util/cresmgr.h" +#include "util/directory.h" + + +BtBookmarkFolder::BtBookmarkFolder(const QString &name, QTreeWidgetItem *parent) + : BtBookmarkItemBase(parent) { + setText(0, name); + setFlags(Qt::ItemIsEditable | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | Qt::ItemIsEnabled); +} + +bool BtBookmarkFolder::enableAction(MenuAction action) { + if (action == ChangeFolder || action == NewFolder || action == DeleteEntries || action == ImportBookmarks ) + return true; + if (action == SortFolderBookmarks || action == ExportBookmarks || action == ImportBookmarks ) + return true; + if ((action == PrintBookmarks) && childCount()) + return true; + return false; +} + +void BtBookmarkFolder::exportBookmarks() { + QString filter = QObject::tr("BibleTime bookmark files") + QString(" (*.btb);;") + QObject::tr("All files") + QString(" (*.*)"); + QString fileName = QFileDialog::getSaveFileName(0, QObject::tr("Export Bookmarks"), "", filter); + + if (!fileName.isEmpty()) { + qDebug() << "exportBookmarks()"; + BtBookmarkLoader loader; + loader.saveTreeFromRootItem(this, fileName, false ); //false: don't overwrite without asking + }; + +} + +void BtBookmarkFolder::importBookmarks() { + QString filter = QObject::tr("BibleTime bookmark files") + QString(" (*.btb);;") + QObject::tr("All files") + QString(" (*.*)"); + QString fileName = QFileDialog::getOpenFileName(0, QObject::tr("Import bookmarks"), "", filter); + if (!fileName.isEmpty()) { + qDebug() << "import bookmarks"; + BtBookmarkLoader loader; + QList<QTreeWidgetItem*> itemList = loader.loadTree(fileName); + this->insertChildren(0, itemList); + }; +} + +QString BtBookmarkFolder::toolTip() const { + return QString::null; +} + +void BtBookmarkFolder::newSubFolder() { + if (dynamic_cast<BtBookmarkFolder*>(this)) { + BtBookmarkFolder* f = new BtBookmarkFolder(QObject::tr("New folder"), this); + + treeWidget()->setCurrentItem(f); + f->update(); + f->rename(); + } +} + +QList<QTreeWidgetItem*> BtBookmarkFolder::getChildList() const { + QList<QTreeWidgetItem*> list; + for (int i = 0; i < childCount(); i++) { + list.append(child(i)); + } + return list; +} + +void BtBookmarkFolder::rename() { + treeWidget()->editItem(this); +} + +void BtBookmarkFolder::update() { + namespace DU = util::directory; + + qDebug() << "BtBookmarkFolder::update()"; + BtBookmarkItemBase::update(); + if (isExpanded() && childCount()) + setIcon(0, DU::getIcon(CResMgr::mainIndex::openedFolder::icon)); + else + setIcon(0, DU::getIcon(CResMgr::mainIndex::closedFolder::icon)); +} + +bool BtBookmarkFolder::hasDescendant(QTreeWidgetItem* item) const { + qDebug() << "BtBookmarkFolder::hasDescendant, this:" << this << "possible descendant:" << item; + + if (this == item) { + qDebug() << "it's this, return true"; + return true; + } + if (getChildList().indexOf(item) > -1) { + qDebug() << "direct child, return true"; + return true; + } + foreach(QTreeWidgetItem* childItem, getChildList()) { + bool subresult = false; + BtBookmarkFolder* folder = 0; + if ( (folder = dynamic_cast<BtBookmarkFolder*>(childItem)) ) { + subresult = folder->hasDescendant(childItem); + } + + if (subresult == true) { + qDebug() << "descendand child, return true"; + return true; + } + } + qDebug() << "no child, return false"; + return false; +} + +BtBookmarkFolder* BtBookmarkFolder::deepCopy() { + qDebug() << "BtBookmarkFolder::deepCopy"; + BtBookmarkFolder* newFolder = new BtBookmarkFolder(this->text(0)); + foreach(QTreeWidgetItem* subitem, getChildList()) { + if (BtBookmarkItem* bmItem = dynamic_cast<BtBookmarkItem*>(subitem)) { + newFolder->addChild(new BtBookmarkItem(*bmItem)); + } + else { + if (BtBookmarkFolder* bmFolder = dynamic_cast<BtBookmarkFolder*>(subitem)) { + newFolder->addChild(bmFolder->deepCopy()); + } + } + } + newFolder->update(); + return newFolder; +} + diff --git a/src/frontend/bookmarks/btbookmarkfolder.h b/src/frontend/bookmarks/btbookmarkfolder.h new file mode 100644 index 0000000..90021f8 --- /dev/null +++ b/src/frontend/bookmarks/btbookmarkfolder.h @@ -0,0 +1,50 @@ +/********* +* +* 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. +* +**********/ + +#ifndef BTBOOKMARKFOLDER_H +#define BTBOOKMARKFOLDER_H + +#include "frontend/bookmarks/btbookmarkitembase.h" + + +#define CURRENT_SYNTAX_VERSION 1 + +class BtBookmarkFolder : public BtBookmarkItemBase { + public: + friend class BtBookmarkLoader; + BtBookmarkFolder(const QString &name, QTreeWidgetItem *parent = 0); + ~BtBookmarkFolder() {} + + /** See the base class. */ + virtual bool enableAction(const MenuAction action); + + /** User gives a file from which to load items into this folder. */ + virtual void exportBookmarks(); + /** User gives a file to which items from this folder are saved. */ + virtual void importBookmarks(); + + /** Creates a new folder under this. */ + void newSubFolder(); + + /** Returns a list of direct childs of this item. */ + QList<QTreeWidgetItem*> getChildList() const; + + /** Returns true if the given item is this or a direct or indirect subitem of this. */ + bool hasDescendant(QTreeWidgetItem* item) const; + + /** Creates a deep copy of this item. */ + BtBookmarkFolder* deepCopy(); + + void rename(); + void update(); + + QString toolTip() const; +}; + +#endif diff --git a/src/frontend/bookmarks/btbookmarkitem.cpp b/src/frontend/bookmarks/btbookmarkitem.cpp new file mode 100644 index 0000000..fff3d2c --- /dev/null +++ b/src/frontend/bookmarks/btbookmarkitem.cpp @@ -0,0 +1,149 @@ +/********* +* +* 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/btbookmarkitem.h" + +#include <QSharedPointer> +#include <QDebug> +#include "backend/config/cbtconfig.h" +#include "backend/drivers/cswordmoduleinfo.h" +#include "backend/keys/cswordversekey.h" +#include "btglobal.h" +#include "frontend/bookmarks/btbookmarkfolder.h" +#include "frontend/bookmarks/bteditbookmarkdialog.h" +#include "util/cresmgr.h" +#include "util/directory.h" + + +BtBookmarkItem::BtBookmarkItem(const CSwordModuleInfo *module, + const QString &key, + const QString &description, + const QString &title) + : m_description(description), + m_moduleName(module ? module->name() : QString::null), + m_title(title) +{ + if (((module && (module->type() == CSwordModuleInfo::Bible)) || (module->type() == CSwordModuleInfo::Commentary)) ) { + CSwordVerseKey vk(0); + vk.setKey(key); + vk.setLocale("en"); + m_key = vk.key(); //the m_key member is always the english key! + } + else { + m_key = key; + }; + + update(); +} + +BtBookmarkItem::BtBookmarkItem(QTreeWidgetItem* parent) + : BtBookmarkItemBase(parent) {} + +BtBookmarkItem::BtBookmarkItem(const BtBookmarkItem& other) + : BtBookmarkItemBase(0), + m_key(other.m_key), + m_description(other.m_description), + m_moduleName(other.m_moduleName), + m_title(other.m_title) +{ + update(); +} + +CSwordModuleInfo *BtBookmarkItem::module() const { + return CSwordBackend::instance()->findModuleByName(m_moduleName); +} + +QString BtBookmarkItem::key() const { + const QString englishKeyName = englishKey(); + if (!module()) { + return englishKeyName; + } + + QString returnKeyName = englishKeyName; + if ((module()->type() == CSwordModuleInfo::Bible) || (module()->type() == CSwordModuleInfo::Commentary)) { + CSwordVerseKey vk(0); + vk.setKey(englishKeyName); + vk.setLocale(CSwordBackend::instance()->booknameLanguage().toLatin1() ); + + returnKeyName = vk.key(); //the returned key is always in the currently set bookname language + } + + return returnKeyName; +} + +QString BtBookmarkItem::toolTip() const { + if (!module()) { + return QString::null; + } + + FilterOptions filterOptions = CBTConfig::getFilterOptionDefaults(); + filterOptions.footnotes = false; + filterOptions.scriptureReferences = false; + CSwordBackend::instance()->setFilterOptions(filterOptions); + + QString ret; + QSharedPointer<CSwordKey> k( CSwordKey::createInstance(module()) ); + k->setKey(key()); + + // const CLanguageMgr::Language* lang = module()->language(); + // CBTConfig::FontSettingsPair fontPair = CBTConfig::get(lang); + + Q_ASSERT(k.data()); + QString header = QString::fromLatin1("%1 (%2)") + .arg(key()) + .arg(module()->name()); + if (title() != header) { + ret = QString::fromLatin1("<b>%1</b><br>%2<hr>%3") + .arg(header) + .arg(title()) + .arg(description()) + ; + } + else { + ret = QString::fromLatin1("<b>%1</b><hr>%2") + .arg(header) + .arg(description()) + ; + } + + return ret; +} + +bool BtBookmarkItem::enableAction(MenuAction action) { + if (action == EditBookmark || (module() && (action == PrintBookmarks)) || action == DeleteEntries) + return true; + + return false; +} + +void BtBookmarkItem::rename() { + BtEditBookmarkDialog d(QString::fromLatin1("%1 (%2)").arg(key()).arg(module() ? module()->name() : QObject::tr("unknown")), + m_title, + m_description, treeWidget()); + + if (d.exec() == QDialog::Accepted) { + m_title = d.titleText(); + m_description = d.descriptionText(); + update(); + } +} + +void BtBookmarkItem::update() { + namespace DU = util::directory; + + qDebug() << "BtBookmarkItem::update"; + setIcon(0, DU::getIcon(CResMgr::mainIndex::bookmark::icon)); + + if (m_title.isEmpty()) { + m_title = QString::fromLatin1("%1 (%2)").arg(key()).arg(module() ? module()->name() : QObject::tr("unknown")); + } + setText(0,m_title); + setToolTip(0, toolTip()); +} + diff --git a/src/frontend/bookmarks/btbookmarkitem.h b/src/frontend/bookmarks/btbookmarkitem.h new file mode 100644 index 0000000..3166e61 --- /dev/null +++ b/src/frontend/bookmarks/btbookmarkitem.h @@ -0,0 +1,76 @@ +/********* +* +* 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. +* +**********/ + +#ifndef BTBOOKMARKITEM_H +#define BTBOOKMARKITEM_H + +#include "frontend/bookmarks/btbookmarkitembase.h" + +#include <QString> + + +class BtBookmarkFolder; +class CSwordModuleInfo; + +class BtBookmarkItem : public BtBookmarkItemBase { + public: + friend class BtBookmarkLoader; + + BtBookmarkItem(QTreeWidgetItem* parent); + + /** Creates a bookmark with module, key and description. */ + BtBookmarkItem(const CSwordModuleInfo *module, const QString &key, + const QString &description, const QString &title); + + /** Creates a copy. */ + BtBookmarkItem(const BtBookmarkItem& other); + + ~BtBookmarkItem() {} + + /** Returns the used module, 0 if there is no such module. */ + CSwordModuleInfo *module() const; + + /** Returns the used key. */ + QString key() const; + + /** Returns the used description. */ + inline const QString &description() const { + return m_description; + } + + /** Returns the title. */ + inline const QString &title() const { + return m_title; + } + + /** Returns a tooltip for this bookmark. */ + virtual QString toolTip() const; + + /** Returns whether the action is supported by this item. */ + virtual bool enableAction(MenuAction action); + + /** Changes this bookmark. */ + virtual void rename(); + + void update(); + + private: + /** Returns the english key.*/ + inline const QString &englishKey() const { + return m_key; + } + + private: + QString m_key; + QString m_description; + QString m_moduleName; + QString m_title; +}; + +#endif diff --git a/src/frontend/bookmarks/btbookmarkitembase.cpp b/src/frontend/bookmarks/btbookmarkitembase.cpp new file mode 100644 index 0000000..1a80f7d --- /dev/null +++ b/src/frontend/bookmarks/btbookmarkitembase.cpp @@ -0,0 +1,21 @@ +/********* +* +* 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/btbookmarkitembase.h" + + +BtBookmarkItemBase::BtBookmarkItemBase() { + // Intentionally empty +} + +BtBookmarkItemBase::BtBookmarkItemBase(QTreeWidgetItem *parent) + : QTreeWidgetItem(parent) +{ + // Intentionally empty +} diff --git a/src/frontend/bookmarks/btbookmarkitembase.h b/src/frontend/bookmarks/btbookmarkitembase.h new file mode 100644 index 0000000..8452473 --- /dev/null +++ b/src/frontend/bookmarks/btbookmarkitembase.h @@ -0,0 +1,62 @@ +/********* +* +* 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. +* +**********/ + +#ifndef BTBOOKMARKITEMBASE_H +#define BTBOOKMARKITEMBASE_H + +#include <QTreeWidgetItem> + +#include <QDropEvent> +#include <QMimeData> +#include <QString> + + +class CBookmarkIndex; + +class BtBookmarkItemBase : public QTreeWidgetItem { + public: + enum MenuAction { + NewFolder = 0, + ChangeFolder, + + EditBookmark, + SortFolderBookmarks, + SortAllBookmarks, + ImportBookmarks, + ExportBookmarks, + PrintBookmarks, + + DeleteEntries, + + ActionBegin = NewFolder, + ActionEnd = DeleteEntries + }; + + /** Where to drop/create item(s): above, below or inside an item.*/ + enum Location {Above, Below, Inside}; + + BtBookmarkItemBase(); + BtBookmarkItemBase(QTreeWidgetItem* parent); + virtual ~BtBookmarkItemBase() {} + + virtual QString toolTip() const = 0; + + /** Returns true if the given action should be enabled in the popup menu. */ + virtual bool enableAction( MenuAction action ) = 0; + + /** Rename the item. */ + virtual void rename() = 0; + + /** Update the item (icon etc.) after creating or changing it. */ + virtual void update() {} + +}; + +#endif + diff --git a/src/frontend/bookmarks/btbookmarkloader.cpp b/src/frontend/bookmarks/btbookmarkloader.cpp new file mode 100644 index 0000000..ed1dd29 --- /dev/null +++ b/src/frontend/bookmarks/btbookmarkloader.cpp @@ -0,0 +1,177 @@ +/********* +* +* 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/btbookmarkloader.h" + +#include <QDebug> +#include <QDomElement> +#include <QDomNode> +#include <QDomDocument> +#include <QFile> +#include <QIODevice> +#include <QTextCodec> +#include <QTextStream> +#include <QTreeWidgetItem> +#include "backend/drivers/cswordmoduleinfo.h" +#include "frontend/bookmarks/btbookmarkitem.h" +#include "frontend/bookmarks/btbookmarkfolder.h" +#include "util/tool.h" + + +#define CURRENT_SYNTAX_VERSION 1 + +QList<QTreeWidgetItem*> BtBookmarkLoader::loadTree(QString fileName) { + qDebug() << "BtBookmarkLoader::loadTree"; + QList<QTreeWidgetItem*> itemList; + + QDomDocument doc; + doc.setContent(loadXmlFromFile(fileName)); + + //bookmarkfolder::loadBookmarksFromXML() + + QDomElement document = doc.documentElement(); + if ( document.tagName() != "SwordBookmarks" ) { + qWarning("Not a BibleTime Bookmark XML file"); + return QList<QTreeWidgetItem*>(); + } + + QDomElement child = document.firstChild().toElement(); + + while ( !child.isNull() && child.parentNode() == document) { + qDebug() << "BtBookmarkLoader::loadTree while start"; + QTreeWidgetItem* i = handleXmlElement(child, 0); + itemList.append(i); + if (!child.nextSibling().isNull()) { + child = child.nextSibling().toElement(); + } + else { + child = QDomElement(); //null + } + + } + + return itemList; +} + +QTreeWidgetItem* BtBookmarkLoader::handleXmlElement(QDomElement& element, QTreeWidgetItem* parent) { + qDebug() << "BtBookmarkLoader::handleXmlElement"; + QTreeWidgetItem* newItem = 0; + if (element.tagName() == "Folder") { + qDebug() << "BtBookmarkLoader::handleXmlElement: found folder"; + BtBookmarkFolder* newFolder = new BtBookmarkFolder(QString::null, parent); + if (element.hasAttribute("caption")) { + newFolder->setText(0, element.attribute("caption")); + } + QDomNodeList childList = element.childNodes(); + for (unsigned int i = 0; i < childList.length(); i++) { + qDebug() << "BtBookmarkLoader::handleXmlElement: go through child list of folder"; + QDomElement newElement = childList.at(i).toElement(); + QTreeWidgetItem* newChildItem = handleXmlElement(newElement, newFolder); + newFolder->addChild(newChildItem); + } + newFolder->update(); + newItem = newFolder; + } + else if (element.tagName() == "Bookmark") { + qDebug() << "BtBookmarkLoader::handleXmlElement: found bookmark"; + BtBookmarkItem* newBookmarkItem = new BtBookmarkItem(parent); + if (element.hasAttribute("modulename")) { + //we use the name in all cases, even if the module isn't installed anymore + newBookmarkItem->m_moduleName = element.attribute("modulename"); + } + if (element.hasAttribute("key")) { + newBookmarkItem->m_key = element.attribute("key"); + } + if (element.hasAttribute("description")) { + newBookmarkItem->m_description = element.attribute("description"); + } + if (element.hasAttribute("title")) { + newBookmarkItem->m_title = element.attribute("title"); + } + newBookmarkItem->update(); + newItem = newBookmarkItem; + } + qDebug() << "BtBookmarkLoader::handleXmlElement: return new item"; + return newItem; +} + + +QString BtBookmarkLoader::loadXmlFromFile(QString fileName) { + namespace DU = util::directory; + + if (fileName.isNull()) { + fileName = DU::getUserBaseDir().absolutePath() + "/bookmarks.xml"; + } + QFile file(fileName); + if (!file.exists()) + return QString::null; + + QString xml; + if (file.open(QIODevice::ReadOnly)) { + QTextStream t; + t.setAutoDetectUnicode(false); + t.setCodec(QTextCodec::codecForName("UTF-8")); + t.setDevice(&file); + xml = t.readAll(); + file.close(); + } + return xml; +} + +void BtBookmarkLoader::saveTreeFromRootItem(QTreeWidgetItem* rootItem, QString fileName, bool forceOverwrite) { + namespace DU = util::directory; + + Q_ASSERT(rootItem); + if (fileName.isNull()) { + fileName = DU::getUserBaseDir().absolutePath() + "/bookmarks.xml"; + } + + QDomDocument doc("DOC"); + doc.appendChild( doc.createProcessingInstruction( "xml", "version=\"1.0\" encoding=\"UTF-8\"" ) ); + + QDomElement content = doc.createElement("SwordBookmarks"); + content.setAttribute("syntaxVersion", CURRENT_SYNTAX_VERSION); + doc.appendChild(content); + + //append the XML nodes of all child items + + for (int i = 0; i < rootItem->childCount(); i++) { + saveItem(rootItem->child(i), content); + } + util::tool::savePlainFile(fileName, doc.toString(), forceOverwrite, QTextCodec::codecForName("UTF-8")); + +} + +void BtBookmarkLoader::saveItem(QTreeWidgetItem* item, QDomElement& parentElement) { + BtBookmarkFolder* folderItem = 0; + BtBookmarkItem* bookmarkItem = 0; + + if ((folderItem = dynamic_cast<BtBookmarkFolder*>(item))) { + QDomElement elem = parentElement.ownerDocument().createElement("Folder"); + elem.setAttribute("caption", folderItem->text(0)); + + parentElement.appendChild(elem); + + for (int i = 0; i < folderItem->childCount(); i++) { + saveItem(folderItem->child(i), elem); + } + } + else if ((bookmarkItem = dynamic_cast<BtBookmarkItem*>(item))) { + QDomElement elem = parentElement.ownerDocument().createElement("Bookmark"); + + elem.setAttribute("key", bookmarkItem->englishKey()); + elem.setAttribute("description", bookmarkItem->description()); + elem.setAttribute("modulename", bookmarkItem->m_moduleName); + elem.setAttribute("moduledescription", bookmarkItem->module() ? bookmarkItem->module()->config(CSwordModuleInfo::Description) : QString::null); + if ( ! bookmarkItem->title().isEmpty()) { + elem.setAttribute("title", bookmarkItem->m_title); + } + parentElement.appendChild(elem); + } +} diff --git a/src/frontend/bookmarks/btbookmarkloader.h b/src/frontend/bookmarks/btbookmarkloader.h new file mode 100644 index 0000000..8b819ce --- /dev/null +++ b/src/frontend/bookmarks/btbookmarkloader.h @@ -0,0 +1,46 @@ +/********* +* +* 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. +* +**********/ + +#ifndef BTBOOKMARKLOADER_H +#define BTBOOKMARKLOADER_H + +#include "util/directory.h" + +#include <QDomElement> +#include <QList> +#include <QString> + + +class QTreeWidgetItem; + +/** +* Class for loading and saving bookmarks. +*/ +class BtBookmarkLoader { + public: + /** Loads a list of items (with subitem trees) from a named file + * or from the default bookmarks file. */ + QList<QTreeWidgetItem*> loadTree(QString fileName = QString::null); + + /** Takes one item and saves the tree which is under it to a named file + * or to the default bookmarks file, asking the user about overwriting if necessary. */ + void saveTreeFromRootItem(QTreeWidgetItem* rootItem, QString fileName = QString::null, bool forceOverwrite = true); + + private: + /** Create a new item from a document element. */ + QTreeWidgetItem* handleXmlElement(QDomElement& element, QTreeWidgetItem* parent); + + /** Writes one item to a document element. */ + void saveItem(QTreeWidgetItem* item, QDomElement& parentElement); + + /** Loads a bookmark XML document from a named file or from the default bookmarks file. */ + QString loadXmlFromFile(QString fileName = QString::null); +}; + +#endif diff --git a/src/frontend/bookmarks/bteditbookmarkdialog.cpp b/src/frontend/bookmarks/bteditbookmarkdialog.cpp new file mode 100644 index 0000000..614498f --- /dev/null +++ b/src/frontend/bookmarks/bteditbookmarkdialog.cpp @@ -0,0 +1,78 @@ +/********* +* +* 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 "bteditbookmarkdialog.h" + +#include <QDialogButtonBox> +#include <QVBoxLayout> +#include <QFormLayout> +#include <QLabel> +#include <QLineEdit> +#include <QTextEdit> +#include <QWidget> +#include "util/cresmgr.h" +#include "util/dialogutil.h" +#include "util/directory.h" + + +BtEditBookmarkDialog::BtEditBookmarkDialog(const QString &key, + const QString &title, + const QString &description, + QWidget *parent, + Qt::WindowFlags wflags) + : QDialog(parent, wflags) +{ + namespace DU = util::directory; + + QVBoxLayout *mainLayout = new QVBoxLayout(this); + + resize(400, 300); + setWindowIcon(DU::getIcon(CResMgr::mainIndex::bookmark::icon)); + + m_layout = new QFormLayout; + + m_keyLabel = new QLabel(this); + m_keyTextLabel = new QLabel(key, this); + m_layout->addRow(m_keyLabel, m_keyTextLabel); + + m_titleLabel = new QLabel(this); + m_titleEdit = new QLineEdit(title, this); + m_layout->addRow(m_titleLabel, m_titleEdit); + + m_descriptionLabel = new QLabel(this); + m_descriptionEdit = new QTextEdit(description, this); + m_descriptionEdit->setWordWrapMode(QTextOption::WordWrap); + m_layout->addRow(m_descriptionLabel, m_descriptionEdit); + + mainLayout->addLayout(m_layout); + + m_buttonBox = new QDialogButtonBox(QDialogButtonBox::Cancel + | QDialogButtonBox::NoButton + | QDialogButtonBox::Ok, + Qt::Horizontal, + this); + util::prepareDialogBox(m_buttonBox); + mainLayout->addWidget(m_buttonBox); + + QObject::connect(m_buttonBox, SIGNAL(accepted()), this, SLOT(accept())); + QObject::connect(m_buttonBox, SIGNAL(rejected()), this, SLOT(reject())); + + retranslateUi(); + + m_titleEdit->setFocus(); +} + +void BtEditBookmarkDialog::retranslateUi() { + setWindowTitle(tr("Edit Bookmark")); + m_keyLabel->setText(tr("Location:")); + m_titleLabel->setText(tr("Title:")); + m_descriptionLabel->setText(tr("Description:")); + + /// \todo Add tooltips and what's this texts etc. +} diff --git a/src/frontend/bookmarks/bteditbookmarkdialog.h b/src/frontend/bookmarks/bteditbookmarkdialog.h new file mode 100644 index 0000000..c3455e2 --- /dev/null +++ b/src/frontend/bookmarks/bteditbookmarkdialog.h @@ -0,0 +1,62 @@ +/********* +* +* 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. +* +**********/ + +#ifndef BTEDITBOOKMARKDIALOG_H +#define BTEDITBOOKMARKDIALOG_H + +#include <QDialog> +#include <QLineEdit> +#include <QTextEdit> + +class QDialogButtonBox; +class QFormLayout; +class QLabel; +class QWidget; + +/** + \brief A dialog box for editing bookmarks. +*/ +class BtEditBookmarkDialog : public QDialog { + Q_OBJECT + + public: /* Methods: */ + BtEditBookmarkDialog(const QString &key, + const QString &title, + const QString &description, + QWidget *parent = 0, + Qt::WindowFlags wflags = Qt::Dialog); + + /** + * Returns the description written in the description box. + */ + inline const QString descriptionText() { + return m_descriptionEdit->toPlainText(); + } + + /** + * Returns the title written in the title box. + */ + inline const QString titleText() { return m_titleEdit->text(); } + + protected: /* Methods: */ + void retranslateUi(); + + private: /* Fields: */ + QFormLayout *m_layout; + QLabel *m_keyLabel; + QLabel *m_keyTextLabel; + QLabel *m_titleLabel; + QLineEdit *m_titleEdit; + QLabel *m_descriptionLabel; + QTextEdit *m_descriptionEdit; + QDialogButtonBox *m_buttonBox; + +}; + +#endif diff --git a/src/frontend/bookmarks/cbookmarkindex.cpp b/src/frontend/bookmarks/cbookmarkindex.cpp new file mode 100644 index 0000000..b6adbfa --- /dev/null +++ b/src/frontend/bookmarks/cbookmarkindex.cpp @@ -0,0 +1,937 @@ +/********* +* +* 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 <QSharedPointer> +#include <QAction> +#include <QApplication> +#include <QCursor> +#include <QDebug> +#include <QDrag> +#include <QDragLeaveEvent> +#include <QDragMoveEvent> +#include <QDropEvent> +#include <QInputDialog> +#include <QList> +#include <QMenu> +#include <QMouseEvent> +#include <QPainter> +#include <QPaintEvent> +#include <QTimer> +#include <QTreeWidget> +#include <QTreeWidgetItem> +#include <QToolTip> +#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() { + //qDebug() << "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; + //qDebug() << "CBookmarkIndex::initView end"; +} + +/** 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() { + //qDebug() << "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) { + //qDebug() << "CBookmarkIndex::mouseReleaseEvent"; + m_mouseReleaseEventModifiers = event->modifiers(); + QTreeWidget::mouseReleaseEvent(event); +} + +/** Called when an item is clicked with mouse or activated with keyboard. */ +void CBookmarkIndex::slotExecuted( QTreeWidgetItem* i ) { + qDebug() << "CBookmarkIndex::slotExecuted"; + + //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<BtBookmarkItemBase*>(i); + if (!btItem) { + return; + } + + BtBookmarkFolder* folderItem = 0; + BtBookmarkItem* bookmarkItem = 0; + if ((folderItem = dynamic_cast<BtBookmarkFolder*>(btItem))) { + i->setExpanded( !i->isExpanded() ); + } + else if (( bookmarkItem = dynamic_cast<BtBookmarkItem*>(btItem) )) { //clicked on a bookmark + if (CSwordModuleInfo* mod = bookmarkItem->module()) { + QList<CSwordModuleInfo*> 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<BtBookmarkItemBase*>(widgetItem)) { + if (BtBookmarkItem* bookmark = dynamic_cast<BtBookmarkItem*>( 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 ) { + //qDebug() << "CBookmarkIndex::dragEnterEvent"; + setState(QAbstractItemView::DraggingState); + QTreeWidget::dragEnterEvent(event); + if (event->source() == this || event->mimeData()->hasFormat("BibleTime/Bookmark")) { + event->acceptProposedAction(); + } +} + + +void CBookmarkIndex::dragMoveEvent( QDragMoveEvent* event ) { + //qDebug() << "CBookmarkIndex::dragMoveEvent"; + + // 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* ) { + qDebug() << "CBookmarkIndex::dragLeaveEvent"; + 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<BtBookmarkFolder*>(item); + bool isBookmark = dynamic_cast<BtBookmarkItem*>(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 ) { + qDebug() << "CBookmarkIndex::dropEvent"; + + //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) { + qDebug() << "there was item"; + + QRect rect = visualItemRect(item); + bool isFolder = dynamic_cast<BtBookmarkFolder*>(item); + bool isBookmark = dynamic_cast<BtBookmarkItem*>(item); + + if (isFolder) { // item is a folder + qDebug() << "item was folder"; + if (event->pos().y() > rect.bottom() - (2* rect.height() / 3) ) { + parentItem = item; + } + else { + parentItem = item->parent(); + if (!parentItem) { + parentItem = invisibleRootItem(); + } + qDebug() << "item:" << item << "parent:" << parentItem; + indexUnderParent = parentItem->indexOfChild(item); // before the current folder + } + } + else { + if (isBookmark) { // item is a bookmark + qDebug() << "item was 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 + qDebug() << "there was no item"; + parentItem = invisibleRootItem(); + indexUnderParent = parentItem->childCount() - 1; + } + + + if ( event->source() == this ) { + qDebug() << "dropping internal drag"; + event->accept(); + + bool bookmarksOnly = true; + bool targetIncluded = false; + bool moreThanOneFolder = false; + + QList<QTreeWidgetItem*> 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) { + qDebug() << "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) { + qDebug() << "move"; + parentItem->insertChildren(indexUnderParent, newItems); + deleteEntries(false); + } + else { + QObject::disconnect(this, SIGNAL(itemCollapsed(QTreeWidgetItem*)), + this, SLOT(expandAutoCollapsedItem(QTreeWidgetItem*))); + return; // user canceled + } + } + } + else { + qDebug() << "the source was outside this"; + 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) { + //qDebug() << "CBookmarkIndex::createBookmarkFromDrop"; + //take the bookmark data from the mime source + const BTMimeData* mdata = dynamic_cast<const BTMimeData*>(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); + + qDebug() << "Saving in...CBookmarkIndex::createBookmarkFromDrop"; + needToSaveBookmarks(); + } +} + + +/** Load the tree from file */ +void CBookmarkIndex::initTree() { + qDebug() << "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) { + qDebug() << "CBookmarkIndex::slotItemEntered"; + 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) { + //qDebug() << "CBookmarkIndex::contextMenu"; + //setup menu entries depending on current selection + QTreeWidgetItem* i = itemAt(p); + QList<QTreeWidgetItem *> items = selectedItems(); + //The item which was clicked may not be selected + if (i && !items.contains(i) && i != m_extraItem) + items.append(i); + + if (items.count() == 0) { + //special handling for no selection + BtBookmarkItemBase::MenuAction actionType; + for (int index = BtBookmarkItemBase::ActionBegin; index <= BtBookmarkItemBase::ActionEnd; ++index) { + actionType = static_cast<BtBookmarkItemBase::MenuAction>(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<BtBookmarkItemBase*>(items.at(0)); + BtBookmarkItemBase::MenuAction actionType; + for (int index = BtBookmarkItemBase::ActionBegin; index <= BtBookmarkItemBase::ActionEnd; ++index) { + actionType = static_cast<BtBookmarkItemBase::MenuAction>(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<BtBookmarkItemBase::MenuAction>(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<BtBookmarkItemBase::MenuAction>(index); + bool enableAction = isMultiAction(actionType); + QListIterator<QTreeWidgetItem *> it(items); + while (it.hasNext()) { + BtBookmarkItemBase* i = dynamic_cast<BtBookmarkItemBase*>(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)); + //qDebug() << "CBookmarkIndex::contextMenu end"; +} + +/** Adds a new subfolder to the current item. */ +void CBookmarkIndex::createNewFolder() { + //qDebug() << "CBookmarkIndex::createNewFolder"; + QList<QTreeWidgetItem*> selected = selectedItems(); + if (selected.count() > 0) { + BtBookmarkFolder* i = dynamic_cast<BtBookmarkFolder*>(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<BtBookmarkFolder*>(currentItem()); + Q_ASSERT(i); + if (i) { + i->rename(); + } +} + +/** Edits the current bookmark. */ +void CBookmarkIndex::editBookmark() { + BtBookmarkItem* i = dynamic_cast<BtBookmarkItem*>(currentItem()); + Q_ASSERT(i); + + if (i) { + i->rename(); + } +} + +/** Sorts the current folder bookmarks. */ +void CBookmarkIndex::sortFolderBookmarks() { + BtBookmarkFolder* i = dynamic_cast<BtBookmarkFolder*>(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<BtBookmarkFolder*>(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<BtBookmarkFolder*>(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<QTreeWidgetItem*> items; + BtBookmarkFolder* bf = dynamic_cast<BtBookmarkFolder*>(currentItem()); + + if (bf) { + items = bf->getChildList(); + } + else { + items = selectedItems(); + } + + //create a tree of keytreeitems using the bookmark hierarchy. + QListIterator<QTreeWidgetItem*> it(items); + while (it.hasNext()) { + BtBookmarkItem* i = dynamic_cast<BtBookmarkItem*>(it.next()); + if (i) { + qDebug() << "printBookmarks: add to list" << i->key(); + tree.append( new Printing::CPrinter::KeyTreeItem( i->key(), i->module(), settings ) ); + } + } + + if (items.count() == 0) { + qWarning("Tried to print empty bookmark list."); + return; + } + QSharedPointer<Printing::CPrinter> 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<BtBookmarkItemBase*>(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) { + qDebug() << "CBookmarkIndex::startDrag"; + + 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() { + + qDebug() << "CBookmarkIndex::saveBookmarks()"; + BtBookmarkLoader loader; + loader.saveTreeFromRootItem(invisibleRootItem()); +} + +void CBookmarkIndex::mouseMoveEvent(QMouseEvent* event) { + //qDebug() << "CBookmarkIndex::mouseMoveEvent"; + + // 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) ) { + //qDebug() << "CBookmarkIndex::mouseMoveEvent, moved onto another item"; + 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() { + //qDebug() << "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<BtBookmarkItem*>(itemUnderPointer); + if (bitem) { + //qDebug() << "CBookmarkIndex::timerEvent: update the infodisplay"; + // 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<QTreeWidgetItem*> CBookmarkIndex::addItemsToDropTree( + QTreeWidgetItem* target, bool& bookmarksOnly, bool& targetIncluded, bool& moreThanOneFolder) { + qDebug() << "CBookmarkIndex::addItemsToDropTree"; + QList<QTreeWidgetItem*> selectedList = selectedItems(); + QList<QTreeWidgetItem*> newList; + + foreach(QTreeWidgetItem* item, selectedList) { + qDebug() << "go through all items:" << item; + if ( BtBookmarkFolder* folder = dynamic_cast<BtBookmarkFolder*>(item)) { + bookmarksOnly = false; + if (selectedList.count() > 1) { // only one item allowed if a folder is selected + qDebug() << "one folder and something else is selected"; + moreThanOneFolder = true; + break; + } + if (folder->hasDescendant(target)) { // dropping to self or descendand not allowed + qDebug() << "addItemsToDropTree: target is included"; + targetIncluded = true; + break; + } + } + else { + qDebug() << "append new QTreeWidget item (should be BtBookmarkItem?)"; + newList.append(new BtBookmarkItem( *(dynamic_cast<BtBookmarkItem*>(item)) )); + } + } + if (!bookmarksOnly && selectedList.count() == 1) { + qDebug() << "exactly one folder"; + BtBookmarkFolder* folder = dynamic_cast<BtBookmarkFolder*>(selectedList.value(0)); + BtBookmarkFolder* copy = folder->deepCopy(); + newList.append(copy); + } + if (!bookmarksOnly && selectedList.count() > 1) { + // wrong amount of items + qDebug() << "one folder and something else is selected 2"; + moreThanOneFolder = true; + } + qDebug() << "return the new list" << newList; + 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() { + qDebug() << "Got signal to save bookmarks!"; + 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<BtBookmarkItemBase*>(treeItem); + if (bookmark) { + qDebug() << "Got signal to save bookmarks!"; + 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() { + qDebug() << "Considering to save bookmarks!"; + if (m_bookmarksModified) { + saveBookmarks(); + m_bookmarksModified = false; + } +} + diff --git a/src/frontend/bookmarks/cbookmarkindex.h b/src/frontend/bookmarks/cbookmarkindex.h new file mode 100644 index 0000000..2875a5d --- /dev/null +++ b/src/frontend/bookmarks/cbookmarkindex.h @@ -0,0 +1,214 @@ +/********* +* +* 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. +* +**********/ + +#ifndef CBOOKMARKINDEX_H +#define CBOOKMARKINDEX_H + +#include "frontend/bookmarks/btbookmarkitembase.h" + +#include <QList> +#include <QTimer> +#include <QTreeWidget> +#include <QTreeWidgetItem> +#include <QToolTip> +#include "frontend/displaywindow/cdisplaywindow.h" + + +class BTMimeData; +class CSearchDialog; +class CSwordModuleInfo; +class QAction; +class QDragLeaveEvent; +class QDragMoveEvent; +class QDropEvent; +class QMenu; +class QMouseEvent; +class QPaintEvent; +class QWidget; + +/** +* The widget which manages all bookmarks. +* @author The BibleTime team +*/ +class CBookmarkIndex : public QTreeWidget { + Q_OBJECT + public: + CBookmarkIndex(QWidget *parent); + virtual ~CBookmarkIndex(); + + void initTree(); + + /** + * Saves the bookmarks to disk + */ + void saveBookmarks(); + + signals: + /** + * Is emitted when a module should be opened, + */ + void createReadDisplayWindow( QList<CSwordModuleInfo*>, const QString& ); + + public slots: + + /** + * Indicates a need to save the bookmarks. + * This is needed to provide a way for a bookmarkitem stored in the + * treeWidget to inform us that it has been modified, namely its + * description text. It only sets a dirty-bit so we don't execute many + * consecutive saves. + */ + void needToSaveBookmarks(); + void needToSaveBookmarks(QTreeWidgetItem* treeItem); + + + protected: // Protected methods + + /** A hack to get the modifiers. */ + virtual void mouseReleaseEvent(QMouseEvent* event); + + /** Needed to paint an drag pointer arrow. */ + virtual void paintEvent(QPaintEvent* event); + + /** Initialize the SIGNAL<->SLOT connections. */ + void initConnections(); + + /** Returns the drag object for the current selection. */ + virtual QMimeData* dragObject(); + + /** + * D'n'd methods are reimplementations from QTreeWidget or its ancestors. + * In these we handle creating, moving and copying bookmarks with d'n'd. + */ + virtual void dragEnterEvent( QDragEnterEvent* event ); + virtual void dragMoveEvent( QDragMoveEvent* event ); + virtual void dropEvent( QDropEvent* event ); + virtual void dragLeaveEvent( QDragLeaveEvent* event ); + + /** Returns the correct action object for the given type of action. */ + QAction* action( BtBookmarkItemBase::MenuAction type ) const; + + /** Reimplementation from QAbstractItemView. Takes care of movable items. */ + virtual void startDrag(Qt::DropActions supportedActions); + + + /** Handle mouse moving (mag updates) */ + virtual void mouseMoveEvent(QMouseEvent* event); + + + protected slots: + + /** Prevents annoying folder collapsing while dropping. */ + void expandAutoCollapsedItem(QTreeWidgetItem* i) { + expandItem(i); + } + + /** Is called when an item was clicked or activated. */ + void slotExecuted( QTreeWidgetItem* ); + + /** Shows the context menu at the given position. */ + void contextMenu(const QPoint&); + + /** Adds a new subfolder to the current item. */ + void createNewFolder(); + + /** Opens a dialog to change the current folder. */ + void changeFolder(); + + /** Exports the bookmarks from the selected folder. */ + void exportBookmarks(); + + /** Changes the current bookmark. */ + void editBookmark(); + + /** Sorts the current folder bookmarks. */ + void sortFolderBookmarks(); + + /** Sorts all bookmarks. */ + void sortAllBookmarks(); + + /** Helps with the extra item. */ + void slotItemEntered(QTreeWidgetItem*, int); + + /** Import bookmarks from a file and add them to the selected folder. */ + void importBookmarks(); + + /** Deletes the selected entries. */ + void deleteEntries(bool confirm = true); + + /** Prints the selected bookmarks. */ + void printBookmarks(); + + /** Slot for the mag update timer. */ + void magTimeout(); + + private: + + /** Initializes the view. */ + void initView(); + + /** Convenience function for creating a new action. */ + QAction* newQAction(const QString& text, const QString& pix, int shortcut, const QObject* receiver, const char* slot, QObject* parent); + + /** + * Returns true if more than one entry is supported by this action type. + * Returns false for actions which support only one entry. + */ + bool isMultiAction( const BtBookmarkItemBase::MenuAction type ) const; + + /** A helper function for d'n'd which creates a new bookmark item when drop happens. */ + void createBookmarkFromDrop(QDropEvent* event, QTreeWidgetItem* parentItem, int indexInParent); + + /** + * Returns a list of new items created from the selection. + * Sets flags which indicate whether the selection was legal for dropping. + */ + QList<QTreeWidgetItem*> addItemsToDropTree(QTreeWidgetItem* target, bool& bookmarksOnly, bool& targetIncluded, bool& moreThanOneFolder); + + struct Actions { + QAction* newFolder; + QAction* changeFolder; + + QAction* editBookmark; + QAction* sortFolderBookmarks; + QAction* sortAllBookmarks; + QAction* importBookmarks; + QAction* exportBookmarks; + QAction* printBookmarks; + + QAction* deleteEntries; + } + m_actions; + + QMenu* m_popup; + QTimer m_magTimer; + int m_mouseReleaseEventModifiers; + QTreeWidgetItem* m_previousEventItem; + QPoint m_dragMovementPosition; + QPoint m_dragStartPosition; + QTreeWidgetItem* m_extraItem; + + // The following is for managing saving bookmarks. It uses a QTimer to + // determine whether the bookmarks should be saved. This may seem like + // a hassle, but it is to prevent many saves from being executed at a + // time. + + /** Flag indicating that bookmarks have been modified. */ + bool m_bookmarksModified; + QTimer bookmarkSaveTimer; + + private slots: + /** + * Saves the bookmarks. + * It checks m_bookmarksModified and resets it at the end. It should be + * connected to a timer that periodically calls this. */ + void considerSavingBookmarks(); +}; + +#endif |