1/*
2 Copyright (c) 2006-2008 Tobias Koenig <tokoe@kde.org>
3
4 This library is free software; you can redistribute it and/or modify it
5 under the terms of the GNU Library General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or (at your
7 option) any later version.
8
9 This library is distributed in the hope that it will be useful, but WITHOUT
10 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
12 License for more details.
13
14 You should have received a copy of the GNU Library General Public License
15 along with this library; see the file COPYING.LIB. If not, write to the
16 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17 02110-1301, USA.
18*/
19
20#include "agentinstancewidget.h"
21
22#include "agentfilterproxymodel.h"
23#include "agentinstance.h"
24#include "agentinstancemodel.h"
25
26#include <KIcon>
27#include <KIconLoader>
28
29#include <QtCore/QUrl>
30#include <QApplication>
31#include <QHBoxLayout>
32#include <QListView>
33#include <QPainter>
34
35namespace Akonadi {
36namespace Internal {
37
38static void iconsEarlyCleanup();
39
40struct Icons
41{
42 Icons()
43 : readyPixmap(KIcon(QLatin1String("user-online")).pixmap(QSize(16, 16)))
44 , syncPixmap(KIcon(QLatin1String("network-connect")).pixmap(QSize(16, 16)))
45 , errorPixmap(KIcon(QLatin1String("dialog-error")).pixmap(QSize(16, 16)))
46 , offlinePixmap(KIcon(QLatin1String("network-disconnect")).pixmap(QSize(16, 16)))
47 {
48 qAddPostRoutine(iconsEarlyCleanup);
49 }
50 QPixmap readyPixmap, syncPixmap, errorPixmap, offlinePixmap;
51};
52
53K_GLOBAL_STATIC(Icons, s_icons)
54
55// called as a Qt post routine, to prevent pixmap leaking
56void iconsEarlyCleanup() {
57 Icons *const ic = s_icons;
58 ic->readyPixmap = ic->syncPixmap = ic->errorPixmap = ic->offlinePixmap = QPixmap();
59}
60
61static const int s_delegatePaddingSize = 7;
62
63/**
64 * @internal
65 */
66
67class AgentInstanceWidgetDelegate : public QAbstractItemDelegate
68{
69public:
70 AgentInstanceWidgetDelegate(QObject *parent = 0);
71
72 virtual void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const;
73 virtual QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const;
74};
75
76}
77
78using Akonadi::Internal::AgentInstanceWidgetDelegate;
79
80/**
81 * @internal
82 */
83class AgentInstanceWidget::Private
84{
85public:
86 Private(AgentInstanceWidget *parent)
87 : mParent(parent)
88 , mView(0)
89 , mModel(0)
90 , proxy(0)
91 {
92 }
93
94 void currentAgentInstanceChanged(const QModelIndex &, const QModelIndex &);
95 void currentAgentInstanceDoubleClicked(const QModelIndex &);
96 void currentAgentInstanceClicked(const QModelIndex &currentIndex);
97
98 AgentInstanceWidget *mParent;
99 QListView *mView;
100 AgentInstanceModel *mModel;
101 AgentFilterProxyModel *proxy;
102};
103
104void AgentInstanceWidget::Private::currentAgentInstanceChanged(const QModelIndex &currentIndex, const QModelIndex &previousIndex)
105{
106 AgentInstance currentInstance;
107 if (currentIndex.isValid()) {
108 currentInstance = currentIndex.data(AgentInstanceModel::InstanceRole).value<AgentInstance>();
109 }
110
111 AgentInstance previousInstance;
112 if (previousIndex.isValid()) {
113 previousInstance = previousIndex.data(AgentInstanceModel::InstanceRole).value<AgentInstance>();
114 }
115
116 emit mParent->currentChanged(currentInstance, previousInstance);
117}
118
119void AgentInstanceWidget::Private::currentAgentInstanceDoubleClicked(const QModelIndex &currentIndex)
120{
121 AgentInstance currentInstance;
122 if (currentIndex.isValid()) {
123 currentInstance = currentIndex.data(AgentInstanceModel::InstanceRole).value<AgentInstance>();
124 }
125
126 emit mParent->doubleClicked(currentInstance);
127}
128
129void AgentInstanceWidget::Private::currentAgentInstanceClicked(const QModelIndex &currentIndex)
130{
131 AgentInstance currentInstance;
132 if (currentIndex.isValid()) {
133 currentInstance = currentIndex.data(AgentInstanceModel::InstanceRole).value<AgentInstance>();
134 }
135
136 emit mParent->clicked(currentInstance);
137}
138
139AgentInstanceWidget::AgentInstanceWidget(QWidget *parent)
140 : QWidget(parent)
141 , d(new Private(this))
142{
143 QHBoxLayout *layout = new QHBoxLayout(this);
144 layout->setMargin(0);
145
146 d->mView = new QListView(this);
147 d->mView->setContextMenuPolicy(Qt::NoContextMenu);
148 d->mView->setItemDelegate(new Internal::AgentInstanceWidgetDelegate(d->mView));
149 d->mView->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
150 d->mView->setAlternatingRowColors(true);
151 d->mView->setSelectionMode(QAbstractItemView::ExtendedSelection);
152 layout->addWidget(d->mView);
153
154 d->mModel = new AgentInstanceModel(this);
155
156 d->proxy = new AgentFilterProxyModel(this);
157 d->proxy->setSourceModel(d->mModel);
158 d->mView->setModel(d->proxy);
159
160 d->mView->selectionModel()->setCurrentIndex(d->mView->model()->index(0, 0), QItemSelectionModel::Select);
161 d->mView->scrollTo(d->mView->model()->index(0, 0));
162
163 connect(d->mView->selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)),
164 this, SLOT(currentAgentInstanceChanged(QModelIndex,QModelIndex)));
165 connect(d->mView, SIGNAL(doubleClicked(QModelIndex)),
166 this, SLOT(currentAgentInstanceDoubleClicked(QModelIndex)));
167 connect(d->mView, SIGNAL(clicked(QModelIndex)),
168 this, SLOT(currentAgentInstanceClicked(QModelIndex)));
169}
170
171AgentInstanceWidget::~AgentInstanceWidget()
172{
173 delete d;
174}
175
176AgentInstance AgentInstanceWidget::currentAgentInstance() const
177{
178 QItemSelectionModel *selectionModel = d->mView->selectionModel();
179 if (!selectionModel) {
180 return AgentInstance();
181 }
182
183 QModelIndex index = selectionModel->currentIndex();
184 if (!index.isValid()) {
185 return AgentInstance();
186 }
187
188 return index.data(AgentInstanceModel::InstanceRole).value<AgentInstance>();
189}
190
191QList<AgentInstance> AgentInstanceWidget::selectedAgentInstances() const
192{
193 QList<AgentInstance> list;
194 QItemSelectionModel *selectionModel = d->mView->selectionModel();
195 if (!selectionModel) {
196 return list;
197 }
198
199 const QModelIndexList indexes = selectionModel->selection().indexes();
200
201 foreach (const QModelIndex &index, indexes) {
202 list.append(index.data(AgentInstanceModel::InstanceRole).value<AgentInstance>());
203 }
204
205 return list;
206}
207
208QAbstractItemView *AgentInstanceWidget::view() const
209{
210 return d->mView;
211}
212
213AgentFilterProxyModel *AgentInstanceWidget::agentFilterProxyModel() const
214{
215 return d->proxy;
216}
217
218AgentInstanceWidgetDelegate::AgentInstanceWidgetDelegate(QObject *parent)
219 : QAbstractItemDelegate(parent)
220{
221}
222
223void AgentInstanceWidgetDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
224{
225 if (!index.isValid()) {
226 return;
227 }
228
229 QStyle *style = QApplication::style();
230 style->drawPrimitive(QStyle::PE_PanelItemViewItem, &option, painter, 0);
231
232 QIcon icon = index.data(Qt::DecorationRole).value<QIcon>();
233 const QString name = index.model()->data(index, Qt::DisplayRole).toString();
234 int status = index.model()->data(index, AgentInstanceModel::StatusRole).toInt();
235 uint progress = index.model()->data(index, AgentInstanceModel::ProgressRole).toUInt();
236 QString statusMessage = index.model()->data(index, AgentInstanceModel::StatusMessageRole).toString();
237
238 QPixmap statusPixmap;
239
240 if (!index.data(AgentInstanceModel::OnlineRole).toBool()) {
241 statusPixmap = s_icons->offlinePixmap;
242 } else if (status == AgentInstance::Idle) {
243 statusPixmap = s_icons->readyPixmap;
244 } else if (status == AgentInstance::Running) {
245 statusPixmap = s_icons->syncPixmap;
246 } else {
247 statusPixmap = s_icons->errorPixmap;
248 }
249
250 if (status == 1) {
251 statusMessage.append(QString::fromLatin1(" (%1%)").arg(progress));
252 }
253
254 QRect innerRect = option.rect.adjusted(s_delegatePaddingSize, s_delegatePaddingSize, -s_delegatePaddingSize, -s_delegatePaddingSize); //add some padding round entire delegate
255
256 const QSize decorationSize(KIconLoader::global()->currentSize(KIconLoader::Desktop), KIconLoader::global()->currentSize(KIconLoader::Desktop));
257 const QSize statusIconSize = QSize(16, 16); //= KIconLoader::global()->currentSize(KIconLoader::Small);
258
259 QFont nameFont = option.font;
260 nameFont.setBold(true);
261
262 QFont statusTextFont = option.font;
263 const QRect decorationRect(innerRect.left(), innerRect.top(), decorationSize.width(), innerRect.height());
264 const QRect nameTextRect(decorationRect.topRight() + QPoint(4, 0), innerRect.topRight() + QPoint(0, innerRect.height() / 2));
265 const QRect statusTextRect(decorationRect.bottomRight() + QPoint(4, - innerRect.height() / 2), innerRect.bottomRight());
266
267 const QPixmap iconPixmap = icon.pixmap(decorationSize);
268
269 QPalette::ColorGroup cg = option.state &QStyle::State_Enabled ? QPalette::Normal : QPalette::Disabled;
270 if (cg == QPalette::Normal && !(option.state & QStyle::State_Active)) {
271 cg = QPalette::Inactive;
272 }
273
274 if (option.state & QStyle::State_Selected) {
275 painter->setPen(option.palette.color(cg, QPalette::HighlightedText));
276 } else {
277 painter->setPen(option.palette.color(cg, QPalette::Text));
278 }
279
280 painter->drawPixmap(style->itemPixmapRect(decorationRect, Qt::AlignCenter, iconPixmap), iconPixmap);
281
282 painter->setFont(nameFont);
283 painter->drawText(nameTextRect, Qt::AlignVCenter | Qt::AlignLeft, name);
284
285 painter->setFont(statusTextFont);
286 painter->drawText(statusTextRect.adjusted(statusIconSize.width() + 4, 0, 0, 0), Qt::AlignVCenter | Qt::AlignLeft, statusMessage);
287 painter->drawPixmap(style->itemPixmapRect(statusTextRect, Qt::AlignVCenter | Qt::AlignLeft, statusPixmap), statusPixmap);
288}
289
290QSize AgentInstanceWidgetDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const
291{
292 Q_UNUSED(index);
293
294 const int iconHeight = KIconLoader::global()->currentSize(KIconLoader::Desktop) + (s_delegatePaddingSize * 2); //icon height + padding either side
295 const int textHeight = option.fontMetrics.height() + qMax(option.fontMetrics.height(), 16) + (s_delegatePaddingSize * 2); //height of text + icon/text + padding either side
296
297 return QSize(1, qMax(iconHeight, textHeight)); //any width,the view will give us the whole thing in list mode
298}
299
300}
301
302#include "moc_agentinstancewidget.cpp"
303