Zrythm v2.0.0-alpha.1
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#include "utils/registry_utils.h"
10#include "utils/serialization.h"
11
12#include <nlohmann/json.hpp>
13
14#define DEFINE_ARRANGER_OBJECT_OWNER_QML_PROPERTIES( \
15 ClassType, QPropertyName, ChildType) \
16public: \
17 Q_PROPERTY ( \
18 zrythm::structure::arrangement::ArrangerObjectListModel * QPropertyName \
19 READ QPropertyName CONSTANT) \
20 structure::arrangement::ArrangerObjectListModel * QPropertyName () const \
21 { \
22 return ArrangerObjectOwner<ChildType>::get_model (); \
23 }
24
25namespace zrythm::structure::arrangement
26{
27template <FinalArrangerObjectSubclass ChildT> class ArrangerObjectOwner
28{
29public:
30 using ArrangerObjectChildType = ChildT;
31 template <typename T>
32 explicit ArrangerObjectOwner (utils::IObjectRegistry &registry, T &derived)
33 requires std::derived_from<T, QObject>
34 : registry_ (registry)
35 {
36 if constexpr (std::derived_from<T, ArrangerObject>)
37 {
38 list_model_ = utils::make_qobject_unique<ArrangerObjectListModel> (
39 children_, derived);
40 }
41 else
42 {
43 list_model_ = utils::make_qobject_unique<ArrangerObjectListModel> (
44 children_, &derived);
45 }
46 }
47 virtual ~ArrangerObjectOwner () = default;
48 Q_DISABLE_COPY_MOVE (ArrangerObjectOwner)
49
50 auto &get_children_vector () const
51 {
52 return children_.get<random_access_index> ();
53 }
54
55 auto get_children_view () const
56 {
57 const auto &vec_ref = children_.get<random_access_index> ();
58 return vec_ref
59 | std::views::transform (
60 &ArrangerObjectUuidReference::get_object_as<ChildT>);
61 }
62
63 auto get_sorted_children_view () const
64 {
65 const auto &vec_ref = children_.get<sorted_index> ();
66 return vec_ref
67 | std::views::transform (
68 &ArrangerObjectUuidReference::get_object_as<ChildT>);
69 }
70
71 void add_ticks_to_children (double ticks)
72 {
73 for (auto * child : get_children_view ())
74 {
75 child->position ()->setTicks (child->position ()->ticks () + ticks);
76 }
77 }
78
83 {
84 return children_.get<uuid_hash_index> ().contains (id);
85 }
86
87 ArrangerObjectListModel * get_model () const { return list_model_.get (); }
88
89 ArrangerObjectUuidReference remove_object (const ArrangerObject::Uuid &id)
90 {
91 auto &container = ArrangerObjectOwner<ChildT>::children_;
92 auto &random_access_idx = container.template get<random_access_index> ();
93 auto it_to_remove = std::ranges::find (
94 random_access_idx, id, &ArrangerObjectUuidReference::id);
95 if (it_to_remove == random_access_idx.end ())
96 {
97 throw std::runtime_error (
98 fmt::format ("object to remove not found: {}", id));
99 }
100 z_trace ("removing object: {}", id);
101
102 auto obj_ref = *it_to_remove;
103 const auto remove_idx =
104 std::distance (random_access_idx.begin (), it_to_remove);
105
106 // Remove from model first to handle Qt signals properly
107 ArrangerObjectOwner<ChildT>::list_model_->removeRows (
108 static_cast<int> (remove_idx), 1);
109
110 return obj_ref;
111 }
112
113 void insert_object (const ArrangerObjectUuidReference &obj_ref, int idx)
114 {
115 ArrangerObjectOwner<ChildT>::list_model_->insertObject (obj_ref, idx);
116 }
117
118 void add_object (const ArrangerObjectUuidReference &obj_ref)
119 {
120 ArrangerObjectOwner<ChildT>::insert_object (
121 obj_ref,
122 static_cast<int> (ArrangerObjectOwner<ChildT>::children_.size ()));
123 }
124
125 void clear_objects () { list_model_->clear (); }
126
136 virtual std::string
137 get_field_name_for_serialization (const ChildT *) const = 0;
138
139 friend void init_from (
140 ArrangerObjectOwner &obj,
141 const ArrangerObjectOwner &other,
142 utils::ObjectCloneType clone_type)
143 {
144 if (clone_type == utils::ObjectCloneType::NewIdentity)
145 {
146 obj.children_.reserve (other.children_.size ());
147 for (const auto &child : other.get_children_view ())
148 {
149 // FIXME: not ideal to have child-specific logic here...
150 std::optional<ArrangerObjectUuidReference> clone_ref;
151 if constexpr (std::is_same_v<ChildT, AudioSourceObject>)
152 {
153 clone_ref = utils::clone_object (
154 *child, obj.registry_, utils::ObjectCloneType::NewIdentity,
155 child->get_tempo_map (), obj.registry_,
156 child->audio_source_ref ());
157 }
158 else if constexpr (RegionObject<ChildT>)
159 {
160 z_warning ("RegionObject clone not implemented - skipping");
161 continue;
162 }
163 else if constexpr (std::is_same_v<ChildT, Marker>)
164 {
165 clone_ref = utils::clone_object (
166 *child, obj.registry_, utils::ObjectCloneType::NewIdentity,
167 child->get_tempo_map (), child->markerType ());
168 }
169 else
170 {
171 clone_ref = utils::clone_object (
172 *child, obj.registry_, utils::ObjectCloneType::NewIdentity,
173 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 utils::IObjectRegistry &registry_;
205 ArrangerObjectRefMultiIndexContainer children_;
206 utils::QObjectUniquePtr<ArrangerObjectListModel> list_model_;
207
208 BOOST_DESCRIBE_CLASS (ArrangerObjectOwner<ChildT>, (), (), (), (children_))
209};
210} // 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.
Abstract interface for a UUID-keyed object registry.
@ NewIdentity
Creates a separately identified object.
Definition icloneable.h:30