1 /***************************************************************************
2     kwave/FileContext.h  -  Context of a Loaded File
3 			     -------------------
4     begin                : 2009-12-31
5     copyright            : (C) 2009 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 #ifndef KWAVE_FILE_CONTEXT_H
19 #define KWAVE_FILE_CONTEXT_H
20 
21 #include "config.h"
22 
23 #include <QtGlobal>
24 #include <QAtomicInt>
25 #include <QElapsedTimer>
26 #include <QList>
27 #include <QObject>
28 #include <QPointer>
29 #include <QString>
30 #include <QTimer>
31 #include <QUrl>
32 
33 #include "libkwave/MetaDataList.h"
34 #include "libkwave/Sample.h"
35 
36 class QApplication;
37 class QMdiSubWindow;
38 class QSize;
39 class QTextStream;
40 class QWidget;
41 
42 class QUrl;
43 
44 namespace Kwave
45 {
46 
47     class App;
48     class MainWidget;
49     class Parser;
50     class PluginManager;
51     class SignalManager;
52     class TopWidget;
53     class Zoomable;
54 
55     class Q_DECL_EXPORT FileContext: public QObject
56     {
57 	Q_OBJECT
58     public:
59 	/**
60 	 * Constructor. Creates a new toplevel window, signal manager,
61 	 * plugin manager and so on.
62 	 * @param app reference to the Kwave application
63 	 */
64 	explicit FileContext(Kwave::App &app);
65 
66 	/**
67 	 * Destructor
68 	 */
69 	virtual ~FileContext();
70 
71 	/**
72 	 * initializes the instance
73 	 * @param top_widget pointer to the toplevel widget
74 	 * @return true if successful
75 	 */
76 	bool init(Kwave::TopWidget *top_widget);
77 
78 	/**
79 	 * create a main widget, within the MDI area
80 	 * or toplevel widget in case of SDI interface
81 	 * @param preferred_size preferred size of the main widget
82 	 * @return true if successful, false if failed
83 	 */
84 	bool createMainWidget(const QSize &preferred_size);
85 
86 	/**
87 	 * migrate this context to a different toplevel widget
88 	 * @param top_widget pointer to the new toplevel widget
89 	 */
90 	void setParent(Kwave::TopWidget *top_widget);
91 
92 	/** returns a reference to the application instance */
app()93 	Kwave::App           &app() const { return m_application; }
94 
95 	/**
96 	 * returns a pointer to the main widget of this context
97 	 */
98 	QWidget              *mainWidget() const;
99 
100 	/** returns a pointer to the signal manager of this context */
101 	Kwave::SignalManager *signalManager() const;
102 
103 	/** returns a pointer to the plugin manager of this context */
104 	Kwave::PluginManager *pluginManager() const;
105 
106 	/**
107 	 * Returns a pointer to a GUI element that receives zoom info
108 	 * (the MainWidget)
109 	 */
110 	Kwave::Zoomable *zoomable() const;
111 
112 	/**
113 	 * Returns whether this context is empty (has a main widget) or not.
114 	 * @retval true if the context is empty
115 	 * @retval false if the context has a main widget
116 	 */
isEmpty()117 	inline bool isEmpty() const { return m_main_widget.isNull(); }
118 
119 	/**
120 	 * Returns whether this context is active or not.
121 	 * @retval true if the context is active
122 	 * @retval false if the context is inactive
123 	 */
isActive()124 	inline bool isActive() const { return m_active; }
125 
126 	/**
127 	 * Returns true it this context has a signal (file is loaded)
128 	 * or the context is executing a script
129 	 */
130 	bool isInUse() const;
131 
132 	/** returns the name of the signal */
133 	QString signalName() const;
134 
135 	/** returns the instance of the loaded file or -1 */
instanceNr()136 	inline int instanceNr() const { return m_instance_nr; }
137 
138 	/**
139 	 * returns a string suitable as window caption
140 	 * @param with_modified if true, include the "modified" state
141 	 */
142 	QString windowCaption(bool with_modified) const;
143 
144 	/**
145 	 * Loads a batch file into memory, parses and executes
146 	 * all commands in it.
147 	 * @param url URL of the macro (batch file) to be loaded
148 	 */
149 	int loadBatch(const QUrl &url);
150 
151 	/**
152 	 * Saves the current file.
153 	 * @return zero if succeeded, non-zero if failed
154 	 */
155 	int saveFile();
156 
157 	/**
158 	 * Opens a dialog for saving the current file.
159 	 * @param filename the name of the new file
160 	 *                 or empty string to open the File/SaveAs dialog
161 	 * @param selection if set to true, only the current selection
162 	 *        will be saved
163 	 * @return zero if succeeded, non-zero if failed
164 	 */
165 	int saveFileAs(const QString &filename, bool selection = false);
166 
167 	/**
168 	 * Closes the current file.
169 	 * If the file has been modified and the user wanted to cancel
170 	 * the close operation, the file will not get closed and the
171 	 * function returns with false.
172 	 * @return true if closing is allowed, false if canceled
173 	 */
174 	bool closeFile();
175 
176     protected:
177 	friend class App;
178 	friend class TopWidget;
179 	friend class UsageGuard;
180 
181 	/**
182 	 * increments the usage count of this context, prevents it from
183 	 * being deleted
184 	 */
185 	void use();
186 
187 	/**
188 	 * decrements the usage count of this context, and if it has reached
189 	 * zero this instance will be deleted (delayed)
190 	 */
191 	void release();
192 
193     signals:
194 
195 	/**
196 	 * emitted when there is a status bar message to show
197 	 * @param message the status bar message, already localized
198 	 * @param ms the time in milliseconds to show the message
199 	 */
200 	void sigStatusBarMessage(const QString &message, unsigned int ms);
201 
202 	/**
203 	 * emitted when the zoom factor of the corresponding main widget
204 	 * has changed
205 	 * @param context contains "this"
206 	 * @param zoom new zoom factor
207 	 */
208 	void sigZoomChanged(Kwave::FileContext *context, double zoom);
209 
210 	/**
211 	 * emitted when the meta data of the current signal has changed
212 	 * @param meta_data the new meta data, after the change
213 	 */
214 	void sigMetaDataChanged(Kwave::MetaDataList meta_data);
215 
216 	/**
217 	 * emits a change in the selected range.
218 	 * @param offset index of the first selected items
219 	 * @param length number of selected items
220 	 */
221 	void sigSelectionChanged(sample_index_t offset, sample_index_t length);
222 
223 	/**
224 	 * Emitted if the state or description of undo/redo has changed. If
225 	 * undo or redo is unavailable the description will be zero.
226 	 */
227 	void sigUndoRedoInfo(const QString &undo, const QString &redo);
228 
229 	/** emitted when the visible range has changed */
230 	void sigVisibleRangeChanged(sample_index_t offset,
231 	                            sample_index_t visible,
232 	                            sample_index_t total);
233 
234 	/**
235 	 * Emitted if the signal changes from non-modified to modified
236 	 * state or vice-versa.
237 	 */
238 	void sigModified();
239 
240 	/**
241 	 * emitted when the context is about to be destroyed
242 	 * (in the context of it's destructor)
243 	 */
244 	void destroyed(Kwave::FileContext *context);
245 
246     public slots:
247 
248 	/**
249 	 * Execute a Kwave text command
250 	 * @param command a text command
251 	 * @return zero if succeeded or negative error code if failed
252 	 */
253 	int executeCommand(const QString &command);
254 
255     private slots:
256 
257 	/**
258 	 * called when the current file context has changed
259 	 * @param context the new file context (can be "this")
260 	 */
261 	void contextSwitched(Kwave::FileContext *context);
262 
263 	/**
264 	 * emits a sigZoomChanged(this, zoom) when the zoom has changed
265 	 * in the m_main_widget
266 	 */
267 	void forwardZoomChanged(double zoom);
268 
269 	/**
270 	 * Called when the playback position has changed
271 	 * @param offset the current playback position [samples]
272 	 */
273 	void updatePlaybackPos(sample_index_t offset);
274 
275 	/**
276 	 * Called when the meta data of the current signal has changed
277 	 * @param meta_data the new meta data, after the change
278 	 */
279 	void metaDataChanged(Kwave::MetaDataList meta_data);
280 
281 	/**
282 	 * Called when the number of selected samples has changed.
283 	 * @param offset index of the first selected sample
284 	 * @param length number of selected samples
285 	 */
286 	void selectionChanged(sample_index_t offset, sample_index_t length);
287 
288 	/**
289 	 * Called when the undo or redo action has changed.
290 	 * @param undo description of the last undo action
291 	 * @param redo description of the last redo action
292 	 */
293 	void setUndoRedoInfo(const QString &undo, const QString &redo);
294 
295 	/**
296 	 * Called after changes of the currently visible view range
297 	 * @param offset index of the first visible sample
298 	 * @param visible number of visible samples
299 	 * @param total length of the whole signal
300 	 */
301 	void visibleRangeChanged(sample_index_t offset,
302 	                         sample_index_t visible,
303 	                         sample_index_t total);
304 
305 	/**
306 	 * called if the signal now or no longer is modified
307 	 */
308 	void modifiedChanged();
309 
310 	/** process the next delayed command from m_delayed_command_queue */
311 	void processDelayedCommand();
312 
313     private:
314 
315 	class UsageGuard
316 	{
317 	public:
318 	    /**
319 	     * constructor, increments use count
320 	     * @param context the file context to use
321 	     */
UsageGuard(Kwave::FileContext * context)322 	    explicit UsageGuard(Kwave::FileContext *context)
323 		:m_context(context)
324 	    {
325 		if (m_context) m_context->use();
326 	    }
327 
328 	    /** destructor, decrements use count of the context */
~UsageGuard()329 	    virtual ~UsageGuard()
330 	    {
331 		if (m_context) m_context->release();
332                 m_context = Q_NULLPTR;
333 	    }
334 
335 	private:
336 	    QPointer<Kwave::FileContext> m_context;
337 	};
338 
339     private:
340 
341 	/**
342 	 * should be called when this context got active, to update
343 	 * the status bar, toolbar etc.
344 	 */
345 	void activated();
346 
347 	/**
348 	 * Show a message in the status bar
349 	 * @param msg the status bar message, already localized
350 	 * @param ms the time in milliseconds to show the message
351 	 */
352 	void statusBarMessage(const QString &msg, unsigned int ms);
353 
354 	/**
355 	 * Parses a text stream line by line and executes each line
356 	 * as a command until all commands are done or the first one fails.
357 	 * @param stream a QTextStream to read from
358 	 * @return zero if successful, non-zero error code if a command failed
359 	 */
360 	int parseCommands(QTextStream &stream);
361 
362 	/**
363 	 * enqueues a command for later execution
364 	 * @param delay milliseconds to wait before execution
365 	 * @param command the command to execute
366 	 */
367 	void enqueueCommand(unsigned int delay, const QString &command);
368 
369 	/**
370 	 * Discards all changes to the current file and loads
371 	 * it again.
372 	 * @return zero if succeeded or error code
373 	 */
374 	int revert();
375 
376 	/**
377 	 * delegate a command to a plugin
378 	 * @param plugin name of a plugin to delegate the command to
379 	 * @param parser the parser with the parts of the command
380 	 * @param param_count required number of parameters
381 	 */
382 	int delegateCommand(const char *plugin,
383                             Kwave::Parser &parser,
384                             unsigned int param_count);
385 
386     private:
387 
388 	/** usage counter [0...n] */
389 	QAtomicInt m_use_count;
390 
391 	/** reference to the global Kwave application object */
392 	Kwave::App &m_application;
393 
394 	/** instance of our toplevel window */
395 	QPointer<Kwave::TopWidget> m_top_widget;
396 
397 	/** instance of our main widget */
398 	QPointer<Kwave::MainWidget> m_main_widget;
399 
400 	/** instance of our signal manager */
401 	QPointer<Kwave::SignalManager> m_signal_manager;
402 
403 	/** instance of our plugin manager */
404 	QPointer<Kwave::PluginManager> m_plugin_manager;
405 
406 	/** if true, this context is active, otherwise it is inactive */
407 	bool m_active;
408 
409 	/** last zoom factor */
410 	double m_last_zoom;
411 
412 	/** last playback position, only valid if playback is running */
413 	sample_index_t m_last_playback_pos;
414 
415 	/** last status bar message */
416 	QString m_last_status_message_text;
417 
418 	/** time when the last status message has been shown */
419 	QElapsedTimer m_last_status_message_timer;
420 
421 	/** number of milliseconds the status message should be shown */
422 	unsigned int m_last_status_message_ms;
423 
424 	/** name of the last undo action */
425 	QString m_last_undo;
426 
427 	/** name of the last redo action */
428 	QString m_last_redo;
429 
430 	/** instance of the loaded file or -1 */
431 	int m_instance_nr;
432 
433 	/** timer for delayed commands */
434 	QTimer m_delayed_command_timer;
435 
436 	/** queue for delayed execution of commands */
437 	QList< QPair<unsigned int, QString> > m_delayed_command_queue;
438 
439     };
440 
441 }
442 
443 #endif /* KWAVE_FILE_CONTEXT_H */
444 
445 //***************************************************************************
446 //***************************************************************************
447