summaryrefslogtreecommitdiff
path: root/muse2
diff options
context:
space:
mode:
Diffstat (limited to 'muse2')
-rw-r--r--muse2/muse/app.cpp131
-rw-r--r--muse2/muse/app.h19
-rw-r--r--muse2/muse/cobject.h2
-rw-r--r--muse2/muse/midiedit/scoreedit.cpp4183
-rw-r--r--muse2/muse/midiedit/scoreedit.h763
5 files changed, 3632 insertions, 1466 deletions
diff --git a/muse2/muse/app.cpp b/muse2/muse/app.cpp
index 8143bb0c..9f10a614 100644
--- a/muse2/muse/app.cpp
+++ b/muse2/muse/app.cpp
@@ -114,6 +114,20 @@ pthread_t splashThread;
// // pyscript->runPythonScript(script);
// }
+
+void MusE::clearScoreMenuMappers()
+{
+ delete scoreOneStaffPerTrackMapper;
+ delete scoreAllInOneMapper;
+
+ scoreOneStaffPerTrackMapper = new QSignalMapper(this);
+ scoreAllInOneMapper = new QSignalMapper(this);
+
+ connect(scoreOneStaffPerTrackMapper, SIGNAL(mapped(QWidget*)), this, SLOT(openInScoreEdit_oneStaffPerTrack(QWidget*)));
+ connect(scoreAllInOneMapper, SIGNAL(mapped(QWidget*)), this, SLOT(openInScoreEdit_allInOne(QWidget*)));
+}
+
+
//---------------------------------------------------------
// sleep function
//---------------------------------------------------------
@@ -803,6 +817,8 @@ MusE::MusE(int argc, char** argv) : QMainWindow()
editSignalMapper = new QSignalMapper(this);
midiPluginSignalMapper = new QSignalMapper(this);
followSignalMapper = new QSignalMapper(this);
+ scoreOneStaffPerTrackMapper = new QSignalMapper(this);
+ scoreAllInOneMapper = new QSignalMapper(this);
song = new Song("song");
song->blockSignals(true);
@@ -977,7 +993,15 @@ MusE::MusE(int argc, char** argv) : QMainWindow()
editOutsideLoopAction = new QAction(QIcon(*select_outside_loopIcon), tr("&Outside Loop"), this);
editAllPartsAction = new QAction( QIcon(*select_all_parts_on_trackIcon), tr("All &Parts on Track"), this);
- startScoreEditAction = new QAction(*scoreIconSet, tr("Score"), this);
+
+ scoreSubmenu = new QMenu(tr("Score"), this);
+ scoreSubmenu->setIcon(QIcon(*scoreIconSet));
+
+ scoreAllInOneSubsubmenu = new QMenu(tr("all parts in one staff"), this);
+ scoreOneStaffPerTrackSubsubmenu = new QMenu(tr("one staff per part"), this);
+
+ updateScoreMenus();
+
startPianoEditAction = new QAction(*pianoIconSet, tr("Pianoroll"), this);
startDrumEditAction = new QAction(QIcon(*edit_drummsIcon), tr("Drums"), this);
startListEditAction = new QAction(QIcon(*edit_listIcon), tr("List"), this);
@@ -1127,10 +1151,12 @@ MusE::MusE(int argc, char** argv) : QMainWindow()
connect(editSignalMapper, SIGNAL(mapped(int)), this, SLOT(cmd(int)));
connect(startPianoEditAction, SIGNAL(activated()), SLOT(startPianoroll()));
- connect(startScoreEditAction, SIGNAL(activated()), SLOT(startScoreEdit()));
connect(startDrumEditAction, SIGNAL(activated()), SLOT(startDrumEditor()));
connect(startListEditAction, SIGNAL(activated()), SLOT(startListEditor()));
connect(startWaveEditAction, SIGNAL(activated()), SLOT(startWaveEditor()));
+ connect(scoreOneStaffPerTrackMapper, SIGNAL(mapped(QWidget*)), this, SLOT(openInScoreEdit_oneStaffPerTrack(QWidget*)));
+ connect(scoreAllInOneMapper, SIGNAL(mapped(QWidget*)), this, SLOT(openInScoreEdit_allInOne(QWidget*)));
+
connect(masterGraphicAction, SIGNAL(activated()), SLOT(startMasterEditor()));
connect(masterListAction, SIGNAL(activated()), SLOT(startLMasterEditor()));
@@ -1346,7 +1372,9 @@ MusE::MusE(int argc, char** argv) : QMainWindow()
menuEdit->addSeparator();
menuEdit->addAction(startPianoEditAction);
- menuEdit->addAction(startScoreEditAction);
+ menuEdit->addMenu(scoreSubmenu);
+ scoreSubmenu->addMenu(scoreAllInOneSubsubmenu);
+ scoreSubmenu->addMenu(scoreOneStaffPerTrackSubsubmenu);
menuEdit->addAction(startDrumEditAction);
menuEdit->addAction(startListEditAction);
menuEdit->addAction(startWaveEditAction);
@@ -3424,29 +3452,86 @@ PartList* MusE::getMidiPartsToEdit()
return pl;
}
+void MusE::scoreNamingChanged()
+{
+ updateScoreMenus();
+}
+
+void MusE::updateScoreMenus()
+{
+ QAction* action;
+
+
+ scoreOneStaffPerTrackSubsubmenu->clear();
+ scoreAllInOneSubsubmenu->clear();
+
+
+ action=new QAction(tr("New"), this);
+ connect(action, SIGNAL(activated()), scoreOneStaffPerTrackMapper, SLOT(map()));
+ scoreOneStaffPerTrackMapper->setMapping(action, (QWidget*)NULL);
+ scoreOneStaffPerTrackSubsubmenu->addAction(action);
+
+
+ action=new QAction(tr("New"), this); //the above action may NOT be reused!
+ connect(action, SIGNAL(activated()), scoreAllInOneMapper, SLOT(map()));
+ scoreAllInOneMapper->setMapping(action, (QWidget*)NULL);
+ scoreAllInOneSubsubmenu->addAction(action);
+
+ for (ToplevelList::iterator it=toplevels.begin(); it!=toplevels.end(); it++)
+ if (it->type()==Toplevel::SCORE)
+ {
+ ScoreEdit* score = (ScoreEdit*) it->cobject();
+
+ action=new QAction(QString(score->get_name().c_str()), this);
+ connect(action, SIGNAL(activated()), scoreOneStaffPerTrackMapper, SLOT(map()));
+ scoreOneStaffPerTrackMapper->setMapping(action, (QWidget*)score);
+ scoreOneStaffPerTrackSubsubmenu->addAction(action);
+
+
+ action=new QAction(QString(score->get_name().c_str()), this); //the above action may NOT be reused!
+ connect(action, SIGNAL(activated()), scoreAllInOneMapper, SLOT(map()));
+ scoreAllInOneMapper->setMapping(action, (QWidget*)score);
+ scoreAllInOneSubsubmenu->addAction(action);
+ }
+}
+
//---------------------------------------------------------
// startScoreEdit
//---------------------------------------------------------
-void MusE::startScoreEdit()
- {
- PartList* pl = getMidiPartsToEdit();
- if (pl == 0)
- return;
- startScoreEdit(pl, true);
- }
+void MusE::openInScoreEdit_oneStaffPerTrack(QWidget* dest)
+{
+ openInScoreEdit((ScoreEdit*)dest, false);
+}
-void MusE::startScoreEdit(PartList* pl, bool showDefaultCtrls)
- {
+void MusE::openInScoreEdit_allInOne(QWidget* dest)
+{
+ openInScoreEdit((ScoreEdit*)dest, true);
+}
- ScoreEdit* scoreedit = new ScoreEdit(pl, this, 0, arranger->cursorValue());
- if(showDefaultCtrls) // p4.0.12
- scoreedit->addCtrl();
- scoreedit->show();
- toplevels.push_back(Toplevel(Toplevel::PIANO_ROLL, (unsigned long)(scoreedit), scoreedit));
- connect(scoreedit, SIGNAL(deleted(unsigned long)), SLOT(toplevelDeleted(unsigned long)));
- connect(muse, SIGNAL(configChanged()), scoreedit, SLOT(configChanged()));
- }
+void MusE::openInScoreEdit(ScoreEdit* destination, bool allInOne)
+{
+ PartList* pl = getMidiPartsToEdit();
+ if (pl == 0)
+ return;
+ openInScoreEdit(destination, pl, allInOne);
+}
+
+void MusE::openInScoreEdit(ScoreEdit* destination, PartList* pl, bool allInOne)
+{
+ if (destination==NULL) // if no destination given, create a new one
+ {
+ destination = new ScoreEdit(pl, this, 0, arranger->cursorValue());
+ destination->show();
+ toplevels.push_back(Toplevel(Toplevel::SCORE, (unsigned long)(destination), destination));
+ connect(destination, SIGNAL(deleted(unsigned long)), SLOT(toplevelDeleted(unsigned long)));
+ connect(muse, SIGNAL(configChanged()), destination, SLOT(configChanged()));
+
+ updateScoreMenus();
+ }
+
+ destination->add_parts(pl, allInOne);
+}
//---------------------------------------------------------
// startPianoroll
@@ -3667,6 +3752,7 @@ void MusE::toplevelDeleted(unsigned long tl)
{
for (iToplevel i = toplevels.begin(); i != toplevels.end(); ++i) {
if (i->object() == tl) {
+ bool mustUpdateScoreMenus=false;
switch(i->type()) {
case Toplevel::MARKER:
break;
@@ -3685,8 +3771,12 @@ void MusE::toplevelDeleted(unsigned long tl)
case Toplevel::WAVE:
case Toplevel::LMASTER:
break;
+ case Toplevel::SCORE:
+ mustUpdateScoreMenus=true;
}
toplevels.erase(i);
+ if (mustUpdateScoreMenus)
+ updateScoreMenus();
return;
}
}
@@ -4839,6 +4929,7 @@ again:
case Toplevel::MARKER:
break;
case Toplevel::PIANO_ROLL:
+ case Toplevel::SCORE:
case Toplevel::LISTE:
case Toplevel::DRUM:
case Toplevel::MASTER:
diff --git a/muse2/muse/app.h b/muse2/muse/app.h
index 168602d3..8a9fdac0 100644
--- a/muse2/muse/app.h
+++ b/muse2/muse/app.h
@@ -65,6 +65,7 @@ class Appearance;
class WaveTrack;
class AudioOutput;
class EditInstrument;
+class ScoreEdit;
#define MENU_ADD_SYNTH_ID_BASE 0x1000
@@ -109,7 +110,7 @@ class MusE : public QMainWindow
QAction *editInvertSelectionAction, *editInsideLoopAction, *editOutsideLoopAction, *editAllPartsAction;
QAction *trackMidiAction, *trackDrumAction, *trackWaveAction, *trackAOutputAction, *trackAGroupAction;
QAction *trackAInputAction, *trackAAuxAction;
- QAction *startPianoEditAction, *startDrumEditAction, *startListEditAction, *startWaveEditAction, *startScoreEditAction;
+ QAction *startPianoEditAction, *startDrumEditAction, *startListEditAction, *startWaveEditAction;
QAction *masterGraphicAction, *masterListAction;
QAction *midiTransposeAction;
QAction *midiTransformerAction;
@@ -158,6 +159,7 @@ class MusE : public QMainWindow
QMenu* menu_audio, *menuAutomation;
QMenu* menu_functions, *menuScriptPlugins;
QMenu* select, *master, *midiEdit, *addTrack;
+ QMenu *scoreSubmenu, *scoreOneStaffPerTrackSubsubmenu, *scoreAllInOneSubsubmenu;
// Special 'stay-open' menu for routes.
PopupMenu* routingPopupMenu;
@@ -220,6 +222,8 @@ class MusE : public QMainWindow
QSignalMapper *editSignalMapper;
QSignalMapper *midiPluginSignalMapper;
QSignalMapper *followSignalMapper;
+ QSignalMapper *scoreOneStaffPerTrackMapper;
+ QSignalMapper *scoreAllInOneMapper;
signals:
void configChanged();
@@ -261,8 +265,15 @@ class MusE : public QMainWindow
void startDrumEditor();
void startDrumEditor(PartList* /*pl*/, bool /*showDefaultCtrls*/ = false);
void startEditor(Track*);
- void startScoreEdit();
- void startScoreEdit(PartList* /*pl*/, bool /*showDefaultCtrls*/ = false);
+
+ void openInScoreEdit(ScoreEdit* destination, PartList* pl, bool allInOne=false);
+ void openInScoreEdit(ScoreEdit* destination, bool allInOne=false);
+ void openInScoreEdit_allInOne(QWidget* destination);
+ void openInScoreEdit_oneStaffPerTrack(QWidget* destination);
+ void clearScoreMenuMappers();
+ void updateScoreMenus();
+ void scoreNamingChanged();
+
void startPianoroll();
void startPianoroll(PartList* /*pl*/, bool /*showDefaultCtrls*/ = false);
void startWaveEditor();
@@ -320,7 +331,7 @@ class MusE : public QMainWindow
void execDeliveredScript(int);
void execUserScript(int);
-
+
public slots:
bool saveAs();
void bounceToFile(AudioOutput* ao = 0);
diff --git a/muse2/muse/cobject.h b/muse2/muse/cobject.h
index 8e21eaf0..9a80b2b1 100644
--- a/muse2/muse/cobject.h
+++ b/muse2/muse/cobject.h
@@ -38,7 +38,7 @@ class TopWin : public QMainWindow
class Toplevel {
public:
enum ToplevelType { PIANO_ROLL, LISTE, DRUM, MASTER, WAVE,
- LMASTER, CLIPLIST, MARKER
+ LMASTER, CLIPLIST, MARKER, SCORE
#ifdef PATCHBAY
, M_PATCHBAY
#endif /* PATCHBAY */
diff --git a/muse2/muse/midiedit/scoreedit.cpp b/muse2/muse/midiedit/scoreedit.cpp
index f236a0d5..3c4543b3 100644
--- a/muse2/muse/midiedit/scoreedit.cpp
+++ b/muse2/muse/midiedit/scoreedit.cpp
@@ -1,13 +1,20 @@
+//you need to download http://home.arcor.de/michael.jung11/glyphs.tar.bz2
+//and extract it somewhere. then change FONT_PATH to the correct directory
+//the trailing slash is necessary
+#define FONT_PATH "/home/flo/muse-glyphs/"
+
//=========================================================
// MusE
// Linux Music Editor
-// $Id: ScoreEdit.cpp,v 1.25.2.15 2009/11/16 11:29:33 lunar_shuttle Exp $
-// (C) Copyright 1999 Werner Schweer (ws@seh.de)
+// scoreedit.cpp
+// (C) Copyright 2011 Florian Jung (florian.a.jung@web.de)
//=========================================================
+
#include <QLayout>
#include <QSizeGrip>
#include <QLabel>
+#include <QScrollBar>
#include <QPushButton>
#include <QToolButton>
#include <QToolTip>
@@ -26,8 +33,14 @@
#include <QMimeData>
#include <QScrollArea>
#include <QSettings>
+#include <QImage>
#include <stdio.h>
+#include <math.h>
+
+#include <iostream>
+#include <sstream>
+using namespace std;
#include "xml.h"
#include "mtscale.h"
@@ -51,1361 +64,2995 @@
#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);
+
+
+#define SPLIT_NOTE 60
+//TODO: let the user specify that somehow?
+
+
+
+//TODO: all das unten richtig machen!
+#define TICKS_PER_WHOLE (config.division*4)
+#define SONG_LENGTH (song->len())
+
+#define quant_max 3 //whole, half, quarter = 0,1,2
+#define quant_max_fraction (1 << quant_max) //whole, half, quarter= 1,2,4
+#define FLO_QUANT (TICKS_PER_WHOLE/quant_max_fraction)
+//FLO_QUANT = how many ticks has a single quantisation area?
-static const int xscale = -10;
-static const int yscale = 1;
-static const int pianoWidth = 40;
-static int ScoreEditTools = PointerTool | PencilTool | RubberTool | DrawTool;
+//TODO: quant_max richtig setzen!
+
+
+
+//do NOT put parentheses around this!
+#define PAGESTEP 3/4
+
+
+#define SCROLL_MARGIN 10
+#define SCROLL_SPEED 5
+//SCROLL_SPEED is in (scroll_pixels per second) per mouse-move-pixel
+#define SCROLL_SPEED_MAX 500
+//SCROLL_SPEED_MAX is in scroll_pixels_per_second
+
+
+
+#define STAFF_DISTANCE (10*YLEN)
+#define GRANDSTAFF_DISTANCE (8*YLEN)
+
+string create_random_string(int len=8)
+{
+ string result;
+
+ for (int i=0;i<len;i++)
+ result+=char((rand() % 26) + 'A');
+
+ return result;
+}
+
+
+
+
+
+
+QPixmap *pix_whole, *pix_half, *pix_quarter; // arrays [NUM_PARTCOLORS+2]
+QPixmap *pix_dot, *pix_b, *pix_sharp, *pix_noacc; // arrays [NUM_PARTCOLORS+2]
+QPixmap *pix_r1, *pix_r2, *pix_r4, *pix_r8, *pix_r16; // pointers
+QPixmap *pix_flag_up, *pix_flag_down; // arrays [4]
+QPixmap *pix_num; // array [10]
+QPixmap *pix_clef_violin, *pix_clef_bass; //pointers
+bool pixmaps_loaded=false;
+
+
+
+
+
+
+int ScoreEdit::serial=1;
+set<string> ScoreEdit::names;
//---------------------------------------------------------
// 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());
-
- }
+ : MidiEditor(0, 0, pl, parent, name)
+{
+ score_canvas=new ScoreCanvas(this, mainw, 1, 1);
+ hscroll = new QScrollBar(Qt::Horizontal, mainw);
+
+ connect(hscroll, SIGNAL(valueChanged(int)), score_canvas, SLOT(scroll_event(int)));
+ connect(score_canvas, SIGNAL(xpos_changed(int)), hscroll, SLOT(setValue(int)));
+ connect(score_canvas, SIGNAL(canvas_width_changed(int)), SLOT(canvas_width_changed(int)));
+ connect(score_canvas, SIGNAL(viewport_width_changed(int)), SLOT(viewport_width_changed(int)));
+ connect(song, SIGNAL(songChanged(int)), score_canvas, SLOT(song_changed(int)));
+
+ mainGrid->addWidget(score_canvas, 0, 0);
+ mainGrid->addWidget(hscroll,1,0);
+
+ hscroll->setMinimum(0);
+
+ score_canvas->song_changed(0);
+ score_canvas->goto_tick(initPos,true);
+
+ if (name!=NULL)
+ set_name(name, false, true);
+ else
+ set_name("Score "+IntToStr(serial++), false, true);
+}
-//---------------------------------------------------------
-// songChanged1
-//---------------------------------------------------------
-void ScoreEdit::songChanged1(int bits)
- {
-
- if (bits & SC_SOLO)
- {
- toolbar->setSolo(canvas->track()->solo());
- return;
- }
- songChanged(bits);
- //trackInfo->songChanged(bits);
- // We'll receive SC_SELECTION if a different part is selected.
- if (bits & SC_SELECTION)
- updateTrackInfo();
- }
+void ScoreEdit::add_parts(PartList* pl, bool all_in_one)
+{
+ score_canvas->add_staves(pl, all_in_one);
+}
+
+bool ScoreEdit::set_name(string newname, bool emit_signal, bool emergency_name)
+{
+ if (names.find(newname)==names.end())
+ {
+ names.erase(name);
+ names.insert(newname);
+
+ name=newname;
+
+ if (emit_signal)
+ emit name_changed();
+
+ return true;
+ }
+ else
+ {
+ if (emergency_name)
+ {
+ while (set_name(create_random_string(), emit_signal, false) == false);
+ return true;
+ }
+ else
+ return false;
+ }
+}
//---------------------------------------------------------
-// configChanged
+// ~ScoreEdit
//---------------------------------------------------------
-void ScoreEdit::configChanged()
- {
- initShortcuts();
- //trackInfo->updateTrackInfo();
- }
+ScoreEdit::~ScoreEdit()
+{
+
+}
-//---------------------------------------------------------
-// updateHScrollRange
-//---------------------------------------------------------
-void ScoreEdit::updateHScrollRange()
+void ScoreEdit::canvas_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);
+ hscroll->setMaximum(width);
+}
+void ScoreEdit::viewport_width_changed(int width)
+{
+ hscroll->setPageStep(width * PAGESTEP);
}
-void ScoreEdit::updateTrackInfo()
+void ScoreEdit::closeEvent(QCloseEvent* e)
{
- selected = curCanvasPart()->track();
- if (selected->isMidiTrack()) {
- midiTrackInfo->setTrack(selected);
- ///midiTrackInfo->updateTrackInfo(-1);
- }
+ QSettings settings("MusE", "MusE-qt");
+ //settings.setValue("ScoreEdit/geometry", saveGeometry());
+ settings.setValue("ScoreEdit/windowState", saveState());
+
+ emit deleted((unsigned long)this);
+ e->accept();
}
-//---------------------------------------------------------
-// follow
-//---------------------------------------------------------
-void ScoreEdit::follow(int pos)
- {
- int s, e;
- canvas->range(&s, &e);
- if (pos < e && pos >= s)
- hscroll->setOffset(pos);
- if (pos < s)
- hscroll->setOffset(s);
- }
+void ScoreCanvas::add_staves(PartList* pl, bool all_in_one)
+{
+ staff_t staff;
+
+ if (all_in_one)
+ {
+ staff.parts.clear();
+ for (ciPart part_it=pl->begin(); part_it!=pl->end(); part_it++)
+ staff.parts.insert(part_it->second);
+
+ staff.split_note=SPLIT_NOTE;
+
+ staff.type=GRAND_TOP; //FINDMICH
+ staff.clef=VIOLIN;
+ staffs.push_back(staff);
+
+ staff.type=GRAND_BOTTOM;
+ staff.clef=BASS;
+ staffs.push_back(staff);
+ }
+ else
+ {
+ set<Track*> tracks;
+
+ for (ciPart it=pl->begin(); it!=pl->end(); it++)
+ tracks.insert(it->second->track());
+
+ for (set<Track*>::iterator it=tracks.begin(); it!=tracks.end(); it++)
+ {
+ staff.parts.clear();
+ for (ciPart part_it=pl->begin(); part_it!=pl->end(); part_it++)
+ if (part_it->second->track() == *it)
+ staff.parts.insert(part_it->second);
+
+ staff.split_note=SPLIT_NOTE;
+
+ staff.type=GRAND_TOP; //FINDMICH
+ staff.clef=VIOLIN;
+ staffs.push_back(staff);
+
+ staff.type=GRAND_BOTTOM;
+ staff.clef=BASS;
+ staffs.push_back(staff);
+ }
+ }
+
+ recalc_staff_pos();
+ song_changed(0);
+}
-//---------------------------------------------------------
-// setTime
-//---------------------------------------------------------
-void ScoreEdit::setTime(unsigned tick)
- {
- toolbar->setTime(tick);
- time->setPos(3, tick, false);
- }
+ScoreCanvas::ScoreCanvas(MidiEditor* pr, QWidget* parent,
+ int sx, int sy) : View(parent, sx, sy)
+{
+ editor = pr;
+ setFocusPolicy(Qt::StrongFocus);
+ setBg(Qt::white);
+
+ setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding);
+
+ load_pixmaps();
+
+ x_pos=0;
+ x_left=0;
+ dragging=false;
+ mouse_erases_notes=false;
+ mouse_inserts_notes=true;
+
+ curr_part=editor->parts()->begin()->second; //TODO FINDMICHJETZT
+
+ last_len=384;
+ new_len=-1;
+
+ dragging_staff=false;
+
+
+ scroll_speed=0;
+ scroll_pos=0;
+ connect (heartBeatTimer, SIGNAL(timeout()), SLOT(heartbeat_timer_event()));
+
+ connect(song, SIGNAL(posChanged(int, unsigned, bool)), SLOT(pos_changed(int,unsigned,bool)));
+
+
+
+ staff_menu=new QMenu(this);
+
+ staffmode_treble_action = staff_menu->addAction(tr("Treble"));
+ connect(staffmode_treble_action, SIGNAL(triggered()), SLOT(staffmode_treble_slot()));
+
+ staffmode_bass_action = staff_menu->addAction(tr("Bass"));
+ connect(staffmode_bass_action, SIGNAL(triggered()), SLOT(staffmode_bass_slot()));
+
+ staffmode_both_action = staff_menu->addAction(tr("Grand Staff"));
+ connect(staffmode_both_action, SIGNAL(triggered()), SLOT(staffmode_both_slot()));
+
+ remove_staff_action = staff_menu->addAction(tr("Remove staff"));
+ connect(remove_staff_action, SIGNAL(triggered()), SLOT(remove_staff_slot()));
-//---------------------------------------------------------
-// ~ScoreEdit
-//---------------------------------------------------------
+}
-ScoreEdit::~ScoreEdit()
- {
- // undoRedo->removeFrom(tools); // p4.0.6 Removed
- }
+void ScoreCanvas::staffmode_treble_slot()
+{
+ set_staffmode(current_staff, MODE_TREBLE);
+}
-//---------------------------------------------------------
-// cmd
-// pulldown menu commands
-//---------------------------------------------------------
+void ScoreCanvas::staffmode_bass_slot()
+{
+ set_staffmode(current_staff, MODE_BASS);
+}
-void ScoreEdit::cmd(int cmd)
- {
- ((PianoCanvas*)canvas)->cmd(cmd, _quantStrength, _quantLimit, _quantLen, _to);
- }
+void ScoreCanvas::staffmode_both_slot()
+{
+ set_staffmode(current_staff, MODE_BOTH);
+}
-//---------------------------------------------------------
-// setSelection
-// update Info Line
-//---------------------------------------------------------
+void ScoreCanvas::remove_staff_slot()
+{
+ remove_staff(current_staff);
+}
-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::set_staffmode(list<staff_t>::iterator it, staff_mode_t mode)
+{
+ if (it->type == GRAND_BOTTOM)
+ {
+ it--;
+ if (it->type!=GRAND_TOP)
+ cout << "THIS SHOULD NEVER HAPPEN: grand_bottom without top!"<<endl;
+ }
+
+ if (it->type==GRAND_TOP)
+ {
+ list<staff_t>::iterator tmp=it;
+ tmp++;
+ if (tmp->type!=GRAND_BOTTOM)
+ cout << "THIS SHOULD NEVER HAPPEN: grand_top without bottom!"<<endl;
+ staffs.erase(tmp);
+ }
+
+ switch (mode)
+ {
+ case MODE_TREBLE:
+ it->type=NORMAL;
+ it->clef=VIOLIN;
+ break;
+
+ case MODE_BASS:
+ it->type=NORMAL;
+ it->clef=BASS;
+ break;
+
+ case MODE_BOTH:
+ it->type=GRAND_BOTTOM;
+ it->clef=BASS;
+ it->split_note=SPLIT_NOTE;
+
+ staffs.insert(it, staff_t(GRAND_TOP, VIOLIN, it->parts, it->split_note));
+ break;
+
+ default:
+ cout << "ILLEGAL FUNCTION CALL: invalid mode in set_staffmode" << endl;
+ }
+
+ recalc_staff_pos();
+ song_changed(0);
+}
-//---------------------------------------------------------
-// edit currently selected Event
-//---------------------------------------------------------
+void ScoreCanvas::remove_staff(list<staff_t>::iterator it)
+{
+ if (it->type == GRAND_BOTTOM)
+ {
+ it--;
+ if (it->type!=GRAND_TOP)
+ cout << "THIS SHOULD NEVER HAPPEN: grand_bottom without top!"<<endl;
+ }
+
+ if (it->type == NORMAL)
+ {
+ staffs.erase(it);
+ }
+ else if (it->type == GRAND_TOP)
+ {
+ staffs.erase(it++);
+ if (it->type!=GRAND_BOTTOM)
+ cout << "THIS SHOULD NEVER HAPPEN: grand_top without bottom!"<<endl;
+ staffs.erase(it);
+ }
+
+ recalc_staff_pos();
+ song_changed(0);
+}
-void ScoreEdit::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::merge_staves(list<staff_t>::iterator dest, list<staff_t>::iterator src)
+{
+ if (dest->type == GRAND_BOTTOM)
+ {
+ dest--;
+ if (dest->type!=GRAND_TOP)
+ cout << "THIS SHOULD NEVER HAPPEN: grand_bottom without top!"<<endl;
+ }
+
+ if (src->type == GRAND_BOTTOM)
+ {
+ src--;
+ if (src->type!=GRAND_TOP)
+ cout << "THIS SHOULD NEVER HAPPEN: grand_bottom without top!"<<endl;
+ }
+
+ if (dest==src) //dragged to itself?
+ return;
+
+
+ dest->parts.insert(src->parts.begin(), src->parts.end());
+
+ if (dest->type == GRAND_TOP)
+ {
+ dest++;
+ if (dest->type != GRAND_BOTTOM)
+ cout << "THIS SHOULD NEVER HAPPEN: grand_top without bottom!"<<endl;
+ dest->parts.insert(src->parts.begin(), src->parts.end());
+ }
+
+ remove_staff(src);
+
+ recalc_staff_pos();
+ song_changed(0);
+}
-//---------------------------------------------------------
-// addCtrl
-//---------------------------------------------------------
-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::song_changed(int)
+{
+ cout << "song changed!" << endl;
+
+ calc_pos_add_list();
+
+ for (list<staff_t>::iterator it=staffs.begin(); it!=staffs.end(); it++)
+ {
+ it->create_appropriate_eventlist(it->parts);
+ it->create_itemlist();
+ it->process_itemlist(); // do note- and rest-grouping and collision avoiding
+ it->calc_item_pos();
+ }
+
+ redraw();
+ cout << "song had changed, recalculation complete" << endl;
+
+ emit canvas_width_changed(canvas_width());
+}
-//---------------------------------------------------------
-// removeCtrl
-//---------------------------------------------------------
+int ScoreCanvas::canvas_width()
+{
+ //return tick_to_x(staffs.begin()->itemlist.rbegin()->first);
+ return tick_to_x(SONG_LENGTH);
+}
-void ScoreEdit::removeCtrl(CtrlEdit* ctrl)
- {
- for (std::list<CtrlEdit*>::iterator i = ctrlEditList.begin();
- i != ctrlEditList.end(); ++i) {
- if (*i == ctrl) {
- ctrlEditList.erase(i);
- break;
- }
- }
- }
+int ScoreCanvas::viewport_width()
+{
+ return (width() - x_left);
+}
-//---------------------------------------------------------
-// closeEvent
-//---------------------------------------------------------
+string IntToStr(int i)
+{
+ ostringstream s;
+ s<<i;
+ return s.str();
+}
-void ScoreEdit::closeEvent(QCloseEvent* e)
- {
- QSettings settings("MusE", "MusE-qt");
- //settings.setValue("ScoreEdit/geometry", saveGeometry());
- settings.setValue("ScoreEdit/windowState", saveState());
+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;
+ }
+}
- emit deleted((unsigned long)this);
- e->accept();
- }
+void load_colored_pixmaps(string file, QPixmap* array)
+{
+ QString fn(file.c_str());
+ QImage img(fn);
+
+ color_image(img, Qt::black);
+ array[BLACK_PIXMAP]=QPixmap::fromImage(img);
+
+ color_image(img, Qt::red);
+ array[HIGHLIGHTED_PIXMAP]=QPixmap::fromImage(img);
+
+
+ for (int color_index=0;color_index<NUM_PARTCOLORS; color_index++)
+ {
+ color_image(img, config.partColors[color_index]);
+ array[color_index]=QPixmap::fromImage(img);
+ }
+}
-//---------------------------------------------------------
-// readConfiguration
-//---------------------------------------------------------
-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;
- }
- }
- }
-//---------------------------------------------------------
-// writeConfiguration
-//---------------------------------------------------------
+void ScoreCanvas::load_pixmaps()
+{
+ if (!pixmaps_loaded)
+ {
+ cout << "loading pixmaps..." << endl;
+
+ pix_whole=new QPixmap[NUM_PARTCOLORS+2];
+ pix_half=new QPixmap[NUM_PARTCOLORS+2];
+ pix_quarter=new QPixmap[NUM_PARTCOLORS+2];
+ pix_dot=new QPixmap[NUM_PARTCOLORS+2];
+ pix_b=new QPixmap[NUM_PARTCOLORS+2];
+ pix_sharp=new QPixmap[NUM_PARTCOLORS+2];
+ pix_noacc=new QPixmap[NUM_PARTCOLORS+2];
+ pix_num=new QPixmap[10];
+
+ pix_r1=new QPixmap;
+ pix_r2=new QPixmap;
+ pix_r4=new QPixmap;
+ pix_r8=new QPixmap;
+ pix_r16=new QPixmap;
+
+ pix_clef_violin=new QPixmap;
+ pix_clef_bass=new QPixmap;
+
+ pix_flag_up=new QPixmap[4];
+ pix_flag_down=new QPixmap[4];
+
+
+
+
+ load_colored_pixmaps(FONT_PATH "whole.png", pix_whole);
+ load_colored_pixmaps(FONT_PATH "half.png", pix_half);
+ load_colored_pixmaps(FONT_PATH "quarter.png", pix_quarter);
+ load_colored_pixmaps(FONT_PATH "dot.png", pix_dot);
+ load_colored_pixmaps(FONT_PATH "acc_none.png", pix_noacc);
+ load_colored_pixmaps(FONT_PATH "acc_sharp.png", pix_sharp);
+ load_colored_pixmaps(FONT_PATH "acc_b.png", pix_b);
+
+ pix_r1->load(FONT_PATH "rest1.png");
+ pix_r2->load(FONT_PATH "rest2.png");
+ pix_r4->load(FONT_PATH "rest4.png");
+ pix_r8->load(FONT_PATH "rest8.png");
+ pix_r16->load(FONT_PATH "rest16.png");
+ pix_flag_up[0].load(FONT_PATH "flags8u.png");
+ pix_flag_up[1].load(FONT_PATH "flags16u.png");
+ pix_flag_up[2].load(FONT_PATH "flags32u.png");
+ pix_flag_up[3].load(FONT_PATH "flags64u.png");
+ pix_flag_down[0].load(FONT_PATH "flags8d.png");
+ pix_flag_down[1].load(FONT_PATH "flags16d.png");
+ pix_flag_down[2].load(FONT_PATH "flags32d.png");
+ pix_flag_down[3].load(FONT_PATH "flags64d.png");
+
+ pix_clef_violin->load(FONT_PATH "clef_violin_big.png");
+ pix_clef_bass->load(FONT_PATH "clef_bass_big.png");
+
+ for (int i=0;i<10;i++)
+ pix_num[i].load(QString((string(FONT_PATH "")+IntToStr(i)+string(".png")).c_str()));
+
+ pixmaps_loaded=true;
+ }
+}
-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");
- }
-//---------------------------------------------------------
-// soloChanged
-// signal from solo button
-//---------------------------------------------------------
-void ScoreEdit::soloChanged(bool flag)
- {
- audio->msgSetSolo(canvas->track(), flag);
- song->update(SC_SOLO);
- }
+int modulo(int a, int b) // similar to a % b
+{
+ return (((a%b)+b)%b);
+}
-//---------------------------------------------------------
-// setRaster
-//---------------------------------------------------------
+int divide_floor(int a, int b) // similar to a / b
+{ //TODO can be done better :/
+ return int(floor(float(a)/float(b)));
+}
-void ScoreEdit::setRaster(int val)
- {
- _rasterInit = val;
- MidiEditor::setRaster(val);
- canvas->redrawGrid();
- canvas->setFocus(); // give back focus after kb input
- }
+#define DEFAULT_REST_HEIGHT 6 // TODO
-//---------------------------------------------------------
-// setQuant
-//---------------------------------------------------------
-void ScoreEdit::setQuant(int val)
- {
- _quantInit = val;
- MidiEditor::setQuant(val);
- canvas->setFocus();
- }
+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;
+}
-//---------------------------------------------------------
-// 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 flo_quantize(int tick)
+{
+ //TODO quantizing must be done with the proper functions!
+ return int(nearbyint((float)tick / FLO_QUANT))*FLO_QUANT;
+}
+
+int flo_quantize_floor(int tick)
+{
+ //TODO quantizing must be done with the proper functions, see above
+ return int(tick / FLO_QUANT) * FLO_QUANT;
+}
-//---------------------------------------------------------
-// readStatus
-//---------------------------------------------------------
+
+/* builds the event list used by the score editor.
+ * that list contains only note-on and -off, time-sig- and
+ * key-change events.
+ * it stores them sorted by their time (quantized); if more
+ * events with the same time occur, the NOTE-OFFs are
+ * put before the NOTE-ONs
+ * it only operates on parts which were selected in the
+ * arranger when the score viewer was started
+ *
+ * this abstracts the rest of the renderer from muse's internal
+ * data structures, making this easy to port to another application
+ */
+void staff_t::create_appropriate_eventlist(const set<Part*>& parts)
+{
+ using AL::sigmap;
+ using AL::iSigEvent;
+
+ eventlist.clear();
+
+ // phase one: fill the list -----------------------------------------
+
+ //insert note on events
+ for (set<Part*>::const_iterator part_it=parts.begin(); part_it!=parts.end(); part_it++)
+ {
+ Part* part=*part_it;
+ EventList* el=part->events();
+
+ for (iEvent it=el->begin(); it!=el->end(); it++)
+ {
+ Event& event=it->second;
+
+ if ( (event.isNote() && !event.isNoteOff()) &&
+ ( ((type==GRAND_TOP) && (event.pitch() >= split_note)) ||
+ ((type==GRAND_BOTTOM) && (event.pitch() < split_note)) ||
+ (type==NORMAL) ) )
+ {
+ unsigned begin, end;
+ begin=flo_quantize(event.tick()+part->tick());
+ end=flo_quantize(event.endTick()+part->tick());
+ cout <<"inserting note on at "<<begin<<" with pitch="<<event.pitch()<<" and len="<<end-begin<<endl;
+ eventlist.insert(pair<unsigned, FloEvent>(begin, FloEvent(begin,event.pitch(), event.velo(),end-begin,FloEvent::NOTE_ON,part,&it->second)));
+ }
+ //else ignore it
+ }
+ }
+
+ //insert bars and time signatures
+ for (iSigEvent it=sigmap.begin(); it!=sigmap.end(); it++)
+ {
+ unsigned from=it->second->tick;
+ unsigned to=it->first;
+ unsigned ticks_per_measure=sigmap.ticksMeasure(it->second->tick);
+
+ if (to > unsigned(SONG_LENGTH))
+ {
+ cout << "time signature's end-of-validness is outside of our song, limiting it." << endl;
+ to=SONG_LENGTH;
+ }
+
+ cout << "new signature from tick "<<from<<" to " << to << ": "<<it->second->sig.z<<"/"<<it->second->sig.n<<"; ticks per measure = "<<ticks_per_measure<<endl;
+ eventlist.insert(pair<unsigned, FloEvent>(from, FloEvent(from, FloEvent::TIME_SIG, it->second->sig.z, it->second->sig.n) ) );
+ for (unsigned t=from; t<to; t+=ticks_per_measure)
+ eventlist.insert(pair<unsigned, FloEvent>(t, FloEvent(t,0,0,ticks_per_measure,FloEvent::BAR) ) );
+ }
+
+ //insert key changes
+ for (iKeyEvent it=keymap.begin(); it!=keymap.end(); it++)
+ eventlist.insert(pair<unsigned, FloEvent>(it->second.tick, FloEvent(it->second.tick,FloEvent::KEY_CHANGE, it->second.key ) ) );
+
+
+ // phase two: deal with overlapping notes ---------------------------
+ ScoreEventList::iterator it, it2;
+
+ //iterate through all note_on - events
+ for (it=eventlist.begin(); it!=eventlist.end(); it++)
+ if (it->second.type==FloEvent::NOTE_ON)
+ {
+ unsigned end_tick=it->first + it->second.len;
+
+ //iterate though all (relevant) later note_ons which are
+ //at the same pitch. if there's a collision, shorten it's len
+ for (it2=it, it2++; it2!=eventlist.end() && it2->first < end_tick; it2++)
+ if ((it2->second.type==FloEvent::NOTE_ON) && (it2->second.pitch == it->second.pitch))
+ it->second.len=it2->first - it->first;
+ }
+
+
+ // phase three: eliminate zero-length-notes -------------------------
+ for (it=eventlist.begin(); it!=eventlist.end();)
+ if ((it->second.type==FloEvent::NOTE_ON) && (it->second.len<=0))
+ eventlist.erase(it++);
+ else
+ it++;
+}
-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
- };
-//---------------------------------------------------------
-// viewKeyPressEvent
-//---------------------------------------------------------
+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::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);
- }
+int n_accidentials(key_enum t)
+{
+ if (is_sharp_key(t))
+ return t-KEY_SHARP_BEGIN-1;
+ else
+ return t-KEY_B_BEGIN-1;
+}
-//---------------------------------------------------------
-// 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();
- }
+//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))
+ cout << "WARNING: ILLEGAL FUNCTION CALL (note_pos, note out of range)" << endl;
+
+ if (foo[note]!=-1)
+ {
+ result.height=foo[note];
+ result.vorzeichen=NONE;
+ }
+ else
+ {
+ if (is_sharp_key(key))
+ {
+ result.height=foo[note-1];
+ result.vorzeichen=SHARP;
+ }
+ else // if is_b_key
+ {
+ result.height=foo[note+1];
+ result.vorzeichen=B;
+ }
+ }
+
+ // Special cases for GES / FIS keys
+ if (key==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;
+}
-//---------------------------------------------------------
-// setSteprec
-//---------------------------------------------------------
-void ScoreEdit::setSteprec(bool flag)
- {
- canvas->setSteprec(flag);
- if (flag == false)
- midiin->setChecked(flag);
- }
+// 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
-//---------------------------------------------------------
-// eventColorModeChanged
-//---------------------------------------------------------
+// the "spaces" in between the lines have odd numbers.
+// that is, the space between line 2 and 4 is numbered 3.
-void ScoreEdit::eventColorModeChanged(int mode)
- {
- colorMode = mode;
- colorModeInit = colorMode;
-
- ((PianoCanvas*)(canvas))->setColorMode(colorMode);
- }
+// 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
-//---------------------------------------------------------
-// setEventColorMode
-//---------------------------------------------------------
+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;
+}
-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);
- }
-//---------------------------------------------------------
-// clipboardChanged
-//---------------------------------------------------------
+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;
+}
-void ScoreEdit::clipboardChanged()
- {
- editPasteAction->setEnabled(QApplication::clipboard()->mimeData()->hasFormat(QString("text/x-muse-eventlist")));
- }
+bool operator< (const note_len_t& a,const note_len_t& b) //TODO sane sorting order
+{
+ if (a.len<b.len) return true;
+ else if (a.dots<b.dots) return true;
+ else return false;
+}
-//---------------------------------------------------------
-// selectionChanged
-//---------------------------------------------------------
-void ScoreEdit::selectionChanged()
- {
- bool flag = canvas->selectionSize() > 0;
- editCutAction->setEnabled(flag);
- editCopyAction->setEnabled(flag);
- editDelEventsAction->setEnabled(flag);
- }
-//---------------------------------------------------------
-// setSpeaker
-//---------------------------------------------------------
+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;
+}
-void ScoreEdit::setSpeaker(bool val)
- {
- _playEvents = val;
- canvas->playEvents(_playEvents);
- }
+vector<int> create_emphasize_list(const list<int>& nums, int denom)
+{
+ cout << "creating emphasize list for ";
+ for (list<int>::const_iterator it=nums.begin(); it!=nums.end(); it++)
+ cout << *it << " ";
+ cout << "/ "<<denom;
+
+ // |----- 8th -----|
+ int foo[]={4,7,6,7,5,7,6,7}; //if 64 changes, this also must change
+ int pos=0;
+ int len=calc_measure_len(nums, denom);
+
+ vector<int> result(len);
+
+ for (int i=0;i<len;i++)
+ result[i]=foo[i%8];
+
+ for (list<int>::const_iterator it=nums.begin(); it!=nums.end(); it++)
+ {
+ result[pos]=1;
+ for (int i=1;i<*it;i++)
+ result[pos + i*64/denom]=2;
+ pos+= *it * 64 / denom;
+ }
+
+ result[0]=0;
+
+ for (int i=0;i<len;i++)
+ {
+ if (i%8==0)
+ cout << endl<<i<<":\t";
+ cout << result[i]<<" ";
+ }
+ cout << endl;
+
+ return result;
+}
-//---------------------------------------------------------
-// resizeEvent
-//---------------------------------------------------------
+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);
+}
-void ScoreEdit::resizeEvent(QResizeEvent* ev)
- {
- QWidget::resizeEvent(ev);
- _widthInit = ev->size().width();
- _heightInit = ev->size().height();
- }
+//quant_max must be in log(len), that is
+//whole, half, quarter, eighth = 0,1,2,3
+//NOT: 1,2,4,8! (think of 2^foo)
+//len is in ticks
+list<note_len_t> parse_note_len(int len_ticks, int begin_tick, vector<int>& foo, bool allow_dots, bool allow_normal)
+{
+ list<note_len_t> retval;
+
+ if (len_ticks<0)
+ cout << "WARNING: ILLEGAL FUNCTION CALL in parse_note_len: len_ticks < 0" << endl;
+ if (begin_tick<0)
+ cout << "WARNING: ILLEGAL FUNCTION CALL in parse_note_len: begin_tick < 0" << endl;
+
+ if (allow_normal)
+ {
+ int dot_max = allow_dots ? quant_max : 0;
+
+ for (int i=0;i<=quant_max;i++)
+ for (int j=0;j<=dot_max-i;j++)
+ if (calc_len(i,j) == len_ticks)
+ {
+ retval.push_back(note_len_t (i,j));
+ return retval;
+ }
+ }
+
+ //if !allow_normal or if the above failed
+
+ int begin=begin_tick * 64 / TICKS_PER_WHOLE;
+ int len=len_ticks * 64 / TICKS_PER_WHOLE;
+
+ unsigned pos=begin;
+ int len_done=0;
+
+ while (len_done<len)
+ {
+ int len_now=0;
+ int last_number=foo[pos];
+
+ do {pos++;len_done++;len_now++;} while (! ((foo[pos]<=last_number) || (len_done==len) || (pos==foo.size())) );
+
+ len_now=len_now*TICKS_PER_WHOLE/64;
+
+ cout << "add " << len_now << " ticks" << endl;
+ if (allow_dots)
+ {
+ int dot_max = quant_max;
+
+ for (int i=0;i<=quant_max;i++)
+ for (int j=0;j<=dot_max-i;j++)
+ if (calc_len(i,j) == len_now)
+ {
+ retval.push_back(note_len_t (i,j));
+ len_now=0;
+ }
+ }
+
+ if (len_now) //the above failed or allow_dots=false
+ {
+ for (int i=0; i<=quant_max; i++)
+ {
+ int tmp=calc_len(i,0);
+ if (tmp <= len_now)
+ {
+ retval.push_back(note_len_t(i));
+ len_now-=tmp;
+ if (len_now==0) break;
+ }
+ }
+ }
+
+ if (len_now!=0)
+ cout << "WARNING: THIS SHOULD NEVER HAPPEN. wasn't able to split note len properly; len_now="<<len_now << endl;
+
+ if (pos==foo.size()) //we cross measure boundaries?
+ pos=0;
+ }
+
+
+ return retval;
+}
-/*
-//---------------------------------------------------------
-// trackInfoScroll
-//---------------------------------------------------------
+#define USED_CLEF VIOLIN
-void ScoreEdit::trackInfoScroll(int y)
- {
- if (trackInfo->visibleWidget())
- trackInfo->visibleWidget()->move(0, -y);
- }
-*/
+#define YLEN 10
+#define NOTE_XLEN 10
+#define NOTE_SHIFT 3
+#define PIXELS_PER_WHOLE (320) //how many px are between two wholes?
+#define PIXELS_PER_NOTEPOS (PIXELS_PER_WHOLE/quant_max_fraction) //how many px are between the smallest drawn beats?
-//---------------------------------------------------------
-// initShortcuts
-//---------------------------------------------------------
+//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
-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);
-
- }
+// 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!
-//---------------------------------------------------------
-// execDeliveredScript
-//---------------------------------------------------------
-void ScoreEdit::execDeliveredScript(int id)
+#define NOTE_MOVE_X (PIXELS_PER_NOTEPOS/2)
+#define REST_AUSWEICH_X 10
+#define DOT_XDIST 6
+#define DOT_XBEGIN 10
+#define DOT_XBEGIN_REST 10
+
+#define NUMBER_HEIGHT (pix_num[0].height())
+
+//kann 0 oder 1 sein:
+//bei notenkollisionen mit ungerader anzahl von kollidierenden
+//wird immer so ausgewichen, dass möglichst wenige ausweichen müssen
+//wenn die anzahl aber gerade ist, gibt es keine "bessere" lösung
+//in dem fall werden immer die geraden (0) bzw. ungeraden (1)
+//ausweichen.
+#define AUSWEICHEN_BEVORZUGT 0
+
+#define STEM_LEN 30
+
+#define DOTTED_RESTS true
+#define UNSPLIT_RESTS false
+
+#define AUX_LINE_LEN 1.5
+
+#define ACCIDENTIAL_DIST 11
+#define KEYCHANGE_ACC_DIST 9
+#define KEYCHANGE_ACC_LEFTDIST 9
+#define KEYCHANGE_ACC_RIGHTDIST 0
+
+
+#define stdmap std::map
+
+#define no_notepos note_pos_t()
+
+#define TIE_DIST 5
+#define TIE_HEIGHT 6
+#define TIE_THICKNESS 3
+
+void ScoreCanvas::draw_tie (QPainter& p, int x1, int x4, int yo, bool up, QColor color)
{
- //QString scriptfile = QString(INSTPREFIX) + SCRIPTSSUFFIX + deliveredScriptNames[id];
- QString scriptfile = song->getScriptPath(id, true);
- song->executeScript(scriptfile.toAscii().data(), parts(), quant(), true);
+ 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);
}
-//---------------------------------------------------------
-// execUserScript
-//---------------------------------------------------------
-void ScoreEdit::execUserScript(int id)
+void ScoreCanvas::draw_accidentials(QPainter& p, int x, int y_offset, const list<int>& acc_list, const QPixmap& pix)
{
- QString scriptfile = song->getScriptPath(id, false);
- song->executeScript(scriptfile.toAscii().data(), parts(), quant(), true);
+ 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++;
+ }
}
-//---------------------------------------------------------
-// newCanvasWidth
-//---------------------------------------------------------
+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);
+
+ printf("FLO: t=%i\ttype=%i\tpitch=%i\tvel=%i\tlen=%i\n",it->first, it->second.type, it->second.pitch, it->second.vel, it->second.len);
+ cout << "\tline="<<notepos.height<<"\tvorzeichen="<<notepos.vorzeichen << endl;
+
+
+ if (type==FloEvent::BAR)
+ {
+ if (last_measure!=-1) //i.e.: "this is NOT the first bar"
+ {
+ if (lastevent==last_measure) //there was no note?
+ {
+ unsigned tmppos=(last_measure+t-FLO_QUANT)/2;
+ cout << "\tend-of-measure: this was an empty measure. inserting rest in between at t="<<tmppos << endl;
+ itemlist[tmppos].insert( FloItem(FloItem::REST,notepos,0,0) );
+ itemlist[t].insert( FloItem(FloItem::REST_END,notepos,0,0) );
+ }
+ else
+ {
+ // if neccessary, insert rest at between last note and end-of-measure
+ int rest=t-lastevent;
+ if (rest)
+ {
+ printf("\tend-of-measure: set rest at %i with len %i\n",lastevent,rest);
+
+ list<note_len_t> lens=parse_note_len(rest,lastevent-last_measure,emphasize_list,DOTTED_RESTS,UNSPLIT_RESTS);
+ unsigned tmppos=lastevent;
+ for (list<note_len_t>::iterator x=lens.begin(); x!=lens.end(); x++)
+ {
+ cout << "\t\tpartial rest with len="<<x->len<<", dots="<<x->dots<<endl;
+ itemlist[tmppos].insert( FloItem(FloItem::REST,notepos,x->len,x->dots) );
+ tmppos+=calc_len(x->len,x->dots);
+ itemlist[tmppos].insert( FloItem(FloItem::REST_END,notepos,0,0) );
+ }
+ }
+ }
+ }
+
+ lastevent=t;
+ last_measure=t;
+ next_measure=t+len;
+
+ itemlist[t].insert( FloItem(FloItem::BAR,no_notepos,0,0) );
+ }
+ else if (type==FloEvent::NOTE_ON)
+ {
+ int rest=t-lastevent;
+ if (rest)
+ {
+ printf("\tset rest at %i with len %i\n",lastevent,rest);
+ // no need to check if the rest crosses measure boundaries;
+ // it can't.
+
+ list<note_len_t> lens=parse_note_len(rest,lastevent-last_measure,emphasize_list,DOTTED_RESTS,UNSPLIT_RESTS);
+ unsigned tmppos=lastevent;
+ for (list<note_len_t>::iterator x=lens.begin(); x!=lens.end(); x++)
+ {
+ cout << "\t\tpartial rest with len="<<x->len<<", dots="<<x->dots<<endl;
+ itemlist[tmppos].insert( FloItem(FloItem::REST,notepos,x->len,x->dots) );
+ tmppos+=calc_len(x->len,x->dots);
+ itemlist[tmppos].insert( FloItem(FloItem::REST_END,notepos,0,0) );
+ }
+ }
+
+
+
+ printf("\tset note at %i with len=%i\n", t, len);
+
+ int tmplen;
+ bool tied_note;
+
+ // if the note exceeds the current measure, split it.
+ if (t+len>next_measure)
+ {
+ tmplen=next_measure-t;
+ tied_note=true;
+
+ //append the "remainder" of the note to our EventList, so that
+ //it gets processed again when entering the new measure
+ int newlen=len-tmplen;
+ eventlist.insert(pair<unsigned, FloEvent>(next_measure, FloEvent(actual_tick,pitch, velo,0,FloEvent::NOTE_OFF, it->second.source_part, it->second.source_event)));
+ eventlist.insert(pair<unsigned, FloEvent>(next_measure, FloEvent(actual_tick,pitch, velo,newlen,FloEvent::NOTE_ON, it->second.source_part, it->second.source_event)));
+
+ cout << "\t\tnote was split to length "<<tmplen<<" + " << newlen<<endl;
+ }
+ else
+ {
+ tmplen=len;
+ tied_note=false;
+
+ cout << "\t\tinserting NOTE OFF at "<<t+len<<endl;
+ eventlist.insert(pair<unsigned, FloEvent>(t+len, FloEvent(t+len,pitch, velo,0,FloEvent::NOTE_OFF,it->second.source_part, it->second.source_event)));
+ }
+
+ list<note_len_t> lens=parse_note_len(tmplen,t-last_measure,emphasize_list,true,true);
+ unsigned tmppos=t;
+ int n_lens=lens.size();
+ int count=0;
+ for (list<note_len_t>::iterator x=lens.begin(); x!=lens.end(); x++)
+ {
+ cout << "\t\tpartial note with len="<<x->len<<", dots="<<x->dots<<endl;
+ count++;
+
+ bool tie;
+
+ if (count<n_lens)
+ tie=true; // all notes except the last are always tied
+ else
+ tie=tied_note; // only the last respects tied_note
+
+ itemlist[tmppos].insert( FloItem(FloItem::NOTE,notepos,x->len,x->dots, tie, actual_tick, it->second.source_part, it->second.source_event) );
+ tmppos+=calc_len(x->len,x->dots);
+ itemlist[tmppos].insert( FloItem(FloItem::NOTE_END,notepos,0,0) );
+ }
+ }
+ else if (type==FloEvent::NOTE_OFF)
+ {
+ lastevent=t;
+ }
+ else if (type==FloEvent::TIME_SIG)
+ {
+ cout << "inserting TIME SIGNATURE "<<it->second.num<<"/"<<it->second.denom<<" at "<<t<<endl;
+ itemlist[t].insert( FloItem(FloItem::TIME_SIG, it->second.num, it->second.denom) );
+
+ emphasize_list=create_emphasize_list(it->second.num, it->second.denom);
+ }
+ else if (type==FloEvent::KEY_CHANGE)
+ {
+ cout << "inserting KEY CHANGE ("<<it->second.key<<") at "<<t<<endl;
+ itemlist[t].insert( FloItem(FloItem::KEY_CHANGE, it->second.key) );
+ tmp_key=it->second.key;
+ }
+ }
+}
+
+void staff_t::process_itemlist()
+{
+ stdmap<int,int> occupied;
+ int last_measure=0;
+ vector<int> emphasize_list=create_emphasize_list(4,4); //unneccessary, only for safety
+
+ //iterate through all times with items
+ for (ScoreItemList::iterator it2=itemlist.begin(); it2!=itemlist.end(); it2++)
+ {
+ set<FloItem, floComp>& curr_items=it2->second;
+
+ cout << "at t="<<it2->first<<endl;
+
+ // phase 0: keep track of active notes, rests -------------------
+ // (and occupied lines) and the last measure
+ // and the current time signature
+ for (set<FloItem, floComp>::iterator it=curr_items.begin(); it!=curr_items.end(); it++)
+ {
+ if ((it->type==FloItem::NOTE) || (it->type==FloItem::REST))
+ occupied[it->pos.height]++;
+ else if ((it->type==FloItem::NOTE_END) || (it->type==FloItem::REST_END))
+ occupied[it->pos.height]--;
+ else if (it->type==FloItem::BAR)
+ last_measure=it2->first;
+ else if (it->type==FloItem::TIME_SIG)
+ emphasize_list=create_emphasize_list(it->num, it->denom);
+ }
+
+ cout << "occupied: ";
+ for (stdmap<int,int>::iterator i=occupied.begin(); i!=occupied.end(); i++)
+ if (i->second) cout << i->first << "("<<i->second<<") ";
+ cout << endl;
+
+
+
+
+
+ // phase 1: group rests together -----------------------------------
+ int n_groups=0;
+ bool dont_group=false;
+
+ //iterate through all rests R at that time
+ // iterate through all rests X at that time below R
+ // if something is between X and R ("barrier"), stop
+ // else: group them together
+ for (set<FloItem, floComp>::iterator it=curr_items.begin(); it!=curr_items.end();)
+ {
+ //only operate on rests; ignore rests which are created by this code
+ //(can be seen on already_grouped)
+ if ((it->type==FloItem::REST) && (it->already_grouped==false))
+ {
+ cout << "trying to group" << endl;
+
+ int lastheight;
+ int height_cumulative=0;
+ int counter=0;
+
+ lastheight=it->pos.height;
+
+ set<FloItem, floComp>::iterator tmp;
+ for (tmp=it; tmp!=curr_items.end();)
+ {
+ cout << "checking if we can proceed with an item at height="<<tmp->pos.height<<endl;
+
+ for (int i=lastheight+1; i<=tmp->pos.height-1; i++)
+ if (occupied[i]!=0)
+ {
+ cout << "we can NOT, because occ["<<i<<"] != 0" << endl;
+ //stop grouping that rest
+ goto get_out_here;
+ }
+
+ lastheight=tmp->pos.height;
+
+ // the current item is a rest with equal len? cool!
+ if (tmp->type==FloItem::REST && tmp->len==it->len && tmp->dots==it->dots)
+ {
+ // füge diese pause zur gruppe dazu und entferne sie von diesem set hier
+ // entfernen aber nur, wenn sie nicht it, also die erste pause ist, die brauchen wir noch!
+ cout << "\tgrouping rest at height="<<tmp->pos.height<<endl;
+ height_cumulative+=tmp->pos.height;
+ counter++;
+ if (tmp!=it)
+ curr_items.erase(tmp++);
+ else
+ tmp++;
+ }
+ else //it's something else? well, we can stop grouping that rest then
+ {
+ cout << "we can NOT, because that item is not a rest" << endl;
+ //stop grouping that rest
+ goto get_out_here;
+ }
+ }
+ cout << "no items to proceed on left, continuing" << endl;
+ get_out_here:
+
+ n_groups++;
+
+ // entferne it vom set und
+ // füge eine pause mit dem "mittelwert" ein.
+ // occupied und die "_END"-events bleiben unberührt
+
+ FloItem temp=*it;
+ temp.already_grouped=true;
+
+ // have we grouped all available rests into one single?
+ if ( (n_groups==1) && (tmp==curr_items.end()) && !dont_group)
+ {
+ cout << "wow, we were able to group all rests into one single" << endl;
+ if (temp.len==0) //the whole rest is shifted one line (one space and one line)
+ temp.pos.height=DEFAULT_REST_HEIGHT+2;
+ else
+ temp.pos.height=DEFAULT_REST_HEIGHT;
+ }
+ else
+ {
+ cout << "creating group #"<<n_groups<<endl;
+ temp.pos.height=nearbyint((float)height_cumulative/counter);
+ }
+
+ // do NOT first insert, then erase, because if temp.height ==
+ // it->height, the set considers temp and it equal (it doesn't
+ // take already_grouped into account)
+ // the result of this: insert does nothing, and erase erases
+ // the item. effect: you don't have the rest at all
+ curr_items.erase(it++);
+
+ cout << "replacing all grouped rests with a rest at height="<<temp.pos.height<<endl;
+
+ curr_items.insert(temp);
+ }
+ else
+ {
+ if (it->type==FloItem::NOTE)
+ dont_group=true;
+
+ it++;
+ }
+ }
+
+
+
+
+
+ // phase 2: avoid collisions of items ------------------------------
+ set<FloItem, floComp>::iterator lastit, groupbegin, invalid;
+ invalid=curr_items.end();
+ lastit=invalid;
+ groupbegin=invalid;
+ int count;
+
+ //TODO FINDMICH MARKER: is "grouping" notes and rests together okay?
+ // or is it better to ignore rests when grouping?
+ for (set<FloItem, floComp>::iterator it=curr_items.begin(); it!=curr_items.end(); it++)
+ if ( (it->type==FloItem::NOTE) || (it->type==FloItem::REST) )
+ {
+ if (lastit != invalid)
+ {
+ if (it->pos.height == lastit->pos.height+1) // they would collide?
+ {
+ if (groupbegin==invalid) // we have no group atm?
+ {
+ groupbegin=lastit; // start a new group
+ count=1; // because lastit has to be taken into account.
+ // for "it", there's a count++ later
+ }
+
+ // the following will work even on start-new-group,
+ // because lastit will be "untouched", and that's why
+ // still be initalized to "false"
+ it->ausweich=!lastit->ausweich;
+
+ count++;
+ }
+ else
+ {
+ if (groupbegin!=invalid) //this is the first item which
+ { //doesn't belong to the previous group any more
+ if (count%2 == 0) //count is even?
+ if (modulo(groupbegin->pos.height, 2) == AUSWEICHEN_BEVORZUGT)
+ for (set<FloItem, floComp>::iterator tmp=groupbegin; tmp!=it; tmp++)
+ tmp->ausweich=!tmp->ausweich;
+
+ groupbegin=invalid;
+ }
+ // else: everything is ok :)
+ }
+ }
+
+ lastit=it;
+ }
+
+ // this could be the case if the last processed item before end()
+ // still belonged to a group. finalize this last group as well:
+ if (groupbegin!=invalid)
+ {
+ if (count%2 == 0) //count is even?
+ if (modulo(groupbegin->pos.height, 2) == AUSWEICHEN_BEVORZUGT)
+ for (set<FloItem, floComp>::iterator tmp=groupbegin; tmp!=curr_items.end(); tmp++)
+ tmp->ausweich=!tmp->ausweich;
+ }
+ // else: everything is ok :)
+
+
+
+
+
+ // phase 3: group notes by their length and ------------------------
+ // find out appropriate stem directions
+group_them_again:
+ stdmap<int, cumulative_t> lengths;
+ bool has_whole=false;
+
+ // find out which note lengths are present at that time
+ for (set<FloItem, floComp>::iterator it=curr_items.begin(); it!=curr_items.end(); it++)
+ if (it->type==FloItem::NOTE)
+ lengths[it->len].add(it->pos.height);
+
+ cout << "note lengths at that time are:";
+ for (stdmap<int, cumulative_t>::iterator it=lengths.begin(); it!=lengths.end(); it++)
+ cout << it->first << "("<< it->second.mean() <<") ";
+ cout << endl;
+
+ if (lengths.erase(0)) // in case "0" is in the set, erase it
+ has_whole=true; // but remember there were whole notes
+
+ if (lengths.size()==0)
+ {
+ cout << "no notes other than wholes, or no notes at all. we can relax" << endl;
+ }
+ else if (lengths.size()==1)
+ {
+ pair<const int, cumulative_t>& group=*(lengths.begin());
+ stem_t stem;
+ int shift=0;
+ cout << "only one non-whole note group (len="<<group.first<<") at height="<<group.second.mean()<< endl;
+
+ if (group.second.mean()>=6)
+ {
+ stem=DOWNWARDS;
+ if (has_whole)
+ shift=-1;
+ }
+ else
+ {
+ stem=UPWARDS;
+ if (has_whole)
+ shift=1;
+ }
+
+ // for each note in that group
+ for (set<FloItem, floComp>::iterator it=curr_items.begin(); it!=curr_items.end(); it++)
+ if ( (it->type==FloItem::NOTE) && (it->len==group.first) )
+ {
+ it->stem=stem;
+ it->shift=shift;
+ }
+ }
+ else if (lengths.size()==2)
+ {
+ stdmap<int, cumulative_t>::iterator it=lengths.begin();
+ pair<const int, cumulative_t>& group1=*it;
+ it++;
+ pair<const int, cumulative_t>& group2=*it;
+ stem_t stem1, stem2;
+ int shift1=0, shift2=0;
+ cout << "two non-whole note group: len="<<group1.first<<" at height="<<group1.second.mean()<<" and len="<<group2.first<<" at height="<<group2.second.mean()<< endl;
+
+ if (group1.second.mean()<group2.second.mean())
+ {
+ stem1=DOWNWARDS;
+ stem2=UPWARDS;
+ shift1=-1;
+ if (has_whole)
+ shift2=1;
+ }
+ else
+ {
+ stem1=UPWARDS;
+ stem2=DOWNWARDS;
+ shift2=-1;
+ if (has_whole)
+ shift1=1;
+ }
+
+ // for each note in group1
+ for (set<FloItem, floComp>::iterator it=curr_items.begin(); it!=curr_items.end(); it++)
+ if ( (it->type==FloItem::NOTE) && (it->len==group1.first) )
+ {
+ it->stem=stem1;
+ it->shift=shift1;
+ }
+
+ // for each note in group2
+ for (set<FloItem, floComp>::iterator it=curr_items.begin(); it!=curr_items.end(); it++)
+ if ( (it->type==FloItem::NOTE) && (it->len==group2.first) )
+ {
+ it->stem=stem2;
+ it->shift=shift2;
+ }
+ }
+ else //more than 2 groups
+ {
+ //at this time, there are no iterators pointing to curr_items.
+ //this means, we can erase and insert safely into curr_items here.
+
+ //group1 contains the longer notes, group2 the shorter
+
+ int group1_n=lengths.size()/2; //round down
+ int group2_n=lengths.size()-group1_n;
+
+ int group1_len, group2_len;
+ int group1_len_ticks, group2_len_ticks;
+
+
+ stdmap<int, cumulative_t>::iterator lit=lengths.begin();
+ for (int i=0;i<group1_n-1;i++) lit++; //go to the group1_n-th entry
+ group1_len=lit->first;
+ for (int i=0;i<group2_n;i++) lit++; //go to the (group1_n+group2_n)-th entry (i.e., the last before end() )
+ group2_len=lit->first;
+
+ group1_len_ticks=calc_len(group1_len,0);
+ group2_len_ticks=calc_len(group2_len,0);
+
+ cout << "we have "<<lengths.size()<<" groups. putting the "<<group1_n<<" longest and the "<<group2_n<<"shortest groups together"<<endl;
+ cout << "\tgroup1 will have len="<<group1_len<<" ("<<group1_len_ticks<<" ticks), group2 will have len="<<group2_len<<" ("<<group2_len_ticks<<" ticks)"<<endl;
+
+ for (set<FloItem, floComp>::iterator it=curr_items.begin(); it!=curr_items.end();)
+ if (it->type==FloItem::NOTE)
+ {
+ //if *it belongs to group1 and has not already its destination length
+ cout << "\tprocessing note-item with len="<<it->len<<endl;
+ if (it->len<group1_len)
+ {
+ cout << "\t\thas to be changed to fit into group 1" << endl;
+ FloItem tmp=*it;
+ curr_items.erase(it++);
+
+ int len_ticks_remaining=calc_len(tmp.len, tmp.dots)-group1_len_ticks;
+ bool tied_note=tmp.tied;
+
+
+ //shorten the current item to it's group's length
+ tmp.len=group1_len;
+ tmp.dots=0;
+ tmp.tied=true;
+ curr_items.insert(tmp);
+
+ //create items for the remaining lengths (and a note_END for the just created shortened note)
+ int t=it2->first+group1_len_ticks;
+
+ itemlist[t].insert( FloItem(FloItem::NOTE_END,tmp.pos,0,0) );
+
+ list<note_len_t> lens=parse_note_len(len_ticks_remaining,t-last_measure,emphasize_list,true,true);
+ unsigned tmppos=t;
+ int n_lens=lens.size();
+ int count=0;
+ for (list<note_len_t>::iterator x=lens.begin(); x!=lens.end(); x++)
+ {
+ cout << "\t\twhile regrouping: partial note with len="<<x->len<<", dots="<<x->dots<<endl;
+ count++;
+
+ bool tie;
+
+ if (count<n_lens)
+ tie=true; // all notes except the last are always tied
+ else
+ tie=tied_note; // only the last respects tied_note
+
+ itemlist[tmppos].insert( FloItem(FloItem::NOTE, tmp.pos,x->len,x->dots, tie, tmp.begin_tick, tmp.source_part, tmp.source_event) );
+ tmppos+=calc_len(x->len,x->dots);
+ itemlist[tmppos].insert( FloItem(FloItem::NOTE_END, tmp.pos,0,0) );
+ }
+
+ }
+ //else if *it belongs to group2 and has not already its destination length
+ else if ((it->len<group2_len) && (it->len>group1_len))
+ {
+ cout << "\t\thas to be changed to fit into group 2" << endl;
+
+ FloItem tmp=*it;
+ curr_items.erase(it++);
+
+ int len_ticks_remaining=calc_len(tmp.len, tmp.dots)-group2_len_ticks;
+ bool tied_note=tmp.tied;
+
+
+ //shorten the current item to it's group's length
+ tmp.len=group2_len;
+ tmp.dots=0;
+ tmp.tied=true;
+ curr_items.insert(tmp);
+
+ //create items for the remaining lengths (and a note_END for the just created shortened note)
+ int t=it2->first+group2_len_ticks;
+
+ itemlist[t].insert( FloItem(FloItem::NOTE_END,tmp.pos,0,0) );
+
+ list<note_len_t> lens=parse_note_len(len_ticks_remaining,t-last_measure,emphasize_list,true,true);
+ unsigned tmppos=t;
+ int n_lens=lens.size();
+ int count=0;
+ for (list<note_len_t>::iterator x=lens.begin(); x!=lens.end(); x++)
+ {
+ cout << "\t\twhile regrouping: partial note with len="<<x->len<<", dots="<<x->dots<<endl;
+ count++;
+
+ bool tie;
+
+ if (count<n_lens)
+ tie=true; // all notes except the last are always tied
+ else
+ tie=tied_note; // only the last respects tied_note
+
+ itemlist[tmppos].insert( FloItem(FloItem::NOTE,tmp.pos,x->len,x->dots, tie, tmp.begin_tick, tmp.source_part, tmp.source_event) );
+ tmppos+=calc_len(x->len,x->dots);
+ itemlist[tmppos].insert( FloItem(FloItem::NOTE_END,tmp.pos,0,0) );
+ }
+
+ }
+ else //nothing to do?
+ {
+ cout << "\t\tnothing to do" << endl;
+ it++;
+ }
+ }
+ else
+ it++;
+
+ goto group_them_again; //do it again
+ }
+
+ }
+}
+
+//draw a pixmap centered
+void ScoreCanvas::draw_pixmap(QPainter& p, int x, int y, const QPixmap& pm)
+{
+ cout << "drawing pixmap width size="<<pm.width()<<"/"<<pm.height()<<" at "<<x<<"/"<<y<<endl;
+ p.drawPixmap(x-pm.width()/2,y-pm.height()/2,pm);
+}
+
+QRect bbox_center(int x, int y, const QSize& size)
+{
+ //why x-foo/2+foo? because due to integer divisions,
+ // x-foo/2+foo can be smaller than x+foo/2!
+ return QRect(x-size.width()/2,y-size.height()/2,size.width(),size.height());
+}
+
+QRect FloItem::bbox() const
+{
+ return bbox_center(x,y,pix->size());
+}
+
+void ScoreCanvas::draw_note_lines(QPainter& p, int y)
+{
+ int xend=width();
+
+ p.setPen(Qt::black);
+
+ for (int i=0;i<5;i++)
+ p.drawLine(0,y + i*YLEN - 2*YLEN,xend,y + i*YLEN - 2*YLEN);
+}
+
+
+void staff_t::calc_item_pos()
+{
+ 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 * PIXELS_PER_WHOLE/TICKS_PER_WHOLE +pos_add;
+ //if this changes, also change the line(s) with YLEN (but not all). don't change it.
+ it->y=2*YLEN - (it->pos.height-2)*YLEN/2;
+
+ if (it->type==FloItem::NOTE)
+ {
+ it->x+=NOTE_MOVE_X + it->shift*NOTE_SHIFT;
+
+ switch (it->len)
+ {
+ case 0: it->pix=pix_whole; break;
+ case 1: it->pix=pix_half; break;
+ default: it->pix=pix_quarter; break;
+ }
+
+ it->stem_x=it->x;
+
+ if (it->ausweich)
+ {
+ if ((it->stem==UPWARDS) || (it->len==0))
+ it->x += it->pix->width()-1; //AUSWEICH_X
+ else
+ it->x -= it->pix->width()-1; //AUSWEICH_X
+ }
+
+ //if there's a tie, try to find the tie's destination and set is_tie_dest
+ if (it->tied)
+ {
+ set<FloItem, floComp>::iterator dest;
+ set<FloItem, floComp>& desttime = itemlist[it2->first+calc_len(it->len,it->dots)];
+ for (dest=desttime.begin(); dest!=desttime.end();dest++)
+ if ((dest->type==FloItem::NOTE) && (dest->pos==it->pos))
+ {
+ dest->is_tie_dest=true;
+ dest->tie_from_x=it->x;
+ break;
+ }
+
+ if (dest==desttime.end())
+ cout << "THIS SHOULD NEVER HAPPEN: did not find destination note for tie!" << endl;
+ }
+ }
+ else if (it->type==FloItem::REST)
+ {
+ switch (it->len)
+ {
+ case 0: it->pix=pix_r1; break;
+ case 1: it->pix=pix_r2; break;
+ case 2: it->pix=pix_r4; break;
+ case 3: it->pix=pix_r8; break;
+ case 4: it->pix=pix_r16; break;
+ }
+
+ it->x+=NOTE_MOVE_X + (it->ausweich ? REST_AUSWEICH_X : 0); //AUSWEICH_X
+ }
+ else if (it->type==FloItem::BAR)
+ {
+ //nothing to do :)
+ }
+ else if (it->type==FloItem::TIME_SIG)
+ {
+ int add=calc_timesig_width(it->num, it->denom);
+ pos_add+=add;
+ }
+ else if (it->type==FloItem::KEY_CHANGE)
+ {
+ 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++)
+ {
+ cout << "at t="<<it2->first << endl;
+
+ int upstem_y1 = -1, upstem_y2=-1, upstem_x=-1, upflag=-1;
+ int downstem_y1 = -1, downstem_y2=-1, downstem_x=-1, downflag=-1;
+
+ for (set<FloItem, floComp>::iterator it=it2->second.begin(); it!=it2->second.end();it++)
+ {
+ if (it->type==FloItem::NOTE)
+ {
+ cout << "\tNOTE at line"<<it->pos.height<<" with acc.="<<it->pos.vorzeichen<<", len="<<pow(2,it->len);
+ for (int i=0;i<it->dots;i++) cout << ".";
+ cout << " , stem=";
+ if (it->stem==UPWARDS)
+ cout << "UPWARDS";
+ else
+ cout << "DOWNWARDS";
+
+ cout << " , shift="<<it->shift<<", ausweich="<<it->ausweich<<", ";
+ if (!it->tied) cout << "un";
+ cout << "tied, is_tie_dest="<<it->is_tie_dest<<endl;
+
+ if (it->len!=0) //only for non-whole notes the stems are relevant!
+ {
+ if (it->stem==UPWARDS)
+ {
+ if (upstem_y1 == -1)
+ upstem_y1=it->y;
+
+ upstem_y2=it->y;
+
+
+ if ((upflag!=-1) && (upflag!=it->len))
+ cout << "WARNING: THIS SHOULD NEVER HAPPEN: upflag != this->flag" << endl;
+ upflag=it->len;
+
+ if ((upstem_x!=-1) && (upstem_x!=it->stem_x ))
+ cout << "WARNING: THIS SHOULD NEVER HAPPEN: upstem_x != x_result" << endl;
+ upstem_x=it->stem_x;
+ }
+ else
+ {
+ if (downstem_y1 == -1)
+ downstem_y1=it->y;
+
+ downstem_y2=it->y;
+
+
+ if ((downflag!=-1) && (downflag!=it->len))
+ cout << "WARNING: THIS SHOULD NEVER HAPPEN: downflag != this->flag" << endl;
+ downflag=it->len;
+
+ if ((downstem_x!=-1) && (downstem_x!=it->stem_x))
+ cout << "WARNING: THIS SHOULD NEVER HAPPEN: downstem_x != x_result" << endl;
+ downstem_x=it->stem_x; //important: before the below calculation!
+ }
+ }
+
+
+ if (it->pos.height <= 0) //we need auxiliary lines on the bottom?
+ {
+ p.setPen(Qt::black);
+ for (int i=0; i>=it->pos.height; i-=2)
+ p.drawLine(it->x-it->pix->width()*AUX_LINE_LEN/2 -x_pos+x_left,y_offset + 2*YLEN - (i-2)*YLEN/2,it->x+it->pix->width()*AUX_LINE_LEN/2-x_pos+x_left,y_offset + 2*YLEN - (i-2)*YLEN/2);
+ }
+ else if (it->pos.height >= 12) //we need auxiliary lines on the top?
+ {
+ p.setPen(Qt::black);
+ for (int i=12; i<=it->pos.height; i+=2)
+ p.drawLine(it->x-it->pix->width()*AUX_LINE_LEN/2 -x_pos+x_left,y_offset + 2*YLEN - (i-2)*YLEN/2,it->x+it->pix->width()*AUX_LINE_LEN/2-x_pos+x_left,y_offset + 2*YLEN - (i-2)*YLEN/2);
+ }
+
+ it->is_active= ( (song->cpos() >= it->source_event->tick() + it->source_part->tick()) &&
+ (song->cpos() < it->source_event->endTick() + it->source_part->tick()) );
+
+ int color_index=it->source_part->colorIndex();
+
+ if (audio->isPlaying() && it->is_active)
+ color_index=HIGHLIGHTED_PIXMAP;
+
+ draw_pixmap(p,it->x -x_pos+x_left,y_offset + it->y,it->pix[color_index]);
+ //TODO FINDMICH maybe draw a margin around bright colors?
+ //maybe draw the default color in black?
+
+ //draw dots
+
+ int x_dot=DOT_XBEGIN;
+ int y_dot;
+ if (modulo(it->pos.height, 2) == 0) //note is on a line?
+ y_dot=YLEN * 0.33; // actually 0.5, but that would be _exactly_ in the space
+ else //note is between two lines?
+ y_dot=YLEN * 0.1;
+
+ if (it->stem==DOWNWARDS)
+ y_dot=-y_dot;
+ //else y_dot=y_dot;
+
+ for (int i=0;i<it->dots;i++)
+ {
+ draw_pixmap(p,it->x+x_dot -x_pos+x_left,y_offset + it->y+y_dot,pix_dot[color_index]);
+ x_dot+=DOT_XDIST;
+ }
+
+
+
+ //draw accidentials
+ if (it->pos.vorzeichen != curr_accidential[modulo(it->pos.height,7)])
+ {
+ QPixmap* acc_pix;
+ switch (it->pos.vorzeichen)
+ {
+ case NONE: acc_pix=pix_noacc; break;
+ case SHARP: acc_pix=pix_sharp; break;
+ case B: acc_pix=pix_b; break;
+ }
+ draw_pixmap(p,it->x-ACCIDENTIAL_DIST -x_pos+x_left,y_offset + it->y, acc_pix[color_index]);
+
+ curr_accidential[modulo(it->pos.height,7)]=it->pos.vorzeichen;
+ }
+
+
+ //if needed, draw tie
+ if (it->is_tie_dest)
+ {
+ cout << "drawing tie" << endl;
+ draw_tie(p,it->tie_from_x-x_pos+x_left,it->x -x_pos+x_left,y_offset + it->y, (it->len==0) ? true : (it->stem==DOWNWARDS) , config.partColors[color_index]);
+ // in english: "if it's a whole note, tie is upwards (true). if not, tie is upwards if
+ // stem is downwards and vice versa"
+ }
+ }
+ else if (it->type==FloItem::REST)
+ {
+ cout << "\tREST at line"<<it->pos.height<<" with len="<<pow(2,it->len);
+ for (int i=0;i<it->dots;i++) cout << ".";
+ cout << " , ausweich="<<it->ausweich<<endl;
+
+ draw_pixmap(p,it->x -x_pos+x_left,y_offset + it->y,*it->pix);
+
+
+ //draw dots
+
+ int x_dot=DOT_XBEGIN_REST;
+ int y_dot;
+ if (modulo(it->pos.height, 2) == 0) //rest is on a line?
+ y_dot=YLEN * 0.33; // actually 0.5, but that would be _exactly_ in the space
+ else //note is between two lines?
+ y_dot=YLEN * 0.1;
+
+ if (it->len!=0) // all rests except the whole are treated as
+ y_dot=-y_dot; // if they had a downwards stem
+
+ for (int i=0;i<it->dots;i++)
+ {
+ draw_pixmap(p,it->x+x_dot -x_pos+x_left,y_offset + it->y+y_dot,pix_dot[BLACK_PIXMAP]);
+ x_dot+=DOT_XDIST;
+ }
+ }
+ else if (it->type==FloItem::BAR)
+ {
+ cout << "\tBAR" << endl;
+
+ p.setPen(Qt::black);
+ p.drawLine(it->x -x_pos+x_left,y_offset -2*YLEN,it->x -x_pos+x_left,y_offset +2*YLEN);
+
+ for (int i=0;i<7;i++)
+ curr_accidential[i]=default_accidential[i];
+ }
+ else if (it->type==FloItem::TIME_SIG)
+ {
+ cout << "\tTIME SIGNATURE: "<<it->num<<"/"<<it->denom<<endl;
+
+ draw_timesig(p, it->x - x_pos+x_left, y_offset, it->num, it->denom);
+ }
+ else if (it->type==FloItem::KEY_CHANGE)
+ {
+ key_enum new_key=it->key;
+ cout << "\tKEY CHANGE: from "<<curr_key<<" to "<<new_key<<endl;
+
+ list<int> aufloes_list=calc_accidentials(curr_key, staff.clef, new_key);
+ list<int> new_acc_list=calc_accidentials(new_key, staff.clef);
+
+ // vorzeichen aus curr_key auflösen
+ draw_accidentials(p, it->x + KEYCHANGE_ACC_LEFTDIST - x_pos+x_left, y_offset, aufloes_list, pix_noacc[BLACK_PIXMAP]);
+
+ // alle vorzeichen aus new_key zeichnen
+ QPixmap* pix = is_sharp_key(new_key) ? &pix_sharp[BLACK_PIXMAP] : &pix_b[BLACK_PIXMAP];
+ vorzeichen_t new_accidential = is_sharp_key(new_key) ? SHARP : B;
+
+ draw_accidentials(p, it->x + aufloes_list.size()*KEYCHANGE_ACC_DIST + KEYCHANGE_ACC_LEFTDIST - x_pos+x_left, y_offset, new_acc_list, *pix);
+
+ for (int i=0;i<7;i++)
+ curr_accidential[i]=default_accidential[i]=NONE;
+
+ for (list<int>::iterator acc_it=new_acc_list.begin(); acc_it!=new_acc_list.end(); acc_it++)
+ default_accidential[*acc_it % 7]=curr_accidential[*acc_it % 7]=new_accidential;
+
+ curr_key=new_key;
+ }
+ }
+
+ p.setPen(Qt::black);
+ //note: y1 is bottom, y2 is top!
+ if (upstem_x!=-1)
+ {
+ upstem_x=upstem_x-pix_quarter[0].width()/2 +pix_quarter[0].width() -1;
+ p.drawLine(upstem_x -x_pos+x_left, y_offset + upstem_y1, upstem_x -x_pos+x_left, y_offset + upstem_y2-STEM_LEN);
+
+ if (upflag>=3) //if the note needs a flag
+ p.drawPixmap(upstem_x -x_pos+x_left,y_offset + upstem_y2-STEM_LEN,pix_flag_up[upflag-3]);
+ }
+ if (downstem_x!=-1)
+ {
+ downstem_x=downstem_x-pix_quarter[0].width()/2;
+ p.drawLine(downstem_x -x_pos+x_left, y_offset + downstem_y1+STEM_LEN, downstem_x -x_pos+x_left, y_offset + downstem_y2);
+
+ if (downflag>=3) //if the note needs a flag
+ p.drawPixmap(downstem_x -x_pos+x_left,y_offset + downstem_y1+STEM_LEN-pix_flag_down[downflag-3].height(),pix_flag_down[downflag-3]);
+ }
+ }
+}
+
+bool ScoreCanvas::need_redraw_for_hilighting()
+{
+ for (list<staff_t>::iterator it=staffs.begin(); it!=staffs.end(); it++)
+ if (need_redraw_for_hilighting(it->itemlist)) return true;
+
+ return false;
+}
+
+bool ScoreCanvas::need_redraw_for_hilighting(ScoreItemList& itemlist)
+{
+ return need_redraw_for_hilighting(itemlist, x_pos,x_pos+width()-x_left);
+}
+
+bool ScoreCanvas::need_redraw_for_hilighting(ScoreItemList& itemlist, int x1, int x2)
+{
+ int from_tick, to_tick;
+ ScoreItemList::iterator from_it, to_it;
+
+ from_tick=x_to_tick(x1);
+ from_it=itemlist.lower_bound(from_tick);
+ //from_it now contains the first time which is fully drawn
+ //however, the previous beat could still be relevant, when it's
+ //partly drawn. so we decrement from_it
+ if (from_it!=itemlist.begin()) from_it--;
+
+ to_tick=x_to_tick(x2);
+ to_it=itemlist.upper_bound(to_tick);
+ //to_it now contains the first time which is not drawn at all any more
+
+ return need_redraw_for_hilighting(from_it, to_it);
+}
+
+bool ScoreCanvas::need_redraw_for_hilighting(ScoreItemList::iterator from_it, ScoreItemList::iterator to_it)
+{
+ //if we aren't playing, there will never be a need for redrawing due to highlighting things
+ if (audio->isPlaying()==false)
+ return false;
+
+ for (ScoreItemList::iterator it2=from_it; it2!=to_it; it2++)
+ for (set<FloItem, floComp>::iterator it=it2->second.begin(); it!=it2->second.end();it++)
+ if (it->type==FloItem::NOTE)
+ {
+ bool is_active= ( (song->cpos() >= it->source_event->tick() + it->source_part->tick()) &&
+ (song->cpos() < it->source_event->endTick() + it->source_part->tick()) );
+ if (it->is_active != is_active)
+ return true;
+ }
+
+ return false;
+}
+
+int clef_height(clef_t clef)
+{
+ switch (clef) //CLEF_MARKER
+ {
+ case VIOLIN: return 4;
+ case BASS: return 8;
+ default:
+ cout << "WARNING: ILLEGAL FUNCTION CALL in clef_height()" << endl;
+ return 6;
+ }
+}
+
+#define TIMESIG_LEFTMARGIN 5
+#define TIMESIG_RIGHTMARGIN 5
+#define DIGIT_YDIST 9
+#define DIGIT_WIDTH 12
+
+#define CLEF_LEFTMARGIN 5
+#define CLEF_RIGHTMARGIN 5
+
+void ScoreCanvas::draw_preamble(QPainter& p, int y_offset, clef_t clef)
+{
+ int x_left_old=x_left;
+ int tick=x_to_tick(x_pos);
+
+ // draw clef --------------------------------------------------------
+ QPixmap* pix_clef= (clef==BASS) ? pix_clef_bass : pix_clef_violin;
+ int y_coord=2*YLEN - ( clef_height(clef) -2)*YLEN/2;
+
+ draw_pixmap(p,CLEF_LEFTMARGIN + pix_clef->width()/2,y_offset + y_coord,*pix_clef);
+
+ x_left= CLEF_LEFTMARGIN + pix_clef->width() + CLEF_RIGHTMARGIN + KEYCHANGE_ACC_LEFTDIST;
+
+
+ // draw accidentials ------------------------------------------------
+ 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 + TIMESIG_LEFTMARGIN;
+
+
+ // draw time signature ----------------------------------------------
+ timesig_t timesig=timesig_at_tick(tick);
+
+ draw_timesig(p, x_left, y_offset, timesig.num, timesig.denom);
+
+ x_left+=calc_timesig_width(timesig.num, timesig.denom)+TIMESIG_RIGHTMARGIN;
+
+ // draw bar ---------------------------------------------------------
+ p.setPen(Qt::black);
+ p.drawLine(x_left,y_offset -2*YLEN,x_left,y_offset +2*YLEN);
+
+
+ if (x_left_old!=x_left)
+ emit viewport_width_changed(viewport_width());
+}
+
+
+void ScoreCanvas::draw_timesig(QPainter& p, int x, int y_offset, int num, int denom)
+{
+ int num_width=calc_number_width(num);
+ int denom_width=calc_number_width(denom);
+ int width=((num_width > denom_width) ? num_width : denom_width);
+ int num_indent=(width-num_width)/2 + TIMESIG_LEFTMARGIN;
+ int denom_indent=(width-denom_width)/2 + TIMESIG_LEFTMARGIN;
+
+ draw_number(p, x+num_indent, y_offset -DIGIT_YDIST, num);
+ draw_number(p, x+denom_indent, y_offset +DIGIT_YDIST, denom);
+}
+
+int calc_timesig_width(int num, int denom)
+{
+ int num_width=calc_number_width(num);
+ int denom_width=calc_number_width(denom);
+ int width=((num_width > denom_width) ? num_width : denom_width);
+ return width+TIMESIG_LEFTMARGIN+TIMESIG_RIGHTMARGIN;
+}
-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();
-*/
- }
+int calc_number_width(int n)
+{
+ string str=IntToStr(n);
+ return (str.length()*DIGIT_WIDTH);
+}
+
+void ScoreCanvas::draw_number(QPainter& p, int x, int y, int n)
+{
+ string str=IntToStr(n);
+ int curr_x=x+DIGIT_WIDTH/2;
+
+ for (size_t i=0;i<str.length(); i++)
+ {
+ draw_pixmap(p, curr_x, y, pix_num[str[i]-'0']);
+ curr_x+=DIGIT_WIDTH;
+ }
+}
+
+
+void ScoreCanvas::draw(QPainter& p, const QRect&)
+{
+ cout <<"now in ScoreCanvas::draw"<<endl;
+
+
+
+ p.setPen(Qt::black);
+
+ for (list<staff_t>::iterator it=staffs.begin(); it!=staffs.end(); it++)
+ {
+ draw_note_lines(p,it->y_draw);
+ draw_preamble(p,it->y_draw, it->clef);
+ p.setClipRect(x_left+1,0,p.device()->width(),p.device()->height());
+ draw_items(p,it->y_draw, *it);
+ p.setClipping(false);
+ }
+}
+
+
+list<int> calc_accidentials(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;
+
+ cout << "t="<<t<<endl;
+
+ for (std::map<int,int>::iterator it=pos_add_list.begin(); it!=pos_add_list.end() && it->first<t; it++)
+ {
+ cout << "at pos_add event at t="<<it->first<<", add="<<it->second<<endl;
+ min_t=it->first;
+ x-=it->second;
+ t=TICKS_PER_WHOLE * x/PIXELS_PER_WHOLE;
+ }
+
+ return t > min_t ? t : min_t;
+}
+
+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:
+ cout << "WARNING: THIS SHOULD NEVER HAPPEN: unknown clef in height_to_pitch" << endl;
+ return 60;
+ }
+}
+
+int ScoreCanvas::height_to_pitch(int h, clef_t clef, 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 it=staff_at_y(event->y());
+
+ int y=event->y() - it->y_draw;
+ int x=event->x()+x_pos-x_left;
+ int tick=flo_quantize_floor(x_to_tick(x));
+ //TODO quantizing must (maybe?) be done with the proper functions
+
+ if (it!=staffs.end())
+ {
+ if (event->x() <= x_left) //clicked in the preamble?
+ {
+ if (event->button() == Qt::RightButton) //right-click?
+ {
+ current_staff=it;
+ staff_menu->popup(event->globalPos());
+ }
+ else if (event->button() == Qt::MidButton) //middle click?
+ {
+ remove_staff(it);
+ }
+ else if (event->button() == Qt::LeftButton) //left click?
+ {
+ current_staff=it;
+ dragging_staff=true;
+ }
+ }
+ else
+ {
+ ScoreItemList& itemlist=it->itemlist;
+
+ cout << "mousePressEvent at "<<x<<"/"<<y<<"; tick="<<tick<<endl;
+ set<FloItem, floComp>::iterator it;
+ for (it=itemlist[tick].begin(); it!=itemlist[tick].end(); it++)
+ if (it->type==FloItem::NOTE)
+ if (it->bbox().contains(x,y))
+ break;
+
+ if (it!=itemlist[tick].end()) //we found something?
+ {
+ mouse_down_pos=event->pos();
+ mouse_operation=NO_OP;
+
+ int t=tick;
+ set<FloItem, floComp>::iterator found;
+
+ do
+ {
+ found=itemlist[t].find(FloItem(FloItem::NOTE, it->pos));
+ if (found == itemlist[t].end())
+ {
+ cout << "FATAL: THIS SHOULD NEVER HAPPEN: could not find the note's tie-destination" << endl;
+ break;
+ }
+ else
+ {
+ t+=calc_len(found->len, found->dots);
+ }
+ } while (found->tied);
+
+ int total_begin=it->begin_tick;
+ int total_end=t;
+
+ int this_begin=tick;
+ int this_end=this_begin+calc_len(it->len, it->dots);
+
+ //that's the only note corresponding to the event?
+ if (this_begin==total_begin && this_end==total_end)
+ {
+ if (x < it->x)
+ mouse_x_drag_operation=BEGIN;
+ else
+ mouse_x_drag_operation=LENGTH;
+ }
+ //that's NOT the only note?
+ else
+ {
+ if (this_begin==total_begin)
+ mouse_x_drag_operation=BEGIN;
+ else if (this_end==total_end)
+ mouse_x_drag_operation=LENGTH;
+ else
+ mouse_x_drag_operation=NO_OP;
+ }
+
+ cout << "you clicked at a note with begin at "<<it->begin_tick<<" and end at "<<t<<endl;
+ cout << "x-drag-operation will be "<<mouse_x_drag_operation<<endl;
+ cout << "pointer to part is "<<it->source_part;
+ if (!it->source_part) cout << " (WARNING! THIS SHOULD NEVER HAPPEN!)";
+ cout << endl;
+
+ dragged_event=*it->source_event;
+ dragged_event_part=it->source_part;
+ dragged_event_original_pitch=dragged_event.pitch();
+
+ if ((mouse_erases_notes) || (event->button()==Qt::MidButton)) //erase?
+ {
+ audio->msgDeleteEvent(dragged_event, dragged_event_part, true, false, false);
+ }
+ else if (event->button()==Qt::LeftButton) //edit?
+ {
+ setMouseTracking(true);
+ dragging=true;
+ song->startUndo();
+ }
+ }
+ else //we found nothing?
+ {
+ if ((event->button()==Qt::LeftButton) && (mouse_inserts_notes))
+ {
+ signed int relative_tick=(signed) tick - curr_part->tick();
+ if (relative_tick>=0) //TODO FINDMICH do that better
+ {
+ song->startUndo();
+ //stopping undo at the end of this function is unneccessary
+ //because we'll begin a drag right after it. finishing
+ //this drag will stop undo as well (in mouseReleaseEvent)
+
+ Event newevent(Note);
+ newevent.setPitch(y_to_pitch(y,tick, USED_CLEF));
+ newevent.setVelo(64); //TODO
+ newevent.setVeloOff(64); //TODO
+ newevent.setTick(relative_tick);
+ newevent.setLenTick((new_len>0)?new_len:last_len);
+
+ audio->msgAddEvent(newevent, curr_part, false, false, false);
+
+ dragged_event_part=curr_part;
+ dragged_event=newevent;
+ dragged_event_original_pitch=newevent.pitch();
+
+ mouse_down_pos=event->pos();
+ mouse_operation=NO_OP;
+ mouse_x_drag_operation=LENGTH;
+
+ song_changed(0);
+
+ setMouseTracking(true);
+ dragging=true;
+ //song->startUndo(); unneccessary because we have started it already above
+ }
+ }
+ }
+ }
+ }
+}
+
+void ScoreCanvas::mouseReleaseEvent (QMouseEvent* event)
+{
+ if (dragging)
+ {
+ if (event->button()==Qt::LeftButton)
+ {
+ if (mouse_operation==LENGTH)
+ {
+ if (flo_quantize(dragged_event.lenTick()) <= 0)
+ {
+ cout << "new length <= 0, erasing item" << endl;
+ audio->msgDeleteEvent(dragged_event, dragged_event_part, false, false, false);
+ }
+ else
+ {
+ last_len=flo_quantize(dragged_event.lenTick());
+ }
+ }
+
+ song->endUndo(SC_EVENT_MODIFIED);
+ setMouseTracking(false);
+ dragging=false;
+
+ scroll_speed=0; scroll_pos=0;
+ }
+ }
+
+ if (dragging_staff)
+ {
+ merge_staves(staff_at_y(event->y()), current_staff);
+ dragging_staff=false;
+ }
+}
+
+#define PITCH_DELTA 5
+
+
+void ScoreCanvas::mouseMoveEvent (QMouseEvent* event)
+{
+ if (dragging)
+ {
+ int dx=event->x()-mouse_down_pos.x();
+ int dy=event->y()-mouse_down_pos.y();
+
+ int x=event->x()+x_pos-x_left;
+
+ int tick=flo_quantize_floor(x_to_tick(x));
+
+ if (mouse_operation==NO_OP)
+ {
+ if ((abs(dx)>DRAG_INIT_DISTANCE) && (mouse_x_drag_operation!=NO_OP))
+ {
+ cout << "mouse-operation is now "<<mouse_x_drag_operation<<endl;
+ mouse_operation=mouse_x_drag_operation;
+ }
+ else if (abs(dy)>DRAG_INIT_DISTANCE)
+ {
+ cout << "mouse-operation is now PITCH" << endl;
+ mouse_operation=PITCH;
+ }
+ }
+
+ int new_pitch;
+
+ switch (mouse_operation)
+ {
+ case NONE:
+ break;
+
+ case PITCH:
+ cout << "changing pitch, delta="<<nearbyint((float)dy/PITCH_DELTA)<<endl;
+ new_pitch=dragged_event_original_pitch - nearbyint((float)dy/PITCH_DELTA);
+
+ if (dragged_event.pitch()!=new_pitch)
+ {
+ Event tmp=dragged_event.clone();
+ tmp.setPitch(new_pitch);
+
+ audio->msgChangeEvent(dragged_event, tmp, dragged_event_part, false, false, false);
+ dragged_event=tmp;
+
+ song_changed(0);
+ }
+
+ break;
+
+ case BEGIN:
+ if (dragged_event.tick()+dragged_event_part->tick() != unsigned(tick)) //TODO FINDMICHJETZT tick kann unsigned werden
+ {
+ Event tmp=dragged_event.clone();
+
+ if (tick-signed(dragged_event_part->tick()) >= 0) //TODO FINDMICH do that better
+ tmp.setTick(tick-dragged_event_part->tick());
+
+ audio->msgChangeEvent(dragged_event, tmp, dragged_event_part, false, false, false);
+ dragged_event=tmp;
+
+ song_changed(0);
+ }
+
+ break;
+
+ case LENGTH:
+ tick+=FLO_QUANT;
+ if (dragged_event.tick()+dragged_event.lenTick() + dragged_event_part->tick() != unsigned(tick))
+ {
+ Event tmp=dragged_event.clone();
+
+ if (tick-signed(dragged_event.tick() -dragged_event_part->tick()) >= 0) //TODO FINDMICH do that better
+ tmp.setLenTick(tick-dragged_event.tick() -dragged_event_part->tick());
+
+ audio->msgChangeEvent(dragged_event, tmp, dragged_event_part, false, false, false);
+ dragged_event=tmp;
+
+ song_changed(0);
+ }
+
+ break;
+ }
+
+
+ if ((mouse_operation==LENGTH) || (mouse_operation==BEGIN)) //scrolling enabled?
+ {
+ int win_x=event->x();
+
+ if (win_x < x_left + SCROLL_MARGIN)
+ {
+ scroll_speed=(win_x - (x_left + SCROLL_MARGIN)) * SCROLL_SPEED;
+ if (scroll_speed < -SCROLL_SPEED_MAX) scroll_speed=-SCROLL_SPEED_MAX;
+ }
+ else if (win_x > width() - SCROLL_MARGIN)
+ {
+ scroll_speed=(win_x - (width() - SCROLL_MARGIN)) * SCROLL_SPEED;
+ if (scroll_speed > SCROLL_SPEED_MAX) scroll_speed=SCROLL_SPEED_MAX;
+ }
+ else
+ scroll_speed=0;
+ }
+ else
+ {
+ scroll_speed=0;
+ }
+ }
+}
+
+void ScoreCanvas::heartbeat_timer_event()
+{
+ if (scroll_speed)
+ {
+ int old_xpos=x_pos;
+
+ scroll_pos+=scroll_speed*heartBeatTimer->interval()/1000.0;
+ int tmp=int(scroll_pos);
+ if (tmp!=0)
+ x_pos+=tmp;
+ scroll_pos-=tmp;
+
+ if (x_pos<0) x_pos=0;
+ if (x_pos>canvas_width()) x_pos=canvas_width();
+
+ if (old_xpos!=x_pos) emit xpos_changed(x_pos);
+ }
+}
+
+void ScoreCanvas::scroll_event(int x)
+{
+ cout << "SCROLL EVENT: x="<<x<<endl;
+ x_pos=x;
+ redraw();
+}
+
+
+//if force is true, it will always happen something
+//if force is false, it will only happen something, if
+//tick isn't visible at all. if it's visible, but not at
+//the position goto would set it to, nothing happens
+void ScoreCanvas::goto_tick(int tick, bool force)
+{
+ if (!force)
+ {
+ if (tick < x_to_tick(x_pos))
+ {
+ x_pos=tick_to_x(tick) - x_left;
+ if (x_pos<0) x_pos=0;
+ if (x_pos>canvas_width()) x_pos=canvas_width();
+
+ emit xpos_changed(x_pos);
+ }
+ else if (tick > x_to_tick(x_pos+viewport_width()*PAGESTEP))
+ {
+ x_pos=tick_to_x(tick);
+ if (x_pos<0) x_pos=0;
+ if (x_pos>canvas_width()) x_pos=canvas_width();
+
+ emit xpos_changed(x_pos);
+ }
+ }
+ else
+ {
+ x_pos=tick_to_x(tick)-viewport_width()/2;
+ if (x_pos<0) x_pos=0;
+ if (x_pos>canvas_width()) x_pos=canvas_width();
+
+ emit xpos_changed(x_pos);
+ }
+}
//---------------------------------------------------------
-// toggleTrackInfo
+// resizeEvent
//---------------------------------------------------------
-void ScoreEdit::toggleTrackInfo()
+void ScoreCanvas::resizeEvent(QResizeEvent* ev)
{
- bool vis = midiTrackInfo->isVisible();
- infoScroll->setVisible(!vis);
- infoScroll->setEnabled(!vis);
+ QWidget::resizeEvent(ev); //TODO is this really neccessary?
+
+ emit viewport_width_changed( viewport_width() );
}
+
+void ScoreCanvas::pos_changed(int index, unsigned tick, bool scroll)
+{
+ if (index==0)
+ {
+ if (scroll) //potential need to scroll?
+ {
+ switch (song->follow())
+ {
+ case Song::NO: break;
+ case Song::JUMP: goto_tick(tick,false); break;
+ case Song::CONTINUOUS: goto_tick(tick,true); break;
+ }
+ }
+
+ if (need_redraw_for_hilighting())
+ redraw();
+ }
+}
+
+
+void ScoreCanvas::recalc_staff_pos()
+{
+ int y=0;
+
+ for (list<staff_t>::iterator it=staffs.begin(); it!=staffs.end(); it++)
+ {
+ it->y_top=y;
+ switch (it->type)
+ {
+ case NORMAL:
+ it->y_draw = it->y_top + STAFF_DISTANCE/2;
+ it->y_bottom = it->y_draw + STAFF_DISTANCE/2;
+ break;
+ case GRAND_TOP:
+ it->y_draw = it->y_top + STAFF_DISTANCE/2;
+ it->y_bottom = it->y_draw + GRANDSTAFF_DISTANCE/2;
+ break;
+ case GRAND_BOTTOM:
+ it->y_draw = it->y_top + GRANDSTAFF_DISTANCE/2;
+ it->y_bottom = it->y_draw + STAFF_DISTANCE/2;
+ break;
+ default:
+ cout << "THIS SHOULD NEVER HAPPEN: invalid staff type!" << endl;
+ }
+ y=it->y_bottom;
+ }
+}
+
+list<staff_t>::iterator ScoreCanvas::staff_at_y(int y)
+{
+ for (list<staff_t>::iterator it=staffs.begin(); it!=staffs.end(); it++)
+ if ((y >= it->y_top) && (y < it->y_bottom))
+ return it;
+
+ return staffs.end();
+}
+
+//the following assertions are made:
+// pix_quarter.width() == pix_half.width()
+
+
+// pix->width()-1 + 1/2*pix->width() + SHIFT + ADD_SPACE
+// 10-1+5+3+3=20 <- um so viel wird der taktstrich verschoben
+// um das doppelte (20*2=40) werden die kleinsten schläge gegeneinander versetzt
+
+
+
+
+
+//hint: recalculating event- and itemlists "from zero"
+// could happen in realtime, as it is pretty fast.
+// however, this adds unneccessary cpu usage.
+// it is NO problem to recalc the stuff "from zero"
+// every time something changes.
+
+
+/* BUGS and potential bugs
+ * o updating the keymap doesn't emit a songChanged() signal!
+ * o when the keymap is not used, this will probably lead to a bug
+ * o when adding a note, it's added to the first stave
+ * the problem is: there's always the first part selected
+ * o when changing color of a displayed part, note heads aren't redrawn
+ * o when pressing "STOP", the active note isn't redrawn "normally"
+ *
+ * CURRENT TODO
+ * o y-scroll for staff window, with automatic margin-scrolling
+ * o let the user edit the score's name
+ *
+ * IMPORTANT TODO
+ * o removing the part the score's working on isn't handled
+ * o let the user select the currently edited part
+ * o let the user select between "colors after the parts",
+ * "colors after selected/unselected part" and "all black"
+ * o support selections
+ * o emit a "song-changed" signal instead of calling our
+ * internal song_changed() function
+ * o check if "moving away" works for whole notes [seems to NOT work properly]
+ *
+ * less important stuff
+ * o must add_parts() update the part-list?
+ * o support different keys in different tracks at the same time
+ * calc_pos_add_list and calc_item_pos will be affected by this
+ * calc_pos_add_list must be called before calc_item_pos then,
+ * and calc_item_pos must respect the pos_add_list instead of
+ * keeping its own pos_add variable (which is only an optimisation)
+ * o use nearest part instead of curr_part, maybe expand
+ * o draw measure numbers
+ * o use "unsigned" whereever "unsigned" is meant
+ * o when moving or resizing a note, so that its end is out-of-part,
+ * there's strange behaviour
+ * o redraw is called too often
+ * for example, when scroll is continuous, and note-hilighting has
+ * changed, redraw() is called twice
+ * o song_changed() should distinguish between relevant and
+ * irrelevant changes. (for example controllers can be ignored)
+ * o ties aren't always drawn correctly when the destination note
+ * is out of view
+ * o tied notes don't work properly when there's a key-change in
+ * between, for example, when a cis is tied to a des
+ * o display only the part, not the whole song filled with rests?
+ * o let the user select whether the preamble should have
+ * a fixed length (?)
+ * o let the user select what the preamble has to contain
+ * > o use timesig_t in all timesig-stuff
+ * o draw a margin around notes which are in a bright color
+ * o maybe override color 0 with "black"?
+ * o use bars instead of flags over groups of 8ths / 16ths etc
+ * o (change ItemList into map< pos_t , mutable_stuff_t >) [no]
+ * o deal with expanding parts or clip (expanding is better)
+ * o check if making the program clef-aware hasn't broken anything
+ * e.g. accidentials, creating notes, rendering etc.
+ * o check if the new function for drawing accidential works
+ * o refuse to resize so that width gets smaller or equal than x_left
+ * o set distances properly [looks okay, doesn't it?]
+ * o change iterators into const iterators
+ * o add tracks in correct order to score
+ * o rename staffs to staves
+ *
+ * stuff for the other muse developers
+ * o check if dragging notes is done correctly
+ * o after doing the undo stuff right, the "pianoroll isn't informed
+ * about score-editor's changes"-bug has vanished. did it vanish
+ * "by accident", or is that the correct solution for this?
+ * o process accurate timesignatures from muse's list (has to be implemented first in muse)
+ * ( (2+2+3)/4 or (3+2+2)/4 instead of 7/4 )
+ * o maybe do expanding parts inside the msgChangeEvent or
+ * msgNewEvent functions (see my e-mail)
+ *
+ * GUI stuff
+ * o offer a button for bool mouse_erases_notes and mouse_inserts_notes
+ * o offer dropdown-boxes for lengths of the inserted note
+ * (select between 16th, 8th, ... whole and "last used length")
+ * o offer a dropdown-box for the clef to use
+ * o offer some way to setup the colorizing method to be used
+ */
+
+
+
+/* how to use the score editor with multiple tracks
+ * ================================================
+ *
+ * select parts, right-click, "display in new score window" or "display per-track in new score window"
+ * or "display in existing window -> 1,2,3,4" or "display per-track in existing..."
+ *
+ * ScoreCanvas has a list of note systems, consisting of the following:
+ * * all parts included in that view
+ * * eventlist, itemlist
+ * * used clef, transposing/octave settings
+ * * enum { NOT_GROUPED, I_AM_TOP, I_AM_BOTTOM } group_state
+ * NOT_GROUPED means "single note system"
+ * I_AM_TOP and I_AM_BOTTOM mean that the two systems belong
+ * together
+ *
+ * when redrawing, we iterate through all systems.
+ * we add a distance according to group_state
+ * then we draw the system. if group_state is I_AM_BOTTOM, we
+ * draw our beams longer/higher, and we draw a bracket
+ *
+ * when clicking around, we first determine which system has been clicked in
+ * (the systems have enough space in between, so there won't be notes
+ * from sys1 in sys2. if there are, they're ignored for simplicity)
+ * then we proceed as usual (adding, removing, changing notes)
+ *
+ *
+ * pos_add_list stays the same for each staff, so we only need one
+ */
+
+/* R O A D M A P
+ * =============
+ *
+ * 1. finish the score editor, without transposing instruments and
+ * with only a global keymap
+ *
+ * REASON: a score editor with few functions is better than
+ * no score editor at all
+ *
+ *
+ * 2. support transposing by octave-steps
+ *
+ * REASON: the main problem with transposing is, that the
+ * editor needs different key signatures and needs
+ * to align them against each other. this problem
+ * doesn't exist when only transposing by octaves
+ *
+ *
+ * 3. support transposing instruments, but only one
+ * transposing-setting per score window. that is, you won't be
+ * able to display your (C-)strings in the same window as your
+ * B-trumpet. this will be very easy to implement
+ *
+ * REASON: the above problem still exists, but is circumvented
+ * by simply not having to align them against each other
+ * (because they're in different windows)
+ *
+ *
+ * 4. support different transposing instruments in the same score
+ * window. this will be some hassle, because we need to align
+ * the scores properly. for example, when the C-violin has
+ * C-major (no accidentials), then the B-trumpet need some
+ * accidentials. we now must align the staves so that the
+ * "note-after-keychange"s of both staves are again at the
+ * same x-position
+ *
+ * REASON: some solution for that problem must be written.
+ * this is a large step, which atm isn't very important
+ *
+ *
+ * 5. support different keys per track. this wouldn't be that
+ * hard, when 4) is already done; because we then already have
+ * the "align it properly" functionality, and can use it
+ *
+ * REASON: this is only a nice-to-have, which can however be
+ * easily implemented when 4) is done
+ */
diff --git a/muse2/muse/midiedit/scoreedit.h b/muse2/muse/midiedit/scoreedit.h
index 767dc463..cc1a06ec 100644
--- a/muse2/muse/midiedit/scoreedit.h
+++ b/muse2/muse/midiedit/scoreedit.h
@@ -1,8 +1,8 @@
//=========================================================
// MusE
// Linux Music Editor
-// $Id: pianoroll.h,v 1.5.2.4 2009/11/16 11:29:33 lunar_shuttle Exp $
-// (C) Copyright 1999 Werner Schweer (ws@seh.de)
+// scoreedit.h
+// (C) Copyright 2011 Florian Jung (florian.a.jung@web.de)
//=========================================================
#ifndef __SCOREEDIT_H__
@@ -12,6 +12,10 @@
#include <QResizeEvent>
#include <QLabel>
#include <QKeyEvent>
+#include <QPainter>
+#include <QPixmap>
+#include <QTimer>
+#include <QScrollBar>
#include <values.h>
#include "noteinfo.h"
@@ -19,183 +23,596 @@
#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;
+
+
+
+
+
+class ScoreCanvas;
//---------------------------------------------------------
-// PianoRoll
+// ScoreEdit
//---------------------------------------------------------
-class ScoreEdit : public MidiEditor {
- Event selEvent;
- MidiPart* selPart;
- int selTick;
-
- //enum { CMD_EVENT_COLOR, CMD_CONFIG_QUANT, CMD_LAST };
- //int menu_ids[CMD_LAST];
- //Q3PopupMenu *menuEdit, *menuFunctions, *menuSelect, *menuConfig, *menuPlugins;
-
-
- QMenu *menuEdit, *menuFunctions, *menuSelect, *menuConfig, *eventColor, *menuPlugins;
- MidiTrackInfo *midiTrackInfo;
- Track* selected;
-
- QAction* editCutAction;
- QAction* editCopyAction;
- QAction* editPasteAction;
- QAction* editDelEventsAction;
-
- QAction* selectAllAction;
- QAction* selectNoneAction;
- QAction* selectInvertAction;
- QAction* selectInsideLoopAction;
- QAction* selectOutsideLoopAction;
- QAction* selectPrevPartAction;
- QAction* selectNextPartAction;
-
- QAction* evColorBlueAction;
- QAction* evColorPitchAction;
- QAction* evColorVelAction;
-
- QAction* funcOverQuantAction;
- QAction* funcNoteOnQuantAction;
- QAction* funcNoteOnOffQuantAction;
- QAction* funcIterQuantAction;
- QAction* funcConfigQuantAction;
- QAction* funcGateTimeAction;
- QAction* funcModVelAction;
- QAction* funcCrescendoAction;
- QAction* funcTransposeAction;
- QAction* funcThinOutAction;
- QAction* funcEraseEventAction;
- QAction* funcNoteShiftAction;
- QAction* funcMoveClockAction;
- QAction* funcCopyMeasureAction;
- QAction* funcEraseMeasureAction;
- QAction* funcDelMeasureAction;
- QAction* funcCreateMeasureAction;
- QAction* funcSetFixedLenAction;
- QAction* funcDelOverlapsAction;
-
-
- int tickOffset;
- int lenOffset;
- int pitchOffset;
- int veloOnOffset;
- int veloOffOffset;
- bool deltaMode;
-
- NoteInfo* info;
- QToolButton* srec;
- QToolButton* midiin;
-
- Toolbar1* toolbar;
- Splitter* splitter;
- Splitter* hsplitter;
- Splitter* ctrlLane;
-
- QToolButton* speaker;
- QToolBar* tools;
- EditToolBar* tools2;
-
- int colorMode;
-
- static int _quantInit, _rasterInit;
- static int _widthInit, _heightInit;
-
- static int _quantStrengthInit;
- static int _quantLimitInit;
- static bool _quantLenInit;
- static int _toInit;
- static int colorModeInit;
-
- int _quantStrength;
- int _quantLimit;
- int _to;
- bool _quantLen;
- QuantConfig* quantConfig;
- bool _playEvents;
-
- //QScrollBar* infoScroll;
- QScrollArea* infoScroll;
-
- Q_OBJECT
- void initShortcuts();
- void setEventColorMode(int);
- QWidget* genToolbar(QWidget* parent);
- virtual void closeEvent(QCloseEvent*);
- virtual void keyPressEvent(QKeyEvent*);
- virtual void resizeEvent(QResizeEvent*);
-
- private slots:
- void setSelection(int, Event&, Part*);
- void noteinfoChanged(NoteInfo::ValType, int);
- //CtrlEdit* addCtrl();
- void removeCtrl(CtrlEdit* ctrl);
- void soloChanged(bool flag);
- //void trackInfoScroll(int);
- void setRaster(int);
- void setQuant(int);
- void configQuant();
- void setQuantStrength(int val) { _quantStrength = val; }
- void setQuantLimit(int val) { _quantLimit = val; }
- void setQuantLen(bool val) { _quantLen = val; }
- void cmd(int);
- void setSteprec(bool);
- void setTo(int val) { _to = val; }
- void eventColorModeChanged(int);
- void clipboardChanged(); // enable/disable "Paste"
- void selectionChanged(); // enable/disable "Copy" & "Paste"
- void setSpeaker(bool);
- void setTime(unsigned);
- void follow(int pos);
- void songChanged1(int);
- void configChanged();
- void newCanvasWidth(int);
- void toggleTrackInfo();
- void updateTrackInfo();
-
- signals:
- void deleted(unsigned long);
-
+class ScoreEdit : public MidiEditor
+{
+ Q_OBJECT
+
+ private:
+ virtual void closeEvent(QCloseEvent*);
+
+ QScrollBar* hscroll;
+ ScoreCanvas* score_canvas;
+
+ static int serial;
+ static set<string> names;
+
+ string name;
+
+ bool set_name(string newname, bool emit_signal=true, bool emergency_name=false);
+ private slots:
+
+
+ signals:
+ void deleted(unsigned long);
+ void name_changed();
+
+ public slots:
+ void canvas_width_changed(int);
+ void viewport_width_changed(int);
+
+ public:
+ ScoreEdit(PartList*, QWidget* parent = 0, const char* name = 0, unsigned initPos = MAXINT);
+ ~ScoreEdit();
+ static void readConfiguration(Xml&){}; //TODO does nothing
+ static void writeConfiguration(int, Xml&){}; //TODO does nothing
+
+ void add_parts(PartList* pl, bool all_in_one=false);
+ string get_name() { return name; }
+ };
+
+
+
+
+
+
+enum 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;
+ }
+};
+
+bool operator< (const note_len_t& a,const note_len_t& b);
+
+struct cumulative_t
+{
+ int count;
+ int cumul;
+
+ cumulative_t()
+ {
+ count=0;
+ cumul=0;
+ }
+
+ void add(int v)
+ {
+ count++;
+ cumul+=v;
+ }
+
+ float mean()
+ {
+ return (float)cumul/count;
+ }
+};
+
+#define BLACK_PIXMAP (NUM_PARTCOLORS)
+#define HIGHLIGHTED_PIXMAP (NUM_PARTCOLORS+1)
+
+struct timesig_t
+{
+ int num;
+ int denom;
+};
+
+enum staff_type_t
+{
+ NORMAL,
+ GRAND_TOP,
+ GRAND_BOTTOM
+};
+
+enum staff_mode_t
+{
+ MODE_TREBLE,
+ MODE_BASS,
+ MODE_BOTH
+};
+
+struct staff_t
+{
+ set<Part*> parts;
+ ScoreEventList eventlist;
+ ScoreItemList itemlist;
+
+ int y_top;
+ int y_draw;
+ int y_bottom;
+
+ staff_type_t type;
+ clef_t clef;
+ int split_note;
+
+ void create_appropriate_eventlist(const set<Part*>& parts);
+ void create_itemlist();
+ void process_itemlist();
+ void calc_item_pos();
+
+ staff_t()
+ {
+ type=NORMAL;
+ clef=VIOLIN;
+ }
+
+ staff_t (staff_type_t type_, clef_t clef_, set<Part*> parts_, int split_note_=0)
+ {
+ type=type_;
+ clef=clef_;
+ split_note=split_note_;
+ parts=parts_;
+ }
+};
+
+list<int> calc_accidentials(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, bool allow_dots=true, bool allow_normal=true);
+
+int clef_height(clef_t clef);
+
+
+int calc_timesig_width(int num, int denom);
+int calc_number_width(int n);
+
+
+class ScoreCanvas : public View
+{
+ Q_OBJECT
+ private:
+ static void load_pixmaps();
+ static void draw_pixmap(QPainter& p, int x, int y, const QPixmap& pm);
+ static void draw_tie (QPainter& p, int x1, int x4, int yo, bool up=true, QColor color=Qt::black);
+
+ static void draw_accidentials(QPainter& p, int x, int y_offset, const list<int>& acc_list, const QPixmap& pix);
+
+ static void draw_timesig(QPainter& p, int x, int y_offset, int num, int denom);
+
+ static void draw_number(QPainter& p, int x, int y, int n);
+
+
+
+
+
+ static int height_to_pitch(int h, clef_t clef, 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();
+
+ int canvas_width();
+ int viewport_width();
+
+
+ void set_staffmode(list<staff_t>::iterator it, staff_mode_t mode);
+ void remove_staff(list<staff_t>::iterator it);
+ void merge_staves(list<staff_t>::iterator dest, list<staff_t>::iterator src);
+
+// member variables ---------------------------------------------------
+
+ std::map<int,int> pos_add_list;
+
+ list<staff_t> staffs;
+
+ // the drawing area is split into a "preamble" containing clef,
+ // key and time signature, and the "item's area" containing the
+ // actual items (notes, bars, rests, etc.)
+ // x_pos is responsible for scrolling. an item with item->x==x_pos
+ // will be drawn exactly at the left beginning of the item's area
+ // x_left could also be called "preamble's width". it defines
+ // where the item's area begins
+ // when multiple note systems are drawn into one window, the
+ // preamble's length is the same for each system
+ int x_pos;
+ int x_left;
+
+ //for mouse-scrolling
+ float scroll_speed;
+ float scroll_pos;
+
+ Part* curr_part;
+ int last_len;
+ int new_len; //when zero or negative, last_len is used
+
+ QPoint mouse_down_pos;
+ bool mouse_down;
+ enum operation_t
+ {
+ NO_OP=0,
+ BEGIN=1,
+ LENGTH=2,
+ PITCH=3
+ };
+ operation_t mouse_operation;
+ operation_t mouse_x_drag_operation;
+ bool mouse_erases_notes;
+ bool mouse_inserts_notes;
+
+ bool dragging;
+ Part* dragged_event_part;
+ Event dragged_event;
+ int dragged_event_original_pitch;
+
+
+
+
+
+ //menu stuff
+ QAction* staffmode_treble_action;
+ QAction* staffmode_bass_action;
+ QAction* staffmode_both_action;
+ QAction* remove_staff_action;
+
+ QMenu* staff_menu;
+ list<staff_t>::iterator current_staff;
+ bool dragging_staff;
+
+
+ private slots:
+ void staffmode_treble_slot();
+ void staffmode_bass_slot();
+ void staffmode_both_slot();
+ void remove_staff_slot();
+
public slots:
- virtual void updateHScrollRange();
- void execDeliveredScript(int id);
- void execUserScript(int id);
- CtrlEdit* addCtrl();
-
- public:
- ScoreEdit(PartList*, QWidget* parent = 0, const char* name = 0, unsigned initPos = MAXINT);
- ~ScoreEdit();
- virtual void readStatus(Xml&);
- virtual void writeStatus(int, Xml&) const;
- static void readConfiguration(Xml&);
- static void writeConfiguration(int, Xml&);
- };
+ void scroll_event(int);
+ void song_changed(int);
+ void goto_tick(int,bool);
+ void pos_changed(int i, unsigned u, bool b);
+ void heartbeat_timer_event();
+
+ signals:
+ void xpos_changed(int);
+ void viewport_width_changed(int);
+ void canvas_width_changed(int);
+
+ protected:
+ virtual void draw(QPainter& p, const QRect& rect);
+ MidiEditor* editor;
+
+ virtual void mousePressEvent (QMouseEvent* event);
+ virtual void mouseMoveEvent (QMouseEvent* event);
+ virtual void mouseReleaseEvent (QMouseEvent* event);
+ virtual void resizeEvent(QResizeEvent*);
+
+ public:
+ ScoreCanvas(MidiEditor*, QWidget*, int, int);
+ ~ScoreCanvas(){};
+
+ void add_staves(PartList* pl, bool all_in_one);
+
+};
+
+int calc_measure_len(const list<int>& nums, int denom);
+vector<int> create_emphasize_list(const list<int>& nums, int denom);
+vector<int> create_emphasize_list(int num, int denom);
+
#endif