//=============================================================================
//  MuseScore
//  Music Composition & Notation
//
//  Copyright (C) 2002-2016 Werner Schweer
//
//  This program is free software; you can redistribute it and/or modify
//  it under the terms of the GNU General Public License version 2
//  as published by the Free Software Foundation and appearing in
//  the file LICENCE.GPL
//=============================================================================

#include <fenv.h>
#include <QStyleFactory>
#include "palettebox.h"
#include "config.h"
#include "musescore.h"
#include "scoreview.h"
#include "libmscore/style.h"
#include "libmscore/score.h"
#include "instrdialog.h"
#include "preferences.h"
#include "prefsdialog.h"
#include "icons.h"
#include "libmscore/xml.h"
#include "seq.h"
#include "libmscore/tempo.h"
#include "libmscore/sym.h"
#include "pagesettings.h"
#include "debugger/debugger.h"
#include "editstyle.h"
#include "playpanel.h"
#include "libmscore/page.h"
#include "mixer.h"
#include "selectionwindow.h"
#include "palette.h"
#include "palettebox.h"
#include "libmscore/part.h"
#include "libmscore/drumset.h"
#include "libmscore/instrtemplate.h"
#include "libmscore/note.h"
#include "libmscore/staff.h"
#include "driver.h"
#include "libmscore/harmony.h"
#include "magbox.h"
#include "libmscore/sig.h"
#include "libmscore/undo.h"
#include "synthcontrol.h"
#include "pianoroll.h"
#include "drumroll.h"
#include "scoretab.h"
#include "timedialog.h"
#include "keyedit.h"
#include "harmonyedit.h"
#include "navigator.h"
#include "importmidi/importmidi_panel.h"
#include "libmscore/chord.h"
#include "mstyle/mstyle.h"
#include "mstyle/mconfig.h"
#include "libmscore/segment.h"
#include "editraster.h"
#include "pianotools.h"
#include "mediadialog.h"
#include "workspace.h"
#include "selectdialog.h"
#include "selectnotedialog.h"
#include "transposedialog.h"
#include "metaedit.h"
#include "inspector/inspector.h"
#ifdef OMR
#include "omrpanel.h"
#endif
#include "shortcut.h"
#ifdef SCRIPT_INTERFACE
#include "pluginCreator.h"
#include "pluginManager.h"
#endif
#include "helpBrowser.h"
#include "drumtools.h"
#include "editstafftype.h"
#include "texttools.h"
#include "textpalette.h"
#include "resourceManager.h"
#include "scoreaccessibility.h"

#include "libmscore/mscore.h"
#include "libmscore/system.h"
#include "libmscore/measure.h"
#include "libmscore/chordlist.h"
#include "libmscore/volta.h"
#include "libmscore/lasso.h"
#include "libmscore/excerpt.h"

#include "driver.h"

#include "effects/zita1/zita.h"
#include "effects/compressor/compressor.h"
#include "effects/noeffect/noeffect.h"
#include "synthesizer/synthesizer.h"
#include "synthesizer/synthesizergui.h"
#include "synthesizer/msynthesizer.h"
#include "fluid/fluid.h"
#include "qmlplugin.h"
#include "accessibletoolbutton.h"
#include "toolbuttonmenu.h"
#include "searchComboBox.h"
#include "startcenter.h"
#include "help.h"
#include "awl/aslider.h"

#ifdef AEOLUS
extern Ms::Synthesizer* createAeolus();
#endif
#ifdef ZERBERUS
extern Ms::Synthesizer* createZerberus();
#endif

#ifdef QT_NO_DEBUG
      Q_LOGGING_CATEGORY(undoRedo, "undoRedo", QtCriticalMsg)
#else
      Q_LOGGING_CATEGORY(undoRedo, "undoRedo", QtCriticalMsg)
//      Q_LOGGING_CATEGORY(undoRedo, "undoRedo")
#endif

