//=========================================================
//  MusE
//  Linux Music Editor
//  $Id: app.cpp,v 1.4 2003/11/01 19:49:06 spamatica Exp $
//
//  (C) Copyright 1999/2000 Werner Schweer (ws@seh.de)
//=========================================================

#include "config.h"

#include <assert.h>
#include <getopt.h>
#include <errno.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <time.h>
#include <signal.h>
#include <stdarg.h>

#include <qbuttongroup.h>
#include <qpopupmenu.h>
#include <qmessagebox.h>
#include <qclipboard.h>
#include <qsocketnotifier.h>
#include <qtextcodec.h>
#include <qstylefactory.h>
#include <qmenubar.h>
#include <qapplication.h>

#include "app.h"
#include "transport.h"
#include "bigtime.h"
#include "arranger.h"
#include "pianoroll.h"
#include "score.h"
#include "xml.h"
#include "midi.h"
#include "conf.h"
#include "listedit.h"
#include "master/masteredit.h"
#include "master/lmaster.h"
#include "drumedit.h"
#include "ttoolbar.h"
#include "mixer.h"
#include "mixer/amixer.h"
#include "cliplist/cliplist.h"
#include "midiport.h"
#include "audiodev.h"
#include "driver/mididev.h"
#include "waveedit.h"
#include "icons.h"
#include "minstrument.h"
#include "mixdowndialog.h"
#include "midictrl.h"
#include "midithread.h"

#include "filedialog.h"
#include "plugins/plugin.h"
#include "marker/markerview.h"
#include "transpose.h"
#include "appearance.h"
#include "seq.h"
#include "gatetime.h"
#include "metronome.h"
#include "debug.h"
#include "event.h"
#include "driver/jackaudio.h"
#include "driver/alsaaudio.h"
#include "audio.h"
#include "wave.h"

extern void initIcons();

const char* fileOpenText =
      QT_TR_NOOP("Click this button to open a <em>new song</em>.<br>"
      "You can also select the <b>Open command</b> from the File menu.");
const char* fileSaveText =
      QT_TR_NOOP("Click this button to save the song you are "
      "editing.  You will be prompted for a file name.\n"
      "You can also select the Save command from the File menu.");
const char* fileNewText        = QT_TR_NOOP("Create New Song");

const char* infoLoopButton     = QT_TR_NOOP("loop between left mark and right mark");
const char* infoPunchinButton  = QT_TR_NOOP("record starts at left mark");
const char* infoPunchoutButton = QT_TR_NOOP("record stops at right mark");
const char* infoStartButton    = QT_TR_NOOP("rewind to start position");
const char* infoRewindButton   = QT_TR_NOOP("rewind current position");
const char* infoForwardButton  = QT_TR_NOOP("move current position");
const char* infoStopButton     = QT_TR_NOOP("stop sequencer");
const char* infoPlayButton     = QT_TR_NOOP("start sequencer play");
const char* infoRecordButton   = QT_TR_NOOP("to record press record and then play");
const char* infoPanicButton    = QT_TR_NOOP("send note off to all midi channels");

#define PROJECT_LIST_LEN  6
QString* projectList[PROJECT_LIST_LEN];

bool configLoaded = false;

extern void initMidiSynth();
extern void initAlsaAudio();

#include <alsa/asoundlib.h>

extern void initMidiSynth();

#ifdef HAVE_LADCCA
#include <ladcca/ladcca.h>
cca_client_t * cca_client;
extern snd_seq_t * alsaSeq;
#endif /* HAVE_LADCCA */

//---------------------------------------------------------
//   error_handler
//---------------------------------------------------------

static void error_handler(const char *file, int line, const char *function, int err, const char *fmt, ...)
      {
      va_list arg;

      if (err == ENOENT) {      // Ignore those misleading "warnings"
            return;
            }
      va_start(arg, fmt);
      fprintf(stderr, "ALSA lib %s:%i:(%s) ", file, line, function);
      vfprintf(stderr, fmt, arg);
      if (err)
            fprintf(stderr, ": %s", snd_strerror(err));
      putc('\n', stderr);
      va_end(arg);
      }

#if HAVE_JACK
extern bool initJackAudio();
extern void exitJackAudio();
#endif

//---------------------------------------------------------
//   addProject
//---------------------------------------------------------

static void addProject(const QString& name)
      {
      for (int i = 0; i < PROJECT_LIST_LEN; ++i) {
            if (projectList[i] == 0)
                  break;
            if (name == *projectList[i]) {
                  int dst = i;
                  int src = i+1;
                  int n = PROJECT_LIST_LEN - i - 1;
                  delete projectList[i];
                  for (int k = 0; k < n; ++k)
                        projectList[dst++] = projectList[src++];
                  projectList[dst] = 0;
                  break;
                  }
            }
      QString** s = &projectList[PROJECT_LIST_LEN - 2];
      QString** d = &projectList[PROJECT_LIST_LEN - 1];
      if (*d)
            delete *d;
      for (int i = 0; i < PROJECT_LIST_LEN-1; ++i)
            *d-- = *s--;
      projectList[0] = new QString(name);
      }

//---------------------------------------------------------
//   MusE
//---------------------------------------------------------

