Akonadi::EntityTreeModel Class

class Akonadi::EntityTreeModel

A model for collections and items together. More...

Header: #include <Akonadi/EntityTreeModel>
CMake: find_package(KPim6 REQUIRED COMPONENTS AkonadiCore)
target_link_libraries(mytarget PRIVATE KPim6::AkonadiCore)
Inherits: QAbstractItemModel

Public Types

enum CollectionFetchStrategy { FetchNoCollections, FetchFirstLevelChildCollections, FetchCollectionsRecursive, FetchCollectionsMerged }
enum FetchState { IdleState, FetchingState }
enum HeaderGroup { EntityTreeHeaders, CollectionTreeHeaders, ItemListHeaders, UserHeaders, EndHeaderGroup }
enum ItemPopulationStrategy { NoItemPopulation, ImmediatePopulation, LazyPopulation }
enum Roles { ItemIdRole, ItemRole, MimeTypeRole, CollectionColorRole, CollectionIdRole, …, EndRole }

Public Functions

EntityTreeModel(Akonadi::Monitor *monitor, QObject *parent = nullptr)
virtual ~EntityTreeModel() override
Akonadi::EntityTreeModel::CollectionFetchStrategy collectionFetchStrategy() const
bool includeRootCollection() const
bool isCollectionPopulated(Akonadi::Collection::Id) const
bool isCollectionTreeFetched() const
bool isFullyPopulated() const
Akonadi::EntityTreeModel::ItemPopulationStrategy itemPopulationStrategy() const
Akonadi::CollectionFetchScope::ListFilter listFilter() const
QString rootCollectionDisplayName() const
void setCollectionFetchStrategy(Akonadi::EntityTreeModel::CollectionFetchStrategy strategy)
void setCollectionMonitored(const Akonadi::Collection &col, bool monitored = true)
void setCollectionsMonitored(const Akonadi::Collection::List &collections)
void setIncludeRootCollection(bool include)
void setItemPopulationStrategy(Akonadi::EntityTreeModel::ItemPopulationStrategy strategy)
void setListFilter(Akonadi::CollectionFetchScope::ListFilter filter)
void setRootCollectionDisplayName(const QString &name)
void setShowSystemEntities(bool show)
bool systemEntitiesShown() const

Reimplemented Public Functions

virtual QModelIndexList match(const QModelIndex &start, int role, const QVariant &value, int hits = 1, Qt::MatchFlags flags = Qt::MatchFlags(Qt::MatchStartsWith | Qt::MatchWrap)) const override

Signals

void collectionFetched(int collectionId)
void collectionPopulated(Akonadi::Collection::Id collectionId)
void collectionTreeFetched(const Akonadi::Collection::List &collections)
(since 6.4.0) void errorOccurred(const QString &message)

Static Public Members

QModelIndex modelIndexForCollection(const QAbstractItemModel *model, const Akonadi::Collection &collection)
QModelIndexList modelIndexesForItem(const QAbstractItemModel *model, const Akonadi::Item &item)
Akonadi::Collection updatedCollection(const QAbstractItemModel *model, qint64 collectionId)

Protected Functions

void clearAndReset()
virtual QVariant entityData(const Akonadi::Collection &collection, int column, int role = Qt::DisplayRole) const
virtual QVariant entityData(const Akonadi::Item &item, int column, int role = Qt::DisplayRole) const
virtual QVariant entityHeaderData(int section, Qt::Orientation orientation, int role, Akonadi::EntityTreeModel::HeaderGroup headerGroup) const

Detailed Description

Akonadi models and views provide a high level way to interact with the Akonadi server. Most applications will use these classes.

Models provide an interface for viewing, updating, deleting and moving Items and Collections. Additionally, the models are updated automatically if another application changes the data or inserts or deletes items etc.

Note: The EntityTreeModel should be used with the EntityTreeView or the EntityListView class either directly or indirectly via proxy models.

<h3>Retrieving Collections and Items from the model</h3>

If you want to retrieve and Item or Collection from the model, and already have a valid QModelIndex for the correct row, the Collection can be retrieved like this:

 Collection col = index.data(EntityTreeModel::CollectionRole).value<Collection>();

And similarly for Items. This works even if there is a proxy model between the calling code and the EntityTreeModel.

If you want to retrieve a Collection for a particular Collection::Id and you do not yet have a valid QModelIndex, use modelIndexForCollection.

<h3>Using EntityTreeModel in your application</h3>

The responsibilities which fall to the application developer are - Configuring the Monitor and EntityTreeModel - Making use of this class via proxy models - Subclassing for type specific display information

<h3>Creating and configuring the EntityTreeModel</h3>

