summaryrefslogtreecommitdiffstats
path: root/src/commands/notation/NoteInsertionCommand.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/commands/notation/NoteInsertionCommand.cpp')
-rw-r--r--src/commands/notation/NoteInsertionCommand.cpp296
1 files changed, 296 insertions, 0 deletions
diff --git a/src/commands/notation/NoteInsertionCommand.cpp b/src/commands/notation/NoteInsertionCommand.cpp
new file mode 100644
index 0000000..cadae55
--- /dev/null
+++ b/src/commands/notation/NoteInsertionCommand.cpp
@@ -0,0 +1,296 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "NoteInsertionCommand.h"
+
+#include <cmath>
+#include <klocale.h>
+#include "base/Event.h"
+#include "base/NotationTypes.h"
+#include "base/Segment.h"
+#include "base/SegmentMatrixHelper.h"
+#include "base/SegmentNotationHelper.h"
+#include "document/BasicCommand.h"
+#include "gui/editors/notation/NotationProperties.h"
+#include "gui/editors/notation/NoteStyleFactory.h"
+#include "base/BaseProperties.h"
+
+
+namespace Rosegarden
+{
+
+using namespace BaseProperties;
+
+NoteInsertionCommand::NoteInsertionCommand(Segment &segment, timeT time,
+ timeT endTime, Note note, int pitch,
+ Accidental accidental,
+ AutoBeamMode autoBeam,
+ MatrixMode matrixType,
+ GraceMode grace,
+ float targetSubordering,
+ NoteStyleName noteStyle) :
+ BasicCommand(i18n("Insert Note"), segment,
+ getModificationStartTime(segment, time),
+ (autoBeam ? segment.getBarEndForTime(endTime) : endTime)),
+ m_insertionTime(time),
+ m_note(note),
+ m_pitch(pitch),
+ m_accidental(accidental),
+ m_autoBeam(autoBeam == AutoBeamOn),
+ m_matrixType(matrixType == MatrixModeOn),
+ m_grace(grace),
+ m_targetSubordering(targetSubordering),
+ m_noteStyle(noteStyle),
+ m_lastInsertedEvent(0)
+{
+ // nothing
+}
+
+NoteInsertionCommand::~NoteInsertionCommand()
+{
+ // nothing
+}
+
+timeT
+NoteInsertionCommand::getModificationStartTime(Segment &segment,
+ timeT time)
+{
+ // We may be splitting a rest to insert this note, so we'll have
+ // to record the change from the start of that rest rather than
+ // just the start of the note
+
+ timeT barTime = segment.getBarStartForTime(time);
+ Segment::iterator i = segment.findNearestTime(time);
+
+ if (i != segment.end() &&
+ (*i)->getAbsoluteTime() < time &&
+ (*i)->getAbsoluteTime() + (*i)->getDuration() > time &&
+ (*i)->isa(Note::EventRestType)) {
+ return std::min(barTime, (*i)->getAbsoluteTime());
+ }
+
+ return barTime;
+}
+
+void
+NoteInsertionCommand::modifySegment()
+{
+ Segment &segment(getSegment());
+ SegmentNotationHelper helper(segment);
+ Segment::iterator i, j;
+
+ // insert via a model event, so as to apply the note style
+
+ // subordering is always negative for these insertions; round it down
+ int actualSubordering = lrintf(floorf(m_targetSubordering + 0.01));
+ if ((m_grace != GraceModeOff) && actualSubordering >= 0) {
+ actualSubordering = -1;
+ }
+
+ // this is true if the subordering is "more or less" an integer,
+ // as opposed to something like -0.5
+ bool suborderingExact = (actualSubordering !=
+ (lrintf(floorf(m_targetSubordering - 0.01))));
+
+ std::cerr << "actualSubordering = " << actualSubordering
+ << " suborderingExact = " << suborderingExact << std::endl;
+
+ Event *e;
+
+ if (m_grace == GraceModeOff) {
+
+ e = new Event
+ (Note::EventType,
+ m_insertionTime,
+ m_note.getDuration(),
+ 0,
+ m_insertionTime,
+ m_note.getDuration());
+
+ } else {
+
+ e = new Event
+ (Note::EventType,
+ m_insertionTime,
+ 0,
+ actualSubordering == 0 ? -1 : actualSubordering,
+ m_insertionTime,
+ m_note.getDuration());
+ }
+
+ e->set<Int>(PITCH, m_pitch);
+ e->set<Int>(VELOCITY, 100);
+
+ if (m_accidental != Accidentals::NoAccidental) {
+ e->set<String>(ACCIDENTAL, m_accidental);
+ }
+
+ if (m_noteStyle != NoteStyleFactory::DefaultStyle) {
+ e->set<String>(NotationProperties::NOTE_STYLE, m_noteStyle);
+ }
+
+ if (m_grace != GraceModeOff) {
+
+ if (!suborderingExact) {
+
+ // Adjust suborderings of any existing grace notes, if there
+ // is at least one with the same subordering and
+ // suborderingExact is not set
+
+ segment.getTimeSlice(m_insertionTime, i, j);
+ bool collision = false;
+ for (Segment::iterator k = i; k != j; ++k) {
+ if ((*k)->getSubOrdering() == actualSubordering) {
+ collision = true;
+ break;
+ }
+ }
+
+ if (collision) {
+ std::vector<Event *> toInsert, toErase;
+ for (Segment::iterator k = i; k != j; ++k) {
+ if ((*k)->isa(Note::EventType) &&
+ (*k)->getSubOrdering() <= actualSubordering) {
+ toErase.push_back(*k);
+ toInsert.push_back
+ (new Event(**k,
+ (*k)->getAbsoluteTime(),
+ (*k)->getDuration(),
+ (*k)->getSubOrdering() - 1,
+ (*k)->getNotationAbsoluteTime(),
+ (*k)->getNotationDuration()));
+ }
+ }
+ for (std::vector<Event *>::iterator k = toErase.begin();
+ k != toErase.end(); ++k) segment.eraseSingle(*k);
+ for (std::vector<Event *>::iterator k = toInsert.begin();
+ k != toInsert.end(); ++k) segment.insert(*k);
+ }
+ }
+
+ e->set<Bool>(IS_GRACE_NOTE, true);
+ i = segment.insert(e);
+
+ Segment::iterator k;
+ segment.getTimeSlice(m_insertionTime, j, k);
+ Segment::iterator bg0 = segment.end(), bg1 = segment.end();
+ while (j != k) {
+ std::cerr << "testing for truthiness: time " << (*j)->getAbsoluteTime() << ", subordering " << (*j)->getSubOrdering() << std::endl;
+ if ((*j)->isa(Note::EventType) &&
+ (*j)->getSubOrdering() < 0 &&
+ (*j)->has(IS_GRACE_NOTE) &&
+ (*j)->get<Bool>(IS_GRACE_NOTE)) {
+ std::cerr << "truthiful" << std::endl;
+ if (bg0 == segment.end()) bg0 = j;
+ bg1 = j;
+ }
+ ++j;
+ }
+
+ if (bg0 != segment.end() && bg1 != bg0) {
+ if (bg1 != segment.end()) ++bg1;
+ int count = 0;
+ int pso = 0;
+ for (Segment::iterator i = bg0; i != bg1; ++i) {
+ (*i)->unset(BEAMED_GROUP_ID);
+ (*i)->unset(BEAMED_GROUP_TYPE);
+ (*i)->unset(BEAMED_GROUP_TUPLED_COUNT);
+ (*i)->unset(BEAMED_GROUP_UNTUPLED_COUNT);
+ if ((*i)->getSubOrdering() != pso) {
+ ++count;
+ pso = (*i)->getSubOrdering();
+ }
+ }
+ if (m_grace == GraceAndTripletModesOn) {
+ helper.makeBeamedGroupExact(bg0, bg1, GROUP_TYPE_TUPLED);
+ if (count > 1) {
+ for (Segment::iterator i = bg0; i != bg1; ++i) {
+ (*i)->set<Int>(BEAMED_GROUP_TUPLED_COUNT, count-1);
+ (*i)->set<Int>(BEAMED_GROUP_UNTUPLED_COUNT, count);
+ }
+ }
+ } else {
+ helper.makeBeamedGroupExact(bg0, bg1, GROUP_TYPE_BEAMED);
+ }
+ }
+
+ } else {
+
+ // If we're attempting to insert at the same time and pitch as
+ // an existing note, then we remove the existing note first
+ // (so as to change its duration, if the durations differ)
+ segment.getTimeSlice(m_insertionTime, i, j);
+ while (i != j) {
+ if ((*i)->isa(Note::EventType)) {
+ long pitch;
+ if ((*i)->get<Int>(PITCH, pitch) && pitch == m_pitch) {
+ helper.deleteNote(*i);
+ break;
+ }
+ }
+ ++i;
+ }
+
+ if (m_matrixType) {
+ i = SegmentMatrixHelper(segment).insertNote(e);
+ } else {
+ i = helper.insertNote(e);
+ // e is just a model for SegmentNotationHelper::insertNote
+ delete e;
+ }
+ }
+
+ if (i != segment.end()) m_lastInsertedEvent = *i;
+
+ if (m_autoBeam) {
+
+ // We auto-beam the bar if it contains no beamed groups
+ // after the insertion point and if it contains no tupled
+ // groups at all.
+
+ timeT barStartTime = segment.getBarStartForTime(m_insertionTime);
+ timeT barEndTime = segment.getBarEndForTime(m_insertionTime);
+
+ for (Segment::iterator j = i;
+ j != segment.end() && (*j)->getAbsoluteTime() < barEndTime;
+ ++j) {
+ if ((*j)->has(BEAMED_GROUP_ID))
+ return ;
+ }
+
+ for (Segment::iterator j = i;
+ j != segment.end() && (*j)->getAbsoluteTime() >= barStartTime;
+ --j) {
+ if ((*j)->has(BEAMED_GROUP_TUPLET_BASE))
+ return ;
+ if (j == segment.begin())
+ break;
+ }
+
+ helper.autoBeam(m_insertionTime, m_insertionTime, GROUP_TYPE_BEAMED);
+ }
+}
+
+}