MusE::MusE(int argc, char** argv) : QMainWindow(0, "mainwindow")
      {
      muse                  = this;    // hack
      midiMixer             = 0;
      clipListEdit          = 0;
      midiSyncConfig        = 0;
      midiRemoteConfig      = 0;
      midiPortConfig        = 0;
      printerConfig         = 0;
      metronomeConfig       = 0;
      audioConfig           = 0;
      midiFileConfig        = 0;
      midiFilterConfig      = 0;
      midiInputTransform    = 0;
      midiRhythmGenerator   = 0;
      globalSettingsConfig  = 0;
      markerView            = 0;
      softSynthesizerConfig = 0;
      midiTransformerDialog = 0;
      printerType           = 1;        // print to file
      printerFile           = QString("mops.ps");
      printerCommand        = QString("lpr -");
      previewCommand        = QString("gv %s&");
      appName               = QString("MusE");

      configName            = QString(getenv("HOME"));
      configName           += QString("/.MusE");
      configTheme	          = QString("default");
      _currentTheme	    = configTheme;

      song = new Song("song");

      //---------------------------------------------------
      //    undo/redo
      //---------------------------------------------------

      undoRedo = new QActionGroup(this, tr("UndoRedo"), false);
      undoAction = new QAction(tr("undo"), QIconSet(*undoIconS, *undoIcon), tr("Und&o"),
        CTRL+Key_Z, undoRedo, "undo");
      redoAction = new QAction(tr("redo"), QIconSet(*redoIconS, *redoIcon), tr("Re&do"),
        CTRL+Key_Y, undoRedo, "redo");
      undoAction->setWhatsThis(tr("undo last change to song"));
      redoAction->setWhatsThis(tr("redo last undo"));
      undoAction->setEnabled(false);
      redoAction->setEnabled(false);
      connect(redoAction, SIGNAL(activated()), song, SLOT(redo()));
      connect(undoAction, SIGNAL(activated()), song, SLOT(undo()));

      //---------------------------------------------------
      //    Transport
      //---------------------------------------------------

      transportAction = new QActionGroup(this, tr("Transport"), false);

      loopAction = new QAction(tr("loop"), QIconSet(*loop1Icon),
         tr("Loop"), 0, transportAction, "loop", true);
      loopAction->setWhatsThis(tr(infoLoopButton));
      connect(loopAction, SIGNAL(toggled(bool)), song, SLOT(setLoop(bool)));

      punchinAction = new QAction(tr("punchin"), QIconSet(*punchin1Icon),
         tr("Punchin"), 0, transportAction, "Punchin", true);
      punchinAction->setWhatsThis(tr(infoPunchinButton));
      connect(punchinAction, SIGNAL(toggled(bool)), song, SLOT(setPunchin(bool)));

      punchoutAction = new QAction(tr("punchout"), QIconSet(*punchout1Icon),
         tr("Punchout"), 0, transportAction, "punchout", true);
      punchoutAction->setWhatsThis(tr(infoPunchoutButton));
      connect(punchoutAction, SIGNAL(toggled(bool)), song, SLOT(setPunchout(bool)));

      transportAction->addSeparator();

      startAction = new QAction(tr("start"), QIconSet(*startIcon),
         tr("Start"), 0, transportAction, "start");
      startAction->setWhatsThis(tr(infoStartButton));
      connect(startAction, SIGNAL(activated()), song, SLOT(rewindStart()));

      rewindAction = new QAction(tr("rewind"), QIconSet(*frewindIcon),
         tr("Rewind"), 0, transportAction, "rewind");
      rewindAction->setWhatsThis(tr(infoRewindButton));
      connect(rewindAction, SIGNAL(activated()), song, SLOT(rewind()));

      forwardAction = new QAction(tr("forward"), QIconSet(*fforwardIcon),
         tr("Forward"), 0, transportAction, "forward");
      forwardAction->setWhatsThis(tr(infoForwardButton));
      connect(forwardAction, SIGNAL(activated()), song, SLOT(forward()));

      stopAction = new QAction(tr("stop"), QIconSet(*stopIcon),
         tr("Stop"), 0, transportAction, "stop", true);
      stopAction->setWhatsThis(tr(infoStopButton));
      stopAction->setOn(!song->play());
      connect(stopAction, SIGNAL(toggled(bool)), song, SLOT(setStop(bool)));

      playAction = new QAction(tr("play"),  QIconSet(*playIcon),
         tr("Play"), 0, transportAction, "play", true);
      playAction->setWhatsThis(tr(infoPlayButton));
      playAction->setOn(song->play());
      connect(playAction, SIGNAL(toggled(bool)), song, SLOT(setPlay(bool)));

      recordAction = new QAction(tr("record"),  QIconSet(*recordIcon),
         tr("Record"), 0, transportAction, "record", true);
      recordAction->setWhatsThis(tr(infoRecordButton));
      connect(recordAction, SIGNAL(toggled(bool)), song, SLOT(setRecord(bool)));

      panicAction = new QAction(tr("panic"),  QIconSet(*panicIcon),
         tr("Panic"), 0, 0, "panic", false);
      panicAction->setWhatsThis(tr(infoPanicButton));
      connect(panicAction, SIGNAL(activated()), song, SLOT(panic()));

      initMidiInstruments();
      initMidiPorts();
      initMidiDevices();
      configGeometryMain.setHeight(300);
      //_fontSize = qApp->font().pixelSize();

      //----Actions

      QAction* fileNewAction = new QAction(tr("new"),
        QIconSet(*filenewIconS, *filenewIcon), tr("&New"), CTRL+Key_N, this, "new");
      fileNewAction->setToolTip(tr(fileNewText));
      fileNewAction->setWhatsThis(tr(fileNewText));

      QAction* fileOpenAction = new QAction(tr("open"),
        QIconSet(*openIconS, *openIcon), tr("&Open"), CTRL+Key_O, this, "open");
      fileOpenAction->setToolTip(tr(fileOpenText));
      fileOpenAction->setWhatsThis(tr(fileOpenText));

      QAction* fileSaveAction = new QAction(tr("save"),
        QIconSet(*saveIconS, *saveIcon), tr("&Save"), CTRL+Key_S, this, "save");
      fileSaveAction->setToolTip(tr(fileSaveText));
      fileSaveAction->setWhatsThis(tr(fileSaveText));

      QAction* pianoAction = new QAction(tr("pianoroll"),
        *pianoIconSet, tr("Pianoroll"), CTRL+Key_E, this, "pianoroll");
      connect(pianoAction, SIGNAL(activated()), SLOT(startPianoroll()));

      QAction* scoreAction = new QAction(tr("score"),
        *scoreIconSet, tr("Score"), CTRL+Key_R, this, "score");
      connect(scoreAction, SIGNAL(activated()), SLOT(startScoreEditor()));

      QAction* markerAction = new QAction(tr("marker"), QIconSet(*flagIcon), tr("Marker"),
        0, this, "marker");
      connect(markerAction, SIGNAL(activated()), SLOT(startMarkerView()));

      connect(fileNewAction,  SIGNAL(activated()), SLOT(newSong()));
      connect(fileOpenAction, SIGNAL(activated()), SLOT(loadProject()));
      connect(fileSaveAction, SIGNAL(activated()), SLOT(save()));

      //--------------------------------------------------
      //    Toolbar
      //--------------------------------------------------

      tools = new QToolBar(tr("File Buttons"), this);
      fileNewAction->addTo(tools);
      fileOpenAction->addTo(tools);
      fileSaveAction->addTo(tools);

      //
      //    Whats This
      //
      QWhatsThis::whatsThisButton(tools);

      tools->addSeparator();
      undoRedo->addTo(tools);

      QToolBar* tools1 = new EditToolBar(this, arrangerTools);

      QToolBar* transportToolbar = new QToolBar(this);
      transportAction->addTo(transportToolbar);

      QToolBar* panicToolbar = new QToolBar(this);
      panicAction->addTo(panicToolbar);

      seq = new Sequencer();

      //---------------------------------------------------
      //    Popups
      //---------------------------------------------------
      //-------------------------------------------------------------
      //    popup File
      //-------------------------------------------------------------

      menu_file = new QPopupMenu(this);
      menuBar()->insertItem(tr("&File"), menu_file);
      fileNewAction->addTo(menu_file);
      fileOpenAction->addTo(menu_file);
      openRecent = new QPopupMenu(this);
      connect(openRecent, SIGNAL(aboutToShow()), this, SLOT(openRecentMenu()));
      connect(openRecent, SIGNAL(activated(int)), this, SLOT(selectProject(int)));
      menu_file->insertItem(tr("Open &Recent"), openRecent, CTRL+Key_S);
      menu_file->insertSeparator();
      fileSaveAction->addTo(menu_file);
      menu_file->insertItem(tr("Save &As"), this, SLOT(saveAs()), CTRL+Key_A, -2);
      menu_file->insertSeparator();
      menu_file->insertItem(*printIconS, tr("Config &Printer"), this, SLOT(configPrinter()), CTRL+Key_P, -2);
      menu_file->insertSeparator();
      menu_file->insertItem(*openIconS, tr("Import Midifile"), this, SLOT(importMidi()), 0, -2);
      menu_file->insertItem(*saveIconS, tr("Export Midifile"), this, SLOT(exportMidi()), 0, -2);
      menu_file->insertSeparator();
      menu_file->insertItem(*openIconS, tr("Import Wave File"), this, SLOT(importWave()), 0, -2);
      menu_file->insertSeparator();
      menu_file->insertItem(*exitIconS, tr("&Quit"), this, SLOT(quitDoc()), CTRL+Key_Q, -2);
      menu_file->insertSeparator();

      //-------------------------------------------------------------
      //    popup Edit
      //-------------------------------------------------------------

      menuEdit = new QPopupMenu(this);
      undoRedo->addTo(menuEdit);
      menuEdit->insertSeparator();
      menuBar()->insertItem(tr("&Edit"), menuEdit);

      menuEdit->insertItem(*editcutIconSet, tr("C&ut"),   CMD_CUT);
      menuEdit->setAccel(CTRL+Key_X, CMD_CUT);
      menuEdit->insertItem(*editcopyIconSet, tr("&Copy"),  CMD_COPY);
      menuEdit->setAccel(CTRL+Key_C, CMD_COPY);
      menuEdit->insertItem(*editpasteIconSet, tr("&Paste"), CMD_PASTE);
      menuEdit->setAccel(CTRL+Key_V, CMD_PASTE);
      menuEdit->insertSeparator();
      menuEdit->insertItem(tr("Delete Track"), CMD_DELETE_TRACK);
      menuEdit->setAccel(Key_Delete, CMD_DELETE);
//      menuEdit->setAccel(Key_Backspace, CMD_DELETE);
      menuEdit->insertItem(tr("Add Track"), CMD_ADD_TRACK);
      menuEdit->setAccel(CTRL+Key_J, CMD_ADD_TRACK);
      QPopupMenu* select = new QPopupMenu(this);
      select->insertItem(tr("Select &All"),  CMD_SELECT_ALL);
//used by "undo"      select->setAccel(CTRL+Key_Z, CMD_SELECT_ALL);
      select->insertItem(tr("&Deselect All"), CMD_SELECT_NONE);
      select->setAccel(CTRL+Key_B, CMD_SELECT_NONE);
      menuEdit->insertSeparator();
      select->insertItem(tr("Invert &Selection"), CMD_SELECT_INVERT);
      select->insertItem(tr("&Inside Loop"), CMD_SELECT_ILOOP);
      select->insertItem(tr("&Outside Loop"), CMD_SELECT_OLOOP);
      select->insertItem(tr("All &Parts on Track"), CMD_SELECT_PARTS);
      menuEdit->insertItem(tr("Select"), select);
      menuEdit->insertSeparator();
      pianoAction->addTo(menuEdit);
      scoreAction->addTo(menuEdit);
      menuEdit->insertItem(tr("Drums"), this, SLOT(startDrumEditor()), CTRL+Key_D);
      menuEdit->insertItem(tr("List"), this, SLOT(startListEditor()), CTRL+Key_L);

      QPopupMenu* master = new QPopupMenu(this);
      master->setCheckable(false);
      master->insertItem(tr("Graphic"), this, SLOT(startMasterEditor()), CTRL+Key_M);
      master->insertItem(tr("List"), this, SLOT(startLMasterEditor()), SHIFT+CTRL+Key_M);
      menuEdit->insertItem(tr("Mastertrack"), master, Key_F);

      markerAction->addTo(menuEdit);
      connect(menuEdit, SIGNAL(activated(int)), SLOT(cmd(int)));
      connect(select, SIGNAL(activated(int)), SLOT(cmd(int)));

      menuEdit->insertItem(tr("Midi &Transform"), this, SLOT(startMidiTransformer()), CTRL+Key_T);
      QPopupMenu* midiEdit = new QPopupMenu(this);
      midiEdit->setCheckable(false);
#if 0  // TODO
      midiEdit->insertItem(tr("Modify Gate Time"), this, SLOT(modifyGateTime()));
      midiEdit->insertItem(tr("Modify Velocity"),  this, SLOT(modifyVelocity()));
      midiEdit->insertItem(tr("Crescendo"),        this, SLOT(crescendo()));
      midiEdit->insertItem(tr("Transpose"),        this, SLOT(transpose()));
      midiEdit->insertItem(tr("Thin Out"),         this, SLOT(thinOut()));
      midiEdit->insertItem(tr("Erase Event"),      this, SLOT(eraseEvent()));
      midiEdit->insertItem(tr("Note Shift"),       this, SLOT(noteShift()));
      midiEdit->insertItem(tr("Move Clock"),       this, SLOT(moveClock()));
      midiEdit->insertItem(tr("Copy Measure"),     this, SLOT(copyMeasure()));
      midiEdit->insertItem(tr("Erase Measure"),    this, SLOT(eraseMeasure()));
      midiEdit->insertItem(tr("Delete Measure"),   this, SLOT(deleteMeasure()));
      midiEdit->insertItem(tr("Create Measure"),   this, SLOT(createMeasure()));
      midiEdit->insertItem(tr("Mix Track"),        this, SLOT(mixTrack()));
#endif
      midiEdit->insertItem(tr("Transpose"),        this, SLOT(transpose()));
      menuEdit->insertItem(tr("Midi"), midiEdit);

      //-------------------------------------------------------------
      //    popup Structure
      //-------------------------------------------------------------

      menuStructure = new QPopupMenu(this);
      menuStructure->setCheckable(false);
      menuBar()->insertItem(tr("&Structure"), menuStructure);
      menuStructure->insertItem(tr("Global Cut"),    this, SLOT(globalCut()));
      menuStructure->insertItem(tr("Global Insert"), this, SLOT(globalInsert()));
      menuStructure->insertItem(tr("Global Split"),  this, SLOT(globalSplit()));
      menuStructure->insertItem(tr("Copy Range"),    this, SLOT(copyRange()));
      menuStructure->insertSeparator();
      menuStructure->insertItem(tr("Cut Events"),    this, SLOT(cutEvents()));

      //-------------------------------------------------------------
      //    popup Display
      //-------------------------------------------------------------

      menu_display = new QPopupMenu(this);
      menu_display->setCheckable(true);
      menuBar()->insertItem(tr("&Display"), menu_display);

      tr_id = menu_display->insertItem(tr("Transport Panel"), this, SLOT(toggleTransport()), Key_F11);
      bt_id = menu_display->insertItem(tr("Bigtime window"), this, SLOT(toggleBigTime()), Key_F12);

      //-------------------------------------------------------------
      //    popup Config
      //-------------------------------------------------------------

      follow = new QPopupMenu(this);
      follow->setCheckable(false);
      fid0 = follow->insertItem(tr("dont follow Song"), CMD_FOLLOW_NO);
      fid1 = follow->insertItem(tr("follow page"), CMD_FOLLOW_JUMP);
      fid2 = follow->insertItem(tr("follow continuous"), CMD_FOLLOW_CONTINUOUS);
      follow->setItemChecked(fid1, true);
      connect(follow, SIGNAL(activated(int)), SLOT(cmd(int)));

      menu_config = new QPopupMenu(this);
      menu_config->setCheckable(false);
      menuBar()->insertItem(tr("&Config"), menu_config);
      menu_config->insertItem(tr("Global Settings"), this, SLOT(configGlobalSettings()));
      menu_config->insertItem(tr("follow song"), follow, Key_F);
      menu_config->insertItem(tr("Metronome"), this, SLOT(configMetronome()));
      menu_config->insertSeparator();
      menu_config->insertItem(tr("Midi Sync"), this, SLOT(configMidiSync()));
      menu_config->insertItem(tr("Midi File Config"), this, SLOT(configMidiFile()));
      menu_config->insertSeparator();
      menu_config->insertItem(tr("Appearance settings"), this, SLOT(configAppearance()));
      menu_config->insertSeparator();
      menu_config->insertItem(tr("Midi Ports / Soft Synth"), this, SLOT(configMidiPorts()));
      menu_config->insertSeparator();
      menu_config->insertItem(tr("Audio System"), this, SLOT(configAudioPorts()));
      menu_config->insertSeparator();
      menu_config->insertItem(tr("Save Configuration"), this, SLOT(writeConfiguration()));

      //-------------------------------------------------------------
      //    popup Midi
      //-------------------------------------------------------------

      midiInputPlugins = new QPopupMenu(this);
      midiInputPlugins->setCheckable(false);
      mpid0 = midiInputPlugins->insertItem(tr("Transpose"), 0);
      mpid1 = midiInputPlugins->insertItem(tr("Midi Input Transform"), 1);
      mpid2 = midiInputPlugins->insertItem(tr("Midi Input Filter"), 2);
      mpid3 = midiInputPlugins->insertItem(tr("Midi Remote Control"), 3);
      mpid4 = midiInputPlugins->insertItem(tr("Random Rhythm Generator"), 4);
      connect(midiInputPlugins, SIGNAL(activated(int)), SLOT(startMidiInputPlugin(int)));

      menu_functions = new QPopupMenu(this);
      menu_functions->setCheckable(true);
      menuBar()->insertItem(tr("&Midi"), menu_functions);
      menu_functions->setCaption(tr("Midi"));
      midid1 = menu_functions->insertItem(tr("Mixer"), this, SLOT(startMidiMixer()));
      menu_functions->insertItem(tr("Define Controller"), this, SLOT(startDefineController()));
      menu_functions->insertItem(tr("Input Plugins"), midiInputPlugins, Key_P);
      menu_functions->insertSeparator();
      menu_functions->insertItem(tr("Reset Instr."), midiThread, SLOT(resetDevices()));
      menu_functions->insertItem(tr("Init Instr."), midiThread, SLOT(msgInitDevices()));
      menu_functions->insertItem(tr("local off"), midiThread, SLOT(localOff()));

      //-------------------------------------------------------------
      //    popup Audio
      //-------------------------------------------------------------

      menu_audio = new QPopupMenu(this);
      menu_audio->setCheckable(true);
      menuBar()->insertItem(tr("&Audio"), menu_audio);
      aid1  = menu_audio->insertItem(tr("Mixer"), this, SLOT(startAudioMixer()));
      aid2 = menu_audio->insertItem(tr("Cliplist"), this, SLOT(startClipList()));
      QPopupMenu* menuBounce = new QPopupMenu(this);
      menuBounce->setCheckable(false);
      menuBounce->insertItem(tr("Bounce to Track"), this, SLOT(bounceToTrack()));
      menuBounce->insertItem(tr("Bounce to File"), this, SLOT(bounceToFile()));
      menu_audio->insertItem(tr("Bounce"), menuBounce);

      //---------------------------------------------------
      //    popup Help
      //---------------------------------------------------

      menuBar()->insertSeparator();
      menu_help = new QPopupMenu(this);
      menu_help->setCheckable(false);
      menuBar()->insertItem(tr("&Help"), menu_help);

      menu_help->insertItem(tr("Browser"), this, SLOT(startHelpBrowser()), Key_F1);
      menu_help->insertItem(tr("&About"), this, SLOT(about()));
      menu_help->insertItem(tr("About&Qt"), this, SLOT(aboutQt()));
      menu_help->insertSeparator();
      menu_help->insertItem(tr("What's &This"), this, SLOT(whatsThis()), SHIFT+Key_F1);

      //---------------------------------------------------
      //    Central Widget
      //---------------------------------------------------

      arranger = new Arranger(this, "arranger");
      setCentralWidget(arranger);

      connect(tools1, SIGNAL(toolChanged(int)), arranger, SLOT(setTool(int)));
      connect(arranger, SIGNAL(editPart(Track*)), SLOT(startEditor(Track*)));
      connect(arranger, SIGNAL(dropFile(const QString&)), SLOT(loadProjectFile(const QString&)));
      connect(arranger, SIGNAL(startEditor(PartList*,int)),  SLOT(startEditor(PartList*,int)));
      connect(arranger, SIGNAL(toolChanged(int)), tools1, SLOT(set(int)));

      transport = new Transport(this, "transport");
      bigtime   = new BigTime(0, this);
      connect(song, SIGNAL(posChanged(int, int, bool)), bigtime, SLOT(setPos(int, int, bool)));

      //---------------------------------------------------
      //  read list of "Recent Projects"
      //---------------------------------------------------

      QString prjPath(getenv("HOME"));
      prjPath += QString("/.musePrj");
      FILE* f = fopen(prjPath.latin1(), "r");
      if (f == 0) {
            perror("open projectfile");
            for (int i = 0; i < PROJECT_LIST_LEN; ++i)
                  projectList[i] = 0;
            }
      else {
            for (int i = 0; i < PROJECT_LIST_LEN; ++i) {
                  char buffer[256];
                  if (fgets(buffer, 256, f)) {
                        int n = strlen(buffer);
                        if (n && buffer[n-1] == '\n')
                              buffer[n-1] = 0;
                        projectList[i] = *buffer ? new QString(buffer) : 0;
                        }
                  else
                        break;
                  }
            fclose(f);
            }

      if (!noAudio) {
#if HAVE_JACK
            noJack = initJackAudio();
#endif
            snd_lib_error_set_handler(error_handler);
            }

      if (noAudio || (noAlsa && noJack)) {
            noAudio = true;
            }
      else {
            noAudio = false;
            initMidiSynth();
            }

      //---------------------------------------------------
      //  load project
      //---------------------------------------------------

      newSongFlag = argc >= 2;

      QString name;
      if (argc < 2) {
            name = projectList[0] ? *projectList[0] : QString("");
            }
      else
            name = argv[0];

      // set defaults:
      configTransportHandleColor   = transport->getHandleColor();
      configActivityColor          = arranger->getActivityColor();
      configActivityMode           = arranger->getActivityMode();
//      configSelectedTrackColor     = arranger->getSelectedTrackColor();
      configTransportVisible       = false;
      configBigTimeVisible         = false;
      configBigTimeForegroundColor = bigtime->getFgColor();
      configBigTimeBackgroundColor = bigtime->getBgColor();

      QClipboard* cb = QApplication::clipboard();
      connect(cb, SIGNAL(dataChanged()), SLOT(clipboardChanged()));
      connect(cb, SIGNAL(selectionChanged()), SLOT(clipboardChanged()));
      connect(arranger, SIGNAL(selectionChanged()), SLOT(selectionChanged()));

      loadProjectFile(name);
      }

