1 /*
2 * Copyright (C) 2019-2020 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/engine.h"
21 #include "audio/midi.h"
22 #include "gui/widgets/dialogs/bind_cc_dialog.h"
23 #include "project.h"
24 #include "utils/error.h"
25 #include "utils/io.h"
26 #include "utils/resources.h"
27 #include "utils/ui.h"
28 #include "zrythm_app.h"
29
30 #include <gtk/gtk.h>
31 #include <glib/gi18n.h>
32
G_DEFINE_TYPE(BindCcDialogWidget,bind_cc_dialog_widget,GTK_TYPE_DIALOG)33 G_DEFINE_TYPE (
34 BindCcDialogWidget,
35 bind_cc_dialog_widget,
36 GTK_TYPE_DIALOG)
37
38 static void
39 on_ok_clicked (
40 GtkButton * btn,
41 BindCcDialogWidget * self)
42 {
43 if (self->perform_action)
44 {
45 if (self->cc[0])
46 {
47 GError * err = NULL;
48 bool ret =
49 midi_mapping_action_perform_bind (
50 self->cc, NULL, self->port, &err);
51 if (!ret)
52 {
53 HANDLE_ERROR (
54 err, "%s",
55 _("Failed to bind mapping"));
56 }
57 }
58 }
59
60 gtk_dialog_response (
61 GTK_DIALOG (self), GTK_RESPONSE_ACCEPT);
62 }
63
64 static void
on_cancel_clicked(GtkButton * btn,BindCcDialogWidget * self)65 on_cancel_clicked (
66 GtkButton * btn,
67 BindCcDialogWidget * self)
68 {
69 gtk_dialog_response (
70 GTK_DIALOG (self), GTK_RESPONSE_CANCEL);
71 }
72
73 static gboolean
tick_cb(GtkWidget * widget,GdkFrameClock * frame_clock,BindCcDialogWidget * self)74 tick_cb (
75 GtkWidget * widget,
76 GdkFrameClock * frame_clock,
77 BindCcDialogWidget * self)
78 {
79 if (AUDIO_ENGINE->last_cc[0] != 0)
80 {
81 memcpy (
82 self->cc, AUDIO_ENGINE->last_cc,
83 sizeof (midi_byte_t) * 3);
84 char ctrl_change[60];
85 int ctrl_change_ch =
86 midi_ctrl_change_get_ch_and_description (
87 AUDIO_ENGINE->last_cc, ctrl_change);
88
89 bool port_is_toggle =
90 self->port &&
91 self->port->id.flags & PORT_FLAG_TOGGLE;
92
93 gtk_widget_set_sensitive (
94 GTK_WIDGET (self->ok_btn), true);
95
96 if (ctrl_change_ch > 0)
97 {
98 char str[100];
99 sprintf (
100 str, "<b>Ch%d - %s</b>",
101 ctrl_change_ch, ctrl_change);
102 gtk_label_set_markup (
103 self->lbl, str);
104 }
105 else if (port_is_toggle)
106 {
107 char str[100];
108 sprintf (
109 str,
110 "<b>%02X %02X %02X</b>",
111 self->cc[0], self->cc[1], self->cc[2]);
112 gtk_label_set_markup (
113 self->lbl, str);
114 }
115 else
116 {
117 char str[100];
118 sprintf (
119 str,
120 "<b><span foreground='red'>%s</span></b>",
121 _("Not a control change event"));
122 gtk_label_set_markup (
123 self->lbl, str);
124
125 gtk_widget_set_sensitive (
126 GTK_WIDGET (self->ok_btn), false);
127 }
128 }
129
130 return G_SOURCE_CONTINUE;
131 }
132
133 /**
134 * Creates a new bind_cc dialog.
135 */
136 BindCcDialogWidget *
bind_cc_dialog_widget_new(Port * port,bool perform_action)137 bind_cc_dialog_widget_new (
138 Port * port,
139 bool perform_action)
140 {
141 BindCcDialogWidget * self =
142 g_object_new (BIND_CC_DIALOG_WIDGET_TYPE, NULL);
143
144 self->port = port;
145 self->perform_action = perform_action;
146
147 return self;
148 }
149
150 static void
finalize(BindCcDialogWidget * self)151 finalize (
152 BindCcDialogWidget * self)
153 {
154 AUDIO_ENGINE->capture_cc = 0;
155
156 G_OBJECT_CLASS (
157 bind_cc_dialog_widget_parent_class)->
158 finalize (G_OBJECT (self));
159 }
160
161 static void
bind_cc_dialog_widget_class_init(BindCcDialogWidgetClass * _klass)162 bind_cc_dialog_widget_class_init (
163 BindCcDialogWidgetClass * _klass)
164 {
165 GtkWidgetClass * klass = GTK_WIDGET_CLASS (_klass);
166 resources_set_class_template (
167 klass, "bind_cc_dialog.ui");
168
169 #define BIND_CHILD(x) \
170 gtk_widget_class_bind_template_child ( \
171 klass, BindCcDialogWidget, x)
172
173 BIND_CHILD (ok_btn);
174 BIND_CHILD (cancel_btn);
175 BIND_CHILD (lbl);
176
177 GObjectClass * oklass = G_OBJECT_CLASS (klass);
178 oklass->finalize =
179 (GObjectFinalizeFunc) finalize;
180 }
181
182 static void
bind_cc_dialog_widget_init(BindCcDialogWidget * self)183 bind_cc_dialog_widget_init (
184 BindCcDialogWidget * self)
185 {
186 gtk_widget_init_template (GTK_WIDGET (self));
187
188 AUDIO_ENGINE->capture_cc = 1;
189 AUDIO_ENGINE->last_cc[0] = 0;
190 AUDIO_ENGINE->last_cc[1] = 0;
191 AUDIO_ENGINE->last_cc[2] = 0;
192
193 gtk_widget_add_tick_callback (
194 GTK_WIDGET (self),
195 (GtkTickCallback) tick_cb,
196 self, NULL);
197
198 g_signal_connect (
199 G_OBJECT (self->ok_btn), "clicked",
200 G_CALLBACK (on_ok_clicked), self);
201 g_signal_connect (
202 G_OBJECT (self->cancel_btn), "clicked",
203 G_CALLBACK (on_cancel_clicked), self);
204 }
205