1/*
2 * This file is part of the KDE project
3 * Copyright (C) 2009 Shaun Reich <shaun.reich@kdemail.net>
4 * Copyright (C) 2006-2008 Rafael Fernández López <ereslibre@kde.org>
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Library General Public
8 * License version 2 as published by the Free Software Foundation.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
14 *
15 * You should have received a copy of the GNU Library General Public License
16 * along with this library; see the file COPYING.LIB. If not, write to
17 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
19*/
20
21#include "progresslistdelegate.h"
22#include "progresslistdelegate_p.h"
23#include "progresslistmodel.h"
24
25#include <QApplication>
26#include <QPushButton>
27#include <QPainter>
28#include <QHash>
29#include <QFontMetrics>
30#include <QListView>
31#include <QProgressBar>
32
33#include <kicon.h>
34#include <klocale.h>
35#include <kpushbutton.h>
36
37#define MIN_CONTENT_PIXELS 50
38
39QString ProgressListDelegate::Private::getIcon(const QModelIndex &index) const
40{
41 return index.model()->data(index, JobView::Icon).toString();
42}
43
44QString ProgressListDelegate::Private::getSizeTotal(const QModelIndex &index) const
45{
46 return index.model()->data(index, JobView::SizeTotal).toString();
47}
48
49QString ProgressListDelegate::Private::getSizeProcessed(const QModelIndex &index) const
50{
51 return index.model()->data(index, JobView::SizeProcessed).toString();
52}
53
54qlonglong ProgressListDelegate::Private::getTimeTotal(const QModelIndex &index) const
55{
56 return index.model()->data(index, JobView::TimeTotal).toLongLong();
57}
58
59qlonglong ProgressListDelegate::Private::getTimeProcessed(const QModelIndex &index) const
60{
61 return index.model()->data(index, JobView::TimeElapsed).toLongLong();
62}
63
64QString ProgressListDelegate::Private::getSpeed(const QModelIndex &index) const
65{
66 return index.model()->data(index, JobView::Speed).toString();
67}
68
69int ProgressListDelegate::Private::getPercent(const QModelIndex &index) const
70{
71 return index.model()->data(index, JobView::Percent).toInt();
72}
73
74QString ProgressListDelegate::Private::getInfoMessage(const QModelIndex &index) const
75{
76 return index.model()->data(index, JobView::InfoMessage).toString();
77}
78
79int ProgressListDelegate::Private::getCurrentLeftMargin(int fontHeight) const
80{
81 return leftMargin + separatorPixels + fontHeight;
82}
83
84ProgressListDelegate::ProgressListDelegate(QObject *parent, QListView *listView)
85 : KWidgetItemDelegate(listView, parent)
86 , d(new Private(listView))
87{
88}
89
90ProgressListDelegate::~ProgressListDelegate()
91{
92 delete d;
93}
94
95void ProgressListDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
96{
97 QApplication::style()->drawPrimitive(QStyle::PE_PanelItemViewItem, &option, painter, 0);
98 if (!index.isValid()) {
99 return;
100 }
101
102 QFontMetrics fontMetrics = painter->fontMetrics();
103 int textHeight = fontMetrics.height();
104
105 int coordY = d->separatorPixels + option.rect.top();
106
107 KIcon iconToShow(d->getIcon(index));
108
109 QColor unselectedTextColor = option.palette.text().color();
110 QColor selectedTextColor = option.palette.highlightedText().color();
111 QPen currentPen = painter->pen();
112 QPen unselectedPen = QPen(currentPen);
113 QPen selectedPen = QPen(currentPen);
114
115 unselectedPen.setColor(unselectedTextColor);
116 selectedPen.setColor(selectedTextColor);
117
118 if (option.state & QStyle::State_Selected) {
119 painter->fillRect(option.rect, option.palette.highlight());
120 painter->setPen(selectedPen);
121 } else {
122 painter->setPen(unselectedPen);
123 }
124
125 painter->save();
126 painter->setRenderHint(QPainter::Antialiasing, true);
127
128 QRect canvas = option.rect;
129 int iconWidth = canvas.height() / 2 - d->leftMargin - d->rightMargin;
130 int iconHeight = iconWidth;
131 d->iconWidth = iconWidth;
132
133 painter->drawPixmap(option.rect.right() - iconWidth - d->rightMargin, coordY, iconToShow.pixmap(iconWidth, iconHeight));
134
135 if (!d->getInfoMessage(index).isEmpty()) {
136 QString textToShow = fontMetrics.elidedText(d->getInfoMessage(index), Qt::ElideRight, canvas.width() - d->getCurrentLeftMargin(textHeight) - d->rightMargin);
137
138 textHeight = fontMetrics.size(Qt::TextSingleLine, textToShow).height();
139
140 painter->drawText(d->getCurrentLeftMargin(textHeight) + option.rect.left(), coordY, fontMetrics.width(textToShow), textHeight, Qt::AlignLeft, textToShow);
141
142 coordY += textHeight;
143 }
144
145 if (!d->getSizeProcessed(index).isEmpty() || !d->getSizeTotal(index).isEmpty() || !d->getSpeed(index).isEmpty()) {
146 QString textToShow;
147 if (!d->getSizeTotal(index).isEmpty() && !d->getSpeed(index).isEmpty())
148 textToShow = fontMetrics.elidedText(i18n("%1 of %2 processed at %3/s", d->getSizeProcessed(index), d->getSizeTotal(index), d->getSpeed(index)), Qt::ElideRight, canvas.width() - d->getCurrentLeftMargin(textHeight) - d->rightMargin);
149 else if (!d->getSizeTotal(index).isEmpty() && d->getSpeed(index).isEmpty())
150 textToShow = fontMetrics.elidedText(i18n("%1 of %2 processed", d->getSizeProcessed(index), d->getSizeTotal(index)), Qt::ElideRight, canvas.width() - d->getCurrentLeftMargin(textHeight) - d->rightMargin);
151 else if (d->getSizeTotal(index).isEmpty() && !d->getSpeed(index).isEmpty())
152 textToShow = fontMetrics.elidedText(i18n("%1 processed at %2/s", d->getSizeProcessed(index), d->getSpeed(index)), Qt::ElideRight, canvas.width() - d->getCurrentLeftMargin(textHeight) - d->rightMargin);
153 else
154 textToShow = fontMetrics.elidedText(i18n("%1 processed", d->getSizeProcessed(index)), Qt::ElideRight, canvas.width() - d->getCurrentLeftMargin(textHeight) - d->rightMargin);
155
156 textHeight = fontMetrics.size(Qt::TextSingleLine, textToShow).height();
157
158 painter->drawText(d->getCurrentLeftMargin(textHeight) + option.rect.left(), coordY, fontMetrics.width(textToShow), textHeight, Qt::AlignLeft, textToShow);
159
160 coordY += textHeight;
161 }
162
163 painter->restore();
164}
165
166QSize ProgressListDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const
167{
168 QFontMetrics fontMetrics = option.fontMetrics;
169
170 int itemHeight = d->separatorPixels;
171 int itemWidth = d->leftMargin + d->rightMargin + d->iconWidth + d->separatorPixels * 2 +
172 fontMetrics.height();
173
174 int textSize = fontMetrics.height();
175
176 if (!d->getInfoMessage(index).isEmpty()) {
177 textSize = fontMetrics.size(Qt::TextSingleLine, d->getInfoMessage(index)).height();
178 itemHeight += textSize;
179 }
180
181 if (!d->getSizeProcessed(index).isEmpty() || !d->getSpeed(index).isEmpty() ||
182 !d->getSizeTotal(index).isEmpty()) {
183 textSize = fontMetrics.size(Qt::TextSingleLine, d->getSizeProcessed(index)).height();
184 itemHeight += textSize;
185 }
186
187 if (d->getPercent(index) > 0) {
188 itemHeight += d->progressBar->sizeHint().height();
189 }
190
191 if (d->editorHeight > 0)
192 itemHeight += d->editorHeight;
193
194 if (itemHeight + d->separatorPixels >= d->minimumItemHeight)
195 itemHeight += d->separatorPixels;
196 else
197 itemHeight = d->minimumItemHeight;
198
199 return QSize(itemWidth + MIN_CONTENT_PIXELS, itemHeight);
200}
201
202void ProgressListDelegate::setSeparatorPixels(int separatorPixels)
203{
204 d->separatorPixels = separatorPixels;
205}
206
207void ProgressListDelegate::setLeftMargin(int leftMargin)
208{
209 d->leftMargin = leftMargin;
210}
211
212void ProgressListDelegate::setRightMargin(int rightMargin)
213{
214 d->rightMargin = rightMargin;
215}
216
217void ProgressListDelegate::setMinimumItemHeight(int minimumItemHeight)
218{
219 d->minimumItemHeight = minimumItemHeight;
220}
221
222void ProgressListDelegate::setMinimumContentWidth(int minimumContentWidth)
223{
224 d->minimumContentWidth = minimumContentWidth;
225}
226
227void ProgressListDelegate::setEditorHeight(int editorHeight)
228{
229 d->editorHeight = editorHeight;
230}
231
232QList<QWidget*> ProgressListDelegate::createItemWidgets() const
233{
234 QList<QWidget*> widgetList;
235
236 KPushButton *pauseResumeButton = new KPushButton();
237 pauseResumeButton->setIcon(KIcon("media-playback-pause"));
238
239 KPushButton *cancelButton = new KPushButton();
240 cancelButton->setIcon(KIcon("media-playback-stop"));
241
242 KPushButton *clearButton = new KPushButton(KIcon("edit-clear"), i18n("Clear"));
243 QProgressBar *progressBar = new QProgressBar();
244
245 connect(pauseResumeButton, SIGNAL(clicked(bool)), this, SLOT(slotPauseResumeClicked()));
246 connect(cancelButton, SIGNAL(clicked(bool)), this, SLOT(slotCancelClicked()));
247 connect(clearButton, SIGNAL(clicked(bool)), this, SLOT(slotClearClicked()));
248
249 setBlockedEventTypes(pauseResumeButton, QList<QEvent::Type>() << QEvent::MouseButtonPress
250 << QEvent::MouseButtonRelease << QEvent::MouseButtonDblClick);
251 setBlockedEventTypes(cancelButton, QList<QEvent::Type>() << QEvent::MouseButtonPress
252 << QEvent::MouseButtonRelease << QEvent::MouseButtonDblClick);
253
254 widgetList << pauseResumeButton << cancelButton << progressBar << clearButton;
255
256 return widgetList;
257}
258
259void ProgressListDelegate::updateItemWidgets(const QList<QWidget*> widgets,
260 const QStyleOptionViewItem &option,
261 const QPersistentModelIndex &index) const
262{
263 if (!index.isValid()) {
264 return;
265 }
266
267 KPushButton *pauseResumeButton = static_cast<KPushButton*>(widgets[0]);
268
269 KPushButton *cancelButton = static_cast<KPushButton*>(widgets[1]);
270 cancelButton->setToolTip(i18n("Cancel"));
271
272 QProgressBar *progressBar = static_cast<QProgressBar*>(widgets[2]);
273 KPushButton *clearButton = static_cast<KPushButton*>(widgets[3]);
274
275 int percent = d->getPercent(index);
276
277 cancelButton->setVisible(percent < 100);
278 pauseResumeButton->setVisible(percent < 100);
279 clearButton->setVisible(percent > 99);
280
281 KJob::Capabilities capabilities = (KJob::Capabilities) index.model()->data(index, JobView::Capabilities).toInt();
282 cancelButton->setEnabled(capabilities & KJob::Killable);
283 pauseResumeButton->setEnabled(capabilities & KJob::Suspendable);
284
285
286 JobView::JobState state = (JobView::JobState) index.model()->data(index, JobView::State).toInt();
287 switch (state) {
288 case JobView::Running:
289 pauseResumeButton->setToolTip(i18n("Pause"));
290 pauseResumeButton->setIcon(KIcon("media-playback-pause"));
291 break;
292 case JobView::Suspended:
293 pauseResumeButton->setToolTip(i18n("Resume"));
294 pauseResumeButton->setIcon(KIcon("media-playback-start"));
295 break;
296 default:
297 Q_ASSERT(0);
298 break;
299 }
300
301 QSize progressBarButtonSizeHint;
302
303
304
305 if (percent < 100) {
306 QSize cancelButtonSizeHint = cancelButton->sizeHint();
307
308 cancelButton->move(option.rect.width() - d->separatorPixels - cancelButtonSizeHint.width(),
309 option.rect.height() - d->separatorPixels - cancelButtonSizeHint.height());
310
311 QSize pauseResumeButtonSizeHint = pauseResumeButton->sizeHint();
312
313
314 pauseResumeButton->move(option.rect.width() - d->separatorPixels * 2 - pauseResumeButtonSizeHint.width() - cancelButtonSizeHint.width(),
315 option.rect.height() - d->separatorPixels - pauseResumeButtonSizeHint.height());
316
317 progressBarButtonSizeHint = pauseResumeButtonSizeHint;
318 } else {
319 progressBarButtonSizeHint = clearButton->sizeHint();
320 clearButton->resize(progressBarButtonSizeHint);
321
322 clearButton->move(option.rect.width() - d->separatorPixels - progressBarButtonSizeHint.width(),
323 option.rect.height() - d->separatorPixels - progressBarButtonSizeHint.height());
324 }
325 progressBar->setValue(percent);
326
327 QFontMetrics fm(QApplication::font());
328 QSize progressBarSizeHint = progressBar->sizeHint();
329
330 progressBar->resize(QSize(option.rect.width() - d->getCurrentLeftMargin(fm.height()) - d->rightMargin, progressBarSizeHint.height()));
331
332 progressBar->move(d->getCurrentLeftMargin(fm.height()),
333 option.rect.height() - d->separatorPixels * 2 - progressBarButtonSizeHint.height() - progressBarSizeHint.height());
334}
335
336void ProgressListDelegate::slotPauseResumeClicked()
337{
338 const QModelIndex index = focusedIndex();
339 JobView *jobView = index.model()->data(index, JobView::JobViewRole).value<JobView*>();
340 JobView::JobState state = (JobView::JobState) index.model()->data(index, JobView::State).toInt();
341 if (jobView) {
342 switch (state) {
343 case JobView::Running:
344 jobView->requestSuspend();
345 break;
346 case JobView::Suspended:
347 jobView->requestResume();
348 break;
349 default:
350 Q_ASSERT(0); // this point should have never been reached
351 break;
352 }
353 }
354}
355
356void ProgressListDelegate::slotCancelClicked()
357{
358 const QModelIndex index = focusedIndex();
359 JobView *jobView = index.model()->data(index, JobView::JobViewRole).value<JobView*>();
360 if (jobView) {
361 jobView->requestCancel();
362 }
363}
364
365void ProgressListDelegate::slotClearClicked()
366{
367 const QModelIndex index = focusedIndex();
368 JobView *jobView = index.model()->data(index, JobView::JobViewRole).value<JobView*>();
369 if (jobView) {
370 jobView->terminate(QString());
371 }
372}
373
374#include "progresslistdelegate.moc"
375