8f0da78613a2e4dc94a5e299796b5d1da087982b
[profile/ivi/qtbase.git] / src / widgets / widgets / qprogressbar.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/legal
5 **
6 ** This file is part of the QtGui module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and Digia.  For licensing terms and
14 ** conditions see http://qt.digia.com/licensing.  For further information
15 ** use the contact form at http://qt.digia.com/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file.  Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24 **
25 ** In addition, as a special exception, Digia gives you certain additional
26 ** rights.  These rights are described in the Digia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28 **
29 ** GNU General Public License Usage
30 ** Alternatively, this file may be used under the terms of the GNU
31 ** General Public License version 3.0 as published by the Free Software
32 ** Foundation and appearing in the file LICENSE.GPL included in the
33 ** packaging of this file.  Please review the following information to
34 ** ensure the GNU General Public License version 3.0 requirements will be
35 ** met: http://www.gnu.org/copyleft/gpl.html.
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     \inmodule QtWidgets
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 QNetworkAccessManager to download items when
196     they are unable to determine the size of the item being downloaded.
197
198     \table
199     \row \li \inlineimage macintosh-progressbar.png Screenshot of a Macintosh style progress bar
200          \li A progress bar shown in the Macintosh widget style.
201     \row \li \inlineimage windowsxp-progressbar.png Screenshot of a Windows XP style progress bar
202          \li A progress bar shown in the Windows XP widget style.
203     \row \li \inlineimage plastique-progressbar.png Screenshot of a Plastique style progress bar
204          \li A progress bar shown in the Plastique widget style.
205     \endtable
206
207     \sa QProgressDialog, {fowler}{GUI Design Handbook: Progress Indicator}
208 */
209
210 /*!
211     \since 4.1
212     \enum QProgressBar::Direction
213     \brief Specifies the reading direction of the \l text for vertical progress bars.
214
215     \value TopToBottom The text is rotated 90 degrees clockwise.
216     \value BottomToTop The text is rotated 90 degrees counter-clockwise.
217
218     Note that whether or not the text is drawn is dependent on the style.
219     Currently CleanLooks and Plastique draw the text. Mac, Windows
220     and WindowsXP style do not.
221
222     \sa textDirection
223 */
224
225 /*!
226     \fn void QProgressBar::valueChanged(int value)
227
228     This signal is emitted when the value shown in the progress bar changes.
229     \a value is the new value shown by the progress bar.
230 */
231
232 /*!
233     Constructs a progress bar with the given \a parent.
234
235     By default, the minimum step value is set to 0, and the maximum to 100.
236
237     \sa setRange()
238 */
239
240 QProgressBar::QProgressBar(QWidget *parent)
241     : QWidget(*(new QProgressBarPrivate), parent, 0)
242 {
243     d_func()->init();
244 }
245
246 /*!
247     Reset the progress bar. The progress bar "rewinds" and shows no
248     progress.
249 */
250
251 void QProgressBar::reset()
252 {
253     Q_D(QProgressBar);
254     d->value = d->minimum - 1;
255     if (d->minimum == INT_MIN)
256         d->value = INT_MIN;
257     repaint();
258 }
259
260 /*!
261     \property QProgressBar::minimum
262     \brief the progress bar's minimum value
263
264     When setting this property, the \l maximum is adjusted if
265     necessary to ensure that the range remains valid. If the
266     current value falls outside the new range, the progress bar is reset
267     with reset().
268 */
269 void QProgressBar::setMinimum(int minimum)
270 {
271     setRange(minimum, qMax(d_func()->maximum, minimum));
272 }
273
274 int QProgressBar::minimum() const
275 {
276     return d_func()->minimum;
277 }
278
279
280 /*!
281     \property QProgressBar::maximum
282     \brief the progress bar's maximum value
283
284     When setting this property, the \l minimum is adjusted if
285     necessary to ensure that the range remains valid. If the
286     current value falls outside the new range, the progress bar is reset
287     with reset().
288 */
289
290 void QProgressBar::setMaximum(int maximum)
291 {
292     setRange(qMin(d_func()->minimum, maximum), maximum);
293 }
294
295 int QProgressBar::maximum() const
296 {
297     return d_func()->maximum;
298 }
299
300 /*!
301     \property QProgressBar::value
302     \brief the progress bar's current value
303
304     Attempting to change the current value to one outside
305     the minimum-maximum range has no effect on the current value.
306 */
307 void QProgressBar::setValue(int value)
308 {
309     Q_D(QProgressBar);
310     if (d->value == value
311             || ((value > d->maximum || value < d->minimum)
312                 && (d->maximum != 0 || d->minimum != 0)))
313         return;
314     d->value = value;
315     emit valueChanged(value);
316 #ifndef QT_NO_ACCESSIBILITY
317     if (isVisible()) {
318         QAccessibleValueChangeEvent event(this, value);
319         QAccessible::updateAccessibility(&event);
320     }
321 #endif
322     if (d->repaintRequired())
323         repaint();
324 }
325
326 int QProgressBar::value() const
327 {
328     return d_func()->value;
329 }
330
331 /*!
332     Sets the progress bar's minimum and maximum values to \a minimum and
333     \a maximum respectively.
334
335     If \a maximum is smaller than \a minimum, \a minimum becomes the only
336     legal value.
337
338     If the current value falls outside the new range, the progress bar is reset
339     with reset().
340
341     \sa minimum, maximum
342 */
343 void QProgressBar::setRange(int minimum, int maximum)
344 {
345     Q_D(QProgressBar);
346     if (minimum != d->minimum || maximum != d->maximum) {
347         d->minimum = minimum;
348         d->maximum = qMax(minimum, maximum);
349
350         if (d->value < (d->minimum - 1) || d->value > d->maximum)
351             reset();
352         else
353             update();
354     }
355 }
356
357 /*!
358     \property QProgressBar::textVisible
359     \brief whether the current completed percentage should be displayed
360
361     This property may be ignored by the style (e.g., QMacStyle never draws the text).
362
363     \sa textDirection
364 */
365 void QProgressBar::setTextVisible(bool visible)
366 {
367     Q_D(QProgressBar);
368     if (d->textVisible != visible) {
369         d->textVisible = visible;
370         repaint();
371     }
372 }
373
374 bool QProgressBar::isTextVisible() const
375 {
376     return d_func()->textVisible;
377 }
378
379 /*!
380     \property QProgressBar::alignment
381     \brief the alignment of the progress bar
382 */
383 void QProgressBar::setAlignment(Qt::Alignment alignment)
384 {
385     if (d_func()->alignment != alignment) {
386         d_func()->alignment = alignment;
387         repaint();
388     }
389 }
390
391 Qt::Alignment QProgressBar::alignment() const
392 {
393     return d_func()->alignment;
394 }
395
396 /*!
397     \reimp
398 */
399 void QProgressBar::paintEvent(QPaintEvent *)
400 {
401     QStylePainter paint(this);
402     QStyleOptionProgressBarV2 opt;
403     initStyleOption(&opt);
404     paint.drawControl(QStyle::CE_ProgressBar, opt);
405     d_func()->lastPaintedValue = d_func()->value;
406 }
407
408 /*!
409     \reimp
410 */
411 QSize QProgressBar::sizeHint() const
412 {
413     ensurePolished();
414     QFontMetrics fm = fontMetrics();
415     QStyleOptionProgressBarV2 opt;
416     initStyleOption(&opt);
417     int cw = style()->pixelMetric(QStyle::PM_ProgressBarChunkWidth, &opt, this);
418     QSize size = QSize(qMax(9, cw) * 7 + fm.width(QLatin1Char('0')) * 4, fm.height() + 8);
419     if (opt.orientation == Qt::Vertical)
420         size.transpose();
421     return style()->sizeFromContents(QStyle::CT_ProgressBar, &opt, size, this);
422 }
423
424 /*!
425     \reimp
426 */
427 QSize QProgressBar::minimumSizeHint() const
428 {
429     QSize size;
430     if (orientation() == Qt::Horizontal)
431         size = QSize(sizeHint().width(), fontMetrics().height() + 2);
432     else
433         size = QSize(fontMetrics().height() + 2, sizeHint().height());
434     return size;
435 }
436
437 /*!
438     \property QProgressBar::text
439     \brief the descriptive text shown with the progress bar
440
441     The text returned is the same as the text displayed in the center
442     (or in some styles, to the left) of the progress bar.
443
444     The progress shown in the text may be smaller than the minimum value,
445     indicating that the progress bar is in the "reset" state before any
446     progress is set.
447
448     In the default implementation, the text either contains a percentage
449     value that indicates the progress so far, or it is blank because the
450     progress bar is in the reset state.
451 */
452 QString QProgressBar::text() const
453 {
454     Q_D(const QProgressBar);
455     if ((d->maximum == 0 && d->minimum == 0) || d->value < d->minimum
456             || (d->value == INT_MIN && d->minimum == INT_MIN))
457         return QString();
458
459     qint64 totalSteps = qint64(d->maximum) - d->minimum;
460
461     QString result = d->format;
462     result.replace(QLatin1String("%m"), QString::number(totalSteps));
463     result.replace(QLatin1String("%v"), QString::number(d->value));
464
465     // If max and min are equal and we get this far, it means that the
466     // progress bar has one step and that we are on that step. Return
467     // 100% here in order to avoid division by zero further down.
468     if (totalSteps == 0) {
469         result.replace(QLatin1String("%p"), QString::number(100));
470         return result;
471     }
472
473     int progress = (qreal(d->value) - d->minimum) * 100.0 / totalSteps;
474     result.replace(QLatin1String("%p"), QString::number(progress));
475     return result;
476 }
477
478 /*!
479     \since 4.1
480     \property QProgressBar::orientation
481     \brief the orientation of the progress bar
482
483     The orientation must be \l Qt::Horizontal (the default) or \l
484     Qt::Vertical.
485
486     \sa invertedAppearance, textDirection
487 */
488
489 void QProgressBar::setOrientation(Qt::Orientation orientation)
490 {
491     Q_D(QProgressBar);
492     if (d->orientation == orientation)
493         return;
494     d->orientation = orientation;
495     if (!testAttribute(Qt::WA_WState_OwnSizePolicy)) {
496         QSizePolicy sp = sizePolicy();
497         sp.transpose();
498         setSizePolicy(sp);
499         setAttribute(Qt::WA_WState_OwnSizePolicy, false);
500     }
501     d->resetLayoutItemMargins();
502     update();
503     updateGeometry();
504 }
505
506 Qt::Orientation QProgressBar::orientation() const
507 {
508     Q_D(const QProgressBar);
509     return d->orientation;
510 }
511
512 /*!
513     \since 4.1
514     \property QProgressBar::invertedAppearance
515     \brief whether or not a progress bar shows its progress inverted
516
517     If this property is false, the progress bar grows in the other
518     direction (e.g. from right to left). By default, the progress bar
519     is not inverted.
520
521     \sa orientation, layoutDirection
522 */
523
524 void QProgressBar::setInvertedAppearance(bool invert)
525 {
526     Q_D(QProgressBar);
527     d->invertedAppearance = invert;
528     update();
529 }
530
531 bool QProgressBar::invertedAppearance() const
532 {
533     Q_D(const QProgressBar);
534     return d->invertedAppearance;
535 }
536
537 /*!
538     \since 4.1
539     \property QProgressBar::textDirection
540     \brief the reading direction of the \l text for vertical progress bars
541
542     This property has no impact on horizontal progress bars.
543     By default, the reading direction is QProgressBar::TopToBottom.
544
545     \sa orientation, textVisible
546 */
547 void QProgressBar::setTextDirection(QProgressBar::Direction textDirection)
548 {
549     Q_D(QProgressBar);
550     d->textDirection = textDirection;
551     update();
552 }
553
554 QProgressBar::Direction QProgressBar::textDirection() const
555 {
556     Q_D(const QProgressBar);
557     return d->textDirection;
558 }
559
560 /*! \reimp */
561 bool QProgressBar::event(QEvent *e)
562 {
563     Q_D(QProgressBar);
564     if (e->type() == QEvent::StyleChange
565 #ifdef Q_WS_MAC
566             || e->type() == QEvent::MacSizeChange
567 #endif
568             )
569         d->resetLayoutItemMargins();
570     return QWidget::event(e);
571 }
572
573 /*!
574     \since 4.2
575     \property QProgressBar::format
576     \brief the string used to generate the current text
577
578     %p - is replaced by the percentage completed.
579     %v - is replaced by the current value.
580     %m - is replaced by the total number of steps.
581
582     The default value is "%p%".
583
584     \sa text()
585 */
586 void QProgressBar::setFormat(const QString &format)
587 {
588     Q_D(QProgressBar);
589     if (d->format == format)
590         return;
591     d->format = format;
592     update();
593 }
594
595 QString QProgressBar::format() const
596 {
597     Q_D(const QProgressBar);
598     return d->format;
599 }
600
601 QT_END_NAMESPACE
602
603 #endif // QT_NO_PROGRESSBAR