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