diff options
author | Florian Jung <flo@windfisch.org> | 2011-04-27 20:33:15 +0000 |
---|---|---|
committer | Florian Jung <flo@windfisch.org> | 2011-04-27 20:33:15 +0000 |
commit | e2cec3f03fdee64da1c5080498476c1cdd43bbdb (patch) | |
tree | 2b373e4fafd3b015bc5c50fca77d67b3e5677e94 /muse2/muse | |
parent | d8ac618f4dc9afd0731a9a3531d4641c9e7ea6fd (diff) | |
parent | 0f7a7ab47a2a7742fba2ea6af980ee5d950b8c37 (diff) |
merged scoreedit branch into trunk
Diffstat (limited to 'muse2/muse')
-rw-r--r-- | muse2/muse/app.cpp | 141 | ||||
-rw-r--r-- | muse2/muse/app.h | 24 | ||||
-rw-r--r-- | muse2/muse/arranger/pcanvas.cpp | 27 | ||||
-rw-r--r-- | muse2/muse/cobject.h | 2 | ||||
-rw-r--r-- | muse2/muse/globals.cpp | 1 | ||||
-rw-r--r-- | muse2/muse/globals.h | 1 | ||||
-rw-r--r-- | muse2/muse/main.cpp | 15 | ||||
-rw-r--r-- | muse2/muse/midiedit/scoreedit.cpp | 4725 | ||||
-rw-r--r-- | muse2/muse/midiedit/scoreedit.h | 837 | ||||
-rw-r--r-- | muse2/muse/widgets/canvas.cpp | 2 |
10 files changed, 4286 insertions, 1489 deletions
diff --git a/muse2/muse/app.cpp b/muse2/muse/app.cpp index 98bc0d85..8def91ec 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,19 @@ 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); + + scoreSubmenu->addMenu(scoreAllInOneSubsubmenu); + scoreSubmenu->addMenu(scoreOneStaffPerTrackSubsubmenu); + + updateScoreMenus(); + + startScoreEditAction = new QAction(*scoreIconSet, tr("New score window"), this); 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 +1155,13 @@ 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(startScoreEditAction, SIGNAL(activated()), SLOT(startScoreQuickly())); 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,6 +1377,7 @@ MusE::MusE(int argc, char** argv) : QMainWindow() menuEdit->addSeparator(); menuEdit->addAction(startPianoEditAction); + menuEdit->addMenu(scoreSubmenu); menuEdit->addAction(startScoreEditAction); menuEdit->addAction(startDrumEditAction); menuEdit->addAction(startListEditAction); @@ -3424,29 +3456,94 @@ 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(score->get_name(), this); + connect(action, SIGNAL(activated()), scoreOneStaffPerTrackMapper, SLOT(map())); + scoreOneStaffPerTrackMapper->setMapping(action, (QWidget*)score); + scoreOneStaffPerTrackSubsubmenu->addAction(action); + + + action=new QAction(score->get_name(), 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(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(destination, SIGNAL(name_changed()), SLOT(scoreNamingChanged())); + //connect(muse, SIGNAL(configChanged()), destination, SLOT(config_changed())); + //commented out by flo, because the ScoreEditor connects to all + //relevant signals on his own + + updateScoreMenus(); + } + + destination->add_parts(pl, allInOne); +} + +void MusE::startScoreQuickly() +{ + openInScoreEdit_oneStaffPerTrack(NULL); +} //--------------------------------------------------------- // startPianoroll @@ -3667,6 +3764,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 +3783,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 +4941,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..2fd83854 100644 --- a/muse2/muse/app.h +++ b/muse2/muse/app.h @@ -65,9 +65,12 @@ class Appearance; class WaveTrack; class AudioOutput; class EditInstrument; +class ScoreEdit; #define MENU_ADD_SYNTH_ID_BASE 0x1000 + + //--------------------------------------------------------- // MusE //--------------------------------------------------------- @@ -109,12 +112,14 @@ 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 *masterGraphicAction, *masterListAction; QAction *midiTransposeAction; QAction *midiTransformerAction; QAction *editSongInfoAction; - + public: + QAction *startScoreEditAction, *startPianoEditAction, *startDrumEditAction, *startListEditAction, *startWaveEditAction; + QMenu *scoreSubmenu, *scoreOneStaffPerTrackSubsubmenu, *scoreAllInOneSubsubmenu; + private: // View Menu actions QAction *viewTransportAction, *viewBigtimeAction, *viewMixerAAction, *viewMixerBAction, *viewCliplistAction, *viewMarkerAction; @@ -220,6 +225,8 @@ class MusE : public QMainWindow QSignalMapper *editSignalMapper; QSignalMapper *midiPluginSignalMapper; QSignalMapper *followSignalMapper; + QSignalMapper *scoreOneStaffPerTrackMapper; + QSignalMapper *scoreAllInOneMapper; signals: void configChanged(); @@ -261,8 +268,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 startScoreQuickly(); void startPianoroll(); void startPianoroll(PartList* /*pl*/, bool /*showDefaultCtrls*/ = false); void startWaveEditor(); @@ -320,7 +334,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/arranger/pcanvas.cpp b/muse2/muse/arranger/pcanvas.cpp index e2c2b32d..c76e4421 100644 --- a/muse2/muse/arranger/pcanvas.cpp +++ b/muse2/muse/arranger/pcanvas.cpp @@ -783,19 +783,17 @@ QMenu* PartCanvas::genItemPopup(CItem* item) partPopup->addSeparator(); switch(trackType) { case Track::MIDI: { - QAction *act_pianoroll = partPopup->addAction(QIcon(*pianoIconSet), tr("pianoroll")); - act_pianoroll->setData(10); - QAction *act_mlist = partPopup->addAction(QIcon(*edit_listIcon), tr("list")); - act_mlist->setData(12); + partPopup->addAction(muse->startPianoEditAction); + partPopup->addMenu(muse->scoreSubmenu); + partPopup->addAction(muse->startScoreEditAction); + partPopup->addAction(muse->startListEditAction); QAction *act_mexport = partPopup->addAction(tr("save part to disk")); act_mexport->setData(16); } break; case Track::DRUM: { - QAction *act_dlist = partPopup->addAction(QIcon(*edit_listIcon), tr("list")); - act_dlist->setData(12); - QAction *act_drums = partPopup->addAction(QIcon(*edit_drummsIcon), tr("drums")); - act_drums->setData(13); + partPopup->addAction(muse->startDrumEditAction); + partPopup->addAction(muse->startListEditAction); QAction *act_dexport = partPopup->addAction(tr("save part to disk")); act_dexport->setData(16); } @@ -866,15 +864,7 @@ void PartCanvas::itemPopup(CItem* item, int n, const QPoint& pt) case 5: copy(pl); break; - case 10: // pianoroll edit - emit startEditor(pl, 0); - return; - case 12: // list edit - emit startEditor(pl, 1); - return; - case 13: // drum edit - emit startEditor(pl, 3); - return; + case 14: // wave edit { // Changed to allow multiple selected parts to be shown. By T356 @@ -994,7 +984,8 @@ void PartCanvas::itemPopup(CItem* item, int n, const QPoint& pt) // If no items selected, use the one clicked on. if(!selfound) item->part()->setColorIndex(curColorIndex); - + + song->update(SC_PART_MODIFIED); redraw(); break; } 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/globals.cpp b/muse2/muse/globals.cpp index 80990f0e..6c0bbbc6 100644 --- a/muse2/muse/globals.cpp +++ b/muse2/muse/globals.cpp @@ -87,6 +87,7 @@ QString lastMidiPath("."); bool debugMode = false; bool debugMsg = false; +bool heavyDebugMsg = false; bool midiInputTrace = false; bool midiOutputTrace = false; bool realTimeScheduling = false; diff --git a/muse2/muse/globals.h b/muse2/muse/globals.h index 151e7800..894f1baf 100644 --- a/muse2/muse/globals.h +++ b/muse2/muse/globals.h @@ -59,6 +59,7 @@ extern bool debugMode; extern bool midiInputTrace; extern bool midiOutputTrace; extern bool debugMsg; +extern bool heavyDebugMsg; extern bool debugSync; extern bool loadPlugins; extern bool loadVST; diff --git a/muse2/muse/main.cpp b/muse2/muse/main.cpp index e1b30d0c..53f8961b 100644 --- a/muse2/muse/main.cpp +++ b/muse2/muse/main.cpp @@ -190,12 +190,14 @@ static void usage(const char* prog, const char* txt) fprintf(stderr, " -v print version\n"); fprintf(stderr, " -d debug mode: no threads, no RT\n"); fprintf(stderr, " -D debug mode: enable some debug messages\n"); + fprintf(stderr, " specify twice for lots of debug messages\n"); + fprintf(stderr, " this may slow down MusE massively!\n"); fprintf(stderr, " -m debug mode: trace midi Input\n"); fprintf(stderr, " -M debug mode: trace midi Output\n"); fprintf(stderr, " -s debug mode: trace sync\n"); fprintf(stderr, " -a no audio\n"); - //fprintf(stderr, " -P n set real time priority to n (default: 50)\n"); - fprintf(stderr, " -P n set audio driver real time priority to n (Dummy only, default 40. Else fixed by Jack.)\n"); + fprintf(stderr, " -P n set audio driver real time priority to n\n"); + fprintf(stderr, " (Dummy only, default 40. Else fixed by Jack.)\n"); fprintf(stderr, " -Y n force midi real time priority to n (default: audio driver prio +2)\n"); fprintf(stderr, " -p don't load LADSPA plugins\n"); #ifdef ENABLE_PYTHON @@ -210,7 +212,8 @@ static void usage(const char* prog, const char* txt) #ifdef HAVE_LASH fprintf(stderr, " -L don't use LASH\n"); #endif - fprintf(stderr, " -l xx force locale to the given language/country code (xx = %s)\n", localeList().toLatin1().constData()); + fprintf(stderr, " -l xx force locale to the given language/country code\n"); + fprintf(stderr, " (xx = %s)\n", localeList().toLatin1().constData()); } //--------------------------------------------------------- @@ -303,7 +306,11 @@ int main(int argc, char* argv[]) case 'a': noAudio = true; break; - case 'D': debugMsg = true; break; + case 'D': + if (!debugMsg) + debugMsg=true; + else + heavyDebugMsg=true; case 'm': midiInputTrace = true; break; case 'M': midiOutputTrace = true; break; case 's': debugSync = true; break; diff --git a/muse2/muse/midiedit/scoreedit.cpp b/muse2/muse/midiedit/scoreedit.cpp index f236a0d5..cbe9c20a 100644 --- a/muse2/muse/midiedit/scoreedit.cpp +++ b/muse2/muse/midiedit/scoreedit.cpp @@ -1,13 +1,15 @@ //========================================================= // 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 (flo93@users.sourceforge.net) //========================================================= + #include <QLayout> #include <QSizeGrip> #include <QLabel> +#include <QScrollBar> #include <QPushButton> #include <QToolButton> #include <QToolTip> @@ -18,6 +20,7 @@ #include <QClipboard> #include <QDir> #include <QAction> +#include <QActionGroup> #include <QKeySequence> #include <QKeyEvent> #include <QGridLayout> @@ -26,9 +29,18 @@ #include <QMimeData> #include <QScrollArea> #include <QSettings> +#include <QImage> +#include <QInputDialog> +#include <QMessageBox> #include <stdio.h> +#include <math.h> + +#include <iostream> +#include <sstream> +using namespace std; +#include "app.h" #include "xml.h" #include "mtscale.h" #include "prcanvas.h" @@ -51,1361 +63,3538 @@ #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" + + +string IntToStr(int i); +QString IntToQStr(int i); + + +#define SPLIT_NOTE 60 + + + + + + +//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! -static const int xscale = -10; -static const int yscale = 1; -static const int pianoWidth = 40; -static int ScoreEditTools = PointerTool | PencilTool | RubberTool | DrawTool; + + +//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) + +QString create_random_string(int len=8) +{ + string result; + + for (int i=0;i<len;i++) + result+=char((rand() % 26) + 'A'); + + return QString(result.c_str()); +} + + + + + + +QPixmap *pix_whole, *pix_half, *pix_quarter; // arrays [NUM_MYCOLORS] +QPixmap *pix_dot, *pix_b, *pix_sharp, *pix_noacc; // arrays [NUM_MYCOLORS] +QPixmap *pix_r1, *pix_r2, *pix_r4, *pix_r8, *pix_r16, *pix_r32; // 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_initalized=false; +QColor* mycolors; // array [NUM_MYCOLORS] + + + + + +int ScoreEdit::serial=1; +set<QString> ScoreEdit::names; + //--------------------------------------------------------- // 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()); - - } +ScoreEdit::ScoreEdit(QWidget* parent, const char* name, unsigned initPos) + : TopWin(parent, name) +{ + setAttribute(Qt::WA_DeleteOnClose); + + mainw = new QWidget(this); + + mainGrid = new QGridLayout(); + mainw->setLayout(mainGrid); + + mainGrid->setContentsMargins(0, 0, 0, 0); + mainGrid->setSpacing(0); + setCentralWidget(mainw); + + + + + + score_canvas=new ScoreCanvas(this, mainw, 1, 1); + xscroll = new QScrollBar(Qt::Horizontal, mainw); + yscroll = new QScrollBar(Qt::Vertical, mainw); + + connect(xscroll, SIGNAL(valueChanged(int)), score_canvas, SLOT(x_scroll_event(int))); + connect(score_canvas, SIGNAL(xscroll_changed(int)), xscroll, SLOT(setValue(int))); + + connect(yscroll, SIGNAL(valueChanged(int)), score_canvas, SLOT(y_scroll_event(int))); + connect(score_canvas, SIGNAL(yscroll_changed(int)), yscroll, 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(score_canvas, SIGNAL(canvas_height_changed(int)), SLOT(canvas_height_changed(int))); + connect(score_canvas, SIGNAL(viewport_height_changed(int)), SLOT(viewport_height_changed(int))); + + connect(song, SIGNAL(songChanged(int)), score_canvas, SLOT(song_changed(int))); + + mainGrid->addWidget(score_canvas, 0, 0); + mainGrid->addWidget(xscroll,1,0); + mainGrid->addWidget(yscroll,0,1); + + xscroll->setMinimum(0); + yscroll->setMinimum(0); + + menu_mapper=new QSignalMapper(this); + connect(menu_mapper, SIGNAL(mapped(int)), SLOT(menu_command(int))); + + + + // Toolbars --------------------------------------------------------- + QToolBar* undo_tools=addToolBar(tr("Undo/Redo tools")); + undo_tools->setObjectName("Undo/Redo tools"); + undo_tools->addActions(undoRedo->actions()); + addToolBar(undo_tools); + + EditToolBar* edit_tools = new EditToolBar(this, PointerTool | PencilTool | RubberTool); + addToolBar(edit_tools); + edit_tools->set(PointerTool); + score_canvas->set_tool(PointerTool); + connect(edit_tools, SIGNAL(toolChanged(int)), score_canvas, SLOT(set_tool(int))); + + QToolBar* panic_toolbar = addToolBar(tr("panic")); + panic_toolbar->setObjectName("panic"); + panic_toolbar->addAction(panicAction); + + QToolBar* transport_toolbar = addToolBar(tr("transport")); + transport_toolbar->setObjectName("transport"); + transport_toolbar->addActions(transportAction->actions()); + + QToolBar* newnote_toolbar = addToolBar(tr("New note settings")); + newnote_toolbar->setObjectName("New note settings"); + newnote_toolbar->addWidget(new QLabel(tr("Note length:"), newnote_toolbar)); + QActionGroup* len_actions=new QActionGroup(this); + QAction* n1_action = newnote_toolbar->addAction("1", menu_mapper, SLOT(map())); + QAction* n2_action = newnote_toolbar->addAction("2", menu_mapper, SLOT(map())); + QAction* n4_action = newnote_toolbar->addAction("4", menu_mapper, SLOT(map())); + QAction* n8_action = newnote_toolbar->addAction("8", menu_mapper, SLOT(map())); + QAction* n16_action = newnote_toolbar->addAction("16", menu_mapper, SLOT(map())); + QAction* n32_action = newnote_toolbar->addAction("32", menu_mapper, SLOT(map())); + QAction* nlast_action = newnote_toolbar->addAction(tr("last"), menu_mapper, SLOT(map())); + menu_mapper->setMapping(n1_action, CMD_NOTELEN_1); + menu_mapper->setMapping(n2_action, CMD_NOTELEN_2); + menu_mapper->setMapping(n4_action, CMD_NOTELEN_4); + menu_mapper->setMapping(n8_action, CMD_NOTELEN_8); + menu_mapper->setMapping(n16_action, CMD_NOTELEN_16); + menu_mapper->setMapping(n32_action, CMD_NOTELEN_32); + menu_mapper->setMapping(nlast_action, CMD_NOTELEN_LAST); + n1_action->setCheckable(true); + n2_action->setCheckable(true); + n4_action->setCheckable(true); + n8_action->setCheckable(true); + n16_action->setCheckable(true); + n32_action->setCheckable(true); + nlast_action->setCheckable(true); + len_actions->addAction(n1_action); + len_actions->addAction(n2_action); + len_actions->addAction(n4_action); + len_actions->addAction(n8_action); + len_actions->addAction(n16_action); + len_actions->addAction(n32_action); + len_actions->addAction(nlast_action); + + nlast_action->setChecked(true); + menu_command(CMD_NOTELEN_LAST); + + newnote_toolbar->addSeparator(); + + newnote_toolbar->addWidget(new QLabel(tr("Velocity:"), newnote_toolbar)); + QSpinBox* velo_spinbox = new QSpinBox(this); + velo_spinbox->setRange(0, 127); + velo_spinbox->setSingleStep(1); + connect(velo_spinbox, SIGNAL(valueChanged(int)), score_canvas, SLOT(set_newnote_velo(int))); + newnote_toolbar->addWidget(velo_spinbox); + velo_spinbox->setValue(64); + + newnote_toolbar->addWidget(new QLabel(tr("Off-Velocity:"), newnote_toolbar)); + QSpinBox* velo_off_spinbox = new QSpinBox(this); + velo_off_spinbox->setRange(0, 127); + velo_off_spinbox->setSingleStep(1); + connect(velo_off_spinbox, SIGNAL(valueChanged(int)), score_canvas, SLOT(set_newnote_velo_off(int))); + newnote_toolbar->addWidget(velo_off_spinbox); + velo_off_spinbox->setValue(64); + + + + QToolBar* quant_toolbar = addToolBar(tr("Quantisation settings")); + newnote_toolbar->setObjectName("Quantisation settings"); + quant_toolbar->addWidget(new QLabel(tr("Quantisation:"), quant_toolbar)); + QComboBox* quant_combobox = new QComboBox(this); + quant_combobox->addItem("2"); // if you add or remove items from + quant_combobox->addItem("4"); // here, also change quant_mapper[] + quant_combobox->addItem("8"); // in ScoreCanvas::set_quant()! + quant_combobox->addItem("16"); + quant_combobox->addItem("32"); + connect(quant_combobox, SIGNAL(currentIndexChanged(int)), score_canvas, SLOT(set_quant(int))); + quant_toolbar->addWidget(quant_combobox); + quant_combobox->setCurrentIndex(2); + + quant_toolbar->addSeparator(); + + quant_toolbar->addWidget(new QLabel(tr("Pixels per whole:"), quant_toolbar)); + QSpinBox* px_per_whole_spinbox = new QSpinBox(this); + px_per_whole_spinbox->setRange(10, 1200); + px_per_whole_spinbox->setSingleStep(50); + connect(px_per_whole_spinbox, SIGNAL(valueChanged(int)), score_canvas, SLOT(set_pixels_per_whole(int))); + connect(score_canvas, SIGNAL(pixels_per_whole_changed(int)), px_per_whole_spinbox, SLOT(setValue(int))); + quant_toolbar->addWidget(px_per_whole_spinbox); + px_per_whole_spinbox->setValue(300); + + QMenu* settings_menu = menuBar()->addMenu(tr("&Settings")); + + QMenu* color_menu = settings_menu->addMenu(tr("Note head &colors")); + QActionGroup* color_actions = new QActionGroup(this); + QAction* color_black_action = color_menu->addAction(tr("&Black"), menu_mapper, SLOT(map())); + QAction* color_velo_action = color_menu->addAction(tr("&Velocity"), menu_mapper, SLOT(map())); + QAction* color_part_action = color_menu->addAction(tr("&Part"), menu_mapper, SLOT(map())); + color_black_action->setCheckable(true); + color_velo_action->setCheckable(true); + color_part_action->setCheckable(true); + color_actions->addAction(color_black_action); + color_actions->addAction(color_velo_action); + color_actions->addAction(color_part_action); + menu_mapper->setMapping(color_black_action, CMD_COLOR_BLACK); + menu_mapper->setMapping(color_velo_action, CMD_COLOR_VELO); + menu_mapper->setMapping(color_part_action, CMD_COLOR_PART); + + color_black_action->setChecked(true); + menu_command(CMD_COLOR_BLACK); + + QMenu* preamble_menu = settings_menu->addMenu(tr("Set up &preamble")); + QAction* preamble_keysig_action = preamble_menu->addAction(tr("Display &key signature")); + QAction* preamble_timesig_action = preamble_menu->addAction(tr("Display &time signature")); + connect(preamble_keysig_action, SIGNAL(toggled(bool)), score_canvas, SLOT(preamble_keysig_slot(bool))); + connect(preamble_timesig_action, SIGNAL(toggled(bool)), score_canvas, SLOT(preamble_timesig_slot(bool))); + + preamble_keysig_action->setCheckable(true); + preamble_timesig_action->setCheckable(true); + + preamble_keysig_action->setChecked(true); + preamble_timesig_action->setChecked(true); + + QAction* set_name_action = settings_menu->addAction(tr("Set Score &name"), menu_mapper, SLOT(map())); + menu_mapper->setMapping(set_name_action, CMD_SET_NAME); + + + + + + + score_canvas->song_changed(SC_EVENT_INSERTED); + score_canvas->goto_tick(initPos,true); + + if (name!=NULL) + set_name(name, false, true); + else + set_name("Score "+IntToQStr(serial++), false, true); +} + -//--------------------------------------------------------- -// songChanged1 -//--------------------------------------------------------- +void ScoreEdit::add_parts(PartList* pl, bool all_in_one) +{ + score_canvas->add_staves(pl, all_in_one); +} -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(); - } +bool ScoreEdit::set_name(QString 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; + } +} //--------------------------------------------------------- -// configChanged +// ~ScoreEdit //--------------------------------------------------------- -void ScoreEdit::configChanged() - { - initShortcuts(); - //trackInfo->updateTrackInfo(); - } +ScoreEdit::~ScoreEdit() +{ + +} -//--------------------------------------------------------- -// updateHScrollRange -//--------------------------------------------------------- -void ScoreEdit::updateHScrollRange() +void ScoreEdit::canvas_width_changed(int width) +{ + xscroll->setMaximum(width); +} +void ScoreEdit::viewport_width_changed(int width) { - 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); + xscroll->setPageStep(width * PAGESTEP); } -void ScoreEdit::updateTrackInfo() +void ScoreEdit::canvas_height_changed(int height) { - selected = curCanvasPart()->track(); - if (selected->isMidiTrack()) { - midiTrackInfo->setTrack(selected); - ///midiTrackInfo->updateTrackInfo(-1); - } + int val=height - score_canvas->viewport_height(); + if (val<=0) val=0; + + yscroll->setMaximum(val); + + if (val==0) + yscroll->hide(); + else + yscroll->show(); +} +void ScoreEdit::viewport_height_changed(int height) +{ + int val=score_canvas->canvas_height() - height; + if (val<0) val=0; + yscroll->setPageStep(height * PAGESTEP); + yscroll->setMaximum(val); + + if (val==0) + yscroll->hide(); + else + yscroll->show(); } -//--------------------------------------------------------- -// follow -//--------------------------------------------------------- +void ScoreEdit::closeEvent(QCloseEvent* e) +{ + QSettings settings("MusE", "MusE-qt"); + //settings.setValue("ScoreEdit/geometry", saveGeometry()); + settings.setValue("ScoreEdit/windowState", saveState()); -void ScoreEdit::follow(int pos) - { - int s, e; - canvas->range(&s, &e); + emit deleted((unsigned long)this); + e->accept(); +} - if (pos < e && pos >= s) - hscroll->setOffset(pos); - if (pos < s) - hscroll->setOffset(s); - } +void ScoreEdit::menu_command(int cmd) +{ + switch (cmd) + { + case CMD_SET_NAME: + { + bool ok; + QString newname = QInputDialog::getText(this, tr("Enter the new score title"), + tr("Enter the new score title"), QLineEdit::Normal, + name, &ok); + if (ok) + { + if (!set_name(newname)) + QMessageBox::warning(this, tr("Error"), tr("Changing score title failed:\nthe selected title is not unique")); + } + } + + default: + score_canvas->menu_command(cmd); + } +} -//--------------------------------------------------------- -// setTime -//--------------------------------------------------------- -void ScoreEdit::setTime(unsigned tick) - { - toolbar->setTime(tick); - time->setPos(3, tick, false); - } -//--------------------------------------------------------- -// ~ScoreEdit -//--------------------------------------------------------- +void ScoreCanvas::add_staves(PartList* pl, bool all_in_one) +{ + staff_t staff(this); + + 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.cleanup_parts(); + + staff.split_note=SPLIT_NOTE; + + staff.type=GRAND_TOP; //FINDME_INITCLEF + staff.clef=VIOLIN; + staves.push_back(staff); + + staff.type=GRAND_BOTTOM; + staff.clef=BASS; + staves.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.cleanup_parts(); + + staff.split_note=SPLIT_NOTE; + + staff.type=GRAND_TOP; //FINDME_INITCLEF + staff.clef=VIOLIN; + staves.push_back(staff); + + staff.type=GRAND_BOTTOM; + staff.clef=BASS; + staves.push_back(staff); + } + } + + cleanup_staves(); + recalc_staff_pos(); + song_changed(SC_EVENT_INSERTED); +} -ScoreEdit::~ScoreEdit() - { - // undoRedo->removeFrom(tools); // p4.0.6 Removed - } -//--------------------------------------------------------- -// cmd -// pulldown menu commands -//--------------------------------------------------------- +ScoreCanvas::ScoreCanvas(ScoreEdit* pr, QWidget* parent_widget, + int sx, int sy) : View(parent_widget, sx, sy) +{ + parent = pr; + setFocusPolicy(Qt::StrongFocus); + setBg(Qt::white); + + setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding); + + init_pixmaps(); + + x_pos=0; + x_left=0; + y_pos=0; + dragging=false; + mouse_erases_notes=false; + mouse_inserts_notes=true; + + selected_part=NULL; + + last_len=384; + new_len=-1; + + set_quant(2); //this is actually unneccessary, as while + //initalizing the quant_combobox, this gets + //called again. but for safety... + set_pixels_per_whole(300); //same as above. but safety rocks + + set_newnote_velo(64); + set_newnote_velo_off(64); + + dragging_staff=false; + + + coloring_mode=COLOR_MODE_BLACK; + preamble_contains_keysig=true; + preamble_contains_timesig=true; + + + x_scroll_speed=0; + x_scroll_pos=0; + y_scroll_speed=0; + y_scroll_pos=0; + connect (heartBeatTimer, SIGNAL(timeout()), SLOT(heartbeat_timer_event())); + + connect(song, SIGNAL(posChanged(int, unsigned, bool)), SLOT(pos_changed(int,unsigned,bool))); + connect(song, SIGNAL(playChanged(bool)), SLOT(play_changed(bool))); + connect(muse, SIGNAL(configChanged()), SLOT(config_changed())); + + + 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::cmd(int cmd) - { - ((PianoCanvas*)canvas)->cmd(cmd, _quantStrength, _quantLimit, _quantLen, _to); - } +} -//--------------------------------------------------------- -// setSelection -// update Info Line -//--------------------------------------------------------- +void ScoreCanvas::staffmode_treble_slot() +{ + set_staffmode(current_staff, MODE_TREBLE); +} -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(); - } +void ScoreCanvas::staffmode_bass_slot() +{ + set_staffmode(current_staff, MODE_BASS); +} -//--------------------------------------------------------- -// edit currently selected Event -//--------------------------------------------------------- +void ScoreCanvas::staffmode_both_slot() +{ + set_staffmode(current_staff, MODE_BOTH); +} -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); - } - } +void ScoreCanvas::remove_staff_slot() +{ + remove_staff(current_staff); +} -//--------------------------------------------------------- -// addCtrl -//--------------------------------------------------------- +void ScoreCanvas::set_staffmode(list<staff_t>::iterator it, staff_mode_t mode) +{ + if (it->type == GRAND_BOTTOM) + { + it--; + if (it->type!=GRAND_TOP) + cerr << "ERROR: 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) + cerr << "ERROR: THIS SHOULD NEVER HAPPEN: grand_top without bottom!"<<endl; + staves.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; + + staves.insert(it, staff_t(this, GRAND_TOP, VIOLIN, it->parts, it->split_note)); + break; + + default: + cerr << "ERROR: ILLEGAL FUNCTION CALL: invalid mode in set_staffmode" << endl; + } + + recalc_staff_pos(); + song_changed(SC_EVENT_INSERTED); +} -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; - } +void ScoreCanvas::remove_staff(list<staff_t>::iterator it) +{ + if (it->type == GRAND_BOTTOM) + { + it--; + if (it->type!=GRAND_TOP) + cerr << "ERROR: THIS SHOULD NEVER HAPPEN: grand_bottom without top!"<<endl; + } + + if (it->type == NORMAL) + { + staves.erase(it); + } + else if (it->type == GRAND_TOP) + { + staves.erase(it++); + if (it->type!=GRAND_BOTTOM) + cerr << "ERROR: THIS SHOULD NEVER HAPPEN: grand_top without bottom!"<<endl; + staves.erase(it); + } + + maybe_close_if_empty(); + recalc_staff_pos(); + song_changed(SC_EVENT_INSERTED); +} -//--------------------------------------------------------- -// removeCtrl -//--------------------------------------------------------- +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) + cerr << "ERROR: THIS SHOULD NEVER HAPPEN: grand_bottom without top!"<<endl; + } + + if (src->type == GRAND_BOTTOM) + { + src--; + if (src->type!=GRAND_TOP) + cerr << "ERROR: 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) + cerr << "ERROR: 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(SC_EVENT_INSERTED); +} -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::song_changed(int flags) +{ + if (flags & (SC_PART_MODIFIED | + SC_EVENT_INSERTED | SC_EVENT_MODIFIED | SC_EVENT_REMOVED | + SC_SIG | SC_KEY) ) + { + calc_pos_add_list(); + + for (list<staff_t>::iterator it=staves.begin(); it!=staves.end(); it++) + it->recalculate(); + + redraw(); + emit canvas_width_changed(canvas_width()); + } + + if (flags & SC_PART_REMOVED) + { + bool something_changed=false; + + for (list<staff_t>::iterator it=staves.begin(); it!=staves.end(); it++) + { + if (it->cleanup_parts()) + something_changed=true; + } + + cleanup_staves(); + recalc_staff_pos(); + + for (list<staff_t>::iterator it=staves.begin(); it!=staves.end(); it++) + it->recalculate(); + + redraw(); + } +} -void ScoreEdit::closeEvent(QCloseEvent* e) - { - QSettings settings("MusE", "MusE-qt"); - //settings.setValue("ScoreEdit/geometry", saveGeometry()); - settings.setValue("ScoreEdit/windowState", saveState()); +int ScoreCanvas::canvas_width() +{ + //return tick_to_x(staves.begin()->itemlist.rbegin()->first); + return tick_to_x(SONG_LENGTH); +} - emit deleted((unsigned long)this); - e->accept(); - } +int ScoreCanvas::canvas_height() +{ + return staves.rbegin()->y_bottom; +} -//--------------------------------------------------------- -// readConfiguration -//--------------------------------------------------------- +int ScoreCanvas::viewport_width() +{ + return (width() - x_left); +} -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; - } - } - } +int ScoreCanvas::viewport_height() +{ + return height(); +} -//--------------------------------------------------------- -// writeConfiguration -//--------------------------------------------------------- +string IntToStr(int i) +{ + ostringstream s; + s<<i; + return s.str(); +} -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"); - } +QString IntToQStr(int i) +{ + return QString(IntToStr(i).c_str()); +} -//--------------------------------------------------------- -// soloChanged -// signal from solo button -//--------------------------------------------------------- +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; + } +} -void ScoreEdit::soloChanged(bool flag) - { - audio->msgSetSolo(canvas->track(), flag); - song->update(SC_SOLO); - } +void load_colored_pixmaps(QString file, QPixmap* array) +{ + QImage img(file); + + for (int color_index=0;color_index<NUM_MYCOLORS; color_index++) + { + color_image(img, mycolors[color_index]); + array[color_index]=QPixmap::fromImage(img); + } +} -//--------------------------------------------------------- -// setRaster -//--------------------------------------------------------- -void ScoreEdit::setRaster(int val) - { - _rasterInit = val; - MidiEditor::setRaster(val); - canvas->redrawGrid(); - canvas->setFocus(); // give back focus after kb input - } -//--------------------------------------------------------- -// setQuant -//--------------------------------------------------------- +void ScoreCanvas::init_pixmaps() +{ + if (!pixmaps_initalized) + { + if (debugMsg) cout << "initalizing colors..." << endl; + + mycolors=new QColor[NUM_MYCOLORS]; + + mycolors[0]=Qt::black; + for (int i=1;i<NUM_PARTCOLORS;i++) + mycolors[i]=config.partColors[i]; + mycolors[BLACK_PIXMAP]=Qt::black; + mycolors[HIGHLIGHTED_PIXMAP]=Qt::red; + + for (int i=0; i<64; i++) + mycolors[i+VELO_PIXMAP_BEGIN]=QColor(i*4,0,0xff); + for (int i=64; i<128; i++) + mycolors[i+VELO_PIXMAP_BEGIN]=QColor(0xff,0,(127-i)*4); + + + if (debugMsg) cout << "loading pixmaps..." << endl; + + pix_whole=new QPixmap[NUM_MYCOLORS]; + pix_half=new QPixmap[NUM_MYCOLORS]; + pix_quarter=new QPixmap[NUM_MYCOLORS]; + pix_dot=new QPixmap[NUM_MYCOLORS]; + pix_b=new QPixmap[NUM_MYCOLORS]; + pix_sharp=new QPixmap[NUM_MYCOLORS]; + pix_noacc=new QPixmap[NUM_MYCOLORS]; + 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_r32=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(museGlobalShare + "/scoreglyphs/whole.png", pix_whole); + load_colored_pixmaps(museGlobalShare + "/scoreglyphs/half.png", pix_half); + load_colored_pixmaps(museGlobalShare + "/scoreglyphs/quarter.png", pix_quarter); + load_colored_pixmaps(museGlobalShare + "/scoreglyphs/dot.png", pix_dot); + load_colored_pixmaps(museGlobalShare + "/scoreglyphs/acc_none.png", pix_noacc); + load_colored_pixmaps(museGlobalShare + "/scoreglyphs/acc_sharp.png", pix_sharp); + load_colored_pixmaps(museGlobalShare + "/scoreglyphs/acc_b.png", pix_b); + + pix_r1->load(museGlobalShare + "/scoreglyphs/rest1.png"); + pix_r2->load(museGlobalShare + "/scoreglyphs/rest2.png"); + pix_r4->load(museGlobalShare + "/scoreglyphs/rest4.png"); + pix_r8->load(museGlobalShare + "/scoreglyphs/rest8.png"); + pix_r16->load(museGlobalShare + "/scoreglyphs/rest16.png"); + pix_r32->load(museGlobalShare + "/scoreglyphs/rest32.png"); + pix_flag_up[0].load(museGlobalShare + "/scoreglyphs/flags8u.png"); + pix_flag_up[1].load(museGlobalShare + "/scoreglyphs/flags16u.png"); + pix_flag_up[2].load(museGlobalShare + "/scoreglyphs/flags32u.png"); + pix_flag_up[3].load(museGlobalShare + "/scoreglyphs/flags64u.png"); + pix_flag_down[0].load(museGlobalShare + "/scoreglyphs/flags8d.png"); + pix_flag_down[1].load(museGlobalShare + "/scoreglyphs/flags16d.png"); + pix_flag_down[2].load(museGlobalShare + "/scoreglyphs/flags32d.png"); + pix_flag_down[3].load(museGlobalShare + "/scoreglyphs/flags64d.png"); + + pix_clef_violin->load(museGlobalShare + "/scoreglyphs/clef_violin_big.png"); + pix_clef_bass->load(museGlobalShare + "/scoreglyphs/clef_bass_big.png"); + + for (int i=0;i<10;i++) + pix_num[i].load(museGlobalShare + "/scoreglyphs/"+IntToQStr(i)+".png"); + + pixmaps_initalized=true; + + if (debugMsg) cout << "done" << endl; + } +} -void ScoreEdit::setQuant(int val) - { - _quantInit = val; - MidiEditor::setQuant(val); - canvas->setFocus(); - } -//--------------------------------------------------------- -// writeStatus -//--------------------------------------------------------- -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"); - } +int modulo(int a, int b) // similar to a % b +{ + return (((a%b)+b)%b); +} -//--------------------------------------------------------- -// readStatus -//--------------------------------------------------------- +int divide_floor(int a, int b) // similar to a / b +{ + return int(floor(float(a)/float(b))); +} -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 - }; +#define DEFAULT_REST_HEIGHT 6 -//--------------------------------------------------------- -// 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); - } +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; +} -//--------------------------------------------------------- -// configQuant -//--------------------------------------------------------- -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 flo_quantize(int tick, int quant_ticks) +{ + return int(nearbyint((float)tick / quant_ticks))*quant_ticks; +} + +int flo_quantize_floor(int tick, int quant_ticks) +{ + return int(tick / quant_ticks) * quant_ticks; +} -//--------------------------------------------------------- -// setSteprec -//--------------------------------------------------------- + +/* 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() +{ + 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(), parent->quant_ticks()); + end=flo_quantize(event.endTick()+part->tick(), parent->quant_ticks()); + if (heavyDebugMsg) 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)) + to=SONG_LENGTH; + + if (heavyDebugMsg) 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++; +} -void ScoreEdit::setSteprec(bool flag) - { - canvas->setSteprec(flag); - if (flag == false) - midiin->setChecked(flag); - } -//--------------------------------------------------------- -// eventColorModeChanged -//--------------------------------------------------------- +bool is_sharp_key(key_enum t) +{ + return ((t>=KEY_SHARP_BEGIN) && (t<=KEY_SHARP_END)); +} +bool is_b_key(key_enum t) +{ + return ((t>=KEY_B_BEGIN) && (t<=KEY_B_END)); +} -void ScoreEdit::eventColorModeChanged(int mode) - { - colorMode = mode; - colorModeInit = colorMode; - - ((PianoCanvas*)(canvas))->setColorMode(colorMode); - } +int n_accidentials(key_enum t) +{ + if (is_sharp_key(t)) + return t-KEY_SHARP_BEGIN-1; + else + return t-KEY_B_BEGIN-1; +} -//--------------------------------------------------------- -// setEventColorMode -//--------------------------------------------------------- -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); - } +//note needs to be 0..11 +//always assumes violin clef +//only for internal use +note_pos_t note_pos_(int note, key_enum 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)) + cerr << "ERROR: 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==KEY_GES) + { + // convert a H to a Ces + if (note==11) + { + result.height=12; + result.vorzeichen=B; + } + } + else if (key==KEY_FIS) + { + // convert a F to an Eis + if (note==5) + { + result.height=2; + result.vorzeichen=SHARP; + } + } + + return result; +} -//--------------------------------------------------------- -// clipboardChanged -//--------------------------------------------------------- -void ScoreEdit::clipboardChanged() - { - editPasteAction->setEnabled(QApplication::clipboard()->mimeData()->hasFormat(QString("text/x-muse-eventlist"))); - } +// 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 -//--------------------------------------------------------- -// selectionChanged -//--------------------------------------------------------- +// the "spaces" in between the lines have odd numbers. +// that is, the space between line 2 and 4 is numbered 3. -void ScoreEdit::selectionChanged() - { - bool flag = canvas->selectionSize() > 0; - editCutAction->setEnabled(flag); - editCopyAction->setEnabled(flag); - editDelEventsAction->setEnabled(flag); - } +// 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 -//--------------------------------------------------------- -// setSpeaker -//--------------------------------------------------------- +note_pos_t note_pos (unsigned note, key_enum 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; +} + + +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; +} + + +int calc_measure_len(const list<int>& nums, int denom) +{ + int sum=0; + + for (list<int>::const_iterator it=nums.begin(); it!=nums.end(); it++) + sum+=*it; + + return 64* sum/denom; +} + +vector<int> create_emphasize_list(const list<int>& nums, int denom) +{ + if (heavyDebugMsg) + { + 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; + + if (heavyDebugMsg) + { + for (int i=0;i<len;i++) + { + if (i%8==0) + cout << endl<<i<<":\t"; + cout << result[i]<<" "; + } + cout << endl; + } + + return result; +} + +vector<int> create_emphasize_list(int num, int denom) +{ + 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_power2 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, int quant_power2, bool allow_dots, bool allow_normal) +{ + list<note_len_t> retval; + + if (len_ticks<0) + cerr << "ERROR: ILLEGAL FUNCTION CALL in parse_note_len: len_ticks < 0" << endl; + if (begin_tick<0) + cerr << "ERROR: ILLEGAL FUNCTION CALL in parse_note_len: begin_tick < 0" << endl; + + if (allow_normal) + { + int dot_max = allow_dots ? quant_power2 : 0; + + for (int i=0;i<=quant_power2;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; + + if (heavyDebugMsg) cout << "add " << len_now << " ticks" << endl; + if (allow_dots) + { + for (int i=0;i<=quant_power2;i++) + for (int j=0;j<=quant_power2-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_power2; 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) + cerr << "ERROR: 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 YLEN 10 +#define NOTE_SHIFT 3 + +#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 -void ScoreEdit::setSpeaker(bool val) - { - _playEvents = val; - canvas->playEvents(_playEvents); - } + +#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() +{ + key_enum tmp_key=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); + + if (heavyDebugMsg) + { + 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-parent->quant_ticks())/2; + if (heavyDebugMsg) 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) + { + if (heavyDebugMsg) 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,parent->quant_power2(),DOTTED_RESTS,UNSPLIT_RESTS); + unsigned tmppos=lastevent; + for (list<note_len_t>::iterator x=lens.begin(); x!=lens.end(); x++) + { + if (heavyDebugMsg) 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) + { + if (heavyDebugMsg) 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,parent->quant_power2(),DOTTED_RESTS,UNSPLIT_RESTS); + unsigned tmppos=lastevent; + for (list<note_len_t>::iterator x=lens.begin(); x!=lens.end(); x++) + { + if (heavyDebugMsg) 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) ); + } + } + + + + if (heavyDebugMsg) 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))); + + if (heavyDebugMsg) cout << "\t\tnote was split to length "<<tmplen<<" + " << newlen<<endl; + } + else + { + tmplen=len; + tied_note=false; + + if (heavyDebugMsg) 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,parent->quant_power2(),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++) + { + if (heavyDebugMsg) 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) + { + if (heavyDebugMsg) 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) + { + if (heavyDebugMsg) cout << "inserting KEY CHANGE ("<<it->second.key<<") at "<<t<<endl; + itemlist[t].insert( FloItem(FloItem::KEY_CHANGE, it->second.key) ); + tmp_key=it->second.key; + } + } +} + +void staff_t::process_itemlist() +{ + map<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; + + if (heavyDebugMsg) 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 + 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); + } + + if (heavyDebugMsg) + { + cout << "occupied: "; + for (map<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)) + { + if (heavyDebugMsg) 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();) + { + if (heavyDebugMsg) 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) + { + if (heavyDebugMsg) 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! + if (heavyDebugMsg) 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 + { + if (heavyDebugMsg) cout << "we can NOT, because that item is not a rest" << endl; + //stop grouping that rest + goto get_out_here; + } + } + if (heavyDebugMsg) 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) + { + if (heavyDebugMsg) 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 + { + if (heavyDebugMsg) 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++); + + if (heavyDebugMsg) 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: 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: + map<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); + + if (heavyDebugMsg) + { + cout << "note lengths at that time are:"; + for (map<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) + { + if (heavyDebugMsg) 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; + if (heavyDebugMsg) 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) + { + map<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; + if (heavyDebugMsg) 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; + + + map<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); + + if (heavyDebugMsg) cout << "we have "<<lengths.size()<<" groups. putting the "<<group1_n<<" longest and the "<<group2_n<<"shortest groups together"<<endl << + "\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 + if (heavyDebugMsg) cout << "\tprocessing note-item with len="<<it->len<<endl; + if (it->len<group1_len) + { + if (heavyDebugMsg) 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,parent->quant_power2(),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++) + { + if (heavyDebugMsg) 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)) + { + if (heavyDebugMsg) 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,parent->quant_power2(),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++) + { + if (heavyDebugMsg) 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? + { + if (heavyDebugMsg) 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) +{ + if (heavyDebugMsg) cout << "drawing pixmap with 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() +{ + key_enum curr_key=KEY_C; //this has to be KEY_C or KEY_C_B and nothing else, + //because only with these two keys the next (initial) + //key signature is properly drawn. + 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 * parent->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+=parent->note_x_indent() + 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()) + cerr << "ERROR: 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; + case 5: it->pix=pix_r32; break; + } + + it->x+=parent->note_x_indent() + (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) + { + key_enum new_key=it->key; + + 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 + key_enum curr_key=KEY_C; //this has to be KEY_C or KEY_C_B and nothing else, + //because only with these two keys the next (initial) + //key signature is properly calculated. + for (iKeyEvent it=keymap.begin(); it!=keymap.end(); it++) + { + key_enum 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]; + key_enum 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++) + { + if (heavyDebugMsg) 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) + { + if (heavyDebugMsg) + { + 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)) + cerr << "ERROR: THIS SHOULD NEVER HAPPEN: upflag != this->flag" << endl; + upflag=it->len; + + if ((upstem_x!=-1) && (upstem_x!=it->stem_x )) + cerr << "ERROR: 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)) + cerr << "ERROR: THIS SHOULD NEVER HAPPEN: downflag != this->flag" << endl; + downflag=it->len; + + if ((downstem_x!=-1) && (downstem_x!=it->stem_x)) + cerr << "ERROR: 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; + switch (coloring_mode) + { + case COLOR_MODE_BLACK: + color_index=BLACK_PIXMAP; + break; + + case COLOR_MODE_PART: + color_index=it->source_part->colorIndex(); + break; + + case COLOR_MODE_VELO: + color_index=VELO_PIXMAP_BEGIN + it->source_event->velo(); + break; + } + 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]); + + //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) + { + if (heavyDebugMsg) 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) , mycolors[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) + { + if (heavyDebugMsg) + { + 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) + { + if (heavyDebugMsg) 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) + { + if (heavyDebugMsg) 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) + { + key_enum new_key=it->key; + if (heavyDebugMsg) 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=staves.begin(); it!=staves.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: + cerr << "ERROR: 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 + +#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; + + + // draw accidentials ------------------------------------------------ + if (preamble_contains_keysig) + { + x_left+=KEYCHANGE_ACC_LEFTDIST; + + key_enum 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; + } + + + // draw time signature ---------------------------------------------- + if (preamble_contains_timesig) + { + x_left+=TIMESIG_LEFTMARGIN; + + 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&) +{ + if (debugMsg) cout <<"now in ScoreCanvas::draw"<<endl; + + + + p.setPen(Qt::black); + + for (list<staff_t>::iterator it=staves.begin(); it!=staves.end(); it++) + { + //TODO: maybe only draw visible staves? + draw_note_lines(p,it->y_draw - y_pos); + draw_preamble(p,it->y_draw - y_pos, it->clef); + p.setClipRect(x_left+1,0,p.device()->width(),p.device()->height()); + draw_items(p,it->y_draw - y_pos, *it); + p.setClipping(false); + } + + if (debugMsg) cout << "drawing done." << endl; +} + + +list<int> calc_accidentials(key_enum key, clef_t clef, key_enum 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; + + for (std::map<int,int>::iterator it=pos_add_list.begin(); it!=pos_add_list.end() && it->first<t; it++) + { + min_t=it->first; + x-=it->second; + t=TICKS_PER_WHOLE * x/pixels_per_whole(); + } + + return t > min_t ? t : min_t; +} + +key_enum ScoreCanvas::key_at_tick(int t_) +{ + unsigned int t= (t_>=0) ? t_ : 0; + + return keymap.keyAtTick(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: + cerr << "ERROR: THIS SHOULD NEVER HAPPEN: unknown clef in height_to_pitch" << endl; + return 60; + } +} + +int ScoreCanvas::height_to_pitch(int h, clef_t clef, key_enum 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 staff_it=staff_at_y(event->y() + y_pos); + + int y=event->y() + y_pos - staff_it->y_draw; + int x=event->x()+x_pos-x_left; + int tick=flo_quantize_floor(x_to_tick(x), quant_ticks()); + + if (staff_it!=staves.end()) + { + if (event->x() <= x_left) //clicked in the preamble? + { + if (event->button() == Qt::RightButton) //right-click? + { + current_staff=staff_it; + staff_menu->popup(event->globalPos()); + } + else if (event->button() == Qt::MidButton) //middle click? + { + remove_staff(staff_it); + } + else if (event->button() == Qt::LeftButton) //left click? + { + current_staff=staff_it; + dragging_staff=true; + } + } + else + { + ScoreItemList& itemlist=staff_it->itemlist; + + if (debugMsg) cout << "mousePressEvent at "<<x<<"/"<<y<<"; tick="<<tick<<endl; + set<FloItem, floComp>::iterator set_it; + for (set_it=itemlist[tick].begin(); set_it!=itemlist[tick].end(); set_it++) + if (set_it->type==FloItem::NOTE) + if (set_it->bbox().contains(x,y)) + break; + + if (set_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, set_it->pos)); + if (found == itemlist[t].end()) + { + cerr << "ERROR: 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=set_it->begin_tick; + int total_end=t; + + int this_begin=tick; + int this_end=this_begin+calc_len(set_it->len, set_it->dots); + + selected_part=set_it->source_part; + + //that's the only note corresponding to the event? + if (this_begin==total_begin && this_end==total_end) + { + if (x < set_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; + } + + if (debugMsg) + cout << "you clicked at a note with begin at "<<set_it->begin_tick<<" and end at "<<t<<endl + << "x-drag-operation will be "<<mouse_x_drag_operation<<endl + << "pointer to part is "<<set_it->source_part << endl; + + if (set_it->source_part == NULL) cerr << "ERROR: THIS SHOULD NEVER HAPPEN: set_it->source_part is NULL!" << endl; + + + + dragged_event=*set_it->source_event; + dragged_event_part=set_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)) + { + Part* curr_part = NULL; + set<Part*> possible_dests=staff_it->parts_at_tick(tick); + + if (!possible_dests.empty()) + { + if (possible_dests.size()==1) + curr_part=*possible_dests.begin(); + else + { + if (possible_dests.find(selected_part)!=possible_dests.end()) + curr_part=selected_part; + else + QMessageBox::information(this, tr("Ambiguous part"), tr("There are two or more possible parts you could add the note to, but none matches the selected part. Please select the destination part by clicking on any note belonging to it and try again, or add a new stave containing only the destination part.")); + } + } + else + QMessageBox::information(this, tr("No part"), tr("There are no parts you could add the note to.")); + + if (curr_part!=NULL) + { + signed int relative_tick=(signed) tick - curr_part->tick(); + if (relative_tick<0) + cerr << "ERROR: THIS SHOULD NEVER HAPPEN: relative_tick is negative!" << endl; + 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, staff_it->clef)); + newevent.setVelo(newnote_velo); + newevent.setVeloOff(newnote_velo_off); + newevent.setTick(relative_tick); + newevent.setLenTick((new_len>0)?new_len:last_len); + + if (flo_quantize(newevent.lenTick(), quant_ticks()) <= 0) + { + newevent.setLenTick(quant_ticks()); + if (debugMsg) cout << "inserted note's length would be invisible after quantisation (too short)." << endl << + " setting it to " << newevent.lenTick() << endl; + } + + if (newevent.endTick() > curr_part->lenTick()) + { + if (debugMsg) cout << "clipping inserted note from len="<<newevent.endTick()<<" to len="<<(curr_part->lenTick() - newevent.tick())<<endl; + newevent.setLenTick(curr_part->lenTick() - newevent.tick()); + } + + 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(SC_EVENT_INSERTED); + + 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) || (mouse_operation==BEGIN)) //also BEGIN can change the len by clipping + { + if (flo_quantize(dragged_event.lenTick(), quant_ticks()) <= 0) + { + if (debugMsg) 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(), quant_ticks()); + } + } + + song->endUndo(SC_EVENT_MODIFIED); + setMouseTracking(false); + dragging=false; + + x_scroll_speed=0; x_scroll_pos=0; + } + } + + if (dragging_staff) + { + merge_staves(staff_at_y(event->y()+y_pos), current_staff); + dragging_staff=false; + + y_scroll_speed=0; y_scroll_pos=0; + } +} + +#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), quant_ticks()); + + if (mouse_operation==NO_OP) + { + if ((abs(dx)>DRAG_INIT_DISTANCE) && (mouse_x_drag_operation!=NO_OP)) + { + if (debugMsg) cout << "mouse-operation is now "<<mouse_x_drag_operation<<endl; + mouse_operation=mouse_x_drag_operation; + } + else if (abs(dy)>DRAG_INIT_DISTANCE) + { + if (debugMsg) cout << "mouse-operation is now PITCH" << endl; + mouse_operation=PITCH; + } + } + + int new_pitch; + + switch (mouse_operation) + { + case NONE: + break; + + case PITCH: + if (debugMsg) 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(SC_EVENT_INSERTED); + } + + break; + + case BEGIN: + if (dragged_event.tick()+dragged_event_part->tick() != unsigned(tick)) + { + Event tmp=dragged_event.clone(); + signed relative_tick=tick-signed(dragged_event_part->tick()); + + if (relative_tick >= 0) + tmp.setTick(relative_tick); + else + { + tmp.setTick(0); + if (debugMsg) cout << "not moving note before begin of part; setting it directly to the begin" << endl; + } + + if (tmp.endTick() > dragged_event_part->lenTick()) + { + signed new_len=dragged_event_part->lenTick() - tmp.tick(); + if (new_len>=0) + { + tmp.setLenTick(dragged_event_part->lenTick() - tmp.tick()); + if (debugMsg) cout << "moved note would exceed its part; clipping length to " << tmp.lenTick() << endl; + } + else + { + tmp.setLenTick(0); + if (debugMsg) cout << "moved note would exceed its part; clipping length to 0 (actually negative)" << endl; + } + } + + audio->msgChangeEvent(dragged_event, tmp, dragged_event_part, false, false, false); + dragged_event=tmp; + + song_changed(SC_EVENT_INSERTED); + } + + break; + + case LENGTH: + tick+=quant_ticks(); + if (dragged_event.tick()+dragged_event.lenTick() + dragged_event_part->tick() != unsigned(tick)) + { + Event tmp=dragged_event.clone(); + signed relative_tick=tick-signed(dragged_event_part->tick()); + signed new_len=relative_tick-dragged_event.tick(); + + if (new_len>=0) + tmp.setLenTick(new_len); + else + { + tmp.setLenTick(0); + if (debugMsg) cout << "not setting len to a negative value. using 0 instead" << endl; + } + + if (tmp.endTick() > dragged_event_part->lenTick()) + { + tmp.setLenTick(dragged_event_part->lenTick() - tmp.tick()); + if (debugMsg) cout << "resized note would exceed its part; limiting length to " << tmp.lenTick() << endl; + } + + audio->msgChangeEvent(dragged_event, tmp, dragged_event_part, false, false, false); + dragged_event=tmp; + + song_changed(SC_EVENT_INSERTED); + } + + break; + } + + + if ((mouse_operation==LENGTH) || (mouse_operation==BEGIN)) //x-scrolling enabled? + { + int win_x=event->x(); + + if (win_x < x_left + SCROLL_MARGIN) + { + x_scroll_speed=(win_x - (x_left + SCROLL_MARGIN)) * SCROLL_SPEED; + if (x_scroll_speed < -SCROLL_SPEED_MAX) x_scroll_speed=-SCROLL_SPEED_MAX; + } + else if (win_x > width() - SCROLL_MARGIN) + { + x_scroll_speed=(win_x - (width() - SCROLL_MARGIN)) * SCROLL_SPEED; + if (x_scroll_speed > SCROLL_SPEED_MAX) x_scroll_speed=SCROLL_SPEED_MAX; + } + else + x_scroll_speed=0; + } + else + { + x_scroll_speed=0; + } + } + + if (dragging_staff) //y-scrolling enabled? + { + int win_y=event->y(); + + if (win_y < SCROLL_MARGIN) + { + y_scroll_speed=(win_y - SCROLL_MARGIN) * SCROLL_SPEED; + if (y_scroll_speed < -SCROLL_SPEED_MAX) y_scroll_speed=-SCROLL_SPEED_MAX; + } + else if (win_y > height() - SCROLL_MARGIN) + { + y_scroll_speed=(win_y - (height() - SCROLL_MARGIN)) * SCROLL_SPEED; + if (y_scroll_speed > SCROLL_SPEED_MAX) y_scroll_speed=SCROLL_SPEED_MAX; + } + else + y_scroll_speed=0; + } + else + { + y_scroll_speed=0; + } +} + +void ScoreCanvas::heartbeat_timer_event() +{ + if (x_scroll_speed) + { + int old_xpos=x_pos; + + x_scroll_pos+=x_scroll_speed*heartBeatTimer->interval()/1000.0; + int tmp=int(x_scroll_pos); + if (tmp!=0) + x_pos+=tmp; + x_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 xscroll_changed(x_pos); + } + + if (y_scroll_speed) + { + int old_ypos=y_pos; + + y_scroll_pos+=y_scroll_speed*heartBeatTimer->interval()/1000.0; + int tmp=int(y_scroll_pos); + if (tmp!=0) + y_pos+=tmp; + y_scroll_pos-=tmp; + + if (y_pos<0) y_pos=0; + if (y_pos>canvas_height()) y_pos=canvas_height(); + + if (old_ypos!=y_pos) emit yscroll_changed(y_pos); + } +} + +void ScoreCanvas::x_scroll_event(int x) +{ + if (debugMsg) cout << "SCROLL EVENT: x="<<x<<endl; + x_pos=x; + redraw(); +} + +void ScoreCanvas::y_scroll_event(int y) +{ + if (debugMsg) cout << "SCROLL EVENT: y="<<y<<endl; + y_pos=y; + 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 xscroll_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 xscroll_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 xscroll_changed(x_pos); + } +} //--------------------------------------------------------- // resizeEvent //--------------------------------------------------------- -void ScoreEdit::resizeEvent(QResizeEvent* ev) - { - QWidget::resizeEvent(ev); - _widthInit = ev->size().width(); - _heightInit = ev->size().height(); - } +void ScoreCanvas::resizeEvent(QResizeEvent* ev) +{ + QWidget::resizeEvent(ev); + emit viewport_width_changed( viewport_width() ); + emit viewport_height_changed( viewport_height() ); +} -/* -//--------------------------------------------------------- -// trackInfoScroll -//--------------------------------------------------------- +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 ScoreEdit::trackInfoScroll(int y) - { - if (trackInfo->visibleWidget()) - trackInfo->visibleWidget()->move(0, -y); - } -*/ -//--------------------------------------------------------- -// initShortcuts -//--------------------------------------------------------- +void ScoreCanvas::recalc_staff_pos() +{ + int y=0; + + for (list<staff_t>::iterator it=staves.begin(); it!=staves.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: + cerr << "ERROR: THIS SHOULD NEVER HAPPEN: invalid staff type!" << endl; + } + y=it->y_bottom; + } + + emit canvas_height_changed( canvas_height() ); +} -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); - - } +list<staff_t>::iterator ScoreCanvas::staff_at_y(int y) +{ + for (list<staff_t>::iterator it=staves.begin(); it!=staves.end(); it++) + if ((y >= it->y_top) && (y < it->y_bottom)) + return it; -//--------------------------------------------------------- -// execDeliveredScript -//--------------------------------------------------------- -void ScoreEdit::execDeliveredScript(int id) + return staves.end(); +} + +void ScoreCanvas::play_changed(bool) { - //QString scriptfile = QString(INSTPREFIX) + SCRIPTSSUFFIX + deliveredScriptNames[id]; - QString scriptfile = song->getScriptPath(id, true); - song->executeScript(scriptfile.toAscii().data(), parts(), quant(), true); + redraw(); } -//--------------------------------------------------------- -// execUserScript -//--------------------------------------------------------- -void ScoreEdit::execUserScript(int id) +void ScoreCanvas::config_changed() { - QString scriptfile = song->getScriptPath(id, false); - song->executeScript(scriptfile.toAscii().data(), parts(), quant(), true); + redraw(); } -//--------------------------------------------------------- -// newCanvasWidth -//--------------------------------------------------------- +void ScoreCanvas::set_tool(int tool) +{ + switch (tool) + { + case PointerTool: mouse_erases_notes=false; mouse_inserts_notes=false; break; + case RubberTool: mouse_erases_notes=true; mouse_inserts_notes=false; break; + case PencilTool: mouse_erases_notes=false; mouse_inserts_notes=true; break; + default: + cerr << "ERROR: THIS SHOULD NEVER HAPPEN: set_tool called with unknown tool ("<<tool<<")"<<endl; + } +} -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(); -*/ - } +void ScoreCanvas::menu_command(int cmd) +{ + switch (cmd) + { + case CMD_COLOR_BLACK: coloring_mode=COLOR_MODE_BLACK; redraw(); break; + case CMD_COLOR_PART: coloring_mode=COLOR_MODE_PART; redraw(); break; + case CMD_COLOR_VELO: coloring_mode=COLOR_MODE_VELO; redraw(); break; + case CMD_NOTELEN_1: new_len=TICKS_PER_WHOLE/ 1; break; + case CMD_NOTELEN_2: new_len=TICKS_PER_WHOLE/ 2; break; + case CMD_NOTELEN_4: new_len=TICKS_PER_WHOLE/ 4; break; + case CMD_NOTELEN_8: new_len=TICKS_PER_WHOLE/ 8; break; + case CMD_NOTELEN_16: new_len=TICKS_PER_WHOLE/16; break; + case CMD_NOTELEN_32: new_len=TICKS_PER_WHOLE/32; break; + case CMD_NOTELEN_LAST: new_len=-1; break; + default: + cerr << "ERROR: ILLEGAL FUNCTION CALL: ScoreCanvas::menu_command called with unknown command ("<<cmd<<")"<<endl; + } +} -//--------------------------------------------------------- -// toggleTrackInfo -//--------------------------------------------------------- +void ScoreCanvas::preamble_keysig_slot(bool state) +{ + preamble_contains_keysig=state; + redraw(); +} +void ScoreCanvas::preamble_timesig_slot(bool state) +{ + preamble_contains_timesig=state; + redraw(); +} + +void ScoreCanvas::set_quant(int val) +{ + int quant_mapper[]={1,2,3,4,5}; + + if ((val>=0) && (val<signed(sizeof(quant_mapper)/sizeof(*quant_mapper)))) + { + int old_len=quant_len(); + + _quant_power2=quant_mapper[val]; + + set_pixels_per_whole(pixels_per_whole() * quant_len() / old_len ); + + song_changed(SC_EVENT_INSERTED); + } + else + { + cerr << "ERROR: ILLEGAL FUNCTION CALL: set_quant called with invalid value of "<<val<<endl; + } +} + +void ScoreCanvas::set_pixels_per_whole(int val) +{ + if (debugMsg) cout << "setting px per whole to " << val << endl; + _pixels_per_whole=val; + + for (list<staff_t>::iterator it=staves.begin(); it!=staves.end(); it++) + it->calc_item_pos(); + + emit pixels_per_whole_changed(val); + + redraw(); +} + +void ScoreCanvas::cleanup_staves() +{ + for (list<staff_t>::iterator it=staves.begin(); it!=staves.end();) + { + if (it->parts.empty()) + staves.erase(it++); + else + it++; + } + + maybe_close_if_empty(); +} + +void ScoreCanvas::maybe_close_if_empty() +{ + if (staves.empty()) + { + if (!parent->close()) + cerr << "ERROR: THIS SHOULD NEVER HAPPEN: tried to close, but event hasn't been accepted!" << endl; + } +} + +void ScoreCanvas::set_newnote_velo(int velo) +{ + newnote_velo=velo; +} + +void ScoreCanvas::set_newnote_velo_off(int velo) +{ + newnote_velo_off=velo; +} + +bool staff_t::cleanup_parts() +{ + bool did_something=false; + + for (set<Part*>::iterator it=parts.begin(); it!=parts.end();) + { + bool valid=false; + + for (iTrack track=song->tracks()->begin(); track!=song->tracks()->end(); track++) + if ((*track)->type() == Track::MIDI) + { + PartList* pl=(*track)->parts(); + for (iPart part=pl->begin(); part!=pl->end(); part++) + if (*it == part->second) + { + valid=true; + goto get_out_here2; + } + } + + get_out_here2: + if (!valid) + { + parts.erase(it++); + + did_something=true; + } + else + it++; + } + + return did_something; +} -void ScoreEdit::toggleTrackInfo() +set<Part*> staff_t::parts_at_tick(unsigned tick) { - bool vis = midiTrackInfo->isVisible(); - infoScroll->setVisible(!vis); - infoScroll->setEnabled(!vis); + set<Part*> result; + + for (set<Part*>::iterator it=parts.begin(); it!=parts.end(); it++) + if ((tick >= (*it)->tick()) && (tick<=(*it)->endTick())) + result.insert(*it); + + return result; } + +//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 the keymap is not used, this will probably lead to a bug + * same when mastertrack is disabled + * 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 + * + * CURRENT TODO + * o clean up code (find TODOs) + * + * IMPORTANT TODO + * o save and restore window settings, automatically reopen windows + * after loading file etc + * o offer functions like in the pianoroll: quantize etc. + * o support selections + * + * less important stuff + * o deal with expanding parts + * o do all the song_changed(SC_EVENT_INSERTED) properly + * o add tracks in correct order to score + * o draw measure numbers + * o use timesig_t in all timesig-stuff + * o use bars instead of flags over groups of 8ths / 16ths etc + * 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 draw a margin around notes which are in a bright color + * o refuse to resize so that width gets smaller or equal than x_left + * o use the proper quantisation functions instead of + * flo_quantize() and flo_quantize_floor() + * o let the user set up SPLIT_NOTE + * o let the user decide about the initial clef (search for FINDME_INITCLEF) + * + * stuff for the other muse developers + * 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 velocity/release-velo for already existing notes + * - do this by right-click -> some dialog shows up? + * - or by selecting the note and changing the values in the same widget which also is used for new notes? + * - or by controller graphs, as used by the piano roll + */ + + +/* 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..b68b637a 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 (flo93@users.sourceforge.net) //========================================================= #ifndef __SCOREEDIT_H__ @@ -12,6 +12,11 @@ #include <QResizeEvent> #include <QLabel> #include <QKeyEvent> +#include <QPainter> +#include <QPixmap> +#include <QTimer> +#include <QScrollBar> +#include <QSignalMapper> #include <values.h> #include "noteinfo.h" @@ -19,183 +24,669 @@ #include "midieditor.h" #include "tools.h" #include "event.h" +#include "view.h" +#include "gconfig.h" +#include "part.h" +#include "keyevent.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; + + + +#define TICKS_PER_WHOLE (config.division*4) +#define SONG_LENGTH (song->len()) + + + +enum {CMD_COLOR_BLACK, CMD_COLOR_VELO, CMD_COLOR_PART, + CMD_SET_NAME, + CMD_NOTELEN_1, CMD_NOTELEN_2, CMD_NOTELEN_4, CMD_NOTELEN_8, + CMD_NOTELEN_16, CMD_NOTELEN_32, CMD_NOTELEN_LAST }; + +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 TopWin +{ + Q_OBJECT + + private: + virtual void closeEvent(QCloseEvent*); + + QGridLayout* mainGrid; + QWidget* mainw; + + QScrollBar* xscroll; + QScrollBar* yscroll; + ScoreCanvas* score_canvas; + + static int serial; + static set<QString> names; + + QString name; + + QSignalMapper* menu_mapper; + + bool set_name(QString newname, bool emit_signal=true, bool emergency_name=false); + + private slots: + void menu_command(int); + + signals: + void deleted(unsigned long); + void name_changed(); + + public slots: + void canvas_width_changed(int); + void viewport_width_changed(int); + void canvas_height_changed(int); + void viewport_height_changed(int); + + public: + ScoreEdit(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); + QString get_name() { return name; } + }; + + + + + + +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); + + + + + + + +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; + + key_enum key; + + + 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, key_enum k) + { + type=t; + key=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; + + key_enum key; + + 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, key_enum k) + { + type=t; + key=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; + } +}; + + +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) +#define NUM_MYCOLORS (NUM_PARTCOLORS+2 + 128) +#define VELO_PIXMAP_BEGIN (NUM_PARTCOLORS+2) + +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; + + ScoreCanvas* parent; + + void create_appropriate_eventlist(); + void create_itemlist(); + void process_itemlist(); + void calc_item_pos(); + + void recalculate() + { + create_appropriate_eventlist(); + create_itemlist(); + process_itemlist(); + calc_item_pos(); + } + + staff_t(ScoreCanvas* parent_) + { + type=NORMAL; + clef=VIOLIN; + parent=parent_; + } + + staff_t (ScoreCanvas* parent_, staff_type_t type_, clef_t clef_, set<Part*> parts_, int split_note_=0) + { + type=type_; + clef=clef_; + split_note=split_note_; + parts=parts_; + parent=parent_; + } + + bool cleanup_parts(); + + set<Part*> parts_at_tick(unsigned tick); +}; + +list<int> calc_accidentials(key_enum key, clef_t clef, key_enum next_key=KEY_C); +note_pos_t note_pos_(int note, key_enum key); +note_pos_t note_pos (unsigned note, key_enum 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, int quant_power2, 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 init_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, key_enum 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); + key_enum 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(); + + + 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); + void cleanup_staves(); + void maybe_close_if_empty(); + +// member variables --------------------------------------------------- + int _quant_power2; + int _pixels_per_whole; + + int newnote_velo; + int newnote_velo_off; + + std::map<int,int> pos_add_list; + + list<staff_t> staves; + + // 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; + + int y_pos; + + //for mouse-scrolling + float x_scroll_speed; + float x_scroll_pos; + float y_scroll_speed; + float y_scroll_pos; + + Part* selected_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; + + + + enum {COLOR_MODE_BLACK, COLOR_MODE_PART, COLOR_MODE_VELO} coloring_mode; + bool preamble_contains_keysig; + bool preamble_contains_timesig; + + + //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(); + + void play_changed(bool); + void config_changed(); + 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 x_scroll_event(int); + void y_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(); + + void set_tool(int); + void set_quant(int); + void menu_command(int); + void preamble_keysig_slot(bool); + void preamble_timesig_slot(bool); + void set_pixels_per_whole(int); + + void set_newnote_velo(int); + void set_newnote_velo_off(int); + + signals: + void xscroll_changed(int); + void yscroll_changed(int); + void viewport_width_changed(int); + void canvas_width_changed(int); + void viewport_height_changed(int); + void canvas_height_changed(int); + void pixels_per_whole_changed(int); + + protected: + virtual void draw(QPainter& p, const QRect& rect); + ScoreEdit* parent; + + virtual void mousePressEvent (QMouseEvent* event); + virtual void mouseMoveEvent (QMouseEvent* event); + virtual void mouseReleaseEvent (QMouseEvent* event); + virtual void resizeEvent(QResizeEvent*); + + public: + ScoreCanvas(ScoreEdit*, QWidget*, int, int); + ~ScoreCanvas(){}; + + void add_staves(PartList* pl, bool all_in_one); + + int canvas_width(); + int canvas_height(); + int viewport_width(); + int viewport_height(); + + int quant_power2() { return _quant_power2; } + int quant_len() { return (1<<_quant_power2); } + int quant_ticks() { return TICKS_PER_WHOLE / (1<<_quant_power2); } + int pixels_per_whole() { return _pixels_per_whole; } + int note_x_indent() { return pixels_per_whole()/quant_len()/2; } +}; + +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 diff --git a/muse2/muse/widgets/canvas.cpp b/muse2/muse/widgets/canvas.cpp index 596e4067..14f414b7 100644 --- a/muse2/muse/widgets/canvas.cpp +++ b/muse2/muse/widgets/canvas.cpp @@ -557,7 +557,7 @@ void Canvas::viewMousePressEvent(QMouseEvent* event) itemPopupMenu = genItemPopup(curItem); if (itemPopupMenu) { QAction *act = itemPopupMenu->exec(QCursor::pos()); - if (act) + if (act && act->data().isValid()) itemPopup(curItem, act->data().toInt(), start); delete itemPopupMenu; } |