diff options
Diffstat (limited to 'src')
49 files changed, 2393 insertions, 210 deletions
diff --git a/src/backend/bookshelfmodel/btbookshelfmodel.cpp b/src/backend/bookshelfmodel/btbookshelfmodel.cpp new file mode 100644 index 0000000..6764a88 --- /dev/null +++ b/src/backend/bookshelfmodel/btbookshelfmodel.cpp @@ -0,0 +1,184 @@ +/********* +* +* In the name of the Father, and of the Son, and of the Holy Spirit. +* +* This file is part of BibleTime's source code, http://www.bibletime.info/. +* +* Copyright 1999-2009 by the BibleTime developers. +* The BibleTime source code is licensed under the GNU General Public License +* version 2.0. +* +**********/ + +#include "backend/bookshelfmodel/btbookshelfmodel.h" + +#include <QSet> +#include "util/cresmgr.h" +#include "util/directoryutil.h" + +BtBookshelfModel::BtBookshelfModel(QObject *parent) + : QAbstractListModel(parent) +{ + // Intentionally empty +} + +BtBookshelfModel::~BtBookshelfModel() { + // Intentionally empty +} + +int BtBookshelfModel::rowCount(const QModelIndex &parent) const { + return m_data.size(); +} + +QVariant BtBookshelfModel::data(const QModelIndex &index, int role) const { + if (!index.isValid() || index.column() != 0 || index.parent().isValid()) { + return QVariant(); + } + int row(index.row()); + if (row >= m_data.size()) return QVariant(); + + switch (role) { + case ModuleNameRole: // Qt::DisplayRole + return m_data.at(row)->name(); + case ModuleIconRole: // Qt::DecorationRole + return categoryIcon(m_data.at(row)->category()); + case ModulePointerRole: + return qVariantFromValue((void*) m_data.at(row)); + default: + return QVariant(); + } +} + +QVariant BtBookshelfModel::headerData(int section, Qt::Orientation orientation, + int role) const +{ + if (role == Qt::DisplayRole && orientation == Qt::Horizontal && + section == 0) + { + return tr("Module"); + } + + return QVariant(); +} + +QIcon BtBookshelfModel::categoryIcon(const CSwordModuleInfo::Category &category) +{ + typedef util::filesystem::DirectoryUtil DU; + + switch (category) { + case CSwordModuleInfo::Bibles: + return DU::getIcon(CResMgr::categories::bibles::icon); + case CSwordModuleInfo::Commentaries: + return DU::getIcon(CResMgr::categories::commentaries::icon); + case CSwordModuleInfo::Books: + return DU::getIcon(CResMgr::categories::books::icon); + case CSwordModuleInfo::Cult: + return DU::getIcon(CResMgr::categories::cults::icon); + case CSwordModuleInfo::Images: + return DU::getIcon(CResMgr::categories::images::icon); + case CSwordModuleInfo::DailyDevotional: + return DU::getIcon(CResMgr::categories::dailydevotional::icon); + case CSwordModuleInfo::Lexicons: + return DU::getIcon(CResMgr::categories::lexicons::icon); + case CSwordModuleInfo::Glossary: + return DU::getIcon(CResMgr::categories::glossary::icon); + case CSwordModuleInfo::UnknownCategory: + default: + return QIcon(); + } +} + +QString BtBookshelfModel::categoryName( + const CSwordModuleInfo::Category &category) +{ + switch (category) { + case CSwordModuleInfo::Bibles: + return tr("Bibles"); + case CSwordModuleInfo::Commentaries: + return tr("Commentaries"); + case CSwordModuleInfo::Books: + return tr("Books"); + case CSwordModuleInfo::Cult: + return tr("Cults/Unorthodox"); + case CSwordModuleInfo::Images: + return tr("Maps and Images"); + case CSwordModuleInfo::DailyDevotional: + return tr("Daily Devotionals"); + case CSwordModuleInfo::Lexicons: + return tr("Lexicons and Dictionaries"); + case CSwordModuleInfo::Glossary: + return tr("Glossaries"); + default: + return tr("Unknown"); + } +} + +QString BtBookshelfModel::languageName( + const CLanguageMgr::Language *language) +{ + return language->translatedName(); +} + +void BtBookshelfModel::clear() { + beginRemoveRows(QModelIndex(), 0, m_data.size() - 1); + m_data.clear(); + endRemoveRows(); +} + +void BtBookshelfModel::addModule(CSwordModuleInfo * const module) { + Q_ASSERT(module != 0); + + if (m_data.contains(module)) return; + + const int index(m_data.size()); + beginInsertRows(QModelIndex(), index, index); + m_data.append(module); + endInsertRows(); +} + +void BtBookshelfModel::addModules(const QList<CSwordModuleInfo *> &modules) { + addModules(modules.toSet()); +} + +void BtBookshelfModel::addModules(const QSet<CSwordModuleInfo *> &modules) { + QList<CSwordModuleInfo *> newModules; + Q_FOREACH(CSwordModuleInfo *module, modules) { + if (!m_data.contains(module)) { + newModules.append(module); + } + } + + if (newModules.isEmpty()) return; + + beginInsertRows(QModelIndex(), m_data.size(), + m_data.size() + newModules.size() - 1); + m_data.append(newModules); + endInsertRows(); +} + +void BtBookshelfModel::removeModule(CSwordModuleInfo * const module) { + const int index(m_data.indexOf(module)); + if (index == -1) return; + + beginRemoveRows(QModelIndex(), index, index); + m_data.removeAt(index); + endRemoveRows(); +} + +void BtBookshelfModel::removeModules(const QList<CSwordModuleInfo *> &modules) { + removeModules(modules.toSet()); +} + +void BtBookshelfModel::removeModules(const QSet<CSwordModuleInfo *> &modules) { + // This is inefficient, since signals are emitted for each removed module: + Q_FOREACH(CSwordModuleInfo *module, modules) { + removeModule(module); + } +} + +CSwordModuleInfo* BtBookshelfModel::getModule(const QString &name) const { + Q_FOREACH(CSwordModuleInfo *module, m_data) { + if (module->name() == name) return module; + } + return 0; +} diff --git a/src/backend/bookshelfmodel/btbookshelfmodel.h b/src/backend/bookshelfmodel/btbookshelfmodel.h new file mode 100644 index 0000000..c268d2c --- /dev/null +++ b/src/backend/bookshelfmodel/btbookshelfmodel.h @@ -0,0 +1,67 @@ +/********* +* +* In the name of the Father, and of the Son, and of the Holy Spirit. +* +* This file is part of BibleTime's source code, http://www.bibletime.info/. +* +* Copyright 1999-2009 by the BibleTime developers. +* The BibleTime source code is licensed under the GNU General Public License +* version 2.0. +* +**********/ + +#ifndef BTBOOKSHELFMODEL_H +#define BTBOOKSHELFMODEL_H + +#include <QAbstractListModel> + +#include "backend/drivers/cswordmoduleinfo.h" + +class BtBookshelfModel: public QAbstractListModel { + Q_OBJECT + public: + enum ModuleRole { + ModuleNameRole = Qt::DisplayRole, + ModuleIconRole = Qt::DecorationRole, + ModulePointerRole = Qt::UserRole, + ModuleCategoryRole = Qt::UserRole + 1, + ModuleLanguageRole = Qt::UserRole + 2, + UserRole = Qt::UserRole + 100 + }; + + BtBookshelfModel(QObject *parent = 0); + virtual ~BtBookshelfModel(); + + virtual int rowCount(const QModelIndex &parent) const; + virtual QVariant data(const QModelIndex &index, int role) const; + virtual QVariant headerData(int section, Qt::Orientation orientation, + int role = Qt::DisplayRole) const; + + inline CSwordModuleInfo *module(const QModelIndex &index) const { + return (CSwordModuleInfo *) + data(index, BtBookshelfModel::ModulePointerRole) + .value<void *>(); + } + + static QIcon categoryIcon(const CSwordModuleInfo::Category &category); + static QString categoryName(const CSwordModuleInfo::Category &category); + static QString languageName(const CLanguageMgr::Language *language); + + void clear(); + void addModule(CSwordModuleInfo * const module); + void addModules(const QSet<CSwordModuleInfo *> &modules); + void addModules(const QList<CSwordModuleInfo *> &modules); + void removeModule(CSwordModuleInfo * const module); + void removeModules(const QSet<CSwordModuleInfo *> &modules); + void removeModules(const QList<CSwordModuleInfo *> &modules); + + CSwordModuleInfo* getModule(const QString &name) const; + inline const QList<CSwordModuleInfo *> &modules() const { + return m_data; + } + + protected: + QList<CSwordModuleInfo *> m_data; +}; + +#endif // BTBOOKSHELFMODEL_H diff --git a/src/backend/bookshelfmodel/btbookshelftreemodel.cpp b/src/backend/bookshelfmodel/btbookshelftreemodel.cpp new file mode 100644 index 0000000..47526e9 --- /dev/null +++ b/src/backend/bookshelfmodel/btbookshelftreemodel.cpp @@ -0,0 +1,474 @@ +/********* +* +* In the name of the Father, and of the Son, and of the Holy Spirit. +* +* This file is part of BibleTime's source code, http://www.bibletime.info/. +* +* Copyright 1999-2009 by the BibleTime developers. +* The BibleTime source code is licensed under the GNU General Public License +* version 2.0. +* +**********/ + +#include "backend/bookshelfmodel/btbookshelftreemodel.h" + +#include <QQueue> +#include <QSet> +#include "backend/bookshelfmodel/categoryitem.h" +#include "backend/bookshelfmodel/distributionitem.h" +#include "backend/bookshelfmodel/languageitem.h" +#include "backend/bookshelfmodel/moduleitem.h" + +using namespace BookshelfModel; + +BtBookshelfTreeModel::BtBookshelfTreeModel(QObject *parent) + : QAbstractItemModel(parent), m_sourceModel(0), m_rootItem(new RootItem), + m_checkable(false), m_defaultChecked(false) +{ + m_groupingOrder.push_back(GROUP_CATEGORY); + m_groupingOrder.push_back(GROUP_LANGUAGE); +} + +BtBookshelfTreeModel::~BtBookshelfTreeModel() { + delete m_rootItem; +} + +int BtBookshelfTreeModel::rowCount(const QModelIndex &parent) const { + return getItem(parent)->children().size(); +} + +int BtBookshelfTreeModel::columnCount(const QModelIndex &parent) const { + return 1; +} + +bool BtBookshelfTreeModel::hasChildren(const QModelIndex &parent) const { + return !getItem(parent)->children().isEmpty(); +} + +QModelIndex BtBookshelfTreeModel::index(int row, int column, + const QModelIndex &parent) const +{ + if (!hasIndex(row, column, parent)) return QModelIndex(); + + Item *parentItem(getItem(parent)); + Item *childItem(parentItem->childAt(row)); + if (childItem != 0) { + return createIndex(row, column, childItem); + } else { + return QModelIndex(); + } +} + +QModelIndex BtBookshelfTreeModel::parent(const QModelIndex &index) const { + if (!index.isValid()) return QModelIndex(); + + Item *childItem(static_cast<Item*>(index.internalPointer())); + Q_ASSERT(childItem != 0); + Item *parentItem(childItem->parent()); + Q_ASSERT(parentItem != 0); + + if (parentItem == m_rootItem) { + return QModelIndex(); + } + return createIndex(parentItem->childIndex(), 0, parentItem); +} + +QVariant BtBookshelfTreeModel::data(const QModelIndex &index, int role) const { + typedef util::filesystem::DirectoryUtil DU; + + if (!index.isValid() || index.column() != 0) { + return QVariant(); + } + + Item *i(static_cast<Item*>(index.internalPointer())); + Q_ASSERT(i != 0); + switch (role) { + case Qt::DisplayRole: + return i->name(); + case Qt::CheckStateRole: + if (!m_checkable) break; + case BtBookshelfTreeModel::CheckStateRole: + return i->checkState(); + case Qt::DecorationRole: + return i->icon(); + case BtBookshelfModel::ModulePointerRole: + if (i->type() == Item::ITEM_MODULE) { + ModuleItem *mi(dynamic_cast<ModuleItem *>(i)); + if (mi != 0) { + return qVariantFromValue((void*) mi->moduleInfo()); + } + } + return 0; + default: + break; + } + return QVariant(); +} + +bool BtBookshelfTreeModel::setData(const QModelIndex &itemIndex, + const QVariant &value, + int role) +{ + typedef QPair<Item *, QModelIndex> IP; + + if (role == Qt::CheckStateRole) { + bool ok; + Qt::CheckState newState((Qt::CheckState) value.toInt(&ok)); + if (ok && newState != Qt::PartiallyChecked) { + Item *i(static_cast<Item*>(itemIndex.internalPointer())); + Q_ASSERT(i != 0); + // Recursively (un)check all children: + QList<IP> q; + q.append(IP(i, itemIndex)); + while (!q.isEmpty()) { + const IP p(q.takeFirst()); + Item *item(p.first); + item->setCheckState(newState); + emit dataChanged(p.second, p.second); + const QList<Item*> &children(item->children()); + for (int i(0); i < children.size(); i++) { + q.append(IP(children.at(i), index(i, 0, p.second))); + } + } + + // Change check state of the item itself + i->setCheckState(newState); + emit dataChanged(itemIndex, itemIndex); + + // Recursively change parent check states. + resetParentCheckStates(itemIndex.parent()); + + return true; + } // if (ok && newState != Qt::PartiallyChecked) + } // if (role == Qt::CheckStateRole) + return false; +} + +Qt::ItemFlags BtBookshelfTreeModel::flags(const QModelIndex &index) const { + if (!index.isValid()) return 0; + + Qt::ItemFlags f(Qt::ItemIsEnabled | Qt::ItemIsSelectable); + + if (m_checkable) { + f |= Qt::ItemIsUserCheckable; + + Item *i(static_cast<Item*>(index.internalPointer())); + Q_ASSERT(i != 0); + + if (i->type() != Item::ITEM_MODULE) { + f |= Qt::ItemIsTristate; + } + } + + return f; +} + +QVariant BtBookshelfTreeModel::headerData(int section, + Qt::Orientation orientation, + int role) const +{ + if (orientation == Qt::Horizontal) { + return m_sourceModel->headerData(section, orientation, role); + } + return QVariant(); +} + +void BtBookshelfTreeModel::setSourceModel(QAbstractListModel *sourceModel) { + if (m_sourceModel == sourceModel) return; + + if (m_sourceModel != 0) { + disconnect(this, SLOT(moduleInserted(QModelIndex,int,int))); + disconnect(this, SLOT(moduleRemoved(QModelIndex,int,int))); + disconnect(this, SLOT(moduleDataChanged(QModelIndex,QModelIndex))); + beginRemoveRows(QModelIndex(), 0, m_rootItem->children().size() - 1); + delete m_rootItem; + m_modules.clear(); + m_rootItem = new RootItem; + endRemoveRows(); + } + + m_sourceModel = sourceModel; + + if (sourceModel != 0) { + connect(sourceModel, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)), + this, SLOT(moduleRemoved(QModelIndex,int,int))); + connect(sourceModel, SIGNAL(rowsInserted(QModelIndex,int,int)), + this, SLOT(moduleInserted(QModelIndex,int,int))); + connect(sourceModel, SIGNAL(dataChanged(QModelIndex, QModelIndex)), + this, SLOT(moduleDataChanged(QModelIndex, QModelIndex))); + + BtBookshelfModel *m(dynamic_cast<BtBookshelfModel*>(sourceModel)); + if (m != 0) { + Q_FOREACH(CSwordModuleInfo *module, m->modules()) { + addModule(module, m_defaultChecked); + } + } else { + for (int i(0); i < sourceModel->rowCount(); i++) { + CSwordModuleInfo *module( + static_cast<CSwordModuleInfo *>( + sourceModel->data( + sourceModel->index(i), + BtBookshelfModel::ModulePointerRole + ).value<void*>() + ) + ); + Q_ASSERT(module != 0); + addModule( + module, + m_defaultChecked + ); + } + } + } +} + +void BtBookshelfTreeModel::setGroupingOrder(const Grouping &groupingOrder) { + if (m_groupingOrder == groupingOrder) return; + m_groupingOrder = groupingOrder; + + if (m_sourceModel != 0) { + QSet<CSwordModuleInfo*> checked(checkedModules().toSet()); + beginRemoveRows(QModelIndex(), 0, m_rootItem->children().size() - 1); + delete m_rootItem; + m_modules.clear(); + m_rootItem = new RootItem; + endRemoveRows(); + + BtBookshelfModel *m(dynamic_cast<BtBookshelfModel*>(m_sourceModel)); + if (m != 0) { + Q_FOREACH(CSwordModuleInfo *module, m->modules()) { + addModule(module, checked.contains(module)); + } + } else { + for (int i(0); i < m_sourceModel->rowCount(); i++) { + CSwordModuleInfo *module( + static_cast<CSwordModuleInfo *>( + m_sourceModel->data( + m_sourceModel->index(i), + BtBookshelfModel::ModulePointerRole + ).value<void*>() + ) + ); + Q_ASSERT(module != 0); + addModule(module, checked.contains(module)); + } + } + } +} + +void BtBookshelfTreeModel::setCheckable(bool checkable) { + if (m_checkable == checkable) return; + m_checkable = checkable; + if (m_sourceModel != 0) { + QModelIndexList queue; + queue.append(QModelIndex()); + do { + QModelIndex parent(queue.takeFirst()); + int numChildren(rowCount(parent)); + emit dataChanged(index(0, 0, parent), + index(numChildren - 1, 0, parent)); + for (int i(0); i < numChildren; i++) { + QModelIndex childIndex(index(i, 0, parent)); + if (rowCount(childIndex) > 0) { + queue.append(childIndex); + } + } + } while (!queue.isEmpty()); + } +} + +QList<CSwordModuleInfo*> BtBookshelfTreeModel::checkedModules() const { + typedef ModuleItemMap::const_iterator MMCI; + + QList<CSwordModuleInfo*> modules; + for (MMCI it(m_modules.constBegin()); it != m_modules.constEnd(); it++) { + if (it.value()->checkState() == Qt::Checked) { + modules.append(it.key()); + } + } + return modules; +} + +void BtBookshelfTreeModel::addModule(CSwordModuleInfo *module, bool checked) { + if (m_modules.contains(module)) return; + Grouping g(m_groupingOrder); + addModule(module, QModelIndex(), g, checked); + + /** + \bug Calling reset() shouldn't be necessary here, but omitting it will + will break things like switching to a grouped layout or installing + new modules. As a side effect, all attached views will also reset + themselves. + */ + reset(); +} + +void BtBookshelfTreeModel::addModule(CSwordModuleInfo *module, + QModelIndex parentIndex, + Grouping &intermediateGrouping, + bool checked) +{ + Q_ASSERT(module != 0); + + if (!intermediateGrouping.empty()) { + QModelIndex newIndex; + switch (intermediateGrouping.front()) { + case GROUP_DISTRIBUTION: + newIndex = getGroup<DistributionItem>(module, parentIndex); + break; + case GROUP_CATEGORY: + newIndex = getGroup<CategoryItem>(module, parentIndex); + break; + case GROUP_LANGUAGE: + newIndex = getGroup<LanguageItem>(module, parentIndex); + break; + } + intermediateGrouping.pop_front(); + addModule(module, newIndex, intermediateGrouping, checked); + } else { + Item *parentItem(getItem(parentIndex)); + ModuleItem *newItem(new ModuleItem(module)); + newItem->setCheckState(checked ? Qt::Checked : Qt::Unchecked); + const int newIndex(parentItem->indexFor(newItem)); + beginInsertRows(parentIndex, newIndex, newIndex); + parentItem->insertChild(newIndex, newItem); + m_modules.insert(module, newItem); + endInsertRows(); + resetParentCheckStates(parentIndex); + } +} + +void BtBookshelfTreeModel::removeModule(CSwordModuleInfo *module) { + Item *i(m_modules.value(module, 0)); + if (i == 0) return; + + // Set i to be the lowest item (including empty groups) to remove: + Q_ASSERT(i->parent() != 0); + while (i->parent() != m_rootItem && i->parent()->children().size() <= 1) { + i = i->parent(); + } + Q_ASSERT(i != 0); + + // Calculate index of parent item: + QModelIndex parentIndex; + { + QList<int> indexes; + for (Item *j(i->parent()); j != m_rootItem; j = j->parent()) { + Q_ASSERT(j != 0); + indexes.push_back(j->childIndex()); + } + while (!indexes.isEmpty()) { + parentIndex = index(indexes.takeLast(), 0, parentIndex); + } + } + + // Remove item: + int index(i->childIndex()); + beginRemoveRows(parentIndex, index, index); + i->parent()->deleteChildAt(index); + m_modules.remove(module); + endRemoveRows(); + resetParentCheckStates(parentIndex); +} + +Item *BtBookshelfTreeModel::getItem(const QModelIndex &index) const { + if (index.isValid()) { + Item *item(static_cast<Item*>(index.internalPointer())); + Q_ASSERT(item != 0); + return item; + } else { + return m_rootItem; + } +} + +void BtBookshelfTreeModel::resetParentCheckStates(QModelIndex parentIndex) { + for ( ; parentIndex.isValid(); parentIndex = parentIndex.parent()) { + Item *parentItem(static_cast<Item*>(parentIndex.internalPointer())); + Q_ASSERT(parentItem != 0); + + Qt::CheckState oldState(parentItem->checkState()); + bool haveCheckedChildren(false); + bool haveUncheckedChildren(false); + for (int i(0); i < parentItem->children().size(); i++) { + Qt::CheckState state(parentItem->childAt(i)->checkState()); + if (state == Qt::PartiallyChecked) { + haveCheckedChildren = true; + haveUncheckedChildren = true; + break; + } else if (state == Qt::Checked) { + haveCheckedChildren = true; + if (haveUncheckedChildren) break; + } else if (state == Qt::Unchecked) { + haveUncheckedChildren = true; + if (haveCheckedChildren) break; + } + } + + Qt::CheckState newState; + if (haveCheckedChildren) { + if (haveUncheckedChildren) { + newState = Qt::PartiallyChecked; + } else { + newState = Qt::Checked; + } + } else { + newState = Qt::Unchecked; + } + if (newState == oldState) break; + + parentItem->setCheckState(newState); + emit dataChanged(parentIndex, parentIndex); + } // for ( ; parentIndex.isValid(); parentIndex = parentIndex.parent()) +} + +void BtBookshelfTreeModel::moduleDataChanged(const QModelIndex &topLeft, + const QModelIndex &bottomRight) +{ + typedef BtBookshelfModel BM; + static const BM::ModuleRole PR(BM::ModulePointerRole); + + Q_ASSERT(!topLeft.parent().isValid()); + Q_ASSERT(!bottomRight.parent().isValid()); + Q_ASSERT(topLeft.column() == 0 && bottomRight.column() == 0); + + for (int i(topLeft.row()); i <= bottomRight.row(); i++) { + QModelIndex moduleIndex(m_sourceModel->index(i, 0, topLeft.parent())); + QVariant data(m_sourceModel->data(moduleIndex, PR)); + CSwordModuleInfo *module((CSwordModuleInfo *) (data.value<void*>())); + + /// \todo There might be a better way to do this. + bool checked(m_modules.value(module)->checkState() == Qt::Checked); + removeModule(module); + addModule(module, checked); + } +} + +void BtBookshelfTreeModel::moduleInserted(const QModelIndex &parent, int start, + int end) +{ + typedef BtBookshelfModel BM; + static const BM::ModuleRole PR(BM::ModulePointerRole); + + for (int i(start); i <= end; i++) { + QModelIndex moduleIndex(m_sourceModel->index(i, 0, parent)); + QVariant data(m_sourceModel->data(moduleIndex, PR)); + CSwordModuleInfo *module((CSwordModuleInfo *) (data.value<void*>())); + + addModule(module, m_defaultChecked); + } +} + +void BtBookshelfTreeModel::moduleRemoved(const QModelIndex &parent, int start, + int end) +{ + typedef BtBookshelfModel BM; + static const BM::ModuleRole PR(BM::ModulePointerRole); + + for (int i(start); i <= end; i++) { + QModelIndex moduleIndex(m_sourceModel->index(i, 0, parent)); + QVariant data(m_sourceModel->data(moduleIndex, PR)); + CSwordModuleInfo *module((CSwordModuleInfo *) (data.value<void*>())); + + removeModule(module); + } +} diff --git a/src/backend/bookshelfmodel/btbookshelftreemodel.h b/src/backend/bookshelfmodel/btbookshelftreemodel.h new file mode 100644 index 0000000..0a84ac3 --- /dev/null +++ b/src/backend/bookshelfmodel/btbookshelftreemodel.h @@ -0,0 +1,114 @@ +/********* +* +* In the name of the Father, and of the Son, and of the Holy Spirit. +* +* This file is part of BibleTime's source code, http://www.bibletime.info/. +* +* Copyright 1999-2009 by the BibleTime developers. +* The BibleTime source code is licensed under the GNU General Public License +* version 2.0. +* +**********/ + +#ifndef BTBOOKSHELFTREEMODEL_H +#define BTBOOKSHELFTREEMODEL_H + +#include <QAbstractItemModel> + +#include <QMap> +#include "backend/bookshelfmodel/btbookshelfmodel.h" +#include "backend/bookshelfmodel/item.h" + +namespace BookshelfModel { + class ModuleItem; +} +class CSwordModuleInfo; + +class BtBookshelfTreeModel: public QAbstractItemModel { + Q_OBJECT + + typedef QMap<CSwordModuleInfo*, BookshelfModel::ModuleItem*> ModuleItemMap; + + public: + enum ModuleRole { + CheckStateRole = BtBookshelfModel::UserRole, + UserRole = BtBookshelfModel::UserRole + 100 + }; + enum Group { GROUP_CATEGORY, GROUP_LANGUAGE, GROUP_DISTRIBUTION }; + typedef QList<Group> Grouping; + + BtBookshelfTreeModel(QObject *parent = 0); + virtual ~BtBookshelfTreeModel(); + + virtual int rowCount(const QModelIndex &parent = QModelIndex()) const; + virtual int columnCount(const QModelIndex &parent = QModelIndex()) + const; + virtual bool hasChildren(const QModelIndex &parent = QModelIndex()) + const; + virtual QModelIndex index(int row, int column, + const QModelIndex &parent = QModelIndex()) + const; + virtual QModelIndex parent(const QModelIndex &index) const; + virtual QVariant data(const QModelIndex &index, int role) const; + virtual Qt::ItemFlags flags(const QModelIndex &index) const; + virtual QVariant headerData(int section, Qt::Orientation orientation, + int role = Qt::DisplayRole) const; + virtual bool setData(const QModelIndex &index, const QVariant &value, + int role); + + void setSourceModel(QAbstractListModel *sourceModel); + inline QAbstractListModel *sourceModel() const { return m_sourceModel; } + void setGroupingOrder(const Grouping &groupingOrder); + inline Grouping groupingOrder() const { return m_groupingOrder; } + void setCheckable(bool checkable); + inline bool checkable() const { return m_checkable; } + inline void setDefaultChecked(bool defaultChecked) { + m_defaultChecked = defaultChecked; + } + inline bool defaultChecked() const { return m_defaultChecked; } + + QList<CSwordModuleInfo*> checkedModules() const; + + protected: + void addModule(CSwordModuleInfo *module, bool checked); + void addModule(CSwordModuleInfo *module, QModelIndex parentIndex, + Grouping &intermediateGrouping, bool checked); + void removeModule(CSwordModuleInfo *module); + + BookshelfModel::Item *getItem(const QModelIndex &index) const; + void resetParentCheckStates(QModelIndex parentIndex); + + template <class T> + QModelIndex getGroup(CSwordModuleInfo *module, + QModelIndex parentIndex) + { + BookshelfModel::Item *parentItem(getItem(parentIndex)); + int groupIndex; + T *groupItem(parentItem->getGroupItem<T>(module, &groupIndex)); + + if (groupItem == 0) { + groupItem = new T(module); + groupIndex = parentItem->indexFor(groupItem); + beginInsertRows(parentIndex, groupIndex, groupIndex); + parentItem->insertChild(groupIndex, groupItem); + endInsertRows(); + } + return index(groupIndex, 0, parentIndex); + } + + protected slots: + void moduleDataChanged(const QModelIndex &topLeft, + const QModelIndex &bottomRight); + void moduleInserted(const QModelIndex &parent, int start, int end); + void moduleRemoved(const QModelIndex &parent, int start, int end); + + protected: + QAbstractListModel *m_sourceModel; + BookshelfModel::Item *m_rootItem; + ModuleItemMap m_modules; + Grouping m_groupingOrder; + bool m_checkable; + bool m_defaultChecked; +}; + +#endif // BTBOOKSHELFTREEMODEL_H diff --git a/src/backend/bookshelfmodel/btcheckstatefilterproxymodel.cpp b/src/backend/bookshelfmodel/btcheckstatefilterproxymodel.cpp new file mode 100644 index 0000000..4d9cfd6 --- /dev/null +++ b/src/backend/bookshelfmodel/btcheckstatefilterproxymodel.cpp @@ -0,0 +1,70 @@ +/********* +* +* In the name of the Father, and of the Son, and of the Holy Spirit. +* +* This file is part of BibleTime's source code, http://www.bibletime.info/. +* +* Copyright 1999-2009 by the BibleTime developers. +* The BibleTime source code is licensed under the GNU General Public License +* version 2.0. +* +**********/ + +#include "backend/bookshelfmodel/btcheckstatefilterproxymodel.h" + +BtCheckStateFilterProxyModel::BtCheckStateFilterProxyModel(QObject *parent) + : QSortFilterProxyModel(parent), m_enabled(true), m_showChecked(true), + m_showUnchecked(false), m_showPartiallyChecked(true) +{ + setFilterRole(Qt::CheckStateRole); +} + +BtCheckStateFilterProxyModel::~BtCheckStateFilterProxyModel() { + // Intentionally empty +} + +void BtCheckStateFilterProxyModel::setEnabled(bool enable) { + if (enable == m_enabled) return; + m_enabled = enable; + invalidateFilter(); +} + +void BtCheckStateFilterProxyModel::setShowChecked(bool show) { + if (m_showChecked == show) return; + m_showChecked = show; + invalidateFilter(); +} + +void BtCheckStateFilterProxyModel::setShowUnchecked(bool show) { + if (m_showUnchecked == show) return; + m_showUnchecked = show; + invalidateFilter(); +} + +void BtCheckStateFilterProxyModel::setShowPartiallyChecked(bool show) { + if (m_showPartiallyChecked == show) return; + m_showPartiallyChecked = show; + invalidateFilter(); +} + +bool BtCheckStateFilterProxyModel::filterAcceptsRow(int row, + const QModelIndex &parent) const +{ + typedef Qt::CheckState CS; + + if (!m_enabled) return true; + + QAbstractItemModel *m(sourceModel()); + + QModelIndex i(m->index(row, filterKeyColumn(), parent)); + CS state((CS) m->data(i, filterRole()).toInt()); + Q_ASSERT(state == Qt::Checked || state == Qt::Unchecked || + state == Qt::PartiallyChecked); + if (state == Qt::Unchecked) { + return m_showUnchecked; + } else if (state == Qt::Checked) { + return m_showChecked; + } else { + return m_showPartiallyChecked; + } +} diff --git a/src/backend/bookshelfmodel/btcheckstatefilterproxymodel.h b/src/backend/bookshelfmodel/btcheckstatefilterproxymodel.h new file mode 100644 index 0000000..b2081f1 --- /dev/null +++ b/src/backend/bookshelfmodel/btcheckstatefilterproxymodel.h @@ -0,0 +1,47 @@ +/********* +* +* In the name of the Father, and of the Son, and of the Holy Spirit. +* +* This file is part of BibleTime's source code, http://www.bibletime.info/. +* +* Copyright 1999-2009 by the BibleTime developers. +* The BibleTime source code is licensed under the GNU General Public License +* version 2.0. +* +**********/ + +#ifndef BTCHECKSTATEFILTERPROXYMODEL_H +#define BTCHECKSTATEFILTERPROXYMODEL_H + +#include <QSortFilterProxyModel> + +class BtCheckStateFilterProxyModel: public QSortFilterProxyModel { + Q_OBJECT + public: + BtCheckStateFilterProxyModel(QObject *parent = 0); + virtual ~BtCheckStateFilterProxyModel(); + + inline bool enabled() const { return m_enabled; } + void setEnabled(bool enable); + + inline bool showChecked() const { return m_showChecked; } + void setShowChecked(bool show); + + inline bool showUnchecked() const { return m_showUnchecked; } + void setShowUnchecked(bool show); + + inline bool showPartiallyChecked() const { + return m_showPartiallyChecked; + } + void setShowPartiallyChecked(bool show); + + virtual bool filterAcceptsRow(int row, const QModelIndex &parent) const; + + protected: + bool m_enabled; + bool m_showChecked; + bool m_showUnchecked; + bool m_showPartiallyChecked; +}; + +#endif // BTSELECTEDMODULESBOOKSHELFPROXYMODEL_H diff --git a/src/backend/bookshelfmodel/btmodulenamefilterproxymodel.cpp b/src/backend/bookshelfmodel/btmodulenamefilterproxymodel.cpp new file mode 100644 index 0000000..f416175 --- /dev/null +++ b/src/backend/bookshelfmodel/btmodulenamefilterproxymodel.cpp @@ -0,0 +1,43 @@ +/********* +* +* In the name of the Father, and of the Son, and of the Holy Spirit. +* +* This file is part of BibleTime's source code, http://www.bibletime.info/. +* +* Copyright 1999-2009 by the BibleTime developers. +* The BibleTime source code is licensed under the GNU General Public License +* version 2.0. +* +**********/ + +#include "backend/bookshelfmodel/btmodulenamefilterproxymodel.h" + +BtModuleNameFilterProxyModel::BtModuleNameFilterProxyModel(QObject *parent) + : QSortFilterProxyModel(parent), m_enabled(true) +{ + setFilterCaseSensitivity(Qt::CaseInsensitive); +} + +BtModuleNameFilterProxyModel::~BtModuleNameFilterProxyModel() { + // Intentionally empty +} + +bool BtModuleNameFilterProxyModel::filterAcceptsRow(int row, + const QModelIndex &p) const +{ + if (!m_enabled) return true; + + const QAbstractItemModel *m(sourceModel()); + Q_ASSERT(m != 0); + + QModelIndex itemIndex(m->index(row, 0, p)); + int numChildren(m->rowCount(itemIndex)); + if (numChildren == 0) { + return QSortFilterProxyModel::filterAcceptsRow(row, p); + } else { + for (int i(0); i < numChildren; i++) { + if (filterAcceptsRow(i, itemIndex)) return true; + } + return false; + } +} diff --git a/src/backend/bookshelfmodel/btmodulenamefilterproxymodel.h b/src/backend/bookshelfmodel/btmodulenamefilterproxymodel.h new file mode 100644 index 0000000..dd6f652 --- /dev/null +++ b/src/backend/bookshelfmodel/btmodulenamefilterproxymodel.h @@ -0,0 +1,34 @@ +/********* +* +* In the name of the Father, and of the Son, and of the Holy Spirit. +* +* This file is part of BibleTime's source code, http://www.bibletime.info/. +* +* Copyright 1999-2009 by the BibleTime developers. +* The BibleTime source code is licensed under the GNU General Public License +* version 2.0. +* +**********/ + +#ifndef BTMODULENAMEFILTERPROXYMODEL_H +#define BTMODULENAMEFILTERPROXYMODEL_H + +#include <QSortFilterProxyModel> + +class BtModuleNameFilterProxyModel: public QSortFilterProxyModel { + Q_OBJECT + public: + BtModuleNameFilterProxyModel(QObject *parent = 0); + virtual ~BtModuleNameFilterProxyModel(); + + inline bool enabled() const { return m_enabled; } + void setEnabled(bool enable); + + virtual bool filterAcceptsRow(int row, const QModelIndex &parent) const; + + protected: + QString m_filter; + bool m_enabled; +}; + +#endif // BTMODULENAMEFILTERPROXYMODEL_H diff --git a/src/backend/bookshelfmodel/categoryitem.cpp b/src/backend/bookshelfmodel/categoryitem.cpp new file mode 100644 index 0000000..0d4b853 --- /dev/null +++ b/src/backend/bookshelfmodel/categoryitem.cpp @@ -0,0 +1,23 @@ +/********* +* +* In the name of the Father, and of the Son, and of the Holy Spirit. +* +* This file is part of BibleTime's source code, http://www.bibletime.info/. +* +* Copyright 1999-2009 by the BibleTime developers. +* The BibleTime source code is licensed under the GNU General Public License +* version 2.0. +* +**********/ + +#include "categoryitem.h" + +namespace BookshelfModel { + +CategoryItem::CategoryItem(CSwordModuleInfo *module) + : Item(ITEM_CATEGORY), m_category(module->category()) +{ + // Intentionally empty +} + +} // namespace BookshelfModel diff --git a/src/backend/bookshelfmodel/categoryitem.h b/src/backend/bookshelfmodel/categoryitem.h new file mode 100644 index 0000000..3cf7996 --- /dev/null +++ b/src/backend/bookshelfmodel/categoryitem.h @@ -0,0 +1,54 @@ +/********* +* +* In the name of the Father, and of the Son, and of the Holy Spirit. +* +* This file is part of BibleTime's source code, http://www.bibletime.info/. +* +* Copyright 1999-2009 by the BibleTime developers. +* The BibleTime source code is licensed under the GNU General Public License +* version 2.0. +* +**********/ + +#ifndef CATEGORYITEM_H +#define CATEGORYITEM_H + +#include "backend/bookshelfmodel/item.h" + +#include <QCoreApplication> +#include "backend/bookshelfmodel/btbookshelfmodel.h" +#include "backend/drivers/cswordmoduleinfo.h" + +namespace BookshelfModel { + +class CategoryItem: public Item { + Q_DECLARE_TR_FUNCTIONS(CategoryItem); + + public: + static const Item::Type GROUP_TYPE = Item::ITEM_CATEGORY; + + CategoryItem(CSwordModuleInfo *module); + + inline const CSwordModuleInfo::Category &category() const { + return m_category; + } + + inline QString name() const { + return BtBookshelfModel::categoryName(m_category); + } + + inline QIcon icon() const { + return BtBookshelfModel::categoryIcon(m_category); + } + + inline bool fitFor(CSwordModuleInfo *module) { + return module->category() == m_category; + } + + protected: + CSwordModuleInfo::Category m_category; +}; + +} // namespace BookshelfModel + +#endif // CATEGORYITEM_H diff --git a/src/backend/bookshelfmodel/distributionitem.cpp b/src/backend/bookshelfmodel/distributionitem.cpp new file mode 100644 index 0000000..411e236 --- /dev/null +++ b/src/backend/bookshelfmodel/distributionitem.cpp @@ -0,0 +1,23 @@ +/********* +* +* In the name of the Father, and of the Son, and of the Holy Spirit. +* +* This file is part of BibleTime's source code, http://www.bibletime.info/. +* +* Copyright 1999-2009 by the BibleTime developers. +* The BibleTime source code is licensed under the GNU General Public License +* version 2.0. +* +**********/ + +#include "backend/bookshelfmodel/distributionitem.h" + +namespace BookshelfModel { + +DistributionItem::DistributionItem(CSwordModuleInfo *module) + : Item(ITEM_DISTRIBUTION) +{ + m_distribution = module->config(CSwordModuleInfo::DistributionSource); +} + +} // namespace BookshelfModel diff --git a/src/backend/bookshelfmodel/distributionitem.h b/src/backend/bookshelfmodel/distributionitem.h new file mode 100644 index 0000000..ac3912d --- /dev/null +++ b/src/backend/bookshelfmodel/distributionitem.h @@ -0,0 +1,43 @@ +/********* +* +* In the name of the Father, and of the Son, and of the Holy Spirit. +* +* This file is part of BibleTime's source code, http://www.bibletime.info/. +* +* Copyright 1999-2009 by the BibleTime developers. +* The BibleTime source code is licensed under the GNU General Public License +* version 2.0. +* +**********/ + +#ifndef DISTRIBUTIONITEM_H +#define DISTRIBUTIONITEM_H + +#include "backend/bookshelfmodel/item.h" + +#include "backend/drivers/cswordmoduleinfo.h" + +namespace BookshelfModel { + +class DistributionItem: public Item { + public: + static const Item::Type GROUP_TYPE = Item::ITEM_DISTRIBUTION; + + DistributionItem(CSwordModuleInfo *module); + + inline QString distribution() const { return m_distribution; } + + inline QString name() const { return m_distribution; } + + inline bool fitFor(CSwordModuleInfo *module) { + return module->config(CSwordModuleInfo::DistributionSource) + == m_distribution; + } + + protected: + QString m_distribution; +}; + +} // namespace BookshelfModel + +#endif // DISTRIBUTIONITEM_H diff --git a/src/backend/bookshelfmodel/item.cpp b/src/backend/bookshelfmodel/item.cpp new file mode 100644 index 0000000..22bed91 --- /dev/null +++ b/src/backend/bookshelfmodel/item.cpp @@ -0,0 +1,57 @@ +/********* +* +* In the name of the Father, and of the Son, and of the Holy Spirit. +* +* This file is part of BibleTime's source code, http://www.bibletime.info/. +* +* Copyright 1999-2009 by the BibleTime developers. +* The BibleTime source code is licensed under the GNU General Public License +* version 2.0. +* +**********/ + +#include "backend/bookshelfmodel/item.h" + +#include "backend/bookshelfmodel/categoryitem.h" +#include "backend/bookshelfmodel/distributionitem.h" +#include "backend/bookshelfmodel/languageitem.h" + +namespace BookshelfModel { + +Item::Item(Type type) + : m_type(type), m_parent(0), m_checkState(Qt::Unchecked) +{ + // Intentionally empty +} + +Item::~Item() { + qDeleteAll(m_children); +} + +int Item::indexFor(Item *newItem) { + Q_ASSERT(newItem != 0); + + if (m_children.empty()) return 0; + + int i(0); + for (;;) { + Item *nextItem(m_children.at(i)); + Q_ASSERT(nextItem->type() == newItem->type()); + if (*newItem < *nextItem) { + return i; + } + i++; + if (i >= m_children.size()) { + return i; + } + } +} + +bool Item::operator<(const Item &other) const { + if (m_type != other.type()) { + return m_type < other.type(); + } + return name().localeAwareCompare(other.name()) < 0; +} + +} // namespace BookshelfModel diff --git a/src/backend/bookshelfmodel/item.h b/src/backend/bookshelfmodel/item.h new file mode 100644 index 0000000..cb2e6d9 --- /dev/null +++ b/src/backend/bookshelfmodel/item.h @@ -0,0 +1,170 @@ +/********* +* +* In the name of the Father, and of the Son, and of the Holy Spirit. +* +* This file is part of BibleTime's source code, http://www.bibletime.info/. +* +* Copyright 1999-2009 by the BibleTime developers. +* The BibleTime source code is licensed under the GNU General Public License +* version 2.0. +* +**********/ + +#ifndef ITEM_H +#define ITEM_H + +#include <QIcon> +#include <QList> +#include <QString> +#include <QtGlobal> + +class CSwordModuleInfo; + +namespace BookshelfModel { + +class Item { + public: + enum Type { + ITEM_ROOT = 0, + ITEM_CATEGORY = 1, + ITEM_LANGUAGE = 2, + ITEM_MODULE = 3, + ITEM_DISTRIBUTION = 4 + }; + + Item(Type type); + virtual ~Item(); + + /** + \brief Returns the type of this item. + */ + inline Type type() const { return m_type; } + + /** + \brief Returns a pointer to the parent item of this item. + \retval 0 if this item has no parent. + */ + inline Item *parent() const { return m_parent; } + + /** + \brief Returns the list of child items of this node. + */ + inline const QList<Item*> &children() const { return m_children; } + + /** + \brief Returns a pointer to the child item at the given index. + \pre The given index is valid + \param[in] index Index of child item to return. + */ + Item *childAt(int index) const { + Q_ASSERT(index >= 0 && index < m_children.size()); + return m_children.at(index); + } + + /** + \brief Returns the index of this item under its parent. + \retval -1 if this item has no parent. + */ + inline int childIndex() { + if (m_parent == 0) return -1; + return m_parent->m_children.indexOf(this); + } + + /** + \brief Returns the position for where the given child item would be + inserted. + \param[in] newItem Pointer to the item that would be inserted. + */ + int indexFor(Item *newItem); + + /** + \brief Inserts the given item as a child at the given index. + \pre The given index is a valid position for the item. + \param[in] index The child index to insert the item at. + \param[in] newItem The item to insert. + */ + inline void insertChild(int index, Item *newItem) { + Q_ASSERT(newItem != 0); + Q_ASSERT(index >= 0 && index <= m_children.size()); + m_children.insert(index, newItem); + newItem->setParent(this); + } + + inline void deleteChildAt(int index) { + Q_ASSERT(index >= 0 && index <= m_children.size()); + delete m_children.takeAt(index); + } + + template <class T> + T *getGroupItem(CSwordModuleInfo *module, int *index) { + for (int i(0); i < m_children.size(); i++) { + Q_ASSERT(m_children.at(i)->type() == T::GROUP_TYPE); + T *item(static_cast<T*>(m_children.at(i))); + if (item->fitFor(module)) { + if (index != 0) *index = i; + return item; + } + } + return 0; + } + + /** + \brief Returns the visible name of the item. + */ + inline virtual QString name() const { return QString::null; } + + /** + \brief Returns the visible icon of the item. + */ + inline virtual QIcon icon() const { return QIcon(); } + + /** + \brief Returns the check state of this item. + */ + inline const Qt::CheckState checkState() const { return m_checkState; } + + /** + \brief Sets the check state of this item. + \param[in] state new check state. + */ + inline void setCheckState(const Qt::CheckState state) { + m_checkState = state; + } + + /** + \brief Returns whether this item is fit to contain the given module. + \param[in] module The module to check with. + \retval true If this item is a group and can contain the given module. + \retval false This item is not a group or a wrong group. + */ + inline virtual bool fitFor(CSwordModuleInfo *module) { + Q_UNUSED(module); + return false; + } + + /** + \brief Comparsion operator used sorting child items. + */ + bool operator<(const Item &other) const; + + protected: + inline void setParent(Item *parent) { + Q_ASSERT(parent != 0); + m_parent = parent; + } + + protected: + Type m_type; + Item *m_parent; + QList<Item*> m_children; + Qt::CheckState m_checkState; +}; + +class RootItem: public Item { + public: + inline RootItem() : Item(Item::ITEM_ROOT) {} +}; + +} // Namespace BookshelfModel + +#endif // ITEM_H diff --git a/src/backend/bookshelfmodel/languageitem.cpp b/src/backend/bookshelfmodel/languageitem.cpp new file mode 100644 index 0000000..6c2af30 --- /dev/null +++ b/src/backend/bookshelfmodel/languageitem.cpp @@ -0,0 +1,23 @@ +/********* +* +* In the name of the Father, and of the Son, and of the Holy Spirit. +* +* This file is part of BibleTime's source code, http://www.bibletime.info/. +* +* Copyright 1999-2009 by the BibleTime developers. +* The BibleTime source code is licensed under the GNU General Public License +* version 2.0. +* +**********/ + +#include "languageitem.h" + +namespace BookshelfModel { + +LanguageItem::LanguageItem(CSwordModuleInfo *module) + : Item(ITEM_LANGUAGE), m_language(module->language()) +{ + // Intentionally empty +} + +} // namespace BookshelfModel diff --git a/src/backend/bookshelfmodel/languageitem.h b/src/backend/bookshelfmodel/languageitem.h new file mode 100644 index 0000000..3b329f1 --- /dev/null +++ b/src/backend/bookshelfmodel/languageitem.h @@ -0,0 +1,50 @@ +/********* +* +* In the name of the Father, and of the Son, and of the Holy Spirit. +* +* This file is part of BibleTime's source code, http://www.bibletime.info/. +* +* Copyright 1999-2009 by the BibleTime developers. +* The BibleTime source code is licensed under the GNU General Public License +* version 2.0. +* +**********/ + +#ifndef LANGUAGEITEM_H +#define LANGUAGEITEM_H + +#include "backend/bookshelfmodel/item.h" + +#include "backend/bookshelfmodel/btbookshelfmodel.h" +#include "backend/drivers/cswordmoduleinfo.h" +#include "util/directoryutil.h" + +namespace BookshelfModel { + +class LanguageItem: public Item { + public: + static const Item::Type GROUP_TYPE = Item::ITEM_LANGUAGE; + + LanguageItem(CSwordModuleInfo *module); + + inline const CLanguageMgr::Language *language() const { return m_language; } + + inline QString name() const { + return BtBookshelfModel::languageName(m_language); + } + + inline QIcon icon() const { + return util::filesystem::DirectoryUtil::getIcon("flag.svg"); + } + + inline bool fitFor(CSwordModuleInfo *module) { + return module->language() == m_language; + } + + protected: + const CLanguageMgr::Language *m_language; +}; + +} // namespace BookshelfModel + +#endif // LANGUAGEITEM_H diff --git a/src/backend/bookshelfmodel/moduleitem.cpp b/src/backend/bookshelfmodel/moduleitem.cpp new file mode 100644 index 0000000..6a543bc --- /dev/null +++ b/src/backend/bookshelfmodel/moduleitem.cpp @@ -0,0 +1,25 @@ +/********* +* +* In the name of the Father, and of the Son, and of the Holy Spirit. +* +* This file is part of BibleTime's source code, http://www.bibletime.info/. +* +* Copyright 1999-2009 by the BibleTime developers. +* The BibleTime source code is licensed under the GNU General Public License +* version 2.0. +* +**********/ + +#include "moduleitem.h" + +#include "util/cresmgr.h" + +namespace BookshelfModel { + +ModuleItem::ModuleItem(CSwordModuleInfo *module) + : Item(ITEM_MODULE), m_moduleInfo(module) +{ + Q_ASSERT(module != 0); +} + +} // namespace BookshelfModel diff --git a/src/backend/bookshelfmodel/moduleitem.h b/src/backend/bookshelfmodel/moduleitem.h new file mode 100644 index 0000000..7bafc1f --- /dev/null +++ b/src/backend/bookshelfmodel/moduleitem.h @@ -0,0 +1,41 @@ +/********* +* +* In the name of the Father, and of the Son, and of the Holy Spirit. +* +* This file is part of BibleTime's source code, http://www.bibletime.info/. +* +* Copyright 1999-2009 by the BibleTime developers. +* The BibleTime source code is licensed under the GNU General Public License +* version 2.0. +* +**********/ + +#ifndef MODULEITEM_H +#define MODULEITEM_H + +#include "backend/bookshelfmodel/item.h" + +#include "backend/bookshelfmodel/btbookshelfmodel.h" +#include "backend/drivers/cswordmoduleinfo.h" + +namespace BookshelfModel { + +class ModuleItem: public Item { + public: + ModuleItem(CSwordModuleInfo *module); + + CSwordModuleInfo *moduleInfo() const { return m_moduleInfo; } + + inline QString name() const { return m_moduleInfo->name(); } + + inline QIcon icon() const { + return BtBookshelfModel::categoryIcon(m_moduleInfo->category()); + } + + protected: + CSwordModuleInfo *m_moduleInfo; +}; + +} // namespace BookshelfModel + +#endif // MODULEITEM_H diff --git a/src/backend/keys/cswordversekey.cpp b/src/backend/keys/cswordversekey.cpp index 8db5d74..8008a7f 100644 --- a/src/backend/keys/cswordversekey.cpp +++ b/src/backend/keys/cswordversekey.cpp @@ -18,8 +18,13 @@ #include <swmodule.h> #include <localemgr.h> -CSwordVerseKey::CSwordVerseKey( CSwordModuleInfo* const module ) : CSwordKey(module) { +CSwordVerseKey::CSwordVerseKey( CSwordModuleInfo* const module ) : + CSwordKey(module) +{ if ( CSwordBibleModuleInfo* bible = dynamic_cast<CSwordBibleModuleInfo*>(module) ) { + // Copy important settings like versification system + copyFrom((sword::VerseKey*) bible->module()->getKey()); + key( bible->lowerBound().key() ); } } diff --git a/src/backend/rendering/cdisplayrendering.cpp b/src/backend/rendering/cdisplayrendering.cpp index 50d5b18..32444b4 100644 --- a/src/backend/rendering/cdisplayrendering.cpp +++ b/src/backend/rendering/cdisplayrendering.cpp @@ -99,8 +99,9 @@ namespace Rendering { const QString CDisplayRendering::keyToHTMLAnchor(const QString& key) { QString ret = key; - ret = ret.trimmed().remove(QRegExp("[^A-Za-z0-9]+")); - ret = ret.remove(QRegExp("^\\d+|")); + // Be careful not to remove non-ASCII characters, this causes problems + // with many languages. + ret = ret.trimmed().remove(QRegExp("\\s")).replace(QString(":"), QString("_")); return ret; } diff --git a/src/bibletime.cpp b/src/bibletime.cpp index fdc9f48..c7af792 100644 --- a/src/bibletime.cpp +++ b/src/bibletime.cpp @@ -8,8 +8,8 @@ **********/ #include "bibletime.h" +#include "frontend/btaboutmoduledialog.h" #include "frontend/cmdiarea.h" -#include "frontend/mainindex/cmainindex.h" #include "frontend/mainindex/bookshelf/cbookshelfindex.h" #include "frontend/displaywindow/btactioncollection.h" #include "frontend/displaywindow/cdisplaywindow.h" @@ -17,6 +17,7 @@ #include "frontend/displaywindow/creadwindow.h" #include "frontend/displaywindow/cwritewindow.h" #include "frontend/keychooser/ckeychooser.h" +#include "frontend/searchdialog/csearchdialog.h" #include "backend/config/cbtconfig.h" #include "util/ctoolclass.h" @@ -33,6 +34,7 @@ #include "backend/keys/cswordldkey.h" //Qt includes +#include <QInputDialog> #include <QSplitter> #include <QDebug> #include <QAction> @@ -155,7 +157,7 @@ CDisplayWindow* BibleTime::createReadDisplayWindow(QList<CSwordModuleInfo*> modu qApp->processEvents(); // Now all events, including mouse clicks for the displayWindow have been handled // and we can let the user click the same module again - m_bookshelfPage->unfreezeModules(modules); + //m_bookshelfPage->unfreezeModules(modules); qApp->restoreOverrideCursor(); return displayWindow; } @@ -192,6 +194,53 @@ CDisplayWindow* BibleTime::createWriteDisplayWindow(CSwordModuleInfo* module, co return displayWindow; } +CDisplayWindow* BibleTime::moduleEditPlain(CSwordModuleInfo *module) { + /// \todo Refactor this. + return createWriteDisplayWindow(module, + QString::null, + CDisplayWindow::PlainTextWindow); +} + +CDisplayWindow* BibleTime::moduleEditHtml(CSwordModuleInfo *module) { + /// \todo Refactor this. + return createWriteDisplayWindow(module, + QString::null, + CDisplayWindow::HTMLWindow); +} + + +void BibleTime::searchInModule(CSwordModuleInfo *module) { + /// \todo Refactor this. + QList<CSwordModuleInfo *> modules; + modules.append(module); + Search::CSearchDialog::openDialog(modules, QString::null); +} + +void BibleTime::moduleUnlock(CSwordModuleInfo *module) { + bool ok; + const QString unlockKey = + QInputDialog::getText( + this, + tr("Unlock Work"), + tr("Enter the unlock key for this work."), + QLineEdit::Normal, + module->config(CSwordModuleInfo::CipherKey), + &ok + ); + if (ok) { + /// \todo Refactor. Unlock the module via a global modules model. + if (module->unlock(unlockKey)) { + CPointers::backend()->reloadModules(CSwordBackend::OtherChange); + } + } +} + +void BibleTime::moduleAbout(CSwordModuleInfo *module) { + BTAboutModuleDialog *dialog(new BTAboutModuleDialog(this, module)); + dialog->show(); + dialog->raise(); +} + /** Refreshes all presenters.*/ void BibleTime::refreshDisplayWindows() { diff --git a/src/bibletime.h b/src/bibletime.h index 63d603d..4ddf2e1 100644 --- a/src/bibletime.h +++ b/src/bibletime.h @@ -22,9 +22,9 @@ class CSwordModuleInfo; class BtActionClass; class CMDIArea; class CDisplayWindow; -class CMainIndex; class CBookmarkIndex; class CBookshelfIndex; +class BtBookshelfDockWidget; namespace InfoDisplay { class CInfoDisplay; @@ -232,8 +232,14 @@ protected slots: * Creates a new presenter in the MDI area according to the type of the module. */ CDisplayWindow* createReadDisplayWindow(QList<CSwordModuleInfo*> modules, const QString& key); - CDisplayWindow* createReadDisplayWindow(CSwordModuleInfo* module, const QString& key); + CDisplayWindow* createReadDisplayWindow(CSwordModuleInfo* module, const QString& key = QString::null); CDisplayWindow* createWriteDisplayWindow(CSwordModuleInfo* module, const QString& key, const CDisplayWindow::WriteWindowType& type); + CDisplayWindow* moduleEditPlain(CSwordModuleInfo *module); + CDisplayWindow* moduleEditHtml(CSwordModuleInfo *module); + void searchInModule(CSwordModuleInfo *module); + void moduleUnlock(CSwordModuleInfo *module); + void moduleAbout(CSwordModuleInfo *module); + /** * Is called when the window menu is about to show ;-) */ @@ -327,8 +333,7 @@ protected slots: private: // Docking widgets and their respective content widgets: - QDockWidget* m_bookshelfDock; - CBookshelfIndex* m_bookshelfPage; + BtBookshelfDockWidget* m_bookshelfDock; QDockWidget* m_bookmarksDock; CBookmarkIndex* m_bookmarksPage; QDockWidget* m_magDock; diff --git a/src/bibletime_init.cpp b/src/bibletime_init.cpp index c59964b..614469b 100644 --- a/src/bibletime_init.cpp +++ b/src/bibletime_init.cpp @@ -15,7 +15,7 @@ #include "backend/managers/btstringmgr.h" #include "backend/managers/cswordbackend.h" #include "backend/managers/clanguagemgr.h" -#include "frontend/mainindex/cmainindex.h" +#include "frontend/btbookshelfdockwidget.h" #include "frontend/displaywindow/btactioncollection.h" #include "frontend/profile/cprofilemgr.h" #include "frontend/profile/cprofile.h" @@ -50,10 +50,7 @@ void BibleTime::initView() m_mdi = new CMDIArea(this); setCentralWidget(m_mdi); - m_bookshelfDock = new QDockWidget(tr("Bookshelf"), this); - m_bookshelfDock->setObjectName("BookshelfDock"); - m_bookshelfPage = new CBookshelfIndex(0); - m_bookshelfDock->setWidget(m_bookshelfPage); + m_bookshelfDock = new BtBookshelfDockWidget(this); addDockWidget(Qt::LeftDockWidgetArea, m_bookshelfDock); m_bookmarksDock = new QDockWidget(tr("Bookmarks"), this); @@ -396,12 +393,18 @@ void BibleTime::initConnections() ok = connect(m_bookmarksPage, SIGNAL(createReadDisplayWindow(QList<CSwordModuleInfo*>, const QString&)), this, SLOT(createReadDisplayWindow(QList<CSwordModuleInfo*>,const QString&))); Q_ASSERT(ok); - ok = connect(m_bookshelfPage, SIGNAL(createReadDisplayWindow(QList<CSwordModuleInfo*>, const QString&)), - this, SLOT(createReadDisplayWindow(QList<CSwordModuleInfo*>,const QString&))); - Q_ASSERT(ok); - ok = connect(m_bookshelfPage, SIGNAL(createWriteDisplayWindow(CSwordModuleInfo*, const QString&, const CDisplayWindow::WriteWindowType&)), - this, SLOT(createWriteDisplayWindow(CSwordModuleInfo*,const QString&, const CDisplayWindow::WriteWindowType&))); - Q_ASSERT(ok); + connect(m_bookshelfDock, SIGNAL(moduleOpenTriggered(CSwordModuleInfo*)), + this, SLOT(createReadDisplayWindow(CSwordModuleInfo*))); + connect(m_bookshelfDock, SIGNAL(moduleSearchTriggered(CSwordModuleInfo*)), + this, SLOT(searchInModule(CSwordModuleInfo*))); + connect(m_bookshelfDock, SIGNAL(moduleEditPlainTriggered(CSwordModuleInfo*)), + this, SLOT(moduleEditPlain(CSwordModuleInfo*))); + connect(m_bookshelfDock, SIGNAL(moduleEditHtmlTriggered(CSwordModuleInfo*)), + this, SLOT(moduleEditHtml(CSwordModuleInfo*))); + connect(m_bookshelfDock, SIGNAL(moduleUnlockTriggered(CSwordModuleInfo*)), + this, SLOT(moduleUnlock(CSwordModuleInfo*))); + connect(m_bookshelfDock, SIGNAL(moduleAboutTriggered(CSwordModuleInfo*)), + this, SLOT(moduleAbout(CSwordModuleInfo*))); connect(qApp, SIGNAL(aboutToQuit()), this, SLOT(slot_aboutToQuit())); } diff --git a/src/bibletime_slots.cpp b/src/bibletime_slots.cpp index 471c96f..40d1319 100644 --- a/src/bibletime_slots.cpp +++ b/src/bibletime_slots.cpp @@ -23,7 +23,6 @@ #include "backend/config/cbtconfig.h" #include "frontend/cinputdialog.h" #include "frontend/cinfodisplay.h" -#include "frontend/mainindex/cmainindex.h" #include "frontend/displaywindow/cdisplaywindow.h" #include "frontend/displaywindow/cbiblereadwindow.h" #include "frontend/searchdialog/csearchdialog.h" diff --git a/src/frontend/bookshelfmanager/indexpage/btindexpage.cpp b/src/frontend/bookshelfmanager/indexpage/btindexpage.cpp index 8b2b335..cad5a84 100644 --- a/src/frontend/bookshelfmanager/indexpage/btindexpage.cpp +++ b/src/frontend/bookshelfmanager/indexpage/btindexpage.cpp @@ -37,7 +37,7 @@ BtIndexPage::BtIndexPage() QVBoxLayout *vboxLayout; QHBoxLayout *hboxLayout; vboxLayout = new QVBoxLayout(this); - + m_autoDeleteOrphanedIndicesBox = new QCheckBox(this); m_autoDeleteOrphanedIndicesBox->setToolTip(tr("If selected, those indexes which have no corresponding work will be deleted when BibleTime starts")); m_autoDeleteOrphanedIndicesBox->setText(tr("Automatically delete orphaned indexes when BibleTime starts")); @@ -106,15 +106,15 @@ QString BtIndexPage::header() /** Populates the module list with installed modules and orphaned indices */ void BtIndexPage::populateModuleList() { m_moduleList->clear(); - + // populate installed modules m_modsWithIndices = new QTreeWidgetItem(m_moduleList); - m_modsWithIndices->setText(0, tr("Works with indexes")); + m_modsWithIndices->setText(0, tr("Indexed Works")); m_modsWithIndices->setFlags(Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemIsTristate); m_modsWithIndices->setExpanded(true); m_modsWithoutIndices = new QTreeWidgetItem(m_moduleList); - m_modsWithoutIndices->setText(0, tr("Works without indexes")); + m_modsWithoutIndices->setText(0, tr("Unindexed Works")); m_modsWithoutIndices->setFlags(Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemIsTristate); m_modsWithoutIndices->setExpanded(true); @@ -124,7 +124,7 @@ void BtIndexPage::populateModuleList() { QList<CSwordModuleInfo*>::iterator end_it = modules.end(); for (QList<CSwordModuleInfo*>::iterator it = modules.begin(); it != end_it; ++it) { QTreeWidgetItem* item = 0; - + if ((*it)->hasIndex()) { item = new QTreeWidgetItem(m_modsWithIndices); item->setText(0, (*it)->name()); @@ -169,7 +169,7 @@ void BtIndexPage::createIndices() void BtIndexPage::deleteIndices() { bool indicesDeleted = false; - + for (int i = 0; i < m_modsWithIndices->childCount(); i++) { if (m_modsWithIndices->child(i)->checkState(0) == Qt::Checked) { CSwordModuleInfo* module = CPointers::backend()->findModuleByName(m_modsWithIndices->child(i)->text(0).toUtf8()); @@ -191,7 +191,7 @@ void BtIndexPage::deleteOrphanedIndices() QDir dir(CSwordModuleInfo::getGlobalBaseIndexLocation()); dir.setFilter(QDir::Dirs); CSwordModuleInfo* module; - + for (unsigned int i = 0; i < dir.count(); i++) { if (dir[i] != "." && dir[i] != "..") { if ( (module = CPointers::backend()->findModuleByName(dir[i])) ) { //mod exists diff --git a/src/frontend/btbookshelfdockwidget.cpp b/src/frontend/btbookshelfdockwidget.cpp new file mode 100644 index 0000000..4b73c3a --- /dev/null +++ b/src/frontend/btbookshelfdockwidget.cpp @@ -0,0 +1,300 @@ +/********* +* +* In the name of the Father, and of the Son, and of the Holy Spirit. +* +* This file is part of BibleTime's source code, http://www.bibletime.info/. +* +* Copyright 1999-2009 by the BibleTime developers. +* The BibleTime source code is licensed under the GNU General Public License +* version 2.0. +* +**********/ + +#include "frontend/btbookshelfdockwidget.h" + +#include <QAction> +#include <QActionGroup> +#include <QApplication> +#include <QHBoxLayout> +#include <QKeyEvent> +#include <QLabel> +#include <QLineEdit> +#include <QMenu> +#include <QToolBar> +#include <QToolButton> +#include <QVBoxLayout> +#include "backend/bookshelfmodel/btbookshelfmodel.h" +#include "backend/bookshelfmodel/btbookshelftreemodel.h" +#include "backend/bookshelfmodel/btcheckstatefilterproxymodel.h" +#include "backend/bookshelfmodel/btmodulenamefilterproxymodel.h" +#include "backend/drivers/cswordmoduleinfo.h" +#include "backend/managers/cswordbackend.h" +#include "frontend/btaboutmoduledialog.h" +#include "frontend/mainindex/btbookshelfview.h" +#include "util/cpointers.h" +#include "util/cresmgr.h" +#include "util/directoryutil.h" + +BtBookshelfDockWidget::BtBookshelfDockWidget(QWidget *parent, Qt::WindowFlags f) + : QDockWidget(parent, f) +{ + setObjectName("BookshelfDock"); + + // Setup models: + m_bookshelfModel = new BtBookshelfModel(this); + m_bookshelfModel->addModules(CPointers::backend()->moduleList()); + m_bookshelfTreeModel = new BtBookshelfTreeModel(this); + m_bookshelfTreeModel->setDefaultChecked(true); + m_bookshelfTreeModel->setSourceModel(m_bookshelfModel); + m_filterProxyModel = new BtCheckStateFilterProxyModel(this); + m_filterProxyModel->setFilterRole(BtBookshelfTreeModel::CheckStateRole); + m_filterProxyModel->setSourceModel(m_bookshelfTreeModel); + m_nameFilterProxyModel = new BtModuleNameFilterProxyModel(this); + m_nameFilterProxyModel->setSourceModel(m_filterProxyModel); + + // Setup actions and menus: + initMenus(); + + // Setup widgets: + m_widget = new QWidget(this); + QVBoxLayout *layout(new QVBoxLayout); + layout->setContentsMargins(0, 0, 0, 0); + QHBoxLayout *toolBar(new QHBoxLayout); + m_nameFilterLabel = new QLabel(this); + toolBar->addWidget(m_nameFilterLabel); + + m_nameFilterEdit = new QLineEdit(this); + m_nameFilterEdit->installEventFilter(this); + m_nameFilterLabel->setBuddy(m_nameFilterEdit); + toolBar->addWidget(m_nameFilterEdit); + + m_groupingButton = new QToolButton(this); + m_groupingButton->setPopupMode(QToolButton::InstantPopup); + m_groupingButton->setMenu(m_groupingMenu); + m_groupingButton->setIcon(m_groupingMenu->icon()); + m_groupingButton->setAutoRaise(true); + toolBar->addWidget(m_groupingButton); + + m_showHideButton = new QToolButton(this); + m_showHideButton->setDefaultAction(m_showHideAction); + m_showHideButton->setAutoRaise(true); + toolBar->addWidget(m_showHideButton); + layout->addLayout(toolBar); + + m_view = new BtBookshelfView(this); + m_view->setModel(m_nameFilterProxyModel); + layout->addWidget(m_view); + m_widget->setLayout(layout); + setWidget(m_widget); + + connect(CPointers::backend(), + SIGNAL(sigSwordSetupChanged(CSwordBackend::SetupChangedReason)), + this, SLOT(swordSetupChanged())); + connect(m_nameFilterEdit, SIGNAL(textEdited(QString)), + m_nameFilterProxyModel, SLOT(setFilterFixedString(QString))); + connect(m_view, SIGNAL(contextMenuActivated(QPoint)), + this, SLOT(showContextMenu(QPoint))); + connect(m_view, + SIGNAL(moduleContextMenuActivated(CSwordModuleInfo*,QPoint)), + this, SLOT(showItemContextMenu(CSwordModuleInfo*,QPoint))); + connect(m_view, SIGNAL(moduleActivated(CSwordModuleInfo*)), + this, SIGNAL(moduleOpenTriggered(CSwordModuleInfo*))); + + retranslateInterface(); +} + +bool BtBookshelfDockWidget::eventFilter(QObject *object, QEvent *event) { + Q_ASSERT(object == m_nameFilterEdit); + if (event->type() == QEvent::KeyPress) { + QKeyEvent *e = static_cast<QKeyEvent*>(event); + switch (e->key()) { + case Qt::Key_Up: + case Qt::Key_Down: + case Qt::Key_Enter: + case Qt::Key_Return: + QApplication::sendEvent(m_view, event); + return true; + default: + break; + } + } + return false; +} + +void BtBookshelfDockWidget::initMenus() { + typedef util::filesystem::DirectoryUtil DU; + namespace RM = CResMgr::mainIndex; + + m_contextMenu = new QMenu(this); + m_groupingMenu = new QMenu(this); + m_contextMenu->addMenu(m_groupingMenu); + m_groupingMenu->setIcon(DU::getIcon(RM::grouping::icon)); + m_groupingActionGroup = new QActionGroup(this); + connect(m_groupingActionGroup, SIGNAL(triggered(QAction*)), + this, SLOT(groupingActionTriggered(QAction*))); + + m_groupingCatLangAction = new QAction(this); + m_groupingCatLangAction->setIcon(DU::getIcon(RM::grouping::icon)); + m_groupingCatLangAction->setChecked(true); + m_groupingActionGroup->addAction(m_groupingCatLangAction); + m_groupingMenu->addAction(m_groupingCatLangAction); + + m_groupingCatAction = new QAction(this); + m_groupingCatAction->setIcon(DU::getIcon(RM::grouping::icon)); + m_groupingActionGroup->addAction(m_groupingCatAction); + m_groupingMenu->addAction(m_groupingCatAction); + + m_groupingLangCatAction = new QAction(this); + m_groupingLangCatAction->setIcon(DU::getIcon(RM::grouping::icon)); + m_groupingActionGroup->addAction(m_groupingLangCatAction); + m_groupingMenu->addAction(m_groupingLangCatAction); + + m_groupingLangAction = new QAction(this); + m_groupingLangAction->setIcon(DU::getIcon(RM::grouping::icon)); + m_groupingActionGroup->addAction(m_groupingLangAction); + m_groupingMenu->addAction(m_groupingLangAction); + + m_groupingNoneAction = new QAction(this); + m_groupingNoneAction->setIcon(DU::getIcon(RM::grouping::icon)); + m_groupingActionGroup->addAction(m_groupingNoneAction); + m_groupingMenu->addAction(m_groupingNoneAction); + + m_showHideAction = new QAction(this); + m_showHideAction->setIcon(DU::getIcon(RM::search::icon)); + m_showHideAction->setCheckable(true); + connect(m_showHideAction, SIGNAL(toggled(bool)), + this, SLOT(showHideEnabled(bool))); + m_contextMenu->addAction(m_showHideAction); + + m_itemContextMenu = new QMenu(this); + m_itemActionGroup = new QActionGroup(this); + connect(m_itemActionGroup, SIGNAL(triggered(QAction*)), + this, SLOT(itemActionTriggered(QAction*))); + + m_itemOpenAction = new QAction(this); + m_itemActionGroup->addAction(m_itemOpenAction); + m_itemContextMenu->addAction(m_itemOpenAction); + + m_itemSearchAction = new QAction(this); + m_itemSearchAction->setIcon(DU::getIcon(RM::search::icon)); + m_itemActionGroup->addAction(m_itemSearchAction); + m_itemContextMenu->addAction(m_itemSearchAction); + + m_itemEditMenu = new QMenu(this); + m_itemEditMenu->setIcon(DU::getIcon(RM::editModuleMenu::icon)); + m_itemContextMenu->addMenu(m_itemEditMenu); + m_itemEditPlainAction = new QAction(this); + m_itemEditPlainAction->setIcon(DU::getIcon(RM::editModulePlain::icon)); + m_itemActionGroup->addAction(m_itemEditPlainAction); + m_itemEditMenu->addAction(m_itemEditPlainAction); + + m_itemEditHtmlAction = new QAction(this); + m_itemEditHtmlAction->setIcon(DU::getIcon(RM::editModuleHTML::icon)); + m_itemActionGroup->addAction(m_itemEditHtmlAction); + m_itemEditMenu->addAction(m_itemEditHtmlAction); + + m_itemUnlockAction = new QAction(this); + m_itemUnlockAction->setIcon(DU::getIcon(RM::unlockModule::icon)); + m_itemActionGroup->addAction(m_itemUnlockAction); + m_itemContextMenu->addAction(m_itemUnlockAction); + + m_itemAboutAction = new QAction(this); + m_itemAboutAction->setIcon(DU::getIcon(RM::aboutModule::icon)); + m_itemActionGroup->addAction(m_itemAboutAction); + m_itemContextMenu->addAction(m_itemAboutAction); +} + +void BtBookshelfDockWidget::retranslateInterface() { + setWindowTitle(tr("Bookshelf")); + + m_nameFilterLabel->setText(tr("Fi<er:")); + m_groupingButton->setText(tr("Grouping")); + m_groupingButton->setToolTip(tr("Change the grouping of items in the bookshelf.")); + + m_groupingMenu->setTitle(tr("Grouping")); + m_groupingCatLangAction->setText(tr("Category/Language")); + m_groupingCatAction->setText(tr("Category")); + m_groupingLangCatAction->setText(tr("Language/Category")); + m_groupingLangAction->setText(tr("Language")); + m_groupingNoneAction->setText(tr("No grouping")); + m_showHideAction->setText(tr("Show/hide works")); + + m_itemOpenAction->setText(tr("&Open")); + m_itemEditMenu->setTitle(tr("&Edit")); + m_itemEditPlainAction->setText(tr("&Plain text")); + m_itemEditHtmlAction->setText(tr("&HTML")); + m_itemUnlockAction->setText(tr("&Unlock...")); + m_itemAboutAction->setText(tr("&About...")); +} + +void BtBookshelfDockWidget::swordSetupChanged() { + QSet<CSwordModuleInfo*> added(CPointers::backend()->moduleList().toSet()); + QSet<CSwordModuleInfo*> removed(m_bookshelfModel->modules().toSet()); + QSet<CSwordModuleInfo*> t(removed); + removed.subtract(added); + added.subtract(t); + m_bookshelfModel->removeModules(removed); + m_bookshelfModel->addModules(added); +} + +void BtBookshelfDockWidget::showContextMenu(QPoint pos) { + m_contextMenu->popup(pos); +} + +void BtBookshelfDockWidget::groupingActionTriggered(QAction *action) { + BtBookshelfTreeModel::Grouping g; + if (action == m_groupingCatAction) { + g.append(BtBookshelfTreeModel::GROUP_CATEGORY); + } else if (action == m_groupingCatLangAction) { + g.append(BtBookshelfTreeModel::GROUP_CATEGORY); + g.append(BtBookshelfTreeModel::GROUP_LANGUAGE); + } else if (action == m_groupingLangAction) { + g.append(BtBookshelfTreeModel::GROUP_LANGUAGE); + } else if (action == m_groupingLangCatAction) { + g.append(BtBookshelfTreeModel::GROUP_LANGUAGE); + g.append(BtBookshelfTreeModel::GROUP_CATEGORY); + } + m_bookshelfTreeModel->setGroupingOrder(g); + m_view->setRootIsDecorated(!g.isEmpty()); +} + +void BtBookshelfDockWidget::showHideEnabled(bool enable) { + if (enable) { + m_bookshelfTreeModel->setCheckable(true); + m_filterProxyModel->setEnabled(false); + } else { + m_filterProxyModel->setEnabled(true); + m_bookshelfTreeModel->setCheckable(false); + } +} + +void BtBookshelfDockWidget::showItemContextMenu(CSwordModuleInfo *module, + QPoint pos) +{ + m_itemContextMenu->setProperty("BtModule", qVariantFromValue((void*) module)); + m_itemSearchAction->setText(tr("&Search in %1...").arg(module->name())); + m_itemOpenAction->setEnabled(!module->isLocked()); + m_itemEditMenu->setEnabled(module->isWritable()); + m_itemUnlockAction->setEnabled(module->isLocked()); + + m_itemContextMenu->popup(pos); +} + +void BtBookshelfDockWidget::itemActionTriggered(QAction *action) { + CSwordModuleInfo *module((CSwordModuleInfo*) m_itemContextMenu->property("BtModule").value<void*>()); + if (module == 0) return; + + if (action == m_itemOpenAction) { + emit moduleOpenTriggered(module); + } else if (action == m_itemSearchAction) { + emit moduleSearchTriggered(module); + } else if (action == m_itemEditPlainAction) { + emit moduleEditPlainTriggered(module); + } else if (action == m_itemEditHtmlAction) { + emit moduleEditHtmlTriggered(module); + } else if (action == m_itemUnlockAction) { + emit moduleUnlockTriggered(module); + } else if (action == m_itemAboutAction) { + emit moduleAboutTriggered(module); + } +} diff --git a/src/frontend/btbookshelfdockwidget.h b/src/frontend/btbookshelfdockwidget.h new file mode 100644 index 0000000..d437c92 --- /dev/null +++ b/src/frontend/btbookshelfdockwidget.h @@ -0,0 +1,94 @@ +/********* +* +* In the name of the Father, and of the Son, and of the Holy Spirit. +* +* This file is part of BibleTime's source code, http://www.bibletime.info/. +* +* Copyright 1999-2009 by the BibleTime developers. +* The BibleTime source code is licensed under the GNU General Public License +* version 2.0. +* +**********/ + +#ifndef BTBOOKSHELFDOCKWIDGET_H +#define BTBOOKSHELFDOCKWIDGET_H + +#include <QDockWidget> + +class CSwordModuleInfo; +class BtBookshelfView; +class BtBookshelfModel; +class BtBookshelfTreeModel; +class BtCheckStateFilterProxyModel; +class BtModuleNameFilterProxyModel; +class QAction; +class QActionGroup; +class QLabel; +class QLineEdit; +class QMenu; +class QToolBar; +class QToolButton; + +class BtBookshelfDockWidget: public QDockWidget { + Q_OBJECT + public: + BtBookshelfDockWidget(QWidget *parent = 0, Qt::WindowFlags f = 0); + + signals: + void moduleOpenTriggered(CSwordModuleInfo *module); + void moduleSearchTriggered(CSwordModuleInfo *module); + void moduleEditPlainTriggered(CSwordModuleInfo *module); + void moduleEditHtmlTriggered(CSwordModuleInfo *module); + void moduleUnlockTriggered(CSwordModuleInfo *module); + void moduleAboutTriggered(CSwordModuleInfo *module); + + protected: + bool eventFilter(QObject *object, QEvent *event); + void initMenus(); + void retranslateInterface(); + + protected slots: + void swordSetupChanged(); + void showContextMenu(QPoint pos); + void groupingActionTriggered(QAction *action); + void showHideEnabled(bool enable); + void showItemContextMenu(CSwordModuleInfo *module, QPoint pos); + void itemActionTriggered(QAction *action); + + protected: + // Models: + BtBookshelfModel *m_bookshelfModel; + BtBookshelfTreeModel *m_bookshelfTreeModel; + BtCheckStateFilterProxyModel *m_filterProxyModel; + BtModuleNameFilterProxyModel *m_nameFilterProxyModel; + + // Widgets: + QWidget *m_widget; + BtBookshelfView *m_view; + QLabel *m_nameFilterLabel; + QLineEdit *m_nameFilterEdit; + QToolButton *m_groupingButton; + QToolButton *m_showHideButton; + + // Popup menus: + QMenu *m_contextMenu; + QMenu *m_groupingMenu; + QActionGroup *m_groupingActionGroup; + QAction *m_groupingCatLangAction; + QAction *m_groupingCatAction; + QAction *m_groupingLangCatAction; + QAction *m_groupingLangAction; + QAction *m_groupingNoneAction; + QAction *m_showHideAction; + QMenu *m_itemContextMenu; + QActionGroup *m_itemActionGroup; + QAction *m_itemOpenAction; + QAction *m_itemSearchAction; + QMenu *m_itemEditMenu; + QAction *m_itemEditPlainAction; + QAction *m_itemEditHtmlAction; + QAction *m_itemUnlockAction; + QAction *m_itemAboutAction; +}; + +#endif // BTBOOKSHELFDOCKWIDGET_H diff --git a/src/frontend/displaywindow/btactioncollection.cpp b/src/frontend/displaywindow/btactioncollection.cpp index 046c3c0..fc1a412 100644 --- a/src/frontend/displaywindow/btactioncollection.cpp +++ b/src/frontend/displaywindow/btactioncollection.cpp @@ -15,6 +15,7 @@ #include <QSettings> #include <QString> #include <QStringList> +#include <QDebug> class BtActionItem : public QObject { @@ -52,10 +53,10 @@ QList<QAction*> BtActionCollection::actions() QAction* BtActionCollection::action(const QString& name) { - Q_ASSERT(m_actions[name] != 0); - QAction* action = m_actions[name]->action; - Q_ASSERT(action != 0); - return action; + if (m_actions.contains(name)) + return m_actions[name]->action; + qWarning() << "A QAction for a shortcut named" << name << "was requested but it is not defined."; + return (new QAction(this)); // dummy QAction* } QAction* BtActionCollection::addAction(const QString& name, QAction* action) diff --git a/src/frontend/displaywindow/cbookreadwindow.cpp b/src/frontend/displaywindow/cbookreadwindow.cpp index 40f737d..465eec9 100644 --- a/src/frontend/displaywindow/cbookreadwindow.cpp +++ b/src/frontend/displaywindow/cbookreadwindow.cpp @@ -100,6 +100,9 @@ void CBookReadWindow::initView() QSplitter* splitter = new QSplitter(this); setMainToolBar( new QToolBar(this) ); + mainToolBar()->setAllowedAreas(Qt::TopToolBarArea); + mainToolBar()->setFloatable(false); + addToolBar(mainToolBar()); m_treeChooser = new CBookTreeChooser(modules(), key(), splitter); @@ -112,6 +115,8 @@ void CBookReadWindow::initView() addToolBar(moduleChooserBar()); setButtonsToolBar( new QToolBar(this) ); + buttonsToolBar()->setAllowedAreas(Qt::TopToolBarArea); + buttonsToolBar()->setFloatable(false); setDisplaySettingsButton( new CDisplaySettingsButton( &displayOptions(), &filterOptions(), modules(), buttonsToolBar()) ); addToolBar(buttonsToolBar()); m_treeChooser->hide(); diff --git a/src/frontend/displaywindow/cdisplaywindow.cpp b/src/frontend/displaywindow/cdisplaywindow.cpp index 6cf160d..8b4a9cd 100644 --- a/src/frontend/displaywindow/cdisplaywindow.cpp +++ b/src/frontend/displaywindow/cdisplaywindow.cpp @@ -129,6 +129,15 @@ void CDisplayWindow::insertKeyboardActions( BtActionCollection* a ) actn->setShortcut(QKeySequence::Find); a->addAction("findText", actn); + actn = new QAction(QIcon(), tr("Change location"), 0); + actn->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_L)); + a->addAction("openLocation", actn); + + actn = new QAction(QIcon(util::filesystem::DirectoryUtil::getIcon(CResMgr::displaywindows::general::search::icon)), + tr("Search with works of this window"), 0); + actn->setShortcut(CResMgr::displaywindows::general::search::accel); + a->addAction(CResMgr::displaywindows::general::search::actionName, actn); + BtToolBarPopupAction* action = new BtToolBarPopupAction( QIcon(util::filesystem::DirectoryUtil::getIcon(CResMgr::displaywindows::general::backInHistory::icon)), tr("Back in history"), @@ -154,18 +163,16 @@ void CDisplayWindow::initActions() CDisplayWindow::insertKeyboardActions(ac); - QAction* qaction = new QAction( - QIcon(util::filesystem::DirectoryUtil::getIcon(CResMgr::displaywindows::general::search::icon)), - tr("Open the search dialog with the works of this window"), - ac - ); - qaction->setShortcut(CResMgr::displaywindows::general::search::accel); - QObject::connect(qaction, SIGNAL(triggered()), this, SLOT(slotSearchInModules())); - ac->addAction(CResMgr::displaywindows::general::search::actionName, qaction); + QAction* actn = ac->action(CResMgr::displaywindows::general::search::actionName); + QObject::connect(actn, SIGNAL(triggered()), this, SLOT(slotSearchInModules())); CDisplayConnections* conn = displayWidget()->connectionsProxy(); - QAction* actn = ac->action("zoomIn"); + actn = ac->action("openLocation"); + QObject::connect(actn, SIGNAL(triggered()), this, SLOT(setFocusKeyChooser())); + addAction(actn); + + actn = ac->action("zoomIn"); QObject::connect(actn, SIGNAL(triggered()), conn, SLOT(zoomIn())); addAction(actn); @@ -536,3 +543,9 @@ BtActionCollection* CDisplayWindow::actionCollection() { return m_actionCollection; } + +void CDisplayWindow::setFocusKeyChooser() +{ + keyChooser()->setFocus(); +} + diff --git a/src/frontend/displaywindow/cdisplaywindow.h b/src/frontend/displaywindow/cdisplaywindow.h index 80877fb..733f06e 100644 --- a/src/frontend/displaywindow/cdisplaywindow.h +++ b/src/frontend/displaywindow/cdisplaywindow.h @@ -202,6 +202,7 @@ protected slots: void printAnchorWithText(); + void setFocusKeyChooser(); private: BtActionCollection* m_actionCollection; diff --git a/src/frontend/displaywindow/chtmlwritewindow.cpp b/src/frontend/displaywindow/chtmlwritewindow.cpp index 7e97aa6..cee16e0 100644 --- a/src/frontend/displaywindow/chtmlwritewindow.cpp +++ b/src/frontend/displaywindow/chtmlwritewindow.cpp @@ -42,6 +42,8 @@ void CHTMLWriteWindow::initView() { setCentralWidget( displayWidget()->view() ); setMainToolBar( new QToolBar(this) ); + mainToolBar()->setAllowedAreas(Qt::TopToolBarArea); + mainToolBar()->setFloatable(false); addToolBar(mainToolBar()); setKeyChooser( CKeyChooser::createInstance(modules(), key(), mainToolBar()) ); @@ -104,6 +106,8 @@ void CHTMLWriteWindow::initToolbars() { //html formatting toolbar QToolBar* bar = new QToolBar(this); + bar->setAllowedAreas(Qt::TopToolBarArea); + bar->setFloatable(false); ((CWriteDisplay*)displayWidget())->setupToolbar( bar, actionCollection() ); addToolBar(bar); } diff --git a/src/frontend/displaywindow/clexiconreadwindow.cpp b/src/frontend/displaywindow/clexiconreadwindow.cpp index 8ceb326..df22de9 100644 --- a/src/frontend/displaywindow/clexiconreadwindow.cpp +++ b/src/frontend/displaywindow/clexiconreadwindow.cpp @@ -187,6 +187,8 @@ void CLexiconReadWindow::initView() qDebug("CLexiconReadWindow::initView"); setDisplayWidget( CDisplay::createReadInstance(this) ); setMainToolBar( new QToolBar(this) ); + mainToolBar()->setAllowedAreas(Qt::TopToolBarArea); + mainToolBar()->setFloatable(false); addToolBar(mainToolBar()); setKeyChooser( CKeyChooser::createInstance(modules(), key(), mainToolBar()) ); mainToolBar()->addWidget(keyChooser()); @@ -194,6 +196,8 @@ void CLexiconReadWindow::initView() moduleChooserBar()->adjustSize(); addToolBar(moduleChooserBar()); setButtonsToolBar( new QToolBar(this) ); + buttonsToolBar()->setAllowedAreas(Qt::TopToolBarArea); + buttonsToolBar()->setFloatable(false); addToolBar(buttonsToolBar()); setWindowIcon(CToolClass::getIconForModule(modules().first())); setCentralWidget( displayWidget()->view() ); diff --git a/src/frontend/displaywindow/cmodulechooserbar.cpp b/src/frontend/displaywindow/cmodulechooserbar.cpp index fc891ad..a282c4b 100644 --- a/src/frontend/displaywindow/cmodulechooserbar.cpp +++ b/src/frontend/displaywindow/cmodulechooserbar.cpp @@ -22,6 +22,8 @@ CModuleChooserBar::CModuleChooserBar(QList<CSwordModuleInfo*> useModules, CSword m_idCounter(0), m_buttonLimit(-1) //-1 means no limit { + setAllowedAreas(Qt::TopToolBarArea); + setFloatable(false); //insert buttons if useModules != 0 QList<CSwordModuleInfo*>::iterator end_it = useModules.end(); for (QList<CSwordModuleInfo*>::iterator it(useModules.begin()); it != end_it; ++it) { diff --git a/src/frontend/displaywindow/cplainwritewindow.cpp b/src/frontend/displaywindow/cplainwritewindow.cpp index 9f12020..71dc603 100644 --- a/src/frontend/displaywindow/cplainwritewindow.cpp +++ b/src/frontend/displaywindow/cplainwritewindow.cpp @@ -45,6 +45,8 @@ void CPlainWriteWindow::initView() { setCentralWidget( displayWidget()->view() ); setMainToolBar( new QToolBar(this) ); + mainToolBar()->setAllowedAreas(Qt::TopToolBarArea); + mainToolBar()->setFloatable(false); addToolBar(mainToolBar()); addToolBarBreak(); diff --git a/src/frontend/keychooser/ckeychooserwidget.cpp b/src/frontend/keychooser/ckeychooserwidget.cpp index dd8f7a5..d3e1a55 100644 --- a/src/frontend/keychooser/ckeychooserwidget.cpp +++ b/src/frontend/keychooser/ckeychooserwidget.cpp @@ -22,20 +22,46 @@ #include <QLineEdit> #include <QDebug> +class BtKeyLineEdit : public QLineEdit +{ +public: + BtKeyLineEdit(QWidget* parent) + : QLineEdit(parent) + { + } +protected: + void focusInEvent(QFocusEvent* event) + { + Qt::FocusReason reason = event->reason(); + if (reason == Qt::OtherFocusReason) + { + selectAll(); + } + QWidget::focusInEvent(event); + } +}; + + + CKCComboBox::CKCComboBox() -: QComboBox() { +: QComboBox() +{ setFocusPolicy(Qt::WheelFocus); + setLineEdit(new BtKeyLineEdit(this)); if (lineEdit()) { installEventFilter( lineEdit() ); } } /** Reimplementation. */ -bool CKCComboBox::eventFilter( QObject *o, QEvent *e ) { - if (e->type() == QEvent::FocusOut) { +bool CKCComboBox::eventFilter( QObject *o, QEvent *e ) +{ + if (e->type() == QEvent::FocusOut) + { QFocusEvent* f = static_cast<QFocusEvent*>(e); - if (o == lineEdit() && f->reason() == Qt::TabFocusReason) { + if (o == lineEdit() && f->reason() == Qt::TabFocusReason) + { int index = findText(currentText()); if (index == -1) { index = 0;// return 0 if not found @@ -45,18 +71,22 @@ bool CKCComboBox::eventFilter( QObject *o, QEvent *e ) { return false; } - else if (f->reason() == Qt::PopupFocusReason) { + else if (f->reason() == Qt::PopupFocusReason) + { return false; } - else if (f->reason() == Qt::ActiveWindowFocusReason) { + else if (f->reason() == Qt::ActiveWindowFocusReason) + { emit activated(currentText()); return false; } - else if (f->reason() == Qt::MouseFocusReason) { + else if (f->reason() == Qt::MouseFocusReason) + { emit activated(currentText()); return false; } - else if (o == this) { + else if (o == this) + { emit activated(currentText()); return false; } @@ -66,13 +96,15 @@ bool CKCComboBox::eventFilter( QObject *o, QEvent *e ) { } /** Scrolls in the list if the wheel of the mouse was used. */ -void CKCComboBox::wheelEvent( QWheelEvent* e ) { +void CKCComboBox::wheelEvent( QWheelEvent* e ) +{ return QComboBox::wheelEvent(e); const signed int change = (int)((float)e->delta()/(float)120); int current = currentIndex(); - if ((current+change >= 0) && (current+change<count()) ) { + if ((current+change >= 0) && (current+change<count()) ) + { setCurrentIndex(current+change); e->accept(); emit activated( currentIndex() ); @@ -84,23 +116,28 @@ void CKCComboBox::wheelEvent( QWheelEvent* e ) { //**********************************************************************************/ -CKeyChooserWidget::CKeyChooserWidget(int count, const bool useNextPrevSignals, QWidget *parent ) : QWidget(parent) { +CKeyChooserWidget::CKeyChooserWidget(int count, const bool useNextPrevSignals, QWidget *parent ) : QWidget(parent) +{ m_useNextPrevSignals = useNextPrevSignals; - for (int index=1; index <= count; index++) { + for (int index=1; index <= count; index++) + { m_list.append( QString::number(index) ); } init(); reset(m_list,0,false); } -CKeyChooserWidget::CKeyChooserWidget(QStringList *list, const bool useNextPrevSignals, QWidget *parent ) : QWidget(parent) { +CKeyChooserWidget::CKeyChooserWidget(QStringList *list, const bool useNextPrevSignals, QWidget *parent ) : QWidget(parent) +{ m_useNextPrevSignals = useNextPrevSignals; - if (list) { + if (list) + { m_list = *list; //deep copy the items of list } - else { + else + { m_list.clear(); } @@ -108,21 +145,24 @@ CKeyChooserWidget::CKeyChooserWidget(QStringList *list, const bool useNextPrevSi reset(m_list,0,false); } -void CKeyChooserWidget::reset(const int count, int index, bool do_emit) { +void CKeyChooserWidget::reset(const int count, int index, bool do_emit) +{ //This prevents the widget from resetting during application load, which //produces undesirable behavior. //if (!updatesEnabled()) // return; m_list.clear(); - for (int i=1; i <= count; i++) { //TODO: CHECK + for (int i=1; i <= count; i++) + { //TODO: CHECK m_list.append( QString::number(i) ); } reset(&m_list,index,do_emit); } -void CKeyChooserWidget::reset(QStringList& list, int index, bool do_emit) { +void CKeyChooserWidget::reset(QStringList& list, int index, bool do_emit) +{ //This prevents the widget from resetting during application load, which //produces undesirable behavior. //if (!updatesEnabled()) @@ -133,7 +173,8 @@ void CKeyChooserWidget::reset(QStringList& list, int index, bool do_emit) { } -void CKeyChooserWidget::reset(QStringList *list, int index, bool do_emit) { +void CKeyChooserWidget::reset(QStringList *list, int index, bool do_emit) +{ //if (isResetting || !updatesEnabled()) if (isResetting) return; @@ -147,18 +188,21 @@ void CKeyChooserWidget::reset(QStringList *list, int index, bool do_emit) { //DON'T REMOVE THE HIDE: Otherwise QComboBox's sizeHint() function won't work properly m_comboBox->hide(); m_comboBox->clear(); - if (list) { + if (list) + { m_comboBox->insertItems(-1, *list); } if (!list || (list && !list->count())) { //nothing in the combobox setEnabled(false); } - else if (!isEnabled()) { //was disabled + else if (!isEnabled()) + { //was disabled setEnabled(true); } - if (list->count()) { + if (list->count()) + { m_comboBox->setCurrentIndex(index); } if (do_emit) { @@ -177,13 +221,15 @@ void CKeyChooserWidget::reset(QStringList *list, int index, bool do_emit) { } /** Initializes this widget. We need this function because we have more than one constructor. */ -void CKeyChooserWidget::init() { +void CKeyChooserWidget::init() +{ qDebug("CKeyChooserWidget::init"); oldKey = QString::null; setFocusPolicy(Qt::WheelFocus); m_comboBox = new CKCComboBox(); + setFocusProxy(m_comboBox); m_comboBox->setAutoCompletion( true ); m_comboBox->setEditable(true); m_comboBox->setInsertPolicy(QComboBox::NoInsert); @@ -200,6 +246,7 @@ void CKeyChooserWidget::init() { m_mainLayout->addSpacing(0); setTabOrder(m_comboBox, 0); + setFocusProxy(m_comboBox); connect(m_scroller, SIGNAL(scroller_pressed()), SLOT(lock())); connect(m_scroller, SIGNAL(scroller_released()), SLOT(unlock())); @@ -215,13 +262,16 @@ void CKeyChooserWidget::init() { } /** Is called when the return key was presed in the combobox. */ -void CKeyChooserWidget::slotReturnPressed( /*const QString& text*/) { +void CKeyChooserWidget::slotReturnPressed( /*const QString& text*/) +{ Q_ASSERT(comboBox()->lineEdit()); qDebug("return pressed"); QString text = comboBox()->lineEdit()->text(); - for (int index = 0; index < comboBox()->count(); ++index) { - if (comboBox()->itemText(index) == text) { + for (int index = 0; index < comboBox()->count(); ++index) + { + if (comboBox()->itemText(index) == text) + { // emit changed(index); emit focusOut(index); // a workaround because focusOut is not checked, the slot connected to changed to check break; @@ -230,16 +280,19 @@ void CKeyChooserWidget::slotReturnPressed( /*const QString& text*/) { } /** Is called when the current item of the combo box was changed. */ -void CKeyChooserWidget::slotComboChanged(int index) { +void CKeyChooserWidget::slotComboChanged(int index) +{ qDebug("CKeyChooserWidget::slotComboChanged(int index)"); - if (!updatesEnabled()) { + if (!updatesEnabled()) + { return; } setUpdatesEnabled(false); const QString key = comboBox()->itemText( index ); - if (oldKey.isNull() || (oldKey != key)) { + if (oldKey.isNull() || (oldKey != key)) + { emit changed(index); } @@ -249,17 +302,21 @@ void CKeyChooserWidget::slotComboChanged(int index) { } /** Sets the tooltips for the given entries using the parameters as text. */ -void CKeyChooserWidget::setToolTips( const QString comboTip, const QString nextEntryTip, const QString scrollButtonTip, const QString previousEntryTip) { +void CKeyChooserWidget::setToolTips( const QString comboTip, const QString nextEntryTip, const QString scrollButtonTip, const QString previousEntryTip) +{ comboBox()->setToolTip(comboTip); m_scroller->setToolTips(nextEntryTip, scrollButtonTip, previousEntryTip); } /** Sets the current item to the one with the given text */ -bool CKeyChooserWidget::setItem( const QString item ) { +bool CKeyChooserWidget::setItem( const QString item ) +{ bool ret = false; const int count = comboBox()->count(); - for (int i = 0; i < count; ++i) { - if (comboBox()->itemText(i) == item) { + for (int i = 0; i < count; ++i) + { + if (comboBox()->itemText(i) == item) + { comboBox()->setCurrentIndex(i); ret = true; break; @@ -271,22 +328,26 @@ bool CKeyChooserWidget::setItem( const QString item ) { } /* Handlers for the various scroller widgetset. */ -void CKeyChooserWidget::lock() { +void CKeyChooserWidget::lock() +{ updatelock = true; comboBox()->setEditable(false); oldKey = comboBox()->currentText(); } -void CKeyChooserWidget::unlock() { +void CKeyChooserWidget::unlock() +{ updatelock = false; comboBox()->setEditable(true); comboBox()->setEditText(comboBox()->itemText(comboBox()->currentIndex())); - if (comboBox()->currentText() != oldKey) { + if (comboBox()->currentText() != oldKey) + { emit changed(comboBox()->currentIndex()); } } -void CKeyChooserWidget::changeCombo(int n) { +void CKeyChooserWidget::changeCombo(int n) +{ const int old_index = comboBox()->currentIndex(); int new_index = old_index + n; @@ -295,7 +356,8 @@ void CKeyChooserWidget::changeCombo(int n) { if(new_index > max) new_index = max; if(new_index < 0) new_index = 0; - if(new_index != old_index) { + if(new_index != old_index) + { comboBox()->setCurrentIndex(new_index); if(!updatelock) emit changed(new_index); diff --git a/src/frontend/keychooser/clexiconkeychooser.cpp b/src/frontend/keychooser/clexiconkeychooser.cpp index 00c02ba..53d8ac0 100644 --- a/src/frontend/keychooser/clexiconkeychooser.cpp +++ b/src/frontend/keychooser/clexiconkeychooser.cpp @@ -42,6 +42,7 @@ CLexiconKeyChooser::CLexiconKeyChooser(QList<CSwordModuleInfo*> modules, CSwordK m_layout->setSizeConstraint(QLayout::SetNoConstraint); m_widget = new CKeyChooserWidget(0, false, this); + setFocusProxy(m_widget); //don't allow a too high width, try to keep as narrow as possible //to aid users with smaller screen resolutions diff --git a/src/frontend/keychooser/versekeychooser/cbiblekeychooser.cpp b/src/frontend/keychooser/versekeychooser/cbiblekeychooser.cpp index 2bc9e77..afe3482 100644 --- a/src/frontend/keychooser/versekeychooser/cbiblekeychooser.cpp +++ b/src/frontend/keychooser/versekeychooser/cbiblekeychooser.cpp @@ -40,6 +40,7 @@ CBibleKeyChooser::CBibleKeyChooser(QList<CSwordModuleInfo*> modules, CSwordKey * layout->setDirection( QBoxLayout::LeftToRight ); w_ref = new CKeyReferenceWidget(dynamic_cast<CSwordBibleModuleInfo*>(m_modules.first()), m_key, this); + setFocusProxy(w_ref); layout->addWidget(w_ref); connect(w_ref,SIGNAL(changed(CSwordVerseKey *)),SLOT(refChanged(CSwordVerseKey *))); diff --git a/src/frontend/keychooser/versekeychooser/ckeyreferencewidget.cpp b/src/frontend/keychooser/versekeychooser/ckeyreferencewidget.cpp index 11c5ddc..cedb4b3 100644 --- a/src/frontend/keychooser/versekeychooser/ckeyreferencewidget.cpp +++ b/src/frontend/keychooser/versekeychooser/ckeyreferencewidget.cpp @@ -31,12 +31,32 @@ #include <QString> #include <QStringList> #include <QToolButton> +#include <QFocusEvent> +class BtLineEdit : public QLineEdit +{ +public: + BtLineEdit(QWidget* parent) + : QLineEdit(parent) + { + } +protected: + void focusInEvent(QFocusEvent* event) + { + Qt::FocusReason reason = event->reason(); + if (reason == Qt::OtherFocusReason) + { + selectAll(); + } + QWidget::focusInEvent(event); + } +}; -CKeyReferenceWidget::CKeyReferenceWidget( CSwordBibleModuleInfo *mod, CSwordVerseKey *key, QWidget *parent, const char* /*name*/) : + +CKeyReferenceWidget::CKeyReferenceWidget( CSwordBibleModuleInfo *mod, CSwordVerseKey *key, QWidget *parent, const char* /*name*/) : QWidget(parent), - m_key(new CSwordVerseKey(mod)), + m_key(key), m_dropDownHoverTimer(this) { @@ -53,7 +73,8 @@ CKeyReferenceWidget::CKeyReferenceWidget( CSwordBibleModuleInfo *mod, CSwordVers m_bookScroller = new CScrollerWidgetSet(this); - m_textbox = new QLineEdit( this ); + m_textbox = new BtLineEdit( this ); + setFocusProxy(m_textbox); m_textbox->setContentsMargins(0, 0, 0, 0); setKey(key); // The order of these two functions is important. @@ -127,7 +148,8 @@ CKeyReferenceWidget::CKeyReferenceWidget( CSwordBibleModuleInfo *mod, CSwordVers connect(m_verseScroller, SIGNAL(scroller_released()), SLOT(slotUpdateUnlock())); } -CKeyReferenceWidget::~CKeyReferenceWidget() { +CKeyReferenceWidget::~CKeyReferenceWidget() +{ delete m_dropDownButtons; } @@ -140,7 +162,8 @@ void CKeyReferenceWidget::setModule(CSwordBibleModuleInfo *m) } } -bool CKeyReferenceWidget::eventFilter(QObject *o, QEvent *e) { +bool CKeyReferenceWidget::eventFilter(QObject *o, QEvent *e) +{ if (o != m_dropDownButtons) return false; switch (e->type()) { case QEvent::Enter: @@ -154,7 +177,8 @@ bool CKeyReferenceWidget::eventFilter(QObject *o, QEvent *e) { } } -void CKeyReferenceWidget::enterEvent(QEvent *) { +void CKeyReferenceWidget::enterEvent(QEvent *) +{ m_dropDownHoverTimer.stop(); resetDropDownButtons(); @@ -163,18 +187,22 @@ void CKeyReferenceWidget::enterEvent(QEvent *) { m_dropDownButtons->show(); } -void CKeyReferenceWidget::leaveEvent(QEvent *) { +void CKeyReferenceWidget::leaveEvent(QEvent *) +{ m_dropDownHoverTimer.start(); } -void CKeyReferenceWidget::resizeEvent(QResizeEvent *event) { - if (m_dropDownButtons->isVisible()) { +void CKeyReferenceWidget::resizeEvent(QResizeEvent *event) +{ + if (m_dropDownButtons->isVisible()) + { resetDropDownButtons(); } QWidget::resizeEvent(event); } -void CKeyReferenceWidget::resetDropDownButtons() { +void CKeyReferenceWidget::resetDropDownButtons() +{ m_dropDownButtons->setParent(window()); int h(m_dropDownButtons->layout()->minimumSize().height()); QPoint topLeft(mapTo(window(), QPoint(m_textbox->x(), height()))); @@ -203,7 +231,7 @@ void CKeyReferenceWidget::updateText() bool CKeyReferenceWidget::setKey(CSwordVerseKey *key) { if (!key) return false; - + m_key->key(key->key()); updateText(); return true; @@ -218,8 +246,7 @@ void CKeyReferenceWidget::slotReturnPressed() { m_key->key(m_textbox->text()); updateText(); - - emit changed(m_key.get()); + emit changed(m_key); } /* Handlers for the various scroller widgetsets. Do we really want a verse scroller? */ @@ -232,58 +259,70 @@ void CKeyReferenceWidget::slotUpdateLock() void CKeyReferenceWidget::slotUpdateUnlock() { updatelock = false; - if (oldKey != m_key->key()) emit changed(m_key.get()); + if (oldKey != m_key->key()) + emit changed(m_key); } void CKeyReferenceWidget::slotStepBook(int n) { - n > 0 ? m_key->next( CSwordVerseKey::UseBook ) : m_key->previous( CSwordVerseKey::UseBook ); + CSwordVerseKey key = *m_key; + n > 0 ? key.next( CSwordVerseKey::UseBook ) : key.previous( CSwordVerseKey::UseBook ); + if (!updatelock) + emit changed(&key); // does *m_key = key updateText(); - if (!updatelock) emit changed(m_key.get()); } void CKeyReferenceWidget::slotStepChapter(int n) { - n > 0 ? m_key->next( CSwordVerseKey::UseChapter ) : m_key->previous( CSwordVerseKey::UseChapter ); - updateText(); - if (!updatelock) emit changed(m_key.get()); + CSwordVerseKey key = *m_key; + n > 0 ? key.next( CSwordVerseKey::UseChapter ) : key.previous( CSwordVerseKey::UseChapter ); + if (!updatelock) + emit changed(&key); // does *m_key = key + updateText(); } void CKeyReferenceWidget::slotStepVerse(int n) { - n > 0 ? m_key->next( CSwordVerseKey::UseVerse ) : m_key->previous( CSwordVerseKey::UseVerse ); + CSwordVerseKey key = *m_key; + n > 0 ? key.next( CSwordVerseKey::UseVerse ) : key.previous( CSwordVerseKey::UseVerse ); + if (!updatelock) + emit changed(&key); // does *m_key = key updateText(); - if (!updatelock) emit changed(m_key.get()); } void CKeyReferenceWidget::slotChangeVerse(int n) { - if (m_key->Verse() != n) { + if (m_key->Verse() != n) + { m_key->Verse( n ); - setKey( m_key.get() ); + setKey( m_key ); } updateText(); - if (!updatelock) emit changed(m_key.get()); + if (!updatelock) emit changed(m_key); } void CKeyReferenceWidget::slotChangeChapter(int n) { - if (m_key->Chapter() != n) { + if (m_key->Chapter() != n) + { m_key->Chapter( n ); - setKey( m_key.get() ); + setKey( m_key ); } updateText(); - if (!updatelock) emit changed(m_key.get()); + if (!updatelock) + emit changed(m_key); } void CKeyReferenceWidget::slotChangeBook(QString bookname) { - if (m_key->book() != bookname) { + if (m_key->book() != bookname) + { m_key->book( bookname ); - setKey( m_key.get() ); + setKey( m_key ); } updateText(); - if (!updatelock) emit changed(m_key.get()); + if (!updatelock) + emit changed(m_key); } diff --git a/src/frontend/keychooser/versekeychooser/ckeyreferencewidget.h b/src/frontend/keychooser/versekeychooser/ckeyreferencewidget.h index 0ecb7a9..0324dbe 100644 --- a/src/frontend/keychooser/versekeychooser/ckeyreferencewidget.h +++ b/src/frontend/keychooser/versekeychooser/ckeyreferencewidget.h @@ -14,8 +14,6 @@ #include "backend/drivers/cswordbiblemoduleinfo.h" #include <QWidget> - -#include <boost/scoped_ptr.hpp> #include <QTimer> @@ -73,7 +71,7 @@ private: friend class BtChapterDropdownChooserButton; friend class BtVerseDropdownChooserButton; - boost::scoped_ptr<CSwordVerseKey> m_key; + CSwordVerseKey *m_key; QLineEdit* m_textbox; diff --git a/src/frontend/mainindex/bookmarks/cbookmarkindex.h b/src/frontend/mainindex/bookmarks/cbookmarkindex.h index 39e2df3..340fabc 100644 --- a/src/frontend/mainindex/bookmarks/cbookmarkindex.h +++ b/src/frontend/mainindex/bookmarks/cbookmarkindex.h @@ -26,7 +26,6 @@ class CSwordModuleInfo; #include <QTreeWidgetItem> class CSearchDialog; -class CMainIndex; class QWidget; class QDropEvent; class QDragMoveEvent; diff --git a/src/frontend/mainindex/bookshelf/btindexitem.h b/src/frontend/mainindex/bookshelf/btindexitem.h index 33d2f7b..d9e74ad 100644 --- a/src/frontend/mainindex/bookshelf/btindexitem.h +++ b/src/frontend/mainindex/bookshelf/btindexitem.h @@ -15,7 +15,6 @@ #include <QTreeWidgetItem> #include <QString> -class CMainIndex; class QMimeData; class QAction; diff --git a/src/frontend/mainindex/bookshelf/cbookshelfindex.h b/src/frontend/mainindex/bookshelf/cbookshelfindex.h index f9e56e7..bd21069 100644 --- a/src/frontend/mainindex/bookshelf/cbookshelfindex.h +++ b/src/frontend/mainindex/bookshelf/cbookshelfindex.h @@ -27,7 +27,6 @@ class CSwordModuleInfo; class CSearchDialog; -class CMainIndex; class QWidget; class QDropEvent; class QDragMoveEvent; diff --git a/src/frontend/mainindex/btbookshelfview.cpp b/src/frontend/mainindex/btbookshelfview.cpp new file mode 100644 index 0000000..52d0ec4 --- /dev/null +++ b/src/frontend/mainindex/btbookshelfview.cpp @@ -0,0 +1,105 @@ +/********* +* +* In the name of the Father, and of the Son, and of the Holy Spirit. +* +* This file is part of BibleTime's source code, http://www.bibletime.info/. +* +* Copyright 1999-2009 by the BibleTime developers. +* The BibleTime source code is licensed under the GNU General Public License +* version 2.0. +* +**********/ + +#include "frontend/mainindex/btbookshelfview.h" + +#include <QApplication> +#include <QHeaderView> +#include <QMouseEvent> +#include "backend/bookshelfmodel/btbookshelftreemodel.h" +#include "backend/drivers/cswordmoduleinfo.h" + +BtBookshelfView::BtBookshelfView(QWidget *parent) + : QTreeView(parent) +{ + header()->hide(); + + /* + Uncommenting the following statement will hide the [+]expand/[-]collapse + controls in front of first-level items, which might not be desirable since + hiding them also means that one has to do a total of 2 clicks (double- + click)to expand or collapse top-level items: + */ + // setRootIsDecorated(false); + + connect(this, SIGNAL(activated(QModelIndex)), + this, SLOT(slotItemActivated(QModelIndex))); +} + +BtBookshelfView::~BtBookshelfView() { + // Intentionally empty +} + +CSwordModuleInfo *BtBookshelfView::getModule(const QModelIndex &index) const { + return (CSwordModuleInfo *) model() + ->data(index, BtBookshelfModel::ModulePointerRole).value<void*>(); +} + +void BtBookshelfView::keyPressEvent(QKeyEvent *event) { + switch (event->key()) { + case Qt::Key_Menu: + scrollTo(currentIndex()); + { + CSwordModuleInfo *i(getModule(currentIndex())); + QRect itemRect(visualRect(currentIndex())); + QPoint p(viewport()->mapToGlobal(itemRect.bottomLeft())); + if (i == 0) { + emit contextMenuActivated(p); + } else { + emit moduleContextMenuActivated(i, p); + } + } + event->accept(); + break; + case Qt::Key_Return: + case Qt::Key_Enter: + { + QModelIndex i(currentIndex()); + CSwordModuleInfo *m(getModule(i)); + if (m != 0) { + emit moduleActivated(m); + } else { + setExpanded(i, !isExpanded(i)); + } + } + event->accept(); + break; + default: + QTreeView::keyPressEvent(event); + break; + } +} + +void BtBookshelfView::mousePressEvent(QMouseEvent *event) { + if (event->buttons() == Qt::RightButton) { + QModelIndex clickedItemIndex(indexAt(event->pos())); + if (clickedItemIndex.isValid()) { + setCurrentIndex(clickedItemIndex); + } + CSwordModuleInfo *i(getModule(clickedItemIndex)); + if (i == 0) { + emit contextMenuActivated(mapToGlobal(event->pos())); + } else { + emit moduleContextMenuActivated(i, mapToGlobal(event->pos())); + } + event->accept(); + } else { + QTreeView::mousePressEvent(event); + } +} + +void BtBookshelfView::slotItemActivated(const QModelIndex &index) { + CSwordModuleInfo *i(getModule(index)); + if (i != 0) { + emit moduleActivated(i); + } +} diff --git a/src/frontend/mainindex/btbookshelfview.h b/src/frontend/mainindex/btbookshelfview.h new file mode 100644 index 0000000..80c7f31 --- /dev/null +++ b/src/frontend/mainindex/btbookshelfview.h @@ -0,0 +1,42 @@ +/********* +* +* In the name of the Father, and of the Son, and of the Holy Spirit. +* +* This file is part of BibleTime's source code, http://www.bibletime.info/. +* +* Copyright 1999-2009 by the BibleTime developers. +* The BibleTime source code is licensed under the GNU General Public License +* version 2.0. +* +**********/ + +#ifndef BTBOOKSHELFVIEW_H +#define BTBOOKSHELFVIEW_H + +#include <QTreeView> + +class CSwordModuleInfo; + +class BtBookshelfView: public QTreeView { + Q_OBJECT + public: + BtBookshelfView(QWidget *parent = 0); + virtual ~BtBookshelfView(); + + CSwordModuleInfo *getModule(const QModelIndex &index) const; + + signals: + void contextMenuActivated(QPoint pos); + void moduleContextMenuActivated(CSwordModuleInfo *item, + QPoint pos); + void moduleActivated(CSwordModuleInfo *item); + + protected: + void keyPressEvent(QKeyEvent *event); + void mousePressEvent(QMouseEvent *event); + + protected slots: + void slotItemActivated(const QModelIndex &index); +}; + +#endif // BTBOOKSHELFVIEW_H diff --git a/src/frontend/mainindex/cmainindex.cpp b/src/frontend/mainindex/cmainindex.cpp deleted file mode 100644 index 04341b0..0000000 --- a/src/frontend/mainindex/cmainindex.cpp +++ /dev/null @@ -1,36 +0,0 @@ -/********* -* -* This file is part of BibleTime's source code, http://www.bibletime.info/. -* -* Copyright 1999-2008 by the BibleTime developers. -* The BibleTime source code is licensed under the GNU General Public License version 2.0. -* -**********/ - -//BibleTime includes -#include "cmainindex.h" - -#include "backend/drivers/cswordmoduleinfo.h" - -#include "bookshelf/cbookshelfindex.h" -#include "bookmarks/cbookmarkindex.h" - -#include <QTabWidget> - - -CMainIndex::CMainIndex(QWidget *parent) : QTabWidget(parent) -{ - setFocusPolicy(Qt::StrongFocus); - m_bookmarksPage = new CBookmarkIndex(0); - m_bookshelfPage = new CBookshelfIndex(0); - addTab(m_bookshelfPage, tr("Bookshelf")); - addTab(m_bookmarksPage, tr("Bookmarks")); - - //shortcut some signals from pages to signals of this widget so that outsiders - // do not have to use the pages directly - QObject::connect(m_bookshelfPage, SIGNAL(createReadDisplayWindow( QList<CSwordModuleInfo*>, const QString& )), this, SIGNAL(createReadDisplayWindow( QList<CSwordModuleInfo*>, const QString& ))); - QObject::connect(m_bookmarksPage, SIGNAL(createReadDisplayWindow( QList<CSwordModuleInfo*>, const QString& )), this, SIGNAL(createReadDisplayWindow( QList<CSwordModuleInfo*>, const QString& ))); - - QObject::connect(m_bookshelfPage, SIGNAL(createWriteDisplayWindow( CSwordModuleInfo*, const QString&, const CDisplayWindow::WriteWindowType& )), this, SIGNAL(createWriteDisplayWindow( CSwordModuleInfo*, const QString&, const CDisplayWindow::WriteWindowType&))); - -} diff --git a/src/frontend/mainindex/cmainindex.h b/src/frontend/mainindex/cmainindex.h deleted file mode 100644 index b5d217d..0000000 --- a/src/frontend/mainindex/cmainindex.h +++ /dev/null @@ -1,57 +0,0 @@ -/********* -* -* This file is part of BibleTime's source code, http://www.bibletime.info/. -* -* Copyright 1999-2008 by the BibleTime developers. -* The BibleTime source code is licensed under the GNU General Public License version 2.0. -* -**********/ - -#ifndef CMAININDEX_H -#define CMAININDEX_H - -#include "frontend/displaywindow/cdisplaywindow.h" - -#include <QTabWidget> -#include <QFocusEvent> - -class CBookmarkIndex; -class CBookshelfIndex; -class CSwordModuleInfo; - -/** The class which manages all bookmarks and modules. The modules are put into own, fixed subfolders sorted by language. - * @author The BibleTime team - */ -class CMainIndex : public QTabWidget { - Q_OBJECT - -public: - CMainIndex(QWidget *parent); - virtual ~CMainIndex() {}; - - //void reloadSword(); - - CBookshelfIndex* bookshelfIndex() {return m_bookshelfPage;} - -signals: - /** - * Is emitted when a module should be opened, - */ - void createReadDisplayWindow( QList<CSwordModuleInfo*>, const QString& ); - /** - * Is emitted when a write window should be created. - */ - void createWriteDisplayWindow( CSwordModuleInfo*, const QString&, const CDisplayWindow::WriteWindowType& ); - -protected: - /** QWidget method - move focus to the active page widget */ - virtual void focusInEvent(QFocusEvent*) {currentWidget()->setFocus();} - -private: - - CBookmarkIndex* m_bookmarksPage; - CBookshelfIndex* m_bookshelfPage; - -}; - -#endif diff --git a/src/main.cpp b/src/main.cpp index bf8aff7..785cc1c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -178,7 +178,7 @@ int main(int argc, char* argv[]) { BibleTime bibleTime; // a new BibleTime version was installed (maybe a completely new installation) - if (CBTConfig::get(CBTConfig::bibletimeVersion) != BT_VERSION) + if (CBTConfig::get(CBTConfig::bibletimeVersion) != BT_VERSION) { CBTConfig::set(CBTConfig::bibletimeVersion, BT_VERSION); bibleTime.saveConfigSettings(); @@ -192,6 +192,7 @@ int main(int argc, char* argv[]) { #ifndef NO_DBUS new BibleTimeDBusAdaptor(&bibleTime); // connect to D-Bus and register as an object: + QDBusConnection::sessionBus().registerService("info.bibletime.BibleTime"); QDBusConnection::sessionBus().registerObject("/BibleTime", &bibleTime); #endif diff --git a/src/util/cresmgr.cpp b/src/util/cresmgr.cpp index 27eb8f4..e0c7573 100644 --- a/src/util/cresmgr.cpp +++ b/src/util/cresmgr.cpp @@ -233,7 +233,7 @@ namespace CResMgr { namespace general { namespace search { const QString icon = "find.svg"; - const QKeySequence accel(Qt::CTRL + Qt::Key_L); + const QKeySequence accel(Qt::CTRL + Qt::Key_N); const char* actionName = "window_search_action"; } |