1 /*
2  * Copyright (C) 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 "actions/midi_mapping_action.h"
21 #include "audio/channel.h"
22 #include "audio/router.h"
23 #include "gui/backend/event.h"
24 #include "gui/backend/event_manager.h"
25 #include "gui/widgets/main_window.h"
26 #include "plugins/plugin.h"
27 #include "project.h"
28 #include "utils/flags.h"
29 #include "utils/objects.h"
30 #include "zrythm_app.h"
31 
32 #include <glib/gi18n.h>
33 
34 void
midi_mapping_action_init_loaded(MidiMappingAction * self)35 midi_mapping_action_init_loaded (
36   MidiMappingAction * self)
37 {
38 }
39 
40 /**
41  * Creates a new action.
42  */
43 UndoableAction *
midi_mapping_action_new_enable(int idx,bool enable,GError ** error)44 midi_mapping_action_new_enable (
45   int       idx,
46   bool      enable,
47   GError ** error)
48 {
49   MidiMappingAction * self =
50     object_new (MidiMappingAction);
51   UndoableAction * ua = (UndoableAction *) self;
52   undoable_action_init (ua, UA_MIDI_MAPPING);
53 
54   self->type =
55     enable ?
56       MIDI_MAPPING_ACTION_ENABLE :
57       MIDI_MAPPING_ACTION_DISABLE;
58   self->idx = idx;
59 
60   return ua;
61 }
62 
63 /**
64  * Creates a new action.
65  */
66 UndoableAction *
midi_mapping_action_new_bind(midi_byte_t * buf,ExtPort * device_port,Port * dest_port,GError ** error)67 midi_mapping_action_new_bind (
68   midi_byte_t * buf,
69   ExtPort *     device_port,
70   Port *        dest_port,
71   GError **     error)
72 {
73   MidiMappingAction * self =
74     object_new (MidiMappingAction);
75   UndoableAction * ua = (UndoableAction *) self;
76   undoable_action_init (ua, UA_MIDI_MAPPING);
77 
78   self->type = MIDI_MAPPING_ACTION_BIND;
79   memcpy (self->buf, buf, 3 * sizeof (midi_byte_t));
80   if (device_port)
81     {
82       self->dev_port = ext_port_clone (device_port);
83     }
84   self->dest_port_id = dest_port->id;
85 
86   return ua;
87 }
88 
89 /**
90  * Creates a new action.
91  */
92 UndoableAction *
midi_mapping_action_new_unbind(int idx,GError ** error)93 midi_mapping_action_new_unbind (
94   int       idx,
95   GError ** error)
96 {
97   MidiMappingAction * self =
98     object_new (MidiMappingAction);
99   UndoableAction * ua = (UndoableAction *) self;
100   undoable_action_init (ua, UA_MIDI_MAPPING);
101 
102   self->type = MIDI_MAPPING_ACTION_UNBIND;
103   self->idx = idx;
104 
105   return ua;
106 }
107 
108 MidiMappingAction *
midi_mapping_action_clone(const MidiMappingAction * src)109 midi_mapping_action_clone (
110   const MidiMappingAction * src)
111 {
112   MidiMappingAction * self =
113     object_new (MidiMappingAction);
114   self->parent_instance = src->parent_instance;
115 
116   self->idx = src->idx;
117   port_identifier_copy (
118     &self->dest_port_id, &src->dest_port_id);
119   if (src->dev_port)
120     {
121       self->dev_port =
122         ext_port_clone (src->dev_port);
123     }
124   memcpy (
125     self->buf, src->buf, 3 * sizeof (midi_byte_t));
126 
127   return self;
128 }
129 
130 /**
131  * Wrapper of midi_mapping_action_new_enable().
132  */
133 bool
midi_mapping_action_perform_enable(int idx,bool enable,GError ** error)134 midi_mapping_action_perform_enable (
135   int       idx,
136   bool      enable,
137   GError ** error)
138 {
139   UNDO_MANAGER_PERFORM_AND_PROPAGATE_ERR (
140     midi_mapping_action_new_enable,
141     error, idx, enable, error);
142 }
143 
144 /**
145  * Wrapper of midi_mapping_action_new_bind().
146  */
147 bool
midi_mapping_action_perform_bind(midi_byte_t * buf,ExtPort * device_port,Port * dest_port,GError ** error)148 midi_mapping_action_perform_bind (
149   midi_byte_t *  buf,
150   ExtPort *      device_port,
151   Port *         dest_port,
152   GError **      error)
153 {
154   UNDO_MANAGER_PERFORM_AND_PROPAGATE_ERR (
155     midi_mapping_action_new_bind,
156     error, buf, device_port, dest_port, error);
157 }
158 
159 /**
160  * Wrapper of midi_mapping_action_new_unbind().
161  */
162 bool
midi_mapping_action_perform_unbind(int idx,GError ** error)163 midi_mapping_action_perform_unbind (
164   int       idx,
165   GError ** error)
166 {
167   UNDO_MANAGER_PERFORM_AND_PROPAGATE_ERR (
168     midi_mapping_action_new_unbind,
169     error, idx, error);
170 }
171 
172 static void
bind_or_unbind(MidiMappingAction * self,bool bind)173 bind_or_unbind (
174   MidiMappingAction * self,
175   bool                bind)
176 {
177   if (bind)
178     {
179       Port * port =
180         port_find_from_identifier (
181           &self->dest_port_id);
182       self->idx = MIDI_MAPPINGS->num_mappings;
183       midi_mappings_bind_device (
184         MIDI_MAPPINGS, self->buf, self->dev_port,
185         port, F_NO_PUBLISH_EVENTS);
186     }
187   else
188     {
189       MidiMapping * mapping =
190         MIDI_MAPPINGS->mappings[self->idx];
191       memcpy (
192         self->buf, mapping->key,
193         3 * sizeof (midi_byte_t));
194       if (self->dev_port)
195         {
196           ext_port_free (self->dev_port);
197         }
198       if (mapping->device_port)
199         {
200           self->dev_port =
201             ext_port_clone (mapping->device_port);
202         }
203       self->dest_port_id = mapping->dest_id;
204       midi_mappings_unbind (
205         MIDI_MAPPINGS, self->idx,
206         F_NO_PUBLISH_EVENTS);
207     }
208 }
209 
210 int
midi_mapping_action_do(MidiMappingAction * self,GError ** error)211 midi_mapping_action_do (
212   MidiMappingAction * self,
213   GError **           error)
214 {
215   switch (self->type)
216     {
217     case MIDI_MAPPING_ACTION_ENABLE:
218       midi_mapping_set_enabled (
219         MIDI_MAPPINGS->mappings[self->idx], true);
220       break;
221     case MIDI_MAPPING_ACTION_DISABLE:
222       midi_mapping_set_enabled (
223         MIDI_MAPPINGS->mappings[self->idx], false);
224       break;
225     case MIDI_MAPPING_ACTION_BIND:
226       bind_or_unbind (self, true);
227       break;
228     case MIDI_MAPPING_ACTION_UNBIND:
229       bind_or_unbind (self, false);
230       break;
231     default:
232       break;
233     }
234 
235   EVENTS_PUSH (ET_MIDI_BINDINGS_CHANGED, NULL);
236 
237   return 0;
238 }
239 
240 /**
241  * Edits the plugin.
242  */
243 int
midi_mapping_action_undo(MidiMappingAction * self,GError ** error)244 midi_mapping_action_undo (
245   MidiMappingAction * self,
246   GError **           error)
247 {
248   switch (self->type)
249     {
250     case MIDI_MAPPING_ACTION_ENABLE:
251       midi_mapping_set_enabled (
252         MIDI_MAPPINGS->mappings[self->idx], false);
253       break;
254     case MIDI_MAPPING_ACTION_DISABLE:
255       midi_mapping_set_enabled (
256         MIDI_MAPPINGS->mappings[self->idx], true);
257       break;
258     case MIDI_MAPPING_ACTION_BIND:
259       bind_or_unbind (self, false);
260       break;
261     case MIDI_MAPPING_ACTION_UNBIND:
262       bind_or_unbind (self, true);
263       break;
264     default:
265       break;
266     }
267 
268   EVENTS_PUSH (ET_MIDI_BINDINGS_CHANGED, NULL);
269 
270   return 0;
271 }
272 
273 char *
midi_mapping_action_stringize(MidiMappingAction * self)274 midi_mapping_action_stringize (
275   MidiMappingAction * self)
276 {
277   switch (self->type)
278     {
279     case MIDI_MAPPING_ACTION_ENABLE:
280       return g_strdup (_("MIDI mapping enable"));
281       break;
282     case MIDI_MAPPING_ACTION_DISABLE:
283       return g_strdup (_("MIDI mapping disable"));
284       break;
285     case MIDI_MAPPING_ACTION_BIND:
286       return g_strdup (_("MIDI mapping bind"));
287       break;
288     case MIDI_MAPPING_ACTION_UNBIND:
289       return g_strdup (_("MIDI mapping unbind"));
290       break;
291     default:
292       g_warn_if_reached ();
293       break;
294     }
295 
296   g_return_val_if_reached (NULL);
297 }
298 
299 void
midi_mapping_action_free(MidiMappingAction * self)300 midi_mapping_action_free (
301   MidiMappingAction * self)
302 {
303   object_free_w_func_and_null (
304     ext_port_free, self->dev_port);
305 
306   object_zero_and_free (self);
307 }
308