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