|
1 /**************************************************************************** |
|
2 ** |
|
3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). |
|
4 ** All rights reserved. |
|
5 ** Contact: Nokia Corporation (qt-info@nokia.com) |
|
6 ** |
|
7 ** This file is part of the QtGui module of the Qt Toolkit. |
|
8 ** |
|
9 ** $QT_BEGIN_LICENSE:LGPL$ |
|
10 ** No Commercial Usage |
|
11 ** This file contains pre-release code and may not be distributed. |
|
12 ** You may use this file in accordance with the terms and conditions |
|
13 ** contained in the Technology Preview License Agreement accompanying |
|
14 ** this package. |
|
15 ** |
|
16 ** GNU Lesser General Public License Usage |
|
17 ** Alternatively, this file may be used under the terms of the GNU Lesser |
|
18 ** General Public License version 2.1 as published by the Free Software |
|
19 ** Foundation and appearing in the file LICENSE.LGPL included in the |
|
20 ** packaging of this file. Please review the following information to |
|
21 ** ensure the GNU Lesser General Public License version 2.1 requirements |
|
22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. |
|
23 ** |
|
24 ** In addition, as a special exception, Nokia gives you certain additional |
|
25 ** rights. These rights are described in the Nokia Qt LGPL Exception |
|
26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. |
|
27 ** |
|
28 ** If you have questions regarding the use of this file, please contact |
|
29 ** Nokia at qt-info@nokia.com. |
|
30 ** |
|
31 ** |
|
32 ** |
|
33 ** |
|
34 ** |
|
35 ** |
|
36 ** |
|
37 ** |
|
38 ** $QT_END_LICENSE$ |
|
39 ** |
|
40 ****************************************************************************/ |
|
41 |
|
42 #include "qprogressbar.h" |
|
43 #ifndef QT_NO_PROGRESSBAR |
|
44 #include <qevent.h> |
|
45 #include <qpainter.h> |
|
46 #include <qstylepainter.h> |
|
47 #include <qstyleoption.h> |
|
48 #include <private/qwidget_p.h> |
|
49 #ifndef QT_NO_ACCESSIBILITY |
|
50 #include <qaccessible.h> |
|
51 #endif |
|
52 #include <limits.h> |
|
53 |
|
54 QT_BEGIN_NAMESPACE |
|
55 |
|
56 class QProgressBarPrivate : public QWidgetPrivate |
|
57 { |
|
58 Q_DECLARE_PUBLIC(QProgressBar) |
|
59 |
|
60 public: |
|
61 QProgressBarPrivate(); |
|
62 |
|
63 void init(); |
|
64 inline void resetLayoutItemMargins(); |
|
65 |
|
66 int minimum; |
|
67 int maximum; |
|
68 int value; |
|
69 Qt::Alignment alignment; |
|
70 uint textVisible : 1; |
|
71 int lastPaintedValue; |
|
72 Qt::Orientation orientation; |
|
73 bool invertedAppearance; |
|
74 QProgressBar::Direction textDirection; |
|
75 QString format; |
|
76 inline int bound(int val) const { return qMax(minimum-1, qMin(maximum, val)); } |
|
77 bool repaintRequired() const; |
|
78 }; |
|
79 |
|
80 QProgressBarPrivate::QProgressBarPrivate() |
|
81 : minimum(0), maximum(100), value(-1), alignment(Qt::AlignLeft), textVisible(true), |
|
82 lastPaintedValue(-1), orientation(Qt::Horizontal), invertedAppearance(false), |
|
83 textDirection(QProgressBar::TopToBottom), format(QLatin1String("%p%")) |
|
84 { |
|
85 } |
|
86 |
|
87 void QProgressBarPrivate::init() |
|
88 { |
|
89 Q_Q(QProgressBar); |
|
90 QSizePolicy sp(QSizePolicy::Expanding, QSizePolicy::Fixed); |
|
91 if (orientation == Qt::Vertical) |
|
92 sp.transpose(); |
|
93 q->setSizePolicy(sp); |
|
94 q->setAttribute(Qt::WA_WState_OwnSizePolicy, false); |
|
95 resetLayoutItemMargins(); |
|
96 } |
|
97 |
|
98 void QProgressBarPrivate::resetLayoutItemMargins() |
|
99 { |
|
100 Q_Q(QProgressBar); |
|
101 QStyleOptionProgressBar option; |
|
102 q->initStyleOption(&option); |
|
103 setLayoutItemMargins(QStyle::SE_ProgressBarLayoutItem, &option); |
|
104 } |
|
105 |
|
106 /*! |
|
107 Initialize \a option with the values from this QProgressBar. This method is useful |
|
108 for subclasses when they need a QStyleOptionProgressBar or QStyleOptionProgressBarV2, |
|
109 but don't want to fill in all the information themselves. This function will check the version |
|
110 of the QStyleOptionProgressBar and fill in the additional values for a |
|
111 QStyleOptionProgressBarV2. |
|
112 |
|
113 \sa QStyleOption::initFrom() |
|
114 */ |
|
115 void QProgressBar::initStyleOption(QStyleOptionProgressBar *option) const |
|
116 { |
|
117 if (!option) |
|
118 return; |
|
119 Q_D(const QProgressBar); |
|
120 option->initFrom(this); |
|
121 |
|
122 if (d->orientation == Qt::Horizontal) |
|
123 option->state |= QStyle::State_Horizontal; |
|
124 option->minimum = d->minimum; |
|
125 option->maximum = d->maximum; |
|
126 option->progress = d->value; |
|
127 option->textAlignment = d->alignment; |
|
128 option->textVisible = d->textVisible; |
|
129 option->text = text(); |
|
130 |
|
131 if (QStyleOptionProgressBarV2 *optionV2 |
|
132 = qstyleoption_cast<QStyleOptionProgressBarV2 *>(option)) { |
|
133 optionV2->orientation = d->orientation; // ### Qt 5: use State_Horizontal instead |
|
134 optionV2->invertedAppearance = d->invertedAppearance; |
|
135 optionV2->bottomToTop = (d->textDirection == QProgressBar::BottomToTop); |
|
136 } |
|
137 } |
|
138 |
|
139 bool QProgressBarPrivate::repaintRequired() const |
|
140 { |
|
141 Q_Q(const QProgressBar); |
|
142 if (value == lastPaintedValue) |
|
143 return false; |
|
144 |
|
145 int valueDifference = qAbs(value - lastPaintedValue); |
|
146 |
|
147 // Check if the text needs to be repainted |
|
148 if (value == minimum || value == maximum) |
|
149 return true; |
|
150 if (textVisible) { |
|
151 if ((format.contains(QLatin1String("%v")))) |
|
152 return true; |
|
153 if ((format.contains(QLatin1String("%p")) |
|
154 && valueDifference >= qAbs((maximum - minimum) / 100))) |
|
155 return true; |
|
156 } |
|
157 |
|
158 // Check if the bar needs to be repainted |
|
159 QStyleOptionProgressBarV2 opt; |
|
160 q->initStyleOption(&opt); |
|
161 int cw = q->style()->pixelMetric(QStyle::PM_ProgressBarChunkWidth, &opt, q); |
|
162 QRect groove = q->style()->subElementRect(QStyle::SE_ProgressBarGroove, &opt, q); |
|
163 // This expression is basically |
|
164 // (valueDifference / (maximum - minimum) > cw / groove.width()) |
|
165 // transformed to avoid integer division. |
|
166 int grooveBlock = (q->orientation() == Qt::Horizontal) ? groove.width() : groove.height(); |
|
167 return (valueDifference * grooveBlock > cw * (maximum - minimum)); |
|
168 } |
|
169 |
|
170 /*! |
|
171 \class QProgressBar |
|
172 \brief The QProgressBar widget provides a horizontal or vertical progress bar. |
|
173 |
|
174 \ingroup basicwidgets |
|
175 |
|
176 |
|
177 A progress bar is used to give the user an indication of the |
|
178 progress of an operation and to reassure them that the application |
|
179 is still running. |
|
180 |
|
181 The progress bar uses the concept of \e steps. You set it up by |
|
182 specifying the minimum and maximum possible step values, and it |
|
183 will display the percentage of steps that have been completed |
|
184 when you later give it the current step value. The percentage is |
|
185 calculated by dividing the progress (value() - minimum()) divided |
|
186 by maximum() - minimum(). |
|
187 |
|
188 You can specify the minimum and maximum number of steps with |
|
189 setMinimum() and setMaximum. The current number of steps is set |
|
190 with setValue(). The progress bar can be rewound to the |
|
191 beginning with reset(). |
|
192 |
|
193 If minimum and maximum both are set to 0, the bar shows a busy |
|
194 indicator instead of a percentage of steps. This is useful, for |
|
195 example, when using QFtp or QNetworkAccessManager to download |
|
196 items when they are unable to determine the size of the item being |
|
197 downloaded. |
|
198 |
|
199 \table |
|
200 \row \o \inlineimage macintosh-progressbar.png Screenshot of a Macintosh style progress bar |
|
201 \o A progress bar shown in the Macintosh widget style. |
|
202 \row \o \inlineimage windowsxp-progressbar.png Screenshot of a Windows XP style progress bar |
|
203 \o A progress bar shown in the Windows XP widget style. |
|
204 \row \o \inlineimage plastique-progressbar.png Screenshot of a Plastique style progress bar |
|
205 \o A progress bar shown in the Plastique widget style. |
|
206 \endtable |
|
207 |
|
208 \sa QProgressDialog, {fowler}{GUI Design Handbook: Progress Indicator} |
|
209 */ |
|
210 |
|
211 /*! |
|
212 \since 4.1 |
|
213 \enum QProgressBar::Direction |
|
214 \brief Specifies the reading direction of the \l text for vertical progress bars. |
|
215 |
|
216 \value TopToBottom The text is rotated 90 degrees clockwise. |
|
217 \value BottomToTop The text is rotated 90 degrees counter-clockwise. |
|
218 |
|
219 Note that whether or not the text is drawn is dependent on the style. |
|
220 Currently CDE, CleanLooks, Motif, and Plastique draw the text. Mac, Windows |
|
221 and WindowsXP style do not. |
|
222 |
|
223 \sa textDirection |
|
224 */ |
|
225 |
|
226 /*! |
|
227 \fn void QProgressBar::valueChanged(int value) |
|
228 |
|
229 This signal is emitted when the value shown in the progress bar changes. |
|
230 \a value is the new value shown by the progress bar. |
|
231 */ |
|
232 |
|
233 /*! |
|
234 Constructs a progress bar with the given \a parent. |
|
235 |
|
236 By default, the minimum step value is set to 0, and the maximum to 100. |
|
237 |
|
238 \sa setRange() |
|
239 */ |
|
240 |
|
241 QProgressBar::QProgressBar(QWidget *parent) |
|
242 : QWidget(*(new QProgressBarPrivate), parent, 0) |
|
243 { |
|
244 d_func()->init(); |
|
245 } |
|
246 |
|
247 /*! |
|
248 Reset the progress bar. The progress bar "rewinds" and shows no |
|
249 progress. |
|
250 */ |
|
251 |
|
252 void QProgressBar::reset() |
|
253 { |
|
254 Q_D(QProgressBar); |
|
255 d->value = d->minimum - 1; |
|
256 if (d->minimum == INT_MIN) |
|
257 d->value = INT_MIN; |
|
258 repaint(); |
|
259 } |
|
260 |
|
261 /*! |
|
262 \property QProgressBar::minimum |
|
263 \brief the progress bar's minimum value |
|
264 |
|
265 When setting this property, the \l maximum is adjusted if |
|
266 necessary to ensure that the range remains valid. If the |
|
267 current value falls outside the new range, the progress bar is reset |
|
268 with reset(). |
|
269 */ |
|
270 void QProgressBar::setMinimum(int minimum) |
|
271 { |
|
272 setRange(minimum, qMax(d_func()->maximum, minimum)); |
|
273 } |
|
274 |
|
275 int QProgressBar::minimum() const |
|
276 { |
|
277 return d_func()->minimum; |
|
278 } |
|
279 |
|
280 |
|
281 /*! |
|
282 \property QProgressBar::maximum |
|
283 \brief the progress bar's maximum value |
|
284 |
|
285 When setting this property, the \l minimum is adjusted if |
|
286 necessary to ensure that the range remains valid. If the |
|
287 current value falls outside the new range, the progress bar is reset |
|
288 with reset(). |
|
289 */ |
|
290 |
|
291 void QProgressBar::setMaximum(int maximum) |
|
292 { |
|
293 setRange(qMin(d_func()->minimum, maximum), maximum); |
|
294 } |
|
295 |
|
296 int QProgressBar::maximum() const |
|
297 { |
|
298 return d_func()->maximum; |
|
299 } |
|
300 |
|
301 /*! |
|
302 \property QProgressBar::value |
|
303 \brief the progress bar's current value |
|
304 |
|
305 Attempting to change the current value to one outside |
|
306 the minimum-maximum range has no effect on the current value. |
|
307 */ |
|
308 void QProgressBar::setValue(int value) |
|
309 { |
|
310 Q_D(QProgressBar); |
|
311 if (d->value == value |
|
312 || ((value > d->maximum || value < d->minimum) |
|
313 && (d->maximum != 0 || d->minimum != 0))) |
|
314 return; |
|
315 d->value = value; |
|
316 emit valueChanged(value); |
|
317 #ifndef QT_NO_ACCESSIBILITY |
|
318 QAccessible::updateAccessibility(this, 0, QAccessible::ValueChanged); |
|
319 #endif |
|
320 if (d->repaintRequired()) |
|
321 repaint(); |
|
322 } |
|
323 |
|
324 int QProgressBar::value() const |
|
325 { |
|
326 return d_func()->value; |
|
327 } |
|
328 |
|
329 /*! |
|
330 Sets the progress bar's minimum and maximum values to \a minimum and |
|
331 \a maximum respectively. |
|
332 |
|
333 If \a maximum is smaller than \a minimum, \a minimum becomes the only |
|
334 legal value. |
|
335 |
|
336 If the current value falls outside the new range, the progress bar is reset |
|
337 with reset(). |
|
338 |
|
339 \sa minimum maximum |
|
340 */ |
|
341 void QProgressBar::setRange(int minimum, int maximum) |
|
342 { |
|
343 Q_D(QProgressBar); |
|
344 d->minimum = minimum; |
|
345 d->maximum = qMax(minimum, maximum); |
|
346 if ( d->value <(d->minimum-1) || d->value > d->maximum) |
|
347 reset(); |
|
348 } |
|
349 /*! |
|
350 \property QProgressBar::textVisible |
|
351 \brief whether the current completed percentage should be displayed |
|
352 |
|
353 This property may be ignored by the style (e.g., QMacStyle never draws the text). |
|
354 |
|
355 \sa textDirection |
|
356 */ |
|
357 void QProgressBar::setTextVisible(bool visible) |
|
358 { |
|
359 Q_D(QProgressBar); |
|
360 if (d->textVisible != visible) { |
|
361 d->textVisible = visible; |
|
362 repaint(); |
|
363 } |
|
364 } |
|
365 |
|
366 bool QProgressBar::isTextVisible() const |
|
367 { |
|
368 return d_func()->textVisible; |
|
369 } |
|
370 |
|
371 /*! |
|
372 \property QProgressBar::alignment |
|
373 \brief the alignment of the progress bar |
|
374 */ |
|
375 void QProgressBar::setAlignment(Qt::Alignment alignment) |
|
376 { |
|
377 if (d_func()->alignment != alignment) { |
|
378 d_func()->alignment = alignment; |
|
379 repaint(); |
|
380 } |
|
381 } |
|
382 |
|
383 Qt::Alignment QProgressBar::alignment() const |
|
384 { |
|
385 return d_func()->alignment; |
|
386 } |
|
387 |
|
388 /*! |
|
389 \reimp |
|
390 */ |
|
391 void QProgressBar::paintEvent(QPaintEvent *) |
|
392 { |
|
393 QStylePainter paint(this); |
|
394 QStyleOptionProgressBarV2 opt; |
|
395 initStyleOption(&opt); |
|
396 paint.drawControl(QStyle::CE_ProgressBar, opt); |
|
397 d_func()->lastPaintedValue = d_func()->value; |
|
398 } |
|
399 |
|
400 /*! |
|
401 \reimp |
|
402 */ |
|
403 QSize QProgressBar::sizeHint() const |
|
404 { |
|
405 ensurePolished(); |
|
406 QFontMetrics fm = fontMetrics(); |
|
407 QStyleOptionProgressBarV2 opt; |
|
408 initStyleOption(&opt); |
|
409 int cw = style()->pixelMetric(QStyle::PM_ProgressBarChunkWidth, &opt, this); |
|
410 QSize size = QSize(qMax(9, cw) * 7 + fm.width(QLatin1Char('0')) * 4, fm.height() + 8); |
|
411 if (opt.orientation == Qt::Vertical) |
|
412 size.transpose(); |
|
413 return style()->sizeFromContents(QStyle::CT_ProgressBar, &opt, size, this); |
|
414 } |
|
415 |
|
416 /*! |
|
417 \reimp |
|
418 */ |
|
419 QSize QProgressBar::minimumSizeHint() const |
|
420 { |
|
421 QSize size; |
|
422 if (orientation() == Qt::Horizontal) |
|
423 size = QSize(sizeHint().width(), fontMetrics().height() + 2); |
|
424 else |
|
425 size = QSize(fontMetrics().height() + 2, sizeHint().height()); |
|
426 return size; |
|
427 } |
|
428 |
|
429 /*! |
|
430 \property QProgressBar::text |
|
431 \brief the descriptive text shown with the progress bar |
|
432 |
|
433 The text returned is the same as the text displayed in the center |
|
434 (or in some styles, to the left) of the progress bar. |
|
435 |
|
436 The progress shown in the text may be smaller than the minimum value, |
|
437 indicating that the progress bar is in the "reset" state before any |
|
438 progress is set. |
|
439 |
|
440 In the default implementation, the text either contains a percentage |
|
441 value that indicates the progress so far, or it is blank because the |
|
442 progress bar is in the reset state. |
|
443 */ |
|
444 QString QProgressBar::text() const |
|
445 { |
|
446 Q_D(const QProgressBar); |
|
447 if ((d->maximum == 0 && d->minimum == 0) || d->value < d->minimum |
|
448 || (d->value == INT_MIN && d->minimum == INT_MIN)) |
|
449 return QString(); |
|
450 |
|
451 qint64 totalSteps = qint64(d->maximum) - d->minimum; |
|
452 |
|
453 QString result = d->format; |
|
454 result.replace(QLatin1String("%m"), QString::number(totalSteps)); |
|
455 result.replace(QLatin1String("%v"), QString::number(d->value)); |
|
456 |
|
457 // If max and min are equal and we get this far, it means that the |
|
458 // progress bar has one step and that we are on that step. Return |
|
459 // 100% here in order to avoid division by zero further down. |
|
460 if (totalSteps == 0) { |
|
461 result.replace(QLatin1String("%p"), QString::number(100)); |
|
462 return result; |
|
463 } |
|
464 |
|
465 int progress = (qreal(d->value) - d->minimum) * 100.0 / totalSteps; |
|
466 result.replace(QLatin1String("%p"), QString::number(progress)); |
|
467 return result; |
|
468 } |
|
469 |
|
470 /*! |
|
471 \since 4.1 |
|
472 \property QProgressBar::orientation |
|
473 \brief the orientation of the progress bar |
|
474 |
|
475 The orientation must be \l Qt::Horizontal (the default) or \l |
|
476 Qt::Vertical. |
|
477 |
|
478 \sa invertedAppearance, textDirection |
|
479 */ |
|
480 |
|
481 void QProgressBar::setOrientation(Qt::Orientation orientation) |
|
482 { |
|
483 Q_D(QProgressBar); |
|
484 if (d->orientation == orientation) |
|
485 return; |
|
486 d->orientation = orientation; |
|
487 if (!testAttribute(Qt::WA_WState_OwnSizePolicy)) { |
|
488 QSizePolicy sp = sizePolicy(); |
|
489 sp.transpose(); |
|
490 setSizePolicy(sp); |
|
491 setAttribute(Qt::WA_WState_OwnSizePolicy, false); |
|
492 } |
|
493 d->resetLayoutItemMargins(); |
|
494 update(); |
|
495 updateGeometry(); |
|
496 } |
|
497 |
|
498 Qt::Orientation QProgressBar::orientation() const |
|
499 { |
|
500 Q_D(const QProgressBar); |
|
501 return d->orientation; |
|
502 } |
|
503 |
|
504 /*! |
|
505 \since 4.1 |
|
506 \property QProgressBar::invertedAppearance |
|
507 \brief whether or not a progress bar shows its progress inverted |
|
508 |
|
509 If this property is false, the progress bar grows in the other |
|
510 direction (e.g. from right to left). By default, the progress bar |
|
511 is not inverted. |
|
512 |
|
513 \sa orientation, layoutDirection |
|
514 */ |
|
515 |
|
516 void QProgressBar::setInvertedAppearance(bool invert) |
|
517 { |
|
518 Q_D(QProgressBar); |
|
519 d->invertedAppearance = invert; |
|
520 update(); |
|
521 } |
|
522 |
|
523 bool QProgressBar::invertedAppearance() |
|
524 { |
|
525 Q_D(QProgressBar); |
|
526 return d->invertedAppearance; |
|
527 } |
|
528 |
|
529 /*! |
|
530 \since 4.1 |
|
531 \property QProgressBar::textDirection |
|
532 \brief the reading direction of the \l text for vertical progress bars |
|
533 |
|
534 This property has no impact on horizontal progress bars. |
|
535 By default, the reading direction is QProgressBar::TopToBottom. |
|
536 |
|
537 \sa orientation, textVisible |
|
538 */ |
|
539 void QProgressBar::setTextDirection(QProgressBar::Direction textDirection) |
|
540 { |
|
541 Q_D(QProgressBar); |
|
542 d->textDirection = textDirection; |
|
543 update(); |
|
544 } |
|
545 |
|
546 QProgressBar::Direction QProgressBar::textDirection() |
|
547 { |
|
548 Q_D(QProgressBar); |
|
549 return d->textDirection; |
|
550 } |
|
551 |
|
552 /*! \reimp */ |
|
553 bool QProgressBar::event(QEvent *e) |
|
554 { |
|
555 Q_D(QProgressBar); |
|
556 if (e->type() == QEvent::StyleChange |
|
557 #ifdef Q_WS_MAC |
|
558 || e->type() == QEvent::MacSizeChange |
|
559 #endif |
|
560 ) |
|
561 d->resetLayoutItemMargins(); |
|
562 return QWidget::event(e); |
|
563 } |
|
564 |
|
565 /*! |
|
566 \since 4.2 |
|
567 \property QProgressBar::format |
|
568 \brief the string used to generate the current text |
|
569 |
|
570 %p - is replaced by the percentage completed. |
|
571 %v - is replaced by the current value. |
|
572 %m - is replaced by the total number of steps. |
|
573 |
|
574 The default value is "%p%". |
|
575 |
|
576 \sa text() |
|
577 */ |
|
578 void QProgressBar::setFormat(const QString &format) |
|
579 { |
|
580 Q_D(QProgressBar); |
|
581 if (d->format == format) |
|
582 return; |
|
583 d->format = format; |
|
584 update(); |
|
585 } |
|
586 |
|
587 QString QProgressBar::format() const |
|
588 { |
|
589 Q_D(const QProgressBar); |
|
590 return d->format; |
|
591 } |
|
592 |
|
593 QT_END_NAMESPACE |
|
594 |
|
595 #endif // QT_NO_PROGRESSBAR |