1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the QtWidgets 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 The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/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 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or (at your option) the GNU General
28 ** Public license version 3 or any later version approved by the KDE Free
29 ** Qt Foundation. The licenses are as published by the Free Software
30 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31 ** included in the packaging of this file. Please review the following
32 ** information to ensure the GNU General Public License requirements will
33 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34 ** https://www.gnu.org/licenses/gpl-3.0.html.
35 **
36 ** $QT_END_LICENSE$
37 **
38 ****************************************************************************/
39 
40 #include "qprogressdialog.h"
41 
42 #include "qshortcut.h"
43 #include "qpainter.h"
44 #include "qdrawutil.h"
45 #include "qlabel.h"
46 #include "qprogressbar.h"
47 #include "qapplication.h"
48 #include "qstyle.h"
49 #include "qpushbutton.h"
50 #include "qtimer.h"
51 #include "qelapsedtimer.h"
52 #include "qscopedvaluerollback.h"
53 #include <private/qdialog_p.h>
54 #include <limits.h>
55 
56 QT_BEGIN_NAMESPACE
57 
58 // If the operation is expected to take this long (as predicted by
59 // progress time), show the progress dialog.
60 static const int defaultShowTime = 4000;
61 // Wait at least this long before attempting to make a prediction.
62 static const int minWaitTime = 50;
63 
64 class QProgressDialogPrivate : public QDialogPrivate
65 {
66     Q_DECLARE_PUBLIC(QProgressDialog)
67 
68 public:
QProgressDialogPrivate()69     QProgressDialogPrivate() : label(nullptr), cancel(nullptr), bar(nullptr),
70         shown_once(false),
71         cancellation_flag(false),
72         setValue_called(false),
73         processingEvents(false),
74         showTime(defaultShowTime),
75 #ifndef QT_NO_SHORTCUT
76         escapeShortcut(nullptr),
77 #endif
78         useDefaultCancelText(false)
79     {
80     }
81 
82     void init(const QString &labelText, const QString &cancelText, int min, int max);
83     void layout();
84     void retranslateStrings();
85     void setCancelButtonText(const QString &cancelButtonText);
86     void adoptChildWidget(QWidget *c);
87     void ensureSizeIsAtLeastSizeHint();
88     void _q_disconnectOnClose();
89 
90     QLabel *label;
91     QPushButton *cancel;
92     QProgressBar *bar;
93     QTimer *forceTimer;
94     bool shown_once;
95     bool cancellation_flag;
96     bool setValue_called;
97     bool processingEvents;
98     QElapsedTimer starttime;
99     int showTime;
100     bool autoClose;
101     bool autoReset;
102     bool forceHide;
103 #ifndef QT_NO_SHORTCUT
104     QShortcut *escapeShortcut;
105 #endif
106     bool useDefaultCancelText;
107     QPointer<QObject> receiverToDisconnectOnClose;
108     QByteArray memberToDisconnectOnClose;
109 };
110 
init(const QString & labelText,const QString & cancelText,int min,int max)111 void QProgressDialogPrivate::init(const QString &labelText, const QString &cancelText,
112                                   int min, int max)
113 {
114     Q_Q(QProgressDialog);
115     label = new QLabel(labelText, q);
116     bar = new QProgressBar(q);
117     bar->setRange(min, max);
118     int align = q->style()->styleHint(QStyle::SH_ProgressDialog_TextLabelAlignment, nullptr, q);
119     label->setAlignment(Qt::Alignment(align));
120     autoClose = true;
121     autoReset = true;
122     forceHide = false;
123     QObject::connect(q, SIGNAL(canceled()), q, SLOT(cancel()));
124     forceTimer = new QTimer(q);
125     QObject::connect(forceTimer, SIGNAL(timeout()), q, SLOT(forceShow()));
126     if (useDefaultCancelText) {
127         retranslateStrings();
128     } else {
129         q->setCancelButtonText(cancelText);
130     }
131     starttime.start();
132     forceTimer->start(showTime);
133 }
134 
layout()135 void QProgressDialogPrivate::layout()
136 {
137     Q_Q(QProgressDialog);
138     int sp = q->style()->pixelMetric(QStyle::PM_LayoutVerticalSpacing, nullptr, q);
139     int mb = q->style()->pixelMetric(QStyle::PM_LayoutBottomMargin, nullptr, q);
140     int ml = qMin(q->width() / 10, q->style()->pixelMetric(QStyle::PM_LayoutLeftMargin, nullptr, q));
141     int mr = qMin(q->width() / 10, q->style()->pixelMetric(QStyle::PM_LayoutRightMargin, nullptr, q));
142     const bool centered =
143         bool(q->style()->styleHint(QStyle::SH_ProgressDialog_CenterCancelButton, nullptr, q));
144 
145     int additionalSpacing = 0;
146     QSize cs = cancel ? cancel->sizeHint() : QSize(0,0);
147     QSize bh = bar->sizeHint();
148     int cspc;
149     int lh = 0;
150 
151     // Find spacing and sizes that fit.  It is important that a progress
152     // dialog can be made very small if the user demands it so.
153     for (int attempt=5; attempt--;) {
154         cspc = cancel ? cs.height() + sp : 0;
155         lh = qMax(0, q->height() - mb - bh.height() - sp - cspc);
156 
157         if (lh < q->height()/4) {
158             // Getting cramped
159             sp /= 2;
160             mb /= 2;
161             if (cancel) {
162                 cs.setHeight(qMax(4,cs.height()-sp-2));
163             }
164             bh.setHeight(qMax(4,bh.height()-sp-1));
165         } else {
166             break;
167         }
168     }
169 
170     if (cancel) {
171         cancel->setGeometry(
172             centered ? q->width()/2 - cs.width()/2 : q->width() - mr - cs.width(),
173             q->height() - mb - cs.height(),
174             cs.width(), cs.height());
175     }
176 
177     if (label)
178         label->setGeometry(ml, additionalSpacing, q->width() - ml - mr, lh);
179     bar->setGeometry(ml, lh + sp + additionalSpacing, q->width() - ml - mr, bh.height());
180 }
181 
retranslateStrings()182 void QProgressDialogPrivate::retranslateStrings()
183 {
184     if (useDefaultCancelText)
185         setCancelButtonText(QProgressDialog::tr("Cancel"));
186 }
187 
_q_disconnectOnClose()188 void QProgressDialogPrivate::_q_disconnectOnClose()
189 {
190     Q_Q(QProgressDialog);
191     if (receiverToDisconnectOnClose) {
192         QObject::disconnect(q, SIGNAL(canceled()), receiverToDisconnectOnClose,
193                             memberToDisconnectOnClose);
194         receiverToDisconnectOnClose = nullptr;
195     }
196     memberToDisconnectOnClose.clear();
197 }
198 
199 /*!
200   \class QProgressDialog
201   \brief The QProgressDialog class provides feedback on the progress of a slow operation.
202   \ingroup standard-dialogs
203   \inmodule QtWidgets
204 
205 
206   A progress dialog is used to give the user an indication of how long
207   an operation is going to take, and to demonstrate that the
208   application has not frozen. It can also give the user an opportunity
209   to abort the operation.
210 
211   A common problem with progress dialogs is that it is difficult to know
212   when to use them; operations take different amounts of time on different
213   hardware.  QProgressDialog offers a solution to this problem:
214   it estimates the time the operation will take (based on time for
215   steps), and only shows itself if that estimate is beyond minimumDuration()
216   (4 seconds by default).
217 
218   Use setMinimum() and setMaximum() or the constructor to set the number of
219   "steps" in the operation and call setValue() as the operation
220   progresses. The number of steps can be chosen arbitrarily. It can be the
221   number of files copied, the number of bytes received, the number of
222   iterations through the main loop of your algorithm, or some other
223   suitable unit. Progress starts at the value set by setMinimum(),
224   and the progress dialog shows that the operation has finished when
225   you call setValue() with the value set by setMaximum() as its argument.
226 
227   The dialog automatically resets and hides itself at the end of the
228   operation.  Use setAutoReset() and setAutoClose() to change this
229   behavior. Note that if you set a new maximum (using setMaximum() or
230   setRange()) that equals your current value(), the dialog will not
231   close regardless.
232 
233   There are two ways of using QProgressDialog: modal and modeless.
234 
235   Compared to a modeless QProgressDialog, a modal QProgressDialog is simpler
236   to use for the programmer. Do the operation in a loop, call \l setValue() at
237   intervals, and check for cancellation with wasCanceled(). For example:
238 
239   \snippet dialogs/dialogs.cpp 3
240 
241   A modeless progress dialog is suitable for operations that take
242   place in the background, where the user is able to interact with the
243   application. Such operations are typically based on QTimer (or
244   QObject::timerEvent()) or QSocketNotifier; or performed
245   in a separate thread. A QProgressBar in the status bar of your main window
246   is often an alternative to a modeless progress dialog.
247 
248   You need to have an event loop to be running, connect the
249   canceled() signal to a slot that stops the operation, and call \l
250   setValue() at intervals. For example:
251 
252   \snippet dialogs/dialogs.cpp 4
253   \codeline
254   \snippet dialogs/dialogs.cpp 5
255   \codeline
256   \snippet dialogs/dialogs.cpp 6
257 
258   In both modes the progress dialog may be customized by
259   replacing the child widgets with custom widgets by using setLabel(),
260   setBar(), and setCancelButton().
261   The functions setLabelText() and setCancelButtonText()
262   set the texts shown.
263 
264   \image fusion-progressdialog.png A progress dialog shown in the Fusion widget style.
265 
266   \sa QDialog, QProgressBar, {fowler}{GUI Design Handbook: Progress Indicator},
267       {Find Files Example}, {Pixelator Example}
268 */
269 
270 
271 /*!
272   Constructs a progress dialog.
273 
274   Default settings:
275   \list
276   \li The label text is empty.
277   \li The cancel button text is (translated) "Cancel".
278   \li minimum is 0;
279   \li maximum is 100
280   \endlist
281 
282   The \a parent argument is dialog's parent widget. The widget flags, \a f, are
283   passed to the QDialog::QDialog() constructor.
284 
285   \sa setLabelText(), setCancelButtonText(), setCancelButton(),
286   setMinimum(), setMaximum()
287 */
288 
QProgressDialog(QWidget * parent,Qt::WindowFlags f)289 QProgressDialog::QProgressDialog(QWidget *parent, Qt::WindowFlags f)
290     : QDialog(*(new QProgressDialogPrivate), parent, f)
291 {
292     Q_D(QProgressDialog);
293     d->useDefaultCancelText = true;
294     d->init(QString::fromLatin1(""), QString(), 0, 100);
295 }
296 
297 /*!
298   Constructs a progress dialog.
299 
300    The \a labelText is the text used to remind the user what is progressing.
301 
302    The \a cancelButtonText is the text to display on the cancel button.  If
303    QString() is passed then no cancel button is shown.
304 
305    The \a minimum and \a maximum is the number of steps in the operation for
306    which this progress dialog shows progress.  For example, if the
307    operation is to examine 50 files, this value minimum value would be 0,
308    and the maximum would be 50. Before examining the first file, call
309    setValue(0). As each file is processed call setValue(1), setValue(2),
310    etc., finally calling setValue(50) after examining the last file.
311 
312    The \a parent argument is the dialog's parent widget. The parent, \a parent, and
313    widget flags, \a f, are passed to the QDialog::QDialog() constructor.
314 
315   \sa setLabelText(), setLabel(), setCancelButtonText(), setCancelButton(),
316   setMinimum(), setMaximum()
317 */
318 
QProgressDialog(const QString & labelText,const QString & cancelButtonText,int minimum,int maximum,QWidget * parent,Qt::WindowFlags f)319 QProgressDialog::QProgressDialog(const QString &labelText,
320                                  const QString &cancelButtonText,
321                                  int minimum, int maximum,
322                                  QWidget *parent, Qt::WindowFlags f)
323     : QDialog(*(new QProgressDialogPrivate), parent, f)
324 {
325     Q_D(QProgressDialog);
326     d->init(labelText, cancelButtonText, minimum, maximum);
327 }
328 
329 
330 /*!
331   Destroys the progress dialog.
332 */
333 
~QProgressDialog()334 QProgressDialog::~QProgressDialog()
335 {
336 }
337 
338 /*!
339   \fn void QProgressDialog::canceled()
340 
341   This signal is emitted when the cancel button is clicked.
342   It is connected to the cancel() slot by default.
343 
344   \sa wasCanceled()
345 */
346 
347 
348 /*!
349   Sets the label to \a label. The progress dialog resizes to fit. The
350   label becomes owned by the progress dialog and will be deleted when
351   necessary, so do not pass the address of an object on the stack.
352 
353   \sa setLabelText()
354 */
355 
setLabel(QLabel * label)356 void QProgressDialog::setLabel(QLabel *label)
357 {
358     Q_D(QProgressDialog);
359     if (label == d->label) {
360         if (Q_UNLIKELY(label))
361             qWarning("QProgressDialog::setLabel: Attempt to set the same label again");
362         return;
363     }
364     delete d->label;
365     d->label = label;
366     d->adoptChildWidget(label);
367 }
368 
369 
370 /*!
371   \property QProgressDialog::labelText
372   \brief the label's text
373 
374   The default text is an empty string.
375 */
376 
labelText() const377 QString QProgressDialog::labelText() const
378 {
379     Q_D(const QProgressDialog);
380     if (d->label)
381         return d->label->text();
382     return QString();
383 }
384 
setLabelText(const QString & text)385 void QProgressDialog::setLabelText(const QString &text)
386 {
387     Q_D(QProgressDialog);
388     if (d->label) {
389         d->label->setText(text);
390         d->ensureSizeIsAtLeastSizeHint();
391     }
392 }
393 
394 
395 /*!
396   Sets the cancel button to the push button, \a cancelButton. The
397   progress dialog takes ownership of this button which will be deleted
398   when necessary, so do not pass the address of an object that is on
399   the stack, i.e. use new() to create the button.  If \nullptr is passed,
400   no cancel button will be shown.
401 
402   \sa setCancelButtonText()
403 */
404 
setCancelButton(QPushButton * cancelButton)405 void QProgressDialog::setCancelButton(QPushButton *cancelButton)
406 {
407     Q_D(QProgressDialog);
408     if (d->cancel == cancelButton) {
409         if (Q_UNLIKELY(cancelButton))
410             qWarning("QProgressDialog::setCancelButton: Attempt to set the same button again");
411         return;
412     }
413     delete d->cancel;
414     d->cancel = cancelButton;
415     if (cancelButton) {
416         connect(d->cancel, SIGNAL(clicked()), this, SIGNAL(canceled()));
417 #ifndef QT_NO_SHORTCUT
418         // FIXME: This only registers the primary key sequence of the cancel action
419         d->escapeShortcut = new QShortcut(QKeySequence::Cancel, this, SIGNAL(canceled()));
420 #endif
421     } else {
422 #ifndef QT_NO_SHORTCUT
423         delete d->escapeShortcut;
424         d->escapeShortcut = nullptr;
425 #endif
426     }
427     d->adoptChildWidget(cancelButton);
428 }
429 
430 /*!
431   Sets the cancel button's text to \a cancelButtonText.  If the text
432   is set to QString() then it will cause the cancel button to be
433   hidden and deleted.
434 
435   \sa setCancelButton()
436 */
437 
setCancelButtonText(const QString & cancelButtonText)438 void QProgressDialog::setCancelButtonText(const QString &cancelButtonText)
439 {
440     Q_D(QProgressDialog);
441     d->useDefaultCancelText = false;
442     d->setCancelButtonText(cancelButtonText);
443 }
444 
setCancelButtonText(const QString & cancelButtonText)445 void QProgressDialogPrivate::setCancelButtonText(const QString &cancelButtonText)
446 {
447     Q_Q(QProgressDialog);
448 
449     if (!cancelButtonText.isNull()) {
450         if (cancel) {
451             cancel->setText(cancelButtonText);
452         } else {
453             q->setCancelButton(new QPushButton(cancelButtonText, q));
454         }
455     } else {
456         q->setCancelButton(nullptr);
457     }
458     ensureSizeIsAtLeastSizeHint();
459 }
460 
461 
462 /*!
463   Sets the progress bar widget to \a bar. The progress dialog resizes to
464   fit. The progress dialog takes ownership of the progress \a bar which
465   will be deleted when necessary, so do not use a progress bar
466   allocated on the stack.
467 */
468 
setBar(QProgressBar * bar)469 void QProgressDialog::setBar(QProgressBar *bar)
470 {
471     Q_D(QProgressDialog);
472     if (Q_UNLIKELY(!bar)) {
473         qWarning("QProgressDialog::setBar: Cannot set a null progress bar");
474         return;
475     }
476 #ifndef QT_NO_DEBUG
477     if (Q_UNLIKELY(value() > 0))
478         qWarning("QProgressDialog::setBar: Cannot set a new progress bar "
479                   "while the old one is active");
480 #endif
481     if (Q_UNLIKELY(bar == d->bar)) {
482         qWarning("QProgressDialog::setBar: Attempt to set the same progress bar again");
483         return;
484     }
485     delete d->bar;
486     d->bar = bar;
487     d->adoptChildWidget(bar);
488 }
489 
adoptChildWidget(QWidget * c)490 void QProgressDialogPrivate::adoptChildWidget(QWidget *c)
491 {
492     Q_Q(QProgressDialog);
493 
494     if (c) {
495         if (c->parentWidget() == q)
496             c->hide(); // until after ensureSizeIsAtLeastSizeHint()
497         else
498             c->setParent(q, { });
499     }
500     ensureSizeIsAtLeastSizeHint();
501     if (c)
502         c->show();
503 }
504 
ensureSizeIsAtLeastSizeHint()505 void QProgressDialogPrivate::ensureSizeIsAtLeastSizeHint()
506 {
507     Q_Q(QProgressDialog);
508 
509     QSize size = q->sizeHint();
510     if (q->isVisible())
511         size = size.expandedTo(q->size());
512     q->resize(size);
513 }
514 
515 
516 /*!
517   \property QProgressDialog::wasCanceled
518   \brief whether the dialog was canceled
519 */
520 
wasCanceled() const521 bool QProgressDialog::wasCanceled() const
522 {
523     Q_D(const QProgressDialog);
524     return d->cancellation_flag;
525 }
526 
527 
528 /*!
529     \property QProgressDialog::maximum
530     \brief the highest value represented by the progress bar
531 
532     The default is 100.
533 
534     \sa minimum, setRange()
535 */
536 
maximum() const537 int QProgressDialog::maximum() const
538 {
539     Q_D(const QProgressDialog);
540     return d->bar->maximum();
541 }
542 
setMaximum(int maximum)543 void QProgressDialog::setMaximum(int maximum)
544 {
545     Q_D(QProgressDialog);
546     d->bar->setMaximum(maximum);
547 }
548 
549 /*!
550     \property QProgressDialog::minimum
551     \brief the lowest value represented by the progress bar
552 
553     The default is 0.
554 
555     \sa maximum, setRange()
556 */
557 
minimum() const558 int QProgressDialog::minimum() const
559 {
560     Q_D(const QProgressDialog);
561     return d->bar->minimum();
562 }
563 
setMinimum(int minimum)564 void QProgressDialog::setMinimum(int minimum)
565 {
566     Q_D(QProgressDialog);
567     d->bar->setMinimum(minimum);
568 }
569 
570 /*!
571     Sets the progress dialog's minimum and maximum values
572     to \a minimum and \a maximum, respectively.
573 
574     If \a maximum is smaller than \a minimum, \a minimum becomes the only
575     legal value.
576 
577     If the current value falls outside the new range, the progress
578     dialog is reset with reset().
579 
580     \sa minimum, maximum
581 */
setRange(int minimum,int maximum)582 void QProgressDialog::setRange(int minimum, int maximum)
583 {
584     Q_D(QProgressDialog);
585     d->bar->setRange(minimum, maximum);
586 }
587 
588 
589 /*!
590   Resets the progress dialog.
591   The progress dialog becomes hidden if autoClose() is true.
592 
593   \sa setAutoClose(), setAutoReset()
594 */
595 
reset()596 void QProgressDialog::reset()
597 {
598     Q_D(QProgressDialog);
599     if (d->autoClose || d->forceHide)
600         hide();
601     d->bar->reset();
602     d->cancellation_flag = false;
603     d->shown_once = false;
604     d->setValue_called = false;
605     d->forceTimer->stop();
606 
607     /*
608         I wish we could disconnect the user slot provided to open() here but
609         unfortunately reset() is usually called before the slot has been invoked.
610         (reset() is itself invoked when canceled() is emitted.)
611     */
612     if (d->receiverToDisconnectOnClose)
613         QMetaObject::invokeMethod(this, "_q_disconnectOnClose", Qt::QueuedConnection);
614 }
615 
616 /*!
617   Resets the progress dialog. wasCanceled() becomes true until
618   the progress dialog is reset.
619   The progress dialog becomes hidden.
620 */
621 
cancel()622 void QProgressDialog::cancel()
623 {
624     Q_D(QProgressDialog);
625     d->forceHide = true;
626     reset();
627     d->forceHide = false;
628     d->cancellation_flag = true;
629 }
630 
631 
value() const632 int QProgressDialog::value() const
633 {
634     Q_D(const QProgressDialog);
635     return d->bar->value();
636 }
637 
638 /*!
639   \property QProgressDialog::value
640   \brief the current amount of progress made.
641 
642   For the progress dialog to work as expected, you should initially set
643   this property to QProgressDialog::minimum() and finally set it to
644   QProgressDialog::maximum(); you can call setValue() any number of times
645   in-between.
646 
647   \warning If the progress dialog is modal
648     (see QProgressDialog::QProgressDialog()),
649     setValue() calls QCoreApplication::processEvents(), so take care that
650     this does not cause undesirable re-entrancy in your code. For example,
651     don't use a QProgressDialog inside a paintEvent()!
652 
653   \sa minimum, maximum
654 */
setValue(int progress)655 void QProgressDialog::setValue(int progress)
656 {
657     Q_D(QProgressDialog);
658     if (d->setValue_called && progress == d->bar->value())
659         return;
660 
661     d->bar->setValue(progress);
662 
663     if (d->shown_once) {
664         if (isModal() && !d->processingEvents) {
665             const QScopedValueRollback<bool> guard(d->processingEvents, true);
666             QCoreApplication::processEvents();
667         }
668     } else {
669         if ((!d->setValue_called && progress == 0 /* for compat with Qt < 5.4 */) || progress == minimum()) {
670             d->starttime.start();
671             d->forceTimer->start(d->showTime);
672             d->setValue_called = true;
673             return;
674         } else {
675             d->setValue_called = true;
676             bool need_show;
677             int elapsed = d->starttime.elapsed();
678             if (elapsed >= d->showTime) {
679                 need_show = true;
680             } else {
681                 if (elapsed > minWaitTime) {
682                     int estimate;
683                     int totalSteps = maximum() - minimum();
684                     int myprogress = progress - minimum();
685                     if (myprogress == 0) myprogress = 1;
686                     if ((totalSteps - myprogress) >= INT_MAX / elapsed)
687                         estimate = (totalSteps - myprogress) / myprogress * elapsed;
688                     else
689                         estimate = elapsed * (totalSteps - myprogress) / myprogress;
690                     need_show = estimate >= d->showTime;
691                 } else {
692                     need_show = false;
693                 }
694             }
695             if (need_show) {
696                 d->ensureSizeIsAtLeastSizeHint();
697                 show();
698                 d->shown_once = true;
699             }
700         }
701     }
702 
703     if (progress == d->bar->maximum() && d->autoReset)
704         reset();
705 }
706 
707 /*!
708   Returns a size that fits the contents of the progress dialog.
709   The progress dialog resizes itself as required, so you should not
710   need to call this yourself.
711 */
712 
sizeHint() const713 QSize QProgressDialog::sizeHint() const
714 {
715     Q_D(const QProgressDialog);
716     QSize labelSize = d->label ? d->label->sizeHint() : QSize(0, 0);
717     QSize barSize = d->bar->sizeHint();
718     int marginBottom = style()->pixelMetric(QStyle::PM_LayoutBottomMargin, 0, this);
719     int spacing = style()->pixelMetric(QStyle::PM_LayoutVerticalSpacing, 0, this);
720     int marginLeft = style()->pixelMetric(QStyle::PM_LayoutLeftMargin, 0, this);
721     int marginRight = style()->pixelMetric(QStyle::PM_LayoutRightMargin, 0, this);
722 
723     int height = marginBottom * 2 + barSize.height() + labelSize.height() + spacing;
724     if (d->cancel)
725         height += d->cancel->sizeHint().height() + spacing;
726     return QSize(qMax(200, labelSize.width() + marginLeft + marginRight), height);
727 }
728 
729 /*!\reimp
730 */
resizeEvent(QResizeEvent *)731 void QProgressDialog::resizeEvent(QResizeEvent *)
732 {
733     Q_D(QProgressDialog);
734     d->layout();
735 }
736 
737 /*!
738   \reimp
739 */
changeEvent(QEvent * ev)740 void QProgressDialog::changeEvent(QEvent *ev)
741 {
742     Q_D(QProgressDialog);
743     if (ev->type() == QEvent::StyleChange) {
744         d->layout();
745     } else if (ev->type() == QEvent::LanguageChange) {
746         d->retranslateStrings();
747     }
748     QDialog::changeEvent(ev);
749 }
750 
751 /*!
752     \property QProgressDialog::minimumDuration
753     \brief the time that must pass before the dialog appears
754 
755     If the expected duration of the task is less than the
756     minimumDuration, the dialog will not appear at all. This prevents
757     the dialog popping up for tasks that are quickly over. For tasks
758     that are expected to exceed the minimumDuration, the dialog will
759     pop up after the minimumDuration time or as soon as any progress
760     is set.
761 
762     If set to 0, the dialog is always shown as soon as any progress is
763     set. The default is 4000 milliseconds.
764 */
setMinimumDuration(int ms)765 void QProgressDialog::setMinimumDuration(int ms)
766 {
767     Q_D(QProgressDialog);
768     d->showTime = ms;
769     if (d->bar->value() == d->bar->minimum()) {
770         d->forceTimer->stop();
771         d->forceTimer->start(ms);
772     }
773 }
774 
minimumDuration() const775 int QProgressDialog::minimumDuration() const
776 {
777     Q_D(const QProgressDialog);
778     return d->showTime;
779 }
780 
781 
782 /*!
783   \reimp
784 */
785 
closeEvent(QCloseEvent * e)786 void QProgressDialog::closeEvent(QCloseEvent *e)
787 {
788     emit canceled();
789     QDialog::closeEvent(e);
790 }
791 
792 /*!
793   \property QProgressDialog::autoReset
794   \brief whether the progress dialog calls reset() as soon as value() equals maximum()
795 
796   The default is true.
797 
798   \sa setAutoClose()
799 */
800 
setAutoReset(bool b)801 void QProgressDialog::setAutoReset(bool b)
802 {
803     Q_D(QProgressDialog);
804     d->autoReset = b;
805 }
806 
autoReset() const807 bool QProgressDialog::autoReset() const
808 {
809     Q_D(const QProgressDialog);
810     return d->autoReset;
811 }
812 
813 /*!
814   \property QProgressDialog::autoClose
815   \brief whether the dialog gets hidden by reset()
816 
817   The default is true.
818 
819   \sa setAutoReset()
820 */
821 
setAutoClose(bool close)822 void QProgressDialog::setAutoClose(bool close)
823 {
824     Q_D(QProgressDialog);
825     d->autoClose = close;
826 }
827 
autoClose() const828 bool QProgressDialog::autoClose() const
829 {
830     Q_D(const QProgressDialog);
831     return d->autoClose;
832 }
833 
834 /*!
835   \reimp
836 */
837 
showEvent(QShowEvent * e)838 void QProgressDialog::showEvent(QShowEvent *e)
839 {
840     Q_D(QProgressDialog);
841     QDialog::showEvent(e);
842     d->ensureSizeIsAtLeastSizeHint();
843     d->forceTimer->stop();
844 }
845 
846 /*!
847   Shows the dialog if it is still hidden after the algorithm has been started
848   and minimumDuration milliseconds have passed.
849 
850   \sa setMinimumDuration()
851 */
852 
forceShow()853 void QProgressDialog::forceShow()
854 {
855     Q_D(QProgressDialog);
856     d->forceTimer->stop();
857     if (d->shown_once || d->cancellation_flag)
858         return;
859 
860     show();
861     d->shown_once = true;
862 }
863 
864 /*!
865     \since 4.5
866 
867     Opens the dialog and connects its canceled() signal to the slot specified
868     by \a receiver and \a member.
869 
870     The signal will be disconnected from the slot when the dialog is closed.
871 */
open(QObject * receiver,const char * member)872 void QProgressDialog::open(QObject *receiver, const char *member)
873 {
874     Q_D(QProgressDialog);
875     connect(this, SIGNAL(canceled()), receiver, member);
876     d->receiverToDisconnectOnClose = receiver;
877     d->memberToDisconnectOnClose = member;
878     QDialog::open();
879 }
880 
881 QT_END_NAMESPACE
882 
883 #include "moc_qprogressdialog.cpp"
884