namespace Ms {

extern void checkProperties();
MuseScore* mscore;
MasterSynthesizer* synti;

bool enableExperimental = false;
bool enableTestMode = false;

QString dataPath;
QString iconPath;

bool converterMode = false;
bool processJob = false;
bool externalIcons = false;
bool pluginMode = false;
static bool startWithNewScore = false;
double converterDpi = 0;
double guiScaling = 0.0;
static double userDPI = 0.0;
int trimMargin = -1;
bool noWebView = false;
bool exportScoreParts = false;
bool ignoreWarnings = false;

QString mscoreGlobalShare;

static QString outFileName;
static QString jsonFileName;
static QString audioDriver;
static QString pluginName;
static QString styleFile;
static bool scoresOnCommandline { false };

static QList<QTranslator*> translatorList;

QString localeName;
bool useFactorySettings = false;
bool deletePreferences = false;
QString styleName;
QString revision;
QErrorMessage* errorMessage;
const char* voiceActions[] = { "voice-1", "voice-2", "voice-3", "voice-4" };

const std::list<const char*> MuseScore::_allNoteInputMenuEntries {
            "note-input",
            "pad-note-128",
            "pad-note-64",
            "pad-note-32",
            "pad-note-16",
            "pad-note-8",
            "pad-note-4",
            "pad-note-2",
            "pad-note-1",
            "note-breve",
            "note-longa",
            "pad-dot",
            "pad-dotdot",
            "pad-dot3",
            "pad-dot4",
            "tie",
            "",
            "pad-rest",
            "",
            "sharp2",
            "sharp",
            "nat",
            "flat",
            "flat2",
            "flip",
            "",
            "voice-1",
            "voice-2",
            "voice-3",
            "voice-4"
            };

const std::list<const char*> MuseScore::_advancedNoteInputMenuEntries {
            "note-input",
            "pad-note-128",
            "pad-note-64",
            "pad-note-32",
            "pad-note-16",
            "pad-note-8",
            "pad-note-4",
            "pad-note-2",
            "pad-note-1",
            "note-breve",
            "note-longa",
            "pad-dot",
            "pad-dotdot",
            "pad-dot3",
            "pad-dot4",
            "tie",
            "",
            "pad-rest",
            "",
            "sharp2",
            "sharp",
            "nat",
            "flat",
            "flat2",
            "flip",
            "",
            "voice-1",
            "voice-2",
            "voice-3",
            "voice-4"
            };

const std::list<const char*> MuseScore::_basicNoteInputMenuEntries {
            "note-input",
            "pad-note-32",
            "pad-note-16",
            "pad-note-8",
            "pad-note-4",
            "pad-note-2",
            "pad-note-1",
            "pad-dot",
            "tie",
            "",
            "pad-rest",
            "",
            "sharp",
            "nat",
            "flat",
            "flip",
            "",
            "voice-1",
            "voice-2",
            "voice-3",
            "voice-4"
            };


extern bool savePositions(Score*, const QString& name, bool segments );
extern TextPalette* textPalette;

static constexpr double SCALE_MAX  = 16.0;
static constexpr double SCALE_MIN  = 0.05;
static constexpr double SCALE_STEP = 1.7;

//---------------------------------------------------------
// cmdInsertMeasure
//---------------------------------------------------------

void MuseScore::cmdInsertMeasures()
      {
      if (cs) {
            if (cs->selection().isNone() && !cs->selection().findMeasure()) {
                  QMessageBox::warning(0, "MuseScore",
                        tr("No measure selected:\n" "Please select a measure and try again"));
                  }
            else {
                  insertMeasuresDialog = new InsertMeasuresDialog;
                  insertMeasuresDialog->show();
                  }
            }
      }

//---------------------------------------------------------
// InsertMeasuresDialog
//---------------------------------------------------------

InsertMeasuresDialog::InsertMeasuresDialog(QWidget* parent)
   : QDialog(parent)
      {
      setObjectName("InsertMeasuresDialog");
      setupUi(this);
      setWindowFlags(this->windowFlags() & ~Qt::WindowContextHelpButtonHint);
      insmeasures->selectAll();
      }

//---------------------------------------------------------
// Insert Measure -->   accept
//---------------------------------------------------------

void InsertMeasuresDialog::accept()
      {
      int n = insmeasures->value();
      if (mscore->currentScore())
            mscore->currentScoreView()->cmdInsertMeasures(n, ElementType::MEASURE);
      done(1);
      }

//---------------------------------------------------------
// InsertMeasuresDialog hideEvent
//---------------------------------------------------------

void InsertMeasuresDialog::hideEvent(QHideEvent* event)
      {
      MuseScore::saveGeometry(this);
      QDialog::hideEvent(event);
      }

//---------------------------------------------------------
//   getSharePath
//---------------------------------------------------------

QString getSharePath()
      {
#ifdef Q_OS_WIN
      QDir dir(QCoreApplication::applicationDirPath() + QString("/../" INSTALL_NAME));
      return dir.absolutePath() + "/";
#else
#ifdef Q_OS_MAC
      QDir dir(QCoreApplication::applicationDirPath() + QString("/../Resources"));
      return dir.absolutePath() + "/";
#else
      // Try relative path (needed for portable AppImage and non-standard installations)
      QDir dir(QCoreApplication::applicationDirPath() + QString("/../share/" INSTALL_NAME));
      if (dir.exists())
            return dir.absolutePath() + "/";
      // Otherwise fall back to default location (e.g. if binary has moved relative to share)
      return QString( INSTPREFIX "/share/" INSTALL_NAME);
#endif
#endif
      }

//---------------------------------------------------------
//   printVersion
//---------------------------------------------------------

static void printVersion(const char* prog)
      {
      if (MuseScore::unstable())
            fprintf(stderr, "%s: Music Score Editor\nUnstable Prerelease for Version %s; Build %s\n",
         prog, VERSION, qPrintable(revision));
      else
            fprintf(stderr, "%s: Music Score Editor; Version %s; Build %s\n", prog, VERSION, qPrintable(revision));
      }

static const int RECENT_LIST_SIZE = 20;

//---------------------------------------------------------
//   closeEvent
//---------------------------------------------------------

void MuseScore::closeEvent(QCloseEvent* ev)
      {
      unloadPlugins();
      QList<MasterScore*> removeList;
      for (MasterScore* score : scoreList) {
            if (score->created() && !score->dirty())
                  removeList.append(score);
            else {
                  if (checkDirty(score)) {      // ask user if file is dirty
                        ev->ignore();
                        return;
                        }
                  //
                  // if score is still dirty, then the user has discarded the
                  // score and we can remove it from the list
                  //
                  if (score->created() && score->dirty())
                        removeList.append(score);
                  }
            }

      // remove all new created/not save score so they are
      // note saved as session data
      for (MasterScore* score : removeList)
            scoreList.removeAll(score);

      writeSessionFile(true);
      for (MasterScore* score : scoreList) {
            if (!score->tmpName().isEmpty()) {
                  QFile f(score->tmpName());
                  f.remove();
                  }
            }

      writeSettings();

      _loginManager->save();

      ev->accept();
      if (preferences.dirty)
            preferences.write();
      this->deleteLater();     //this is necessary on windows http://musescore.org/node/16713
      qApp->quit();
      }

//---------------------------------------------------------
//   preferencesChanged
//---------------------------------------------------------

void MuseScore::preferencesChanged()
      {
      for (int i = 0; i < tab1->count(); ++i) {
            ScoreView* canvas = tab1->view(i);
            if (canvas == 0)
                  continue;
            if (preferences.bgUseColor)
                  canvas->setBackground(MScore::bgColor);
            else {
                  QPixmap* pm = new QPixmap(preferences.bgWallpaper);
                  canvas->setBackground(pm);
                  }
            if (preferences.fgUseColor)
                  canvas->setForeground(preferences.fgColor);
            else {
                  QPixmap* pm = new QPixmap(preferences.fgWallpaper);
                  if (pm == 0 || pm->isNull())
                        qDebug("no valid pixmap %s", preferences.fgWallpaper.toLatin1().data());
                  canvas->setForeground(pm);
                  }
            }
      if (tab2) {
            for (int i = 0; i < tab2->count(); ++i) {
                  ScoreView* canvas = tab2->view(i);
                  if (canvas == 0)
                        continue;
                  if (preferences.bgUseColor)
                        canvas->setBackground(MScore::bgColor);
                  else {
                        QPixmap* pm = new QPixmap(preferences.bgWallpaper);
                        canvas->setBackground(pm);
                        }
                  if (preferences.fgUseColor)
                        canvas->setForeground(preferences.fgColor);
                  else {
                        QPixmap* pm = new QPixmap(preferences.fgWallpaper);
                        if (pm == 0 || pm->isNull())
                              qDebug("no valid pixmap %s", preferences.fgWallpaper.toLatin1().data());
                        canvas->setForeground(pm);
                        }
                  }
            }

      transportTools->setEnabled(!noSeq && seq && seq->isRunning());
      playId->setEnabled(!noSeq && seq && seq->isRunning());

      getAction("midi-on")->setEnabled(preferences.enableMidiInput);
      _statusBar->setVisible(preferences.showStatusBar);

      delete newWizard;
      newWizard = 0;
      }

//---------------------------------------------------------
//   populateNoteInputMenu
//---------------------------------------------------------

void MuseScore::populateNoteInputMenu()
      {
      entryTools->clear();

      for (const auto s : _noteInputMenuEntries) {
            if (!*s)
                  entryTools->addSeparator();
            else {
                  QAction* a = getAction(s);
                  QWidget* w;
                  if (strcmp(s, "note-input") == 0) {
                        //-----------------------------------------------------------------
                        // Note Entry Modes menu
                        // ToolButtonMenu to swap between Note Entry Methods
                        //-----------------------------------------------------------------
                        QActionGroup* noteEntryMethods = new QActionGroup(entryTools);

                        noteEntryMethods->addAction(getAction("note-input-steptime"));
                        noteEntryMethods->addAction(getAction("note-input-repitch"));
                        noteEntryMethods->addAction(getAction("note-input-rhythm"));
                        noteEntryMethods->addAction(getAction("note-input-realtime-auto"));
                        noteEntryMethods->addAction(getAction("note-input-realtime-manual"));

                        connect(noteEntryMethods, SIGNAL(triggered(QAction*)), this, SLOT(setNoteEntryState()));

                        w = new ToolButtonMenu(tr("Note Entry Methods"),
                           ToolButtonMenu::TYPES::ICON_CHANGED,
                           getAction("note-input"),
                           noteEntryMethods,
                           this);
                        }
                  else if (strncmp(s, "voice-", 6) == 0) {
                        AccessibleToolButton* tb = new AccessibleToolButton(this, a);
                        tb->setObjectName("voice");
                        tb->setFocusPolicy(Qt::ClickFocus);
                        tb->setToolButtonStyle(Qt::ToolButtonTextOnly);
                        tb->setProperty((QString("voice%1").arg(atoi(s+6))).toUtf8().data(), true);
                        QPalette p(tb->palette());
                        int i = atoi(s+6) - 1;
                        p.setColor(QPalette::Base, MScore::selectColor[i]);
                        tb->setPalette(p);
                        a->setCheckable(true);
                        // tb->setDefaultAction(a);
                        w = tb;
                        }
                  else
                        w = new AccessibleToolButton(entryTools, a);
                  entryTools->addWidget(w);
                  }
            }
      }

//---------------------------------------------------------
//   MuseScore
//---------------------------------------------------------

MuseScore::MuseScore()
   : QMainWindow()
      {
      QScreen* screen      = QGuiApplication::primaryScreen();
      if (userDPI == 0.0) {
#if defined(Q_OS_WIN)
      if (QSysInfo::WindowsVersion <= QSysInfo::WV_WINDOWS7)
            _physicalDotsPerInch = screen->logicalDotsPerInch() * screen->devicePixelRatio();
      else
            _physicalDotsPerInch = screen->physicalDotsPerInch();  // physical resolution
#else
      _physicalDotsPerInch = screen->physicalDotsPerInch();        // physical resolution
#endif
            }
      else {
            _physicalDotsPerInch = userDPI;
            }
      if (guiScaling == 0.0) {
            // set scale for icons, palette elements, window sizes, etc
            // the default values are hard coded in pixel sizes and assume ~96 DPI
            if (qAbs(_physicalDotsPerInch - DPI_DISPLAY) > 6.0)
                  guiScaling = _physicalDotsPerInch / DPI_DISPLAY;
            else
                  guiScaling = 1.0;
            }

      MScore::pixelRatio = DPI / screen->logicalDotsPerInch();

      setObjectName("MuseScore");
      _sstate = STATE_INIT;
      setWindowTitle(QString(MUSESCORE_NAME_VERSION));
      setIconSize(QSize(preferences.iconWidth * guiScaling, preferences.iconHeight * guiScaling));

      ucheck = new UpdateChecker();

      setAcceptDrops(true);
      setFocusPolicy(Qt::NoFocus);

      if (!converterMode && !pluginMode) {
            _loginManager = new LoginManager(this);
#if 0
            // initialize help engine
            QString lang = mscore->getLocaleISOCode();
            if (lang == "en_US")    // HACK
                  lang = "en";

            QString s = getSharePath() + "manual/doc_" + lang + ".qhc";
            _helpEngine = new QHelpEngine(s, this);
            if (!_helpEngine->setupData()) {
                  qDebug("cannot setup data for help engine: %s", qPrintable(_helpEngine->error()));
                  delete _helpEngine;
                  _helpEngine = 0;
                  }
#endif
            }

      _positionLabel = new QLabel;
      _positionLabel->setObjectName("decoration widget");  // this prevents animations

      _modeText = new QLabel;
      _modeText->setAutoFillBackground(false);
      _modeText->setObjectName("modeLabel");

      _statusBar = new QStatusBar;

      hRasterAction   = getAction("hraster");
      vRasterAction   = getAction("vraster");
      loopAction      = getAction("loop");
      loopInAction    = getAction("loop-in");
      loopOutAction   = getAction("loop-out");
      metronomeAction = getAction("metronome");
      countInAction   = getAction("countin");
      panAction       = getAction("pan");

      _statusBar->addPermanentWidget(new QWidget(this), 2);
      _statusBar->addPermanentWidget(new QWidget(this), 100);
      _statusBar->addPermanentWidget(_modeText, 0);

      if (enableExperimental) {
            layerSwitch = new QComboBox(this);
            layerSwitch->setToolTip(tr("Switch layer"));
            connect(layerSwitch, SIGNAL(activated(const QString&)), SLOT(switchLayer(const QString&)));
            playMode = new QComboBox(this);
            playMode->addItem(tr("Synthesizer"));
            playMode->addItem(tr("Audio track"));
            playMode->setToolTip(tr("Switch play mode"));
            connect(playMode, SIGNAL(activated(int)), SLOT(switchPlayMode(int)));

            _statusBar->addPermanentWidget(playMode);
            _statusBar->addPermanentWidget(layerSwitch);
            }
      _statusBar->addPermanentWidget(_positionLabel, 0);

      setStatusBar(_statusBar);
      ScoreAccessibility::createInstance(this);

      // otherwise unused actions:
      //   must be added somewere to work
      QActionGroup* ag = Shortcut::getActionGroupForWidget(MsWidget::MAIN_WINDOW);
      ag->setParent(this);
      addActions(ag->actions());
      connect(ag, SIGNAL(triggered(QAction*)), SLOT(cmd(QAction*)));

      mainWindow = new QSplitter;
      mainWindow->setChildrenCollapsible(false);

      QWidget* mainScore = new QWidget;
      mainScore->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
      mainWindow->addWidget(mainScore);

      layout = new QVBoxLayout;
      layout->setMargin(0);
      layout->setSpacing(0);
      mainScore->setLayout(layout);

      _navigator = new NScrollArea;
      _navigator->setFocusPolicy(Qt::NoFocus);
      mainWindow->addWidget(_navigator);
      scorePageLayoutChanged();
      showNavigator(preferences.showNavigator);

      mainWindow->setStretchFactor(0, 1);
      mainWindow->setStretchFactor(1, 0);
      mainWindow->setSizes(QList<int>({500, 50}));

      QSplitter* envelope = new QSplitter;
      envelope->setChildrenCollapsible(false);
      envelope->setOrientation(Qt::Vertical);
      envelope->addWidget(mainWindow);

      importmidiPanel = new ImportMidiPanel(this);
      importmidiPanel->setVisible(false);
      envelope->addWidget(importmidiPanel);

      {
      importmidiShowPanel = new QFrame;
      QHBoxLayout *hl = new QHBoxLayout;
      hl->setMargin(0);
      hl->setSpacing(0);
      importmidiShowPanel->setLayout(hl);
      showMidiImportButton = new QPushButton();
      showMidiImportButton->setFocusPolicy(Qt::ClickFocus);
      importmidiShowPanel->setVisible(false);
      connect(showMidiImportButton, SIGNAL(clicked()), SLOT(showMidiImportPanel()));
      connect(importmidiPanel, SIGNAL(closeClicked()), importmidiShowPanel, SLOT(show()));
      hl->addWidget(showMidiImportButton);
      QSpacerItem *item = new QSpacerItem(1, 1, QSizePolicy::Expanding, QSizePolicy::Fixed);
      hl->addSpacerItem(item);
      envelope->addWidget(importmidiShowPanel);
      }

      envelope->setSizes(QList<int>({550, 180}));

      splitter = new QSplitter;
      tab1 = new ScoreTab(&scoreList, this);
      tab1->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
      connect(tab1, SIGNAL(currentScoreViewChanged(ScoreView*)), SLOT(setCurrentScoreView(ScoreView*)));
      connect(tab1, SIGNAL(tabCloseRequested(int)), SLOT(removeTab(int)));
      connect(tab1, SIGNAL(actionTriggered(QAction*)), SLOT(cmd(QAction*)));
      splitter->addWidget(tab1);

      if (splitScreen()) {
            tab2 = new ScoreTab(&scoreList, this);
            tab2->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
            connect(tab2, SIGNAL(currentScoreViewChanged(ScoreView*)), SLOT(setCurrentScoreView(ScoreView*)));
            connect(tab2, SIGNAL(tabCloseRequested(int)), SLOT(removeTab(int)));
            connect(tab2, SIGNAL(actionTriggered(QAction*)), SLOT(cmd(QAction*)));
            splitter->addWidget(tab2);
            tab2->setVisible(false);
            }
      else
            tab2 = 0;
      layout->addWidget(splitter);


      //---------------------------------------------------
      //    Transport Action
      //---------------------------------------------------

      QAction* a;
#ifdef HAS_MIDI
      a  = getAction("midi-on");
      a->setEnabled(preferences.enableMidiInput);
      a->setChecked(_midiinEnabled);
#endif

      getAction("undo")->setEnabled(false);
      getAction("redo")->setEnabled(false);
      getAction("paste")->setEnabled(false);
      getAction("swap")->setEnabled(false);
      selectionChanged(SelState::NONE);

      //---------------------------------------------------
      //    File Tool Bar
      //---------------------------------------------------

      fileTools = addToolBar("");
      fileTools->setObjectName("file-operations");
      if (qApp->layoutDirection() == Qt::LayoutDirection::LeftToRight) {
            for (auto i : { "file-new", "file-open", "file-save", "print", "undo", "redo"})
                  fileTools->addWidget(new AccessibleToolButton(fileTools, getAction(i)));
            }
      else {
            for (auto i : { "file-new", "file-open", "file-save", "print", "redo", "undo"})
                  fileTools->addWidget(new AccessibleToolButton(fileTools, getAction(i)));
            }

      fileTools->addSeparator();
      mag = new MagBox;
      connect(mag, SIGNAL(magChanged(MagIdx)), SLOT(magChanged(MagIdx)));
      fileTools->addWidget(mag);
      viewModeCombo = new QComboBox(this);
#if defined(Q_OS_MAC)
      viewModeCombo->setFocusPolicy(Qt::StrongFocus);
#else
      viewModeCombo->setFocusPolicy(Qt::TabFocus);
#endif
      viewModeCombo->setFixedHeight(preferences.iconHeight + 8);  // hack
      viewModeCombo->addItem("",       int(LayoutMode::PAGE));
      viewModeCombo->addItem("", int(LayoutMode::LINE));
      viewModeCombo->addItem("", int(LayoutMode::SYSTEM));
      connect(viewModeCombo, SIGNAL(activated(int)), SLOT(switchLayoutMode(int)));
      fileTools->addWidget(viewModeCombo);

      //---------------------
      //    Transport Tool Bar
      //---------------------

      transportTools = addToolBar("");
      transportTools->setObjectName("transport-tools");
#ifdef HAS_MIDI
      transportTools->addWidget(new AccessibleToolButton(transportTools, getAction("midi-on")));
      transportTools->addSeparator();
#endif
      transportTools->addWidget(new AccessibleToolButton(transportTools, getAction("rewind")));
      _playButton = new AccessibleToolButton(transportTools, getAction("play"));
      transportTools->addWidget(_playButton);
      transportTools->addWidget(new AccessibleToolButton(transportTools, getAction("loop")));
      transportTools->addSeparator();
      QAction* repeatAction = getAction("repeat");
      repeatAction->setChecked(MScore::playRepeats);
      transportTools->addWidget(new AccessibleToolButton(transportTools, repeatAction));
      transportTools->addWidget(new AccessibleToolButton(transportTools, getAction("pan")));
      transportTools->addWidget(new AccessibleToolButton(transportTools, metronomeAction));

      //-------------------------------
      //    Concert Pitch Tool Bar
      //-------------------------------

      cpitchTools = addToolBar("");
      cpitchTools->setObjectName("pitch-tools");
      a = getAction("concert-pitch");
      a->setCheckable(true);
      cpitchTools->addWidget(new AccessibleToolButton(cpitchTools, a));

      //-------------------------------
      //    Image Capture Tool Bar
      //-------------------------------

      fotoTools = addToolBar("");
      fotoTools->setObjectName("foto-tools");
      fotoTools->addWidget(new AccessibleToolButton(fotoTools, getAction("fotomode")));

      addToolBarBreak();

      //-------------------------------
      //    Note Input Tool Bar
      //-------------------------------

      entryTools = addToolBar("");
      entryTools->setObjectName("entry-tools");

      populateNoteInputMenu();

      //---------------------
      //    Menus
      //---------------------

      QMenuBar* mb = menuBar();

      //---------------------
      //    Menu File
      //---------------------

      menuFile = mb->addMenu("");
      menuFile->setObjectName("File");

      a = getAction("startcenter");
      a->setCheckable(true);
      menuFile->addAction(a);
      menuFile->addAction(getAction("file-new"));
      menuFile->addAction(getAction("file-open"));

      openRecent = menuFile->addMenu("");

      connect(openRecent, SIGNAL(aboutToShow()), SLOT(openRecentMenu()));
      connect(openRecent, SIGNAL(triggered(QAction*)), SLOT(selectScore(QAction*)));

      for (auto i : {
            "", "file-save", "file-save-as", "file-save-a-copy",
            "file-save-selection", "file-save-online", "file-export", "file-part-export", "file-import-pdf",
            "", "file-close", "", "parts", "album" }) {
            if (!*i)
                  menuFile->addSeparator();
            else if (enableExperimental || strcmp(i,"file-save-online") != 0)
                  menuFile->addAction(getAction(i));
            }
      if (enableExperimental)
            menuFile->addAction(getAction("layer"));
      menuFile->addSeparator();
      menuFile->addAction(getAction("edit-info"));
      if (enableExperimental)
            menuFile->addAction(getAction("media"));
      menuFile->addSeparator();
      menuFile->addAction(getAction("print"));
#ifndef Q_OS_MAC
      menuFile->addSeparator();
      menuFile->addAction(getAction("quit"));
#endif

      //---------------------
      //    Menu Edit
      //---------------------

      menuEdit = mb->addMenu("");
      menuEdit->setObjectName("Edit");
      menuEdit->addAction(getAction("undo"));
      menuEdit->addAction(getAction("redo"));

      menuEdit->addSeparator();
      menuEdit->addAction(getAction("cut"));
      menuEdit->addAction(getAction("copy"));
      menuEdit->addAction(getAction("paste"));
      menuEdit->addAction(getAction("swap"));

      menuEdit->addSeparator();
      menuEdit->addAction(getAction("select-all"));
      menuEdit->addAction(getAction("select-section"));
      menuEdit->addAction(getAction("find"));

      menuEdit->addSeparator();

      menuEdit->addAction(getAction("instruments"));

#ifdef NDEBUG
      if (enableExperimental) {
#endif
            menuEdit->addSeparator();
            menuEdit->addAction(getAction("debugger"));
#ifdef NDEBUG
            }
#endif

      menuEdit->addSeparator();
      pref = menuEdit->addAction("", this, SLOT(startPreferenceDialog()));
      pref->setMenuRole(QAction::PreferencesRole);

      //---------------------
      //    Menu View
      //---------------------

      menuView = mb->addMenu("");
      menuView->setObjectName("View");

      a = getAction("toggle-palette");
      a->setCheckable(true);
      menuView->addAction(a);

      a = getAction("masterpalette");
      a->setCheckable(true);
      menuView->addAction(a);

      a = getAction("inspector");
      a->setCheckable(true);
      menuView->addAction(a);
#ifdef OMR
      a = getAction("omr");
      a->setCheckable(true);
      menuView->addAction(a);
#endif
      playId = getAction("toggle-playpanel");
      playId->setCheckable(true);
      menuView->addAction(playId);

      a = getAction("toggle-navigator");
      a->setCheckable(true);
      a->setChecked(preferences.showNavigator);
      menuView->addAction(a);

      a = getAction("toggle-mixer");
      a->setCheckable(true);
      menuView->addAction(a);

      a = getAction("synth-control");
      a->setCheckable(true);
      menuView->addAction(a);

      a = getAction("toggle-selection-window");
      a->setCheckable(true);
      menuView->addAction(a);

      a = getAction("toggle-piano");
      a->setCheckable(true);
      menuView->addAction(a);

      menuView->addSeparator();
      menuView->addAction(getAction("zoomin"));
      menuView->addAction(getAction("zoomout"));
      menuView->addSeparator();

      menuToolbars = new QMenu();

      a = getAction("toggle-fileoperations");
      a->setCheckable(true);
      a->setChecked(fileTools->isVisible());
      connect(fileTools, SIGNAL(visibilityChanged(bool)), a, SLOT(setChecked(bool)));
      menuToolbars->addAction(a);

      a = getAction("toggle-transport");
      a->setCheckable(true);
      a->setChecked(transportTools->isVisible());
      connect(transportTools, SIGNAL(visibilityChanged(bool)), a, SLOT(setChecked(bool)));
      menuToolbars->addAction(a);

      a = getAction("toggle-concertpitch");
      a->setCheckable(true);
      a->setChecked(cpitchTools->isVisible());
      connect(cpitchTools, SIGNAL(visibilityChanged(bool)), a, SLOT(setChecked(bool)));
      menuToolbars->addAction(a);

      a = getAction("toggle-imagecapture");
      a->setCheckable(true);
      a->setChecked(fotoTools->isVisible());
      connect(fotoTools, SIGNAL(visibilityChanged(bool)), a, SLOT(setChecked(bool)));
      menuToolbars->addAction(a);

      a = getAction("toggle-noteinput");
      a->setCheckable(true);
      a->setChecked(entryTools->isVisible());
      connect(entryTools, SIGNAL(visibilityChanged(bool)), a, SLOT(setChecked(bool)));
      menuToolbars->addAction(a);

      menuToolbars->addSeparator();

      menuToolbars->addAction(getAction("edit-toolbars"));

      menuView->addMenu(menuToolbars);

      menuWorkspaces = new QMenu();
      connect(menuWorkspaces, SIGNAL(aboutToShow()), SLOT(showWorkspaceMenu()));
      menuView->addMenu(menuWorkspaces);

      a = getAction("toggle-statusbar");
      a->setCheckable(true);
      a->setChecked(true);
      menuView->addAction(a);

      menuView->addSeparator();
      a = getAction("split-h");
      a->setCheckable(true);
      menuView->addAction(a);
      a = getAction("split-v");
      a->setCheckable(true);
      menuView->addAction(a);

      menuView->addSeparator();
      menuView->addAction(getAction("show-invisible"));
      menuView->addAction(getAction("show-unprintable"));
      menuView->addAction(getAction("show-frames"));
      menuView->addAction(getAction("show-pageborders"));
      menuView->addAction(getAction("mark-irregular"));
      menuView->addSeparator();

      a = getAction("fullscreen");
      a->setCheckable(true);
      a->setChecked(false);
#ifndef Q_OS_MAC
      menuView->addAction(a);
#endif

      //---------------------
      //    Menu Add
      //---------------------

      menuAdd = mb->addMenu("");
      menuAdd->setObjectName("Add");

      menuAddPitch = new QMenu();
      menuAddPitch->addAction(getAction("note-input"));
      menuAddPitch->addSeparator();

      for (int i = 0; i < 7; ++i) {
            char buffer[8];
            sprintf(buffer, "note-%c", "cdefgab"[i]);
            a = getAction(buffer);
            menuAddPitch->addAction(a);
            }
      menuAddPitch->addSeparator();
      for (int i = 0; i < 7; ++i) {
            char buffer[8];
            sprintf(buffer, "chord-%c", "cdefgab"[i]);
            a = getAction(buffer);
            menuAddPitch->addAction(a);
            }
      menuAdd->addMenu(menuAddPitch);

      menuAddInterval = new QMenu();
      for (int i = 1; i < 10; ++i) {
            char buffer[16];
            sprintf(buffer, "interval%d", i);
            a = getAction(buffer);
            menuAddInterval->addAction(a);
            }
      menuAddInterval->addSeparator();
      for (int i = 2; i < 10; ++i) {
            char buffer[16];
            sprintf(buffer, "interval-%d", i);
            a = getAction(buffer);
            menuAddInterval->addAction(a);
            }
      menuAdd->addMenu(menuAddInterval);

      menuTuplet = new QMenu();
      for (auto i : { "duplet", "triplet", "quadruplet", "quintuplet", "sextuplet",
            "septuplet", "octuplet", "nonuplet", "tuplet-dialog" })
            menuTuplet->addAction(getAction(i));
      menuAdd->addMenu(menuTuplet);

      menuAdd->addSeparator();

      menuAddMeasures = new QMenu("");
      menuAddMeasures->addAction(getAction("insert-measure"));
      menuAddMeasures->addAction(getAction("insert-measures"));
      menuAddMeasures->addSeparator();
      menuAddMeasures->addAction(getAction("append-measure"));
      menuAddMeasures->addAction(getAction("append-measures"));
      menuAdd->addMenu(menuAddMeasures);

      menuAddFrames = new QMenu();
      menuAddFrames->addAction(getAction("insert-hbox"));
      menuAddFrames->addAction(getAction("insert-vbox"));
      menuAddFrames->addAction(getAction("insert-textframe"));
      if (enableExperimental)
            menuAddFrames->addAction(getAction("insert-fretframe"));
      menuAddFrames->addSeparator();
      menuAddFrames->addAction(getAction("append-hbox"));
      menuAddFrames->addAction(getAction("append-vbox"));
      menuAddFrames->addAction(getAction("append-textframe"));
      menuAdd->addMenu(menuAddFrames);

      menuAddText = new QMenu();
      menuAddText->addAction(getAction("title-text"));
      menuAddText->addAction(getAction("subtitle-text"));
      menuAddText->addAction(getAction("composer-text"));
      menuAddText->addAction(getAction("poet-text"));
      menuAddText->addAction(getAction("part-text"));
      menuAddText->addSeparator();
      menuAddText->addAction(getAction("system-text"));
      menuAddText->addAction(getAction("staff-text"));
      menuAddText->addAction(getAction("expression-text"));
      menuAddText->addAction(getAction("chord-text"));
      menuAddText->addAction(getAction("rehearsalmark-text"));
      menuAddText->addAction(getAction("instrument-change-text"));
      menuAddText->addSeparator();
      menuAddText->addAction(getAction("lyrics"));
      menuAddText->addAction(getAction("figured-bass"));
      menuAddText->addAction(getAction("tempo"));
      menuAdd->addMenu(menuAddText);

      menuAddLines = new QMenu();
      menuAddLines->addAction(getAction("add-slur"));
      menuAddLines->addAction(getAction("add-hairpin"));
      menuAddLines->addAction(getAction("add-hairpin-reverse"));
      menuAddLines->addAction(getAction("add-8va"));
      menuAddLines->addAction(getAction("add-8vb"));
      menuAddLines->addAction(getAction("add-noteline"));
      menuAdd->addMenu(menuAddLines);

      //---------------------
      //    Menu Format
      //---------------------

      menuFormat = mb->addMenu("");
      menuFormat->setObjectName("Format");

      menuFormat->addAction(getAction("edit-style"));
//      menuFormat->addAction(getAction("edit-text-style"));
      menuFormat->addAction(getAction("page-settings"));
      menuFormat->addSeparator();

      menuFormat->addAction(getAction("add-remove-breaks"));

      QMenu* menuStretch = new QMenu(tr("&Stretch"));
      for (auto i : { "stretch+", "stretch-", "reset-stretch" })
            menuStretch->addAction(getAction(i));
      menuFormat->addMenu(menuStretch);
      menuFormat->addSeparator();

      menuFormat->addAction(getAction("reset-beammode"));
      menuFormat->addAction(getAction("reset"));
      menuFormat->addSeparator();

      if (enableExperimental)
            menuFormat->addAction(getAction("edit-harmony"));

      menuFormat->addSeparator();
      menuFormat->addAction(getAction("load-style"));
      menuFormat->addAction(getAction("save-style"));

      //---------------------
      //    Menu Tools
      //---------------------

      menuTools = mb->addMenu("");
      menuTools->setObjectName("Tools");

      menuTools->addAction(getAction("transpose"));
      menuTools->addSeparator();
      menuTools->addAction(getAction("explode"));
      menuTools->addAction(getAction("implode"));

      menuVoices = new QMenu("");
      for (auto i : { "voice-x12", "voice-x13", "voice-x14", "voice-x23", "voice-x24", "voice-x34" })
            menuVoices->addAction(getAction(i));
      menuTools->addMenu(menuVoices);
      menuTools->addSeparator();

      menuTools->addAction(getAction("slash-fill"));
      menuTools->addAction(getAction("slash-rhythm"));
      menuTools->addSeparator();

      menuTools->addAction(getAction("pitch-spell"));
      menuTools->addAction(getAction("reset-groupings"));
      menuTools->addAction(getAction("resequence-rehearsal-marks"));
      menuTools->addSeparator();

      menuTools->addAction(getAction("copy-lyrics-to-clipboard"));
      menuTools->addAction(getAction("fotomode"));

      menuTools->addAction(getAction("del-empty-measures"));

      //---------------------
      //    Menu Plugins
      //---------------------

      menuPlugins = mb->addMenu("");
      menuPlugins->setObjectName("Plugins");

      menuPlugins->addAction(getAction("plugin-manager"));

      a = getAction("plugin-creator");
      a->setCheckable(true);
      menuPlugins->addAction(a);

      menuPlugins->addSeparator();

      //---------------------
      //    Menu Debug
      //---------------------

#ifndef NDEBUG
      QMenu* menuDebug = mb->addMenu("Debug");
      menuDebug->setObjectName("Debug");
      a = getAction("no-horizontal-stretch");
      a->setCheckable(true);
      menuDebug->addAction(a);
      a = getAction("no-vertical-stretch");
      a->setCheckable(true);
      menuDebug->addAction(a);
      menuDebug->addSeparator();
      a = getAction("show-segment-shapes");
      a->setCheckable(true);
      menuDebug->addAction(a);
      a = getAction("show-measure-shapes");
      a->setCheckable(true);
      menuDebug->addAction(a);
      a = getAction("show-bounding-rect");
      a->setCheckable(true);
      menuDebug->addAction(a);
      a = getAction("show-corrupted-measures");
      a->setCheckable(true);
      a->setChecked(true);
      menuDebug->addAction(a);
#endif

      //---------------------
      //    Menu Help
      //---------------------

      mb->addSeparator();
      menuHelp = mb->addMenu("");
      menuHelp->setObjectName("Help");

#if 0
      if (_helpEngine) {
            HelpQuery* hw = new HelpQuery(menuHelp);
            menuHelp->addAction(hw);
            connect(menuHelp, SIGNAL(aboutToShow()), hw, SLOT(setFocus()));
            }
#endif
      //menuHelp->addAction(getAction("help"));
      onlineHandbookAction = menuHelp->addAction("", this, SLOT(helpBrowser1()));

      menuHelp->addSeparator();

      aboutAction = new QAction("", 0);

      aboutAction->setMenuRole(QAction::AboutRole);
      connect(aboutAction, SIGNAL(triggered()), this, SLOT(about()));
      menuHelp->addAction(aboutAction);

      aboutQtAction = new QAction("", 0);
      aboutQtAction->setMenuRole(QAction::AboutQtRole);
      connect(aboutQtAction, SIGNAL(triggered()), this, SLOT(aboutQt()));
      menuHelp->addAction(aboutQtAction);

      aboutMusicXMLAction = new QAction("", 0);
      aboutMusicXMLAction->setMenuRole(QAction::ApplicationSpecificRole);
      connect(aboutMusicXMLAction, SIGNAL(triggered()), this, SLOT(aboutMusicXML()));
      menuHelp->addAction(aboutMusicXMLAction);

#if defined(Q_OS_MAC) || defined(Q_OS_WIN)
      checkForUpdateAction = menuHelp->addAction("", this, SLOT(checkForUpdate()));
#endif

      menuHelp->addSeparator();
      askForHelpAction = menuHelp->addAction("", this, SLOT(askForHelp()));
      reportBugAction = menuHelp->addAction("", this, SLOT(reportBug()));

      menuHelp->addSeparator();
      menuHelp->addAction(getAction("resource-manager"));
      menuHelp->addSeparator();
      revertToFactoryAction = menuHelp->addAction("", this, SLOT(resetAndRestart()));

      if (!MScore::noGui) {
            retranslate(true);
            //accessibility for menus
            for (QMenu* menu : mb->findChildren<QMenu*>()) {
                  menu->setAccessibleName(menu->objectName());
                  menu->setAccessibleDescription(Shortcut::getMenuShortcutString(menu));
                  }
            }

      setCentralWidget(envelope);

      // load cascading instrument templates
      loadInstrumentTemplates(preferences.instrumentList1);
      if (!preferences.instrumentList2.isEmpty())
            loadInstrumentTemplates(preferences.instrumentList2);

      preferencesChanged();
      if (seq) {
            connect(seq, SIGNAL(started()), SLOT(seqStarted()));
            connect(seq, SIGNAL(stopped()), SLOT(seqStopped()));
            }
      loadScoreList();

      QClipboard* cb = QApplication::clipboard();
      connect(cb, SIGNAL(dataChanged()), SLOT(clipboardChanged()));
      connect(cb, SIGNAL(selectionChanged()), SLOT(clipboardChanged()));
      autoSaveTimer = new QTimer(this);
      autoSaveTimer->setSingleShot(true);
      connect(autoSaveTimer, SIGNAL(timeout()), this, SLOT(autoSaveTimerTimeout()));
      initOsc();
      startAutoSave();

      if (enableExperimental) {
            cornerLabel = new QLabel(this);
            cornerLabel->setScaledContents(true);
            cornerLabel->setPixmap(QPixmap(":/data/mscore.png"));
            cornerLabel->setGeometry(width() - 48, 0, 48, 48);
            }
      }

MuseScore::~MuseScore()
      {
      delete synti;
      }

//---------------------------------------------------------
//   showError
//---------------------------------------------------------

void MuseScore::showError()
      {
      static QErrorMessage* msg = 0;
      if (msg == 0)
            msg = new QErrorMessage(this);
      msg->showMessage(tr(MScore::errorMessage()), MScore::errorGroup());
      MScore::setError(MS_NO_ERROR);
      }

//---------------------------------------------------------
//   retranslate
//---------------------------------------------------------

void MuseScore::retranslate(bool firstStart)
      {
      _positionLabel->setToolTip(tr("Measure:Beat:Tick"));

      // retranslate the menu
      menuFile->setTitle(tr("&File"));
      openRecent->setTitle(tr("Open &Recent"));
      menuEdit->setTitle(tr("&Edit"));
      menuView->setTitle(tr("&View"));
      menuToolbars->setTitle(tr("&Toolbars"));
      menuWorkspaces->setTitle(tr("W&orkspaces"));
      pref->setText(tr("&Preferences..."));
      menuAdd->setTitle(tr("&Add"));
      menuAddMeasures->setTitle(tr("&Measures"));
      menuAddFrames->setTitle(tr("&Frames"));
      menuAddText->setTitle(tr("&Text"));
      menuAddLines->setTitle(tr("&Lines"));
      menuAddPitch->setTitle(tr("N&otes"));
      menuAddInterval->setTitle(tr("&Intervals"));
      menuTuplet->setTitle(tr("T&uplets"));
      menuFormat->setTitle(tr("F&ormat"));
      menuTools->setTitle(tr("&Tools"));
      menuVoices->setTitle(tr("&Voices"));
      menuPlugins->setTitle(tr("&Plugins"));
      menuHelp->setTitle(tr("&Help"));

      aboutAction->setText(tr("&About..."));
      aboutQtAction->setText(tr("About &Qt..."));
      aboutMusicXMLAction->setText(tr("About &MusicXML..."));
      onlineHandbookAction->setText(tr("&Online Handbook"));
      if (checkForUpdateAction)
            checkForUpdateAction->setText(tr("Check for &Update"));
      askForHelpAction->setText(tr("Ask for Help"));
      reportBugAction->setText(tr("Report a Bug"));
      revertToFactoryAction->setText(tr("Revert to Factory Settings"));

      fileTools->setWindowTitle(tr("File Operations"));
      transportTools->setWindowTitle(tr("Playback Controls"));
      cpitchTools->setWindowTitle(tr("Concert Pitch"));
      fotoTools->setWindowTitle(tr("Image Capture"));
      entryTools->setWindowTitle(tr("Note Input"));

      viewModeCombo->setAccessibleName(tr("View Mode"));
      viewModeCombo->setItemText(viewModeCombo->findData(int(LayoutMode::PAGE)), tr("Page View"));
      viewModeCombo->setItemText(viewModeCombo->findData(int(LayoutMode::LINE)), tr("Continuous View"));
      viewModeCombo->setItemText(viewModeCombo->findData(int(LayoutMode::SYSTEM)), tr("Single Page"));

      showMidiImportButton->setText(tr("Show MIDI import panel"));

      Shortcut::retranslate();
      if (!firstStart && Workspace::currentWorkspace->readOnly()) {
            changeWorkspace(Workspace::currentWorkspace);
            }
      }

//---------------------------------------------------------
//   resizeEvent
//---------------------------------------------------------

void MuseScore::resizeEvent(QResizeEvent*)
      {
      if (enableExperimental) {
            cornerLabel->setGeometry(width() - 48, 0, 48, 48);
            }
      }

//---------------------------------------------------------
//   startAutoSave
//---------------------------------------------------------

void MuseScore::startAutoSave()
      {
      if (preferences.autoSave) {
            int t = preferences.autoSaveTime * 60 * 1000;
            autoSaveTimer->start(t);
            }
      else
            autoSaveTimer->stop();
      }

//---------------------------------------------------------
//   getLocaleISOCode
//---------------------------------------------------------

QString MuseScore::getLocaleISOCode() const
      {
      QString lang;
      if (localeName.toLower() == "system")
            lang = QLocale::system().name();
      else
            lang = localeName;
      return lang;
      }

//---------------------------------------------------------
//   helpBrowser1
//    show online help
//---------------------------------------------------------

void MuseScore::helpBrowser1() const
      {
      QString lang = getLocaleISOCode();

      if (MScore::debugMode)
            qDebug("open online handbook for language <%s>", qPrintable(lang));
      QString help = QString("https://musescore.org/redirect/help?tag=handbook&locale=%1").arg(getLocaleISOCode());
      //try to find an exact match
      bool found = false;
      foreach (LanguageItem item, _languages) {
            if (item.key == lang) {
                  QString handbook = item.handbook;
                  if (!handbook.isNull()) {
                      help = item.handbook;
                      found = true;
                      }
                  break;
                  }
            }
      //try a to find a match on first two letters
      if (!found && lang.size() > 2) {
            lang = lang.left(2);
            foreach (LanguageItem item, _languages) {
                  if (item.key == lang){
                      QString handbook = item.handbook;
                      if (!handbook.isNull())
                          help = item.handbook;
                      break;
                      }
                  }
            }

      //track visits. see: http://www.google.com/support/googleanalytics/bin/answer.py?answer=55578
      help += QString("&utm_source=desktop&utm_medium=menu&utm_content=%1&utm_campaign=MuseScore%2").arg(rev.trimmed()).arg(QString(VERSION));
      QDesktopServices::openUrl(QUrl(help));
      }

//---------------------------------------------------------
//   resetAndRestart
//---------------------------------------------------------

void MuseScore::resetAndRestart()
      {
      int ret = QMessageBox::question(0, tr("Are you sure?"),
                  tr("This will reset all your preferences.\n"
                   "Custom palettes, custom shortcuts, and the list of recent scores will be deleted. "
                   "MuseScore will restart with its default settings.\n"
                   "Reverting will not remove any scores from your computer.\n"
                   "Are you sure you want to proceed?"),
                   QMessageBox::Yes|QMessageBox::No, QMessageBox::No);
      if (ret == QMessageBox::Yes ) {
             close();
             QStringList args("-F");
             QProcess::startDetached(qApp->arguments()[0], args);
             }
      }

//---------------------------------------------------------
//   aboutQt
//---------------------------------------------------------

void MuseScore::aboutQt()
      {
      QMessageBox::aboutQt(this, QString("About Qt"));
      }

//---------------------------------------------------------
//   aboutMusicXML
//---------------------------------------------------------

void MuseScore::aboutMusicXML()
      {
      AboutMusicXMLBoxDialog ab;
      ab.show();
      ab.exec();
      }

//---------------------------------------------------------
//   selectScore
//    "open recent"
//---------------------------------------------------------

void MuseScore::selectScore(QAction* action)
      {
      QString a = action->data().toString();
      if (!a.isEmpty()) {
            if (a == "clear-recent") {
                  _recentScores.clear();
                  if (startcenter)
                        startcenter->updateRecentScores();
                  }
            else {
                  MasterScore* score = readScore(a);
                  if (score) {
                        setCurrentScoreView(appendScore(score));
                        addRecentScore(score);
                        writeSessionFile(false);
                        }
                  }
            }
      }

//---------------------------------------------------------
//   selectionChanged
//---------------------------------------------------------

void MuseScore::selectionChanged(SelState selectionState)
      {
      bool enable = selectionState != SelState::NONE;
      getAction("cut")->setEnabled(enable);
      getAction("copy")->setEnabled(enable);
      getAction("select-similar-range")->setEnabled(selectionState == SelState::RANGE);
      if (pianorollEditor)
            pianorollEditor->changeSelection(selectionState);
      if (drumrollEditor)
            drumrollEditor->changeSelection(selectionState);
      if (_inspector)
            updateInspector();
      }

//---------------------------------------------------------
//   updateInspector
//---------------------------------------------------------

void MuseScore::updateInspector()
      {
      if (!_inspector)
            return;
      if (_inspector->isVisible() && cs) {
            if (state() == STATE_EDIT)
                  _inspector->setElement(cv->getEditObject());
            else if (state() == STATE_FOTO)
                  _inspector->setElement(cv->fotoLasso());
            else
                  _inspector->setElements(cs->selection().elements());
            }
      else
            _inspector->setElement(0);
      }

//---------------------------------------------------------
//   updateShadowNote
//   update the shadow note in the current ScoreView
//---------------------------------------------------------

void MuseScore::updateShadowNote()
      {
      currentScoreView()->updateShadowNotes();
      }

//---------------------------------------------------------
//   appendScore
//    append score to project list
//---------------------------------------------------------

int MuseScore::appendScore(MasterScore* score)
      {
      int index = scoreList.size();
      for (int i = 0; i < scoreList.size(); ++i) {
            if ((!score->importedFilePath().isEmpty()
                 && scoreList[i]->importedFilePath() == score->importedFilePath())
                    || (scoreList[i]->fileInfo()->canonicalFilePath() == score->fileInfo()->canonicalFilePath() && score->fileInfo()->exists())) {
                  removeTab(i);
                  index = i;
                  break;
                  }
            }
      scoreList.insert(index, score);
      tab1->blockSignals(true);
      if (tab2)
            tab2->blockSignals(true);
      tab1->insertTab(score);
      if (tab2)
            tab2->insertTab(score);
      tab1->blockSignals(false);
      if (tab2)
            tab2->blockSignals(false);
      return index;
      }

//---------------------------------------------------------
//   addRecentScore
//---------------------------------------------------------

void MuseScore::addRecentScore(Score* score)
      {
      QString path = score->importedFilePath(); // defined for scores imported from e.g. MIDI files
      addRecentScore(path);
      path = score->masterScore()->fileInfo()->absoluteFilePath();
      addRecentScore(path);
      if (startcenter)
            startcenter->updateRecentScores();
      }

void MuseScore::addRecentScore(const QString& scorePath)
      {
      if (scorePath.isEmpty())
            return;
      QFileInfo fi(scorePath);
      QString absoluteFilePath = fi.absoluteFilePath();
      _recentScores.removeAll(absoluteFilePath);
      _recentScores.prepend(absoluteFilePath);
      if (_recentScores.size() > RECENT_LIST_SIZE)
            _recentScores.removeLast();
      }

#if 0
//---------------------------------------------------------
//   updateTabNames
//---------------------------------------------------------

void MuseScore::updateTabNames()
      {
      for (int i = 0; i < tab1->count(); ++i) {
            ScoreView* view = tab1->view(i);
            if (view)
                  tab1->setTabText(i, view->score()->masterScore()->fileInfo()->completeBaseName());
            }
      if (tab2) {
            for (int i = 0; i < tab2->count(); ++i) {
                  ScoreView* view = tab2->view(i);
                  if (view)
                        tab2->setTabText(i, view->score()->masterScore()->fileInfo()->completeBaseName());
                  }
            }
      }
#endif

//---------------------------------------------------------
//   loadScoreList
//    read list of "Recent Scores"
//---------------------------------------------------------

void MuseScore::loadScoreList()
      {
      if (useFactorySettings)
            return;
      QSettings s;
      for (int i = RECENT_LIST_SIZE-1; i >= 0; --i) {
            QString path = s.value(QString("recent-%1").arg(i),"").toString();
            if (!path.isEmpty() && QFileInfo(path).exists()) {
                  _recentScores.removeAll(path);
                  _recentScores.prepend(path);
                  }
            }
      }

//---------------------------------------------------------
//   openRecentMenu
//---------------------------------------------------------

void MuseScore::openRecentMenu()
      {
      openRecent->clear();
      bool one = false;
      for (const QFileInfo& fi : recentScores()) {
            QAction* action = openRecent->addAction(fi.fileName().replace("&", "&&"));  // show filename only
            QString data(fi.canonicalFilePath());
            action->setData(data);
            action->setToolTip(data);
            one = true;
            }
      if (one) {
            openRecent->addSeparator();
            QAction* action = openRecent->addAction(tr("Clear Recent Files"));
            action->setData("clear-recent");
            }
      }

//---------------------------------------------------------
//   setCurrentView
//---------------------------------------------------------

void MuseScore::setCurrentScoreView(int idx)
      {
      setCurrentView(0, idx);
      if (tab2)
            setCurrentView(1, idx);
      }

void MuseScore::setCurrentView(int tabIdx, int idx)
      {
      if (idx == -1)
            setCurrentScoreView(nullptr);
      else {
            ScoreTab* tab = tabIdx ? tab2 : tab1;
            if (tab)
                  tab->setCurrentIndex(idx);
            }
      }

//---------------------------------------------------------
//   setCurrentScoreView
//---------------------------------------------------------

void MuseScore::setCurrentScoreView(ScoreView* view)
      {
      cv = view;
      if (cv) {
            if (cv->score() && (cs != cv->score())) {
                  // exit note entry mode
                  if (cv->noteEntryMode()) {
                        cv->cmd(getAction("escape"));
                        qApp->processEvents();
                        }
                  updateInputState(cv->score());
                  }
            cs = cv->score();
            cv->setFocusRect();
            }
      else
            cs = 0;

                  // set midi import panel
      QString fileName = cs ? cs->importedFilePath() : "";
      midiPanelOnSwitchToFile(fileName);

      if (enableExperimental) {
            updateLayer();
            updatePlayMode();
            }

      if (seq)
            seq->setScoreView(cv);
      if (playPanel)
            playPanel->setScore(cs);
      if (synthControl)
            synthControl->setScore(cs);
      if (selectionWindow)
            selectionWindow->setScore(cs);
      if (mixer)
            mixer->updateAll(cs ? cs->masterScore() : nullptr);
#ifdef OMR
      if (omrPanel) {
            if (cv && cv->omrView())
                  omrPanel->setOmrView(cv->omrView());
            else
                  omrPanel->setOmrView(0);
            }
#endif
      if (!cs) {
            setWindowTitle(MUSESCORE_NAME_VERSION);
            if (_navigator && _navigator->widget()) {
                  navigator()->setScoreView(cv);
                  navigator()->setScore(0);
                  }
            if (_inspector)
                  _inspector->setElement(0);
            viewModeCombo->setEnabled(false);
            if (_textTools) {
                  _textTools->hide();
                  if (textPalette)
                        textPalette->hide();
                  }
            if (_pianoTools)
                  _pianoTools->hide();
            if (_drumTools)
                  _drumTools->hide();
            changeState(STATE_DISABLED);
            return;
            }

      viewModeCombo->setEnabled(true);
      updateViewModeCombo();

      selectionChanged(cs->selection().state());

      _sstate = STATE_DISABLED; // defeat optimization
      changeState(cv->mscoreState());

      cv->setFocus(Qt::OtherFocusReason);
      setFocusProxy(cv);

      getAction("file-save")->setEnabled(cs->masterScore()->isSavable());
      getAction("file-part-export")->setEnabled(cs->masterScore()->excerpts().size() > 0);
      getAction("show-invisible")->setChecked(cs->showInvisible());
      getAction("show-unprintable")->setChecked(cs->showUnprintable());
      getAction("show-frames")->setChecked(cs->showFrames());
      getAction("show-pageborders")->setChecked(cs->showPageborders());
      getAction("mark-irregular")->setChecked(cs->markIrregularMeasures());
      getAction("fotomode")->setChecked(cv->fotoMode());
      getAction("join-measures")->setEnabled(cs->masterScore()->excerpts().size() == 0);
      getAction("split-measure")->setEnabled(cs->masterScore()->excerpts().size() == 0);
      updateUndoRedo();

      MagIdx midx = cv->magIdx();
      if (midx == MagIdx::MAG_FREE)
            mag->setMag(view->lmag());
      else {
            mag->setMagIdx(midx);
            magChanged(midx);
            }

      setWindowTitle(MUSESCORE_NAME_VERSION ": " + cs->title());

      QAction* a = getAction("concert-pitch");
      a->setChecked(cs->styleB(StyleIdx::concertPitch));

      setPos(cs->inputPos());
      //showMessage(cs->filePath(), 2000);
      if (_navigator && _navigator->widget()) {
            navigator()->setScoreView(view);
            navigator()->setScore(cs);
            }
      ScoreAccessibility::instance()->updateAccessibilityInfo();
      }

//---------------------------------------------------------
//   updateViewModeCombo
//---------------------------------------------------------

void MuseScore::updateViewModeCombo()
      {
      int idx;
      switch (cs->layoutMode()) {
            case LayoutMode::PAGE:
            case LayoutMode::FLOAT:
                  idx = 0;
                  break;
            case LayoutMode::LINE:
                  idx = 1;
                  break;
            case LayoutMode::SYSTEM:
                  idx = 2;
                  break;
            default:
                  idx = 0;
                  break;
            }
      viewModeCombo->setCurrentIndex(idx);
      }

//---------------------------------------------------------
//   showMessage
//---------------------------------------------------------

void MuseScore::showMessage(const QString& s, int timeout)
      {
      _statusBar->showMessage(s, timeout);
      }

//---------------------------------------------------------
//   midiPanel
//---------------------------------------------------------

void MuseScore::midiPanelOnSwitchToFile(const QString &file)
      {
      bool isMidiFile = ImportMidiPanel::isMidiFile(file);
      if (isMidiFile) {
            importmidiPanel->setMidiFile(file);
            if (importmidiPanel->isPreferredVisible())
                  importmidiPanel->setVisible(true);
            }
      else
            importmidiPanel->setVisible(false);
      importmidiShowPanel->setVisible(!importmidiPanel->isPreferredVisible() && isMidiFile);
      }

//---------------------------------------------------------
//   midiPanelOnCloseFile
//---------------------------------------------------------

void MuseScore::midiPanelOnCloseFile(const QString &file)
      {
      if (ImportMidiPanel::isMidiFile(file))
            importmidiPanel->excludeMidiFile(file);
      }

//---------------------------------------------------------
//   allowShowMidiPanel
//---------------------------------------------------------

void MuseScore::allowShowMidiPanel(const QString &file)
      {
      if (ImportMidiPanel::isMidiFile(file))
            importmidiPanel->setPreferredVisible(true);
      }

//---------------------------------------------------------
//   setMidiReopenInProgress
//---------------------------------------------------------

void MuseScore::setMidiReopenInProgress(const QString &file)
      {
      if (ImportMidiPanel::isMidiFile(file))
            importmidiPanel->setReopenInProgress();
      }

//---------------------------------------------------------
//   showMidiImportPanel
//---------------------------------------------------------

void MuseScore::showMidiImportPanel()
      {
      importmidiPanel->setPreferredVisible(true);
      QString fileName = cs ? cs->importedFilePath() : "";
      if (ImportMidiPanel::isMidiFile(fileName))
            importmidiPanel->setVisible(true);
      importmidiShowPanel->hide();
      }

//---------------------------------------------------------
//   dragEnterEvent
//---------------------------------------------------------

void MuseScore::dragEnterEvent(QDragEnterEvent* event)
      {
      const QMimeData* data = event->mimeData();
      if (data->hasUrls()) {
            QList<QUrl>ul = event->mimeData()->urls();
            foreach(const QUrl& u, ul) {
                  if (MScore::debugMode)
                        qDebug("drag Url: %s scheme <%s>", qPrintable(u.toString()), qPrintable(u.scheme()));
                  if (u.scheme() == "file") {
                        // QFileInfo fi(u.toLocalFile());
                        event->acceptProposedAction();
                        break;
                        }
                  }
            }
      }

//---------------------------------------------------------
//   dropEvent
//---------------------------------------------------------

void MuseScore::dropEvent(QDropEvent* event)
      {
      const QMimeData* data = event->mimeData();
      if (data->hasUrls()) {
            int view = -1;
            foreach(const QUrl& u, event->mimeData()->urls()) {
                  if (u.scheme() == "file") {
                        QString file = u.toLocalFile();
                        MasterScore* score = readScore(file);
                        if (score) {
                              view = appendScore(score);
                              addRecentScore(score);
                              }
                        }
                  }
            if (view != -1) {
                  setCurrentScoreView(view);
                  writeSessionFile(false);
                  }
            event->acceptProposedAction();
            }
      }

//---------------------------------------------------------
//   changeEvent
//---------------------------------------------------------

void MuseScore::changeEvent(QEvent *e)
      {
      QMainWindow::changeEvent(e);
      switch (e->type()) {
            case QEvent::LanguageChange:
                  retranslate();
                  break;
            default:
                  break;
            }
      }


//---------------------------------------------------------
//   showPageSettings
//---------------------------------------------------------

void MuseScore::showPageSettings()
      {
      if (pageSettings == 0)
            pageSettings = new PageSettings();
      pageSettings->setScore(cs->masterScore());
      pageSettings->show();
      pageSettings->raise();
      }

//---------------------------------------------------------
//   startDebugger
//---------------------------------------------------------

void MuseScore::startDebugger()
      {
      if (!cs)
            return;
      if (debugger == 0)
            debugger = new Debugger(this);
      debugger->updateList(cs);
      debugger->show();
      }

//---------------------------------------------------------
//   showElementContext
//---------------------------------------------------------

void MuseScore::showElementContext(Element* el)
      {
      if (el == 0)
            return;
      startDebugger();
      debugger->setElement(el);
      }

//---------------------------------------------------------
//   showPlayPanel
//---------------------------------------------------------

void MuseScore::showPlayPanel(bool visible)
      {
      if (noSeq || !(seq && seq->isRunning()))
            return;
      if (playPanel == 0) {
            if (!visible)
                  return;
            playPanel = new PlayPanel(this);
            connect(playPanel, SIGNAL(gainChange(float)),     synti, SLOT(setGain(float)));
            connect(playPanel, SIGNAL(relTempoChanged(double)),seq, SLOT(setRelTempo(double)));
            connect(playPanel, SIGNAL(posChange(int)),         seq, SLOT(seek(int)));
            connect(playPanel, SIGNAL(closed(bool)),          playId,   SLOT(setChecked(bool)));
            connect(synti,     SIGNAL(gainChanged(float)), playPanel, SLOT(setGain(float)));
            playPanel->setGain(synti->gain());
            playPanel->setScore(cs);
            mscore->stackUnder(playPanel);
            }
      playPanel->setVisible(visible);
      playId->setChecked(visible);
      }

//---------------------------------------------------------
//   cmdAppendMeasures
//---------------------------------------------------------

void MuseScore::cmdAppendMeasures()
      {
      if (cs) {
            if (measuresDialog == 0)
                  measuresDialog = new MeasuresDialog;
            measuresDialog->show();
            }
      }

//---------------------------------------------------------
//   MeasuresDialog
//---------------------------------------------------------

MeasuresDialog::MeasuresDialog(QWidget* parent)
   : QDialog(parent)
      {
      setupUi(this);
      setWindowFlags(this->windowFlags() & ~Qt::WindowContextHelpButtonHint);
      measures->selectAll();
      }

//---------------------------------------------------------
//   accept
//---------------------------------------------------------

void MeasuresDialog::accept()
      {
      int n = measures->value();
      if (mscore->currentScore())
            mscore->currentScoreView()->cmdAppendMeasures(n, ElementType::MEASURE);
      done(1);
      }

//---------------------------------------------------------
//   midiinToggled
//---------------------------------------------------------

void MuseScore::midiinToggled(bool val)
      {
      _midiinEnabled = val;
      }

//---------------------------------------------------------
//   midiinEnabled
//---------------------------------------------------------

bool MuseScore::midiinEnabled() const
      {
      return preferences.enableMidiInput && _midiinEnabled;
      }

//---------------------------------------------------------
//   processMidiRemote
//    return if midi remote command detected
//---------------------------------------------------------

bool MuseScore::processMidiRemote(MidiRemoteType type, int data, int value)
      {
      if (!preferences.useMidiRemote)
            return false;
      if (!value) {
            // This was a "NoteOff" or "CtrlOff" event. Most MidiRemote actions should only
            // be triggered by an "On" event, so we need to check if this is one of those.
            if (!preferences.advanceOnRelease
                    || type != preferences.midiRemote[RMIDI_REALTIME_ADVANCE].type
                    || data != preferences.midiRemote[RMIDI_REALTIME_ADVANCE].data)
                  return false;
            }
      for (int i = 0; i < MIDI_REMOTES; ++i) {
            if (preferences.midiRemote[i].type == type && preferences.midiRemote[i].data == data) {
                  if (cv == 0)
                        return false;
                  QAction* a = 0;
                  switch (i) {
                        case RMIDI_REWIND:      a = getAction("rewind"); break;
                        case RMIDI_TOGGLE_PLAY: a = getAction("play");  break;
                        case RMIDI_PLAY:
                              a = getAction("play");
                              if (a->isChecked())
                                    return true;
                              break;
                        case RMIDI_STOP:
                              a = getAction("play");
                              if (!a->isChecked())
                                    return true;
                              break;
                        case RMIDI_NOTE1:   a = getAction("pad-note-1");  break;
                        case RMIDI_NOTE2:   a = getAction("pad-note-2");  break;
                        case RMIDI_NOTE4:   a = getAction("pad-note-4");  break;
                        case RMIDI_NOTE8:   a = getAction("pad-note-8");  break;
                        case RMIDI_NOTE16:  a = getAction("pad-note-16");  break;
                        case RMIDI_NOTE32:  a = getAction("pad-note-32");  break;
                        case RMIDI_NOTE64:  a = getAction("pad-note-64");  break;
                        case RMIDI_REST:    a = getAction("rest");  break;
                        case RMIDI_DOT:     a = getAction("pad-dot");  break;
                        case RMIDI_DOTDOT:  a = getAction("pad-dotdot");  break;
                        case RMIDI_TIE:     a = getAction("tie");  break;
                        case RMIDI_UNDO:    a = getAction("undo"); break;
                        case RMIDI_NOTE_EDIT_MODE: a = getAction("note-input");  break;
                        case RMIDI_REALTIME_ADVANCE: a = getAction("realtime-advance");  break;
                        }
                  if (a)
                        a->trigger();
                  return true;
                  }
            }
      return false;
      }

//---------------------------------------------------------
//   midiNoteReceived
//---------------------------------------------------------

void MuseScore::midiNoteReceived(int channel, int pitch, int velo)
      {
      static const int THRESHOLD = 3; // iterations required before consecutive drum notes
                                     // are not considered part of a chord
      static int active = 0;
      static int iter = 0;

      if (!midiinEnabled())
            return;

// qDebug("midiNoteReceived %d %d %d", channel, pitch, velo);

      if (_midiRecordId != -1) {
            preferences.midiRemote[_midiRecordId].type = MIDI_REMOTE_TYPE_NOTEON;
            preferences.midiRemote[_midiRecordId].data = pitch;
            _midiRecordId = -1;
            if (preferenceDialog)
                  preferenceDialog->updateRemote();
            return;
            }

      if (processMidiRemote(MIDI_REMOTE_TYPE_NOTEON, pitch, velo)) {
            active = 0;
            return;
            }

      QWidget* w = QApplication::activeModalWidget();
      if (!cv || w) {
            active = 1;
            return;
            }
      if (velo) {
             /*
             * Since some drum modules do not overlap note on / off messages
             * we need to take a bit of a different tactic to allow chords
             * to be entered.
             *
             * Rather than decrement active for drums (midi on ch10),
             * we'll just assume that if read() has been called a couple
             * times in a row without a drum note, that this note is not
             * part of a cord.
             */
            if (channel == 0x09) {
                  if (iter >= THRESHOLD)
                        active = 0;
                  iter = 0;
                  }
// qDebug("    midiNoteReceived %d active %d", pitch, active);
            cv->midiNoteReceived(pitch, active > 0, velo);
            ++active;
            }
      else {
               /*
               * Since a note may be assigned to a midi_remote, don't decrease active below zero
               * on noteoff.
               */

            if ((channel != 0x09) && (active > 0))
                  --active;
            cv->midiNoteReceived(pitch, false, velo);
            }

      if (_pianoTools && _pianoTools->isVisible()) {
            if (velo)
                  _pianoTools->pressPitch(pitch);
            else
                  _pianoTools->releasePitch(pitch);
            }
      }

//---------------------------------------------------------
//   midiCtrlReceived
//---------------------------------------------------------

void MuseScore::midiCtrlReceived(int controller, int value)
      {
      if (!midiinEnabled())
            return;
      if (_midiRecordId != -1) {
            preferences.midiRemote[_midiRecordId].type = MIDI_REMOTE_TYPE_CTRL;
            preferences.midiRemote[_midiRecordId].data = controller;
            _midiRecordId = -1;
            if (preferenceDialog)
                  preferenceDialog->updateRemote();
            return;
            }
      // when value is 0 (usually when a key is released ) nothing happens
      if (processMidiRemote(MIDI_REMOTE_TYPE_CTRL, controller, value))
            return;
      }

//---------------------------------------------------------
//   removeTab
//---------------------------------------------------------

void MuseScore::removeTab()
      {
      int n = scoreList.indexOf(cs->masterScore());
      if (n == -1) {
            qDebug("removeTab: %p not found", cs);
            return;
            }
      removeTab(n);
      }

void MuseScore::removeTab(int i)
      {
      MasterScore* score = scoreList.value(i);

      if (score == 0)
            return;

      QString tmpName = score->tmpName();

      if (checkDirty(score))
            return;
      if (seq && seq->score() == score) {
            seq->stopWait();
            seq->setScoreView(0);
            }

      int idx1      = tab1->currentIndex();
      bool firstTab = tab1->view(idx1) == cv;

      midiPanelOnCloseFile(score->importedFilePath());
      scoreList.removeAt(i);

      tab1->blockSignals(true);
      tab1->removeTab(i);
      tab1->blockSignals(false);

      if (tab2) {
            tab2->blockSignals(true);
            tab2->removeTab(i);
            tab2->blockSignals(false);
            }

      cs = 0;
      cv = 0;
      int n = scoreList.size();
      if (n == 0)
            setCurrentScoreView(nullptr);
      else
            setCurrentScoreView((firstTab ? tab1 : tab2)->view());
      writeSessionFile(false);
      if (!tmpName.isEmpty()) {
            QFile f(tmpName);
            f.remove();
            }
      delete score;
      // Shouldn't be necessary... but fix #21841
      update();
      }

//---------------------------------------------------------
//   loadTranslation
//---------------------------------------------------------

void loadTranslation(QString filename, QString localeName)
      {
      QTranslator* translator = new QTranslator;
      QString userPrefix = dataPath + "/locale/"+ filename +"_";
      QString defaultPrefix = mscoreGlobalShare + "locale/"+ filename +"_";
      QString userlp = userPrefix + localeName;

      QString defaultlp = defaultPrefix + localeName;
      QString lp = defaultlp;

      QFileInfo userFi(userlp + ".qm");
      QFileInfo defaultFi(defaultlp + ".qm");

      if(!defaultFi.exists()) { // try with a shorter locale name
      QString shortLocaleName = localeName.left(localeName.lastIndexOf("_"));
            QString shortDefaultlp = defaultPrefix + shortLocaleName;
            QFileInfo shortDefaultFi(shortDefaultlp + ".qm");
            if(shortDefaultFi.exists()) {
                  userlp = userPrefix + shortLocaleName;
                  userFi = QFileInfo(userlp + ".qm");
                  defaultFi = shortDefaultFi;
                  defaultlp = shortDefaultlp;
                  }
            }

      //      qDebug() << userFi.exists();
      //      qDebug() << userFi.lastModified() << defaultFi.lastModified();
      if (userFi.exists()) {
            if (userFi.lastModified() > defaultFi.lastModified())
                  lp = userlp;
            else
                  QFile::remove(userlp + ".qm");
      }

      if (MScore::debugMode)
            qDebug("load translator <%s>", qPrintable(lp));
      bool success = translator->load(lp);
      if (success) {
            qApp->installTranslator(translator);
            translatorList.append(translator);
            }
      else {
            if (MScore::debugMode)
                  qDebug("load translator <%s> failed", qPrintable(lp));
            delete translator;
            }
      }

//---------------------------------------------------------
//   setLocale
//---------------------------------------------------------

void setMscoreLocale(QString localeName)
      {
      for(QTranslator* t : translatorList) {
            qApp->removeTranslator(t);
            delete t;
            }
      translatorList.clear();

      if (MScore::debugMode)
            qDebug("configured localeName <%s>", qPrintable(localeName));
      if (localeName.toLower() == "system") {
            localeName = QLocale::system().name();
            if (MScore::debugMode)
                  qDebug("real localeName <%s>", qPrintable(localeName));
            if (localeName == "en_AU") {
                  localeName = "en_GB"; // otherwise Australia would fall back to US English
                  if (MScore::debugMode)
                        qDebug("modified localeName <%s>", qPrintable(localeName));
                  }
            }

      // find the most recent translation file
      // try to replicate QTranslator.load algorithm in our particular case
      loadTranslation("mscore", localeName);
      loadTranslation("instruments", localeName);

      QString resourceDir;
#if defined(Q_OS_MAC) || defined(Q_OS_WIN)
      resourceDir = mscoreGlobalShare + "locale/";
#else
      resourceDir = QLibraryInfo::location(QLibraryInfo::TranslationsPath);
#endif
      QTranslator* qtTranslator = new QTranslator;
      if (MScore::debugMode)
            qDebug("load translator <qt_%s> from <%s>",
               qPrintable(localeName), qPrintable(resourceDir));

      if (!qtTranslator->load(QLatin1String("qt_") + localeName, resourceDir) && MScore::debugMode)
            qDebug("load translator <qt_%s> failed", qPrintable(localeName));
      else {
            qApp->installTranslator(qtTranslator);
            translatorList.append(qtTranslator);
            }
      QLocale locale(localeName);
      QLocale::setDefault(locale);
      qApp->setLayoutDirection(locale.textDirection());
      // initShortcuts();
      }

//---------------------------------------------------------
//   loadScores
//    load scores for a new session
//---------------------------------------------------------

static void loadScores(const QStringList& argv)
      {
      int currentScoreView = 0;
      if (argv.isEmpty()) {
            if (startWithNewScore)
                  mscore->newFile();
            else {
                  switch (preferences.sessionStart) {
                        case SessionStart::LAST:
                              {
                              QSettings settings;
                              int n = settings.value("scores", 0).toInt();
                              int c = settings.value("currentScore", 0).toInt();
                              for (int i = 0; i < n; ++i) {
                                    QString s = settings.value(QString("score-%1").arg(i),"").toString();
                                    MasterScore* score = mscore->readScore(s);
                                    if (score) {
                                          int view = mscore->appendScore(score);
                                          if (i == c)
                                                currentScoreView = view;
                                          }
                                    }
                              }
                              break;
                        case SessionStart::EMPTY:
                              break;
                        case SessionStart::NEW:
                              mscore->newFile();
                              break;
                        case SessionStart::SCORE:
                              {
                              MasterScore* score = mscore->readScore(preferences.startScore);
                              if (preferences.startScore.startsWith(":/") && score) {
                                    // TODO score->setPageFormat(*MScore::defaultStyle().pageFormat());
                                    score->doLayout();
                                    score->setCreated(true);
                                    }
                              if (score == 0) {
                                    score = mscore->readScore(":/data/My_First_Score.mscz");
                                    if (score) {
                                          // TODO score->setPageFormat(*MScore::defaultStyle().pageFormat());
                                          score->doLayout();
                                          score->setCreated(true);
                                          }
                                    }
                              if (score)
                                    currentScoreView = mscore->appendScore(score);
                              }
                              break;
                        }
                  }
            }
      else {
            foreach(const QString& name, argv) {
                  if (name.isEmpty())
                        continue;
                  MasterScore* score = mscore->readScore(name);
                  if (score) {
                        mscore->appendScore(score);
                        scoresOnCommandline = true;
                        if(!MScore::noGui) {
                              mscore->addRecentScore(score);
                              mscore->writeSessionFile(false);
                              }
                        }
                  }
            }

      if (mscore->noScore())
            currentScoreView = -1;
      mscore->setCurrentView(0, currentScoreView);
      mscore->setCurrentView(1, currentScoreView);
      }

//---------------------------------------------------------
//   doConvert
//---------------------------------------------------------

static bool doConvert(Score* cs, QString fn)
      {
      bool rv = true;

      LayoutMode layoutMode = cs->layoutMode();
      cs->setLayoutMode(LayoutMode::PAGE);
      if (cs->layoutMode() != layoutMode) {
            cs->setLayoutMode(LayoutMode::PAGE);
            cs->doLayout();
            }

      if (!styleFile.isEmpty()) {
            QFile f(styleFile);
            if (f.open(QIODevice::ReadOnly))
                  cs->style().load(&f);
            }
      if (fn.endsWith(".mscx")) {
            QFileInfo fi(fn);
            if (!cs->saveFile(fi))
                  return false;
            return true;
            }
      else if (fn.endsWith(".mscz")) {
            QFileInfo fi(fn);
            if (!cs->saveCompressedFile(fi, false))
                  return false;
            return true;
            }
      else if (fn.endsWith(".xml")) {
            rv = saveXml(cs, fn);
            }
      else if (fn.endsWith(".mxl")) {
            rv = saveMxl(cs, fn);
            }
      else if (fn.endsWith(".mid"))
            return mscore->saveMidi(cs, fn);
      else if (fn.endsWith(".pdf")) {
            if (!exportScoreParts) {
                  rv = mscore->savePdf(cs, fn);
                  }
            else {
                  if (cs->excerpts().size() == 0) {
                        auto excerpts = Excerpt::createAllExcerpt(cs->masterScore());

                        for (Excerpt* e : excerpts) {
                              Score* nscore = new Score(e->oscore());
                              e->setPartScore(nscore);
                              nscore->style().set(StyleIdx::createMultiMeasureRests, true);
                              Excerpt::createExcerpt(e);
                              cs->startCmd();
                              cs->undo(new AddExcerpt(e));
                              cs->endCmd();
                              }
                        }
                  QList<Score*> scores;
                  scores.append(cs);
                  for (Excerpt* e : cs->excerpts())
                        scores.append(e->partScore());
                  return mscore->savePdf(scores, fn);
                  }
            }
      else if (fn.endsWith(".png")) {
            if (!exportScoreParts)
                  return mscore->savePng(cs, fn);
            else {
                  if (cs->excerpts().size() == 0) {
                        auto excerpts = Excerpt::createAllExcerpt(cs->masterScore());

                        for (Excerpt* e: excerpts) {
                              Score* nscore = new Score(e->oscore());
                              e->setPartScore(nscore);
                              nscore->setExcerpt(e);
                              // nscore->setName(e->title()); // needed before AddExcerpt
                              nscore->style().set(StyleIdx::createMultiMeasureRests, true);
                              Excerpt::createExcerpt(e);
                              cs->startCmd();
                              cs->undo(new AddExcerpt(e));
                              cs->endCmd();
                              }
                        }
                  if (!mscore->savePng(cs, fn))
                        return false;
                  int idx = 0;
                  int padding = QString("%1").arg(cs->excerpts().size()).size();
                  for (Excerpt* e: cs->excerpts()) {
                        QString suffix = QString("__excerpt__%1.png").arg(idx, padding, 10, QLatin1Char('0'));
                        QString excerptFn = fn.left(fn.size() - 4) + suffix;
                        if (!mscore->savePng(e->partScore(), excerptFn))
                              return false;
                        idx++;
                        }
                  return true;
                  }
            }
      else if (fn.endsWith(".svg")) {
            rv = mscore->saveSvg(cs, fn);
            }
#ifdef HAS_AUDIOFILE
            else if (fn.endsWith(".wav") || fn.endsWith(".ogg") || fn.endsWith(".flac"))
                  return mscore->saveAudio(cs, fn);
#endif
#ifdef USE_LAME
            else if (fn.endsWith(".mp3"))
                  return mscore->saveMp3(cs, fn);
#endif
      else if (fn.endsWith(".spos")) {
            rv = savePositions(cs, fn, true);
            }
      else if (fn.endsWith(".mpos")) {
            rv = savePositions(cs, fn, false);
            }
      else if (fn.endsWith(".mlog"))
            return cs->sanityCheck(fn);
      else {
            qDebug("dont know how to convert to %s", qPrintable(outFileName));
            return false;
            }
      if (layoutMode != cs->layoutMode()) {
            cs->setLayoutMode(layoutMode);
            cs->doLayout();
            }
      return rv;
      }

//---------------------------------------------------------
//   convert
//---------------------------------------------------------

static bool convert(const QString& inFile, const QString& outFile)
      {
      if (inFile.isEmpty() || outFile.isEmpty()) {
            fprintf(stderr, "cannot convert <%s> to <%s>\n", qPrintable(inFile), qPrintable(outFile));
            return false;
            }
      fprintf(stderr, "convert <%s> to <%s>\n", qPrintable(inFile), qPrintable(outFile));
      MasterScore* score = mscore->readScore(inFile);
      if (!score)
            return false;
      if (!doConvert(score, outFile)) {
            delete score;
            return false;
            }
      delete score;
      return true;
      }

//---------------------------------------------------------
//   doProcessJob
//---------------------------------------------------------

static bool doProcessJob(QString jsonFile)
      {
      QFile f(jsonFile);
      if (!f.open(QIODevice::ReadOnly)) {
            fprintf(stderr, "cannon open json file <%s>\n", qPrintable(jsonFile));
            return false;
            }
      QJsonParseError pe;
      QJsonDocument doc = QJsonDocument::fromJson(f.readAll(), &pe);
      if (pe.error != QJsonParseError::NoError) {
            fprintf(stderr, "error reading json file <%s> at %d: %s\n",
               qPrintable(jsonFile), pe.offset, qPrintable(pe.errorString()));
            return false;
            }
      if (!doc.isArray()) {
            fprintf(stderr, "json file <%s> is not an array\n", qPrintable(jsonFile));
            return false;
            }
      QJsonArray a = doc.array();
      for (const auto i : a) {
            QString inFile;
            QString outFile;
            if (!i.isObject()) {
                  fprintf(stderr, "array value is not an object\n");
                  return false;
                  }
            QJsonObject obj = i.toObject();
            for (const auto& key : obj.keys()) {
                  QString val = obj.value(key).toString();
                  if (key == "in")
                        inFile = val;
                  else if (key == "out")
                        outFile = val;
                  else {
                        fprintf(stderr, "unknown key <%s>\n", qPrintable(key));
                        return false;
                        }
                  }
            if (!convert(inFile, outFile))
                  return false;
            }
      return true;
      }

//---------------------------------------------------------
//   processNonGui
//---------------------------------------------------------

static bool processNonGui(const QStringList& argv)
      {
      if (pluginMode) {
            loadScores(argv);
            QString pn(pluginName);
            bool res = false;
            if (mscore->loadPlugin(pn)){
                  Score* cs = mscore->currentScore();
                  if (!styleFile.isEmpty()) {
                        QFile f(styleFile);
                        if (f.open(QIODevice::ReadOnly))
                              cs->style().load(&f);
                        }
                  LayoutMode layoutMode = cs->layoutMode();
                  if (layoutMode != LayoutMode::PAGE) {
                        cs->setLayoutMode(LayoutMode::PAGE);
                        cs->doLayout();
                        }
                  mscore->pluginTriggered(0);
                  if (layoutMode != cs->layoutMode()) {
                        cs->setLayoutMode(layoutMode);
                        cs->doLayout();
                        }
                  res = true;
                  }
            if (!converterMode)
                  return res;
            }
      bool rv = true;
      if (converterMode) {
            if (processJob)
                  return doProcessJob(jsonFileName);
            else
                  return convert(argv[0], outFileName);
            }
      return rv;
      }

//---------------------------------------------------------
//   Message handler
//---------------------------------------------------------

#if defined(QT_DEBUG) && defined(Q_OS_WIN)
static void mscoreMessageHandler(QtMsgType type, const QMessageLogContext &context,const QString &msg)
     {
     QTextStream cerr(stderr);
     QByteArray localMsg = msg.toLocal8Bit();

     switch (type) {
     case QtInfoMsg:
         cerr << "Info: "     << localMsg.constData() << " ("  << context.file << ":" << context.line << ", " << context.function << ")" << endl;
         break;
     case QtDebugMsg:
         cerr << "Debug: "    << localMsg.constData() << " ("  << context.file << ":" << context.line << ", " << context.function << ")" << endl;
         break;
     case QtWarningMsg:
         cerr << "Warning: "  << localMsg.constData() << " ("  << context.file << ":" << context.line << ", " << context.function << ")" << endl;
         break;
     case QtCriticalMsg: // same as QtSystemMsg
         cerr << "Critical: " << localMsg.constData() << " ("  << context.file << ":" << context.line << ", " << context.function << ")" << endl;
         break;
     case QtFatalMsg: // set your breakpoint here, if you want to catch the abort
         cerr << "Fatal: "    << localMsg.constData() << " ("  << context.file << ":" << context.line << ", " << context.function << ")" << endl;
         abort();
         }
     }
#endif

//---------------------------------------------------------
//   synthesizerFactory
//    create and initialize the master synthesizer
//---------------------------------------------------------

MasterSynthesizer* synthesizerFactory()
      {
      MasterSynthesizer* ms = new MasterSynthesizer();

      FluidS::Fluid* fluid = new FluidS::Fluid();
      ms->registerSynthesizer(fluid);

#ifdef AEOLUS
      ms->registerSynthesizer(::createAeolus());
#endif
#ifdef ZERBERUS
      ms->registerSynthesizer(createZerberus());
#endif
      ms->registerEffect(0, new NoEffect);
      ms->registerEffect(0, new ZitaReverb);
      ms->registerEffect(0, new Compressor);
      // ms->registerEffect(0, new Freeverb);
      ms->registerEffect(1, new NoEffect);
      ms->registerEffect(1, new ZitaReverb);
      ms->registerEffect(1, new Compressor);
      // ms->registerEffect(1, new Freeverb);
      ms->setEffect(0, 1);
      ms->setEffect(1, 0);
      return ms;
      }

//---------------------------------------------------------
//   unstable
//---------------------------------------------------------

bool MuseScore::unstable()
      {
#ifdef MSCORE_UNSTABLE
      return true;
#else
      return false;
#endif
      }

//---------------------------------------------------------
//   MuseScoreApplication::event (mac only)
//---------------------------------------------------------

bool MuseScoreApplication::event(QEvent* event)
      {
      switch(event->type()) {
            case QEvent::FileOpen:
                  // store names of files requested to be loaded by OS X to be handled later
                  // this event is generated when a file is dragged onto the MuseScore icon
                  // in the dock and MuseScore is not running yet
                  paths.append(static_cast<QFileOpenEvent *>(event)->file());
                  return true;
            default:
                  return QtSingleApplication::event(event);
            }
      }

//---------------------------------------------------------
//   eventFilter
//---------------------------------------------------------

bool MuseScore::eventFilter(QObject *obj, QEvent *event)
      {
      switch(event->type()) {
#ifdef Q_OS_MAC
            case QEvent::FileOpen:
                  // open files requested to be loaded by OS X
                  // this event is generated when a file is dragged onto the MuseScore icon
                  // in the dock when MuseScore is already running
                  scoresOnCommandline = true;
                  handleMessage(static_cast<QFileOpenEvent *>(event)->file());
                  return true;
#endif
            case QEvent::MouseMove: {
                  QMouseEvent* me = static_cast<QMouseEvent*>(event);
                  globalX = me->globalX();
                  globalY = me->globalY();
                  return QMainWindow::eventFilter(obj, event);
                  }
            case QEvent::StatusTip:
                  return true; // prevent updates to the status bar
            case QEvent::KeyPress:
                  {
                  QKeyEvent* e = static_cast<QKeyEvent*>(event);
                  if(obj->isWidgetType() && e->key() == Qt::Key_Escape && e->modifiers() == Qt::NoModifier) {
                        if (isActiveWindow()) {
                              obj->event(e);
                              if(currentScoreView())
                                    currentScoreView()->setFocus();
                              else
                                    mscore->setFocus();
                              return true;
                              }

                        QWidget* w = static_cast<QWidget*>(obj);
                        if(getPaletteBox()->isAncestorOf(w) ||
                           inspector()->isAncestorOf(w) ||
                           (selectionWindow && selectionWindow->isAncestorOf(w))) {
                              activateWindow();
                              if(currentScoreView())
                                    currentScoreView()->setFocus();
                              else
                                    mscore->setFocus();
                              return true;
                              }
                        }
                  break;
                  }
            default:
                  return QMainWindow::eventFilter(obj, event);
            }
      return QMainWindow::eventFilter(obj, event);
      }

//---------------------------------------------------------
//   hasToCheckForUpdate
//---------------------------------------------------------

bool MuseScore::hasToCheckForUpdate()
      {
      if (ucheck)
            return ucheck->hasToCheck();
      else
          return false;
      }

//---------------------------------------------------------
//   checkForUpdate
//---------------------------------------------------------

void MuseScore::checkForUpdate()
      {
      if (ucheck)
            ucheck->check(version(), sender() != 0);
      }

//---------------------------------------------------------
//   readLanguages
//---------------------------------------------------------

bool MuseScore::readLanguages(const QString& path)
      {
      //: The default language of the operating system. NOT a music system.
      _languages.append(LanguageItem("system", tr("System")));
      QFile qf(path);
      if (qf.exists()){
          QDomDocument doc;
          int line, column;
          QString err;
          if (!doc.setContent(&qf, false, &err, &line, &column)) {
                QString error;
                error.sprintf(qPrintable(tr("Error reading language file %s at line %d column %d: %s\n")),
                   qPrintable(qf.fileName()), line, column, qPrintable(err));
                QMessageBox::warning(0,
                   QWidget::tr("MuseScore: Load Languages Failed:"),
                   error,
                   QString::null, QWidget::tr("Quit"), QString::null, 0, 1);
                return false;
                }

          for (QDomElement e = doc.documentElement(); !e.isNull(); e = e.nextSiblingElement()) {
                if(e.tagName() == "languages") {
                      for (e = e.firstChildElement(); !e.isNull(); e = e.nextSiblingElement()) {
                        if (e.tagName() == "language") {
                              QString code = e.attribute(QString("code"));
                              QString name = e.attribute(QString("name"));
                              QString handbook = e.attribute(QString("handbook"));
                              _languages.append(LanguageItem(code, name, handbook));
                              }
                          }
                      }
                }
            return true;
            }
      return false;
      }

//---------------------------------------------------------
//   clipboardChanged
//---------------------------------------------------------

void MuseScore::clipboardChanged()
      {
#if 0
      const QMimeData* ms = QApplication::clipboard()->mimeData();
      QStringList formats = ms->formats();

      bool flag = ms->hasFormat(mimeSymbolFormat)
            ||    ms->hasFormat(mimeStaffListFormat)
            ||    ms->hasFormat(mimeSymbolListFormat)
            ||    ms->hasText();
      // TODO: depends on selection state
#endif

      bool flag = true;
      getAction("paste")->setEnabled(flag);
      getAction("swap")->setEnabled(flag);
      }

//---------------------------------------------------------
//   showModeText
//---------------------------------------------------------

void MuseScore::showModeText(const QString& s)
      {
      _modeText->setText(s);
      _modeText->show();
      }

//---------------------------------------------------------
//   changeState
//---------------------------------------------------------

void MuseScore::changeState(ScoreState val)
      {
// printf("MuseScore::changeState: %s\n", stateName(val));
      if (MScore::debugMode)
            qDebug("MuseScore::changeState: %s", stateName(val));

//      if (_sstate == val)
//            return;

      // disallow change to edit modes if currently in play mode
      if (_sstate == STATE_PLAY && (val == STATE_EDIT || val & STATE_ALLTEXTUAL_EDIT || val & STATE_NOTE_ENTRY))
            return;

      static const char* stdNames[] = {
            "note-longa", "note-breve", "pad-note-1", "pad-note-2", "pad-note-4",
      "pad-note-8", "pad-note-16", "pad-note-32", "pad-note-64", "pad-note-128", "pad-rest", "rest"};
      static const char* tabNames[] = {
            "note-longa-TAB", "note-breve-TAB", "pad-note-1-TAB", "pad-note-2-TAB", "pad-note-4-TAB",
      "pad-note-8-TAB", "pad-note-16-TAB", "pad-note-32-TAB", "pad-note-64-TAB", "pad-note-128-TAB", "pad-rest-TAB", "rest-TAB"};
      bool intoTAB = (_sstate != STATE_NOTE_ENTRY_STAFF_TAB) && (val == STATE_NOTE_ENTRY_STAFF_TAB);
      bool fromTAB = (_sstate == STATE_NOTE_ENTRY_STAFF_TAB) && (val != STATE_NOTE_ENTRY_STAFF_TAB);
      // if activating TAB note entry, swap "pad-note-...-TAB" shorctuts into "pad-note-..." actions
      if (intoTAB) {
            for (unsigned i = 0; i < sizeof(stdNames)/sizeof(char*); ++i) {
                  QAction* act = getAction(stdNames[i]);
                  const Shortcut* srt = Shortcut::getShortcut(tabNames[i]);
                  act->setShortcuts(srt->keys());
                  }
            }
      // if de-ativating TAB note entry, restore shortcuts for "pad-note-..." actions
      else if (fromTAB) {
            for (unsigned i = 0; i < sizeof(stdNames)/sizeof(char*); ++i) {
                  QAction* act = getAction(stdNames[i]);
                  const Shortcut* srt = Shortcut::getShortcut(stdNames[i]);
                  act->setShortcuts(srt->keys());
                  }
            }

      bool enable = (val != STATE_DISABLED) && (val != STATE_LOCK);

      foreach (const Shortcut* s, Shortcut::shortcuts()) {
            QAction* a = s->action();
            if (!a)
                  continue;
            if (enable && (s->key() == "undo"))
                  a->setEnabled((s->state() & val) && (cs ? cs->undoStack()->canUndo() : false));
            else if (enable && (s->key() == "redo"))
                  a->setEnabled((s->state() & val) && (cs ? cs->undoStack()->canRedo() : false));
            else if (enable && (s->key() == "cut"))
                  a->setEnabled(cs && cs->selection().state() != SelState::NONE);
            else if (enable && (s->key() == "copy"))
                  a->setEnabled(cs && (cs->selection().state() != SelState::NONE || val == STATE_FOTO));
            else if (enable && (s->key() == "select-similar-range"))
                  a->setEnabled(cs && cs->selection().state() == SelState::RANGE);
            else if (enable && (s->key() == "synth-control")) {
                  Driver* driver = seq ? seq->driver() : 0;
                  // a->setEnabled(driver && driver->getSynth());
                  if (MScore::debugMode)
                        qDebug("disable synth control");
                  a->setEnabled(driver);
                  }
            else if (s->key() == "pad-dot" || s->key() == "pad-dot-dot")
                        a->setEnabled(!(val & (STATE_ALLTEXTUAL_EDIT | STATE_EDIT)));
            else {
                  a->setEnabled(s->state() & val);
                  }
            }

      if (getAction("file-part-export")->isEnabled())
            getAction("file-part-export")->setEnabled(cs && cs->masterScore()->excerpts().size() > 0);
      if (getAction("join-measures")->isEnabled())
            getAction("join-measures")->setEnabled(cs && cs->masterScore()->excerpts().size() == 0);
      if (getAction("split-measure")->isEnabled())
            getAction("split-measure")->setEnabled(cs && cs->masterScore()->excerpts().size() == 0);

      //getAction("split-measure")->setEnabled(cs->masterScore()->excerpts().size() == 0);

      // disabling top level menu entries does not
      // work for MAC

      QList<QObject*> ol = menuBar()->children();
      foreach(QObject* o, ol) {
            QMenu* menu = qobject_cast<QMenu*>(o);
            if (!menu)
                  continue;
            QString s(menu->objectName());
            if (s == "File" || s == "Help" || s == "Edit" || s == "Plugins" || s == "View")
                  continue;
            menu->setEnabled(enable);
            }

      menuWorkspaces->setEnabled(enable);

      transportTools->setEnabled(enable && !noSeq && seq && seq->isRunning());
      cpitchTools->setEnabled(enable);
      mag->setEnabled(enable);
      entryTools->setEnabled(enable);

      if (_sstate == STATE_FOTO)
            updateInspector();
      if (_sstate == STATE_NOTE_ENTRY_STAFF_DRUM)
            showDrumTools(0, 0);

      switch(val) {
            case STATE_DISABLED:
                  showModeText(tr("No score"));
                  if (debugger)
                        debugger->hide();
                  showPianoKeyboard(false);
                  break;
            case STATE_NORMAL:
                  _modeText->hide();
                  break;
            case STATE_NOTE_ENTRY:
                  if (cv && !cv->noteEntryMode())
                        cv->postCmd("note-input");
            case STATE_NOTE_ENTRY_STAFF_PITCHED:
                  if (getAction("note-input-repitch")->isChecked()) {
                        showModeText(tr("Repitch input mode"));
                        cs->setNoteEntryMethod(NoteEntryMethod::REPITCH);
                        val = STATE_NOTE_ENTRY_METHOD_REPITCH;
                        }
                  else if (getAction("note-input-rhythm")->isChecked()) {
                        showModeText(tr("Rhythm input mode"));
                        cs->setNoteEntryMethod(NoteEntryMethod::RHYTHM);
                        val = STATE_NOTE_ENTRY_METHOD_RHYTHM;
                        }
                  else if (getAction("note-input-realtime-auto")->isChecked()) {
                        showModeText(tr("Realtime (automatic) note input mode"));
                        cs->setNoteEntryMethod(NoteEntryMethod::REALTIME_AUTO);
                        val = STATE_NOTE_ENTRY_METHOD_REALTIME_AUTO;
                        }
                  else if (getAction("note-input-realtime-manual")->isChecked()) {
                        showModeText(tr("Realtime (manual) note input mode"));
                        cs->setNoteEntryMethod(NoteEntryMethod::REALTIME_MANUAL);
                        val = STATE_NOTE_ENTRY_METHOD_REALTIME_MANUAL;
                        }
                  else {
                        showModeText(tr("Steptime note input mode"));
                        cs->setNoteEntryMethod(NoteEntryMethod::STEPTIME);
                        val = STATE_NOTE_ENTRY_METHOD_STEPTIME;
                        }
                  break;
            case STATE_NOTE_ENTRY_STAFF_DRUM:
                  {
                  showModeText(tr("Drum input mode"));
                  InputState& is = cs->inputState();
                  showDrumTools(is.drumset(), cs->staff(is.track() / VOICES));
                  if (_drumTools)
                        is.setDrumNote(_drumTools->selectedDrumNote());
                  }
                  break;
            case STATE_NOTE_ENTRY_STAFF_TAB:
                  showModeText(tr("TAB input mode"));
                  break;
            case STATE_EDIT:
                  showModeText(tr("Edit mode"));
                  break;
            case STATE_TEXT_EDIT:
                  showModeText(tr("Text edit mode"));
                  break;
            case STATE_LYRICS_EDIT:
                  showModeText(tr("Lyrics edit mode"));
                  break;
            case STATE_HARMONY_FIGBASS_EDIT:
                  showModeText(tr("Chord symbol/figured bass edit mode"));
                  break;
            case STATE_PLAY:
                  showModeText(tr("Play"));
                  break;
            case STATE_FOTO:
                  showModeText(tr("Image capture mode"));
                  updateInspector();
                  break;
            case STATE_LOCK:
                  showModeText(tr("Score locked"));
                  break;
            default:
                  qFatal("MuseScore::changeState: illegal state %d", val);
                  break;
            }
      if (paletteBox)
            paletteBox->setDisabled(val == STATE_PLAY || val == STATE_DISABLED);
      if (selectionWindow)
            selectionWindow->setDisabled(val == STATE_PLAY || val == STATE_DISABLED);
      QAction* a = getAction("note-input");
      bool noteEntry = val & STATE_NOTE_ENTRY;
      a->setChecked(noteEntry);
      _sstate = val;

      Element* e = 0;
      if (_sstate & STATE_ALLTEXTUAL_EDIT || _sstate == STATE_EDIT) {
            if (cv)
                  e = cv->getEditObject();
            }
      if (!e) {
            textTools()->hide();
            if (textTools()->kbAction()->isChecked())
                  textTools()->kbAction()->setChecked(false);
            }
      else {
            if (e->isText()) {
                  textTools()->setText(static_cast<Text*>(e));
                  textTools()->updateTools();
                  if (e->type() != ElementType::FIGURED_BASS && e->type() != ElementType::HARMONY)   // do not show text tools for f.b.
                        textTools()->show();
                  }
            if (_inspector)
                  _inspector->setElement(e);
            }
      }

//---------------------------------------------------------
//   saveDialogState
//---------------------------------------------------------

void MuseScore::saveDialogState(const char* name, QFileDialog* d)
      {
      if (d) {
            settings.beginGroup(name);
            settings.setValue("geometry", d->saveGeometry());
            settings.setValue("state", d->saveState());
            settings.endGroup();
            }
      }

//---------------------------------------------------------
//   restoreDialogState
//---------------------------------------------------------

void MuseScore::restoreDialogState(const char* name, QFileDialog* d)
      {
      settings.beginGroup(name);
      d->restoreGeometry(settings.value("geometry").toByteArray());
      d->restoreState(settings.value("state").toByteArray());
      settings.endGroup();
      }

//---------------------------------------------------------
//   writeSettings
//---------------------------------------------------------

void MuseScore::writeSettings()
      {
      // save score list
      for (int i = 0; i < RECENT_LIST_SIZE; ++i)
            settings.setValue(QString("recent-%1").arg(i), _recentScores.value(i));

      settings.setValue("scores", scoreList.size());
      if (cs) {
            int curScore = scoreList.indexOf(cs->masterScore());
            if (curScore == -1)  // cs removed if new created and not modified
                  curScore = 0;
            settings.setValue("currentScore", curScore);

            for (int idx = 0; idx < scoreList.size(); ++idx)
                  settings.setValue(QString("score-%1").arg(idx), scoreList[idx]->masterScore()->fileInfo()->absoluteFilePath());
            }

      settings.setValue("lastSaveCopyDirectory", lastSaveCopyDirectory);
      settings.setValue("lastSaveCopyFormat", lastSaveCopyFormat);
      settings.setValue("lastSaveDirectory", lastSaveDirectory);

      MuseScore::saveGeometry(this);

      settings.beginGroup("MainWindow");
      settings.setValue("showPanel", paletteBox && paletteBox->isVisible());
      settings.setValue("showInspector", _inspector && _inspector->isVisible());
      settings.setValue("showPianoKeyboard", _pianoTools && _pianoTools->isVisible());
      settings.setValue("showSelectionWindow", selectionWindow && selectionWindow->isVisible());
      settings.setValue("state", saveState());
      settings.setValue("splitScreen", _splitScreen);
      settings.setValue("debuggerSplitter", mainWindow->saveState());
      settings.setValue("split", _horizontalSplit);
      settings.setValue("splitter", splitter->saveState());
      settings.endGroup();

      Workspace::currentWorkspace->save();
      if (keyEditor && keyEditor->dirty())
            keyEditor->save();
      if (chordStyleEditor)
            chordStyleEditor->save();

      saveDialogState("loadScoreDialog",      loadScoreDialog);
      saveDialogState("saveScoreDialog",      saveScoreDialog);
      saveDialogState("loadStyleDialog",      loadStyleDialog);
      saveDialogState("saveStyleDialog",      saveStyleDialog);
      saveDialogState("saveImageDialog",      saveImageDialog);
      saveDialogState("loadChordStyleDialog", loadChordStyleDialog);
      saveDialogState("saveChordStyleDialog", saveChordStyleDialog);
      saveDialogState("loadScanDialog",       loadScanDialog);
      saveDialogState("loadAudioDialog",      loadAudioDialog);
      saveDialogState("loadDrumsetDialog",    loadDrumsetDialog);
      saveDialogState("saveDrumsetDialog",    saveDrumsetDialog);
      saveDialogState("loadPaletteDialog",    loadPaletteDialog);
      saveDialogState("savePaletteDialog",    savePaletteDialog);

      if (debugger)
            debugger->writeSettings();

#ifdef SCRIPT_INTERFACE
      if (_pluginCreator)
            _pluginCreator->writeSettings();
#endif
      if (synthControl)
            synthControl->writeSettings();
      settings.setValue("synthControlVisible", synthControl && synthControl->isVisible());
      if (mixer)
            mixer->writeSettings();
      settings.setValue("mixerVisible", mixer && mixer->isVisible());
      if (seq) {
            seq->stopWait();
            seq->exit();
            }
      if (instrList)
            instrList->writeSettings();
      if (pianorollEditor)
            pianorollEditor->writeSettings();
      if (drumrollEditor)
            drumrollEditor->writeSettings();
      if (startcenter)
            startcenter->writeSettings();
      }

//---------------------------------------------------------
//   readSettings
//---------------------------------------------------------

void MuseScore::readSettings()
      {
      resize(QSize(1024, 768)); //ensure default size if no geometry in settings
      if (useFactorySettings) {
            QList<int> sizes;
            sizes << 500 << 100;
            mainWindow->setSizes(sizes);
            mscore->showPalette(true);
            mscore->showInspector(true);
            return;
            }

      MuseScore::restoreGeometry(this);

      settings.beginGroup("MainWindow");
      mainWindow->restoreState(settings.value("debuggerSplitter").toByteArray());
      mainWindow->setOpaqueResize(false);
      scorePageLayoutChanged();

      //for some reason when MuseScore starts maximized the screen-reader
      //doesn't respond to QAccessibleEvents --> so force normal mode
      if (isMaximized() && QAccessible::isActive()) {
            showNormal();
            }
      mscore->showPalette(settings.value("showPanel", "1").toBool());
      mscore->showInspector(settings.value("showInspector", "1").toBool());
      mscore->showPianoKeyboard(settings.value("showPianoKeyboard", "0").toBool());
      mscore->showSelectionWindow(settings.value("showSelectionWindow", "0").toBool());

      restoreState(settings.value("state").toByteArray());
      //if we were in full screen mode, go to maximized mode
      if (isFullScreen()) {
            showMaximized();
            }

      _horizontalSplit = settings.value("split", true).toBool();
      bool splitScreen = settings.value("splitScreen", false).toBool();
      if (splitScreen) {
            splitWindow(_horizontalSplit);
            QAction* a = getAction(_horizontalSplit ? "split-h" : "split-v");
            a->setChecked(true);
            }
      splitter->restoreState(settings.value("splitter").toByteArray());
      settings.endGroup();

      QAction* a = getAction("toggle-fileoperations");
      a->setChecked(!fileTools->isHidden());

      a = getAction("toggle-transport");
      a->setChecked(!transportTools->isHidden());

      a = getAction("toggle-concertpitch");
      a->setChecked(!cpitchTools->isHidden());

      a = getAction("toggle-imagecapture");
      a->setChecked(!fotoTools->isHidden());

      a = getAction("toggle-noteinput");
      a->setChecked(!entryTools->isHidden());
      }

//---------------------------------------------------------
//   play
//    play note for preferences.defaultPlayDuration
//---------------------------------------------------------

void MuseScore::play(Element* e) const
      {
      if (noSeq || !(seq && seq->isRunning()) || !preferences.playNotes)
            return;

      if (e->isNote()) {
            Note* note = toNote(e);
            play(e, note->ppitch());
            }
      else if (e->isChord()) {
            seq->stopNotes();
            Chord* c   = toChord(e);
            Part* part = c->staff()->part();
            int tick   = c->segment() ? c->segment()->tick() : 0;
            seq->seek(tick);
            Instrument* instr = part->instrument(tick);
            for (Note* n : c->notes()) {
                  const Channel* channel = instr->channel(n->subchannel());
                  seq->startNote(channel->channel, n->ppitch(), 80, n->tuning());
                  }
            seq->startNoteTimer(MScore::defaultPlayDuration);
            }
      }

void MuseScore::play(Element* e, int pitch) const
      {
      if (noSeq || !(seq && seq->isRunning()))
            return;
      if (preferences.playNotes && e->isNote()) {
            Note* note = static_cast<Note*>(e);
            int tick = note->chord()->tick();
            if (tick < 0)
                  tick = 0;
            Instrument* instr = note->part()->instrument(tick);
            const Channel* channel = instr->channel(note->subchannel());
            seq->startNote(channel->channel, pitch, 80, MScore::defaultPlayDuration, note->tuning());
            }
      }

//---------------------------------------------------------
//   reportBug
//---------------------------------------------------------

void MuseScore::reportBug()
      {
      QString url = QString("https://musescore.org/redirect/post/bug-report?sha=%1&locale=%2").arg(revision()).arg(getLocaleISOCode());
      QDesktopServices::openUrl(QUrl(url.trimmed()));
      }

//---------------------------------------------------------
//   askForHelp
//---------------------------------------------------------

void MuseScore::askForHelp()
      {
      QString url = QString("https://musescore.org/redirect/post/question?locale=%1").arg(getLocaleISOCode());
      QDesktopServices::openUrl(QUrl(url.trimmed()));
      }

//---------------------------------------------------------
//   about
//---------------------------------------------------------

void MuseScore::about()
      {
      AboutBoxDialog ab;
      ab.show();
      ab.exec();
      }

//---------------------------------------------------------
//   AboutBoxDialog
//---------------------------------------------------------

AboutBoxDialog::AboutBoxDialog()
      {
      setupUi(this);
      museLogo->setPixmap(QPixmap(preferences.isThemeDark() ?
            ":/data/musescore-logo-transbg-m.png" : ":/data/musescore_logo_full.png"));

      if (MuseScore::unstable())
            versionLabel->setText(tr("Unstable Prerelease for Version: %1").arg(VERSION));
      else
            versionLabel->setText(tr("Version: %1").arg(VERSION));
      revisionLabel->setText(tr("Revision: %1").arg(revision));
      setWindowFlags(this->windowFlags() & ~Qt::WindowContextHelpButtonHint);

      copyrightLabel->setText(QString("<span style=\"font-size:10pt;\">%1</span>")
                              .arg(tr(   "Visit %1www.musescore.org%2 for new versions and more information.\n"
                                         "Support MuseScore with your %3donation%4.\n\n"
                                         "Copyright &copy; 1999-2017 Werner Schweer and Others.\n"
                                         "Published under the GNU General Public License.")
                                   .arg("<a href=\"http://www.musescore.org/\">")
                                   .arg("</a>")
                                   .arg("<a href=\"http://www.musescore.org/donate\">")
                                   .arg("</a>")
                                   .replace("\n","<br/>")));
      connect(copyRevisionButton, SIGNAL(clicked()), this, SLOT(copyRevisionToClipboard()));
      }

//---------------------------------------------------------
//   copyRevisionToClipboard
//---------------------------------------------------------

void AboutBoxDialog::copyRevisionToClipboard()
      {
      QClipboard* cb = QApplication::clipboard();
      cb->setText(QString("github-musescore-musescore-") + revision);
      }

//---------------------------------------------------------
//   AboutBoxDialog
//---------------------------------------------------------

AboutMusicXMLBoxDialog::AboutMusicXMLBoxDialog()
      {
      setupUi(this);
      setWindowFlags(this->windowFlags() & ~Qt::WindowContextHelpButtonHint);
      label->setText(QString("<span style=\"font-size:10pt;\">%1<br/></span>")
                     .arg(tr(   "MusicXML is an open file format for exchanging digital sheet music,\n"
                                "supported by many applications. MusicXML is copyright &copy; MakeMusic, Inc.\n"
                                "All rights reserved. For more information, see: %1MusicXML.com%2.")
                          .arg("<a href=\"http://www.musicxml.com\">")
                          .arg("</a>")
                          .replace("\n","<br/>")));
      }

//---------------------------------------------------------
//   dirtyChanged
//---------------------------------------------------------

void MuseScore::dirtyChanged(Score* s)
      {
      MasterScore* score = s->masterScore();

      int idx = scoreList.indexOf(score);
      if (idx == -1) {
            qDebug("score not in list");
            return;
            }
      QString label(score->fileInfo()->completeBaseName());
      if (score->dirty())
            label += "*";
      tab1->setTabText(idx, label);
      if (tab2)
            tab2->setTabText(idx, label);
      }

//---------------------------------------------------------
//   magChanged
//---------------------------------------------------------

void MuseScore::magChanged(MagIdx idx)
      {
      if (cv)
            cv->setMag(idx, mag->getLMag(cv));
      }

//---------------------------------------------------------
//   incMag
//---------------------------------------------------------

void MuseScore::incMag()
      {
      if (cv) {
            qreal _mag = cv->lmag() * SCALE_STEP;
            if (_mag > SCALE_MAX)
                  _mag = SCALE_MAX;
            cv->setMag(MagIdx::MAG_FREE, _mag);
            setMag(_mag);
            }
      }

//---------------------------------------------------------
//   decMag
//---------------------------------------------------------

void MuseScore::decMag()
      {
      if (cv) {
            qreal _mag = cv->lmag() / SCALE_STEP;
            if (_mag < SCALE_MIN)
                  _mag = SCALE_MIN;
            cv->setMag(MagIdx::MAG_FREE, _mag);
            setMag(_mag);
            }
      }

//---------------------------------------------------------
//   getMag
//    return physical scale
//---------------------------------------------------------

double MuseScore::getMag(ScoreView* canvas) const
      {
      return mag->getMag(canvas);
      }

//---------------------------------------------------------
//   setMag
//    set logical scale
//---------------------------------------------------------

void MuseScore::setMag(double d)
      {
      mag->setMag(d);
      mag->setMagIdx(MagIdx::MAG_FREE);
      }

//---------------------------------------------------------
//   setPos
//    set position label
//---------------------------------------------------------

void MuseScore::setPos(int t)
      {
      if (cs == 0 || t < 0)
            return;

      TimeSigMap* s = cs->sigmap();
      int bar, beat, tick;
      s->tickValues(t, &bar, &beat, &tick);
      _positionLabel->setText(QString("%1:%2:%3")
         .arg(bar + 1,  3, 10, QLatin1Char(' '))
         .arg(beat + 1, 2, 10, QLatin1Char('0'))
         .arg(tick,     3, 10, QLatin1Char('0'))
         );
      }

//---------------------------------------------------------
//   undoRedo
//---------------------------------------------------------

void MuseScore::undoRedo(bool undo)
      {
      if (_sstate & (STATE_EDIT | STATE_TEXT_EDIT | STATE_HARMONY_FIGBASS_EDIT | STATE_LYRICS_EDIT)) {
            cv->postCmd("escape");
            qApp->processEvents();
            }
      if (cv) {
            Q_ASSERT(cs);
            cv->startUndoRedo();
            cs->undoRedo(undo);
            if (cs->inputState().segment())
                  setPos(cs->inputState().tick());
            if (cs->noteEntryMode() && !cv->noteEntryMode())
                  cv->postCmd("note-input");    // enter note entry mode
            else if (!cs->noteEntryMode() && cv->noteEntryMode())
                  cv->postCmd("escape");        // leave note entry mode
            updateInputState(cs);
            }
      endCmd();
      if (_inspector)
            _inspector->reset();
      }

//---------------------------------------------------------
//   showProgressBar
//---------------------------------------------------------

QProgressBar* MuseScore::showProgressBar()
      {
      if (_progressBar == 0)
            _progressBar = new QProgressBar(this);
      _statusBar->addWidget(_progressBar);
      _progressBar->show();
      return _progressBar;
      }

//---------------------------------------------------------
//   hideProgressBar
//---------------------------------------------------------

void MuseScore::hideProgressBar()
      {
      if (_progressBar)
            _statusBar->removeWidget(_progressBar);
      }

//---------------------------------------------------------
//   handleMessage
//---------------------------------------------------------

void MuseScore::handleMessage(const QString& message)
      {
      if (message.isEmpty())
            return;
      if (startcenter)
            showStartcenter(false);
      ((QtSingleApplication*)(qApp))->activateWindow();
      MasterScore* score = readScore(message);
      if (score) {
            setCurrentScoreView(appendScore(score));
            addRecentScore(score);
            writeSessionFile(false);
            }
      }

//---------------------------------------------------------
//   editInPianoroll
//---------------------------------------------------------

void MuseScore::editInPianoroll(Staff* staff)
      {
      if (pianorollEditor == 0)
            pianorollEditor = new PianorollEditor;
      pianorollEditor->setScore(staff->score());
      pianorollEditor->setStaff(staff);
      pianorollEditor->show();
      }

//---------------------------------------------------------
//   editInDrumroll
//---------------------------------------------------------

void MuseScore::editInDrumroll(Staff* staff)
      {
      if (drumrollEditor == 0)
            drumrollEditor = new DrumrollEditor;
      drumrollEditor->setStaff(staff);
      drumrollEditor->show();
      }

//---------------------------------------------------------
//   writeSessionFile
//---------------------------------------------------------

void MuseScore::writeSessionFile(bool cleanExit)
      {
// qDebug("write session file");

      QDir dir;
      dir.mkpath(dataPath);
      QFile f(dataPath + "/session");
      if (!f.open(QIODevice::WriteOnly)) {
            qDebug("cannot create session file <%s>", qPrintable(f.fileName()));
            return;
            }
      XmlWriter xml(0, &f);
      xml.header();
      xml.stag("museScore version=\"" MSC_VERSION "\"");
      xml.tagE(cleanExit ? "clean" : "dirty");

      foreach(MasterScore* score, scoreList) {
            xml.stag("Score");
            xml.tag("created", score->created());
            xml.tag("dirty", score->dirty());

            QString path;
            if (score->importedFilePath().isEmpty()) {
                              // score was not imported from another format, e.g. MIDI file
                  path = score->masterScore()->fileInfo()->absoluteFilePath();
                  }
            else if (score->masterScore()->fileInfo()->exists()) {   // score was saved
                  path = score->masterScore()->fileInfo()->absoluteFilePath();
                  }
            else {      // score was imported but not saved - store original file (e.g. MIDI) path
                  path = score->importedFilePath();
                  }

            if (cleanExit || score->tmpName().isEmpty()) {
                  xml.tag("path", path);
                  }
            else {
                  xml.tag("name", path);
                  xml.tag("path", score->tmpName());
                  }
            xml.etag();
            }
      int tab = 0;
      int idx = 0;
      for (int i = 0; i < tab1->count(); ++i) {
            ScoreView* v = tab1->view(i);
            if (v) {
                  if (v == cv) {
                        tab = 0;
                        idx = i;
                        }
                  xml.stag("ScoreView");
                  xml.tag("tab", tab);    // 0 instead of "tab" does not work
                  xml.tag("idx", i);
                  if (v->magIdx() == MagIdx::MAG_FREE)
                        xml.tag("mag", v->lmag());
                  else
                        xml.tag("magIdx", int(v->magIdx()));
                  xml.tag("x",   v->xoffset() / DPMM);
                  xml.tag("y",   v->yoffset() / DPMM);
                  xml.etag();
                  }
            }
      if (splitScreen()) {
            for (int i = 0; i < tab2->count(); ++i) {
                  ScoreView* v = tab2->view(i);
                  if (v) {
                        if (v == cv) {
                              tab = 1;
                              idx = i;
                              }
                        xml.stag("ScoreView");
                        xml.tag("tab", 1);
                        xml.tag("idx", i);
                        if (v->magIdx() == MagIdx::MAG_FREE)
                              xml.tag("mag", v->lmag());
                        else
                              xml.tag("magIdx", int(v->magIdx()));
                        xml.tag("x",   v->xoffset() / DPMM);
                        xml.tag("y",   v->yoffset() / DPMM);
                        xml.etag();
                        }
                  }
            }
      xml.tag("tab", tab);
      xml.tag("idx", idx);
      xml.etag();
      f.close();
      if (cleanExit) {
            // TODO: remove all temporary session backup files
            }
      }

//---------------------------------------------------------
//   removeSessionFile
//    remove temp files and session file
//---------------------------------------------------------

void MuseScore::removeSessionFile()
      {
      QFile f(dataPath + "/session");
      if (!f.exists())
            return;
      if (!f.remove()) {
            qDebug("cannot remove session file <%s>", qPrintable(f.fileName()));
            }
      }

//---------------------------------------------------------
//   autoSaveTimerTimeout
//---------------------------------------------------------

void MuseScore::autoSaveTimerTimeout()
      {
      bool sessionChanged = false;
      foreach (MasterScore* s, scoreList) {
            if (s->autosaveDirty()) {
                  QString tmp = s->tmpName();
                  if (!tmp.isEmpty()) {
                        QFileInfo fi(tmp);
                        // TODO: cannot catch exeption here:
                        s->saveCompressedFile(fi, false);
                        }
                  else {
                        QDir dir;
                        dir.mkpath(dataPath);
                        QTemporaryFile tf(dataPath + "/scXXXXXX.mscz");
                        tf.setAutoRemove(false);
                        if (!tf.open()) {
                              qDebug("autoSaveTimerTimeout(): create temporary file failed");
                              return;
                              }
                        s->setTmpName(tf.fileName());
                        QFileInfo info(tf.fileName());
                        s->saveCompressedFile(&tf, info, false);
                        tf.close();
                        sessionChanged = true;
                        }
                  s->setAutosaveDirty(false);
                  }
            }
      if (sessionChanged)
            writeSessionFile(false);
      if (preferences.autoSave) {
            int t = preferences.autoSaveTime * 60 * 1000;
            autoSaveTimer->start(t);
            }
      }

//---------------------------------------------------------
//   restoreSession
//    Restore last session. If "always" is true, then restore
//    last session even on a clean exit else only if last
//    session ended abnormal.
//    Return true if a session was found and restored.
//---------------------------------------------------------

bool MuseScore::restoreSession(bool always)
      {
      QFile f(dataPath + "/session");
      if (!f.exists())
            return false;
      if (!f.open(QIODevice::ReadOnly)) {
            qDebug("Cannot open session file <%s>", qPrintable(f.fileName()));
            return false;
            }
      XmlReader e(0, &f);
      int tab = 0;
      int idx = -1;
      bool cleanExit = false;
      while (e.readNextStartElement()) {
            if (e.name() == "museScore") {
                  /* QString version = e.attribute(QString("version"));
                  QStringList sl  = version.split('.');
                  int v           = sl[0].toInt() * 100 + sl[1].toInt();
                  */
                  while (e.readNextStartElement()) {
                        const QStringRef& tag(e.name());
                        if (tag == "clean") {
                              if (!always)
                                    return false;
                              cleanExit = true;
                              e.readNext();
                              }
                        else if (tag == "dirty") {
                              QMessageBox::StandardButton b = QMessageBox::question(0,
                                 tr("MuseScore"),
                                 tr("The previous session quit unexpectedly.\n\nRestore session?"),
                                 QMessageBox::Yes | QMessageBox::No,
                                 QMessageBox::Yes
                                 );
                              if (b != QMessageBox::Yes)
                                    return false;
                              e.readNext();
                              }
                        else if (tag == "Score") {
                              QString name;
                              bool created = false;
                              while (e.readNextStartElement()) {
                                    const QStringRef& tag(e.name());
                                    if (tag == "name")
                                          name = e.readElementText();
                                    else if (tag == "created")
                                          created = e.readInt();
                                    else if (tag == "dirty")
                                          /*int dirty =*/ e.readInt();
                                    else if (tag == "path") {
                                          MasterScore* score = readScore(e.readElementText());
                                          if (score) {
                                                if (!name.isEmpty())
                                                      score->masterScore()->setName(name);
                                                if (cleanExit) {
                                                      // override if last session did a clean exit
                                                      created = false;
                                                      }
                                                appendScore(score);
                                                score->setCreated(created);
                                                }
                                          }
                                    else {
                                          e.unknown();
                                          return false;
                                          }
                                    }
                              }
                        else if (tag == "ScoreView") {
                              qreal x       = .0;
                              qreal y       = .0;
                              qreal vmag    = 1.0;
                              MagIdx magIdx = MagIdx::MAG_FREE;
                              int tab       = 0;
                              int idx       = 0;
                              while (e.readNextStartElement()) {
                                    const QStringRef& tag(e.name());
                                    if (tag == "tab")
                                          tab = e.readInt();
                                    else if (tag == "idx")
                                          idx = e.readInt();
                                    else if (tag == "mag")
                                          vmag = e.readDouble();
                                    else if (tag == "magIdx")
                                          magIdx = MagIdx(e.readInt());
                                    else if (tag == "x")
                                          x = e.readDouble() * DPMM;
                                    else if (tag == "y")
                                          y = e.readDouble() * DPMM;
                                    else {
                                          e.unknown();
                                          return false;
                                          }
                                    }
                              (tab == 0 ? tab1 : tab2)->initScoreView(idx, vmag, magIdx, x, y);
                              }
                        else if (tag == "tab")
                              tab = e.readInt();
                        else if (tag == "idx")
                              idx = e.readInt();
                        else {
                              e.unknown();
                              return false;
                              }
                        }
                  }
            else {
                  e.unknown();
                  return false;
                  }
            }
      setCurrentView(tab, idx);
      return true;
      }

//---------------------------------------------------------
//   splitWindow
//---------------------------------------------------------

void MuseScore::splitWindow(bool horizontal)
      {
      if (!_splitScreen) {
            if (tab2 == 0) {
                  tab2 = new ScoreTab(&scoreList, this);
                  tab2->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
                  connect(tab2, SIGNAL(currentScoreViewChanged(ScoreView*)), SLOT(setCurrentScoreView(ScoreView*)));
                  connect(tab2, SIGNAL(tabCloseRequested(int)), SLOT(removeTab(int)));
                  connect(tab2, SIGNAL(actionTriggered(QAction*)), SLOT(cmd(QAction*)));
                  splitter->addWidget(tab2);
                  }
            tab2->setVisible(true);
            _splitScreen = true;
            _horizontalSplit = horizontal;
            splitter->setOrientation(_horizontalSplit ? Qt::Horizontal : Qt::Vertical);
            if (!scoreList.isEmpty()) {
                  tab2->setCurrentIndex(0);
                  MasterScore* s = scoreList[0];
                  s->setLayoutAll();
                  s->update();
                  setCurrentView(1, 0);
                  }
            }
      else {
            if (_horizontalSplit == horizontal) {
                  _splitScreen = false;
                  tab2->setVisible(false);
                  }
            else {
                  _horizontalSplit = horizontal;
                  QAction* a;
                  if (_horizontalSplit)
                        a = getAction("split-v");
                  else
                        a = getAction("split-h");
                  a->setChecked(false);
                  splitter->setOrientation(_horizontalSplit ? Qt::Horizontal : Qt::Vertical);
                  }
            }
      }

//---------------------------------------------------------
//   stateName
//---------------------------------------------------------

const char* stateName(ScoreState s)
      {
      switch(s) {
            case STATE_DISABLED:           return "STATE_DISABLED";
            case STATE_NORMAL:             return "STATE_NORMAL";
            case STATE_NOTE_ENTRY_STAFF_PITCHED: return "STATE_NOTE_ENTRY_STAFF_PITCHED";
            case STATE_NOTE_ENTRY_STAFF_DRUM:    return "STATE_NOTE_ENTRY_STAFF_DRUM";
            case STATE_NOTE_ENTRY_STAFF_TAB:     return "STATE_NOTE_ENTRY_STAFF_TAB";
            case STATE_NOTE_ENTRY:         return "STATE_NOTE_ENTRY";
            case STATE_NOTE_ENTRY_METHOD_STEPTIME:          return "STATE_NOTE_ENTRY_METHOD_STEPTIME";
            case STATE_NOTE_ENTRY_METHOD_REPITCH:           return "STATE_NOTE_ENTRY_METHOD_REPITCH";
            case STATE_NOTE_ENTRY_METHOD_RHYTHM:            return "STATE_NOTE_ENTRY_METHOD_RHYTHM";
            case STATE_NOTE_ENTRY_METHOD_REALTIME_AUTO:     return "STATE_NOTE_ENTRY_METHOD_REALTIME_AUTO";
            case STATE_NOTE_ENTRY_METHOD_REALTIME_MANUAL:   return "STATE_NOTE_ENTRY_METHOD_REALTIME_MANUAL";
            case STATE_EDIT:               return "STATE_EDIT";
            case STATE_TEXT_EDIT:          return "STATE_TEXT_EDIT";
            case STATE_LYRICS_EDIT:        return "STATE_LYRICS_EDIT";
            case STATE_HARMONY_FIGBASS_EDIT: return "STATE_HARMONY_FIGBASS_EDIT";
            case STATE_PLAY:               return "STATE_PLAY";
            case STATE_FOTO:               return "STATE_FOTO";
            default:                       return "??";
            }
      }

//---------------------------------------------------------
//   scorePageLayoutChanged
//---------------------------------------------------------

void MuseScore::scorePageLayoutChanged()
      {
      if (mainWindow) {
            mainWindow->setOrientation(MScore::verticalOrientation() ? Qt::Horizontal : Qt::Vertical);
            if (navigatorScrollArea())
                  navigatorScrollArea()->orientationChanged();
            }
      }

//---------------------------------------------------------
//   editRaster
//---------------------------------------------------------

void MuseScore::editRaster()
      {
      if (editRasterDialog == 0) {
            editRasterDialog = new EditRaster(this);
            }
      if (editRasterDialog->exec()) {
            qDebug("=====accept config raster");
            }
      }

//---------------------------------------------------------
//   showPianoKeyboard
//---------------------------------------------------------

void MuseScore::showPianoKeyboard(bool on)
      {
      if (_pianoTools == 0) {
            QAction* a = getAction("toggle-piano");
            _pianoTools = new PianoTools(this);
            addDockWidget(Qt::BottomDockWidgetArea, _pianoTools);
            connect(_pianoTools, SIGNAL(keyPressed(int, bool, int)), SLOT(midiNoteReceived(int, bool, int)));
            connect(_pianoTools, SIGNAL(keyReleased(int, bool, int)), SLOT(midiNoteReceived(int, bool, int)));
            connect(_pianoTools, SIGNAL(visibilityChanged(bool)), a, SLOT(setChecked(bool)));
            }
      if (on) {
            _pianoTools->show();
            }
      else {
            if (_pianoTools)
                  _pianoTools->hide();
            }
      }

//---------------------------------------------------------
//   showPluginCreator
//---------------------------------------------------------

void MuseScore::showPluginCreator(QAction* a)
      {
#ifdef SCRIPT_INTERFACE
      bool on = a->isChecked();
      if (on) {
            if (_pluginCreator == 0) {
                  _pluginCreator = new PluginCreator(0);
                  connect(_pluginCreator, SIGNAL(closed(bool)), a, SLOT(setChecked(bool)));
                  }
            _pluginCreator->show();
            }
      else {
            if (_pluginCreator)
                  _pluginCreator->hide();
            }
#endif
      }

//---------------------------------------------------------
//   showPluginManager
//---------------------------------------------------------

void MuseScore::showPluginManager()
      {
#ifdef SCRIPT_INTERFACE
      if (!pluginManager)
            pluginManager = new PluginManager(0);
      pluginManager->init();
      pluginManager->show();
#endif
      }

//---------------------------------------------------------
//   showMediaDialog
//---------------------------------------------------------

void MuseScore::showMediaDialog()
      {
      if (_mediaDialog == 0)
            _mediaDialog = new MediaDialog(this);
      _mediaDialog->setScore(cs);
      _mediaDialog->exec();
      }

//---------------------------------------------------------
//   getPaletteBox
//---------------------------------------------------------

PaletteBox* MuseScore::getPaletteBox()
      {
      if (paletteBox == 0) {
            paletteBox = new PaletteBox(this);
            QAction* a = getAction("toggle-palette");
            connect(paletteBox, SIGNAL(visibilityChanged(bool)), a, SLOT(setChecked(bool)));
            addDockWidget(Qt::LeftDockWidgetArea, paletteBox);
            }
      return paletteBox;
      }

//---------------------------------------------------------
//   midiNoteReceived
//---------------------------------------------------------

void MuseScore::midiNoteReceived(int pitch, bool ctrl, int vel)
      {
      if (cv)
            cv->midiNoteReceived(pitch, ctrl, vel);
      }

//---------------------------------------------------------
//   switchLayer
//---------------------------------------------------------

void MuseScore::switchLayer(const QString& s)
      {
      if (cs->switchLayer(s)) {
            cs->setLayoutAll();
            cs->update();
            }
      }

//---------------------------------------------------------
//   switchPlayMode
//---------------------------------------------------------

void MuseScore::switchPlayMode(int mode)
      {
      if (cs)
            cs->setPlayMode(PlayMode(mode));
      }

//---------------------------------------------------------
//   networkFinished
//---------------------------------------------------------

void MuseScore::networkFinished(QNetworkReply* reply)
      {
      if (reply->error() != QNetworkReply::NoError) {
            qDebug("Error while checking update [%s]", qPrintable(reply->errorString()));
            return;
            }
      QByteArray ha = reply->rawHeader("Content-Disposition");
      QString s(ha);
      QString name;
      QRegExp re(".*filename=\"(.*)\"");

      if (!s.isEmpty() && re.indexIn(s) != -1) {
            name = re.cap(1);
             }
      else {
            QUrl url = reply->url();
            QString path = url.path();
            qDebug("Path <%s>", qPrintable(path));
            QFileInfo fi(path);
            name = fi.fileName();
            }

      qDebug("header <%s>", qPrintable(s));
      qDebug("name <%s>", qPrintable(name));

      QByteArray data = reply->readAll();
      QString tmpName = QDir::tempPath () + "/"+ name;
      QFile f(tmpName);
      f.open(QIODevice::WriteOnly);
      f.write(data);
      f.close();

      MasterScore* score = readScore(tmpName);
      if (!score) {
            qDebug("readScore failed");
            return;
            }
      score->setCreated(true);
      setCurrentScoreView(appendScore(score));
      }

//---------------------------------------------------------
//   loadFile
//    load file from url
//---------------------------------------------------------

void MuseScore::loadFile(const QString& s)
      {
      loadFile(QUrl(s));
      }

void MuseScore::loadFile(const QUrl& url)
      {
      if (!networkManager) {
            networkManager = new QNetworkAccessManager(this);
            connect(networkManager, SIGNAL(finished(QNetworkReply*)),
               SLOT(networkFinished(QNetworkReply*)));
            }
      networkManager->get(QNetworkRequest(url));
      }

//---------------------------------------------------------
//   selectSimilar
//---------------------------------------------------------

void MuseScore::selectSimilar(Element* e, bool sameStaff)
      {
      Score* score = e->score();
      score->selectSimilar(e, sameStaff);

      if (score->selectionChanged()) {
            score->setSelectionChanged(false);
            SelState ss = score->selection().state();
            selectionChanged(ss);
            }
      }

void MuseScore::selectSimilarInRange(Element* e)
      {
      Score* score = e->score();
      score->selectSimilarInRange(e);

      if (score->selectionChanged()) {
            score->setSelectionChanged(false);
            SelState ss = score->selection().state();
            selectionChanged(ss);
            }
      }

//---------------------------------------------------------
//   selectElementDialog
//---------------------------------------------------------

void MuseScore::selectElementDialog(Element* e)
      {
      Score* score = e->score();
      if (e->isNote()) {
            SelectNoteDialog sd(toNote(e), 0);
            if (sd.exec()) {
                  NotePattern pattern;
                  sd.setPattern(&pattern);

                  if (sd.isInSelection())
                        score->scanElementsInRange(&pattern, Score::collectNoteMatch);
                  else
                        score->scanElements(&pattern, Score::collectNoteMatch);

                  if (sd.doReplace()) {
                        score->select(0, SelectType::SINGLE, 0);
                        for (Note* ee : pattern.el)
                              score->select(ee, SelectType::ADD, 0);
                        }
                  else if (sd.doSubtract()) {
                        QList<Element*> sl(score->selection().elements());
                        for (Note* ee : pattern.el)
                              sl.removeOne(ee);
                        score->select(0, SelectType::SINGLE, 0);
                        for (Element* ee : sl)
                              score->select(ee, SelectType::ADD, 0);
                        }
                  else if (sd.doAdd()) {
                        QList<Element*> sl(score->selection().elements());
                        for (Note* ee : pattern.el) {
                              if(!sl.contains(ee))
                                    score->select(ee, SelectType::ADD, 0);
                              }
                        }
                  }
            }
      else {
            SelectDialog sd(e, 0);
            if (sd.exec()) {
                  ElementPattern pattern;
                  sd.setPattern(&pattern);

                  if (sd.isInSelection())
                        score->scanElementsInRange(&pattern, Score::collectMatch);
                  else
                        score->scanElements(&pattern, Score::collectMatch);

                  if (sd.doReplace()) {
                        score->select(0, SelectType::SINGLE, 0);
                        for (Element* ee : pattern.el)
                              score->select(ee, SelectType::ADD, 0);
                        }
                  else if (sd.doSubtract()) {
                        QList<Element*> sl(score->selection().elements());
                        for (Element* ee : pattern.el)
                              sl.removeOne(ee);
                        score->select(0, SelectType::SINGLE, 0);
                        for (Element* ee : sl)
                              score->select(ee, SelectType::ADD, 0);
                        }
                  else if (sd.doAdd()) {
                        QList<Element*> sl(score->selection().elements());
                        for (Element* ee : pattern.el) {
                              if(!sl.contains(ee))
                                    score->select(ee, SelectType::ADD, 0);
                              }
                        }
                  }
            }
      if (score->selectionChanged()) {
            score->setSelectionChanged(false);
            SelState ss = score->selection().state();
            selectionChanged(ss);
            }
      }

//---------------------------------------------------------
//   RecordButton
//---------------------------------------------------------

RecordButton::RecordButton(QWidget* parent)
   : SimpleButton(":/data/recordOn.svg", ":/data/recordOff.svg", parent)
      {
      setCheckable(true);
      defaultAction()->setCheckable(true);
      setToolTip(qApp->translate("RecordButton", "Record"));
      }

//---------------------------------------------------------
//   GreendotButton
//---------------------------------------------------------

GreendotButton::GreendotButton(QWidget* parent)
   : SimpleButton(":/data/greendot.svg", ":/data/darkgreendot.svg", parent)
      {
      setCheckable(true);
      setToolTip(qApp->translate("GreendotButton", "Record"));
      }

//---------------------------------------------------------
//   drawHandle
//---------------------------------------------------------

QRectF drawHandle(QPainter& p, const QPointF& pos, bool active)
      {
      p.save();
      p.setPen(QPen(QColor(MScore::selectColor[0]), 2.0/p.matrix().m11()));
      if (active)
            p.setBrush(MScore::selectColor[0]);
      else
            p.setBrush(Qt::NoBrush);
      qreal w = 8.0 / p.matrix().m11();
      qreal h = 8.0 / p.matrix().m22();

      QRectF r(-w/2, -h/2, w, h);
      r.translate(pos);
      p.drawRect(r);
      p.restore();
      return r;
      }

//---------------------------------------------------------
//   transpose
//---------------------------------------------------------

void MuseScore::transpose()
      {
      if (cs->last() == 0)     // empty score?
            return;

      bool noSelection = cs->selection().isNone();
      if (noSelection)
            cs->cmdSelectAll();
      bool rangeSelection = cs->selection().isRange();
      TransposeDialog td;

      // TRANSPOSE_BY_KEY and "transpose keys" is only possible if selection state is SelState::RANGE
      td.enableTransposeKeys(rangeSelection);
      td.enableTransposeByKey(rangeSelection);
      td.enableTransposeChordNames(rangeSelection);

      int startStaffIdx = 0;
      int endStaffIdx   = 0;
      int startTick     = 0;
      if (rangeSelection) {
            startStaffIdx = cs->selection().staffStart();
            endStaffIdx   = cs->selection().staffEnd();
            startTick     = cs->selection().tickStart();
            }

      // find the key of the first pitched staff
      Key key = Key::C;
      for (int i = startStaffIdx; i < endStaffIdx; ++i) {
            Staff* staff = cs->staff(i);
            if (staff->isPitchedStaff(startTick)) {
                  key = staff->key(startTick);
                  if (!cs->styleB(StyleIdx::concertPitch)) {
                        int diff = staff->part()->instrument(startTick)->transpose().chromatic;
                        if (diff)
                              key = transposeKey(key, diff);
                        }
                  break;
                  }
            }

      td.setKey(key);
      if (!td.exec())
            return;

      cs->transpose(td.mode(), td.direction(), td.transposeKey(), td.transposeInterval(),
         td.getTransposeKeys(), td.getTransposeChordNames(), td.useDoubleSharpsFlats());

      if (noSelection)
            cs->deselectAll();
      }

//---------------------------------------------------------
//   cmd
//---------------------------------------------------------

void MuseScore::cmd(QAction* a)
      {
      if (inChordEditor)      // HACK
            return;

      MScore::setError(MS_NO_ERROR);

      QString cmdn(a->data().toString());

      if (MScore::debugMode)
            qDebug("MuseScore::cmd <%s>", qPrintable(cmdn));

      const Shortcut* sc = Shortcut::getShortcut(cmdn.toLatin1().data());
      if (sc == 0) {
            qDebug("MuseScore::cmd(): unknown action <%s>", qPrintable(cmdn));
            return;
            }
      if (cs && (sc->state() & _sstate) == 0) {
            QMessageBox::warning(0,
               QWidget::tr("MuseScore: Invalid Command"),
               QString("Command %1 not valid in current state").arg(cmdn));
            return;
            }
      if (cmdn == "repeat-cmd") {
            a  = lastCmd;
            sc = lastShortcut;
            if (a == 0)
                  return;
            cmdn = a->data().toString();
            }
      else {
            lastCmd = a;
            lastShortcut = sc;
            }

      if (sc->needsScore() && ! cs) {
            qDebug("no score");
            return;
            }
      if (sc->isCmd()) {
            if (!cv->editMode())
                  cs->startCmd();
            }
      cmd(a, cmdn);
      if (lastShortcut->isCmd())
            cs->endCmd();
      endCmd();
      }

//---------------------------------------------------------
//   endCmd
//    Called after every command action (including every
//    mouse action).
//    Updates the UI after a possible score change.
//---------------------------------------------------------

void MuseScore::endCmd()
      {
      if (MScore::_error != MS_NO_ERROR)
            showError();
      if (cs) {
            setPos(cs->inputState().tick());
            updateInputState(cs);
            updateUndoRedo();
            dirtyChanged(cs);
            Element* e = cs->selection().element();

            // For multiple notes selected check if they all have same pitch and tuning
            bool samePitch = true;
            int pitch    = -1;
            float tuning = 0;
            for (Element* e : cs->selection().elements()) {
                  if (!e->isNote()) {
                        samePitch = false;
                        break;
                        }
                  Note* note = toNote(e);
                  if (pitch == -1) {
                        pitch = note->ppitch();
                        tuning = note->tuning();
                        }
                  else if (note->ppitch() != pitch || fabs(tuning - note->tuning()) > 0.01) {
                        samePitch = false;
                        break;
                        }
                  }
            if (samePitch && pitch >= 0)
                  e = cs->selection().elements()[0];

            NoteEntryMethod entryMethod = cs->noteEntryMethod();
            if (e && (cs->playNote() || cs->playChord())
                        && entryMethod != NoteEntryMethod::REALTIME_AUTO
                        && entryMethod != NoteEntryMethod::REALTIME_MANUAL) {
                  if (cs->playChord() && preferences.playChordOnAddNote &&  e->type() == ElementType::NOTE)
                        play(static_cast<Note*>(e)->chord());
                  else
                        play(e);
                  cs->setPlayNote(false);
                  cs->setPlayChord(false);
                  }
            MasterScore* ms = cs->masterScore();
            if (ms->excerptsChanged()) {
                  if (tab2) {
//                      ScoreView* v = tab2->view();
//                      if (v && v->score() == ms) {
                              tab2->updateExcerpts();
//                            }
                        }
                  if (tab1) {
                        ScoreView* v = tab1->view();
                        if (v && v->score()->masterScore() == ms) {
                              tab1->updateExcerpts();
                              }
                        else if (v == 0) {
                              tab1->setExcerpt(0);
                              tab1->updateExcerpts();
                              }
                        }
                  ms->setExcerptsChanged(false);
                  }
            if (ms->instrumentsChanged()) {
                  if (!noSeq && (seq && seq->isRunning()))
                        seq->initInstruments();
                  instrumentChanged();                // update mixer
                  ms->setInstrumentsChanged(false);
                  }
            if (cs->selectionChanged()) {
                  cs->setSelectionChanged(false);
                  SelState ss = cs->selection().state();
                  selectionChanged(ss);
                  }
            getAction("concert-pitch")->setChecked(cs->styleB(StyleIdx::concertPitch));

            if (e == 0 && cs->noteEntryMode())
                  e = cs->inputState().cr();
            updateViewModeCombo();
            ScoreAccessibility::instance()->updateAccessibilityInfo();
            }
      else {
            if (_inspector)
                  _inspector->setElement(0);
            selectionChanged(SelState::NONE);
            }
      }

//---------------------------------------------------------
//   updateUndoRedo
//---------------------------------------------------------

void MuseScore::updateUndoRedo()
      {
      QAction* a = getAction("undo");
      a->setEnabled(cs ? cs->undoStack()->canUndo() : false);
      a = getAction("redo");
      a->setEnabled(cs ? cs->undoStack()->canRedo() : false);
      }

//---------------------------------------------------------
//   cmd
//---------------------------------------------------------

void MuseScore::cmd(QAction* a, const QString& cmd)
      {
      if (cmd == "instruments") {
            editInstrList();
            if (mixer)
                  mixer->updateAll(cs->masterScore());
            }
      else if (cmd == "rewind") {
            seq->rewindStart();
            if (playPanel)
                  playPanel->heartBeat(0, 0, 0);
            }
      else if (cmd == "play-next-measure")
            seq->nextMeasure();
      else if (cmd == "play-next-chord")
            seq->nextChord();
      else if (cmd == "play-prev-measure")
            seq->prevMeasure();
      else if (cmd == "play-prev-chord")
            seq->prevChord();
      else if (cmd == "seek-begin")
            seq->rewindStart();
      else if (cmd == "seek-end")
            seq->seekEnd();
      else if (cmd == "keys")
            showKeyEditor();
      else if (cmd == "file-open")
            loadFiles();
      else if (cmd == "file-save")
            saveFile();
      else if (cmd == "file-save-online")
            showUploadScoreDialog();
      else if (cmd == "file-export")
            exportFile();
      else if (cmd == "file-part-export")
            exportParts();
      else if (cmd == "file-import-pdf")
            openExternalLink("https://musescore.com/import");
      else if (cmd == "file-close")
            closeScore(cs);
      else if (cmd == "file-save-as")
            saveAs(cs, false);
      else if (cmd == "file-save-selection")
            saveSelection(cs);
      else if (cmd == "file-save-a-copy")
            saveAs(cs, true);
      else if (cmd == "file-new")
            newFile();
      else if (cmd == "quit")
            close();
      else if (cmd == "masterpalette")
            showMasterPalette();
      else if (cmd == "key-signatures")
            showMasterPalette(qApp->translate("Palette", "Key Signatures"));
      else if (cmd == "time-signatures")
            showMasterPalette(qApp->translate("Palette", "Time Signatures"));
      else if (cmd == "symbols")
            showMasterPalette(qApp->translate("MasterPalette", "Symbols"));
      else if (cmd == "toggle-statusbar") {
            preferences.showStatusBar = a->isChecked();
            _statusBar->setVisible(preferences.showStatusBar);
            preferences.write();
            }
      else if (cmd == "append-measures")
            cmdAppendMeasures();
      else if (cmd == "insert-measures")
            cmdInsertMeasures();
      else if (cmd == "debugger")
            startDebugger();
      else if (cmd == "album")
            showAlbumManager();
      else if (cmd == "layer")
            showLayerManager();
      else if (cmd == "backspace") {
            if (_sstate != STATE_NORMAL )
                  undoRedo(true);
#ifdef Q_OS_MAC
            else if (cs) {
                  cs->startCmd();
                  cs->cmdDeleteSelection();
                  cs->endCmd();
                  }
#endif
            }
      else if (cmd == "zoomin")
            incMag();
      else if (cmd == "zoomout")
            decMag();
      else if (cmd == "zoom100") {
            if (cv)
                  cv->setMag(MagIdx::MAG_100, 1.0);
            setMag(1.0);
            }
      else if (cmd == "midi-on")
            midiinToggled(a->isChecked());
      else if (cmd == "undo")
            undoRedo(true);
      else if (cmd == "redo")
            undoRedo(false);
      else if (cmd == "toggle-palette")
            showPalette(a->isChecked());
      else if (cmd == "startcenter")
            showStartcenter(a->isChecked());
      else if (cmd == "inspector")
            showInspector(a->isChecked());
#ifdef OMR
      else if (cmd == "omr")
            showOmrPanel(a->isChecked());
#endif
      else if (cmd == "toggle-playpanel")
            showPlayPanel(a->isChecked());
      else if (cmd == "toggle-navigator")
            showNavigator(a->isChecked());
      else if (cmd == "toggle-midiimportpanel")
            importmidiPanel->setVisible(a->isChecked());
      else if (cmd == "toggle-mixer")
            showMixer(a->isChecked());
      else if (cmd == "synth-control")
            showSynthControl(a->isChecked());
      else if (cmd == "toggle-selection-window")
            showSelectionWindow(a->isChecked());
      else if (cmd == "show-keys")
            ;
      else if (cmd == "toggle-fileoperations")
            fileTools->setVisible(!fileTools->isVisible());
      else if (cmd == "toggle-transport")
            transportTools->setVisible(!transportTools->isVisible());
      else if (cmd == "toggle-concertpitch")
            cpitchTools->setVisible(!cpitchTools->isVisible());
      else if (cmd == "toggle-imagecapture")
            fotoTools->setVisible(!fotoTools->isVisible());
      else if (cmd == "toggle-noteinput")
            entryTools->setVisible(!entryTools->isVisible());
      else if (cmd == "help")
            showContextHelp();
      else if (cmd == "follow")
            preferences.followSong = a->isChecked();
      else if (cmd == "split-h")
            splitWindow(true);
      else if (cmd == "split-v")
            splitWindow(false);
      else if (cmd == "edit-harmony")
            editChordStyle();
      else if (cmd == "parts")
            startExcerptsDialog();
      else if (cmd == "fullscreen") {
            _fullscreen = a->isChecked();
            if (_fullscreen)
                  showFullScreen();
            else
                  showNormal();

#ifdef Q_OS_MAC
            // Qt Bug: Toolbar goes into unified mode
            // after switching back from fullscreen
            setUnifiedTitleAndToolBarOnMac(false);
#endif
            }
      else if (cmd == "config-raster")
            editRaster();
      else if (cmd == "hraster" || cmd == "vraster")  // value in [hv]RasterAction already set
            ;
      else if (cmd == "toggle-piano")
            showPianoKeyboard(a->isChecked());
      else if (cmd == "plugin-creator")
            showPluginCreator(a);
      else if (cmd == "plugin-manager")
            showPluginManager();
      else if(cmd == "resource-manager"){
            ResourceManager r(0);
            r.exec();
            }
      else if (cmd == "media")
            showMediaDialog();
      else if (cmd == "page-settings")
            showPageSettings();
      else if (cmd == "next-score")
            changeScore(1);
      else if (cmd == "previous-score")
            changeScore(1);
      else if (cmd == "transpose")
            transpose();
      else if (cmd == "save-style") {
            QString name = getStyleFilename(false);
            if (!name.isEmpty()) {
                  if (!cs->saveStyle(name)) {
                        QMessageBox::critical(this,
                           tr("MuseScore: Save Style"), MScore::lastError);
                        }
                  }
            }
      else if (cmd == "load-style") {
            QString name = mscore->getStyleFilename(true);
            if (!name.isEmpty()) {
                  cs->startCmd();
                  if (!cs->loadStyle(name)) {
                        QMessageBox::critical(this,
                           tr("MuseScore: Load Style"), MScore::lastError);
                        }
                  cs->endCmd();
                  }
            }
      else if (cmd == "edit-style") {
            EditStyle es(cs, this);
            es.exec();
            }
//      else if (cmd == "edit-text-style") {
//            TextStyleDialog es(0, cs);
//            es.exec();
//            }
      else if (cmd == "edit-info") {
            MetaEditDialog med(cs, 0);
            med.exec();
            }
      else if (cmd == "print")
            printFile();
      else if (cmd == "repeat") {
            MScore::playRepeats = a->isChecked();
            if (cs) {
                  cs->updateRepeatList(MScore::playRepeats);
                  emit cs->playlistChanged();
                  }
            }
      else if (cmd == "pan")
            MScore::panPlayback = !MScore::panPlayback;
      else if (cmd == "show-invisible")
            cs->setShowInvisible(a->isChecked());
      else if (cmd == "show-unprintable")
            cs->setShowUnprintable(a->isChecked());
      else if (cmd == "show-frames")
            cs->setShowFrames(a->isChecked());
      else if (cmd == "show-pageborders")
            cs->setShowPageborders(a->isChecked());
      else if (cmd == "mark-irregular")
            cs->setMarkIrregularMeasures(a->isChecked());
      else if (cmd == "tempo")
            addTempo();
      else if (cmd == "loop") {
            if (loop()) {
                  if (cs->selection().isRange())
                        seq->setLoopSelection();
                  }
            }
      else if (cmd == "loop-in") {
            seq->setLoopIn();
            loopAction->setChecked(true);
            }
      else if (cmd == "loop-out") {
            seq->setLoopOut();
            loopAction->setChecked(true);
            }
      else if (cmd == "metronome")  // no action
            ;
      else if (cmd == "countin")    // no action
            ;
      else if (cmd == "lock") {
            if (_sstate == STATE_LOCK)
                  changeState(STATE_NORMAL);
            else
                  changeState(STATE_LOCK);
            }
      else if (cmd == "find")
            showSearchDialog();
      else if (cmd == "text-b") {
            if (_textTools)
                  _textTools->toggleBold();
            }
      else if (cmd == "text-i") {
            if (_textTools)
                  _textTools->toggleItalic();
            }
      else if (cmd == "text-u") {
            if (_textTools)
                  _textTools->toggleUnderline();
            }
      else if (cmd == "edit-toolbars")
            showToolbarEditor();
      else if (cmd == "viewmode") {
            if (cs) {
                  if (cs->layoutMode() == LayoutMode::PAGE)
                        switchLayoutMode(LayoutMode::LINE);
                  else
                        switchLayoutMode(LayoutMode::PAGE);
                  }
            }
#ifndef NDEBUG
      else if (cmd == "no-horizontal-stretch") {
            MScore::noHorizontalStretch = a->isChecked();
            if (cs) {
                  cs->setLayoutAll();
                  cs->update();
                  }
            }
      else if (cmd == "no-vertical-stretch") {
            MScore::noVerticalStretch = a->isChecked();
            if (cs) {
                  cs->setLayoutAll();
                  cs->update();
                  }
            }
      else if (cmd == "show-segment-shapes") {
            MScore::showSegmentShapes = a->isChecked();
            if (cs) {
                  cs->setLayoutAll();
                  cs->update();
                  }
            }
      else if (cmd == "show-measure-shapes") {
            MScore::showMeasureShapes = a->isChecked();
            if (cs) {
                  cs->setLayoutAll();
                  cs->update();
                  }
            }
      else if (cmd == "show-bounding-rect") {
            MScore::showBoundingRect = a->isChecked();
            if (cs) {
                  cs->setLayoutAll();
                  cs->update();
                  }
            }
      else if (cmd == "show-corrupted-measures") {
            MScore::showCorruptedMeasures = a->isChecked();
            if (cs) {
                  cs->setLayoutAll();
                  cs->update();
                  }
            }
#endif
      else {
            if (cv) {
                  //isAncestorOf is called to see if a widget from inspector has focus
                  //if so, the focus doesn't get shifted to the score, unless escape is
                  //pressed, or the user clicks in the score
                  if (!inspector()->isAncestorOf(qApp->focusWidget()) || cmd == "escape")
                        cv->setFocus();
                  cv->cmd(a);
                  }
            else
                  qDebug("2:unknown cmd <%s>", qPrintable(cmd));
            }
      if (debugger)
            debugger->reloadClicked();
      }

//---------------------------------------------------------
//   openExternalLink
//---------------------------------------------------------

void MuseScore::openExternalLink(const QString& url)
      {
      QDesktopServices::openUrl(url);
      }

//---------------------------------------------------------
//   navigator
//---------------------------------------------------------

Navigator* MuseScore::navigator() const
      {
      return _navigator ? static_cast<Navigator*>(_navigator->widget()) : 0;
      }

//---------------------------------------------------------
//   getSearchDialog
//---------------------------------------------------------

QWidget* MuseScore::searchDialog() const
      {
      return _searchDialog;
      }

//---------------------------------------------------------
//   updateLayer
//---------------------------------------------------------

void MuseScore::updateLayer()
      {
      layerSwitch->clear();
      bool enable;
      if (cs) {
            enable = cs->layer().size() > 1;
            if (enable) {
                  foreach(const Layer& l, cs->layer())
                        layerSwitch->addItem(l.name);
                  layerSwitch->setCurrentIndex(cs->currentLayer());
                  }
            }
      else
           enable = false;
      layerSwitch->setVisible(enable);
      }

//---------------------------------------------------------
//   updatePlayMode
//---------------------------------------------------------

void MuseScore::updatePlayMode()
      {
      bool enable = false;
      if (cs) {
            enable = cs->audio() != 0;
            playMode->setCurrentIndex(int(cs->playMode()));
            }
      playMode->setVisible(enable);
      }

//---------------------------------------------------------
//   closeScore
//---------------------------------------------------------

void MuseScore::closeScore(Score* score)
      {
      // Let's compute maximum count of ports in remaining scores
      if (seq)
            seq->recomputeMaxMidiOutPort();
      removeTab(scoreList.indexOf(score->masterScore()));
      }

//---------------------------------------------------------
//   noteTooShortForTupletDialog
//---------------------------------------------------------

void MuseScore::noteTooShortForTupletDialog()
      {
      QMessageBox::warning(this, tr("MuseScore: Warning"),
        tr("Cannot create tuplet: Note value is too short")
        );
      }

//---------------------------------------------------------
//   instrumentChanged
//---------------------------------------------------------

void MuseScore::instrumentChanged()
      {
      if (mixer)
            mixer->updateAll(cs->masterScore());
      }

//---------------------------------------------------------
//   mixerPreferencesChanged
//---------------------------------------------------------

void MuseScore::mixerPreferencesChanged(bool showMidiControls)
      {
      if (mixer)
            mixer->midiPrefsChanged(showMidiControls);
      }

//---------------------------------------------------------
//   changeScore
//    switch current score
//    step = 1    switch to next score
//    step = -1   switch to previous score
//---------------------------------------------------------

void MuseScore::changeScore(int step)
      {
      int index = tab1->currentIndex();
      int n     = tab1->count();
      if (n == 1)
            return;
      index += step;
      if (index >= n)
            index = 0;
      if (index < 0)
            index = n - 1;
      setCurrentScoreView(index);
      }

//---------------------------------------------------------
//   switchLayoutMode
//    val is index in QComboBox viewModeCombo
//---------------------------------------------------------

void MuseScore::switchLayoutMode(int val)
      {
      if (!cs)
            return;
      switchLayoutMode(static_cast<LayoutMode>(viewModeCombo->itemData(val).toInt()));
      }

void MuseScore::switchLayoutMode(LayoutMode mode)
      {
      // find a measure to use as reference, if possible
      QRectF view = cv->toLogical(QRect(0.0, 0.0, width(), height()));
      Measure* m = cs->firstMeasure();
      while (m && !view.intersects(m->canvasBoundingRect()))
            m = m->nextMeasureMM();

      cv->loopUpdate(getAction("loop")->isChecked());

      if (mode != cs->layoutMode()) {
            cs->setLayoutMode(mode);
            cs->doLayout();
            }

      // adjustCanvasPosition often tries to preserve Y position
      // but this doesn't make sense when switching modes
      // also, better positioning is usually achieved if you start from the top
      // and there is really no better place to position canvas if we were all the way off page previously
      cv->pageTop();
      if (m && m != cs->firstMeasure())
            cv->adjustCanvasPosition(m, false);
      }

//---------------------------------------------------------
//   showDrumTools
//---------------------------------------------------------

void MuseScore::showDrumTools(const Drumset* drumset, Staff* staff)
      {
      if (drumset) {
            if (!_drumTools) {
                  _drumTools = new DrumTools(this);
                  addDockWidget(Qt::BottomDockWidgetArea, _drumTools);
                  }
            _drumTools->setDrumset(cs, staff, drumset);
            _drumTools->show();
            }
      else {
            if (_drumTools)
                  _drumTools->hide();
            }
      }

//---------------------------------------------------------
//   updateDrumTools
//---------------------------------------------------------

void MuseScore::updateDrumTools(const Drumset* ds)
      {
      if (_drumTools)
            _drumTools->updateDrumset(ds);
      }

//---------------------------------------------------------
//   endSearch
//---------------------------------------------------------

void MuseScore::endSearch()
      {
      _searchDialog->hide();
      if (cv)
            cv->setFocus();
      }

//---------------------------------------------------------
//   showSearchDialog
//---------------------------------------------------------

void MuseScore::showSearchDialog()
      {
      if (_searchDialog == 0) {
            _searchDialog = new QWidget;
            _searchDialog->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum);
            QHBoxLayout* searchDialogLayout = new QHBoxLayout;
            _searchDialog->setLayout(searchDialogLayout);
            layout->insertWidget(2, _searchDialog);

            QToolButton* searchExit = new QToolButton;
            searchExit->setAutoRaise(true);
            searchExit->setIcon(*icons[int(Icons::close_ICON)]);
            connect(searchExit, SIGNAL(clicked()), SLOT(endSearch()));
            searchDialogLayout->addWidget(searchExit);
            searchDialogLayout->addSpacing(10);

            searchDialogLayout->addWidget(new QLabel(tr("Go To: ")));

            searchCombo = new SearchComboBox;
            searchDialogLayout->addWidget(searchCombo);

            searchDialogLayout->addStretch(10);
            _searchDialog->hide();

            qDebug("Line edit %p", searchCombo->lineEdit());

            // does not work: connect(searchCombo->lineEdit(), SIGNAL(returnPressed()), SLOT(endSearch()));
            connect(searchCombo->lineEdit(), SIGNAL(editingFinished()), SLOT(endSearch()));
            }

