1 /*
2 * Copyright (C) 2018-2021 Alexandros Theodotou <alex at zrythm dot org>
3 *
4 * This file is part of Zrythm
5 *
6 * Zrythm is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU Affero General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * Zrythm is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Affero General Public License for more details.
15 *
16 * You should have received a copy of the GNU Affero General Public License
17 * along with Zrythm. If not, see <https://www.gnu.org/licenses/>.
18 */
19
20 #include "zrythm.h"
21 #include "actions/actions.h"
22 #include "actions/arranger_selections.h"
23 #include "audio/automation_region.h"
24 #include "audio/automation_track.h"
25 #include "audio/channel.h"
26 #include "audio/chord_region.h"
27 #include "audio/chord_track.h"
28 #include "audio/control_port.h"
29 #include "audio/instrument_track.h"
30 #include "audio/marker_track.h"
31 #include "audio/midi_region.h"
32 #include "audio/track.h"
33 #include "audio/transport.h"
34 #include "gui/backend/event.h"
35 #include "gui/backend/event_manager.h"
36 #include "gui/widgets/arranger.h"
37 #include "gui/widgets/arranger_draw.h"
38 #include "gui/widgets/arranger_playhead.h"
39 #include "gui/widgets/audio_arranger.h"
40 #include "gui/widgets/audio_editor_space.h"
41 #include "gui/widgets/automation_arranger.h"
42 #include "gui/widgets/automation_editor_space.h"
43 #include "gui/widgets/automation_point.h"
44 #include "gui/widgets/bot_dock_edge.h"
45 #include "gui/widgets/center_dock.h"
46 #include "gui/widgets/chord_arranger.h"
47 #include "gui/widgets/chord_editor_space.h"
48 #include "gui/widgets/clip_editor.h"
49 #include "gui/widgets/clip_editor_inner.h"
50 #include "gui/widgets/color_area.h"
51 #include "gui/widgets/dialogs/string_entry_dialog.h"
52 #include "gui/widgets/editor_ruler.h"
53 #include "gui/widgets/foldable_notebook.h"
54 #include "gui/widgets/main_notebook.h"
55 #include "gui/widgets/main_window.h"
56 #include "gui/widgets/midi_arranger.h"
57 #include "gui/widgets/midi_editor_space.h"
58 #include "gui/widgets/midi_modifier_arranger.h"
59 #include "gui/widgets/midi_note.h"
60 #include "gui/widgets/piano_roll_keys.h"
61 #include "gui/widgets/ruler.h"
62 #include "gui/widgets/scale_object.h"
63 #include "gui/widgets/scale_selector_window.h"
64 #include "gui/widgets/timeline_arranger.h"
65 #include "gui/widgets/timeline_bg.h"
66 #include "gui/widgets/timeline_bot_box.h"
67 #include "gui/widgets/timeline_minimap.h"
68 #include "gui/widgets/timeline_panel.h"
69 #include "gui/widgets/timeline_ruler.h"
70 #include "gui/widgets/track.h"
71 #include "gui/widgets/tracklist.h"
72 #include "project.h"
73 #include "settings/settings.h"
74 #include "utils/arrays.h"
75 #include "utils/cairo.h"
76 #include "utils/error.h"
77 #include "utils/flags.h"
78 #include "utils/gtk.h"
79 #include "utils/math.h"
80 #include "utils/objects.h"
81 #include "utils/resources.h"
82 #include "utils/ui.h"
83 #include "zrythm_app.h"
84
85 #include <gtk/gtk.h>
86 #include <glib/gi18n.h>
87
G_DEFINE_TYPE(ArrangerWidget,arranger_widget,GTK_TYPE_DRAWING_AREA)88 G_DEFINE_TYPE (
89 ArrangerWidget,
90 arranger_widget,
91 GTK_TYPE_DRAWING_AREA)
92
93 #define FOREACH_TYPE(func) \
94 func (TIMELINE, timeline); \
95 func (MIDI, midi); \
96 func (AUDIO, audio); \
97 func (AUTOMATION, automation); \
98 func (MIDI_MODIFIER, midi_modifier); \
99 func (CHORD, chord)
100
101 #define ACTION_IS(x) \
102 (self->action == UI_OVERLAY_ACTION_##x)
103
104 #define TYPE(x) ARRANGER_WIDGET_TYPE_##x
105
106 #define TYPE_IS(x) \
107 (self->type == TYPE (x))
108
109 #define SCROLL_PADDING 8.0
110
111 const char *
112 arranger_widget_get_type_str (
113 ArrangerWidgetType type)
114 {
115 static const char * arranger_widget_type_str[] = {
116 "timeline",
117 "midi",
118 "midi modifier",
119 "audio",
120 "chord",
121 "automation",
122 };
123 return arranger_widget_type_str[type];
124 }
125
126 /**
127 * Returns true if MIDI arranger and track mode
128 * is enabled.
129 */
130 bool
arranger_widget_get_drum_mode_enabled(ArrangerWidget * self)131 arranger_widget_get_drum_mode_enabled (
132 ArrangerWidget * self)
133 {
134 if (self->type != ARRANGER_WIDGET_TYPE_MIDI)
135 return false;
136
137 if (!CLIP_EDITOR->has_region)
138 return false;
139
140 Track * tr =
141 clip_editor_get_track (CLIP_EDITOR);
142 g_return_val_if_fail (tr, false);
143
144 return tr->drum_mode;
145 }
146
147 /**
148 * Returns the playhead's x coordinate in absolute
149 * coordinates.
150 */
151 int
arranger_widget_get_playhead_px(ArrangerWidget * self)152 arranger_widget_get_playhead_px (
153 ArrangerWidget * self)
154 {
155 ZRegion * clip_editor_region =
156 clip_editor_get_region (CLIP_EDITOR);
157
158 /* get frames */
159 long frames = 0;
160 if (self->type == TYPE (TIMELINE))
161 {
162 frames = PLAYHEAD->frames;
163 }
164 else if (clip_editor_region)
165 {
166 ZRegion * r = NULL;
167
168 if (self->type ==
169 ARRANGER_WIDGET_TYPE_AUTOMATION)
170 {
171 /* for some reason hidden arrangers
172 * try to call this */
173 if (clip_editor_region->id.type !=
174 REGION_TYPE_AUTOMATION)
175 {
176 return 0;
177 }
178
179 AutomationTrack * at =
180 region_get_automation_track (
181 clip_editor_region);
182 r =
183 region_at_position (
184 NULL, at, PLAYHEAD);
185 }
186 else
187 {
188 r =
189 region_at_position (
190 arranger_object_get_track (
191 (ArrangerObject *)
192 clip_editor_region),
193 NULL, PLAYHEAD);
194 }
195 Position tmp;
196 if (r)
197 {
198 ArrangerObject * obj =
199 (ArrangerObject *) r;
200 long region_local_frames =
201 region_timeline_frames_to_local (
202 r, PLAYHEAD->frames, 1);
203 region_local_frames +=
204 obj->pos.frames;
205 position_from_frames (
206 &tmp, region_local_frames);
207 frames = tmp.frames;
208 }
209 else
210 {
211 frames = PLAYHEAD->frames;
212 }
213 }
214
215 Position pos;
216 position_from_frames (&pos, frames);
217 return
218 arranger_widget_pos_to_px (self, &pos, 1);
219 }
220
221 /**
222 * Sets the cursor on the arranger and all of its
223 * children.
224 */
225 void
arranger_widget_set_cursor(ArrangerWidget * self,ArrangerCursor cursor)226 arranger_widget_set_cursor (
227 ArrangerWidget * self,
228 ArrangerCursor cursor)
229 {
230 #define SET_X_CURSOR(x) \
231 ui_set_##x##_cursor (GTK_WIDGET (self)); \
232
233 #define SET_CURSOR_FROM_NAME(name) \
234 ui_set_cursor_from_name ( \
235 GTK_WIDGET (self), name); \
236
237 switch (cursor)
238 {
239 case ARRANGER_CURSOR_SELECT:
240 SET_X_CURSOR (pointer);
241 break;
242 case ARRANGER_CURSOR_EDIT:
243 SET_X_CURSOR (pencil);
244 break;
245 case ARRANGER_CURSOR_AUTOFILL:
246 SET_X_CURSOR (brush);
247 break;
248 case ARRANGER_CURSOR_CUT:
249 SET_X_CURSOR (cut_clip);
250 break;
251 case ARRANGER_CURSOR_RAMP:
252 SET_X_CURSOR (line);
253 break;
254 case ARRANGER_CURSOR_ERASER:
255 SET_X_CURSOR (eraser);
256 break;
257 case ARRANGER_CURSOR_AUDITION:
258 SET_X_CURSOR (speaker);
259 break;
260 case ARRANGER_CURSOR_GRAB:
261 SET_X_CURSOR (hand);
262 break;
263 case ARRANGER_CURSOR_GRABBING:
264 SET_CURSOR_FROM_NAME ("grabbing");
265 break;
266 case ARRANGER_CURSOR_GRABBING_COPY:
267 SET_CURSOR_FROM_NAME ("copy");
268 break;
269 case ARRANGER_CURSOR_GRABBING_LINK:
270 SET_CURSOR_FROM_NAME ("link");
271 break;
272 case ARRANGER_CURSOR_RESIZING_L:
273 case ARRANGER_CURSOR_RESIZING_L_FADE:
274 SET_X_CURSOR (left_resize);
275 break;
276 case ARRANGER_CURSOR_STRETCHING_L:
277 SET_X_CURSOR (left_stretch);
278 break;
279 case ARRANGER_CURSOR_RESIZING_L_LOOP:
280 SET_X_CURSOR (left_resize_loop);
281 break;
282 case ARRANGER_CURSOR_RESIZING_R:
283 case ARRANGER_CURSOR_RESIZING_R_FADE:
284 SET_X_CURSOR (right_resize);
285 break;
286 case ARRANGER_CURSOR_STRETCHING_R:
287 SET_X_CURSOR (right_stretch);
288 break;
289 case ARRANGER_CURSOR_RESIZING_R_LOOP:
290 SET_X_CURSOR (right_resize_loop);
291 break;
292 case ARRANGER_CURSOR_RESIZING_UP:
293 SET_CURSOR_FROM_NAME ("n-resize");
294 break;
295 case ARRANGER_CURSOR_RESIZING_UP_FADE_IN:
296 SET_CURSOR_FROM_NAME ("n-resize");
297 break;
298 case ARRANGER_CURSOR_RESIZING_UP_FADE_OUT:
299 SET_CURSOR_FROM_NAME ("n-resize");
300 break;
301 case ARRANGER_CURSOR_RANGE:
302 SET_CURSOR_FROM_NAME ("text");
303 break;
304 case ARRANGER_CURSOR_FADE_IN:
305 SET_X_CURSOR (fade_in);
306 break;
307 case ARRANGER_CURSOR_FADE_OUT:
308 SET_X_CURSOR (fade_out);
309 break;
310 case ARRANGER_CURSOR_RENAME:
311 SET_CURSOR_FROM_NAME ("text");
312 break;
313 default:
314 g_warn_if_reached ();
315 break;
316 }
317 }
318
319 /**
320 * Returns whether the cursor at y is in the top
321 * half of the arranger.
322 */
323 static bool
is_cursor_in_top_half(ArrangerWidget * self,double y)324 is_cursor_in_top_half (
325 ArrangerWidget * self,
326 double y)
327 {
328 int height =
329 gtk_widget_get_allocated_height (
330 GTK_WIDGET (self));
331 return y < ((double) height / 2.0);
332 }
333
334 /**
335 * Sets whether selecting objects or range.
336 */
337 static void
set_select_type(ArrangerWidget * self,double y)338 set_select_type (
339 ArrangerWidget * self,
340 double y)
341 {
342 if (self->type == TYPE (TIMELINE))
343 {
344 timeline_arranger_widget_set_select_type (
345 self, y);
346 }
347 else if (self->type == TYPE (AUDIO))
348 {
349 if (is_cursor_in_top_half (
350 self, y))
351 {
352 self->resizing_range = false;
353 }
354 else
355 {
356 self->resizing_range = true;
357 self->resizing_range_start = true;
358 self->action =
359 UI_OVERLAY_ACTION_RESIZING_R;
360 }
361 }
362 }
363
364 SnapGrid *
arranger_widget_get_snap_grid(ArrangerWidget * self)365 arranger_widget_get_snap_grid (
366 ArrangerWidget * self)
367 {
368 if (self == MW_MIDI_MODIFIER_ARRANGER ||
369 self == MW_MIDI_ARRANGER ||
370 self == MW_AUTOMATION_ARRANGER ||
371 self == MW_AUDIO_ARRANGER ||
372 self == MW_CHORD_ARRANGER)
373 {
374 return SNAP_GRID_EDITOR;
375 }
376 else if (self == MW_TIMELINE ||
377 self == MW_PINNED_TIMELINE)
378 {
379 return SNAP_GRID_TIMELINE;
380 }
381 g_return_val_if_reached (NULL);
382 }
383
384 #if 0
385 /**
386 * Returns the number of regions inside the given
387 * editor arranger.
388 */
389 static int
390 get_regions_in_editor_rect (
391 ArrangerWidget * self,
392 GdkRectangle * rect,
393 ZRegion ** regions)
394 {
395 return
396 editor_ruler_get_regions_in_range (
397 EDITOR_RULER, rect->x, rect->x + rect->width,
398 regions);
399 }
400 #endif
401
402 typedef struct ObjectOverlapInfo
403 {
404 /**
405 * When rect is NULL, this is a special case for
406 * automation points. The object will only
407 * be added if the cursor is on the automation
408 * point or within n px from the curve.
409 */
410 GdkRectangle * rect;
411
412 /** X, or -1 to not check x. */
413 double x;
414
415 /** Y, or -1 to not check y. */
416 double y;
417
418 /** Position for x or rect->x (cached). */
419 Position start_pos;
420
421 /**
422 * Position for rect->x + rect->width.
423 *
424 * If rect is NULL, this is the same as
425 * \ref start_pos.
426 */
427 Position end_pos;
428
429 ArrangerObject ** array;
430 int * array_size;
431 ArrangerObject * obj;
432 } ObjectOverlapInfo;
433
434 /**
435 * Adds the object to the array if it or its
436 * transient overlaps with the rectangle, or with
437 * \ref x \ref y if \ref rect is NULL.
438 *
439 * @return Whether the object was added or not.
440 */
441 HOT
442 static bool
add_object_if_overlap(ArrangerWidget * self,ObjectOverlapInfo * nfo)443 add_object_if_overlap (
444 ArrangerWidget * self,
445 ObjectOverlapInfo * nfo)
446 {
447 GdkRectangle * rect = nfo->rect;
448 double x = nfo->x;
449 double y = nfo->y;
450 ArrangerObject ** array = nfo->array;
451 int * array_size = nfo->array_size;
452 ArrangerObject * obj = nfo->obj;
453
454 g_return_val_if_fail (
455 IS_ARRANGER_OBJECT (obj), false);
456 g_return_val_if_fail (
457 (math_doubles_equal (x, -1) || x >= 0.0) &&
458 (math_doubles_equal (y, -1) || y >= 0.0),
459 false);
460
461 if (obj->deleted_temporarily)
462 {
463 return false;
464 }
465
466 /* --- optimization to skip expensive
467 * calculations for most objects --- */
468
469 /* skip objects that end before the rect */
470 Position tmp;
471 if (arranger_object_type_has_length (obj->type))
472 {
473 if (arranger_object_type_has_global_pos (
474 obj->type))
475 {
476 tmp = obj->end_pos;
477 }
478 else
479 {
480 ZRegion * r =
481 arranger_object_get_region (obj);
482 g_return_val_if_fail (
483 IS_REGION_AND_NONNULL (r), false);
484 tmp = r->base.pos;
485 position_add_ticks (
486 &tmp, obj->end_pos.ticks);
487 }
488 if (position_is_before (
489 &tmp, &nfo->start_pos))
490 {
491 return false;
492 }
493 }
494
495 /* skip objects that start after the end */
496 if (arranger_object_type_has_global_pos (
497 obj->type))
498 {
499 tmp = obj->pos;
500 }
501 else
502 {
503 ZRegion * r =
504 arranger_object_get_region (obj);
505 g_return_val_if_fail (
506 IS_REGION_AND_NONNULL (r), false);
507 tmp = r->base.pos;
508 position_add_ticks (
509 &tmp, obj->pos.ticks);
510 }
511 if (position_is_after (
512 &obj->pos, &nfo->end_pos))
513 {
514 return false;
515 }
516
517 /* --- end optimization --- */
518
519 bool is_same_arranger =
520 arranger_object_get_arranger (obj) == self;
521 if (!is_same_arranger)
522 return false;
523
524 arranger_object_set_full_rectangle (obj, self);
525 bool add = false;
526 if (rect)
527 {
528 if ((ui_rectangle_overlap (
529 &obj->full_rect, rect) ||
530 /* also check original (transient) */
531 (arranger_object_should_orig_be_visible (
532 obj) &&
533 obj->transient &&
534 ui_rectangle_overlap (
535 &obj->transient->full_rect, rect))))
536 {
537 add = true;
538 }
539 }
540 else if (
541 (ui_is_point_in_rect_hit (
542 &obj->full_rect,
543 x >= 0 ? true : false,
544 y >= 0 ? true : false,
545 x, y, 0, 0) ||
546 /* also check original (transient) */
547 (arranger_object_should_orig_be_visible (
548 obj) &&
549 obj->transient &&
550 ui_is_point_in_rect_hit (
551 &obj->transient->full_rect,
552 x >= 0 ? true : false,
553 y >= 0 ? true : false,
554 x, y, 0, 0))))
555 {
556 /* object to check for automation point
557 * curve cross (either main object or
558 * transient) */
559 ArrangerObject * obj_to_check =
560 (arranger_object_should_orig_be_visible (
561 obj) && obj->transient &&
562 ui_is_point_in_rect_hit (
563 &obj->transient->full_rect,
564 x >= 0 ? true : false,
565 y >= 0 ? true : false,
566 x, y, 0, 0)) ?
567 obj->transient : obj;
568
569 /** handle special case for automation
570 * points */
571 if (obj->type ==
572 ARRANGER_OBJECT_TYPE_AUTOMATION_POINT &&
573 !automation_point_is_point_hit (
574 (AutomationPoint *) obj_to_check,
575 x, y) &&
576 !automation_point_is_curve_hit (
577 (AutomationPoint *) obj_to_check,
578 x, y, 16.0))
579 {
580 return false;
581 }
582
583 add = true;
584 }
585
586 if (add)
587 {
588 array[*array_size] = obj;
589 (*array_size)++;
590 }
591
592 return add;
593 }
594
595 /**
596 * Fills in the given array with the
597 * ArrangerObject's of the given type that appear
598 * in the given rect, or at the given coords if
599 * \ref rect is NULL.
600 *
601 * @param rect The rectangle to search in.
602 * @param type The type of arranger objects to find,
603 * or -1 to look for all objects.
604 * @param x X, or -1 to not check x.
605 * @param y Y, or -1 to not check y.
606 * @param array The array to fill.
607 * @param array_size The size of the array to fill.
608 */
609 static void
get_hit_objects(ArrangerWidget * self,ArrangerObjectType type,GdkRectangle * rect,double x,double y,ArrangerObject ** array,int * array_size)610 get_hit_objects (
611 ArrangerWidget * self,
612 ArrangerObjectType type,
613 GdkRectangle * rect,
614 double x,
615 double y,
616 ArrangerObject ** array,
617 int * array_size)
618 {
619 g_return_if_fail (self && array);
620
621 *array_size = 0;
622 ArrangerObject * obj = NULL;
623
624 /* skip if haven't drawn yet */
625 if (self->first_draw)
626 {
627 return;
628 }
629
630 int start_y = rect ? rect->y : (int) y;
631
632 /* prepare struct to pass for each object */
633 ObjectOverlapInfo nfo;
634 nfo.rect = rect;
635 nfo.x = x;
636 nfo.y = y;
637 arranger_widget_px_to_pos (
638 self, rect ? rect->x : x, &nfo.start_pos, true);
639 if (rect)
640 {
641 arranger_widget_px_to_pos (
642 self, rect->x + rect->width,
643 &nfo.end_pos, F_PADDING);
644 }
645 else
646 {
647 nfo.end_pos = nfo.start_pos;
648 }
649 nfo.array = array;
650 nfo.array_size = array_size;
651
652 switch (self->type)
653 {
654 case TYPE (TIMELINE):
655 if (type != ARRANGER_OBJECT_TYPE_ALL &&
656 type != ARRANGER_OBJECT_TYPE_REGION &&
657 type != ARRANGER_OBJECT_TYPE_MARKER &&
658 type != ARRANGER_OBJECT_TYPE_SCALE_OBJECT)
659 break;
660
661 /* add overlapping scales */
662 if (type == ARRANGER_OBJECT_TYPE_ALL ||
663 type ==
664 ARRANGER_OBJECT_TYPE_SCALE_OBJECT)
665 {
666 for (int i = 0;
667 i < P_CHORD_TRACK->num_scales; i++)
668 {
669 obj =
670 (ArrangerObject *)
671 P_CHORD_TRACK->scales[i];
672 nfo.obj = obj;
673 add_object_if_overlap (self, &nfo);
674 }
675 }
676
677 /* add overlapping regions */
678 if (type == ARRANGER_OBJECT_TYPE_ALL ||
679 type == ARRANGER_OBJECT_TYPE_REGION)
680 {
681 /* midi and audio regions */
682 for (int i = 0;
683 i < TRACKLIST->num_tracks;
684 i++)
685 {
686 Track * track = TRACKLIST->tracks[i];
687
688 /* skip tracks if not visible or this
689 * is timeline and pin status doesn't
690 * match */
691 if (!track->visible ||
692 (TYPE (TIMELINE) &&
693 track_is_pinned (track) !=
694 self->is_pinned))
695 {
696 continue;
697 }
698
699 /* skip if track should not be
700 * visible */
701 if (!track_get_should_be_visible (
702 track))
703 continue;
704
705 if (G_LIKELY (track->widget))
706 {
707 int track_y =
708 track_widget_get_local_y (
709 track->widget, self,
710 start_y);
711
712 /* skip if track starts after the
713 * rect */
714 if (track_y +
715 (rect ?
716 rect->height : 0) < 0)
717 {
718 continue;
719 }
720
721 double full_track_height =
722 track_get_full_visible_height (
723 track);
724
725 /* skip if track ends before the
726 * rect */
727 if (track_y > full_track_height)
728 {
729 continue;
730 }
731 }
732
733 for (int j = 0;
734 j < track->num_lanes; j++)
735 {
736 TrackLane * lane =
737 track->lanes[j];
738 for (int k = 0;
739 k < lane->num_regions;
740 k++)
741 {
742 ZRegion *r =
743 lane->regions[k];
744 g_warn_if_fail (
745 IS_REGION (r));
746 obj =
747 (ArrangerObject *) r;
748 nfo.obj = obj;
749 bool ret =
750 add_object_if_overlap (
751 self, &nfo);
752 if (!ret)
753 {
754 /* check lanes */
755 if (!track->
756 lanes_visible)
757 continue;
758 GdkRectangle lane_rect;
759 region_get_lane_full_rect (
760 lane->regions[k],
761 &lane_rect);
762 if (((rect &&
763 ui_rectangle_overlap (
764 &lane_rect,
765 rect)) ||
766 (!rect &&
767 ui_is_point_in_rect_hit (
768 &lane_rect,
769 true, true, x, y,
770 0, 0))) &&
771 arranger_object_get_arranger (obj) == self &&
772 !obj->deleted_temporarily)
773 {
774 array[*array_size] =
775 obj;
776 (*array_size)++;
777 }
778 }
779 }
780 }
781
782 /* chord regions */
783 for (int j = 0;
784 j < track->num_chord_regions;
785 j++)
786 {
787 ZRegion * cr =
788 track->chord_regions[j];
789 obj = (ArrangerObject *) cr;
790 nfo.obj = obj;
791 add_object_if_overlap (self, &nfo);
792 }
793
794 /* automation regions */
795 AutomationTracklist * atl =
796 track_get_automation_tracklist (
797 track);
798 if (atl &&
799 track->automation_visible)
800 {
801 for (int j = 0;
802 j < atl->num_ats;
803 j++)
804 {
805 AutomationTrack * at =
806 atl->ats[j];
807
808 if (!at->visible)
809 continue;
810
811 for (int k = 0;
812 k < at->num_regions;
813 k++)
814 {
815 obj =
816 (ArrangerObject *)
817 at->regions[k];
818 nfo.obj = obj;
819 add_object_if_overlap (
820 self, &nfo);
821 }
822 }
823 }
824 }
825 }
826
827 /* add overlapping scales */
828 if (type == ARRANGER_OBJECT_TYPE_ALL ||
829 type == ARRANGER_OBJECT_TYPE_SCALE_OBJECT)
830 {
831 for (int j = 0;
832 j < P_CHORD_TRACK->num_scales;
833 j++)
834 {
835 ScaleObject * scale =
836 P_CHORD_TRACK->scales[j];
837 obj =
838 (ArrangerObject *) scale;
839 nfo.obj = obj;
840 add_object_if_overlap (self, &nfo);
841 }
842 }
843
844 /* add overlapping markers */
845 if (type == ARRANGER_OBJECT_TYPE_ALL ||
846 type == ARRANGER_OBJECT_TYPE_MARKER)
847 {
848 for (int j = 0;
849 j < P_MARKER_TRACK->num_markers;
850 j++)
851 {
852 Marker * marker =
853 P_MARKER_TRACK->markers[j];
854 obj =
855 (ArrangerObject *) marker;
856 nfo.obj = obj;
857 add_object_if_overlap (self, &nfo);
858 }
859 }
860 break;
861 case TYPE (MIDI):
862 /* add overlapping midi notes */
863 if (type == ARRANGER_OBJECT_TYPE_ALL ||
864 type == ARRANGER_OBJECT_TYPE_MIDI_NOTE)
865 {
866 ZRegion * r =
867 clip_editor_get_region (CLIP_EDITOR);
868 if (!r)
869 break;
870
871 for (int i = 0; i < r->num_midi_notes;
872 i++)
873 {
874 MidiNote * mn = r->midi_notes[i];
875 obj =
876 (ArrangerObject *)
877 mn;
878 nfo.obj = obj;
879 add_object_if_overlap (self, &nfo);
880 }
881 }
882 break;
883 case TYPE (MIDI_MODIFIER):
884 /* add overlapping midi notes */
885 if (type == ARRANGER_OBJECT_TYPE_ALL ||
886 type == ARRANGER_OBJECT_TYPE_VELOCITY)
887 {
888 ZRegion * r =
889 clip_editor_get_region (CLIP_EDITOR);
890 if (!r)
891 break;
892
893 for (int i = 0; i < r->num_midi_notes;
894 i++)
895 {
896 MidiNote * mn = r->midi_notes[i];
897 g_return_if_fail (
898 IS_MIDI_NOTE (mn));
899 Velocity * vel = mn->vel;
900 g_return_if_fail (
901 IS_ARRANGER_OBJECT (vel));
902 obj = (ArrangerObject *) vel;
903 nfo.obj = obj;
904 add_object_if_overlap (self, &nfo);
905 }
906 }
907 break;
908 case TYPE (CHORD):
909 /* add overlapping midi notes */
910 if (type == ARRANGER_OBJECT_TYPE_ALL ||
911 type == ARRANGER_OBJECT_TYPE_CHORD_OBJECT)
912 {
913 ZRegion * r =
914 clip_editor_get_region (CLIP_EDITOR);
915 if (!r)
916 break;
917
918 for (int i = 0; i < r->num_chord_objects;
919 i++)
920 {
921 ChordObject * co =
922 r->chord_objects[i];
923 obj =
924 (ArrangerObject *) co;
925 g_return_if_fail (
926 co->chord_index <
927 CHORD_EDITOR->num_chords);
928 nfo.obj = obj;
929 add_object_if_overlap (self, &nfo);
930 }
931 }
932 break;
933 case TYPE (AUTOMATION):
934 /* add overlapping midi notes */
935 if (type == ARRANGER_OBJECT_TYPE_ALL ||
936 type == ARRANGER_OBJECT_TYPE_AUTOMATION_POINT)
937 {
938 ZRegion * r =
939 clip_editor_get_region (CLIP_EDITOR);
940 if (!r)
941 break;
942
943 for (int i = 0; i < r->num_aps; i++)
944 {
945 AutomationPoint * ap = r->aps[i];
946 obj = (ArrangerObject *) ap;
947 nfo.obj = obj;
948 add_object_if_overlap (self, &nfo);
949 }
950 }
951 break;
952 case TYPE (AUDIO):
953 /* no objects in audio arranger yet */
954 break;
955 default:
956 g_warn_if_reached ();
957 break;
958 }
959 }
960
961 /**
962 * Fills in the given array with the ArrangerObject's
963 * of the given type that appear in the given
964 * range.
965 *
966 * @param rect The rectangle to search in.
967 * @param type The type of arranger objects to find,
968 * or -1 to look for all objects.
969 * @param array The array to fill.
970 * @param array_size The size of the array to fill.
971 */
972 void
arranger_widget_get_hit_objects_in_rect(ArrangerWidget * self,ArrangerObjectType type,GdkRectangle * rect,ArrangerObject ** array,int * array_size)973 arranger_widget_get_hit_objects_in_rect (
974 ArrangerWidget * self,
975 ArrangerObjectType type,
976 GdkRectangle * rect,
977 ArrangerObject ** array,
978 int * array_size)
979 {
980 get_hit_objects (
981 self, type, rect, 0, 0, array, array_size);
982 }
983
984 /**
985 * Filters out objects from frozen tracks.
986 */
987 static void
filter_out_frozen_objects(ArrangerWidget * self,ArrangerObject ** objs,int * num_objs)988 filter_out_frozen_objects (
989 ArrangerWidget * self,
990 ArrangerObject ** objs,
991 int * num_objs)
992 {
993 if (self->type != ARRANGER_WIDGET_TYPE_TIMELINE)
994 {
995 return;
996 }
997
998 for (int i = *num_objs - 1; i >= 0; i--)
999 {
1000 ArrangerObject * obj = objs[i];
1001 Track * track =
1002 arranger_object_get_track (obj);
1003 g_return_if_fail (track);
1004
1005 if (track->frozen)
1006 {
1007 for (int j = i; j < *num_objs - 1; j++)
1008 {
1009 objs[j] = objs[j + 1];
1010 }
1011 (*num_objs)--;
1012 }
1013 }
1014 }
1015
1016 /**
1017 * Fills in the given array with the ArrangerObject's
1018 * of the given type that appear in the given
1019 * ranger.
1020 *
1021 * @param type The type of arranger objects to find,
1022 * or -1 to look for all objects.
1023 * @param x X, or -1 to not check x.
1024 * @param y Y, or -1 to not check y.
1025 * @param array The array to fill.
1026 * @param array_size The size of the array to fill.
1027 */
1028 void
arranger_widget_get_hit_objects_at_point(ArrangerWidget * self,ArrangerObjectType type,double x,double y,ArrangerObject ** array,int * array_size)1029 arranger_widget_get_hit_objects_at_point (
1030 ArrangerWidget * self,
1031 ArrangerObjectType type,
1032 double x,
1033 double y,
1034 ArrangerObject ** array,
1035 int * array_size)
1036 {
1037 get_hit_objects (
1038 self, type, NULL, x, y, array, array_size);
1039 }
1040
1041 /**
1042 * Returns if the arranger is in a moving-related
1043 * operation or starting a moving-related operation.
1044 *
1045 * Useful to know if we need transient widgets or
1046 * not.
1047 */
1048 bool
arranger_widget_is_in_moving_operation(ArrangerWidget * self)1049 arranger_widget_is_in_moving_operation (
1050 ArrangerWidget * self)
1051 {
1052 if (self->action ==
1053 UI_OVERLAY_ACTION_STARTING_MOVING ||
1054 self->action ==
1055 UI_OVERLAY_ACTION_STARTING_MOVING_COPY ||
1056 self->action ==
1057 UI_OVERLAY_ACTION_STARTING_MOVING_LINK ||
1058 self->action ==
1059 UI_OVERLAY_ACTION_MOVING ||
1060 self->action ==
1061 UI_OVERLAY_ACTION_MOVING_COPY ||
1062 self->action ==
1063 UI_OVERLAY_ACTION_MOVING_LINK)
1064 return true;
1065
1066 return false;
1067 }
1068
1069 /**
1070 * Moves the ArrangerSelections by the given
1071 * amount of ticks.
1072 *
1073 * @param ticks_diff Ticks to move by.
1074 * @param copy_moving 1 if copy-moving.
1075 */
1076 static void
move_items_x(ArrangerWidget * self,const double ticks_diff)1077 move_items_x (
1078 ArrangerWidget * self,
1079 const double ticks_diff)
1080 {
1081 ArrangerSelections * sel =
1082 arranger_widget_get_selections (self);
1083 g_return_if_fail (sel);
1084
1085 /* queue a redraw for the selections at their
1086 * current position before the move */
1087 EVENTS_PUSH_NOW (
1088 ET_ARRANGER_SELECTIONS_IN_TRANSIT, sel);
1089
1090 arranger_selections_add_ticks (
1091 sel, ticks_diff);
1092
1093 if (sel->type ==
1094 ARRANGER_SELECTIONS_TYPE_AUTOMATION)
1095 {
1096 /* re-sort the automation region */
1097 ZRegion * region =
1098 clip_editor_get_region (CLIP_EDITOR);
1099 g_return_if_fail (region);
1100 automation_region_force_sort (region);
1101 }
1102
1103 transport_recalculate_total_bars (TRANSPORT, sel);
1104
1105 EVENTS_PUSH_NOW (
1106 ET_ARRANGER_SELECTIONS_IN_TRANSIT, sel);
1107 }
1108
1109 /**
1110 * Gets the float value at the given Y coordinate
1111 * relative to the automation arranger.
1112 */
1113 static float
get_fvalue_at_y(ArrangerWidget * self,double y)1114 get_fvalue_at_y (
1115 ArrangerWidget * self,
1116 double y)
1117 {
1118 float height =
1119 (float)
1120 gtk_widget_get_allocated_height (
1121 GTK_WIDGET (self));
1122
1123 ZRegion * region =
1124 clip_editor_get_region (CLIP_EDITOR);
1125 g_return_val_if_fail (
1126 region &&
1127 region->id.type == REGION_TYPE_AUTOMATION,
1128 -1.f);
1129 AutomationTrack * at =
1130 region_get_automation_track (region);
1131
1132 /* get ratio from widget */
1133 float widget_value = height - (float) y;
1134 float widget_ratio =
1135 CLAMP (
1136 widget_value / height,
1137 0.f, 1.f);
1138 Port * port =
1139 port_find_from_identifier (&at->port_id);
1140 float automatable_value =
1141 control_port_normalized_val_to_real (
1142 port, widget_ratio);
1143
1144 return automatable_value;
1145 }
1146
1147 static void
move_items_y(ArrangerWidget * self,double offset_y)1148 move_items_y (
1149 ArrangerWidget * self,
1150 double offset_y)
1151 {
1152 ArrangerSelections * sel =
1153 arranger_widget_get_selections (self);
1154 g_return_if_fail (sel);
1155
1156 /* queue a redraw for the selections at their
1157 * current position before the move */
1158 arranger_selections_redraw (sel);
1159
1160 switch (self->type)
1161 {
1162 case TYPE (AUTOMATION):
1163 if (AUTOMATION_SELECTIONS->
1164 num_automation_points)
1165 {
1166 double offset_y_normalized =
1167 - offset_y /
1168 (double)
1169 gtk_widget_get_allocated_height (
1170 GTK_WIDGET (self));
1171 g_warn_if_fail (self->sel_at_start);
1172 (void) get_fvalue_at_y;
1173 for (int i = 0;
1174 i < AUTOMATION_SELECTIONS->
1175 num_automation_points; i++)
1176 {
1177 AutomationPoint * ap =
1178 AUTOMATION_SELECTIONS->
1179 automation_points[i];
1180 AutomationSelections * automation_sel =
1181 (AutomationSelections *)
1182 self->sel_at_start;
1183 AutomationPoint * start_ap =
1184 automation_sel->
1185 automation_points[i];
1186
1187 automation_point_set_fvalue (
1188 ap,
1189 start_ap->normalized_val +
1190 (float) offset_y_normalized,
1191 F_NORMALIZED, F_PUBLISH_EVENTS);
1192 }
1193 ArrangerObject * start_ap_obj =
1194 self->start_object;
1195 g_return_if_fail (start_ap_obj);
1196 /*arranger_object_widget_update_tooltip (*/
1197 /*Z_ARRANGER_OBJECT_WIDGET (*/
1198 /*start_ap_obj->widget), 1);*/
1199 }
1200 break;
1201 case TYPE (TIMELINE):
1202 {
1203 /* get old track, track where last change
1204 * happened, and new track */
1205 Track * track =
1206 timeline_arranger_widget_get_track_at_y (
1207 self, self->start_y + offset_y);
1208 Track * old_track =
1209 timeline_arranger_widget_get_track_at_y (
1210 self, self->start_y);
1211 Track * last_track =
1212 tracklist_get_visible_track_after_delta (
1213 TRACKLIST, old_track,
1214 self->visible_track_diff);
1215
1216 /* TODO automations and other lanes */
1217 TrackLane * lane =
1218 timeline_arranger_widget_get_track_lane_at_y (
1219 self, self->start_y + offset_y);
1220 TrackLane * old_lane =
1221 timeline_arranger_widget_get_track_lane_at_y (
1222 self, self->start_y);
1223 TrackLane * last_lane = NULL;
1224 if (old_lane)
1225 {
1226 Track * old_lane_track =
1227 track_lane_get_track (old_lane);
1228 last_lane =
1229 old_lane_track->lanes[
1230 old_lane->pos + self->lane_diff];
1231 }
1232
1233 /* if new track is equal, move lanes or
1234 * automation lanes */
1235 if (track && last_track &&
1236 track == last_track &&
1237 self->visible_track_diff == 0 &&
1238 old_lane && lane && last_lane)
1239 {
1240 int cur_diff =
1241 lane->pos - old_lane->pos;
1242 int delta =
1243 lane->pos - last_lane->pos;
1244 if (delta != 0)
1245 {
1246 bool moved =
1247 timeline_selections_move_regions_to_new_lanes (
1248 TL_SELECTIONS, delta);
1249 if (moved)
1250 {
1251 self->lane_diff =
1252 cur_diff;
1253 }
1254 }
1255 }
1256 /* otherwise move tracks */
1257 else if (track && last_track && old_track &&
1258 track != last_track)
1259 {
1260 int cur_diff =
1261 tracklist_get_visible_track_diff (
1262 TRACKLIST, old_track, track);
1263 int delta =
1264 tracklist_get_visible_track_diff (
1265 TRACKLIST, last_track, track);
1266 if (delta != 0)
1267 {
1268 bool moved =
1269 timeline_selections_move_regions_to_new_tracks (
1270 TL_SELECTIONS, delta);
1271 if (moved)
1272 {
1273 self->visible_track_diff =
1274 cur_diff;
1275 }
1276 }
1277 }
1278 }
1279 break;
1280 case TYPE (MIDI):
1281 {
1282 int y_delta;
1283 /* first note selected */
1284 int first_note_selected =
1285 ((MidiNote *) self->start_object)->val;
1286 /* note at cursor */
1287 int note_at_cursor =
1288 piano_roll_keys_widget_get_key_from_y (
1289 MW_PIANO_ROLL_KEYS,
1290 self->start_y + offset_y);
1291
1292 y_delta = note_at_cursor - first_note_selected;
1293 y_delta =
1294 midi_arranger_calc_deltamax_for_note_movement (y_delta);
1295
1296 for (int i = 0;
1297 i < MA_SELECTIONS->num_midi_notes; i++)
1298 {
1299 MidiNote * midi_note =
1300 MA_SELECTIONS->midi_notes[i];
1301 /*ArrangerObject * mn_obj =*/
1302 /*(ArrangerObject *) midi_note;*/
1303 midi_note_set_val (
1304 midi_note,
1305 (midi_byte_t)
1306 ((int) midi_note->val + y_delta));
1307 /*if (Z_IS_ARRANGER_OBJECT_WIDGET (*/
1308 /*mn_obj->widget))*/
1309 /*{*/
1310 /*arranger_object_widget_update_tooltip (*/
1311 /*Z_ARRANGER_OBJECT_WIDGET (*/
1312 /*mn_obj->widget), 0);*/
1313 /*}*/
1314 }
1315
1316 /*midi_arranger_listen_notes (*/
1317 /*self, 1);*/
1318 }
1319 break;
1320 default:
1321 break;
1322 }
1323 }
1324
1325 void
arranger_widget_select_all(ArrangerWidget * self,bool select,bool fire_events)1326 arranger_widget_select_all (
1327 ArrangerWidget * self,
1328 bool select,
1329 bool fire_events)
1330 {
1331 ArrangerSelections * sel =
1332 arranger_widget_get_selections (
1333 (ArrangerWidget *) self);
1334 g_return_if_fail (sel);
1335
1336 if (select)
1337 {
1338 /* TODO move array to ArrangerWidget struct
1339 * and allocate a large value dynamically
1340 * during initialization */
1341 ArrangerObject * objs[10000];
1342 int num_objs = 0;
1343 arranger_widget_get_all_objects (
1344 self, objs, &num_objs);
1345 for (int i = 0; i < num_objs; i++)
1346 {
1347 arranger_object_select (
1348 objs[i], F_SELECT, F_APPEND,
1349 F_NO_PUBLISH_EVENTS);
1350 }
1351
1352 if (fire_events)
1353 {
1354 EVENTS_PUSH (
1355 ET_ARRANGER_SELECTIONS_CREATED, sel);
1356 }
1357 }
1358 else
1359 {
1360 g_debug ("deselecting all");
1361 if (arranger_selections_has_any (sel))
1362 {
1363 arranger_selections_clear (
1364 sel, F_NO_FREE, F_NO_PUBLISH_EVENTS);
1365
1366 if (fire_events)
1367 {
1368 EVENTS_PUSH_NOW (
1369 ET_ARRANGER_SELECTIONS_REMOVED,
1370 sel);
1371 }
1372 }
1373 }
1374 }
1375
1376 /**
1377 * Returns the EditorSettings corresponding to
1378 * the given arranger.
1379 */
1380 EditorSettings *
arranger_widget_get_editor_settings(ArrangerWidget * self)1381 arranger_widget_get_editor_settings (
1382 ArrangerWidget * self)
1383 {
1384 switch (self->type)
1385 {
1386 case ARRANGER_WIDGET_TYPE_TIMELINE:
1387 return &PRJ_TIMELINE->editor_settings;
1388 break;
1389 case ARRANGER_WIDGET_TYPE_AUTOMATION:
1390 return &AUTOMATION_EDITOR->editor_settings;
1391 break;
1392 case ARRANGER_WIDGET_TYPE_AUDIO:
1393 return &AUDIO_CLIP_EDITOR->editor_settings;
1394 break;
1395 case ARRANGER_WIDGET_TYPE_MIDI:
1396 case ARRANGER_WIDGET_TYPE_MIDI_MODIFIER:
1397 return &PIANO_ROLL->editor_settings;
1398 break;
1399 case ARRANGER_WIDGET_TYPE_CHORD:
1400 return &CHORD_EDITOR->editor_settings;
1401 break;
1402 default: break;
1403 }
1404
1405 g_return_val_if_reached (NULL);
1406 }
1407
1408 static void
show_context_menu_audio(ArrangerWidget * self,gdouble x,gdouble y)1409 show_context_menu_audio (
1410 ArrangerWidget * self,
1411 gdouble x,
1412 gdouble y)
1413 {
1414 GtkWidget *menu, *menuitem;
1415
1416 menu = gtk_menu_new();
1417
1418 menuitem =
1419 gtk_menu_item_new_with_label ("Do something");
1420
1421 gtk_menu_shell_append (
1422 GTK_MENU_SHELL(menu), menuitem);
1423
1424 gtk_widget_show_all(menu);
1425
1426 gtk_menu_popup_at_pointer (GTK_MENU(menu), NULL);
1427 }
1428
1429 static void
show_context_menu_chord(ArrangerWidget * self,gdouble x,gdouble y)1430 show_context_menu_chord (
1431 ArrangerWidget * self,
1432 gdouble x,
1433 gdouble y)
1434 {
1435 }
1436
1437 static void
show_context_menu_midi_modifier(ArrangerWidget * self,gdouble x,gdouble y)1438 show_context_menu_midi_modifier (
1439 ArrangerWidget * self,
1440 gdouble x,
1441 gdouble y)
1442 {
1443 }
1444
1445 static void
show_context_menu(ArrangerWidget * self,gdouble x,gdouble y)1446 show_context_menu (
1447 ArrangerWidget * self,
1448 gdouble x,
1449 gdouble y)
1450 {
1451 switch (self->type)
1452 {
1453 case TYPE (TIMELINE):
1454 timeline_arranger_widget_show_context_menu (
1455 self, x, y);
1456 break;
1457 case TYPE (MIDI):
1458 midi_arranger_show_context_menu (self, x, y);
1459 break;
1460 case TYPE (MIDI_MODIFIER):
1461 show_context_menu_midi_modifier (self, x, y);
1462 break;
1463 case TYPE (CHORD):
1464 show_context_menu_chord (self, x, y);
1465 break;
1466 case TYPE (AUTOMATION):
1467 automation_arranger_widget_show_context_menu (
1468 self, x, y);
1469 break;
1470 case TYPE (AUDIO):
1471 show_context_menu_audio (self, x, y);
1472 break;
1473 }
1474 }
1475
1476 static void
on_right_click(GtkGestureMultiPress * gesture,gint n_press,gdouble x,gdouble y,ArrangerWidget * self)1477 on_right_click (
1478 GtkGestureMultiPress *gesture,
1479 gint n_press,
1480 gdouble x,
1481 gdouble y,
1482 ArrangerWidget * self)
1483 {
1484 g_message ("right mb released");
1485 #if 0
1486 if (n_press != 1)
1487 return;
1488
1489 MAIN_WINDOW->last_focused = GTK_WIDGET (self);
1490
1491 /* if object clicked and object is unselected,
1492 * select it */
1493 ArrangerObject * obj =
1494 arranger_widget_get_hit_arranger_object (
1495 (ArrangerWidget *) self,
1496 ARRANGER_OBJECT_TYPE_ALL, x, y);
1497 if (obj)
1498 {
1499 if (!arranger_object_is_selected (obj))
1500 {
1501 arranger_object_select (
1502 obj, F_SELECT, F_NO_APPEND,
1503 F_NO_PUBLISH_EVENTS);
1504 }
1505 }
1506
1507 show_context_menu (self, x, y);
1508 #endif
1509 }
1510
1511 static void
auto_scroll(ArrangerWidget * self,int x,int y)1512 auto_scroll (
1513 ArrangerWidget * self,
1514 int x,
1515 int y)
1516 {
1517 /* figure out if we should scroll */
1518 int scroll_h = 0, scroll_v = 0;
1519 switch (self->action)
1520 {
1521 case UI_OVERLAY_ACTION_MOVING:
1522 case UI_OVERLAY_ACTION_MOVING_COPY:
1523 case UI_OVERLAY_ACTION_MOVING_LINK:
1524 case UI_OVERLAY_ACTION_CREATING_MOVING:
1525 case UI_OVERLAY_ACTION_SELECTING:
1526 case UI_OVERLAY_ACTION_RAMPING:
1527 scroll_h = 1;
1528 scroll_v = 1;
1529 break;
1530 case UI_OVERLAY_ACTION_RESIZING_R:
1531 case UI_OVERLAY_ACTION_RESIZING_L:
1532 case UI_OVERLAY_ACTION_STRETCHING_L:
1533 case UI_OVERLAY_ACTION_CREATING_RESIZING_R:
1534 case UI_OVERLAY_ACTION_STRETCHING_R:
1535 case UI_OVERLAY_ACTION_AUTOFILLING:
1536 case UI_OVERLAY_ACTION_AUDITIONING:
1537 scroll_h = 1;
1538 break;
1539 case UI_OVERLAY_ACTION_RESIZING_UP:
1540 scroll_v = 1;
1541 break;
1542 default:
1543 break;
1544 }
1545
1546 if (!scroll_h && !scroll_v)
1547 return;
1548
1549 GtkScrolledWindow *scroll =
1550 arranger_widget_get_scrolled_window (self);
1551 g_return_if_fail (scroll);
1552 int h_scroll_speed = 20;
1553 int v_scroll_speed = 10;
1554 int border_distance = 5;
1555 int scroll_width =
1556 gtk_widget_get_allocated_width (
1557 GTK_WIDGET (scroll));
1558 int scroll_height =
1559 gtk_widget_get_allocated_height (
1560 GTK_WIDGET (scroll));
1561 GtkAdjustment *hadj =
1562 gtk_scrolled_window_get_hadjustment (
1563 GTK_SCROLLED_WINDOW (scroll));
1564 GtkAdjustment *vadj =
1565 gtk_scrolled_window_get_vadjustment (
1566 GTK_SCROLLED_WINDOW (scroll));
1567 int v_delta = 0;
1568 int h_delta = 0;
1569 int adj_x =
1570 (int)
1571 gtk_adjustment_get_value (hadj);
1572 int adj_y =
1573 (int)
1574 gtk_adjustment_get_value (vadj);
1575 if (y + border_distance
1576 >= adj_y + scroll_height)
1577 {
1578 v_delta = v_scroll_speed;
1579 }
1580 else if (y - border_distance <= adj_y)
1581 {
1582 v_delta = - v_scroll_speed;
1583 }
1584 if (x + border_distance >= adj_x + scroll_width)
1585 {
1586 h_delta = h_scroll_speed;
1587 }
1588 else if (x - border_distance <= adj_x)
1589 {
1590 h_delta = - h_scroll_speed;
1591 }
1592 if (h_delta != 0 && scroll_h)
1593 {
1594 gtk_adjustment_set_value (
1595 hadj,
1596 gtk_adjustment_get_value (hadj) + h_delta);
1597 }
1598 if (v_delta != 0 && scroll_v)
1599 {
1600 gtk_adjustment_set_value (
1601 vadj,
1602 gtk_adjustment_get_value (vadj) + v_delta);
1603 }
1604
1605 return;
1606 }
1607
1608 /**
1609 * Called from MainWindowWidget because the
1610 * events don't reach here.
1611 */
1612 gboolean
arranger_widget_on_key_release(GtkWidget * widget,GdkEventKey * event,ArrangerWidget * self)1613 arranger_widget_on_key_release (
1614 GtkWidget *widget,
1615 GdkEventKey *event,
1616 ArrangerWidget * self)
1617 {
1618 self->key_is_pressed = 0;
1619
1620 const guint keyval = event->keyval;
1621
1622 if (z_gtk_keyval_is_ctrl (keyval))
1623 {
1624 self->ctrl_held = 0;
1625 }
1626
1627 if (z_gtk_keyval_is_shift (keyval))
1628 {
1629 self->shift_held = 0;
1630 }
1631 if (z_gtk_keyval_is_alt (keyval))
1632 {
1633 self->alt_held = 0;
1634 }
1635
1636 if (ACTION_IS (STARTING_MOVING))
1637 {
1638 if (self->alt_held && self->can_link)
1639 self->action =
1640 UI_OVERLAY_ACTION_MOVING_LINK;
1641 else if (self->ctrl_held)
1642 self->action =
1643 UI_OVERLAY_ACTION_MOVING_COPY;
1644 else
1645 self->action =
1646 UI_OVERLAY_ACTION_MOVING;
1647 }
1648 else if (ACTION_IS (MOVING) &&
1649 self->alt_held &&
1650 self->can_link)
1651 {
1652 self->action =
1653 UI_OVERLAY_ACTION_MOVING_LINK;
1654 }
1655 else if (ACTION_IS (MOVING) &&
1656 self->ctrl_held)
1657 {
1658 self->action =
1659 UI_OVERLAY_ACTION_MOVING_COPY;
1660 }
1661 else if (ACTION_IS (MOVING_LINK) &&
1662 !self->alt_held &&
1663 self->can_link)
1664 {
1665 self->action =
1666 self->ctrl_held ?
1667 UI_OVERLAY_ACTION_MOVING_COPY :
1668 UI_OVERLAY_ACTION_MOVING;
1669 }
1670 else if (ACTION_IS (MOVING_COPY) &&
1671 !self->ctrl_held)
1672 {
1673 self->action =
1674 UI_OVERLAY_ACTION_MOVING;
1675 }
1676
1677 if (self->type == TYPE (TIMELINE))
1678 {
1679 timeline_arranger_widget_set_cut_lines_visible (
1680 self);
1681 }
1682
1683 /*arranger_widget_update_visibility (self);*/
1684
1685 arranger_widget_refresh_cursor (
1686 self);
1687
1688 return TRUE;
1689 }
1690
1691 /**
1692 * Called from MainWindowWidget because some
1693 * events don't reach here.
1694 */
1695 gboolean
arranger_widget_on_key_action(GtkWidget * widget,GdkEventKey * event,ArrangerWidget * self)1696 arranger_widget_on_key_action (
1697 GtkWidget *widget,
1698 GdkEventKey *event,
1699 ArrangerWidget * self)
1700 {
1701 const guint keyval = event->keyval;
1702
1703 g_debug ("arranger widget key action");
1704
1705 if (z_gtk_keyval_is_ctrl (keyval))
1706 {
1707 self->ctrl_held = 1;
1708 }
1709
1710 if (z_gtk_keyval_is_shift (keyval))
1711 {
1712 self->shift_held = 1;
1713 }
1714 if (z_gtk_keyval_is_alt (keyval))
1715 {
1716 self->alt_held = 1;
1717 }
1718
1719 if (ACTION_IS (STARTING_MOVING))
1720 {
1721 if (self->ctrl_held)
1722 self->action =
1723 UI_OVERLAY_ACTION_MOVING_COPY;
1724 else
1725 self->action =
1726 UI_OVERLAY_ACTION_MOVING;
1727 }
1728 else if (ACTION_IS (MOVING) &&
1729 self->alt_held &&
1730 self->can_link)
1731 {
1732 self->action =
1733 UI_OVERLAY_ACTION_MOVING_LINK;
1734 }
1735 else if (ACTION_IS (MOVING) && self->ctrl_held)
1736 {
1737 self->action =
1738 UI_OVERLAY_ACTION_MOVING_COPY;
1739 }
1740 else if (ACTION_IS (MOVING_LINK) &&
1741 !self->alt_held)
1742 {
1743 self->action =
1744 self->ctrl_held ?
1745 UI_OVERLAY_ACTION_MOVING_COPY :
1746 UI_OVERLAY_ACTION_MOVING;
1747 }
1748 else if (ACTION_IS (MOVING_COPY) &&
1749 !self->ctrl_held)
1750 {
1751 self->action =
1752 UI_OVERLAY_ACTION_MOVING;
1753 }
1754 else if (ACTION_IS (NONE))
1755 {
1756 ArrangerSelections * sel =
1757 arranger_widget_get_selections (self);
1758 g_return_val_if_fail (sel, false);
1759
1760 if (arranger_selections_has_any (sel))
1761 {
1762 double move_ticks = 0.0;
1763 if (self->ctrl_held)
1764 {
1765 Position tmp;
1766 position_set_to_bar (&tmp, 2);
1767 move_ticks = tmp.ticks;
1768 }
1769 else
1770 {
1771 SnapGrid * sg =
1772 arranger_widget_get_snap_grid (
1773 self);
1774 move_ticks =
1775 (double)
1776 snap_grid_get_snap_ticks (sg);
1777 }
1778
1779 /* check arrow movement */
1780 if (keyval == GDK_KEY_Left)
1781 {
1782 Position min_possible_pos;
1783 arranger_widget_get_min_possible_position (
1784 self, &min_possible_pos);
1785
1786 /* get earliest object */
1787 ArrangerObject * obj =
1788 arranger_selections_get_first_object (
1789 sel);
1790
1791 if (obj->pos.ticks - move_ticks >=
1792 min_possible_pos.ticks)
1793 {
1794 GError * err = NULL;
1795 bool ret =
1796 arranger_selections_action_perform_move (
1797 sel, - move_ticks, 0, 0,
1798 0, 0, 0, F_NOT_ALREADY_MOVED,
1799 &err);
1800 if (!ret)
1801 {
1802 HANDLE_ERROR (
1803 err, "%s",
1804 _("Failed to move "
1805 "selection"));
1806 }
1807
1808 /* scroll left if needed */
1809 arranger_widget_scroll_until_obj (
1810 self, obj,
1811 1, 0, 1, SCROLL_PADDING);
1812 }
1813 }
1814 else if (keyval == GDK_KEY_Right)
1815 {
1816 GError * err = NULL;
1817 bool ret =
1818 arranger_selections_action_perform_move (
1819 sel, move_ticks, 0, 0, 0, 0, 0,
1820 F_NOT_ALREADY_MOVED,
1821 &err);
1822 if (!ret)
1823 {
1824 HANDLE_ERROR (
1825 err, "%s",
1826 _("Failed to move selection"));
1827 }
1828
1829 /* get latest object */
1830 ArrangerObject * obj =
1831 arranger_selections_get_last_object (
1832 sel);
1833
1834 /* scroll right if needed */
1835 arranger_widget_scroll_until_obj (
1836 self, obj,
1837 1, 0, 0, SCROLL_PADDING);
1838 }
1839 else if (keyval == GDK_KEY_Down)
1840 {
1841 if (self == MW_MIDI_ARRANGER ||
1842 self == MW_MIDI_MODIFIER_ARRANGER)
1843 {
1844 int pitch_delta = 0;
1845 MidiNote * mn =
1846 midi_arranger_selections_get_lowest_note (
1847 MA_SELECTIONS);
1848 ArrangerObject * obj =
1849 (ArrangerObject *) mn;
1850
1851 if (self->ctrl_held)
1852 {
1853 if (mn->val - 12 >= 0)
1854 pitch_delta = - 12;
1855 }
1856 else
1857 {
1858 if (mn->val - 1 >= 0)
1859 pitch_delta = - 1;
1860 }
1861
1862 if (pitch_delta)
1863 {
1864 GError * err = NULL;
1865 bool ret =
1866 arranger_selections_action_perform_move_midi (
1867 sel, 0, pitch_delta,
1868 F_NOT_ALREADY_MOVED,
1869 &err);
1870 if (!ret)
1871 {
1872 HANDLE_ERROR (
1873 err, "%s",
1874 _("Failed to move "
1875 "selection"));
1876 }
1877
1878 /* scroll down if needed */
1879 arranger_widget_scroll_until_obj (
1880 self, obj,
1881 0, 0, 0, SCROLL_PADDING);
1882 }
1883 }
1884 else if (self == MW_CHORD_ARRANGER)
1885 {
1886 GError * err = NULL;
1887 bool ret =
1888 arranger_selections_action_perform_move_chord (
1889 sel, 0, -1,
1890 F_NOT_ALREADY_MOVED, &err);
1891 if (!ret)
1892 {
1893 HANDLE_ERROR (
1894 err, "%s",
1895 _("Failed to move chords"));
1896 }
1897 }
1898 else if (self == MW_TIMELINE)
1899 {
1900 /* TODO check if can be moved */
1901 /*action =*/
1902 /*arranger_selections_action_new_move (*/
1903 /*sel, 0, -1, 0, 0, 0);*/
1904 /*undo_manager_perform (*/
1905 /*UNDO_MANAGER, action);*/
1906 }
1907 }
1908 else if (keyval == GDK_KEY_Up)
1909 {
1910 if (self == MW_MIDI_ARRANGER ||
1911 self == MW_MIDI_MODIFIER_ARRANGER)
1912 {
1913 int pitch_delta = 0;
1914 MidiNote * mn =
1915 midi_arranger_selections_get_highest_note (
1916 MA_SELECTIONS);
1917 ArrangerObject * obj =
1918 (ArrangerObject *) mn;
1919
1920 if (self->ctrl_held)
1921 {
1922 if (mn->val + 12 < 128)
1923 pitch_delta = 12;
1924 }
1925 else
1926 {
1927 if (mn->val + 1 < 128)
1928 pitch_delta = 1;
1929 }
1930
1931 if (pitch_delta)
1932 {
1933 GError * err = NULL;
1934 bool ret =
1935 arranger_selections_action_perform_move_midi (
1936 sel, 0, pitch_delta,
1937 F_NOT_ALREADY_MOVED,
1938 &err);
1939 if (!ret)
1940 {
1941 HANDLE_ERROR (
1942 err, "%s",
1943 _("Failed to move "
1944 "selection"));
1945 }
1946
1947 /* scroll up if needed */
1948 arranger_widget_scroll_until_obj (
1949 self, obj,
1950 0, 1, 0, SCROLL_PADDING);
1951 }
1952 }
1953 else if (self == MW_CHORD_ARRANGER)
1954 {
1955 GError * err = NULL;
1956 bool ret =
1957 arranger_selections_action_perform_move_chord (
1958 sel, 0, 1,
1959 F_NOT_ALREADY_MOVED, &err);
1960 if (!ret)
1961 {
1962 HANDLE_ERROR (
1963 err, "%s",
1964 _("Failed to move "
1965 "selection"));
1966 }
1967 }
1968 else if (self == MW_TIMELINE)
1969 {
1970 /* TODO check if can be moved */
1971 /*action =*/
1972 /*arranger_selections_action_new_move (*/
1973 /*sel, 0, 1, 0, 0, 0);*/
1974 /*undo_manager_perform (*/
1975 /*UNDO_MANAGER, action);*/
1976 }
1977 }
1978 } /* arranger selections has any */
1979 }
1980
1981 if (self->type == TYPE (TIMELINE))
1982 {
1983 timeline_arranger_widget_set_cut_lines_visible (
1984 self);
1985 }
1986
1987 /*arranger_widget_update_visibility (self);*/
1988
1989 arranger_widget_refresh_cursor (self);
1990
1991 /*if (num > 0)*/
1992 /*auto_scroll (self);*/
1993
1994 return true;
1995 }
1996
1997 /**
1998 * Sets the highlight rectangle.
1999 *
2000 * @param rect The rectangle, or NULL to
2001 * unset/unhighlight.
2002 */
2003 void
arranger_widget_set_highlight_rect(ArrangerWidget * self,GdkRectangle * rect)2004 arranger_widget_set_highlight_rect (
2005 ArrangerWidget * self,
2006 GdkRectangle * rect)
2007 {
2008 if (rect)
2009 {
2010 self->is_highlighted = true;
2011 /*self->prev_highlight_rect =*/
2012 /*self->highlight_rect;*/
2013 self->highlight_rect = *rect;
2014 }
2015 else
2016 {
2017 self->is_highlighted = false;
2018 }
2019
2020 EVENTS_PUSH (ET_ARRANGER_HIGHLIGHT_CHANGED, self);
2021 }
2022
2023 /**
2024 * On button press.
2025 *
2026 * This merely sets the number of clicks and
2027 * objects clicked on. It is always called
2028 * before drag_begin, so the logic is done in drag_begin.
2029 */
2030 static void
multipress_pressed(GtkGestureMultiPress * gesture,gint n_press,gdouble x,gdouble y,ArrangerWidget * self)2031 multipress_pressed (
2032 GtkGestureMultiPress * gesture,
2033 gint n_press,
2034 gdouble x,
2035 gdouble y,
2036 ArrangerWidget * self)
2037 {
2038 g_debug (
2039 "arranger multipress pressed - npress %d",
2040 n_press);
2041
2042 /* set number of presses */
2043 self->n_press = n_press;
2044
2045 /* set modifier button states */
2046 GdkModifierType state_mask =
2047 ui_get_state_mask (
2048 GTK_GESTURE (gesture));
2049 if (state_mask & GDK_SHIFT_MASK)
2050 self->shift_held = 1;
2051 if (state_mask & GDK_CONTROL_MASK)
2052 self->ctrl_held = 1;
2053
2054 PROJECT->last_selection =
2055 self->type == ARRANGER_WIDGET_TYPE_TIMELINE ?
2056 SELECTION_TYPE_TIMELINE :
2057 SELECTION_TYPE_EDITOR;
2058 EVENTS_PUSH (
2059 ET_PROJECT_SELECTION_TYPE_CHANGED, NULL);
2060 }
2061
2062 /**
2063 * Called when an item needs to be created at the
2064 * given position.
2065 *
2066 * @param autofilling Whether this is part of an
2067 * autofill action.
2068 */
2069 NONNULL
2070 static void
create_item(ArrangerWidget * self,double start_x,double start_y,bool autofilling)2071 create_item (
2072 ArrangerWidget * self,
2073 double start_x,
2074 double start_y,
2075 bool autofilling)
2076 {
2077 /* something will be created */
2078 Position pos;
2079 Track * track = NULL;
2080 AutomationTrack * at = NULL;
2081 int note, chord_index;
2082 ZRegion * region = NULL;
2083
2084 /* get the position */
2085 arranger_widget_px_to_pos (
2086 self, start_x, &pos, F_PADDING);
2087
2088 /* make sure the position is positive */
2089 Position init_pos;
2090 position_init (&init_pos);
2091 if (position_is_before (&pos, &init_pos))
2092 {
2093 position_init (&pos);
2094 }
2095
2096 /* snap it */
2097 if (!self->shift_held &&
2098 SNAP_GRID_ANY_SNAP (self->snap_grid))
2099 {
2100 Track * track_for_snap = NULL;
2101 if (self->type == TYPE (TIMELINE))
2102 {
2103 track_for_snap =
2104 timeline_arranger_widget_get_track_at_y (
2105 self, start_y);
2106 }
2107 position_snap (
2108 &self->earliest_obj_start_pos,
2109 &pos, track_for_snap, NULL,
2110 self->snap_grid);
2111 /*start_x =*/
2112 /*arranger_widget_pos_to_px (*/
2113 /*self, &pos, true);*/
2114 }
2115
2116 g_message (
2117 "creating item at %f,%f", start_x, start_y);
2118
2119 switch (self->type)
2120 {
2121 case TYPE (TIMELINE):
2122 /* figure out if we are creating a region or
2123 * automation point */
2124 at =
2125 timeline_arranger_widget_get_at_at_y (
2126 self, start_y);
2127 track =
2128 timeline_arranger_widget_get_track_at_y (
2129 self, start_y);
2130
2131 /* creating automation region */
2132 if (at)
2133 {
2134 timeline_arranger_widget_create_region (
2135 self,
2136 REGION_TYPE_AUTOMATION, track, NULL, at,
2137 &pos);
2138 }
2139 /* double click inside a track */
2140 else if (track)
2141 {
2142 TrackLane * lane =
2143 timeline_arranger_widget_get_track_lane_at_y (
2144 self, start_y);
2145 switch (track->type)
2146 {
2147 case TRACK_TYPE_INSTRUMENT:
2148 timeline_arranger_widget_create_region (
2149 self, REGION_TYPE_MIDI, track,
2150 lane, NULL, &pos);
2151 break;
2152 case TRACK_TYPE_MIDI:
2153 timeline_arranger_widget_create_region (
2154 self, REGION_TYPE_MIDI, track,
2155 lane, NULL, &pos);
2156 break;
2157 case TRACK_TYPE_AUDIO:
2158 break;
2159 case TRACK_TYPE_MASTER:
2160 break;
2161 case TRACK_TYPE_CHORD:
2162 timeline_arranger_widget_create_chord_or_scale (
2163 self, track, start_y, &pos);
2164 break;
2165 case TRACK_TYPE_AUDIO_BUS:
2166 break;
2167 case TRACK_TYPE_AUDIO_GROUP:
2168 break;
2169 case TRACK_TYPE_MARKER:
2170 timeline_arranger_widget_create_marker (
2171 self, track, &pos);
2172 break;
2173 default:
2174 /* TODO */
2175 break;
2176 }
2177 }
2178 break;
2179 case TYPE (MIDI):
2180 /* find the note and region at x,y */
2181 note =
2182 piano_roll_keys_widget_get_key_from_y (
2183 MW_PIANO_ROLL_KEYS, start_y);
2184 region =
2185 clip_editor_get_region (CLIP_EDITOR);
2186
2187 /* create a note */
2188 if (region)
2189 {
2190 midi_arranger_widget_create_note (
2191 self, &pos, note, (ZRegion *) region);
2192 }
2193 break;
2194 case TYPE (MIDI_MODIFIER):
2195 case TYPE (AUDIO):
2196 break;
2197 case TYPE (CHORD):
2198 /* find the chord and region at x,y */
2199 chord_index =
2200 chord_arranger_widget_get_chord_at_y (
2201 start_y);
2202 region =
2203 clip_editor_get_region (CLIP_EDITOR);
2204
2205 /* create a chord object */
2206 if (region && chord_index <
2207 CHORD_EDITOR->num_chords)
2208 {
2209 chord_arranger_widget_create_chord (
2210 self, &pos, chord_index,
2211 region);
2212 }
2213 break;
2214 case TYPE (AUTOMATION):
2215 region =
2216 clip_editor_get_region (CLIP_EDITOR);
2217
2218 if (region)
2219 {
2220 automation_arranger_widget_create_ap (
2221 self, &pos, start_y, region,
2222 autofilling);
2223 }
2224 break;
2225 }
2226
2227 if (!autofilling)
2228 {
2229 /* set the start selections */
2230 ArrangerSelections * sel =
2231 arranger_widget_get_selections (self);
2232 g_return_if_fail (sel);
2233 self->sel_at_start =
2234 arranger_selections_clone (sel);
2235 }
2236 }
2237
2238 /**
2239 * Called to autofill at the given position.
2240 *
2241 * In the case of velocities, this will set the
2242 * velocity wherever hit.
2243 *
2244 * In the case of automation, this will create or
2245 * edit the automation point at the given position.
2246 *
2247 * In other cases, this will create an object with
2248 * the default length at the given position, unless
2249 * an object already exists there.
2250 */
2251 NONNULL
2252 static void
autofill(ArrangerWidget * self,double x,double y)2253 autofill (
2254 ArrangerWidget * self,
2255 double x,
2256 double y)
2257 {
2258 /* make sure values are valid */
2259 x = MAX (x, 0);
2260 y = MAX (y, 0);
2261
2262 /* start autofill if not started yet */
2263 if (self->action != UI_OVERLAY_ACTION_AUTOFILLING)
2264 {
2265 self->action =
2266 UI_OVERLAY_ACTION_AUTOFILLING;
2267 ArrangerSelections * sel =
2268 arranger_widget_get_selections (self);
2269 g_return_if_fail (sel);
2270
2271 /* clear the actual selections to append
2272 * created objects */
2273 arranger_selections_clear (
2274 sel, F_NO_FREE, F_NO_PUBLISH_EVENTS);
2275
2276 /* also clear the selections at start so we
2277 * can append the affected objects */
2278 if (self->sel_at_start)
2279 {
2280 arranger_selections_clear (
2281 self->sel_at_start, F_FREE,
2282 F_NO_PUBLISH_EVENTS);
2283 }
2284 if (!self->sel_at_start)
2285 {
2286 self->sel_at_start =
2287 arranger_selections_clone (sel);
2288 }
2289
2290 ZRegion * clip_editor_region =
2291 clip_editor_get_region (CLIP_EDITOR);
2292 if (clip_editor_region)
2293 {
2294 self->region_at_start =
2295 (ZRegion *)
2296 arranger_object_clone (
2297 (ArrangerObject *)
2298 clip_editor_region);
2299 }
2300 else
2301 {
2302 self->region_at_start = NULL;
2303 }
2304 }
2305
2306 if (self->type == TYPE (MIDI_MODIFIER))
2307 {
2308 midi_modifier_arranger_set_hit_velocity_vals (
2309 self, x, y, true);
2310 }
2311 else if (self->type == TYPE (AUTOMATION))
2312 {
2313 /* move aps or create ap */
2314 if (!automation_arranger_move_hit_aps (
2315 self, x, y))
2316 {
2317 create_item (self, x, y, true);
2318 }
2319 }
2320 else
2321 {
2322 ArrangerObject * obj =
2323 arranger_widget_get_hit_arranger_object (
2324 self, ARRANGER_OBJECT_TYPE_ALL, x, y);
2325
2326 /* don't write over object */
2327 if (obj)
2328 {
2329 g_message (
2330 "object already exists at %f,%f, "
2331 "skipping",
2332 x, y);
2333 return;
2334 }
2335 create_item (self, x, y, true);
2336 }
2337 }
2338
2339 static void
drag_cancel(GtkGesture * gesture,GdkEventSequence * sequence,ArrangerWidget * self)2340 drag_cancel (
2341 GtkGesture * gesture,
2342 GdkEventSequence * sequence,
2343 ArrangerWidget * self)
2344 {
2345 g_message ("drag cancelled");
2346 }
2347
2348 /**
2349 * Sets the start pos of the earliest object and
2350 * the flag whether the earliest object exists.
2351 */
2352 NONNULL
2353 static void
set_earliest_obj(ArrangerWidget * self)2354 set_earliest_obj (
2355 ArrangerWidget * self)
2356 {
2357 ArrangerSelections * sel =
2358 arranger_widget_get_selections (self);
2359 g_return_if_fail (sel);
2360 if (arranger_selections_has_any (sel))
2361 {
2362 arranger_selections_get_start_pos (
2363 sel, &self->earliest_obj_start_pos,
2364 F_GLOBAL);
2365 self->earliest_obj_exists = 1;
2366 }
2367 else
2368 {
2369 self->earliest_obj_exists = 0;
2370 }
2371 }
2372
2373 /**
2374 * Checks for the first object hit, sets the
2375 * appropriate action and selects it.
2376 *
2377 * @return If an object was handled or not.
2378 */
2379 static bool
on_drag_begin_handle_hit_object(ArrangerWidget * self,const double x,const double y)2380 on_drag_begin_handle_hit_object (
2381 ArrangerWidget * self,
2382 const double x,
2383 const double y)
2384 {
2385 ArrangerObject * obj =
2386 arranger_widget_get_hit_arranger_object (
2387 self, ARRANGER_OBJECT_TYPE_ALL, x, y);
2388
2389 (void) filter_out_frozen_objects;
2390 if (!obj || arranger_object_is_frozen (obj))
2391 {
2392 return false;
2393 }
2394
2395 /* get x,y as local to the object */
2396 int wx = (int) x - obj->full_rect.x;
2397 int wy = (int) y - obj->full_rect.y;
2398
2399 /* remember object and pos */
2400 self->start_object = obj;
2401 self->start_pos_px = x;
2402
2403 /* get flags */
2404 bool is_fade_in_point =
2405 arranger_object_is_fade_in (obj, wx, wy, 1, 0);
2406 bool is_fade_out_point =
2407 arranger_object_is_fade_out (obj, wx, wy, 1, 0);
2408 bool is_fade_in_outer =
2409 arranger_object_is_fade_in (obj, wx, wy, 0, 1);
2410 bool is_fade_out_outer =
2411 arranger_object_is_fade_out (obj, wx, wy, 0, 1);
2412 bool is_resize_l =
2413 arranger_object_is_resize_l (obj, wx);
2414 bool is_resize_r =
2415 arranger_object_is_resize_r (obj, wx);
2416 bool is_resize_up =
2417 arranger_object_is_resize_up (obj, wx, wy);
2418 bool is_resize_loop =
2419 arranger_object_is_resize_loop (obj, wy);
2420 bool show_cut_lines =
2421 arranger_object_should_show_cut_lines (
2422 obj, self->alt_held);
2423 bool is_rename =
2424 arranger_object_is_rename (obj, wx, wy);
2425 bool is_selected =
2426 arranger_object_is_selected (obj);
2427 self->start_object_was_selected = is_selected;
2428
2429 /* select object if unselected */
2430 switch (P_TOOL)
2431 {
2432 case TOOL_SELECT_NORMAL:
2433 case TOOL_SELECT_STRETCH:
2434 case TOOL_EDIT:
2435 if (!is_selected)
2436 {
2437 if (self->ctrl_held)
2438 {
2439 /* append to selections */
2440 arranger_object_select (
2441 obj, F_SELECT, F_APPEND,
2442 F_PUBLISH_EVENTS);
2443 }
2444 else
2445 {
2446 /* make it the only selection */
2447 arranger_object_select (
2448 obj, F_SELECT, F_NO_APPEND,
2449 F_PUBLISH_EVENTS);
2450 g_message ("making only selection");
2451 }
2452 }
2453 break;
2454 case TOOL_CUT:
2455 /* only select this object */
2456 arranger_object_select (
2457 obj, F_SELECT, F_NO_APPEND,
2458 F_PUBLISH_EVENTS);
2459 break;
2460 default:
2461 break;
2462 }
2463
2464 /* set editor region and show editor if double
2465 * click */
2466 if (obj->type == ARRANGER_OBJECT_TYPE_REGION
2467 &&
2468 self->drag_start_btn == GDK_BUTTON_PRIMARY)
2469 {
2470 clip_editor_set_region (
2471 CLIP_EDITOR, (ZRegion *) obj, true);
2472
2473 /* if double click bring up piano roll */
2474 if (self->n_press == 2 &&
2475 !self->ctrl_held)
2476 {
2477 g_debug (
2478 "double clicked on region - "
2479 "showing piano roll");
2480 EVENTS_PUSH (ET_REGION_ACTIVATED, NULL);
2481 }
2482 }
2483 /* if open marker dialog if double click on
2484 * marker */
2485 else if (obj->type == ARRANGER_OBJECT_TYPE_MARKER)
2486 {
2487 if (self->n_press == 2 && !self->ctrl_held)
2488 {
2489 StringEntryDialogWidget * dialog =
2490 string_entry_dialog_widget_new (
2491 _("Marker name"), obj,
2492 (GenericStringGetter)
2493 arranger_object_get_name,
2494 (GenericStringSetter)
2495 arranger_object_set_name_with_action);
2496 gtk_widget_show_all (GTK_WIDGET (dialog));
2497 self->action = UI_OVERLAY_ACTION_NONE;
2498 return true;
2499 }
2500 }
2501 /* if double click on scale, open scale
2502 * selector */
2503 else if (obj->type ==
2504 ARRANGER_OBJECT_TYPE_SCALE_OBJECT)
2505 {
2506 if (self->n_press == 2 && !self->ctrl_held)
2507 {
2508 ScaleSelectorWindowWidget *
2509 scale_selector =
2510 scale_selector_window_widget_new (
2511 (ScaleObject *) obj);
2512 gtk_widget_show_all (
2513 GTK_WIDGET (scale_selector));
2514 self->action = UI_OVERLAY_ACTION_NONE;
2515 return true;
2516 }
2517 }
2518
2519 /* check if all selected objects are fadeable or
2520 * resizeable */
2521 if (is_resize_l || is_resize_r)
2522 {
2523 ArrangerSelections * sel =
2524 arranger_widget_get_selections (self);
2525
2526 bool have_unresizable =
2527 arranger_selections_contains_object_with_property (
2528 sel,
2529 ARRANGER_SELECTIONS_PROPERTY_HAS_LENGTH,
2530 false);
2531 if (have_unresizable)
2532 {
2533 ui_show_message_printf (
2534 MAIN_WINDOW, GTK_MESSAGE_WARNING,
2535 "%s",
2536 _("Cannot resize because the "
2537 "selection contains objects "
2538 "without length"));
2539 return false;
2540 }
2541
2542 bool have_looped =
2543 arranger_selections_contains_object_with_property (
2544 sel,
2545 ARRANGER_SELECTIONS_PROPERTY_HAS_LOOPED,
2546 true);
2547 if ((is_resize_l || is_resize_r)
2548 && !is_resize_loop && have_looped)
2549 {
2550 bool have_unloopable =
2551 arranger_selections_contains_object_with_property (
2552 sel,
2553 ARRANGER_SELECTIONS_PROPERTY_CAN_LOOP,
2554 false);
2555 if (have_unloopable)
2556 {
2557 /* cancel resize since we have
2558 * a looped object mixed with
2559 * unloopable objects in the
2560 * selection */
2561 ui_show_message_printf (
2562 MAIN_WINDOW, GTK_MESSAGE_WARNING,
2563 "%s",
2564 _("Cannot resize because the "
2565 "selection contains a mix of "
2566 "looped and unloopable objects"));
2567 return false;
2568 }
2569 else
2570 {
2571 /* loop-resize since we have a
2572 * loopable object in the selection
2573 * and all other objects are
2574 * loopable */
2575 g_debug (
2576 "convert resize to resize-loop - "
2577 "have looped object in the "
2578 "selection");
2579 is_resize_loop = true;
2580 }
2581 }
2582 }
2583 if (is_fade_in_point || is_fade_in_outer
2584 || is_fade_out_point || is_fade_out_outer)
2585 {
2586 ArrangerSelections * sel =
2587 arranger_widget_get_selections (self);
2588 bool have_unfadeable =
2589 arranger_selections_contains_object_with_property (
2590 sel,
2591 ARRANGER_SELECTIONS_PROPERTY_CAN_FADE,
2592 false);
2593 if (have_unfadeable)
2594 {
2595 /* don't fade */
2596 is_fade_in_point = false;
2597 is_fade_in_outer = false;
2598 is_fade_out_point = false;
2599 is_fade_out_outer = false;
2600 }
2601 }
2602
2603 #define SET_ACTION(x) \
2604 self->action = UI_OVERLAY_ACTION_##x
2605
2606 g_debug ("action before");
2607 arranger_widget_print_action (self);
2608
2609 bool drum_mode =
2610 arranger_widget_get_drum_mode_enabled (self);
2611
2612 /* update arranger action */
2613 switch (obj->type)
2614 {
2615 case ARRANGER_OBJECT_TYPE_REGION:
2616 switch (P_TOOL)
2617 {
2618 case TOOL_ERASER:
2619 SET_ACTION (STARTING_ERASING);
2620 break;
2621 case TOOL_AUDITION:
2622 SET_ACTION (AUDITIONING);
2623 break;
2624 case TOOL_SELECT_NORMAL:
2625 if (is_fade_in_point)
2626 SET_ACTION (RESIZING_L_FADE);
2627 else if (is_fade_out_point)
2628 SET_ACTION (RESIZING_R_FADE);
2629 else if (is_resize_l && is_resize_loop)
2630 SET_ACTION (RESIZING_L_LOOP);
2631 else if (is_resize_l)
2632 SET_ACTION (RESIZING_L);
2633 else if (is_resize_r && is_resize_loop)
2634 SET_ACTION (RESIZING_R_LOOP);
2635 else if (is_resize_r)
2636 SET_ACTION (RESIZING_R);
2637 else if (is_rename)
2638 SET_ACTION (RENAMING);
2639 else if (show_cut_lines)
2640 SET_ACTION (CUTTING);
2641 else if (is_fade_in_outer)
2642 SET_ACTION (RESIZING_UP_FADE_IN);
2643 else if (is_fade_out_outer)
2644 SET_ACTION (RESIZING_UP_FADE_OUT);
2645 else
2646 SET_ACTION (STARTING_MOVING);
2647 break;
2648 case TOOL_SELECT_STRETCH:
2649 if (is_resize_l)
2650 SET_ACTION (STRETCHING_L);
2651 else if (is_resize_r)
2652 SET_ACTION (STRETCHING_R);
2653 else
2654 SET_ACTION (STARTING_MOVING);
2655 break;
2656 case TOOL_EDIT:
2657 if (is_resize_l)
2658 SET_ACTION (RESIZING_L);
2659 else if (is_resize_r)
2660 SET_ACTION (RESIZING_R);
2661 else
2662 SET_ACTION (STARTING_MOVING);
2663 break;
2664 case TOOL_CUT:
2665 SET_ACTION (CUTTING);
2666 break;
2667 case TOOL_RAMP:
2668 /* TODO */
2669 break;
2670 }
2671 break;
2672 case ARRANGER_OBJECT_TYPE_MIDI_NOTE:
2673 switch (P_TOOL)
2674 {
2675 case TOOL_ERASER:
2676 SET_ACTION (STARTING_ERASING);
2677 break;
2678 case TOOL_AUDITION:
2679 SET_ACTION (AUDITIONING);
2680 break;
2681 case TOOL_SELECT_NORMAL:
2682 case TOOL_EDIT:
2683 case TOOL_SELECT_STRETCH:
2684 if ((is_resize_l) && !drum_mode)
2685 SET_ACTION (RESIZING_L);
2686 else if (is_resize_r && !drum_mode)
2687 SET_ACTION (RESIZING_R);
2688 else
2689 SET_ACTION (STARTING_MOVING);
2690 break;
2691 case TOOL_CUT:
2692 /* TODO */
2693 break;
2694 case TOOL_RAMP:
2695 break;
2696 }
2697 break;
2698 case ARRANGER_OBJECT_TYPE_AUTOMATION_POINT:
2699 switch (P_TOOL)
2700 {
2701 case TOOL_SELECT_NORMAL:
2702 case TOOL_EDIT:
2703 case TOOL_SELECT_STRETCH:
2704 if (is_resize_up)
2705 SET_ACTION (RESIZING_UP);
2706 else
2707 SET_ACTION (STARTING_MOVING);
2708 break;
2709 default:
2710 break;
2711 }
2712 break;
2713 case ARRANGER_OBJECT_TYPE_VELOCITY:
2714 switch (P_TOOL)
2715 {
2716 case TOOL_SELECT_NORMAL:
2717 case TOOL_EDIT:
2718 case TOOL_SELECT_STRETCH:
2719 case TOOL_RAMP:
2720 SET_ACTION (STARTING_MOVING);
2721 if (is_resize_up)
2722 SET_ACTION (RESIZING_UP);
2723 else
2724 SET_ACTION (NONE);
2725 break;
2726 default:
2727 break;
2728 }
2729 break;
2730 case ARRANGER_OBJECT_TYPE_CHORD_OBJECT:
2731 switch (P_TOOL)
2732 {
2733 case TOOL_SELECT_NORMAL:
2734 case TOOL_EDIT:
2735 case TOOL_SELECT_STRETCH:
2736 SET_ACTION (STARTING_MOVING);
2737 break;
2738 default:
2739 break;
2740 }
2741 break;
2742 case ARRANGER_OBJECT_TYPE_SCALE_OBJECT:
2743 switch (P_TOOL)
2744 {
2745 case TOOL_SELECT_NORMAL:
2746 case TOOL_EDIT:
2747 case TOOL_SELECT_STRETCH:
2748 SET_ACTION (STARTING_MOVING);
2749 break;
2750 default:
2751 break;
2752 }
2753 break;
2754 case ARRANGER_OBJECT_TYPE_MARKER:
2755 switch (P_TOOL)
2756 {
2757 case TOOL_SELECT_NORMAL:
2758 case TOOL_EDIT:
2759 case TOOL_SELECT_STRETCH:
2760 SET_ACTION (STARTING_MOVING);
2761 break;
2762 default:
2763 break;
2764 }
2765 break;
2766 default:
2767 g_return_val_if_reached (0);
2768 }
2769
2770 g_debug ("action after");
2771 arranger_widget_print_action (self);
2772
2773 #undef SET_ACTION
2774
2775 ArrangerSelections * orig_selections =
2776 arranger_widget_get_selections (self);
2777
2778 /* set index in prev lane for selected objects
2779 * if timeline */
2780 if (self->type == ARRANGER_WIDGET_TYPE_TIMELINE)
2781 {
2782 timeline_selections_set_index_in_prev_lane (
2783 TL_SELECTIONS);
2784 }
2785
2786 /* clone the arranger selections at this point */
2787 self->sel_at_start =
2788 arranger_selections_clone (orig_selections);
2789
2790 /* if the action is stretching, set the
2791 * "before_length" on each region */
2792 if (orig_selections->type ==
2793 ARRANGER_SELECTIONS_TYPE_TIMELINE &&
2794 ACTION_IS (STRETCHING_R))
2795 {
2796 TimelineSelections * sel =
2797 (TimelineSelections *) orig_selections;
2798 transport_prepare_audio_regions_for_stretch (
2799 TRANSPORT, sel);
2800 }
2801
2802 return true;
2803 }
2804
2805 static void
drag_begin(GtkGestureDrag * gesture,gdouble start_x,gdouble start_y,ArrangerWidget * self)2806 drag_begin (
2807 GtkGestureDrag * gesture,
2808 gdouble start_x,
2809 gdouble start_y,
2810 ArrangerWidget * self)
2811 {
2812 g_debug ("arranger drag begin starting...");
2813
2814 self->start_x = start_x;
2815 self->hover_x = start_x;
2816 arranger_widget_px_to_pos (
2817 self, start_x, &self->start_pos, F_PADDING);
2818 self->start_y = start_y;
2819 self->hover_y = start_y;;
2820 self->drag_update_started = false;
2821
2822 /* set last project selection type */
2823 if (self->type == ARRANGER_WIDGET_TYPE_TIMELINE)
2824 {
2825 PROJECT->last_selection =
2826 SELECTION_TYPE_TIMELINE;
2827 }
2828 else
2829 {
2830 PROJECT->last_selection =
2831 SELECTION_TYPE_EDITOR;
2832 }
2833
2834 GdkEventSequence *sequence =
2835 gtk_gesture_single_get_current_sequence (
2836 GTK_GESTURE_SINGLE (gesture));
2837 const GdkEvent * ev =
2838 gtk_gesture_get_last_event (
2839 GTK_GESTURE (gesture), sequence);
2840 g_warn_if_fail (
2841 gdk_event_get_button (
2842 ev, &self->drag_start_btn));
2843
2844 /* check if selections can create links */
2845 self->can_link =
2846 TYPE_IS (TIMELINE) &&
2847 TL_SELECTIONS->num_regions > 0 &&
2848 TL_SELECTIONS->num_scale_objects == 0 &&
2849 TL_SELECTIONS->num_markers == 0;
2850
2851 if (!gtk_widget_has_focus (GTK_WIDGET (self)))
2852 gtk_widget_grab_focus (GTK_WIDGET (self));
2853
2854 /* get current pos */
2855 arranger_widget_px_to_pos (
2856 self, self->start_x,
2857 &self->curr_pos, F_PADDING);
2858
2859 /* get difference with drag start pos */
2860 self->curr_ticks_diff_from_start =
2861 position_get_ticks_diff (
2862 &self->curr_pos, &self->start_pos, NULL);
2863
2864 /* handle hit object */
2865 int objects_hit =
2866 on_drag_begin_handle_hit_object (
2867 self, start_x, start_y);
2868 g_message ("objects hit %d", objects_hit);
2869 arranger_widget_print_action (self);
2870
2871 if (objects_hit)
2872 {
2873 ArrangerSelections * sel =
2874 arranger_widget_get_selections (self);
2875 self->sel_at_start =
2876 arranger_selections_clone (sel);
2877 }
2878 /* if nothing hit */
2879 else
2880 {
2881 self->sel_at_start = NULL;
2882
2883 /* single click */
2884 if (self->n_press == 1)
2885 {
2886 switch (P_TOOL)
2887 {
2888 case TOOL_SELECT_NORMAL:
2889 case TOOL_SELECT_STRETCH:
2890 /* selection */
2891 self->action =
2892 UI_OVERLAY_ACTION_STARTING_SELECTION;
2893
2894 if (!self->ctrl_held)
2895 {
2896 /* deselect all */
2897 arranger_widget_select_all (
2898 self, false, true);
2899 }
2900
2901 /* set whether selecting
2902 * objects or selecting range */
2903 set_select_type (self, start_y);
2904
2905 /* hide range selection */
2906 transport_set_has_range (
2907 TRANSPORT, false);
2908
2909 /* hide range selection if audio
2910 * arranger and set appropriate
2911 * action */
2912 if (self->type == TYPE (AUDIO))
2913 {
2914 AUDIO_SELECTIONS->
2915 has_selection = false;
2916 self->action =
2917 audio_arranger_widget_get_action_on_drag_begin (
2918 self);
2919 }
2920 break;
2921 case TOOL_EDIT:
2922 if (self->type == TYPE (TIMELINE) ||
2923 self->type == TYPE (MIDI) ||
2924 self->type == TYPE (CHORD))
2925 {
2926 if (self->ctrl_held)
2927 {
2928 /* autofill */
2929 autofill (
2930 self, start_x, start_y);
2931 }
2932 else
2933 {
2934 /* something is created */
2935 create_item (
2936 self, start_x, start_y,
2937 false);
2938 }
2939 }
2940 else if (self->type ==
2941 TYPE (MIDI_MODIFIER) ||
2942 self->type ==
2943 TYPE (AUTOMATION))
2944 {
2945 /* autofill (also works for
2946 * manual edit for velocity and
2947 * automation */
2948 autofill (self, start_x, start_y);
2949 }
2950 break;
2951 case TOOL_ERASER:
2952 /* delete selection */
2953 self->action =
2954 UI_OVERLAY_ACTION_STARTING_DELETE_SELECTION;
2955 break;
2956 case TOOL_RAMP:
2957 self->action =
2958 UI_OVERLAY_ACTION_STARTING_RAMP;
2959 break;
2960 default:
2961 break;
2962 }
2963 }
2964 /* double click */
2965 else if (self->n_press == 2)
2966 {
2967 switch (P_TOOL)
2968 {
2969 case TOOL_SELECT_NORMAL:
2970 case TOOL_SELECT_STRETCH:
2971 case TOOL_EDIT:
2972 create_item (
2973 self, start_x, start_y, false);
2974 break;
2975 case TOOL_ERASER:
2976 /* delete selection */
2977 self->action =
2978 UI_OVERLAY_ACTION_STARTING_DELETE_SELECTION;
2979 break;
2980 default:
2981 break;
2982 }
2983 }
2984 }
2985
2986 /* set the start pos of the earliest object and
2987 * the flag whether the earliest object exists */
2988 set_earliest_obj (self);
2989
2990 arranger_widget_refresh_cursor (self);
2991
2992 g_debug ("arranger drag begin done");
2993 }
2994
2995 /**
2996 * Selects objects for the given arranger in the
2997 * range from start_* to offset_*.
2998 *
2999 * @param ignore_frozen Ignore frozen objects.
3000 * @param[in] delete Whether this is a select-delete
3001 * operation.
3002 * @param[in] in_range Whether to select/delete
3003 * objects in the range or exactly at the current
3004 * point.
3005 */
3006 NONNULL
3007 static void
select_in_range(ArrangerWidget * self,double offset_x,double offset_y,bool in_range,bool ignore_frozen,bool delete)3008 select_in_range (
3009 ArrangerWidget * self,
3010 double offset_x,
3011 double offset_y,
3012 bool in_range,
3013 bool ignore_frozen,
3014 bool delete)
3015 {
3016 ArrangerSelections * arranger_sel =
3017 arranger_widget_get_selections (self);
3018 g_return_if_fail (arranger_sel);
3019 ArrangerSelections * prev_sel =
3020 arranger_selections_clone (arranger_sel);
3021
3022 if (delete && in_range)
3023 {
3024 int num_objs;
3025 ArrangerObject ** objs =
3026 arranger_selections_get_all_objects (
3027 self->sel_to_delete, &num_objs);
3028 if (ignore_frozen)
3029 {
3030 filter_out_frozen_objects (
3031 self, objs, &num_objs);
3032 }
3033 for (int i = 0; i < num_objs; i++)
3034 {
3035 objs[i]->deleted_temporarily = false;
3036 }
3037 arranger_selections_clear (
3038 self->sel_to_delete, F_NO_FREE,
3039 F_NO_PUBLISH_EVENTS);
3040 free (objs);
3041 }
3042 else if (!delete)
3043 {
3044 if (!self->ctrl_held)
3045 {
3046 /* deselect all */
3047 arranger_widget_select_all (
3048 self, false, false);
3049 }
3050 }
3051
3052 ArrangerObject * objs[800];
3053 int num_objs = 0;
3054 GdkRectangle rect;
3055 if (in_range)
3056 {
3057 GdkRectangle _rect = {
3058 .x = (int) offset_x >= 0 ?
3059 (int) self->start_x :
3060 (int) (self->start_x + offset_x),
3061 .y = (int) offset_y >= 0 ?
3062 (int) self->start_y :
3063 (int) (self->start_y + offset_y),
3064 .width = abs ((int) offset_x),
3065 .height = abs ((int) offset_y),
3066 };
3067 rect = _rect;
3068 }
3069 else
3070 {
3071 GdkRectangle _rect = {
3072 .x = (int) (self->start_x + offset_x),
3073 .y = (int) (self->start_y + offset_y),
3074 .width = 1, .height = 1,
3075 };
3076 rect = _rect;
3077 }
3078
3079 /* redraw union of last rectangle and new
3080 * rectangle */
3081 GdkRectangle union_rect;
3082 gdk_rectangle_union (
3083 &rect, &self->last_selection_rect,
3084 &union_rect);
3085 arranger_widget_redraw_rectangle (
3086 self, &union_rect);
3087 self->last_selection_rect = rect;
3088
3089 switch (self->type)
3090 {
3091 case TYPE (CHORD):
3092 arranger_widget_get_hit_objects_in_rect (
3093 self, ARRANGER_OBJECT_TYPE_CHORD_OBJECT,
3094 &rect, objs, &num_objs);
3095 if (ignore_frozen)
3096 {
3097 filter_out_frozen_objects (
3098 self, objs, &num_objs);
3099 }
3100 for (int i = 0; i < num_objs; i++)
3101 {
3102 ArrangerObject * obj = objs[i];
3103 if (delete)
3104 {
3105 arranger_selections_add_object (
3106 self->sel_to_delete, obj);
3107 obj->deleted_temporarily = true;
3108 }
3109 else
3110 {
3111 arranger_object_select (
3112 obj, F_SELECT, F_APPEND,
3113 F_NO_PUBLISH_EVENTS);
3114 }
3115 }
3116 break;
3117 case TYPE (AUTOMATION):
3118 arranger_widget_get_hit_objects_in_rect (
3119 self, ARRANGER_OBJECT_TYPE_AUTOMATION_POINT,
3120 &rect, objs, &num_objs);
3121 if (ignore_frozen)
3122 {
3123 filter_out_frozen_objects (
3124 self, objs, &num_objs);
3125 }
3126 for (int i = 0; i < num_objs; i++)
3127 {
3128 ArrangerObject * obj = objs[i];
3129 if (delete)
3130 {
3131 arranger_selections_add_object (
3132 self->sel_to_delete, obj);
3133 obj->deleted_temporarily = true;
3134 }
3135 else
3136 {
3137 arranger_object_select (
3138 obj, F_SELECT, F_APPEND,
3139 F_NO_PUBLISH_EVENTS);
3140 }
3141 }
3142 break;
3143 case TYPE (TIMELINE):
3144 arranger_widget_get_hit_objects_in_rect (
3145 self, ARRANGER_OBJECT_TYPE_REGION,
3146 &rect, objs, &num_objs);
3147 if (ignore_frozen)
3148 {
3149 filter_out_frozen_objects (
3150 self, objs, &num_objs);
3151 }
3152 for (int i = 0; i < num_objs; i++)
3153 {
3154 ArrangerObject * obj = objs[i];
3155 if (delete)
3156 {
3157 arranger_selections_add_object (
3158 self->sel_to_delete, obj);
3159 obj->deleted_temporarily = true;
3160 }
3161 else
3162 {
3163 /* select the enclosed region */
3164 arranger_object_select (
3165 obj, F_SELECT, F_APPEND,
3166 F_NO_PUBLISH_EVENTS);
3167 }
3168 }
3169 arranger_widget_get_hit_objects_in_rect (
3170 self, ARRANGER_OBJECT_TYPE_SCALE_OBJECT,
3171 &rect, objs, &num_objs);
3172 if (ignore_frozen)
3173 {
3174 filter_out_frozen_objects (
3175 self, objs, &num_objs);
3176 }
3177 for (int i = 0; i < num_objs; i++)
3178 {
3179 ArrangerObject * obj = objs[i];
3180 if (delete)
3181 {
3182 arranger_selections_add_object (
3183 self->sel_to_delete, obj);
3184 obj->deleted_temporarily = true;
3185 }
3186 else
3187 {
3188 arranger_object_select (
3189 obj, F_SELECT, F_APPEND,
3190 F_NO_PUBLISH_EVENTS);
3191 }
3192 }
3193 arranger_widget_get_hit_objects_in_rect (
3194 self, ARRANGER_OBJECT_TYPE_MARKER,
3195 &rect, objs, &num_objs);
3196 if (ignore_frozen)
3197 {
3198 filter_out_frozen_objects (
3199 self, objs, &num_objs);
3200 }
3201 for (int i = 0; i < num_objs; i++)
3202 {
3203 ArrangerObject * obj = objs[i];
3204 if (delete)
3205 {
3206 if (arranger_object_is_deletable (
3207 obj))
3208 {
3209 arranger_selections_add_object (
3210 self->sel_to_delete, obj);
3211 obj->deleted_temporarily = true;
3212 }
3213 }
3214 else
3215 {
3216 arranger_object_select (
3217 obj, F_SELECT, F_APPEND,
3218 F_NO_PUBLISH_EVENTS);
3219 }
3220 }
3221 break;
3222 case TYPE (MIDI):
3223 arranger_widget_get_hit_objects_in_rect (
3224 self, ARRANGER_OBJECT_TYPE_MIDI_NOTE,
3225 &rect, objs, &num_objs);
3226 if (ignore_frozen)
3227 {
3228 filter_out_frozen_objects (
3229 self, objs, &num_objs);
3230 }
3231 for (int i = 0; i < num_objs; i++)
3232 {
3233 ArrangerObject * obj = objs[i];
3234 if (delete)
3235 {
3236 arranger_selections_add_object (
3237 self->sel_to_delete, obj);
3238 obj->deleted_temporarily = true;
3239 }
3240 else
3241 {
3242 arranger_object_select (
3243 obj, F_SELECT, F_APPEND,
3244 F_NO_PUBLISH_EVENTS);
3245 }
3246 }
3247 midi_arranger_selections_unlisten_note_diff (
3248 (MidiArrangerSelections *) prev_sel,
3249 (MidiArrangerSelections *)
3250 arranger_widget_get_selections (self));
3251 break;
3252 case TYPE (MIDI_MODIFIER):
3253 arranger_widget_get_hit_objects_in_rect (
3254 self, ARRANGER_OBJECT_TYPE_VELOCITY,
3255 &rect, objs, &num_objs);
3256 if (ignore_frozen)
3257 {
3258 filter_out_frozen_objects (
3259 self, objs, &num_objs);
3260 }
3261 for (int i = 0; i < num_objs; i++)
3262 {
3263 ArrangerObject * obj = objs[i];
3264 Velocity * vel =
3265 (Velocity *) obj;
3266 MidiNote * mn =
3267 velocity_get_midi_note (vel);
3268 ArrangerObject * mn_obj =
3269 (ArrangerObject *) mn;
3270
3271 if (delete)
3272 {
3273 arranger_selections_add_object (
3274 self->sel_to_delete, mn_obj);
3275 obj->deleted_temporarily = true;
3276 }
3277 else
3278 {
3279 arranger_object_select (
3280 mn_obj, F_SELECT, F_APPEND,
3281 F_NO_PUBLISH_EVENTS);
3282 }
3283 }
3284 break;
3285 default:
3286 break;
3287 }
3288
3289 if (prev_sel)
3290 {
3291 /* redraw newly unselected objects */
3292 int num_prev_objs;
3293 ArrangerObject ** prev_objs =
3294 arranger_selections_get_all_objects (
3295 prev_sel, &num_prev_objs);
3296 for (int i = 0; i < num_prev_objs; i++)
3297 {
3298 ArrangerObject * obj =
3299 arranger_object_find (prev_objs[i]);
3300 g_return_if_fail (obj);
3301 if (!arranger_object_is_selected (obj))
3302 arranger_object_queue_redraw (obj);
3303 }
3304 free (prev_objs);
3305
3306 arranger_selections_free (prev_sel);
3307 }
3308 }
3309
3310 NONNULL
3311 static void
drag_update(GtkGestureDrag * gesture,gdouble offset_x,gdouble offset_y,ArrangerWidget * self)3312 drag_update (
3313 GtkGestureDrag * gesture,
3314 gdouble offset_x,
3315 gdouble offset_y,
3316 ArrangerWidget * self)
3317 {
3318 if (!self->drag_update_started &&
3319 !gtk_drag_check_threshold (
3320 GTK_WIDGET (self),
3321 (int) self->start_x, (int) self->start_y,
3322 (int) (self->start_x + offset_x),
3323 (int) (self->start_y + offset_y)))
3324 {
3325 return;
3326 }
3327
3328 self->drag_update_started = true;
3329
3330 ArrangerSelections * sel =
3331 arranger_widget_get_selections (self);
3332
3333 /* state mask needs to be updated */
3334 GdkModifierType state_mask =
3335 ui_get_state_mask (
3336 GTK_GESTURE (gesture));
3337 if (state_mask & GDK_SHIFT_MASK)
3338 self->shift_held = 1;
3339 else
3340 self->shift_held = 0;
3341 if (state_mask & GDK_CONTROL_MASK)
3342 self->ctrl_held = 1;
3343 else
3344 self->ctrl_held = 0;
3345
3346 arranger_widget_print_action (self);
3347
3348 /* get current pos */
3349 arranger_widget_px_to_pos (
3350 self, self->start_x + offset_x,
3351 &self->curr_pos, F_PADDING);
3352
3353 /* get difference with drag start pos */
3354 self->curr_ticks_diff_from_start =
3355 position_get_ticks_diff (
3356 &self->curr_pos, &self->start_pos, NULL);
3357
3358 if (self->earliest_obj_exists)
3359 {
3360 /* add diff to the earliest object's start pos
3361 * and snap it, then get the diff ticks */
3362 Position earliest_obj_new_pos;
3363 position_set_to_pos (
3364 &earliest_obj_new_pos,
3365 &self->earliest_obj_start_pos);
3366 position_add_ticks (
3367 &earliest_obj_new_pos,
3368 self->curr_ticks_diff_from_start);
3369
3370 if (position_is_before_or_equal (
3371 &earliest_obj_new_pos, &POSITION_START))
3372 {
3373 /* stop at 0.0.0.0 */
3374 position_set_to_pos (
3375 &earliest_obj_new_pos, &POSITION_START);
3376 }
3377 else if (!self->shift_held &&
3378 SNAP_GRID_ANY_SNAP (self->snap_grid))
3379 {
3380 Track * track_for_snap = NULL;
3381 if (self->type == TYPE (TIMELINE))
3382 {
3383 track_for_snap =
3384 timeline_arranger_widget_get_track_at_y (
3385 self, self->start_y + offset_y);
3386 }
3387 position_snap (
3388 &self->earliest_obj_start_pos,
3389 &earliest_obj_new_pos, track_for_snap,
3390 NULL, self->snap_grid);
3391 }
3392 self->adj_ticks_diff =
3393 position_get_ticks_diff (
3394 &earliest_obj_new_pos,
3395 &self->earliest_obj_start_pos,
3396 NULL);
3397 }
3398
3399 /* if right clicking, start erasing action */
3400 if (self->drag_start_btn ==
3401 GDK_BUTTON_SECONDARY &&
3402 P_TOOL == TOOL_SELECT_NORMAL &&
3403 self->action !=
3404 UI_OVERLAY_ACTION_STARTING_ERASING &&
3405 self->action != UI_OVERLAY_ACTION_ERASING)
3406 {
3407 self->action =
3408 UI_OVERLAY_ACTION_STARTING_ERASING;
3409 }
3410
3411 /* set action to selecting if starting
3412 * selection. this
3413 * is because drag_update never gets called if
3414 * it's just
3415 * a click, so we can check at drag_end and see if
3416 * anything was selected */
3417 switch (self->action)
3418 {
3419 case UI_OVERLAY_ACTION_STARTING_SELECTION:
3420 self->action =
3421 UI_OVERLAY_ACTION_SELECTING;
3422 break;
3423 case UI_OVERLAY_ACTION_STARTING_DELETE_SELECTION:
3424 self->action =
3425 UI_OVERLAY_ACTION_DELETE_SELECTING;
3426 {
3427 arranger_selections_clear (
3428 sel, F_NO_FREE, F_NO_PUBLISH_EVENTS);
3429 self->sel_to_delete =
3430 arranger_selections_clone (
3431 arranger_widget_get_selections (self));
3432 }
3433 break;
3434 case UI_OVERLAY_ACTION_STARTING_ERASING:
3435 self->action = UI_OVERLAY_ACTION_ERASING;
3436 {
3437 arranger_selections_clear (
3438 sel, F_NO_FREE, F_NO_PUBLISH_EVENTS);
3439 self->sel_to_delete =
3440 arranger_selections_clone (
3441 arranger_widget_get_selections (self));
3442 }
3443 break;
3444 case UI_OVERLAY_ACTION_STARTING_MOVING:
3445 if (self->alt_held &&
3446 self->can_link)
3447 self->action =
3448 UI_OVERLAY_ACTION_MOVING_LINK;
3449 else if (self->ctrl_held)
3450 self->action =
3451 UI_OVERLAY_ACTION_MOVING_COPY;
3452 else
3453 self->action =
3454 UI_OVERLAY_ACTION_MOVING;
3455 break;
3456 case UI_OVERLAY_ACTION_MOVING:
3457 if (self->alt_held &&
3458 self->can_link)
3459 self->action =
3460 UI_OVERLAY_ACTION_MOVING_LINK;
3461 else if (self->ctrl_held)
3462 self->action =
3463 UI_OVERLAY_ACTION_MOVING_COPY;
3464 break;
3465 case UI_OVERLAY_ACTION_MOVING_LINK:
3466 if (!self->alt_held)
3467 self->action =
3468 self->ctrl_held ?
3469 UI_OVERLAY_ACTION_MOVING_COPY :
3470 UI_OVERLAY_ACTION_MOVING;
3471 break;
3472 case UI_OVERLAY_ACTION_MOVING_COPY:
3473 if (!self->ctrl_held)
3474 self->action =
3475 self->alt_held && self->can_link ?
3476 UI_OVERLAY_ACTION_MOVING_LINK :
3477 UI_OVERLAY_ACTION_MOVING;
3478 break;
3479 case UI_OVERLAY_ACTION_STARTING_RAMP:
3480 self->action =
3481 UI_OVERLAY_ACTION_RAMPING;
3482 if (self->type == TYPE (MIDI_MODIFIER))
3483 {
3484 midi_modifier_arranger_widget_set_start_vel (
3485 self);
3486 }
3487 break;
3488 case UI_OVERLAY_ACTION_CUTTING:
3489 /* alt + move changes the action from
3490 * cutting to moving-link */
3491 if (self->alt_held && self->can_link)
3492 self->action =
3493 UI_OVERLAY_ACTION_MOVING_LINK;
3494 break;
3495 default:
3496 break;
3497 }
3498
3499 /* update visibility */
3500 /*arranger_widget_update_visibility (self);*/
3501
3502 switch (self->action)
3503 {
3504 /* if drawing a selection */
3505 case UI_OVERLAY_ACTION_SELECTING:
3506 {
3507 /* redraw previous selections */
3508 arranger_selections_redraw (sel);
3509
3510 /* find and select objects inside selection */
3511 select_in_range (
3512 self, offset_x, offset_y, F_IN_RANGE,
3513 F_IGNORE_FROZEN, F_NO_DELETE);
3514
3515 /* redraw new selections and other needed
3516 * things */
3517 EVENTS_PUSH (
3518 ET_SELECTING_IN_ARRANGER, self);
3519 }
3520 break;
3521 case UI_OVERLAY_ACTION_DELETE_SELECTING:
3522 /* find and delete objects inside
3523 * selection */
3524 select_in_range (
3525 self, offset_x, offset_y, F_IN_RANGE,
3526 F_IGNORE_FROZEN, F_DELETE);
3527 EVENTS_PUSH (
3528 ET_SELECTING_IN_ARRANGER, self);
3529 break;
3530 case UI_OVERLAY_ACTION_ERASING:
3531 /* delete anything touched */
3532 select_in_range (
3533 self, offset_x, offset_y, F_NOT_IN_RANGE,
3534 F_IGNORE_FROZEN, F_DELETE);
3535 break;
3536 case UI_OVERLAY_ACTION_RESIZING_L_FADE:
3537 case UI_OVERLAY_ACTION_RESIZING_L_LOOP:
3538 /* snap selections based on new pos */
3539 if (self->type == TYPE (TIMELINE))
3540 {
3541 int ret =
3542 timeline_arranger_widget_snap_regions_l (
3543 self,
3544 &self->curr_pos, F_DRY_RUN);
3545 if (!ret)
3546 timeline_arranger_widget_snap_regions_l (
3547 self,
3548 &self->curr_pos, F_NOT_DRY_RUN);
3549 }
3550 else if (self->type == TYPE (AUDIO))
3551 {
3552 int ret =
3553 audio_arranger_widget_snap_fade (
3554 self, &self->curr_pos, true,
3555 F_DRY_RUN);
3556 if (!ret)
3557 audio_arranger_widget_snap_fade (
3558 self, &self->curr_pos, true,
3559 F_NOT_DRY_RUN);
3560 }
3561 break;
3562 case UI_OVERLAY_ACTION_RESIZING_L:
3563 case UI_OVERLAY_ACTION_STRETCHING_L:
3564 {
3565 /* queue a redraw for the selections at
3566 * their current position before the
3567 * resize */
3568 arranger_selections_redraw (sel);
3569
3570 /* snap selections based on new pos */
3571 if (self->type == TYPE (TIMELINE))
3572 {
3573 int ret =
3574 timeline_arranger_widget_snap_regions_l (
3575 self, &self->curr_pos, 1);
3576 if (!ret)
3577 timeline_arranger_widget_snap_regions_l (
3578 self, &self->curr_pos, 0);
3579 }
3580 else if (self->type == TYPE (MIDI))
3581 {
3582 int ret =
3583 midi_arranger_widget_snap_midi_notes_l (
3584 self, &self->curr_pos, 1);
3585 if (!ret)
3586 midi_arranger_widget_snap_midi_notes_l (
3587 self, &self->curr_pos, 0);
3588 }
3589 }
3590 break;
3591 case UI_OVERLAY_ACTION_RESIZING_R_FADE:
3592 case UI_OVERLAY_ACTION_RESIZING_R_LOOP:
3593 if (self->type == TYPE (TIMELINE))
3594 {
3595 if (self->resizing_range)
3596 timeline_arranger_widget_snap_range_r (
3597 self, &self->curr_pos);
3598 else
3599 {
3600 int ret =
3601 timeline_arranger_widget_snap_regions_r (
3602 self, &self->curr_pos, F_DRY_RUN);
3603 if (!ret)
3604 timeline_arranger_widget_snap_regions_r (
3605 self, &self->curr_pos, F_NOT_DRY_RUN);
3606 }
3607 }
3608 else if (self->type == TYPE (AUDIO))
3609 {
3610 int ret =
3611 audio_arranger_widget_snap_fade (
3612 self, &self->curr_pos, false,
3613 F_DRY_RUN);
3614 if (!ret)
3615 audio_arranger_widget_snap_fade (
3616 self, &self->curr_pos, false,
3617 F_NOT_DRY_RUN);
3618 }
3619 break;
3620 case UI_OVERLAY_ACTION_RESIZING_R:
3621 case UI_OVERLAY_ACTION_STRETCHING_R:
3622 case UI_OVERLAY_ACTION_CREATING_RESIZING_R:
3623 {
3624 /* queue a redraw for the selections at
3625 * their current position before the
3626 * resize */
3627 arranger_selections_redraw (sel);
3628
3629 if (self->type == TYPE (TIMELINE))
3630 {
3631 if (self->resizing_range)
3632 {
3633 timeline_arranger_widget_snap_range_r (
3634 self, &self->curr_pos);
3635 }
3636 else
3637 {
3638 int ret =
3639 timeline_arranger_widget_snap_regions_r (
3640 self, &self->curr_pos, F_DRY_RUN);
3641 if (!ret)
3642 {
3643 timeline_arranger_widget_snap_regions_r (
3644 self, &self->curr_pos,
3645 F_NOT_DRY_RUN);
3646 }
3647 }
3648 }
3649 else if (self->type == TYPE (MIDI))
3650 {
3651 int ret =
3652 midi_arranger_widget_snap_midi_notes_r (
3653 self, &self->curr_pos, F_DRY_RUN);
3654 if (!ret)
3655 {
3656 midi_arranger_widget_snap_midi_notes_r (
3657 self, &self->curr_pos,
3658 F_NOT_DRY_RUN);
3659 }
3660 move_items_y (self, offset_y);
3661 }
3662 else if (self->type == TYPE (AUDIO))
3663 {
3664 if (self->resizing_range)
3665 {
3666 audio_arranger_widget_snap_range_r (
3667 self, &self->curr_pos);
3668 }
3669 }
3670
3671 transport_recalculate_total_bars (
3672 TRANSPORT, sel);
3673 }
3674 break;
3675 case UI_OVERLAY_ACTION_RESIZING_UP:
3676 if (self->type == TYPE (MIDI_MODIFIER))
3677 {
3678 midi_modifier_arranger_widget_resize_velocities (
3679 self, offset_y);
3680 }
3681 else if (self->type == TYPE (AUTOMATION))
3682 {
3683 automation_arranger_widget_resize_curves (
3684 self, offset_y);
3685 }
3686 else if (self->type == TYPE (AUDIO))
3687 {
3688 audio_arranger_widget_update_gain (
3689 self, offset_y);
3690 }
3691 break;
3692 case UI_OVERLAY_ACTION_RESIZING_UP_FADE_IN:
3693 if (self->type == TYPE (TIMELINE))
3694 {
3695 timeline_arranger_widget_fade_up (
3696 self, offset_y, true);
3697 }
3698 else if (self->type == TYPE (AUDIO))
3699 {
3700 audio_arranger_widget_fade_up (
3701 self, offset_y, true);
3702 }
3703 break;
3704 case UI_OVERLAY_ACTION_RESIZING_UP_FADE_OUT:
3705 if (self->type == TYPE (TIMELINE))
3706 {
3707 timeline_arranger_widget_fade_up (
3708 self, offset_y, false);
3709 }
3710 else if (self->type == TYPE (AUDIO))
3711 {
3712 audio_arranger_widget_fade_up (
3713 self, offset_y, false);
3714 }
3715 break;
3716 case UI_OVERLAY_ACTION_MOVING:
3717 case UI_OVERLAY_ACTION_CREATING_MOVING:
3718 case UI_OVERLAY_ACTION_MOVING_COPY:
3719 case UI_OVERLAY_ACTION_MOVING_LINK:
3720 move_items_x (
3721 self,
3722 self->adj_ticks_diff -
3723 self->last_adj_ticks_diff);
3724 move_items_y (self, offset_y);
3725 break;
3726 case UI_OVERLAY_ACTION_AUTOFILLING:
3727 g_message ("autofilling");
3728 autofill (
3729 self,
3730 self->start_x + offset_x,
3731 self->start_y + offset_y);
3732 break;
3733 case UI_OVERLAY_ACTION_AUDITIONING:
3734 /* TODO */
3735 g_message ("auditioning");
3736 break;
3737 case UI_OVERLAY_ACTION_RAMPING:
3738 /* find and select objects inside selection */
3739 if (self->type == TYPE (MIDI_MODIFIER))
3740 {
3741 midi_modifier_arranger_widget_ramp (
3742 self, offset_x, offset_y);
3743 }
3744 break;
3745 case UI_OVERLAY_ACTION_CUTTING:
3746 /* nothing to do, wait for drag end */
3747 break;
3748 default:
3749 /* TODO */
3750 break;
3751 }
3752
3753 if (self->action != UI_OVERLAY_ACTION_NONE)
3754 auto_scroll (
3755 self, (int) (self->start_x + offset_x),
3756 (int) (self->start_y + offset_y));
3757
3758 if (self->type == TYPE (MIDI))
3759 {
3760 midi_arranger_listen_notes (
3761 self, 1);
3762 }
3763
3764 /* update last offsets */
3765 self->last_offset_x = offset_x;
3766 self->last_offset_y = offset_y;
3767 self->last_adj_ticks_diff = self->adj_ticks_diff;
3768
3769 arranger_widget_refresh_cursor (self);
3770 }
3771
3772 /**
3773 * To be called on drag_end() to handle erase
3774 * actions.
3775 */
3776 static void
handle_erase_action(ArrangerWidget * self)3777 handle_erase_action (
3778 ArrangerWidget * self)
3779 {
3780 if (self->sel_to_delete)
3781 {
3782 if (arranger_selections_has_any (
3783 self->sel_to_delete) &&
3784 !arranger_selections_contains_undeletable_object (
3785 self->sel_to_delete))
3786 {
3787 GError * err = NULL;
3788 bool ret =
3789 arranger_selections_action_perform_delete (
3790 self->sel_to_delete, &err);
3791 if (!ret)
3792 {
3793 HANDLE_ERROR (
3794 err, "%s",
3795 _("Failed to delete selection"));
3796 }
3797 }
3798 object_free_w_func_and_null (
3799 arranger_selections_free,
3800 self->sel_to_delete);
3801 }
3802 }
3803
3804 static void
on_drag_end_automation(ArrangerWidget * self)3805 on_drag_end_automation (
3806 ArrangerWidget * self)
3807 {
3808 switch (self->action)
3809 {
3810 case UI_OVERLAY_ACTION_RESIZING_UP:
3811 {
3812 GError * err = NULL;
3813 bool ret =
3814 arranger_selections_action_perform_edit (
3815 self->sel_at_start,
3816 (ArrangerSelections *)
3817 AUTOMATION_SELECTIONS,
3818 ARRANGER_SELECTIONS_ACTION_EDIT_PRIMITIVE,
3819 F_ALREADY_EDITED, &err);
3820 if (!ret)
3821 {
3822 HANDLE_ERROR (
3823 err, "%s",
3824 _("Failed to edit selection"));
3825 }
3826 }
3827 break;
3828 case UI_OVERLAY_ACTION_STARTING_MOVING:
3829 {
3830 /* if something was clicked with ctrl without
3831 * moving*/
3832 if (self->ctrl_held)
3833 {
3834 /*if (self->start_region &&*/
3835 /*self->start_region_was_selected)*/
3836 /*{*/
3837 /*[> deselect it <]*/
3838 /*[>ARRANGER_WIDGET_SELECT_REGION (<]*/
3839 /*[>self, self->start_region,<]*/
3840 /*[>F_NO_SELECT, F_APPEND);<]*/
3841 /*}*/
3842 }
3843 else if (self->n_press == 2)
3844 {
3845 /* double click on object */
3846 /*g_message ("DOUBLE CLICK");*/
3847 }
3848 }
3849 break;
3850 case UI_OVERLAY_ACTION_MOVING:
3851 {
3852 AutomationSelections * sel_at_start =
3853 (AutomationSelections *) self->sel_at_start;
3854 AutomationPoint * start_ap =
3855 sel_at_start->automation_points[0];
3856 ArrangerObject * start_obj =
3857 (ArrangerObject *) start_ap;
3858 AutomationPoint * ap =
3859 AUTOMATION_SELECTIONS->automation_points[0];
3860 ArrangerObject * obj =
3861 (ArrangerObject *) ap;
3862 double ticks_diff =
3863 obj->pos.ticks -
3864 start_obj->pos.ticks;
3865 double norm_value_diff =
3866 (double)
3867 (ap->normalized_val -
3868 start_ap->normalized_val);
3869 GError * err = NULL;
3870 bool ret =
3871 arranger_selections_action_perform_move_automation (
3872 AUTOMATION_SELECTIONS,
3873 ticks_diff, norm_value_diff,
3874 F_ALREADY_MOVED, &err);
3875 if (!ret)
3876 {
3877 HANDLE_ERROR (
3878 err, "%s",
3879 _("Failed to move automation"));
3880 }
3881 }
3882 break;
3883 /* if copy-moved */
3884 case UI_OVERLAY_ACTION_MOVING_COPY:
3885 {
3886 ArrangerObject * obj =
3887 (ArrangerObject *) self->start_object;
3888 double ticks_diff =
3889 obj->pos.ticks -
3890 obj->transient->pos.ticks;
3891 float value_diff =
3892 ((AutomationPoint *) obj)->normalized_val -
3893 ((AutomationPoint *) obj->transient)->
3894 normalized_val;
3895
3896 GError * err = NULL;
3897 bool ret =
3898 (UndoableAction *)
3899 arranger_selections_action_perform_duplicate_automation (
3900 (ArrangerSelections *)
3901 AUTOMATION_SELECTIONS,
3902 ticks_diff, value_diff,
3903 F_ALREADY_MOVED, &err);
3904 if (!ret)
3905 {
3906 HANDLE_ERROR (
3907 err, "%s",
3908 _("Failed to duplicate automation"));
3909 }
3910 }
3911 break;
3912 case UI_OVERLAY_ACTION_NONE:
3913 case UI_OVERLAY_ACTION_STARTING_SELECTION:
3914 {
3915 arranger_selections_clear (
3916 (ArrangerSelections *)
3917 AUTOMATION_SELECTIONS, F_NO_FREE,
3918 F_NO_PUBLISH_EVENTS);
3919 }
3920 break;
3921 /* if something was created */
3922 case UI_OVERLAY_ACTION_CREATING_MOVING:
3923 {
3924 GError * err = NULL;
3925 bool ret =
3926 arranger_selections_action_perform_create (
3927 AUTOMATION_SELECTIONS, &err);
3928 if (!ret)
3929 {
3930 HANDLE_ERROR (
3931 err, "%s",
3932 _("Failed to create objects"));
3933 }
3934 }
3935 break;
3936 case UI_OVERLAY_ACTION_DELETE_SELECTING:
3937 case UI_OVERLAY_ACTION_ERASING:
3938 handle_erase_action (self);
3939 break;
3940 case UI_OVERLAY_ACTION_AUTOFILLING:
3941 {
3942 ZRegion * region =
3943 clip_editor_get_region (CLIP_EDITOR);
3944
3945 GError * err = NULL;
3946 bool ret =
3947 arranger_selections_action_perform_automation_fill (
3948 self->region_at_start, region, true,
3949 &err);
3950 if (!ret)
3951 {
3952 HANDLE_ERROR (
3953 err, "%s",
3954 _("Failed to fill automation"));
3955 }
3956 }
3957 break;
3958 /* if didn't click on something */
3959 default:
3960 break;
3961 }
3962
3963 self->start_object = NULL;
3964 }
3965
3966 static void
on_drag_end_midi_modifier(ArrangerWidget * self)3967 on_drag_end_midi_modifier (
3968 ArrangerWidget * self)
3969 {
3970 switch (self->action)
3971 {
3972 case UI_OVERLAY_ACTION_RESIZING_UP:
3973 {
3974 g_return_if_fail (self->sel_at_start);
3975
3976 GError * err = NULL;
3977 bool ret =
3978 arranger_selections_action_perform_edit (
3979 self->sel_at_start,
3980 (ArrangerSelections *) MA_SELECTIONS,
3981 ARRANGER_SELECTIONS_ACTION_EDIT_PRIMITIVE,
3982 true, &err);
3983 if (!ret)
3984 {
3985 HANDLE_ERROR (
3986 err, "%s",
3987 _("Failed to edit selections"));
3988 }
3989 }
3990 break;
3991 case UI_OVERLAY_ACTION_RAMPING:
3992 {
3993 Position selection_start_pos,
3994 selection_end_pos;
3995 ui_px_to_pos_editor (
3996 self->start_x,
3997 self->last_offset_x >= 0 ?
3998 &selection_start_pos :
3999 &selection_end_pos,
4000 F_PADDING);
4001 ui_px_to_pos_editor (
4002 self->start_x + self->last_offset_x,
4003 self->last_offset_x >= 0 ?
4004 &selection_end_pos :
4005 &selection_start_pos,
4006 F_PADDING);
4007
4008 /* prepare the velocities in cloned
4009 * arranger selections from the
4010 * vels at start */
4011 midi_modifier_arranger_widget_select_vels_in_range (
4012 self, self->last_offset_x);
4013 self->sel_at_start =
4014 arranger_selections_clone (
4015 (ArrangerSelections *)
4016 MA_SELECTIONS);
4017 MidiArrangerSelections * sel_at_start =
4018 (MidiArrangerSelections *)
4019 self->sel_at_start;
4020 for (int i = 0;
4021 i < sel_at_start->num_midi_notes; i++)
4022 {
4023 MidiNote * mn =
4024 sel_at_start->midi_notes[i];
4025 Velocity * vel = mn->vel;
4026 vel->vel = vel->vel_at_start;
4027 }
4028
4029 GError * err = NULL;
4030 bool ret =
4031 arranger_selections_action_perform_edit (
4032 self->sel_at_start,
4033 (ArrangerSelections *) MA_SELECTIONS,
4034 ARRANGER_SELECTIONS_ACTION_EDIT_PRIMITIVE,
4035 true, &err);
4036 if (!ret)
4037 {
4038 HANDLE_ERROR (
4039 err, "%s",
4040 _("Failed to edit selections"));
4041 }
4042 }
4043 break;
4044 case UI_OVERLAY_ACTION_DELETE_SELECTING:
4045 case UI_OVERLAY_ACTION_ERASING:
4046 handle_erase_action (self);
4047 break;
4048 case UI_OVERLAY_ACTION_AUTOFILLING:
4049 if (arranger_selections_has_any (
4050 (ArrangerSelections *) MA_SELECTIONS))
4051 {
4052 GError * err = NULL;
4053 bool ret =
4054 arranger_selections_action_perform_edit (
4055 self->sel_at_start,
4056 (ArrangerSelections *) MA_SELECTIONS,
4057 ARRANGER_SELECTIONS_ACTION_EDIT_PRIMITIVE,
4058 true, &err);
4059 if (!ret)
4060 {
4061 HANDLE_ERROR (
4062 err, "%s",
4063 _("Failed to edit selections"));
4064 }
4065 }
4066 break;
4067 default:
4068 break;
4069 }
4070 }
4071
4072 static void
on_drag_end_midi(ArrangerWidget * self)4073 on_drag_end_midi (
4074 ArrangerWidget * self)
4075 {
4076 midi_arranger_listen_notes (self, 0);
4077
4078 switch (self->action)
4079 {
4080 case UI_OVERLAY_ACTION_RESIZING_L:
4081 {
4082 ArrangerObject * obj =
4083 (ArrangerObject *) self->start_object;
4084 double ticks_diff =
4085 obj->pos.ticks -
4086 obj->transient->pos.ticks;
4087
4088 GError * err = NULL;
4089 bool ret =
4090 arranger_selections_action_perform_resize (
4091 (ArrangerSelections *) MA_SELECTIONS,
4092 ARRANGER_SELECTIONS_ACTION_RESIZE_L,
4093 ticks_diff, F_ALREADY_EDITED, &err);
4094 if (!ret)
4095 {
4096 HANDLE_ERROR (
4097 err, "%s",
4098 _("Failed to resize objects"));
4099 }
4100 }
4101 break;
4102 case UI_OVERLAY_ACTION_RESIZING_R:
4103 {
4104 ArrangerObject * obj =
4105 (ArrangerObject *) self->start_object;
4106 MidiNote * mn = (MidiNote *) obj;
4107 MidiNote * mn_trans =
4108 (MidiNote *) obj->transient;
4109 int pitch_diff = mn->val - mn_trans->val;
4110 double ticks_diff =
4111 obj->end_pos.ticks -
4112 obj->transient->end_pos.ticks;
4113
4114 GError * err = NULL;
4115 bool ret =
4116 arranger_selections_action_perform_resize (
4117 (ArrangerSelections *) MA_SELECTIONS,
4118 ARRANGER_SELECTIONS_ACTION_RESIZE_R,
4119 ticks_diff, F_ALREADY_EDITED, &err);
4120 if (!ret)
4121 {
4122 HANDLE_ERROR (
4123 err, "%s",
4124 _("Failed to resize objects"));
4125 }
4126 else if (pitch_diff)
4127 {
4128 ret =
4129 arranger_selections_action_perform_move_midi (
4130 MA_SELECTIONS, 0, pitch_diff,
4131 F_ALREADY_MOVED, &err);
4132 if (!ret)
4133 {
4134 HANDLE_ERROR (
4135 err, "%s",
4136 _("Failed to move MIDI notes"));
4137 }
4138 else
4139 {
4140 UndoableAction * ua =
4141 undo_manager_get_last_action (
4142 UNDO_MANAGER);
4143 ua->num_actions = 2;
4144 }
4145 }
4146 }
4147 break;
4148 case UI_OVERLAY_ACTION_STARTING_MOVING:
4149 {
4150 /* if something was clicked with ctrl without
4151 * moving*/
4152 if (self->ctrl_held)
4153 {
4154 if (self->start_object &&
4155 self->start_object_was_selected)
4156 {
4157 /* deselect it */
4158 arranger_object_select (
4159 self->start_object,
4160 F_NO_SELECT, F_APPEND,
4161 F_NO_PUBLISH_EVENTS);
4162 }
4163 }
4164 }
4165 break;
4166 case UI_OVERLAY_ACTION_MOVING:
4167 {
4168 ArrangerObject * obj =
4169 (ArrangerObject *) self->start_object;
4170 double ticks_diff =
4171 obj->pos.ticks -
4172 obj->transient->pos.ticks;
4173 int pitch_diff =
4174 ((MidiNote *) obj)->val -
4175 ((MidiNote *) obj->transient)->val;
4176
4177 GError * err = NULL;
4178 bool ret =
4179 arranger_selections_action_perform_move_midi (
4180 MA_SELECTIONS, ticks_diff, pitch_diff,
4181 F_ALREADY_MOVED, &err);
4182 if (!ret)
4183 {
4184 HANDLE_ERROR (
4185 err, "%s",
4186 _("Failed to move MIDI notes"));
4187 }
4188 }
4189 break;
4190 /* if copy/link-moved */
4191 case UI_OVERLAY_ACTION_MOVING_COPY:
4192 {
4193 ArrangerObject * obj =
4194 (ArrangerObject *) self->start_object;
4195 double ticks_diff =
4196 obj->pos.ticks -
4197 obj->transient->pos.ticks;
4198 int pitch_diff =
4199 ((MidiNote *) obj)->val -
4200 ((MidiNote *) obj->transient)->val;
4201
4202 GError * err = NULL;
4203 bool ret =
4204 arranger_selections_action_perform_duplicate_midi (
4205 (ArrangerSelections *) MA_SELECTIONS,
4206 ticks_diff, pitch_diff,
4207 F_ALREADY_MOVED, &err);
4208 if (!ret)
4209 {
4210 HANDLE_ERROR (
4211 err, "%s",
4212 _("Failed to duplicate MIDI notes"));
4213 }
4214 }
4215 break;
4216 case UI_OVERLAY_ACTION_NONE:
4217 {
4218 arranger_selections_clear (
4219 (ArrangerSelections *) MA_SELECTIONS,
4220 F_NO_FREE, F_NO_PUBLISH_EVENTS);
4221 }
4222 break;
4223 /* something was created */
4224 case UI_OVERLAY_ACTION_CREATING_RESIZING_R:
4225 case UI_OVERLAY_ACTION_AUTOFILLING:
4226 {
4227 GError * err = NULL;
4228 bool ret =
4229 arranger_selections_action_perform_create (
4230 MA_SELECTIONS, &err);
4231 if (!ret)
4232 {
4233 HANDLE_ERROR (
4234 err, "%s",
4235 _("Failed to create MIDI notes"));
4236 }
4237 }
4238 break;
4239 case UI_OVERLAY_ACTION_DELETE_SELECTING:
4240 case UI_OVERLAY_ACTION_ERASING:
4241 handle_erase_action (self);
4242 break;
4243 /* if didn't click on something */
4244 default:
4245 {
4246 }
4247 break;
4248 }
4249
4250 self->start_object = NULL;
4251 /*if (self->start_midi_note_clone)*/
4252 /*{*/
4253 /*midi_note_free (self->start_midi_note_clone);*/
4254 /*self->start_midi_note_clone = NULL;*/
4255 /*}*/
4256
4257 EVENTS_PUSH (
4258 ET_ARRANGER_SELECTIONS_CHANGED, MA_SELECTIONS);
4259 }
4260
4261 static void
on_drag_end_chord(ArrangerWidget * self)4262 on_drag_end_chord (
4263 ArrangerWidget * self)
4264 {
4265 switch (self->action)
4266 {
4267 case UI_OVERLAY_ACTION_STARTING_MOVING:
4268 {
4269 /* if something was clicked with ctrl without
4270 * moving*/
4271 if (self->ctrl_held)
4272 {
4273 if (self->start_object &&
4274 self->start_object_was_selected)
4275 {
4276 /*[> deselect it <]*/
4277 arranger_object_select (
4278 self->start_object,
4279 F_NO_SELECT, F_APPEND,
4280 F_NO_PUBLISH_EVENTS);
4281 }
4282 }
4283 }
4284 break;
4285 case UI_OVERLAY_ACTION_MOVING:
4286 {
4287 ArrangerObject * obj =
4288 (ArrangerObject *) self->start_object;
4289 double ticks_diff =
4290 obj->pos.ticks -
4291 obj->transient->pos.ticks;
4292
4293 GError * err = NULL;
4294 bool ret =
4295 arranger_selections_action_perform_move_chord (
4296 CHORD_SELECTIONS, ticks_diff,
4297 0, F_ALREADY_MOVED, &err);
4298 if (!ret)
4299 {
4300 HANDLE_ERROR (
4301 err, "%s",
4302 _("Failed to move chords"));
4303 }
4304 }
4305 break;
4306 case UI_OVERLAY_ACTION_MOVING_COPY:
4307 case UI_OVERLAY_ACTION_MOVING_LINK:
4308 {
4309 ArrangerObject * obj =
4310 (ArrangerObject *) self->start_object;
4311 double ticks_diff =
4312 obj->pos.ticks -
4313 obj->transient->pos.ticks;
4314
4315 GError * err = NULL;
4316 bool ret =
4317 arranger_selections_action_perform_duplicate_chord (
4318 CHORD_SELECTIONS, ticks_diff,
4319 0, F_ALREADY_MOVED, &err);
4320 if (!ret)
4321 {
4322 HANDLE_ERROR (
4323 err, "%s",
4324 _("Failed to duplicate chords"));
4325 }
4326 }
4327 break;
4328 case UI_OVERLAY_ACTION_NONE:
4329 case UI_OVERLAY_ACTION_STARTING_SELECTION:
4330 {
4331 arranger_selections_clear (
4332 (ArrangerSelections *)
4333 CHORD_SELECTIONS, F_NO_FREE,
4334 F_NO_PUBLISH_EVENTS);
4335 }
4336 break;
4337 case UI_OVERLAY_ACTION_CREATING_MOVING:
4338 {
4339 GError * err = NULL;
4340 bool ret =
4341 arranger_selections_action_perform_create (
4342 CHORD_SELECTIONS, &err);
4343 if (!ret)
4344 {
4345 HANDLE_ERROR (
4346 err, "%s",
4347 _("Failed to create objects"));
4348 }
4349 }
4350 break;
4351 case UI_OVERLAY_ACTION_DELETE_SELECTING:
4352 case UI_OVERLAY_ACTION_ERASING:
4353 handle_erase_action (self);
4354 break;
4355 /* if didn't click on something */
4356 default:
4357 {
4358 }
4359 break;
4360 }
4361 }
4362
4363 static void
on_drag_end_audio(ArrangerWidget * self)4364 on_drag_end_audio (
4365 ArrangerWidget * self)
4366 {
4367 switch (self->action)
4368 {
4369 case UI_OVERLAY_ACTION_RESIZING_R:
4370 {
4371 /* if start range selection is after
4372 * end, fix it */
4373 if (AUDIO_SELECTIONS->has_selection &&
4374 position_is_after (
4375 &AUDIO_SELECTIONS->sel_start,
4376 &AUDIO_SELECTIONS->sel_end))
4377 {
4378 Position tmp =
4379 AUDIO_SELECTIONS->sel_start;
4380 AUDIO_SELECTIONS->sel_start =
4381 AUDIO_SELECTIONS->sel_end;
4382 AUDIO_SELECTIONS->sel_end = tmp;
4383 }
4384 }
4385 break;
4386 case UI_OVERLAY_ACTION_RESIZING_L_FADE:
4387 case UI_OVERLAY_ACTION_RESIZING_R_FADE:
4388 {
4389 ArrangerObject * obj =
4390 (ArrangerObject *)
4391 clip_editor_get_region (CLIP_EDITOR);
4392 g_return_if_fail (
4393 IS_REGION_AND_NONNULL (obj));
4394 bool is_fade_in =
4395 self->action ==
4396 UI_OVERLAY_ACTION_RESIZING_L_FADE;
4397 double ticks_diff =
4398 (is_fade_in
4399 ? obj->fade_in_pos.ticks
4400 : obj->fade_out_pos.ticks) -
4401 self->fade_pos_at_start.ticks;
4402
4403 ArrangerSelections * sel =
4404 arranger_selections_new (
4405 ARRANGER_SELECTIONS_TYPE_TIMELINE);
4406 arranger_selections_add_object (
4407 sel, obj);
4408
4409 GError * err = NULL;
4410 bool ret =
4411 arranger_selections_action_perform_resize (
4412 sel,
4413 is_fade_in
4414 ? ARRANGER_SELECTIONS_ACTION_RESIZE_L_FADE
4415 : ARRANGER_SELECTIONS_ACTION_RESIZE_R_FADE,
4416 ticks_diff, F_ALREADY_EDITED, &err);
4417 arranger_selections_free (sel);
4418 if (!ret)
4419 {
4420 HANDLE_ERROR (
4421 err, "%s",
4422 _("Failed resizing selection"));
4423 }
4424 }
4425 break;
4426 case UI_OVERLAY_ACTION_RESIZING_UP_FADE_IN:
4427 case UI_OVERLAY_ACTION_RESIZING_UP_FADE_OUT:
4428 case UI_OVERLAY_ACTION_RESIZING_UP:
4429 {
4430 ZRegion * r =
4431 clip_editor_get_region (CLIP_EDITOR);
4432 ArrangerObject * obj =
4433 (ArrangerObject *) r;
4434 g_return_if_fail (
4435 IS_REGION_AND_NONNULL (obj));
4436
4437 /* prepare current selections */
4438 ArrangerSelections * sel =
4439 arranger_selections_new (
4440 ARRANGER_SELECTIONS_TYPE_TIMELINE);
4441 arranger_selections_add_object (
4442 sel, obj);
4443
4444 ArrangerSelectionsActionEditType edit_type;
4445
4446 /* prepare selections before */
4447 ArrangerSelections * sel_before =
4448 arranger_selections_new (
4449 ARRANGER_SELECTIONS_TYPE_TIMELINE);
4450 ArrangerObject * clone_obj =
4451 arranger_object_clone (obj);
4452 if (self->action ==
4453 UI_OVERLAY_ACTION_RESIZING_UP_FADE_IN)
4454 {
4455 clone_obj->fade_in_opts.curviness =
4456 self->dval_at_start;
4457 edit_type =
4458 ARRANGER_SELECTIONS_ACTION_EDIT_FADES;
4459 }
4460 else if (
4461 self->action ==
4462 UI_OVERLAY_ACTION_RESIZING_UP_FADE_OUT)
4463 {
4464 clone_obj->fade_out_opts.curviness =
4465 self->dval_at_start;
4466 edit_type =
4467 ARRANGER_SELECTIONS_ACTION_EDIT_FADES;
4468 }
4469 else if (
4470 self->action ==
4471 UI_OVERLAY_ACTION_RESIZING_UP)
4472 {
4473 ZRegion * clone_r =
4474 (ZRegion *) clone_obj;
4475 clone_r->gain = self->fval_at_start;
4476 edit_type =
4477 ARRANGER_SELECTIONS_ACTION_EDIT_PRIMITIVE;
4478 }
4479 arranger_selections_add_object (
4480 sel_before, clone_obj);
4481
4482 GError * err = NULL;
4483 bool ret =
4484 arranger_selections_action_perform_edit (
4485 sel_before, sel, edit_type,
4486 F_ALREADY_EDITED, &err);
4487 arranger_selections_free_full (sel_before);
4488 arranger_selections_free (sel);
4489 if (!ret)
4490 {
4491 HANDLE_ERROR (
4492 err, "%s",
4493 _("Failed to edit selection"));
4494 }
4495 }
4496 break;
4497 default:
4498 break;
4499 }
4500 }
4501
4502 NONNULL
4503 static void
on_drag_end_timeline(ArrangerWidget * self)4504 on_drag_end_timeline (
4505 ArrangerWidget * self)
4506 {
4507 g_debug ("drag end timeline starting...");
4508
4509 ArrangerSelections * sel =
4510 arranger_widget_get_selections (self);
4511 g_return_if_fail (sel);
4512
4513 switch (self->action)
4514 {
4515 case UI_OVERLAY_ACTION_RESIZING_UP_FADE_IN:
4516 case UI_OVERLAY_ACTION_RESIZING_UP_FADE_OUT:
4517 {
4518 GError * err = NULL;
4519 bool ret =
4520 arranger_selections_action_perform_edit (
4521 self->sel_at_start,
4522 (ArrangerSelections *) TL_SELECTIONS,
4523 ARRANGER_SELECTIONS_ACTION_EDIT_FADES,
4524 true, &err);
4525 if (!ret)
4526 {
4527 HANDLE_ERROR (
4528 err, "%s",
4529 _("Failed to edit timeline objects"));
4530 }
4531 }
4532 break;
4533 case UI_OVERLAY_ACTION_RESIZING_L:
4534 if (!self->resizing_range)
4535 {
4536 ArrangerObject * obj =
4537 (ArrangerObject *) self->start_object;
4538 double ticks_diff =
4539 obj->pos.ticks -
4540 obj->transient->pos.ticks;
4541
4542 GError * err = NULL;
4543 bool ret =
4544 arranger_selections_action_perform_resize (
4545 (ArrangerSelections *) TL_SELECTIONS,
4546 ARRANGER_SELECTIONS_ACTION_RESIZE_L,
4547 ticks_diff, F_ALREADY_EDITED, &err);
4548 if (!ret)
4549 {
4550 HANDLE_ERROR (
4551 err, "%s",
4552 _("Failed to resize timeline "
4553 "objects"));
4554 }
4555 }
4556 break;
4557 case UI_OVERLAY_ACTION_STRETCHING_L:
4558 {
4559 ArrangerObject * obj =
4560 (ArrangerObject *) self->start_object;
4561 double ticks_diff =
4562 obj->pos.ticks -
4563 obj->transient->pos.ticks;
4564
4565 GError * err = NULL;
4566 bool ret =
4567 arranger_selections_action_perform_resize (
4568 (ArrangerSelections *) TL_SELECTIONS,
4569 ARRANGER_SELECTIONS_ACTION_STRETCH_L,
4570 ticks_diff, F_ALREADY_EDITED, &err);
4571 if (!ret)
4572 {
4573 HANDLE_ERROR (
4574 err, "%s",
4575 _("Failed to resize timeline "
4576 "objects"));
4577 }
4578 }
4579 break;
4580 case UI_OVERLAY_ACTION_RESIZING_L_LOOP:
4581 {
4582 ArrangerObject * obj =
4583 (ArrangerObject *) self->start_object;
4584 double ticks_diff =
4585 obj->pos.ticks -
4586 obj->transient->pos.ticks;
4587
4588 GError * err = NULL;
4589 bool ret =
4590 arranger_selections_action_perform_resize (
4591 (ArrangerSelections *) TL_SELECTIONS,
4592 ARRANGER_SELECTIONS_ACTION_RESIZE_L_LOOP,
4593 ticks_diff, F_ALREADY_EDITED, &err);
4594 if (!ret)
4595 {
4596 HANDLE_ERROR (
4597 err, "%s",
4598 _("Failed to resize selection"));
4599 }
4600 }
4601 break;
4602 case UI_OVERLAY_ACTION_RESIZING_L_FADE:
4603 {
4604 ArrangerObject * obj =
4605 (ArrangerObject *) self->start_object;
4606 double ticks_diff =
4607 obj->fade_in_pos.ticks -
4608 obj->transient->fade_in_pos.ticks;
4609
4610 GError * err = NULL;
4611 bool ret =
4612 arranger_selections_action_perform_resize (
4613 (ArrangerSelections *) TL_SELECTIONS,
4614 ARRANGER_SELECTIONS_ACTION_RESIZE_L_FADE,
4615 ticks_diff, F_ALREADY_EDITED, &err);
4616 if (!ret)
4617 {
4618 HANDLE_ERROR (
4619 err, "%s",
4620 _("Failed setting fade in "
4621 "position"));
4622 }
4623 }
4624 break;
4625 case UI_OVERLAY_ACTION_RESIZING_R:
4626 if (!self->resizing_range)
4627 {
4628 ArrangerObject * obj =
4629 (ArrangerObject *) self->start_object;
4630 double ticks_diff =
4631 obj->end_pos.ticks -
4632 obj->transient->end_pos.ticks;
4633
4634 GError * err = NULL;
4635 bool ret =
4636 arranger_selections_action_perform_resize (
4637 (ArrangerSelections *) TL_SELECTIONS,
4638 ARRANGER_SELECTIONS_ACTION_RESIZE_R,
4639 ticks_diff, F_ALREADY_EDITED, &err);
4640 if (!ret)
4641 {
4642 HANDLE_ERROR (
4643 err, "%s",
4644 _("Failed resizing selections"));
4645 }
4646 }
4647 break;
4648 case UI_OVERLAY_ACTION_STRETCHING_R:
4649 {
4650 ArrangerObject * obj =
4651 (ArrangerObject *) self->start_object;
4652 double ticks_diff =
4653 obj->end_pos.ticks -
4654 obj->transient->end_pos.ticks;
4655 /* stretch now */
4656 transport_stretch_audio_regions (
4657 TRANSPORT, TL_SELECTIONS, false, 0.0);
4658
4659 GError * err = NULL;
4660 bool ret =
4661 arranger_selections_action_perform_resize (
4662 (ArrangerSelections *) TL_SELECTIONS,
4663 ARRANGER_SELECTIONS_ACTION_STRETCH_R,
4664 ticks_diff, F_ALREADY_EDITED, &err);
4665 if (!ret)
4666 {
4667 HANDLE_ERROR (
4668 err, "%s",
4669 _("Failed resizing selections"));
4670 }
4671 }
4672 break;
4673 case UI_OVERLAY_ACTION_RESIZING_R_LOOP:
4674 {
4675 ArrangerObject * obj =
4676 (ArrangerObject *) self->start_object;
4677 double ticks_diff =
4678 obj->end_pos.ticks -
4679 obj->transient->end_pos.ticks;
4680
4681 GError * err = NULL;
4682 bool ret =
4683 arranger_selections_action_perform_resize (
4684 (ArrangerSelections *) TL_SELECTIONS,
4685 ARRANGER_SELECTIONS_ACTION_RESIZE_R_LOOP,
4686 ticks_diff, F_ALREADY_EDITED, &err);
4687 if (!ret)
4688 {
4689 HANDLE_ERROR (
4690 err, "%s",
4691 _("Failed resizing selections"));
4692 }
4693 }
4694 break;
4695 case UI_OVERLAY_ACTION_RESIZING_R_FADE:
4696 {
4697 ArrangerObject * obj =
4698 (ArrangerObject *) self->start_object;
4699 double ticks_diff =
4700 obj->fade_out_pos.ticks -
4701 obj->transient->fade_out_pos.ticks;
4702
4703 GError * err = NULL;
4704 bool ret =
4705 arranger_selections_action_perform_resize (
4706 (ArrangerSelections *) TL_SELECTIONS,
4707 ARRANGER_SELECTIONS_ACTION_RESIZE_R_FADE,
4708 ticks_diff, F_ALREADY_EDITED, &err);
4709 if (!ret)
4710 {
4711 HANDLE_ERROR (
4712 err, "%s",
4713 _("Failed resizing selection"));
4714 }
4715 }
4716 break;
4717 case UI_OVERLAY_ACTION_STARTING_MOVING:
4718 /* if something was clicked with ctrl without
4719 * moving*/
4720 if (self->ctrl_held)
4721 {
4722 if (self->start_object &&
4723 self->start_object_was_selected)
4724 {
4725 /* deselect it */
4726 arranger_object_select (
4727 self->start_object,
4728 F_NO_SELECT, F_APPEND,
4729 F_PUBLISH_EVENTS);
4730 g_debug ("deselecting object");
4731 }
4732 }
4733 else if (self->n_press == 2)
4734 {
4735 /* double click on object */
4736 /*g_message ("DOUBLE CLICK");*/
4737 }
4738 break;
4739 case UI_OVERLAY_ACTION_MOVING:
4740 {
4741 ArrangerObject * obj =
4742 (ArrangerObject *) self->start_object;
4743 g_return_if_fail (obj && obj->transient);
4744 double ticks_diff =
4745 obj->pos.ticks -
4746 obj->transient->pos.ticks;
4747
4748 GError * err = NULL;
4749 bool ret =
4750 arranger_selections_action_perform_move_timeline (
4751 TL_SELECTIONS, ticks_diff,
4752 self->visible_track_diff,
4753 self->lane_diff, F_ALREADY_MOVED, &err);
4754 if (!ret)
4755 {
4756 HANDLE_ERROR (
4757 err, "%s",
4758 _("Failed to move objects"));
4759 }
4760 }
4761 break;
4762 case UI_OVERLAY_ACTION_MOVING_COPY:
4763 case UI_OVERLAY_ACTION_MOVING_LINK:
4764 {
4765 ArrangerObject * obj =
4766 (ArrangerObject *) self->start_object;
4767 double ticks_diff =
4768 obj->pos.ticks -
4769 obj->transient->pos.ticks;
4770
4771 GError * err = NULL;
4772 bool ret;
4773 if (ACTION_IS (MOVING_COPY))
4774 {
4775 ret =
4776 arranger_selections_action_perform_duplicate_timeline (
4777 TL_SELECTIONS, ticks_diff,
4778 self->visible_track_diff,
4779 self->lane_diff, F_ALREADY_MOVED,
4780 &err);
4781 }
4782 else if (ACTION_IS (MOVING_LINK))
4783 {
4784 ret =
4785 arranger_selections_action_perform_link (
4786 self->sel_at_start,
4787 (ArrangerSelections *)
4788 TL_SELECTIONS, ticks_diff,
4789 self->visible_track_diff,
4790 self->lane_diff, F_ALREADY_MOVED,
4791 &err);
4792 }
4793 else
4794 g_return_if_reached ();
4795
4796 if (!ret)
4797 {
4798 HANDLE_ERROR (
4799 err, "%s",
4800 _("Failed to link or move "
4801 "selection"));
4802 }
4803 }
4804 break;
4805 case UI_OVERLAY_ACTION_NONE:
4806 case UI_OVERLAY_ACTION_STARTING_SELECTION:
4807 arranger_selections_clear (
4808 sel, F_NO_FREE, F_NO_PUBLISH_EVENTS);
4809 break;
4810 /* if something was created */
4811 case UI_OVERLAY_ACTION_CREATING_MOVING:
4812 case UI_OVERLAY_ACTION_CREATING_RESIZING_R:
4813 case UI_OVERLAY_ACTION_AUTOFILLING:
4814 if (arranger_selections_has_any (sel))
4815 {
4816 GError * err = NULL;
4817 bool ret =
4818 arranger_selections_action_perform_create (
4819 sel, &err);
4820 if (!ret)
4821 {
4822 HANDLE_ERROR (
4823 err, "%s",
4824 _("Failed to create objects"));
4825 }
4826 }
4827 break;
4828 case UI_OVERLAY_ACTION_DELETE_SELECTING:
4829 case UI_OVERLAY_ACTION_ERASING:
4830 handle_erase_action (self);
4831 break;
4832 case UI_OVERLAY_ACTION_CUTTING:
4833 {
4834 /* get cut position */
4835 Position cut_pos;
4836 position_set_to_pos (
4837 &cut_pos, &self->curr_pos);
4838
4839 if (SNAP_GRID_ANY_SNAP (
4840 self->snap_grid) &&
4841 !self->shift_held)
4842 {
4843 position_snap_simple (
4844 &cut_pos, self->snap_grid);
4845 }
4846 if (arranger_selections_can_split_at_pos (
4847 (ArrangerSelections *) TL_SELECTIONS,
4848 &cut_pos))
4849 {
4850 GError * err = NULL;
4851 bool ret =
4852 arranger_selections_action_perform_split (
4853 (ArrangerSelections *)
4854 TL_SELECTIONS,
4855 &cut_pos, &err);
4856 if (!ret)
4857 {
4858 HANDLE_ERROR (
4859 err, "%s",
4860 _("Failed to split selection"));
4861 }
4862 }
4863 }
4864 break;
4865 case UI_OVERLAY_ACTION_RENAMING:
4866 {
4867 const char * obj_type_str =
4868 arranger_object_get_type_as_string (
4869 self->start_object->type);
4870 char * str =
4871 g_strdup_printf (
4872 _("%s name"), obj_type_str);
4873 StringEntryDialogWidget * dialog =
4874 string_entry_dialog_widget_new (
4875 str, self->start_object,
4876 (GenericStringGetter)
4877 arranger_object_get_name,
4878 (GenericStringSetter)
4879 arranger_object_set_name_with_action);
4880 gtk_widget_show_all (GTK_WIDGET (dialog));
4881 self->action = UI_OVERLAY_ACTION_NONE;
4882 g_free (str);
4883 }
4884 break;
4885 default:
4886 break;
4887 }
4888
4889 self->resizing_range = 0;
4890 self->resizing_range_start = 0;
4891 self->visible_track_diff = 0;
4892 self->lane_diff = 0;
4893
4894 g_debug ("drag end timeline done");
4895 }
4896
4897 static void
drag_end(GtkGestureDrag * gesture,gdouble offset_x,gdouble offset_y,ArrangerWidget * self)4898 drag_end (
4899 GtkGestureDrag *gesture,
4900 gdouble offset_x,
4901 gdouble offset_y,
4902 ArrangerWidget * self)
4903 {
4904 g_debug ("arranger drag end starting...");
4905
4906 if (ACTION_IS (SELECTING) ||
4907 ACTION_IS (DELETE_SELECTING))
4908 {
4909 EVENTS_PUSH (
4910 ET_SELECTING_IN_ARRANGER, self);
4911 }
4912
4913 #undef ON_DRAG_END
4914
4915 /* if something was clicked with ctrl without
4916 * moving */
4917 if (ACTION_IS (STARTING_MOVING) &&
4918 self->start_object && self->ctrl_held)
4919 {
4920 /* if was selected, deselect it */
4921 if (self->start_object_was_selected)
4922 {
4923 arranger_object_select (
4924 self->start_object,
4925 F_NO_SELECT, F_APPEND,
4926 F_PUBLISH_EVENTS);
4927 g_debug ("ctrl-deselecting object");
4928 }
4929 /* if was deselected, select it */
4930 else
4931 {
4932 /* select it */
4933 arranger_object_select (
4934 self->start_object,
4935 F_SELECT, F_APPEND,
4936 F_PUBLISH_EVENTS);
4937 g_debug ("ctrl-selecting object");
4938 }
4939 }
4940
4941 /* handle click without drag for
4942 * delete-selecting */
4943 if ((self->action ==
4944 UI_OVERLAY_ACTION_STARTING_DELETE_SELECTION ||
4945 self->action ==
4946 UI_OVERLAY_ACTION_STARTING_ERASING) &&
4947 self->drag_start_btn == GDK_BUTTON_PRIMARY)
4948 {
4949 self->action =
4950 UI_OVERLAY_ACTION_DELETE_SELECTING;
4951 ArrangerSelections * sel =
4952 arranger_widget_get_selections (self);
4953 g_return_if_fail (sel);
4954 arranger_selections_clear (
4955 sel, F_NO_FREE, F_NO_PUBLISH_EVENTS);
4956 self->sel_to_delete =
4957 arranger_selections_clone (sel);
4958 select_in_range (
4959 self, offset_x, offset_y, F_IN_RANGE,
4960 F_IGNORE_FROZEN, F_DELETE);
4961 }
4962
4963 switch (self->type)
4964 {
4965 case TYPE (TIMELINE):
4966 on_drag_end_timeline (self);
4967 break;
4968 case TYPE (AUDIO):
4969 on_drag_end_audio (self);
4970 break;
4971 case TYPE (MIDI):
4972 on_drag_end_midi (self);
4973 break;
4974 case TYPE (MIDI_MODIFIER):
4975 on_drag_end_midi_modifier (self);
4976 break;
4977 case TYPE (CHORD):
4978 on_drag_end_chord (self);
4979 break;
4980 case TYPE (AUTOMATION):
4981 on_drag_end_automation (self);
4982 break;
4983 default:
4984 break;
4985 }
4986
4987 /* if right click, show context */
4988 GdkEventSequence *sequence =
4989 gtk_gesture_single_get_current_sequence (
4990 GTK_GESTURE_SINGLE (gesture));
4991 const GdkEvent * ev =
4992 gtk_gesture_get_last_event (
4993 GTK_GESTURE (gesture), sequence);
4994 guint btn;
4995 if (ev)
4996 {
4997 g_warn_if_fail (
4998 gdk_event_get_button (ev, &btn));
4999 if (btn == GDK_BUTTON_SECONDARY &&
5000 self->action !=
5001 UI_OVERLAY_ACTION_ERASING)
5002 {
5003 show_context_menu (
5004 self, self->start_x + offset_x,
5005 self->start_y + offset_y);
5006 }
5007 }
5008
5009 /* reset start coordinates and offsets */
5010 self->start_x = 0;
5011 self->start_y = 0;
5012 self->last_offset_x = 0;
5013 self->last_offset_y = 0;
5014 self->drag_update_started = false;
5015 self->last_adj_ticks_diff = 0;
5016 self->start_object = NULL;
5017 self->hovered_object = NULL;
5018
5019 self->shift_held = 0;
5020 self->ctrl_held = 0;
5021
5022 if (self->sel_at_start)
5023 {
5024 arranger_selections_free_full (
5025 self->sel_at_start);
5026 self->sel_at_start = NULL;
5027 }
5028 if (self->region_at_start)
5029 {
5030 arranger_object_free (
5031 (ArrangerObject *) self->region_at_start);
5032 self->region_at_start = NULL;
5033 }
5034
5035 /* reset action */
5036 self->action = UI_OVERLAY_ACTION_NONE;
5037
5038 /* queue redraw to hide the selection */
5039 /*gtk_widget_queue_draw (GTK_WIDGET (self->bg));*/
5040
5041 arranger_widget_redraw_whole (self);
5042 arranger_widget_refresh_cursor (self);
5043
5044 g_debug ("arranger drag end done");
5045 }
5046
5047 #if 0
5048 /**
5049 * @param type The arranger object type, or -1 to
5050 * search for all types.
5051 */
5052 static ArrangerObject *
5053 get_hit_timeline_object (
5054 ArrangerWidget * self,
5055 ArrangerObjectType type,
5056 double x,
5057 double y)
5058 {
5059 AutomationTrack * at =
5060 timeline_arranger_widget_get_at_at_y (
5061 self, y);
5062 TrackLane * lane = NULL;
5063 if (!at)
5064 lane =
5065 timeline_arranger_widget_get_track_lane_at_y (
5066 self, y);
5067 Track * track = NULL;
5068
5069 /* get the position of x */
5070 Position pos;
5071 arranger_widget_px_to_pos (
5072 self, x, &pos, 1);
5073
5074 /* check for hit automation regions */
5075 if (at)
5076 {
5077 if (type != ARRANGER_OBJECT_TYPE_ALL &&
5078 type != ARRANGER_OBJECT_TYPE_REGION)
5079 return NULL;
5080
5081 /* return if any region matches the
5082 * position */
5083 for (int i = 0; i < at->num_regions; i++)
5084 {
5085 ZRegion * r = at->regions[i];
5086 if (region_is_hit (r, pos.frames, 1))
5087 {
5088 return (ArrangerObject *) r;
5089 }
5090 }
5091 }
5092 /* check for hit regions in lane */
5093 else if (lane)
5094 {
5095 if (type >= ARRANGER_OBJECT_TYPE_ALL &&
5096 type != ARRANGER_OBJECT_TYPE_REGION)
5097 return NULL;
5098
5099 /* return if any region matches the
5100 * position */
5101 for (int i = 0; i < lane->num_regions; i++)
5102 {
5103 ZRegion * r = lane->regions[i];
5104 if (region_is_hit (r, pos.frames, 1))
5105 {
5106 return (ArrangerObject *) r;
5107 }
5108 }
5109 }
5110 /* check for hit regions in main track */
5111 else
5112 {
5113 track =
5114 timeline_arranger_widget_get_track_at_y (
5115 self, y);
5116
5117 if (track)
5118 {
5119 for (int i = 0; i < track->num_lanes; i++)
5120 {
5121 lane = track->lanes[i];
5122 for (int j = 0; j < lane->num_regions;
5123 j++)
5124 {
5125 ZRegion * r = lane->regions[j];
5126 if (region_is_hit (
5127 r, pos.frames, 1))
5128 {
5129 return (ArrangerObject *) r;
5130 }
5131 }
5132 }
5133 }
5134 }
5135
5136 return NULL;
5137 }
5138 #endif
5139
5140 /**
5141 * Returns the ArrangerObject of the given type
5142 * at (x,y).
5143 *
5144 * @param type The arranger object type, or -1 to
5145 * search for all types.
5146 * @param x X, or -1 to not check x.
5147 * @param y Y, or -1 to not check y.
5148 */
5149 ArrangerObject *
arranger_widget_get_hit_arranger_object(ArrangerWidget * self,ArrangerObjectType type,double x,double y)5150 arranger_widget_get_hit_arranger_object (
5151 ArrangerWidget * self,
5152 ArrangerObjectType type,
5153 double x,
5154 double y)
5155 {
5156 ArrangerObject * objs[800];
5157 int num_objs;
5158 arranger_widget_get_hit_objects_at_point (
5159 self, type, x, y, objs, &num_objs);
5160 if (num_objs > 0)
5161 {
5162 return objs[num_objs - 1];
5163 }
5164 else
5165 {
5166 return NULL;
5167 }
5168
5169 /*switch (self->type)*/
5170 /*{*/
5171 /*case TYPE (TIMELINE):*/
5172 /*return*/
5173 /*get_hit_timeline_object (self, type, x, y);*/
5174 /*break;*/
5175 /*default:*/
5176 /*break;*/
5177 /*}*/
5178 /*return NULL;*/
5179 }
5180
5181 /**
5182 * Wrapper of the UI functions based on the arranger
5183 * type.
5184 */
5185 int
arranger_widget_pos_to_px(ArrangerWidget * self,Position * pos,int use_padding)5186 arranger_widget_pos_to_px (
5187 ArrangerWidget * self,
5188 Position * pos,
5189 int use_padding)
5190 {
5191 if (self->type == TYPE (TIMELINE))
5192 {
5193 return
5194 ui_pos_to_px_timeline (pos, use_padding);
5195 }
5196 else
5197 {
5198 return
5199 ui_pos_to_px_editor (pos, use_padding);
5200 }
5201
5202 g_return_val_if_reached (-1);
5203 }
5204
5205 /**
5206 * Returns the ArrangerSelections for this
5207 * ArrangerWidget.
5208 */
5209 ArrangerSelections *
arranger_widget_get_selections(ArrangerWidget * self)5210 arranger_widget_get_selections (
5211 ArrangerWidget * self)
5212 {
5213 switch (self->type)
5214 {
5215 case TYPE (TIMELINE):
5216 return
5217 (ArrangerSelections *) TL_SELECTIONS;
5218 case TYPE (MIDI):
5219 case TYPE (MIDI_MODIFIER):
5220 return
5221 (ArrangerSelections *) MA_SELECTIONS;
5222 case TYPE (AUTOMATION):
5223 return
5224 (ArrangerSelections *)
5225 AUTOMATION_SELECTIONS;
5226 case TYPE (CHORD):
5227 return
5228 (ArrangerSelections *) CHORD_SELECTIONS;
5229 case TYPE (AUDIO):
5230 /* FIXME */
5231 return
5232 (ArrangerSelections *) TL_SELECTIONS;
5233 }
5234
5235 g_return_val_if_reached (NULL);
5236 }
5237
5238 /**
5239 * Sets transient object and actual object
5240 * visibility for every ArrangerObject in the
5241 * ArrangerWidget based on the current action.
5242 */
5243 /*void*/
5244 /*arranger_widget_update_visibility (*/
5245 /*ArrangerWidget * self)*/
5246 /*{*/
5247 /*GList *children, *iter;*/
5248 /*ArrangerObjectWidget * aow;*/
5249
5250 /*#define UPDATE_VISIBILITY(x) \*/
5251 /*children = \*/
5252 /*gtk_container_get_children ( \*/
5253 /*GTK_CONTAINER (x)); \*/
5254 /*aow = NULL; \*/
5255 /*for (iter = children; \*/
5256 /*iter != NULL; \*/
5257 /*iter = g_list_next (iter)) \*/
5258 /*{ \*/
5259 /*if (!Z_IS_ARRANGER_OBJECT_WIDGET ( \*/
5260 /*iter->data)) \*/
5261 /*continue; \*/
5262 /*\*/
5263 /*aow = \*/
5264 /*Z_ARRANGER_OBJECT_WIDGET (iter->data); \*/
5265 /*ARRANGER_OBJECT_WIDGET_GET_PRIVATE (aow); \*/
5266 /*g_warn_if_fail (ao_prv->arranger_object); \*/
5267 /*arranger_object_set_widget_visibility_and_state ( \*/
5268 /*ao_prv->arranger_object, 1); \*/
5269 /*} \*/
5270 /*g_list_free (children);*/
5271
5272 /*UPDATE_VISIBILITY (self);*/
5273
5274 /* if midi arranger, do the same for midi modifier
5275 * arranger, and vice versa */
5276 /*if (Z_IS_MIDI_ARRANGER_WIDGET (self))*/
5277 /*{*/
5278 /*UPDATE_VISIBILITY (MW_MIDI_MODIFIER_ARRANGER);*/
5279 /*}*/
5280 /*else if (Z_IS_MIDI_MODIFIER_ARRANGER_WIDGET (self))*/
5281 /*{*/
5282 /*UPDATE_VISIBILITY (MW_MIDI_ARRANGER);*/
5283 /*}*/
5284
5285 /*#undef UPDATE_VISIBILITY*/
5286 /*}*/
5287
5288 /**
5289 * Gets the corresponding scrolled window.
5290 */
5291 GtkScrolledWindow *
arranger_widget_get_scrolled_window(ArrangerWidget * self)5292 arranger_widget_get_scrolled_window (
5293 ArrangerWidget * self)
5294 {
5295 switch (self->type)
5296 {
5297 case TYPE (TIMELINE):
5298 if (self->is_pinned)
5299 {
5300 return
5301 MW_TIMELINE_PANEL->
5302 pinned_timeline_scroll;
5303 }
5304 else
5305 {
5306 return
5307 MW_TIMELINE_PANEL->timeline_scroll;
5308 }
5309 case TYPE (MIDI):
5310 return MW_MIDI_EDITOR_SPACE->arranger_scroll;
5311 case TYPE (MIDI_MODIFIER):
5312 return MW_MIDI_EDITOR_SPACE->
5313 modifier_arranger_scroll;
5314 case TYPE (AUDIO):
5315 return MW_AUDIO_EDITOR_SPACE->arranger_scroll;
5316 case TYPE (CHORD):
5317 return MW_CHORD_EDITOR_SPACE->arranger_scroll;
5318 case TYPE (AUTOMATION):
5319 return MW_AUTOMATION_EDITOR_SPACE->
5320 arranger_scroll;
5321 }
5322
5323 return NULL;
5324 }
5325
5326 /**
5327 * Get all objects currently present in the
5328 * arranger.
5329 *
5330 * @param objs Array to fill in.
5331 * @param size Array size to fill in.
5332 */
5333 void
arranger_widget_get_all_objects(ArrangerWidget * self,ArrangerObject ** objs,int * size)5334 arranger_widget_get_all_objects (
5335 ArrangerWidget * self,
5336 ArrangerObject ** objs,
5337 int * size)
5338 {
5339 GdkRectangle rect = {
5340 0, 0,
5341 gtk_widget_get_allocated_width (
5342 GTK_WIDGET (self)),
5343 gtk_widget_get_allocated_height (
5344 GTK_WIDGET (self)),
5345 };
5346
5347 arranger_widget_get_hit_objects_in_rect (
5348 self, ARRANGER_OBJECT_TYPE_ALL, &rect,
5349 objs, size);
5350 }
5351
5352 RulerWidget *
arranger_widget_get_ruler(ArrangerWidget * self)5353 arranger_widget_get_ruler (
5354 ArrangerWidget * self)
5355 {
5356 return
5357 self->type == TYPE (TIMELINE) ?
5358 MW_RULER : EDITOR_RULER;
5359 }
5360
5361 /**
5362 * Returns the current visible rectangle.
5363 *
5364 * @param rect The rectangle to fill in.
5365 */
5366 void
arranger_widget_get_visible_rect(ArrangerWidget * self,GdkRectangle * rect)5367 arranger_widget_get_visible_rect (
5368 ArrangerWidget * self,
5369 GdkRectangle * rect)
5370 {
5371 GtkScrolledWindow * scroll =
5372 arranger_widget_get_scrolled_window (self);
5373 GtkAdjustment * xadj =
5374 gtk_scrolled_window_get_hadjustment (
5375 scroll);
5376 rect->x =
5377 (int) gtk_adjustment_get_value (xadj);
5378 GtkAdjustment * yadj =
5379 gtk_scrolled_window_get_vadjustment (scroll);
5380 rect->y =
5381 (int) gtk_adjustment_get_value (yadj);
5382 rect->height =
5383 gtk_widget_get_allocated_height (
5384 GTK_WIDGET (scroll));
5385 rect->width =
5386 gtk_widget_get_allocated_width (
5387 GTK_WIDGET (scroll));
5388 }
5389
5390 /**
5391 * Queues a redraw of the whole visible arranger.
5392 */
5393 void
arranger_widget_redraw_whole(ArrangerWidget * self)5394 arranger_widget_redraw_whole (
5395 ArrangerWidget * self)
5396 {
5397 g_message (
5398 "redraw whole %s arranger",
5399 arranger_widget_get_type_str (self->type));
5400
5401 #if 0
5402 if (self->type == ARRANGER_WIDGET_TYPE_TIMELINE)
5403 {
5404 char * bt =
5405 backtrace_get_with_lines ("", 4, false);
5406 g_message ("bt: %s", bt);
5407 g_free (bt);
5408 }
5409 #endif
5410
5411 GdkRectangle rect;
5412 arranger_widget_get_visible_rect (self, &rect);
5413
5414 /* redraw visible area */
5415 arranger_widget_redraw_rectangle (self, &rect);
5416 }
5417
5418 bool
arranger_widget_is_playhead_visible(ArrangerWidget * self)5419 arranger_widget_is_playhead_visible (
5420 ArrangerWidget * self)
5421 {
5422 GdkRectangle rect;
5423 arranger_widget_get_visible_rect (self, &rect);
5424
5425 int playhead_x =
5426 arranger_widget_get_playhead_px (self);
5427 int min_x =
5428 MIN (self->last_playhead_px, playhead_x);
5429 min_x = MAX (min_x - 4, rect.x);
5430 int max_x =
5431 MAX (self->last_playhead_px, playhead_x);
5432 max_x = MIN (max_x + 4, rect.x + rect.width);
5433
5434 int width = max_x - min_x;
5435
5436 return width >= 0;
5437 }
5438
5439 /**
5440 * Only redraws the playhead part.
5441 */
5442 void
arranger_widget_redraw_playhead(ArrangerWidget * self)5443 arranger_widget_redraw_playhead (
5444 ArrangerWidget * self)
5445 {
5446 if (!gtk_widget_is_visible (GTK_WIDGET (self)) ||
5447 !arranger_widget_is_playhead_visible (self))
5448 return;
5449
5450 GdkRectangle rect;
5451 arranger_widget_get_visible_rect (self, &rect);
5452
5453 int buffer = 5;
5454 int playhead_x =
5455 arranger_widget_get_playhead_px (self);
5456 int min_x =
5457 MIN (self->last_playhead_px, playhead_x);
5458 min_x = MAX (min_x - buffer, rect.x);
5459 int max_x =
5460 MAX (self->last_playhead_px, playhead_x);
5461 max_x = MIN (max_x + buffer, rect.x + rect.width);
5462
5463 /*g_message ("queueing redraw %d", playhead_x);*/
5464 GdkRectangle draw_rect = {
5465 .x = min_x, .y = rect.y,
5466 .width = (max_x - min_x),
5467 .height = rect.height };
5468 arranger_widget_redraw_rectangle (
5469 self, &draw_rect);
5470
5471 if (!gtk_widget_get_mapped (GTK_WIDGET (self)))
5472 {
5473 #if 0
5474 g_debug (
5475 "%s arranger is unmapped, skipping "
5476 "autoscroll in %s",
5477 arranger_widget_get_type_str (self->type),
5478 __func__);
5479 #endif
5480 return;
5481 }
5482
5483 /* auto scroll */
5484 bool scroll_edges = false;
5485 bool follow = false;
5486 if (self->type == ARRANGER_WIDGET_TYPE_TIMELINE)
5487 {
5488 scroll_edges =
5489 g_settings_get_boolean (
5490 S_UI, "timeline-playhead-scroll-edges");
5491 follow =
5492 g_settings_get_boolean (
5493 S_UI, "timeline-playhead-follow");
5494 }
5495 else
5496 {
5497 scroll_edges =
5498 g_settings_get_boolean (
5499 S_UI, "editor-playhead-scroll-edges");
5500 follow =
5501 g_settings_get_boolean (
5502 S_UI, "editor-playhead-follow");
5503 }
5504
5505 GtkScrolledWindow * scroll =
5506 arranger_widget_get_scrolled_window (self);
5507 GtkAdjustment * adj =
5508 gtk_scrolled_window_get_hadjustment (scroll);
5509 if (follow)
5510 {
5511 /* scroll */
5512 gtk_adjustment_set_value (
5513 adj, playhead_x - rect.width / 2);
5514 }
5515 else if (scroll_edges)
5516 {
5517 buffer = 32;
5518 if (playhead_x >
5519 ((rect.x + rect.width) - buffer) ||
5520 playhead_x < rect.x + buffer)
5521 {
5522 /* scroll */
5523 gtk_adjustment_set_value (
5524 adj,
5525 CLAMP (
5526 (double) playhead_x - buffer,
5527 gtk_adjustment_get_lower (adj),
5528 gtk_adjustment_get_upper (adj)));
5529 }
5530 }
5531
5532 /* cache x to draw */
5533 /*self->queued_playhead_px = playhead_x;*/
5534 }
5535
5536 /**
5537 * Only redraws the given rectangle.
5538 */
5539 void
arranger_widget_redraw_rectangle(ArrangerWidget * self,GdkRectangle * rect)5540 arranger_widget_redraw_rectangle (
5541 ArrangerWidget * self,
5542 GdkRectangle * rect)
5543 {
5544 self->redraw = true;
5545
5546 self->ruler_display =
5547 (TransportDisplay)
5548 g_settings_get_enum (S_UI, "ruler-display");
5549
5550 /* add 1px padding */
5551 GdkRectangle draw_rect = *rect;
5552 if (draw_rect.x > 0)
5553 draw_rect.x--;
5554 if (draw_rect.y > 0)
5555 draw_rect.y--;
5556 draw_rect.width += 2;
5557 draw_rect.height += 2;
5558
5559 /* queue draw in next cycle */
5560 gtk_widget_queue_draw_area (
5561 GTK_WIDGET (self), draw_rect.x, draw_rect.y,
5562 draw_rect.width, draw_rect.height);
5563
5564 #if 0
5565 g_message (
5566 "queue redraw rect x:%d y:%d w:%d h:%d for %s "
5567 "arranger",
5568 rect->x, rect->y, rect->width, rect->height,
5569 arranger_widget_get_type_str (self->type));
5570 #endif
5571
5572 #if 0
5573 if (self->dummy_surface && false)
5574 {
5575 /* start drawing now in thread */
5576 ArrangerDrawTaskData * task_data =
5577 (ArrangerDrawTaskData *)
5578 object_pool_get (self->draw_task_obj_pool);
5579 task_data->rect = *rect;
5580 if (task_data->surface)
5581 {
5582 cairo_surface_destroy (
5583 task_data->surface);
5584 }
5585 if (task_data->cr)
5586 {
5587 cairo_destroy (task_data->cr);
5588 }
5589 task_data->surface =
5590 cairo_surface_create_similar (
5591 self->dummy_surface,
5592 CAIRO_CONTENT_COLOR_ALPHA,
5593 rect->width, rect->height);
5594 task_data->cr =
5595 cairo_create (task_data->surface);
5596 GError * err = NULL;
5597 bool ret =
5598 g_thread_pool_push (
5599 self->draw_thread_pool, task_data, &err);
5600 if (!ret)
5601 {
5602 g_critical (
5603 "failed to push task to arranger draw "
5604 "thread: %s", err->message);
5605 g_error_free (err);
5606 }
5607 }
5608 #endif
5609 }
5610
5611 static gboolean
on_scroll(GtkWidget * widget,GdkEventScroll * event,ArrangerWidget * self)5612 on_scroll (
5613 GtkWidget *widget,
5614 GdkEventScroll *event,
5615 ArrangerWidget * self)
5616 {
5617 double x = event->x,
5618 y = event->y,
5619 adj_val,
5620 diff;
5621
5622 g_debug ("scrolled to %f, %f", x, y);
5623
5624 if (!(event->state & GDK_CONTROL_MASK))
5625 return FALSE;
5626
5627 /* if shift also pressed, handle vertical zoom */
5628 if (event->state & GDK_SHIFT_MASK)
5629 {
5630 if (self->type == TYPE (MIDI))
5631 {
5632 midi_arranger_handle_vertical_zoom_scroll (
5633 self, event);
5634 }
5635 else if (self->type == TYPE (TIMELINE))
5636 {
5637 tracklist_widget_handle_vertical_zoom_scroll (
5638 MW_TRACKLIST, event);
5639 }
5640 }
5641 /* else if just control pressed handle horizontal
5642 * zooom */
5643 else
5644 {
5645 Position cursor_pos;
5646 GtkScrolledWindow * scroll =
5647 arranger_widget_get_scrolled_window (self);
5648 GtkAdjustment * adj;
5649 int new_x;
5650 RulerWidget * ruler =
5651 arranger_widget_get_ruler (self);
5652
5653 /* get current adjustment so we can get the
5654 * difference from the cursor */
5655 adj = gtk_scrolled_window_get_hadjustment (
5656 scroll);
5657 adj_val = gtk_adjustment_get_value (adj);
5658
5659 /* get positions of cursor */
5660 arranger_widget_px_to_pos (
5661 self, x, &cursor_pos, F_PADDING);
5662
5663 /* get px diff so we can calculate the new
5664 * adjustment later */
5665 diff = x - adj_val;
5666
5667 /* scroll down, zoom out */
5668 if (event->delta_y > 0)
5669 {
5670 ruler_widget_set_zoom_level (
5671 ruler,
5672 ruler_widget_get_zoom_level (ruler) / 1.3);
5673 }
5674 else /* scroll up, zoom in */
5675 {
5676 ruler_widget_set_zoom_level (
5677 ruler,
5678 ruler_widget_get_zoom_level (ruler) * 1.3);
5679 }
5680
5681 new_x = arranger_widget_pos_to_px (
5682 self, &cursor_pos, 1);
5683
5684 /* refresh relevant widgets */
5685 if (self->type == TYPE (TIMELINE))
5686 timeline_minimap_widget_refresh (
5687 MW_TIMELINE_MINIMAP);
5688
5689 /* get updated adjustment and set its value
5690 at the same offset as before */
5691 adj =
5692 gtk_scrolled_window_get_hadjustment (scroll);
5693 gtk_adjustment_set_value (adj, new_x - diff);
5694 }
5695
5696 return TRUE;
5697 }
5698
5699 /**
5700 * Motion callback.
5701 */
5702 static gboolean
on_motion(GtkWidget * widget,GdkEventMotion * event,ArrangerWidget * self)5703 on_motion (
5704 GtkWidget * widget,
5705 GdkEventMotion * event,
5706 ArrangerWidget * self)
5707 {
5708 self->hover_x = MAX (event->x, 0.0);
5709 self->hover_y = MAX (event->y, 0.0);
5710
5711 GdkModifierType state;
5712 int has_state =
5713 gtk_get_current_event_state (&state);
5714 if (has_state)
5715 {
5716 self->alt_held =
5717 state & GDK_MOD1_MASK;
5718 self->ctrl_held =
5719 state & GDK_CONTROL_MASK;
5720 self->shift_held =
5721 state & GDK_SHIFT_MASK;
5722 }
5723
5724 /* highlight hovered object */
5725 ArrangerObject * obj =
5726 arranger_widget_get_hit_arranger_object (
5727 self, ARRANGER_OBJECT_TYPE_ALL,
5728 self->hover_x, self->hover_y);
5729 if (obj && arranger_object_is_frozen (obj))
5730 {
5731 obj = NULL;
5732 }
5733 if (self->hovered_object != obj)
5734 {
5735 g_return_val_if_fail (
5736 !self->hovered_object ||
5737 IS_ARRANGER_OBJECT (self->hovered_object),
5738 false);
5739 ArrangerObject * prev_obj =
5740 self->hovered_object;
5741 self->hovered_object = obj;
5742
5743 /* redraw previous hovered object to
5744 * unhover it */
5745 if (prev_obj)
5746 arranger_object_queue_redraw (prev_obj);
5747
5748 /* redraw new hovered object */
5749 if (obj)
5750 arranger_object_queue_redraw (obj);
5751 }
5752
5753 arranger_widget_refresh_cursor (self);
5754
5755 switch (self->type)
5756 {
5757 case TYPE (TIMELINE):
5758 timeline_arranger_widget_set_cut_lines_visible (
5759 self);
5760 break;
5761 case TYPE (CHORD):
5762 if (event->type == GDK_LEAVE_NOTIFY)
5763 self->hovered_chord_index = -1;
5764 else
5765 self->hovered_chord_index =
5766 chord_arranger_widget_get_chord_at_y (
5767 event->y);
5768 break;
5769 case TYPE (MIDI):
5770 if (event->type == GDK_LEAVE_NOTIFY)
5771 {
5772 midi_arranger_widget_set_hovered_note (
5773 self, -1);
5774 }
5775 else
5776 {
5777 midi_arranger_widget_set_hovered_note (
5778 self,
5779 piano_roll_keys_widget_get_key_from_y (
5780 MW_PIANO_ROLL_KEYS, event->y));
5781 }
5782 break;
5783 default:
5784 break;
5785 }
5786
5787 return FALSE;
5788 }
5789
5790 static gboolean
on_focus(GtkWidget * widget,gpointer user_data)5791 on_focus (
5792 GtkWidget * widget,
5793 gpointer user_data)
5794 {
5795 g_debug ("arranger focused");
5796
5797 return FALSE;
5798 }
5799
5800 static gboolean
on_focus_out(GtkWidget * widget,GdkEvent * event,ArrangerWidget * self)5801 on_focus_out (GtkWidget *widget,
5802 GdkEvent *event,
5803 ArrangerWidget * self)
5804 {
5805 g_debug ("arranger focus out");
5806
5807 self->alt_held = 0;
5808 self->ctrl_held = 0;
5809 self->shift_held = 0;
5810
5811 return FALSE;
5812 }
5813
5814 static gboolean
on_grab_broken(GtkWidget * widget,GdkEvent * event,gpointer user_data)5815 on_grab_broken (GtkWidget *widget,
5816 GdkEvent *event,
5817 gpointer user_data)
5818 {
5819 /*GdkEventGrabBroken * ev =*/
5820 /*(GdkEventGrabBroken *) event;*/
5821 g_message ("arranger grab broken");
5822 return FALSE;
5823 }
5824
5825 /**
5826 * Wrapper for ui_px_to_pos depending on the
5827 * arranger type.
5828 */
5829 void
arranger_widget_px_to_pos(ArrangerWidget * self,double px,Position * pos,bool has_padding)5830 arranger_widget_px_to_pos (
5831 ArrangerWidget * self,
5832 double px,
5833 Position * pos,
5834 bool has_padding)
5835 {
5836 if (self->type == TYPE (TIMELINE))
5837 {
5838 ui_px_to_pos_timeline (
5839 px, pos, has_padding);
5840 }
5841 else
5842 {
5843 ui_px_to_pos_editor (
5844 px, pos, has_padding);
5845 }
5846 }
5847
5848 static ArrangerCursor
get_audio_arranger_cursor(ArrangerWidget * self,Tool tool)5849 get_audio_arranger_cursor (
5850 ArrangerWidget * self,
5851 Tool tool)
5852 {
5853 ArrangerCursor ac = ARRANGER_CURSOR_SELECT;
5854 UiOverlayAction action = self->action;
5855
5856 switch (action)
5857 {
5858 case UI_OVERLAY_ACTION_NONE:
5859 if (P_TOOL == TOOL_SELECT_NORMAL ||
5860 P_TOOL == TOOL_SELECT_STRETCH)
5861 {
5862 /* gain line */
5863 if (audio_arranger_widget_is_cursor_gain (
5864 self, self->hover_x, self->hover_y))
5865 return ARRANGER_CURSOR_RESIZING_UP;
5866
5867 if (!is_cursor_in_top_half (
5868 self, self->hover_y))
5869 {
5870 /* set cursor to range selection */
5871 return ARRANGER_CURSOR_RANGE;
5872 }
5873
5874 /* resize fade in */
5875 /* note cursor is opposite */
5876 if (audio_arranger_widget_is_cursor_in_fade (
5877 self, self->hover_x, self->hover_y,
5878 true, true))
5879 {
5880 return ARRANGER_CURSOR_RESIZING_R_FADE;
5881 }
5882 /* resize fade out */
5883 if (audio_arranger_widget_is_cursor_in_fade (
5884 self, self->hover_x, self->hover_y,
5885 false, true))
5886 {
5887 return ARRANGER_CURSOR_RESIZING_L_FADE;
5888 }
5889 /* fade in curviness */
5890 if (audio_arranger_widget_is_cursor_in_fade (
5891 self, self->hover_x, self->hover_y,
5892 true, false))
5893 {
5894 return ARRANGER_CURSOR_RESIZING_UP_FADE_IN;
5895 }
5896 /* fade out curviness */
5897 if (audio_arranger_widget_is_cursor_in_fade (
5898 self, self->hover_x, self->hover_y,
5899 false, false))
5900 {
5901 return ARRANGER_CURSOR_RESIZING_UP_FADE_OUT;
5902 }
5903
5904 /* set cursor to normal */
5905 return ARRANGER_CURSOR_SELECT;
5906 }
5907 else if (P_TOOL == TOOL_EDIT)
5908 ac = ARRANGER_CURSOR_EDIT;
5909 else if (P_TOOL == TOOL_ERASER)
5910 ac = ARRANGER_CURSOR_ERASER;
5911 else if (P_TOOL == TOOL_RAMP)
5912 ac = ARRANGER_CURSOR_RAMP;
5913 else if (P_TOOL == TOOL_AUDITION)
5914 ac = ARRANGER_CURSOR_AUDITION;
5915 break;
5916 case UI_OVERLAY_ACTION_STARTING_DELETE_SELECTION:
5917 case UI_OVERLAY_ACTION_DELETE_SELECTING:
5918 case UI_OVERLAY_ACTION_STARTING_ERASING:
5919 case UI_OVERLAY_ACTION_ERASING:
5920 ac = ARRANGER_CURSOR_ERASER;
5921 break;
5922 case UI_OVERLAY_ACTION_STARTING_MOVING_COPY:
5923 case UI_OVERLAY_ACTION_MOVING_COPY:
5924 ac = ARRANGER_CURSOR_GRABBING_COPY;
5925 break;
5926 case UI_OVERLAY_ACTION_STARTING_MOVING:
5927 case UI_OVERLAY_ACTION_MOVING:
5928 ac = ARRANGER_CURSOR_GRABBING;
5929 break;
5930 case UI_OVERLAY_ACTION_STARTING_MOVING_LINK:
5931 case UI_OVERLAY_ACTION_MOVING_LINK:
5932 ac = ARRANGER_CURSOR_GRABBING_LINK;
5933 break;
5934 case UI_OVERLAY_ACTION_RESIZING_UP:
5935 ac = ARRANGER_CURSOR_RESIZING_UP;
5936 break;
5937 case UI_OVERLAY_ACTION_RESIZING_UP_FADE_IN:
5938 ac = ARRANGER_CURSOR_RESIZING_UP_FADE_IN;
5939 break;
5940 case UI_OVERLAY_ACTION_RESIZING_UP_FADE_OUT:
5941 ac = ARRANGER_CURSOR_RESIZING_UP_FADE_OUT;
5942 break;
5943 case UI_OVERLAY_ACTION_RESIZING_L:
5944 ac = ARRANGER_CURSOR_RESIZING_L;
5945 break;
5946 case UI_OVERLAY_ACTION_RESIZING_L_FADE:
5947 ac = ARRANGER_CURSOR_RESIZING_L_FADE;
5948 break;
5949 case UI_OVERLAY_ACTION_RESIZING_R:
5950 ac = ARRANGER_CURSOR_RESIZING_R;
5951 break;
5952 case UI_OVERLAY_ACTION_RESIZING_R_FADE:
5953 ac = ARRANGER_CURSOR_RESIZING_R_FADE;
5954 break;
5955 default:
5956 ac = ARRANGER_CURSOR_SELECT;
5957 break;
5958 }
5959
5960 return ac;
5961 }
5962
5963 static ArrangerCursor
get_midi_modifier_arranger_cursor(ArrangerWidget * self,Tool tool)5964 get_midi_modifier_arranger_cursor (
5965 ArrangerWidget * self,
5966 Tool tool)
5967 {
5968 ArrangerCursor ac = ARRANGER_CURSOR_SELECT;
5969 UiOverlayAction action =
5970 self->action;
5971
5972 switch (action)
5973 {
5974 case UI_OVERLAY_ACTION_NONE:
5975 if (tool == TOOL_SELECT_NORMAL ||
5976 tool == TOOL_SELECT_STRETCH)
5977 {
5978 ArrangerObject * vel_obj =
5979 arranger_widget_get_hit_arranger_object (
5980 (ArrangerWidget *) self,
5981 ARRANGER_OBJECT_TYPE_VELOCITY,
5982 self->hover_x, self->hover_y);
5983 int is_hit = vel_obj != NULL;
5984
5985 if (is_hit)
5986 {
5987 int is_resize =
5988 arranger_object_is_resize_up (
5989 vel_obj,
5990 (int) self->hover_x -
5991 vel_obj->full_rect.x,
5992 (int) self->hover_y -
5993 vel_obj->full_rect.y);
5994 if (is_resize)
5995 {
5996 return
5997 ARRANGER_CURSOR_RESIZING_UP;
5998 }
5999 }
6000
6001 return ARRANGER_CURSOR_SELECT;
6002 }
6003 else if (P_TOOL == TOOL_EDIT)
6004 ac = ARRANGER_CURSOR_EDIT;
6005 else if (P_TOOL == TOOL_ERASER)
6006 ac = ARRANGER_CURSOR_ERASER;
6007 else if (P_TOOL == TOOL_RAMP)
6008 ac = ARRANGER_CURSOR_RAMP;
6009 else if (P_TOOL == TOOL_AUDITION)
6010 ac = ARRANGER_CURSOR_AUDITION;
6011 break;
6012 case UI_OVERLAY_ACTION_STARTING_DELETE_SELECTION:
6013 case UI_OVERLAY_ACTION_DELETE_SELECTING:
6014 case UI_OVERLAY_ACTION_STARTING_ERASING:
6015 case UI_OVERLAY_ACTION_ERASING:
6016 ac = ARRANGER_CURSOR_ERASER;
6017 break;
6018 case UI_OVERLAY_ACTION_STARTING_MOVING_COPY:
6019 case UI_OVERLAY_ACTION_MOVING_COPY:
6020 ac = ARRANGER_CURSOR_GRABBING_COPY;
6021 break;
6022 case UI_OVERLAY_ACTION_STARTING_MOVING:
6023 case UI_OVERLAY_ACTION_MOVING:
6024 ac = ARRANGER_CURSOR_GRABBING;
6025 break;
6026 case UI_OVERLAY_ACTION_STARTING_MOVING_LINK:
6027 case UI_OVERLAY_ACTION_MOVING_LINK:
6028 ac = ARRANGER_CURSOR_GRABBING_LINK;
6029 break;
6030 case UI_OVERLAY_ACTION_RESIZING_L:
6031 ac = ARRANGER_CURSOR_RESIZING_L;
6032 break;
6033 case UI_OVERLAY_ACTION_RESIZING_R:
6034 ac = ARRANGER_CURSOR_RESIZING_R;
6035 break;
6036 case UI_OVERLAY_ACTION_RESIZING_UP:
6037 ac = ARRANGER_CURSOR_RESIZING_UP;
6038 break;
6039 case UI_OVERLAY_ACTION_STARTING_SELECTION:
6040 case UI_OVERLAY_ACTION_SELECTING:
6041 ac = ARRANGER_CURSOR_SELECT;
6042 /* TODO depends on tool */
6043 break;
6044 case UI_OVERLAY_ACTION_STARTING_RAMP:
6045 case UI_OVERLAY_ACTION_RAMPING:
6046 ac = ARRANGER_CURSOR_RAMP;
6047 break;
6048 /* editing */
6049 case UI_OVERLAY_ACTION_AUTOFILLING:
6050 ac = ARRANGER_CURSOR_EDIT;
6051 break;
6052 default:
6053 g_warn_if_reached ();
6054 ac = ARRANGER_CURSOR_SELECT;
6055 break;
6056 }
6057
6058 return ac;
6059
6060 }
6061
6062 static ArrangerCursor
get_chord_arranger_cursor(ArrangerWidget * self,Tool tool)6063 get_chord_arranger_cursor (
6064 ArrangerWidget * self,
6065 Tool tool)
6066 {
6067 ArrangerCursor ac = ARRANGER_CURSOR_SELECT;
6068 UiOverlayAction action =
6069 self->action;
6070
6071 int is_hit =
6072 arranger_widget_get_hit_arranger_object (
6073 (ArrangerWidget *) self,
6074 ARRANGER_OBJECT_TYPE_CHORD_OBJECT,
6075 self->hover_x, self->hover_y) != NULL;
6076
6077 switch (action)
6078 {
6079 case UI_OVERLAY_ACTION_NONE:
6080 switch (P_TOOL)
6081 {
6082 case TOOL_SELECT_NORMAL:
6083 {
6084 if (is_hit)
6085 {
6086 return ARRANGER_CURSOR_GRAB;
6087 }
6088 else
6089 {
6090 /* set cursor to normal */
6091 return ARRANGER_CURSOR_SELECT;
6092 }
6093 }
6094 break;
6095 case TOOL_SELECT_STRETCH:
6096 break;
6097 case TOOL_EDIT:
6098 ac = ARRANGER_CURSOR_EDIT;
6099 break;
6100 case TOOL_CUT:
6101 ac = ARRANGER_CURSOR_CUT;
6102 break;
6103 case TOOL_ERASER:
6104 ac = ARRANGER_CURSOR_ERASER;
6105 break;
6106 case TOOL_RAMP:
6107 ac = ARRANGER_CURSOR_RAMP;
6108 break;
6109 case TOOL_AUDITION:
6110 ac = ARRANGER_CURSOR_AUDITION;
6111 break;
6112 }
6113 break;
6114 case UI_OVERLAY_ACTION_STARTING_DELETE_SELECTION:
6115 case UI_OVERLAY_ACTION_DELETE_SELECTING:
6116 case UI_OVERLAY_ACTION_STARTING_ERASING:
6117 case UI_OVERLAY_ACTION_ERASING:
6118 ac = ARRANGER_CURSOR_ERASER;
6119 break;
6120 case UI_OVERLAY_ACTION_STARTING_MOVING_COPY:
6121 case UI_OVERLAY_ACTION_MOVING_COPY:
6122 ac = ARRANGER_CURSOR_GRABBING_COPY;
6123 break;
6124 case UI_OVERLAY_ACTION_STARTING_MOVING:
6125 case UI_OVERLAY_ACTION_MOVING:
6126 ac = ARRANGER_CURSOR_GRABBING;
6127 break;
6128 case UI_OVERLAY_ACTION_STARTING_MOVING_LINK:
6129 case UI_OVERLAY_ACTION_MOVING_LINK:
6130 ac = ARRANGER_CURSOR_GRABBING_LINK;
6131 break;
6132 case UI_OVERLAY_ACTION_RESIZING_L:
6133 ac = ARRANGER_CURSOR_RESIZING_L;
6134 break;
6135 case UI_OVERLAY_ACTION_RESIZING_R:
6136 ac = ARRANGER_CURSOR_RESIZING_R;
6137 break;
6138 default:
6139 ac = ARRANGER_CURSOR_SELECT;
6140 break;
6141 }
6142
6143 return ac;
6144 }
6145
6146 static ArrangerCursor
get_automation_arranger_cursor(ArrangerWidget * self,Tool tool)6147 get_automation_arranger_cursor (
6148 ArrangerWidget * self,
6149 Tool tool)
6150 {
6151 ArrangerCursor ac = ARRANGER_CURSOR_SELECT;
6152 UiOverlayAction action =
6153 self->action;
6154
6155 int is_hit =
6156 arranger_widget_get_hit_arranger_object (
6157 (ArrangerWidget *) self,
6158 ARRANGER_OBJECT_TYPE_AUTOMATION_POINT,
6159 self->hover_x, self->hover_y) != NULL;
6160
6161 switch (action)
6162 {
6163 case UI_OVERLAY_ACTION_NONE:
6164 switch (P_TOOL)
6165 {
6166 case TOOL_SELECT_NORMAL:
6167 {
6168 if (is_hit)
6169 {
6170 return ARRANGER_CURSOR_GRAB;
6171 }
6172 else
6173 {
6174 /* set cursor to normal */
6175 return ARRANGER_CURSOR_SELECT;
6176 }
6177 }
6178 break;
6179 case TOOL_SELECT_STRETCH:
6180 break;
6181 case TOOL_EDIT:
6182 ac = ARRANGER_CURSOR_EDIT;
6183 break;
6184 case TOOL_CUT:
6185 ac = ARRANGER_CURSOR_CUT;
6186 break;
6187 case TOOL_ERASER:
6188 ac = ARRANGER_CURSOR_ERASER;
6189 break;
6190 case TOOL_RAMP:
6191 ac = ARRANGER_CURSOR_RAMP;
6192 break;
6193 case TOOL_AUDITION:
6194 ac = ARRANGER_CURSOR_AUDITION;
6195 break;
6196 }
6197 break;
6198 case UI_OVERLAY_ACTION_STARTING_DELETE_SELECTION:
6199 case UI_OVERLAY_ACTION_DELETE_SELECTING:
6200 case UI_OVERLAY_ACTION_STARTING_ERASING:
6201 case UI_OVERLAY_ACTION_ERASING:
6202 ac = ARRANGER_CURSOR_ERASER;
6203 break;
6204 case UI_OVERLAY_ACTION_STARTING_MOVING_COPY:
6205 case UI_OVERLAY_ACTION_MOVING_COPY:
6206 ac = ARRANGER_CURSOR_GRABBING_COPY;
6207 break;
6208 case UI_OVERLAY_ACTION_STARTING_MOVING:
6209 case UI_OVERLAY_ACTION_MOVING:
6210 ac = ARRANGER_CURSOR_GRABBING;
6211 break;
6212 case UI_OVERLAY_ACTION_STARTING_MOVING_LINK:
6213 case UI_OVERLAY_ACTION_MOVING_LINK:
6214 ac = ARRANGER_CURSOR_GRABBING_LINK;
6215 break;
6216 case UI_OVERLAY_ACTION_RESIZING_L:
6217 ac = ARRANGER_CURSOR_RESIZING_L;
6218 break;
6219 case UI_OVERLAY_ACTION_RESIZING_R:
6220 ac = ARRANGER_CURSOR_RESIZING_R;
6221 break;
6222 case UI_OVERLAY_ACTION_RESIZING_UP:
6223 ac = ARRANGER_CURSOR_GRABBING;
6224 break;
6225 case UI_OVERLAY_ACTION_AUTOFILLING:
6226 ac = ARRANGER_CURSOR_AUTOFILL;
6227 break;
6228 case UI_OVERLAY_ACTION_STARTING_SELECTION:
6229 case UI_OVERLAY_ACTION_SELECTING:
6230 case UI_OVERLAY_ACTION_CREATING_MOVING:
6231 ac = ARRANGER_CURSOR_SELECT;
6232 break;
6233 default:
6234 g_warn_if_reached ();
6235 ac = ARRANGER_CURSOR_SELECT;
6236 break;
6237 }
6238
6239 return ac;
6240 }
6241
6242 static ArrangerCursor
get_timeline_cursor(ArrangerWidget * self,Tool tool)6243 get_timeline_cursor (
6244 ArrangerWidget * self,
6245 Tool tool)
6246 {
6247 ArrangerCursor ac = ARRANGER_CURSOR_SELECT;
6248 UiOverlayAction action =
6249 self->action;
6250
6251 ArrangerObject * r_obj =
6252 arranger_widget_get_hit_arranger_object (
6253 (ArrangerWidget *) self,
6254 ARRANGER_OBJECT_TYPE_REGION,
6255 self->hover_x, self->hover_y);
6256 ArrangerObject * s_obj =
6257 arranger_widget_get_hit_arranger_object (
6258 (ArrangerWidget *) self,
6259 ARRANGER_OBJECT_TYPE_SCALE_OBJECT,
6260 self->hover_x, self->hover_y);
6261 ArrangerObject * m_obj =
6262 arranger_widget_get_hit_arranger_object (
6263 (ArrangerWidget *) self,
6264 ARRANGER_OBJECT_TYPE_MARKER,
6265 self->hover_x, self->hover_y);
6266
6267 if (r_obj && arranger_object_is_frozen (r_obj))
6268 {
6269 r_obj = NULL;
6270 }
6271 if (s_obj && arranger_object_is_frozen (s_obj))
6272 {
6273 s_obj = NULL;
6274 }
6275 if (m_obj && arranger_object_is_frozen (m_obj))
6276 {
6277 m_obj = NULL;
6278 }
6279 int is_hit = r_obj || s_obj || m_obj;
6280
6281 switch (action)
6282 {
6283 case UI_OVERLAY_ACTION_NONE:
6284 switch (P_TOOL)
6285 {
6286 case TOOL_SELECT_NORMAL:
6287 case TOOL_SELECT_STRETCH:
6288 {
6289 if (is_hit)
6290 {
6291 if (r_obj)
6292 {
6293 if (self->alt_held)
6294 return ARRANGER_CURSOR_CUT;
6295 int wx =
6296 (int) self->hover_x -
6297 r_obj->full_rect.x;
6298 int wy =
6299 (int) self->hover_y -
6300 r_obj->full_rect.y;
6301 int is_fade_in_point =
6302 arranger_object_is_fade_in (
6303 r_obj, wx, wy, 1, 0);
6304 int is_fade_out_point =
6305 arranger_object_is_fade_out (
6306 r_obj, wx, wy, 1, 0);
6307 int is_fade_in_outer_region =
6308 arranger_object_is_fade_in (
6309 r_obj, wx, wy, 0, 1);
6310 int is_fade_out_outer_region =
6311 arranger_object_is_fade_out (
6312 r_obj, wx, wy, 0, 1);
6313 int is_resize_l =
6314 arranger_object_is_resize_l (
6315 r_obj, wx);
6316 int is_resize_r =
6317 arranger_object_is_resize_r (
6318 r_obj, wx);
6319 int is_resize_loop =
6320 arranger_object_is_resize_loop (
6321 r_obj, wy);
6322 bool is_rename =
6323 arranger_object_is_rename (
6324 r_obj, wx, wy);
6325 if (is_fade_in_point)
6326 return
6327 ARRANGER_CURSOR_FADE_IN;
6328 else if (is_fade_out_point)
6329 return
6330 ARRANGER_CURSOR_FADE_OUT;
6331 else if (is_resize_l &&
6332 is_resize_loop)
6333 {
6334 return
6335 ARRANGER_CURSOR_RESIZING_L_LOOP;
6336 }
6337 else if (is_resize_l)
6338 {
6339 if (P_TOOL ==
6340 TOOL_SELECT_NORMAL)
6341 return
6342 ARRANGER_CURSOR_RESIZING_L;
6343 else if (P_TOOL ==
6344 TOOL_SELECT_STRETCH)
6345 {
6346 return
6347 ARRANGER_CURSOR_STRETCHING_L;
6348 }
6349 }
6350 else if (is_resize_r &&
6351 is_resize_loop)
6352 return
6353 ARRANGER_CURSOR_RESIZING_R_LOOP;
6354 else if (is_resize_r)
6355 {
6356 if (P_TOOL ==
6357 TOOL_SELECT_NORMAL)
6358 return
6359 ARRANGER_CURSOR_RESIZING_R;
6360 else if (P_TOOL ==
6361 TOOL_SELECT_STRETCH)
6362 return
6363 ARRANGER_CURSOR_STRETCHING_R;
6364 }
6365 else if (is_fade_in_outer_region)
6366 return
6367 ARRANGER_CURSOR_FADE_IN;
6368 else if (is_fade_out_outer_region)
6369 return
6370 ARRANGER_CURSOR_FADE_OUT;
6371 else if (is_rename)
6372 return ARRANGER_CURSOR_RENAME;
6373 }
6374 return ARRANGER_CURSOR_GRAB;
6375 }
6376 else
6377 {
6378 Track * track =
6379 timeline_arranger_widget_get_track_at_y (
6380 self, self->hover_y);
6381
6382 if (track)
6383 {
6384 if (track_widget_is_cursor_in_range_select_half (
6385 track->widget,
6386 self->hover_y))
6387 {
6388 /* set cursor to range
6389 * selection */
6390 return ARRANGER_CURSOR_RANGE;
6391 }
6392 else
6393 {
6394 /* set cursor to normal */
6395 return ARRANGER_CURSOR_SELECT;
6396 }
6397 }
6398 else
6399 {
6400 /* set cursor to normal */
6401 return ARRANGER_CURSOR_SELECT;
6402 }
6403 }
6404 }
6405 break;
6406 case TOOL_EDIT:
6407 ac = ARRANGER_CURSOR_EDIT;
6408 break;
6409 case TOOL_CUT:
6410 ac = ARRANGER_CURSOR_CUT;
6411 break;
6412 case TOOL_ERASER:
6413 ac = ARRANGER_CURSOR_ERASER;
6414 break;
6415 case TOOL_RAMP:
6416 ac = ARRANGER_CURSOR_RAMP;
6417 break;
6418 case TOOL_AUDITION:
6419 ac = ARRANGER_CURSOR_AUDITION;
6420 break;
6421 }
6422 break;
6423 case UI_OVERLAY_ACTION_STARTING_DELETE_SELECTION:
6424 case UI_OVERLAY_ACTION_DELETE_SELECTING:
6425 case UI_OVERLAY_ACTION_STARTING_ERASING:
6426 case UI_OVERLAY_ACTION_ERASING:
6427 ac = ARRANGER_CURSOR_ERASER;
6428 break;
6429 case UI_OVERLAY_ACTION_STARTING_MOVING_COPY:
6430 case UI_OVERLAY_ACTION_MOVING_COPY:
6431 ac = ARRANGER_CURSOR_GRABBING_COPY;
6432 break;
6433 case UI_OVERLAY_ACTION_STARTING_MOVING_LINK:
6434 case UI_OVERLAY_ACTION_MOVING_LINK:
6435 ac = ARRANGER_CURSOR_GRABBING_LINK;
6436 break;
6437 case UI_OVERLAY_ACTION_STARTING_MOVING:
6438 case UI_OVERLAY_ACTION_CREATING_MOVING:
6439 case UI_OVERLAY_ACTION_MOVING:
6440 ac = ARRANGER_CURSOR_GRABBING;
6441 break;
6442 case UI_OVERLAY_ACTION_STRETCHING_L:
6443 ac = ARRANGER_CURSOR_STRETCHING_L;
6444 break;
6445 case UI_OVERLAY_ACTION_RESIZING_L:
6446 if (self->resizing_range)
6447 ac = ARRANGER_CURSOR_RANGE;
6448 else
6449 ac = ARRANGER_CURSOR_RESIZING_L;
6450 break;
6451 case UI_OVERLAY_ACTION_RESIZING_L_LOOP:
6452 ac = ARRANGER_CURSOR_RESIZING_L_LOOP;
6453 break;
6454 case UI_OVERLAY_ACTION_RESIZING_L_FADE:
6455 ac = ARRANGER_CURSOR_FADE_IN;
6456 break;
6457 case UI_OVERLAY_ACTION_STRETCHING_R:
6458 ac = ARRANGER_CURSOR_STRETCHING_R;
6459 break;
6460 case UI_OVERLAY_ACTION_CREATING_RESIZING_R:
6461 case UI_OVERLAY_ACTION_RESIZING_R:
6462 if (self->resizing_range)
6463 ac = ARRANGER_CURSOR_RANGE;
6464 else
6465 ac = ARRANGER_CURSOR_RESIZING_R;
6466 break;
6467 case UI_OVERLAY_ACTION_RESIZING_R_LOOP:
6468 ac = ARRANGER_CURSOR_RESIZING_R_LOOP;
6469 break;
6470 case UI_OVERLAY_ACTION_RESIZING_R_FADE:
6471 ac = ARRANGER_CURSOR_FADE_OUT;
6472 break;
6473 case UI_OVERLAY_ACTION_RESIZING_UP_FADE_IN:
6474 ac = ARRANGER_CURSOR_FADE_IN;
6475 break;
6476 case UI_OVERLAY_ACTION_RESIZING_UP_FADE_OUT:
6477 ac = ARRANGER_CURSOR_FADE_OUT;
6478 break;
6479 case UI_OVERLAY_ACTION_AUTOFILLING:
6480 ac = ARRANGER_CURSOR_AUTOFILL;
6481 break;
6482 case UI_OVERLAY_ACTION_STARTING_SELECTION:
6483 case UI_OVERLAY_ACTION_SELECTING:
6484 ac = ARRANGER_CURSOR_SELECT;
6485 break;
6486 case UI_OVERLAY_ACTION_RENAMING:
6487 ac = ARRANGER_CURSOR_RENAME;
6488 break;
6489 case UI_OVERLAY_ACTION_CUTTING:
6490 ac = ARRANGER_CURSOR_CUT;
6491 break;
6492 default:
6493 g_warn_if_reached ();
6494 ac = ARRANGER_CURSOR_SELECT;
6495 break;
6496 }
6497
6498 return ac;
6499 }
6500
6501 static ArrangerCursor
get_midi_arranger_cursor(ArrangerWidget * self,Tool tool)6502 get_midi_arranger_cursor (
6503 ArrangerWidget * self,
6504 Tool tool)
6505 {
6506 ArrangerCursor ac = ARRANGER_CURSOR_SELECT;
6507 UiOverlayAction action =
6508 self->action;
6509
6510 ArrangerObject * obj =
6511 arranger_widget_get_hit_arranger_object (
6512 (ArrangerWidget *) self,
6513 ARRANGER_OBJECT_TYPE_MIDI_NOTE,
6514 self->hover_x, self->hover_y);
6515 int is_hit = obj != NULL;
6516
6517 bool drum_mode =
6518 arranger_widget_get_drum_mode_enabled (self);
6519
6520 switch (action)
6521 {
6522 case UI_OVERLAY_ACTION_NONE:
6523 if (tool == TOOL_SELECT_NORMAL ||
6524 tool == TOOL_SELECT_STRETCH ||
6525 tool == TOOL_EDIT)
6526 {
6527 int is_resize_l = 0, is_resize_r = 0;
6528
6529 if (is_hit)
6530 {
6531 is_resize_l =
6532 arranger_object_is_resize_l (
6533 obj,
6534 (int) self->hover_x -
6535 obj->full_rect.x);
6536 is_resize_r =
6537 arranger_object_is_resize_r (
6538 obj,
6539 (int) self->hover_x -
6540 obj->full_rect.x);
6541 }
6542
6543 if (is_hit && is_resize_l
6544 && !drum_mode)
6545 {
6546 return ARRANGER_CURSOR_RESIZING_L;
6547 }
6548 else if (is_hit && is_resize_r
6549 && !drum_mode)
6550 {
6551 return ARRANGER_CURSOR_RESIZING_R;
6552 }
6553 else if (is_hit)
6554 {
6555 return ARRANGER_CURSOR_GRAB;
6556 }
6557 else
6558 {
6559 /* set cursor to whatever it is */
6560 if (tool == TOOL_EDIT)
6561 return ARRANGER_CURSOR_EDIT;
6562 else
6563 return ARRANGER_CURSOR_SELECT;
6564 }
6565 }
6566 else if (P_TOOL == TOOL_EDIT)
6567 ac = ARRANGER_CURSOR_EDIT;
6568 else if (P_TOOL == TOOL_ERASER)
6569 ac = ARRANGER_CURSOR_ERASER;
6570 else if (P_TOOL == TOOL_RAMP)
6571 ac = ARRANGER_CURSOR_RAMP;
6572 else if (P_TOOL == TOOL_AUDITION)
6573 ac = ARRANGER_CURSOR_AUDITION;
6574 break;
6575 case UI_OVERLAY_ACTION_STARTING_DELETE_SELECTION:
6576 case UI_OVERLAY_ACTION_DELETE_SELECTING:
6577 case UI_OVERLAY_ACTION_STARTING_ERASING:
6578 case UI_OVERLAY_ACTION_ERASING:
6579 ac = ARRANGER_CURSOR_ERASER;
6580 break;
6581 case UI_OVERLAY_ACTION_STARTING_MOVING_COPY:
6582 case UI_OVERLAY_ACTION_MOVING_COPY:
6583 ac = ARRANGER_CURSOR_GRABBING_COPY;
6584 break;
6585 case UI_OVERLAY_ACTION_STARTING_MOVING:
6586 case UI_OVERLAY_ACTION_MOVING:
6587 ac = ARRANGER_CURSOR_GRABBING;
6588 break;
6589 case UI_OVERLAY_ACTION_STARTING_MOVING_LINK:
6590 case UI_OVERLAY_ACTION_MOVING_LINK:
6591 ac = ARRANGER_CURSOR_GRABBING_LINK;
6592 break;
6593 case UI_OVERLAY_ACTION_RESIZING_L:
6594 ac = ARRANGER_CURSOR_RESIZING_L;
6595 break;
6596 case UI_OVERLAY_ACTION_RESIZING_R:
6597 case UI_OVERLAY_ACTION_CREATING_RESIZING_R:
6598 ac = ARRANGER_CURSOR_RESIZING_R;
6599 break;
6600 case UI_OVERLAY_ACTION_STARTING_SELECTION:
6601 case UI_OVERLAY_ACTION_SELECTING:
6602 ac = ARRANGER_CURSOR_SELECT;
6603 /* TODO depends on tool */
6604 break;
6605 case UI_OVERLAY_ACTION_AUTOFILLING:
6606 ac = ARRANGER_CURSOR_AUTOFILL;
6607 break;
6608 default:
6609 g_warn_if_reached ();
6610 ac = ARRANGER_CURSOR_SELECT;
6611 break;
6612 }
6613
6614 return ac;
6615 }
6616
6617 /**
6618 * Gets the cursor based on the current hover
6619 * position.
6620 */
6621 ArrangerCursor
arranger_widget_get_cursor(ArrangerWidget * self)6622 arranger_widget_get_cursor (
6623 ArrangerWidget * self)
6624 {
6625 ArrangerCursor ac = ARRANGER_CURSOR_SELECT;
6626
6627 switch (self->type)
6628 {
6629 case TYPE (TIMELINE):
6630 ac =
6631 get_timeline_cursor (self, P_TOOL);
6632 break;
6633 case TYPE (AUDIO):
6634 ac = get_audio_arranger_cursor (self, P_TOOL);
6635 break;
6636 case TYPE (CHORD):
6637 ac = get_chord_arranger_cursor (self, P_TOOL);
6638 break;
6639 case TYPE (MIDI):
6640 ac = get_midi_arranger_cursor (self, P_TOOL);
6641 break;
6642 case TYPE (MIDI_MODIFIER):
6643 ac =
6644 get_midi_modifier_arranger_cursor (
6645 self, P_TOOL);
6646 break;
6647 case TYPE (AUTOMATION):
6648 ac =
6649 get_automation_arranger_cursor (
6650 self, P_TOOL);
6651 break;
6652 default:
6653 break;
6654 }
6655
6656 return ac;
6657 }
6658
6659 /**
6660 * Figures out which cursor should be used based
6661 * on the current state and then sets it.
6662 */
6663 void
arranger_widget_refresh_cursor(ArrangerWidget * self)6664 arranger_widget_refresh_cursor (
6665 ArrangerWidget * self)
6666 {
6667 if (!gtk_widget_get_realized (
6668 GTK_WIDGET (self)))
6669 return;
6670
6671 ArrangerCursor ac =
6672 arranger_widget_get_cursor (self);
6673
6674 arranger_widget_set_cursor (self, ac);
6675 }
6676
6677 /**
6678 * Toggles the mute status of the selection, based
6679 * on the mute status of the selected object.
6680 *
6681 * This creates an undoable action and executes it.
6682 */
6683 void
arranger_widget_toggle_selections_muted(ArrangerWidget * self,ArrangerObject * clicked_object)6684 arranger_widget_toggle_selections_muted (
6685 ArrangerWidget * self,
6686 ArrangerObject * clicked_object)
6687 {
6688 g_return_if_fail (
6689 arranger_object_can_mute (clicked_object));
6690
6691 GAction * action =
6692 g_action_map_lookup_action (
6693 G_ACTION_MAP (MAIN_WINDOW),
6694 "mute-selection");
6695 GVariant * var =
6696 g_variant_new_string ("timeline");
6697 g_action_activate (action, var);
6698 g_free (var);
6699 }
6700
6701 /**
6702 * Scroll until the given object is visible.
6703 *
6704 * @param horizontal 1 for horizontal, 2 for
6705 * vertical.
6706 * @param up Whether scrolling up or down.
6707 * @param padding Padding pixels.
6708 */
6709 void
arranger_widget_scroll_until_obj(ArrangerWidget * self,ArrangerObject * obj,int horizontal,int up,int left,double padding)6710 arranger_widget_scroll_until_obj (
6711 ArrangerWidget * self,
6712 ArrangerObject * obj,
6713 int horizontal,
6714 int up,
6715 int left,
6716 double padding)
6717 {
6718 GtkScrolledWindow *scroll =
6719 arranger_widget_get_scrolled_window (self);
6720 int scroll_width =
6721 gtk_widget_get_allocated_width (
6722 GTK_WIDGET (scroll));
6723 int scroll_height =
6724 gtk_widget_get_allocated_height (
6725 GTK_WIDGET (scroll));
6726 GtkAdjustment *hadj =
6727 gtk_scrolled_window_get_hadjustment (
6728 GTK_SCROLLED_WINDOW (scroll));
6729 GtkAdjustment *vadj =
6730 gtk_scrolled_window_get_vadjustment (
6731 GTK_SCROLLED_WINDOW (scroll));
6732 double adj_x =
6733 gtk_adjustment_get_value (hadj);
6734 double adj_y =
6735 gtk_adjustment_get_value (vadj);
6736
6737 if (horizontal)
6738 {
6739 double start_px =
6740 (double)
6741 arranger_widget_pos_to_px (
6742 self, &obj->pos, 1);
6743 double end_px =
6744 (double)
6745 arranger_widget_pos_to_px (
6746 self, &obj->end_pos, 1);
6747
6748 /* adjust px for objects with non-global
6749 * positions */
6750 if (!arranger_object_type_has_global_pos (
6751 obj->type))
6752 {
6753 ArrangerObject * r_obj =
6754 (ArrangerObject *)
6755 clip_editor_get_region (CLIP_EDITOR);
6756 g_return_if_fail (r_obj);
6757 double tmp_px =
6758 (double)
6759 arranger_widget_pos_to_px (
6760 self, &r_obj->pos, 1);
6761 start_px += tmp_px;
6762 end_px += tmp_px;
6763 }
6764
6765 if (start_px <= adj_x ||
6766 end_px >= adj_x + (double) scroll_width)
6767 {
6768 if (left)
6769 {
6770 gtk_adjustment_set_value (
6771 hadj, start_px - padding);
6772 }
6773 else
6774 {
6775 double tmp =
6776 (end_px + padding) -
6777 (double) scroll_width;
6778 gtk_adjustment_set_value (hadj, tmp);
6779 }
6780 }
6781 }
6782 else
6783 {
6784 arranger_object_set_full_rectangle (
6785 obj, self);
6786 double start_px = obj->full_rect.y;
6787 double end_px =
6788 obj->full_rect.y + obj->full_rect.height;
6789 if (start_px <= adj_y ||
6790 end_px >= adj_y + (double) scroll_height)
6791 {
6792 if (up)
6793 {
6794 gtk_adjustment_set_value (
6795 vadj, start_px - padding);
6796 }
6797 else
6798 {
6799 double tmp =
6800 (end_px + padding) -
6801 (double) scroll_height;
6802 gtk_adjustment_set_value (vadj, tmp);
6803 }
6804 }
6805 }
6806 }
6807
6808 /**
6809 * Returns whether any arranger is in the middle
6810 * of an action.
6811 */
6812 bool
arranger_widget_any_doing_action(void)6813 arranger_widget_any_doing_action (void)
6814 {
6815 #define CHECK_ARRANGER(arranger) \
6816 if (arranger \
6817 && \
6818 arranger->action != UI_OVERLAY_ACTION_NONE) \
6819 return true;
6820
6821 CHECK_ARRANGER (MW_TIMELINE);
6822 CHECK_ARRANGER (MW_PINNED_TIMELINE);
6823 CHECK_ARRANGER (MW_MIDI_ARRANGER);
6824 CHECK_ARRANGER (MW_MIDI_MODIFIER_ARRANGER);
6825 CHECK_ARRANGER (MW_CHORD_ARRANGER);
6826 CHECK_ARRANGER (MW_AUTOMATION_ARRANGER);
6827 CHECK_ARRANGER (MW_AUDIO_ARRANGER);
6828
6829 #undef CHECK_ARRANGER
6830
6831 return false;
6832 }
6833
6834 /**
6835 * Returns the earliest possible position allowed
6836 * in this arranger (eg, 1.1.0.0 for timeline).
6837 */
6838 void
arranger_widget_get_min_possible_position(ArrangerWidget * self,Position * pos)6839 arranger_widget_get_min_possible_position (
6840 ArrangerWidget * self,
6841 Position * pos)
6842 {
6843 switch (self->type)
6844 {
6845 case TYPE (TIMELINE):
6846 position_set_to_bar (pos, 1);
6847 break;
6848 case TYPE (MIDI):
6849 case TYPE (MIDI_MODIFIER):
6850 case TYPE (CHORD):
6851 case TYPE (AUTOMATION):
6852 case TYPE (AUDIO):
6853 {
6854 ZRegion * region =
6855 clip_editor_get_region (CLIP_EDITOR);
6856 g_return_if_fail (region);
6857 position_set_to_pos (
6858 pos, &((ArrangerObject *) region)->pos);
6859 position_change_sign (pos);
6860 }
6861 break;
6862 }
6863 }
6864
6865 static bool
on_arranger_map_event(GtkWidget * widget,GdkEvent * event,ArrangerWidget * self)6866 on_arranger_map_event (
6867 GtkWidget * widget,
6868 GdkEvent * event,
6869 ArrangerWidget * self)
6870 {
6871 return FALSE;
6872 }
6873
6874 void
arranger_widget_setup(ArrangerWidget * self,ArrangerWidgetType type,SnapGrid * snap_grid)6875 arranger_widget_setup (
6876 ArrangerWidget * self,
6877 ArrangerWidgetType type,
6878 SnapGrid * snap_grid)
6879 {
6880 g_debug ("setting up arranger widget...");
6881
6882 g_return_if_fail (
6883 self &&
6884 type >= ARRANGER_WIDGET_TYPE_TIMELINE &&
6885 snap_grid);
6886 self->type = type;
6887 self->snap_grid = snap_grid;
6888
6889 switch (type)
6890 {
6891 case TYPE (TIMELINE):
6892 /* make drag dest */
6893 timeline_arranger_setup_drag_dest (self);
6894 break;
6895 case TYPE (AUTOMATION):
6896 self->ap_layout =
6897 z_cairo_create_pango_layout_from_string (
6898 GTK_WIDGET (self), "8",
6899 PANGO_ELLIPSIZE_NONE, 0);
6900 break;
6901 case TYPE (MIDI_MODIFIER):
6902 self->vel_layout =
6903 z_cairo_create_pango_layout_from_string (
6904 GTK_WIDGET (self), "8",
6905 PANGO_ELLIPSIZE_NONE, 0);
6906 break;
6907 case TYPE (AUDIO):
6908 self->audio_layout =
6909 z_cairo_create_pango_layout_from_string (
6910 GTK_WIDGET (self), "8",
6911 PANGO_ELLIPSIZE_NONE, 0);
6912 default:
6913 break;
6914 }
6915
6916 /* connect signals */
6917 g_signal_connect (
6918 G_OBJECT(self->drag), "drag-begin",
6919 G_CALLBACK (drag_begin), self);
6920 g_signal_connect (
6921 G_OBJECT(self->drag), "drag-update",
6922 G_CALLBACK (drag_update), self);
6923 g_signal_connect (
6924 G_OBJECT(self->drag), "drag-end",
6925 G_CALLBACK (drag_end), self);
6926 g_signal_connect (
6927 G_OBJECT (self->drag), "cancel",
6928 G_CALLBACK (drag_cancel), self);
6929 g_signal_connect (
6930 G_OBJECT (self->multipress), "pressed",
6931 G_CALLBACK (multipress_pressed), self);
6932 g_signal_connect (
6933 G_OBJECT (self->right_mouse_mp), "released",
6934 G_CALLBACK (on_right_click), self);
6935 g_signal_connect (
6936 G_OBJECT (self), "scroll-event",
6937 G_CALLBACK (on_scroll), self);
6938 g_signal_connect (
6939 G_OBJECT (self), "key-press-event",
6940 G_CALLBACK (arranger_widget_on_key_action),
6941 self);
6942 g_signal_connect (
6943 G_OBJECT (self), "key-release-event",
6944 G_CALLBACK (arranger_widget_on_key_release),
6945 self);
6946 g_signal_connect (
6947 G_OBJECT(self), "motion-notify-event",
6948 G_CALLBACK (on_motion), self);
6949 g_signal_connect (
6950 G_OBJECT (self), "leave-notify-event",
6951 G_CALLBACK (on_motion), self);
6952 g_signal_connect (
6953 G_OBJECT (self), "enter-notify-event",
6954 G_CALLBACK (on_motion), self);
6955 g_signal_connect (
6956 G_OBJECT (self), "focus-out-event",
6957 G_CALLBACK (on_focus_out), self);
6958 g_signal_connect (
6959 G_OBJECT (self), "grab-focus",
6960 G_CALLBACK (on_focus), self);
6961 g_signal_connect (
6962 G_OBJECT (self), "grab-broken-event",
6963 G_CALLBACK (on_grab_broken), self);
6964 g_signal_connect (
6965 G_OBJECT (self), "draw",
6966 G_CALLBACK (arranger_draw_cb), self);
6967 g_signal_connect (
6968 G_OBJECT (self), "map-event",
6969 G_CALLBACK (on_arranger_map_event), self);
6970
6971 /*gtk_widget_add_tick_callback (*/
6972 /*GTK_WIDGET (self),*/
6973 /*(GtkTickCallback) arranger_tick_cb,*/
6974 /*self, NULL);*/
6975
6976 gtk_widget_set_focus_on_click (
6977 GTK_WIDGET (self), 1);
6978
6979 g_debug ("done");
6980 }
6981
6982 static void
finalize(ArrangerWidget * self)6983 finalize (
6984 ArrangerWidget * self)
6985 {
6986 #if 0
6987 if (self->draw_thread_pool)
6988 {
6989 g_thread_pool_free (
6990 self->draw_thread_pool, true, false);
6991 }
6992 object_free_w_func_and_null (
6993 object_pool_free, self->draw_task_obj_pool);
6994 #endif
6995
6996 object_free_w_func_and_null (
6997 g_object_unref, self->vel_layout);
6998 object_free_w_func_and_null (
6999 g_object_unref, self->ap_layout);
7000 object_free_w_func_and_null (
7001 g_object_unref, self->audio_layout);
7002
7003 G_OBJECT_CLASS (
7004 arranger_widget_parent_class)->
7005 finalize (G_OBJECT (self));
7006 }
7007
7008 static void
arranger_widget_class_init(ArrangerWidgetClass * _klass)7009 arranger_widget_class_init (
7010 ArrangerWidgetClass * _klass)
7011 {
7012 GObjectClass * oklass =
7013 G_OBJECT_CLASS (_klass);
7014 oklass->finalize =
7015 (GObjectFinalizeFunc) finalize;
7016 }
7017
7018 static void
arranger_widget_init(ArrangerWidget * self)7019 arranger_widget_init (
7020 ArrangerWidget *self)
7021 {
7022 self->first_draw = true;
7023
7024 /* make widget able to notify */
7025 gtk_widget_add_events (
7026 GTK_WIDGET (self),
7027 GDK_ALL_EVENTS_MASK);
7028
7029 /* make widget able to focus */
7030 gtk_widget_set_can_focus (
7031 GTK_WIDGET (self), 1);
7032 gtk_widget_set_focus_on_click (
7033 GTK_WIDGET (self), 1);
7034
7035 self->drag =
7036 GTK_GESTURE_DRAG (
7037 gtk_gesture_drag_new (GTK_WIDGET (self)));
7038 gtk_event_controller_set_propagation_phase (
7039 GTK_EVENT_CONTROLLER (self->drag),
7040 GTK_PHASE_CAPTURE);
7041
7042 /* allow all buttons for drag */
7043 gtk_gesture_single_set_button (
7044 GTK_GESTURE_SINGLE (self->drag), 0);
7045
7046 self->multipress =
7047 GTK_GESTURE_MULTI_PRESS (
7048 gtk_gesture_multi_press_new (
7049 GTK_WIDGET (self)));
7050 gtk_event_controller_set_propagation_phase (
7051 GTK_EVENT_CONTROLLER (self->multipress),
7052 GTK_PHASE_CAPTURE);
7053 self->right_mouse_mp =
7054 GTK_GESTURE_MULTI_PRESS (
7055 gtk_gesture_multi_press_new (
7056 GTK_WIDGET (self)));
7057 gtk_gesture_single_set_button (
7058 GTK_GESTURE_SINGLE (
7059 self->right_mouse_mp),
7060 GDK_BUTTON_SECONDARY);
7061
7062 #if 0
7063 self->draw_task_obj_pool =
7064 object_pool_new (
7065 arranger_draw_task_data_new,
7066 arranger_draw_task_data_free, 8);
7067
7068 GError * err = NULL;
7069 self->draw_thread_pool =
7070 g_thread_pool_new (
7071 arranger_draw_thread_func, self,
7072 8, F_NOT_EXCLUSIVE, &err);
7073 if (!self->draw_thread_pool)
7074 {
7075 HANDLE_ERROR (
7076 err, "%s",
7077 "Failed to create arranger thread pool");
7078 }
7079 #endif
7080 }
7081