1 /*
2  * Copyright (C) 2019-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 "audio/region.h"
21 #include "gui/backend/timeline_selections.h"
22 #include "gui/widgets/audio_arranger.h"
23 #include "gui/widgets/audio_editor_space.h"
24 #include "gui/widgets/automation_arranger.h"
25 #include "gui/widgets/automation_editor_space.h"
26 #include "gui/widgets/automation_point.h"
27 #include "gui/widgets/bot_dock_edge.h"
28 #include "gui/widgets/center_dock.h"
29 #include "gui/widgets/chord_arranger.h"
30 #include "gui/widgets/chord_editor_space.h"
31 #include "gui/widgets/chord_object.h"
32 #include "gui/widgets/clip_editor.h"
33 #include "gui/widgets/clip_editor_inner.h"
34 #include "gui/widgets/event_viewer.h"
35 #include "gui/widgets/main_notebook.h"
36 #include "gui/widgets/marker.h"
37 #include "gui/widgets/midi_arranger.h"
38 #include "gui/widgets/midi_editor_space.h"
39 #include "gui/widgets/midi_modifier_arranger.h"
40 #include "gui/widgets/midi_note.h"
41 #include "gui/widgets/region.h"
42 #include "gui/widgets/timeline_arranger.h"
43 #include "gui/widgets/timeline_panel.h"
44 #include "project.h"
45 #include "settings/settings.h"
46 #include "utils/flags.h"
47 #include "utils/gtk.h"
48 #include "utils/objects.h"
49 #include "utils/resources.h"
50 #include "zrythm_app.h"
51 
52 #include <glib/gi18n.h>
53 
54 G_DEFINE_TYPE (
55   EventViewerWidget,
56   event_viewer_widget,
57   GTK_TYPE_BOX)
58 
59 enum TimelineColumns
60 {
61   TIMELINE_COLUMN_NAME,
62   TIMELINE_COLUMN_TYPE,
63   TIMELINE_COLUMN_START_POS,
64   TIMELINE_COLUMN_CLIP_START_POS,
65   TIMELINE_COLUMN_LOOP_START_POS,
66   TIMELINE_COLUMN_LOOP_END_POS,
67   TIMELINE_COLUMN_END_POS,
68   TIMELINE_COLUMN_OBJ,
69   NUM_TIMELINE_COLUMNS,
70 };
71 
72 enum MidiColumns
73 {
74   MIDI_COLUMN_NAME,
75   MIDI_COLUMN_PITCH,
76   MIDI_COLUMN_VELOCITY,
77   MIDI_COLUMN_START_POS,
78   MIDI_COLUMN_END_POS,
79   MIDI_COLUMN_OBJ,
80   NUM_MIDI_COLUMNS,
81 };
82 
83 enum ChordColumns
84 {
85   CHORD_COLUMN_NAME,
86   CHORD_COLUMN_START_POS,
87   CHORD_COLUMN_OBJ,
88   NUM_CHORD_COLUMNS,
89 };
90 
91 enum AutomationColumns
92 {
93   AUTOMATION_COLUMN_INDEX,
94   AUTOMATION_COLUMN_POS,
95   AUTOMATION_COLUMN_VALUE,
96   AUTOMATION_COLUMN_CURVINESS,
97   AUTOMATION_COLUMN_OBJ,
98   NUM_AUTOMATION_COLUMNS,
99 };
100 
101 enum AudioColumns
102 {
103   AUDIO_COLUMN_START_POS,
104   AUDIO_COLUMN_END_POS,
105   NUM_AUDIO_COLUMNS,
106 };
107 
108 #define SET_SORT_POSITION_FUNC(col) \
109   { \
110     FuncData * data = object_new (FuncData); \
111     data->column = col; \
112     gtk_tree_sortable_set_sort_func ( \
113       GTK_TREE_SORTABLE (store), \
114       data->column, sort_position_func, data, \
115       free); \
116   }
117 
118 #define SET_POSITION_PRINT_FUNC(col) \
119   { \
120     FuncData * data = object_new (FuncData); \
121     data->column = col; \
122     gtk_tree_view_column_set_cell_data_func ( \
123       column, renderer, \
124       print_position_cell_data_func, data, free); \
125   }
126 
127 static void
get_event_type_as_string(ArrangerObjectType type,char * buf)128 get_event_type_as_string (
129   ArrangerObjectType type,
130   char *             buf)
131 {
132   g_return_if_fail (
133     buf || type > ARRANGER_OBJECT_TYPE_ALL);
134 
135   const char * untranslated_type =
136     arranger_object_stringize_type (type);
137   strcpy (buf, _(untranslated_type));
138 }
139 
140 typedef struct FuncData
141 {
142   /** Column ID. */
143   int                 column;
144 } FuncData;
145 
146 static int
sort_position_func(GtkTreeModel * model,GtkTreeIter * a,GtkTreeIter * b,gpointer user_data)147 sort_position_func (
148   GtkTreeModel * model,
149   GtkTreeIter *  a,
150   GtkTreeIter *  b,
151   gpointer       user_data)
152 {
153   FuncData * sort_data = (FuncData *) user_data;
154   Position * pos1 = NULL;
155   gtk_tree_model_get (
156     model, a, sort_data->column, &pos1, -1);
157   Position * pos2 = NULL;
158   gtk_tree_model_get (
159     model, b, sort_data->column, &pos2, -1);
160   if (!pos1 && !pos2)
161     return 0;
162   else if (pos1 && !pos2)
163     return 1;
164   else if (!pos1 && pos2)
165     return -1;
166   else
167     return (pos1->ticks < pos2->ticks) ? -1 : 1;
168 }
169 
170 static void
print_position_cell_data_func(GtkTreeViewColumn * tree_column,GtkCellRenderer * cell,GtkTreeModel * tree_model,GtkTreeIter * iter,gpointer data)171 print_position_cell_data_func (
172   GtkTreeViewColumn * tree_column,
173   GtkCellRenderer *   cell,
174   GtkTreeModel *      tree_model,
175   GtkTreeIter *       iter,
176   gpointer            data)
177 {
178   FuncData * print_data = (FuncData *) data;
179   int col_id = print_data->column;
180   Position * pos = NULL;
181   gtk_tree_model_get (
182     tree_model, iter, col_id, &pos, -1);
183   char pos_str[50];
184   if (pos)
185     position_to_string_full (pos, pos_str, 1);
186   else
187     strcpy (pos_str, "");
188   g_object_set (
189     cell, "text", pos_str, NULL);
190 }
191 
192 static void
add_from_object(GtkListStore * store,GtkTreeIter * iter,ArrangerObject * obj)193 add_from_object (
194   GtkListStore *   store,
195   GtkTreeIter *    iter,
196   ArrangerObject * obj)
197 {
198   char name[200];
199   char type[200];
200   switch (obj->type)
201     {
202     case ARRANGER_OBJECT_TYPE_REGION:
203       {
204         ZRegion * r = (ZRegion *) obj;
205 
206         region_get_type_as_string (
207           r->id.type, type);
208         gtk_list_store_append (store, iter);
209         gtk_list_store_set (
210           store, iter,
211           TIMELINE_COLUMN_NAME, r->name,
212           TIMELINE_COLUMN_TYPE, type,
213           TIMELINE_COLUMN_START_POS, &obj->pos,
214           TIMELINE_COLUMN_CLIP_START_POS,
215           &obj->clip_start_pos,
216           TIMELINE_COLUMN_LOOP_START_POS,
217           &obj->loop_start_pos,
218           TIMELINE_COLUMN_LOOP_END_POS,
219           &obj->loop_end_pos,
220           TIMELINE_COLUMN_END_POS,
221           &obj->end_pos,
222           TIMELINE_COLUMN_OBJ, r,
223           -1);
224       }
225       break;
226     case ARRANGER_OBJECT_TYPE_MARKER:
227       {
228         Marker * m = (Marker *) obj;
229 
230         get_event_type_as_string (obj->type, type);
231         gtk_list_store_append (store, iter);
232         gtk_list_store_set (
233           store, iter,
234           TIMELINE_COLUMN_NAME, m->name,
235           TIMELINE_COLUMN_TYPE, type,
236           TIMELINE_COLUMN_START_POS, &obj->pos,
237           TIMELINE_COLUMN_CLIP_START_POS, NULL,
238           TIMELINE_COLUMN_LOOP_START_POS, NULL,
239           TIMELINE_COLUMN_LOOP_END_POS, NULL,
240           TIMELINE_COLUMN_END_POS, NULL,
241           TIMELINE_COLUMN_OBJ, m,
242           -1);
243       }
244       break;
245     case ARRANGER_OBJECT_TYPE_MIDI_NOTE:
246       {
247         MidiNote * mn = (MidiNote *) obj;
248 
249         midi_note_get_val_as_string (
250           mn, name, 0);
251         get_event_type_as_string (obj->type, type);
252         gtk_list_store_append (store, iter);
253         gtk_list_store_set (
254           store, iter,
255           MIDI_COLUMN_NAME, name,
256           MIDI_COLUMN_PITCH, mn->val,
257           MIDI_COLUMN_VELOCITY, mn->vel->vel,
258           MIDI_COLUMN_START_POS, &obj->pos,
259           MIDI_COLUMN_END_POS, &obj->end_pos,
260           MIDI_COLUMN_OBJ, mn,
261           -1);
262       }
263       break;
264     case ARRANGER_OBJECT_TYPE_CHORD_OBJECT:
265       {
266         ChordObject * c = (ChordObject *) obj;
267 
268         ChordDescriptor * descr =
269           chord_object_get_chord_descriptor (c);
270         chord_descriptor_to_string (
271           descr, name);
272         get_event_type_as_string (obj->type, type);
273         gtk_list_store_append (store, iter);
274         gtk_list_store_set (
275           store, iter,
276           CHORD_COLUMN_NAME, name,
277           CHORD_COLUMN_START_POS, &obj->pos,
278           CHORD_COLUMN_OBJ, c,
279           -1);
280       }
281       break;
282     case ARRANGER_OBJECT_TYPE_AUTOMATION_POINT:
283       {
284         AutomationPoint * ap =
285           (AutomationPoint*) obj;
286 
287         get_event_type_as_string (obj->type, type);
288         gtk_list_store_append (store, iter);
289         gtk_list_store_set (
290           store, iter,
291           AUTOMATION_COLUMN_INDEX, ap->index,
292           AUTOMATION_COLUMN_POS, &obj->pos,
293           AUTOMATION_COLUMN_VALUE, ap->fvalue,
294           AUTOMATION_COLUMN_CURVINESS,
295           ap->curve_opts.curviness,
296           AUTOMATION_COLUMN_OBJ, ap,
297           -1);
298       }
299       break;
300     default:
301       break;
302     }
303 }
304 
305 #define ADD_FOREACH_IN_ARRANGER(arranger) \
306   arranger_widget_get_all_objects ( \
307     arranger, objs, &num_objs); \
308   for (int i = 0; i < num_objs; i++) \
309     { \
310       ArrangerObject * obj = objs[i]; \
311       add_from_object ( \
312         store, &iter, obj); \
313     } \
314 
315 static GtkTreeModel *
create_timeline_model(EventViewerWidget * self)316 create_timeline_model (
317   EventViewerWidget * self)
318 {
319   GtkListStore *store;
320   GtkTreeIter iter;
321 
322   /* create list store */
323   store =
324     gtk_list_store_new (
325       NUM_TIMELINE_COLUMNS,
326       /* name */
327       G_TYPE_STRING,
328       /* type */
329       G_TYPE_STRING,
330       /* start pos */
331       G_TYPE_POINTER,
332       /* clip start pos */
333       G_TYPE_POINTER,
334       /* loop start pos */
335       G_TYPE_POINTER,
336       /* loop end pos */
337       G_TYPE_POINTER,
338       /* end pos */
339       G_TYPE_POINTER,
340       /* object */
341       G_TYPE_POINTER);
342 
343   /* add data to the list store (cheat by using
344    * the timeline arranger children) */
345   ArrangerObject * objs[2000];
346   int              num_objs;
347   ADD_FOREACH_IN_ARRANGER (MW_TIMELINE);
348   ADD_FOREACH_IN_ARRANGER (MW_PINNED_TIMELINE);
349 
350   SET_SORT_POSITION_FUNC (
351     TIMELINE_COLUMN_START_POS);
352   SET_SORT_POSITION_FUNC (
353     TIMELINE_COLUMN_CLIP_START_POS);
354   SET_SORT_POSITION_FUNC (
355     TIMELINE_COLUMN_LOOP_START_POS);
356   SET_SORT_POSITION_FUNC (
357     TIMELINE_COLUMN_LOOP_END_POS);
358   SET_SORT_POSITION_FUNC (
359     TIMELINE_COLUMN_END_POS);
360 
361   return GTK_TREE_MODEL (store);
362 }
363 
364 static GtkTreeModel *
create_midi_model(EventViewerWidget * self)365 create_midi_model (
366   EventViewerWidget * self)
367 {
368   GtkListStore *store;
369   GtkTreeIter iter;
370 
371   /* create list store */
372   store =
373     gtk_list_store_new (
374       NUM_MIDI_COLUMNS,
375       /* name */
376       G_TYPE_STRING,
377       /* pitch */
378       G_TYPE_INT,
379       /* velocity */
380       G_TYPE_INT,
381       /* start pos */
382       G_TYPE_POINTER,
383       /* end pos */
384       G_TYPE_POINTER,
385       /* object ptr */
386       G_TYPE_POINTER);
387 
388   /* add data to the list */
389   ArrangerObject * objs[2000];
390   int              num_objs;
391   ADD_FOREACH_IN_ARRANGER (
392     MW_MIDI_ARRANGER);
393 
394   SET_SORT_POSITION_FUNC (
395     MIDI_COLUMN_START_POS);
396   SET_SORT_POSITION_FUNC (
397     MIDI_COLUMN_END_POS);
398 
399   return GTK_TREE_MODEL (store);
400 }
401 
402 static GtkTreeModel *
create_chord_model(EventViewerWidget * self)403 create_chord_model (
404   EventViewerWidget * self)
405 {
406   GtkListStore *store;
407   GtkTreeIter iter;
408 
409   /* create list store */
410   store =
411     gtk_list_store_new (
412       NUM_CHORD_COLUMNS,
413       /* name */
414       G_TYPE_STRING,
415       /* position */
416       G_TYPE_POINTER,
417       G_TYPE_POINTER);
418 
419   /* add data to the list */
420   ArrangerObject * objs[2000];
421   int              num_objs;
422   ADD_FOREACH_IN_ARRANGER (
423     MW_CHORD_ARRANGER);
424 
425   SET_SORT_POSITION_FUNC (
426     CHORD_COLUMN_START_POS);
427 
428   return GTK_TREE_MODEL (store);
429 }
430 
431 static GtkTreeModel *
create_automation_model(EventViewerWidget * self)432 create_automation_model (
433   EventViewerWidget * self)
434 {
435   GtkListStore *store;
436   GtkTreeIter iter;
437 
438   /* create list store */
439   store =
440     gtk_list_store_new (
441       NUM_AUTOMATION_COLUMNS,
442       /* index */
443       G_TYPE_INT,
444       /* position */
445       G_TYPE_POINTER,
446       /* value */
447       G_TYPE_FLOAT,
448       /* curviness */
449       G_TYPE_DOUBLE,
450       /* object */
451       G_TYPE_POINTER);
452 
453   /* add data to the list */
454   ArrangerObject * objs[2000];
455   int              num_objs;
456   ADD_FOREACH_IN_ARRANGER (
457     MW_AUTOMATION_ARRANGER);
458 
459   SET_SORT_POSITION_FUNC (
460     AUTOMATION_COLUMN_POS);
461 
462   return GTK_TREE_MODEL (store);
463 }
464 
465 static GtkTreeModel *
create_audio_model(EventViewerWidget * self)466 create_audio_model (
467   EventViewerWidget * self)
468 {
469   GtkListStore *store;
470   GtkTreeIter iter;
471 
472   /* create list store */
473   store =
474     gtk_list_store_new (
475       NUM_AUDIO_COLUMNS,
476       /* start position */
477       G_TYPE_POINTER,
478       /* end position */
479       G_TYPE_POINTER);
480 
481   gtk_list_store_append (store, &iter);
482   gtk_list_store_set (
483     store, &iter,
484     AUDIO_COLUMN_START_POS,
485     &AUDIO_SELECTIONS->sel_start,
486     AUDIO_COLUMN_END_POS,
487     &AUDIO_SELECTIONS->sel_end,
488     -1);
489 
490   SET_SORT_POSITION_FUNC (
491     AUDIO_COLUMN_START_POS);
492   SET_SORT_POSITION_FUNC (
493     AUDIO_COLUMN_END_POS);
494 
495   return GTK_TREE_MODEL (store);
496 }
497 
498 static GtkTreeModel *
create_editor_model(EventViewerWidget * self)499 create_editor_model (
500   EventViewerWidget * self)
501 {
502   /* Check what to add based on the selected
503    * region */
504   ZRegion * r =
505     clip_editor_get_region (CLIP_EDITOR);
506   g_return_val_if_fail (r, NULL);
507 
508   /* add data to the list */
509   switch (r->id.type)
510     {
511     case REGION_TYPE_MIDI:
512       return create_midi_model (self);
513       break;
514     case REGION_TYPE_AUDIO:
515       return create_audio_model (self);
516       break;
517     case REGION_TYPE_AUTOMATION:
518       return create_automation_model (self);
519       break;
520     case REGION_TYPE_CHORD:
521       return create_chord_model (self);
522       break;
523     }
524 
525   g_return_val_if_reached (NULL);
526 }
527 
528 #undef ADD_FOREACH_IN_ARRANGER
529 
530 static void
add_timeline_columns(EventViewerWidget * self)531 add_timeline_columns (
532   EventViewerWidget * self)
533 {
534   GtkCellRenderer *renderer;
535   GtkTreeViewColumn *column;
536 
537   /* first remove existing columns */
538   z_gtk_tree_view_remove_all_columns (
539     self->treeview);
540 
541   /* column for name */
542   renderer = gtk_cell_renderer_text_new ();
543   column =
544     gtk_tree_view_column_new_with_attributes (
545       _("Name"), renderer, "text",
546       TIMELINE_COLUMN_NAME, NULL);
547   gtk_tree_view_column_set_sort_column_id (
548     column, TIMELINE_COLUMN_NAME);
549   gtk_tree_view_column_set_reorderable (
550     column, true);
551   gtk_tree_view_append_column (
552     self->treeview, column);
553 
554   /* column for type */
555   renderer = gtk_cell_renderer_text_new ();
556   column =
557     gtk_tree_view_column_new_with_attributes (
558       _("Type"), renderer, "text",
559       TIMELINE_COLUMN_TYPE, NULL);
560   gtk_tree_view_column_set_sort_column_id (
561     column, TIMELINE_COLUMN_TYPE);
562   gtk_tree_view_column_set_reorderable (
563     column, true);
564   gtk_tree_view_append_column (
565     self->treeview, column);
566 
567   /* column for start pos */
568   renderer = gtk_cell_renderer_text_new ();
569   column =
570     gtk_tree_view_column_new_with_attributes (
571       _("Start"), renderer, NULL);
572   gtk_tree_view_column_set_sort_column_id (
573     column, TIMELINE_COLUMN_START_POS);
574   SET_POSITION_PRINT_FUNC (
575     TIMELINE_COLUMN_START_POS);
576   gtk_tree_view_column_set_reorderable (
577     column, true);
578   gtk_tree_view_append_column (
579     self->treeview, column);
580 
581   /* column for clip start pos */
582   renderer = gtk_cell_renderer_text_new ();
583   column =
584     gtk_tree_view_column_new_with_attributes (
585       _("Clip start"), renderer, NULL);
586   SET_POSITION_PRINT_FUNC (
587     TIMELINE_COLUMN_CLIP_START_POS);
588   gtk_tree_view_column_set_sort_column_id (
589     column, TIMELINE_COLUMN_CLIP_START_POS);
590   gtk_tree_view_column_set_reorderable (
591     column, true);
592   gtk_tree_view_append_column (
593     self->treeview, column);
594 
595   /* column for loop start pos */
596   renderer = gtk_cell_renderer_text_new ();
597   column =
598     gtk_tree_view_column_new_with_attributes (
599       _("Loop start"), renderer, NULL);
600   SET_POSITION_PRINT_FUNC (
601     TIMELINE_COLUMN_LOOP_START_POS);
602   gtk_tree_view_column_set_sort_column_id (
603     column, TIMELINE_COLUMN_LOOP_START_POS);
604   gtk_tree_view_column_set_reorderable (
605     column, true);
606   gtk_tree_view_append_column (
607     self->treeview, column);
608 
609   /* column for loop end pos */
610   renderer = gtk_cell_renderer_text_new ();
611   column =
612     gtk_tree_view_column_new_with_attributes (
613       _("Loop end"), renderer, NULL);
614   SET_POSITION_PRINT_FUNC (
615     TIMELINE_COLUMN_LOOP_END_POS);
616   gtk_tree_view_column_set_sort_column_id (
617     column, TIMELINE_COLUMN_LOOP_END_POS);
618   gtk_tree_view_column_set_reorderable (
619     column, true);
620   gtk_tree_view_append_column (
621     self->treeview, column);
622 
623   /* column for end pos */
624   renderer = gtk_cell_renderer_text_new ();
625   column =
626     gtk_tree_view_column_new_with_attributes (
627       _("End"), renderer, NULL);
628   SET_POSITION_PRINT_FUNC (
629     TIMELINE_COLUMN_END_POS);
630   gtk_tree_view_column_set_sort_column_id (
631     column, TIMELINE_COLUMN_END_POS);
632   gtk_tree_view_column_set_reorderable (
633     column, true);
634   gtk_tree_view_append_column (
635     self->treeview, column);
636 }
637 
638 static void
append_midi_columns(EventViewerWidget * self)639 append_midi_columns (
640   EventViewerWidget * self)
641 {
642   GtkCellRenderer *renderer;
643   GtkTreeViewColumn *column;
644 
645   /* column for name */
646   renderer = gtk_cell_renderer_text_new ();
647   column =
648     gtk_tree_view_column_new_with_attributes (
649       _("Note"), renderer, "text",
650       MIDI_COLUMN_NAME, NULL);
651   gtk_tree_view_column_set_sort_column_id (
652     column, MIDI_COLUMN_NAME);
653   gtk_tree_view_column_set_reorderable (
654     column, true);
655   gtk_tree_view_append_column (
656     self->treeview, column);
657 
658   /* column for pitch */
659   renderer = gtk_cell_renderer_text_new ();
660   column =
661     gtk_tree_view_column_new_with_attributes (
662       _("Pitch"), renderer, "text",
663       MIDI_COLUMN_PITCH, NULL);
664   gtk_tree_view_column_set_sort_column_id (
665     column, MIDI_COLUMN_PITCH);
666   gtk_tree_view_column_set_reorderable (
667     column, true);
668   gtk_tree_view_append_column (
669     self->treeview, column);
670 
671   /* column for velocity */
672   renderer = gtk_cell_renderer_text_new ();
673   column =
674     gtk_tree_view_column_new_with_attributes (
675       _("Velocity"), renderer, "text",
676       MIDI_COLUMN_VELOCITY, NULL);
677   gtk_tree_view_column_set_sort_column_id (
678     column, MIDI_COLUMN_VELOCITY);
679   gtk_tree_view_column_set_reorderable (
680     column, true);
681   gtk_tree_view_append_column (
682     self->treeview, column);
683 
684   /* column for start pos */
685   renderer = gtk_cell_renderer_text_new ();
686   column =
687     gtk_tree_view_column_new_with_attributes (
688       _("Start"), renderer, NULL);
689   SET_POSITION_PRINT_FUNC (
690     MIDI_COLUMN_START_POS);
691   gtk_tree_view_column_set_sort_column_id (
692     column, MIDI_COLUMN_START_POS);
693   gtk_tree_view_column_set_reorderable (
694     column, true);
695   gtk_tree_view_append_column (
696     self->treeview, column);
697 
698   /* column for end pos */
699   renderer = gtk_cell_renderer_text_new ();
700   column =
701     gtk_tree_view_column_new_with_attributes (
702       _("End"), renderer, NULL);
703   SET_POSITION_PRINT_FUNC (
704     MIDI_COLUMN_END_POS);
705   gtk_tree_view_column_set_sort_column_id (
706     column, MIDI_COLUMN_END_POS);
707   gtk_tree_view_column_set_reorderable (
708     column, true);
709   gtk_tree_view_append_column (
710     self->treeview, column);
711 }
712 
713 static void
append_chord_columns(EventViewerWidget * self)714 append_chord_columns (
715   EventViewerWidget * self)
716 {
717   GtkCellRenderer *renderer;
718   GtkTreeViewColumn *column;
719 
720   /* column for name */
721   renderer = gtk_cell_renderer_text_new ();
722   column =
723     gtk_tree_view_column_new_with_attributes (
724       _("Chord"), renderer, "text",
725       CHORD_COLUMN_NAME, NULL);
726   gtk_tree_view_column_set_sort_column_id (
727     column, CHORD_COLUMN_NAME);
728   gtk_tree_view_column_set_reorderable (
729     column, true);
730   gtk_tree_view_append_column (
731     self->treeview, column);
732 
733   /* column for start pos */
734   renderer = gtk_cell_renderer_text_new ();
735   column =
736     gtk_tree_view_column_new_with_attributes (
737       _("Position"), renderer, NULL);
738   SET_POSITION_PRINT_FUNC (
739     CHORD_COLUMN_START_POS);
740   gtk_tree_view_column_set_sort_column_id (
741     column, CHORD_COLUMN_START_POS);
742   gtk_tree_view_column_set_reorderable (
743     column, true);
744   gtk_tree_view_append_column (
745     self->treeview, column);
746 }
747 
748 static void
append_automation_columns(EventViewerWidget * self)749 append_automation_columns (
750   EventViewerWidget * self)
751 {
752   GtkCellRenderer *renderer;
753   GtkTreeViewColumn *column;
754 
755   /* column for name */
756   renderer = gtk_cell_renderer_text_new ();
757   column =
758     gtk_tree_view_column_new_with_attributes (
759       _("Index"), renderer, "text",
760       AUTOMATION_COLUMN_INDEX, NULL);
761   gtk_tree_view_column_set_sort_column_id (
762     column, AUTOMATION_COLUMN_INDEX);
763   gtk_tree_view_column_set_reorderable (
764     column, true);
765   gtk_tree_view_append_column (
766     self->treeview, column);
767 
768   /* column for start pos */
769   renderer = gtk_cell_renderer_text_new ();
770   column =
771     gtk_tree_view_column_new_with_attributes (
772       _("Position"), renderer, NULL);
773   SET_POSITION_PRINT_FUNC (
774     AUTOMATION_COLUMN_POS);
775   gtk_tree_view_column_set_sort_column_id (
776     column, AUTOMATION_COLUMN_POS);
777   gtk_tree_view_column_set_reorderable (
778     column, true);
779   gtk_tree_view_append_column (
780     self->treeview, column);
781 
782   /* column for value */
783   renderer = gtk_cell_renderer_text_new ();
784   column =
785     gtk_tree_view_column_new_with_attributes (
786       _("Value"), renderer, "text",
787       AUTOMATION_COLUMN_VALUE, NULL);
788   gtk_tree_view_column_set_sort_column_id (
789     column, AUTOMATION_COLUMN_VALUE);
790   gtk_tree_view_column_set_reorderable (
791     column, true);
792   gtk_tree_view_append_column (
793     self->treeview, column);
794 
795   /* column for curviness */
796   renderer = gtk_cell_renderer_text_new ();
797   column =
798     gtk_tree_view_column_new_with_attributes (
799       _("Curviness"), renderer, "text",
800       AUTOMATION_COLUMN_CURVINESS, NULL);
801   gtk_tree_view_column_set_sort_column_id (
802     column, AUTOMATION_COLUMN_CURVINESS);
803   gtk_tree_view_column_set_reorderable (
804     column, true);
805   gtk_tree_view_append_column (
806     self->treeview, column);
807 }
808 
809 static void
append_audio_columns(EventViewerWidget * self)810 append_audio_columns (
811   EventViewerWidget * self)
812 {
813   GtkCellRenderer * renderer;
814   GtkTreeViewColumn * column;
815 
816   /* column for start pos */
817   renderer = gtk_cell_renderer_text_new ();
818   column =
819     gtk_tree_view_column_new_with_attributes (
820       _("Selection start"), renderer, NULL);
821   SET_POSITION_PRINT_FUNC (
822     AUDIO_COLUMN_START_POS);
823   gtk_tree_view_column_set_sort_column_id (
824     column, AUDIO_COLUMN_START_POS);
825   gtk_tree_view_column_set_reorderable (
826     column, true);
827   gtk_tree_view_append_column (
828     self->treeview, column);
829 
830   /* column for end pos */
831   renderer = gtk_cell_renderer_text_new ();
832   column =
833     gtk_tree_view_column_new_with_attributes (
834       _("Selection end"), renderer, NULL);
835   SET_POSITION_PRINT_FUNC (
836     AUDIO_COLUMN_END_POS);
837   gtk_tree_view_column_set_sort_column_id (
838     column, AUDIO_COLUMN_END_POS);
839   gtk_tree_view_column_set_reorderable (
840     column, true);
841   gtk_tree_view_append_column (
842     self->treeview, column);
843 }
844 
845 /**
846  * @return If columns added.
847  */
848 static int
add_editor_columns(EventViewerWidget * self)849 add_editor_columns (
850   EventViewerWidget * self)
851 {
852   ZRegion * r =
853     clip_editor_get_region (CLIP_EDITOR);
854   if (!r)
855     return 0;
856 
857   /* if the region type is the same no need to
858    * remove/readd columns */
859   if (self->region_type == r->id.type)
860     return 1;
861   else
862     self->region_type = r->id.type;
863 
864   /* first remove existing columns */
865   z_gtk_tree_view_remove_all_columns (
866     self->treeview);
867 
868   switch (r->id.type)
869     {
870     case REGION_TYPE_MIDI:
871       append_midi_columns (self);
872       break;
873     case REGION_TYPE_AUDIO:
874       append_audio_columns (self);
875       break;
876     case REGION_TYPE_CHORD:
877       append_chord_columns (self);
878       break;
879     case REGION_TYPE_AUTOMATION:
880       append_automation_columns (self);
881       break;
882     default:
883       break;
884     }
885 
886   return 1;
887 }
888 
889 static ArrangerSelections *
get_arranger_selections(EventViewerWidget * self)890 get_arranger_selections (
891   EventViewerWidget * self)
892 {
893   if (self->type == EVENT_VIEWER_TYPE_TIMELINE)
894     {
895       return (ArrangerSelections *) TL_SELECTIONS;
896     }
897   else
898     {
899       ZRegion * r =
900         clip_editor_get_region (CLIP_EDITOR);
901       if (!r)
902         return NULL;
903 
904       switch (r->id.type)
905         {
906         case REGION_TYPE_MIDI:
907           return (ArrangerSelections *) MA_SELECTIONS;
908         case REGION_TYPE_AUDIO:
909           return
910             (ArrangerSelections *) AUDIO_SELECTIONS;
911         case REGION_TYPE_AUTOMATION:
912           return
913             (ArrangerSelections *)
914             AUTOMATION_SELECTIONS;
915         case REGION_TYPE_CHORD:
916           return
917             (ArrangerSelections *) CHORD_SELECTIONS;
918         }
919     }
920 
921   g_return_val_if_reached (NULL);
922 }
923 
924 static int
get_obj_column(EventViewerWidget * self)925 get_obj_column (
926   EventViewerWidget * self)
927 {
928   if (self->type == EVENT_VIEWER_TYPE_TIMELINE)
929     {
930       return TIMELINE_COLUMN_OBJ;
931     }
932   else
933     {
934       ZRegion * r =
935         clip_editor_get_region (CLIP_EDITOR);
936       if (!r)
937         return -1;
938 
939       switch (r->id.type)
940         {
941         case REGION_TYPE_MIDI:
942           return MIDI_COLUMN_OBJ;
943         case REGION_TYPE_AUDIO:
944           return -1;
945         case REGION_TYPE_AUTOMATION:
946           return AUTOMATION_COLUMN_OBJ;
947         case REGION_TYPE_CHORD:
948           return CHORD_COLUMN_OBJ;
949         }
950     }
951 
952   g_return_val_if_reached (-1);
953 }
954 
955 static void
mark_selected_objects_as_selected(EventViewerWidget * self)956 mark_selected_objects_as_selected (
957   EventViewerWidget * self)
958 {
959   ArrangerSelections * sel =
960     get_arranger_selections (self);;
961   int obj_column = get_obj_column (self);;
962   if (!sel || obj_column < 0)
963     return;
964 
965   self->marking_selected_objs = true;
966 
967   GtkTreeSelection * selection =
968     gtk_tree_view_get_selection (
969       GTK_TREE_VIEW (self->treeview));
970   gtk_tree_selection_unselect_all (selection);
971   int num_objs = 0;
972   ArrangerObject ** objs =
973     arranger_selections_get_all_objects (
974       sel, &num_objs);
975   GtkTreeIter iter;
976   bool has_iter =
977     gtk_tree_model_get_iter_first (
978       GTK_TREE_MODEL (self->model), &iter);
979   while (has_iter)
980     {
981       ArrangerObject * iter_obj = NULL;
982       gtk_tree_model_get (
983         GTK_TREE_MODEL (self->model), &iter,
984         obj_column, &iter_obj, -1);
985       g_return_if_fail (iter_obj);
986 
987       for (int i = 0; i < num_objs; i++)
988         {
989           ArrangerObject * obj = objs[i];
990           if (obj != iter_obj)
991             continue;
992 
993           gtk_tree_selection_select_iter (
994             selection, &iter);
995         }
996 
997       has_iter =
998         gtk_tree_model_iter_next (
999           GTK_TREE_MODEL (self->model), &iter);
1000     }
1001   free (objs);
1002 
1003   self->marking_selected_objs = false;
1004 }
1005 
1006 static void
refresh_timeline_events(EventViewerWidget * self)1007 refresh_timeline_events (
1008   EventViewerWidget * self)
1009 {
1010   add_timeline_columns (self);
1011   self->model =
1012     create_timeline_model (self);
1013   gtk_tree_view_set_model (
1014     GTK_TREE_VIEW (self->treeview),
1015     self->model);
1016 }
1017 
1018 static void
refresh_editor_events(EventViewerWidget * self)1019 refresh_editor_events (
1020   EventViewerWidget * self)
1021 {
1022   if (add_editor_columns (self))
1023     {
1024       self->model =
1025         create_editor_model (self);
1026       gtk_tree_view_set_model (
1027         GTK_TREE_VIEW (self->treeview),
1028         self->model);
1029     }
1030 }
1031 
1032 /**
1033  * Called to update the models.
1034  */
1035 void
event_viewer_widget_refresh(EventViewerWidget * self)1036 event_viewer_widget_refresh (
1037   EventViewerWidget * self)
1038 {
1039   if (self->type == EVENT_VIEWER_TYPE_TIMELINE
1040       &&
1041       !g_settings_get_boolean (
1042          S_UI, "timeline-event-viewer-visible"))
1043     return;
1044   if (self->type == EVENT_VIEWER_TYPE_EDITOR
1045       &&
1046       !g_settings_get_boolean (
1047          S_UI, "editor-event-viewer-visible"))
1048     return;
1049 
1050   switch (self->type)
1051     {
1052     case EVENT_VIEWER_TYPE_TIMELINE:
1053       refresh_timeline_events (self);
1054       break;
1055     case EVENT_VIEWER_TYPE_EDITOR:
1056       refresh_editor_events (self);
1057       break;
1058     }
1059 
1060   GtkTreeSelection * selection =
1061     gtk_tree_view_get_selection (
1062       GTK_TREE_VIEW (self->treeview));
1063   gtk_tree_selection_set_mode (
1064     selection, GTK_SELECTION_MULTIPLE);
1065 
1066   mark_selected_objects_as_selected (self);
1067 }
1068 
1069 /**
1070  * Convenience function.
1071  */
1072 void
event_viewer_widget_refresh_for_selections(ArrangerSelections * sel)1073 event_viewer_widget_refresh_for_selections (
1074   ArrangerSelections * sel)
1075 {
1076   if (sel->type == ARRANGER_SELECTIONS_TYPE_TIMELINE)
1077     event_viewer_widget_refresh (
1078       MW_TIMELINE_EVENT_VIEWER);
1079   else
1080     event_viewer_widget_refresh (
1081       MW_EDITOR_EVENT_VIEWER);
1082 }
1083 
1084 void
event_viewer_widget_refresh_for_arranger(ArrangerWidget * arranger)1085 event_viewer_widget_refresh_for_arranger (
1086   ArrangerWidget * arranger)
1087 {
1088   switch (arranger->type)
1089     {
1090     case ARRANGER_WIDGET_TYPE_TIMELINE:
1091       event_viewer_widget_refresh (
1092         MW_TIMELINE_EVENT_VIEWER);
1093       break;
1094     case ARRANGER_WIDGET_TYPE_MIDI:
1095     case ARRANGER_WIDGET_TYPE_MIDI_MODIFIER:
1096     case ARRANGER_WIDGET_TYPE_CHORD:
1097     case ARRANGER_WIDGET_TYPE_AUTOMATION:
1098     case ARRANGER_WIDGET_TYPE_AUDIO:
1099       event_viewer_widget_refresh (
1100         MW_EDITOR_EVENT_VIEWER);
1101       break;
1102     default:
1103       g_warn_if_reached ();
1104       break;
1105     }
1106 }
1107 
1108 static int
selection_func(GtkTreeSelection * selection,GtkTreeModel * model,GtkTreePath * path,int path_currently_selected,void * data)1109 selection_func (
1110   GtkTreeSelection * selection,
1111   GtkTreeModel *     model,
1112   GtkTreePath *      path,
1113   int                path_currently_selected,
1114   void *             data)
1115 {
1116   EventViewerWidget * self =
1117     (EventViewerWidget *) data;
1118 
1119   GtkTreeIter iter;
1120   bool has_any =
1121     gtk_tree_model_get_iter (model, &iter, path);
1122   g_return_val_if_fail (has_any, false);
1123 
1124   /* select object if selection is not made
1125    * programmatically */
1126   if (!self->marking_selected_objs)
1127     {
1128       int obj_column = get_obj_column (self);
1129       if (obj_column >= 0)
1130         {
1131           ArrangerObject * obj = NULL;
1132           gtk_tree_model_get (
1133             model, &iter, obj_column, &obj, -1);
1134           arranger_object_select (
1135             obj, !path_currently_selected, F_APPEND,
1136             F_PUBLISH_EVENTS);
1137         }
1138     }
1139 
1140   /* allow toggle */
1141   return true;
1142 }
1143 
1144 /**
1145  * Sets up the event viewer.
1146  */
1147 void
event_viewer_widget_setup(EventViewerWidget * self,EventViewerType type)1148 event_viewer_widget_setup (
1149   EventViewerWidget * self,
1150   EventViewerType     type)
1151 {
1152   self->type = type;
1153 
1154   GtkTreeSelection * sel =
1155     gtk_tree_view_get_selection (
1156       GTK_TREE_VIEW (self->treeview));
1157   gtk_tree_selection_set_select_function (
1158     sel, selection_func, self, NULL);
1159 
1160   event_viewer_widget_refresh (self);
1161 }
1162 
1163 static void
event_viewer_widget_init(EventViewerWidget * self)1164 event_viewer_widget_init (
1165   EventViewerWidget * self)
1166 {
1167   gtk_widget_init_template (GTK_WIDGET (self));
1168 
1169   self->region_type = -1;
1170 }
1171 
1172 
1173 static void
event_viewer_widget_class_init(EventViewerWidgetClass * _klass)1174 event_viewer_widget_class_init (
1175   EventViewerWidgetClass * _klass)
1176 {
1177   GtkWidgetClass * klass =
1178     GTK_WIDGET_CLASS (_klass);
1179   resources_set_class_template (
1180     klass, "event_viewer.ui");
1181 
1182 #define BIND_CHILD(x) \
1183   gtk_widget_class_bind_template_child ( \
1184     klass, EventViewerWidget, x)
1185 
1186   BIND_CHILD (treeview);
1187 }
1188