diff options
-rw-r--r-- | muse2/muse/app.cpp | 131 | ||||
-rw-r--r-- | muse2/muse/app.h | 19 | ||||
-rw-r--r-- | muse2/muse/cobject.h | 2 | ||||
-rw-r--r-- | muse2/muse/midiedit/scoreedit.cpp | 4212 | ||||
-rw-r--r-- | muse2/muse/midiedit/scoreedit.h | 808 |
5 files changed, 3706 insertions, 1466 deletions
diff --git a/muse2/muse/app.cpp b/muse2/muse/app.cpp index 8143bb0c..9f10a614 100644 --- a/muse2/muse/app.cpp +++ b/muse2/muse/app.cpp @@ -114,6 +114,20 @@ pthread_t splashThread; // // pyscript->runPythonScript(script); // } + +void MusE::clearScoreMenuMappers() +{ + delete scoreOneStaffPerTrackMapper; + delete scoreAllInOneMapper; + + scoreOneStaffPerTrackMapper = new QSignalMapper(this); + scoreAllInOneMapper = new QSignalMapper(this); + + connect(scoreOneStaffPerTrackMapper, SIGNAL(mapped(QWidget*)), this, SLOT(openInScoreEdit_oneStaffPerTrack(QWidget*))); + connect(scoreAllInOneMapper, SIGNAL(mapped(QWidget*)), this, SLOT(openInScoreEdit_allInOne(QWidget*))); +} + + //--------------------------------------------------------- // sleep function //--------------------------------------------------------- @@ -803,6 +817,8 @@ MusE::MusE(int argc, char** argv) : QMainWindow() editSignalMapper = new QSignalMapper(this); midiPluginSignalMapper = new QSignalMapper(this); followSignalMapper = new QSignalMapper(this); + scoreOneStaffPerTrackMapper = new QSignalMapper(this); + scoreAllInOneMapper = new QSignalMapper(this); song = new Song("song"); song->blockSignals(true); @@ -977,7 +993,15 @@ MusE::MusE(int argc, char** argv) : QMainWindow() editOutsideLoopAction = new QAction(QIcon(*select_outside_loopIcon), tr("&Outside Loop"), this); editAllPartsAction = new QAction( QIcon(*select_all_parts_on_trackIcon), tr("All &Parts on Track"), this); - startScoreEditAction = new QAction(*scoreIconSet, tr("Score"), this); + + scoreSubmenu = new QMenu(tr("Score"), this); + scoreSubmenu->setIcon(QIcon(*scoreIconSet)); + + scoreAllInOneSubsubmenu = new QMenu(tr("all parts in one staff"), this); + scoreOneStaffPerTrackSubsubmenu = new QMenu(tr("one staff per part"), this); + + updateScoreMenus(); + startPianoEditAction = new QAction(*pianoIconSet, tr("Pianoroll"), this); startDrumEditAction = new QAction(QIcon(*edit_drummsIcon), tr("Drums"), this); startListEditAction = new QAction(QIcon(*edit_listIcon), tr("List"), this); @@ -1127,10 +1151,12 @@ MusE::MusE(int argc, char** argv) : QMainWindow() connect(editSignalMapper, SIGNAL(mapped(int)), this, SLOT(cmd(int))); connect(startPianoEditAction, SIGNAL(activated()), SLOT(startPianoroll())); - connect(startScoreEditAction, SIGNAL(activated()), SLOT(startScoreEdit())); connect(startDrumEditAction, SIGNAL(activated()), SLOT(startDrumEditor())); connect(startListEditAction, SIGNAL(activated()), SLOT(startListEditor())); connect(startWaveEditAction, SIGNAL(activated()), SLOT(startWaveEditor())); + connect(scoreOneStaffPerTrackMapper, SIGNAL(mapped(QWidget*)), this, SLOT(openInScoreEdit_oneStaffPerTrack(QWidget*))); + connect(scoreAllInOneMapper, SIGNAL(mapped(QWidget*)), this, SLOT(openInScoreEdit_allInOne(QWidget*))); + connect(masterGraphicAction, SIGNAL(activated()), SLOT(startMasterEditor())); connect(masterListAction, SIGNAL(activated()), SLOT(startLMasterEditor())); @@ -1346,7 +1372,9 @@ MusE::MusE(int argc, char** argv) : QMainWindow() menuEdit->addSeparator(); menuEdit->addAction(startPianoEditAction); - menuEdit->addAction(startScoreEditAction); + menuEdit->addMenu(scoreSubmenu); + scoreSubmenu->addMenu(scoreAllInOneSubsubmenu); + scoreSubmenu->addMenu(scoreOneStaffPerTrackSubsubmenu); menuEdit->addAction(startDrumEditAction); menuEdit->addAction(startListEditAction); menuEdit->addAction(startWaveEditAction); @@ -3424,29 +3452,86 @@ PartList* MusE::getMidiPartsToEdit() return pl; } +void MusE::scoreNamingChanged() +{ + updateScoreMenus(); +} + +void MusE::updateScoreMenus() +{ + QAction* action; + + + scoreOneStaffPerTrackSubsubmenu->clear(); + scoreAllInOneSubsubmenu->clear(); + + + action=new QAction(tr("New"), this); + connect(action, SIGNAL(activated()), scoreOneStaffPerTrackMapper, SLOT(map())); + scoreOneStaffPerTrackMapper->setMapping(action, (QWidget*)NULL); + scoreOneStaffPerTrackSubsubmenu->addAction(action); + + + action=new QAction(tr("New"), this); //the above action may NOT be reused! + connect(action, SIGNAL(activated()), scoreAllInOneMapper, SLOT(map())); + scoreAllInOneMapper->setMapping(action, (QWidget*)NULL); + scoreAllInOneSubsubmenu->addAction(action); + + for (ToplevelList::iterator it=toplevels.begin(); it!=toplevels.end(); it++) + if (it->type()==Toplevel::SCORE) + { + ScoreEdit* score = (ScoreEdit*) it->cobject(); + + action=new QAction(QString(score->get_name().c_str()), this); + connect(action, SIGNAL(activated()), scoreOneStaffPerTrackMapper, SLOT(map())); + scoreOneStaffPerTrackMapper->setMapping(action, (QWidget*)score); + scoreOneStaffPerTrackSubsubmenu->addAction(action); + + + action=new QAction(QString(score->get_name().c_str()), this); //the above action may NOT be reused! + connect(action, SIGNAL(activated()), scoreAllInOneMapper, SLOT(map())); + scoreAllInOneMapper->setMapping(action, (QWidget*)score); + scoreAllInOneSubsubmenu->addAction(action); + } +} + //--------------------------------------------------------- // startScoreEdit //--------------------------------------------------------- -void MusE::startScoreEdit() - { - PartList* pl = getMidiPartsToEdit(); - if (pl == 0) - return; - startScoreEdit(pl, true); - } +void MusE::openInScoreEdit_oneStaffPerTrack(QWidget* dest) +{ + openInScoreEdit((ScoreEdit*)dest, false); +} -void MusE::startScoreEdit(PartList* pl, bool showDefaultCtrls) - { +void MusE::openInScoreEdit_allInOne(QWidget* dest) +{ + openInScoreEdit((ScoreEdit*)dest, true); +} - ScoreEdit* scoreedit = new ScoreEdit(pl, this, 0, arranger->cursorValue()); - if(showDefaultCtrls) // p4.0.12 - scoreedit->addCtrl(); - scoreedit->show(); - toplevels.push_back(Toplevel(Toplevel::PIANO_ROLL, (unsigned long)(scoreedit), scoreedit)); - connect(scoreedit, SIGNAL(deleted(unsigned long)), SLOT(toplevelDeleted(unsigned long))); - connect(muse, SIGNAL(configChanged()), scoreedit, SLOT(configChanged())); - } +void MusE::openInScoreEdit(ScoreEdit* destination, bool allInOne) +{ + PartList* pl = getMidiPartsToEdit(); + if (pl == 0) + return; + openInScoreEdit(destination, pl, allInOne); +} + +void MusE::openInScoreEdit(ScoreEdit* destination, PartList* pl, bool allInOne) +{ + if (destination==NULL) // if no destination given, create a new one + { + destination = new ScoreEdit(pl, this, 0, arranger->cursorValue()); + destination->show(); + toplevels.push_back(Toplevel(Toplevel::SCORE, (unsigned long)(destination), destination)); + connect(destination, SIGNAL(deleted(unsigned long)), SLOT(toplevelDeleted(unsigned long))); + connect(muse, SIGNAL(configChanged()), destination, SLOT(configChanged())); + + updateScoreMenus(); + } + + destination->add_parts(pl, allInOne); +} //--------------------------------------------------------- // startPianoroll @@ -3667,6 +3752,7 @@ void MusE::toplevelDeleted(unsigned long tl) { for (iToplevel i = toplevels.begin(); i != toplevels.end(); ++i) { if (i->object() == tl) { + bool mustUpdateScoreMenus=false; switch(i->type()) { case Toplevel::MARKER: break; @@ -3685,8 +3771,12 @@ void MusE::toplevelDeleted(unsigned long tl) case Toplevel::WAVE: case Toplevel::LMASTER: break; + case Toplevel::SCORE: + mustUpdateScoreMenus=true; } toplevels.erase(i); + if (mustUpdateScoreMenus) + updateScoreMenus(); return; } } @@ -4839,6 +4929,7 @@ again: case Toplevel::MARKER: break; case Toplevel::PIANO_ROLL: + case Toplevel::SCORE: case Toplevel::LISTE: case Toplevel::DRUM: case Toplevel::MASTER: diff --git a/muse2/muse/app.h b/muse2/muse/app.h index 168602d3..8a9fdac0 100644 --- a/muse2/muse/app.h +++ b/muse2/muse/app.h @@ -65,6 +65,7 @@ class Appearance; class WaveTrack; class AudioOutput; class EditInstrument; +class ScoreEdit; #define MENU_ADD_SYNTH_ID_BASE 0x1000 @@ -109,7 +110,7 @@ class MusE : public QMainWindow QAction *editInvertSelectionAction, *editInsideLoopAction, *editOutsideLoopAction, *editAllPartsAction; QAction *trackMidiAction, *trackDrumAction, *trackWaveAction, *trackAOutputAction, *trackAGroupAction; QAction *trackAInputAction, *trackAAuxAction; - QAction *startPianoEditAction, *startDrumEditAction, *startListEditAction, *startWaveEditAction, *startScoreEditAction; + QAction *startPianoEditAction, *startDrumEditAction, *startListEditAction, *startWaveEditAction; QAction *masterGraphicAction, *masterListAction; QAction *midiTransposeAction; QAction *midiTransformerAction; @@ -158,6 +159,7 @@ class MusE : public QMainWindow QMenu* menu_audio, *menuAutomation; QMenu* menu_functions, *menuScriptPlugins; QMenu* select, *master, *midiEdit, *addTrack; + QMenu *scoreSubmenu, *scoreOneStaffPerTrackSubsubmenu, *scoreAllInOneSubsubmenu; // Special 'stay-open' menu for routes. PopupMenu* routingPopupMenu; @@ -220,6 +222,8 @@ class MusE : public QMainWindow QSignalMapper *editSignalMapper; QSignalMapper *midiPluginSignalMapper; QSignalMapper *followSignalMapper; + QSignalMapper *scoreOneStaffPerTrackMapper; + QSignalMapper *scoreAllInOneMapper; signals: void configChanged(); @@ -261,8 +265,15 @@ class MusE : public QMainWindow void startDrumEditor(); void startDrumEditor(PartList* /*pl*/, bool /*showDefaultCtrls*/ = false); void startEditor(Track*); - void startScoreEdit(); - void startScoreEdit(PartList* /*pl*/, bool /*showDefaultCtrls*/ = false); + + void openInScoreEdit(ScoreEdit* destination, PartList* pl, bool allInOne=false); + void openInScoreEdit(ScoreEdit* destination, bool allInOne=false); + void openInScoreEdit_allInOne(QWidget* destination); + void openInScoreEdit_oneStaffPerTrack(QWidget* destination); + void clearScoreMenuMappers(); + void updateScoreMenus(); + void scoreNamingChanged(); + void startPianoroll(); void startPianoroll(PartList* /*pl*/, bool /*showDefaultCtrls*/ = false); void startWaveEditor(); @@ -320,7 +331,7 @@ class MusE : public QMainWindow void execDeliveredScript(int); void execUserScript(int); - + public slots: bool saveAs(); void bounceToFile(AudioOutput* ao = 0); diff --git a/muse2/muse/cobject.h b/muse2/muse/cobject.h index 8e21eaf0..9a80b2b1 100644 --- a/muse2/muse/cobject.h +++ b/muse2/muse/cobject.h @@ -38,7 +38,7 @@ class TopWin : public QMainWindow class Toplevel { public: enum ToplevelType { PIANO_ROLL, LISTE, DRUM, MASTER, WAVE, - LMASTER, CLIPLIST, MARKER + LMASTER, CLIPLIST, MARKER, SCORE #ifdef PATCHBAY , M_PATCHBAY #endif /* PATCHBAY */ diff --git a/muse2/muse/midiedit/scoreedit.cpp b/muse2/muse/midiedit/scoreedit.cpp index f236a0d5..18c3bc71 100644 --- a/muse2/muse/midiedit/scoreedit.cpp +++ b/muse2/muse/midiedit/scoreedit.cpp @@ -1,13 +1,20 @@ +//you need to download http://home.arcor.de/michael.jung11/glyphs.tar.bz2 +//and extract it somewhere. then change FONT_PATH to the correct directory +//the trailing slash is necessary +#define FONT_PATH "/home/flo/muse-glyphs/" + //========================================================= // MusE // Linux Music Editor -// $Id: ScoreEdit.cpp,v 1.25.2.15 2009/11/16 11:29:33 lunar_shuttle Exp $ -// (C) Copyright 1999 Werner Schweer (ws@seh.de) +// scoreedit.cpp +// (C) Copyright 2011 Florian Jung (florian.a.jung@web.de) //========================================================= + #include <QLayout> #include <QSizeGrip> #include <QLabel> +#include <QScrollBar> #include <QPushButton> #include <QToolButton> #include <QToolTip> @@ -26,8 +33,14 @@ #include <QMimeData> #include <QScrollArea> #include <QSettings> +#include <QImage> #include <stdio.h> +#include <math.h> + +#include <iostream> +#include <sstream> +using namespace std; #include "xml.h" #include "mtscale.h" @@ -51,1361 +64,3024 @@ #include "mtrackinfo.h" -int ScoreEdit::_quantInit = 96; -int ScoreEdit::_rasterInit = 96; -int ScoreEdit::_widthInit = 600; -int ScoreEdit::_heightInit = 400; -int ScoreEdit::_quantStrengthInit = 80; // 1 - 100% -int ScoreEdit::_quantLimitInit = 50; // tick value -bool ScoreEdit::_quantLenInit = false; -int ScoreEdit::_toInit = 0; -int ScoreEdit::colorModeInit = 0; +#include "sig.h" -static const int xscale = -10; -static const int yscale = 1; -static const int pianoWidth = 40; -static int ScoreEditTools = PointerTool | PencilTool | RubberTool | DrawTool; +string IntToStr(int i); -//--------------------------------------------------------- -// ScoreEdit -//--------------------------------------------------------- -ScoreEdit::ScoreEdit(PartList* pl, QWidget* parent, const char* name, unsigned initPos) - : MidiEditor(_quantInit, _rasterInit, pl, parent, name) - { - deltaMode = false; - resize(_widthInit, _heightInit); - selPart = 0; - quantConfig = 0; - _playEvents = false; - _quantStrength = _quantStrengthInit; - _quantLimit = _quantLimitInit; - _quantLen = _quantLenInit; - _to = _toInit; - colorMode = colorModeInit; - - QSignalMapper* mapper = new QSignalMapper(this); - QSignalMapper* colorMapper = new QSignalMapper(this); - - //---------Menu---------------------------------- - - menuEdit = menuBar()->addMenu(tr("&Edit")); - - menuEdit->addActions(undoRedo->actions()); - - menuEdit->addSeparator(); - - editCutAction = menuEdit->addAction(QIcon(*editcutIconSet), tr("C&ut")); - mapper->setMapping(editCutAction, PianoCanvas::CMD_CUT); - connect(editCutAction, SIGNAL(triggered()), mapper, SLOT(map())); - - editCopyAction = menuEdit->addAction(QIcon(*editcopyIconSet), tr("&Copy")); - mapper->setMapping(editCopyAction, PianoCanvas::CMD_COPY); - connect(editCopyAction, SIGNAL(triggered()), mapper, SLOT(map())); - - editPasteAction = menuEdit->addAction(QIcon(*editpasteIconSet), tr("&Paste")); - mapper->setMapping(editPasteAction, PianoCanvas::CMD_PASTE); - connect(editPasteAction, SIGNAL(triggered()), mapper, SLOT(map())); - - menuEdit->addSeparator(); - - editDelEventsAction = menuEdit->addAction(tr("Delete &Events")); - mapper->setMapping(editDelEventsAction, PianoCanvas::CMD_DEL); - connect(editDelEventsAction, SIGNAL(triggered()), mapper, SLOT(map())); - - menuEdit->addSeparator(); - - menuSelect = menuEdit->addMenu(QIcon(*selectIcon), tr("&Select")); - - selectAllAction = menuSelect->addAction(QIcon(*select_allIcon), tr("Select &All")); - mapper->setMapping(selectAllAction, PianoCanvas::CMD_SELECT_ALL); - connect(selectAllAction, SIGNAL(triggered()), mapper, SLOT(map())); - - selectNoneAction = menuSelect->addAction(QIcon(*select_deselect_allIcon), tr("&Deselect All")); - mapper->setMapping(selectNoneAction, PianoCanvas::CMD_SELECT_NONE); - connect(selectNoneAction, SIGNAL(triggered()), mapper, SLOT(map())); - - selectInvertAction = menuSelect->addAction(QIcon(*select_invert_selectionIcon), tr("Invert &Selection")); - mapper->setMapping(selectInvertAction, PianoCanvas::CMD_SELECT_INVERT); - connect(selectInvertAction, SIGNAL(triggered()), mapper, SLOT(map())); - - menuSelect->addSeparator(); - - selectInsideLoopAction = menuSelect->addAction(QIcon(*select_inside_loopIcon), tr("&Inside Loop")); - mapper->setMapping(selectInsideLoopAction, PianoCanvas::CMD_SELECT_ILOOP); - connect(selectInsideLoopAction, SIGNAL(triggered()), mapper, SLOT(map())); - - selectOutsideLoopAction = menuSelect->addAction(QIcon(*select_outside_loopIcon), tr("&Outside Loop")); - mapper->setMapping(selectOutsideLoopAction, PianoCanvas::CMD_SELECT_OLOOP); - connect(selectOutsideLoopAction, SIGNAL(triggered()), mapper, SLOT(map())); - - menuSelect->addSeparator(); - - //selectPrevPartAction = select->addAction(tr("&Previous Part")); - selectPrevPartAction = menuSelect->addAction(QIcon(*select_all_parts_on_trackIcon), tr("&Previous Part")); - mapper->setMapping(selectPrevPartAction, PianoCanvas::CMD_SELECT_PREV_PART); - connect(selectPrevPartAction, SIGNAL(triggered()), mapper, SLOT(map())); - - //selNextPartAction = select->addAction(tr("&Next Part")); - selectNextPartAction = menuSelect->addAction(QIcon(*select_all_parts_on_trackIcon), tr("&Next Part")); - mapper->setMapping(selectNextPartAction, PianoCanvas::CMD_SELECT_NEXT_PART); - connect(selectNextPartAction, SIGNAL(triggered()), mapper, SLOT(map())); - - menuConfig = menuBar()->addMenu(tr("&Config")); - - eventColor = menuConfig->addMenu(tr("&Event Color")); - - QActionGroup* actgrp = new QActionGroup(this); - actgrp->setExclusive(true); - - //evColorBlueAction = eventColor->addAction(tr("&Blue")); - evColorBlueAction = actgrp->addAction(tr("&Blue")); - evColorBlueAction->setCheckable(true); - colorMapper->setMapping(evColorBlueAction, 0); - - //evColorPitchAction = eventColor->addAction(tr("&Pitch colors")); - evColorPitchAction = actgrp->addAction(tr("&Pitch colors")); - evColorPitchAction->setCheckable(true); - colorMapper->setMapping(evColorPitchAction, 1); - - //evColorVelAction = eventColor->addAction(tr("&Velocity colors")); - evColorVelAction = actgrp->addAction(tr("&Velocity colors")); - evColorVelAction->setCheckable(true); - colorMapper->setMapping(evColorVelAction, 2); - - connect(evColorBlueAction, SIGNAL(triggered()), colorMapper, SLOT(map())); - connect(evColorPitchAction, SIGNAL(triggered()), colorMapper, SLOT(map())); - connect(evColorVelAction, SIGNAL(triggered()), colorMapper, SLOT(map())); - - eventColor->addActions(actgrp->actions()); - - connect(colorMapper, SIGNAL(mapped(int)), this, SLOT(eventColorModeChanged(int))); - - menuFunctions = menuBar()->addMenu(tr("&Functions")); - - menuFunctions->setTearOffEnabled(true); - - funcOverQuantAction = menuFunctions->addAction(tr("Over Quantize")); - mapper->setMapping(funcOverQuantAction, PianoCanvas::CMD_OVER_QUANTIZE); - connect(funcOverQuantAction, SIGNAL(triggered()), mapper, SLOT(map())); - - funcNoteOnQuantAction = menuFunctions->addAction(tr("Note On Quantize")); - mapper->setMapping(funcNoteOnQuantAction, PianoCanvas::CMD_ON_QUANTIZE); - connect(funcNoteOnQuantAction, SIGNAL(triggered()), mapper, SLOT(map())); - - funcNoteOnOffQuantAction = menuFunctions->addAction(tr("Note On/Off Quantize")); - mapper->setMapping(funcNoteOnOffQuantAction, PianoCanvas::CMD_ONOFF_QUANTIZE); - connect(funcNoteOnOffQuantAction, SIGNAL(triggered()), mapper, SLOT(map())); - - funcIterQuantAction = menuFunctions->addAction(tr("Iterative Quantize")); - mapper->setMapping(funcIterQuantAction, PianoCanvas::CMD_ITERATIVE_QUANTIZE); - connect(funcIterQuantAction, SIGNAL(triggered()), mapper, SLOT(map())); - - menuFunctions->addSeparator(); - - funcConfigQuantAction = menuFunctions->addAction(tr("Config Quant...")); - connect(funcConfigQuantAction, SIGNAL(triggered()), this, SLOT(configQuant())); - - menuFunctions->addSeparator(); - - funcGateTimeAction = menuFunctions->addAction(tr("Modify Gate Time")); - mapper->setMapping(funcGateTimeAction, PianoCanvas::CMD_MODIFY_GATE_TIME); - connect(funcGateTimeAction, SIGNAL(triggered()), mapper, SLOT(map())); - - funcModVelAction = menuFunctions->addAction(tr("Modify Velocity")); - mapper->setMapping(funcModVelAction, PianoCanvas::CMD_MODIFY_VELOCITY); - connect(funcModVelAction, SIGNAL(triggered()), mapper, SLOT(map())); - - funcCrescendoAction = menuFunctions->addAction(tr("Crescendo")); - mapper->setMapping(funcCrescendoAction, PianoCanvas::CMD_CRESCENDO); - funcCrescendoAction->setEnabled(false); - connect(funcCrescendoAction, SIGNAL(triggered()), mapper, SLOT(map())); - - funcTransposeAction = menuFunctions->addAction(tr("Transpose")); - mapper->setMapping(funcTransposeAction, PianoCanvas::CMD_TRANSPOSE); - funcTransposeAction->setEnabled(false); - connect(funcTransposeAction, SIGNAL(triggered()), mapper, SLOT(map())); - - funcThinOutAction = menuFunctions->addAction(tr("Thin Out")); - mapper->setMapping(funcThinOutAction, PianoCanvas::CMD_THIN_OUT); - funcThinOutAction->setEnabled(false); - connect(funcThinOutAction, SIGNAL(triggered()), mapper, SLOT(map())); - - funcEraseEventAction = menuFunctions->addAction(tr("Erase Event")); - mapper->setMapping(funcEraseEventAction, PianoCanvas::CMD_ERASE_EVENT); - funcEraseEventAction->setEnabled(false); - connect(funcEraseEventAction, SIGNAL(triggered()), mapper, SLOT(map())); - - funcNoteShiftAction = menuFunctions->addAction(tr("Note Shift")); - mapper->setMapping(funcNoteShiftAction, PianoCanvas::CMD_NOTE_SHIFT); - funcNoteShiftAction->setEnabled(false); - connect(funcNoteShiftAction, SIGNAL(triggered()), mapper, SLOT(map())); - - funcMoveClockAction = menuFunctions->addAction(tr("Move Clock")); - mapper->setMapping(funcMoveClockAction, PianoCanvas::CMD_MOVE_CLOCK); - funcMoveClockAction->setEnabled(false); - connect(funcMoveClockAction, SIGNAL(triggered()), mapper, SLOT(map())); - - funcCopyMeasureAction = menuFunctions->addAction(tr("Copy Measure")); - mapper->setMapping(funcCopyMeasureAction, PianoCanvas::CMD_COPY_MEASURE); - funcCopyMeasureAction->setEnabled(false); - connect(funcCopyMeasureAction, SIGNAL(triggered()), mapper, SLOT(map())); - - funcEraseMeasureAction = menuFunctions->addAction(tr("Erase Measure")); - mapper->setMapping(funcEraseMeasureAction, PianoCanvas::CMD_ERASE_MEASURE); - funcEraseMeasureAction->setEnabled(false); - connect(funcEraseMeasureAction, SIGNAL(triggered()), mapper, SLOT(map())); - - funcDelMeasureAction = menuFunctions->addAction(tr("Delete Measure")); - mapper->setMapping(funcDelMeasureAction, PianoCanvas::CMD_DELETE_MEASURE); - funcDelMeasureAction->setEnabled(false); - connect(funcDelMeasureAction, SIGNAL(triggered()), mapper, SLOT(map())); - - funcCreateMeasureAction = menuFunctions->addAction(tr("Create Measure")); - mapper->setMapping(funcCreateMeasureAction, PianoCanvas::CMD_CREATE_MEASURE); - funcCreateMeasureAction->setEnabled(false); - connect(funcCreateMeasureAction, SIGNAL(triggered()), mapper, SLOT(map())); - - funcSetFixedLenAction = menuFunctions->addAction(tr("Set Fixed Length")); - mapper->setMapping(funcSetFixedLenAction, PianoCanvas::CMD_FIXED_LEN); - connect(funcSetFixedLenAction, SIGNAL(triggered()), mapper, SLOT(map())); - - funcDelOverlapsAction = menuFunctions->addAction(tr("Delete Overlaps")); - mapper->setMapping(funcDelOverlapsAction, PianoCanvas::CMD_DELETE_OVERLAPS); - connect(funcDelOverlapsAction, SIGNAL(triggered()), mapper, SLOT(map())); - - menuPlugins = menuBar()->addMenu(tr("&Plugins")); - song->populateScriptMenu(menuPlugins, this); - - connect(mapper, SIGNAL(mapped(int)), this, SLOT(cmd(int))); - - //---------ToolBar---------------------------------- - tools = addToolBar(tr("ScoreEdit tools")); - tools->setObjectName("ScoreEdit tools"); - tools->addActions(undoRedo->actions()); - tools->addSeparator(); - - srec = new QToolButton(); - srec->setToolTip(tr("Step Record")); - srec->setIcon(*steprecIcon); - srec->setCheckable(true); - tools->addWidget(srec); - - midiin = new QToolButton(); - midiin->setToolTip(tr("Midi Input")); - midiin->setIcon(*midiinIcon); - midiin->setCheckable(true); - tools->addWidget(midiin); - - speaker = new QToolButton(); - speaker->setToolTip(tr("Play Events")); - speaker->setIcon(*speakerIcon); - speaker->setCheckable(true); - tools->addWidget(speaker); - - tools2 = new EditToolBar(this, ScoreEditTools); - addToolBar(tools2); - - QToolBar* panicToolbar = addToolBar(tr("panic")); - panicToolbar->setObjectName("panic"); - panicToolbar->addAction(panicAction); - - //------------------------------------------------------------- - // Transport Bar - QToolBar* transport = addToolBar(tr("transport")); - transport->setObjectName("transport"); - transport->addActions(transportAction->actions()); - - addToolBarBreak(); - toolbar = new Toolbar1(this, _rasterInit, _quantInit); - addToolBar(toolbar); - - addToolBarBreak(); - info = new NoteInfo(this); - addToolBar(info); - - //--------------------------------------------------- - // split - //--------------------------------------------------- - - splitter = new Splitter(Qt::Vertical, mainw, "splitter"); - splitter->setHandleWidth(2); - - hsplitter = new Splitter(Qt::Horizontal, mainw, "hsplitter"); - hsplitter->setChildrenCollapsible(true); - hsplitter->setHandleWidth(2); - - QPushButton* ctrl = new QPushButton(tr("ctrl"), mainw); - //QPushButton* ctrl = new QPushButton(tr("C"), mainw); // Tim. - ctrl->setObjectName("Ctrl"); - ctrl->setFont(config.fonts[3]); - ctrl->setToolTip(tr("Add Controller View")); - hscroll = new ScrollScale(-25, -2, xscale, 20000, Qt::Horizontal, mainw); - ctrl->setFixedSize(pianoWidth, hscroll->sizeHint().height()); - //ctrl->setFixedSize(pianoWidth / 2, hscroll->sizeHint().height()); // Tim. - - // Tim. - /* - QPushButton* trackInfoButton = new QPushButton(tr("T"), mainw); - trackInfoButton->setObjectName("TrackInfo"); - trackInfoButton->setFont(config.fonts[3]); - trackInfoButton->setToolTip(tr("Show track info")); - trackInfoButton->setFixedSize(pianoWidth / 2, hscroll->sizeHint().height()); - */ - - QSizeGrip* corner = new QSizeGrip(mainw); - - midiTrackInfo = new MidiTrackInfo(mainw); - int mtiw = midiTrackInfo->width(); // Save this. - midiTrackInfo->setMinimumWidth(100); - //midiTrackInfo->setMaximumWidth(150); - - midiTrackInfo->setSizePolicy(QSizePolicy(QSizePolicy::Ignored, QSizePolicy::Expanding)); - infoScroll = new QScrollArea; - infoScroll->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); - infoScroll->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); - infoScroll->setSizePolicy(QSizePolicy(QSizePolicy::Maximum, QSizePolicy::Expanding)); - infoScroll->setWidget(midiTrackInfo); - infoScroll->setWidgetResizable(true); - //infoScroll->setVisible(false); - //infoScroll->setEnabled(false); - - //hsplitter->addWidget(midiTrackInfo); - hsplitter->addWidget(infoScroll); // Tim. - hsplitter->addWidget(splitter); - - mainGrid->setRowStretch(0, 100); - mainGrid->setColumnStretch(1, 100); - mainGrid->addWidget(hsplitter, 0, 1, 1, 3); - - // Original. - /* - mainGrid->setColumnStretch(1, 100); - mainGrid->addWidget(splitter, 0, 0, 1, 3); - mainGrid->addWidget(ctrl, 1, 0); - mainGrid->addWidget(hscroll, 1, 1); - mainGrid->addWidget(corner, 1, 2, Qt::AlignBottom|Qt::AlignRight); - */ - - - // Tim. - /* - mainGrid->setColumnStretch(2, 100); - mainGrid->addWidget(splitter, 0, 0, 1, 4); - mainGrid->addWidget(trackInfoButton, 1, 0); - mainGrid->addWidget(ctrl, 1, 1); - mainGrid->addWidget(hscroll, 1, 2); - mainGrid->addWidget(corner, 1, 3, Qt::AlignBottom|Qt::AlignRight); - */ - - //mainGrid->addRowSpacing(1, hscroll->sizeHint().height()); - //mainGrid->addItem(new QSpacerItem(0, hscroll->sizeHint().height()), 1, 0); // Orig + Tim. - - QWidget* split1 = new QWidget(splitter); - split1->setObjectName("split1"); - QGridLayout* gridS1 = new QGridLayout(split1); - gridS1->setContentsMargins(0, 0, 0, 0); - gridS1->setSpacing(0); - //Defined and configure your program change bar here. - //This may well be a copy of MTScale extended for our needs - time = new MTScale(&_raster, split1, xscale); - Piano* piano = new Piano(split1, yscale); - canvas = new PianoCanvas(this, split1, xscale, yscale); - vscroll = new ScrollScale(-3, 7, yscale, KH * 75, Qt::Vertical, split1); - - //setFocusProxy(canvas); // Tim. - - int offset = -(config.division/4); - canvas->setOrigin(offset, 0); - canvas->setCanvasTools(ScoreEditTools); - canvas->setFocus(); - connect(canvas, SIGNAL(toolChanged(int)), tools2, SLOT(set(int))); - time->setOrigin(offset, 0); - - gridS1->setRowStretch(2, 100); - gridS1->setColumnStretch(1, 100); - //gridS1->setColumnStretch(2, 100); // Tim. - - gridS1->addWidget(time, 0, 1, 1, 2); - gridS1->addWidget(hLine(split1), 1, 0, 1, 3); - gridS1->addWidget(piano, 2, 0); - gridS1->addWidget(canvas, 2, 1); - gridS1->addWidget(vscroll, 2, 2); - - // Tim. - /* - gridS1->addWidget(time, 0, 2, 1, 3); - gridS1->addWidget(hLine(split1), 1, 1, 1, 4); - //gridS1->addWidget(infoScroll, 2, 0); - gridS1->addWidget(infoScroll, 0, 0, 3, 1); - gridS1->addWidget(piano, 2, 1); - gridS1->addWidget(canvas, 2, 2); - gridS1->addWidget(vscroll, 2, 3); - */ - - ctrlLane = new Splitter(Qt::Vertical, splitter, "ctrllane"); - QWidget* split2 = new QWidget(splitter); - split2->setMaximumHeight(hscroll->sizeHint().height()); - split2->setMinimumHeight(hscroll->sizeHint().height()); - QGridLayout* gridS2 = new QGridLayout(split2); - gridS2->setContentsMargins(0, 0, 0, 0); - gridS2->setSpacing(0); - gridS2->setRowStretch(0, 100); - gridS2->setColumnStretch(1, 100); - gridS2->addWidget(ctrl, 0, 0); - gridS2->addWidget(hscroll, 0, 1); - gridS2->addWidget(corner, 0, 2, Qt::AlignBottom|Qt::AlignRight); - //splitter->setCollapsible(0, true); - - piano->setFixedWidth(pianoWidth); - - // Tim. - QList<int> mops; - mops.append(mtiw + 30); // 30 for possible scrollbar - mops.append(width() - mtiw - 30); - hsplitter->setSizes(mops); - - connect(tools2, SIGNAL(toolChanged(int)), canvas, SLOT(setTool(int))); - - connect(ctrl, SIGNAL(clicked()), SLOT(addCtrl())); - //connect(trackInfoButton, SIGNAL(clicked()), SLOT(toggleTrackInfo())); Tim. - connect(info, SIGNAL(valueChanged(NoteInfo::ValType, int)), SLOT(noteinfoChanged(NoteInfo::ValType, int))); - connect(vscroll, SIGNAL(scrollChanged(int)), piano, SLOT(setYPos(int))); - connect(vscroll, SIGNAL(scrollChanged(int)), canvas, SLOT(setYPos(int))); - connect(vscroll, SIGNAL(scaleChanged(int)), canvas, SLOT(setYMag(int))); - connect(vscroll, SIGNAL(scaleChanged(int)), piano, SLOT(setYMag(int))); - - connect(hscroll, SIGNAL(scrollChanged(int)), canvas, SLOT(setXPos(int))); - connect(hscroll, SIGNAL(scrollChanged(int)), time, SLOT(setXPos(int))); - - connect(hscroll, SIGNAL(scaleChanged(int)), canvas, SLOT(setXMag(int))); - connect(hscroll, SIGNAL(scaleChanged(int)), time, SLOT(setXMag(int))); - - connect(canvas, SIGNAL(newWidth(int)), SLOT(newCanvasWidth(int))); - connect(canvas, SIGNAL(pitchChanged(int)), piano, SLOT(setPitch(int))); - connect(canvas, SIGNAL(verticalScroll(unsigned)), vscroll, SLOT(setPos(unsigned))); - connect(canvas, SIGNAL(horizontalScroll(unsigned)),hscroll, SLOT(setPos(unsigned))); - connect(canvas, SIGNAL(horizontalScrollNoLimit(unsigned)),hscroll, SLOT(setPosNoLimit(unsigned))); - connect(canvas, SIGNAL(selectionChanged(int, Event&, Part*)), this, - SLOT(setSelection(int, Event&, Part*))); - - connect(piano, SIGNAL(keyPressed(int, int, bool)), canvas, SLOT(pianoPressed(int, int, bool))); - connect(piano, SIGNAL(keyReleased(int, bool)), canvas, SLOT(pianoReleased(int, bool))); - connect(srec, SIGNAL(toggled(bool)), SLOT(setSteprec(bool))); - connect(midiin, SIGNAL(toggled(bool)), canvas, SLOT(setMidiin(bool))); - connect(speaker, SIGNAL(toggled(bool)), SLOT(setSpeaker(bool))); - connect(canvas, SIGNAL(followEvent(int)), SLOT(follow(int))); - - connect(hscroll, SIGNAL(scaleChanged(int)), SLOT(updateHScrollRange())); - piano->setYPos(KH * 30); - canvas->setYPos(KH * 30); - vscroll->setPos(KH * 30); - //setSelection(0, 0, 0); //Really necessary? Causes segfault when only 1 item selected, replaced by the following: - info->setEnabled(false); - - connect(song, SIGNAL(songChanged(int)), SLOT(songChanged1(int))); - - setWindowTitle(canvas->getCaption()); - - updateHScrollRange(); - // connect to toolbar - connect(canvas, SIGNAL(pitchChanged(int)), toolbar, SLOT(setPitch(int))); - connect(canvas, SIGNAL(timeChanged(unsigned)), SLOT(setTime(unsigned))); - connect(piano, SIGNAL(pitchChanged(int)), toolbar, SLOT(setPitch(int))); - connect(time, SIGNAL(timeChanged(unsigned)), SLOT(setTime(unsigned))); - connect(toolbar, SIGNAL(quantChanged(int)), SLOT(setQuant(int))); - connect(toolbar, SIGNAL(rasterChanged(int)),SLOT(setRaster(int))); - connect(toolbar, SIGNAL(toChanged(int)), SLOT(setTo(int))); - connect(toolbar, SIGNAL(soloChanged(bool)), SLOT(soloChanged(bool))); - - setFocusPolicy(Qt::StrongFocus); - setEventColorMode(colorMode); - - QClipboard* cb = QApplication::clipboard(); - connect(cb, SIGNAL(dataChanged()), SLOT(clipboardChanged())); - - clipboardChanged(); // enable/disable "Paste" - selectionChanged(); // enable/disable "Copy" & "Paste" - initShortcuts(); // initialize shortcuts - - const Pos cpos=song->cPos(); - canvas->setPos(0, cpos.tick(), true); - canvas->selectAtTick(cpos.tick()); - //canvas->selectFirst();// - - unsigned pos=0; - if(initPos >= MAXINT) - pos = song->cpos(); - if(pos > MAXINT) - pos = MAXINT; - if (pos) - hscroll->setOffset((int)pos); - - if(canvas->track()) - { - updateTrackInfo(); - toolbar->setSolo(canvas->track()->solo()); - } - - QSettings settings("MusE", "MusE-qt"); - //restoreGeometry(settings.value("ScoreEdit/geometry").toByteArray()); - restoreState(settings.value("ScoreEdit/windowState").toByteArray()); - - } +#define SPLIT_NOTE 60 +//TODO: let the user specify that somehow? -//--------------------------------------------------------- -// songChanged1 -//--------------------------------------------------------- -void ScoreEdit::songChanged1(int bits) - { - - if (bits & SC_SOLO) - { - toolbar->setSolo(canvas->track()->solo()); - return; - } - songChanged(bits); - //trackInfo->songChanged(bits); - // We'll receive SC_SELECTION if a different part is selected. - if (bits & SC_SELECTION) - updateTrackInfo(); - } -//--------------------------------------------------------- -// configChanged -//--------------------------------------------------------- +//TODO: all das unten richtig machen! +#define TICKS_PER_WHOLE (config.division*4) +#define SONG_LENGTH (song->len()) -void ScoreEdit::configChanged() - { - initShortcuts(); - //trackInfo->updateTrackInfo(); - } +#define quant_max 3 //whole, half, quarter = 0,1,2 +#define quant_max_fraction (1 << quant_max) //whole, half, quarter= 1,2,4 +#define FLO_QUANT (TICKS_PER_WHOLE/quant_max_fraction) +//FLO_QUANT = how many ticks has a single quantisation area? -//--------------------------------------------------------- -// updateHScrollRange -//--------------------------------------------------------- -void ScoreEdit::updateHScrollRange() +//FINDMICH MARKER +//TODO: quant_max richtig setzen! + + + +//do NOT put parentheses around this! +#define PAGESTEP 3/4 + + +#define SCROLL_MARGIN 10 +#define SCROLL_SPEED 5 +//SCROLL_SPEED is in (scroll_pixels per second) per mouse-move-pixel +#define SCROLL_SPEED_MAX 500 +//SCROLL_SPEED_MAX is in scroll_pixels_per_second + + + +#define STAFF_DISTANCE (10*YLEN) +#define GRANDSTAFF_DISTANCE (8*YLEN) + +string create_random_string(int len=8) { - int s, e; - canvas->range(&s, &e); - // Show one more measure. - e += AL::sigmap.ticksMeasure(e); - // Show another quarter measure due to imprecise drawing at canvas end point. - e += AL::sigmap.ticksMeasure(e) / 4; - // Compensate for the fixed piano and vscroll widths. - e += canvas->rmapxDev(pianoWidth - vscroll->width()); - int s1, e1; - hscroll->range(&s1, &e1); - if(s != s1 || e != e1) - hscroll->setRange(s, e); + string result; + + for (int i=0;i<len;i++) + result+=char((rand() % 26) + 'A'); + + return result; } -void ScoreEdit::updateTrackInfo() + + +KeyList keymap; + + +KeyList::KeyList() { - selected = curCanvasPart()->track(); - if (selected->isMidiTrack()) { - midiTrackInfo->setTrack(selected); - ///midiTrackInfo->updateTrackInfo(-1); - } + clear(); } -//--------------------------------------------------------- -// follow -//--------------------------------------------------------- +void KeyList::clear() +{ + _KeyList::clear(); //DEBUG -- remove these lines and use the commented out below + insert(std::pair<const unsigned, KeyEvent> (1536, KeyEvent(A, 0))); + insert(std::pair<const unsigned, KeyEvent> (MAX_TICK, KeyEvent(ES, 1536))); + + //insert(std::pair<const unsigned, KeyEvent> (MAX_TICK, KeyEvent(A, 0))); +} + +tonart_t KeyList::key_at_tick(unsigned tick) +{ + ciKeyEvent it = upper_bound(tick); + if (it == end()) + { + cout << "THIS SHOULD NEVER HAPPEN: key at "<<tick<<" not found!" << endl; + return C; + } + + return it->second.key; +} -void ScoreEdit::follow(int pos) - { - int s, e; - canvas->range(&s, &e); - if (pos < e && pos >= s) - hscroll->setOffset(pos); - if (pos < s) - hscroll->setOffset(s); - } + + + + +QPixmap *pix_whole, *pix_half, *pix_quarter; // arrays [NUM_PARTCOLORS+2] +QPixmap *pix_dot, *pix_b, *pix_sharp, *pix_noacc; // arrays [NUM_PARTCOLORS+2] +QPixmap *pix_r1, *pix_r2, *pix_r4, *pix_r8, *pix_r16; // pointers +QPixmap *pix_flag_up, *pix_flag_down; // arrays [4] +QPixmap *pix_num; // array [10] +QPixmap *pix_clef_violin, *pix_clef_bass; //pointers +bool pixmaps_loaded=false; + + + + + + +int ScoreEdit::serial=1; +set<string> ScoreEdit::names; //--------------------------------------------------------- -// setTime +// ScoreEdit //--------------------------------------------------------- -void ScoreEdit::setTime(unsigned tick) - { - toolbar->setTime(tick); - time->setPos(3, tick, false); - } +ScoreEdit::ScoreEdit(PartList* pl, QWidget* parent, const char* name, unsigned initPos) + : MidiEditor(0, 0, pl, parent, name) +{ + score_canvas=new ScoreCanvas(this, mainw, 1, 1); + hscroll = new QScrollBar(Qt::Horizontal, mainw); + + connect(hscroll, SIGNAL(valueChanged(int)), score_canvas, SLOT(scroll_event(int))); + connect(score_canvas, SIGNAL(xpos_changed(int)), hscroll, SLOT(setValue(int))); + connect(score_canvas, SIGNAL(canvas_width_changed(int)), SLOT(canvas_width_changed(int))); + connect(score_canvas, SIGNAL(viewport_width_changed(int)), SLOT(viewport_width_changed(int))); + connect(song, SIGNAL(songChanged(int)), score_canvas, SLOT(song_changed(int))); + + mainGrid->addWidget(score_canvas, 0, 0); + mainGrid->addWidget(hscroll,1,0); + + hscroll->setMinimum(0); + + score_canvas->song_changed(0); + score_canvas->goto_tick(initPos,true); + + if (name!=NULL) + set_name(name, false, true); + else + set_name("Score "+IntToStr(serial++), false, true); +} + + +void ScoreEdit::add_parts(PartList* pl, bool all_in_one) +{ + score_canvas->add_staves(pl, all_in_one); +} + +bool ScoreEdit::set_name(string newname, bool emit_signal, bool emergency_name) +{ + if (names.find(newname)==names.end()) + { + names.erase(name); + names.insert(newname); + + name=newname; + + if (emit_signal) + emit name_changed(); + + return true; + } + else + { + if (emergency_name) + { + while (set_name(create_random_string(), emit_signal, false) == false); + return true; + } + else + return false; + } +} //--------------------------------------------------------- // ~ScoreEdit //--------------------------------------------------------- ScoreEdit::~ScoreEdit() - { - // undoRedo->removeFrom(tools); // p4.0.6 Removed - } +{ + +} -//--------------------------------------------------------- -// cmd -// pulldown menu commands -//--------------------------------------------------------- -void ScoreEdit::cmd(int cmd) - { - ((PianoCanvas*)canvas)->cmd(cmd, _quantStrength, _quantLimit, _quantLen, _to); - } +void ScoreEdit::canvas_width_changed(int width) +{ + hscroll->setMaximum(width); +} +void ScoreEdit::viewport_width_changed(int width) +{ + hscroll->setPageStep(width * PAGESTEP); +} -//--------------------------------------------------------- -// setSelection -// update Info Line -//--------------------------------------------------------- +void ScoreEdit::closeEvent(QCloseEvent* e) +{ + QSettings settings("MusE", "MusE-qt"); + //settings.setValue("ScoreEdit/geometry", saveGeometry()); + settings.setValue("ScoreEdit/windowState", saveState()); -void ScoreEdit::setSelection(int tick, Event& e, Part* p) - { - int selections = canvas->selectionSize(); - - selEvent = e; - selPart = (MidiPart*)p; - selTick = tick; - - if (selections > 1) { - info->setEnabled(true); - info->setDeltaMode(true); - if (!deltaMode) { - deltaMode = true; - info->setValues(0, 0, 0, 0, 0); - tickOffset = 0; - lenOffset = 0; - pitchOffset = 0; - veloOnOffset = 0; - veloOffOffset = 0; - } - } - else if (selections == 1) { - deltaMode = false; - info->setEnabled(true); - info->setDeltaMode(false); - info->setValues(tick, - selEvent.lenTick(), - selEvent.pitch(), - selEvent.velo(), - selEvent.veloOff()); - } - else { - deltaMode = false; - info->setEnabled(false); - } - selectionChanged(); - } + emit deleted((unsigned long)this); + e->accept(); +} -//--------------------------------------------------------- -// edit currently selected Event -//--------------------------------------------------------- -void ScoreEdit::noteinfoChanged(NoteInfo::ValType type, int val) - { - int selections = canvas->selectionSize(); - - if (selections == 0) { - printf("noteinfoChanged while nothing selected\n"); - } - else if (selections == 1) { - Event event = selEvent.clone(); - switch(type) { - case NoteInfo::VAL_TIME: - event.setTick(val - selPart->tick()); - 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_PITCH: - event.setPitch(val); - break; - } - // Indicate do undo, and do not do port controller values and clone parts. - //audio->msgChangeEvent(selEvent, event, selPart); - audio->msgChangeEvent(selEvent, event, selPart, true, false, false); - } - else { - // multiple events are selected; treat noteinfo values - // as offsets to event values - - int delta = 0; - switch (type) { - case NoteInfo::VAL_TIME: - delta = val - tickOffset; - tickOffset = val; - break; - case NoteInfo::VAL_LEN: - delta = val - lenOffset; - lenOffset = val; - break; - case NoteInfo::VAL_VELON: - delta = val - veloOnOffset; - veloOnOffset = val; - break; - case NoteInfo::VAL_VELOFF: - delta = val - veloOffOffset; - veloOffOffset = val; - break; - case NoteInfo::VAL_PITCH: - delta = val - pitchOffset; - pitchOffset = val; - break; - } - if (delta) - canvas->modifySelected(type, delta); - } - } -//--------------------------------------------------------- -// addCtrl -//--------------------------------------------------------- +void ScoreCanvas::add_staves(PartList* pl, bool all_in_one) +{ + staff_t staff; + + if (all_in_one) + { + staff.parts.clear(); + for (ciPart part_it=pl->begin(); part_it!=pl->end(); part_it++) + staff.parts.insert(part_it->second); + + staff.split_note=SPLIT_NOTE; + + staff.type=GRAND_TOP; //FINDMICH + staff.clef=VIOLIN; + staffs.push_back(staff); + + staff.type=GRAND_BOTTOM; + staff.clef=BASS; + staffs.push_back(staff); + } + else + { + set<Track*> tracks; + + for (ciPart it=pl->begin(); it!=pl->end(); it++) + tracks.insert(it->second->track()); + + for (set<Track*>::iterator it=tracks.begin(); it!=tracks.end(); it++) + { + staff.parts.clear(); + for (ciPart part_it=pl->begin(); part_it!=pl->end(); part_it++) + if (part_it->second->track() == *it) + staff.parts.insert(part_it->second); + + staff.split_note=SPLIT_NOTE; + + staff.type=GRAND_TOP; //FINDMICH + staff.clef=VIOLIN; + staffs.push_back(staff); + + staff.type=GRAND_BOTTOM; + staff.clef=BASS; + staffs.push_back(staff); + } + } + + recalc_staff_pos(); + song_changed(0); +} -CtrlEdit* ScoreEdit::addCtrl() - { - ///CtrlEdit* ctrlEdit = new CtrlEdit(splitter, this, xscale, false, "pianoCtrlEdit"); - CtrlEdit* ctrlEdit = new CtrlEdit(ctrlLane/*splitter*/, this, xscale, false, "pianoCtrlEdit"); // ccharrett - connect(tools2, SIGNAL(toolChanged(int)), ctrlEdit, SLOT(setTool(int))); - connect(hscroll, SIGNAL(scrollChanged(int)), ctrlEdit, SLOT(setXPos(int))); - connect(hscroll, SIGNAL(scaleChanged(int)), ctrlEdit, SLOT(setXMag(int))); - connect(ctrlEdit, SIGNAL(timeChanged(unsigned)), SLOT(setTime(unsigned))); - connect(ctrlEdit, SIGNAL(destroyedCtrl(CtrlEdit*)), SLOT(removeCtrl(CtrlEdit*))); - connect(ctrlEdit, SIGNAL(yposChanged(int)), toolbar, SLOT(setInt(int))); - - ctrlEdit->setTool(tools2->curTool()); - ctrlEdit->setXPos(hscroll->pos()); - ctrlEdit->setXMag(hscroll->getScaleValue()); - - ctrlEdit->show(); - ctrlEditList.push_back(ctrlEdit); - return ctrlEdit; - } -//--------------------------------------------------------- -// removeCtrl -//--------------------------------------------------------- +ScoreCanvas::ScoreCanvas(MidiEditor* pr, QWidget* parent, + int sx, int sy) : View(parent, sx, sy) +{ + editor = pr; + setFocusPolicy(Qt::StrongFocus); + setBg(Qt::white); + + setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding); + + load_pixmaps(); + + x_pos=0; + x_left=0; + dragging=false; + mouse_erases_notes=false; + mouse_inserts_notes=true; + + curr_part=editor->parts()->begin()->second; //TODO FINDMICH + + last_len=384; + new_len=-1; + + dragging_staff=false; + + + scroll_speed=0; + scroll_pos=0; + connect (heartBeatTimer, SIGNAL(timeout()), SLOT(heartbeat_timer_event())); + + connect(song, SIGNAL(posChanged(int, unsigned, bool)), SLOT(pos_changed(int,unsigned,bool))); + + + + staff_menu=new QMenu(this); + + staffmode_treble_action = staff_menu->addAction(tr("Treble")); + connect(staffmode_treble_action, SIGNAL(triggered()), SLOT(staffmode_treble_slot())); + + staffmode_bass_action = staff_menu->addAction(tr("Bass")); + connect(staffmode_bass_action, SIGNAL(triggered()), SLOT(staffmode_bass_slot())); + + staffmode_both_action = staff_menu->addAction(tr("Grand Staff")); + connect(staffmode_both_action, SIGNAL(triggered()), SLOT(staffmode_both_slot())); + + remove_staff_action = staff_menu->addAction(tr("Remove staff")); + connect(remove_staff_action, SIGNAL(triggered()), SLOT(remove_staff_slot())); -void ScoreEdit::removeCtrl(CtrlEdit* ctrl) - { - for (std::list<CtrlEdit*>::iterator i = ctrlEditList.begin(); - i != ctrlEditList.end(); ++i) { - if (*i == ctrl) { - ctrlEditList.erase(i); - break; - } - } - } +} -//--------------------------------------------------------- -// closeEvent -//--------------------------------------------------------- +void ScoreCanvas::staffmode_treble_slot() +{ + set_staffmode(current_staff, MODE_TREBLE); +} -void ScoreEdit::closeEvent(QCloseEvent* e) - { - QSettings settings("MusE", "MusE-qt"); - //settings.setValue("ScoreEdit/geometry", saveGeometry()); - settings.setValue("ScoreEdit/windowState", saveState()); +void ScoreCanvas::staffmode_bass_slot() +{ + set_staffmode(current_staff, MODE_BASS); +} - emit deleted((unsigned long)this); - e->accept(); - } +void ScoreCanvas::staffmode_both_slot() +{ + set_staffmode(current_staff, MODE_BOTH); +} -//--------------------------------------------------------- -// readConfiguration -//--------------------------------------------------------- +void ScoreCanvas::remove_staff_slot() +{ + remove_staff(current_staff); +} -void ScoreEdit::readConfiguration(Xml& xml) - { - for (;;) { - Xml::Token token = xml.parse(); - if (token == Xml::Error || token == Xml::End) - break; - const QString& tag = xml.s1(); - switch (token) { - case Xml::TagStart: - if (tag == "quant") - _quantInit = xml.parseInt(); - else if (tag == "raster") - _rasterInit = xml.parseInt(); - else if (tag == "quantStrength") - _quantStrengthInit = xml.parseInt(); - else if (tag == "quantLimit") - _quantLimitInit = xml.parseInt(); - else if (tag == "quantLen") - _quantLenInit = xml.parseInt(); - else if (tag == "to") - _toInit = xml.parseInt(); - else if (tag == "colormode") - colorModeInit = xml.parseInt(); - else if (tag == "width") - _widthInit = xml.parseInt(); - else if (tag == "height") - _heightInit = xml.parseInt(); - else - xml.unknown("ScoreEdit"); - break; - case Xml::TagEnd: - if (tag == "ScoreEdit") - return; - default: - break; - } - } - } +void ScoreCanvas::set_staffmode(list<staff_t>::iterator it, staff_mode_t mode) +{ + if (it->type == GRAND_BOTTOM) + { + it--; + if (it->type!=GRAND_TOP) + cout << "THIS SHOULD NEVER HAPPEN: grand_bottom without top!"<<endl; + } + + if (it->type==GRAND_TOP) + { + list<staff_t>::iterator tmp=it; + tmp++; + if (tmp->type!=GRAND_BOTTOM) + cout << "THIS SHOULD NEVER HAPPEN: grand_top without bottom!"<<endl; + staffs.erase(tmp); + } + + switch (mode) + { + case MODE_TREBLE: + it->type=NORMAL; + it->clef=VIOLIN; + break; + + case MODE_BASS: + it->type=NORMAL; + it->clef=BASS; + break; + + case MODE_BOTH: + it->type=GRAND_BOTTOM; + it->clef=BASS; + it->split_note=SPLIT_NOTE; + + staffs.insert(it, staff_t(GRAND_TOP, VIOLIN, it->parts, it->split_note)); + break; + + default: + cout << "ILLEGAL FUNCTION CALL: invalid mode in set_staffmode" << endl; + } + + recalc_staff_pos(); + song_changed(0); +} -//--------------------------------------------------------- -// writeConfiguration -//--------------------------------------------------------- +void ScoreCanvas::remove_staff(list<staff_t>::iterator it) +{ + if (it->type == GRAND_BOTTOM) + { + it--; + if (it->type!=GRAND_TOP) + cout << "THIS SHOULD NEVER HAPPEN: grand_bottom without top!"<<endl; + } + + if (it->type == NORMAL) + { + staffs.erase(it); + } + else if (it->type == GRAND_TOP) + { + staffs.erase(it++); + if (it->type!=GRAND_BOTTOM) + cout << "THIS SHOULD NEVER HAPPEN: grand_top without bottom!"<<endl; + staffs.erase(it); + } + + recalc_staff_pos(); + song_changed(0); +} -void ScoreEdit::writeConfiguration(int level, Xml& xml) - { - xml.tag(level++, "ScoreEdit"); - xml.intTag(level, "quant", _quantInit); - xml.intTag(level, "raster", _rasterInit); - xml.intTag(level, "quantStrength", _quantStrengthInit); - xml.intTag(level, "quantLimit", _quantLimitInit); - xml.intTag(level, "quantLen", _quantLenInit); - xml.intTag(level, "to", _toInit); - xml.intTag(level, "width", _widthInit); - xml.intTag(level, "height", _heightInit); - xml.intTag(level, "colormode", colorModeInit); - xml.etag(level, "ScoreEdit"); - } +void ScoreCanvas::merge_staves(list<staff_t>::iterator dest, list<staff_t>::iterator src) +{ + if (dest->type == GRAND_BOTTOM) + { + dest--; + if (dest->type!=GRAND_TOP) + cout << "THIS SHOULD NEVER HAPPEN: grand_bottom without top!"<<endl; + } + + if (src->type == GRAND_BOTTOM) + { + src--; + if (src->type!=GRAND_TOP) + cout << "THIS SHOULD NEVER HAPPEN: grand_bottom without top!"<<endl; + } + + if (dest==src) //dragged to itself? + return; + + + dest->parts.insert(src->parts.begin(), src->parts.end()); + + if (dest->type == GRAND_TOP) + { + dest++; + if (dest->type != GRAND_BOTTOM) + cout << "THIS SHOULD NEVER HAPPEN: grand_top without bottom!"<<endl; + dest->parts.insert(src->parts.begin(), src->parts.end()); + } + + remove_staff(src); + + recalc_staff_pos(); + song_changed(0); +} -//--------------------------------------------------------- -// soloChanged -// signal from solo button -//--------------------------------------------------------- -void ScoreEdit::soloChanged(bool flag) - { - audio->msgSetSolo(canvas->track(), flag); - song->update(SC_SOLO); - } +void ScoreCanvas::song_changed(int) +{ + cout << "song changed!" << endl; + + calc_pos_add_list(); + + for (list<staff_t>::iterator it=staffs.begin(); it!=staffs.end(); it++) + { + it->create_appropriate_eventlist(it->parts); + it->create_itemlist(); + it->process_itemlist(); // do note- and rest-grouping and collision avoiding + it->calc_item_pos(); + } + + redraw(); + cout << "song had changed, recalculation complete" << endl; + + emit canvas_width_changed(canvas_width()); +} -//--------------------------------------------------------- -// setRaster -//--------------------------------------------------------- +int ScoreCanvas::canvas_width() +{ + //return tick_to_x(staffs.begin()->itemlist.rbegin()->first); + return tick_to_x(SONG_LENGTH); +} -void ScoreEdit::setRaster(int val) - { - _rasterInit = val; - MidiEditor::setRaster(val); - canvas->redrawGrid(); - canvas->setFocus(); // give back focus after kb input - } +int ScoreCanvas::viewport_width() +{ + return (width() - x_left); +} -//--------------------------------------------------------- -// setQuant -//--------------------------------------------------------- +string IntToStr(int i) +{ + ostringstream s; + s<<i; + return s.str(); +} -void ScoreEdit::setQuant(int val) - { - _quantInit = val; - MidiEditor::setQuant(val); - canvas->setFocus(); - } +void color_image(QImage& img, const QColor& color) +{ + uchar* ptr=img.bits(); + int bytes=img.byteCount(); + int r,g,b; + color.getRgb(&r,&g,&b); + + for (int i=0; i<bytes/4; i++) + { + QRgb* rgb=((QRgb*)ptr); + (*rgb) = qRgba(r,g,b,qAlpha(*rgb)); + + ptr+=4; + } +} -//--------------------------------------------------------- -// writeStatus -//--------------------------------------------------------- +void load_colored_pixmaps(string file, QPixmap* array) +{ + QString fn(file.c_str()); + QImage img(fn); + + color_image(img, Qt::black); + array[BLACK_PIXMAP]=QPixmap::fromImage(img); + + color_image(img, Qt::red); + array[HIGHLIGHTED_PIXMAP]=QPixmap::fromImage(img); + + + for (int color_index=0;color_index<NUM_PARTCOLORS; color_index++) + { + color_image(img, config.partColors[color_index]); + array[color_index]=QPixmap::fromImage(img); + } +} -void ScoreEdit::writeStatus(int level, Xml& xml) const - { - writePartList(level, xml); - xml.tag(level++, "ScoreEdit"); - MidiEditor::writeStatus(level, xml); - splitter->writeStatus(level, xml); - hsplitter->writeStatus(level, xml); - - for (std::list<CtrlEdit*>::const_iterator i = ctrlEditList.begin(); - i != ctrlEditList.end(); ++i) { - (*i)->writeStatus(level, xml); - } - - xml.intTag(level, "steprec", canvas->steprec()); - xml.intTag(level, "midiin", canvas->midiin()); - xml.intTag(level, "tool", int(canvas->tool())); - xml.intTag(level, "quantStrength", _quantStrength); - xml.intTag(level, "quantLimit", _quantLimit); - xml.intTag(level, "quantLen", _quantLen); - xml.intTag(level, "playEvents", _playEvents); - xml.intTag(level, "xpos", hscroll->pos()); - xml.intTag(level, "xmag", hscroll->mag()); - xml.intTag(level, "ypos", vscroll->pos()); - xml.intTag(level, "ymag", vscroll->mag()); - xml.tag(level, "/ScoreEdit"); - } -//--------------------------------------------------------- -// readStatus -//--------------------------------------------------------- -void ScoreEdit::readStatus(Xml& xml) - { - for (;;) { - Xml::Token token = xml.parse(); - if (token == Xml::Error || token == Xml::End) - break; - const QString& tag = xml.s1(); - switch (token) { - case Xml::TagStart: - if (tag == "steprec") { - int val = xml.parseInt(); - canvas->setSteprec(val); - srec->setChecked(val); - } - else if (tag == "midiin") { - int val = xml.parseInt(); - canvas->setMidiin(val); - midiin->setChecked(val); - } - else if (tag == "tool") { - int tool = xml.parseInt(); - canvas->setTool(tool); - tools2->set(tool); - } - else if (tag == "midieditor") - MidiEditor::readStatus(xml); - else if (tag == "ctrledit") { - CtrlEdit* ctrl = addCtrl(); - ctrl->readStatus(xml); - } - else if (tag == splitter->objectName()) - splitter->readStatus(xml); - else if (tag == hsplitter->objectName()) - hsplitter->readStatus(xml); - else if (tag == "quantStrength") - _quantStrength = xml.parseInt(); - else if (tag == "quantLimit") - _quantLimit = xml.parseInt(); - else if (tag == "quantLen") - _quantLen = xml.parseInt(); - else if (tag == "playEvents") { - _playEvents = xml.parseInt(); - canvas->playEvents(_playEvents); - speaker->setChecked(_playEvents); - } - else if (tag == "xmag") - hscroll->setMag(xml.parseInt()); - else if (tag == "xpos") - hscroll->setPos(xml.parseInt()); - else if (tag == "ymag") - vscroll->setMag(xml.parseInt()); - else if (tag == "ypos") - vscroll->setPos(xml.parseInt()); - else - xml.unknown("ScoreEdit"); - break; - case Xml::TagEnd: - if (tag == "ScoreEdit") { - _quantInit = _quant; - _rasterInit = _raster; - toolbar->setRaster(_raster); - toolbar->setQuant(_quant); - canvas->redrawGrid(); - return; - } - default: - break; - } - } - } - -static int rasterTable[] = { - //-9----8- 7 6 5 4 3(1/4) 2 1 - 4, 8, 16, 32, 64, 128, 256, 512, 1024, // triple - 6, 12, 24, 48, 96, 192, 384, 768, 1536, - 9, 18, 36, 72, 144, 288, 576, 1152, 2304 // dot - }; +void ScoreCanvas::load_pixmaps() +{ + if (!pixmaps_loaded) + { + cout << "loading pixmaps..." << endl; + + pix_whole=new QPixmap[NUM_PARTCOLORS+2]; + pix_half=new QPixmap[NUM_PARTCOLORS+2]; + pix_quarter=new QPixmap[NUM_PARTCOLORS+2]; + pix_dot=new QPixmap[NUM_PARTCOLORS+2]; + pix_b=new QPixmap[NUM_PARTCOLORS+2]; + pix_sharp=new QPixmap[NUM_PARTCOLORS+2]; + pix_noacc=new QPixmap[NUM_PARTCOLORS+2]; + pix_num=new QPixmap[10]; + + pix_r1=new QPixmap; + pix_r2=new QPixmap; + pix_r4=new QPixmap; + pix_r8=new QPixmap; + pix_r16=new QPixmap; + + pix_clef_violin=new QPixmap; + pix_clef_bass=new QPixmap; + + pix_flag_up=new QPixmap[4]; + pix_flag_down=new QPixmap[4]; + + + + + load_colored_pixmaps(FONT_PATH "whole.png", pix_whole); + load_colored_pixmaps(FONT_PATH "half.png", pix_half); + load_colored_pixmaps(FONT_PATH "quarter.png", pix_quarter); + load_colored_pixmaps(FONT_PATH "dot.png", pix_dot); + load_colored_pixmaps(FONT_PATH "acc_none.png", pix_noacc); + load_colored_pixmaps(FONT_PATH "acc_sharp.png", pix_sharp); + load_colored_pixmaps(FONT_PATH "acc_b.png", pix_b); + + pix_r1->load(FONT_PATH "rest1.png"); + pix_r2->load(FONT_PATH "rest2.png"); + pix_r4->load(FONT_PATH "rest4.png"); + pix_r8->load(FONT_PATH "rest8.png"); + pix_r16->load(FONT_PATH "rest16.png"); + pix_flag_up[0].load(FONT_PATH "flags8u.png"); + pix_flag_up[1].load(FONT_PATH "flags16u.png"); + pix_flag_up[2].load(FONT_PATH "flags32u.png"); + pix_flag_up[3].load(FONT_PATH "flags64u.png"); + pix_flag_down[0].load(FONT_PATH "flags8d.png"); + pix_flag_down[1].load(FONT_PATH "flags16d.png"); + pix_flag_down[2].load(FONT_PATH "flags32d.png"); + pix_flag_down[3].load(FONT_PATH "flags64d.png"); + + pix_clef_violin->load(FONT_PATH "clef_violin_big.png"); + pix_clef_bass->load(FONT_PATH "clef_bass_big.png"); + + for (int i=0;i<10;i++) + pix_num[i].load(QString((string(FONT_PATH "")+IntToStr(i)+string(".png")).c_str())); + + pixmaps_loaded=true; + } +} -//--------------------------------------------------------- -// viewKeyPressEvent -//--------------------------------------------------------- -void ScoreEdit::keyPressEvent(QKeyEvent* event) - { - if (info->hasFocus()) { - event->ignore(); - return; - } - - int index; - int n = sizeof(rasterTable)/sizeof(*rasterTable); - for (index = 0; index < n; ++index) - if (rasterTable[index] == raster()) - break; - if (index == n) { - index = 0; - // raster 1 is not in table - } - int off = (index / 9) * 9; - index = index % 9; - - int val = 0; - - PianoCanvas* pc = (PianoCanvas*)canvas; - int key = event->key(); - - //if (event->state() & Qt::ShiftButton) - if (((QInputEvent*)event)->modifiers() & Qt::ShiftModifier) - key += Qt::SHIFT; - //if (event->state() & Qt::AltButton) - if (((QInputEvent*)event)->modifiers() & Qt::AltModifier) - key += Qt::ALT; - //if (event->state() & Qt::ControlButton) - if (((QInputEvent*)event)->modifiers() & Qt::ControlModifier) - key+= Qt::CTRL; - - if (key == Qt::Key_Escape) { - close(); - return; - } - else if (key == shortcuts[SHRT_TOOL_POINTER].key) { - tools2->set(PointerTool); - return; - } - else if (key == shortcuts[SHRT_TOOL_PENCIL].key) { - tools2->set(PencilTool); - return; - } - else if (key == shortcuts[SHRT_TOOL_RUBBER].key) { - tools2->set(RubberTool); - return; - } - else if (key == shortcuts[SHRT_TOOL_LINEDRAW].key) { - tools2->set(DrawTool); - return; - } - else if (key == shortcuts[SHRT_POS_INC].key) { - pc->pianoCmd(CMD_RIGHT); - return; - } - else if (key == shortcuts[SHRT_POS_DEC].key) { - pc->pianoCmd(CMD_LEFT); - return; - } - else if (key == shortcuts[SHRT_POS_INC_NOSNAP].key) { - pc->pianoCmd(CMD_RIGHT_NOSNAP); - return; - } - else if (key == shortcuts[SHRT_POS_DEC_NOSNAP].key) { - pc->pianoCmd(CMD_LEFT_NOSNAP); - return; - } - else if (key == shortcuts[SHRT_INSERT_AT_LOCATION].key) { - pc->pianoCmd(CMD_INSERT); - return; - } - else if (key == Qt::Key_Delete) { - pc->pianoCmd(CMD_DELETE); - return; - } - else if (key == shortcuts[SHRT_ZOOM_IN].key) { - int mag = hscroll->mag(); - int zoomlvl = ScrollScale::getQuickZoomLevel(mag); - if (zoomlvl < 23) - zoomlvl++; - - int newmag = ScrollScale::convertQuickZoomLevelToMag(zoomlvl); - hscroll->setMag(newmag); - //printf("mag = %d zoomlvl = %d newmag = %d\n", mag, zoomlvl, newmag); - return; - } - else if (key == shortcuts[SHRT_ZOOM_OUT].key) { - int mag = hscroll->mag(); - int zoomlvl = ScrollScale::getQuickZoomLevel(mag); - if (zoomlvl > 1) - zoomlvl--; - - int newmag = ScrollScale::convertQuickZoomLevelToMag(zoomlvl); - hscroll->setMag(newmag); - //printf("mag = %d zoomlvl = %d newmag = %d\n", mag, zoomlvl, newmag); - return; - } - else if (key == shortcuts[SHRT_GOTO_CPOS].key) { - PartList* p = this->parts(); - Part* first = p->begin()->second; - hscroll->setPos(song->cpos() - first->tick() ); - return; - } - else if (key == shortcuts[SHRT_SCROLL_LEFT].key) { - int pos = hscroll->pos() - config.division; - if (pos < 0) - pos = 0; - hscroll->setPos(pos); - return; - } - else if (key == shortcuts[SHRT_SCROLL_RIGHT].key) { - int pos = hscroll->pos() + config.division; - hscroll->setPos(pos); - return; - } - else if (key == shortcuts[SHRT_SET_QUANT_1].key) - val = rasterTable[8 + off]; - else if (key == shortcuts[SHRT_SET_QUANT_2].key) - val = rasterTable[7 + off]; - else if (key == shortcuts[SHRT_SET_QUANT_3].key) - val = rasterTable[6 + off]; - else if (key == shortcuts[SHRT_SET_QUANT_4].key) - val = rasterTable[5 + off]; - else if (key == shortcuts[SHRT_SET_QUANT_5].key) - val = rasterTable[4 + off]; - else if (key == shortcuts[SHRT_SET_QUANT_6].key) - val = rasterTable[3 + off]; - else if (key == shortcuts[SHRT_SET_QUANT_7].key) - val = rasterTable[2 + off]; - else if (key == shortcuts[SHRT_TOGGLE_TRIOL].key) - val = rasterTable[index + ((off == 0) ? 9 : 0)]; - else if (key == shortcuts[SHRT_EVENT_COLOR].key) { - if (colorMode == 0) - colorMode = 1; - else if (colorMode == 1) - colorMode = 2; - else - colorMode = 0; - setEventColorMode(colorMode); - return; - } - else if (key == shortcuts[SHRT_TOGGLE_PUNCT].key) - val = rasterTable[index + ((off == 18) ? 9 : 18)]; - - else if (key == shortcuts[SHRT_TOGGLE_PUNCT2].key) {//CDW - if ((off == 18) && (index > 2)) { - val = rasterTable[index + 9 - 1]; - } - else if ((off == 9) && (index < 8)) { - val = rasterTable[index + 18 + 1]; - } - else - return; - } - else { //Default: - event->ignore(); - return; - } - setQuant(val); - setRaster(val); - toolbar->setQuant(_quant); - toolbar->setRaster(_raster); - } -//--------------------------------------------------------- -// configQuant -//--------------------------------------------------------- +int modulo(int a, int b) // similar to a % b +{ + return (((a%b)+b)%b); +} -void ScoreEdit::configQuant() - { - if (!quantConfig) { - quantConfig = new QuantConfig(_quantStrength, _quantLimit, _quantLen); - connect(quantConfig, SIGNAL(setQuantStrength(int)), SLOT(setQuantStrength(int))); - connect(quantConfig, SIGNAL(setQuantLimit(int)), SLOT(setQuantLimit(int))); - connect(quantConfig, SIGNAL(setQuantLen(bool)), SLOT(setQuantLen(bool))); - } - quantConfig->show(); - } +int divide_floor(int a, int b) // similar to a / b +{ //TODO can be done better :/ + return int(floor(float(a)/float(b))); +} -//--------------------------------------------------------- -// setSteprec -//--------------------------------------------------------- +#define DEFAULT_REST_HEIGHT 6 // TODO -void ScoreEdit::setSteprec(bool flag) - { - canvas->setSteprec(flag); - if (flag == false) - midiin->setChecked(flag); - } -//--------------------------------------------------------- -// eventColorModeChanged -//--------------------------------------------------------- +bool operator< (const note_pos_t& a, const note_pos_t& b) +{ + if (a.height<b.height) return true; + if (a.height>b.height) return false; + return a.vorzeichen<b.vorzeichen; +} -void ScoreEdit::eventColorModeChanged(int mode) - { - colorMode = mode; - colorModeInit = colorMode; - - ((PianoCanvas*)(canvas))->setColorMode(colorMode); - } -//--------------------------------------------------------- -// setEventColorMode -//--------------------------------------------------------- + +int flo_quantize(int tick) +{ + //TODO quantizing must be done with the proper functions! + return int(nearbyint((float)tick / FLO_QUANT))*FLO_QUANT; +} + +int flo_quantize_floor(int tick) +{ + //TODO quantizing must be done with the proper functions, see above + return int(tick / FLO_QUANT) * FLO_QUANT; +} -void ScoreEdit::setEventColorMode(int mode) - { - colorMode = mode; - colorModeInit = colorMode; - - ///eventColor->setItemChecked(0, mode == 0); - ///eventColor->setItemChecked(1, mode == 1); - ///eventColor->setItemChecked(2, mode == 2); - evColorBlueAction->setChecked(mode == 0); - evColorPitchAction->setChecked(mode == 1); - evColorVelAction->setChecked(mode == 2); - - ((PianoCanvas*)(canvas))->setColorMode(colorMode); - } + +/* builds the event list used by the score editor. + * that list contains only note-on and -off, time-sig- and + * key-change events. + * it stores them sorted by their time (quantized); if more + * events with the same time occur, the NOTE-OFFs are + * put before the NOTE-ONs + * it only operates on parts which were selected in the + * arranger when the score viewer was started + * + * this abstracts the rest of the renderer from muse's internal + * data structures, making this easy to port to another application + */ +void staff_t::create_appropriate_eventlist(const set<Part*>& parts) +{ + using AL::sigmap; + using AL::iSigEvent; + + eventlist.clear(); + + // phase one: fill the list ----------------------------------------- + + //insert note on events + for (set<Part*>::const_iterator part_it=parts.begin(); part_it!=parts.end(); part_it++) + { + Part* part=*part_it; + EventList* el=part->events(); + + for (iEvent it=el->begin(); it!=el->end(); it++) + { + Event& event=it->second; + + if ( (event.isNote() && !event.isNoteOff()) && + ( ((type==GRAND_TOP) && (event.pitch() >= split_note)) || + ((type==GRAND_BOTTOM) && (event.pitch() < split_note)) || + (type==NORMAL) ) ) + { + unsigned begin, end; + begin=flo_quantize(event.tick()+part->tick()); + end=flo_quantize(event.endTick()+part->tick()); + cout <<"inserting note on at "<<begin<<" with pitch="<<event.pitch()<<" and len="<<end-begin<<endl; + eventlist.insert(pair<unsigned, FloEvent>(begin, FloEvent(begin,event.pitch(), event.velo(),end-begin,FloEvent::NOTE_ON,part,&it->second))); + } + //else ignore it + } + } + + //insert bars and time signatures + for (iSigEvent it=sigmap.begin(); it!=sigmap.end(); it++) + { + unsigned from=it->second->tick; + unsigned to=it->first; + unsigned ticks_per_measure=sigmap.ticksMeasure(it->second->tick); + + if (to > unsigned(SONG_LENGTH)) + { + cout << "time signature's end-of-validness is outside of our song, limiting it." << endl; + to=SONG_LENGTH; + } + + cout << "new signature from tick "<<from<<" to " << to << ": "<<it->second->sig.z<<"/"<<it->second->sig.n<<"; ticks per measure = "<<ticks_per_measure<<endl; + eventlist.insert(pair<unsigned, FloEvent>(from, FloEvent(from, FloEvent::TIME_SIG, it->second->sig.z, it->second->sig.n) ) ); + for (unsigned t=from; t<to; t+=ticks_per_measure) + eventlist.insert(pair<unsigned, FloEvent>(t, FloEvent(t,0,0,ticks_per_measure,FloEvent::BAR) ) ); + } + + //insert key changes + for (iKeyEvent it=keymap.begin(); it!=keymap.end(); it++) + eventlist.insert(pair<unsigned, FloEvent>(it->second.tick, FloEvent(it->second.tick,FloEvent::KEY_CHANGE, it->second.key ) ) ); + + + // phase two: deal with overlapping notes --------------------------- + ScoreEventList::iterator it, it2; + + //iterate through all note_on - events + for (it=eventlist.begin(); it!=eventlist.end(); it++) + if (it->second.type==FloEvent::NOTE_ON) + { + unsigned end_tick=it->first + it->second.len; + + //iterate though all (relevant) later note_ons which are + //at the same pitch. if there's a collision, shorten it's len + for (it2=it, it2++; it2!=eventlist.end() && it2->first < end_tick; it2++) + if ((it2->second.type==FloEvent::NOTE_ON) && (it2->second.pitch == it->second.pitch)) + it->second.len=it2->first - it->first; + } + + + // phase three: eliminate zero-length-notes ------------------------- + for (it=eventlist.begin(); it!=eventlist.end();) + if ((it->second.type==FloEvent::NOTE_ON) && (it->second.len<=0)) + eventlist.erase(it++); + else + it++; +} -//--------------------------------------------------------- -// clipboardChanged -//--------------------------------------------------------- -void ScoreEdit::clipboardChanged() - { - editPasteAction->setEnabled(QApplication::clipboard()->mimeData()->hasFormat(QString("text/x-muse-eventlist"))); - } +bool is_sharp_key(tonart_t t) +{ + return ((t>=SHARP_BEGIN) && (t<=SHARP_END)); +} +bool is_b_key(tonart_t t) +{ + return ((t>=B_BEGIN) && (t<=B_END)); +} -//--------------------------------------------------------- -// selectionChanged -//--------------------------------------------------------- +int n_accidentials(tonart_t t) +{ + if (is_sharp_key(t)) + return t-SHARP_BEGIN-1; + else + return t-B_BEGIN-1; +} -void ScoreEdit::selectionChanged() - { - bool flag = canvas->selectionSize() > 0; - editCutAction->setEnabled(flag); - editCopyAction->setEnabled(flag); - editDelEventsAction->setEnabled(flag); - } -//--------------------------------------------------------- -// setSpeaker -//--------------------------------------------------------- +//note needs to be 0..11 +//always assumes violin clef +//only for internal use +note_pos_t note_pos_(int note, tonart_t key) +{ + note_pos_t result; + //C CIS D DIS E F FIS G GIS A AIS H + int foo[12]={0,-1, 1,-1, 2,3,-1, 4,-1, 5, -1,6}; + + if ((note<0) || (note>=12)) + cout << "WARNING: ILLEGAL FUNCTION CALL (note_pos, note out of range)" << endl; + + if (foo[note]!=-1) + { + result.height=foo[note]; + result.vorzeichen=NONE; + } + else + { + if (is_sharp_key(key)) + { + result.height=foo[note-1]; + result.vorzeichen=SHARP; + } + else // if is_b_key + { + result.height=foo[note+1]; + result.vorzeichen=B; + } + } + + // Special cases for GES / FIS keys + if (key==GES) + { + // convert a H to a Ces + if (note==11) + { + result.height=12; + result.vorzeichen=B; + } + } + else if (key==FIS) + { + // convert a F to an Eis + if (note==5) + { + result.height=2; + result.vorzeichen=SHARP; + } + } + + return result; +} -void ScoreEdit::setSpeaker(bool val) - { - _playEvents = val; - canvas->playEvents(_playEvents); - } -//--------------------------------------------------------- -// resizeEvent -//--------------------------------------------------------- +// V -------------------------- <-- height=10 +// I C -------------------------- <-- height=8 +// O L -------------------------- <-- height=6 +// L E -------------------------- <-- height=4 +// I F -------------------------- <-- height=2 +// N --o-- <-- this is C4. height=0 -void ScoreEdit::resizeEvent(QResizeEvent* ev) - { - QWidget::resizeEvent(ev); - _widthInit = ev->size().width(); - _heightInit = ev->size().height(); - } +// the "spaces" in between the lines have odd numbers. +// that is, the space between line 2 and 4 is numbered 3. +// these numbers do not change when clef changes. line 2 +// is always the "bottom line" of the system. +// in violin clef, line 2 is E4 +// in bass clef, line 2 is G2 -/* -//--------------------------------------------------------- -// trackInfoScroll -//--------------------------------------------------------- +note_pos_t note_pos (unsigned note, tonart_t key, clef_t clef) +{ + int octave=(note/12)-1; //integer division. note is unsigned + note=note%12; + + //now octave contains the octave the note is in + //(A4 is the 440Hz tone. C4 is the "low C" in the violin clef + //and the "high C" in the bass clef. + //note contains 0 for C, 1 for Cis, ..., 11 for H (or B if you're not german) + + note_pos_t pos=note_pos_(note,key); + + switch (clef) //CLEF_MARKER + { + case VIOLIN: + pos.height=pos.height + (octave-4)*7; + break; + + case BASS: + pos.height=pos.height + (octave-3)*7 + 5; + break; + } + + return pos; +} -void ScoreEdit::trackInfoScroll(int y) - { - if (trackInfo->visibleWidget()) - trackInfo->visibleWidget()->move(0, -y); - } -*/ -//--------------------------------------------------------- -// initShortcuts -//--------------------------------------------------------- +int calc_len(int l, int d) +{ + // l=0,1,2 -> whole, half, quarter (think of 2^0, 2^1, 2^2) + // d=number of dots + + int tmp=0; + for (int i=0;i<=d;i++) + tmp+=TICKS_PER_WHOLE / (1 << (l+i)); + + return tmp; +} + +bool operator< (const note_len_t& a,const note_len_t& b) //TODO sane sorting order +{ + if (a.len<b.len) return true; + else if (a.dots<b.dots) return true; + else return false; +} -void ScoreEdit::initShortcuts() - { - editCutAction->setShortcut(shortcuts[SHRT_CUT].key); - editCopyAction->setShortcut(shortcuts[SHRT_COPY].key); - editPasteAction->setShortcut(shortcuts[SHRT_PASTE].key); - editDelEventsAction->setShortcut(shortcuts[SHRT_DELETE].key); - - selectAllAction->setShortcut(shortcuts[SHRT_SELECT_ALL].key); - selectNoneAction->setShortcut(shortcuts[SHRT_SELECT_NONE].key); - selectInvertAction->setShortcut(shortcuts[SHRT_SELECT_INVERT].key); - selectInsideLoopAction->setShortcut(shortcuts[SHRT_SELECT_ILOOP].key); - selectOutsideLoopAction->setShortcut(shortcuts[SHRT_SELECT_OLOOP].key); - selectPrevPartAction->setShortcut(shortcuts[SHRT_SELECT_PREV_PART].key); - selectNextPartAction->setShortcut(shortcuts[SHRT_SELECT_NEXT_PART].key); - - eventColor->menuAction()->setShortcut(shortcuts[SHRT_EVENT_COLOR].key); - //evColorBlueAction->setShortcut(shortcuts[ ].key); - //evColorPitchAction->setShortcut(shortcuts[ ].key); - //evColorVelAction->setShortcut(shortcuts[ ].key); - - funcOverQuantAction->setShortcut(shortcuts[SHRT_OVER_QUANTIZE].key); - funcNoteOnQuantAction->setShortcut(shortcuts[SHRT_ON_QUANTIZE].key); - funcNoteOnOffQuantAction->setShortcut(shortcuts[SHRT_ONOFF_QUANTIZE].key); - funcIterQuantAction->setShortcut(shortcuts[SHRT_ITERATIVE_QUANTIZE].key); - - funcConfigQuantAction->setShortcut(shortcuts[SHRT_CONFIG_QUANT].key); - - funcGateTimeAction->setShortcut(shortcuts[SHRT_MODIFY_GATE_TIME].key); - funcModVelAction->setShortcut(shortcuts[SHRT_MODIFY_VELOCITY].key); - funcCrescendoAction->setShortcut(shortcuts[SHRT_CRESCENDO].key); - funcTransposeAction->setShortcut(shortcuts[SHRT_TRANSPOSE].key); - funcThinOutAction->setShortcut(shortcuts[SHRT_THIN_OUT].key); - funcEraseEventAction->setShortcut(shortcuts[SHRT_ERASE_EVENT].key); - funcNoteShiftAction->setShortcut(shortcuts[SHRT_NOTE_SHIFT].key); - funcMoveClockAction->setShortcut(shortcuts[SHRT_MOVE_CLOCK].key); - funcCopyMeasureAction->setShortcut(shortcuts[SHRT_COPY_MEASURE].key); - funcEraseMeasureAction->setShortcut(shortcuts[SHRT_ERASE_MEASURE].key); - funcDelMeasureAction->setShortcut(shortcuts[SHRT_DELETE_MEASURE].key); - funcCreateMeasureAction->setShortcut(shortcuts[SHRT_CREATE_MEASURE].key); - funcSetFixedLenAction->setShortcut(shortcuts[SHRT_FIXED_LEN].key); - funcDelOverlapsAction->setShortcut(shortcuts[SHRT_DELETE_OVERLAPS].key); - - } -//--------------------------------------------------------- -// execDeliveredScript -//--------------------------------------------------------- -void ScoreEdit::execDeliveredScript(int id) + +int calc_measure_len(const list<int>& nums, int denom) { - //QString scriptfile = QString(INSTPREFIX) + SCRIPTSSUFFIX + deliveredScriptNames[id]; - QString scriptfile = song->getScriptPath(id, true); - song->executeScript(scriptfile.toAscii().data(), parts(), quant(), true); + int sum=0; + + for (list<int>::const_iterator it=nums.begin(); it!=nums.end(); it++) + sum+=*it; + + return 64* sum/denom; } -//--------------------------------------------------------- -// execUserScript -//--------------------------------------------------------- -void ScoreEdit::execUserScript(int id) +vector<int> create_emphasize_list(const list<int>& nums, int denom) { - QString scriptfile = song->getScriptPath(id, false); - song->executeScript(scriptfile.toAscii().data(), parts(), quant(), true); + cout << "creating emphasize list for "; + for (list<int>::const_iterator it=nums.begin(); it!=nums.end(); it++) + cout << *it << " "; + cout << "/ "<<denom; + + // |----- 8th -----| + int foo[]={4,7,6,7,5,7,6,7}; //if 64 changes, this also must change + int pos=0; + int len=calc_measure_len(nums, denom); + + vector<int> result(len); + + for (int i=0;i<len;i++) + result[i]=foo[i%8]; + + for (list<int>::const_iterator it=nums.begin(); it!=nums.end(); it++) + { + result[pos]=1; + for (int i=1;i<*it;i++) + result[pos + i*64/denom]=2; + pos+= *it * 64 / denom; + } + + result[0]=0; + + for (int i=0;i<len;i++) + { + if (i%8==0) + cout << endl<<i<<":\t"; + cout << result[i]<<" "; + } + cout << endl; + + return result; } -//--------------------------------------------------------- -// newCanvasWidth -//--------------------------------------------------------- +vector<int> create_emphasize_list(int num, int denom) //TODO FINDMICH +{ + list<int> nums; + + if (num%3 ==0) + { + for (int i=0;i<num/3;i++) + nums.push_back(3); + } + else if (num%2 ==0) + { + for (int i=0;i<num/2;i++) + nums.push_back(2); + } + else // num is odd + { + for (int i=0;i<(num-3)/2;i++) + nums.push_back(2); + + nums.push_back(3); + } + + return create_emphasize_list(nums, denom); +} + +//quant_max must be in log(len), that is +//whole, half, quarter, eighth = 0,1,2,3 +//NOT: 1,2,4,8! (think of 2^foo) +//len is in ticks +list<note_len_t> parse_note_len(int len_ticks, int begin_tick, vector<int>& foo, bool allow_dots, bool allow_normal) +{ + list<note_len_t> retval; + + if (len_ticks<0) + cout << "WARNING: ILLEGAL FUNCTION CALL in parse_note_len: len_ticks < 0" << endl; + if (begin_tick<0) + cout << "WARNING: ILLEGAL FUNCTION CALL in parse_note_len: begin_tick < 0" << endl; + + if (allow_normal) + { + int dot_max = allow_dots ? quant_max : 0; + + for (int i=0;i<=quant_max;i++) + for (int j=0;j<=dot_max-i;j++) + if (calc_len(i,j) == len_ticks) + { + retval.push_back(note_len_t (i,j)); + return retval; + } + } + + //if !allow_normal or if the above failed + + int begin=begin_tick * 64 / TICKS_PER_WHOLE; + int len=len_ticks * 64 / TICKS_PER_WHOLE; + + unsigned pos=begin; + int len_done=0; + + while (len_done<len) + { + int len_now=0; + int last_number=foo[pos]; + + do {pos++;len_done++;len_now++;} while (! ((foo[pos]<=last_number) || (len_done==len) || (pos==foo.size())) ); + + len_now=len_now*TICKS_PER_WHOLE/64; + + cout << "add " << len_now << " ticks" << endl; + if (allow_dots) + { + int dot_max = quant_max; + + for (int i=0;i<=quant_max;i++) + for (int j=0;j<=dot_max-i;j++) + if (calc_len(i,j) == len_now) + { + retval.push_back(note_len_t (i,j)); + len_now=0; + } + } + + if (len_now) //the above failed or allow_dots=false + { + for (int i=0; i<=quant_max; i++) + { + int tmp=calc_len(i,0); + if (tmp <= len_now) + { + retval.push_back(note_len_t(i)); + len_now-=tmp; + if (len_now==0) break; + } + } + } + + if (len_now!=0) + cout << "WARNING: THIS SHOULD NEVER HAPPEN. wasn't able to split note len properly; len_now="<<len_now << endl; + + if (pos==foo.size()) //we cross measure boundaries? + pos=0; + } + + + return retval; +} + + +#define USED_CLEF VIOLIN + +#define YLEN 10 +#define NOTE_XLEN 10 +#define NOTE_SHIFT 3 +#define PIXELS_PER_WHOLE (320) //how many px are between two wholes? +#define PIXELS_PER_NOTEPOS (PIXELS_PER_WHOLE/quant_max_fraction) //how many px are between the smallest drawn beats? + +//PIXELS_PER_NOTEPOS must be greater or equal to 3*NOTE_XLEN + 2*NOTE_SHIFT +//because if tick 0 is at x=0: the notes can be shifted by NOTE_SHIFT. +//additionally, they can be moved by NOTE_XLEN (collision avoiding) +//then, they have their own width, which is NOTE_XLEN/2 into the x>0-area +//the same thing applies to the x<0-area + +// OOO +// | +// ^actual calculated x, without shifting or moving +// ^ and +// ^ : moved note (by XLEN) +// additionally, a shift is possible +// total_width = shift + move + note_xlen + move + shift, where move==note_xlen +// total_width = 2*shift + 3*note_xlen +// if total_width is greater than px_per_notepos, there will be collisions! + +#define NOTE_MOVE_X (PIXELS_PER_NOTEPOS/2) +//TODO richtige werte finden! +#define REST_AUSWEICH_X 10 +#define DOT_XDIST 6 +#define DOT_XBEGIN 10 +#define DOT_XBEGIN_REST 10 + +#define NUMBER_HEIGHT (pix_num[0].height()) + +//kann 0 oder 1 sein: +//bei notenkollisionen mit ungerader anzahl von kollidierenden +//wird immer so ausgewichen, dass möglichst wenige ausweichen müssen +//wenn die anzahl aber gerade ist, gibt es keine "bessere" lösung +//in dem fall werden immer die geraden (0) bzw. ungeraden (1) +//ausweichen. +#define AUSWEICHEN_BEVORZUGT 0 + +#define STEM_LEN 30 + +#define DOTTED_RESTS true +#define UNSPLIT_RESTS false + +#define AUX_LINE_LEN 1.5 + +#define ACCIDENTIAL_DIST 11 +#define KEYCHANGE_ACC_DIST 9 +#define KEYCHANGE_ACC_LEFTDIST 9 +#define KEYCHANGE_ACC_RIGHTDIST 0 + + +#define stdmap std::map + +#define no_notepos note_pos_t() + +#define TIE_DIST 5 +#define TIE_HEIGHT 6 +#define TIE_THICKNESS 3 + +void ScoreCanvas::draw_tie (QPainter& p, int x1, int x4, int yo, bool up, QColor color) +{ + QPainterPath path; + + int y1, y2, y3; + + if (up) + { + y1 = yo - TIE_DIST; + y2 = y1 - TIE_HEIGHT; + y3=y2-TIE_THICKNESS; + } + else + { + y1 = yo + TIE_DIST; + y2 = y1 + TIE_HEIGHT; + y3=y2+TIE_THICKNESS; + } + + int x2 = x1 + (x4-x1)/4; + int x3 = x4 - (x4-x1)/4; + + path.moveTo(x1,y1); + path.cubicTo( x2,y2 , x3,y2 , x4,y1 ); + path.cubicTo( x3,y3 , x2,y3 , x1,y1 ); + + p.setPen(color); + p.setBrush(color); + + p.drawPath(path); +} + +void ScoreCanvas::draw_accidentials(QPainter& p, int x, int y_offset, const list<int>& acc_list, const QPixmap& pix) +{ + int n_acc_drawn=0; + + for (list<int>::const_iterator acc_it=acc_list.begin(); acc_it!=acc_list.end(); acc_it++) + { + int y_coord=2*YLEN - ( *acc_it -2)*YLEN/2; + draw_pixmap(p,x + n_acc_drawn*KEYCHANGE_ACC_DIST,y_offset + y_coord,pix); + n_acc_drawn++; + } +} + +void staff_t::create_itemlist() +{ + tonart_t tmp_key=C; + int lastevent=0; + int next_measure=-1; + int last_measure=-1; + vector<int> emphasize_list=create_emphasize_list(4,4); //actually unneccessary, for safety + + itemlist.clear(); + + for (ScoreEventList::iterator it=eventlist.begin(); it!=eventlist.end(); it++) + { + int t, pitch, len, velo, actual_tick; + FloEvent::typeEnum type; + t=it->first; + pitch=it->second.pitch; + velo=it->second.vel; + len=it->second.len; + type=it->second.type; + actual_tick=it->second.tick; + if (actual_tick==-1) actual_tick=t; + + note_pos_t notepos=note_pos(pitch,tmp_key,clef); //TODO einstellmöglichkeiten + + printf("FLO: t=%i\ttype=%i\tpitch=%i\tvel=%i\tlen=%i\n",it->first, it->second.type, it->second.pitch, it->second.vel, it->second.len); + cout << "\tline="<<notepos.height<<"\tvorzeichen="<<notepos.vorzeichen << endl; + + + if (type==FloEvent::BAR) + { + if (last_measure!=-1) //i.e.: "this is NOT the first bar" + { + if (lastevent==last_measure) //there was no note? + { + unsigned tmppos=(last_measure+t-FLO_QUANT)/2; + cout << "\tend-of-measure: this was an empty measure. inserting rest in between at t="<<tmppos << endl; + itemlist[tmppos].insert( FloItem(FloItem::REST,notepos,0,0) ); + itemlist[t].insert( FloItem(FloItem::REST_END,notepos,0,0) ); + } + else + { + // if neccessary, insert rest at between last note and end-of-measure + int rest=t-lastevent; + if (rest) + { + printf("\tend-of-measure: set rest at %i with len %i\n",lastevent,rest); + + list<note_len_t> lens=parse_note_len(rest,lastevent-last_measure,emphasize_list,DOTTED_RESTS,UNSPLIT_RESTS); + unsigned tmppos=lastevent; + for (list<note_len_t>::iterator x=lens.begin(); x!=lens.end(); x++) + { + cout << "\t\tpartial rest with len="<<x->len<<", dots="<<x->dots<<endl; + itemlist[tmppos].insert( FloItem(FloItem::REST,notepos,x->len,x->dots) ); + tmppos+=calc_len(x->len,x->dots); + itemlist[tmppos].insert( FloItem(FloItem::REST_END,notepos,0,0) ); + } + } + } + } + + lastevent=t; + last_measure=t; + next_measure=t+len; + + itemlist[t].insert( FloItem(FloItem::BAR,no_notepos,0,0) ); + } + else if (type==FloEvent::NOTE_ON) + { + int rest=t-lastevent; + if (rest) + { + printf("\tset rest at %i with len %i\n",lastevent,rest); + // no need to check if the rest crosses measure boundaries; + // it can't. + + list<note_len_t> lens=parse_note_len(rest,lastevent-last_measure,emphasize_list,DOTTED_RESTS,UNSPLIT_RESTS); + unsigned tmppos=lastevent; + for (list<note_len_t>::iterator x=lens.begin(); x!=lens.end(); x++) + { + cout << "\t\tpartial rest with len="<<x->len<<", dots="<<x->dots<<endl; + itemlist[tmppos].insert( FloItem(FloItem::REST,notepos,x->len,x->dots) ); + tmppos+=calc_len(x->len,x->dots); + itemlist[tmppos].insert( FloItem(FloItem::REST_END,notepos,0,0) ); + } + } + + + + printf("\tset note at %i with len=%i\n", t, len); + + int tmplen; + bool tied_note; + + // if the note exceeds the current measure, split it. + if (t+len>next_measure) + { + tmplen=next_measure-t; + tied_note=true; + + //append the "remainder" of the note to our EventList, so that + //it gets processed again when entering the new measure + int newlen=len-tmplen; + eventlist.insert(pair<unsigned, FloEvent>(next_measure, FloEvent(actual_tick,pitch, velo,0,FloEvent::NOTE_OFF, it->second.source_part, it->second.source_event))); + eventlist.insert(pair<unsigned, FloEvent>(next_measure, FloEvent(actual_tick,pitch, velo,newlen,FloEvent::NOTE_ON, it->second.source_part, it->second.source_event))); + + cout << "\t\tnote was split to length "<<tmplen<<" + " << newlen<<endl; + } + else + { + tmplen=len; + tied_note=false; + + cout << "\t\tinserting NOTE OFF at "<<t+len<<endl; + eventlist.insert(pair<unsigned, FloEvent>(t+len, FloEvent(t+len,pitch, velo,0,FloEvent::NOTE_OFF,it->second.source_part, it->second.source_event))); + } + + list<note_len_t> lens=parse_note_len(tmplen,t-last_measure,emphasize_list,true,true); + unsigned tmppos=t; + int n_lens=lens.size(); + int count=0; + for (list<note_len_t>::iterator x=lens.begin(); x!=lens.end(); x++) + { + cout << "\t\tpartial note with len="<<x->len<<", dots="<<x->dots<<endl; + count++; + + bool tie; + + if (count<n_lens) + tie=true; // all notes except the last are always tied + else + tie=tied_note; // only the last respects tied_note + + itemlist[tmppos].insert( FloItem(FloItem::NOTE,notepos,x->len,x->dots, tie, actual_tick, it->second.source_part, it->second.source_event) ); + tmppos+=calc_len(x->len,x->dots); + itemlist[tmppos].insert( FloItem(FloItem::NOTE_END,notepos,0,0) ); + } + } + else if (type==FloEvent::NOTE_OFF) + { + lastevent=t; + } + else if (type==FloEvent::TIME_SIG) + { + cout << "inserting TIME SIGNATURE "<<it->second.num<<"/"<<it->second.denom<<" at "<<t<<endl; + itemlist[t].insert( FloItem(FloItem::TIME_SIG, it->second.num, it->second.denom) ); + + emphasize_list=create_emphasize_list(it->second.num, it->second.denom); + } + else if (type==FloEvent::KEY_CHANGE) + { + cout << "inserting KEY CHANGE ("<<it->second.tonart<<") at "<<t<<endl; + itemlist[t].insert( FloItem(FloItem::KEY_CHANGE, it->second.tonart) ); + tmp_key=it->second.tonart; // TODO FINDMICH MARKER das muss schöner werden + } + } +} + +void staff_t::process_itemlist() +{ + stdmap<int,int> occupied; + int last_measure=0; + vector<int> emphasize_list=create_emphasize_list(4,4); //unneccessary, only for safety + + //iterate through all times with items + for (ScoreItemList::iterator it2=itemlist.begin(); it2!=itemlist.end(); it2++) + { + set<FloItem, floComp>& curr_items=it2->second; + + cout << "at t="<<it2->first<<endl; + + // phase 0: keep track of active notes, rests ------------------- + // (and occupied lines) and the last measure + // and the current time signature (TODO FINDMICH) + for (set<FloItem, floComp>::iterator it=curr_items.begin(); it!=curr_items.end(); it++) + { + if ((it->type==FloItem::NOTE) || (it->type==FloItem::REST)) + occupied[it->pos.height]++; + else if ((it->type==FloItem::NOTE_END) || (it->type==FloItem::REST_END)) + occupied[it->pos.height]--; + else if (it->type==FloItem::BAR) + last_measure=it2->first; + else if (it->type==FloItem::TIME_SIG) + emphasize_list=create_emphasize_list(it->num, it->denom); + } + + cout << "occupied: "; + for (stdmap<int,int>::iterator i=occupied.begin(); i!=occupied.end(); i++) + if (i->second) cout << i->first << "("<<i->second<<") "; + cout << endl; + + + + + + // phase 1: group rests together ----------------------------------- + int n_groups=0; + bool dont_group=false; + + //iterate through all rests R at that time + // iterate through all rests X at that time below R + // if something is between X and R ("barrier"), stop + // else: group them together + for (set<FloItem, floComp>::iterator it=curr_items.begin(); it!=curr_items.end();) + { + //only operate on rests; ignore rests which are created by this code + //(can be seen on already_grouped) + if ((it->type==FloItem::REST) && (it->already_grouped==false)) + { + cout << "trying to group" << endl; + + int lastheight; + int height_cumulative=0; + int counter=0; + + lastheight=it->pos.height; + + set<FloItem, floComp>::iterator tmp; + for (tmp=it; tmp!=curr_items.end();) + { + cout << "checking if we can proceed with an item at height="<<tmp->pos.height<<endl; + + for (int i=lastheight+1; i<=tmp->pos.height-1; i++) + if (occupied[i]!=0) + { + cout << "we can NOT, because occ["<<i<<"] != 0" << endl; + //stop grouping that rest + goto get_out_here; + } + + lastheight=tmp->pos.height; + + // the current item is a rest with equal len? cool! + if (tmp->type==FloItem::REST && tmp->len==it->len && tmp->dots==it->dots) + { + // füge diese pause zur gruppe dazu und entferne sie von diesem set hier + // entfernen aber nur, wenn sie nicht it, also die erste pause ist, die brauchen wir noch! + cout << "\tgrouping rest at height="<<tmp->pos.height<<endl; + height_cumulative+=tmp->pos.height; + counter++; + if (tmp!=it) + curr_items.erase(tmp++); + else + tmp++; + } + else //it's something else? well, we can stop grouping that rest then + { + cout << "we can NOT, because that item is not a rest" << endl; + //stop grouping that rest + goto get_out_here; + } + } + cout << "no items to proceed on left, continuing" << endl; + get_out_here: + + n_groups++; + + // entferne it vom set und + // füge eine pause mit dem "mittelwert" ein. + // occupied und die "_END"-events bleiben unberührt + + FloItem temp=*it; + temp.already_grouped=true; + + // have we grouped all available rests into one single? + if ( (n_groups==1) && (tmp==curr_items.end()) && !dont_group) + { + cout << "wow, we were able to group all rests into one single" << endl; + if (temp.len==0) //the whole rest is shifted one line (one space and one line) + temp.pos.height=DEFAULT_REST_HEIGHT+2; + else + temp.pos.height=DEFAULT_REST_HEIGHT; + } + else + { + cout << "creating group #"<<n_groups<<endl; + temp.pos.height=nearbyint((float)height_cumulative/counter); + } + + // do NOT first insert, then erase, because if temp.height == + // it->height, the set considers temp and it equal (it doesn't + // take already_grouped into account) + // the result of this: insert does nothing, and erase erases + // the item. effect: you don't have the rest at all + curr_items.erase(it++); + + cout << "replacing all grouped rests with a rest at height="<<temp.pos.height<<endl; + + curr_items.insert(temp); + } + else + { + if (it->type==FloItem::NOTE) + dont_group=true; + + it++; + } + } + + + + + + // phase 2: avoid collisions of items ------------------------------ + set<FloItem, floComp>::iterator lastit, groupbegin, invalid; + invalid=curr_items.end(); + lastit=invalid; + groupbegin=invalid; + int count; + + //TODO FINDMICH MARKER: is "grouping" notes and rests together okay? + // or is it better to ignore rests when grouping? + for (set<FloItem, floComp>::iterator it=curr_items.begin(); it!=curr_items.end(); it++) + if ( (it->type==FloItem::NOTE) || (it->type==FloItem::REST) ) + { + if (lastit != invalid) + { + if (it->pos.height == lastit->pos.height+1) // they would collide? + { + if (groupbegin==invalid) // we have no group atm? + { + groupbegin=lastit; // start a new group + count=1; // because lastit has to be taken into account. + // for "it", there's a count++ later + } + + // the following will work even on start-new-group, + // because lastit will be "untouched", and that's why + // still be initalized to "false" + it->ausweich=!lastit->ausweich; + + count++; + } + else + { + if (groupbegin!=invalid) //this is the first item which + { //doesn't belong to the previous group any more + if (count%2 == 0) //count is even? + if (modulo(groupbegin->pos.height, 2) == AUSWEICHEN_BEVORZUGT) + for (set<FloItem, floComp>::iterator tmp=groupbegin; tmp!=it; tmp++) + tmp->ausweich=!tmp->ausweich; + + groupbegin=invalid; + } + // else: everything is ok :) + } + } + + lastit=it; + } + + // this could be the case if the last processed item before end() + // still belonged to a group. finalize this last group as well: + if (groupbegin!=invalid) + { + if (count%2 == 0) //count is even? + if (modulo(groupbegin->pos.height, 2) == AUSWEICHEN_BEVORZUGT) + for (set<FloItem, floComp>::iterator tmp=groupbegin; tmp!=curr_items.end(); tmp++) + tmp->ausweich=!tmp->ausweich; + } + // else: everything is ok :) + + + + + + // phase 3: group notes by their length and ------------------------ + // find out appropriate stem directions +group_them_again: + stdmap<int, cumulative_t> lengths; + bool has_whole=false; + + // find out which note lengths are present at that time + for (set<FloItem, floComp>::iterator it=curr_items.begin(); it!=curr_items.end(); it++) + if (it->type==FloItem::NOTE) + lengths[it->len].add(it->pos.height); + + cout << "note lengths at that time are:"; + for (stdmap<int, cumulative_t>::iterator it=lengths.begin(); it!=lengths.end(); it++) + cout << it->first << "("<< it->second.mean() <<") "; + cout << endl; + + if (lengths.erase(0)) // in case "0" is in the set, erase it + has_whole=true; // but remember there were whole notes + + if (lengths.size()==0) + { + cout << "no notes other than wholes, or no notes at all. we can relax" << endl; + } + else if (lengths.size()==1) + { + pair<const int, cumulative_t>& group=*(lengths.begin()); + stem_t stem; + int shift=0; + cout << "only one non-whole note group (len="<<group.first<<") at height="<<group.second.mean()<< endl; + + if (group.second.mean()>=6) + { + stem=DOWNWARDS; + if (has_whole) + shift=-1; + } + else + { + stem=UPWARDS; + if (has_whole) + shift=1; + } + + // for each note in that group + for (set<FloItem, floComp>::iterator it=curr_items.begin(); it!=curr_items.end(); it++) + if ( (it->type==FloItem::NOTE) && (it->len==group.first) ) + { + it->stem=stem; + it->shift=shift; + } + } + else if (lengths.size()==2) + { + stdmap<int, cumulative_t>::iterator it=lengths.begin(); + pair<const int, cumulative_t>& group1=*it; + it++; + pair<const int, cumulative_t>& group2=*it; + stem_t stem1, stem2; + int shift1=0, shift2=0; + cout << "two non-whole note group: len="<<group1.first<<" at height="<<group1.second.mean()<<" and len="<<group2.first<<" at height="<<group2.second.mean()<< endl; + + if (group1.second.mean()<group2.second.mean()) + { + stem1=DOWNWARDS; + stem2=UPWARDS; + shift1=-1; + if (has_whole) + shift2=1; + } + else + { + stem1=UPWARDS; + stem2=DOWNWARDS; + shift2=-1; + if (has_whole) + shift1=1; + } + + // for each note in group1 + for (set<FloItem, floComp>::iterator it=curr_items.begin(); it!=curr_items.end(); it++) + if ( (it->type==FloItem::NOTE) && (it->len==group1.first) ) + { + it->stem=stem1; + it->shift=shift1; + } + + // for each note in group2 + for (set<FloItem, floComp>::iterator it=curr_items.begin(); it!=curr_items.end(); it++) + if ( (it->type==FloItem::NOTE) && (it->len==group2.first) ) + { + it->stem=stem2; + it->shift=shift2; + } + } + else //more than 2 groups + { + //at this time, there are no iterators pointing to curr_items. + //this means, we can erase and insert safely into curr_items here. + + //group1 contains the longer notes, group2 the shorter + + int group1_n=lengths.size()/2; //round down + int group2_n=lengths.size()-group1_n; + + int group1_len, group2_len; + int group1_len_ticks, group2_len_ticks; + + + stdmap<int, cumulative_t>::iterator lit=lengths.begin(); + for (int i=0;i<group1_n-1;i++) lit++; //go to the group1_n-th entry + group1_len=lit->first; + for (int i=0;i<group2_n;i++) lit++; //go to the (group1_n+group2_n)-th entry (i.e., the last before end() ) + group2_len=lit->first; + + group1_len_ticks=calc_len(group1_len,0); + group2_len_ticks=calc_len(group2_len,0); + + cout << "we have "<<lengths.size()<<" groups. putting the "<<group1_n<<" longest and the "<<group2_n<<"shortest groups together"<<endl; + cout << "\tgroup1 will have len="<<group1_len<<" ("<<group1_len_ticks<<" ticks), group2 will have len="<<group2_len<<" ("<<group2_len_ticks<<" ticks)"<<endl; + + for (set<FloItem, floComp>::iterator it=curr_items.begin(); it!=curr_items.end();) + if (it->type==FloItem::NOTE) + { + //if *it belongs to group1 and has not already its destination length + cout << "\tprocessing note-item with len="<<it->len<<endl; + if (it->len<group1_len) + { + cout << "\t\thas to be changed to fit into group 1" << endl; + FloItem tmp=*it; + curr_items.erase(it++); + + int len_ticks_remaining=calc_len(tmp.len, tmp.dots)-group1_len_ticks; + bool tied_note=tmp.tied; + + + //shorten the current item to it's group's length + tmp.len=group1_len; + tmp.dots=0; + tmp.tied=true; + curr_items.insert(tmp); + + //create items for the remaining lengths (and a note_END for the just created shortened note) + int t=it2->first+group1_len_ticks; + + itemlist[t].insert( FloItem(FloItem::NOTE_END,tmp.pos,0,0) ); + + list<note_len_t> lens=parse_note_len(len_ticks_remaining,t-last_measure,emphasize_list,true,true); + unsigned tmppos=t; + int n_lens=lens.size(); + int count=0; + for (list<note_len_t>::iterator x=lens.begin(); x!=lens.end(); x++) + { + cout << "\t\twhile regrouping: partial note with len="<<x->len<<", dots="<<x->dots<<endl; + count++; + + bool tie; + + if (count<n_lens) + tie=true; // all notes except the last are always tied + else + tie=tied_note; // only the last respects tied_note + + itemlist[tmppos].insert( FloItem(FloItem::NOTE, tmp.pos,x->len,x->dots, tie, tmp.begin_tick, tmp.source_part, tmp.source_event) ); + tmppos+=calc_len(x->len,x->dots); + itemlist[tmppos].insert( FloItem(FloItem::NOTE_END, tmp.pos,0,0) ); + } + + } + //else if *it belongs to group2 and has not already its destination length + else if ((it->len<group2_len) && (it->len>group1_len)) + { + cout << "\t\thas to be changed to fit into group 2" << endl; + + FloItem tmp=*it; + curr_items.erase(it++); + + int len_ticks_remaining=calc_len(tmp.len, tmp.dots)-group2_len_ticks; + bool tied_note=tmp.tied; + + + //shorten the current item to it's group's length + tmp.len=group2_len; + tmp.dots=0; + tmp.tied=true; + curr_items.insert(tmp); + + //create items for the remaining lengths (and a note_END for the just created shortened note) + int t=it2->first+group2_len_ticks; + + itemlist[t].insert( FloItem(FloItem::NOTE_END,tmp.pos,0,0) ); + + list<note_len_t> lens=parse_note_len(len_ticks_remaining,t-last_measure,emphasize_list,true,true); + unsigned tmppos=t; + int n_lens=lens.size(); + int count=0; + for (list<note_len_t>::iterator x=lens.begin(); x!=lens.end(); x++) + { + cout << "\t\twhile regrouping: partial note with len="<<x->len<<", dots="<<x->dots<<endl; + count++; + + bool tie; + + if (count<n_lens) + tie=true; // all notes except the last are always tied + else + tie=tied_note; // only the last respects tied_note + + itemlist[tmppos].insert( FloItem(FloItem::NOTE,tmp.pos,x->len,x->dots, tie, tmp.begin_tick, tmp.source_part, tmp.source_event) ); + tmppos+=calc_len(x->len,x->dots); + itemlist[tmppos].insert( FloItem(FloItem::NOTE_END,tmp.pos,0,0) ); + } + + } + else //nothing to do? + { + cout << "\t\tnothing to do" << endl; + it++; + } + } + else + it++; + + goto group_them_again; //do it again + } + + } +} + +//draw a pixmap centered +void ScoreCanvas::draw_pixmap(QPainter& p, int x, int y, const QPixmap& pm) +{ + cout << "drawing pixmap width size="<<pm.width()<<"/"<<pm.height()<<" at "<<x<<"/"<<y<<endl; + p.drawPixmap(x-pm.width()/2,y-pm.height()/2,pm); +} + +QRect bbox_center(int x, int y, const QSize& size) +{ + //why x-foo/2+foo? because due to integer divisions, + // x-foo/2+foo can be smaller than x+foo/2! + return QRect(x-size.width()/2,y-size.height()/2,size.width(),size.height()); +} + +QRect FloItem::bbox() const +{ + return bbox_center(x,y,pix->size()); +} + +void ScoreCanvas::draw_note_lines(QPainter& p, int y) +{ + int xend=width(); + + p.setPen(Qt::black); + + for (int i=0;i<5;i++) + p.drawLine(0,y + i*YLEN - 2*YLEN,xend,y + i*YLEN - 2*YLEN); +} + + +void staff_t::calc_item_pos() +{ + tonart_t curr_key=C; + int pos_add=0; + + for (ScoreItemList::iterator it2=itemlist.begin(); it2!=itemlist.end(); it2++) + { + for (set<FloItem, floComp>::iterator it=it2->second.begin(); it!=it2->second.end();it++) + { + it->x=it2->first * PIXELS_PER_WHOLE/TICKS_PER_WHOLE +pos_add; + //if this changes, also change the line(s) with YLEN (but not all). don't change it. + it->y=2*YLEN - (it->pos.height-2)*YLEN/2; + + if (it->type==FloItem::NOTE) + { + it->x+=NOTE_MOVE_X + it->shift*NOTE_SHIFT; + + switch (it->len) + { + case 0: it->pix=pix_whole; break; + case 1: it->pix=pix_half; break; + default: it->pix=pix_quarter; break; + } + + it->stem_x=it->x; + + if (it->ausweich) + { + if ((it->stem==UPWARDS) || (it->len==0)) + it->x += it->pix->width()-1; //AUSWEICH_X + else + it->x -= it->pix->width()-1; //AUSWEICH_X + } + + //if there's a tie, try to find the tie's destination and set is_tie_dest + if (it->tied) + { + set<FloItem, floComp>::iterator dest; + set<FloItem, floComp>& desttime = itemlist[it2->first+calc_len(it->len,it->dots)]; + for (dest=desttime.begin(); dest!=desttime.end();dest++) + if ((dest->type==FloItem::NOTE) && (dest->pos==it->pos)) + { + dest->is_tie_dest=true; + dest->tie_from_x=it->x; + break; + } + + if (dest==desttime.end()) + cout << "THIS SHOULD NEVER HAPPEN: did not find destination note for tie!" << endl; + } + } + else if (it->type==FloItem::REST) + { + switch (it->len) + { + case 0: it->pix=pix_r1; break; + case 1: it->pix=pix_r2; break; + case 2: it->pix=pix_r4; break; + case 3: it->pix=pix_r8; break; + case 4: it->pix=pix_r16; break; + } + + it->x+=NOTE_MOVE_X + (it->ausweich ? REST_AUSWEICH_X : 0); //AUSWEICH_X + } + else if (it->type==FloItem::BAR) + { + //nothing to do :) + } + else if (it->type==FloItem::TIME_SIG) + { + int add=calc_timesig_width(it->num, it->denom); + pos_add+=add; + } + else if (it->type==FloItem::KEY_CHANGE) + { + tonart_t new_key=it->tonart; + + list<int> aufloes_list=calc_accidentials(curr_key, clef, new_key); + list<int> new_acc_list=calc_accidentials(new_key, clef); + + int n_acc_drawn=aufloes_list.size() + new_acc_list.size(); + pos_add+=n_acc_drawn*KEYCHANGE_ACC_DIST+ KEYCHANGE_ACC_LEFTDIST+ KEYCHANGE_ACC_RIGHTDIST; + + curr_key=new_key; + } + } + } +} + +void ScoreCanvas::calc_pos_add_list() +{ + using AL::sigmap; + using AL::iSigEvent; + + + pos_add_list.clear(); + + //process time signatures + for (iSigEvent it=sigmap.begin(); it!=sigmap.end(); it++) + pos_add_list[it->second->tick]+=calc_timesig_width(it->second->sig.z, it->second->sig.n); + + + //process key changes + tonart_t curr_key=C; + for (iKeyEvent it=keymap.begin(); it!=keymap.end(); it++) + { + tonart_t new_key=it->second.key; + list<int> aufloes_list=calc_accidentials(curr_key, VIOLIN, new_key); //clef argument is unneccessary + list<int> new_acc_list=calc_accidentials(new_key, VIOLIN); //in this case + int n_acc_drawn=aufloes_list.size() + new_acc_list.size(); + pos_add_list[it->second.tick]+=n_acc_drawn*KEYCHANGE_ACC_DIST+ KEYCHANGE_ACC_LEFTDIST+ KEYCHANGE_ACC_RIGHTDIST; + + curr_key=new_key; + } +} + +void ScoreCanvas::draw_items(QPainter& p, int y, staff_t& staff, int x1, int x2) +{ + int from_tick, to_tick; + ScoreItemList::iterator from_it, to_it; + + //drawing too much isn't bad. drawing too few is. + + from_tick=x_to_tick(x1); + from_it=staff.itemlist.lower_bound(from_tick); + //from_it now contains the first time which is fully drawn + //however, the previous beat could still be relevant, when it's + //partly drawn. so we decrement from_it + if (from_it!=staff.itemlist.begin()) from_it--; + + //decrement until we're at a time with a bar + //otherwise, drawing accidentials will be broken + while (from_it!=staff.itemlist.begin() && from_it->second.find(FloItem(FloItem::BAR))==from_it->second.end()) + from_it--; + + + to_tick=x_to_tick(x2); + to_it=staff.itemlist.upper_bound(to_tick); + //to_it now contains the first time which is not drawn at all any more + //however, a tie from 1:04 to 2:01 is stored in 2:01, not in 1:04, + //so for drawing ties, we need to increment to_it, so that the + //"first time not drawn at all any more" is the last which gets + //actually drawn. + if (to_it!=staff.itemlist.end()) to_it++; //do one tick more than neccessary. this will draw ties + + draw_items(p,y, staff, from_it, to_it); +} + +void ScoreCanvas::draw_items(QPainter& p, int y, staff_t& staff) +{ + draw_items(p,y, staff,x_pos,x_pos+width()-x_left); +} + +void ScoreCanvas::draw_items(QPainter& p, int y_offset, staff_t& staff, ScoreItemList::iterator from_it, ScoreItemList::iterator to_it) +{ + // init accidentials properly + vorzeichen_t curr_accidential[7]; + vorzeichen_t default_accidential[7]; + tonart_t curr_key; + + curr_key=key_at_tick(from_it->first); + list<int> new_acc_list=calc_accidentials(curr_key, staff.clef); + vorzeichen_t new_accidential = is_sharp_key(curr_key) ? SHARP : B; + + for (int i=0;i<7;i++) + curr_accidential[i]=default_accidential[i]=NONE; + + for (list<int>::iterator acc_it=new_acc_list.begin(); acc_it!=new_acc_list.end(); acc_it++) + default_accidential[*acc_it % 7]=curr_accidential[*acc_it % 7]=new_accidential; + + + + for (ScoreItemList::iterator it2=from_it; it2!=to_it; it2++) + { + cout << "at t="<<it2->first << endl; + + int upstem_y1 = -1, upstem_y2=-1, upstem_x=-1, upflag=-1; + int downstem_y1 = -1, downstem_y2=-1, downstem_x=-1, downflag=-1; + + for (set<FloItem, floComp>::iterator it=it2->second.begin(); it!=it2->second.end();it++) + { + if (it->type==FloItem::NOTE) + { + cout << "\tNOTE at line"<<it->pos.height<<" with acc.="<<it->pos.vorzeichen<<", len="<<pow(2,it->len); + for (int i=0;i<it->dots;i++) cout << "."; + cout << " , stem="; + if (it->stem==UPWARDS) + cout << "UPWARDS"; + else + cout << "DOWNWARDS"; + + cout << " , shift="<<it->shift<<", ausweich="<<it->ausweich<<", "; + if (!it->tied) cout << "un"; + cout << "tied, is_tie_dest="<<it->is_tie_dest<<endl; + + if (it->len!=0) //only for non-whole notes the stems are relevant! + { + if (it->stem==UPWARDS) + { + if (upstem_y1 == -1) + upstem_y1=it->y; + + upstem_y2=it->y; + + + if ((upflag!=-1) && (upflag!=it->len)) + cout << "WARNING: THIS SHOULD NEVER HAPPEN: upflag != this->flag" << endl; + upflag=it->len; + + if ((upstem_x!=-1) && (upstem_x!=it->stem_x )) + cout << "WARNING: THIS SHOULD NEVER HAPPEN: upstem_x != x_result" << endl; + upstem_x=it->stem_x; + } + else + { + if (downstem_y1 == -1) + downstem_y1=it->y; + + downstem_y2=it->y; + + + if ((downflag!=-1) && (downflag!=it->len)) + cout << "WARNING: THIS SHOULD NEVER HAPPEN: downflag != this->flag" << endl; + downflag=it->len; + + if ((downstem_x!=-1) && (downstem_x!=it->stem_x)) + cout << "WARNING: THIS SHOULD NEVER HAPPEN: downstem_x != x_result" << endl; + downstem_x=it->stem_x; //important: before the below calculation! + } + } + + + if (it->pos.height <= 0) //we need auxiliary lines on the bottom? + { + p.setPen(Qt::black); + for (int i=0; i>=it->pos.height; i-=2) + p.drawLine(it->x-it->pix->width()*AUX_LINE_LEN/2 -x_pos+x_left,y_offset + 2*YLEN - (i-2)*YLEN/2,it->x+it->pix->width()*AUX_LINE_LEN/2-x_pos+x_left,y_offset + 2*YLEN - (i-2)*YLEN/2); + } + else if (it->pos.height >= 12) //we need auxiliary lines on the top? + { + p.setPen(Qt::black); + for (int i=12; i<=it->pos.height; i+=2) + p.drawLine(it->x-it->pix->width()*AUX_LINE_LEN/2 -x_pos+x_left,y_offset + 2*YLEN - (i-2)*YLEN/2,it->x+it->pix->width()*AUX_LINE_LEN/2-x_pos+x_left,y_offset + 2*YLEN - (i-2)*YLEN/2); + } + + it->is_active= ( (song->cpos() >= it->source_event->tick() + it->source_part->tick()) && + (song->cpos() < it->source_event->endTick() + it->source_part->tick()) ); + + int color_index=it->source_part->colorIndex(); + + if (audio->isPlaying() && it->is_active) + color_index=HIGHLIGHTED_PIXMAP; + + draw_pixmap(p,it->x -x_pos+x_left,y_offset + it->y,it->pix[color_index]); + //TODO FINDMICH maybe draw a margin around bright colors? + //maybe draw the default color in black? + + //draw dots + + int x_dot=DOT_XBEGIN; + int y_dot; + if (modulo(it->pos.height, 2) == 0) //note is on a line? + y_dot=YLEN * 0.33; // actually 0.5, but that would be _exactly_ in the space + else //note is between two lines? + y_dot=YLEN * 0.1; + + if (it->stem==DOWNWARDS) + y_dot=-y_dot; + //else y_dot=y_dot; + + for (int i=0;i<it->dots;i++) + { + draw_pixmap(p,it->x+x_dot -x_pos+x_left,y_offset + it->y+y_dot,pix_dot[color_index]); + x_dot+=DOT_XDIST; + } + + + + //draw accidentials + if (it->pos.vorzeichen != curr_accidential[modulo(it->pos.height,7)]) + { + QPixmap* acc_pix; + switch (it->pos.vorzeichen) + { + case NONE: acc_pix=pix_noacc; break; + case SHARP: acc_pix=pix_sharp; break; + case B: acc_pix=pix_b; break; + } + draw_pixmap(p,it->x-ACCIDENTIAL_DIST -x_pos+x_left,y_offset + it->y, acc_pix[color_index]); + + curr_accidential[modulo(it->pos.height,7)]=it->pos.vorzeichen; + } + + + //if needed, draw tie + if (it->is_tie_dest) + { + cout << "drawing tie" << endl; + draw_tie(p,it->tie_from_x-x_pos+x_left,it->x -x_pos+x_left,y_offset + it->y, (it->len==0) ? true : (it->stem==DOWNWARDS) , config.partColors[color_index]); + // in english: "if it's a whole note, tie is upwards (true). if not, tie is upwards if + // stem is downwards and vice versa" + } + } + else if (it->type==FloItem::REST) + { + cout << "\tREST at line"<<it->pos.height<<" with len="<<pow(2,it->len); + for (int i=0;i<it->dots;i++) cout << "."; + cout << " , ausweich="<<it->ausweich<<endl; + + draw_pixmap(p,it->x -x_pos+x_left,y_offset + it->y,*it->pix); + + + //draw dots + + int x_dot=DOT_XBEGIN_REST; + int y_dot; + if (modulo(it->pos.height, 2) == 0) //rest is on a line? + y_dot=YLEN * 0.33; // actually 0.5, but that would be _exactly_ in the space + else //note is between two lines? + y_dot=YLEN * 0.1; + + if (it->len!=0) // all rests except the whole are treated as + y_dot=-y_dot; // if they had a downwards stem + + for (int i=0;i<it->dots;i++) + { + draw_pixmap(p,it->x+x_dot -x_pos+x_left,y_offset + it->y+y_dot,pix_dot[BLACK_PIXMAP]); + x_dot+=DOT_XDIST; + } + } + else if (it->type==FloItem::BAR) + { + cout << "\tBAR" << endl; + + p.setPen(Qt::black); + p.drawLine(it->x -x_pos+x_left,y_offset -2*YLEN,it->x -x_pos+x_left,y_offset +2*YLEN); + + for (int i=0;i<7;i++) + curr_accidential[i]=default_accidential[i]; + } + else if (it->type==FloItem::TIME_SIG) + { + cout << "\tTIME SIGNATURE: "<<it->num<<"/"<<it->denom<<endl; + + draw_timesig(p, it->x - x_pos+x_left, y_offset, it->num, it->denom); + } + else if (it->type==FloItem::KEY_CHANGE) + { + tonart_t new_key=it->tonart; + cout << "\tKEY CHANGE: from "<<curr_key<<" to "<<new_key<<endl; + + list<int> aufloes_list=calc_accidentials(curr_key, staff.clef, new_key); + list<int> new_acc_list=calc_accidentials(new_key, staff.clef); + + // vorzeichen aus curr_key auflösen + draw_accidentials(p, it->x + KEYCHANGE_ACC_LEFTDIST - x_pos+x_left, y_offset, aufloes_list, pix_noacc[BLACK_PIXMAP]); + + // alle vorzeichen aus new_key zeichnen + QPixmap* pix = is_sharp_key(new_key) ? &pix_sharp[BLACK_PIXMAP] : &pix_b[BLACK_PIXMAP]; + vorzeichen_t new_accidential = is_sharp_key(new_key) ? SHARP : B; + + draw_accidentials(p, it->x + aufloes_list.size()*KEYCHANGE_ACC_DIST + KEYCHANGE_ACC_LEFTDIST - x_pos+x_left, y_offset, new_acc_list, *pix); + + for (int i=0;i<7;i++) + curr_accidential[i]=default_accidential[i]=NONE; + + for (list<int>::iterator acc_it=new_acc_list.begin(); acc_it!=new_acc_list.end(); acc_it++) + default_accidential[*acc_it % 7]=curr_accidential[*acc_it % 7]=new_accidential; + + curr_key=new_key; + } + } + + p.setPen(Qt::black); + //note: y1 is bottom, y2 is top! + if (upstem_x!=-1) + { + upstem_x=upstem_x-pix_quarter[0].width()/2 +pix_quarter[0].width() -1; + p.drawLine(upstem_x -x_pos+x_left, y_offset + upstem_y1, upstem_x -x_pos+x_left, y_offset + upstem_y2-STEM_LEN); + + if (upflag>=3) //if the note needs a flag + p.drawPixmap(upstem_x -x_pos+x_left,y_offset + upstem_y2-STEM_LEN,pix_flag_up[upflag-3]); + } + if (downstem_x!=-1) + { + downstem_x=downstem_x-pix_quarter[0].width()/2; + p.drawLine(downstem_x -x_pos+x_left, y_offset + downstem_y1+STEM_LEN, downstem_x -x_pos+x_left, y_offset + downstem_y2); + + if (downflag>=3) //if the note needs a flag + p.drawPixmap(downstem_x -x_pos+x_left,y_offset + downstem_y1+STEM_LEN-pix_flag_down[downflag-3].height(),pix_flag_down[downflag-3]); + } + } +} + +bool ScoreCanvas::need_redraw_for_hilighting() +{ + for (list<staff_t>::iterator it=staffs.begin(); it!=staffs.end(); it++) + if (need_redraw_for_hilighting(it->itemlist)) return true; + + return false; +} + +bool ScoreCanvas::need_redraw_for_hilighting(ScoreItemList& itemlist) +{ + return need_redraw_for_hilighting(itemlist, x_pos,x_pos+width()-x_left); +} + +bool ScoreCanvas::need_redraw_for_hilighting(ScoreItemList& itemlist, int x1, int x2) +{ + int from_tick, to_tick; + ScoreItemList::iterator from_it, to_it; + + from_tick=x_to_tick(x1); + from_it=itemlist.lower_bound(from_tick); + //from_it now contains the first time which is fully drawn + //however, the previous beat could still be relevant, when it's + //partly drawn. so we decrement from_it + if (from_it!=itemlist.begin()) from_it--; + + to_tick=x_to_tick(x2); + to_it=itemlist.upper_bound(to_tick); + //to_it now contains the first time which is not drawn at all any more + + return need_redraw_for_hilighting(from_it, to_it); +} + +bool ScoreCanvas::need_redraw_for_hilighting(ScoreItemList::iterator from_it, ScoreItemList::iterator to_it) +{ + //if we aren't playing, there will never be a need for redrawing due to highlighting things + if (audio->isPlaying()==false) + return false; + + for (ScoreItemList::iterator it2=from_it; it2!=to_it; it2++) + for (set<FloItem, floComp>::iterator it=it2->second.begin(); it!=it2->second.end();it++) + if (it->type==FloItem::NOTE) + { + bool is_active= ( (song->cpos() >= it->source_event->tick() + it->source_part->tick()) && + (song->cpos() < it->source_event->endTick() + it->source_part->tick()) ); + if (it->is_active != is_active) + return true; + } + + return false; +} + +int clef_height(clef_t clef) +{ + switch (clef) //CLEF_MARKER + { + case VIOLIN: return 4; + case BASS: return 8; + default: + cout << "WARNING: ILLEGAL FUNCTION CALL in clef_height()" << endl; + return 6; + } +} + +#define TIMESIG_LEFTMARGIN 5 +#define TIMESIG_RIGHTMARGIN 5 +#define DIGIT_YDIST 9 +#define DIGIT_WIDTH 12 -void ScoreEdit::newCanvasWidth(int /*w*/) - { -/* - int nw = w + (vscroll->width() - 18); // 18 is the fixed width of the CtlEdit VScale widget. - if(nw < 1) - nw = 1; - - for (std::list<CtrlEdit*>::iterator i = ctrlEditList.begin(); - i != ctrlEditList.end(); ++i) { - // Changed by Tim. p3.3.7 - //(*i)->setCanvasWidth(w); - (*i)->setCanvasWidth(nw); - } - - updateHScrollRange(); -*/ - } +#define CLEF_LEFTMARGIN 5 +#define CLEF_RIGHTMARGIN 5 + +void ScoreCanvas::draw_preamble(QPainter& p, int y_offset, clef_t clef) +{ + int x_left_old=x_left; + int tick=x_to_tick(x_pos); + + // draw clef -------------------------------------------------------- + QPixmap* pix_clef= (clef==BASS) ? pix_clef_bass : pix_clef_violin; + int y_coord=2*YLEN - ( clef_height(clef) -2)*YLEN/2; + + draw_pixmap(p,CLEF_LEFTMARGIN + pix_clef->width()/2,y_offset + y_coord,*pix_clef); + + x_left= CLEF_LEFTMARGIN + pix_clef->width() + CLEF_RIGHTMARGIN + KEYCHANGE_ACC_LEFTDIST; + + + // draw accidentials ------------------------------------------------ + tonart_t key=key_at_tick(tick); + QPixmap* pix_acc=is_sharp_key(key) ? &pix_sharp[BLACK_PIXMAP] : &pix_b[BLACK_PIXMAP]; + list<int> acclist=calc_accidentials(key,clef); + + draw_accidentials(p,x_left, y_offset, acclist ,*pix_acc); + + x_left+=acclist.size()*KEYCHANGE_ACC_DIST + KEYCHANGE_ACC_RIGHTDIST + TIMESIG_LEFTMARGIN; + + + // draw time signature ---------------------------------------------- + timesig_t timesig=timesig_at_tick(tick); + + draw_timesig(p, x_left, y_offset, timesig.num, timesig.denom); + + x_left+=calc_timesig_width(timesig.num, timesig.denom)+TIMESIG_RIGHTMARGIN; + + // draw bar --------------------------------------------------------- + p.setPen(Qt::black); + p.drawLine(x_left,y_offset -2*YLEN,x_left,y_offset +2*YLEN); + + + if (x_left_old!=x_left) + emit viewport_width_changed(viewport_width()); +} + + +void ScoreCanvas::draw_timesig(QPainter& p, int x, int y_offset, int num, int denom) +{ + int num_width=calc_number_width(num); + int denom_width=calc_number_width(denom); + int width=((num_width > denom_width) ? num_width : denom_width); + int num_indent=(width-num_width)/2 + TIMESIG_LEFTMARGIN; + int denom_indent=(width-denom_width)/2 + TIMESIG_LEFTMARGIN; + + draw_number(p, x+num_indent, y_offset -DIGIT_YDIST, num); + draw_number(p, x+denom_indent, y_offset +DIGIT_YDIST, denom); +} + +int calc_timesig_width(int num, int denom) +{ + int num_width=calc_number_width(num); + int denom_width=calc_number_width(denom); + int width=((num_width > denom_width) ? num_width : denom_width); + return width+TIMESIG_LEFTMARGIN+TIMESIG_RIGHTMARGIN; +} + +int calc_number_width(int n) +{ + string str=IntToStr(n); + return (str.length()*DIGIT_WIDTH); +} + +void ScoreCanvas::draw_number(QPainter& p, int x, int y, int n) +{ + string str=IntToStr(n); + int curr_x=x+DIGIT_WIDTH/2; + + for (size_t i=0;i<str.length(); i++) + { + draw_pixmap(p, curr_x, y, pix_num[str[i]-'0']); + curr_x+=DIGIT_WIDTH; + } +} + + +void ScoreCanvas::draw(QPainter& p, const QRect&) +{ + cout <<"now in ScoreCanvas::draw"<<endl; + + + + p.setPen(Qt::black); + + for (list<staff_t>::iterator it=staffs.begin(); it!=staffs.end(); it++) + { + draw_note_lines(p,it->y_draw); + draw_preamble(p,it->y_draw, it->clef); + p.setClipRect(x_left+1,0,p.device()->width(),p.device()->height()); + draw_items(p,it->y_draw, *it); + p.setClipping(false); + } +} + + +list<int> calc_accidentials(tonart_t key, clef_t clef, tonart_t next_key) +{ + list<int> result; + + int violin_sharp_pos[]={10,7,11,8,5,9,6}; //CLEF_MARKER + int violin_b_pos[]={6,9,5,8,4,7,3}; + int bass_sharp_pos[]={8,5,9,6,3,7,4}; + int bass_b_pos[]={4,7,3,6,2,5,1}; + + int* accidential_pos; + + switch (clef) + { + case VIOLIN: accidential_pos = is_sharp_key(key) ? violin_sharp_pos : violin_b_pos; break; + case BASS: accidential_pos = is_sharp_key(key) ? bass_sharp_pos : bass_b_pos; break; + } + + int begin=0; + + if (is_sharp_key(key)==is_sharp_key(next_key)) //same kind of key (both b or both #)? + begin=n_accidentials(next_key); + else + begin=0; + + + int end=n_accidentials(key); + + for (int i=begin; i<end; i++) + result.push_back(accidential_pos[i]); + + return result; +} + + + + +int ScoreCanvas::tick_to_x(int t) +{ + int x=t*PIXELS_PER_WHOLE/TICKS_PER_WHOLE; + + for (std::map<int,int>::iterator it=pos_add_list.begin(); it!=pos_add_list.end() && it->first<=t; it++) + x+=it->second; + + return x; +} + +int ScoreCanvas::calc_posadd(int t) +{ + int result=0; + + for (std::map<int,int>::iterator it=pos_add_list.begin(); it!=pos_add_list.end() && it->first<t; it++) + result+=it->second; + + return result; +} + +//doesn't round mathematically correct, but i don't think this +//will be a problem, because a tick is pretty small +int ScoreCanvas::x_to_tick(int x) +{ + int t=TICKS_PER_WHOLE * x/PIXELS_PER_WHOLE; + int min_t=0; + + cout << "t="<<t<<endl; + + for (std::map<int,int>::iterator it=pos_add_list.begin(); it!=pos_add_list.end() && it->first<t; it++) + { + cout << "at pos_add event at t="<<it->first<<", add="<<it->second<<endl; + min_t=it->first; + x-=it->second; + t=TICKS_PER_WHOLE * x/PIXELS_PER_WHOLE; + } + + return t > min_t ? t : min_t; +} + +tonart_t ScoreCanvas::key_at_tick(int t_) +{ + unsigned int t= (t_>=0) ? t_ : 0; + + return keymap.key_at_tick(t); +} + +timesig_t ScoreCanvas::timesig_at_tick(int t_) +{ + timesig_t tmp; + unsigned int t= (t_>=0) ? t_ : 0; + + AL::sigmap.timesig(t, tmp.num, tmp.denom); + + return tmp; +} + +int ScoreCanvas::height_to_pitch(int h, clef_t clef) +{ + int foo[]={0,2,4,5,7,9,11}; + + switch(clef) //CLEF_MARKER + { + case VIOLIN: return foo[modulo(h,7)] + ( divide_floor(h,7)*12 ) + 60; + case BASS: return foo[modulo((h-5),7)] + ( divide_floor(h-5,7)*12 ) + 48; + default: + cout << "WARNING: THIS SHOULD NEVER HAPPEN: unknown clef in height_to_pitch" << endl; + return 60; + } +} + +int ScoreCanvas::height_to_pitch(int h, clef_t clef, tonart_t key) +{ + int add=0; + + list<int> accs=calc_accidentials(key,clef); + + for (list<int>::iterator it=accs.begin(); it!=accs.end(); it++) + { + if (modulo(*it,7) == modulo(h,7)) + { + add=is_sharp_key(key) ? 1 : -1; + break; + } + } + + return height_to_pitch(h,clef)+add; +} + +int ScoreCanvas::y_to_height(int y) +{ + return int(nearbyint(float(2*YLEN - y)*2.0/YLEN))+2 ; +} + +int ScoreCanvas::y_to_pitch(int y, int t, clef_t clef) +{ + return height_to_pitch(y_to_height(y), clef, key_at_tick(t)); +} + + +#define DRAG_INIT_DISTANCE 5 + +void ScoreCanvas::mousePressEvent (QMouseEvent* event) +{ + // den errechneten tick immer ABrunden! + // denn der "bereich" eines schlags geht von schlag_begin bis nächsterschlag_begin-1 + // noten werden aber genau in die mitte dieses bereiches gezeichnet + + list<staff_t>::iterator it=staff_at_y(event->y()); + + int y=event->y() - it->y_draw; + int x=event->x()+x_pos-x_left; + int tick=flo_quantize_floor(x_to_tick(x)); + //TODO quantizing must (maybe?) be done with the proper functions + + if (it!=staffs.end()) + { + if (event->x() <= x_left) //clicked in the preamble? + { + if (event->button() == Qt::RightButton) //right-click? + { + current_staff=it; + staff_menu->popup(event->globalPos()); + } + else if (event->button() == Qt::MidButton) //middle click? + { + remove_staff(it); + } + else if (event->button() == Qt::LeftButton) //left click? + { + current_staff=it; + dragging_staff=true; + } + } + else + { + ScoreItemList& itemlist=it->itemlist; + + cout << "mousePressEvent at "<<x<<"/"<<y<<"; tick="<<tick<<endl; + set<FloItem, floComp>::iterator it; + for (it=itemlist[tick].begin(); it!=itemlist[tick].end(); it++) + if (it->type==FloItem::NOTE) + if (it->bbox().contains(x,y)) + break; + + if (it!=itemlist[tick].end()) //we found something? + { + mouse_down_pos=event->pos(); + mouse_operation=NO_OP; + + int t=tick; + set<FloItem, floComp>::iterator found; + + do + { + found=itemlist[t].find(FloItem(FloItem::NOTE, it->pos)); + if (found == itemlist[t].end()) + { + cout << "FATAL: THIS SHOULD NEVER HAPPEN: could not find the note's tie-destination" << endl; + break; + } + else + { + t+=calc_len(found->len, found->dots); + } + } while (found->tied); + + int total_begin=it->begin_tick; + int total_end=t; + + int this_begin=tick; + int this_end=this_begin+calc_len(it->len, it->dots); + + //that's the only note corresponding to the event? + if (this_begin==total_begin && this_end==total_end) + { + if (x < it->x) + mouse_x_drag_operation=BEGIN; + else + mouse_x_drag_operation=LENGTH; + } + //that's NOT the only note? + else + { + if (this_begin==total_begin) + mouse_x_drag_operation=BEGIN; + else if (this_end==total_end) + mouse_x_drag_operation=LENGTH; + else + mouse_x_drag_operation=NO_OP; + } + + cout << "you clicked at a note with begin at "<<it->begin_tick<<" and end at "<<t<<endl; + cout << "x-drag-operation will be "<<mouse_x_drag_operation<<endl; + cout << "pointer to part is "<<it->source_part; + if (!it->source_part) cout << " (WARNING! THIS SHOULD NEVER HAPPEN!)"; + cout << endl; + + dragged_event=*it->source_event; + dragged_event_part=it->source_part; + dragged_event_original_pitch=dragged_event.pitch(); + + if ((mouse_erases_notes) || (event->button()==Qt::MidButton)) //erase? + { + audio->msgDeleteEvent(dragged_event, dragged_event_part, true, false, false); + } + else if (event->button()==Qt::LeftButton) //edit? + { + setMouseTracking(true); + dragging=true; + song->startUndo(); + } + } + else //we found nothing? + { + if ((event->button()==Qt::LeftButton) && (mouse_inserts_notes)) + { + signed int relative_tick=(signed) tick - curr_part->tick(); + if (relative_tick>=0) //TODO FINDMICH do that better + { + song->startUndo(); + //stopping undo at the end of this function is unneccessary + //because we'll begin a drag right after it. finishing + //this drag will stop undo as well (in mouseReleaseEvent) + + Event newevent(Note); + newevent.setPitch(y_to_pitch(y,tick, USED_CLEF)); + newevent.setVelo(64); //TODO + newevent.setVeloOff(64); //TODO + newevent.setTick(relative_tick); + newevent.setLenTick((new_len>0)?new_len:last_len); + + audio->msgAddEvent(newevent, curr_part, false, false, false); + + dragged_event_part=curr_part; + dragged_event=newevent; + dragged_event_original_pitch=newevent.pitch(); + + mouse_down_pos=event->pos(); + mouse_operation=NO_OP; + mouse_x_drag_operation=LENGTH; + + song_changed(0); + + setMouseTracking(true); + dragging=true; + //song->startUndo(); unneccessary because we have started it already above + } + } + } + } + } +} + +void ScoreCanvas::mouseReleaseEvent (QMouseEvent* event) +{ + if (dragging) + { + if (event->button()==Qt::LeftButton) + { + if (mouse_operation==LENGTH) + { + if (flo_quantize(dragged_event.lenTick()) <= 0) + { + cout << "new length <= 0, erasing item" << endl; + audio->msgDeleteEvent(dragged_event, dragged_event_part, false, false, false); + } + else + { + last_len=flo_quantize(dragged_event.lenTick()); + } + } + + song->endUndo(SC_EVENT_MODIFIED); + setMouseTracking(false); + dragging=false; + + scroll_speed=0; scroll_pos=0; + } + } + + if (dragging_staff) + { + merge_staves(staff_at_y(event->y()), current_staff); + dragging_staff=false; + } +} + +#define PITCH_DELTA 5 + + +void ScoreCanvas::mouseMoveEvent (QMouseEvent* event) +{ + if (dragging) + { + int dx=event->x()-mouse_down_pos.x(); + int dy=event->y()-mouse_down_pos.y(); + + int x=event->x()+x_pos-x_left; + + int tick=flo_quantize_floor(x_to_tick(x)); + + if (mouse_operation==NO_OP) + { + if ((abs(dx)>DRAG_INIT_DISTANCE) && (mouse_x_drag_operation!=NO_OP)) + { + cout << "mouse-operation is now "<<mouse_x_drag_operation<<endl; + mouse_operation=mouse_x_drag_operation; + } + else if (abs(dy)>DRAG_INIT_DISTANCE) + { + cout << "mouse-operation is now PITCH" << endl; + mouse_operation=PITCH; + } + } + + int new_pitch; + + switch (mouse_operation) + { + case NONE: + break; + + case PITCH: + cout << "changing pitch, delta="<<nearbyint((float)dy/PITCH_DELTA)<<endl; + new_pitch=dragged_event_original_pitch - nearbyint((float)dy/PITCH_DELTA); + + if (dragged_event.pitch()!=new_pitch) + { + Event tmp=dragged_event.clone(); + tmp.setPitch(new_pitch); + + audio->msgChangeEvent(dragged_event, tmp, dragged_event_part, false, false, false); + dragged_event=tmp; + + song_changed(0); + } + + break; + + case BEGIN: + if (dragged_event.tick()+dragged_event_part->tick() != unsigned(tick)) //TODO FINDMICHJETZT tick kann unsigned werden + { + Event tmp=dragged_event.clone(); + + if (tick-signed(dragged_event_part->tick()) >= 0) //TODO FINDMICH do that better + tmp.setTick(tick-dragged_event_part->tick()); + + audio->msgChangeEvent(dragged_event, tmp, dragged_event_part, false, false, false); + dragged_event=tmp; + + song_changed(0); + } + + break; + + case LENGTH: + tick+=FLO_QUANT; + if (dragged_event.tick()+dragged_event.lenTick() + dragged_event_part->tick() != unsigned(tick)) + { + Event tmp=dragged_event.clone(); + + if (tick-signed(dragged_event.tick() -dragged_event_part->tick()) >= 0) //TODO FINDMICH do that better + tmp.setLenTick(tick-dragged_event.tick() -dragged_event_part->tick()); + + audio->msgChangeEvent(dragged_event, tmp, dragged_event_part, false, false, false); + dragged_event=tmp; + + song_changed(0); + } + + break; + } + + + if ((mouse_operation==LENGTH) || (mouse_operation==BEGIN)) //scrolling enabled? + { + int win_x=event->x(); + + if (win_x < x_left + SCROLL_MARGIN) + { + scroll_speed=(win_x - (x_left + SCROLL_MARGIN)) * SCROLL_SPEED; + if (scroll_speed < -SCROLL_SPEED_MAX) scroll_speed=-SCROLL_SPEED_MAX; + } + else if (win_x > width() - SCROLL_MARGIN) + { + scroll_speed=(win_x - (width() - SCROLL_MARGIN)) * SCROLL_SPEED; + if (scroll_speed > SCROLL_SPEED_MAX) scroll_speed=SCROLL_SPEED_MAX; + } + else + scroll_speed=0; + } + else + { + scroll_speed=0; + } + } +} + +void ScoreCanvas::heartbeat_timer_event() +{ + if (scroll_speed) + { + int old_xpos=x_pos; + + scroll_pos+=scroll_speed*heartBeatTimer->interval()/1000.0; + int tmp=int(scroll_pos); + if (tmp!=0) + x_pos+=tmp; + scroll_pos-=tmp; + + if (x_pos<0) x_pos=0; + if (x_pos>canvas_width()) x_pos=canvas_width(); + + if (old_xpos!=x_pos) emit xpos_changed(x_pos); + } +} + +void ScoreCanvas::scroll_event(int x) +{ + cout << "SCROLL EVENT: x="<<x<<endl; + x_pos=x; + redraw(); +} + + +//if force is true, it will always happen something +//if force is false, it will only happen something, if +//tick isn't visible at all. if it's visible, but not at +//the position goto would set it to, nothing happens +void ScoreCanvas::goto_tick(int tick, bool force) +{ + if (!force) + { + if (tick < x_to_tick(x_pos)) + { + x_pos=tick_to_x(tick) - x_left; + if (x_pos<0) x_pos=0; + if (x_pos>canvas_width()) x_pos=canvas_width(); + + emit xpos_changed(x_pos); + } + else if (tick > x_to_tick(x_pos+viewport_width()*PAGESTEP)) + { + x_pos=tick_to_x(tick); + if (x_pos<0) x_pos=0; + if (x_pos>canvas_width()) x_pos=canvas_width(); + + emit xpos_changed(x_pos); + } + } + else + { + x_pos=tick_to_x(tick)-viewport_width()/2; + if (x_pos<0) x_pos=0; + if (x_pos>canvas_width()) x_pos=canvas_width(); + + emit xpos_changed(x_pos); + } +} //--------------------------------------------------------- -// toggleTrackInfo +// resizeEvent //--------------------------------------------------------- -void ScoreEdit::toggleTrackInfo() +void ScoreCanvas::resizeEvent(QResizeEvent* ev) { - bool vis = midiTrackInfo->isVisible(); - infoScroll->setVisible(!vis); - infoScroll->setEnabled(!vis); + QWidget::resizeEvent(ev); //TODO is this really neccessary? + + emit viewport_width_changed( viewport_width() ); } + +void ScoreCanvas::pos_changed(int index, unsigned tick, bool scroll) +{ + if (index==0) + { + if (scroll) //potential need to scroll? + { + switch (song->follow()) + { + case Song::NO: break; + case Song::JUMP: goto_tick(tick,false); break; + case Song::CONTINUOUS: goto_tick(tick,true); break; + } + } + + if (need_redraw_for_hilighting()) + redraw(); + } +} + + +void ScoreCanvas::recalc_staff_pos() +{ + int y=0; + + for (list<staff_t>::iterator it=staffs.begin(); it!=staffs.end(); it++) + { + it->y_top=y; + switch (it->type) + { + case NORMAL: + it->y_draw = it->y_top + STAFF_DISTANCE/2; + it->y_bottom = it->y_draw + STAFF_DISTANCE/2; + break; + case GRAND_TOP: + it->y_draw = it->y_top + STAFF_DISTANCE/2; + it->y_bottom = it->y_draw + GRANDSTAFF_DISTANCE/2; + break; + case GRAND_BOTTOM: + it->y_draw = it->y_top + GRANDSTAFF_DISTANCE/2; + it->y_bottom = it->y_draw + STAFF_DISTANCE/2; + break; + default: + cout << "THIS SHOULD NEVER HAPPEN: invalid staff type!" << endl; + } + y=it->y_bottom; + } +} + +list<staff_t>::iterator ScoreCanvas::staff_at_y(int y) +{ + for (list<staff_t>::iterator it=staffs.begin(); it!=staffs.end(); it++) + if ((y >= it->y_top) && (y < it->y_bottom)) + return it; + + return staffs.end(); +} + +//the following assertions are made: +// pix_quarter.width() == pix_half.width() + + +// pix->width()-1 + 1/2*pix->width() + SHIFT + ADD_SPACE +// 10-1+5+3+3=20 <- um so viel wird der taktstrich verschoben +// um das doppelte (20*2=40) werden die kleinsten schläge gegeneinander versetzt + + + + + +//hint: recalculating event- and itemlists "from zero" +// could happen in realtime, as it is pretty fast. +// however, this adds unneccessary cpu usage. +// it is NO problem to recalc the stuff "from zero" +// every time something changes. + + +/* BUGS and potential bugs + * o when adding a note, it's added to the first stave + * the problem is: there's always the first part selected + * o when changing color of a displayed part, note heads aren't redrawn + * o when pressing "STOP", the active note isn't redrawn "normally" + * + * CURRENT TODO + * o y-scroll for staff window, with automatic margin-scrolling + * o let the user edit the score's name + * + * IMPORTANT TODO + * o removing the part the score's working on isn't handled + * o let the user select the currently edited part + * o let the user select between "colors after the parts", + * "colors after selected/unselected part" and "all black" + * o support selections + * o emit a "song-changed" signal instead of calling our + * internal song_changed() function + * o check if "moving away" works for whole notes [seems to NOT work properly] + * + * less important stuff + * o must add_parts() update the part-list? + * o support different keys in different tracks at the same time + * calc_pos_add_list and calc_item_pos will be affected by this + * calc_pos_add_list must be called before calc_item_pos then, + * and calc_item_pos must respect the pos_add_list instead of + * keeping its own pos_add variable (which is only an optimisation) + * o use nearest part instead of curr_part, maybe expand + * o draw measure numbers + * o use "unsigned" whereever "unsigned" is meant + * o when moving or resizing a note, so that its end is out-of-part, + * there's strange behaviour + * o redraw is called too often + * for example, when scroll is continuous, and note-hilighting has + * changed, redraw() is called twice + * o song_changed() should distinguish between relevant and + * irrelevant changes. (for example controllers can be ignored) + * o ties aren't always drawn correctly when the destination note + * is out of view + * o tied notes don't work properly when there's a key-change in + * between, for example, when a cis is tied to a des + * o display only the part, not the whole song filled with rests? + * o let the user select whether the preamble should have + * a fixed length (?) + * o let the user select what the preamble has to contain + * > o use timesig_t in all timesig-stuff + * o draw a margin around notes which are in a bright color + * o maybe override color 0 with "black"? + * o use bars instead of flags over groups of 8ths / 16ths etc + * o (change ItemList into map< pos_t , mutable_stuff_t >) [no] + * o deal with expanding parts or clip (expanding is better) + * o check if making the program clef-aware hasn't broken anything + * e.g. accidentials, creating notes, rendering etc. + * o check if the new function for drawing accidential works + * o refuse to resize so that width gets smaller or equal than x_left + * o set distances properly [looks okay, doesn't it?] + * o change iterators into const iterators + * o add tracks in correct order to score + * o rename staffs to staves + * + * stuff for the other muse developers + * o OFFER A WAY TO EDIT THE KEYMAP (and integrate the keymap properly) + * + * o check if dragging notes is done correctly + * o after doing the undo stuff right, the "pianoroll isn't informed + * about score-editor's changes"-bug has vanished. did it vanish + * "by accident", or is that the correct solution for this? + * o process accurate timesignatures from muse's list (has to be implemented first in muse) + * ( (2+2+3)/4 or (3+2+2)/4 instead of 7/4 ) + * o maybe do expanding parts inside the msgChangeEvent or + * msgNewEvent functions (see my e-mail) + * + * GUI stuff + * o offer a button for bool mouse_erases_notes and mouse_inserts_notes + * o offer dropdown-boxes for lengths of the inserted note + * (select between 16th, 8th, ... whole and "last used length") + * o offer a dropdown-box for the clef to use + * o offer some way to setup the colorizing method to be used + */ + + + +/* how to use the score editor with multiple tracks + * ================================================ + * + * select parts, right-click, "display in new score window" or "display per-track in new score window" + * or "display in existing window -> 1,2,3,4" or "display per-track in existing..." + * + * ScoreCanvas has a list of note systems, consisting of the following: + * * all parts included in that view + * * eventlist, itemlist + * * used clef, transposing/octave settings + * * enum { NOT_GROUPED, I_AM_TOP, I_AM_BOTTOM } group_state + * NOT_GROUPED means "single note system" + * I_AM_TOP and I_AM_BOTTOM mean that the two systems belong + * together + * + * when redrawing, we iterate through all systems. + * we add a distance according to group_state + * then we draw the system. if group_state is I_AM_BOTTOM, we + * draw our beams longer/higher, and we draw a bracket + * + * when clicking around, we first determine which system has been clicked in + * (the systems have enough space in between, so there won't be notes + * from sys1 in sys2. if there are, they're ignored for simplicity) + * then we proceed as usual (adding, removing, changing notes) + * + * + * pos_add_list stays the same for each staff, so we only need one + */ + +/* R O A D M A P + * ============= + * + * 1. finish the score editor, without transposing instruments and + * with only a global keymap + * + * REASON: a score editor with few functions is better than + * no score editor at all + * + * + * 2. support transposing by octave-steps + * + * REASON: the main problem with transposing is, that the + * editor needs different key signatures and needs + * to align them against each other. this problem + * doesn't exist when only transposing by octaves + * + * + * 3. support transposing instruments, but only one + * transposing-setting per score window. that is, you won't be + * able to display your (C-)strings in the same window as your + * B-trumpet. this will be very easy to implement + * + * REASON: the above problem still exists, but is circumvented + * by simply not having to align them against each other + * (because they're in different windows) + * + * + * 4. support different transposing instruments in the same score + * window. this will be some hassle, because we need to align + * the scores properly. for example, when the C-violin has + * C-major (no accidentials), then the B-trumpet need some + * accidentials. we now must align the staves so that the + * "note-after-keychange"s of both staves are again at the + * same x-position + * + * REASON: some solution for that problem must be written. + * this is a large step, which atm isn't very important + * + * + * 5. support different keys per track. this wouldn't be that + * hard, when 4) is already done; because we then already have + * the "align it properly" functionality, and can use it + * + * REASON: this is only a nice-to-have, which can however be + * easily implemented when 4) is done + */ diff --git a/muse2/muse/midiedit/scoreedit.h b/muse2/muse/midiedit/scoreedit.h index 767dc463..a296d8a8 100644 --- a/muse2/muse/midiedit/scoreedit.h +++ b/muse2/muse/midiedit/scoreedit.h @@ -1,8 +1,8 @@ //========================================================= // MusE // Linux Music Editor -// $Id: pianoroll.h,v 1.5.2.4 2009/11/16 11:29:33 lunar_shuttle Exp $ -// (C) Copyright 1999 Werner Schweer (ws@seh.de) +// scoreedit.h +// (C) Copyright 2011 Florian Jung (florian.a.jung@web.de) //========================================================= #ifndef __SCOREEDIT_H__ @@ -12,6 +12,10 @@ #include <QResizeEvent> #include <QLabel> #include <QKeyEvent> +#include <QPainter> +#include <QPixmap> +#include <QTimer> +#include <QScrollBar> #include <values.h> #include "noteinfo.h" @@ -19,183 +23,641 @@ #include "midieditor.h" #include "tools.h" #include "event.h" +#include "view.h" +#include "gconfig.h" +#include "part.h" -class MidiPart; -class TimeLabel; -class PitchLabel; -class QLabel; -class PianoCanvas; -class MTScale; -class Track; -class QToolButton; -class QToolBar; -class QPushButton; -class CtrlEdit; -class Splitter; -class PartList; -class Toolbar1; -class Xml; -class QuantConfig; -class ScrollScale; -class Part; -class SNode; -class QMenu; -class QAction; -class QWidget; -class QScrollBar; -class MidiTrackInfo; -class QScrollArea; +#include <set> +#include <map> +#include <list> +#include <vector> +#include <string> + +using std::set; +using std::pair; +using std::map; +using std::list; +using std::vector; +using std::string; + + + + + +class ScoreCanvas; //--------------------------------------------------------- -// PianoRoll +// ScoreEdit //--------------------------------------------------------- -class ScoreEdit : public MidiEditor { - Event selEvent; - MidiPart* selPart; - int selTick; - - //enum { CMD_EVENT_COLOR, CMD_CONFIG_QUANT, CMD_LAST }; - //int menu_ids[CMD_LAST]; - //Q3PopupMenu *menuEdit, *menuFunctions, *menuSelect, *menuConfig, *menuPlugins; - - - QMenu *menuEdit, *menuFunctions, *menuSelect, *menuConfig, *eventColor, *menuPlugins; - MidiTrackInfo *midiTrackInfo; - Track* selected; - - QAction* editCutAction; - QAction* editCopyAction; - QAction* editPasteAction; - QAction* editDelEventsAction; - - QAction* selectAllAction; - QAction* selectNoneAction; - QAction* selectInvertAction; - QAction* selectInsideLoopAction; - QAction* selectOutsideLoopAction; - QAction* selectPrevPartAction; - QAction* selectNextPartAction; - - QAction* evColorBlueAction; - QAction* evColorPitchAction; - QAction* evColorVelAction; - - QAction* funcOverQuantAction; - QAction* funcNoteOnQuantAction; - QAction* funcNoteOnOffQuantAction; - QAction* funcIterQuantAction; - QAction* funcConfigQuantAction; - QAction* funcGateTimeAction; - QAction* funcModVelAction; - QAction* funcCrescendoAction; - QAction* funcTransposeAction; - QAction* funcThinOutAction; - QAction* funcEraseEventAction; - QAction* funcNoteShiftAction; - QAction* funcMoveClockAction; - QAction* funcCopyMeasureAction; - QAction* funcEraseMeasureAction; - QAction* funcDelMeasureAction; - QAction* funcCreateMeasureAction; - QAction* funcSetFixedLenAction; - QAction* funcDelOverlapsAction; - - - int tickOffset; - int lenOffset; - int pitchOffset; - int veloOnOffset; - int veloOffOffset; - bool deltaMode; - - NoteInfo* info; - QToolButton* srec; - QToolButton* midiin; - - Toolbar1* toolbar; - Splitter* splitter; - Splitter* hsplitter; - Splitter* ctrlLane; - - QToolButton* speaker; - QToolBar* tools; - EditToolBar* tools2; - - int colorMode; - - static int _quantInit, _rasterInit; - static int _widthInit, _heightInit; - - static int _quantStrengthInit; - static int _quantLimitInit; - static bool _quantLenInit; - static int _toInit; - static int colorModeInit; - - int _quantStrength; - int _quantLimit; - int _to; - bool _quantLen; - QuantConfig* quantConfig; - bool _playEvents; - - //QScrollBar* infoScroll; - QScrollArea* infoScroll; - - Q_OBJECT - void initShortcuts(); - void setEventColorMode(int); - QWidget* genToolbar(QWidget* parent); - virtual void closeEvent(QCloseEvent*); - virtual void keyPressEvent(QKeyEvent*); - virtual void resizeEvent(QResizeEvent*); - - private slots: - void setSelection(int, Event&, Part*); - void noteinfoChanged(NoteInfo::ValType, int); - //CtrlEdit* addCtrl(); - void removeCtrl(CtrlEdit* ctrl); - void soloChanged(bool flag); - //void trackInfoScroll(int); - void setRaster(int); - void setQuant(int); - void configQuant(); - void setQuantStrength(int val) { _quantStrength = val; } - void setQuantLimit(int val) { _quantLimit = val; } - void setQuantLen(bool val) { _quantLen = val; } - void cmd(int); - void setSteprec(bool); - void setTo(int val) { _to = val; } - void eventColorModeChanged(int); - void clipboardChanged(); // enable/disable "Paste" - void selectionChanged(); // enable/disable "Copy" & "Paste" - void setSpeaker(bool); - void setTime(unsigned); - void follow(int pos); - void songChanged1(int); - void configChanged(); - void newCanvasWidth(int); - void toggleTrackInfo(); - void updateTrackInfo(); - - signals: - void deleted(unsigned long); - +class ScoreEdit : public MidiEditor +{ + Q_OBJECT + + private: + virtual void closeEvent(QCloseEvent*); + + QScrollBar* hscroll; + ScoreCanvas* score_canvas; + + static int serial; + static set<string> names; + + string name; + + bool set_name(string newname, bool emit_signal=true, bool emergency_name=false); + private slots: + + + signals: + void deleted(unsigned long); + void name_changed(); + + public slots: + void canvas_width_changed(int); + void viewport_width_changed(int); + + public: + ScoreEdit(PartList*, QWidget* parent = 0, const char* name = 0, unsigned initPos = MAXINT); + ~ScoreEdit(); + static void readConfiguration(Xml&){}; //TODO does nothing + static void writeConfiguration(int, Xml&){}; //TODO does nothing + + void add_parts(PartList* pl, bool all_in_one=false); + string get_name() { return name; } + }; + + + + + +enum tonart_t +{ + SHARP_BEGIN, + C, // C or am, uses # for "black keys" + G, + D, + A, + E, + H, + FIS, //produces a #E (sounds like a F) + SHARP_END, + B_BEGIN, + C_B, // the same as C, but uses b for "black keys" + F, + ES, + AS, + DES, + GES, //sounds like FIS, but uses b instead of # + B_END +}; + +enum stem_t +{ + UPWARDS, + DOWNWARDS +}; + +enum vorzeichen_t +{ + B=-1, + NONE=0, + SHARP=1 +}; + +struct note_pos_t +{ + int height; // 0 means "C-line", 1 "D-line" and so on + vorzeichen_t vorzeichen; + + bool operator== (const note_pos_t& that) const + { + return (this->height==that.height) && (this->vorzeichen == that.vorzeichen); + } +}; + +bool operator< (const note_pos_t& a, const note_pos_t& b); + + + +// FINDMICH put that keymap-stuff to its appropriate place +struct KeyEvent +{ + tonart_t key; + unsigned tick; + + KeyEvent(tonart_t k, unsigned t) + { + key=k; + tick=t; + } +}; + +typedef std::map<unsigned, KeyEvent, std::less<unsigned> > _KeyList; +typedef _KeyList::iterator iKeyEvent; +typedef _KeyList::const_iterator ciKeyEvent; +typedef _KeyList::reverse_iterator riKeyEvent; +typedef _KeyList::const_reverse_iterator criKeyEvent; + +class KeyList : public _KeyList +{ + public: + KeyList(); + void clear(); + tonart_t key_at_tick(unsigned tick); + //TODO FINDMICH: more functions, like add(), remove() have to be implemented +}; + + + +class FloEvent +{ + public: + enum typeEnum { NOTE_ON = 30, NOTE_OFF = 10, BAR = 20, KEY_CHANGE=23, TIME_SIG=26 }; //the order matters! + typeEnum type; + unsigned tick; + Part* source_part; + Event* source_event; + + int pitch; + mutable int vel; + mutable int len; + + int num; + int denom; + + tonart_t tonart; + + + FloEvent(unsigned ti, int p,int v,int l,typeEnum t, Part* part=NULL, Event* event=NULL) + { + pitch=p; + vel=v; + len=l; + type= t; + tick=ti; + source_event=event; + source_part=part; + } + FloEvent(unsigned ti, typeEnum t, int num_, int denom_) + { + type=t; + num=num_; + denom=denom_; + tick=ti; + source_event=NULL; + source_part=NULL; + } + FloEvent(unsigned ti, typeEnum t, tonart_t k) + { + type=t; + tonart=k; + tick=ti; + source_event=NULL; + source_part=NULL; + } +}; +class FloItem +{ + public: + enum typeEnum { NOTE=21, REST=22, NOTE_END=01, REST_END=02, BAR =10, KEY_CHANGE=13, TIME_SIG=16}; //the order matters! + typeEnum type; + unsigned begin_tick; + Event* source_event; + Part* source_part; + + note_pos_t pos; + int len; + int dots; + bool tied; + bool already_grouped; + + int num; + int denom; + + tonart_t tonart; + + mutable stem_t stem; + mutable int shift; + mutable bool ausweich; + mutable bool is_tie_dest; + mutable int tie_from_x; + + mutable int x; + mutable int y; + mutable int stem_x; + mutable QPixmap* pix; + + mutable bool is_active; + + QRect bbox() const; + + + + FloItem(typeEnum t, note_pos_t p, int l=0,int d=0, bool ti=false, unsigned beg=0, Part* part=NULL, Event* event=NULL) + { + pos=p; + dots=d; + len=l; + type=t; + already_grouped=false; + tied=ti; + shift=0; + ausweich=false; + is_tie_dest=false; + begin_tick=beg; + source_event=event; + source_part=part; + } + + FloItem(typeEnum t, int num_, int denom_) + { + type=t; + num=num_; + denom=denom_; + begin_tick=-1; + source_event=NULL; + source_part=NULL; + } + + FloItem(typeEnum t, tonart_t k) + { + type=t; + tonart=k; + begin_tick=-1; + source_event=NULL; + source_part=NULL; + } + + FloItem(typeEnum t) + { + type=t; + + already_grouped=false; + tied=false; + shift=0; + ausweich=false; + is_tie_dest=false; + begin_tick=-1; + source_event=NULL; + source_part=NULL; + } + + FloItem() + { + already_grouped=false; + tied=false; + shift=0; + ausweich=false; + is_tie_dest=false; + begin_tick=-1; + source_event=NULL; + source_part=NULL; + } + + bool operator==(const FloItem& that) + { + if (this->type != that.type) return false; + + switch(type) + { + case NOTE: + case REST: + case NOTE_END: + case REST_END: + return (this->pos == that.pos); + + //the following may only occurr once in a set + //so we don't search for "the time signature with 4/4 + //at t=0", but only for "some time signature at t=0" + //that's why true is returned, and not some conditional + //expression + case BAR: + case KEY_CHANGE: + case TIME_SIG: + return true; + } + } +}; +struct floComp +{ + bool operator() (const pair<unsigned, FloEvent>& a, const pair<unsigned, FloEvent>& b ) + { + if (a.first < b.first) return true; + if (a.first > b.first) return false; + + if (a.second.type<b.second.type) return true; + if (a.second.type>b.second.type) return false; + + return (a.second.pitch<b.second.pitch); + } + bool operator() (const FloItem& a, const FloItem& b ) + { + if (a.type < b.type) return true; + if (a.type > b.type) return false; + + switch(a.type) + { + case FloItem::NOTE: + case FloItem::REST: + case FloItem::NOTE_END: + case FloItem::REST_END: + return (a.pos < b.pos); + + //the following may only occurr once in a set + //so we don't search for "the time signature with 4/4 + //at t=0", but only for "some time signature at t=0" + //that's why true is returned, and not some conditional + //expression + case FloItem::BAR: + case FloItem::KEY_CHANGE: + case FloItem::TIME_SIG: + return false; + } + return (a.pos < b.pos); + } +}; + +typedef set< pair<unsigned, FloEvent>, floComp > ScoreEventList; +typedef map< unsigned, set<FloItem, floComp> > ScoreItemList; + +enum clef_t +{ + VIOLIN, + BASS +}; + + +struct note_len_t +{ + int len; + int dots; + + note_len_t(int l, int d) + { + len=l; dots=d; + } + + note_len_t(int l) + { + len=l; dots=0; + } +}; + +bool operator< (const note_len_t& a,const note_len_t& b); + +struct cumulative_t +{ + int count; + int cumul; + + cumulative_t() + { + count=0; + cumul=0; + } + + void add(int v) + { + count++; + cumul+=v; + } + + float mean() + { + return (float)cumul/count; + } +}; + +#define BLACK_PIXMAP (NUM_PARTCOLORS) +#define HIGHLIGHTED_PIXMAP (NUM_PARTCOLORS+1) + +struct timesig_t +{ + int num; + int denom; +}; + +enum staff_type_t +{ + NORMAL, + GRAND_TOP, + GRAND_BOTTOM +}; + +enum staff_mode_t +{ + MODE_TREBLE, + MODE_BASS, + MODE_BOTH +}; + +struct staff_t +{ + set<Part*> parts; + ScoreEventList eventlist; + ScoreItemList itemlist; + + int y_top; + int y_draw; + int y_bottom; + + staff_type_t type; + clef_t clef; + int split_note; + + void create_appropriate_eventlist(const set<Part*>& parts); + void create_itemlist(); + void process_itemlist(); + void calc_item_pos(); + + staff_t() + { + type=NORMAL; + clef=VIOLIN; + } + + staff_t (staff_type_t type_, clef_t clef_, set<Part*> parts_, int split_note_=0) + { + type=type_; + clef=clef_; + split_note=split_note_; + parts=parts_; + } +}; + +list<int> calc_accidentials(tonart_t key, clef_t clef, tonart_t next_key=C); +note_pos_t note_pos_(int note, tonart_t key); +note_pos_t note_pos (unsigned note, tonart_t key, clef_t clef); + +int calc_len(int l, int d); +list<note_len_t> parse_note_len(int len_ticks, int begin_tick, vector<int>& foo, bool allow_dots=true, bool allow_normal=true); + +int clef_height(clef_t clef); + + +int calc_timesig_width(int num, int denom); +int calc_number_width(int n); + + +class ScoreCanvas : public View +{ + Q_OBJECT + private: + static void load_pixmaps(); + static void draw_pixmap(QPainter& p, int x, int y, const QPixmap& pm); + static void draw_tie (QPainter& p, int x1, int x4, int yo, bool up=true, QColor color=Qt::black); + + static void draw_accidentials(QPainter& p, int x, int y_offset, const list<int>& acc_list, const QPixmap& pix); + + static void draw_timesig(QPainter& p, int x, int y_offset, int num, int denom); + + static void draw_number(QPainter& p, int x, int y, int n); + + + + + + static int height_to_pitch(int h, clef_t clef, tonart_t key); + static int height_to_pitch(int h, clef_t clef); + static int y_to_height(int y); + int y_to_pitch(int y, int t, clef_t clef); + + + + + void draw_note_lines(QPainter& p, int y); + void draw_preamble(QPainter& p, int y, clef_t clef); + void draw_items(QPainter& p, int y, staff_t& staff, ScoreItemList::iterator from_it, ScoreItemList::iterator to_it); + void draw_items(QPainter& p, int y, staff_t& staff, int x1, int x2); + void draw_items(QPainter& p, int y, staff_t& staff); + void calc_pos_add_list(); + + + void recalc_staff_pos(); + list<staff_t>::iterator staff_at_y(int y); + + + timesig_t timesig_at_tick(int t); + tonart_t key_at_tick(int t); + int tick_to_x(int t); + int x_to_tick(int x); + int calc_posadd(int t); + + + + bool need_redraw_for_hilighting(ScoreItemList::iterator from_it, ScoreItemList::iterator to_it); + bool need_redraw_for_hilighting(ScoreItemList& itemlist, int x1, int x2); + bool need_redraw_for_hilighting(ScoreItemList& itemlist); + bool need_redraw_for_hilighting(); + + int canvas_width(); + int viewport_width(); + + + void set_staffmode(list<staff_t>::iterator it, staff_mode_t mode); + void remove_staff(list<staff_t>::iterator it); + void merge_staves(list<staff_t>::iterator dest, list<staff_t>::iterator src); + +// member variables --------------------------------------------------- + + std::map<int,int> pos_add_list; + + list<staff_t> staffs; + + // the drawing area is split into a "preamble" containing clef, + // key and time signature, and the "item's area" containing the + // actual items (notes, bars, rests, etc.) + // x_pos is responsible for scrolling. an item with item->x==x_pos + // will be drawn exactly at the left beginning of the item's area + // x_left could also be called "preamble's width". it defines + // where the item's area begins + // when multiple note systems are drawn into one window, the + // preamble's length is the same for each system + int x_pos; + int x_left; + + //for mouse-scrolling + float scroll_speed; + float scroll_pos; + + Part* curr_part; + int last_len; + int new_len; //when zero or negative, last_len is used + + QPoint mouse_down_pos; + bool mouse_down; + enum operation_t + { + NO_OP=0, + BEGIN=1, + LENGTH=2, + PITCH=3 + }; + operation_t mouse_operation; + operation_t mouse_x_drag_operation; + bool mouse_erases_notes; + bool mouse_inserts_notes; + + bool dragging; + Part* dragged_event_part; + Event dragged_event; + int dragged_event_original_pitch; + + + + + + //menu stuff + QAction* staffmode_treble_action; + QAction* staffmode_bass_action; + QAction* staffmode_both_action; + QAction* remove_staff_action; + + QMenu* staff_menu; + list<staff_t>::iterator current_staff; + bool dragging_staff; + + + private slots: + void staffmode_treble_slot(); + void staffmode_bass_slot(); + void staffmode_both_slot(); + void remove_staff_slot(); + public slots: - virtual void updateHScrollRange(); - void execDeliveredScript(int id); - void execUserScript(int id); - CtrlEdit* addCtrl(); - - public: - ScoreEdit(PartList*, QWidget* parent = 0, const char* name = 0, unsigned initPos = MAXINT); - ~ScoreEdit(); - virtual void readStatus(Xml&); - virtual void writeStatus(int, Xml&) const; - static void readConfiguration(Xml&); - static void writeConfiguration(int, Xml&); - }; + void scroll_event(int); + void song_changed(int); + void goto_tick(int,bool); + void pos_changed(int i, unsigned u, bool b); + void heartbeat_timer_event(); + + signals: + void xpos_changed(int); + void viewport_width_changed(int); + void canvas_width_changed(int); + + protected: + virtual void draw(QPainter& p, const QRect& rect); + MidiEditor* editor; + + virtual void mousePressEvent (QMouseEvent* event); + virtual void mouseMoveEvent (QMouseEvent* event); + virtual void mouseReleaseEvent (QMouseEvent* event); + virtual void resizeEvent(QResizeEvent*); + + public: + ScoreCanvas(MidiEditor*, QWidget*, int, int); + ~ScoreCanvas(){}; + + void add_staves(PartList* pl, bool all_in_one); + +}; + +int calc_measure_len(const list<int>& nums, int denom); +vector<int> create_emphasize_list(const list<int>& nums, int denom); +vector<int> create_emphasize_list(int num, int denom); + #endif |