This class is a wrapper around a Akonadi::Monitor object. The model represents a part of the collection and item tree configured in the Monitor. The structure of the model mirrors the structure of Collections and Items on the %Akonadi server.

The following code creates a model which fetches items and collections relevant to addressees (contacts), and automatically manages keeping the items up to date.

 auto monitor = new Monitor(this);
 monitor->setCollectionMonitored(Collection::root());
 monitor->setMimeTypeMonitored(KContacts::Addressee::mimeType());
 monitor->setSession(session);

 auto model = new EntityTreeModel(monitor, this);

 auto view = new EntityTreeView(this);
 view->setModel(model);

The EntityTreeModel will show items of a different type by changing the line

 monitor->setMimeTypeMonitored(KContacts::Addressee::mimeType());

to a different mimetype. KContacts::Addressee::mimeType() is an alias for "text/directory". If changed to KMime::Message::mimeType() (an alias for "message/rfc822") the model would instead contain emails. The model can be configured to contain items of any mimetype known to %Akonadi.

Note: The EntityTreeModel does some extra configuration on the Monitor, such as setting itemFetchScope() and collectionFetchScope() to retrieve all ancestors. This is necessary for proper function of the model.

The EntityTreeModel can be further configured for certain behaviours such as fetching of collections and items.

The model can be configured to not fetch items into the model (ie, fetch collections only) by setting

 entityTreeModel->setItemPopulationStrategy(EntityTreeModel::NoItemPopulation);

The items may be fetched lazily, i.e. not inserted into the model until request by the user for performance reasons.

The Collection tree is always built immediately if Collections are to be fetched.

 entityTreeModel->setItemPopulationStrategy(EntityTreeModel::LazyPopulation);

This will typically be used with a EntityMimeTypeFilterModel in a configuration such as KMail or AkonadiConsole.

The CollectionFetchStrategy determines how the model will be populated with Collections. That is, if FetchNoCollections is set, no collections beyond the root of the model will be fetched. This can be used in combination with setting a particular Collection to monitor.

 // Get an collection id from a config file.
 Collection::Id id;
 monitor->setCollectionMonitored(Collection(id));
 // ... Other initialization code.
 entityTree->setCollectionFetchStrategy(FetchNoCollections);

This has the effect of creating a model of only a list of Items, and not collections. This is similar in behaviour and aims to the ItemModel. By using FetchFirstLevelCollections instead, a mixed list of entities can be created.

Note: It is important that you set only one Collection to be monitored in the monitor object. This one collection will be the root of the tree. If you need a model with a more complex structure, consider monitoring a common ancestor and using a SelectionProxyModel.

It is also possible to show the root Collection as part of the selectable model:

 entityTree->setIncludeRootCollection(true);

By default the displayed name of the root collection is '[*]', because it doesn't require i18n, and is generic. It can be changed too.

 entityTree->setIncludeRootCollection(true);
 entityTree->setRootCollectionDisplayName(i18nc("Name of top level for all addressbooks in the application", "[All AddressBooks]"))

This feature is used in KAddressBook.

If items are to be fetched by the model, it is necessary to specify which parts of the items are to be fetched, using the ItemFetchScope class. By default, only the basic metadata is fetched. To fetch all item data, including all attributes:

 monitor->itemFetchScope().fetchFullPayload();
 monitor->itemFetchScope().fetchAllAttributes();

<h2>Using EntityTreeModel with Proxy models</h2>

An Akonadi::SelectionProxyModel can be used to simplify managing selection in one view through multiple proxy models to a representation in another view. The selectionModel of the initial view is used to create a proxied model which filters out anything not related to the current selection.

 // ... create an EntityTreeModel

 collectionTree = new EntityMimeTypeFilterModel(this);
 collectionTree->setSourceModel(entityTreeModel);

 // Include only collections in this proxy model.
 collectionTree->addMimeTypeInclusionFilter(Collection::mimeType());
 collectionTree->setHeaderGroup(EntityTreeModel::CollectionTreeHeaders);

 treeview->setModel(collectionTree);

 // SelectionProxyModel can handle complex selections:
 treeview->setSelectionMode(QAbstractItemView::ExtendedSelection);

 auto selProxy = new SelectionProxyModel(treeview->selectionModel(), this);
 selProxy->setSourceModel(entityTreeModel);

 itemList = new EntityMimeTypeFilterModel(this);
 itemList->setSourceModel(selProxy);

 // Filter out collections. Show only items.
 itemList->addMimeTypeExclusionFilter(Collection::mimeType());
 itemList->setHeaderGroup(EntityTreeModel::ItemListHeaders);

 auto itemView = new EntityTreeView(splitter);
 itemView->setModel(itemList);

