• Skip to content
  • Skip to link menu
KDE API Reference
  • KDE API Reference
  • kdepim API Reference
  • KDE Home
  • Contact Us
 

kalarm

  • sources
  • kde-4.12
  • kdepim
  • kalarm
collectionmodel.cpp
Go to the documentation of this file.
1 /*
2  * collectionmodel.cpp - Akonadi collection models
3  * Program: kalarm
4  * Copyright © 2007-2012 by David Jarvie <djarvie@kde.org>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License along
17  * with this program; if not, write to the Free Software Foundation, Inc.,
18  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19  */
20 
21 #include "collectionmodel.h"
22 #include "autoqpointer.h"
23 #include "messagebox.h"
24 #include "preferences.h"
25 
26 #include <kalarmcal/collectionattribute.h>
27 #include <kalarmcal/compatibilityattribute.h>
28 
29 #include <akonadi/agentmanager.h>
30 #include <akonadi/collectiondialog.h>
31 #include <akonadi/collectiondeletejob.h>
32 #include <akonadi/collectionmodifyjob.h>
33 #include <akonadi/entitymimetypefiltermodel.h>
34 
35 #include <klocale.h>
36 
37 #include <QApplication>
38 #include <QMouseEvent>
39 #include <QHelpEvent>
40 #include <QToolTip>
41 #include <QTimer>
42 #include <QObject>
43 
44 using namespace Akonadi;
45 using namespace KAlarmCal;
46 
47 static Collection::Rights writableRights = Collection::CanChangeItem | Collection::CanCreateItem | Collection::CanDeleteItem;
48 
49 
50 /*=============================================================================
51 = Class: CollectionMimeTypeFilterModel
52 = Proxy model to filter AkonadiModel to restrict its contents to Collections,
53 = not Items, containing specified content mime types.
54 = It can optionally be restricted to writable and/or enabled Collections.
55 =============================================================================*/
56 class CollectionMimeTypeFilterModel : public Akonadi::EntityMimeTypeFilterModel
57 {
58  Q_OBJECT
59  public:
60  explicit CollectionMimeTypeFilterModel(QObject* parent = 0);
61  void setEventTypeFilter(CalEvent::Type);
62  void setFilterWritable(bool writable);
63  void setFilterEnabled(bool enabled);
64  Akonadi::Collection collection(int row) const;
65  Akonadi::Collection collection(const QModelIndex&) const;
66  QModelIndex collectionIndex(const Akonadi::Collection&) const;
67 
68  protected:
69  virtual bool filterAcceptsRow(int sourceRow, const QModelIndex& sourceParent) const;
70 
71  private:
72  CalEvent::Type mAlarmType; // collection content type contained in this model
73  bool mWritableOnly; // only include writable collections in this model
74  bool mEnabledOnly; // only include enabled collections in this model
75 };
76 
77 CollectionMimeTypeFilterModel::CollectionMimeTypeFilterModel(QObject* parent)
78  : EntityMimeTypeFilterModel(parent),
79  mAlarmType(CalEvent::EMPTY),
80  mWritableOnly(false),
81  mEnabledOnly(false)
82 {
83  addMimeTypeInclusionFilter(Collection::mimeType());
84  setHeaderGroup(EntityTreeModel::CollectionTreeHeaders);
85  setSourceModel(AkonadiModel::instance());
86 }
87 
88 void CollectionMimeTypeFilterModel::setEventTypeFilter(CalEvent::Type type)
89 {
90  if (type != mAlarmType)
91  {
92  mAlarmType = type;
93  invalidateFilter();
94  }
95 }
96 
97 void CollectionMimeTypeFilterModel::setFilterWritable(bool writable)
98 {
99  if (writable != mWritableOnly)
100  {
101  mWritableOnly = writable;
102  invalidateFilter();
103  }
104 }
105 
106 void CollectionMimeTypeFilterModel::setFilterEnabled(bool enabled)
107 {
108  if (enabled != mEnabledOnly)
109  {
110  emit layoutAboutToBeChanged();
111  mEnabledOnly = enabled;
112  invalidateFilter();
113  emit layoutChanged();
114  }
115 }
116 
117 bool CollectionMimeTypeFilterModel::filterAcceptsRow(int sourceRow, const QModelIndex& sourceParent) const
118 {
119  if (!EntityMimeTypeFilterModel::filterAcceptsRow(sourceRow, sourceParent))
120  return false;
121  AkonadiModel* model = AkonadiModel::instance();
122  QModelIndex ix = model->index(sourceRow, 0, sourceParent);
123  Collection collection = model->data(ix, AkonadiModel::CollectionRole).value<Collection>();
124  if (!AgentManager::self()->instance(collection.resource()).isValid())
125  return false;
126  if (!mWritableOnly && mAlarmType == CalEvent::EMPTY)
127  return true;
128  if (mWritableOnly && (collection.rights() & writableRights) != writableRights)
129  return false;
130  if (mAlarmType != CalEvent::EMPTY && !collection.contentMimeTypes().contains(CalEvent::mimeType(mAlarmType)))
131  return false;
132  if ((mWritableOnly || mEnabledOnly) && !collection.hasAttribute<CollectionAttribute>())
133  return false;
134  if (mWritableOnly && (!collection.hasAttribute<CompatibilityAttribute>()
135  || collection.attribute<CompatibilityAttribute>()->compatibility() != KACalendar::Current))
136  return false;
137  if (mEnabledOnly && !collection.attribute<CollectionAttribute>()->isEnabled(mAlarmType))
138  return false;
139  return true;
140 }
141 
142 /******************************************************************************
143 * Return the collection for a given row.
144 */
145 Collection CollectionMimeTypeFilterModel::collection(int row) const
146 {
147  return static_cast<AkonadiModel*>(sourceModel())->data(mapToSource(index(row, 0)), EntityTreeModel::CollectionRole).value<Collection>();
148 }
149 
150 Collection CollectionMimeTypeFilterModel::collection(const QModelIndex& index) const
151 {
152  return static_cast<AkonadiModel*>(sourceModel())->data(mapToSource(index), EntityTreeModel::CollectionRole).value<Collection>();
153 }
154 
155 QModelIndex CollectionMimeTypeFilterModel::collectionIndex(const Collection& collection) const
156 {
157  return mapFromSource(static_cast<AkonadiModel*>(sourceModel())->collectionIndex(collection));
158 }
159 
160 
161 /*=============================================================================
162 = Class: CollectionListModel
163 = Proxy model converting the AkonadiModel collection tree into a flat list.
164 = The model may be restricted to specified content mime types.
165 = It can optionally be restricted to writable and/or enabled Collections.
166 =============================================================================*/
167 
168 CollectionListModel::CollectionListModel(QObject* parent)
169  : KDescendantsProxyModel(parent),
170  mUseCollectionColour(true)
171 {
172  setSourceModel(new CollectionMimeTypeFilterModel(this));
173  setDisplayAncestorData(false);
174 }
175 
176 /******************************************************************************
177 * Return the collection for a given row.
178 */
179 Collection CollectionListModel::collection(int row) const
180 {
181  return data(index(row, 0), EntityTreeModel::CollectionRole).value<Collection>();
182 }
183 
184 Collection CollectionListModel::collection(const QModelIndex& index) const
185 {
186  return data(index, EntityTreeModel::CollectionRole).value<Collection>();
187 }
188 
189 QModelIndex CollectionListModel::collectionIndex(const Collection& collection) const
190 {
191  return mapFromSource(static_cast<CollectionMimeTypeFilterModel*>(sourceModel())->collectionIndex(collection));
192 }
193 
194 void CollectionListModel::setEventTypeFilter(CalEvent::Type type)
195 {
196  static_cast<CollectionMimeTypeFilterModel*>(sourceModel())->setEventTypeFilter(type);
197 }
198 
199 void CollectionListModel::setFilterWritable(bool writable)
200 {
201  static_cast<CollectionMimeTypeFilterModel*>(sourceModel())->setFilterWritable(writable);
202 }
203 
204 void CollectionListModel::setFilterEnabled(bool enabled)
205 {
206  static_cast<CollectionMimeTypeFilterModel*>(sourceModel())->setFilterEnabled(enabled);
207 }
208 
209 bool CollectionListModel::isDescendantOf(const QModelIndex& ancestor, const QModelIndex& descendant) const
210 {
211  Q_UNUSED(descendant);
212  return !ancestor.isValid();
213 }
214 
215 /******************************************************************************
216 * Return the data for a given role, for a specified item.
217 */
218 QVariant CollectionListModel::data(const QModelIndex& index, int role) const
219 {
220  switch (role)
221  {
222  case Qt::BackgroundRole:
223  if (!mUseCollectionColour)
224  role = AkonadiModel::BaseColourRole;
225  break;
226  default:
227  break;
228  }
229  return KDescendantsProxyModel::data(index, role);
230 }
231 
232 
233 /*=============================================================================
234 = Class: CollectionCheckListModel
235 = Proxy model providing a checkable list of all Collections. A Collection's
236 = checked status is equivalent to whether it is selected or not.
237 = An alarm type is specified, whereby Collections which are enabled for that
238 = alarm type are checked; Collections which do not contain that alarm type, or
239 = which are disabled for that alarm type, are unchecked.
240 =============================================================================*/
241 
242 CollectionListModel* CollectionCheckListModel::mModel = 0;
243 int CollectionCheckListModel::mInstanceCount = 0;
244 
245 CollectionCheckListModel::CollectionCheckListModel(CalEvent::Type type, QObject* parent)
246  : KCheckableProxyModel(parent),
247  mAlarmType(type)
248 {
249  ++mInstanceCount;
250  if (!mModel)
251  mModel = new CollectionListModel(0);
252  setSourceModel(mModel); // the source model is NOT filtered by alarm type
253  mSelectionModel = new QItemSelectionModel(mModel);
254  setSelectionModel(mSelectionModel);
255  connect(mSelectionModel, SIGNAL(selectionChanged(QItemSelection,QItemSelection)),
256  SLOT(selectionChanged(QItemSelection,QItemSelection)));
257  connect(mModel, SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int)), SIGNAL(layoutAboutToBeChanged()));
258  connect(mModel, SIGNAL(rowsInserted(QModelIndex,int,int)), SLOT(slotRowsInserted(QModelIndex,int,int)));
259  // This is probably needed to make CollectionFilterCheckListModel update
260  // (similarly to when rows are inserted).
261  connect(mModel, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)), SIGNAL(layoutAboutToBeChanged()));
262  connect(mModel, SIGNAL(rowsRemoved(QModelIndex,int,int)), SIGNAL(layoutChanged()));
263 
264  connect(AkonadiModel::instance(), SIGNAL(collectionStatusChanged(Akonadi::Collection,AkonadiModel::Change,QVariant,bool)),
265  SLOT(collectionStatusChanged(Akonadi::Collection,AkonadiModel::Change,QVariant,bool)));
266 
267  // Initialise checked status for all collections.
268  // Note that this is only necessary if the model is recreated after
269  // being deleted.
270  for (int row = 0, count = mModel->rowCount(); row < count; ++row)
271  setSelectionStatus(mModel->collection(row), mModel->index(row, 0));
272 }
273 
274 CollectionCheckListModel::~CollectionCheckListModel()
275 {
276  if (--mInstanceCount <= 0)
277  {
278  delete mModel;
279  mModel = 0;
280  }
281 }
282 
283 
284 /******************************************************************************
285 * Return the collection for a given row.
286 */
287 Collection CollectionCheckListModel::collection(int row) const
288 {
289  return mModel->collection(mapToSource(index(row, 0)));
290 }
291 
292 Collection CollectionCheckListModel::collection(const QModelIndex& index) const
293 {
294  return mModel->collection(mapToSource(index));
295 }
296 
297 /******************************************************************************
298 * Return model data for one index.
299 */
300 QVariant CollectionCheckListModel::data(const QModelIndex& index, int role) const
301 {
302  const Collection collection = mModel->collection(index);
303  if (collection.isValid())
304  {
305  // This is a Collection row
306  switch (role)
307  {
308  case Qt::ForegroundRole:
309  {
310  QString mimeType = CalEvent::mimeType(mAlarmType);
311  if (collection.contentMimeTypes().contains(mimeType))
312  return AkonadiModel::foregroundColor(collection, QStringList(mimeType));
313  break;
314  }
315  case Qt::FontRole:
316  {
317  if (!collection.hasAttribute<CollectionAttribute>()
318  || !AkonadiModel::isCompatible(collection))
319  break;
320  CollectionAttribute* attr = collection.attribute<CollectionAttribute>();
321  if (!attr->enabled())
322  break;
323  QStringList mimeTypes = collection.contentMimeTypes();
324  if (attr->isStandard(mAlarmType) && mimeTypes.contains(CalEvent::mimeType(mAlarmType)))
325  {
326  // It's the standard collection for a mime type
327  QFont font = qvariant_cast<QFont>(KCheckableProxyModel::data(index, role));
328  font.setBold(true);
329  return font;
330  }
331  break;
332  }
333  default:
334  break;
335  }
336  }
337  return KCheckableProxyModel::data(index, role);
338 }
339 
340 /******************************************************************************
341 * Set model data for one index.
342 * If the change is to disable a collection, check for eligibility and prevent
343 * the change if necessary.
344 */
345 bool CollectionCheckListModel::setData(const QModelIndex& index, const QVariant& value, int role)
346 {
347  if (role == Qt::CheckStateRole && static_cast<Qt::CheckState>(value.toInt()) != Qt::Checked)
348  {
349  // A collection is to be disabled.
350  const Collection collection = mModel->collection(index);
351  if (collection.isValid() && collection.hasAttribute<CollectionAttribute>())
352  {
353  const CollectionAttribute* attr = collection.attribute<CollectionAttribute>();
354  if (attr->isEnabled(mAlarmType))
355  {
356  QString errmsg;
357  QWidget* messageParent = qobject_cast<QWidget*>(QObject::parent());
358  if (attr->isStandard(mAlarmType)
359  && AkonadiModel::isCompatible(collection))
360  {
361  // It's the standard collection for some alarm type.
362  if (mAlarmType == CalEvent::ACTIVE)
363  {
364  errmsg = i18nc("@info", "You cannot disable your default active alarm calendar.");
365  }
366  else if (mAlarmType == CalEvent::ARCHIVED && Preferences::archivedKeepDays())
367  {
368  // Only allow the archived alarms standard collection to be disabled if
369  // we're not saving expired alarms.
370  errmsg = i18nc("@info", "You cannot disable your default archived alarm calendar "
371  "while expired alarms are configured to be kept.");
372  }
373  else if (KAMessageBox::warningContinueCancel(messageParent,
374  i18nc("@info", "Do you really want to disable your default calendar?"))
375  == KMessageBox::Cancel)
376  return false;
377  }
378  if (!errmsg.isEmpty())
379  {
380  KAMessageBox::sorry(messageParent, errmsg);
381  return false;
382  }
383  }
384  }
385  }
386  return KCheckableProxyModel::setData(index, value, role);
387 }
388 
389 /******************************************************************************
390 * Called when rows have been inserted into the model.
391 * Select or deselect them according to their enabled status.
392 */
393 void CollectionCheckListModel::slotRowsInserted(const QModelIndex& parent, int start, int end)
394 {
395  for (int row = start; row <= end; ++row)
396  {
397  const QModelIndex ix = mapToSource(index(row, 0, parent));
398  const Collection collection = mModel->collection(ix);
399  if (collection.isValid())
400  setSelectionStatus(collection, ix);
401  }
402  emit layoutChanged(); // this is needed to make CollectionFilterCheckListModel update
403 }
404 
405 /******************************************************************************
406 * Called when the user has ticked/unticked a collection to enable/disable it
407 * (or when the selection changes for any other reason).
408 */
409 void CollectionCheckListModel::selectionChanged(const QItemSelection& selected, const QItemSelection& deselected)
410 {
411  const QModelIndexList sel = selected.indexes();
412  foreach (const QModelIndex& ix, sel)
413  {
414  // Try to enable the collection, but untick it if not possible
415  if (!CollectionControlModel::setEnabled(mModel->collection(ix), mAlarmType, true))
416  mSelectionModel->select(ix, QItemSelectionModel::Deselect);
417  }
418  const QModelIndexList desel = deselected.indexes();
419  foreach (const QModelIndex& ix, desel)
420  CollectionControlModel::setEnabled(mModel->collection(ix), mAlarmType, false);
421 }
422 
423 /******************************************************************************
424 * Called when a collection parameter or status has changed.
425 * If the collection's alarm types have been reconfigured, ensure that the
426 * model views are updated to reflect this.
427 */
428 void CollectionCheckListModel::collectionStatusChanged(const Collection& collection, AkonadiModel::Change change, const QVariant&, bool inserted)
429 {
430  if (inserted || !collection.isValid())
431  return;
432  switch (change)
433  {
434  case AkonadiModel::Enabled:
435  kDebug() << "Enabled" << collection.id();
436  break;
437  case AkonadiModel::AlarmTypes:
438  kDebug() << "AlarmTypes" << collection.id();
439  break;
440  default:
441  return;
442  }
443  QModelIndex ix = mModel->collectionIndex(collection);
444  if (ix.isValid())
445  setSelectionStatus(collection, ix);
446  if (change == AkonadiModel::AlarmTypes)
447  emit collectionTypeChange(this);
448 }
449 
450 /******************************************************************************
451 * Select or deselect an index according to its enabled status.
452 */
453 void CollectionCheckListModel::setSelectionStatus(const Collection& collection, const QModelIndex& sourceIndex)
454 {
455  QItemSelectionModel::SelectionFlags sel = (collection.hasAttribute<CollectionAttribute>()
456  && collection.attribute<CollectionAttribute>()->isEnabled(mAlarmType))
457  ? QItemSelectionModel::Select : QItemSelectionModel::Deselect;
458  mSelectionModel->select(sourceIndex, sel);
459 }
460 
461 
462 /*=============================================================================
463 = Class: CollectionFilterCheckListModel
464 = Proxy model providing a checkable collection list. The model contains all
465 = alarm types, but returns only one type at any given time. The selected alarm
466 = type may be changed as desired.
467 =============================================================================*/
468 CollectionFilterCheckListModel::CollectionFilterCheckListModel(QObject* parent)
469  : QSortFilterProxyModel(parent),
470  mActiveModel(new CollectionCheckListModel(CalEvent::ACTIVE, this)),
471  mArchivedModel(new CollectionCheckListModel(CalEvent::ARCHIVED, this)),
472  mTemplateModel(new CollectionCheckListModel(CalEvent::TEMPLATE, this)),
473  mAlarmType(CalEvent::EMPTY)
474 {
475  setDynamicSortFilter(true);
476  connect(mActiveModel, SIGNAL(collectionTypeChange(CollectionCheckListModel*)), SLOT(collectionTypeChanged(CollectionCheckListModel*)));
477  connect(mArchivedModel, SIGNAL(collectionTypeChange(CollectionCheckListModel*)), SLOT(collectionTypeChanged(CollectionCheckListModel*)));
478  connect(mTemplateModel, SIGNAL(collectionTypeChange(CollectionCheckListModel*)), SLOT(collectionTypeChanged(CollectionCheckListModel*)));
479 }
480 
481 void CollectionFilterCheckListModel::setEventTypeFilter(CalEvent::Type type)
482 {
483  if (type != mAlarmType)
484  {
485  CollectionCheckListModel* newModel;
486  switch (type)
487  {
488  case CalEvent::ACTIVE: newModel = mActiveModel; break;
489  case CalEvent::ARCHIVED: newModel = mArchivedModel; break;
490  case CalEvent::TEMPLATE: newModel = mTemplateModel; break;
491  default:
492  return;
493  }
494  mAlarmType = type;
495  setSourceModel(newModel);
496  invalidate();
497  }
498 }
499 
500 /******************************************************************************
501 * Return the collection for a given row.
502 */
503 Collection CollectionFilterCheckListModel::collection(int row) const
504 {
505  return static_cast<CollectionCheckListModel*>(sourceModel())->collection(mapToSource(index(row, 0)));
506 }
507 
508 Collection CollectionFilterCheckListModel::collection(const QModelIndex& index) const
509 {
510  return static_cast<CollectionCheckListModel*>(sourceModel())->collection(mapToSource(index));
511 }
512 
513 QVariant CollectionFilterCheckListModel::data(const QModelIndex& index, int role) const
514 {
515  switch (role)
516  {
517  case Qt::ToolTipRole:
518  {
519  const Collection col = collection(index);
520  if (col.isValid())
521  return AkonadiModel::instance()->tooltip(col, mAlarmType);
522  break;
523  }
524  default:
525  break;
526  }
527  return QSortFilterProxyModel::data(index, role);
528 }
529 
530 bool CollectionFilterCheckListModel::filterAcceptsRow(int sourceRow, const QModelIndex& sourceParent) const
531 {
532  if (mAlarmType == CalEvent::EMPTY)
533  return true;
534  CollectionCheckListModel* model = static_cast<CollectionCheckListModel*>(sourceModel());
535  const Collection collection = model->collection(model->index(sourceRow, 0, sourceParent));
536  return collection.contentMimeTypes().contains(CalEvent::mimeType(mAlarmType));
537 }
538 
539 /******************************************************************************
540 * Called when a collection alarm type has changed.
541 * Ensure that the collection is removed from or added to the current model view.
542 */
543 void CollectionFilterCheckListModel::collectionTypeChanged(CollectionCheckListModel* model)
544 {
545  if (model == sourceModel())
546  invalidateFilter();
547 }
548 
549 
550 /*=============================================================================
551 = Class: CollectionView
552 = View displaying a list of collections.
553 =============================================================================*/
554 CollectionView::CollectionView(CollectionFilterCheckListModel* model, QWidget* parent)
555  : QListView(parent)
556 {
557  setModel(model);
558 }
559 
560 void CollectionView::setModel(QAbstractItemModel* model)
561 {
562  QListView::setModel(model);
563 }
564 
565 /******************************************************************************
566 * Return the collection for a given row.
567 */
568 Collection CollectionView::collection(int row) const
569 {
570  return static_cast<CollectionFilterCheckListModel*>(model())->collection(row);
571 }
572 
573 Collection CollectionView::collection(const QModelIndex& index) const
574 {
575  return static_cast<CollectionFilterCheckListModel*>(model())->collection(index);
576 }
577 
578 /******************************************************************************
579 * Called when a mouse button is released.
580 * Any currently selected collection is deselected.
581 */
582 void CollectionView::mouseReleaseEvent(QMouseEvent* e)
583 {
584  if (!indexAt(e->pos()).isValid())
585  clearSelection();
586  QListView::mouseReleaseEvent(e);
587 }
588 
589 /******************************************************************************
590 * Called when a ToolTip or WhatsThis event occurs.
591 */
592 bool CollectionView::viewportEvent(QEvent* e)
593 {
594  if (e->type() == QEvent::ToolTip && isActiveWindow())
595  {
596  QHelpEvent* he = static_cast<QHelpEvent*>(e);
597  QModelIndex index = indexAt(he->pos());
598  QVariant value = static_cast<CollectionFilterCheckListModel*>(model())->data(index, Qt::ToolTipRole);
599  if (qVariantCanConvert<QString>(value))
600  {
601  QString toolTip = value.toString();
602  int i = toolTip.indexOf(QLatin1Char('@'));
603  if (i > 0)
604  {
605  int j = toolTip.indexOf(QRegExp(QLatin1String("<(nl|br)"), Qt::CaseInsensitive), i + 1);
606  int k = toolTip.indexOf(QLatin1Char('@'), j);
607  QString name = toolTip.mid(i + 1, j - i - 1);
608  value = model()->data(index, Qt::FontRole);
609  QFontMetrics fm(qvariant_cast<QFont>(value).resolve(viewOptions().font));
610  int textWidth = fm.boundingRect(name).width() + 1;
611  const int margin = QApplication::style()->pixelMetric(QStyle::PM_FocusFrameHMargin) + 1;
612  QStyleOptionButton opt;
613  opt.QStyleOption::operator=(viewOptions());
614  opt.rect = rectForIndex(index);
615  int checkWidth = QApplication::style()->subElementRect(QStyle::SE_ViewItemCheckIndicator, &opt).width();
616  int left = spacing() + 3*margin + checkWidth + viewOptions().decorationSize.width(); // left offset of text
617  int right = left + textWidth;
618  if (left >= horizontalOffset() + spacing()
619  && right <= horizontalOffset() + width() - spacing() - 2*frameWidth())
620  {
621  // The whole of the collection name is already displayed,
622  // so omit it from the tooltip.
623  if (k > 0)
624  toolTip.remove(i, k + 1 - i);
625  }
626  else
627  {
628  toolTip.remove(k, 1);
629  toolTip.remove(i, 1);
630  }
631  }
632  QToolTip::showText(he->globalPos(), toolTip, this);
633  return true;
634  }
635  }
636  return QListView::viewportEvent(e);
637 }
638 
639 
640 /*=============================================================================
641 = Class: CollectionControlModel
642 = Proxy model to select which Collections will be enabled. Disabled Collections
643 = are not populated or monitored; their contents are ignored. The set of
644 = enabled Collections is stored in the config file's "Collections" group.
645 = Note that this model is not used directly for displaying - its purpose is to
646 = allow collections to be disabled, which will remove them from the other
647 = collection models.
648 =============================================================================*/
649 
650 CollectionControlModel* CollectionControlModel::mInstance = 0;
651 bool CollectionControlModel::mAskDestination = false;
652 
653 CollectionControlModel* CollectionControlModel::instance()
654 {
655  if (!mInstance)
656  mInstance = new CollectionControlModel(qApp);
657  return mInstance;
658 }
659 
660 CollectionControlModel::CollectionControlModel(QObject* parent)
661  : FavoriteCollectionsModel(AkonadiModel::instance(), KConfigGroup(KGlobal::config(), "Collections"), parent),
662  mPopulatedCheckLoop(0)
663 {
664  // Initialise the list of enabled collections
665  EntityMimeTypeFilterModel* filter = new EntityMimeTypeFilterModel(this);
666  filter->addMimeTypeInclusionFilter(Collection::mimeType());
667  filter->setSourceModel(AkonadiModel::instance());
668  Collection::List collections;
669  findEnabledCollections(filter, QModelIndex(), collections);
670  setCollections(collections);
671 
672  connect(AkonadiModel::instance(), SIGNAL(collectionStatusChanged(Akonadi::Collection,AkonadiModel::Change,QVariant,bool)),
673  SLOT(statusChanged(Akonadi::Collection,AkonadiModel::Change,QVariant,bool)));
674 #if KDE_IS_VERSION(4,9,80)
675  connect(AkonadiModel::instance(), SIGNAL(collectionTreeFetched(Akonadi::Collection::List)),
676  SLOT(collectionPopulated()));
677  connect(AkonadiModel::instance(), SIGNAL(collectionPopulated(Akonadi::Collection::Id)),
678  SLOT(collectionPopulated()));
679 #endif
680 }
681 
682 /******************************************************************************
683 * Recursive function to check all collections' enabled status, and to compile a
684 * list of all collections which have any alarm types enabled.
685 * Collections which duplicate the same backend storage are filtered out, to
686 * avoid crashes due to duplicate events in different resources.
687 */
688 void CollectionControlModel::findEnabledCollections(const EntityMimeTypeFilterModel* filter, const QModelIndex& parent, Collection::List& collections) const
689 {
690  AkonadiModel* model = AkonadiModel::instance();
691  for (int row = 0, count = filter->rowCount(parent); row < count; ++row)
692  {
693  const QModelIndex ix = filter->index(row, 0, parent);
694  const Collection collection = model->data(filter->mapToSource(ix), AkonadiModel::CollectionRole).value<Collection>();
695  if (!AgentManager::self()->instance(collection.resource()).isValid())
696  continue; // the collection doesn't belong to a resource, so omit it
697  CalEvent::Types enabled = !collection.hasAttribute<CollectionAttribute>() ? CalEvent::EMPTY
698  : collection.attribute<CollectionAttribute>()->enabled();
699  CalEvent::Types canEnable = checkTypesToEnable(collection, collections, enabled);
700  if (canEnable != enabled)
701  {
702  // There is another collection which uses the same backend
703  // storage. Disable alarm types enabled in the other collection.
704  if (!model->isCollectionBeingDeleted(collection.id()))
705  model->setData(model->collectionIndex(collection), static_cast<int>(canEnable), AkonadiModel::EnabledTypesRole);
706  }
707  if (canEnable)
708  collections += collection;
709  if (filter->rowCount(ix) > 0)
710  findEnabledCollections(filter, ix, collections);
711  }
712 }
713 
714 bool CollectionControlModel::isEnabled(const Collection& collection, CalEvent::Type type)
715 {
716  if (!collection.isValid() || !instance()->collections().contains(collection))
717  return false;
718  if (!AgentManager::self()->instance(collection.resource()).isValid())
719  {
720  // The collection doesn't belong to a resource, so it can't be used.
721  // Remove it from the list of collections.
722  instance()->removeCollection(collection);
723  return false;
724  }
725  Collection col = collection;
726  AkonadiModel::instance()->refresh(col); // update with latest data
727  return col.hasAttribute<CollectionAttribute>()
728  && col.attribute<CollectionAttribute>()->isEnabled(type);
729 }
730 
731 /******************************************************************************
732 * Enable or disable the specified alarm types for a collection.
733 * Reply = alarm types which can be enabled
734 */
735 CalEvent::Types CollectionControlModel::setEnabled(const Collection& collection, CalEvent::Types types, bool enabled)
736 {
737  kDebug() << "id:" << collection.id() << ", alarm types" << types << "->" << enabled;
738  if (!collection.isValid() || (!enabled && !instance()->collections().contains(collection)))
739  return CalEvent::EMPTY;
740  Collection col = collection;
741  AkonadiModel::instance()->refresh(col); // update with latest data
742  CalEvent::Types alarmTypes = !col.hasAttribute<CollectionAttribute>() ? CalEvent::EMPTY
743  : col.attribute<CollectionAttribute>()->enabled();
744  if (enabled)
745  alarmTypes |= static_cast<CalEvent::Types>(types & (CalEvent::ACTIVE | CalEvent::ARCHIVED | CalEvent::TEMPLATE));
746  else
747  alarmTypes &= ~types;
748 
749  return instance()->setEnabledStatus(collection, alarmTypes, false);
750 }
751 
752 /******************************************************************************
753 * Change the collection's enabled status.
754 * Add or remove the collection to/from the enabled list.
755 * Reply = alarm types which can be enabled
756 */
757 CalEvent::Types CollectionControlModel::setEnabledStatus(const Collection& collection, CalEvent::Types types, bool inserted)
758 {
759  kDebug() << "id:" << collection.id() << ", types=" << types;
760  CalEvent::Types disallowedStdTypes(0);
761  CalEvent::Types stdTypes(0);
762 
763  // Prevent the enabling of duplicate alarm types if another collection
764  // uses the same backend storage.
765  const Collection::List cols = collections();
766  CalEvent::Types canEnable = checkTypesToEnable(collection, cols, types);
767 
768  // Update the list of enabled collections
769  if (canEnable)
770  {
771  bool inList = false;
772  const Collection::List cols = collections();
773  foreach (const Collection& c, cols)
774  {
775  if (c.id() == collection.id())
776  {
777  inList = true;
778  break;
779  }
780  }
781  if (!inList)
782  {
783  // It's a new collection.
784  // Prevent duplicate standard collections being created for any alarm type.
785  stdTypes = collection.hasAttribute<CollectionAttribute>()
786  ? collection.attribute<CollectionAttribute>()->standard()
787  : CalEvent::EMPTY;
788  if (stdTypes)
789  {
790  foreach (const Collection& col, cols)
791  {
792  Collection c(col);
793  AkonadiModel::instance()->refresh(c); // update with latest data
794  if (c.isValid())
795  {
796  CalEvent::Types t = stdTypes & CalEvent::types(c.contentMimeTypes());
797  if (t)
798  {
799  if (c.hasAttribute<CollectionAttribute>()
800  && AkonadiModel::isCompatible(c))
801  {
802  disallowedStdTypes |= c.attribute<CollectionAttribute>()->standard() & t;
803  if (disallowedStdTypes == stdTypes)
804  break;
805  }
806  }
807  }
808  }
809  }
810  addCollection(collection);
811  }
812  }
813  else
814  removeCollection(collection);
815 
816  if (disallowedStdTypes || !inserted || canEnable != types)
817  {
818  // Update the collection's status
819  AkonadiModel* model = static_cast<AkonadiModel*>(sourceModel());
820  if (!model->isCollectionBeingDeleted(collection.id()))
821  {
822  QModelIndex ix = model->collectionIndex(collection);
823  if (!inserted || canEnable != types)
824  model->setData(ix, static_cast<int>(canEnable), AkonadiModel::EnabledTypesRole);
825  if (disallowedStdTypes)
826  model->setData(ix, static_cast<int>(stdTypes & ~disallowedStdTypes), AkonadiModel::IsStandardRole);
827  }
828  }
829  return canEnable;
830 }
831 
832 /******************************************************************************
833 * Called when a collection parameter or status has changed.
834 * If it's the enabled status, add or remove the collection to/from the enabled
835 * list.
836 */
837 void CollectionControlModel::statusChanged(const Collection& collection, AkonadiModel::Change change, const QVariant& value, bool inserted)
838 {
839  if (!collection.isValid())
840  return;
841 
842  switch (change)
843  {
844  case AkonadiModel::Enabled:
845  {
846  CalEvent::Types enabled = static_cast<CalEvent::Types>(value.toInt());
847  kDebug() << "id:" << collection.id() << ", enabled=" << enabled << ", inserted=" << inserted;
848  setEnabledStatus(collection, enabled, inserted);
849  break;
850  }
851  case AkonadiModel::ReadOnly:
852  {
853  bool readOnly = value.toBool();
854  kDebug() << "id:" << collection.id() << ", readOnly=" << readOnly;
855  if (readOnly)
856  {
857  // A read-only collection can't be the default for any alarm type
858  CalEvent::Types std = standardTypes(collection, false);
859  if (std != CalEvent::EMPTY)
860  {
861  Collection c(collection);
862  setStandard(c, CalEvent::Types(CalEvent::EMPTY));
863  QWidget* messageParent = qobject_cast<QWidget*>(QObject::parent());
864  bool singleType = true;
865  QString msg;
866  switch (std)
867  {
868  case CalEvent::ACTIVE:
869  msg = i18nc("@info", "The calendar <resource>%1</resource> has been made read-only. "
870  "This was the default calendar for active alarms.",
871  collection.name());
872  break;
873  case CalEvent::ARCHIVED:
874  msg = i18nc("@info", "The calendar <resource>%1</resource> has been made read-only. "
875  "This was the default calendar for archived alarms.",
876  collection.name());
877  break;
878  case CalEvent::TEMPLATE:
879  msg = i18nc("@info", "The calendar <resource>%1</resource> has been made read-only. "
880  "This was the default calendar for alarm templates.",
881  collection.name());
882  break;
883  default:
884  msg = i18nc("@info", "<para>The calendar <resource>%1</resource> has been made read-only. "
885  "This was the default calendar for:%2</para>"
886  "<para>Please select new default calendars.</para>",
887  collection.name(), typeListForDisplay(std));
888  singleType = false;
889  break;
890  }
891  if (singleType)
892  msg = i18nc("@info", "<para>%1</para><para>Please select a new default calendar.</para>", msg);
893  KAMessageBox::information(messageParent, msg);
894  }
895  }
896  break;
897  }
898  default:
899  break;
900  }
901 }
902 
903 /******************************************************************************
904 * Check which alarm types can be enabled for a specified collection.
905 * If the collection uses the same backend storage as another collection, any
906 * alarm types already enabled in the other collection must be disabled in this
907 * collection. This is to avoid duplicating events between different resources,
908 * which causes user confusion and annoyance, and causes crashes.
909 * Parameters:
910 * collection - must be up to date (using AkonadiModel::refresh() etc.)
911 * collections = list of collections to search for duplicates.
912 * types = alarm types to be enabled for the collection.
913 * Reply = alarm types which can be enabled without duplicating other collections.
914 */
915 CalEvent::Types CollectionControlModel::checkTypesToEnable(const Collection& collection, const Collection::List& collections, CalEvent::Types types)
916 {
917  types &= (CalEvent::ACTIVE | CalEvent::ARCHIVED | CalEvent::TEMPLATE);
918  if (types)
919  {
920  // At least on alarm type is to be enabled
921  KUrl location(collection.remoteId());
922  foreach (const Collection& c, collections)
923  {
924  if (c.id() != collection.id() && KUrl(c.remoteId()) == location)
925  {
926  // The collection duplicates the backend storage
927  // used by another enabled collection.
928  // N.B. don't refresh this collection - assume no change.
929  if (c.hasAttribute<CollectionAttribute>())
930  {
931  types &= ~c.attribute<CollectionAttribute>()->enabled();
932  if (!types)
933  break;
934  }
935  }
936  }
937  }
938  return types;
939 }
940 
941 /******************************************************************************
942 * Create a bulleted list of alarm types for insertion into <para>...</para>.
943 */
944 QString CollectionControlModel::typeListForDisplay(CalEvent::Types alarmTypes)
945 {
946  QString list;
947  if (alarmTypes & CalEvent::ACTIVE)
948  list += QLatin1String("<item>") + i18nc("@info/plain", "Active Alarms") + QLatin1String("</item>");
949  if (alarmTypes & CalEvent::ARCHIVED)
950  list += QLatin1String("<item>") + i18nc("@info/plain", "Archived Alarms") + QLatin1String("</item>");
951  if (alarmTypes & CalEvent::TEMPLATE)
952  list += QLatin1String("<item>") + i18nc("@info/plain", "Alarm Templates") + QLatin1String("</item>");
953  if (!list.isEmpty())
954  list = QLatin1String("<list>") + list + QLatin1String("</list>");
955  return list;
956 }
957 
958 /******************************************************************************
959 * Return whether a collection is both enabled and fully writable for a given
960 * alarm type.
961 * Optionally, the enabled status can be ignored.
962 * Reply: 1 = fully enabled and writable,
963 * 0 = enabled and writable except that backend calendar is in an old KAlarm format,
964 * -1 = not enabled, read-only, or incompatible format.
965 */
966 int CollectionControlModel::isWritableEnabled(const Akonadi::Collection& collection, CalEvent::Type type)
967 {
968  KACalendar::Compat format;
969  return isWritableEnabled(collection, type, format);
970 }
971 int CollectionControlModel::isWritableEnabled(const Akonadi::Collection& collection, CalEvent::Type type, KACalendar::Compat& format)
972 {
973  int writable = AkonadiModel::isWritable(collection, format);
974  if (writable == -1)
975  return -1;
976 
977  // Check the collection's enabled status
978  if (!instance()->collections().contains(collection)
979  || !collection.hasAttribute<CollectionAttribute>())
980  return -1;
981  if (!collection.attribute<CollectionAttribute>()->isEnabled(type))
982  return -1;
983  return writable;
984 }
985 
986 /******************************************************************************
987 * Return the standard collection for a specified mime type.
988 * If 'useDefault' is true and there is no standard collection, the only
989 * collection for the mime type will be returned as a default.
990 */
991 Collection CollectionControlModel::getStandard(CalEvent::Type type, bool useDefault)
992 {
993  QString mimeType = CalEvent::mimeType(type);
994  int defalt = -1;
995  Collection::List cols = instance()->collections();
996  for (int i = 0, count = cols.count(); i < count; ++i)
997  {
998  AkonadiModel::instance()->refresh(cols[i]); // update with latest data
999  if (cols[i].isValid()
1000  && cols[i].contentMimeTypes().contains(mimeType))
1001  {
1002  if (cols[i].hasAttribute<CollectionAttribute>()
1003  && (cols[i].attribute<CollectionAttribute>()->standard() & type)
1004  && AkonadiModel::isCompatible(cols[i]))
1005  return cols[i];
1006  defalt = (defalt == -1) ? i : -2;
1007  }
1008  }
1009  return (useDefault && defalt >= 0) ? cols[defalt] : Collection();
1010 }
1011 
1012 /******************************************************************************
1013 * Return whether a collection is the standard collection for a specified
1014 * mime type.
1015 */
1016 bool CollectionControlModel::isStandard(Akonadi::Collection& collection, CalEvent::Type type)
1017 {
1018  if (!instance()->collections().contains(collection))
1019  return false;
1020  AkonadiModel::instance()->refresh(collection); // update with latest data
1021  if (!collection.hasAttribute<CollectionAttribute>()
1022  || !AkonadiModel::isCompatible(collection))
1023  return false;
1024  return collection.attribute<CollectionAttribute>()->isStandard(type);
1025 }
1026 
1027 /******************************************************************************
1028 * Return the alarm type(s) for which a collection is the standard collection.
1029 */
1030 CalEvent::Types CollectionControlModel::standardTypes(const Collection& collection, bool useDefault)
1031 {
1032  if (!instance()->collections().contains(collection))
1033  return CalEvent::EMPTY;
1034  Collection col = collection;
1035  AkonadiModel::instance()->refresh(col); // update with latest data
1036  if (!AkonadiModel::isCompatible(col))
1037  return CalEvent::EMPTY;
1038  CalEvent::Types stdTypes = col.hasAttribute<CollectionAttribute>()
1039  ? col.attribute<CollectionAttribute>()->standard()
1040  : CalEvent::EMPTY;
1041  if (useDefault)
1042  {
1043  // Also return alarm types for which this is the only collection.
1044  CalEvent::Types wantedTypes = AkonadiModel::types(collection) & ~stdTypes;
1045  Collection::List cols = instance()->collections();
1046  for (int i = 0, count = cols.count(); wantedTypes && i < count; ++i)
1047  {
1048  if (cols[i] == col)
1049  continue;
1050  AkonadiModel::instance()->refresh(cols[i]); // update with latest data
1051  if (cols[i].isValid())
1052  wantedTypes &= ~AkonadiModel::types(cols[i]);
1053  }
1054  stdTypes |= wantedTypes;
1055  }
1056  return stdTypes;
1057 }
1058 
1059 /******************************************************************************
1060 * Set or clear a collection as the standard collection for a specified mime
1061 * type. If it is being set as standard, the standard status for the mime type
1062 * is cleared for all other collections.
1063 */
1064 void CollectionControlModel::setStandard(Akonadi::Collection& collection, CalEvent::Type type, bool standard)
1065 {
1066  AkonadiModel* model = AkonadiModel::instance();
1067  model->refresh(collection); // update with latest data
1068  if (!AkonadiModel::isCompatible(collection))
1069  standard = false; // the collection isn't writable
1070  if (standard)
1071  {
1072  // The collection is being set as standard.
1073  // Clear the 'standard' status for all other collections.
1074  Collection::List cols = instance()->collections();
1075  if (!cols.contains(collection))
1076  return;
1077  CalEvent::Types ctypes = collection.hasAttribute<CollectionAttribute>()
1078  ? collection.attribute<CollectionAttribute>()->standard() : CalEvent::EMPTY;
1079  if (ctypes & type)
1080  return; // it's already the standard collection for this type
1081  for (int i = 0, count = cols.count(); i < count; ++i)
1082  {
1083  CalEvent::Types types;
1084  if (cols[i] == collection)
1085  {
1086  cols[i] = collection; // update with latest data
1087  types = ctypes | type;
1088  }
1089  else
1090  {
1091  model->refresh(cols[i]); // update with latest data
1092  types = cols[i].hasAttribute<CollectionAttribute>()
1093  ? cols[i].attribute<CollectionAttribute>()->standard() : CalEvent::EMPTY;
1094  if (!(types & type))
1095  continue;
1096  types &= ~type;
1097  }
1098  QModelIndex index = model->collectionIndex(cols[i]);
1099  model->setData(index, static_cast<int>(types), AkonadiModel::IsStandardRole);
1100  }
1101  }
1102  else
1103  {
1104  // The 'standard' status is being cleared for the collection.
1105  // The collection doesn't have to be in this model's list of collections.
1106  CalEvent::Types types = collection.hasAttribute<CollectionAttribute>()
1107  ? collection.attribute<CollectionAttribute>()->standard() : CalEvent::EMPTY;
1108  if (types & type)
1109  {
1110  types &= ~type;
1111  QModelIndex index = model->collectionIndex(collection);
1112  model->setData(index, static_cast<int>(types), AkonadiModel::IsStandardRole);
1113  }
1114  }
1115 }
1116 
1117 /******************************************************************************
1118 * Set which mime types a collection is the standard collection for.
1119 * If it is being set as standard for any mime types, the standard status for
1120 * those mime types is cleared for all other collections.
1121 */
1122 void CollectionControlModel::setStandard(Akonadi::Collection& collection, CalEvent::Types types)
1123 {
1124  AkonadiModel* model = AkonadiModel::instance();
1125  model->refresh(collection); // update with latest data
1126  if (!AkonadiModel::isCompatible(collection))
1127  types = CalEvent::EMPTY; // the collection isn't writable
1128  if (types)
1129  {
1130  // The collection is being set as standard for at least one mime type.
1131  // Clear the 'standard' status for all other collections.
1132  Collection::List cols = instance()->collections();
1133  if (!cols.contains(collection))
1134  return;
1135  CalEvent::Types t = collection.hasAttribute<CollectionAttribute>()
1136  ? collection.attribute<CollectionAttribute>()->standard() : CalEvent::EMPTY;
1137  if (t == types)
1138  return; // there's no change to the collection's status
1139  for (int i = 0, count = cols.count(); i < count; ++i)
1140  {
1141  CalEvent::Types t;
1142  if (cols[i] == collection)
1143  {
1144  cols[i] = collection; // update with latest data
1145  t = types;
1146  }
1147  else
1148  {
1149  model->refresh(cols[i]); // update with latest data
1150  t = cols[i].hasAttribute<CollectionAttribute>()
1151  ? cols[i].attribute<CollectionAttribute>()->standard() : CalEvent::EMPTY;
1152  if (!(t & types))
1153  continue;
1154  t &= ~types;
1155  }
1156  QModelIndex index = model->collectionIndex(cols[i]);
1157  model->setData(index, static_cast<int>(t), AkonadiModel::IsStandardRole);
1158  }
1159  }
1160  else
1161  {
1162  // The 'standard' status is being cleared for the collection.
1163  // The collection doesn't have to be in this model's list of collections.
1164  if (collection.hasAttribute<CollectionAttribute>()
1165  && collection.attribute<CollectionAttribute>()->standard())
1166  {
1167  QModelIndex index = model->collectionIndex(collection);
1168  model->setData(index, static_cast<int>(types), AkonadiModel::IsStandardRole);
1169  }
1170  }
1171 }
1172 
1173 /******************************************************************************
1174 * Get the collection to use for storing an alarm.
1175 * Optionally, the standard collection for the alarm type is returned. If more
1176 * than one collection is a candidate, the user is prompted.
1177 */
1178 Collection CollectionControlModel::destination(CalEvent::Type type, QWidget* promptParent, bool noPrompt, bool* cancelled)
1179 {
1180  if (cancelled)
1181  *cancelled = false;
1182  Collection standard;
1183  if (type == CalEvent::EMPTY)
1184  return standard;
1185  standard = getStandard(type);
1186  // Archived alarms are always saved in the default resource,
1187  // else only prompt if necessary.
1188  if (type == CalEvent::ARCHIVED || noPrompt || (!mAskDestination && standard.isValid()))
1189  return standard;
1190 
1191  // Prompt for which collection to use
1192  CollectionListModel* model = new CollectionListModel(promptParent);
1193  model->setFilterWritable(true);
1194  model->setFilterEnabled(true);
1195  model->setEventTypeFilter(type);
1196  model->useCollectionColour(false);
1197  Collection col;
1198  switch (model->rowCount())
1199  {
1200  case 0:
1201  break;
1202  case 1:
1203  col = model->collection(0);
1204  break;
1205  default:
1206  {
1207  // Use AutoQPointer to guard against crash on application exit while
1208  // the dialogue is still open. It prevents double deletion (both on
1209  // deletion of 'promptParent', and on return from this function).
1210  AutoQPointer<CollectionDialog> dlg = new CollectionDialog(model, promptParent);
1211  dlg->setCaption(i18nc("@title:window", "Choose Calendar"));
1212  dlg->setDefaultCollection(standard);
1213  dlg->setMimeTypeFilter(QStringList(CalEvent::mimeType(type)));
1214  if (dlg->exec())
1215  col = dlg->selectedCollection();
1216  if (!col.isValid() && cancelled)
1217  *cancelled = true;
1218  }
1219  }
1220  return col;
1221 }
1222 
1223 /******************************************************************************
1224 * Return the enabled collections which contain a specified mime type.
1225 * If 'writable' is true, only writable collections are included.
1226 */
1227 Collection::List CollectionControlModel::enabledCollections(CalEvent::Type type, bool writable)
1228 {
1229  QString mimeType = CalEvent::mimeType(type);
1230  Collection::List cols = instance()->collections();
1231  Collection::List result;
1232  for (int i = 0, count = cols.count(); i < count; ++i)
1233  {
1234  AkonadiModel::instance()->refresh(cols[i]); // update with latest data
1235  if (cols[i].contentMimeTypes().contains(mimeType)
1236  && (!writable || ((cols[i].rights() & writableRights) == writableRights)))
1237  result += cols[i];
1238  }
1239  return result;
1240 }
1241 
1242 /******************************************************************************
1243 * Return the collection ID for a given resource ID.
1244 */
1245 Collection CollectionControlModel::collectionForResource(const QString& resourceId)
1246 {
1247  Collection::List cols = instance()->collections();
1248  for (int i = 0, count = cols.count(); i < count; ++i)
1249  {
1250  if (cols[i].resource() == resourceId)
1251  return cols[i];
1252  }
1253  return Collection();
1254 }
1255 
1256 #if KDE_IS_VERSION(4,9,80)
1257 /******************************************************************************
1258 * Return whether all enabled collections have been populated.
1259 */
1260 bool CollectionControlModel::isPopulated(Collection::Id colId)
1261 {
1262  AkonadiModel* model = AkonadiModel::instance();
1263  Collection::List cols = instance()->collections();
1264  for (int i = 0, count = cols.count(); i < count; ++i)
1265  {
1266  if ((colId == -1 || colId == cols[i].id())
1267  && !model->data(model->collectionIndex(cols[i].id()), AkonadiModel::IsPopulatedRole).toBool())
1268  {
1269  model->refresh(cols[i]); // update with latest data
1270  if (!cols[i].hasAttribute<CollectionAttribute>()
1271  || cols[i].attribute<CollectionAttribute>()->enabled() == CalEvent::EMPTY)
1272  return false;
1273  }
1274  }
1275  return true;
1276 }
1277 
1278 /******************************************************************************
1279 * Wait for one or all enabled collections to be populated.
1280 * Reply = true if successful.
1281 */
1282 bool CollectionControlModel::waitUntilPopulated(Collection::Id colId, int timeout)
1283 {
1284  kDebug();
1285  int result = 1;
1286  AkonadiModel* model = AkonadiModel::instance();
1287  while (!model->isCollectionTreeFetched()
1288  || !isPopulated(colId))
1289  {
1290  if (!mPopulatedCheckLoop)
1291  mPopulatedCheckLoop = new QEventLoop(this);
1292  if (timeout > 0)
1293  QTimer::singleShot(timeout * 1000, mPopulatedCheckLoop, SLOT(quit()));
1294  result = mPopulatedCheckLoop->exec();
1295  }
1296  delete mPopulatedCheckLoop;
1297  mPopulatedCheckLoop = 0;
1298  return result;
1299 }
1300 #endif
1301 
1302 /******************************************************************************
1303 * Exit from the populated event loop when a collection has been populated.
1304 */
1305 void CollectionControlModel::collectionPopulated()
1306 {
1307  if (mPopulatedCheckLoop)
1308  mPopulatedCheckLoop->exit(1);
1309 }
1310 
1311 /******************************************************************************
1312 * Return the data for a given role, for a specified item.
1313 */
1314 QVariant CollectionControlModel::data(const QModelIndex& index, int role) const
1315 {
1316  return sourceModel()->data(mapToSource(index), role);
1317 }
1318 
1319 #include "collectionmodel.moc"
1320 #include "moc_collectionmodel.cpp"
1321 
1322 // vim: et sw=4:
CollectionCheckListModel::collectionTypeChange
void collectionTypeChange(CollectionCheckListModel *)
CollectionControlModel::getStandard
static Akonadi::Collection getStandard(CalEvent::Type, bool useDefault=false)
Return the standard collection for a specified mime type.
Definition: collectionmodel.cpp:991
CollectionView::viewportEvent
virtual bool viewportEvent(QEvent *)
Definition: collectionmodel.cpp:592
KCheckableProxyModel
CollectionCheckListModel::CollectionCheckListModel
CollectionCheckListModel(CalEvent::Type, QObject *parent=0)
Definition: collectionmodel.cpp:245
KDescendantsProxyModel
CollectionControlModel::setStandard
static void setStandard(Akonadi::Collection &, CalEvent::Type, bool standard)
Set or clear a collection as the standard collection for a specified mime type.
Definition: collectionmodel.cpp:1064
CollectionControlModel::isWritableEnabled
static int isWritableEnabled(const Akonadi::Collection &, CalEvent::Type)
Return whether a collection is both enabled and fully writable for a given alarm type, i.e.
Definition: collectionmodel.cpp:966
CollectionListModel::collection
Akonadi::Collection collection(int row) const
Definition: collectionmodel.cpp:179
CollectionView::mouseReleaseEvent
virtual void mouseReleaseEvent(QMouseEvent *)
Definition: collectionmodel.cpp:582
CollectionListModel::isDescendantOf
virtual bool isDescendantOf(const QModelIndex &ancestor, const QModelIndex &descendant) const
Definition: collectionmodel.cpp:209
CollectionView::CollectionView
CollectionView(CollectionFilterCheckListModel *, QWidget *parent=0)
Definition: collectionmodel.cpp:554
AkonadiModel::IsStandardRole
Definition: akonadimodel.h:64
QWidget
CollectionCheckListModel
Definition: collectionmodel.h:78
CollectionControlModel::isStandard
static bool isStandard(Akonadi::Collection &, CalEvent::Type)
Return whether a collection is the standard collection for a specified mime type. ...
Definition: collectionmodel.cpp:1016
AkonadiModel::refresh
bool refresh(Akonadi::Collection &) const
Refresh the specified collection instance with up to date data.
Definition: akonadimodel.cpp:1744
AkonadiModel::Enabled
Definition: akonadimodel.h:52
CollectionListModel::collectionIndex
QModelIndex collectionIndex(const Akonadi::Collection &) const
Definition: collectionmodel.cpp:189
AkonadiModel::isCollectionBeingDeleted
bool isCollectionBeingDeleted(Akonadi::Collection::Id) const
Definition: akonadimodel.cpp:1069
AkonadiModel::collectionIndex
QModelIndex collectionIndex(Akonadi::Collection::Id id) const
Definition: akonadimodel.h:119
AkonadiModel::EnabledTypesRole
Definition: akonadimodel.h:61
QObject
CollectionControlModel::collectionForResource
static Akonadi::Collection collectionForResource(const QString &resourceId)
Return the collection ID for a given resource ID.
Definition: collectionmodel.cpp:1245
PreferencesBase::archivedKeepDays
static int archivedKeepDays()
Get Days to keep expired alarms.
Definition: kalarmconfig.h:926
CollectionControlModel::isEnabled
static bool isEnabled(const Akonadi::Collection &, CalEvent::Type)
Return whether a collection is enabled (and valid).
Definition: collectionmodel.cpp:714
CollectionControlModel::typeListForDisplay
static QString typeListForDisplay(CalEvent::Types)
Return a bulleted list of alarm types for inclusion in an i18n message.
Definition: collectionmodel.cpp:944
CollectionCheckListModel::collection
Akonadi::Collection collection(int row) const
Definition: collectionmodel.cpp:287
CollectionListModel::useCollectionColour
void useCollectionColour(bool use)
Definition: collectionmodel.h:58
CollectionControlModel::instance
static CollectionControlModel * instance()
Definition: collectionmodel.cpp:653
CollectionListModel::CollectionListModel
CollectionListModel(QObject *parent=0)
Definition: collectionmodel.cpp:168
autoqpointer.h
CollectionControlModel::destination
static Akonadi::Collection destination(CalEvent::Type, QWidget *promptParent=0, bool noPrompt=false, bool *cancelled=0)
Find the collection to be used to store an event of a given type.
Definition: collectionmodel.cpp:1178
CollectionControlModel::data
virtual QVariant data(const QModelIndex &, int role=Qt::DisplayRole) const
Definition: collectionmodel.cpp:1314
CollectionListModel::setFilterWritable
void setFilterWritable(bool writable)
Definition: collectionmodel.cpp:199
AkonadiModel::instance
static AkonadiModel * instance()
Definition: akonadimodel.cpp:83
CollectionControlModel
Definition: collectionmodel.h:168
CollectionControlModel::standardTypes
static CalEvent::Types standardTypes(const Akonadi::Collection &, bool useDefault=false)
Return the alarm type(s) for which a collection is the standard collection.
Definition: collectionmodel.cpp:1030
AkonadiModel::Change
Change
Definition: akonadimodel.h:52
AkonadiModel::tooltip
QString tooltip(const Akonadi::Collection &, CalEvent::Types) const
Get the tooltip for a collection.
Definition: akonadimodel.cpp:894
messagebox.h
CollectionFilterCheckListModel::setEventTypeFilter
void setEventTypeFilter(CalEvent::Type)
Definition: collectionmodel.cpp:481
AkonadiModel::AlarmTypes
Definition: akonadimodel.h:52
CollectionCheckListModel::data
virtual QVariant data(const QModelIndex &, int role=Qt::DisplayRole) const
Definition: collectionmodel.cpp:300
CollectionFilterCheckListModel::filterAcceptsRow
bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const
Definition: collectionmodel.cpp:530
AkonadiModel::data
virtual QVariant data(const QModelIndex &, int role=Qt::DisplayRole) const
Definition: akonadimodel.cpp:177
AutoQPointer
preferences.h
AkonadiModel::ReadOnly
Definition: akonadimodel.h:52
QSortFilterProxyModel
CollectionControlModel::enabledCollections
static Akonadi::Collection::List enabledCollections(CalEvent::Type, bool writable)
Return the enabled collections which contain a specified mime type.
Definition: collectionmodel.cpp:1227
QListView
collectionmodel.h
AkonadiModel::types
static CalEvent::Types types(const Akonadi::Collection &)
Definition: akonadimodel.cpp:1863
KAMessageBox::warningContinueCancel
static int warningContinueCancel(QWidget *parent, ButtonCode defaultButton, const QString &text, const QString &caption=QString(), const KGuiItem &buttonContinue=KStandardGuiItem::cont(), const QString &dontAskAgainName=QString())
AkonadiModel::isCompatible
static bool isCompatible(const Akonadi::Collection &)
Check whether a collection is stored in the current KAlarm calendar format.
Definition: akonadimodel.cpp:1821
KAMessageBox::sorry
static void sorry(QWidget *parent, const QString &text, const QString &caption=QString(), Options options=Options(Notify|WindowModal))
CollectionFilterCheckListModel::CollectionFilterCheckListModel
CollectionFilterCheckListModel(QObject *parent=0)
Definition: collectionmodel.cpp:468
AkonadiModel::BaseColourRole
Definition: akonadimodel.h:62
CollectionListModel::data
virtual QVariant data(const QModelIndex &, int role=Qt::DisplayRole) const
Definition: collectionmodel.cpp:218
CollectionView::collection
Akonadi::Collection collection(int row) const
Definition: collectionmodel.cpp:568
CollectionFilterCheckListModel
Definition: collectionmodel.h:113
CollectionFilterCheckListModel::collection
Akonadi::Collection collection(int row) const
Definition: collectionmodel.cpp:503
CollectionListModel
Definition: collectionmodel.h:50
CollectionCheckListModel::setData
virtual bool setData(const QModelIndex &, const QVariant &value, int role)
Definition: collectionmodel.cpp:345
CollectionFilterCheckListModel::data
virtual QVariant data(const QModelIndex &, int role=Qt::DisplayRole) const
Definition: collectionmodel.cpp:513
CollectionCheckListModel::~CollectionCheckListModel
~CollectionCheckListModel()
Definition: collectionmodel.cpp:274
CollectionView::setModel
virtual void setModel(QAbstractItemModel *)
Definition: collectionmodel.cpp:560
AkonadiModel::foregroundColor
static QColor foregroundColor(const Akonadi::Collection &, const QStringList &mimeTypes)
Get the foreground color for a collection, based on specified mime types.
Definition: akonadimodel.cpp:821
CollectionListModel::setEventTypeFilter
void setEventTypeFilter(CalEvent::Type)
Definition: collectionmodel.cpp:194
AkonadiModel
Definition: akonadimodel.h:48
AkonadiModel::isWritable
static int isWritable(const Akonadi::Collection &)
Return whether a collection is fully writable, i.e.
Definition: akonadimodel.cpp:1830
writableRights
static Collection::Rights writableRights
Definition: collectionmodel.cpp:47
CollectionListModel::setFilterEnabled
void setFilterEnabled(bool enabled)
Definition: collectionmodel.cpp:204
AkonadiModel::setData
virtual bool setData(const QModelIndex &, const QVariant &value, int role)
Definition: akonadimodel.cpp:496
KAMessageBox::information
static void information(QWidget *parent, const QString &text, const QString &caption=QString(), const QString &dontShowAgainName=QString(), Options options=Options(Notify|WindowModal))
CollectionControlModel::setEnabled
static CalEvent::Types setEnabled(const Akonadi::Collection &, CalEvent::Types, bool enabled)
Enable or disable a collection (if it is valid) for specified alarm types.
Definition: collectionmodel.cpp:735
This file is part of the KDE documentation.
Documentation copyright © 1996-2014 The KDE developers.
Generated on Tue Oct 14 2014 22:59:10 by doxygen 1.8.7 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

kalarm

Skip menu "kalarm"
  • Main Page
  • Namespace List
  • Namespace Members
  • Alphabetical List
  • Class List
  • Class Hierarchy
  • Class Members
  • File List
  • File Members

kdepim API Reference

Skip menu "kdepim API Reference"
  • akonadi_next
  • akregator
  • blogilo
  • calendarsupport
  • console
  •   kabcclient
  •   konsolekalendar
  • kaddressbook
  • kalarm
  •   lib
  • kdgantt2
  • kjots
  • kleopatra
  • kmail
  • knode
  • knotes
  • kontact
  • korgac
  • korganizer
  • ktimetracker
  • libkdepim
  • libkleo
  • libkpgp
  • mailcommon
  • messagelist
  • messageviewer

Search



Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal