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

kalarm

  • sources
  • kde-4.14
  • 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 KAlarm 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()); // select collections, not items
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  const QModelIndex ix = model->index(sourceRow, 0, sourceParent);
123  const 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  const 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  const CollectionAttribute* attr = collection.attribute<CollectionAttribute>();
321  if (!attr->enabled())
322  break;
323  const 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  const 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  const 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  const 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  const QHelpEvent* he = static_cast<QHelpEvent*>(e);
597  const 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  const QString name = toolTip.mid(i + 1, j - i - 1);
608  value = model()->data(index, Qt::FontRole);
609  const 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  connect(AkonadiModel::instance(), SIGNAL(serverStopped()), SLOT(reset()));
681 }
682 
683 /******************************************************************************
684 * Recursive function to check all collections' enabled status, and to compile a
685 * list of all collections which have any alarm types enabled.
686 * Collections which duplicate the same backend storage are filtered out, to
687 * avoid crashes due to duplicate events in different resources.
688 */
689 void CollectionControlModel::findEnabledCollections(const EntityMimeTypeFilterModel* filter, const QModelIndex& parent, Collection::List& collections) const
690 {
691  AkonadiModel* model = AkonadiModel::instance();
692  for (int row = 0, count = filter->rowCount(parent); row < count; ++row)
693  {
694  const QModelIndex ix = filter->index(row, 0, parent);
695  const Collection collection = model->data(filter->mapToSource(ix), AkonadiModel::CollectionRole).value<Collection>();
696  if (!AgentManager::self()->instance(collection.resource()).isValid())
697  continue; // the collection doesn't belong to a resource, so omit it
698  const CalEvent::Types enabled = !collection.hasAttribute<CollectionAttribute>() ? CalEvent::EMPTY
699  : collection.attribute<CollectionAttribute>()->enabled();
700  const CalEvent::Types canEnable = checkTypesToEnable(collection, collections, enabled);
701  if (canEnable != enabled)
702  {
703  // There is another collection which uses the same backend
704  // storage. Disable alarm types enabled in the other collection.
705  if (!model->isCollectionBeingDeleted(collection.id()))
706  model->setData(model->collectionIndex(collection), static_cast<int>(canEnable), AkonadiModel::EnabledTypesRole);
707  }
708  if (canEnable)
709  collections += collection;
710  if (filter->rowCount(ix) > 0)
711  findEnabledCollections(filter, ix, collections);
712  }
713 }
714 
715 bool CollectionControlModel::isEnabled(const Collection& collection, CalEvent::Type type)
716 {
717  if (!collection.isValid() || !instance()->collections().contains(collection))
718  return false;
719  if (!AgentManager::self()->instance(collection.resource()).isValid())
720  {
721  // The collection doesn't belong to a resource, so it can't be used.
722  // Remove it from the list of collections.
723  instance()->removeCollection(collection);
724  return false;
725  }
726  Collection col = collection;
727  AkonadiModel::instance()->refresh(col); // update with latest data
728  return col.hasAttribute<CollectionAttribute>()
729  && col.attribute<CollectionAttribute>()->isEnabled(type);
730 }
731 
732 /******************************************************************************
733 * Enable or disable the specified alarm types for a collection.
734 * Reply = alarm types which can be enabled
735 */
736 CalEvent::Types CollectionControlModel::setEnabled(const Collection& collection, CalEvent::Types types, bool enabled)
737 {
738  kDebug() << "id:" << collection.id() << ", alarm types" << types << "->" << enabled;
739  if (!collection.isValid() || (!enabled && !instance()->collections().contains(collection)))
740  return CalEvent::EMPTY;
741  Collection col = collection;
742  AkonadiModel::instance()->refresh(col); // update with latest data
743  CalEvent::Types alarmTypes = !col.hasAttribute<CollectionAttribute>() ? CalEvent::EMPTY
744  : col.attribute<CollectionAttribute>()->enabled();
745  if (enabled)
746  alarmTypes |= static_cast<CalEvent::Types>(types & (CalEvent::ACTIVE | CalEvent::ARCHIVED | CalEvent::TEMPLATE));
747  else
748  alarmTypes &= ~types;
749 
750  return instance()->setEnabledStatus(collection, alarmTypes, false);
751 }
752 
753 /******************************************************************************
754 * Change the collection's enabled status.
755 * Add or remove the collection to/from the enabled list.
756 * Reply = alarm types which can be enabled
757 */
758 CalEvent::Types CollectionControlModel::setEnabledStatus(const Collection& collection, CalEvent::Types types, bool inserted)
759 {
760  kDebug() << "id:" << collection.id() << ", types=" << types;
761  CalEvent::Types disallowedStdTypes(0);
762  CalEvent::Types stdTypes(0);
763 
764  // Prevent the enabling of duplicate alarm types if another collection
765  // uses the same backend storage.
766  const Collection::List cols = collections();
767  const CalEvent::Types canEnable = checkTypesToEnable(collection, cols, types);
768 
769  // Update the list of enabled collections
770  if (canEnable)
771  {
772  bool inList = false;
773  const Collection::List cols = collections();
774  foreach (const Collection& c, cols)
775  {
776  if (c.id() == collection.id())
777  {
778  inList = true;
779  break;
780  }
781  }
782  if (!inList)
783  {
784  // It's a new collection.
785  // Prevent duplicate standard collections being created for any alarm type.
786  stdTypes = collection.hasAttribute<CollectionAttribute>()
787  ? collection.attribute<CollectionAttribute>()->standard()
788  : CalEvent::EMPTY;
789  if (stdTypes)
790  {
791  foreach (const Collection& col, cols)
792  {
793  Collection c(col);
794  AkonadiModel::instance()->refresh(c); // update with latest data
795  if (c.isValid())
796  {
797  const CalEvent::Types t = stdTypes & CalEvent::types(c.contentMimeTypes());
798  if (t)
799  {
800  if (c.hasAttribute<CollectionAttribute>()
801  && AkonadiModel::isCompatible(c))
802  {
803  disallowedStdTypes |= c.attribute<CollectionAttribute>()->standard() & t;
804  if (disallowedStdTypes == stdTypes)
805  break;
806  }
807  }
808  }
809  }
810  }
811  addCollection(collection);
812  }
813  }
814  else
815  removeCollection(collection);
816 
817  if (disallowedStdTypes || !inserted || canEnable != types)
818  {
819  // Update the collection's status
820  AkonadiModel* model = static_cast<AkonadiModel*>(sourceModel());
821  if (!model->isCollectionBeingDeleted(collection.id()))
822  {
823  const QModelIndex ix = model->collectionIndex(collection);
824  if (!inserted || canEnable != types)
825  model->setData(ix, static_cast<int>(canEnable), AkonadiModel::EnabledTypesRole);
826  if (disallowedStdTypes)
827  model->setData(ix, static_cast<int>(stdTypes & ~disallowedStdTypes), AkonadiModel::IsStandardRole);
828  }
829  }
830  return canEnable;
831 }
832 
833 /******************************************************************************
834 * Called when a collection parameter or status has changed.
835 * If it's the enabled status, add or remove the collection to/from the enabled
836 * list.
837 */
838 void CollectionControlModel::statusChanged(const Collection& collection, AkonadiModel::Change change, const QVariant& value, bool inserted)
839 {
840  if (!collection.isValid())
841  return;
842 
843  switch (change)
844  {
845  case AkonadiModel::Enabled:
846  {
847  const CalEvent::Types enabled = static_cast<CalEvent::Types>(value.toInt());
848  kDebug() << "id:" << collection.id() << ", enabled=" << enabled << ", inserted=" << inserted;
849  setEnabledStatus(collection, enabled, inserted);
850  break;
851  }
852  case AkonadiModel::ReadOnly:
853  {
854  bool readOnly = value.toBool();
855  kDebug() << "id:" << collection.id() << ", readOnly=" << readOnly;
856  if (readOnly)
857  {
858  // A read-only collection can't be the default for any alarm type
859  const CalEvent::Types std = standardTypes(collection, false);
860  if (std != CalEvent::EMPTY)
861  {
862  Collection c(collection);
863  setStandard(c, CalEvent::Types(CalEvent::EMPTY));
864  QWidget* messageParent = qobject_cast<QWidget*>(QObject::parent());
865  bool singleType = true;
866  QString msg;
867  switch (std)
868  {
869  case CalEvent::ACTIVE:
870  msg = i18nc("@info", "The calendar <resource>%1</resource> has been made read-only. "
871  "This was the default calendar for active alarms.",
872  collection.name());
873  break;
874  case CalEvent::ARCHIVED:
875  msg = i18nc("@info", "The calendar <resource>%1</resource> has been made read-only. "
876  "This was the default calendar for archived alarms.",
877  collection.name());
878  break;
879  case CalEvent::TEMPLATE:
880  msg = i18nc("@info", "The calendar <resource>%1</resource> has been made read-only. "
881  "This was the default calendar for alarm templates.",
882  collection.name());
883  break;
884  default:
885  msg = i18nc("@info", "<para>The calendar <resource>%1</resource> has been made read-only. "
886  "This was the default calendar for:%2</para>"
887  "<para>Please select new default calendars.</para>",
888  collection.name(), typeListForDisplay(std));
889  singleType = false;
890  break;
891  }
892  if (singleType)
893  msg = i18nc("@info", "<para>%1</para><para>Please select a new default calendar.</para>", msg);
894  KAMessageBox::information(messageParent, msg);
895  }
896  }
897  break;
898  }
899  default:
900  break;
901  }
902 }
903 
904 /******************************************************************************
905 * Check which alarm types can be enabled for a specified collection.
906 * If the collection uses the same backend storage as another collection, any
907 * alarm types already enabled in the other collection must be disabled in this
908 * collection. This is to avoid duplicating events between different resources,
909 * which causes user confusion and annoyance, and causes crashes.
910 * Parameters:
911 * collection - must be up to date (using AkonadiModel::refresh() etc.)
912 * collections = list of collections to search for duplicates.
913 * types = alarm types to be enabled for the collection.
914 * Reply = alarm types which can be enabled without duplicating other collections.
915 */
916 CalEvent::Types CollectionControlModel::checkTypesToEnable(const Collection& collection, const Collection::List& collections, CalEvent::Types types)
917 {
918  types &= (CalEvent::ACTIVE | CalEvent::ARCHIVED | CalEvent::TEMPLATE);
919  if (types)
920  {
921  // At least on alarm type is to be enabled
922  const KUrl location(collection.remoteId());
923  foreach (const Collection& c, collections)
924  {
925  if (c.id() != collection.id() && KUrl(c.remoteId()) == location)
926  {
927  // The collection duplicates the backend storage
928  // used by another enabled collection.
929  // N.B. don't refresh this collection - assume no change.
930  kDebug() << "Collection" << c.id() << "duplicates backend for" << collection.id();
931  if (c.hasAttribute<CollectionAttribute>())
932  {
933  types &= ~c.attribute<CollectionAttribute>()->enabled();
934  if (!types)
935  break;
936  }
937  }
938  }
939  }
940  return types;
941 }
942 
943 /******************************************************************************
944 * Create a bulleted list of alarm types for insertion into <para>...</para>.
945 */
946 QString CollectionControlModel::typeListForDisplay(CalEvent::Types alarmTypes)
947 {
948  QString list;
949  if (alarmTypes & CalEvent::ACTIVE)
950  list += QLatin1String("<item>") + i18nc("@info/plain", "Active Alarms") + QLatin1String("</item>");
951  if (alarmTypes & CalEvent::ARCHIVED)
952  list += QLatin1String("<item>") + i18nc("@info/plain", "Archived Alarms") + QLatin1String("</item>");
953  if (alarmTypes & CalEvent::TEMPLATE)
954  list += QLatin1String("<item>") + i18nc("@info/plain", "Alarm Templates") + QLatin1String("</item>");
955  if (!list.isEmpty())
956  list = QLatin1String("<list>") + list + QLatin1String("</list>");
957  return list;
958 }
959 
960 /******************************************************************************
961 * Return whether a collection is both enabled and fully writable for a given
962 * alarm type.
963 * Optionally, the enabled status can be ignored.
964 * Reply: 1 = fully enabled and writable,
965 * 0 = enabled and writable except that backend calendar is in an old KAlarm format,
966 * -1 = not enabled, read-only, or incompatible format.
967 */
968 int CollectionControlModel::isWritableEnabled(const Akonadi::Collection& collection, CalEvent::Type type)
969 {
970  KACalendar::Compat format;
971  return isWritableEnabled(collection, type, format);
972 }
973 int CollectionControlModel::isWritableEnabled(const Akonadi::Collection& collection, CalEvent::Type type, KACalendar::Compat& format)
974 {
975  int writable = AkonadiModel::isWritable(collection, format);
976  if (writable == -1)
977  return -1;
978 
979  // Check the collection's enabled status
980  if (!instance()->collections().contains(collection)
981  || !collection.hasAttribute<CollectionAttribute>())
982  return -1;
983  if (!collection.attribute<CollectionAttribute>()->isEnabled(type))
984  return -1;
985  return writable;
986 }
987 
988 /******************************************************************************
989 * Return the standard collection for a specified mime type.
990 * If 'useDefault' is true and there is no standard collection, the only
991 * collection for the mime type will be returned as a default.
992 */
993 Collection CollectionControlModel::getStandard(CalEvent::Type type, bool useDefault)
994 {
995  const QString mimeType = CalEvent::mimeType(type);
996  int defalt = -1;
997  Collection::List cols = instance()->collections();
998  for (int i = 0, count = cols.count(); i < count; ++i)
999  {
1000  AkonadiModel::instance()->refresh(cols[i]); // update with latest data
1001  if (cols[i].isValid()
1002  && cols[i].contentMimeTypes().contains(mimeType))
1003  {
1004  if (cols[i].hasAttribute<CollectionAttribute>()
1005  && (cols[i].attribute<CollectionAttribute>()->standard() & type)
1006  && AkonadiModel::isCompatible(cols[i]))
1007  return cols[i];
1008  defalt = (defalt == -1) ? i : -2;
1009  }
1010  }
1011  return (useDefault && defalt >= 0) ? cols[defalt] : Collection();
1012 }
1013 
1014 /******************************************************************************
1015 * Return whether a collection is the standard collection for a specified
1016 * mime type.
1017 */
1018 bool CollectionControlModel::isStandard(Akonadi::Collection& collection, CalEvent::Type type)
1019 {
1020  if (!instance()->collections().contains(collection))
1021  return false;
1022  AkonadiModel::instance()->refresh(collection); // update with latest data
1023  if (!collection.hasAttribute<CollectionAttribute>()
1024  || !AkonadiModel::isCompatible(collection))
1025  return false;
1026  return collection.attribute<CollectionAttribute>()->isStandard(type);
1027 }
1028 
1029 /******************************************************************************
1030 * Return the alarm type(s) for which a collection is the standard collection.
1031 */
1032 CalEvent::Types CollectionControlModel::standardTypes(const Collection& collection, bool useDefault)
1033 {
1034  if (!instance()->collections().contains(collection))
1035  return CalEvent::EMPTY;
1036  Collection col = collection;
1037  AkonadiModel::instance()->refresh(col); // update with latest data
1038  if (!AkonadiModel::isCompatible(col))
1039  return CalEvent::EMPTY;
1040  CalEvent::Types stdTypes = col.hasAttribute<CollectionAttribute>()
1041  ? col.attribute<CollectionAttribute>()->standard()
1042  : CalEvent::EMPTY;
1043  if (useDefault)
1044  {
1045  // Also return alarm types for which this is the only collection.
1046  CalEvent::Types wantedTypes = AkonadiModel::types(collection) & ~stdTypes;
1047  Collection::List cols = instance()->collections();
1048  for (int i = 0, count = cols.count(); wantedTypes && i < count; ++i)
1049  {
1050  if (cols[i] == col)
1051  continue;
1052  AkonadiModel::instance()->refresh(cols[i]); // update with latest data
1053  if (cols[i].isValid())
1054  wantedTypes &= ~AkonadiModel::types(cols[i]);
1055  }
1056  stdTypes |= wantedTypes;
1057  }
1058  return stdTypes;
1059 }
1060 
1061 /******************************************************************************
1062 * Set or clear a collection as the standard collection for a specified mime
1063 * type. If it is being set as standard, the standard status for the mime type
1064 * is cleared for all other collections.
1065 */
1066 void CollectionControlModel::setStandard(Akonadi::Collection& collection, CalEvent::Type type, bool standard)
1067 {
1068  AkonadiModel* model = AkonadiModel::instance();
1069  model->refresh(collection); // update with latest data
1070  if (!AkonadiModel::isCompatible(collection))
1071  standard = false; // the collection isn't writable
1072  if (standard)
1073  {
1074  // The collection is being set as standard.
1075  // Clear the 'standard' status for all other collections.
1076  Collection::List cols = instance()->collections();
1077  if (!cols.contains(collection))
1078  return;
1079  const CalEvent::Types ctypes = collection.hasAttribute<CollectionAttribute>()
1080  ? collection.attribute<CollectionAttribute>()->standard() : CalEvent::EMPTY;
1081  if (ctypes & type)
1082  return; // it's already the standard collection for this type
1083  for (int i = 0, count = cols.count(); i < count; ++i)
1084  {
1085  CalEvent::Types types;
1086  if (cols[i] == collection)
1087  {
1088  cols[i] = collection; // update with latest data
1089  types = ctypes | type;
1090  }
1091  else
1092  {
1093  model->refresh(cols[i]); // update with latest data
1094  types = cols[i].hasAttribute<CollectionAttribute>()
1095  ? cols[i].attribute<CollectionAttribute>()->standard() : CalEvent::EMPTY;
1096  if (!(types & type))
1097  continue;
1098  types &= ~type;
1099  }
1100  const QModelIndex index = model->collectionIndex(cols[i]);
1101  model->setData(index, static_cast<int>(types), AkonadiModel::IsStandardRole);
1102  }
1103  }
1104  else
1105  {
1106  // The 'standard' status is being cleared for the collection.
1107  // The collection doesn't have to be in this model's list of collections.
1108  CalEvent::Types types = collection.hasAttribute<CollectionAttribute>()
1109  ? collection.attribute<CollectionAttribute>()->standard() : CalEvent::EMPTY;
1110  if (types & type)
1111  {
1112  types &= ~type;
1113  const QModelIndex index = model->collectionIndex(collection);
1114  model->setData(index, static_cast<int>(types), AkonadiModel::IsStandardRole);
1115  }
1116  }
1117 }
1118 
1119 /******************************************************************************
1120 * Set which mime types a collection is the standard collection for.
1121 * If it is being set as standard for any mime types, the standard status for
1122 * those mime types is cleared for all other collections.
1123 */
1124 void CollectionControlModel::setStandard(Akonadi::Collection& collection, CalEvent::Types types)
1125 {
1126  AkonadiModel* model = AkonadiModel::instance();
1127  model->refresh(collection); // update with latest data
1128  if (!AkonadiModel::isCompatible(collection))
1129  types = CalEvent::EMPTY; // the collection isn't writable
1130  if (types)
1131  {
1132  // The collection is being set as standard for at least one mime type.
1133  // Clear the 'standard' status for all other collections.
1134  Collection::List cols = instance()->collections();
1135  if (!cols.contains(collection))
1136  return;
1137  const CalEvent::Types t = collection.hasAttribute<CollectionAttribute>()
1138  ? collection.attribute<CollectionAttribute>()->standard() : CalEvent::EMPTY;
1139  if (t == types)
1140  return; // there's no change to the collection's status
1141  for (int i = 0, count = cols.count(); i < count; ++i)
1142  {
1143  CalEvent::Types t;
1144  if (cols[i] == collection)
1145  {
1146  cols[i] = collection; // update with latest data
1147  t = types;
1148  }
1149  else
1150  {
1151  model->refresh(cols[i]); // update with latest data
1152  t = cols[i].hasAttribute<CollectionAttribute>()
1153  ? cols[i].attribute<CollectionAttribute>()->standard() : CalEvent::EMPTY;
1154  if (!(t & types))
1155  continue;
1156  t &= ~types;
1157  }
1158  const QModelIndex index = model->collectionIndex(cols[i]);
1159  model->setData(index, static_cast<int>(t), AkonadiModel::IsStandardRole);
1160  }
1161  }
1162  else
1163  {
1164  // The 'standard' status is being cleared for the collection.
1165  // The collection doesn't have to be in this model's list of collections.
1166  if (collection.hasAttribute<CollectionAttribute>()
1167  && collection.attribute<CollectionAttribute>()->standard())
1168  {
1169  const QModelIndex index = model->collectionIndex(collection);
1170  model->setData(index, static_cast<int>(types), AkonadiModel::IsStandardRole);
1171  }
1172  }
1173 }
1174 
1175 /******************************************************************************
1176 * Get the collection to use for storing an alarm.
1177 * Optionally, the standard collection for the alarm type is returned. If more
1178 * than one collection is a candidate, the user is prompted.
1179 */
1180 Collection CollectionControlModel::destination(CalEvent::Type type, QWidget* promptParent, bool noPrompt, bool* cancelled)
1181 {
1182  if (cancelled)
1183  *cancelled = false;
1184  Collection standard;
1185  if (type == CalEvent::EMPTY)
1186  return standard;
1187  standard = getStandard(type);
1188  // Archived alarms are always saved in the default resource,
1189  // else only prompt if necessary.
1190  if (type == CalEvent::ARCHIVED || noPrompt || (!mAskDestination && standard.isValid()))
1191  return standard;
1192 
1193  // Prompt for which collection to use
1194  CollectionListModel* model = new CollectionListModel(promptParent);
1195  model->setFilterWritable(true);
1196  model->setFilterEnabled(true);
1197  model->setEventTypeFilter(type);
1198  model->useCollectionColour(false);
1199  Collection col;
1200  switch (model->rowCount())
1201  {
1202  case 0:
1203  break;
1204  case 1:
1205  col = model->collection(0);
1206  break;
1207  default:
1208  {
1209  // Use AutoQPointer to guard against crash on application exit while
1210  // the dialogue is still open. It prevents double deletion (both on
1211  // deletion of 'promptParent', and on return from this function).
1212  AutoQPointer<CollectionDialog> dlg = new CollectionDialog(model, promptParent);
1213  dlg->setCaption(i18nc("@title:window", "Choose Calendar"));
1214  dlg->setDefaultCollection(standard);
1215  dlg->setMimeTypeFilter(QStringList(CalEvent::mimeType(type)));
1216  if (dlg->exec())
1217  col = dlg->selectedCollection();
1218  if (!col.isValid() && cancelled)
1219  *cancelled = true;
1220  }
1221  }
1222  return col;
1223 }
1224 
1225 /******************************************************************************
1226 * Return the enabled collections which contain a specified mime type.
1227 * If 'writable' is true, only writable collections are included.
1228 */
1229 Collection::List CollectionControlModel::enabledCollections(CalEvent::Type type, bool writable)
1230 {
1231  const QString mimeType = CalEvent::mimeType(type);
1232  Collection::List cols = instance()->collections();
1233  Collection::List result;
1234  for (int i = 0, count = cols.count(); i < count; ++i)
1235  {
1236  AkonadiModel::instance()->refresh(cols[i]); // update with latest data
1237  if (cols[i].contentMimeTypes().contains(mimeType)
1238  && (!writable || ((cols[i].rights() & writableRights) == writableRights)))
1239  result += cols[i];
1240  }
1241  return result;
1242 }
1243 
1244 /******************************************************************************
1245 * Return the collection ID for a given resource ID.
1246 */
1247 Collection CollectionControlModel::collectionForResource(const QString& resourceId)
1248 {
1249  const Collection::List cols = instance()->collections();
1250  for (int i = 0, count = cols.count(); i < count; ++i)
1251  {
1252  if (cols[i].resource() == resourceId)
1253  return cols[i];
1254  }
1255  return Collection();
1256 }
1257 
1258 #if KDE_IS_VERSION(4,9,80)
1259 /******************************************************************************
1260 * Return whether all enabled collections have been populated.
1261 */
1262 bool CollectionControlModel::isPopulated(Collection::Id colId)
1263 {
1264  AkonadiModel* model = AkonadiModel::instance();
1265  Collection::List cols = instance()->collections();
1266  for (int i = 0, count = cols.count(); i < count; ++i)
1267  {
1268  if ((colId == -1 || colId == cols[i].id())
1269  && !model->data(model->collectionIndex(cols[i].id()), AkonadiModel::IsPopulatedRole).toBool())
1270  {
1271  model->refresh(cols[i]); // update with latest data
1272  if (!cols[i].hasAttribute<CollectionAttribute>()
1273  || cols[i].attribute<CollectionAttribute>()->enabled() == CalEvent::EMPTY)
1274  return false;
1275  }
1276  }
1277  return true;
1278 }
1279 
1280 /******************************************************************************
1281 * Wait for one or all enabled collections to be populated.
1282 * Reply = true if successful.
1283 */
1284 bool CollectionControlModel::waitUntilPopulated(Collection::Id colId, int timeout)
1285 {
1286  kDebug();
1287  int result = 1;
1288  AkonadiModel* model = AkonadiModel::instance();
1289  while (!model->isCollectionTreeFetched()
1290  || !isPopulated(colId))
1291  {
1292  if (!mPopulatedCheckLoop)
1293  mPopulatedCheckLoop = new QEventLoop(this);
1294  if (timeout > 0)
1295  QTimer::singleShot(timeout * 1000, mPopulatedCheckLoop, SLOT(quit()));
1296  result = mPopulatedCheckLoop->exec();
1297  }
1298  delete mPopulatedCheckLoop;
1299  mPopulatedCheckLoop = 0;
1300  return result;
1301 }
1302 #endif
1303 
1304 /******************************************************************************
1305 * Called when the Akonadi server has stopped. Reset the model.
1306 */
1307 void CollectionControlModel::reset()
1308 {
1309  delete mPopulatedCheckLoop;
1310  mPopulatedCheckLoop = 0;
1311 
1312  // Clear the collections list. This is required because addCollection() or
1313  // setCollections() don't work if the collections which they specify are
1314  // already in the list.
1315  setCollections(Collection::List());
1316 }
1317 
1318 /******************************************************************************
1319 * Exit from the populated event loop when a collection has been populated.
1320 */
1321 void CollectionControlModel::collectionPopulated()
1322 {
1323  if (mPopulatedCheckLoop)
1324  mPopulatedCheckLoop->exit(1);
1325 }
1326 
1327 /******************************************************************************
1328 * Return the data for a given role, for a specified item.
1329 */
1330 QVariant CollectionControlModel::data(const QModelIndex& index, int role) const
1331 {
1332  return sourceModel()->data(mapToSource(index), role);
1333 }
1334 
1335 #include "collectionmodel.moc"
1336 
1337 // vim: et sw=4:
CollectionCheckListModel::collectionTypeChange
void collectionTypeChange(CollectionCheckListModel *)
QSortFilterProxyModel::index
virtual QModelIndex index(int row, int column, const QModelIndex &parent) const
QSortFilterProxyModel::invalidate
void invalidate()
QItemSelection::indexes
QModelIndexList indexes() const
QModelIndex
QString::indexOf
int indexOf(QChar ch, int from, Qt::CaseSensitivity cs) const
QEvent
QWidget
CollectionControlModel::getStandard
static Akonadi::Collection getStandard(CalEvent::Type, bool useDefault=false)
Return the standard collection for a specified mime type.
Definition: collectionmodel.cpp:993
CollectionView::viewportEvent
virtual bool viewportEvent(QEvent *)
Definition: collectionmodel.cpp:592
QEvent::type
Type type() const
KCheckableProxyModel
QEventLoop
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:1066
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:968
CollectionListModel::collection
Akonadi::Collection collection(int row) const
Definition: collectionmodel.cpp:179
QSortFilterProxyModel::setSourceModel
virtual void setSourceModel(QAbstractItemModel *sourceModel)
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
QFont
AkonadiModel::IsStandardRole
Definition: akonadimodel.h:62
QStyle::pixelMetric
virtual int pixelMetric(PixelMetric metric, const QStyleOption *option, const QWidget *widget) const =0
QAbstractItemView::setModel
virtual void setModel(QAbstractItemModel *model)
QListView::mouseReleaseEvent
virtual void mouseReleaseEvent(QMouseEvent *e)
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:1018
QStringList::contains
bool contains(const QString &str, Qt::CaseSensitivity cs) const
AkonadiModel::refresh
bool refresh(Akonadi::Collection &) const
Refresh the specified collection instance with up to date data.
Definition: akonadimodel.cpp:1807
AkonadiModel::Enabled
Definition: akonadimodel.h:50
QHelpEvent::pos
const QPoint & pos() const
QVariant::value
T value() const
CollectionListModel::collectionIndex
QModelIndex collectionIndex(const Akonadi::Collection &) const
Definition: collectionmodel.cpp:189
QFontMetrics
QMouseEvent
AkonadiModel::isCollectionBeingDeleted
bool isCollectionBeingDeleted(Akonadi::Collection::Id) const
Definition: akonadimodel.cpp:1105
QString::remove
QString & remove(int position, int n)
AkonadiModel::collectionIndex
QModelIndex collectionIndex(Akonadi::Collection::Id id) const
Definition: akonadimodel.h:119
AkonadiModel::EnabledTypesRole
Definition: akonadimodel.h:59
CollectionControlModel::collectionForResource
static Akonadi::Collection collectionForResource(const QString &resourceId)
Return the collection ID for a given resource ID.
Definition: collectionmodel.cpp:1247
QToolTip::showText
void showText(const QPoint &pos, const QString &text, QWidget *w)
QStyleOptionButton
CollectionControlModel::isEnabled
static bool isEnabled(const Akonadi::Collection &, CalEvent::Type)
Return whether a collection is enabled (and valid).
Definition: collectionmodel.cpp:715
QListView
QWidget::width
int width() const
QFont::setBold
void setBold(bool enable)
CollectionControlModel::typeListForDisplay
static QString typeListForDisplay(CalEvent::Types)
Return a bulleted list of alarm types for inclusion in an i18n message.
Definition: collectionmodel.cpp:946
QRegExp
CollectionCheckListModel::collection
Akonadi::Collection collection(int row) const
Definition: collectionmodel.cpp:287
QObject::name
const char * name() const
QModelIndex::isValid
bool isValid() const
QFontMetrics::boundingRect
QRect boundingRect(QChar ch) const
QAbstractItemView::viewportEvent
virtual bool viewportEvent(QEvent *event)
CollectionListModel::useCollectionColour
void useCollectionColour(bool use)
Definition: collectionmodel.h:58
CollectionControlModel::instance
static CollectionControlModel * instance()
Definition: collectionmodel.cpp:653
QVariant::toInt
int toInt(bool *ok) const
CollectionListModel::CollectionListModel
CollectionListModel(QObject *parent=0)
Definition: collectionmodel.cpp:168
QEventLoop::exec
int exec(QFlags< QEventLoop::ProcessEventsFlag > flags)
autoqpointer.h
QObject
QHelpEvent::globalPos
const QPoint & globalPos() const
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:1180
CollectionControlModel::data
virtual QVariant data(const QModelIndex &, int role=Qt::DisplayRole) const
Definition: collectionmodel.cpp:1330
CollectionListModel::setFilterWritable
void setFilterWritable(bool writable)
Definition: collectionmodel.cpp:199
AkonadiModel::instance
static AkonadiModel * instance()
Definition: akonadimodel.cpp:83
QEventLoop::exit
void exit(int returnCode)
QItemSelectionModel::select
virtual void select(const QModelIndex &index, QFlags< QItemSelectionModel::SelectionFlag > command)
QString::isEmpty
bool isEmpty() const
CollectionControlModel
Definition: collectionmodel.h:168
QSortFilterProxyModel::invalidateFilter
void invalidateFilter()
QSortFilterProxyModel::setDynamicSortFilter
void setDynamicSortFilter(bool enable)
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:1032
AkonadiModel::Change
Change
Definition: akonadimodel.h:50
QListView::horizontalOffset
virtual int horizontalOffset() const
QAbstractItemModel::data
virtual QVariant data(const QModelIndex &index, int role) const =0
AkonadiModel::tooltip
QString tooltip(const Akonadi::Collection &, CalEvent::Types) const
Get the tooltip for a collection.
Definition: akonadimodel.cpp:930
messagebox.h
QString
QWidget::isActiveWindow
bool isActiveWindow() const
QListView::rectForIndex
QRect rectForIndex(const QModelIndex &index) const
CollectionFilterCheckListModel::setEventTypeFilter
void setEventTypeFilter(CalEvent::Type)
Definition: collectionmodel.cpp:481
QStringList
AkonadiModel::AlarmTypes
Definition: akonadimodel.h:50
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:213
QLatin1Char
QSortFilterProxyModel
AutoQPointer
QWidget::font
const QFont & font() const
preferences.h
AkonadiModel::ReadOnly
Definition: akonadimodel.h:50
CollectionControlModel::enabledCollections
static Akonadi::Collection::List enabledCollections(CalEvent::Type, bool writable)
Return the enabled collections which contain a specified mime type.
Definition: collectionmodel.cpp:1229
QListView::viewOptions
virtual QStyleOptionViewItem viewOptions() const
QListView::spacing
int spacing() const
QItemSelection
collectionmodel.h
AkonadiModel::types
static CalEvent::Types types(const Akonadi::Collection &)
Definition: akonadimodel.cpp:1926
QAbstractProxyModel::sourceModel
QAbstractItemModel * sourceModel() const
QSortFilterProxyModel::mapToSource
virtual QModelIndex mapToSource(const QModelIndex &proxyIndex) const
AkonadiModel::isCompatible
static bool isCompatible(const Akonadi::Collection &)
Check whether a collection is stored in the current KAlarm calendar format.
Definition: akonadimodel.cpp:1884
QListView::indexAt
virtual QModelIndex indexAt(const QPoint &p) const
QRect::width
int width() const
QString::mid
QString mid(int position, int n) const
QFrame::frameWidth
int frameWidth() const
QApplication::style
QStyle * style()
QLatin1String
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:60
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
QVariant::toBool
bool toBool() const
CollectionFilterCheckListModel::collection
Akonadi::Collection collection(int row) const
Definition: collectionmodel.cpp:503
QAbstractItemModel
CollectionListModel
Definition: collectionmodel.h:50
KAMessageBox::warningContinueCancel
static int warningContinueCancel(QWidget *parent, const QString &text, const QString &caption=QString(), const KGuiItem &buttonContinue=KStandardGuiItem::cont(), const KGuiItem &buttonCancel=KStandardGuiItem::cancel(), const QString &dontAskAgainName=QString(), Options options=Options(Notify|WindowModal))
CollectionCheckListModel::setData
virtual bool setData(const QModelIndex &, const QVariant &value, int role)
Definition: collectionmodel.cpp:345
QStyle::subElementRect
virtual QRect subElementRect(SubElement element, const QStyleOption *option, const QWidget *widget) const =0
QMouseEvent::pos
const QPoint & pos() const
QWidget::toolTip
QString toolTip() const
CollectionFilterCheckListModel::data
virtual QVariant data(const QModelIndex &, int role=Qt::DisplayRole) const
Definition: collectionmodel.cpp:513
CollectionCheckListModel::~CollectionCheckListModel
~CollectionCheckListModel()
Definition: collectionmodel.cpp:274
QAbstractItemView::model
QAbstractItemModel * model() const
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:857
CollectionListModel::setEventTypeFilter
void setEventTypeFilter(CalEvent::Type)
Definition: collectionmodel.cpp:194
QItemSelectionModel
QAbstractItemView::clearSelection
void clearSelection()
QObject::connect
bool connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
QObject::parent
QObject * parent() const
QHelpEvent
QItemSelectionModel::SelectionFlags
typedef SelectionFlags
AkonadiModel
Definition: akonadimodel.h:46
QSortFilterProxyModel::data
virtual QVariant data(const QModelIndex &index, int role) const
AkonadiModel::isWritable
static int isWritable(const Akonadi::Collection &)
Return whether a collection is fully writable, i.e.
Definition: akonadimodel.cpp:1893
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:532
QTimer::singleShot
singleShot
QVariant
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:736
This file is part of the KDE documentation.
Documentation copyright © 1996-2020 The KDE developers.
Generated on Mon Jun 22 2020 13:34:51 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
  • pimprint

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