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 "[PasteEventsCommand]"
19 #define RG_NO_DEBUG_PRINT 1
20 
21 #include "PasteEventsCommand.h"
22 
23 #include "misc/Debug.h"
24 #include "base/Clipboard.h"
25 #include "base/Event.h"
26 #include "base/NotationTypes.h"
27 #include "base/Segment.h"
28 #include "base/SegmentNotationHelper.h"
29 #include "document/BasicCommand.h"
30 #include <QString>
31 #include "base/BaseProperties.h"
MoveAcrossSegmentsCommand(Segment &,Segment & secondSegment,timeT newStartTime,bool notation,EventSelection & selection)32 
33 
34 namespace Rosegarden
35 {
36 
37 using namespace BaseProperties;
38 
39 PasteEventsCommand::PasteEventsCommand(Segment &segment,
40                                        Clipboard *clipboard,
41                                        timeT pasteTime,
42                                        PasteType pasteType) :
43     BasicCommand(getGlobalName(), segment, pasteTime,
44                  getEffectiveEndTime(segment, clipboard, pasteTime)),
45     m_relayoutEndTime(getEndTime()),
46     m_clipboard(new Clipboard(*clipboard)),
47     m_pasteType(pasteType)
48 {
49     if (pasteType != OpenAndPaste) {
50 
51         // paste clef or key -> relayout to end
52 
53         if (clipboard->isSingleSegment()) {
54 
55             Segment *s(clipboard->getSingleSegment());
56             for (Segment::iterator i = s->begin(); i != s->end(); ++i) {
57                 if ((*i)->isa(Clef::EventType) ||
58                     (*i)->isa(Key::EventType)) {
59                     m_relayoutEndTime = s->getEndTime();
60                     break;
61                 }
62             }
63         }
64     }
65 }
66 
67 PasteEventsCommand::PasteEventsCommand(const QString& marking,
68                                        Clipboard *clipboard,
69                                        timeT pasteTime,
70                                        PasteType pasteType) :
71     BasicCommand(getGlobalName(), pasteTime, marking),
72     m_relayoutEndTime(getEndTime()),
73     m_clipboard(new Clipboard(*clipboard)),
74     m_pasteType(pasteType)
75 {
76     if (pasteType != OpenAndPaste) {
77 
78         // paste clef or key -> relayout to end
79 
80         if (clipboard->isSingleSegment()) {
81 
82             Segment *s(clipboard->getSingleSegment());
83             for (Segment::iterator i = s->begin(); i != s->end(); ++i) {
84                 if ((*i)->isa(Clef::EventType) ||
85                     (*i)->isa(Key::EventType)) {
86                     m_relayoutEndTime = s->getEndTime();
87                     break;
88                 }
89             }
90         }
91     }
92 }
93 
94 PasteEventsCommand::PasteEventsCommand(Segment &segment,
95                                        Clipboard *clipboard,
96                                        timeT pasteTime,
97                                        timeT pasteEndTime,
98                                        PasteType pasteType) :
99     BasicCommand(getGlobalName(), segment, pasteTime, pasteEndTime),
100     m_relayoutEndTime(getEndTime()),
101     m_clipboard(new Clipboard(*clipboard)),
102     m_pasteType(pasteType)
103 {
104 }
105 
106 PasteEventsCommand::~PasteEventsCommand()
107 {
108     delete m_clipboard;
109 }
110 
111 PasteEventsCommand::PasteTypeMap
112 
113 PasteEventsCommand::getPasteTypes()
114 {
115     static PasteTypeMap types;
116     static bool haveTypes = false;
117     if (!haveTypes) {
118         types[Restricted] =
119             tr("Paste into an existing gap [\"restricted\"]");
120         types[Simple] =
121             tr("Erase existing events to make room [\"simple\"]");
122         types[OpenAndPaste] =
123             tr("Move existing events out of the way [\"open-n-paste\"]");
124         types[NoteOverlay] =
125             tr("Overlay notes, tying against present notes [\"note-overlay\"]");
126         types[MatrixOverlay] =
127             tr("Overlay notes, ignoring present notes [\"matrix-overlay\"]");
128     }
129     return types;
130 }
131 
132 timeT
133 PasteEventsCommand::getEffectiveEndTime(Segment &segment,
134                                         Clipboard *clipboard,
135                                         timeT pasteTime)
136 {
137     if (!clipboard->isSingleSegment()) {
138         RG_DEBUG << "PasteEventsCommand::getEffectiveEndTime: not single segment";
139         return pasteTime;
140     }
141 
142     RG_DEBUG << "PasteEventsCommand::getEffectiveEndTime: clipboard "
143     << clipboard->getSingleSegment()->getStartTime()
144     << " -> "
145     << clipboard->getSingleSegment()->getEndTime();
146 
147     timeT d = clipboard->getSingleSegment()->getEndTime() -
148               clipboard->getSingleSegment()->getStartTime();
149 
150     if (m_pasteType == OpenAndPaste) {
151         return segment.getEndTime() + d;
152     } else {
153         Segment::iterator i = segment.findTime(pasteTime + d);
154         if (i == segment.end())
155             return segment.getEndTime();
156         else
157             return (*i)->getAbsoluteTime();
158     }
159 }
160 
161 timeT
162 PasteEventsCommand::getRelayoutEndTime()
163 {
164     return m_relayoutEndTime;
165 }
166 
167 bool
168 PasteEventsCommand::isPossible()
169 {
170     if (m_clipboard->isEmpty() || !m_clipboard->isSingleSegment()) {
171         return false;
172     }
173 
174     Segment *source = m_clipboard->getSingleSegment();
175     Segment *destination(&getSegment());
176 
177     timeT pasteTime = std::max(getStartTime(), destination->getStartTime());
178     timeT origin = source->getStartTime();
179     timeT duration = source->getEndTime() - origin;
180 
181     if (m_pasteType == MatrixOverlay) {
182         // Compute the duration of the source without the rests at the end.
183         Segment::iterator last = source->end();
184         for (--last ; last != source->begin(); --last) {
185             if (!(*last)->isa(Note::EventRestType)) {
186                 break;
187             }
188             duration = (*last)->getAbsoluteTime() - origin;
189         }
190     }
191 
192     RG_DEBUG << "PasteEventsCommand::isPossible: paste time is " << pasteTime << ", origin is " << origin << ", duration is " << duration;
193 
194     if (pasteTime + duration > destination->getEndTime()) {
195         return false;
196     }
197 
198     if (m_pasteType == OpenAndPaste &&
199         destination->begin() != destination->end()) {
200         timeT lastEnd = destination->getEndTime();
201         // Ignore the rests at the end of the destination segment.
202         Segment::iterator last = destination->end();
203         for (--last ; last != destination->begin(); --last) {
204             if (!(*last)->isa(Note::EventRestType)) {
205                 break;
206             }
207             lastEnd = (*last)->getAbsoluteTime();
208         }
209 
210         if (lastEnd + duration > destination->getEndTime()) {
211             return false;
212         }
213     }
214 
215     if (m_pasteType != Restricted) {
216         return true;
217     }
218 
219     SegmentNotationHelper helper(getSegment());
220     return helper.removeRests(pasteTime, duration, true);
221 }
222 
223 void
224 PasteEventsCommand::modifySegment()
225 {
226     RG_DEBUG << "PasteEventsCommand::modifySegment" << getSegment();
227 
228     if (!m_clipboard->isSingleSegment())
229         return ;
230 
231     Segment *source = m_clipboard->getSingleSegment();
232     Segment *destination(&getSegment());
233 
234     RG_DEBUG << "segment source";
235     RG_DEBUG << *source;
236     RG_DEBUG << "segment source end";
237     RG_DEBUG << "segment destination";
238     RG_DEBUG << *destination;
239     RG_DEBUG << "segment destination end";
240 
241     timeT destEndTime = destination->getEndTime();
242     timeT pasteTime = std::max(getStartTime(), destination->getStartTime());
243     timeT origin = source->getStartTime();
244     timeT duration = source->getEndTime() - origin;
245 
246     RG_DEBUG << "pasteTime" << pasteTime << "origin" << origin;
247 
248     SegmentNotationHelper helper(*destination);
249     bool possible = helper.removeRests(pasteTime, duration, true);
250     if (! possible) RG_WARNING << "pasting when not possible";
251 
252     RG_DEBUG << "PasteEventsCommand::modifySegment() : paste type = "
253     << m_pasteType << " - pasteTime = "
254     << pasteTime << " - origin = " << origin;
255 
256     // First check for group IDs, which we want to make unique in the
257     // copies in the destination segment
258 
259     std::map<long, long> groupIdMap;
260     for (Segment::iterator i = source->begin(); i != source->end(); ++i) {
261         long groupId = -1;
262         if ((*i)->get
263                 <Int>(BEAMED_GROUP_ID, groupId)) {
264             if (groupIdMap.find(groupId) == groupIdMap.end()) {
265                 groupIdMap[groupId] = destination->getNextId();
266             }
267         }
268     }
269 
270     switch (m_pasteType) {
271 
272         // Do some preliminary work to make space or whatever;
273         // we do the actual paste after this switch statement
274         // (except where individual cases do the work and return)
275 
276     case Restricted: {
277             // removeRests() changes the duration destructively but the
278             // variable "duration" is used by normalizeRests()
279             timeT d = duration;
280             if (!helper.removeRests(pasteTime, d)) {
281                 return;
282             }
283             break;
284         }
285 
286     case Simple:
287         destination->erase(destination->findTime(pasteTime),
288                            destination->findTime(pasteTime + duration));
289         break;
290 
291     case OpenAndPaste: {
292             timeT endTime = pasteTime + duration;
293             std::vector<Event *> copies, toErase;
294             for (Segment::iterator i = destination->findTime(pasteTime);
295                  i != destination->end(); ++i) {
296                 Event *e = (*i)->copyMoving(duration);
297                 timeT myTime =
298                     e->getAbsoluteTime() + e->getGreaterDuration() + duration;
299 
300                 toErase.push_back(*i);
301 
302                 if (e->isa(Note::EventRestType)) {
303                     if (myTime > destEndTime) {
304                         continue;
305                     }
306                 }
307                 if (e->has(BEAMED_GROUP_ID)) {
308                     e->set
309                     <Int>(BEAMED_GROUP_ID, groupIdMap[e->get
310                                                       <Int>(BEAMED_GROUP_ID)]);
311                 }
312                 if (myTime > endTime) {
313                     endTime = myTime;
314                 }
315                 copies.push_back(e);
316             }
317 
318             for (size_t i = 0; i < toErase.size(); ++i) {
319                 destination->eraseSingle(toErase[i]);
320             }
321 
322             for (size_t i = 0; i < copies.size(); ++i) {
323                 destination->insert(copies[i]);
324             }
325 
326             endTime = std::min(destEndTime, destination->getBarEndForTime(endTime));
327             duration = endTime - pasteTime;
328             break;
329         }
330 
331     case NoteOverlay: {
332         for (Segment::iterator i = source->begin(); i != source->end(); ++i) {
333             if ((*i)->isa(Note::EventRestType)) {
334                 continue;
335             }
336 
337             Event *e = (*i)->copyMoving(pasteTime - origin);
338 
339             if (e->has(BEAMED_GROUP_ID)) {
340                 e->set<Int>(BEAMED_GROUP_ID,
341                             groupIdMap[e->get<Int>(BEAMED_GROUP_ID)]);
342             }
343 
344             if ((*i)->isa(Note::EventType)) {
345                 RG_DEBUG << "NoteOverlay insert" <<
346                     e->getNotationAbsoluteTime();
347                 // e is model event: we retain ownership of it
348                 helper.insertNote(e);
349                 delete e;
350             } else {
351                 destination->insert(e);
352             }
353         }
354 
355         RG_DEBUG << "segment after modify";
356         RG_DEBUG << *destination;
357         RG_DEBUG << "segment after modify end";
358 
359         return;
360     }
361 
362     case MatrixOverlay:
363 
364         for (Segment::iterator i = source->begin(); i != source->end(); ++i) {
365             if ((*i)->isa(Note::EventRestType)) {
366                 continue;
367             }
368 
369             Event *e = (*i)->copyMoving(pasteTime - origin);
370 
371             if (e->has(BEAMED_GROUP_TYPE) &&
372                     e->get
373                     <String>(BEAMED_GROUP_TYPE) == GROUP_TYPE_BEAMED) {
374                 e->unset(BEAMED_GROUP_ID);
375                 e->unset(BEAMED_GROUP_TYPE);
376             }
377 
378             if (e->has(BEAMED_GROUP_ID)) {
379                 e->set
380                 <Int>(BEAMED_GROUP_ID, groupIdMap[e->get
381                                                   <Int>(BEAMED_GROUP_ID)]);
382             }
383 
384             destination->insert(e);
385         }
386 
387         timeT endTime = pasteTime + duration;
388         if (endTime > destEndTime) {
389             endTime = destEndTime;
390         }
391 
392         destination->normalizeRests(pasteTime, endTime);
393 
394         return ;
395     }
396 
397     RG_DEBUG << "PasteEventsCommand::modifySegment() - inserting\n";
398 
399     for (Segment::iterator i = source->begin(); i != source->end(); ++i) {
400         Event *e = (*i)->copyMoving(pasteTime - origin);
401         if (e->has(BEAMED_GROUP_ID)) {
402             e->set
403             <Int>(BEAMED_GROUP_ID, groupIdMap[e->get
404                                               <Int>(BEAMED_GROUP_ID)]);
405         }
406         destination->insert(e);
407     }
408 
409     destination->normalizeRests(pasteTime, pasteTime + duration);
410 }
411 
412 }
413