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