The SelectionProxyModel can handle complex selections.

See the KSelectionProxyModel documentation for the valid configurations of a Akonadi::SelectionProxyModel.

Obviously, the SelectionProxyModel may be used in a view, or further processed with other proxy models. Typically, the result from this model will be further filtered to remove collections from the item list as in the above example.

There are several advantages of using EntityTreeModel with the SelectionProxyModel, namely the items can be fetched and cached instead of being fetched many times, and the chain of proxies from the core model to the view is automatically handled. There is no need to manage all the mapToSource and mapFromSource calls manually.

A KDescendantsProxyModel can be used to represent all descendants of a model as a flat list. For example, to show all descendant items in a selected Collection in a list:

 auto collectionTree = new EntityMimeTypeFilterModel(this);
 collectionTree->setSourceModel(entityTreeModel);

 // Include only collections in this proxy model.
 collectionTree->addMimeTypeInclusionFilter(Collection::mimeType());
 collectionTree->setHeaderGroup(EntityTreeModel::CollectionTreeHeaders);

 treeview->setModel(collectionTree);

 auto selProxy = new SelectionProxyModel(treeview->selectionModel(), this);
 selProxy->setSourceModel(entityTreeModel);

 auto descendedList = new KDescendantsProxyModel(this);
 descendedList->setSourceModel(selProxy);

 auto itemList = new EntityMimeTypeFilterModel(this);
 itemList->setSourceModel(descendedList);

 // Exclude collections from the list view.
 itemList->addMimeTypeExclusionFilter(Collection::mimeType());
 itemList->setHeaderGroup(EntityTreeModel::ItemListHeaders);

 listView = new EntityTreeView(this);
 listView->setModel(itemList);

Note that it is important in this case to use the KDescendantsProxyModel before the EntityMimeTypeFilterModel. Otherwise, by filtering out the collections first, you would also be filtering out their child items.

This pattern is used in KAddressBook.

It would not make sense to use a KDescendantsProxyModel with LazyPopulation.

<h3>Subclassing EntityTreeModel</h3>

Usually an application will create a subclass of an EntityTreeModel and use that in several views via proxy models.

The subclassing is necessary in order for the data in the model to have type-specific representation in applications

For example, the headerData for an EntityTreeModel will be different depending on whether it is in a view showing only Collections in which case the header data should be "AddressBooks" for example, or only Items, in which case the headerData would be for example "Family Name", "Given Name" and "Email address" for contacts or "Subject", "Sender", "Date" in the case of emails.

Additionally, the actual data shown in the rows of the model should be type specific.

In summary, it must be possible to have different numbers of columns, different data in the rows of those columns, and different titles for each column depending on the contents of the view.

The way this is accomplished is by using the EntityMimeTypeFilterModel for splitting the model into a "CollectionTree" and an "Item List" as in the above example, and using a type-specific EntityTreeModel subclass to return the type-specific data, typically for only one type (for example, contacts or emails).

The following protected virtual methods should be implemented in the subclass: - `int entityColumnCount( HeaderGroup headerGroup ) const;` – Implement to return the number of columns for a HeaderGroup. If the HeaderGroup is CollectionTreeHeaders, return the number of columns to display for the Collection tree, and if it is ItemListHeaders, return the number of columns to display for the item. In the case of addressee, this could be for example, two (for given name and family name) or for emails it could be three (for subject, sender, date). This is a decision of the subclass implementer. - `QVariant entityHeaderData( int section, Qt::Orientation orientation, int role, HeaderGroup headerGroup ) const;` – Implement to return the data for each section for a HeaderGroup. For example, if the header group is CollectionTreeHeaders in a contacts model, the string "Address books" might be returned for column 0, whereas if the headerGroup is ItemListHeaders, the strings "Given Name", "Family Name", "Email Address" might be returned for the columns 0, 1, and 2. - `QVariant entityData( const Collection &collection, int column, int role = Qt::DisplayRole ) const;` – Implement to return data for a particular Collection. Typically this will be the name of the collection or the EntityDisplayAttribute. - `QVariant entityData(const Item &item, int column, int role = Qt::DisplayRole) const;` – Implement to return the data for a particular item and column. In the case of email for example, this would be the actual subject, sender and date of the email.

Note: The entityData methods are just for convenience. the QAbstractItemModel::data method can be overridden if required.

The application writer must then properly configure proxy models for the views, so that the correct data is shown in the correct view. That is the purpose of these lines in the above example

 collectionTree->setHeaderGroup(EntityTreeModel::CollectionTreeHeaders);
 itemList->setHeaderGroup(EntityTreeModel::ItemListHeaders);

