Zrythm v2.0.0-DEV
a highly automated and intuitive digital audio workstation
Loading...
Searching...
No Matches
arranger_object_owner.h
1// SPDX-FileCopyrightText: © 2025 Alexandros Theodotou <alex@zrythm.org>
2// SPDX-License-Identifier: LicenseRef-ZrythmLicense
3
4#pragma once
5
6#include "dsp/file_audio_source.h"
7#include "structure/arrangement/arranger_object.h"
8#include "structure/arrangement/arranger_object_list_model.h"
9
10#define DEFINE_ARRANGER_OBJECT_OWNER_QML_PROPERTIES( \
11 ClassType, QPropertyName, ChildType) \
12public: \
13 Q_PROPERTY ( \
14 zrythm::structure::arrangement::ArrangerObjectListModel * QPropertyName \
15 READ QPropertyName CONSTANT) \
16 structure::arrangement::ArrangerObjectListModel * QPropertyName () const \
17 { \
18 return ArrangerObjectOwner<ChildType>::get_model (); \
19 }
20
21namespace zrythm::structure::arrangement
22{
23template <FinalArrangerObjectSubclass ChildT> class ArrangerObjectOwner
24{
25public:
26 using ArrangerObjectChildType = ChildT;
27 using ArrangerObjectListModel =
29
30 template <typename T>
31 explicit ArrangerObjectOwner (
32 ArrangerObjectRegistry &registry,
33 dsp::FileAudioSourceRegistry &file_audio_source_registry,
34 T &derived)
35 requires std::derived_from<T, QObject>
36 : registry_ (registry),
37 file_audio_source_registry_ (file_audio_source_registry)
38 {
39 if constexpr (std::derived_from<T, ArrangerObject>)
40 {
41 list_model_ = utils::make_qobject_unique<ArrangerObjectListModel> (
42 children_, derived);
43 }
44 else
45 {
46 list_model_ = utils::make_qobject_unique<ArrangerObjectListModel> (
47 children_, &derived);
48 }
49 }
50 virtual ~ArrangerObjectOwner () = default;
51 Z_DISABLE_COPY_MOVE (ArrangerObjectOwner)
52
53 auto &get_children_vector () const
54 {
55 return children_.get<random_access_index> ();
56 }
57
58 auto get_children_view () const
59 {
60 const auto &vec_ref = children_.get<random_access_index> ();
61 return vec_ref
62 | std::views::transform (
63 &ArrangerObjectUuidReference::get_object_as<ChildT>);
64 }
65
66 auto get_sorted_children_view () const
67 {
68 const auto &vec_ref = children_.get<sorted_index> ();
69 return vec_ref
70 | std::views::transform (
71 &ArrangerObjectUuidReference::get_object_as<ChildT>);
72 }
73
74 void add_ticks_to_children (double ticks)
75 {
76 for (auto * child : get_children_view ())
77 {
78 child->position ()->setTicks (child->position ()->ticks () + ticks);
79 }
80 }
81
86 {
87 return children_.get<uuid_hash_index> ().contains (id);
88 }
89
90 ArrangerObjectListModel * get_model () const { return list_model_.get (); }
91
92 ArrangerObjectUuidReference remove_object (const ArrangerObject::Uuid &id)
93 {
94 auto &container = ArrangerObjectOwner<ChildT>::children_;
95 auto &random_access_idx = container.template get<random_access_index> ();
96 auto it_to_remove = std::ranges::find (
97 random_access_idx, id, &ArrangerObjectUuidReference::id);
98 if (it_to_remove == random_access_idx.end ())
99 {
100 throw std::runtime_error (
101 fmt::format ("object to remove not found: {}", id));
102 }
103 z_trace ("removing object: {}", id);
104
105 auto obj_ref = *it_to_remove;
106 const auto remove_idx =
107 std::distance (random_access_idx.begin (), it_to_remove);
108
109 // Remove from model first to handle Qt signals properly
110 ArrangerObjectOwner<ChildT>::list_model_->removeRows (
111 static_cast<int> (remove_idx), 1);
112
113 return obj_ref;
114 }
115
116 void insert_object (const ArrangerObjectUuidReference &obj_ref, int idx)
117 {
118 ArrangerObjectOwner<ChildT>::list_model_->insertObject (obj_ref, idx);
119 }
120
121 void add_object (const ArrangerObjectUuidReference &obj_ref)
122 {
123 ArrangerObjectOwner<ChildT>::insert_object (
124 obj_ref,
125 static_cast<int> (ArrangerObjectOwner<ChildT>::children_.size ()));
126 }
127
128 void clear_objects () { list_model_->clear (); }
129
139 virtual std::string
140 get_field_name_for_serialization (const ChildT *) const = 0;
141
142 friend void init_from (
143 ArrangerObjectOwner &obj,
144 const ArrangerObjectOwner &other,
145 utils::ObjectCloneType clone_type)
146 {
147 if (clone_type == utils::ObjectCloneType::NewIdentity)
148 {
149 obj.children_.reserve (other.children_.size ());
150 for (const auto &child : other.get_children_view ())
151 {
152 // FIXME: not ideal to have child-specific logic here...
153 std::optional<ArrangerObjectUuidReference> clone_ref;
154 if constexpr (std::is_same_v<ChildT, AudioSourceObject>)
155 {
156 clone_ref = obj.registry_.clone_object (
157 *child, child->get_tempo_map (),
158 obj.file_audio_source_registry_, child->audio_source_ref ());
159 }
160 else if constexpr (RegionObject<ChildT>)
161 {
162 // TODO
163 continue;
164 }
165 else if constexpr (std::is_same_v<ChildT, Marker>)
166 {
167 clone_ref = obj.registry_.clone_object (
168 *child, child->get_tempo_map (), child->markerType ());
169 }
170 else
171 {
172 clone_ref =
173 obj.registry_.clone_object (*child, child->get_tempo_map ());
174 }
175
176 if (clone_ref)
177 {
178 obj.add_object (*clone_ref);
179 }
180 }
181 }
182 }
183
184private:
185 friend void to_json (nlohmann::json &j, const ArrangerObjectOwner &obj)
186 {
187 const auto children_field_name =
188 obj.get_field_name_for_serialization (static_cast<ChildT *> (nullptr));
189 j[children_field_name] = obj.children_;
190 }
191 friend void from_json (const nlohmann::json &j, ArrangerObjectOwner &obj)
192 {
193 const auto children_field_name =
194 obj.get_field_name_for_serialization (static_cast<ChildT *> (nullptr));
195 for (const auto &child_json : j.at (children_field_name))
196 {
197 const auto uuid = child_json.template get<ArrangerObjectUuid> ();
198 ArrangerObjectUuidReference obj_ref{ uuid, obj.registry_ };
199 obj.add_object (obj_ref);
200 }
201 }
202
203private:
204 ArrangerObjectRegistry &registry_;
205 dsp::FileAudioSourceRegistry &file_audio_source_registry_;
206 ArrangerObjectRefMultiIndexContainer children_;
207 utils::QObjectUniquePtr<ArrangerObjectListModel> list_model_;
208
209 BOOST_DESCRIBE_CLASS (ArrangerObjectOwner<ChildT>, (), (), (), (children_))
210};
211} // namespace zrythm::structure::arrangement
virtual std::string get_field_name_for_serialization(const ChildT *) const =0
Get the children field name to be used during serialization/deserialization.
bool contains_object(const ArrangerObject::Uuid &id) const
O(1) lookup if an object with the given ID exists.
@ NewIdentity
Creates a separately identified object.
Definition icloneable.h:30