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