//=========================================================
//  MusE
//  Linux Music Editor
//    $Id: score.cpp,v 1.1.1.1 2003/10/29 10:05:26 wschweer Exp $
//  (C) Copyright 1999,2000 Werner Schweer (ws@seh.de)
//=========================================================

#include <stdio.h>

#include "xml.h"
#include "mtscale.h"
#include "ncanvas.h"
#include "score.h"
#include "scrollscale.h"
#include "intlabel.h"
#include "symbols.h"
#include "ttoolbar.h"
#include "tb1.h"
#include "utils.h"
#include "globals.h"
#include "filedialog.h"
#include "papersize.h"
#include "event.h"
#include "icons.h"
#include "layout.h"
#include "song.h"
#include "action.h"
#include "midithread.h"

#include <qtoolbar.h>
#include <qtoolbutton.h>
#include <qtooltip.h>
#include <qlayout.h>
#include <qhbox.h>
#include <qsizegrip.h>
#include <qscrollbar.h>
#include <qlabel.h>
#include <qpushbutton.h>
#include <qbuttongroup.h>
#include <qmenubar.h>
#include <qpopupmenu.h>
#include <qradiobutton.h>
#include <qwhatsthis.h>

int Score::_quantInit = 96;
int Score::_rasterInit = 96;
int Score::_widthInit = 600;
int Score::_heightInit = 400;

//---------------------------------------------------------
//   Score Editor
//---------------------------------------------------------

