1 /*!
2  * @file mainwindow.h
3  * @brief Member definitions for the MainWindow top-level UI class.
4  *
5  *
6  *      Copyright 2009 - 2017 <qmidiarp-devel@lists.sourceforge.net>
7  *
8  *      This program is free software; you can redistribute it and/or modify
9  *      it under the terms of the GNU General Public License as published by
10  *      the Free Software Foundation; either version 2 of the License, or
11  *      (at your option) any later version.
12  *
13  *      This program is distributed in the hope that it will be useful,
14  *      but WITHOUT ANY WARRANTY; without even the implied warranty of
15  *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  *      GNU General Public License for more details.
17  *
18  *      You should have received a copy of the GNU General Public License
19  *      along with this program; if not, write to the Free Software
20  *      Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
21  *      MA 02110-1301, USA.
22  *
23  */
24 
25 #ifndef MAINWINDOW_H
26 #define MAINWINDOW_H
27 
28 #include <QApplication>
29 #include <QCloseEvent>
30 #include <QMessageBox>
31 #include <QMainWindow>
32 #include <QToolBar>
33 
34 #include "logwidget.h"
35 #include "midicctable.h"
36 #include "passwidget.h"
37 #include "globstore.h"
38 
39 #ifdef NSM
40 #include "nsm.h"
41 #endif
42 
43 
44 static const char ABOUTMSG[] =
45             "<html> <p><b><big>" APP_NAME " " PACKAGE_VERSION "</big></b></p>"
46             "<p>(C) 2009 - 2017 Frank Kober<br/>"
47             "(C) 2011 Nedko Arnaudov<br/>"
48             "(C) 2009 Guido Scholz<br/>"
49             "(C) 2002-2003 Matthias Nagorni (SuSE AG Nuremberg)<br/></p>"
50             "<p><b>Contributions</b><br/>"
51             "Roy Vegard Ovesen (work on nsm support)<br/></p>"
52             "<p><b>Translations</b><br/>"
53             "Pavel Fric<br/>"
54             "Pedro Lopez-Cabanillas<br/>"
55             "Robert Dietrich<br/></p>"
56             "<p>For getting support please type <b>man qmidiarp</b> or go to<br/>"
57             "<a href=\"http://qmidiarp.sourceforge.net\">"
58             "http://qmidiarp.sourceforge.net</a></p>"
59             APP_NAME " is licensed under the GPLv2.</b></p></html>";
60 
61 /*!
62  * The MainWindow class is the main UI that holds functions to manage global
63  * QMidiArp parameters and modules and to load and save parameters to
64  * disk. The constructor sets up all main window elements including
65  * toolbars and menus. It instantiates the LogWidget, PassWidget,
66  * MidiCCTable and their DockWidget windows. It also instantiates the
67  * Engine widget holding the lists of modules.
68 
69  * @brief Top-level UI class. Instantiates Engine
70  */
71 class MainWindow : public QMainWindow
72 {
73     Q_OBJECT
74 
75   private:
76     static int sigpipe[2];
77     bool alsaMidi;
78     QSpinBox *tempoSpin;
79     PassWidget *passWidget;
80     GrooveWidget *grooveWidget;
81     LogWidget *logWidget;
82     GlobStore *globStore;
83     Engine *engine;
84     MidiCCTable *midiCCTable;
85     QString lastDir, filename;
86     QStringList patternNames, patternPresets;
87     QStringList recentFiles;
88     QDockWidget *logWindow, *grooveWindow, *passWindow, *globStoreWindow;
89 
90     QToolBar *controlToolBar, *fileToolBar;
91     QAction *runAction, *addArpAction;
92     QAction *addLfoAction, *addSeqAction;
93     QAction *fileNewAction, *fileOpenAction, *fileSaveAction, *fileSaveAsAction;
94     QAction *fileQuitAction;
95     QAction *midiClockAction, *jackSyncAction;
96     QAction *showAllIOAction, *hideAllIOAction;
97     QMenu* fileRecentlyOpenedFiles;
98 
99 #ifdef NSM
100     static nsm_client_t *nsm;
101     QString configFile;
102 #endif
103 /*!
104 * @brief  opens a file dialog and calls MainWindow::openFile().
105 * It is called by MainWindow::fileOpen().
106 */
107     void chooseFile();
108 /*! @brief  checks whether parameter modifications
109  * were done after the last save.
110  *
111  * If yes, it queries the user how to handle unsaved changes using a
112  * message box.
113  * @return True if the parameters can be overwritten
114  */
115     bool isSave();
116     void updateWindowTitle();
117 /*!
118 * @brief  opens a QMidiArp XML session file named
119 * MainWindow::filename for write using QXmlStreamReader.
120 *
121 * It writes global and GUI parameters and and calls the
122 * block writers in the module widgets.
123 *
124 * @return True if write was successful
125 */
126     bool saveFile();
127 /*! @brief  opens a file dialog and appends the file extension to
128  * the chosen name if not present. It is called by fileSaveAs.
129  * @return True if a file name was successfully chosen
130  */
131     bool saveFileAs();
132 /*!
133 * @brief  returns the result of Engine::isModified
134 * @return True if unsaved parameter modifications exist in any module
135 *
136 */
137     bool isModified();
138 /*!
139 * @brief  creates and adds a new MidiArp to Engine.
140 *
141 * It also creates and adds the associated ArpWidget
142 * and DockWidget to the corresponding lists in Engine. It sets
143 * the ManageBox::name and ManageBox::ID as the current count of the
144 * Engine::midiArpList.
145 *
146 * @param name Name attribute of the created arpeggiator module
147 * @param fromfile Set to True if module is added by a file read
148 * @param inOutVisible Set to True if In-Out panel should be shown
149 */
150     void addArp(const QString& name, bool fromfile = false,
151                 bool inOutVisible = true);
152 /*!
153 * @brief  creates and adds a new MidiLfo to Engine.
154 *
155 * It also creates and adds the associated LfoWidget
156 * and DockWidget to the corresponding lists in Engine. It sets
157 * the ManageBox::name and ManageBox::ID as the current count of the
158 * Engine::midiLfoList.
159 *
160 * @param name Name attribute of this LFO module
161 * @param fromfile Set to True if module is added by a file read
162 * @param clonefrom Set to the ID to clone this module from,
163 * -1 for a new module (default)
164 * @param inOutVisible Set to True if In-Out panel should be shown
165 */
166     void addLfo(const QString& name, bool fromfile = false,
167                 int clonefrom = -1, bool inOutVisible = true);
168 /*!
169 * @brief  creates and adds a new MidiSeq to Engine.
170 *
171 * It also creates and adds the associated SeqWidget
172 * and DockWidget to the corresponding lists in Engine. It sets
173 * the ManageBox::name and ManageBox::ID as the current count of the
174 * Engine::midiSeqList.
175 *
176 * @param name Name attribute of this module
177 * @param fromfile Set to True if module is added by a file read
178 * @param clonefrom Set to the ID to clone this module from,
179 * -1 for a new module (default)
180 * @param inOutVisible Set to True if In-Out panel should be shown
181 */
182     void addSeq(const QString& name, bool fromfile = false,
183                 int clonefrom = -1, bool inOutVisible = true);
184 /*!
185 * @brief  wraps the given widget in a QDockWidget and adds
186 * it to the list in Engine.
187 *
188 * @param *moduleWidget The QWidget to be embedded
189 * @param name Name attribute of this module
190 * @param count DockWidget list location at which the window is insertet
191 */
192     void appendDock(QWidget *moduleWidget, const QString& name, int count);
193 /*!
194 * @brief  reads global parameter block from an XML session
195 * stream using the QXmlStreamReader passed by the caller.
196 *
197 * @param xml Reference to QXmlStreamReader containing the open XML stream
198 */
199     void readFilePartGlobal(QXmlStreamReader& xml);
200 /*!
201 * @brief  reads the module parameter blocks from the XML stream
202 *  by calling their read functions.
203 *
204 * It uses the first three letters of the module name to distinguish their
205 * type. It creates the according module components and calls their UI
206 * widgets, which fill them with the parameters found in the same stream.
207 * Calls Engine->NNNWidget->readData, where NNN is Arp, Lfo or Seq.
208 *
209 * @param xml Reference to QXmlStreamReader containing the open XML stream
210 */
211     void readFilePartModules(QXmlStreamReader& xml);
212 /*!
213 * @brief  reads the GUI settings block
214 * from the XML session stream passed by the caller.
215 *
216 * @param xml Reference to QXmlStreamReader containing the XML stream
217 */
218     void readFilePartGUI(QXmlStreamReader& xml);
219 /*!
220 * @brief  prepends a filename at the beginning of the recently
221 * opened files list.
222 *
223 * It is called by openFile if the opening was successful.
224 * @param fn Filename with full path to be prepended
225 * @param lst The list of recently opened files
226 * @see setupRecentFilesMenu
227 */
228     void addRecentlyOpenedFile(const QString &fn, QStringList &lst);
229 /*!
230 * @brief  checks whether a .qmidiarprc file is present
231 * and creates it with default settings, if not.
232 * @return True if the file existed, False if it was created now.
233 * @see readRcFile, updatePatternPresets
234 *
235 */
236     bool checkRcFile();
237 /*!
238 * @brief  writes the .qmidiarprc text resource file.
239 *
240 * It is called on program exit and upon modification of the
241 * Arp preset list.
242 * @see readRcFile, updatePatternPresets
243 */
244     void writeRcFile();
245 /*!
246 * @brief  reads all elements from the .qmidiarprc text
247 * resource file.
248 *
249 * The file contains the Arp preset patterns, the last
250 * GUI state, settings made in the Settings dialog (PassWidget) and
251 * the recent files and path.
252 * This function is called from the MainWindow constructor.
253 * @see readRcFile, updatePatternPresets
254 */
255     void readRcFile();
256 /*!
257 * @brief  checks if there are no more modules present and sets
258 * some GUI elements accordingly if so.
259 *
260 * It is called by removeArp, removeSeq and removeLfo.
261 * @see checkIfFirstModule
262 */
263     void checkIfLastModule();
264 /*!
265 * @brief  checks if there were no modules present, i.e.
266 * if the module we just created is the first one, and sets some GUI
267 * elements accordingly if so.
268 *
269 * It is called by addArp, addSeq and addLfo.
270 * @see checkIfLastModule
271 */
272     void checkIfFirstModule();
273 /*!
274 * @brief  removes and deletes all modules from the
275 * lists.
276 *
277 * It removes all module components, i.e. the Midi workers, UI widgets
278 * and DockWidgets. It disconnects jack transport and stops the
279 * transport if running.
280 */
281     void clear();
282 /*!
283 * @brief  disables or enables GUI elements depending on
284 * synchronization mode
285 *
286 * It distinguishes between internal and external (MIDI Clock or
287 * JACK Transport) sync. This is necessary since some operations would
288 * break the synchronization, and some others would become useless.
289 * @param on True sets the GUI for external synchronization, False
290 * returns it to internal clock state.
291 *
292 * @see midiClockToggle, jackSyncToggle
293 */
294     void setGUIforExtSync(bool on);
295 
296 /*! @brief Handler for system signals (SIGUSR1, SIGINT...).
297  * This function writes a message to the pipe and leaves as soon as possible
298  */
299     static void handleSignal(int);
300 /*! @brief  sets up a QSocketNotifier forwarding UNIX signals
301  * as Qt signals to provide Ladish L1 support.
302  *
303  * @return True if installation succeeded.
304 */
305     bool installSignalHandlers();
306 /*!
307 * @brief allows ignoring one XML element in the XML stream
308 * passed by the caller.
309 *
310 * It also advances the stream read-in. It is used to
311 * ignore unknown elements for both-ways-compatibility
312 *
313 * @param xml reference to QXmlStreamReader containing the open XML stream
314 */
315     void skipXmlElement(QXmlStreamReader& xml);
316 
317   protected:
318 /*!
319 * @brief Handler for close events either by quit or if the user closes
320 * the window.
321 *
322 */
323     void closeEvent(QCloseEvent*);
324 
325 /* PUBLIC MEMBERS */
326   public:
327 /*!
328 * @param p_portCount Number of registered MIDI output ports
329 * @param p_alsamidi Start as ALSA MIDI client
330 * @param *execName Name of the application's executable
331 */
332     MainWindow(int p_portCount, bool p_alsamidi, char *execName);
333     ~MainWindow();
334 
335     bool jackFailed;
336 
337 /* SIGNALS */
338   signals:
339     void newTempo(int);
340 #ifdef NSM
341     void nsmOpenFile(const QString & name);
342 #endif
343 
344 /* PUBLIC SLOTS */
345   public slots:
346 /*!
347 * @brief Slot for "New..." UI entries.
348 *
349 * This function calls MainWindow::clear
350 * and empties the current MainWindow::filename.
351 */
352     void fileNew();
353 /*!
354 * @brief Slot for "Open..." UI entries.
355 *
356 * This function calls MainWindow::isSave
357 *  and MainWindow::chooseFile if all changes are in saved state.
358 */
359     void fileOpen();
360 /*!
361 * @brief  opens a QMidiArp XML session file for reading
362 * using QXmlStreamReader.
363 *
364 * It queries XML block elements and calls the block readers
365 * MainWindow::readFilePartGlobal, MainWindow::readFilePartModules,
366 * MainWindow::readFilePartGUI. It sets MainWindow::lastDir according to
367 * the file path given with fn and calls MainWindow::updateWindowTitle.
368 * It updates MainWindow::recentFiles list.
369 *
370 * @param fn File name to open including its absolute path
371 */
372     void openFile(const QString&);
373 /*!
374 * @brief Slot for file Save GUI elements.
375 *
376 * This function calls either
377 * MainWindow::saveFileAs
378 * or MainWindow::saveFile depending on whether the MainWindow::filename is set.
379 *
380 */
381     void fileSave();
382 /*!
383 * @brief Slot for file SaveAs GUI elements.
384 *
385 * This function calls saveFileAs.
386 *
387 */
388     void fileSaveAs();
389 /*!
390 * @brief Slot for "Add Arpeggiator" menu entry and toolbutton.
391 *
392 * It asks for
393 * the module name and calls MainWindow::addArp with that name.
394 */
395     void arpNew();
396 /*!
397 * @brief Slot for "Add LFO" menu entry and toolbutton.
398 *
399 * It asks for
400 * the module name and calls MainWindow::addLfo with that name.
401 */
402     void lfoNew();
403 /*!
404 * @brief Slot for "Add Sequencer" menu entry and toolbutton.
405 *
406 * It asks for
407 * the module name and calls MainWindow::addSeq with that name.
408 */
409     void seqNew();
410 /*!
411 * @brief  removes and deletes an Arpeggiator module.
412 *
413 * It removes all components MidiArp, ArpWidget and
414 * DockWidget from the corresponding lists in Engine.
415 *
416 * @param index The Engine::midiArpList index of the arpeggiator to remove
417 */
418     void removeArp(int index);
419 /*!
420 * @brief  removes and deletes an LFO module.
421 *
422 * It removes all components MidiLfo, LfoWidget and
423 * DockWidget from the corresponding lists in Engine.
424 *
425 * @param index The Engine::midiLfoList index of the LFO to
426 */
427     void removeLfo(int index);
428 /*!
429 * @brief  removes and deletes a Seq module.
430 *
431 * It removes all components MidiSeq, SeqWidget and
432 * DockWidget from the corresponding lists in Engine.
433 *
434 * @param index The Engine::midiSeqList index of the sequencer to remove
435 */
436     void removeSeq(int index);
437 /*!
438 * @brief  duplicates and adds a MidiLfo to the Engine.
439 *
440 * @param ID List ID of the module to copy
441 */
442     void cloneLfo(int ID);
443 /*!
444 * @brief  duplicates and adds a MidiSeq to the Engine.
445 *
446 * @param ID List ID of the module to copy
447 */
448     void cloneSeq(int ID);
449 
450     void helpAbout();
451     void helpAboutQt();
452 /*! @brief Slot for tempo spinBox changes.
453 * This function forwards a new tempo value to the driver.
454 * @param tempo The new tempo to be set
455 *
456 */
457     void updateTempo(int tempo);
458 /*! @brief Slot for Engine::tempoUpdated() signal.
459 *
460 * This function displays a new tempo value in the tempo spin box.
461 * @param p_tempo The new tempo to be displayed
462 *
463 */
464     void displayTempo(double p_tempo);
465 /*! @brief Slot for MainWindow::runAction ToolButton.
466 * This function calls Engine::setStatus() and disables the
467 * MainWindow::tempoSpin box
468 * @param on True to set Transport to running state
469 *
470 */
471     void updateTransportStatus(bool on);
472 /*! @brief Slot for midiClock ToolButton.
473 * This function toggles SeqDriver between MIDI Clock and internal clock
474 * operation.
475 * @param on True sets SeqDriver to MIDI Clock operation, false returns to
476 * internal clock.
477 *
478 */
479     void midiClockToggle(bool on);
480 /*! @brief Slot for jackSync ToolButton.
481 * This function toggles the driver between Jack Transport and internal
482 * clock operation.
483 * @param on True sets driver to JACK Transport operation, false
484 * returns to internal clock.
485 *
486 * @see jackSyncToggle
487 */
488     void jackSyncToggle(bool on);
489 /*! @brief Slot for the JackDriver::j_shutdown() signal.
490 * This function switches the sync to internal clock operation and
491 * deactivates the Jack Transport Action.
492 * @see jackSyncToggle
493 */
494     void jackShutdown();
495 /*! @brief Slot for "Midi Controllers" menu action. This function displays
496  * the MidiCC Dialog window.
497 */
498     void showMidiCCDialog();
499     void showIO();
500     void hideIO();
501 /*!
502 * @brief  appends or deletes an Arp pattern preset.
503 *
504 * If index = 0, it appends an Arp pattern. If index > 0 it deletes
505 * the Arp pattern at index from the list.
506 * It deploys the new pattern preset list to all Arp modules by calling
507 * Engine::updatePatternPresets and writes
508 * the new preset list to the resource file.
509 * This function is a signal slot for ArpWidget::presetsChanged and
510 * called whenever the preset list is modified in one Arp module.
511 *
512 * @param n Name of the preset to append if index = 0
513 * @param p Text sequence of te preset to append if index = 0
514 * @param index List index of the preset to delete or 0 to append a preset
515 * @see ArpWidget::presetsChanged, Engine::updatePatternPresets
516 */
517     void updatePatternPresets(const QString& n, const QString& p, int index);
518 /*! @brief Slot for fileRecentlyOpenedFiles.
519 * This function proceeds to open the file selected in the recent
520 * files menu.
521 *
522 * @see recentFileActivated, setupRecentFilesMenu
523 */
524     void recentFileActivated(QAction*);
525 /*!
526 * @brief  populates the recent files menu.
527 *
528 * It is called by the constructor MainWindow::MainWindow
529 * @see recentFileActivated, addRecentlyOpenedFile
530 */
531     void setupRecentFilesMenu();
532 /*! @brief Slot to give response to an incoming pipe message (Ladish L1).
533  *
534  * This function calls fileSave upon reception of SIGUSR1 and close upon
535  * reception of SIGINT.
536  * @param fd UNIX signal number
537 */
538     void signalAction(int);
539 
540 /*! @brief Slot to give response to an incoming Jack Session event.
541  *
542  * @param ev Type of the event (internal to QMidiarp)
543 */
544     void jsAction(int ev);
545     void ctb_update_orientation(Qt::Orientation orient);
546     void ftb_update_orientation(Qt::Orientation orient);
547 
548 #ifdef NSM
549     static int cb_nsm_open(const char *name, const char *display_name, const char *client_id, char **out_msg, void *userdata);
550     static int cb_nsm_save(char **out_msg, void *userdata);
551 
552     int nsm_open(const char *name, const char *display_name, const char *client_id, char **out_msg);
553     int nsm_save(char **out_msg);
554 #endif
555 };
556 
557 #endif
558