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 Zrythm.  If not, see <https://www.gnu.org/licenses/>.
18  */
19 
20 #include "audio/chord_region.h"
21 #include "audio/chord_object.h"
22 #include "audio/chord_track.h"
23 #include "gui/backend/event.h"
24 #include "gui/backend/event_manager.h"
25 #include "project.h"
26 #include "utils/arrays.h"
27 #include "utils/flags.h"
28 #include "utils/mem.h"
29 #include "utils/object_utils.h"
30 #include "utils/objects.h"
31 #include "zrythm_app.h"
32 
33 #include <glib/gi18n.h>
34 
35 /**
36  * Creates a new ZRegion for chords.
37  *
38  * @param idx Index inside chord track.
39  */
40 ZRegion *
chord_region_new(const Position * start_pos,const Position * end_pos,int idx)41 chord_region_new (
42   const Position * start_pos,
43   const Position * end_pos,
44   int              idx)
45 {
46   ZRegion * self = object_new (ZRegion);
47 
48   self->chord_objects_size = 1;
49   self->chord_objects =
50     object_new_n (
51       self->chord_objects_size, ChordObject *);
52 
53   self->id.type = REGION_TYPE_CHORD;
54 
55   region_init (
56     self, start_pos, end_pos,
57     track_get_name_hash (P_CHORD_TRACK),
58     0, idx);
59 
60   g_warn_if_fail (IS_REGION (self));
61 
62   return self;
63 }
64 
65 /**
66  * Inserts a ChordObject to the Region.
67  */
68 void
chord_region_insert_chord_object(ZRegion * self,ChordObject * chord,int pos,bool fire_events)69 chord_region_insert_chord_object (
70   ZRegion *     self,
71   ChordObject * chord,
72   int           pos,
73   bool          fire_events)
74 {
75   g_return_if_fail (IS_REGION (self));
76 
77   char str[500];
78   ChordDescriptor * cd =
79     chord_object_get_chord_descriptor (chord);
80   chord_descriptor_to_string (cd, str);
81   g_message (
82     "inserting chord '%s' (index %d) to "
83     "region '%s' at pos %d",
84     str, chord->index, self->name, pos);
85 
86   array_double_size_if_full (
87     self->chord_objects, self->num_chord_objects,
88     self->chord_objects_size, ChordObject *);
89   array_insert (
90     self->chord_objects, self->num_chord_objects,
91     pos, chord);
92 
93   for (int i = pos; i < self->num_chord_objects; i++)
94     {
95       ChordObject * co = self->chord_objects[i];
96       chord_object_set_region_and_index (
97         co, self, i);
98     }
99 
100   if (fire_events)
101     {
102       EVENTS_PUSH (
103         ET_ARRANGER_OBJECT_CREATED, chord);
104     }
105 }
106 
107 /**
108  * Adds a ChordObject to the Region.
109  */
110 void
chord_region_add_chord_object(ZRegion * self,ChordObject * chord,bool fire_events)111 chord_region_add_chord_object (
112   ZRegion *     self,
113   ChordObject * chord,
114   bool          fire_events)
115 {
116   chord_region_insert_chord_object (
117     self, chord, self->num_chord_objects,
118     fire_events);
119 }
120 
121 /**
122  * Removes a ChordObject from the Region.
123  *
124  * @param free Optionally free the ChordObject.
125  */
126 void
chord_region_remove_chord_object(ZRegion * self,ChordObject * chord,int free,bool fire_events)127 chord_region_remove_chord_object (
128   ZRegion *     self,
129   ChordObject * chord,
130   int           free,
131   bool          fire_events)
132 {
133   g_return_if_fail (
134     IS_REGION (self) && IS_CHORD_OBJECT (chord));
135 
136   char str[500];
137   ChordDescriptor * cd =
138     chord_object_get_chord_descriptor (chord);
139   chord_descriptor_to_string (cd, str);
140   g_message (
141     "removing chord '%s' (index %d) from "
142     "region '%s'",
143     str, chord->index, self->name);
144 
145   /* deselect */
146   if (CHORD_SELECTIONS)
147     {
148       arranger_object_select (
149         (ArrangerObject *) chord, F_NO_SELECT,
150         F_APPEND,
151         F_NO_PUBLISH_EVENTS);
152     }
153 
154   int pos = -1;
155   array_delete_return_pos (
156     self->chord_objects, self->num_chord_objects,
157     chord, pos);
158   g_return_if_fail (pos >= 0);
159 
160   for (int i = pos; i < self->num_chord_objects; i++)
161     {
162       chord_object_set_region_and_index (
163         self->chord_objects[i], self, i);
164     }
165 
166   if (free)
167     {
168       free_later (chord, arranger_object_free);
169     }
170 
171   if (fire_events)
172     {
173       EVENTS_PUSH (
174         ET_ARRANGER_OBJECT_REMOVED,
175         ARRANGER_OBJECT_TYPE_CHORD_OBJECT);
176     }
177 }
178 
179 bool
chord_region_validate(ZRegion * self)180 chord_region_validate (
181   ZRegion * self)
182 {
183   for (int i = 0; i < self->num_chord_objects; i++)
184     {
185       ChordObject * c = self->chord_objects[i];
186 
187       g_return_val_if_fail (c->index == i, false);
188     }
189 
190   return true;
191 }
192 
193 /**
194  * Frees members only but not the ZRegion itself.
195  *
196  * Regions should be free'd using region_free.
197  */
198 void
chord_region_free_members(ZRegion * self)199 chord_region_free_members (
200   ZRegion * self)
201 {
202   g_return_if_fail (IS_REGION (self));
203 
204   for (int i = 0; i < self->num_chord_objects; i++)
205     {
206       chord_region_remove_chord_object (
207         self, self->chord_objects[i], F_FREE,
208         F_NO_PUBLISH_EVENTS);
209     }
210 
211   object_zero_and_free (self->chord_objects);
212 }
213