Score::Score(PartList* pl)
   : MidiEditor(_quantInit, _rasterInit, pl)
      {
      resize(_widthInit, _heightInit);
      paletteBg  = new QButtonGroup(this, "paletteBg");
      paletteBg->hide();
      paletteBg->setExclusive(true);
      connect(paletteBg, SIGNAL(clicked(int)), SLOT(setPaletteItem(int)));

      int scale   = 1;
      n1          = false;
      n2          = false;
      scoreConfig = 0;
      dynPalette  = 0;
      initSymbols();

      canvas     = new ScoreCanvas(this, mainw, scale, scale);
      steprec = false;

      //---------Menü----------------------------------
      QPopupMenu* menuConfig = new QPopupMenu(this, "menuConfig");
      menuBar()->insertItem(tr("&Config"), menuConfig);
      menuConfig->insertItem(tr("Page Settings"), this, SLOT(openPageSettings()));
      menuConfig->insertItem(tr("Staff Settings"), this, SLOT(showScoreConfig()));
      menuConfig->insertItem(tr("Background Pixmap"), this, SLOT(configBackground()));

      QPopupMenu* menuPalettes = new QPopupMenu(this, "menuPalettes");
      menuBar()->insertItem(tr("Palettes"), menuPalettes);
      menuPalettes->insertItem(tr("Dynamics"), this, SLOT(openDynamicsPalette()));

      //---------ToolBar----------------------------------
      tools = new QToolBar(this, "tools");
      QWhatsThis::whatsThisButton(tools);
      QToolButton* printButton = new QToolButton(tools, "printButton");
      printButton->setPixmap(*printIcon);
      QToolTip::add(printButton, tr("Print"));
      QToolButton* viewButton = new QToolButton(tools, "viewButton");
      viewButton->setPixmap(*gvIcon);
      QToolTip::add(viewButton, tr("Preview Print"));

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

      etb = new EditToolBar(this, PointerTool | PencilTool
         | RubberTool | ScoreTool | QuantTool, "etb");
      tool = PointerTool;

      //-------------------------------------------------------------
      //    Transport Bar
      QToolBar* transport = new QToolBar(this);
      transportAction->addTo(transport);

      //---------ToolBar 1----------------------------------
      toolbar = new Toolbar1(this, _rasterInit, _quantInit);

      //---------------------------------------------------
      // Toolbar3
      //    Palettes
      //---------------------------------------------------

      QToolBar* toolbar3  = new QToolBar(tr("Palettes"), this, this, true, "toolbar3");
//      QHBox* hbox4 = new QHBox(toolbar3, "hbox4");
      QToolButton* lyricsB = new QToolButton(toolbar3, "lyricsB");
      lyricsB->setText(tr("Lyrics"));
      lyricsB->setToggleButton(true);
      QToolTip::add(lyricsB, tr("enter lyrics"));
      paletteBg->insert(lyricsB, -2);
      QToolButton* textB = new QToolButton(toolbar3, "textB");
      textB->setText(tr("Text"));
      textB->setToggleButton(true);
      paletteBg->insert(textB, -3);
      QToolTip::add(textB, tr("enter text"));
      QToolButton* chordB = new QToolButton(toolbar3, "chordB");
      chordB->setText(tr("C7m"));
      chordB->setToggleButton(true);
      QToolTip::add(chordB, tr("enter chord symbol"));
      paletteBg->insert(chordB, -4);

      //---------------------------------------------------
      // Toolbar 2
      //---------------------------------------------------

      QToolBar* toolbar2  = new QToolBar(tr("Score"), this, this, true, "toolbar2");

      QActionGroup* action2 = new QActionGroup(toolbar2, "action2", true);
      for (int k = 0; k < 7; ++k) {
            Action* action = new Action(action2, k);
            action->setToggleAction(true);
            action->setIconSet(QIconSet(*snoteBM[k]));
            }
      action2->addTo(toolbar2);
      connect(action2, SIGNAL(selected(QAction*)), SLOT(setQuant1(QAction*)));

      noteButtons[7] = new QToolButton(toolbar2, "noteButtons7");    // Dot tool
      noteButtons[7]->setPixmap(*snoteBM[7]);
      noteButtons[7]->setToggleButton(true);
      noteButtons[8] = new QToolButton(toolbar2, "noteButtons8");    // T tool
      noteButtons[8]->setPixmap(*snoteBM[8]);
      noteButtons[8]->setToggleButton(true);

      toolbar2->addSeparator();

      // enharmonic shift:
      QActionGroup* action3 = new QActionGroup(toolbar2, "action3", true);
      for (int k = 0; k < 5; ++k) {
            Action* action = new Action(action3, k);
            action->setToggleAction(true);
            action->setIconSet(QIconSet(*enhBM[k]));
            }
      action3->addTo(toolbar2);
      connect(action3, SIGNAL(selected(QAction*)), canvas, SLOT(setEnh(QAction*)));

      //---------------------------------------------------
      //  note attributes
      //    - voice selection
      //    - stem direction
      //---------------------------------------------------

      toolbar2->addSeparator();
//      QHBox* hbox2 = new QHBox(toolbar2, "hbox2");
      QToolButton* voiceUpButton = new QToolButton(toolbar2, "voiceUpButton");
      voiceUpButton->setPixmap(*upVoice);
      QToolButton* voiceDownButton = new QToolButton(toolbar2, "voiceDownButton");
      voiceDownButton->setPixmap(*downVoice);
      QToolButton* flipStemButton = new QToolButton(toolbar2, "flipStemButton");
      flipStemButton->setPixmap(*flipNote);

      QToolTip::add(voiceUpButton, tr("to previous voice"));
      QToolTip::add(voiceDownButton, tr("to next voice"));
      QToolTip::add(flipStemButton, tr("flip stem direction"));
      QWhatsThis::add(voiceUpButton,
        tr("move marked notes to previous voice or to upper stave"
           " in a split system"));
      QWhatsThis::add(voiceDownButton,
        tr("move marked notes to next voice or to lower stave"
           " in a split system"));
      QWhatsThis::add(flipStemButton,
        tr("flip stem direction of selected notes"));

      //---------------------------------------------------
      // Configuration Tools
      //    - staff settings
      //---------------------------------------------------

      toolbar2->addSeparator();
      QToolButton* scoreConfigButton = new QToolButton(toolbar2);
      scoreConfigButton->setPixmap(*configBM);
      connect(scoreConfigButton, SIGNAL(pressed()), SLOT(showScoreConfig()));
      QToolTip::add(scoreConfigButton, tr("StaffSettings"));

      toolbar2->addSeparator();

      info = new NoteInfo(this);

      //---------------------------------------------------
      //    Rest
      //---------------------------------------------------

#define mm2dot(x) int((x)*72.0/25.4/printScale)

      QSizeGrip* corner   = new QSizeGrip(mainw);
      vscroll = new ScrollScale(1, 5, scale,
         mm2dot(paperHeight), Vertical, mainw);
      hscroll = new ScrollScale(1, 5, scale,
         mm2dot(paperWidth), Horizontal, mainw);

      connect(vscroll, SIGNAL(lscaleChanged(int)), hscroll, SLOT(setScale(int)));

      hscroll->showMag(false);
      hscroll->setPageButtons(true);

      mainGrid->setRowStretch(0, 100);
      mainGrid->setColStretch(0, 100);

      mainGrid->addMultiCellWidget(hLine(mainw),   0, 0, 0, 1);
      mainGrid->addWidget(canvas,  1, 0);
      mainGrid->addWidget(vscroll, 1, 1);
      mainGrid->addWidget(hscroll, 2, 0);
      mainGrid->addWidget(corner,  2, 1, AlignBottom | AlignRight);
      vscroll->setPos(0);
      hscroll->setPos(0);
      setSelection(0, 0, 0);

      connect(voiceUpButton,   SIGNAL(pressed()), canvas, SLOT(voiceUp()));
      connect(voiceDownButton, SIGNAL(pressed()), canvas, SLOT(voiceDown()));
      connect(flipStemButton,  SIGNAL(pressed()), canvas, SLOT(flipStems()));

      connect(vscroll, SIGNAL(scrollChanged(int)),   canvas, SLOT(setYPos(int)));
      connect(hscroll, SIGNAL(scrollChanged(int)),   canvas, SLOT(setXPos(int)));
      connect(hscroll, SIGNAL(newPage(int)), canvas, SLOT(setPage(int)));
      connect(vscroll, SIGNAL(scaleChanged(int)), canvas, SLOT(setYMag(int)));
      connect(vscroll, SIGNAL(scaleChanged(int)), canvas, SLOT(setXMag(int)));

      connect(etb, SIGNAL(toolChanged(int)), canvas, SLOT(setTool(int)));

      connect(noteButtons[7], SIGNAL(toggled(bool)), SLOT(n1Change(bool)));
      connect(noteButtons[8], SIGNAL(toggled(bool)), SLOT(n2Change(bool)));

      connect(canvas, SIGNAL(selectionChanged(int, MidiEvent*, MidiPart*)), this,
         SLOT(setSelection(int, MidiEvent*, MidiPart*)));
      connect(info, SIGNAL(valueChanged(NoteInfo::ValType, int)),
         SLOT(noteinfoChanged(NoteInfo::ValType, int)));
      connect(song, SIGNAL(songChanged(int)), SLOT(songChanged(int)));

      // connect to toolbar
      connect(canvas,  SIGNAL(pitchChanged(int)), toolbar, SLOT(setPitch(int)));
      connect(canvas,  SIGNAL(timeChanged(int)),  toolbar, SLOT(setTime(int)));
      connect(toolbar, SIGNAL(quantChanged(int)),          SLOT(setQuant(int)));
      connect(toolbar, SIGNAL(rasterChanged(int)),         SLOT(setRaster(int)));
      connect(toolbar, SIGNAL(soloChanged(bool)),          SLOT(soloChanged(bool)));

	connect(printButton, SIGNAL(clicked()), canvas, SLOT(print()));
	connect(viewButton, SIGNAL(clicked()), canvas, SLOT(preview()));

      hscroll->setPages(canvas->pages());

      setCaption(canvas->getCaption());
      if (!scoreBg.isEmpty())
            canvas->setBg(QPixmap(scoreBg));
      }