      searchCombo->clearEditText();
      searchCombo->setFocus();
      _searchDialog->show();
      }

#ifndef SCRIPT_INTERFACE
void MuseScore::pluginTriggered(int) {}
void MuseScore::loadPlugins() {}
bool MuseScore::loadPlugin(const QString&) { return false;}
void MuseScore::unloadPlugins() {}
#endif


void MuseScore::saveGeometry(QWidget const*const qw)
      {
      QSettings settings;
      QString objectName = qw->objectName();
      Q_ASSERT(!objectName.isEmpty());
      settings.beginGroup("Geometries");
      settings.setValue(objectName, qw->saveGeometry());
      settings.endGroup();
      }

void MuseScore::restoreGeometry(QWidget *const qw)
      {
      if (!useFactorySettings) {
            QSettings settings;
            QString objectName = qw->objectName();
            Q_ASSERT(!objectName.isEmpty());
            settings.beginGroup("Geometries");
            qw->restoreGeometry(settings.value(objectName).toByteArray());
            settings.endGroup();
            }
      }

//---------------------------------------------------------
//   recentScores
//    return a list of recent scores
//    omit loaded scores
//---------------------------------------------------------

QFileInfoList MuseScore::recentScores() const
      {
      QFileInfoList fil;
      for (const QString& s : _recentScores) {
            if (s.isEmpty())
                  continue;
            QString data(s);
            QFileInfo fi(s);
            bool alreadyLoaded = false;
            QString fp = fi.canonicalFilePath();
            for (Score* s : mscore->scores()) {
                  if ((s->masterScore()->fileInfo()->canonicalFilePath() == fp) || (s->importedFilePath() == fp)) {
                        alreadyLoaded = true;
                        break;
                        }
                  }
            if (!alreadyLoaded && fi.exists())
                  fil.append(fi);
            }
      return fil;
      }

