Warning, file /office/calligra/libs/widgetutils/KoProgressUpdater.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

0001 /* This file is part of the KDE project
0002  * Copyright (C) 2006-2007 Thomas Zander <zander@kde.org>
0003  * Copyright (C) 2009 Boudewijn Rempt <boud@valdyas.org>
0004  *
0005  * This library is free software; you can redistribute it and/or
0006  * modify it under the terms of the GNU Library General Public
0007  * License as published by the Free Software Foundation; either
0008  * version 2 of the License, or (at your option) any later version.
0009  *
0010  * This library is distributed in the hope that it will be useful,
0011  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0012  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0013  * Library General Public License for more details.
0014  *
0015  * You should have received a copy of the GNU Library General Public License
0016  * along with this library; see the file COPYING.LIB.  If not, write to
0017  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0018  * Boston, MA 02110-1301, USA.
0019  */
0020 #include "KoProgressUpdater.h"
0021 
0022 #include <QApplication>
0023 #include <QString>
0024 #include <QTextStream>
0025 #include <QTimer>
0026 
0027 #include "KoUpdaterPrivate_p.h"
0028 #include "KoUpdater.h"
0029 #include "KoProgressProxy.h"
0030 
0031 
0032 // 4 updates per second should be enough
0033 #define PROGRESSUPDATER_GUITIMERINTERVAL 250
0034 
0035 class Q_DECL_HIDDEN KoProgressUpdater::Private
0036 {
0037 public:
0038 
0039     Private(KoProgressUpdater *_parent, KoProgressProxy *p, Mode _mode,
0040             QTextStream *output_ = 0)
0041         : parent(_parent)
0042         , progressBar(p)
0043         , mode(_mode)
0044         , totalWeight(0)
0045         , currentProgress(0)
0046         , updated(false)
0047         , output(output_)
0048         , updateGuiTimer(_parent)
0049         , canceled(false)
0050     {
0051     }
0052 
0053     KoProgressUpdater *parent;
0054     KoProgressProxy *progressBar;
0055     Mode mode;
0056     int totalWeight;
0057     int currentProgress;
0058     bool updated;          // is true whenever the progress needs to be recomputed
0059     QTextStream *output;
0060     QTimer updateGuiTimer; // fires regularly to update the progress bar widget
0061     QList<QPointer<KoUpdaterPrivate> > subtasks;
0062     QList<QPointer<KoUpdater> > subTaskWrappers; // We delete these
0063     QTime referenceTime;
0064 
0065     static void logEvents(QTextStream& out, KoProgressUpdater::Private *updater,
0066                           const QTime& startTime, const QString& prefix);
0067     bool canceled;
0068 };
0069 
0070 // NOTE: do not make the KoProgressUpdater object part of the QObject
0071 // hierarchy. Do not make KoProgressProxy its parent (note that KoProgressProxy
0072 // is not necessarily castable to QObject ). This prevents proper functioning
0073 // of progress reporting in multi-threaded environments.
0074 KoProgressUpdater::KoProgressUpdater(KoProgressProxy *progressBar,
0075                                      Mode mode, QTextStream *output)
0076     : d (new Private(this, progressBar, mode, output))
0077 {
0078     Q_ASSERT(d->progressBar);
0079     connect(&d->updateGuiTimer, SIGNAL(timeout()), SLOT(updateUi()));
0080 }
0081 
0082 KoProgressUpdater::~KoProgressUpdater()
0083 {
0084     if (d->output) {
0085         Private::logEvents(*d->output, d, referenceTime(), "");
0086     }
0087     d->progressBar->setValue(d->progressBar->maximum());
0088 
0089     // make sure to stop the timer to avoid accessing
0090     // the data we are going to delete right now
0091     d->updateGuiTimer.stop();
0092 
0093     qDeleteAll(d->subtasks);
0094     d->subtasks.clear();
0095 
0096     qDeleteAll(d->subTaskWrappers);
0097     d->subTaskWrappers.clear();
0098 
0099     delete d;
0100 }
0101 
0102 void KoProgressUpdater::setReferenceTime(const QTime &referenceTime)
0103 {
0104     d->referenceTime = referenceTime;
0105 }
0106 
0107 QTime KoProgressUpdater::referenceTime() const
0108 {
0109     return d->referenceTime;
0110 }
0111 
0112 void KoProgressUpdater::start(int range, const QString &text)
0113 {
0114     d->updateGuiTimer.start(PROGRESSUPDATER_GUITIMERINTERVAL);
0115 
0116     qDeleteAll(d->subtasks);
0117     d->subtasks.clear();
0118 
0119     qDeleteAll(d->subTaskWrappers);
0120     d->subTaskWrappers.clear();
0121 
0122     d->progressBar->setRange(0, range-1);
0123     d->progressBar->setValue(0);
0124 
0125     if(!text.isEmpty()) {
0126         d->progressBar->setFormat(text);
0127     }
0128     d->totalWeight = 0;
0129     d->canceled = false;
0130 }
0131 
0132 QPointer<KoUpdater> KoProgressUpdater::startSubtask(int weight,
0133                                                     const QString &name)
0134 {
0135     KoUpdaterPrivate *p = new KoUpdaterPrivate(this, weight, name);
0136     d->totalWeight += weight;
0137     d->subtasks.append(p);
0138     connect(p, SIGNAL(sigUpdated()), SLOT(update()));
0139 
0140     QPointer<KoUpdater> updater = new KoUpdater(p);
0141     d->subTaskWrappers.append(updater);
0142 
0143     if (!d->updateGuiTimer.isActive()) {
0144         // we maybe need to restart the timer if it was stopped in updateUi() cause
0145         // other sub-tasks created before this one finished already.
0146         d->updateGuiTimer.start(PROGRESSUPDATER_GUITIMERINTERVAL);
0147     }
0148 
0149     return updater;
0150 }
0151 
0152 void KoProgressUpdater::cancel()
0153 {
0154     foreach(KoUpdaterPrivate *updater, d->subtasks) {
0155         updater->setProgress(100);
0156         updater->interrupt();
0157     }
0158     d->canceled = true;
0159     updateUi();
0160 }
0161 
0162 void KoProgressUpdater::update()
0163 {
0164     d->updated = true;
0165     if (d->mode == Unthreaded) {
0166         qApp->processEvents();
0167     }
0168 }
0169 
0170 void KoProgressUpdater::updateUi()
0171 {
0172     // This function runs in the app main thread. All the progress
0173     // updates arrive at the KoUpdaterPrivate instances through
0174     // queued connections, so until we relinquish control to the
0175     // event loop, the progress values cannot change, and that
0176     // won't happen until we return from this function (which is
0177     // triggered by a timer)
0178 
0179     if (d->updated) {
0180         int totalProgress = 0;
0181         foreach(QPointer<KoUpdaterPrivate> updater, d->subtasks) {
0182             if (updater->interrupted()) {
0183                 d->currentProgress = -1;
0184                 return;
0185             }
0186 
0187             int progress = updater->progress();
0188             if (progress > 100 || progress < 0) {
0189                 progress = updater->progress();
0190             }
0191 
0192             totalProgress += progress *updater->weight();
0193         }
0194 
0195         d->currentProgress = totalProgress / d->totalWeight;
0196         d->updated = false;
0197     }
0198 
0199     if (d->currentProgress == -1) {
0200         d->progressBar->setValue( d->progressBar->maximum() );
0201         // should we hide the progressbar after a little while?
0202         return;
0203     }
0204 
0205     if (d->currentProgress >= d->progressBar->maximum()) {
0206         // we're done
0207         d->updateGuiTimer.stop(); // 10 updates/second should be enough?
0208     }
0209     d->progressBar->setValue(d->currentProgress);
0210 }
0211 
0212 bool KoProgressUpdater::interrupted() const
0213 {
0214     return d->canceled;
0215 }
0216 
0217 bool KoProgressUpdater::hasOutput() const
0218 {
0219     return d->output != 0;
0220 }
0221 
0222 void KoProgressUpdater::Private::logEvents(QTextStream& out,
0223                                            KoProgressUpdater::Private *updater,
0224                                            const QTime& startTime,
0225                                            const QString& prefix) {
0226     // initial implementation: write out the names of all events
0227     foreach (QPointer<KoUpdaterPrivate> p, updater->subtasks) {
0228         if (!p) continue;
0229         foreach (const KoUpdaterPrivate::TimePoint &tp, p->getPoints()) {
0230             out << prefix+p->objectName() << '\t'
0231                     << startTime.msecsTo(tp.time) << '\t' << tp.value << endl;
0232         }
0233     }
0234 }