Warning, /pim/akonadi/docs/client_libraries.md is written in an unsupported language. File is not indexed.
0001 # Akonadi client libraries # {#client_libraries} 0002 0003 [TOC] 0004 0005 Akonadi client libraries consist of three libraries that provide tools to access 0006 the Akonadi PIM data server: AkonadiCore, AkonadiWidgets and AkonadiAgentBase. 0007 All processes accessing Akonadi, including those which communicate with a remote 0008 server [agents](@ref agents), are considered clients. 0009 0010 <!-- 0011 Additional information about Akonadi: 0012 0013 - <a href="https://api.kde.org/kdesupport-api/akonadi-apidocs/">Akonadi Server documentation</a> 0014 - \ref akonadi_history 0015 - <a href="https://community.kde.org/KDE_PIM/Akonadi">Website</a> 0016 - <a href="https://techbase.kde.org/KDE_PIM/Akonadi">Wiki</a> 0017 0018 Tools for developers: 0019 0020 - <a href="https://bugs.kde.org/buglist.cgi?query_format=advanced&product=Akonadi&component=libakonadi&bug_status=REPORTED&bug_status=CONFIRMED&bug_status=ASSIGNED&bug_status=REOPENED">Bugtracker</a> 0021 //--> 0022 0023 # Akonadi Objects # {#objects} 0024 0025 Akonadi works on two basic object types: collections and items. 0026 0027 Collections are comparable to folders in a file system and are represented by 0028 the class Akonadi::Collection. Every collection has an associated cache policy 0029 represented by the class Akonadi::CachePolicy which defines what part of its 0030 content is cached for how long. All available ways to work with collections are 0031 listed in the "[Collections](#collections)" section. 0032 0033 Akonadi items are comparable to files in a file system and are represented by 0034 the class Akonadi::Item. Each item represents a single PIM object such as a mail 0035 or a contact. The actual object it represents is its so-called payload. All 0036 available ways to work with items are listed in the "[Items](#items)" 0037 section. 0038 0039 Both items and collections are identified by a persistent unique identifier. 0040 Also, they can contain arbitrary attributes (derived from Akonadi::Attribute) to 0041 attach general or application specific meta data to them. 0042 0043 # Collection retrieval and manipulation # {#collections} 0044 0045 A collection is represented by the Akonadi::Collection class. 0046 0047 Classes to retrieve information about collections: 0048 0049 * Akonadi::CollectionFetchJob 0050 * Akonadi::CollectionStatisticsJob 0051 0052 Classes to manipulate collections: 0053 0054 * Akonadi::CollectionCreateJob 0055 * Akonadi::CollectionCopyJob 0056 * Akonadi::CollectionModifyJob 0057 * Akonadi::CollectionDeleteJob 0058 0059 There is also Akonadi::CollectionModel, which is a self-updating model class which can 0060 be used in combination with Akonadi::CollectionView. Akonadi::CollectionFilterProxyModel 0061 can be used to limit a displayed collection tree to collections supporting a certain 0062 type of PIM items. Akonadi::CollectionPropertiesDialog provides an extensible properties 0063 dialog for collections. Often needed QAction for collection operations are provided by 0064 Akonadi::StandardActionManager. 0065 0066 # PIM item retrieval and manipulation # {#items} 0067 0068 PIM items are represented by classes derived from Akonadi::Item. 0069 Items can be retrieved using Akonadi::ItemFetchJob. 0070 0071 The following classes are provided to manipulate PIM items: 0072 0073 * Akonadi::ItemCreateJob 0074 * Akonadi::ItemCopyJob 0075 * Akonadi::ItemModifyJob 0076 * Akonadi::ItemDeleteJob 0077 0078 Akonadi::ItemModel provides a self-updating model class which can be used to display the content 0079 of a collection. Akonadi::ItemView is the base class for a corresponding view. Often needed QAction 0080 for item operations are provided by Akonadi::StandardActionManager. 0081 0082 # Low-level access to the Akonadi server # {#jobs} 0083 0084 Accessing the Akonadi server is done using job classes derived from Akonadi::Job. The 0085 communication channel with the server is provided by Akonadi::Session. 0086 0087 To use server-side transactions, the following jobs are provided: 0088 0089 * Akonadi::TransactionBeginJob 0090 * Akonadi::TransactionCommitJob 0091 * Akonadi::TransactionRollbackJob 0092 0093 There also is Akonadi::TransactionSequence which can be used to automatically group 0094 a set of jobs into a single transaction. 0095 0096 0097 # Change notifications (Monitor) # {#monitor} 0098 0099 The Akonadi::Monitor class allows you to monitor specific resources, 0100 collections and PIM items for changes. Akonadi::ChangeRecorder augments this 0101 by providing a way to record and replay change notifications. 0102 0103 # PIM item serializer # {#serializer} 0104 0105 The class Akonadi::ItemSerializer is responsible for converting between the stored (binary) representation 0106 of a PIM item and the objects used to handle these items provided by the corresponding libraries (kabc, kcal, etc.). 0107 0108 Serializer plugins allow you to add support for new kinds of PIM items to Akonadi. 0109 Akonadi::ItemSerializerPlugin can be used as a base class for such a plugin. 0110 0111 # Agents and Resources # {#resource} 0112 0113 Agents are independent processes that watch the Akonadi store for changes and react to them if necessary. 0114 Example: The Akonadi Indexing Agent is an agent that watches Akonadi for new emails, calendar events, etc., 0115 retrieves the new items and indexes them into a special database. 0116 0117 The class Akonadi::AgentBase is the common base class for all agents. It provides commonly needed 0118 functionality such as change monitoring and recording. 0119 0120 Resources are a special kind of agents. They are used as the actual backend for whatever data the resource represents. 0121 In this situation the akonadi server acts more like a proxy service. It caches data on behalf of its clients 0122 (client here being the Resource), not permanently storing it. The Akonadi server forwards item retrieval requests to the 0123 corresponding resource, if the item is not in the cache. 0124 Example: The imap resource is responsible for storing and fetching emails from an imap server. 0125 0126 Akonadi::ResourceBase is the base class for them. It provides the 0127 necessary interfaces to the server as well as many convenience functions to make implementing 0128 a new resource as easy as possible. Note that a collection contains items belonging to a single 0129 resource, although there are plans in the future for 'virtual' collections which will contain 0130 the results of a search query spanning multiple resources. 0131 0132 A resource can support multiple mimetypes. There are two places where a resource can specify 0133 mimetypes: in its desktop files, and in the content mimetypes field of 0134 collections created by it. The ones in the desktop file are used for 0135 filtering agent types, e.g. in the resource creation dialogs. The collection 0136 content mimetypes specify what you can actually put into a collection, which 0137 is not necessarily the same (e.g. the Kolab resource supports contacts and events, but not 0138 in the same folder). 0139 0140 0141 # Integration in your Application # {#integration} 0142 0143 Akonadi::Control provides ways to ensure that the Akonadi server is running, to monitor its availability 0144 and provide help on server-side errors. A more low-level interface to the Akonadi server is provided 0145 by Akonadi::ServerManager. 0146 0147 A set of standard actions is provided by Akonadi::StandardActionManager. These provide consistent 0148 look and feel across applications. 0149 0150 0151 This library provides classes for KDE applications to communicate with the Akonadi server. The most high-level interface to Akonadi is the Models and Views provided in this library. Ready to use models are provided for use with views to interact with a tree of collections, a list of items in a collection, or a combined tree of Collections and items. 0152 0153 ## Collections and Items ## {#collections_and_items} 0154 0155 In the Akonadi concept, Items are individual objects of PIM data, e.g. emails, contacts, events, notes etc. The data in an item is stored in a typed payload. For example, if an Akonadi Item holds a contact, the contact is available as a KABC::Addressee: 0156 0157 ~~~~~~~~~~~~~{.cpp} 0158 if (item.hasPayload<KABC::Addressee>()) { 0159 KABC::Addressee addr = item.payload<KABC::Addressee>(); 0160 // use addr in some way... 0161 } 0162 ~~~~~~~~~~~~~ 0163 0164 Additionally, an Item must have a mimetype which corresponds to the type of payload it holds. 0165 0166 Collections are simply containers of Items. A Collection has a name and a list of mimetypes that it may contain. A collection may for example contain events if it can contain the mimetype 'text/calendar'. A Collection itself (as opposed to its contents) has a mimetype, which is the same for all Collections. A Collection which can itself contain Collections must be able to contain the Collection mimetype. 0167 0168 ~~~~~~~~~~~~~{.cpp} 0169 Collection col; 0170 // This collection can contain events and nested collections. 0171 col.setContentMimetypes({ Akonadi::Collection::mimeType(), 0172 QStringLiteral("text/calendar") }); 0173 ~~~~~~~~~~~~~ 0174 0175 This system makes it simple to create PIM applications. For example, to create an application for viewing and editing events, you simply need to tell %Akonadi to retrieve all items matching the mimetype 'text/calendar'. 0176 0177 ## Convenience Mimetype Accessors ## {#convenience_mimetype_accessors} 0178 0179 In order to avoid typos, improve readability, and to encapsulate the correct mimetypes for particular pim items, many of the standard classes have an accessor for the kind of mimetype the can handle. For example, you can use KMime::Message::mimeType() for emails, KABC::Addressee::mimeType() for contacts etc. It makes sense to define a similar static function in your own types. 0180 0181 ~~~~~~~~~~~~~{.cpp} 0182 col.setContentMimetypes({ Akonadi::Collection::mimeType(), 0183 KABC::Addressee::mimeType(), 0184 KMime::Message::mimeType() }); 0185 ~~~~~~~~~~~~~ 0186 0187 ## Models and Views ## {#models_and_views} 0188 Akonadi models and views are a high level way to interact with the Akonadi server. Most applications will use these classes. See the EntityTreeModel documentation for more information. 0189 0190 Models provide an interface for viewing, deleting and moving Items and Collections. New Items can also be created by dropping data of the appropriate type on a model. Additionally, the models are updated automatically if another application changes the data or inserts or deletes items etc. 0191 0192 Akonadi provides several models for particular uses, e.g. the MailModel is used for emails and the ContactsModel is used for showing contacts. Additional specific models can be implemented using EntityTreeModel as a base class. 0193 0194 A typical use of these would be to create a model and use proxy models to make the view show different parts of the model. For example, show a collection tree in on one side and show items in a selected collection in another view. 0195 0196 ~~~~~~~~~~~~~{.cpp} 0197 mailModel = new MailModel(session, monitor, this); 0198 0199 collectionTree = new Akonadi::EntityMimeTypeFilterModel(this); 0200 collectionTree->setSourceModel(mailModel); 0201 // Filter out everything that is not a collection. 0202 collectionTree->addMimeTypeInclusionFilter(Akonadi::Collection::mimeType()); 0203 collectionTree->setHeaderSet(Akonadi::EntityTreeModel::CollectionTreeHeaders); 0204 0205 collectionView = new Akonadi::EntityTreeView(this); 0206 collectionView->setModel(collectionTree); 0207 0208 itemList = new Akonadi::EntityMimeTypeFilterModel(this); 0209 itemList->setSourceModel(mailModel); 0210 // Filter out collections 0211 itemList->addMimeTypeExclusionFilter(Akonadi::Collection::mimeType()); 0212 itemList->setHeaderSet(Akonadi::EntityTreeModel::ItemListHeaders); 0213 0214 itemView = new Akonadi::EntityTreeView(this); 0215 itemView->setModel(itemList); 0216 ~~~~~~~~~~~~~ 0217 0218 ![An email application using MailModel](/docs/images/mailmodelapp.png "An email application using MailModel") 0219 0220 The content of the model is determined by the configuration of the Monitor passed into it. The examples below show a use of the EntityTreeModel and some proxy models for a simple hierarchical note collection. As the model is generic, the configuration and proxy models will also work with any other mimetype. 0221 0222 ~~~~~~~~~~~~~{.cpp} 0223 // Configure what should be shown in the model: 0224 Monitor *monitor = new Akonadi::Monitor(this); 0225 monitor->fetchCollection(true); 0226 monitor->setItemFetchScope(scope); 0227 monitor->setCollectionMonitored(Akonadi::Collection::root()); 0228 monitor->setMimeTypeMonitored(MyEntity::mimeType()); 0229 0230 Akonadi::Session *session = new Akonadi::Session(QByteArray("MyEmailApp-") + QByteArray::number(qrand()), this); 0231 monitor->setSession(session); 0232 0233 Akonadi::EntityTreeModel *entityTree = new Akonadi::EntityTreeModel(monitor, this); 0234 ~~~~~~~~~~~~~ 0235 0236 ![A plain EntityTreeModel in a view](/docs/images/entitytreemodel.png "A plain EntityTreeModel in a view") 0237 0238 The EntityTreeModel can be further configured for certain behaviours such as fetching of collections and items. 0239 0240 To create a model of only a collection tree and no items, set that in the model. This is just like CollectionModel: 0241 0242 ~~~~~~~~~~~~~{.cpp} 0243 entityTree->setItemPopulationStrategy(Akonadi::EntityTreeModel::NoItemPopulation); 0244 ~~~~~~~~~~~~~ 0245 0246 ![A plain EntityTreeModel which does not fetch items.](/docs/images/entitytreemodel-collections.png "A plain EntityTreeModel which does not fetch items.") 0247 0248 Or, create a model of only items and not child collections. This is just like ItemModel: 0249 0250 ~~~~~~~~~~~~~{.cpp} 0251 entityTree->setRootCollection(myCollection); 0252 entityTree->setCollectionFetchStrategy(Akonadi::EntityTreeModel::FetchNoCollections); 0253 ~~~~~~~~~~~~~ 0254 0255 Or, to create a model which includes items and first level collections: 0256 0257 ~~~~~~~~~~~~~{.cpp} 0258 entityTree->setCollectionFetchStrategy(Akonadi::EntityTreeModel::FetchFirstLevelCollections); 0259 ~~~~~~~~~~~~~ 0260 0261 The items in the model can also be inserted lazily for performance reasons. The Collection tree is always built immediately. 0262 0263 Additionally, a KDescendantsProxyModel may be used to alter how the items in the tree are presented. 0264 0265 ~~~~~~~~~~~~~{.cpp} 0266 // ... Create an entityTreeModel 0267 KDescendantsProxyModel *descProxy = new KDescendantsProxyModel(this); 0268 descProxy->setSourceModel(entityTree); 0269 view->setModel(descProxy); 0270 ~~~~~~~~~~~~~ 0271 0272 ![A KDescendantsProxyModel wrapping an EntityTreeModel](/docs/images/descendantentitiesproxymodel.png "A KDescendantsProxyModel wrapping an EntityTreeModel") 0273 0274 KDescendantsProxyModel can also display ancestors of each Entity in the list. 0275 0276 ~~~~~~~~~~~~~{.cpp} 0277 // ... Create an entityTreeModel 0278 KDescendantsProxyModel *descProxy = new KDescendantsProxyModel(this); 0279 descProxy->setSourceModel(entityTree); 0280 0281 // #### This is new 0282 descProxy->setDisplayAncestorData(true, QLatin1StringView(" / ")); 0283 0284 view->setModel(descProxy); 0285 ~~~~~~~~~~~~~ 0286 0287 ![A KDescendantsProxyModel with ancestor names.](/docs/images/descendantentitiesproxymodel-withansecnames.png "A KDescendantsProxyModel with ancestor names.") 0288 0289 This proxy can be combined with a filter to for example remove collections. 0290 0291 ~~~~~~~~~~~~~{.cpp} 0292 // ... Create an entityTreeModel 0293 KDescendantsProxyModel *descProxy = new KDescendantsProxyModel(this); 0294 descProxy->setSourceModel(entityTree); 0295 0296 // #### This is new. 0297 Akonadi::EntityMimeTypeFilterModel *filterModel = new Akonadi::EntityMimeTypeFilterModel(this); 0298 filterModel->setSourceModel(descProxy); 0299 filterModel->setExclusionFilter({ Akonadi::Collection::mimeType() }); 0300 0301 view->setModel(filterModel); 0302 ~~~~~~~~~~~~~ 0303 0304 ![An EntityMimeTypeFilterModel wrapping a KDescendantsProxyModel wrapping an EntityTreeModel](/docs/images/descendantentitiesproxymodel-colfilter.png "An EntityMimeTypeFilterModel wrapping a KDescendantsProxyModel wrapping an EntityTreeModel") 0305 0306 It is also possible to show the root item as part of the selectable model: 0307 0308 ~~~~~~~~~~~~~{.cpp} 0309 entityTree->setIncludeRootCollection(true); 0310 ~~~~~~~~~~~~~ 0311 0312 ![An EntityTreeModel showing Collection::root](/docs/images/entitytreemodel-showroot.png "An EntityTreeModel showing Collection::root") 0313 0314 By default the displayed name of the root collection is '[*]', because it doesn't require i18n, and is generic. It can be changed too. 0315 0316 ~~~~~~~~~~~~~{.cpp} 0317 entityTree->setIncludeRootCollection(true); 0318 entityTree->setRootCollectionDisplayName(i18nc("Name of top level for all collections in the application", "[All]")) 0319 ~~~~~~~~~~~~~ 0320 0321 ![An EntityTreeModel showing Collection::root with an application specific name.](/docs/images/entitytreemodel-showrootwithname.png "An EntityTreeModel showing Collection::root with an application specific name.") 0322 0323 These can of course be combined to create an application which uses one EntityTreeModel along with several proxies and views. 0324 0325 ~~~~~~~~~~~~~{.cpp} 0326 // ... create an EntityTreeModel. 0327 Akonadi::EntityMimeTypeFilterModel *collectionTree = new Akonadi::EntityMimeTypeFilterModel(this); 0328 collectionTree->setSourceModel(entityTree); 0329 // Filter to include collections only: 0330 collectionTree->setInclusionFilter({ Akonadi:: Collection::mimeType() }); 0331 Akonadi::EntityTreeView *treeView = new Akonadi::EntityTreeView(this); 0332 treeView->setModel(collectionTree); 0333 0334 Akonadi::EntityMimeTypeFilterModel *itemList = new Akonadi::EntityMimeTypeFilterModel(this); 0335 itemList->setSourceModel(entityTree); 0336 // Filter *out* collections 0337 itemList->setExclusionFilter({ Akonadi::Collection::mimeType() }); 0338 Akonadi::EntityTreeView *listView = new Akonadi::EntityTreeView(this); 0339 listView->setModel(itemList); 0340 ~~~~~~~~~~~~~ 0341 0342 ![A single EntityTreeModel with several views and proxies.](/docs/images/treeandlistapp.png "A single EntityTreeModel with several views and proxies.") 0343 0344 Or to also show items of child collections in the list: 0345 0346 ~~~~~~~~~~~~~{.cpp} 0347 // ... Create an entityTreeModel 0348 collectionTree = new Akonadi::EntityMimeTypeFilterModel(this); 0349 collectionTree->setSourceModel(entityTree); 0350 0351 // Include only collections in this proxy model. 0352 collectionTree->addMimeTypeInclusionFilter(Akonadi::Collection::mimeType()); 0353 0354 treeview->setModel(collectionTree); 0355 0356 descendedList = new KDescendantsProxyModel(this); 0357 descendedList->setSourceModel(entityTree); 0358 0359 itemList = new Akonadi::EntityMimeTypeFilterModel(this); 0360 itemList->setSourceModel(descendedList); 0361 0362 // Exclude collections from the list view. 0363 itemList->addMimeTypeExclusionFilter(Akonadi::Collection::mimeType()); 0364 0365 listView = new EntityTreeView(this); 0366 listView->setModel(itemList); 0367 ~~~~~~~~~~~~~ 0368 0369 ![Showing descendants of all Collections in the list](/docs/images/treeandlistappwithdesclist.png "Showing descendants of all Collections in the list") 0370 0371 Note that it is important in this case to use the DescendantEntitesProxyModel before the EntityMimeTypeFilterModel. Otherwise, by filtering out the collections first, you would also be filtering out their child items. 0372 0373 A 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 includes only the selected indexes and their children. 0374 0375 0376 ~~~~~~~~~~~~~{.cpp} 0377 // ... Create an entityTreeModel 0378 collectionTree = new Akonadi::EntityMimeTypeFilterModel(this); 0379 collectionTree->setSourceModel(entityTree); 0380 0381 // Include only collections in this proxy model. 0382 collectionTree->addMimeTypeInclusionFilter(Akonadi::Collection::mimeType()); 0383 0384 treeview->setModel(collectionTree); 0385 0386 // SelectionProxyModel can handle complex selections: 0387 treeview->setSelectionMode(QAbstractItemView::ExtendedSelection); 0388 0389 SelectionProxyModel *selProxy = new SelectionProxyModel(treeview->selectionModel(), this); 0390 selProxy->setSourceModel(entityTree); 0391 0392 Akonadi::EntityTreeView *selView = new Akonadi::EntityTreeView(splitter); 0393 selView->setModel(selProxy); 0394 ~~~~~~~~~~~~~ 0395 0396 ![A Selection in one view creating a model for use with another view.](/docs/images/selectionproxymodelsimpleselection.png "A Selection in one view creating a model for use with another view.") 0397 0398 The SelectionProxyModel can handle complex selections. 0399 0400 ![Non-contiguous selection creating a new simple model in a second view](/docs/images//selectionproxymodelmultipleselection.png "Non-contiguous selection creating a new simple model in a second view.") 0401 0402 If an index and one or more of its descendants are selected, only the top-most selected index (including all of its descendants) are included in the proxy model. (Though this is configurable. See below) 0403 0404 ![Selecting an item and its descendant](/docs/images/selectionproxymodelmultipleselection-withdescendant.png "Selecting an item and its descendant.") 0405 0406 SelectionProxyModel allows configuration using the methods setStartWithChildTrees, setOmitDescendants, setIncludeAllSelected. See testapp/proxymodeltestapp to try out the 5 valid configurations. 0407 0408 Obviously, the SelectionProxyModel may be used in a view, or further processed with other proxy models. See the example_contacts application for example which uses a further KDescendantsProxyModel and EntityMimeTypeFilterModel on top of a SelectionProxyModel. 0409 0410 The SelectionProxyModel orders its items in the same top-to-bottom order as they appear in the source model. Note that this order may be different to the order in the selection model if there is a QSortFilterProxyModel between the selection and the source model. 0411 0412 ![Ordered items in the SelectionProxyModel](/docs/images/selectionproxymodel-ordered.png "Ordered items in the SelectionProxyModel") 0413 0414 Details on the actual implementation of lazy population are described on [this page](@ref internals). 0415 0416 # Jobs and Monitors # {#jobs_and_monitors} 0417 0418 The lower level way to interact with Akonadi is to use Jobs and Monitors (This is what models use internally). Jobs are used to make changes to akonadi, and in some cases (e.g., a fetch job) emit a signal with data resulting from the job. A Monitor reports changes made to the data stored in Akonadi (e.g., creating, updating, deleting or moving an item or collection ) via signals. 0419 0420 Typically, an application will configure a monitor to report changes to a particular Collection, mimetype or resource, and then connect to the signals it emits. 0421 0422 Most applications will use some of the low level api for actions unrelated to a model-tree view, such as creating new items and collections. 0423 0424 # Tricky details # {#tricky_details} 0425 0426 ## Change Conflicts ## {#change_conflicts} 0427 It is possible that while an application is editing an item, that item gets updated in akonadi. Akonadi will notify the application that that item has changed via a Monitor signal. It is the responsibility of the application to handle the conflict by for example offering the user a dialog to resolve it. Alternatively, the application could ignore the dataChanged signal for that item, and will get another chance to resolve the conflict when trying to save the result back to akonadi. In that case, the ItemModifyJob will fail and report that the revision number of the item on the server differs from its revision number as reported by the job. Again, it is up to the application to handle this case. 0428 0429 This is something that every application using akonadi will have to handle. 0430 0431 ## Using Item::Id or Collection::Id as an identifier ## {#using_id_as_an_identifier} 0432 0433 Items and Collections have a id() member which is a unique identifier used by akonadi. It can be useful to use the id() as an identifier when storing Collections or Items. 0434 0435 However, as an item and a collection can have the same id(), if you need to store both Collections and Items together by a simple identifier, conflicts can occur. 0436 0437 ~~~~~~~~~~~~~{.cpp} 0438 QString getRemoteIdById(Item::Id id) 0439 { 0440 // Note: 0441 // m_items is QHash<Item::Id, Item> 0442 // m_collections is QHash<Collection::Id, Collection> 0443 if (m_items.contains(id)) { 0444 // Oops, we could accidentally match a collection here. 0445 return m_items.value(id).remoteId(); 0446 } else if (m_collections.contains(id)) { 0447 return m_collections.value(id).remoteId(); 0448 } 0449 return QString(); 0450 } 0451 ~~~~~~~~~~~~~ 0452 0453 In this case, it makes more sense to use a normal qint64 as the internal identifier, and use the sign bit to determine if the identifier refers to a Collection or an Item. This is done in the implementation of EntityTreeModel to tell Collections and Items apart. 0454 0455 ~~~~~~~~~~~~~{.cpp} 0456 qstring getremoteidbyinternalidentifier(qint64 internalidentifier) 0457 { 0458 // note: 0459 // m_items is qhash<item::id, item> 0460 // m_collections is qhash<collection::id, collection> 0461 0462 // if the id is negative, it refers to an item 0463 // otherwise it refers to a collection. 0464 0465 if (internalidentifier < 0) { 0466 // reverse the sign of the id before using it. 0467 return m_items.value(-internalidentifier).remoteid(); 0468 } else { 0469 return m_collections.value(internalidentifier).remoteid(); 0470 } 0471 } 0472 ~~~~~~~~~~~~~ 0473 0474 0475 ### Unordered Lists ### {#unordered_lists} 0476 Collection and Item both provide a ::List to represent groups of objects. However the objects in the list are usually not ordered in any particular way, even though the API provides methods to work with an ordered list. It makes more sense to think of it as a Set instead of a list in most cases. 0477 0478 For example, when using an ItemFetchJob to fetch the items in a collection, the items could be in any order when returned from the job. The order that a Monitor emits notices of changes is also indeterminate. By using a Transaction however, it is sometimes possible to retrieve objects in order. Additionally, using s constructor overload in the CollectionFetchJob it is possible to retrieve collections in a particular order. 0479 0480 ~~~~~~~~~~~~~{.cpp} 0481 Collection::List getCollections(const QList<Collection::Id> &idsToGet) 0482 { 0483 Collection::List getList; 0484 for (Collection::Id id : idsToGet) { 0485 getList << Collection(id); 0486 } 0487 CollectionFetchJob *job = CollectionFetchJob(getList); 0488 if (job->exec()) { 0489 // job->collections() is in the same order as the ids in idsToGet. 0490 } 0491 } 0492 ~~~~~~~~~~~~~ 0493 0494 # Resources # {#resources} 0495 The KDEPIM module includes resources for handling many types of PIM data, such as imap email, vcard files and vcard directories, ical event files etc. These cover many of the sources for your PIM data, but in the case that you need to use data from another source (for example a website providing a contacts storage service and an api), you simply have to write a new resource. 0496 0497 https://techbase.kde.org/Development/Tutorials/Akonadi/Resources 0498 0499 # Serializers # {#serializers} 0500 Serializers provide the functionality of converting raw data, for example from a file, to a strongly typed object of PIM data. For example, the addressee serializer reads data from a file and creates a KABC::Addressee object. 0501 0502 New serializers can also easily be written if the data you are dealing with is not one of the standard PIM data types. 0503 0504 # Implementation details # {#implementation_details} 0505 0506 ## Updating Akonadi Models ## {#updating_models} 0507 0508 NOTE: The details here are only relevant if you are writing a new view using EntityTreeModel, or writing a new model. 0509 0510 Because communication with Akonadi happens asynchronously, and the models only hold a cached copy of the data on the akonadi server, some typical behaviours of models are not followed by Akonadi models. 0511 0512 For example, when setting data on a model via a view, most models synchronously update their internal store and notify akonadi to update its view of the data by returning <tt>true</tt>. 0513 0514 <!-- 0515 TODO: Render this manually 0516 @dot 0517 digraph utmg { 0518 rankdir = LR; 0519 { node [label="",style=invis, height=0, width=0 ]; 0520 V_Set_Data; V_Result; V_Data_Changed; 0521 M_Set_Data; M_Result; 0522 } 0523 { node [shape=box, fillcolor=lightyellow, style=filled,fontsize=26]; 0524 View [label=":View"]; Model [label=":Model"]; 0525 } 0526 { node [style=invis]; 0527 View_EOL; Model_EOL; 0528 } 0529 { 0530 V_Set_Data -> M_Set_Data [label="Set Data"]; 0531 M_Result -> V_Result [label="Success",arrowhead="vee", style="dashed"]; 0532 V_Result -> V_Data_Changed [label="Update View \n[ Success = True ]"]; 0533 } 0534 0535 // Dashed Vertical lines for object lifetimes. 0536 edge [style=dashed, arrowhead=none]; 0537 { rank = same; View -> V_Set_Data -> V_Result -> V_Data_Changed -> View_EOL; } 0538 { rank = same; Model -> M_Set_Data -> M_Result -> Model_EOL; } 0539 0540 // Make sure top nodes are in a straight line. 0541 { View -> Model [style=invis]; } 0542 // And the bottom nodes. 0543 { View_EOL -> Model_EOL [style=invis]; } 0544 } 0545 @enddot 0546 //--> 0547 0548 Akonadi models only cache data from the Akonadi server. To update data on an Akonadi::Entity stored in a model, the model makes a request to the Akonadi server to update the model data. At that point the data cached internally in the model is not updated, so <tt>false</tt> is always returned from setData. If the request to update data on the Akonadi server is successful, an Akonadi::Monitor notifies the model that the data on that item has changed. The model then updates its internal data store and notifies the view that the data has changed. The details of how the Monitor communicates with akonadi are omitted for clarity. 0549 0550 <!-- 0551 TODO: Render this into PNG 0552 @dot 0553 digraph utmg { 0554 rankdir = LR; 0555 { node [label="",style=invis, height=0, width=0 ]; 0556 ETV_Set_Data; ETV_Result; ETV_Data_Changed; 0557 ETM_Set_Data; ETM_Result; ETM_Changed; 0558 M_Dummy_1; M_Changed; 0559 AS_Modify; 0560 } 0561 { node [shape=box, fillcolor=lightyellow, style=filled,fontsize=26]; 0562 EntityTreeView [label=":View"]; EntityTreeModel [label=":Model"]; Monitor [label=":Monitor"]; Akonadi_Server [label=":Akonadi"]; 0563 } 0564 { node [style=invis]; 0565 EntityTreeView_EOL; EntityTreeModel_EOL; Monitor_EOL; Akonadi_Server_EOL; 0566 } 0567 { 0568 { rank = same; ETV_Set_Data -> ETM_Set_Data [label="Set Data"]; } 0569 { rank = same; ETM_Result -> ETV_Result [label="False",arrowhead="vee", style="dashed"]; } 0570 { rank = same; ETM_Result -> M_Dummy_1 [style=invis]; } 0571 { rank = same; ETM_Set_Data -> AS_Modify [arrowhead="vee",taillabel="Modify Item", labeldistance=14.0, labelangle=10]; } 0572 { rank = same; M_Changed -> ETM_Changed [arrowhead="vee",label="Item Changed"]; } 0573 { rank = same; ETM_Changed -> ETV_Data_Changed [arrowhead="vee",label="Update View"]; } 0574 } 0575 0576 // Dashed Vertical lines for object lifetimes. 0577 edge [style=dashed, arrowhead=none]; 0578 { rank = same; EntityTreeView -> ETV_Set_Data -> ETV_Result -> ETV_Data_Changed -> EntityTreeView_EOL; } 0579 { rank = same; EntityTreeModel -> ETM_Set_Data -> ETM_Result -> ETM_Changed -> EntityTreeModel_EOL; } 0580 { rank = same; Monitor -> M_Dummy_1 -> M_Changed -> Monitor_EOL; } 0581 { rank = same; Akonadi_Server -> AS_Modify -> Akonadi_Server_EOL; } 0582 0583 // Make sure top nodes are in a straight line. 0584 { EntityTreeView -> EntityTreeModel -> Monitor -> Akonadi_Server [style=invis]; } 0585 // And the bottom nodes. 0586 { EntityTreeView_EOL -> EntityTreeModel_EOL -> Monitor_EOL -> Akonadi_Server_EOL [style=invis]; } 0587 } 0588 @enddot 0589 //--> 0590 0591 Similarly, in drag and drop operations, most models would update an internal data store and return <tt>true</tt> from dropMimeData if the drop is successful. 0592 0593 <!-- 0594 TODO: Render this to PNG 0595 @dot 0596 digraph utmg { 0597 rankdir = LR; 0598 { node [label="",style=invis, height=0, width=0 ]; 0599 Left_Phantom; Left_Phantom_DropEvent; 0600 V_DropEvent; V_Result; V_Data_Changed; V_Dummy_1; 0601 M_DropMimeData; M_Result; 0602 } 0603 { node [shape=box, fillcolor=lightyellow, style=filled,fontsize=26]; 0604 View [label=":View"]; Model [label=":Model"]; 0605 } 0606 { node [style=invis]; 0607 Left_Phantom_EOL; 0608 View_EOL; Model_EOL; 0609 } 0610 { 0611 Left_Phantom_DropEvent -> V_DropEvent [label="DropEvent"]; 0612 V_DropEvent -> M_DropMimeData [label="DropMimeData"]; 0613 M_Result -> V_Result [label="Success",arrowhead="vee", style="dashed"]; 0614 V_Result -> V_Data_Changed [label="Update View \n[Success = True]"]; 0615 } 0616 0617 // Dashed Vertical lines for object lifetimes. 0618 edge [style=dashed, arrowhead=none]; 0619 { rank = same; View -> V_DropEvent -> V_Result -> V_Dummy_1 -> V_Data_Changed -> View_EOL; } 0620 { rank = same; Model -> M_DropMimeData -> M_Result -> Model_EOL; } 0621 0622 //Phantom line 0623 { rank= same; Left_Phantom -> Left_Phantom_DropEvent -> Left_Phantom_EOL [style=invis]; } 0624 0625 // Make sure top nodes are in a straight line. 0626 { Left_Phantom -> View -> Model [style=invis]; } 0627 // And the bottom nodes. 0628 { Left_Phantom_EOL -> View_EOL -> Model_EOL [style=invis]; } 0629 } 0630 @enddot 0631 //---> 0632 0633 Akonadi models, for the same reason as above, always return false from dropMimeData. At the same time a suitable request is sent to the akonadi server to make the changes resulting from the drop (for example, moving or copying an entity, or adding a new entity to a collection etc). If that request is successful, the Akonadi::Monitor notifies the model that the data is changed and the model updates its internal store and notifies the view that the model data is changed. 0634 0635 <!-- 0636 TODO: Render this to PNG 0637 @dot 0638 digraph utmg { 0639 rankdir = LR; 0640 { node [label="",style=invis, height=0, width=0 ]; 0641 Left_Phantom; Left_Phantom_DropEvent; 0642 ETV_DropEvent; ETV_Result; ETV_Data_Changed; 0643 ETM_DropMimeData; ETM_Result; ETM_Changed; 0644 M_Dummy_1; M_Changed; 0645 AS_Modify; 0646 } 0647 { node [shape=box, fillcolor=lightyellow, style=filled,fontsize=26]; 0648 EntityTreeView [label=":View"]; 0649 EntityTreeModel [label=":Model"]; 0650 Monitor [label=":Monitor"]; 0651 Akonadi_Server [label=":Akonadi"]; 0652 } 0653 { node [style=invis]; 0654 Left_Phantom_EOL; 0655 EntityTreeView_EOL; EntityTreeModel_EOL; Monitor_EOL; Akonadi_Server_EOL; 0656 } 0657 0658 { 0659 { rank = same; Left_Phantom_DropEvent -> ETV_DropEvent [label="DropEvent"]; } 0660 { rank = same; ETV_DropEvent -> ETM_DropMimeData [label="Drop MimeData"]; } 0661 { rank = same; ETM_Result -> ETV_Result [label="False",arrowhead="vee", style="dashed"]; } 0662 { rank = same; ETM_Result -> M_Dummy_1 [style=invis]; } 0663 { rank = same; ETM_DropMimeData -> AS_Modify [arrowhead="vee",taillabel="Action", labeldistance=14.0, labelangle=10]; } 0664 { rank = same; M_Changed -> ETM_Changed [arrowhead="vee",label="Item Changed"]; } 0665 { rank = same; ETM_Changed -> ETV_Data_Changed [arrowhead="vee",label="Update View"]; } 0666 } 0667 0668 // Dashed Vertical lines for object lifetimes. 0669 edge [style=dashed, arrowhead=none]; 0670 { rank = same; EntityTreeView -> ETV_DropEvent -> ETV_Result -> ETV_Data_Changed -> EntityTreeView_EOL; } 0671 { rank = same; EntityTreeModel -> ETM_DropMimeData -> ETM_Result -> ETM_Changed -> EntityTreeModel_EOL; } 0672 { rank = same; Monitor -> M_Dummy_1 -> M_Changed -> Monitor_EOL; } 0673 { rank = same; Akonadi_Server -> AS_Modify -> Akonadi_Server_EOL; } 0674 0675 //Phantom line 0676 { rank= same; Left_Phantom -> Left_Phantom_DropEvent -> Left_Phantom_EOL [style=invis]; } 0677 0678 // Make sure top nodes are in a straight line. 0679 { Left_Phantom -> EntityTreeView -> EntityTreeModel -> Monitor -> Akonadi_Server [style=invis]; } 0680 // And the bottom nodes. 0681 { Left_Phantom_EOL -> EntityTreeView_EOL -> EntityTreeModel_EOL -> Monitor_EOL -> Akonadi_Server_EOL [style=invis]; } 0682 } 0683 @enddot 0684 //-->