//---------------------------------------------------------
//   Score
//---------------------------------------------------------

Score::~Score()
      {
      if (dynPalette)
            delete dynPalette;
      undoRedo->removeFrom(tools);
      }

//---------------------------------------------------------
//   setSelection
//    update Info Line
//---------------------------------------------------------

void Score::setSelection(int tick, MidiEvent* e, MidiPart* p)
      {
      selEvent = e;
      selPart  = p;
      selTick  = tick;
      if (e) {
            info->setValue(NoteInfo::VAL_TIME, tick);
            info->setValue(NoteInfo::VAL_LEN, e->lenTick());
            info->setValue(NoteInfo::VAL_PITCH, e->pitch());
            info->setValue(NoteInfo::VAL_VELON, e->velo());
            info->setValue(NoteInfo::VAL_VELOFF, e->veloOff());
//            info->setValue(NoteInfo::VAL_CHANNEL, p->track()->outChannel() + 1);
            }
      else {
            // set illegal values
            info->setValue(NoteInfo::VAL_TIME, -1);
            info->setValue(NoteInfo::VAL_LEN, -1);
            info->setValue(NoteInfo::VAL_PITCH, -5);
            info->setValue(NoteInfo::VAL_VELON, -5);
            info->setValue(NoteInfo::VAL_VELOFF, -5);
//            info->setValue(NoteInfo::VAL_CHANNEL, -5);
            }
      }

