Build with clang.
[kdepim.git] / kalarm / resourcemodelview.cpp
blob80339656a4dc898b262dbf6a30f0510b3366fc29
1 /*
2 * resourcemodelview.cpp - model/view classes for alarm resource lists
3 * Program: kalarm
4 * Copyright © 2007-2011 by David Jarvie <djarvie@kde.org>
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.
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.
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.
21 #include "kalarm.h"
23 #include "messagebox.h"
24 #include "preferences.h"
25 #include "resourcemodelview.moc"
27 #include <klocale.h>
28 #include <kdebug.h>
30 #include <QApplication>
31 #include <QToolTip>
32 #include <QMouseEvent>
33 #include <QKeyEvent>
34 #include <QHelpEvent>
35 #include <QTextLayout>
36 #include <QTextLine>
39 ResourceModel* ResourceModel::mInstance = 0;
42 ResourceModel* ResourceModel::instance(QObject* parent)
44 if (!mInstance)
45 mInstance = new ResourceModel(parent);
46 return mInstance;
49 ResourceModel::ResourceModel(QObject* parent)
50 : QAbstractListModel(parent)
52 refresh();
53 AlarmResources* resources = AlarmResources::instance();
54 connect(resources, SIGNAL(signalResourceModified(AlarmResource*)), SLOT(updateResource(AlarmResource*)));
55 connect(resources, SIGNAL(standardResourceChange(KAlarm::CalEvent::Type)), SLOT(slotStandardChanged(KAlarm::CalEvent::Type)));
56 connect(resources, SIGNAL(resourceStatusChanged(AlarmResource*,AlarmResources::Change)), SLOT(slotStatusChanged(AlarmResource*,AlarmResources::Change)));
57 connect(resources, SIGNAL(resourceLoaded(AlarmResource*,bool)), SLOT(slotLoaded(AlarmResource*,bool)));
60 int ResourceModel::rowCount(const QModelIndex& parent) const
62 if (parent.isValid())
63 return 0;
64 return mResources.count();
67 QModelIndex ResourceModel::index(int row, int column, const QModelIndex& parent) const
69 if (parent.isValid() || row >= mResources.count())
70 return QModelIndex();
71 return createIndex(row, column, mResources[row]);
74 QVariant ResourceModel::data(const QModelIndex& index, int role) const
76 if (!index.isValid())
77 return QVariant();
78 AlarmResource* resource = static_cast<AlarmResource*>(index.internalPointer());
79 if (!resource)
80 return QVariant();
81 switch (role)
83 case Qt::DisplayRole:
84 return resource->resourceName();
85 case Qt::CheckStateRole:
86 return resource->isEnabled() ? Qt::Checked : Qt::Unchecked;
87 case Qt::ForegroundRole:
88 switch (resource->alarmType())
90 case KAlarm::CalEvent::ACTIVE: return resource->readOnly() ? Qt::darkGray : Qt::black;
91 case KAlarm::CalEvent::ARCHIVED: return resource->readOnly() ? Qt::green : Qt::darkGreen;
92 case KAlarm::CalEvent::TEMPLATE: return resource->readOnly() ? Qt::blue : Qt::darkBlue;
93 default: break;
95 break;
96 case Qt::BackgroundRole:
97 if (resource->colour().isValid())
98 return resource->colour();
99 break;
100 case Qt::FontRole:
102 if (!resource->isEnabled() || !resource->standardResource())
103 break;
104 QFont font = mFont;
105 font.setBold(true);
106 return font;
108 case Qt::ToolTipRole:
110 QString name = '@' + resource->resourceName(); // insert markers for stripping out name
111 QString type = '@' + resource->displayType();
112 bool inactive = !resource->isActive();
113 QString disabled = resource->isWrongAlarmType() ? i18nc("@info/plain", "Disabled (wrong alarm type)") : i18nc("@info/plain", "Disabled");
114 QString readonly = i18nc("@info/plain", "Read-only");
115 if (inactive && resource->readOnly())
116 return i18nc("@info:tooltip",
117 "%1"
118 "<nl/>%2: <filename>%3</filename>"
119 "<nl/>%4, %5",
120 name, type, resource->displayLocation(), disabled, readonly);
121 if (inactive || resource->readOnly())
122 return i18nc("@info:tooltip",
123 "%1"
124 "<nl/>%2: <filename>%3</filename>"
125 "<nl/>%4",
126 name, type, resource->displayLocation(),
127 (inactive ? disabled : readonly));
128 return i18nc("@info:tooltip",
129 "%1"
130 "<nl/>%2: <filename>%3</filename>",
131 name, type, resource->displayLocation());
133 default:
134 break;
136 return QVariant();
139 /******************************************************************************
140 * Set the font to use for all items, or the checked state of one item.
141 * The font must always be set at initialisation.
143 bool ResourceModel::setData(const QModelIndex& index, const QVariant& value, int role)
145 mErrorPrompt.clear();
146 if (role == Qt::FontRole)
148 // Set the font used in all views.
149 // This enables data(index, Qt::FontRole) to return bold when appropriate.
150 mFont = value.value<QFont>();
151 return true;
153 if (role != Qt::CheckStateRole || !index.isValid())
154 return false;
155 AlarmResource* resource = static_cast<AlarmResource*>(index.internalPointer());
156 if (!resource)
157 return false;
158 Qt::CheckState state = static_cast<Qt::CheckState>(value.toInt());
159 bool active = (state == Qt::Checked);
160 bool saveChange = false;
161 AlarmResources* resources = AlarmResources::instance();
162 if (active)
164 // Enable the resource
165 resource->setActive(true); // enable it now so that load() will work
166 saveChange = resources->load(resource);
167 resource->setActive(false); // reset so that setEnabled() will work
169 else
171 // Disable the resource
172 saveChange = resource->saveAndClose(); // close resource after it is saved
174 if (saveChange)
175 resource->setEnabled(active);
176 emit dataChanged(index, index);
177 return true;
180 Qt::ItemFlags ResourceModel::flags(const QModelIndex&) const
182 return Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsUserCheckable;
185 /******************************************************************************
186 * Return the resource referred to by an index.
188 AlarmResource* ResourceModel::resource(const QModelIndex& index) const
190 if (!index.isValid())
191 return 0;
192 return static_cast<AlarmResource*>(index.internalPointer());
195 /******************************************************************************
196 * Emit a signal that a resource has changed.
198 void ResourceModel::notifyChange(const QModelIndex& index)
200 if (index.isValid())
201 emit dataChanged(index, index);
204 /******************************************************************************
205 * Reload the resources list.
207 void ResourceModel::refresh()
209 // This would be better done by a reset(), but the signals are private to QAbstractItemModel
210 if (!mResources.isEmpty())
212 beginRemoveRows(QModelIndex(), 0, mResources.count() - 1);
213 mResources.clear();
214 endRemoveRows();
216 QList<AlarmResource*> newResources;
217 AlarmResourceManager* manager = AlarmResources::instance()->resourceManager();
218 for (AlarmResourceManager::Iterator it = manager->begin(); it != manager->end(); ++it)
219 newResources += *it;
220 if (!newResources.isEmpty())
222 beginInsertRows(QModelIndex(), 0, newResources.count() - 1);
223 mResources = newResources;
224 endInsertRows();
228 /******************************************************************************
229 * Add the specified resource to the list.
231 void ResourceModel::addResource(AlarmResource* resource)
233 int row = mResources.count();
234 beginInsertRows(QModelIndex(), row, row);
235 mResources += resource;
236 endInsertRows();
239 /******************************************************************************
240 * Delete the specified resource from the list.
242 void ResourceModel::removeResource(AlarmResource* resource)
244 int row = mResources.indexOf(resource);
245 if (row >= 0)
247 beginRemoveRows(QModelIndex(), row, row);
248 mResources.removeAt(row);
249 endRemoveRows();
253 /******************************************************************************
254 * Called when the resource has been updated , to update the
255 * active status displayed for the resource item.
257 void ResourceModel::updateResource(AlarmResource* resource)
259 int row = mResources.indexOf(resource);
260 if (row >= 0)
262 QModelIndex ix = index(row, 0, QModelIndex());
263 emit dataChanged(ix, ix);
267 /******************************************************************************
268 * Called when a different resource has been set as the standard resource.
270 void ResourceModel::slotStandardChanged(KAlarm::CalEvent::Type type)
272 for (int row = 0, end = mResources.count(); row < end; ++row)
274 if (mResources[row]->alarmType() == type)
276 QModelIndex ix = index(row, 0, QModelIndex());
277 emit dataChanged(ix, ix);
282 /******************************************************************************
283 * Called when a resource has completed loading.
284 * Check in case its status has changed.
286 void ResourceModel::slotLoaded(AlarmResource* resource, bool active)
288 if (active)
289 updateResource(resource);
292 /******************************************************************************
293 * Called when a resource status has changed, to update the list.
295 void ResourceModel::slotStatusChanged(AlarmResource* resource, AlarmResources::Change change)
297 switch (change)
299 case AlarmResources::Added:
300 addResource(resource);
301 break;
302 case AlarmResources::Enabled:
303 case AlarmResources::ReadOnly:
304 case AlarmResources::Colour:
305 updateResource(resource);
306 break;
307 default:
308 break;
313 /*=============================================================================
314 = Class: ResourceFilterModel
315 = Proxy model for filtering resource lists.
316 =============================================================================*/
318 ResourceFilterModel::ResourceFilterModel(QAbstractItemModel* baseModel, QObject* parent)
319 : QSortFilterProxyModel(parent),
320 mResourceType(KAlarm::CalEvent::EMPTY)
322 setSourceModel(baseModel);
325 void ResourceFilterModel::setFilter(KAlarm::CalEvent::Type type)
327 if (type != mResourceType)
329 mResourceType = type;
330 invalidateFilter();
334 bool ResourceFilterModel::filterAcceptsRow(int sourceRow, const QModelIndex&) const
336 return static_cast<ResourceModel*>(sourceModel())->resource(sourceModel()->index(sourceRow, 0))->alarmType() == mResourceType;
339 /******************************************************************************
340 * Return the resource referred to by an index.
342 AlarmResource* ResourceFilterModel::resource(int row) const
344 return static_cast<ResourceModel*>(sourceModel())->resource(mapToSource(index(row, 0)));
347 AlarmResource* ResourceFilterModel::resource(const QModelIndex& index) const
349 return static_cast<ResourceModel*>(sourceModel())->resource(mapToSource(index));
352 /******************************************************************************
353 * Emit a signal that a resource has changed.
355 void ResourceFilterModel::notifyChange(int row)
357 static_cast<ResourceModel*>(sourceModel())->notifyChange(mapToSource(index(row, 0)));
360 void ResourceFilterModel::notifyChange(const QModelIndex& index)
362 static_cast<ResourceModel*>(sourceModel())->notifyChange(mapToSource(index));
366 /*=============================================================================
367 = Class: ResourceDelegate
368 = Model/view delegate for resource list.
369 =============================================================================*/
371 /******************************************************************************
372 * Process a change of state of the checkbox for a resource.
374 bool ResourceDelegate::editorEvent(QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option, const QModelIndex& index)
376 if (!(model->flags(index) & Qt::ItemIsEnabled))
377 return false;
378 if (event->type() == QEvent::MouseButtonRelease
379 || event->type() == QEvent::MouseButtonDblClick)
381 const int textMargin = QApplication::style()->pixelMetric(QStyle::PM_FocusFrameHMargin) + 1;
382 QRect checkRect = QStyle::alignedRect(option.direction, Qt::AlignLeft | Qt::AlignVCenter,
383 check(option, option.rect, Qt::Checked).size(),
384 QRect(option.rect.x() + textMargin, option.rect.y(), option.rect.width(), option.rect.height()));
385 if (!checkRect.contains(static_cast<QMouseEvent*>(event)->pos()))
386 return false;
387 if (event->type() == QEvent::MouseButtonDblClick)
388 return true; // ignore double clicks
390 else if (event->type() == QEvent::KeyPress)
392 if (static_cast<QKeyEvent*>(event)->key() != Qt::Key_Space
393 && static_cast<QKeyEvent*>(event)->key() != Qt::Key_Select)
394 return false;
396 else
397 return false;
399 QVariant value = index.data(Qt::CheckStateRole);
400 if (!value.isValid())
401 return false;
402 Qt::CheckState state = (static_cast<Qt::CheckState>(value.toInt()) == Qt::Checked ? Qt::Unchecked : Qt::Checked);
403 if (state == Qt::Unchecked)
405 // The resource is to be disabled.
406 // Check for eligibility.
407 AlarmResource* resource = static_cast<ResourceFilterModel*>(model)->resource(index);
408 if (!resource)
409 return false;
410 if (resource->standardResource())
412 // It's the standard resource for its type.
413 if (resource->alarmType() == KAlarm::CalEvent::ACTIVE)
415 KAMessageBox::sorry(static_cast<QWidget*>(parent()),
416 i18nc("@info", "You cannot disable your default active alarm calendar."));
417 return false;
420 if (resource->alarmType() == KAlarm::CalEvent::ARCHIVED && Preferences::archivedKeepDays())
422 // Only allow the archived alarms standard resource to be disabled if
423 // we're not saving archived alarms.
424 KAMessageBox::sorry(static_cast<QWidget*>(parent()),
425 i18nc("@info", "You cannot disable your default archived alarm calendar "
426 "while expired alarms are configured to be kept."));
427 return false;
429 if (KAMessageBox::warningContinueCancel(static_cast<QWidget*>(parent()),
430 i18nc("@info", "Do you really want to disable your default calendar?"))
431 == KMessageBox::Cancel)
432 return false;
435 return model->setData(index, state, Qt::CheckStateRole);
439 /*=============================================================================
440 = Class: ResourceView
441 = View displaying a list of resources.
442 =============================================================================*/
444 void ResourceView::setModel(QAbstractItemModel* model)
446 model->setData(QModelIndex(), viewOptions().font, Qt::FontRole);
447 QListView::setModel(model);
448 setItemDelegate(new ResourceDelegate(this));
451 /******************************************************************************
452 * Return the resource for a given row.
454 AlarmResource* ResourceView::resource(int row) const
456 return static_cast<ResourceFilterModel*>(model())->resource(row);
459 AlarmResource* ResourceView::resource(const QModelIndex& index) const
461 return static_cast<ResourceFilterModel*>(model())->resource(index);
464 /******************************************************************************
465 * Emit a signal that a resource has changed.
467 void ResourceView::notifyChange(int row) const
469 static_cast<ResourceFilterModel*>(model())->notifyChange(row);
472 void ResourceView::notifyChange(const QModelIndex& index) const
474 static_cast<ResourceFilterModel*>(model())->notifyChange(index);
477 /******************************************************************************
478 * Called when a mouse button is released.
479 * Any currently selected resource is deselected.
481 void ResourceView::mouseReleaseEvent(QMouseEvent* e)
483 if (!indexAt(e->pos()).isValid())
484 clearSelection();
485 QListView::mouseReleaseEvent(e);
488 /******************************************************************************
489 * Called when a ToolTip or WhatsThis event occurs.
491 bool ResourceView::viewportEvent(QEvent* e)
493 if (e->type() == QEvent::ToolTip && isActiveWindow())
495 QHelpEvent* he = static_cast<QHelpEvent*>(e);
496 QModelIndex index = indexAt(he->pos());
497 QVariant value = model()->data(index, Qt::ToolTipRole);
498 if (qVariantCanConvert<QString>(value))
500 QString toolTip = value.toString();
501 int i = toolTip.indexOf('@');
502 if (i > 0)
504 int j = toolTip.indexOf(QRegExp("<(nl|br)", Qt::CaseInsensitive), i + 1);
505 int k = toolTip.indexOf('@', j);
506 QString name = toolTip.mid(i + 1, j - i - 1);
507 value = model()->data(index, Qt::FontRole);
508 QFontMetrics fm(qvariant_cast<QFont>(value).resolve(viewOptions().font));
509 int textWidth = fm.boundingRect(name).width() + 1;
510 const int margin = QApplication::style()->pixelMetric(QStyle::PM_FocusFrameHMargin) + 1;
511 QStyleOptionButton opt;
512 opt.QStyleOption::operator=(viewOptions());
513 opt.rect = rectForIndex(index);
514 int checkWidth = QApplication::style()->subElementRect(QStyle::SE_ViewItemCheckIndicator, &opt).width();
515 int left = spacing() + 3*margin + checkWidth + viewOptions().decorationSize.width(); // left offset of text
516 int right = left + textWidth;
517 if (left >= horizontalOffset() + spacing()
518 && right <= horizontalOffset() + width() - spacing() - 2*frameWidth())
520 // The whole of the resource name is already displayed,
521 // so omit it from the tooltip.
522 if (k > 0)
523 toolTip.remove(i, k + 1 - i);
525 else
527 toolTip.remove(k, 1);
528 toolTip.remove(i, 1);
531 QToolTip::showText(he->globalPos(), toolTip, this);
532 return true;
535 return QListView::viewportEvent(e);
538 // vim: et sw=4: