/************************************************************************** ** ** Copyright (C) 2012-2013 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** **************************************************************************/ #include "progresscoordinator.h" #include #include using namespace QInstaller; QT_BEGIN_NAMESPACE uint qHash(QPointer key) { return qHash(key.data()); } QT_END_NAMESPACE ProgressCoordinator::ProgressCoordinator(QObject *parent) : QObject(parent) , m_currentCompletePercentage(0) , m_currentBasePercentage(0) , m_manualAddedPercentage(0) , m_reservedPercentage(0) , m_undoMode(false) , m_reachedPercentageBeforeUndo(0) { // it has to be in the main thread to be able refresh the ui with processEvents Q_ASSERT(thread() == qApp->thread()); } ProgressCoordinator::~ProgressCoordinator() { } ProgressCoordinator *ProgressCoordinator::instance() { static ProgressCoordinator *instance = 0; if (instance == 0) instance = new ProgressCoordinator(qApp); return instance; } void ProgressCoordinator::reset() { disconnectAllSenders(); m_installationLabelText.clear(); m_currentCompletePercentage = 0; m_currentBasePercentage = 0; m_manualAddedPercentage = 0; m_reservedPercentage = 0; m_undoMode = false; m_reachedPercentageBeforeUndo = 0; emit detailTextResetNeeded(); } void ProgressCoordinator::registerPartProgress(QObject *sender, const char *signal, double partProgressSize) { Q_ASSERT(sender); Q_ASSERT(QString::fromLatin1(signal).contains(QLatin1String("(double)"))); Q_ASSERT(partProgressSize <= 1); m_senderPartProgressSizeHash.insert(sender, partProgressSize); bool isConnected = connect(sender, signal, this, SLOT(partProgressChanged(double))); Q_UNUSED(isConnected); Q_ASSERT(isConnected); } /*! This slot gets the progress changed signals from different tasks. The values 0 and 1 are handled as special values. 0 - is just ignored, so you can use a timer which gives the progress, e.g. like a downloader does. 1 - means the task is finished, even if there comes another 1 from that task, so it will be ignored. */ void ProgressCoordinator::partProgressChanged(double fraction) { if (fraction < 0 || fraction > 1) { qWarning() << "The fraction is outside from possible value:" << QString::number(fraction); return; } // no fraction no change if (fraction == 0) return; // ignore senders sending 100% multiple times if (fraction == 1 && m_senderPendingCalculatedPercentageHash.contains(sender()) && m_senderPendingCalculatedPercentageHash.value(sender()) == 0) { return; } double partProgressSize = m_senderPartProgressSizeHash.value(sender(), 0); if (partProgressSize == 0) { qWarning() << "It seems that this sender was not registered in the right way:" << sender(); return; } if (m_undoMode) { //qDebug() << "fraction:" << fraction; double maxSize = m_reachedPercentageBeforeUndo * partProgressSize; double pendingCalculatedPartPercentage = maxSize * fraction; // allPendingCalculatedPartPercentages has negative values double newCurrentCompletePercentage = m_currentBasePercentage - pendingCalculatedPartPercentage + allPendingCalculatedPartPercentages(sender()); //we can't check this here, because some round issues can make it little bit under 0 or over 100 //Q_ASSERT(newCurrentCompletePercentage >= 0); //Q_ASSERT(newCurrentCompletePercentage <= 100); if (newCurrentCompletePercentage < 0) { qDebug() << newCurrentCompletePercentage << "is smaller then 0 - this should happen max once"; newCurrentCompletePercentage = 0; } if (newCurrentCompletePercentage > 100) { qDebug() << newCurrentCompletePercentage << "is bigger then 100 - this should happen max once"; newCurrentCompletePercentage = 100; } // In undo mode, the progress has to go backward, new has to be smaller than current if (qRound(m_currentCompletePercentage) < qRound(newCurrentCompletePercentage)) qDebug("Something is wrong with the calculation of the progress."); m_currentCompletePercentage = newCurrentCompletePercentage; if (fraction == 1) { m_currentBasePercentage = m_currentBasePercentage - pendingCalculatedPartPercentage; m_senderPendingCalculatedPercentageHash.insert(sender(), 0); } else { m_senderPendingCalculatedPercentageHash.insert(sender(), pendingCalculatedPartPercentage); } } else { //if (m_undoMode) int availablePercentagePoints = 100 - m_manualAddedPercentage - m_reservedPercentage; double pendingCalculatedPartPercentage = availablePercentagePoints * partProgressSize * fraction; //double checkValue = allPendingCalculatedPartPercentages(sender()); double newCurrentCompletePercentage = m_manualAddedPercentage + m_currentBasePercentage + pendingCalculatedPartPercentage + allPendingCalculatedPartPercentages(sender()); //we can't check this here, because some round issues can make it little bit under 0 or over 100 //Q_ASSERT(newCurrentCompletePercentage >= 0); //Q_ASSERT(newCurrentCompletePercentage <= 100); if (newCurrentCompletePercentage < 0) { qDebug() << newCurrentCompletePercentage << "is smaller then 0 - this should happen max once"; newCurrentCompletePercentage = 0; } if (newCurrentCompletePercentage > 100) { qDebug() << newCurrentCompletePercentage << "is bigger then 100 - this should happen max once"; newCurrentCompletePercentage = 100; } // In normal mode, the progress has to go forward, new has to be larger than current if (qRound(m_currentCompletePercentage) > qRound(newCurrentCompletePercentage)) qDebug("Something is wrong with the calculation of the progress."); m_currentCompletePercentage = newCurrentCompletePercentage; if (fraction == 1) { m_currentBasePercentage = m_currentBasePercentage + pendingCalculatedPartPercentage; m_senderPendingCalculatedPercentageHash.insert(sender(), 0); } else { m_senderPendingCalculatedPercentageHash.insert(sender(), pendingCalculatedPartPercentage); } } //if (m_undoMode) } /*! Contains the installation progress percentage. */ int ProgressCoordinator::progressInPercentage() const { int currentValue = qRound(m_currentCompletePercentage); Q_ASSERT( currentValue <= 100); Q_ASSERT( currentValue >= 0); return currentValue; } void ProgressCoordinator::disconnectAllSenders() { foreach (QPointer sender, m_senderPartProgressSizeHash.keys()) { if (!sender.isNull()) { bool isDisconnected = sender->disconnect(this); Q_UNUSED(isDisconnected); Q_ASSERT(isDisconnected); } } m_senderPartProgressSizeHash.clear(); m_senderPendingCalculatedPercentageHash.clear(); } void ProgressCoordinator::setUndoMode() { Q_ASSERT(!m_undoMode); m_undoMode = true; disconnectAllSenders(); m_reachedPercentageBeforeUndo = progressInPercentage(); m_currentBasePercentage = m_reachedPercentageBeforeUndo; } void ProgressCoordinator::addManualPercentagePoints(int value) { m_manualAddedPercentage = m_manualAddedPercentage + value; if (m_undoMode) { //we don't do other things in the undomode, maybe later if the last percentage point comes to early return; } m_currentCompletePercentage = m_currentCompletePercentage + value; if (m_currentCompletePercentage > 100.0) m_currentCompletePercentage = 100.0; qApp->processEvents(); //makes the result available in the ui } void ProgressCoordinator::addReservePercentagePoints(int value) { m_reservedPercentage = m_reservedPercentage + value; } void ProgressCoordinator::setLabelText(const QString &text) { if (m_installationLabelText == text) return; m_installationLabelText = text; } /*! Contains the installation progress label text. */ QString ProgressCoordinator::labelText() const { return m_installationLabelText; } void ProgressCoordinator::emitDetailTextChanged(const QString &text) { emit detailTextChanged(text); } void ProgressCoordinator::emitLabelAndDetailTextChanged(const QString &text) { emit detailTextChanged(text); m_installationLabelText = QString(text).remove(QLatin1String("\n")); qApp->processEvents(); //makes the result available in the ui } double ProgressCoordinator::allPendingCalculatedPartPercentages(QObject *excludeKeyObject) { double result = 0; QHash, double>::iterator it = m_senderPendingCalculatedPercentageHash.begin(); while (it != m_senderPendingCalculatedPercentageHash.end()) { if (it.key() != excludeKeyObject) result += it.value(); it++; } return result; } void ProgressCoordinator::emitDownloadStatus(const QString &status) { emit downloadStatusChanged(status); }