//---------------------------------------------------------
//   songChanged
//---------------------------------------------------------

void Score::songChanged(int type)
      {
      if (type) {
            canvas->songChanged(type);
            if (canvas->getSystem()->tracks()->size() == 0) {
                  close(true);
                  return;
                  }
            setCaption(canvas->getCaption());
            hscroll->setPages(canvas->pages());
            hscroll->setRange(0, mm2dot(paperWidth));
            vscroll->setRange(0, mm2dot(paperHeight));
            }
      }

//---------------------------------------------------------
//   setRaster
//---------------------------------------------------------

void Score::setRaster(int val)
      {
      _rasterInit = val;
      MidiEditor::setRaster(val);
      }

//---------------------------------------------------------
//   setQuant
//---------------------------------------------------------

void Score::setQuant(int val)
      {
      _quantInit = val;
//      canvas->setQuant(val);
      MidiEditor::setQuant(val);
      }

void Score::soloChanged(bool)
      {
//TODO: für alle Tracks      song->soloChanged(flag, canvas->track());
      }

void Score::closeEvent(QCloseEvent* e)
      {
      emit deleted((int)this);
      e->accept();
      }

//---------------------------------------------------------
//   writeStatus
//---------------------------------------------------------

void Score::writeStatus(int level, Xml& xml) const
      {
      xml.tag(level++, "scoreeditor");
      MidiEditor::writeStatus(level, xml);
      xml.tag(level, "/scoreeditor");
      }

//---------------------------------------------------------
//   readStatus
//---------------------------------------------------------

void Score::readStatus(Xml& xml)
      {
      for (;;) {
            Xml::Token token = xml.parse();
            const QString& tag = xml.s1();
            switch (token) {
                  case Xml::Error:
                  case Xml::End:
                        return;
                  case Xml::TagStart:
                        if (tag == "midieditor")
                              MidiEditor::readStatus(xml);
                        else
                              xml.unknown("Score");
                        break;
                  case Xml::TagEnd:
                        if (tag == "scoreeditor")
                              return;
                  default:
                        break;
                  }
            }
      }

//---------------------------------------------------------
//   readConfiguration
//---------------------------------------------------------

