1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
2 
3 /*
4     Rosegarden
5     A MIDI and audio sequencer and musical notation editor.
6     Copyright 2000-2021 the Rosegarden development team.
7 
8     Other copyrights also apply to some parts of this work.  Please
9     see the AUTHORS file and individual file headers for details.
10 
11     This program is free software; you can redistribute it and/or
12     modify it under the terms of the GNU General Public License as
13     published by the Free Software Foundation; either version 2 of the
14     License, or (at your option) any later version.  See the file
15     COPYING included with this distribution for more information.
16 */
17 
18 #define RG_MODULE_STRING "[EraseCommand]"
19 
20 #include "EraseCommand.h"
21 
22 #include "misc/Debug.h"
23 #include "base/NotationTypes.h"
24 #include "base/Selection.h"
25 #include "base/BaseProperties.h"
26 #include "document/BasicSelectionCommand.h"
27 #include "gui/editors/notation/NotationProperties.h"
28 
29 #include <QString>
30 
31 
32 namespace Rosegarden
33 {
34 
35 
36 EraseCommand::EraseCommand(EventSelection &selection) :
37         BasicSelectionCommand(getGlobalName(), selection, true),
38         m_selection(&selection),
39         m_relayoutEndTime(getEndTime())
40 {
41     // nothing else
42 }
43 
44 EraseCommand::~EraseCommand()
45 {
46     // ??? MEMORY LEAK (confirmed)  delete here isn't a reliable solution.
47     //delete m_selection;
48 }
49 
50 void
51 EraseCommand::modifySegment()
52 {
53     bool needRelayOut = eraseInSegment(m_selection);
54     if (needRelayOut)
55         { m_relayoutEndTime = getSegment().getEndTime(); }
56 }
57 
58 // Erase the events in segment that are in selection.
59 // @return
60 // whether any deletions that affect later in the segment were done,
61 // meaning key or clef deletions.
62 bool
63 EraseCommand::eraseInSegment(EventSelection *selection)
64 {
65     RG_DEBUG << "EraseCommand::eraseInSegment";
66     timeT startTime  = selection->getStartTime();
67     timeT endTime    = selection->getEndTime();
68     Segment &segment = selection->getSegment();
69 
70     bool erasedLongEffectEvent = false;
71 
72     std::vector<Event *> toErase;
73     EventSelection::eventcontainer::iterator i;
74 
75     for (i = selection->getSegmentEvents().begin();
76             i != selection->getSegmentEvents().end(); ++i) {
77 
78         if ((*i)->isa(Clef::EventType) ||
79                 (*i)->isa(Key ::EventType)) {
80             erasedLongEffectEvent = true;
81         } else if ((*i)->isa(Indication::EventType)) {
82 
83             try {
84                 int graceToAdjust = 0;
85                 int minGraceSubOrdering = 0;
86                 int maxDeltaGraceSubOrdering = 0;
87                 int indicationSubOrdering = (*i)->getSubOrdering();
88                 int minSubOrdering = 0;
89 
90                 // Adjust suborderings of any existing grace notes if necessary.
91 
92                 Segment::iterator h, j;
93                 segment.getTimeSlice((*i)->getAbsoluteTime(), h, j);
94                 for (Segment::iterator k = h; k != j; ++k) {
95                     if ((*k)->has(BaseProperties::IS_GRACE_NOTE)) {
96                         if ((*k)->getSubOrdering() < indicationSubOrdering) {
97                             ++graceToAdjust;
98                             if ((*k)->getSubOrdering() < minGraceSubOrdering) {
99                                 minGraceSubOrdering = (*k)->getSubOrdering();
100                                 maxDeltaGraceSubOrdering =
101                                     indicationSubOrdering - minGraceSubOrdering;
102                             }
103                         }
104                     } else if ((*i) != (*k) &&
105                                (*k)->getSubOrdering() < minSubOrdering) {
106                         minSubOrdering = (*k)->getSubOrdering();
107                     }
108                 }
109 
110                 if (graceToAdjust > 0 &&
111                     minGraceSubOrdering < indicationSubOrdering &&
112                     minSubOrdering > indicationSubOrdering &&
113                     maxDeltaGraceSubOrdering >= graceToAdjust) {
114                     int incr = minSubOrdering - indicationSubOrdering;
115                     std::vector<Event *> toInsert, toErase;
116                     for (Segment::iterator k = h; k != j; ++k) {
117                         if ((*k)->has(BaseProperties::IS_GRACE_NOTE) &&
118                             (*k)->getSubOrdering() < indicationSubOrdering) {
119                             // Subordering of the grace note is incremented to
120                             // avoid (a rare) relevant decrement of that value.
121                             toErase.push_back(*k);
122                             toInsert.push_back
123                                 (new Event(**k,
124                                            (*k)->getAbsoluteTime(),
125                                            (*k)->getDuration(),
126                                            (*k)->getSubOrdering() + incr,
127                                            (*k)->getNotationAbsoluteTime(),
128                                            (*k)->getNotationDuration()));
129                         }
130                     }
131                     for (std::vector<Event *>::iterator k = toErase.begin();
132                          k != toErase.end(); ++k) segment.eraseSingle(*k);
133                     for (std::vector<Event *>::iterator k = toInsert.begin();
134                          k != toInsert.end(); ++k) segment.insert(*k);
135                 }
136 
137                 Indication indication(**i);
138                 if (indication.isOttavaType()) {
139 
140                     for (Segment::iterator j = segment.findTime ((*i)->getAbsoluteTime());
141                          j != segment.findTime
142                              ((*i)->getAbsoluteTime() + indication.getIndicationDuration());
143                          ++j) {
144                         (*j)->unset(NotationProperties::OTTAVA_SHIFT);
145                     }
146                 }
147             } catch (...) {}
148         }
149 
150         // We used to do this by calling SegmentNotationHelper::deleteEvent
151         // on each event in the selection, but it's probably easier to
152         // cope with general selections by deleting everything in the
153         // selection and then normalizing the rests.  The deleteEvent
154         // mechanism is still the more sensitive way to do it for single
155         // events, and it's what's used by EraseEventCommand and thus
156         // the notation eraser tool.
157 
158         toErase.push_back(*i);
159     }
160 
161     for (size_t j = 0; j < toErase.size(); ++j) {
162         segment.eraseSingle(toErase[j]);
163     }
164 
165     segment.normalizeRests(startTime, endTime);
166     return erasedLongEffectEvent;
167 }
168 
169 timeT
170 EraseCommand::getRelayoutEndTime()
171 {
172     return m_relayoutEndTime;
173 }
174 
175 }
176