Qt: Add an event processing timer.
[metze/wireshark/wip.git] / ui / qt / progress_frame.cpp
1 /* progress_frame.cpp
2  *
3  * Wireshark - Network traffic analyzer
4  * By Gerald Combs <gerald@wireshark.org>
5  * Copyright 1998 Gerald Combs
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20  */
21
22 #include "config.h"
23
24 #include "progress_frame.h"
25 #include <ui_progress_frame.h>
26
27 #include "ui/progress_dlg.h"
28
29 #include <QDialogButtonBox>
30 #include <QElapsedTimer>
31 #include <QGraphicsOpacityEffect>
32 #include <QBoxLayout>
33 #include <QPropertyAnimation>
34
35 #include "stock_icon_tool_button.h"
36 #include "wireshark_application.h"
37
38 // To do:
39 // - Use a different icon?
40 // - Add an NSProgressIndicator to the dock icon on OS X.
41 // - Start adding the progress bar to dialogs.
42 // - Don't complain so loudly when the user stops a capture.
43
44 progdlg_t *create_progress_dlg(const gpointer top_level_window, const gchar *, const gchar *,
45                                gboolean terminate_is_stop, gboolean *stop_flag) {
46     ProgressFrame *pf;
47     QWidget *main_window;
48
49     if (!top_level_window) {
50         return NULL;
51     }
52
53     main_window = qobject_cast<QWidget *>((QObject *)top_level_window);
54
55     if (!main_window) {
56         return NULL;
57     }
58
59     pf = main_window->findChild<ProgressFrame *>();
60
61     if (!pf) {
62         return NULL;
63     }
64     return pf->showProgress(true, terminate_is_stop, stop_flag, 0);
65 }
66
67 progdlg_t *
68 delayed_create_progress_dlg(const gpointer top_level_window, const gchar *task_title, const gchar *item_title,
69                             gboolean terminate_is_stop, gboolean *stop_flag,
70                             const GTimeVal *, gfloat progress)
71 {
72     progdlg_t *progress_dialog = create_progress_dlg(top_level_window, task_title, item_title, terminate_is_stop, stop_flag);
73     update_progress_dlg(progress_dialog, progress, item_title);
74     return progress_dialog;
75 }
76
77 /*
78  * Update the progress information of the progress bar box.
79  */
80 static const int app_update_freq_ = 100; // ms
81 void
82 update_progress_dlg(progdlg_t *dlg, gfloat percentage, const gchar *)
83 {
84     if (!dlg || dlg->elapsed_timer->elapsed() < app_update_freq_) return;
85
86     dlg->progress_frame->setValue(percentage * 100);
87
88     /*
89      * Flush out the update and process any input events.
90      */
91     WiresharkApplication::processEvents();
92     dlg->elapsed_timer->restart();
93 }
94
95 /*
96  * Destroy the progress bar.
97  */
98 void
99 destroy_progress_dlg(progdlg_t *dlg)
100 {
101     dlg->progress_frame->hide();
102 }
103
104 ProgressFrame::ProgressFrame(QWidget *parent) :
105     QFrame(parent),
106     ui(new Ui::ProgressFrame)
107   , terminate_is_stop_(false)
108   , stop_flag_(NULL)
109 #if !defined(Q_OS_MAC) || QT_VERSION > QT_VERSION_CHECK(5, 0, 0)
110   , show_timer_(-1)
111   , effect_(NULL)
112   , animation_(NULL)
113 #endif
114 #ifdef QWINTASKBARPROGRESS_H
115   , update_taskbar_(false)
116   , taskbar_progress_(NULL)
117 #endif
118 {
119     ui->setupUi(this);
120
121     progress_dialog_.progress_frame = this;
122     progress_dialog_.elapsed_timer = new QElapsedTimer();
123     progress_dialog_.top_level_window = window();
124
125     ui->progressBar->setStyleSheet(QString(
126             "QProgressBar {"
127             "  max-width: 20em;"
128             "  min-height: 0.5em;"
129             "  max-height: 1em;"
130             "  border-bottom: 0px;"
131             "  border-top: 0px;"
132             "  background: transparent;"
133             "}"));
134
135     ui->stopButton->setStockIcon("x-filter-clear");
136     ui->stopButton->setIconSize(QSize(14, 14));
137     ui->stopButton->setStyleSheet(
138             "QToolButton {"
139             "  border: none;"
140             "  background: transparent;" // Disables platform style on Windows.
141             "  padding: 0px;"
142             "  margin: 0px;"
143             "  min-height: 0.8em;"
144             "  max-height: 1em;"
145             "  min-width: 0.8em;"
146             "  max-width: 1em;"
147             "}"
148             );
149
150 #if !defined(Q_OS_MAC) || QT_VERSION > QT_VERSION_CHECK(5, 0, 0)
151     effect_ = new QGraphicsOpacityEffect(this);
152     animation_ = new QPropertyAnimation(effect_, "opacity", this);
153 #endif
154
155     connect(this, SIGNAL(showRequested(bool,bool,gboolean*)),
156             this, SLOT(show(bool,bool,gboolean*)));
157     hide();
158 }
159
160 ProgressFrame::~ProgressFrame()
161 {
162     delete ui;
163     delete progress_dialog_.elapsed_timer;
164 }
165
166 struct progdlg *ProgressFrame::showProgress(bool animate, bool terminate_is_stop, gboolean *stop_flag, int value)
167 {
168     setMaximumValue(100);
169     ui->progressBar->setValue(value);
170     progress_dialog_.elapsed_timer->start();
171     emit showRequested(animate, terminate_is_stop, stop_flag);
172     return &progress_dialog_;
173 }
174
175 progdlg *ProgressFrame::showBusy(bool animate, bool terminate_is_stop, gboolean *stop_flag)
176 {
177     setMaximumValue(0);
178     emit showRequested(animate, terminate_is_stop, stop_flag);
179     return &progress_dialog_;
180 }
181
182 void ProgressFrame::addToButtonBox(QDialogButtonBox *button_box, QObject *main_window)
183 {
184     // We have a ProgressFrame in the main status bar which is controlled
185     // from the capture file and other parts of the application via
186     // create_progress_dlg and delayed_create_progress_dlg.
187     // Create a new ProgressFrame and pair it with the main instance.
188     ProgressFrame *main_progress_frame = main_window->findChild<ProgressFrame *>();
189     if (!button_box || !main_progress_frame) return;
190
191     QBoxLayout *layout = qobject_cast<QBoxLayout *>(button_box->layout());
192     if (!layout) return;
193
194     ProgressFrame *progress_frame = new ProgressFrame(button_box);
195
196     // Insert ourselves after the first spacer we find, otherwise the
197     // far right of the button box.
198     int idx = layout->count();
199     for (int i = 0; i < layout->count(); i++) {
200         if (layout->itemAt(i)->spacerItem()) {
201             idx = i + 1;
202             break;
203         }
204     }
205     layout->insertWidget(idx, progress_frame);
206
207     int one_em = progress_frame->fontMetrics().height();
208     progress_frame->setMaximumWidth(one_em * 8);
209     connect(main_progress_frame, SIGNAL(showRequested(bool,bool,gboolean*)),
210             progress_frame, SLOT(show(bool,bool,gboolean*)));
211     connect(main_progress_frame, SIGNAL(maximumValueChanged(int)),
212             progress_frame, SLOT(setMaximumValue(int)));
213     connect(main_progress_frame, SIGNAL(valueChanged(int)),
214             progress_frame, SLOT(setValue(int)));
215     connect(main_progress_frame, SIGNAL(setHidden()),
216             progress_frame, SLOT(hide()));
217
218     connect(progress_frame, SIGNAL(stopLoading()),
219             main_progress_frame, SIGNAL(stopLoading()));
220 }
221
222 void ProgressFrame::captureFileClosing()
223 {
224     // Hide any paired ProgressFrames and disconnect from them.
225     emit setHidden();
226     disconnect(SIGNAL(showRequested(bool,bool,gboolean*)));
227     disconnect(SIGNAL(maximumValueChanged(int)));
228     disconnect(SIGNAL(valueChanged(int)));
229
230     connect(this, SIGNAL(showRequested(bool,bool,gboolean*)),
231             this, SLOT(show(bool,bool,gboolean*)));
232 }
233
234 void ProgressFrame::setValue(int value)
235 {
236     ui->progressBar->setValue(value);
237     emit valueChanged(value);
238 }
239
240 #if !defined(Q_OS_MAC) || QT_VERSION > QT_VERSION_CHECK(5, 0, 0)
241 void ProgressFrame::timerEvent(QTimerEvent *event)
242 {
243     if (event->timerId() == show_timer_) {
244         killTimer(show_timer_);
245         show_timer_ = -1;
246
247         this->setGraphicsEffect(effect_);
248
249         animation_->setDuration(750);
250         animation_->setStartValue(0.1);
251         animation_->setEndValue(1.0);
252         animation_->setEasingCurve(QEasingCurve::InOutQuad);
253         animation_->start();
254
255         QFrame::show();
256     }
257 }
258 #endif
259
260 void ProgressFrame::hide()
261 {
262     progress_dialog_.elapsed_timer->invalidate();
263 #if !defined(Q_OS_MAC) || QT_VERSION > QT_VERSION_CHECK(5, 0, 0)
264     show_timer_ = -1;
265 #endif
266     emit setHidden();
267     QFrame::hide();
268 #ifdef QWINTASKBARPROGRESS_H
269     if (taskbar_progress_) {
270         taskbar_progress_->reset();
271         taskbar_progress_->hide();
272     }
273 #endif
274 }
275
276 void ProgressFrame::on_stopButton_clicked()
277 {
278     emit stopLoading();
279 }
280
281 const int show_delay_ = 500; // ms
282 void ProgressFrame::show(bool animate, bool terminate_is_stop, gboolean *stop_flag)
283 {
284     terminate_is_stop_ = terminate_is_stop;
285     stop_flag_ = stop_flag;
286
287     if (stop_flag) {
288         ui->stopButton->show();
289     } else {
290         ui->stopButton->hide();
291     }
292
293 #if !defined(Q_OS_MAC) || QT_VERSION > QT_VERSION_CHECK(5, 0, 0)
294     if (animate) {
295         show_timer_ = startTimer(show_delay_);
296     } else {
297         QFrame::show();
298     }
299 #else
300     Q_UNUSED(animate);
301     QFrame::show();
302 #endif
303
304 #ifdef QWINTASKBARPROGRESS_H
305     // windowHandle() is picky about returning a non-NULL value so we check it
306     // each time.
307     if (update_taskbar_ && !taskbar_progress_ && window()->windowHandle()) {
308         QWinTaskbarButton *taskbar_button = new QWinTaskbarButton(this);
309         if (taskbar_button) {
310             taskbar_button->setWindow(window()->windowHandle());
311             taskbar_progress_ = taskbar_button->progress();
312             connect(this, SIGNAL(valueChanged(int)), taskbar_progress_, SLOT(setValue(int)));
313         }
314     }
315     if (taskbar_progress_) {
316         taskbar_progress_->show();
317         taskbar_progress_->resume();
318     }
319 #endif
320 }
321
322 void ProgressFrame::setMaximumValue(int value)
323 {
324     ui->progressBar->setMaximum(value);
325     emit maximumValueChanged(value);
326 }
327
328 /*
329  * Editor modelines
330  *
331  * Local Variables:
332  * c-basic-offset: 4
333  * tab-width: 8
334  * indent-tabs-mode: nil
335  * End:
336  *
337  * ex: set shiftwidth=4 tabstop=8 expandtab:
338  * :indentSize=4:tabSize=8:noTabs=true:
339  */