Libkdepim

statusbarprogresswidget.cpp
1/*
2 statusbarprogresswidget.cpp
3
4 SPDX-FileCopyrightText: 2004 Till Adam <adam@kde.org>
5 SPDX-FileCopyrightText: 2004 Don Sanders
6 SPDX-FileCopyrightText: 2004 David Faure <faure@kde.org>
7
8 Includes StatusbarProgressWidget which is based on KIOLittleProgressDlg
9 by SPDX-FileCopyrightText: Matt Koss <koss@miesto.sk>
10
11 SPDX-License-Identifier: GPL-2.0-or-later
12*/
13
14#include "statusbarprogresswidget.h"
15using namespace Qt::Literals::StringLiterals;
16
17#include "progressdialog.h"
18#include "ssllabel.h"
19using KPIM::SSLLabel;
20#include "progressmanager.h"
23
24#include <KLocalizedString>
25
26#include <QEvent>
27#include <QHBoxLayout>
28#include <QLabel>
29#include <QMouseEvent>
30#include <QProgressBar>
31#include <QPushButton>
32#include <QStackedWidget>
33#include <QTimer>
34#include <chrono>
35
36using namespace std::chrono_literals;
37
38using namespace KPIM;
39
40//-----------------------------------------------------------------------------
41StatusbarProgressWidget::StatusbarProgressWidget(ProgressDialog *progressDialog, QWidget *parent, bool button)
42 : QFrame(parent)
43 , mButton(new QPushButton(this))
44 , mShowButton(button)
45 , mProgressDialog(progressDialog)
46{
47 int w = fontMetrics().boundingRect(QStringLiteral(" 999.9 kB/s 00:00:01 ")).width() + 8;
48 auto boxLayout = new QHBoxLayout(this);
49 boxLayout->setObjectName("boxLayout"_L1);
50 boxLayout->setContentsMargins(0, 0, 0, 0);
51 boxLayout->setSpacing(0);
52
53 mButton->setObjectName("button"_L1);
55 mButton->setIcon(QIcon::fromTheme(QStringLiteral("go-up")));
56 boxLayout->addWidget(mButton);
57 mStackedWidget = new QStackedWidget(this);
58 mStackedWidget->setObjectName("stackedwidget"_L1);
59 int maximumHeight = qMax(mButton->iconSize().height(), fontMetrics().height());
60 mStackedWidget->setMaximumHeight(maximumHeight);
61 boxLayout->addWidget(mStackedWidget);
62
63 mSslLabel = new SSLLabel(this);
64 mSslLabel->setObjectName("ssllabel"_L1);
65 boxLayout->addWidget(mSslLabel);
66
67 mButton->setToolTip(i18nc("@info:tooltip", "Open detailed progress dialog"));
68
69 mProgressBar = new QProgressBar(this);
70 mProgressBar->setObjectName("progressbar"_L1);
71 mProgressBar->installEventFilter(this);
72 mProgressBar->setMinimumWidth(w);
73 mProgressBar->setFormat(i18nc("Percent value; %p is the value, % is the percent sign", "%p%"));
74 mStackedWidget->insertWidget(1, mProgressBar);
75
76 mLabel = new QLabel(QString(), this);
77 mLabel->setObjectName("emptylabel"_L1);
78 mLabel->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter);
79 mLabel->installEventFilter(this);
80 mLabel->setMinimumWidth(w);
81 mStackedWidget->insertWidget(2, mLabel);
82 mButton->setMaximumHeight(maximumHeight);
83 setFixedWidth(600);
84
85 mMode = Progress; // so the call below works
86 setMode(Clean);
87
88 connect(mButton, &QAbstractButton::clicked, this, &StatusbarProgressWidget::slotProgressButtonClicked);
89
90 connect(ProgressManager::instance(), &ProgressManager::progressItemAdded, this, &StatusbarProgressWidget::slotProgressItemAdded);
91 connect(ProgressManager::instance(), &ProgressManager::progressItemCompleted, this, &StatusbarProgressWidget::slotProgressItemCompleted);
92 connect(ProgressManager::instance(), &ProgressManager::progressItemUsesBusyIndicator, this, &StatusbarProgressWidget::updateBusyMode);
93
94 connect(progressDialog, &ProgressDialog::visibilityChanged, this, &StatusbarProgressWidget::slotProgressDialogVisible);
95
96 mDelayTimer = new QTimer(this);
97 mDelayTimer->setSingleShot(true);
98 connect(mDelayTimer, &QTimer::timeout, this, &StatusbarProgressWidget::slotShowItemDelayed);
99
100 mCleanTimer = new QTimer(this);
101 mCleanTimer->setSingleShot(true);
102 connect(mCleanTimer, &QTimer::timeout, this, &StatusbarProgressWidget::slotClean);
103}
104
105void StatusbarProgressWidget::setShowTypeProgressItem(unsigned int type)
106{
107 mShowTypeProgressItem = type;
108}
109
110// There are three cases: no progressitem, one progressitem (connect to it directly),
111// or many progressitems (display busy indicator). Let's call them 0,1,N.
112// In slot..Added we can only end up in 1 or N.
113// In slot..Removed we can end up in 0, 1, or we can stay in N if we were already.
114
115void StatusbarProgressWidget::updateBusyMode(KPIM::ProgressItem *item)
116{
117 if (item->typeProgressItem() == mShowTypeProgressItem) {
118 connectSingleItem(); // if going to 1 item
119 if (mCurrentItem) { // Exactly one item
120 delete mBusyTimer;
121 mBusyTimer = nullptr;
122 mDelayTimer->start(1s);
123 } else { // N items
124 if (!mBusyTimer) {
125 mBusyTimer = new QTimer(this);
126 connect(mBusyTimer, &QTimer::timeout, this, &StatusbarProgressWidget::slotBusyIndicator);
127 mDelayTimer->start(1s);
128 }
129 }
130 }
131}
132
133void StatusbarProgressWidget::slotProgressItemAdded(ProgressItem *item)
134{
135 if (item->parent()) {
136 return; // we are only interested in top level items
137 }
138
139 updateBusyMode(item);
140}
141
142void StatusbarProgressWidget::slotProgressItemCompleted(ProgressItem *item)
143{
144 if (item->parent()) {
145 item->deleteLater();
146 item = nullptr;
147 return; // we are only interested in top level items
148 }
149 item->deleteLater();
150 item = nullptr;
151 connectSingleItem(); // if going back to 1 item
152 if (ProgressManager::instance()->isEmpty()) { // No item
153 // Done. In 5s the progress-widget will close, then we can clean up the statusbar
154 mCleanTimer->start(5s);
155 } else if (mCurrentItem) { // Exactly one item
156 delete mBusyTimer;
157 mBusyTimer = nullptr;
158 activateSingleItemMode();
159 }
160}
161
162void StatusbarProgressWidget::connectSingleItem()
163{
164 if (mCurrentItem) {
165 disconnect(mCurrentItem, &ProgressItem::progressItemProgress, this, &StatusbarProgressWidget::slotProgressItemProgress);
166 mCurrentItem = nullptr;
167 }
168 mCurrentItem = ProgressManager::instance()->singleItem();
169 if (mCurrentItem) {
170 connect(mCurrentItem, &ProgressItem::progressItemProgress, this, &StatusbarProgressWidget::slotProgressItemProgress);
171 }
172}
173
174void StatusbarProgressWidget::activateSingleItemMode()
175{
176 mProgressBar->setMaximum(100);
177 mProgressBar->setValue(mCurrentItem->progress());
178 mProgressBar->setTextVisible(true);
179}
180
181void StatusbarProgressWidget::slotShowItemDelayed()
182{
183 bool noItems = ProgressManager::instance()->isEmpty();
184 if (mCurrentItem) {
185 activateSingleItemMode();
186 } else if (!noItems) { // N items
187 mProgressBar->setMaximum(0);
188 mProgressBar->setTextVisible(false);
189 Q_ASSERT(mBusyTimer);
190 if (mBusyTimer) {
191 mBusyTimer->start(100ms);
192 }
193 }
194
195 if (!noItems) {
196 setMode(Progress);
197 }
198}
199
200void StatusbarProgressWidget::slotBusyIndicator()
201{
202 const int p = mProgressBar->value();
203 mProgressBar->setValue(p + 10);
204}
205
206void StatusbarProgressWidget::slotProgressItemProgress(ProgressItem *item, unsigned int value)
207{
208 Q_ASSERT(item == mCurrentItem); // the only one we should be connected to
209 Q_UNUSED(item)
210 mProgressBar->setValue(value);
211}
212
213void StatusbarProgressWidget::setMode(Mode mode)
214{
215 if (mMode == mode) {
216 return;
217 }
218 mMode = mode;
219 switch (mMode) {
220 case Clean:
221 if (mShowButton) {
222 mButton->hide();
223 }
224 mSslLabel->setState(SSLLabel::Done);
225 // show the empty label in order to make the status bar look better
226 mStackedWidget->show();
227 mStackedWidget->setCurrentWidget(mLabel);
228 break;
229 case Progress:
230 mStackedWidget->show();
231 mStackedWidget->setCurrentWidget(mProgressBar);
232 if (mShowButton) {
233 mShowDetailedProgress = mProgressDialog->wasLastShown();
234 updateProgressButton(mShowDetailedProgress);
235 mButton->show();
236 }
237 mSslLabel->setState(mSslLabel->lastState());
238 break;
239 }
240}
241
242void StatusbarProgressWidget::slotClean()
243{
244 // check if a new item showed up since we started the timer. If not, clear
245 if (ProgressManager::instance()->isEmpty()) {
246 mProgressBar->setValue(0);
247 setMode(Clean);
248 }
249}
250
251bool StatusbarProgressWidget::eventFilter(QObject *obj, QEvent *ev)
252{
253 if (ev->type() == QEvent::MouseButtonPress) {
254 auto e = static_cast<QMouseEvent *>(ev);
255
256 if (e->button() == Qt::LeftButton && mMode == Progress) { // toggle view on left mouse button
257 // Consensus seems to be that we should show/hide the fancy dialog when the user
258 // clicks anywhere in the small one.
259 slotProgressButtonClicked();
260 return true;
261 }
262 }
263 return QFrame::eventFilter(obj, ev);
264}
265
266void StatusbarProgressWidget::updateProgressButton(bool showingProgress)
267{
268 if (!showingProgress) {
269 mButton->setIcon(QIcon::fromTheme(QStringLiteral("go-up")));
270 mButton->setToolTip(i18nc("@info:tooltip", "Show detailed progress window"));
271 } else {
272 mButton->setIcon(QIcon::fromTheme(QStringLiteral("go-down")));
273 mButton->setToolTip(i18nc("@info:tooltip", "Hide detailed progress window"));
274 }
275}
276
277void StatusbarProgressWidget::slotProgressButtonClicked()
278{
279 mProgressDialog->slotToggleVisibility();
280 mShowDetailedProgress = !mProgressDialog->isHidden();
281 setFixedWidth(qMax(600, mProgressDialog->width()));
282}
283
284void StatusbarProgressWidget::slotProgressDialogVisible(bool b)
285{
286 // Show the hide/show button (mButton) as soon as the progress dialog is shown
287 // (StatusbarProgressWidget::slotShowItemDelayed happens later)
288 if (b) {
289 setMode(Progress);
290 }
291
292 updateProgressButton(b);
293}
294
295#include "moc_statusbarprogresswidget.cpp"
The ProgressItem class.
unsigned int progress() const
ProgressItem * parent() const
void progressItemProgress(KPIM::ProgressItem *, unsigned int)
Emitted when the progress value of an item changes.
The ProgressManager singleton keeps track of all ongoing transactions and notifies observers (progres...
void progressItemAdded(KPIM::ProgressItem *)
static ProgressManager * instance()
ProgressItem * singleItem() const
void progressItemUsesBusyIndicator(KPIM::ProgressItem *, bool)
void progressItemCompleted(KPIM::ProgressItem *)
QString i18nc(const char *context, const char *text, const TYPE &arg...)
Type type(const QSqlDatabase &db)
Class KCheckComboBox::KCheckComboBoxPrivate.
void clicked(bool checked)
void setIcon(const QIcon &icon)
MouseButtonPress
Type type() const const
QIcon fromTheme(const QString &name)
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
void deleteLater()
bool disconnect(const QMetaObject::Connection &connection)
virtual bool eventFilter(QObject *watched, QEvent *event)
void setMaximum(int maximum)
void setTextVisible(bool visible)
void setValue(int value)
void setCurrentWidget(QWidget *widget)
AlignHCenter
LeftButton
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
void start()
void timeout()
void hide()
bool isHidden() const const
void setFixedWidth(int w)
void show()
void setToolTip(const QString &)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Sat Dec 21 2024 16:58:12 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.