1 /*
2   progressmanager.h
3 
4   This file is part of libkdepim.
5 
6   SPDX-FileCopyrightText: 2004 Till Adam <adam@kde.org>
7 
8   SPDX-License-Identifier: LGPL-2.0-or-later
9 */
10 
11 #pragma once
12 
13 #include "kdepim_export.h"
14 
15 #include <QHash>
16 #include <QMap>
17 #include <QObject>
18 #include <QPointer>
19 #include <QString>
20 
21 namespace KPIM
22 {
23 class ProgressItem;
24 class ProgressManager;
25 using ProgressItemMap = QMap<ProgressItem *, bool>;
26 /**
27  * @brief The ProgressItem class
28  */
29 class KDEPIM_EXPORT ProgressItem : public QObject
30 {
31     Q_OBJECT
32     friend class ProgressManager;
33 
34 public:
35     enum CryptoStatus { Encrypted, Unencrypted, Unknown };
36 
37     /**
38      * @return The id string which uniquely identifies the operation
39      *         represented by this item.
40      */
41     Q_REQUIRED_RESULT const QString &id() const;
42 
43     /**
44      * @return The parent item of this one, if there is one.
45      */
46     Q_REQUIRED_RESULT ProgressItem *parent() const;
47 
48     /**
49      * @return The user visible string to be used to represent this item.
50      */
51     Q_REQUIRED_RESULT const QString &label() const;
52 
53     /**
54      * @param v Set the user visible string identifying this item.
55      */
56     void setLabel(const QString &v);
57 
58     /**
59      * @return The string to be used for showing this item's current status.
60      */
61     Q_REQUIRED_RESULT const QString &status() const;
62     /**
63      * Set the string to be used for showing this item's current status.
64      * @param v The status string.
65      */
66     void setStatus(const QString &v);
67 
68     /**
69      * @return Whether this item can be canceled.
70      */
71     Q_REQUIRED_RESULT bool canBeCanceled() const;
72 
73     /**
74      * @param b Set if can be canceled
75      */
76     void setCanBeCanceled(bool b);
77 
78     /**
79      * @return Whether this item uses secure communication
80      * (Account uses ssl, for example.).
81      */
82     Q_REQUIRED_RESULT CryptoStatus cryptoStatus() const;
83 
84     /**
85      * Set whether this item uses encrypted communication, so listeners
86      * can display a nice crypto icon.
87      * @param v The value.
88      */
89     void setCryptoStatus(ProgressItem::CryptoStatus v);
90 
91     /**
92      * @return whether this item uses a busy indicator instead of real progress display
93      */
94     Q_REQUIRED_RESULT bool usesBusyIndicator() const;
95 
96     /**
97      * Sets whether this item uses a busy indicator instead of real progress for its progress bar.
98      * If it uses a busy indicator, you are still responsible for calling setProgress() from time to
99      * time to update the busy indicator.
100      */
101     void setUsesBusyIndicator(bool useBusyIndicator);
102 
103     /**
104      * @return The current progress value of this item in percent.
105      */
106     Q_REQUIRED_RESULT unsigned int progress() const;
107 
108     /**
109      * Set the progress (percentage of completion) value of this item.
110      * @param v The percentage value.
111      */
112     void setProgress(unsigned int v);
113 
114     /**
115      * Tell the item it has finished. This will Q_EMIT progressItemCompleted()
116      * result in the destruction of the item after all slots connected to this
117      * signal have executed. This is the only way to get rid of an item and
118      * needs to be called even if the item is canceled. Don't use the item
119      * after this has been called on it.
120      */
121     void setComplete();
122 
123     /**
124      * Reset the progress value of this item to 0 and the status string to
125      * the empty string.
126      */
127     void reset();
128 
129     void cancel();
130 
131     // Often needed values for calculating progress.
132     void setTotalItems(unsigned int v);
133     Q_REQUIRED_RESULT unsigned int totalItems() const;
134     void setCompletedItems(unsigned int v);
135     void incCompletedItems(unsigned int v = 1);
136     Q_REQUIRED_RESULT unsigned int completedItems() const;
137 
138     /**
139      * Recalculate progress according to total/completed items and update.
140      */
141     void updateProgress();
142 
143     void addChild(ProgressItem *kiddo);
144     void removeChild(ProgressItem *kiddo);
145 
146     bool canceled() const;
147 
148     unsigned int typeProgressItem() const;
149     void setTypeProgressItem(unsigned int);
150 
151 Q_SIGNALS:
152     /**
153      * Emitted when a new ProgressItem is added.
154      * @param The ProgressItem that was added.
155      */
156     void progressItemAdded(KPIM::ProgressItem *);
157 
158     /**
159      * Emitted when the progress value of an item changes.
160      * @param  The item which got a new value.
161      * @param  The value, for convenience.
162      */
163     void progressItemProgress(KPIM::ProgressItem *, unsigned int);
164 
165     /**
166      * Emitted when a progress item was completed. The item will be
167      * deleted afterwards, so slots connected to this are the last
168      * chance to work with this item.
169      * @param The completed item.
170      */
171     void progressItemCompleted(KPIM::ProgressItem *);
172 
173     /**
174      * Emitted when an item was canceled. It will _not_ go away immediately,
175      * only when the owner sets it complete, which will usually happen. Can be
176      * used to visually indicate the canceled status of an item. Should be used
177      * by the owner of the item to make sure it is set completed even if it is
178      * canceled. There is a ProgressManager::slotStandardCancelHandler which
179      * simply sets the item completed and can be used if no other work needs to
180      * be done on cancel.
181      * @param The canceled item;
182      */
183     void progressItemCanceled(KPIM::ProgressItem *);
184 
185     /**
186      * Emitted when the status message of an item changed. Should be used by
187      * progress dialogs to update the status message for an item.
188      * @param  The updated item.
189      * @param  The new message.
190      */
191     void progressItemStatus(KPIM::ProgressItem *, const QString &);
192 
193     /**
194      * Emitted when the label of an item changed. Should be used by
195      * progress dialogs to update the label of an item.
196      * @param  The updated item.
197      * @param  The new label.
198      */
199     void progressItemLabel(KPIM::ProgressItem *, const QString &);
200 
201     /**
202      * Emitted when the crypto status of an item changed. Should be used by
203      * progress dialogs to update the crypto indicator of an item.
204      * @param  The updated item.
205      * @param  The new state.
206      */
207     void progressItemCryptoStatus(KPIM::ProgressItem *, KPIM::ProgressItem::CryptoStatus);
208 
209     /**
210      * Emitted when the busy indicator state of an item changes. Should be used
211      * by progress dialogs so that they can adjust the display of the progress bar
212      * to the new mode.
213      * @param item The updated item
214      * @param value True if the item uses a busy indicator now, false otherwise
215      */
216     void progressItemUsesBusyIndicator(KPIM::ProgressItem *item, bool value);
217 
218 protected:
219     /* Only to be used by our good friend the ProgressManager */
220     ProgressItem(ProgressItem *parent, const QString &id, const QString &label, const QString &status, bool isCancellable, CryptoStatus cryptoStatus);
221     ~ProgressItem() override;
222 
223 private:
224     QString mId;
225     QString mLabel;
226     QString mStatus;
227     QPointer<ProgressItem> mParent;
228     bool mCanBeCanceled = false;
229     unsigned int mProgress = 0;
230     ProgressItemMap mChildren;
231     unsigned int mTotal = 0;
232     unsigned int mCompleted = 0;
233     CryptoStatus mCryptoStatus;
234     unsigned int mType = 0;
235     bool mWaitingForKids = false;
236     bool mCanceled = false;
237     bool mUsesBusyIndicator = false;
238     bool mCompletedCalled = false;
239 };
240 
241 struct ProgressManagerPrivate;
242 
243 /**
244  * The ProgressManager singleton keeps track of all ongoing transactions
245  * and notifies observers (progress dialogs) when their progress percent value
246  * changes, when they are completed (by their owner), and when they are canceled.
247  * Each ProgressItem emits those signals individually and the singleton
248  * broadcasts them. Use the ::createProgressItem() statics to acquire an item
249  * and then call ->setProgress( int percent ) on it every time you want to
250  * update the item and ->setComplete() when the operation is done. This will
251  * delete the item. Connect to the item's progressItemCanceled() signal to be
252  * notified when the user cancels the transaction using one of the observing
253  * progress dialogs or by calling item->cancel() in some other way. The owner
254  * is responsible for calling setComplete() on the item, even if it is canceled.
255  * Use the standardCancelHandler() slot if that is all you want to do on cancel.
256  *
257  * Note that if you request an item with a certain id and there is already
258  * one with that id, there will not be a new one created but the existing
259  * one will be returned. This is convenient for accessing items that are
260  * needed regularly without the to store a pointer to them or to add child
261  * items to parents by id.
262  */
263 class KDEPIM_EXPORT ProgressManager : public QObject
264 {
265     Q_OBJECT
266 
267     friend struct ProgressManagerPrivate;
268 
269 public:
270     ~ProgressManager() override;
271 
272     /**
273      * @return The singleton instance of this class.
274      */
275     static ProgressManager *instance();
276 
277     /**
278      * Use this to acquire a unique id number which can be used to discern
279      * an operation from all others going on at the same time. Use that
280      * number as the id string for your progressItem to ensure it is unique.
281      * @return
282      */
283     static QString getUniqueID();
284 
285     /**
286      * Creates a ProgressItem with a unique id and the given label.
287      * This is the simplest way to acquire a progress item. It will not
288      * have a parent and will be set to be cancellable and not using crypto.
289      */
290     static ProgressItem *createProgressItem(unsigned int progressType, const QString &label);
291 
292     /**
293      * Creates a ProgressItem with a unique id and the given label.
294      * This is the simplest way to acquire a progress item. It will not
295      * have a parent and will be set to be cancellable and not using crypto.
296      */
297     static ProgressItem *createProgressItem(const QString &label);
298 
299     /**
300      * Creates a new progressItem with the given parent, id, label and initial
301      * status.
302      *
303      * @param parent Specify an already existing item as the parent of this one.
304      * @param id Used to identify this operation for cancel and progress info.
305      * @param label The text to be displayed by progress handlers
306      * @param status Additional text to be displayed for the item.
307      * @param canBeCanceled can the user cancel this operation?
308      * @param usesCrypto does the operation use secure transports (SSL)
309      * Cancelling the parent will cancel the children as well (if they can be
310      * canceled) and ongoing children prevent parents from finishing.
311      * @return The ProgressItem representing the operation.
312      */
313     static ProgressItem *createProgressItem(ProgressItem *parent,
314                                             const QString &id,
315                                             const QString &label,
316                                             const QString &status = QString(),
317                                             bool canBeCanceled = true,
318                                             KPIM::ProgressItem::CryptoStatus cryptoStatus = KPIM::ProgressItem::Unencrypted);
319 
320     /**
321      * Use this version if you have the id string of the parent and want to
322      * add a subjob to it.
323      */
324     static ProgressItem *createProgressItem(const QString &parent,
325                                             const QString &id,
326                                             const QString &label,
327                                             const QString &status = QString(),
328                                             bool canBeCanceled = true,
329                                             KPIM::ProgressItem::CryptoStatus cryptoStatus = KPIM::ProgressItem::Unencrypted);
330 
331     /**
332      * Version without a parent.
333      */
334     static ProgressItem *createProgressItem(const QString &id,
335                                             const QString &label,
336                                             const QString &status = QString(),
337                                             bool canBeCanceled = true,
338                                             KPIM::ProgressItem::CryptoStatus cryptoStatus = KPIM::ProgressItem::Unencrypted);
339 
340     /**
341      * @return true when there are no more progress items.
342      */
343     bool isEmpty() const;
344 
345     /**
346      * @return the only top level progressitem when there's only one.
347      * Returns 0 if there is no item, or more than one top level item.
348      * Since this is used to calculate the overall progress, it will also return
349      * 0 if there is an item which uses a busy indicator, since that will invalidate
350      * the overall progress.
351      */
352     ProgressItem *singleItem() const;
353 
354     /**
355      * Ask all listeners to show the progress dialog, because there is
356      * something that wants to be shown.
357      */
358     static void emitShowProgressDialog();
359 
360     ProgressItem *progressItem(const QString &id) const;
361 
362 Q_SIGNALS:
363     /** @see ProgressItem::progressItemAdded() */
364     void progressItemAdded(KPIM::ProgressItem *);
365     /** @see ProgressItem::progressItemProgress() */
366     void progressItemProgress(KPIM::ProgressItem *, unsigned int);
367     /** @see ProgressItem::progressItemCompleted() */
368     void progressItemCompleted(KPIM::ProgressItem *);
369     /** @see ProgressItem::progressItemCanceled() */
370     void progressItemCanceled(KPIM::ProgressItem *);
371     /** @see ProgressItem::progressItemStatus() */
372     void progressItemStatus(KPIM::ProgressItem *, const QString &);
373     /** @see ProgressItem::progressItemLabel() */
374     void progressItemLabel(KPIM::ProgressItem *, const QString &);
375     /** @see ProgressItem::progressItemCryptoStatus() */
376     void progressItemCryptoStatus(KPIM::ProgressItem *, KPIM::ProgressItem::CryptoStatus);
377     /** @see ProgressItem::progressItemUsesBusyIndicator */
378     void progressItemUsesBusyIndicator(KPIM::ProgressItem *, bool);
379 
380     /**
381      * Emitted when an operation requests the listeners to be shown.
382      * Use emitShowProgressDialog() to trigger it.
383      */
384     void showProgressDialog();
385 
386 public Q_SLOTS:
387 
388     /**
389      * Calls setCompleted() on the item, to make sure it goes away.
390      * Provided for convenience.
391      * @param item the canceled item.
392      */
393     void slotStandardCancelHandler(KPIM::ProgressItem *item);
394 
395     /**
396      * Aborts all running jobs. Bound to "Esc"
397      */
398     void slotAbortAll();
399 
400 private Q_SLOTS:
401     void slotTransactionCompleted(KPIM::ProgressItem *item);
402 
403 private:
404     ProgressManager();
405     // prevent unsolicited copies
406     ProgressManager(const ProgressManager &);
407 
408     ProgressItem *createProgressItemImpl(ProgressItem *parent,
409                                          const QString &id,
410                                          const QString &label,
411                                          const QString &status,
412                                          bool cancellable,
413                                          ProgressItem::CryptoStatus cryptoStatus,
414                                          unsigned int progressType = 0);
415 
416     ProgressItem *createProgressItemImpl(const QString &parent,
417                                          const QString &id,
418                                          const QString &label,
419                                          const QString &status,
420                                          bool cancellable,
421                                          ProgressItem::CryptoStatus cryptoStatus,
422                                          unsigned int progressType = 0);
423     void emitShowProgressDialogImpl();
424 
425     QHash<QString, ProgressItem *> mTransactions;
426     static unsigned int uID;
427 };
428 }
429 
430