//---------------------------------------------------------
//   loadProjectFile
//    load *.med, *.mid, *.kar
//---------------------------------------------------------

void MusE::loadProjectFile(const QString& name)
      {
      bool restartSequencer = seq->isRunning();
      if (restartSequencer) {
            if (song->play()) {
                  midiThread->msgPlay(false);
                  while (song->play())
                        qApp->processEvents();
                  }
            seq->stop();
            }
      loadProjectFile1(name);
      if (!configLoaded) {
            // open default devices:
            if (midiPorts[0].device() == 0) {
                  if (!midiDevices.empty()) {
                        MidiDevice* dev = midiDevices.front();
                        midiThread->setMidiDevice(&midiPorts[0], dev);
                        }
                  }
            }
      if (noJack && noAlsa) {
            useAlsaAudio = false;
            useJackAudio = false;
            audioTimebase = false;
            noAudio = true;
            printf("===noAudio====\n");
            }
      else {
#if HAVE_JACK
            if ((useJackAudio && !noJack) || (useAlsaAudio && noAlsa)) {
                  audioDevice = jackAudioDevice;
                  useJackAudio = true;
                  useAlsaAudio = false;
                  }
            if (useJackAudio && noJack) {
                  useAlsaAudio = true;
                  useJackAudio = false;
                  }
#endif
            if (useAlsaAudio && !noAlsa)
                  audioDevice = alsaAudioDevice;
            }

      enableAudio(!noAudio);

      if (_currentTheme != configTheme)
            loadTheme(configTheme);

      menu_display->setItemChecked(tr_id, configTransportVisible);
      menu_display->setItemChecked(bt_id, configBigTimeVisible);
      if (configTransportVisible)
            transport->show();
      if (configBigTimeVisible)
            bigtime->show();

      setGeometry(configGeometryMain);
      transport->setGeometry(configGeometryTransport);
      transport->setMasterFlag(song->masterFlag());
      transport->setHandleColor(configTransportHandleColor);

      if (configGeometryBigTime.width())
            bigtime->setGeometry(configGeometryBigTime);
      bigtime->setFgColor(configBigTimeForegroundColor);
      bigtime->setBgColor(configBigTimeBackgroundColor);

      arranger->setActivityColor(configActivityColor);
      arranger->setActivityMode(configActivityMode);
//      arranger->setSelectedTrackColor(configSelectedTrackColor);

      punchinAction->setOn(song->punchin());
      punchoutAction->setOn(song->punchout());
      loopAction->setOn(song->loop());
      song->updatePos();

      seq->start();
      if (audioMixer)
            audioMixer->updateMixer();
      song->update();
      clipboardChanged(); // enable/disable "Paste"
      selectionChanged(); // enable/disable "Copy" & "Paste"
      }

