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