//---------------------------------------------------------
//   createPopupMenu
//---------------------------------------------------------

QMenu* MuseScore::createPopupMenu()
      {
      QMenu* m = QMainWindow::createPopupMenu();
      QList<QAction*> al = m->actions();
      for (QAction* a : al) {
            // textTool visibility is handled differentlyr
            if (_textTools && a->text() == _textTools->windowTitle())
                  m->removeAction(a);
            }
      return m;
      }

}

using namespace Ms;


//---------------------------------------------------------
//   main
//---------------------------------------------------------

int main(int argc, char* av[])
      {
#ifndef NDEBUG
      qSetMessagePattern("%{file}:%{function}: %{message}");
      checkProperties();
      Ms::checkStyles();
#endif

      QApplication::setDesktopSettingsAware(true);
#if defined(QT_DEBUG) && defined(Q_OS_WIN)
      qInstallMessageHandler(mscoreMessageHandler);
#endif

      QFile f(":/revision.h");
      f.open(QIODevice::ReadOnly);
      revision = QString(f.readAll()).trimmed();
      f.close();

      MuseScoreApplication* app;
      if (MuseScore::unstable()) {
            app = new MuseScoreApplication("mscore-dev3", argc, av);
            QCoreApplication::setApplicationName("MuseScoreDevelopment");
            }
      else {
            app = new MuseScoreApplication("mscore3", argc, av);
            QCoreApplication::setApplicationName("MuseScore3");
            }
      QCoreApplication::setOrganizationName("MuseScore");
      QCoreApplication::setOrganizationDomain("musescore.org");
      QCoreApplication::setApplicationVersion(VERSION);
      QAccessible::installFactory(AccessibleScoreView::ScoreViewFactory);
      QAccessible::installFactory(AccessibleSearchBox::SearchBoxFactory);
      QAccessible::installFactory(Awl::AccessibleAbstractSlider::AbstractSliderFactory);

      Q_INIT_RESOURCE(zita);

#ifndef Q_OS_MAC
      QSettings::setDefaultFormat(QSettings::IniFormat);
#endif

      QCommandLineParser parser;

      parser.addHelpOption(); // -?, -h, --help
      parser.addVersionOption(); // -v, --version

    //parser.addOption(QCommandLineOption({"v", "version"}, "Print version")); // see above
      parser.addOption(QCommandLineOption(      "long-version", "Print detailed version information"));
      parser.addOption(QCommandLineOption({"d", "debug"}, "Debug mode"));
      parser.addOption(QCommandLineOption({"L", "layout-debug"}, "Layout debug"));
      parser.addOption(QCommandLineOption({"s", "no-synthesizer"}, "No internal synthesizer"));
      parser.addOption(QCommandLineOption({"m", "no-midi"}, "No midi"));
      parser.addOption(QCommandLineOption({"a", "use-audio"}, "Use audio driver: jack, alsa, pulse, or portaudio", "driver"));
      parser.addOption(QCommandLineOption({"n", "new-score"}, "Start with new score"));
      parser.addOption(QCommandLineOption({"I", "dump-midi-in"}, "Dump midi input"));
      parser.addOption(QCommandLineOption({"O", "dump-midi-out"}, "Dump midi output"));
      parser.addOption(QCommandLineOption({"o", "export-to"}, "Export to 'file'; format depends on file extension", "file"));
      parser.addOption(QCommandLineOption({"r", "image-resolution"}, "Set output resolution for image export", "dpi"));
      parser.addOption(QCommandLineOption({"T", "trim-image"}, "Trim exported image with specified margin (in pixels)", "margin"));
      parser.addOption(QCommandLineOption({"x", "gui-scaling"}, "Set scaling factor for GUI elements", "factor"));
      parser.addOption(QCommandLineOption({"D", "monitor-resolution"}, "Specify monitor resolution", "dpi"));
      parser.addOption(QCommandLineOption({"S", "style"}, "Load style file", "style"));
      parser.addOption(QCommandLineOption({"p", "plugin"}, "Execute named plugin", "name"));
      parser.addOption(QCommandLineOption(      "template-mode", "Save template mode, no page size"));
      parser.addOption(QCommandLineOption({"F", "factory-settings"}, "Use factory settings"));
      parser.addOption(QCommandLineOption({"R", "revert-settings"}, "Revert to default preferences"));
      parser.addOption(QCommandLineOption({"i", "load-icons"}, "Load icons from INSTALLPATH/icons"));
      parser.addOption(QCommandLineOption({"j", "job"}, "process a conversion job", "file"));
      parser.addOption(QCommandLineOption({"e", "experimental"}, "Enable experimental features"));
      parser.addOption(QCommandLineOption({"c", "config-folder"}, "Override config/settings folder", "dir"));
      parser.addOption(QCommandLineOption({"t", "test-mode"}, "Set testMode flag for all files"));
      parser.addOption(QCommandLineOption({"M", "midi-operations"}, "Specify MIDI import operations file", "file"));
      parser.addOption(QCommandLineOption({"w", "no-webview"}, "No web view in start center"));
      parser.addOption(QCommandLineOption({"P", "export-score-parts"}, "Used with -o <file>.pdf, export score + parts"));
      parser.addOption(QCommandLineOption(      "no-fallback-font", "will not use Bravura as fallback musical font"));
      parser.addOption(QCommandLineOption({"f", "force"}, "Used with -o, ignore warnings reg. score being corrupted or from wrong version"));

      parser.addPositionalArgument("scorefiles", "The files to open", "[scorefile...]");

      parser.process(QCoreApplication::arguments());

    //if (parser.isSet("v")) parser.showVersion(); // a) needs Qt >= 5.4 , b) instead we use addVersionOption()
      if (parser.isSet("long-version")) {
            printVersion("MuseScore");
            return EXIT_SUCCESS;
            }
      MScore::debugMode = parser.isSet("d");
      MScore::noHorizontalStretch = MScore::noVerticalStretch = parser.isSet("L");
      noSeq = parser.isSet("s");
      noMidi = parser.isSet("m");
      if (parser.isSet("a")) {
            audioDriver = parser.value("a");
            if (audioDriver.isEmpty())
                  parser.showHelp(EXIT_FAILURE);
            }
      startWithNewScore = parser.isSet("n");
      externalIcons = parser.isSet("i");
      midiInputTrace = parser.isSet("I");
      midiOutputTrace = parser.isSet("O");
      MScore::useFallbackFont = !parser.isSet("no-fallback-font");

      if ((converterMode = parser.isSet("o"))) {
            MScore::noGui = true;
            outFileName = parser.value("o");
            if (outFileName.isEmpty())
                  parser.showHelp(EXIT_FAILURE);
            }
      if ((processJob = parser.isSet("j"))) {
            MScore::noGui = true;
            converterMode = true;
            jsonFileName = parser.value("j");
            if (jsonFileName.isEmpty()) {
                  fprintf(stderr, "json file name missing\n");
                  parser.showHelp(EXIT_FAILURE);
                  }
            }
      if ((pluginMode = parser.isSet("p"))) {
            MScore::noGui = true;
            pluginName = parser.value("p");
            if (pluginName.isEmpty())
                  parser.showHelp(EXIT_FAILURE);
            }
      MScore::saveTemplateMode = parser.isSet("template-mode");
      if (parser.isSet("r")) {
            QString temp = parser.value("r");
            if (temp.isEmpty())
                   parser.showHelp(EXIT_FAILURE);
            converterDpi = temp.toDouble();
            }
      if (parser.isSet("T")) {
            QString temp = parser.value("T");
            if (temp.isEmpty())
                   parser.showHelp(EXIT_FAILURE);
            bool ok = false;
            trimMargin = temp.toInt(&ok);
            if (!ok)
                  trimMargin = -1;
           }
      if (parser.isSet("x")) {
            QString temp = parser.value("x");
            if (temp.isEmpty())
                   parser.showHelp(EXIT_FAILURE);
            bool ok = false;
            guiScaling = temp.toDouble(&ok);
            if (!ok)
                  guiScaling = 0.0;
            }
      if (parser.isSet("D")) {
            QString temp = parser.value("D");
            if (temp.isEmpty())
                   parser.showHelp(EXIT_FAILURE);
            bool ok = 0.0;
            userDPI = temp.toDouble(&ok);
            if (!ok)
                  userDPI = 0.0;
            }
      if (parser.isSet("S")) {
            styleFile = parser.value("S");
            if (styleFile.isEmpty())
                  parser.showHelp(EXIT_FAILURE);
            }
      useFactorySettings = parser.isSet("F");
      deletePreferences = (useFactorySettings || parser.isSet("R"));
      enableExperimental = parser.isSet("e");
      if (parser.isSet("c")) {
            QString path = parser.value("c");
            if (path.isEmpty())
                  parser.showHelp(EXIT_FAILURE);
            QDir dir;
            if (dir.exists(path)) {
                  QSettings::setPath(QSettings::IniFormat, QSettings::UserScope, path);
                  QSettings::setPath(QSettings::IniFormat, QSettings::SystemScope, path);
                  dataPath = path;
                  }
            }
      enableTestMode = parser.isSet("t");
      if (parser.isSet("M")) {
            QString temp = parser.value("M");
            if (temp.isEmpty())
                  parser.showHelp(EXIT_FAILURE);
            preferences.midiImportOperations.setOperationsFile(temp);
            }
      noWebView = parser.isSet("w");
      exportScoreParts = parser.isSet("export-score-parts");
      if (exportScoreParts && !converterMode)
            parser.showHelp(EXIT_FAILURE);
      ignoreWarnings = parser.isSet("f");

      QStringList argv = parser.positionalArguments();

      mscoreGlobalShare = getSharePath();
      iconPath = externalIcons ? mscoreGlobalShare + QString("icons/") :  QString(":/data/icons/");

      if (!converterMode && !pluginMode) {
            if (!argv.isEmpty()) {
                  int ok = true;
                  for (const QString& message : argv) {
                        QFileInfo fi(message);
                        if (!app->sendMessage(fi.absoluteFilePath())) {
                              ok = false;
                              break;
                              }
                        }
                  if (ok)
                        return 0;
                  }
            else
                  if (app->sendMessage(QString("")))
                      return 0;
            }

      if (dataPath.isEmpty())
            dataPath = QStandardPaths::writableLocation(QStandardPaths::DataLocation);

      if (deletePreferences) {
            QDir(dataPath).removeRecursively();
            QSettings settings;
            QFile::remove(settings.fileName() + ".lock"); //forcibly remove lock
            QFile::remove(settings.fileName());
            settings.clear();
            }

      // create local plugin directory
      // if not already there:
      QDir().mkpath(dataPath + "/plugins");

      if (MScore::debugMode)
            qDebug("global share: <%s>", qPrintable(mscoreGlobalShare));

      // set translator before preferences are read to get
      //    translations for all shortcuts
      //
      if (useFactorySettings)
            localeName = "system";
      else {
            QSettings s;
            localeName = s.value("language", "system").toString();
            }

      setMscoreLocale(localeName);

      Shortcut::init();
      preferences.init();

      QNetworkProxyFactory::setUseSystemConfiguration(true);

      MScore::init();                                      // initialize libmscore
      if (!MScore::testMode) {
            QSizeF psf = QPrinter().paperSize(QPrinter::Inch);
            MScore::defaultStyle().set(StyleIdx::pageWidth, psf.width());
            MScore::defaultStyle().set(StyleIdx::pageHeight, psf.height());
            MScore::defaultStyle().set(StyleIdx::pagePrintableWidth, psf.width()-20.0/INCH);
            }

#ifdef SCRIPT_INTERFACE
      qmlRegisterType<QmlPlugin>  ("MuseScore", 1, 0, "MuseScore");
#endif
      if (MScore::debugMode) {
            qDebug("DPI %f", DPI);

            QScreen* screen      = QGuiApplication::primaryScreen();
            qDebug() << "Information for screen:" << screen->name();
            qDebug() << "  Available geometry:" << screen->availableGeometry().x() << screen->availableGeometry().y() << screen->availableGeometry().width() << "x" << screen->availableGeometry().height();
            qDebug() << "  Available size:" << screen->availableSize().width() << "x" << screen->availableSize().height();
            qDebug() << "  Available virtual geometry:" << screen->availableVirtualGeometry().x() << screen->availableVirtualGeometry().y() << screen->availableVirtualGeometry().width() << "x" << screen->availableVirtualGeometry().height();
            qDebug() << "  Available virtual size:" << screen->availableVirtualSize().width() << "x" << screen->availableVirtualSize().height();
            qDebug() << "  Depth:" << screen->depth() << "bits";
            qDebug() << "  Geometry:" << screen->geometry().x() << screen->geometry().y() << screen->geometry().width() << "x" << screen->geometry().height();
            qDebug() << "  Logical DPI:" << screen->logicalDotsPerInch();
            qDebug() << "  Logical DPI X:" << screen->logicalDotsPerInchX();
            qDebug() << "  Logical DPI Y:" << screen->logicalDotsPerInchY();
            qDebug() << "  Physical DPI:" << screen->physicalDotsPerInch();
            qDebug() << "  Physical DPI X:" << screen->physicalDotsPerInchX();
            qDebug() << "  Physical DPI Y:" << screen->physicalDotsPerInchY();
            qDebug() << "  Physical size:" << screen->physicalSize().width() << "x" << screen->physicalSize().height() << "mm";
            qDebug() << "  Refresh rate:" << screen->refreshRate() << "Hz";
            qDebug() << "  Size:" << screen->size().width() << "x" << screen->size().height();
            qDebug() << "  Virtual geometry:" << screen->virtualGeometry().x() << screen->virtualGeometry().y() << screen->virtualGeometry().width() << "x" << screen->virtualGeometry().height();
            qDebug() << "  Virtual size:" << screen->virtualSize().width() << "x" << screen->virtualSize().height();
            }

      if (!useFactorySettings)
            preferences.read();

      preferences.readDefaultStyle();

      if (converterDpi == 0)
            converterDpi = preferences.pngResolution;

      QSplashScreen* sc = 0;
      QTimer* stimer = 0;
      if (!MScore::noGui && preferences.showSplashScreen) {
            QPixmap pm(":/data/splash.png");
            sc = new QSplashScreen(pm);
            sc->setWindowTitle(QString("MuseScore Startup"));
#ifdef Q_OS_MAC // to have session dialog on top of splashscreen on mac
            sc->setWindowFlags(Qt::FramelessWindowHint);
#endif
            // show splash screen for 5 sec
            stimer = new QTimer(0);
            qApp->connect(stimer, SIGNAL(timeout()), sc, SLOT(close()));
            stimer->start(5000);
            sc->show();
            qApp->processEvents();
            }

      if (!converterMode && !pluginMode) {

            // set UI Theme
            if (preferences.isOxygen()) {
                  MgStyleConfigData::animationsEnabled = preferences.animations;
                  QApplication::setStyle(new MgStyle);
            } else
                  QApplication::setStyle(QStyleFactory::create("Fusion"));

            QString wd      = QString("%1/%2").arg(QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation)).arg(QCoreApplication::applicationName());
            // set UI Color Palette
            QPalette p(QApplication::palette());
            QString jsonPaletteFilename = "palette_light_fusion.json";
            if (preferences.isThemeDark())
                  jsonPaletteFilename = preferences.isOxygen() ? "palette_dark_oxygen.json" : "palette_dark_fusion.json";
            else
                  jsonPaletteFilename = preferences.isOxygen() ? "palette_light_oxygen.json" : "palette_light_fusion.json";
            QFile jsonPalette(QString(":/themes/%1").arg(jsonPaletteFilename));
            // read from Documents TODO: remove this
            if (QFile::exists(QString("%1/%2").arg(wd, "ms_palette.json")))
                  jsonPalette.setFileName(QString("%1/%2").arg(wd, "ms_palette.json"));
            if (jsonPalette.open(QFile::ReadOnly | QFile::Text)) {
                  QJsonDocument d = QJsonDocument::fromJson(jsonPalette.readAll());
                  QJsonObject o = d.object();
                  QMetaEnum metaEnum = QMetaEnum::fromType<QPalette::ColorRole>();
                  for (int i = 0; i < metaEnum.keyCount(); ++i) {
                        QJsonValue v = o.value(metaEnum.valueToKey(i));
                        if (!v.isUndefined())
                              p.setColor(static_cast<QPalette::ColorRole>(metaEnum.value(i)), QColor(v.toString()));
                        }
                  }
            QApplication::setPalette(p);

            // set UI Style
            QString css;
            QString styleFilename("style_light_fusion.css");
            if (preferences.isThemeDark())
                  styleFilename = preferences.isOxygen() ? "style_dark_oxygen.css" : "style_dark_fusion.css";
            else
                  styleFilename = preferences.isOxygen() ? "style_light_oxygen.css" : "style_light_fusion.css";
            QFile fstyle(QString(":/themes/%1").arg(styleFilename));
            // read from Documents TODO: remove this
            if (QFile::exists(QString("%1/%2").arg(wd, "ms_style.css")))
                  fstyle.setFileName(QString("%1/%2").arg(wd, "ms_style.css"));
            if (fstyle.open(QFile::ReadOnly | QFile::Text)) {
                  QTextStream in(&fstyle);
                  css = in.readAll();
                  }
            css.replace("$voice1-bgcolor", MScore::selectColor[0].name(QColor::HexRgb));
            css.replace("$voice2-bgcolor", MScore::selectColor[1].name(QColor::HexRgb));
            css.replace("$voice3-bgcolor", MScore::selectColor[2].name(QColor::HexRgb));
            css.replace("$voice4-bgcolor", MScore::selectColor[3].name(QColor::HexRgb));
            qApp->setStyleSheet(css);

            qApp->setAttribute(Qt::AA_UseHighDpiPixmaps);
            }
      else
            noSeq = true;

      genIcons();

      // Do not create sequencer and audio drivers if run with '-s'
      if (!noSeq) {
            seq            = new Seq();
            MScore::seq    = seq;
            Driver* driver = driverFactory(seq, audioDriver);
            synti          = synthesizerFactory();
            if (driver) {
                  MScore::sampleRate = driver->sampleRate();
                  synti->setSampleRate(MScore::sampleRate);
                  synti->init();

                  seq->setDriver(driver);
                  }
            else {
                  // Do not delete the sequencer If we can't load driver.
                  // Allow user to select the working driver later.
                  MScore::sampleRate = 44100;  // Would be changed when user changes driver
                  synti->setSampleRate(MScore::sampleRate);
                  synti->init();
                  }
            seq->setMasterSynthesizer(synti);
            }
      else {
            seq         = 0;
            MScore::seq = 0;
            }

      //
      // avoid font problems by overriding the environment
      //    fall back to "C" locale
      //

      //#ifndef Q_OS_WIN
      //setenv("LANG", "C", 1);
      //#endif
      //QLocale::setDefault(QLocale(QLocale::C));

      if (MScore::debugMode) {
            QStringList sl(QCoreApplication::libraryPaths());
            foreach(const QString& s, sl)
                  qDebug("LibraryPath: <%s>", qPrintable(s));
            }

      // rastral size of font is 20pt = 20/72 inch = 20*DPI/72 dots
      //   staff has 5 lines = 4 * _spatium
      //   _spatium    = SPATIUM20;     // 20.0 / 72.0 * DPI / 4.0;

      if (!MScore::noGui) {
#ifndef Q_OS_MAC
            qApp->setWindowIcon(*icons[int(Icons::window_ICON)]);
#endif
            Workspace::initWorkspace();
            }

      mscore = new MuseScore();

      // create a score for internal use
      gscore = new MasterScore();
      gscore->setMovements(new Movements());
      gscore->setStyle(MScore::baseStyle());

      gscore->style().set(StyleIdx::MusicalTextFont, QString("Bravura Text"));
      ScoreFont* scoreFont = ScoreFont::fontFactory("Bravura");
      gscore->setScoreFont(scoreFont);
      gscore->setNoteHeadWidth(scoreFont->width(SymId::noteheadBlack, gscore->spatium()) / SPATIUM20);

      if (!noSeq) {
            if (!seq->init())
                  qDebug("sequencer init failed");
            }

      //read languages list
      mscore->readLanguages(mscoreGlobalShare + "locale/languages.xml");

      QApplication::instance()->installEventFilter(mscore);

      mscore->setRevision(revision);

      int files = 0;
      bool restoredSession = false;
      if (MScore::noGui) {
#ifdef Q_OS_MAC
            // see issue #28706: Hangup in converter mode with MusicXML source
            qApp->processEvents();
#endif
            exit(processNonGui(argv) ? 0 : EXIT_FAILURE);
            }
      else {
            mscore->readSettings();
            QObject::connect(qApp, SIGNAL(messageReceived(const QString&)),
               mscore, SLOT(handleMessage(const QString&)));

            static_cast<QtSingleApplication*>(qApp)->setActivationWindow(mscore, false);
            // count filenames specified on the command line
            // these are the non-empty strings remaining in argv
            foreach(const QString& name, argv) {
                  if (!name.isEmpty())
                        ++files;
                  }
#ifdef Q_WS_MAC
            // app->paths contains files requested to be loaded by OS X
            // append these to argv and update file count
            foreach(const QString& name, app->paths) {
                  if (!name.isEmpty()) {
                        argv << name;
                        ++files;
                        }
                  }
#endif
            //
            // TODO: delete old session backups
            //
            restoredSession = mscore->restoreSession((preferences.sessionStart == SessionStart::LAST && (files == 0)));
            if (!restoredSession || files)
                  loadScores(argv);
            }
      errorMessage = new QErrorMessage(mscore);
      mscore->loadPlugins();
      mscore->writeSessionFile(false);