void Score::readConfiguration(Xml& xml)
      {
      for (;;) {
            Xml::Token token = xml.parse();
            const QString& tag = xml.s1();
            switch (token) {
                  case Xml::Error:
                  case Xml::End:
                        return;
                  case Xml::TagStart:
                        if (tag == "quant")
                              _quantInit = xml.parseInt();
                        else if (tag == "image")
                              scoreBg = xml.parse1();
                        else if (tag == "raster")
                              _rasterInit = xml.parseInt();
                        else
                              xml.unknown("score");
                        break;
                  case Xml::TagEnd:
                        if (tag == "score")
                              return;
                  default:
                        break;
                  }
            }
      }

//---------------------------------------------------------
//   writeConfiguration
//---------------------------------------------------------

void Score::writeConfiguration(int level, Xml& xml)
      {
      xml.tag(level++, "score");
      xml.intTag(level, "quant", _quantInit);
      xml.intTag(level, "raster", _rasterInit);
      xml.tag(level, "/score");
      }

//---------------------------------------------------------
//   setQuant1
//---------------------------------------------------------

void Score::setQuant1(QAction* action)
      {
      quantId = ((Action*)action)->id();
      noteButtons[7]->setOn(false);       // deselect 'T' and '.' Button
      noteButtons[8]->setOn(false);
      n1 = n2 = 0;
      etb->set(ScoreTool);
      setQuant2();
      }

void Score::setQuant2()
      {
      int tab[3][7] = {
            {1024,  512, 256, 128,  64, 32, 16 },
            {1536,  768, 384, 192,  96, 48, 24 },
            {2304, 1152, 576, 288, 144, 72, 36 }
            };
      if (n1)
            _quant = tab[2][quantId];
      else if (n2)
            _quant = tab[0][quantId];
      else
            _quant = tab[1][quantId];
      toolbar->setQuant(_quant);
      canvas->setTool(ScoreTool);
      }

// '.' Button
void Score::n1Change(bool f)
      {
      n1 = f;
      if (_quant  <= 24) {       // kein Punkt hinter 64'tel
            noteButtons[7]->setOn(false);
            return;
            }
      if (f)
            noteButtons[8]->setOn(false);
      setQuant2();
      }

// 'T' Button

void Score::n2Change(bool f)
      {
      n2 = f;
      if (f)
            noteButtons[7]->setOn(false);
      setQuant2();
      }

//---------------------------------------------------------
//    edit currently selected Event
//---------------------------------------------------------

void Score::noteinfoChanged(NoteInfo::ValType type, int val)
      {
      MidiEvent* event = new MidiEvent(*selEvent);
      switch(type) {
            case NoteInfo::VAL_TIME:
                  break;
            case NoteInfo::VAL_LEN:
                  event->setLenTick(val);
                  break;
            case NoteInfo::VAL_VELON:
                  event->setVelo(val);
                  break;
            case NoteInfo::VAL_VELOFF:
                  event->setVeloOff(val);
                  break;
//            case NoteInfo::VAL_CHANNEL:
//                  event->setChannel(val-1);
//                  break;
            case NoteInfo::VAL_PITCH:
                  event->setPitch(val);
                  break;
            }
      midiThread->msgChangeEvent(selEvent, event, selPart);
      selEvent = event;
      }

void Score::soloChanged(Track*)
      {
//TODO      toolbar->setSolo(t == canvas->track());
      }

//---------------------------------------------------------
//   configBackground
//---------------------------------------------------------

void Score::configBackground()
      {
      scoreBg = getImageFileName(QString("wallpapers"), image_file_pattern, this,
        tr("MusE: config wallpaper"));
      if (scoreBg.isEmpty())
            return;
      canvas->setBg(QPixmap(scoreBg));
      }

/*---------------------------------------------------------
 *    openPageSettings
 *---------------------------------------------------------*/

void Score::openPageSettings()
      {
      new PageSettings(this);
      }

//---------------------------------------------------------
//   setPaletteItem
//---------------------------------------------------------

void Score::setPaletteItem(int item)
      {
      etb->set(PencilTool);
      canvas->setPaletteItem(item);
      }

