diff --git a/kwave/App.cpp b/kwave/App.cpp index 67a1cee0..14749554 100644 --- a/kwave/App.cpp +++ b/kwave/App.cpp @@ -1,428 +1,431 @@ /*************************************************************************** App.cpp - The Kwave main application ------------------- begin : Wed Feb 28 2001 copyright : (C) 2001 by Thomas Eschenbacher email : Thomas.Eschenbacher@gmx.de ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include #include "libkwave/ClipBoard.h" #include "libkwave/LabelList.h" #include "libkwave/Logger.h" #include "libkwave/MemoryManager.h" #include "libkwave/Parser.h" #include "libkwave/PluginManager.h" #include "libkwave/Sample.h" #include "libkwave/SampleArray.h" #include "libkwave/SignalManager.h" #include "libkwave/String.h" #include "libkwave/Utils.h" #include "App.h" #include "FileContext.h" #include "Splash.h" #include "TopWidget.h" /** maximum number of recent files */ #define MAX_RECENT_FILES 20 //*************************************************************************** Kwave::App::App(int &argc, char **argv) :QApplication(argc, argv), m_cmdline(0), m_recent_files(), m_top_widgets(), m_gui_type(Kwave::App::GUI_TAB) { qRegisterMetaType("Kwave::SampleArray"); qRegisterMetaType("Kwave::LabelList"); qRegisterMetaType("sample_index_t"); qRegisterMetaType("Kwave::MetaDataList"); // connect the clipboard connect(QApplication::clipboard(), SIGNAL(changed(QClipboard::Mode)), &(Kwave::ClipBoard::instance()), SLOT(slotChanged(QClipboard::Mode))); } //*************************************************************************** void Kwave::App::processCmdline(QCommandLineParser *cmdline) { Q_ASSERT(cmdline); if (!cmdline) return; m_cmdline = cmdline; m_cmdline->parse(arguments()); // read the configured user interface type QString result; KConfigGroup cfg = KSharedConfig::openConfig()->group("Global"); result = cfg.readEntry("UI Type"); if (result == _("SDI")) { m_gui_type = Kwave::App::GUI_SDI; } else if (result == _("MDI")) { m_gui_type = Kwave::App::GUI_MDI; } else if (result == _("TAB")) { m_gui_type = Kwave::App::GUI_TAB; } // else: use default // if user interface type is given as cmdline parameter: use that one if (m_cmdline->isSet(_("gui"))) { QString arg = m_cmdline->value(_("gui")).toUpper(); bool valid = false; if (arg == _("SDI")) { m_gui_type = Kwave::App::GUI_SDI; valid = true; } else if (arg == _("MDI")) { m_gui_type = Kwave::App::GUI_MDI; valid = true; } else if (arg == _("TAB")) { m_gui_type = Kwave::App::GUI_TAB; valid = true; } // else: use previous setting // save this setting if (valid && (arg != result)) cfg.writeEntry(_("UI Type"), arg); } } //*************************************************************************** Kwave::App::~App() { saveRecentFiles(); m_recent_files.clear(); + + // let remaining cleanup handlers run (deferred delete) + processEvents(QEventLoop::ExcludeUserInputEvents); } //*************************************************************************** int Kwave::App::newInstance(const QStringList &args, const QString &dir) { int retval = 0; Q_UNUSED(dir); Q_ASSERT(m_cmdline); if (!m_cmdline) return -EINVAL; m_cmdline->parse(args); static bool first_time = true; if (first_time) { first_time = false; // open the log file if given on the command line if (m_cmdline->isSet(_("logfile"))) { if (!Kwave::Logger::open(m_cmdline->value(_("logfile")))) exit(-1); } Kwave::Splash::showMessage(i18n("Reading configuration...")); readConfig(); // close when the last window closed connect(this, SIGNAL(lastWindowClosed()), this, SLOT(quit())); } QStringList params = m_cmdline->positionalArguments(); // only one parameter -> open with empty window if (params.isEmpty()) { retval = newWindow(QUrl()); } else { // open a window for each file specified in the // command line an load it foreach (const QString &name, params) { retval = newWindow(QUrl::fromUserInput(name)); } } return retval; } //*************************************************************************** bool Kwave::App::isOK() const { return (!m_top_widgets.isEmpty()); } //*************************************************************************** int Kwave::App::executeCommand(const QString &command) { Kwave::Parser parser(command); if (parser.command() == _("newwindow")) { int retval = 0; if (parser.hasParams()) { retval = newWindow(QUrl(parser.params().at(0))); } else { retval = newWindow(QUrl(QString())); } return (retval == 0) ? 0 : -EIO; } else if (parser.command() == _("openrecent:clear")) { m_recent_files.clear(); saveRecentFiles(); emit recentFilesChanged(); } else if (parser.command() == _("help")) { KHelpClient::invokeHelp(); } else { return ENOSYS; // command not implemented (here) } return 0; } //*************************************************************************** void Kwave::App::addRecentFile(const QString &newfile) { if (!newfile.length()) return; // remove old entries if present m_recent_files.removeAll(newfile); // shorten the list down to (MAX_RECENT_FILES - 1) entries while (m_recent_files.count() >= MAX_RECENT_FILES) m_recent_files.removeLast(); // insert the new entry at top m_recent_files.prepend(newfile); // save the list of recent files saveRecentFiles(); // update all toplevel widgets emit recentFilesChanged(); } //*************************************************************************** int Kwave::App::newWindow(const QUrl &url) { int retval = 0; Kwave::TopWidget *new_top_widget = 0; Kwave::Splash::showMessage(i18n("Opening main window...")); switch (m_gui_type) { case Kwave::App::GUI_TAB: /* FALLTHROUGH */ case Kwave::App::GUI_MDI: // re-use the last top widget to open the file if (!m_top_widgets.isEmpty()) new_top_widget = m_top_widgets.last(); break; case Kwave::App::GUI_SDI: // always create a new top widget, except when handling commands if ( (url.scheme().toLower() == Kwave::urlScheme()) && !m_top_widgets.isEmpty() ) new_top_widget = m_top_widgets.last(); break; } if (!new_top_widget) { new_top_widget = new(std::nothrow) Kwave::TopWidget(*this); if (!new_top_widget || !new_top_widget->init()) { // init failed qWarning("ERROR: initialization of TopWidget failed"); delete new_top_widget; return ECANCELED; } if (!m_top_widgets.isEmpty()) { // create a new widget with the same geometry as // the last created one const QRect &geom = m_top_widgets.last()->geometry(); // calling setGeometry(geom) would overlap :-( new_top_widget->resize(geom.width(), geom.height()); } m_top_widgets.append(new_top_widget); new_top_widget->show(); // inform the widget about changes in the list of recent files connect(this, SIGNAL(recentFilesChanged()), new_top_widget, SLOT(updateRecentFiles())); } retval = (!url.isEmpty()) ? new_top_widget->loadFile(url) : 0; if (retval == ECANCELED) delete new_top_widget; Kwave::Splash::showMessage(i18n("Startup done")); return retval; } //*************************************************************************** bool Kwave::App::toplevelWindowHasClosed(Kwave::TopWidget *todel) { // save the list of recent files saveRecentFiles(); // remove the toplevel widget from our list if (m_top_widgets.contains(todel)) m_top_widgets.removeAll(todel); // if list is empty -> no more windows there -> exit application return (m_top_widgets.isEmpty()); } //*************************************************************************** QList Kwave::App::openFiles() const { QList all_files; foreach (Kwave::TopWidget *topwidget, m_top_widgets) { if (!topwidget) continue; QList files = topwidget->openFiles(); if (!files.isEmpty()) all_files += files; } return all_files; } //*************************************************************************** void Kwave::App::switchGuiType(Kwave::TopWidget *top, GuiType new_type) { Q_ASSERT(top); if (!top) return; if (new_type == m_gui_type) return; // collect all contexts of all toplevel widgets, and delete all except // the new top widget that is calling us QList all_contexts; QMutableListIterator it(m_top_widgets); while (it.hasNext()) { Kwave::TopWidget *topwidget = it.next(); if (!topwidget) { it.remove(); continue; } QList contexts = topwidget->detachAllContexts(); if (!contexts.isEmpty()) all_contexts += contexts; if (topwidget != top) { it.remove(); delete topwidget; } } // from now on use the new GUI type m_gui_type = new_type; // at this point we should have exactly one toplevel widget without // context and a list of contexts (which may be empty) if (!all_contexts.isEmpty()) { bool first = true; foreach (Kwave::FileContext *context, all_contexts) { Kwave::TopWidget *top_widget = 0; switch (m_gui_type) { case GUI_SDI: if (!context->isEmpty() || (all_contexts.count() == 1)) { // either the context contains some signal and is worth // assigning it to a toplevel widget, or it is the one // and only context if (first) { // the context reuses the calling toplevel widget top_widget = top; first = false; } else { // for all other contexts we have to create a new // toplevel widget top_widget = new(std::nothrow) Kwave::TopWidget(*this); if (!top_widget || !top_widget->init()) { // init failed qWarning("ERROR: initialization of TopWidget failed"); delete top_widget; delete context; } m_top_widgets.append(top_widget); top_widget->show(); // inform the widget about changes in the list of // recent files connect(this, SIGNAL(recentFilesChanged()), top_widget, SLOT(updateRecentFiles())); } } else { // probably this context is only executing a script and // has (no longer) any valid signal. We need to assign // it to a toplevel widget for processing further // commands but reduce the reference count to make it // vanish as soon as the script terminates. context->setParent(top); context->release(); } break; case GUI_MDI: /* FALLTHROUGH */ case GUI_TAB: // all contexts go into the same toplevel widget top_widget = top; break; } if (top_widget) top_widget->insertContext(context); } } else { // give our one and only toplevel widget a default context top->insertContext(0); } } //*************************************************************************** void Kwave::App::saveRecentFiles() { KConfigGroup cfg = KSharedConfig::openConfig()->group("Recent Files"); QString num; for (int i = 0 ; i < MAX_RECENT_FILES; i++) { num.setNum(i); if (i < m_recent_files.count()) cfg.writeEntry(num, m_recent_files[i]); else cfg.deleteEntry(num); } cfg.sync(); } //*************************************************************************** void Kwave::App::readConfig() { QString result; QString key; const KConfigGroup cfg = KSharedConfig::openConfig()->group("Recent Files"); for (unsigned int i = 0 ; i < MAX_RECENT_FILES; i++) { key = QString::number(i); // generate number // read corresponding entry, which is stored in UTF-8 result = cfg.readEntry(key); if (result.length()) { QFile file(result); //check if file exists and insert it if not already present if (file.exists() && (m_recent_files.contains(result) == 0)) m_recent_files.append(result); } } } //*************************************************************************** //*************************************************************************** diff --git a/libkwave/PluginManager.h b/libkwave/PluginManager.h index 08911ed0..bc1051bf 100644 --- a/libkwave/PluginManager.h +++ b/libkwave/PluginManager.h @@ -1,390 +1,390 @@ /*************************************************************************** PluginManager.h - manager class for kwave's plugins ------------------- begin : Sun Aug 27 2000 copyright : (C) 2000 by Thomas Eschenbacher email : Thomas.Eschenbacher@gmx.de ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #ifndef PLUGIN_MANAGER_H #define PLUGIN_MANAGER_H #include "config.h" #include #include #include #include #include #include #include #include #include "libkwave/InsertMode.h" #include "libkwave/Sample.h" #include "libkwave/ViewManager.h" class QLibrary; class QString; class QStringList; class KPluginFactory; namespace Kwave { class PlaybackController; class PlaybackDeviceFactory; class PlayBackParam; class Plugin; class PluginContext; class SampleSink; class SignalManager; class Writer; /** * Manages the loading, initializing, starting, running and closing * of the plugins of kwave. Each instance of a TopWidget creates a * new instance of the PluginManager to be independent from other * toplevel widgets. */ class Q_DECL_EXPORT PluginManager: public QObject { Q_OBJECT public: /** * Constructor. * @param parent reference to the toplevel widget (our parent) * @param signal_manager reference to a SignalManager */ PluginManager(QWidget *parent, Kwave::SignalManager &signal_manager); /** * Default destructor */ virtual ~PluginManager(); /** * Tries to load all plugins. If a pesistent plugin is found, * it will stay loaded in memory, all other (non-persistent) * plugins will be unloaded afterwards. This also filters out * all plugins that do not correctly load. * @internal used once by each toplevel window at startup */ void loadAllPlugins(); /** * Stops all currently running plugins */ void stopAllPlugins(); /** * Executes a plugin in the context of a given parent widget. * @param name the name of the plugin * @param params pointer to a parameter list * or null if defaults should be used * @return zero on success or negative error code */ int executePlugin(const QString &name, QStringList *params); /** * Returns true if there is no running plugin that blocks * a "close" operation. */ bool canClose(); /** Returns true if at least one plugin is currently running */ bool onePluginRunning(); /** * Waits until all currently running actions have completed. */ void sync(); /** * Loads a plugin, calls it's setup function and then closes * it again. The parameters will be loaded before the setup * and saved if the setup has not been aborted. * @param name the name of the plugin * @param params pointer to a parameter list * or null if defaults should be used * @retval 0 if succeeded and accepted * @retval 1 if canceled * @retval -1 if failed */ int setupPlugin(const QString &name, const QStringList ¶ms); /** * loads a plugin's default parameters from the user's * configuration file. If nothing is found in the config file, * the return value will be 0. If the current version number of * the plugin does not match the version number in the config file, * the return value will also be 0. * @param name the name of the plugin * @return list of strings */ QStringList defaultParams(const QString &name); /** * Returns the length of the current signal in samples. * If no signal is present the return value will be 0. */ sample_index_t signalLength(); /** * Returns the current sample rate in samples per second. * If no signal is present the return value will be 0. */ double signalRate(); /** * Returns the start of the selection. If nothing is currently * selected this will be the first sample (0). */ sample_index_t selectionStart(); /** * Returns the end of the selection. If nothing is currently * selected this will be the last sample (length-1). */ sample_index_t selectionEnd(); /** * Sets the current start and length of the selection to new values. * @param offset index of the first sample * @param length number of samples */ void selectRange(sample_index_t offset, sample_index_t length); /** * Opens a Kwave::MultiTrackSink for playback purposes. * @param tracks number of tracks * @param playback_params points to a class that holds all playback * parameters. If null, the default parameters * of the current signal will be used * @return a multitrack sink that receives the playback stream */ Kwave::SampleSink *openMultiTrackPlayback( unsigned int tracks, const Kwave::PlayBackParam *playback_params = 0 ); /** * Returns a reference to the current playback controller. This is * only needed for plugins doing playback. */ Kwave::PlaybackController &playbackController(); /** * assigns a new parent widget, to be used for messages * @param new_parent pointer to a QWidget */ inline void setParentWidget(QWidget *new_parent) { m_parent_widget = new_parent; } /** returns a pointer to the parent widget */ inline QPointer parentWidget() { return m_parent_widget; } /** returns a reference to our signal manager */ inline Kwave::SignalManager &signalManager() { return m_signal_manager; } /** * Insert a new signal view into this widget (or the upper/lower * dock area. * @param view the signal view, must not be a null pointer * @param controls a widget with controls, optionally, can be null */ void insertView(Kwave::SignalView *view, QWidget *controls); /** * registers a view manager, must only be called once! */ void registerViewManager(Kwave::ViewManager *view_manager); /** * Enqueues a command that will be processed threadsafe in the X11 * thread. */ void enqueueCommand(const QString &command); /** * Searches the standard KDE data directories for plugins (through * the KDE's standard search algorithm) and creates a map of * plugin names and file names. First it collects a list of * filenames and then filters it to sort out invalid entries. */ void searchPluginModules(); /** structure with information about a plugin */ typedef struct { QString m_name; /**< name of the plugin */ QString m_author; /**< name of the author */ QString m_description; /**< short description */ QString m_version; /**< settings version */ KPluginFactory *m_factory; /**< plugin factory */ int m_use_count; /**< usage counter */ } PluginModule; /** * returns a list with info of all known plugins * @todo rename to pluginModuleList */ const QList pluginInfoList() const; /** * Migrate a plugin to the currently active file context (which * might be different from the one that is currently executing * the plugin). The plugin will be removed from our lists and * inserted into the currently active plugin manager instance. * @param plugin the plugin to migrate */ void migratePluginToActiveContext(Kwave::Plugin *plugin); /** Let this instance be the active one */ void setActive() { m_active_instance = this; } signals: /** * Forwards commands to the parent TopWidget execute a command */ void sigCommand(const QString &command); /** * Informs all plugins and client windows that we close down */ void sigClosed(); /** * Informs the plugins that the name of the signal has changed. * This might be used to update the caption of a window. */ void sigSignalNameChanged(const QString &name); /** * informs about progress, e.g. for showing a message in * a splash screen or status bar. */ void sigProgress(const QString &message); public slots: /** * Notify all plugins that the signal or file is to be closed */ void signalClosed(); - /** - * Will be connected to the plugin's "closed" signal. - * @param p pointer to the plugin to be closed - */ - void pluginClosed(Kwave::Plugin *p); - /** * Called if the name of the current signal has changed. This will be * forwarded to all plugins by emitting the signal sigSignalNameChanged. * @see sigSignalNameChanged() */ void setSignalName(const QString &name); private slots: + /** + * Will be connected to the plugin's "closed" signal. + * @param p pointer to the plugin to be closed + */ + void pluginClosed(Kwave::Plugin *p); + /** called when a plugin has started (running) it's worker thread */ void pluginStarted(Kwave::Plugin *p); /** called when a plugin has finished it's worker thread */ void pluginDone(Kwave::Plugin *p); private: /** typedef: QPointer to a Kwave::Plugin */ typedef QPointer KwavePluginPointer; /** typedef: list of pointers to kwave plugins */ typedef QList< KwavePluginPointer > PluginList; /** typedef: mutable iterator for PluginList */ typedef QMutableListIterator< KwavePluginPointer > PluginListMutableIterator; /** typedef: const iterator for PluginList */ typedef QListIterator< KwavePluginPointer > PluginListIterator; private: /** * Creates an instance of a plugin. * @param name the name of the plugin (filename) * @return pointer to the loaded plugin or zero if the * plugin was not found or invalid */ Kwave::Plugin *createPluginInstance(const QString &name); /** * Saves a plugin's default parameters to the user's configuration * file. The whole section in the configuration file will be deleted * before saving the new settings in order to wipe out invalid * entries and settings that belong to an older version. * @param name the name of the plugin * @param params a list of configuration strings */ void savePluginDefaults(const QString &name, QStringList ¶ms); /** connects all signals of and for a plugin */ void connectPlugin(Kwave::Plugin *plugin); /** connects all signals from and to a plugin */ void disconnectPlugin(Kwave::Plugin *plugin); private: /** pointer to the currently active instance */ static Kwave::PluginManager *m_active_instance; /** * map with plugin information: key = short name of the plugin, * data = plugin info (description, author, version etc...) */ static QMap m_plugin_modules; /** list of all plugins that were loaded by this instance */ PluginList m_plugin_instances; /** list of currently running plugins */ PluginList m_running_plugins; /** reference to our parent toplevel widget */ QPointer m_parent_widget; /** reference to our signal manager */ Kwave::SignalManager &m_signal_manager; /** interface for registering a SignalView */ ViewManager *m_view_manager; }; } #endif /* PLUGIN_MANAGER_H */ //*************************************************************************** //***************************************************************************