kwave  18.07.70
FileProgress.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  FileProgress.cpp - progress window for loading/saving files
3  -------------------
4  begin : Mar 11 2001
5  copyright : (C) 2001 by Thomas Eschenbacher
6  email : Thomas Eschenbacher <thomas.eschenbacher@gmx.de>
7  ***************************************************************************/
8 
9 /***************************************************************************
10  * *
11  * This program is free software; you can redistribute it and/or modify *
12  * it under the terms of the GNU General Public License as published by *
13  * the Free Software Foundation; either version 2 of the License, or *
14  * (at your option) any later version. *
15  * *
16  ***************************************************************************/
17 
18 #include "config.h"
19 
20 #include <math.h>
21 
22 #include <QApplication>
23 #include <QCloseEvent>
24 #include <QGridLayout>
25 #include <QLabel>
26 #include <QLocale>
27 #include <QProgressBar>
28 #include <QPushButton>
29 #include <QThread>
30 #include <QTimer>
31 #include <QVBoxLayout>
32 #include <QtGlobal>
33 
34 #include <KFormat>
35 #include <KLocalizedString>
36 #include <KStandardGuiItem>
37 
38 #include "libkwave/FileProgress.h"
39 #include "libkwave/MessageBox.h"
40 #include "libkwave/String.h"
41 #include "libkwave/Utils.h"
42 
43 //***************************************************************************
45  const QUrl &url, quint64 size,
46  sample_index_t samples, double rate, unsigned int bits,
47  unsigned int tracks)
48  :QDialog(parent),
49  m_url(url),
50  m_size(size),
51  m_lbl_url(Q_NULLPTR),
52  m_lbl_length(Q_NULLPTR),
53  m_progress(Q_NULLPTR),
54  m_stat_transfer(Q_NULLPTR),
55  m_stat_bytes(Q_NULLPTR),
56  m_time(),
57  m_canceled(true),
58  m_last_percent(0),
59  m_bits_per_sample(bits),
60  m_sample_rate(rate),
61  m_tracks(tracks)
62 {
63  setModal(true);
64 
65  QString text;
66 
67  // start the timer now
68  m_time.start();
69 
70  // set the caption to the url
71  setWindowTitle(m_url.toString());
72 
73  // toplevel uses a vbox layout
74  QVBoxLayout *top_layout = new QVBoxLayout(this);
75  Q_ASSERT(top_layout);
76  if (!top_layout) return;
77  top_layout->setMargin(10);
78  top_layout->setSpacing(10);
79 
80  // sublayout for the lines with the file info
81  QGridLayout *info_layout = new QGridLayout();
82  Q_ASSERT(info_layout);
83  if (!info_layout) return;
84  info_layout->setSpacing(0);
85  info_layout->setColumnStretch(0, 0);
86  info_layout->setColumnStretch(1, 100);
87  top_layout->addLayout(info_layout);
88 
89  // label with "file"
90  if (!addInfoLabel(info_layout,
91  i18nc("file progress dialog", "File: "), 0, 0)) return;
92  text = _("?");
93  m_lbl_url = addInfoLabel(info_layout, text, 0, 1);
94  if (!m_lbl_url) return;
95 
96  // label with "length"
97  if (!addInfoLabel(info_layout,
98  i18nc("file progress dialog", "Length: "), 1, 0)) return;
99 
100  m_lbl_length = addInfoLabel(info_layout, _(""), 1, 1);
101  if (!m_lbl_length) return;
102  setLength(quint64(samples) * quint64(tracks));
103 
104  // label with "rate:"
105  if (!addInfoLabel(info_layout,
106  i18nc("file progress dialog", "Sample Rate: "), 2, 0)) return;
107  text = i18nc("file progress dialog, %1=number of samples per second",
108  "%1 Samples per second", rate);
109  if (!addInfoLabel(info_layout, text, 2, 1)) return;
110 
111  // label with "resolution:"
112  if (!addInfoLabel(info_layout,
113  i18nc("file progress dialog", "Resolution: "), 3, 0)) return;
114  text = i18nc("file progress dialog, "
115  "%1=number of bits per sample (8, 16, 24...)",
116  "%1 Bits per sample", bits);
117  if (!addInfoLabel(info_layout, text, 3, 1)) return;
118 
119  // label with "tracks:"
120  if (!addInfoLabel(info_layout,
121  i18nc("file progress dialog", "Tracks: "), 4, 0)) return;
122  switch (tracks) {
123  case 1:
124  text = i18nc("number of tracks", "1 (mono)");
125  break;
126  case 2:
127  text = i18nc("number of tracks", "2 (stereo)");
128  break;
129  case 4:
130  text = i18nc("number of tracks", "4 (quadro)");
131  break;
132  default:
133  text = text.setNum(tracks);
134  }
135  if (!addInfoLabel(info_layout, text, 4, 1)) return;
136 
137  // progress bar
138  m_progress = new QProgressBar(this);
139  Q_ASSERT(m_progress);
140  if (!m_progress) return;
141  top_layout->addWidget(m_progress, 0);
142  m_progress->setMinimum(0);
143  m_progress->setMaximum(100);
144 
145  // sublayout for the line with estimated time
146  QGridLayout *status_layout = new QGridLayout();
147  Q_ASSERT(status_layout);
148  if (!status_layout) return;
149  status_layout->setSpacing(1);
150  status_layout->setColumnMinimumWidth(1, 20);
151  top_layout->addLayout(status_layout);
152 
153  // create the statistic labels
154  m_stat_transfer = addInfoLabel(status_layout, _("-"), 1, 0);
155  if (!m_stat_transfer) return;
156  m_stat_bytes = addInfoLabel(status_layout, _("-"), 1, 2);
157  if (!m_stat_bytes) return;
158 
159  // some dummy update to get maximum size
160  updateStatistics(9999999.9, static_cast<double>(99*24*60*60), size);
161 
162  // now correct the minimum sizes of the statistic entries
163  m_stat_transfer->adjustSize();
164  m_stat_transfer->setMinimumWidth(m_stat_transfer->sizeHint().width());
165  m_stat_bytes->adjustSize();
166  m_stat_bytes->setMinimumWidth(m_stat_bytes->sizeHint().width());
167 
168  // right lower edge: the "cancel" button
169  QPushButton *bt_cancel = new QPushButton(this);
170  Q_ASSERT(bt_cancel);
171  if (!bt_cancel) return;
172  KGuiItem::assign(bt_cancel, KStandardGuiItem::cancel());
173  bt_cancel->setFixedSize(bt_cancel->sizeHint());
174  bt_cancel->setFocus();
175  bt_cancel->setShortcut(Qt::Key_Escape);
176  connect(bt_cancel, SIGNAL(clicked()), this, SLOT(cancel()));
177  top_layout->addWidget(bt_cancel, 0, Qt::AlignRight);
178 
179  // activate the layout and show the dialog
180  top_layout->activate();
181  setFixedHeight(sizeHint().height());
182  setMinimumWidth((sizeHint().width() * 110) / 100);
183 
184  show();
185  fitUrlLabel();
186 
187  // now set canceled to false, this dialog is ready for use
188  m_canceled = false;
189 }
190 
191 //***************************************************************************
193 {
194  fitUrlLabel();
195 }
196 
197 //***************************************************************************
199 {
200  Q_ASSERT(e);
201  if (!e) return;
202 
203  // if not already cancelled -> ask user to confirm
204  if (!m_canceled) {
206  i18n("Do you really want to abort the operation?")) !=
207  KMessageBox::Yes)
208  {
209  // the user was wise and said "No"
210  e->ignore();
211  return;
212  } else {
213  // user pressed "Yes", he should know what he has done
214  m_canceled = true;
215  }
216  }
217 
218  // default action: accept
219  e->accept();
220 }
221 
222 //***************************************************************************
224 {
225  if (!m_lbl_url) return;
226 
227  int width = m_lbl_url->frameRect().width();
228  QString url = m_url.toString();
229  m_lbl_url->setText(url);
230  int todel = 4;
231  while (m_lbl_url->sizeHint().width() > width) {
232  // delete characters in the middle of the string
233  url = m_url.toString();
234  int len = url.length();
235  if (len <= todel) break;
236  url = url.left((len-todel)/2) + _("...") +
237  url.right((len-todel)/2 + 4);
238  m_lbl_url->setText(url);
239  todel++;
240  }
241 
242  m_lbl_url->adjustSize();
243 }
244 
245 //***************************************************************************
246 QLabel *Kwave::FileProgress::addInfoLabel(QGridLayout *layout,
247  const QString text, int row, int col)
248 {
249  QLabel *label = new QLabel(this);
250  Q_ASSERT(label);
251  if (!label) return Q_NULLPTR;
252 
253  label->setText(text);
254  label->adjustSize();
255  label->setFixedHeight(label->sizeHint().height());
256  label->setMinimumWidth(label->sizeHint().width());
257 
258  layout->addWidget(label, row, col);
259 
260  return label;
261 }
262 
263 //***************************************************************************
264 void Kwave::FileProgress::updateStatistics(double rate, double rest,
265  quint64 pos)
266 {
267  QString text;
268  QString num;
269 
270  if (!m_stat_transfer) return;
271  if (!m_stat_bytes) return;
272 
273  // left: transfer rate and estimated time
274  num = num.sprintf("%1.1f", rate / 1024.0);
275 
276  quint64 ms = qMin(quint64(rest * 1000.0), 24ULL * 60ULL * 60ULL * 1000ULL);
277  text = i18nc("file progress dialog, "
278  "%1=transfer rate, %2=remaining duration",
279  "%1 kB/s (%2 remaining)", num,
280  KFormat().formatDecimalDuration(ms));
281  m_stat_transfer->setText(text);
282 
283  // right: statistic over the transferred bytes
284  QString num1, num2;
285  text = i18nc("file progress dialog, "
286  "%1=number of loaded/saved megabytes, "
287  "%2=number of total megabytes to load or save",
288  "%1 MB of %2 MB done",
289  num1.sprintf("%1.1f", pos / (1024.0 * 1024.0)),
290  num2.sprintf("%1.1f", m_size / (1024.0 * 1024.0)));
291  m_stat_bytes->setText(text);
292 
293  // process events for some short time, otherwise we would
294  // not have GUI updates and the "cancel" button would not work
295  // check: this must be called from the GUI thread only!
296  Q_ASSERT(this->thread() == QThread::currentThread());
297  Q_ASSERT(this->thread() == qApp->thread());
298  QTimer t;
299  t.setSingleShot(true);
300  t.start(5);
301  while (t.isActive()) {
302  qApp->sendPostedEvents();
303  qApp->processEvents();
304  }
305 }
306 
307 //***************************************************************************
308 void Kwave::FileProgress::setValue(qreal percent)
309 {
310  // position is in samples, we need bytes
311  quint64 pos = static_cast<quint64>(percent * qreal(m_size) / 100.0);
312  setBytePosition(pos);
313 }
314 
315 //***************************************************************************
317 {
318  if (!m_progress) return;
319  if (pos > m_size) pos = m_size;
320 
321  // the easiest part: the progress bar and the caption
322  int percent = Kwave::toInt(
323  (static_cast<double>(pos) / static_cast<double>(m_size)) * 100.0);
324 
325  // not enough progress not worth showing ?
326  if (percent <= m_last_percent) return;
327  m_last_percent = percent;
328 
329  if (m_progress->value() != percent) {
330  QString newcap;
331  newcap = i18nc(
332  "%1=Progress in percentage, %2=path to file",
333  "(%1%) %2",
334  percent, m_url.toString()
335  );
336  setWindowTitle(newcap);
337 
338  m_progress->setValue(percent);
339  }
340 
341  // update the transfer statistics
342  double seconds = m_time.elapsed() / 1000.0; // [sec]
343  double rate = pos / seconds; // [bytes/sec]
344  double rest = 0;
345  if (rate > 10) {
346  rest = static_cast<double>(m_size - pos) / rate; // [seconds]
347  }
348  updateStatistics(rate, rest, pos);
349 }
350 
351 //***************************************************************************
352 void Kwave::FileProgress::setLength(quint64 samples)
353 {
354  QString text;
355 
356  // length in samples -> h:m:s
357  if ((m_sample_rate > 0) && m_tracks) {
358  // length in ms
359  text = Kwave::ms2string(
360  1000.0 *
361  qreal(samples / m_tracks) /
362  qreal(m_sample_rate));
363  } else {
364  // fallback if no rate: length in samples
365  text = i18nc("file progress dialog, %1=a number of samples",
366  "%1 samples", samples);
367  }
368  m_lbl_length->setText(text);
369 }
370 
371 //***************************************************************************
373 {
374  close();
375  if (m_canceled) emit canceled();
376 }
377 
378 //***************************************************************************
379 //***************************************************************************
QProgressBar * m_progress
Definition: FileProgress.h:162
void setBytePosition(quint64 pos)
QString Q_DECL_EXPORT ms2string(double ms, int precision=6)
Definition: Utils.cpp:66
QLabel * m_stat_transfer
Definition: FileProgress.h:165
void setLength(quint64 samples)
quint64 sample_index_t
Definition: Sample.h:28
void setValue(qreal percent)
void updateStatistics(double rate, double rest, quint64 pos)
virtual void resizeEvent(QResizeEvent *) Q_DECL_OVERRIDE
bool connect(Kwave::StreamObject &source, const char *output, Kwave::StreamObject &sink, const char *input)
Definition: Connect.cpp:48
int toInt(T x)
Definition: Utils.h:127
virtual void closeEvent(QCloseEvent *e) Q_DECL_OVERRIDE
static int warningYesNo(QWidget *widget, QString message, QString caption=QString(), const QString buttonYes=QString(), const QString buttonNo=QString(), const QString &dontAskAgainName=QString())
Definition: MessageBox.cpp:93
QLabel * addInfoLabel(QGridLayout *layout, const QString text, int row, int column)
#define _(m)
Definition: memcpy.c:66
FileProgress(QWidget *parent, const QUrl &url, quint64 size, sample_index_t samples, double rate, unsigned int bits, unsigned int tracks)
unsigned int m_tracks
Definition: FileProgress.h:186