Git commit 0a92fb453bad60345291d4e585d6266a41c84dae by Jonathan Marten. Committed on 02/05/2014 at 20:14. Pushed by marten into branch 'master'.
KAddressBook: Add a category filter This is a widget which by default appears to the right of the quick search bar. Checking or unchecking categories in this list filters the entries that are shown. FEATURE:332103 REVIEW:117834 FIXED-IN:4.14 GUI: M +2 -0 kaddressbook/CMakeLists.txt A +150 -0 kaddressbook/categoryfilterproxymodel.cpp [License: LGPL (v2+)] A +86 -0 kaddressbook/categoryfilterproxymodel.h [License: LGPL (v2+)] A +316 -0 kaddressbook/categoryselectwidget.cpp [License: LGPL (v2+)] A +87 -0 kaddressbook/categoryselectwidget.h [License: LGPL (v2+)] M +2 -1 kaddressbook/kaddressbookui.rc M +24 -4 kaddressbook/mainwidget.cpp M +5 -0 kaddressbook/mainwidget.h M +2 -1 kaddressbook/settings/kaddressbook.kcfg http://commits.kde.org/kdepim/0a92fb453bad60345291d4e585d6266a41c84dae diff --git a/kaddressbook/CMakeLists.txt b/kaddressbook/CMakeLists.txt index 8c39833..9feea1c 100644 --- a/kaddressbook/CMakeLists.txt +++ b/kaddressbook/CMakeLists.txt @@ -89,6 +89,8 @@ set(kaddressbook_merge_SRCS set(kaddressbook_LIB_SRCS startup.cpp aboutdata.cpp + categoryfilterproxymodel.cpp + categoryselectwidget.cpp contactfields.cpp contactselectiondialog.cpp contactselectionwidget.cpp diff --git a/kaddressbook/categoryfilterproxymodel.cpp b/kaddressbook/categoryfilterproxymodel.cpp new file mode 100644 index 0000000..f006274 --- /dev/null +++ b/kaddressbook/categoryfilterproxymodel.cpp @@ -0,0 +1,150 @@ +/* + Copyright (c) 2014 Jonathan Marten <jjm at keelhaul.me.uk> + + This library is free software; you can redistribute it and/or modify it + under the terms of the GNU Library General Public License as published by + the Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This library is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public + License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. +*/ + +#include "categoryfilterproxymodel.h" + +#include <kdebug.h> +#include <klocale.h> + +#include <akonadi/entitytreemodel.h> +#include <akonadi/item.h> + +#include <kabc/addressee.h> +#include <kabc/contactgroup.h> + +#include "categoryselectwidget.h" + +using namespace Akonadi; + + +class CategoryFilterProxyModelPrivate : public QObject +{ + Q_OBJECT + Q_DECLARE_PUBLIC(CategoryFilterProxyModel); + +public: + CategoryFilterProxyModelPrivate(CategoryFilterProxyModel *parent); + + QList<Tag::Id> filterIdList; + bool filterEnabled; + +private: + CategoryFilterProxyModel *q_ptr; +}; + + +CategoryFilterProxyModelPrivate::CategoryFilterProxyModelPrivate(CategoryFilterProxyModel *parent) + : QObject(), + filterEnabled(false), + q_ptr(parent) +{ +} + + +CategoryFilterProxyModel::CategoryFilterProxyModel(QObject *parent) + : QSortFilterProxyModel(parent), + d_ptr(new CategoryFilterProxyModelPrivate(this)) +{ + setDynamicSortFilter(true); +} + + +CategoryFilterProxyModel::~CategoryFilterProxyModel() +{ + delete d_ptr; +} + + +bool CategoryFilterProxyModel::filterAcceptsRow(int row, const QModelIndex &parent) const +{ + Q_D(const CategoryFilterProxyModel); + + const QModelIndex index = sourceModel()->index(row, 0, parent); + const Akonadi::Item item = index.data(EntityTreeModel::ItemRole).value<Akonadi::Item>(); + + if (!d->filterEnabled) return true; // filter not enabled + if (d->filterIdList.isEmpty()) return false; // nothing accepted + // all accepted + if (d->filterIdList.first()==CategorySelectWidget::FilterAll) return true; + + //kDebug() << "for row" << row << "item" << item.url() << "filter" << d->filterIdList; + if (item.hasPayload<KABC::Addressee>()) { + const KABC::Addressee contact = item.payload<KABC::Addressee>(); + + const QStringList categories = contact.categories(); + //kDebug() << "is contact" << contact.assembledName() << "cats" << categories; + + int validCategories = 0; + int count = categories.count(); + for (int i = 0; i<count; ++i) { + const QString cat = categories.at(i); + if (cat.startsWith(QLatin1String("akonadi:"))) { + const int idx = cat.indexOf(QLatin1String("?tag=")); + if (idx>=0) { + ++validCategories; + Tag::Id id = cat.mid(idx+5).toInt(); + if (d->filterIdList.contains(id)) { + //kDebug() << "matches category" << cat; + return true; // a category matches filter + } + } + } + } + + if (validCategories>0) { + //kDebug() << "valid item but no match"; + return false; // categorised but no match + } else { + //kDebug() << "item with no categories"; + return d->filterIdList.contains(CategorySelectWidget::FilterUntagged); + } + } + else if (item.hasPayload<KABC::ContactGroup>()) { // a contact group item + return d->filterIdList.contains(CategorySelectWidget::FilterGroups); + } + + return true; // not a recognised item +} + + +void CategoryFilterProxyModel::setFilterCategories(const QList<Akonadi::Tag::Id> &idList) +{ + Q_D(CategoryFilterProxyModel); + + if (idList!=d->filterIdList) { + kDebug() << idList; + d->filterIdList = idList; + invalidateFilter(); + } +} + + +void CategoryFilterProxyModel::setFilterEnabled(bool enable) +{ + Q_D(CategoryFilterProxyModel); + + if (enable!=d->filterEnabled) { + kDebug() << enable; + d->filterEnabled = enable; + invalidateFilter(); + } +} + + +#include "categoryfilterproxymodel.moc" diff --git a/kaddressbook/categoryfilterproxymodel.h b/kaddressbook/categoryfilterproxymodel.h new file mode 100644 index 0000000..f863abf --- /dev/null +++ b/kaddressbook/categoryfilterproxymodel.h @@ -0,0 +1,86 @@ +/* + Copyright (c) 2014 Jonathan Marten <jjm at keelhaul.me.uk> + + This library is free software; you can redistribute it and/or modify it + under the terms of the GNU Library General Public License as published by + the Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This library is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public + License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. +*/ + +#ifndef CATEGORYFILTERPROXYMODEL_H +#define CATEGORYFILTERPROXYMODEL_H + +#include <qsortfilterproxymodel.h> + +#include <akonadi/tag.h> + +class CategoryFilterProxyModelPrivate; + + +/** + * @short A proxy model to filter contacts by categories (tags). + * + * @since 4.14 + * @author Jonathan Marten + **/ + +class CategoryFilterProxyModel : public QSortFilterProxyModel +{ + Q_OBJECT + Q_DECLARE_PRIVATE(CategoryFilterProxyModel); + +public: + /** + * Constructor. + * + * @param parent The parent object + **/ + explicit CategoryFilterProxyModel(QObject *parent = 0); + + /** + * Destructor. + **/ + virtual ~CategoryFilterProxyModel(); + +public slots: + /** + * Set the categories to be accepted by the filter. + * + * @param idList A list of @c Akonadi::Tag::Id's of the categories + * which are to be accepted by the filter. + * @see CategorySelectModel::filterChanged + **/ + void setFilterCategories(const QList<Akonadi::Tag::Id> &idList); + + /** + * Enable or disable the filter. + * + * @param enable If @c true, enable the filter to accept only those categories + * set by @c setFilterCategories(). If @false, disable the filter so that all + * entries are accepted. + * + * The default state is that the filter is disabled. + **/ + void setFilterEnabled(bool enable); + +protected: + /** + * @reimp + **/ + virtual bool filterAcceptsRow(int row, const QModelIndex &parent) const; + +private: + CategoryFilterProxyModelPrivate * const d_ptr; +}; + +#endif // CATEGORYFILTERPROXYMODEL_H diff --git a/kaddressbook/categoryselectwidget.cpp b/kaddressbook/categoryselectwidget.cpp new file mode 100644 index 0000000..cca1028 --- /dev/null +++ b/kaddressbook/categoryselectwidget.cpp @@ -0,0 +1,316 @@ +/* + Copyright (c) 2014 Jonathan Marten <jjm at keelhaul.me.uk> + + This library is free software; you can redistribute it and/or modify it + under the terms of the GNU Library General Public License as published by + the Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This library is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public + License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. +*/ + +#include "categoryselectwidget.h" + +#include <kicon.h> +#include <klocale.h> +#include <kdebug.h> +#include <kdialog.h> + +#include <qtoolbutton.h> +#include <qlayout.h> +#include <qstandarditemmodel.h> +#include <qtimer.h> + +#include <akonadi/monitor.h> +#include <akonadi/tagmodel.h> + +#include <widgets/kcheckcombobox.h> + +using namespace Akonadi; + + +static const int FILTER_ROLE = Qt::UserRole+1; + + +class CategorySelectWidgetPrivate : public QObject +{ + Q_OBJECT + Q_DECLARE_PUBLIC(CategorySelectWidget); + +public: + CategorySelectWidgetPrivate(CategorySelectWidget *parent); + + Akonadi::TagModel *tagModel; + int rowOffset; + QTimer *updateTimer; + KPIM::KCheckComboBox *checkCombo; + + void init(); + QStandardItemModel *itemModel() const; + void selectAll(Qt::CheckState state) const; + QList<Akonadi::Tag::Id> filterTags() const; + +public slots: + void slotSelectAll(); + void slotSelectNone(); + + void slotTagsInserted(const QModelIndex &parent, int start, int end); + void slotTagsRemoved(const QModelIndex &parent, int start, int end); + void slotTagsChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight); + + void slotCheckedItemsChanged(); + void slotCheckedItemsTimer(); + +private: + CategorySelectWidget *q_ptr; +}; + + +CategorySelectWidgetPrivate::CategorySelectWidgetPrivate(CategorySelectWidget *parent) + : QObject(), + q_ptr(parent) +{ +} + + +void CategorySelectWidgetPrivate::init() +{ + Q_Q(CategorySelectWidget); + + QHBoxLayout *hbox = new QHBoxLayout(q); + hbox->setSpacing(0); + + checkCombo = new KPIM::KCheckComboBox; + checkCombo->setSqueezeText(true); + connect(checkCombo, SIGNAL(checkedItemsChanged(QStringList)), + SLOT(slotCheckedItemsChanged())); + hbox->addWidget(checkCombo); + + Monitor *monitor = new Monitor(this); + monitor->setTypeMonitored(Monitor::Tags); + tagModel = new Akonadi::TagModel(monitor, this); + + connect(tagModel, SIGNAL(rowsInserted(const QModelIndex &,int,int)), + SLOT(slotTagsInserted(const QModelIndex &,int,int))); + connect(tagModel, SIGNAL(rowsRemoved(const QModelIndex &,int,int)), + SLOT(slotTagsRemoved(const QModelIndex &,int,int))); + connect(tagModel, SIGNAL(dataChanged(const QModelIndex &,const QModelIndex &)), + SLOT(slotTagsChanged(const QModelIndex &,const QModelIndex &))); + + updateTimer = new QTimer(this); + updateTimer->setSingleShot(true); + updateTimer->setInterval(200); + connect(updateTimer, SIGNAL(timeout()), SLOT(slotCheckedItemsTimer())); + + hbox->addSpacing(KDialog::spacingHint()); + + QToolButton *but = new QToolButton(q); + but ->setAutoRaise(true); + but->setIcon(KIcon(QLatin1String("edit-undo"))); + but->setToolTip(i18nc("@action:button", "Reset category filter")); + connect(but, SIGNAL(clicked(bool)), SLOT(slotSelectAll())); + hbox->addWidget(but); + + but = new QToolButton(q); + but->setAutoRaise(true); + but->setIcon(KIcon(QLatin1String("edit-clear"))); + but->setToolTip(i18nc("@action:button", "Clear category filter")); + connect(but, SIGNAL(clicked(bool)), SLOT(slotSelectNone())); + hbox->addWidget(but); + + QStandardItem *item = new QStandardItem(i18n("(Untagged)")); + item->setCheckState(Qt::Checked); + item->setData(CategorySelectWidget::FilterUntagged, FILTER_ROLE); + itemModel()->appendRow(item); + + item = new QStandardItem(i18n("(Groups)")); + item->setCheckState(Qt::Checked); + item->setData(CategorySelectWidget::FilterGroups, FILTER_ROLE); + itemModel()->appendRow(item); + + rowOffset = itemModel()->rowCount(); +} + + +QStandardItemModel *CategorySelectWidgetPrivate::itemModel() const +{ + QStandardItemModel *m = qobject_cast<QStandardItemModel *>(checkCombo->model()); + Q_ASSERT(m!=NULL); + return m; +} + + +void CategorySelectWidgetPrivate::slotTagsRemoved(const QModelIndex &parent, int start, int end) +{ + itemModel()->removeRows(start+rowOffset, end+rowOffset, parent); +} + + +void CategorySelectWidgetPrivate::slotTagsChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight) +{ + for (int row = topLeft.row(); row<=bottomRight.row(); ++row) { + QStandardItem *it = itemModel()->item(row+rowOffset); + Q_ASSERT(it!=NULL); + + QModelIndex idx = tagModel->index(row, 0); + it->setText(tagModel->data(idx, TagModel::NameRole).toString()); + it->setIcon(tagModel->data(idx, Qt::DecorationRole).value<QIcon>()); + it->setData(tagModel->data(idx, TagModel::IdRole), FILTER_ROLE); + } +} + + +void CategorySelectWidgetPrivate::slotTagsInserted(const QModelIndex &parent, int start, int end) +{ + for (int row = start; row<=end; ++row) { + QModelIndex idx = tagModel->index(row, 0, parent); + kDebug() << "idx" << idx << "=" << tagModel->data(idx, Qt::DisplayRole).toString() + << "name" << tagModel->data(idx, TagModel::NameRole).toString() + << "tag" << tagModel->data(idx, TagModel::TagRole) + << "id" << tagModel->data(idx, TagModel::IdRole).toInt(); + + QStandardItem *it = new QStandardItem(tagModel->data(idx, TagModel::NameRole).toString()); + it->setIcon(tagModel->data(idx, Qt::DecorationRole).value<QIcon>()); + it->setData(tagModel->data(idx, TagModel::IdRole), FILTER_ROLE); + it->setCheckState(Qt::Checked); + + // If a tag with a parent arrives from the model, we know that its parent + // must already have arrived. So there is no need for a list of pending + // tags, as is required in Akonadi::TagModel. + // + // FIXME: not tested (no way to create hierarchial tags at present) + if (parent!=QModelIndex()) { + const Tag::Id parentId = tagModel->data(idx, TagModel::IdRole).value<Tag::Id>(); + QModelIndexList matchList = itemModel()->match(itemModel()->index(0, 0), FILTER_ROLE, + parentId, 1, + Qt::MatchExactly|Qt::MatchRecursive); + if (matchList.count()==1) { // found the parent tag + QModelIndex parentIndex = matchList.first(); + itemModel()->itemFromIndex(parentIndex)->appendRow(it); + } else { + kWarning() << "Cannot find parent with ID" << parentId; + itemModel()->insertRow(row+rowOffset, it); + } + } else { + itemModel()->insertRow(row+rowOffset, it); + } + } +} + + +void CategorySelectWidgetPrivate::selectAll(Qt::CheckState state) const +{ + for (int row = 0; row<itemModel()->rowCount(); ++row) { + QStandardItem *it = itemModel()->item(row); + it->setCheckState(state); + } +} + + +void CategorySelectWidgetPrivate::slotSelectAll() +{ + selectAll(Qt::Checked); +} + + +void CategorySelectWidgetPrivate::slotSelectNone() +{ + selectAll(Qt::Unchecked); +} + + +void CategorySelectWidgetPrivate::slotCheckedItemsChanged() +{ + updateTimer->start(); +} + + +void CategorySelectWidgetPrivate::slotCheckedItemsTimer() +{ + Q_Q(CategorySelectWidget); + + bool allOn = true; + for (int row = 0; row<itemModel()->rowCount(); ++row) { + const QStandardItem *it = itemModel()->item(row); + Qt::CheckState rowState = static_cast<Qt::CheckState>(it->data(Qt::CheckStateRole).toInt()); + if (rowState!=Qt::Checked) { + allOn = false; + break; + } + } + + if (allOn) { + checkCombo->setAlwaysShowDefaultText(true); + checkCombo->setDefaultText(i18n("(All)")); + } else { + checkCombo->setAlwaysShowDefaultText(false); + checkCombo->setDefaultText(i18n("(None)")); + } + + const QStringList checkedList = checkCombo->checkedItems(); + if (!checkedList.isEmpty()) { + checkCombo->setToolTip(i18n("<qt>Category filter: %1", checkedList.join(i18n(", ")))); + } else { + checkCombo->setToolTip(QString()); + } + + emit q->filterChanged(filterTags()); +} + + +QList<Akonadi::Tag::Id> CategorySelectWidgetPrivate::filterTags() const +{ + QList<Tag::Id> filter; + bool allOn = true; + for (int row = 0; row<itemModel()->rowCount(); ++row) { + const QStandardItem *it = itemModel()->item(row); + Q_ASSERT(it!=NULL); + if (it->checkState()==Qt::Checked) { + Tag::Id id = it->data(FILTER_ROLE).toInt(); + if (id!=0) filter.append(id); + } else { + allOn = false; + } + } + + if (allOn) { + filter.clear(); + filter.append(CategorySelectWidget::FilterAll); + } + + kDebug() << "filter" << filter; + return filter; +} + + +CategorySelectWidget::CategorySelectWidget(QWidget *parent) + : QWidget(parent), + d_ptr(new CategorySelectWidgetPrivate(this)) +{ + Q_D(CategorySelectWidget); + d->init(); +} + + +CategorySelectWidget::~CategorySelectWidget() +{ + delete d_ptr; +} + + +QList<Akonadi::Tag::Id> CategorySelectWidget::filterTags() const +{ + Q_D(const CategorySelectWidget); + return d->filterTags(); +} + + +#include "categoryselectwidget.moc" diff --git a/kaddressbook/categoryselectwidget.h b/kaddressbook/categoryselectwidget.h new file mode 100644 index 0000000..eff9fc3 --- /dev/null +++ b/kaddressbook/categoryselectwidget.h @@ -0,0 +1,87 @@ +/* + Copyright (c) 2014 Jonathan Marten <jjm at keelhaul.me.uk> + + This library is free software; you can redistribute it and/or modify it + under the terms of the GNU Library General Public License as published by + the Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This library is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public + License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. +*/ + +#ifndef CATEGORYSELECTWIDGET_H +#define CATEGORYSELECTWIDGET_H + +#include <qwidget.h> +#include <akonadi/tag.h> + +class CategorySelectWidgetPrivate; + +/** + * @short A widget to specify a category (tag) filter. + * + * @since 4.14 + * @author Jonathan Marten + **/ + +class CategorySelectWidget : public QWidget +{ + Q_OBJECT + Q_DECLARE_PRIVATE(CategorySelectWidget); + +public: + /** + * Constructor. + * + * @param parent The parent widget + **/ + explicit CategorySelectWidget(QWidget *parent = 0); + + /** + * Destructor. + **/ + virtual ~CategorySelectWidget(); + + /** + * Special @c Akonadi::Tag::Id values for filtering. + **/ + enum FilterTag + { + FilterAll = -2, /**< All items */ + FilterUntagged = -3, /**< Untagged items */ + FilterGroups = -4 /**< Contact groups */ + }; + + /** + * Get the current tag filter list. + * + * @return The filter list, as would be sent by @c filterChanged() + * + * @see filterChanged + **/ + QList<Akonadi::Tag::Id> filterTags() const; + +signals: + /** + * The tag filter selection has changed. + * + * @param idList A list of @c Akonadi::Tag::Id's of the tags which + * are to be included in the filter. + * + * @see CategorySelectModel::filterChanged + **/ + void filterChanged(const QList<Akonadi::Tag::Id> &idList); + +private: + CategorySelectWidgetPrivate * const d_ptr; +}; + +#endif // CATEGORYSELECTWIDGET_H diff --git a/kaddressbook/kaddressbookui.rc b/kaddressbook/kaddressbookui.rc index 6f4a774..623b2b8 100644 --- a/kaddressbook/kaddressbookui.rc +++ b/kaddressbook/kaddressbookui.rc @@ -1,5 +1,5 @@ <!DOCTYPE kpartgui> -<kpartgui name="kaddressbook" version="18"> +<kpartgui name="kaddressbook" version="19"> <MenuBar> <Menu name="file"><text>&File</text> @@ -98,6 +98,7 @@ <Action name="akonadi_contact_create"/> <Action name="akonadi_contact_group_create"/> <Action name="quick_search"/> + <Action name="category_filter"/> </ToolBar> </kpartgui> diff --git a/kaddressbook/mainwidget.cpp b/kaddressbook/mainwidget.cpp index c2555fe..8983649 100644 --- a/kaddressbook/mainwidget.cpp +++ b/kaddressbook/mainwidget.cpp @@ -29,7 +29,8 @@ #include "xxportmanager.h" #include "utils.h" #include "kaddressbookadaptor.h" - +#include "categoryselectwidget.h" +#include "categoryfilterproxymodel.h" #include "kaddressbookgrantlee/formatter/grantleecontactformatter.h" #include "kaddressbookgrantlee/formatter/grantleecontactgroupformatter.h" @@ -154,6 +155,9 @@ MainWidget::MainWidget( KXMLGUIClient *guiClient, QWidget *parent ) * mContactsFilterModel * ^ * | + * mCategorySelectWidget --> mCategoryFilterModel + * ^ + * | * mItemTree * ^ * | @@ -184,6 +188,8 @@ MainWidget::MainWidget( KXMLGUIClient *guiClient, QWidget *parent ) * selectionProxyModel: Filters out all collections and items that are no children * of the collections currently selected in selectionModel * mItemTree: Filters out all collections + * mCategorySelectWidget: Selects a list of categories for filtering + * mCategoryFilterModel: Filters the contacts by the selected categories * mContactsFilterModel: Filters the contacts by the content of mQuickSearchWidget * mItemView: Shows the items (contacts and contact groups) in a view * @@ -226,8 +232,17 @@ MainWidget::MainWidget( KXMLGUIClient *guiClient, QWidget *parent ) mItemTree->addMimeTypeExclusionFilter( Akonadi::Collection::mimeType() ); mItemTree->setHeaderGroup( Akonadi::EntityTreeModel::ItemListHeaders ); + mCategoryFilterModel = new CategoryFilterProxyModel( this ); + mCategoryFilterModel->setSourceModel( mItemTree ); + mCategoryFilterModel->setFilterCategories( mCategorySelectWidget->filterTags() ); + mCategoryFilterModel->setFilterEnabled( true ); + + connect(mCategorySelectWidget, SIGNAL(filterChanged(const QList<Akonadi::Tag::Id> &)), + mCategoryFilterModel, SLOT(setFilterCategories(const QList<Akonadi::Tag::Id> &))); + mContactsFilterModel = new Akonadi::ContactsFilterProxyModel( this ); - mContactsFilterModel->setSourceModel( mItemTree ); + mContactsFilterModel->setSourceModel( mCategoryFilterModel ); + connect( mQuickSearchWidget, SIGNAL(filterStringChanged(QString)), mContactsFilterModel, SLOT(setFilterString(QString)) ); connect( mQuickSearchWidget, SIGNAL(filterStringChanged(QString)), @@ -519,6 +534,9 @@ void MainWidget::setupGui() // the quick search widget which is embedded in the toolbar action mQuickSearchWidget = new QuickSearchWidget; + // the category filter widget which is embedded in the toolbar action + mCategorySelectWidget = new CategorySelectWidget; + // setup the default actions Akonadi::ContactDefaultActions *actions = new Akonadi::ContactDefaultActions( this ); actions->connectToView( mContactDetails ); @@ -560,6 +578,10 @@ void MainWidget::setupActions( KActionCollection *collection ) action->setText( i18n( "Quick search" ) ); action->setDefaultWidget( mQuickSearchWidget ); + action = collection->addAction( QLatin1String("category_filter") ); + action->setText( i18n( "Category filter" ) ); + action->setDefaultWidget( mCategorySelectWidget ); + action = collection->addAction( QLatin1String("select_all") ); action->setText( i18n( "Select All" ) ); action->setShortcut( QKeySequence( Qt::CTRL + Qt::Key_A ) ); @@ -955,5 +977,3 @@ void MainWidget::slotCheckNewCalendar( const QModelIndex &parent, int begin, int } } } - - diff --git a/kaddressbook/mainwidget.h b/kaddressbook/mainwidget.h index 0075edf..69ccb6d 100644 --- a/kaddressbook/mainwidget.h +++ b/kaddressbook/mainwidget.h @@ -59,6 +59,8 @@ class QStackedWidget; class QuickSearchWidget; class XXPortManager; class QActionGroup; +class CategorySelectWidget; +class CategoryFilterProxyModel; class KADDRESSBOOK_EXPORT MainWidget : public QWidget { @@ -121,10 +123,13 @@ private: Akonadi::EntityMimeTypeFilterModel *mItemTree; Akonadi::EntityMimeTypeFilterModel *mAllContactsModel; Akonadi::ContactsFilterProxyModel *mContactsFilterModel; + CategoryFilterProxyModel *mCategoryFilterModel; QuickSearchWidget *mQuickSearchWidget; Akonadi::EntityTreeView *mCollectionView; Akonadi::EntityTreeView *mItemView; + CategorySelectWidget *mCategorySelectWidget; + QWidget *mDetailsPane; QStackedWidget *mDetailsViewStack; ContactSwitcher *mContactSwitcher; diff --git a/kaddressbook/settings/kaddressbook.kcfg b/kaddressbook/settings/kaddressbook.kcfg index f3a3e38..cc06279 100644 --- a/kaddressbook/settings/kaddressbook.kcfg +++ b/kaddressbook/settings/kaddressbook.kcfg @@ -55,7 +55,8 @@ <label>Viewing mode</label> <tooltip>Viewing mode (number of columns)</tooltip> <whatsthis>Choose the layout for the main view</whatsthis> - </entry> + </entry> + </group> </kcfg>
