kwave  18.07.70
PlayBackDialog.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  PlayBackDialog.cpp - dialog for configuring the playback
3  -------------------
4  begin : Sun May 13 2001
5  copyright : (C) 2001 by Thomas Eschenbacher
6  email : 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 #include <stdio.h>
20 #include <stdlib.h>
21 
22 #include <QApplication>
23 #include <QIcon>
24 #include <QLabel>
25 #include <QLatin1Char>
26 #include <QPointer>
27 #include <QPushButton>
28 #include <QSlider>
29 #include <QSpinBox>
30 #include <QStringList>
31 #include <QTreeWidget>
32 #include <QTreeWidgetItem>
33 
34 #include <KComboBox>
35 #include <KConfig>
36 #include <KConfigGroup>
37 #include <KHelpClient>
38 #include <KLocalizedString>
39 #include <KIconLoader>
40 #include <KIconTheme>
41 #include <KSharedConfig>
42 
46 #include "libkwave/Plugin.h"
47 #include "libkwave/String.h"
48 
49 #include "libgui/FileDialog.h"
50 
51 #include "PlayBackDialog.h"
52 #include "PlayBackPlugin.h"
53 
54 //***************************************************************************
56  Kwave::Plugin &p,
57  Kwave::PlaybackController &playback_controller,
58  const Kwave::PlayBackParam &params
59 )
60  :QDialog(p.parentWidget()), PlayBackDlg(),
61  m_playback_controller(playback_controller),
62  m_device(Q_NULLPTR),
63  m_playback_params(params),
64  m_methods_map(),
65  m_file_filter(_("")),
66  m_devices_list_map(),
67  m_enable_setDevice(true)
68 {
69  setupUi(this);
70  setModal(true);
71 
72  // fill the combo box with playback methods
73  unsigned int index=0;
74  for (index = 0; index < m_methods_map.count(); index++) {
75  cbMethod->addItem(
76  m_methods_map.description(index, true),
77  static_cast<int>(m_methods_map.data(index)) );
78  }
79  cbMethod->setEnabled(cbMethod->count() > 1);
80 
81  connect(cbMethod, SIGNAL(activated(int)),
82  SLOT(methodSelected(int)));
83  connect(cbDevice, SIGNAL(editTextChanged(QString)),
84  SLOT(setDevice(QString)));
85  connect(cbDevice, SIGNAL(activated(QString)),
86  SLOT(setDevice(QString)));
87  connect(cbBitsPerSample, SIGNAL(editTextChanged(QString)),
88  SLOT(bitsPerSampleSelected(QString)));
89  connect(cbBitsPerSample, SIGNAL(activated(QString)),
90  SLOT(bitsPerSampleSelected(QString)));
91  connect(sbChannels, SIGNAL(valueChanged(int)),
92  SLOT(setChannels(int)));
93  connect(slBufferSize, SIGNAL(valueChanged(int)),
94  SLOT(setBufferSize(int)));
95  connect(btSelectDevice, SIGNAL(clicked()),
96  SLOT(selectPlaybackDevice()));
97 
98  connect(listDevices,
99  SIGNAL(currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)),
100  SLOT(listEntrySelected(QTreeWidgetItem*,QTreeWidgetItem*)));
101  connect(listDevices, SIGNAL(itemExpanded(QTreeWidgetItem*)),
102  SLOT(listItemExpanded(QTreeWidgetItem*)));
103  connect(listDevices, SIGNAL(focusLost()),
104  SLOT(updateListSelection()));
105 
106  connect(btTest, SIGNAL(clicked()),
107  SIGNAL(sigTestPlayback()));
108  connect(btHelp->button(QDialogButtonBox::Help), SIGNAL(clicked()),
109  this, SLOT(invokeHelp()));
110 
111  // remove the header of the tree view
112  listDevices->headerItem()->setHidden(true);
113 
114  // fix the dialog size
115  setFixedHeight(sizeHint().height());
116 
117  // update the GUI elements
118  // order is: Method -> Device -> "Select..."-button
119  // -> Channels -> Bits per Sample
120  setMethod(params.method);
121  setDevice(params.device);
123  setChannels(params.channels);
124 
125  // buffer size is independent
126  setBufferSize(params.bufbase);
127 
128  // set the focus onto the "OK" button
129  buttonBox->button(QDialogButtonBox::Ok)->setFocus();
130 }
131 
132 //***************************************************************************
134 {
135 }
136 
137 //***************************************************************************
139 {
141 
142  m_playback_params.method = method;
143 
144  // update the selection in the combo box if necessary
145  int index = cbMethod->findData(static_cast<int>(method));
146  if (cbMethod->currentIndex() != index) {
147  cbMethod->setCurrentIndex(index);
148  return; // we will get called again, through "methodSelected(...)"
149  }
150 
151  qDebug("PlayBackDialog::setMethod('%s' [%d])",
153  static_cast<int>(method) );
154 
155  // set hourglass cursor
156  QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
157 
158  // change the playback method (class PlayBackDevice)
159  if (m_device) delete m_device;
160  m_device = Q_NULLPTR;
161 
162  // remember the device selection, just for the GUI
163  // for the next time this method gets selected
164  // change in method -> save the current device and use
165  // the previous one
166  QString device = _("");
167  QString section = _("plugin playback");
168  KConfigGroup cfg = KSharedConfig::openConfig()->group(section);
169 
170  // save the current device
171  cfg.writeEntry(
172  QString(_("last_device_%1")).arg(static_cast<int>(old_method)),
174  qDebug("SAVE: '%s' (%d) -> '%s'",
176  static_cast<int>(old_method),
177  DBG(m_playback_params.device.split(_("|")).at(0)));
178  cfg.sync();
179 
180  // NOTE: the "method" may get modified here if not supported!
182  if (method != m_playback_params.method) {
183  // method has been modified to some fallback -> start through
184  qDebug(" method has changed: %d -> %d",
185  static_cast<int>(m_playback_params.method),
186  static_cast<int>(method));
187  setMethod(method); // -> recursion
188 
189  // remove hourglass
190  QApplication::restoreOverrideCursor();
191 
192  return;
193  }
194 
195  // if we found no playback method
196  if (method == Kwave::PLAYBACK_INVALID) {
197  qWarning("found no valid playback method");
198  }
199 
200  // create a new playback device (will fail if method is unsupported)
202  if (!m_device) {
203  // oops, something has failed :-(
204  setSupportedDevices(QStringList());
205  setDevice(QString());
206 
207  // remove hourglass
208  QApplication::restoreOverrideCursor();
209 
210  return;
211  }
212 
213  // restore the previous settings of the new method
214  device = cfg.readEntry(
215  _("last_device_%1").arg(static_cast<int>(method)));
216  qDebug("RESTORE: '%s' (%d) -> '%s'",
218  static_cast<int>(method),
219  DBG(device.split(_("|")).at(0)));
220 
221  m_playback_params.device = device;
222 
223  // set list of supported devices
225 
226  // set current device, no matter if supported or not,
227  // the dialog will take care of this.
229 
230  // check the filter for the "select..." dialog. If it is
231  // empty, the "select" dialog will be disabled
233 
234  // remove hourglass
235  QApplication::restoreOverrideCursor();
236 }
237 
238 //***************************************************************************
240 {
242  cbMethod->itemData(index).toInt());
243 
244  qDebug("PlayBackDialog::methodSelected(%d) -> %s [%d]", index,
246  static_cast<int>(method) );
247 
248  if (method <= Kwave::PLAYBACK_NONE) return;
249  if (method >= Kwave::PLAYBACK_INVALID) return;
250 
251  setMethod(method);
252 }
253 
254 //***************************************************************************
256 {
257  Q_ASSERT(cbDevice);
258  Q_ASSERT(listDevices);
259  if (!cbDevice || !listDevices) return;
260  QString current_device = m_playback_params.device;
261 
262  // disable all that noisy stuff that comes from modifying the
263  // device controls...
264  m_enable_setDevice = false;
265 
266 // qDebug("PlayBackDialog::setSupportedDevices():");
267 // foreach (const QString &d, devices)
268 // qDebug(" '%s'", DBG(d));
269 
270  cbDevice->clearEditText();
271  cbDevice->clear();
272  listDevices->clear();
273 
274  if (devices.contains(_("#EDIT#"))) {
275  devices.removeAll(_("#EDIT#"));
276  cbDevice->setEditable(true);
277  } else {
278  cbDevice->setEditable(false);
279  }
280 
281  if (devices.contains(_("#SELECT#"))) {
282  devices.removeAll(_("#SELECT#"));
283  btSelectDevice->setEnabled(true);
284  btSelectDevice->show();
285  } else {
286  btSelectDevice->setEnabled(false);
287  btSelectDevice->hide();
288  }
289 
290  if (devices.contains(_("#TREE#"))) {
291  // treeview mode
292  KIconLoader *icon_loader = KIconLoader::global();
293 
294  devices.removeAll((_("#TREE#")));
295  listDevices->setEnabled(true);
296  cbDevice->setEnabled(false);
297  cbDevice->hide();
298  m_devices_list_map.clear();
299 
300  // build a tree with all nodes in the list
301  foreach (const QString &dev_id, devices) {
302  QTreeWidgetItem *parent = Q_NULLPTR;
303 
304  QStringList list = dev_id.split(_("||"), QString::KeepEmptyParts);
305  foreach (const QString &t, list) {
306  QString token(t);
307  QTreeWidgetItem *item = Q_NULLPTR;
308 
309  // split the icon name from the token
310  QString icon_name;
311  int pos = token.indexOf(QLatin1Char('|'));
312  if (pos > 0) {
313  icon_name = token.mid(pos+1);
314  token = token.left(pos);
315  }
316 
317  // find the first item with the same text
318  // and the same root
319  if (parent) {
320  for (int i = 0; i < parent->childCount(); i++) {
321  QTreeWidgetItem *node = parent->child(i);
322  if (node && node->text(0) == token) {
323  item = node;
324  break;
325  }
326  }
327  } else {
328  QList<QTreeWidgetItem *> matches =
329  listDevices->findItems(token, Qt::MatchExactly);
330  if (matches.count())
331  item = matches.takeFirst();
332  }
333 
334  if (item) {
335  // already in the list
336  parent = item;
337  } else if (parent) {
338  // new leaf, add to the parent
339  item = new QTreeWidgetItem(parent);
340  Q_ASSERT(item);
341  if (item) {
342  item->setText(0, token);
343  m_devices_list_map.insert(item, dev_id);
344  }
345 
346  parent->setExpanded(true);
347  parent->setFlags(parent->flags() &
348  ~(Qt::ItemIsUserCheckable | Qt::ItemIsSelectable));
349  if (m_devices_list_map.contains(parent)) {
350  // make the parent not selectable
351  m_devices_list_map.remove(parent);
352  }
353  } else {
354  // new root node
355  item = new QTreeWidgetItem(listDevices);
356  Q_ASSERT(item);
357  if (item) {
358  item->setText(0, token);
359  m_devices_list_map.insert(item, dev_id);
360  }
361  }
362 
363  if (item && icon_name.length() && icon_loader) {
364  QIcon icon = icon_loader->loadIcon(
365  icon_name, KIconLoader::User);
366  item->setIcon(0, icon);
367  }
368 
369  // use the current item as parent for the next pass
370  parent = item;
371  }
372  }
373  } else {
374  // combo box mode
375  cbDevice->addItems(devices);
376  cbDevice->show();
377  listDevices->setEnabled(false);
378 
379  if (devices.contains(current_device)) {
380  // current device is in the list
381  cbDevice->setCurrentIndex(cbDevice->findText(current_device));
382  } else {
383  if (cbDevice->isEditable() && current_device.length()) {
384  // user defined device name
385  cbDevice->setEditText(current_device);
386  } else if (devices.count()) {
387  // one or more other possibilities -> take the first one
388  cbDevice->setCurrentIndex(0);
389  } else {
390  // empty list of possibilities
391  cbDevice->clearEditText();
392  cbDevice->clear();
393  }
394  }
395  cbDevice->setEnabled(devices.count() > 1);
396  }
397 
398  // enable changes in the device controls again
399  m_enable_setDevice = true;
400 }
401 
402 //***************************************************************************
403 void Kwave::PlayBackDialog::listEntrySelected(QTreeWidgetItem *current,
404  QTreeWidgetItem *previous)
405 {
406  Q_ASSERT(listDevices);
407  Q_UNUSED(previous);
408  if (!current || !listDevices) return;
409 
410  if (m_devices_list_map.contains(current))
411  setDevice(m_devices_list_map[current]);
412 }
413 
414 //***************************************************************************
415 void Kwave::PlayBackDialog::listItemExpanded(QTreeWidgetItem *item)
416 {
417  Q_UNUSED(item);
419 }
420 
421 //***************************************************************************
423 {
424  // set the current device again, otherwise nothing will be selected
426 }
427 
428 //***************************************************************************
429 void Kwave::PlayBackDialog::setDevice(const QString &device)
430 {
431  Q_ASSERT(cbDevice);
432  Q_ASSERT(listDevices);
433  if (!cbDevice || !listDevices) return;
434 
435  if (!m_enable_setDevice) return;
436 
437  qDebug("PlayBackDialog::setDevice(): '%s' -> '%s'",
438  DBG(m_playback_params.device.split(_("|")).at(0)),
439  DBG(device.split(_("|")).at(0)));
440 
441  if (listDevices->isEnabled()) {
442  // treeview mode
443  QTreeWidgetItem *node = m_devices_list_map.key(device, Q_NULLPTR);
444  if (node) {
445  node->setSelected(true);
446  listDevices->scrollToItem(node);
447  listDevices->setCurrentItem(node);
448  }
449  } else if (cbDevice->isEditable() && device.length()) {
450  // user defined device name
451  if (!device.isEmpty() && (cbDevice->currentText() != device)) {
452  cbDevice->setCurrentIndex(cbDevice->findText(device));
453  cbDevice->setEditText(device);
454  }
455  } else {
456  // just take one from the list
457  if (cbDevice->findText(device) >= 0) {
458  cbDevice->setCurrentIndex(cbDevice->findText(device));
459  } else if (cbDevice->count()) {
460  cbDevice->setCurrentIndex(0);
461  }
462  }
463 
464  // select the default device if new one is not supported
465  QString dev = device;
466  if (m_device) {
467  QStringList supported = m_device->supportedDevices();
468  supported.removeAll(_("#EDIT#"));
469  supported.removeAll(_("#SELECT#"));
470  supported.removeAll(_("#TREE#"));
471  if (!supported.isEmpty() && !supported.contains(device)) {
472  // use the first entry as default
473  dev = supported.first();
474  qDebug("PlayBackPlugin::setDevice(%s) -> fallback to '%s'",
475  DBG(device.split(_("|")).at(0)),
476  DBG(dev.split(_("|")).at(0)));
477  }
478  }
479 
480  // take over the device, please note that this one might differ from
481  // the device we got as parameter, maybe it is a fallback
483 
484  QList<unsigned int> supported_bits;
485  if (m_device) supported_bits = m_device->supportedBits(dev);
486  setSupportedBits(supported_bits);
487 
488  unsigned int min = 0;
489  unsigned int max = 0;
490  if (m_device) m_device->detectChannels(dev, min, max);
491  setSupportedChannels(min, max);
492 }
493 
494 //***************************************************************************
496 {
497  Q_ASSERT(slBufferSize);
498  Q_ASSERT(txtBufferSize);
499  if (!slBufferSize || !txtBufferSize) return;
500 
501  if (exp < 8) exp = 8;
502  if (exp > 18) exp = 18;
503 
504  // update the slider if necessary
505  if (slBufferSize->value() != exp) slBufferSize->setValue(exp);
506 
507  // take the value into our struct
509 
510  // update the text
511  unsigned int buffer_size = (1 << exp);
512  QString text;
513  if (buffer_size < 1024) {
514  text = i18n("%1 Bytes", buffer_size);
515  } else {
516  text = i18n("%1 kB", buffer_size >> 10);
517  }
518  txtBufferSize->setText(text);
519 }
520 
521 //***************************************************************************
522 void Kwave::PlayBackDialog::setSupportedBits(const QList<unsigned int> &bits)
523 {
524  Q_ASSERT(cbBitsPerSample);
525  if (!cbBitsPerSample) return;
526 
527  int current_bits = m_playback_params.bits_per_sample;
528  cbBitsPerSample->clear();
529  QString txt;
530  foreach (unsigned int b, bits) {
531  txt.setNum(b);
532  cbBitsPerSample->addItem(txt);
533  }
534 
535  // if possibilities are "unknown" -> use last known setting
536  if (!bits.count()) {
537  txt.setNum(current_bits);
538  cbBitsPerSample->addItem(txt);
539  }
540 
541  if (!bits.contains(current_bits) && bits.count())
542  current_bits = bits.last();
543 
544  setBitsPerSample(current_bits);
545  cbBitsPerSample->setEnabled(bits.count() > 0);
546 }
547 
548 //***************************************************************************
550 {
551  bool ok = false;
552  unsigned int bits = text.toUInt(&ok);
553  if (!ok) bits = 0;
554 
555  setBitsPerSample(bits);
556 }
557 
558 //***************************************************************************
560 {
561  Q_ASSERT(cbBitsPerSample);
562  if (!cbBitsPerSample) return;
563 
564  qDebug("PlayBackDialog::setBitsPerSample(): %u -> %u",
566 
567  QString txt;
568  txt.setNum(bits);
569  if (cbBitsPerSample->findText(txt) >= 0) {
570  cbBitsPerSample->setCurrentIndex(cbBitsPerSample->findText(txt));
572  }
573 }
574 
575 //***************************************************************************
576 void Kwave::PlayBackDialog::setSupportedChannels(unsigned int min, unsigned int max)
577 {
578  Q_ASSERT(sbChannels);
579  if (!sbChannels) return;
580 
581  int current_channels = m_playback_params.channels;
582 
583  // if possibilities are "unknown" -> use last known setting
584  if (!min && !max && current_channels)
585  min = max = current_channels;
586 
587  sbChannels->setMinimum(min);
588  sbChannels->setMaximum(max);
589  setChannels(current_channels);
590  sbChannels->setEnabled(min != max);
591 }
592 
593 //***************************************************************************
595 {
596  Q_ASSERT(sbChannels);
597  if (!sbChannels) return;
598 
599  if ((sbChannels->value() != channels) &&
600  (sbChannels->minimum() != sbChannels->maximum()) &&
601  (sbChannels->maximum() > 0))
602  {
603  sbChannels->setValue(channels);
604  channels = sbChannels->value();
605  }
606 
607  qDebug("PlayBackDialog::setChannels(): %d -> %d",
608  m_playback_params.channels, channels);
609  m_playback_params.channels = channels;
610 
611  QString txt;
612  switch (channels) {
613  case 1: txt = i18n("(mono)"); break;
614  case 2: txt = i18n("(stereo)"); break;
615  case 4: txt = i18n("(quadro)"); break;
616  default: txt = _("");
617  }
618  lblChannels->setText(txt);
619 }
620 
621 //***************************************************************************
623 {
624  return m_playback_params;
625 }
626 
627 //***************************************************************************
628 void Kwave::PlayBackDialog::setFileFilter(const QString &filter)
629 {
630  m_file_filter = filter;
631  if (btSelectDevice) btSelectDevice->setEnabled(m_file_filter.length());
632 }
633 
634 //***************************************************************************
636 {
637  QString filter = m_file_filter;
638 
639  QPointer<Kwave::FileDialog> dlg = new(std::nothrow) Kwave::FileDialog(
640  _("kfiledialog:///kwave_playback_device"),
641  Kwave::FileDialog::OpenFile, filter, this,
642  QUrl(_("file:/dev"))
643  );
644  if (!dlg) return;
645  dlg->setWindowTitle(i18n("Select Playback Device"));
646  if (!m_playback_params.device.startsWith(_("#")))
647  dlg->selectUrl(QUrl(_("file:") + m_playback_params.device));
648  else
649  dlg->selectUrl(QUrl(_("file:/dev/*")));
650  if (dlg->exec() == QDialog::Accepted) {
651  QString new_device = dlg->selectedUrl().fileName();
652  // selected new device
653  if (cbDevice) cbDevice->setEditText(new_device);
654  }
655  delete dlg;
656 }
657 
658 //***************************************************************************
660 {
661  KHelpClient::invokeHelp(_("playback"));
662 }
663 
664 //***************************************************************************
665 //***************************************************************************
unsigned int count() const
Definition: TypesMap.h:81
void setMethod(Kwave::playback_method_t method)
virtual int detectChannels(const QString &device, unsigned int &min, unsigned int &max)
PlayBackDialog(Kwave::Plugin &p, Kwave::PlaybackController &playback_controller, const Kwave::PlayBackParam &params)
unsigned int channels
Definition: PlayBackParam.h:66
playback_method_t
Definition: PlayBackParam.h:31
QString description(IDX type, bool localized) const
Definition: TypesMap.h:128
void selectUrl(const QUrl &url)
Definition: FileDialog.cpp:271
DATA data(IDX type) const
Definition: TypesMap.h:110
virtual QString fileFilter()
unsigned int bits_per_sample
Definition: PlayBackParam.h:69
void listItemExpanded(QTreeWidgetItem *item)
void checkMethod(Kwave::playback_method_t &method)
void methodSelected(int index)
unsigned int bufbase
Definition: PlayBackParam.h:75
bool connect(Kwave::StreamObject &source, const char *output, Kwave::StreamObject &sink, const char *input)
Definition: Connect.cpp:48
void setDevice(const QString &device)
QMap< QTreeWidgetItem *, QString > m_devices_list_map
Kwave::PlayBackDevice * m_device
Kwave::PlayBackTypesMap m_methods_map
virtual QStringList supportedDevices()
void setSupportedDevices(QStringList devices)
void listEntrySelected(QTreeWidgetItem *current, QTreeWidgetItem *previous)
void setSupportedChannels(unsigned int min, unsigned int max)
virtual QList< unsigned int > supportedBits(const QString &device)=0
void setBitsPerSample(unsigned int bits)
void setSupportedBits(const QList< unsigned int > &bits)
void bitsPerSampleSelected(const QString &text)
void setChannels(int channels)
void setFileFilter(const QString &filter)
#define _(m)
Definition: memcpy.c:66
QString name(IDX type) const
Definition: TypesMap.h:117
Kwave::playback_method_t method
Definition: PlayBackParam.h:78
#define DBG(qs)
Definition: String.h:55
Kwave::PlayBackParam m_playback_params
virtual Kwave::PlayBackDevice * createDevice(Kwave::playback_method_t method)
Kwave::PlaybackController & m_playback_controller
void setBufferSize(int exp)
IDX findFromData(const DATA &data) const
Definition: TypesMap.h:89
const Kwave::PlayBackParam & params()