//---------------------------------------------------------
//   loadProjectFile1
//    only called from loadProjectFile()
//---------------------------------------------------------

void MusE::loadProjectFile1(const QString& name)
      {
      if (clearSong())
            return;
      // reset midi ports to "unknown state"
      for (int i = 0; i < MIDI_PORTS; ++i) {
            for (int k = 0; k < MIDI_CHANNELS; ++k) {
                  midiPorts[i].resetIstate(k, false);
                  }
            }
      song->removeSoftSynth();

      if (name.isEmpty()) {
            readConfiguration();
            setCaption(QString("MusE: Song: untitled"));
            newSongFlag = true;
            return;
            }
      museProject = QFileInfo(name).dirPath(true);

      project.setFile(name);
      QString ex = project.extension(true).lower();
      if (ex.length() == 3)
            ex += ".";
      ex = ex.left(4);
      if (ex.isEmpty() || ex == "med.") {
            load();
            }
      else if (ex == "mid." || ex == "kar.") {
            if (!configLoaded)
                  readConfiguration();
            importMidi(name);
            }
      else
            QMessageBox::critical(this, QString("MusE"),
               tr("Unknown File Format"));
      }

//---------------------------------------------------------
//   load
//    load "project"
//---------------------------------------------------------

void MusE::load()
      {
      bool popenFlag;
      FILE* f = fileOpen(this, project.filePath(), QString(".med"), "r", popenFlag, newSongFlag);
      if (f == 0) {
            readConfiguration();
            if (newSongFlag) {
                  newSongFlag = false;
                  song->dirty = true;
                  addProject(project.absFilePath());
                  song->update();
                  setCaption(QString("MusE: Song: ") + project.baseName());
                  }
            return;
            }
      newSongFlag = false;
       Xml xml(f);
      read(xml);
      bool fileError = ferror(f);
      popenFlag ? pclose(f) : fclose(f);
      if (fileError) {
            song->setName(tr("none"));
            readConfiguration();
            newSongFlag = true;
            return;
            }
      addProject(project.absFilePath());
      setCaption(QString("MusE: Song: ") + project.baseName());
      setFollow();
      }

//---------------------------------------------------------
//   setFollow
//---------------------------------------------------------

void MusE::setFollow()
      {
      Song::FollowMode fm = song->follow();
      follow->setItemChecked(fid0, fm == Song::NO);
      follow->setItemChecked(fid1, fm == Song::JUMP);
      follow->setItemChecked(fid2, fm == Song::CONTINUOUS);
      }

//---------------------------------------------------------
//   setTransportHandleColor
//---------------------------------------------------------

void MusE::setTransportHandleColor(QColor c)
      {
      if (transport)
	      transport->setHandleColor(c);
      }


//---------------------------------------------------------
//   setBigTimeForegroundColor
//---------------------------------------------------------

void MusE::setBigTimeForegroundColor(QColor c)
      {
      if (bigtime)
	      bigtime->setFgColor(c);
      }

//---------------------------------------------------------
//   setBigTimeBackgroundColor
//---------------------------------------------------------

void MusE::setBigTimeBackgroundColor(QColor c)
      {
      if (bigtime)
	      bigtime->setBgColor(c);
      }

//---------------------------------------------------------
//   getTransportHandleColor
//---------------------------------------------------------

QColor MusE::getTransportHandleColor()
      {
      if (transport)
	      return(transport->getHandleColor());

      return QColor(0, 0, 0xff);
      }

//---------------------------------------------------------
//   getBigTimeForegroundColor
//---------------------------------------------------------

QColor MusE::getBigTimeForegroundColor()
      {
      if (bigtime)
	      return(bigtime->getFgColor());

      return QColor(0xff, 0, 0);
      }

//---------------------------------------------------------
//   getBigTimeBackgroundColor
//---------------------------------------------------------

QColor MusE::getBigTimeBackgroundColor()
      {
      if (bigtime)
	      return(bigtime->getBgColor());

      return QColor(0, 0, 0);
      }

//---------------------------------------------------------
//   MusE::~MusE
//---------------------------------------------------------

MusE::~MusE()
      {
      }

//---------------------------------------------------------
//   newSong
//---------------------------------------------------------

void MusE::newSong()
      {
      loadProjectFile(QString());
      }

//---------------------------------------------------------
//   MusE::loadProject
//---------------------------------------------------------

void MusE::loadProject()
      {
      QString fn = getOpenFileName(QString(""), med_file_pattern, this,
         tr("MusE: load project"));
      if (!fn.isEmpty()) {
            museProject = QFileInfo(fn).dirPath(true);
            printf("MusE::loadProject - setting museProject to %s\n", museProject.latin1());
            loadProjectFile(fn);
            }
      }

//---------------------------------------------------------
//   save
//---------------------------------------------------------

bool MusE::save()
      {
      if (newSongFlag)
            return saveAs();
      else
            return save(project.filePath(), false);
      }

//---------------------------------------------------------
//   save
//---------------------------------------------------------

bool MusE::save(const QString& name, bool overwriteWarn)
      {
      bool popenFlag;
      FILE* f = fileOpen(this, name, QString(".med"), "w", popenFlag, false, overwriteWarn);
      if (f == 0)
            return false;
      Xml xml(f);
      write(xml);
      if (ferror(f)) {
            QString s = "Write File\n" + name + "\nfailed: "
               + strerror(errno);
            QMessageBox::critical(this,
               tr("MusE: Write File failed"), s);
            popenFlag? pclose(f) : fclose(f);
            unlink(name.latin1());
            return false;
            }
      else {
            popenFlag? pclose(f) : fclose(f);
            song->dirty = false;
            return true;
            }
      }

//---------------------------------------------------------
//   quitDoc
//---------------------------------------------------------

void MusE::quitDoc()
      {
      close(true);
      }

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

extern void terminateSynthGuis();

void MusE::closeEvent(QCloseEvent*)
      {
      song->setStop(true);
      //
      // wait for sequencer
      //
      while (song->play()) {
            qApp->processEvents();
            }
      if (song->dirty) {
            int n = 0;
            n = QMessageBox::warning(this, appName,
               tr("The current Project contains unsaved data\n"
               "Save Current Project?"),
               tr("&Save"), tr("&Nosave"), tr("&Abort"), 0, 2);
            if (n == 0) {
                  if (!save())      // donīt quit if save failed
                        return;
                  }
            else if (n == 2)
                  return;
            }
      seq->stop();
      terminateSynthGuis();

      // save "Open Recent" list
      QString prjPath(getenv("HOME"));
      prjPath += "/.musePrj";
      FILE* f = fopen(prjPath.latin1(), "w");
      if (f) {
            for (int i = 0; i < PROJECT_LIST_LEN; ++i) {
                  fprintf(f, "%s\n", projectList[i] ? projectList[i]->latin1() : "");
                  }
            fclose(f);
            }
      if (seq)
            delete seq;

#if HAVE_JACK
      exitJackAudio();
#endif

      qApp->quit();
      }

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

void MusE::about()
      {
      QMessageBox* mb = new QMessageBox();
      mb->setCaption(tr("MusE: About"));
      mb->setText(tr("Linux Music Editor\n"
       "Version " VERSION "\n"
       "(C) Copyright 1999-2001 Werner Schweer\n"
       "see http://muse.seh.de for new versions\n"
       "and more information\n"
       "Published under the GNU Public Licence"));
      mb->setIconPixmap(QPixmap(QString("muse1.png")));
      mb->exec();
      }

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

void MusE::aboutQt()
      {
      QMessageBox::aboutQt(this, QString("MusE"));
      }

//---------------------------------------------------------
//   toggleTransport
//---------------------------------------------------------

void MusE::toggleTransport()
      {
      bool flag = true;
      if (transport->isVisible()) {
            transport->hide();
            flag = false;
            }
      else {
            transport->show();
            }
      menu_display->setItemChecked(tr_id, flag);
      }

//---------------------------------------------------------
//   toggleBigTime
//---------------------------------------------------------

void MusE::toggleBigTime()
      {
      bool flag = true;
      if (bigtime->isVisible()) {
            bigtime->hide();
            flag = false;
            }
      else {
            bigtime->show();
//          bigtime->raise();
            }
      menu_display->setItemChecked(bt_id, flag);
      }

//---------------------------------------------------------
//   saveAs
//---------------------------------------------------------

bool MusE::saveAs()
      {
//      QString name = getSaveFileName(museProject, med_file_pattern, this,
      QString name = getSaveFileName(QString(""), med_file_pattern, this,
         tr("MusE: Save As"));
      bool ok = false;
      if (!name.isEmpty()) {
            ok = save(name, true);
            if (ok) {
                  project.setFile(name);
                  setCaption(tr("MusE: Song: ") + project.baseName());
                  addProject(name);
                  newSongFlag = false;
                  museProject = QFileInfo(name).dirPath(true);
                  }
            }

      return ok;
      }

//---------------------------------------------------------
//   importMidi
//---------------------------------------------------------

void MusE::importMidi()
      {
      QString fn = getOpenFileName(QString("midis"), midi_file_pattern, this,
         tr("MusE: Import Midi"));
      if (!fn.isEmpty())
            loadProjectFile(fn);
      }

//---------------------------------------------------------
//   exportMidi
//---------------------------------------------------------

void MusE::exportMidi()
      {
      MFile file(QString("midis"), QString(".mid"));

      FILE* fp = file.open("w", midi_file_pattern, this, false, true,
         tr("MusE: Export Midi"));
      if (fp == 0)
            return;
      MidiFile mf(fp);
      mf.save();
      }

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

static void printVersion(const char* prog)
      {
      fprintf(stderr, "%s: Linux Music Editor; Version %s\n", prog, VERSION);
      }

//---------------------------------------------------------
//   importMidi
//---------------------------------------------------------

