1 //=========================================================
2 //  MusE
3 //  Linux Music Editor
4 //  $Id: app.cpp,v 1.113.2.68 2009/12/21 14:51:51 spamatica Exp $
5 //
6 //  (C) Copyright 1999-2011 Werner Schweer (ws@seh.de)
7 //  (C) Copyright 2011-2016 Tim E. Real (terminator356 on sourceforge)
8 //
9 //  This program is free software; you can redistribute it and/or
10 //  modify it under the terms of the GNU General Public License
11 //  as published by the Free Software Foundation; version 2 of
12 //  the License, or (at your option) any later version.
13 //
14 //  This program is distributed in the hope that it will be useful,
15 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
16 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 //  GNU General Public License for more details.
18 //
19 //  You should have received a copy of the GNU General Public License
20 //  along with this program; if not, write to the Free Software
21 //  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
22 //
23 //=========================================================
24 
25 #include <QDesktopWidget>
26 #include <QClipboard>
27 #include <QMessageBox>
28 #include <QShortcut>
29 #include <QWhatsThis>
30 #include <QSettings>
31 #include <QMdiArea>
32 #include <QMdiSubWindow>
33 #include <QSocketNotifier>
34 #include <QStyleFactory>
35 #include <QTextStream>
36 #include <QInputDialog>
37 #include <QAction>
38 #include <QStringList>
39 #include <QPushButton>
40 #include <QDir>
41 #include <QStatusBar>
42 #if QT_VERSION >= 0x050b00
43 #include <QScreen>
44 #endif
45 
46 #include <samplerate.h>
47 
48 #include <errno.h>
49 #include <iostream>
50 #include <algorithm>
51 #include <typeinfo>
52 
53 #include "app.h"
54 #include "master/lmaster.h"
55 #include "al/dsp.h"
56 #include "audio.h"
57 #include "audiodev.h"
58 #include "audioprefetch.h"
59 // FIXME Move cliplist into components ?
60 #include "cliplist/cliplist.h"
61 #include "debug.h"
62 #include "components/didyouknow.h"
63 #include "drumedit.h"
64 #include "components/filedialog.h"
65 #include "gconfig.h"
66 #include "globals.h"
67 #include "gui.h"
68 #include "helper.h"
69 #include "wave_helper.h"
70 #include "icons.h"
71 #include "listedit.h"
72 #include "master/masteredit.h"
73 #include "midiseq.h"
74 #include "mitplugin.h"
75 #include "mittranspose.h"
76 #include "components/mixdowndialog.h"
77 #include "mrconfig.h"
78 #include "pianoroll.h"
79 #ifdef PYTHON_SUPPORT
80 #include "remote/pyapi.h"
81 #endif
82 #include "song.h"
83 #include "components/routepopup.h"
84 #include "components/savenewrevisiondialog.h"
85 #include "components/songinfo.h"
86 #include "ticksynth.h"
87 #include "tempo.h"
88 #include "tlist.h"
89 #include "waveedit.h"
90 #include "components/projectcreateimpl.h"
91 #include "widgets/menutitleitem.h"
92 #include "components/unusedwavefiles.h"
93 #include "functions.h"
94 #include "components/songpos_toolbar.h"
95 #include "components/sig_tempo_toolbar.h"
96 #include "songfile_discovery.h"
97 #include "pos.h"
98 #include "wave.h"
99 #include "wavepreview.h"
100 #include "shortcuts.h"
101 #include "rectoolbar.h"
102 #include "postoolbar.h"
103 #include "synctoolbar.h"
104 
105 #ifdef _WIN32
106 #include <Windows.h>
107 #endif
108 
109 // Forwards from header:
110 #include <QCloseEvent>
111 #include <QMenu>
112 #include <QToolBar>
113 #include <QToolButton>
114 #include <QProgressDialog>
115 #include <QTimer>
116 #include <QMdiSubWindow>
117 #include <QDockWidget>
118 #include "track.h"
119 #include "minstrument.h"
120 #include "midiport.h"
121 #include "part.h"
122 #include "synth.h"
123 #include "undo.h"
124 #include "appearance.h"
125 #include "arranger.h"
126 #include "arrangerview.h"
127 #include "amixer.h"
128 #include "bigtime.h"
129 //#include "cliplist.h"
130 #include "editinstrument.h"
131 #include "tools.h"
132 #include "genset.h"
133 #include "mrconfig.h"
134 #include "marker/markerview.h"
135 #include "metronome.h"
136 #include "conf.h"
137 #include "midifilterimpl.h"
138 #include "midiitransform.h"
139 #include "miditransform.h"
140 #include "midisyncimpl.h"
141 #include "scoreedit.h"
142 #include "shortcutconfig.h"
143 #include "transport.h"
144 #include "visibletracks.h"
145 #include "routedialog.h"
146 #include "cpu_toolbar.h"
147 #include "musemdiarea.h"
148 #include "snooper.h"
149 #include "xml.h"
150 #ifdef BUILD_EXPERIMENTAL
151   #include "rhythm.h"
152 #endif
153 
154 namespace MusECore {
155 extern void exitJackAudio();
156 extern void exitDummyAudio();
157 extern void exitOSC();
158 extern void exitMidiAlsa();
159 
160 #ifdef HAVE_RTAUDIO
161 extern void exitRtAudio();
162 #endif
163 }
164 
165 namespace MusEGui {
166 
167 extern void deleteIcons();
168 
169 static pthread_t watchdogThread;
170 //ErrorHandler *error;
171 
172 QStringList projectRecentList;
173 
174 #ifdef HAVE_LASH
175 #include <lash/lash.h>
176 lash_client_t * lash_client = 0;
177 #endif /* HAVE_LASH */
178 
179 int watchAudioPrefetch, watchMidi;
180 pthread_t splashThread;
181 
182 
183 
184 
185 
186 //---------------------------------------------------------
187 //   sleep function
188 //---------------------------------------------------------
microSleep(long msleep)189 void microSleep(long msleep)
190 {
191     int sleepOk=-1;
192 
193     while(sleepOk==-1)
194         sleepOk=usleep(msleep);
195 }
196 
197 //---------------------------------------------------------
198 //   seqStart
199 //---------------------------------------------------------
200 
seqStart()201 bool MusE::seqStart()
202       {
203       if(MusEGlobal::audio)
204       {
205         if(!MusEGlobal::audio->isRunning())
206         {
207           // Start the audio. (Re)connect audio inputs and outputs. Force-fill the audio pre-fetch buffers for the current cpos.
208           if(MusEGlobal::audio->start())
209           {
210             //
211             // wait for jack callback
212             //
213             for(int i = 0; i < 60; ++i)
214             {
215               if(MusEGlobal::audio->isRunning())
216                 break;
217               sleep(1);
218             }
219             if(!MusEGlobal::audio->isRunning())
220             {
221               QMessageBox::critical( MusEGlobal::muse, tr("Failed to start audio!"),
222                   tr("Timeout waiting for audio to run. Check if jack is running or try another driver.\n"));
223             }
224           }
225           else
226           {
227             QMessageBox::critical( MusEGlobal::muse, tr("Failed to start audio!"),
228                 tr("Was not able to start audio, check if jack is running or try another driver.\n"));
229           }
230         }
231       }
232       else
233         fprintf(stderr, "seqStart(): audio is NULL\n");
234 
235 
236       // Now it is safe to ask the driver for realtime priority
237 
238       int pfprio = 0;
239       // TODO: Hm why is prefetch priority so high again? Was it to overcome some race problems? Should it be lowest - disk thread?
240       if(MusEGlobal::audioDevice)
241       {
242         MusEGlobal::realTimePriority = MusEGlobal::audioDevice->realtimePriority();
243         if(MusEGlobal::debugMsg)
244           fprintf(stderr, "MusE::seqStart: getting audio driver MusEGlobal::realTimePriority:%d\n", MusEGlobal::realTimePriority);
245 
246         // NOTE: MusEGlobal::realTimeScheduling can be true (gotten using jack_is_realtime()),
247         //  while the determined MusEGlobal::realTimePriority can be 0.
248         // MusEGlobal::realTimePriority is gotten using pthread_getschedparam() on the client thread
249         //  in JackAudioDevice::realtimePriority() which is a bit flawed - it reports there's no RT...
250         if(MusEGlobal::realTimeScheduling)
251         {
252           if(MusEGlobal::realTimePriority - 5 >= 0)
253             pfprio = MusEGlobal::realTimePriority - 5;
254         }
255         // FIXME: The realTimePriority of the Jack thread seems to always be 5 less than the value passed to jackd command.
256       }
257       else
258         fprintf(stderr, "seqStart(): audioDevice is NULL\n");
259 
260       if(MusEGlobal::audioPrefetch)
261       {
262         if(!MusEGlobal::audioPrefetch->isRunning())
263         {
264           MusEGlobal::audioPrefetch->start(pfprio);
265           // In case prefetch is not filled, do it now.
266           MusEGlobal::audioPrefetch->msgSeek(MusEGlobal::audio->pos().frame(), true); // Force it upon startup only.
267         }
268       }
269       else
270         fprintf(stderr, "seqStart(): audioPrefetch is NULL\n");
271 
272       if(MusEGlobal::midiSeq)
273         MusEGlobal::midiSeq->start(0); // Prio unused, set in start.
274 
275       return true;
276       }
277 
278 //---------------------------------------------------------
279 //   stop
280 //---------------------------------------------------------
281 
seqStop()282 void MusE::seqStop()
283       {
284       // label sequencer as disabled before it actually happened to minimize race condition
285       MusEGlobal::midiSeqRunning = false;
286 
287       MusEGlobal::song->setStop(true);
288       MusEGlobal::song->setStopPlay(false);
289       if(MusEGlobal::midiSeq)
290          MusEGlobal::midiSeq->stop(true);
291       MusEGlobal::audio->stop(true);
292       MusEGlobal::audioPrefetch->stop(true);
293       if (MusEGlobal::realTimeScheduling && watchdogThread)
294             pthread_cancel(watchdogThread);
295       }
296 
297 //---------------------------------------------------------
298 //   seqRestart
299 //---------------------------------------------------------
300 
seqRestart()301 bool MusE::seqRestart()
302 {
303     bool restartSequencer = MusEGlobal::audio->isRunning();
304     if (restartSequencer) {
305           if (MusEGlobal::audio->isPlaying()) {
306                 MusEGlobal::audio->msgPlay(false);
307                 while (MusEGlobal::audio->isPlaying())
308                       qApp->processEvents();
309                 }
310           seqStop();
311           }
312 
313     if(!seqStart())
314         return false;
315 
316     MusEGlobal::audioDevice->graphChanged();
317     return true;
318 }
319 
addProjectToRecentList(const QString & name)320 void MusE::addProjectToRecentList(const QString& name)
321 {
322   if (projectRecentList.contains(name))
323     return;
324 
325   projectRecentList.push_front(name);
326   if (projectRecentList.size() > MusEGlobal::config.recentListLength)
327     projectRecentList.pop_back();
328 
329   saveProjectRecentList();
330 }
331 
saveProjectRecentList()332 void MusE::saveProjectRecentList()
333 {
334     // save "Open Recent" list
335     QString prjPath(MusEGlobal::configPath);
336     prjPath += "/projects";
337     QFile f(prjPath);
338     f.open(QIODevice::WriteOnly | QIODevice::Text);
339     if (f.exists()) {
340         QTextStream out(&f);
341         for (int i = 0; i < projectRecentList.size(); ++i) {
342             out << projectRecentList[i] << "\n";
343         }
344     }
345 }
346 
347 //---------------------------------------------------------
348 //   MusE
349 //---------------------------------------------------------
350 
MusE()351 MusE::MusE() : QMainWindow()
352       {
353       setIconSize(QSize(MusEGlobal::config.iconSize, MusEGlobal::config.iconSize));
354       setFocusPolicy(Qt::NoFocus);
355       MusEGlobal::muse      = this;    // hack
356       _isRestartingApp      = false;
357       midiSyncConfig        = nullptr;
358       midiRemoteConfig      = nullptr;
359       midiPortConfig        = nullptr;
360       metronomeConfig       = nullptr;
361       midiFileConfig        = nullptr;
362       midiFilterConfig      = nullptr;
363       midiInputTransform    = nullptr;
364 #ifdef BUILD_EXPERIMENTAL
365       midiRhythmGenerator   = nullptr;
366 #endif
367       globalSettingsConfig  = nullptr;
368       arrangerView          = nullptr;
369       softSynthesizerConfig = nullptr;
370       midiTransformerDialog = nullptr;
371       shortcutConfig        = nullptr;
372       appearance            = nullptr;
373       _snooperDialog        = nullptr;
374       //audioMixer            = 0;
375       mixer1                = nullptr;
376       mixer2                = nullptr;
377       masterEditor          = nullptr;
378       routeDialog           = nullptr;
379       watchdogThread        = 0;
380       editInstrument        = nullptr;
381       //routingPopupMenu      = 0;
382       progress              = nullptr;
383       saveIncrement         = 0;
384       activeTopWin          = nullptr;
385       currentMenuSharingTopwin = nullptr;
386       waitingForTopwin      = nullptr;
387       _lastProjectWasTemplate = false;
388       _lastProjectLoadedConfig = true;
389 
390       appName               = PACKAGE_NAME;
391       setWindowTitle(appName);
392       setWindowIcon(*MusEGui::museIcon);
393 
394       MusEGlobal::globalRasterizer = new Rasterizer(MusEGlobal::config.division, this);
395 
396       MusEGlobal::song = new MusECore::Song("song");
397       MusEGlobal::song->blockSignals(true);
398       MusEGlobal::heartBeatTimer = new QTimer(this);
399       MusEGlobal::heartBeatTimer->setObjectName("timer");
400       connect(MusEGlobal::heartBeatTimer, SIGNAL(timeout()), MusEGlobal::song, SLOT(beat()));
401       connect(MusEGlobal::heartBeatTimer, SIGNAL(timeout()), SLOT(heartBeat()));
402       connect(this, SIGNAL(activeTopWinChanged(MusEGui::TopWin*)), SLOT(activeTopWinChangedSlot(MusEGui::TopWin*)));
403       connect(MusEGlobal::song, SIGNAL(sigDirty()), this, SLOT(setDirty()));
404 
405       blinkTimer = new QTimer(this);
406       blinkTimer->setObjectName("blinkTimer");
407       connect(blinkTimer, SIGNAL(timeout()), SLOT(blinkTimerSlot()));
408       blinkTimer->start( 250 );      // Every quarter second, for a flash rate of 2 Hz.
409 
410       saveTimer = new QTimer(this);
411       connect(saveTimer, SIGNAL(timeout()), this, SLOT(saveTimerSlot()));
412       saveTimer->start( 60 * 1000 ); // every minute
413 
414       messagePollTimer = new QTimer(this);
415       messagePollTimer->setObjectName("messagePollTimer");
416       connect(messagePollTimer, SIGNAL(timeout()), SLOT(messagePollTimerSlot()));
417       // A zero-millisecond poll timer. Oops, no can't do that in the gui thread,
418       //  it spikes the CPU usage because it eats up all the idle time. Use say, 50Hz 20msec.
419       messagePollTimer->start(20);
420 
421       //init cpuload stuff
422       clock_gettime(CLOCK_REALTIME, &lastSysTime);
423       lastCpuTime.tv_sec = 0;
424       lastCpuTime.tv_usec = 0;
425       fAvrCpuLoad = 0.0f;
426       avrCpuLoadCounter = 0;
427       fCurCpuLoad = 0.0f;
428 
429 #ifdef PYTHON_SUPPORT
430       //---------------------------------------------------
431       //    Python bridge
432       //---------------------------------------------------
433       // Uncomment in order to enable MusE Python bridge:
434       if (MusEGlobal::usePythonBridge) {
435             fprintf(stderr, "Initializing python bridge!\n");
436             if (startPythonBridge() == false) {
437                   fprintf(stderr, "Could not initialize Python bridge\n");
438                   exit(1);
439                   }
440             }
441 #endif
442 
443       setCorner(Qt::BottomRightCorner, Qt::RightDockWidgetArea);
444       setCorner(Qt::TopRightCorner, Qt::RightDockWidgetArea);
445 
446       markerDock = new QDockWidget("Markers", this);
447       markerDock->setObjectName("markerDock");
448 //      markerDock->setAllowedAreas(Qt::BottomDockWidgetArea | Qt::RightDockWidgetArea);
449       markerView = new MusEGui::MarkerView(markerDock);
450       markerDock->setWidget(markerView);
451       addDockWidget(Qt::RightDockWidgetArea, markerDock);
452       markerDock->hide();
453 
454       masterListDock = new QDockWidget("Mastertrack List", this);
455       masterListDock->setObjectName("masterListDock");
456       // listMasterDock->setAllowedAreas(Qt::BottomDockWidgetArea | Qt::RightDockWidgetArea);
457       masterList = new MusEGui::LMaster(this);
458       masterListDock->setWidget(masterList);
459       addDockWidget(Qt::RightDockWidgetArea, masterListDock);
460       masterListDock->hide();
461 
462       clipListDock = new QDockWidget("Clip List", this);
463       clipListDock->setObjectName("clipListDock");
464 //      clipListDock->setAllowedAreas(Qt::BottomDockWidgetArea | Qt::RightDockWidgetArea);
465       clipListEdit = new MusEGui::ClipListEdit(clipListDock);
466       clipListDock->setWidget(clipListEdit);
467       addDockWidget(Qt::RightDockWidgetArea, clipListDock);
468       clipListDock->hide();
469 
470       //---------------------------------------------------
471       //    undo/redo
472       //---------------------------------------------------
473 
474       MusEGlobal::undoRedo = new QActionGroup(this);
475       MusEGlobal::undoRedo->setExclusive(false);
476       MusEGlobal::undoAction = new QAction(*MusEGui::undoSVGIcon, tr("Und&o"),
477         MusEGlobal::undoRedo);
478       MusEGlobal::redoAction = new QAction(*MusEGui::redoSVGIcon, tr("Re&do"),
479         MusEGlobal::undoRedo);
480 
481       MusEGlobal::undoAction->setWhatsThis(tr("Undo last change to project"));
482       MusEGlobal::redoAction->setWhatsThis(tr("Redo last undo"));
483       MusEGlobal::undoAction->setEnabled(false);
484       MusEGlobal::redoAction->setEnabled(false);
485       connect(MusEGlobal::redoAction, SIGNAL(triggered()), MusEGlobal::song, SLOT(redo()));
486       connect(MusEGlobal::undoAction, SIGNAL(triggered()), MusEGlobal::song, SLOT(undo()));
487 
488       //---------------------------------------------------
489       //    Transport
490       //---------------------------------------------------
491 
492       MusEGlobal::transportAction = new QActionGroup(this);
493       MusEGlobal::transportAction->setExclusive(false);
494 
495       MusEGlobal::loopAction = new QAction(*MusEGui::loopSVGIcon, tr("Loop"),
496                                            MusEGlobal::transportAction);
497       MusEGlobal::loopAction->setCheckable(true);
498 
499       MusEGlobal::loopAction->setWhatsThis(tr("Loop between left mark and right mark"));
500       MusEGlobal::loopAction->setStatusTip(tr("Loop between left mark and right mark"));
501       connect(MusEGlobal::loopAction, SIGNAL(toggled(bool)), MusEGlobal::song, SLOT(setLoop(bool)));
502 
503       MusEGlobal::punchinAction = new QAction(*MusEGui::punchinSVGIcon, tr("Punch in"),
504                                               MusEGlobal::transportAction);
505       MusEGlobal::punchinAction->setCheckable(true);
506 
507       MusEGlobal::punchinAction->setWhatsThis(tr("Record starts at left mark"));
508       MusEGlobal::punchinAction->setStatusTip(tr("Recording starts at left mark"));
509       connect(MusEGlobal::punchinAction, SIGNAL(toggled(bool)), MusEGlobal::song, SLOT(setPunchin(bool)));
510 
511       MusEGlobal::punchoutAction = new QAction(*MusEGui::punchoutSVGIcon, tr("Punch out"),
512                                                MusEGlobal::transportAction);
513       MusEGlobal::punchoutAction->setCheckable(true);
514 
515       MusEGlobal::punchoutAction->setWhatsThis(tr("Record stops at right mark"));
516       MusEGlobal::punchoutAction->setStatusTip(tr("Recording stops at right mark"));
517       connect(MusEGlobal::punchoutAction, SIGNAL(toggled(bool)), MusEGlobal::song, SLOT(setPunchout(bool)));
518 
519       QAction *tseparator = new QAction(this);
520       tseparator->setSeparator(true);
521       MusEGlobal::transportAction->addAction(tseparator);
522 
523       MusEGlobal::startAction = new QAction(*MusEGui::rewindToStartSVGIcon, tr("Start"),
524                                              MusEGlobal::transportAction);
525 
526       MusEGlobal::startAction->setWhatsThis(tr("Rewind to start position"));
527       connect(MusEGlobal::startAction, SIGNAL(triggered()), MusEGlobal::song, SLOT(rewindStart()));
528 
529       MusEGlobal::rewindAction = new QAction(*MusEGui::rewindSVGIcon, tr("Rewind"),
530                                               MusEGlobal::transportAction);
531 
532       MusEGlobal::rewindAction->setWhatsThis(tr("Rewind current position"));
533       connect(MusEGlobal::rewindAction, SIGNAL(triggered()), MusEGlobal::song, SLOT(rewind()));
534 
535       MusEGlobal::forwardAction = new QAction(*MusEGui::fastForwardSVGIcon, tr("Forward"),
536                                                MusEGlobal::transportAction);
537 
538       MusEGlobal::forwardAction->setWhatsThis(tr("Move current position"));
539       connect(MusEGlobal::forwardAction, SIGNAL(triggered()), MusEGlobal::song, SLOT(forward()));
540 
541       MusEGlobal::stopAction = new QAction(*MusEGui::stopSVGIcon, tr("Stop"),
542                                             MusEGlobal::transportAction);
543       MusEGlobal::stopAction->setCheckable(true);
544 
545       MusEGlobal::stopAction->setWhatsThis(tr("Stop sequencer"));
546       MusEGlobal::stopAction->setChecked(true);
547       connect(MusEGlobal::stopAction, SIGNAL(toggled(bool)), MusEGlobal::song, SLOT(setStop(bool)));
548 
549       MusEGlobal::playAction = new QAction(*MusEGui::playSVGIcon,
550                      tr("Play") + " (" + shrtToStr(MusEGui::SHRT_PLAY_TOGGLE)
551                          + ")<br>"+ tr("Restart rec")+" (" + QKeySequence(MusEGui::shortcuts[MusEGui::SHRT_REC_RESTART].key).toString() + ")",
552                      MusEGlobal::transportAction);
553       MusEGlobal::playAction->setCheckable(true);
554 
555       MusEGlobal::playAction->setWhatsThis(tr("Start sequencer play"));
556       MusEGlobal::playAction->setChecked(false);
557       connect(MusEGlobal::playAction, SIGNAL(toggled(bool)), MusEGlobal::song, SLOT(setPlay(bool)));
558 
559       MusEGlobal::recordAction = new QAction(*MusEGui::recMasterSVGIcon, tr("Record"),
560                                               MusEGlobal::transportAction);
561       MusEGlobal::recordAction->setCheckable(true);
562       MusEGlobal::recordAction->setWhatsThis(tr("To record press record and then play"));
563       MusEGlobal::recordAction->setStatusTip(tr("To record press record and then play"));
564       connect(MusEGlobal::recordAction, SIGNAL(toggled(bool)), MusEGlobal::song, SLOT(setRecord(bool)));
565 
566       MusEGlobal::panicAction = new QAction(*MusEGui::panicSVGIcon, tr("Panic"), this);
567 
568       QMenu* panicPopupMenu = new QMenu(this);
569       MusEGlobal::panicAction->setMenu(panicPopupMenu);
570       MusEGlobal::panicAction->setObjectName("PanicButton");
571 
572 //      MusEGlobal::panicAction->setWhatsThis(tr("Send note off to all midi channels")); // wrong?
573       MusEGlobal::panicAction->setStatusTip(tr("Panic button: Send 'all sounds off' and 'reset all controls' to all midi channels. Press F1 for help."));
574       connect(MusEGlobal::panicAction, SIGNAL(triggered()), MusEGlobal::song, SLOT(panic()));
575 
576       MusEGlobal::metronomeAction = new QAction(*MusEGui::metronomeOnSVGIcon, tr("Metronome"), this);
577       MusEGlobal::metronomeAction->setObjectName("MetronomeButton");
578       MusEGlobal::metronomeAction->setCheckable(true);
579       MusEGlobal::metronomeAction->setWhatsThis(tr("Turn on/off metronome"));
580       MusEGlobal::metronomeAction->setStatusTip(tr("Metronome on/off. Press F1 for help."));
581       MusEGlobal::metronomeAction->setChecked(MusEGlobal::song->click());
582       connect(MusEGlobal::metronomeAction, SIGNAL(toggled(bool)), MusEGlobal::song, SLOT(setClick(bool)));
583       connect(MusEGlobal::song, SIGNAL(clickChanged(bool)), MusEGlobal::metronomeAction, SLOT(setChecked(bool)));
584 
585       //----Actions
586       //-------- File Actions
587 
588       fileNewAction = new QAction(*MusEGui::filenewSVGIcon, tr("&New"), this);
589       fileNewAction->setToolTip(tr("Create new song"));
590       fileNewAction->setWhatsThis(tr("Create new song"));
591 
592       fileNewFromTemplateAction = new QAction(*MusEGui::filetemplateSVGIcon, tr("New from &Template..."), this);
593       fileNewFromTemplateAction->setToolTip(tr("Create new song from template"));
594       fileNewFromTemplateAction->setWhatsThis(tr("Create new song from template"));
595 
596       fileOpenAction = new QAction(*MusEGui::fileopenSVGIcon, tr("&Open..."), this);
597       fileOpenAction->setToolTip(tr("Open song from file"));
598       fileOpenAction->setWhatsThis(tr("Click this button to open an existing song."));
599 
600       openRecent = new QMenu(tr("Open &Recent"), this);
601 
602       fileSaveAction = new QAction(*MusEGui::filesaveSVGIcon, tr("&Save"), this);
603       fileSaveAction->setToolTip(tr("Save current song"));
604       fileSaveAction->setWhatsThis(tr("Click this button to save the song you are editing. You will be prompted for a file name."));
605 
606       fileSaveAsAction = new QAction(*MusEGui::filesaveasSVGIcon, tr("Save &As..."), this);
607       fileSaveAsNewProjectAction = new QAction(*MusEGui::filesaveProjectSVGIcon, tr("Save As New &Project..."), this);
608       fileSaveRevisionAction = new QAction(*MusEGui::filesaveRevisionSVGIcon, tr("Save New Re&vision"), this);
609       fileSaveAsTemplateAction = new QAction(*MusEGui::filesaveTemplateSVGIcon, tr("Save As Te&mplate..."), this);
610 
611       fileCloseAction = new QAction(*MusEGui::filecloseSVGIcon, tr("&Close"), this);
612 
613       fileImportMidiAction = new QAction(tr("Import Midi File..."), this);
614       fileExportMidiAction = new QAction(tr("Export Midi File..."), this);
615       fileImportPartAction = new QAction(tr("Import Part..."), this);
616 
617       fileImportWaveAction = new QAction(tr("Import Audio File..."), this);
618       fileMoveWaveFiles = new QAction(tr("Find Unused Wave Files..."), this);
619 
620       quitAction = new QAction(*MusEGui::appexitSVGIcon, tr("&Quit"), this);
621 
622       editSongInfoAction = new QAction(tr("Edit Project Description..."), this);
623 
624       //-------- View Actions
625       viewTransportAction = new QAction(*MusEGui::transportSVGIcon, tr("Transport Panel"), this);
626       viewTransportAction->setCheckable(true);
627       viewBigtimeAction = new QAction(*MusEGui::bigtimeSVGIcon, tr("Bigtime Window"),  this);
628       viewBigtimeAction->setCheckable(true);
629       viewMixerAAction = new QAction(*MusEGui::mixerSVGIcon, tr("Mixer A"), this);
630       viewMixerAAction->setCheckable(true);
631       viewMixerBAction = new QAction(*MusEGui::mixerSVGIcon, tr("Mixer B"), this);
632       viewMixerBAction->setCheckable(true);
633 
634       viewMarkerAction = markerDock->toggleViewAction();
635       viewCliplistAction = clipListDock->toggleViewAction();
636 
637       toggleDocksAction = new QAction(tr("Show Docks"), this);
638       toggleDocksAction->setCheckable(true);
639       toggleDocksAction->setChecked(true);
640       toggleDocksAction->setStatusTip(tr("Toggle display of currently visible dock windows."));
641 
642       fullscreenAction=new QAction(tr("Fullscreen"), this);
643       fullscreenAction->setCheckable(true);
644       fullscreenAction->setChecked(false);
645       fullscreenAction->setStatusTip(tr("Display MusE main window in full screen mode."));
646 
647 //      QMenu* master = new QMenu(tr("Mastertrack"), this);
648 //      master->setIcon(QIcon(*edit_mastertrackIcon));
649       masterGraphicAction = new QAction(QIcon(*mastereditSVGIcon),tr("Mastertrack Graphic..."), this);
650       masterListAction = masterListDock->toggleViewAction();
651 //      masterListAction = new QAction(QIcon(*mastertrack_listIcon),tr("List..."), this);
652 //      master->addAction(masterGraphicAction);
653 //      master->addAction(masterListAction);
654 
655       //-------- Midi Actions
656       midiEditInstAction = new QAction(*MusEGui::editInstrumentSVGIcon, tr("Edit Instrument..."), this);
657 //      midiInputPlugins = new QMenu(tr("Input Plugins"), this);
658       midiTrpAction = new QAction(*MusEGui::midiInputTransposeSVGIcon, tr("Input Transpose..."), this);
659       midiInputTrfAction = new QAction(*MusEGui::midiInputTransformSVGIcon, tr("Input Transform..."), this);
660       midiInputFilterAction = new QAction(*MusEGui::midiInputFilterSVGIcon, tr("Input Filter..."), this);
661       midiRemoteAction = new QAction(*MusEGui::midiInputRemoteSVGIcon, tr("Remote Control..."), this);
662 #ifdef BUILD_EXPERIMENTAL
663       midiRhythmAction = new QAction(QIcon(*midi_inputplugins_random_rhythm_generatorIcon), tr("Rhythm Generator"), this);
664 #endif
665       midiResetInstAction = new QAction(*MusEGui::midiResetSVGIcon, tr("Reset Instrument"), this);
666       midiResetInstAction->setStatusTip(tr("Send 'note-off' command to all midi channels."));
667       midiInitInstActions = new QAction(*MusEGui::midiInitSVGIcon, tr("Init Instrument"), this);
668       midiInitInstActions->setStatusTip(tr("Send initialization messages as found in instrument definition."));
669       midiLocalOffAction = new QAction(*MusEGui::midiLocalOffSVGIcon, tr("Local Off"), this);
670       midiLocalOffAction->setStatusTip(tr("Send 'local-off' command to all midi channels."));
671 
672       //-------- Audio Actions
673       audioBounce2TrackAction = new QAction(*MusEGui::downmixTrackSVGIcon, tr("Render Downmix to Selected Wave Track"), this);
674       audioBounce2FileAction = new QAction(*MusEGui::downmixOnSVGIcon, tr("Render Downmix to a File..."), this);
675       audioRestartAction = new QAction(*MusEGui::restartSVGIcon, tr("Restart Audio"), this);
676 
677       //-------- Automation Actions
678 // REMOVE Tim. automation. Removed.
679 // Deprecated. MusEGlobal::automation is now fixed TRUE
680 //   for now until we decide what to do with it.
681 //       autoMixerAction = new QAction(QIcon(*MusEGui::automation_mixerIcon), tr("Mixer Automation"), this);
682 //       autoMixerAction->setCheckable(true);
683       autoSnapshotAction = new QAction(*MusEGui::snapshotSVGIcon, tr("Take Automation Snapshot"), this);
684       autoClearAction = new QAction(*MusEGui::clearSVGIcon, tr("Clear Automation Data"), this);
685 
686        //-------- Settings Actions
687       settingsGlobalAction = new QAction(*MusEGui::settingsSVGIcon, tr("Global Settings..."), this);
688       settingsAppearanceAction = new QAction(*MusEGui::appearanceSVGIcon, tr("Appearance..."), this);
689       settingsShortcutsAction = new QAction(*MusEGui::keySVGIcon, tr("Keyboard Shortcuts..."), this);
690       follow = new QMenu(tr("Follow Song"), this);
691       follow->setObjectName("CheckmarkOnly");
692       QActionGroup *followAG = new QActionGroup(this);
693       followAG->setExclusive(true);
694       dontFollowAction = new QAction(tr("Don't Follow Song"), followAG);
695       dontFollowAction->setCheckable(true);
696       followPageAction = new QAction(tr("Follow Page"), followAG);
697       followPageAction->setCheckable(true);
698       followCtsAction = new QAction(tr("Follow Continuous"), followAG);
699       followCtsAction->setCheckable(true);
700       followPageAction->setChecked(true);
701 
702       rewindOnStopAction=new QAction(tr("Rewind on Stop"), this);
703       rewindOnStopAction->setCheckable(true);
704       rewindOnStopAction->setChecked(MusEGlobal::config.useRewindOnStop);
705 
706       settingsMetronomeAction = new QAction(*MusEGui::metronomeOnSVGIcon, tr("Metronome..."), this);
707       settingsMidiSyncAction = new QAction(*MusEGui::midiSyncSVGIcon, tr("Midi Sync..."), this);
708       settingsMidiIOAction = new QAction(*MusEGui::midiExportImportSVGIcon, tr("Midi File Import/Export..."), this);
709       settingsMidiPortAction = new QAction(*MusEGui::ankerSVGIcon, tr("Midi Ports/Soft Synths..."), this);
710 
711       //-------- Help Actions
712       helpManualAction = new QAction(tr("&Manual (Wiki)..."), this);
713       helpHomepageAction = new QAction(tr("MusE &Homepage..."), this);
714       helpDidYouKnow = new QAction(tr("&Did You Know?"), this);
715 
716       helpReportAction = new QAction(tr("&Report Bug..."), this);
717       helpAboutAction = new QAction(tr("&About MusE..."), this);
718 
719       helpSnooperAction = new QAction(tr("Snooper (Developer Tool)..."), this);
720 
721       //---- Connections
722       //-------- File connections
723 
724       connect(fileNewAction,  SIGNAL(triggered()), SLOT(loadDefaultTemplate()));
725       connect(fileNewFromTemplateAction,  SIGNAL(triggered()), SLOT(loadTemplate()));
726       connect(fileOpenAction, SIGNAL(triggered()), SLOT(loadProject()));
727       connect(openRecent, SIGNAL(aboutToShow()), SLOT(openRecentMenu()));
728       connect(openRecent, SIGNAL(triggered(QAction*)), SLOT(selectProject(QAction*)));
729 
730       connect(fileSaveAction, SIGNAL(triggered()), SLOT(save()));
731       connect(fileSaveAsAction, SIGNAL(triggered()), SLOT(saveAs()));
732       connect(fileSaveAsNewProjectAction, SIGNAL(triggered()), SLOT(saveAsNewProject()));
733       connect(fileSaveRevisionAction, SIGNAL(triggered()), SLOT(saveNewRevision()));
734       connect(fileSaveAsTemplateAction, SIGNAL(triggered()), SLOT(saveAsTemplate()));
735 
736       connect(fileCloseAction, SIGNAL(triggered()), SLOT(fileClose()));
737 
738       connect(fileImportMidiAction, SIGNAL(triggered()), SLOT(importMidi()));
739       connect(fileExportMidiAction, SIGNAL(triggered()), SLOT(exportMidi()));
740       connect(fileImportPartAction, SIGNAL(triggered()), SLOT(importPart()));
741 
742       connect(fileImportWaveAction, SIGNAL(triggered()), SLOT(importWave()));
743       connect(fileMoveWaveFiles, SIGNAL(triggered()), SLOT(findUnusedWaveFiles()));
744       connect(quitAction, SIGNAL(triggered()), SLOT(quitDoc()));
745 
746       connect(editSongInfoAction, SIGNAL(triggered()), SLOT(startSongInfo()));
747 
748       //-------- View connections
749       connect(viewTransportAction, SIGNAL(toggled(bool)), SLOT(toggleTransport(bool)));
750       connect(viewBigtimeAction, SIGNAL(toggled(bool)), SLOT(toggleBigTime(bool)));
751       connect(viewMixerAAction, SIGNAL(toggled(bool)),SLOT(toggleMixer1(bool)));
752       connect(viewMixerBAction, SIGNAL(toggled(bool)), SLOT(toggleMixer2(bool)));
753 //      connect(viewArrangerAction, SIGNAL(toggled(bool)), SLOT(toggleArranger(bool)));
754       connect(masterGraphicAction, SIGNAL(triggered()), SLOT(startMasterEditor()));
755 //      connect(masterListAction, SIGNAL(triggered()), SLOT(startLMasterEditor()));
756       connect(toggleDocksAction, SIGNAL(toggled(bool)), SLOT(toggleDocks(bool)));
757       connect(fullscreenAction, SIGNAL(toggled(bool)), SLOT(setFullscreen(bool)));
758 
759       //-------- Midi connections
760       connect(midiEditInstAction, SIGNAL(triggered()), SLOT(startEditInstrument()));
761       connect(midiResetInstAction, SIGNAL(triggered()), SLOT(resetMidiDevices()));
762       connect(midiInitInstActions, SIGNAL(triggered()), SLOT(initMidiDevices()));
763       connect(midiLocalOffAction, SIGNAL(triggered()), SLOT(localOff()));
764 
765       connect(midiTrpAction,         &QAction::triggered, [this]() { startMidiInputPlugin(0); } );
766       connect(midiInputTrfAction,    &QAction::triggered, [this]() { startMidiInputPlugin(1); } );
767       connect(midiInputFilterAction, &QAction::triggered, [this]() { startMidiInputPlugin(2); } );
768       connect(midiRemoteAction,      &QAction::triggered, [this]() { startMidiInputPlugin(3); } );
769 
770 #ifdef BUILD_EXPERIMENTAL
771       connect(midiRhythmAction,      &QAction::triggered, [this]() { startMidiInputPlugin(4); } );
772 #endif
773 
774       //-------- Audio connections
775       connect(audioBounce2TrackAction, SIGNAL(triggered()), SLOT(bounceToTrack()));
776       connect(audioBounce2FileAction, SIGNAL(triggered()), SLOT(bounceToFile()));
777       connect(audioRestartAction, SIGNAL(triggered()), SLOT(seqRestart()));
778 
779       //-------- Automation connections
780 // REMOVE Tim. automation. Removed.
781 // Deprecated. MusEGlobal::automation is now fixed TRUE
782 //   for now until we decide what to do with it.
783 //       connect(autoMixerAction, SIGNAL(triggered()), SLOT(switchMixerAutomation()));
784       connect(autoSnapshotAction, SIGNAL(triggered()), SLOT(takeAutomationSnapshot()));
785       connect(autoClearAction, SIGNAL(triggered()), SLOT(clearAutomation()));
786 
787       //-------- Settings connections
788       connect(settingsGlobalAction, SIGNAL(triggered()), SLOT(configGlobalSettings()));
789       connect(settingsShortcutsAction, SIGNAL(triggered()), SLOT(configShortCuts()));
790       connect(settingsMetronomeAction, SIGNAL(triggered()), SLOT(configMetronome()));
791       connect(settingsMidiSyncAction, SIGNAL(triggered()), SLOT(configMidiSync()));
792       connect(settingsMidiIOAction, SIGNAL(triggered()), SLOT(configMidiFile()));
793       connect(settingsAppearanceAction, SIGNAL(triggered()), SLOT(configAppearance()));
794       connect(settingsMidiPortAction, SIGNAL(triggered()), SLOT(configMidiPorts()));
795 
796       connect(dontFollowAction, &QAction::triggered, [this]() { cmd(CMD_FOLLOW_NO); } );
797       connect(followPageAction, &QAction::triggered, [this]() { cmd(CMD_FOLLOW_JUMP); } );
798       connect(followCtsAction,  &QAction::triggered, [this]() { cmd(CMD_FOLLOW_CONTINUOUS); } );
799       connect(rewindOnStopAction, SIGNAL(toggled(bool)), SLOT(toggleRewindOnStop(bool)));
800 
801       //-------- Help connections
802       connect(helpManualAction, SIGNAL(triggered()), SLOT(startHelpBrowser()));
803       connect(helpHomepageAction, SIGNAL(triggered()), SLOT(startHomepageBrowser()));
804       connect(helpReportAction, SIGNAL(triggered()), SLOT(startBugBrowser()));
805       connect(helpDidYouKnow, SIGNAL(triggered()), SLOT(showDidYouKnowDialog()));
806       connect(helpAboutAction, SIGNAL(triggered()), SLOT(about()));
807       connect(helpSnooperAction, &QAction::triggered, [this]() { startSnooper(); } );
808 
809       //--------------------------------------------------
810       //    Toolbar
811       //--------------------------------------------------
812 
813       // when adding a toolbar to the main window, remember adding it to
814       // either the requiredToolbars or optionalToolbars list!
815       // NOTICE: Please ensure that any tool bar object names here match the names
816       //          assigned in the 'toolbar' creation section of TopWin::TopWin(),
817       //          or any other TopWin class.
818       //         This allows MusE::setCurrentMenuSharingTopwin() to do some magic
819       //          to retain the original toolbar layout. If it finds an existing
820       //          toolbar with the same object name, it /replaces/ it using insertToolBar(),
821       //          instead of /appending/ with addToolBar().
822 
823       tools = addToolBar(tr("File Buttons"));
824       tools->setObjectName("File buttons");
825       tools->addAction(fileNewAction);
826       tools->addAction(fileNewFromTemplateAction);
827       tools->addAction(fileOpenAction);
828       tools->addAction(fileSaveAction);
829       QAction* whatsthis = QWhatsThis::createAction(this);
830       whatsthis->setIcon(*whatsthisSVGIcon);
831       tools->addAction(whatsthis);
832 
833       QToolBar* undo_tools = addToolBar(tr("Undo/Redo"));
834       undo_tools->setObjectName("Undo/Redo");
835       undo_tools->addActions(MusEGlobal::undoRedo->actions());
836 
837       QToolBar* panic_toolbar = addToolBar(tr("Panic"));
838       panic_toolbar->setObjectName("Panic tool");
839       panic_toolbar->addAction(MusEGlobal::panicAction);
840 
841       QToolBar* metronome_toolbar = addToolBar(tr("Metronome"));
842       metronome_toolbar->setObjectName("Metronome tool");
843       metronome_toolbar->addAction(MusEGlobal::metronomeAction);
844 
845       cpuLoadToolbar = new CpuToolbar(tr("Cpu Load"), this);
846       addToolBar(cpuLoadToolbar);
847       cpuLoadToolbar->hide(); // hide as a default, the info is now in status bar too
848       connect(cpuLoadToolbar, SIGNAL(resetClicked()), SLOT(resetXrunsCounter()));
849 
850       QToolBar* songpos_tb = addToolBar(tr("Timeline"));
851       songpos_tb->setObjectName("Timeline tool");
852       songpos_tb->addWidget(new MusEGui::SongPosToolbarWidget(songpos_tb));
853       songpos_tb->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
854       songpos_tb->setContextMenuPolicy(Qt::PreventContextMenu);
855       addToolBar(Qt::BottomToolBarArea, songpos_tb);
856 
857       QToolBar* transportToolbar = addToolBar(tr("Transport"));
858       transportToolbar->setObjectName("Transport tool");
859       transportToolbar->addActions(MusEGlobal::transportAction->actions());
860       transportToolbar->setIconSize(QSize(MusEGlobal::config.iconSize, MusEGlobal::config.iconSize));
861 
862       RecToolbar *recToolbar = new RecToolbar(tr("Recording"), this);
863       addToolBar(recToolbar);
864 
865       SyncToolbar *syncToolbar = new SyncToolbar(tr("Sync"), this);
866       addToolBar(syncToolbar);
867 
868       addToolBarBreak();
869 
870       TempoToolbar* tempo_tb = new TempoToolbar(tr("Tempo"), this);
871       addToolBar(tempo_tb);
872 
873       SigToolbar* sig_tb = new SigToolbar(tr("Signature"), this);
874       addToolBar(sig_tb);
875 
876       PosToolbar *posToolbar = new PosToolbar(tr("Position"), this);
877       addToolBar(posToolbar);
878 
879       requiredToolbars.push_back(tools);
880       requiredToolbars.push_back(cpuLoadToolbar);
881 
882       optionalToolbars.push_back(undo_tools);
883       optionalToolbars.push_back(panic_toolbar);
884       optionalToolbars.push_back(metronome_toolbar);
885       optionalToolbars.push_back(songpos_tb);
886       optionalToolbars.push_back(nullptr);  // Toolbar break
887       optionalToolbars.push_back(transportToolbar);
888       optionalToolbars.push_back(recToolbar);
889       optionalToolbars.push_back(syncToolbar);
890       optionalToolbars.push_back(posToolbar);
891       optionalToolbars.push_back(tempo_tb);
892       optionalToolbars.push_back(sig_tb);
893 
894 
895        QSocketNotifier* ss = new QSocketNotifier(MusEGlobal::audio->getFromThreadFdr(), QSocketNotifier::Read, this);
896        connect(ss, SIGNAL(activated(int)), MusEGlobal::song, SLOT(seqSignal(int)));
897 
898       //---------------------------------------------------
899       //    Popups
900       //---------------------------------------------------
901 
902       // when adding a menu to the main window, remember adding it to
903       // either the leadingMenus or trailingMenus list!
904       // also do NOT use menuBar()->addMenu(QString&), but ALWAYS
905       // create the menu with new QMenu and add it afterwards.
906       // the menu's owner must be this and not this->menuBar()!
907 
908 
909       //-------------------------------------------------------------
910       //    popup File
911       //-------------------------------------------------------------
912 
913       menu_file = new QMenu(tr("&File"), this);
914       menuBar()->addMenu(menu_file);
915       leadingMenus.push_back(menu_file);
916       menu_file->addAction(fileNewAction);
917       menu_file->addAction(fileNewFromTemplateAction);
918       menu_file->addAction(fileOpenAction);
919       menu_file->addMenu(openRecent);
920       menu_file->addSeparator();
921       menu_file->addAction(fileSaveAction);
922       menu_file->addAction(fileSaveAsAction);
923       menu_file->addAction(fileSaveRevisionAction);
924       menu_file->addAction(fileSaveAsNewProjectAction);
925       menu_file->addAction(fileSaveAsTemplateAction);
926       menu_file->addSeparator();
927       menu_file->addAction(fileCloseAction);
928       menu_file->addSeparator();
929       menu_file->addAction(editSongInfoAction);
930       menu_file->addSeparator();
931       menu_file->addAction(fileImportMidiAction);
932       menu_file->addAction(fileExportMidiAction);
933       menu_file->addAction(fileImportPartAction);
934       menu_file->addAction(fileImportWaveAction);
935       menu_file->addSeparator();
936       menu_file->addAction(fileMoveWaveFiles);
937       menu_file->addSeparator();
938       menu_file->addAction(quitAction);
939       menu_file->addSeparator();
940 
941 
942 
943       //-------------------------------------------------------------
944       //    popup View
945       //-------------------------------------------------------------
946 
947       menuView = new QMenu(tr("&View"), this);
948       menuBar()->addMenu(menuView);
949       trailingMenus.push_back(menuView);
950 
951       menuView->addAction(viewTransportAction);
952       menuView->addAction(viewBigtimeAction);
953       menuView->addAction(viewMixerAAction);
954       menuView->addAction(viewMixerBAction);
955       menuView->addSeparator();
956 //      menuView->addAction(viewArrangerAction);
957 //      menuView->addMenu(master);
958       menuView->addAction(masterGraphicAction);
959       menuView->addAction(masterListAction);
960       menuView->addAction(viewMarkerAction);
961       menuView->addAction(viewCliplistAction);
962       menuView->addSeparator();
963       menuView->addAction(toggleDocksAction);
964       menuView->addAction(fullscreenAction);
965 
966       //-------------------------------------------------------------
967       //    popup Midi
968       //-------------------------------------------------------------
969 
970       menu_functions = new QMenu(tr("&Midi"), this);
971       menuBar()->addMenu(menu_functions);
972       trailingMenus.push_back(menu_functions);
973 
974       menu_functions->addAction(midiEditInstAction);
975 
976       menu_functions->addSeparator();
977 //      menu_functions->addMenu(midiInputPlugins);
978       menu_functions->addAction(midiTrpAction);
979       menu_functions->addAction(midiInputTrfAction);
980       menu_functions->addAction(midiInputFilterAction);
981       menu_functions->addAction(midiRemoteAction);
982 //      midiInputPlugins->addAction(midiTrpAction);
983 //      midiInputPlugins->addAction(midiInputTrfAction);
984 //      midiInputPlugins->addAction(midiInputFilterAction);
985 //      midiInputPlugins->addAction(midiRemoteAction);
986 #ifdef BUILD_EXPERIMENTAL
987       midiInputPlugins->addAction(midiRhythmAction);
988 #endif
989 
990       menu_functions->addSeparator();
991       menu_functions->addAction(midiResetInstAction);
992       menu_functions->addAction(midiInitInstActions);
993       menu_functions->addAction(midiLocalOffAction);
994 
995       panicPopupMenu->addAction(midiResetInstAction);
996       panicPopupMenu->addAction(midiInitInstActions);
997       panicPopupMenu->addAction(midiLocalOffAction);
998 
999       //-------------------------------------------------------------
1000       //    popup Audio
1001       //-------------------------------------------------------------
1002 
1003       menu_audio = new QMenu(tr("&Audio"), this);
1004       menuBar()->addMenu(menu_audio);
1005       trailingMenus.push_back(menu_audio);
1006 
1007       menu_audio->addAction(audioBounce2TrackAction);
1008       menu_audio->addAction(audioBounce2FileAction);
1009       menu_audio->addSeparator();
1010       menu_audio->addAction(audioRestartAction);
1011       menu_audio->addSeparator();
1012 // REMOVE Tim. automation. Removed.
1013 // Deprecated. MusEGlobal::automation is now fixed TRUE
1014 //   for now until we decide what to do with it.
1015 //       menu_audio->addAction(autoMixerAction);
1016       //menu_audio->addSeparator();
1017       menu_audio->addAction(autoSnapshotAction);
1018       menu_audio->addAction(autoClearAction);
1019 
1020       //-------------------------------------------------------------
1021       //    popup Windows
1022       //-------------------------------------------------------------
1023 
1024       menuWindows = new QMenu(tr("&Windows"), this);
1025       menuBar()->addMenu(menuWindows);
1026       trailingMenus.push_back(menuWindows);
1027 
1028       //-------------------------------------------------------------
1029       //    popup Settings
1030       //-------------------------------------------------------------
1031 
1032       menuSettings = new QMenu(tr("Se&ttings"), this);
1033       menuBar()->addMenu(menuSettings);
1034       trailingMenus.push_back(menuSettings);
1035 
1036       menuSettings->addAction(settingsGlobalAction);
1037       menuSettings->addAction(settingsAppearanceAction);
1038       menuSettings->addAction(settingsShortcutsAction);
1039       menuSettings->addSeparator();
1040       menuSettings->addMenu(follow);
1041       follow->addActions(followAG->actions());
1042       menuSettings->addAction(rewindOnStopAction);
1043       menuSettings->addAction(settingsMetronomeAction);
1044       menuSettings->addSeparator();
1045       menuSettings->addAction(settingsMidiSyncAction);
1046       menuSettings->addAction(settingsMidiIOAction);
1047       menuSettings->addAction(settingsMidiPortAction);
1048 
1049       //---------------------------------------------------
1050       //    popup Help
1051       //---------------------------------------------------
1052 
1053       menu_help = new QMenu(tr("&Help"), this);
1054       menuBar()->addMenu(menu_help);
1055       trailingMenus.push_back(menu_help);
1056 
1057       menu_help->addAction(helpManualAction);
1058       menu_help->addAction(whatsthis);
1059       menu_help->addAction(helpHomepageAction);
1060       menu_help->addAction(helpDidYouKnow);
1061       menu_help->addSeparator();
1062       menu_help->addAction(helpReportAction);
1063       menu_help->addAction(helpSnooperAction);
1064       menu_help->addSeparator();
1065       menu_help->addAction(helpAboutAction);
1066 
1067       menu_help->addAction(tr("About &Qt..."), qApp, SLOT(aboutQt()));
1068 
1069       //---------------------------------------------------
1070       //    Central Widget
1071       //---------------------------------------------------
1072 
1073 
1074       mdiArea=new MuseMdiArea(this);
1075 //      mdiArea->setOption(QMdiArea::DontMaximizeSubWindowOnActivation);
1076       mdiArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
1077       mdiArea->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
1078       mdiArea->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored);
1079 
1080       mdiArea->setViewMode(QMdiArea::TabbedView);
1081       mdiArea->setTabsMovable(true);
1082       mdiArea->setTabPosition(QTabWidget::South);
1083       QTabBar* tb = mdiArea->findChild<QTabBar*>();
1084       if (tb) {
1085           tb->setExpanding(false);
1086 //          tb->setAutoHide(true);
1087       }
1088 //      viewArrangerAction->setEnabled(false);
1089 
1090       setCentralWidget(mdiArea);
1091 
1092       arrangerView = new MusEGui::ArrangerView(this);
1093 //      connect(arrangerView, SIGNAL(closed()), SLOT(arrangerClosed()));
1094       toplevels.push_back(arrangerView);
1095 //      arrangerView->hide();
1096       _arranger=arrangerView->getArranger();
1097 
1098       connect(tempo_tb, SIGNAL(returnPressed()), arrangerView, SLOT(focusCanvas()));
1099       connect(tempo_tb, SIGNAL(escapePressed()), arrangerView, SLOT(focusCanvas()));
1100       connect(tempo_tb, SIGNAL(masterTrackChanged(bool)), MusEGlobal::song, SLOT(setMasterFlag(bool)));
1101 
1102       connect(sig_tb,   SIGNAL(returnPressed()), arrangerView, SLOT(focusCanvas()));
1103       connect(sig_tb,   SIGNAL(escapePressed()), arrangerView, SLOT(focusCanvas()));
1104 
1105       //---------------------------------------------------
1106       //  read list of "Recent Projects"
1107       //---------------------------------------------------
1108 
1109       QString prjPath(MusEGlobal::configPath);
1110       prjPath += QString("/projects");
1111       QFile projFile(prjPath);
1112       if (!projFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
1113         perror("open projectfile");
1114         projectRecentList.clear();
1115       }
1116       else
1117       {
1118         for (int i = 0; i < MusEGlobal::config.recentListLength; ++i)
1119         {
1120           if (projFile.atEnd()) {
1121             break;
1122           }
1123           projectRecentList.append(projFile.readLine().simplified());
1124         }
1125       }
1126 
1127       transport = new MusEGui::Transport(this, "transport");
1128       bigtime   = nullptr;
1129 
1130       MusEGlobal::song->blockSignals(false);
1131 
1132       if (MusEGlobal::config.geometryMain.size().width()) {
1133           resize(MusEGlobal::config.geometryMain.size());
1134           move(MusEGlobal::config.geometryMain.topLeft());
1135       }
1136       else
1137           centerAndResize();
1138 
1139       setAndAdjustFonts();
1140 
1141       MusEGlobal::song->update();
1142       updateWindowMenu();
1143 }
1144 
1145 //---------------------------------------------------------
1146 //   setAndAdjustFonts
1147 //---------------------------------------------------------
setAndAdjustFonts()1148 void MusE::setAndAdjustFonts() {
1149 
1150     ensurePolished();
1151     MusEGlobal::config.fonts[0].setFamily(font().family());
1152     MusEGlobal::config.fonts[0].setPointSize(font().pointSize());
1153     MusEGlobal::config.fonts[0].setBold(font().bold());
1154     MusEGlobal::config.fonts[0].setItalic(font().italic());
1155 
1156     // init font family with system font (the original sans-serif default looked terrible on KDE)
1157     for (int i = 1; i < NUM_FONTS; i++) {
1158         if (MusEGlobal::config.fonts[i].family().isEmpty())
1159             MusEGlobal::config.fonts[i].setFamily(font().family());
1160     }
1161 
1162     if (MusEGlobal::config.autoAdjustFontSize) {
1163         int fs = font().pointSize();
1164         MusEGlobal::config.fonts[1].setPointSize(qRound(fs * MusEGlobal::FntFac::F1));
1165         MusEGlobal::config.fonts[2].setPointSize(qRound(fs * MusEGlobal::FntFac::F2));
1166         MusEGlobal::config.fonts[3].setPointSize(qRound(fs * MusEGlobal::FntFac::F3));
1167         MusEGlobal::config.fonts[4].setPointSize(qRound(fs * MusEGlobal::FntFac::F4));
1168         MusEGlobal::config.fonts[5].setPointSize(qRound(fs * MusEGlobal::FntFac::F5));
1169         MusEGlobal::config.fonts[6].setPointSize(qRound(fs * MusEGlobal::FntFac::F6));
1170     }
1171 }
1172 
1173 //---------------------------------------------------------
1174 //   centerAndResize
1175 //---------------------------------------------------------
1176 
centerAndResize()1177 void MusE::centerAndResize() {
1178 
1179     // set sensible initial sizes/positions for mainwin/transport (kybos)
1180 
1181 // Class QDesktopWidget deprecated as of Qt 5.11
1182 #if QT_VERSION >= 0x050b00
1183     const QRect screenRect = qApp->primaryScreen()->availableGeometry();
1184 #else
1185     const QRect screenRect = qApp->desktop()->availableGeometry();
1186 #endif
1187     const QSize screenSize = screenRect.size();
1188     int width = screenSize.width();
1189     int height = screenSize.height();
1190     width *= 0.9; // 90% of the screen size
1191     height *= 0.9; // 90% of the screen size
1192     const QSize newSize( width, height );
1193 
1194     setGeometry( QStyle::alignedRect(
1195                      Qt::LeftToRight,
1196                      Qt::AlignCenter,
1197                      newSize,
1198                      screenRect
1199                      )
1200                );
1201 
1202     MusEGlobal::config.geometryMain = geometry();
1203 
1204     if (MusEGlobal::config.transportVisible) {
1205         QRect r( geometry().x() + (width / 2),
1206                  geometry().y() + (height / 10),
1207                  0, 0);
1208         MusEGlobal::config.geometryTransport = r;
1209         // don't position the window here, it's done when file/template is loaded
1210     }
1211 }
1212 
1213 //---------------------------------------------------------
1214 //   setHeartBeat
1215 //---------------------------------------------------------
1216 
setHeartBeat()1217 void MusE::setHeartBeat()
1218       {
1219       if(MusEGlobal::debugMsg)
1220         fprintf(stderr, "MusE: STARTING Heartbeat timer\n");
1221       MusEGlobal::heartBeatTimer->start(1000/MusEGlobal::config.guiRefresh);
1222       }
1223 
stopHeartBeat()1224 void MusE::stopHeartBeat()
1225 {
1226   if(MusEGlobal::debugMsg)
1227     fprintf(stderr, "MusE: STOPPING Heartbeat timer\n");
1228   MusEGlobal::heartBeatTimer->stop();
1229 }
1230 
heartBeat()1231 void MusE::heartBeat()
1232 {
1233     if (cpuLoadToolbar->isVisible())
1234         cpuLoadToolbar->setValues(MusEGlobal::song->cpuLoad(),
1235                                   MusEGlobal::song->dspLoad(),
1236                                   MusEGlobal::song->xRunsCount());
1237 
1238     if (statusBar()->isVisible())
1239         cpuStatusBar->setValues(MusEGlobal::song->cpuLoad(),
1240                                 MusEGlobal::song->dspLoad(),
1241                                 MusEGlobal::song->xRunsCount());
1242 }
1243 
populateAddTrack()1244 void MusE::populateAddTrack()
1245 {
1246   arrangerView->populateAddTrack();
1247   arrangerView->updateShortcuts();
1248 }
1249 
blinkTimerSlot()1250 void MusE::blinkTimerSlot()
1251 {
1252   MusEGlobal::blinkTimerPhase = !MusEGlobal::blinkTimerPhase;
1253   emit blinkTimerToggled(MusEGlobal::blinkTimerPhase);
1254 }
1255 
messagePollTimerSlot()1256 void MusE::messagePollTimerSlot()
1257 {
1258   if(MusEGlobal::song)
1259     MusEGlobal::song->processIpcInEventBuffers();
1260 }
1261 
1262 //---------------------------------------------------------
1263 //   setDirty
1264 //---------------------------------------------------------
1265 
setDirty()1266 void MusE::setDirty()
1267       {
1268       MusEGlobal::song->dirty = true;
1269       setWindowTitle(projectTitle(project.absoluteFilePath()) + " <unsaved changes>");
1270       }
1271 
1272 //---------------------------------------------------
1273 //  loadDefaultSong
1274 //    if no songname entered on command line:
1275 //    startMode: 0  - load last song
1276 //               1  - load default template
1277 //               2  - load configured start song
1278 //---------------------------------------------------
1279 
loadDefaultSong(const QString & filename_override,bool use_template,bool load_config)1280 void MusE::loadDefaultSong(const QString& filename_override, bool use_template, bool load_config)
1281 {
1282   QString name;
1283   bool useTemplate = false;
1284   bool loadConfig = true;
1285   if (!filename_override.isEmpty())
1286   {
1287         name = filename_override;
1288         useTemplate = use_template;
1289         loadConfig = load_config;
1290   }
1291   else if (MusEGlobal::config.startMode == 0) {
1292               name = !projectRecentList.isEmpty() ? projectRecentList.first() : MusEGui::getUniqueUntitledName();
1293         fprintf(stderr, "starting with last song %s\n", name.toLatin1().constData());
1294         }
1295   else if (MusEGlobal::config.startMode == 1) {
1296         if(MusEGlobal::config.startSong.isEmpty()) // Sanity check to avoid some errors later
1297         {
1298           name = MusEGlobal::museGlobalShare + QString("/templates/default.med");
1299           loadConfig = false;
1300         }
1301         else
1302         {
1303           name = MusEGlobal::config.startSong;
1304           if (name == "default.med")
1305               name = MusEGlobal::museGlobalShare + QString("/templates/default.med");
1306           loadConfig = MusEGlobal::config.startSongLoadConfig;
1307         }
1308         useTemplate = true;
1309         fprintf(stderr, "starting with template %s\n", name.toLatin1().constData());
1310         }
1311   else if (MusEGlobal::config.startMode == 2) {
1312         if(MusEGlobal::config.startSong.isEmpty()) // Sanity check to avoid some errors later
1313         {
1314           name = MusEGlobal::museGlobalShare + QString("/templates/default.med");
1315           useTemplate = true;
1316           loadConfig = false;
1317         }
1318         else
1319         {
1320           name = MusEGlobal::config.startSong;
1321           loadConfig = MusEGlobal::config.startSongLoadConfig;
1322         }
1323         fprintf(stderr, "starting with pre configured song %s\n", name.toLatin1().constData());
1324   }
1325   loadProjectFile(name, useTemplate, loadConfig);
1326 }
1327 
1328 //---------------------------------------------------------
1329 //   resetDevices
1330 //---------------------------------------------------------
1331 
resetMidiDevices()1332 void MusE::resetMidiDevices()
1333       {
1334       MusEGlobal::audio->msgResetMidiDevices();
1335       }
1336 
1337 //---------------------------------------------------------
1338 //   initMidiDevices
1339 //---------------------------------------------------------
1340 
initMidiDevices()1341 void MusE::initMidiDevices()
1342       {
1343       //MusEGlobal::audio->msgIdle(true);
1344       MusEGlobal::audio->msgInitMidiDevices();
1345       //MusEGlobal::audio->msgIdle(false);
1346       }
1347 
1348 //---------------------------------------------------------
1349 //   localOff
1350 //---------------------------------------------------------
1351 
localOff()1352 void MusE::localOff()
1353       {
1354       MusEGlobal::audio->msgLocalOff();
1355       }
1356 
1357 //---------------------------------------------------------
1358 //   loadProjectFile
1359 //    load *.med, *.mid, *.kar
1360 //
1361 //    template - if true, load file but do not change
1362 //                project name
1363 //---------------------------------------------------------
1364 
1365 // for drop:
loadProjectFile(const QString & name)1366 void MusE::loadProjectFile(const QString& name)
1367       {
1368       loadProjectFile(name, false, false);
1369       }
1370 
loadProjectFile(const QString & name,bool songTemplate,bool doReadMidiPorts)1371 void MusE::loadProjectFile(const QString& name, bool songTemplate, bool doReadMidiPorts)
1372       {
1373       QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
1374 
1375       if(!progress)
1376           progress = new QProgressDialog();
1377 
1378       QString label = "Loading project " + QFileInfo(name).fileName();
1379       progress->setLabelText(label);
1380 //       progress->setWindowModality(Qt::WindowModal); // REMOVE Tim. Persistent routes. Removed for version warning dialog to take priority. FIXME
1381       progress->setCancelButton(nullptr);
1382       if (!songTemplate)
1383         progress->setMinimumDuration(0); // if we are loading a template it will probably be fast and we can wait before showing the dialog
1384 
1385       //
1386       // stop audio threads if running
1387       //
1388       progress->setValue(0);
1389       qApp->processEvents();
1390       bool restartSequencer = MusEGlobal::audio->isRunning();
1391       if (restartSequencer) {
1392             if (MusEGlobal::audio->isPlaying()) {
1393                   MusEGlobal::audio->msgPlay(false);
1394                   while (MusEGlobal::audio->isPlaying())
1395                         qApp->processEvents();
1396                   }
1397             seqStop();
1398             // REMOVE Tim. Persistent routes. TESTING.
1399             //MusEGlobal::audio->msgIdle(true);
1400             }
1401       microSleep(100000);
1402       progress->setValue(10);
1403       qApp->processEvents();
1404       loadProjectFile1(name, songTemplate, doReadMidiPorts);
1405       microSleep(100000);
1406       progress->setValue(90);
1407       qApp->processEvents();
1408       if (restartSequencer)
1409           seqStart();
1410         // REMOVE Tim. Persistent routes. TESTING.
1411         //MusEGlobal::audio->msgIdle(false);
1412       //MusEGlobal::song->connectPorts();
1413 
1414       arrangerView->updateVisibleTracksButtons();
1415       progress->setValue(100);
1416       qApp->processEvents();
1417       delete progress;
1418       progress = nullptr;
1419 
1420       QApplication::restoreOverrideCursor();
1421 
1422       // Prompt and send init sequences.
1423       MusEGlobal::audio->msgInitMidiDevices(false);
1424 
1425       if (MusEGlobal::song->getSongInfo().length()>0 && MusEGlobal::song->showSongInfoOnStartup()) {
1426           startSongInfo(false);
1427         }
1428       }
1429 
1430 //---------------------------------------------------------
1431 //   loadProjectFile
1432 //    load *.med, *.mid, *.kar
1433 //
1434 //    template - if true, load file but do not change
1435 //                project name
1436 //    doReadMidiPorts  - also read midi port configuration
1437 //---------------------------------------------------------
1438 
loadProjectFile1(const QString & name,bool songTemplate,bool doReadMidiPorts)1439 void MusE::loadProjectFile1(const QString& name, bool songTemplate, bool doReadMidiPorts)
1440       {
1441       if (mixer1)
1442             mixer1->clearAndDelete();
1443       if (mixer2)
1444             mixer2->clearAndDelete();
1445       _arranger->clear();      // clear track info
1446       if (clearSong(doReadMidiPorts))  // Allow not touching things like midi ports.
1447             return;
1448 
1449       MusEGlobal::recordAction->setChecked(false);
1450 
1451       progress->setValue(20);
1452       qApp->processEvents();
1453 
1454       QFileInfo fi(name);
1455       if (songTemplate)
1456       {
1457             if(!fi.isReadable()) {
1458                 QMessageBox::critical(this, QString("MusE"),
1459                     tr("Cannot read template"));
1460                 QApplication::restoreOverrideCursor();
1461                 return;
1462                 }
1463             project.setFile(MusEGui::getUniqueUntitledName());
1464             MusEGlobal::museProject = MusEGlobal::museProjectInitPath;
1465             QDir::setCurrent(QDir::homePath());
1466             }
1467       else {
1468             fprintf(stderr, "Setting project path to %s\n", fi.absolutePath().toLocal8Bit().constData());
1469             MusEGlobal::museProject = fi.absolutePath();
1470             project.setFile(name);
1471             QDir::setCurrent(MusEGlobal::museProject);
1472             }
1473 
1474       _lastProjectFilePath = name;
1475       _lastProjectWasTemplate = songTemplate;
1476       _lastProjectLoadedConfig = doReadMidiPorts;
1477 
1478       QString ex = fi.completeSuffix().toLower();
1479       QString mex = ex.section('.', -1, -1);
1480       if((mex == "gz") || (mex == "bz2"))
1481         mex = ex.section('.', -2, -2);
1482 
1483       if (ex.isEmpty() || mex == "med") {
1484             //
1485             //  read *.med file
1486             //
1487             bool popenFlag;
1488             FILE* f = MusEGui::fileOpen(this, fi.filePath(), QString(".med"), "r", popenFlag, true);
1489             if (f == nullptr) {
1490                   if (errno != ENOENT) {
1491                         QMessageBox::critical(this, QString("MusE"),
1492                            tr("File open error"));
1493                         setUntitledProject();
1494                         _lastProjectFilePath = QString();
1495                         }
1496                   else
1497                         setConfigDefaults();
1498                   }
1499             else {
1500 
1501                   if(songTemplate)
1502                   {
1503                     // The project is a template. Set the project's sample rate
1504                     //  to the system rate.
1505                     // NOTE: A template should never contain anything 'frame' related
1506                     //        like wave parts and events, or even audio automation graphs !
1507                     //       That is more under the category of say, 'demo songs'.
1508                     //       And here is the reason why:
1509                     MusEGlobal::projectSampleRate = MusEGlobal::sampleRate;
1510                   }
1511                   else
1512                   {
1513                     MusECore::Xml d_xml(f);
1514                     MusECore::SongfileDiscovery d_list(MusEGlobal::museProject);
1515                     d_list.readSongfile(d_xml);
1516 
1517                     // If it is a compressed file we cannot seek the stream, we must reopen it.
1518                     if(popenFlag)
1519                     {
1520                       pclose(f);
1521                       f = nullptr;
1522                       f = MusEGui::fileOpen(this, fi.filePath(), QString(".med"), "r", popenFlag, true);
1523                       if (f == nullptr) {
1524                             if (errno != ENOENT) {
1525                                   QMessageBox::critical(this, QString("MusE"),
1526                                     tr("File open error"));
1527                                   setUntitledProject();
1528                                   _lastProjectFilePath = QString();
1529                                   }
1530                             else
1531                                   setConfigDefaults();
1532                             }
1533                     }
1534                     else
1535                     {
1536                       // Be kind. Rewind.
1537                       fseek(f, 0, SEEK_SET);
1538                     }
1539 
1540                     // Is there a project sample rate setting in the song? (Setting added circa 2011).
1541                     if(d_list._waveList._projectSampleRateValid)
1542                     {
1543                       MusEGlobal::projectSampleRate = d_list._waveList._projectSampleRate;
1544                     }
1545                     else
1546                     {
1547                       int sugg_val;
1548                       QString sugg_phrase;
1549                       if(d_list._waveList.empty())
1550                       {
1551                         // Suggest the current system sample rate.
1552                         sugg_val = MusEGlobal::sampleRate;
1553                         sugg_phrase =
1554                         tr("The project has no project sample rate (added 2011).\n"
1555                            "Please enter a rate. The current system rate (%1Hz)\n"
1556                            " is suggested, and cancelling uses it:").arg(MusEGlobal::sampleRate);
1557                       }
1558                       else
1559                       {
1560                         // Suggest the most common sample rate used in the song.
1561                         sugg_val = d_list._waveList.getMostCommonSamplerate();
1562                         sugg_phrase =
1563                         tr("The project has audio waves, but no project sample rate (added 2011).\n"
1564                            "Please enter a rate. The most common wave rate found is suggested,\n"
1565                            " the project was probably made with it. Cancelling uses the\n"
1566                            " current system rate (%1Hz):").arg(MusEGlobal::sampleRate);
1567                       }
1568 
1569                       bool ok;
1570                       const int res = QInputDialog::getInt(
1571                         this, tr("Project sample rate"),
1572                         sugg_phrase, sugg_val,
1573                         0, (10 * 1000 * 1000), 1, &ok);
1574 
1575                       if(ok)
1576                         MusEGlobal::projectSampleRate = res;
1577                       else
1578                         MusEGlobal::projectSampleRate = MusEGlobal::sampleRate;
1579                     }
1580 
1581                     if (!songTemplate &&
1582                         //MusEGlobal::audioDevice->deviceType() != AudioDevice::DUMMY_AUDIO &&  // Why exclude dummy?
1583                         MusEGlobal::projectSampleRate != MusEGlobal::sampleRate)
1584                     {
1585                       QString msg = QString("The sample rate in this project (%1Hz) and the\n"
1586                         " current system setting (%2Hz) differ.\n"
1587                         "Project timing will be scaled to match the new sample rate.\n"
1588                         "Caution: Accuracy and sound quality may vary with rate and settings.\n\n"
1589                         "Live realtime audio sample rate converters will be enabled\n"
1590                         " on audio files where required.\n"
1591                         "The files can be permanently converted to the new sample rate.\n\n"
1592                         "Save this song if you are sure you didn't mean to open it\n"
1593                         " at the original sample rate.").arg(MusEGlobal::projectSampleRate).arg(MusEGlobal::sampleRate);
1594                       QMessageBox::warning(MusEGlobal::muse,"Wrong sample rate", msg);
1595                       // Automatically convert the project.
1596                       // No: Try to keep the rate until user tells it to change.
1597                       //convertProjectSampleRate();
1598                     }
1599                   }
1600 
1601                   if(f) {
1602                         MusECore::Xml xml(f);
1603                         read(xml, doReadMidiPorts, songTemplate);
1604                         bool fileError = ferror(f);
1605                         popenFlag ? pclose(f) : fclose(f);
1606                         if (fileError) {
1607                               QMessageBox::critical(this, QString("MusE"),
1608                                 tr("File read error"));
1609                               setUntitledProject();
1610                               _lastProjectFilePath = QString();
1611                               }
1612                         }
1613                   }
1614             }
1615       else if (mex == "mid" || mex == "kar") {
1616             setConfigDefaults();
1617             if (!importMidi(name, false))
1618             {
1619                   setUntitledProject();
1620                   _lastProjectFilePath = QString();
1621             }
1622             }
1623       else {
1624             QMessageBox::critical(this, QString("MusE"),
1625                tr("Unknown File Format: %1").arg(ex));
1626             setUntitledProject();
1627             _lastProjectFilePath = QString();
1628             }
1629       if (!songTemplate) {
1630             addProjectToRecentList(project.absoluteFilePath());
1631             setWindowTitle(projectTitle(project.absoluteFilePath()));
1632             }
1633 
1634       for (const auto& it : toplevels) {
1635           if (it->isMdiWin() && it->type() == TopWin::ARRANGER) {
1636               mdiArea->setActiveSubWindow(it->getMdiWin());
1637               break;
1638           }
1639       }
1640 
1641       MusEGlobal::song->dirty = false;
1642       progress->setValue(30);
1643       qApp->processEvents();
1644 
1645       viewTransportAction->setChecked(MusEGlobal::config.transportVisible);
1646       viewBigtimeAction->setChecked(MusEGlobal::config.bigTimeVisible);
1647       viewMarkerAction->setChecked(MusEGlobal::config.markerVisible);
1648 //      viewArrangerAction->setChecked(MusEGlobal::config.arrangerVisible);
1649 
1650 // REMOVE Tim. automation. Removed.
1651 // Deprecated. MusEGlobal::automation is now fixed TRUE
1652 //   for now until we decide what to do with it.
1653 //       autoMixerAction->setChecked(MusEGlobal::automation);
1654 
1655       showBigtime(MusEGlobal::config.bigTimeVisible);
1656 
1657       // NOTICE! Mixers may set their own maximum size according to their content, on SongChanged.
1658       //         Therefore if the mixer is ALREADY OPEN, it may have a maximum size imposed on it,
1659       //          which may be SMALLER than any new size we might try to set after this.
1660       //         So we MUST RESET maximium size now, BEFORE attempts to set size. As per docs:
1661       if(mixer1)
1662       {
1663         mixer1->setMaximumSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX);
1664         mixer1->setGeometry(MusEGlobal::config.mixer1.geometry);
1665       }
1666       if(mixer2)
1667       {
1668         mixer2->setMaximumSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX);
1669         mixer2->setGeometry(MusEGlobal::config.mixer2.geometry);
1670       }
1671 
1672       showMixer1(MusEGlobal::config.mixer1Visible);
1673       showMixer2(MusEGlobal::config.mixer2Visible);
1674 
1675 // Loading a file should not manipulate the geometry of the main window (kybos)
1676 //      resize(MusEGlobal::config.geometryMain.size());
1677 //      move(MusEGlobal::config.geometryMain.topLeft());
1678 
1679       transport->move(MusEGlobal::config.geometryTransport.topLeft());
1680       showTransport(MusEGlobal::config.transportVisible);
1681 
1682       progress->setValue(40);
1683       qApp->processEvents();
1684 
1685       transport->setMasterFlag(MusEGlobal::tempomap.masterFlag());
1686       MusEGlobal::punchinAction->setChecked(MusEGlobal::song->punchin());
1687       MusEGlobal::punchoutAction->setChecked(MusEGlobal::song->punchout());
1688       MusEGlobal::loopAction->setChecked(MusEGlobal::song->loop());
1689       // Inform the rest of the app the song changed, with all flags MINUS
1690       //  these flags which are already sent in the call to MusE::read() above:
1691       MusEGlobal::song->update(~SC_TRACK_INSERTED);
1692       MusEGlobal::song->updatePos();
1693       arrangerView->clipboardChanged(); // enable/disable "Paste"
1694       arrangerView->selectionChanged(); // enable/disable "Copy" & "Paste"
1695       arrangerView->scoreNamingChanged(); // inform the score menus about the new scores and their names
1696       progress->setValue(50);
1697       qApp->processEvents();
1698 
1699       // Moved here from above due to crash with a song loaded and then File->New.
1700       // Marker view list was not updated, had non-existent items from marker list (cleared in ::clear()).
1701       showMarker(MusEGlobal::config.markerVisible);
1702       }
1703 
1704 //---------------------------------------------------------
1705 //   fileClose
1706 //---------------------------------------------------------
1707 
fileClose()1708 void MusE::fileClose()
1709 {
1710     // For now we just don't read the ports, leaving the last setup intact.
1711     const bool doReadMidiPorts = false;
1712 
1713     //   if (mixer1)
1714     //         mixer1->clearAndDelete();
1715     //   if (mixer2)
1716     //         mixer2->clearAndDelete();
1717     //   _arranger->clear();      // clear track info
1718     if(clearSong(doReadMidiPorts))  // Allow not touching things like midi ports.
1719         return;
1720 
1721     MusEGlobal::recordAction->setChecked(false);
1722 
1723     //setConfigDefaults();
1724     QString name(MusEGui::getUniqueUntitledName());
1725     MusEGlobal::museProject = MusEGlobal::museProjectInitPath;
1726     //QDir::setCurrent(QDir::homePath());
1727     QDir::setCurrent(MusEGlobal::museProject);
1728     project.setFile(name);
1729     _lastProjectFilePath = QString();
1730     _lastProjectWasTemplate = false;
1731     _lastProjectLoadedConfig = true;
1732 
1733     setWindowTitle(projectTitle(name));
1734 
1735     //writeTopwinState=true;
1736 
1737     MusEGlobal::song->dirty = false;
1738 
1739     // Inform the rest of the app the song changed, with all flags.
1740     MusEGlobal::song->update(SC_EVERYTHING);
1741     MusEGlobal::song->updatePos();
1742     arrangerView->clipboardChanged(); // enable/disable "Paste"
1743     arrangerView->selectionChanged(); // enable/disable "Copy" & "Paste"
1744     arrangerView->scoreNamingChanged(); // inform the score menus about the new scores and their names
1745 }
1746 
1747 //---------------------------------------------------------
1748 //   setUntitledProject
1749 //---------------------------------------------------------
1750 
setUntitledProject()1751 void MusE::setUntitledProject()
1752       {
1753       setConfigDefaults();
1754       QString name(MusEGui::getUniqueUntitledName());
1755       MusEGlobal::museProject = MusEGlobal::museProjectInitPath;
1756       QDir::setCurrent(QDir::homePath());
1757       project.setFile(name);
1758       setWindowTitle(projectTitle(name));
1759       writeTopwinState=true;
1760       }
1761 
1762 //---------------------------------------------------------
1763 //   setConfigDefaults
1764 //---------------------------------------------------------
1765 
setConfigDefaults()1766 void MusE::setConfigDefaults()
1767       {
1768       MusECore::readConfiguration();    // used for reading midi files
1769       MusEGlobal::song->dirty = false;
1770       }
1771 
1772 //---------------------------------------------------------
1773 //   MusE::loadProject
1774 //---------------------------------------------------------
1775 
loadProject()1776 void MusE::loadProject()
1777       {
1778       bool doReadMidiPorts;
1779       QString fn = MusEGui::getOpenFileName(QString(""), MusEGlobal::med_file_pattern, this,
1780          tr("MusE: load project"), &doReadMidiPorts);
1781       if (!fn.isEmpty()) {
1782             MusEGlobal::museProject = QFileInfo(fn).absolutePath();
1783             QDir::setCurrent(QFileInfo(fn).absolutePath());
1784             loadProjectFile(fn, false, doReadMidiPorts);
1785             }
1786       }
1787 
1788 //---------------------------------------------------------
1789 //   loadTemplate
1790 //---------------------------------------------------------
1791 
loadTemplate()1792 void MusE::loadTemplate()
1793       {
1794       bool doReadMidiPorts;
1795       QString fn = MusEGui::getOpenFileName(QString("templates"), MusEGlobal::med_file_pattern, this,
1796                                                tr("MusE: load template"), &doReadMidiPorts, MusEGui::MFileDialog::GLOBAL_VIEW);
1797       if (!fn.isEmpty()) {
1798             loadProjectFile(fn, true, doReadMidiPorts);
1799             setUntitledProject();
1800             }
1801       }
1802 
1803 //---------------------------------------------------------
1804 //   loadDefaultTemplate
1805 //---------------------------------------------------------
1806 
loadDefaultTemplate()1807 void MusE::loadDefaultTemplate()
1808 {
1809         loadProjectFile(MusEGlobal::museGlobalShare + QString("/templates/default.med"), true, false);
1810         setUntitledProject();
1811 }
1812 
1813 //---------------------------------------------------------
1814 //   save
1815 //---------------------------------------------------------
1816 
save()1817 bool MusE::save()
1818       {
1819       if (MusEGlobal::museProject == MusEGlobal::museProjectInitPath )
1820             return saveAs();
1821       else
1822             return save(project.filePath(), false, writeTopwinState);
1823       }
1824 
1825 //---------------------------------------------------------
1826 //   save
1827 //---------------------------------------------------------
1828 
save(const QString & name,bool overwriteWarn,bool writeTopwins)1829 bool MusE::save(const QString& name, bool overwriteWarn, bool writeTopwins)
1830       {
1831 //       QString backupCommand;
1832 
1833       QFile currentName(name);
1834       if (QFile::exists(name)) {
1835             currentName.copy(name+".backup");
1836             //backupCommand.sprintf("cp \"%s\" \"%s.backup\"", name.toLatin1().constData(), name.toLatin1().constData());
1837             }
1838       else if (QFile::exists(name + QString(".med"))) {
1839             QString currentName2(name+".med");
1840             currentName.copy(name+".med.backup");
1841             //backupCommand.sprintf("cp \"%s.med\" \"%s.med.backup\"", name.toLatin1().constData(), name.toLatin1().constData());
1842             }
1843 //      if (!backupCommand.isEmpty())
1844 //            system(backupCommand.toLatin1().constData());
1845 
1846       bool popenFlag;
1847       FILE* f = MusEGui::fileOpen(this, name, QString(".med"), "w", popenFlag, false, overwriteWarn);
1848       if (f == nullptr)
1849             return false;
1850       MusECore::Xml xml(f);
1851       write(xml, writeTopwins);
1852       if (ferror(f)) {
1853             QString s = "Write File\n" + name + "\nfailed: "
1854                + QString(strerror(errno));
1855             QMessageBox::critical(this,
1856                tr("MusE: Write File failed"), s);
1857             popenFlag? pclose(f) : fclose(f);
1858             unlink(name.toLatin1().constData());
1859             return false;
1860             }
1861       else {
1862             popenFlag? pclose(f) : fclose(f);
1863             MusEGlobal::song->dirty = false;
1864             setWindowTitle(projectTitle(project.absoluteFilePath()));
1865             saveIncrement = 0;
1866             return true;
1867             }
1868       }
1869 
1870 //---------------------------------------------------------
1871 //   quitDoc
1872 //---------------------------------------------------------
1873 
quitDoc()1874 void MusE::quitDoc()
1875       {
1876       close();
1877       }
1878 
1879 //---------------------------------------------------------
1880 //   closeEvent
1881 //---------------------------------------------------------
1882 
closeEvent(QCloseEvent * event)1883 void MusE::closeEvent(QCloseEvent* event)
1884 {
1885     QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
1886     MusEGlobal::song->setStop(true);
1887     //
1888     // wait for sequencer
1889     //
1890     while (MusEGlobal::audio->isPlaying()) {
1891         qApp->processEvents();
1892     }
1893     if (MusEGlobal::song->dirty) {
1894         int n = 0;
1895         n = QMessageBox::warning(this, appName,
1896                                  tr("The current project contains unsaved data.\n"
1897                                     "Save current project?"),
1898                                  tr("&Save"), tr("S&kip"), tr("&Cancel"), 0, 2);
1899         if (n == 0) {
1900             if (!save())      // don't quit if save failed
1901             {
1902                 setRestartingApp(false); // Cancel any restart.
1903                 event->ignore();
1904                 QApplication::restoreOverrideCursor();
1905                 return;
1906             }
1907         }
1908         else if (n == 2)
1909         {
1910             setRestartingApp(false); // Cancel any restart.
1911             event->ignore();
1912             QApplication::restoreOverrideCursor();
1913             return;
1914         }
1915     }
1916 
1917 
1918     seqStop();
1919 
1920     MusECore::WaveTrackList* wt = MusEGlobal::song->waves();
1921     for (MusECore::iWaveTrack iwt = wt->begin(); iwt != wt->end(); ++iwt) {
1922         MusECore::WaveTrack* t = *iwt;
1923         if (t->recFile() && t->recFile()->samples() == 0) {
1924             t->recFile()->remove();
1925         }
1926     }
1927 
1928     // Don't use saveGeoemetry for the main window: Qt has issues (data gets invalid)
1929     //    when both standard MusE and AppImage are used on the same PC
1930     //      QSettings settings;
1931     //      settings.setValue("MusE/geometry", saveGeometry());
1932 
1933     MusEGlobal::config.geometryMain = geometry();
1934 
1935     // must be done here as the close events of child windows are not always called on quit
1936     saveStateTopLevels();
1937 
1938     saveStateExtra();
1939 
1940     writeGlobalConfiguration();
1941 
1942     if(MusEGlobal::debugMsg)
1943         fprintf(stderr, "MusE: Exiting JackAudio\n");
1944     MusECore::exitJackAudio();
1945     if(MusEGlobal::debugMsg)
1946         fprintf(stderr, "MusE: Exiting DummyAudio\n");
1947     MusECore::exitDummyAudio();
1948 #ifdef HAVE_RTAUDIO
1949     if(MusEGlobal::debugMsg)
1950         fprintf(stderr, "MusE: Exiting RtAudio\n");
1951     MusECore::exitRtAudio();
1952 #endif
1953     if(MusEGlobal::debugMsg)
1954         fprintf(stderr, "MusE: Exiting Metronome\n");
1955     MusECore::exitMetronome();
1956 
1957     MusEGlobal::song->cleanupForQuit();
1958 
1959     // Give midi devices a chance to close first, above in cleanupForQuit.
1960     if(MusEGlobal::debugMsg)
1961         fprintf(stderr, "Muse: Exiting ALSA midi\n");
1962     MusECore::exitMidiAlsa();
1963 
1964     if(MusEGlobal::debugMsg)
1965         fprintf(stderr, "Muse: Cleaning up temporary wavefiles + peakfiles\n");
1966     // Cleanup temporary wavefiles + peakfiles used for undo
1967     for (std::list<QString>::iterator i = MusECore::temporaryWavFiles.begin(); i != MusECore::temporaryWavFiles.end(); i++) {
1968         QString filename = *i;
1969         QFileInfo fi(filename);
1970         QDir d = fi.dir();
1971         d.remove(filename);
1972         d.remove(fi.completeBaseName() + ".wca");
1973     }
1974 
1975     if(MusEGlobal::usePythonBridge)
1976     {
1977         fprintf(stderr, "Stopping MusE Pybridge...\n");
1978         if(!stopPythonBridge())
1979             fprintf(stderr, "MusE: Could not stop Python bridge\n");
1980         else
1981             fprintf(stderr, "MusE: Pybridge stopped\n");
1982     }
1983 
1984 #ifdef HAVE_LASH
1985     // Disconnect gracefully from LASH.
1986     if(lash_client)
1987     {
1988         if(MusEGlobal::debugMsg)
1989             fprintf(stderr, "MusE: Disconnecting from LASH\n");
1990         lash_event_t* lashev = lash_event_new_with_type (LASH_Quit);
1991         lash_send_event(lash_client, lashev);
1992     }
1993 #endif
1994 
1995     if(MusEGlobal::debugMsg)
1996         fprintf(stderr, "MusE: Exiting Dsp\n");
1997     AL::exitDsp();
1998 
1999     if(MusEGlobal::debugMsg)
2000         fprintf(stderr, "MusE: Exiting OSC\n");
2001     MusECore::exitOSC();
2002 
2003     delete MusEGlobal::audioPrefetch;
2004     delete MusEGlobal::audio;
2005 
2006     // Destroy the sequencer object if it exists.
2007     MusECore::exitMidiSequencer();
2008 
2009     delete MusEGlobal::song;
2010 
2011     if(MusEGlobal::debugMsg)
2012         fprintf(stderr, "MusE: Deleting icons\n");
2013     deleteIcons();
2014 
2015     if(MusEGlobal::debugMsg)
2016         fprintf(stderr, "MusE: Deleting all parentless dialogs and widgets\n");
2017     deleteParentlessDialogs();
2018 
2019     qApp->quit();
2020 }
2021 
2022 
2023 //---------------------------------------------------------
2024 //   showMarker
2025 //---------------------------------------------------------
2026 
showMarker(bool flag)2027 void MusE::showMarker(bool flag)
2028 {
2029     markerDock->setVisible(flag);
2030 }
2031 
2032 //---------------------------------------------------------
2033 //   toggleArranger
2034 //---------------------------------------------------------
2035 
2036 //void MusE::toggleArranger(bool checked)
2037 //      {
2038 //      showArranger(checked);
2039 //      }
2040 
2041 //---------------------------------------------------------
2042 //   showArranger
2043 //---------------------------------------------------------
2044 
2045 //void MusE::showArranger(bool flag)
2046 //      {
2047 //      if(arrangerView->isVisible() != flag)
2048 //        arrangerView->setVisible(flag);
2049 //      if(viewArrangerAction->isChecked() != flag)
2050 //        viewArrangerAction->setChecked(flag);
2051 //      if (!flag)
2052 //        if (currentMenuSharingTopwin == arrangerView)
2053 //          setCurrentMenuSharingTopwin(nullptr);
2054 //      updateWindowMenu();
2055 //      }
2056 
2057 //---------------------------------------------------------
2058 //   arrangerClosed
2059 //---------------------------------------------------------
2060 
2061 //void MusE::arrangerClosed()
2062 //{
2063     //      if(viewArrangerAction->isChecked())
2064     //        viewArrangerAction->setChecked(false);
2065     //      updateWindowMenu();
2066 
2067     ////      if (mdiArea->viewMode() == QMdiArea::TabbedView) {
2068     ////          QTabBar* tb = mdiArea->findChild<QTabBar*>();
2069     ////          if (tb) {
2070     ////              int i = tb->currentIndex();
2071     ////              tb->setTabEnabled(i, false);
2072     ////              tb->setTabButton(i, QTabBar::RightSide, nullptr);
2073     ////          }
2074     ////      }
2075 
2076     //      // focus the last activated topwin which is not the arranger view
2077     //      QList<QMdiSubWindow*> l = mdiArea->subWindowList(QMdiArea::StackingOrder);
2078     //      for (QList<QMdiSubWindow*>::const_reverse_iterator lit=l.rbegin(); lit!=l.rend(); lit++)
2079     //        if ((*lit)->isVisible() && (*lit)->widget() != arrangerView)
2080     //        {
2081     //          if (MusEGlobal::debugMsg)
2082     //            fprintf(stderr, "bringing '%s' to front instead of closed arranger window\n",(*lit)->widget()->windowTitle().toLatin1().data());
2083 
2084     //          bringToFront((*lit)->widget());
2085 
2086     //          break;
2087     //        }
2088 //}
2089 
2090 //---------------------------------------------------------
2091 //   toggleTransport
2092 //---------------------------------------------------------
2093 
toggleTransport(bool checked)2094 void MusE::toggleTransport(bool checked)
2095       {
2096       showTransport(checked);
2097       }
2098 
2099 //---------------------------------------------------------
2100 //   showTransport
2101 //---------------------------------------------------------
2102 
showTransport(bool flag)2103 void MusE::showTransport(bool flag)
2104       {
2105       if(transport->isVisible() != flag)
2106         transport->setVisible(flag);
2107       if(viewTransportAction->isChecked() != flag)
2108          viewTransportAction->setChecked(flag);
2109 }
2110 
2111 #ifdef _WIN32
CalculateCPULoad(unsigned long long idleTicks,unsigned long long totalTicks)2112 static float CalculateCPULoad(unsigned long long idleTicks, unsigned long long totalTicks)
2113 {
2114    static unsigned long long _previousTotalTicks = 0;
2115    static unsigned long long _previousIdleTicks = 0;
2116 
2117    unsigned long long totalTicksSinceLastTime = totalTicks-_previousTotalTicks;
2118    unsigned long long idleTicksSinceLastTime  = idleTicks-_previousIdleTicks;
2119 
2120    float ret = 1.0f-((totalTicksSinceLastTime > 0) ? ((float)idleTicksSinceLastTime)/totalTicksSinceLastTime : 0);
2121 
2122    _previousTotalTicks = totalTicks;
2123    _previousIdleTicks  = idleTicks;
2124    return ret;
2125 }
2126 
FileTimeToInt64(const FILETIME & ft)2127 static unsigned long long FileTimeToInt64(const FILETIME & ft)
2128 {
2129    return (((unsigned long long)(ft.dwHighDateTime))<<32)|((unsigned long long)ft.dwLowDateTime);
2130 }
2131 #endif
2132 
getCPULoad()2133 float MusE::getCPULoad()
2134 {
2135 #ifdef _WIN32
2136    FILETIME idleTime, kernelTime, userTime;
2137    return GetSystemTimes(&idleTime, &kernelTime, &userTime) ? CalculateCPULoad(FileTimeToInt64(idleTime), FileTimeToInt64(kernelTime)+FileTimeToInt64(userTime)) : -1.0f;
2138 #else
2139     struct rusage ru;
2140     struct timespec curSysTime;
2141     if(clock_gettime(CLOCK_REALTIME, &curSysTime) != 0)
2142     {
2143        return 0.0f;
2144     }
2145     //float fLoad = 0.0f;
2146     if(getrusage(RUSAGE_SELF, &ru) != 0)
2147     {
2148         return 0.0f;
2149     }
2150     long msSysElapsed = (curSysTime.tv_nsec / 1000000L) + curSysTime.tv_sec * 1000L;
2151     msSysElapsed -= (lastSysTime.tv_nsec / 1000000L) + lastSysTime.tv_sec * 1000L;
2152     long msCpuElasped = (ru.ru_utime.tv_usec / 1000L) + ru.ru_utime.tv_sec * 1000L;
2153     msCpuElasped -= (lastCpuTime.tv_usec / 1000L) + lastCpuTime.tv_sec * 1000L;
2154     if(msSysElapsed > 0)
2155     {
2156         fAvrCpuLoad += (float)((double)msCpuElasped / (double)msSysElapsed);
2157         avrCpuLoadCounter++;
2158     }
2159     lastCpuTime = ru.ru_utime;
2160     lastSysTime = curSysTime;
2161     if(avrCpuLoadCounter > 10)
2162     {
2163         fCurCpuLoad = (fAvrCpuLoad / (float)avrCpuLoadCounter) * 100.0f;
2164         fAvrCpuLoad = 0.0f;
2165         avrCpuLoadCounter = 0;
2166     }
2167 
2168     return fCurCpuLoad;
2169 #endif
2170 }
2171 
saveAsNewProject()2172 void MusE::saveAsNewProject()
2173 {
2174   auto storedProject = project;
2175   project = QFileInfo();
2176   auto storedMusEProject = MusEGlobal::museProject;
2177   MusEGlobal::museProject = MusEGlobal::museProjectInitPath;
2178   saveAs(true);
2179   if (MusEGlobal::museProject == MusEGlobal::museProjectInitPath )
2180   {
2181     // change was rejected, restore the old project
2182     project = storedProject;
2183     MusEGlobal::museProject = storedMusEProject;
2184   }
2185 }
2186 
saveNewRevision()2187 void MusE::saveNewRevision()
2188 {
2189   if (MusEGlobal::museProject == MusEGlobal::museProjectInitPath )
2190   {
2191     saveAs();
2192     return;
2193   }
2194 
2195   QString newFilePath = "";
2196   QString oldProjectFileName = project.filePath();
2197   MusEGui::SaveNewRevisionDialog newRevision(MusEGlobal::muse, project);
2198 
2199   newFilePath = newRevision.getNewRevision();
2200 
2201   if (newFilePath.isEmpty())
2202   {
2203     // could not set revision automatically, open dialog.
2204     newFilePath = newRevision.getNewRevisionWithDialog();
2205   }
2206 
2207   if (newFilePath.isEmpty())
2208     return;
2209 
2210   bool ok = save(newFilePath, true, writeTopwinState);
2211   if (ok)
2212   {
2213     project.setFile(newFilePath);
2214     _lastProjectFilePath = newFilePath;
2215     _lastProjectWasTemplate = false;
2216     _lastProjectLoadedConfig = true;
2217     setWindowTitle(projectTitle(project.absoluteFilePath()));
2218 
2219     // replace project in lastProjects
2220     if (projectRecentList.contains(oldProjectFileName))
2221       projectRecentList.removeAt(projectRecentList.indexOf(oldProjectFileName));
2222     addProjectToRecentList(newFilePath);
2223 
2224     project.setFile(newFilePath);
2225   }
2226 }
2227 //---------------------------------------------------------
2228 //   saveAs
2229 //---------------------------------------------------------
2230 
saveAs(bool overrideProjectSaveDialog)2231 bool MusE::saveAs(bool overrideProjectSaveDialog)
2232 {
2233   QString name;
2234   // if this is the initial save and ProjectDialog is enabled, use that.
2235   if (overrideProjectSaveDialog ||
2236       (MusEGlobal::config.useProjectSaveDialog && MusEGlobal::museProject == MusEGlobal::museProjectInitPath))
2237   {
2238     MusEGui::ProjectCreateImpl pci(MusEGlobal::muse);
2239     pci.setWriteTopwins(writeTopwinState);
2240     if (pci.exec() == QDialog::Rejected) {
2241       return false;
2242     }
2243 
2244     MusEGlobal::song->setSongInfo(pci.getSongInfo(), true);
2245     name = pci.getProjectPath();
2246     writeTopwinState=pci.getWriteTopwins();
2247   }
2248   else
2249   {
2250     name = MusEGui::getSaveFileName(QString(""), MusEGlobal::med_file_save_pattern, this, tr("MusE: Save As"), &writeTopwinState);
2251     if (name.isEmpty())
2252       return false;
2253   }
2254 
2255   MusEGlobal::museProject = QFileInfo(name).absolutePath();
2256   QDir dirmanipulator;
2257   if (!dirmanipulator.mkpath(MusEGlobal::museProject)) {
2258     QMessageBox::warning(this,"Path error","Can't create project path", QMessageBox::Ok);
2259     return false;
2260   }
2261 
2262   bool ok = false;
2263   if (!name.isEmpty()) {
2264     QString tempOldProj = MusEGlobal::museProject;
2265     MusEGlobal::museProject = QFileInfo(name).absolutePath();
2266     ok = save(name, true, writeTopwinState);
2267     if (ok) {
2268       project.setFile(name);
2269       _lastProjectFilePath = name;
2270       _lastProjectWasTemplate = false;
2271       _lastProjectLoadedConfig = true;
2272       setWindowTitle(projectTitle(project.absoluteFilePath()));
2273       addProjectToRecentList(name);
2274     }
2275     else
2276       MusEGlobal::museProject = tempOldProj;
2277 
2278     QDir::setCurrent(MusEGlobal::museProject);
2279   }
2280 
2281   return ok;
2282 }
2283 //---------------------------------------------------------
2284 //   saveAsTemplate
2285 //---------------------------------------------------------
2286 
saveAsTemplate()2287 void MusE::saveAsTemplate()
2288 {
2289   QString templatesDir = MusEGlobal::configPath + QString("/") + "templates";
2290 
2291   printf ("templates dir %s\n", templatesDir.toLatin1().data());
2292 
2293   QDir dirmanipulator;
2294   if (!dirmanipulator.mkpath(templatesDir)) {
2295     QMessageBox::warning(this,"Path error","Could not create templates directory", QMessageBox::Ok);
2296     return;
2297   }
2298   QString name;
2299   name = MusEGui::getSaveFileName(QString("templates"), MusEGlobal::med_file_save_pattern, this, tr("MusE: Save As"), &writeTopwinState, MFileDialog::USER_VIEW);
2300   if (name.isEmpty())
2301     return;
2302 
2303   auto finalPath = QFileInfo(name).absolutePath();
2304   if (!dirmanipulator.mkpath(finalPath)) {
2305     QMessageBox::warning(this,"Path error","Can't create final project path", QMessageBox::Ok);
2306     return;
2307   }
2308   save(name, true, false);
2309 }
2310 //---------------------------------------------------------
2311 //   startEditor
2312 //---------------------------------------------------------
2313 
startEditor(MusECore::PartList * pl,int type)2314 void MusE::startEditor(MusECore::PartList* pl, int type)
2315       {
2316       switch (type) {
2317             case 0: startPianoroll(pl, true); break;
2318             case 1: startListEditor(pl); break;
2319             case 3: startDrumEditor(pl, true); break;
2320             case 4: startWaveEditor(pl); break;
2321             }
2322       }
2323 
2324 //---------------------------------------------------------
2325 //   startEditor
2326 //---------------------------------------------------------
2327 
startEditor(MusECore::Track * t)2328 void MusE::startEditor(MusECore::Track* t)
2329       {
2330       switch (t->type()) {
2331             case MusECore::Track::MIDI: startPianoroll(); break;
2332             case MusECore::Track::DRUM: startDrumEditor(); break;
2333             case MusECore::Track::WAVE: startWaveEditor(); break;
2334             default:
2335                   break;
2336             }
2337       }
2338 
2339 //---------------------------------------------------------
2340 //   getMidiPartsToEdit
2341 //---------------------------------------------------------
2342 
getMidiPartsToEdit()2343 MusECore::PartList* MusE::getMidiPartsToEdit()
2344       {
2345       MusECore::PartList* pl = MusECore::getSelectedMidiParts();
2346       if (pl->empty()) {
2347             QMessageBox::critical(this, QString("MusE"), tr("Nothing to edit"));
2348             return nullptr;
2349             }
2350       return pl;
2351       }
2352 
2353 
2354 //---------------------------------------------------------
2355 //   startScoreEdit
2356 //---------------------------------------------------------
2357 
openInScoreEdit_oneStaffPerTrack(QWidget * dest)2358 void MusE::openInScoreEdit_oneStaffPerTrack(QWidget* dest)
2359 {
2360  openInScoreEdit((MusEGui::ScoreEdit*)dest, false);
2361 }
2362 
openInScoreEdit_allInOne(QWidget * dest)2363 void MusE::openInScoreEdit_allInOne(QWidget* dest)
2364 {
2365  openInScoreEdit((MusEGui::ScoreEdit*)dest, true);
2366 }
2367 
openInScoreEdit(MusEGui::ScoreEdit * destination,bool allInOne)2368 void MusE::openInScoreEdit(MusEGui::ScoreEdit* destination, bool allInOne)
2369 {
2370  MusECore::PartList* pl = getMidiPartsToEdit();
2371  if (pl == nullptr)
2372     return;
2373  openInScoreEdit(destination, pl, allInOne);
2374 }
2375 
openInScoreEdit(MusEGui::ScoreEdit * destination,MusECore::PartList * pl,bool allInOne)2376 void MusE::openInScoreEdit(MusEGui::ScoreEdit* destination, MusECore::PartList* pl, bool allInOne)
2377 {
2378  if (destination==nullptr) // if no destination given, create a new one
2379  {
2380       destination = new MusEGui::ScoreEdit(this, nullptr, _arranger->cursorValue());
2381       toplevels.push_back(destination);
2382       destination->show();
2383       connect(destination, SIGNAL(isDeleting(MusEGui::TopWin*)), SLOT(toplevelDeleting(MusEGui::TopWin*)));
2384       connect(destination, SIGNAL(name_changed()), arrangerView, SLOT(scoreNamingChanged()));
2385       //connect(muse, SIGNAL(configChanged()), destination, SLOT(config_changed()));
2386       //commented out by flo, because the ScoreEditor connects to all
2387       //relevant signals on his own
2388 
2389       arrangerView->updateScoreMenus();
2390       updateWindowMenu();
2391   }
2392 
2393   destination->add_parts(pl, allInOne);
2394 }
2395 
startScoreQuickly()2396 void MusE::startScoreQuickly()
2397 {
2398  openInScoreEdit_oneStaffPerTrack(nullptr);
2399 }
2400 
2401 //---------------------------------------------------------
2402 //   startPianoroll
2403 //---------------------------------------------------------
2404 
startPianoroll(bool newwin)2405 void MusE::startPianoroll(bool newwin)
2406 {
2407     MusECore::PartList* pl = getMidiPartsToEdit();
2408     if (pl == nullptr)
2409         return;
2410 
2411     if (!filterInvalidParts(TopWin::PIANO_ROLL, pl))
2412         return;
2413 
2414     startPianoroll(pl, true, newwin);
2415 }
2416 
startPianoroll(MusECore::PartList * pl,bool showDefaultCtrls,bool newwin)2417 void MusE::startPianoroll(MusECore::PartList* pl, bool showDefaultCtrls, bool newwin)
2418 {
2419     if (!filterInvalidParts(TopWin::PIANO_ROLL, pl))
2420         return;
2421 
2422     if (!newwin && findOpenEditor(TopWin::PIANO_ROLL, pl))
2423         return;
2424 
2425     MusEGui::PianoRoll* pianoroll = new MusEGui::PianoRoll(pl, this, nullptr, _arranger->cursorValue(), showDefaultCtrls);
2426     toplevels.push_back(pianoroll);
2427     pianoroll->show();
2428     connect(pianoroll, SIGNAL(isDeleting(MusEGui::TopWin*)), SLOT(toplevelDeleting(MusEGui::TopWin*)));
2429     connect(MusEGlobal::muse, SIGNAL(configChanged()), pianoroll, SLOT(configChanged()));
2430     updateWindowMenu();
2431 }
2432 
2433 
filterInvalidParts(const TopWin::ToplevelType type,MusECore::PartList * pl)2434 bool MusE::filterInvalidParts(const TopWin::ToplevelType type, MusECore::PartList* pl) {
2435 
2436     for (auto it = pl->begin(); it != pl->end(); ) {
2437         if ((it->second->track()->type() == MusECore::Track::MIDI && type == TopWin::PIANO_ROLL)
2438                 || (it->second->track()->type() == MusECore::Track::DRUM && type == TopWin::DRUM))
2439             it++;
2440         else
2441             it = pl->erase(it);
2442     }
2443 
2444     if (pl->empty()) {
2445           QMessageBox::critical(this, QString("MusE"), tr("No valid parts selected"));
2446           return false;
2447     }
2448 
2449     return true;
2450 }
2451 
findOpenEditor(const TopWin::ToplevelType type,MusECore::PartList * pl)2452 bool MusE::findOpenEditor(const TopWin::ToplevelType type, MusECore::PartList* pl) {
2453 
2454     if (QGuiApplication::keyboardModifiers() & Qt::ControlModifier
2455             && QGuiApplication::keyboardModifiers() & Qt::AltModifier)
2456         return false;
2457 
2458     for (const auto& it : toplevels) {
2459         if (it->type() != type)
2460             continue;
2461 
2462         MusEGui::MidiEditor* med = dynamic_cast<MusEGui::MidiEditor*>(it);
2463         if (med == nullptr)
2464             return false;
2465 
2466         const MusECore::PartList* pl_tmp = med->parts();
2467 
2468         if (pl_tmp->size() != pl->size())
2469             continue;
2470 
2471         bool found = false;
2472         for (const auto& it_pl : *pl) {
2473             for (const auto& it_pl_tmp : *pl_tmp) {
2474                 if (it_pl.second->sn() == it_pl_tmp.second->sn()) {
2475                     found = true;
2476                     break;
2477                 }
2478             }
2479             if (!found)
2480                 break;
2481         }
2482 
2483         if (!found)
2484             continue;
2485 
2486         med->setHScrollOffset(_arranger->cursorValue());
2487 
2488         if (it->isMdiWin())
2489             mdiArea->setActiveSubWindow(it->getMdiWin());
2490         else
2491             it->activateWindow();
2492 
2493         return true;
2494     }
2495 
2496     return false;
2497 }
2498 
2499 //---------------------------------------------------------
2500 //   startListenEditor
2501 //---------------------------------------------------------
2502 
startListEditor(bool newwin)2503 void MusE::startListEditor(bool newwin)
2504       {
2505       MusECore::PartList* pl = getMidiPartsToEdit();
2506       if (pl == nullptr)
2507             return;
2508       startListEditor(pl, newwin);
2509       }
2510 
startListEditor(MusECore::PartList * pl,bool newwin)2511 void MusE::startListEditor(MusECore::PartList* pl, bool newwin)
2512 {
2513     pl->erase(++pl->begin(), pl->end());
2514 
2515     if (!newwin && findOpenListEditor(pl))
2516         return;
2517 
2518     QDockWidget* dock = new QDockWidget("List Editor", this);
2519 //    dock->setAllowedAreas(Qt::BottomDockWidgetArea | Qt::RightDockWidgetArea);
2520     MusEGui::ListEdit* listEditor = new MusEGui::ListEdit(pl, this);
2521     dock->setWidget(listEditor);
2522 
2523     {
2524         int bar1, bar2, xx;
2525         unsigned x;
2526         const auto p = pl->begin()->second;
2527         MusEGlobal::sigmap.tickValues(p->tick(), &bar1, &xx, &x);
2528         MusEGlobal::sigmap.tickValues(p->tick() + p->lenTick(), &bar2, &xx, &x);
2529 
2530         dock->setWindowTitle("Event List <" + p->name() + QString("> %1-%2").arg(bar1+1).arg(bar2+1));
2531     }
2532 
2533     dock->setObjectName(dock->windowTitle());
2534 
2535     addDockWidget(Qt::BottomDockWidgetArea, dock);
2536 //    addTabbedDock(Qt::BottomDockWidgetArea, dock);
2537 
2538     dock->setAttribute(Qt::WA_DeleteOnClose);
2539 
2540     connect(MusEGlobal::muse,SIGNAL(configChanged()), listEditor, SLOT(configChanged()));
2541 }
2542 
findOpenListEditor(MusECore::PartList * pl)2543 bool MusE::findOpenListEditor(MusECore::PartList* pl) {
2544 
2545     if (QGuiApplication::keyboardModifiers() & Qt::ControlModifier
2546             && QGuiApplication::keyboardModifiers() & Qt::AltModifier)
2547         return false;
2548 
2549     for (const auto& d : findChildren<QDockWidget*>()) {
2550         if (strcmp(d->widget()->metaObject()->className(), "MusEGui::ListEdit") != 0)
2551             continue;
2552 
2553         const MusECore::PartList* pl_tmp = static_cast<MusEGui::ListEdit*>(d->widget())->parts();
2554 
2555         if (pl->begin()->second->sn() != pl_tmp->begin()->second->sn())
2556             continue;
2557 
2558         if (!d->isVisible())
2559             toggleDocksAction->setChecked(true);
2560         d->raise();
2561 
2562         return true;
2563     }
2564 
2565     return false;
2566 }
2567 
2568 //---------------------------------------------------------
2569 //   startMasterEditor
2570 //---------------------------------------------------------
2571 
startMasterEditor()2572 void MusE::startMasterEditor()
2573 {
2574     if (!masterEditor) {
2575         masterEditor = new MusEGui::MasterEdit(this);
2576         toplevels.push_back(masterEditor);
2577         masterEditor->show();
2578         connect(masterEditor, SIGNAL(isDeleting(MusEGui::TopWin*)), SLOT(toplevelDeleting(MusEGui::TopWin*)));
2579         updateWindowMenu();
2580     } else {
2581         if (masterEditor->isMdiWin())
2582             mdiArea->setActiveSubWindow(masterEditor->getMdiWin());
2583         else
2584             masterEditor->activateWindow();
2585     }
2586 }
2587 
2588 //---------------------------------------------------------
2589 //   startLMasterEditor
2590 //---------------------------------------------------------
2591 
showMasterList(bool show)2592 void MusE::showMasterList(bool show)
2593 {
2594     masterListDock->setVisible(show);
2595 }
2596 
2597 //---------------------------------------------------------
2598 //   startDrumEditor
2599 //---------------------------------------------------------
2600 
startDrumEditor(bool newwin)2601 void MusE::startDrumEditor(bool newwin)
2602 {
2603     MusECore::PartList* pl = getMidiPartsToEdit();
2604     if (pl == nullptr)
2605         return;
2606 
2607     if (!filterInvalidParts(TopWin::DRUM, pl))
2608         return;
2609 
2610     startDrumEditor(pl, true, newwin);
2611 }
2612 
startDrumEditor(MusECore::PartList * pl,bool showDefaultCtrls,bool newwin)2613 void MusE::startDrumEditor(MusECore::PartList* pl, bool showDefaultCtrls, bool newwin)
2614 {
2615     if (!filterInvalidParts(TopWin::DRUM, pl))
2616         return;
2617 
2618     if (!newwin &&findOpenEditor(TopWin::DRUM, pl))
2619         return;
2620 
2621     MusEGui::DrumEdit* drumEditor = new MusEGui::DrumEdit(pl, this, nullptr, _arranger->cursorValue(), showDefaultCtrls);
2622     toplevels.push_back(drumEditor);
2623     drumEditor->show();
2624     connect(drumEditor, SIGNAL(isDeleting(MusEGui::TopWin*)), SLOT(toplevelDeleting(MusEGui::TopWin*)));
2625     connect(MusEGlobal::muse, SIGNAL(configChanged()), drumEditor, SLOT(configChanged()));
2626     updateWindowMenu();
2627 }
2628 
2629 //---------------------------------------------------------
2630 //   startWaveEditor
2631 //---------------------------------------------------------
2632 
startWaveEditor(bool newwin)2633 void MusE::startWaveEditor(bool newwin)
2634 {
2635     MusECore::PartList* pl = MusECore::getSelectedWaveParts();
2636     if (pl->empty()) {
2637         QMessageBox::critical(this, QString("MusE"), tr("Nothing to edit"));
2638         return;
2639     }
2640     startWaveEditor(pl, newwin);
2641 }
2642 
startWaveEditor(MusECore::PartList * pl,bool newwin)2643 void MusE::startWaveEditor(MusECore::PartList* pl, bool newwin)
2644 {
2645     if (! newwin && findOpenEditor(TopWin::WAVE, pl))
2646         return;
2647 
2648     MusEGui::WaveEdit* waveEditor = new MusEGui::WaveEdit(pl, this);
2649     toplevels.push_back(waveEditor);
2650     waveEditor->show();
2651     connect(MusEGlobal::muse, SIGNAL(configChanged()), waveEditor, SLOT(configChanged()));
2652     connect(waveEditor, SIGNAL(isDeleting(MusEGui::TopWin*)), SLOT(toplevelDeleting(MusEGui::TopWin*)));
2653     updateWindowMenu();
2654 }
2655 
2656 
2657 //---------------------------------------------------------
2658 //   startSongInfo
2659 //---------------------------------------------------------
startSongInfo(bool editable)2660 void MusE::startSongInfo(bool editable)
2661       {
2662         MusEGui::SongInfoWidget info;
2663         info.viewCheckBox->setChecked(MusEGlobal::song->showSongInfoOnStartup());
2664         info.viewCheckBox->setEnabled(editable);
2665         info.songInfoText->setPlainText(MusEGlobal::song->getSongInfo());
2666         info.songInfoText->setReadOnly(!editable);
2667         info.setModal(true);
2668         info.show();
2669         if( info.exec() == QDialog::Accepted) {
2670           if (editable) {
2671             MusEGlobal::song->setSongInfo(info.songInfoText->toPlainText(), info.viewCheckBox->isChecked());
2672           }
2673         }
2674 
2675       }
2676 
2677 
showDidYouKnowDialogIfEnabled()2678 void MusE::showDidYouKnowDialogIfEnabled()
2679 {
2680     if ((bool)MusEGlobal::config.showDidYouKnow == true) {
2681         showDidYouKnowDialog();
2682     }
2683 }
2684 //---------------------------------------------------------
2685 //   showDidYouKnowDialog
2686 //---------------------------------------------------------
showDidYouKnowDialog()2687 void MusE::showDidYouKnowDialog()
2688 {
2689     MusEGui::DidYouKnowWidget didYouKnow;
2690 
2691     QFile file(MusEGlobal::museGlobalShare + "/didyouknow.txt");
2692     if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
2693       fprintf(stderr, "could not open didyouknow.txt!\n");
2694       return;
2695     }
2696 
2697     // All tips are separated by an empty line. Lines starting with # are ignored
2698     QString tipMessage = "";
2699     while (!file.atEnd())  {
2700       QString line = file.readLine();
2701 
2702       if (!line.simplified().isEmpty() && line.at(0) != QChar('#'))
2703         tipMessage.append(line);
2704 
2705       if (!tipMessage.isEmpty() && line.simplified().isEmpty()) {
2706         didYouKnow.tipList.append(tipMessage);
2707         tipMessage="";
2708       }
2709     }
2710     if (!tipMessage.isEmpty()) {
2711       didYouKnow.tipList.append(tipMessage);
2712     }
2713 
2714     std::random_shuffle(didYouKnow.tipList.begin(),didYouKnow.tipList.end());
2715 
2716     didYouKnow.show();
2717     if( didYouKnow.exec()) {
2718           if (didYouKnow.dontShowCheckBox->isChecked()) {
2719                 MusEGlobal::config.showDidYouKnow=false;
2720                 // Save settings. Use simple version - do NOT set style or stylesheet, this has nothing to do with that.
2721                 MusEGlobal::muse->changeConfig(true);    // save settings
2722           }
2723     }
2724 }
2725 //---------------------------------------------------------
2726 //   startDefineController
2727 //---------------------------------------------------------
2728 
2729 
2730 //---------------------------------------------------------
2731 //   startClipList
2732 //---------------------------------------------------------
2733 
showClipList(bool show)2734 void MusE::showClipList(bool show)
2735 {
2736     clipListDock->setVisible(show);
2737 }
2738 
2739 //---------------------------------------------------------
2740 //   fileMenu
2741 //---------------------------------------------------------
2742 
openRecentMenu()2743 void MusE::openRecentMenu()
2744 {
2745   openRecent->clear();
2746   for (int i = 0; i < projectRecentList.size(); ++i)
2747   {
2748     if (!QFileInfo(projectRecentList[i]).exists())
2749         continue;
2750 
2751     QString fileName = QFileInfo(projectRecentList[i]).fileName();
2752     QAction *act = openRecent->addAction(fileName);
2753     act->setData(i);
2754   }
2755 }
2756 
2757 //---------------------------------------------------------
2758 //   selectProject
2759 //---------------------------------------------------------
2760 
selectProject(QAction * act)2761 void MusE::selectProject(QAction* act)
2762       {
2763       if (!act)
2764             return;
2765       int id = act->data().toInt();
2766       if (id > projectRecentList.size()-1)
2767       {
2768         fprintf(stderr, "THIS SHOULD NEVER HAPPEN: id(%i) < recent len(%i) in MusE::selectProject!\n",
2769                 id, MusEGlobal::config.recentListLength);
2770         return;
2771       }
2772       QString name = projectRecentList[id];
2773       if (name == "")
2774             return;
2775       loadProjectFile(name, false, true);
2776       }
2777 
2778 //---------------------------------------------------------
2779 //   toplevelDeleting
2780 //---------------------------------------------------------
2781 
toplevelDeleting(MusEGui::TopWin * tl)2782 void MusE::toplevelDeleting(MusEGui::TopWin* tl)
2783 {
2784     for (MusEGui::iToplevel i = toplevels.begin(); i != toplevels.end(); ++i) {
2785         if (*i == tl) {
2786 
2787             tl->storeInitialState();
2788 
2789             if (tl == activeTopWin)
2790             {
2791                 activeTopWin=nullptr;
2792                 emit activeTopWinChanged(nullptr);
2793 
2794                 // focus the last activated topwin which is not the deleting one
2795                 QList<QMdiSubWindow*> list = mdiArea->subWindowList(QMdiArea::StackingOrder);
2796                 for (QList<QMdiSubWindow*>::const_reverse_iterator lit=list.rbegin(); lit!=list.rend(); lit++)
2797                     if ((*lit)->isVisible() && (*lit)->widget() != tl)
2798                     {
2799                         if (MusEGlobal::debugMsg)
2800                             fprintf(stderr, "bringing '%s' to front instead of closed window\n",(*lit)->widget()->windowTitle().toLatin1().data());
2801 
2802                         bringToFront((*lit)->widget());
2803 
2804                         break;
2805                     }
2806             }
2807 
2808             if (tl == currentMenuSharingTopwin)
2809                 setCurrentMenuSharingTopwin(nullptr);
2810 
2811             toplevels.erase(i);
2812 
2813             if (tl->type() == MusEGui::TopWin::SCORE)
2814                 arrangerView->updateScoreMenus();
2815 
2816             updateWindowMenu();
2817             return;
2818         }
2819     }
2820     fprintf(stderr, "topLevelDeleting: top level %p not found\n", tl);
2821 }
2822 
2823 //---------------------------------------------------------
2824 //   kbAccel - Global keyboard accelerators
2825 //---------------------------------------------------------
2826 
kbAccel(int key)2827 void MusE::kbAccel(int key)
2828       {
2829       if (key == MusEGui::shortcuts[MusEGui::SHRT_TOGGLE_METRO].key) {
2830             MusEGlobal::song->setClick(!MusEGlobal::song->click());
2831             }
2832       else if (key == MusEGui::shortcuts[MusEGui::SHRT_REC_RESTART].key) {
2833          MusEGlobal::song->restartRecording();
2834       }
2835       else if (key == MusEGui::shortcuts[MusEGui::SHRT_REC_RESTART_MULTI].key) {
2836          MusEGlobal::song->restartRecording(false);
2837       }
2838       else if (key == MusEGui::shortcuts[MusEGui::SHRT_PLAY_TOGGLE].key) {
2839 
2840             if (MusEGlobal::audio->isPlaying())
2841                   MusEGlobal::song->setStop(true);
2842             else if (!MusEGlobal::config.useOldStyleStopShortCut)
2843                   MusEGlobal::song->setPlay(true);
2844             else if (MusEGlobal::song->cpos() != MusEGlobal::song->lpos())
2845                   MusEGlobal::song->setPos(MusECore::Song::CPOS, MusEGlobal::song->lPos());
2846             else {
2847                   MusECore::Pos p(0, true);
2848                   MusEGlobal::song->setPos(MusECore::Song::CPOS, p);
2849                   }
2850             }
2851       else if (key == MusEGui::shortcuts[MusEGui::SHRT_STOP].key) {
2852             MusEGlobal::song->setStop(true);
2853             }
2854       else if (key == MusEGui::shortcuts[MusEGui::SHRT_GOTO_END].key) {
2855             MusECore::Pos p(MusEGlobal::song->len(), true);
2856             MusEGlobal::song->setPos(MusECore::Song::CPOS, p);
2857             }
2858       else if (key == MusEGui::shortcuts[MusEGui::SHRT_GOTO_START].key) {
2859             MusECore::Pos p(0, true);
2860             MusEGlobal::song->setPos(MusECore::Song::CPOS, p);
2861             }
2862       else if (key == MusEGui::shortcuts[MusEGui::SHRT_PLAY_SONG].key ) {
2863             MusEGlobal::song->setPlay(true);
2864             }
2865 
2866       // Normally each editor window handles these, to inc by the editor's raster snap value.
2867       // But users were asking for a global version - "they don't work when I'm in mixer or transport".
2868       // Since no editor claimed the key event, we don't know a specific editor's snap setting,
2869       //  so adopt a policy where the arranger is the 'main' raster reference, I guess...
2870       else if (key == MusEGui::shortcuts[MusEGui::SHRT_POS_DEC].key) {
2871             int spos = MusEGlobal::song->cpos();
2872             if(spos > 0)
2873             {
2874               spos -= 1;     // Nudge by -1, then snap down with raster1.
2875               spos = MusEGlobal::sigmap.raster1(spos, _arranger->rasterVal());
2876             }
2877             if(spos < 0)
2878               spos = 0;
2879             MusECore::Pos p(spos,true);
2880             MusEGlobal::song->setPos(MusECore::Song::CPOS, p, true, true, true);
2881             return;
2882             }
2883       else if (key == MusEGui::shortcuts[MusEGui::SHRT_POS_INC].key) {
2884             // Nudge by +1, then snap up with raster2.
2885             int spos = MusEGlobal::sigmap.raster2(MusEGlobal::song->cpos() + 1, _arranger->rasterVal());
2886             MusECore::Pos p(spos,true);
2887             MusEGlobal::song->setPos(MusECore::Song::CPOS, p, true, true, true); //CDW
2888             return;
2889             }
2890       else if (key == MusEGui::shortcuts[MusEGui::SHRT_POS_DEC_NOSNAP].key) {
2891             int spos = MusEGlobal::song->cpos() - MusEGlobal::sigmap.rasterStep(MusEGlobal::song->cpos(),
2892               _arranger->rasterVal());
2893             if(spos < 0)
2894               spos = 0;
2895             MusECore::Pos p(spos,true);
2896             MusEGlobal::song->setPos(MusECore::Song::CPOS, p, true, true, true);
2897             return;
2898             }
2899       else if (key == MusEGui::shortcuts[MusEGui::SHRT_POS_INC_NOSNAP].key) {
2900             MusECore::Pos p(MusEGlobal::song->cpos() + MusEGlobal::sigmap.rasterStep(MusEGlobal::song->cpos(),
2901               _arranger->rasterVal()), true);
2902             MusEGlobal::song->setPos(MusECore::Song::CPOS, p, true, true, true);
2903             return;
2904             }
2905       else if (key == MusEGui::shortcuts[MusEGui::SHRT_REC_ARM_TRACK].key) {
2906             if (!MusEGlobal::song->record())
2907                 toggleTrackArmSelectedTrack();
2908             }
2909 
2910       else if (key == MusEGui::shortcuts[MusEGui::SHRT_GOTO_LEFT].key) {
2911             if (!MusEGlobal::song->record())
2912                   MusEGlobal::song->setPos(MusECore::Song::CPOS, MusEGlobal::song->lPos());
2913             }
2914       else if (key == MusEGui::shortcuts[MusEGui::SHRT_GOTO_RIGHT].key) {
2915             if (!MusEGlobal::song->record())
2916                   MusEGlobal::song->setPos(MusECore::Song::CPOS, MusEGlobal::song->rPos());
2917             }
2918       else if (key == MusEGui::shortcuts[MusEGui::SHRT_TOGGLE_LOOP].key) {
2919             MusEGlobal::song->setLoop(!MusEGlobal::song->loop());
2920             }
2921       else if (key == MusEGui::shortcuts[MusEGui::SHRT_START_REC].key) {
2922             if (!MusEGlobal::audio->isPlaying()) {
2923                   MusEGlobal::song->setRecord(!MusEGlobal::song->record());
2924                   }
2925             }
2926       else if (key == MusEGui::shortcuts[MusEGui::SHRT_REC_CLEAR].key) {
2927             if (!MusEGlobal::audio->isPlaying()) {
2928                   MusEGlobal::song->clearTrackRec();
2929                   }
2930             }
2931       else if (key == MusEGui::shortcuts[MusEGui::SHRT_OPEN_TRANSPORT].key) {
2932             toggleTransport(!viewTransportAction->isChecked());
2933             }
2934       else if (key == MusEGui::shortcuts[MusEGui::SHRT_OPEN_BIGTIME].key) {
2935             toggleBigTime(!viewBigtimeAction->isChecked());
2936             }
2937       else if (key == MusEGui::shortcuts[MusEGui::SHRT_OPEN_MIXER].key) {
2938             toggleMixer1(!viewMixerAAction->isChecked());
2939             }
2940       else if (key == MusEGui::shortcuts[MusEGui::SHRT_OPEN_MIXER2].key) {
2941             toggleMixer2(!viewMixerBAction->isChecked());
2942             }
2943       else if (key == MusEGui::shortcuts[MusEGui::SHRT_NEXT_MARKER].key) {
2944             if (markerView)
2945               markerView->nextMarker();
2946             }
2947       else if (key == MusEGui::shortcuts[MusEGui::SHRT_PREV_MARKER].key) {
2948             if (markerView)
2949               markerView->prevMarker();
2950             }
2951       else if (key == MusEGui::shortcuts[MusEGui::SHRT_CONFIG_SHORTCUTS].key) {
2952             configShortCuts();
2953             }
2954       else if (key == MusEGui::shortcuts[MusEGui::SHRT_PART_NORMALIZE].key) {
2955             MusEGlobal::song->normalizeWaveParts();
2956             }
2957       else if (key == MusEGui::shortcuts[MusEGui::SHRT_TOGGLE_REWINDONSTOP].key) {
2958             rewindOnStopAction->activate(QAction::Trigger);
2959             }
2960       else {
2961             if (MusEGlobal::debugMsg)
2962                   fprintf(stderr, "unknown kbAccel 0x%x\n", key);
2963             }
2964       }
2965 
2966 #if 0
2967 //---------------------------------------------------------
2968 //   catchSignal
2969 //    only for debugging
2970 //---------------------------------------------------------
2971 
2972 // if enabling this code, also enable the line containing
2973 // "catchSignal" in main.cpp
2974 static void catchSignal(int sig)
2975       {
2976       if (MusEGlobal::debugMsg)
2977             fprintf(stderr, "MusE: signal %d caught\n", sig);
2978       if (sig == SIGSEGV) {
2979             fprintf(stderr, "MusE: segmentation fault\n");
2980             abort();
2981             }
2982       if (sig == SIGCHLD) {
2983             M_DEBUG("caught SIGCHLD - child died\n");
2984             int status;
2985             int n = waitpid (-1, &status, WNOHANG);
2986             if (n > 0) {
2987                   fprintf(stderr, "SIGCHLD for unknown process %d received\n", n);
2988                   }
2989             }
2990       }
2991 #endif
2992 
2993 //---------------------------------------------------------
2994 //   cmd
2995 //    some cmd's from pulldown menu
2996 //---------------------------------------------------------
2997 
cmd(int cmd)2998 void MusE::cmd(int cmd)
2999       {
3000       switch(cmd) {
3001             case CMD_FOLLOW_NO:
3002                   MusEGlobal::song->setFollow(MusECore::Song::NO);
3003                   break;
3004             case CMD_FOLLOW_JUMP:
3005                   MusEGlobal::song->setFollow(MusECore::Song::JUMP);
3006                   break;
3007             case CMD_FOLLOW_CONTINUOUS:
3008                   MusEGlobal::song->setFollow(MusECore::Song::CONTINUOUS);
3009                   break;
3010             }
3011       }
3012 
3013 //---------------------------------------------------------
3014 //   deleteParentlessDialogs
3015 //   All these dialogs and/or widgets have no parent,
3016 //    but are not considered MusE 'top-level', so could not
3017 //    be handled via the top-levels list...
3018 //---------------------------------------------------------
3019 
deleteParentlessDialogs()3020 void MusE::deleteParentlessDialogs()
3021 {
3022 // Appearance is already a child of MusE !!!
3023 //   if(appearance)
3024 //   {
3025 //     delete appearance;
3026 //     appearance = 0;
3027 //   }
3028   if(_snooperDialog)
3029   {
3030     delete _snooperDialog;
3031     _snooperDialog = nullptr;
3032   }
3033   if(metronomeConfig)
3034   {
3035     delete metronomeConfig;
3036     metronomeConfig = nullptr;
3037   }
3038   if(shortcutConfig)
3039   {
3040     delete shortcutConfig;
3041     shortcutConfig = nullptr;
3042   }
3043   if(midiSyncConfig)
3044   {
3045     delete midiSyncConfig;
3046     midiSyncConfig = nullptr;
3047   }
3048   if(midiFileConfig)
3049   {
3050     delete midiFileConfig;
3051     midiFileConfig = nullptr;
3052   }
3053   if(globalSettingsConfig)
3054   {
3055     delete globalSettingsConfig;
3056     globalSettingsConfig = nullptr;
3057   }
3058 
3059   destroy_function_dialogs();
3060 
3061 
3062   if(MusEGlobal::mitPluginTranspose)
3063   {
3064     delete MusEGlobal::mitPluginTranspose;
3065     MusEGlobal::mitPluginTranspose = nullptr;
3066   }
3067 
3068   if(midiInputTransform)
3069   {
3070     delete midiInputTransform;
3071     midiInputTransform = nullptr;
3072   }
3073   if(midiFilterConfig)
3074   {
3075      delete midiFilterConfig;
3076      midiFilterConfig = nullptr;
3077   }
3078   if(midiRemoteConfig)
3079   {
3080     delete midiRemoteConfig;
3081     midiRemoteConfig = nullptr;
3082   }
3083 #ifdef BUILD_EXPERIMENTAL
3084   if(midiRhythmGenerator)
3085   {
3086     delete midiRhythmGenerator;
3087     midiRhythmGenerator = 0;
3088   }
3089 #endif
3090   if(midiTransformerDialog)
3091   {
3092     delete midiTransformerDialog;
3093     midiTransformerDialog = nullptr;
3094   }
3095   if(routeDialog)
3096   {
3097     delete routeDialog;
3098     routeDialog = nullptr;
3099   }
3100 
3101 }
3102 
3103 //---------------------------------------------------------
3104 //   configAppearance
3105 //---------------------------------------------------------
3106 
configAppearance()3107 void MusE::configAppearance()
3108 {
3109     if (!appearance) {
3110         // NOTE: For deleting parentless dialogs and widgets, please add them to MusE::deleteParentlessDialogs().
3111         appearance = new MusEGui::Appearance(this);
3112         appearance->resetValues();
3113     }
3114 
3115     if(appearance->isVisible()) {
3116         appearance->raise();
3117         appearance->activateWindow();
3118     }
3119     else
3120         appearance->show();
3121 }
3122 
3123 //---------------------------------------------------------
3124 //   startSnooper
3125 //---------------------------------------------------------
3126 
startSnooper()3127 void MusE::startSnooper()
3128       {
3129       if (!_snooperDialog)
3130             // NOTE: For deleting parentless dialogs and widgets, please add them to MusE::deleteParentlessDialogs().
3131             _snooperDialog = new MusEGui::SnooperDialog();
3132       //_snooperDialog->resetValues();
3133       if(_snooperDialog->isVisible()) {
3134           _snooperDialog->raise();
3135           _snooperDialog->activateWindow();
3136           }
3137       else
3138           _snooperDialog->show();
3139       }
3140 
3141 //---------------------------------------------------------
3142 //   changeConfig
3143 //    - called whenever configuration has changed
3144 //    - when configuration has changed by user, call with
3145 //      writeFlag=true to save configuration in ~/.MusE
3146 //      simple=true Don't bother with theme, style,
3147 //       and font etc. updates, just emit the configChanged signal.
3148 //---------------------------------------------------------
3149 
changeConfig(bool writeFlag)3150 void MusE::changeConfig(bool writeFlag)
3151       {
3152       if (writeFlag)
3153             writeGlobalConfiguration();
3154       updateConfiguration();
3155       emit configChanged();
3156       }
3157 
3158 //---------------------------------------------------------
3159 //   configMetronome
3160 //---------------------------------------------------------
3161 
configMetronome()3162 void MusE::configMetronome()
3163       {
3164       if (!metronomeConfig)
3165       {
3166           // NOTE: For deleting parentless dialogs and widgets, please add them to MusE::deleteParentlessDialogs().
3167           metronomeConfig = new MusEGui::MetronomeConfig;
3168           metronomeConfig->show();
3169           return;
3170       }
3171 
3172       if(metronomeConfig->isVisible()) {
3173           metronomeConfig->raise();
3174           metronomeConfig->activateWindow();
3175           }
3176       else
3177       {
3178           metronomeConfig->updateValues();
3179           metronomeConfig->show();
3180       }
3181       }
3182 
3183 
3184 //---------------------------------------------------------
3185 //   configShortCuts
3186 //---------------------------------------------------------
3187 
configShortCuts()3188 void MusE::configShortCuts()
3189       {
3190       if (!shortcutConfig)
3191       {
3192             // NOTE: For deleting parentless dialogs and widgets, please add them to MusE::deleteParentlessDialogs().
3193             shortcutConfig = new MusEGui::ShortcutConfig();
3194             connect(shortcutConfig, SIGNAL(saveConfig()), SLOT(configShortCutsSaveConfig()));
3195       }
3196       if(shortcutConfig->isVisible()) {
3197           shortcutConfig->raise();
3198           shortcutConfig->activateWindow();
3199           }
3200       else
3201           shortcutConfig->show();
3202       }
3203 
3204 //---------------------------------------------------------
3205 //   configShortCutsSaveConfig
3206 //---------------------------------------------------------
3207 
configShortCutsSaveConfig()3208 void MusE::configShortCutsSaveConfig()
3209       {
3210       // Save settings. Use simple version - do NOT set style or stylesheet, this has nothing to do with that.
3211       changeConfig(true);
3212       }
3213 
3214 //---------------------------------------------------------
3215 //   bounceToTrack
3216 //---------------------------------------------------------
3217 
bounceToTrack(MusECore::AudioOutput * ao)3218 void MusE::bounceToTrack(MusECore::AudioOutput* ao)
3219       {
3220       if (MusEGlobal::audio->bounce())
3221         return;
3222 
3223       MusEGlobal::song->bounceOutput = nullptr;
3224       MusEGlobal::song->bounceTrack = nullptr;
3225 
3226       if(MusEGlobal::song->waves()->empty())
3227       {
3228         QMessageBox::critical(this,
3229             tr("MusE: Record Downmix to Track"),
3230             tr("No wave tracks found")
3231             );
3232         return;
3233       }
3234 
3235       MusECore::OutputList* ol = MusEGlobal::song->outputs();
3236       if(ol->empty())
3237       {
3238         QMessageBox::critical(this,
3239             tr("MusE: Record Downmix to Track"),
3240             tr("No audio output tracks found")
3241             );
3242         return;
3243       }
3244 
3245       if(checkRegionNotNull())
3246         return;
3247 
3248       MusECore::AudioOutput* out = nullptr;
3249       // If only one output, pick it, else pick the first selected.
3250       if (ao)
3251           out = ao;
3252       else if(ol->size() == 1)
3253         out = ol->front();
3254       else
3255       {
3256         for(MusECore::iAudioOutput iao = ol->begin(); iao != ol->end(); ++iao)
3257         {
3258           MusECore::AudioOutput* o = *iao;
3259           if(o->selected())
3260           {
3261             if(out)
3262             {
3263               out = nullptr;
3264               break;
3265             }
3266             out = o;
3267           }
3268         }
3269         if(!out)
3270         {
3271           QMessageBox::critical(this,
3272               tr("MusE: Record Downmix to Track"),
3273               tr("Select one audio output track,\nand one target wave track")
3274               );
3275           return;
3276         }
3277       }
3278 
3279       // search target track
3280       MusECore::TrackList* tl = MusEGlobal::song->tracks();
3281       MusECore::WaveTrack* track = nullptr;
3282 
3283       for (MusECore::iTrack it = tl->begin(); it != tl->end(); ++it) {
3284             MusECore::Track* t = *it;
3285             if (t->selected()) {
3286                     if(t->type() != MusECore::Track::WAVE && t->type() != MusECore::Track::AUDIO_OUTPUT) {
3287                         track = nullptr;
3288                         break;
3289                     }
3290                     if(t->type() == MusECore::Track::WAVE)
3291                     {
3292                       if(track)
3293                       {
3294                         track = nullptr;
3295                         break;
3296                       }
3297                       track = (MusECore::WaveTrack*)t;
3298                     }
3299 
3300                   }
3301             }
3302 
3303       if (track == nullptr) {
3304           if(ol->size() == 1) {
3305             QMessageBox::critical(this,
3306                tr("MusE: Record Downmix to Track"),
3307                tr("Select one target wave track")
3308                );
3309             return;
3310           }
3311           else
3312           {
3313             QMessageBox::critical(this,
3314                tr("MusE: Record Downmix to Track"),
3315                tr("Select one target wave track,\nand one audio output track")
3316                );
3317             return;
3318           }
3319       }
3320 
3321       // Switch all wave converters to offline settings mode.
3322       MusEGlobal::song->setAudioConvertersOfflineOperation(true);
3323 
3324       // This will wait a few cycles until freewheel is set and a seek is done.
3325       MusEGlobal::audio->msgBounce();
3326       MusEGlobal::song->bounceOutput = out;
3327       MusEGlobal::song->bounceTrack = track;
3328       MusEGlobal::song->setRecord(true);
3329       MusEGlobal::song->setRecordFlag(track, true);
3330       track->prepareRecording();
3331       MusEGlobal::song->setPlay(true);
3332       }
3333 
3334 //---------------------------------------------------------
3335 //   bounceToFile
3336 //---------------------------------------------------------
3337 
bounceToFile(MusECore::AudioOutput * ao)3338 void MusE::bounceToFile(MusECore::AudioOutput* ao)
3339 {
3340     if(MusEGlobal::audio->bounce())
3341         return;
3342     MusEGlobal::song->bounceOutput = nullptr;
3343     MusEGlobal::song->bounceTrack = nullptr;
3344     if(!ao)
3345     {
3346         MusECore::OutputList* ol = MusEGlobal::song->outputs();
3347         if(ol->empty())
3348         {
3349             QMessageBox::critical(this,
3350                                   tr("MusE: Record Downmix to File"),
3351                                   tr("No audio output tracks found")
3352                                   );
3353             return;
3354         }
3355         // If only one output, pick it, else pick the first selected.
3356         if(ol->size() == 1)
3357             ao = ol->front();
3358         else
3359         {
3360             for(MusECore::iAudioOutput iao = ol->begin(); iao != ol->end(); ++iao)
3361             {
3362                 MusECore::AudioOutput* o = *iao;
3363                 if(o->selected())
3364                 {
3365                     if(ao)
3366                     {
3367                         ao = nullptr;
3368                         break;
3369                     }
3370                     ao = o;
3371                 }
3372             }
3373             if (ao == nullptr) {
3374                 QMessageBox::critical(this,
3375                                       tr("MusE: Record Downmix to File"),
3376                                       tr("Select one audio output track")
3377                                       );
3378                 return;
3379             }
3380         }
3381     }
3382 
3383     if (checkRegionNotNull())
3384         return;
3385 
3386     MusECore::SndFile* sf = MusECore::getSndFile(nullptr, this);
3387     if (sf == nullptr)
3388         return;
3389 
3390     // Switch all wave converters to offline settings mode.
3391     MusEGlobal::song->setAudioConvertersOfflineOperation(true);
3392 
3393     // This will wait a few cycles until freewheel is set and a seek is done.
3394     MusEGlobal::audio->msgBounce();
3395     MusEGlobal::song->bounceOutput = ao;
3396     ao->setRecFile(sf);
3397     if(MusEGlobal::debugMsg)
3398         fprintf(stderr, "ao->setRecFile %p\n", sf);
3399     MusEGlobal::song->setRecord(true, false);
3400     MusEGlobal::song->setRecordFlag(ao, true);
3401     ao->prepareRecording();
3402     MusEGlobal::song->setPlay(true);
3403 }
3404 
3405 
3406 //---------------------------------------------------------
3407 //   checkRegionNotNull
3408 //    return true if (rPos - lPos) <= 0
3409 //---------------------------------------------------------
3410 
checkRegionNotNull()3411 bool MusE::checkRegionNotNull()
3412       {
3413       int start = MusEGlobal::song->lPos().frame();
3414       int end   = MusEGlobal::song->rPos().frame();
3415       if (end - start <= 0) {
3416             QMessageBox::critical(this,
3417                tr("Render Downmix"),
3418                tr("Set left and right markers for downmix range")
3419                );
3420             return true;
3421             }
3422       return false;
3423       }
3424 
3425 
3426 #ifdef HAVE_LASH
3427 //---------------------------------------------------------
3428 //   lash_idle_cb
3429 //---------------------------------------------------------
3430 
3431 void
lash_idle_cb()3432 MusE::lash_idle_cb ()
3433 {
3434   lash_event_t * event;
3435   if (!lash_client)
3436     return;
3437 
3438   while ( (event = lash_get_event (lash_client)) )
3439   {
3440       switch (lash_event_get_type (event))
3441       {
3442         case LASH_Save_File:
3443         {
3444           /* save file */
3445           QString ss = QString(lash_event_get_string(event)) + QString("/lash-project-muse.med");
3446           int ok = save (ss.toLatin1(), false, true);
3447           if (ok) {
3448             project.setFile(ss.toLatin1());
3449             _lastProjectFilePath = ss.toLatin1();
3450             _lastProjectWasTemplate = false;
3451             _lastProjectLoadedConfig = true;
3452             setWindowTitle(tr("MusE: Song: %1").arg(MusEGui::projectTitleFromFilename(project.absoluteFilePath())));
3453             addProjectToRecentList(ss.toLatin1());
3454             MusEGlobal::museProject = QFileInfo(ss.toLatin1()).absolutePath();
3455             QDir::setCurrent(MusEGlobal::museProject);
3456           }
3457           lash_send_event (lash_client, event);
3458         }
3459         break;
3460 
3461         case LASH_Restore_File:
3462         {
3463           /* load file */
3464           QString sr = QString(lash_event_get_string(event)) + QString("/lash-project-muse.med");
3465           loadProjectFile(sr.toLatin1(), false, true);
3466           lash_send_event (lash_client, event);
3467         }
3468         break;
3469 
3470         case LASH_Quit:
3471         {
3472           /* quit muse */
3473           std::cout << "MusE::lash_idle_cb Received LASH_Quit"
3474                     << std::endl;
3475           lash_event_destroy (event);
3476         }
3477         break;
3478 
3479         default:
3480         {
3481           std::cout << "MusE::lash_idle_cb Received unknown LASH event of type "
3482                     << lash_event_get_type (event)
3483                     << std::endl;
3484           lash_event_destroy (event);
3485         }
3486         break;
3487       }
3488   }
3489 }
3490 #endif /* HAVE_LASH */
3491 
3492 //---------------------------------------------------------
3493 //   clearSong
3494 //    return true if operation aborted
3495 //    called with sequencer stopped
3496 //    If clear_all is false, it will not touch things like midi ports.
3497 //---------------------------------------------------------
3498 
clearSong(bool clear_all)3499 bool MusE::clearSong(bool clear_all)
3500 {
3501     if (MusEGlobal::song->dirty) {
3502         int n = 0;
3503         n = QMessageBox::warning(this, appName,
3504                                  tr("The current project contains unsaved data.\n"
3505                                     "Load overwrites current project.\n"
3506                                     "Save current project?"),
3507                                  tr("&Save"), tr("S&kip"), tr("&Abort"), 0, 2);
3508         switch (n) {
3509         case 0:
3510             if (!save())      // abort if save failed
3511                 return true;
3512             break;
3513         case 1:
3514             break;
3515         case 2:
3516             return true;
3517         default:
3518             fprintf(stderr, "InternalError: gibt %d\n", n);
3519         }
3520     }
3521     if (MusEGlobal::audio->isPlaying()) {
3522         MusEGlobal::audio->msgPlay(false);
3523         while (MusEGlobal::audio->isPlaying())
3524             qApp->processEvents();
3525     }
3526     microSleep(100000);
3527 
3528 again:
3529     for (const auto& i : toplevels) {
3530         MusEGui::TopWin* tl = i;
3531         switch (tl->type()) {
3532         case MusEGui::TopWin::ARRANGER:
3533             break;
3534         case MusEGui::TopWin::PIANO_ROLL:
3535         case MusEGui::TopWin::SCORE:
3536             //                  case MusEGui::TopWin::LISTE:
3537         case MusEGui::TopWin::DRUM:
3538         case MusEGui::TopWin::MASTER:
3539         case MusEGui::TopWin::WAVE:
3540         {
3541             if(tl->isVisible())   // Don't keep trying to close, only if visible.
3542             {
3543                 if(!tl->close())
3544                     fprintf(stderr, "MusE::clearSong TopWin did not close!\n");
3545                 goto again;
3546             }
3547         }
3548         case MusEGui::TopWin::TOPLEVELTYPE_LAST_ENTRY: //to avoid a warning
3549             break;
3550         }
3551     }
3552 
3553     closeDocks();
3554 
3555     microSleep(100000);
3556     _arranger->songIsClearing();
3557     MusEGlobal::song->clear(true, clear_all);
3558     microSleep(100000);
3559     return false;
3560 }
3561 
3562 //---------------------------------------------------------
3563 //   startEditInstrument
3564 //---------------------------------------------------------
3565 
startEditInstrument(const QString & find_instrument,EditInstrumentTabType show_tab)3566 void MusE::startEditInstrument(const QString& find_instrument, EditInstrumentTabType show_tab)
3567     {
3568       if(editInstrument == nullptr)
3569       {
3570             editInstrument = new MusEGui::EditInstrument(this);
3571             editInstrument->show();
3572             editInstrument->findInstrument(find_instrument);
3573             editInstrument->showTab(show_tab);
3574       }
3575       else
3576       {
3577         if(! editInstrument->isHidden())
3578           editInstrument->hide();
3579         else
3580         {
3581           editInstrument->show();
3582           editInstrument->findInstrument(find_instrument);
3583           editInstrument->showTab(show_tab);
3584         }
3585       }
3586     }
3587 
3588 //---------------------------------------------------------
3589 //   switchMixerAutomation
3590 //---------------------------------------------------------
3591 
switchMixerAutomation()3592 void MusE::switchMixerAutomation()
3593       {
3594       // Could be intensive, try idling instead of a single message.
3595       MusEGlobal::audio->msgIdle(true);
3596 
3597       MusEGlobal::automation = ! MusEGlobal::automation;
3598       // Clear all pressed and touched and rec event lists.
3599       MusEGlobal::song->clearRecAutomation(true);
3600 
3601       // If going to OFF mode, need to update current 'manual' values from the automation values at this time...
3602       if(!MusEGlobal::automation)
3603       {
3604         MusECore::TrackList* tracks = MusEGlobal::song->tracks();
3605         for (MusECore::iTrack i = tracks->begin(); i != tracks->end(); ++i) {
3606               if ((*i)->isMidiTrack())
3607                     continue;
3608               MusECore::AudioTrack* track = static_cast<MusECore::AudioTrack*>(*i);
3609               if(track->automationType() != MusECore::AUTO_OFF) // && track->automationType() != MusECore::AUTO_WRITE)
3610                 track->controller()->updateCurValues(MusEGlobal::audio->curFramePos());
3611               }
3612       }
3613 
3614       MusEGlobal::audio->msgIdle(false);
3615 
3616 // REMOVE Tim. automation. Removed.
3617 // Deprecated. MusEGlobal::automation is now fixed TRUE
3618 //   for now until we decide what to do with it.
3619 //       autoMixerAction->setChecked(MusEGlobal::automation);
3620       }
3621 
3622 //---------------------------------------------------------
3623 //   clearAutomation
3624 //---------------------------------------------------------
3625 
clearAutomation()3626 void MusE::clearAutomation()
3627       {
3628       QMessageBox::StandardButton b = QMessageBox::warning(this, appName,
3629           tr("This will clear all automation data on\n all audio tracks!\nProceed?"),
3630           QMessageBox::Ok | QMessageBox::Cancel, QMessageBox::Cancel);
3631 
3632       if(b != QMessageBox::Ok)
3633         return;
3634 
3635       // Could be intensive, try idling instead of a single message.
3636       MusEGlobal::audio->msgIdle(true);
3637 
3638       MusECore::TrackList* tracks = MusEGlobal::song->tracks();
3639       for (MusECore::iTrack i = tracks->begin(); i != tracks->end(); ++i) {
3640             if ((*i)->isMidiTrack())
3641                   continue;
3642             static_cast<MusECore::AudioTrack*>(*i)->controller()->clearAllAutomation();
3643             }
3644 
3645       MusEGlobal::audio->msgIdle(false);
3646       }
3647 
3648 //---------------------------------------------------------
3649 //   takeAutomationSnapshot
3650 //---------------------------------------------------------
3651 
takeAutomationSnapshot()3652 void MusE::takeAutomationSnapshot()
3653       {
3654       QMessageBox::StandardButton b = QMessageBox::warning(this, appName,
3655           tr("This takes an automation snapshot of\n all controllers on all audio tracks,\n"
3656              " at the current position.\nProceed?"),
3657           QMessageBox::Ok | QMessageBox::Cancel, QMessageBox::Cancel);
3658 
3659       if(b != QMessageBox::Ok)
3660         return;
3661 
3662       // Could be intensive, try idling instead of a single message.
3663       MusEGlobal::audio->msgIdle(true);
3664 
3665       int frame = MusEGlobal::audio->curFramePos();
3666       MusECore::TrackList* tracks = MusEGlobal::song->tracks();
3667       for (MusECore::iTrack i = tracks->begin(); i != tracks->end(); ++i) {
3668             if ((*i)->isMidiTrack())
3669                   continue;
3670      MusECore::AudioTrack* track = static_cast<MusECore::AudioTrack*>(*i);
3671             MusECore::CtrlListList* cll = track->controller();
3672             // Need to update current 'manual' values from the automation values at this time.
3673             if(track->automationType() != MusECore::AUTO_OFF) // && track->automationType() != MusECore::AUTO_WRITE)
3674               cll->updateCurValues(frame);
3675 
3676             for (MusECore::iCtrlList icl = cll->begin(); icl != cll->end(); ++icl) {
3677                   double val = icl->second->curVal();
3678                   icl->second->add(frame, val);
3679                   }
3680             }
3681 
3682       MusEGlobal::audio->msgIdle(false);
3683       }
3684 
3685 //---------------------------------------------------------
3686 //   updateConfiguration
3687 //    called whenever the configuration has changed
3688 //---------------------------------------------------------
3689 
updateConfiguration()3690 void MusE::updateConfiguration()
3691       {
3692       fileOpenAction->setShortcut(MusEGui::shortcuts[MusEGui::SHRT_OPEN].key);
3693       fileNewAction->setShortcut(MusEGui::shortcuts[MusEGui::SHRT_NEW].key);
3694       fileNewFromTemplateAction->setShortcut(MusEGui::shortcuts[MusEGui::SHRT_NEW_FROM_TEMPLATE].key);
3695       fileSaveAction->setShortcut(MusEGui::shortcuts[MusEGui::SHRT_SAVE].key);
3696       fileSaveAsAction->setShortcut(MusEGui::shortcuts[MusEGui::SHRT_SAVE_AS].key);
3697       fileSaveAsNewProjectAction->setShortcut(MusEGui::shortcuts[MusEGui::SHRT_SAVE_AS_NEW_PROJECT].key);
3698       fileSaveRevisionAction->setShortcut(MusEGui::shortcuts[MusEGui::SHRT_SAVE_REVISION].key);
3699       fileSaveAsTemplateAction->setShortcut(MusEGui::shortcuts[MusEGui::SHRT_SAVE_AS_TEMPLATE].key);
3700       //menu_file->setShortcut(MusEGui::shortcuts[MusEGui::SHRT_OPEN_RECENT].key, menu_ids[CMD_OPEN_RECENT]);    // Not used.
3701       fileImportMidiAction->setShortcut(MusEGui::shortcuts[MusEGui::SHRT_IMPORT_MIDI].key);
3702       fileExportMidiAction->setShortcut(MusEGui::shortcuts[MusEGui::SHRT_EXPORT_MIDI].key);
3703       fileImportPartAction->setShortcut(MusEGui::shortcuts[MusEGui::SHRT_IMPORT_PART].key);
3704       fileImportWaveAction->setShortcut(MusEGui::shortcuts[MusEGui::SHRT_IMPORT_AUDIO].key);
3705       quitAction->setShortcut(MusEGui::shortcuts[MusEGui::SHRT_QUIT].key);
3706 
3707       //menu_file->setShortcut(MusEGui::shortcuts[MusEGui::SHRT_LOAD_TEMPLATE].key, menu_ids[CMD_LOAD_TEMPLATE]);  // Not used.
3708 
3709       if(MusEGlobal::undoAction)
3710         MusEGlobal::undoAction->setShortcut(MusEGui::shortcuts[MusEGui::SHRT_UNDO].key);
3711       if(MusEGlobal::redoAction)
3712         MusEGlobal::redoAction->setShortcut(MusEGui::shortcuts[MusEGui::SHRT_REDO].key);
3713 
3714 
3715       //editSongInfoAction has no acceleration
3716 
3717       viewTransportAction->setShortcut(MusEGui::shortcuts[MusEGui::SHRT_OPEN_TRANSPORT].key);
3718       viewBigtimeAction->setShortcut(MusEGui::shortcuts[MusEGui::SHRT_OPEN_BIGTIME].key);
3719       viewMixerAAction->setShortcut(MusEGui::shortcuts[MusEGui::SHRT_OPEN_MIXER].key);
3720       viewMixerBAction->setShortcut(MusEGui::shortcuts[MusEGui::SHRT_OPEN_MIXER2].key);
3721       //viewCliplistAction has no acceleration
3722       masterGraphicAction->setShortcut(shortcuts[MusEGui::SHRT_OPEN_GRAPHIC_MASTER].key);
3723       masterListAction->setShortcut(shortcuts[MusEGui::SHRT_OPEN_LIST_MASTER].key);
3724       viewMarkerAction->setShortcut(MusEGui::shortcuts[MusEGui::SHRT_OPEN_MARKER].key);
3725 
3726 
3727       // midiEditInstAction does not have acceleration
3728       midiResetInstAction->setShortcut(MusEGui::shortcuts[MusEGui::SHRT_MIDI_RESET].key);
3729       midiInitInstActions->setShortcut(MusEGui::shortcuts[MusEGui::SHRT_MIDI_INIT].key);
3730       midiLocalOffAction->setShortcut(MusEGui::shortcuts[MusEGui::SHRT_MIDI_LOCAL_OFF].key);
3731       midiTrpAction->setShortcut(MusEGui::shortcuts[MusEGui::SHRT_MIDI_INPUT_TRANSPOSE].key);
3732       midiInputTrfAction->setShortcut(MusEGui::shortcuts[MusEGui::SHRT_MIDI_INPUT_TRANSFORM].key);
3733       midiInputFilterAction->setShortcut(MusEGui::shortcuts[MusEGui::SHRT_MIDI_INPUT_FILTER].key);
3734       midiRemoteAction->setShortcut(MusEGui::shortcuts[MusEGui::SHRT_MIDI_REMOTE_CONTROL].key);
3735 #ifdef BUILD_EXPERIMENTAL
3736       midiRhythmAction->setShortcut(MusEGui::shortcuts[MusEGui::SHRT_RANDOM_RHYTHM_GENERATOR].key);
3737 #endif
3738 
3739       audioBounce2TrackAction->setShortcut(MusEGui::shortcuts[MusEGui::SHRT_AUDIO_BOUNCE_TO_TRACK].key);
3740       audioBounce2FileAction->setShortcut(MusEGui::shortcuts[MusEGui::SHRT_AUDIO_BOUNCE_TO_FILE].key);
3741       audioRestartAction->setShortcut(MusEGui::shortcuts[MusEGui::SHRT_AUDIO_RESTART].key);
3742 
3743 // REMOVE Tim. automation. Removed.
3744 // Deprecated. MusEGlobal::automation is now fixed TRUE
3745 //   for now until we decide what to do with it.
3746 //       autoMixerAction->setShortcut(MusEGui::shortcuts[MusEGui::SHRT_MIXER_AUTOMATION].key);
3747       autoSnapshotAction->setShortcut(MusEGui::shortcuts[MusEGui::SHRT_MIXER_SNAPSHOT].key);
3748       autoClearAction->setShortcut(MusEGui::shortcuts[MusEGui::SHRT_MIXER_AUTOMATION_CLEAR].key);
3749 
3750       settingsGlobalAction->setShortcut(MusEGui::shortcuts[MusEGui::SHRT_GLOBAL_CONFIG].key);
3751       //settingsShortcutsAction->setShortcut(MusEGui::shortcuts[MusEGui::SHRT_CONFIG_SHORTCUTS].key); // This is global now, handled in MusE::kbAccel
3752       settingsMetronomeAction->setShortcut(MusEGui::shortcuts[MusEGui::SHRT_CONFIG_METRONOME].key);
3753       settingsMidiSyncAction->setShortcut(MusEGui::shortcuts[MusEGui::SHRT_CONFIG_MIDISYNC].key);
3754       // settingsMidiIOAction does not have acceleration
3755       settingsAppearanceAction->setShortcut(MusEGui::shortcuts[MusEGui::SHRT_APPEARANCE_SETTINGS].key);
3756       settingsMidiPortAction->setShortcut(MusEGui::shortcuts[MusEGui::SHRT_CONFIG_MIDI_PORTS].key);
3757 
3758 
3759       dontFollowAction->setShortcut(MusEGui::shortcuts[MusEGui::SHRT_FOLLOW_NO].key);
3760       followPageAction->setShortcut(MusEGui::shortcuts[MusEGui::SHRT_FOLLOW_JUMP].key);
3761       followCtsAction->setShortcut(MusEGui::shortcuts[MusEGui::SHRT_FOLLOW_CONTINUOUS].key);
3762 
3763       helpManualAction->setShortcut(MusEGui::shortcuts[MusEGui::SHRT_OPEN_HELP].key);
3764       fullscreenAction->setShortcut(MusEGui::shortcuts[MusEGui::SHRT_FULLSCREEN].key);
3765       toggleDocksAction->setShortcut(MusEGui::shortcuts[MusEGui::SHRT_TOGGLE_DOCKS].key);
3766       //rewindOnStopAction->setShortcut(MusEGui::shortcuts[MusEGui::SHRT_TOGGLE_REWINDONSTOP].key); moved to global shortcuts in MusE::kbAccel
3767 
3768       //arrangerView->updateMusEGui::Shortcuts(); //commented out by flo: is done via signal
3769 
3770       updateStatusBar();
3771 }
3772 
3773 //---------------------------------------------------------
3774 //   showBigtime
3775 //---------------------------------------------------------
3776 
showBigtime(bool on)3777 void MusE::showBigtime(bool on)
3778 {
3779     if (on && bigtime == nullptr) {
3780         bigtime = new MusEGui::BigTime(this);
3781         bigtime->setPos(0, MusEGlobal::song->cpos(), false);
3782         connect(MusEGlobal::song, SIGNAL(posChanged(int, unsigned, bool)), bigtime, SLOT(setPos(int, unsigned, bool)));
3783         connect(MusEGlobal::muse, SIGNAL(configChanged()), bigtime, SLOT(configChanged()));
3784         connect(MusEGlobal::song, SIGNAL(songChanged(MusECore::SongChangedStruct_t)), bigtime, SLOT(songChanged(MusECore::SongChangedStruct_t)));
3785         connect(bigtime, SIGNAL(closed()), SLOT(bigtimeClosed()));
3786     }
3787     if (bigtime) {
3788         bigtime->resize(MusEGlobal::config.geometryBigTime.size());
3789         bigtime->move(MusEGlobal::config.geometryBigTime.topLeft());
3790         bigtime->setVisible(on);
3791     }
3792     viewBigtimeAction->setChecked(on);
3793 }
3794 
3795 //---------------------------------------------------------
3796 //   toggleBigTime
3797 //---------------------------------------------------------
3798 
toggleBigTime(bool checked)3799 void MusE::toggleBigTime(bool checked)
3800       {
3801       showBigtime(checked);
3802       }
3803 
3804 //---------------------------------------------------------
3805 //   bigtimeClosed
3806 //---------------------------------------------------------
3807 
bigtimeClosed()3808 void MusE::bigtimeClosed()
3809       {
3810       viewBigtimeAction->setChecked(false);
3811       }
3812 
3813 
3814 //---------------------------------------------------------
3815 //   showMixer1
3816 //---------------------------------------------------------
3817 
showMixer1(bool on)3818 void MusE::showMixer1(bool on)
3819       {
3820       if (on && mixer1 == nullptr) {
3821             mixer1 = new MusEGui::AudioMixerApp(this, &(MusEGlobal::config.mixer1));
3822             connect(mixer1, SIGNAL(closed()), SLOT(mixer1Closed()));
3823             mixer1->setGeometry(MusEGlobal::config.mixer1.geometry);
3824       }
3825       if (mixer1)
3826             mixer1->setVisible(on);
3827       viewMixerAAction->setChecked(on);
3828       }
3829 
3830 
3831 //---------------------------------------------------------
3832 //   showMixer2
3833 //---------------------------------------------------------
3834 
showMixer2(bool on)3835 void MusE::showMixer2(bool on)
3836       {
3837       if (on && mixer2 == nullptr) {
3838             mixer2 = new MusEGui::AudioMixerApp(this, &(MusEGlobal::config.mixer2));
3839             connect(mixer2, SIGNAL(closed()), SLOT(mixer2Closed()));
3840             mixer2->setGeometry(MusEGlobal::config.mixer2.geometry);
3841       }
3842       if (mixer2)
3843             mixer2->setVisible(on);
3844       viewMixerBAction->setChecked(on);
3845       }
3846 
3847 //---------------------------------------------------------
3848 //   toggleMixer1
3849 //---------------------------------------------------------
3850 
toggleMixer1(bool checked)3851 void MusE::toggleMixer1(bool checked)
3852       {
3853       showMixer1(checked);
3854       }
3855 
3856 //---------------------------------------------------------
3857 //   toggleMixer2
3858 //---------------------------------------------------------
3859 
toggleMixer2(bool checked)3860 void MusE::toggleMixer2(bool checked)
3861       {
3862       showMixer2(checked);
3863       }
3864 
3865 //---------------------------------------------------------
3866 //   mixer1Closed
3867 //---------------------------------------------------------
3868 
mixer1Closed()3869 void MusE::mixer1Closed()
3870       {
3871       viewMixerAAction->setChecked(false);
3872       }
3873 
3874 //---------------------------------------------------------
3875 //   mixer2Closed
3876 //---------------------------------------------------------
3877 
mixer2Closed()3878 void MusE::mixer2Closed()
3879       {
3880       viewMixerBAction->setChecked(false);
3881       }
3882 
3883 
mixer1Window()3884 QWidget* MusE::mixer1Window()     { return mixer1; }
mixer2Window()3885 QWidget* MusE::mixer2Window()     { return mixer2; }
3886 
transportWindow()3887 QWidget* MusE::transportWindow() { return transport; }
bigtimeWindow()3888 QWidget* MusE::bigtimeWindow()   { return bigtime; }
3889 
3890 //---------------------------------------------------------
3891 //   findUnusedWaveFiles
3892 //---------------------------------------------------------
findUnusedWaveFiles()3893 void MusE::findUnusedWaveFiles()
3894 {
3895     MusEGui::UnusedWaveFiles unused(MusEGlobal::muse);
3896     unused.exec();
3897 }
3898 
focusChanged(QWidget * old,QWidget * now)3899 void MusE::focusChanged(QWidget* old, QWidget* now)
3900 {
3901   if(MusEGlobal::heavyDebugMsg)
3902   {
3903     fprintf(stderr, "\n");
3904     fprintf(stderr, "focusChanged: old:%p now:%p activeWindow:%p\n", old, now, qApp->activeWindow());
3905     if(old)
3906       fprintf(stderr, " old type: %s\n", typeid(*old).name());
3907     if(now)
3908       fprintf(stderr, " now type: %s\n", typeid(*now).name());
3909     if (dynamic_cast<QMdiSubWindow*>(now) != nullptr)
3910     {
3911       QWidget* tmp=dynamic_cast<QMdiSubWindow*>(now)->widget();
3912       if (tmp)
3913         fprintf(stderr, "  subwin contains %p which is a %s\n", tmp, typeid(*tmp).name());
3914       else
3915         fprintf(stderr, "  subwin contains NULL\n");
3916     }
3917     if(qApp->activeWindow())
3918     {
3919        const char *strTid = typeid(qApp->activeWindow()).name();
3920        fprintf(stderr, " activeWindow type: %s\n", strTid);
3921     }
3922     fprintf(stderr, "\n");
3923   }
3924 
3925   // NOTE: FYI: This is what is required if, for 'Smart Focus', we try simply calling clearFocus from each relevant control
3926   //    upon Return/Enter/Escape or whatever.
3927   // It's nice to be able to do that, but this was crash-prone and I don't like it. Instead each relevant control connects
3928   //  signals to slots in the editors which set focus on the canvases AND activate their top windows.
3929   // Who knows, this code might be needed in some way. Informational, please keep.  Tim.
3930   //
3931   /*
3932   // Allow focus proxy to do its job (if set).
3933   if(now == this)
3934   {
3935     if(mdiArea)
3936     {
3937       QMdiSubWindow* mw = mdiArea->activeSubWindow();
3938       if(mw && mw->widget()->focusProxy())  // Did we set a focus proxy on the window?
3939         //mw->widget()->setFocus(); // Give focus to the window so proxy gets it.
3940         mw->widget()->focusProxy()->setFocus(); // Give focus directly to the proxy.
3941     }
3942   }
3943   else
3944   if(!now)
3945   {
3946     QWidget* aw = qApp->activeWindow();
3947     if(aw)
3948     {
3949       if(aw == this) // Active top-level window is MusE?
3950       {
3951         if(mdiArea)
3952         {
3953           QMdiSubWindow* mw = mdiArea->activeSubWindow();
3954           if(mw && mw->widget()->focusProxy())  // Did we set a focus proxy on the window?
3955             //mw->widget()->setFocus(); // Give focus to the window so proxy gets it.
3956             mw->widget()->focusProxy()->setFocus(); // Give focus directly to the proxy.
3957         }
3958       }
3959       else   // Active top-level window is outside the MusE mdi window.
3960       {
3961         if(aw->focusProxy())  // Did we set a focus proxy on the window?
3962         {
3963           //aw->setFocus(); // Give focus to the window so proxy gets it.
3964           aw->focusProxy()->setFocus(); // Give focus directly to the proxy.
3965           if(!aw->focusProxy()->isActiveWindow())
3966             aw->focusProxy()->activateWindow();
3967         }
3968       }
3969     }
3970   }
3971   */
3972 
3973   QWidget* ptr=now;
3974 
3975   if (activeTopWin)
3976   {
3977     if(MusEGlobal::heavyDebugMsg)
3978       fprintf(stderr, " activeTopWin: %s\n", typeid(*activeTopWin).name());
3979     activeTopWin->storeInitialState();
3980   }
3981 
3982   if (currentMenuSharingTopwin && (currentMenuSharingTopwin!=activeTopWin))
3983   {
3984     if(MusEGlobal::heavyDebugMsg)
3985       fprintf(stderr, " currentMenuSharingTopwin: %s\n", typeid(*currentMenuSharingTopwin).name());
3986     currentMenuSharingTopwin->storeInitialState();
3987   }
3988 
3989   // if the activated widget is a QMdiSubWindow containing some TopWin
3990   if ( (dynamic_cast<QMdiSubWindow*>(ptr) != nullptr) &&
3991        (dynamic_cast<MusEGui::TopWin*>( ((QMdiSubWindow*)ptr)->widget() )!=0) )
3992   {
3993     MusEGui::TopWin* tmp = (MusEGui::TopWin*) ((QMdiSubWindow*)ptr)->widget();
3994     if (tmp->initalizing())
3995     {
3996       waitingForTopwin=tmp;
3997       return;
3998     }
3999     else
4000     {
4001       ptr=tmp;
4002       // go on.
4003     }
4004   }
4005 
4006   while (ptr)
4007   {
4008     if (MusEGlobal::heavyDebugMsg)
4009       fprintf(stderr, "focusChanged: at widget %p with type %s\n",ptr, typeid(*ptr).name());
4010 
4011     if ( (dynamic_cast<MusEGui::TopWin*>(ptr) != nullptr) || // *ptr is a TopWin or a derived class
4012          (ptr==this) )                               // the main window is selected
4013       break;
4014     ptr=dynamic_cast<QWidget*>(ptr->parent()); //in the unlikely case that ptr is a QObject, this returns NULL, which stops the loop
4015   }
4016 
4017   MusEGui::TopWin* win=dynamic_cast<MusEGui::TopWin*>(ptr);
4018   // ptr is either NULL, this or the pointer to a TopWin
4019 
4020   // if the main win or some deleting topwin is selected,
4021   // don't treat that as "none", but also don't handle it
4022   if (ptr!=this && (!win || !win->deleting()) )
4023   {
4024     // now 'win' is either NULL or the pointer to the active TopWin
4025     if (win!=activeTopWin)
4026     {
4027       activeTopWin=win;
4028       emit activeTopWinChanged(activeTopWin);
4029     }
4030   }
4031 }
4032 
4033 
activeTopWinChangedSlot(MusEGui::TopWin * win)4034 void MusE::activeTopWinChangedSlot(MusEGui::TopWin* win)
4035 {
4036   if (MusEGlobal::debugMsg) fprintf(stderr, "ACTIVE TOPWIN CHANGED to '%s' (%p)\n", win ? win->windowTitle().toLatin1().data() : "<None>", win);
4037 
4038 //  if ( (win && (win->isMdiWin()==false) && win->sharesToolsAndMenu()) &&
4039 //       ( (mdiArea->currentSubWindow() != nullptr) && (mdiArea->currentSubWindow()->isVisible()==true) ) )
4040 //  {
4041 //    if (MusEGlobal::debugMsg) fprintf(stderr, "  that's a menu sharing muse window which isn't inside the MDI area.\n");
4042 //    // if a window gets active which a) is a muse window, b) is not a mdi subwin and c) shares menu- and toolbar,
4043 //    // then unfocus the MDI area and/or the currently active MDI subwin. otherwise you'll be unable to use win's
4044 //    // tools or menu entries, as whenever you click at them, they're replaced by the currently active MDI subwin's
4045 //    // menus and toolbars.
4046 //    // as unfocusing the MDI area and/or the subwin does not work for some reason, we must do this workaround:
4047 //    // simply focus anything in the main window except the mdi area.
4048 //    menuBar()->setFocus(Qt::MenuBarFocusReason);
4049 //  }
4050 
4051   if (win && (win->sharesToolsAndMenu()))
4052     setCurrentMenuSharingTopwin(win);
4053 }
4054 
4055 
4056 
setCurrentMenuSharingTopwin(MusEGui::TopWin * win)4057 void MusE::setCurrentMenuSharingTopwin(MusEGui::TopWin* win)
4058 {
4059   if (win && (win->sharesToolsAndMenu()==false))
4060   {
4061     fprintf(stderr, "WARNING: THIS SHOULD NEVER HAPPEN: MusE::setCurrentMenuSharingTopwin() called with a win which does not share (%s)! ignoring...\n", win->windowTitle().toLatin1().data());
4062     return;
4063   }
4064 
4065   if (win!=currentMenuSharingTopwin)
4066   {
4067     MusEGui::TopWin* previousMenuSharingTopwin = currentMenuSharingTopwin;
4068     currentMenuSharingTopwin = nullptr;
4069 
4070     if (MusEGlobal::debugMsg) fprintf(stderr, "MENU SHARING TOPWIN CHANGED to '%s' (%p)\n", win ? win->windowTitle().toLatin1().data() : "<None>", win);
4071 
4072 
4073     list<QToolBar*> add_toolbars;
4074     if(win)
4075       add_toolbars = win->toolbars();
4076 
4077     // empty our toolbars
4078     if (previousMenuSharingTopwin)
4079     {
4080       list<QToolBar*> add_foreign_toolbars;
4081       for (list<QToolBar*>::iterator it = foreignToolbars.begin(); it!=foreignToolbars.end(); ++it)
4082       {
4083         QToolBar* tb = *it;
4084         if(tb)
4085         {
4086           // Check for existing toolbar with same object name, and replace it.
4087           bool found = false;
4088           for(list<QToolBar*>::iterator i_atb = add_toolbars.begin(); i_atb!=add_toolbars.end(); ++i_atb)
4089           {
4090             QToolBar* atb = *i_atb;
4091             if(atb)
4092             {
4093               if(tb->objectName() == atb->objectName())
4094               {
4095                 //tb->hide();
4096 
4097                 if(MusEGlobal::heavyDebugMsg)
4098                   fprintf(stderr, "  inserting toolbar '%s'\n", atb->windowTitle().toLatin1().data());
4099 
4100                 found = true;
4101                 insertToolBar(tb, atb);
4102                 add_foreign_toolbars.push_back(atb);
4103                 add_toolbars.remove(atb);
4104                 atb->show();
4105                 break;
4106               }
4107             }
4108           }
4109 
4110           // Remove any toolbar break that may exist before the toolbar - unless there
4111           //  is a replacement is to be made, in which case leave the break intact.
4112           if(!found && toolBarBreak(tb))
4113           {
4114             if(MusEGlobal::heavyDebugMsg)
4115               fprintf(stderr, "  removing break before sharer's toolbar '%s'\n", tb->windowTitle().toLatin1().data());
4116             removeToolBarBreak(tb);
4117           }
4118 
4119 
4120           if(MusEGlobal::heavyDebugMsg)
4121             fprintf(stderr, "  removing sharer's toolbar '%s'\n", tb->windowTitle().toLatin1().data());
4122           removeToolBar(tb); // this does not delete *it, which is good
4123           tb->setParent(nullptr);
4124         }
4125       }
4126 
4127       foreignToolbars = add_foreign_toolbars;
4128 
4129     }
4130     else
4131     {
4132       for (list<QToolBar*>::iterator it = optionalToolbars.begin(); it!=optionalToolbars.end(); ++it)
4133       {
4134         QToolBar* tb = *it;
4135         if (tb)
4136         {
4137           // Check for existing toolbar with same object name, and replace it.
4138           for(list<QToolBar*>::iterator i_atb = add_toolbars.begin(); i_atb!=add_toolbars.end(); ++i_atb)
4139           {
4140             QToolBar* atb = *i_atb;
4141             if(atb)
4142             {
4143               if(tb->objectName() == atb->objectName())
4144               {
4145                 //tb->hide();
4146 
4147                 if(MusEGlobal::heavyDebugMsg)
4148                   fprintf(stderr, "  inserting toolbar '%s'\n", atb->windowTitle().toLatin1().data());
4149 
4150                 insertToolBar(tb, atb);
4151                 foreignToolbars.push_back(atb);
4152                 add_toolbars.remove(atb);
4153                 atb->show();
4154                 break;
4155               }
4156             }
4157           }
4158 
4159           if (MusEGlobal::heavyDebugMsg)
4160             fprintf(stderr, "  removing optional toolbar '%s'\n", tb->windowTitle().toLatin1().data());
4161           removeToolBar(tb); // this does not delete *it, which is good
4162           tb->setParent(nullptr);
4163         }
4164       }
4165     }
4166 
4167     //empty our menu
4168     menuBar()->clear();
4169 
4170     for (list<QMenu*>::iterator it = leadingMenus.begin(); it!=leadingMenus.end(); it++)
4171       menuBar()->addMenu(*it);
4172 
4173     if (win)
4174     {
4175       const QList<QAction*>& actions=win->menuBar()->actions();
4176       for (QList<QAction*>::const_iterator it=actions.begin(); it!=actions.end(); it++)
4177       {
4178         if (MusEGlobal::heavyDebugMsg) fprintf(stderr, "  adding menu entry '%s'\n", (*it)->text().toLatin1().data());
4179 
4180         menuBar()->addAction(*it);
4181       }
4182 
4183       for (list<QToolBar*>::const_iterator it=add_toolbars.begin(); it!=add_toolbars.end(); ++it)
4184         if (*it)
4185         {
4186           if (MusEGlobal::heavyDebugMsg) fprintf(stderr, "  adding toolbar '%s'\n", (*it)->windowTitle().toLatin1().data());
4187 
4188           addToolBar(*it);
4189           foreignToolbars.push_back(*it);
4190           (*it)->show();
4191         }
4192         else
4193         {
4194           if (MusEGlobal::heavyDebugMsg) fprintf(stderr, "  adding toolbar break\n");
4195 
4196           addToolBarBreak();
4197           foreignToolbars.push_back(nullptr);
4198         }
4199     }
4200 
4201     for (list<QMenu*>::iterator it = trailingMenus.begin(); it!=trailingMenus.end(); it++)
4202       menuBar()->addMenu(*it);
4203 
4204 
4205     currentMenuSharingTopwin=win;
4206 
4207     if (win)
4208       win->restoreMainwinState(); //restore toolbar positions in main window
4209   }
4210 }
4211 
addMdiSubWindow(QMdiSubWindow * win)4212 void MusE::addMdiSubWindow(QMdiSubWindow* win)
4213 {
4214   mdiArea->addSubWindow(win);
4215 }
4216 
setActiveMdiSubWindow(QMdiSubWindow * win)4217 void MusE::setActiveMdiSubWindow(QMdiSubWindow* win)
4218 {
4219     mdiArea->setActiveSubWindow(win);
4220 }
4221 
shareMenuAndToolbarChanged(MusEGui::TopWin * win,bool val)4222 void MusE::shareMenuAndToolbarChanged(MusEGui::TopWin* win, bool val)
4223 {
4224   if (val)
4225   {
4226     if ((win == activeTopWin) && (win != currentMenuSharingTopwin))
4227       setCurrentMenuSharingTopwin(win);
4228   }
4229   else
4230   {
4231     if (win == currentMenuSharingTopwin)
4232     {
4233       if (activeTopWin && (win != activeTopWin) && (activeTopWin->sharesToolsAndMenu()))
4234         setCurrentMenuSharingTopwin(activeTopWin);
4235       else
4236         setCurrentMenuSharingTopwin(nullptr);
4237     }
4238   }
4239 }
4240 
topwinMenuInited(MusEGui::TopWin * topwin)4241 void MusE::topwinMenuInited(MusEGui::TopWin* topwin)
4242 {
4243   if (topwin == nullptr)
4244     return;
4245 
4246   if (topwin == waitingForTopwin)
4247   {
4248     if (waitingForTopwin->deleting())
4249     {
4250       waitingForTopwin = nullptr;
4251     }
4252     else
4253     {
4254       activeTopWin=waitingForTopwin;
4255       waitingForTopwin = nullptr;
4256       emit activeTopWinChanged(activeTopWin);
4257     }
4258   }
4259   else if (topwin == currentMenuSharingTopwin)
4260   {
4261     fprintf(stderr, "====== DEBUG ======: topwin's menu got inited AFTER being shared!\n");
4262     if (!topwin->sharesToolsAndMenu()) fprintf(stderr, "======       ======: WTF, now it doesn't share any more?!?\n");
4263     setCurrentMenuSharingTopwin(nullptr);
4264     setCurrentMenuSharingTopwin(topwin);
4265   }
4266 }
4267 
updateWindowMenu()4268 void MusE::updateWindowMenu()
4269 {
4270     menuWindows->clear(); // frees memory automatically
4271 
4272     for (const auto& it : toplevels) {
4273         if (it->isMdiWin()) {
4274             QAction* temp = menuWindows->addAction(it->windowTitle());
4275             temp->setIcon(it->typeIcon(it->type()));
4276             QWidget* tlw = static_cast<QWidget*>(it);
4277             connect(temp, &QAction::triggered, [this, tlw]() { bringToFront(tlw); } );
4278 
4279             if (it->type() == TopWin::ARRANGER) { // should be always on top
4280                 temp->setShortcut(shortcuts[SHRT_ARRANGER].key);
4281                 if (toplevels.size() > 1)
4282                     menuWindows->addSeparator();
4283             }
4284         }
4285     }
4286 
4287     bool sep = false;
4288     for (const auto& it : toplevels) {
4289         if (!it->isMdiWin()) {
4290             if (!sep && toplevels.size() > 2) {
4291                 menuWindows->addSeparator();
4292                 sep = true;
4293             }
4294             QAction* temp = menuWindows->addAction(it->windowTitle());
4295             temp->setIcon(it->typeIcon(it->type()));
4296             QWidget* tlw = static_cast<QWidget*>(it);
4297             connect(temp, &QAction::triggered, [this, tlw]() { bringToFront(tlw); } );
4298         }
4299     }
4300 }
4301 
resetXrunsCounter()4302 void MusE::resetXrunsCounter()
4303 {
4304    MusEGlobal::audio->resetXruns();
4305 }
4306 
startPythonBridge()4307 bool MusE::startPythonBridge()
4308 {
4309 #ifdef PYTHON_SUPPORT
4310   printf("Starting MusE Pybridge...\n");
4311   return MusECore::startPythonBridge();
4312 #endif
4313   return false;
4314 }
4315 
stopPythonBridge()4316 bool MusE::stopPythonBridge()
4317 {
4318 #ifdef PYTHON_SUPPORT
4319   printf("Stopping MusE Pybridge...\n");
4320   return MusECore::stopPythonBridge();
4321 #endif
4322   return true;
4323 }
4324 
bringToFront(QWidget * widget)4325 void MusE::bringToFront(QWidget* widget)
4326 {
4327   MusEGui::TopWin* win=dynamic_cast<MusEGui::TopWin*>(widget);
4328   if (!win) return;
4329 
4330   if (win->isMdiWin())
4331   {
4332     win->showMaximized();
4333     mdiArea->setActiveSubWindow(win->getMdiWin());
4334   }
4335   else
4336   {
4337     win->activateWindow();
4338     win->raise();
4339   }
4340 
4341   activeTopWin=win;
4342   emit activeTopWinChanged(win);
4343 }
4344 
setFullscreen(bool val)4345 void MusE::setFullscreen(bool val)
4346 {
4347   if (val)
4348     showFullScreen();
4349   else
4350     showNormal();
4351 }
4352 
toggleRewindOnStop(bool onoff)4353 void MusE::toggleRewindOnStop(bool onoff)
4354 {
4355   MusEGlobal::config.useRewindOnStop = onoff;
4356 }
4357 
get_all_visible_subwins(QMdiArea * mdiarea)4358 list<QMdiSubWindow*> get_all_visible_subwins(QMdiArea* mdiarea)
4359 {
4360   QList<QMdiSubWindow*> wins = mdiarea->subWindowList();
4361   list<QMdiSubWindow*> result;
4362 
4363   // always put the arranger at the top of the list, if visible
4364 
4365   for (QList<QMdiSubWindow*>::iterator it=wins.begin(); it!=wins.end(); it++)
4366     if ((*it)->isVisible() && ((*it)->isMinimized()==false))
4367       if (dynamic_cast<MusEGui::TopWin*>((*it)->widget())->type()==MusEGui::TopWin::ARRANGER)
4368         result.push_back(*it);
4369 
4370   for (QList<QMdiSubWindow*>::iterator it=wins.begin(); it!=wins.end(); it++)
4371     if ((*it)->isVisible() && ((*it)->isMinimized()==false))
4372       if (dynamic_cast<MusEGui::TopWin*>((*it)->widget())->type()!=MusEGui::TopWin::ARRANGER)
4373         result.push_back(*it);
4374 
4375   return result;
4376 }
4377 
projectTitle(QString name)4378 QString MusE::projectTitle(QString name)
4379 {
4380   return tr("MusE Project: ") + MusEGui::projectTitleFromFilename(name);
4381 }
4382 
projectTitle() const4383 QString MusE::projectTitle() const
4384 {
4385   return MusEGui::projectTitleFromFilename(project.fileName());
4386 }
4387 
projectPath() const4388 QString MusE::projectPath() const
4389 {
4390   return MusEGui::projectPathFromFilename(project.absoluteFilePath());
4391 }
4392 
projectExtension() const4393 QString MusE::projectExtension() const
4394 {
4395   return MusEGui::projectExtensionFromFilename(project.fileName());
4396 }
4397 
saveTimerSlot()4398 void MusE::saveTimerSlot()
4399 {
4400     if (MusEGlobal::config.autoSave == false ||
4401         MusEGlobal::museProject == MusEGlobal::museProjectInitPath ||
4402         MusEGlobal::song->dirty == false)
4403     {
4404         //printf("conditions not met, ignore %d %d\n", MusEGlobal::config.autoSave, MusEGlobal::song->dirty);
4405         return;
4406     }
4407     saveIncrement++;
4408     if (saveIncrement > 4) {
4409         // printf("five minutes passed %d %d\n", MusEGlobal::config.autoSave, MusEGlobal::song->dirty);
4410         // time to see if we are allowed to save, if so. Do
4411         if (MusEGlobal::audio->isPlaying() == false) {
4412             fprintf(stderr, "Performing autosave\n");
4413             save(project.filePath(), false, writeTopwinState);
4414         } else
4415         {
4416             //printf("isPlaying, can't save\n");
4417         }
4418     }
4419 }
4420 
toggleTrackArmSelectedTrack()4421 void MusE::toggleTrackArmSelectedTrack()
4422 {
4423     // If there is only one track selected we toggle it's rec-arm status.
4424 
4425     int selectedTrackCount = 0;
4426     MusECore::WaveTrackList* wtl = MusEGlobal::song->waves();
4427     MusECore::TrackList selectedTracks;
4428 
4429     for (MusECore::iWaveTrack i = wtl->begin(); i != wtl->end(); ++i) {
4430           if((*i)->selected())
4431           {
4432               selectedTrackCount++;
4433               selectedTracks.push_back(*i);
4434           }
4435     }
4436     MusECore::MidiTrackList* mtl = MusEGlobal::song->midis();
4437     for (MusECore::iMidiTrack i = mtl->begin(); i != mtl->end(); ++i) {
4438           if((*i)->selected())
4439           {
4440               selectedTrackCount++;
4441               selectedTracks.push_back(*i);
4442           }
4443     }
4444     if (selectedTrackCount == 1) {
4445         // Let's toggle the selected instance.
4446         MusECore::PendingOperationList operations;
4447         foreach (MusECore::Track *t, selectedTracks)
4448         {
4449           bool newRecState = !t->recordFlag();
4450           if(!t->setRecordFlag1(newRecState))
4451             continue;
4452           operations.add(MusECore::PendingOperationItem(t, newRecState, MusECore::PendingOperationItem::SetTrackRecord));
4453         }
4454         MusEGlobal::audio->msgExecutePendingOperations(operations, true);
4455     }
4456 }
4457 
4458 //---------------------------------------------------------
4459 //   importWave
4460 //---------------------------------------------------------
4461 
importWave()4462 void MusE::importWave()
4463 {
4464    MusECore::Track* track = _arranger->curTrack();
4465    if (!track || track->type() != MusECore::Track::WAVE) {
4466 
4467       //just create new wave track and go on...
4468       if(MusEGlobal::song)
4469       {
4470          QAction act(MusEGlobal::song);
4471          act.setData(MusECore::Track::WAVE);
4472          track = MusEGlobal::song->addNewTrack(&act, nullptr);
4473       }
4474 
4475       if(!track)
4476       {
4477          QMessageBox::critical(this, QString("MusE"),
4478                  tr("Failed to import wave track"));
4479                return;
4480 
4481       }
4482 
4483    }
4484    MusECore::AudioPreviewDialog afd(this, MusEGlobal::sampleRate);
4485    afd.setDirectory(MusEGlobal::lastWavePath);
4486    afd.setWindowTitle(tr("Import Audio File"));
4487    /*QString fn = afd.getOpenFileName(MusEGlobal::lastWavePath, MusEGlobal::audio_file_pattern, this,
4488          tr("Import Audio File"), 0);
4489 */
4490    if(afd.exec() == QFileDialog::Rejected)
4491    {
4492       return;
4493    }
4494 
4495    QStringList filenames = afd.selectedFiles();
4496    if(filenames.size() < 1)
4497    {
4498       return;
4499    }
4500    QString fn = filenames [0];
4501 
4502    if (!fn.isEmpty()) {
4503       MusEGlobal::lastWavePath = fn;
4504       importWaveToTrack(fn);
4505    }
4506 }
4507 
4508 //---------------------------------------------------------
4509 //   importWaveToTrack
4510 //---------------------------------------------------------
4511 
importWaveToTrack(QString & name,unsigned tick,MusECore::Track * track)4512 bool MusE::importWaveToTrack(QString& name, unsigned tick, MusECore::Track* track)
4513 {
4514    if (!track)
4515       track = _arranger->curTrack();
4516 
4517    MusECore::SndFileR f = MusECore::sndFileGetWave(name, true);
4518 
4519    if (f.isNull()) {
4520       fprintf(stderr, "import audio file failed\n");
4521       return true;
4522    }
4523    track->setChannels(f->channels());
4524    track->resetMeter();
4525    int samples = f->samples();
4526    if (MusEGlobal::sampleRate != f->samplerate()) {
4527       QMessageBox mbox(this);
4528       mbox.setWindowTitle(tr("Import Wavefile"));
4529       mbox.setText(tr("This wave file has a samplerate of %1 Hz,\n"
4530                       " as opposed to current setting %2 Hz.\n"
4531                       "A live, real-time samplerate converter can be used on this file.\n"
4532                       "Or, a copy of the file can be resampled now from %1 Hz to %2 Hz.")
4533                       .arg(f->samplerate()).arg(MusEGlobal::sampleRate));
4534       mbox.setInformativeText(tr("Do you want to use a converter or resample the file now?"));
4535 
4536       QPushButton* converter_button = mbox.addButton(tr("Use live converter"), QMessageBox::YesRole);
4537       QPushButton* resample_button = mbox.addButton(tr("Resample now"), QMessageBox::NoRole);
4538       mbox.addButton(tr("Cancel"), QMessageBox::RejectRole);
4539       mbox.setDefaultButton(converter_button);
4540 
4541       mbox.exec();
4542       if(mbox.clickedButton() != converter_button && mbox.clickedButton() != resample_button)
4543       {
4544          return true; // this removed f from the stack, dropping refcount maybe to zero and maybe deleting the thing
4545       }
4546 
4547       if(mbox.clickedButton() == converter_button)
4548       {
4549         samples = f->samplesConverted();
4550       }
4551       else if(mbox.clickedButton() == resample_button)
4552       {
4553         //save project if necessary
4554         //copy wave to project's folder,
4555         //rename it if there is a duplicate,
4556         //resample to project's rate
4557 
4558         if(MusEGlobal::museProject == MusEGlobal::museProjectInitPath)
4559         {
4560           if(!MusEGlobal::muse->saveAs())
4561               return true;
4562         }
4563 
4564         QFileInfo fi(f.name());
4565         QString projectPath = MusEGlobal::museProject + QDir::separator();
4566         QString fExt = "wav";
4567         QString fBaseName = fi.baseName();
4568         QString fNewPath = "";
4569         bool bNameIsNotUsed = false;
4570         for(int i = 0; i < 1000; i++)
4571         {
4572           fNewPath = projectPath + fBaseName + ((i == 0) ? "" : QString::number(i)) +  "." + fExt;
4573           if(!QFile(fNewPath).exists())
4574           {
4575               bNameIsNotUsed = true;
4576               break;
4577           }
4578         }
4579 
4580         if(!bNameIsNotUsed)
4581         {
4582           QMessageBox::critical(MusEGlobal::muse, tr("Wave import error"),
4583                                 tr("There are too many wave files\n"
4584                                     "of the same base name as imported wave file\n"
4585                                     "Can not continue."));
4586           return true;
4587         }
4588 
4589         SF_INFO sfiNew;
4590         sfiNew.channels = f.channels();
4591         sfiNew.format = SF_FORMAT_WAV | SF_FORMAT_FLOAT;
4592         sfiNew.frames = 0;
4593         sfiNew.samplerate = MusEGlobal::sampleRate;
4594         sfiNew.seekable = 1;
4595         sfiNew.sections = 0;
4596 
4597         SNDFILE *sfNew = sf_open(fNewPath.toUtf8().constData(), SFM_RDWR, &sfiNew);
4598         if(sfNew == nullptr)
4599         {
4600           QMessageBox::critical(MusEGlobal::muse, tr("Wave import error"),
4601                                 tr("Can't create new wav file in project folder!\n") + sf_strerror(NULL));
4602           return true;
4603         }
4604 
4605         int srErr = 0;
4606         SRC_STATE *srState = src_new(SRC_SINC_BEST_QUALITY, sfiNew.channels, &srErr);
4607         if(!srState)
4608         {
4609           QMessageBox::critical(MusEGlobal::muse, tr("Wave import error"),
4610                                 tr("Failed to initialize sample rate converter!"));
4611           sf_close(sfNew);
4612           QFile(fNewPath).remove();
4613           return true;
4614         }
4615 
4616 
4617 
4618         float fPeekMax = 1.0f; //if output save file will peek above this walue
4619         //it should be normalized later
4620         float fNormRatio = 1.0f / fPeekMax;
4621         int nTriesMax = 5;
4622         int nCurTry = 0;
4623         do
4624         {
4625           QProgressDialog pDlg(MusEGlobal::muse);
4626           pDlg.setMinimum(0);
4627           pDlg.setMaximum(f.samples());
4628           pDlg.setCancelButtonText(tr("Cancel"));
4629           if(nCurTry == 0)
4630           {
4631               pDlg.setLabelText(tr("Resampling wave file\n"
4632                                       "\"%1\"\n"
4633                                       "from %2 to %3 Hz...")
4634                                   .arg(f.name()).arg(f.samplerate()).arg(sfiNew.samplerate));
4635           }
4636           else
4637           {
4638               pDlg.setLabelText(tr("Output has clipped\n"
4639                                   "Resampling again and normalizing wave file\n"
4640                                   "\"%1\"\n"
4641                                   "Try %2 of %3...")
4642                                 .arg(QFileInfo(fNewPath).fileName()).arg(nCurTry).arg(nTriesMax));
4643           }
4644           pDlg.setWindowModality(Qt::WindowModal);
4645           src_reset(srState);
4646           SRC_DATA sd;
4647           sd.src_ratio = ((double)MusEGlobal::sampleRate) / (double)f.samplerate();
4648           sf_count_t szBuf = 8192;
4649           float srcBuffer [szBuf];
4650           float dstBuffer [szBuf];
4651           unsigned sChannels = f.channels();
4652           sf_count_t szBufInFrames = szBuf / sChannels;
4653           sf_count_t szFInFrames = f.samples();
4654           sf_count_t nFramesRead = 0;
4655           sf_count_t nFramesWrote = 0;
4656           sd.end_of_input = 0;
4657           bool bEndOfInput = false;
4658           pDlg.setValue(0);
4659 
4660           f.seek(0, SEEK_SET);
4661 
4662           while(sd.end_of_input == 0)
4663           {
4664               size_t nFramesBuf = 0;
4665               if(bEndOfInput)
4666                 sd.end_of_input = 1;
4667               else
4668               {
4669                 nFramesBuf = f.readDirect(srcBuffer, szBufInFrames);
4670                 if(nFramesBuf == 0)
4671                     break;
4672                 nFramesRead += nFramesBuf;
4673               }
4674 
4675               sd.data_in = srcBuffer;
4676               sd.data_out = dstBuffer;
4677               sd.input_frames = nFramesBuf;
4678               sd.output_frames = szBufInFrames;
4679               sd.input_frames_used = 0;
4680               sd.output_frames_gen = 0;
4681               do
4682               {
4683                 if(src_process(srState, &sd) != 0)
4684                     break;
4685                 sd.data_in += sd.input_frames_used * sChannels;
4686                 sd.input_frames -= sd.input_frames_used;
4687 
4688                 if(sd.output_frames_gen > 0)
4689                 {
4690                     nFramesWrote += sd.output_frames_gen;
4691                     //detect maximum peek value;
4692                     for(unsigned ch = 0; ch < sChannels; ch++)
4693                     {
4694 
4695                       for(long k = 0; k < sd.output_frames_gen; k++)
4696                       {
4697                           dstBuffer [k * sChannels + ch] *= fNormRatio; //normilize if needed
4698                           float fCurPeek = dstBuffer [k * sChannels + ch];
4699                           if(fPeekMax < fCurPeek)
4700                           {
4701                             //update maximum peek value
4702                             fPeekMax = fCurPeek;
4703                           }
4704                       }
4705                     }
4706                     sf_writef_float(sfNew, dstBuffer, sd.output_frames_gen);
4707                 }
4708                 else
4709                     break;
4710 
4711               }
4712               while(true);
4713 
4714               pDlg.setValue(nFramesRead);
4715 
4716               if(nFramesRead >= szFInFrames)
4717               {
4718                 bEndOfInput = true;
4719               }
4720 
4721               if(pDlg.wasCanceled())//free all resources
4722               {
4723                 src_delete(srState);
4724                 sf_close(sfNew);
4725                 f.close();
4726                 f = nullptr;
4727                 QFile(fNewPath).remove();
4728                 return true;
4729               }
4730           }
4731 
4732           pDlg.setValue(szFInFrames);
4733 
4734           if(fPeekMax > 1.0f) //output has clipped. Normilize it
4735           {
4736               nCurTry++;
4737               sf_seek(sfNew, 0, SEEK_SET);
4738               f.seek(0, SEEK_SET);
4739               pDlg.setValue(0);
4740               fNormRatio = 1.0f / fPeekMax;
4741               fPeekMax = 1.0f;
4742           }
4743           else
4744               break;
4745         }
4746         while(nCurTry <= nTriesMax);
4747 
4748         src_delete(srState);
4749 
4750         sf_close(sfNew);
4751 
4752         f.close();
4753         f = nullptr;
4754 
4755         //reopen resampled wave again
4756         f = MusECore::sndFileGetWave(fNewPath, true);
4757         if(!f)
4758         {
4759           printf("import audio file failed\n");
4760           return true;
4761         }
4762         samples = f->samples();
4763       }
4764    }
4765 
4766    MusECore::WavePart* part = new MusECore::WavePart((MusECore::WaveTrack *)track);
4767    if (tick)
4768       part->setTick(tick);
4769    else
4770       part->setTick(MusEGlobal::song->cpos());
4771    part->setLenFrame(samples);
4772 
4773    MusECore::Event event(MusECore::Wave);
4774    MusECore::SndFileR sf(f);
4775    event.setSndFile(sf);
4776    event.setSpos(0);
4777    event.setLenFrame(samples);
4778    part->addEvent(event);
4779 
4780    part->setName(QFileInfo(f->name()).completeBaseName());
4781    MusEGlobal::song->applyOperation(MusECore::UndoOp(MusECore::UndoOp::AddPart, part));
4782    unsigned endTick = part->tick() + part->lenTick();
4783    if (MusEGlobal::song->len() < endTick)
4784       MusEGlobal::song->setLen(endTick);
4785    return false;
4786 }
4787 
4788 
arrangerRaster() const4789 int MusE::arrangerRaster() const
4790 {
4791   return _arranger->rasterVal();
4792 }
4793 
currentPartColorIndex() const4794 int MusE::currentPartColorIndex() const
4795 {
4796   if(_arranger)
4797     return _arranger->currentPartColorIndex();
4798   return 0;
4799 }
4800 
4801 
restoreState(const QByteArray & state,int version)4802 bool MusE::restoreState(const QByteArray &state, int version) {
4803 
4804     QList<QDockWidget *> docksVisible;
4805     for (const auto& d : findChildren<QDockWidget *>()) {
4806         if (d->isVisible()) {
4807             docksVisible.prepend(d);
4808             d->hide();
4809         }
4810     }
4811 
4812     bool ret = QMainWindow::restoreState(state, version);
4813 
4814     for (const auto& d : findChildren<QDockWidget *>()) {
4815         if (d->isVisible()) {
4816             if (docksVisible.contains(d))
4817                 docksVisible.removeOne(d);
4818             else
4819                 d->hide();
4820         }
4821     }
4822 
4823     for (const auto& d : docksVisible) {
4824         d->show();
4825     }
4826 
4827 
4828 //    if (!docksVisible.isEmpty()) {
4829 
4830 //        QVector<QDockWidget*> areaDocks;
4831 //        for (const auto& d : findChildren<QDockWidget*>()) {
4832 //            if (dockWidgetArea(d) == Qt::BottomDockWidgetArea
4833 //                    && strcmp(d->widget()->metaObject()->className(), "MusEGui::ListEdit") == 0
4834 //                    && !docksVisible.contains(d))
4835 //                areaDocks.prepend(d);
4836 //        }
4837 
4838 //        if (!(areaDocks.isEmpty() && docksVisible.count() == 1)) {
4839 //            auto& cur = areaDocks.isEmpty() ? docksVisible.first() : areaDocks.last();
4840 //            for (const auto& d : docksVisible) {
4841 //                if (areaDocks.isEmpty() && d == docksVisible.first())
4842 //                    continue;;
4843 //                tabifyDockWidget(cur, d);
4844 //                QTimer::singleShot(0, [d](){ d->raise(); });
4845 //                cur = d;
4846 //            }
4847 //        }
4848 //    }
4849 
4850     return ret;
4851 }
4852 
toggleDocks(bool show)4853 void MusE::toggleDocks(bool show) {
4854     if (show) {
4855         if (!hiddenDocks.isEmpty()) {
4856             for (const auto& d : hiddenDocks)
4857                 d->show();
4858             hiddenDocks.clear();
4859         }
4860     } else {
4861         hiddenDocks.clear();
4862         for (const auto& d : findChildren<QDockWidget *>()) {
4863             if (d->isVisible()) {
4864                 hiddenDocks.prepend(d);
4865                 d->hide();
4866             }
4867         }
4868     }
4869 }
4870 
closeDocks()4871 void MusE::closeDocks() {
4872 
4873     hiddenDocks.clear();
4874     toggleDocksAction->setChecked(true);
4875 
4876     for (const auto& d : findChildren<QDockWidget *>()) {
4877         if (strcmp(d->widget()->metaObject()->className(), "MusEGui::ListEdit") == 0)
4878             d->close();
4879         else if (d->isVisible()) {
4880             d->hide();
4881         }
4882     }
4883 }
4884 
addTabbedDock(Qt::DockWidgetArea area,QDockWidget * dock)4885 void MusE::addTabbedDock(Qt::DockWidgetArea area, QDockWidget *dock)
4886 {
4887     QVector<QDockWidget*> areaDocks;
4888     for (const auto& d : findChildren<QDockWidget*>()) {
4889         if (dockWidgetArea(d) == area)
4890             areaDocks.append(d);
4891     }
4892 
4893     if (areaDocks.empty()) {
4894         addDockWidget(area, dock);
4895     } else {
4896         tabifyDockWidget(areaDocks.last(), dock);
4897 //        dock->raise(); // doesn't work, Qt problem (kybos)
4898         QTimer::singleShot(0, [dock](){ dock->raise(); });
4899     }
4900 }
4901 
saveStateTopLevels()4902 void MusE::saveStateTopLevels() {
4903 
4904     for (const auto& it : toplevels) {
4905         if (activeTopWin && (activeTopWin == it))
4906             it->storeInitialState();
4907         it->storeSettings();
4908     }
4909 }
4910 
saveStateExtra()4911 void MusE::saveStateExtra() {
4912 
4913     MusEGlobal::config.transportVisible = transport->isVisible();
4914     MusEGlobal::config.geometryTransport.setTopLeft(transport->pos());
4915     // size part not used, transport window has fixed size
4916 
4917     if (bigtime) {
4918         MusEGlobal::config.bigTimeVisible = bigtime->isVisible();
4919         MusEGlobal::config.geometryBigTime.setTopLeft(bigtime->pos());
4920         MusEGlobal::config.geometryBigTime.setSize(bigtime->size());
4921     }
4922 
4923     if (mixer1) {
4924         MusEGlobal::config.mixer1Visible = mixer1->isVisible();
4925         MusEGlobal::config.mixer1.geometry = mixer1->geometry();
4926     }
4927 
4928     if (mixer2) {
4929         MusEGlobal::config.mixer2Visible = mixer2->isVisible();
4930         MusEGlobal::config.mixer2.geometry = mixer2->geometry();
4931     }
4932 }
4933 
initStatusBar()4934 void MusE::initStatusBar() {
4935 
4936     statusBar()->setSizeGripEnabled(false);
4937     statusBar()->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Minimum);
4938 
4939     cpuStatusBar = new CpuStatusBar(statusBar());
4940     connect(cpuStatusBar, SIGNAL(resetClicked()), SLOT(resetXrunsCounter()));
4941     statusBar()->addPermanentWidget(cpuStatusBar);
4942 
4943     QString s = QString("%1 | Sample rate: %2Hz | Segment size: %3 | Segment count: %4")
4944             .arg(MusEGlobal::audioDevice->driverName())
4945             .arg(MusEGlobal::sampleRate)
4946             .arg(MusEGlobal::segmentSize)
4947             .arg(MusEGlobal::segmentCount);
4948     statusBar()->addWidget(new QLabel(s));
4949 
4950     updateStatusBar();
4951 }
4952 
updateStatusBar()4953 void MusE::updateStatusBar() {
4954     statusBar()->setVisible(MusEGlobal::config.showStatusBar);
4955 }
4956 
setStatusBarText(const QString & message,int timeout)4957 void MusE::setStatusBarText(const QString &message, int timeout) {
4958     if (MusEGlobal::config.showStatusBar)
4959         statusBar()->showMessage(message, timeout);
4960 }
4961 
clearStatusBarText()4962 void MusE::clearStatusBarText() {
4963     if (MusEGlobal::config.showStatusBar)
4964         statusBar()->clearMessage();
4965 }
4966 
createPopupMenu()4967 QMenu* MusE::createPopupMenu() {
4968     QMenu* menu= QMainWindow::createPopupMenu();
4969     menu->setObjectName("CheckmarkOnly");
4970     return menu;
4971 }
4972 
4973 } //namespace MusEGui
4974