statusbarprogresswidget.cpp 9.61 KB
Newer Older
1
2
3
/*
  statusbarprogresswidget.cpp

4
5
6
  SPDX-FileCopyrightText: 2004 Till Adam <adam@kde.org>
  SPDX-FileCopyrightText: 2004 Don Sanders
  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
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
*/

#include "statusbarprogresswidget.h"
#include "progressdialog.h"
#include "ssllabel.h"
using KPIM::SSLLabel;
#include "progressmanager.h"
using KPIM::ProgressItem;
using KPIM::ProgressManager;

#include <KLocalizedString>

#include <QEvent>
#include <QHBoxLayout>
#include <QLabel>
#include <QMouseEvent>
#include <QProgressBar>
#include <QPushButton>
#include <QStackedWidget>
#include <QTimer>

using namespace KPIM;

//-----------------------------------------------------------------------------
StatusbarProgressWidget::StatusbarProgressWidget(ProgressDialog *progressDialog, QWidget *parent, bool button)
Laurent Montel's avatar
Laurent Montel committed
37
    : QFrame(parent)
38
    , mShowButton(button)
Laurent Montel's avatar
Laurent Montel committed
39
    , mProgressDialog(progressDialog)
40
{
Laurent Montel's avatar
Laurent Montel committed
41
    int w = fontMetrics().boundingRect(QStringLiteral(" 999.9 kB/s 00:00:01 ")).width() + 8;
42
43
    QHBoxLayout *boxLayout = new QHBoxLayout(this);
    boxLayout->setObjectName(QStringLiteral("boxLayout"));
44
    boxLayout->setContentsMargins(0, 0, 0, 0);
45
46
47
48
49
    boxLayout->setSpacing(0);

    mButton = new QPushButton(this);
    mButton->setObjectName(QStringLiteral("button"));
    mButton->setSizePolicy(QSizePolicy(QSizePolicy::Minimum,
Laurent Montel's avatar
Laurent Montel committed
50
                                       QSizePolicy::Minimum));
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
    mButton->setIcon(QIcon::fromTheme(QStringLiteral("go-up")));
    boxLayout->addWidget(mButton);
    mStackedWidget = new QStackedWidget(this);
    mStackedWidget->setObjectName(QStringLiteral("stackedwidget"));
    int maximumHeight = qMax(mButton->iconSize().height(), fontMetrics().height());
    mStackedWidget->setMaximumHeight(maximumHeight);
    boxLayout->addWidget(mStackedWidget);

    mSslLabel = new SSLLabel(this);
    mSslLabel->setObjectName(QStringLiteral("ssllabel"));
    boxLayout->addWidget(mSslLabel);

    mButton->setToolTip(i18n("Open detailed progress dialog"));

    mProgressBar = new QProgressBar(this);
    mProgressBar->setObjectName(QStringLiteral("progressbar"));
    mProgressBar->installEventFilter(this);
    mProgressBar->setMinimumWidth(w);
    mStackedWidget->insertWidget(1, mProgressBar);

    mLabel = new QLabel(QString(), this);
    mLabel->setObjectName(QStringLiteral("emptylabel"));
    mLabel->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter);
    mLabel->installEventFilter(this);
    mLabel->setMinimumWidth(w);
    mStackedWidget->insertWidget(2, mLabel);
    mButton->setMaximumHeight(maximumHeight);
78
    setFixedWidth(600);
79

80
81
    mMode = Progress; // so the call below works
    setMode(Clean);
82

83
    connect(mButton, &QAbstractButton::clicked,
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
            this, &StatusbarProgressWidget::slotProgressButtonClicked);

    connect(ProgressManager::instance(), &ProgressManager::progressItemAdded,
            this, &StatusbarProgressWidget::slotProgressItemAdded);
    connect(ProgressManager::instance(), &ProgressManager::progressItemCompleted,
            this, &StatusbarProgressWidget::slotProgressItemCompleted);
    connect(ProgressManager::instance(), &ProgressManager::progressItemUsesBusyIndicator,
            this, &StatusbarProgressWidget::updateBusyMode);

    connect(progressDialog, &ProgressDialog::visibilityChanged,
            this, &StatusbarProgressWidget::slotProgressDialogVisible);

    mDelayTimer = new QTimer(this);
    mDelayTimer->setSingleShot(true);
    connect(mDelayTimer, &QTimer::timeout,
            this, &StatusbarProgressWidget::slotShowItemDelayed);

    mCleanTimer = new QTimer(this);
    mCleanTimer->setSingleShot(true);
    connect(mCleanTimer, &QTimer::timeout,
            this, &StatusbarProgressWidget::slotClean);
}