<h3>Progress reporting</h3>

The EntityTreeModel uses asynchronous Akonadi::Job instances to fill and update itself. For example, a job is run to fetch the contents of collections (that is, list the items in it). Additionally, individual Akonadi::Items can be fetched in different parts at different times.

To indicate that such a job is underway, the EntityTreeModel makes the FetchState available. The FetchState returned from a QModelIndex representing a Akonadi::Collection will be FetchingState if a listing of the items in that collection is underway, otherwise the state is IdleState.

Author: Stephen Kelly <steveire@gmail.com>

See also Akonadi::ItemFetchScope::AncestorRetrieval, akonadi-mimetypes, and lazy-model-population.

Member Type Documentation

enum EntityTreeModel::CollectionFetchStrategy

Describes what collections shall be fetched by and represent in the model.

enum EntityTreeModel::FetchState

Describes the state of fetch jobs related to particular collections.

 QModelIndex collectionIndex = getIndex();
 if (collectionIndex.data(EntityTreeModel::FetchStateRole).toLongLong() == FetchingState) {
   // There is a fetch underway
 } else {
   // There is no fetch underway.
 }

enum EntityTreeModel::HeaderGroup

Describes what header information the model shall return.

enum EntityTreeModel::ItemPopulationStrategy

Describes how the model should populated its items.

enum EntityTreeModel::Roles

Describes the roles for items. Roles for collections are defined by the superclass.

Member Function Documentation

[explicit] EntityTreeModel::EntityTreeModel(Akonadi::Monitor *monitor, QObject *parent = nullptr)

Creates a new entity tree model.

monitor The Monitor whose entities should be represented in the model. parent The parent object.

[override virtual noexcept] EntityTreeModel::~EntityTreeModel()

Destroys the entity tree model.

[protected] void EntityTreeModel::clearAndReset()

Clears and resets the model. Always call this instead of the reset method in the superclass. Using the reset method will not reliably clear or refill the model.

Akonadi::EntityTreeModel::CollectionFetchStrategy EntityTreeModel::collectionFetchStrategy() const

Returns the collection fetch strategy of the model.

See also setCollectionFetchStrategy().

[signal] void EntityTreeModel::collectionFetched(int collectionId)

Emitted once a collection has been fetched for the very first time. This is like a dataChanged(), but specific to the initial loading, in order to update the GUI (window caption, state of actions). Usually, the GUI uses Akonadi::Monitor to be notified of further changes to the collections. collectionId the identifier of the fetched collection

[signal] void EntityTreeModel::collectionPopulated(Akonadi::Collection::Id collectionId)

Signal emitted when a collection has been populated, i.e. its items have been fetched. collectionId id of the collection which has been populated

See also collectionTreeFetched.

[signal] void EntityTreeModel::collectionTreeFetched(const Akonadi::Collection::List &collections)

Signal emitted when the collection tree has been fetched for the first time. collections list of collections which have been fetched

See also isCollectionTreeFetched and collectionPopulated.

[virtual protected] QVariant EntityTreeModel::entityData(const Akonadi::Collection &collection, int column, int role = Qt::DisplayRole) const

Provided for convenience of subclasses.

[virtual protected] QVariant EntityTreeModel::entityData(const Akonadi::Item &item, int column, int role = Qt::DisplayRole) const

Provided for convenience of subclasses.

[virtual protected] QVariant EntityTreeModel::entityHeaderData(int section, Qt::Orientation orientation, int role, Akonadi::EntityTreeModel::HeaderGroup headerGroup) const

Reimplement this to provide different header data. This is needed when using one model with multiple proxies and views, and each should show different header data.

[signal, since 6.4.0] void EntityTreeModel::errorOccurred(const QString &message)

Emitted when an error occurred.

This function was introduced in 6.4.0.

bool EntityTreeModel::includeRootCollection() const

Returns whether the root collection is provided by the model.

See also setIncludeRootCollection().

bool EntityTreeModel::isCollectionPopulated(Akonadi::Collection::Id) const

Returns whether the collection has been populated.

See also collectionPopulated.

bool EntityTreeModel::isCollectionTreeFetched() const

Returns whether the collection tree has been fetched at initialisation.

See also collectionTreeFetched.

bool EntityTreeModel::isFullyPopulated() const

Returns whether the model is fully populated.

Returns true once the collection tree has been fetched and all collections have been populated.

See also isCollectionPopulated and isCollectionTreeFetched.

Akonadi::EntityTreeModel::ItemPopulationStrategy EntityTreeModel::itemPopulationStrategy() const

