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 this program.  If not, see <https://www.gnu.org/licenses/>.
18  */
19 
20 #include "audio/channel.h"
21 #include "audio/chord_track.h"
22 #include "audio/region.h"
23 #include "audio/track.h"
24 #include "gui/backend/piano_roll.h"
25 #include "gui/widgets/arranger.h"
26 #include "gui/widgets/bot_dock_edge.h"
27 #include "gui/widgets/center_dock.h"
28 #include "gui/widgets/clip_editor.h"
29 #include "gui/widgets/clip_editor_inner.h"
30 #include "gui/widgets/color_area.h"
31 #include "gui/widgets/main_window.h"
32 #include "gui/widgets/midi_arranger.h"
33 #include "gui/widgets/midi_editor_space.h"
34 #include "gui/widgets/midi_modifier_arranger.h"
35 #include "gui/widgets/midi_note.h"
36 #include "gui/widgets/editor_ruler.h"
37 #include "gui/widgets/piano_roll_keys.h"
38 #include "gui/widgets/ruler.h"
39 #include "project.h"
40 #include "utils/gtk.h"
41 #include "utils/math.h"
42 #include "utils/resources.h"
43 #include "zrythm_app.h"
44 
45 #include <glib/gi18n.h>
46 
G_DEFINE_TYPE(MidiEditorSpaceWidget,midi_editor_space_widget,GTK_TYPE_BOX)47 G_DEFINE_TYPE (
48   MidiEditorSpaceWidget, midi_editor_space_widget,
49   GTK_TYPE_BOX)
50 
51 static void
52 on_midi_modifier_changed (
53   GtkComboBox *widget,
54   MidiEditorSpaceWidget * self)
55 {
56   piano_roll_set_midi_modifier (
57     PIANO_ROLL,
58     gtk_combo_box_get_active (widget));
59 }
60 
61 /**
62  * Links scroll windows after all widgets have been
63  * initialized.
64  */
65 static void
link_scrolls(MidiEditorSpaceWidget * self)66 link_scrolls (
67   MidiEditorSpaceWidget * self)
68 {
69   /* link note keys v scroll to arranger v scroll */
70   if (self->piano_roll_keys_scroll)
71     {
72       gtk_scrolled_window_set_vadjustment (
73         self->piano_roll_keys_scroll,
74         gtk_scrolled_window_get_vadjustment (
75           GTK_SCROLLED_WINDOW (
76             self->arranger_scroll)));
77     }
78 
79   /* link ruler h scroll to arranger h scroll */
80   if (MW_CLIP_EDITOR_INNER->ruler_scroll)
81     {
82       gtk_scrolled_window_set_hadjustment (
83         MW_CLIP_EDITOR_INNER->ruler_scroll,
84         gtk_scrolled_window_get_hadjustment (
85           GTK_SCROLLED_WINDOW (
86             self->arranger_scroll)));
87     }
88 
89   /* link modifier arranger h scroll to arranger h scroll */
90   if (self->modifier_arranger_scroll)
91     {
92       gtk_scrolled_window_set_hadjustment (
93         self->modifier_arranger_scroll,
94         gtk_scrolled_window_get_hadjustment (
95           GTK_SCROLLED_WINDOW (
96             self->arranger_scroll)));
97     }
98 
99 }
100 
101 static int
on_scroll(GtkWidget * widget,GdkEventScroll * event,MidiEditorSpaceWidget * self)102 on_scroll (
103   GtkWidget * widget,
104   GdkEventScroll * event,
105   MidiEditorSpaceWidget * self)
106 {
107   if (event->state & GDK_CONTROL_MASK &&
108       event->state & GDK_SHIFT_MASK)
109     {
110       midi_arranger_handle_vertical_zoom_scroll (
111         MW_MIDI_ARRANGER, event);
112     }
113 
114   return TRUE;
115 }
116 
117 /*static void*/
118 /*scroll_to_mid_note (*/
119   /*MidiEditorSpaceWidget * self)*/
120 /*{*/
121 /*}*/
122 
123 void
midi_editor_space_widget_refresh(MidiEditorSpaceWidget * self)124 midi_editor_space_widget_refresh (
125   MidiEditorSpaceWidget * self)
126 {
127   piano_roll_keys_widget_refresh (
128     self->piano_roll_keys);
129 
130   /* relink scrolls */
131   link_scrolls (self);
132 
133   /* setup combo box */
134   gtk_combo_box_set_active (
135     GTK_COMBO_BOX (
136       self->midi_modifier_chooser),
137     PIANO_ROLL->midi_modifier);
138 }
139 
140 void
midi_editor_space_widget_update_size_group(MidiEditorSpaceWidget * self,int visible)141 midi_editor_space_widget_update_size_group (
142   MidiEditorSpaceWidget * self,
143   int                     visible)
144 {
145   clip_editor_inner_widget_add_to_left_of_ruler_sizegroup (
146     MW_CLIP_EDITOR_INNER,
147     GTK_WIDGET (self->midi_vel_chooser_box),
148     visible);
149   clip_editor_inner_widget_add_to_left_of_ruler_sizegroup (
150     MW_CLIP_EDITOR_INNER,
151     GTK_WIDGET (self->midi_notes_box),
152     visible);
153 }
154 
155 void
midi_editor_space_widget_setup(MidiEditorSpaceWidget * self)156 midi_editor_space_widget_setup (
157   MidiEditorSpaceWidget * self)
158 {
159   if (self->arranger)
160     {
161       arranger_widget_setup (
162         Z_ARRANGER_WIDGET (self->arranger),
163         ARRANGER_WIDGET_TYPE_MIDI,
164         SNAP_GRID_EDITOR);
165       gtk_widget_show_all (
166         GTK_WIDGET (self->arranger));
167     }
168   if (self->modifier_arranger)
169     {
170       arranger_widget_setup (
171         Z_ARRANGER_WIDGET (self->modifier_arranger),
172         ARRANGER_WIDGET_TYPE_MIDI_MODIFIER,
173         SNAP_GRID_EDITOR);
174       gtk_widget_show_all (
175         GTK_WIDGET (self->modifier_arranger));
176     }
177 
178   piano_roll_keys_widget_setup (
179     self->piano_roll_keys);
180 
181   midi_editor_space_widget_refresh (self);
182 
183   /* scroll to note in middle */
184   GtkAdjustment * adj =
185     gtk_scrolled_window_get_vadjustment (
186       self->arranger_scroll);
187   double lower =
188     gtk_adjustment_get_lower (adj);
189   double upper =
190     gtk_adjustment_get_upper (adj);
191   gtk_adjustment_set_value (
192     adj,
193     lower + (upper - lower) / 2.0);
194 }
195 
196 static void
midi_editor_space_widget_init(MidiEditorSpaceWidget * self)197 midi_editor_space_widget_init (
198   MidiEditorSpaceWidget * self)
199 {
200   g_type_ensure (PIANO_ROLL_KEYS_WIDGET_TYPE);
201 
202   gtk_widget_init_template (GTK_WIDGET (self));
203 
204   self->arranger->type =
205     ARRANGER_WIDGET_TYPE_MIDI;
206   self->modifier_arranger->type =
207     ARRANGER_WIDGET_TYPE_MIDI_MODIFIER;
208 
209   self->arranger_and_keys_vsize_group =
210     gtk_size_group_new (GTK_SIZE_GROUP_VERTICAL);
211   gtk_size_group_add_widget (
212     self->arranger_and_keys_vsize_group,
213     GTK_WIDGET (self->arranger));
214   gtk_size_group_add_widget (
215     self->arranger_and_keys_vsize_group,
216     GTK_WIDGET (self->piano_roll_keys));
217 
218   /* setup signals */
219   g_signal_connect (
220     G_OBJECT(self->midi_modifier_chooser),
221     "changed",
222     G_CALLBACK (on_midi_modifier_changed),  self);
223   /*g_signal_connect (*/
224     /*G_OBJECT (self->piano_roll_keys_box),*/
225     /*"size-allocate",*/
226     /*G_CALLBACK (on_keys_box_size_allocate), self);*/
227   g_signal_connect (
228     G_OBJECT(self), "scroll-event",
229     G_CALLBACK (on_scroll),  self);
230 }
231 
232 static void
midi_editor_space_widget_class_init(MidiEditorSpaceWidgetClass * _klass)233 midi_editor_space_widget_class_init (
234   MidiEditorSpaceWidgetClass * _klass)
235 {
236   GtkWidgetClass * klass = GTK_WIDGET_CLASS (_klass);
237   resources_set_class_template (
238     klass,
239     "midi_editor_space.ui");
240 
241 #define BIND_CHILD(x) \
242   gtk_widget_class_bind_template_child ( \
243     klass, \
244     MidiEditorSpaceWidget, \
245     x)
246 
247   BIND_CHILD (midi_modifier_chooser);
248   BIND_CHILD (piano_roll_keys_scroll);
249   BIND_CHILD (piano_roll_keys_viewport);
250   BIND_CHILD (piano_roll_keys);
251   BIND_CHILD (midi_arranger_velocity_paned);
252   BIND_CHILD (arranger_scroll);
253   BIND_CHILD (arranger_viewport);
254   BIND_CHILD (arranger);
255   BIND_CHILD (modifier_arranger_scroll);
256   BIND_CHILD (modifier_arranger_viewport);
257   BIND_CHILD (modifier_arranger);
258   BIND_CHILD (midi_notes_box);
259   BIND_CHILD (midi_vel_chooser_box);
260 }
261