bool MusE::importMidi(const QString name)
      {
      bool popenFlag;
      FILE* fp = fileOpen(this, name, QString(".mid"), "r", popenFlag);
      if (fp == 0)
            return true;
      MidiFile mf(fp);
      bool rv = mf.read();
      popenFlag ? pclose(fp) : fclose(fp);
      if (rv) {
            QString s(tr("reading midifile\n  "));
            s += name;
            s += tr("\nfailed");
            QMessageBox::critical(this, QString("MusE"), s);
            return rv;
            }
      TrackList* tl = song->tracks();
      if (!tl->empty()) {
            Track* track = tl->front();
            track->setSelected(true);
            }
      song->initLen();

      int z, n;
      sigmap.timesig(0, z, n);

      int tempo = tempomap.tempo(0);
      transport->setTimesig(z, n);
      transport->setTempo(tempo);

      bool masterF = !tempomap.empty();
      song->setMasterFlag(masterF);
      transport->setMasterFlag(masterF);

      song->updatePos();
      song->setLoop(false);

      project.setFile(name);
      addProject(name);

      if (song->name().isEmpty())
            song->setName(project.baseName());
      setCaption(QString("MusE: Song: ") + project.baseName());
      arranger->reset();
      arranger->setMode(int(song->mtype()));
      newSongFlag = true;
      song->dirty = false;

      song->update();

      return false;
      }

//---------------------------------------------------------
//   startEditor
//---------------------------------------------------------

void MusE::startEditor(PartList* pl, int type)
      {
      switch (type) {
            case 0: startPianoroll(pl); break;
            case 1: startListEditor(pl); break;
            case 2: startScoreEditor(pl); break;
            case 3: startDrumEditor(pl); break;
            case 4: startWaveEditor(pl); break;
            }
      }

//---------------------------------------------------------
//   startEditor
//---------------------------------------------------------

void MusE::startEditor(Track* t)
      {
      switch (t->type()) {
            case Track::MIDI: startPianoroll(); break;
            case Track::DRUM: startDrumEditor(); break;
            case Track::WAVE: startWaveEditor(); break;
            }
      }

//---------------------------------------------------------
//   getMidiPartsToEdit
//---------------------------------------------------------

PartList* MusE::getMidiPartsToEdit()
      {
      PartList* pl = song->getSelectedMidiParts();
      if (pl->empty()) {
            QMessageBox::critical(this, QString("MusE"), tr("Nothing to edit"));
            return 0;
            }
      return pl;
      }

//---------------------------------------------------------
//   startPianoroll
//---------------------------------------------------------

void MusE::startPianoroll()
      {
      PartList* pl = getMidiPartsToEdit();
      if (pl == 0)
            return;
      startPianoroll(pl);
      }

void MusE::startPianoroll(PartList* pl)
      {
      PianoRoll* pianoroll = new PianoRoll(pl, this);
      pianoroll->show();
      toplevels.push_back(Toplevel(Toplevel::PIANO_ROLL, int(pianoroll), pianoroll));
      connect(pianoroll, SIGNAL(deleted(int)), SLOT(toplevelDeleted(int)));
      }

//---------------------------------------------------------
//   startScoreEditor
//---------------------------------------------------------

void MusE::startScoreEditor()
      {
      PartList* pl = getMidiPartsToEdit();
      if (pl == 0)
            return;
      startScoreEditor(pl);
      }

void MusE::startScoreEditor(PartList* pl)
      {
      Score* scoreEditor = new Score(pl);
      scoreEditor->show();
      toplevels.push_back(Toplevel(Toplevel::NOTEN, int(scoreEditor), scoreEditor));
      connect(scoreEditor, SIGNAL(deleted(int)), SLOT(toplevelDeleted(int)));
      }

//---------------------------------------------------------
//   startListenEditor
//---------------------------------------------------------

void MusE::startListEditor()
      {
      PartList* pl = getMidiPartsToEdit();
      if (pl == 0)
            return;
      startListEditor(pl);
      }

void MusE::startListEditor(PartList* pl)
      {
      ListEdit* listEditor = new ListEdit(pl);
      listEditor->show();
      toplevels.push_back(Toplevel(Toplevel::LISTE, int(listEditor), listEditor));
      connect(listEditor, SIGNAL(deleted(int)), SLOT(toplevelDeleted(int)));
      }

//---------------------------------------------------------
//   startMasterEditor
//---------------------------------------------------------

void MusE::startMasterEditor()
      {
      MasterEdit* masterEditor = new MasterEdit();
      masterEditor->show();
      toplevels.push_back(Toplevel(Toplevel::MASTER, int(masterEditor), masterEditor));
      connect(masterEditor, SIGNAL(deleted(int)), SLOT(toplevelDeleted(int)));
      }

//---------------------------------------------------------
//   startLMasterEditor
//---------------------------------------------------------

void MusE::startLMasterEditor()
      {
      LMaster* lmaster = new LMaster();
      lmaster->show();
      toplevels.push_back(Toplevel(Toplevel::LMASTER, int(lmaster), lmaster));
      connect(lmaster, SIGNAL(deleted(int)), SLOT(toplevelDeleted(int)));
      }

//---------------------------------------------------------
//   startDrumEditor
//---------------------------------------------------------

void MusE::startDrumEditor()
      {
      PartList* pl = getMidiPartsToEdit();
      if (pl == 0)
            return;
      startDrumEditor(pl);
      }

void MusE::startDrumEditor(PartList* pl)
      {
      DrumEdit* drumEditor = new DrumEdit(pl, this);
      drumEditor->show();
      toplevels.push_back(Toplevel(Toplevel::DRUM, int(drumEditor), drumEditor));
      connect(drumEditor, SIGNAL(deleted(int)), SLOT(toplevelDeleted(int)));
      }

//---------------------------------------------------------
//   startWaveEditor
//---------------------------------------------------------

void MusE::startWaveEditor()
      {
      PartList* pl = song->getSelectedWaveParts();
      if (pl->empty()) {
            QMessageBox::critical(this, QString("MusE"), tr("Nothing to edit"));
            return;
            }
      startWaveEditor(pl);
      }

void MusE::startWaveEditor(PartList* pl)
      {
      WaveEdit* waveEditor = new WaveEdit(pl);
      waveEditor->show();
      toplevels.push_back(Toplevel(Toplevel::WAVE, int(waveEditor), waveEditor));
      connect(waveEditor, SIGNAL(deleted(int)), SLOT(toplevelDeleted(int)));
      }

//---------------------------------------------------------
//   midiMixer
//---------------------------------------------------------

void MusE::startMidiMixer()
      {
      if (midiMixer == 0) {
            midiMixer = new Mixer();
            toplevels.push_back(Toplevel(Toplevel::M_MIXER, int(midiMixer), midiMixer));
            connect(midiMixer, SIGNAL(ctrlChanged(int,int,int,int)), song, SLOT(ctrlChanged(int,int,int,int)));
            connect(midiMixer, SIGNAL(masterVolChanged(int)), song, SLOT(masterVolChanged(int)));
            connect(midiMixer, SIGNAL(deleted(int)), SLOT(toplevelDeleted(int)));
            }
      midiMixer->show();
      menu_functions->setItemChecked(midid1, true);
      }

//---------------------------------------------------------
//   startDefineController
//---------------------------------------------------------

void MusE::startDefineController()
      {
      configMidiController();
      }

//---------------------------------------------------------
//   startMarkerView
//---------------------------------------------------------

void MusE::startMarkerView()
      {
      if (markerView == 0) {
            markerView = new MarkerView(this);
            toplevels.push_back(Toplevel(Toplevel::MARKER, int(markerView), markerView));
//            connect(markerView, SIGNAL(deleted(int)), SLOT(toplevelDeleted(int)));
            }
      markerView->show();
      }

//---------------------------------------------------------
//   startAudioMixer
//---------------------------------------------------------

void MusE::startAudioMixer()
      {
      if (audioMixer == 0) {
            audioMixer = new AudioMixerApp();
            toplevels.push_back(Toplevel(Toplevel::A_MIXER, int(audioMixer), audioMixer));
            connect(audioMixer, SIGNAL(deleted(int)), SLOT(toplevelDeleted(int)));
            }
      audioMixer->show();
      menu_audio->setItemChecked(aid1, true);
      }

//---------------------------------------------------------
//   startClipList
//---------------------------------------------------------

void MusE::startClipList()
      {
      if (clipListEdit == 0) {
            clipListEdit = new ClipListEdit();
            toplevels.push_back(Toplevel(Toplevel::CLIPLIST, int(clipListEdit), clipListEdit));
            connect(clipListEdit, SIGNAL(deleted(int)), SLOT(toplevelDeleted(int)));
            }
      clipListEdit->show();
      menu_audio->setItemChecked(aid2, true);
      }

//---------------------------------------------------------
//   fileMenu
//---------------------------------------------------------

void MusE::openRecentMenu()
      {
      openRecent->clear();
      for (int i = 0; i < PROJECT_LIST_LEN; ++i) {
            if (projectList[i] == 0)
                  break;
            const char* path = projectList[i]->latin1();
            const char* p = strrchr(path, '/');
            if (p == 0)
                  p = path;
            else
                  ++p;
            openRecent->insertItem(QString(p), i);
            }
      }

//---------------------------------------------------------
//   selectProject
//---------------------------------------------------------

void MusE::selectProject(int id)
      {
      if (id < 0)
            return;
      assert(id < PROJECT_LIST_LEN);
      QString* name = projectList[id];
      if (name == 0)
            return;
      loadProjectFile(*name);
      }

//---------------------------------------------------------
//   toplevelDeleted
//---------------------------------------------------------

void MusE::toplevelDeleted(int tl)
      {
      for (iToplevel i = toplevels.begin(); i != toplevels.end(); ++i) {
            if (i->object() == tl) {
                  switch(i->type()) {
                        case Toplevel::M_MIXER:
                              menu_functions->setItemChecked(midid1, false);
                              return;
                        case Toplevel::MARKER:
                              break;
                        case Toplevel::A_MIXER:
                              menu_audio->setItemChecked(aid1, false);
                              return;
                        case Toplevel::CLIPLIST:
                              menu_audio->setItemChecked(aid2, false);
                              return;
                        // the followin editors can exist in more than
                        // one instantiation:
                        case Toplevel::MIXDOWN:
                        case Toplevel::PIANO_ROLL:
                        case Toplevel::NOTEN:
                        case Toplevel::LISTE:
                        case Toplevel::DRUM:
                        case Toplevel::MASTER:
                        case Toplevel::WAVE:
                        case Toplevel::LMASTER:
                              break;
                        }
                  toplevels.erase(i);
                  return;
                  }
            }
      assert(false);
      }