#ifdef Q_OS_MAC
      // there's a bug in Qt showing the toolbar unified after switching showFullScreen(), showMaximized(),
      // showNormal()...
      mscore->setUnifiedTitleAndToolBarOnMac(false);
#endif

      mscore->changeState(mscore->noScore() ? STATE_DISABLED : STATE_NORMAL);
      mscore->show();

      if (mscore->hasToCheckForUpdate())
            mscore->checkForUpdate();

      if (!scoresOnCommandline && preferences.showStartcenter && (!restoredSession || mscore->scores().size() == 0)) {
#ifdef Q_OS_MAC
// ugly, but on mac we get an event when a file is open.
// We can't get the event when the startcenter is shown.
// So we let the event loop run a bit before showing the start center.
            QTimer *timer = new QTimer();
            timer->setSingleShot(true);
            QObject::connect(timer, &QTimer::timeout, [=]() {
                  if (!scoresOnCommandline) {
                        getAction("startcenter")->setChecked(true);
                        mscore->showStartcenter(true);
                        }
                  timer->deleteLater();
                  } );
            timer->start(500);
#else

            getAction("startcenter")->setChecked(true);
            mscore->showStartcenter(true);
#endif
            }

      mscore->showPlayPanel(preferences.showPlayPanel);
      QSettings settings;
      if (settings.value("synthControlVisible", false).toBool())
            mscore->showSynthControl(true);
      if (settings.value("mixerVisible", false).toBool())
            mscore->showMixer(true);

      return qApp->exec();
      }

