kwave  18.07.70
App.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  App.cpp - The Kwave main application
3  -------------------
4  begin : Wed Feb 28 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 
20 #include <errno.h>
21 
22 #include <QCommandLineParser>
23 #include <QFile>
24 #include <QMetaType>
25 #include <QMutableListIterator>
26 #include <QString>
27 
28 #include <KConfig>
29 #include <KConfigGroup>
30 #include <KHelpClient>
31 #include <KLocalizedString>
32 #include <KSharedConfig>
33 
34 #include "libkwave/ClipBoard.h"
35 #include "libkwave/LabelList.h"
36 #include "libkwave/Logger.h"
37 #include "libkwave/MemoryManager.h"
38 #include "libkwave/Parser.h"
39 #include "libkwave/PluginManager.h"
40 #include "libkwave/Sample.h"
41 #include "libkwave/SampleArray.h"
42 #include "libkwave/SignalManager.h"
43 #include "libkwave/String.h"
44 #include "libkwave/Utils.h"
45 
46 #include "App.h"
47 #include "FileContext.h"
48 #include "Splash.h"
49 #include "TopWidget.h"
50 
52 #define MAX_RECENT_FILES 20
53 
54 //***************************************************************************
55 Kwave::App::App(int &argc, char **argv)
56  :QApplication(argc, argv),
57  m_cmdline(Q_NULLPTR),
58  m_recent_files(),
59  m_top_widgets(),
60  m_gui_type(Kwave::App::GUI_TAB)
61 {
62  qRegisterMetaType<Kwave::SampleArray>("Kwave::SampleArray");
63  qRegisterMetaType<Kwave::LabelList>("Kwave::LabelList");
64  qRegisterMetaType<sample_index_t>("sample_index_t");
65  qRegisterMetaType<Kwave::MetaDataList>("Kwave::MetaDataList");
66 
67  // connect the clipboard
68  connect(QApplication::clipboard(),
69  SIGNAL(changed(QClipboard::Mode)),
71  SLOT(slotChanged(QClipboard::Mode)));
72 }
73 
74 //***************************************************************************
75 void Kwave::App::processCmdline(QCommandLineParser *cmdline)
76 {
77  Q_ASSERT(cmdline);
78  if (!cmdline) return;
79 
81  m_cmdline->parse(arguments());
82 
83  // read the configured user interface type
84  QString result;
85  KConfigGroup cfg = KSharedConfig::openConfig()->group("Global");
86  result = cfg.readEntry("UI Type");
87  if (result == _("SDI")) {
89  } else if (result == _("MDI")) {
91  } else if (result == _("TAB")) {
93  }
94  // else: use default
95 
96  // if user interface type is given as cmdline parameter: use that one
97  if (m_cmdline->isSet(_("gui"))) {
98  QString arg = m_cmdline->value(_("gui")).toUpper();
99  bool valid = false;
100  if (arg == _("SDI")) {
102  valid = true;
103  } else if (arg == _("MDI")) {
105  valid = true;
106  } else if (arg == _("TAB")) {
108  valid = true;
109  }
110  // else: use previous setting
111 
112  // save this setting
113  if (valid && (arg != result))
114  cfg.writeEntry(_("UI Type"), arg);
115  }
116 }
117 
118 //***************************************************************************
120 {
121  saveRecentFiles();
122  m_recent_files.clear();
123 
124  // let remaining cleanup handlers run (deferred delete)
125  processEvents(QEventLoop::ExcludeUserInputEvents);
126 }
127 
128 //***************************************************************************
129 int Kwave::App::newInstance(const QStringList &args, const QString &dir)
130 {
131  int retval = 0;
132  Q_UNUSED(dir);
133 
134  Q_ASSERT(m_cmdline);
135  if (!m_cmdline) return -EINVAL;
136 
137  m_cmdline->parse(args);
138 
139  static bool first_time = true;
140  if (first_time) {
141  first_time = false;
142 
143  // open the log file if given on the command line
144  if (m_cmdline->isSet(_("logfile"))) {
145  if (!Kwave::Logger::open(m_cmdline->value(_("logfile"))))
146  exit(-1);
147  }
148 
149  Kwave::Splash::showMessage(i18n("Reading configuration..."));
150  readConfig();
151 
152  // close when the last window closed
153  connect(this, SIGNAL(lastWindowClosed()), this, SLOT(quit()));
154  }
155 
156  QStringList params = m_cmdline->positionalArguments();
157 
158  // only one parameter -> open with empty window
159  if (params.isEmpty()) {
160  retval = newWindow(QUrl());
161  } else {
162  // open a window for each file specified in the
163  // command line an load it
164  foreach (const QString &name, params) {
165  retval = newWindow(QUrl::fromUserInput(name));
166  }
167  }
168  return retval;
169 }
170 
171 //***************************************************************************
172 bool Kwave::App::isOK() const
173 {
174  return (!m_top_widgets.isEmpty());
175 }
176 
177 //***************************************************************************
178 int Kwave::App::executeCommand(const QString &command)
179 {
180  Kwave::Parser parser(command);
181  if (parser.command() == _("newwindow")) {
182  int retval = 0;
183  if (parser.hasParams()) {
184  retval = newWindow(QUrl(parser.params().at(0)));
185  } else {
186  retval = newWindow(QUrl(QString()));
187  }
188  return (retval == 0) ? 0 : -EIO;
189  } else if (parser.command() == _("openrecent:clear")) {
190  m_recent_files.clear();
191  saveRecentFiles();
192  emit recentFilesChanged();
193  } else if (parser.command() == _("help")) {
194  KHelpClient::invokeHelp();
195  } else {
196  return ENOSYS; // command not implemented (here)
197  }
198  return 0;
199 }
200 
201 //***************************************************************************
202 void Kwave::App::addRecentFile(const QString &newfile)
203 {
204  if (!newfile.length()) return;
205 
206  // remove old entries if present
207  m_recent_files.removeAll(newfile);
208 
209  // shorten the list down to (MAX_RECENT_FILES - 1) entries
210  while (m_recent_files.count() >= MAX_RECENT_FILES)
211  m_recent_files.removeLast();
212 
213  // insert the new entry at top
214  m_recent_files.prepend(newfile);
215 
216  // save the list of recent files
217  saveRecentFiles();
218 
219  // update all toplevel widgets
220  emit recentFilesChanged();
221 }
222 
223 //***************************************************************************
224 int Kwave::App::newWindow(const QUrl &url)
225 {
226  int retval = 0;
227  Kwave::TopWidget *new_top_widget = Q_NULLPTR;
228 
229  Kwave::Splash::showMessage(i18n("Opening main window..."));
230 
231  switch (m_gui_type) {
232  case Kwave::App::GUI_TAB: /* FALLTHROUGH */
233  case Kwave::App::GUI_MDI:
234  // re-use the last top widget to open the file
235  if (!m_top_widgets.isEmpty())
236  new_top_widget = m_top_widgets.last();
237  break;
238  case Kwave::App::GUI_SDI:
239  // always create a new top widget, except when handling commands
240  if ( (url.scheme().toLower() == Kwave::urlScheme()) &&
241  !m_top_widgets.isEmpty() )
242  new_top_widget = m_top_widgets.last();
243  break;
244  }
245 
246  if (!new_top_widget) {
247  new_top_widget = new(std::nothrow) Kwave::TopWidget(*this);
248  if (!new_top_widget || !new_top_widget->init()) {
249  // init failed
250  qWarning("ERROR: initialization of TopWidget failed");
251  delete new_top_widget;
252  return ECANCELED;
253  }
254 
255  if (!m_top_widgets.isEmpty()) {
256  // create a new widget with the same geometry as
257  // the last created one
258  const QRect &geom = m_top_widgets.last()->geometry();
259  // calling setGeometry(geom) would overlap :-(
260  new_top_widget->resize(geom.width(), geom.height());
261  }
262 
263  m_top_widgets.append(new_top_widget);
264  new_top_widget->show();
265 
266  // inform the widget about changes in the list of recent files
267  connect(this, SIGNAL(recentFilesChanged()),
268  new_top_widget, SLOT(updateRecentFiles()));
269  }
270 
271  retval = (!url.isEmpty()) ? new_top_widget->loadFile(url) : 0;
272  if (retval == ECANCELED)
273  delete new_top_widget;
274 
275  Kwave::Splash::showMessage(i18n("Startup done"));
276  return retval;
277 }
278 
279 //***************************************************************************
281 {
282  // save the list of recent files
283  saveRecentFiles();
284 
285  // remove the toplevel widget from our list
286  if (m_top_widgets.contains(todel))
287  m_top_widgets.removeAll(todel);
288 
289  // if list is empty -> no more windows there -> exit application
290  return (m_top_widgets.isEmpty());
291 }
292 
293 //***************************************************************************
294 QList<Kwave::App::FileAndInstance> Kwave::App::openFiles() const
295 {
296  QList<Kwave::App::FileAndInstance> all_files;
297  foreach (Kwave::TopWidget *topwidget, m_top_widgets) {
298  if (!topwidget) continue;
299  QList<Kwave::App::FileAndInstance> files = topwidget->openFiles();
300  if (!files.isEmpty())
301  all_files += files;
302  }
303  return all_files;
304 }
305 
306 //***************************************************************************
308 {
309  Q_ASSERT(top);
310  if (!top) return;
311  if (new_type == m_gui_type) return;
312 
313  // collect all contexts of all toplevel widgets, and delete all except
314  // the new top widget that is calling us
315  QList<Kwave::FileContext *> all_contexts;
316  QMutableListIterator<Kwave::TopWidget *> it(m_top_widgets);
317  while (it.hasNext()) {
318  Kwave::TopWidget *topwidget = it.next();
319  if (!topwidget) { it.remove(); continue; }
320  QList<Kwave::FileContext *> contexts = topwidget->detachAllContexts();
321  if (!contexts.isEmpty()) all_contexts += contexts;
322  if (topwidget != top) {
323  it.remove();
324  delete topwidget;
325  }
326  }
327 
328  // from now on use the new GUI type
329  m_gui_type = new_type;
330 
331  // at this point we should have exactly one toplevel widget without
332  // context and a list of contexts (which may be empty)
333  if (!all_contexts.isEmpty()) {
334  bool first = true;
335  foreach (Kwave::FileContext *context, all_contexts) {
336  Kwave::TopWidget *top_widget = Q_NULLPTR;
337 
338  switch (m_gui_type) {
339  case GUI_SDI:
340  if (!context->isEmpty() || (all_contexts.count() == 1)) {
341  // either the context contains some signal and is worth
342  // assigning it to a toplevel widget, or it is the one
343  // and only context
344  if (first) {
345  // the context reuses the calling toplevel widget
346  top_widget = top;
347  first = false;
348  } else {
349  // for all other contexts we have to create a new
350  // toplevel widget
351  top_widget = new(std::nothrow) Kwave::TopWidget(*this);
352  if (!top_widget || !top_widget->init()) {
353  // init failed
354  qWarning("ERROR: initialization of TopWidget failed");
355  delete top_widget;
356  delete context;
357  }
358  m_top_widgets.append(top_widget);
359  top_widget->show();
360 
361  // inform the widget about changes in the list of
362  // recent files
363  connect(this, SIGNAL(recentFilesChanged()),
364  top_widget, SLOT(updateRecentFiles()));
365  }
366  } else {
367  // probably this context is only executing a script and
368  // has (no longer) any valid signal. We need to assign
369  // it to a toplevel widget for processing further
370  // commands but reduce the reference count to make it
371  // vanish as soon as the script terminates.
372  context->setParent(top);
373  context->release();
374  }
375  break;
376  case GUI_MDI: /* FALLTHROUGH */
377  case GUI_TAB:
378  // all contexts go into the same toplevel widget
379  top_widget = top;
380  break;
381  }
382 
383  if (top_widget) top_widget->insertContext(context);
384  }
385  } else {
386  // give our one and only toplevel widget a default context
387  top->insertContext(Q_NULLPTR);
388  }
389 }
390 
391 //***************************************************************************
393 {
394  KConfigGroup cfg = KSharedConfig::openConfig()->group("Recent Files");
395 
396  QString num;
397  for (int i = 0 ; i < MAX_RECENT_FILES; i++) {
398  num.setNum(i);
399  if (i < m_recent_files.count())
400  cfg.writeEntry(num, m_recent_files[i]);
401  else
402  cfg.deleteEntry(num);
403  }
404 
405  cfg.sync();
406 }
407 
408 //***************************************************************************
410 {
411  QString result;
412  QString key;
413  const KConfigGroup cfg = KSharedConfig::openConfig()->group("Recent Files");
414 
415  for (unsigned int i = 0 ; i < MAX_RECENT_FILES; i++) {
416  key = QString::number(i); // generate number
417 
418  // read corresponding entry, which is stored in UTF-8
419  result = cfg.readEntry(key);
420  if (result.length()) {
421  QFile file(result);
422 
423  //check if file exists and insert it if not already present
424  if (file.exists() && (m_recent_files.contains(result) == 0))
425  m_recent_files.append(result);
426  }
427  }
428 }
429 
430 //***************************************************************************
431 //***************************************************************************
int newInstance(const QStringList &args, const QString &dir)
Definition: App.cpp:129
App(int &argc, char **argv)
Definition: App.cpp:55
int loadFile(const QUrl &url)
Definition: TopWidget.cpp:991
static bool Q_DECL_EXPORT open(const QString &filename)
Definition: Logger.cpp:59
QCommandLineParser * m_cmdline
Definition: App.h:179
Definition: App.h:33
static void showMessage(const QString &message)
Definition: Splash.cpp:115
static ClipBoard & instance()
Definition: ClipBoard.cpp:39
void setParent(Kwave::TopWidget *top_widget)
QStringList m_recent_files
Definition: App.h:186
QList< FileAndInstance > openFiles() const
Definition: App.cpp:294
GuiType
Definition: App.h:49
QList< Kwave::FileContext * > detachAllContexts()
Definition: TopWidget.cpp:520
bool connect(Kwave::StreamObject &source, const char *output, Kwave::StreamObject &sink, const char *input)
Definition: Connect.cpp:48
GuiType m_gui_type
Definition: App.h:192
Definition: App.h:44
const char name[16]
Definition: memcpy.c:510
void readConfig()
Definition: App.cpp:409
void switchGuiType(Kwave::TopWidget *top, GuiType new_type)
Definition: App.cpp:307
void processCmdline(QCommandLineParser *cmdline)
Definition: App.cpp:75
QList< Kwave::App::FileAndInstance > openFiles() const
Definition: TopWidget.cpp:504
void recentFilesChanged()
virtual bool isOK() const
Definition: App.cpp:172
bool hasParams()
Definition: Parser.h:57
int newWindow(const QUrl &url)
Definition: App.cpp:224
const QCommandLineParser * cmdline() const
Definition: App.h:130
#define _(m)
Definition: memcpy.c:66
virtual ~App()
Definition: App.cpp:119
const QStringList & params()
Definition: Parser.h:52
QString Q_DECL_EXPORT urlScheme()
Definition: Utils.cpp:177
bool isEmpty() const
Definition: FileContext.h:117
void insertContext(Kwave::FileContext *context)
Definition: TopWidget.cpp:572
#define MAX_RECENT_FILES
Definition: App.cpp:52
void addRecentFile(const QString &filename)
Definition: App.cpp:202
bool toplevelWindowHasClosed(Kwave::TopWidget *todel)
Definition: App.cpp:280
int executeCommand(const QString &command)
Definition: App.cpp:178
QString command()
Definition: Parser.h:47
QList< Kwave::TopWidget * > m_top_widgets
Definition: App.h:189
void saveRecentFiles()
Definition: App.cpp:392