void StatusbarProgressWidget::setShowTypeProgressItem(unsigned int type)
{
    mShowTypeProgressItem = type;
}

// There are three cases: no progressitem, one progressitem (connect to it directly),
// or many progressitems (display busy indicator). Let's call them 0,1,N.
// In slot..Added we can only end up in 1 or N.
// In slot..Removed we can end up in 0, 1, or we can stay in N if we were already.

void StatusbarProgressWidget::updateBusyMode(KPIM::ProgressItem *item)
{
    if (item->typeProgressItem() == mShowTypeProgressItem) {
        connectSingleItem(); // if going to 1 item
        if (mCurrentItem) {   // Exactly one item
            delete mBusyTimer;
Laurent Montel's avatar
Laurent Montel committed
123
            mBusyTimer = nullptr;
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
            mDelayTimer->start(1000);
        } else { // N items
            if (!mBusyTimer) {
                mBusyTimer = new QTimer(this);
                connect(mBusyTimer, &QTimer::timeout,
                        this, &StatusbarProgressWidget::slotBusyIndicator);
                mDelayTimer->start(1000);
            }
        }
    }
}

void StatusbarProgressWidget::slotProgressItemAdded(ProgressItem *item)
{
    if (item->parent()) {
        return;    // we are only interested in top level items
    }

    updateBusyMode(item);
}

void StatusbarProgressWidget::slotProgressItemCompleted(ProgressItem *item)
{
    if (item->parent()) {
        item->deleteLater();
Laurent Montel's avatar
Laurent Montel committed
149
        item = nullptr;
150
151
152
        return; // we are only interested in top level items
    }
    item->deleteLater();
Laurent Montel's avatar
Laurent Montel committed
153
    item = nullptr;
154
155
156
157
158
159
    connectSingleItem(); // if going back to 1 item
    if (ProgressManager::instance()->isEmpty()) {   // No item
        // Done. In 5s the progress-widget will close, then we can clean up the statusbar
        mCleanTimer->start(5000);
    } else if (mCurrentItem) {   // Exactly one item
        delete mBusyTimer;
Laurent Montel's avatar
Laurent Montel committed
160
        mBusyTimer = nullptr;
161
162
163
164
165
166
167
168
169
        activateSingleItemMode();
    }
}

void StatusbarProgressWidget::connectSingleItem()
{
    if (mCurrentItem) {
        disconnect(mCurrentItem, &ProgressItem::progressItemProgress,
                   this, &StatusbarProgressWidget::slotProgressItemProgress);
Laurent Montel's avatar
Laurent Montel committed
170
        mCurrentItem = nullptr;
171
172
173
174
175
176
177
178
179
180
    }
    mCurrentItem = ProgressManager::instance()->singleItem();
    if (mCurrentItem) {
        connect(mCurrentItem, &ProgressItem::progressItemProgress,
                this, &StatusbarProgressWidget::slotProgressItemProgress);
    }
}

void StatusbarProgressWidget::activateSingleItemMode()
{
181
182
183
    mProgressBar->setMaximum(100);
    mProgressBar->setValue(mCurrentItem->progress());
    mProgressBar->setTextVisible(true);
184
185
186
187
188
189
190
191
}

void StatusbarProgressWidget::slotShowItemDelayed()
{
    bool noItems = ProgressManager::instance()->isEmpty();
    if (mCurrentItem) {
        activateSingleItemMode();
    } else if (!noItems) {   // N items
192
193
        mProgressBar->setMaximum(0);
        mProgressBar->setTextVisible(false);
194
195
196
197
198
199
        Q_ASSERT(mBusyTimer);
        if (mBusyTimer) {
            mBusyTimer->start(100);
        }
    }

200
201
    if (!noItems) {
        setMode(Progress);
202
203
204
205
206
    }
}

