1 /*
2  * Copyright (C) 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 <time.h>
21 #include <sys/time.h>
22 
23 #include "actions/tracklist_selections.h"
24 #include "actions/undoable_action.h"
25 #include "actions/undo_manager.h"
26 #include "audio/master_track.h"
27 #include "audio/meter.h"
28 #include "audio/port_connections_manager.h"
29 #include "audio/track.h"
30 #include "gui/widgets/balance_control.h"
31 #include "gui/widgets/bot_dock_edge.h"
32 #include "gui/widgets/center_dock.h"
33 #include "gui/widgets/folder_channel.h"
34 #include "gui/widgets/color_area.h"
35 #include "gui/widgets/fader_buttons.h"
36 #include "gui/widgets/editable_label.h"
37 #include "gui/widgets/expander_box.h"
38 #include "gui/widgets/meter.h"
39 #include "gui/widgets/mixer.h"
40 #include "gui/widgets/fader.h"
41 #include "gui/widgets/knob.h"
42 #include "gui/widgets/plugin_strip_expander.h"
43 #include "gui/widgets/route_target_selector.h"
44 #include "plugins/lv2_plugin.h"
45 #include "project.h"
46 #include "utils/error.h"
47 #include "utils/flags.h"
48 #include "utils/gtk.h"
49 #include "utils/math.h"
50 #include "utils/resources.h"
51 #include "utils/symap.h"
52 #include "utils/ui.h"
53 #include "zrythm.h"
54 #include "zrythm_app.h"
55 
56 #include <gtk/gtk.h>
57 
58 #include <glib/gi18n.h>
59 
G_DEFINE_TYPE(FolderChannelWidget,folder_channel_widget,GTK_TYPE_EVENT_BOX)60 G_DEFINE_TYPE (
61   FolderChannelWidget, folder_channel_widget,
62   GTK_TYPE_EVENT_BOX)
63 
64 static void
65 on_drag_data_received (
66   GtkWidget        *widget,
67   GdkDragContext   *context,
68   gint              x,
69   gint              y,
70   GtkSelectionData *data,
71   guint             info,
72   guint             time,
73   FolderChannelWidget * self)
74 {
75   g_message ("drag data received");
76   Track * this = self->track;
77 
78   /* determine if moving or copying */
79   GdkDragAction action =
80     gdk_drag_context_get_selected_action (
81       context);
82 
83   int w =
84     gtk_widget_get_allocated_width (widget);
85 
86   /* determine position to move to */
87   int pos;
88   if (x < w / 2)
89     {
90       if (this->pos <=
91           MW_MIXER->start_drag_track->pos)
92         pos = this->pos;
93       else
94         {
95           Track * prev =
96             tracklist_get_prev_visible_track (
97               TRACKLIST, this);
98           pos =
99             prev ? prev->pos : this->pos;
100         }
101     }
102   else
103     {
104       if (this->pos >=
105           MW_MIXER->start_drag_track->pos)
106         pos = this->pos;
107       else
108         {
109           Track * next =
110             tracklist_get_next_visible_track (
111               TRACKLIST, this);
112           pos =
113             next ? next->pos : this->pos;
114         }
115     }
116 
117   tracklist_selections_select_foldable_children (
118     TRACKLIST_SELECTIONS);
119 
120   GError * err = NULL;
121   bool ret;
122   if (action == GDK_ACTION_COPY)
123     {
124       ret =
125         tracklist_selections_action_perform_copy (
126           TRACKLIST_SELECTIONS,
127           PORT_CONNECTIONS_MGR, pos, &err);
128     }
129   else
130     {
131       ret =
132         tracklist_selections_action_perform_move (
133           TRACKLIST_SELECTIONS,
134           PORT_CONNECTIONS_MGR, pos, &err);
135     }
136 
137   if (!ret)
138     {
139       HANDLE_ERROR (
140         err, "%s",
141         _("Failed to move or copy track(s)"));
142     }
143 }
144 
145 static void
on_drag_data_get(GtkWidget * widget,GdkDragContext * context,GtkSelectionData * data,guint info,guint time,FolderChannelWidget * self)146 on_drag_data_get (
147   GtkWidget        *widget,
148   GdkDragContext   *context,
149   GtkSelectionData *data,
150   guint             info,
151   guint             time,
152   FolderChannelWidget * self)
153 {
154   g_debug ("drag data get");
155 
156   /* Not really needed since the selections are
157    * used. just send master */
158   gtk_selection_data_set (
159     data,
160     gdk_atom_intern_static_string (
161       TARGET_ENTRY_TRACK),
162     32,
163     (const guchar *) &P_MASTER_TRACK,
164     sizeof (P_MASTER_TRACK));
165 }
166 
167 /**
168  * For drag n drop.
169  */
170 static void
on_dnd_drag_begin(GtkWidget * widget,GdkDragContext * context,FolderChannelWidget * self)171 on_dnd_drag_begin (
172   GtkWidget      *widget,
173   GdkDragContext *context,
174   FolderChannelWidget * self)
175 {
176   Track * track = self->track;
177   self->selected_in_dnd = 1;
178   MW_MIXER->start_drag_track = track;
179 
180   if (self->n_press == 1)
181     {
182       int ctrl = 0, selected = 0;
183 
184       ctrl = self->ctrl_held_at_start;
185 
186       if (tracklist_selections_contains_track (
187             TRACKLIST_SELECTIONS,
188             track))
189         selected = 1;
190 
191       /* no control & not selected */
192       if (!ctrl && !selected)
193         {
194           tracklist_selections_select_single (
195             TRACKLIST_SELECTIONS,
196             track, F_PUBLISH_EVENTS);
197         }
198       else if (!ctrl && selected)
199         { }
200       else if (ctrl && !selected)
201         tracklist_selections_add_track (
202           TRACKLIST_SELECTIONS, track, 1);
203     }
204 }
205 
206 /**
207  * Highlights/unhighlights the folder_channels
208  * appropriately.
209  */
210 static void
do_highlight(FolderChannelWidget * self,gint x,gint y)211 do_highlight (
212   FolderChannelWidget * self,
213   gint x,
214   gint y)
215 {
216   /* if we are closer to the start of selection than
217    * the end */
218   int w =
219     gtk_widget_get_allocated_width (
220       GTK_WIDGET (self));
221   if (x < w / 2)
222     {
223       /* highlight left */
224       gtk_drag_highlight (
225         GTK_WIDGET (
226           self->highlight_left_box));
227       gtk_widget_set_size_request (
228         GTK_WIDGET (
229           self->highlight_left_box),
230         2, -1);
231 
232       /* unhilight right */
233       gtk_drag_unhighlight (
234         GTK_WIDGET (
235           self->highlight_right_box));
236       gtk_widget_set_size_request (
237         GTK_WIDGET (
238           self->highlight_right_box),
239         -1, -1);
240     }
241   else
242     {
243       /* highlight right */
244       gtk_drag_highlight (
245         GTK_WIDGET (
246           self->highlight_right_box));
247       gtk_widget_set_size_request (
248         GTK_WIDGET (
249           self->highlight_right_box),
250         2, -1);
251 
252       /* unhilight left */
253       gtk_drag_unhighlight (
254         GTK_WIDGET (
255           self->highlight_left_box));
256       gtk_widget_set_size_request (
257         GTK_WIDGET (
258           self->highlight_left_box),
259         -1, -1);
260     }
261 }
262 
263 static void
on_drag_motion(GtkWidget * widget,GdkDragContext * context,gint x,gint y,guint time,FolderChannelWidget * self)264 on_drag_motion (
265   GtkWidget *widget,
266   GdkDragContext *context,
267   gint x,
268   gint y,
269   guint time,
270   FolderChannelWidget * self)
271 {
272   GdkModifierType mask;
273   z_gtk_widget_get_mask (
274     widget, &mask);
275   if (mask & GDK_CONTROL_MASK)
276     gdk_drag_status (context, GDK_ACTION_COPY, time);
277   else
278     gdk_drag_status (context, GDK_ACTION_MOVE, time);
279 
280   do_highlight (self, x, y);
281 }
282 
283 static void
on_drag_leave(GtkWidget * widget,GdkDragContext * context,guint time,FolderChannelWidget * self)284 on_drag_leave (
285   GtkWidget *      widget,
286   GdkDragContext * context,
287   guint            time,
288   FolderChannelWidget *  self)
289 {
290   g_message ("on_drag_leave");
291 
292   /*do_highlight (self);*/
293   gtk_drag_unhighlight (
294     GTK_WIDGET (self->highlight_left_box));
295   gtk_widget_set_size_request (
296     GTK_WIDGET (self->highlight_left_box),
297     -1, -1);
298   gtk_drag_unhighlight (
299     GTK_WIDGET (self->highlight_right_box));
300   gtk_widget_set_size_request (
301     GTK_WIDGET (self->highlight_right_box),
302     -1, -1);
303 }
304 
305 /**
306  * Callback when somewhere in the folder_channel is
307  * pressed.
308  *
309  * Only responsible for setting the tracklist
310  * selection and should not do anything else.
311  */
312 static void
on_whole_folder_channel_press(GtkGestureMultiPress * gesture,gint n_press,gdouble x,gdouble y,FolderChannelWidget * self)313 on_whole_folder_channel_press (
314   GtkGestureMultiPress *gesture,
315   gint                  n_press,
316   gdouble               x,
317   gdouble               y,
318   FolderChannelWidget * self)
319 {
320   self->n_press = n_press;
321 
322   GdkModifierType state_mask =
323     ui_get_state_mask (
324       GTK_GESTURE (gesture));
325   self->ctrl_held_at_start =
326     state_mask & GDK_CONTROL_MASK;
327 }
328 
329 static void
on_drag_begin(GtkGestureDrag * gesture,gdouble start_x,gdouble start_y,FolderChannelWidget * self)330 on_drag_begin (
331   GtkGestureDrag * gesture,
332   gdouble          start_x,
333   gdouble          start_y,
334   FolderChannelWidget *  self)
335 {
336   self->selected_in_dnd = 0;
337   self->dragged = 0;
338 }
339 
340 static void
on_drag_update(GtkGestureDrag * gesture,gdouble offset_x,gdouble offset_y,FolderChannelWidget * self)341 on_drag_update (
342   GtkGestureDrag * gesture,
343   gdouble          offset_x,
344   gdouble          offset_y,
345   FolderChannelWidget *  self)
346 {
347   self->dragged = true;
348 }
349 
350 static bool
on_btn_release(GtkWidget * widget,GdkEventButton * event,FolderChannelWidget * self)351 on_btn_release (
352   GtkWidget *      widget,
353   GdkEventButton * event,
354   FolderChannelWidget *  self)
355 {
356   if (self->dragged || self->selected_in_dnd)
357     return false;
358 
359   Track * track = self->track;
360   if (self->n_press == 1)
361     {
362       PROJECT->last_selection =
363         SELECTION_TYPE_TRACKLIST;
364 
365       bool ctrl = event->state & GDK_CONTROL_MASK;
366       bool shift = event->state & GDK_SHIFT_MASK;
367       tracklist_selections_handle_click (
368         track, ctrl, shift, self->dragged);
369     }
370 
371   return FALSE;
372 }
373 
374 static void
refresh_color(FolderChannelWidget * self)375 refresh_color (
376   FolderChannelWidget * self)
377 {
378   Track * track = self->track;
379   color_area_widget_setup_track (
380     self->color_top, track);
381   color_area_widget_set_color (
382     self->color_top, &track->color);
383   color_area_widget_set_color (
384     self->color_left, &track->color);
385 }
386 
387 static void
setup_folder_channel_icon(FolderChannelWidget * self)388 setup_folder_channel_icon (
389   FolderChannelWidget * self)
390 {
391   Track * track = self->track;
392   gtk_image_set_from_icon_name (
393     self->icon, track->icon_name,
394     GTK_ICON_SIZE_BUTTON);
395   gtk_widget_set_sensitive (
396     GTK_WIDGET (self->icon),
397     track_is_enabled (track));
398 }
399 
400 static void
refresh_name(FolderChannelWidget * self)401 refresh_name (FolderChannelWidget * self)
402 {
403   Track * track = self->track;
404   if (track_is_enabled (track))
405     {
406       gtk_label_set_text (
407         GTK_LABEL (self->name->label), track->name);
408     }
409   else
410     {
411       char * markup =
412         g_strdup_printf (
413           "<span foreground=\"grey\">%s</span>",
414           track->name);
415       gtk_label_set_markup (
416         GTK_LABEL (self->name->label), markup);
417     }
418 
419   gtk_label_set_angle (self->name->label, 90);
420 }
421 
422 /**
423  * Updates everything on the widget.
424  *
425  * It is reduntant but keeps code organized. Should fix if it causes lags.
426  */
427 void
folder_channel_widget_refresh(FolderChannelWidget * self)428 folder_channel_widget_refresh (
429   FolderChannelWidget * self)
430 {
431   refresh_name (self);
432   refresh_color (self);
433   setup_folder_channel_icon (self);
434   fader_buttons_widget_refresh (
435     self->fader_buttons, self->track);
436 
437 #define ICON_NAME_FOLD "fluentui-folder-regular"
438 #define ICON_NAME_FOLD_OPEN "fluentui-folder-open-regular"
439 
440   Track * track = self->track;
441   z_gtk_button_set_icon_name (
442     GTK_BUTTON (self->fold_toggle),
443     track->folded ?
444       ICON_NAME_FOLD : ICON_NAME_FOLD_OPEN);
445 
446 #undef ICON_NAME_FOLD
447 #undef ICON_NAME_FOLD_OPEN
448 
449   g_signal_handler_block (
450     self->fold_toggle,
451     self->fold_toggled_handler_id);
452   gtk_toggle_button_set_active (
453     self->fold_toggle, !track->folded);
454   g_signal_handler_unblock (
455     self->fold_toggle,
456     self->fold_toggled_handler_id);
457 
458   if (track_is_selected (track))
459     {
460       /* set selected or not */
461       gtk_widget_set_state_flags (
462         GTK_WIDGET (self),
463         GTK_STATE_FLAG_SELECTED, 0);
464     }
465   else
466     {
467       gtk_widget_unset_state_flags (
468         GTK_WIDGET (self),
469         GTK_STATE_FLAG_SELECTED);
470     }
471 }
472 
473 static void
show_context_menu(FolderChannelWidget * self)474 show_context_menu (
475   FolderChannelWidget * self)
476 {
477   GtkWidget *menu;
478   GtkMenuItem *menuitem;
479   menu = gtk_menu_new();
480   Track * track = self->track;
481 
482 #define APPEND(mi) \
483   gtk_menu_shell_append ( \
484     GTK_MENU_SHELL (menu), \
485     GTK_WIDGET (menuitem));
486 
487 #define ADD_SEPARATOR \
488   menuitem = \
489     GTK_MENU_ITEM ( \
490       gtk_separator_menu_item_new ()); \
491   gtk_widget_set_visible ( \
492     GTK_WIDGET (menuitem), true); \
493   APPEND (menuitem)
494 
495   int num_selected =
496     TRACKLIST_SELECTIONS->num_tracks;
497 
498   if (num_selected > 0)
499     {
500       char * str;
501 
502       if (track->type != TRACK_TYPE_MASTER &&
503           track->type != TRACK_TYPE_CHORD &&
504           track->type != TRACK_TYPE_MARKER &&
505           track->type != TRACK_TYPE_TEMPO)
506         {
507           /* delete track */
508           if (num_selected == 1)
509             str =
510               g_strdup (_("_Delete Track"));
511           else
512             str =
513               g_strdup (_("_Delete Tracks"));
514           menuitem =
515             z_gtk_create_menu_item (
516               str, "edit-delete", F_NO_TOGGLE,
517               "app.delete-selected-tracks");
518           g_free (str);
519           APPEND (menuitem);
520 
521           /* duplicate track */
522           if (num_selected == 1)
523             str =
524               g_strdup (_("_Duplicate Track"));
525           else
526             str =
527               g_strdup (_("_Duplicate Tracks"));
528           menuitem =
529             z_gtk_create_menu_item (
530               str, "edit-copy", F_NO_TOGGLE,
531               "app.duplicate-selected-tracks");
532           g_free (str);
533           APPEND (menuitem);
534         }
535 
536       menuitem =
537         z_gtk_create_menu_item (
538           num_selected == 1 ?
539             _("Hide Track") :
540             _("Hide Tracks"),
541           "view-hidden", F_NO_TOGGLE,
542           "app.hide-selected-tracks");
543       APPEND (menuitem);
544 
545       menuitem =
546         z_gtk_create_menu_item (
547           num_selected == 1 ?
548             _("Pin/Unpin Track") :
549             _("Pin/Unpin Tracks"),
550           "window-pin", F_NO_TOGGLE,
551           "app.pin-selected-tracks");
552       APPEND (menuitem);
553     }
554 
555   /* add solo/mute/listen */
556   ADD_SEPARATOR;
557 
558   if (tracklist_selections_contains_soloed_track (
559         TRACKLIST_SELECTIONS, F_NO_SOLO))
560     {
561       menuitem =
562         z_gtk_create_menu_item (
563           _("Solo"), "solo", F_NO_TOGGLE,
564           "app.solo-selected-tracks");
565       APPEND (menuitem);
566     }
567   if (tracklist_selections_contains_soloed_track (
568         TRACKLIST_SELECTIONS, F_SOLO))
569     {
570       menuitem =
571         z_gtk_create_menu_item (
572           _("Unsolo"), "unsolo", F_NO_TOGGLE,
573           "app.unsolo-selected-tracks");
574       APPEND (menuitem);
575     }
576 
577   if (tracklist_selections_contains_muted_track (
578         TRACKLIST_SELECTIONS, F_NO_MUTE))
579     {
580       menuitem =
581         z_gtk_create_menu_item (
582           _("Mute"), "mute", F_NO_TOGGLE,
583           "app.mute-selected-tracks");
584       APPEND (menuitem);
585     }
586   if (tracklist_selections_contains_muted_track (
587         TRACKLIST_SELECTIONS, F_MUTE))
588     {
589       menuitem =
590         z_gtk_create_menu_item (
591           _("Unmute"), "unmute", F_NO_TOGGLE,
592           "app.unmute-selected-tracks");
593       APPEND (menuitem);
594     }
595 
596   if (tracklist_selections_contains_listened_track (
597         TRACKLIST_SELECTIONS, F_NO_LISTEN))
598     {
599       menuitem =
600         z_gtk_create_menu_item (
601           _("Listen"), "listen",
602           F_NO_TOGGLE,
603           "app.listen-selected-tracks");
604       APPEND (menuitem);
605     }
606   if (tracklist_selections_contains_listened_track (
607         TRACKLIST_SELECTIONS, F_LISTEN))
608     {
609       menuitem =
610         z_gtk_create_menu_item (
611           _("Unlisten"), "unlisten",
612           F_NO_TOGGLE,
613           "app.unlisten-selected-tracks");
614       APPEND (menuitem);
615     }
616 
617   /* add enable/disable */
618   if (tracklist_selections_contains_enabled_track (
619         TRACKLIST_SELECTIONS, F_ENABLED))
620     {
621       menuitem =
622         z_gtk_create_menu_item (
623           _("Disable"), "offline",
624           F_NO_TOGGLE,
625           "app.disable-selected-tracks");
626       APPEND (menuitem);
627     }
628   else
629     {
630       menuitem =
631         z_gtk_create_menu_item (
632           _("Enable"), "online",
633           F_NO_TOGGLE,
634           "app.enable-selected-tracks");
635       APPEND (menuitem);
636     }
637 
638   ADD_SEPARATOR;
639   menuitem =
640     z_gtk_create_menu_item (
641       _("Change color..."), "color-fill",
642       F_NO_TOGGLE, "app.change-track-color");
643   APPEND (menuitem);
644 
645 #undef APPEND
646 #undef ADD_SEPARATOR
647 
648   gtk_menu_attach_to_widget (
649     GTK_MENU (menu),
650     GTK_WIDGET (self), NULL);
651   gtk_menu_popup_at_pointer (GTK_MENU (menu), NULL);
652 }
653 
654 static void
on_right_click(GtkGestureMultiPress * gesture,gint n_press,gdouble x,gdouble y,FolderChannelWidget * self)655 on_right_click (
656   GtkGestureMultiPress * gesture,
657   gint                    n_press,
658   gdouble                x,
659   gdouble                y,
660   FolderChannelWidget *        self)
661 {
662   GdkModifierType state_mask =
663     ui_get_state_mask (GTK_GESTURE (gesture));
664 
665   Track * track = self->track;
666   if (!track_is_selected (track))
667     {
668       if (state_mask & GDK_SHIFT_MASK ||
669           state_mask & GDK_CONTROL_MASK)
670         {
671           track_select (
672             track, F_SELECT, 0, 1);
673         }
674       else
675         {
676           track_select (
677             track, F_SELECT, 1, 1);
678         }
679     }
680   if (n_press == 1)
681     {
682       show_context_menu (self);
683     }
684 }
685 
686 static void
on_fold_toggled(GtkToggleButton * toggle,FolderChannelWidget * self)687 on_fold_toggled (
688   GtkToggleButton *     toggle,
689   FolderChannelWidget * self)
690 {
691   bool folded =
692     !gtk_toggle_button_get_active (toggle);
693 
694   track_set_folded (
695     self->track, folded,
696     F_TRIGGER_UNDO, F_AUTO_SELECT,
697     F_PUBLISH_EVENTS);
698 }
699 
700 static void
on_destroy(FolderChannelWidget * self)701 on_destroy (
702   FolderChannelWidget * self)
703 {
704   folder_channel_widget_tear_down (self);
705 }
706 
707 FolderChannelWidget *
folder_channel_widget_new(Track * track)708 folder_channel_widget_new (
709   Track * track)
710 {
711   FolderChannelWidget * self =
712     g_object_new (FOLDER_CHANNEL_WIDGET_TYPE, NULL);
713   self->track = track;
714 
715   setup_folder_channel_icon (self);
716   editable_label_widget_setup (
717     self->name, track,
718     (GenericStringGetter) track_get_name,
719     (GenericStringSetter)
720     track_set_name_with_action);
721 
722   self->fold_toggled_handler_id =
723     g_signal_connect (
724       self->fold_toggle, "toggled",
725       G_CALLBACK (on_fold_toggled), self);
726   g_signal_connect (
727     self, "destroy",
728     G_CALLBACK (on_destroy), NULL);
729 
730   folder_channel_widget_refresh (self);
731 
732   g_object_ref (self);
733 
734   self->setup = true;
735 
736   return self;
737 }
738 
739 /**
740  * Prepare for finalization.
741  */
742 void
folder_channel_widget_tear_down(FolderChannelWidget * self)743 folder_channel_widget_tear_down (
744   FolderChannelWidget * self)
745 {
746   if (self->setup)
747     {
748       g_object_unref (self);
749       self->track->folder_ch_widget = NULL;
750       self->setup = false;
751     }
752 }
753 
754 static void
folder_channel_widget_class_init(FolderChannelWidgetClass * _klass)755 folder_channel_widget_class_init (
756   FolderChannelWidgetClass * _klass)
757 {
758   GtkWidgetClass * klass =
759     GTK_WIDGET_CLASS (_klass);
760   resources_set_class_template (
761     klass, "folder_channel.ui");
762   gtk_widget_class_set_css_name (
763     klass, "folder_channel");
764 
765 #define BIND_CHILD(x) \
766   gtk_widget_class_bind_template_child ( \
767     klass, FolderChannelWidget, x)
768 
769   BIND_CHILD (color_left);
770   BIND_CHILD (color_top);
771   BIND_CHILD (grid);
772   BIND_CHILD (icon_and_name_event_box);
773   BIND_CHILD (name);
774   BIND_CHILD (fader_buttons);
775   BIND_CHILD (icon);
776   BIND_CHILD (fold_toggle);
777   BIND_CHILD (highlight_left_box);
778   BIND_CHILD (highlight_right_box);
779 
780 #undef BIND_CHILD
781 }
782 
783 static void
folder_channel_widget_init(FolderChannelWidget * self)784 folder_channel_widget_init (
785   FolderChannelWidget * self)
786 {
787   g_type_ensure (FADER_BUTTONS_WIDGET_TYPE);
788   g_type_ensure (COLOR_AREA_WIDGET_TYPE);
789 
790   gtk_widget_init_template (GTK_WIDGET (self));
791 
792   /* set font sizes */
793   GtkStyleContext * context =
794     gtk_widget_get_style_context (
795       GTK_WIDGET (self->name->label));
796   gtk_style_context_add_class (
797     context, "folder_channel_label");
798   gtk_label_set_max_width_chars (
799     self->name->label, 10);
800 
801   char * entry_track = g_strdup (TARGET_ENTRY_TRACK);
802   GtkTargetEntry entries[] = {
803     {
804       entry_track, GTK_TARGET_SAME_APP,
805       symap_map (ZSYMAP, TARGET_ENTRY_TRACK),
806     },
807   };
808 
809   /* set as drag source for track */
810   gtk_drag_source_set (
811     GTK_WIDGET (self->icon_and_name_event_box),
812     GDK_BUTTON1_MASK,
813     entries, G_N_ELEMENTS (entries),
814     GDK_ACTION_MOVE | GDK_ACTION_COPY);
815 
816   /* set as drag dest for folder_channel (the folder_channel will
817    * be moved based on which half it was dropped in,
818    * left or right) */
819   gtk_drag_dest_set (
820     GTK_WIDGET (self),
821     GTK_DEST_DEFAULT_MOTION |
822       GTK_DEST_DEFAULT_DROP,
823     entries, G_N_ELEMENTS (entries),
824     GDK_ACTION_MOVE | GDK_ACTION_COPY);
825   g_free (entry_track);
826 
827   self->mp =
828     GTK_GESTURE_MULTI_PRESS (
829       gtk_gesture_multi_press_new (
830         GTK_WIDGET (self)));
831   self->drag =
832     GTK_GESTURE_DRAG (
833       gtk_gesture_drag_new (
834         GTK_WIDGET (
835           self)));
836 
837   gtk_widget_set_hexpand (
838     GTK_WIDGET (self), 0);
839 
840   self->right_mouse_mp =
841     GTK_GESTURE_MULTI_PRESS (
842       gtk_gesture_multi_press_new (
843         GTK_WIDGET (self->icon_and_name_event_box)));
844   gtk_gesture_single_set_button (
845     GTK_GESTURE_SINGLE (self->right_mouse_mp),
846     GDK_BUTTON_SECONDARY);
847 
848   g_signal_connect (
849     G_OBJECT (self->mp), "pressed",
850     G_CALLBACK (on_whole_folder_channel_press), self);
851   g_signal_connect (
852     G_OBJECT (self->drag), "drag-begin",
853     G_CALLBACK (on_drag_begin), self);
854   g_signal_connect (
855     G_OBJECT (self->drag), "drag-update",
856     G_CALLBACK (on_drag_update), self);
857   g_signal_connect_after (
858     GTK_WIDGET (self->icon_and_name_event_box),
859     "drag-begin",
860     G_CALLBACK(on_dnd_drag_begin), self);
861   g_signal_connect (
862     GTK_WIDGET (self), "drag-data-received",
863     G_CALLBACK(on_drag_data_received), self);
864   g_signal_connect (
865     GTK_WIDGET (self->icon_and_name_event_box),
866     "drag-data-get",
867     G_CALLBACK (on_drag_data_get), self);
868   g_signal_connect (
869     GTK_WIDGET (self), "drag-motion",
870     G_CALLBACK (on_drag_motion), self);
871   g_signal_connect (
872     GTK_WIDGET (self), "drag-leave",
873     G_CALLBACK (on_drag_leave), self);
874   g_signal_connect (
875     GTK_WIDGET (self), "button-release-event",
876     G_CALLBACK (on_btn_release), self);
877   g_signal_connect (
878     G_OBJECT (self->right_mouse_mp), "pressed",
879     G_CALLBACK (on_right_click), self);
880 }
881