src/gui/widgets/qprogressbar.cpp
changeset 0 1918ee327afb
child 4 3b1da2848fc7
equal deleted inserted replaced
-1:000000000000 0:1918ee327afb
       
     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