//---------------------------------------------------------
//   ctrlChanged
//    midi ctrl value changed
//---------------------------------------------------------

void MusE::ctrlChanged()
      {
      if (midiMixer)
            midiMixer->updateValues(true);      // set current values
      }


//---------------------------------------------------------
//   kbAccel
//---------------------------------------------------------

void MusE::kbAccel(int key)
      {
      switch (key) {
            case Key_C:
                  song->setClick(!song->click());
                  break;

            case Key_Space:
                  if (song->play())
                        song->setStopPlay(false);
                  else if (song->cpos() != song->lpos())
                        song->setPos(0, song->lpos());
                  else
                        song->setPos(0, 0);
                  break;
            case Key_Insert:
                  song->setPlay(false);
                  break;
            case Key_Enter:
                  song->setPlay(true);
                  break;
            case Key_End:
                  if (!song->record())
                        song->setPos(0, song->lpos());
                  break;
            case Key_Down:
                  if (!song->record())
                        song->setPos(0, song->rpos());
                  break;
            case Key_Slash:
                  song->setLoop(!song->loop());
                  break;
            case Key_Asterisk:
                  if (!song->play()) {
                        song->setRecord(!song->record());
                        }
                  break;
            case Key_F11:
                  toggleTransport();
                  break;
            case Key_F12:
                  toggleBigTime();
                  break;
            default:
                  if (debugMsg)
                        printf("unknown kbAccel 0x%x\n", key);
                  break;
            }
      }

//---------------------------------------------------------
//   MuseApplication
//---------------------------------------------------------

class MuseApplication : public QApplication {
      MusE* muse;

   public:
      MuseApplication(int& argc, char** argv)
         : QApplication(argc, argv)
            {
            muse = 0;
            }


      void setMuse(MusE* m) {
            muse = m;
#ifdef HAVE_LADCCA
            startTimer (300);
#endif
            }

      bool notify(QObject* receiver, QEvent* event) {
            bool flag = QApplication::notify(receiver, event);
            if (event->type() == QEvent::KeyPress) {
                  QKeyEvent* ke = (QKeyEvent*)event;
                  globalKeyState = ke->stateAfter();
                  bool accepted = ke->isAccepted();
                  if (!accepted && seq) {
                        muse->kbAccel(ke->key());
                        return true;
                        }
                  }
            if (event->type() == QEvent::KeyRelease) {
                  QKeyEvent* ke = (QKeyEvent*)event;
                  globalKeyState = ke->stateAfter();
                  }

            return flag;
            }

#ifdef HAVE_LADCCA
     virtual void timerEvent (QTimerEvent * e) {
            muse->cca_idle_cb ();
            }
#endif /* HAVE_LADCCA */

      };

//---------------------------------------------------------
//   usage
//---------------------------------------------------------

static void usage(const char* prog, const char* txt)
      {
      fprintf(stderr, "%s: %s\nusage: %s flags midifile\n   Flags:",
         prog, txt, prog);
      fprintf(stderr, "   -v       print version\n");
      fprintf(stderr, "   -d       debug mode: no threads\n");
      fprintf(stderr, "   -D       debug mode: enable some debug messages\n");
      fprintf(stderr, "   -m       debug mode: trace midi Input\n");
      fprintf(stderr, "   -M       debug mode: trace midi Output\n");
      fprintf(stderr, "   -s       debug mode: trace sync\n");
      fprintf(stderr, "   -R       enable real time scheduling\n");
      fprintf(stderr, "   -P  n    set real time priority to n (default: 50)\n");
      fprintf(stderr, "   -a       no audio\n");
      fprintf(stderr, "   -p       don't load LADSPA plugins\n");
      fprintf(stderr, "   -A name  set alsa audio device (default: hw:0)\n");
      }

//---------------------------------------------------------
//   getCapabilities
//---------------------------------------------------------

void getCapabilities()
      {
#ifdef RTCAP
#ifdef __linux__
      const char* napp = getenv("GIVERTCAP");
      system(napp ? napp : "givertcap");
#endif // __linux__
#endif
      }

//---------------------------------------------------------
//   catchSignal
//---------------------------------------------------------

static void catchSignal(int sig)
      {
      if (debugMsg)
            fprintf(stderr, "MusE: signal %d catched\n", sig);
      if (sig == SIGSEGV) {
            fprintf(stderr, "MusE: segmentation fault\n");
            abort();
            }
//      if (sig == SIGINT)
//            exit(0);
      if (sig == SIGCHLD) {
            //
            // try to catch software synthesizer gui exit
            //
            M_DEBUG("caught SIGCHLD - child died\n");
            int status;
            int n = waitpid (-1, &status, WNOHANG);
            if (n > 0) {
                  for (iSynthI ii = synthiInstances.begin(); ii != synthiInstances.end(); ++ii) {
                        SynthI* si = *ii;
                        if (si && (si->guiPid() == n)) {
                              si->guiExited();
                              break;
                              }
                        }
                  }
            }
      }

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

int main(int argc, char* argv[])
      {
      ruid = getuid();
      euid = geteuid();
      undoSetuid();
#ifdef HAVE_LADCCA
      cca_args_t * cca_args;

      cca_args = cca_extract_args (&argc, &argv);
#endif

      srand(time(0));   // initialize random number generator

/*      for (int i = 0; i < SIGRTMIN-1; ++i) {
            if (i != SIGABRT && i != SIGSEGV)
                  signal(i, catchSignal);
            }
  */
      signal(SIGCHLD, catchSignal);

      QApplication::setColorSpec(QApplication::ManyColor);

      MuseApplication app(argc, argv);
      initAlsaAudio();
      noAlsa = alsaAudioDevice == 0;

      int c;
      while ((c = getopt(argc, argv, "vdDmMRsP:apA:")) != EOF) {
            switch (c) {
                  case 'v': printVersion(argv[0]); return 0;
                  case 'd': debugMode = true; break;
                  case 'D': debugMsg = true; break;
                  case 'm': midiInputTrace = true; break;
                  case 'M': midiOutputTrace = true; break;
                  case 'R':
                        realTimeScheduling = true;
                        break;
                  case 's': debugSync = true; break;
                  case 'P': realTimePriority = atoi(optarg); break;
                  case 'a': noAudio = true; break;
                  case 'p': loadPlugins = false; break;
                  case 'A':
                        if (alsaAudioDevice) {
                              alsaAudioDevice->setInputPort(QString(optarg));
                              alsaAudioDevice->setOutputPort(QString(optarg));
                              }
                        break;
                  default:  usage(argv[0], "bad argument"); return -1;
                  }
            }
      argc -= optind;
      ++argc;

      museUser = getenv("MUSEHOME");
      if (museUser == 0)
            museUser = getenv("HOME");
      QString museGlobal;
      const char* p = getenv("MUSE");
      if (p)
            museGlobal = p;

      if (museGlobal.isEmpty()) {
            QString museGlobal(INSTPREFIX);
            museGlobalLib   =  QString(INSTLIBDIR) + "/muse";
            museGlobalShare =  museGlobal + "/share/muse";
            }
      else {
            museGlobalShare = museGlobal + "/share";
            museGlobalLib = museGlobal + "/lib";
            }
      museProject = "./"; //getcwd(0, 0);
      if (debugMsg) {
            printf("global lib:   <%s>\n", museGlobalLib.latin1());
            printf("global share: <%s>\n", museGlobalShare.latin1());
            printf("muse home:    <%s>\n", museUser.latin1());
            printf("project dir:  <%s>\n", museProject.latin1());
            }

      QString wp(museGlobalShare);
//      wp += "/rawwaves/";
      wp += QString("/");
      if (debugMsg)
            printf("RAWWAVES searched at <%s>\n", wp.latin1());

      setenv("RAWWAVE_PATH", wp.latin1(), 0);  // hack for stk lib

      static QTranslator translator(0);
      QString locale(QTextCodec::locale());
      if (locale != "C") {
            QString loc("muse_");
            loc += QString(QTextCodec::locale());
            if (translator.load(loc, QString(".")) == false) {
                  QString lp(museGlobalShare);
                  lp += QString("/locale");
                  if (translator.load(loc, lp) == false) {
                        printf("no locale <%s>/<%s>\n", loc.latin1(), lp.latin1());
                        }
                  }
            app.installTranslator(&translator);
            }
      if (debugMode)
            printf("locale <%s>\n", locale.latin1());
      if (locale == "de") {
            hIsB = false;
            }

      waveClips = new ClipList();

      font0 = QApplication::font();
      if (!noAudio && loadPlugins)
            initPlugins();
      initIcons();
      QApplication::clipboard()->setSelectionMode(false);

      muse = new MusE(argc, &argv[optind]);
      app.setMuse(muse);
      muse->show();

#ifdef HAVE_LADCCA
      cca_client = cca_init (cca_args, "MusE", CCA_Config_File, CCA_PROTOCOL(2,0));

      if (alsaSeq)
	cca_alsa_client_id (cca_client, snd_seq_client_id (alsaSeq));
# ifdef HAVE_JACK
      if (jackAudioDevice)
	cca_jack_client_name (cca_client, "MusE");
# endif /* HAVE_JACK */
#endif /* HAVE_LADCCA */

      return app.exec();
      }

#if 0
//---------------------------------------------------------
//   configPart
//---------------------------------------------------------

void MusE::configPart(int id)
      {
      if (id < 3) {
            partConfig->setItemChecked(0, id == 0);
            partConfig->setItemChecked(1, id == 1);
            partConfig->setItemChecked(2, id == 2);
            arranger->setShowPartType(id);
            for (int i = 3; i < 10; ++i) {
                  partConfig->setItemEnabled(i, id == 2);
                  }
            }
      else {
            bool flag = !partConfig->isItemChecked(id);
            partConfig->setItemChecked(id, flag);
            int val = arranger->showPartEvent();
            if (flag) {
                  val |= 1 << (id-3);
                  }
            else {
                  val &= ~(1 << (id-3));
                  }
            arranger->setShowPartEvent(val);
            }
      }
#endif

//---------------------------------------------------------
//   cmd
//    some cmd's from pulldown menu
//---------------------------------------------------------

