1 //=============================================================================
2 //  MuseScore
3 //  Music Composition & Notation
4 //
5 //  Copyright (C) 2002-2016 Werner Schweer
6 //
7 //  This program is free software; you can redistribute it and/or modify
8 //  it under the terms of the GNU General Public License version 2
9 //  as published by the Free Software Foundation and appearing in
10 //  the file LICENCE.GPL
11 //=============================================================================
12 
13 #include "musescore.h"
14 
15 #include <fenv.h>
16 #include <QStyleFactory>
17 #include <QStandardPaths>
18 #include <QDir>
19 
20 #include "config.h"
21 
22 #include "cloud/loginmanager.h"
23 #include "cloud/uploadscoredialog.h"
24 
25 #include "musescoredialogs.h"
26 #include "scoreview.h"
27 #include "libmscore/style.h"
28 #include "libmscore/score.h"
29 #include "instrdialog.h"
30 #include "preferences.h"
31 #include "prefsdialog.h"
32 #include "realizeharmonydialog.h"
33 #include "icons.h"
34 #include "libmscore/xml.h"
35 #include "seq.h"
36 #include "libmscore/tempo.h"
37 #include "libmscore/sym.h"
38 #include "pagesettings.h"
39 #include "debugger/debugger.h"
40 #include "editstyle.h"
41 #include "playpanel.h"
42 #include "libmscore/page.h"
43 #include "mixer/mixer.h"
44 #include "selectionwindow.h"
45 #include "palette.h"
46 #include "palette/palettemodel.h"
47 #include "palette/palettewidget.h"
48 #include "palette/paletteworkspace.h"
49 #include "libmscore/part.h"
50 #include "libmscore/drumset.h"
51 #include "libmscore/instrtemplate.h"
52 #include "libmscore/scoreOrder.h"
53 #include "libmscore/note.h"
54 #include "libmscore/staff.h"
55 #include "libmscore/harmony.h"
56 #include "zoombox.h"
57 #include "libmscore/sig.h"
58 #include "libmscore/undo.h"
59 #include "synthcontrol.h"
60 #include "pianoroll/pianoroll.h"
61 #include "drumroll.h"
62 #include "scoretab.h"
63 #include "timedialog.h"
64 #include "keyedit.h"
65 #include "harmonyedit.h"
66 #include "navigator.h"
67 #include "newwizard.h"
68 #include "timeline.h"
69 
70 #include "importmidi_ui/importmidi_panel.h"
71 #include "importexport/midiimport/importmidi_instrument.h"
72 #include "importexport/midiimport/importmidi_operations.h"
73 
74 #include "migration/scoremigrationdialog.h"
75 #include "scorecmp/scorecmp.h"
76 #include "script/recorderwidget.h"
77 #include "libmscore/scorediff.h"
78 #include "libmscore/chord.h"
79 #include "libmscore/segment.h"
80 #include "editraster.h"
81 #include "pianotools.h"
82 #include "mediadialog.h"
83 #include "workspace.h"
84 #include "workspacecombobox.h"
85 #include "selectdialog.h"
86 #include "selectnotedialog.h"
87 #include "transposedialog.h"
88 #include "metaedit.h"
89 #include "inspector/inspector.h"
90 #ifdef OMR
91 #include "omrpanel.h"
92 #endif
93 #include "shortcut.h"
94 #ifdef SCRIPT_INTERFACE
95 #include "plugin/pluginCreator.h"
96 #include "plugin/pluginManager.h"
97 #include "plugin/qmlpluginengine.h"
98 #endif
99 #include "helpBrowser.h"
100 #include "drumtools.h"
101 #include "editstafftype.h"
102 #include "texttools.h"
103 #include "textpalette.h"
104 #include "resourceManager.h"
105 #include "scoreaccessibility.h"
106 #include "startupWizard.h"
107 #include "tourhandler.h"
108 #include "mssplashscreen.h"
109 
110 #include "libmscore/mscore.h"
111 #include "libmscore/system.h"
112 #include "libmscore/measure.h"
113 #include "libmscore/chordlist.h"
114 #include "libmscore/volta.h"
115 #include "libmscore/lasso.h"
116 #include "libmscore/excerpt.h"
117 #include "libmscore/synthesizerstate.h"
118 #include "libmscore/utils.h"
119 #include "libmscore/icon.h"
120 
121 #include "audio/drivers/driver.h"
122 
123 #include "effects/zita1/zita.h"
124 #include "effects/compressor/compressor.h"
125 #include "effects/noeffect/noeffect.h"
126 #include "audio/midi/synthesizer.h"
127 #include "audio/midi/synthesizergui.h"
128 #include "audio/midi/msynthesizer.h"
129 #include "audio/midi/event.h"
130 #include "audio/midi/fluid/fluid.h"
131 
132 #include "plugin/qmlplugin.h"
133 #include "accessibletoolbutton.h"
134 #include "toolbuttonmenu.h"
135 #include "searchComboBox.h"
136 #include "startcenter.h"
137 #include "help.h"
138 #include "awl/aslider.h"
139 #include "extension.h"
140 #include "thirdparty/qzip/qzipreader_p.h"
141 
142 #include "sparkle/autoUpdater.h"
143 #if defined(WIN_SPARKLE_ENABLED)
144 #include "sparkle/winSparkleAutoUpdater.h"
145 #elif defined(MAC_SPARKLE_ENABLED)
146 #include "sparkle/sparkleAutoUpdater.h"
147 #endif
148 
149 #ifdef USE_LAME
150 #include "audio/exports/exportmp3.h"
151 #endif
152 #ifdef Q_OS_MAC
153 #include "macos/cocoabridge.h"
154 #endif
155 
156 #ifdef AEOLUS
157 extern Ms::Synthesizer* createAeolus();
158 #endif
159 
160 #ifdef ZERBERUS
161 extern Ms::Synthesizer* createZerberus();
162 #endif
163 
164 #ifdef QT_NO_DEBUG
165       Q_LOGGING_CATEGORY(undoRedo, "undoRedo", QtCriticalMsg);
166 #else
167       Q_LOGGING_CATEGORY(undoRedo, "undoRedo", QtCriticalMsg);
168 //      Q_LOGGING_CATEGORY(undoRedo, "undoRedo");
169 #endif
170 
171 #ifdef BUILD_CRASH_REPORTER
172 #include "thirdparty/libcrashreporter-qt/src/libcrashreporter-handler/Handler.h"
173 #endif
174 
175 #ifndef TELEMETRY_DISABLED
176 #include "actioneventobserver.h"
177 #include "widgets/telemetrypermissiondialog.h"
178 #endif
179 #include "telemetrymanager.h"
180 
181 namespace Ms {
182 
183 MuseScore* mscore;
184 MasterSynthesizer* synti;
185 
186 bool enableExperimental = false;
187 
188 QString dataPath;
189 QString iconPath;
190 
191 bool converterMode = false;
192 static bool rawDiffMode = false;
193 static bool diffMode = false;
194 static bool scriptTestMode = false;
195 bool processJob = false;
196 bool externalIcons = false;
197 bool pluginMode = false;
198 static bool startWithNewScore = false;
199 double guiScaling = 0.0;
200 static double userDPI = 0.0;
201 int trimMargin = -1;
202 bool noWebView = false;
203 bool exportScoreParts = false;
204 bool saveScoreParts = false;
205 bool ignoreWarnings = false;
206 bool cliSaveOnline = false;
207 bool exportScoreMedia = false;
208 bool exportScoreMeta = false;
209 bool exportScoreMp3 = false;
210 bool exportScorePartsPdf = false;
211 bool needUpdateSource = false;
212 static bool exportTransposedScore = false;
213 static QString transposeExportOptions;
214 static QString highlightConfigPath;
215 
216 QString mscoreGlobalShare;
217 
218 static QString outFileName;
219 static QString jsonFileName;
220 static QString audioDriver;
221 static QString pluginName;
222 static QString styleFile;
223 static QString extensionName;
224 static bool scoresOnCommandline { false };
225 
226 static QList<QTranslator*> translatorList;
227 
228 bool useFactorySettings = false;
229 bool deletePreferences = false;
230 QString styleName;
231 QString revision;
232 QErrorMessage* errorMessage;
233 const char* voiceActions[] = { "voice-1", "voice-2", "voice-3", "voice-4" };
234 
235 bool mscoreFirstStart = false;
236 
237 const std::list<const char*> MuseScore::_allNoteInputMenuEntries {
238             "note-input",
239             "pad-note-1024",
240             "pad-note-512",
241             "pad-note-256",
242             "pad-note-128",
243             "pad-note-64",
244             "pad-note-32",
245             "pad-note-16",
246             "pad-note-8",
247             "pad-note-4",
248             "pad-note-2",
249             "pad-note-1",
250             "note-breve",
251             "note-longa",
252             "pad-dot",
253             "pad-dotdot",
254             "pad-dot3",
255             "pad-dot4",
256             "tie",
257             "",
258             "pad-rest",
259             "",
260             "sharp2",
261             "sharp",
262             "nat",
263             "flat",
264             "flat2",
265             "flip",
266             "",
267             "voice-1",
268             "voice-2",
269             "voice-3",
270             "voice-4"
271             };
272 
273 const std::list<const char*> MuseScore::_allFileOperationEntries {
274             "file-new",
275             "file-open",
276             "file-save",
277             "file-save-online",
278             "print",
279             "undo",
280             "redo"
281             };
282 
283 const std::list<const char*> MuseScore::_allPlaybackControlEntries {
284 #ifdef HAS_MIDI
285             "midi-on",
286             "",
287 #endif
288             "rewind",
289             "play",
290             "loop",
291             "",
292             "repeat",
293             "pan",
294             "metronome",
295             "countin"
296             };
297 
298 extern TextPalette* textPalette;
299 
300 static const char* saveOnlineMenuItem = "file-save-online";
301 
302 #ifdef BUILD_TELEMETRY_MODULE
303 std::unique_ptr<TelemetryManager> TelemetryManager::mgr;
304 #endif
305 
306 //---------------------------------------------------------
307 // cmdInsertMeasure
308 //---------------------------------------------------------
309 
cmdInsertMeasures()310 void MuseScore::cmdInsertMeasures()
311       {
312       if (cs) {
313             if (cs->selection().isNone() && !cs->selection().findMeasure()) {
314                   QMessageBox::warning(0, "MuseScore",
315                         tr("No measure selected:\n" "Please select a measure and try again"));
316                   }
317             else {
318                   insertMeasuresDialog = new InsertMeasuresDialog;
319                   insertMeasuresDialog->show();
320                   }
321             }
322       }
323 
324 //---------------------------------------------------------
325 //   getSharePath
326 //---------------------------------------------------------
327 
getSharePath()328 QString getSharePath()
329       {
330 #ifdef Q_OS_WIN
331       QDir dir(QCoreApplication::applicationDirPath() + QString("/../" INSTALL_NAME));
332       return dir.absolutePath() + "/";
333 #else
334 #ifdef Q_OS_MAC
335       QDir dir(QCoreApplication::applicationDirPath() + QString("/../Resources"));
336       return dir.absolutePath() + "/";
337 #else
338       // Try relative path (needed for portable AppImage and non-standard installations)
339       QDir dir(QCoreApplication::applicationDirPath() + QString("/../share/" INSTALL_NAME));
340       if (dir.exists())
341             return dir.absolutePath() + "/";
342       // Otherwise fall back to default location (e.g. if binary has moved relative to share)
343       return QString( INSTPREFIX "/share/" INSTALL_NAME);
344 #endif
345 #endif
346       }
347 
348 //---------------------------------------------------------
349 //   localeName
350 //---------------------------------------------------------
351 
localeName()352 QString localeName()
353       {
354       if (useFactorySettings)
355             return "system";
356       return preferences.getString(PREF_UI_APP_LANGUAGE);
357       }
358 
359 //---------------------------------------------------------
360 //   printVersion
361 //---------------------------------------------------------
362 
printVersion(const char * prog)363 static void printVersion(const char* prog)
364       {
365       if (MuseScore::unstable())
366             fprintf(stderr, "%s: Music Score Editor\nUnstable Prerelease for Version %s; Build %s\n",
367          prog, VERSION, qPrintable(revision));
368       else
369             fprintf(stderr, "%s: Music Score Editor; Version %s; Build %s\n", prog, VERSION, qPrintable(revision));
370       }
371 
372 static const int RECENT_LIST_SIZE = 20;
373 
374 //---------------------------------------------------------
375 //   closeEvent
376 //---------------------------------------------------------
377 
closeEvent(QCloseEvent * ev)378 void MuseScore::closeEvent(QCloseEvent* ev)
379       {
380       unloadPlugins();
381       QList<MasterScore*> removeList;
382       for (MasterScore* score : scoreList) {
383             // Prompt the user to save the score if it's "dirty" (has unsaved changes) or if it's newly created but non-empty.
384             if (checkDirty(score)) {
385                   // The user has canceled out entirely, so ignore the close event.
386                   ev->ignore();
387                   return;
388                   }
389             // If the score is still flagged as newly created at this point, it means that either it's empty or the user has just
390             // chosen to discard it, so we need to remove it from the list of scores to be saved to the session file.
391             if (score->created())
392                   removeList.append(score);
393             }
394 
395       // remove all new created/not save score so they are
396       // note saved as session data
397       for (MasterScore* score : removeList) {
398             scoreList.removeAll(score);
399             scoreWasShown.remove(score);
400             }
401 
402       writeSessionFile(true);
403       for (MasterScore* score : scoreList) {
404             if (!score->tmpName().isEmpty()) {
405                   QFile f(score->tmpName());
406                   f.remove();
407                   }
408             }
409 
410       writeSettings();
411 
412       ev->accept();
413 
414       if (Shortcut::dirty)
415             Shortcut::save();
416 
417       this->deleteLater();     //this is necessary on windows http://musescore.org/node/16713
418       qApp->quit();
419       }
420 
updateExternalValuesFromPreferences()421 void updateExternalValuesFromPreferences() {
422       // set values in libmscore
423       MScore::bgColor = preferences.getColor(PREF_UI_CANVAS_BG_COLOR);
424       MScore::dropColor = preferences.getColor(PREF_UI_SCORE_NOTE_DROPCOLOR);
425       MScore::defaultColor = preferences.getColor(PREF_UI_SCORE_DEFAULTCOLOR);
426       MScore::defaultPlayDuration = preferences.getInt(PREF_SCORE_NOTE_DEFAULTPLAYDURATION);
427       MScore::panPlayback = preferences.getBool(PREF_APP_PLAYBACK_PANPLAYBACK);
428       MScore::harmonyPlayDisableCompatibility = preferences.getBool(PREF_SCORE_HARMONY_PLAY_DISABLE_COMPATIBILITY);
429       MScore::harmonyPlayDisableNew = preferences.getBool(PREF_SCORE_HARMONY_PLAY_DISABLE_NEW);
430       MScore::playRepeats = preferences.getBool(PREF_APP_PLAYBACK_PLAYREPEATS);
431       MScore::playbackSpeedIncrement = preferences.getInt(PREF_APP_PLAYBACK_SPEEDINCREMENT);
432       MScore::warnPitchRange = preferences.getBool(PREF_SCORE_NOTE_WARNPITCHRANGE);
433       MScore::pedalEventsMinTicks = preferences.getInt(PREF_IO_MIDI_PEDAL_EVENTS_MIN_TICKS);
434       MScore::layoutBreakColor = preferences.getColor(PREF_UI_SCORE_LAYOUTBREAKCOLOR);
435       MScore::frameMarginColor = preferences.getColor(PREF_UI_SCORE_FRAMEMARGINCOLOR);
436       MScore::setVerticalOrientation(preferences.getBool(PREF_UI_CANVAS_SCROLL_VERTICALORIENTATION));
437 
438       MScore::selectColor[0] = preferences.getColor(PREF_UI_SCORE_VOICE1_COLOR);
439       MScore::selectColor[1] = preferences.getColor(PREF_UI_SCORE_VOICE2_COLOR);
440       MScore::selectColor[2] = preferences.getColor(PREF_UI_SCORE_VOICE3_COLOR);
441       MScore::selectColor[3] = preferences.getColor(PREF_UI_SCORE_VOICE4_COLOR);
442 
443       MScore::setHRaster(preferences.getInt(PREF_UI_APP_RASTER_HORIZONTAL));
444       MScore::setVRaster(preferences.getInt(PREF_UI_APP_RASTER_VERTICAL));
445 
446       MScore::setNudgeStep(.1);         // cursor key (default 0.1)
447       MScore::setNudgeStep10(1.0);      // Ctrl + cursor key (default 1.0)
448       MScore::setNudgeStep50(0.01);     // Alt  + cursor key (default 0.01)
449 
450       if (!preferences.getBool(PREF_APP_STARTUP_FIRSTSTART)) {
451             //Create directories if they are missing
452             QDir dir;
453             dir.mkpath(preferences.getString(PREF_APP_PATHS_MYSCORES));
454             dir.mkpath(preferences.getString(PREF_APP_PATHS_MYSTYLES));
455             dir.mkpath(preferences.getString(PREF_APP_PATHS_MYIMAGES));
456             dir.mkpath(preferences.getString(PREF_APP_PATHS_MYTEMPLATES));
457             dir.mkpath(preferences.getString(PREF_APP_PATHS_MYEXTENSIONS));
458             dir.mkpath(preferences.getString(PREF_APP_PATHS_MYPLUGINS));
459             foreach (QString path, preferences.getString(PREF_APP_PATHS_MYSOUNDFONTS).split(";"))
460                   dir.mkpath(path);
461                   }
462             }
463 
464 //---------------------------------------------------------
465 //   preferencesChanged
466 //---------------------------------------------------------
467 
preferencesChanged(bool fromWorkspace,bool changeUI)468 void MuseScore::preferencesChanged(bool fromWorkspace, bool changeUI)
469       {
470       updateExternalValuesFromPreferences();
471 
472       getAction("repeat")->setChecked(MScore::playRepeats);
473       getAction("pan")->setChecked(MScore::panPlayback);
474       getAction("follow")->setChecked(preferences.getBool(PREF_APP_PLAYBACK_FOLLOWSONG));
475       getAction("midi-on")->setChecked(preferences.getBool(PREF_IO_MIDI_ENABLEINPUT));
476       getAction("toggle-statusbar")->setChecked(preferences.getBool(PREF_UI_APP_SHOWSTATUSBAR));
477       getAction("show-tours")->setChecked(preferences.getBool(PREF_UI_APP_STARTUP_SHOWTOURS));
478       _statusBar->setVisible(preferences.getBool(PREF_UI_APP_SHOWSTATUSBAR));
479 
480       if (!cs)
481             zoomBox->resetToDefaultLogicalZoom();
482 
483       if (playPanel)
484             playPanel->setSpeedIncrement(preferences.getInt(PREF_APP_PLAYBACK_SPEEDINCREMENT));
485 
486       if (changeUI)
487             MuseScore::updateUiStyleAndTheme(); // this is a slow operation
488       updateIcons();
489 
490       QString fgWallpaper = preferences.getString(PREF_UI_CANVAS_FG_WALLPAPER);
491       for (int i = 0; i < tab1->count(); ++i) {
492             ScoreView* canvas = tab1->view(i);
493             if (canvas == 0)
494                   continue;
495             if (preferences.getBool(PREF_UI_CANVAS_BG_USECOLOR))
496                   canvas->setBackground(preferences.getColor(PREF_UI_CANVAS_BG_COLOR));
497             else {
498                   QPixmap* pm = new QPixmap(preferences.getString(PREF_UI_CANVAS_BG_WALLPAPER));
499                   canvas->setBackground(pm);
500                   }
501             if (preferences.getBool(PREF_UI_CANVAS_FG_USECOLOR))
502                   canvas->setForeground(preferences.getColor(PREF_UI_CANVAS_FG_COLOR));
503             else {
504                   QPixmap* pm = new QPixmap(fgWallpaper);
505                   if (pm == 0 || pm->isNull())
506                         qDebug("no valid pixmap %s", fgWallpaper.toLatin1().data());
507                   canvas->setForeground(pm);
508                   }
509             }
510       if (tab2) {
511             for (int i = 0; i < tab2->count(); ++i) {
512                   ScoreView* canvas = tab2->view(i);
513                   if (canvas == 0)
514                         continue;
515                   if (preferences.getBool(PREF_UI_CANVAS_BG_USECOLOR))
516                         canvas->setBackground(preferences.getColor(PREF_UI_CANVAS_BG_COLOR));
517                   else {
518                         QPixmap* pm = new QPixmap(preferences.getString(PREF_UI_CANVAS_BG_WALLPAPER));
519                         canvas->setBackground(pm);
520                         }
521                   if (preferences.getBool(PREF_UI_CANVAS_FG_USECOLOR))
522                         canvas->setForeground(preferences.getColor(PREF_UI_CANVAS_FG_COLOR));
523                   else {
524                         QPixmap* pm = new QPixmap(fgWallpaper);
525                         if (pm == 0 || pm->isNull())
526                               qDebug("no valid pixmap %s", fgWallpaper.toLatin1().data());
527                         canvas->setForeground(pm);
528                         }
529                   }
530             }
531 
532       transportTools->setEnabled(!noSeq && seq && seq->isRunning());
533       playId->setEnabled(!noSeq && seq && seq->isRunning());
534 
535       // change workspace
536       if (!fromWorkspace && preferences.getString(PREF_APP_WORKSPACE) != WorkspacesManager::currentWorkspace()->name()) {
537             Workspace* workspace = WorkspacesManager::findByName(preferences.getString(PREF_APP_WORKSPACE));
538 
539             if (workspace)
540                   mscore->changeWorkspace(workspace);
541             }
542 
543       delete newWizard;
544       newWizard = 0;
545       reloadInstrumentTemplates();
546       updateInstrumentDialog();
547 
548       if (seq)
549             seq->preferencesChanged();
550       }
551 
552 //---------------------------------------------------------
553 //   populateNoteInputMenu
554 //---------------------------------------------------------
555 
populateNoteInputMenu()556 void MuseScore::populateNoteInputMenu()
557       {
558       entryTools->clear();
559 
560       for (const auto s : _noteInputMenuEntries) {
561             if (!*s)
562                   entryTools->addSeparator();
563             else {
564                   QAction* a = getAction(s);
565                   QWidget* w;
566                   if (strcmp(s, "note-input") == 0) {
567                         //-----------------------------------------------------------------
568                         // Note Entry Modes menu
569                         // ToolButtonMenu to swap between Note Entry Methods
570                         //-----------------------------------------------------------------
571                         QActionGroup* noteEntryMethods = new QActionGroup(entryTools);
572 
573                         noteEntryMethods->addAction(getAction("note-input-steptime"));
574                         noteEntryMethods->addAction(getAction("note-input-repitch"));
575                         noteEntryMethods->addAction(getAction("note-input-rhythm"));
576                         noteEntryMethods->addAction(getAction("note-input-realtime-auto"));
577                         noteEntryMethods->addAction(getAction("note-input-realtime-manual"));
578                         noteEntryMethods->addAction(getAction("note-input-timewise"));
579 
580                         connect(noteEntryMethods, SIGNAL(triggered(QAction*)), this, SLOT(cmd(QAction*)));
581 
582                         w = new ToolButtonMenu(tr("Note Entry Methods"),
583                            getAction("note-input"),
584                            noteEntryMethods,
585                            this,
586                            false);
587                         w->setObjectName("note-entry-methods");
588                         }
589                   else if (strncmp(s, "voice-", 6) == 0) {
590                         AccessibleToolButton* tb = new AccessibleToolButton(this, a);
591                         tb->setObjectName("voice");
592                         tb->setFocusPolicy(Qt::ClickFocus);
593                         tb->setToolButtonStyle(Qt::ToolButtonTextOnly);
594                         tb->setProperty((QString("voice%1").arg(atoi(s+6))).toUtf8().data(), true);
595                         QPalette p(tb->palette());
596                         int i = atoi(s+6) - 1;
597                         p.setColor(QPalette::Base, MScore::selectColor[i]);
598                         tb->setPalette(p);
599                         a->setCheckable(true);
600                         // tb->setDefaultAction(a);
601                         w = tb;
602                         }
603                   else {
604                         w = new AccessibleToolButton(entryTools, a);
605                         w->setObjectName(s);
606                         }
607                   entryTools->addWidget(w);
608                   }
609             }
610       }
611 
612 //---------------------------------------------------------
613 //   notifyElementDraggedToScoreView
614 //---------------------------------------------------------
615 
notifyElementDraggedToScoreView()616 void MuseScore::notifyElementDraggedToScoreView()
617       {
618       if (paletteWidget)
619             paletteWidget->notifyElementDraggedToScoreView();
620       }
621 
622 //---------------------------------------------------------
623 //   onLongOperationFinished
624 //---------------------------------------------------------
625 
onLongOperationFinished()626 void MuseScore::onLongOperationFinished()
627       {
628       infoMsgBox->accept();
629       }
630 
631 //---------------------------------------------------------
632 //   moveControlCursor
633 //---------------------------------------------------------
634 
moveControlCursor()635 void MuseScore::moveControlCursor()
636       {
637       if (!cv)
638             return;
639       cv->moveControlCursorNearCursor();
640       }
641 
642 //---------------------------------------------------------
643 //   importExtension
644 //---------------------------------------------------------
645 
importExtension(QString path)646 bool MuseScore::importExtension(QString path)
647       {
648       MQZipReader zipFile(path);
649       // compute total unzipped size
650       qint64 totalZipSize = 0;
651       for (auto fi : zipFile.fileInfoList())
652             totalZipSize += fi.size;
653 
654       // check if extension path is writable and has enough space
655       const QString extensionsPath = preferences.getString(PREF_APP_PATHS_MYEXTENSIONS);
656       const QFileInfo extensionsDirInfo(extensionsPath);
657       if (!extensionsDirInfo.isWritable()) {
658             if (!MScore::noGui)
659                   QMessageBox::critical(mscore, QWidget::tr("Import Extension File"), QWidget::tr("Cannot import extension on read-only storage: %1").arg(extensionsPath)); // TODO: on read-only *directory*
660             return false;
661             }
662       QStorageInfo storage = QStorageInfo(extensionsPath);
663       if (totalZipSize >= storage.bytesAvailable()) {
664             if (!MScore::noGui)
665                   QMessageBox::critical(mscore, QWidget::tr("Import Extension File"), QWidget::tr("Cannot import extension: storage %1 is full").arg(storage.displayName()));
666             return false;
667             }
668       // Check structure of the extension
669       bool hasMetadata = false;
670       bool hasAlienDirectory = false;
671       bool hasAlienFiles = false;
672       QSet<QString> acceptableFolders = { Extension::sfzsDir, Extension::soundfontsDir, Extension::templatesDir, Extension::instrumentsDir, Extension::workspacesDir };
673       for (auto fi : zipFile.fileInfoList()) {
674             if (fi.filePath == "metadata.json")
675                   hasMetadata = true;
676             else {
677                   // get folders
678                   auto fpath = QDir::cleanPath(fi.filePath);
679                   QStringList folders(fpath);
680                   while ((fpath = QFileInfo(fpath).path()).length() < folders.last().length())
681                         folders << fpath;
682                   if (folders.size() < 2) {
683                         hasAlienFiles = true; // in root dir
684                         break;
685                         }
686                   QString rootDir = folders.at(folders.size() - 2);
687                   if (!acceptableFolders.contains(rootDir)) {
688                         hasAlienDirectory = true; // in root dir
689                         break;
690                         }
691                   }
692             }
693       if (!hasMetadata) {
694             if (!MScore::noGui)
695                   QMessageBox::critical(mscore, QWidget::tr("Import Extension File"), QWidget::tr("Corrupted extension: no metadata.json"));
696             return false;
697             }
698       if (hasAlienDirectory) {
699             if (!MScore::noGui)
700                   QMessageBox::critical(mscore, QWidget::tr("Import Extension File"), QWidget::tr("Corrupted extension: unsupported directories in root directory"));
701             return false;
702             }
703       if (hasAlienFiles) {
704             if (!MScore::noGui)
705                   QMessageBox::critical(mscore, QWidget::tr("Import Extension File"), QWidget::tr("Corrupted extension: unsupported files in root directory"));
706             return false;
707             }
708       zipFile.close();
709 
710       MQZipReader zipFile2(path);
711       // get extension id from metadata.json
712       QByteArray mdba = zipFile2.fileData("metadata.json");
713       zipFile2.close();
714       QJsonDocument loadDoc = QJsonDocument::fromJson(mdba);
715       QJsonObject mdObject = loadDoc.object();
716       QString extensionId = mdObject["id"].toString();
717       QString version = mdObject["version"].toString();
718       if (extensionId.isEmpty() || version.isEmpty()) {
719             if (!MScore::noGui)
720                   QMessageBox::critical(mscore, QWidget::tr("Import Extension File"), QWidget::tr("Corrupted extension: corrupted metadata.json"));
721             return false;
722             }
723 
724       // Check if extension is already installed, ask for uninstall
725       QDir dir(preferences.getString(PREF_APP_PATHS_MYEXTENSIONS));
726       auto dirList = dir.entryList(QStringList(extensionId), QDir::Dirs | QDir::NoDotAndDotDot);
727       bool newerVersion = false;
728       if (dirList.contains(extensionId)) {
729             QString extDirName = QString("%1/%2").arg(preferences.getString(PREF_APP_PATHS_MYEXTENSIONS)).arg(extensionId);
730             QDir extDir(extDirName);
731             auto versionDirList = extDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
732             if (versionDirList.size() > 0) {
733                   // potentially other versions
734                   // is there a more recent version?
735                   for (auto versionDir : versionDirList) {
736                         if (compareVersion(version, versionDir)) {
737                               qDebug() << "There is a newer version. We don't install";
738                               if (!MScore::noGui)
739                                     QMessageBox::critical(mscore, QWidget::tr("Import Extension File"), QWidget::tr("A newer version is already installed"));
740                               newerVersion = true;
741                               return false;
742                               }
743                         }
744                   }
745             if (!newerVersion) {
746                   qDebug() << "found already install extension without newer version: deleting it";
747                   QDir d(QString("%1/%2").arg(preferences.getString(PREF_APP_PATHS_MYEXTENSIONS)).arg(extensionId));
748                   if (!d.removeRecursively()) {
749                         if (!MScore::noGui)
750                               QMessageBox::critical(mscore, QWidget::tr("Import Extension File"), QWidget::tr("Error while deleting previous version of the extension: %1").arg(extensionId));
751                         return false;
752                         }
753                   }
754             }
755 
756       //setup the message box
757       infoMsgBox = new QMessageBox();
758       infoMsgBox->setWindowModality(Qt::ApplicationModal);
759       infoMsgBox->setWindowFlags(Qt::WindowFlags(Qt::Dialog | Qt::FramelessWindowHint | Qt::WindowTitleHint));
760       infoMsgBox->setTextFormat(Qt::RichText);
761       infoMsgBox->setMinimumSize(300, 100);
762       infoMsgBox->setMaximumSize(300, 100);
763       infoMsgBox->setStandardButtons({});
764       infoMsgBox->setText(QString("<p align='center'>") + tr("Please wait; unpacking extension…") + QString("</p>"));
765 
766       //setup async run of long operations
767       QFutureWatcher<bool> futureWatcherUnzip;
768       connect(&futureWatcherUnzip, SIGNAL(finished()), this, SLOT(onLongOperationFinished()));
769 
770       MQZipReader* zipFile3 = new MQZipReader(path);
771       // Unzip the extension asynchronously
772       QFuture<bool> futureUnzip = QtConcurrent::run(zipFile3, &MQZipReader::extractAll, QString("%1/%2/%3").arg(preferences.getString(PREF_APP_PATHS_MYEXTENSIONS)).arg(extensionId).arg(version));
773       futureWatcherUnzip.setFuture(futureUnzip);
774       if (!MScore::noGui)
775             infoMsgBox->exec();
776       bool unzipResult = futureUnzip.result();
777       zipFile3->close();
778       delete zipFile3;
779       zipFile3 = nullptr;
780 
781       if (!unzipResult) {
782             if (!MScore::noGui)
783                   QMessageBox::critical(mscore, QWidget::tr("Import Extension File"), QWidget::tr("Unable to extract files from the extension"));
784             return false;
785             }
786 
787       delete newWizard;
788       newWizard = 0;
789       mscore->reloadInstrumentTemplates();
790       mscore->updateInstrumentDialog();
791 
792       auto loadSoundFontAsync = [&]() {
793             // After install: add sfz to zerberus
794             QDir sfzDir(QString("%1/%2/%3/%4").arg(preferences.getString(PREF_APP_PATHS_MYEXTENSIONS)).arg(extensionId).arg(version).arg(Extension::sfzsDir));
795             if (sfzDir.exists()) {
796                   // get all sfz files
797                   QDirIterator it(sfzDir.absolutePath(), QStringList("*.sfz"), QDir::Files, QDirIterator::Subdirectories);
798                   Synthesizer* s = synti->synthesizer("Zerberus");
799                   QStringList sfzs;
800                   while (it.hasNext()) {
801                         it.next();
802                         sfzs.append(it.fileName());
803                         }
804                   sfzs.sort();
805                   for (int sfzNum = 0; sfzNum < sfzs.size(); ++sfzNum)
806                         s->addSoundFont(sfzs[sfzNum]);
807 
808                   if (!sfzs.isEmpty())
809                         synti->storeState();
810 
811                   s->gui()->synthesizerChanged();
812                   }
813 
814             // After install: add soundfont to fluid
815             QDir sfDir(QString("%1/%2/%3/%4").arg(preferences.getString(PREF_APP_PATHS_MYEXTENSIONS)).arg(extensionId).arg(version).arg(Extension::soundfontsDir));
816             if (sfDir.exists()) {
817                   // get all soundfont files
818                   QStringList filters("*.sf2");
819                   filters.append("*.sf3");
820                   QDirIterator it(sfDir.absolutePath(), filters, QDir::Files, QDirIterator::Subdirectories);
821                   QStringList sfs;
822                   while (it.hasNext()) {
823                         it.next();
824                         sfs.append(it.fileInfo().absoluteFilePath());
825                         }
826                   sfs.sort();
827                   Synthesizer* s = synti->synthesizer("Fluid");
828                   for (auto sf : sfs) {
829                         s->addSoundFont(sf);
830                         }
831                   if (!sfs.isEmpty())
832                         synti->storeState();
833 
834                   s->gui()->synthesizerChanged();
835                   }
836             };
837       if (!enableExperimental) {
838             //load soundfonts async
839             QFuture<void> futureLoadSFs = QtConcurrent::run(loadSoundFontAsync);
840             QFutureWatcher<void> futureWatcherLoadSFs;
841             futureWatcherLoadSFs.setFuture(futureLoadSFs);
842             connect(&futureWatcherLoadSFs, SIGNAL(finished()), this, SLOT(onLongOperationFinished()));
843             infoMsgBox->setText(QString("<p align='center'>") + tr("Please wait; loading SoundFonts…") + QString("</p>"));
844             if (!MScore::noGui)
845                   infoMsgBox->exec();
846             else
847                   futureLoadSFs.waitForFinished();
848             }
849 
850       // after install: refresh workspaces if needed
851       QDir workspacesDir(QString("%1/%2/%3/%4").arg(preferences.getString(PREF_APP_PATHS_MYEXTENSIONS)).arg(extensionId).arg(version).arg(Extension::workspacesDir));
852       if (workspacesDir.exists() && !MScore::noGui) {
853             auto wsList = workspacesDir.entryInfoList(QStringList("*.workspace"), QDir::Files);
854             if (!wsList.isEmpty()) {
855                   WorkspacesManager::refreshWorkspaces();
856                   emit workspacesChanged();
857                   changeWorkspace(WorkspacesManager::workspaces().last());
858                   }
859             }
860       return true;
861       }
862 
863 //---------------------------------------------------------
864 //   uninstallExtension
865 //---------------------------------------------------------
866 
uninstallExtension(QString extensionId)867 bool MuseScore::uninstallExtension(QString extensionId)
868       {
869       QString version = Extension::getLatestVersion(extensionId);
870       // Before install: remove sfz from zerberus
871       QDir sfzDir(QString("%1/%2/%3/%4").arg(preferences.getString(PREF_APP_PATHS_MYEXTENSIONS)).arg(extensionId).arg(version).arg(Extension::sfzsDir));
872       if (sfzDir.exists()) {
873             // get all sfz files
874             QDirIterator it(sfzDir.absolutePath(), QStringList("*.sfz"), QDir::Files, QDirIterator::Subdirectories);
875             Synthesizer* s = synti->synthesizer("Zerberus");
876             bool found = it.hasNext();
877             while (it.hasNext()) {
878                   it.next();
879                   s->removeSoundFont(it.fileInfo().absoluteFilePath());
880                   }
881             if (found)
882                   synti->storeState();
883             s->gui()->synthesizerChanged();
884             }
885       // Before install: remove soundfont from fluid
886       QDir sfDir(QString("%1/%2/%3/%4").arg(preferences.getString(PREF_APP_PATHS_MYEXTENSIONS)).arg(extensionId).arg(version).arg(Extension::soundfontsDir));
887       if (sfDir.exists()) {
888             // get all soundfont files
889             QStringList filters("*.sf2");
890             filters.append("*.sf3");
891             QDirIterator it(sfDir.absolutePath(), filters, QDir::Files, QDirIterator::Subdirectories);
892             Synthesizer* s = synti->synthesizer("Fluid");
893             bool found = it.hasNext();
894             while (it.hasNext()) {
895                   it.next();
896                   s->removeSoundFont(it.fileName());
897                   }
898             if (found)
899                   synti->storeState();
900 
901             s->gui()->synthesizerChanged();
902             }
903       bool refreshWorkspaces = false;
904       QDir workspacesDir(QString("%1/%2/%3/%4").arg(preferences.getString(PREF_APP_PATHS_MYEXTENSIONS)).arg(extensionId).arg(version).arg(Extension::workspacesDir));
905       if (workspacesDir.exists()) {
906             auto wsList = workspacesDir.entryInfoList(QStringList("*.workspace"), QDir::Files);
907             if (!wsList.isEmpty())
908                   refreshWorkspaces = true;
909             }
910 
911       // delete directories
912       QDir extensionDir(QString("%1/%2").arg(preferences.getString(PREF_APP_PATHS_MYEXTENSIONS)).arg(extensionId));
913       extensionDir.removeRecursively();
914 
915       // update UI
916       delete newWizard;
917       newWizard = 0;
918       mscore->reloadInstrumentTemplates();
919       mscore->updateInstrumentDialog();
920       if (refreshWorkspaces) {
921             const auto curWorkspaceName = WorkspacesManager::currentWorkspace()->name();
922             WorkspacesManager::refreshWorkspaces();
923             emit workspacesChanged();
924             auto ws = WorkspacesManager::workspaces();
925             //If current worksapce is alive, do nothing
926             //Select first available workspace in the list otherwise
927             bool curWorkspaceDisappeared = false;
928             if (WorkspacesManager::findByName(curWorkspaceName))
929                   curWorkspaceDisappeared = true;
930 
931             if (curWorkspaceDisappeared)
932                   changeWorkspace(WorkspacesManager::workspaces().last());
933             }
934       return true;
935       }
936 
937 //---------------------------------------------------------
938 //   isInstalled
939 ///  used from javascript in start center webview
940 //---------------------------------------------------------
941 
isInstalledExtension(QString extensionId)942 bool MuseScore::isInstalledExtension(QString extensionId)
943       {
944       return Extension::isInstalled(extensionId);
945       }
946 
947 //---------------------------------------------------------
948 //   populateFileOperations
949 //---------------------------------------------------------
950 
populateFileOperations()951 void MuseScore::populateFileOperations()
952       {
953       // Save the current zoom and view-mode combobox states. if any.
954       const auto zoomBoxState = zoomBox
955          ? std::make_pair(zoomBox->currentIndex(), zoomBox->itemText(static_cast<int>(ZoomIndex::ZOOM_FREE)))
956          : std::make_pair(-1, QString());
957       const auto viewModeComboIndex = viewModeCombo ? viewModeCombo->currentIndex() : -1;
958 
959       fileTools->clear();
960 
961       if (qApp->layoutDirection() == Qt::LayoutDirection::LeftToRight) {
962             for (auto s : _fileOperationEntries) {
963                   if (!*s)
964                         fileTools->addSeparator();
965                   else
966                         fileTools->addWidget(new AccessibleToolButton(fileTools, getAction(s)));
967                   }
968             }
969       else {
970             _fileOperationEntries.reverse();
971             for (auto s : _fileOperationEntries) {
972                   if (!*s)
973                         fileTools->addSeparator();
974                   else
975                         fileTools->addWidget(new AccessibleToolButton(fileTools, getAction(s)));
976                   }
977             _fileOperationEntries.reverse();
978             }
979 
980       // Currently not customizable in ToolbarEditor
981       fileTools->addSeparator();
982       zoomBox = new ZoomBox;
983 
984       // Restore the saved zoom combobox index and text, if any.
985       if (zoomBoxState.first != -1) {
986             zoomBox->setCurrentIndex(zoomBoxState.first);
987             zoomBox->setItemText(static_cast<int>(ZoomIndex::ZOOM_FREE), zoomBoxState.second);
988             }
989 
990       connect(zoomBox, SIGNAL(zoomChanged(const ZoomIndex, const qreal)), SLOT(zoomBoxChanged(const ZoomIndex, const qreal)));
991       fileTools->addWidget(zoomBox);
992 
993       viewModeCombo = new QComboBox(this);
994 #if defined(Q_OS_MAC)
995       viewModeCombo->setFocusPolicy(Qt::StrongFocus);
996 #else
997       viewModeCombo->setFocusPolicy(Qt::TabFocus);
998 #endif
999       viewModeCombo->setFixedHeight(preferences.getInt(PREF_UI_THEME_ICONHEIGHT) + 8);  // hack
1000 
1001       viewModeCombo->setAccessibleName(tr("View Mode"));
1002       viewModeCombo->addItem(tr("Page View"), int(LayoutMode::PAGE));
1003       viewModeCombo->addItem(tr("Continuous View"), int(LayoutMode::LINE));
1004       viewModeCombo->addItem(tr("Single Page"), int(LayoutMode::SYSTEM));
1005 
1006       // Restore the saved view-mode combobox index, if any.
1007       if (viewModeComboIndex != -1)
1008             viewModeCombo->setCurrentIndex(viewModeComboIndex);
1009 
1010       connect(viewModeCombo, SIGNAL(activated(int)), SLOT(switchLayoutMode(int)));
1011       fileTools->addWidget(viewModeCombo);
1012       }
1013 
1014 //---------------------------------------------------------
1015 //   populatePlaybackControls
1016 //---------------------------------------------------------
1017 
populatePlaybackControls()1018 void MuseScore::populatePlaybackControls()
1019       {
1020       transportTools->clear();
1021 
1022       for (const auto s : _playbackControlEntries) {
1023             if (!*s)
1024                   transportTools->addSeparator();
1025             else {
1026                   if (QString(s) == "repeat") {
1027                               QAction* repeatAction = getAction("repeat");
1028                               repeatAction->setChecked(preferences.getBool(PREF_APP_PLAYBACK_PLAYREPEATS));
1029                               QWidget* w = new AccessibleToolButton(transportTools, repeatAction);
1030                               transportTools->addWidget(w);
1031                         }
1032                   else if (QString(s) == "play") {
1033                         _playButton = new AccessibleToolButton(transportTools, getAction("play"));
1034                         transportTools->addWidget(_playButton);
1035                         }
1036                   else {
1037                         QWidget* w = new AccessibleToolButton(transportTools, getAction(s));
1038                         transportTools->addWidget(w);
1039                         }
1040                   }
1041             }
1042       }
1043 
1044 //---------------------------------------------------------
1045 //   MuseScore
1046 //---------------------------------------------------------
1047 
MuseScore()1048 MuseScore::MuseScore()
1049    : QMainWindow()
1050       {
1051       _tourHandler = new TourHandler(this);
1052       qApp->installEventFilter(_tourHandler);
1053       _tourHandler->loadTours();
1054 
1055       setTabPosition(Qt::AllDockWidgetAreas, QTabWidget::North);
1056 
1057       QScreen* screen = QGuiApplication::primaryScreen();
1058       if (userDPI == 0.0) {
1059 #if defined(Q_OS_WIN)
1060       if (QOperatingSystemVersion::current() <= QOperatingSystemVersion(QOperatingSystemVersion::Windows, 7))
1061             _physicalDotsPerInch = screen->logicalDotsPerInch() * screen->devicePixelRatio();
1062       else
1063             _physicalDotsPerInch = screen->physicalDotsPerInch();  // physical resolution
1064 #else
1065       _physicalDotsPerInch = screen->physicalDotsPerInch();        // physical resolution
1066 #endif
1067             }
1068       else {
1069             _physicalDotsPerInch = userDPI;
1070             }
1071       if (guiScaling == 0.0) {
1072             // set scale for icons, palette elements, window sizes, etc
1073             // the default values are hard coded in pixel sizes and assume ~96 DPI
1074             if (qAbs(_physicalDotsPerInch - DPI_DISPLAY) > 6.0)
1075                   guiScaling = _physicalDotsPerInch / DPI_DISPLAY;
1076             else
1077                   guiScaling = 1.0;
1078             }
1079 
1080       MScore::pixelRatio = DPI / screen->logicalDotsPerInch();
1081 
1082       setObjectName("MuseScore");
1083       _sstate = STATE_INIT;
1084       setWindowTitle(QString(MUSESCORE_NAME_VERSION));
1085       setIconSize(QSize(preferences.getInt(PREF_UI_THEME_ICONWIDTH) * guiScaling, preferences.getInt(PREF_UI_THEME_ICONHEIGHT) * guiScaling));
1086 
1087       ucheck = new UpdateChecker(this);
1088       packUChecker = new ExtensionsUpdateChecker(this);
1089 
1090       setAcceptDrops(true);
1091       setFocusPolicy(Qt::NoFocus);
1092 
1093 #ifdef SCRIPT_INTERFACE
1094       pluginManager = new PluginManager(0);
1095 #endif
1096 
1097       _positionLabel = new QLabel;
1098       _positionLabel->setObjectName("decoration widget");  // this prevents animations
1099 
1100       _modeText = new QLabel;
1101       _modeText->setAutoFillBackground(false);
1102       _modeText->setObjectName("modeLabel");
1103 
1104       hRasterAction   = getAction("hraster");
1105       vRasterAction   = getAction("vraster");
1106       loopAction      = getAction("loop");
1107       loopInAction    = getAction("loop-in");
1108       loopOutAction   = getAction("loop-out");
1109       metronomeAction = getAction("metronome");
1110       countInAction   = getAction("countin");
1111       panAction       = getAction("pan");
1112 
1113       _statusBar = new QStatusBar;
1114       _statusBar->addPermanentWidget(new QWidget(this), 2);
1115       _statusBar->addPermanentWidget(new QWidget(this), 100);
1116       _statusBar->addPermanentWidget(_modeText, 0);
1117 
1118       if (enableExperimental) {
1119             layerSwitch = new QComboBox(this);
1120             layerSwitch->setToolTip(tr("Switch layer"));
1121             connect(layerSwitch, SIGNAL(activated(const QString&)), SLOT(switchLayer(const QString&)));
1122             playMode = new QComboBox(this);
1123             playMode->addItem(tr("Synthesizer"));
1124             playMode->addItem(tr("Audio track"));
1125             playMode->setToolTip(tr("Switch play mode"));
1126             connect(playMode, SIGNAL(activated(int)), SLOT(switchPlayMode(int)));
1127 
1128             _statusBar->addPermanentWidget(playMode);
1129             _statusBar->addPermanentWidget(layerSwitch);
1130             }
1131 
1132       _statusBar->addPermanentWidget(_positionLabel, 0);
1133 
1134       setStatusBar(_statusBar);
1135       ScoreAccessibility::createInstance(this);
1136 
1137       // otherwise unused actions:
1138       //   must be added somewhere to work
1139       QActionGroup* ag = Shortcut::getActionGroupForWidget(MsWidget::MAIN_WINDOW);
1140       ag->setParent(this);
1141       addActions(ag->actions());
1142       connect(ag, SIGNAL(triggered(QAction*)), SLOT(cmd(QAction*)));
1143 
1144       mainWindow = new QSplitter;
1145       mainWindow->setObjectName("mainwindow");
1146       mainWindow->setAccessibleName("");
1147       mainWindow->setChildrenCollapsible(false);
1148 
1149       QWidget* mainScore = new QWidget;
1150       mainScore->setObjectName("mainscore");
1151       mainScore->setAccessibleName("");
1152       mainScore->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
1153       mainWindow->addWidget(mainScore);
1154 
1155       layout = new QVBoxLayout;
1156       layout->setObjectName("layout");
1157       layout->setMargin(0);
1158       layout->setSpacing(0);
1159       mainScore->setLayout(layout);
1160 
1161       _navigator = new NScrollArea;
1162       _navigator->setFocusPolicy(Qt::NoFocus);
1163       mainWindow->addWidget(_navigator);
1164       scorePageLayoutChanged();
1165       showNavigator(preferences.getBool(PREF_UI_APP_STARTUP_SHOWNAVIGATOR));
1166 
1167       _timeline = new TDockWidget;
1168       _timeline->setFocusPolicy(Qt::NoFocus);
1169       addDockWidget(Qt::BottomDockWidgetArea, _timeline);
1170       scorePageLayoutChanged();
1171       showTimeline(false);
1172 
1173       scoreCmpTool = new ScoreComparisonTool;
1174       scoreCmpTool->setVisible(false);
1175       {
1176       QAction* a = getAction("toggle-scorecmp-tool");
1177       connect(
1178          scoreCmpTool, &ScoreComparisonTool::visibilityChanged,
1179          a,            &QAction::setChecked
1180          );
1181       }
1182       addDockWidget(Qt::BottomDockWidgetArea, scoreCmpTool);
1183 
1184       if (MuseScore::unstable()) {
1185             scriptRecorder = new ScriptRecorderWidget(this, this);
1186             scriptRecorder->setVisible(false);
1187             QAction* a = getAction("toggle-script-recorder");
1188             connect(
1189                scriptRecorder, &ScriptRecorderWidget::visibilityChanged,
1190                a,              &QAction::setChecked
1191                );
1192             addDockWidget(Qt::RightDockWidgetArea, scriptRecorder);
1193             }
1194 
1195       mainWindow->setStretchFactor(0, 1);
1196       mainWindow->setStretchFactor(1, 0);
1197       mainWindow->setSizes(QList<int>({500, 50}));
1198 
1199       QSplitter* envelope = new QSplitter;
1200       envelope->setObjectName("pane");
1201       envelope->setAccessibleName("");
1202       envelope->setChildrenCollapsible(false);
1203       envelope->setOrientation(Qt::Vertical);
1204       envelope->addWidget(mainWindow);
1205 
1206       importmidiPanel = new ImportMidiPanel(this);
1207       importmidiPanel->setVisible(false);
1208       envelope->addWidget(importmidiPanel);
1209 
1210       {
1211       importmidiShowPanel = new QFrame;
1212       QHBoxLayout *hl = new QHBoxLayout;
1213       hl->setMargin(0);
1214       hl->setSpacing(0);
1215       importmidiShowPanel->setLayout(hl);
1216       showMidiImportButton = new QPushButton();
1217       showMidiImportButton->setFocusPolicy(Qt::ClickFocus);
1218       importmidiShowPanel->setVisible(false);
1219       connect(showMidiImportButton, SIGNAL(clicked()), SLOT(showMidiImportPanel()));
1220       connect(importmidiPanel, SIGNAL(closeClicked()), importmidiShowPanel, SLOT(show()));
1221       hl->addWidget(showMidiImportButton);
1222       QSpacerItem *item = new QSpacerItem(1, 1, QSizePolicy::Expanding, QSizePolicy::Fixed);
1223       hl->addSpacerItem(item);
1224       envelope->addWidget(importmidiShowPanel);
1225       }
1226 
1227       envelope->setSizes(QList<int>({550, 180}));
1228 
1229       splitter = new QSplitter;
1230       splitter->setObjectName("splitter");
1231       splitter->setAccessibleName("");
1232       tab1 = createScoreTab();
1233       splitter->addWidget(tab1);
1234       ctab = tab1; // make tab1 active by default.
1235 
1236       if (splitScreen()) {
1237             tab2 = createScoreTab();
1238             splitter->addWidget(tab2);
1239             tab2->setVisible(false);
1240             }
1241       else
1242             tab2 = 0;
1243       layout->addWidget(splitter);
1244 
1245 
1246       //---------------------------------------------------
1247       //    Transport Action
1248       //---------------------------------------------------
1249 
1250       QAction* a;
1251 #ifdef HAS_MIDI
1252       a  = getAction("midi-on");
1253       a->setChecked(preferences.getBool(PREF_IO_MIDI_ENABLEINPUT));
1254 #endif
1255 
1256       getAction("undo")->setEnabled(false);
1257       getAction("redo")->setEnabled(false);
1258       getAction("paste")->setEnabled(false);
1259       getAction("swap")->setEnabled(false);
1260       selectionChanged(SelState::NONE);
1261 
1262       //---------------------------------------------------
1263       //    File Tool Bar
1264       //---------------------------------------------------
1265 
1266       fileTools = addToolBar("");
1267       fileTools->setObjectName("file-operations");
1268       populateFileOperations();
1269 
1270       //---------------------
1271       //    Transport Tool Bar
1272       //---------------------
1273 
1274       transportTools = addToolBar("");
1275       transportTools->setObjectName("transport-tools");
1276       populatePlaybackControls();
1277 
1278       //-------------------------------
1279       //    Concert Pitch Tool Bar
1280       //-------------------------------
1281 
1282       cpitchTools = addToolBar("");
1283       cpitchTools->setObjectName("pitch-tools");
1284       a = getAction("concert-pitch");
1285       a->setCheckable(true);
1286       AccessibleToolButton* concertPitchButton = new AccessibleToolButton(cpitchTools, a);
1287       concertPitchButton->setProperty("iconic-text", true);
1288       cpitchTools->addWidget(concertPitchButton);
1289 
1290       //-------------------------------
1291       //    Image Capture Tool Bar
1292       //-------------------------------
1293 
1294       fotoTools = addToolBar("");
1295       fotoTools->setObjectName("foto-tools");
1296       fotoTools->addWidget(new AccessibleToolButton(fotoTools, getAction("fotomode")));
1297 
1298       //-------------------------------
1299       //    Feedback Tool Bar
1300       //-------------------------------
1301 
1302       {
1303       feedbackTools = addToolBar("");
1304       feedbackTools->setObjectName("feedback-tools");
1305       // Forbid to move or undock the toolbar...
1306       feedbackTools->setMovable(false);
1307       feedbackTools->setFloatable(false);
1308       // Add a spacer to align the buttons to the right side.
1309       QWidget* spacer = new QWidget(feedbackTools);
1310       spacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
1311       feedbackTools->addWidget(spacer);
1312       // And, finally, add the buttons themselves.
1313       AccessibleToolButton* feedbackButton = new AccessibleToolButton(feedbackTools, getAction("leave-feedback"));
1314       feedbackButton->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
1315       feedbackTools->addWidget(feedbackButton);
1316       }
1317 
1318       addToolBarBreak();
1319 
1320       //-------------------------------
1321       //    Note Input Tool Bar
1322       //-------------------------------
1323 
1324       entryTools = addToolBar("");
1325       entryTools->setObjectName("entry-tools");
1326 
1327       populateNoteInputMenu();
1328 
1329       //-------------------------------
1330       //    Workspaces Tool Bar
1331       //-------------------------------
1332 
1333       {
1334       workspacesTools = addToolBar("");
1335       workspacesTools->setObjectName("workspaces-tools");
1336 
1337       // Add a spacer to align the buttons to the right side.
1338       QWidget* spacer = new QWidget(workspacesTools);
1339       spacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
1340       workspacesTools->addWidget(spacer);
1341 
1342       WorkspaceComboBox* box = new WorkspaceComboBox(this);
1343       workspacesTools->addWidget(box);
1344 
1345       AccessibleToolButton* addWorkspaceButton = new AccessibleToolButton(workspacesTools, getAction("create-new-workspace"));
1346       addWorkspaceButton->setMinimumHeight(24);
1347       workspacesTools->addWidget(addWorkspaceButton);
1348       }
1349 
1350       addToolBarBreak();
1351 
1352       //---------------------
1353       //    Menus
1354       //---------------------
1355 
1356       QMenuBar* mb = menuBar();
1357 
1358       //---------------------
1359       //    Menu File
1360       //---------------------
1361 
1362       menuFile = mb->addMenu("");
1363       menuFile->setObjectName("File");
1364 
1365       a = getAction("startcenter");
1366       a->setCheckable(true);
1367       menuFile->addAction(a);
1368       menuFile->addAction(getAction("file-new"));
1369       menuFile->addAction(getAction("file-open"));
1370 
1371       openRecent = menuFile->addMenu("");
1372 
1373       connect(openRecent, SIGNAL(aboutToShow()), SLOT(openRecentMenu()));
1374       connect(openRecent, SIGNAL(triggered(QAction*)), SLOT(selectScore(QAction*)));
1375 
1376       menuFile->addSeparator();
1377       menuFile->addAction(getAction("file-close"));
1378       menuFile->addAction(getAction("file-save"));
1379       menuFile->addAction(getAction("file-save-as"));
1380       menuFile->addAction(getAction("file-save-a-copy"));
1381       menuFile->addAction(getAction("file-save-selection"));
1382       menuFile->addAction(getAction(saveOnlineMenuItem));
1383 
1384       menuFile->addSeparator();
1385       menuFile->addAction(getAction("file-import-pdf"));
1386       menuFile->addAction(getAction("file-export"));
1387 
1388       menuFile->addSeparator();
1389       menuFile->addAction(getAction("edit-info"));
1390       menuFile->addAction(getAction("parts"));
1391 
1392       // Show these items only in experimental mode
1393       if (enableExperimental) {
1394             menuFile->addAction(getAction("album"));
1395             menuFile->addAction(getAction("layer"));
1396             menuFile->addAction(getAction("media"));
1397       }
1398       menuFile->addSeparator();
1399       menuFile->addAction(getAction("print"));
1400 #ifndef Q_OS_MAC // On Mac, the Quit option is already in the "MuseScore" menu
1401       menuFile->addSeparator();
1402       menuFile->addAction(getAction("quit"));
1403 #endif
1404 
1405       //---------------------
1406       //    Menu Edit
1407       //---------------------
1408 
1409       menuEdit = mb->addMenu("");
1410       menuEdit->setObjectName("Edit");
1411       menuEdit->addAction(getAction("undo"));
1412       menuEdit->addAction(getAction("redo"));
1413 
1414       menuEdit->addSeparator();
1415       menuEdit->addAction(getAction("cut"));
1416       menuEdit->addAction(getAction("copy"));
1417       menuEdit->addAction(getAction("paste"));
1418       menuEdit->addAction(getAction("paste-half"));
1419       menuEdit->addAction(getAction("paste-double"));
1420       menuEdit->addAction(getAction("swap"));
1421       menuEdit->addAction(getAction("delete"));
1422 
1423       menuEdit->addSeparator();
1424       menuEdit->addAction(getAction("select-all"));
1425       menuEdit->addAction(getAction("select-section"));
1426       menuEdit->addAction(getAction("find"));
1427 
1428       menuEdit->addSeparator();
1429 
1430       menuEdit->addAction(getAction("instruments"));
1431 
1432 #ifdef NDEBUG
1433       if (enableExperimental) {
1434 #endif
1435             menuEdit->addSeparator();
1436             menuEdit->addAction(getAction("debugger"));
1437 #ifdef NDEBUG
1438             }
1439 #endif
1440 
1441       menuEdit->addSeparator();
1442       pref = new QAction("", 0);
1443       connect(pref, SIGNAL(triggered()), this, SLOT(startPreferenceDialog()));
1444       menuEdit->addAction(pref);
1445       pref->setMenuRole(QAction::PreferencesRole);
1446       Workspace::addActionAndString(pref, "preference-dialog");
1447 
1448       //---------------------
1449       //    Menu View
1450       //---------------------
1451 
1452       menuView = mb->addMenu("");
1453       menuView->setObjectName("View");
1454 
1455       a = getAction("toggle-palette");
1456       a->setCheckable(true);
1457       menuView->addAction(a);
1458 
1459       a = getAction("masterpalette");
1460       a->setCheckable(true);
1461       menuView->addAction(a);
1462 
1463       a = getAction("inspector");
1464       a->setCheckable(true);
1465       menuView->addAction(a);
1466 #ifdef OMR
1467       a = getAction("omr");
1468       a->setCheckable(true);
1469       menuView->addAction(a);
1470 #endif
1471       playId = getAction("toggle-playpanel");
1472       playId->setCheckable(true);
1473       menuView->addAction(playId);
1474 
1475       a = getAction("toggle-navigator");
1476       a->setCheckable(true);
1477       a->setChecked(preferences.getBool(PREF_UI_APP_STARTUP_SHOWNAVIGATOR));
1478       menuView->addAction(a);
1479 
1480       a = getAction("toggle-timeline");
1481       a->setCheckable(true);
1482       menuView->addAction(a);
1483 
1484       a = getAction("toggle-mixer");
1485       a->setCheckable(true);
1486       menuView->addAction(a);
1487 
1488       a = getAction("synth-control");
1489       a->setCheckable(true);
1490       menuView->addAction(a);
1491 
1492       a = getAction("toggle-selection-window");
1493       a->setCheckable(true);
1494       menuView->addAction(a);
1495 
1496       a = getAction("toggle-piano");
1497       a->setCheckable(true);
1498       menuView->addAction(a);
1499 
1500       a = getAction("toggle-scorecmp-tool");
1501       a->setCheckable(true);
1502       menuView->addAction(a);
1503 
1504       menuView->addSeparator();
1505       menuView->addAction(getAction("zoomin"));
1506       menuView->addAction(getAction("zoomout"));
1507       menuView->addSeparator();
1508 
1509       menuToolbars = new QMenu();
1510 
1511       a = getAction("toggle-fileoperations");
1512       a->setCheckable(true);
1513       a->setChecked(fileTools->isVisible());
1514       connect(fileTools, SIGNAL(visibilityChanged(bool)), a, SLOT(setChecked(bool)));
1515       menuToolbars->addAction(a);
1516 
1517       a = getAction("toggle-transport");
1518       a->setCheckable(true);
1519       a->setChecked(transportTools->isVisible());
1520       connect(transportTools, SIGNAL(visibilityChanged(bool)), a, SLOT(setChecked(bool)));
1521       menuToolbars->addAction(a);
1522 
1523       a = getAction("toggle-concertpitch");
1524       a->setCheckable(true);
1525       a->setChecked(cpitchTools->isVisible());
1526       connect(cpitchTools, SIGNAL(visibilityChanged(bool)), a, SLOT(setChecked(bool)));
1527       menuToolbars->addAction(a);
1528 
1529       a = getAction("toggle-imagecapture");
1530       a->setCheckable(true);
1531       a->setChecked(fotoTools->isVisible());
1532       connect(fotoTools, SIGNAL(visibilityChanged(bool)), a, SLOT(setChecked(bool)));
1533       menuToolbars->addAction(a);
1534 
1535       a = getAction("toggle-noteinput");
1536       a->setCheckable(true);
1537       a->setChecked(entryTools->isVisible());
1538       connect(entryTools, SIGNAL(visibilityChanged(bool)), a, SLOT(setChecked(bool)));
1539       menuToolbars->addAction(a);
1540 
1541       a = getAction("toggle-feedback");
1542       a->setCheckable(true);
1543       a->setChecked(feedbackTools->isVisible());
1544       connect(feedbackTools, SIGNAL(visibilityChanged(bool)), a, SLOT(setChecked(bool)));
1545       menuToolbars->addAction(a);
1546 
1547       a = getAction("toggle-workspaces-toolbar");
1548       a->setCheckable(true);
1549       a->setChecked(workspacesTools->isVisible());
1550       connect(workspacesTools, SIGNAL(visibilityChanged(bool)), a, SLOT(setChecked(bool)));
1551       menuToolbars->addAction(a);
1552 
1553       menuToolbars->addSeparator();
1554 
1555       menuToolbars->addAction(getAction("edit-toolbars"));
1556 
1557       menuView->addMenu(menuToolbars);
1558 
1559       menuWorkspaces = new QMenu();
1560       connect(menuWorkspaces, SIGNAL(aboutToShow()), SLOT(showWorkspaceMenu()));
1561       menuView->addMenu(menuWorkspaces);
1562 
1563       a = getAction("toggle-statusbar");
1564       a->setCheckable(true);
1565       a->setChecked(preferences.getBool(PREF_UI_APP_SHOWSTATUSBAR));
1566       menuView->addAction(a);
1567 
1568       menuView->addSeparator();
1569       a = getAction("split-h");
1570       a->setCheckable(true);
1571       menuView->addAction(a);
1572       a = getAction("split-v");
1573       a->setCheckable(true);
1574       menuView->addAction(a);
1575 
1576       menuView->addSeparator();
1577       menuView->addAction(getAction("show-invisible"));
1578       menuView->addAction(getAction("show-unprintable"));
1579       menuView->addAction(getAction("show-frames"));
1580       menuView->addAction(getAction("show-pageborders"));
1581       menuView->addAction(getAction("mark-irregular"));
1582       menuView->addSeparator();
1583 
1584       a = getAction("fullscreen");
1585       a->setCheckable(true);
1586       a->setChecked(false);
1587 #ifndef Q_OS_MAC
1588       menuView->addAction(a);
1589 #endif
1590 
1591       //---------------------
1592       //    Menu Add
1593       //---------------------
1594 
1595       menuAdd = mb->addMenu("");
1596       menuAdd->setObjectName("Add");
1597 
1598       menuAddPitch = new QMenu();
1599       menuAddPitch->addAction(getAction("note-input"));
1600       menuAddPitch->addSeparator();
1601 
1602       for (int i = 0; i < 7; ++i) {
1603             char buffer[8];
1604             sprintf(buffer, "note-%c", "cdefgab"[i]);
1605             a = getAction(buffer);
1606             menuAddPitch->addAction(a);
1607             }
1608       menuAddPitch->addSeparator();
1609       for (int i = 0; i < 7; ++i) {
1610             char buffer[8];
1611             sprintf(buffer, "chord-%c", "cdefgab"[i]);
1612             a = getAction(buffer);
1613             menuAddPitch->addAction(a);
1614             }
1615       menuAdd->addMenu(menuAddPitch);
1616 
1617       menuAddInterval = new QMenu();
1618       for (int i = 1; i < 10; ++i) {
1619             char buffer[16];
1620             sprintf(buffer, "interval%d", i);
1621             a = getAction(buffer);
1622             menuAddInterval->addAction(a);
1623             }
1624       menuAddInterval->addSeparator();
1625       for (int i = 2; i < 10; ++i) {
1626             char buffer[16];
1627             sprintf(buffer, "interval-%d", i);
1628             a = getAction(buffer);
1629             menuAddInterval->addAction(a);
1630             }
1631       menuAdd->addMenu(menuAddInterval);
1632 
1633       menuTuplet = new QMenu();
1634       for (auto i : { "duplet", "triplet", "quadruplet", "quintuplet", "sextuplet",
1635             "septuplet", "octuplet", "nonuplet", "tuplet-dialog" })
1636             menuTuplet->addAction(getAction(i));
1637       menuAdd->addMenu(menuTuplet);
1638 
1639       menuAdd->addSeparator();
1640 
1641       menuAddMeasures = new QMenu("");
1642       menuAddMeasures->addAction(getAction("insert-measure"));
1643       menuAddMeasures->addAction(getAction("insert-measures"));
1644       menuAddMeasures->addSeparator();
1645       menuAddMeasures->addAction(getAction("append-measure"));
1646       menuAddMeasures->addAction(getAction("append-measures"));
1647       menuAdd->addMenu(menuAddMeasures);
1648 
1649       menuAddFrames = new QMenu();
1650       menuAddFrames->addAction(getAction("insert-hbox"));
1651       menuAddFrames->addAction(getAction("insert-vbox"));
1652       menuAddFrames->addAction(getAction("insert-textframe"));
1653       if (enableExperimental)
1654             menuAddFrames->addAction(getAction("insert-fretframe"));
1655       menuAddFrames->addSeparator();
1656       menuAddFrames->addAction(getAction("append-hbox"));
1657       menuAddFrames->addAction(getAction("append-vbox"));
1658       menuAddFrames->addAction(getAction("append-textframe"));
1659       menuAdd->addMenu(menuAddFrames);
1660 
1661       menuAddText = new QMenu();
1662       menuAddText->addAction(getAction("title-text"));
1663       menuAddText->addAction(getAction("subtitle-text"));
1664       menuAddText->addAction(getAction("composer-text"));
1665       menuAddText->addAction(getAction("poet-text"));
1666       menuAddText->addAction(getAction("part-text"));
1667       menuAddText->addSeparator();
1668       menuAddText->addAction(getAction("system-text"));
1669       menuAddText->addAction(getAction("staff-text"));
1670       menuAddText->addAction(getAction("expression-text"));
1671       menuAddText->addAction(getAction("chord-text"));
1672       menuAddText->addAction(getAction("rehearsalmark-text"));
1673       menuAddText->addAction(getAction("instrument-change-text"));
1674       menuAddText->addAction(getAction("fingering-text"));
1675       menuAddText->addSeparator();
1676       menuAddText->addAction(getAction("lyrics"));
1677       menuAddText->addAction(getAction("sticking-text"));
1678       menuAddText->addAction(getAction("roman-numeral-text"));
1679       menuAddText->addAction(getAction("nashville-number-text"));
1680       menuAddText->addAction(getAction("figured-bass"));
1681       menuAddText->addAction(getAction("tempo"));
1682       menuAdd->addMenu(menuAddText);
1683 
1684       menuAddLines = new QMenu();
1685       menuAddLines->addAction(getAction("add-slur"));
1686       menuAddLines->addAction(getAction("add-hairpin"));
1687       menuAddLines->addAction(getAction("add-hairpin-reverse"));
1688       menuAddLines->addAction(getAction("add-8va"));
1689       menuAddLines->addAction(getAction("add-8vb"));
1690       menuAddLines->addAction(getAction("add-noteline"));
1691       menuAdd->addMenu(menuAddLines);
1692 
1693       //---------------------
1694       //    Menu Format
1695       //---------------------
1696 
1697       menuFormat = mb->addMenu("");
1698       menuFormat->setObjectName("Format");
1699 
1700       menuFormat->addAction(getAction("edit-style"));
1701       QAction* pageSettingsAction = getAction("page-settings");
1702       // in some locale (fr), page settings ends up in Application menu on mac
1703       // this line prevents it.
1704       pageSettingsAction->setMenuRole(QAction::NoRole);
1705       menuFormat->addAction(pageSettingsAction);
1706       menuFormat->addSeparator();
1707 
1708       menuFormat->addAction(getAction("add-remove-breaks"));
1709 
1710       menuStretch = new QMenu(tr("&Stretch"));
1711       for (auto i : { "stretch+", "stretch-", "reset-stretch" })
1712             menuStretch->addAction(getAction(i));
1713       Workspace::addMenuAndString(menuStretch, "menu-stretch");
1714       menuFormat->addMenu(menuStretch);
1715       menuFormat->addSeparator();
1716 
1717       menuFormat->addAction(getAction("reset-text-style-overrides"));
1718       menuFormat->addAction(getAction("reset-beammode"));
1719       menuFormat->addAction(getAction("reset"));
1720       menuFormat->addSeparator();
1721 
1722       if (enableExperimental)
1723             menuFormat->addAction(getAction("edit-harmony"));
1724 
1725       menuFormat->addSeparator();
1726       menuFormat->addAction(getAction("load-style"));
1727       menuFormat->addAction(getAction("save-style"));
1728 
1729       //---------------------
1730       //    Menu Tools
1731       //---------------------
1732 
1733       menuTools = mb->addMenu("");
1734       menuTools->setObjectName("Tools");
1735 
1736       menuTools->addAction(getAction("transpose"));
1737       menuTools->addSeparator();
1738       menuTools->addAction(getAction("explode"));
1739       menuTools->addAction(getAction("implode"));
1740 
1741       menuTools->addAction(getAction("realize-chord-symbols"));
1742 
1743       menuVoices = new QMenu("");
1744       for (auto i : { "voice-x12", "voice-x13", "voice-x14", "voice-x23", "voice-x24", "voice-x34" })
1745             menuVoices->addAction(getAction(i));
1746       menuTools->addMenu(menuVoices);
1747 
1748       menuMeasure = new QMenu("");
1749       for (auto i : { "split-measure", "join-measures" })
1750             menuMeasure->addAction(getAction(i));
1751       menuTools->addMenu(menuMeasure);
1752       menuTools->addAction(getAction("time-delete"));
1753 
1754       menuTools->addSeparator();
1755 
1756       menuTools->addAction(getAction("slash-fill"));
1757       menuTools->addAction(getAction("slash-rhythm"));
1758       menuTools->addSeparator();
1759 
1760       menuTools->addAction(getAction("pitch-spell"));
1761       menuTools->addAction(getAction("reset-groupings"));
1762       menuTools->addAction(getAction("resequence-rehearsal-marks"));
1763       menuTools->addAction(getAction("unroll-repeats"));
1764       menuTools->addSeparator();
1765 
1766       menuTools->addAction(getAction("copy-lyrics-to-clipboard"));
1767       menuTools->addAction(getAction("fotomode"));
1768 
1769       menuTools->addAction(getAction("del-empty-measures"));
1770 
1771       if (MuseScore::unstable()) {
1772             menuTools->addSeparator();
1773             a = getAction("toggle-script-recorder");
1774             a->setCheckable(true);
1775             menuTools->addAction(a);
1776             }
1777 
1778       //---------------------
1779       //    Menu Plugins
1780       //---------------------
1781 
1782 #ifdef SCRIPT_INTERFACE
1783       menuPlugins = mb->addMenu("");
1784       menuPlugins->setObjectName("Plugins");
1785 
1786       menuPlugins->addAction(getAction("plugin-manager"));
1787 
1788       a = getAction("plugin-creator");
1789       a->setCheckable(true);
1790       menuPlugins->addAction(a);
1791 
1792       menuPlugins->addSeparator();
1793 #endif
1794 
1795       //---------------------
1796       //    Menu Debug
1797       //---------------------
1798 
1799 #ifndef NDEBUG
1800       menuDebug = mb->addMenu("Debug");
1801       menuDebug->setObjectName("Debug");
1802       a = getAction("no-horizontal-stretch");
1803       a->setCheckable(true);
1804       menuDebug->addAction(a);
1805       a = getAction("no-vertical-stretch");
1806       a->setCheckable(true);
1807       menuDebug->addAction(a);
1808       menuDebug->addSeparator();
1809       a = getAction("show-segment-shapes");
1810       a->setCheckable(true);
1811       menuDebug->addAction(a);
1812       a = getAction("show-skylines");
1813       a->setCheckable(true);
1814       a->setChecked(MScore::showSkylines);
1815       menuDebug->addAction(a);
1816       a = getAction("show-bounding-rect");
1817       a->setCheckable(true);
1818       menuDebug->addAction(a);
1819       a = getAction("show-system-bounding-rect");
1820       a->setCheckable(true);
1821       menuDebug->addAction(a);
1822       a = getAction("show-corrupted-measures");
1823       a->setCheckable(true);
1824       a->setChecked(true);
1825       menuDebug->addAction(a);
1826       a = getAction("relayout");
1827       menuDebug->addAction(a);
1828       a = getAction("qml-reload-source");
1829       menuDebug->addAction(a);
1830       Workspace::addMenuAndString(menuDebug, "menu-debug");
1831 #endif
1832 
1833       //---------------------
1834       //    Menu Help
1835       //---------------------
1836 
1837       mb->addSeparator();
1838       menuHelp = mb->addMenu("");
1839       menuHelp->setObjectName("Help");
1840 
1841 #if 0
1842       if (_helpEngine) {
1843             HelpQuery* hw = new HelpQuery(menuHelp);
1844             menuHelp->addAction(hw);
1845             connect(menuHelp, SIGNAL(aboutToShow()), hw, SLOT(setFocus()));
1846             }
1847 #endif
1848       //menuHelp->addAction(getAction("help"));
1849       onlineHandbookAction = new QAction("", 0);
1850       connect(onlineHandbookAction, SIGNAL(triggered()), this, SLOT(helpBrowser1()));
1851       onlineHandbookAction->setMenuRole(QAction::NoRole);
1852       menuHelp->addAction(onlineHandbookAction);
1853       Workspace::addActionAndString(onlineHandbookAction, "online-handbook");
1854 
1855       menuTours = new QMenu();
1856       a = getAction("show-tours");
1857       a->setChecked(preferences.getBool(PREF_UI_APP_STARTUP_SHOWTOURS));
1858       menuTours->addAction(a);
1859       menuTours->addAction(getAction("reset-tours"));
1860       menuHelp->addMenu(menuTours);
1861 
1862       menuHelp->addSeparator();
1863 
1864       aboutAction = new QAction("", 0);
1865 
1866       aboutAction->setMenuRole(QAction::AboutRole);
1867       connect(aboutAction, SIGNAL(triggered()), this, SLOT(about()));
1868       menuHelp->addAction(aboutAction);
1869       Workspace::addActionAndString(aboutAction, "about");
1870 
1871       aboutQtAction = new QAction("", 0);
1872       aboutQtAction->setMenuRole(QAction::AboutQtRole);
1873       connect(aboutQtAction, SIGNAL(triggered()), this, SLOT(aboutQt()));
1874       menuHelp->addAction(aboutQtAction);
1875       Workspace::addActionAndString(aboutQtAction, "about-qt");
1876 
1877       aboutMusicXMLAction = new QAction("", 0);
1878       //aboutMusicXMLAction->setMenuRole(QAction::ApplicationSpecificRole);
1879       aboutMusicXMLAction->setMenuRole(QAction::NoRole);
1880       connect(aboutMusicXMLAction, SIGNAL(triggered()), this, SLOT(aboutMusicXML()));
1881       menuHelp->addAction(aboutMusicXMLAction);
1882       Workspace::addActionAndString(aboutMusicXMLAction, "about-musicxml");
1883 
1884 #if defined(Q_OS_MAC) || defined(Q_OS_WIN)
1885 #if (!defined(FOR_WINSTORE)) && (!defined(WIN_PORTABLE))
1886       checkForUpdateAction = new QAction("", 0);
1887       connect(checkForUpdateAction, SIGNAL(triggered()), this, SLOT(checkForUpdatesUI()));
1888       checkForUpdateAction->setMenuRole(QAction::NoRole);
1889       menuHelp->addAction(checkForUpdateAction);
1890       Workspace::addActionAndString(checkForUpdateAction, "check-update");
1891 #endif
1892 #endif
1893       menuHelp->addSeparator();
1894 
1895       askForHelpAction = new QAction("", 0);
1896       connect(askForHelpAction, SIGNAL(triggered()), this, SLOT(askForHelp()));
1897       askForHelpAction->setMenuRole(QAction::NoRole);
1898       menuHelp->addAction(askForHelpAction);
1899       Workspace::addActionAndString(askForHelpAction, "ask-help");
1900 
1901       reportBugAction = new QAction("", 0);
1902       connect(reportBugAction, &QAction::triggered, this, [this]{ reportBug("menu"); });
1903       reportBugAction->setMenuRole(QAction::NoRole);
1904       menuHelp->addAction(reportBugAction);
1905       Workspace::addActionAndString(reportBugAction, "report-bug");
1906 
1907       leaveFeedbackAction = new QAction("", 0);
1908       connect(leaveFeedbackAction, &QAction::triggered, this, [this]{ leaveFeedback("menu"); });
1909       leaveFeedbackAction->setMenuRole(QAction::NoRole);
1910       menuHelp->addAction(leaveFeedbackAction);
1911       Workspace::addActionAndString(leaveFeedbackAction, "leave-feedback");
1912 
1913       menuHelp->addSeparator();
1914       menuHelp->addAction(getAction("resource-manager"));
1915       menuHelp->addSeparator();
1916 
1917       revertToFactoryAction = new QAction("", 0);
1918       connect(revertToFactoryAction, SIGNAL(triggered()), this, SLOT(resetAndRestart()));
1919       revertToFactoryAction->setMenuRole(QAction::NoRole);
1920       menuHelp->addAction(revertToFactoryAction);
1921       Workspace::addActionAndString(revertToFactoryAction, "revert-factory");
1922 
1923       Workspace::addRemainingFromMenuBar(mb);
1924 
1925       // Add all menus to workspace for loading
1926       Workspace::addMenuAndString(menuFile,        "menu-file");
1927       Workspace::addMenuAndString(openRecent,      "menu-open-recent");
1928       Workspace::addMenuAndString(menuEdit,        "menu-edit");
1929       Workspace::addMenuAndString(menuView,        "menu-view");
1930       Workspace::addMenuAndString(menuToolbars,    "menu-toolbars");
1931       Workspace::addMenuAndString(menuWorkspaces,  "menu-workspaces");
1932       Workspace::addMenuAndString(menuAdd,         "menu-add");
1933       Workspace::addMenuAndString(menuAddMeasures, "menu-add-measures");
1934       Workspace::addMenuAndString(menuAddFrames,   "menu-add-frames");
1935       Workspace::addMenuAndString(menuAddText,     "menu-add-text");
1936       Workspace::addMenuAndString(menuAddLines,    "menu-add-lines");
1937       Workspace::addMenuAndString(menuAddPitch,    "menu-add-pitch");
1938       Workspace::addMenuAndString(menuAddInterval, "menu-add-interval");
1939       Workspace::addMenuAndString(menuTuplet,      "menu-tuplet");
1940       Workspace::addMenuAndString(menuFormat,      "menu-format");
1941       Workspace::addMenuAndString(menuTools,       "menu-tools");
1942       Workspace::addMenuAndString(menuVoices,      "menu-voices");
1943       Workspace::addMenuAndString(menuMeasure,     "menu-measure");
1944 #ifdef SCRIPT_INTERFACE
1945       Workspace::addMenuAndString(menuPlugins,     "menu-plugins");
1946 #endif
1947       Workspace::addMenuAndString(menuHelp,        "menu-help");
1948       Workspace::addMenuAndString(menuTours,       "menu-tours");
1949 
1950       Workspace::writeGlobalMenuBar(mb);
1951 
1952       if (!MScore::noGui) {
1953             retranslate();
1954             //accessibility for menus
1955             for (QMenu* menu : mb->findChildren<QMenu*>()) {
1956                   menu->setAccessibleName(menu->objectName());
1957                   menu->setAccessibleDescription(Shortcut::getMenuShortcutString(menu));
1958                   }
1959             }
1960 
1961       setCentralWidget(envelope);
1962 
1963       if (!MScore::noGui)
1964             preferencesChanged();
1965 
1966       if (seq) {
1967             connect(seq, SIGNAL(started()), SLOT(seqStarted()));
1968             connect(seq, SIGNAL(stopped()), SLOT(seqStopped()));
1969             }
1970       loadScoreList();
1971 
1972       QClipboard* cb = QApplication::clipboard();
1973       connect(cb, SIGNAL(dataChanged()), SLOT(clipboardChanged()));
1974       connect(cb, SIGNAL(selectionChanged()), SLOT(clipboardChanged()));
1975 
1976       autoSaveTimer = new QTimer(this);
1977       autoSaveTimer->setSingleShot(true);
1978       connect(autoSaveTimer, SIGNAL(timeout()), this, SLOT(autoSaveTimerTimeout()));
1979       initOsc();
1980       startAutoSave();
1981 
1982 #ifndef NDEBUG
1983       // for debugging, but possible may need
1984       QInputMethod* im = QGuiApplication::inputMethod();
1985       connect(im, SIGNAL(anchorRectangleChanged()), SLOT(inputMethodAnchorRectangleChanged()));
1986       connect(im, SIGNAL(animatingChanged()), SLOT(inputMethodAnimatingChanged()));
1987       connect(im, SIGNAL(cursorRectangleChanged()), SLOT(inputMethodCursorRectangleChanged()));
1988 //signal does not exist:
1989 //      connect(im, SIGNAL(inputDirectionChanged(Qt::LayoutDirection newDirection)), SLOT(inputMethodInputDirectionChanged(Qt::LayoutDirection newDirection)));
1990       connect(im, SIGNAL(inputItemClipRectangleChanged()), SLOT(inputMethodInputItemClipRectangleChanged()));
1991       connect(im, SIGNAL(keyboardRectangleChanged()), SLOT(inputMethodKeyboardRectangleChanged()));
1992       connect(im, SIGNAL(localeChanged()), SLOT(inputMethodLocaleChanged()));
1993       connect(im, SIGNAL(visibleChanged()), SLOT(inputMethodVisibleChanged()));
1994 #endif
1995 
1996       if (enableExperimental) {
1997             cornerLabel = new QLabel(this);
1998             cornerLabel->setScaledContents(true);
1999             cornerLabel->setPixmap(QPixmap(":/data/mscore.png"));
2000             cornerLabel->setGeometry(width() - 48, 0, 48, 48);
2001             }
2002 #if defined(WIN_SPARKLE_ENABLED)
2003       autoUpdater.reset(new WinSparkleAutoUpdater);
2004 #elif defined(MAC_SPARKLE_ENABLED)
2005       autoUpdater.reset(new SparkleAutoUpdater);
2006 #endif
2007       connect(this, SIGNAL(musescoreWindowWasShown()), this, SLOT(checkForUpdates()),
2008             Qt::ConnectionType(Qt::QueuedConnection | Qt::UniqueConnection));
2009 
2010       if (!converterMode && !pluginMode) {
2011             _loginManager = new LoginManager(getAction(saveOnlineMenuItem), this);
2012             const bool loadSuccess = _loginManager->load();
2013 
2014             if (cliSaveOnline && !loadSuccess) {
2015                   fprintf(stderr, "%s\n", qPrintable(tr("No login credentials stored. Please sign in via the GUI.")));
2016                   ::exit(EXIT_FAILURE);
2017                   }
2018             }
2019 
2020       connect(qApp, &QGuiApplication::focusWindowChanged, this, &MuseScore::onFocusWindowChanged);
2021       }
2022 
~MuseScore()2023 MuseScore::~MuseScore()
2024       {
2025 #ifdef Q_OS_MAC // On Mac, remove the Cocoa Notification Observers
2026       // (Currently, there is only one for Dark Mode)
2027       CocoaBridge::removeObservers();
2028 #endif
2029 
2030       if (autoUpdater)
2031             autoUpdater->cleanup();
2032 
2033       delete synti;
2034       synti = nullptr;
2035 
2036       // A crash is possible if paletteWorkspace gets
2037       // deleted before paletteWidget, so force the widget
2038       // be deleted before paletteWorkspace.
2039       delete paletteWidget;
2040       paletteWidget = nullptr;
2041       }
2042 
2043 //---------------------------------------------------------
2044 //   playPanelInterface
2045 //---------------------------------------------------------
2046 
playPanelInterface() const2047 IPlayPanel* MuseScore::playPanelInterface() const
2048       {
2049       return playPanel;
2050       }
2051 
2052 //---------------------------------------------------------
2053 //   onFocusWindowChanged
2054 //---------------------------------------------------------
2055 
onFocusWindowChanged(QWindow * w)2056 void MuseScore::onFocusWindowChanged(QWindow* w)
2057       {
2058       const QWindow* mscoreWindow = windowHandle();
2059 
2060       if (!QApplication::focusWidget() && _lastFocusWindow
2061          && ((!w && _lastFocusWindow != mscoreWindow) || (w == mscoreWindow && _lastFocusWindowIsQQuickView))) {
2062             // Switch to temporary window to work around inability to return focus to QML-based windows
2063             QWidget* tmpContainer = createWindowContainer(new QWindow, this);
2064             tmpContainer->show();
2065             tmpContainer->setFocus();
2066 
2067             QTimer::singleShot(0, [this, tmpContainer]() {
2068                   focusScoreView();
2069                   tmpContainer->deleteLater();
2070                   });
2071             }
2072 
2073       _lastFocusWindow = w;
2074       _lastFocusWindowIsQQuickView = qobject_cast<QQuickView*>(_lastFocusWindow);
2075       }
2076 
2077 //---------------------------------------------------------
2078 //   showError
2079 //---------------------------------------------------------
2080 
showError()2081 void MuseScore::showError()
2082       {
2083       static QErrorMessage* msg = 0;
2084       if (msg == 0)
2085             msg = new QErrorMessage(this);
2086       msg->showMessage(qApp->translate("error", MScore::errorMessage()), MScore::errorGroup());
2087       MScore::setError(MS_NO_ERROR);
2088       }
2089 
2090 //---------------------------------------------------------
2091 //   retranslate
2092 //---------------------------------------------------------
2093 
retranslate()2094 void MuseScore::retranslate()
2095       {
2096       setMenuTitles();
2097       _positionLabel->setToolTip(tr("Measure:Beat:Tick"));
2098       pref->setText(tr("&Preferences…"));
2099       aboutAction->setText(tr("&About…"));
2100       aboutQtAction->setText(tr("About &Qt…"));
2101       aboutMusicXMLAction->setText(tr("About &MusicXML…"));
2102       onlineHandbookAction->setText(tr("&Online Handbook"));
2103       if (checkForUpdateAction)
2104             checkForUpdateAction->setText(tr("Check for &Update"));
2105       askForHelpAction->setText(tr("Ask for Help"));
2106       reportBugAction->setText(tr("Report a Bug"));
2107       leaveFeedbackAction->setText(tr("Feedback"));
2108       revertToFactoryAction->setText(tr("Revert to Factory Settings"));
2109 
2110       fileTools->setWindowTitle(tr("File Operations"));
2111       transportTools->setWindowTitle(tr("Playback Controls"));
2112       cpitchTools->setWindowTitle(tr("Concert Pitch"));
2113       fotoTools->setWindowTitle(tr("Image Capture"));
2114       entryTools->setWindowTitle(tr("Note Input"));
2115       feedbackTools->setWindowTitle(tr("Feedback"));
2116       workspacesTools->setWindowTitle(tr("Workspaces"));
2117 
2118       viewModeCombo->setAccessibleName(tr("View Mode"));
2119       viewModeCombo->setItemText(viewModeCombo->findData(int(LayoutMode::PAGE)), tr("Page View"));
2120       viewModeCombo->setItemText(viewModeCombo->findData(int(LayoutMode::LINE)), tr("Continuous View"));
2121       viewModeCombo->setItemText(viewModeCombo->findData(int(LayoutMode::SYSTEM)), tr("Single Page"));
2122 
2123       showMidiImportButton->setText(tr("Show MIDI import panel"));
2124 
2125       Shortcut::retranslate();
2126       WorkspacesManager::retranslateAll();
2127 
2128       if (paletteWorkspace)
2129           paletteWorkspace->retranslate();
2130       }
2131 
2132 //---------------------------------------------------------
2133 //   setMenuTitles
2134 //---------------------------------------------------------
2135 
setMenuTitles()2136 void MuseScore::setMenuTitles()
2137       {
2138       // The list below is not static, both strings
2139       // and menu pointers need refreshing.
2140       const std::initializer_list<std::pair<QMenu*, QString>> titles {
2141             { menuFile,             tr("&File")             },
2142             { openRecent,           tr("Open &Recent")      },
2143             { menuEdit,             tr("&Edit")             },
2144             { menuView,             tr("&View")             },
2145             { menuToolbars,         tr("&Toolbars")         },
2146             { menuWorkspaces,       tr("W&orkspaces")       },
2147             { menuAdd,              tr("&Add")              },
2148             { menuAddMeasures,      tr("&Measures")         },
2149             { menuAddFrames,        tr("&Frames")           },
2150             { menuAddText,          tr("&Text")             },
2151             { menuAddLines,         tr("&Lines")            },
2152             { menuAddPitch,         tr("N&otes")            },
2153             { menuAddInterval,      tr("&Intervals")        },
2154             { menuTuplet,           tr("T&uplets")          },
2155             { menuFormat,           tr("F&ormat")           },
2156             { menuStretch,          tr("&Stretch")          },
2157             { menuTools,            tr("&Tools")            },
2158             { menuVoices,           tr("&Voices")           },
2159             { menuMeasure,          tr("&Measure")          },
2160 #ifdef SCRIPT_INTERFACE
2161             { menuPlugins,          tr("&Plugins")          },
2162 #endif
2163 #ifndef NDEBUG
2164             { menuDebug,            "Debug"                 }, // not translated
2165 #endif
2166             { menuHelp,             tr("&Help")             },
2167             { menuTours,            tr("&Tours")            }
2168             };
2169 
2170       for (const auto& t : titles) {
2171             QMenu* m = t.first;
2172             if (m)
2173                   m->setTitle(t.second);
2174             }
2175       }
2176 
2177 //---------------------------------------------------------
2178 //   updateMenu
2179 //---------------------------------------------------------
2180 
updateMenu(QMenu * & menu,QString menu_id,QString name)2181 void MuseScore::updateMenu(QMenu*& menu, QString menu_id, QString name)
2182       {
2183       menu = Workspace::findMenuFromString(menu_id);
2184       if (menu) {
2185             if (name != "")
2186                   menu->setObjectName(name);
2187             }
2188       }
2189 
2190 //---------------------------------------------------------
2191 //   updateMenus
2192 //---------------------------------------------------------
2193 
updateMenus()2194 void MuseScore::updateMenus()
2195       {
2196       updateMenu(menuFile,        "menu-file",         "File");
2197       updateMenu(openRecent,      "menu-open-recent",  "");
2198       updateMenu(menuEdit,        "menu-edit",         "Edit");
2199       updateMenu(menuView,        "menu-view",         "View");
2200       updateMenu(menuToolbars,    "menu-toolbars",     "");
2201       updateMenu(menuWorkspaces,  "menu-workspaces",   "");
2202       updateMenu(menuAdd,         "menu-add",          "Add");
2203       updateMenu(menuAddMeasures, "menu-add-measures", "");
2204       updateMenu(menuAddFrames,   "menu-add-frames",   "");
2205       updateMenu(menuAddText,     "menu-add-text",     "");
2206       updateMenu(menuAddLines,    "menu-add-lines",    "");
2207       updateMenu(menuAddPitch,    "menu-add-pitch",    "");
2208       updateMenu(menuAddInterval, "menu-add-interval", "");
2209       updateMenu(menuTuplet,      "menu-tuplet",       "");
2210       updateMenu(menuFormat,      "menu-format",       "Format");
2211       updateMenu(menuStretch,     "menu-stretch",      "");
2212       updateMenu(menuTools,       "menu-tools",        "Tools");
2213       updateMenu(menuVoices,      "menu-voices",       "");
2214       updateMenu(menuMeasure,     "menu-measure",      "");
2215 #ifdef SCRIPT_INTERFACE
2216       updateMenu(menuPlugins,     "menu-plugins",      "Plugins");
2217 #endif
2218       updateMenu(menuHelp,        "menu-help",         "Help");
2219       updateMenu(menuTours,       "menu-tours",        "");
2220 #ifndef NDEBUG
2221       updateMenu(menuDebug,       "menu-debug",        "Debug");
2222 #endif
2223       connect(openRecent,     SIGNAL(aboutToShow()),       SLOT(openRecentMenu()));
2224       connect(openRecent,     SIGNAL(triggered(QAction*)), SLOT(selectScore(QAction*)));
2225       connect(menuWorkspaces, SIGNAL(aboutToShow()),       SLOT(showWorkspaceMenu()));
2226       setMenuTitles();
2227 #ifdef SCRIPT_INTERFACE
2228       addPluginMenuEntries();
2229 #endif
2230       }
2231 
2232 //---------------------------------------------------------
2233 //   resizeEvent
2234 //---------------------------------------------------------
2235 
resizeEvent(QResizeEvent *)2236 void MuseScore::resizeEvent(QResizeEvent*)
2237       {
2238       if (enableExperimental) {
2239             cornerLabel->setGeometry(width() - 48, 0, 48, 48);
2240             }
2241       }
2242 
2243 //---------------------------------------------------------
2244 //   startAutoSave
2245 //---------------------------------------------------------
2246 
startAutoSave()2247 void MuseScore::startAutoSave()
2248       {
2249       if (preferences.getBool(PREF_APP_AUTOSAVE_USEAUTOSAVE)) {
2250             int t = preferences.getInt(PREF_APP_AUTOSAVE_AUTOSAVETIME) * 60 * 1000;
2251             autoSaveTimer->start(t);
2252             }
2253       else
2254             autoSaveTimer->stop();
2255       }
2256 
2257 //---------------------------------------------------------
2258 //   getLocaleISOCode
2259 //---------------------------------------------------------
2260 
getLocaleISOCode() const2261 QString MuseScore::getLocaleISOCode() const
2262       {
2263       QString l = localeName();
2264       if (l.toLower() == "system")
2265             return QLocale::system().name();
2266       return l;
2267       }
2268 
2269 //---------------------------------------------------------
2270 //   helpBrowser1
2271 //    show online help
2272 //---------------------------------------------------------
2273 
helpBrowser1() const2274 void MuseScore::helpBrowser1() const
2275       {
2276       QString lang = getLocaleISOCode();
2277 
2278       if (MScore::debugMode)
2279             qDebug("open online handbook for language <%s>", qPrintable(lang));
2280       QString help = QString("https://musescore.org/redirect/help?tag=handbook&locale=%1").arg(getLocaleISOCode());
2281       //try to find an exact match
2282       bool found = false;
2283       foreach (LanguageItem item, _languages) {
2284             if (item.key == lang) {
2285                   QString handbook = item.handbook;
2286                   if (!handbook.isNull()) {
2287                       help = item.handbook;
2288                       found = true;
2289                       }
2290                   break;
2291                   }
2292             }
2293       //try a to find a match on first two letters
2294       if (!found && lang.size() > 2) {
2295             lang = lang.left(2);
2296             foreach (LanguageItem item, _languages) {
2297                   if (item.key == lang){
2298                       QString handbook = item.handbook;
2299                       if (!handbook.isNull())
2300                           help = item.handbook;
2301                       break;
2302                       }
2303                   }
2304             }
2305 
2306       //track visits. see: http://www.google.com/support/googleanalytics/bin/answer.py?answer=55578
2307       help += '&';
2308       help += getUtmParameters("menu");
2309       QDesktopServices::openUrl(QUrl(help));
2310       }
2311 
2312 //---------------------------------------------------------
2313 //   resetAndRestart
2314 //---------------------------------------------------------
2315 
resetAndRestart()2316 void MuseScore::resetAndRestart()
2317       {
2318       int ret = QMessageBox::question(0, tr("Are you sure?"),
2319                   tr("This will reset all your preferences.\n"
2320                    "Custom palettes, custom shortcuts, and the list of recent scores will be deleted. "
2321                    "MuseScore will restart with its default settings.\n"
2322                    "Reverting will not remove any scores from your computer.\n"
2323                    "Are you sure you want to proceed?"),
2324                    QMessageBox::Yes|QMessageBox::No, QMessageBox::No);
2325       if (ret == QMessageBox::Yes ) {
2326              close();
2327              QStringList args("-F");
2328              QProcess::startDetached(qApp->arguments()[0], args);
2329              }
2330       }
2331 
2332 //---------------------------------------------------------
2333 //   aboutQt
2334 //---------------------------------------------------------
2335 
aboutQt()2336 void MuseScore::aboutQt()
2337       {
2338       QMessageBox::aboutQt(this, QString("About Qt"));
2339       }
2340 
2341 //---------------------------------------------------------
2342 //   aboutMusicXML
2343 //---------------------------------------------------------
2344 
aboutMusicXML()2345 void MuseScore::aboutMusicXML()
2346       {
2347       AboutMusicXMLBoxDialog ab;
2348       ab.show();
2349       ab.exec();
2350       }
2351 
2352 //---------------------------------------------------------
2353 //   selectScore
2354 //    "open recent"
2355 //---------------------------------------------------------
2356 
selectScore(QAction * action)2357 void MuseScore::selectScore(QAction* action)
2358       {
2359       QVariant actionData = action->data();
2360 
2361       if (!actionData.isValid())
2362             return;
2363 
2364       switch (actionData.type()) {
2365             case QVariant::String: {
2366                   if (actionData.toString() == "clear-recent") {
2367                         _recentScores.clear();
2368 
2369                         if (startcenter)
2370                               startcenter->updateRecentScores();
2371                         }
2372                   break;
2373                   }
2374             case QVariant::Map: {
2375                   QVariantMap pathMap = actionData.toMap();
2376 
2377                   openScore(pathMap.value("filePath").toString());
2378                   break;
2379                   }
2380             default:
2381                   return;
2382             }
2383       }
2384 
2385 //---------------------------------------------------------
2386 //   selectionChanged
2387 //---------------------------------------------------------
2388 
selectionChanged(SelState selectionState)2389 void MuseScore::selectionChanged(SelState selectionState)
2390       {
2391       bool enable = selectionState != SelState::NONE;
2392       getAction("cut")->setEnabled(enable);
2393       getAction("copy")->setEnabled(enable);
2394       getAction("select-similar-range")->setEnabled(selectionState == SelState::RANGE);
2395       if (pianorollEditor)
2396             pianorollEditor->changeSelection(selectionState);
2397       if (drumrollEditor)
2398             drumrollEditor->changeSelection(selectionState);
2399       if (timeline())
2400             timeline()->changeSelection(selectionState);
2401       if (_pianoTools && _pianoTools->isVisible()) {
2402             if (cs)
2403                   _pianoTools->changeSelection(cs->selection());
2404             else
2405                   _pianoTools->clearSelection();
2406             }
2407       if (_inspector)
2408             updateInspector();
2409       }
2410 
2411 //---------------------------------------------------------
2412 //   updatePaletteBeamMode
2413 //
2414 //   Updates the selected index of the Beam Properties
2415 //   palette to reflect the beam mode of the selected
2416 //   chord rest
2417 //---------------------------------------------------------
2418 
updatePaletteBeamMode()2419 void MuseScore::updatePaletteBeamMode()
2420       {
2421       if (paletteWorkspace && cs)
2422             paletteWorkspace->updateCellsState(cs->selection());
2423       }
2424 
2425 //---------------------------------------------------------
2426 //   updateInspector
2427 //---------------------------------------------------------
2428 
updateInspector()2429 void MuseScore::updateInspector()
2430       {
2431       // skip update if no inspector, or if inspector is hidden and there is a GUI
2432       // (important not to skip when running test scripts)
2433       if (_inspector && (_inspector->isVisible() || MScore::testMode || scriptTestMode))
2434             _inspector->update(cs);
2435       }
2436 
2437 //---------------------------------------------------------
2438 //   updateShadowNote
2439 //   update the shadow note in the current ScoreView
2440 //---------------------------------------------------------
2441 
updateShadowNote()2442 void MuseScore::updateShadowNote()
2443       {
2444       currentScoreView()->updateShadowNotes();
2445       }
2446 
2447 //---------------------------------------------------------
2448 //   appendScore
2449 //    append score to project list
2450 //---------------------------------------------------------
2451 
appendScore(MasterScore * score)2452 int MuseScore::appendScore(MasterScore* score)
2453       {
2454       int index = scoreList.size();
2455       for (int i = 0; i < scoreList.size(); ++i) {
2456             if ((!score->importedFilePath().isEmpty()
2457                  && scoreList[i]->importedFilePath() == score->importedFilePath())
2458                     || (scoreList[i]->fileInfo()->canonicalFilePath() == score->fileInfo()->canonicalFilePath() && score->fileInfo()->exists())) {
2459                   removeTab(i);
2460                   index = i;
2461                   break;
2462                   }
2463             }
2464       scoreList.insert(index, score);
2465       scoreWasShown[score] = false;
2466       tab1->insertTab(score);
2467       if (tab2)
2468             tab2->insertTab(score);
2469       return index;
2470       }
2471 
2472 //---------------------------------------------------------
2473 //   addRecentScore
2474 //---------------------------------------------------------
2475 
addRecentScore(Score * score)2476 void MuseScore::addRecentScore(Score* score)
2477       {
2478       QString path = score->importedFilePath(); // defined for scores imported from e.g. MIDI files
2479       addRecentScore(path);
2480       path = score->masterScore()->fileInfo()->absoluteFilePath();
2481       addRecentScore(path);
2482       if (startcenter)
2483             startcenter->updateRecentScores();
2484       }
2485 
addRecentScore(const QString & scorePath)2486 void MuseScore::addRecentScore(const QString& scorePath)
2487       {
2488       if (scorePath.isEmpty())
2489             return;
2490       QFileInfo fi(scorePath);
2491       QString absoluteFilePath = fi.absoluteFilePath();
2492       _recentScores.removeAll(absoluteFilePath);
2493       _recentScores.prepend(absoluteFilePath);
2494       if (_recentScores.size() > RECENT_LIST_SIZE)
2495             _recentScores.removeLast();
2496       }
2497 
2498 #if 0
2499 //---------------------------------------------------------
2500 //   updateTabNames
2501 //---------------------------------------------------------
2502 
2503 void MuseScore::updateTabNames()
2504       {
2505       for (int i = 0; i < tab1->count(); ++i) {
2506             ScoreView* view = tab1->view(i);
2507             if (view)
2508                   tab1->setTabText(i, view->score()->masterScore()->fileInfo()->completeBaseName());
2509             }
2510       if (tab2) {
2511             for (int i = 0; i < tab2->count(); ++i) {
2512                   ScoreView* view = tab2->view(i);
2513                   if (view)
2514                         tab2->setTabText(i, view->score()->masterScore()->fileInfo()->completeBaseName());
2515                   }
2516             }
2517       }
2518 #endif
2519 
2520 //---------------------------------------------------------
2521 //   loadScoreList
2522 //    read list of "Recent Scores"
2523 //---------------------------------------------------------
2524 
loadScoreList()2525 void MuseScore::loadScoreList()
2526       {
2527       if (useFactorySettings)
2528             return;
2529       QSettings s;
2530       for (int i = RECENT_LIST_SIZE-1; i >= 0; --i) {
2531             QString path = s.value(QString("recent-%1").arg(i),"").toString();
2532             if (!path.isEmpty() && QFileInfo::exists(path)) {
2533                   _recentScores.removeAll(path);
2534                   _recentScores.prepend(path);
2535                   }
2536             }
2537       }
2538 
2539 //---------------------------------------------------------
2540 //   openRecentMenu
2541 //---------------------------------------------------------
2542 
openRecentMenu()2543 void MuseScore::openRecentMenu()
2544       {
2545       openRecent->clear();
2546       bool one = false;
2547       for (const QFileInfo& fi : recentScores()) {
2548             QAction* action = openRecent->addAction(fi.fileName().replace("&", "&&"));  // show filename only
2549 
2550             QString filePath = fi.canonicalFilePath();
2551 
2552             QVariantMap actionData;
2553             actionData.insert("actionName", "open-recent");
2554             actionData.insert("filePath", filePath);
2555 
2556             action->setData(actionData);
2557             action->setToolTip(filePath);
2558             one = true;
2559             }
2560       if (one) {
2561             openRecent->addSeparator();
2562             QAction* action = openRecent->addAction(tr("Clear Recent Files"));
2563             action->setData("clear-recent");
2564             }
2565       }
2566 
2567 //---------------------------------------------------------
2568 //   reloadInstrumentTemplates
2569 //---------------------------------------------------------
2570 
reloadInstrumentTemplates()2571 void MuseScore::reloadInstrumentTemplates()
2572       {
2573       clearInstrumentTemplates();
2574       // load cascading instrument templates
2575       loadInstrumentTemplates(preferences.getString(PREF_APP_PATHS_INSTRUMENTLIST1));
2576       QString list2 = preferences.getString(PREF_APP_PATHS_INSTRUMENTLIST2);
2577       if (!list2.isEmpty())
2578             loadInstrumentTemplates(list2);
2579 
2580       // load instrument templates from extension
2581       QStringList extensionDir = Extension::getDirectoriesByType(Extension::instrumentsDir);
2582       QStringList filter("*.xml");
2583       for (QString s : extensionDir) {
2584             QDir extDir(s);
2585             extDir.setNameFilters(filter);
2586             auto instFiles = extDir.entryInfoList(QDir::Files | QDir::NoSymLinks | QDir::Readable);
2587             for (auto instFile : instFiles)
2588                   loadInstrumentTemplates(instFile.absoluteFilePath());
2589             }
2590 
2591       // load cascading score orders
2592       loadScoreOrders(preferences.getString(PREF_APP_PATHS_SCOREORDERLIST1));
2593       list2 = preferences.getString(PREF_APP_PATHS_SCOREORDERLIST2);
2594       if (!list2.isEmpty())
2595             loadScoreOrders(list2);
2596 
2597 
2598       MidiInstr::instrumentTemplatesChanged();
2599       if (importmidiPanel)
2600             importmidiPanel->instrumentTemplatesChanged();
2601       }
2602 
2603 //---------------------------------------------------------
2604 //   askMigrateScore
2605 //---------------------------------------------------------
2606 
askMigrateScore(Score * score)2607 void MuseScore::askMigrateScore(Score* score)
2608       {
2609       if (!preferences.getBool(PREF_MIGRATION_DO_NOT_ASK_ME_AGAIN) && score->mscVersion() < MSCVERSION) {
2610             ScoreMigrationDialog* migrationDialog = new ScoreMigrationDialog(mscore->getQmlUiEngine(), score);
2611             migrationDialog->show();
2612             }
2613       }
2614 
2615 //---------------------------------------------------------
2616 //   setCurrentView
2617 //---------------------------------------------------------
2618 
setCurrentScoreView(int idx)2619 void MuseScore::setCurrentScoreView(int idx)
2620       {
2621       tab1->blockSignals(ctab != tab1);
2622       setCurrentView(0, idx);
2623       tab1->blockSignals(false);
2624 
2625       if (tab2) {
2626             tab2->blockSignals(ctab != tab2);
2627             setCurrentView(1, idx);
2628             tab2->blockSignals(false);
2629             }
2630       }
2631 
setCurrentView(int tabIdx,int idx)2632 void MuseScore::setCurrentView(int tabIdx, int idx)
2633       {
2634       if (idx == -1)
2635             setCurrentScoreView(nullptr);
2636       else {
2637             ScoreTab* tab = tabIdx ? tab2 : tab1;
2638             if (tab)
2639                   tab->setCurrentIndex(idx);
2640             }
2641       }
2642 
2643 //---------------------------------------------------------
2644 //   setCurrentScoreView
2645 //---------------------------------------------------------
2646 
setCurrentScoreView(ScoreView * view)2647 void MuseScore::setCurrentScoreView(ScoreView* view)
2648       {
2649       cv = view;
2650       if (cv) {
2651             ctab = (tab2 && tab2->view() == view) ? tab2 : tab1;
2652             if (timeline())
2653                   timeline()->setScoreView(cv);
2654             if (cv->score() && (cs != cv->score())) {
2655                   // exit note entry mode
2656                   if (cv->noteEntryMode()) {
2657                         cv->cmd(getAction("escape"));
2658                         qApp->processEvents();
2659                         }
2660                   updateInputState(cv->score());
2661                   }
2662             cs = cv->score();
2663             cv->setFocusRect();
2664             if (!cv->wasShown) {
2665                   cv->wasShown = true;
2666                   cv->pageTop();
2667                   }
2668             }
2669       else
2670             cs = 0;
2671 
2672       updateWindowTitle(cs);
2673       setWindowModified(cs ? cs->dirty() : false);
2674 
2675       if (ScriptRecorder* rec = getScriptRecorder())
2676             rec->recordCurrentScoreChange();
2677 
2678       if (cs)
2679             cs->masterScore()->setPlaybackScore(_playPartOnly ? cs : cs->masterScore());
2680 
2681       // set midi import panel
2682       QString fileName = cs ? cs->importedFilePath() : "";
2683       midiPanelOnSwitchToFile(fileName);
2684 
2685       if (enableExperimental) {
2686             updateLayer();
2687             updatePlayMode();
2688             }
2689 
2690       if (seq)
2691             seq->setScoreView(cv);
2692       if (playPanel)
2693             playPanel->setScore(cs);
2694       if (synthControl)
2695             synthControl->setScore(cs);
2696       if (selectionWindow)
2697             selectionWindow->setScore(cs);
2698       if (mixer)
2699             mixer->setScore(cs);
2700 #ifdef OMR
2701       if (omrPanel) {
2702             if (cv && cv->omrView())
2703                   omrPanel->setOmrView(cv->omrView());
2704             else
2705                   omrPanel->setOmrView(0);
2706             }
2707 #endif
2708       if (!cs) {
2709             if (_navigator && _navigator->widget()) {
2710                   navigator()->setScoreView(cv);
2711                   }
2712             if (timeline()) {
2713                   timeline()->setScoreView(cv);
2714                   timeline()->setScore(0);
2715                   }
2716             if (_inspector)
2717                   _inspector->update(0);
2718             zoomBox->resetToDefaultLogicalZoom();
2719             viewModeCombo->setEnabled(false);
2720             if (_textTools) {
2721                   _textTools->hide();
2722                   if (textPalette)
2723                         textPalette->hide();
2724                   }
2725             if (_pianoTools)
2726                   _pianoTools->hide();
2727             if (_drumTools)
2728                   _drumTools->hide();
2729             changeState(STATE_DISABLED);
2730             ScoreAccessibility::instance()->clearAccessibilityInfo();
2731             return;
2732             }
2733 
2734       viewModeCombo->setEnabled(true);
2735       updateViewModeCombo();
2736 
2737       selectionChanged(cs->selection().state());
2738 
2739       _sstate = STATE_DISABLED; // defeat optimization
2740       changeState(cv->mscoreState());
2741 
2742       cv->setFocus(Qt::OtherFocusReason);
2743       setFocusProxy(cv);
2744 
2745       getAction("file-save")->setEnabled(cs->masterScore()->isSavable());
2746       getAction("show-invisible")->setChecked(cs->showInvisible());
2747       getAction("show-unprintable")->setChecked(cs->showUnprintable());
2748       getAction("show-frames")->setChecked(cs->showFrames());
2749       getAction("show-pageborders")->setChecked(cs->showPageborders());
2750       getAction("mark-irregular")->setChecked(cs->markIrregularMeasures());
2751       getAction("fotomode")->setChecked(cv->fotoMode());
2752       getAction("join-measures")->setEnabled(cs->masterScore()->excerpts().size() == 0);
2753       getAction("split-measure")->setEnabled(cs->masterScore()->excerpts().size() == 0);
2754       getAction("concert-pitch")->setChecked(cs->styleB(Sid::concertPitch));
2755       updateUndoRedo();
2756 
2757       setZoom(cv->zoomIndex(), cv->logicalZoomLevel());
2758       setPos(cs->inputPos());
2759       //showMessage(cs->filePath(), 2000);
2760       if (_navigator && _navigator->widget()) {
2761             navigator()->setScoreView(view);
2762             }
2763       if (timeline()) {
2764             timeline()->setScore(cs);
2765             timeline()->setScoreView(view);
2766             }
2767       ScoreAccessibility::instance()->updateAccessibilityInfo();
2768 
2769       MasterScore* master = cs->masterScore();
2770       if (!scoreWasShown[master]) {
2771             scoreWasShown[master] = true;
2772             askMigrateScore(master);
2773             }
2774       }
2775 
2776 //---------------------------------------------------------
2777 //   setCurrentScores
2778 //---------------------------------------------------------
2779 
setCurrentScores(Score * s1,Score * s2)2780 void MuseScore::setCurrentScores(Score* s1, Score* s2)
2781       {
2782       if (s1)
2783             tab1->setCurrentScore(s1);
2784       if (s2) {
2785             setSplitScreen(true);
2786             tab2->setCurrentScore(s2);
2787             }
2788       }
2789 
2790 //---------------------------------------------------------
2791 //   setSplitScreen
2792 //---------------------------------------------------------
2793 
setSplitScreen(bool val)2794 void MuseScore::setSplitScreen(bool val)
2795       {
2796       if (splitScreen() != val)
2797             splitWindow(_horizontalSplit);
2798       }
2799 
2800 //---------------------------------------------------------
2801 //   updateViewModeCombo
2802 //---------------------------------------------------------
2803 
updateViewModeCombo()2804 void MuseScore::updateViewModeCombo()
2805       {
2806       int idx;
2807       switch (cs->layoutMode()) {
2808             case LayoutMode::PAGE:
2809             case LayoutMode::FLOAT:
2810                   idx = 0;
2811                   break;
2812             case LayoutMode::LINE:
2813                   idx = 1;
2814                   break;
2815             case LayoutMode::SYSTEM:
2816                   idx = 2;
2817                   break;
2818             default:
2819                   idx = 0;
2820                   break;
2821             }
2822       viewModeCombo->setCurrentIndex(idx);
2823       }
2824 
2825 //---------------------------------------------------------
2826 //   showMessage
2827 //---------------------------------------------------------
2828 
showMessage(const QString & s,int timeout)2829 void MuseScore::showMessage(const QString& s, int timeout)
2830       {
2831       _statusBar->showMessage(s, timeout);
2832       }
2833 
2834 //---------------------------------------------------------
2835 //   midiPanel
2836 //---------------------------------------------------------
2837 
midiPanelOnSwitchToFile(const QString & file)2838 void MuseScore::midiPanelOnSwitchToFile(const QString &file)
2839       {
2840       bool isMidiFile = ImportMidiPanel::isMidiFile(file);
2841       if (isMidiFile) {
2842             importmidiPanel->setMidiFile(file);
2843             if (importmidiPanel->isPreferredVisible())
2844                   importmidiPanel->setVisible(true);
2845             }
2846       else
2847             importmidiPanel->setVisible(false);
2848       importmidiShowPanel->setVisible(!importmidiPanel->isPreferredVisible() && isMidiFile);
2849       }
2850 
2851 //---------------------------------------------------------
2852 //   midiPanelOnCloseFile
2853 //---------------------------------------------------------
2854 
midiPanelOnCloseFile(const QString & file)2855 void MuseScore::midiPanelOnCloseFile(const QString &file)
2856       {
2857       if (ImportMidiPanel::isMidiFile(file))
2858             importmidiPanel->excludeMidiFile(file);
2859       }
2860 
2861 //---------------------------------------------------------
2862 //   allowShowMidiPanel
2863 //---------------------------------------------------------
2864 
allowShowMidiPanel(const QString & file)2865 void MuseScore::allowShowMidiPanel(const QString &file)
2866       {
2867       if (ImportMidiPanel::isMidiFile(file))
2868             importmidiPanel->setPreferredVisible(true);
2869       }
2870 
2871 //---------------------------------------------------------
2872 //   setMidiReopenInProgress
2873 //---------------------------------------------------------
2874 
setMidiReopenInProgress(const QString & file)2875 void MuseScore::setMidiReopenInProgress(const QString &file)
2876       {
2877       if (ImportMidiPanel::isMidiFile(file))
2878             importmidiPanel->setReopenInProgress();
2879       }
2880 
2881 //---------------------------------------------------------
2882 //   showMidiImportPanel
2883 //---------------------------------------------------------
2884 
showMidiImportPanel()2885 void MuseScore::showMidiImportPanel()
2886       {
2887       importmidiPanel->setPreferredVisible(true);
2888       QString fileName = cs ? cs->importedFilePath() : "";
2889       if (ImportMidiPanel::isMidiFile(fileName))
2890             importmidiPanel->setVisible(true);
2891       importmidiShowPanel->hide();
2892       }
2893 
2894 //---------------------------------------------------------
2895 //   dragEnterEvent
2896 //---------------------------------------------------------
2897 
dragEnterEvent(QDragEnterEvent * event)2898 void MuseScore::dragEnterEvent(QDragEnterEvent* event)
2899       {
2900       const QMimeData* dta = event->mimeData();
2901       if (dta->hasUrls()) {
2902             QList<QUrl>ul = event->mimeData()->urls();
2903             for (const QUrl& u : ul) {
2904                   if (MScore::debugMode)
2905                         qDebug("drag Url: %s scheme <%s>", qPrintable(u.toString()), qPrintable(u.scheme()));
2906                   if (u.scheme() == "file") {
2907                         // QFileInfo fi(u.toLocalFile());
2908                         event->acceptProposedAction();
2909                         break;
2910                         }
2911                   }
2912             }
2913       }
2914 
2915 //---------------------------------------------------------
2916 //   dropEvent
2917 //---------------------------------------------------------
2918 
dropEvent(QDropEvent * event)2919 void MuseScore::dropEvent(QDropEvent* event)
2920       {
2921       const QMimeData* dta = event->mimeData();
2922       if (dta->hasUrls()) {
2923             int view = -1;
2924             foreach(const QUrl& u, event->mimeData()->urls()) {
2925                   if (u.scheme() == "file") {
2926                         QString file = u.toLocalFile();
2927                         MasterScore* score = readScore(file);
2928                         if (score) {
2929                               view = appendScore(score);
2930                               addRecentScore(score);
2931                               }
2932                         }
2933                   }
2934             if (view != -1) {
2935                   setCurrentScoreView(view);
2936                   writeSessionFile(false);
2937                   }
2938             event->acceptProposedAction();
2939             }
2940       }
2941 
2942 //---------------------------------------------------------
2943 //   changeEvent
2944 //---------------------------------------------------------
2945 
changeEvent(QEvent * e)2946 void MuseScore::changeEvent(QEvent *e)
2947       {
2948       QMainWindow::changeEvent(e);
2949       switch (e->type()) {
2950             case QEvent::LanguageChange:
2951                   retranslate();
2952                   break;
2953             default:
2954                   break;
2955             }
2956       }
2957 
2958 //---------------------------------------------------------
2959 //   showEvent
2960 //---------------------------------------------------------
2961 
showEvent(QShowEvent * showEvent)2962 void MuseScore::showEvent(QShowEvent* showEvent)
2963 {
2964       QMainWindow::showEvent(showEvent);
2965       emit musescoreWindowWasShown();
2966 }
2967 
2968 
2969 //---------------------------------------------------------
2970 //   showPageSettings
2971 //---------------------------------------------------------
2972 
showPageSettings()2973 void MuseScore::showPageSettings()
2974       {
2975       if (pageSettings == 0)
2976             pageSettings = new PageSettings();
2977       pageSettings->setScore(cs);
2978       pageSettings->show();
2979       pageSettings->raise();
2980       }
2981 
2982 //---------------------------------------------------------
2983 //   startDebugger
2984 //---------------------------------------------------------
2985 
startDebugger()2986 void MuseScore::startDebugger()
2987       {
2988       if (!cs)
2989             return;
2990       if (debugger == 0)
2991             debugger = new Debugger(this);
2992       debugger->updateList(cs);
2993       debugger->show();
2994       }
2995 
2996 //---------------------------------------------------------
2997 //   showElementContext
2998 //---------------------------------------------------------
2999 
showElementContext(Element * el)3000 void MuseScore::showElementContext(Element* el)
3001       {
3002       if (el == 0)
3003             return;
3004       startDebugger();
3005       debugger->setElement(el);
3006       }
3007 
3008 //---------------------------------------------------------
3009 //   reDisplayDockWidget
3010 //
3011 //   Helper function to ensure when un-docked widgets are
3012 //   re-displayed, they are also re-dockable
3013 //---------------------------------------------------------
3014 
reDisplayDockWidget(QDockWidget * widget,bool visible)3015 void MuseScore::reDisplayDockWidget(QDockWidget* widget, bool visible)
3016       {
3017       if (widget->isFloating() && !widget->isVisible()) {
3018             // Ensure the widget is re-dockable if it has been closed and re-opened when un-docked. This is a workaround for a known QT bug. See QTBUG-69922.
3019             widget->setFloating(false);
3020             widget->setFloating(true);
3021             }
3022       widget->setVisible(visible);
3023       }
3024 
3025 //---------------------------------------------------------
3026 //   createPlayPanel
3027 //---------------------------------------------------------
3028 
createPlayPanel()3029 void MuseScore::createPlayPanel()
3030       {
3031       if (!playPanel) {
3032             playPanel = new PlayPanel(this);
3033             connect(playPanel, SIGNAL(metronomeGainChanged(float)), seq, SLOT(setMetronomeGain(float)));
3034             connect(playPanel, SIGNAL(speedChanged(double)), seq, SLOT(setRelTempo(double)));
3035             connect(playPanel, SIGNAL(posChange(int)), seq, SLOT(seek(int)));
3036             connect(playPanel, SIGNAL(closed(bool)), playId, SLOT(setChecked(bool)));
3037             connect(synti, SIGNAL(gainChanged(float)), playPanel, SLOT(setGain(float)));
3038             playPanel->setSpeedIncrement(preferences.getInt(PREF_APP_PLAYBACK_SPEEDINCREMENT));
3039             playPanel->setGain(synti->gain());
3040             playPanel->setScore(cs);
3041             addDockWidget(Qt::RightDockWidgetArea, playPanel);
3042             playPanel->setVisible(false);
3043             playPanel->setFloating(false);
3044             }
3045       }
3046 
3047 //---------------------------------------------------------
3048 //   showPlayPanel
3049 //---------------------------------------------------------
3050 
showPlayPanel(bool visible)3051 void MuseScore::showPlayPanel(bool visible)
3052       {
3053       if (noSeq || !(seq && seq->isRunning()))
3054             return;
3055       if (playPanel == 0) {
3056             if (!visible)
3057                   return;
3058 
3059             createPlayPanel();
3060 
3061             // The play panel must be set visible before being set floating for positioning
3062             // and window geometry reasons.
3063             playPanel->setVisible(visible);
3064             playPanel->setFloating(false);
3065             }
3066       else
3067             reDisplayDockWidget(playPanel, visible);
3068       playId->setChecked(visible);
3069       }
3070 
3071 //---------------------------------------------------------
3072 //   cmdAppendMeasures
3073 //---------------------------------------------------------
3074 
cmdAppendMeasures()3075 void MuseScore::cmdAppendMeasures()
3076       {
3077       if (cs) {
3078             if (measuresDialog == 0)
3079                   measuresDialog = new MeasuresDialog;
3080             measuresDialog->show();
3081             }
3082       }
3083 
3084 //---------------------------------------------------------
3085 //   rebuildAudioDrivers
3086 //---------------------------------------------------------
3087 
restartAudioEngine()3088 void MuseScore::restartAudioEngine()
3089       {
3090       if (seq)
3091             seq->exit();
3092 
3093       if (seq) {
3094             Driver* driver = driverFactory(seq, "");
3095             if (driver) {
3096                   // Updating synthesizer's sample rate
3097                   if (seq->synti()) {
3098                         seq->synti()->setSampleRate(driver->sampleRate());
3099                         seq->synti()->init();
3100                         }
3101                   seq->setDriver(driver);
3102                   }
3103             if (!seq->init())
3104                   qDebug("sequencer init failed");
3105             }
3106       }
3107 
3108 //---------------------------------------------------------
3109 //   enableMidiIn
3110 //---------------------------------------------------------
3111 
enableMidiIn(const bool enable)3112 void MuseScore::enableMidiIn(const bool enable)
3113       {
3114       // This function must be called only when handling the "midi-on" action.
3115       Q_ASSERT(getAction("midi-on")->isChecked() == enable);
3116 
3117       const auto wasEnabled = isMidiInEnabled();
3118 
3119       preferences.setPreference(PREF_IO_MIDI_ENABLEINPUT, enable);
3120 
3121       if (enable && !wasEnabled)
3122             restartAudioEngine();
3123       }
3124 
3125 //---------------------------------------------------------
3126 //   isMidiInEnabled
3127 //---------------------------------------------------------
3128 
isMidiInEnabled() const3129 bool MuseScore::isMidiInEnabled() const
3130       {
3131       return preferences.getBool(PREF_IO_MIDI_ENABLEINPUT);
3132       }
3133 
3134 //---------------------------------------------------------
3135 //   processMidiRemote
3136 //    return if midi remote command detected
3137 //---------------------------------------------------------
3138 
processMidiRemote(MidiRemoteType type,int dta,int value)3139 bool MuseScore::processMidiRemote(MidiRemoteType type, int dta, int value)
3140       {
3141       if (!preferences.getBool(PREF_IO_MIDI_USEREMOTECONTROL))
3142             return false;
3143       if (!value) {
3144             // This was a "NoteOff" or "CtrlOff" event. Most MidiRemote actions should only
3145             // be triggered by an "On" event, so we need to check if this is one of those.
3146             if (!preferences.getBool(PREF_IO_MIDI_ADVANCEONRELEASE)
3147                     || type != preferences.midiRemote(RMIDI_REALTIME_ADVANCE).type
3148                     || dta != preferences.midiRemote(RMIDI_REALTIME_ADVANCE).data)
3149                   return false;
3150             }
3151       for (int i = 0; i < MIDI_REMOTES; ++i) {
3152             if (preferences.midiRemote(i).type == type && preferences.midiRemote(i).data == dta) {
3153                   if (cv == 0)
3154                         return false;
3155                   QAction* a = 0;
3156                   switch (i) {
3157                         case RMIDI_REWIND:      a = getAction("rewind"); break;
3158                         case RMIDI_TOGGLE_PLAY: a = getAction("play");  break;
3159                         case RMIDI_PLAY:
3160                               a = getAction("play");
3161                               if (a->isChecked())
3162                                     return true;
3163                               break;
3164                         case RMIDI_STOP:
3165                               a = getAction("play");
3166                               if (!a->isChecked())
3167                                     return true;
3168                               break;
3169                         case RMIDI_NOTE1:   a = getAction("pad-note-1");  break;
3170                         case RMIDI_NOTE2:   a = getAction("pad-note-2");  break;
3171                         case RMIDI_NOTE4:   a = getAction("pad-note-4");  break;
3172                         case RMIDI_NOTE8:   a = getAction("pad-note-8");  break;
3173                         case RMIDI_NOTE16:  a = getAction("pad-note-16");  break;
3174                         case RMIDI_NOTE32:  a = getAction("pad-note-32");  break;
3175                         case RMIDI_NOTE64:  a = getAction("pad-note-64");  break;
3176                         case RMIDI_REST:    a = getAction("rest");  break;
3177                         case RMIDI_DOT:     a = getAction("pad-dot");  break;
3178                         case RMIDI_DOTDOT:  a = getAction("pad-dotdot");  break;
3179                         case RMIDI_TIE:     a = getAction("tie");  break;
3180                         case RMIDI_UNDO:    a = getAction("undo"); break;
3181                         case RMIDI_NOTE_EDIT_MODE: a = getAction("note-input");  break;
3182                         case RMIDI_REALTIME_ADVANCE: a = getAction("realtime-advance");  break;
3183                         }
3184                   if (a)
3185                         a->trigger();
3186                   return true;
3187                   }
3188             }
3189       return false;
3190       }
3191 
3192 //---------------------------------------------------------
3193 //   midiNoteReceived
3194 //---------------------------------------------------------
3195 
midiNoteReceived(int channel,int pitch,int velo)3196 void MuseScore::midiNoteReceived(int channel, int pitch, int velo)
3197       {
3198       static const int THRESHOLD_DRUMS = 5; // iterations required before consecutive drum notes
3199                                      // are not considered part of a chord
3200       static int active = 0;
3201       static int iterDrums = 0;
3202       static int activeDrums = 0;
3203 
3204       if (!isMidiInEnabled())
3205             return;
3206 
3207 // qDebug("midiNoteReceived %d %d %d", channel, pitch, velo);
3208 
3209       if (_midiRecordId != -1) {
3210             preferences.updateMidiRemote(_midiRecordId, MIDI_REMOTE_TYPE_NOTEON, pitch);
3211             _midiRecordId = -1;
3212             if (preferenceDialog)
3213                   preferenceDialog->updateRemote();
3214             return;
3215             }
3216 
3217       if (processMidiRemote(MIDI_REMOTE_TYPE_NOTEON, pitch, velo)) {
3218             active = 0;
3219             return;
3220             }
3221 
3222       QWidget* w = QApplication::activeModalWidget();
3223       if (!cv || w) {
3224             active = 0;
3225             return;
3226             }
3227       if (velo) {
3228              /*
3229              * Since some drum modules do not overlap note on / off messages
3230              * we need to take a bit of a different tactic to allow chords
3231              * to be entered.
3232              *
3233              * Rather than decrement active for drums (midi on ch10),
3234              * we'll just assume that if read() has been called a couple
3235              * times in a row without a drum note, that this note is not
3236              * part of a cord.
3237              */
3238             if (channel == 0x09) {
3239                   if (iterDrums >= THRESHOLD_DRUMS)
3240                         activeDrums = 0;
3241                   iterDrums = 0;
3242                   cv->midiNoteReceived(pitch, activeDrums > 0, velo);
3243                   }
3244             else {
3245                   //qDebug("    midiNoteReceived %d active %d", pitch, active);
3246                   cv->midiNoteReceived(pitch, active > 0, velo);
3247                   ++active;
3248                   }
3249             }
3250       else {
3251             /*
3252             * Since a note may be assigned to a midi_remote,
3253             * don't decrease active below zero on noteoff.
3254             */
3255             if ((channel != 0x09) && (active > 0))
3256                   --active;
3257             if ((channel == 0x09) && (activeDrums > 0))
3258                   --activeDrums;
3259             cv->midiNoteReceived(pitch, false, velo);
3260             }
3261 
3262       if (_pianoTools && _pianoTools->isVisible()) {
3263             if (velo)
3264                   _pianoTools->pressPitch(pitch);
3265             else
3266                   _pianoTools->releasePitch(pitch);
3267             }
3268       }
3269 
3270 //---------------------------------------------------------
3271 //   midiCtrlReceived
3272 //---------------------------------------------------------
3273 
midiCtrlReceived(int controller,int value)3274 void MuseScore::midiCtrlReceived(int controller, int value)
3275       {
3276       if (!isMidiInEnabled())
3277             return;
3278       if (_midiRecordId != -1) {
3279             preferences.updateMidiRemote(_midiRecordId, MIDI_REMOTE_TYPE_CTRL, controller);
3280             _midiRecordId = -1;
3281             if (preferenceDialog)
3282                   preferenceDialog->updateRemote();
3283             return;
3284             }
3285       // when value is 0 (usually when a key is released ) nothing happens
3286       if (processMidiRemote(MIDI_REMOTE_TYPE_CTRL, controller, value))
3287             return;
3288       }
3289 
3290 //---------------------------------------------------------
3291 //   removeTab
3292 //---------------------------------------------------------
3293 
removeTab()3294 void MuseScore::removeTab()
3295       {
3296       int n = scoreList.indexOf(cs->masterScore());
3297       if (n == -1) {
3298             qDebug("removeTab: %p not found", cs);
3299             return;
3300             }
3301       removeTab(n);
3302       }
3303 
removeTab(int i)3304 void MuseScore::removeTab(int i)
3305       {
3306       MasterScore* score = scoreList.value(i);
3307 
3308       if (score == 0)
3309             return;
3310 
3311       QString tmpName = score->tmpName();
3312 
3313       if (!scriptTestMode && !converterMode && checkDirty(score))
3314             return;
3315       if (seq && seq->score() == score) {
3316             seq->stopWait();
3317             seq->setScoreView(0);
3318             }
3319 
3320       int idx1      = tab1->currentIndex();
3321       bool firstTab = tab1->view(idx1) == cv;
3322 
3323       midiPanelOnCloseFile(score->importedFilePath());
3324       scoreList.removeAt(i);
3325       scoreWasShown.remove(score);
3326 
3327       tab1->removeTab(i, /* noCurrentChangedSignals */ true);
3328       if (tab2)
3329             tab2->removeTab(i, /* noCurrentChangedSignals */ true);
3330 
3331       cs = 0;
3332       cv = 0;
3333       int n = scoreList.size();
3334       if (n == 0)
3335             setCurrentScoreView(nullptr);
3336       else
3337             setCurrentScoreView((firstTab ? tab1 : tab2)->view());
3338       writeSessionFile(false);
3339       if (!tmpName.isEmpty()) {
3340             QFile f(tmpName);
3341             f.remove();
3342             }
3343       delete score;
3344       // Shouldn't be necessary... but fix #21841
3345       update();
3346       }
3347 
3348 //---------------------------------------------------------
3349 //   runTestScripts
3350 //---------------------------------------------------------
3351 
runTestScripts(const QStringList & scriptFiles)3352 bool MuseScore::runTestScripts(const QStringList& scriptFiles)
3353       {
3354       setDefaultPalette();
3355       showInspector(true);
3356 
3357       ScriptContext ctx(this);
3358       bool allPassed = true;
3359       int passed = 0;
3360       int total = 0;
3361       for (const QString& scriptFile : scriptFiles) {
3362             // ensure no scores are open not to interfere with
3363             // the next script initialization process.
3364             while (!scores().empty()) {
3365                   closeScore(scores().back());
3366                   }
3367             QTextStream(stdout) << "Start test: " << scriptFile << endl;
3368             std::unique_ptr<Script> script = Script::fromFile(scriptFile);
3369             const bool pass = script->execute(ctx);
3370             QTextStream(stdout) << "Test " << scriptFile << (pass ? " PASS" : " FAIL") << endl;
3371             ++total;
3372             if (pass)
3373                   ++passed;
3374             else
3375                   allPassed = false;
3376             }
3377       QTextStream(stdout) << "Test scripts: total: " << total << ", passed: " << passed << endl;
3378       return allPassed;
3379       }
3380 
3381 //---------------------------------------------------------
3382 //   loadTranslation
3383 //---------------------------------------------------------
3384 
loadTranslation(QString filename,QString _localeName)3385 void loadTranslation(QString filename, QString _localeName)
3386       {
3387       QString userPrefix    = dataPath + "/locale/"+ filename +"_";
3388       QString defaultPrefix = mscoreGlobalShare + "locale/"+ filename +"_";
3389       QString userlp        = userPrefix + _localeName;
3390       QString defaultlp     = defaultPrefix + _localeName;
3391       QString lp            = defaultlp;
3392 
3393       QFileInfo userFi(userlp + ".qm");
3394       QFileInfo defaultFi(defaultlp + ".qm");
3395 
3396       if (!defaultFi.exists()) {     // try with a shorter locale name
3397             QString shortLocaleName = _localeName.left(_localeName.lastIndexOf("_"));
3398             QString shortDefaultlp = defaultPrefix + shortLocaleName;
3399             QFileInfo shortDefaultFi(shortDefaultlp + ".qm");
3400             if (shortDefaultFi.exists()) {
3401                   userlp    = userPrefix + shortLocaleName;
3402                   userFi    = QFileInfo(userlp + ".qm");
3403                   defaultFi = shortDefaultFi;
3404                   defaultlp = shortDefaultlp;
3405                   }
3406             }
3407 
3408       if (userFi.exists()) {
3409             if (userFi.lastModified() > defaultFi.lastModified())
3410                   lp = userlp;
3411 //            else
3412 //REVIEW                  QFile::remove(userlp + ".qm");
3413             }
3414 
3415       if (MScore::debugMode)
3416             qDebug("load translator <%s>", qPrintable(lp));
3417 
3418       QTranslator* translator = new QTranslator;
3419       bool success = translator->load(lp);
3420       if (success) {
3421             qApp->installTranslator(translator);
3422             translatorList.append(translator);
3423             }
3424       else {
3425             if (MScore::debugMode)
3426                   qDebug("load translator <%s> failed", qPrintable(lp));
3427             delete translator;
3428             }
3429       }
3430 
3431 //---------------------------------------------------------
3432 //   setLocale
3433 //---------------------------------------------------------
3434 
setMscoreLocale(QString _localeName)3435 void setMscoreLocale(QString _localeName)
3436       {
3437       for (QTranslator* t : translatorList) {
3438             qApp->removeTranslator(t);
3439             delete t;
3440             }
3441       translatorList.clear();
3442 
3443       if (MScore::debugMode)
3444             qDebug("configured localeName <%s>", qPrintable(_localeName));
3445       if (_localeName.toLower() == "system") {
3446             _localeName = QLocale::system().name();
3447             if (MScore::debugMode)
3448                   qDebug("real localeName <%s>", qPrintable(_localeName));
3449             if (_localeName == "en_AU") {
3450                   _localeName = "en_GB"; // otherwise Australia would fall back to US English
3451                   if (MScore::debugMode)
3452                         qDebug("modified localeName <%s>", qPrintable(_localeName));
3453                   }
3454             }
3455 
3456       // find the most recent translation file
3457       // try to replicate QTranslator.load algorithm in our particular case
3458       loadTranslation("mscore", _localeName);
3459       loadTranslation("instruments", _localeName);
3460       loadTranslation("tours", _localeName);
3461 
3462       QString resourceDir;
3463 #if defined(Q_OS_MAC) || defined(Q_OS_WIN)
3464       resourceDir = mscoreGlobalShare + "locale/";
3465 #else
3466       resourceDir = QLibraryInfo::location(QLibraryInfo::TranslationsPath);
3467 #endif
3468       QTranslator* qtTranslator = new QTranslator;
3469       if (MScore::debugMode)
3470             qDebug("load translator <qt_%s> from <%s>",
3471                qPrintable(_localeName), qPrintable(resourceDir));
3472 
3473       if (!qtTranslator->load(QLatin1String("qt_") + _localeName, resourceDir) && MScore::debugMode)
3474             qDebug("load translator <qt_%s> failed", qPrintable(_localeName));
3475       else {
3476             qApp->installTranslator(qtTranslator);
3477             translatorList.append(qtTranslator);
3478             }
3479       QLocale locale(_localeName);
3480       QLocale::setDefault(locale);
3481       qApp->setLayoutDirection(locale.textDirection());
3482       // initShortcuts();
3483       }
3484 
3485 //---------------------------------------------------------
3486 //   loadScores
3487 //    load scores for a new session
3488 //---------------------------------------------------------
3489 
loadScores(const QStringList & argv)3490 static void loadScores(const QStringList& argv)
3491       {
3492       int currentScoreView = 0;
3493       if (argv.isEmpty()) {
3494             if (startWithNewScore)
3495                   mscore->newFile();
3496             else {
3497                   switch (preferences.sessionStart()) {
3498                         case SessionStart::LAST:
3499                               return; // Already managed by `restoreSession`
3500                         case SessionStart::EMPTY:
3501                               break;
3502                         case SessionStart::NEW:
3503                               mscore->newFile();
3504                               break;
3505                         case SessionStart::SCORE:
3506                               {
3507                               QString startScore = preferences.getString(PREF_APP_STARTUP_STARTSCORE);
3508                               if (startScore == ":/data/My_First_Score.mscz") {
3509                                     startScore = ":/data/My_First_Score.mscx";
3510                                     preferences.setPreference(PREF_APP_STARTUP_STARTSCORE, startScore);
3511                                     }
3512 
3513                               MasterScore* score = mscore->readScore(startScore);
3514                               if (startScore.startsWith(":/") && score) {
3515                                     score->setStyle(MScore::defaultStyle());
3516                                     if (preferences.getBool(PREF_SCORE_HARMONY_PLAY_DISABLE_NEW)) {
3517                                           score->style().set(Sid::harmonyPlay, false);
3518                                           }
3519                                     score->style().checkChordList();
3520                                     score->styleChanged();
3521                                     score->setName(mscore->createDefaultName());
3522                                     // TODO score->setPageFormat(*MScore::defaultStyle().pageFormat());
3523                                     score->doLayout();
3524                                     score->setStartedEmpty(true);
3525                                     }
3526                               if (score == 0) {
3527                                     score = mscore->readScore(":/data/My_First_Score.mscx");
3528                                     if (score) {
3529                                           score->setStyle(MScore::defaultStyle());
3530                                           if (preferences.getBool(PREF_SCORE_HARMONY_PLAY_DISABLE_NEW)) {
3531                                                 score->style().set(Sid::harmonyPlay, false);
3532                                                 }
3533                                           score->style().checkChordList();
3534                                           score->styleChanged();
3535                                           score->setName(mscore->createDefaultName());
3536                                           // TODO score->setPageFormat(*MScore::defaultStyle().pageFormat());
3537                                           score->doLayout();
3538                                           score->setStartedEmpty(true);
3539                                           }
3540                                     }
3541                               if (score)
3542                                     currentScoreView = mscore->appendScore(score);
3543                               }
3544                               break;
3545                         }
3546                   }
3547             }
3548       else {
3549             for(const QString& name : argv) {
3550                   if (name.isEmpty())
3551                         continue;
3552 
3553                   Score* score = mscore->openScore(name);
3554 
3555                   if (score)
3556                         scoresOnCommandline = true;
3557                   }
3558             }
3559 
3560       if (mscore->noScore())
3561             currentScoreView = -1;
3562       mscore->setCurrentView(0, currentScoreView);
3563       mscore->setCurrentView(1, currentScoreView);
3564       }
3565 
3566 //---------------------------------------------------------
3567 //   doConvert
3568 //---------------------------------------------------------
3569 
3570 static bool doConvert(Score *cs, const QString& fn);
3571 
doConvert(Score * cs,const QJsonArray & outFiles,QString plugin)3572 static bool doConvert(Score* cs, const QJsonArray& outFiles, QString plugin)
3573       {
3574       LayoutMode layoutMode = cs->layoutMode();
3575       cs->setLayoutMode(LayoutMode::PAGE);
3576       if (cs->layoutMode() != layoutMode)
3577             cs->doLayout();
3578 
3579       if (!styleFile.isEmpty()) {
3580             QFile f(styleFile);
3581             if (f.open(QIODevice::ReadOnly)) {
3582                   fprintf(stderr, "\tusing style <%s>\n", qPrintable(styleFile));
3583                   cs->style().load(&f);
3584                   }
3585             }
3586       if (!plugin.isEmpty()) {
3587             if (mscore->loadPlugin(plugin)) {
3588                   fprintf(stderr, "\tusing plugin <%s>\n", qPrintable(plugin));
3589                   mscore->pluginTriggered(0);
3590                   }
3591             mscore->unloadPlugins();
3592             }
3593       for (const QJsonValue& outFile : outFiles) {
3594             if (outFile.isArray()) {
3595                   QJsonArray fns = outFile.toArray();
3596                   if (fns.size() != 2 || !fns[0].isString() || !fns[1].isString()) {
3597                         fprintf(stderr, "out element array length or type mismatch: %s\n",
3598                                 QJsonDocument(QJsonArray{outFile}).toJson().constData());
3599                         return false;
3600                         }
3601                   if (cs->excerpts().size() == 0)
3602                         // no parts, silently ignore
3603                         continue;
3604                   // convert parts
3605                   QString fnbeg = fns[0].toString();
3606                   QString fnend = fns[1].toString();
3607                   for (Excerpt* e : cs->excerpts()) {
3608                         Score* pScore = e->partScore();
3609                         QString partfn = fnbeg + mscore->saveFilename(pScore->title()) + fnend;
3610                         fprintf(stderr, "\tpart <%s>\n", qPrintable(partfn));
3611                         QFileInfo fi(partfn);
3612                         if (!mscore->saveAs(pScore, true, partfn, fi.suffix()))
3613                               return false;
3614                         }
3615                   }
3616             else if (!outFile.isString()) {
3617                   fprintf(stderr, "out element type mismatch: %s\n",
3618                           QJsonDocument(QJsonArray{outFile}).toJson().constData());
3619                   return false;
3620                   }
3621             else {
3622                   QString fn = outFile.toString();
3623                   fprintf(stderr, "\tto <%s>\n", qPrintable(fn));
3624                   if (!doConvert(cs, fn))
3625                         return false;
3626                   }
3627             }
3628       // don’t bother cleaning up layoutMode:
3629       // cs is destroyed immediately after return anyway
3630       return true;
3631       }
3632 
doConvert(Score * cs,const QString & fn)3633 static bool doConvert(Score *cs, const QString& fn)
3634       {
3635       if (fn.endsWith(".mscx")) {
3636             QFileInfo fi(fn);
3637             return cs->saveFile(fi);
3638             }
3639       else if (fn.endsWith(".mscz")) {
3640             QFileInfo fi(fn);
3641             return cs->saveCompressedFile(fi, false);
3642             }
3643       else if (fn.endsWith(".xml") || fn.endsWith(".musicxml"))
3644             return saveXml(cs, fn);
3645       else if (fn.endsWith(".mxl"))
3646             return saveMxl(cs, fn);
3647       else if (fn.endsWith(".mid") || fn.endsWith(".midi"))
3648             return mscore->saveMidi(cs, fn);
3649       else if (fn.endsWith(".pdf")) {
3650             if (!exportScoreParts)
3651                   return mscore->savePdf(cs, fn);
3652             if (cs->excerpts().size() == 0) {
3653                   auto excerpts = Excerpt::createAllExcerpt(cs->masterScore());
3654 
3655                   for (Excerpt* e : excerpts) {
3656                         Score* nscore = new Score(e->oscore());
3657                         e->setPartScore(nscore);
3658                         nscore->style().set(Sid::createMultiMeasureRests, true);
3659                         cs->startCmd();
3660                         cs->undo(new AddExcerpt(e));
3661                         Excerpt::createExcerpt(e);
3662                         cs->endCmd();
3663                         }
3664                   }
3665             QList<Score*> scores;
3666             scores.append(cs);
3667             for (Excerpt* e : cs->excerpts())
3668                   scores.append(e->partScore());
3669             return mscore->savePdf(scores, fn);
3670             }
3671       else if (fn.endsWith(".png")) {
3672             if (!exportScoreParts)
3673                   return mscore->savePng(cs, fn);
3674             if (cs->excerpts().size() == 0) {
3675                   auto excerpts = Excerpt::createAllExcerpt(cs->masterScore());
3676 
3677                   for (Excerpt* e: excerpts) {
3678                         Score* nscore = new Score(e->oscore());
3679                         e->setPartScore(nscore);
3680                         nscore->setExcerpt(e);
3681                         // nscore->setName(e->title()); // needed before AddExcerpt
3682                         nscore->style().set(Sid::createMultiMeasureRests, true);
3683                         cs->startCmd();
3684                         cs->undo(new AddExcerpt(e));
3685                         Excerpt::createExcerpt(e);
3686                         cs->endCmd();
3687                         }
3688                   }
3689             if (!mscore->savePng(cs, fn))
3690                   return false;
3691             int idx = 0;
3692             int padding = QString("%1").arg(cs->excerpts().size()).size();
3693             for (Excerpt* e: cs->excerpts()) {
3694                   QString suffix = QString("__excerpt__%1.png").arg(idx, padding, 10, QLatin1Char('0'));
3695                   QString excerptFn = fn.left(fn.size() - 4) + suffix;
3696                   if (!mscore->savePng(e->partScore(), excerptFn))
3697                         return false;
3698                   idx++;
3699                   }
3700             return true;
3701             }
3702       else if (fn.endsWith(".svg"))
3703             return mscore->saveSvg(cs, fn);
3704 #ifdef HAS_AUDIOFILE
3705       else if (fn.endsWith(".wav") || fn.endsWith(".ogg") || fn.endsWith(".flac"))
3706             return mscore->saveAudio(cs, fn);
3707 #endif
3708 #ifdef USE_LAME
3709       else if (fn.endsWith(".mp3"))
3710             return mscore->saveMp3(cs, fn);
3711 #endif
3712       else if (fn.endsWith(".spos"))
3713             return mscore->savePositions(cs, fn, true);
3714       else if (fn.endsWith(".mpos"))
3715             return mscore->savePositions(cs, fn, false);
3716       else if (fn.endsWith(".mlog"))
3717             return cs->sanityCheck(fn);
3718       else if (fn.endsWith(".metajson"))
3719             return mscore->saveMetadataJSON(cs, fn);
3720       // unknown file type
3721       fprintf(stderr, "Unknown file type!\n");
3722       return false;
3723       }
3724 
3725 //---------------------------------------------------------
3726 //   convert
3727 //---------------------------------------------------------
3728 
convert(const QString & inFile,const QJsonArray & outFiles,const QString & plugin="")3729 static bool convert(const QString& inFile, const QJsonArray& outFiles, const QString& plugin = "")
3730       {
3731       if (inFile.isEmpty() || (outFiles.isEmpty() && plugin.isEmpty())) {
3732             fprintf(stderr, "cannot convert <%s>: neither out nor plugin given\n", qPrintable(inFile));
3733             return false;
3734             }
3735       fprintf(stderr, "convert <%s>...\n", qPrintable(inFile));
3736       Score* score = mscore->openScore(inFile, false, false);
3737       if (!score)
3738             return false;
3739       mscore->setCurrentScore(score);
3740       bool success = doConvert(score, outFiles, plugin);
3741       fprintf(stderr, success ? "... success!\n" : "... failed!\n");
3742       if (plugin.isEmpty())
3743             delete score;
3744       else
3745             mscore->closeScore(score);
3746       mscore->setCurrentScore(nullptr);
3747       return success;
3748       }
3749 
convert(const QString & inFile,const QString & outFile)3750 static bool convert(const QString& inFile, const QString& outFile)
3751       {
3752       if (pluginMode)
3753             return convert(inFile, QJsonArray{ outFile }, pluginName);
3754       else
3755             return convert(inFile, QJsonArray{ outFile });
3756       }
3757 
3758 //---------------------------------------------------------
3759 //   doProcessJob
3760 //---------------------------------------------------------
3761 
doProcessJob(QString jsonFile)3762 static bool doProcessJob(QString jsonFile)
3763       {
3764       QFile f(jsonFile);
3765       if (!f.open(QIODevice::ReadOnly)) {
3766             fprintf(stderr, "cannon open json file <%s>\n", qPrintable(jsonFile));
3767             return false;
3768             }
3769       QJsonParseError pe;
3770       QJsonDocument doc = QJsonDocument::fromJson(f.readAll(), &pe);
3771       if (pe.error != QJsonParseError::NoError) {
3772             fprintf(stderr, "error reading json file <%s> at %d: %s\n",
3773                qPrintable(jsonFile), pe.offset, qPrintable(pe.errorString()));
3774             return false;
3775             }
3776       if (!doc.isArray()) {
3777             fprintf(stderr, "json file <%s> is not an array\n", qPrintable(jsonFile));
3778             return false;
3779             }
3780       QJsonArray a = doc.array();
3781       for (const auto i : a) {
3782             QString inFile;
3783             QJsonArray outFiles;
3784             QString plugin;
3785             if (!i.isObject()) {
3786                   fprintf(stderr, "array value is not an object\n");
3787                   return false;
3788                   }
3789             QJsonObject obj = i.toObject();
3790             for (const auto& key : obj.keys()) {
3791                   if (key == "in")
3792                         inFile = obj.value(key).toString();
3793                   else if (key == "out") {
3794                         if (obj.value(key).isArray())
3795                               outFiles = obj.value(key).toArray();
3796                         else
3797                               outFiles.push_back(obj.value(key));
3798                         }
3799                   else if (key == "plugin")
3800                         plugin = obj.value(key).toString();
3801                   else {
3802                         fprintf(stderr, "unknown key <%s>\n", qPrintable(key));
3803                         return false;
3804                         }
3805                   }
3806             if (!convert(inFile, outFiles, plugin))
3807                   return false;
3808             }
3809       return true;
3810       }
3811 
3812 //---------------------------------------------------------
3813 //   processNonGui
3814 //---------------------------------------------------------
3815 
processNonGui(const QStringList & argv)3816 static bool processNonGui(const QStringList& argv)
3817       {
3818       if (cliSaveOnline)
3819             return mscore->saveOnline(argv);
3820       if (exportScoreMedia)
3821             return mscore->exportAllMediaFiles(argv[0], highlightConfigPath);
3822       if (exportScoreMeta)
3823             return mscore->exportScoreMetadata(argv[0]);
3824       else if (exportScoreMp3)
3825             return mscore->exportMp3AsJSON(argv[0]);
3826       else if (saveScoreParts)
3827             return mscore->saveScoreParts(argv[0]);
3828       else if (exportScorePartsPdf)
3829             return mscore->exportPartsPdfsToJSON(argv[0]);
3830       else if (exportTransposedScore)
3831             return mscore->exportTransposedScoreToJSON(argv[0], transposeExportOptions);
3832       else if (needUpdateSource)
3833             return mscore->updateSource(argv[0] /* scorePath */, argv[1] /* newSource */);
3834 
3835       if (pluginMode && !converterMode) {
3836             loadScores(argv);
3837             QString pn(pluginName);
3838             bool res = false;
3839             if (mscore->loadPlugin(pn)){
3840                   Score* cs = mscore->currentScore();
3841                   if (!styleFile.isEmpty()) {
3842                         QFile f(styleFile);
3843                         if (f.open(QIODevice::ReadOnly))
3844                               cs->style().load(&f);
3845                         }
3846                   LayoutMode layoutMode = cs->layoutMode();
3847                   if (layoutMode != LayoutMode::PAGE) {
3848                         cs->setLayoutMode(LayoutMode::PAGE);
3849                         cs->doLayout();
3850                         }
3851                   mscore->pluginTriggered(0);
3852                   if (layoutMode != cs->layoutMode()) {
3853                         cs->setLayoutMode(layoutMode);
3854                         cs->doLayout();
3855                         }
3856                   res = true;
3857                   }
3858             return res;
3859             }
3860 
3861       if (converterMode) {
3862             if (processJob)
3863                   return doProcessJob(jsonFileName);
3864             else
3865                   return convert(argv[0], outFileName);
3866             }
3867 
3868       if (!extensionName.isEmpty()) {
3869             QFileInfo fi(extensionName);
3870             QString suffix = fi.suffix().toLower();
3871             if (suffix == "muxt")
3872                   return mscore->importExtension(extensionName);
3873             else {
3874                   fprintf(stderr, "cannot install extension: <%s>\n", qPrintable(extensionName));
3875                   return false;
3876                   }
3877             }
3878 
3879       if (rawDiffMode || diffMode) {
3880             if (argv.size() != 2)
3881                   return false;
3882             MasterScore* s1 = mscore->readScore(argv[0]);
3883             MasterScore* s2 = mscore->readScore(argv[1]);
3884             if (!s1 || !s2)
3885                   return false;
3886             ScoreDiff diff(s1, s2, /* textDiffOnly */ !diffMode);
3887 
3888             if (rawDiffMode)
3889                   QTextStream(stdout) << diff.rawDiff() << endl;
3890             if (diffMode)
3891                   QTextStream(stdout) << diff.userDiff() << endl;
3892 
3893             delete s1;
3894             delete s2;
3895             }
3896 
3897       if (scriptTestMode)
3898             return mscore->runTestScripts(argv);
3899 
3900       return true;
3901       }
3902 
3903 //---------------------------------------------------------
3904 //   Message handler
3905 //---------------------------------------------------------
3906 
3907 #if defined(QT_DEBUG) && defined(Q_OS_WIN)
mscoreMessageHandler(QtMsgType type,const QMessageLogContext & context,const QString & msg)3908 static void mscoreMessageHandler(QtMsgType type, const QMessageLogContext &context,const QString &msg)
3909      {
3910      QTextStream err(stderr);
3911      QByteArray localMsg = msg.toLocal8Bit();
3912 
3913      switch (type) {
3914      case QtInfoMsg:
3915          err << "Info: "     << localMsg.constData() << " ("  << context.file << ":" << context.line << ", " << context.function << ")" << endl;
3916          break;
3917      case QtDebugMsg:
3918          err << "Debug: "    << localMsg.constData() << " ("  << context.file << ":" << context.line << ", " << context.function << ")" << endl;
3919          break;
3920      case QtWarningMsg:
3921          err << "Warning: "  << localMsg.constData() << " ("  << context.file << ":" << context.line << ", " << context.function << ")" << endl;
3922          break;
3923      case QtCriticalMsg: // same as QtSystemMsg
3924          err << "Critical: " << localMsg.constData() << " ("  << context.file << ":" << context.line << ", " << context.function << ")" << endl;
3925          break;
3926      case QtFatalMsg: // set your breakpoint here, if you want to catch the abort
3927          err << "Fatal: "    << localMsg.constData() << " ("  << context.file << ":" << context.line << ", " << context.function << ")" << endl;
3928          abort();
3929          }
3930      }
3931 #endif
3932 
3933 //---------------------------------------------------------
3934 //   synthesizerFactory
3935 //    create and initialize the master synthesizer
3936 //---------------------------------------------------------
3937 
synthesizerFactory()3938 MasterSynthesizer* synthesizerFactory()
3939       {
3940       MasterSynthesizer* ms = new MasterSynthesizer();
3941 
3942       FluidS::Fluid* fluid = new FluidS::Fluid();
3943       ms->registerSynthesizer(fluid);
3944 
3945 #ifdef AEOLUS
3946       ms->registerSynthesizer(::createAeolus());
3947 #endif
3948 #ifdef ZERBERUS
3949       ms->registerSynthesizer(createZerberus());
3950 #endif
3951       ms->registerEffect(0, new NoEffect);
3952 
3953 #ifdef ZITA_REVERB
3954       ms->registerEffect(0, new ZitaReverb);
3955 #endif
3956 
3957       ms->registerEffect(0, new Compressor);
3958       // ms->registerEffect(0, new Freeverb);
3959       ms->registerEffect(1, new NoEffect);
3960 
3961 #ifdef ZITA_REVERB
3962       ms->registerEffect(1, new ZitaReverb);
3963 #endif
3964 
3965       ms->registerEffect(1, new Compressor);
3966       // ms->registerEffect(1, new Freeverb);
3967       ms->setEffect(0, 1);
3968       ms->setEffect(1, 0);
3969       return ms;
3970       }
3971 
3972 //---------------------------------------------------------
3973 //   unstable
3974 //---------------------------------------------------------
3975 
unstable()3976 bool MuseScore::unstable()
3977       {
3978 #ifdef MSCORE_UNSTABLE
3979       return true;
3980 #else
3981       return false;
3982 #endif
3983       }
3984 
3985 //---------------------------------------------------------
3986 //   MuseScoreApplication::event (mac only)
3987 //---------------------------------------------------------
3988 
event(QEvent * event)3989 bool MuseScoreApplication::event(QEvent* event)
3990       {
3991       switch(event->type()) {
3992             case QEvent::FileOpen:
3993                   // store names of files requested to be loaded by OS X to be handled later
3994                   // this event is generated when a file is dragged onto the MuseScore icon
3995                   // in the dock and MuseScore is not running yet
3996                   paths.append(static_cast<QFileOpenEvent *>(event)->file());
3997                   return true;
3998             default:
3999                   return QtSingleApplication::event(event);
4000             }
4001       }
4002 
4003 //---------------------------------------------------------
4004 //   focusScoreView
4005 //---------------------------------------------------------
4006 
focusScoreView()4007 void MuseScore::focusScoreView()
4008       {
4009       if (currentScoreView()) {
4010             currentScoreView()->setFocus();
4011             }
4012       else
4013             mscore->setFocus();
4014       }
4015 
4016 //---------------------------------------------------------
4017 //   eventFilter
4018 //---------------------------------------------------------
4019 
eventFilter(QObject * obj,QEvent * event)4020 bool MuseScore::eventFilter(QObject *obj, QEvent *event)
4021       {
4022       switch(event->type()) {
4023 #ifdef Q_OS_MAC
4024             case QEvent::FileOpen:
4025                   // open files requested to be loaded by OS X
4026                   // this event is generated when a file is dragged onto the MuseScore icon
4027                   // in the dock when MuseScore is already running
4028                   scoresOnCommandline = true;
4029                   handleMessage(static_cast<QFileOpenEvent *>(event)->file());
4030                   return true;
4031 #endif
4032             case QEvent::MouseMove: {
4033                   QMouseEvent* me = static_cast<QMouseEvent*>(event);
4034                   globalX = me->globalX();
4035                   globalY = me->globalY();
4036                   return QMainWindow::eventFilter(obj, event);
4037                   }
4038             case QEvent::StatusTip:
4039                   return true; // prevent updates to the status bar
4040             case QEvent::KeyPress:
4041                   {
4042                   QKeyEvent* e = static_cast<QKeyEvent*>(event);
4043                   if(obj->isWidgetType() && e->key() == Qt::Key_Escape && e->modifiers() == Qt::NoModifier) {
4044                         // Close the search dialog when Escape is pressed:
4045                         if(_searchDialog != 0)
4046                               endSearch();
4047                         if (isActiveWindow()) {
4048                               obj->event(e);
4049                               focusScoreView();
4050                               return true;
4051                               }
4052 
4053                         QWidget* w = static_cast<QWidget*>(obj);
4054                         if (inspector()->isAncestorOf(w) ||
4055                            (selectionWindow && selectionWindow->isAncestorOf(w))) {
4056                               activateWindow();
4057                               focusScoreView();
4058                               return true;
4059                               }
4060                         }
4061                   break;
4062                   }
4063             case QEvent::ShortcutOverride:
4064                   if (qobject_cast<QMenu*>(obj)) {
4065                         // Disable one-letter shortcuts while in menu
4066                         // to prevent blocking menu mnemonics
4067                         QKeyEvent* ke = static_cast<QKeyEvent*>(event);
4068                         const QString evtText = ke->text();
4069                         const bool letterOrNumber = !ke->modifiers() && evtText.size() == 1 && evtText.at(0).isLetterOrNumber();
4070 
4071                         if (letterOrNumber) {
4072                               ke->accept();
4073                               return true;
4074                               }
4075                         }
4076                   break;
4077             default:
4078                   return QMainWindow::eventFilter(obj, event);
4079             }
4080       return QMainWindow::eventFilter(obj, event);
4081       }
4082 
4083 //---------------------------------------------------------
4084 //   hasToCheckForUpdate
4085 //---------------------------------------------------------
4086 
hasToCheckForUpdate()4087 bool MuseScore::hasToCheckForUpdate()
4088       {
4089       if (ucheck)
4090             return ucheck->hasToCheck();
4091       else
4092             return false;
4093       }
4094 
4095 //---------------------------------------------------------
4096 //   hasToCheckForExtensionsUpdate
4097 //---------------------------------------------------------
4098 
hasToCheckForExtensionsUpdate()4099 bool MuseScore::hasToCheckForExtensionsUpdate()
4100       {
4101       return packUChecker ? packUChecker->hasToCheck() : false;
4102       }
4103 
4104 //---------------------------------------------------------
4105 //   checkForUpdatesNoUI
4106 //         Doesn't show any messages if software is up to date
4107 //---------------------------------------------------------
4108 
checkForUpdatesNoUI()4109 void MuseScore::checkForUpdatesNoUI()
4110       {
4111       if (autoUpdater)
4112             autoUpdater->checkUpdates();
4113       else if (ucheck)
4114             ucheck->check(version(), false);
4115       }
4116 
4117 //---------------------------------------------------------
4118 //   checkForUpdatesUI
4119 //          Show message like "Software is up to date" if software is up to date
4120 //---------------------------------------------------------
checkForUpdatesUI()4121 void MuseScore::checkForUpdatesUI()
4122       {
4123       if (autoUpdater)
4124             autoUpdater->checkForUpdatesNow();
4125       else if (ucheck)
4126             ucheck->check(version(), true);
4127       }
4128 
4129 //---------------------------------------------------------
4130 //   checkForExtensionsUpdate
4131 //---------------------------------------------------------
4132 
checkForExtensionsUpdate()4133 void MuseScore::checkForExtensionsUpdate()
4134       {
4135       if (packUChecker)
4136             packUChecker->check();
4137       }
4138 
4139 //---------------------------------------------------------
4140 //   readLanguages
4141 //---------------------------------------------------------
4142 
readLanguages(const QString & path)4143 bool MuseScore::readLanguages(const QString& path)
4144       {
4145       //: The default language of the operating system. NOT a music system.
4146       _languages.append(LanguageItem("system", tr("System")));
4147       QFile qf(path);
4148       if (qf.exists()) {
4149             QDomDocument doc;
4150             int line, column;
4151             QString err;
4152             if (!doc.setContent(&qf, false, &err, &line, &column)) {
4153                   QString error;
4154                   error = QString::asprintf(qPrintable(tr("Error reading language file %s at line %d column %d: %s\n")),
4155                                             qPrintable(qf.fileName()), line, column, qPrintable(err));
4156                   QMessageBox::warning(0,
4157                      QWidget::tr("Load Languages Failed:"),
4158                      error,
4159                      QString(), QWidget::tr("Quit"), QString(), 0, 1);
4160                   return false;
4161                   }
4162 
4163             for (QDomElement e = doc.documentElement(); !e.isNull(); e = e.nextSiblingElement()) {
4164                   if(e.tagName() == "languages") {
4165                         for (e = e.firstChildElement(); !e.isNull(); e = e.nextSiblingElement()) {
4166                               if (e.tagName() == "language") {
4167                                     QString code = e.attribute(QString("code"));
4168                                     QString name = e.attribute(QString("name"));
4169                                     QString handbook = e.attribute(QString("handbook"));
4170                                     _languages.append(LanguageItem(code, name, handbook));
4171                                     }
4172                               }
4173                         }
4174                   }
4175             return true;
4176             }
4177       return false;
4178       }
4179 
4180 //---------------------------------------------------------
4181 //   clipboardChanged
4182 //---------------------------------------------------------
4183 
clipboardChanged()4184 void MuseScore::clipboardChanged()
4185       {
4186 #if 0
4187       const QMimeData* ms = QApplication::clipboard()->mimeData();
4188       QStringList formats = ms->formats();
4189 
4190       bool flag = ms->hasFormat(mimeSymbolFormat)
4191             ||    ms->hasFormat(mimeStaffListFormat)
4192             ||    ms->hasFormat(mimeSymbolListFormat)
4193             ||    ms->hasText();
4194       // TODO: depends on selection state
4195 #endif
4196 
4197       bool flag = true;
4198       getAction("paste")->setEnabled(flag);
4199       getAction("swap")->setEnabled(flag);
4200       }
4201 
4202 //---------------------------------------------------------
4203 //   inputMethodAnchorRectangleChanged
4204 //---------------------------------------------------------
4205 
inputMethodAnchorRectangleChanged()4206 void MuseScore::inputMethodAnchorRectangleChanged()
4207       {
4208 //      QRectF r =  QGuiApplication::inputMethod()->anchorRectangle();
4209 //      qDebug("inputMethod AnchorRectangle Changed: (%f, %f) + (%f, %f)", r.x(), r.y(), r.width(), r.height());
4210       }
4211 
4212 //---------------------------------------------------------
4213 //   inputMethodAnimatingChanged
4214 //---------------------------------------------------------
4215 
inputMethodAnimatingChanged()4216 void MuseScore::inputMethodAnimatingChanged()
4217       {
4218 //      qDebug("inputMethod Animating Changed: %d", QGuiApplication::inputMethod()->isAnimating());
4219       }
4220 
4221 //---------------------------------------------------------
4222 //   inputMethodCursorRectangleChanged
4223 //---------------------------------------------------------
4224 
inputMethodCursorRectangleChanged()4225 void MuseScore::inputMethodCursorRectangleChanged()
4226       {
4227 //      QRectF r =  QGuiApplication::inputMethod()->cursorRectangle();
4228 //      qDebug("inputMethod CursorRectangle Changed: (%f, %f) + (%f, %f)", r.x(), r.y(), r.width(), r.height());
4229       }
4230 
4231 //---------------------------------------------------------
4232 //   inputMethodInputDirectionChanged
4233 //---------------------------------------------------------
4234 
inputMethodInputDirectionChanged(Qt::LayoutDirection)4235 void MuseScore::inputMethodInputDirectionChanged(Qt::LayoutDirection /*newDirection*/)
4236       {
4237 //      qDebug("inputMethod InputDirection Changed: (QLocale::LayoutDirection enum) #%d", newDirection);
4238       }
4239 
4240 //---------------------------------------------------------
4241 //   inputMethodInputItemClipRectangleChanged
4242 //---------------------------------------------------------
4243 
inputMethodInputItemClipRectangleChanged()4244 void MuseScore::inputMethodInputItemClipRectangleChanged()
4245       {
4246 //      QRectF r =  QGuiApplication::inputMethod()->inputItemClipRectangle();
4247 //      qDebug("inputMethod InputItemClipRectangle Changed: (%f, %f) + (%f, %f)", r.x(), r.y(), r.width(), r.height());
4248       }
4249 
4250 //---------------------------------------------------------
4251 //   inputMethodKeyboardRectangleChanged
4252 //---------------------------------------------------------
4253 
inputMethodKeyboardRectangleChanged()4254 void MuseScore::inputMethodKeyboardRectangleChanged()
4255       {
4256 //      QRectF r =  QGuiApplication::inputMethod()->keyboardRectangle();
4257 //      qDebug("inputMethod KeyboardRectangle Changed: (%f, %f) + (%f, %f)", r.x(), r.y(), r.width(), r.height());
4258       }
4259 
4260 //---------------------------------------------------------
4261 //   inputMethodLocaleChanged
4262 //---------------------------------------------------------
4263 
inputMethodLocaleChanged()4264 void MuseScore::inputMethodLocaleChanged()
4265       {
4266 //      qDebug("inputMethod Locale Changed: (QLocale::Script enum) #%d.", QGuiApplication::inputMethod()->locale().script());
4267       }
4268 
4269 //---------------------------------------------------------
4270 //   inputMethodVisibleChanged
4271 //---------------------------------------------------------
4272 
inputMethodVisibleChanged()4273 void MuseScore::inputMethodVisibleChanged()
4274       {
4275 //      qDebug("inputMethodVisibleChanged: %d", QGuiApplication::inputMethod()->isVisible());
4276       }
4277 
4278 //---------------------------------------------------------
4279 //   showModeText
4280 //---------------------------------------------------------
4281 
showModeText(const QString & s,bool informScreenReader)4282 void MuseScore::showModeText(const QString& s, bool informScreenReader)
4283       {
4284       if (s == _modeText->text())
4285             return;
4286       if (informScreenReader && cs)
4287             cs->setAccessibleMessage(s);
4288       _modeText->setText(s);
4289       }
4290 
4291 //---------------------------------------------------------
4292 //   changeState
4293 //---------------------------------------------------------
4294 
changeState(ScoreState val)4295 void MuseScore::changeState(ScoreState val)
4296       {
4297       if (MScore::debugMode)
4298             qDebug("MuseScore::changeState: %s", stateName(val));
4299 
4300       // disallow change to edit modes if currently in play mode
4301       if (_sstate == STATE_PLAY && (val == STATE_EDIT || val & STATE_ALLTEXTUAL_EDIT || val & STATE_NOTE_ENTRY))
4302             return;
4303 
4304       static const char* stdNames[] = {
4305             "note-longa", "note-breve", "pad-note-1", "pad-note-2", "pad-note-4",
4306             "pad-note-8", "pad-note-16", "pad-note-32", "pad-note-64", "pad-note-128",
4307             "pad-note-256","pad-note-512","pad-note-1024","pad-rest", "rest"};
4308       static const char* tabNames[] = {
4309             "note-longa-TAB", "note-breve-TAB", "pad-note-1-TAB", "pad-note-2-TAB", "pad-note-4-TAB",
4310             "pad-note-8-TAB", "pad-note-16-TAB", "pad-note-32-TAB", "pad-note-64-TAB", "pad-note-128-TAB",
4311             "pad-note-256-TAB", "pad-note-512-TAB", "pad-note-1024-TAB", "pad-rest-TAB", "rest-TAB"};
4312       bool intoTAB = (_sstate != STATE_NOTE_ENTRY_STAFF_TAB) && (val == STATE_NOTE_ENTRY_STAFF_TAB);
4313       bool fromTAB = (_sstate == STATE_NOTE_ENTRY_STAFF_TAB) && (val != STATE_NOTE_ENTRY_STAFF_TAB);
4314       // if activating TAB note entry, swap "pad-note-...-TAB" shorctuts into "pad-note-..." actions
4315       if (intoTAB) {
4316             for (unsigned i = 0; i < sizeof(stdNames)/sizeof(char*); ++i) {
4317                   QAction* act = getAction(stdNames[i]);
4318                   const Shortcut* srt = Shortcut::getShortcut(tabNames[i]);
4319                   act->setShortcuts(srt->keys());
4320                   }
4321             }
4322       // if de-ativating TAB note entry, restore shortcuts for "pad-note-..." actions
4323       else if (fromTAB) {
4324             for (unsigned i = 0; i < sizeof(stdNames)/sizeof(char*); ++i) {
4325                   QAction* act = getAction(stdNames[i]);
4326                   const Shortcut* srt = Shortcut::getShortcut(stdNames[i]);
4327                   act->setShortcuts(srt->keys());
4328                   }
4329             }
4330 
4331       bool enable = (val != STATE_DISABLED) && (val != STATE_LOCK);
4332 
4333       for (const Shortcut* s : Shortcut::shortcuts()) {
4334             QAction* a = s->action();
4335             if (!a)
4336                   continue;
4337             if (enable && (s->key() == "undo"))
4338                   a->setEnabled((s->state() & val) && (cs ? cs->undoStack()->canUndo() : false));
4339             else if (enable && (s->key() == "redo"))
4340                   a->setEnabled((s->state() & val) && (cs ? cs->undoStack()->canRedo() : false));
4341             else if (enable && (s->key() == "cut"))
4342                   a->setEnabled(cs && cs->selection().state() != SelState::NONE);
4343             else if (enable && (s->key() == "copy"))
4344                   a->setEnabled(cs && (cs->selection().state() != SelState::NONE || val == STATE_FOTO));
4345             else if (enable && (s->key() == "select-similar-range"))
4346                   a->setEnabled(cs && cs->selection().state() == SelState::RANGE);
4347             else if (enable && (s->key() == "synth-control" || s->key() == "toggle-mixer")) {
4348                   Driver* driver = seq ? seq->driver() : 0;
4349                   // a->setEnabled(driver && driver->getSynth());
4350                   if (MScore::debugMode)
4351                         qDebug("disable synth control");
4352                   a->setEnabled(driver);
4353                   }
4354             else {
4355                   a->setEnabled(s->state() & val);
4356                   }
4357             }
4358 
4359       if (getAction("join-measures")->isEnabled())
4360             getAction("join-measures")->setEnabled(cs && cs->masterScore()->excerpts().size() == 0);
4361       if (getAction("split-measure")->isEnabled())
4362             getAction("split-measure")->setEnabled(cs && cs->masterScore()->excerpts().size() == 0);
4363 
4364       // disabling top level menu entries does not
4365       // work for MAC
4366 
4367       QList<QObject*> ol = menuBar()->children();
4368       foreach(QObject* o, ol) {
4369             QMenu* menu = qobject_cast<QMenu*>(o);
4370             if (!menu)
4371                   continue;
4372             QString s(menu->objectName());
4373             if (s == "File" || s == "Help" || s == "Edit" || s == "Plugins" || s == "View")
4374                   continue;
4375             menu->setEnabled(enable);
4376             }
4377 
4378       menuWorkspaces->setEnabled(enable);
4379 
4380       transportTools->setEnabled(enable && !noSeq && seq && seq->isRunning());
4381       cpitchTools->setEnabled(enable);
4382       zoomBox->setEnabled(enable);
4383       entryTools->setEnabled(enable);
4384 
4385       if (_sstate == STATE_FOTO)
4386             updateInspector();
4387 
4388       //always hide drumtools to support hiding on switching docs, switching node entry mode off, etc.
4389       //it will be shown later if (_sstate == STATE_NOTE_ENTRY_STAFF_DRUM)
4390       showDrumTools(0, 0);
4391 
4392       switch (val) {
4393             case STATE_DISABLED:
4394                   showModeText(tr("No score"));
4395                   if (debugger)
4396                         debugger->hide();
4397                   showPianoKeyboard(false);
4398                   break;
4399             case STATE_NORMAL:
4400                   showModeText(tr("Normal mode"));;
4401                   break;
4402             case STATE_NOTE_ENTRY:
4403                   if (cv && !cv->noteEntryMode())
4404                         cv->cmd("note-input");
4405                   // fall through
4406             case STATE_NOTE_ENTRY_STAFF_PITCHED:
4407                   if (getAction("note-input-repitch")->isChecked()) {
4408                         showModeText(tr("Repitch input mode"));
4409                         cs->setNoteEntryMethod(NoteEntryMethod::REPITCH);
4410                         val = STATE_NOTE_ENTRY_METHOD_REPITCH;
4411                         }
4412                   else if (getAction("note-input-rhythm")->isChecked()) {
4413                         showModeText(tr("Rhythm input mode"));
4414                         cs->setNoteEntryMethod(NoteEntryMethod::RHYTHM);
4415                         val = STATE_NOTE_ENTRY_METHOD_RHYTHM;
4416                         }
4417                   else if (getAction("note-input-realtime-auto")->isChecked()) {
4418                         showModeText(tr("Realtime (automatic) note input mode"));
4419                         cs->setNoteEntryMethod(NoteEntryMethod::REALTIME_AUTO);
4420                         val = STATE_NOTE_ENTRY_METHOD_REALTIME_AUTO;
4421                         }
4422                   else if (getAction("note-input-realtime-manual")->isChecked()) {
4423                         showModeText(tr("Realtime (manual) note input mode"));
4424                         cs->setNoteEntryMethod(NoteEntryMethod::REALTIME_MANUAL);
4425                         val = STATE_NOTE_ENTRY_METHOD_REALTIME_MANUAL;
4426                         }
4427                   else if (getAction("note-input-timewise")->isChecked()) {
4428                         showModeText(tr("Insert mode"));
4429                         cs->setNoteEntryMethod(NoteEntryMethod::TIMEWISE);
4430                         val = STATE_NOTE_ENTRY_METHOD_TIMEWISE;
4431                         }
4432                   else {
4433                         showModeText(tr("Steptime note input mode"));
4434                         cs->setNoteEntryMethod(NoteEntryMethod::STEPTIME);
4435                         val = STATE_NOTE_ENTRY_METHOD_STEPTIME;
4436                         }
4437                   break;
4438             case STATE_NOTE_ENTRY_STAFF_DRUM:
4439                   {
4440                   if (getAction("note-input-repitch")->isChecked())
4441                         cs->setNoteEntryMethod(NoteEntryMethod::REPITCH);
4442                   else if (getAction("note-input-rhythm")->isChecked())
4443                         cs->setNoteEntryMethod(NoteEntryMethod::RHYTHM);
4444                   else if (getAction("note-input-realtime-auto")->isChecked())
4445                         cs->setNoteEntryMethod(NoteEntryMethod::REALTIME_AUTO);
4446                   else if (getAction("note-input-realtime-manual")->isChecked())
4447                         cs->setNoteEntryMethod(NoteEntryMethod::REALTIME_MANUAL);
4448                   else if (getAction("note-input-timewise")->isChecked())
4449                         cs->setNoteEntryMethod(NoteEntryMethod::TIMEWISE);
4450                   else
4451                         cs->setNoteEntryMethod(NoteEntryMethod::STEPTIME);
4452                   showModeText(tr("Drumset input mode"));
4453                   InputState& is = cs->inputState();
4454                   showDrumTools(is.drumset(), cs->staff(is.track() / VOICES));
4455                   if (_drumTools)
4456                         is.setDrumNote(_drumTools->selectedDrumNote());
4457                   }
4458                   break;
4459             case STATE_NOTE_ENTRY_STAFF_TAB:
4460                   if (getAction("note-input-repitch")->isChecked())
4461                         cs->setNoteEntryMethod(NoteEntryMethod::REPITCH);
4462                   else if (getAction("note-input-rhythm")->isChecked())
4463                         cs->setNoteEntryMethod(NoteEntryMethod::RHYTHM);
4464                   else if (getAction("note-input-realtime-auto")->isChecked())
4465                         cs->setNoteEntryMethod(NoteEntryMethod::REALTIME_AUTO);
4466                   else if (getAction("note-input-realtime-manual")->isChecked())
4467                         cs->setNoteEntryMethod(NoteEntryMethod::REALTIME_MANUAL);
4468                   else if (getAction("note-input-timewise")->isChecked())
4469                         cs->setNoteEntryMethod(NoteEntryMethod::TIMEWISE);
4470                   else
4471                         cs->setNoteEntryMethod(NoteEntryMethod::STEPTIME);
4472                   showModeText(tr("TAB input mode"));
4473                   break;
4474             case STATE_EDIT:
4475                   showModeText(tr("Edit mode"));
4476                   break;
4477             case STATE_TEXT_EDIT:
4478                   showModeText(tr("Text edit mode"));
4479                   break;
4480             case STATE_LYRICS_EDIT:
4481                   showModeText(tr("Lyrics edit mode"));
4482                   break;
4483             case STATE_HARMONY_FIGBASS_EDIT:
4484                   showModeText(tr("Chord symbol/figured bass edit mode"));
4485                   break;
4486             case STATE_PLAY:
4487                   showModeText(tr("Play"), false); // don't talk over playback
4488                   break;
4489             case STATE_FOTO:
4490                   showModeText(tr("Image capture mode"));
4491                   updateInspector();
4492                   break;
4493             case STATE_LOCK:
4494                   showModeText(tr("Score locked"));
4495                   break;
4496             default:
4497                   qFatal("MuseScore::changeState: illegal state %d", val);
4498                   break;
4499             }
4500       if (paletteWidget)
4501             paletteWidget->setDisabled(val == STATE_PLAY || val == STATE_DISABLED);
4502       if (selectionWindow)
4503             selectionWindow->setDisabled(val == STATE_PLAY || val == STATE_DISABLED);
4504       QAction* a = getAction("note-input");
4505       bool noteEntry = val & STATE_NOTE_ENTRY;
4506       a->setChecked(noteEntry);
4507       _sstate = val;
4508 
4509       emit scoreStateChanged(_sstate);
4510 
4511       Element* e = cv && (_sstate & STATE_ALLTEXTUAL_EDIT || _sstate == STATE_EDIT) ? cv->getEditElement() : 0;
4512       if (!e) {
4513             textTools()->hide();
4514             if (textTools()->kbAction()->isChecked())
4515                   textTools()->kbAction()->setChecked(false);
4516             }
4517       else {
4518             if (e->isTextBase()) {
4519                   textTools()->updateTools(cv->getEditData());
4520                   if (!(e->isFiguredBass() || e->isHarmony())) {  // do not show text tools for f.b.
4521                         if (timelineScrollArea() && timelineScrollArea()->isVisible()) {
4522                               if (dockWidgetArea(timelineScrollArea()) != dockWidgetArea(textTools()) || timelineScrollArea()->isFloating()) {
4523                                     QSizePolicy policy(QSizePolicy::Maximum, QSizePolicy::Maximum);
4524                                     textTools()->widget()->setSizePolicy(policy);
4525                                     }
4526                               else {
4527                                     QSizePolicy policy(QSizePolicy::Expanding, QSizePolicy::Expanding);
4528                                     textTools()->widget()->setSizePolicy(policy);
4529                                     }
4530                               }
4531                         else {
4532                               QSizePolicy policy(QSizePolicy::Maximum, QSizePolicy::Maximum);
4533                               textTools()->widget()->setSizePolicy(policy);
4534                               }
4535                         if (timelineScrollArea())
4536                               splitDockWidget(textTools(), timelineScrollArea(), Qt::Vertical);
4537                         textTools()->show();
4538                         }
4539                   }
4540             if (_inspector)
4541                   _inspector->update(e->score());
4542             }
4543       }
4544 
4545 //---------------------------------------------------------
4546 //   saveDialogState
4547 //---------------------------------------------------------
4548 
saveDialogState(const char * name,QFileDialog * d)4549 void MuseScore::saveDialogState(const char* name, QFileDialog* d)
4550       {
4551       if (d) {
4552             settings.beginGroup(name);
4553             settings.setValue("geometry", d->saveGeometry());
4554             settings.setValue("state", d->saveState());
4555             settings.endGroup();
4556             }
4557       }
4558 
4559 //---------------------------------------------------------
4560 //   restoreDialogState
4561 //---------------------------------------------------------
4562 
restoreDialogState(const char * name,QFileDialog * d)4563 void MuseScore::restoreDialogState(const char* name, QFileDialog* d)
4564       {
4565       settings.beginGroup(name);
4566       d->restoreGeometry(settings.value("geometry").toByteArray());
4567       d->restoreState(settings.value("state").toByteArray());
4568       settings.endGroup();
4569       }
4570 
4571 //---------------------------------------------------------
4572 //   writeSettings
4573 //---------------------------------------------------------
4574 
writeSettings()4575 void MuseScore::writeSettings()
4576       {
4577       // save score list
4578       for (int i = 0; i < RECENT_LIST_SIZE; ++i)
4579             settings.setValue(QString("recent-%1").arg(i), _recentScores.value(i));
4580 
4581       settings.setValue("scores", scoreList.size());
4582       if (cs) {
4583             int curScore = scoreList.indexOf(cs->masterScore());
4584             if (curScore == -1)  // cs removed if new created and not modified
4585                   curScore = 0;
4586             settings.setValue("currentScore", curScore);
4587 
4588             for (int idx = 0; idx < scoreList.size(); ++idx)
4589                   settings.setValue(QString("score-%1").arg(idx), scoreList[idx]->masterScore()->fileInfo()->absoluteFilePath());
4590             }
4591 
4592       settings.setValue("lastSaveCopyDirectory", lastSaveCopyDirectory);
4593       settings.setValue("lastSaveCopyFormat", lastSaveCopyFormat);
4594       settings.setValue("lastSaveDirectory", lastSaveDirectory);
4595 
4596       MuseScore::saveGeometry(this);
4597 
4598       settings.beginGroup("MainWindow");
4599       settings.setValue("showPanel", paletteWidget && paletteWidget->isVisible());
4600       settings.setValue("showInspector", _inspector && _inspector->isVisible());
4601       settings.setValue("showPianoKeyboard", _pianoTools && _pianoTools->isVisible());
4602       settings.setValue("showSelectionWindow", selectionWindow && selectionWindow->isVisible());
4603       settings.setValue("state", saveState());
4604       settings.setValue("splitScreen", _splitScreen);
4605       settings.setValue("debuggerSplitter", mainWindow->saveState());
4606       settings.setValue("split", _horizontalSplit);
4607       settings.setValue("splitter", splitter->saveState());
4608       settings.endGroup();
4609 
4610       WorkspacesManager::currentWorkspace()->save();
4611       if (keyEditor && keyEditor->dirty())
4612             keyEditor->save();
4613       if (chordStyleEditor)
4614             chordStyleEditor->save();
4615 
4616       saveDialogState("loadScoreDialog",      loadScoreDialog);
4617       saveDialogState("saveScoreDialog",      saveScoreDialog);
4618       saveDialogState("loadStyleDialog",      loadStyleDialog);
4619       saveDialogState("saveStyleDialog",      saveStyleDialog);
4620       saveDialogState("saveImageDialog",      saveImageDialog);
4621       saveDialogState("loadChordStyleDialog", loadChordStyleDialog);
4622       saveDialogState("saveChordStyleDialog", saveChordStyleDialog);
4623       saveDialogState("loadScanDialog",       loadScanDialog);
4624       saveDialogState("loadAudioDialog",      loadAudioDialog);
4625       saveDialogState("loadDrumsetDialog",    loadDrumsetDialog);
4626       saveDialogState("saveDrumsetDialog",    saveDrumsetDialog);
4627       saveDialogState("loadPaletteDialog",    loadPaletteDialog);
4628       saveDialogState("savePaletteDialog",    savePaletteDialog);
4629 
4630       if (debugger)
4631             debugger->writeSettings();
4632 
4633 #ifdef SCRIPT_INTERFACE
4634       if (_pluginCreator)
4635             _pluginCreator->writeSettings();
4636 #endif
4637       if (synthControl)
4638             synthControl->writeSettings();
4639       settings.setValue("synthControlVisible", synthControl && synthControl->isVisible());
4640       settings.setValue("mixerVisible", mixer && mixer->isVisible());
4641       if (seq) {
4642             seq->exit();
4643             }
4644       if (instrList)
4645             instrList->writeSettings();
4646       if (pianorollEditor)
4647             pianorollEditor->writeSettings();
4648       if (drumrollEditor)
4649             drumrollEditor->writeSettings();
4650       if (startcenter)
4651             startcenter->writeSettings();
4652 
4653       _tourHandler->writeCompletedTours();
4654       }
4655 
4656 //---------------------------------------------------------
4657 //   readSettings
4658 //---------------------------------------------------------
4659 
readSettings()4660 void MuseScore::readSettings()
4661       {
4662       int margin = 100;
4663       int offset = margin / 2;
4664       int w = 1024;
4665       int h = 768;
4666       QScreen* screen      = QGuiApplication::primaryScreen();
4667       const QSize screenSize = screen->availableSize();
4668       if (screenSize.width() - margin > w)
4669             w = screenSize.width() - margin;
4670       else
4671             offset = 0;
4672       if (screenSize.height() - margin > h)
4673             h = screenSize.height() - margin;
4674 
4675       resize(QSize(w, h)); //ensure default size if no geometry in settings
4676       move(offset, 0);
4677       if (useFactorySettings) {
4678             QList<int> sizes;
4679             sizes << 500 << 100;
4680             mainWindow->setSizes(sizes);
4681             mscore->showPalette(true);
4682             mscore->showInspector(true);
4683             return;
4684             }
4685 
4686       MuseScore::restoreGeometry(this);
4687 
4688       // Grab the mixer visible state before the beginGroup.
4689       // Previously the showMixer() call was made at the end of
4690       // main(). Now it's made inside this beginGroup / endGroup.
4691       // This code ensures user previous setting is respected when
4692       // updating their version of MuseScore.
4693       bool mixerVisible = settings.value("mixerVisible", "0").toBool();
4694       settings.beginGroup("MainWindow");
4695       mainWindow->restoreState(settings.value("debuggerSplitter").toByteArray());
4696       mainWindow->setOpaqueResize(false);
4697       scorePageLayoutChanged();
4698 
4699       //for some reason when MuseScore starts maximized the screen-reader
4700       //doesn't respond to QAccessibleEvents --> so force normal mode
4701       if (isMaximized() && QAccessible::isActive()) {
4702             showNormal();
4703             }
4704       mscore->showPalette(settings.value("showPanel", "1").toBool());
4705       mscore->showInspector(settings.value("showInspector", "1").toBool());
4706       mscore->showPianoKeyboard(settings.value("showPianoKeyboard", "0").toBool());
4707       mscore->showSelectionWindow(settings.value("showSelectionWindow", "0").toBool());
4708       mscore->showMixer(mixerVisible);
4709 
4710       restoreState(settings.value("state").toByteArray());
4711       //if we were in full screen mode, go to maximized mode
4712       if (isFullScreen()) {
4713             showMaximized();
4714             }
4715 
4716       _horizontalSplit = settings.value("split", true).toBool();
4717       bool splitScreen = settings.value("splitScreen", false).toBool();
4718       if (splitScreen) {
4719             splitWindow(_horizontalSplit);
4720             QAction* a = getAction(_horizontalSplit ? "split-h" : "split-v");
4721             a->setChecked(true);
4722             }
4723       splitter->restoreState(settings.value("splitter").toByteArray());
4724       settings.endGroup();
4725 
4726       QAction* a = getAction("toggle-fileoperations");
4727       a->setChecked(!fileTools->isHidden());
4728 
4729       a = getAction("toggle-transport");
4730       a->setChecked(!transportTools->isHidden());
4731 
4732       a = getAction("toggle-concertpitch");
4733       a->setChecked(!cpitchTools->isHidden());
4734 
4735       a = getAction("toggle-imagecapture");
4736       a->setChecked(!fotoTools->isHidden());
4737 
4738       a = getAction("toggle-noteinput");
4739       a->setChecked(!entryTools->isHidden());
4740       }
4741 
4742 //---------------------------------------------------------
4743 //   play
4744 //    play note for preferences.defaultPlayDuration
4745 //---------------------------------------------------------
4746 
play(Element * e) const4747 void MuseScore::play(Element* e) const
4748       {
4749       if (noSeq || !(seq && seq->isRunning()) || !preferences.getBool(PREF_SCORE_NOTE_PLAYONCLICK))
4750             return;
4751 
4752       if (e->isNote()) {
4753             Note* note = toNote(e);
4754             play(e, note->ppitch());
4755             }
4756       else if (e->isChord()) {
4757             seq->stopNotes();
4758             Chord* c   = toChord(e);
4759             Part* part = c->staff()->part();
4760             Fraction tick   = c->segment() ? c->segment()->tick() : Fraction(0,1);
4761             Instrument* instr = part->instrument(tick);
4762             for (Note* n : c->notes()) {
4763                   const int channel = instr->channel(n->subchannel())->channel();
4764                   seq->startNote(channel, n->ppitch(), 80, n->tuning());
4765                   }
4766             seq->startNoteTimer(MScore::defaultPlayDuration);
4767             }
4768       else if (e->isHarmony()
4769                && preferences.getBool(PREF_SCORE_HARMONY_PLAY_ONEDIT)) {
4770             seq->stopNotes();
4771             Harmony* h = toHarmony(e);
4772             if (!h->isRealizable())
4773                   return;
4774             RealizedHarmony r = h->getRealizedHarmony();
4775             QList<int> pitches = r.pitches();
4776 
4777             const Channel* hChannel = e->part()->harmonyChannel();
4778             if (!hChannel)
4779                   return;
4780 
4781             int channel = hChannel->channel();
4782 
4783             // reset the cc that is used for single note dynamics, if any
4784             int cc = synthesizerState().ccToUse();
4785             if (cc != -1)
4786                   seq->sendEvent(NPlayEvent(ME_CONTROLLER, channel, cc, 80));
4787 
4788             for (int pitch : pitches)
4789                   seq->startNote(channel, pitch, 80, 0);
4790             seq->startNoteTimer(MScore::defaultPlayDuration);
4791             }
4792       }
4793 
play(Element * e,int pitch) const4794 void MuseScore::play(Element* e, int pitch) const
4795       {
4796       if (noSeq || !(seq && seq->isRunning()))
4797             return;
4798       if (preferences.getBool(PREF_SCORE_NOTE_PLAYONCLICK) && e->isNote()) {
4799             Note* note = static_cast<Note*>(e);
4800 
4801             Note* masterNote = note;
4802             if (note->linkList().size() > 1) {
4803                   for (ScoreElement* se_ : note->linkList()) {
4804                         if (se_->score() == note->masterScore() && se_->isNote()) {
4805                               masterNote = toNote(se_);
4806                               break;
4807                               }
4808                         }
4809                   }
4810 
4811             Fraction tick = masterNote->chord()->tick();
4812             if (tick < Fraction(0,1))
4813                   tick = Fraction(0,1);
4814             Instrument* instr = masterNote->part()->instrument(tick);
4815             const int channel = instr->channel(masterNote->subchannel())->channel();
4816 
4817             // reset the cc that is used for single note dynamics, if any
4818             int cc = synthesizerState().ccToUse();
4819             if (cc != -1)
4820                   seq->sendEvent(NPlayEvent(ME_CONTROLLER, channel, cc, 80));
4821 
4822             seq->startNote(channel, pitch, 80, MScore::defaultPlayDuration, masterNote->tuning());
4823             }
4824       }
4825 
4826 //---------------------------------------------------------
4827 //   reportBug
4828 //---------------------------------------------------------
4829 
reportBug(QString medium)4830 void MuseScore::reportBug(QString medium)
4831       {
4832       QString url = QString("https://musescore.org/redirect/post/bug-report?sha=%1&locale=%2&%3")
4833          .arg(revision())
4834          .arg(getLocaleISOCode())
4835          .arg(getUtmParameters(medium));
4836       QDesktopServices::openUrl(QUrl(url.trimmed()));
4837       }
4838 
4839 //---------------------------------------------------------
4840 //   askForHelp
4841 //---------------------------------------------------------
4842 
askForHelp()4843 void MuseScore::askForHelp()
4844       {
4845       QString url = QString("https://musescore.org/redirect/post/question?locale=%1").arg(getLocaleISOCode());
4846       QDesktopServices::openUrl(QUrl(url.trimmed()));
4847       }
4848 
4849 //---------------------------------------------------------
4850 //   leaveFeedback
4851 //---------------------------------------------------------
4852 
leaveFeedback(QString medium)4853 void MuseScore::leaveFeedback(QString medium)
4854       {
4855       QString url = QString("https://musescore.com/content/editor-feedback?%1")
4856          .arg(getUtmParameters(medium));
4857       QDesktopServices::openUrl(QUrl(url.trimmed()));
4858       }
4859 
4860 //---------------------------------------------------------
4861 //   getUtmParameters
4862 //    Get UTM labels to track visits.
4863 //    See: https://www.google.com/support/googleanalytics/bin/answer.py?answer=55578
4864 //---------------------------------------------------------
4865 
getUtmParameters(QString medium) const4866 QString MuseScore::getUtmParameters(QString medium) const
4867       {
4868       return QString("utm_source=desktop&utm_medium=%1&utm_content=%2&utm_campaign=MuseScore%3")
4869          .arg(medium)
4870          .arg(rev.trimmed())
4871          .arg(QString(VERSION));
4872       }
4873 
4874 //---------------------------------------------------------
4875 //   about
4876 //---------------------------------------------------------
4877 
about()4878 void MuseScore::about()
4879       {
4880       AboutBoxDialog ab;
4881       ab.show();
4882       ab.exec();
4883       }
4884 
4885 //---------------------------------------------------------
4886 //   dirtyChanged
4887 //---------------------------------------------------------
4888 
dirtyChanged(Score * s)4889 void MuseScore::dirtyChanged(Score* s)
4890       {
4891       MasterScore* score = s->masterScore();
4892 
4893       int idx = scoreList.indexOf(score);
4894       if (idx == -1) {
4895             qDebug("score not in list");
4896             return;
4897             }
4898       QString label(score->fileInfo()->completeBaseName());
4899       if (score->dirty())
4900             label += "*";
4901       tab1->setTabText(idx, label);
4902       if (tab2)
4903             tab2->setTabText(idx, label);
4904       setWindowModified(score->dirty());
4905       }
4906 
4907 //---------------------------------------------------------
4908 //   zoomBoxChanged
4909 //    Called when the zoom-box value has changed; do not call directly.
4910 //---------------------------------------------------------
4911 
zoomBoxChanged(const ZoomIndex index,const qreal logicalLevel)4912 void MuseScore::zoomBoxChanged(const ZoomIndex index, const qreal logicalLevel)
4913       {
4914       setZoom(index, logicalLevel);
4915       }
4916 
4917 //---------------------------------------------------------
4918 //   setZoom
4919 //    Sets the zoom type and the logical free-zoom level.
4920 //    logicalFreeZoomLevel is optional and may be omitted unless index is ZoomIndex::ZOOM_FREE.
4921 //---------------------------------------------------------
4922 
setZoom(const ZoomIndex index,const qreal logicalFreeZoomLevel)4923 void MuseScore::setZoom(const ZoomIndex index, const qreal logicalFreeZoomLevel/* = 0.0*/)
4924       {
4925       zoomAndSavePrevious([=]() { cv->setLogicalZoom(index, cv->calculateLogicalZoomLevel(index, logicalFreeZoomLevel)); });
4926       }
4927 
4928 //---------------------------------------------------------
4929 //   setZoomWithToggle
4930 //    Sets the specified zoom type, or toggles back to the previous zoom state if the zoom type is already the specified one.
4931 //---------------------------------------------------------
4932 
setZoomWithToggle(const ZoomIndex index)4933 void MuseScore::setZoomWithToggle(const ZoomIndex index)
4934       {
4935       if (cv) {
4936             const ZoomIndex currentZoomIndex = cv->zoomIndex();
4937             if (currentZoomIndex != index) {
4938                   // The current zoom type isn't the specified one, so just set it to that.
4939                   setZoom(index);
4940                   }
4941             else {
4942                   // The current zoom type is already the specified one, so toggle back to the previous zoom state.
4943                   setZoom(cv->previousZoomIndex(), cv->previousLogicalZoomLevel());
4944                   }
4945             }
4946       }
4947 
4948 //---------------------------------------------------------
4949 //   zoomBySteps
4950 //    Zooms in or out by the specified number of keyboard-based zoom steps (positive to zoom in, negative to zoom out).
4951 //---------------------------------------------------------
4952 
zoomBySteps(const qreal numSteps)4953 void MuseScore::zoomBySteps(const qreal numSteps)
4954       {
4955       zoomAndSavePrevious([=]() { cv->zoomBySteps(numSteps); });
4956       }
4957 
4958 //---------------------------------------------------------
4959 //   zoomAndSavePrevious
4960 //    Calls the specified zoom function and also saves the previous zoom state if it has changed by the zoom function.
4961 //---------------------------------------------------------
4962 
zoomAndSavePrevious(const std::function<void (void)> & zoomFunction)4963 void MuseScore::zoomAndSavePrevious(const std::function<void(void)>& zoomFunction)
4964       {
4965       if (cv) {
4966             // Make a copy of the current zoom state before it changes.
4967             const auto previousLogicalZoom = cv->logicalZoom();
4968 
4969             // Zoom!
4970             zoomFunction();
4971 
4972             // Save the previous zoom state, but only if the zoom state has actually changed.
4973             if (cv->logicalZoom() != previousLogicalZoom)
4974                   cv->setPreviousLogicalZoom(previousLogicalZoom);
4975             }
4976       }
4977 
4978 //---------------------------------------------------------
4979 //   updateZoomBox
4980 //    Public function called by the score view to update the zoom box after the actual zoom type and/or level have changed.
4981 //---------------------------------------------------------
4982 
updateZoomBox(const ZoomIndex index,const qreal logicalLevel)4983 void MuseScore::updateZoomBox(const ZoomIndex index, const qreal logicalLevel)
4984       {
4985       const QSignalBlocker blocker(zoomBox);
4986       zoomBox->setLogicalZoom(index, logicalLevel);
4987       }
4988 
4989 //---------------------------------------------------------
4990 //   setPos
4991 //    set position label
4992 //---------------------------------------------------------
4993 
setPos(const Fraction & t)4994 void MuseScore::setPos(const Fraction& t)
4995       {
4996       if (cs == 0 || t < Fraction(0,1))
4997             return;
4998 
4999       TimeSigMap* s = cs->sigmap();
5000       int bar, beat, tick;
5001       s->tickValues(t.ticks(), &bar, &beat, &tick);
5002       _positionLabel->setText(QString("%1:%2:%3")
5003          .arg(bar + 1,  3, 10, QLatin1Char(' '))
5004          .arg(beat + 1, 2, 10, QLatin1Char('0'))
5005          .arg(tick,     3, 10, QLatin1Char('0'))
5006          );
5007       }
5008 
5009 //---------------------------------------------------------
5010 //   undoRedo
5011 //---------------------------------------------------------
5012 
undoRedo(bool undo)5013 void MuseScore::undoRedo(bool undo)
5014       {
5015       Q_ASSERT(cv);
5016       Q_ASSERT(cs);
5017       if (_sstate & (STATE_EDIT | STATE_HARMONY_FIGBASS_EDIT | STATE_LYRICS_EDIT))
5018             cv->changeState(ViewState::NORMAL);
5019       cv->startUndoRedo(undo);
5020       updateInputState(cs);
5021       endCmd(/* undoRedo */ true);
5022       if (pianorollEditor)
5023             pianorollEditor->update();
5024       }
5025 
5026 //---------------------------------------------------------
5027 //   showProgressBar
5028 //---------------------------------------------------------
5029 
showProgressBar()5030 QProgressBar* MuseScore::showProgressBar()
5031       {
5032       if (_progressBar == 0)
5033             _progressBar = new QProgressBar(this);
5034       _statusBar->addWidget(_progressBar);
5035       _progressBar->show();
5036       return _progressBar;
5037       }
5038 
5039 //---------------------------------------------------------
5040 //   hideProgressBar
5041 //---------------------------------------------------------
5042 
hideProgressBar()5043 void MuseScore::hideProgressBar()
5044       {
5045       if (_progressBar)
5046             _statusBar->removeWidget(_progressBar);
5047       }
5048 
5049 //---------------------------------------------------------
5050 //   handleMessage
5051 //---------------------------------------------------------
5052 
handleMessage(const QString & message)5053 void MuseScore::handleMessage(const QString& message)
5054       {
5055       if (message.isEmpty())
5056             return;
5057       if (startcenter)
5058             showStartcenter(false);
5059       ((QtSingleApplication*)(qApp))->activateWindow();
5060       openScore(message);
5061       }
5062 
5063 //---------------------------------------------------------
5064 //   editInPianoroll
5065 //---------------------------------------------------------
5066 
editInPianoroll(Staff * staff,Position * p)5067 void MuseScore::editInPianoroll(Staff* staff, Position* p)
5068       {
5069       if (pianorollEditor == 0)
5070             pianorollEditor = new PianorollEditor(this);
5071       pianorollEditor->setScore(staff->score());
5072       pianorollEditor->setStaff(staff);
5073       pianorollEditor->show();
5074       pianorollEditor->focusOnPosition(p);
5075       }
5076 
5077 //---------------------------------------------------------
5078 //   editInDrumroll
5079 //---------------------------------------------------------
5080 
editInDrumroll(Staff * staff)5081 void MuseScore::editInDrumroll(Staff* staff)
5082       {
5083       if (drumrollEditor == 0)
5084             drumrollEditor = new DrumrollEditor;
5085       drumrollEditor->setStaff(staff);
5086       drumrollEditor->show();
5087       }
5088 
5089 //---------------------------------------------------------
5090 //   writeSessionFile
5091 //---------------------------------------------------------
5092 
writeSessionFile(bool cleanExit)5093 void MuseScore::writeSessionFile(bool cleanExit)
5094       {
5095 // qDebug("write session file");
5096 
5097       QDir dir;
5098       dir.mkpath(dataPath);
5099       QFile f(dataPath + "/session");
5100       if (!f.open(QIODevice::WriteOnly)) {
5101             qDebug("cannot create session file <%s>", qPrintable(f.fileName()));
5102             return;
5103             }
5104       XmlWriter xml(0, &f);
5105       xml.header();
5106       xml.stag(QStringLiteral("museScore version=\"" MSC_VERSION "\" full-version=\"%1\"").arg(fullVersion()));
5107       xml.tagE(cleanExit ? "clean" : "dirty");
5108 
5109       foreach(MasterScore* score, scoreList) {
5110             xml.stag("Score");
5111             xml.tag("created", score->created());
5112             xml.tag("dirty", score->dirty());
5113 
5114             QString path;
5115             if (score->importedFilePath().isEmpty()) {
5116                               // score was not imported from another format, e.g. MIDI file
5117                   path = score->masterScore()->fileInfo()->absoluteFilePath();
5118                   }
5119             else if (score->masterScore()->fileInfo()->exists()) {   // score was saved
5120                   path = score->masterScore()->fileInfo()->absoluteFilePath();
5121                   }
5122             else {      // score was imported but not saved - store original file (e.g. MIDI) path
5123                   path = score->importedFilePath();
5124                   }
5125 
5126             if (cleanExit || score->tmpName().isEmpty()) {
5127                   xml.tag("path", path);
5128                   }
5129             else {
5130                   xml.tag("name", path);
5131                   xml.tag("path", score->tmpName());
5132                   }
5133             xml.etag();
5134             }
5135       int tab = 0;
5136       int idx = 0;
5137       for (int i = 0; i < tab1->count(); ++i) {
5138             ScoreView* v = tab1->view(i);
5139             if (v) {
5140                   if (v == cv) {
5141                         tab = 0;
5142                         idx = i;
5143                         }
5144                   xml.stag("ScoreView");
5145                   xml.tag("tab", tab);    // 0 instead of "tab" does not work
5146                   xml.tag("idx", i);
5147                   if (v->zoomIndex() == ZoomIndex::ZOOM_FREE)
5148                         xml.tag("mag", v->logicalZoomLevel());
5149                   else
5150                         xml.tag("magIdx", int(v->zoomIndex()));
5151                   xml.tag("x",   v->xoffset() / DPMM);
5152                   xml.tag("y",   v->yoffset() / DPMM);
5153                   xml.etag();
5154                   }
5155             }
5156       if (splitScreen()) {
5157             for (int i = 0; i < tab2->count(); ++i) {
5158                   ScoreView* v = tab2->view(i);
5159                   if (v) {
5160                         if (v == cv) {
5161                               tab = 1;
5162                               idx = i;
5163                               }
5164                         xml.stag("ScoreView");
5165                         xml.tag("tab", 1);
5166                         xml.tag("idx", i);
5167                         if (v->zoomIndex() == ZoomIndex::ZOOM_FREE)
5168                               xml.tag("mag", v->logicalZoomLevel());
5169                         else
5170                               xml.tag("magIdx", int(v->zoomIndex()));
5171                         xml.tag("x",   v->xoffset() / DPMM);
5172                         xml.tag("y",   v->yoffset() / DPMM);
5173                         xml.etag();
5174                         }
5175                   }
5176             }
5177       xml.tag("tab", tab);
5178       xml.tag("idx", idx);
5179       xml.etag();
5180       f.close();
5181       if (cleanExit) {
5182             // TODO: remove all temporary session backup files
5183             }
5184       }
5185 
5186 //---------------------------------------------------------
5187 //   removeSessionFile
5188 //    remove temp files and session file
5189 //---------------------------------------------------------
5190 
removeSessionFile()5191 void MuseScore::removeSessionFile()
5192       {
5193       QFile f(dataPath + "/session");
5194       if (!f.exists())
5195             return;
5196       if (!f.remove()) {
5197             qDebug("cannot remove session file <%s>", qPrintable(f.fileName()));
5198             }
5199       }
5200 
5201 //---------------------------------------------------------
5202 //   autoSaveTimerTimeout
5203 //---------------------------------------------------------
5204 
autoSaveTimerTimeout()5205 void MuseScore::autoSaveTimerTimeout()
5206       {
5207       bool sessionChanged = false;
5208 
5209       ScoreLoad sl;           //disable debug message "no active command"
5210 
5211       for (MasterScore* s : scoreList) {
5212             if (s->autosaveDirty()) {
5213                   qDebug("<%s>", qPrintable(s->fileInfo()->completeBaseName()));
5214                   QString tmp = s->tmpName();
5215                   if (!tmp.isEmpty()) {
5216                         QFileInfo fi(tmp);
5217                         // TODO: cannot catch exception here:
5218                         s->saveCompressedFile(fi, false, false); // no thumbnail
5219                         }
5220                   else {
5221                         QDir dir;
5222                         dir.mkpath(dataPath);
5223                         QTemporaryFile tf(dataPath + "/scXXXXXX.mscz");
5224                         tf.setAutoRemove(false); // do not remove when tf goes out of scope!
5225                         if (!tf.open()) {
5226                               qDebug("autoSaveTimerTimeout(): create temporary file failed");
5227                               return;
5228                               }
5229                         s->setTmpName(tf.fileName());
5230 
5231                         QString fileName = QFileInfo(tf.fileName()).completeBaseName() + ".mscx";
5232                         s->saveCompressedFile(&tf, fileName, false, false);  // no thumbnail
5233 
5234                         tf.close();
5235                         sessionChanged = true;
5236                         }
5237                   s->setAutosaveDirty(false);
5238                   }
5239             }
5240       if (sessionChanged)
5241             writeSessionFile(false);
5242       if (preferences.getBool(PREF_APP_AUTOSAVE_USEAUTOSAVE)) {
5243             int t = preferences.getInt(PREF_APP_AUTOSAVE_AUTOSAVETIME) * 60 * 1000;
5244             autoSaveTimer->start(t);
5245             }
5246       }
5247 
5248 class CallOnReturn {
5249       std::function<void()> f;
5250    public:
CallOnReturn(std::function<void ()> func)5251       CallOnReturn(std::function<void()> func) : f(std::move(func)) {}
5252       CallOnReturn(const CallOnReturn&) = delete;
5253       CallOnReturn& operator=(const CallOnReturn&) = delete;
~CallOnReturn()5254       ~CallOnReturn() { f(); }
5255       };
5256 
5257 //---------------------------------------------------------
5258 //   restoreSession
5259 //    Restore last session. If "always" is true, then restore
5260 //    last session even on a clean exit else only if last
5261 //    session ended abnormal.
5262 //    Return true if a session was found and restored.
5263 //---------------------------------------------------------
5264 
restoreSession(bool always)5265 bool MuseScore::restoreSession(bool always)
5266       {
5267       bool sessionFileFound = false;
5268       bool cleanExit = false;
5269       QString sessionFullVersion;
5270 
5271       CallOnReturn onReturn([this, &sessionFileFound, &sessionFullVersion, &cleanExit]() {
5272             sessionStatusObserver.prevSessionStatus(sessionFileFound, sessionFullVersion, cleanExit);
5273             });
5274 
5275       QFile f(dataPath + "/session");
5276       if (!f.exists())
5277             return false;
5278       if (!f.open(QIODevice::ReadOnly)) {
5279             qDebug("Cannot open session file <%s>", qPrintable(f.fileName()));
5280             return false;
5281             }
5282       sessionFileFound = true;
5283 
5284       XmlReader e(&f);
5285       int tab = 0;
5286       int idx = -1;
5287       while (e.readNextStartElement()) {
5288             if (e.name() == "museScore") {
5289                   /* QString version = e.attribute(QString("version"));
5290                   QStringList sl  = version.split('.');
5291                   int v           = sl[0].toInt() * 100 + sl[1].toInt();
5292                   */
5293                   sessionFullVersion = e.attribute("full-version");
5294                   while (e.readNextStartElement()) {
5295                         const QStringRef& tag(e.name());
5296                         if (tag == "clean") {
5297                               cleanExit = true;
5298                               if (!always)
5299                                     return false;
5300                               e.readNext();
5301                               }
5302                         else if (tag == "dirty") {
5303                               QMessageBox::StandardButton b = QMessageBox::question(0,
5304                                  tr("MuseScore"),
5305                                  tr("The previous session quit unexpectedly.\n\nRestore session?"),
5306                                  QMessageBox::Yes | QMessageBox::No,
5307                                  QMessageBox::Yes
5308                                  );
5309                               if (b != QMessageBox::Yes)
5310                                     return false;
5311                               e.readNext();
5312                               }
5313                         else if (tag == "Score") {
5314                               QString name;
5315                               bool created = false;
5316                               while (e.readNextStartElement()) {
5317                                     const QStringRef& t(e.name());
5318                                     if (t == "name")
5319                                           name = e.readElementText();
5320                                     else if (t == "created")
5321                                           created = e.readInt();
5322                                     else if (t == "dirty")
5323                                           /*int dirty =*/ e.readInt();
5324                                     else if (t == "path") {
5325                                           Score* score = openScore(e.readElementText(), false, true, name);
5326                                           if (score) {
5327                                                 if (cleanExit) {
5328                                                       // override if last session did a clean exit
5329                                                       created = false;
5330                                                       }
5331                                                 score->setCreated(created);
5332                                                 }
5333                                           else {
5334                                                 //! NOTE Return true so that there is no attempt to open this file again
5335                                                 //! See: static void loadScores(const QStringList& argv)
5336                                                 return true;
5337                                                 }
5338                                           }
5339                                     else {
5340                                           e.unknown();
5341                                           return false;
5342                                           }
5343                                     }
5344                               }
5345                         else if (tag == "ScoreView") {
5346                               qreal x                = 0.0;
5347                               qreal y                = 0.0;
5348                               qreal logicalZoomLevel = 1.0;
5349                               ZoomIndex zoomIndex    = ZoomIndex::ZOOM_FREE;
5350                               int tab3               = 0;
5351                               int idx1               = 0;
5352                               while (e.readNextStartElement()) {
5353                                     const QStringRef& t(e.name());
5354                                     if (t == "tab")
5355                                           tab3 = e.readInt();
5356                                     else if (t == "idx")
5357                                           idx1 = e.readInt();
5358                                     else if (t == "mag")
5359                                           logicalZoomLevel = e.readDouble();
5360                                     else if (t == "magIdx") {
5361                                           zoomIndex = ZoomIndex(e.readInt());
5362 
5363                                           // The zoom level isn't saved along with the index, so reconstruct it if possible.
5364                                           const auto i = std::find(zoomEntries.cbegin(), zoomEntries.cend(), zoomIndex);
5365                                           if ((i != zoomEntries.cend()) && i->isNumericPreset())
5366                                                 logicalZoomLevel = i->level / 100.0;
5367                                           }
5368                                     else if (t == "x")
5369                                           x = e.readDouble() * DPMM;
5370                                     else if (t == "y")
5371                                           y = e.readDouble() * DPMM;
5372                                     else {
5373                                           e.unknown();
5374                                           return false;
5375                                           }
5376                                     }
5377                               (tab3 == 0 ? tab1 : tab2)->queueInitScoreView(idx1, logicalZoomLevel, zoomIndex, x, y);
5378                               }
5379                         else if (tag == "tab")
5380                               tab = e.readInt();
5381                         else if (tag == "idx")
5382                               idx = e.readInt();
5383                         else {
5384                               e.unknown();
5385                               return false;
5386                               }
5387                         }
5388                   }
5389             else {
5390                   e.unknown();
5391                   return false;
5392                   }
5393             }
5394       setCurrentView(tab, idx);
5395       return true;
5396       }
5397 
5398 //---------------------------------------------------------
5399 //   splitWindow
5400 //---------------------------------------------------------
5401 
splitWindow(bool horizontal)5402 void MuseScore::splitWindow(bool horizontal)
5403       {
5404       if (!_splitScreen) {
5405             if (tab2 == 0) {
5406                   tab2 = createScoreTab();
5407                   splitter->addWidget(tab2);
5408                   }
5409             tab2->setVisible(true);
5410             _splitScreen = true;
5411             _horizontalSplit = horizontal;
5412             splitter->setOrientation(_horizontalSplit ? Qt::Horizontal : Qt::Vertical);
5413             if (!scoreList.isEmpty()) {
5414                   tab2->setCurrentIndex(0);
5415                   MasterScore* s = scoreList[0];
5416                   s->setLayoutAll();
5417                   s->update();
5418                   setCurrentView(1, 0);
5419                   }
5420             emit windowSplit(true);
5421             }
5422       else {
5423             if (_horizontalSplit == horizontal) {
5424                   _splitScreen = false;
5425                   tab2->setVisible(false);
5426                   setCurrentView(0, tab1->currentIndex());
5427                   emit windowSplit(false);
5428                   }
5429             else {
5430                   _horizontalSplit = horizontal;
5431                   splitter->setOrientation(_horizontalSplit ? Qt::Horizontal : Qt::Vertical);
5432                   }
5433             }
5434       getAction("split-h")->setChecked(_splitScreen && _horizontalSplit);
5435       getAction("split-v")->setChecked(_splitScreen && !_horizontalSplit);
5436       }
5437 
5438 //---------------------------------------------------------
5439 //   stateName
5440 //---------------------------------------------------------
5441 
stateName(ScoreState s)5442 const char* stateName(ScoreState s)
5443       {
5444       switch(s) {
5445             case STATE_DISABLED:           return "STATE_DISABLED";
5446             case STATE_NORMAL:             return "STATE_NORMAL";
5447             case STATE_NOTE_ENTRY_STAFF_PITCHED: return "STATE_NOTE_ENTRY_STAFF_PITCHED";
5448             case STATE_NOTE_ENTRY_STAFF_DRUM:    return "STATE_NOTE_ENTRY_STAFF_DRUM";
5449             case STATE_NOTE_ENTRY_STAFF_TAB:     return "STATE_NOTE_ENTRY_STAFF_TAB";
5450             case STATE_NOTE_ENTRY:         return "STATE_NOTE_ENTRY";
5451             case STATE_NOTE_ENTRY_METHOD_STEPTIME:          return "STATE_NOTE_ENTRY_METHOD_STEPTIME";
5452             case STATE_NOTE_ENTRY_METHOD_REPITCH:           return "STATE_NOTE_ENTRY_METHOD_REPITCH";
5453             case STATE_NOTE_ENTRY_METHOD_RHYTHM:            return "STATE_NOTE_ENTRY_METHOD_RHYTHM";
5454             case STATE_NOTE_ENTRY_METHOD_REALTIME_AUTO:     return "STATE_NOTE_ENTRY_METHOD_REALTIME_AUTO";
5455             case STATE_NOTE_ENTRY_METHOD_REALTIME_MANUAL:   return "STATE_NOTE_ENTRY_METHOD_REALTIME_MANUAL";
5456             case STATE_EDIT:               return "STATE_EDIT";
5457             case STATE_TEXT_EDIT:          return "STATE_TEXT_EDIT";
5458             case STATE_LYRICS_EDIT:        return "STATE_LYRICS_EDIT";
5459             case STATE_HARMONY_FIGBASS_EDIT: return "STATE_HARMONY_FIGBASS_EDIT";
5460             case STATE_PLAY:               return "STATE_PLAY";
5461             case STATE_FOTO:               return "STATE_FOTO";
5462             default:                       return "??";
5463             }
5464       }
5465 
5466 //---------------------------------------------------------
5467 //   scorePageLayoutChanged
5468 //---------------------------------------------------------
5469 
scorePageLayoutChanged()5470 void MuseScore::scorePageLayoutChanged()
5471       {
5472       if (mainWindow) {
5473             mainWindow->setOrientation(MScore::verticalOrientation() ? Qt::Horizontal : Qt::Vertical);
5474             if (navigatorScrollArea())
5475                   navigatorScrollArea()->orientationChanged();
5476             }
5477       }
5478 
5479 //---------------------------------------------------------
5480 //   editRaster
5481 //---------------------------------------------------------
5482 
editRaster()5483 void MuseScore::editRaster()
5484       {
5485       if (editRasterDialog == 0) {
5486             editRasterDialog = new EditRaster(this);
5487             }
5488       if (editRasterDialog->exec()) {
5489             qDebug("=====accept config raster");
5490             }
5491       }
5492 
5493 //---------------------------------------------------------
5494 //   showPianoKeyboard
5495 //---------------------------------------------------------
5496 
showPianoKeyboard(bool visible)5497 void MuseScore::showPianoKeyboard(bool visible)
5498       {
5499       if (_pianoTools == 0) {
5500             QAction* a = getAction("toggle-piano");
5501             _pianoTools = new PianoTools(this);
5502             addDockWidget(Qt::BottomDockWidgetArea, _pianoTools);
5503             connect(_pianoTools, SIGNAL(keyPressed(int, bool, int)), SLOT(midiNoteReceived(int, bool, int)));
5504             connect(_pianoTools, SIGNAL(keyReleased(int, bool, int)), SLOT(midiNoteReceived(int, bool, int)));
5505             connect(_pianoTools, SIGNAL(visibilityChanged(bool)), a, SLOT(setChecked(bool)));
5506             }
5507       if (visible) {
5508             reDisplayDockWidget(_pianoTools, visible);
5509             if (currentScore())
5510                   _pianoTools->changeSelection(currentScore()->selection());
5511             else
5512                   _pianoTools->clearSelection();
5513             }
5514       else {
5515             if (_pianoTools)
5516                   _pianoTools->hide();
5517             }
5518       }
5519 
5520 //---------------------------------------------------------
5521 //   showPluginCreator
5522 //---------------------------------------------------------
5523 
showPluginCreator(QAction * a)5524 void MuseScore::showPluginCreator(QAction* a)
5525       {
5526 #ifdef SCRIPT_INTERFACE
5527       bool on = a->isChecked();
5528       if (on) {
5529             if (_pluginCreator == 0) {
5530                   _pluginCreator = new PluginCreator(0);
5531                   connect(_pluginCreator, SIGNAL(closed(bool)), a, SLOT(setChecked(bool)));
5532                   }
5533             _pluginCreator->show();
5534             }
5535       else {
5536             if (_pluginCreator)
5537                   _pluginCreator->hide();
5538             }
5539 #endif
5540       }
5541 
5542 //---------------------------------------------------------
5543 //   showPluginManager
5544 //---------------------------------------------------------
5545 
showPluginManager()5546 void MuseScore::showPluginManager()
5547       {
5548 #ifdef SCRIPT_INTERFACE
5549       pluginManager->init();
5550       pluginManager->show();
5551 #endif
5552       }
5553 
5554 //---------------------------------------------------------
5555 //   showMediaDialog
5556 //---------------------------------------------------------
5557 
showMediaDialog()5558 void MuseScore::showMediaDialog()
5559       {
5560       if (_mediaDialog == 0)
5561             _mediaDialog = new MediaDialog(this);
5562       _mediaDialog->setScore(cs);
5563       _mediaDialog->exec();
5564       }
5565 
5566 //---------------------------------------------------------
5567 //   getPaletteWorkspace
5568 //---------------------------------------------------------
5569 
getPaletteWorkspace()5570 PaletteWorkspace* MuseScore::getPaletteWorkspace()
5571       {
5572       if (!paletteWorkspace) {
5573             PaletteTreeModel* emptyModel = new PaletteTreeModel(new PaletteTree);
5574             PaletteTreeModel* masterPaletteModel = new PaletteTreeModel(MuseScore::newMasterPaletteTree());
5575 
5576             paletteWorkspace = new PaletteWorkspace(emptyModel, masterPaletteModel, /* parent */ this);
5577             emptyModel->setParent(paletteWorkspace);
5578             masterPaletteModel->setParent(paletteWorkspace);
5579 
5580             if (WorkspacesManager::currentWorkspace())
5581                   connect(paletteWorkspace, &PaletteWorkspace::userPaletteChanged, WorkspacesManager::currentWorkspace(), QOverload<>::of(&Workspace::setDirty), Qt::UniqueConnection);
5582             }
5583 
5584       return paletteWorkspace;
5585       }
5586 
5587 //---------------------------------------------------------
5588 //   qmlDockWidgets
5589 //---------------------------------------------------------
5590 
qmlDockWidgets()5591 std::vector<QmlDockWidget*> MuseScore::qmlDockWidgets()
5592       {
5593       return { paletteWidget };
5594       }
5595 
5596 //---------------------------------------------------------
5597 //   midiNoteReceived
5598 //---------------------------------------------------------
5599 
midiNoteReceived(int pitch,bool ctrl,int vel)5600 void MuseScore::midiNoteReceived(int pitch, bool ctrl, int vel)
5601       {
5602       if (cv)
5603             cv->midiNoteReceived(pitch, ctrl, vel);
5604       }
5605 
5606 //---------------------------------------------------------
5607 //   switchLayer
5608 //---------------------------------------------------------
5609 
switchLayer(const QString & s)5610 void MuseScore::switchLayer(const QString& s)
5611       {
5612       if (cs->switchLayer(s)) {
5613             cs->setLayoutAll();
5614             cs->update();
5615             }
5616       }
5617 
5618 //---------------------------------------------------------
5619 //   switchPlayMode
5620 //---------------------------------------------------------
5621 
switchPlayMode(int mode)5622 void MuseScore::switchPlayMode(int mode)
5623       {
5624       if (cs)
5625             cs->setPlayMode(PlayMode(mode));
5626       }
5627 
5628 //---------------------------------------------------------
5629 //   networkFinished
5630 //---------------------------------------------------------
5631 
networkFinished()5632 void MuseScore::networkFinished()
5633       {
5634       QNetworkReply* reply = qobject_cast<QNetworkReply*>(QObject::sender());
5635       if (reply->error() != QNetworkReply::NoError) {
5636             qDebug("Error while checking update [%s]", qPrintable(reply->errorString()));
5637             return;
5638             }
5639       QByteArray ha = reply->rawHeader("Content-Disposition");
5640       QString s(ha);
5641       QString name;
5642       QRegExp re(".*filename=\"(.*)\"");
5643 
5644       if (!s.isEmpty() && re.indexIn(s) != -1) {
5645             name = re.cap(1);
5646              }
5647       else {
5648             QUrl url = reply->url();
5649             QString path = url.path();
5650             qDebug("Path <%s>", qPrintable(path));
5651             QFileInfo fi(path);
5652             name = fi.fileName();
5653             }
5654 
5655       qDebug("header <%s>", qPrintable(s));
5656       qDebug("name <%s>", qPrintable(name));
5657 
5658       QByteArray dta = reply->readAll();
5659       QString tmpName = QDir::tempPath () + "/"+ name;
5660       QFile f(tmpName);
5661       f.open(QIODevice::WriteOnly);
5662       f.write(dta);
5663       f.close();
5664 
5665       reply->deleteLater();
5666 
5667       Score* score = openScore(tmpName);
5668       // delete the temp file created above
5669       QFile::remove(tmpName);
5670       if (!score) {
5671             qDebug("readScore failed");
5672             return;
5673             }
5674       score->setCreated(true);
5675       }
5676 
5677 //---------------------------------------------------------
5678 //   loadFile
5679 //    load file from url
5680 //---------------------------------------------------------
5681 
loadFile(const QString & s)5682 void MuseScore::loadFile(const QString& s)
5683       {
5684       loadFile(QUrl(s));
5685       }
5686 
loadFile(const QUrl & url)5687 void MuseScore::loadFile(const QUrl& url)
5688       {
5689       QEventLoop loop;
5690       QNetworkReply* nr = mscore->networkManager()->get(QNetworkRequest(url));
5691       connect(nr, SIGNAL(finished()), this,
5692                SLOT(networkFinished()));
5693       connect(nr, SIGNAL(finished()), &loop, SLOT(quit()));
5694       loop.exec();
5695       }
5696 
5697 //---------------------------------------------------------
5698 //   networkManager
5699 //---------------------------------------------------------
5700 
networkManager()5701 QNetworkAccessManager* MuseScore::networkManager()
5702       {
5703       if (!_networkManager)
5704             _networkManager = new QNetworkAccessManager(this);
5705       return _networkManager;
5706       }
5707 
5708 //---------------------------------------------------------
5709 //   selectSimilar
5710 //---------------------------------------------------------
5711 
selectSimilar(Element * e,bool sameStaff)5712 void MuseScore::selectSimilar(Element* e, bool sameStaff)
5713       {
5714       Score* score = e->score();
5715       score->selectSimilar(e, sameStaff);
5716 
5717       if (score->selectionChanged()) {
5718             score->setSelectionChanged(false);
5719             SelState ss = score->selection().state();
5720             selectionChanged(ss);
5721             }
5722       }
5723 
selectSimilarInRange(Element * e)5724 void MuseScore::selectSimilarInRange(Element* e)
5725       {
5726       Score* score = e->score();
5727       score->selectSimilarInRange(e);
5728 
5729       if (score->selectionChanged()) {
5730             score->setSelectionChanged(false);
5731             SelState ss = score->selection().state();
5732             selectionChanged(ss);
5733             }
5734       }
5735 
5736 //---------------------------------------------------------
5737 //   selectElementDialog
5738 //---------------------------------------------------------
5739 
selectElementDialog(Element * e)5740 void MuseScore::selectElementDialog(Element* e)
5741       {
5742       Score* score = e->score();
5743       if (e->isNote()) {
5744             Note* n = toNote(e);
5745             SelectNoteDialog sd(n, 0);
5746             // hide same string option if it doesn't make sense for the instrument
5747             if (n->staff()->part()->instrument()->stringData()->strings() == 0)
5748                   sd.setSameStringVisible(false);
5749             if (sd.exec()) {
5750                   NotePattern pattern;
5751                   sd.setPattern(&pattern);
5752 
5753                   if (sd.isInSelection())
5754                         score->scanElementsInRange(&pattern, Score::collectNoteMatch);
5755                   else
5756                         score->scanElements(&pattern, Score::collectNoteMatch);
5757 
5758                   if (sd.doReplace()) {
5759                         score->select(0, SelectType::SINGLE, 0);
5760                         for (Note* ee : pattern.el)
5761                               score->select(ee, SelectType::ADD, 0);
5762                         }
5763                   else if (sd.doSubtract()) {
5764                         QList<Element*> sl(score->selection().elements());
5765                         for (Note* ee : pattern.el)
5766                               sl.removeOne(ee);
5767                         score->select(0, SelectType::SINGLE, 0);
5768                         for (Element* ee : sl)
5769                               score->select(ee, SelectType::ADD, 0);
5770                         }
5771                   else if (sd.doAdd()) {
5772                         QList<Element*> sl(score->selection().elements());
5773                         for (Note* ee : pattern.el) {
5774                               if(!sl.contains(ee))
5775                                     score->select(ee, SelectType::ADD, 0);
5776                               }
5777                         }
5778                   }
5779             }
5780       else {
5781             SelectDialog sd(e, 0);
5782             if (sd.exec()) {
5783                   ElementPattern pattern;
5784                   sd.setPattern(&pattern);
5785 
5786                   if (sd.isInSelection())
5787                         score->scanElementsInRange(&pattern, Score::collectMatch);
5788                   else
5789                         score->scanElements(&pattern, Score::collectMatch);
5790 
5791                   if (sd.doReplace()) {
5792                         score->select(0, SelectType::SINGLE, 0);
5793                         for (Element* ee : pattern.el)
5794                               score->select(ee, SelectType::ADD, 0);
5795                         }
5796                   else if (sd.doSubtract()) {
5797                         QList<Element*> sl(score->selection().elements());
5798                         for (Element* ee : pattern.el)
5799                               sl.removeOne(ee);
5800                         score->select(0, SelectType::SINGLE, 0);
5801                         for (Element* ee : sl)
5802                               score->select(ee, SelectType::ADD, 0);
5803                         }
5804                   else if (sd.doAdd()) {
5805                         QList<Element*> sl(score->selection().elements());
5806                         for (Element* ee : pattern.el) {
5807                               if(!sl.contains(ee))
5808                                     score->select(ee, SelectType::ADD, 0);
5809                               }
5810                         }
5811                   }
5812             }
5813       if (score->selectionChanged()) {
5814             score->setSelectionChanged(false);
5815             SelState ss = score->selection().state();
5816             selectionChanged(ss);
5817             }
5818       }
5819 
5820 //---------------------------------------------------------
5821 //   RecordButton
5822 //---------------------------------------------------------
5823 
RecordButton(QWidget * parent)5824 RecordButton::RecordButton(QWidget* parent)
5825    : SimpleButton(":/data/recordOn.svg", ":/data/recordOff.svg", parent)
5826       {
5827       setCheckable(true);
5828       defaultAction()->setCheckable(true);
5829       setToolTip(qApp->translate("RecordButton", "Record"));
5830       }
5831 
5832 //---------------------------------------------------------
5833 //   GreendotButton
5834 //---------------------------------------------------------
5835 
GreendotButton(QWidget * parent)5836 GreendotButton::GreendotButton(QWidget* parent)
5837    : SimpleButton(":/data/greendot.svg", ":/data/darkgreendot.svg", parent)
5838       {
5839       setCheckable(true);
5840       setToolTip(qApp->translate("GreendotButton", "Record"));
5841       }
5842 
5843 //---------------------------------------------------------
5844 //   drawHandle
5845 //---------------------------------------------------------
5846 
drawHandle(QPainter & p,const QPointF & pos,bool active)5847 QRectF drawHandle(QPainter& p, const QPointF& pos, bool active)
5848       {
5849       p.save();
5850       p.setPen(QPen(QColor(MScore::selectColor[0]), 2.0/p.worldTransform().toAffine().m11()));
5851       if (active)
5852             p.setBrush(MScore::selectColor[0]);
5853       else
5854             p.setBrush(Qt::NoBrush);
5855       qreal w = 8.0 / p.worldTransform().toAffine().m11();
5856       qreal h = 8.0 / p.worldTransform().toAffine().m22();
5857 
5858       QRectF r(-w/2, -h/2, w, h);
5859       r.translate(pos);
5860       p.drawRect(r);
5861       p.restore();
5862       return r;
5863       }
5864 
5865 //---------------------------------------------------------
5866 //   transpose
5867 //---------------------------------------------------------
5868 
transpose()5869 void MuseScore::transpose()
5870       {
5871       if (cs->last() == 0)     // empty score?
5872             return;
5873 
5874       bool noSelection = cs->selection().isNone();
5875       if (noSelection)
5876             cs->cmdSelectAll();
5877       bool rangeSelection = cs->selection().isRange();
5878       TransposeDialog td;
5879 
5880       // TRANSPOSE_TO_KEY and "transpose keys" is only possible if selection state is SelState::RANGE
5881       td.enableTransposeKeys(rangeSelection);
5882       td.enableTransposeToKey(rangeSelection);
5883       td.enableTransposeChordNames(rangeSelection);
5884 
5885       int startStaffIdx = 0;
5886       int endStaffIdx   = 0;
5887       Fraction startTick = Fraction(0,1);
5888       if (rangeSelection) {
5889             startStaffIdx = cs->selection().staffStart();
5890             endStaffIdx   = cs->selection().staffEnd();
5891             startTick     = cs->selection().tickStart();
5892             }
5893 
5894       // find the key of the first pitched staff
5895       Key key = Key::C;
5896       for (int i = startStaffIdx; i < endStaffIdx; ++i) {
5897             Staff* staff = cs->staff(i);
5898             if (staff->isPitchedStaff(startTick)) {
5899                   key = staff->key(startTick);
5900                   if (!cs->styleB(Sid::concertPitch)) {
5901                         int diff = staff->part()->instrument(startTick)->transpose().chromatic;
5902                         if (diff)
5903                               key = transposeKey(key, diff, staff->part()->preferSharpFlat());
5904                         }
5905                   break;
5906                   }
5907             }
5908 
5909       td.setKey(key);
5910       if (!td.exec())
5911             return;
5912 
5913       cs->transpose(td.mode(), td.direction(), td.transposeKey(), td.transposeInterval(),
5914          td.getTransposeKeys(), td.getTransposeChordNames(), td.useDoubleSharpsFlats());
5915 
5916       if (noSelection)
5917             cs->deselectAll();
5918       }
5919 
5920 //---------------------------------------------------------
5921 //   realizeChordSymbols
5922 ///   Realize selected chord symbols into notes on the staff.
5923 ///   Display dialog to offer overrides to default behavior
5924 //---------------------------------------------------------
5925 
realizeChordSymbols()5926 void MuseScore::realizeChordSymbols()
5927       {
5928       if (!cs)
5929             return;
5930       if (!cs->selection().isList()) {
5931             QErrorMessage err;
5932             err.showMessage(tr("Invalid selection. Cannot realize chord symbol"));
5933             err.exec();
5934             return;
5935             }
5936       QList<Harmony*> hlist;
5937       for (Element* e : cs->selection().elements()) {
5938             if (e->isHarmony())
5939                   hlist << toHarmony(e);
5940             }
5941 
5942       RealizeHarmonyDialog dialog;
5943       if (!hlist.empty()) {
5944             dialog.setChordList(hlist);
5945             }
5946       else {
5947             QErrorMessage err;
5948             err.showMessage(tr("No chord symbol selected. Cannot realize chord symbol"));
5949             err.exec();
5950             return;
5951             }
5952 
5953       if (dialog.exec()) {    //realize the chord symbols onto the track
5954             cs->startCmd();
5955             cs->cmdRealizeChordSymbols(dialog.getLiteral(),
5956                                        dialog.optionsOverride() ? Voicing(dialog.getVoicing()) : Voicing::INVALID,
5957                                        dialog.optionsOverride() ? HDuration(dialog.getDuration()) : HDuration::INVALID);
5958             cs->endCmd();
5959             }
5960       }
5961 
5962 
5963 //---------------------------------------------------------
5964 //   cmd
5965 //---------------------------------------------------------
5966 
cmd(QAction * a)5967 void MuseScore::cmd(QAction* a)
5968       {
5969       if (inChordEditor)      // HACK
5970             return;
5971 
5972       MScore::setError(MS_NO_ERROR);
5973 
5974       QString cmdn(a->data().toString());
5975 
5976       if (MScore::debugMode)
5977             qDebug("MuseScore::cmd <%s>", qPrintable(cmdn));
5978 
5979       const Shortcut* sc = Shortcut::getShortcut(cmdn.toLatin1().data());
5980       if (sc == 0) {
5981             qDebug("MuseScore::cmd(): unknown action <%s>", qPrintable(cmdn));
5982             return;
5983             }
5984       if (cs && (sc->state() & _sstate) == 0) {
5985             qDebug("invalid state %04x %04x", sc->state(), _sstate);
5986             QMessageBox::warning(0,
5987                tr("Invalid Command"),
5988                tr("Command %1 not valid in current state").arg(cmdn));
5989             return;
5990             }
5991       if (cmdn == "toggle-palette") {
5992             showPalette(a->isChecked());
5993             if (a->isChecked())
5994                   paletteWidget->activateSearchBox();
5995             else if (cv)
5996                   cv->setFocus();
5997             return;
5998             }
5999       if (cmdn == "palette-search") {
6000             showPalette(true);
6001             if (paletteWidget)
6002                   paletteWidget->activateSearchBox();
6003             return;
6004             }
6005       if (cmdn == "apply-current-palette-element") {
6006             if (paletteWidget)
6007                   paletteWidget->applyCurrentPaletteElement();
6008             return;
6009             }
6010       if (cmdn == "repeat-cmd") {
6011             a  = lastCmd;
6012             sc = lastShortcut;
6013             if (a == 0)
6014                   return;
6015             cmdn = a->data().toString();
6016             }
6017       else {
6018             lastCmd = a;
6019             lastShortcut = sc;
6020             }
6021 
6022       if (sc->needsScore() && ! cs) {
6023             qDebug("no score");
6024             return;
6025             }
6026       if (sc->isCmd()) {
6027             if (!cv->editMode())
6028                   cs->startCmd();
6029             }
6030       cmd(a, cmdn);
6031       if (lastShortcut->isCmd())
6032             cs->endCmd();
6033       else if (!lastShortcut->isUndoRedo()) // undoRedo() calls endCmd() itself
6034             endCmd();
6035       TourHandler::startTour(cmdn);
6036       }
6037 
6038 //---------------------------------------------------------
6039 //   endCmd
6040 //    Called after every command action (including every
6041 //    mouse action).
6042 //    Updates the UI after a possible score change.
6043 //---------------------------------------------------------
6044 
endCmd(bool undoRedo)6045 void MuseScore::endCmd(bool undoRedo)
6046       {
6047 #ifdef SCRIPT_INTERFACE
6048       getPluginEngine()->beginEndCmd(this, undoRedo);
6049 #endif
6050       if (timeline())
6051             timeline()->updateGridFromCmdState();
6052       if (MScore::_error != MS_NO_ERROR)
6053             showError();
6054       if (cs) {
6055             setPos(cs->inputState().tick());
6056             updateInputState(cs);
6057             updateUndoRedo();
6058             dirtyChanged(cs);
6059             scoreCmpTool->updateDiff();
6060             Element* e = cs->selection().element();
6061 
6062             // For multiple notes selected check if they all have same pitch and tuning
6063             bool samePitch = true;
6064             int pitch    = -1;
6065             float tuning = 0;
6066             for (Element* ee : cs->selection().elements()) {
6067                   if (!ee->isNote()) {
6068                         samePitch = false;
6069                         break;
6070                         }
6071                   Note* note = toNote(ee);
6072                   if (pitch == -1) {
6073                         pitch = note->ppitch();
6074                         tuning = note->tuning();
6075                         }
6076                   else if (note->ppitch() != pitch || fabs(tuning - note->tuning()) > 0.01) {
6077                         samePitch = false;
6078                         break;
6079                         }
6080                   }
6081             if (samePitch && pitch >= 0)
6082                   e = cs->selection().elements()[0];
6083 
6084             NoteEntryMethod entryMethod = cs->noteEntryMethod();
6085             if (e && (cs->playNote() || cs->playChord())
6086                         && entryMethod != NoteEntryMethod::REALTIME_AUTO
6087                         && entryMethod != NoteEntryMethod::REALTIME_MANUAL) {
6088                   if (cs->playChord() && preferences.getBool(PREF_SCORE_CHORD_PLAYONADDNOTE) &&  e->type() == ElementType::NOTE)
6089                         play(static_cast<Note*>(e)->chord());
6090                   else
6091                         play(e);
6092                   cs->setPlayNote(false);
6093                   cs->setPlayChord(false);
6094                   }
6095             MasterScore* ms = cs->masterScore();
6096             if (ms->excerptsChanged()) {
6097                   if (tab1) {
6098                         tab1->blockSignals(ctab != tab1);
6099                         tab1->updateExcerpts();
6100                         tab1->blockSignals(false);
6101                         }
6102                   if (tab2) {
6103                         tab2->blockSignals(ctab != tab2);
6104                         tab2->updateExcerpts();
6105                         tab2->blockSignals(false);
6106                         }
6107                   ms->setExcerptsChanged(false);
6108                   }
6109             if (ms->instrumentsChanged()) {
6110                   if (!noSeq && (seq && seq->isRunning()))
6111                         seq->initInstruments();
6112                   instrumentChanged();                // update mixer
6113                   ms->setInstrumentsChanged(false);
6114                   }
6115             if (cs->selectionChanged()) {
6116                   cs->setSelectionChanged(false);
6117                   SelState ss = cs->selection().state();
6118                   selectionChanged(ss);
6119                   }
6120 
6121             if (cv)
6122                   cv->moveViewportToLastEdit();
6123 
6124             getAction("concert-pitch")->setChecked(cs->styleB(Sid::concertPitch));
6125 
6126             if (e == 0 && cs->noteEntryMode())
6127                   e = cs->inputState().cr();
6128             updateViewModeCombo();
6129             ScoreAccessibility::instance()->updateAccessibilityInfo();
6130             }
6131       else {
6132             selectionChanged(SelState::NONE);
6133             }
6134       updateInspector();
6135       updatePaletteBeamMode();
6136 #ifdef SCRIPT_INTERFACE
6137       getPluginEngine()->endEndCmd(this);
6138 #endif
6139       }
6140 
6141 //---------------------------------------------------------
6142 //   updateUndoRedo
6143 //---------------------------------------------------------
6144 
updateUndoRedo()6145 void MuseScore::updateUndoRedo()
6146       {
6147       QAction* a = getAction("undo");
6148       a->setEnabled(cs ? cs->undoStack()->canUndo() : false);
6149       a = getAction("redo");
6150       a->setEnabled(cs ? cs->undoStack()->canRedo() : false);
6151       }
6152 
6153 
setPlayRepeats(bool repeat)6154 void MuseScore::setPlayRepeats(bool repeat)
6155       {
6156       getAction("repeat")->setChecked(repeat);
6157       preferences.setPreference(PREF_APP_PLAYBACK_PLAYREPEATS, repeat);
6158       MScore::playRepeats = repeat;
6159       if (cs) {
6160             cs->masterScore()->setExpandRepeats(repeat);
6161             emit cs->playlistChanged();
6162             }
6163       }
6164 
setPanPlayback(bool pan)6165 void MuseScore::setPanPlayback(bool pan)
6166       {
6167       getAction("pan")->setChecked(pan);
6168       preferences.setPreference(PREF_APP_PLAYBACK_PANPLAYBACK, pan);
6169       MScore::panPlayback = pan;
6170       }
6171 
6172 //---------------------------------------------------------
6173 //   setPlayPartOnly
6174 //---------------------------------------------------------
6175 
setPlayPartOnly(bool val)6176 void MuseScore::setPlayPartOnly(bool val)
6177       {
6178       _playPartOnly = val;
6179       if (cs)
6180             cs->masterScore()->setPlaybackScore(_playPartOnly ? cs : cs->masterScore());
6181       }
6182 
6183 //---------------------------------------------------------
6184 //   createScoreTab
6185 //---------------------------------------------------------
6186 
createScoreTab()6187 ScoreTab* MuseScore::createScoreTab()
6188       {
6189       ScoreTab* tab = new ScoreTab(&scoreList, this);
6190       tab->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
6191       connect(tab, SIGNAL(currentScoreViewChanged(ScoreView*)), SLOT(setCurrentScoreView(ScoreView*)));
6192       connect(tab, SIGNAL(tabCloseRequested(int)), SLOT(removeTab(int)));
6193       connect(tab, SIGNAL(actionTriggered(QAction*)), SLOT(cmd(QAction*)));
6194       return tab;
6195       }
6196 
6197 //---------------------------------------------------------
6198 //   cmd
6199 //---------------------------------------------------------
6200 
cmd(QAction * a,const QString & cmd)6201 void MuseScore::cmd(QAction* a, const QString& cmd)
6202       {
6203       if (ScriptRecorder* rec = getScriptRecorder())
6204             rec->recordCommand(cmd);
6205 
6206       if (cmd == "instruments")
6207             editInstrumentList();
6208       else if (cmd == "rewind") {
6209             if (cs) {
6210                   Fraction tick = loop() ? cs->loopInTick() : Fraction(0,1);
6211                   seq->seek(tick.ticks());
6212                   if (cv) {
6213                         Measure* m = cs->tick2measureMM(tick);
6214                         if (m)
6215                               cv->gotoMeasure(m);
6216                         }
6217                   if (playPanel)
6218                         playPanel->heartBeat(0, 0, 0);
6219                   }
6220             }
6221       else if (cmd == "play-next-measure")
6222             seq->nextMeasure();
6223       else if (cmd == "play-next-chord")
6224             seq->nextChord();
6225       else if (cmd == "play-prev-measure")
6226             seq->prevMeasure();
6227       else if (cmd == "play-prev-chord")
6228             seq->prevChord();
6229       else if (cmd == "seek-begin")
6230             seq->rewindStart();
6231       else if (cmd == "seek-end")
6232             seq->seekEnd();
6233       else if (cmd == "keys")
6234             showKeyEditor();
6235       else if (cmd == "file-new")
6236             newFile();
6237       else if (cmd == "file-open")
6238             openFiles();
6239       else if (cmd == "file-close")
6240             closeScore(cs);
6241       else if (cmd == "file-save")
6242             saveFile();
6243       else if (cmd == "file-save-as")
6244             saveAs(cs, false);
6245       else if (cmd == "file-save-a-copy")
6246             saveAs(cs, true);
6247       else if (cmd == "file-save-selection")
6248             saveSelection(cs);
6249       else if (cmd == saveOnlineMenuItem)
6250             showUploadScoreDialog();
6251       else if (cmd == "file-import-pdf")
6252             importScore();
6253       else if (cmd == "file-export")
6254             showExportDialog();
6255       else if (cmd == "unroll-repeats")
6256             scoreUnrolled(cs->masterScore());
6257       else if (cmd == "quit")
6258             close();
6259       else if (cmd == "masterpalette")
6260             showMasterPalette();
6261       else if (cmd == "key-signatures")
6262             showMasterPalette(qApp->translate("Palette", "Key Signatures"));
6263       else if (cmd == "time-signatures")
6264             showMasterPalette(qApp->translate("Palette", "Time Signatures"));
6265       else if (cmd == "symbols")
6266             showMasterPalette(qApp->translate("MasterPalette", "Symbols"));
6267       else if (cmd == "toggle-statusbar") {
6268             preferences.setPreference(PREF_UI_APP_SHOWSTATUSBAR, a->isChecked());
6269             _statusBar->setVisible(a->isChecked());
6270             }
6271       else if (cmd == "append-measures")
6272             cmdAppendMeasures();
6273       else if (cmd == "insert-measures")
6274             cmdInsertMeasures();
6275       else if (cmd == "debugger")
6276             startDebugger();
6277       else if (cmd == "album")
6278             showAlbumManager();
6279       else if (cmd == "layer" && enableExperimental)
6280             showLayerManager();
6281       else if (cmd == "backspace") {
6282             if (_sstate != STATE_NORMAL )
6283                   undoRedo(true);
6284 #ifdef Q_OS_MAC
6285             else if (cv) {
6286                   cv->cmd(getAction("delete"));
6287                   return;
6288                   }
6289 #endif
6290             }
6291       else if (cmd == "zoomin")
6292             zoomBySteps(1.0);
6293       else if (cmd == "zoomout")
6294             zoomBySteps(-1.0);
6295       else if (cmd == "zoom100")
6296             setZoom(ZoomIndex::ZOOM_100);
6297       else if (cmd == "zoom-page-width")
6298             setZoomWithToggle(ZoomIndex::ZOOM_PAGE_WIDTH);
6299       else if (cmd == "midi-on")
6300             enableMidiIn(a->isChecked());
6301       else if (cmd == "undo")
6302             undoRedo(true);
6303       else if (cmd == "redo")
6304             undoRedo(false);
6305       else if (cmd == "startcenter")
6306             showStartcenter(a->isChecked());
6307       else if (cmd == "inspector")
6308             showInspector(a->isChecked());
6309 #ifdef OMR
6310       else if (cmd == "omr")
6311             showOmrPanel(a->isChecked());
6312 #endif
6313       else if (cmd == "toggle-playpanel")
6314             showPlayPanel(a->isChecked());
6315       else if (cmd == "toggle-navigator")
6316             showNavigator(a->isChecked());
6317       else if (cmd == "toggle-timeline")
6318             showTimeline(a->isChecked());
6319       else if (cmd == "toggle-midiimportpanel")
6320             importmidiPanel->setVisible(a->isChecked());
6321       else if (cmd == "toggle-mixer")
6322             showMixer(a->isChecked());
6323       else if (cmd == "synth-control")
6324             showSynthControl(a->isChecked());
6325       else if (cmd == "toggle-selection-window")
6326             showSelectionWindow(a->isChecked());
6327       else if (cmd == "show-keys")
6328             ;
6329       else if (cmd == "toggle-fileoperations")
6330             fileTools->setVisible(!fileTools->isVisible());
6331       else if (cmd == "toggle-transport")
6332             transportTools->setVisible(!transportTools->isVisible());
6333       else if (cmd == "toggle-concertpitch")
6334             cpitchTools->setVisible(!cpitchTools->isVisible());
6335       else if (cmd == "toggle-imagecapture")
6336             fotoTools->setVisible(!fotoTools->isVisible());
6337       else if (cmd == "toggle-noteinput")
6338             entryTools->setVisible(!entryTools->isVisible());
6339       else if (cmd == "toggle-feedback")
6340             feedbackTools->setVisible(!feedbackTools->isVisible());
6341       else if (cmd == "toggle-workspaces-toolbar")
6342             workspacesTools->setVisible(!workspacesTools->isVisible());
6343       else if (cmd == "create-new-workspace") {
6344             mscore->createNewWorkspace();
6345             emit mscore->workspacesChanged();
6346             }
6347       else if (cmd == "help")
6348             showContextHelp();
6349       else if (cmd == "follow")
6350             preferences.setPreference(PREF_APP_PLAYBACK_FOLLOWSONG, a->isChecked());
6351       else if (cmd == "split-h")
6352             splitWindow(true);
6353       else if (cmd == "split-v")
6354             splitWindow(false);
6355       else if (cmd == "edit-harmony" && enableExperimental)
6356             editChordStyle();
6357       else if (cmd == "parts")
6358             startExcerptsDialog();
6359       else if (cmd == "fullscreen") {
6360             _fullscreen = a->isChecked();
6361             if (_fullscreen)
6362                   showFullScreen();
6363             else
6364                   showNormal();
6365 
6366 #ifdef Q_OS_MAC
6367             // Qt Bug: Toolbar goes into unified mode
6368             // after switching back from fullscreen
6369             setUnifiedTitleAndToolBarOnMac(false);
6370 #endif
6371             }
6372       else if (cmd == "config-raster")
6373             editRaster();
6374       else if (cmd == "hraster" || cmd == "vraster")  // value in [hv]RasterAction already set
6375             ;
6376       else if (cmd == "toggle-piano")
6377             showPianoKeyboard(a->isChecked());
6378       else if (cmd == "toggle-scorecmp-tool")
6379             reDisplayDockWidget(scoreCmpTool, a->isChecked());
6380 #ifdef MSCORE_UNSTABLE
6381       else if (cmd == "toggle-script-recorder")
6382             scriptRecorder->setVisible(a->isChecked());
6383 #endif
6384       else if (cmd == "plugin-creator")
6385             showPluginCreator(a);
6386       else if (cmd == "plugin-manager")
6387             showPluginManager();
6388       else if(cmd == "resource-manager"){
6389             ResourceManager r(0);
6390             r.exec();
6391             }
6392       else if (cmd == "media" && enableExperimental)
6393             showMediaDialog();
6394       else if (cmd == "page-settings")
6395             showPageSettings();
6396       else if (cmd == "next-score")
6397             changeScore(1);
6398       else if (cmd == "previous-score")
6399             changeScore(-1);
6400       else if (cmd == "transpose")
6401             transpose();
6402       else if (cmd == "save-style") {
6403             QString name = getStyleFilename(false);
6404             if (!name.isEmpty()) {
6405                   if (!cs->saveStyle(name)) {
6406                         QMessageBox::critical(this,
6407                            tr("Save Style"), MScore::lastError);
6408                         }
6409                   }
6410             }
6411       else if (cmd == "load-style") {
6412             QString name = mscore->getStyleFilename(true);
6413             if (!name.isEmpty()) {
6414                   cs->startCmd();
6415                   if (!cs->loadStyle(name)) {
6416                         QMessageBox::StandardButton b = QMessageBox::warning(this, tr("Load Style"),
6417                            tr("MuseScore may not be able to load this style file: %1").arg(MScore::lastError),
6418                            QMessageBox::Cancel|QMessageBox::Ignore, QMessageBox::Cancel);
6419                         if (b == QMessageBox::Ignore)
6420                               cs->loadStyle(name, true);
6421                         }
6422                   cs->endCmd();
6423                   }
6424             }
6425       else if (cmd == "edit-style") {
6426             showStyleDialog();
6427             }
6428       else if (cmd == "edit-info") {
6429             MetaEditDialog med(cs, 0);
6430             med.exec();
6431             }
6432       else if (cmd == "print")
6433             printFile();
6434       else if (cmd == "repeat")
6435             setPlayRepeats(a->isChecked());
6436       else if (cmd == "pan")
6437             setPanPlayback(a->isChecked());
6438       else if (cmd == "show-invisible") {
6439             cs->setShowInvisible(a->isChecked());
6440             cs->update();
6441             }
6442       else if (cmd == "show-unprintable") {
6443             cs->setShowUnprintable(a->isChecked());
6444             cs->update();
6445             }
6446       else if (cmd == "show-frames") {
6447             cs->setShowFrames(a->isChecked());
6448             cs->update();
6449             }
6450       else if (cmd == "show-pageborders") {
6451             cs->setShowPageborders(a->isChecked());
6452             cs->update();
6453             }
6454       else if (cmd == "mark-irregular") {
6455             cs->setMarkIrregularMeasures(a->isChecked());
6456             cs->update();
6457             }
6458       else if (cmd == "tempo")
6459             addTempo();
6460       else if (cmd == "loop") {
6461             if (loop()) {
6462                   seq->setLoopSelection();
6463                   }
6464             }
6465       else if (cmd == "loop-in") {
6466             seq->setLoopIn();
6467             loopAction->setChecked(true);
6468             }
6469       else if (cmd == "loop-out") {
6470             seq->setLoopOut();
6471             loopAction->setChecked(true);
6472             }
6473       else if (cmd == "metronome")  // no action
6474             ;
6475       else if (cmd == "countin")    // no action
6476             ;
6477       else if (cmd == "playback-speed-increase") {
6478             createPlayPanel();
6479             playPanel->increaseSpeed();
6480             }
6481       else if (cmd == "playback-speed-decrease") {
6482             createPlayPanel();
6483             playPanel->decreaseSpeed();
6484             }
6485       else if (cmd == "playback-speed-reset") {
6486             createPlayPanel();
6487             playPanel->resetSpeed();
6488             }
6489       else if (cmd == "lock") {
6490             if (_sstate == STATE_LOCK)
6491                   changeState(STATE_NORMAL);
6492             else
6493                   changeState(STATE_LOCK);
6494             }
6495       else if (cmd == "find")
6496             showSearchDialog();
6497       else if (cmd == "text-b") {
6498             if (_textTools)
6499                   _textTools->toggleBold();
6500             }
6501       else if (cmd == "text-i") {
6502             if (_textTools)
6503                   _textTools->toggleItalic();
6504             }
6505       else if (cmd == "text-u") {
6506             if (_textTools)
6507                   _textTools->toggleUnderline();
6508             }
6509       else if (cmd == "edit-toolbars")
6510             showToolbarEditor();
6511       else if (cmd == "viewmode") {
6512             if (cs) {
6513                   if (cs->layoutMode() == LayoutMode::PAGE)
6514                         switchLayoutMode(LayoutMode::LINE);
6515                   else
6516                         switchLayoutMode(LayoutMode::PAGE);
6517                   }
6518             }
6519       else if (cmd == "show-tours")
6520             preferences.setPreference(PREF_UI_APP_STARTUP_SHOWTOURS, a->isChecked());
6521       else if (cmd == "reset-tours")
6522             tourHandler()->resetCompletedTours();
6523       else if (cmd == "report-bug")
6524             reportBug("panel");
6525       else if (cmd == "leave-feedback")
6526             leaveFeedback("panel");
6527 #ifndef NDEBUG
6528       else if (cmd == "no-horizontal-stretch") {
6529             MScore::noHorizontalStretch = a->isChecked();
6530             if (cs) {
6531                   cs->setLayoutAll();
6532                   cs->update();
6533                   }
6534             }
6535       else if (cmd == "no-vertical-stretch") {
6536             MScore::noVerticalStretch = a->isChecked();
6537             if (cs) {
6538                   cs->setLayoutAll();
6539                   cs->update();
6540                   }
6541             }
6542       else if (cmd == "show-segment-shapes") {
6543             MScore::showSegmentShapes = a->isChecked();
6544             if (cs) {
6545                   cs->setLayoutAll();
6546                   cs->update();
6547                   }
6548             }
6549       else if (cmd == "show-skylines") {
6550             MScore::showSkylines = a->isChecked();
6551             if (cs) {
6552                   cs->setLayoutAll();
6553                   cs->update();
6554                   }
6555             }
6556       else if (cmd == "show-bounding-rect") {
6557             MScore::showBoundingRect = a->isChecked();
6558             if (cs) {
6559                   cs->setLayoutAll();
6560                   cs->update();
6561                   }
6562             }
6563       else if (cmd == "show-system-bounding-rect") {
6564             MScore::showSystemBoundingRect = a->isChecked();
6565             if (cs) {
6566                   cs->setLayoutAll();
6567                   cs->update();
6568                   }
6569             }
6570       else if (cmd == "show-corrupted-measures") {
6571             MScore::showCorruptedMeasures = a->isChecked();
6572             if (cs) {
6573                   cs->setLayoutAll();
6574                   cs->update();
6575                   }
6576             }
6577       else if (cmd == "qml-reload-source") {
6578             const QList<QmlDockWidget*> qmlWidgets = findChildren<QmlDockWidget*>();
6579 
6580             const QString oldPrefix = QmlDockWidget::qmlSourcePrefix();
6581             useSourceQmlFiles = true;
6582             const QString newPrefix = QmlDockWidget::qmlSourcePrefix();
6583 
6584             getQmlUiEngine()->clearComponentCache();
6585 
6586             for (QmlDockWidget* w : qmlWidgets) {
6587                   const QString urlString = w->source().toString().replace(oldPrefix, newPrefix);
6588                   w->setSource(QUrl(urlString));
6589                   }
6590             }
6591 #endif
6592       else {
6593             if (cv) {
6594                   //isAncestorOf is called to see if a widget from inspector has focus
6595                   //if so, the focus doesn't get shifted to the score, unless escape is
6596                   //pressed, or the user clicks in the score
6597                   if (!inspector()->isAncestorOf(qApp->focusWidget()) || cmd == "escape")
6598                         cv->setFocus();
6599                   cv->cmd(a);
6600                   }
6601             else
6602                   qDebug("2:unknown cmd <%s>", qPrintable(cmd));
6603             }
6604       if (debugger)
6605             debugger->reloadClicked();
6606       }
6607 
6608 //---------------------------------------------------------
6609 //   openExternalLink
6610 //---------------------------------------------------------
6611 
openExternalLink(const QString & url)6612 void MuseScore::openExternalLink(const QString& url)
6613       {
6614       QDesktopServices::openUrl(url);
6615       }
6616 
6617 //---------------------------------------------------------
6618 //   navigator
6619 //---------------------------------------------------------
6620 
navigator() const6621 Navigator* MuseScore::navigator() const
6622       {
6623       return _navigator ? static_cast<Navigator*>(_navigator->widget()) : 0;
6624       }
6625 
6626 //---------------------------------------------------------
6627 //   timeline
6628 //---------------------------------------------------------
6629 
timeline() const6630 Timeline* MuseScore::timeline() const
6631       {
6632       if (_timeline) {
6633             QSplitter* s = static_cast<QSplitter *>(_timeline->widget());
6634             if (s && s->count() > 0)
6635                   return _timeline ? static_cast<Timeline*>(s->widget(1)) : 0;
6636             return 0;
6637             }
6638       return 0;
6639       }
6640 
6641 //---------------------------------------------------------
6642 //   getScriptRecorder
6643 //---------------------------------------------------------
6644 
getScriptRecorder()6645 ScriptRecorder* MuseScore::getScriptRecorder()
6646       {
6647 #ifdef MSCORE_UNSTABLE
6648       if (scriptRecorder)
6649             return &scriptRecorder->scriptRecorder();
6650 #endif
6651       return nullptr;
6652       }
6653 
6654 //---------------------------------------------------------
6655 //   getSearchDialog
6656 //---------------------------------------------------------
6657 
searchDialog() const6658 QWidget* MuseScore::searchDialog() const
6659       {
6660       return _searchDialog;
6661       }
6662 
6663 //---------------------------------------------------------
6664 //   updateLayer
6665 //---------------------------------------------------------
6666 
updateLayer()6667 void MuseScore::updateLayer()
6668       {
6669       layerSwitch->clear();
6670       bool enable;
6671       if (cs) {
6672             enable = cs->layer().size() > 1;
6673             if (enable) {
6674                   foreach(const Layer& l, cs->layer())
6675                         layerSwitch->addItem(l.name);
6676                   layerSwitch->setCurrentIndex(cs->currentLayer());
6677                   }
6678             }
6679       else
6680            enable = false;
6681       layerSwitch->setVisible(enable);
6682       }
6683 
6684 //---------------------------------------------------------
6685 //   updatePlayMode
6686 //---------------------------------------------------------
6687 
updatePlayMode()6688 void MuseScore::updatePlayMode()
6689       {
6690       bool enable = false;
6691       if (cs) {
6692             enable = cs->audio() != 0;
6693             playMode->setCurrentIndex(int(cs->playMode()));
6694             }
6695       playMode->setVisible(enable);
6696       }
6697 
6698 //---------------------------------------------------------
6699 //   closeScore
6700 //---------------------------------------------------------
6701 
closeScore(Score * score)6702 void MuseScore::closeScore(Score* score)
6703       {
6704       // Let's compute maximum count of ports in remaining scores
6705       if (seq)
6706             seq->recomputeMaxMidiOutPort();
6707       removeTab(scoreList.indexOf(score->masterScore()));
6708       }
6709 
6710 //---------------------------------------------------------
6711 //   noteTooShortForTupletDialog
6712 //---------------------------------------------------------
6713 
noteTooShortForTupletDialog()6714 void MuseScore::noteTooShortForTupletDialog()
6715       {
6716       QMessageBox::warning(this, tr("Warning"),
6717         tr("Cannot create tuplet: Note value is too short")
6718         );
6719       }
6720 
6721 //---------------------------------------------------------
6722 //   instrumentChanged
6723 //---------------------------------------------------------
6724 
instrumentChanged()6725 void MuseScore::instrumentChanged()
6726       {
6727       if (mixer)
6728             mixer->setScore(cs);
6729       }
6730 
6731 //---------------------------------------------------------
6732 //   mixerPreferencesChanged
6733 //---------------------------------------------------------
6734 
mixerPreferencesChanged(bool showMidiControls)6735 void MuseScore::mixerPreferencesChanged(bool showMidiControls)
6736       {
6737       if (mixer)
6738             mixer->midiPrefsChanged(showMidiControls);
6739       }
6740 
6741 //---------------------------------------------------------
6742 //   checkForUpdates
6743 //---------------------------------------------------------
6744 
checkForUpdates()6745 void MuseScore::checkForUpdates()
6746       {
6747 #ifndef MSCORE_NO_UPDATE_CHECKER
6748       if (hasToCheckForUpdate())
6749             checkForUpdatesNoUI();
6750 #endif
6751       }
6752 
6753 //---------------------------------------------------------
6754 //   changeScore
6755 //    switch current score
6756 //    step = 1    switch to next score
6757 //    step = -1   switch to previous score
6758 //---------------------------------------------------------
6759 
changeScore(int step)6760 void MuseScore::changeScore(int step)
6761       {
6762       int index = tab1->currentIndex();
6763       int n     = tab1->count();
6764       if (n == 1)
6765             return;
6766       index += step;
6767       if (index >= n)
6768             index = 0;
6769       if (index < 0)
6770             index = n - 1;
6771       setCurrentScoreView(index);
6772       }
6773 
6774 //---------------------------------------------------------
6775 //   switchLayoutMode
6776 //    val is index in QComboBox viewModeCombo
6777 //---------------------------------------------------------
6778 
switchLayoutMode(int val)6779 void MuseScore::switchLayoutMode(int val)
6780       {
6781       if (!cs)
6782             return;
6783       switchLayoutMode(static_cast<LayoutMode>(viewModeCombo->itemData(val).toInt()));
6784       }
6785 
switchLayoutMode(LayoutMode mode)6786 void MuseScore::switchLayoutMode(LayoutMode mode)
6787       {
6788       // find a measure to use as reference, if possible
6789       QRectF view = cv->toLogical(QRect(0.0, 0.0, width(), height()));
6790       Measure* m = cs->firstMeasureMM();
6791       while (m && !view.intersects(m->canvasBoundingRect()))
6792             m = m->nextMeasureMM();
6793 
6794       cv->loopUpdate(getAction("loop")->isChecked());
6795 
6796       if (mode != cs->layoutMode()) {
6797             cs->setLayoutMode(mode);
6798             cs->doLayout();
6799             }
6800 
6801       // adjustCanvasPosition often tries to preserve Y position
6802       // but this doesn't make sense when switching modes
6803       // also, better positioning is usually achieved if you start from the top
6804       // and there is really no better place to position canvas if we were all the way off page previously
6805       cv->pageTop();
6806       if (m && m != cs->firstMeasureMM())
6807             cv->adjustCanvasPosition(m, false);
6808       if (cv->noteEntryMode())
6809             cv->moveCursor();
6810       }
6811 
6812 //---------------------------------------------------------
6813 //   showDrumTools
6814 //---------------------------------------------------------
6815 
showDrumTools(const Drumset * drumset,Staff * staff)6816 void MuseScore::showDrumTools(const Drumset* drumset, Staff* staff)
6817       {
6818       if (drumset) {
6819             if (!_drumTools) {
6820                   _drumTools = new DrumTools(this);
6821                   addDockWidget(Qt::BottomDockWidgetArea, _drumTools);
6822                   }
6823             if (timelineScrollArea())
6824                   splitDockWidget(_drumTools, timelineScrollArea(), Qt::Vertical);
6825             _drumTools->setDrumset(cs, staff, drumset);
6826             _drumTools->show();
6827             }
6828       else {
6829             if (_drumTools)
6830                   _drumTools->hide();
6831             }
6832       }
6833 
6834 //---------------------------------------------------------
6835 //   updateDrumTools
6836 //---------------------------------------------------------
6837 
updateDrumTools(const Drumset * ds)6838 void MuseScore::updateDrumTools(const Drumset* ds)
6839       {
6840       if (_drumTools)
6841             _drumTools->updateDrumset(ds);
6842       }
6843 
6844 //---------------------------------------------------------
6845 //   endSearch
6846 //---------------------------------------------------------
6847 
endSearch()6848 void MuseScore::endSearch()
6849       {
6850       _searchDialog->hide();
6851       if (cv)
6852             cv->setFocus();
6853       }
6854 
6855 //---------------------------------------------------------
6856 //   showSearchDialog
6857 //---------------------------------------------------------
6858 
showSearchDialog()6859 void MuseScore::showSearchDialog()
6860       {
6861       if (_searchDialog == 0) {
6862             _searchDialog = new QWidget;
6863             _searchDialog->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum);
6864             QHBoxLayout* searchDialogLayout = new QHBoxLayout;
6865             _searchDialog->setLayout(searchDialogLayout);
6866             layout->insertWidget(2, _searchDialog);
6867 
6868             QToolButton* searchExit = new QToolButton;
6869             searchExit->setAutoRaise(true);
6870             searchExit->setIcon(*icons[int(Icons::close_ICON)]);
6871             connect(searchExit, SIGNAL(clicked()), SLOT(endSearch()));
6872             searchDialogLayout->addWidget(searchExit);
6873             searchDialogLayout->addSpacing(10);
6874 
6875             searchDialogLayout->addWidget(new QLabel(tr("Find / Go to:")));
6876 
6877             searchCombo = new SearchComboBox;
6878             searchDialogLayout->addWidget(searchCombo);
6879 
6880             searchDialogLayout->addStretch(10);
6881             _searchDialog->hide();
6882 
6883             qDebug("Line edit %p", searchCombo->lineEdit());
6884 
6885             connect(searchCombo->lineEdit(), SIGNAL(returnPressed()), SLOT(endSearch()));
6886             }
6887 
6888       searchCombo->clearEditText();
6889       searchCombo->setFocus();
6890       _searchDialog->show();
6891       }
6892 
6893 
6894 
6895 #ifndef SCRIPT_INTERFACE
pluginTriggered(int)6896 void MuseScore::pluginTriggered(int) {}
pluginTriggered(QString path)6897 void MuseScore::pluginTriggered(QString path) {}
loadPlugins()6898 void MuseScore::loadPlugins() {}
loadPlugin(const QString &)6899 bool MuseScore::loadPlugin(const QString&) { return false;}
unloadPlugins()6900 void MuseScore::unloadPlugins() {}
6901 #endif
6902 
6903 
saveGeometry(QWidget const * const qw)6904 void MuseScore::saveGeometry(QWidget const*const qw)
6905       {
6906       QSettings settings;
6907       QString objectName = qw->objectName();
6908       Q_ASSERT(!objectName.isEmpty());
6909       settings.beginGroup("Geometries");
6910       settings.setValue(objectName, qw->saveGeometry());
6911       settings.endGroup();
6912       }
6913 
restoreGeometry(QWidget * const qw)6914 void MuseScore::restoreGeometry(QWidget *const qw)
6915       {
6916       if (!useFactorySettings) {
6917             QSettings settings;
6918             QString objectName = qw->objectName();
6919             Q_ASSERT(!objectName.isEmpty());
6920             settings.beginGroup("Geometries");
6921             qw->restoreGeometry(settings.value(objectName).toByteArray());
6922             settings.endGroup();
6923             }
6924       }
6925 
updateWindowTitle(Score * score)6926 void MuseScore::updateWindowTitle(Score* score)
6927       {
6928       if (!score) {
6929             setWindowTitle(MUSESCORE_NAME_VERSION);
6930             setWindowFilePath(QString());
6931             return;
6932             }
6933       QString scoreTitle;
6934       if (score->isMaster())
6935             scoreTitle = score->title();
6936       else
6937             scoreTitle = score->masterScore()->title() + "-" + score->title();
6938 #ifdef Q_OS_MAC
6939       setWindowTitle(scoreTitle);
6940       if (score->masterScore()->created())
6941             setWindowFilePath(QString());
6942       else
6943             setWindowFilePath(score->masterScore()->fileInfo()->absoluteFilePath());
6944 #else
6945       setWindowTitle(MUSESCORE_NAME_VERSION ": " + scoreTitle + "[*]");
6946 #endif
6947       }
6948 
6949 //---------------------------------------------------------
6950 //   recentScores
6951 //    return a list of recent scores
6952 //    omit loaded scores
6953 //---------------------------------------------------------
6954 
recentScores() const6955 QFileInfoList MuseScore::recentScores() const
6956       {
6957       QFileInfoList fil;
6958       for (const QString& s : _recentScores) {
6959             if (s.isEmpty())
6960                   continue;
6961             QFileInfo fi(s);
6962             bool alreadyLoaded = false;
6963             QString fp = fi.canonicalFilePath();
6964             for (Score* sc : mscore->scores()) {
6965                   if ((sc->masterScore()->fileInfo()->canonicalFilePath() == fp) || (sc->importedFilePath() == fp)) {
6966                         alreadyLoaded = true;
6967                         break;
6968                         }
6969                   }
6970             if (!alreadyLoaded && fi.exists())
6971                   fil.append(fi);
6972             }
6973       return fil;
6974       }
6975 
6976 //---------------------------------------------------------
6977 //   createPopupMenu
6978 //---------------------------------------------------------
6979 
createPopupMenu()6980 QMenu* MuseScore::createPopupMenu()
6981       {
6982       QMenu* m = QMainWindow::createPopupMenu();
6983       QList<QAction*> al = m->actions();
6984       for (QAction* a : al) {
6985             // textTool visibility is handled differentlyr
6986             if (_textTools && a->text() == _textTools->windowTitle())
6987                   m->removeAction(a);
6988             }
6989       return m;
6990       }
6991 
6992 //---------------------------------------------------------
6993 //   editInstrumentList
6994 //---------------------------------------------------------
6995 
editInstrumentList()6996 void MuseScore::editInstrumentList()
6997       {
6998       editInstrList();
6999       if (mixer)
7000             mixer->setScore(cs);
7001       }
7002 
7003 //---------------------------------------------------------
7004 //   synthesizerState
7005 //---------------------------------------------------------
7006 
synthesizerState() const7007 SynthesizerState MuseScore::synthesizerState() const
7008       {
7009       SynthesizerState state;
7010       return synti ? synti->state() : state;
7011       }
7012 
7013 //---------------------------------------------------------
7014 //   synthesizer
7015 //---------------------------------------------------------
7016 
synthesizer(const QString & name)7017 Synthesizer* MuseScore::synthesizer(const QString& name)
7018       {
7019       return synti ? synti->synthesizer(name) : nullptr;
7020       }
7021 
7022 //---------------------------------------------------------
7023 //   canSaveMp3
7024 //---------------------------------------------------------
7025 
canSaveMp3()7026 bool MuseScore::canSaveMp3()
7027       {
7028 #ifndef USE_LAME
7029       return false;
7030 #else
7031       MP3Exporter exporter;
7032       if (!exporter.loadLibrary(MP3Exporter::AskUser::NO)) {
7033             qDebug("Could not open MP3 encoding library!");
7034             return false;
7035             }
7036 
7037       if (!exporter.validLibraryLoaded()) {
7038             qDebug("Not a valid or supported MP3 encoding library!");
7039             return false;
7040             }
7041       return true;
7042 #endif
7043       }
7044 
7045 //---------------------------------------------------------
7046 //   saveMp3
7047 //---------------------------------------------------------
7048 
saveMp3(Score * score,const QString & name)7049 bool MuseScore::saveMp3(Score* score, const QString& name)
7050       {
7051       QFile file(name);
7052       if (!file.open(QIODevice::WriteOnly)) {
7053             if (!MScore::noGui) {
7054                   QMessageBox::warning(0,
7055                                        tr("Encoding Error"),
7056                                        tr("Unable to open target file for writing"),
7057                                        QString(), QString());
7058                   }
7059             return false;
7060             }
7061       bool wasCanceled = false;
7062       bool res = saveMp3(score, &file, wasCanceled);
7063       file.close();
7064       if (wasCanceled || !res)
7065             file.remove();
7066       return res;
7067       }
7068 
saveMp3(Score * score,QIODevice * device,bool & wasCanceled)7069 bool MuseScore::saveMp3(Score* score, QIODevice* device, bool& wasCanceled)
7070       {
7071 #ifndef USE_LAME
7072       Q_UNUSED(score);
7073       Q_UNUSED(device);
7074       Q_UNUSED(wasCanceled);
7075       return false;
7076 #else
7077       EventMap events;
7078       // In non-GUI mode current synthesizer settings won't
7079       // allow single note dynamics. See issue #289947.
7080       const bool useCurrentSynthesizerState = !MScore::noGui;
7081 
7082       if (useCurrentSynthesizerState) {
7083             score->renderMidi(&events, synthesizerState());
7084             if (events.empty())
7085                   return false;
7086             }
7087 
7088       MP3Exporter exporter;
7089       if (!exporter.loadLibrary(MP3Exporter::AskUser::MAYBE)) {
7090             QSettings set;
7091             set.setValue("/Export/lameMP3LibPath", "");
7092             if(!MScore::noGui)
7093                   QMessageBox::warning(0,
7094                                tr("Error Opening LAME library"),
7095                                tr("Could not open MP3 encoding library!"),
7096                                QString(), QString());
7097             qDebug("Could not open MP3 encoding library!");
7098             return false;
7099             }
7100 
7101       if (!exporter.validLibraryLoaded()) {
7102             QSettings set;
7103             set.setValue("/Export/lameMP3LibPath", "");
7104             if(!MScore::noGui)
7105                   QMessageBox::warning(0,
7106                                tr("Error Opening LAME library"),
7107                                tr("Not a valid or supported MP3 encoding library!"),
7108                                QString(), QString());
7109             qDebug("Not a valid or supported MP3 encoding library!");
7110             return false;
7111             }
7112 
7113       // Retrieve preferences
7114 //      int highrate = 48000;
7115 //      int lowrate = 8000;
7116 //      int bitrate = 64;
7117 //      int brate = 128;
7118 //      int rmode = MODE_CBR;
7119 //      int vmode = ROUTINE_FAST;
7120 //      int cmode = CHANNEL_STEREO;
7121 
7122       int channels = 2;
7123 
7124       int oldSampleRate = MScore::sampleRate;
7125       int sampleRate = preferences.getInt(PREF_EXPORT_AUDIO_SAMPLERATE);
7126       exporter.setBitrate(preferences.getInt(PREF_EXPORT_MP3_BITRATE));
7127 
7128       int inSamples = exporter.initializeStream(channels, sampleRate);
7129       if (inSamples < 0) {
7130             if (!MScore::noGui) {
7131                   QMessageBox::warning(0, tr("Encoding Error"),
7132                      tr("Unable to initialize MP3 stream"),
7133                      QString(), QString());
7134                   }
7135             qDebug("Unable to initialize MP3 stream");
7136             MScore::sampleRate = oldSampleRate;
7137             return false;
7138             }
7139 
7140       int bufferSize   = exporter.getOutBufferSize();
7141       uchar* bufferOut = new uchar[bufferSize];
7142       MasterSynthesizer* synth = synthesizerFactory();
7143       synth->init();
7144       synth->setSampleRate(sampleRate);
7145 
7146       const SynthesizerState state = useCurrentSynthesizerState ? mscore->synthesizerState() : score->synthesizerState();
7147       const bool setStateOk = synth->setState(state);
7148 
7149       if (!setStateOk || !synth->hasSoundFontsLoaded()) {
7150             synth->init(); // re-initialize master synthesizer with default settings
7151             synth->setSampleRate(sampleRate);
7152       }
7153 
7154       MScore::sampleRate = sampleRate;
7155 
7156       if (!useCurrentSynthesizerState) {
7157             score->masterScore()->rebuildAndUpdateExpressive(synth->synthesizer("Fluid"));
7158             score->renderMidi(&events, score->synthesizerState());
7159             if (synti)
7160                   score->masterScore()->rebuildAndUpdateExpressive(synti->synthesizer("Fluid"));
7161 
7162             if (events.empty())
7163                   return false;
7164             }
7165 
7166       QProgressDialog progress(this);
7167       progress.setWindowFlags(Qt::WindowFlags(Qt::Dialog | Qt::FramelessWindowHint | Qt::WindowTitleHint));
7168       progress.setWindowModality(Qt::ApplicationModal);
7169       //progress.setCancelButton(0);
7170       progress.setCancelButtonText(tr("Cancel"));
7171       progress.setLabelText(tr("Exporting…"));
7172       if (!MScore::noGui)
7173             progress.show();
7174 
7175       static const int FRAMES = 512;
7176       float bufferL[FRAMES];
7177       float bufferR[FRAMES];
7178 
7179       float  peak = 0.0;
7180       double gain = 1.0;
7181       EventMap::const_iterator endPos = events.cend();
7182       --endPos;
7183       const int et = (score->utick2utime(endPos->first) + 1) * MScore::sampleRate;
7184       const int maxEndTime = (score->utick2utime(endPos->first) + 3) * MScore::sampleRate;
7185       progress.setRange(0, et);
7186 
7187       for (int pass = 0; pass < 2; ++pass) {
7188             EventMap::const_iterator playPos;
7189             playPos = events.cbegin();
7190             synth->allSoundsOff(-1);
7191 
7192             //
7193             // init instruments
7194             //
7195             for (Part* part : score->parts()) {
7196                   const InstrumentList* il = part->instruments();
7197                   for (auto i = il->begin(); i!= il->end(); i++) {
7198                         for (const Channel* channel : i->second->channel()) {
7199                               const Channel* a = score->masterScore()->playbackChannel(channel);
7200                               for (MidiCoreEvent e : a->initList()) {
7201                                     if (e.type() == ME_INVALID)
7202                                           continue;
7203                                     e.setChannel(a->channel());
7204                                     int syntiIdx= synth->index(score->masterScore()->midiMapping(a->channel())->articulation()->synti());
7205                                     synth->play(e, syntiIdx);
7206                                     }
7207                               }
7208                         }
7209                   }
7210 
7211             int playTime = 0.0;
7212 
7213             for (;;) {
7214                   unsigned frames = FRAMES;
7215                   float max = 0;
7216                   //
7217                   // collect events for one segment
7218                   //
7219                   memset(bufferL, 0, sizeof(float) * FRAMES);
7220                   memset(bufferR, 0, sizeof(float) * FRAMES);
7221                   double endTime = playTime + frames;
7222 
7223                   float* l = bufferL;
7224                   float* r = bufferR;
7225 
7226                   for (; playPos != events.cend(); ++playPos) {
7227                         double f = score->utick2utime(playPos->first) * MScore::sampleRate;
7228                         if (f >= endTime)
7229                               break;
7230                         int n = f - playTime;
7231                         if (n) {
7232 #if (!defined (_MSCVER) && !defined (_MSC_VER))
7233                               float bu[n * 2];
7234                               memset(bu, 0, sizeof(float) * 2 * n);
7235 #else
7236                               // MSVC does not support VLA. Replace with std::vector. If profiling determines that the
7237                               //    heap allocation is slow, an optimization might be used.
7238                               std::vector<float> vBu(n * 2, 0);   // Default initialized, memset() not required.
7239                               float* bu = vBu.data();
7240 #endif
7241 
7242                               synth->process(n, bu);
7243                               float* sp = bu;
7244                               for (int i = 0; i < n; ++i) {
7245                                     *l++ = *sp++;
7246                                     *r++ = *sp++;
7247                                     }
7248                               playTime  += n;
7249                               frames    -= n;
7250                               }
7251                         const NPlayEvent& e = playPos->second;
7252                         if (!(!e.velo() && e.discard()) && e.isChannelEvent()) {
7253                               int channelIdx = e.channel();
7254                               Channel* c = score->masterScore()->midiMapping(channelIdx)->articulation();
7255                               if (!c->mute()) {
7256                                     synth->play(e, synth->index(c->synti()));
7257                                     }
7258                               }
7259                         }
7260                   if (frames) {
7261 #if (!defined (_MSCVER) && !defined (_MSC_VER))
7262                         float bu[frames * 2];
7263                         memset(bu, 0, sizeof(float) * 2 * frames);
7264 #else
7265                         // MSVC does not support VLA. Replace with std::vector. If profiling determines that the
7266                         //    heap allocation is slow, an optimization might be used.
7267                         std::vector<float> vBu(frames * 2, 0);   // Default initialized, memset() not required.
7268                         float* bu = vBu.data();
7269 #endif
7270                         synth->process(frames, bu);
7271                         float* sp = bu;
7272                         for (unsigned i = 0; i < frames; ++i) {
7273                               *l++ = *sp++;
7274                               *r++ = *sp++;
7275                               }
7276                         playTime += frames;
7277                         }
7278 
7279                   if (pass == 1) {
7280                         for (int i = 0; i < FRAMES; ++i) {
7281                               max = qMax(max, qAbs(bufferL[i]));
7282                               max = qMax(max, qAbs(bufferR[i]));
7283                               bufferL[i] *= gain;
7284                               bufferR[i] *= gain;
7285                               }
7286                         long bytes;
7287                         if (FRAMES < inSamples)
7288                               bytes = exporter.encodeRemainder(bufferL, bufferR,  FRAMES , bufferOut);
7289                         else
7290                               bytes = exporter.encodeBuffer(bufferL, bufferR, bufferOut);
7291                         if (bytes < 0) {
7292                               if (MScore::noGui)
7293                                     qDebug("exportmp3: error from encoder: %ld", bytes);
7294                               else
7295                                     QMessageBox::warning(0,
7296                                        tr("Encoding Error"),
7297                                        tr("Error %1 returned from MP3 encoder").arg(bytes),
7298                                        QString(), QString());
7299                               break;
7300                               }
7301                         else
7302                               device->write((char*)bufferOut, bytes);
7303                         }
7304                   else {
7305                         for (int i = 0; i < FRAMES; ++i) {
7306                               max = qMax(max, qAbs(bufferL[i]));
7307                               max = qMax(max, qAbs(bufferR[i]));
7308                               peak = qMax(peak, qAbs(bufferL[i]));
7309                               peak = qMax(peak, qAbs(bufferR[i]));
7310                               }
7311                         }
7312                   playTime = endTime;
7313                   if (!MScore::noGui) {
7314                         if (progress.wasCanceled())
7315                               break;
7316                         progress.setValue((pass * et + playTime) / 2);
7317                         qApp->processEvents();
7318                         }
7319                   if (playTime >= et)
7320                         synth->allNotesOff(-1);
7321                   // create sound until the sound decays
7322                   if (playTime >= et && max * peak < 0.000001)
7323                         break;
7324                   // hard limit
7325                   if (playTime > maxEndTime)
7326                         break;
7327                   }
7328             if (progress.wasCanceled())
7329                   break;
7330             if (pass == 0 && peak == 0.0) {
7331                   qDebug("song is empty");
7332                   break;
7333                   }
7334             gain = 0.99 / peak;
7335             }
7336 
7337       long bytes = exporter.finishStream(bufferOut);
7338       if (bytes > 0L)
7339             device->write((char*)bufferOut, bytes);
7340       wasCanceled = progress.wasCanceled();
7341       progress.close();
7342       delete synth;
7343       delete[] bufferOut;
7344       MScore::sampleRate = oldSampleRate;
7345       return true;
7346 #endif
7347       }
7348 
7349 #ifndef TELEMETRY_DISABLED
7350 
7351 //---------------------------------------------------------
7352 //   tryToRequestTelemetryPermission
7353 //---------------------------------------------------------
7354 
tryToRequestTelemetryPermission()7355 void tryToRequestTelemetryPermission()
7356       {
7357       static const QString telemetryVersion = "3.4.1";
7358       QString accessRequestedAtVersion = preferences.getString(PREF_APP_STARTUP_TELEMETRY_ACCESS_REQUESTED);
7359 
7360       if (accessRequestedAtVersion == telemetryVersion)
7361             return;
7362 
7363       // Disable telemetry until the permission is explicitly confirmed by user
7364       preferences.setPreference(PREF_APP_TELEMETRY_ALLOWED, false);
7365 
7366       if (MScore::noGui)
7367             return;
7368 
7369       QEventLoop eventLoop;
7370 
7371       TelemetryPermissionDialog *requestDialog = new TelemetryPermissionDialog(mscore->getQmlUiEngine());
7372       QObject::connect(requestDialog, &TelemetryPermissionDialog::closeRequested, [&eventLoop] () {
7373             eventLoop.quit();
7374             });
7375 
7376       requestDialog->show();
7377       eventLoop.exec();
7378       requestDialog->deleteLater();
7379 
7380       preferences.setPreference(PREF_APP_STARTUP_TELEMETRY_ACCESS_REQUESTED, telemetryVersion);
7381       }
7382 #endif
7383 
7384 //---------------------------------------------------------
7385 //   updateUiStyleAndTheme
7386 //---------------------------------------------------------
7387 
updateUiStyleAndTheme()7388 void MuseScore::updateUiStyleAndTheme()
7389       {
7390       // set UI Theme
7391       QApplication::setStyle(QStyleFactory::create("Fusion"));
7392 
7393 #ifdef Q_OS_MAC
7394       // On Mac, update the color of the window title bars
7395       CocoaBridge::setWindowAppearanceIsDark(preferences.isThemeDark());
7396 #endif
7397 
7398 #if defined(WIN_PORTABLE)
7399       QString wd = QDir::cleanPath(QString("%1/../../../Data/%2").arg(QCoreApplication::applicationDirPath()).arg(QCoreApplication::applicationName()));
7400 #else
7401       QString wd = QString("%1/%2").arg(QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation)).arg(QCoreApplication::applicationName());
7402 #endif
7403 
7404       // set UI Color Palette
7405       QPalette p(QApplication::palette());
7406       QString jsonPaletteFilename = preferences.isThemeDark() ? "palette_dark_fusion.json" : "palette_light_fusion.json";;
7407       QFile jsonPalette(QString(":/themes/%1").arg(jsonPaletteFilename));
7408       // read from Documents TODO: remove this
7409       if (QFile::exists(QString("%1/%2").arg(wd, "ms_palette.json")))
7410             jsonPalette.setFileName(QString("%1/%2").arg(wd, "ms_palette.json"));
7411       if (jsonPalette.open(QFile::ReadOnly | QFile::Text)) {
7412             QJsonDocument d = QJsonDocument::fromJson(jsonPalette.readAll());
7413             QJsonObject o = d.object();
7414             QMetaEnum metaEnum = QMetaEnum::fromType<QPalette::ColorRole>();
7415             for (int i = 0; i < metaEnum.keyCount(); ++i) {
7416                   QJsonValue v = o.value(metaEnum.valueToKey(i));
7417                   if (!v.isUndefined())
7418                         p.setColor(static_cast<QPalette::ColorRole>(metaEnum.value(i)), QColor(v.toString()));
7419                   }
7420             }
7421       QApplication::setPalette(p);
7422 
7423       // set UI Style
7424       QString css;
7425       QString styleFilename = preferences.isThemeDark() ? "style_dark_fusion.css" : "style_light_fusion.css";
7426       QFile fstyle(QString(":/themes/%1").arg(styleFilename));
7427       // read from Documents TODO: remove this
7428       if (QFile::exists(QString("%1/%2").arg(wd, "ms_style.css")))
7429             fstyle.setFileName(QString("%1/%2").arg(wd, "ms_style.css"));
7430       if (fstyle.open(QFile::ReadOnly | QFile::Text)) {
7431             QTextStream in(&fstyle);
7432             css = in.readAll();
7433             }
7434 
7435       css.replace("$voice1-bgcolor", MScore::selectColor[0].name(QColor::HexRgb));
7436       css.replace("$voice2-bgcolor", MScore::selectColor[1].name(QColor::HexRgb));
7437       css.replace("$voice3-bgcolor", MScore::selectColor[2].name(QColor::HexRgb));
7438       css.replace("$voice4-bgcolor", MScore::selectColor[3].name(QColor::HexRgb));
7439 
7440       if (preferences.isThemeDark()) {
7441             css.replace("$buttonHighlightDisabledOff", Ms::preferences.getColor(PREF_UI_BUTTON_HIGHLIGHT_COLOR_DISABLED_DARK_OFF).name().toLatin1());
7442             css.replace("$buttonHighlightDisabledOn", Ms::preferences.getColor(PREF_UI_BUTTON_HIGHLIGHT_COLOR_DISABLED_DARK_ON).name().toLatin1());
7443             css.replace("$buttonHighlightEnabledOff", Ms::preferences.getColor(PREF_UI_BUTTON_HIGHLIGHT_COLOR_ENABLED_DARK_OFF).name().toLatin1());
7444             css.replace("$buttonHighlightEnabledOn", Ms::preferences.getColor(PREF_UI_BUTTON_HIGHLIGHT_COLOR_ENABLED_DARK_ON).name().toLatin1());
7445             }
7446       else {
7447             css.replace("$buttonHighlightDisabledOff", Ms::preferences.getColor(PREF_UI_BUTTON_HIGHLIGHT_COLOR_DISABLED_LIGHT_OFF).name().toLatin1());
7448             css.replace("$buttonHighlightDisabledOn", Ms::preferences.getColor(PREF_UI_BUTTON_HIGHLIGHT_COLOR_DISABLED_LIGHT_ON).name().toLatin1());
7449             css.replace("$buttonHighlightEnabledOff", Ms::preferences.getColor(PREF_UI_BUTTON_HIGHLIGHT_COLOR_ENABLED_LIGHT_OFF).name().toLatin1());
7450             css.replace("$buttonHighlightEnabledOn", Ms::preferences.getColor(PREF_UI_BUTTON_HIGHLIGHT_COLOR_ENABLED_LIGHT_ON).name().toLatin1());
7451             }
7452 
7453       qApp->setStyleSheet(css);
7454 
7455       QString style = QString("*, QSpinBox { font: %1pt \"%2\" } ")
7456                   .arg(QString::number(preferences.getDouble(PREF_UI_THEME_FONTSIZE)), preferences.getString(PREF_UI_THEME_FONTFAMILY))
7457                   + qApp->styleSheet();
7458       qApp->setStyleSheet(style);
7459 
7460       genIcons();
7461       Shortcut::refreshIcons();
7462       }
7463 
7464 //---------------------------------------------------------
7465 //   fullVersion
7466 ///   \return Full version of MuseScore, including version
7467 ///   label (e.g. 3.4.0-Beta).
7468 //---------------------------------------------------------
7469 
fullVersion()7470 QString MuseScore::fullVersion()
7471       {
7472       QString version(VERSION);
7473       QString versionLabel(VERSION_LABEL);
7474       versionLabel = versionLabel.replace(' ', "");
7475       if (!versionLabel.isEmpty())
7476             version.append("-").append(versionLabel);
7477       return version;
7478       }
7479 
7480 //---------------------------------------------------------
7481 //   initApplication
7482 //---------------------------------------------------------
7483 
initApplication(int & argc,char ** argv)7484 MuseScoreApplication* MuseScoreApplication::initApplication(int& argc, char** argv)
7485       {
7486       revision = QString(MUSESCORE_REVISION).trimmed();
7487 
7488       const char* appName;
7489       const char* appName2;
7490       if (MuseScore::unstable()) {
7491             appName2 = "mscore-dev3";
7492             appName  = "MuseScore3Development";
7493             }
7494       else {
7495             appName2 = "mscore3";
7496             appName  = "MuseScore3";
7497             }
7498 
7499       //! NOTE Disable cache for all platforms
7500       //! Enabled qml cache on MacOS BigSur on ARM leads to a crash at the start
7501       //! There were also crash problems on some Linux platforms when upgrading
7502       //! In general, the Qml cache doesn't really matter.
7503       //! Therefore, it is better to turn it off.
7504       qputenv("QML_DISABLE_DISK_CACHE", "true");
7505 
7506       MuseScoreApplication* app = new MuseScoreApplication(appName2, argc, argv);
7507       QCoreApplication::setApplicationName(appName);
7508 
7509       QCoreApplication::setOrganizationName("MuseScore");
7510       QCoreApplication::setOrganizationDomain("musescore.org");
7511       QCoreApplication::setApplicationVersion(MuseScore::fullVersion());
7512 
7513 #ifdef BUILD_CRASH_REPORTER
7514       {
7515       static_assert(sizeof(CRASHREPORTER_EXECUTABLE) > 1,
7516          "CRASHREPORTER_EXECUTABLE should be defined to build with crash reporter"
7517          );
7518       CrashReporter::Handler* crashHandler = new CrashReporter::Handler(QDir::tempPath(), true, CRASHREPORTER_EXECUTABLE);
7519       QObject::connect(app, &QCoreApplication::aboutToQuit, [crashHandler]() { delete crashHandler; });
7520       }
7521 #endif
7522 
7523       return app;
7524       }
7525 
7526 //---------------------------------------------------------
7527 //   setCustomConfigFolder
7528 //---------------------------------------------------------
7529 
setCustomConfigFolder(const QString & path)7530 bool MuseScoreApplication::setCustomConfigFolder(const QString& path)
7531       {
7532       const QFileInfo pinfo(path);
7533 
7534       if (pinfo.exists() && pinfo.isDir()) {
7535             QSettings::setPath(QSettings::IniFormat, QSettings::UserScope, path);
7536             QSettings::setPath(QSettings::IniFormat, QSettings::SystemScope, path);
7537             dataPath = path;
7538             return true;
7539             }
7540 
7541       return false;
7542       }
7543 
7544 //---------------------------------------------------------
7545 //   parseCommandLineArguments
7546 //---------------------------------------------------------
7547 
parseCommandLineArguments(MuseScoreApplication * app)7548 MuseScoreApplication::CommandLineParseResult MuseScoreApplication::parseCommandLineArguments(MuseScoreApplication* app)
7549       {
7550       QCommandLineParser parser;
7551       CommandLineParseResult parseResult;
7552 
7553       parser.addHelpOption(); // -?, -h, --help
7554       parser.addVersionOption(); // -v, --version
7555 
7556     //parser.addOption(QCommandLineOption({"v", "version"}, "Print version")); // see above
7557       parser.addOption(QCommandLineOption(      "long-version", "Print detailed version information"));
7558       parser.addOption(QCommandLineOption({"d", "debug"}, "Debug mode"));
7559       parser.addOption(QCommandLineOption({"L", "layout-debug"}, "Layout debug mode"));
7560       parser.addOption(QCommandLineOption({"s", "no-synthesizer"}, "No internal synthesizer"));
7561       parser.addOption(QCommandLineOption({"m", "no-midi"}, "No MIDI"));
7562       parser.addOption(QCommandLineOption({"a", "use-audio"}, "Use audio driver: jack, alsa, pulse, or portaudio", "driver"));
7563       parser.addOption(QCommandLineOption({"n", "new-score"}, "Start with new score"));
7564       parser.addOption(QCommandLineOption({"I", "dump-midi-in"}, "Dump midi input"));
7565       parser.addOption(QCommandLineOption({"O", "dump-midi-out"}, "Dump midi output"));
7566       parser.addOption(QCommandLineOption({"o", "export-to"}, "Export to 'file'. Format depends on file's extension", "file"));
7567       parser.addOption(QCommandLineOption({"r", "image-resolution"}, "Use with '-o <file>.png'. Set output resolution for image export", "DPI"));
7568       parser.addOption(QCommandLineOption({"T", "trim-image"}, "Use with '-o <file>.png' and '-o <file.svg>'. Trim exported image with specified margin (in pixels)", "margin"));
7569       parser.addOption(QCommandLineOption({"x", "gui-scaling"}, "Set scaling factor for GUI elements", "factor"));
7570       parser.addOption(QCommandLineOption({"D", "monitor-resolution"}, "Specify monitor resolution", "DPI"));
7571       parser.addOption(QCommandLineOption({"S", "style"}, "Load style file", "style"));
7572       parser.addOption(QCommandLineOption({"p", "plugin"}, "Execute named plugin", "name"));
7573       parser.addOption(QCommandLineOption(      "template-mode", "Save template mode, no page size")); // and no platform and creationDate tags
7574       parser.addOption(QCommandLineOption({"F", "factory-settings"}, "Use factory settings"));  // this includes -R, --revert-settimngs
7575       parser.addOption(QCommandLineOption({"R", "revert-settings"}, "Revert to factory settings, but keep default preferences"));
7576       parser.addOption(QCommandLineOption({"i", "load-icons"}, "Load icons from INSTALLPATH/icons"));
7577       parser.addOption(QCommandLineOption({"j", "job"}, "Process a conversion job", "file"));
7578       parser.addOption(QCommandLineOption({"e", "experimental"}, "Enable experimental features"));
7579       parser.addOption(QCommandLineOption({"c", "config-folder"}, "Override configuration and settings folder", "dir"));
7580       parser.addOption(QCommandLineOption({"t", "test-mode"}, "Set test mode flag for all files")); // this includes --template-mode
7581       parser.addOption(QCommandLineOption(      "run-test-script", "Run script tests listed in the command line arguments"));
7582       parser.addOption(QCommandLineOption({"M", "midi-operations"}, "Specify MIDI import operations file", "file"));
7583       parser.addOption(QCommandLineOption({"w", "no-webview"}, "No web view in start center"));
7584       parser.addOption(QCommandLineOption({"P", "export-score-parts"}, "Use with '-o <file>.pdf', export score and parts"));
7585       parser.addOption(QCommandLineOption(      "no-fallback-font", "Don't use a fallback musical font"));
7586       parser.addOption(QCommandLineOption({"f", "force"}, "Use with '-o <file>', ignore warnings reg. score being corrupted or from wrong version"));
7587       parser.addOption(QCommandLineOption({"b", "bitrate"}, "Use with '-o <file>.mp3', sets bitrate, in kbps", "bitrate"));
7588       parser.addOption(QCommandLineOption({"E", "install-extension"}, "Install an extension, load soundfont as default unless -e is passed too", "extension file"));
7589       parser.addOption(QCommandLineOption(      "save-online", "Upload score(s) to their source URL. Replaces existing online score(s)."));
7590       parser.addOption(QCommandLineOption(      "score-media", "Export all media (excepting mp3) for a given score in a single JSON file and print it to stdout"));
7591       parser.addOption(QCommandLineOption(      "highlight-config", "Set highlight to svg, generated from a given score", "highlight-config"));
7592       parser.addOption(QCommandLineOption(      "score-meta", "Export score metadata to JSON document and print it to stdout"));
7593       parser.addOption(QCommandLineOption(      "score-mp3", "Generate mp3 for the given score and export the data to a single JSON file, print it to stdout"));
7594       parser.addOption(QCommandLineOption(      "score-parts", "Generate parts data for the given score and save them to separate mscz files"));
7595       parser.addOption(QCommandLineOption(      "score-parts-pdf", "Generate parts data for the given score and export the data to a single JSON file, print it to stdout"));
7596       parser.addOption(QCommandLineOption(      "score-transpose", "Transpose the given score and export the data to a single JSON file, print it to stdout", "options"));
7597       parser.addOption(QCommandLineOption(      "source-update", "Update the source in the given score"));
7598       parser.addOption(QCommandLineOption(      "raw-diff", "Print a raw diff for the given scores"));
7599       parser.addOption(QCommandLineOption(      "diff", "Print a diff for the given scores"));
7600 
7601       parser.addPositionalArgument("scorefiles", "The files to open", "[scorefile...]");
7602 
7603       parser.process(QCoreApplication::arguments());
7604 
7605     //if (parser.isSet("v")) parser.showVersion(); // a) needs Qt >= 5.4 , b) instead we use addVersionOption()
7606       if (parser.isSet("long-version")) {
7607             printVersion("MuseScore");
7608             parseResult.exit = true;
7609             return parseResult;
7610             }
7611       MScore::debugMode = parser.isSet("d");
7612       MScore::noHorizontalStretch = MScore::noVerticalStretch = parser.isSet("L");
7613       noSeq = parser.isSet("s");
7614       noMidi = parser.isSet("m");
7615       if (parser.isSet("a")) {
7616             audioDriver = parser.value("a");
7617             if (audioDriver.isEmpty())
7618                   parser.showHelp(EXIT_FAILURE);
7619             }
7620       startWithNewScore = parser.isSet("n");
7621       externalIcons = parser.isSet("i");
7622       midiInputTrace = parser.isSet("I");
7623       midiOutputTrace = parser.isSet("O");
7624       MScore::useFallbackFont = !parser.isSet("no-fallback-font");
7625 
7626       if ((converterMode = parser.isSet("o"))) {
7627             MScore::noGui = true;
7628             outFileName = parser.value("o");
7629             if (outFileName.isEmpty())
7630                   parser.showHelp(EXIT_FAILURE);
7631             }
7632       if ((processJob = parser.isSet("j"))) {
7633             MScore::noGui = true;
7634             converterMode = true;
7635             jsonFileName = parser.value("j");
7636             if (jsonFileName.isEmpty()) {
7637                   fprintf(stderr, "json file name missing\n");
7638                   parser.showHelp(EXIT_FAILURE);
7639                   }
7640             }
7641       if ((pluginMode = parser.isSet("p"))) {
7642             MScore::noGui = true;
7643             pluginName = parser.value("p");
7644             if (pluginName.isEmpty())
7645                   parser.showHelp(EXIT_FAILURE);
7646             }
7647       if (parser.isSet("E")) {
7648             MScore::noGui = true;
7649             extensionName = parser.value("E");
7650             }
7651       MScore::saveTemplateMode = parser.isSet("template-mode");
7652       if (parser.isSet("r")) {
7653             QString temp = parser.value("r");
7654             if (temp.isEmpty())
7655                    parser.showHelp(EXIT_FAILURE);
7656             bool ok = false;
7657             double res = temp.toDouble(&ok);
7658             if (ok)
7659                   preferences.setTemporaryPreference(PREF_EXPORT_PNG_RESOLUTION, res);
7660             else
7661                   fprintf(stderr, "PNG resolution value '%s' not recognized, using default setting from preferences instead.\n", qPrintable(temp));
7662             }
7663       if (parser.isSet("T")) {
7664             QString temp = parser.value("T");
7665             if (temp.isEmpty())
7666                    parser.showHelp(EXIT_FAILURE);
7667             bool ok = false;
7668             trimMargin = temp.toInt(&ok);
7669             if (!ok) {
7670                   fprintf(stderr, "Trim margin value '%s' not recognized, so no trimming will be done.\n", qPrintable(temp));
7671                   trimMargin = -1;
7672                   }
7673            }
7674       if (parser.isSet("x")) {
7675             QString temp = parser.value("x");
7676             if (temp.isEmpty())
7677                    parser.showHelp(EXIT_FAILURE);
7678             bool ok = false;
7679             guiScaling = temp.toDouble(&ok);
7680             if (!ok) {
7681                   fprintf(stderr, "GUI scaling value '%s' not recognized, so the values detected by Qt are taken.\n", qPrintable(temp));
7682                   guiScaling = 0.0;
7683                   }
7684             }
7685       if (parser.isSet("D")) {
7686             QString temp = parser.value("D");
7687             if (temp.isEmpty())
7688                    parser.showHelp(EXIT_FAILURE);
7689             bool ok = false;
7690             userDPI = temp.toDouble(&ok);
7691             if (!ok) {
7692                   fprintf(stderr, "DPI value '%s' not recognized, so the values detected by Qt are taken.\n", qPrintable(temp));
7693                   userDPI = 0.0;
7694                   }
7695             }
7696       if (parser.isSet("S")) {
7697             styleFile = parser.value("S");
7698             if (styleFile.isEmpty())
7699                   parser.showHelp(EXIT_FAILURE);
7700             }
7701       deletePreferences = parser.isSet("F");
7702       useFactorySettings = (deletePreferences || parser.isSet("R"));
7703       enableExperimental = parser.isSet("e");
7704       if (parser.isSet("c")) {
7705             QString path = parser.value("c");
7706             if (path.isEmpty())
7707                   parser.showHelp(EXIT_FAILURE);
7708             setCustomConfigFolder(path);
7709             }
7710       MScore::testMode = parser.isSet("t");
7711       if (parser.isSet("M")) {
7712             QString temp = parser.value("M");
7713             if (temp.isEmpty())
7714                   parser.showHelp(EXIT_FAILURE);
7715             midiImportOperations.setOperationsFile(temp);
7716             }
7717       noWebView = parser.isSet("w");
7718       exportScoreParts = parser.isSet("export-score-parts");
7719       if (exportScoreParts && !converterMode)
7720             parser.showHelp(EXIT_FAILURE);
7721       ignoreWarnings = parser.isSet("f");
7722       if (parser.isSet("b")) {
7723             QString temp = parser.value("b");
7724             if (temp.isEmpty())
7725                    parser.showHelp(EXIT_FAILURE);
7726             bool ok = false;
7727             int rate = temp.toInt(&ok);
7728             if (ok)
7729                   preferences.setTemporaryPreference(PREF_EXPORT_MP3_BITRATE, rate);
7730             else
7731                   fprintf(stderr, "MP3 bitrate value '%s' not recognized, using default setting from preferences instead.\n", qPrintable(temp));
7732            }
7733 
7734       if (parser.isSet("save-online")) {
7735             cliSaveOnline = true;
7736             MScore::noGui = true;
7737 
7738             if (parser.positionalArguments().isEmpty()) {
7739                   fprintf(stderr, "%s\n", qPrintable(tr("Must specify at least one score to save online.")));
7740                   ::exit(EXIT_FAILURE);
7741             }
7742       }
7743 
7744       if (parser.isSet("score-media")) {
7745             exportScoreMedia = true;
7746             MScore::noGui = true;
7747             converterMode = true;
7748 
7749             if (parser.isSet("highlight-config")) {
7750                 highlightConfigPath = parser.value("highlight-config");
7751             }
7752       }
7753 
7754       if (parser.isSet("score-meta")) {
7755             exportScoreMeta = true;
7756             MScore::noGui = true;
7757             converterMode = true;
7758             }
7759 
7760       if (parser.isSet("score-mp3")) {
7761             exportScoreMp3 = true;
7762             MScore::noGui = true;
7763             converterMode = true;
7764             }
7765 
7766       if (parser.isSet("score-parts")) {
7767             saveScoreParts = true;
7768             MScore::noGui = true;
7769             converterMode = true;
7770             }
7771 
7772       if (parser.isSet("score-parts-pdf")) {
7773             exportScorePartsPdf = true;
7774             MScore::noGui = true;
7775             converterMode = true;
7776             }
7777 
7778       if (parser.isSet("score-transpose")) {
7779             exportTransposedScore = true;
7780             transposeExportOptions = parser.value("score-transpose");
7781             MScore::noGui = true;
7782             converterMode = true;
7783             }
7784 
7785       if (parser.isSet("source-update")) {
7786             MScore::noGui = true;
7787             needUpdateSource = true;
7788             }
7789 
7790       if (parser.isSet("raw-diff")) {
7791             MScore::noGui = true;
7792             rawDiffMode = true;
7793             }
7794       if (parser.isSet("diff")) {
7795             MScore::noGui = true;
7796             diffMode = true;
7797             }
7798       if (parser.isSet("run-test-script")) {
7799             if (rawDiffMode || diffMode) {
7800                   fprintf(stderr, "%s\n", qPrintable(tr("--run-test-script is incompatible with --diff and --raw-diff")));
7801                   ::exit(EXIT_FAILURE);
7802                   }
7803             MScore::noGui = true;
7804             scriptTestMode = true;
7805             }
7806 
7807       QStringList argv = parser.positionalArguments();
7808 
7809       if (app && !converterMode && !pluginMode) {
7810             if (!argv.isEmpty()) {
7811                   int ok = true;
7812                   for (const QString& message : argv) {
7813                         QFileInfo fi(message);
7814                         if (!app->sendMessage(fi.absoluteFilePath())) {
7815                               ok = false;
7816                               break;
7817                               }
7818 
7819                         }
7820                   if (ok) {
7821                         parseResult.exit = true;
7822                         return parseResult;
7823                         }
7824                   }
7825             else
7826                   if (app->sendMessage(QString(""))) {
7827                         parseResult.exit = true;
7828                         return parseResult;
7829                         }
7830             }
7831       if (rawDiffMode || diffMode) {
7832             if (argv.size() != 2) {
7833                   fprintf(stderr, "%s\n", qPrintable(tr("Only two scores are needed for performing a comparison")));
7834                   ::exit(EXIT_FAILURE);
7835                   }
7836             }
7837       if (scriptTestMode && argv.empty()) {
7838             fprintf(stderr, "%s\n", qPrintable(tr("Please specify scripts to execute")));
7839             ::exit(EXIT_FAILURE);
7840             }
7841 
7842       parseResult.argv = argv;
7843       return parseResult;
7844       }
7845 } // namespace Ms
7846 
7847 //---------------------------------------------------------
7848 //   initZitaResources
7849 ///   must be placed outside of namespace
7850 //---------------------------------------------------------
7851 
initZitaResources()7852 static void initZitaResources()
7853       {
7854       Q_INIT_RESOURCE(zita);
7855       }
7856 
7857 namespace Ms {
7858 //---------------------------------------------------------
7859 //   runApplication
7860 //---------------------------------------------------------
7861 
runApplication(int & argc,char ** av)7862 int runApplication(int& argc, char** av)
7863       {
7864 #ifndef NDEBUG
7865       qSetMessagePattern("%{file}:%{function}: %{message}");
7866       Ms::checkStyles();
7867 #endif
7868 
7869       QApplication::setDesktopSettingsAware(true);
7870 #ifdef Q_OS_LINUX
7871       QGuiApplication::setDesktopFileName("mscore");
7872 #endif
7873       QApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
7874       QApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
7875 #if defined(QT_DEBUG) && defined(Q_OS_WIN)
7876       qInstallMessageHandler(mscoreMessageHandler);
7877 #endif
7878 
7879 #ifdef Q_OS_WIN
7880       if (!qEnvironmentVariableIsSet("QT_OPENGL_BUGLIST")) {
7881             // Set custom OpenGL buglist to work around rendering issues
7882             // on Windows 7 (#296682). This should also prevent crashes
7883             // happening for some Intel GPUs due to outdated buglist in Qt 5.9.
7884             qputenv("QT_OPENGL_BUGLIST", ":/data/win_opengl_buglist.json");
7885             }
7886 #endif
7887 
7888       qRegisterMetaTypeStreamOperators<SessionStart>("SessionStart");
7889       qRegisterMetaTypeStreamOperators<MusicxmlExportBreaks>("MusicxmlExportBreaks");
7890       qRegisterMetaTypeStreamOperators<MuseScorePreferredStyleType>("MuseScorePreferredStyleType");
7891       qRegisterMetaTypeStreamOperators<MuseScoreEffectiveStyleType>("MuseScoreEffectiveStyleType");
7892 
7893       MuseScoreApplication* app = MuseScoreApplication::initApplication(argc, av);
7894 
7895       QAccessible::installFactory(AccessibleScoreView::ScoreViewFactory);
7896       QAccessible::installFactory(AccessibleSearchBox::SearchBoxFactory);
7897       QAccessible::installFactory(Awl::AccessibleAbstractSlider::AbstractSliderFactory);
7898 
7899       initZitaResources();
7900 
7901 #ifndef Q_OS_MAC
7902       QSettings::setDefaultFormat(QSettings::IniFormat);
7903 #endif
7904 
7905       auto cmdLineParseResult = MuseScoreApplication::parseCommandLineArguments(app);
7906 
7907       if (cmdLineParseResult.exit)
7908             return 0;
7909 
7910       MuseScore::init(cmdLineParseResult.argv);
7911 
7912       if (MScore::noGui) {
7913 #ifdef Q_OS_MAC
7914             // see issue #28706: Hangup in converter mode with MusicXML source
7915             qApp->processEvents();
7916 #endif
7917             const bool ok = processNonGui(cmdLineParseResult.argv);
7918             return ok ? EXIT_SUCCESS : EXIT_FAILURE;
7919             }
7920 
7921       QDir::setCurrent(app->applicationDirPath());
7922 
7923       return qApp->exec();
7924       }
7925 
7926 //---------------------------------------------------------
7927 //   showSplashMessage
7928 //---------------------------------------------------------
7929 
showSplashMessage(MsSplashScreen * sc,QString && message)7930 inline static void showSplashMessage(MsSplashScreen* sc, QString&& message)
7931       {
7932       if (sc)
7933             sc->showMessage(message);
7934       else
7935             qInfo("%s", qPrintable(message));
7936       }
7937 
7938 //---------------------------------------------------------
7939 //   init
7940 //---------------------------------------------------------
7941 
init(QStringList & argv)7942 void MuseScore::init(QStringList& argv)
7943       {
7944       mscoreGlobalShare = getSharePath();
7945       iconPath = externalIcons ? mscoreGlobalShare + QString("icons/") :  QString(":/data/icons/");
7946 
7947 #if defined(WIN_PORTABLE)
7948       if (dataPath.isEmpty()) {
7949             dataPath = QDir::cleanPath(QString("%1/../../../Data/settings").arg(QCoreApplication::applicationDirPath()).arg(QCoreApplication::applicationName()));
7950             QSettings::setPath(QSettings::IniFormat, QSettings::UserScope, dataPath);
7951             QSettings::setPath(QSettings::IniFormat, QSettings::SystemScope, dataPath);
7952             }
7953 #else
7954       if (dataPath.isEmpty())
7955             dataPath = QStandardPaths::writableLocation(QStandardPaths::DataLocation);
7956 #endif
7957 
7958       if (useFactorySettings) {
7959             if (deletePreferences)
7960                   QDir(dataPath).removeRecursively();
7961             QSettings settings;
7962             QFile::remove(settings.fileName() + ".lock"); //forcibly remove lock
7963             QFile::remove(settings.fileName());
7964             settings.clear();
7965             }
7966 
7967       // create local plugin directory
7968       // if not already there:
7969       QDir().mkpath(dataPath + "/plugins");
7970 
7971       if (MScore::debugMode)
7972             qDebug("global share: <%s>", qPrintable(mscoreGlobalShare));
7973 
7974       // Set translator before Shortcuts are initialized to get translations for all shortcuts.
7975       // Cannot use `preferences` or `localeName()` (which uses `preferences`) to retrieve
7976       // these values, as `preferences` needs to be initialized after translator is set.
7977       QString localeName;
7978       if (useFactorySettings)
7979             localeName = "system";
7980       else {
7981             QSettings s;
7982             localeName = s.value(PREF_UI_APP_LANGUAGE, "system").toString();
7983             }
7984 
7985       setMscoreLocale(localeName);
7986 
7987       Shortcut::init();
7988       preferences.init();
7989 
7990       QNetworkProxyFactory::setUseSystemConfiguration(true);
7991 
7992       MScore::init();         // initialize libmscore
7993       updateExternalValuesFromPreferences();
7994 
7995       // initialize current page size from default printer
7996 #ifndef QT_NO_PRINTER
7997       if (!MScore::testMode && !MScore::saveTemplateMode) {
7998             QPrinter p;
7999             if (p.isValid()) {
8000 //                  qDebug("set paper size from default printer");
8001                   QRectF psf = p.paperRect(QPrinter::Inch);
8002                   MScore::defaultStyle().set(Sid::pageWidth,  psf.width());
8003                   MScore::defaultStyle().set(Sid::pageHeight, psf.height());
8004                   MScore::defaultStyle().set(Sid::enableVerticalSpread, true);
8005                   }
8006             }
8007 #endif
8008 
8009       if (MScore::debugMode) {
8010             qDebug("DPI %f", DPI);
8011 
8012             QScreen* screen      = QGuiApplication::primaryScreen();
8013             qDebug() << "Information for screen:" << screen->name();
8014             qDebug() << "  Available geometry:" << screen->availableGeometry().x() << screen->availableGeometry().y() << screen->availableGeometry().width() << "x" << screen->availableGeometry().height();
8015             qDebug() << "  Available size:" << screen->availableSize().width() << "x" << screen->availableSize().height();
8016             qDebug() << "  Available virtual geometry:" << screen->availableVirtualGeometry().x() << screen->availableVirtualGeometry().y() << screen->availableVirtualGeometry().width() << "x" << screen->availableVirtualGeometry().height();
8017             qDebug() << "  Available virtual size:" << screen->availableVirtualSize().width() << "x" << screen->availableVirtualSize().height();
8018             qDebug() << "  Depth:" << screen->depth() << "bits";
8019             qDebug() << "  Geometry:" << screen->geometry().x() << screen->geometry().y() << screen->geometry().width() << "x" << screen->geometry().height();
8020             qDebug() << "  Logical DPI:" << screen->logicalDotsPerInch();
8021             qDebug() << "  Logical DPI X:" << screen->logicalDotsPerInchX();
8022             qDebug() << "  Logical DPI Y:" << screen->logicalDotsPerInchY();
8023             qDebug() << "  Physical DPI:" << screen->physicalDotsPerInch();
8024             qDebug() << "  Physical DPI X:" << screen->physicalDotsPerInchX();
8025             qDebug() << "  Physical DPI Y:" << screen->physicalDotsPerInchY();
8026             qDebug() << "  Physical size:" << screen->physicalSize().width() << "x" << screen->physicalSize().height() << "mm";
8027             qDebug() << "  Refresh rate:" << screen->refreshRate() << "Hz";
8028             qDebug() << "  Size:" << screen->size().width() << "x" << screen->size().height();
8029             qDebug() << "  Virtual geometry:" << screen->virtualGeometry().x() << screen->virtualGeometry().y() << screen->virtualGeometry().width() << "x" << screen->virtualGeometry().height();
8030             qDebug() << "  Virtual size:" << screen->virtualSize().width() << "x" << screen->virtualSize().height();
8031             }
8032 
8033       if (!MScore::testMode)
8034             MScore::readDefaultStyle(preferences.getString(PREF_SCORE_STYLE_DEFAULTSTYLEFILE));
8035 
8036       MsSplashScreen* sc = nullptr;
8037       if (!MScore::noGui && preferences.getBool(PREF_UI_APP_STARTUP_SHOWSPLASHSCREEN)) {
8038             sc = new MsSplashScreen();
8039             sc->show();
8040             qApp->processEvents();
8041             }
8042 
8043       // Best not to show this since the font used to display the message
8044       // isn't updated till updateUiStyleAndTheme()
8045       // showSplashMessage(sc, tr("Updating user interface and theme…"));
8046       if (!MScore::noGui) {
8047             MuseScore::updateUiStyleAndTheme();
8048             }
8049       else {
8050             genIcons(); // in GUI mode generated in updateUiStyleAndTheme()
8051             noSeq = true;
8052             }
8053 
8054       // Do not create sequencer and audio drivers if run with '-s'
8055       if (!noSeq) {
8056             showSplashMessage(sc, tr("Initializing sequencer and audio driver…"));
8057             seq            = new Seq();
8058             MScore::seq    = seq;
8059             Driver* driver = driverFactory(seq, audioDriver);
8060             synti          = synthesizerFactory();
8061             if (driver) {
8062                   MScore::sampleRate = driver->sampleRate();
8063                   synti->setSampleRate(MScore::sampleRate);
8064 
8065                   showSplashMessage(sc, tr("Loading SoundFonts…"));
8066                   synti->init();
8067 
8068                   seq->setDriver(driver);
8069                   }
8070             else {
8071                   // Do not delete the sequencer If we can't load driver.
8072                   // Allow user to select the working driver later.
8073                   MScore::sampleRate = 44100;  // Would be changed when user changes driver
8074                   synti->setSampleRate(MScore::sampleRate);
8075                   synti->init();
8076                   }
8077             seq->setMasterSynthesizer(synti);
8078             }
8079       else {
8080             seq         = 0;
8081             MScore::seq = 0;
8082             }
8083 //---
8084       //
8085       // avoid font problems by overriding the environment
8086       //    fall back to "C" locale
8087       //
8088 
8089       //#ifndef Q_OS_WIN
8090       //setenv("LANG", "C", 1);
8091       //#endif
8092       //QLocale::setDefault(QLocale(QLocale::C));
8093 
8094       if (MScore::debugMode) {
8095             QStringList sl(QCoreApplication::libraryPaths());
8096             foreach(const QString& s, sl)
8097                   qDebug("LibraryPath: <%s>", qPrintable(s));
8098             }
8099 
8100       // rastral size of font is 20pt = 20/72 inch = 20*DPI/72 dots
8101       //   staff has 5 lines = 4 * _spatium
8102       //   _spatium    = SPATIUM20;     // 20.0 / 72.0 * DPI / 4.0;
8103 
8104       if (!MScore::noGui) {
8105 #ifndef Q_OS_MAC
8106             qApp->setWindowIcon(*icons[int(Icons::window_ICON)]);
8107 #endif
8108             showSplashMessage(sc, tr("Initializing workspace…"));
8109             WorkspacesManager::initCurrentWorkspace();
8110             }
8111 
8112       showSplashMessage(sc, tr("Creating main window…"));
8113       mscore = new MuseScore();
8114       // create a score for internal use
8115       gscore = new MasterScore();
8116       gscore->setPaletteMode(true);
8117       gscore->setMovements(new Movements());
8118       gscore->setStyle(MScore::baseStyle());
8119 
8120       gscore->style().set(Sid::MusicalTextFont, QString("Leland Text"));
8121       ScoreFont* scoreFont = ScoreFont::fontFactory("Leland");
8122       gscore->setScoreFont(scoreFont);
8123       gscore->setNoteHeadWidth(scoreFont->width(SymId::noteheadBlack, gscore->spatium()) / SPATIUM20);
8124 
8125 #ifndef TELEMETRY_DISABLED
8126       tryToRequestTelemetryPermission();
8127 #endif
8128 
8129       showSplashMessage(sc, tr("Reading translations…"));
8130       //read languages list
8131       mscore->readLanguages(mscoreGlobalShare + "locale/languages.xml");
8132 
8133       if (!MScore::noGui) {
8134             if (preferences.getBool(PREF_APP_STARTUP_FIRSTSTART)) {
8135                   mscoreFirstStart = true;
8136                   showSplashMessage(sc, tr("Initializing startup wizard…"));
8137                   StartupWizard* sw = new StartupWizard;
8138                   sw->exec();
8139                   preferences.setPreference(PREF_APP_STARTUP_FIRSTSTART, false);
8140                   preferences.setPreference(PREF_APP_KEYBOARDLAYOUT, sw->keyboardLayout());
8141                   preferences.setPreference(PREF_UI_APP_LANGUAGE, sw->language());
8142                   setMscoreLocale(sw->language());
8143                   Workspace::writeGlobalToolBar();
8144                   Workspace::writeGlobalGUIState();
8145                   Workspace* targetWorkspace = WorkspacesManager::visibleWorkspaces()[0];
8146                   if (targetWorkspace)
8147                         mscore->changeWorkspace(targetWorkspace, true);
8148 
8149                   preferences.setPreference(PREF_UI_APP_STARTUP_SHOWTOURS, sw->showTours());
8150                   delete sw;
8151 
8152                   showSplashMessage(sc, tr("Initializing preferences…"));
8153                   // reinitialize preferences so some default values are calculated based on chosen language
8154                   preferences.init();
8155                   // store preferences with locale-dependent default values
8156                   // so that the values from first start will be used later
8157                   preferences.setToDefaultValue(PREF_APP_PATHS_MYSCORES);
8158                   preferences.setToDefaultValue(PREF_APP_PATHS_MYSTYLES);
8159                   preferences.setToDefaultValue(PREF_APP_PATHS_MYIMAGES);
8160                   preferences.setToDefaultValue(PREF_APP_PATHS_MYTEMPLATES);
8161                   preferences.setToDefaultValue(PREF_APP_PATHS_MYPLUGINS);
8162                   preferences.setToDefaultValue(PREF_APP_PATHS_MYSOUNDFONTS);
8163                   preferences.setToDefaultValue(PREF_APP_PATHS_MYEXTENSIONS);
8164                   updateExternalValuesFromPreferences();
8165                   }
8166             if (!Shortcut::customSource()) {
8167                   QString keyboardLayout = preferences.getString(PREF_APP_KEYBOARDLAYOUT);
8168                   StartupWizard::autoSelectShortcuts(keyboardLayout);
8169                   }
8170             }
8171 
8172       if (!noSeq) {
8173             if (!seq->init())
8174                   qDebug("sequencer init failed");
8175             }
8176 
8177       QApplication::instance()->installEventFilter(mscore);
8178 
8179 #ifndef TELEMETRY_DISABLED
8180       QApplication::instance()->installEventFilter(ActionEventObserver::instance());
8181       ActionEventObserver::instance()->setScoreState(mscore->state());
8182       QObject::connect(mscore, &MuseScore::scoreStateChanged, ActionEventObserver::instance(), &ActionEventObserver::setScoreState);
8183 #endif
8184 
8185       mscore->setRevision(Ms::revision);
8186       int files = 0;
8187       bool restoredSession = false;
8188 
8189       if (MScore::noGui)
8190             return;
8191       else {
8192             showSplashMessage(sc, tr("Initializing main window…"));
8193             mscore->readSettings();
8194             QObject::connect(qApp, SIGNAL(messageReceived(const QString&)),
8195                mscore, SLOT(handleMessage(const QString&)));
8196 
8197             static_cast<QtSingleApplication*>(qApp)->setActivationWindow(mscore, false);
8198             // count filenames specified on the command line
8199             // these are the non-empty strings remaining in argv
8200             foreach(const QString& name, argv) {
8201                   if (!name.isEmpty())
8202                         ++files;
8203                   }
8204 #ifdef Q_OS_MAC
8205             // app->paths contains files requested to be loaded by OS X
8206             // append these to argv and update file count
8207             foreach(const QString& name, static_cast<MuseScoreApplication*>(qApp)->paths) {
8208                   if (!name.isEmpty()) {
8209                         argv << name;
8210                         ++files;
8211                         }
8212                   }
8213 #endif
8214             //
8215             // TODO: delete old session backups
8216             //
8217             showSplashMessage(sc, tr("Restoring session…"));
8218             restoredSession = mscore->restoreSession((preferences.sessionStart() == SessionStart::LAST && (files == 0)));
8219             }
8220 
8221       errorMessage = new QErrorMessage(mscore);
8222 #ifdef SCRIPT_INTERFACE
8223       mscore->getPluginManager()->readPluginList();
8224       mscore->loadPlugins();
8225 #endif
8226       mscore->writeSessionFile(false);
8227 
8228 #ifdef Q_OS_MAC
8229       // there's a bug in Qt showing the toolbar unified after switching
8230       // showFullScreen(), showMaximized(), showNormal()...
8231       mscore->setUnifiedTitleAndToolBarOnMac(false);
8232 
8233       // don't let macOS add "Show Tab Bar" to "View" Menu
8234       CocoaBridge::setAllowsAutomaticWindowTabbing(false);
8235 
8236       // Observe when macOS's Dark Mode is switched on or off and let MuseScore's
8237       // theme switch along with it if the user has chosen that setting
8238       CocoaBridge::observeDarkModeSwitches([]() {
8239             if (preferences.preferredGlobalStyle() == MuseScorePreferredStyleType::FOLLOW_SYSTEM)
8240                   MuseScore::updateUiStyleAndTheme();
8241             });
8242 #endif
8243 
8244       mscore->changeState(mscore->noScore() ? STATE_DISABLED : STATE_NORMAL);
8245       mscore->show();
8246 
8247       if (!restoredSession || files) {
8248             showSplashMessage(sc, tr("Loading scores…"));
8249             loadScores(argv);
8250             }
8251 
8252       if (mscore->hasToCheckForExtensionsUpdate())
8253             mscore->checkForExtensionsUpdate();
8254 
8255       if (QWidget* menubar = mscore->menuWidget())
8256             TourHandler::addWidgetToTour("welcome", menubar, "menubar");
8257 
8258       if (!scoresOnCommandline && preferences.getBool(PREF_UI_APP_STARTUP_SHOWSTARTCENTER) && (!restoredSession || mscore->scores().size() == 0)) {
8259             showSplashMessage(sc, tr("Initializing start center…"));
8260 #ifdef Q_OS_MAC
8261 // ugly, but on mac we get an event when a file is open.
8262 // We can't get the event when the startcenter is shown.
8263 // So we let the event loop run a bit before showing the start center.
8264             QTimer* timer = new QTimer();
8265             timer->setSingleShot(true);
8266             QObject::connect(timer, &QTimer::timeout, [=]() {
8267                   if (!scoresOnCommandline) {
8268                         getAction("startcenter")->setChecked(true);
8269                         mscore->showStartcenter(true);
8270                         }
8271                   timer->deleteLater();
8272                   } );
8273             timer->start(500);
8274 #else
8275 
8276             getAction("startcenter")->setChecked(true);
8277             mscore->showStartcenter(true);
8278 #endif
8279             }
8280       else {
8281             showSplashMessage(sc, tr("Initializing tours…"));
8282             mscore->tourHandler()->startTour("welcome");
8283             //otherwise, welcome tour will appear on closing StartCenter
8284             }
8285 
8286       if (sc) {
8287             sc->close();
8288             qApp->processEvents();
8289             }
8290 
8291       mscore->showPlayPanel(preferences.getBool(PREF_UI_APP_STARTUP_SHOWPLAYPANEL));
8292       QSettings settings;
8293       if (settings.value("synthControlVisible", false).toBool())
8294             mscore->showSynthControl(true);
8295       }
8296 
8297 
saveScoreParts(const QString & inFilePath,const QString & outFilePath)8298 bool MuseScore::saveScoreParts(const QString& inFilePath, const QString& outFilePath)
8299 {
8300     MasterScore* score = mscore->readScore(inFilePath);
8301     if (!score) {
8302         return false;
8303     }
8304 
8305     if (!styleFile.isEmpty()) {
8306         QFile f(styleFile);
8307         if (f.open(QIODevice::ReadOnly)) {
8308             score->style().load(&f);
8309         }
8310     }
8311     score->switchToPageMode();
8312 
8313     // if no parts, generate parts from existing instruments
8314     if (score->excerpts().isEmpty()) {
8315         auto excerpts = Excerpt::createAllExcerpt(score);
8316         for (Excerpt* e : excerpts) {
8317               Score* nscore = new Score(e->oscore());
8318               e->setPartScore(nscore);
8319               nscore->style().set(Sid::createMultiMeasureRests, true);
8320               auto excerptCmdFake = new AddExcerpt(e);
8321               excerptCmdFake->redo(nullptr);
8322               Excerpt::createExcerpt(e);
8323         }
8324     }
8325 
8326     QJsonArray partsObjList;
8327     QJsonArray partsMetaList;
8328     QJsonArray partsTitles;
8329 
8330     for (Excerpt* excerpt : score->excerpts()) {
8331         Score* part = excerpt->partScore();
8332         QMap<QString, QString> partMetaTags = part->metaTags();
8333 
8334         QJsonValue partTitle(part->title());
8335         partsTitles << partTitle;
8336 
8337         QVariantMap meta;
8338         for (const QString& key: partMetaTags.keys()) {
8339             meta[key] = partMetaTags[key];
8340         }
8341 
8342         QJsonValue partMetaObj = QJsonObject::fromVariantMap(meta);
8343         partsMetaList << partMetaObj;
8344 
8345         QJsonValue partObj(QString::fromLatin1(exportMsczAsJSON(part)));
8346         partsObjList << partObj;
8347     }
8348 
8349     QJsonObject json;
8350     json["parts"] = partsTitles;
8351     json["partsMeta"] = partsMetaList;
8352     json["partsBin"] = partsObjList;
8353 
8354     QJsonDocument jsonDoc(json);
8355     QFile out(outFilePath);
8356 
8357     bool res = out.open(QIODevice::WriteOnly);
8358     if (res) {
8359         out.write(jsonDoc.toJson(QJsonDocument::Compact));
8360         out.close();
8361     }
8362 
8363     delete score;
8364     return res;
8365 }
8366 
exportMsczAsJSON(Score * score)8367 QByteArray MuseScore::exportMsczAsJSON(Score* score)
8368 {
8369     QBuffer buffer;
8370     buffer.open(QIODevice::ReadWrite);
8371 
8372     QString fileName = saveFilename(score->title()) + ".mscz";
8373     score->saveCompressedFile(&buffer, fileName, false, true);
8374 
8375     buffer.open(QIODevice::ReadOnly);
8376     QByteArray scoreData = buffer.readAll();
8377     buffer.close();
8378 
8379     return scoreData.toBase64();
8380 }
8381 
8382 //---------------------------------------------------------
8383 //   exportPartsPdfsToJSON
8384 //---------------------------------------------------------
8385 
exportPartsPdfsToJSON(const QString & inFilePath,const QString & outFilePath)8386 bool MuseScore::exportPartsPdfsToJSON(const QString& inFilePath, const QString& outFilePath)
8387 {
8388       Score* score = mscore->openScore(inFilePath);
8389       if (!score)
8390             return false;
8391 
8392       QString outPath = QFileInfo(inFilePath).path() + "/";
8393 
8394       QJsonObject jsonForPdfs;
8395       QString outName = outPath + QFileInfo(inFilePath).completeBaseName() + ".pdf";
8396       jsonForPdfs["score"] = outName;
8397 
8398       //save score pdf
8399       if (!styleFile.isEmpty()) {
8400             QFile f(styleFile);
8401             if (f.open(QIODevice::ReadOnly))
8402                   score->style().load(&f);
8403       }
8404       score->switchToPageMode();
8405 
8406       jsonForPdfs["scoreBin"] = QString::fromLatin1(mscore->exportPdfAsJSON(score));
8407 
8408       //save extended score+parts and separate parts pdfs
8409       //if no parts, generate parts from existing instruments
8410       if (score->excerpts().size() == 0) {
8411             auto excerpts = Excerpt::createAllExcerpt(score->masterScore());
8412             for (Excerpt* e : excerpts) {
8413                   Score* nscore = new Score(e->oscore());
8414                   e->setPartScore(nscore);
8415                   nscore->style().set(Sid::createMultiMeasureRests, true);
8416                   auto excerptCmdFake = new AddExcerpt(e);
8417                   excerptCmdFake->redo(nullptr);
8418                   Excerpt::createExcerpt(e);
8419             }
8420       }
8421 
8422       QList<Score*> scores;
8423       scores.append(score);
8424       QJsonArray partsArray;
8425       QJsonArray partsNamesArray;
8426       for (Excerpt* e : score->excerpts()) {
8427             scores.append(e->partScore());
8428             QJsonValue partNameVal(e->title());
8429             partsNamesArray.append(partNameVal);
8430             QJsonValue partVal(QString::fromLatin1(exportPdfAsJSON(e->partScore())));
8431             partsArray.append(partVal);
8432       }
8433       jsonForPdfs["parts"] = partsNamesArray;
8434       jsonForPdfs["partsBin"] = partsArray;
8435 
8436       jsonForPdfs["scoreFullPostfix"] = QString("-Score_and_parts") + ".pdf";
8437 
8438       QString tempFileName = outPath + "tempPdf.pdf";
8439       bool res = mscore->savePdf(scores, tempFileName);
8440       QFile tempPdf(tempFileName);
8441       tempPdf.open(QIODevice::ReadWrite);
8442       QByteArray fullScoreData = tempPdf.readAll();
8443       tempPdf.remove();
8444       jsonForPdfs["scoreFullBin"] = QString::fromLatin1(fullScoreData.toBase64());
8445 
8446       QJsonDocument jsonDoc(jsonForPdfs);
8447       const QString& jsonPath{outFilePath};
8448       QFile file(jsonPath);
8449       res &= file.open(QIODevice::WriteOnly);
8450       if (res) {
8451             file.write(jsonDoc.toJson(QJsonDocument::Compact));
8452             file.close();
8453       }
8454 
8455       delete score;
8456       return res;
8457       }
8458 
8459 //---------------------------------------------------------
8460 //   getQmlEngine
8461 //---------------------------------------------------------
8462 
getQmlUiEngine()8463 MsQmlEngine* MuseScore::getQmlUiEngine()
8464       {
8465       if (!_qmlUiEngine)
8466             _qmlUiEngine = new MsQmlEngine(this);
8467       return _qmlUiEngine;
8468       }
8469 
8470 //---------------------------------------------------------
8471 //   getPluginEngine
8472 //---------------------------------------------------------
8473 
8474 #ifdef SCRIPT_INTERFACE
getPluginEngine()8475 QmlPluginEngine* MuseScore::getPluginEngine()
8476       {
8477       if (!_qmlEngine)
8478             _qmlEngine = new QmlPluginEngine(this);
8479       return _qmlEngine;
8480       }
8481 #endif
8482 
8483 //---------------------------------------------------------
8484 //   scoreUnrolled
8485 //    create a version of the given score with repeats unrolled
8486 //---------------------------------------------------------
8487 
scoreUnrolled(MasterScore * original)8488 void MuseScore::scoreUnrolled(MasterScore * original)
8489       {
8490       MasterScore * score = original->unrollRepeats();
8491       setCurrentScoreView(appendScore(score));
8492       }
8493 } // namespace Ms
8494