void StatusbarProgressWidget::slotBusyIndicator()
{
207
208
    const int p = mProgressBar->value();
    mProgressBar->setValue(p + 10);
209
210
211
212
213
214
}

void StatusbarProgressWidget::slotProgressItemProgress(ProgressItem *item, unsigned int value)
{
    Q_ASSERT(item == mCurrentItem);  // the only one we should be connected to
    Q_UNUSED(item);
215
    mProgressBar->setValue(value);
216
217
}

218
void StatusbarProgressWidget::setMode(Mode mode)
219
{
Laurent Montel's avatar
Laurent Montel committed
220
    if (mMode == mode) {
221
        return;
Laurent Montel's avatar
Laurent Montel committed
222
    }
223
    mMode = mode;
224
    switch (mMode) {
225
    case Clean:
226
        if (mShowButton) {
David Faure's avatar
David Faure committed
227
            mButton->hide();
228
        }
229
        mSslLabel->setState(SSLLabel::Done);
230
        // show the empty label in order to make the status bar look better
231
232
        mStackedWidget->show();
        mStackedWidget->setCurrentWidget(mLabel);
233
234
        break;
    case Progress:
235
236
237
        mStackedWidget->show();
        mStackedWidget->setCurrentWidget(mProgressBar);
        if (mShowButton) {
Laurent Montel's avatar
Laurent Montel committed
238
            mShowDetailedProgress = mProgressDialog->wasLastShown();
239
            updateProgressButton(mShowDetailedProgress);
240
            mButton->show();
241
        }
242
        mSslLabel->setState(mSslLabel->lastState());
243
244
245
246
247
248
249
250
        break;
    }
}

void StatusbarProgressWidget::slotClean()
{
    // check if a new item showed up since we started the timer. If not, clear
    if (ProgressManager::instance()->isEmpty()) {
251
        mProgressBar->setValue(0);
252
        setMode(Clean);
253
254
255
    }
}

Laurent Montel's avatar
Laurent Montel committed
256
bool StatusbarProgressWidget::eventFilter(QObject *obj, QEvent *ev)
257
258
{
    if (ev->type() == QEvent::MouseButtonPress) {
Laurent Montel's avatar
Laurent Montel committed
259
        QMouseEvent *e = static_cast<QMouseEvent *>(ev);
260

261
        if (e->button() == Qt::LeftButton && mMode == Progress) {      // toggle view on left mouse button
262
263
264
265
266
267
            // Consensus seems to be that we should show/hide the fancy dialog when the user
            // clicks anywhere in the small one.
            slotProgressButtonClicked();
            return true;
        }
    }
Laurent Montel's avatar
Laurent Montel committed
268
    return QFrame::eventFilter(obj, ev);
269
270
}

271
void StatusbarProgressWidget::updateProgressButton(bool showingProgress)
272
{
273
    if (!showingProgress) {
274
275
        mButton->setIcon(QIcon::fromTheme(QStringLiteral("go-up")));
        mButton->setToolTip(i18n("Show detailed progress window"));
276
    } else {
277
278
        mButton->setIcon(QIcon::fromTheme(QStringLiteral("go-down")));
        mButton->setToolTip(i18n("Hide detailed progress window"));
279
    }
Laurent Montel's avatar
Laurent Montel committed
280
281
282
283
}

void StatusbarProgressWidget::slotProgressButtonClicked()
{
284
    mProgressDialog->slotToggleVisibility();
285
    mShowDetailedProgress = !mProgressDialog->isHidden();
Laurent Montel's avatar
Laurent Montel committed
286
    setFixedWidth(qMax(600, mProgressDialog->width()));
287
288
289
290
}

void StatusbarProgressWidget::slotProgressDialogVisible(bool b)
{
291
292
    // Show the hide/show button (mButton) as soon as the progress dialog is shown
    // (StatusbarProgressWidget::slotShowItemDelayed happens later)
293
294
    if (b) {
        setMode(Progress);
295
    }
296
297

    updateProgressButton(b);
298
}