2 * resourcemodelview.cpp - model/view classes for alarm resource lists
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.
23 #include "messagebox.h"
24 #include "preferences.h"
25 #include "resourcemodelview.moc"
30 #include <QApplication>
32 #include <QMouseEvent>
35 #include <QTextLayout>
39 ResourceModel
* ResourceModel::mInstance
= 0;
42 ResourceModel
* ResourceModel::instance(QObject
* parent
)
45 mInstance
= new ResourceModel(parent
);
49 ResourceModel::ResourceModel(QObject
* parent
)
50 : QAbstractListModel(parent
)
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
64 return mResources
.count();
67 QModelIndex
ResourceModel::index(int row
, int column
, const QModelIndex
& parent
) const
69 if (parent
.isValid() || row
>= mResources
.count())
71 return createIndex(row
, column
, mResources
[row
]);
74 QVariant
ResourceModel::data(const QModelIndex
& index
, int role
) const
78 AlarmResource
* resource
= static_cast<AlarmResource
*>(index
.internalPointer());
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
;
96 case Qt::BackgroundRole
:
97 if (resource
->colour().isValid())
98 return resource
->colour();
102 if (!resource
->isEnabled() || !resource
->standardResource())
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",
118 "<nl/>%2: <filename>%3</filename>"
120 name
, type
, resource
->displayLocation(), disabled
, readonly
);
121 if (inactive
|| resource
->readOnly())
122 return i18nc("@info:tooltip",
124 "<nl/>%2: <filename>%3</filename>"
126 name
, type
, resource
->displayLocation(),
127 (inactive
? disabled
: readonly
));
128 return i18nc("@info:tooltip",
130 "<nl/>%2: <filename>%3</filename>",
131 name
, type
, resource
->displayLocation());
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
>();
153 if (role
!= Qt::CheckStateRole
|| !index
.isValid())
155 AlarmResource
* resource
= static_cast<AlarmResource
*>(index
.internalPointer());
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();
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
171 // Disable the resource
172 saveChange
= resource
->saveAndClose(); // close resource after it is saved
175 resource
->setEnabled(active
);
176 emit
dataChanged(index
, index
);
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())
192 return static_cast<AlarmResource
*>(index
.internalPointer());
195 /******************************************************************************
196 * Emit a signal that a resource has changed.
198 void ResourceModel::notifyChange(const QModelIndex
& index
)
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);
216 QList
<AlarmResource
*> newResources
;
217 AlarmResourceManager
* manager
= AlarmResources::instance()->resourceManager();
218 for (AlarmResourceManager::Iterator it
= manager
->begin(); it
!= manager
->end(); ++it
)
220 if (!newResources
.isEmpty())
222 beginInsertRows(QModelIndex(), 0, newResources
.count() - 1);
223 mResources
= newResources
;
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
;
239 /******************************************************************************
240 * Delete the specified resource from the list.
242 void ResourceModel::removeResource(AlarmResource
* resource
)
244 int row
= mResources
.indexOf(resource
);
247 beginRemoveRows(QModelIndex(), row
, row
);
248 mResources
.removeAt(row
);
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
);
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
)
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
)
299 case AlarmResources::Added
:
300 addResource(resource
);
302 case AlarmResources::Enabled
:
303 case AlarmResources::ReadOnly
:
304 case AlarmResources::Colour
:
305 updateResource(resource
);
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
;
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
))
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()))
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
)
399 QVariant value
= index
.data(Qt::CheckStateRole
);
400 if (!value
.isValid())
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
);
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."));
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."));
429 if (KAMessageBox::warningContinueCancel(static_cast<QWidget
*>(parent()),
430 i18nc("@info", "Do you really want to disable your default calendar?"))
431 == KMessageBox::Cancel
)
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())
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('@');
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.
523 toolTip
.remove(i
, k
+ 1 - i
);
527 toolTip
.remove(k
, 1);
528 toolTip
.remove(i
, 1);
531 QToolTip::showText(he
->globalPos(), toolTip
, this);
535 return QListView::viewportEvent(e
);