kwave  18.07.70
TopWidget.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  TopWidget.cpp - Toplevel widget of Kwave
3  -------------------
4  begin : 1999
5  copyright : (C) 1999 by Martin Wilz
6  email : Martin Wilz <mwilz@ernie.mi.uni-koeln.de>
7 
8  ***************************************************************************/
9 
10 /***************************************************************************
11  * *
12  * This program is free software; you can redistribute it and/or modify *
13  * it under the terms of the GNU General Public License as published by *
14  * the Free Software Foundation; either version 2 of the License, or *
15  * (at your option) any later version. *
16  * *
17  ***************************************************************************/
18 
19 #include "config.h"
20 
21 #include <new>
22 
23 #include <errno.h>
24 #include <math.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <unistd.h>
28 
29 #include <QAction>
30 #include <QApplication>
31 #include <QCloseEvent>
32 #include <QCommandLineParser>
33 #include <QDesktopWidget>
34 #include <QFile>
35 #include <QFrame>
36 #include <QLabel>
37 #include <QLatin1Char>
38 #include <QMap>
39 #include <QMdiArea>
40 #include <QMdiSubWindow>
41 #include <QMenuBar>
42 #include <QMutableMapIterator>
43 #include <QPixmap>
44 #include <QSizePolicy>
45 #include <QStatusBar>
46 #include <QStringList>
47 #include <QtGlobal>
48 
49 #include <KComboBox>
50 #include <KConfig>
51 #include <KConfigGroup>
52 #include <KHelpMenu>
53 #include <KLocalizedString>
54 #include <KMessageBox>
55 #include <KSharedConfig>
56 #include <KToolBar>
57 
58 #include "libkwave/ClipBoard.h"
59 #include "libkwave/CodecManager.h"
60 #include "libkwave/FileDrag.h"
61 #include "libkwave/LabelList.h"
62 #include "libkwave/Logger.h"
63 #include "libkwave/MessageBox.h"
64 #include "libkwave/MetaDataList.h"
65 #include "libkwave/Parser.h"
66 #include "libkwave/Plugin.h" // for some helper functions
67 #include "libkwave/PluginManager.h"
68 #include "libkwave/SignalManager.h"
69 #include "libkwave/String.h"
70 #include "libkwave/Utils.h"
71 
72 #include "libgui/FileDialog.h"
73 #include "libgui/MenuManager.h"
74 
75 #include "App.h"
76 #include "FileContext.h"
77 #include "MainWidget.h"
78 #include "PlayerToolBar.h"
79 #include "Splash.h"
80 #include "TopWidget.h"
81 #include "ZoomToolBar.h"
82 
86 #define CASE_COMMAND(x) } else if (parser.command() == _(x)) {
87 
89 #define TOOLBAR_FILE _("MainWidget File")
90 
92 #define TOOLBAR_EDIT _("MainWidget Edit")
93 
95 #define TOOLBAR_RECORD_PLAY _("MainWidget Record/Playback")
96 
98 #define TOOLBAR_ZOOM _("MainWidget Zoom")
99 
100 //***************************************************************************
101 //***************************************************************************
103  :KMainWindow(),
104  m_application(app),
105  m_context_map(),
106  m_toolbar_record_playback(Q_NULLPTR),
107  m_toolbar_zoom(Q_NULLPTR),
108  m_menu_manager(Q_NULLPTR),
109  m_mdi_area(Q_NULLPTR),
110  m_action_save(Q_NULLPTR),
111  m_action_save_as(Q_NULLPTR),
112  m_action_close(Q_NULLPTR),
113  m_action_undo(Q_NULLPTR),
114  m_action_redo(Q_NULLPTR),
115  m_action_cut(Q_NULLPTR),
116  m_action_copy(Q_NULLPTR),
117  m_action_erase(Q_NULLPTR),
118  m_action_delete(Q_NULLPTR),
119  m_lbl_status_size(Q_NULLPTR),
120  m_lbl_status_mode(Q_NULLPTR),
121  m_lbl_status_cursor(Q_NULLPTR)
122 {
123  // status bar items
124  QStatusBar *status_bar = statusBar();
125  Q_ASSERT(status_bar);
126  if (!status_bar) return;
127 
128  QLabel *spacer = new QLabel(this);
129  const int frame_style = QFrame::StyledPanel | QFrame::Sunken;
130  status_bar->addWidget(spacer);
131  spacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
132  spacer->setFrameStyle(frame_style);
133  QSizePolicy policy = spacer->sizePolicy();
134  policy.setHorizontalStretch(100);
135  spacer->setSizePolicy(policy);
136  policy.setHorizontalStretch(0);
137 
138  m_lbl_status_cursor = new QLabel(this);
139  status_bar->addWidget(m_lbl_status_cursor);
140  m_lbl_status_cursor->setSizePolicy(policy);
141  m_lbl_status_cursor->setFrameStyle(frame_style);
142 
143  m_lbl_status_mode = new QLabel(this);
144  status_bar->addWidget(m_lbl_status_mode);
145  m_lbl_status_mode->setSizePolicy(policy);
146  m_lbl_status_mode->setFrameStyle(frame_style);
147 
148  m_lbl_status_size = new QLabel(this);
149  status_bar->addWidget(m_lbl_status_size);
150  m_lbl_status_size->setSizePolicy(policy);
151  m_lbl_status_size->setFrameStyle(frame_style);
152 
153  // start up iconified if requested
154  const QCommandLineParser *args = m_application.cmdline();
155  bool iconic = (args && args->isSet(_("iconic")));
156  if (iconic) {
157  showMinimized();
158  }
159 
160  setAcceptDrops(true); // enable drag&drop
161 
162  // direct all kind of focus to this window per default
163  setFocusPolicy(Qt::WheelFocus);
164 }
165 
166 //***************************************************************************
168 {
169  // connect the context to the top widget
170  connect(context, SIGNAL(sigMetaDataChanged(Kwave::MetaDataList)),
171  this, SLOT(metaDataChanged(Kwave::MetaDataList)));
172  connect(context,
173  SIGNAL(sigSelectionChanged(sample_index_t,sample_index_t)),
174  this,
176  connect(context, SIGNAL(sigUndoRedoInfo(QString,QString)),
177  this, SLOT(setUndoRedoInfo(QString,QString)));
178  connect(context, SIGNAL(sigModified()),
179  this, SLOT(modifiedChanged()));
180 
181  // connect the zoom toolbar
182  connect(context, SIGNAL(sigZoomChanged(Kwave::FileContext*,double)),
183  m_toolbar_zoom, SLOT(setZoomInfo(Kwave::FileContext*,double)));
184  connect(context, SIGNAL(destroyed(Kwave::FileContext*)),
185  m_toolbar_zoom, SLOT(contextDestroyed(Kwave::FileContext*)));
186 
187  // connect the playback/record toolbar
188  connect(context,
189  SIGNAL(destroyed(Kwave::FileContext*)),
191  SLOT(contextDestroyed(Kwave::FileContext*))
192  );
193  connect(context, SIGNAL(sigMetaDataChanged(Kwave::MetaDataList)),
196 
197  // connect the status bar
198  connect(context, SIGNAL(sigStatusBarMessage(QString,uint)),
199  this, SLOT(showStatusBarMessage(QString,uint)));
200 }
201 
202 //***************************************************************************
204 {
205  Q_ASSERT(m_toolbar_zoom);
206  if (!m_toolbar_zoom) return Q_NULLPTR;
207 
208  Kwave::FileContext *context =
209  new(std::nothrow) Kwave::FileContext(m_application);
210  if (!context) return Q_NULLPTR;
211  if (!context->init(this)) {
212  delete context;
213  return Q_NULLPTR;
214  }
215 
216  // if we are in SDI mode, there is a context but no MDI sub window
217  // and in MDI/TAB mode we use this special entry for a context that
218  // has just been created but has no sub window yet
219  m_context_map[Q_NULLPTR] = context;
220 
221  // do all signal/slot connections
222  connectContext(context);
223 
224  // if we reach this point everything was ok, now we can safely switch
225  // to the new context
226  emit sigFileContextSwitched(context);
227 
228  return context;
229 }
230 
231 //***************************************************************************
233 {
234  if (m_context_map.isEmpty()) return Q_NULLPTR;
235 
236  if (m_context_map.contains(Q_NULLPTR)) {
237  // the "null" entry is special, it has precedence over all
238  // other entries. In SDI mode it is the only one ever and in
239  // MDI/TAB mode it is used for a context that has no sub
240  // window yet.
241  return m_context_map[Q_NULLPTR];
242  }
243 
244  if (m_mdi_area) {
245  // MDI or TAB mode
246  QMdiSubWindow *current_sub = m_mdi_area->currentSubWindow();
247  if (!m_context_map.contains(current_sub)) {
248  qWarning("WARNING: unassociated MDI sub window %p?",
249  static_cast<void *>(current_sub));
250  QMapIterator<QMdiSubWindow*, Kwave::FileContext*> it(m_context_map);
251  Kwave::FileContext *context = Q_NULLPTR;
252  while (it.hasNext()) {
253  it.next();
254  context = it.value();
255  }
256  return (m_context_map.count() == 1) ? context : Q_NULLPTR;
257  }
258  return m_context_map[current_sub];
259  } else {
260  // SDI mode
261  Q_ASSERT(0 && "SDI mode but no context?");
262  return Q_NULLPTR;
263  }
264 }
265 
266 //***************************************************************************
268 {
269  showInSplashSreen(i18n("Loading main menu..."));
270  QMenuBar *menubar = menuBar();
271  Q_ASSERT(menubar);
272  if (!menubar) return false;
273  m_menu_manager = new(std::nothrow) Kwave::MenuManager(this, *menubar);
274  Q_ASSERT(m_menu_manager);
275  if (!m_menu_manager) return false;
276 
277  // connect clicked menu entries with main communication channel of kwave
278  connect(m_menu_manager, SIGNAL(sigMenuCommand(QString)),
279  this, SLOT(forwardCommand(QString)));
281  this, SLOT(clipboardChanged(bool)));
282 
283  // --- zoom control toolbar ---
284 
285  m_toolbar_zoom = new(std::nothrow) Kwave::ZoomToolBar(this, TOOLBAR_ZOOM);
286  Q_ASSERT(m_toolbar_zoom);
287  if (!m_toolbar_zoom) return false;
288 
290  m_toolbar_zoom, SLOT(contextSwitched(Kwave::FileContext*)));
291 
292  // --- playback control toolbar ---
295  Q_ASSERT(m_toolbar_record_playback);
296  if (!m_toolbar_record_playback) return false;
297 
300  SLOT(contextSwitched(Kwave::FileContext*)));
301  connect(m_toolbar_record_playback, SIGNAL(sigCommand(QString)),
302  this, SLOT(forwardCommand(QString)));
303 
304  // -- create a new file context ---
305  Kwave::FileContext *context = newFileContext();
306  if (!context) return false;
307 
308  QWidget *central_widget = Q_NULLPTR;
309  switch (m_application.guiType()) {
310  case Kwave::App::GUI_SDI:
311  // create a main widget
312  if (!context->createMainWidget(geometry().size() * 0.85))
313  return false;
314  central_widget = context->mainWidget();
315  break;
316  case Kwave::App::GUI_MDI:
317  // create a MDI area if required, MDI mode
318  m_mdi_area = new(std::nothrow) QMdiArea(this);
319  Q_ASSERT(m_mdi_area);
320  if (!m_mdi_area) return false;
321  m_mdi_area->setViewMode(QMdiArea::SubWindowView);
322  break;
323  case Kwave::App::GUI_TAB:
324  // create a MDI area if required, TAB mode
325  m_mdi_area = new(std::nothrow) QMdiArea(this);
326  Q_ASSERT(m_mdi_area);
327  if (!m_mdi_area) return false;
328  m_mdi_area->setViewMode(QMdiArea::TabbedView);
329  m_mdi_area->setTabsClosable(true);
330  m_mdi_area->setTabsMovable(true);
331  break;
332  }
333 
334  if (m_mdi_area) {
335  connect(m_mdi_area, SIGNAL(subWindowActivated(QMdiSubWindow*)),
336  this, SLOT(subWindowActivated(QMdiSubWindow*)) );
337  central_widget = m_mdi_area;
338  }
339 
340  // --- set up the toolbar ---
341 
342  showInSplashSreen(i18n("Initializing toolbar..."));
343  KToolBar *toolbar_file = toolBar(TOOLBAR_FILE);
344  Q_ASSERT(toolbar_file);
345  if (!toolbar_file) return false;
346 
347  // --- file open and save ---
348 
349  toolbar_file->addAction(
350  QIcon::fromTheme(_("document-new")),
351  i18n("Create a new empty file"),
352  this, SLOT(toolbarFileNew()));
353 
354  toolbar_file->addAction(
355  QIcon::fromTheme(_("document-open")),
356  i18n("Open an existing file"),
357  this, SLOT(toolbarFileOpen()));
358 
359  m_action_save = toolbar_file->addAction(
360  QIcon::fromTheme(_("document-save")),
361  i18n("Save the current file"),
362  this, SLOT(toolbarFileSave()));
363 
364  m_action_save_as = toolbar_file->addAction(
365  QIcon::fromTheme(_("document-save-as")),
366  i18n("Save the current file under a different name or file format..."),
367  this, SLOT(toolbarFileSaveAs()));
368 
369  m_action_close = toolbar_file->addAction(
370  QIcon::fromTheme(_("document-close")),
371  i18n("Close the current file"),
372  this, SLOT(toolbarFileClose()));
373 
374  // --- edit, cut&paste ---
375 
376  KToolBar *toolbar_edit = toolBar(TOOLBAR_EDIT);
377  Q_ASSERT(toolbar_edit);
378  if (!toolbar_edit) return false;
379 
380  m_action_undo = toolbar_edit->addAction(
381  QIcon::fromTheme(_("edit-undo")),
382  i18n("Undo"),
383  this, SLOT(toolbarEditUndo()));
384 
385  m_action_redo = toolbar_edit->addAction(
386  QIcon::fromTheme(_("edit-redo")),
387  i18n("Redo"),
388  this, SLOT(toolbarEditRedo()));
389 
390  m_action_cut = toolbar_edit->addAction(
391  QIcon::fromTheme(_("edit-cut")),
392  i18n("Cut"),
393  this, SLOT(toolbarEditCut()));
394 
395  m_action_copy = toolbar_edit->addAction(
396  QIcon::fromTheme(_("edit-copy")),
397  i18n("Copy"),
398  this, SLOT(toolbarEditCopy()));
399 
400  QAction *btPaste = toolbar_edit->addAction(
401  QIcon::fromTheme(_("edit-paste")),
402  i18n("Insert"),
403  this, SLOT(toolbarEditPaste()));
404  btPaste->setEnabled(!Kwave::ClipBoard::instance().isEmpty());
406  btPaste, SLOT(setEnabled(bool)));
407 
408  m_action_erase = toolbar_edit->addAction(
409  QIcon::fromTheme(_("draw-eraser")),
410  i18n("Mute selection"),
411  this, SLOT(toolbarEditErase()));
412 
413  m_action_delete = toolbar_edit->addAction(
414  QIcon::fromTheme(_("edit-delete")),
415  i18n("Delete selection"),
416  this, SLOT(toolbarEditDelete()));
417 
418  // set up the relationship between top widget / mdi area / main widget
419  Q_ASSERT(central_widget);
420  if (!central_widget)
421  return false;
422 
423  setCentralWidget(central_widget);
424 
425  // set a nice initial size of the toplevel window
426  int w = central_widget->minimumSize().width();
427  w = qMax(w, central_widget->sizeHint().width());
428  w = qMax(w, width());
429  int h = qMax(central_widget->sizeHint().height(), (w * 6) / 10);
430  h = qMax(h, height());
431  resize(w, h);
432 
434  setUndoRedoInfo(QString(), QString());
435  selectionChanged(0, 0);
436  updateMenu();
437  updateToolbar();
439 
440  // make sure that everything of our window is visible
441  QRect desk = qApp->desktop()->rect();
442  QRect g = this->geometry();
443  if (!desk.contains(g)) {
444  // KDE's stupid geometry management has failed ?
445  // this happens when one passes "-geometry <WIDTH>x<HEIGHT>" without
446  // specifying a target position!?
447  // passing "-geometry <WIDTH>x<HEIGHT>-<LEFT>-<TOP>" works...
448  g = desk.intersected(g);
449  setGeometry(g);
450  }
451 
452  // enable saving of window size and position for next startup
453  setAutoSaveSettings();
454 
455  // workaround for KDE4: detect first startup and set all toolbars
456  // to "only symbols" mode
457  KConfigGroup cfg = KSharedConfig::openConfig()->group("MainWindow");
458  QString magic = _("3");
459  if (cfg.readEntry("toolbars") != magic) {
460  qDebug("toolbar layout changed => resetting toolbars to defaults");
461 
462  // unlock all toolbars
463  KToolBar::setToolBarsLocked(false);
464 
465  // reset all toolbars to default layout
467 
468  cfg.writeEntry("toolbars", magic);
469  }
470 
471  // make sure we have the focus, not the zoom combo box
472  setFocus();
473 
474  // special handling: a null string tells the splash screen to hide
475  showInSplashSreen(QString());
476  return true;
477 }
478 
479 //***************************************************************************
481 {
482  // close the current file (no matter what the user wants)
484 
485  delete m_toolbar_zoom;
486  m_toolbar_zoom = Q_NULLPTR;
487 
489  m_toolbar_record_playback = Q_NULLPTR;
490 
491  delete m_menu_manager;
492  m_menu_manager = Q_NULLPTR;
493 
494  while (!m_context_map.isEmpty()) {
495  Kwave::FileContext *context = m_context_map.values().last();
496  m_context_map.remove(m_context_map.keys().last());
497  if (context) delete context;
498  }
499 
501 }
502 
503 //***************************************************************************
504 QList<Kwave::App::FileAndInstance> Kwave::TopWidget::openFiles() const
505 {
506  QList<Kwave::App::FileAndInstance> all_files;
507 
508  foreach (Kwave::FileContext *context, m_context_map.values()) {
509  if (!context) continue;
510  QString name = context->signalName();
511  if (!name.length()) continue;
512  int instance_nr = context->instanceNr();
513  all_files.append(Kwave::App::FileAndInstance(name, instance_nr));
514  }
515 
516  return all_files;
517 }
518 
519 //***************************************************************************
520 QList<Kwave::FileContext *> Kwave::TopWidget::detachAllContexts()
521 {
522  QList<Kwave::FileContext *> list;
523 
524  QMutableMapIterator<QMdiSubWindow *, Kwave::FileContext *> i(m_context_map);
525  while (i.hasNext()) {
526  i.next();
527  QMdiSubWindow *sub = i.key();
528  Kwave::FileContext *context = i.value();
529 
530  // remove the entry from the map to prevent damage
531  i.remove();
532 
533  if (sub) {
534  // leave the "minimized" state before migration
535  Qt::WindowStates state = sub->windowState();
536  if (state & Qt::WindowMinimized)
537  {
538  state &= ~Qt::WindowMinimized;
539  sub->setWindowState(state);
540  sub->showNormal();
541  }
542 
543  // detach the main widget from the MDI sub window
544  sub->setWidget(Q_NULLPTR);
545  delete sub;
546  }
547 
548  // detach the context from this parent widget
549  if (context) {
550  context->disconnect();
551  context->setParent(Q_NULLPTR);
552  if (context->isInUse())
553  list += context; // in use -> keep
554  else
555  context->release(); // empty -> release
556  }
557  }
558 
559  // get rid of the MDI area, it should be empty now
560  setCentralWidget(Q_NULLPTR);
561  if (m_mdi_area) {
562  delete m_mdi_area;
563  m_mdi_area = Q_NULLPTR;
564  }
565 
566  emit sigFileContextSwitched(Q_NULLPTR);
567 
568  return list;
569 }
570 
571 //***************************************************************************
573 {
574  // if no context was given: create a new empty default context
575  if (!context) {
576  context = newFileContext();
577  Q_ASSERT(context);
578  if (!context) return;
580  !context->mainWidget() )
581  {
582  context->createMainWidget(geometry().size() * 0.85);
583  }
584  // prevent it from getting removed again
585  m_context_map.remove(Q_NULLPTR);
586  }
587 
588  switch (m_application.guiType()) {
589  case Kwave::App::GUI_SDI:
590  // we may have an empty default context -> get rid of it
591  Q_ASSERT(m_context_map.count() <= 1);
592  if (!m_context_map.isEmpty()) {
593  Kwave::FileContext *ctx = m_context_map[Q_NULLPTR];
594  m_context_map.remove(Q_NULLPTR);
595  if (ctx) ctx->release();
596  }
597  // take over the new context
598  m_context_map[Q_NULLPTR] = context;
599  connectContext(context);
600  context->setParent(this);
601 
602  // set the central widget to the new main widget
603  setCentralWidget(context->mainWidget());
604  break;
605  case Kwave::App::GUI_MDI:
606  if (!m_mdi_area) {
607  // create a MDI area if required, MDI mode
608  m_mdi_area = new(std::nothrow) QMdiArea(this);
609  Q_ASSERT(m_mdi_area);
610  if (!m_mdi_area) return;
611  m_mdi_area->setViewMode(QMdiArea::SubWindowView);
612  }
613  /* FALLTHROUGH */
614  case Kwave::App::GUI_TAB: {
615  // create a MDI area if required, TAB mode
616  if (!m_mdi_area) {
617  m_mdi_area = new(std::nothrow) QMdiArea(this);
618  Q_ASSERT(m_mdi_area);
619  if (!m_mdi_area) return;
620  m_mdi_area->setViewMode(QMdiArea::TabbedView);
621  m_mdi_area->setTabsClosable(true);
622  m_mdi_area->setTabsMovable(true);
623  }
624 
625  context->setParent(this);
626  setCentralWidget(m_mdi_area);
627 
628  connect(m_mdi_area, SIGNAL(subWindowActivated(QMdiSubWindow*)),
629  this, SLOT(subWindowActivated(QMdiSubWindow*)) );
630 
631  QWidget *main_widget = context->mainWidget();
632  if (main_widget) {
633  QMdiSubWindow *sub = m_mdi_area->addSubWindow(
634  main_widget, Qt::SubWindow);
635  Q_ASSERT(sub);
636  if (!sub) return;
637  sub->adjustSize();
638 
639  if (m_context_map.contains(Q_NULLPTR))
640  m_context_map.remove(Q_NULLPTR);
641  m_context_map[sub] = context;
642 
643  connect(context->mainWidget(), SIGNAL(destroyed(QObject*)),
644  sub, SLOT(close()));
645  connect(sub, SIGNAL(destroyed(QObject*)),
646  this, SLOT(subWindowDeleted(QObject*)));
647  connectContext(context);
648 
650  // this really sucks...
651  // Qt adds a "Close" entry to the MDI subwindow's system
652  // menu, with the shortcut "Ctrl+W". This collides with
653  // our own shortcut, produces a warning and makes the
654  // shortcut key not work:
655  // QAction::eventFilter: Ambiguous shortcut overload: Ctrl+W
656  QMenu *m = sub->systemMenu();
657  if (m) {
658  foreach (QAction *act, m->actions())
659  if (act) act->setShortcut(0); // remove shortcut
660  }
661  }
662 
663  sub->setAttribute(Qt::WA_DeleteOnClose);
664  main_widget->setWindowTitle(context->windowCaption(true));
665 
666  // workaround for stupid bug in Qt: when having only one
667  // single sub window, switching to tab mode shows a tab +
668  // a sub window with frame and title (not maximized within
669  // the mdi area)
670  Qt::WindowStates state = sub->windowState();
672  {
673  state |= Qt::WindowMaximized;
674  sub->setWindowState(state);
675  sub->show();
676  }
677  else
678  {
679  state &= ~Qt::WindowMaximized;
680  sub->setWindowState(state);
681  sub->showNormal();
682  }
683 
684  // NOTE: we have to mark the sub window as "not hidden",
685  // otherwise currentSubWindow() would return a null pointer!
686  sub->setHidden(false);
687  m_mdi_area->setActiveSubWindow(sub);
688  } else {
689  m_context_map[Q_NULLPTR] = context; // set empty default context
690  }
691 
692  break;
693  }
694  }
695 
696  // update the menu bar, toolbar etc.
697  emit sigFileContextSwitched(Q_NULLPTR);
698  emit sigFileContextSwitched(context);
699  updateMenu();
700  updateToolbar();
701  updateCaption();
702 
703 }
704 
705 //***************************************************************************
706 int Kwave::TopWidget::executeCommand(const QString &line)
707 {
708  int result = 0;
709  QString command = line;
710 
711 // qDebug("TopWidget::executeCommand(%s)", DBG(command));
712  if (!command.length()) return 0; // empty line -> nothing to do
713 
714  // parse one single command
715  Kwave::Parser parser(command);
716 
717  // playback commands are always possible
718  if ( (parser.command() == _("playback")) && (m_toolbar_record_playback) )
720 
721  if ((result = m_application.executeCommand(command)) != ENOSYS)
722  return result;
723  result = 0;
724  if (false) {
725  CASE_COMMAND("about_kde")
726  // Help / About KDE
727  KHelpMenu *dlg = new KHelpMenu(this, _("Kwave"));
728  if (dlg) dlg->aboutKDE();
729  result = 0;
730  CASE_COMMAND("menu")
731  Q_ASSERT(m_menu_manager);
732  if (m_menu_manager) result = m_menu_manager->executeCommand(command);
733  CASE_COMMAND("newsignal")
734  sample_index_t samples = parser.toSampleIndex();
735  double rate = parser.toDouble();
736  unsigned int bits = parser.toUInt();
737  unsigned int tracks = parser.toUInt();
738  result = newSignal(samples, rate, bits, tracks);
739  CASE_COMMAND("open")
740  QString filename = parser.nextParam();
741  if (!filename.isEmpty()) {
742  // open the selected file
743  result = loadFile(QUrl::fromUserInput(filename));
744  } else {
745  // show file open dialog
746  result = openFile();
747  }
748  CASE_COMMAND("openrecent")
749  result = openRecent(command);
750  CASE_COMMAND("quit")
751  result = (close()) ? 0 : -1;
752  CASE_COMMAND("reset_toolbars")
753  if ((result = (Kwave::MessageBox::questionYesNo(this,
754  i18n("Reset the toolbar to default settings?"))
755  == KMessageBox::Yes) ? 1 : 0))
756  {
758  }
759  result = 0;
760  CASE_COMMAND("select_gui_type")
761  QString gui_type = parser.nextParam();
763 
764  if (gui_type == _("SDI"))
765  new_type = Kwave::App::GUI_SDI;
766  else if (gui_type == _("MDI"))
767  new_type = Kwave::App::GUI_MDI;
768  else if (gui_type == _("TAB"))
769  new_type = Kwave::App::GUI_TAB;
770  else
771  return -1;
772 
773  KConfigGroup cfg = KSharedConfig::openConfig()->group("Global");
774  cfg.writeEntry(_("UI Type"), gui_type);
775  m_application.switchGuiType(this, new_type);
776  result = 0;
777  CASE_COMMAND("reenable_dna")
778  if ((result = (Kwave::MessageBox::questionYesNo(this,
779  i18n("Re-enable all disabled notifications?\n"
780  "All messages that you previously turned off by activating "
781  "the \"Do not ask again\" checkbox will then be enabled again."
782  ))
783  == KMessageBox::Yes) ? 1 : 0))
784  {
785  KMessageBox::enableAllMessages();
786  }
787  result = 0;
788  CASE_COMMAND("window:minimize")
790  // in case of MDI mode: minimize the current sub window
791  if (m_mdi_area) {
792  QMdiSubWindow *sub = m_mdi_area->activeSubWindow();
793  if (!sub) return -1;
794  sub->setWindowState(windowState() | Qt::WindowMinimized);
795  }
796  } else {
797  // in case of TAB or SDI mode: minimize the toplevel window
798  setWindowState(windowState() | Qt::WindowMinimized);
799  }
800  result = 0;
801  CASE_COMMAND("window:next_sub")
802  if (m_mdi_area) m_mdi_area->activateNextSubWindow();
803  result = 0;
804  CASE_COMMAND("window:prev_sub")
805  if (m_mdi_area) m_mdi_area->activatePreviousSubWindow();
806  result = 0;
807  CASE_COMMAND("window:cascade")
808  if (m_mdi_area) m_mdi_area->cascadeSubWindows();
809  result = 0;
810  CASE_COMMAND("window:tile")
811  if (m_mdi_area) m_mdi_area->tileSubWindows();
812  result = 0;
813  CASE_COMMAND("window:tile_vertical")
814  if (!m_mdi_area) return 0;
815 
816  // determine the number of not minimized sub windows
817  int count = 0;
818  foreach (QMdiSubWindow *sub, m_mdi_area->subWindowList()) {
819  if (sub && !(sub->windowState() & Qt::WindowMinimized))
820  ++count;
821  }
822  if (!count) return 0;
823 
824  int total_height = m_mdi_area->height();
825  int width = m_mdi_area->width();
826  int height = total_height / count;
827  int increment = height;
828  int y = 0;
829  foreach (QMdiSubWindow *sub, m_mdi_area->subWindowList()) {
830  if (!sub || (sub->windowState() & Qt::WindowMinimized))
831  continue;
832  // resize/move the sub window
833  sub->resize(width, height);
834  sub->move(0, y);
835  y += increment;
836  }
837  result = 0;
838 
839  CASE_COMMAND("window:activate")
840  if (m_mdi_area) {
841  QString title = parser.nextParam();
842  foreach (QMdiSubWindow *sub, m_context_map.keys()) {
843  if (!sub) continue;
844  Kwave::FileContext *context = m_context_map[sub];
845  if (!context) continue;
846 
847  // identify the window by it's title
848  if (context->windowCaption(false) == title) {
849  // activate the sub window if it is not the active one
850  if (m_mdi_area->activeSubWindow() != sub)
851  m_mdi_area->setActiveSubWindow(sub);
852 
853  // leave the "minimized" state if necessary
854  Qt::WindowStates state = sub->windowState();
855  if (state & Qt::WindowMinimized)
856  sub->setWindowState(state & ~(Qt::WindowMinimized));
857  sub->raise();
858  return 0;
859  }
860  }
861  } else return ENOSYS;
862  return EINVAL;
863  } else {
864  return ENOSYS; // command not implemented (here)
865  }
866 
867  return result;
868 }
869 
870 //***************************************************************************
871 int Kwave::TopWidget::forwardCommand(const QString &command)
872 {
873  Kwave::FileContext *context = currentContext();
874  if (!context) return EAGAIN;
875 
876  // execute the command in the current context
877  int retval = context->executeCommand(command);
878 
879  // synchronize after the command
880  Kwave::PluginManager *plugin_manager = context->pluginManager();
881  if (plugin_manager) plugin_manager->sync();
882 
883  return retval;
884 }
885 
886 //***************************************************************************
888 {
889  bool allowed = true;
890 
891  QMutableMapIterator<QMdiSubWindow *, Kwave::FileContext *>
892  it(m_context_map);
893  it.toBack();
894  while (it.hasPrevious()) {
895  it.previous();
896  QMdiSubWindow *sub = it.key();
897  Kwave::FileContext *context = it.value();
898 
899  if (!sub) {
900  // reached the default context (without sub windows)
901  // or SDI mode with only one context
902  if (context) allowed &= context->closeFile();
903  break;
904  }
905 
906  if (!context) {
907  // invalid entry?
908  it.remove();
909  continue;
910  }
911 
912  // try to close the sub window / context
913  if (!sub->close()) {
914  allowed = false;
915  break;
916  } else {
917  it.remove();
918  if (m_context_map.isEmpty()) {
919  // keep the default context, without a window
920  m_context_map[Q_NULLPTR] = context;
921  break;
922  }
923  }
924  }
925 
926  return allowed;
927 }
928 
929 //***************************************************************************
930 int Kwave::TopWidget::newWindow(Kwave::FileContext *&context, const QUrl &url)
931 {
932  switch (m_application.guiType()) {
933  case Kwave::App::GUI_SDI: {
934  // SDI mode and already something loaded
935  // -> open a new toplevel window
936  // (except for processing commands per kwave: URL
937  Kwave::SignalManager *signal_manager = (context) ?
938  context->signalManager() : Q_NULLPTR;
939  if ( signal_manager && !signal_manager->isEmpty() &&
940  (url.scheme().toLower() != Kwave::urlScheme()) )
941  return m_application.newWindow(url);
942 
943  // try to close the previous file
944  if (context && !context->closeFile()) return -1;
945 
946  break;
947  }
948  case Kwave::App::GUI_MDI: /* FALLTHROUGH */
949  case Kwave::App::GUI_TAB:
950  // MDI or TAB mode: open a new sub window
951  Q_ASSERT(m_mdi_area);
952  if (!m_mdi_area) return -1;
953 
954  if (context && context->isEmpty()) {
955  // current context is empty, no main widget etc -> discard it
956  if (m_context_map.contains(Q_NULLPTR)) {
957  // must have been the default context
958  Q_ASSERT(m_context_map[Q_NULLPTR] == context);
959  m_context_map.remove(Q_NULLPTR);
960  }
961 
962  // NOTE: do not handle the following context switch
963  // notification, it might be handled with a
964  // refcount that has already been set to zero
965  disconnect(
966  this,
968  context,
969  SLOT(contextSwitched(Kwave::FileContext*))
970  );
971  context->release();
972  }
973 
974  // create a new file context
975  context = newFileContext();
976  if (!context) return -1;
977 
978  // create a main widget
979  if (!context->createMainWidget(
980  m_mdi_area->geometry().size() * 0.85)) return -1;
981 
982  // insert the context into this instance
983  insertContext(context);
984  break;
985  }
986 
987  return 1;
988 }
989 
990 //***************************************************************************
991 int Kwave::TopWidget::loadFile(const QUrl &url)
992 {
993  Kwave::FileContext *context = currentContext();
994 
995  // special handling for kwave: URLs
996  if (url.scheme().toLower() == Kwave::urlScheme()) {
997  QString cmd = Kwave::Parser::fromUrl(url);
999  _("CMD: from command line: '") + cmd + _("'"));
1000  Kwave::Splash::showMessage(i18n("Executing command '%1'...", cmd));
1001  return (context) ? context->executeCommand(cmd) : executeCommand(cmd);
1002  }
1003 
1004  if (!context) return -1;
1005  Kwave::SignalManager *signal_manager = context->signalManager();
1006  Q_ASSERT(signal_manager);
1007 
1008  // add an entry to the list of recent files
1009  m_application.addRecentFile(url.isLocalFile() ?
1010  url.toLocalFile() : url.toDisplayString());
1011 
1012  // abort if new file not valid and local
1013  if (!url.isLocalFile()) return -1;
1014 
1015  // detect whether it is a macro (batch) file
1016  QFileInfo file(url.fileName());
1017  QString suffix = file.suffix();
1018  if (suffix == _("kwave")) {
1020  i18n("Executing Kwave script file '%1'...", url.toDisplayString())
1021  );
1022  return context->loadBatch(url);
1023  }
1024 
1025  // open a new window (empty in case of MDI/TAB)
1026  int retval = newWindow(context, url);
1027  if ((retval <= 0) || !context) return retval;
1028 
1030  i18n("Loading file '%1'...", url.toDisplayString())
1031  );
1032 
1033  // NOTE: the context may have changed, now we may have a different
1034  // signal manager
1035  signal_manager = context->signalManager();
1036  int res = -ENOMEM;
1037  if (signal_manager && !(res = signal_manager->loadFile(url))) {
1038  // succeeded
1039  } else {
1040  qWarning("TopWidget::loadFile() failed: result=%d", res);
1041  QString reason;
1042  switch (res) {
1043  case -ENOMEM:
1044  reason = i18n("Out of memory");
1045  break;
1046  case -EIO:
1047  reason = i18nc("error message after opening a file failed",
1048  "Unable to open '%1'", url.toDisplayString());
1049  break;
1050  case -EINVAL:
1051  reason = i18nc("error message after opening a file failed",
1052  "Invalid or unknown file type: '%1'",
1053  url.toDisplayString());
1054  break;
1055  default:
1056  reason = _("");
1057  }
1058 
1059  // show an error message box if the reason was known
1060  if (reason.length()) {
1061  Kwave::MessageBox::error(this, reason);
1062  }
1063 
1064  // load failed
1065  context->closeFile();
1066  }
1067 
1068  updateMenu();
1069  updateToolbar();
1070 
1071  return 0;
1072 }
1073 
1074 //***************************************************************************
1075 int Kwave::TopWidget::openRecent(const QString &str)
1076 {
1077  Kwave::Parser parser(str);
1078  return loadFile(QUrl::fromUserInput(parser.firstParam()));
1079 }
1080 
1081 //***************************************************************************
1083 {
1084  QString filter = Kwave::CodecManager::decodingFilter();
1085  QPointer<Kwave::FileDialog> dlg = new (std::nothrow) Kwave::FileDialog(
1086  _("kfiledialog:///kwave_open_dir"),
1087  Kwave::FileDialog::OpenFile, filter, this
1088  );
1089  if (!dlg) return -1;
1090  dlg->setWindowTitle(i18n("Open"));
1091  if (dlg->exec() == QDialog::Accepted) {
1092  QUrl url = dlg->selectedUrl();
1093  delete dlg;
1094  return loadFile(url);
1095  } else {
1096  delete dlg;
1097  return -1;
1098  }
1099 }
1100 
1101 //***************************************************************************
1103  unsigned int bits, unsigned int tracks)
1104 {
1105  Kwave::FileContext *context = currentContext();
1106  if (!context) return -1;
1107 
1108  Kwave::SignalManager *signal_manager = context->signalManager();
1109  if (!signal_manager) return -1;
1110 
1111  QUrl url = Kwave::Parser::toUrl(
1112  _("newsignal(%1,%2,%3,%4)"
1113  ).arg(samples).arg(rate).arg(bits).arg(tracks));
1114  int retval = newWindow(context, url);
1115  if (retval <= 0) return retval;
1116 
1117  signal_manager = context->signalManager();
1118  if (!signal_manager) return -1;
1119 
1120  signal_manager->newSignal(samples, rate, bits, tracks);
1121  return 0;
1122 }
1123 
1124 //***************************************************************************
1126 {
1127  Q_ASSERT(statusBar());
1128  if (!statusBar() || !m_menu_manager) return;
1129  QString txt;
1130 
1131  const Kwave::FileInfo info(meta_data);
1132  sample_index_t length = info.length();
1133  unsigned int tracks = info.tracks();
1134  double rate = info.rate();
1135  unsigned int bits = info.bits();
1136 
1137  // length in milliseconds
1138  if (length) {
1139  double ms = (rate > 0) ? (static_cast<double>(length) /
1140  static_cast<double>(rate) * 1E3) : 0;
1141  txt = _(" ") + i18nc(
1142  "Length, as in total duration of loaded song",
1143  "Length: %1 (%2 samples)",
1144  Kwave::ms2string(ms),
1145  Kwave::samples2string(length)
1146  ) + _(" ");
1147  } else txt = _("");
1148  m_lbl_status_size->setText(txt);
1149 
1150  // sample rate and resolution
1151  if (bits) {
1152  QString khz = _("%0.3f");
1153  khz = khz.sprintf("%0.3f", static_cast<double>(rate) * 1E-3);
1154  txt = _(" ") + i18n("Mode: %1 kHz @ %2 Bit", khz, bits) + _(" ");
1155  } else txt = _("");
1156  m_lbl_status_mode->setText(txt);
1157 
1158  // remove selection/position display on file close
1159  bool have_signal = (tracks != 0);
1160  if (!have_signal) selectionChanged(0, 0);
1161 
1162  // update the menu
1163  updateMenu();
1164 
1165  // update the toolbar as well
1166  updateToolbar();
1167 
1168  // update the window caption
1169  updateCaption();
1170 }
1171 
1172 //***************************************************************************
1174  sample_index_t length)
1175 {
1176  const Kwave::FileContext *context = currentContext();
1177  if (!context) return;
1178 
1179  Kwave::SignalManager *signal_manager = context->signalManager();
1180  Q_ASSERT(signal_manager);
1181  if (!signal_manager) return;
1182  Q_ASSERT(statusBar());
1183  if (!statusBar()) return;
1184 
1185  // force sample mode if rate==0
1186  const double rate = signal_manager->rate();
1187  const bool sample_mode = (qFuzzyIsNull(rate));
1188 
1189  if (length > 1) {
1190  // show offset and length
1191  // Selected: 2000...3000 (1000 samples)
1192  // Selected: 02:00...05:00 (3 min)
1193  sample_index_t last = offset + ((length) ? length-1 : 0);
1194 
1195  QString txt = _(" ");
1196  if (sample_mode) {
1197  txt += i18nc(
1198  "%1=first sample, %2=last sample, %3=number of samples, "\
1199  "example: 'Selected: 2000...3000 (1000 samples)'",
1200  "Selected: %1...%2 (%3 samples)",
1201  Kwave::samples2string(offset),
1202  Kwave::samples2string(last),
1203  Kwave::samples2string(length)
1204  );
1205  } else {
1206  double ms_first = static_cast<double>(offset) * 1E3 / rate;
1207  double ms_last = static_cast<double>(last + 1) * 1E3 / rate;
1208  double ms = (ms_last - ms_first);
1209  txt += i18nc(
1210  "%1=start time, %2=end time, %3=time span, "\
1211  "example: 'Selected: 02:00...05:00 (3 min)'",
1212  "Selected: %1...%2 (%3)",
1213  Kwave::ms2string(ms_first),
1214  Kwave::ms2string(ms_last),
1215  Kwave::ms2string(ms)
1216  );
1217  }
1218 
1219  m_lbl_status_cursor->setText(_(""));
1220  statusBar()->showMessage(txt, 4000);
1221  if (m_menu_manager)
1222  m_menu_manager->setItemEnabled(_("@SELECTION"), true);
1223  } else {
1224  // show cursor position
1225  // Position: 02:00
1226 
1227  if (sample_mode || !signal_manager->tracks()) {
1228  m_lbl_status_cursor->setText(_(""));
1229  } else {
1230  double ms_first = static_cast<double>(offset) * 1E3 / rate;
1231  QString txt = i18n("Position: %1",
1232  Kwave::ms2string(ms_first));
1233  m_lbl_status_cursor->setText(txt);
1234  }
1235 
1236  if (m_menu_manager)
1237  m_menu_manager->setItemEnabled(_("@SELECTION"), false);
1238  }
1239 
1240  // update the toolbar on selection change, maybe the
1241  // button for "zoom selection" has to be enabled/disabled
1242  updateToolbar();
1243 }
1244 
1245 //***************************************************************************
1246 void Kwave::TopWidget::setUndoRedoInfo(const QString &undo,
1247  const QString &redo)
1248 {
1249  QString txt;
1250  bool undo_enabled = (undo.length() != 0);
1251  bool redo_enabled = (redo.length() != 0);
1252 
1253  // set the state and tooltip of the undo toolbar button
1254  if (m_action_undo) {
1255  txt = (undo_enabled) ?
1256  i18nc("tooltip of the undo toolbar button if undo enabled",
1257  "Undo (%1)", undo) :
1258  i18nc("tooltip of the undo toolbar button if undo disabled",
1259  "Undo");
1260  m_action_undo->setToolTip(txt);
1261  m_action_undo->setEnabled(undo_enabled);
1262  }
1263 
1264  // set the state and tooltip of the redo toolbar button
1265  if (m_action_redo) {
1266  txt = (redo_enabled) ?
1267  i18nc("tooltip of the redo toolbar button, redo enabled",
1268  "Redo (%1)", redo) :
1269  i18nc("tooltip of the redo toolbar button, redo disabled",
1270  "Redo");
1271  m_action_redo->setToolTip(txt);
1272  m_action_redo->setEnabled(redo_enabled);
1273  }
1274 
1275  if (!m_menu_manager) return;
1276 
1277  // set new enable and text of the undo menu entry
1278  m_menu_manager->setItemEnabled(_("ID_EDIT_UNDO"), undo_enabled);
1279  txt = (undo_enabled) ?
1280  i18nc("menu entry for undo if undo enabled", "Undo (%1)", undo) :
1281  i18nc("menu entry for undo if undo disabled", "Undo");
1282  m_menu_manager->setItemText(_("ID_EDIT_UNDO"), txt);
1283 
1284  // set new enable and text of the undo menu entry
1285  m_menu_manager->setItemEnabled(_("ID_EDIT_REDO"), redo_enabled);
1286  txt = (redo_enabled) ?
1287  i18nc("menu entry for redo if redo enabled", "Redo (%1)", redo) :
1288  i18nc("menu entry for redo if redo disabled", "Redo");
1289  m_menu_manager->setItemText(_("ID_EDIT_REDO"), txt);
1290 }
1291 
1292 //***************************************************************************
1293 void Kwave::TopWidget::clipboardChanged(bool data_available)
1294 {
1295  if (!m_menu_manager) return;
1296  m_menu_manager->setItemEnabled(_("@CLIPBOARD"), data_available);
1297 }
1298 
1299 //***************************************************************************
1301 {
1302  Q_ASSERT(m_menu_manager);
1303  if (!m_menu_manager) return;
1304  m_menu_manager->clearNumberedMenu(_("ID_FILE_OPEN_RECENT"));
1305 
1306  foreach (const QString &file, m_application.recentFiles())
1308  _("ID_FILE_OPEN_RECENT"), file, QString());
1309 
1310  // enable/disable the "clear" menu entry in Files / Open Recent
1311  m_menu_manager->setItemEnabled(_("ID_FILE_OPEN_RECENT_CLEAR"),
1312  !m_application.recentFiles().isEmpty());
1313 }
1314 
1315 //***************************************************************************
1317 {
1318  const Kwave::FileContext *context = currentContext();
1319  Kwave::SignalManager *signal_manager =
1320  (context) ? context->signalManager() : Q_NULLPTR;
1321  Q_ASSERT(m_menu_manager);
1322  if (!m_menu_manager) return;
1323 
1324  bool have_window_menu = false;
1325  switch (m_application.guiType()) {
1326  case Kwave::App::GUI_SDI:
1327  m_menu_manager->selectItem(_("@GUI_TYPE"), _("ID_GUI_SDI"));
1328  m_menu_manager->setItemVisible(_("ID_WINDOW"), false);
1329  m_menu_manager->setItemVisible(_("ID_WINDOW_NEXT"), false);
1330  m_menu_manager->setItemVisible(_("ID_WINDOW_PREV"), false);
1331  m_menu_manager->setItemVisible(_("ID_WINDOW_CASCADE"), false);
1332  m_menu_manager->setItemVisible(_("ID_WINDOW_TILE"), false);
1333  m_menu_manager->setItemVisible(_("ID_WINDOW_TILE_VERTICAL"), false);
1334  m_menu_manager->setItemVisible(_("ID_FILE_NEW_WINDOW"), true);
1335  break;
1336  case Kwave::App::GUI_MDI:
1337  m_menu_manager->selectItem(_("@GUI_TYPE"), _("ID_GUI_MDI"));
1338  m_menu_manager->setItemVisible(_("ID_WINDOW"), true);
1339  m_menu_manager->setItemVisible(_("ID_WINDOW_NEXT"), true);
1340  m_menu_manager->setItemVisible(_("ID_WINDOW_PREV"), true);
1341  m_menu_manager->setItemVisible(_("ID_WINDOW_CASCADE"), true);
1342  m_menu_manager->setItemVisible(_("ID_WINDOW_TILE"), true);
1343  m_menu_manager->setItemVisible(_("ID_WINDOW_TILE_VERTICAL"), true);
1344  m_menu_manager->setItemVisible(_("ID_FILE_NEW_WINDOW"), false);
1345  have_window_menu = true;
1346  break;
1347  case Kwave::App::GUI_TAB:
1348  m_menu_manager->selectItem(_("@GUI_TYPE"), _("ID_GUI_TAB"));
1349  m_menu_manager->setItemVisible(_("ID_WINDOW"), true);
1350  m_menu_manager->setItemVisible(_("ID_WINDOW_NEXT"), true);
1351  m_menu_manager->setItemVisible(_("ID_WINDOW_PREV"), true);
1352  m_menu_manager->setItemVisible(_("ID_WINDOW_CASCADE"), false);
1353  m_menu_manager->setItemVisible(_("ID_WINDOW_TILE"), false);
1354  m_menu_manager->setItemVisible(_("ID_WINDOW_TILE_VERTICAL"), false);
1355  m_menu_manager->setItemVisible(_("ID_FILE_NEW_WINDOW"), false);
1356  have_window_menu = true;
1357  break;
1358  }
1359 
1360  if (have_window_menu) {
1361  // update the "Windows" menu
1362  m_menu_manager->clearNumberedMenu(_("ID_WINDOW_LIST"));
1363  unsigned int win_count = 0;
1364  foreach (const Kwave::FileContext *ctx, m_context_map.values()) {
1365  if (!ctx) continue;
1366  QString caption = ctx->windowCaption(false);
1367  if (!caption.length()) continue;
1368  m_menu_manager->addNumberedMenuEntry(_("ID_WINDOW_LIST"),
1369  caption, QString());
1370  ++win_count;
1371  }
1372 
1373  bool on = (win_count > 1);
1374  m_menu_manager->setItemEnabled(_("ID_WINDOW_NEXT"), on);
1375  m_menu_manager->setItemEnabled(_("ID_WINDOW_PREV"), on);
1376  m_menu_manager->setItemEnabled(_("ID_WINDOW_CASCADE"), on);
1377  m_menu_manager->setItemEnabled(_("ID_WINDOW_TILE"), on);
1378  m_menu_manager->setItemEnabled(_("ID_WINDOW_TILE_VERTICAL"), on);
1379  }
1380 
1381  // enable/disable all items that depend on having a file
1382  bool have_file = (context && context->signalName().length());
1383  m_menu_manager->setItemEnabled(_("@NOT_CLOSED"), have_file);
1384 
1385  // enable/disable all items that depend on having a label
1386  // and update the label menu
1387  bool have_labels = false;
1388  if (signal_manager) {
1389  Kwave::LabelList labels(signal_manager->metaData());
1390  have_labels = !labels.isEmpty();
1391 
1392  m_menu_manager->clearNumberedMenu(_("ID_LABEL_DELETE"));
1393  if (labels.count()) {
1394 
1395  // add special entry to delete all labels
1397  _("ID_LABEL_DELETE"),
1398  i18nc("special entry in the list of labels to delete all",
1399  "(All)"), _("-1"));
1400 
1401  // iterate over the list of labels
1402  unsigned int index = 0;
1403  foreach (const Kwave::Label &label, labels) {
1404  QString name = label.name();
1405  QString desc = (name.length()) ?
1406  i18nc(
1407  "list menu entry of a label, %1=index, %2=description/name",
1408  "#%1 (%2)", index, name) :
1409  i18nc("list menue entry of a label, "
1410  "without description, %1=index",
1411  "#%1", index);
1413  _("ID_LABEL_DELETE"), desc, name.setNum(index));
1414  index++;
1415  }
1416  }
1417  }
1418  m_menu_manager->setItemEnabled(_("@LABELS"), have_labels);
1419 
1420  // update the list of deletable tracks
1421  unsigned int tracks = (signal_manager) ? signal_manager->tracks() : 0;
1422  m_menu_manager->clearNumberedMenu(_("ID_EDIT_TRACK_DELETE"));
1423  QString buf;
1424  for (unsigned int i = 0; i < tracks; i++) {
1426  _("ID_EDIT_TRACK_DELETE"), buf.setNum(i), buf.setNum(i));
1427  }
1428 
1429  // enable/disable all items that depend on having a signal
1430  bool have_signal = (tracks != 0);
1431  m_menu_manager->setItemEnabled(_("@SIGNAL"), have_signal);
1432 
1433  // revert is only possible if the signal exists, is modified and there
1434  // is a file behind from which we could reload (which is not the case
1435  // after File/New...).
1436  bool enable_revert = false;
1437  if (signal_manager) {
1438  bool have_filename = Kwave::FileInfo(
1439  signal_manager->metaData()
1440  ).contains(Kwave::INF_FILENAME);
1441  bool is_modified = signal_manager->isModified();
1442  enable_revert = have_filename && is_modified;
1443  }
1444  m_menu_manager->setItemEnabled(_("ID_FILE_REVERT"), enable_revert);
1445 
1446  // enable/disable all items that depend on having something in the
1447  // clipboard
1448  bool have_clipboard_data = !Kwave::ClipBoard::instance().isEmpty();
1449  clipboardChanged(have_clipboard_data);
1450 }
1451 
1452 //***************************************************************************
1454 {
1455  KToolBar *toolbar_file = toolBar(TOOLBAR_FILE);
1456  KToolBar *toolbar_edit = toolBar(TOOLBAR_EDIT);
1457  KToolBar *toolbar_record_play = toolBar(TOOLBAR_RECORD_PLAY);
1458  KToolBar *toolbar_zoom = toolBar(TOOLBAR_ZOOM);
1459 
1460  int icon_size_def = style()->pixelMetric(QStyle::PM_ToolBarIconSize);
1461  int icon_size_big = style()->pixelMetric(QStyle::PM_LargeIconSize);
1462 
1463  // change style to "symbols only mode" and set standard size
1464  foreach(KToolBar *bar, toolBars()) {
1465  bar->setToolButtonStyle(Qt::ToolButtonIconOnly);
1466  bar->setIconSize( QSize(icon_size_def, icon_size_def) );
1467  }
1468 
1469  // re-order the tool bars:
1470  // -----------------------
1471  // file | edit |
1472  // -----------------------
1473  // record/play | zoom
1474  // -----------------------
1475  insertToolBar(toolbar_zoom, toolbar_record_play);
1476  insertToolBar(toolbar_record_play, toolbar_edit);
1477  insertToolBar(toolbar_edit, toolbar_file);
1478 
1479  // move record/playback into a separate line, below file/edit
1480  insertToolBarBreak(toolbar_record_play);
1481 
1482  // let record/playback and zoom use bigger icons
1483  toolbar_record_play->setIconSize(QSize(icon_size_big, icon_size_big));
1484  toolbar_zoom->setIconSize(QSize(icon_size_big, icon_size_big));
1485 
1486  foreach(KToolBar *bar, toolBars()) {
1487  bar->update();
1488  bar->show();
1489  }
1490 }
1491 
1492 //***************************************************************************
1494 {
1495  const Kwave::FileContext *context = currentContext();
1496  if (!context) return;
1497 
1498  Kwave::SignalManager *signal_manager = context->signalManager();
1499  Q_ASSERT(signal_manager);
1500  if (!signal_manager) return;
1501 
1502  bool have_signal = signal_manager->tracks();
1503  if (m_action_save)
1504  m_action_save->setEnabled(have_signal);
1505  if (m_action_save_as)
1506  m_action_save_as->setEnabled(have_signal);
1507  if (m_action_close)
1508  m_action_close->setEnabled(have_signal);
1509 
1510  bool have_selection = (signal_manager->selection().length() > 1);
1511  if (m_action_cut)
1512  m_action_cut->setEnabled(have_selection);
1513  if (m_action_copy)
1514  m_action_copy->setEnabled(have_selection);
1515  if (m_action_erase)
1516  m_action_erase->setEnabled(have_selection);
1517  if (m_action_delete)
1518  m_action_delete->setEnabled(have_selection);
1519 
1520  // update the zoom toolbar
1521  if (m_toolbar_zoom)
1523 }
1524 
1525 //***************************************************************************
1527 {
1528  updateCaption();
1529  updateMenu();
1530 }
1531 
1532 //***************************************************************************
1534 {
1535  const Kwave::FileContext *context = currentContext();
1536  QString caption = (context) ? context->windowCaption(true) : QString();
1537  setCaption(caption);
1538 }
1539 
1540 //***************************************************************************
1541 void Kwave::TopWidget::closeEvent(QCloseEvent *e)
1542 {
1543  (closeAllSubWindows()) ? e->accept() : e->ignore();
1544 }
1545 
1546 //***************************************************************************
1547 void Kwave::TopWidget::showInSplashSreen(const QString &message)
1548 {
1549  Kwave::Splash::showMessage(message);
1550 }
1551 
1552 //***************************************************************************
1554  unsigned int ms)
1555 {
1556  QStatusBar *status_bar = statusBar();
1557  if (!status_bar) return;
1558 
1559  if (msg.length())
1560  status_bar->showMessage(msg, ms);
1561  else
1562  status_bar->clearMessage();
1563 }
1564 
1565 //***************************************************************************
1567 {
1568  if (!sub || !m_context_map.contains(sub)) return;
1570 }
1571 
1572 //***************************************************************************
1574 {
1575  QMdiSubWindow *sub = static_cast<QMdiSubWindow *>(obj);
1576  if (!sub || !m_context_map.contains(sub)) {
1577  // sub window is not in the map, maybe it has already been detached
1578  return;
1579  }
1580 
1581  Kwave::FileContext *context = m_context_map[sub];
1582  Q_ASSERT(context);
1583  if (!context) return;
1584 
1585  m_context_map.remove(sub);
1586 
1587  if (m_context_map.isEmpty()) {
1588  // keep the default context, without a window
1589  m_context_map[Q_NULLPTR] = context;
1590  } else {
1591  // remove the context
1592  context->release();
1593  }
1594 }
1595 
1596 //***************************************************************************
1597 void Kwave::TopWidget::dragEnterEvent(QDragEnterEvent *event)
1598 {
1599  if (!event) return;
1600  if ((event->proposedAction() != Qt::MoveAction) &&
1601  (event->proposedAction() != Qt::CopyAction))
1602  return; /* unsupported action */
1603 
1604  if (Kwave::FileDrag::canDecode(event->mimeData()))
1605  event->acceptProposedAction();
1606 }
1607 
1608 //***************************************************************************
1609 void Kwave::TopWidget::dropEvent(QDropEvent *event)
1610 {
1611  if (!event) return;
1612  if (!event->mimeData()) return;
1613 
1614  if (event->mimeData()->hasUrls()) {
1615  bool first = true;
1616  foreach (const QUrl &url, event->mimeData()->urls()) {
1617  QString filename = url.toLocalFile();
1618  QString mimetype = Kwave::CodecManager::mimeTypeOf(url);
1619  if (Kwave::CodecManager::canDecode(mimetype)) {
1620  if (first) {
1621  // first dropped URL -> open in this window
1622  forwardCommand(_("open(%1)").arg(
1623  Kwave::Parser::escape(filename)));
1624  first = false;
1625  } else {
1626  // all others -> open a new window
1627  forwardCommand(_("newwindow(%1)").arg(
1628  Kwave::Parser::escape(filename)));
1629  }
1630  }
1631  }
1632  }
1633 }
1634 
1635 //***************************************************************************
1636 //***************************************************************************
sample_index_t toSampleIndex()
Definition: Parser.cpp:246
int loadFile(const QUrl &url)
void subWindowActivated(QMdiSubWindow *sub)
Definition: TopWidget.cpp:1566
void selectionChanged(sample_index_t offset, sample_index_t length)
Definition: TopWidget.cpp:1173
void metaDataChanged(Kwave::MetaDataList meta_data)
Definition: TopWidget.cpp:1125
void showStatusBarMessage(const QString &msg, unsigned int ms)
Definition: TopWidget.cpp:1553
int loadBatch(const QUrl &url)
void clearNumberedMenu(const QString &uid)
int loadFile(const QUrl &url)
Definition: TopWidget.cpp:991
void sigFileContextSwitched(Kwave::FileContext *context)
void setItemEnabled(const QString &uid, bool enable)
QUrl selectedUrl() const
Definition: FileDialog.cpp:253
QMdiArea * m_mdi_area
Definition: TopWidget.h:362
int newWindow(Kwave::FileContext *&context, const QUrl &url)
Definition: TopWidget.cpp:930
static QString fromUrl(const QUrl &url)
Definition: Parser.cpp:360
double rate() const
Definition: FileInfo.cpp:415
QString Q_DECL_EXPORT ms2string(double ms, int precision=6)
Definition: Utils.cpp:66
QString Q_DECL_EXPORT samples2string(sample_index_t samples)
Definition: Utils.cpp:98
virtual ~TopWidget() Q_DECL_OVERRIDE
Definition: TopWidget.cpp:480
void setUndoRedoInfo(const QString &undo, const QString &redo)
Definition: TopWidget.cpp:1246
static void showMessage(const QString &message)
Definition: Splash.cpp:115
static ClipBoard & instance()
Definition: ClipBoard.cpp:39
void toolbarEditRedo()
Definition: TopWidget.h:222
void setParent(Kwave::TopWidget *top_widget)
void toolbarEditUndo()
Definition: TopWidget.h:219
void toolbarEditPaste()
Definition: TopWidget.h:231
bool isInUse() const
Kwave::MetaDataList & metaData()
void modifiedChanged()
Definition: TopWidget.cpp:1526
void toolbarFileSave()
Definition: TopWidget.h:210
Kwave::Selection & selection()
void newSignal(sample_index_t samples, double rate, unsigned int bits, unsigned int tracks)
QAction * m_action_erase
Definition: TopWidget.h:386
static int questionYesNo(QWidget *widget, QString message, QString caption=QString(), const QString buttonYes=QString(), const QString buttonNo=QString(), const QString &dontAskAgainName=QString())
Definition: MessageBox.cpp:63
unsigned int tracks()
void setItemText(const QString &uid, const QString &text)
void toolbarEditCut()
Definition: TopWidget.h:225
quint64 sample_index_t
Definition: Sample.h:28
GuiType
Definition: App.h:49
QAction * m_action_save
Definition: TopWidget.h:365
QLabel * m_lbl_status_mode
Definition: TopWidget.h:395
virtual void dragEnterEvent(QDragEnterEvent *event) Q_DECL_OVERRIDE
Definition: TopWidget.cpp:1597
bool init(Kwave::TopWidget *top_widget)
QList< Kwave::FileContext * > detachAllContexts()
Definition: TopWidget.cpp:520
static QString mimeTypeOf(const QUrl &url)
QAction * m_action_redo
Definition: TopWidget.h:377
int instanceNr() const
Definition: FileContext.h:136
bool connect(Kwave::StreamObject &source, const char *output, Kwave::StreamObject &sink, const char *input)
Definition: Connect.cpp:48
void setItemVisible(const QString &uid, bool show)
QString windowCaption(bool with_modified) const
Definition: App.h:44
sample_index_t length() const
Definition: Selection.h:66
void toolbarFileOpen()
Definition: TopWidget.h:207
const char name[16]
Definition: memcpy.c:510
Manager class for access to Kwave&#39;s menu subsystem.
Definition: MenuManager.h:46
Kwave::App & m_application
Definition: TopWidget.h:340
QAction * m_action_copy
Definition: TopWidget.h:383
QLabel * m_lbl_status_size
Definition: TopWidget.h:392
void connectContext(Kwave::FileContext *context)
Definition: TopWidget.cpp:167
void toolbarFileSaveAs()
Definition: TopWidget.h:213
QAction * m_action_close
Definition: TopWidget.h:371
static int error(QWidget *widget, QString message, QString caption=QString())
Definition: MessageBox.cpp:126
void switchGuiType(Kwave::TopWidget *top, GuiType new_type)
Definition: App.cpp:307
QString signalName() const
void selectItem(const QString &group, const QString &uid)
sample_index_t length() const
Definition: FileInfo.cpp:400
#define TOOLBAR_FILE
Definition: TopWidget.cpp:89
GuiType guiType() const
Definition: App.h:119
TopWidget(Kwave::App &app)
Definition: TopWidget.cpp:102
QAction * m_action_undo
Definition: TopWidget.h:374
double rate() const
bool createMainWidget(const QSize &preferred_size)
virtual QString name() const
Definition: Label.cpp:74
virtual void closeEvent(QCloseEvent *e) Q_DECL_OVERRIDE
Definition: TopWidget.cpp:1541
int forwardCommand(const QString &command)
Definition: TopWidget.cpp:871
unsigned int toUInt()
Definition: Parser.cpp:231
#define TOOLBAR_EDIT
Definition: TopWidget.cpp:92
#define TOOLBAR_ZOOM
Definition: TopWidget.cpp:98
double toDouble()
Definition: Parser.cpp:262
QList< Kwave::App::FileAndInstance > openFiles() const
Definition: TopWidget.cpp:504
void resetToolbarToDefaults()
Definition: TopWidget.cpp:1453
unsigned int tracks() const
Definition: FileInfo.cpp:445
QAction * m_action_save_as
Definition: TopWidget.h:368
void toolbarEditCopy()
Definition: TopWidget.h:228
static QUrl toUrl(const QString &command)
Definition: Parser.cpp:336
virtual void dropEvent(QDropEvent *event) Q_DECL_OVERRIDE
Definition: TopWidget.cpp:1609
QAction * m_action_cut
Definition: TopWidget.h:380
void subWindowDeleted(QObject *obj)
Definition: TopWidget.cpp:1573
static QString decodingFilter()
int newWindow(const QUrl &url)
Definition: App.cpp:224
QWidget * mainWidget() const
const QCommandLineParser * cmdline() const
Definition: App.h:130
int executeCommand(const QString &command)
void clipboardChanged(bool data_available)
Definition: TopWidget.cpp:1293
Kwave::MenuManager * m_menu_manager
Definition: TopWidget.h:356
void showInSplashSreen(const QString &message)
Definition: TopWidget.cpp:1547
void toolbarEditDelete()
Definition: TopWidget.h:237
Kwave::PlayerToolBar * m_toolbar_record_playback
Definition: TopWidget.h:350
void updateRecentFiles()
Definition: TopWidget.cpp:1300
const QString & firstParam()
Definition: Parser.cpp:168
#define _(m)
Definition: memcpy.c:66
void toolbarEditErase()
Definition: TopWidget.h:234
static bool canDecode(const QMimeData *source)
Definition: FileDrag.h:34
QMap< QMdiSubWindow *, Kwave::FileContext * > m_context_map
Definition: TopWidget.h:347
int executeCommand(const QString &command)
Definition: TopWidget.cpp:706
QStringList recentFiles() const
Definition: App.h:113
QString Q_DECL_EXPORT urlScheme()
Definition: Utils.cpp:177
Kwave::FileContext * newFileContext()
Definition: TopWidget.cpp:203
QPair< QString, int > FileAndInstance
Definition: App.h:60
bool isEmpty() const
Definition: FileContext.h:117
static void Q_DECL_EXPORT log(const QObject *sender, LogLevel level, const QString &msg)
Definition: Logger.cpp:103
unsigned int bits() const
Definition: FileInfo.cpp:430
Kwave::PluginManager * pluginManager() const
Kwave::SignalManager * signalManager() const
QAction * m_action_delete
Definition: TopWidget.h:389
#define CASE_COMMAND(x)
Definition: TopWidget.cpp:86
void insertContext(Kwave::FileContext *context)
Definition: TopWidget.cpp:572
static QString escape(const QString &text)
Definition: Parser.cpp:277
void addRecentFile(const QString &filename)
Definition: App.cpp:202
int executeCommand(const QString &command)
Definition: App.cpp:178
bool toplevelWindowHasClosed(Kwave::TopWidget *todel)
Definition: App.cpp:280
int executeCommand(const QString &command)
int newSignal(sample_index_t samples, double rate, unsigned int bits, unsigned int tracks)
Definition: TopWidget.cpp:1102
static bool canDecode(const QString &mimetype_name)
QString command()
Definition: Parser.h:47
Kwave::ZoomToolBar * m_toolbar_zoom
Definition: TopWidget.h:353
int openRecent(const QString &str)
Definition: TopWidget.cpp:1075
void toolbarFileNew()
Definition: TopWidget.h:204
#define TOOLBAR_RECORD_PLAY
Definition: TopWidget.cpp:95
QLabel * m_lbl_status_cursor
Definition: TopWidget.h:398
const QString & nextParam()
Definition: Parser.cpp:175
Kwave::FileContext * currentContext() const
Definition: TopWidget.cpp:232
int executeCommand(const QString &command)
void addNumberedMenuEntry(const QString &uid, const QString &entry, const QString &param)
void toolbarFileClose()
Definition: TopWidget.h:216
bool closeAllSubWindows()
Definition: TopWidget.cpp:887