1+ #include " InsertItemScenario.h"
2+ #include " InsertItemScenario_p.h"
3+
4+ #include < algorithm>
5+ #include < utility>
6+
7+ #include < QCursor>
8+ #include < QEventLoop>
9+ #include < QLoggingCategory>
10+ #include < QQmlComponent>
11+ #include < QQuickItem>
12+ #include < QQuickWindow>
13+ #include < QVariant>
14+ #include < QtGlobal>
15+
16+ #include < CoreApi/runtimeinterface.h>
17+
18+ #include < opendspx/track.h>
19+
20+ #include < SVSCraftCore/MusicTimeline.h>
21+
22+ #include < dspxmodel/Label.h>
23+ #include < dspxmodel/LabelSequence.h>
24+ #include < dspxmodel/Model.h>
25+ #include < dspxmodel/SelectionModel.h>
26+ #include < dspxmodel/Timeline.h>
27+ #include < dspxmodel/Track.h>
28+ #include < dspxmodel/TrackList.h>
29+
30+ #include < coreplugin/DspxDocument.h>
31+ #include < coreplugin/ProjectTimeline.h>
32+
33+ #include < transactional/TransactionController.h>
34+
35+ namespace Core {
36+
37+ Q_STATIC_LOGGING_CATEGORY (lcInsertItemScenario, " diffscope.core.insertitemscenario" )
38+
39+ QObject *InsertItemScenarioPrivate::createAndPositionDialog (QQmlComponent *component, const QVariantMap &initialProperties) const {
40+ if (component->isError ()) {
41+ qFatal () << component->errorString ();
42+ }
43+ QVariantMap properties = initialProperties;
44+ properties.insert (" parent" , QVariant::fromValue (window->contentItem ()));
45+ auto dialog = component->createWithInitialProperties (properties);
46+ if (!dialog) {
47+ qFatal () << component->errorString ();
48+ }
49+ auto width = dialog->property (" width" ).toDouble ();
50+ auto height = dialog->property (" height" ).toDouble ();
51+ if (shouldDialogPopupAtCursor) {
52+ auto pos = window->mapFromGlobal (QCursor::pos ()).toPointF ();
53+ dialog->setProperty (" x" , qBound (0.0 , pos.x (), window->width () - width));
54+ dialog->setProperty (" y" , qBound (0.0 , pos.y (), window->height () - height));
55+ } else {
56+ dialog->setProperty (" x" , window->width () / 2.0 - width / 2 );
57+ if (auto popupTopMarginHint = window->property (" popupTopMarginHint" ); popupTopMarginHint.isValid ()) {
58+ dialog->setProperty (" y" , popupTopMarginHint);
59+ } else {
60+ dialog->setProperty (" y" , window->height () / 2.0 - height / 2 );
61+ }
62+ }
63+ return dialog;
64+ }
65+
66+ bool InsertItemScenarioPrivate::execDialog (QObject *dialog) const {
67+ QEventLoop eventLoop;
68+ QObject::connect (dialog, SIGNAL (accepted ()), &eventLoop, SLOT (quit ()));
69+ QObject::connect (dialog, SIGNAL (rejected ()), &eventLoop, SLOT (quit ()));
70+ QMetaObject::invokeMethod (dialog, " open" );
71+ eventLoop.exec ();
72+ return dialog->property (" result" ).toInt () == 1 ;
73+ }
74+
75+ InsertItemScenario::InsertItemScenario (QObject *parent)
76+ : QObject(parent), d_ptr(new InsertItemScenarioPrivate) {
77+ Q_D (InsertItemScenario);
78+ d->q_ptr = this ;
79+ }
80+
81+ InsertItemScenario::~InsertItemScenario () = default ;
82+
83+ QQuickWindow *InsertItemScenario::window () const {
84+ Q_D (const InsertItemScenario);
85+ return d->window ;
86+ }
87+
88+ void InsertItemScenario::setWindow (QQuickWindow *window) {
89+ Q_D (InsertItemScenario);
90+ if (d->window != window) {
91+ d->window = window;
92+ Q_EMIT windowChanged ();
93+ }
94+ }
95+
96+ ProjectTimeline *InsertItemScenario::projectTimeline () const {
97+ Q_D (const InsertItemScenario);
98+ return d->projectTimeline ;
99+ }
100+
101+ void InsertItemScenario::setProjectTimeline (ProjectTimeline *projectTimeline) {
102+ Q_D (InsertItemScenario);
103+ if (d->projectTimeline != projectTimeline) {
104+ d->projectTimeline = projectTimeline;
105+ Q_EMIT projectTimelineChanged ();
106+ }
107+ }
108+
109+ DspxDocument *InsertItemScenario::document () const {
110+ Q_D (const InsertItemScenario);
111+ return d->document ;
112+ }
113+
114+ void InsertItemScenario::setDocument (DspxDocument *document) {
115+ Q_D (InsertItemScenario);
116+ if (d->document != document) {
117+ d->document = document;
118+ Q_EMIT documentChanged ();
119+ }
120+ }
121+
122+ bool InsertItemScenario::shouldDialogPopupAtCursor () const {
123+ Q_D (const InsertItemScenario);
124+ return d->shouldDialogPopupAtCursor ;
125+ }
126+
127+ void InsertItemScenario::setShouldDialogPopupAtCursor (bool shouldDialogPopupAtCursor) {
128+ Q_D (InsertItemScenario);
129+ if (d->shouldDialogPopupAtCursor != shouldDialogPopupAtCursor) {
130+ d->shouldDialogPopupAtCursor = shouldDialogPopupAtCursor;
131+ Q_EMIT shouldDialogPopupAtCursorChanged ();
132+ }
133+ }
134+
135+ void InsertItemScenario::addTrack () const {
136+ Q_D (const InsertItemScenario);
137+ if (!d->document )
138+ return ;
139+ auto model = d->document ->model ();
140+ auto trackList = model->tracks ();
141+ dspx::Track *newTrack = nullptr ;
142+ bool success = false ;
143+ auto selectionModel = d->document ->selectionModel ();
144+ d->document ->transactionController ()->beginScopedTransaction (tr (" Adding track" ), [=, &newTrack, &success] {
145+ newTrack = model->createTrack ();
146+ newTrack->fromQDspx (QDspx::Track{});
147+ newTrack->setName (tr (" Unnamed track" ));
148+ auto insertionIndex = trackList->size ();
149+ do {
150+ auto currentTrack = qobject_cast<dspx::Track *>(selectionModel->currentItem ());
151+ if (!currentTrack) {
152+ break ;
153+ }
154+ auto index = trackList->items ().indexOf (currentTrack);
155+ if (index == -1 ) {
156+ break ;
157+ }
158+ insertionIndex = index;
159+ } while (false );
160+ if (!trackList->insertItem (insertionIndex, newTrack)) {
161+ model->destroyItem (newTrack);
162+ newTrack = nullptr ;
163+ return false ;
164+ }
165+ success = true ;
166+ return true ;
167+ }, [] {
168+ qCCritical (lcInsertItemScenario ()) << " Failed to add track in exclusive transaction" ;
169+ });
170+ if (success && newTrack) {
171+ selectionModel->select (newTrack, dspx::SelectionModel::Select | dspx::SelectionModel::SetCurrentItem | dspx::SelectionModel::ClearPreviousSelection);
172+ }
173+ }
174+
175+ void InsertItemScenario::insertTrack () const {
176+ Q_D (const InsertItemScenario);
177+ if (!d->document || !d->window )
178+ return ;
179+ auto model = d->document ->model ();
180+ auto trackList = model->tracks ();
181+ QQmlComponent component (RuntimeInterface::qmlEngine (), " DiffScope.Core" , " InsertTrackDialog" );
182+ QVariantMap properties;
183+ properties.insert (" trackCount" , trackList->size ());
184+ properties.insert (" insertionIndex" , trackList->size ());
185+ properties.insert (" insertionCount" , 1 );
186+ auto dialog = d->createAndPositionDialog (&component, properties);
187+ if (!d->execDialog (dialog))
188+ return ;
189+ auto insertionIndex = dialog->property (" insertionIndex" ).toInt ();
190+ auto insertionCount = dialog->property (" insertionCount" ).toInt ();
191+ insertionIndex = std::clamp (insertionIndex, 0 , trackList->size ());
192+ QList<dspx::Track *> newTracks;
193+ newTracks.reserve (insertionCount);
194+ bool success = false ;
195+ d->document ->transactionController ()->beginScopedTransaction (tr (" Inserting track" ), [=, &newTracks, &success] {
196+ for (int i = 0 ; i < insertionCount; ++i) {
197+ auto track = model->createTrack ();
198+ track->fromQDspx (QDspx::Track{});
199+ track->setName (tr (" Unnamed track" ));
200+ if (!trackList->insertItem (insertionIndex + i, track)) {
201+ model->destroyItem (track);
202+ return false ;
203+ }
204+ newTracks.append (track);
205+ }
206+ success = true ;
207+ return true ;
208+ }, [] {
209+ qCCritical (lcInsertItemScenario ()) << " Failed to insert track in exclusive transaction" ;
210+ });
211+ if (success && !newTracks.isEmpty ()) {
212+ auto selectionModel = d->document ->selectionModel ();
213+ bool first = true ;
214+ for (auto track : std::as_const (newTracks)) {
215+ auto command = dspx::SelectionModel::Select | dspx::SelectionModel::SetCurrentItem;
216+ if (first) {
217+ command |= dspx::SelectionModel::ClearPreviousSelection;
218+ first = false ;
219+ }
220+ selectionModel->select (track, command);
221+ }
222+ }
223+ }
224+
225+ void InsertItemScenario::insertLabel () const {
226+ Q_D (const InsertItemScenario);
227+ if (!d->document || !d->projectTimeline || !d->window )
228+ return ;
229+ auto model = d->document ->model ();
230+ auto labelSequence = model->timeline ()->labels ();
231+ QQmlComponent component (RuntimeInterface::qmlEngine (), " DiffScope.Core" , " InsertLabelDialog" );
232+ QVariantMap properties;
233+ properties.insert (" timeline" , QVariant::fromValue (d->projectTimeline ->musicTimeline ()));
234+ properties.insert (" labelPos" , d->projectTimeline ->position ());
235+ properties.insert (" labelText" , QString ());
236+ auto dialog = d->createAndPositionDialog (&component, properties);
237+ if (!d->execDialog (dialog))
238+ return ;
239+ auto labelPos = dialog->property (" labelPos" ).toInt ();
240+ auto labelText = dialog->property (" labelText" ).toString ();
241+ dspx::Label *newLabel = nullptr ;
242+ bool success = false ;
243+ d->document ->transactionController ()->beginScopedTransaction (tr (" Inserting label" ), [=, &newLabel, &success] {
244+ newLabel = model->createLabel ();
245+ newLabel->setPos (qMax (0 , labelPos));
246+ newLabel->setText (labelText);
247+ if (!labelSequence->insertItem (newLabel)) {
248+ model->destroyItem (newLabel);
249+ newLabel = nullptr ;
250+ return false ;
251+ }
252+ success = true ;
253+ return true ;
254+ }, [] {
255+ qCCritical (lcInsertItemScenario ()) << " Failed to insert label in exclusive transaction" ;
256+ });
257+ if (success && newLabel) {
258+ auto selectionModel = d->document ->selectionModel ();
259+ selectionModel->select (newLabel, dspx::SelectionModel::Select | dspx::SelectionModel::SetCurrentItem | dspx::SelectionModel::ClearPreviousSelection);
260+ }
261+ }
262+
263+ }
264+
265+ #include " moc_InsertItemScenario.cpp"
0 commit comments