1 //=========================================================
2 // MusE
3 // Linux Music Editor
4 // $Id: functions.cpp,v 1.20.2.19 2011/05/05 20:10 flo93 Exp $
5 // (C) Copyright 2011,2013 Florian Jung (flo93@sourceforge.net)
6 //
7 // This program is free software; you can redistribute it and/or
8 // modify it under the terms of the GNU General Public License
9 // as published by the Free Software Foundation; version 2 of
10 // the License, or (at your option) any later version.
11 //
12 // This program is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 // GNU General Public License for more details.
16 //
17 // You should have received a copy of the GNU General Public License
18 // along with this program; if not, write to the Free Software
19 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 //
21 //=========================================================
22
23 #include "functions.h"
24 #include "song.h"
25 #include "helper.h"
26
27 #include "event.h"
28 #include "audio.h"
29 #include "gconfig.h"
30 #include "sig.h"
31
32 #include "function_dialogs/velocity.h"
33 #include "function_dialogs/quantize.h"
34 #include "function_dialogs/crescendo.h"
35 #include "function_dialogs/gatetime.h"
36 #include "function_dialogs/remove.h"
37 #include "function_dialogs/transpose.h"
38 #include "function_dialogs/setlen.h"
39 #include "function_dialogs/move.h"
40 #include "function_dialogs/deloverlaps.h"
41 #include "function_dialogs/legato.h"
42 #include "components/pasteeventsdialog.h"
43
44 #include <limits.h>
45 #include <iostream>
46 #include <errno.h>
47 #include <sys/stat.h>
48 #include <sys/types.h>
49 #include <stdint.h>
50 #ifdef _WIN32
51 #include "mman.h"
52 #include "mman.c"
53 #else
54 #include <sys/mman.h>
55 #endif
56 #include "muse_math.h"
57
58 #include <QTemporaryFile>
59 #include <QByteArray>
60 #include <QDrag>
61 #include <QMessageBox>
62 #include <QClipboard>
63 #include <QSet>
64
65 // Forwards from header:
66 #include <QMimeData>
67 #include "track.h"
68 #include "part.h"
69
70 using namespace std;
71
72 using MusEGlobal::config;
73
74
75 namespace MusEGui {
76
erase_items_dialog(const FunctionDialogMode & mode)77 FunctionDialogReturnErase erase_items_dialog(const FunctionDialogMode& mode)
78 {
79 erase_dialog->setElements(mode._buttons);
80 if(!erase_dialog->exec())
81 return FunctionDialogReturnErase();
82
83 const int flags = erase_dialog->_ret_flags;
84 return FunctionDialogReturnErase(flags & FunctionReturnAllEvents,
85 flags & FunctionReturnAllParts,
86 flags & FunctionReturnLooped,
87 MusEGlobal::song->lPos(), MusEGlobal::song->rPos(),
88 erase_dialog->velo_thres_used, erase_dialog->velo_threshold,
89 erase_dialog->len_thres_used, erase_dialog->len_threshold);
90 }
91
crescendo_items_dialog(const FunctionDialogMode & mode)92 FunctionDialogReturnCrescendo crescendo_items_dialog(const FunctionDialogMode& mode)
93 {
94 if (MusEGlobal::song->rPos() <= MusEGlobal::song->lPos())
95 {
96 QMessageBox::warning(NULL, QObject::tr("Error"), QObject::tr("Please first select the range for crescendo with the loop markers."));
97 return FunctionDialogReturnCrescendo();
98 }
99
100 crescendo_dialog->setElements(mode._buttons);
101 if(!crescendo_dialog->exec())
102 return FunctionDialogReturnCrescendo();
103
104 const int flags = crescendo_dialog->_ret_flags;
105 return FunctionDialogReturnCrescendo(flags & FunctionReturnAllEvents,
106 flags & FunctionReturnAllParts,
107 flags & FunctionReturnLooped,
108 MusEGlobal::song->lPos(), MusEGlobal::song->rPos(),
109 crescendo_dialog->start_val, crescendo_dialog->end_val,
110 crescendo_dialog->absolute);
111 }
112
deloverlaps_items_dialog(const FunctionDialogMode & mode)113 FunctionDialogReturnDelOverlaps deloverlaps_items_dialog(const FunctionDialogMode& mode)
114 {
115 del_overlaps_dialog->setElements(mode._buttons);
116 if(!del_overlaps_dialog->exec())
117 return FunctionDialogReturnDelOverlaps();
118
119 const int flags = del_overlaps_dialog->_ret_flags;
120 return FunctionDialogReturnDelOverlaps(flags & FunctionReturnAllEvents,
121 flags & FunctionReturnAllParts,
122 flags & FunctionReturnLooped,
123 MusEGlobal::song->lPos(), MusEGlobal::song->rPos());
124 }
125
gatetime_items_dialog(const FunctionDialogMode & mode)126 FunctionDialogReturnGateTime gatetime_items_dialog(const FunctionDialogMode& mode)
127 {
128 gatetime_dialog->setElements(mode._buttons);
129 if(!gatetime_dialog->exec())
130 return FunctionDialogReturnGateTime();
131
132 const int flags = gatetime_dialog->_ret_flags;
133 return FunctionDialogReturnGateTime(flags & FunctionReturnAllEvents,
134 flags & FunctionReturnAllParts,
135 flags & FunctionReturnLooped,
136 MusEGlobal::song->lPos(), MusEGlobal::song->rPos(),
137 gatetime_dialog->rateVal, gatetime_dialog->offsetVal);
138 }
139
legato_items_dialog(const FunctionDialogMode & mode)140 FunctionDialogReturnLegato legato_items_dialog(const FunctionDialogMode& mode)
141 {
142 legato_dialog->setElements(mode._buttons);
143 if(!legato_dialog->exec())
144 return FunctionDialogReturnLegato();
145
146 const int flags = legato_dialog->_ret_flags;
147 return FunctionDialogReturnLegato(flags & FunctionReturnAllEvents,
148 flags & FunctionReturnAllParts,
149 flags & FunctionReturnLooped,
150 MusEGlobal::song->lPos(), MusEGlobal::song->rPos(),
151 legato_dialog->min_len, legato_dialog->allow_shortening);
152 }
153
move_items_dialog(const FunctionDialogMode & mode)154 FunctionDialogReturnMove move_items_dialog(const FunctionDialogMode& mode)
155 {
156 move_notes_dialog->setElements(mode._buttons);
157 if(!move_notes_dialog->exec())
158 return FunctionDialogReturnMove();
159
160 const int flags = move_notes_dialog->_ret_flags;
161 return FunctionDialogReturnMove(flags & FunctionReturnAllEvents,
162 flags & FunctionReturnAllParts,
163 flags & FunctionReturnLooped,
164 MusEGlobal::song->lPos(), MusEGlobal::song->rPos(),
165 move_notes_dialog->amount);
166 }
167
quantize_items_dialog(const FunctionDialogMode & mode)168 FunctionDialogReturnQuantize quantize_items_dialog(const FunctionDialogMode& mode)
169 {
170 quantize_dialog->setElements(mode._buttons);
171 if(!quantize_dialog->exec())
172 return FunctionDialogReturnQuantize();
173
174 const int flags = quantize_dialog->_ret_flags;
175 return FunctionDialogReturnQuantize(flags & FunctionReturnAllEvents,
176 flags & FunctionReturnAllParts,
177 flags & FunctionReturnLooped,
178 MusEGlobal::song->lPos(), MusEGlobal::song->rPos(),
179 quantize_dialog->strength, quantize_dialog->threshold,
180 quantize_dialog->raster_index, quantize_dialog->swing,
181 quantize_dialog->quant_len);
182 }
183
setlen_items_dialog(const FunctionDialogMode & mode)184 FunctionDialogReturnSetLen setlen_items_dialog(const FunctionDialogMode& mode)
185 {
186 set_notelen_dialog->setElements(mode._buttons);
187 if(!set_notelen_dialog->exec())
188 return FunctionDialogReturnSetLen();
189
190 const int flags = set_notelen_dialog->_ret_flags;
191 return FunctionDialogReturnSetLen(flags & FunctionReturnAllEvents,
192 flags & FunctionReturnAllParts,
193 flags & FunctionReturnLooped,
194 MusEGlobal::song->lPos(), MusEGlobal::song->rPos(),
195 set_notelen_dialog->len);
196 }
197
transpose_items_dialog(const FunctionDialogMode & mode)198 FunctionDialogReturnTranspose transpose_items_dialog(const FunctionDialogMode& mode)
199 {
200 transpose_dialog->setElements(mode._buttons);
201 if(!transpose_dialog->exec())
202 return FunctionDialogReturnTranspose();
203
204 const int flags = transpose_dialog->_ret_flags;
205 return FunctionDialogReturnTranspose(flags & FunctionReturnAllEvents,
206 flags & FunctionReturnAllParts,
207 flags & FunctionReturnLooped,
208 MusEGlobal::song->lPos(), MusEGlobal::song->rPos(),
209 transpose_dialog->amount);
210 }
211
velocity_items_dialog(const FunctionDialogMode & mode)212 FunctionDialogReturnVelocity velocity_items_dialog(const FunctionDialogMode& mode)
213 {
214 velocity_dialog->setElements(mode._buttons);
215 if(!velocity_dialog->exec())
216 return FunctionDialogReturnVelocity();
217
218 const int flags = velocity_dialog->_ret_flags;
219 return FunctionDialogReturnVelocity(flags & FunctionReturnAllEvents,
220 flags & FunctionReturnAllParts,
221 flags & FunctionReturnLooped,
222 MusEGlobal::song->lPos(), MusEGlobal::song->rPos(),
223 velocity_dialog->rateVal, velocity_dialog->offsetVal);
224 }
225
226
227 } // namespace MusEGui
228
229
230 namespace MusECore {
231
232 // unit private functions:
233
234 bool read_eventlist_and_part(Xml& xml, EventList* el, int* part_id);
235
236 // -----------------------
237
238 typedef map<const Part*, unsigned> expand_map_t;
239 typedef map<const Part*, set<const Part*> > new_part_map_t;
240
241
partlist_to_set(PartList * pl)242 set<const Part*> partlist_to_set(PartList* pl)
243 {
244 set<const Part*> result;
245
246 for (PartList::iterator it=pl->begin(); it!=pl->end(); it++)
247 result.insert(it->second);
248
249 return result;
250 }
251
part_to_set(const Part * p)252 set<const Part*> part_to_set(const Part* p)
253 {
254 set<const Part*> result;
255 result.insert(p);
256 return result;
257 }
258
get_all_parts()259 set<const Part*> get_all_parts()
260 {
261 set<const Part*> result;
262
263 TrackList* tracks=MusEGlobal::song->tracks();
264 for (TrackList::const_iterator t_it=tracks->begin(); t_it!=tracks->end(); t_it++)
265 {
266 const PartList* parts=(*t_it)->cparts();
267 for (ciPart p_it=parts->begin(); p_it!=parts->end(); p_it++)
268 result.insert(p_it->second);
269 }
270
271 return result;
272 }
273
get_all_selected_parts()274 set<const Part*> get_all_selected_parts()
275 {
276 set<const Part*> result;
277
278 TrackList* tracks=MusEGlobal::song->tracks();
279 for (TrackList::const_iterator t_it=tracks->begin(); t_it!=tracks->end(); t_it++)
280 {
281 const PartList* parts=(*t_it)->cparts();
282 for (ciPart p_it=parts->begin(); p_it!=parts->end(); p_it++)
283 if (p_it->second->selected())
284 result.insert(p_it->second);
285 }
286
287 return result;
288 }
289
is_relevant(const Event & event,const Part * part,int range,RelevantSelectedEvents_t relevant)290 bool is_relevant(const Event& event, const Part* part, int range, RelevantSelectedEvents_t relevant)
291 {
292 unsigned tick;
293
294 switch(event.type())
295 {
296 case Note:
297 if(!(relevant & NotesRelevant))
298 return false;
299 break;
300
301 case Controller:
302 if(!(relevant & ControllersRelevant))
303 return false;
304 break;
305
306 case Sysex:
307 if(!(relevant & SysexRelevant))
308 return false;
309 break;
310
311 case Meta:
312 if(!(relevant & MetaRelevant))
313 return false;
314 break;
315
316 case Wave:
317 if(!(relevant & WaveRelevant))
318 return false;
319 break;
320 }
321
322 switch (range)
323 {
324 case 0: return true;
325 case 1: return event.selected();
326 case 2: tick=event.tick()+part->tick(); return (tick >= MusEGlobal::song->lpos()) && (tick < MusEGlobal::song->rpos());
327 case 3: return is_relevant(event,part,1, relevant) && is_relevant(event,part,2, relevant);
328 default: cout << "ERROR: ILLEGAL FUNCTION CALL in is_relevant: range is illegal: "<<range<<endl;
329 return false;
330 }
331 }
332
333
get_events(const set<const Part * > & parts,int range,RelevantSelectedEvents_t relevant)334 map<const Event*, const Part*> get_events(const set<const Part*>& parts, int range, RelevantSelectedEvents_t relevant)
335 {
336 map<const Event*, const Part*> events;
337
338 for (set<const Part*>::iterator part=parts.begin(); part!=parts.end(); part++)
339 for (ciEvent event=(*part)->events().begin(); event!=(*part)->events().end(); event++)
340 if (is_relevant(event->second, *part, range, relevant))
341 events.insert(pair<const Event*, const Part*>(&event->second, *part));
342
343 return events;
344 }
345
346
modify_velocity(const set<const Part * > & parts,int range,int rate,int offset)347 bool modify_velocity(const set<const Part*>& parts, int range, int rate, int offset)
348 {
349 map<const Event*, const Part*> events = get_events(parts, range);
350 Undo operations;
351
352 if ( (!events.empty()) && ((rate!=100) || (offset!=0)) )
353 {
354 for (map<const Event*, const Part*>::iterator it=events.begin(); it!=events.end(); it++)
355 {
356 const Event& event=*(it->first);
357 // This operation can only apply to notes.
358 if(event.type() != Note)
359 continue;
360
361 const Part* part=it->second;
362
363 int velo = event.velo();
364
365 velo = (velo * rate) / 100;
366 velo += offset;
367
368 if (velo <= 0)
369 velo = 1;
370 else if (velo > 127)
371 velo = 127;
372
373 if (event.velo() != velo)
374 {
375 Event newEvent = event.clone();
376 newEvent.setVelo(velo);
377 operations.push_back(UndoOp(UndoOp::ModifyEvent, newEvent, event, part, false, false));
378 }
379 }
380
381 return MusEGlobal::song->applyOperationGroup(operations);
382 }
383 else
384 return false;
385 }
386
modify_off_velocity(const set<const Part * > & parts,int range,int rate,int offset)387 bool modify_off_velocity(const set<const Part*>& parts, int range, int rate, int offset)
388 {
389 map<const Event*, const Part*> events = get_events(parts, range);
390 Undo operations;
391
392 if ( (!events.empty()) && ((rate!=100) || (offset!=0)) )
393 {
394 for (map<const Event*, const Part*>::iterator it=events.begin(); it!=events.end(); it++)
395 {
396 const Event& event=*(it->first);
397 // This operation can only apply to notes.
398 if(event.type() != Note)
399 continue;
400 const Part* part=it->second;
401
402 int velo = event.veloOff();
403
404 velo = (velo * rate) / 100;
405 velo += offset;
406
407 if (velo <= 0)
408 velo = 1;
409 else if (velo > 127)
410 velo = 127;
411
412 if (event.veloOff() != velo)
413 {
414 Event newEvent = event.clone();
415 newEvent.setVeloOff(velo);
416 operations.push_back(UndoOp(UndoOp::ModifyEvent, newEvent, event, part, false, false));
417 }
418 }
419
420 return MusEGlobal::song->applyOperationGroup(operations);
421 }
422 else
423 return false;
424 }
425
modify_notelen(const set<const Part * > & parts,int range,int rate,int offset)426 bool modify_notelen(const set<const Part*>& parts, int range, int rate, int offset)
427 {
428 map<const Event*, const Part*> events = get_events(parts, range);
429 Undo operations;
430 map<const Part*, int> partlen;
431
432 if ( (!events.empty()) && ((rate!=100) || (offset!=0)) )
433 {
434 for (map<const Event*, const Part*>::iterator it=events.begin(); it!=events.end(); it++)
435 {
436 const Event& event=*(it->first);
437 // This operation can only apply to notes.
438 if(event.type() != Note)
439 continue;
440 const Part* part=it->second;
441
442 unsigned int len = event.lenTick(); //prevent compiler warning: comparison signed/unsigned
443
444 len = (len * rate) / 100;
445 len += offset;
446
447 if (len <= 0)
448 len = 1;
449
450 if ((event.tick()+len > part->lenTick()) && (!(part->hasHiddenEvents() & Part::RightEventsHidden)))
451 partlen[part]=event.tick()+len; // schedule auto-expanding
452
453 if (event.lenTick() != len)
454 {
455 Event newEvent = event.clone();
456 newEvent.setLenTick(len);
457 operations.push_back(UndoOp(UndoOp::ModifyEvent, newEvent, event, part, false, false));
458 }
459 }
460
461 for (map<const Part*, int>::iterator it=partlen.begin(); it!=partlen.end(); it++)
462 schedule_resize_all_same_len_clone_parts(it->first, it->second, operations);
463
464 return MusEGlobal::song->applyOperationGroup(operations);
465 }
466 else
467 return false;
468 }
469
set_notelen(const set<const Part * > & parts,int range,int len)470 bool set_notelen(const set<const Part*>& parts, int range, int len)
471 {
472 return modify_notelen(parts, range, 0, len);
473 }
474
quantize_tick(unsigned tick,unsigned raster,int swing)475 unsigned quantize_tick(unsigned tick, unsigned raster, int swing)
476 {
477 //find out the nearest tick and the distance to it:
478 //this is so complicated because this function supports
479 //swing: if swing is 50, the resulting rhythm is not
480 //"daa daa daa daa" but "daaaa da daaaa da"...
481 int tick_dest1 = MusEGlobal::sigmap.raster1(tick, raster*2); //round down
482 int tick_dest2 = tick_dest1 + raster + raster*swing/100;
483 int tick_dest3 = tick_dest1 + raster*2;
484
485 int tick_diff1 = abs(tick_dest1 - (int)tick);
486 int tick_diff2 = abs(tick_dest2 - (int)tick);
487 int tick_diff3 = abs(tick_dest3 - (int)tick);
488
489 if ((tick_diff3 <= tick_diff1) && (tick_diff3 <= tick_diff2)) //tick_dest3 is the nearest tick
490 return tick_dest3;
491 else if ((tick_diff2 <= tick_diff1) && (tick_diff2 <= tick_diff3)) //tick_dest2 is the nearest tick
492 return tick_dest2;
493 else
494 return tick_dest1;
495 }
496
quantize_notes(const set<const Part * > & parts,int range,int raster,bool quant_len,int strength,int swing,int threshold)497 bool quantize_notes(const set<const Part*>& parts, int range, int raster, bool quant_len, int strength, int swing, int threshold)
498 {
499 map<const Event*, const Part*> events = get_events(parts, range);
500 Undo operations;
501
502 if (!events.empty())
503 {
504 for (map<const Event*, const Part*>::iterator it=events.begin(); it!=events.end(); it++)
505 {
506 const Event& event=*(it->first);
507 // This operation can only apply to notes.
508 if(event.type() != Note)
509 continue;
510 const Part* part=it->second;
511
512 unsigned begin_tick = event.tick() + part->tick();
513 int begin_diff = quantize_tick(begin_tick, raster, swing) - begin_tick;
514
515 if (abs(begin_diff) > threshold)
516 begin_tick = begin_tick + begin_diff*strength/100;
517
518
519 unsigned len=event.lenTick();
520
521 unsigned end_tick = begin_tick + len;
522 int len_diff = quantize_tick(end_tick, raster, swing) - end_tick;
523
524 if ((abs(len_diff) > threshold) && quant_len)
525 len = len + len_diff*strength/100;
526
527 if (len <= 0)
528 len = 1;
529
530
531 if ( (event.lenTick() != len) || (event.tick() + part->tick() != begin_tick) )
532 {
533 Event newEvent = event.clone();
534 newEvent.setTick(begin_tick - part->tick());
535 newEvent.setLenTick(len);
536 operations.push_back(UndoOp(UndoOp::ModifyEvent, newEvent, event, part, false, false));
537 }
538 }
539
540 return MusEGlobal::song->applyOperationGroup(operations);
541 }
542 else
543 return false;
544 }
545
erase_notes(const set<const Part * > & parts,int range,int velo_threshold,bool velo_thres_used,int len_threshold,bool len_thres_used)546 bool erase_notes(const set<const Part*>& parts, int range, int velo_threshold, bool velo_thres_used, int len_threshold, bool len_thres_used)
547 {
548 map<const Event*, const Part*> events = get_events(parts, range);
549 Undo operations;
550
551 if (!events.empty())
552 {
553 for (map<const Event*, const Part*>::iterator it=events.begin(); it!=events.end(); it++)
554 {
555 // This operation can apply to any event...
556 const Event& event=*(it->first);
557 const Part* part=it->second;
558
559 if ( (!velo_thres_used && !len_thres_used) ||
560 (velo_thres_used && event.velo() < velo_threshold) ||
561 (len_thres_used && int(event.lenTick()) < len_threshold) )
562 operations.push_back(UndoOp(UndoOp::DeleteEvent, event, part, false, false));
563 }
564
565 return MusEGlobal::song->applyOperationGroup(operations);
566 }
567 else
568 return false;
569 }
570
transpose_notes(const set<const Part * > & parts,int range,signed int halftonesteps)571 bool transpose_notes(const set<const Part*>& parts, int range, signed int halftonesteps)
572 {
573 map<const Event*, const Part*> events = get_events(parts, range);
574 Undo operations;
575
576 if ( (!events.empty()) && (halftonesteps!=0) )
577 {
578 for (map<const Event*, const Part*>::iterator it=events.begin(); it!=events.end(); it++)
579 {
580 const Event& event=*(it->first);
581 // This operation can only apply to notes.
582 if(event.type() != Note)
583 continue;
584 const Part* part=it->second;
585
586 Event newEvent = event.clone();
587 int pitch = event.pitch()+halftonesteps;
588 if (pitch > 127) pitch=127;
589 if (pitch < 0) pitch=0;
590 newEvent.setPitch(pitch);
591 operations.push_back(UndoOp(UndoOp::ModifyEvent, newEvent, event, part, false, false));
592 }
593
594 return MusEGlobal::song->applyOperationGroup(operations);
595 }
596 else
597 return false;
598 }
599
crescendo(const set<const Part * > & parts,int range,int start_val,int end_val,bool absolute)600 bool crescendo(const set<const Part*>& parts, int range, int start_val, int end_val, bool absolute)
601 {
602 map<const Event*, const Part*> events = get_events(parts, range);
603 Undo operations;
604
605 int from=MusEGlobal::song->lpos();
606 int to=MusEGlobal::song->rpos();
607
608 if ( (!events.empty()) && (to>from) )
609 {
610 for (map<const Event*, const Part*>::iterator it=events.begin(); it!=events.end(); it++)
611 {
612 const Event& event=*(it->first);
613 // This operation can only apply to notes.
614 if(event.type() != Note)
615 continue;
616 const Part* part=it->second;
617
618 unsigned tick = event.tick() + part->tick();
619 float curr_val= (float)start_val + (float)(end_val-start_val) * (tick-from) / (to-from);
620
621 Event newEvent = event.clone();
622 int velo = event.velo();
623
624 if (absolute)
625 velo=curr_val;
626 else
627 velo=curr_val*velo/100;
628
629 if (velo > 127) velo=127;
630 if (velo <= 0) velo=1;
631 newEvent.setVelo(velo);
632 operations.push_back(UndoOp(UndoOp::ModifyEvent, newEvent, event, part, false, false));
633 }
634
635 return MusEGlobal::song->applyOperationGroup(operations);
636 }
637 else
638 return false;
639 }
640
move_notes(const set<const Part * > & parts,int range,signed int ticks)641 bool move_notes(const set<const Part*>& parts, int range, signed int ticks)
642 {
643 map<const Event*, const Part*> events = get_events(parts, range);
644 Undo operations;
645 map<const Part*, int> partlen;
646
647 if ( (!events.empty()) && (ticks!=0) )
648 {
649 for (map<const Event*, const Part*>::iterator it=events.begin(); it!=events.end(); it++)
650 {
651 const Event& event=*(it->first);
652 // This operation can only apply to notes.
653 if(event.type() != Note)
654 continue;
655
656 const Part* part=it->second;
657 bool del=false;
658
659 Event newEvent = event.clone();
660 if ((signed)event.tick()+ticks < 0) //don't allow moving before the part's begin
661 newEvent.setTick(0);
662 else
663 newEvent.setTick(event.tick()+ticks);
664
665 if (newEvent.endTick() > part->lenTick()) //if exceeding the part's end:
666 {
667 if (part->hasHiddenEvents() & Part::RightEventsHidden) // auto-expanding is forbidden, clip
668 {
669 if (part->lenTick() > newEvent.tick())
670 newEvent.setLenTick(part->lenTick() - newEvent.tick());
671 else
672 del=true; //if the new length would be <= 0, erase the note
673 }
674 else
675 partlen[part]=newEvent.endTick(); // schedule auto-expanding
676 }
677
678 if (del==false)
679 operations.push_back(UndoOp(UndoOp::ModifyEvent, newEvent, event, part, false, false));
680 else
681 operations.push_back(UndoOp(UndoOp::DeleteEvent, event, part, false, false));
682 }
683
684 for (map<const Part*, int>::iterator it=partlen.begin(); it!=partlen.end(); it++)
685 schedule_resize_all_same_len_clone_parts(it->first, it->second, operations);
686
687 return MusEGlobal::song->applyOperationGroup(operations);
688 }
689 else
690 return false;
691 }
692
delete_overlaps(const set<const Part * > & parts,int range)693 bool delete_overlaps(const set<const Part*>& parts, int range)
694 {
695 map<const Event*, const Part*> events = get_events(parts, range);
696 Undo operations;
697
698 set<const Event*> deleted_events;
699
700 if (!events.empty())
701 {
702 for (map<const Event*, const Part*>::iterator it1=events.begin(); it1!=events.end(); it1++)
703 {
704 const Event& event1=*(it1->first);
705 // This operation can only apply to notes.
706 if(event1.type() != Note)
707 continue;
708 const Part* part1=it1->second;
709
710 // we may NOT optimize by letting it2 start at (it1 +1); this optimisation
711 // is only allowed when events was sorted by time. it is, however, sorted
712 // randomly by pointer.
713 for (map<const Event*, const Part*>::iterator it2=events.begin(); it2!=events.end(); it2++)
714 {
715 const Event& event2=*(it2->first);
716 // This operation can only apply to notes.
717 if(event2.type() != Note)
718 continue;
719 const Part* part2=it2->second;
720
721 if ( (part1->isCloneOf(part2)) && // part1 and part2 are the same or are duplicates
722 (&event1 != &event2) && // and event1 and event2 aren't the same
723 (deleted_events.find(&event2) == deleted_events.end()) ) //and event2 hasn't been deleted before
724 {
725 if ( (event1.pitch() == event2.pitch()) &&
726 (event1.tick() <= event2.tick()) &&
727 (event1.endTick() > event2.tick()) ) //they overlap
728 {
729 int new_len = event2.tick() - event1.tick();
730
731 if (new_len==0)
732 {
733 operations.push_back(UndoOp(UndoOp::DeleteEvent, event2, part2, false, false));
734 deleted_events.insert(&event2);
735 }
736 else
737 {
738 Event new_event1 = event1.clone();
739 new_event1.setLenTick(new_len);
740
741 operations.push_back(UndoOp(UndoOp::ModifyEvent, new_event1, event1, part1, false, false));
742 }
743 }
744 }
745 }
746 }
747
748 return MusEGlobal::song->applyOperationGroup(operations);
749 }
750 else
751 return false;
752 }
753
legato(const set<const Part * > & parts,int range,int min_len,bool dont_shorten)754 bool legato(const set<const Part*>& parts, int range, int min_len, bool dont_shorten)
755 {
756 map<const Event*, const Part*> events = get_events(parts, range);
757 Undo operations;
758
759 if (min_len<=0) min_len=1;
760
761 if (!events.empty())
762 {
763 for (map<const Event*, const Part*>::iterator it1=events.begin(); it1!=events.end(); it1++)
764 {
765 const Event& event1=*(it1->first);
766 // This operation can only apply to notes.
767 if(event1.type() != Note)
768 continue;
769 const Part* part1=it1->second;
770
771 unsigned len=INT_MAX;
772 // we may NOT optimize by letting it2 start at (it1 +1); this optimisation
773 // is only allowed when events was sorted by time. it is, however, sorted
774 // randomly by pointer.
775 for (map<const Event*, const Part*>::iterator it2=events.begin(); it2!=events.end(); it2++)
776 {
777 const Event& event2=*(it2->first);
778 // This operation can only apply to notes.
779 if(event2.type() != Note)
780 continue;
781 const Part* part2=it2->second;
782
783 bool relevant = (event2.tick() >= event1.tick() + min_len);
784 if (dont_shorten)
785 relevant = relevant && (event2.tick() >= event1.endTick());
786
787 if ( (part1->isCloneOf(part2)) && // part1 and part2 are the same or are duplicates
788 relevant && // they're not too near (respect min_len and dont_shorten)
789 (event2.tick()-event1.tick() < len ) ) // that's the nearest relevant following note
790 len=event2.tick()-event1.tick();
791 }
792
793 if (len==INT_MAX) len=event1.lenTick(); // if no following note was found, keep the length
794
795 if (event1.lenTick() != len)
796 {
797 Event new_event1 = event1.clone();
798 new_event1.setLenTick(len);
799
800 operations.push_back(UndoOp(UndoOp::ModifyEvent, new_event1, event1, part1, false, false));
801 }
802 }
803
804 return MusEGlobal::song->applyOperationGroup(operations);
805 }
806 else
807 return false;
808 }
809
810 // if nothing is selected/relevant, this function returns NULL
selected_events_to_mime(const set<const Part * > & parts,int range)811 QMimeData* selected_events_to_mime(const set<const Part*>& parts, int range)
812 {
813 unsigned start_tick = INT_MAX; //will be the tick of the first event or INT_MAX if no events are there
814
815 for (set<const Part*>::iterator part=parts.begin(); part!=parts.end(); part++)
816 for (ciEvent ev=(*part)->events().begin(); ev!=(*part)->events().end(); ev++)
817 if (is_relevant(ev->second, *part, range, AllEventsRelevant))
818 if (ev->second.tick() < start_tick)
819 start_tick=ev->second.tick();
820
821 if (start_tick == INT_MAX)
822 return NULL;
823
824 //---------------------------------------------------
825 // write events as XML into tmp file
826 //---------------------------------------------------
827
828 FILE* tmp = tmpfile();
829 if (tmp == 0)
830 {
831 fprintf(stderr, "EventCanvas::getTextDrag() fopen failed: %s\n", strerror(errno));
832 return 0;
833 }
834
835 Xml xml(tmp);
836 int level = 0;
837
838 for (set<const Part*>::iterator part=parts.begin(); part!=parts.end(); part++)
839 {
840 xml.tag(level++, "eventlist part_id=\"%d\"", (*part)->sn());
841 for (ciEvent ev=(*part)->events().begin(); ev!=(*part)->events().end(); ev++)
842 if (is_relevant(ev->second, *part, range, AllEventsRelevant))
843 ev->second.write(level, xml, -start_tick);
844 xml.etag(--level, "eventlist");
845 }
846
847 QMimeData *mimeData = file_to_mimedata(tmp, "text/x-muse-groupedeventlists" );
848 fclose(tmp);
849 return mimeData;
850 }
851
copy_notes(const set<const Part * > & parts,int range)852 void copy_notes(const set<const Part*>& parts, int range)
853 {
854 QMimeData* drag = selected_events_to_mime(parts,range);
855
856 if (drag)
857 QApplication::clipboard()->setMimeData(drag, QClipboard::Clipboard);
858 }
859
get_groupedevents_len(const QString & pt)860 unsigned get_groupedevents_len(const QString& pt)
861 {
862 unsigned maxlen=0;
863
864 QByteArray pt_= pt.toLatin1();
865 Xml xml(pt_.constData());
866 for (;;)
867 {
868 Xml::Token token = xml.parse();
869 const QString& tag = xml.s1();
870 switch (token)
871 {
872 case Xml::Error:
873 case Xml::End:
874 return maxlen;
875
876 case Xml::TagStart:
877 if (tag == "eventlist")
878 {
879 EventList el;
880 int part_id;
881 if (read_eventlist_and_part(xml, &el, &part_id))
882 {
883 unsigned len = el.rbegin()->first;
884 if (len > maxlen) maxlen=len;
885 }
886 }
887 else
888 xml.unknown("get_clipboard_len");
889 break;
890
891 case Xml::Attribut:
892 case Xml::TagEnd:
893 default:
894 break;
895 }
896 }
897
898 return maxlen; // see also the return statement above!
899 }
900
get_clipboard_len()901 unsigned get_clipboard_len()
902 {
903 QString tmp="x-muse-groupedeventlists"; // QClipboard::text() expects a QString&, not a QString :(
904 QString s = QApplication::clipboard()->text(tmp, QClipboard::Clipboard);
905
906 return get_groupedevents_len(s);
907 }
908
paste_notes(const Part * paste_into_part)909 bool paste_notes(const Part* paste_into_part)
910 {
911 unsigned temp_begin = MusEGlobal::sigmap.raster1(MusEGlobal::song->cpos(),0);
912 unsigned temp_end = MusEGlobal::sigmap.raster2(temp_begin + get_clipboard_len(), 0);
913 MusEGui::paste_events_dialog->raster = temp_end - temp_begin;
914 MusEGui::paste_events_dialog->into_single_part_allowed = (paste_into_part!=NULL);
915
916 if (!MusEGui::paste_events_dialog->exec())
917 return false;
918
919 paste_notes(MusEGui::paste_events_dialog->max_distance, MusEGui::paste_events_dialog->always_new_part,
920 MusEGui::paste_events_dialog->never_new_part, MusEGui::paste_events_dialog->into_single_part ? paste_into_part : NULL,
921 MusEGui::paste_events_dialog->number, MusEGui::paste_events_dialog->raster);
922
923 return true;
924 }
925
paste_notes(int max_distance,bool always_new_part,bool never_new_part,const Part * paste_into_part,int amount,int raster)926 void paste_notes(int max_distance, bool always_new_part, bool never_new_part, const Part* paste_into_part, int amount, int raster)
927 {
928 QString tmp="x-muse-groupedeventlists"; // QClipboard::text() expects a QString&, not a QString :(
929 QString s = QApplication::clipboard()->text(tmp, QClipboard::Clipboard);
930 paste_at(s, MusEGlobal::song->cpos(), max_distance, always_new_part, never_new_part, paste_into_part, amount, raster);
931 }
932
933 // if nothing is selected/relevant, this function returns NULL
parts_to_mime(const set<const Part * > & parts)934 QMimeData* parts_to_mime(const set<const Part*>& parts)
935 {
936
937 //---------------------------------------------------
938 // write events as XML into tmp file
939 //---------------------------------------------------
940
941 FILE* tmp = tmpfile();
942 if (tmp == 0)
943 {
944 fprintf(stderr, "EventCanvas::getTextDrag() fopen failed: %s\n", strerror(errno));
945 return 0;
946 }
947
948 Xml xml(tmp);
949 int level = 0;
950
951 bool midi=false;
952 bool wave=false;
953 for (set<const Part*>::iterator part=parts.begin(); part!=parts.end(); part++)
954 {
955 if ((*part)->track()->type() == MusECore::Track::MIDI)
956 midi=true;
957 else
958 wave=true;
959 (*part)->write(level, xml, true, true);
960 }
961 QString mimeString = "text/x-muse-mixedpartlist";
962 if (!midi)
963 mimeString = "text/x-muse-wavepartlist";
964 else if (!wave)
965 mimeString = "text/x-muse-midipartlist";
966 QMimeData *mimeData = file_to_mimedata(tmp, mimeString );
967 fclose(tmp);
968 return mimeData;
969 }
970
971 //---------------------------------------------------
972 // read datafile into mime Object
973 //---------------------------------------------------
file_to_mimedata(FILE * datafile,QString mimeType)974 QMimeData* file_to_mimedata(FILE *datafile, QString mimeType)
975 {
976
977 fflush(datafile);
978 struct stat f_stat;
979 if (fstat(fileno(datafile), &f_stat) == -1)
980 {
981 fprintf(stderr, "copy_notes() fstat failed:<%s>\n",
982 strerror(errno));
983 fclose(datafile);
984 return 0;
985 }
986 int n = f_stat.st_size;
987 char* fbuf = (char*)mmap(0, n+1, PROT_READ|PROT_WRITE,
988 MAP_PRIVATE, fileno(datafile), 0);
989 fbuf[n] = 0;
990
991 QByteArray data(fbuf);
992
993 QMimeData* md = new QMimeData();
994 md->setData(mimeType, data);
995
996 munmap(fbuf, n);
997
998 return md;
999 }
1000
read_eventlist_and_part(Xml & xml,EventList * el,int * part_id)1001 bool read_eventlist_and_part(Xml& xml, EventList* el, int* part_id) // true on success, false on failure
1002 {
1003 *part_id = -1;
1004
1005 for (;;)
1006 {
1007 Xml::Token token = xml.parse();
1008 const QString& tag = xml.s1();
1009 switch (token)
1010 {
1011 case Xml::Error:
1012 case Xml::End:
1013 return false;
1014
1015 case Xml::Attribut:
1016 if (tag == "part_id")
1017 *part_id = xml.s2().toInt();
1018 else
1019 printf("unknown attribute '%s' in read_eventlist_and_part(), ignoring it...\n", tag.toLatin1().data());
1020 break;
1021
1022 case Xml::TagStart:
1023 if (tag == "event")
1024 {
1025 Event e(Note);
1026 e.read(xml);
1027 el->add(e);
1028 }
1029 else
1030 xml.unknown("read_eventlist_and_part");
1031 break;
1032
1033 case Xml::TagEnd:
1034 if (tag == "eventlist")
1035 return true;
1036
1037 default:
1038 break;
1039 }
1040 }
1041 }
1042
paste_at(const QString & pt,int pos,int max_distance,bool always_new_part,bool never_new_part,const Part * paste_into_part,int amount,int raster)1043 void paste_at(const QString& pt, int pos, int max_distance, bool always_new_part, bool never_new_part, const Part* paste_into_part, int amount, int raster)
1044 {
1045 Undo operations;
1046 map<const Part*, unsigned> expand_map;
1047 map<const Part*, set<const Part*> > new_part_map;
1048
1049 QByteArray pt_= pt.toLatin1();
1050 Xml xml(pt_.constData());
1051 for (;;)
1052 {
1053 Xml::Token token = xml.parse();
1054 const QString& tag = xml.s1();
1055 switch (token)
1056 {
1057 case Xml::Error:
1058 case Xml::End:
1059 goto out_of_paste_at_for;
1060
1061 case Xml::TagStart:
1062 if (tag == "eventlist")
1063 {
1064 EventList el;
1065 int part_id;
1066
1067 if (read_eventlist_and_part(xml, &el, &part_id))
1068 {
1069 const Part* dest_part;
1070 Track* dest_track;
1071 const Part* old_dest_part;
1072
1073 if (paste_into_part == NULL)
1074 dest_part = partFromSerialNumber(part_id);
1075 else
1076 dest_part=paste_into_part;
1077
1078 if (dest_part == NULL)
1079 {
1080 printf("ERROR: destination part wasn't found. ignoring these events\n");
1081 }
1082 else
1083 {
1084 dest_track=dest_part->track();
1085 old_dest_part=dest_part;
1086 unsigned first_paste_tick = el.begin()->first + pos;
1087 bool create_new_part = ( (dest_part->tick() > first_paste_tick) || // dest_part begins too late
1088 ( ( (dest_part->endTick() + max_distance < first_paste_tick) || // dest_part is too far away
1089 always_new_part ) && !never_new_part ) ); // respect function arguments
1090
1091 for (int i=0;i<amount;i++)
1092 {
1093 unsigned curr_pos = pos + i*raster;
1094 first_paste_tick = el.begin()->first + curr_pos;
1095
1096 if (create_new_part)
1097 {
1098 dest_part = NULL;
1099 Part* newpart = dest_track->newPart();
1100 if(newpart)
1101 {
1102 newpart->setTick(MusEGlobal::sigmap.raster1(first_paste_tick, config.division));
1103 dest_part = newpart;
1104 new_part_map[old_dest_part].insert(dest_part);
1105 operations.push_back(UndoOp(UndoOp::AddPart, dest_part));
1106 }
1107 }
1108
1109 if(dest_part)
1110 {
1111 for (iEvent i = el.begin(); i != el.end(); ++i)
1112 {
1113 // If the destination part is a midi part, any midi event is allowed.
1114 // If the destination part is a wave part, any wave event is allowed.
1115 switch(i->second.type())
1116 {
1117 case Note:
1118 case Controller:
1119 case Sysex:
1120 case Meta:
1121 if(dest_part->type() == Pos::FRAMES)
1122 continue;
1123 break;
1124
1125 case Wave:
1126 // FIXME TODO: To support pasting wave events, some code below must be made agnostic.
1127 // For now just ignore wave events.
1128 //if(dest_part->type() == Pos::TICKS)
1129 continue;
1130 break;
1131 }
1132
1133 // FIXME TODO: To support pasting wave events, this code block and some code below it MUST be made position-agnostic.
1134 Event e = i->second.clone();
1135 int tick = e.tick() + curr_pos - dest_part->tick();
1136 if (tick<0)
1137 {
1138 printf("ERROR: trying to add event before current part! ignoring this event\n");
1139 continue;
1140 }
1141
1142 e.setTick(tick);
1143 e.setSelected(true); // No need to select clones, AddEvent operation below will take care of that.
1144
1145 if (e.endTick() > dest_part->lenTick()) // event exceeds part?
1146 {
1147 if (dest_part->hasHiddenEvents() & Part::RightEventsHidden) // auto-expanding is forbidden?
1148 {
1149 if (e.tick() < dest_part->lenTick())
1150 e.setLenTick(dest_part->lenTick() - e.tick()); // clip
1151 else
1152 e.setLenTick(0); // don't insert that note at all
1153 }
1154 else
1155 {
1156 if (e.endTick() > expand_map[dest_part])
1157 expand_map[dest_part]=e.endTick();
1158 }
1159 }
1160
1161 switch(e.type())
1162 {
1163 case Note:
1164 // Don't add Note event types if they have no length.
1165 // Notes are allowed to overlap. There is no DeleteEvent or ModifyEvent first.
1166 if(e.lenTick() != 0)
1167 operations.push_back(UndoOp(UndoOp::AddEvent, e, dest_part, false, false));
1168 break;
1169
1170 case Wave:
1171 // Don't add Wave event types if they have no length.
1172 if(e.lenFrame() != 0)
1173 {
1174 EventList el;
1175 // Compare time, and wave position, path, and start position.
1176 dest_part->events().findSimilarType(e, el, true, false, false, false,
1177 true, true, true);
1178 // Do NOT add the new wave event if it already exists at the position.
1179 // Don't event bother replacing it using DeletEvent or ModifyEvent.
1180 if(el.empty())
1181 {
1182 operations.push_back(UndoOp(UndoOp::AddEvent, e, dest_part, false, false));
1183 }
1184 else
1185 {
1186 // Delete all but one of them. There shouldn't be more than one wave event
1187 // at a time for a given wave event anyway.
1188 iEvent nie;
1189 for(iEvent ie = el.begin(); ie != el.end(); ++ie)
1190 {
1191 // Break on the second-last one, to leave one item intact.
1192 nie = ie;
1193 ++nie;
1194 if(nie == el.end())
1195 {
1196 break;
1197 }
1198
1199 operations.push_back(UndoOp(UndoOp::DeleteEvent, ie->second, dest_part, false, false));
1200 }
1201 }
1202
1203 }
1204 break;
1205
1206 case Controller:
1207 {
1208 EventList el;
1209 // Compare time and controller number (data A) only.
1210 dest_part->events().findSimilarType(e, el, true, true);
1211 // Delete them all. There shouldn't be more than one controller event
1212 // at a time for a given controller number anyway.
1213 for(iEvent ie = el.begin(); ie != el.end(); ++ie)
1214 {
1215 operations.push_back(UndoOp(UndoOp::DeleteEvent, ie->second, dest_part, true, true));
1216 }
1217
1218 // Do port controller values and clone parts.
1219 operations.push_back(UndoOp(UndoOp::AddEvent, e, dest_part, true, true));
1220 }
1221 break;
1222
1223 case Sysex:
1224 {
1225 EventList el;
1226 // Compare time and sysex data only.
1227 dest_part->events().findSimilarType(e, el, true);
1228 // Do NOT add the new sysex if it already exists at the position.
1229 // Don't even bother replacing it using DeletEvent or ModifyEvent.
1230 if(el.empty())
1231 {
1232 operations.push_back(UndoOp(UndoOp::AddEvent, e, dest_part, false, false));
1233 }
1234 else
1235 {
1236 // Delete all but one of them. There shouldn't be more than one sysex event
1237 // at a time for a given sysex anyway.
1238 iEvent nie;
1239 for(iEvent ie = el.begin(); ie != el.end(); ++ie)
1240 {
1241 // Break on the second-last one, to leave one item intact.
1242 nie = ie;
1243 ++nie;
1244 if(nie == el.end())
1245 {
1246 break;
1247 }
1248
1249 operations.push_back(UndoOp(UndoOp::DeleteEvent, ie->second, dest_part, false, false));
1250 }
1251 }
1252 }
1253 break;
1254
1255 case Meta:
1256 {
1257 EventList el;
1258 // Compare time and meta data only.
1259 dest_part->events().findSimilarType(e, el, true);
1260 // Do NOT add the new meta if it already exists at the position.
1261 // Don't even bother replacing it using DeletEvent or ModifyEvent.
1262 if(el.empty())
1263 {
1264 operations.push_back(UndoOp(UndoOp::AddEvent, e, dest_part, false, false));
1265 }
1266 else
1267 {
1268 // Delete all but one of them. There shouldn't be more than one meta event
1269 // at a time for a given meta anyway.
1270 iEvent nie;
1271 for(iEvent ie = el.begin(); ie != el.end(); ++ie)
1272 {
1273 // Break on the second-last one, to leave one item intact.
1274 nie = ie;
1275 ++nie;
1276 if(nie == el.end())
1277 {
1278 break;
1279 }
1280
1281 operations.push_back(UndoOp(UndoOp::DeleteEvent, ie->second, dest_part, false, false));
1282 }
1283 }
1284 }
1285 break;
1286 }
1287 }
1288 }
1289 }
1290 }
1291 }
1292 else
1293 {
1294 printf("ERROR: reading eventlist from clipboard failed. ignoring this one...\n");
1295 }
1296 }
1297 else
1298 xml.unknown("paste_at");
1299 break;
1300
1301 case Xml::Attribut:
1302 case Xml::TagEnd:
1303 default:
1304 break;
1305 }
1306 }
1307
1308 out_of_paste_at_for:
1309
1310 for (map<const Part*, unsigned>::iterator it = expand_map.begin(); it!=expand_map.end(); it++)
1311 if (it->second != it->first->lenTick())
1312 schedule_resize_all_same_len_clone_parts(it->first, it->second, operations);
1313
1314 MusEGlobal::song->informAboutNewParts(new_part_map); // must be called before apply. otherwise
1315 // pointer changes (by resize) screw it up
1316 MusEGlobal::song->applyOperationGroup(operations);
1317 MusEGlobal::song->update(SC_SELECTION | SC_PART_SELECTION);
1318 }
1319
select_all(const set<const Part * > & parts)1320 void select_all(const set<const Part*>& parts)
1321 {
1322 Undo operations;
1323 operations.combobreaker=true;
1324
1325 for (set<const Part*>::iterator part=parts.begin(); part!=parts.end(); part++)
1326 for (ciEvent ev_it=(*part)->events().begin(); ev_it!=(*part)->events().end(); ev_it++)
1327 {
1328 const Event& event=ev_it->second;
1329 operations.push_back(UndoOp(UndoOp::SelectEvent,event, *part, true, event.selected()));
1330 }
1331 MusEGlobal::song->applyOperationGroup(operations, MusECore::Song::OperationExecuteUpdate);
1332 }
1333
select_none(const set<const Part * > & parts)1334 void select_none(const set<const Part*>& parts)
1335 {
1336 Undo operations;
1337 operations.combobreaker=true;
1338
1339 for (set<const Part*>::iterator part=parts.begin(); part!=parts.end(); part++)
1340 for (ciEvent ev_it=(*part)->events().begin(); ev_it!=(*part)->events().end(); ev_it++)
1341 {
1342 const Event& event=ev_it->second;
1343 operations.push_back(UndoOp(UndoOp::SelectEvent,event, *part, false, event.selected()));
1344 }
1345 MusEGlobal::song->applyOperationGroup(operations, MusECore::Song::OperationExecuteUpdate);
1346 }
1347
select_invert(const set<const Part * > & parts)1348 void select_invert(const set<const Part*>& parts)
1349 {
1350 Undo operations;
1351 operations.combobreaker=true;
1352
1353 for (set<const Part*>::iterator part=parts.begin(); part!=parts.end(); part++)
1354 for (ciEvent ev_it=(*part)->events().begin(); ev_it!=(*part)->events().end(); ev_it++)
1355 {
1356 const Event& event=ev_it->second;
1357 operations.push_back(UndoOp(UndoOp::SelectEvent,event, *part, !event.selected(), event.selected()));
1358 }
1359 MusEGlobal::song->applyOperationGroup(operations, MusECore::Song::OperationExecuteUpdate);
1360 }
1361
select_in_loop(const set<const Part * > & parts)1362 void select_in_loop(const set<const Part*>& parts)
1363 {
1364 select_none(parts);
1365 Undo operations;
1366 operations.combobreaker=true;
1367
1368 for (set<const Part*>::iterator part=parts.begin(); part!=parts.end(); part++)
1369 for (ciEvent ev_it=(*part)->events().begin(); ev_it!=(*part)->events().end(); ev_it++)
1370 {
1371 const Event& event=ev_it->second;
1372 operations.push_back(UndoOp(UndoOp::SelectEvent,event, *part,
1373 (event.tick()>=MusEGlobal::song->lpos() && event.endTick()<=MusEGlobal::song->rpos()), event.selected()));
1374 }
1375 MusEGlobal::song->applyOperationGroup(operations, MusECore::Song::OperationExecuteUpdate);
1376 }
1377
select_not_in_loop(const set<const Part * > & parts)1378 void select_not_in_loop(const set<const Part*>& parts)
1379 {
1380 select_none(parts);
1381 Undo operations;
1382 operations.combobreaker=true;
1383
1384 for (set<const Part*>::iterator part=parts.begin(); part!=parts.end(); part++)
1385 for (ciEvent ev_it=(*part)->events().begin(); ev_it!=(*part)->events().end(); ev_it++)
1386 {
1387 const Event& event=ev_it->second;
1388 operations.push_back(UndoOp(UndoOp::SelectEvent,event, *part,
1389 !(event.tick()>=MusEGlobal::song->lpos() && event.endTick()<=MusEGlobal::song->rpos()), event.selected()));
1390 }
1391 MusEGlobal::song->applyOperationGroup(operations, MusECore::Song::OperationExecuteUpdate);
1392 }
1393
tracks_are_selected()1394 bool tracks_are_selected()
1395 {
1396 const TrackList* tl = MusEGlobal::song->tracks();
1397 for(ciTrack it = tl->begin(); it != tl->end(); ++it)
1398 if((*it)->selected())
1399 return true;
1400 return false;
1401 }
1402
parts_are_selected()1403 bool parts_are_selected()
1404 {
1405 const TrackList* tl = MusEGlobal::song->tracks();
1406 for(ciTrack it = tl->begin(); it != tl->end(); ++it)
1407 {
1408 const PartList* pl = (*it)->cparts();
1409 for(ciPart ip = pl->begin(); ip != pl->end(); ++ip)
1410 if(ip->second->selected())
1411 return true;
1412 }
1413 return false;
1414 }
1415
1416
1417
shrink_parts(int raster)1418 void shrink_parts(int raster)
1419 {
1420 Undo operations;
1421
1422 unsigned min_len;
1423 if (raster<0) raster=MusEGlobal::config.division;
1424 if (raster>=0) min_len=raster; else min_len=MusEGlobal::config.division;
1425
1426 TrackList* tracks = MusEGlobal::song->tracks();
1427 for (iTrack track = tracks->begin(); track != tracks->end(); track++)
1428 for (iPart part = (*track)->parts()->begin(); part != (*track)->parts()->end(); part++)
1429 if (part->second->selected())
1430 {
1431 unsigned len=0;
1432
1433 for (ciEvent ev=part->second->events().begin(); ev!=part->second->events().end(); ev++)
1434 if (ev->second.endTick() > len)
1435 len=ev->second.endTick();
1436
1437 if (raster) len=ceil((float)len/raster)*raster;
1438 if (len<min_len) len=min_len;
1439
1440 if (len < part->second->lenTick())
1441 operations.push_back(UndoOp(UndoOp::ModifyPartLength, part->second, part->second->lenValue(), len, 0, Pos::TICKS));
1442 }
1443
1444 MusEGlobal::song->applyOperationGroup(operations);
1445 }
1446
1447
schedule_resize_all_same_len_clone_parts(const Part * part,unsigned new_len,Undo & operations)1448 void schedule_resize_all_same_len_clone_parts(const Part* part, unsigned new_len, Undo& operations)
1449 {
1450 QSet<const Part*> already_done;
1451
1452 for (Undo::iterator op_it=operations.begin(); op_it!=operations.end();op_it++)
1453 if (op_it->type==UndoOp::DeletePart)
1454 already_done.insert(op_it->part);
1455
1456 unsigned old_len = part->lenValue();
1457 if (old_len!=new_len)
1458 {
1459 const Part* part_it=part;
1460 do
1461 {
1462 if (part_it->lenValue()==old_len && !already_done.contains(part_it))
1463 operations.push_back(UndoOp(UndoOp::ModifyPartLength, part_it, old_len, new_len, 0, part->type()));
1464
1465 part_it=part_it->nextClone();
1466 } while (part_it!=part);
1467 }
1468 }
1469
expand_parts(int raster)1470 void expand_parts(int raster)
1471 {
1472 Undo operations;
1473
1474 unsigned min_len;
1475 if (raster<0) raster=MusEGlobal::config.division;
1476 if (raster>=0) min_len=raster; else min_len=MusEGlobal::config.division;
1477
1478 TrackList* tracks = MusEGlobal::song->tracks();
1479 for (iTrack track = tracks->begin(); track != tracks->end(); track++)
1480 for (iPart part = (*track)->parts()->begin(); part != (*track)->parts()->end(); part++)
1481 if (part->second->selected())
1482 {
1483 unsigned len=part->second->lenTick();
1484
1485 for (ciEvent ev=part->second->events().begin(); ev!=part->second->events().end(); ev++)
1486 if (ev->second.endTick() > len)
1487 len=ev->second.endTick();
1488
1489 if (raster) len=ceil((float)len/raster)*raster;
1490 if (len<min_len) len=min_len;
1491
1492 if (len > part->second->lenTick())
1493 operations.push_back(UndoOp(UndoOp::ModifyPartLength, part->second, part->second->lenValue(), len, 0, Pos::TICKS));
1494 }
1495
1496 MusEGlobal::song->applyOperationGroup(operations);
1497 }
1498
clean_parts()1499 void clean_parts()
1500 {
1501 Undo operations;
1502 set<const Part*> already_processed;
1503
1504 TrackList* tracks = MusEGlobal::song->tracks();
1505 for (iTrack track = tracks->begin(); track != tracks->end(); track++)
1506 for (iPart part = (*track)->parts()->begin(); part != (*track)->parts()->end(); part++)
1507 if ((part->second->selected()) && (already_processed.find(part->second)==already_processed.end()))
1508 {
1509 // find out the length of the longest clone of this part;
1510 // avoid processing eventlist multiple times (because of
1511 // multiple clones)
1512 unsigned len=0;
1513
1514 const Part* part_it=part->second;
1515 do
1516 {
1517 if (part_it->lenTick() > len)
1518 len=part_it->lenTick();
1519
1520 already_processed.insert(part_it);
1521 part_it=part_it->nextClone();
1522 } while ((part_it!=part->second) && (part_it!=NULL));
1523
1524
1525 // erase all events exceeding the longest clone of this part
1526 // (i.e., erase all hidden events) or shorten them
1527 for (ciEvent ev=part->second->events().begin(); ev!=part->second->events().end(); ev++)
1528 if (ev->second.tick() >= len)
1529 operations.push_back(UndoOp(UndoOp::DeleteEvent, ev->second, part->second, true, true));
1530 else if (ev->second.endTick() > len)
1531 {
1532 Event new_event = ev->second.clone();
1533 new_event.setLenTick(len - ev->second.tick());
1534
1535 operations.push_back(UndoOp(UndoOp::ModifyEvent, new_event, ev->second, part->second, true, true));
1536 }
1537 }
1538
1539 MusEGlobal::song->applyOperationGroup(operations);
1540 }
1541
1542
merge_with_next_part(const Part * oPart)1543 bool merge_with_next_part(const Part* oPart)
1544 {
1545 const Track* track = oPart->track();
1546
1547 if(track->type() != Track::WAVE && !track->isMidiTrack())
1548 return false;
1549
1550 const PartList* pl = track->cparts();
1551 const Part* nextPart = 0;
1552
1553 for (ciPart ip = pl->begin(); ip != pl->end(); ++ip)
1554 {
1555 if (ip->second == oPart)
1556 {
1557 ++ip;
1558 if (ip == pl->end())
1559 return false;
1560 nextPart = ip->second;
1561 break;
1562 }
1563 }
1564
1565 if (nextPart)
1566 {
1567 set<const Part*> parts;
1568 parts.insert(oPart);
1569 parts.insert(nextPart);
1570 return merge_parts(parts);
1571 }
1572 else
1573 return false;
1574 }
1575
merge_selected_parts()1576 bool merge_selected_parts()
1577 {
1578 set<const Part*> temp = get_all_selected_parts();
1579 return merge_parts(temp);
1580 }
1581
merge_parts(const set<const Part * > & parts)1582 bool merge_parts(const set<const Part*>& parts)
1583 {
1584 set<const Track*> tracks;
1585 for (set<const Part*>::iterator it=parts.begin(); it!=parts.end(); it++)
1586 tracks.insert( (*it)->track() );
1587
1588 Undo operations;
1589
1590 // process tracks separately
1591 for (set<const Track*>::iterator t_it=tracks.begin(); t_it!=tracks.end(); t_it++)
1592 {
1593 const Track* track=*t_it;
1594
1595 unsigned begin=INT_MAX, end=0;
1596 const Part* first_part=NULL;
1597
1598 // find begin of the first and end of the last part
1599 for (set<const Part*>::iterator it=parts.begin(); it!=parts.end(); it++)
1600 if ((*it)->track()==track)
1601 {
1602 const Part* p=*it;
1603 if (p->tick() < begin)
1604 {
1605 begin=p->tick();
1606 first_part=p;
1607 }
1608
1609 if (p->endTick() > end)
1610 end=p->endTick();
1611 }
1612
1613 if (begin==INT_MAX || end==0)
1614 {
1615 printf("THIS SHOULD NEVER HAPPEN: begin==INT_MAX || end==0 in merge_parts()\n");
1616 continue; // skip the actual work, as we cannot work under errornous conditions.
1617 }
1618
1619 // create and prepare the new part
1620 Part* new_part = first_part->duplicateEmpty();
1621 new_part->setTick(begin);
1622 new_part->setLenTick(end-begin);
1623
1624 // copy all events from the source parts into the new part
1625 for (set<const Part*>::iterator p_it=parts.begin(); p_it!=parts.end(); p_it++)
1626 if ((*p_it)->track()==track)
1627 {
1628 const EventList& old_el= (*p_it)->events();
1629 for (ciEvent ev_it=old_el.begin(); ev_it!=old_el.end(); ev_it++)
1630 {
1631 Event new_event=ev_it->second.clone();
1632 new_event.setTick( new_event.tick() + (*p_it)->tick() - new_part->tick() );
1633 new_part->addEvent(new_event);
1634 }
1635 }
1636
1637 // delete all the source parts
1638 for (set<const Part*>::iterator it=parts.begin(); it!=parts.end(); it++)
1639 if ((*it)->track()==track)
1640 operations.push_back( UndoOp(UndoOp::DeletePart, *it) );
1641 // and add the new one
1642 operations.push_back( UndoOp(UndoOp::AddPart, new_part) );
1643 }
1644
1645 return MusEGlobal::song->applyOperationGroup(operations);
1646 }
1647
split_part(const Part * part,int tick)1648 bool split_part(const Part* part, int tick)
1649 {
1650 int l1 = tick - part->tick();
1651 int l2 = part->lenTick() - l1;
1652 if (l1 <= 0 || l2 <= 0)
1653 return false;
1654 Part* p1;
1655 Part* p2;
1656 part->splitPart(tick, p1, p2);
1657
1658 MusEGlobal::song->informAboutNewParts(part, p1);
1659 MusEGlobal::song->informAboutNewParts(part, p2);
1660
1661 Undo operations;
1662 operations.push_back(UndoOp(UndoOp::DeletePart, part));
1663 operations.push_back(UndoOp(UndoOp::AddPart, p1));
1664 operations.push_back(UndoOp(UndoOp::AddPart, p2));
1665 return MusEGlobal::song->applyOperationGroup(operations);
1666 }
1667
delete_selected_parts()1668 bool delete_selected_parts()
1669 {
1670 Undo operations;
1671 bool partSelected = false;
1672
1673 TrackList* tl = MusEGlobal::song->tracks();
1674
1675 for (iTrack it = tl->begin(); it != tl->end(); ++it)
1676 {
1677 PartList* pl = (*it)->parts();
1678 for (iPart ip = pl->begin(); ip != pl->end(); ++ip)
1679 {
1680 if (ip->second->selected())
1681 {
1682 operations.push_back(UndoOp(UndoOp::DeletePart,ip->second));
1683 partSelected = true;
1684 }
1685 }
1686 }
1687
1688 MusEGlobal::song->applyOperationGroup(operations);
1689
1690 return partSelected;
1691 }
1692
1693
1694 //=============================================================================
1695 // BEGIN item-based functions
1696 //=============================================================================
1697
1698
1699 // For erasing existing target controller events before pasting source controller events.
1700 typedef std::pair<unsigned long /*t0*/, unsigned long /*t1*/ > PasteEraseMapInsertPair_t;
1701 typedef std::map<unsigned long /*t0*/, unsigned long /*t1*/> PasteEraseMap_t;
1702 typedef PasteEraseMap_t::iterator iPasteEraseMap;
1703 typedef PasteEraseMap_t::const_iterator ciPasteEraseMap;
1704 typedef std::pair<int /*ctlnum*/, PasteEraseMap_t > PasteEraseCtlMapPair_t;
1705 typedef std::map<int /*ctlnum*/, PasteEraseMap_t> PasteEraseCtlMap_t;
1706 typedef PasteEraseCtlMap_t::iterator iPasteEraseCtlMap;
1707 typedef PasteEraseCtlMap_t::const_iterator ciPasteEraseCtlMap;
1708
1709 class PasteEraseCtlMap : public PasteEraseCtlMap_t
1710 {
1711 private:
1712 bool _erase_controllers_wysiwyg;
1713 bool _erase_controllers_inclusive;
1714
1715 public:
PasteEraseCtlMap(bool erase_controllers_wysiwyg,bool erase_controllers_inclusive)1716 PasteEraseCtlMap(bool erase_controllers_wysiwyg,
1717 bool erase_controllers_inclusive) :
1718 _erase_controllers_wysiwyg(erase_controllers_wysiwyg),
1719 _erase_controllers_inclusive(erase_controllers_inclusive) { }
1720
1721 // Add an item. All ctl_time must be sorted beforehand.
1722 // Be sure to call tidy() after all items have been added.
1723 void add(int ctl_num, unsigned int ctl_time,
1724 unsigned int len_val);
1725 // Tidy up the very last items in the list.
1726 void tidy();
1727 };
1728
add(int ctl_num,unsigned int ctl_time,unsigned int len_val)1729 void PasteEraseCtlMap::add(int ctl_num, unsigned int ctl_time,
1730 unsigned int len_val)
1731 {
1732 unsigned long ctl_end_time;
1733
1734 if(len_val > 0)
1735 ctl_end_time = ctl_time + len_val;
1736 else
1737 ctl_end_time = ctl_time + 1;
1738
1739 iPasteEraseCtlMap icm = find(ctl_num);
1740 if(icm == end())
1741 {
1742 PasteEraseMap_t new_tmap;
1743 new_tmap.insert(PasteEraseMapInsertPair_t(ctl_time, ctl_end_time));
1744 insert(PasteEraseCtlMapPair_t(ctl_num, new_tmap));
1745 }
1746 else
1747 {
1748 PasteEraseMap_t& tmap = icm->second;
1749 // The event times must be sorted already. So this would always return end().
1750 //iPasteEraseMap itm = tmap.upper_bound(ctl_time);
1751 iPasteEraseMap itm = tmap.end();
1752
1753 if(itm != tmap.begin())
1754 {
1755 --itm;
1756
1757 iPasteEraseMap prev_itm_2 = tmap.end();
1758 if(itm != tmap.begin())
1759 {
1760 prev_itm_2 = itm;
1761 --prev_itm_2;
1762 }
1763
1764 if((itm->second >= ctl_time) || _erase_controllers_inclusive)
1765 {
1766 if(_erase_controllers_inclusive)
1767 itm->second = ctl_time;
1768
1769 if(prev_itm_2 != tmap.end())
1770 {
1771 if((prev_itm_2->second >= itm->first) || _erase_controllers_inclusive)
1772 {
1773 prev_itm_2->second = itm->second;
1774 tmap.erase(itm);
1775 }
1776 }
1777
1778 tmap.insert(PasteEraseMapInsertPair_t(ctl_time, ctl_end_time));
1779 }
1780 else
1781 {
1782 // If we want wysiwyg pasting, we erase existing events up to
1783 // the end-time of the last tmap item which ended a contiguous
1784 // 'cluster' of items. Otherwise we ONLY erase UP TO AND INCLUDING
1785 // the start-time of that last tmap item. So we 'truncate' that
1786 // last item in the cluster by setting the end-time to the start-time,
1787 // so that the gathering routine below knows to erase that last
1788 // single-time position.
1789
1790 if(!_erase_controllers_wysiwyg)
1791 itm->second = itm->first + 1;
1792
1793 if(prev_itm_2 != tmap.end())
1794 {
1795 if(prev_itm_2->second >= itm->first)
1796 {
1797 prev_itm_2->second = itm->second;
1798 tmap.erase(itm);
1799 }
1800 }
1801
1802 tmap.insert(PasteEraseMapInsertPair_t(ctl_time, ctl_end_time));
1803 }
1804 }
1805 }
1806 }
1807
tidy()1808 void PasteEraseCtlMap::tidy()
1809 {
1810 // Tidy up the very last items in the list.
1811 for(iPasteEraseCtlMap icm = begin(); icm != end(); ++icm)
1812 {
1813 PasteEraseMap_t& tmap = icm->second;
1814 iPasteEraseMap itm = tmap.end();
1815 if(itm != tmap.begin())
1816 {
1817 --itm;
1818
1819 if(!_erase_controllers_wysiwyg)
1820 itm->second = itm->first + 1;
1821
1822 if(itm != tmap.begin())
1823 {
1824 iPasteEraseMap itm_2 = itm;
1825 --itm_2;
1826 if((itm_2->second >= itm->second) || _erase_controllers_inclusive)
1827 {
1828 itm_2->second = itm->second;
1829 tmap.erase(itm);
1830 }
1831 }
1832 }
1833 }
1834 }
1835
erase_items(TagEventList * tag_list,int velo_threshold,bool velo_thres_used,int len_threshold,bool len_thres_used)1836 bool erase_items(TagEventList* tag_list, int velo_threshold, bool velo_thres_used, int len_threshold, bool len_thres_used)
1837 {
1838 Undo operations;
1839
1840 const Part* part;
1841
1842 for(ciTagEventList itl = tag_list->begin(); itl != tag_list->end(); ++itl)
1843 {
1844 part = itl->first;
1845 const EventList& el = itl->second.evlist();
1846 for(ciEvent ie = el.begin(); ie != el.end(); ie++)
1847 {
1848 // This operation can apply to any event.
1849 const Event& e = ie->second;
1850
1851 // FIXME TODO Likely need agnostic Pos or frames rather than ticks if WaveCanvas is to use this.
1852 if ( e.type() != Note || (!velo_thres_used && !len_thres_used) ||
1853 (velo_thres_used && e.velo() < velo_threshold) ||
1854 (len_thres_used && int(e.lenTick()) < len_threshold) )
1855 {
1856 operations.push_back(UndoOp(UndoOp::DeleteEvent, e, part, true, true));
1857 }
1858 }
1859 }
1860
1861 return MusEGlobal::song->applyOperationGroup(operations);
1862 }
1863
crescendo_items(TagEventList * tag_list,int start_val,int end_val,bool absolute)1864 bool crescendo_items(TagEventList* tag_list, int start_val, int end_val, bool absolute)
1865 {
1866 const Pos& from = MusEGlobal::song->lPos();
1867 const Pos& to = MusEGlobal::song->rPos();
1868 if(to <= from)
1869 return false;
1870
1871 Undo operations;
1872 Pos pos;
1873 float curr_val;
1874 unsigned int pos_val = (to - from).posValue();
1875 const Part* part;
1876
1877 for(ciTagEventList itl = tag_list->begin(); itl != tag_list->end(); ++itl)
1878 {
1879 part = itl->first;
1880 const EventList& el = itl->second.evlist();
1881 for(ciEvent ie = el.begin(); ie != el.end(); ie++)
1882 {
1883 const Event& e = ie->second;
1884
1885 // This operation can only apply to notes.
1886 if(e.type() != Note)
1887 continue;
1888
1889 pos = e.pos() + *part;
1890 curr_val = (float)start_val + (float)(end_val - start_val) * (pos - from).posValue() / pos_val;
1891
1892 Event newEvent = e.clone();
1893 int velo = e.velo();
1894
1895 if (absolute)
1896 velo=curr_val;
1897 else
1898 velo=curr_val*velo/100;
1899
1900 if (velo > 127) velo=127;
1901 if (velo <= 0) velo=1;
1902 newEvent.setVelo(velo);
1903
1904 operations.push_back(UndoOp(UndoOp::ModifyEvent, newEvent, e, part, false, false));
1905 }
1906 }
1907
1908 return MusEGlobal::song->applyOperationGroup(operations);
1909 }
1910
delete_overlaps_items(TagEventList * tag_list)1911 bool delete_overlaps_items(TagEventList* tag_list)
1912 {
1913 Undo operations;
1914
1915 set<const Event*> deleted_events;
1916 int new_len;
1917 Event new_event1;
1918 const Part* part;
1919
1920 for(ciTagEventList itl = tag_list->begin(); itl != tag_list->end(); ++itl)
1921 {
1922 part = itl->first;
1923 const EventList& el = itl->second.evlist();
1924 for(ciEvent ie = el.begin(); ie != el.end(); ie++)
1925 {
1926 const Event& e = ie->second;
1927
1928 // This operation can only apply to notes.
1929 if(e.type() != Note)
1930 continue;
1931
1932 // Has this event already been scheduled for deletion? Ignore it.
1933 if(deleted_events.find(&e) != deleted_events.end())
1934 continue;
1935
1936 ciEvent ie2 = ie;
1937 ++ie2;
1938 for( ; ie2 != el.end(); ++ie2)
1939 {
1940 const Event& e2 = ie2->second;
1941 // This operation can only apply to notes.
1942 if(e2.type() != Note)
1943 continue;
1944
1945 // Do e2 and e point to the same event? Or has e2 already been scheduled for deletion? Ignore it.
1946 if(e == e2 || deleted_events.find(&e2) != deleted_events.end())
1947 continue;
1948
1949 if ( (e.pitch() == e2.pitch()) &&
1950 (e.tick() <= e2.tick()) &&
1951 (e.endTick() > e2.tick()) ) //they overlap
1952 {
1953 new_len = e2.tick() - e.tick();
1954
1955 if(new_len==0)
1956 {
1957 operations.push_back(UndoOp(UndoOp::DeleteEvent, e2, part, false, false));
1958 deleted_events.insert(&e2);
1959 }
1960 else
1961 {
1962 new_event1 = e.clone();
1963 new_event1.setLenTick(new_len);
1964
1965 operations.push_back(UndoOp(UndoOp::ModifyEvent, new_event1, e, part, false, false));
1966
1967 // After resizing the event, it should not be necessary to continue with any further
1968 // events in this loop since any more sorted events will come at or AFTER e2's position
1969 // which we have just resized the end of e to.
1970 break;
1971 }
1972 }
1973 }
1974 }
1975 }
1976
1977 return MusEGlobal::song->applyOperationGroup(operations);
1978 }
1979
modify_notelen_items(TagEventList * tag_list,int rate,int offset)1980 bool modify_notelen_items(TagEventList* tag_list, int rate, int offset)
1981 {
1982 if(rate == 100 && offset == 0)
1983 return false;
1984
1985 Undo operations;
1986
1987 unsigned int len;
1988 map<const Part*, int> partlen;
1989 Event newEvent;
1990 const Part* part;
1991
1992 for(ciTagEventList itl = tag_list->begin(); itl != tag_list->end(); ++itl)
1993 {
1994 part = itl->first;
1995 const EventList& el = itl->second.evlist();
1996 for(ciEvent ie = el.begin(); ie != el.end(); ie++)
1997 {
1998 const Event& e = ie->second;
1999
2000 // This operation can only apply to notes.
2001 if(e.type() != Note)
2002 continue;
2003
2004 len = e.lenTick(); //prevent compiler warning: comparison signed/unsigned
2005
2006 len = (len * rate) / 100;
2007 len += offset;
2008
2009 if (len <= 0)
2010 len = 1;
2011
2012 if ((e.tick()+len > part->lenTick()) && (!(part->hasHiddenEvents() & Part::RightEventsHidden)))
2013 partlen[part] = e.tick() + len; // schedule auto-expanding
2014
2015 if (e.lenTick() != len)
2016 {
2017 newEvent = e.clone();
2018 newEvent.setLenTick(len);
2019 operations.push_back(UndoOp(UndoOp::ModifyEvent, newEvent, e, part, false, false));
2020 }
2021 }
2022
2023 for (map<const Part*, int>::iterator it=partlen.begin(); it!=partlen.end(); it++)
2024 schedule_resize_all_same_len_clone_parts(it->first, it->second, operations);
2025 }
2026
2027 return MusEGlobal::song->applyOperationGroup(operations);
2028 }
2029
legato_items(TagEventList * tag_list,int min_len,bool dont_shorten)2030 bool legato_items(TagEventList* tag_list, int min_len, bool dont_shorten)
2031 {
2032 Undo operations;
2033
2034 if (min_len<=0) min_len=1;
2035
2036 unsigned len = INT_MAX;
2037 bool relevant;
2038 Event new_event1;
2039 const Part* part;
2040
2041 for(ciTagEventList itl = tag_list->begin(); itl != tag_list->end(); ++itl)
2042 {
2043 part = itl->first;
2044 const EventList& el = itl->second.evlist();
2045 for(ciEvent ie = el.begin(); ie != el.end(); ie++)
2046 {
2047 const Event& e = ie->second;
2048
2049 // This operation can only apply to notes.
2050 if(e.type() != Note)
2051 continue;
2052
2053 ciEvent ie2 = ie;
2054 ++ie2;
2055 for( ; ie2 != el.end(); ++ie2)
2056 {
2057 const Event& e2 = ie2->second;
2058 // This operation can only apply to notes.
2059 if(e2.type() != Note)
2060 continue;
2061
2062 relevant = (e2.tick() >= e.tick() + min_len);
2063 if (dont_shorten)
2064 relevant = relevant && (e2.tick() >= e.endTick());
2065
2066 if ( relevant && // they're not too near (respect min_len and dont_shorten)
2067 (e2.tick() - e.tick() < len ) ) // that's the nearest relevant following note
2068 len = e2.tick() - e.tick();
2069 }
2070
2071 if (len==INT_MAX) len = e.lenTick(); // if no following note was found, keep the length
2072
2073 if (e.lenTick() != len)
2074 {
2075 new_event1 = e.clone();
2076 new_event1.setLenTick(len);
2077
2078 operations.push_back(UndoOp(UndoOp::ModifyEvent, new_event1, e, part, false, false));
2079 }
2080 }
2081 }
2082
2083 return MusEGlobal::song->applyOperationGroup(operations);
2084 }
2085
move_items(TagEventList * tag_list,signed int ticks)2086 bool move_items(TagEventList* tag_list, signed int ticks)
2087 {
2088 if(ticks == 0)
2089 return false;
2090
2091 Undo operations;
2092 map<const Part*, int> partlen;
2093
2094 bool del;
2095 Event newEvent;
2096 const Part* part;
2097
2098 for(ciTagEventList itl = tag_list->begin(); itl != tag_list->end(); ++itl)
2099 {
2100 part = itl->first;
2101 const EventList& el = itl->second.evlist();
2102 for(ciEvent ie = el.begin(); ie != el.end(); ie++)
2103 {
2104 const Event& e = ie->second;
2105
2106 del = false;
2107
2108 // This operation can only apply to notes.
2109 if(e.type() != Note)
2110 continue;
2111
2112 newEvent = e.clone();
2113 if ((signed)e.tick() + ticks < 0) //don't allow moving before the part's begin
2114 newEvent.setTick(0);
2115 else
2116 newEvent.setTick(e.tick() + ticks);
2117
2118 if (newEvent.endTick() > part->lenTick()) //if exceeding the part's end:
2119 {
2120 if (part->hasHiddenEvents() & Part::RightEventsHidden) // auto-expanding is forbidden, clip
2121 {
2122 if (part->lenTick() > newEvent.tick())
2123 newEvent.setLenTick(part->lenTick() - newEvent.tick());
2124 else
2125 del = true; //if the new length would be <= 0, erase the note
2126 }
2127 else
2128 partlen[part] = newEvent.endTick(); // schedule auto-expanding
2129 }
2130
2131 if (del == false)
2132 operations.push_back(UndoOp(UndoOp::ModifyEvent, newEvent, e, part, false, false));
2133 else
2134 operations.push_back(UndoOp(UndoOp::DeleteEvent, e, part, false, false));
2135 }
2136
2137 for (map<const Part*, int>::iterator it=partlen.begin(); it!=partlen.end(); it++)
2138 schedule_resize_all_same_len_clone_parts(it->first, it->second, operations);
2139 }
2140
2141 return MusEGlobal::song->applyOperationGroup(operations);
2142 }
2143
quantize_items(TagEventList * tag_list,int raster_idx,bool quant_len,int strength,int swing,int threshold)2144 bool quantize_items(TagEventList* tag_list, int raster_idx, bool quant_len, int strength, int swing, int threshold)
2145 {
2146 const int rv = MusEGui::functionQuantizeRasterVals[raster_idx];
2147 if(rv <= 0)
2148 return false;
2149
2150 const int raster = (MusEGlobal::config.division*4) / rv;
2151
2152 Undo operations;
2153
2154 unsigned begin_tick;
2155 int begin_diff;
2156 unsigned len;
2157 unsigned end_tick;
2158 int len_diff;
2159 Event newEvent;
2160 const Part* part;
2161
2162 for(ciTagEventList itl = tag_list->begin(); itl != tag_list->end(); ++itl)
2163 {
2164 part = itl->first;
2165 const EventList& el = itl->second.evlist();
2166 for(ciEvent ie = el.begin(); ie != el.end(); ie++)
2167 {
2168 const Event& e = ie->second;
2169
2170 // This operation can only apply to notes.
2171 if(e.type() != Note)
2172 continue;
2173
2174 begin_tick = e.tick() + part->tick();
2175 begin_diff = quantize_tick(begin_tick, raster, swing) - begin_tick;
2176
2177 if (abs(begin_diff) > threshold)
2178 begin_tick = begin_tick + begin_diff*strength/100;
2179
2180 len = e.lenTick();
2181
2182 end_tick = begin_tick + len;
2183 len_diff = quantize_tick(end_tick, raster, swing) - end_tick;
2184
2185 if ((abs(len_diff) > threshold) && quant_len)
2186 len = len + len_diff*strength/100;
2187
2188 if (len <= 0)
2189 len = 1;
2190
2191
2192 if ( (e.lenTick() != len) || (e.tick() + part->tick() != begin_tick) )
2193 {
2194 newEvent = e.clone();
2195 newEvent.setTick(begin_tick - part->tick());
2196 newEvent.setLenTick(len);
2197 operations.push_back(UndoOp(UndoOp::ModifyEvent, newEvent, e, part, false, false));
2198 }
2199 }
2200 }
2201
2202 return MusEGlobal::song->applyOperationGroup(operations);
2203 }
2204
set_notelen_items(TagEventList * tag_list,int len)2205 bool set_notelen_items(TagEventList* tag_list, int len)
2206 {
2207 return modify_notelen_items(tag_list, 0, len);
2208 }
2209
transpose_items(TagEventList * tag_list,signed int halftonesteps)2210 bool transpose_items(TagEventList* tag_list, signed int halftonesteps)
2211 {
2212 if(halftonesteps == 0)
2213 return false;
2214
2215 Undo operations;
2216
2217 Event newEvent;
2218 int pitch;
2219 const Part* part;
2220
2221 for(ciTagEventList itl = tag_list->begin(); itl != tag_list->end(); ++itl)
2222 {
2223 part = itl->first;
2224 const EventList& el = itl->second.evlist();
2225 for(ciEvent ie = el.begin(); ie != el.end(); ie++)
2226 {
2227 const Event& e = ie->second;
2228
2229 // This operation can only apply to notes.
2230 if(e.type() != Note)
2231 continue;
2232
2233 newEvent = e.clone();
2234 pitch = e.pitch() + halftonesteps;
2235 if (pitch > 127) pitch = 127;
2236 if (pitch < 0) pitch = 0;
2237 newEvent.setPitch(pitch);
2238 operations.push_back(UndoOp(UndoOp::ModifyEvent, newEvent, e, part, false, false));
2239 }
2240 }
2241
2242 return MusEGlobal::song->applyOperationGroup(operations);
2243 }
2244
modify_velocity_items(TagEventList * tag_list,int rate,int offset)2245 bool modify_velocity_items(TagEventList* tag_list, int rate, int offset)
2246 {
2247 if(rate == 100 && offset == 0)
2248 return false;
2249
2250 Undo operations;
2251 int velo;
2252 Event newEvent;
2253 const Part* part;
2254
2255 for(ciTagEventList itl = tag_list->begin(); itl != tag_list->end(); ++itl)
2256 {
2257 part = itl->first;
2258 const EventList& el = itl->second.evlist();
2259 for(ciEvent ie = el.begin(); ie != el.end(); ie++)
2260 {
2261 const Event& e = ie->second;
2262
2263 // This operation can only apply to notes.
2264 if(e.type() != Note)
2265 continue;
2266
2267 velo = e.velo();
2268
2269 velo = (velo * rate) / 100;
2270 velo += offset;
2271
2272 if (velo <= 0)
2273 velo = 1;
2274 else if (velo > 127)
2275 velo = 127;
2276
2277 if (e.velo() != velo)
2278 {
2279 newEvent = e.clone();
2280 newEvent.setVelo(velo);
2281 operations.push_back(UndoOp(UndoOp::ModifyEvent, newEvent, e, part, false, false));
2282 }
2283 }
2284 }
2285
2286 return MusEGlobal::song->applyOperationGroup(operations);
2287 }
2288
modify_off_velocity_items(TagEventList * tag_list,int rate,int offset)2289 bool modify_off_velocity_items(TagEventList* tag_list, int rate, int offset)
2290 {
2291 if(rate == 100 && offset == 0)
2292 return false;
2293
2294 Undo operations;
2295 int velo;
2296 Event newEvent;
2297 const Part* part;
2298
2299 for(ciTagEventList itl = tag_list->begin(); itl != tag_list->end(); ++itl)
2300 {
2301 part = itl->first;
2302 const EventList& el = itl->second.evlist();
2303 for(ciEvent ie = el.begin(); ie != el.end(); ie++)
2304 {
2305 const Event& e = ie->second;
2306
2307 // This operation can only apply to notes.
2308 if(e.type() != Note)
2309 continue;
2310
2311 velo = e.veloOff();
2312
2313 velo = (velo * rate) / 100;
2314 velo += offset;
2315
2316 if (velo <= 0)
2317 velo = 1;
2318 else if (velo > 127)
2319 velo = 127;
2320
2321 if (e.veloOff() != velo)
2322 {
2323 newEvent = e.clone();
2324 newEvent.setVeloOff(velo);
2325 operations.push_back(UndoOp(UndoOp::ModifyEvent, newEvent, e, part, false, false));
2326 }
2327 }
2328 }
2329
2330 return MusEGlobal::song->applyOperationGroup(operations);
2331 }
2332
copy_items(TagEventList * tag_list)2333 void copy_items(TagEventList* tag_list)
2334 {
2335 QMimeData* drag = cut_or_copy_tagged_items_to_mime(tag_list);
2336
2337 if (drag)
2338 QApplication::clipboard()->setMimeData(drag, QClipboard::Clipboard);
2339 }
2340
cut_items(TagEventList * tag_list)2341 bool cut_items(TagEventList* tag_list)
2342 {
2343 QMimeData* drag = cut_or_copy_tagged_items_to_mime(tag_list, true);
2344
2345 if(drag)
2346 {
2347 QApplication::clipboard()->setMimeData(drag, QClipboard::Clipboard);
2348 return true;
2349 }
2350
2351 return false;
2352 }
2353
2354 // if nothing is selected/relevant, this function returns NULL
cut_or_copy_tagged_items_to_mime(TagEventList * tag_list,bool cut_mode)2355 QMimeData* cut_or_copy_tagged_items_to_mime(TagEventList* tag_list, bool cut_mode)
2356 {
2357 if(tag_list->empty())
2358 return NULL;
2359
2360 QTemporaryFile tmp;
2361 if(!tmp.open())
2362 {
2363 fprintf(stderr, "cut_or_copy_tagged_items_to_mime(): ERROR: Failed to open temporary file\n");
2364 return NULL;
2365 }
2366
2367 const Pos start_pos = tag_list->globalStats().evrange();
2368
2369 Undo operations;
2370
2371 bool changed = false;
2372 const Part* part;
2373
2374 //---------------------------------------------------
2375 // write events as XML into tmp file
2376 //---------------------------------------------------
2377
2378 Xml xml(&tmp);
2379 int level = 0;
2380
2381 for(ciTagEventList itl = tag_list->begin(); itl != tag_list->end(); ++itl)
2382 {
2383 part = itl->first;
2384 const EventList& el = itl->second.evlist();
2385 if(el.empty())
2386 continue;
2387
2388 xml.tag(level++, "eventlist part_id=\"%d\"", part->sn());
2389 for(ciEvent ie = el.begin(); ie != el.end(); ie++)
2390 {
2391 const Event& e = ie->second;
2392 Event ne = e.clone();
2393 ne.setPos(ne.pos() - start_pos);
2394 ne.write(level, xml, Pos(0, e.pos().type() == Pos::TICKS));
2395 if(cut_mode)
2396 {
2397 changed = true;
2398 operations.push_back(UndoOp(UndoOp::DeleteEvent, e, part, true, true));
2399 }
2400 }
2401 xml.etag(--level, "eventlist");
2402 }
2403
2404 tmp.flush();
2405 tmp.seek(0);
2406 const QByteArray data = tmp.readAll();
2407 QMimeData* mimeData = new QMimeData();
2408 mimeData->setData("text/x-muse-groupedeventlists", data);
2409
2410 if(changed)
2411 MusEGlobal::song->applyOperationGroup(operations);
2412
2413 return mimeData;
2414 }
2415
paste_items(const std::set<const Part * > & parts,const Part * paste_into_part)2416 bool paste_items(const std::set<const Part*>& parts, const Part* paste_into_part)
2417 {
2418 unsigned temp_begin = MusEGlobal::sigmap.raster1(MusEGlobal::song->cpos(),0);
2419 unsigned temp_end = MusEGlobal::sigmap.raster2(temp_begin + get_clipboard_len(), 0);
2420 MusEGui::paste_events_dialog->raster = temp_end - temp_begin;
2421 MusEGui::paste_events_dialog->into_single_part_allowed = (paste_into_part!=NULL);
2422
2423 if (!MusEGui::paste_events_dialog->exec())
2424 return false;
2425
2426 paste_items(parts, MusEGui::paste_events_dialog->max_distance,
2427 FunctionOptionsStruct(
2428 (MusEGui::paste_events_dialog->ctrl_erase ? FunctionEraseItems : FunctionNoOptions)
2429 | (MusEGui::paste_events_dialog->ctrl_erase_wysiwyg ? FunctionEraseItemsWysiwyg : FunctionNoOptions)
2430 | (MusEGui::paste_events_dialog->ctrl_erase_inclusive ? FunctionEraseItemsInclusive : FunctionNoOptions)
2431 | (MusEGui::paste_events_dialog->always_new_part ? FunctionPasteAlwaysNewPart : FunctionNoOptions)
2432 | (MusEGui::paste_events_dialog->never_new_part ? FunctionPasteNeverNewPart : FunctionNoOptions)),
2433 MusEGui::paste_events_dialog->into_single_part ? paste_into_part : NULL,
2434 MusEGui::paste_events_dialog->number, MusEGui::paste_events_dialog->raster,
2435 AllEventsRelevant,
2436 -1 /*paste to ctrl num*/
2437 );
2438
2439 return true;
2440 }
2441
paste_items(const set<const Part * > & parts,int max_distance,const FunctionOptionsStruct & options,const Part * paste_into_part,int amount,int raster,RelevantSelectedEvents_t relevant,int paste_to_ctrl_num)2442 void paste_items(const set<const Part*>& parts, int max_distance,
2443 const FunctionOptionsStruct& options,
2444 const Part* paste_into_part, int amount, int raster,
2445 RelevantSelectedEvents_t relevant,
2446 int paste_to_ctrl_num
2447 )
2448 {
2449 QString tmp="x-muse-groupedeventlists"; // QClipboard::text() expects a QString&, not a QString :(
2450 QString s = QApplication::clipboard()->text(tmp, QClipboard::Clipboard);
2451 paste_items_at(parts, s, MusEGlobal::song->cPos(), max_distance, options,
2452 paste_into_part, amount, raster, relevant, paste_to_ctrl_num
2453 );
2454 }
2455
pasteEventList(const EventList & el,const Pos & pos,Part * dest_part,Undo & operations,Undo & add_operations,expand_map_t & expand_map,new_part_map_t & new_part_map,const Part * source_part=nullptr,bool erase_source=false,const Pos & start_pos=Pos (),int max_distance=3072,const FunctionOptionsStruct & options=FunctionOptionsStruct (),int amount=1,int raster=3072,const RelevantSelectedEvents_t relevant=AllEventsRelevant,int paste_to_ctrl_num=-1)2456 void pasteEventList(
2457 const EventList& el,
2458 const Pos& pos,
2459 Part* dest_part,
2460 Undo& operations,
2461 Undo& add_operations,
2462 expand_map_t& expand_map,
2463 new_part_map_t& new_part_map,
2464 // The source part where the event list came from, in case
2465 // the erase_source argument is true.
2466 const Part* source_part = nullptr,
2467 // Whether to erase ('cut') the source events after pasting.
2468 bool erase_source = false,
2469 const Pos& start_pos = Pos(),
2470 int max_distance=3072,
2471 // Options. Default is erase target existing controllers first + erase wysiwyg.
2472 const FunctionOptionsStruct& options = FunctionOptionsStruct(),
2473 // Number of copies to paste.
2474 int amount=1,
2475 // Separation between copies.
2476 int raster=3072,
2477 // Choose which events to paste.
2478 const RelevantSelectedEvents_t relevant = AllEventsRelevant,
2479 // If pasting controllers, paste into this controller number if not -1.
2480 // If the source has multiple controllers, user will be asked which one to paste.
2481 int paste_to_ctrl_num = -1
2482 )
2483 {
2484 const bool wave_mode = dest_part->partType() == Part::WavePartType;
2485
2486 int num_events;
2487 PosLen el_range = el.evrange(wave_mode, relevant, &num_events, paste_to_ctrl_num);
2488 if(num_events <= 0)
2489 return;
2490
2491 const bool always_new_part = options._flags & FunctionPasteAlwaysNewPart;
2492 const bool never_new_part = options._flags & FunctionPasteNeverNewPart;
2493 const bool erase_controllers = options._flags & FunctionEraseItems;
2494 const bool erase_controllers_wysiwyg = options._flags & FunctionEraseItemsWysiwyg;
2495 const bool erase_controllers_inclusive = options._flags & FunctionEraseItemsInclusive;
2496
2497 const Pos::TType time_type = wave_mode ? Pos::FRAMES : Pos::TICKS;
2498 Track* dest_track = NULL;
2499 const Part* old_dest_part = NULL;
2500
2501 // Be sure to subtract the position of the very first event of interest.
2502 // This is exactly what the copy/cut functions do before they write the results
2503 // to an output file. But here the events in the directly-passed source list
2504 // cannot be time-modified beforehand. So here we subtract this start position:
2505 el_range -= start_pos;
2506
2507 const unsigned pos_value = pos.posValue(time_type);
2508 unsigned dest_part_pos_value = dest_part->posValue(time_type);
2509 unsigned dest_part_end_value = dest_part->end().posValue(time_type);
2510 dest_track=dest_part->track();
2511 old_dest_part=dest_part;
2512 unsigned first_paste_pos_value = el_range.posValue() + pos_value;
2513 bool create_new_part = ( (first_paste_pos_value < dest_part_pos_value) || // dest_part begins too late
2514 ( ( (dest_part_end_value + max_distance < first_paste_pos_value) || // dest_part is too far away
2515 always_new_part ) && !never_new_part ) ); // respect function arguments
2516
2517 for (int i=0;i<amount;i++)
2518 {
2519 unsigned curr_pos = pos_value + i*raster;
2520 first_paste_pos_value = el_range.posValue() + curr_pos;
2521
2522 if (create_new_part)
2523 {
2524 dest_part = NULL;
2525 Part* newpart = dest_track->newPart();
2526 if(newpart)
2527 {
2528 // TODO: Shouldn't we snap to frames for wave parts? But snap to what exactly?
2529 const unsigned pos_tick = Pos(first_paste_pos_value, !wave_mode).tick();
2530 const unsigned rast_pos_tick = MusEGlobal::sigmap.raster1(pos_tick, config.division);
2531 newpart->setTick(rast_pos_tick);
2532 const unsigned len_rast_off_value = pos_tick >= rast_pos_tick ? pos_tick - rast_pos_tick : 0;
2533 newpart->setLenValue(el_range.lenValue() + len_rast_off_value, time_type);
2534 dest_part = newpart;
2535 dest_part_pos_value = dest_part->posValue(time_type);
2536 dest_part_end_value = dest_part->end().posValue(time_type);
2537 new_part_map[old_dest_part].insert(dest_part);
2538 add_operations.push_back(UndoOp(UndoOp::AddPart, dest_part));
2539 }
2540 }
2541
2542 if(!dest_part)
2543 continue;
2544
2545 // This will be filled as we go.
2546 PasteEraseCtlMap ctl_map(erase_controllers_wysiwyg, erase_controllers_inclusive);
2547
2548 const unsigned dest_part_len_value = dest_part->lenValue(time_type);
2549 for (ciEvent i = el.cbegin(); i != el.cend(); ++i)
2550 {
2551 const Event& old_e = i->second;
2552
2553 // If the destination part is a midi part, any midi event is allowed.
2554 // If the destination part is a wave part, any wave event is allowed.
2555 switch(old_e.type())
2556 {
2557 case Note:
2558 if(!(relevant & NotesRelevant) || dest_part->type() == Pos::FRAMES)
2559 continue;
2560 break;
2561
2562 case Controller:
2563 if(!(relevant & ControllersRelevant) || dest_part->type() == Pos::FRAMES ||
2564 (paste_to_ctrl_num >= 0 && paste_to_ctrl_num != old_e.dataA()))
2565 continue;
2566 break;
2567
2568 case Sysex:
2569 if(!(relevant & SysexRelevant) || dest_part->type() == Pos::FRAMES)
2570 continue;
2571 break;
2572
2573 case Meta:
2574 if(!(relevant & MetaRelevant) || dest_part->type() == Pos::FRAMES)
2575 continue;
2576 break;
2577
2578 case Wave:
2579 if(!(relevant & WaveRelevant) || dest_part->type() == Pos::TICKS)
2580 continue;
2581 break;
2582 }
2583
2584 Event e = old_e.clone();
2585 unsigned tick = e.posValue(time_type) + curr_pos;
2586
2587 // Be sure to subtract the position of the very first event of interest.
2588 const unsigned sp_val = start_pos.posValue(time_type);
2589 if(tick >= sp_val)
2590 tick -= sp_val;
2591 else
2592 {
2593 printf("WARNING: paste_items_at(): Should not happen: event pos value: %u less than start pos value: %u\n",
2594 tick, sp_val);
2595 tick = 0;
2596 }
2597
2598 if (tick < dest_part_pos_value)
2599 //if (tick.posValue() < 0)
2600 {
2601 printf("ERROR: paste_items_at(): trying to add event before current part! ignoring this event\n");
2602 continue;
2603 }
2604 tick -= dest_part_pos_value;
2605
2606 e.setPosValue(tick, time_type);
2607 e.setSelected(true); // No need to select clones, AddEvent operation below will take care of that.
2608
2609 // Don't bother with expansion if these are new parts.
2610 if (!create_new_part && e.endPosValue() > dest_part_len_value) // event exceeds part?
2611 {
2612 if (dest_part->hasHiddenEvents() & Part::RightEventsHidden) // auto-expanding is forbidden?
2613 {
2614 if (e.posValue(time_type) < dest_part_len_value)
2615 e.setLenValue(dest_part_len_value - e.posValue(time_type), time_type); // clip
2616 else
2617 e.setLenValue(0, time_type); // don't insert that note at all
2618 }
2619 else
2620 {
2621 if (e.endPosValue() > expand_map[dest_part])
2622 expand_map[dest_part]=e.endPosValue();
2623 }
2624 }
2625
2626 switch(e.type())
2627 {
2628 case Note:
2629 // Don't add Note event types if they have no length.
2630 // Notes are allowed to overlap. There is no DeleteEvent or ModifyEvent first.
2631 //if(e.lenTick() != 0)
2632 //{
2633 // If this is a fresh new part, to avoid double operation warnings when undoing
2634 // just add the event directly to the part instead of an operation.
2635 if(create_new_part)
2636 ((Part*)dest_part)->addEvent(e);
2637 else
2638 add_operations.push_back(UndoOp(UndoOp::AddEvent, e, dest_part, false, false));
2639 //}
2640 break;
2641
2642 case Wave:
2643 // Don't add Wave event types if they have no length.
2644 //if(e.lenFrame() != 0)
2645 //{
2646 // If this is a fresh new part, to avoid double operation warnings when undoing
2647 // just add the event directly to the part instead of an operation.
2648 if(create_new_part)
2649 {
2650 ((Part*)dest_part)->addEvent(e);
2651 }
2652 else
2653 {
2654 EventList s_el;
2655 // Compare time, and wave position, path, and start position.
2656 dest_part->events().findSimilarType(e, s_el, true, false, false, false,
2657 true, true, true);
2658 // Do NOT add the new wave event if it already exists at the position.
2659 // Don't event bother replacing it using DeletEvent or ModifyEvent.
2660 if(s_el.empty())
2661 {
2662 add_operations.push_back(UndoOp(UndoOp::AddEvent, e, dest_part, false, false));
2663 }
2664 else
2665 {
2666 // Delete all but one of them. There shouldn't be more than one wave event
2667 // at a time for a given wave event anyway.
2668 ciEvent nie;
2669 for(ciEvent ie = s_el.cbegin(); ie != s_el.cend(); ++ie)
2670 {
2671 // Break on the second-last one, to leave one item intact.
2672 nie = ie;
2673 ++nie;
2674 if(nie == s_el.end())
2675 {
2676 break;
2677 }
2678
2679 // If we are 'cutting' the source events, and the source and destination parts
2680 // are the same, and the cut and erase events are the same, don't push the
2681 // deletes here. Let the cutting section at the end of the routine take do it.
2682 if(!erase_source || source_part != dest_part || el.findId(ie->second) == el.end())
2683 operations.push_back(UndoOp(UndoOp::DeleteEvent, ie->second, dest_part, false, false));
2684 }
2685 }
2686 }
2687 //}
2688 break;
2689
2690 case Controller:
2691 {
2692 // HACK Grab the event length since we use it for indicating
2693 // the visual width when tagging controller items.
2694 const unsigned int len_val = e.lenValue();
2695 // Be sure to reset this always since we use it for the above hack.
2696 e.setLenValue(0);
2697
2698 // If this is a fresh new part, to avoid double DeleteMidiCtrlVal warnings when undoing
2699 // just add the event directly to the part instead of an operation.
2700 if(create_new_part)
2701 {
2702 ((Part*)dest_part)->addEvent(e);
2703 }
2704 else
2705 {
2706 // If we are erasing existing controller values first, this block will
2707 // take care of all of the erasures. But even if we are NOT specifically
2708 // erasing first, we still MUST erase any existing controller values found
2709 // at that exact time value. So that is done in the next block.
2710 if(erase_controllers)
2711 {
2712 ctl_map.add(e.dataA(), e.posValue(), len_val);
2713 }
2714 else
2715 // Here we are not specifically erasing first. But we still MUST erase any
2716 // existing controller values found at that exact time value.
2717 {
2718 EventList s_el;
2719 // Compare time and controller number (data A) only.
2720 dest_part->events().findSimilarType(e, s_el, true, true);
2721 // Delete them all. There shouldn't be more than one controller event
2722 // at a time for a given controller number anyway.
2723 for(ciEvent ie = s_el.cbegin(); ie != s_el.cend(); ++ie)
2724 {
2725 // If we are 'cutting' the source events, and the source and destination parts
2726 // are the same, and the cut and erase events are the same, don't push the
2727 // deletes here. Let the cutting section at the end of the routine take do it.
2728 if(!erase_source || source_part != dest_part || el.findId(ie->second) == el.end())
2729 // Do port controller values and clone parts.
2730 operations.push_back(UndoOp(UndoOp::DeleteEvent, ie->second, dest_part, true, true));
2731 }
2732 }
2733 // Do port controller values and clone parts.
2734 add_operations.push_back(UndoOp(UndoOp::AddEvent, e, dest_part, true, true));
2735 }
2736 }
2737 break;
2738
2739 case Sysex:
2740 {
2741 // If this is a fresh new part, to avoid double operation warnings when undoing
2742 // just add the event directly to the part instead of an operation.
2743 if(create_new_part)
2744 {
2745 ((Part*)dest_part)->addEvent(e);
2746 }
2747 else
2748 {
2749 EventList s_el;
2750 // Compare time and sysex data only.
2751 dest_part->events().findSimilarType(e, s_el, true);
2752 // Do NOT add the new sysex if it already exists at the position.
2753 // Don't event bother replacing it using DeletEvent or ModifyEvent.
2754 if(s_el.empty())
2755 {
2756 add_operations.push_back(UndoOp(UndoOp::AddEvent, e, dest_part, false, false));
2757 }
2758 else
2759 {
2760 // Delete all but one of them. There shouldn't be more than one sysex event
2761 // at a time for a given sysex anyway.
2762 ciEvent nie;
2763 for(ciEvent ie = s_el.cbegin(); ie != s_el.cend(); ++ie)
2764 {
2765 // Break on the second-last one, to leave one item intact.
2766 nie = ie;
2767 ++nie;
2768 if(nie == s_el.end())
2769 {
2770 break;
2771 }
2772
2773 // If we are 'cutting' the source events, and the source and destination parts
2774 // are the same, and the cut and erase events are the same, don't push the
2775 // deletes here. Let the cutting section at the end of the routine take do it.
2776 if(!erase_source || source_part != dest_part || el.findId(ie->second) == el.end())
2777 operations.push_back(UndoOp(UndoOp::DeleteEvent, ie->second, dest_part, false, false));
2778 }
2779 }
2780 }
2781 }
2782 break;
2783
2784 case Meta:
2785 {
2786 // If this is a fresh new part, to avoid double operation warnings when undoing
2787 // just add the event directly to the part instead of an operation.
2788 if(create_new_part)
2789 {
2790 ((Part*)dest_part)->addEvent(e);
2791 }
2792 else
2793 {
2794 EventList s_el;
2795 // Compare time and meta data only.
2796 dest_part->events().findSimilarType(e, s_el, true);
2797 // Do NOT add the new meta if it already exists at the position.
2798 // Don't event bother replacing it using DeletEvent or ModifyEvent.
2799 if(s_el.empty())
2800 {
2801 add_operations.push_back(UndoOp(UndoOp::AddEvent, e, dest_part, false, false));
2802 }
2803 else
2804 {
2805 // Delete all but one of them. There shouldn't be more than one meta event
2806 // at a time for a given meta anyway.
2807 ciEvent nie;
2808 for(ciEvent ie = s_el.cbegin(); ie != s_el.cend(); ++ie)
2809 {
2810 // Break on the second-last one, to leave one item intact.
2811 nie = ie;
2812 ++nie;
2813 if(nie == s_el.end())
2814 {
2815 break;
2816 }
2817
2818 // If we are 'cutting' the source events, and the source and destination parts
2819 // are the same, and the cut and erase events are the same, don't push the
2820 // deletes here. Let the cutting section at the end of the routine take do it.
2821 if(!erase_source || source_part != dest_part || el.findId(ie->second) == el.end())
2822 operations.push_back(UndoOp(UndoOp::DeleteEvent, ie->second, dest_part, false, false));
2823 }
2824 }
2825 }
2826 }
2827 break;
2828 }
2829 }
2830
2831 // If this is not a fresh new part, gather the operations to
2832 // erase existing controller events in the destination part.
2833 if(erase_controllers && !create_new_part && dest_part && !ctl_map.empty())
2834 {
2835 // Tidy up the very last items in the list.
2836 ctl_map.tidy();
2837
2838 unsigned e_pos;
2839 const EventList& er_el = dest_part->events();
2840 for(ciEvent ie = er_el.cbegin(); ie != er_el.cend(); ++ie)
2841 {
2842 const Event& er_e = ie->second;
2843 if(er_e.type() != Controller)
2844 continue;
2845
2846 ciPasteEraseCtlMap icm = ctl_map.find(er_e.dataA());
2847 if(icm == ctl_map.end())
2848 continue;
2849
2850 const PasteEraseMap_t& tmap = icm->second;
2851 e_pos = er_e.posValue();
2852 ciPasteEraseMap itm = tmap.upper_bound(e_pos);
2853 if(itm == tmap.begin())
2854 continue;
2855
2856 --itm;
2857 if(e_pos >= itm->first && e_pos < itm->second)
2858 {
2859 // If we are 'cutting' the source events, and the source and destination parts
2860 // are the same, and the cut and erase events are the same, don't push the
2861 // deletes here. Let the cutting section at the end of the routine take do it.
2862 if(!erase_source || source_part != dest_part || el.findId(er_e) == el.end())
2863 operations.push_back(UndoOp(UndoOp::DeleteEvent, er_e, dest_part, true, true));
2864 }
2865 }
2866 }
2867 }
2868
2869 // Do we want to cut the items as well?
2870 if(erase_source && source_part)
2871 {
2872 for(ciEvent i = el.cbegin(); i != el.cend(); ++i)
2873 {
2874 const Event& old_e = i->second;
2875 operations.push_back(UndoOp(UndoOp::DeleteEvent, old_e, source_part, true, true));
2876 }
2877 }
2878 }
2879
paste_items_at(const std::set<const Part * > & parts,const QString & pt,const Pos & pos,int max_distance,const FunctionOptionsStruct & options,const Part * paste_into_part,int amount,int raster,RelevantSelectedEvents_t relevant,int paste_to_ctrl_num)2880 void paste_items_at(const std::set<const Part*>& parts, const QString& pt, const Pos& pos, int max_distance,
2881 const FunctionOptionsStruct& options,
2882 const Part* paste_into_part, int amount, int raster,
2883 RelevantSelectedEvents_t relevant,
2884 int paste_to_ctrl_num)
2885 {
2886 // To maximize speed and minimize memory use, the processing below
2887 // can only find any delete operations AFTER it has gathered
2888 // add operations. So we keep two separate operations lists and
2889 // combine them later so that all the deletes come BEFORE all the adds.
2890 Undo add_operations, operations;
2891
2892 map<const Part*, unsigned> expand_map;
2893 map<const Part*, set<const Part*> > new_part_map;
2894
2895 QByteArray pt_= pt.toLatin1();
2896 Xml xml(pt_.constData());
2897 for (;;)
2898 {
2899 Xml::Token token = xml.parse();
2900 const QString& tag = xml.s1();
2901 switch (token)
2902 {
2903 case Xml::Error:
2904 case Xml::End:
2905 goto out_of_paste_at_for;
2906
2907 case Xml::TagStart:
2908 if (tag == "eventlist")
2909 {
2910 EventList el;
2911 int part_id;
2912
2913 if (!read_eventlist_and_part(xml, &el, &part_id))
2914 {
2915 printf("ERROR: reading eventlist from clipboard failed. ignoring this one...\n");
2916 break;
2917 }
2918
2919 const Part* dest_part;
2920 if (paste_into_part == NULL)
2921 dest_part = partFromSerialNumber(part_id);
2922 else
2923 dest_part=paste_into_part;
2924
2925 if (dest_part == NULL)
2926 {
2927 printf("ERROR: destination part wasn't found. ignoring these events\n");
2928 break;
2929 }
2930
2931 // Paste into the destination part ONLY if it is included in the given set of parts,
2932 // typically the parts used by an editor window instance's canvas. (WYSIWYG).
2933 // Override if paste_into_part is given, to allow 'Paste to current part' to work.
2934 if(!paste_into_part && parts.find(dest_part) == parts.end())
2935 break;
2936
2937 const bool wave_mode = dest_part->partType() == Part::WavePartType;
2938
2939 FindMidiCtlsList_t ctrlList;
2940 el.findControllers(wave_mode, &ctrlList);
2941 int ctrlsFound = 0;
2942 if(!ctrlList.empty())
2943 ctrlsFound = ctrlList.size();
2944 if(paste_to_ctrl_num >= 0 && ctrlsFound > 0)
2945 {
2946 // TODO Dialog for choosing which controller to paste.
2947 }
2948
2949 pasteEventList(
2950 el, pos, ((Part*)dest_part), operations, add_operations,
2951 expand_map, new_part_map, nullptr, false,
2952 Pos(), max_distance, options, amount, raster, relevant, paste_to_ctrl_num);
2953 }
2954 else
2955 xml.unknown("paste_items_at");
2956 break;
2957
2958 case Xml::Attribut:
2959 case Xml::TagEnd:
2960 default:
2961 break;
2962 }
2963 }
2964
2965 out_of_paste_at_for:
2966
2967 // Push any part resizing operations onto the operations list now, before merging
2968 // the add operations.
2969 for (map<const Part*, unsigned>::iterator it = expand_map.begin(); it!=expand_map.end(); it++)
2970 if (it->second != it->first->lenValue())
2971 schedule_resize_all_same_len_clone_parts(it->first, it->second, operations);
2972
2973 // Now merge the add operations into the operations so that all of the 'deletes' come first.
2974 //add_operations.splice(add_operations.begin(), delete_operations);
2975 for(ciUndoOp iuo = add_operations.begin(); iuo != add_operations.end(); ++iuo)
2976 operations.push_back(*iuo);
2977
2978 MusEGlobal::song->informAboutNewParts(new_part_map); // must be called before apply. otherwise
2979 // pointer changes (by resize) screw it up
2980
2981 MusEGlobal::song->applyOperationGroup(operations);
2982 MusEGlobal::song->update(SC_SELECTION | SC_PART_SELECTION);
2983 }
2984
paste_items_at(const std::set<const Part * > & parts,const TagEventList * tag_list,const Pos & pos,int max_distance,const FunctionOptionsStruct & options,const Part * paste_into_part,int amount,int raster,RelevantSelectedEvents_t relevant,int paste_to_ctrl_num)2985 void paste_items_at(
2986 const std::set<const Part*>& parts,
2987 const TagEventList* tag_list,
2988 const Pos& pos,
2989 int max_distance,
2990 const FunctionOptionsStruct& options,
2991 const Part* paste_into_part,
2992 int amount,
2993 int raster,
2994 RelevantSelectedEvents_t relevant,
2995 int paste_to_ctrl_num
2996 )
2997 {
2998 const bool cut_mode = options._flags & FunctionCutItems;
2999
3000 // To maximize speed and minimize memory use, the processing below
3001 // can only find any delete operations AFTER it has gathered
3002 // add operations. So we keep two separate operations lists and
3003 // combine them later so that all the deletes come BEFORE all the adds.
3004 Undo add_operations, operations;
3005
3006 expand_map_t expand_map;
3007 new_part_map_t new_part_map;
3008
3009 // Find the lowest position of all the events - the 'start' position.
3010 const Pos start_pos = tag_list->globalStats().evrange(relevant);
3011
3012 // At this point the tag list's event list will still have any controller
3013 // visual lengths HACK applied.
3014 // Those lengths will be reset below. But for now we could use them...
3015 FindMidiCtlsList_t globalCtrlList;
3016 int globalCtrlsFound = 0;
3017 if(!globalCtrlList.empty())
3018 globalCtrlsFound = globalCtrlList.size();
3019 if(paste_to_ctrl_num >= 0)
3020 {
3021 tag_list->globalCtlStats(&globalCtrlList, paste_to_ctrl_num);
3022 if(!globalCtrlList.empty())
3023 globalCtrlsFound = globalCtrlList.size();
3024 if(globalCtrlsFound > 0)
3025 {
3026 // Prompt user to choose controller...
3027
3028 }
3029 }
3030
3031 for(ciTagEventList itl = tag_list->cbegin(); itl != tag_list->cend(); ++itl)
3032 {
3033 const Part* dest_part = NULL;
3034 const Part* src_part = itl->first;
3035
3036 if (paste_into_part == NULL)
3037 // Paste to original source part.
3038 dest_part = src_part;
3039 else
3040 // Paste to specific part.
3041 dest_part=paste_into_part;
3042
3043 if (dest_part == NULL)
3044 {
3045 printf("paste_items_at(): ERROR: destination part wasn't found. ignoring these events\n");
3046 continue;
3047 }
3048
3049 // Paste into the destination part ONLY if it is included in the given set of parts,
3050 // typically the parts used by an editor window instance's canvas. (WYSIWYG).
3051 // Override if paste_into_part is given, to allow 'Paste to current part' to work.
3052 if(!paste_into_part && parts.find(dest_part) == parts.end())
3053 continue;
3054
3055 // Grab the event list and find the number of relevant events.
3056 const EventList& el = itl->second.evlist();
3057
3058 pasteEventList(
3059 el, pos, ((Part*)dest_part), operations, add_operations,
3060 expand_map, new_part_map, src_part, cut_mode, start_pos, max_distance, options,
3061 amount, raster, relevant, paste_to_ctrl_num);
3062 }
3063
3064 // Push any part resizing operations onto the operations list now, before merging
3065 // the add operations.
3066 for (map<const Part*, unsigned>::iterator it = expand_map.begin(); it!=expand_map.end(); it++)
3067 if (it->second != it->first->lenValue())
3068 schedule_resize_all_same_len_clone_parts(it->first, it->second, operations);
3069
3070 // Now merge the add operations into the operations so that all of the 'deletes' come first.
3071 //add_operations.splice(add_operations.begin(), delete_operations);
3072 for(ciUndoOp iuo = add_operations.begin(); iuo != add_operations.end(); ++iuo)
3073 operations.push_back(*iuo);
3074
3075 MusEGlobal::song->informAboutNewParts(new_part_map); // must be called before apply. otherwise
3076 // pointer changes (by resize) screw it up
3077
3078 MusEGlobal::song->applyOperationGroup(operations);
3079 MusEGlobal::song->update(SC_SELECTION | SC_PART_SELECTION);
3080 }
3081
3082 //---------------------------------------------------------
3083 // getSelectedParts
3084 //---------------------------------------------------------
getSelectedParts()3085 PartList* getSelectedParts()
3086 {
3087 PartList* parts1;
3088 PartList* parts2;
3089
3090 parts1 = getSelectedMidiParts();
3091 parts2 = getSelectedWaveParts();
3092
3093 for (ciPart p = parts2->begin(); p != parts2->end(); ++p) {
3094 parts1->add(p->second);
3095 }
3096
3097 return parts1;
3098 }
3099
getSelectedMidiParts()3100 PartList* getSelectedMidiParts()
3101 {
3102 PartList* parts = new PartList();
3103
3104 /*
3105 If a part is selected, edit that.
3106 If a track is selected, edit the first
3107 part of the track, the rest are
3108 'ghost parts'
3109 When multiple parts are selected, then edit the first,
3110 the rest are 'ghost parts'
3111 */
3112
3113
3114 // collect marked parts
3115 for (ciMidiTrack t = MusEGlobal::song->midis()->begin(); t != MusEGlobal::song->midis()->end(); ++t) {
3116 PartList* pl = (*t)->parts();
3117 for (iPart p = pl->begin(); p != pl->end(); ++p) {
3118 if (p->second->selected()) {
3119 parts->add(p->second);
3120 }
3121 }
3122 }
3123 // if no part is selected, then search for selected track
3124 // and collect all parts of this track
3125
3126 if (parts->empty()) {
3127 for (ciMidiTrack t = MusEGlobal::song->midis()->begin(); t != MusEGlobal::song->midis()->end(); ++t) {
3128 if ((*t)->selected()) {
3129 PartList* pl = (*t)->parts();
3130 for (iPart p = pl->begin(); p != pl->end(); ++p)
3131 parts->add(p->second);
3132 break;
3133 }
3134 }
3135 }
3136
3137 return parts;
3138 }
3139
getSelectedWaveParts()3140 PartList* getSelectedWaveParts()
3141 {
3142 PartList* parts = new PartList();
3143
3144 /*
3145 If a part is selected, edit that.
3146 If a track is selected, edit the first
3147 part of the track, the rest are
3148 'ghost parts'
3149 When multiple parts are selected, then edit the first,
3150 the rest are 'ghost parts'
3151 */
3152
3153 // collect selected parts
3154 for (ciWaveTrack t = MusEGlobal::song->waves()->begin(); t != MusEGlobal::song->waves()->end(); ++t) {
3155 PartList* pl = (*t)->parts();
3156 for (ciPart p = pl->begin(); p != pl->end(); ++p) {
3157 if (p->second->selected()) {
3158 parts->add(p->second);
3159 }
3160 }
3161 }
3162 // if no parts are selected, then search the selected track
3163 // and collect all parts in this track
3164
3165 if (parts->empty()) {
3166 for (ciWaveTrack t = MusEGlobal::song->waves()->begin(); t != MusEGlobal::song->waves()->end(); ++t) {
3167 if ((*t)->selected()) {
3168 PartList* pl = (*t)->parts();
3169 for (ciPart p = pl->begin(); p != pl->end(); ++p)
3170 parts->add(p->second);
3171 break;
3172 }
3173 }
3174 }
3175 return parts;
3176 }
3177
3178 //---------------------------------------------------------
3179 // resize_part
3180 //---------------------------------------------------------
3181
resize_part(Track * track,Part * originalPart,unsigned int newTickPosOrLen,MusECore::ResizeDirection resizeDirection,bool doClones,bool dragEvents)3182 void resize_part(
3183 Track* track, Part* originalPart, unsigned int newTickPosOrLen, MusECore::ResizeDirection resizeDirection,
3184 bool doClones, bool dragEvents)
3185 {
3186
3187 // Are the events to be offset?
3188 const bool use_events_offset =
3189 (resizeDirection == MusECore::ResizeDirection::RESIZE_TO_THE_RIGHT && dragEvents) ||
3190 (resizeDirection == MusECore::ResizeDirection::RESIZE_TO_THE_LEFT && !dragEvents);
3191
3192 // Under this condition we MUST do all clones. The RULE is that event times, which are relative to the part start,
3193 // must be the same in all clones.
3194 if(use_events_offset)
3195 doClones = true;
3196
3197 switch(track->type()) {
3198 case Track::WAVE:
3199 case Track::MIDI:
3200 case Track::DRUM:
3201 {
3202 Undo operations;
3203
3204 #ifdef ALLOW_LEFT_HIDDEN_EVENTS
3205 const Pos::TType newPosOrLenType = Pos::TType::TICKS;
3206 const unsigned int origPosValue = originalPart->posValue();
3207 const unsigned int newOrigPosValue = Pos::convert(newTickPosOrLen, newPosOrLenType, originalPart->type());
3208 // The amount to shift a part. The int64_t cast ensures we preserve the unsigned range.
3209 const int64_t origPosValueDiff = (int64_t)newOrigPosValue - (int64_t)origPosValue;
3210 const unsigned int origPosValueConverted = originalPart->posValue(newPosOrLenType);
3211 const unsigned int newOrigEndPosValue =
3212 Pos::convert(origPosValueConverted + newTickPosOrLen, newPosOrLenType, originalPart->type());
3213 const unsigned int newOrigLenValue = newOrigEndPosValue - origPosValue;
3214
3215 const unsigned int origLenValue = originalPart->lenValue();
3216 const int64_t origLenValueDiff = (int64_t)newOrigLenValue - (int64_t)origLenValue;
3217
3218 int64_t events_offset = 0L;
3219 if(use_events_offset)
3220 {
3221 switch(resizeDirection)
3222 {
3223 case MusECore::ResizeDirection::RESIZE_TO_THE_RIGHT:
3224 events_offset = origLenValueDiff;
3225 break;
3226 case MusECore::ResizeDirection::RESIZE_TO_THE_LEFT:
3227 events_offset = -origPosValueDiff;
3228 break;
3229 }
3230 }
3231
3232 auto currentPart = originalPart;
3233
3234 do
3235 {
3236 if(resizeDirection == MusECore::ResizeDirection::RESIZE_TO_THE_RIGHT)
3237 {
3238 const unsigned int pos_val = currentPart->posValue(originalPart->type());
3239 const unsigned int new_end_pos_val = Pos::convert(pos_val + newOrigLenValue, originalPart->type(), currentPart->type());
3240 const unsigned int new_len_val = new_end_pos_val - pos_val;
3241 operations.push_back(
3242 UndoOp(UndoOp::ModifyPartLength, currentPart,
3243 currentPart->lenValue(),
3244 new_len_val,
3245 // The amount to shift all events in the part.
3246 events_offset,
3247 // The position type of the amount to shift all events in the part.
3248 originalPart->type()));
3249 }
3250 else if(resizeDirection == MusECore::ResizeDirection::RESIZE_TO_THE_LEFT)
3251 {
3252 const unsigned int pos_val = currentPart->posValue(originalPart->type());
3253 const unsigned int end_pos_val = currentPart->endValue(originalPart->type());
3254 unsigned int new_pos_val, new_len_val;
3255 if((int64_t)pos_val + origPosValueDiff < 0L)
3256 {
3257 new_pos_val = 0;
3258 new_len_val = Pos::convert((int64_t)end_pos_val - ((int64_t)pos_val + origPosValueDiff),
3259 originalPart->type(), currentPart->type()) - new_pos_val;
3260 }
3261 else
3262 {
3263 new_pos_val = Pos::convert((int64_t)pos_val + origPosValueDiff, originalPart->type(), currentPart->type());
3264 new_len_val = currentPart->endValue() - new_pos_val;
3265 }
3266 operations.push_back(
3267 UndoOp(UndoOp::ModifyPartStart, currentPart,
3268 currentPart->posValue(),
3269 new_pos_val,
3270 currentPart->lenValue(),
3271 new_len_val,
3272 // The amount to shift all events in the part.
3273 events_offset,
3274 // The position type of the amount to shift all events in the part.
3275 originalPart->type()));
3276 }
3277
3278 currentPart = currentPart->nextClone();
3279
3280 } while (doClones && (currentPart != originalPart));
3281 #else
3282 const Pos::TType newPosOrLenType = Pos::TType::TICKS;
3283 const unsigned int origPosValue = originalPart->posValue();
3284 const unsigned int newOrigPosValue = Pos::convert(newTickPosOrLen, newPosOrLenType, originalPart->type());
3285 // The amount to shift a part. The int64_t cast ensures we preserve the unsigned range.
3286 const int64_t origPosValueDiff = (int64_t)newOrigPosValue - (int64_t)origPosValue;
3287 const Pos::TType events_offset_time_type = originalPart->type();
3288 const unsigned int origPosValueConverted = originalPart->posValue(newPosOrLenType);
3289 const unsigned int newOrigEndPosValue = Pos::convert(origPosValueConverted + newTickPosOrLen, newPosOrLenType, originalPart->type());
3290 const unsigned int newOrigLenValue = newOrigEndPosValue - origPosValue;
3291
3292 const unsigned int origLenValue = originalPart->lenValue();
3293 const int64_t origLenValueDiff = (int64_t)newOrigLenValue - (int64_t)origLenValue;
3294
3295 int64_t events_offset = 0L;
3296 if(use_events_offset)
3297 {
3298 switch(resizeDirection)
3299 {
3300 case MusECore::ResizeDirection::RESIZE_TO_THE_RIGHT:
3301 events_offset = origLenValueDiff;
3302 break;
3303 case MusECore::ResizeDirection::RESIZE_TO_THE_LEFT:
3304 events_offset = -origPosValueDiff;
3305 break;
3306 }
3307 }
3308
3309 // Check to see if the events offset would move events before time zero, and limit if so.
3310 if(use_events_offset)
3311 {
3312 const EventList& el = originalPart->events();
3313 const ciEvent iev = el.cbegin();
3314 if(iev != el.cend())
3315 {
3316 const Event first_ev = iev->second;
3317
3318 // In case the event and part pos types differ, the event dominates.
3319 unsigned int new_part_pos_val;
3320 switch(resizeDirection)
3321 {
3322 // If resizing to the right, just use the original part position, converted.
3323 case MusECore::ResizeDirection::RESIZE_TO_THE_RIGHT:
3324 new_part_pos_val = originalPart->posValue(first_ev.pos().type());
3325 break;
3326 // If resizing to the left, use the new part position, converted.
3327 case MusECore::ResizeDirection::RESIZE_TO_THE_LEFT:
3328 new_part_pos_val = Pos::convert(newTickPosOrLen, newPosOrLenType, first_ev.pos().type());
3329 break;
3330 }
3331
3332 const unsigned int old_abs_ev_pos_val =
3333 Pos::convert(first_ev.posValue() + new_part_pos_val, first_ev.pos().type(), events_offset_time_type);
3334
3335 if((int64_t)old_abs_ev_pos_val + events_offset < 0L)
3336 events_offset = -(int64_t)old_abs_ev_pos_val;
3337
3338 const unsigned int new_abs_ev_pos_val =
3339 Pos::convert((int64_t)old_abs_ev_pos_val + events_offset, events_offset_time_type, first_ev.pos().type());
3340
3341 if(new_abs_ev_pos_val < new_part_pos_val)
3342 events_offset = -(int64_t)first_ev.pos().posValue();
3343 }
3344 }
3345
3346 auto currentPart = originalPart;
3347
3348 do
3349 {
3350 if(resizeDirection == MusECore::ResizeDirection::RESIZE_TO_THE_RIGHT)
3351 {
3352 const unsigned int pos_val = currentPart->posValue(originalPart->type());
3353 const unsigned int new_end_pos_val = Pos::convert(pos_val + newOrigLenValue, originalPart->type(), currentPart->type());
3354 const unsigned int new_len_val = new_end_pos_val - pos_val;
3355 operations.push_back(
3356 UndoOp(UndoOp::ModifyPartLength, currentPart,
3357 currentPart->lenValue(),
3358 new_len_val,
3359 // The amount to shift all events in the part.
3360 events_offset,
3361 // The position type of the amount to shift all events in the part.
3362 originalPart->type()));
3363 }
3364 else if(resizeDirection == MusECore::ResizeDirection::RESIZE_TO_THE_LEFT)
3365 {
3366 const unsigned int pos_val = currentPart->posValue(originalPart->type());
3367 const unsigned int end_pos_val = currentPart->endValue(originalPart->type());
3368 unsigned int new_pos_val, new_len_val;
3369 if((int64_t)pos_val + origPosValueDiff < 0L)
3370 {
3371 new_pos_val = 0;
3372 new_len_val = Pos::convert((int64_t)end_pos_val - ((int64_t)pos_val + origPosValueDiff),
3373 originalPart->type(), currentPart->type()) - new_pos_val;
3374 }
3375 else
3376 {
3377 new_pos_val = Pos::convert((int64_t)pos_val + origPosValueDiff, originalPart->type(), currentPart->type());
3378 new_len_val = currentPart->endValue() - new_pos_val;
3379 }
3380 operations.push_back(
3381 UndoOp(UndoOp::ModifyPartStart, currentPart,
3382 currentPart->posValue(),
3383 new_pos_val,
3384 currentPart->lenValue(),
3385 new_len_val,
3386 // The amount to shift all events in the part.
3387 events_offset,
3388 // The position type of the amount to shift all events in the part.
3389 originalPart->type()));
3390 }
3391
3392 currentPart = currentPart->nextClone();
3393
3394 } while (doClones && (currentPart != originalPart));
3395 #endif
3396
3397 MusEGlobal::song->applyOperationGroup(operations);
3398 break;
3399 }
3400
3401 default:
3402 break;
3403 }
3404 }
3405
3406
3407 } // namespace MusECore
3408