Returns the item population strategy of the model.

See also setItemPopulationStrategy().

Akonadi::CollectionFetchScope::ListFilter EntityTreeModel::listFilter() const

Returns the currently used listfilter.

See also setListFilter().

[override virtual] QModelIndexList EntityTreeModel::match(const QModelIndex &start, int role, const QVariant &value, int hits = 1, Qt::MatchFlags flags = Qt::MatchFlags(Qt::MatchStartsWith | Qt::MatchWrap)) const

Reimplements: QAbstractItemModel::match(const QModelIndex &start, int role, const QVariant &value, int hits, Qt::MatchFlags flags) const.

Reimplemented to handle the AmazingCompletionRole.

[static] QModelIndex EntityTreeModel::modelIndexForCollection(const QAbstractItemModel *model, const Akonadi::Collection &collection)

Returns a QModelIndex in model which points to collection. This method can be used through proxy models if model is a proxy model.

 EntityTreeModel *model = getEntityTreeModel();
 QSortFilterProxyModel *proxy1 = new QSortFilterProxyModel;
 proxy1->setSourceModel(model);
 QSortFilterProxyModel *proxy2 = new QSortFilterProxyModel;
 proxy2->setSourceModel(proxy1);

 ...

 QModelIndex idx = EntityTreeModel::modelIndexForCollection(proxy2, Collection(colId));
 if (!idx.isValid())
   // Collection with id colId is not in the proxy2.
   // Maybe it is filtered out if proxy 2 is only showing items? Make sure you use the correct proxy.
   return;

 Collection collection = idx.data( EntityTreeModel::CollectionRole ).value<Collection>();
 // collection has the id colId, and all other attributes already fetched by the model such as name, remoteId, Akonadi::Attributes etc.

This can be useful for example if an id is stored in a config file and needs to be used in the application.

Note however, that to restore view state such as scrolling, selection and expansion of items in trees, the ETMViewStateSaver can be used for convenience.

See also modelIndexesForItem.

[static] QModelIndexList EntityTreeModel::modelIndexesForItem(const QAbstractItemModel *model, const Akonadi::Item &item)

Returns a QModelIndex in model which points to item. This method can be used through proxy models if model is a proxy model. model the model to query for the item item the item to look for

See also modelIndexForCollection.

QString EntityTreeModel::rootCollectionDisplayName() const

Returns the display name of the root collection.

See also setRootCollectionDisplayName().

void EntityTreeModel::setCollectionFetchStrategy(Akonadi::EntityTreeModel::CollectionFetchStrategy strategy)

Sets the collection fetch strategy of the model.

See also collectionFetchStrategy().

void EntityTreeModel::setCollectionMonitored(const Akonadi::Collection &col, bool monitored = true)

Adds or removes a specific collection from the monitored set without resetting the model. Only call this if you're monitoring specific collections (not mimetype/resources/items).

See also setCollectionsMonitored().

void EntityTreeModel::setCollectionsMonitored(const Akonadi::Collection::List &collections)

Monitors the specified collections and resets the model.

void EntityTreeModel::setIncludeRootCollection(bool include)

Sets whether the root collection shall be provided by the model. include enables root collection if set as \ true

See also includeRootCollection() and setRootCollectionDisplayName().

void EntityTreeModel::setItemPopulationStrategy(Akonadi::EntityTreeModel::ItemPopulationStrategy strategy)

Sets the item population strategy of the model.

See also itemPopulationStrategy().

void EntityTreeModel::setListFilter(Akonadi::CollectionFetchScope::ListFilter filter)

Sets the currently used listfilter.

See also listFilter().

void EntityTreeModel::setRootCollectionDisplayName(const QString &name)

Sets the display name of the root collection of the model. The default display name is "[*]". name the name to display for the root collection

Note: The display name for the root collection is only used if the root collection has been included with setIncludeRootCollection().

See also rootCollectionDisplayName().

void EntityTreeModel::setShowSystemEntities(bool show)

Some Entities are hidden in the model, but exist for internal purposes, for example, custom object directories in groupware resources. They are hidden by default, but can be shown by setting show to true. show enabled displaying of hidden entities if set as \ true Most applications will not need to use this feature.

bool EntityTreeModel::systemEntitiesShown() const

Returns \ true if internal system entities are shown, and \ false otherwise.

[static] Akonadi::Collection EntityTreeModel::updatedCollection(const QAbstractItemModel *model, qint64 collectionId)

Returns an Akonadi::Collection from the model based on given collectionId.

This is faster and simpler than retrieving a full Collection from the ETM by using modelIndexForCollection() and then querying for the index data.