1 /* 2 SPDX-FileCopyrightText: 2008 Stephen Kelly <steveire@gmail.com> 3 4 SPDX-License-Identifier: LGPL-2.0-or-later 5 */ 6 7 #pragma once 8 9 #include "akonadicore_export.h" 10 #include "collection.h" 11 #include "collectionfetchscope.h" 12 #include "item.h" 13 14 #include <QAbstractItemModel> 15 #include <QStringList> 16 17 #include <memory> 18 19 namespace Akonadi 20 { 21 class CollectionStatistics; 22 class Item; 23 class ItemFetchScope; 24 class Monitor; 25 class Session; 26 27 class EntityTreeModelPrivate; 28 29 /** 30 * @short A model for collections and items together. 31 * 32 * Akonadi models and views provide a high level way to interact with the akonadi server. 33 * Most applications will use these classes. 34 * 35 * Models provide an interface for viewing, updating, deleting and moving Items and Collections. 36 * Additionally, the models are updated automatically if another application changes the 37 * data or inserts of deletes items etc. 38 * 39 * @note The EntityTreeModel should be used with the EntityTreeView or the EntityListView class 40 * either directly or indirectly via proxy models. 41 * 42 * <h3>Retrieving Collections and Items from the model</h3> 43 * 44 * If you want to retrieve and Item or Collection from the model, and already have a valid 45 * QModelIndex for the correct row, the Collection can be retrieved like this: 46 * 47 * @code 48 * Collection col = index.data( EntityTreeModel::CollectionRole ).value<Collection>(); 49 * @endcode 50 * 51 * And similarly for Items. This works even if there is a proxy model between the calling code 52 * and the EntityTreeModel. 53 * 54 * If you want to retrieve a Collection for a particular Collection::Id and you do not yet 55 * have a valid QModelIndex, use modelIndexForCollection. 56 * 57 * <h3>Using EntityTreeModel in your application</h3> 58 * 59 * The responsibilities which fall to the application developer are 60 * - Configuring the Monitor and EntityTreeModel 61 * - Making use of this class via proxy models 62 * - Subclassing for type specific display information 63 * 64 * <h3>Creating and configuring the EntityTreeModel</h3> 65 * 66 * This class is a wrapper around a Akonadi::Monitor object. The model represents a 67 * part of the collection and item tree configured in the Monitor. The structure of the 68 * model mirrors the structure of Collections and Items on the %Akonadi server. 69 * 70 * The following code creates a model which fetches items and collections relevant to 71 * addressees (contacts), and automatically manages keeping the items up to date. 72 * 73 * @code 74 * 75 * Monitor *monitor = new Monitor( this ); 76 * monitor->setCollectionMonitored( Collection::root() ); 77 * monitor->setMimeTypeMonitored( KContacts::Addressee::mimeType() ); 78 * monitor->setSession( session ); 79 * 80 * EntityTreeModel *model = new EntityTreeModel( monitor, this ); 81 * 82 * EntityTreeView *view = new EntityTreeView( this ); 83 * view->setModel( model ); 84 * 85 * @endcode 86 * 87 * The EntityTreeModel will show items of a different type by changing the line 88 * 89 * @code 90 * monitor->setMimeTypeMonitored( KContacts::Addressee::mimeType() ); 91 * @endcode 92 * 93 * to a different mimetype. KContacts::Addressee::mimeType() is an alias for "text/directory". If changed to KMime::Message::mimeType() 94 * (an alias for "message/rfc822") the model would instead contain emails. The model can be configured to contain items of any mimetype 95 * known to %Akonadi. 96 * 97 * @note The EntityTreeModel does some extra configuration on the Monitor, such as setting itemFetchScope() and collectionFetchScope() 98 * to retrieve all ancestors. This is necessary for proper function of the model. 99 * 100 * @see Akonadi::ItemFetchScope::AncestorRetrieval. 101 * 102 * @see akonadi-mimetypes. 103 * 104 * The EntityTreeModel can be further configured for certain behaviours such as fetching of collections and items. 105 * 106 * The model can be configured to not fetch items into the model (ie, fetch collections only) by setting 107 * 108 * @code 109 * entityTreeModel->setItemPopulationStrategy( EntityTreeModel::NoItemPopulation ); 110 * @endcode 111 * 112 * The items may be fetched lazily, i.e. not inserted into the model until request by the user for performance reasons. 113 * 114 * The Collection tree is always built immediately if Collections are to be fetched. 115 * 116 * @code 117 * entityTreeModel->setItemPopulationStrategy( EntityTreeModel::LazyPopulation ); 118 * @endcode 119 * 120 * This will typically be used with a EntityMimeTypeFilterModel in a configuration such as KMail4.5 or AkonadiConsole. 121 * 122 * The CollectionFetchStrategy determines how the model will be populated with Collections. That is, if FetchNoCollections is set, 123 * no collections beyond the root of the model will be fetched. This can be used in combination with setting a particular Collection to monitor. 124 * 125 * @code 126 * // Get an collection id from a config file. 127 * Collection::Id id; 128 * monitor->setCollectionMonitored( Collection( id ) ); 129 * // ... Other initialization code. 130 * entityTree->setCollectionFetchStrategy( FetchNoCollections ); 131 * @endcode 132 * 133 * 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. 134 * By using FetchFirstLevelCollections instead, a mixed list of entities can be created. 135 * 136 * @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. 137 * If you need a model with a more complex structure, consider monitoring a common ancestor and using a SelectionProxyModel. 138 * 139 * @see lazy-model-population 140 * 141 * It is also possible to show the root Collection as part of the selectable model: 142 * 143 * @code 144 * entityTree->setIncludeRootCollection( true ); 145 * @endcode 146 * 147 * 148 * By default the displayed name of the root collection is '[*]', because it doesn't require i18n, and is generic. It can be changed too. 149 * 150 * @code 151 * entityTree->setIncludeRootCollection( true ); 152 * entityTree->setRootCollectionDisplayName( i18nc( "Name of top level for all addressbooks in the application", "[All AddressBooks]" ) ) 153 * @endcode 154 * 155 * This feature is used in KAddressBook. 156 * 157 * If items are to be fetched by the model, it is necessary to specify which parts of the items 158 * are to be fetched, using the ItemFetchScope class. By default, only the basic metadata is 159 * fetched. To fetch all item data, including all attributes: 160 * 161 * @code 162 * monitor->itemFetchScope().fetchFullPayload(); 163 * monitor->itemFetchScope().fetchAllAttributes(); 164 * @endcode 165 * 166 * <h2>Using EntityTreeModel with Proxy models</h2> 167 * 168 * An Akonadi::SelectionProxyModel can be used to simplify managing selection in one view through multiple proxy models to a representation in another view. 169 * The selectionModel of the initial view is used to create a proxied model which filters out anything not related to the current selection. 170 * 171 * @code 172 * // ... create an EntityTreeModel 173 * 174 * collectionTree = new EntityMimeTypeFilterModel( this ); 175 * collectionTree->setSourceModel( entityTreeModel ); 176 * 177 * // Include only collections in this proxy model. 178 * collectionTree->addMimeTypeInclusionFilter( Collection::mimeType() ); 179 * collectionTree->setHeaderGroup( EntityTreeModel::CollectionTreeHeaders ); 180 * 181 * treeview->setModel(collectionTree); 182 * 183 * // SelectionProxyModel can handle complex selections: 184 * treeview->setSelectionMode( QAbstractItemView::ExtendedSelection ); 185 * 186 * SelectionProxyModel *selProxy = new SelectionProxyModel( treeview->selectionModel(), this ); 187 * selProxy->setSourceModel( entityTreeModel ); 188 * 189 * itemList = new EntityMimeTypeFilterModel( this ); 190 * itemList->setSourceModel( selProxy ); 191 * 192 * // Filter out collections. Show only items. 193 * itemList->addMimeTypeExclusionFilter( Collection::mimeType() ); 194 * itemList->setHeaderGroup( EntityTreeModel::ItemListHeaders ); 195 * 196 * EntityTreeView *itemView = new EntityTreeView( splitter ); 197 * itemView->setModel( itemList ); 198 * @endcode 199 * 200 * The SelectionProxyModel can handle complex selections. 201 * 202 * See the KSelectionProxyModel documentation for the valid configurations of a Akonadi::SelectionProxyModel. 203 * 204 * Obviously, the SelectionProxyModel may be used in a view, or further processed with other proxy models. Typically, the result 205 * from this model will be further filtered to remove collections from the item list as in the above example. 206 * 207 * There are several advantages of using EntityTreeModel with the SelectionProxyModel, namely the items can be fetched and cached 208 * instead of being fetched many times, and the chain of proxies from the core model to the view is automatically handled. There is 209 * no need to manage all the mapToSource and mapFromSource calls manually. 210 * 211 * A KDescendantsProxyModel can be used to represent all descendants of a model as a flat list. 212 * For example, to show all descendant items in a selected Collection in a list: 213 * @code 214 * collectionTree = new EntityMimeTypeFilterModel( this ); 215 * collectionTree->setSourceModel( entityTreeModel ); 216 * 217 * // Include only collections in this proxy model. 218 * collectionTree->addMimeTypeInclusionFilter( Collection::mimeType() ); 219 * collectionTree->setHeaderGroup( EntityTreeModel::CollectionTreeHeaders ); 220 * 221 * treeview->setModel( collectionTree ); 222 * 223 * SelectionProxyModel *selProxy = new SelectionProxyModel( treeview->selectionModel(), this ); 224 * selProxy->setSourceModel( entityTreeModel ); 225 * 226 * descendedList = new KDescendantsProxyModel( this ); 227 * descendedList->setSourceModel( selProxy ); 228 * 229 * itemList = new EntityMimeTypeFilterModel( this ); 230 * itemList->setSourceModel( descendedList ); 231 * 232 * // Exclude collections from the list view. 233 * itemList->addMimeTypeExclusionFilter( Collection::mimeType() ); 234 * itemList->setHeaderGroup( EntityTreeModel::ItemListHeaders ); 235 * 236 * listView = new EntityTreeView( this ); 237 * listView->setModel( itemList ); 238 * @endcode 239 * 240 * 241 * Note that it is important in this case to use the KDescendantsProxyModel before the EntityMimeTypeFilterModel. 242 * Otherwise, by filtering out the collections first, you would also be filtering out their child items. 243 * 244 * This pattern is used in KAddressBook. 245 * 246 * It would not make sense to use a KDescendantsProxyModel with LazyPopulation. 247 * 248 * <h3>Subclassing EntityTreeModel</h3> 249 * 250 * Usually an application will create a subclass of an EntityTreeModel and use that in several views via proxy models. 251 * 252 * The subclassing is necessary in order for the data in the model to have type-specific representation in applications 253 * 254 * For example, the headerData for an EntityTreeModel will be different depending on whether it is in a view showing only Collections 255 * in which case the header data should be "AddressBooks" for example, or only Items, in which case the headerData would be 256 * for example "Family Name", "Given Name" and "Email address" for contacts or "Subject", "Sender", "Date" in the case of emails. 257 * 258 * Additionally, the actual data shown in the rows of the model should be type specific. 259 * 260 * In summary, it must be possible to have different numbers of columns, different data in the rows of those columns, and different 261 * titles for each column depending on the contents of the view. 262 * 263 * The way this is accomplished is by using the EntityMimeTypeFilterModel for splitting the model into a "CollectionTree" and an "Item List" 264 * 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, 265 * contacts or emails). 266 * 267 * The following protected virtual methods should be implemented in the subclass: 268 * - int entityColumnCount( HeaderGroup headerGroup ) const; 269 * -- Implement to return the number of columns for a HeaderGroup. If the HeaderGroup is CollectionTreeHeaders, return the number of columns to display for the 270 * 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, 271 * 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 implementor. 272 * - QVariant entityHeaderData( int section, Qt::Orientation orientation, int role, HeaderGroup headerGroup ) const; 273 * -- Implement to return the data for each section for a HeaderGroup. For example, if the header group is CollectionTreeHeaders in a contacts model, 274 * the string "Address books" might be returned for column 0, whereas if the headerGroup is ItemListHeaders, the strings "Given Name", "Family Name", 275 * "Email Address" might be returned for the columns 0, 1, and 2. 276 * - QVariant entityData( const Collection &collection, int column, int role = Qt::DisplayRole ) const; 277 * -- Implement to return data for a particular Collection. Typically this will be the name of the collection or the EntityDisplayAttribute. 278 * - QVariant entityData( const Item &item, int column, int role = Qt::DisplayRole ) const; 279 * -- 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 280 * email. 281 * 282 * @note The entityData methods are just for convenience. the QAbstractItemModel::data method can be overridden if required. 283 * 284 * The application writer must then properly configure proxy models for the views, so that the correct data is shown in the correct view. 285 * That is the purpose of these lines in the above example 286 * 287 * @code 288 * collectionTree->setHeaderGroup( EntityTreeModel::CollectionTreeHeaders ); 289 * itemList->setHeaderGroup( EntityTreeModel::ItemListHeaders ); 290 * @endcode 291 * 292 * <h3>Progress reporting</h3> 293 * 294 * The EntityTreeModel uses asynchronous Akonadi::Job instances to fill and update itself. 295 * For example, a job is run to fetch the contents of collections (that is, list the items in it). 296 * Additionally, individual Akonadi::Items can be fetched in different parts at different times. 297 * 298 * To indicate that such a job is underway, the EntityTreeModel makes the FetchState available. The 299 * FetchState returned from a QModelIndex representing a Akonadi::Collection will be FetchingState if a 300 * listing of the items in that collection is underway, otherwise the state is IdleState. 301 * 302 * @author Stephen Kelly <steveire@gmail.com> 303 * @since 4.4 304 */ 305 class AKONADICORE_EXPORT EntityTreeModel : public QAbstractItemModel 306 { 307 Q_OBJECT 308 309 public: 310 /** 311 * Describes the roles for items. Roles for collections are defined by the superclass. 312 */ 313 enum Roles { 314 // sebsauer, 2009-05-07; to be able here to keep the akonadi_next EntityTreeModel compatible with 315 // the akonadi_old ItemModel and CollectionModel, we need to use the same int-values for 316 // ItemRole, ItemIdRole and MimeTypeRole like the Akonadi::ItemModel is using and the same 317 // CollectionIdRole and CollectionRole like the Akonadi::CollectionModel is using. 318 ItemIdRole = Qt::UserRole + 1, ///< The item id 319 ItemRole = Qt::UserRole + 2, ///< The Item 320 MimeTypeRole = Qt::UserRole + 3, ///< The mimetype of the entity 321 322 CollectionIdRole = Qt::UserRole + 10, ///< The collection id. 323 CollectionRole = Qt::UserRole + 11, ///< The collection. 324 325 RemoteIdRole, ///< The remoteId of the entity 326 CollectionChildOrderRole, ///< Ordered list of child items if available 327 ParentCollectionRole, ///< The parent collection of the entity 328 ColumnCountRole, ///< @internal Used by proxies to determine the number of columns for a header group. 329 LoadedPartsRole, ///< Parts available in the model for the item 330 AvailablePartsRole, ///< Parts available in the Akonadi server for the item 331 SessionRole, ///< @internal The Session used by this model 332 CollectionRefRole, ///< @internal Used to increase the reference count on a Collection 333 CollectionDerefRole, ///< @internal Used to decrease the reference count on a Collection 334 PendingCutRole, ///< Used to indicate items which are to be cut 335 EntityUrlRole, ///< The akonadi:/ Url of the entity as a string. Item urls will contain the mimetype. 336 UnreadCountRole, ///< Returns the number of unread items in a collection. @since 4.5 337 FetchStateRole, ///< Returns the FetchState of a particular item. @since 4.5 338 IsPopulatedRole, ///< Returns whether a Collection has been populated, i.e. whether its items have been fetched. @since 4.10 339 OriginalCollectionNameRole, ///< Returns original name for collection @since 4.14 340 UserRole = Qt::UserRole + 500, ///< First role for user extensions. 341 TerminalUserRole = 2000, ///< Last role for user extensions. Don't use a role beyond this or headerData will break. 342 EndRole = 65535 343 }; 344 345 /** 346 * Describes the state of fetch jobs related to particular collections. 347 * 348 * @code 349 * QModelIndex collectionIndex = getIndex(); 350 * if (collectionIndex.data(EntityTreeModel::FetchStateRole).toLongLong() == FetchingState) { 351 * // There is a fetch underway 352 * } else { 353 * // There is no fetch underway. 354 * } 355 * @endcode 356 * 357 * @since 4.5 358 */ 359 enum FetchState { 360 IdleState, ///< There is no fetch of items in this collection in progress. 361 FetchingState ///< There is a fetch of items in this collection in progress. 362 // TODO: Change states for reporting of fetching payload parts of items. 363 }; 364 365 /** 366 * Describes what header information the model shall return. 367 */ 368 enum HeaderGroup { 369 EntityTreeHeaders, ///< Header information for a tree with collections and items 370 CollectionTreeHeaders, ///< Header information for a collection-only tree 371 ItemListHeaders, ///< Header information for a list of items 372 UserHeaders = 10, ///< Last header information for submodel extensions 373 EndHeaderGroup = 32 ///< Last headergroup role. Don't use a role beyond this or headerData will break. 374 // Note that we're splitting up available roles for the header data hack and int(EndRole / TerminalUserRole) == 32 375 }; 376 377 /** 378 * Creates a new entity tree model. 379 * 380 * @param monitor The Monitor whose entities should be represented in the model. 381 * @param parent The parent object. 382 */ 383 explicit EntityTreeModel(Monitor *monitor, QObject *parent = nullptr); 384 385 /** 386 * Destroys the entity tree model. 387 */ 388 ~EntityTreeModel() override; 389 390 /** 391 * Describes how the model should populated its items. 392 */ 393 enum ItemPopulationStrategy { 394 NoItemPopulation, ///< Do not include items in the model. 395 ImmediatePopulation, ///< Retrieve items immediately when their parent is in the model. This is the default. 396 LazyPopulation ///< Fetch items only when requested (using canFetchMore/fetchMore) 397 }; 398 399 /** 400 * Some Entities are hidden in the model, but exist for internal purposes, for example, custom object 401 * directories in groupware resources. 402 * They are hidden by default, but can be shown by setting @p show to true. 403 * @param show enabled displaying of hidden entities if set as @c true 404 * Most applications will not need to use this feature. 405 */ 406 void setShowSystemEntities(bool show); 407 408 /** 409 * Returns @c true if internal system entities are shown, and @c false otherwise. 410 */ 411 Q_REQUIRED_RESULT bool systemEntitiesShown() const; 412 413 /** 414 * Returns the currently used listfilter. 415 * 416 * @since 4.14 417 */ 418 Q_REQUIRED_RESULT Akonadi::CollectionFetchScope::ListFilter listFilter() const; 419 420 /** 421 * Sets the currently used listfilter. 422 * 423 * @since 4.14 424 */ 425 void setListFilter(Akonadi::CollectionFetchScope::ListFilter filter); 426 427 /** 428 * Monitors the specified collections and resets the model. 429 * 430 * @since 4.14 431 */ 432 void setCollectionsMonitored(const Akonadi::Collection::List &collections); 433 434 /** 435 * Adds or removes a specific collection from the monitored set without resetting the model. 436 * Only call this if you're monitoring specific collections (not mimetype/resources/items). 437 * 438 * @since 4.14 439 * @see setCollectionsMonitored() 440 */ 441 void setCollectionMonitored(const Akonadi::Collection &col, bool monitored = true); 442 443 /** 444 * Sets the item population @p strategy of the model. 445 */ 446 void setItemPopulationStrategy(ItemPopulationStrategy strategy); 447 448 /** 449 * Returns the item population strategy of the model. 450 */ 451 Q_REQUIRED_RESULT ItemPopulationStrategy itemPopulationStrategy() const; 452 453 /** 454 * Sets whether the root collection shall be provided by the model. 455 * @param include enables root collection if set as @c true 456 * @see setRootCollectionDisplayName() 457 */ 458 void setIncludeRootCollection(bool include); 459 460 /** 461 * Returns whether the root collection is provided by the model. 462 */ 463 Q_REQUIRED_RESULT bool includeRootCollection() const; 464 465 /** 466 * Sets the display @p name of the root collection of the model. 467 * The default display name is "[*]". 468 * @param name the name to display for the root collection 469 * @note The display name for the root collection is only used if 470 * the root collection has been included with setIncludeRootCollection(). 471 */ 472 void setRootCollectionDisplayName(const QString &name); 473 474 /** 475 * Returns the display name of the root collection. 476 */ 477 Q_REQUIRED_RESULT QString rootCollectionDisplayName() const; 478 479 /** 480 * Describes what collections shall be fetched by and represent in the model. 481 */ 482 enum CollectionFetchStrategy { 483 FetchNoCollections, ///< Fetches nothing. This creates an empty model. 484 FetchFirstLevelChildCollections, ///< Fetches first level collections in the root collection. 485 FetchCollectionsRecursive, ///< Fetches collections in the root collection recursively. This is the default. 486 InvisibleCollectionFetch ///< Fetches collections, but does not put them in the model. This can be used to create a list of items in all collections. 487 ///< The ParentCollectionRole can still be used to retrieve the parent collection of an Item. @since 4.5 488 }; 489 490 /** 491 * Sets the collection fetch @p strategy of the model. 492 */ 493 void setCollectionFetchStrategy(CollectionFetchStrategy strategy); 494 495 /** 496 * Returns the collection fetch strategy of the model. 497 */ 498 Q_REQUIRED_RESULT CollectionFetchStrategy collectionFetchStrategy() const; 499 500 Q_REQUIRED_RESULT QHash<int, QByteArray> roleNames() const override; 501 502 Q_REQUIRED_RESULT int columnCount(const QModelIndex &parent = QModelIndex()) const override; 503 Q_REQUIRED_RESULT int rowCount(const QModelIndex &parent = QModelIndex()) const override; 504 505 Q_REQUIRED_RESULT QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; 506 Q_REQUIRED_RESULT QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; 507 508 Q_REQUIRED_RESULT Qt::ItemFlags flags(const QModelIndex &index) const override; 509 Q_REQUIRED_RESULT QStringList mimeTypes() const override; 510 511 Q_REQUIRED_RESULT Qt::DropActions supportedDropActions() const override; 512 Q_REQUIRED_RESULT QMimeData *mimeData(const QModelIndexList &indexes) const override; 513 bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) override; 514 bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override; 515 516 Q_REQUIRED_RESULT QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override; 517 Q_REQUIRED_RESULT QModelIndex parent(const QModelIndex &index) const override; 518 519 // TODO: Review the implementations of these. I think they could be better. 520 Q_REQUIRED_RESULT bool canFetchMore(const QModelIndex &parent) const override; 521 void fetchMore(const QModelIndex &parent) override; 522 Q_REQUIRED_RESULT bool hasChildren(const QModelIndex &parent = QModelIndex()) const override; 523 524 /** 525 * Returns whether the collection tree has been fetched at initialisation. 526 * 527 * @see collectionTreeFetched 528 * @since 4.10 529 */ 530 Q_REQUIRED_RESULT bool isCollectionTreeFetched() const; 531 532 /** 533 * Returns whether the collection has been populated. 534 * 535 * @see collectionPopulated 536 * @since 4.12 537 */ 538 Q_REQUIRED_RESULT bool isCollectionPopulated(Akonadi::Collection::Id) const; 539 540 /** 541 * Returns whether the model is fully populated. 542 * 543 * Returns true once the collection tree has been fetched and all collections have been populated. 544 * 545 * @see isCollectionPopulated 546 * @see isCollectionTreeFetched 547 * @since 4.14 548 */ 549 Q_REQUIRED_RESULT bool isFullyPopulated() const; 550 551 /** 552 * Reimplemented to handle the AmazingCompletionRole. 553 */ 554 Q_REQUIRED_RESULT QModelIndexList match(const QModelIndex &start, 555 int role, 556 const QVariant &value, 557 int hits = 1, 558 Qt::MatchFlags flags = Qt::MatchFlags(Qt::MatchStartsWith | Qt::MatchWrap)) const override; 559 560 /** 561 * Returns a QModelIndex in @p model which points to @p collection. 562 * This method can be used through proxy models if @p model is a proxy model. 563 * @code 564 * EntityTreeModel *model = getEntityTreeModel(); 565 * QSortFilterProxyModel *proxy1 = new QSortFilterProxyModel; 566 * proxy1->setSourceModel(model); 567 * QSortFilterProxyModel *proxy2 = new QSortFilterProxyModel; 568 * proxy2->setSourceModel(proxy1); 569 * 570 * ... 571 * 572 * QModelIndex idx = EntityTreeModel::modelIndexForCollection(proxy2, Collection(colId)); 573 * if (!idx.isValid()) 574 * // Collection with id colId is not in the proxy2. 575 * // Maybe it is filtered out if proxy 2 is only showing items? Make sure you use the correct proxy. 576 * return; 577 * 578 * Collection collection = idx.data( EntityTreeModel::CollectionRole ).value<Collection>(); 579 * // collection has the id colId, and all other attributes already fetched by the model such as name, remoteId, Akonadi::Attributes etc. 580 * 581 * @endcode 582 * 583 * This can be useful for example if an id is stored in a config file and needs to be used in the application. 584 * 585 * Note however, that to restore view state such as scrolling, selection and expansion of items in trees, the ETMViewStateSaver can be used for convenience. 586 * 587 * @see modelIndexesForItem 588 * @since 4.5 589 */ 590 static QModelIndex modelIndexForCollection(const QAbstractItemModel *model, const Collection &collection); 591 592 /** 593 * Returns a QModelIndex in @p model which points to @p item. 594 * This method can be used through proxy models if @p model is a proxy model. 595 * @param model the model to query for the item 596 * @param item the item to look for 597 * @see modelIndexForCollection 598 * @since 4.5 599 */ 600 static QModelIndexList modelIndexesForItem(const QAbstractItemModel *model, const Item &item); 601 602 /** 603 * Returns an Akonadi::Collection from the @p model based on given @p collectionId. 604 * 605 * This is faster and simpler than retrieving a full Collection from the ETM 606 * by using modelIndexForCollection() and then querying for the index data. 607 */ 608 static Collection updatedCollection(const QAbstractItemModel *model, qint64 collectionId); 609 static Collection updatedCollection(const QAbstractItemModel *model, const Collection &col); 610 611 Q_SIGNALS: 612 /** 613 * Signal emitted when the collection tree has been fetched for the first time. 614 * @param collections list of collections which have been fetched 615 * 616 * @see isCollectionTreeFetched, collectionPopulated 617 * @since 4.10 618 */ 619 void collectionTreeFetched(const Akonadi::Collection::List &collections); 620 621 /** 622 * Signal emitted when a collection has been populated, i.e. its items have been fetched. 623 * @param collectionId id of the collection which has been populated 624 * 625 * @see collectionTreeFetched 626 * @since 4.10 627 */ 628 void collectionPopulated(Akonadi::Collection::Id collectionId); 629 /** 630 * Emitted once a collection has been fetched for the very first time. 631 * This is like a dataChanged(), but specific to the initial loading, in order to update 632 * the GUI (window caption, state of actions). 633 * Usually, the GUI uses Akonadi::Monitor to be notified of further changes to the collections. 634 * @param collectionId the identifier of the fetched collection 635 * @since 4.9.3 636 */ 637 void collectionFetched(int collectionId); 638 639 protected: 640 /** 641 * Clears and resets the model. Always call this instead of the reset method in the superclass. 642 * Using the reset method will not reliably clear or refill the model. 643 */ 644 void clearAndReset(); 645 646 /** 647 * Provided for convenience of subclasses. 648 */ 649 virtual QVariant entityData(const Item &item, int column, int role = Qt::DisplayRole) const; 650 651 /** 652 * Provided for convenience of subclasses. 653 */ 654 virtual QVariant entityData(const Collection &collection, int column, int role = Qt::DisplayRole) const; 655 656 /** 657 * Reimplement this to provide different header data. This is needed when using one model 658 * with multiple proxies and views, and each should show different header data. 659 */ 660 virtual QVariant entityHeaderData(int section, Qt::Orientation orientation, int role, HeaderGroup headerGroup) const; 661 662 virtual int entityColumnCount(HeaderGroup headerGroup) const; 663 664 protected: 665 /// @cond PRIVATE 666 Q_DECLARE_PRIVATE(EntityTreeModel) 667 std::unique_ptr<EntityTreeModelPrivate> const d_ptr; 668 EntityTreeModel(Monitor *monitor, EntityTreeModelPrivate *d, QObject *parent = nullptr); 669 /// @endcond 670 671 private: 672 /// @cond PRIVATE 673 // Make these private, they shouldn't be called by applications 674 bool insertRows(int row, int count, const QModelIndex &index = QModelIndex()) override; 675 bool insertColumns(int column, int count, const QModelIndex &index = QModelIndex()) override; 676 bool removeColumns(int column, int count, const QModelIndex &index = QModelIndex()) override; 677 bool removeRows(int row, int count, const QModelIndex &index = QModelIndex()) override; 678 679 Q_PRIVATE_SLOT(d_func(), void monitoredCollectionStatisticsChanged(Akonadi::Collection::Id, const Akonadi::CollectionStatistics &)) 680 681 Q_PRIVATE_SLOT(d_func(), void startFirstListJob()) 682 Q_PRIVATE_SLOT(d_func(), void serverStarted()) 683 684 Q_PRIVATE_SLOT(d_func(), void collectionFetchJobDone(KJob *job)) 685 Q_PRIVATE_SLOT(d_func(), void rootFetchJobDone(KJob *job)) 686 Q_PRIVATE_SLOT(d_func(), void pasteJobDone(KJob *job)) 687 Q_PRIVATE_SLOT(d_func(), void updateJobDone(KJob *job)) 688 689 Q_PRIVATE_SLOT(d_func(), void itemsFetched(const Akonadi::Item::List &)) 690 Q_PRIVATE_SLOT(d_func(), void collectionsFetched(Akonadi::Collection::List)) 691 Q_PRIVATE_SLOT(d_func(), void topLevelCollectionsFetched(Akonadi::Collection::List)) 692 Q_PRIVATE_SLOT(d_func(), void ancestorsFetched(Akonadi::Collection::List)) 693 694 Q_PRIVATE_SLOT(d_func(), void monitoredMimeTypeChanged(const QString &, bool)) 695 Q_PRIVATE_SLOT(d_func(), void monitoredCollectionsChanged(const Akonadi::Collection &, bool)) 696 Q_PRIVATE_SLOT(d_func(), void monitoredItemsChanged(const Akonadi::Item &, bool)) 697 Q_PRIVATE_SLOT(d_func(), void monitoredResourcesChanged(const QByteArray &, bool)) 698 699 Q_PRIVATE_SLOT(d_func(), void monitoredCollectionAdded(const Akonadi::Collection &, const Akonadi::Collection &)) 700 Q_PRIVATE_SLOT(d_func(), void monitoredCollectionRemoved(const Akonadi::Collection &)) 701 Q_PRIVATE_SLOT(d_func(), void monitoredCollectionChanged(const Akonadi::Collection &)) 702 Q_PRIVATE_SLOT(d_func(), void monitoredCollectionMoved(const Akonadi::Collection &, const Akonadi::Collection &, const Akonadi::Collection &)) 703 704 Q_PRIVATE_SLOT(d_func(), void monitoredItemAdded(const Akonadi::Item &, const Akonadi::Collection &)) 705 Q_PRIVATE_SLOT(d_func(), void monitoredItemRemoved(const Akonadi::Item &)) 706 Q_PRIVATE_SLOT(d_func(), void monitoredItemChanged(const Akonadi::Item &, const QSet<QByteArray> &)) 707 Q_PRIVATE_SLOT(d_func(), void monitoredItemMoved(const Akonadi::Item &, const Akonadi::Collection &, const Akonadi::Collection &)) 708 709 Q_PRIVATE_SLOT(d_func(), void monitoredItemLinked(const Akonadi::Item &, const Akonadi::Collection &)) 710 Q_PRIVATE_SLOT(d_func(), void monitoredItemUnlinked(const Akonadi::Item &, const Akonadi::Collection &)) 711 Q_PRIVATE_SLOT(d_func(), void changeFetchState(const Akonadi::Collection &)) 712 713 Q_PRIVATE_SLOT(d_func(), void agentInstanceRemoved(Akonadi::AgentInstance)) 714 Q_PRIVATE_SLOT(d_func(), void monitoredItemsRetrieved(KJob *job)) 715 /// @endcond 716 }; 717 718 } // namespace 719 720