diff --git a/.github/actions/initialize-build-environment/action.yml b/.github/actions/initialize-build-environment/action.yml
index 18aed6d3..12e10df1 100644
--- a/.github/actions/initialize-build-environment/action.yml
+++ b/.github/actions/initialize-build-environment/action.yml
@@ -39,7 +39,7 @@ runs:
HOMEBREW_NO_INSTALL_CLEANUP: 1
run: |
brew update
- brew install ninja
+ brew install ninja ccache imagemagick create-dmg
- name: Install Qt
uses: jurplel/install-qt-action@v4
diff --git a/.github/actions/initialize-vcpkg/action.yml b/.github/actions/initialize-vcpkg/action.yml
index e19dd5ab..1d083ef5 100644
--- a/.github/actions/initialize-vcpkg/action.yml
+++ b/.github/actions/initialize-vcpkg/action.yml
@@ -31,7 +31,7 @@ runs:
QT_DIR: ${{ env.Qt_ROOT_DIR }}/lib/cmake/Qt6
Qt6_DIR: ${{ env.Qt_ROOT_DIR }}/lib/cmake/Qt6
VCPKG_CMAKE_RELEASE_BUILD_TYPE: "RelWithDebInfo"
- VCPKG_KEEP_ENV_VARS: "QT_DIR;Qt6_DIR;VCPKG_CMAKE_RELEASE_BUILD_TYPE;VCPKG_BUILD_TYPE"
+ VCPKG_KEEP_ENV_VARS: "QT_DIR;Qt6_DIR;VCPKG_CMAKE_RELEASE_BUILD_TYPE;VCPKG_BUILD_TYPE;MACOSX_DEPLOYMENT_TARGET"
shell: pwsh
run: |
if (!(Test-Path $env:VCPKG_DEFAULT_BINARY_CACHE)) {
diff --git a/.github/instructions/viewmodel-binding.instructions.md b/.github/instructions/viewmodel-binding.instructions.md
new file mode 100644
index 00000000..50eddd12
--- /dev/null
+++ b/.github/instructions/viewmodel-binding.instructions.md
@@ -0,0 +1,103 @@
+# View-Model ↔ Document Binding Prompt
+
+Use this prompt to guide new view-model bindings for future document elements. Keep property specifics out; focus on state design, controller-driven transitions, transactions, and synchronization patterns. Derive patterns from existing implementations (e.g., `TrackViewModelContextData`, `LabelViewModelContextData`, `TempoViewModelContextData`).
+
+## Core Goals
+- Mirror document collections to view-model collections with clear ownership and mapping tables in both directions.
+- Drive UI interactions through controllers; never mutate document state directly from QML.
+- Guard against infinite loops by conditioning view→doc updates on active states.
+- Wrap mutating operations in transactions with start/commit/abort semantics tied to state entry/exit.
+
+- Instrument every state with entry/exit logging (use `qCInfo` with a per-context logging category) to trace flows during debugging, even for states without handlers.
+
+## State Machine Design
+- Build a `QStateMachine` with explicit states for idle, rubber-band selection, move/drag, edit/adjust flows, and per-property operations as needed.
+- Keep transitions driven by signals emitted from interaction controllers and internal guards (e.g., started/not-started, commit/abort, finish).
+- Example (move flow, list rotation):
+ - `idle` → `movePending` on `moveTransactionWillStart` (from controller).
+ - `movePending` → `moveProcessing` on `moveTransactionStarted`; → `idle` on `moveTransactionNotStarted`.
+ - `moveProcessing` → `moveCommitting` on `moveTransactionWillCommit`; → `moveAborting` on `moveTransactionWillAbort`.
+ - `moveCommitting`/`moveAborting` → `idle` (reset guards).
+- Example (edit flow, text-like):
+ - `idle` → `namePending` on `nameTransactionWillStart`.
+ - `namePending` → `nameProgressing` on `nameTransactionStarted`; → `idle` if not started.
+ - `nameProgressing` → `nameCommitting` on commit; → `nameAborting` on abort.
+ - `nameCommitting`/`nameAborting` → `idle`.
+- Rubber-band selection: `idle` → `rubberBandDragging` on start, back to `idle` on finish.
+
+## Controller-Driven Transitions
+- Wire controller signals to emit local signals that the state machine consumes (two patterns):
+ 1) **Started/Committed/Aborted**: drag/move and text edits use started/commit/abort triplets.
+ 2) **Started/Finished**: toggle/slider/height edits use started/finished pairs.
+- Set the current target item/index when the controller signals a start; clear it on commit/finish/abort handlers.
+- For rotations or list moves: only propagate view→doc when in the move-processing state; otherwise apply doc→view rotations.
+
+## Transactions
+- Begin a transaction when entering the corresponding "pending" state. Abort immediately if the controller could not start.
+- On commit states: if the new value differs, write to the document and `commitTransaction` with a descriptive label; otherwise `abortTransaction`.
+- On abort states: always `abortTransaction` and reset local guards (`target`, flags like `moveChanged`).
+- Track whether any change occurred during move/drag (`moveChanged`) to avoid committing no-op transactions.
+
+## Synchronization Patterns
+- Maintain bidirectional maps: `doc -> view` and `view -> doc`. Insert/remove bindings on collection signals (`itemInserted`/`itemRemoved`), not "aboutTo" when you need the item fully constructed.
+- When binding a new item:
+ - Create the view-model item, insert into both maps and the view-model collection at the correct index.
+ - Connect doc→view signals to update view items, guarded by equality checks.
+ - Connect view→doc signals but gate them with state checks (only honor during the relevant progressing/doing states; otherwise revert the view to the doc value).
+ - Initialize view properties from the doc model after wiring connections.
+- Selection sync: listen to document selection model `itemSelected` and mark the view item selected; initialize selection for pre-selected items after binding.
+- Rotation sync: doc→view rotations apply when *not* moving; view→doc rotations apply only while the move state is active, and should mark a change flag.
+
+## Example Snippets
+- **Doc→View guarded update** (avoid loops):
+ ```cpp
+ connect(control, &ControlType::propertyChanged, viewItem, [=](auto value) {
+ if (viewItem->property() == value) return;
+ viewItem->setProperty(value);
+ });
+ ```
+- **View→Doc gated by state**:
+ ```cpp
+ connect(viewItem, &ViewType::propertyChanged, docItem, [=] {
+ if (!stateMachine->configuration().contains(propertyProgressingState)) {
+ viewItem->setProperty(docItem->property());
+ return;
+ }
+ // defer actual write to commit handler
+ });
+ ```
+- **Transaction commit handler**:
+ ```cpp
+ void ContextData::onNameCommittingStateEntered() {
+ if (!target || nameTxId == Invalid) { target = {}; return; }
+ auto viewItem = viewMap.value(target);
+ if (viewItem->name() == target->name()) {
+ tx->abortTransaction(nameTxId);
+ } else {
+ target->setName(viewItem->name());
+ tx->commitTransaction(nameTxId, tr("Renaming item"));
+ }
+ nameTxId = {}; target = {};
+ }
+ ```
+- **Rotate handling**:
+ ```cpp
+ connect(docList, &List::rotated, this, [=](int l, int m, int r) {
+ if (stateMachine->configuration().contains(moveProcessingState)) return;
+ viewList->rotate(l, m, r);
+ });
+ connect(viewList, &ViewList::rotated, this, [=](int l, int m, int r) {
+ if (!stateMachine->configuration().contains(moveProcessingState)) return;
+ moveChanged = true;
+ docList->rotate(l, m, r);
+ });
+ ```
+
+## Implementation Checklist
+- Define states and transitions before binding to controllers; start the state machine immediately.
+- Create controllers via context helper methods; hook all relevant signals to emit local transition signals and set the current target.
+- Bind document collections first, then replay existing selection to the view.
+- For each commit/finish handler: compare values, write document, commit transaction; otherwise abort. Always reset `target` and flags.
+- Keep all strings ASCII; add concise comments only where non-obvious.
+
+Use this prompt verbatim when extending bindings to new document elements to maintain consistent interaction, transaction, and synchronization behavior across the codebase.
diff --git a/.github/workflows/dev-build.yml b/.github/workflows/dev-build.yml
index 03dca6ce..7dd6dc8c 100644
--- a/.github/workflows/dev-build.yml
+++ b/.github/workflows/dev-build.yml
@@ -16,6 +16,7 @@ on:
- 'LICENSE'
- 'crowdin.yml'
- '.github/**'
+ - '**/translations/*.ts'
workflow_dispatch:
inputs:
identifier:
@@ -40,16 +41,16 @@ jobs:
os:
- windows-2025
# - ubuntu-24.04
- # - macos-15
-
+ - macos-15
env:
- QT_VERSION: 6.9.2
+ QT_VERSION: 6.10.1
VCPKG_REF: 74e6536215718009aae747d86d84b78376bf9e09
INNOSETUP_REF: is-6_5_4
VERSION_IDENTIFIER: ${{ github.sha }}${{ github.event.inputs.identifier && '.' || '' }}${{ github.event.inputs.identifier }}
BUILD_DIR: ${{ github.workspace }}/build/build
INSTALL_DIR: ${{ github.workspace }}/build/install
CCACHE_DIR: ${{ github.workspace }}/build/ccache
+ MACOSX_DEPLOYMENT_TARGET: 13.0
runs-on: ${{ matrix.os }}
@@ -87,7 +88,10 @@ jobs:
-InstallDir $env:INSTALL_DIR `
-VersionIdentifier $env:VERSION_IDENTIFIER `
${{ github.event.inputs.use_ccache == 'true' && '-CCache' || '' }}
- Write-Output ARTIFACT_NAME=$($output.ApplicationName)_$($output.Semver -replace '[\.\-\+]', '_') >> $env:GITHUB_ENV
+ Write-Output ARTIFACT_NAME=$($output.ApplicationName)_$($output.Semver -replace '[\.\-\+]', '_')_${{ runner.os }}_${{ runner.arch }} >> $env:GITHUB_ENV
+ Write-Output APPLICATION_SEMVER=$($output.Semver) >> $env:GITHUB_ENV
+ Write-Output APPLICATION_DISPLAY_NAME=$($output.ApplicationDisplayName) >> $env:GITHUB_ENV
+ Write-Output APPLICATION_NAME=$($output.ApplicationName) >> $env:GITHUB_ENV
Write-Output INSTALLER_FILE_BASE=$($output.InstallerFileBase) >> $env:GITHUB_ENV
- name: Save CCache cache
@@ -102,11 +106,22 @@ jobs:
$output = & ./scripts/ci/Collect-Symbol-Files.ps1 -VcpkgRootDir $env:VCPKG_ROOT_DIR -InstallDir $env:INSTALL_DIR
Write-Output SYMBOL_FILES_PATH=$($output.Path) >> $env:GITHUB_ENV
- - name: Pack
+ - name: Create InnoSetup installer (Windows)
+ if: ${{ runner.os == 'Windows' }}
run: |
$output = & ./scripts/ci/Pack.ps1 -BuildDir $env:BUILD_DIR -InstallerFileBase $env:INSTALLER_FILE_BASE -InnoSetupCommit $env:INNOSETUP_REF
Write-Output PACKAGE_PATH=$($output.Path) >> $env:GITHUB_ENV
+ - name: Create DMG installer (macOS)
+ if: ${{ runner.os == 'macOS' }}
+ run: |
+ $output = & ./scripts/ci/Create-DMG.ps1 `
+ -AppPath $(Join-Path $env:INSTALL_DIR $env:APPLICATION_NAME'.app') `
+ -Semver $env:APPLICATION_SEMVER `
+ -ApplicationDisplayName $env:APPLICATION_DISPLAY_NAME `
+ -InstallerFileBase $env:INSTALLER_FILE_BASE
+ Write-Output PACKAGE_PATH=$($output) >> $env:GITHUB_ENV
+
- name: Upload symbol files
uses: actions/upload-artifact@v4
with:
diff --git a/crowdin.yml b/crowdin.yml
index 14e32c6a..35758df7 100644
--- a/crowdin.yml
+++ b/crowdin.yml
@@ -1,13 +1,5 @@
files:
- - source: /src/plugins/coreplugin/res/translations/Core_en_US.ts
- translation: /src/plugins/coreplugin/res/translations/Core_%locale_with_underscore%.ts
- - source: /src/plugins/audio/res/translations/org.diffscope.audio_en_US.ts
- translation: /src/plugins/audio/res/translations/org.diffscope.audio_%locale_with_underscore%.ts
- - source: /src/plugins/welcomewizard/res/translations/org.diffscope.welcomewizard_en_US.ts
- translation: /src/plugins/welcomewizard/res/translations/org.diffscope.welcomewizard_%locale_with_underscore%.ts
- - source: /src/plugins/achievement/res/translations/org.diffscope.achievement_en_US.ts
- translation: /src/plugins/achievement/res/translations/org.diffscope.achievement_%locale_with_underscore%.ts
- - source: /src/plugins/maintenance/res/translations/org.diffscope.maintenance_en_US.ts
- translation: /src/plugins/maintenance/res/translations/org.diffscope.maintenance_%locale_with_underscore%.ts
- - source: /src/libs/application/uishell/share/translations/uishell_en_US.ts
- translation: /src/libs/application/uishell/share/translations/uishell_%locale_with_underscore%.ts
+ - source: '/**/translations/*_en_US.ts'
+ translation: '/%original_path%/%file_name%_%locale_with_underscore%.ts'
+ translation_replace:
+ '_en_US': ''
diff --git a/scripts/ci/Build.ps1 b/scripts/ci/Build.ps1
index d7bee6de..0afa4394 100644
--- a/scripts/ci/Build.ps1
+++ b/scripts/ci/Build.ps1
@@ -78,6 +78,8 @@ Write-Host "Semver: $semver"
$installerFileBase = "${applicationName}_$($semver -replace '[\.\-\+]', '_')_installer"
+$depsDir = (Get-ChildItem -Path $(Join-Path $VcpkgRootDir installed) | Where-Object {$_.Name -ne "vcpkg"})[0].FullName
+
cmake -S . -B $(Resolve-Path $BuildDir) -G Ninja `
-DCMAKE_BUILD_TYPE=RelWithDebInfo `
"-DCMAKE_TOOLCHAIN_FILE=$(Join-Path $VcpkgRootDir scripts/buildsystems/vcpkg.cmake)" `
@@ -85,6 +87,8 @@ cmake -S . -B $(Resolve-Path $BuildDir) -G Ninja `
"-DCMAKE_CXX_COMPILER_LAUNCHER=$($CCache ? 'ccache' : '')" `
-DCMAKE_MSVC_DEBUG_INFORMATION_FORMAT=Embedded `
-DCK_ENABLE_CONSOLE:BOOL=FALSE `
+ -DQT_NO_PRIVATE_MODULE_WARNING:BOOL=ON `
+ "-DQMSETUP_APPLOCAL_DEPS_PATHS_RELWITHDEBINFO=$(Join-Path $depsDir lib)" `
-DAPPLICATION_INSTALL:BOOL=ON `
-DAPPLICATION_CONFIGURE_INSTALLER:BOOL=ON `
-DINNOSETUP_USE_UNOFFICIAL_LANGUAGE:BOOL=ON `
diff --git a/scripts/ci/Collect-Symbol-Files.ps1 b/scripts/ci/Collect-Symbol-Files.ps1
index 5c4e9206..914b3c52 100644
--- a/scripts/ci/Collect-Symbol-Files.ps1
+++ b/scripts/ci/Collect-Symbol-Files.ps1
@@ -47,6 +47,7 @@ if ($IsWindows) {
}
dsymutil $dllFile.FullName -o "$pdbTargetDirectory/$($dllFile.Name).dSYM"
strip -S $dllFile.FullName
+ codesign --force --sign - $dllFile.FullName
} else {
Write-Host "Skip: $dllFile"
}
diff --git a/scripts/ci/Create-DMG.ps1 b/scripts/ci/Create-DMG.ps1
index f46f57d3..c4dd5544 100644
--- a/scripts/ci/Create-DMG.ps1
+++ b/scripts/ci/Create-DMG.ps1
@@ -32,6 +32,8 @@ New-Item -ItemType Directory -Path $TempDir | Out-Null
$Bg1xOut = Join-Path $TempDir "dmg_background.png"
$Bg2xOut = Join-Path $TempDir "dmg_background@2x.png"
$BgTiff = Join-Path $TempDir "dmg_background.tiff"
+$AppBundleName = "$ApplicationDisplayName.app"
+$AppBundlePath = Join-Path $TempDir $AppBundleName
$VersionText = "Version $Semver"
@@ -87,16 +89,24 @@ try {
Remove-Item $DmgPath -Force
}
+ if (Test-Path $AppBundlePath) {
+ Remove-Item $AppBundlePath -Recurse -Force
+ }
+
+ Move-Item -Path $AppPath -Destination $AppBundlePath
+
+ & codesign --deep --force --sign - $AppBundlePath | Write-Host
+
<# TODO: create-dmg currently places hidden .background file to the right of the visible area, so we have to leave some space for the horizontal scroll bar #>
& create-dmg `
--volname "$ApplicationDisplayName" `
--background "$BgTiff" `
--window-size 600 448 `
--icon-size 128 `
- --icon "$(Split-Path $AppPath -Leaf)" 132 280 `
+ --icon "$(Split-Path $AppBundlePath -Leaf)" 132 280 `
--app-drop-link 468 280 `
"$DmgPath" `
- "$AppPath" | Write-Host
+ "$AppBundlePath" | Write-Host
if ($LASTEXITCODE -ne 0) {
throw "create-dmg failed"
diff --git a/scripts/vcpkg b/scripts/vcpkg
index 5e0443fe..cf97d45c 160000
--- a/scripts/vcpkg
+++ b/scripts/vcpkg
@@ -1 +1 @@
-Subproject commit 5e0443fea12ce12f165fb16d7546b20a64894615
+Subproject commit cf97d45c7731dbe4a739a29fe7f8b9434c0e4d46
diff --git a/src/app/CMakeLists.txt b/src/app/CMakeLists.txt
index 4264e630..d95d1d65 100644
--- a/src/app/CMakeLists.txt
+++ b/src/app/CMakeLists.txt
@@ -29,6 +29,7 @@ endif()
ck_configure_application(
${_ico_args}
${_icns_args}
+ INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/info.plist.in
)
qm_configure_target(${PROJECT_NAME}
@@ -70,6 +71,12 @@ ck_add_shared_files(
# SRC conf/${CK_PLATFORM_LOWER}/qtmediate.json DEST ${CK_BUILD_QT_CONF_DIR} # qtmediate.json
)
+if(EXISTS ${CMAKE_CURRENT_BINARY_DIR}/icons/dspx.icns)
+ ck_add_shared_files(
+ SRC ${CMAKE_CURRENT_BINARY_DIR}/icons/dspx.icns DEST ${CK_BUILD_DATA_DIR}
+ )
+endif()
+
file(GLOB _icons icons/*)
list(FILTER _icons EXCLUDE REGEX CMakeLists.txt)
diff --git a/src/app/info.plist.in b/src/app/info.plist.in
new file mode 100644
index 00000000..c10a6e46
--- /dev/null
+++ b/src/app/info.plist.in
@@ -0,0 +1,92 @@
+
+
+
+
+
+ CFBundleDevelopmentRegion
+ English
+
+ CFBundleExecutable
+ @APPLICATION_NAME@
+
+ CFBundleIconFile
+ app.icns
+
+ CFBundleIdentifier
+ @APPLICATION_NAME@
+
+ CFBundleInfoDictionaryVersion
+ 6.0
+
+ CFBundleName
+ @APPLICATION_DISPLAY_NAME@
+
+ CFBundlePackageType
+ APPL
+
+ CFBundleShortVersionString
+ @APPLICATION_SEMVER@
+
+ CFBundleSignature
+ ????
+
+ CFBundleVersion
+ @APPLICATION_SEMVER@
+
+ CSResourcesFileMapped
+
+
+ NSHumanReadableCopyright
+ @RC_COPYRIGHT@
+
+ UTExportedTypeDeclarations
+
+
+ UTTypeIdentifier
+ org.diffscope.dspx
+
+ UTTypeDescription
+ DiffScope Project Exchange Format
+
+ UTTypeConformsTo
+
+ public.json
+
+
+ UTTypeTagSpecification
+
+ public.filename-extension
+
+ dspx
+
+
+ public.mime-type
+ application/vnd.openvpi.dspx+json
+
+
+
+
+ CFBundleDocumentTypes
+
+
+ CFBundleTypeName
+ DiffScope Project File
+
+ CFBundleTypeRole
+ Editor
+
+ LSItemContentTypes
+
+ org.diffscope.dspx
+
+
+ CFBundleTypeIconFile
+ dspx.icns
+
+ LSHandlerRank
+ Default
+
+
+
+
+
diff --git a/src/app/main.cpp b/src/app/main.cpp
index b1b4c6f8..ee7fe48a 100644
--- a/src/app/main.cpp
+++ b/src/app/main.cpp
@@ -3,6 +3,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -17,6 +18,9 @@
#include
#include
+#include
+#include
+
#include
#include
@@ -26,6 +30,8 @@
#include
#include
+#include
+
#ifdef APPLICATION_ENABLE_BREAKPAD
# include
#endif
@@ -39,6 +45,8 @@ static QSettings::Format getJsonSettingsFormat() {
static QQmlEngine *engine{};
+static constexpr char kNoWarningLastInitialization[] = "--no-warning-last-initialization";
+
class MyLoaderSpec : public Loader::LoaderSpec {
public:
MyLoaderSpec() {
@@ -55,6 +63,11 @@ class MyLoaderSpec : public Loader::LoaderSpec {
QStringLiteral("/config.json");
pluginPaths << ApplicationInfo::applicationLocation(ApplicationInfo::BuiltinPlugins);
coreName = QStringLiteral("org.diffscope.core");
+ extraArguments << Argument{
+ {kNoWarningLastInitialization},
+ {},
+ QStringLiteral("Suppress warning about 'Last initialization was aborted abnormally'")
+ };
}
QSettings *createExtensionSystemSettings(QSettings::Scope scope) override {
@@ -102,11 +115,25 @@ class MyLoaderSpec : public Loader::LoaderSpec {
locale.setNumberOptions(QLocale::OmitGroupSeparator);
RuntimeInterface::setTranslationManager(new TranslationManager(RuntimeInterface::instance()));
RuntimeInterface::translationManager()->setLocale(locale);
- RuntimeInterface::translationManager()->addTranslationPath(ApplicationInfo::systemLocation(ApplicationInfo::Resources) + QStringLiteral("/ChorusKit/translations"));
- RuntimeInterface::translationManager()->addTranslationPath(ApplicationInfo::systemLocation(ApplicationInfo::Resources) + QStringLiteral("/svscraft/translations"));
- RuntimeInterface::translationManager()->addTranslationPath(ApplicationInfo::systemLocation(ApplicationInfo::Resources) + QStringLiteral("/uishell/translations"));
+ auto translationBaseDir =
+#ifdef Q_OS_MAC
+ ApplicationInfo::systemLocation(ApplicationInfo::Resources) + QStringLiteral("/share");
+#else
+ ApplicationInfo::systemLocation(ApplicationInfo::Resources);
+#endif
+ RuntimeInterface::translationManager()->addTranslationPath(translationBaseDir + QStringLiteral("/ChorusKit/translations"));
+ RuntimeInterface::translationManager()->addTranslationPath(translationBaseDir + QStringLiteral("/svscraft/translations"));
+ RuntimeInterface::translationManager()->addTranslationPath(translationBaseDir + QStringLiteral("/uishell/translations"));
+
+ for (auto pluginSpec : ExtensionSystem::PluginManager::plugins()) {
+ static QRegularExpression rx("^([a-z_][a-z0-9_]*)(\\.[a-z_][a-z0-9_]*)+$");
+ if (!rx.match(pluginSpec->name()).hasMatch()) {
+ qFatal() << "Refused to load due to an invalid plugin name:" << pluginSpec->name() << "Plugin name should match" << rx.pattern();
+ }
+ }
- if (settings->value("lastInitializationAbortedFlag").toBool()) {
+ bool lastInitializationWarningSuppressed = QApplication::arguments().contains(kNoWarningLastInitialization);
+ if (settings->value("lastInitializationAbortedFlag").toBool() && !lastInitializationWarningSuppressed) {
qInfo() << "Last initialization was aborted abnormally";
QQmlComponent component(engine, "DiffScope.UIShell", "InitializationFailureWarningDialog");
std::unique_ptr dialog(component.isError() ? nullptr : component.createWithInitialProperties({
@@ -182,5 +209,13 @@ int main(int argc, char *argv[]) {
QQuickStyle::setFallbackStyle("Basic");
QApplication::setStyle(QStyleFactory::create("Fusion"));
+ // Check QML import
+ {
+ QQmlComponent component(engine, "DiffScope.UIShell", "ProjectWindow");
+ if (component.isError()) {
+ qFatal().nospace() << "QML Import Check Failed: " << component.errorString() << "\n\n" << "Note for developers: If you encounter this error when running after building DiffScope, please check:\n- Whether all targets have been built\n- Whether the correct QML_IMPORT_PATH environment variable was specified at runtime (it may need to be set to `../qml`)";
+ }
+ }
+
return MyLoaderSpec().run();
}
diff --git a/src/libs/3rdparty/choruskit b/src/libs/3rdparty/choruskit
index ead08258..e40b3316 160000
--- a/src/libs/3rdparty/choruskit
+++ b/src/libs/3rdparty/choruskit
@@ -1 +1 @@
-Subproject commit ead082581b7116763a27e29f733fa13e4edb963b
+Subproject commit e40b331652f9a81eea9be505305dbb198abd8dae
diff --git a/src/libs/3rdparty/opendspx b/src/libs/3rdparty/opendspx
index 0a4bb52e..333b2b8f 160000
--- a/src/libs/3rdparty/opendspx
+++ b/src/libs/3rdparty/opendspx
@@ -1 +1 @@
-Subproject commit 0a4bb52e0f199d74d288eda2c9c41bfe2d34d80d
+Subproject commit 333b2b8f422688c707dfa1f2d48f71cadede3ad4
diff --git a/src/libs/3rdparty/qactionkit b/src/libs/3rdparty/qactionkit
index 42b9cd90..573def48 160000
--- a/src/libs/3rdparty/qactionkit
+++ b/src/libs/3rdparty/qactionkit
@@ -1 +1 @@
-Subproject commit 42b9cd9053d76aeb8d66728a650827d97f40f11a
+Subproject commit 573def486e3f8c0f62768bcdcbc490b24cbd59af
diff --git a/src/libs/3rdparty/scopicflow b/src/libs/3rdparty/scopicflow
index 06238363..23e75d14 160000
--- a/src/libs/3rdparty/scopicflow
+++ b/src/libs/3rdparty/scopicflow
@@ -1 +1 @@
-Subproject commit 062383639e43b2baa9c3ae20674c8dd153d3a73e
+Subproject commit 23e75d14a6ad6006de3efcfbbc2dfc2119c20cad
diff --git a/src/libs/3rdparty/svscraft b/src/libs/3rdparty/svscraft
index 70fab799..161777b6 160000
--- a/src/libs/3rdparty/svscraft
+++ b/src/libs/3rdparty/svscraft
@@ -1 +1 @@
-Subproject commit 70fab7997428056a557a82a8e3dce37941c15795
+Subproject commit 161777b66cee8b6cb3213f3e77c564125c3301bf
diff --git a/src/libs/application/dspxmodel/src/AnchorNode.cpp b/src/libs/application/dspxmodel/src/AnchorNode.cpp
index 22eaba9f..bacf4382 100644
--- a/src/libs/application/dspxmodel/src/AnchorNode.cpp
+++ b/src/libs/application/dspxmodel/src/AnchorNode.cpp
@@ -1,7 +1,6 @@
#include "AnchorNode.h"
#include "AnchorNode_p.h"
-#include
#include
#include
@@ -12,39 +11,27 @@
namespace dspx {
- void AnchorNodePrivate::setInterpUnchecked(AnchorNode::InterpolationMode interp_) {
- Q_Q(AnchorNode);
- q->model()->strategy()->setEntityProperty(q->handle(), ModelStrategy::P_Type, QVariant::fromValue(interp_));
- }
-
- void AnchorNodePrivate::setInterp(AnchorNode::InterpolationMode interp_) {
- Q_Q(AnchorNode);
- if (auto engine = qjsEngine(q); engine && (interp_ != AnchorNode::None && interp_ != AnchorNode::Linear && interp_ != AnchorNode::Hermite)) {
- engine->throwError(QJSValue::RangeError, QStringLiteral("Interpolation mode must be one of None, Linear, or Hermite"));
- return;
+ void AnchorNodePrivate::setAnchorNodeSequence(AnchorNode *item, AnchorNodeSequence *anchorNodeSequence) {
+ auto d = item->d_func();
+ if (d->anchorNodeSequence != anchorNodeSequence) {
+ d->anchorNodeSequence = anchorNodeSequence;
+ Q_EMIT item->anchorNodeSequenceChanged();
}
- setInterpUnchecked(interp_);
}
- void AnchorNodePrivate::setXUnchecked(int x_) {
- Q_Q(AnchorNode);
- q->model()->strategy()->setEntityProperty(q->handle(), ModelStrategy::P_Position, x_);
- }
-
- void AnchorNodePrivate::setX(int x_) {
- Q_Q(AnchorNode);
- if (auto engine = qjsEngine(q); engine && x_ < 0) {
- engine->throwError(QJSValue::RangeError, QStringLiteral("Position must be greater or equal to 0"));
- return;
+ void AnchorNodePrivate::setPreviousItem(AnchorNode *item, AnchorNode *previousItem) {
+ auto d = item->d_func();
+ if (d->previousItem != previousItem) {
+ d->previousItem = previousItem;
+ Q_EMIT item->previousItemChanged();
}
- setXUnchecked(x_);
}
- void AnchorNodePrivate::setAnchorNodeSequence(AnchorNode *item, AnchorNodeSequence *anchorNodeSequence) {
+ void AnchorNodePrivate::setNextItem(AnchorNode *item, AnchorNode *nextItem) {
auto d = item->d_func();
- if (d->anchorNodeSequence != anchorNodeSequence) {
- d->anchorNodeSequence = anchorNodeSequence;
- Q_EMIT item->anchorNodeSequenceChanged();
+ if (d->nextItem != nextItem) {
+ d->nextItem = nextItem;
+ Q_EMIT item->nextItemChanged();
}
}
@@ -68,7 +55,7 @@ namespace dspx {
void AnchorNode::setInterp(InterpolationMode interp) {
Q_D(AnchorNode);
Q_ASSERT(interp == None || interp == Linear || interp == Hermite);
- d->setInterpUnchecked(interp);
+ model()->strategy()->setEntityProperty(handle(), ModelStrategy::P_Type, QVariant::fromValue(interp));
}
int AnchorNode::x() const {
@@ -79,7 +66,7 @@ namespace dspx {
void AnchorNode::setX(int x) {
Q_D(AnchorNode);
Q_ASSERT(x >= 0);
- d->setXUnchecked(x);
+ model()->strategy()->setEntityProperty(handle(), ModelStrategy::P_Position, x);
}
int AnchorNode::y() const {
@@ -113,6 +100,16 @@ namespace dspx {
return d->anchorNodeSequence;
}
+ AnchorNode *AnchorNode::previousItem() const {
+ Q_D(const AnchorNode);
+ return d->previousItem;
+ }
+
+ AnchorNode *AnchorNode::nextItem() const {
+ Q_D(const AnchorNode);
+ return d->nextItem;
+ }
+
void AnchorNode::handleSetEntityProperty(int property, const QVariant &value) {
Q_D(AnchorNode);
switch (property) {
diff --git a/src/libs/application/dspxmodel/src/AnchorNode.h b/src/libs/application/dspxmodel/src/AnchorNode.h
index 4eaef0e8..226043f2 100644
--- a/src/libs/application/dspxmodel/src/AnchorNode.h
+++ b/src/libs/application/dspxmodel/src/AnchorNode.h
@@ -22,10 +22,12 @@ namespace dspx {
QML_ELEMENT
QML_UNCREATABLE("")
Q_DECLARE_PRIVATE(AnchorNode)
- Q_PRIVATE_PROPERTY(d_func(), InterpolationMode interp MEMBER interp WRITE setInterp NOTIFY interpChanged)
- Q_PRIVATE_PROPERTY(d_func(), int x MEMBER x WRITE setX NOTIFY xChanged)
+ Q_PROPERTY(InterpolationMode interp READ interp WRITE setInterp NOTIFY interpChanged)
+ Q_PROPERTY(int x READ x WRITE setX NOTIFY xChanged)
Q_PROPERTY(int y READ y WRITE setY NOTIFY yChanged)
Q_PROPERTY(AnchorNodeSequence *anchorNodeSequence READ anchorNodeSequence NOTIFY anchorNodeSequenceChanged)
+ Q_PROPERTY(AnchorNode *previousItem READ previousItem NOTIFY previousItemChanged)
+ Q_PROPERTY(AnchorNode *nextItem READ nextItem NOTIFY nextItemChanged)
public:
enum InterpolationMode {
@@ -50,12 +52,16 @@ namespace dspx {
void fromQDspx(const QDspx::AnchorNode &node);
AnchorNodeSequence *anchorNodeSequence() const;
+ AnchorNode *previousItem() const;
+ AnchorNode *nextItem() const;
Q_SIGNALS:
void interpChanged(InterpolationMode interp);
void xChanged(int x);
void yChanged(int y);
void anchorNodeSequenceChanged();
+ void previousItemChanged();
+ void nextItemChanged();
protected:
void handleSetEntityProperty(int property, const QVariant &value) override;
diff --git a/src/libs/application/dspxmodel/src/AnchorNodeSequence.cpp b/src/libs/application/dspxmodel/src/AnchorNodeSequence.cpp
index 2d6e9d87..15f8f19d 100644
--- a/src/libs/application/dspxmodel/src/AnchorNodeSequence.cpp
+++ b/src/libs/application/dspxmodel/src/AnchorNodeSequence.cpp
@@ -22,13 +22,6 @@ namespace dspx {
d->paramCurveAnchor = paramCurveAnchor;
d->init(model->strategy()->getEntitiesFromSequenceContainer(handle));
-
- connect(this, &AnchorNodeSequence::itemInserted, this, [=](AnchorNode *item) {
- AnchorNodePrivate::setAnchorNodeSequence(item, this);
- });
- connect(this, &AnchorNodeSequence::itemRemoved, this, [=](AnchorNode *item) {
- AnchorNodePrivate::setAnchorNodeSequence(item, nullptr);
- });
}
AnchorNodeSequence::~AnchorNodeSequence() = default;
diff --git a/src/libs/application/dspxmodel/src/AnchorNodeSequence_p.h b/src/libs/application/dspxmodel/src/AnchorNodeSequence_p.h
index 8b27bf67..24cb0f23 100644
--- a/src/libs/application/dspxmodel/src/AnchorNodeSequence_p.h
+++ b/src/libs/application/dspxmodel/src/AnchorNodeSequence_p.h
@@ -8,7 +8,14 @@
namespace dspx {
- class AnchorNodeSequencePrivate : public PointSequenceData {
+ class AnchorNodeSequencePrivate : public PointSequenceData<
+ AnchorNodeSequence,
+ AnchorNode,
+ &AnchorNode::x,
+ &AnchorNode::xChanged,
+ &AnchorNodePrivate::setAnchorNodeSequence,
+ &AnchorNodePrivate::setPreviousItem,
+ &AnchorNodePrivate::setNextItem> {
Q_DECLARE_PUBLIC(AnchorNodeSequence)
public:
ParamCurveAnchor *paramCurveAnchor{};
diff --git a/src/libs/application/dspxmodel/src/AnchorNode_p.h b/src/libs/application/dspxmodel/src/AnchorNode_p.h
index 3b2704b6..ec8af70a 100644
--- a/src/libs/application/dspxmodel/src/AnchorNode_p.h
+++ b/src/libs/application/dspxmodel/src/AnchorNode_p.h
@@ -13,13 +13,12 @@ namespace dspx {
int x;
int y;
AnchorNodeSequence *anchorNodeSequence{};
-
- void setInterpUnchecked(AnchorNode::InterpolationMode interp_);
- void setInterp(AnchorNode::InterpolationMode interp_);
- void setXUnchecked(int x_);
- void setX(int x_);
+ AnchorNode *previousItem{};
+ AnchorNode *nextItem{};
static void setAnchorNodeSequence(AnchorNode *item, AnchorNodeSequence *anchorNodeSequence);
+ static void setPreviousItem(AnchorNode *item, AnchorNode *previousItem);
+ static void setNextItem(AnchorNode *item, AnchorNode *nextItem);
};
}
diff --git a/src/libs/application/dspxmodel/src/BasicModelStrategy.cpp b/src/libs/application/dspxmodel/src/BasicModelStrategy.cpp
index 0e6e8a67..a88d80df 100644
--- a/src/libs/application/dspxmodel/src/BasicModelStrategy.cpp
+++ b/src/libs/application/dspxmodel/src/BasicModelStrategy.cpp
@@ -96,6 +96,20 @@ namespace dspx {
return true;
}
+ bool BasicModelStrategy::moveToAnotherSequenceContainer(Handle sequenceContainerEntity, Handle entity, Handle otherSequenceContainerEntity) {
+ auto sequenceContainerObject = handleCast(sequenceContainerEntity);
+ auto otherSequenceContainerObject = handleCast(otherSequenceContainerEntity);
+ auto object = reinterpret_cast(entity.d);
+ if (!sequenceContainerObject->sequence.contains(object)) {
+ return false;
+ }
+ sequenceContainerObject->sequence.remove(object);
+ otherSequenceContainerObject->sequence.insert(object);
+ object->setParent(otherSequenceContainerObject);
+ Q_EMIT moveToAnotherSequenceContainerNotified(sequenceContainerEntity, entity, otherSequenceContainerEntity);
+ return true;
+ }
+
Handle BasicModelStrategy::takeFromSequenceContainer(Handle sequenceContainerEntity, Handle entity) {
auto sequenceContainerObject = handleCast(sequenceContainerEntity);
auto object = reinterpret_cast(entity.d);
diff --git a/src/libs/application/dspxmodel/src/BasicModelStrategy.h b/src/libs/application/dspxmodel/src/BasicModelStrategy.h
index 9f638397..0cf44804 100644
--- a/src/libs/application/dspxmodel/src/BasicModelStrategy.h
+++ b/src/libs/application/dspxmodel/src/BasicModelStrategy.h
@@ -20,6 +20,7 @@ namespace dspx {
bool insertIntoSequenceContainer(Handle sequenceContainerEntity, Handle entity) override;
bool insertIntoListContainer(Handle listContainerEntity, Handle entity, int index) override;
bool insertIntoMapContainer(Handle mapContainerEntity, Handle entity, const QString &key) override;
+ bool moveToAnotherSequenceContainer(Handle sequenceContainerEntity, Handle entity, Handle otherSequenceContainerEntity) override;
Handle takeFromSequenceContainer(Handle sequenceContainerEntity, Handle entity) override;
Handle takeFromListContainer(Handle listContainerEntity, int index) override;
Handle takeFromMapContainer(Handle mapContainerEntity, const QString &key) override;
diff --git a/src/libs/application/dspxmodel/src/Clip.cpp b/src/libs/application/dspxmodel/src/Clip.cpp
index 4caf4492..a8ec4f8a 100644
--- a/src/libs/application/dspxmodel/src/Clip.cpp
+++ b/src/libs/application/dspxmodel/src/Clip.cpp
@@ -32,6 +32,22 @@ namespace dspx {
}
}
+ void ClipPrivate::setPreviousItem(Clip *item, Clip *previousItem) {
+ auto d = item->d_func();
+ if (d->previousItem != previousItem) {
+ d->previousItem = previousItem;
+ Q_EMIT item->previousItemChanged();
+ }
+ }
+
+ void ClipPrivate::setNextItem(Clip *item, Clip *nextItem) {
+ auto d = item->d_func();
+ if (d->nextItem != nextItem) {
+ d->nextItem = nextItem;
+ Q_EMIT item->nextItemChanged();
+ }
+ }
+
Clip::Clip(ClipType type, Handle handle, Model *model) : EntityObject(handle, model), d_ptr(new ClipPrivate) {
Q_D(Clip);
d->q_ptr = this;
@@ -115,6 +131,16 @@ namespace dspx {
return d->clipSequence;
}
+ Clip *Clip::previousItem() const {
+ Q_D(const Clip);
+ return d->previousItem;
+ }
+
+ Clip *Clip::nextItem() const {
+ Q_D(const Clip);
+ return d->nextItem;
+ }
+
int Clip::position() const {
Q_D(const Clip);
return d->time->start() + d->time->clipStart();
diff --git a/src/libs/application/dspxmodel/src/Clip.h b/src/libs/application/dspxmodel/src/Clip.h
index c35024e9..4d304d3f 100644
--- a/src/libs/application/dspxmodel/src/Clip.h
+++ b/src/libs/application/dspxmodel/src/Clip.h
@@ -30,6 +30,8 @@ namespace dspx {
Q_PROPERTY(ClipType type READ type CONSTANT)
Q_PROPERTY(Workspace *workspace READ workspace CONSTANT)
Q_PROPERTY(ClipSequence *clipSequence READ clipSequence NOTIFY clipSequenceChanged)
+ Q_PROPERTY(Clip *previousItem READ previousItem NOTIFY previousItemChanged)
+ Q_PROPERTY(Clip *nextItem READ nextItem NOTIFY nextItemChanged)
Q_PROPERTY(bool overlapped READ isOverlapped NOTIFY overlappedChanged)
public:
enum ClipType {
@@ -56,6 +58,9 @@ namespace dspx {
ClipSequence *clipSequence() const;
+ Clip *previousItem() const;
+ Clip *nextItem() const;
+
int position() const;
int length() const;
@@ -65,6 +70,8 @@ namespace dspx {
Q_SIGNALS:
void nameChanged(const QString &name);
void clipSequenceChanged();
+ void previousItemChanged();
+ void nextItemChanged();
void positionChanged(int position);
void lengthChanged(int length);
void overlappedChanged(bool overlapped);
diff --git a/src/libs/application/dspxmodel/src/ClipSequence.cpp b/src/libs/application/dspxmodel/src/ClipSequence.cpp
index 93f6a778..16c2fb08 100644
--- a/src/libs/application/dspxmodel/src/ClipSequence.cpp
+++ b/src/libs/application/dspxmodel/src/ClipSequence.cpp
@@ -16,6 +16,67 @@
namespace dspx {
+ void ClipSequencePrivate::handleMoveFromAnotherSequenceContainer(Handle entity, Handle otherSequenceContainerEntity) {
+ auto q = q_ptr;
+ auto item = getItem(entity, true);
+ if (!pointContainer.contains(item)) {
+ QObject::connect(item, &Clip::positionChanged, q, [=](int pos) {
+ int length = item->length();
+ insertItem(item, pos, length);
+ });
+ QObject::connect(item, &Clip::lengthChanged, q, [=](int len) {
+ int position = item->position();
+ insertItem(item, position, len);
+ });
+ QObject::connect(item, &QObject::destroyed, q, [=] {
+ removeItem(item);
+ });
+ }
+ auto otherClipSequence = qobject_cast(pModel->mapToObject(otherSequenceContainerEntity));
+ bool containsItem = pointContainer.contains(item);
+ if (!containsItem) {
+ Q_EMIT q->itemAboutToInsert(item, otherClipSequence);
+ }
+
+ pointContainer.insertItem(item, item->position());
+ ClipPrivate::setClipSequence(item, q);
+ auto affectedItems = rangeContainer.insertItem(item, item->position(), item->length());
+
+ for (auto affectedItem : affectedItems) {
+ bool isOverlapped = rangeContainer.isOverlapped(affectedItem);
+ ClipPrivate::setOverlapped(affectedItem, isOverlapped);
+ }
+
+ if (!containsItem) {
+ updateFirstAndLastItem();
+ Q_EMIT q->itemInserted(item, otherClipSequence);
+ Q_EMIT q->sizeChanged(pointContainer.size());
+ }
+ }
+
+ void ClipSequencePrivate::handleMoveToAnotherSequenceContainer(Handle entity, Handle otherSequenceContainerEntity) {
+ auto q = q_ptr;
+ auto item = getItem(entity, false);
+ if (item) {
+ QObject::disconnect(item, nullptr, q, nullptr);
+ auto otherClipSequence = qobject_cast(pModel->mapToObject(otherSequenceContainerEntity));
+ Q_EMIT q->itemAboutToRemove(item, otherClipSequence);
+
+ pointContainer.removeItem(item);
+ ClipPrivate::setClipSequence(item, otherClipSequence);
+ auto affectedItems = rangeContainer.removeItem(item);
+
+ for (auto affectedItem : affectedItems) {
+ bool isOverlapped = rangeContainer.isOverlapped(affectedItem);
+ ClipPrivate::setOverlapped(affectedItem, isOverlapped);
+ }
+
+ updateFirstAndLastItem();
+ Q_EMIT q->itemRemoved(item, otherClipSequence);
+ Q_EMIT q->sizeChanged(pointContainer.size());
+ }
+ }
+
ClipSequence::ClipSequence(Track *track, Handle handle, Model *model) : EntityObject(handle, model), d_ptr(new ClipSequencePrivate) {
Q_D(ClipSequence);
Q_ASSERT(model->strategy()->getEntityType(handle) == ModelStrategy::ES_Clips);
@@ -24,13 +85,6 @@ namespace dspx {
d->track = track;
d->init(model->strategy()->getEntitiesFromSequenceContainer(handle));
-
- connect(this, &ClipSequence::itemInserted, this, [=](Clip *item) {
- ClipPrivate::setClipSequence(item, this);
- });
- connect(this, &ClipSequence::itemRemoved, this, [=](Clip *item) {
- ClipPrivate::setClipSequence(item, nullptr);
- });
}
ClipSequence::~ClipSequence() = default;
@@ -85,6 +139,11 @@ namespace dspx {
return d->pModel->strategy->takeFromSequenceContainer(handle(), item->handle());
}
+ bool ClipSequence::moveToAnotherClipSequence(Clip *item, ClipSequence *sequence) {
+ Q_D(ClipSequence);
+ return d->pModel->strategy->moveToAnotherSequenceContainer(handle(), item->handle(), sequence->handle());
+ }
+
QList ClipSequence::toQDspx() const {
Q_D(const ClipSequence);
QList ret;
@@ -125,6 +184,14 @@ namespace dspx {
Q_D(ClipSequence);
d->handleTakeFromSequenceContainer(takenEntity, entity);
}
+ void ClipSequence::handleMoveFromAnotherSequenceContainer(Handle entity, Handle otherSequenceContainerEntity) {
+ Q_D(ClipSequence);
+ d->handleMoveFromAnotherSequenceContainer(entity, otherSequenceContainerEntity);
+ }
+ void ClipSequence::handleMoveToAnotherSequenceContainer(Handle entity, Handle otherSequenceContainerEntity) {
+ Q_D(ClipSequence);
+ d->handleMoveToAnotherSequenceContainer(entity, otherSequenceContainerEntity);
+ }
}
diff --git a/src/libs/application/dspxmodel/src/ClipSequence.h b/src/libs/application/dspxmodel/src/ClipSequence.h
index 26ae777c..6a9123f8 100644
--- a/src/libs/application/dspxmodel/src/ClipSequence.h
+++ b/src/libs/application/dspxmodel/src/ClipSequence.h
@@ -41,6 +41,7 @@ namespace dspx {
Q_INVOKABLE bool insertItem(Clip *item);
Q_INVOKABLE bool removeItem(Clip *item);
+ Q_INVOKABLE bool moveToAnotherClipSequence(Clip *item, ClipSequence *sequence);
QList toQDspx() const;
void fromQDspx(const QList &clips);
@@ -52,10 +53,10 @@ namespace dspx {
}
Q_SIGNALS:
- void itemAboutToInsert(Clip *item);
- void itemInserted(Clip *item);
- void itemAboutToRemove(Clip *item);
- void itemRemoved(Clip *item);
+ void itemAboutToInsert(Clip *item, ClipSequence *clipSequenceFromWhichMoved = nullptr);
+ void itemInserted(Clip *item, ClipSequence *clipSequenceFromWhichMoved = nullptr);
+ void itemAboutToRemove(Clip *item, ClipSequence *clipSequenceToWhichMoved = nullptr);
+ void itemRemoved(Clip *item, ClipSequence *clipSequenceToWhichMoved = nullptr);
void sizeChanged(int size);
void firstItemChanged(Clip *item);
void lastItemChanged(Clip *item);
@@ -63,6 +64,8 @@ namespace dspx {
protected:
void handleInsertIntoSequenceContainer(Handle entity) override;
void handleTakeFromSequenceContainer(Handle takenEntity, Handle entity) override;
+ void handleMoveFromAnotherSequenceContainer(Handle entity, Handle otherSequenceContainerEntity) override;
+ void handleMoveToAnotherSequenceContainer(Handle entity, Handle otherSequenceContainerEntity) override;
private:
friend class ModelPrivate;
diff --git a/src/libs/application/dspxmodel/src/ClipSequence_p.h b/src/libs/application/dspxmodel/src/ClipSequence_p.h
index b6c27acb..78f7d8cd 100644
--- a/src/libs/application/dspxmodel/src/ClipSequence_p.h
+++ b/src/libs/application/dspxmodel/src/ClipSequence_p.h
@@ -8,10 +8,13 @@
namespace dspx {
- class ClipSequencePrivate : public RangeSequenceData {
+ class ClipSequencePrivate : public RangeSequenceData {
Q_DECLARE_PUBLIC(ClipSequence)
public:
Track *track{};
+
+ void handleMoveFromAnotherSequenceContainer(Handle entity, Handle otherSequenceContainerEntity);
+ void handleMoveToAnotherSequenceContainer(Handle entity, Handle otherSequenceContainerEntity);
};
}
diff --git a/src/libs/application/dspxmodel/src/ClipTime.cpp b/src/libs/application/dspxmodel/src/ClipTime.cpp
index 6e80b886..50df3a7a 100644
--- a/src/libs/application/dspxmodel/src/ClipTime.cpp
+++ b/src/libs/application/dspxmodel/src/ClipTime.cpp
@@ -1,6 +1,4 @@
#include "ClipTime.h"
-
-#include
#include
#include
@@ -21,56 +19,9 @@ namespace dspx {
int length;
int clipStart;
int clipLen;
-
- void setLengthUnchecked(int length_);
- void setLength(int length_);
- void setClipStartUnchecked(int clipStart_);
- void setClipStart(int clipStart_);
- void setClipLenUnchecked(int clipLen_);
- void setClipLen(int clipLen_);
};
- void ClipTimePrivate::setLengthUnchecked(int length_) {
- Q_Q(ClipTime);
- pModel->strategy->setEntityProperty(handle, ModelStrategy::P_Length, length_);
- }
-
- void ClipTimePrivate::setLength(int length_) {
- Q_Q(ClipTime);
- if (auto engine = qjsEngine(q); engine && length_ < 0) {
- engine->throwError(QJSValue::RangeError, QStringLiteral("Length must be greater than or equal to 0"));
- return;
- }
- setLengthUnchecked(length_);
- }
-
- void ClipTimePrivate::setClipStartUnchecked(int clipStart_) {
- Q_Q(ClipTime);
- pModel->strategy->setEntityProperty(handle, ModelStrategy::P_ClipStart, clipStart_);
- }
- void ClipTimePrivate::setClipStart(int clipStart_) {
- Q_Q(ClipTime);
- if (auto engine = qjsEngine(q); engine && clipStart_ < 0) {
- engine->throwError(QJSValue::RangeError, QStringLiteral("ClipStart must be greater than or equal to 0"));
- return;
- }
- setClipStartUnchecked(clipStart_);
- }
-
- void ClipTimePrivate::setClipLenUnchecked(int clipLen_) {
- Q_Q(ClipTime);
- pModel->strategy->setEntityProperty(handle, ModelStrategy::P_ClipLength, clipLen_);
- }
-
- void ClipTimePrivate::setClipLen(int clipLen_) {
- Q_Q(ClipTime);
- if (auto engine = qjsEngine(q); engine && clipLen_ < 0) {
- engine->throwError(QJSValue::RangeError, QStringLiteral("ClipLen must be greater than or equal to 0"));
- return;
- }
- setClipLenUnchecked(clipLen_);
- }
ClipTime::ClipTime(Handle handle, Model *model) : QObject(model), d_ptr(new ClipTimePrivate) {
Q_D(ClipTime);
@@ -103,7 +54,7 @@ namespace dspx {
void ClipTime::setLength(int length) {
Q_D(ClipTime);
Q_ASSERT(length >= 0);
- d->setLengthUnchecked(length);
+ d->pModel->strategy->setEntityProperty(d->handle, ModelStrategy::P_Length, length);
}
int ClipTime::clipStart() const {
@@ -114,7 +65,7 @@ namespace dspx {
void ClipTime::setClipStart(int clipStart) {
Q_D(ClipTime);
Q_ASSERT(clipStart >= 0);
- d->setClipStartUnchecked(clipStart);
+ d->pModel->strategy->setEntityProperty(d->handle, ModelStrategy::P_ClipStart, clipStart);
}
int ClipTime::clipLen() const {
@@ -125,7 +76,7 @@ namespace dspx {
void ClipTime::setClipLen(int clipLen) {
Q_D(ClipTime);
Q_ASSERT(clipLen >= 0);
- d->setClipLenUnchecked(clipLen);
+ d->pModel->strategy->setEntityProperty(d->handle, ModelStrategy::P_ClipLength, clipLen);
}
QDspx::ClipTime ClipTime::toQDspx() const {
diff --git a/src/libs/application/dspxmodel/src/ClipTime.h b/src/libs/application/dspxmodel/src/ClipTime.h
index a4621347..20375cdb 100644
--- a/src/libs/application/dspxmodel/src/ClipTime.h
+++ b/src/libs/application/dspxmodel/src/ClipTime.h
@@ -18,15 +18,15 @@ namespace dspx {
class ClipTimePrivate;
- class ClipTime : public QObject {
+ class DSPX_MODEL_EXPORT ClipTime : public QObject {
Q_OBJECT
QML_ELEMENT
QML_UNCREATABLE("")
Q_DECLARE_PRIVATE(ClipTime)
Q_PROPERTY(int start READ start WRITE setStart NOTIFY startChanged)
- Q_PRIVATE_PROPERTY(d_func(), int length MEMBER length WRITE setLength NOTIFY lengthChanged)
- Q_PRIVATE_PROPERTY(d_func(), int clipStart MEMBER clipStart WRITE setClipStart NOTIFY clipStartChanged)
- Q_PRIVATE_PROPERTY(d_func(), int clipLen MEMBER clipLen WRITE setClipLen NOTIFY clipLenChanged)
+ Q_PROPERTY(int length READ length WRITE setLength NOTIFY lengthChanged)
+ Q_PROPERTY(int clipStart READ clipStart WRITE setClipStart NOTIFY clipStartChanged)
+ Q_PROPERTY(int clipLen READ clipLen WRITE setClipLen NOTIFY clipLenChanged)
public:
~ClipTime() override;
diff --git a/src/libs/application/dspxmodel/src/Clip_p.h b/src/libs/application/dspxmodel/src/Clip_p.h
index e3a3c02c..a2e8cba5 100644
--- a/src/libs/application/dspxmodel/src/Clip_p.h
+++ b/src/libs/application/dspxmodel/src/Clip_p.h
@@ -15,11 +15,15 @@ namespace dspx {
ClipTime *time;
Clip::ClipType type;
ClipSequence *clipSequence{};
+ Clip *previousItem{};
+ Clip *nextItem{};
Workspace *workspace;
bool overlapped{};
static void setOverlapped(Clip *item, bool overlapped);
static void setClipSequence(Clip *item, ClipSequence *clipSequence);
+ static void setPreviousItem(Clip *item, Clip *previousItem);
+ static void setNextItem(Clip *item, Clip *nextItem);
};
}
diff --git a/src/libs/application/dspxmodel/src/Control.cpp b/src/libs/application/dspxmodel/src/Control.cpp
index d0c823ca..4d8beb45 100644
--- a/src/libs/application/dspxmodel/src/Control.cpp
+++ b/src/libs/application/dspxmodel/src/Control.cpp
@@ -1,6 +1,4 @@
#include "Control.h"
-
-#include
#include
#include
@@ -19,40 +17,10 @@ namespace dspx {
double pan;
bool mute;
- void setGainUnchecked(double gain_);
- void setGain(double gain_);
- void setPanUnchecked(double pan_);
- void setPan(double pan_);
};
- void ControlPrivate::setGainUnchecked(double gain_) {
- Q_Q(Control);
- pModel->strategy->setEntityProperty(handle, ModelStrategy::P_ControlGain, gain_);
- }
-
- void ControlPrivate::setGain(double gain_) {
- Q_Q(Control);
- if (auto engine = qjsEngine(q); engine && (gain_ < 0)) {
- engine->throwError(QJSValue::RangeError, QStringLiteral("Gain must be greater or equal to 0"));
- return;
- }
- setGainUnchecked(gain_);
- }
- void ControlPrivate::setPanUnchecked(double pan_) {
- Q_Q(Control);
- pModel->strategy->setEntityProperty(handle, ModelStrategy::P_ControlPan, pan_);
- }
-
- void ControlPrivate::setPan(double pan_) {
- Q_Q(Control);
- if (auto engine = qjsEngine(q); engine && (pan_ < -1 || pan_ > 1)) {
- engine->throwError(QJSValue::RangeError, QStringLiteral("Pan must be in range [-1.0, 1.0]"));
- return;
- }
- setPanUnchecked(pan_);
- }
Control::Control(Handle handle, Model *model) : QObject(model), d_ptr(new ControlPrivate) {
Q_D(Control);
@@ -74,7 +42,7 @@ namespace dspx {
void Control::setGain(double gain) {
Q_D(Control);
Q_ASSERT(gain >= 0.0);
- d->setGainUnchecked(gain);
+ d->pModel->strategy->setEntityProperty(d->handle, ModelStrategy::P_ControlGain, gain);
}
double Control::pan() const {
@@ -85,7 +53,7 @@ namespace dspx {
void Control::setPan(double pan) {
Q_D(Control);
Q_ASSERT(pan >= -1.0 && pan <= 1.0);
- d->setPanUnchecked(pan);
+ d->pModel->strategy->setEntityProperty(d->handle, ModelStrategy::P_ControlPan, pan);
}
bool Control::mute() const {
diff --git a/src/libs/application/dspxmodel/src/Control.h b/src/libs/application/dspxmodel/src/Control.h
index 20135658..d0330ec7 100644
--- a/src/libs/application/dspxmodel/src/Control.h
+++ b/src/libs/application/dspxmodel/src/Control.h
@@ -14,13 +14,13 @@ namespace dspx {
class ControlPrivate;
- class Control : public QObject {
+ class DSPX_MODEL_EXPORT Control : public QObject {
Q_OBJECT
QML_ELEMENT
QML_UNCREATABLE("")
Q_DECLARE_PRIVATE(Control)
- Q_PRIVATE_PROPERTY(d_func(), double gain MEMBER gain WRITE setGain NOTIFY gainChanged)
- Q_PRIVATE_PROPERTY(d_func(), double pan MEMBER pan WRITE setPan NOTIFY panChanged)
+ Q_PROPERTY(double gain READ gain WRITE setGain NOTIFY gainChanged)
+ Q_PROPERTY(double pan READ pan WRITE setPan NOTIFY panChanged)
Q_PROPERTY(bool mute READ mute WRITE setMute NOTIFY muteChanged)
public:
diff --git a/src/libs/application/dspxmodel/src/EntityObject.cpp b/src/libs/application/dspxmodel/src/EntityObject.cpp
index a42f104a..75019821 100644
--- a/src/libs/application/dspxmodel/src/EntityObject.cpp
+++ b/src/libs/application/dspxmodel/src/EntityObject.cpp
@@ -22,7 +22,7 @@ namespace dspx {
EntityObject::~EntityObject() {
Q_D(EntityObject);
if (d->model && d->handle) {
-
+ Q_ASSERT(false && "EntityObject::~EntityObject: handle is not null. You should call Model::destroyItem() to delete EntityObject.");
}
}
@@ -45,6 +45,12 @@ namespace dspx {
void EntityObject::handleInsertIntoMapContainer(Handle entity, const QString &key) {
Q_UNREACHABLE();
}
+ void EntityObject::handleMoveToAnotherSequenceContainer(Handle entity, Handle otherSequenceContainerEntity) {
+ Q_UNREACHABLE();
+ }
+ void EntityObject::handleMoveFromAnotherSequenceContainer(Handle entity, Handle otherSequenceContainerEntity) {
+ Q_UNREACHABLE();
+ }
void EntityObject::handleTakeFromSequenceContainer(Handle takenEntity, Handle entity) {
Q_UNREACHABLE();
}
diff --git a/src/libs/application/dspxmodel/src/EntityObject.h b/src/libs/application/dspxmodel/src/EntityObject.h
index 1d9e89c4..c4cb85a8 100644
--- a/src/libs/application/dspxmodel/src/EntityObject.h
+++ b/src/libs/application/dspxmodel/src/EntityObject.h
@@ -29,6 +29,9 @@ namespace dspx {
virtual void handleInsertIntoListContainer(Handle entities, int index);
virtual void handleInsertIntoMapContainer(Handle entity, const QString &key);
+ virtual void handleMoveToAnotherSequenceContainer(Handle entity, Handle otherSequenceContainerEntity);
+ virtual void handleMoveFromAnotherSequenceContainer(Handle entity, Handle otherSequenceContainerEntity);
+
virtual void handleTakeFromSequenceContainer(Handle takenEntity, Handle entity);
virtual void handleTakeFromListContainer(Handle takenEntities, int index);
virtual void handleTakeFromMapContainer(Handle takenEntity, const QString &key);
diff --git a/src/libs/application/dspxmodel/src/Global.cpp b/src/libs/application/dspxmodel/src/Global.cpp
index e687463f..fd35f5e5 100644
--- a/src/libs/application/dspxmodel/src/Global.cpp
+++ b/src/libs/application/dspxmodel/src/Global.cpp
@@ -1,6 +1,4 @@
#include "Global.h"
-
-#include
#include
#include
@@ -17,30 +15,8 @@ namespace dspx {
Global *q_ptr;
ModelPrivate *pModel;
Handle handle;
-
- int centShift() const;
- void setCentShiftUnchecked(int centShift);
- void setCentShift(int centShift);
};
- int GlobalPrivate::centShift() const {
- return pModel->centShift;
- }
-
- void GlobalPrivate::setCentShiftUnchecked(int centShift) {
- Q_Q(Global);
- pModel->strategy->setEntityProperty(handle, ModelStrategy::P_CentShift, centShift);
- }
-
- void GlobalPrivate::setCentShift(int centShift) {
- Q_Q(Global);
- if (auto engine = qjsEngine(q); engine && (centShift < -50 || centShift > 50)) {
- engine->throwError(QJSValue::RangeError, QStringLiteral("Cent shift must be in range [-50, 50]"));
- return;
- }
- setCentShiftUnchecked(centShift);
- }
-
Global::Global(Model *model) : QObject(model), d_ptr(new GlobalPrivate) {
Q_D(Global);
d->q_ptr = this;
@@ -67,12 +43,21 @@ namespace dspx {
}
int Global::centShift() const {
Q_D(const Global);
- return d->centShift();
+ return d->pModel->centShift;
}
void Global::setCentShift(int centShift) {
Q_D(Global);
Q_ASSERT(centShift >= -50 && centShift <= 50);
- d->setCentShiftUnchecked(centShift);
+ d->pModel->strategy->setEntityProperty(d->handle, ModelStrategy::P_CentShift, centShift);
+ }
+ Global::AccidentalType Global::accidentalType() const {
+ Q_D(const Global);
+ return static_cast(d->pModel->accidentalType);
+ }
+ void Global::setAccidentalType(AccidentalType accidentalType) {
+ Q_D(Global);
+ Q_ASSERT(accidentalType == Flat || accidentalType == Sharp);
+ d->pModel->strategy->setEntityProperty(d->handle, ModelStrategy::P_AccidentalType, accidentalType);
}
QString Global::editorId() const {
Q_D(const Global);
diff --git a/src/libs/application/dspxmodel/src/Global.h b/src/libs/application/dspxmodel/src/Global.h
index 651f2129..e30b1760 100644
--- a/src/libs/application/dspxmodel/src/Global.h
+++ b/src/libs/application/dspxmodel/src/Global.h
@@ -5,6 +5,8 @@
#include
+#include
+
namespace QDspx {
struct Global;
}
@@ -17,14 +19,15 @@ namespace dspx {
class GlobalPrivate;
- class Global : public QObject {
+ class DSPX_MODEL_EXPORT Global : public QObject {
Q_OBJECT
QML_ELEMENT
QML_UNCREATABLE("")
Q_DECLARE_PRIVATE(Global)
Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged)
Q_PROPERTY(QString author READ author WRITE setAuthor NOTIFY authorChanged)
- Q_PRIVATE_PROPERTY(d_func(), int centShift READ centShift WRITE setCentShift NOTIFY centShiftChanged)
+ Q_PROPERTY(int centShift READ centShift WRITE setCentShift NOTIFY centShiftChanged)
+ Q_PROPERTY(AccidentalType accidentalType READ accidentalType WRITE setAccidentalType NOTIFY accidentalTypeChanged)
Q_PROPERTY(QString editorId READ editorId WRITE setEditorId NOTIFY editorIdChanged)
Q_PROPERTY(QString editorName READ editorName WRITE setEditorName NOTIFY editorNameChanged)
@@ -40,6 +43,14 @@ namespace dspx {
int centShift() const;
void setCentShift(int centShift);
+ enum AccidentalType {
+ Flat,
+ Sharp,
+ };
+ Q_ENUM(AccidentalType)
+ AccidentalType accidentalType() const;
+ void setAccidentalType(AccidentalType accidentalType);
+
QString editorId() const;
void setEditorId(const QString &editorId);
@@ -53,6 +64,7 @@ namespace dspx {
void nameChanged(const QString &name);
void authorChanged(const QString &author);
void centShiftChanged(int centShift);
+ void accidentalTypeChanged(AccidentalType accidentalType);
void editorIdChanged(const QString &editorId);
void editorNameChanged(const QString &editorName);
diff --git a/src/libs/application/dspxmodel/src/Label.cpp b/src/libs/application/dspxmodel/src/Label.cpp
index 7fdfecfe..75c6fb82 100644
--- a/src/libs/application/dspxmodel/src/Label.cpp
+++ b/src/libs/application/dspxmodel/src/Label.cpp
@@ -1,7 +1,5 @@
#include "Label.h"
#include "Label_p.h"
-
-#include
#include
#include
@@ -12,19 +10,7 @@
namespace dspx {
- void LabelPrivate::setPosUnchecked(int pos_) {
- Q_Q(Label);
- q->model()->strategy()->setEntityProperty(q->handle(), ModelStrategy::P_Position, pos_);
- }
- void LabelPrivate::setPos(int pos_) {
- Q_Q(Label);
- if (auto engine = qjsEngine(q); engine && pos_ < -0) {
- engine->throwError(QJSValue::RangeError, QStringLiteral("Pos must be greater or equal to 0"));
- return;
- }
- setPosUnchecked(pos_);
- }
void LabelPrivate::setLabelSequence(Label *item, LabelSequence *labelSequence) {
auto d = item->d_func();
@@ -34,6 +20,22 @@ namespace dspx {
}
}
+ void LabelPrivate::setPreviousItem(Label *item, Label *previousItem) {
+ auto d = item->d_func();
+ if (d->previousItem != previousItem) {
+ d->previousItem = previousItem;
+ Q_EMIT item->previousItemChanged();
+ }
+ }
+
+ void LabelPrivate::setNextItem(Label *item, Label *nextItem) {
+ auto d = item->d_func();
+ if (d->nextItem != nextItem) {
+ d->nextItem = nextItem;
+ Q_EMIT item->nextItemChanged();
+ }
+ }
+
Label::Label(Handle handle, Model *model) : EntityObject(handle, model), d_ptr(new LabelPrivate) {
Q_D(Label);
Q_ASSERT(model->strategy()->getEntityType(handle) == ModelStrategy::EI_Label);
@@ -52,7 +54,7 @@ namespace dspx {
void Label::setPos(int pos) {
Q_D(Label);
Q_ASSERT(pos >= 0);
- d->setPosUnchecked(pos);
+ model()->strategy()->setEntityProperty(handle(), ModelStrategy::P_Position, pos);
}
QString Label::text() const {
@@ -70,6 +72,16 @@ namespace dspx {
return d->labelSequence;
}
+ Label *Label::previousItem() const {
+ Q_D(const Label);
+ return d->previousItem;
+ }
+
+ Label *Label::nextItem() const {
+ Q_D(const Label);
+ return d->nextItem;
+ }
+
QDspx::Label Label::toQDspx() const {
return {
.pos = pos(),
diff --git a/src/libs/application/dspxmodel/src/Label.h b/src/libs/application/dspxmodel/src/Label.h
index dd21eb3f..882a1d2b 100644
--- a/src/libs/application/dspxmodel/src/Label.h
+++ b/src/libs/application/dspxmodel/src/Label.h
@@ -19,9 +19,11 @@ namespace dspx {
QML_ELEMENT
QML_UNCREATABLE("")
Q_DECLARE_PRIVATE(Label);
- Q_PRIVATE_PROPERTY(d_func(), int pos MEMBER pos WRITE setPos NOTIFY posChanged)
+ Q_PROPERTY(int pos READ pos WRITE setPos NOTIFY posChanged)
Q_PROPERTY(QString text READ text WRITE setText NOTIFY textChanged)
Q_PROPERTY(LabelSequence *labelSequence READ labelSequence NOTIFY labelSequenceChanged)
+ Q_PROPERTY(Label *previousItem READ previousItem NOTIFY previousItemChanged)
+ Q_PROPERTY(Label *nextItem READ nextItem NOTIFY nextItemChanged)
public:
~Label() override;
@@ -33,6 +35,9 @@ namespace dspx {
LabelSequence *labelSequence() const;
+ Label *previousItem() const;
+ Label *nextItem() const;
+
QDspx::Label toQDspx() const;
void fromQDspx(const QDspx::Label &label);
@@ -40,6 +45,8 @@ namespace dspx {
void posChanged(int pos);
void textChanged(const QString &text);
void labelSequenceChanged();
+ void previousItemChanged();
+ void nextItemChanged();
protected:
void handleSetEntityProperty(int property, const QVariant &value) override;
diff --git a/src/libs/application/dspxmodel/src/LabelSequence.cpp b/src/libs/application/dspxmodel/src/LabelSequence.cpp
index 2eb86c2b..45ff44ef 100644
--- a/src/libs/application/dspxmodel/src/LabelSequence.cpp
+++ b/src/libs/application/dspxmodel/src/LabelSequence.cpp
@@ -20,13 +20,6 @@ namespace dspx {
d->pModel = ModelPrivate::get(model);
d->init(model->strategy()->getEntitiesFromSequenceContainer(handle));
-
- connect(this, &LabelSequence::itemInserted, this, [=](Label *item) {
- LabelPrivate::setLabelSequence(item, this);
- });
- connect(this, &LabelSequence::itemRemoved, this, [=](Label *item) {
- LabelPrivate::setLabelSequence(item, nullptr);
- });
}
LabelSequence::~LabelSequence() = default;
diff --git a/src/libs/application/dspxmodel/src/LabelSequence_p.h b/src/libs/application/dspxmodel/src/LabelSequence_p.h
index fe03ee4a..2bcb8c4a 100644
--- a/src/libs/application/dspxmodel/src/LabelSequence_p.h
+++ b/src/libs/application/dspxmodel/src/LabelSequence_p.h
@@ -8,7 +8,7 @@
namespace dspx {
- class LabelSequencePrivate : public PointSequenceData {
+ class LabelSequencePrivate : public PointSequenceData {
Q_DECLARE_PUBLIC(LabelSequence)
};
diff --git a/src/libs/application/dspxmodel/src/Label_p.h b/src/libs/application/dspxmodel/src/Label_p.h
index 0ef4b5b3..70e56681 100644
--- a/src/libs/application/dspxmodel/src/Label_p.h
+++ b/src/libs/application/dspxmodel/src/Label_p.h
@@ -12,11 +12,12 @@ namespace dspx {
int pos;
QString text;
LabelSequence *labelSequence{};
-
- void setPosUnchecked(int pos_);
- void setPos(int pos_);
+ Label *previousItem{};
+ Label *nextItem{};
static void setLabelSequence(Label *item, LabelSequence *labelSequence);
+ static void setPreviousItem(Label *item, Label *previousItem);
+ static void setNextItem(Label *item, Label *nextItem);
};
}
diff --git a/src/libs/application/dspxmodel/src/ListData_p.h b/src/libs/application/dspxmodel/src/ListData_p.h
index dff09987..358b707e 100644
--- a/src/libs/application/dspxmodel/src/ListData_p.h
+++ b/src/libs/application/dspxmodel/src/ListData_p.h
@@ -15,7 +15,7 @@ namespace dspx {
static QJSValue create(QObject *o);
};
- template
+ template
class ListData {
public:
ListType *q_ptr;
@@ -36,8 +36,11 @@ namespace dspx {
}
void init(const QList &handles) {
+ auto q = q_ptr;
for (auto handle : handles) {
- itemList.append(getItem(handle, true));
+ auto item = getItem(handle, true);
+ itemList.append(item);
+ setList(item, q);
}
}
@@ -45,6 +48,7 @@ namespace dspx {
auto q = q_ptr;
Q_EMIT q->itemAboutToInsert(index, item);
itemList.insert(index, item);
+ setList(item, q);
Q_EMIT q->itemInserted(index, item);
Q_EMIT q->sizeChanged(itemList.size());
Q_EMIT q->itemsChanged();
@@ -55,6 +59,7 @@ namespace dspx {
auto item = itemList.at(index);
Q_EMIT q->itemAboutToRemove(index, item);
itemList.removeAt(index);
+ setList(item, nullptr);
Q_EMIT q->itemRemoved(index, item);
Q_EMIT q->sizeChanged(itemList.size());
Q_EMIT q->itemsChanged();
diff --git a/src/libs/application/dspxmodel/src/Master.cpp b/src/libs/application/dspxmodel/src/Master.cpp
index 95dd4e05..7f971fc6 100644
--- a/src/libs/application/dspxmodel/src/Master.cpp
+++ b/src/libs/application/dspxmodel/src/Master.cpp
@@ -16,18 +16,36 @@ namespace dspx {
public:
Master *q_ptr;
ModelPrivate *pModel;
+ Handle handle;
BusControl *control;
+ bool multiChannelOutput{};
};
Master::Master(Model *model) : QObject(model), d_ptr(new MasterPrivate) {
Q_D(Master);
d->q_ptr = this;
d->pModel = ModelPrivate::get(model);
+ d->handle = model->handle();
d->control = d->pModel->createObject(model->handle());
}
void Master::handleProxySetEntityProperty(int property, const QVariant &value) {
Q_D(Master);
- ModelPrivate::proxySetEntityPropertyNotify(d->control, property, value);
+ switch (property) {
+ case ModelStrategy::P_ControlGain:
+ case ModelStrategy::P_ControlPan:
+ case ModelStrategy::P_ControlMute: {
+ ModelPrivate::proxySetEntityPropertyNotify(d->control, property, value);
+ break;
+ }
+ case ModelStrategy::P_MultiChannelOutput: {
+ d->multiChannelOutput = value.toBool();
+ Q_EMIT multiChannelOutputChanged(d->multiChannelOutput);
+ break;
+ }
+ default:
+ Q_UNREACHABLE();
+ }
+
}
Master::~Master() = default;
@@ -35,6 +53,14 @@ namespace dspx {
Q_D(const Master);
return d->control;
}
+ bool Master::multiChannelOutput() const {
+ Q_D(const Master);
+ return d->multiChannelOutput;
+ }
+ void Master::setMultiChannelOutput(bool multiChannelOutput) {
+ Q_D(Master);
+ d->pModel->strategy->setEntityProperty(d->handle, ModelStrategy::P_MultiChannelOutput, multiChannelOutput);
+ }
QDspx::Master Master::toQDspx() const {
return {
diff --git a/src/libs/application/dspxmodel/src/Master.h b/src/libs/application/dspxmodel/src/Master.h
index 507ae783..d0ac456d 100644
--- a/src/libs/application/dspxmodel/src/Master.h
+++ b/src/libs/application/dspxmodel/src/Master.h
@@ -1,9 +1,10 @@
#ifndef DIFFSCOPE_DSPX_MODEL_MASTER_H
#define DIFFSCOPE_DSPX_MODEL_MASTER_H
+#include
#include
-#include
+#include
namespace QDspx {
struct Master;
@@ -19,21 +20,27 @@ namespace dspx {
class MasterPrivate;
- class Master : public QObject {
+ class DSPX_MODEL_EXPORT Master : public QObject {
Q_OBJECT
QML_ELEMENT
QML_UNCREATABLE("")
Q_DECLARE_PRIVATE(Master)
Q_PROPERTY(BusControl *control READ control CONSTANT)
-
+ Q_PROPERTY(bool multiChannelOutput READ multiChannelOutput WRITE setMultiChannelOutput NOTIFY multiChannelOutputChanged)
public:
~Master() override;
BusControl *control() const;
+ bool multiChannelOutput() const;
+ void setMultiChannelOutput(bool multiChannelOutput);
+
QDspx::Master toQDspx() const;
void fromQDspx(const QDspx::Master &master);
+ Q_SIGNALS:
+ void multiChannelOutputChanged(bool multiChannelOutput);
+
private:
friend class ModelPrivate;
explicit Master(Model *model);
diff --git a/src/libs/application/dspxmodel/src/Model.cpp b/src/libs/application/dspxmodel/src/Model.cpp
index a91105e0..b17401c5 100644
--- a/src/libs/application/dspxmodel/src/Model.cpp
+++ b/src/libs/application/dspxmodel/src/Model.cpp
@@ -90,6 +90,15 @@ namespace dspx {
}
});
+ QObject::connect(strategy, &ModelStrategy::moveToAnotherSequenceContainerNotified, q, [=, this](Handle sequenceContainerEntity, Handle entity, Handle otherSequenceContainerEntity) {
+ auto sequenceContainerObject = mapToObject(sequenceContainerEntity);
+ auto otherSequenceContainerObject = mapToObject(otherSequenceContainerEntity);
+ if (sequenceContainerObject && otherSequenceContainerObject) {
+ sequenceContainerObject->handleMoveToAnotherSequenceContainer(entity, otherSequenceContainerEntity);
+ otherSequenceContainerObject->handleMoveFromAnotherSequenceContainer(entity, sequenceContainerEntity);
+ }
+ });
+
QObject::connect(strategy, &ModelStrategy::takeFromContainerNotified, q, [=, this](Handle takenEntity, Handle sequenceContainerEntity, Handle entity) {
if (auto sequenceContainerObject = mapToObject(sequenceContainerEntity)) {
sequenceContainerObject->handleTakeFromSequenceContainer(takenEntity, entity);
@@ -210,7 +219,7 @@ namespace dspx {
}
QDspx::Model Model::toQDspx() const {
- return {
+ QDspx::Model model = {
.version = QDspx::Model::V1,
.content = {
.global = global()->toQDspx(),
@@ -220,6 +229,18 @@ namespace dspx {
.workspace = workspace()->toQDspx(),
}
};
+ model.content.workspace["diffscope"] = QJsonObject{
+ {"accidentalType", static_cast(global()->accidentalType())},
+ {"loop", QJsonObject{
+ {"enabled", timeline()->isLoopEnabled()},
+ {"start", timeline()->loopStart()},
+ {"length", timeline()->loopLength()},
+ }},
+ {"master", QJsonObject{
+ {"multiChannelOutput", master()->multiChannelOutput()}
+ }}
+ };
+ return model;
}
void Model::fromQDspx(const QDspx::Model &model) {
@@ -229,6 +250,29 @@ namespace dspx {
d->timeline->fromQDspx(model.content.timeline);
d->tracks->fromQDspx(model.content.tracks);
d->workspace->fromQDspx(model.content.workspace);
+ {
+ auto accidentalType = model.content.workspace.value("diffscope").value("accidentalType").toInt();
+ d->global->setAccidentalType(static_cast(accidentalType));
+ }
+ {
+ auto loop = model.content.workspace.value("diffscope").value("loop").toObject();
+ auto enabled = loop.value("enabled").toBool();
+ auto start = loop.value("start").toInt();
+ auto length = loop.value("length").toInt();
+ if (start < 0 || length <= 0) {
+ d->timeline->setLoopEnabled(false);
+ d->timeline->setLoopStart(0);
+ d->timeline->setLoopLength(1920);
+ } else {
+ d->timeline->setLoopEnabled(enabled);
+ d->timeline->setLoopStart(start);
+ d->timeline->setLoopLength(length);
+ }
+ }
+ {
+ auto master = model.content.workspace.value("diffscope").value("master").toObject();
+ d->master->setMultiChannelOutput(master.value("multiChannelOutput").toBool());
+ }
}
Label *Model::createLabel() {
@@ -341,6 +385,11 @@ namespace dspx {
Q_EMIT d->global->centShiftChanged(d->centShift);
break;
}
+ case ModelStrategy::P_AccidentalType: {
+ d->accidentalType = static_cast(value.toInt());
+ Q_EMIT d->global->accidentalTypeChanged(static_cast(d->accidentalType));
+ break;
+ }
case ModelStrategy::P_EditorId: {
d->editorId = value.toString();
Q_EMIT d->global->editorIdChanged(d->editorId);
@@ -353,10 +402,17 @@ namespace dspx {
}
case ModelStrategy::P_ControlGain:
case ModelStrategy::P_ControlPan:
- case ModelStrategy::P_ControlMute: {
+ case ModelStrategy::P_ControlMute:
+ case ModelStrategy::P_MultiChannelOutput: {
ModelPrivate::proxySetEntityPropertyNotify(d->master, property, value);
break;
}
+ case ModelStrategy::P_LoopEnabled:
+ case ModelStrategy::P_LoopLength:
+ case ModelStrategy::P_LoopStart: {
+ ModelPrivate::proxySetEntityPropertyNotify(d->timeline, property, value);
+ break;
+ }
default:
Q_UNREACHABLE();
}
diff --git a/src/libs/application/dspxmodel/src/ModelStrategy.h b/src/libs/application/dspxmodel/src/ModelStrategy.h
index 0a56fbfa..dc70161d 100644
--- a/src/libs/application/dspxmodel/src/ModelStrategy.h
+++ b/src/libs/application/dspxmodel/src/ModelStrategy.h
@@ -53,6 +53,7 @@ namespace dspx {
};
enum Property {
+ P_AccidentalType,
P_Author,
P_CentShift,
P_ClipStart,
@@ -61,15 +62,21 @@ namespace dspx {
P_ControlGain,
P_ControlMute,
P_ControlPan,
+ P_ControlRecord,
P_ControlSolo,
P_Denominator,
P_EditorId,
P_EditorName,
+ P_Height,
P_JsonObject,
P_KeyNumber,
P_Language,
P_Length,
+ P_LoopEnabled,
+ P_LoopLength,
+ P_LoopStart,
P_Measure,
+ P_MultiChannelOutput,
P_Name,
P_Numerator,
P_Onset,
@@ -132,6 +139,8 @@ namespace dspx {
virtual bool insertIntoListContainer(Handle listContainerEntity, Handle entity, int index) = 0;
virtual bool insertIntoMapContainer(Handle mapContainerEntity, Handle entity, const QString &key) = 0;
+ virtual bool moveToAnotherSequenceContainer(Handle sequenceContainerEntity, Handle entity, Handle otherSequenceContainerEntity) = 0;
+
virtual Handle takeFromSequenceContainer(Handle sequenceContainerEntity, Handle entity) = 0;
virtual Handle takeFromListContainer(Handle listContainerEntity, int index) = 0;
virtual Handle takeFromMapContainer(Handle mapContainerEntity, const QString &key) = 0;
@@ -155,6 +164,8 @@ namespace dspx {
void insertIntoListContainerNotified(Handle listContainerEntity, Handle entity, int index);
void insertIntoMapContainerNotified(Handle mapContainerEntity, Handle entity, const QString &key);
+ void moveToAnotherSequenceContainerNotified(Handle sequenceContainerEntity, Handle entity, Handle otherSequenceContainerEntity);
+
void takeFromContainerNotified(Handle takenEntity, Handle sequenceContainerEntity, Handle entity);
void takeFromListContainerNotified(Handle takenEntities, Handle listContainerEntity, int index);
void takeFromMapContainerNotified(Handle takenEntity, Handle mapContainerEntity, const QString &key);
@@ -228,6 +239,10 @@ namespace dspx {
auto v = value.toInt();
return v >= -50 && v <= 50;
};
+ static auto validateAccidentalType = [](const QVariant &value) {
+ auto v = value.toInt();
+ return v == 0 || v == 1;
+ };
static auto validatePan = [](const QVariant &value) {
auto v = value.toDouble();
return v >= -1 && v <= 1;
@@ -236,6 +251,10 @@ namespace dspx {
auto v = value.toInt();
return v >= 0;
};
+ static auto validateIntGreaterZero = [](const QVariant &value) {
+ auto v = value.toInt();
+ return v > 0;
+ };
static auto validateDoubleGreaterOrEqualZero = [](const QVariant &value) {
auto v = value.toDouble();
return v >= 0;
@@ -276,11 +295,16 @@ namespace dspx {
{P_Name, QMetaType::QString},
{P_Author, QMetaType::QString},
{P_CentShift, QMetaType::Int, validateCentShift},
+ {P_AccidentalType, QMetaType::Int, validateAccidentalType},
{P_EditorId, QMetaType::QString},
{P_EditorName, QMetaType::QString},
{P_ControlGain, QMetaType::Double},
{P_ControlPan, QMetaType::Double, validatePan},
- {P_ControlMute, QMetaType::Bool}
+ {P_ControlMute, QMetaType::Bool},
+ {P_MultiChannelOutput, QMetaType::Bool},
+ {P_LoopEnabled, QMetaType::Bool},
+ {P_LoopStart, QMetaType::Int, validateIntGreaterOrEqualZero},
+ {P_LoopLength, QMetaType::Int, validateIntGreaterZero},
};
case EI_Label: return {
{P_Position, QMetaType::Int, validateIntGreaterOrEqualZero},
@@ -348,7 +372,9 @@ namespace dspx {
{P_ControlGain, QMetaType::Double},
{P_ControlPan, QMetaType::Double, validatePan},
{P_ControlMute, QMetaType::Bool},
- {P_ControlSolo, QMetaType::Bool}
+ {P_ControlRecord, QMetaType::Bool},
+ {P_ControlSolo, QMetaType::Bool},
+ {P_Height, QMetaType::Double, validateDoubleGreaterOrEqualZero}
};
case EI_WorkspaceInfo: return {
{P_JsonObject, QMetaType::QJsonObject}
diff --git a/src/libs/application/dspxmodel/src/Model_p.h b/src/libs/application/dspxmodel/src/Model_p.h
index 4f65328a..b2fa0210 100644
--- a/src/libs/application/dspxmodel/src/Model_p.h
+++ b/src/libs/application/dspxmodel/src/Model_p.h
@@ -35,6 +35,7 @@ namespace dspx {
QString name;
QString author;
int centShift;
+ int accidentalType;
QString editorId;
QString editorName;
diff --git a/src/libs/application/dspxmodel/src/Note.cpp b/src/libs/application/dspxmodel/src/Note.cpp
index 9888c90c..6cf5281d 100644
--- a/src/libs/application/dspxmodel/src/Note.cpp
+++ b/src/libs/application/dspxmodel/src/Note.cpp
@@ -1,7 +1,5 @@
#include "Note.h"
#include "Note_p.h"
-
-#include
#include
#include
@@ -16,61 +14,7 @@
namespace dspx {
- void NotePrivate::setCentShiftUnchecked(int centShift_) {
- Q_Q(Note);
- pModel->strategy->setEntityProperty(q->handle(), ModelStrategy::P_CentShift, centShift_);
- }
-
- void NotePrivate::setCentShift(int centShift_) {
- Q_Q(Note);
- if (auto engine = qjsEngine(q); engine && (centShift_ < -50 || centShift_ > 50)) {
- engine->throwError(QJSValue::RangeError, QStringLiteral("CentShift must be in range [-50, 50]"));
- return;
- }
- setCentShiftUnchecked(centShift_);
- }
- void NotePrivate::setKeyNumUnchecked(int keyNum_) {
- Q_Q(Note);
- pModel->strategy->setEntityProperty(q->handle(), ModelStrategy::P_KeyNumber, keyNum_);
- }
-
- void NotePrivate::setKeyNum(int keyNum_) {
- Q_Q(Note);
- if (auto engine = qjsEngine(q); engine && (keyNum_ < 0 || keyNum_ > 127)) {
- engine->throwError(QJSValue::RangeError, QStringLiteral("KeyNum must be in range [0, 127]"));
- return;
- }
- setKeyNumUnchecked(keyNum_);
- }
-
- void NotePrivate::setLengthUnchecked(int length_) {
- Q_Q(Note);
- pModel->strategy->setEntityProperty(q->handle(), ModelStrategy::P_Length, length_);
- }
-
- void NotePrivate::setLength(int length_) {
- Q_Q(Note);
- if (auto engine = qjsEngine(q); engine && length_ < 0) {
- engine->throwError(QJSValue::RangeError, QStringLiteral("Length must be greater than or equal to 0"));
- return;
- }
- setLengthUnchecked(length_);
- }
-
- void NotePrivate::setPosUnchecked(int pos_) {
- Q_Q(Note);
- pModel->strategy->setEntityProperty(q->handle(), ModelStrategy::P_Position, pos_);
- }
-
- void NotePrivate::setPos(int pos_) {
- Q_Q(Note);
- if (auto engine = qjsEngine(q); engine && pos_ < 0) {
- engine->throwError(QJSValue::RangeError, QStringLiteral("Pos must be greater than or equal to 0"));
- return;
- }
- setPosUnchecked(pos_);
- }
void NotePrivate::setOverlapped(Note *item, bool overlapped) {
auto d = item->d_func();
@@ -88,6 +32,22 @@ namespace dspx {
}
}
+ void NotePrivate::setPreviousItem(Note *item, Note *previousItem) {
+ auto d = item->d_func();
+ if (d->previousItem != previousItem) {
+ d->previousItem = previousItem;
+ Q_EMIT item->previousItemChanged();
+ }
+ }
+
+ void NotePrivate::setNextItem(Note *item, Note *nextItem) {
+ auto d = item->d_func();
+ if (d->nextItem != nextItem) {
+ d->nextItem = nextItem;
+ Q_EMIT item->nextItemChanged();
+ }
+ }
+
Note::Note(Handle handle, Model *model) : EntityObject(handle, model), d_ptr(new NotePrivate) {
Q_D(Note);
Q_ASSERT(model->strategy()->getEntityType(handle) == ModelStrategy::EI_Note);
@@ -115,7 +75,7 @@ namespace dspx {
void Note::setCentShift(int centShift) {
Q_D(Note);
Q_ASSERT(centShift >= -50 && centShift <= 50);
- d->setCentShiftUnchecked(centShift);
+ d->pModel->strategy->setEntityProperty(handle(), ModelStrategy::P_CentShift, centShift);
}
int Note::keyNum() const {
@@ -126,7 +86,7 @@ namespace dspx {
void Note::setKeyNum(int keyNum) {
Q_D(Note);
Q_ASSERT(keyNum >= 0 && keyNum <= 127);
- d->setKeyNumUnchecked(keyNum);
+ d->pModel->strategy->setEntityProperty(handle(), ModelStrategy::P_KeyNumber, keyNum);
}
QString Note::language() const {
@@ -147,7 +107,7 @@ namespace dspx {
void Note::setLength(int length) {
Q_D(Note);
Q_ASSERT(length >= 0);
- d->setLengthUnchecked(length);
+ d->pModel->strategy->setEntityProperty(handle(), ModelStrategy::P_Length, length);
}
QString Note::lyric() const {
@@ -173,7 +133,7 @@ namespace dspx {
void Note::setPos(int pos) {
Q_D(Note);
Q_ASSERT(pos >= 0);
- d->setPosUnchecked(pos);
+ d->pModel->strategy->setEntityProperty(handle(), ModelStrategy::P_Position, pos);
}
Pronunciation *Note::pronunciation() const {
@@ -196,6 +156,16 @@ namespace dspx {
return d->noteSequence;
}
+ Note *Note::previousItem() const {
+ Q_D(const Note);
+ return d->previousItem;
+ }
+
+ Note *Note::nextItem() const {
+ Q_D(const Note);
+ return d->nextItem;
+ }
+
bool Note::isOverlapped() const {
Q_D(const Note);
return d->overlapped;
diff --git a/src/libs/application/dspxmodel/src/Note.h b/src/libs/application/dspxmodel/src/Note.h
index e96311d5..df79af38 100644
--- a/src/libs/application/dspxmodel/src/Note.h
+++ b/src/libs/application/dspxmodel/src/Note.h
@@ -26,17 +26,19 @@ namespace dspx {
QML_ELEMENT
QML_UNCREATABLE("")
Q_DECLARE_PRIVATE(Note)
- Q_PRIVATE_PROPERTY(d_func(), int centShift MEMBER centShift WRITE setCentShift NOTIFY centShiftChanged)
- Q_PRIVATE_PROPERTY(d_func(), int keyNum MEMBER keyNum WRITE setKeyNum NOTIFY keyNumChanged)
+ Q_PROPERTY(int centShift READ centShift WRITE setCentShift NOTIFY centShiftChanged)
+ Q_PROPERTY(int keyNum READ keyNum WRITE setKeyNum NOTIFY keyNumChanged)
Q_PROPERTY(QString language READ language WRITE setLanguage NOTIFY languageChanged)
- Q_PRIVATE_PROPERTY(d_func(), int length MEMBER length WRITE setLength NOTIFY lengthChanged)
+ Q_PROPERTY(int length READ length WRITE setLength NOTIFY lengthChanged)
Q_PROPERTY(QString lyric READ lyric WRITE setLyric NOTIFY lyricChanged)
Q_PROPERTY(PhonemeInfo *phonemes READ phonemes CONSTANT)
- Q_PRIVATE_PROPERTY(d_func(), int pos MEMBER pos WRITE setPos NOTIFY posChanged)
+ Q_PROPERTY(int pos READ pos WRITE setPos NOTIFY posChanged)
Q_PROPERTY(Pronunciation *pronunciation READ pronunciation CONSTANT)
Q_PROPERTY(Vibrato *vibrato READ vibrato CONSTANT)
Q_PROPERTY(Workspace *workspace READ workspace CONSTANT)
Q_PROPERTY(NoteSequence *noteSequence READ noteSequence NOTIFY noteSequenceChanged)
+ Q_PROPERTY(Note *previousItem READ previousItem NOTIFY previousItemChanged)
+ Q_PROPERTY(Note *nextItem READ nextItem NOTIFY nextItemChanged)
Q_PROPERTY(bool overlapped READ isOverlapped NOTIFY overlappedChanged)
public:
@@ -70,6 +72,9 @@ namespace dspx {
NoteSequence *noteSequence() const;
+ Note *previousItem() const;
+ Note *nextItem() const;
+
bool isOverlapped() const;
QDspx::Note toQDspx() const;
@@ -83,6 +88,8 @@ namespace dspx {
void lyricChanged(const QString &lyric);
void posChanged(int pos);
void noteSequenceChanged();
+ void previousItemChanged();
+ void nextItemChanged();
void overlappedChanged(bool overlapped);
protected:
diff --git a/src/libs/application/dspxmodel/src/NoteSequence.cpp b/src/libs/application/dspxmodel/src/NoteSequence.cpp
index dd73d9c4..416cabb0 100644
--- a/src/libs/application/dspxmodel/src/NoteSequence.cpp
+++ b/src/libs/application/dspxmodel/src/NoteSequence.cpp
@@ -22,13 +22,6 @@ namespace dspx {
d->singingClip = singingClip;
d->init(model->strategy()->getEntitiesFromSequenceContainer(handle));
-
- connect(this, &NoteSequence::itemInserted, this, [=](Note *item) {
- NotePrivate::setNoteSequence(item, this);
- });
- connect(this, &NoteSequence::itemRemoved, this, [=](Note *item) {
- NotePrivate::setNoteSequence(item, nullptr);
- });
}
NoteSequence::~NoteSequence() = default;
diff --git a/src/libs/application/dspxmodel/src/NoteSequence_p.h b/src/libs/application/dspxmodel/src/NoteSequence_p.h
index 950ff19b..1200cb50 100644
--- a/src/libs/application/dspxmodel/src/NoteSequence_p.h
+++ b/src/libs/application/dspxmodel/src/NoteSequence_p.h
@@ -8,7 +8,7 @@
namespace dspx {
- class NoteSequencePrivate : public RangeSequenceData {
+ class NoteSequencePrivate : public RangeSequenceData {
Q_DECLARE_PUBLIC(NoteSequence)
public:
SingingClip *singingClip{};
diff --git a/src/libs/application/dspxmodel/src/Note_p.h b/src/libs/application/dspxmodel/src/Note_p.h
index c6a70912..72d6865a 100644
--- a/src/libs/application/dspxmodel/src/Note_p.h
+++ b/src/libs/application/dspxmodel/src/Note_p.h
@@ -22,19 +22,14 @@ namespace dspx {
Vibrato *vibrato;
Workspace *workspace;
NoteSequence *noteSequence{};
+ Note *previousItem{};
+ Note *nextItem{};
bool overlapped{};
- void setCentShiftUnchecked(int centShift_);
- void setCentShift(int centShift_);
- void setKeyNumUnchecked(int keyNum_);
- void setKeyNum(int keyNum_);
- void setLengthUnchecked(int length_);
- void setLength(int length_);
- void setPosUnchecked(int pos_);
- void setPos(int pos_);
-
static void setOverlapped(Note *item, bool overlapped);
static void setNoteSequence(Note *item, NoteSequence *noteSequence);
+ static void setPreviousItem(Note *item, Note *previousItem);
+ static void setNextItem(Note *item, Note *nextItem);
};
}
diff --git a/src/libs/application/dspxmodel/src/ParamCurve.cpp b/src/libs/application/dspxmodel/src/ParamCurve.cpp
index 1da88780..6820d9f4 100644
--- a/src/libs/application/dspxmodel/src/ParamCurve.cpp
+++ b/src/libs/application/dspxmodel/src/ParamCurve.cpp
@@ -22,6 +22,22 @@ namespace dspx {
Q_EMIT paramCurve->paramCurveSequenceChanged();
}
+ void ParamCurvePrivate::setPreviousItem(ParamCurve *paramCurve, ParamCurve *previousItem) {
+ auto d = paramCurve->d_func();
+ if (d->previousItem != previousItem) {
+ d->previousItem = previousItem;
+ Q_EMIT paramCurve->previousItemChanged();
+ }
+ }
+
+ void ParamCurvePrivate::setNextItem(ParamCurve *paramCurve, ParamCurve *nextItem) {
+ auto d = paramCurve->d_func();
+ if (d->nextItem != nextItem) {
+ d->nextItem = nextItem;
+ Q_EMIT paramCurve->nextItemChanged();
+ }
+ }
+
ParamCurve::ParamCurve(CurveType type, Handle handle, Model *model)
: EntityObject(handle, model), d_ptr(new ParamCurvePrivate) {
Q_D(ParamCurve);
@@ -53,6 +69,16 @@ namespace dspx {
return d->paramCurveSequence;
}
+ ParamCurve *ParamCurve::previousItem() const {
+ Q_D(const ParamCurve);
+ return d->previousItem;
+ }
+
+ ParamCurve *ParamCurve::nextItem() const {
+ Q_D(const ParamCurve);
+ return d->nextItem;
+ }
+
QDspx::ParamCurveRef ParamCurve::toQDspx() const {
Q_D(const ParamCurve);
switch (d->type) {
diff --git a/src/libs/application/dspxmodel/src/ParamCurve.h b/src/libs/application/dspxmodel/src/ParamCurve.h
index 7cff8619..787ea0d4 100644
--- a/src/libs/application/dspxmodel/src/ParamCurve.h
+++ b/src/libs/application/dspxmodel/src/ParamCurve.h
@@ -23,6 +23,8 @@ namespace dspx {
Q_PROPERTY(int start READ start WRITE setStart NOTIFY startChanged)
Q_PROPERTY(CurveType type READ type CONSTANT)
Q_PROPERTY(ParamCurveSequence *paramCurveSequence READ paramCurveSequence NOTIFY paramCurveSequenceChanged)
+ Q_PROPERTY(ParamCurve *previousItem READ previousItem NOTIFY previousItemChanged)
+ Q_PROPERTY(ParamCurve *nextItem READ nextItem NOTIFY nextItemChanged)
public:
enum CurveType {
@@ -40,12 +42,17 @@ namespace dspx {
ParamCurveSequence *paramCurveSequence() const;
+ ParamCurve *previousItem() const;
+ ParamCurve *nextItem() const;
+
QDspx::ParamCurveRef toQDspx() const;
void fromQDspx(const QDspx::ParamCurveRef &curve);
Q_SIGNALS:
void startChanged(int start);
void paramCurveSequenceChanged();
+ void previousItemChanged();
+ void nextItemChanged();
protected:
explicit ParamCurve(CurveType type, Handle handle, Model *model);
diff --git a/src/libs/application/dspxmodel/src/ParamCurveSequence.cpp b/src/libs/application/dspxmodel/src/ParamCurveSequence.cpp
index 2ad19e7f..cc34ff57 100644
--- a/src/libs/application/dspxmodel/src/ParamCurveSequence.cpp
+++ b/src/libs/application/dspxmodel/src/ParamCurveSequence.cpp
@@ -27,13 +27,6 @@ namespace dspx {
d->param = param;
d->init(model->strategy()->getEntitiesFromSequenceContainer(handle));
-
- connect(this, &ParamCurveSequence::itemInserted, this, [this](ParamCurve *paramCurve) {
- ParamCurvePrivate::setParamCurveSequence(paramCurve, this);
- });
- connect(this, &ParamCurveSequence::itemRemoved, this, [this](ParamCurve *paramCurve) {
- ParamCurvePrivate::setParamCurveSequence(paramCurve, nullptr);
- });
}
ParamCurveSequence::~ParamCurveSequence() = default;
diff --git a/src/libs/application/dspxmodel/src/ParamCurveSequence_p.h b/src/libs/application/dspxmodel/src/ParamCurveSequence_p.h
index 8ffb2c96..e7ee2b0f 100644
--- a/src/libs/application/dspxmodel/src/ParamCurveSequence_p.h
+++ b/src/libs/application/dspxmodel/src/ParamCurveSequence_p.h
@@ -8,7 +8,7 @@
namespace dspx {
- class ParamCurveSequencePrivate : public PointSequenceData {
+ class ParamCurveSequencePrivate : public PointSequenceData {
Q_DECLARE_PUBLIC(ParamCurveSequence)
public:
Param *param{};
diff --git a/src/libs/application/dspxmodel/src/ParamCurve_p.h b/src/libs/application/dspxmodel/src/ParamCurve_p.h
index f8478651..d8ceb1ee 100644
--- a/src/libs/application/dspxmodel/src/ParamCurve_p.h
+++ b/src/libs/application/dspxmodel/src/ParamCurve_p.h
@@ -16,8 +16,12 @@ namespace dspx {
ParamCurve::CurveType type;
int start{};
ParamCurveSequence *paramCurveSequence{};
+ ParamCurve *previousItem{};
+ ParamCurve *nextItem{};
static void setParamCurveSequence(ParamCurve *item, ParamCurveSequence *paramCurveSequence);
+ static void setPreviousItem(ParamCurve *item, ParamCurve *previousItem);
+ static void setNextItem(ParamCurve *item, ParamCurve *nextItem);
};
}
diff --git a/src/libs/application/dspxmodel/src/PhonemeList.cpp b/src/libs/application/dspxmodel/src/PhonemeList.cpp
index 6b2c76de..66b87c37 100644
--- a/src/libs/application/dspxmodel/src/PhonemeList.cpp
+++ b/src/libs/application/dspxmodel/src/PhonemeList.cpp
@@ -20,13 +20,6 @@ namespace dspx {
d->phonemeInfo = phonemeInfo;
d->init(model->strategy()->getEntitiesFromListContainer(handle));
-
- connect(this, &PhonemeList::itemInserted, this, [this](int, Phoneme *item) {
- PhonemePrivate::setPhonemeList(item, this);
- });
- connect(this, &PhonemeList::itemRemoved, this, [this](int, Phoneme *item) {
- PhonemePrivate::setPhonemeList(item, nullptr);
- });
}
PhonemeList::~PhonemeList() = default;
diff --git a/src/libs/application/dspxmodel/src/PhonemeList_p.h b/src/libs/application/dspxmodel/src/PhonemeList_p.h
index 5cb257c2..336cd0a1 100644
--- a/src/libs/application/dspxmodel/src/PhonemeList_p.h
+++ b/src/libs/application/dspxmodel/src/PhonemeList_p.h
@@ -3,12 +3,13 @@
#include
#include
+#include
namespace dspx {
class PhonemeInfo;
- class PhonemeListPrivate : public ListData {
+ class PhonemeListPrivate : public ListData {
Q_DECLARE_PUBLIC(PhonemeList)
public:
PhonemeInfo *phonemeInfo{};
diff --git a/src/libs/application/dspxmodel/src/PointSequenceData_p.h b/src/libs/application/dspxmodel/src/PointSequenceData_p.h
index f05334d8..c7a2ca44 100644
--- a/src/libs/application/dspxmodel/src/PointSequenceData_p.h
+++ b/src/libs/application/dspxmodel/src/PointSequenceData_p.h
@@ -13,7 +13,14 @@ namespace dspx {
static QJSValue create(QObject *o);
};
- template
+ template <
+ class SequenceType,
+ class ItemType,
+ int (ItemType::*positionGetter)() const,
+ void (ItemType::*positionChangedSignal)(int),
+ void (*setSequence)(ItemType *item, SequenceType *sequence),
+ void (*setPreviousItem)(ItemType *item, ItemType *previousItem),
+ void (*setNextItem)(ItemType *item, ItemType *nextItem)>
class PointSequenceData {
public:
SequenceType *q_ptr;
@@ -36,9 +43,11 @@ namespace dspx {
}
void init(const QList &handles) {
+ auto q = q_ptr;
for (auto handle : handles) {
auto item = getItem(handle, true);
container.insertItem(item, (item->*positionGetter)());
+ setSequence(item, q);
}
updateFirstAndLastItem();
}
@@ -46,12 +55,31 @@ namespace dspx {
void insertItem(ItemType *item, int position) {
auto q = q_ptr;
bool containsItem = container.contains(item);
+ auto oldPreviousItem = container.previousItem(item);
+ auto oldNextItem = container.nextItem(item);
if (!containsItem) {
Q_EMIT q->itemAboutToInsert(item);
}
container.insertItem(item, position);
+ setSequence(item, q);
+ updateFirstAndLastItem();
+ auto newPreviousItem = container.previousItem(item);
+ auto newNextItem = container.nextItem(item);
+ if (oldPreviousItem) {
+ setNextItem(oldPreviousItem, oldNextItem);
+ }
+ if (oldNextItem) {
+ setPreviousItem(oldNextItem, oldPreviousItem);
+ }
+ if (newPreviousItem) {
+ setNextItem(newPreviousItem, item);
+ }
+ if (newNextItem) {
+ setPreviousItem(newNextItem, item);
+ }
+ setPreviousItem(item, newPreviousItem);
+ setNextItem(item, newNextItem);
if (!containsItem) {
- updateFirstAndLastItem();
Q_EMIT q->itemInserted(item);
Q_EMIT q->sizeChanged(container.size());
}
@@ -60,8 +88,19 @@ namespace dspx {
void removeItem(ItemType *item) {
auto q = q_ptr;
Q_EMIT q->itemAboutToRemove(item);
+ auto oldPreviousItem = container.previousItem(item);
+ auto oldNextItem = container.nextItem(item);
container.removeItem(item);
+ setSequence(item, nullptr);
updateFirstAndLastItem();
+ if (oldPreviousItem) {
+ setNextItem(oldPreviousItem, oldNextItem);
+ }
+ if (oldNextItem) {
+ setPreviousItem(oldNextItem, oldPreviousItem);
+ }
+ setPreviousItem(item, nullptr);
+ setNextItem(item, nullptr);
Q_EMIT q->itemRemoved(item);
Q_EMIT q->sizeChanged(container.size());
}
diff --git a/src/libs/application/dspxmodel/src/RangeSequenceData_p.h b/src/libs/application/dspxmodel/src/RangeSequenceData_p.h
index e5f188a2..f97c46f0 100644
--- a/src/libs/application/dspxmodel/src/RangeSequenceData_p.h
+++ b/src/libs/application/dspxmodel/src/RangeSequenceData_p.h
@@ -11,7 +11,17 @@ namespace dspx {
class PointSequenceJSIterable;
- template
+ template <
+ class SequenceType,
+ class ItemType,
+ int (ItemType::*positionGetter)() const,
+ void (ItemType::*positionChangedSignal)(int),
+ int (ItemType::*lengthGetter)() const,
+ void (ItemType::*lengthChangedSignal)(int),
+ void (*setOverlapped)(ItemType *item, bool overlapped),
+ void (*setSequence)(ItemType *item, SequenceType *sequence),
+ void (*setPreviousItem)(ItemType *item, ItemType *previousItem),
+ void (*setNextItem)(ItemType *item, ItemType *nextItem)>
class RangeSequenceData {
public:
SequenceType *q_ptr;
@@ -35,9 +45,11 @@ namespace dspx {
}
void init(const QList &handles) {
+ auto q = q_ptr;
for (auto handle : handles) {
auto item = getItem(handle, true);
pointContainer.insertItem(item, (item->*positionGetter)());
+ setSequence(item, q);
auto affectedItems = rangeContainer.insertItem(item, (item->*positionGetter)(), (item->*lengthGetter)());
for (auto affectedItem : affectedItems) {
bool isOverlapped = rangeContainer.isOverlapped(affectedItem);
@@ -53,9 +65,11 @@ namespace dspx {
if (!containsItem) {
Q_EMIT q->itemAboutToInsert(item);
}
-
+ auto oldPreviousItem = pointContainer.previousItem(item);
+ auto oldNextItem = pointContainer.nextItem(item);
// Insert into both containers
pointContainer.insertItem(item, position);
+ setSequence(item, q);
auto affectedItems = rangeContainer.insertItem(item, position, length);
// Update overlapped status for all affected items
@@ -63,9 +77,24 @@ namespace dspx {
bool isOverlapped = rangeContainer.isOverlapped(affectedItem);
setOverlapped(affectedItem, isOverlapped);
}
-
+ updateFirstAndLastItem();
+ auto newPreviousItem = pointContainer.previousItem(item);
+ auto newNextItem = pointContainer.nextItem(item);
+ if (oldPreviousItem) {
+ setNextItem(oldPreviousItem, oldNextItem);
+ }
+ if (oldNextItem) {
+ setPreviousItem(oldNextItem, oldPreviousItem);
+ }
+ if (newPreviousItem) {
+ setNextItem(newPreviousItem, item);
+ }
+ if (newNextItem) {
+ setPreviousItem(newNextItem, item);
+ }
+ setPreviousItem(item, newPreviousItem);
+ setNextItem(item, newNextItem);
if (!containsItem) {
- updateFirstAndLastItem();
Q_EMIT q->itemInserted(item);
Q_EMIT q->sizeChanged(pointContainer.size());
}
@@ -74,9 +103,11 @@ namespace dspx {
void removeItem(ItemType *item) {
auto q = q_ptr;
Q_EMIT q->itemAboutToRemove(item);
-
+ auto oldPreviousItem = pointContainer.previousItem(item);
+ auto oldNextItem = pointContainer.nextItem(item);
// Remove from both containers
pointContainer.removeItem(item);
+ setSequence(item, nullptr);
auto affectedItems = rangeContainer.removeItem(item);
// Update overlapped status for all affected items
@@ -86,6 +117,14 @@ namespace dspx {
}
updateFirstAndLastItem();
+ if (oldPreviousItem) {
+ setNextItem(oldPreviousItem, oldNextItem);
+ }
+ if (oldNextItem) {
+ setPreviousItem(oldNextItem, oldPreviousItem);
+ }
+ setPreviousItem(item, nullptr);
+ setNextItem(item, nullptr);
Q_EMIT q->itemRemoved(item);
Q_EMIT q->sizeChanged(pointContainer.size());
}
diff --git a/src/libs/application/dspxmodel/src/Tempo.cpp b/src/libs/application/dspxmodel/src/Tempo.cpp
index 0da9844a..814345dc 100644
--- a/src/libs/application/dspxmodel/src/Tempo.cpp
+++ b/src/libs/application/dspxmodel/src/Tempo.cpp
@@ -1,7 +1,5 @@
#include "Tempo.h"
#include "Tempo_p.h"
-
-#include
#include
#include
@@ -12,39 +10,27 @@
namespace dspx {
- void TempoPrivate::setPosUnchecked(int pos_) {
- Q_Q(Tempo);
- q->model()->strategy()->setEntityProperty(q->handle(), ModelStrategy::P_Position, pos_);
- }
-
- void TempoPrivate::setPos(int pos_) {
- Q_Q(Tempo);
- if (auto engine = qjsEngine(q); engine && pos_ < 0) {
- engine->throwError(QJSValue::RangeError, QStringLiteral("Pos must be greater or equal to 0"));
- return;
+ void TempoPrivate::setTempoSequence(Tempo *item, TempoSequence *tempoSequence) {
+ auto d = item->d_func();
+ if (d->tempoSequence != tempoSequence) {
+ d->tempoSequence = tempoSequence;
+ Q_EMIT item->tempoSequenceChanged();
}
- setPosUnchecked(pos_);
}
- void TempoPrivate::setValueUnchecked(double value_) {
- Q_Q(Tempo);
- q->model()->strategy()->setEntityProperty(q->handle(), ModelStrategy::P_Value, value_);
- }
-
- void TempoPrivate::setValue(double value_) {
- Q_Q(Tempo);
- if (auto engine = qjsEngine(q); engine && (value_ < 10.0 || value_ > 1000.0)) {
- engine->throwError(QJSValue::RangeError, QStringLiteral("Value must be in range [10.0, 1000.0]"));
- return;
+ void TempoPrivate::setPreviousItem(Tempo *item, Tempo *previousItem) {
+ auto d = item->d_func();
+ if (d->previousItem != previousItem) {
+ d->previousItem = previousItem;
+ Q_EMIT item->previousItemChanged();
}
- setValueUnchecked(value_);
}
- void TempoPrivate::setTempoSequence(Tempo *item, TempoSequence *tempoSequence) {
+ void TempoPrivate::setNextItem(Tempo *item, Tempo *nextItem) {
auto d = item->d_func();
- if (d->tempoSequence != tempoSequence) {
- d->tempoSequence = tempoSequence;
- Q_EMIT item->tempoSequenceChanged();
+ if (d->nextItem != nextItem) {
+ d->nextItem = nextItem;
+ Q_EMIT item->nextItemChanged();
}
}
@@ -66,7 +52,7 @@ namespace dspx {
void Tempo::setPos(int pos) {
Q_D(Tempo);
Q_ASSERT(pos >= 0);
- d->setPosUnchecked(pos);
+ model()->strategy()->setEntityProperty(handle(), ModelStrategy::P_Position, pos);
}
double Tempo::value() const {
@@ -77,7 +63,7 @@ namespace dspx {
void Tempo::setValue(double value) {
Q_D(Tempo);
Q_ASSERT(value >= 10.0 && value <= 1000.0);
- d->setValueUnchecked(value);
+ model()->strategy()->setEntityProperty(handle(), ModelStrategy::P_Value, value);
}
TempoSequence *Tempo::tempoSequence() const {
@@ -85,6 +71,16 @@ namespace dspx {
return d->tempoSequence;
}
+ Tempo *Tempo::previousItem() const {
+ Q_D(const Tempo);
+ return d->previousItem;
+ }
+
+ Tempo *Tempo::nextItem() const {
+ Q_D(const Tempo);
+ return d->nextItem;
+ }
+
QDspx::Tempo Tempo::toQDspx() const {
return {
.pos = pos(),
diff --git a/src/libs/application/dspxmodel/src/Tempo.h b/src/libs/application/dspxmodel/src/Tempo.h
index 53b71fa7..2a7c6805 100644
--- a/src/libs/application/dspxmodel/src/Tempo.h
+++ b/src/libs/application/dspxmodel/src/Tempo.h
@@ -19,9 +19,11 @@ namespace dspx {
QML_ELEMENT
QML_UNCREATABLE("")
Q_DECLARE_PRIVATE(Tempo);
- Q_PRIVATE_PROPERTY(d_func(), int pos MEMBER pos WRITE setPos NOTIFY posChanged)
- Q_PRIVATE_PROPERTY(d_func(), double value MEMBER value WRITE setValue NOTIFY valueChanged)
+ Q_PROPERTY(int pos READ pos WRITE setPos NOTIFY posChanged)
+ Q_PROPERTY(double value READ value WRITE setValue NOTIFY valueChanged)
Q_PROPERTY(TempoSequence *tempoSequence READ tempoSequence NOTIFY tempoSequenceChanged)
+ Q_PROPERTY(Tempo *previousItem READ previousItem NOTIFY previousItemChanged)
+ Q_PROPERTY(Tempo *nextItem READ nextItem NOTIFY nextItemChanged)
public:
~Tempo() override;
@@ -33,6 +35,9 @@ namespace dspx {
TempoSequence *tempoSequence() const;
+ Tempo *previousItem() const;
+ Tempo *nextItem() const;
+
QDspx::Tempo toQDspx() const;
void fromQDspx(const QDspx::Tempo &tempo);
@@ -40,6 +45,8 @@ namespace dspx {
void posChanged(int pos);
void valueChanged(double value);
void tempoSequenceChanged();
+ void previousItemChanged();
+ void nextItemChanged();
protected:
void handleSetEntityProperty(int property, const QVariant &value) override;
diff --git a/src/libs/application/dspxmodel/src/TempoSequence.cpp b/src/libs/application/dspxmodel/src/TempoSequence.cpp
index 3372afa4..2f9938ab 100644
--- a/src/libs/application/dspxmodel/src/TempoSequence.cpp
+++ b/src/libs/application/dspxmodel/src/TempoSequence.cpp
@@ -20,13 +20,6 @@ namespace dspx {
d->pModel = ModelPrivate::get(model);
d->init(model->strategy()->getEntitiesFromSequenceContainer(handle));
-
- connect(this, &TempoSequence::itemInserted, this, [=](Tempo *item) {
- TempoPrivate::setTempoSequence(item, this);
- });
- connect(this, &TempoSequence::itemRemoved, this, [=](Tempo *item) {
- TempoPrivate::setTempoSequence(item, nullptr);
- });
}
TempoSequence::~TempoSequence() = default;
diff --git a/src/libs/application/dspxmodel/src/TempoSequence_p.h b/src/libs/application/dspxmodel/src/TempoSequence_p.h
index 44435be5..ec90a19f 100644
--- a/src/libs/application/dspxmodel/src/TempoSequence_p.h
+++ b/src/libs/application/dspxmodel/src/TempoSequence_p.h
@@ -8,7 +8,7 @@
namespace dspx {
- class TempoSequencePrivate : public PointSequenceData {
+ class TempoSequencePrivate : public PointSequenceData {
Q_DECLARE_PUBLIC(TempoSequence)
};
diff --git a/src/libs/application/dspxmodel/src/Tempo_p.h b/src/libs/application/dspxmodel/src/Tempo_p.h
index 8bb93871..2dee59a1 100644
--- a/src/libs/application/dspxmodel/src/Tempo_p.h
+++ b/src/libs/application/dspxmodel/src/Tempo_p.h
@@ -12,13 +12,12 @@ namespace dspx {
int pos;
double value;
TempoSequence *tempoSequence{};
-
- void setPosUnchecked(int pos_);
- void setPos(int pos_);
- void setValueUnchecked(double value_);
- void setValue(double value_);
+ Tempo *previousItem{};
+ Tempo *nextItem{};
static void setTempoSequence(Tempo *item, TempoSequence *tempoSequence);
+ static void setPreviousItem(Tempo *item, Tempo *previousItem);
+ static void setNextItem(Tempo *item, Tempo *nextItem);
};
}
diff --git a/src/libs/application/dspxmodel/src/TimeSignature.cpp b/src/libs/application/dspxmodel/src/TimeSignature.cpp
index 5148bf3d..471d4cce 100644
--- a/src/libs/application/dspxmodel/src/TimeSignature.cpp
+++ b/src/libs/application/dspxmodel/src/TimeSignature.cpp
@@ -1,7 +1,5 @@
#include "TimeSignature.h"
#include "TimeSignature_p.h"
-
-#include
#include
#include
@@ -13,58 +11,30 @@
namespace dspx {
static constexpr bool validateDenominator(int d) {
- return d == 1 || d == 2 || d == 4 || d == 8 || d == 16 || d == 32 || d == 64 || d == 128 || d == 256;
- }
-
- void TimeSignaturePrivate::setIndexUnchecked(int index_) {
- Q_Q(TimeSignature);
- q->model()->strategy()->setEntityProperty(q->handle(), ModelStrategy::P_Measure, index_);
- }
-
- void TimeSignaturePrivate::setIndex(int index_) {
- Q_Q(TimeSignature);
- if (auto engine = qjsEngine(q); engine && index_ < 0) {
- engine->throwError(QJSValue::RangeError, QStringLiteral("Index must be greater or equal to 0"));
- return;
- }
- setIndexUnchecked(index_);
- }
-
- void TimeSignaturePrivate::setNumeratorUnchecked(int numerator_) {
- Q_Q(TimeSignature);
- q->model()->strategy()->setEntityProperty(q->handle(), ModelStrategy::P_Numerator, numerator_);
+ return d == 1 || d == 2 || d == 4 || d == 8 || d == 16 || d == 32 || d == 64 || d == 128;
}
- void TimeSignaturePrivate::setNumerator(int numerator_) {
- Q_Q(TimeSignature);
- if (auto engine = qjsEngine(q); engine && numerator_ < 1) {
- engine->throwError(QJSValue::RangeError, QStringLiteral("Numerator must be greater or equal to 1"));
- return;
+ void TimeSignaturePrivate::setTimeSignatureSequence(TimeSignature *item, TimeSignatureSequence *timeSignatureSequence) {
+ auto d = item->d_func();
+ if (d->timeSignatureSequence != timeSignatureSequence) {
+ d->timeSignatureSequence = timeSignatureSequence;
+ Q_EMIT item->timeSignatureSequenceChanged();
}
- setNumeratorUnchecked(numerator_);
}
- void TimeSignaturePrivate::setDenominatorUnchecked(int denominator_) {
- Q_Q(TimeSignature);
- q->model()->strategy()->setEntityProperty(q->handle(), ModelStrategy::P_Denominator, denominator_);
- }
-
- void TimeSignaturePrivate::setDenominator(int denominator_) {
- Q_Q(TimeSignature);
- if (auto engine = qjsEngine(q); engine) {
- if (!validateDenominator(denominator_)) {
- engine->throwError(QJSValue::RangeError, QStringLiteral("Denominator must be one of: 1, 2, 4, 8, 16, 32, 64, 128, 256"));
- return;
- }
+ void TimeSignaturePrivate::setPreviousItem(TimeSignature *item, TimeSignature *previousItem) {
+ auto d = item->d_func();
+ if (d->previousItem != previousItem) {
+ d->previousItem = previousItem;
+ Q_EMIT item->previousItemChanged();
}
- setDenominatorUnchecked(denominator_);
}
- void TimeSignaturePrivate::setTimeSignatureSequence(TimeSignature *item, TimeSignatureSequence *timeSignatureSequence) {
+ void TimeSignaturePrivate::setNextItem(TimeSignature *item, TimeSignature *nextItem) {
auto d = item->d_func();
- if (d->timeSignatureSequence != timeSignatureSequence) {
- d->timeSignatureSequence = timeSignatureSequence;
- Q_EMIT item->timeSignatureSequenceChanged();
+ if (d->nextItem != nextItem) {
+ d->nextItem = nextItem;
+ Q_EMIT item->nextItemChanged();
}
}
@@ -87,7 +57,7 @@ namespace dspx {
void TimeSignature::setIndex(int index) {
Q_D(TimeSignature);
Q_ASSERT(index >= 0);
- d->setIndexUnchecked(index);
+ model()->strategy()->setEntityProperty(handle(), ModelStrategy::P_Measure, index);
}
int TimeSignature::numerator() const {
@@ -97,8 +67,8 @@ namespace dspx {
void TimeSignature::setNumerator(int numerator) {
Q_D(TimeSignature);
- Q_ASSERT(numerator >= 1);
- d->setNumeratorUnchecked(numerator);
+ Q_ASSERT(numerator > 0);
+ model()->strategy()->setEntityProperty(handle(), ModelStrategy::P_Numerator, numerator);
}
int TimeSignature::denominator() const {
@@ -109,7 +79,7 @@ namespace dspx {
void TimeSignature::setDenominator(int denominator) {
Q_D(TimeSignature);
Q_ASSERT(validateDenominator(denominator));
- d->setDenominatorUnchecked(denominator);
+ model()->strategy()->setEntityProperty(handle(), ModelStrategy::P_Denominator, denominator);
}
TimeSignatureSequence *TimeSignature::timeSignatureSequence() const {
@@ -117,6 +87,16 @@ namespace dspx {
return d->timeSignatureSequence;
}
+ TimeSignature *TimeSignature::previousItem() const {
+ Q_D(const TimeSignature);
+ return d->previousItem;
+ }
+
+ TimeSignature *TimeSignature::nextItem() const {
+ Q_D(const TimeSignature);
+ return d->nextItem;
+ }
+
QDspx::TimeSignature TimeSignature::toQDspx() const {
return {
.index = index(),
diff --git a/src/libs/application/dspxmodel/src/TimeSignature.h b/src/libs/application/dspxmodel/src/TimeSignature.h
index 99f23148..b3d17f72 100644
--- a/src/libs/application/dspxmodel/src/TimeSignature.h
+++ b/src/libs/application/dspxmodel/src/TimeSignature.h
@@ -19,10 +19,12 @@ namespace dspx {
QML_ELEMENT
QML_UNCREATABLE("")
Q_DECLARE_PRIVATE(TimeSignature);
- Q_PRIVATE_PROPERTY(d_func(), int index MEMBER index WRITE setIndex NOTIFY indexChanged)
- Q_PRIVATE_PROPERTY(d_func(), int numerator MEMBER numerator WRITE setNumerator NOTIFY numeratorChanged)
- Q_PRIVATE_PROPERTY(d_func(), int denominator MEMBER denominator WRITE setDenominator NOTIFY denominatorChanged)
+ Q_PROPERTY(int index READ index WRITE setIndex NOTIFY indexChanged)
+ Q_PROPERTY(int numerator READ numerator WRITE setNumerator NOTIFY numeratorChanged)
+ Q_PROPERTY(int denominator READ denominator WRITE setDenominator NOTIFY denominatorChanged)
Q_PROPERTY(TimeSignatureSequence *timeSignatureSequence READ timeSignatureSequence NOTIFY timeSignatureSequenceChanged)
+ Q_PROPERTY(TimeSignature *previousItem READ previousItem NOTIFY previousItemChanged)
+ Q_PROPERTY(TimeSignature *nextItem READ nextItem NOTIFY nextItemChanged)
public:
~TimeSignature() override;
@@ -37,6 +39,9 @@ namespace dspx {
TimeSignatureSequence *timeSignatureSequence() const;
+ TimeSignature *previousItem() const;
+ TimeSignature *nextItem() const;
+
QDspx::TimeSignature toQDspx() const;
void fromQDspx(const QDspx::TimeSignature &timeSignature);
@@ -45,6 +50,8 @@ namespace dspx {
void numeratorChanged(int numerator);
void denominatorChanged(int denominator);
void timeSignatureSequenceChanged();
+ void previousItemChanged();
+ void nextItemChanged();
protected:
void handleSetEntityProperty(int property, const QVariant &value) override;
diff --git a/src/libs/application/dspxmodel/src/TimeSignatureSequence.cpp b/src/libs/application/dspxmodel/src/TimeSignatureSequence.cpp
index 441019f0..b7486502 100644
--- a/src/libs/application/dspxmodel/src/TimeSignatureSequence.cpp
+++ b/src/libs/application/dspxmodel/src/TimeSignatureSequence.cpp
@@ -20,13 +20,6 @@ namespace dspx {
d->pModel = ModelPrivate::get(model);
d->init(model->strategy()->getEntitiesFromSequenceContainer(handle));
-
- connect(this, &TimeSignatureSequence::itemInserted, this, [=](TimeSignature *item) {
- TimeSignaturePrivate::setTimeSignatureSequence(item, this);
- });
- connect(this, &TimeSignatureSequence::itemRemoved, this, [=](TimeSignature *item) {
- TimeSignaturePrivate::setTimeSignatureSequence(item, nullptr);
- });
}
TimeSignatureSequence::~TimeSignatureSequence() = default;
diff --git a/src/libs/application/dspxmodel/src/TimeSignatureSequence_p.h b/src/libs/application/dspxmodel/src/TimeSignatureSequence_p.h
index 3cf31cbe..c8a43f20 100644
--- a/src/libs/application/dspxmodel/src/TimeSignatureSequence_p.h
+++ b/src/libs/application/dspxmodel/src/TimeSignatureSequence_p.h
@@ -8,7 +8,7 @@
namespace dspx {
- class TimeSignatureSequencePrivate : public PointSequenceData {
+ class TimeSignatureSequencePrivate : public PointSequenceData {
Q_DECLARE_PUBLIC(TimeSignatureSequence)
};
diff --git a/src/libs/application/dspxmodel/src/TimeSignature_p.h b/src/libs/application/dspxmodel/src/TimeSignature_p.h
index 7a787946..32d27f03 100644
--- a/src/libs/application/dspxmodel/src/TimeSignature_p.h
+++ b/src/libs/application/dspxmodel/src/TimeSignature_p.h
@@ -13,15 +13,12 @@ namespace dspx {
int numerator;
int denominator;
TimeSignatureSequence *timeSignatureSequence{};
-
- void setIndexUnchecked(int index_);
- void setIndex(int index_);
- void setNumeratorUnchecked(int numerator_);
- void setNumerator(int numerator_);
- void setDenominatorUnchecked(int denominator_);
- void setDenominator(int denominator_);
+ TimeSignature *previousItem{};
+ TimeSignature *nextItem{};
static void setTimeSignatureSequence(TimeSignature *item, TimeSignatureSequence *timeSignatureSequence);
+ static void setPreviousItem(TimeSignature *item, TimeSignature *previousItem);
+ static void setNextItem(TimeSignature *item, TimeSignature *nextItem);
};
}
diff --git a/src/libs/application/dspxmodel/src/Timeline.cpp b/src/libs/application/dspxmodel/src/Timeline.cpp
index 7119ef2c..0867647e 100644
--- a/src/libs/application/dspxmodel/src/Timeline.cpp
+++ b/src/libs/application/dspxmodel/src/Timeline.cpp
@@ -2,6 +2,7 @@
#include
+#include
#include
#include
#include
@@ -14,12 +15,39 @@ namespace dspx {
public:
Timeline *q_ptr;
ModelPrivate *pModel;
+ Handle handle;
+
+ bool loopEnabled{false};
+ int loopStart{0};
+ int loopLength{1920};
};
- Timeline::Timeline(Model *model) : QObject(model), d_ptr(new TimelinePrivate) {
+ Timeline::Timeline(Model *model)
+ : QObject(model), d_ptr(new TimelinePrivate) {
Q_D(Timeline);
d->q_ptr = this;
d->pModel = ModelPrivate::get(model);
+ d->handle = model->handle();
+ }
+
+ void Timeline::handleProxySetEntityProperty(int property, const QVariant &value) {
+ Q_D(Timeline);
+ switch (property) {
+ case ModelStrategy::P_LoopEnabled:
+ d->loopEnabled = value.toBool();
+ Q_EMIT loopEnabledChanged(d->loopEnabled);
+ break;
+ case ModelStrategy::P_LoopStart:
+ d->loopStart = value.toInt();
+ Q_EMIT loopStartChanged(d->loopStart);
+ break;
+ case ModelStrategy::P_LoopLength:
+ d->loopLength = value.toInt();
+ Q_EMIT loopLengthChanged(d->loopLength);
+ break;
+ default:
+ Q_UNREACHABLE();
+ }
}
Timeline::~Timeline() = default;
@@ -39,6 +67,38 @@ namespace dspx {
return d->pModel->timeSignatures;
}
+ bool Timeline::isLoopEnabled() const {
+ Q_D(const Timeline);
+ return d->loopEnabled;
+ }
+
+ void Timeline::setLoopEnabled(bool enabled) {
+ Q_D(Timeline);
+ d->pModel->strategy->setEntityProperty(d->handle, ModelStrategy::P_LoopEnabled, enabled);
+ }
+
+ int Timeline::loopStart() const {
+ Q_D(const Timeline);
+ return d->loopStart;
+ }
+
+ void Timeline::setLoopStart(int loopStart) {
+ Q_D(Timeline);
+ Q_ASSERT(loopStart >= 0);
+ d->pModel->strategy->setEntityProperty(d->handle, ModelStrategy::P_LoopStart, loopStart);
+ }
+
+ int Timeline::loopLength() const {
+ Q_D(const Timeline);
+ return d->loopLength;
+ }
+
+ void Timeline::setLoopLength(int loopLength) {
+ Q_D(Timeline);
+ Q_ASSERT(loopLength > 0);
+ d->pModel->strategy->setEntityProperty(d->handle, ModelStrategy::P_LoopLength, loopLength);
+ }
+
QDspx::Timeline Timeline::toQDspx() const {
return {
labels()->toQDspx(),
diff --git a/src/libs/application/dspxmodel/src/Timeline.h b/src/libs/application/dspxmodel/src/Timeline.h
index 8ce439e2..7eb87d8c 100644
--- a/src/libs/application/dspxmodel/src/Timeline.h
+++ b/src/libs/application/dspxmodel/src/Timeline.h
@@ -29,6 +29,9 @@ namespace dspx {
Q_PROPERTY(LabelSequence *labels READ labels CONSTANT)
Q_PROPERTY(TempoSequence *tempos READ tempos CONSTANT)
Q_PROPERTY(TimeSignatureSequence *timeSignatures READ timeSignatures CONSTANT)
+ Q_PROPERTY(bool loopEnabled READ isLoopEnabled WRITE setLoopEnabled NOTIFY loopEnabledChanged)
+ Q_PROPERTY(int loopStart READ loopStart WRITE setLoopStart NOTIFY loopStartChanged)
+ Q_PROPERTY(int loopLength READ loopLength WRITE setLoopLength NOTIFY loopLengthChanged)
public:
~Timeline() override;
@@ -36,12 +39,27 @@ namespace dspx {
TempoSequence *tempos() const;
TimeSignatureSequence *timeSignatures() const;
+ bool isLoopEnabled() const;
+ void setLoopEnabled(bool enabled);
+
+ int loopStart() const;
+ void setLoopStart(int loopStart);
+
+ int loopLength() const;
+ void setLoopLength(int loopLength);
+
QDspx::Timeline toQDspx() const;
void fromQDspx(const QDspx::Timeline &timeline);
+ Q_SIGNALS:
+ void loopEnabledChanged(bool enabled);
+ void loopStartChanged(int loopStart);
+ void loopLengthChanged(int loopLength);
+
private:
friend class ModelPrivate;
explicit Timeline(Model *model);
+ void handleProxySetEntityProperty(int property, const QVariant &value);
QScopedPointer d_ptr;
};
diff --git a/src/libs/application/dspxmodel/src/Track.cpp b/src/libs/application/dspxmodel/src/Track.cpp
index cb10f594..a0e7140d 100644
--- a/src/libs/application/dspxmodel/src/Track.cpp
+++ b/src/libs/application/dspxmodel/src/Track.cpp
@@ -31,6 +31,7 @@ namespace dspx {
d->pModel = ModelPrivate::get(model);
d->name = d->pModel->strategy->getEntityProperty(handle, ModelStrategy::P_Name).toString();
d->colorId = d->pModel->strategy->getEntityProperty(handle, ModelStrategy::P_ColorId).toInt();
+ d->height = d->pModel->strategy->getEntityProperty(handle, ModelStrategy::P_Height).toDouble();
d->control = d->pModel->createObject(handle);
d->workspace = d->pModel->createObject(d->pModel->strategy->getAssociatedSubEntity(handle, ModelStrategy::R_Workspace));
d->clips = d->pModel->createObject(this, d->pModel->strategy->getAssociatedSubEntity(handle, ModelStrategy::R_Children));
@@ -53,6 +54,16 @@ namespace dspx {
d->pModel->strategy->setEntityProperty(handle(), ModelStrategy::P_ColorId, colorId);
}
+ double Track::height() const {
+ Q_D(const Track);
+ return d->height;
+ }
+
+ void Track::setHeight(double height) {
+ Q_D(Track);
+ d->pModel->strategy->setEntityProperty(handle(), ModelStrategy::P_Height, height);
+ }
+
TrackControl *Track::control() const {
Q_D(const Track);
return d->control;
@@ -80,7 +91,11 @@ namespace dspx {
.clips = clips()->toQDspx(),
.workspace = workspace()->toQDspx(),
};
- track.workspace["diffscope"]["colorId"] = colorId();
+ track.workspace["diffscope"] = QJsonObject{
+ {"colorId", colorId()},
+ {"height", height()},
+ {"record", control()->record()},
+ };
return track;
}
@@ -90,6 +105,8 @@ namespace dspx {
clips()->fromQDspx(track.clips);
workspace()->fromQDspx(track.workspace);
setColorId(track.workspace["diffscope"]["colorId"].toInt());
+ setHeight(track.workspace["diffscope"]["height"].toDouble(80));
+ control()->setRecord(track.workspace["diffscope"]["record"].toBool());
}
TrackList *Track::trackList() const {
@@ -110,9 +127,15 @@ namespace dspx {
Q_EMIT colorIdChanged(d->colorId);
break;
}
+ case ModelStrategy::P_Height: {
+ d->height = value.toDouble();
+ Q_EMIT heightChanged(d->height);
+ break;
+ }
case ModelStrategy::P_ControlGain:
case ModelStrategy::P_ControlPan:
case ModelStrategy::P_ControlMute:
+ case ModelStrategy::P_ControlRecord:
case ModelStrategy::P_ControlSolo: {
ModelPrivate::proxySetEntityPropertyNotify(d->control, property, value);
break;
diff --git a/src/libs/application/dspxmodel/src/Track.h b/src/libs/application/dspxmodel/src/Track.h
index c686f3d6..c49fd48c 100644
--- a/src/libs/application/dspxmodel/src/Track.h
+++ b/src/libs/application/dspxmodel/src/Track.h
@@ -26,6 +26,7 @@ namespace dspx {
Q_DECLARE_PRIVATE(Track)
Q_PROPERTY(ClipSequence *clips READ clips CONSTANT)
Q_PROPERTY(int colorId READ colorId WRITE setColorId NOTIFY colorIdChanged)
+ Q_PROPERTY(double height READ height WRITE setHeight NOTIFY heightChanged)
Q_PROPERTY(TrackControl *control READ control CONSTANT)
Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged)
Q_PROPERTY(Workspace *workspace READ workspace CONSTANT)
@@ -39,6 +40,9 @@ namespace dspx {
int colorId() const;
void setColorId(int colorId);
+ double height() const;
+ void setHeight(double height);
+
TrackControl *control() const;
QString name() const;
@@ -54,6 +58,7 @@ namespace dspx {
Q_SIGNALS:
void nameChanged(const QString &name);
void colorIdChanged(int colorId);
+ void heightChanged(double height);
void trackListChanged();
protected:
diff --git a/src/libs/application/dspxmodel/src/TrackControl.cpp b/src/libs/application/dspxmodel/src/TrackControl.cpp
index 1e543252..f3142173 100644
--- a/src/libs/application/dspxmodel/src/TrackControl.cpp
+++ b/src/libs/application/dspxmodel/src/TrackControl.cpp
@@ -13,6 +13,7 @@ namespace dspx {
TrackControl *q_ptr;
ModelPrivate *pModel;
bool solo;
+ bool record;
};
TrackControl::TrackControl(Handle handle, Model *model) : Control(handle, model), d_ptr(new TrackControlPrivate) {
@@ -20,6 +21,7 @@ namespace dspx {
d->q_ptr = this;
d->pModel = ModelPrivate::get(model);
d->solo = d->pModel->strategy->getEntityProperty(handle, ModelStrategy::P_ControlSolo).toBool();
+ d->record = d->pModel->strategy->getEntityProperty(handle, ModelStrategy::P_ControlRecord).toBool();
}
TrackControl::~TrackControl() = default;
@@ -34,6 +36,16 @@ namespace dspx {
d->pModel->strategy->setEntityProperty(handle(), ModelStrategy::P_ControlSolo, solo);
}
+ bool TrackControl::record() const {
+ Q_D(const TrackControl);
+ return d->record;
+ }
+
+ void TrackControl::setRecord(bool record) {
+ Q_D(TrackControl);
+ d->pModel->strategy->setEntityProperty(handle(), ModelStrategy::P_ControlRecord, record);
+ }
+
QDspx::TrackControl TrackControl::toQDspx() const {
return {
.gain = gain(),
@@ -57,6 +69,11 @@ namespace dspx {
Q_EMIT soloChanged(d->solo);
break;
}
+ case ModelStrategy::P_ControlRecord: {
+ d->record = value.toBool();
+ Q_EMIT recordChanged(d->record);
+ break;
+ }
default: {
Control::handleProxySetEntityProperty(property, value);
}
diff --git a/src/libs/application/dspxmodel/src/TrackControl.h b/src/libs/application/dspxmodel/src/TrackControl.h
index d7018840..5d077a01 100644
--- a/src/libs/application/dspxmodel/src/TrackControl.h
+++ b/src/libs/application/dspxmodel/src/TrackControl.h
@@ -11,12 +11,13 @@ namespace dspx {
class TrackControlPrivate;
- class TrackControl : public Control {
+ class DSPX_MODEL_EXPORT TrackControl : public Control {
Q_OBJECT
QML_ELEMENT
QML_UNCREATABLE("")
Q_DECLARE_PRIVATE(TrackControl)
Q_PROPERTY(bool solo READ solo WRITE setSolo NOTIFY soloChanged)
+ Q_PROPERTY(bool record READ record WRITE setRecord NOTIFY recordChanged)
public:
~TrackControl() override;
@@ -24,11 +25,15 @@ namespace dspx {
bool solo() const;
void setSolo(bool solo);
+ bool record() const;
+ void setRecord(bool record);
+
QDspx::TrackControl toQDspx() const;
void fromQDspx(const QDspx::TrackControl &trackControl);
Q_SIGNALS:
void soloChanged(bool solo);
+ void recordChanged(bool record);
private:
friend class ModelPrivate;
diff --git a/src/libs/application/dspxmodel/src/TrackList.cpp b/src/libs/application/dspxmodel/src/TrackList.cpp
index 65b3ce9b..eafbee62 100644
--- a/src/libs/application/dspxmodel/src/TrackList.cpp
+++ b/src/libs/application/dspxmodel/src/TrackList.cpp
@@ -10,7 +10,7 @@
namespace dspx {
- class TrackListPrivate : public ListData {
+ class TrackListPrivate : public ListData {
Q_DECLARE_PUBLIC(TrackList)
};
@@ -21,13 +21,6 @@ namespace dspx {
d->pModel = ModelPrivate::get(model);
d->init(model->strategy()->getEntitiesFromListContainer(handle));
-
- connect(this, &TrackList::itemInserted, this, [this](int, Track *item) {
- TrackPrivate::setTrackList(item, this);
- });
- connect(this, &TrackList::itemRemoved, this, [this](int, Track *item) {
- TrackPrivate::setTrackList(item, nullptr);
- });
}
TrackList::~TrackList() = default;
diff --git a/src/libs/application/dspxmodel/src/Track_p.h b/src/libs/application/dspxmodel/src/Track_p.h
index 4f597df9..4a4caeba 100644
--- a/src/libs/application/dspxmodel/src/Track_p.h
+++ b/src/libs/application/dspxmodel/src/Track_p.h
@@ -13,6 +13,7 @@ namespace dspx {
ClipSequence *clips;
QString name;
int colorId;
+ double height;
TrackControl *control;
Workspace *workspace;
TrackList *trackList;
diff --git a/src/libs/application/dspxmodel/src/UndoableModelStrategy.cpp b/src/libs/application/dspxmodel/src/UndoableModelStrategy.cpp
index bfd2ad73..ff1f3c0b 100644
--- a/src/libs/application/dspxmodel/src/UndoableModelStrategy.cpp
+++ b/src/libs/application/dspxmodel/src/UndoableModelStrategy.cpp
@@ -74,6 +74,10 @@ namespace dspx {
for (auto child : std::as_const(map->map)) {
recursivelyCreate(child);
}
+ } else if (auto item = qobject_cast(object)) {
+ for (auto child: std::as_const(item->associatedSubEntities)) {
+ recursivelyCreate(child);
+ }
}
}
@@ -90,6 +94,10 @@ namespace dspx {
for (auto child : std::as_const(map->map)) {
recursivelyDestroy(child);
}
+ } else if (auto item = qobject_cast(object)) {
+ for (auto child: std::as_const(item->associatedSubEntities)) {
+ recursivelyDestroy(child);
+ }
}
object->setParent(nullptr);
@@ -163,6 +171,35 @@ namespace dspx {
{reinterpret_cast(m_object.data())});
}
+ MoveToAnotherSequenceContainerCommand::MoveToAnotherSequenceContainerCommand(UndoableModelStrategy *strategy,
+ Handle sequenceContainerEntity,
+ Handle entity,
+ Handle otherSequenceContainerEntity,
+ QUndoCommand *parent)
+ : QUndoCommand(parent), m_strategy(strategy), m_container(sequenceContainerEntity), m_entity(entity),
+ m_otherContainer(otherSequenceContainerEntity) {
+ }
+
+ void MoveToAnotherSequenceContainerCommand::undo() {
+ auto containerObj = handle_cast(m_otherContainer);
+ auto otherContainerObj = handle_cast(m_container);
+ auto entityObj = handle_cast(m_entity);
+ containerObj->sequence.remove(entityObj);
+ otherContainerObj->sequence.insert(entityObj);
+ entityObj->setParent(otherContainerObj);
+ Q_EMIT m_strategy->moveToAnotherSequenceContainerNotified(m_otherContainer, m_entity, m_container);
+ }
+
+ void MoveToAnotherSequenceContainerCommand::redo() {
+ auto containerObj = handle_cast(m_container);
+ auto otherContainerObj = handle_cast(m_otherContainer);
+ auto entityObj = handle_cast(m_entity);
+ containerObj->sequence.remove(entityObj);
+ otherContainerObj->sequence.insert(entityObj);
+ entityObj->setParent(otherContainerObj);
+ Q_EMIT m_strategy->moveToAnotherSequenceContainerNotified(m_container, m_entity, m_otherContainer);
+ }
+
InsertIntoListContainerCommand::InsertIntoListContainerCommand(
UndoableModelStrategy *strategy, Handle listContainerEntity,
Handle entity, int index, QUndoCommand *parent)
@@ -362,7 +399,7 @@ namespace dspx {
int SetEntityPropertyCommand::id() const {
// Return a constant to force mergeWith to be called for comparison
- return -1;
+ return 1;
}
SpliceDataArrayCommand::SpliceDataArrayCommand(UndoableModelStrategy *strategy,
@@ -463,6 +500,22 @@ namespace dspx {
return true;
}
+ bool UndoableModelStrategy::moveToAnotherSequenceContainer(Handle sequenceContainerEntity, Handle entity,
+ Handle otherSequenceContainerEntity) {
+ auto sequenceContainerObject = handle_cast(sequenceContainerEntity);
+ auto otherSequenceContainerObject = handle_cast(otherSequenceContainerEntity);
+ auto object = handle_cast(entity);
+ if (sequenceContainerObject == otherSequenceContainerObject) {
+ return false;
+ }
+ if (!sequenceContainerObject->sequence.contains(object)) {
+ return false;
+ }
+ m_undoStack->push(new MoveToAnotherSequenceContainerCommand(this, sequenceContainerEntity, entity,
+ otherSequenceContainerEntity));
+ return true;
+ }
+
Handle UndoableModelStrategy::takeFromSequenceContainer(Handle sequenceContainerEntity,
Handle entity) {
auto sequenceContainerObject = handle_cast(sequenceContainerEntity);
diff --git a/src/libs/application/dspxmodel/src/UndoableModelStrategy.h b/src/libs/application/dspxmodel/src/UndoableModelStrategy.h
index dfc39fa7..ec48c5f8 100644
--- a/src/libs/application/dspxmodel/src/UndoableModelStrategy.h
+++ b/src/libs/application/dspxmodel/src/UndoableModelStrategy.h
@@ -20,6 +20,7 @@ namespace dspx {
bool insertIntoSequenceContainer(Handle sequenceContainerEntity, Handle entity) override;
bool insertIntoListContainer(Handle listContainerEntity, Handle entity, int index) override;
bool insertIntoMapContainer(Handle mapContainerEntity, Handle entity, const QString &key) override;
+ bool moveToAnotherSequenceContainer(Handle sequenceContainerEntity, Handle entity, Handle otherSequenceContainerEntity) override;
Handle takeFromSequenceContainer(Handle sequenceContainerEntity, Handle entity) override;
Handle takeFromListContainer(Handle listContainerEntity, int index) override;
Handle takeFromMapContainer(Handle mapContainerEntity, const QString &key) override;
diff --git a/src/libs/application/dspxmodel/src/UndoableModelStrategy_p.h b/src/libs/application/dspxmodel/src/UndoableModelStrategy_p.h
index dbe90368..dd6737f3 100644
--- a/src/libs/application/dspxmodel/src/UndoableModelStrategy_p.h
+++ b/src/libs/application/dspxmodel/src/UndoableModelStrategy_p.h
@@ -86,6 +86,24 @@ namespace dspx {
bool m_undone = false;
};
+ class MoveToAnotherSequenceContainerCommand : public QUndoCommand {
+ public:
+ MoveToAnotherSequenceContainerCommand(UndoableModelStrategy *strategy,
+ Handle sequenceContainerEntity,
+ Handle entity,
+ Handle otherSequenceContainerEntity,
+ QUndoCommand *parent = nullptr);
+ ~MoveToAnotherSequenceContainerCommand() override = default;
+ void undo() override;
+ void redo() override;
+
+ private:
+ UndoableModelStrategy *m_strategy;
+ Handle m_container;
+ Handle m_entity;
+ Handle m_otherContainer;
+ };
+
class InsertIntoListContainerCommand : public QUndoCommand {
public:
InsertIntoListContainerCommand(UndoableModelStrategy *strategy, Handle listContainerEntity,
diff --git a/src/libs/application/dspxmodel/src/Vibrato.cpp b/src/libs/application/dspxmodel/src/Vibrato.cpp
index 73c84e31..95c416a3 100644
--- a/src/libs/application/dspxmodel/src/Vibrato.cpp
+++ b/src/libs/application/dspxmodel/src/Vibrato.cpp
@@ -1,6 +1,4 @@
#include "Vibrato.h"
-
-#include
#include
#include
@@ -26,73 +24,8 @@ namespace dspx {
double phase;
VibratoPoints *points;
double start;
-
- void setEndUnchecked(double end_);
- void setEnd(double end_);
- void setFreqUnchecked(double freq_);
- void setFreq(double freq_);
- void setPhaseUnchecked(double phase_);
- void setPhase(double phase_);
- void setStartUnchecked(double start_);
- void setStart(double start_);
};
- void VibratoPrivate::setEndUnchecked(double end_) {
- Q_Q(Vibrato);
- pModel->strategy->setEntityProperty(handle, ModelStrategy::P_VibratoEnd, end_);
- }
-
- void VibratoPrivate::setEnd(double end_) {
- Q_Q(Vibrato);
- if (auto engine = qjsEngine(q); engine && (end_ < 0.0 || end_ > 1.0)) {
- engine->throwError(QJSValue::RangeError, QStringLiteral("End must be in range [0, 1]"));
- return;
- }
- setEndUnchecked(end_);
- }
-
- void VibratoPrivate::setFreqUnchecked(double freq_) {
- Q_Q(Vibrato);
- pModel->strategy->setEntityProperty(handle, ModelStrategy::P_VibratoFrequency, freq_);
- }
-
- void VibratoPrivate::setFreq(double freq_) {
- Q_Q(Vibrato);
- if (auto engine = qjsEngine(q); engine && freq_ < 0.0) {
- engine->throwError(QJSValue::RangeError, QStringLiteral("Freq must be greater than or equal to 0"));
- return;
- }
- setFreqUnchecked(freq_);
- }
-
- void VibratoPrivate::setPhaseUnchecked(double phase_) {
- Q_Q(Vibrato);
- pModel->strategy->setEntityProperty(handle, ModelStrategy::P_VibratoPhase, phase_);
- }
-
- void VibratoPrivate::setPhase(double phase_) {
- Q_Q(Vibrato);
- if (auto engine = qjsEngine(q); engine && (phase_ < 0.0 || phase_ > 1.0)) {
- engine->throwError(QJSValue::RangeError, QStringLiteral("Phase must be in range [0, 1]"));
- return;
- }
- setPhaseUnchecked(phase_);
- }
-
- void VibratoPrivate::setStartUnchecked(double start_) {
- Q_Q(Vibrato);
- pModel->strategy->setEntityProperty(handle, ModelStrategy::P_VibratoStart, start_);
- }
-
- void VibratoPrivate::setStart(double start_) {
- Q_Q(Vibrato);
- if (auto engine = qjsEngine(q); engine && (start_ < 0.0 || start_ > 1.0)) {
- engine->throwError(QJSValue::RangeError, QStringLiteral("Start must be in range [0, 1]"));
- return;
- }
- setStartUnchecked(start_);
- }
-
Vibrato::Vibrato(Handle handle, Model *model) : QObject(model), d_ptr(new VibratoPrivate) {
Q_D(Vibrato);
d->q_ptr = this;
@@ -126,8 +59,8 @@ namespace dspx {
void Vibrato::setEnd(double end) {
Q_D(Vibrato);
- Q_ASSERT(end >= 0.0 && end <= 1.0);
- d->setEndUnchecked(end);
+ Q_ASSERT(end >= 0 && end <= 1);
+ d->pModel->strategy->setEntityProperty(d->handle, ModelStrategy::P_VibratoEnd, end);
}
double Vibrato::freq() const {
@@ -137,8 +70,8 @@ namespace dspx {
void Vibrato::setFreq(double freq) {
Q_D(Vibrato);
- Q_ASSERT(freq >= 0.0);
- d->setFreqUnchecked(freq);
+ Q_ASSERT(freq >= 0);
+ d->pModel->strategy->setEntityProperty(d->handle, ModelStrategy::P_VibratoFrequency, freq);
}
int Vibrato::offset() const {
@@ -158,8 +91,8 @@ namespace dspx {
void Vibrato::setPhase(double phase) {
Q_D(Vibrato);
- Q_ASSERT(phase >= 0.0 && phase <= 1.0);
- d->setPhaseUnchecked(phase);
+ Q_ASSERT(phase >= 0 && phase <= 1);
+ d->pModel->strategy->setEntityProperty(d->handle, ModelStrategy::P_VibratoPhase, phase);
}
VibratoPoints *Vibrato::points() const {
@@ -174,8 +107,8 @@ namespace dspx {
void Vibrato::setStart(double start) {
Q_D(Vibrato);
- Q_ASSERT(start >= 0.0 && start <= 1.0);
- d->setStartUnchecked(start);
+ Q_ASSERT(start >= 0 && start <= 1);
+ d->pModel->strategy->setEntityProperty(d->handle, ModelStrategy::P_VibratoStart, start);
}
QDspx::Vibrato Vibrato::toQDspx() const {
diff --git a/src/libs/application/dspxmodel/src/Vibrato.h b/src/libs/application/dspxmodel/src/Vibrato.h
index d61009e1..695b492f 100644
--- a/src/libs/application/dspxmodel/src/Vibrato.h
+++ b/src/libs/application/dspxmodel/src/Vibrato.h
@@ -25,12 +25,12 @@ namespace dspx {
QML_UNCREATABLE("")
Q_DECLARE_PRIVATE(Vibrato)
Q_PROPERTY(int amp READ amp WRITE setAmp NOTIFY ampChanged)
- Q_PRIVATE_PROPERTY(d_func(), double end MEMBER end WRITE setEnd NOTIFY endChanged)
- Q_PRIVATE_PROPERTY(d_func(), double freq MEMBER freq WRITE setFreq NOTIFY freqChanged)
+ Q_PROPERTY(double end READ end WRITE setEnd NOTIFY endChanged)
+ Q_PROPERTY(double freq READ freq WRITE setFreq NOTIFY freqChanged)
Q_PROPERTY(int offset READ offset WRITE setOffset NOTIFY offsetChanged)
- Q_PRIVATE_PROPERTY(d_func(), double phase MEMBER phase WRITE setPhase NOTIFY phaseChanged)
+ Q_PROPERTY(double phase READ phase WRITE setPhase NOTIFY phaseChanged)
Q_PROPERTY(VibratoPoints *points READ points CONSTANT)
- Q_PRIVATE_PROPERTY(d_func(), double start MEMBER start WRITE setStart NOTIFY startChanged)
+ Q_PROPERTY(double start READ start WRITE setStart NOTIFY startChanged)
public:
~Vibrato() override;
diff --git a/src/libs/application/dspxmodel/src/rangehelpers.h b/src/libs/application/dspxmodel/src/rangehelpers.h
index 458160b5..7843ca3a 100644
--- a/src/libs/application/dspxmodel/src/rangehelpers.h
+++ b/src/libs/application/dspxmodel/src/rangehelpers.h
@@ -1,6 +1,7 @@
#ifndef DIFFSCOPE_DSPX_MODEL_RANGEHELPERS_H
#define DIFFSCOPE_DSPX_MODEL_RANGEHELPERS_H
+#include
#include
namespace dspx::impl {
@@ -21,6 +22,9 @@ namespace dspx::impl {
using pointer = ItemType **;
using reference = ItemType *&;
+ iterator() : m_sequence(nullptr), m_item(nullptr) {
+ }
+
iterator(const SequenceType *sequence, ItemType *item) : m_sequence(sequence), m_item(item) {
}
@@ -111,4 +115,7 @@ namespace dspx::impl {
}
+template
+constexpr bool std::ranges::enable_borrowed_range> = true;
+
#endif //DIFFSCOPE_DSPX_MODEL_RANGEHELPERS_H
diff --git a/src/libs/application/dspxmodel/src/selectionmodel/AnchorNodeSelectionModel.h b/src/libs/application/dspxmodel/src/selectionmodel/AnchorNodeSelectionModel.h
index 0ad011b5..f0d31081 100644
--- a/src/libs/application/dspxmodel/src/selectionmodel/AnchorNodeSelectionModel.h
+++ b/src/libs/application/dspxmodel/src/selectionmodel/AnchorNodeSelectionModel.h
@@ -18,6 +18,7 @@ namespace dspx {
class DSPX_MODEL_EXPORT AnchorNodeSelectionModel : public QObject {
Q_OBJECT
QML_ELEMENT
+ QML_UNCREATABLE("")
Q_DECLARE_PRIVATE(AnchorNodeSelectionModel)
Q_PROPERTY(AnchorNode *currentItem READ currentItem NOTIFY currentItemChanged)
diff --git a/src/libs/application/dspxmodel/src/selectionmodel/ClipSelectionModel.cpp b/src/libs/application/dspxmodel/src/selectionmodel/ClipSelectionModel.cpp
index c0bfa978..dad78e12 100644
--- a/src/libs/application/dspxmodel/src/selectionmodel/ClipSelectionModel.cpp
+++ b/src/libs/application/dspxmodel/src/selectionmodel/ClipSelectionModel.cpp
@@ -1,80 +1,211 @@
#include "ClipSelectionModel.h"
#include "ClipSelectionModel_p.h"
-#include "Model.h"
+#include
+#include
+
+#include
#include
#include
#include
+#include
namespace dspx {
- bool ClipSelectionModelPrivate::isAddedToModel(Clip *item) const {
- return item->clipSequence() && item->clipSequence()->track()->trackList() == selectionModel->model()->tracks();
+ bool ClipSelectionModelPrivate::isValidItem(Clip *item) const {
+ if (!item) {
+ return false;
+ }
+ auto clipSeq = item->clipSequence();
+ if (!clipSeq) {
+ return false;
+ }
+ auto track = clipSeq->track();
+ if (!track) {
+ return false;
+ }
+ auto trackList = track->trackList();
+ if (!trackList) {
+ return false;
+ }
+ return trackList == selectionModel->model()->tracks();
}
- void ClipSelectionModelPrivate::updateAssociation(Clip *item) {
- Q_Q(ClipSelectionModel);
- auto previousClipSequence = clipClipSequence.value(item);
- auto currentClipSequence = item->clipSequence();
- if (previousClipSequence == currentClipSequence) {
+ void ClipSelectionModelPrivate::connectItem(Clip *item) {
+ if (connectedItems.contains(item)) {
return;
}
- bool clipSequencesWithSelectedItemsUpdatedFlag = false;
-
- if (previousClipSequence) {
- clipSequencesWithSelectedItems[previousClipSequence].remove(item);
- if (clipSequencesWithSelectedItems[previousClipSequence].isEmpty()) {
- clipSequencesWithSelectedItemsUpdatedFlag = true;
- QObject::disconnect(previousClipSequence, nullptr, q, nullptr);
- clipSequencesWithSelectedItems.remove(previousClipSequence);
+ QObject::connect(item, &QObject::destroyed, q_ptr, [this](QObject *obj) {
+ dropItem(static_cast(obj));
+ });
+ QObject::connect(item, &Clip::clipSequenceChanged, q_ptr, [this, item]() {
+ auto oldClipSequence = clipToClipSequence.value(item, nullptr);
+ auto newClipSequence = item->clipSequence();
+
+ if (oldClipSequence != newClipSequence) {
+ // Update clipToClipSequence
+ if (newClipSequence) {
+ clipToClipSequence[item] = newClipSequence;
+ } else {
+ clipToClipSequence.remove(item);
+ }
+
+ // Update clipSequencesWithSelectedItems
+ if (selectedItems.contains(item)) {
+ if (oldClipSequence) {
+ auto &items = clipSequencesWithSelectedItems[oldClipSequence];
+ items.remove(item);
+ if (items.isEmpty()) {
+ clipSequencesWithSelectedItems.remove(oldClipSequence);
+ }
+ }
+ if (newClipSequence) {
+ clipSequencesWithSelectedItems[newClipSequence].insert(item);
+ }
+ Q_EMIT q_ptr->clipSequencesWithSelectedItemsChanged();
+ }
+ }
+
+ if (!isValidItem(item)) {
+ dropItem(item);
+ }
+ });
+
+ // Also connect to track's trackListChanged signal
+ auto clipSeq = item->clipSequence();
+ if (clipSeq) {
+ auto track = clipSeq->track();
+ if (track) {
+ QPointer item_ = item;
+ QObject::connect(track, &Track::trackListChanged, q_ptr, [this, item_]() {
+ if (!isValidItem(item_)) {
+ dropItem(item_);
+ }
+ });
}
}
+
+ connectedItems.insert(item);
+ }
- Q_ASSERT(currentClipSequence);
- clipClipSequence.insert(item, currentClipSequence);
- if (!clipSequencesWithSelectedItems.contains(currentClipSequence)) {
- clipSequencesWithSelectedItemsUpdatedFlag = true;
- QObject::connect(currentClipSequence->track(), &Track::trackListChanged, q, [currentClipSequence, this] {
- if (currentClipSequence->track()->trackList() == selectionModel->model()->tracks())
- return;
- for (auto item : clipSequencesWithSelectedItems[currentClipSequence]) {
- updateOnItemRemoved(item);
- }
- });
+ void ClipSelectionModelPrivate::disconnectItem(Clip *item) {
+ QObject::disconnect(item, nullptr, q_ptr, nullptr);
+
+ // Disconnect from track if it exists
+ auto clipSeq = clipToClipSequence.value(item, nullptr);
+ if (clipSeq) {
+ auto track = clipSeq->track();
+ if (track) {
+ QObject::disconnect(track, nullptr, q_ptr, nullptr);
+ }
}
- clipSequencesWithSelectedItems[currentClipSequence].insert(item);
+
+ connectedItems.remove(item);
+ }
- if (clipSequencesWithSelectedItemsUpdatedFlag) {
- Q_EMIT q->clipSequencesWithSelectedItemsChanged();
+ bool ClipSelectionModelPrivate::addToSelection(Clip *item) {
+ if (!isValidItem(item) || selectedItems.contains(item)) {
+ return false;
}
+ connectItem(item);
+ selectedItems.insert(item);
+
+ // Update clipToClipSequence
+ auto clipSeq = item->clipSequence();
+ if (clipSeq) {
+ clipToClipSequence[item] = clipSeq;
+ clipSequencesWithSelectedItems[clipSeq].insert(item);
+ }
+
+ Q_EMIT q_ptr->itemSelected(item, true);
+ return true;
}
- void ClipSelectionModelPrivate::removeAssociation(Clip *item) {
- Q_Q(ClipSelectionModel);
- auto clipSequence = clipClipSequence.value(item);
- bool clipSequencesWithSelectedItemsUpdatedFlag = false;
+ bool ClipSelectionModelPrivate::removeFromSelection(Clip *item) {
+ if (!item) {
+ return false;
+ }
+ if (!selectedItems.remove(item)) {
+ return false;
+ }
+
+ // Update clipSequencesWithSelectedItems
+ auto clipSeq = clipToClipSequence.value(item);
+ if (clipSeq) {
+ auto &items = clipSequencesWithSelectedItems[clipSeq];
+ items.remove(item);
+ if (items.isEmpty()) {
+ clipSequencesWithSelectedItems.remove(clipSeq);
+ }
+ }
+ clipToClipSequence.remove(item);
+
+ if (item != currentItem) {
+ disconnectItem(item);
+ }
+ Q_EMIT q_ptr->itemSelected(item, false);
+ return true;
+ }
- Q_ASSERT(clipSequence);
- clipSequencesWithSelectedItems[clipSequence].remove(item);
- if (clipSequencesWithSelectedItems[clipSequence].isEmpty()) {
- clipSequencesWithSelectedItemsUpdatedFlag = true;
- QObject::disconnect(clipSequence, nullptr, q, nullptr);
- clipSequencesWithSelectedItems.remove(clipSequence);
+ bool ClipSelectionModelPrivate::clearSelection() {
+ if (selectedItems.isEmpty()) {
+ return false;
+ }
+ const auto items = selectedItems.values();
+ bool selectionChanged = false;
+ for (auto clip : items) {
+ selectionChanged |= removeFromSelection(clip);
}
- clipClipSequence.remove(item);
+ return selectionChanged;
+ }
- if (clipSequencesWithSelectedItemsUpdatedFlag) {
- Q_EMIT q->clipSequencesWithSelectedItemsChanged();
+ void ClipSelectionModelPrivate::dropItem(Clip *item) {
+ if (!item) {
+ return;
+ }
+ const int oldCount = selectedItems.size();
+ bool selectionChanged = removeFromSelection(item);
+ bool countChanged = selectionChanged && oldCount != selectedItems.size();
+ bool currentChanged = false;
+ bool clipSequencesChanged = selectionChanged;
+
+ if (currentItem == item) {
+ if (!selectedItems.contains(item)) {
+ disconnectItem(item);
+ }
+ currentItem = nullptr;
+ currentChanged = true;
+ }
+ if (selectionChanged) {
+ Q_EMIT q_ptr->selectedItemsChanged();
+ if (countChanged) {
+ Q_EMIT q_ptr->selectedCountChanged();
+ }
+ }
+ if (clipSequencesChanged) {
+ Q_EMIT q_ptr->clipSequencesWithSelectedItemsChanged();
+ }
+ if (currentChanged) {
+ Q_EMIT q_ptr->currentItemChanged();
}
}
- void ClipSelectionModelPrivate::clearAssociation() {
- Q_Q(ClipSelectionModel);
- if (clipClipSequence.isEmpty())
+ void ClipSelectionModelPrivate::setCurrentItem(Clip *item) {
+ if (!isValidItem(item)) {
+ item = nullptr;
+ }
+ if (currentItem == item) {
return;
- clipClipSequence.clear();
- clipSequencesWithSelectedItems.clear();
- Q_EMIT q->clipSequencesWithSelectedItemsChanged();
+ }
+ auto oldItem = currentItem;
+ currentItem = item;
+ if (oldItem && !selectedItems.contains(oldItem)) {
+ disconnectItem(oldItem);
+ }
+ if (currentItem && !selectedItems.contains(currentItem)) {
+ connectItem(currentItem);
+ }
+ Q_EMIT q_ptr->currentItemChanged();
}
ClipSelectionModel::ClipSelectionModel(SelectionModel *parent) : QObject(parent), d_ptr(new ClipSelectionModelPrivate) {
@@ -110,6 +241,41 @@ namespace dspx {
return d->selectedItems.contains(item);
}
+ void ClipSelectionModelPrivate::select(Clip *item, SelectionModel::SelectionCommand command) {
+ const int oldCount = selectedItems.size();
+ bool selectionChanged = false;
+
+ if (command & SelectionModel::ClearPreviousSelection) {
+ selectionChanged |= clearSelection();
+ }
+ if ((command & SelectionModel::Select) && (command & SelectionModel::Deselect)) {
+ if (selectedItems.contains(item)) {
+ selectionChanged |= removeFromSelection(item);
+ } else {
+ selectionChanged |= addToSelection(item);
+ }
+ } else if (command & SelectionModel::Select) {
+ selectionChanged |= addToSelection(item);
+ } else if (command & SelectionModel::Deselect) {
+ selectionChanged |= removeFromSelection(item);
+ }
+ if (command & SelectionModel::SetCurrentItem) {
+ setCurrentItem(item);
+ }
+
+ bool clipSequencesChanged = selectionChanged; // TODO
+
+ if (selectionChanged) {
+ Q_EMIT q_ptr->selectedItemsChanged();
+ if (oldCount != selectedItems.size()) {
+ Q_EMIT q_ptr->selectedCountChanged();
+ }
+ }
+ if (clipSequencesChanged) {
+ Q_EMIT q_ptr->clipSequencesWithSelectedItemsChanged();
+ }
+ }
+
}
#include "moc_ClipSelectionModel.cpp"
diff --git a/src/libs/application/dspxmodel/src/selectionmodel/ClipSelectionModel.h b/src/libs/application/dspxmodel/src/selectionmodel/ClipSelectionModel.h
index bc4b8218..cf89890e 100644
--- a/src/libs/application/dspxmodel/src/selectionmodel/ClipSelectionModel.h
+++ b/src/libs/application/dspxmodel/src/selectionmodel/ClipSelectionModel.h
@@ -1,11 +1,9 @@
#ifndef DIFFSCOPE_DSPX_MODEL_CLIPSELECTIONMODEL_H
#define DIFFSCOPE_DSPX_MODEL_CLIPSELECTIONMODEL_H
-#include
-#include
#include
-#include
+#include
namespace dspx {
@@ -17,6 +15,7 @@ namespace dspx {
class DSPX_MODEL_EXPORT ClipSelectionModel : public QObject {
Q_OBJECT
QML_ELEMENT
+ QML_UNCREATABLE("")
Q_DECLARE_PRIVATE(ClipSelectionModel)
Q_PROPERTY(Clip *currentItem READ currentItem NOTIFY currentItemChanged)
diff --git a/src/libs/application/dspxmodel/src/selectionmodel/ClipSelectionModel_p.h b/src/libs/application/dspxmodel/src/selectionmodel/ClipSelectionModel_p.h
index a5e27278..a499ad37 100644
--- a/src/libs/application/dspxmodel/src/selectionmodel/ClipSelectionModel_p.h
+++ b/src/libs/application/dspxmodel/src/selectionmodel/ClipSelectionModel_p.h
@@ -1,30 +1,38 @@
#ifndef DIFFSCOPE_DSPX_MODEL_CLIPSELECTIONMODEL_P_H
#define DIFFSCOPE_DSPX_MODEL_CLIPSELECTIONMODEL_P_H
+#include
+
#include
-#include
#include
-#include
-
namespace dspx {
- class ClipSelectionModelPrivate : public GenericGlobalItemSelectionModelData<
- ClipSelectionModel,
- ClipSelectionModelPrivate,
- Clip,
- &Clip::clipSequenceChanged
- > {
+ class SelectionModel;
+ class ClipSequence;
+
+ class ClipSelectionModelPrivate {
Q_DECLARE_PUBLIC(ClipSelectionModel)
public:
- QHash clipClipSequence;
+ ClipSelectionModel *q_ptr;
+ SelectionModel *selectionModel;
+ QSet selectedItems;
+ Clip *currentItem = nullptr;
+ QSet connectedItems;
+
+ QHash clipToClipSequence;
QHash> clipSequencesWithSelectedItems;
- bool isAddedToModel(Clip *item) const;
- void updateAssociation(Clip *item);
- void removeAssociation(Clip *item);
- void clearAssociation();
+ bool isValidItem(Clip *item) const;
+ void connectItem(Clip *item);
+ void disconnectItem(Clip *item);
+ bool addToSelection(Clip *item);
+ bool removeFromSelection(Clip *item);
+ bool clearSelection();
+ void dropItem(Clip *item);
+ void setCurrentItem(Clip *item);
+ void select(Clip *item, SelectionModel::SelectionCommand command);
};
}
diff --git a/src/libs/application/dspxmodel/src/selectionmodel/GenericGlobalItemSelectionModelData_p.h b/src/libs/application/dspxmodel/src/selectionmodel/GenericGlobalItemSelectionModelData_p.h
index ab14e939..57acb1e6 100644
--- a/src/libs/application/dspxmodel/src/selectionmodel/GenericGlobalItemSelectionModelData_p.h
+++ b/src/libs/application/dspxmodel/src/selectionmodel/GenericGlobalItemSelectionModelData_p.h
@@ -14,7 +14,7 @@ namespace dspx {
void (Item::*superItemChangedSignal)(),
class SuperItem = void
>
- class GenericGlobalItemSelectionModelData {
+ [[deprecated]] class GenericGlobalItemSelectionModelData {
public:
PublicClass *q_ptr;
SelectionModel *selectionModel;
diff --git a/src/libs/application/dspxmodel/src/selectionmodel/LabelSelectionModel.cpp b/src/libs/application/dspxmodel/src/selectionmodel/LabelSelectionModel.cpp
index eb832235..3f395fc1 100644
--- a/src/libs/application/dspxmodel/src/selectionmodel/LabelSelectionModel.cpp
+++ b/src/libs/application/dspxmodel/src/selectionmodel/LabelSelectionModel.cpp
@@ -1,15 +1,118 @@
#include "LabelSelectionModel.h"
#include "LabelSelectionModel_p.h"
+#include
+
#include
#include
#include
#include
+#include
namespace dspx {
- bool LabelSelectionModelPrivate::isAddedToModel(Label *item) const {
- return item->labelSequence() == selectionModel->model()->timeline()->labels();
+ bool LabelSelectionModelPrivate::isValidItem(Label *item) const {
+ return item && item->labelSequence() == selectionModel->model()->timeline()->labels();
+ }
+
+ void LabelSelectionModelPrivate::connectItem(Label *item) {
+ if (connectedItems.contains(item)) {
+ return;
+ }
+ QObject::connect(item, &QObject::destroyed, q_ptr, [this](QObject *obj) {
+ dropItem(static_cast