void MusE::cmd(int cmd)
      {
      TrackList* tracks = song->tracks();
      int l = song->lpos();
      int r = song->rpos();

      switch(cmd) {
            case CMD_CUT:
                  arranger->cmd(Arranger::CMD_CUT_PART);
                  break;
            case CMD_COPY:
                  arranger->cmd(Arranger::CMD_COPY_PART);
                  break;
            case CMD_PASTE:
                  arranger->cmd(Arranger::CMD_PASTE_PART);
                  break;
            case CMD_DELETE:
                  song->startUndo();
                  if (song->msgRemoveParts()) {
                        song->endUndo(SC_PART_REMOVED);
                        break;
                        }
                  else
                        midiThread->msgRemoveTracks();
                  song->endUndo(SC_TRACK_REMOVED);
                  break;
            case CMD_DELETE_TRACK:
                  song->startUndo();
                  midiThread->msgRemoveTracks();
                  song->endUndo(SC_TRACK_REMOVED);
                  break;
            case CMD_ADD_TRACK:
                  song->addTrack();
                  break;
            case CMD_SELECT_ALL:
            case CMD_SELECT_NONE:
            case CMD_SELECT_INVERT:
            case CMD_SELECT_ILOOP:
            case CMD_SELECT_OLOOP:
                  for (iTrack i = tracks->begin(); i != tracks->end(); ++i) {
                        PartList* parts = (*i)->parts();
                        for (iPart p = parts->begin(); p != parts->end(); ++p) {
                              bool f = false;
                              int t1 = p->second->posTick();
                              int t2 = t1 + p->second->lenTick();
                              bool inside =
                                 ((t1 >= l) && (t1 < r))
                                 ||  ((t2 > l) && (t2 < r))
                                 ||  ((t1 <= l) && (t2 > r));
                              switch(cmd) {
                                    case CMD_SELECT_INVERT:
                                          f = !p->second->selected();
                                          break;
                                    case CMD_SELECT_NONE:
                                          f = false;
                                          break;
                                    case CMD_SELECT_ALL:
                                          f = true;
                                          break;
                                    case CMD_SELECT_ILOOP:
                                          f = inside;
                                          break;
                                    case CMD_SELECT_OLOOP:
                                          f = !inside;
                                          break;
                                    }
                              p->second->setSelected(f);
                              }
                        }
                  song->update();
                  break;

            case CMD_SELECT_PARTS:
                  for (iTrack i = tracks->begin(); i != tracks->end(); ++i) {
                        if (!(*i)->selected())
                              continue;
                        PartList* parts = (*i)->parts();
                        for (iPart p = parts->begin(); p != parts->end(); ++p)
                              p->second->setSelected(true);
                        }
                  song->update();
                  break;
            case CMD_FOLLOW_NO:
                  song->setFollow(Song::NO);
                  setFollow();
                  break;
            case CMD_FOLLOW_JUMP:
                  song->setFollow(Song::JUMP);
                  setFollow();
                  break;
            case CMD_FOLLOW_CONTINUOUS:
                  song->setFollow(Song::CONTINUOUS);
                  setFollow();
                  break;
            }
      }

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

void MusE::clipboardChanged()
      {
      QCString subtype("partlist");
      QMimeSource* ms = QApplication::clipboard()->data(QClipboard::Clipboard);
      if (ms == 0)
            return;
      bool flag = false;
      for (int i = 0; ms->format(i); ++i) {
// printf("Format <%s\n", ms->format(i));
            if ((strncmp(ms->format(i), "text/midipartlist", 17) == 0)
               || strncmp(ms->format(i), "text/wavepartlist", 17) == 0) {
                  flag = true;
                  break;
                  }
            }
      menuEdit->setItemEnabled(CMD_PASTE, flag);
      }

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

void MusE::selectionChanged()
      {
      bool flag = arranger->isSingleSelection();
      menuEdit->setItemEnabled(CMD_CUT, flag);
      menuEdit->setItemEnabled(CMD_COPY, flag);
      }

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

void MusE::transpose()
      {
      Transpose *w = new Transpose();
      w->show();
      }

//---------------------------------------------------------
//   modifyGateTime
//---------------------------------------------------------

void MusE::modifyGateTime()
      {
      GateTime* w = new GateTime(this);
      w->show();
      }

//---------------------------------------------------------
//   modifyVelocity
//---------------------------------------------------------

void MusE::modifyVelocity()
      {
      printf("not implemented\n");
      }

//---------------------------------------------------------
//   crescendo
//---------------------------------------------------------

void MusE::crescendo()
      {
      printf("not implemented\n");
      }

//---------------------------------------------------------
//   thinOut
//---------------------------------------------------------

void MusE::thinOut()
      {
      printf("not implemented\n");
      }

//---------------------------------------------------------
//   eraseEvent
//---------------------------------------------------------

void MusE::eraseEvent()
      {
      printf("not implemented\n");
      }

//---------------------------------------------------------
//   noteShift
//---------------------------------------------------------

void MusE::noteShift()
      {
      printf("not implemented\n");
      }

//---------------------------------------------------------
//   moveClock
//---------------------------------------------------------

void MusE::moveClock()
      {
      printf("not implemented\n");
      }

//---------------------------------------------------------
//   copyMeasure
//---------------------------------------------------------

void MusE::copyMeasure()
      {
      printf("not implemented\n");
      }

//---------------------------------------------------------
//   eraseMeasure
//---------------------------------------------------------

void MusE::eraseMeasure()
      {
      printf("not implemented\n");
      }

//---------------------------------------------------------
//   deleteMeasure
//---------------------------------------------------------

void MusE::deleteMeasure()
      {
      printf("not implemented\n");
      }

//---------------------------------------------------------
//   createMeasure
//---------------------------------------------------------

void MusE::createMeasure()
      {
      printf("not implemented\n");
      }

//---------------------------------------------------------
//   mixTrack
//---------------------------------------------------------

void MusE::mixTrack()
      {
      printf("not implemented\n");
      }

//---------------------------------------------------------
//   configAppearance
//---------------------------------------------------------

void MusE::configAppearance()
      {
      Appearance *a = new Appearance(arranger);
      a->show();
      }

//---------------------------------------------------------
//   setBigTimeChecked
//---------------------------------------------------------

void MusE::setBigTimeChecked(bool c)
      {
      menu_display->setItemChecked(bt_id, c);
      }

//---------------------------------------------------------
//   loadTheme
//---------------------------------------------------------

void MusE::loadTheme(QString s)
      {
      if (s == _currentTheme)
            return;
      if (QApplication::setStyle(s))
            _currentTheme = s;
      }

//---------------------------------------------------------
//   configChanged
//---------------------------------------------------------

void MusE::configChanged()
      {
      for (iToplevel i = toplevels.begin(); i != toplevels.end(); ++i) {
            Toplevel tl = *i;
            if (tl.type() == Toplevel::WAVE) {
                  TopWin* obj = tl.cobject();
                  ((WaveEdit*)(obj))->setBg(WaveEdit::configBg());
                  }
            }
      }

//---------------------------------------------------------
//   configMetronome
//---------------------------------------------------------

void MusE::configMetronome()
      {
      if (!metronomeConfig)
            metronomeConfig = new MetronomeConfig(this, "metronome");
      metronomeConfig->show();
      }

//---------------------------------------------------------
//   globalCut
//    - remove area between left and right locator
//    - do not touch muted track
//    - cut master track
//---------------------------------------------------------

void MusE::globalCut()
      {
      int lpos = song->lpos();
      int rpos = song->rpos();
      if ((lpos - rpos) >= 0)
            return;

      song->startUndo();
      TrackList* tracks = song->tracks();
      for (iTrack it = tracks->begin(); it != tracks->end(); ++it) {
            MidiTrack* track = dynamic_cast<MidiTrack*>(*it);
            if (track == 0 || track->mute())
                  continue;
            PartList* pl = track->parts();
            for (iPart p = pl->begin(); p != pl->end(); ++p) {
                  Part* part = p->second;
                  int t = part->posTick();
                  int l = part->lenTick();
                  if (t + l <= lpos)
                        continue;
                  if ((t >= lpos) && ((t+l) <= rpos)) {
                        midiThread->msgRemovePart(part, false);
                        }
                  else if ((t < lpos) && ((t+l) > lpos) && ((t+l) <= rpos)) {
                        // remove part tail
                        int len = lpos - t;
                        MidiPart* nPart = new MidiPart(*(MidiPart*)part);
                        nPart->setLenTick(len);
                        //
                        // cut Events in nPart
                        EventList* el = nPart->events();
                        iEvent ie = el->lower_bound(t + len);
                        for (; ie != el->end();) {
                              iEvent i = ie;
                              ++ie;
                              midiThread->msgDeleteEvent((MidiEvent*)i->second, nPart, false);
                              }
                        midiThread->msgChangePart(part, nPart, false);
                        }
                  else if ((t < lpos) && ((t+l) > lpos) && ((t+l) > rpos)) {
                        //----------------------
                        // remove part middle
                        //----------------------

                        MidiPart* nPart = new MidiPart(*(MidiPart*)part);
                        EventList* el = nPart->events();
                        iEvent is = el->lower_bound(lpos);
                        iEvent ie = el->upper_bound(rpos);
                        for (iEvent i = is; i != ie;) {
                              iEvent ii = i;
                              ++i;
                              midiThread->msgDeleteEvent((MidiEvent*)ii->second, nPart, false);
                              }

                        ie = el->lower_bound(rpos);
                        for (; ie != el->end();) {
                              iEvent i = ie;
                              ++ie;
                              MidiEvent* event = (MidiEvent*)i->second;
                              MidiEvent* nEvent = new MidiEvent(*event);
                              nEvent->setPosTick(nEvent->posTick() - (rpos-lpos));
                              midiThread->msgChangeEvent(event, nEvent, nPart, false);
                              }
                        nPart->setLenTick(l - (rpos-lpos));
                        midiThread->msgChangePart(part, nPart, false);
                        }
                  else if ((t >= lpos) && (t < rpos) && (t+l) > rpos) {
                        // TODO: remove part head
                        }
                  else if (t >= rpos) {
                        MidiPart* nPart = new MidiPart(*(MidiPart*)part);
                        int nt = part->posTick();
                        nPart->setPosTick(nt - (rpos -lpos));
                        midiThread->msgChangePart(part, nPart, false);
                        }
                  }
            }
      // TODO: cut tempo track
      // TODO: process marker
      song->endUndo(SC_TRACK_MODIFIED | SC_PART_MODIFIED | SC_PART_REMOVED);
      }

