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 | |
39 | QString ProgressListDelegate::Private::getIcon(const QModelIndex &index) const |
40 | { |
41 | return index.model()->data(index, JobView::Icon).toString(); |
42 | } |
43 | |
44 | QString ProgressListDelegate::Private::getSizeTotal(const QModelIndex &index) const |
45 | { |
46 | return index.model()->data(index, JobView::SizeTotal).toString(); |
47 | } |
48 | |
49 | QString ProgressListDelegate::Private::getSizeProcessed(const QModelIndex &index) const |
50 | { |
51 | return index.model()->data(index, JobView::SizeProcessed).toString(); |
52 | } |
53 | |
54 | qlonglong ProgressListDelegate::Private::getTimeTotal(const QModelIndex &index) const |
55 | { |
56 | return index.model()->data(index, JobView::TimeTotal).toLongLong(); |
57 | } |
58 | |
59 | qlonglong ProgressListDelegate::Private::getTimeProcessed(const QModelIndex &index) const |
60 | { |
61 | return index.model()->data(index, JobView::TimeElapsed).toLongLong(); |
62 | } |
63 | |
64 | QString ProgressListDelegate::Private::getSpeed(const QModelIndex &index) const |
65 | { |
66 | return index.model()->data(index, JobView::Speed).toString(); |
67 | } |
68 | |
69 | int ProgressListDelegate::Private::getPercent(const QModelIndex &index) const |
70 | { |
71 | return index.model()->data(index, JobView::Percent).toInt(); |
72 | } |
73 | |
74 | QString ProgressListDelegate::Private::getInfoMessage(const QModelIndex &index) const |
75 | { |
76 | return index.model()->data(index, JobView::InfoMessage).toString(); |
77 | } |
78 | |
79 | int ProgressListDelegate::Private::getCurrentLeftMargin(int fontHeight) const |
80 | { |
81 | return leftMargin + separatorPixels + fontHeight; |
82 | } |
83 | |
84 | ProgressListDelegate::ProgressListDelegate(QObject *parent, QListView *listView) |
85 | : KWidgetItemDelegate(listView, parent) |
86 | , d(new Private(listView)) |
87 | { |
88 | } |
89 | |
90 | ProgressListDelegate::~ProgressListDelegate() |
91 | { |
92 | delete d; |
93 | } |
94 | |
95 | void 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 | |
166 | QSize 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 | |
202 | void ProgressListDelegate::setSeparatorPixels(int separatorPixels) |
203 | { |
204 | d->separatorPixels = separatorPixels; |
205 | } |
206 | |
207 | void ProgressListDelegate::setLeftMargin(int leftMargin) |
208 | { |
209 | d->leftMargin = leftMargin; |
210 | } |
211 | |
212 | void ProgressListDelegate::setRightMargin(int rightMargin) |
213 | { |
214 | d->rightMargin = rightMargin; |
215 | } |
216 | |
217 | void ProgressListDelegate::setMinimumItemHeight(int minimumItemHeight) |
218 | { |
219 | d->minimumItemHeight = minimumItemHeight; |
220 | } |
221 | |
222 | void ProgressListDelegate::setMinimumContentWidth(int minimumContentWidth) |
223 | { |
224 | d->minimumContentWidth = minimumContentWidth; |
225 | } |
226 | |
227 | void ProgressListDelegate::setEditorHeight(int editorHeight) |
228 | { |
229 | d->editorHeight = editorHeight; |
230 | } |
231 | |
232 | QList<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 | |
259 | void 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 | |
336 | void 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 | |
356 | void 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 | |
365 | void 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 | |