1 /*
2  * Copyright (C) 2020-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 "actions/port_connection_action.h"
21 #include "audio/port.h"
22 #include "audio/router.h"
23 #include "gui/backend/event.h"
24 #include "gui/backend/event_manager.h"
25 #include "project.h"
26 #include "utils/flags.h"
27 #include "utils/objects.h"
28 #include "zrythm_app.h"
29 
30 #include <glib/gi18n.h>
31 
32 void
port_connection_action_init_loaded(PortConnectionAction * self)33 port_connection_action_init_loaded (
34   PortConnectionAction * self)
35 {
36   /* no need */
37 }
38 
39 /**
40  * Create a new action.
41  */
42 UndoableAction *
port_connection_action_new(PortConnectionActionType type,PortIdentifier * src_id,PortIdentifier * dest_id,float new_val,GError ** error)43 port_connection_action_new (
44   PortConnectionActionType type,
45   PortIdentifier *         src_id,
46   PortIdentifier *         dest_id,
47   float                    new_val,
48   GError **                error)
49 {
50   PortConnectionAction * self =
51     object_new (PortConnectionAction);
52   UndoableAction * ua = (UndoableAction *) self;
53   undoable_action_init (ua, UA_PORT_CONNECTION);
54 
55   /* check for existing connection to get values */
56   const PortConnection * conn =
57     port_connections_manager_find_connection (
58       PORT_CONNECTIONS_MGR, src_id, dest_id);
59   if (conn)
60     self->connection = port_connection_clone (conn);
61   else
62     self->connection =
63       port_connection_new (
64         src_id, dest_id, 1.f, F_NOT_LOCKED,
65         F_ENABLE);
66   self->type = type;
67   self->val = new_val;
68 
69   return ua;
70 }
71 
72 PortConnectionAction *
port_connection_action_clone(const PortConnectionAction * src)73 port_connection_action_clone (
74   const PortConnectionAction * src)
75 {
76   PortConnectionAction * self =
77     object_new (PortConnectionAction);
78   self->parent_instance = src->parent_instance;
79 
80   self->type = src->type;
81   self->connection =
82     port_connection_clone (src->connection);
83   self->val = src->val;
84 
85   return self;
86 }
87 
88 bool
port_connection_action_perform(PortConnectionActionType type,PortIdentifier * src_id,PortIdentifier * dest_id,float new_val,GError ** error)89 port_connection_action_perform (
90   PortConnectionActionType type,
91   PortIdentifier *         src_id,
92   PortIdentifier *         dest_id,
93   float                    new_val,
94   GError **                error)
95 {
96   UNDO_MANAGER_PERFORM_AND_PROPAGATE_ERR (
97     port_connection_action_new,
98     error, type, src_id, dest_id, new_val,
99     error);
100 }
101 
102 static int
port_connection_action_do_or_undo(PortConnectionAction * self,bool _do,GError ** error)103 port_connection_action_do_or_undo (
104   PortConnectionAction * self,
105   bool                   _do,
106   GError **              error)
107 {
108   Port * src =
109     port_find_from_identifier (
110       self->connection->src_id);
111   Port * dest =
112     port_find_from_identifier (
113       self->connection->dest_id);
114   g_return_val_if_fail (src && dest, -1);
115   PortConnection * prj_connection =
116     port_connections_manager_find_connection (
117       PORT_CONNECTIONS_MGR,
118       self->connection->src_id,
119       self->connection->dest_id);
120 
121   switch (self->type)
122     {
123     case PORT_CONNECTION_CONNECT:
124     case PORT_CONNECTION_DISCONNECT:
125       if ((self->type ==
126              PORT_CONNECTION_CONNECT && _do) ||
127           (self->type ==
128              PORT_CONNECTION_DISCONNECT && !_do))
129         {
130           if (!ports_can_be_connected (src, dest))
131             {
132               g_warning (
133                 "ports cannot be connected");
134               return -1;
135             }
136           port_connections_manager_ensure_connect (
137             PORT_CONNECTIONS_MGR,
138             &src->id, &dest->id, 1.f,
139             F_NOT_LOCKED, F_ENABLE);
140 
141           /* set base value if cv -> control */
142           if (src->id.type == TYPE_CV &&
143               dest->id.type == TYPE_CONTROL)
144             dest->base_value = dest->control;
145         }
146       else
147         {
148           port_connections_manager_ensure_disconnect (
149             PORT_CONNECTIONS_MGR,
150             &src->id, &dest->id);
151         }
152       router_recalc_graph (ROUTER, F_NOT_SOFT);
153       break;
154     case PORT_CONNECTION_ENABLE:
155       prj_connection->enabled = _do ? true : false;
156       break;
157     case PORT_CONNECTION_DISABLE:
158       prj_connection->enabled = _do ? false : true;
159       break;
160     case PORT_CONNECTION_CHANGE_MULTIPLIER:
161       {
162         float val_before =
163           prj_connection->multiplier;
164         prj_connection->multiplier = self->val;
165         self->val = val_before;
166       }
167       break;
168     default:
169       break;
170     }
171 
172   EVENTS_PUSH (ET_PORT_CONNECTION_CHANGED, NULL);
173 
174   return 0;
175 }
176 
177 int
port_connection_action_do(PortConnectionAction * self,GError ** error)178 port_connection_action_do (
179   PortConnectionAction * self,
180   GError **              error)
181 {
182   return
183     port_connection_action_do_or_undo (
184       self, true, error);
185 }
186 
187 int
port_connection_action_undo(PortConnectionAction * self,GError ** error)188 port_connection_action_undo (
189   PortConnectionAction * self,
190   GError **              error)
191 {
192   return
193     port_connection_action_do_or_undo (
194       self, false, error);
195 }
196 
197 char *
port_connection_action_stringize(PortConnectionAction * self)198 port_connection_action_stringize (
199   PortConnectionAction * self)
200 {
201   switch (self->type)
202     {
203     case PORT_CONNECTION_CONNECT:
204       return g_strdup (_("Connect ports"));
205       break;
206     case PORT_CONNECTION_DISCONNECT:
207       return g_strdup (_("Disconnect ports"));
208       break;
209     case PORT_CONNECTION_ENABLE:
210       return g_strdup (_("Enable port connection"));
211       break;
212     case PORT_CONNECTION_DISABLE:
213       return g_strdup (_("Disable port connection"));
214       break;
215     case PORT_CONNECTION_CHANGE_MULTIPLIER:
216       return g_strdup (_("Change port connection"));
217       break;
218     default:
219       g_warn_if_reached ();
220     }
221   g_return_val_if_reached (NULL);
222 }
223 
224 void
port_connection_action_free(PortConnectionAction * self)225 port_connection_action_free (
226   PortConnectionAction * self)
227 {
228   object_free_w_func_and_null (
229     port_connection_free, self->connection);
230 
231   object_zero_and_free (self);
232 }
233