File indexing completed on 2024-05-12 04:38:18

0001 /*
0002     SPDX-FileCopyrightText: 2004 Till Adam <adam@kde.org>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #ifndef KDEVPLATFORM_PROGRESSMANAGER_H
0008 #define KDEVPLATFORM_PROGRESSMANAGER_H
0009 
0010 #include <QObject>
0011 #include <QString>
0012 #include <QMap>
0013 #include <QHash>
0014 #include <QPointer>
0015 namespace Akonadi {
0016 class AgentInstance;
0017 }
0018 
0019 namespace KDevelop {
0020 
0021 class ProgressItem;
0022 class ProgressManager;
0023 using ProgressItemMap = QMap<ProgressItem*, bool>;
0024 
0025 class ProgressItem : public QObject
0026 {
0027     Q_OBJECT
0028     friend class ProgressManager;
0029 
0030 public:
0031 
0032     /**
0033      * @return The id string which uniquely identifies the operation
0034      *         represented by this item.
0035      */
0036     const QString &id() const { return mId; }
0037 
0038     /**
0039      * @return The parent item of this one, if there is one.
0040      */
0041     ProgressItem *parent() const { return mParent.data(); }
0042 
0043     /**
0044      * @return The user visible string to be used to represent this item.
0045      */
0046     const QString &label() const { return mLabel; }
0047 
0048     /**
0049      * @param v Set the user visible string identifying this item.
0050      */
0051     void setLabel( const QString &v );
0052 
0053     /**
0054      * @return The string to be used for showing this item's current status.
0055      */
0056     const QString &status() const { return mStatus; }
0057     /**
0058      * Set the string to be used for showing this item's current status.
0059      * @param v The status string.
0060      */
0061     void setStatus( const QString &v );
0062 
0063     /**
0064      * @return Whether this item can be canceled.
0065      */
0066     bool canBeCanceled() const { return mCanBeCanceled; }
0067 
0068     /**
0069      * @return Whether this item uses secure communication
0070      * (Account uses ssl, for example.).
0071      */
0072     bool usesCrypto() const { return mUsesCrypto; }
0073 
0074     /**
0075      * Set whether this item uses crypted communication, so listeners
0076      * can display a nice crypto icon.
0077      * @param v The value.
0078      */
0079     void setUsesCrypto( bool v );
0080 
0081     /**
0082      * @return whether this item uses a busy indicator instead of real progress display
0083      */
0084     bool usesBusyIndicator() const { return mUsesBusyIndicator; }
0085 
0086     /**
0087      * Sets whether this item uses a busy indicator instead of real progress for its progress bar.
0088      * If it uses a busy indicator, you are still responsible for calling setProgress() from time to
0089      * time to update the busy indicator.
0090      */
0091     void setUsesBusyIndicator( bool useBusyIndicator );
0092 
0093     /**
0094      * @return The current progress value of this item in percent.
0095      */
0096     unsigned int progress() const { return mProgress; }
0097 
0098     /**
0099      * Set the progress (percentage of completion) value of this item.
0100      * @param v The percentage value.
0101      */
0102     void setProgress( unsigned int v );
0103 
0104     /**
0105      * Tell the item it has finished. This will emit progressItemCompleted()
0106      * result in the destruction of the item after all slots connected to this
0107      * signal have executed. This is the only way to get rid of an item and
0108      * needs to be called even if the item is canceled. Don't use the item
0109      * after this has been called on it.
0110      */
0111     void setComplete();
0112 
0113     /**
0114      * Reset the progress value of this item to 0 and the status string to
0115      * the empty string.
0116      */
0117     void reset()
0118     {
0119         setProgress( 0 );
0120         setStatus( QString() );
0121         mCompleted = 0;
0122     }
0123 
0124     void cancel();
0125 
0126     // Often needed values for calculating progress.
0127     void setTotalItems( unsigned int v ) { mTotal = v; }
0128     unsigned int totalItems() const { return mTotal; }
0129     void setCompletedItems( unsigned int v ) { mCompleted = v; }
0130     void incCompletedItems( unsigned int v = 1 ) { mCompleted += v; }
0131     unsigned int completedItems() const { return mCompleted; }
0132 
0133     /**
0134      * Recalculate progress according to total/completed items and update.
0135      */
0136     void updateProgress()
0137     {
0138         setProgress( mTotal? mCompleted * 100 / mTotal : 0 );
0139     }
0140 
0141     void addChild( ProgressItem *kiddo );
0142     void removeChild( ProgressItem *kiddo );
0143 
0144     bool canceled() const { return mCanceled; }
0145     void setBusy( bool busy );
0146 
0147 Q_SIGNALS:
0148     /**
0149      * Emitted when a new ProgressItem is added.
0150      * @param item The ProgressItem that was added.
0151      */
0152     void progressItemAdded( KDevelop::ProgressItem* item );
0153 
0154     /**
0155      * Emitted when the progress value of an item changes.
0156      * @param item The item which got a new value.
0157      * @param value The value, for convenience.
0158      */
0159     void progressItemProgress( KDevelop::ProgressItem* item, unsigned int value );
0160 
0161     /**
0162      * Emitted when a progress item was completed. The item will be
0163      * deleted afterwards, so slots connected to this are the last
0164      * chance to work with this item.
0165      * @param item The completed item.
0166      */
0167     void progressItemCompleted( KDevelop::ProgressItem* item );
0168 
0169     /**
0170      * Emitted when an item was canceled. It will _not_ go away immediately,
0171      * only when the owner sets it complete, which will usually happen. Can be
0172      * used to visually indicate the canceled status of an item. Should be used
0173      * by the owner of the item to make sure it is set completed even if it is
0174      * canceled. There is a ProgressManager::slotStandardCancelHandler which
0175      * simply sets the item completed and can be used if no other work needs to
0176      * be done on cancel.
0177      * @param item The canceled item;
0178      */
0179     void progressItemCanceled( KDevelop::ProgressItem* item );
0180 
0181     /**
0182      * Emitted when the status message of an item changed. Should be used by
0183      * progress dialogs to update the status message for an item.
0184      * @param item The updated item.
0185      * @param message The new message.
0186      */
0187     void progressItemStatus( KDevelop::ProgressItem* item, const QString& message );
0188 
0189     /**
0190      * Emitted when the label of an item changed. Should be used by
0191      * progress dialogs to update the label of an item.
0192      * @param item The updated item.
0193      * @param label The new label.
0194      */
0195     void progressItemLabel( KDevelop::ProgressItem* item, const QString& label );
0196 
0197     /**
0198      * Emitted when the crypto status of an item changed. Should be used by
0199      * progress dialogs to update the crypto indicator of an item.
0200      * @param item The updated item.
0201      * @param state The new state.
0202      */
0203     void progressItemUsesCrypto( KDevelop::ProgressItem* item, bool state );
0204 
0205     /**
0206      * Emitted when the busy indicator state of an item changes. Should be used
0207      * by progress dialogs so that they can adjust the display of the progress bar
0208      * to the new mode.
0209      * @param item The updated item
0210      * @param value True if the item uses a busy indicator now, false otherwise
0211      */
0212     void progressItemUsesBusyIndicator( KDevelop::ProgressItem *item, bool value );
0213 
0214 protected:
0215     /* Only to be used by our good friend the ProgressManager */
0216     ProgressItem( ProgressItem *parent, const QString &id, const QString &label,
0217                   const QString &status, bool isCancellable, bool usesCrypto );
0218     ~ProgressItem() override;
0219 
0220 private:
0221     const QString mId;
0222     QString mLabel;
0223     QString mStatus;
0224     QPointer<ProgressItem> mParent;
0225     const bool mCanBeCanceled;
0226     unsigned int mProgress;
0227     ProgressItemMap mChildren;
0228     unsigned int mTotal;
0229     unsigned int mCompleted;
0230     bool mWaitingForKids;
0231     bool mCanceled;
0232     bool mUsesCrypto;
0233     bool mUsesBusyIndicator;
0234     bool mCompletedCalled;
0235 };
0236 
0237 struct ProgressManagerPrivate;
0238 
0239 /**
0240  * The ProgressManager singleton keeps track of all ongoing transactions
0241  * and notifies observers (progress dialogs) when their progress percent value
0242  * changes, when they are completed (by their owner), and when they are canceled.
0243  * Each ProgressItem emits those signals individually and the singleton
0244  * broadcasts them. Use the createProgressItem() statics to acquire an item
0245  * and then call ->setProgress( int percent ) on it every time you want to
0246  * update the item and ->setComplete() when the operation is done. This will
0247  * delete the item. Connect to the item's progressItemCanceled() signal to be
0248  * notified when the user cancels the transaction using one of the observing
0249  * progress dialogs or by calling item->cancel() in some other way. The owner
0250  * is responsible for calling setComplete() on the item, even if it is canceled.
0251  * Use the standardCancelHandler() slot if that is all you want to do on cancel.
0252  *
0253  * Note that if you request an item with a certain id and there is already
0254  * one with that id, there will not be a new one created but the existing
0255  * one will be returned. This is convenient for accessing items that are
0256  * needed regularly without the to store a pointer to them or to add child
0257  * items to parents by id.
0258  */
0259 class ProgressManager : public QObject
0260 {
0261 
0262     Q_OBJECT
0263 
0264     friend struct ProgressManagerPrivate;
0265 
0266 public:
0267     ~ProgressManager() override;
0268 
0269     /**
0270      * @return The singleton instance of this class.
0271      */
0272     static ProgressManager *instance();
0273 
0274     /**
0275      * @return a unique id number which can be used to discern
0276      * an operation from all others going on at the same time. Use that
0277      * number as the id string for your <i>progressItem</i> to ensure it is unique.
0278      */
0279     static QString createUniqueID()
0280     {
0281         return QString::number( ++uID );
0282     }
0283 
0284     /**
0285       * Creates a ProgressItem with a unique id and the given label.
0286       * This is the simplest way to acquire a progress item. It will not
0287       * have a parent and will be set to be cancellable and not using crypto.
0288       */
0289     static ProgressItem *createProgressItem( const QString &label )
0290     {
0291         return instance()->createProgressItemImpl( nullptr, createUniqueID(), label,
0292                                                    QString(), true, false );
0293     }
0294 
0295     /**
0296      * Creates a new progressItem with the given parent, id, label and initial
0297      * status.
0298      *
0299      * @param parent Specify an already existing item as the parent of this one.
0300      * @param id Used to identify this operation for cancel and progress info.
0301      * @param label The text to be displayed by progress handlers
0302      * @param status Additional text to be displayed for the item.
0303      * @param canBeCanceled can the user cancel this operation?
0304      * @param usesCrypto does the operation use secure transports (SSL)
0305      * Cancelling the parent will cancel the children as well (if they can be
0306      * canceled) and ongoing children prevent parents from finishing.
0307      * @return The ProgressItem representing the operation.
0308      */
0309     static ProgressItem *createProgressItem( ProgressItem *parent,
0310                                              const QString &id,
0311                                              const QString &label,
0312                                              const QString &status = QString(),
0313                                              bool canBeCanceled = true,
0314                                              bool usesCrypto = false )
0315     {
0316         return instance()->createProgressItemImpl( parent, id, label, status,
0317                                                    canBeCanceled, usesCrypto );
0318     }
0319 
0320     /**
0321      * Use this version if you have the id string of the parent and want to
0322      * add a subjob to it.
0323      */
0324     static ProgressItem *createProgressItem( const QString &parent,
0325                                              const QString &id,
0326                                              const QString &label,
0327                                              const QString &status = QString(),
0328                                              bool canBeCanceled = true,
0329                                              bool usesCrypto = false )
0330     {
0331         return instance()->createProgressItemImpl( parent, id, label,
0332                                                    status, canBeCanceled, usesCrypto );
0333     }
0334 
0335     /**
0336      * Version without a parent.
0337      */
0338     static ProgressItem *createProgressItem( const QString &id,
0339                                              const QString &label,
0340                                              const QString &status = QString(),
0341                                              bool canBeCanceled = true,
0342                                              bool usesCrypto = false )
0343     {
0344         return instance()->createProgressItemImpl( nullptr, id, label, status,
0345                                                    canBeCanceled, usesCrypto );
0346     }
0347 
0348     /**
0349      * Version for Akonadi agents.
0350      * This connects all the proper signals so that you do not have to
0351      * worry about updating the progress or reacting to progressItemCanceled().
0352      */
0353     static ProgressItem *createProgressItem( ProgressItem *parent,
0354                                              const Akonadi::AgentInstance &agent,
0355                                              const QString &id,
0356                                              const QString &label,
0357                                              const QString &status = QString(),
0358                                              bool canBeCanceled = true,
0359                                              bool usesCrypto = false )
0360     {
0361         return instance()->createProgressItemForAgent( parent, agent, id, label,
0362                                                        status, canBeCanceled, usesCrypto );
0363     }
0364 
0365     /**
0366      * @return true when there are no more progress items.
0367      */
0368     bool isEmpty() const
0369     {
0370         return mTransactions.isEmpty();
0371     }
0372 
0373     /**
0374      * @return the only top level progressitem when there's only one.
0375      * Returns 0 if there is no item, or more than one top level item.
0376      * Since this is used to calculate the overall progress, it will also return
0377      * 0 if there is an item which uses a busy indicator, since that will invalidate
0378      * the overall progress.
0379      */
0380     ProgressItem *singleItem() const;
0381 
0382     /**
0383      * Ask all listeners to show the progress dialog, because there is
0384      * something that wants to be shown.
0385      */
0386     static void emitShowProgressDialog()
0387     {
0388         instance()->emitShowProgressDialogImpl();
0389     }
0390 
0391 Q_SIGNALS:
0392     /** @see ProgressItem::progressItemAdded() */
0393     void progressItemAdded( KDevelop::ProgressItem * );
0394     /** @see ProgressItem::progressItemProgress() */
0395     void progressItemProgress( KDevelop::ProgressItem *, unsigned int );
0396     /** @see ProgressItem::progressItemCompleted() */
0397     void progressItemCompleted( KDevelop::ProgressItem * );
0398     /** @see ProgressItem::progressItemCanceled() */
0399     void progressItemCanceled( KDevelop::ProgressItem * );
0400     /** @see ProgressItem::progressItemStatus() */
0401     void progressItemStatus( KDevelop::ProgressItem *, const QString & );
0402     /** @see ProgressItem::progressItemLabel() */
0403     void progressItemLabel( KDevelop::ProgressItem *, const QString & );
0404     /** @see ProgressItem::progressItemUsesCrypto() */
0405     void progressItemUsesCrypto( KDevelop::ProgressItem *, bool );
0406     /** @see ProgressItem::progressItemUsesBusyIndicator */
0407     void progressItemUsesBusyIndicator( KDevelop::ProgressItem*, bool );
0408 
0409     /**
0410      * Emitted when an operation requests the listeners to be shown.
0411      * Use emitShowProgressDialog() to trigger it.
0412      */
0413     void showProgressDialog();
0414 
0415 public Q_SLOTS:
0416 
0417     /**
0418      * Calls setCompleted() on the item, to make sure it goes away.
0419      * Provided for convenience.
0420      * @param item the canceled item.
0421      */
0422     void slotStandardCancelHandler( KDevelop::ProgressItem *item );
0423 
0424     /**
0425      * Aborts all running jobs. Bound to "Esc"
0426      */
0427     void slotAbortAll();
0428 
0429 private Q_SLOTS:
0430     void slotTransactionCompleted( KDevelop::ProgressItem *item );
0431 
0432 private:
0433     ProgressManager();
0434     // prevent unsolicited copies
0435     ProgressManager( const ProgressManager & );
0436 
0437     virtual ProgressItem *createProgressItemImpl( ProgressItem *parent,
0438                                                   const QString &id,
0439                                                   const QString &label,
0440                                                   const QString &status,
0441                                                   bool cancellable,
0442                                                   bool usesCrypto );
0443     virtual ProgressItem *createProgressItemImpl( const QString &parent,
0444                                                   const QString &id,
0445                                                   const QString &label,
0446                                                   const QString &status,
0447                                                   bool cancellable,
0448                                                   bool usesCrypto );
0449     ProgressItem *createProgressItemForAgent( ProgressItem *parent,
0450                                               const Akonadi::AgentInstance &instance,
0451                                               const QString &id,
0452                                               const QString &label,
0453                                               const QString &status,
0454                                               bool cancellable,
0455                                               bool usesCrypto );
0456     void emitShowProgressDialogImpl();
0457 
0458     QHash< QString, ProgressItem* > mTransactions;
0459     static unsigned int uID;
0460 };
0461 
0462 }
0463 
0464 #endif // __KDevelop_PROGRESSMANAGER_H__