Skip to content

Commit bf39719

Browse files
committed
Add insert item actions
1 parent 2649c6d commit bf39719

16 files changed

Lines changed: 598 additions & 2 deletions

src/plugins/coreplugin/internal/CorePlugin.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
#include <coreplugin/internal/FileBackupPage.h>
4545
#include <coreplugin/internal/FindActionsAddOn.h>
4646
#include <coreplugin/internal/GeneralPage.h>
47+
#include <coreplugin/internal/InsertItemAddOn.h>
4748
#include <coreplugin/internal/HomeAddOn.h>
4849
#include <coreplugin/internal/KeymapPage.h>
4950
#include <coreplugin/internal/LogPage.h>
@@ -288,6 +289,7 @@ namespace Core::Internal {
288289
HomeWindowInterfaceRegistry::instance()->attach<FindActionsAddOn>();
289290
ProjectWindowInterfaceRegistry::instance()->attach<FindActionsAddOn>();
290291
ProjectWindowInterfaceRegistry::instance()->attach<EditActionsAddOn>();
292+
ProjectWindowInterfaceRegistry::instance()->attach<InsertItemAddOn>();
291293
ProjectWindowInterfaceRegistry::instance()->attach<UndoAddOn>();
292294
ProjectWindowInterfaceRegistry::instance()->attach<TimelineAddOn>();
293295
ProjectWindowInterfaceRegistry::instance()->attach<ProjectStartupTimerAddOn>();
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
#include "InsertItemAddOn.h"
2+
3+
#include <QQmlComponent>
4+
5+
#include <CoreApi/runtimeinterface.h>
6+
7+
#include <QAKQuick/quickactioncontext.h>
8+
9+
#include <coreplugin/ProjectWindowInterface.h>
10+
11+
namespace Core::Internal {
12+
13+
InsertItemAddOn::InsertItemAddOn(QObject *parent) : WindowInterfaceAddOn(parent) {
14+
}
15+
16+
InsertItemAddOn::~InsertItemAddOn() = default;
17+
18+
void InsertItemAddOn::initialize() {
19+
auto windowInterface = windowHandle()->cast<ProjectWindowInterface>();
20+
QQmlComponent component(RuntimeInterface::qmlEngine(), "DiffScope.Core", "InsertItemAddOnActions");
21+
if (component.isError()) {
22+
qFatal() << component.errorString();
23+
}
24+
auto o = component.createWithInitialProperties({
25+
{"addOn", QVariant::fromValue(this)},
26+
});
27+
o->setParent(this);
28+
QMetaObject::invokeMethod(o, "registerToContext", windowInterface->actionContext());
29+
}
30+
31+
void InsertItemAddOn::extensionsInitialized() {
32+
}
33+
34+
bool InsertItemAddOn::delayedInitialize() {
35+
return WindowInterfaceAddOn::delayedInitialize();
36+
}
37+
38+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
#ifndef DIFFSCOPE_COREPLUGIN_INSERTITEMADDON_H
2+
#define DIFFSCOPE_COREPLUGIN_INSERTITEMADDON_H
3+
4+
#include <qqmlintegration.h>
5+
6+
#include <CoreApi/windowinterface.h>
7+
8+
namespace Core::Internal {
9+
10+
class InsertItemAddOn : public WindowInterfaceAddOn {
11+
Q_OBJECT
12+
QML_ELEMENT
13+
QML_UNCREATABLE("")
14+
public:
15+
explicit InsertItemAddOn(QObject *parent = nullptr);
16+
~InsertItemAddOn() override;
17+
18+
void initialize() override;
19+
void extensionsInitialized() override;
20+
bool delayedInitialize() override;
21+
};
22+
23+
}
24+
25+
#endif //DIFFSCOPE_COREPLUGIN_INSERTITEMADDON_H
Lines changed: 265 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,265 @@
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

Comments
 (0)