//---------------------------------------------------------
//   globalInsert
//    - insert empty space at left locator position upto
//      right locator
//    - do not touch muted track
//    - insert in master track
//---------------------------------------------------------

void MusE::globalInsert()
      {
      int lpos = song->lpos();
      int rpos = song->rpos();
      if ((lpos - rpos) >= 0)
            return;

      song->startUndo();
      TrackList* tracks = song->tracks();
      for (iTrack it = tracks->begin(); it != tracks->end(); ++it) {
            MidiTrack* track = dynamic_cast<MidiTrack*>(*it);
            //
            // process only non muted midi tracks
            //
            if (track == 0 || track->mute())
                  continue;
            PartList* pl = track->parts();
            for (iPart p = pl->begin(); p != pl->end(); ++p) {
                  Part* part = p->second;
                  int t = part->posTick();
                  int l = part->lenTick();
                  if (t + l <= lpos)
                        continue;
                  if (lpos >= t && lpos < (t+l)) {
                        MidiPart* nPart = new MidiPart(*(MidiPart*)part);
                        nPart->setLenTick(l + (rpos-lpos));
                        EventList* el = nPart->events();

                        iEvent i = el->end();
                        while (i != el->begin()) {
                              --i;
                              if (i->first < lpos)
                                    break;
                              MidiEvent* event  = (MidiEvent*)(i->second);
                              MidiEvent* nEvent = new MidiEvent(*(MidiEvent*)(i->second));
                              nEvent->setPosTick(nEvent->posTick() + (rpos-lpos));
                              midiThread->msgChangeEvent(event, nEvent, nPart, false);
                              }
                        midiThread->msgChangePart(part, nPart, false);

                        }
                  else if (t > lpos) {
                        MidiPart* nPart = new MidiPart(*(MidiPart*)part);
                        nPart->setPosTick(t + (rpos -lpos));
                        midiThread->msgChangePart(part, nPart, false);
                        }
                  }
            }
      // TODO: process tempo track
      // TODO: process marker
      song->endUndo(SC_TRACK_MODIFIED | SC_PART_MODIFIED | SC_PART_REMOVED);
      }

//---------------------------------------------------------
//   globalSplit
//    - split all parts at the song position pointer
//    - do not touch muted track
//---------------------------------------------------------

void MusE::globalSplit()
      {
      int pos = song->cpos();
      song->startUndo();
      TrackList* tracks = song->tracks();
      for (iTrack it = tracks->begin(); it != tracks->end(); ++it) {
            Track* track = *it;
            PartList* pl = track->parts();
            for (iPart p = pl->begin(); p != pl->end(); ++p) {
                  Part* part = p->second;
                  int p1 = part->posTick();
                  int l0 = part->lenTick();
                  if (pos > p1 && pos < (p1+l0)) {
                        Part* p1;
                        Part* p2;
                        track->splitPart(part, pos, p1, p2);
                        midiThread->msgChangePart(part, p1, false);
                        midiThread->msgAddPart(p2, false);
                        break;
                        }
                  }
            }
      song->endUndo(SC_TRACK_MODIFIED | SC_PART_MODIFIED | SC_PART_INSERTED);
      }

//---------------------------------------------------------
//   copyRange
//    - copy space between left and right locator position
//      to song position pointer
//    - dont process muted tracks
//    - create a new part for every track containing the
//      copied events
//---------------------------------------------------------

void MusE::copyRange()
      {
      QMessageBox::critical(this,
         tr("MusE: Copy Range"),
         tr("not implemented")
         );
      }

//---------------------------------------------------------
//   cutEvents
//    - make sure that all events in a part end where the
//      part ends
//    - process only marked parts
//---------------------------------------------------------

void MusE::cutEvents()
      {
      QMessageBox::critical(this,
         tr("MusE: Cut Events"),
         tr("not implemented")
         );
      }

//---------------------------------------------------------
//   enableAudio
//    enable audio features in GUI
//---------------------------------------------------------

void MusE::enableAudio(bool flag)
      {
      menu_audio->setItemEnabled(aid1, flag);
      menu_audio->setItemEnabled(aid2, flag);
      menu_audio->setItemEnabled(aid3, flag);
      }

//---------------------------------------------------------
//   bounceToTrack
//---------------------------------------------------------

void MusE::bounceToTrack()
      {
      // search target track
      TrackList* tl = song->tracks();
      WaveTrack* track = 0;
      for (iTrack it = tl->begin(); it != tl->end(); ++it) {
            Track* t = *it;
            if (t->selected()) {
                  if (track) {
                        QMessageBox::critical(this,
                           tr("MusE: Bounce to Track"),
                           tr("more than one target track selected")
                           );
                        return;
                        }
                  if (t->type() != Track::WAVE) {
                        QMessageBox::critical(this,
                           tr("MusE: Bounce to Track"),
                           tr("wrong target track type,\nselect wave track as target")
                           );
                        return;
                        }
                  track = (WaveTrack*)t;
                  }
            }
      if (track == 0) {
            QMessageBox::critical(this,
               tr("MusE: Bounce to Track"),
               tr("no target track selected")
               );
            return;
            }
      song->bounceTrack = track;
      song->setLoop(false);
      song->setRecord(true);
      song->update(SC_ROUTE | SC_RECFLAG);
      song->setRecordFlag(track, true);
      }

//---------------------------------------------------------
//   bounceToFile
//---------------------------------------------------------

void MusE::bounceToFile()
      {
      SndFile* sf = getSndFile(0, this, 0);
      if (sf == 0)
            return;
      audioOutput.setRecFile(sf);

      audioOutput.setRecordFlag1(true);
      audio->msgSetRecord(&audioOutput, true);

      song->setLoop(false);
      song->setRecord(true);
      song->update(SC_RECFLAG);
      }


#ifdef HAVE_LADCCA
//---------------------------------------------------------
//   cca_idle_cb
//---------------------------------------------------------
#include <iostream>
void
MusE::cca_idle_cb ()
{
  cca_event_t * event;

  while ( (event = cca_get_event (cca_client)) )
    {
      switch (cca_event_get_type (event))
        {
        case CCA_Save_File:
          /* save file */
          save (cca_get_fqn (cca_event_get_string (event), "muse.med"), false);
          cca_send_event (cca_client, event);
          break;

        case CCA_Restore_File:
          /* load file */
          loadProjectFile (cca_get_fqn (cca_event_get_string (event), "muse.med"));
          cca_send_event (cca_client, event);
          break;

        case CCA_Quit:
          quitDoc ();
          std::cout << "Quit"
                    << std::endl;
          cca_event_destroy (event);
          break;

        default:
          std::cout << "Recieved unknown LADCCA event of type "
                    << cca_event_get_type (event)
                    << std::endl;
          cca_event_destroy (event);
          break;
        }
    }
}
#endif /* HAVE_LADCCA */

//---------------------------------------------------------
//   clearSong
//    return true if operation aborted
//    called with sequencer stopped
//---------------------------------------------------------

bool MusE::clearSong()
      {
      if (song->dirty) {
            int n = 0;
            n = QMessageBox::warning(this, appName,
               tr("The current Project contains unsaved data\n"
               "Load overwrites current Project:\n"
               "Save Current Project?"),
               tr("&Save"), tr("&Overwrite"), tr("&Abort"), 0, 2);
            switch (n) {
                  case 0:
                        if (!save())      // abort if save failed
                              return true;
                        break;
                  case 1:
                        break;
                  case 2:
                        return true;
                  default:
                        printf("InternalError: gibt %d\n", n);
                  }
            }
      if (song->play()) {
            midiThread->msgPlay(false);
            while (song->play())
                  qApp->processEvents();
            }

      //---------------------------------------------------
      //   clear routing of permanent audio nodes
      //---------------------------------------------------

      audioOutput._inRoute.clear();
      audioOutput._outRoute.clear();
      audioInput._inRoute.clear();
      audioInput._outRoute.clear();
      for (int i = 0; i < mixerGroups; ++i) {
            audioGroups[i]._inRoute.clear();
            audioGroups[i]._outRoute.clear();
            }
      audioOutput.setPorts(2);
      audioOutput.setVolume(1.0);
      for (int i = 0; i < mixerGroups; ++i) {
            audioGroups[i].setPorts(2);
            audioGroups[i].connectOut(&audioOutput);
            }
      audioInput.setPorts(2);
      audioInput.connectOut(&audioOutput);

      //---------------------------------------------------
      //    remove plugins from permanent audio nodes
      //---------------------------------------------------

      audioOutput.efxPipe()->removeAll();
      audioInput.efxPipe()->removeAll();
      for (int i = 0; i < AUDIO_GROUPS; ++i) {
            audioGroups[i].efxPipe()->removeAll();
            }

      //---------------------------------------------------
      //  remove soft synthis
      //---------------------------------------------------

      for (iSynthI ii = synthiInstances.begin(); ii != synthiInstances.end();) {
            iSynthI iii = ii;
            ++iii;
            int port;
            for (port = 0; port < MIDI_PORTS; ++port) {
                  if (midiPorts[port].instrument() == *ii)
                        break;
                  }
            if (port != MIDI_PORTS) {
                  // synthi is attached
                  midiThread->setMidiDevice(&midiPorts[port], 0);
                  midiPorts[port].setInstrument(genericMidiInstrument);
                  }
            audio->msgRemoveSynthI(*ii);
            ii = iii;
            }

again:
      for (iToplevel i = toplevels.begin(); i != toplevels.end(); ++i) {
            Toplevel tl = *i;
            int obj = tl.object();
            switch (tl.type()) {
                  case Toplevel::A_MIXER:
                        delete audioMixer;
                        audioMixer = 0;
                        break;
                  case Toplevel::M_MIXER:
                  case Toplevel::CLIPLIST:
                  case Toplevel::MARKER:
                        break;
                  case Toplevel::PIANO_ROLL:
                  case Toplevel::NOTEN:
                  case Toplevel::LISTE:
                  case Toplevel::DRUM:
                  case Toplevel::MASTER:
                  case Toplevel::WAVE:
                  case Toplevel::LMASTER:
                  case Toplevel::MIXDOWN:
                        ((QWidget*)(obj))->close(true);
                        goto again;
                  }
            }
      song->clear(true);
      return false;
      }

