diff --git a/.clang-format b/.clang-format index f3affdec..2857fcec 100644 --- a/.clang-format +++ b/.clang-format @@ -1,130 +1,252 @@ -# References: -# https://clang.llvm.org/docs/ClangFormatStyleOptions.html -# https://code.qt.io/cgit/qt/qt5.git/tree/_clang-format - +Language: Cpp BasedOnStyle: LLVM - -Standard: c++20 - -# 指针和引用的对齐方式。 -# 可能的值有: -# PAS_Left (在配置中: Left) 指针左对齐。 -# PAS_Right (在配置中: Right) 指针右对齐。 -# PAS_Middle (在配置中: Middle) 指针中间对齐。 -PointerAlignment: Right - -# public/protected/private 等访问修饰符偏移量 AccessModifierOffset: -4 - -# 缩进长度 -IndentWidth: 4 - -# 连续空行的最大数 -MaxEmptyLinesToKeep: 999 - -# 在OC中的@property后面添加一个空格。例如:使用“@property (readonly)”而不是“@property(readonly)” -ObjCSpaceAfterProperty: true - -# OC块中所拍的字符数 -ObjCBlockIndentWidth: 4 - -# 取决于值, 语句“int f() { return 0; }”可以被放到一个单行。 -# 可能的值有: -# SFS_None (在配置中: None) 从不合并方法或函数到单独的一行。 -# SFS_Empty (在配置中: Empty) 仅合并空的函数。 -# SFS_Inline (在配置中: Inline) 仅合并类中定义的方法或函数. 意味着 “empty”. -# SFS_All (在配置中: All) 合并所有的方法适应单行. -AllowShortFunctionsOnASingleLine: None - -# 如果为真(true), 语句“if (a) return;” 能被放到单行。 -AllowShortIfStatementsOnASingleLine: false - -# 如果为真(true), 对齐注释。 -AlignTrailingComments: true - -# 如果为真,对齐连续的宏定义 -AlignConsecutiveMacros: true - -# 如果为真(true),将会在“[”之后和“]”之前插入空格。 -SpacesInSquareBrackets: false - -# 如果为真(true), 将会在“(”之后和“)”之前插入空格。 -SpacesInParentheses : false - -# 如果为真(true), 校准连续的声明。 -# 这将会校准连续多行的声明的名字。这将会导致像下面这样的格式: -# int aaaa = 12; -# float b = 23; -# std::string ccc = 23; -AlignConsecutiveDeclarations: false - -# 如果为真(true),连续调整多行 -# 这将会调整连续行中的分配操作符。这将会导致像下面这样的格式: -# int aaaa = 12; -# int b = 23; -# int ccc = 23; -AlignConsecutiveAssignments: false - -# 如果为假(false),移除分配操作符(=)前空格。 -SpaceBeforeAssignmentOperators: true - -# 如果为真(true), 将会在字面量容器中插入空格(例如 OC和Javascript的数组和字典字面量)。 -SpacesInContainerLiterals: false - -# 缩进case标签 +AlignAfterOpenBracket: BlockIndent +AlignArrayOfStructures: None +AlignConsecutiveAssignments: + Enabled: false +AlignConsecutiveDeclarations: + Enabled: false +AlignConsecutiveMacros: + Enabled: false +AlignConsecutiveShortCaseStatements: + Enabled: false +AlignEscapedNewlines: Left +AlignOperands: Align +AlignTrailingComments: + Kind: Never +AllowAllArgumentsOnNextLine: true +AllowAllParametersOfDeclarationOnNextLine: true +AllowBreakBeforeNoexceptSpecifier: Never +AllowShortBlocksOnASingleLine: Empty +AllowShortCaseLabelsOnASingleLine: true +AllowShortEnumsOnASingleLine: false +AllowShortFunctionsOnASingleLine: Inline +AllowShortIfStatementsOnASingleLine: Never +AllowShortLambdasOnASingleLine: All +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: false +AlwaysBreakTemplateDeclarations: MultiLine +AttributeMacros: + - __capability +BinPackArguments: true +BinPackParameters: true +BitFieldColonSpacing: Both +BraceWrapping: + AfterCaseLabel: false + AfterClass: false + AfterControlStatement: Never + AfterEnum: false + AfterExternBlock: false + AfterFunction: false + AfterNamespace: false + AfterObjCDeclaration: false + AfterStruct: false + AfterUnion: false + BeforeCatch: false + BeforeElse: false + BeforeLambdaBody: false + BeforeWhile: false + IndentBraces: false + SplitEmptyFunction: true + SplitEmptyRecord: true + SplitEmptyNamespace: true +BreakAfterAttributes: Leave +BreakAfterJavaFieldAnnotations: false +BreakArrays: true +BreakBeforeBinaryOperators: None +BreakBeforeConceptDeclarations: Always +BreakBeforeBraces: Attach +BreakBeforeInlineASMColon: OnlyMultiline +BreakBeforeTernaryOperators: true +BreakConstructorInitializers: BeforeColon +BreakInheritanceList: BeforeColon +BreakStringLiterals: true +ColumnLimit: 0 +CommentPragmas: '^ IWYU pragma:' +CompactNamespaces: false +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: true +DerivePointerAlignment: false +DisableFormat: false +EmptyLineAfterAccessModifier: Never +EmptyLineBeforeAccessModifier: LogicalBlock +ExperimentalAutoDetectBinPacking: false +FixNamespaceComments: false +ForEachMacros: [foreach, Q_FOREACH, BOOST_FOREACH, forever, Q_FOREVER, QBENCHMARK, QBENCHMARK_ONCE] +IfMacros: + - KJ_IF_MAYBE +IncludeBlocks: Regroup +IncludeCategories: + # Local headers + - Regex: '^".*\.h"$' + Priority: -999 + # Qt public + - Regex: '^<(Q[a-zA-Z0-9_]+|qt_[a-zA-Z0-9_]+\.h|qqml_[a-zA-Z0-9_]+\.h)>$' + Priority: 201 + # Qt qpa + - Regex: '^$' + Priority: 202 + # Qt private + - Regex: '^$' + Priority: 203 + # Windows platform-specific + - Regex: '^<[a-zA-Z0-9_]+\.h>$' + Priority: 1 + # C++ standard library + - Regex: '^<[a-z0-9_]+>$' + Priority: 2 + # Boost + - Regex: '^ 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 98144a6e..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/Audio_en_US.ts - translation: /src/plugins/audio/res/translations/Audio_%locale_with_underscore%.ts - - source: /src/plugins/welcomewizard/res/translations/WelcomeWizard_en_US.ts - translation: /src/plugins/welcomewizard/res/translations/WelcomeWizard_%locale_with_underscore%.ts - - source: /src/plugins/achievement/res/translations/Achievement_en_US.ts - translation: /src/plugins/achievement/res/translations/Achievement_%locale_with_underscore%.ts - - source: /src/plugins/maintenance/res/translations/Maintenance_en_US.ts - translation: /src/plugins/maintenance/res/translations/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 new file mode 100644 index 00000000..c4dd5544 --- /dev/null +++ b/scripts/ci/Create-DMG.ps1 @@ -0,0 +1,122 @@ +param ( + [Parameter(Mandatory)] + [string]$AppPath, + + [Parameter(Mandatory)] + [string]$Semver, + + [Parameter(Mandatory)] + [string]$ApplicationDisplayName, + + [Parameter(Mandatory)] + [string]$InstallerFileBase +) + +$BackgroundSrcDir = "src/app/share/dmg" + +$Bg1x = Join-Path $BackgroundSrcDir "dmg_background.png" +$Bg2x = Join-Path $BackgroundSrcDir "dmg_background@2x.png" + +if (!(Test-Path $Bg1x) -or !(Test-Path $Bg2x)) { + throw "dmg_background.png and dmg_background@2x.png do not exist in $BackgroundSrcDir" +} + +if (!(Test-Path $AppPath)) { + throw "App bundle not exist: $AppPath" +} + +# Temporary directory +$TempDir = Join-Path ([System.IO.Path]::GetTempPath()) ("dmg-build-" + [System.Guid]::NewGuid()) +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" + +try { + # ----------------------------- + # Step 1: Preprocess background + # ----------------------------- + + # 1x image + & magick ` + "$Bg1x" ` + -gravity south ` + -pointsize 12 ` + -fill "rgba(37,37,37,0.25)" ` + -annotate +0+8 "$VersionText" ` + "$Bg1xOut" | Write-Host + + if ($LASTEXITCODE -ne 0) { + throw "ImageMagick failed to process dmg_background.png" + } + + # 2x image (scaled) + & magick ` + "$Bg2x" ` + -gravity south ` + -pointsize 24 ` + -fill "rgba(37,37,37,0.25)" ` + -annotate +0+16 "$VersionText" ` + "$Bg2xOut" | Write-Host + + if ($LASTEXITCODE -ne 0) { + throw "ImageMagick failed to process dmg_background@2x.png" + } + + # Combine into TIFF + & tiffutil ` + -cathidpicheck ` + "$Bg1xOut" ` + "$Bg2xOut" ` + -out "$BgTiff" | Write-Host + + if ($LASTEXITCODE -ne 0) { + throw "tiffutil failed to create TIFF" + } + + # ----------------------------- + # Step 2: Build DMG + # ----------------------------- + $DmgName = "$InstallerFileBase.dmg" + $DmgPath = Join-Path (Get-Location) $DmgName + + if (Test-Path $DmgPath) { + 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 $AppBundlePath -Leaf)" 132 280 ` + --app-drop-link 468 280 ` + "$DmgPath" ` + "$AppBundlePath" | Write-Host + + if ($LASTEXITCODE -ne 0) { + throw "create-dmg failed" + } + + Write-Output $DmgPath +} +finally { + # Cleanup temp files + if (Test-Path $TempDir) { + Remove-Item $TempDir -Recurse -Force + } +} diff --git a/scripts/vcpkg b/scripts/vcpkg index 3561516f..cf97d45c 160000 --- a/scripts/vcpkg +++ b/scripts/vcpkg @@ -1 +1 @@ -Subproject commit 3561516f372ae595301c5bf373ca6930766e540d +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 cf55ff35..ee7fe48a 100644 --- a/src/app/main.cpp +++ b/src/app/main.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -17,13 +18,20 @@ #include #include +#include +#include + #include +#include + #include #include #include +#include + #ifdef APPLICATION_ENABLE_BREAKPAD # include #endif @@ -37,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() { @@ -52,6 +62,12 @@ class MyLoaderSpec : public Loader::LoaderSpec { splashConfigPath = ApplicationInfo::applicationLocation(ApplicationInfo::BuiltinResources) + 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 { @@ -84,6 +100,7 @@ class MyLoaderSpec : public Loader::LoaderSpec { void beforeLoadPlugins() override { RuntimeInterface::setQmlEngine(engine); + SVS::FluentSystemIconsImageProvider::addToEngine(engine); auto settings = RuntimeInterface::settings(); QLocale locale; @@ -98,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({ @@ -178,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/app/share/dmg/dmg_background.png b/src/app/share/dmg/dmg_background.png new file mode 100644 index 00000000..56a7dd80 Binary files /dev/null and b/src/app/share/dmg/dmg_background.png differ diff --git a/src/app/share/dmg/dmg_background@2x.png b/src/app/share/dmg/dmg_background@2x.png new file mode 100644 index 00000000..0f3a01fc Binary files /dev/null and b/src/app/share/dmg/dmg_background@2x.png differ diff --git a/src/libs/3rdparty/choruskit b/src/libs/3rdparty/choruskit index 00cbb74c..e40b3316 160000 --- a/src/libs/3rdparty/choruskit +++ b/src/libs/3rdparty/choruskit @@ -1 +1 @@ -Subproject commit 00cbb74c1c39b54bbd7178fbd4bc9e5f6cbdb1d9 +Subproject commit e40b331652f9a81eea9be505305dbb198abd8dae diff --git a/src/libs/3rdparty/opendspx b/src/libs/3rdparty/opendspx index f0f19e14..333b2b8f 160000 --- a/src/libs/3rdparty/opendspx +++ b/src/libs/3rdparty/opendspx @@ -1 +1 @@ -Subproject commit f0f19e1440f45c6c878074ffb93eb18277ba2866 +Subproject commit 333b2b8f422688c707dfa1f2d48f71cadede3ad4 diff --git a/src/libs/3rdparty/qactionkit b/src/libs/3rdparty/qactionkit index 489fdeaa..573def48 160000 --- a/src/libs/3rdparty/qactionkit +++ b/src/libs/3rdparty/qactionkit @@ -1 +1 @@ -Subproject commit 489fdeaa0ab1f8816c873ad383030410681188d8 +Subproject commit 573def486e3f8c0f62768bcdcbc490b24cbd59af diff --git a/src/libs/3rdparty/scopicflow b/src/libs/3rdparty/scopicflow index 5b545596..23e75d14 160000 --- a/src/libs/3rdparty/scopicflow +++ b/src/libs/3rdparty/scopicflow @@ -1 +1 @@ -Subproject commit 5b5455963b11d3549127a33dfe39c05393d8cd7f +Subproject commit 23e75d14a6ad6006de3efcfbbc2dfc2119c20cad diff --git a/src/libs/3rdparty/svscraft b/src/libs/3rdparty/svscraft index db265541..161777b6 160000 --- a/src/libs/3rdparty/svscraft +++ b/src/libs/3rdparty/svscraft @@ -1 +1 @@ -Subproject commit db26554132eebdc78f7f2ecfa684e8920f1e2bfb +Subproject commit 161777b66cee8b6cb3213f3e77c564125c3301bf diff --git a/src/libs/application/CMakeLists.txt b/src/libs/application/CMakeLists.txt index 23782f35..21ecdf56 100644 --- a/src/libs/application/CMakeLists.txt +++ b/src/libs/application/CMakeLists.txt @@ -2,4 +2,6 @@ add_subdirectory(loadapi) add_subdirectory(uishell) -add_subdirectory(dspxmodel) \ No newline at end of file +add_subdirectory(dspxmodel) + +add_subdirectory(transactional) \ No newline at end of file diff --git a/src/libs/application/dspxmodel/src/AnchorNode.cpp b/src/libs/application/dspxmodel/src/AnchorNode.cpp index 6ed6bbe3..964c52dd 100644 --- a/src/libs/application/dspxmodel/src/AnchorNode.cpp +++ b/src/libs/application/dspxmodel/src/AnchorNode.cpp @@ -1,58 +1,25 @@ #include "AnchorNode.h" +#include "AnchorNode_p.h" #include -#include #include +#include #include #include namespace dspx { - class AnchorNodePrivate { - Q_DECLARE_PUBLIC(AnchorNode) - public: - AnchorNode *q_ptr; - AnchorNode::InterpolationMode interp; - int x; - int y; - - void setInterpUnchecked(AnchorNode::InterpolationMode interp_); - void setInterp(AnchorNode::InterpolationMode interp_); - void setXUnchecked(int x_); - void setX(int x_); - }; - - 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; - } - 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::setAnchorNodeSequence(AnchorNode *item, AnchorNodeSequence *anchorNodeSequence) { + auto d = item->d_func(); + if (d->anchorNodeSequence != anchorNodeSequence) { + d->anchorNodeSequence = anchorNodeSequence; + Q_EMIT item->anchorNodeSequenceChanged(); } - setXUnchecked(x_); } - AnchorNode::AnchorNode(Handle handle, Model *model) + AnchorNode::AnchorNode(Handle handle, Model *model) : EntityObject(handle, model), d_ptr(new AnchorNodePrivate) { Q_D(AnchorNode); Q_ASSERT(model->strategy()->getEntityType(handle) == ModelStrategy::EI_ParamCurveAnchorNode); @@ -72,7 +39,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 { @@ -83,7 +50,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 { @@ -98,7 +65,7 @@ namespace dspx { QDspx::AnchorNode AnchorNode::toQDspx() const { Q_D(const AnchorNode); - return QDspx::AnchorNode { + return { .interp = static_cast(d->interp), .x = d->x, .y = d->y, @@ -112,6 +79,11 @@ namespace dspx { setY(node.y); } + AnchorNodeSequence *AnchorNode::anchorNodeSequence() const { + Q_D(const AnchorNode); + return d->anchorNodeSequence; + } + void AnchorNode::handleSetEntityProperty(int property, const QVariant &value) { Q_D(AnchorNode); switch (property) { @@ -137,4 +109,4 @@ namespace dspx { } -#include "moc_AnchorNode.cpp" \ No newline at end of file +#include "moc_AnchorNode.cpp" diff --git a/src/libs/application/dspxmodel/src/AnchorNode.h b/src/libs/application/dspxmodel/src/AnchorNode.h index 5f67a580..8a8c7b3a 100644 --- a/src/libs/application/dspxmodel/src/AnchorNode.h +++ b/src/libs/application/dspxmodel/src/AnchorNode.h @@ -1,15 +1,20 @@ #ifndef DIFFSCOPE_DSPX_MODEL_ANCHORNODE_H #define DIFFSCOPE_DSPX_MODEL_ANCHORNODE_H -#include #include +#include + namespace QDspx { struct AnchorNode; } namespace dspx { + class AnchorNodeSequence; + + class AnchorNodeSequencePrivate; + class AnchorNodePrivate; class DSPX_MODEL_EXPORT AnchorNode : public EntityObject { @@ -17,9 +22,10 @@ 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) public: enum InterpolationMode { @@ -43,10 +49,13 @@ namespace dspx { QDspx::AnchorNode toQDspx() const; void fromQDspx(const QDspx::AnchorNode &node); + AnchorNodeSequence *anchorNodeSequence() const; + Q_SIGNALS: void interpChanged(InterpolationMode interp); void xChanged(int x); void yChanged(int y); + void anchorNodeSequenceChanged(); protected: void handleSetEntityProperty(int property, const QVariant &value) override; @@ -59,4 +68,4 @@ namespace dspx { } -#endif //DIFFSCOPE_DSPX_MODEL_ANCHORNODE_H \ No newline at end of file +#endif //DIFFSCOPE_DSPX_MODEL_ANCHORNODE_H diff --git a/src/libs/application/dspxmodel/src/AnchorNodeSequence.cpp b/src/libs/application/dspxmodel/src/AnchorNodeSequence.cpp index d7132359..15f8f19d 100644 --- a/src/libs/application/dspxmodel/src/AnchorNodeSequence.cpp +++ b/src/libs/application/dspxmodel/src/AnchorNodeSequence.cpp @@ -1,27 +1,27 @@ #include "AnchorNodeSequence.h" +#include "AnchorNodeSequence_p.h" -#include #include +#include #include -#include -#include #include #include +#include +#include #include namespace dspx { - class AnchorNodeSequencePrivate : public PointSequenceData { - Q_DECLARE_PUBLIC(AnchorNodeSequence) - }; - - AnchorNodeSequence::AnchorNodeSequence(Handle handle, Model *model) : EntityObject(handle, model), d_ptr(new AnchorNodeSequencePrivate) { + AnchorNodeSequence::AnchorNodeSequence(ParamCurveAnchor *paramCurveAnchor, Handle handle, Model *model) : EntityObject(handle, model), d_ptr(new AnchorNodeSequencePrivate) { Q_D(AnchorNodeSequence); Q_ASSERT(model->strategy()->getEntityType(handle) == ModelStrategy::ES_ParamCurveAnchorNodes); d->q_ptr = this; d->pModel = ModelPrivate::get(model); + d->paramCurveAnchor = paramCurveAnchor; + + d->init(model->strategy()->getEntitiesFromSequenceContainer(handle)); } AnchorNodeSequence::~AnchorNodeSequence() = default; @@ -102,6 +102,13 @@ namespace dspx { d->handleTakeFromSequenceContainer(takenEntity, entity); } + ParamCurveAnchor *AnchorNodeSequence::paramCurveAnchor() const { + Q_D(const AnchorNodeSequence); + return d->paramCurveAnchor; + } + + + } -#include "moc_AnchorNodeSequence.cpp" \ No newline at end of file +#include "moc_AnchorNodeSequence.cpp" diff --git a/src/libs/application/dspxmodel/src/AnchorNodeSequence.h b/src/libs/application/dspxmodel/src/AnchorNodeSequence.h index 59b151a3..a965ba5f 100644 --- a/src/libs/application/dspxmodel/src/AnchorNodeSequence.h +++ b/src/libs/application/dspxmodel/src/AnchorNodeSequence.h @@ -4,6 +4,7 @@ #include #include +#include namespace QDspx { struct AnchorNode; @@ -12,6 +13,7 @@ namespace QDspx { namespace dspx { class AnchorNode; + class ParamCurveAnchor; class AnchorNodeSequencePrivate; @@ -23,6 +25,7 @@ namespace dspx { Q_PROPERTY(int size READ size NOTIFY sizeChanged) Q_PROPERTY(AnchorNode *firstItem READ firstItem NOTIFY firstItemChanged) Q_PROPERTY(AnchorNode *lastItem READ lastItem NOTIFY lastItemChanged) + Q_PROPERTY(ParamCurveAnchor *paramCurveAnchor READ paramCurveAnchor CONSTANT) Q_PRIVATE_PROPERTY(d_func(), QJSValue iterable READ iterable CONSTANT) public: ~AnchorNodeSequence() override; @@ -41,6 +44,12 @@ namespace dspx { QList toQDspx() const; void fromQDspx(const QList &nodes); + ParamCurveAnchor *paramCurveAnchor() const; + + auto asRange() const { + return impl::SequenceRange(this); + } + Q_SIGNALS: void itemAboutToInsert(AnchorNode *item); void itemInserted(AnchorNode *item); @@ -56,10 +65,10 @@ namespace dspx { private: friend class ModelPrivate; - explicit AnchorNodeSequence(Handle handle, Model *model); + explicit AnchorNodeSequence(ParamCurveAnchor *paramCurveAnchor, Handle handle, Model *model); QScopedPointer d_ptr; }; } -#endif //DIFFSCOPE_DSPX_MODEL_ANCHORNODESEQUENCE_H \ No newline at end of file +#endif //DIFFSCOPE_DSPX_MODEL_ANCHORNODESEQUENCE_H diff --git a/src/libs/application/dspxmodel/src/AnchorNodeSequence_p.h b/src/libs/application/dspxmodel/src/AnchorNodeSequence_p.h new file mode 100644 index 00000000..4fd3bb21 --- /dev/null +++ b/src/libs/application/dspxmodel/src/AnchorNodeSequence_p.h @@ -0,0 +1,19 @@ +#ifndef DIFFSCOPE_DSPX_MODEL_ANCHORNODESEQUENCE_P_H +#define DIFFSCOPE_DSPX_MODEL_ANCHORNODESEQUENCE_P_H + +#include + +#include +#include + +namespace dspx { + + class AnchorNodeSequencePrivate : public PointSequenceData { + Q_DECLARE_PUBLIC(AnchorNodeSequence) + public: + ParamCurveAnchor *paramCurveAnchor{}; + }; + +} + +#endif //DIFFSCOPE_DSPX_MODEL_ANCHORNODESEQUENCE_P_H \ No newline at end of file diff --git a/src/libs/application/dspxmodel/src/AnchorNode_p.h b/src/libs/application/dspxmodel/src/AnchorNode_p.h new file mode 100644 index 00000000..a018f31f --- /dev/null +++ b/src/libs/application/dspxmodel/src/AnchorNode_p.h @@ -0,0 +1,24 @@ +#ifndef DIFFSCOPE_DSPX_MODEL_ANCHORNODE_P_H +#define DIFFSCOPE_DSPX_MODEL_ANCHORNODE_P_H + +#include + +namespace dspx { + + class AnchorNodePrivate { + Q_DECLARE_PUBLIC(AnchorNode) + public: + AnchorNode *q_ptr; + AnchorNode::InterpolationMode interp; + int x; + int y; + AnchorNodeSequence *anchorNodeSequence{}; + + + + static void setAnchorNodeSequence(AnchorNode *item, AnchorNodeSequence *anchorNodeSequence); + }; + +} + +#endif //DIFFSCOPE_DSPX_MODEL_ANCHORNODE_P_H \ No newline at end of file diff --git a/src/libs/application/dspxmodel/src/AudioClip.cpp b/src/libs/application/dspxmodel/src/AudioClip.cpp index 2a33f721..72a6d736 100644 --- a/src/libs/application/dspxmodel/src/AudioClip.cpp +++ b/src/libs/application/dspxmodel/src/AudioClip.cpp @@ -2,11 +2,11 @@ #include -#include -#include #include #include +#include #include +#include namespace dspx { @@ -68,4 +68,4 @@ namespace dspx { } } -} \ No newline at end of file +} diff --git a/src/libs/application/dspxmodel/src/AudioClip.h b/src/libs/application/dspxmodel/src/AudioClip.h index 0956e8f6..3c8db6cc 100644 --- a/src/libs/application/dspxmodel/src/AudioClip.h +++ b/src/libs/application/dspxmodel/src/AudioClip.h @@ -39,7 +39,6 @@ namespace dspx { friend class ModelPrivate; explicit AudioClip(Handle handle, Model *model); QScopedPointer d_ptr; - }; } diff --git a/src/libs/application/dspxmodel/src/BasicModelStrategy.cpp b/src/libs/application/dspxmodel/src/BasicModelStrategy.cpp index 2056e26d..a88d80df 100644 --- a/src/libs/application/dspxmodel/src/BasicModelStrategy.cpp +++ b/src/libs/application/dspxmodel/src/BasicModelStrategy.cpp @@ -2,131 +2,19 @@ #include -#include -#include - #include +#include namespace dspx { - class BasicModelStrategyEntity : public QObject { - Q_OBJECT - public: - using QObject::QObject; - - ModelStrategy::Entity type{}; - - }; - - class BasicModelStrategyItemEntity : public BasicModelStrategyEntity { - Q_OBJECT - public: - using BasicModelStrategyEntity::BasicModelStrategyEntity; - - QHash properties; - QHash associatedSubEntities; - }; - - class BasicModelStrategySequenceContainerEntity : public BasicModelStrategyEntity { - Q_OBJECT - public: - using BasicModelStrategyEntity::BasicModelStrategyEntity; - - QSet sequence; - }; - - class BasicModelStrategyListContainerEntity : public BasicModelStrategyEntity { - Q_OBJECT - public: - using BasicModelStrategyEntity::BasicModelStrategyEntity; - - QList list; - }; - - class BasicModelStrategyMapContainerEntity : public BasicModelStrategyEntity { - Q_OBJECT - public: - using BasicModelStrategyEntity::BasicModelStrategyEntity; - - QHash map; - }; - - class BasicModelStrategyDataArrayEntity : public BasicModelStrategyEntity { - Q_OBJECT - public: - using BasicModelStrategyEntity::BasicModelStrategyEntity; - - QVariantList data; - }; - - static BasicModelStrategyEntity *createByType(ModelStrategy::Entity type, QObject *parent) { - BasicModelStrategyEntity *obj; - switch (type) { - case ModelStrategy::EI_AudioClip: - case ModelStrategy::EI_Global: - case ModelStrategy::EI_Label: - case ModelStrategy::EI_Note: - case ModelStrategy::EI_Param: - case ModelStrategy::EI_ParamCurveAnchor: - case ModelStrategy::EI_ParamCurveFree: - case ModelStrategy::EI_ParamCurveAnchorNode: - case ModelStrategy::EI_Phoneme: - case ModelStrategy::EI_SingingClip: - case ModelStrategy::EI_Source: - case ModelStrategy::EI_Tempo: - case ModelStrategy::EI_TimeSignature: - case ModelStrategy::EI_Track: - case ModelStrategy::EI_WorkspaceInfo: - obj = new BasicModelStrategyItemEntity(parent); - obj->type = type; - break; - case ModelStrategy::ES_Clips: - case ModelStrategy::ES_Labels: - case ModelStrategy::ES_Notes: - case ModelStrategy::ES_ParamCurveAnchorNodes: - case ModelStrategy::ES_ParamCurves: - case ModelStrategy::ES_Tempos: - case ModelStrategy::ES_TimeSignatures: - obj = new BasicModelStrategySequenceContainerEntity(parent); - obj->type = type; - break; - case ModelStrategy::EL_Phonemes: - case ModelStrategy::EL_Tracks: - obj = new BasicModelStrategyListContainerEntity(parent); - obj->type = type; - break; - case ModelStrategy::ED_ParamCurveFreeValues: - case ModelStrategy::ED_VibratoPoints: - obj = new BasicModelStrategyDataArrayEntity(parent); - obj->type = type; - break; - case ModelStrategy::EM_Params: - case ModelStrategy::EM_Sources: - case ModelStrategy::EM_Workspace: - obj = new BasicModelStrategyMapContainerEntity(parent); - obj->type = type; - break; - default: - Q_UNREACHABLE(); - } - return obj; - } - - template - T *handleCast(Handle entity) { - auto obj = qobject_cast(reinterpret_cast(entity.d)); - Q_ASSERT(obj); - return obj; - } - BasicModelStrategy::BasicModelStrategy(QObject *parent) : ModelStrategy(parent) { } BasicModelStrategy::~BasicModelStrategy() = default; Handle BasicModelStrategy::createEntity(Entity entityType) { - auto object = createByType(entityType, this); - Handle entity {reinterpret_cast(object)}; + auto object = BasicModelStrategyEntity::createByType(entityType, this); + Handle entity{reinterpret_cast(object)}; Q_EMIT createEntityNotified(entity, entityType); return entity; } @@ -141,6 +29,30 @@ namespace dspx { return reinterpret_cast(entity.d)->type; } + QList BasicModelStrategy::getEntitiesFromSequenceContainer(Handle sequenceContainerEntity) { + QList a; + std::ranges::transform(handleCast(sequenceContainerEntity)->sequence, std::back_inserter(a), [](auto *obj) { + return Handle{reinterpret_cast(obj)}; + }); + return a; + } + + QList BasicModelStrategy::getEntitiesFromListContainer(Handle listContainerEntity) { + QList a; + std::ranges::transform(handleCast(listContainerEntity)->list, std::back_inserter(a), [](auto *obj) { + return Handle{reinterpret_cast(obj)}; + }); + return a; + } + + QList> BasicModelStrategy::getEntitiesFromMapContainer(Handle mapContainerEntity) { + QList> a; + for (auto [key, value] : handleCast(mapContainerEntity)->map.asKeyValueRange()) { + a.append({key, Handle{reinterpret_cast(value)}}); + } + return a; + } + bool BasicModelStrategy::insertIntoSequenceContainer(Handle sequenceContainerEntity, Handle entity) { auto sequenceContainerObject = handleCast(sequenceContainerEntity); auto object = reinterpret_cast(entity.d); @@ -184,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); @@ -203,7 +129,7 @@ namespace dspx { } auto object = listContainerObject->list.takeAt(index); object->setParent(this); - Handle entity {reinterpret_cast(object)}; + Handle entity{reinterpret_cast(object)}; Q_EMIT takeFromListContainerNotified(entity, listContainerEntity, index); return entity; } @@ -213,7 +139,7 @@ namespace dspx { if (mapContainerObject->map.contains(key)) { auto object = mapContainerObject->map.take(key); object->setParent(this); - Handle entity {reinterpret_cast(object)}; + Handle entity{reinterpret_cast(object)}; Q_EMIT takeFromMapContainerNotified(entity, mapContainerEntity, key); return entity; } @@ -232,12 +158,14 @@ namespace dspx { void BasicModelStrategy::setEntityProperty(Handle entity, Property property, const QVariant &value) { auto object = handleCast(entity); + Q_ASSERT(isEntityTypeAndPropertyTypeCompatible(object->type, property)); object->properties.insert(property, value); Q_EMIT setEntityPropertyNotified(entity, property, value); } QVariant BasicModelStrategy::getEntityProperty(Handle entity, Property property) { auto object = handleCast(entity); + Q_ASSERT(isEntityTypeAndPropertyTypeCompatible(object->type, property)); return object->properties.value(property); } bool BasicModelStrategy::spliceDataArray(Handle dataArrayEntity, int index, int length, const QVariantList &values) { @@ -274,70 +202,12 @@ namespace dspx { auto object = handleCast(entity); auto subObject = object->associatedSubEntities.value(relationship); if (!subObject) { - Entity subObjectType; - switch (relationship) { - case R_Children: - switch (object->type) { - case EI_Global: - subObjectType = EL_Tracks; - break; - case EI_Track: - subObjectType = ES_Clips; - break; - case EI_SingingClip: - subObjectType = ES_Notes; - break; - case EI_ParamCurveAnchor: - subObjectType = ES_ParamCurveAnchorNodes; - break; - case EI_ParamCurveFree: - subObjectType = ED_ParamCurveFreeValues; - break; - default: - Q_UNREACHABLE(); - } - break; - case R_Labels: - subObjectType = ES_Labels; - break; - case R_ParamCurvesEdited: - case R_ParamCurvesOriginal: - case R_ParamCurvesTransform: - subObjectType = ES_ParamCurves; - break; - case R_Params: - subObjectType = EM_Params; - break; - case R_PhonemesEdited: - case R_PhonemesOriginal: - subObjectType = EL_Phonemes; - break; - case R_Sources: - subObjectType = EM_Sources; - break; - case R_Tempos: - subObjectType = ES_Tempos; - break; - case R_TimeSignatures: - subObjectType = ES_TimeSignatures; - break; - case R_VibratoPointsAmplitude: - case R_VibratoPointsFrequency: - subObjectType = ED_VibratoPoints; - break; - case R_Workspace: - subObjectType = EM_Workspace; - break; - default: - Q_UNREACHABLE(); - } - subObject = createByType(subObjectType, object); + Entity subObjectType = getAssociatedSubEntityTypeFromEntityTypeAndRelationship(object->type, relationship); + subObject = BasicModelStrategyEntity::createByType(subObjectType, object); object->associatedSubEntities.insert(relationship, subObject); } - Handle subEntity {reinterpret_cast(subObject)}; + Handle subEntity{reinterpret_cast(subObject)}; return subEntity; } } - -#include "BasicModelStrategy.moc" \ No newline at end of file diff --git a/src/libs/application/dspxmodel/src/BasicModelStrategy.h b/src/libs/application/dspxmodel/src/BasicModelStrategy.h index a76b33e8..0cf44804 100644 --- a/src/libs/application/dspxmodel/src/BasicModelStrategy.h +++ b/src/libs/application/dspxmodel/src/BasicModelStrategy.h @@ -14,9 +14,13 @@ namespace dspx { Handle createEntity(Entity entityType) override; void destroyEntity(Handle entity) override; Entity getEntityType(Handle entity) override; + QList getEntitiesFromSequenceContainer(Handle sequenceContainerEntity) override; + QList getEntitiesFromListContainer(Handle listContainerEntity) override; + QList> getEntitiesFromMapContainer(Handle mapContainerEntity) override; 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; @@ -28,7 +32,6 @@ namespace dspx { int getSizeOfDataArray(Handle dataContainerEntity) override; bool rotateDataArray(Handle dataContainerEntity, int leftIndex, int middleIndex, int rightIndex) override; Handle getAssociatedSubEntity(Handle entity, Relationship relationship) override; - }; } diff --git a/src/libs/application/dspxmodel/src/BasicModelStrategyEntity_p.h b/src/libs/application/dspxmodel/src/BasicModelStrategyEntity_p.h new file mode 100644 index 00000000..8b111d48 --- /dev/null +++ b/src/libs/application/dspxmodel/src/BasicModelStrategyEntity_p.h @@ -0,0 +1,123 @@ +#ifndef DIFFSCOPE_DSPX_MODEL_BASICMODELSTRATEGYENTITY_P_H +#define DIFFSCOPE_DSPX_MODEL_BASICMODELSTRATEGYENTITY_P_H + +#include +#include +#include + +#include + +namespace dspx { + class BasicModelStrategyEntity : public QObject { + Q_OBJECT + public: + using QObject::QObject; + + ModelStrategy::Entity type{}; + + static inline BasicModelStrategyEntity *createByType(ModelStrategy::Entity type, QObject *parent); + }; + + class BasicModelStrategyItemEntity : public BasicModelStrategyEntity { + Q_OBJECT + public: + using BasicModelStrategyEntity::BasicModelStrategyEntity; + + QHash properties; + QHash associatedSubEntities; + }; + + class BasicModelStrategySequenceContainerEntity : public BasicModelStrategyEntity { + Q_OBJECT + public: + using BasicModelStrategyEntity::BasicModelStrategyEntity; + + QSet sequence; + }; + + class BasicModelStrategyListContainerEntity : public BasicModelStrategyEntity { + Q_OBJECT + public: + using BasicModelStrategyEntity::BasicModelStrategyEntity; + + QList list; + }; + + class BasicModelStrategyMapContainerEntity : public BasicModelStrategyEntity { + Q_OBJECT + public: + using BasicModelStrategyEntity::BasicModelStrategyEntity; + + QHash map; + }; + + class BasicModelStrategyDataArrayEntity : public BasicModelStrategyEntity { + Q_OBJECT + public: + using BasicModelStrategyEntity::BasicModelStrategyEntity; + + QVariantList data; + }; + + BasicModelStrategyEntity *BasicModelStrategyEntity::createByType(ModelStrategy::Entity type, QObject *parent) { + BasicModelStrategyEntity *obj; + switch (type) { + case ModelStrategy::EI_AudioClip: + case ModelStrategy::EI_Global: + case ModelStrategy::EI_Label: + case ModelStrategy::EI_Note: + case ModelStrategy::EI_Param: + case ModelStrategy::EI_ParamCurveAnchor: + case ModelStrategy::EI_ParamCurveFree: + case ModelStrategy::EI_ParamCurveAnchorNode: + case ModelStrategy::EI_Phoneme: + case ModelStrategy::EI_SingingClip: + case ModelStrategy::EI_Source: + case ModelStrategy::EI_Tempo: + case ModelStrategy::EI_TimeSignature: + case ModelStrategy::EI_Track: + case ModelStrategy::EI_WorkspaceInfo: + obj = new BasicModelStrategyItemEntity(parent); + obj->type = type; + break; + case ModelStrategy::ES_Clips: + case ModelStrategy::ES_Labels: + case ModelStrategy::ES_Notes: + case ModelStrategy::ES_ParamCurveAnchorNodes: + case ModelStrategy::ES_ParamCurves: + case ModelStrategy::ES_Tempos: + case ModelStrategy::ES_TimeSignatures: + obj = new BasicModelStrategySequenceContainerEntity(parent); + obj->type = type; + break; + case ModelStrategy::EL_Phonemes: + case ModelStrategy::EL_Tracks: + obj = new BasicModelStrategyListContainerEntity(parent); + obj->type = type; + break; + case ModelStrategy::ED_ParamCurveFreeValues: + case ModelStrategy::ED_VibratoPoints: + obj = new BasicModelStrategyDataArrayEntity(parent); + obj->type = type; + break; + case ModelStrategy::EM_Params: + case ModelStrategy::EM_Sources: + case ModelStrategy::EM_Workspace: + obj = new BasicModelStrategyMapContainerEntity(parent); + obj->type = type; + break; + default: + Q_UNREACHABLE(); + } + return obj; + } + + template + T *handleCast(Handle entity) { + auto obj = qobject_cast(reinterpret_cast(entity.d)); + Q_ASSERT(obj); + return obj; + } +} + +#endif //DIFFSCOPE_DSPX_MODEL_BASICMODELSTRATEGYENTITY_P_H diff --git a/src/libs/application/dspxmodel/src/BusControl.cpp b/src/libs/application/dspxmodel/src/BusControl.cpp index bdf7deeb..ee5d174a 100644 --- a/src/libs/application/dspxmodel/src/BusControl.cpp +++ b/src/libs/application/dspxmodel/src/BusControl.cpp @@ -27,4 +27,4 @@ namespace dspx { Control::handleProxySetEntityProperty(property, value); } -} \ No newline at end of file +} diff --git a/src/libs/application/dspxmodel/src/BusControl.h b/src/libs/application/dspxmodel/src/BusControl.h index ca03ad74..b395f809 100644 --- a/src/libs/application/dspxmodel/src/BusControl.h +++ b/src/libs/application/dspxmodel/src/BusControl.h @@ -24,7 +24,6 @@ namespace dspx { friend class ModelPrivate; explicit BusControl(Handle handle, Model *model); void handleProxySetEntityProperty(int property, const QVariant &value); - }; } diff --git a/src/libs/application/dspxmodel/src/Clip.cpp b/src/libs/application/dspxmodel/src/Clip.cpp index 5d58de52..4caf4492 100644 --- a/src/libs/application/dspxmodel/src/Clip.cpp +++ b/src/libs/application/dspxmodel/src/Clip.cpp @@ -1,33 +1,36 @@ #include "Clip.h" +#include "Clip_p.h" #include #include -#include -#include +#include #include #include #include -#include -#include -#include +#include #include +#include +#include +#include namespace dspx { - class ClipPrivate { - Q_DECLARE_PUBLIC(Clip) - public: - Clip *q_ptr; - ModelPrivate *pModel; - QString name; - BusControl *control; - ClipTime *time; - Clip::ClipType type; - Track *track{}; - Workspace *workspace; - bool overlapped{}; - }; + void ClipPrivate::setOverlapped(Clip *item, bool overlapped) { + auto d = item->d_func(); + if (d->overlapped != overlapped) { + d->overlapped = overlapped; + Q_EMIT item->overlappedChanged(overlapped); + } + } + + void ClipPrivate::setClipSequence(Clip *item, ClipSequence *clipSequence) { + auto d = item->d_func(); + if (d->clipSequence != clipSequence) { + d->clipSequence = clipSequence; + Q_EMIT item->clipSequenceChanged(); + } + } Clip::Clip(ClipType type, Handle handle, Model *model) : EntityObject(handle, model), d_ptr(new ClipPrivate) { Q_D(Clip); @@ -107,9 +110,9 @@ namespace dspx { } } - Track *Clip::track() const { + ClipSequence *Clip::clipSequence() const { Q_D(const Clip); - return d->track; + return d->clipSequence; } int Clip::position() const { @@ -127,22 +130,6 @@ namespace dspx { return d->overlapped; } - void Clip::setOverlapped(bool overlapped) { - Q_D(Clip); - if (d->overlapped != overlapped) { - d->overlapped = overlapped; - Q_EMIT overlappedChanged(overlapped); - } - } - - void Clip::setTrack(Track *track) { - Q_D(Clip); - if (d->track != track) { - d->track = track; - Q_EMIT trackChanged(); - } - } - void Clip::handleSetEntityProperty(int property, const QVariant &value) { Q_D(Clip); switch (property) { @@ -170,4 +157,4 @@ namespace dspx { } } -#include "moc_Clip.cpp" \ No newline at end of file +#include "moc_Clip.cpp" diff --git a/src/libs/application/dspxmodel/src/Clip.h b/src/libs/application/dspxmodel/src/Clip.h index 5dbbcb69..c35024e9 100644 --- a/src/libs/application/dspxmodel/src/Clip.h +++ b/src/libs/application/dspxmodel/src/Clip.h @@ -15,9 +15,7 @@ namespace dspx { class BusControl; class ClipTime; class Workspace; - class Track; - - class ClipSequencePrivate; + class ClipSequence; class ClipPrivate; @@ -31,7 +29,7 @@ namespace dspx { Q_PROPERTY(ClipTime *time READ time CONSTANT) Q_PROPERTY(ClipType type READ type CONSTANT) Q_PROPERTY(Workspace *workspace READ workspace CONSTANT) - Q_PROPERTY(Track *track READ track NOTIFY trackChanged) + Q_PROPERTY(ClipSequence *clipSequence READ clipSequence NOTIFY clipSequenceChanged) Q_PROPERTY(bool overlapped READ isOverlapped NOTIFY overlappedChanged) public: enum ClipType { @@ -56,7 +54,7 @@ namespace dspx { QDspx::ClipRef toQDspx() const; void fromQDspx(const QDspx::ClipRef &clip); - Track *track() const; + ClipSequence *clipSequence() const; int position() const; @@ -66,7 +64,7 @@ namespace dspx { Q_SIGNALS: void nameChanged(const QString &name); - void trackChanged(); + void clipSequenceChanged(); void positionChanged(int position); void lengthChanged(int length); void overlappedChanged(bool overlapped); @@ -76,10 +74,7 @@ namespace dspx { void handleSetEntityProperty(int property, const QVariant &value) override; private: - friend class ClipSequencePrivate; QScopedPointer d_ptr; - void setTrack(Track *track); - void setOverlapped(bool overlapped); }; } // dspx diff --git a/src/libs/application/dspxmodel/src/ClipSequence.cpp b/src/libs/application/dspxmodel/src/ClipSequence.cpp index b0926d21..16c2fb08 100644 --- a/src/libs/application/dspxmodel/src/ClipSequence.cpp +++ b/src/libs/application/dspxmodel/src/ClipSequence.cpp @@ -1,53 +1,90 @@ #include "ClipSequence.h" +#include "ClipSequence_p.h" -#include #include +#include #include -#include -#include -#include -#include +#include #include -#include +#include #include -#include -#include #include -#include +#include +#include namespace dspx { - static void setClipOverlapped(Clip *item, bool overlapped); + 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()); - class ClipSequencePrivate : public RangeSequenceData { - Q_DECLARE_PUBLIC(ClipSequence) - public: - Track *track{}; - static void setOverlapped(Clip *item, bool overlapped) { - item->setOverlapped(overlapped); + for (auto affectedItem : affectedItems) { + bool isOverlapped = rangeContainer.isOverlapped(affectedItem); + ClipPrivate::setOverlapped(affectedItem, isOverlapped); } - static void setTrack(Clip *item, Track *track) { - item->setTrack(track); + + 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); - void setClipOverlapped(Clip *item, bool overlapped) { - ClipSequencePrivate::setOverlapped(item, overlapped); + 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(Handle handle, Model *model) : EntityObject(handle, model), d_ptr(new ClipSequencePrivate) { + 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); d->q_ptr = this; d->pModel = ModelPrivate::get(model); - connect(this, &ClipSequence::itemInserted, this, [=](Clip *item) { - ClipSequencePrivate::setTrack(item, d->track); - }); - connect(this, &ClipSequence::itemRemoved, this, [=](Clip *item) { - ClipSequencePrivate::setTrack(item, nullptr); - }); + d->track = track; + + d->init(model->strategy()->getEntitiesFromSequenceContainer(handle)); } ClipSequence::~ClipSequence() = default; @@ -102,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; @@ -133,11 +175,6 @@ namespace dspx { } } - void ClipSequence::setTrack(Track *track) { - Q_D(ClipSequence); - d->track = track; - } - void ClipSequence::handleInsertIntoSequenceContainer(Handle entity) { Q_D(ClipSequence); d->handleInsertIntoSequenceContainer(entity); @@ -147,7 +184,15 @@ 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); + } } -#include "moc_ClipSequence.cpp" \ No newline at end of file +#include "moc_ClipSequence.cpp" diff --git a/src/libs/application/dspxmodel/src/ClipSequence.h b/src/libs/application/dspxmodel/src/ClipSequence.h index a781810a..6a9123f8 100644 --- a/src/libs/application/dspxmodel/src/ClipSequence.h +++ b/src/libs/application/dspxmodel/src/ClipSequence.h @@ -4,6 +4,7 @@ #include #include +#include namespace QDspx { struct Clip; @@ -40,17 +41,22 @@ 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); Track *track() const; + auto asRange() const { + return impl::SequenceRange(this); + } + 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); @@ -58,15 +64,15 @@ 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; - friend class Track; - explicit ClipSequence(Handle handle, Model *model); - void setTrack(Track *track); + explicit ClipSequence(Track *track, Handle handle, Model *model); QScopedPointer d_ptr; }; } -#endif //DIFFSCOPE_DSPX_MODEL_CLIPSEQUENCE_H \ No newline at end of file +#endif //DIFFSCOPE_DSPX_MODEL_CLIPSEQUENCE_H diff --git a/src/libs/application/dspxmodel/src/ClipSequence_p.h b/src/libs/application/dspxmodel/src/ClipSequence_p.h new file mode 100644 index 00000000..dcea7702 --- /dev/null +++ b/src/libs/application/dspxmodel/src/ClipSequence_p.h @@ -0,0 +1,22 @@ +#ifndef DIFFSCOPE_DSPX_MODEL_CLIPSEQUENCE_P_H +#define DIFFSCOPE_DSPX_MODEL_CLIPSEQUENCE_P_H + +#include + +#include +#include + +namespace dspx { + + class ClipSequencePrivate : public RangeSequenceData { + Q_DECLARE_PUBLIC(ClipSequence) + public: + Track *track{}; + + void handleMoveFromAnotherSequenceContainer(Handle entity, Handle otherSequenceContainerEntity); + void handleMoveToAnotherSequenceContainer(Handle entity, Handle otherSequenceContainerEntity); + }; + +} + +#endif //DIFFSCOPE_DSPX_MODEL_CLIPSEQUENCE_P_H diff --git a/src/libs/application/dspxmodel/src/ClipTime.cpp b/src/libs/application/dspxmodel/src/ClipTime.cpp index 44a46708..50df3a7a 100644 --- a/src/libs/application/dspxmodel/src/ClipTime.cpp +++ b/src/libs/application/dspxmodel/src/ClipTime.cpp @@ -1,12 +1,10 @@ #include "ClipTime.h" - #include -#include #include -#include #include +#include namespace dspx { @@ -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 { @@ -174,4 +125,4 @@ namespace dspx { } -#include "moc_ClipTime.cpp" \ No newline at end of file +#include "moc_ClipTime.cpp" diff --git a/src/libs/application/dspxmodel/src/ClipTime.h b/src/libs/application/dspxmodel/src/ClipTime.h index 2b2333a8..20375cdb 100644 --- a/src/libs/application/dspxmodel/src/ClipTime.h +++ b/src/libs/application/dspxmodel/src/ClipTime.h @@ -1,9 +1,10 @@ #ifndef DIFFSCOPE_DSPX_MODEL_CLIPTIME_H #define DIFFSCOPE_DSPX_MODEL_CLIPTIME_H -#include #include +#include + #include namespace QDspx { @@ -17,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; @@ -56,9 +57,8 @@ namespace dspx { explicit ClipTime(Handle handle, Model *model); void handleProxySetEntityProperty(int property, const QVariant &value); QScopedPointer d_ptr; - }; } -#endif //DIFFSCOPE_DSPX_MODEL_CLIPTIME_H \ No newline at end of file +#endif //DIFFSCOPE_DSPX_MODEL_CLIPTIME_H diff --git a/src/libs/application/dspxmodel/src/Clip_p.h b/src/libs/application/dspxmodel/src/Clip_p.h new file mode 100644 index 00000000..e3a3c02c --- /dev/null +++ b/src/libs/application/dspxmodel/src/Clip_p.h @@ -0,0 +1,27 @@ +#ifndef DIFFSCOPE_DSPX_MODEL_CLIP_P_H +#define DIFFSCOPE_DSPX_MODEL_CLIP_P_H + +#include + +namespace dspx { + + class ClipPrivate { + Q_DECLARE_PUBLIC(Clip) + public: + Clip *q_ptr; + ModelPrivate *pModel; + QString name; + BusControl *control; + ClipTime *time; + Clip::ClipType type; + ClipSequence *clipSequence{}; + Workspace *workspace; + bool overlapped{}; + + static void setOverlapped(Clip *item, bool overlapped); + static void setClipSequence(Clip *item, ClipSequence *clipSequence); + }; + +} + +#endif //DIFFSCOPE_DSPX_MODEL_CLIP_P_H diff --git a/src/libs/application/dspxmodel/src/Control.cpp b/src/libs/application/dspxmodel/src/Control.cpp index b3b0826c..4d8beb45 100644 --- a/src/libs/application/dspxmodel/src/Control.cpp +++ b/src/libs/application/dspxmodel/src/Control.cpp @@ -1,10 +1,8 @@ #include "Control.h" - #include -#include -#include #include +#include namespace dspx { @@ -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 { @@ -128,4 +96,4 @@ namespace dspx { } -#include "moc_Control.cpp" \ No newline at end of file +#include "moc_Control.cpp" diff --git a/src/libs/application/dspxmodel/src/Control.h b/src/libs/application/dspxmodel/src/Control.h index 7adf1c48..d0330ec7 100644 --- a/src/libs/application/dspxmodel/src/Control.h +++ b/src/libs/application/dspxmodel/src/Control.h @@ -1,9 +1,10 @@ #ifndef DIFFSCOPE_DSPX_MODEL_CONTROL_H #define DIFFSCOPE_DSPX_MODEL_CONTROL_H -#include #include +#include + #include namespace dspx { @@ -13,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: @@ -46,7 +47,6 @@ namespace dspx { private: QScopedPointer d_ptr; - }; } diff --git a/src/libs/application/dspxmodel/src/DataArrayData_p.h b/src/libs/application/dspxmodel/src/DataArrayData_p.h index 74d18798..0d7c261e 100644 --- a/src/libs/application/dspxmodel/src/DataArrayData_p.h +++ b/src/libs/application/dspxmodel/src/DataArrayData_p.h @@ -3,8 +3,8 @@ #include -#include #include +#include #include #include @@ -24,6 +24,12 @@ namespace dspx { QList data; QJSValue iterable_; + void init(const QVariantList &values) { + for (auto v : values) { + data.append(v.value()); + } + } + int size() const { return data.size(); } diff --git a/src/libs/application/dspxmodel/src/DspxModelGlobal.h b/src/libs/application/dspxmodel/src/DspxModelGlobal.h index 4ac14b72..b61a80f8 100644 --- a/src/libs/application/dspxmodel/src/DspxModelGlobal.h +++ b/src/libs/application/dspxmodel/src/DspxModelGlobal.h @@ -4,9 +4,9 @@ #include #if defined(DSPXMODEL_LIBRARY) -# define DSPX_MODEL_EXPORT Q_DECL_EXPORT +# define DSPX_MODEL_EXPORT Q_DECL_EXPORT #else -# define DSPX_MODEL_EXPORT Q_DECL_IMPORT +# define DSPX_MODEL_EXPORT Q_DECL_IMPORT #endif #endif //DIFFSCOPE_DSPX_MODEL_DSPXMODELGLOBAL_H diff --git a/src/libs/application/dspxmodel/src/EntityObject.cpp b/src/libs/application/dspxmodel/src/EntityObject.cpp index 61129fd9..75019821 100644 --- a/src/libs/application/dspxmodel/src/EntityObject.cpp +++ b/src/libs/application/dspxmodel/src/EntityObject.cpp @@ -2,8 +2,8 @@ #include "EntityObject_p.h" #include -#include #include +#include namespace dspx { @@ -22,7 +22,7 @@ namespace dspx { EntityObject::~EntityObject() { Q_D(EntityObject); if (d->model && d->handle) { - d->model->strategy()->destroyEntity(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(); } @@ -68,4 +74,4 @@ namespace dspx { } } -#include "moc_EntityObject.cpp" \ No newline at end of file +#include "moc_EntityObject.cpp" diff --git a/src/libs/application/dspxmodel/src/EntityObject.h b/src/libs/application/dspxmodel/src/EntityObject.h index 3af33f85..c4cb85a8 100644 --- a/src/libs/application/dspxmodel/src/EntityObject.h +++ b/src/libs/application/dspxmodel/src/EntityObject.h @@ -3,8 +3,8 @@ #include -#include #include +#include namespace dspx { @@ -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); @@ -46,7 +49,6 @@ namespace dspx { friend class ModelPrivate; explicit EntityObject(QObject *parent = nullptr); QScopedPointer d_ptr; - }; } diff --git a/src/libs/application/dspxmodel/src/FreeValueDataArray.cpp b/src/libs/application/dspxmodel/src/FreeValueDataArray.cpp index 4543935b..6f085afa 100644 --- a/src/libs/application/dspxmodel/src/FreeValueDataArray.cpp +++ b/src/libs/application/dspxmodel/src/FreeValueDataArray.cpp @@ -1,19 +1,25 @@ #include "FreeValueDataArray.h" #include +#include #include namespace dspx { class FreeValueDataArrayPrivate : public DataArrayData { Q_DECLARE_PUBLIC(FreeValueDataArray) + public: + ParamCurveFree *paramCurveFree{}; }; - FreeValueDataArray::FreeValueDataArray(Handle handle, Model *model) : EntityObject(handle, model), d_ptr(new FreeValueDataArrayPrivate) { + FreeValueDataArray::FreeValueDataArray(ParamCurveFree *paramCurveFree, Handle handle, Model *model) : EntityObject(handle, model), d_ptr(new FreeValueDataArrayPrivate) { Q_D(FreeValueDataArray); Q_ASSERT(model->strategy()->getEntityType(handle) == ModelStrategy::ED_ParamCurveFreeValues); d->q_ptr = this; d->pModel = ModelPrivate::get(model); + d->paramCurveFree = paramCurveFree; + + d->init(model->strategy()->sliceDataArray(handle, 0, model->strategy()->getSizeOfDataArray(handle))); } FreeValueDataArray::~FreeValueDataArray() = default; @@ -65,6 +71,13 @@ namespace dspx { d->handleRotateDataArray(leftIndex, middleIndex, rightIndex); } + ParamCurveFree *FreeValueDataArray::paramCurveFree() const { + Q_D(const FreeValueDataArray); + return d->paramCurveFree; + } + + + } -#include "moc_FreeValueDataArray.cpp" \ No newline at end of file +#include "moc_FreeValueDataArray.cpp" diff --git a/src/libs/application/dspxmodel/src/FreeValueDataArray.h b/src/libs/application/dspxmodel/src/FreeValueDataArray.h index b814c906..2c9a263a 100644 --- a/src/libs/application/dspxmodel/src/FreeValueDataArray.h +++ b/src/libs/application/dspxmodel/src/FreeValueDataArray.h @@ -7,6 +7,8 @@ namespace dspx { + class ParamCurveFree; + class FreeValueDataArrayPrivate; class DSPX_MODEL_EXPORT FreeValueDataArray : public EntityObject { @@ -15,6 +17,7 @@ namespace dspx { QML_UNCREATABLE("") Q_DECLARE_PRIVATE(FreeValueDataArray) Q_PROPERTY(int size READ size NOTIFY sizeChanged) + Q_PROPERTY(ParamCurveFree *paramCurveFree READ paramCurveFree CONSTANT) Q_PRIVATE_PROPERTY(d_func(), QJSValue iterable READ iterable CONSTANT) public: @@ -28,6 +31,8 @@ namespace dspx { QList toQDspx() const; void fromQDspx(const QList &values); + ParamCurveFree *paramCurveFree() const; + Q_SIGNALS: void sizeChanged(int size); void aboutToSplice(int index, int length, const QList &values); @@ -41,10 +46,10 @@ namespace dspx { private: friend class ModelPrivate; - explicit FreeValueDataArray(Handle handle, Model *model); + explicit FreeValueDataArray(ParamCurveFree *paramCurveFree, Handle handle, Model *model); QScopedPointer d_ptr; }; } -#endif //DIFFSCOPE_DSPX_MODEL_FREEVALUEDATAARRAY_H \ No newline at end of file +#endif //DIFFSCOPE_DSPX_MODEL_FREEVALUEDATAARRAY_H diff --git a/src/libs/application/dspxmodel/src/Global.cpp b/src/libs/application/dspxmodel/src/Global.cpp index 6780e601..fd35f5e5 100644 --- a/src/libs/application/dspxmodel/src/Global.cpp +++ b/src/libs/application/dspxmodel/src/Global.cpp @@ -1,7 +1,5 @@ #include "Global.h" - #include -#include #include @@ -17,31 +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; @@ -68,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); @@ -112,4 +96,4 @@ namespace dspx { } -#include "moc_Global.cpp" \ No newline at end of file +#include "moc_Global.cpp" diff --git a/src/libs/application/dspxmodel/src/Global.h b/src/libs/application/dspxmodel/src/Global.h index aa2f91da..e30b1760 100644 --- a/src/libs/application/dspxmodel/src/Global.h +++ b/src/libs/application/dspxmodel/src/Global.h @@ -1,9 +1,12 @@ #ifndef DIFFSCOPE_DSPX_MODEL_GLOBAL_H #define DIFFSCOPE_DSPX_MODEL_GLOBAL_H -#include #include +#include + +#include + namespace QDspx { struct Global; } @@ -16,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) @@ -39,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); @@ -52,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); @@ -60,7 +73,6 @@ namespace dspx { explicit Global(Model *model); QScopedPointer d_ptr; - }; } diff --git a/src/libs/application/dspxmodel/src/Handle.h b/src/libs/application/dspxmodel/src/Handle.h index 7f4aeba6..6aeb83f5 100644 --- a/src/libs/application/dspxmodel/src/Handle.h +++ b/src/libs/application/dspxmodel/src/Handle.h @@ -22,13 +22,12 @@ namespace dspx { constexpr operator bool() const { return d; } - }; } namespace std { - template<> + template <> struct hash { size_t operator()(const dspx::Handle &handle) const noexcept { return std::hash{}(handle.d); diff --git a/src/libs/application/dspxmodel/src/Label.cpp b/src/libs/application/dspxmodel/src/Label.cpp index d0b85149..a5d784fa 100644 --- a/src/libs/application/dspxmodel/src/Label.cpp +++ b/src/libs/application/dspxmodel/src/Label.cpp @@ -1,39 +1,23 @@ #include "Label.h" - +#include "Label_p.h" #include -#include #include +#include #include #include namespace dspx { - class LabelPrivate { - Q_DECLARE_PUBLIC(Label) - public: - Label *q_ptr; - int pos; - QString text; - - void setPosUnchecked(int pos_); - void setPos(int pos_); - }; - - 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; + void LabelPrivate::setLabelSequence(Label *item, LabelSequence *labelSequence) { + auto d = item->d_func(); + if (d->labelSequence != labelSequence) { + d->labelSequence = labelSequence; + Q_EMIT item->labelSequenceChanged(); } - setPosUnchecked(pos_); } Label::Label(Handle handle, Model *model) : EntityObject(handle, model), d_ptr(new LabelPrivate) { @@ -54,7 +38,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 { @@ -67,8 +51,13 @@ namespace dspx { model()->strategy()->setEntityProperty(handle(), ModelStrategy::P_Text, text); } + LabelSequence *Label::labelSequence() const { + Q_D(const Label); + return d->labelSequence; + } + QDspx::Label Label::toQDspx() const { - return QDspx::Label { + return { .pos = pos(), .text = text(), }; @@ -99,4 +88,4 @@ namespace dspx { } -#include "moc_Label.cpp" \ No newline at end of file +#include "moc_Label.cpp" diff --git a/src/libs/application/dspxmodel/src/Label.h b/src/libs/application/dspxmodel/src/Label.h index 3da511d9..ba81bbfd 100644 --- a/src/libs/application/dspxmodel/src/Label.h +++ b/src/libs/application/dspxmodel/src/Label.h @@ -1,15 +1,17 @@ #ifndef DIFFSCOPE_DSPX_MODEL_LABEL_H #define DIFFSCOPE_DSPX_MODEL_LABEL_H -#include #include +#include + namespace QDspx { struct Label; } namespace dspx { + class LabelSequence; class LabelPrivate; class DSPX_MODEL_EXPORT Label : public EntityObject { @@ -17,8 +19,9 @@ 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) public: ~Label() override; @@ -28,12 +31,15 @@ namespace dspx { QString text() const; void setText(const QString &text); + LabelSequence *labelSequence() const; + QDspx::Label toQDspx() const; void fromQDspx(const QDspx::Label &label); Q_SIGNALS: void posChanged(int pos); void textChanged(const QString &text); + void labelSequenceChanged(); protected: void handleSetEntityProperty(int property, const QVariant &value) override; @@ -42,7 +48,6 @@ namespace dspx { friend class ModelPrivate; explicit Label(Handle handle, Model *model); QScopedPointer d_ptr; - }; } diff --git a/src/libs/application/dspxmodel/src/LabelSequence.cpp b/src/libs/application/dspxmodel/src/LabelSequence.cpp index 8bd812b0..45ff44ef 100644 --- a/src/libs/application/dspxmodel/src/LabelSequence.cpp +++ b/src/libs/application/dspxmodel/src/LabelSequence.cpp @@ -1,27 +1,25 @@ #include "LabelSequence.h" +#include "LabelSequence_p.h" -#include #include +#include #include -#include -#include #include #include +#include #include namespace dspx { - class LabelSequencePrivate : public PointSequenceData { - Q_DECLARE_PUBLIC(LabelSequence) - }; - LabelSequence::LabelSequence(Handle handle, Model *model) : EntityObject(handle, model), d_ptr(new LabelSequencePrivate) { Q_D(LabelSequence); Q_ASSERT(model->strategy()->getEntityType(handle) == ModelStrategy::ES_Labels); d->q_ptr = this; d->pModel = ModelPrivate::get(model); + + d->init(model->strategy()->getEntitiesFromSequenceContainer(handle)); } LabelSequence::~LabelSequence() = default; @@ -104,4 +102,4 @@ namespace dspx { } -#include "moc_LabelSequence.cpp" \ No newline at end of file +#include "moc_LabelSequence.cpp" diff --git a/src/libs/application/dspxmodel/src/LabelSequence.h b/src/libs/application/dspxmodel/src/LabelSequence.h index bb03d155..cca09003 100644 --- a/src/libs/application/dspxmodel/src/LabelSequence.h +++ b/src/libs/application/dspxmodel/src/LabelSequence.h @@ -4,6 +4,7 @@ #include #include +#include namespace QDspx { struct Label; @@ -41,6 +42,10 @@ namespace dspx { QList toQDspx() const; void fromQDspx(const QList &labels); + auto asRange() const { + return impl::SequenceRange(this); + } + Q_SIGNALS: void itemAboutToInsert(Label *item); void itemInserted(Label *item); diff --git a/src/libs/application/dspxmodel/src/LabelSequence_p.h b/src/libs/application/dspxmodel/src/LabelSequence_p.h new file mode 100644 index 00000000..db856341 --- /dev/null +++ b/src/libs/application/dspxmodel/src/LabelSequence_p.h @@ -0,0 +1,17 @@ +#ifndef DIFFSCOPE_DSPX_MODEL_LABELSEQUENCE_P_H +#define DIFFSCOPE_DSPX_MODEL_LABELSEQUENCE_P_H + +#include + +#include +#include + +namespace dspx { + + class LabelSequencePrivate : public PointSequenceData { + Q_DECLARE_PUBLIC(LabelSequence) + }; + +} + +#endif //DIFFSCOPE_DSPX_MODEL_LABELSEQUENCE_P_H \ No newline at end of file diff --git a/src/libs/application/dspxmodel/src/Label_p.h b/src/libs/application/dspxmodel/src/Label_p.h new file mode 100644 index 00000000..e4c2ef1e --- /dev/null +++ b/src/libs/application/dspxmodel/src/Label_p.h @@ -0,0 +1,23 @@ +#ifndef DIFFSCOPE_DSPX_MODEL_LABEL_P_H +#define DIFFSCOPE_DSPX_MODEL_LABEL_P_H + +#include + +namespace dspx { + + class LabelPrivate { + Q_DECLARE_PUBLIC(Label) + public: + Label *q_ptr; + int pos; + QString text; + LabelSequence *labelSequence{}; + + + + static void setLabelSequence(Label *item, LabelSequence *labelSequence); + }; + +} + +#endif //DIFFSCOPE_DSPX_MODEL_LABEL_P_H \ No newline at end of file diff --git a/src/libs/application/dspxmodel/src/ListData.cpp b/src/libs/application/dspxmodel/src/ListData.cpp index e035c882..0b4c4663 100644 --- a/src/libs/application/dspxmodel/src/ListData.cpp +++ b/src/libs/application/dspxmodel/src/ListData.cpp @@ -20,4 +20,4 @@ namespace dspx { return iterable; } -} \ No newline at end of file +} diff --git a/src/libs/application/dspxmodel/src/ListData_p.h b/src/libs/application/dspxmodel/src/ListData_p.h index 0b21a4bd..358b707e 100644 --- a/src/libs/application/dspxmodel/src/ListData_p.h +++ b/src/libs/application/dspxmodel/src/ListData_p.h @@ -3,8 +3,8 @@ #include -#include #include +#include #include @@ -15,7 +15,7 @@ namespace dspx { static QJSValue create(QObject *o); }; - template + template class ListData { public: ListType *q_ptr; @@ -35,10 +35,20 @@ namespace dspx { Q_UNREACHABLE(); } + void init(const QList &handles) { + auto q = q_ptr; + for (auto handle : handles) { + auto item = getItem(handle, true); + itemList.append(item); + setList(item, q); + } + } + void insertItem(int index, ItemType *item) { 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(); @@ -49,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(); @@ -83,9 +94,9 @@ namespace dspx { void handleRotateListContainer(int leftIndex, int middleIndex, int rightIndex) { auto q = q_ptr; Q_EMIT q->aboutToRotate(leftIndex, middleIndex, rightIndex); - + std::rotate(itemList.begin() + leftIndex, itemList.begin() + middleIndex, itemList.begin() + rightIndex); - + Q_EMIT q->rotated(leftIndex, middleIndex, rightIndex); Q_EMIT q->itemsChanged(); } @@ -102,4 +113,4 @@ namespace dspx { } -#endif //DIFFSCOPE_DSPX_MODEL_LISTDATA_P_H \ No newline at end of file +#endif //DIFFSCOPE_DSPX_MODEL_LISTDATA_P_H diff --git a/src/libs/application/dspxmodel/src/MapData.cpp b/src/libs/application/dspxmodel/src/MapData.cpp index dd615706..a3477b99 100644 --- a/src/libs/application/dspxmodel/src/MapData.cpp +++ b/src/libs/application/dspxmodel/src/MapData.cpp @@ -20,4 +20,4 @@ namespace dspx { return iterable; } -} \ No newline at end of file +} diff --git a/src/libs/application/dspxmodel/src/MapData_p.h b/src/libs/application/dspxmodel/src/MapData_p.h index c89a1e2f..a177940b 100644 --- a/src/libs/application/dspxmodel/src/MapData_p.h +++ b/src/libs/application/dspxmodel/src/MapData_p.h @@ -34,6 +34,12 @@ namespace dspx { Q_UNREACHABLE(); } + void init(const QList> &handles) { + for (auto &[key, handle] : handles) { + itemMap.insert(key, getItem(handle, true)); + } + } + void insertItem(const QString &key, ItemType *item) { auto q = q_ptr; Q_EMIT q->itemAboutToInsert(key, item); @@ -96,4 +102,4 @@ namespace dspx { } -#endif //DIFFSCOPE_DSPX_MODEL_MAPDATA_P_H \ No newline at end of file +#endif //DIFFSCOPE_DSPX_MODEL_MAPDATA_P_H diff --git a/src/libs/application/dspxmodel/src/Master.cpp b/src/libs/application/dspxmodel/src/Master.cpp index 227c0f5f..7f971fc6 100644 --- a/src/libs/application/dspxmodel/src/Master.cpp +++ b/src/libs/application/dspxmodel/src/Master.cpp @@ -1,13 +1,13 @@ #include "Master.h" -#include #include +#include #include +#include #include #include -#include namespace dspx { @@ -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 { @@ -49,4 +75,4 @@ namespace dspx { } -#include "moc_Master.cpp" \ No newline at end of file +#include "moc_Master.cpp" diff --git a/src/libs/application/dspxmodel/src/Master.h b/src/libs/application/dspxmodel/src/Master.h index ef02ae49..d0ac456d 100644 --- a/src/libs/application/dspxmodel/src/Master.h +++ b/src/libs/application/dspxmodel/src/Master.h @@ -4,6 +4,8 @@ #include #include +#include + namespace QDspx { struct Master; } @@ -18,29 +20,34 @@ 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); void handleProxySetEntityProperty(int property, const QVariant &value); QScopedPointer d_ptr; - }; } -#endif //DIFFSCOPE_DSPX_MODEL_MASTER_H \ No newline at end of file +#endif //DIFFSCOPE_DSPX_MODEL_MASTER_H diff --git a/src/libs/application/dspxmodel/src/Model.cpp b/src/libs/application/dspxmodel/src/Model.cpp index 47bbb9a9..b17401c5 100644 --- a/src/libs/application/dspxmodel/src/Model.cpp +++ b/src/libs/application/dspxmodel/src/Model.cpp @@ -5,30 +5,30 @@ #include -#include +#include +#include #include -#include #include +#include +#include +#include #include +#include +#include +#include #include +#include +#include #include +#include #include +#include +#include #include #include -#include -#include -#include -#include -#include -#include #include -#include -#include -#include -#include -#include -#include -#include +#include +#include namespace dspx { @@ -63,54 +63,66 @@ namespace dspx { labels = new LabelSequence(strategy->getAssociatedSubEntity(handle, ModelStrategy::R_Labels), q); tempos = new TempoSequence(strategy->getAssociatedSubEntity(handle, ModelStrategy::R_Tempos), q); timeSignatures = new TimeSignatureSequence(strategy->getAssociatedSubEntity(handle, ModelStrategy::R_TimeSignatures), q); - trackList = new TrackList(strategy->getAssociatedSubEntity(handle, ModelStrategy::R_Children), q); + tracks = new TrackList(strategy->getAssociatedSubEntity(handle, ModelStrategy::R_Children), q); workspace = new Workspace(strategy->getAssociatedSubEntity(handle, ModelStrategy::R_Workspace), q); } void ModelPrivate::handleNotifications() { Q_Q(Model); + QObject::connect(strategy, &ModelStrategy::destroyEntityNotified, q, [=, this](Handle handle) { + handleEntityDestroyed(handle); + }); QObject::connect(strategy, &ModelStrategy::insertIntoSequenceContainerNotified, q, [=, this](Handle sequenceContainerEntity, Handle entity) { if (auto sequenceContainerObject = mapToObject(sequenceContainerEntity)) { sequenceContainerObject->handleInsertIntoSequenceContainer(entity); } }); - + QObject::connect(strategy, &ModelStrategy::insertIntoListContainerNotified, q, [=, this](Handle listContainerEntity, Handle entity, int index) { if (auto listContainerObject = mapToObject(listContainerEntity)) { listContainerObject->handleInsertIntoListContainer(entity, index); } }); - + QObject::connect(strategy, &ModelStrategy::insertIntoMapContainerNotified, q, [=, this](Handle mapContainerEntity, Handle entity, const QString &key) { if (auto mapContainerObject = mapToObject(mapContainerEntity)) { mapContainerObject->handleInsertIntoMapContainer(entity, key); } }); - + + 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); } }); - + QObject::connect(strategy, &ModelStrategy::takeFromListContainerNotified, q, [=, this](Handle takenEntity, Handle listContainerEntity, int index) { if (auto listContainerObject = mapToObject(listContainerEntity)) { listContainerObject->handleTakeFromListContainer(takenEntity, index); } }); - + QObject::connect(strategy, &ModelStrategy::takeFromMapContainerNotified, q, [=, this](Handle takenEntity, Handle mapContainerEntity, const QString &key) { if (auto mapContainerObject = mapToObject(mapContainerEntity)) { mapContainerObject->handleTakeFromMapContainer(takenEntity, key); } }); - + QObject::connect(strategy, &ModelStrategy::rotateListContainerNotified, q, [=, this](Handle listContainerEntity, int leftIndex, int middleIndex, int rightIndex) { if (auto listContainerObject = mapToObject(listContainerEntity)) { listContainerObject->handleRotateListContainer(leftIndex, middleIndex, rightIndex); } }); - + QObject::connect(strategy, &ModelStrategy::setEntityPropertyNotified, q, [=, this](Handle entity, ModelStrategy::Property property, const QVariant &value) { if (auto entityObject = mapToObject(entity)) { entityObject->handleSetEntityProperty(property, value); @@ -130,7 +142,7 @@ namespace dspx { }); } - EntityObject * ModelPrivate::mapToObject(Handle handle) const { + EntityObject *ModelPrivate::mapToObject(Handle handle) const { return objectMap.value(handle); } @@ -151,7 +163,7 @@ namespace dspx { } template <> - ParamCurve * ModelPrivate::createObject(Handle handle) { + ParamCurve *ModelPrivate::createObject(Handle handle) { switch (strategy->getEntityType(handle)) { case ModelStrategy::EI_ParamCurveAnchor: return createObject(handle); @@ -196,9 +208,9 @@ namespace dspx { return d->timeline; } - TrackList *Model::trackList() const { + TrackList *Model::tracks() const { Q_D(const Model); - return d->trackList; + return d->tracks; } Workspace *Model::workspace() const { @@ -207,16 +219,28 @@ namespace dspx { } QDspx::Model Model::toQDspx() const { - return { + QDspx::Model model = { .version = QDspx::Model::V1, .content = { .global = global()->toQDspx(), .master = master()->toQDspx(), .timeline = timeline()->toQDspx(), - .tracks = trackList()->toQDspx(), + .tracks = tracks()->toQDspx(), .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) { @@ -224,8 +248,31 @@ namespace dspx { d->global->fromQDspx(model.content.global); d->master->fromQDspx(model.content.master); d->timeline->fromQDspx(model.content.timeline); - d->trackList->fromQDspx(model.content.tracks); + 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() { @@ -306,12 +353,20 @@ namespace dspx { return d->createObject(handle); } - Source * Model::createSource() { + Source *Model::createSource() { Q_D(Model); auto handle = d->strategy->createEntity(ModelStrategy::EI_Source); return d->createObject(handle); } + void Model::destroyItem(EntityObject *object) { + Q_D(Model); + auto handle = object->handle(); + object->d_func()->handle = {}; + d->strategy->destroyEntity(handle); + object->deleteLater(); + } + void Model::handleSetEntityProperty(int property, const QVariant &value) { Q_D(Model); switch (property) { @@ -330,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); @@ -342,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(); } @@ -353,4 +420,4 @@ namespace dspx { } -#include "moc_Model.cpp" \ No newline at end of file +#include "moc_Model.cpp" diff --git a/src/libs/application/dspxmodel/src/Model.h b/src/libs/application/dspxmodel/src/Model.h index 7bf58239..02d762f2 100644 --- a/src/libs/application/dspxmodel/src/Model.h +++ b/src/libs/application/dspxmodel/src/Model.h @@ -44,7 +44,7 @@ namespace dspx { Q_PROPERTY(Global *global READ global CONSTANT) Q_PROPERTY(Master *master READ master CONSTANT) Q_PROPERTY(Timeline *timeline READ timeline CONSTANT) - Q_PROPERTY(TrackList *trackList READ trackList CONSTANT) + Q_PROPERTY(TrackList *tracks READ tracks CONSTANT) Q_PROPERTY(Workspace *workspace READ workspace CONSTANT) public: @@ -56,7 +56,7 @@ namespace dspx { Global *global() const; Master *master() const; Timeline *timeline() const; - TrackList *trackList() const; + TrackList *tracks() const; Workspace *workspace() const; QDspx::Model toQDspx() const; @@ -77,12 +77,13 @@ namespace dspx { Q_INVOKABLE Param *createParam(); Q_INVOKABLE Source *createSource(); + Q_INVOKABLE void destroyItem(EntityObject *object); + protected: void handleSetEntityProperty(int property, const QVariant &value) override; private: QScopedPointer d_ptr; - }; } diff --git a/src/libs/application/dspxmodel/src/ModelStrategy.h b/src/libs/application/dspxmodel/src/ModelStrategy.h index b41e6f16..dc70161d 100644 --- a/src/libs/application/dspxmodel/src/ModelStrategy.h +++ b/src/libs/application/dspxmodel/src/ModelStrategy.h @@ -1,6 +1,8 @@ #ifndef DIFFSCOPE_DSPX_MODEL_MODELSTRATEGY_H #define DIFFSCOPE_DSPX_MODEL_MODELSTRATEGY_H +#include + #include #include @@ -48,26 +50,33 @@ namespace dspx { EM_Params, EM_Sources, EM_Workspace, - }; enum Property { + P_AccidentalType, P_Author, P_CentShift, P_ClipStart, P_ClipLength, + P_ColorId, 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, @@ -103,14 +112,35 @@ namespace dspx { R_Workspace, }; + struct PropertySpec { + Property propertyType; + QMetaType::Type metaType; + bool (*validate)(const QVariant &value); + + PropertySpec(Property propertyType, QMetaType::Type metaType, bool (*validate)(const QVariant &value) = [](const QVariant &) { return true; }) + : propertyType(propertyType), metaType(metaType), validate(validate) { + } + }; + + static inline Entity getAssociatedSubEntityTypeFromEntityTypeAndRelationship(Entity entityType, Relationship relationship); + static inline std::vector getEntityPropertySpecsFromEntityType(Entity entityType); + static inline PropertySpec getPropertySpecFromEntityTypeAndPropertyType(Entity entityType, Property propertyType); + static inline bool isEntityTypeAndPropertyTypeCompatible(Entity entityType, Property propertyType); + virtual Handle createEntity(Entity entityType) = 0; virtual void destroyEntity(Handle entity) = 0; virtual Entity getEntityType(Handle entity) = 0; + virtual QList getEntitiesFromSequenceContainer(Handle sequenceContainerEntity) = 0; + virtual QList getEntitiesFromListContainer(Handle listContainerEntity) = 0; + virtual QList> getEntitiesFromMapContainer(Handle mapContainerEntity) = 0; + virtual bool insertIntoSequenceContainer(Handle sequenceContainerEntity, Handle entity) = 0; 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; @@ -134,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); @@ -143,9 +175,228 @@ namespace dspx { void spliceDataArrayNotified(Handle dataContainerEntity, int index, int length, const QVariantList &values); void rotateDataArrayNotified(Handle dataContainerEntity, int leftIndex, int middleIndex, int rightIndex); + }; + + ModelStrategy::Entity ModelStrategy::getAssociatedSubEntityTypeFromEntityTypeAndRelationship(Entity entityType, Relationship relationship) { + if (relationship == R_Children) { + if (entityType == EI_Global) { + return EL_Tracks; + } + if (entityType == EI_Track) { + return ES_Clips; + } + if (entityType == EI_SingingClip) { + return ES_Notes; + } + if (entityType == EI_ParamCurveAnchor) { + return ES_ParamCurveAnchorNodes; + } + if (entityType == EI_ParamCurveFree) { + return ED_ParamCurveFreeValues; + } + } else if (relationship == R_Labels) { + if (entityType == EI_Global) { + return ES_Labels; + } + } else if (relationship == R_ParamCurvesEdited || relationship == R_ParamCurvesOriginal || relationship == R_ParamCurvesTransform) { + if (entityType == EI_Param) { + return ES_ParamCurves; + } + } else if (relationship == R_Params) { + if (entityType == EI_SingingClip) { + return EM_Params; + } + } else if (relationship == R_PhonemesEdited || relationship == R_PhonemesOriginal) { + if (entityType == EI_Note) { + return EL_Phonemes; + } + } else if (relationship == R_Sources) { + if (entityType == EI_SingingClip) { + return EM_Sources; + } + } else if (relationship == R_Tempos) { + if (entityType == EI_Global) { + return ES_Tempos; + } + } else if (relationship == R_TimeSignatures) { + if (entityType == EI_Global) { + return ES_TimeSignatures; + } + } else if (relationship == R_VibratoPointsAmplitude || relationship == R_VibratoPointsFrequency) { + if (entityType == EI_Note) { + return ED_VibratoPoints; + } + } else if (relationship == R_Workspace) { + if (entityType == EI_Global || entityType == EI_Track || entityType == EI_AudioClip || entityType == EI_SingingClip || entityType == EI_Note) { + return EM_Workspace; + } + } + Q_UNREACHABLE(); + } + + std::vector ModelStrategy::getEntityPropertySpecsFromEntityType(Entity entityType) { + static auto validateCentShift = [](const QVariant &value) { + 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; + }; + static auto validateIntGreaterOrEqualZero = [](const QVariant &value) { + 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; + }; + static auto validateDoubleBetweenZeroAndOne = [](const QVariant &value) { + auto v = value.toDouble(); + return v >= 0 && v <= 1; + }; + static auto validateKeyNumber = [](const QVariant &value) { + auto v = value.toInt(); + return v >= 0 && v <= 127; + }; + static auto validateTempo = [](const QVariant &value) { + auto v = value.toDouble(); + return v >= 10 && v <= 1000; + }; + static auto validateTimeSignatureDenominator = [](const QVariant &value) { + auto v = value.toInt(); + return v == 1 || v == 2 || v == 4 || v == 8 || v == 16 || v == 32 || v == 64 || v == 128; + }; + static auto validateTimeSignatureNumerator = [](const QVariant &value) { + auto v = value.toInt(); + return v >= 1; + }; + switch (entityType) { + case EI_AudioClip: return { + {P_Name, QMetaType::QString}, + {P_ControlGain, QMetaType::Double}, + {P_ControlPan, QMetaType::Double, validatePan}, + {P_ControlMute, QMetaType::Bool}, + {P_Position, QMetaType::Int}, + {P_Length, QMetaType::Int, validateIntGreaterOrEqualZero}, + {P_ClipStart, QMetaType::Int, validateIntGreaterOrEqualZero}, + {P_ClipLength, QMetaType::Int, validateIntGreaterOrEqualZero}, + {P_Path, QMetaType::QString} + }; + case EI_Global: return { + {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_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}, + {P_Text, QMetaType::QString} + }; + case EI_Note: return { + {P_CentShift, QMetaType::Int, validateCentShift}, + {P_KeyNumber, QMetaType::Int, validateKeyNumber}, + {P_Language, QMetaType::QString}, + {P_Length, QMetaType::Int, validateIntGreaterOrEqualZero}, + {P_Text, QMetaType::QString}, + {P_Position, QMetaType::Int, validateIntGreaterOrEqualZero}, + {P_PronunciationOriginal, QMetaType::QString}, + {P_PronunciationEdited, QMetaType::QString}, + {P_VibratoAmplitude, QMetaType::Int, validateIntGreaterOrEqualZero}, + {P_VibratoEnd, QMetaType::Double, validateDoubleBetweenZeroAndOne}, + {P_VibratoFrequency, QMetaType::Double, validateDoubleGreaterOrEqualZero}, + {P_VibratoOffset, QMetaType::Int}, + {P_VibratoPhase, QMetaType::Double, validateDoubleBetweenZeroAndOne}, + {P_VibratoStart, QMetaType::Double, validateDoubleBetweenZeroAndOne} + }; + case EI_Param: return {}; + case EI_ParamCurveAnchor: return { + {P_Position, QMetaType::Int} + }; + case EI_ParamCurveFree: return { + {P_Position, QMetaType::Int} + }; + case EI_ParamCurveAnchorNode: return { + {P_Type, QMetaType::Int}, + {P_Position, QMetaType::Int, validateIntGreaterOrEqualZero}, + {P_Value, QMetaType::Int} + }; + case EI_Phoneme: return { + {P_Language, QMetaType::QString}, + {P_Position, QMetaType::Int}, + {P_Text, QMetaType::QString}, + {P_Onset, QMetaType::Bool} + }; + case EI_SingingClip: return { + {P_Name, QMetaType::QString}, + {P_ControlGain, QMetaType::Double}, + {P_ControlPan, QMetaType::Double, validatePan}, + {P_ControlMute, QMetaType::Bool}, + {P_Position, QMetaType::Int}, + {P_Length, QMetaType::Int, validateIntGreaterOrEqualZero}, + {P_ClipStart, QMetaType::Int, validateIntGreaterOrEqualZero}, + {P_ClipLength, QMetaType::Int, validateIntGreaterOrEqualZero} + }; + case EI_Source: return { + {P_JsonObject, QMetaType::QJsonObject} + }; + case EI_Tempo: return { + {P_Position, QMetaType::Int, validateIntGreaterOrEqualZero}, + {P_Value, QMetaType::Double, validateTempo} + }; + case EI_TimeSignature: return { + {P_Measure, QMetaType::Int, validateIntGreaterOrEqualZero}, + {P_Numerator, QMetaType::Int, validateTimeSignatureNumerator}, + {P_Denominator, QMetaType::Int, validateTimeSignatureDenominator} + }; + case EI_Track: return { + {P_Name, QMetaType::QString}, + {P_ColorId, QMetaType::Int}, + {P_ControlGain, QMetaType::Double}, + {P_ControlPan, QMetaType::Double, validatePan}, + {P_ControlMute, QMetaType::Bool}, + {P_ControlRecord, QMetaType::Bool}, + {P_ControlSolo, QMetaType::Bool}, + {P_Height, QMetaType::Double, validateDoubleGreaterOrEqualZero} + }; + case EI_WorkspaceInfo: return { + {P_JsonObject, QMetaType::QJsonObject} + }; + default: Q_UNREACHABLE(); + } + } + + inline ModelStrategy::PropertySpec ModelStrategy::getPropertySpecFromEntityTypeAndPropertyType(Entity entityType, Property propertyType) { + auto v = getEntityPropertySpecsFromEntityType(entityType); + auto it = std::ranges::find_if(v, [propertyType](const auto &spec) { return spec.propertyType == propertyType; }); + if (it == v.end()) { + Q_UNREACHABLE(); + } + return *it; + } + + inline bool ModelStrategy::isEntityTypeAndPropertyTypeCompatible(Entity entityType, Property propertyType) { + return std::ranges::any_of(getEntityPropertySpecsFromEntityType(entityType), [propertyType](const auto &spec) { return spec.propertyType == propertyType; }); + } - }; } diff --git a/src/libs/application/dspxmodel/src/Model_p.h b/src/libs/application/dspxmodel/src/Model_p.h index 0f3c6046..b2fa0210 100644 --- a/src/libs/application/dspxmodel/src/Model_p.h +++ b/src/libs/application/dspxmodel/src/Model_p.h @@ -26,7 +26,7 @@ namespace dspx { LabelSequence *labels; TempoSequence *tempos; TimeSignatureSequence *timeSignatures; - TrackList *trackList; + TrackList *tracks; Workspace *workspace; QHash objectMap; @@ -35,6 +35,7 @@ namespace dspx { QString name; QString author; int centShift; + int accidentalType; QString editorId; QString editorName; @@ -49,6 +50,12 @@ namespace dspx { EntityObject *mapToObject(Handle handle) const; Handle mapToHandle(EntityObject *object) const; + template + T *createObject(S *superItem, Handle handle) { + Q_Q(Model); + return new T(superItem, handle, q); + } + template T *createObject(Handle handle) { Q_Q(Model); @@ -61,11 +68,10 @@ namespace dspx { template <> ParamCurve *createObject(Handle handle); - template - static void proxySetEntityPropertyNotify(T* object, int property, const QVariant &value) { + template + static void proxySetEntityPropertyNotify(T *object, int property, const QVariant &value) { object->handleProxySetEntityProperty(property, value); } - }; } diff --git a/src/libs/application/dspxmodel/src/Note.cpp b/src/libs/application/dspxmodel/src/Note.cpp index 705c680b..0ae9ea6f 100644 --- a/src/libs/application/dspxmodel/src/Note.cpp +++ b/src/libs/application/dspxmodel/src/Note.cpp @@ -1,103 +1,35 @@ #include "Note.h" - +#include "Note_p.h" #include -#include #include -#include #include #include #include +#include #include #include -#include +#include namespace dspx { - class NotePrivate { - Q_DECLARE_PUBLIC(Note) - public: - Note *q_ptr; - ModelPrivate *pModel; - - int centShift; - int keyNum; - QString language; - int length; - QString lyric; - PhonemeInfo *phonemes; - int pos; - Pronunciation *pronunciation; - Vibrato *vibrato; - Workspace *workspace; - SingingClip *singingClip{}; - 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_); - }; - - 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; + void NotePrivate::setOverlapped(Note *item, bool overlapped) { + auto d = item->d_func(); + if (d->overlapped != overlapped) { + d->overlapped = overlapped; + Q_EMIT item->overlappedChanged(overlapped); } - 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; + void NotePrivate::setNoteSequence(Note *item, NoteSequence *noteSequence) { + auto d = item->d_func(); + if (d->noteSequence != noteSequence) { + d->noteSequence = noteSequence; + Q_EMIT item->noteSequenceChanged(); } - 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_); } Note::Note(Handle handle, Model *model) : EntityObject(handle, model), d_ptr(new NotePrivate) { @@ -111,7 +43,7 @@ namespace dspx { d->length = d->pModel->strategy->getEntityProperty(handle, ModelStrategy::P_Length).toInt(); d->lyric = d->pModel->strategy->getEntityProperty(handle, ModelStrategy::P_Text).toString(); d->pos = d->pModel->strategy->getEntityProperty(handle, ModelStrategy::P_Position).toInt(); - d->phonemes = d->pModel->createObject(handle); + d->phonemes = d->pModel->createObject(this, handle); d->pronunciation = d->pModel->createObject(handle); d->vibrato = d->pModel->createObject(handle); d->workspace = d->pModel->createObject(d->pModel->strategy->getAssociatedSubEntity(handle, ModelStrategy::R_Workspace)); @@ -127,7 +59,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 { @@ -138,7 +70,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 { @@ -159,7 +91,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 { @@ -185,7 +117,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 { @@ -203,9 +135,9 @@ namespace dspx { return d->workspace; } - SingingClip *Note::singingClip() const { + NoteSequence *Note::noteSequence() const { Q_D(const Note); - return d->singingClip; + return d->noteSequence; } bool Note::isOverlapped() const { @@ -293,18 +225,6 @@ namespace dspx { } } - void Note::setSingingClip(SingingClip *singingClip) { - Q_D(Note); - d->singingClip = singingClip; - Q_EMIT singingClipChanged(); - } - - void Note::setOverlapped(bool overlapped) { - Q_D(Note); - d->overlapped = overlapped; - Q_EMIT overlappedChanged(overlapped); - } - } -#include "moc_Note.cpp" \ No newline at end of file +#include "moc_Note.cpp" diff --git a/src/libs/application/dspxmodel/src/Note.h b/src/libs/application/dspxmodel/src/Note.h index b90dbcaf..b43be8c3 100644 --- a/src/libs/application/dspxmodel/src/Note.h +++ b/src/libs/application/dspxmodel/src/Note.h @@ -15,7 +15,7 @@ namespace dspx { class Pronunciation; class Vibrato; class Workspace; - class SingingClip; + class NoteSequence; class NoteSequencePrivate; @@ -26,17 +26,17 @@ 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(SingingClip *singingClip READ singingClip NOTIFY singingClipChanged) + Q_PROPERTY(NoteSequence *noteSequence READ noteSequence NOTIFY noteSequenceChanged) Q_PROPERTY(bool overlapped READ isOverlapped NOTIFY overlappedChanged) public: @@ -68,7 +68,7 @@ namespace dspx { Workspace *workspace() const; - SingingClip *singingClip() const; + NoteSequence *noteSequence() const; bool isOverlapped() const; @@ -82,7 +82,7 @@ namespace dspx { void lengthChanged(int length); void lyricChanged(const QString &lyric); void posChanged(int pos); - void singingClipChanged(); + void noteSequenceChanged(); void overlappedChanged(bool overlapped); protected: @@ -90,13 +90,10 @@ namespace dspx { private: friend class ModelPrivate; - friend class NoteSequencePrivate; explicit Note(Handle handle, Model *model); QScopedPointer d_ptr; - void setSingingClip(SingingClip *singingClip); - void setOverlapped(bool overlapped); }; } -#endif //DIFFSCOPE_DSPX_MODEL_NOTE_H \ No newline at end of file +#endif //DIFFSCOPE_DSPX_MODEL_NOTE_H diff --git a/src/libs/application/dspxmodel/src/NoteSequence.cpp b/src/libs/application/dspxmodel/src/NoteSequence.cpp index 0ed68074..416cabb0 100644 --- a/src/libs/application/dspxmodel/src/NoteSequence.cpp +++ b/src/libs/application/dspxmodel/src/NoteSequence.cpp @@ -1,50 +1,27 @@ #include "NoteSequence.h" +#include "NoteSequence_p.h" -#include #include +#include #include -#include -#include -#include -#include +#include #include #include -#include +#include #include namespace dspx { - static void setNoteOverlapped(Note *item, bool overlapped); - - class NoteSequencePrivate : public RangeSequenceData { - Q_DECLARE_PUBLIC(NoteSequence) - public: - SingingClip *singingClip{}; - static void setOverlapped(Note *item, bool overlapped) { - item->setOverlapped(overlapped); - } - static void setSingingClip(Note *item, SingingClip *singingClip) { - item->setSingingClip(singingClip); - } - }; - - void setNoteOverlapped(Note *item, bool overlapped) { - NoteSequencePrivate::setOverlapped(item, overlapped); - } - - NoteSequence::NoteSequence(Handle handle, Model *model) : EntityObject(handle, model), d_ptr(new NoteSequencePrivate) { + NoteSequence::NoteSequence(SingingClip *singingClip, Handle handle, Model *model) : EntityObject(handle, model), d_ptr(new NoteSequencePrivate) { Q_D(NoteSequence); Q_ASSERT(model->strategy()->getEntityType(handle) == ModelStrategy::ES_Notes); d->q_ptr = this; d->pModel = ModelPrivate::get(model); - connect(this, &NoteSequence::itemInserted, this, [=](Note *item) { - NoteSequencePrivate::setSingingClip(item, d->singingClip); - }); - connect(this, &NoteSequence::itemRemoved, this, [=](Note *item) { - NoteSequencePrivate::setSingingClip(item, nullptr); - }); + d->singingClip = singingClip; + + d->init(model->strategy()->getEntitiesFromSequenceContainer(handle)); } NoteSequence::~NoteSequence() = default; @@ -119,11 +96,6 @@ namespace dspx { } } - void NoteSequence::setSingingClip(SingingClip *singingClip) { - Q_D(NoteSequence); - d->singingClip = singingClip; - } - void NoteSequence::handleInsertIntoSequenceContainer(Handle entity) { Q_D(NoteSequence); d->handleInsertIntoSequenceContainer(entity); @@ -136,4 +108,4 @@ namespace dspx { } -#include "moc_NoteSequence.cpp" \ No newline at end of file +#include "moc_NoteSequence.cpp" diff --git a/src/libs/application/dspxmodel/src/NoteSequence.h b/src/libs/application/dspxmodel/src/NoteSequence.h index 15a0b77b..211c09ee 100644 --- a/src/libs/application/dspxmodel/src/NoteSequence.h +++ b/src/libs/application/dspxmodel/src/NoteSequence.h @@ -4,6 +4,7 @@ #include #include +#include namespace QDspx { struct Note; @@ -46,6 +47,10 @@ namespace dspx { SingingClip *singingClip() const; + auto asRange() const { + return impl::SequenceRange(this); + } + Q_SIGNALS: void itemAboutToInsert(Note *item); void itemInserted(Note *item); @@ -61,12 +66,10 @@ namespace dspx { private: friend class ModelPrivate; - friend class SingingClip; - explicit NoteSequence(Handle handle, Model *model); - void setSingingClip(SingingClip *singingClip); + explicit NoteSequence(SingingClip *singingClip, Handle handle, Model *model); QScopedPointer d_ptr; }; } -#endif //DIFFSCOPE_DSPX_MODEL_NOTESEQUENCE_H \ No newline at end of file +#endif //DIFFSCOPE_DSPX_MODEL_NOTESEQUENCE_H diff --git a/src/libs/application/dspxmodel/src/NoteSequence_p.h b/src/libs/application/dspxmodel/src/NoteSequence_p.h new file mode 100644 index 00000000..12479c9d --- /dev/null +++ b/src/libs/application/dspxmodel/src/NoteSequence_p.h @@ -0,0 +1,19 @@ +#ifndef DIFFSCOPE_DSPX_MODEL_NOTESEQUENCE_P_H +#define DIFFSCOPE_DSPX_MODEL_NOTESEQUENCE_P_H + +#include + +#include +#include + +namespace dspx { + + class NoteSequencePrivate : public RangeSequenceData { + Q_DECLARE_PUBLIC(NoteSequence) + public: + SingingClip *singingClip{}; + }; + +} + +#endif //DIFFSCOPE_DSPX_MODEL_NOTESEQUENCE_P_H \ No newline at end of file diff --git a/src/libs/application/dspxmodel/src/Note_p.h b/src/libs/application/dspxmodel/src/Note_p.h new file mode 100644 index 00000000..af4a0be1 --- /dev/null +++ b/src/libs/application/dspxmodel/src/Note_p.h @@ -0,0 +1,35 @@ +#ifndef DIFFSCOPE_DSPX_MODEL_NOTE_P_H +#define DIFFSCOPE_DSPX_MODEL_NOTE_P_H + +#include + +namespace dspx { + + class NotePrivate { + Q_DECLARE_PUBLIC(Note) + public: + Note *q_ptr; + ModelPrivate *pModel; + + int centShift; + int keyNum; + QString language; + int length; + QString lyric; + PhonemeInfo *phonemes; + int pos; + Pronunciation *pronunciation; + Vibrato *vibrato; + Workspace *workspace; + NoteSequence *noteSequence{}; + bool overlapped{}; + + + + static void setOverlapped(Note *item, bool overlapped); + static void setNoteSequence(Note *item, NoteSequence *noteSequence); + }; + +} + +#endif //DIFFSCOPE_DSPX_MODEL_NOTE_P_H \ No newline at end of file diff --git a/src/libs/application/dspxmodel/src/Param.cpp b/src/libs/application/dspxmodel/src/Param.cpp index a33fc98b..b81c7f12 100644 --- a/src/libs/application/dspxmodel/src/Param.cpp +++ b/src/libs/application/dspxmodel/src/Param.cpp @@ -1,32 +1,33 @@ #include "Param.h" +#include "Param_p.h" #include -#include #include #include +#include +#include namespace dspx { - class ParamPrivate { - Q_DECLARE_PUBLIC(Param) - public: - Param *q_ptr; - ModelPrivate *pModel; - ParamCurveSequence *original; - ParamCurveSequence *transform; - ParamCurveSequence *edited; - }; + void ParamPrivate::setParamMap(Param *item, ParamMap *paramMap) { + auto d = item->d_func(); + if (d->paramMap != paramMap) { + d->paramMap = paramMap; + Q_EMIT item->paramMapChanged(); + } + } Param::Param(Handle handle, Model *model) : EntityObject(handle, model), d_ptr(new ParamPrivate) { Q_D(Param); Q_ASSERT(model->strategy()->getEntityType(handle) == ModelStrategy::EI_Param); d->q_ptr = this; d->pModel = ModelPrivate::get(model); + d->paramMap = nullptr; - d->original = d->pModel->createObject(d->pModel->strategy->getAssociatedSubEntity(handle, ModelStrategy::R_ParamCurvesOriginal)); - d->transform = d->pModel->createObject(d->pModel->strategy->getAssociatedSubEntity(handle, ModelStrategy::R_ParamCurvesTransform)); - d->edited = d->pModel->createObject(d->pModel->strategy->getAssociatedSubEntity(handle, ModelStrategy::R_ParamCurvesEdited)); + d->original = d->pModel->createObject(this, d->pModel->strategy->getAssociatedSubEntity(handle, ModelStrategy::R_ParamCurvesOriginal)); + d->transform = d->pModel->createObject(this, d->pModel->strategy->getAssociatedSubEntity(handle, ModelStrategy::R_ParamCurvesTransform)); + d->edited = d->pModel->createObject(this, d->pModel->strategy->getAssociatedSubEntity(handle, ModelStrategy::R_ParamCurvesEdited)); } Param::~Param() = default; @@ -48,7 +49,7 @@ namespace dspx { QDspx::Param Param::toQDspx() const { Q_D(const Param); - return QDspx::Param{ + return { .original = d->original->toQDspx(), .transform = d->transform->toQDspx(), .edited = d->edited->toQDspx(), @@ -62,6 +63,11 @@ namespace dspx { d->edited->fromQDspx(param.edited); } + ParamMap *Param::paramMap() const { + Q_D(const Param); + return d->paramMap; + } + } -#include "moc_Param.cpp" \ No newline at end of file +#include "moc_Param.cpp" diff --git a/src/libs/application/dspxmodel/src/Param.h b/src/libs/application/dspxmodel/src/Param.h index ab6431af..dbb04407 100644 --- a/src/libs/application/dspxmodel/src/Param.h +++ b/src/libs/application/dspxmodel/src/Param.h @@ -12,6 +12,7 @@ namespace QDspx { namespace dspx { class ParamCurveSequence; + class ParamMap; class ParamPrivate; class DSPX_MODEL_EXPORT Param : public EntityObject { @@ -22,6 +23,7 @@ namespace dspx { Q_PROPERTY(ParamCurveSequence *original READ original CONSTANT) Q_PROPERTY(ParamCurveSequence *transform READ transform CONSTANT) Q_PROPERTY(ParamCurveSequence *edited READ edited CONSTANT) + Q_PROPERTY(ParamMap *paramMap READ paramMap CONSTANT) public: ~Param() override; @@ -30,9 +32,14 @@ namespace dspx { ParamCurveSequence *transform() const; ParamCurveSequence *edited() const; + ParamMap *paramMap() const; + QDspx::Param toQDspx() const; void fromQDspx(const QDspx::Param ¶m); + Q_SIGNALS: + void paramMapChanged(); + private: friend class ModelPrivate; explicit Param(Handle handle, Model *model); @@ -41,4 +48,4 @@ namespace dspx { } -#endif //DIFFSCOPE_DSPX_MODEL_PARAM_H \ No newline at end of file +#endif //DIFFSCOPE_DSPX_MODEL_PARAM_H diff --git a/src/libs/application/dspxmodel/src/ParamCurve.cpp b/src/libs/application/dspxmodel/src/ParamCurve.cpp index 81af54bf..1da88780 100644 --- a/src/libs/application/dspxmodel/src/ParamCurve.cpp +++ b/src/libs/application/dspxmodel/src/ParamCurve.cpp @@ -1,24 +1,26 @@ #include "ParamCurve.h" +#include "ParamCurve_p.h" #include #include #include -#include #include #include #include +#include +#include +#include namespace dspx { - class ParamCurvePrivate { - Q_DECLARE_PUBLIC(ParamCurve) - public: - ParamCurve *q_ptr; - ModelPrivate *pModel; - ParamCurve::CurveType type; - int start{}; - }; + void ParamCurvePrivate::setParamCurveSequence(ParamCurve *paramCurve, ParamCurveSequence *paramCurveSequence) { + auto d = paramCurve->d_func(); + if (d->paramCurveSequence == paramCurveSequence) + return; + d->paramCurveSequence = paramCurveSequence; + Q_EMIT paramCurve->paramCurveSequenceChanged(); + } ParamCurve::ParamCurve(CurveType type, Handle handle, Model *model) : EntityObject(handle, model), d_ptr(new ParamCurvePrivate) { @@ -26,6 +28,7 @@ namespace dspx { d->q_ptr = this; d->pModel = ModelPrivate::get(model); d->type = type; + d->paramCurveSequence = nullptr; } ParamCurve::~ParamCurve() = default; @@ -45,6 +48,11 @@ namespace dspx { return d->type; } + ParamCurveSequence *ParamCurve::paramCurveSequence() const { + Q_D(const ParamCurve); + return d->paramCurveSequence; + } + QDspx::ParamCurveRef ParamCurve::toQDspx() const { Q_D(const ParamCurve); switch (d->type) { @@ -85,4 +93,4 @@ namespace dspx { } -#include "moc_ParamCurve.cpp" \ No newline at end of file +#include "moc_ParamCurve.cpp" diff --git a/src/libs/application/dspxmodel/src/ParamCurve.h b/src/libs/application/dspxmodel/src/ParamCurve.h index ee716e6b..7cff8619 100644 --- a/src/libs/application/dspxmodel/src/ParamCurve.h +++ b/src/libs/application/dspxmodel/src/ParamCurve.h @@ -12,6 +12,7 @@ namespace QDspx { namespace dspx { + class ParamCurveSequence; class ParamCurvePrivate; class DSPX_MODEL_EXPORT ParamCurve : public EntityObject { @@ -21,6 +22,7 @@ namespace dspx { Q_DECLARE_PRIVATE(ParamCurve) 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) public: enum CurveType { @@ -36,11 +38,14 @@ namespace dspx { CurveType type() const; + ParamCurveSequence *paramCurveSequence() const; + QDspx::ParamCurveRef toQDspx() const; void fromQDspx(const QDspx::ParamCurveRef &curve); Q_SIGNALS: void startChanged(int start); + void paramCurveSequenceChanged(); protected: explicit ParamCurve(CurveType type, Handle handle, Model *model); @@ -53,4 +58,4 @@ namespace dspx { } -#endif //DIFFSCOPE_DSPX_MODEL_PARAMCURVE_H \ No newline at end of file +#endif //DIFFSCOPE_DSPX_MODEL_PARAMCURVE_H diff --git a/src/libs/application/dspxmodel/src/ParamCurveAnchor.cpp b/src/libs/application/dspxmodel/src/ParamCurveAnchor.cpp index cb8d3b4e..7e2bd226 100644 --- a/src/libs/application/dspxmodel/src/ParamCurveAnchor.cpp +++ b/src/libs/application/dspxmodel/src/ParamCurveAnchor.cpp @@ -2,9 +2,9 @@ #include -#include -#include #include +#include +#include namespace dspx { @@ -23,7 +23,7 @@ namespace dspx { d->q_ptr = this; d->pModel = ModelPrivate::get(model); - d->nodes = d->pModel->createObject(d->pModel->strategy->getAssociatedSubEntity(handle, ModelStrategy::R_Children)); + d->nodes = d->pModel->createObject(this, d->pModel->strategy->getAssociatedSubEntity(handle, ModelStrategy::R_Children)); } ParamCurveAnchor::~ParamCurveAnchor() = default; @@ -51,4 +51,4 @@ namespace dspx { } -#include "moc_ParamCurveAnchor.cpp" \ No newline at end of file +#include "moc_ParamCurveAnchor.cpp" diff --git a/src/libs/application/dspxmodel/src/ParamCurveAnchor.h b/src/libs/application/dspxmodel/src/ParamCurveAnchor.h index af87c233..a315e057 100644 --- a/src/libs/application/dspxmodel/src/ParamCurveAnchor.h +++ b/src/libs/application/dspxmodel/src/ParamCurveAnchor.h @@ -36,9 +36,8 @@ namespace dspx { friend class ModelPrivate; explicit ParamCurveAnchor(Handle handle, Model *model); QScopedPointer d_ptr; - }; } -#endif //DIFFSCOPE_DSPX_MODEL_PARAMCURVEANCHOR_H \ No newline at end of file +#endif //DIFFSCOPE_DSPX_MODEL_PARAMCURVEANCHOR_H diff --git a/src/libs/application/dspxmodel/src/ParamCurveFree.cpp b/src/libs/application/dspxmodel/src/ParamCurveFree.cpp index 79271eed..ed94fdb1 100644 --- a/src/libs/application/dspxmodel/src/ParamCurveFree.cpp +++ b/src/libs/application/dspxmodel/src/ParamCurveFree.cpp @@ -2,9 +2,9 @@ #include -#include -#include #include +#include +#include namespace dspx { @@ -23,7 +23,7 @@ namespace dspx { d->q_ptr = this; d->pModel = ModelPrivate::get(model); - d->values = d->pModel->createObject(d->pModel->strategy->getAssociatedSubEntity(handle, ModelStrategy::R_Children)); + d->values = d->pModel->createObject(this, d->pModel->strategy->getAssociatedSubEntity(handle, ModelStrategy::R_Children)); } ParamCurveFree::~ParamCurveFree() = default; @@ -58,4 +58,4 @@ namespace dspx { } -#include "moc_ParamCurveFree.cpp" \ No newline at end of file +#include "moc_ParamCurveFree.cpp" diff --git a/src/libs/application/dspxmodel/src/ParamCurveFree.h b/src/libs/application/dspxmodel/src/ParamCurveFree.h index a82bd1fe..9561ef1b 100644 --- a/src/libs/application/dspxmodel/src/ParamCurveFree.h +++ b/src/libs/application/dspxmodel/src/ParamCurveFree.h @@ -38,9 +38,8 @@ namespace dspx { friend class ModelPrivate; explicit ParamCurveFree(Handle handle, Model *model); QScopedPointer d_ptr; - }; } -#endif //DIFFSCOPE_DSPX_MODEL_PARAMCURVEFREE_H \ No newline at end of file +#endif //DIFFSCOPE_DSPX_MODEL_PARAMCURVEFREE_H diff --git a/src/libs/application/dspxmodel/src/ParamCurveSequence.cpp b/src/libs/application/dspxmodel/src/ParamCurveSequence.cpp index 6134bcb7..cc34ff57 100644 --- a/src/libs/application/dspxmodel/src/ParamCurveSequence.cpp +++ b/src/libs/application/dspxmodel/src/ParamCurveSequence.cpp @@ -1,31 +1,32 @@ #include "ParamCurveSequence.h" +#include "ParamCurveSequence_p.h" -#include #include +#include #include #include #include -#include -#include -#include #include -#include +#include +#include #include #include +#include +#include +#include namespace dspx { - class ParamCurveSequencePrivate : public PointSequenceData { - Q_DECLARE_PUBLIC(ParamCurveSequence) - }; - - ParamCurveSequence::ParamCurveSequence(Handle handle, Model *model) : EntityObject(handle, model), d_ptr(new ParamCurveSequencePrivate) { + ParamCurveSequence::ParamCurveSequence(Param *param, Handle handle, Model *model) : EntityObject(handle, model), d_ptr(new ParamCurveSequencePrivate) { Q_D(ParamCurveSequence); Q_ASSERT(model->strategy()->getEntityType(handle) == ModelStrategy::ES_ParamCurves); d->q_ptr = this; d->pModel = ModelPrivate::get(model); + d->param = param; + + d->init(model->strategy()->getEntitiesFromSequenceContainer(handle)); } ParamCurveSequence::~ParamCurveSequence() = default; @@ -116,6 +117,11 @@ namespace dspx { d->handleTakeFromSequenceContainer(takenEntity, entity); } + Param *ParamCurveSequence::param() const { + Q_D(const ParamCurveSequence); + return d->param; + } + } -#include "moc_ParamCurveSequence.cpp" \ No newline at end of file +#include "moc_ParamCurveSequence.cpp" diff --git a/src/libs/application/dspxmodel/src/ParamCurveSequence.h b/src/libs/application/dspxmodel/src/ParamCurveSequence.h index 93d3b989..b19c69f8 100644 --- a/src/libs/application/dspxmodel/src/ParamCurveSequence.h +++ b/src/libs/application/dspxmodel/src/ParamCurveSequence.h @@ -4,6 +4,7 @@ #include #include +#include namespace QDspx { struct ParamCurve; @@ -13,6 +14,7 @@ namespace QDspx { namespace dspx { class ParamCurve; + class Param; class ParamCurveSequencePrivate; @@ -24,6 +26,7 @@ namespace dspx { Q_PROPERTY(int size READ size NOTIFY sizeChanged) Q_PROPERTY(ParamCurve *firstItem READ firstItem NOTIFY firstItemChanged) Q_PROPERTY(ParamCurve *lastItem READ lastItem NOTIFY lastItemChanged) + Q_PROPERTY(Param *param READ param CONSTANT) Q_PRIVATE_PROPERTY(d_func(), QJSValue iterable READ iterable CONSTANT) public: ~ParamCurveSequence() override; @@ -42,6 +45,12 @@ namespace dspx { QList toQDspx() const; void fromQDspx(const QList &curves); + Param *param() const; + + auto asRange() const { + return impl::SequenceRange(this); + } + Q_SIGNALS: void itemAboutToInsert(ParamCurve *item); void itemInserted(ParamCurve *item); @@ -57,10 +66,10 @@ namespace dspx { private: friend class ModelPrivate; - explicit ParamCurveSequence(Handle handle, Model *model); + explicit ParamCurveSequence(Param *param, Handle handle, Model *model); QScopedPointer d_ptr; }; } -#endif //DIFFSCOPE_DSPX_MODEL_PARAMCURVESEQUENCE_H \ No newline at end of file +#endif //DIFFSCOPE_DSPX_MODEL_PARAMCURVESEQUENCE_H diff --git a/src/libs/application/dspxmodel/src/ParamCurveSequence_p.h b/src/libs/application/dspxmodel/src/ParamCurveSequence_p.h new file mode 100644 index 00000000..259a2b69 --- /dev/null +++ b/src/libs/application/dspxmodel/src/ParamCurveSequence_p.h @@ -0,0 +1,19 @@ +#ifndef DIFFSCOPE_DSPX_MODEL_PARAMCURVESEQUENCE_P_H +#define DIFFSCOPE_DSPX_MODEL_PARAMCURVESEQUENCE_P_H + +#include + +#include +#include + +namespace dspx { + + class ParamCurveSequencePrivate : public PointSequenceData { + Q_DECLARE_PUBLIC(ParamCurveSequence) + public: + Param *param{}; + }; + +} + +#endif //DIFFSCOPE_DSPX_MODEL_PARAMCURVESEQUENCE_P_H \ No newline at end of file diff --git a/src/libs/application/dspxmodel/src/ParamCurve_p.h b/src/libs/application/dspxmodel/src/ParamCurve_p.h new file mode 100644 index 00000000..f8478651 --- /dev/null +++ b/src/libs/application/dspxmodel/src/ParamCurve_p.h @@ -0,0 +1,25 @@ +#ifndef DIFFSCOPE_DSPX_MODEL_PARAMCURVE_P_H +#define DIFFSCOPE_DSPX_MODEL_PARAMCURVE_P_H + +#include + +namespace dspx { + + class ParamCurveSequence; + class ModelPrivate; + + class ParamCurvePrivate { + Q_DECLARE_PUBLIC(ParamCurve) + public: + ParamCurve *q_ptr; + ModelPrivate *pModel; + ParamCurve::CurveType type; + int start{}; + ParamCurveSequence *paramCurveSequence{}; + + static void setParamCurveSequence(ParamCurve *item, ParamCurveSequence *paramCurveSequence); + }; + +} + +#endif //DIFFSCOPE_DSPX_MODEL_PARAMCURVE_P_H \ No newline at end of file diff --git a/src/libs/application/dspxmodel/src/ParamMap.cpp b/src/libs/application/dspxmodel/src/ParamMap.cpp index a5a78e38..e9537f23 100644 --- a/src/libs/application/dspxmodel/src/ParamMap.cpp +++ b/src/libs/application/dspxmodel/src/ParamMap.cpp @@ -1,24 +1,37 @@ -#include "ParamMap.h" - #include "ModelStrategy.h" +#include "ParamMap.h" #include -#include -#include #include +#include +#include +#include +#include namespace dspx { class ParamMapPrivate : public MapData { Q_DECLARE_PUBLIC(ParamMap) + public: + SingingClip *singingClip; }; - ParamMap::ParamMap(Handle handle, Model *model) : EntityObject(handle, model), d_ptr(new ParamMapPrivate) { + ParamMap::ParamMap(SingingClip *singingClip, Handle handle, Model *model) : EntityObject(handle, model), d_ptr(new ParamMapPrivate) { Q_D(ParamMap); Q_ASSERT(model->strategy()->getEntityType(handle) == ModelStrategy::EM_Params); d->q_ptr = this; d->pModel = ModelPrivate::get(model); + d->singingClip = singingClip; + + d->init(model->strategy()->getEntitiesFromMapContainer(handle)); + + connect(this, &ParamMap::itemInserted, this, [this](const QString &, Param *item) { + ParamPrivate::setParamMap(item, this); + }); + connect(this, &ParamMap::itemRemoved, this, [this](const QString &, Param *item) { + ParamPrivate::setParamMap(item, nullptr); + }); } ParamMap::~ParamMap() = default; @@ -79,6 +92,11 @@ namespace dspx { } } + SingingClip *ParamMap::singingClip() const { + Q_D(const ParamMap); + return d->singingClip; + } + void ParamMap::handleInsertIntoMapContainer(Handle entity, const QString &key) { Q_D(ParamMap); d->handleInsertIntoMapContainer(entity, key); @@ -91,4 +109,4 @@ namespace dspx { } -#include "moc_ParamMap.cpp" \ No newline at end of file +#include "moc_ParamMap.cpp" diff --git a/src/libs/application/dspxmodel/src/ParamMap.h b/src/libs/application/dspxmodel/src/ParamMap.h index 29aa351f..0338d524 100644 --- a/src/libs/application/dspxmodel/src/ParamMap.h +++ b/src/libs/application/dspxmodel/src/ParamMap.h @@ -12,6 +12,7 @@ namespace QDspx { namespace dspx { + class SingingClip; class Param; class ParamMapPrivate; @@ -23,6 +24,7 @@ namespace dspx { Q_PROPERTY(int size READ size NOTIFY sizeChanged) Q_PROPERTY(QStringList keys READ keys NOTIFY keysChanged) Q_PROPERTY(QList items READ items NOTIFY itemsChanged) + Q_PROPERTY(SingingClip *singingClip READ singingClip CONSTANT) Q_PRIVATE_PROPERTY(d_func(), QJSValue iterable READ iterable CONSTANT) public: @@ -39,6 +41,8 @@ namespace dspx { QDspx::Params toQDspx() const; void fromQDspx(const QDspx::Params ¶mMap); + SingingClip *singingClip() const; + Q_SIGNALS: void itemAboutToInsert(const QString &key, Param *item); void itemInserted(const QString &key, Param *item); @@ -54,10 +58,10 @@ namespace dspx { private: friend class ModelPrivate; - explicit ParamMap(Handle handle, Model *model); + explicit ParamMap(SingingClip *singingClip, Handle handle, Model *model); QScopedPointer d_ptr; }; } -#endif //DIFFSCOPE_DSPX_MODEL_PARAMMAP_H \ No newline at end of file +#endif //DIFFSCOPE_DSPX_MODEL_PARAMMAP_H diff --git a/src/libs/application/dspxmodel/src/Param_p.h b/src/libs/application/dspxmodel/src/Param_p.h new file mode 100644 index 00000000..a179fe4f --- /dev/null +++ b/src/libs/application/dspxmodel/src/Param_p.h @@ -0,0 +1,26 @@ +#ifndef DIFFSCOPE_DSPX_MODEL_PARAM_P_H +#define DIFFSCOPE_DSPX_MODEL_PARAM_P_H + +#include + +namespace dspx { + + class ParamMap; + class ModelPrivate; + + class ParamPrivate { + Q_DECLARE_PUBLIC(Param) + public: + Param *q_ptr; + ModelPrivate *pModel; + ParamCurveSequence *original; + ParamCurveSequence *transform; + ParamCurveSequence *edited; + ParamMap *paramMap; + + static void setParamMap(Param *item, ParamMap *paramMap); + }; + +} + +#endif //DIFFSCOPE_DSPX_MODEL_PARAM_P_H \ No newline at end of file diff --git a/src/libs/application/dspxmodel/src/Phoneme.cpp b/src/libs/application/dspxmodel/src/Phoneme.cpp index 277145f2..32f6e792 100644 --- a/src/libs/application/dspxmodel/src/Phoneme.cpp +++ b/src/libs/application/dspxmodel/src/Phoneme.cpp @@ -1,29 +1,32 @@ #include "Phoneme.h" +#include "Phoneme_p.h" -#include #include +#include #include #include #include +#include +#include namespace dspx { - class PhonemePrivate { - Q_DECLARE_PUBLIC(Phoneme) - public: - Phoneme *q_ptr; - QString language; - int start; - QString token; - bool onset; - }; + void PhonemePrivate::setPhonemeList(Phoneme *item, PhonemeList *phonemeList) { + auto d = item->d_func(); + if (d->phonemeList != phonemeList) { + d->phonemeList = phonemeList; + Q_EMIT item->phonemeListChanged(); + } + } Phoneme::Phoneme(Handle handle, Model *model) : EntityObject(handle, model), d_ptr(new PhonemePrivate) { Q_D(Phoneme); Q_ASSERT(model->strategy()->getEntityType(handle) == ModelStrategy::EI_Phoneme); d->q_ptr = this; + d->pModel = ModelPrivate::get(model); + d->phonemeList = nullptr; d->language = model->strategy()->getEntityProperty(handle, ModelStrategy::P_Language).toString(); d->start = model->strategy()->getEntityProperty(handle, ModelStrategy::P_Position).toInt(); d->token = model->strategy()->getEntityProperty(handle, ModelStrategy::P_Text).toString(); @@ -116,6 +119,11 @@ namespace dspx { } } + PhonemeList *Phoneme::phonemeList() const { + Q_D(const Phoneme); + return d->phonemeList; + } + } -#include "moc_Phoneme.cpp" \ No newline at end of file +#include "moc_Phoneme.cpp" diff --git a/src/libs/application/dspxmodel/src/Phoneme.h b/src/libs/application/dspxmodel/src/Phoneme.h index 5798dc02..1698944c 100644 --- a/src/libs/application/dspxmodel/src/Phoneme.h +++ b/src/libs/application/dspxmodel/src/Phoneme.h @@ -1,15 +1,17 @@ #ifndef DIFFSCOPE_DSPX_MODEL_PHONEME_H #define DIFFSCOPE_DSPX_MODEL_PHONEME_H -#include #include +#include + namespace QDspx { struct Phoneme; } namespace dspx { + class PhonemeList; class PhonemePrivate; class DSPX_MODEL_EXPORT Phoneme : public EntityObject { @@ -21,6 +23,7 @@ namespace dspx { Q_PROPERTY(int start READ start WRITE setStart NOTIFY startChanged) Q_PROPERTY(QString token READ token WRITE setToken NOTIFY tokenChanged) Q_PROPERTY(bool onset READ onset WRITE setOnset NOTIFY onsetChanged) + Q_PROPERTY(PhonemeList *phonemeList READ phonemeList NOTIFY phonemeListChanged) public: ~Phoneme() override; @@ -37,6 +40,8 @@ namespace dspx { bool onset() const; void setOnset(bool onset); + PhonemeList *phonemeList() const; + QDspx::Phoneme toQDspx() const; void fromQDspx(const QDspx::Phoneme &phoneme); @@ -45,6 +50,7 @@ namespace dspx { void startChanged(int start); void tokenChanged(const QString &token); void onsetChanged(bool onset); + void phonemeListChanged(); protected: void handleSetEntityProperty(int property, const QVariant &value) override; @@ -53,9 +59,8 @@ namespace dspx { friend class ModelPrivate; explicit Phoneme(Handle handle, Model *model); QScopedPointer d_ptr; - }; } -#endif //DIFFSCOPE_DSPX_MODEL_PHONEME_H \ No newline at end of file +#endif //DIFFSCOPE_DSPX_MODEL_PHONEME_H diff --git a/src/libs/application/dspxmodel/src/PhonemeInfo.cpp b/src/libs/application/dspxmodel/src/PhonemeInfo.cpp index 0f590404..40008544 100644 --- a/src/libs/application/dspxmodel/src/PhonemeInfo.cpp +++ b/src/libs/application/dspxmodel/src/PhonemeInfo.cpp @@ -1,31 +1,33 @@ #include "PhonemeInfo.h" +#include "PhonemeInfo_p.h" -#include #include +#include #include -#include #include #include +#include +#include namespace dspx { - class PhonemeInfoPrivate { - Q_DECLARE_PUBLIC(PhonemeInfo) - public: - PhonemeInfo *q_ptr; - ModelPrivate *pModel; - PhonemeList *edited; - PhonemeList *original; - }; + void PhonemeInfoPrivate::setNote(PhonemeInfo *item, Note *note) { + auto d = item->d_func(); + if (d->note != note) { + d->note = note; + Q_EMIT item->noteChanged(); + } + } - PhonemeInfo::PhonemeInfo(Handle handle, Model *model) : QObject(model), d_ptr(new PhonemeInfoPrivate) { + PhonemeInfo::PhonemeInfo(Note *note, Handle handle, Model *model) : QObject(model), d_ptr(new PhonemeInfoPrivate) { Q_D(PhonemeInfo); d->q_ptr = this; d->pModel = ModelPrivate::get(model); - d->edited = d->pModel->createObject(d->pModel->strategy->getAssociatedSubEntity(handle, ModelStrategy::R_PhonemesEdited)); - d->original = d->pModel->createObject(d->pModel->strategy->getAssociatedSubEntity(handle, ModelStrategy::R_PhonemesOriginal)); + d->note = note; + d->edited = d->pModel->createObject(this, d->pModel->strategy->getAssociatedSubEntity(handle, ModelStrategy::R_PhonemesEdited)); + d->original = d->pModel->createObject(this, d->pModel->strategy->getAssociatedSubEntity(handle, ModelStrategy::R_PhonemesOriginal)); } PhonemeInfo::~PhonemeInfo() = default; @@ -53,6 +55,11 @@ namespace dspx { edited()->fromQDspx(phonemeInfo.edited); } + Note *PhonemeInfo::note() const { + Q_D(const PhonemeInfo); + return d->note; + } + } -#include "moc_PhonemeInfo.cpp" \ No newline at end of file +#include "moc_PhonemeInfo.cpp" diff --git a/src/libs/application/dspxmodel/src/PhonemeInfo.h b/src/libs/application/dspxmodel/src/PhonemeInfo.h index f2ad0aa9..6ed2d4cd 100644 --- a/src/libs/application/dspxmodel/src/PhonemeInfo.h +++ b/src/libs/application/dspxmodel/src/PhonemeInfo.h @@ -1,9 +1,10 @@ #ifndef DIFFSCOPE_DSPX_MODEL_PHONEMEINFO_H #define DIFFSCOPE_DSPX_MODEL_PHONEMEINFO_H -#include #include +#include + #include namespace QDspx { @@ -15,6 +16,7 @@ namespace dspx { class Model; class ModelPrivate; class PhonemeList; + class Note; class PhonemeInfoPrivate; @@ -25,6 +27,7 @@ namespace dspx { Q_DECLARE_PRIVATE(PhonemeInfo) Q_PROPERTY(PhonemeList *edited READ edited CONSTANT) Q_PROPERTY(PhonemeList *original READ original CONSTANT) + Q_PROPERTY(Note *note READ note CONSTANT) public: ~PhonemeInfo() override; @@ -32,16 +35,20 @@ namespace dspx { PhonemeList *edited() const; PhonemeList *original() const; + Note *note() const; + QDspx::Phonemes toQDspx() const; void fromQDspx(const QDspx::Phonemes &phonemeInfo); + Q_SIGNALS: + void noteChanged(); + private: friend class ModelPrivate; - explicit PhonemeInfo(Handle handle, Model *model); + explicit PhonemeInfo(Note *note, Handle handle, Model *model); QScopedPointer d_ptr; - }; } -#endif //DIFFSCOPE_DSPX_MODEL_PHONEMEINFO_H \ No newline at end of file +#endif //DIFFSCOPE_DSPX_MODEL_PHONEMEINFO_H diff --git a/src/libs/application/dspxmodel/src/PhonemeInfo_p.h b/src/libs/application/dspxmodel/src/PhonemeInfo_p.h new file mode 100644 index 00000000..987e3c7d --- /dev/null +++ b/src/libs/application/dspxmodel/src/PhonemeInfo_p.h @@ -0,0 +1,25 @@ +#ifndef DIFFSCOPE_DSPX_MODEL_PHONEMEINFO_P_H +#define DIFFSCOPE_DSPX_MODEL_PHONEMEINFO_P_H + +#include + +namespace dspx { + + class Note; + class ModelPrivate; + + class PhonemeInfoPrivate { + Q_DECLARE_PUBLIC(PhonemeInfo) + public: + PhonemeInfo *q_ptr; + ModelPrivate *pModel; + PhonemeList *edited{}; + PhonemeList *original{}; + Note *note{}; + + static void setNote(PhonemeInfo *item, Note *note); + }; + +} + +#endif //DIFFSCOPE_DSPX_MODEL_PHONEMEINFO_P_H \ No newline at end of file diff --git a/src/libs/application/dspxmodel/src/PhonemeList.cpp b/src/libs/application/dspxmodel/src/PhonemeList.cpp index 0f28daac..66b87c37 100644 --- a/src/libs/application/dspxmodel/src/PhonemeList.cpp +++ b/src/libs/application/dspxmodel/src/PhonemeList.cpp @@ -1,24 +1,25 @@ -#include "PhonemeList.h" - #include "ModelStrategy.h" +#include "PhonemeList.h" #include -#include -#include #include +#include +#include +#include +#include +#include namespace dspx { - class PhonemeListPrivate : public ListData { - Q_DECLARE_PUBLIC(PhonemeList) - }; - - PhonemeList::PhonemeList(Handle handle, Model *model) : EntityObject(handle, model), d_ptr(new PhonemeListPrivate) { + PhonemeList::PhonemeList(PhonemeInfo *phonemeInfo, Handle handle, Model *model) : EntityObject(handle, model), d_ptr(new PhonemeListPrivate) { Q_D(PhonemeList); Q_ASSERT(model->strategy()->getEntityType(handle) == ModelStrategy::EL_Phonemes); d->q_ptr = this; d->pModel = ModelPrivate::get(model); + d->phonemeInfo = phonemeInfo; + + d->init(model->strategy()->getEntitiesFromListContainer(handle)); } PhonemeList::~PhonemeList() = default; @@ -89,6 +90,11 @@ namespace dspx { d->handleRotateListContainer(leftIndex, middleIndex, rightIndex); } + PhonemeInfo *PhonemeList::phonemeInfo() const { + Q_D(const PhonemeList); + return d->phonemeInfo; + } + } -#include "moc_PhonemeList.cpp" \ No newline at end of file +#include "moc_PhonemeList.cpp" diff --git a/src/libs/application/dspxmodel/src/PhonemeList.h b/src/libs/application/dspxmodel/src/PhonemeList.h index 737f6c5b..504d4066 100644 --- a/src/libs/application/dspxmodel/src/PhonemeList.h +++ b/src/libs/application/dspxmodel/src/PhonemeList.h @@ -12,6 +12,7 @@ namespace QDspx { namespace dspx { class Phoneme; + class PhonemeInfo; class PhonemeListPrivate; @@ -22,6 +23,7 @@ namespace dspx { Q_DECLARE_PRIVATE(PhonemeList) Q_PROPERTY(int size READ size NOTIFY sizeChanged) Q_PROPERTY(QList items READ items NOTIFY itemsChanged) + Q_PROPERTY(PhonemeInfo *phonemeInfo READ phonemeInfo CONSTANT) Q_PRIVATE_PROPERTY(d_func(), QJSValue iterable READ iterable CONSTANT) public: @@ -37,6 +39,8 @@ namespace dspx { QList toQDspx() const; void fromQDspx(const QList &phonemeList); + PhonemeInfo *phonemeInfo() const; + Q_SIGNALS: void itemAboutToInsert(int index, Phoneme *item); void itemInserted(int index, Phoneme *item); @@ -54,10 +58,10 @@ namespace dspx { private: friend class ModelPrivate; - explicit PhonemeList(Handle handle, Model *model); + explicit PhonemeList(PhonemeInfo *phonemeInfo, Handle handle, Model *model); QScopedPointer d_ptr; }; } -#endif //DIFFSCOPE_DSPX_MODEL_PHONEMELIST_H \ No newline at end of file +#endif //DIFFSCOPE_DSPX_MODEL_PHONEMELIST_H diff --git a/src/libs/application/dspxmodel/src/PhonemeList_p.h b/src/libs/application/dspxmodel/src/PhonemeList_p.h new file mode 100644 index 00000000..336cd0a1 --- /dev/null +++ b/src/libs/application/dspxmodel/src/PhonemeList_p.h @@ -0,0 +1,20 @@ +#ifndef DIFFSCOPE_DSPX_MODEL_PHONEMELIST_P_H +#define DIFFSCOPE_DSPX_MODEL_PHONEMELIST_P_H + +#include +#include +#include + +namespace dspx { + + class PhonemeInfo; + + class PhonemeListPrivate : public ListData { + Q_DECLARE_PUBLIC(PhonemeList) + public: + PhonemeInfo *phonemeInfo{}; + }; + +} + +#endif //DIFFSCOPE_DSPX_MODEL_PHONEMELIST_P_H \ No newline at end of file diff --git a/src/libs/application/dspxmodel/src/Phoneme_p.h b/src/libs/application/dspxmodel/src/Phoneme_p.h new file mode 100644 index 00000000..05010ae0 --- /dev/null +++ b/src/libs/application/dspxmodel/src/Phoneme_p.h @@ -0,0 +1,32 @@ +#ifndef DIFFSCOPE_DSPX_MODEL_PHONEME_P_H +#define DIFFSCOPE_DSPX_MODEL_PHONEME_P_H + +#include + +namespace dspx { + + class PhonemeList; + class ModelPrivate; + + class PhonemePrivate { + Q_DECLARE_PUBLIC(Phoneme) + public: + Phoneme *q_ptr; + ModelPrivate *pModel; + QString language; + int start{}; + QString token; + bool onset{}; + PhonemeList *phonemeList{}; + + static void setPhonemeList(Phoneme *item, PhonemeList *phonemeList); + + void setLanguageUnchecked(const QString &language); + void setStartUnchecked(int start); + void setTokenUnchecked(const QString &token); + void setOnsetUnchecked(bool onset); + }; + +} + +#endif //DIFFSCOPE_DSPX_MODEL_PHONEME_P_H \ No newline at end of file diff --git a/src/libs/application/dspxmodel/src/PointSequenceContainer_p.h b/src/libs/application/dspxmodel/src/PointSequenceContainer_p.h index 22d65580..4068dc1b 100644 --- a/src/libs/application/dspxmodel/src/PointSequenceContainer_p.h +++ b/src/libs/application/dspxmodel/src/PointSequenceContainer_p.h @@ -4,21 +4,27 @@ #include #include -#include #include +#include namespace dspx { - template + template struct PointSequenceContainer { std::multimap m_items; QHash m_positions; T *firstItem() const { + if (m_items.empty()) { + return nullptr; + } return m_items.cbegin()->second; } T *lastItem() const { + if (m_items.empty()) { + return nullptr; + } return m_items.crbegin()->second; } @@ -70,10 +76,7 @@ namespace dspx { return {}; } QList ret; - auto it = m_items.lower_bound(position + length - 1); - while (it != m_items.end() && it != m_items.begin() && it->first >= position + length - 1) { - --it; - } + auto it = m_items.lower_bound(position + length); std::transform(m_items.lower_bound(position), it, std::back_inserter(ret), [](auto it) { return it.second; }); @@ -87,7 +90,6 @@ namespace dspx { bool contains(T *item) const { return m_positions.contains(item); } - }; } diff --git a/src/libs/application/dspxmodel/src/PointSequenceData.cpp b/src/libs/application/dspxmodel/src/PointSequenceData.cpp index c31d6035..bb3795ac 100644 --- a/src/libs/application/dspxmodel/src/PointSequenceData.cpp +++ b/src/libs/application/dspxmodel/src/PointSequenceData.cpp @@ -20,4 +20,4 @@ namespace dspx { return iterable; } -} \ No newline at end of file +} diff --git a/src/libs/application/dspxmodel/src/PointSequenceData_p.h b/src/libs/application/dspxmodel/src/PointSequenceData_p.h index aa08842e..6b15dbdc 100644 --- a/src/libs/application/dspxmodel/src/PointSequenceData_p.h +++ b/src/libs/application/dspxmodel/src/PointSequenceData_p.h @@ -13,7 +13,7 @@ namespace dspx { static QJSValue create(QObject *o); }; - template + template class PointSequenceData { public: SequenceType *q_ptr; @@ -35,6 +35,16 @@ namespace dspx { Q_UNREACHABLE(); } + 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(); + } + void insertItem(ItemType *item, int position) { auto q = q_ptr; bool containsItem = container.contains(item); @@ -42,6 +52,7 @@ namespace dspx { Q_EMIT q->itemAboutToInsert(item); } container.insertItem(item, position); + setSequence(item, q); if (!containsItem) { updateFirstAndLastItem(); Q_EMIT q->itemInserted(item); @@ -53,6 +64,7 @@ namespace dspx { auto q = q_ptr; Q_EMIT q->itemAboutToRemove(item); container.removeItem(item); + setSequence(item, nullptr); updateFirstAndLastItem(); Q_EMIT q->itemRemoved(item); Q_EMIT q->sizeChanged(container.size()); diff --git a/src/libs/application/dspxmodel/src/Pronunciation.cpp b/src/libs/application/dspxmodel/src/Pronunciation.cpp index 5f9098b8..e40c73cf 100644 --- a/src/libs/application/dspxmodel/src/Pronunciation.cpp +++ b/src/libs/application/dspxmodel/src/Pronunciation.cpp @@ -1,12 +1,12 @@ #include "Pronunciation.h" -#include #include +#include #include -#include #include +#include namespace dspx { @@ -84,4 +84,4 @@ namespace dspx { } -#include "moc_Pronunciation.cpp" \ No newline at end of file +#include "moc_Pronunciation.cpp" diff --git a/src/libs/application/dspxmodel/src/Pronunciation.h b/src/libs/application/dspxmodel/src/Pronunciation.h index dde91828..2210772b 100644 --- a/src/libs/application/dspxmodel/src/Pronunciation.h +++ b/src/libs/application/dspxmodel/src/Pronunciation.h @@ -1,9 +1,10 @@ #ifndef DIFFSCOPE_DSPX_MODEL_PRONUNCIATION_H #define DIFFSCOPE_DSPX_MODEL_PRONUNCIATION_H -#include #include +#include + #include namespace QDspx { @@ -46,9 +47,8 @@ namespace dspx { explicit Pronunciation(Handle handle, Model *model); void handleProxySetEntityProperty(int property, const QVariant &value); QScopedPointer d_ptr; - }; } -#endif //DIFFSCOPE_DSPX_MODEL_PRONUNCIATION_H \ No newline at end of file +#endif //DIFFSCOPE_DSPX_MODEL_PRONUNCIATION_H diff --git a/src/libs/application/dspxmodel/src/RangeSequenceContainer_p.h b/src/libs/application/dspxmodel/src/RangeSequenceContainer_p.h index 8ec73220..9e572c3b 100644 --- a/src/libs/application/dspxmodel/src/RangeSequenceContainer_p.h +++ b/src/libs/application/dspxmodel/src/RangeSequenceContainer_p.h @@ -2,14 +2,14 @@ #define DIFFSCOPE_DSPX_MODEL_RANGESEQUENCECONTAINER_P_H #include -#include #include +#include #include namespace dspx { - template + template struct RangeSequenceContainer { struct Interval : lib_interval_tree::interval { constexpr Interval() : interval(0, 0), m_item(nullptr) { @@ -22,6 +22,7 @@ namespace dspx { constexpr bool operator==(const Interval &other) const { return m_item == other.m_item; } + private: T *m_item; }; @@ -86,9 +87,8 @@ namespace dspx { }); return result; } - }; - + } #endif //DIFFSCOPE_DSPX_MODEL_RANGESEQUENCECONTAINER_P_H diff --git a/src/libs/application/dspxmodel/src/RangeSequenceData_p.h b/src/libs/application/dspxmodel/src/RangeSequenceData_p.h index 8e676167..63590f0e 100644 --- a/src/libs/application/dspxmodel/src/RangeSequenceData_p.h +++ b/src/libs/application/dspxmodel/src/RangeSequenceData_p.h @@ -11,10 +11,7 @@ namespace dspx { class PointSequenceJSIterable; - template + template class RangeSequenceData { public: SequenceType *q_ptr; @@ -37,23 +34,39 @@ namespace dspx { Q_UNREACHABLE(); } + 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); + setOverlapped(affectedItem, isOverlapped); + } + } + updateFirstAndLastItem(); + } + void insertItem(ItemType *item, int position, int length) { auto q = q_ptr; bool containsItem = pointContainer.contains(item); if (!containsItem) { Q_EMIT q->itemAboutToInsert(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 for (auto affectedItem : affectedItems) { bool isOverlapped = rangeContainer.isOverlapped(affectedItem); setOverlapped(affectedItem, isOverlapped); } - + if (!containsItem) { updateFirstAndLastItem(); Q_EMIT q->itemInserted(item); @@ -64,17 +77,18 @@ namespace dspx { void removeItem(ItemType *item) { auto q = q_ptr; Q_EMIT q->itemAboutToRemove(item); - + // Remove from both containers pointContainer.removeItem(item); + setSequence(item, nullptr); auto affectedItems = rangeContainer.removeItem(item); - + // Update overlapped status for all affected items for (auto affectedItem : affectedItems) { bool isOverlapped = rangeContainer.isOverlapped(affectedItem); setOverlapped(affectedItem, isOverlapped); } - + updateFirstAndLastItem(); Q_EMIT q->itemRemoved(item); Q_EMIT q->sizeChanged(pointContainer.size()); @@ -128,9 +142,8 @@ namespace dspx { iterable_ = PointSequenceJSIterable::create(q); return iterable_; } - }; } -#endif //DIFFSCOPE_DSPX_MODEL_RANGESEQUENCEDATA_P_H \ No newline at end of file +#endif //DIFFSCOPE_DSPX_MODEL_RANGESEQUENCEDATA_P_H diff --git a/src/libs/application/dspxmodel/src/SingingClip.cpp b/src/libs/application/dspxmodel/src/SingingClip.cpp index ac086951..17fb30c6 100644 --- a/src/libs/application/dspxmodel/src/SingingClip.cpp +++ b/src/libs/application/dspxmodel/src/SingingClip.cpp @@ -2,14 +2,14 @@ #include -#include -#include #include #include -#include +#include #include #include #include +#include +#include namespace dspx { @@ -28,9 +28,8 @@ namespace dspx { Q_ASSERT(model->strategy()->getEntityType(handle) == ModelStrategy::EI_SingingClip); d->q_ptr = this; d->pModel = ModelPrivate::get(model); - d->notes = d->pModel->createObject(d->pModel->strategy->getAssociatedSubEntity(handle, ModelStrategy::R_Children)); - d->notes->setSingingClip(this); - d->params = d->pModel->createObject(d->pModel->strategy->getAssociatedSubEntity(handle, ModelStrategy::R_Params)); + d->notes = d->pModel->createObject(this, d->pModel->strategy->getAssociatedSubEntity(handle, ModelStrategy::R_Children)); + d->params = d->pModel->createObject(this, d->pModel->strategy->getAssociatedSubEntity(handle, ModelStrategy::R_Params)); d->sources = d->pModel->createObject(d->pModel->strategy->getAssociatedSubEntity(handle, ModelStrategy::R_Sources)); } @@ -79,4 +78,4 @@ namespace dspx { } -#include "moc_SingingClip.cpp" \ No newline at end of file +#include "moc_SingingClip.cpp" diff --git a/src/libs/application/dspxmodel/src/SingingClip.h b/src/libs/application/dspxmodel/src/SingingClip.h index f83aa940..df7e7ce5 100644 --- a/src/libs/application/dspxmodel/src/SingingClip.h +++ b/src/libs/application/dspxmodel/src/SingingClip.h @@ -42,7 +42,6 @@ namespace dspx { friend class ModelPrivate; explicit SingingClip(Handle handle, Model *model); QScopedPointer d_ptr; - }; } diff --git a/src/libs/application/dspxmodel/src/Source.cpp b/src/libs/application/dspxmodel/src/Source.cpp index 8479ecc7..d7060b06 100644 --- a/src/libs/application/dspxmodel/src/Source.cpp +++ b/src/libs/application/dspxmodel/src/Source.cpp @@ -1,7 +1,7 @@ #include "Source.h" -#include #include +#include #include #include @@ -49,4 +49,4 @@ namespace dspx { } -#include "moc_Source.cpp" \ No newline at end of file +#include "moc_Source.cpp" diff --git a/src/libs/application/dspxmodel/src/Source.h b/src/libs/application/dspxmodel/src/Source.h index 62e30ff5..c341c42d 100644 --- a/src/libs/application/dspxmodel/src/Source.h +++ b/src/libs/application/dspxmodel/src/Source.h @@ -1,9 +1,10 @@ #ifndef DIFFSCOPE_DSPX_MODEL_SOURCE_H #define DIFFSCOPE_DSPX_MODEL_SOURCE_H -#include #include +#include + class QJsonObject; namespace dspx { @@ -32,9 +33,8 @@ namespace dspx { friend class ModelPrivate; explicit Source(Handle handle, Model *model); QScopedPointer d_ptr; - }; } -#endif //DIFFSCOPE_DSPX_MODEL_SOURCE_H \ No newline at end of file +#endif //DIFFSCOPE_DSPX_MODEL_SOURCE_H diff --git a/src/libs/application/dspxmodel/src/SourceMap.cpp b/src/libs/application/dspxmodel/src/SourceMap.cpp index 0e77dc72..76477524 100644 --- a/src/libs/application/dspxmodel/src/SourceMap.cpp +++ b/src/libs/application/dspxmodel/src/SourceMap.cpp @@ -1,12 +1,11 @@ -#include "SourceMap.h" - #include "ModelStrategy.h" +#include "SourceMap.h" #include -#include -#include #include +#include +#include namespace dspx { @@ -19,6 +18,8 @@ namespace dspx { Q_ASSERT(model->strategy()->getEntityType(handle) == ModelStrategy::EM_Sources); d->q_ptr = this; d->pModel = ModelPrivate::get(model); + + d->init(model->strategy()->getEntitiesFromMapContainer(handle)); } SourceMap::~SourceMap() = default; @@ -91,4 +92,4 @@ namespace dspx { } -#include "moc_SourceMap.cpp" \ No newline at end of file +#include "moc_SourceMap.cpp" diff --git a/src/libs/application/dspxmodel/src/SourceMap.h b/src/libs/application/dspxmodel/src/SourceMap.h index 1e7a5901..45aa8972 100644 --- a/src/libs/application/dspxmodel/src/SourceMap.h +++ b/src/libs/application/dspxmodel/src/SourceMap.h @@ -59,4 +59,4 @@ namespace dspx { } -#endif //DIFFSCOPE_DSPX_MODEL_SOURCEMAP_H \ No newline at end of file +#endif //DIFFSCOPE_DSPX_MODEL_SOURCEMAP_H diff --git a/src/libs/application/dspxmodel/src/SpliceHelper_p.h b/src/libs/application/dspxmodel/src/SpliceHelper_p.h index ce50ece6..219d3007 100644 --- a/src/libs/application/dspxmodel/src/SpliceHelper_p.h +++ b/src/libs/application/dspxmodel/src/SpliceHelper_p.h @@ -7,24 +7,22 @@ namespace dspx { class SpliceHelper { public: - template - static void splice(Container& container, typename Container::iterator first, - typename Container::iterator last, It2 newFirst, It2 newLast) { + static void splice(Container &container, typename Container::iterator first, typename Container::iterator last, It2 newFirst, It2 newLast) { auto oldSize = std::distance(first, last); auto newSize = std::distance(newFirst, newLast); - + auto replaceCount = std::min(oldSize, newSize); auto replaceEnd = first; auto newIt = newFirst; - + if (replaceCount > 0) { auto newEnd = newFirst; std::advance(newEnd, replaceCount); replaceEnd = std::copy(newFirst, newEnd, first); newIt = newEnd; } - + if (newIt == newLast) { if (replaceEnd != last) { container.erase(replaceEnd, last); @@ -33,19 +31,18 @@ namespace dspx { auto remainingCount = std::distance(newIt, newLast); auto insertPos = std::distance(container.begin(), replaceEnd); auto oldContainerSize = container.size(); - + container.resize(oldContainerSize + remainingCount); - + auto moveFrom = container.begin() + insertPos; auto moveTo = container.begin() + insertPos + remainingCount; auto moveEnd = container.begin() + oldContainerSize; - + std::move_backward(moveFrom, moveEnd, container.end()); - + std::copy(newIt, newLast, container.begin() + insertPos); } } - }; } diff --git a/src/libs/application/dspxmodel/src/Tempo.cpp b/src/libs/application/dspxmodel/src/Tempo.cpp index d306a02f..80ef31d2 100644 --- a/src/libs/application/dspxmodel/src/Tempo.cpp +++ b/src/libs/application/dspxmodel/src/Tempo.cpp @@ -1,56 +1,21 @@ #include "Tempo.h" - +#include "Tempo_p.h" #include -#include #include #include #include +#include namespace dspx { - class TempoPrivate { - Q_DECLARE_PUBLIC(Tempo) - public: - Tempo *q_ptr; - int pos; - double value; - - void setPosUnchecked(int pos_); - void setPos(int pos_); - - void setValueUnchecked(double value_); - void setValue(double value_); - - }; - - 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; - } - setValueUnchecked(value_); } Tempo::Tempo(Handle handle, Model *model) : EntityObject(handle, model), d_ptr(new TempoPrivate) { @@ -71,7 +36,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 { @@ -82,7 +47,12 @@ 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 { + Q_D(const Tempo); + return d->tempoSequence; } QDspx::Tempo Tempo::toQDspx() const { @@ -117,4 +87,4 @@ namespace dspx { } -#include "moc_Tempo.cpp" \ No newline at end of file +#include "moc_Tempo.cpp" diff --git a/src/libs/application/dspxmodel/src/Tempo.h b/src/libs/application/dspxmodel/src/Tempo.h index b845e16c..e57db8f8 100644 --- a/src/libs/application/dspxmodel/src/Tempo.h +++ b/src/libs/application/dspxmodel/src/Tempo.h @@ -1,15 +1,17 @@ #ifndef DIFFSCOPE_DSPX_MODEL_TEMPO_H #define DIFFSCOPE_DSPX_MODEL_TEMPO_H -#include #include +#include + namespace QDspx { struct Tempo; } namespace dspx { + class TempoSequence; class TempoPrivate; class DSPX_MODEL_EXPORT Tempo : public EntityObject { @@ -17,8 +19,9 @@ 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) public: ~Tempo() override; @@ -28,12 +31,15 @@ namespace dspx { double value() const; void setValue(double value); + TempoSequence *tempoSequence() const; + QDspx::Tempo toQDspx() const; void fromQDspx(const QDspx::Tempo &tempo); Q_SIGNALS: void posChanged(int pos); void valueChanged(double value); + void tempoSequenceChanged(); protected: void handleSetEntityProperty(int property, const QVariant &value) override; @@ -42,9 +48,8 @@ namespace dspx { friend class ModelPrivate; explicit Tempo(Handle handle, Model *model); QScopedPointer d_ptr; - }; } -#endif //DIFFSCOPE_DSPX_MODEL_TEMPO_H \ No newline at end of file +#endif //DIFFSCOPE_DSPX_MODEL_TEMPO_H diff --git a/src/libs/application/dspxmodel/src/TempoSequence.cpp b/src/libs/application/dspxmodel/src/TempoSequence.cpp index e03fefa6..2f9938ab 100644 --- a/src/libs/application/dspxmodel/src/TempoSequence.cpp +++ b/src/libs/application/dspxmodel/src/TempoSequence.cpp @@ -1,27 +1,25 @@ #include "TempoSequence.h" +#include "TempoSequence_p.h" -#include #include +#include #include -#include -#include -#include #include +#include +#include #include namespace dspx { - class TempoSequencePrivate : public PointSequenceData { - Q_DECLARE_PUBLIC(TempoSequence) - }; - TempoSequence::TempoSequence(Handle handle, Model *model) : EntityObject(handle, model), d_ptr(new TempoSequencePrivate) { Q_D(TempoSequence); Q_ASSERT(model->strategy()->getEntityType(handle) == ModelStrategy::ES_Tempos); d->q_ptr = this; d->pModel = ModelPrivate::get(model); + + d->init(model->strategy()->getEntitiesFromSequenceContainer(handle)); } TempoSequence::~TempoSequence() = default; @@ -104,4 +102,4 @@ namespace dspx { } -#include "moc_TempoSequence.cpp" \ No newline at end of file +#include "moc_TempoSequence.cpp" diff --git a/src/libs/application/dspxmodel/src/TempoSequence.h b/src/libs/application/dspxmodel/src/TempoSequence.h index a371f35b..623a9cef 100644 --- a/src/libs/application/dspxmodel/src/TempoSequence.h +++ b/src/libs/application/dspxmodel/src/TempoSequence.h @@ -4,6 +4,7 @@ #include #include +#include namespace QDspx { struct Tempo; @@ -41,6 +42,10 @@ namespace dspx { QList toQDspx() const; void fromQDspx(const QList &tempos); + auto asRange() const { + return impl::SequenceRange(this); + } + Q_SIGNALS: void itemAboutToInsert(Tempo *item); void itemInserted(Tempo *item); @@ -62,4 +67,4 @@ namespace dspx { } -#endif //DIFFSCOPE_DSPX_MODEL_TEMPOSEQUENCE_H \ No newline at end of file +#endif //DIFFSCOPE_DSPX_MODEL_TEMPOSEQUENCE_H diff --git a/src/libs/application/dspxmodel/src/TempoSequence_p.h b/src/libs/application/dspxmodel/src/TempoSequence_p.h new file mode 100644 index 00000000..d6be7b89 --- /dev/null +++ b/src/libs/application/dspxmodel/src/TempoSequence_p.h @@ -0,0 +1,17 @@ +#ifndef DIFFSCOPE_DSPX_MODEL_TEMPOSEQUENCE_P_H +#define DIFFSCOPE_DSPX_MODEL_TEMPOSEQUENCE_P_H + +#include + +#include +#include + +namespace dspx { + + class TempoSequencePrivate : public PointSequenceData { + Q_DECLARE_PUBLIC(TempoSequence) + }; + +} + +#endif //DIFFSCOPE_DSPX_MODEL_TEMPOSEQUENCE_P_H \ No newline at end of file diff --git a/src/libs/application/dspxmodel/src/Tempo_p.h b/src/libs/application/dspxmodel/src/Tempo_p.h new file mode 100644 index 00000000..1a8968e6 --- /dev/null +++ b/src/libs/application/dspxmodel/src/Tempo_p.h @@ -0,0 +1,21 @@ +#ifndef DIFFSCOPE_DSPX_MODEL_TEMPO_P_H +#define DIFFSCOPE_DSPX_MODEL_TEMPO_P_H + +#include + +namespace dspx { + + class TempoPrivate { + Q_DECLARE_PUBLIC(Tempo) + public: + Tempo *q_ptr; + int pos; + double value; + TempoSequence *tempoSequence{}; + + static void setTempoSequence(Tempo *item, TempoSequence *tempoSequence); + }; + +} + +#endif //DIFFSCOPE_DSPX_MODEL_TEMPO_P_H \ No newline at end of file diff --git a/src/libs/application/dspxmodel/src/TimeSignature.cpp b/src/libs/application/dspxmodel/src/TimeSignature.cpp index b2165aee..0d4c409a 100644 --- a/src/libs/application/dspxmodel/src/TimeSignature.cpp +++ b/src/libs/application/dspxmodel/src/TimeSignature.cpp @@ -1,78 +1,25 @@ #include "TimeSignature.h" - +#include "TimeSignature_p.h" #include -#include #include #include #include +#include namespace dspx { - class TimeSignaturePrivate { - Q_DECLARE_PUBLIC(TimeSignature) - public: - TimeSignature *q_ptr; - int index; - int numerator; - int denominator; - - void setIndexUnchecked(int index_); - void setIndex(int index_); - void setNumeratorUnchecked(int numerator_); - void setNumerator(int numerator_); - void setDenominatorUnchecked(int denominator_); - void setDenominator(int denominator_); - - }; - - 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_); - } - - 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; - } - setNumeratorUnchecked(numerator_); - } - - void TimeSignaturePrivate::setDenominatorUnchecked(int denominator_) { - Q_Q(TimeSignature); - q->model()->strategy()->setEntityProperty(q->handle(), ModelStrategy::P_Denominator, denominator_); - } - 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; + return d == 1 || d == 2 || d == 4 || d == 8 || d == 16 || d == 32 || d == 64 || d == 128; } - 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::setTimeSignatureSequence(TimeSignature *item, TimeSignatureSequence *timeSignatureSequence) { + auto d = item->d_func(); + if (d->timeSignatureSequence != timeSignatureSequence) { + d->timeSignatureSequence = timeSignatureSequence; + Q_EMIT item->timeSignatureSequenceChanged(); } - setDenominatorUnchecked(denominator_); } TimeSignature::TimeSignature(Handle handle, Model *model) : EntityObject(handle, model), d_ptr(new TimeSignaturePrivate) { @@ -94,7 +41,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 { @@ -104,8 +51,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 { @@ -116,11 +63,16 @@ 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 { + Q_D(const TimeSignature); + return d->timeSignatureSequence; } QDspx::TimeSignature TimeSignature::toQDspx() const { - return QDspx::TimeSignature { + return { .index = index(), .numerator = numerator(), .denominator = denominator(), @@ -159,4 +111,4 @@ namespace dspx { } -#include "moc_TimeSignature.cpp" \ No newline at end of file +#include "moc_TimeSignature.cpp" diff --git a/src/libs/application/dspxmodel/src/TimeSignature.h b/src/libs/application/dspxmodel/src/TimeSignature.h index 9c157310..57ca18cb 100644 --- a/src/libs/application/dspxmodel/src/TimeSignature.h +++ b/src/libs/application/dspxmodel/src/TimeSignature.h @@ -1,15 +1,17 @@ #ifndef DIFFSCOPE_DSPX_MODEL_TIMESIGNATURE_H #define DIFFSCOPE_DSPX_MODEL_TIMESIGNATURE_H -#include #include +#include + namespace QDspx { struct TimeSignature; } namespace dspx { + class TimeSignatureSequence; class TimeSignaturePrivate; class DSPX_MODEL_EXPORT TimeSignature : public EntityObject { @@ -17,9 +19,10 @@ 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) public: ~TimeSignature() override; @@ -32,6 +35,8 @@ namespace dspx { int denominator() const; void setDenominator(int denominator); + TimeSignatureSequence *timeSignatureSequence() const; + QDspx::TimeSignature toQDspx() const; void fromQDspx(const QDspx::TimeSignature &timeSignature); @@ -39,6 +44,7 @@ namespace dspx { void indexChanged(int index); void numeratorChanged(int numerator); void denominatorChanged(int denominator); + void timeSignatureSequenceChanged(); protected: void handleSetEntityProperty(int property, const QVariant &value) override; @@ -47,9 +53,8 @@ namespace dspx { friend class ModelPrivate; explicit TimeSignature(Handle handle, Model *model); QScopedPointer d_ptr; - }; } -#endif //DIFFSCOPE_DSPX_MODEL_TIMESIGNATURE_H \ No newline at end of file +#endif //DIFFSCOPE_DSPX_MODEL_TIMESIGNATURE_H diff --git a/src/libs/application/dspxmodel/src/TimeSignatureSequence.cpp b/src/libs/application/dspxmodel/src/TimeSignatureSequence.cpp index 174a35ff..b7486502 100644 --- a/src/libs/application/dspxmodel/src/TimeSignatureSequence.cpp +++ b/src/libs/application/dspxmodel/src/TimeSignatureSequence.cpp @@ -1,27 +1,25 @@ #include "TimeSignatureSequence.h" +#include "TimeSignatureSequence_p.h" -#include #include +#include #include -#include -#include -#include #include +#include +#include #include namespace dspx { - class TimeSignatureSequencePrivate : public PointSequenceData { - Q_DECLARE_PUBLIC(TimeSignatureSequence) - }; - TimeSignatureSequence::TimeSignatureSequence(Handle handle, Model *model) : EntityObject(handle, model), d_ptr(new TimeSignatureSequencePrivate) { Q_D(TimeSignatureSequence); Q_ASSERT(model->strategy()->getEntityType(handle) == ModelStrategy::ES_TimeSignatures); d->q_ptr = this; d->pModel = ModelPrivate::get(model); + + d->init(model->strategy()->getEntitiesFromSequenceContainer(handle)); } TimeSignatureSequence::~TimeSignatureSequence() = default; @@ -104,4 +102,4 @@ namespace dspx { } -#include "moc_TimeSignatureSequence.cpp" \ No newline at end of file +#include "moc_TimeSignatureSequence.cpp" diff --git a/src/libs/application/dspxmodel/src/TimeSignatureSequence.h b/src/libs/application/dspxmodel/src/TimeSignatureSequence.h index d4008859..8e00f951 100644 --- a/src/libs/application/dspxmodel/src/TimeSignatureSequence.h +++ b/src/libs/application/dspxmodel/src/TimeSignatureSequence.h @@ -4,6 +4,7 @@ #include #include +#include namespace QDspx { struct TimeSignature; @@ -41,6 +42,10 @@ namespace dspx { QList toQDspx() const; void fromQDspx(const QList &timeSignatures); + auto asRange() const { + return impl::SequenceRange(this); + } + Q_SIGNALS: void itemAboutToInsert(TimeSignature *item); void itemInserted(TimeSignature *item); @@ -62,4 +67,4 @@ namespace dspx { } -#endif //DIFFSCOPE_DSPX_MODEL_TIMESIGNATURESEQUENCE_H \ No newline at end of file +#endif //DIFFSCOPE_DSPX_MODEL_TIMESIGNATURESEQUENCE_H diff --git a/src/libs/application/dspxmodel/src/TimeSignatureSequence_p.h b/src/libs/application/dspxmodel/src/TimeSignatureSequence_p.h new file mode 100644 index 00000000..a70c1aeb --- /dev/null +++ b/src/libs/application/dspxmodel/src/TimeSignatureSequence_p.h @@ -0,0 +1,17 @@ +#ifndef DIFFSCOPE_DSPX_MODEL_TIMESIGNATURESEQUENCE_P_H +#define DIFFSCOPE_DSPX_MODEL_TIMESIGNATURESEQUENCE_P_H + +#include + +#include +#include + +namespace dspx { + + class TimeSignatureSequencePrivate : public PointSequenceData { + Q_DECLARE_PUBLIC(TimeSignatureSequence) + }; + +} + +#endif //DIFFSCOPE_DSPX_MODEL_TIMESIGNATURESEQUENCE_P_H \ No newline at end of file diff --git a/src/libs/application/dspxmodel/src/TimeSignature_p.h b/src/libs/application/dspxmodel/src/TimeSignature_p.h new file mode 100644 index 00000000..a0d5a0a9 --- /dev/null +++ b/src/libs/application/dspxmodel/src/TimeSignature_p.h @@ -0,0 +1,22 @@ +#ifndef DIFFSCOPE_DSPX_MODEL_TIMESIGNATURE_P_H +#define DIFFSCOPE_DSPX_MODEL_TIMESIGNATURE_P_H + +#include + +namespace dspx { + + class TimeSignaturePrivate { + Q_DECLARE_PUBLIC(TimeSignature) + public: + TimeSignature *q_ptr; + int index; + int numerator; + int denominator; + TimeSignatureSequence *timeSignatureSequence{}; + + static void setTimeSignatureSequence(TimeSignature *item, TimeSignatureSequence *timeSignatureSequence); + }; + +} + +#endif //DIFFSCOPE_DSPX_MODEL_TIMESIGNATURE_P_H \ No newline at end of file diff --git a/src/libs/application/dspxmodel/src/Timeline.cpp b/src/libs/application/dspxmodel/src/Timeline.cpp index a40b907c..0867647e 100644 --- a/src/libs/application/dspxmodel/src/Timeline.cpp +++ b/src/libs/application/dspxmodel/src/Timeline.cpp @@ -2,10 +2,11 @@ #include -#include +#include #include #include #include +#include namespace dspx { @@ -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(), @@ -56,4 +116,4 @@ namespace dspx { } -#include "moc_Timeline.cpp" \ No newline at end of file +#include "moc_Timeline.cpp" diff --git a/src/libs/application/dspxmodel/src/Timeline.h b/src/libs/application/dspxmodel/src/Timeline.h index 728625fb..7eb87d8c 100644 --- a/src/libs/application/dspxmodel/src/Timeline.h +++ b/src/libs/application/dspxmodel/src/Timeline.h @@ -1,9 +1,10 @@ #ifndef DIFFSCOPE_DSPX_MODEL_TIMELINE_H #define DIFFSCOPE_DSPX_MODEL_TIMELINE_H -#include #include +#include + #include namespace QDspx { @@ -28,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; @@ -35,15 +39,29 @@ 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 668fe661..a0e7140d 100644 --- a/src/libs/application/dspxmodel/src/Track.cpp +++ b/src/libs/application/dspxmodel/src/Track.cpp @@ -1,35 +1,27 @@ #include "Track.h" +#include "Track_p.h" -#include #include +#include #include -#include +#include #include #include #include -#include +#include +#include +#include namespace dspx { - class TrackPrivate { - Q_DECLARE_PUBLIC(Track) - public: - Track *q_ptr; - ModelPrivate *pModel; - ClipSequence *clips; - QString name; - TrackControl *control; - Workspace *workspace; - }; - - static QString colorToHex(const QColor &color) { - return color.isValid() ? color.name(QColor::HexRgb) : QString(); - } - - static QColor hexToColor(const QString &hex) { - return QColor::fromString(hex); + void TrackPrivate::setTrackList(Track *item, TrackList *trackList) { + auto d = item->d_func(); + if (d->trackList != trackList) { + d->trackList = trackList; + Q_EMIT item->trackListChanged(); + } } Track::Track(Handle handle, Model *model) : EntityObject(handle, model), d_ptr(new TrackPrivate) { @@ -38,19 +30,40 @@ namespace dspx { d->q_ptr = this; 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(d->pModel->strategy->getAssociatedSubEntity(handle, ModelStrategy::R_Children)); - d->clips->setTrack(this); + d->clips = d->pModel->createObject(this, d->pModel->strategy->getAssociatedSubEntity(handle, ModelStrategy::R_Children)); } Track::~Track() = default; - ClipSequence * Track::clips() const { + ClipSequence *Track::clips() const { Q_D(const Track); return d->clips; } + int Track::colorId() const { + Q_D(const Track); + return d->colorId; + } + + void Track::setColorId(int colorId) { + Q_D(Track); + 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; @@ -72,12 +85,18 @@ namespace dspx { } QDspx::Track Track::toQDspx() const { - return { + QDspx::Track track { .name = name(), .control = control()->toQDspx(), .clips = clips()->toQDspx(), .workspace = workspace()->toQDspx(), }; + track.workspace["diffscope"] = QJsonObject{ + {"colorId", colorId()}, + {"height", height()}, + {"record", control()->record()}, + }; + return track; } void Track::fromQDspx(const QDspx::Track &track) { @@ -85,6 +104,14 @@ namespace dspx { control()->fromQDspx(track.control); 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 { + Q_D(const Track); + return d->trackList; } void Track::handleSetEntityProperty(int property, const QVariant &value) { @@ -95,9 +122,20 @@ namespace dspx { Q_EMIT nameChanged(d->name); break; } + case ModelStrategy::P_ColorId: { + d->colorId = value.toInt(); + 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; @@ -109,4 +147,4 @@ namespace dspx { } -#include "moc_Track.cpp" \ No newline at end of file +#include "moc_Track.cpp" diff --git a/src/libs/application/dspxmodel/src/Track.h b/src/libs/application/dspxmodel/src/Track.h index 4a70be4c..c49fd48c 100644 --- a/src/libs/application/dspxmodel/src/Track.h +++ b/src/libs/application/dspxmodel/src/Track.h @@ -15,6 +15,8 @@ namespace dspx { class TrackControl; class Workspace; + class TrackList; + class TrackPrivate; class DSPX_MODEL_EXPORT Track : public EntityObject { @@ -23,15 +25,24 @@ namespace dspx { QML_UNCREATABLE("") 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) + Q_PROPERTY(TrackList *trackList READ trackList CONSTANT) public: ~Track() override; ClipSequence *clips() const; + int colorId() const; + void setColorId(int colorId); + + double height() const; + void setHeight(double height); + TrackControl *control() const; QString name() const; @@ -42,8 +53,13 @@ namespace dspx { QDspx::Track toQDspx() const; void fromQDspx(const QDspx::Track &track); + TrackList *trackList() const; + Q_SIGNALS: void nameChanged(const QString &name); + void colorIdChanged(int colorId); + void heightChanged(double height); + void trackListChanged(); protected: void handleSetEntityProperty(int property, const QVariant &value) override; @@ -52,7 +68,6 @@ namespace dspx { friend class ModelPrivate; explicit Track(Handle handle, Model *model); QScopedPointer d_ptr; - }; } diff --git a/src/libs/application/dspxmodel/src/TrackControl.cpp b/src/libs/application/dspxmodel/src/TrackControl.cpp index ce57625e..f3142173 100644 --- a/src/libs/application/dspxmodel/src/TrackControl.cpp +++ b/src/libs/application/dspxmodel/src/TrackControl.cpp @@ -2,8 +2,8 @@ #include -#include #include +#include namespace dspx { @@ -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,10 +69,15 @@ 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); } } } -} \ No newline at end of file +} diff --git a/src/libs/application/dspxmodel/src/TrackControl.h b/src/libs/application/dspxmodel/src/TrackControl.h index f5d8af2f..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,18 +25,21 @@ 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; explicit TrackControl(Handle handle, Model *model); void handleProxySetEntityProperty(int property, const QVariant &value); QScopedPointer d_ptr; - }; } diff --git a/src/libs/application/dspxmodel/src/TrackList.cpp b/src/libs/application/dspxmodel/src/TrackList.cpp index f93ba966..eafbee62 100644 --- a/src/libs/application/dspxmodel/src/TrackList.cpp +++ b/src/libs/application/dspxmodel/src/TrackList.cpp @@ -1,16 +1,16 @@ -#include "TrackList.h" - #include "ModelStrategy.h" +#include "TrackList.h" #include -#include -#include #include +#include +#include +#include namespace dspx { - class TrackListPrivate : public ListData { + class TrackListPrivate : public ListData { Q_DECLARE_PUBLIC(TrackList) }; @@ -19,6 +19,8 @@ namespace dspx { Q_ASSERT(model->strategy()->getEntityType(handle) == ModelStrategy::EL_Tracks); d->q_ptr = this; d->pModel = ModelPrivate::get(model); + + d->init(model->strategy()->getEntitiesFromListContainer(handle)); } TrackList::~TrackList() = default; @@ -91,4 +93,4 @@ namespace dspx { } -#include "moc_TrackList.cpp" \ No newline at end of file +#include "moc_TrackList.cpp" diff --git a/src/libs/application/dspxmodel/src/TrackList.h b/src/libs/application/dspxmodel/src/TrackList.h index 1d097f80..dd34606c 100644 --- a/src/libs/application/dspxmodel/src/TrackList.h +++ b/src/libs/application/dspxmodel/src/TrackList.h @@ -60,4 +60,4 @@ namespace dspx { } -#endif //DIFFSCOPE_DSPX_MODEL_TRACKLIST_H \ No newline at end of file +#endif //DIFFSCOPE_DSPX_MODEL_TRACKLIST_H diff --git a/src/libs/application/dspxmodel/src/Track_p.h b/src/libs/application/dspxmodel/src/Track_p.h new file mode 100644 index 00000000..4a4caeba --- /dev/null +++ b/src/libs/application/dspxmodel/src/Track_p.h @@ -0,0 +1,26 @@ +#ifndef DIFFSCOPE_DSPX_MODEL_TRACK_P_H +#define DIFFSCOPE_DSPX_MODEL_TRACK_P_H + +#include + +namespace dspx { + + class TrackPrivate { + Q_DECLARE_PUBLIC(Track) + public: + Track *q_ptr; + ModelPrivate *pModel; + ClipSequence *clips; + QString name; + int colorId; + double height; + TrackControl *control; + Workspace *workspace; + TrackList *trackList; + + static void setTrackList(Track *item, TrackList *trackList); + }; + +} + +#endif //DIFFSCOPE_DSPX_MODEL_TRACK_P_H diff --git a/src/libs/application/dspxmodel/src/UndoableModelStrategy.cpp b/src/libs/application/dspxmodel/src/UndoableModelStrategy.cpp new file mode 100644 index 00000000..ff1f3c0b --- /dev/null +++ b/src/libs/application/dspxmodel/src/UndoableModelStrategy.cpp @@ -0,0 +1,591 @@ +#include "UndoableModelStrategy.h" +#include "UndoableModelStrategy_p.h" + +#include + +#include + +namespace dspx { + + //================================================================ + // Create/Destroy Commands + //================================================================ + + CreateEntityCommand::CreateEntityCommand(UndoableModelStrategy *strategy, BasicModelStrategy::Entity entityType, + QUndoCommand *parent) + : QUndoCommand(parent), m_strategy(strategy), m_entityType(entityType), m_object(nullptr) { + } + + CreateEntityCommand::~CreateEntityCommand() { + // Delete object if command is in undone state + if (m_object && m_undone) { + delete m_object.data(); + } + } + + void CreateEntityCommand::undo() { + // Detach object from strategy but keep it alive + m_object->setParent(nullptr); + m_undone = true; + Q_EMIT m_strategy->destroyEntityNotified(entity()); + } + + void CreateEntityCommand::redo() { + if (!m_object) { + // First time: create the object + m_object = BasicModelStrategyEntity::createByType(m_entityType, m_strategy); + } else { + // Subsequent times: re-parent the existing object + m_object->setParent(m_strategy); + } + m_undone = false; + Q_EMIT m_strategy->createEntityNotified(entity(), m_entityType); + } + + Handle CreateEntityCommand::entity() const { + return {reinterpret_cast(m_object.data())}; + } + + DestroyEntityCommand::DestroyEntityCommand(UndoableModelStrategy *strategy, Handle entity, + QUndoCommand *parent) + : QUndoCommand(parent), m_strategy(strategy), m_object(handle_cast(entity)) { + } + + DestroyEntityCommand::~DestroyEntityCommand() { + // Delete object if command is NOT in undone state (i.e., destroy was executed) + if (m_object && !m_undone) { + delete m_object.data(); + } + } + + void DestroyEntityCommand::recursivelyCreate(BasicModelStrategyEntity *object) { + object->setParent(m_strategy); + Q_EMIT m_strategy->createEntityNotified({reinterpret_cast(object)}, object->type); + + if (auto seq = qobject_cast(object)) { + for (auto child : std::as_const(seq->sequence)) { + recursivelyCreate(child); + } + } else if (auto list = qobject_cast(object)) { + for (auto child : std::as_const(list->list)) { + recursivelyCreate(child); + } + } else if (auto map = qobject_cast(object)) { + 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); + } + } + } + + void DestroyEntityCommand::recursivelyDestroy(BasicModelStrategyEntity *object) { + if (auto seq = qobject_cast(object)) { + for (auto child : std::as_const(seq->sequence)) { + recursivelyDestroy(child); + } + } else if (auto list = qobject_cast(object)) { + for (auto child : std::as_const(list->list)) { + recursivelyDestroy(child); + } + } else if (auto map = qobject_cast(object)) { + 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); + Q_EMIT m_strategy->destroyEntityNotified({reinterpret_cast(object)}); + } + + void DestroyEntityCommand::undo() { + recursivelyCreate(m_object); + m_undone = true; + } + + void DestroyEntityCommand::redo() { + recursivelyDestroy(m_object); + m_undone = false; + } + + //================================================================ + // Container Commands + //================================================================ + + InsertIntoSequenceContainerCommand::InsertIntoSequenceContainerCommand( + UndoableModelStrategy *strategy, Handle sequenceContainerEntity, + Handle entity, QUndoCommand *parent) + : QUndoCommand(parent), m_strategy(strategy), m_container(sequenceContainerEntity), m_entity(entity) { + } + + void InsertIntoSequenceContainerCommand::undo() { + auto containerObj = handle_cast(m_container); + auto entityObj = handle_cast(m_entity); + containerObj->sequence.remove(entityObj); + entityObj->setParent(m_strategy); + Q_EMIT m_strategy->takeFromContainerNotified(m_entity, m_container, m_entity); + } + + void InsertIntoSequenceContainerCommand::redo() { + auto containerObj = handle_cast(m_container); + auto entityObj = handle_cast(m_entity); + containerObj->sequence.insert(entityObj); + entityObj->setParent(containerObj); + Q_EMIT m_strategy->insertIntoSequenceContainerNotified(m_container, m_entity); + } + + TakeFromSequenceContainerCommand::TakeFromSequenceContainerCommand( + UndoableModelStrategy *strategy, Handle sequenceContainerEntity, + Handle entity, QUndoCommand *parent) + : QUndoCommand(parent), m_strategy(strategy), m_container(sequenceContainerEntity), + m_object(handle_cast(entity)) { + } + + TakeFromSequenceContainerCommand::~TakeFromSequenceContainerCommand() { + // Delete object if command is NOT in undone state (i.e., take was executed) + if (m_object && !m_undone) { + delete m_object.data(); + } + } + + void TakeFromSequenceContainerCommand::undo() { + auto containerObj = handle_cast(m_container); + containerObj->sequence.insert(m_object); + m_object->setParent(containerObj); + m_undone = true; + Q_EMIT m_strategy->insertIntoSequenceContainerNotified(m_container, {reinterpret_cast(m_object.data())}); + } + + void TakeFromSequenceContainerCommand::redo() { + auto containerObj = handle_cast(m_container); + containerObj->sequence.remove(m_object); + m_object->setParent(m_strategy); + m_undone = false; + Q_EMIT m_strategy->takeFromContainerNotified({reinterpret_cast(m_object.data())}, m_container, + {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) + : QUndoCommand(parent), m_strategy(strategy), m_container(listContainerEntity), m_entity(entity), + m_index(index) { + } + + void InsertIntoListContainerCommand::undo() { + auto containerObj = handle_cast(m_container); + auto entityObj = handle_cast(m_entity); + containerObj->list.removeAt(m_index); + entityObj->setParent(m_strategy); + Q_EMIT m_strategy->takeFromListContainerNotified(m_entity, m_container, m_index); + } + + void InsertIntoListContainerCommand::redo() { + auto containerObj = handle_cast(m_container); + auto entityObj = handle_cast(m_entity); + containerObj->list.insert(m_index, entityObj); + entityObj->setParent(containerObj); + Q_EMIT m_strategy->insertIntoListContainerNotified(m_container, m_entity, m_index); + } + + TakeFromListContainerCommand::TakeFromListContainerCommand(UndoableModelStrategy *strategy, + Handle listContainerEntity, + int index, QUndoCommand *parent) + : QUndoCommand(parent), m_strategy(strategy), m_container(listContainerEntity), m_object(nullptr), + m_index(index) { + } + + TakeFromListContainerCommand::~TakeFromListContainerCommand() { + // Delete object if command is NOT in undone state (i.e., take was executed) + if (m_object && !m_undone) { + delete m_object.data(); + } + } + + void TakeFromListContainerCommand::undo() { + auto containerObj = handle_cast(m_container); + containerObj->list.insert(m_index, m_object); + m_object->setParent(containerObj); + m_undone = true; + Q_EMIT m_strategy->insertIntoListContainerNotified(m_container, entity(), m_index); + } + + void TakeFromListContainerCommand::redo() { + if (!m_object) { + auto containerObj = handle_cast(m_container); + m_object = containerObj->list.at(m_index); + } + auto containerObj = handle_cast(m_container); + containerObj->list.removeAt(m_index); + m_object->setParent(m_strategy); + m_undone = false; + Q_EMIT m_strategy->takeFromListContainerNotified(entity(), m_container, m_index); + } + + Handle TakeFromListContainerCommand::entity() const { + return {reinterpret_cast(m_object.data())}; + } + + InsertIntoMapContainerCommand::InsertIntoMapContainerCommand(UndoableModelStrategy *strategy, + Handle mapContainerEntity, + Handle entity, const QString &key, + QUndoCommand *parent) + : QUndoCommand(parent), m_strategy(strategy), m_container(mapContainerEntity), m_entity(entity), m_key(key), + m_oldObject(nullptr) { + } + + InsertIntoMapContainerCommand::~InsertIntoMapContainerCommand() { + if (m_oldObject) { + delete m_oldObject.data(); + } + } + + void InsertIntoMapContainerCommand::undo() { + auto containerObj = handle_cast(m_container); + auto entityObj = handle_cast(m_entity); + + containerObj->map.remove(m_key); + entityObj->setParent(m_strategy); + Q_EMIT m_strategy->takeFromMapContainerNotified(m_entity, m_container, m_key); + + if (m_oldObject) { + containerObj->map.insert(m_key, m_oldObject); + m_oldObject->setParent(containerObj); + Q_EMIT m_strategy->insertIntoMapContainerNotified(m_container, + {reinterpret_cast(m_oldObject.data())}, m_key); + m_oldObject = nullptr; // Transfer ownership back to container + } + } + + void InsertIntoMapContainerCommand::redo() { + auto containerObj = handle_cast(m_container); + auto entityObj = handle_cast(m_entity); + + if (containerObj->map.contains(m_key)) { + m_oldObject = containerObj->map.take(m_key); + m_oldObject->setParent(nullptr); // Take ownership + Q_EMIT m_strategy->takeFromMapContainerNotified({reinterpret_cast(m_oldObject.get())}, m_container, + m_key); + } + + containerObj->map.insert(m_key, entityObj); + entityObj->setParent(containerObj); + Q_EMIT m_strategy->insertIntoMapContainerNotified(m_container, m_entity, m_key); + } + + TakeFromMapContainerCommand::TakeFromMapContainerCommand(UndoableModelStrategy *strategy, + Handle mapContainerEntity, + const QString &key, QUndoCommand *parent) + : QUndoCommand(parent), m_strategy(strategy), m_container(mapContainerEntity), m_object(nullptr), m_key(key) { + } + + TakeFromMapContainerCommand::~TakeFromMapContainerCommand() { + // Delete object if command is NOT in undone state (i.e., take was executed) + if (m_object && !m_undone) { + delete m_object.data(); + } + } + + void TakeFromMapContainerCommand::undo() { + auto containerObj = handle_cast(m_container); + containerObj->map.insert(m_key, m_object); + m_object->setParent(containerObj); + m_undone = true; + Q_EMIT m_strategy->insertIntoMapContainerNotified(m_container, entity(), m_key); + } + + void TakeFromMapContainerCommand::redo() { + if (!m_object) { + auto containerObj = handle_cast(m_container); + m_object = containerObj->map.value(m_key); + } + auto containerObj = handle_cast(m_container); + containerObj->map.remove(m_key); + m_object->setParent(m_strategy); + m_undone = false; + Q_EMIT m_strategy->takeFromMapContainerNotified(entity(), m_container, m_key); + } + + Handle TakeFromMapContainerCommand::entity() const { + return {reinterpret_cast(m_object.data())}; + } + + RotateListContainerCommand::RotateListContainerCommand(UndoableModelStrategy *strategy, + Handle listContainerEntity, int left, + int middle, int right, QUndoCommand *parent) + : QUndoCommand(parent), m_strategy(strategy), m_container(listContainerEntity), m_left(left), + m_middle(middle), m_right(right) { + } + + void RotateListContainerCommand::undo() { + auto &list = handle_cast(m_container)->list; + std::rotate(list.begin() + m_left, list.begin() + m_right - (m_middle - m_left), list.begin() + m_right); + Q_EMIT m_strategy->rotateListContainerNotified(m_container, m_left, m_right - (m_middle - m_left), m_right); + } + + void RotateListContainerCommand::redo() { + auto &list = handle_cast(m_container)->list; + std::rotate(list.begin() + m_left, list.begin() + m_middle, list.begin() + m_right); + Q_EMIT m_strategy->rotateListContainerNotified(m_container, m_left, m_middle, m_right); + } + + //================================================================ + // Property & Data Commands + //================================================================ + + SetEntityPropertyCommand::SetEntityPropertyCommand(UndoableModelStrategy *strategy, + Handle entity, + BasicModelStrategy::Property property, const QVariant &value, + QUndoCommand *parent) + : QUndoCommand(parent), m_strategy(strategy), m_entity(entity), m_property(property), m_newValue(value) { + m_oldValue = m_strategy->getEntityProperty(m_entity, m_property); + } + + void SetEntityPropertyCommand::undo() { + auto object = handle_cast(m_entity); + object->properties.insert(m_property, m_oldValue); + Q_EMIT m_strategy->setEntityPropertyNotified(m_entity, m_property, m_oldValue); + } + + void SetEntityPropertyCommand::redo() { + auto object = handle_cast(m_entity); + object->properties.insert(m_property, m_newValue); + Q_EMIT m_strategy->setEntityPropertyNotified(m_entity, m_property, m_newValue); + } + + bool SetEntityPropertyCommand::mergeWith(const QUndoCommand *command) { + const auto other = static_cast(command); + if (other->m_entity != m_entity || other->m_property != m_property) { + return false; + } + m_newValue = other->m_newValue; + return true; + } + + int SetEntityPropertyCommand::id() const { + // Return a constant to force mergeWith to be called for comparison + return 1; + } + + SpliceDataArrayCommand::SpliceDataArrayCommand(UndoableModelStrategy *strategy, + Handle dataContainerEntity, int index, + int length, const QVariantList &values, QUndoCommand *parent) + : QUndoCommand(parent), m_strategy(strategy), m_container(dataContainerEntity), m_index(index), + m_length(length), m_values(values) { + } + + void SpliceDataArrayCommand::undo() { + auto &data = handle_cast(m_container)->data; + SpliceHelper::splice(data, data.begin() + m_index, data.begin() + m_index + m_values.size(), + m_oldValues.begin(), m_oldValues.end()); + Q_EMIT m_strategy->spliceDataArrayNotified(m_container, m_index, m_values.size(), m_oldValues); + } + + void SpliceDataArrayCommand::redo() { + auto &data = handle_cast(m_container)->data; + if (m_oldValues.isEmpty() && m_length > 0) { + m_oldValues = data.mid(m_index, m_length); + } + SpliceHelper::splice(data, data.begin() + m_index, data.begin() + m_index + m_length, m_values.begin(), + m_values.end()); + Q_EMIT m_strategy->spliceDataArrayNotified(m_container, m_index, m_length, m_values); + } + + RotateDataArrayCommand::RotateDataArrayCommand(UndoableModelStrategy *strategy, + Handle dataContainerEntity, int left, + int middle, int right, QUndoCommand *parent) + : QUndoCommand(parent), m_strategy(strategy), m_container(dataContainerEntity), m_left(left), + m_middle(middle), m_right(right) { + } + + void RotateDataArrayCommand::undo() { + auto &data = handle_cast(m_container)->data; + std::rotate(data.begin() + m_left, data.begin() + m_right - (m_middle - m_left), data.begin() + m_right); + Q_EMIT m_strategy->rotateDataArrayNotified(m_container, m_left, m_right - (m_middle - m_left), m_right); + } + + void RotateDataArrayCommand::redo() { + auto &data = handle_cast(m_container)->data; + std::rotate(data.begin() + m_left, data.begin() + m_middle, data.begin() + m_right); + Q_EMIT m_strategy->rotateDataArrayNotified(m_container, m_left, m_middle, m_right); + } + + //================================================================ + // UndoableModelStrategy + //================================================================ + + UndoableModelStrategy::UndoableModelStrategy(QObject *parent) + : BasicModelStrategy(parent), m_undoStack(new QUndoStack(this)) { + } + + UndoableModelStrategy::~UndoableModelStrategy() = default; + + QUndoStack *UndoableModelStrategy::undoStack() const { + return m_undoStack; + } + + Handle UndoableModelStrategy::createEntity(Entity entityType) { + auto cmd = new CreateEntityCommand(this, entityType); + m_undoStack->push(cmd); + return cmd->entity(); + } + + void UndoableModelStrategy::destroyEntity(Handle entity) { + m_undoStack->push(new DestroyEntityCommand(this, entity)); + } + + bool UndoableModelStrategy::insertIntoSequenceContainer(Handle sequenceContainerEntity, Handle entity) { + auto entityObj = handle_cast(entity); + if (entityObj->parent() != this) { + return false; + } + m_undoStack->push(new InsertIntoSequenceContainerCommand(this, sequenceContainerEntity, entity)); + return true; + } + + bool UndoableModelStrategy::insertIntoListContainer(Handle listContainerEntity, Handle entity, int index) { + auto listContainerObject = handle_cast(listContainerEntity); + if (index < 0 || index > listContainerObject->list.size()) { + return false; + } + auto entityObj = handle_cast(entity); + if (entityObj->parent() != this) { + return false; + } + m_undoStack->push(new InsertIntoListContainerCommand(this, listContainerEntity, entity, index)); + return true; + } + + bool UndoableModelStrategy::insertIntoMapContainer(Handle mapContainerEntity, Handle entity, const QString &key) { + auto entityObj = handle_cast(entity); + if (entityObj->parent() != this) { + return false; + } + m_undoStack->push(new InsertIntoMapContainerCommand(this, mapContainerEntity, entity, key)); + 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); + auto object = reinterpret_cast(entity.d); + if (!sequenceContainerObject->sequence.contains(object)) { + return {}; + } + auto cmd = new TakeFromSequenceContainerCommand(this, sequenceContainerEntity, entity); + m_undoStack->push(cmd); + return entity; + } + + Handle UndoableModelStrategy::takeFromListContainer(Handle listContainerEntity, int index) { + auto listContainerObject = handle_cast(listContainerEntity); + if (index < 0 || index >= listContainerObject->list.size()) { + return {}; + } + auto cmd = new TakeFromListContainerCommand(this, listContainerEntity, index); + m_undoStack->push(cmd); + return cmd->entity(); + } + + Handle UndoableModelStrategy::takeFromMapContainer(Handle mapContainerEntity, + const QString &key) { + auto mapContainerObject = handle_cast(mapContainerEntity); + if (!mapContainerObject->map.contains(key)) { + return {}; + } + auto cmd = new TakeFromMapContainerCommand(this, mapContainerEntity, key); + m_undoStack->push(cmd); + return cmd->entity(); + } + + bool UndoableModelStrategy::rotateListContainer(Handle listContainerEntity, int leftIndex, int middleIndex, + int rightIndex) { + auto listContainerObject = handle_cast(listContainerEntity); + if (leftIndex < 0 || leftIndex > listContainerObject->list.size() || middleIndex < leftIndex || + middleIndex > listContainerObject->list.size() || rightIndex < middleIndex || + rightIndex > listContainerObject->list.size()) { + return false; + } + m_undoStack->push(new RotateListContainerCommand(this, listContainerEntity, leftIndex, middleIndex, rightIndex)); + return true; + } + + void UndoableModelStrategy::setEntityProperty(Handle entity, Property property, const QVariant &value) { + auto object = handle_cast(entity); + Q_ASSERT(isEntityTypeAndPropertyTypeCompatible(object->type, property)); + m_undoStack->push(new SetEntityPropertyCommand(this, entity, property, value)); + } + + bool UndoableModelStrategy::spliceDataArray(Handle dataArrayEntity, int index, int length, + const QVariantList &values) { + auto &data = handle_cast(dataArrayEntity)->data; + if (index < 0 || index > data.size() || length < 0 || index + length > data.size()) { + return false; + } + m_undoStack->push(new SpliceDataArrayCommand(this, dataArrayEntity, index, length, values)); + return true; + } + + bool UndoableModelStrategy::rotateDataArray(Handle dataArrayEntity, int leftIndex, int middleIndex, + int rightIndex) { + auto &data = handle_cast(dataArrayEntity)->data; + if (leftIndex < 0 || leftIndex > data.size() || middleIndex < leftIndex || middleIndex > data.size() || + rightIndex < middleIndex || rightIndex > data.size()) { + return false; + } + m_undoStack->push(new RotateDataArrayCommand(this, dataArrayEntity, leftIndex, middleIndex, rightIndex)); + return true; + } + +} diff --git a/src/libs/application/dspxmodel/src/UndoableModelStrategy.h b/src/libs/application/dspxmodel/src/UndoableModelStrategy.h new file mode 100644 index 00000000..ec48c5f8 --- /dev/null +++ b/src/libs/application/dspxmodel/src/UndoableModelStrategy.h @@ -0,0 +1,38 @@ +#ifndef DIFFSCOPE_DSPX_MODEL_UNDOABLEMODELSTRATEGY_H +#define DIFFSCOPE_DSPX_MODEL_UNDOABLEMODELSTRATEGY_H + +#include + +class QUndoStack; + +namespace dspx { + + class DSPX_MODEL_EXPORT UndoableModelStrategy : public BasicModelStrategy { + Q_OBJECT + public: + explicit UndoableModelStrategy(QObject *parent = nullptr); + ~UndoableModelStrategy() override; + + QUndoStack *undoStack() const; + + Handle createEntity(Entity entityType) override; + void destroyEntity(Handle entity) override; + 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; + bool rotateListContainer(Handle listContainerEntity, int leftIndex, int middleIndex, int rightIndex) override; + void setEntityProperty(Handle entity, Property property, const QVariant &value) override; + bool spliceDataArray(Handle dataContainerEntity, int index, int length, const QVariantList &values) override; + bool rotateDataArray(Handle dataContainerEntity, int leftIndex, int middleIndex, int rightIndex) override; + + private: + QUndoStack *m_undoStack; + }; + +} + +#endif // DIFFSCOPE_DSPX_MODEL_UNDOABLEMODELSTRATEGY_H diff --git a/src/libs/application/dspxmodel/src/UndoableModelStrategy_p.h b/src/libs/application/dspxmodel/src/UndoableModelStrategy_p.h new file mode 100644 index 00000000..dd6737f3 --- /dev/null +++ b/src/libs/application/dspxmodel/src/UndoableModelStrategy_p.h @@ -0,0 +1,243 @@ +#ifndef DIFFSCOPE_DSPX_MODEL_UNDOABLEMODELSTRATEGY_P_H +#define DIFFSCOPE_DSPX_MODEL_UNDOABLEMODELSTRATEGY_P_H + +#include +#include + +#include +#include + +namespace dspx { + + // Helper to cast handle to internal object + template + inline T *handle_cast(Handle entity) { + return reinterpret_cast(entity.d); + } + + //================================================================ + // Create/Destroy Commands + //================================================================ + + class CreateEntityCommand : public QUndoCommand { + public: + CreateEntityCommand(UndoableModelStrategy *strategy, BasicModelStrategy::Entity entityType, + QUndoCommand *parent = nullptr); + ~CreateEntityCommand() override; + void undo() override; + void redo() override; + Handle entity() const; + + private: + UndoableModelStrategy *m_strategy; + BasicModelStrategy::Entity m_entityType; + QPointer m_object; + bool m_undone = false; + }; + + class DestroyEntityCommand : public QUndoCommand { + public: + DestroyEntityCommand(UndoableModelStrategy *strategy, Handle entity, + QUndoCommand *parent = nullptr); + ~DestroyEntityCommand() override; + void undo() override; + void redo() override; + + private: + UndoableModelStrategy *m_strategy; + QPointer m_object; + bool m_undone = false; + void recursivelyDestroy(BasicModelStrategyEntity *object); + void recursivelyCreate(BasicModelStrategyEntity *object); + }; + + //================================================================ + // Container Commands + //================================================================ + + class InsertIntoSequenceContainerCommand : public QUndoCommand { + public: + InsertIntoSequenceContainerCommand(UndoableModelStrategy *strategy, + Handle sequenceContainerEntity, + Handle entity, QUndoCommand *parent = nullptr); + ~InsertIntoSequenceContainerCommand() override = default; + void undo() override; + void redo() override; + + private: + UndoableModelStrategy *m_strategy; + Handle m_container; + Handle m_entity; + }; + + class TakeFromSequenceContainerCommand : public QUndoCommand { + public: + TakeFromSequenceContainerCommand(UndoableModelStrategy *strategy, + Handle sequenceContainerEntity, + Handle entity, QUndoCommand *parent = nullptr); + ~TakeFromSequenceContainerCommand() override; + void undo() override; + void redo() override; + + private: + UndoableModelStrategy *m_strategy; + Handle m_container; + QPointer m_object; + 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, + Handle entity, int index, QUndoCommand *parent = nullptr); + ~InsertIntoListContainerCommand() override = default; + void undo() override; + void redo() override; + + private: + UndoableModelStrategy *m_strategy; + Handle m_container; + Handle m_entity; + int m_index; + }; + + class TakeFromListContainerCommand : public QUndoCommand { + public: + TakeFromListContainerCommand(UndoableModelStrategy *strategy, Handle listContainerEntity, + int index, QUndoCommand *parent = nullptr); + ~TakeFromListContainerCommand() override; + void undo() override; + void redo() override; + Handle entity() const; + + private: + UndoableModelStrategy *m_strategy; + Handle m_container; + QPointer m_object; + int m_index; + bool m_undone = false; + }; + + class InsertIntoMapContainerCommand : public QUndoCommand { + public: + InsertIntoMapContainerCommand(UndoableModelStrategy *strategy, Handle mapContainerEntity, + Handle entity, const QString &key, + QUndoCommand *parent = nullptr); + ~InsertIntoMapContainerCommand() override; + void undo() override; + void redo() override; + + private: + UndoableModelStrategy *m_strategy; + Handle m_container; + Handle m_entity; + QString m_key; + QPointer m_oldObject; + }; + + class TakeFromMapContainerCommand : public QUndoCommand { + public: + TakeFromMapContainerCommand(UndoableModelStrategy *strategy, Handle mapContainerEntity, + const QString &key, QUndoCommand *parent = nullptr); + ~TakeFromMapContainerCommand() override; + void undo() override; + void redo() override; + Handle entity() const; + + private: + UndoableModelStrategy *m_strategy; + Handle m_container; + QPointer m_object; + QString m_key; + bool m_undone = false; + }; + + class RotateListContainerCommand : public QUndoCommand { + public: + RotateListContainerCommand(UndoableModelStrategy *strategy, Handle listContainerEntity, + int left, int middle, int right, QUndoCommand *parent = nullptr); + ~RotateListContainerCommand() override = default; + void undo() override; + void redo() override; + + private: + UndoableModelStrategy *m_strategy; + Handle m_container; + int m_left, m_middle, m_right; + }; + + //================================================================ + // Property & Data Commands + //================================================================ + + class SetEntityPropertyCommand : public QUndoCommand { + public: + SetEntityPropertyCommand(UndoableModelStrategy *strategy, Handle entity, + BasicModelStrategy::Property property, const QVariant &value, + QUndoCommand *parent = nullptr); + ~SetEntityPropertyCommand() override = default; + void undo() override; + void redo() override; + bool mergeWith(const QUndoCommand *command) override; + int id() const override; + + private: + UndoableModelStrategy *m_strategy; + Handle m_entity; + BasicModelStrategy::Property m_property; + QVariant m_newValue; + QVariant m_oldValue; + }; + + class SpliceDataArrayCommand : public QUndoCommand { + public: + SpliceDataArrayCommand(UndoableModelStrategy *strategy, Handle dataContainerEntity, + int index, int length, const QVariantList &values, QUndoCommand *parent = nullptr); + ~SpliceDataArrayCommand() override = default; + void undo() override; + void redo() override; + + private: + UndoableModelStrategy *m_strategy; + Handle m_container; + int m_index; + int m_length; + QVariantList m_values; + QVariantList m_oldValues; + }; + + class RotateDataArrayCommand : public QUndoCommand { + public: + RotateDataArrayCommand(UndoableModelStrategy *strategy, Handle dataContainerEntity, + int left, int middle, int right, QUndoCommand *parent = nullptr); + ~RotateDataArrayCommand() override = default; + void undo() override; + void redo() override; + + private: + UndoableModelStrategy *m_strategy; + Handle m_container; + int m_left, m_middle, m_right; + }; + +} + +#endif // DIFFSCOPE_DSPX_MODEL_UNDOABLEMODELSTRATEGY_P_H diff --git a/src/libs/application/dspxmodel/src/Vibrato.cpp b/src/libs/application/dspxmodel/src/Vibrato.cpp index b18b21c2..95c416a3 100644 --- a/src/libs/application/dspxmodel/src/Vibrato.cpp +++ b/src/libs/application/dspxmodel/src/Vibrato.cpp @@ -1,14 +1,12 @@ #include "Vibrato.h" - #include -#include #include -#include #include -#include #include +#include +#include namespace dspx { @@ -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 { @@ -240,4 +173,4 @@ namespace dspx { } -#include "moc_Vibrato.cpp" \ No newline at end of file +#include "moc_Vibrato.cpp" diff --git a/src/libs/application/dspxmodel/src/Vibrato.h b/src/libs/application/dspxmodel/src/Vibrato.h index 3e2bf570..695b492f 100644 --- a/src/libs/application/dspxmodel/src/Vibrato.h +++ b/src/libs/application/dspxmodel/src/Vibrato.h @@ -1,9 +1,10 @@ #ifndef DIFFSCOPE_DSPX_MODEL_VIBRATO_H #define DIFFSCOPE_DSPX_MODEL_VIBRATO_H -#include #include +#include + #include namespace QDspx { @@ -24,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; @@ -70,9 +71,8 @@ namespace dspx { explicit Vibrato(Handle handle, Model *model); void handleProxySetEntityProperty(int property, const QVariant &value); QScopedPointer d_ptr; - }; } -#endif //DIFFSCOPE_DSPX_MODEL_VIBRATO_H \ No newline at end of file +#endif //DIFFSCOPE_DSPX_MODEL_VIBRATO_H diff --git a/src/libs/application/dspxmodel/src/VibratoPointDataArray.cpp b/src/libs/application/dspxmodel/src/VibratoPointDataArray.cpp index 43c26cd8..45af4508 100644 --- a/src/libs/application/dspxmodel/src/VibratoPointDataArray.cpp +++ b/src/libs/application/dspxmodel/src/VibratoPointDataArray.cpp @@ -18,6 +18,8 @@ namespace dspx { Q_ASSERT(model->strategy()->getEntityType(handle) == ModelStrategy::ED_VibratoPoints); d->q_ptr = this; d->pModel = ModelPrivate::get(model); + + d->init(model->strategy()->sliceDataArray(handle, 0, model->strategy()->getSizeOfDataArray(handle))); } VibratoPointDataArray::~VibratoPointDataArray() = default; @@ -74,4 +76,4 @@ namespace dspx { } } -#include "moc_VibratoPointDataArray.cpp" \ No newline at end of file +#include "moc_VibratoPointDataArray.cpp" diff --git a/src/libs/application/dspxmodel/src/VibratoPointDataArray.h b/src/libs/application/dspxmodel/src/VibratoPointDataArray.h index e62d6f1c..28449656 100644 --- a/src/libs/application/dspxmodel/src/VibratoPointDataArray.h +++ b/src/libs/application/dspxmodel/src/VibratoPointDataArray.h @@ -47,7 +47,6 @@ namespace dspx { friend class ModelPrivate; explicit VibratoPointDataArray(Handle handle, Model *model); QScopedPointer d_ptr; - }; } diff --git a/src/libs/application/dspxmodel/src/VibratoPoints.cpp b/src/libs/application/dspxmodel/src/VibratoPoints.cpp index 8ddaa287..8271cb3c 100644 --- a/src/libs/application/dspxmodel/src/VibratoPoints.cpp +++ b/src/libs/application/dspxmodel/src/VibratoPoints.cpp @@ -1,13 +1,13 @@ #include "VibratoPoints.h" -#include #include +#include #include -#include #include #include +#include namespace dspx { @@ -53,7 +53,6 @@ namespace dspx { freq()->fromQDspx(vibratoPoints.freq); } - } -#include "moc_VibratoPoints.cpp" \ No newline at end of file +#include "moc_VibratoPoints.cpp" diff --git a/src/libs/application/dspxmodel/src/VibratoPoints.h b/src/libs/application/dspxmodel/src/VibratoPoints.h index 79ffa1a3..7ff8fd35 100644 --- a/src/libs/application/dspxmodel/src/VibratoPoints.h +++ b/src/libs/application/dspxmodel/src/VibratoPoints.h @@ -1,9 +1,10 @@ #ifndef DIFFSCOPE_DSPX_MODEL_VIBRATOPOINTS_H #define DIFFSCOPE_DSPX_MODEL_VIBRATOPOINTS_H -#include #include +#include + #include namespace QDspx { @@ -39,9 +40,8 @@ namespace dspx { friend class ModelPrivate; explicit VibratoPoints(Handle handle, Model *model); QScopedPointer d_ptr; - }; } -#endif //DIFFSCOPE_DSPX_MODEL_VIBRATOPOINTS_H \ No newline at end of file +#endif //DIFFSCOPE_DSPX_MODEL_VIBRATOPOINTS_H diff --git a/src/libs/application/dspxmodel/src/Workspace.cpp b/src/libs/application/dspxmodel/src/Workspace.cpp index 7b95a80b..576897c8 100644 --- a/src/libs/application/dspxmodel/src/Workspace.cpp +++ b/src/libs/application/dspxmodel/src/Workspace.cpp @@ -1,25 +1,25 @@ -#include "Workspace.h" - #include "ModelStrategy.h" +#include "Workspace.h" #include -#include -#include #include +#include +#include namespace dspx { class WorkspacePrivate : public MapData { Q_DECLARE_PUBLIC(Workspace) }; - Workspace::Workspace(Handle handle, Model *model) : EntityObject(handle, model), d_ptr(new WorkspacePrivate) { Q_D(Workspace); Q_ASSERT(model->strategy()->getEntityType(handle) == ModelStrategy::EM_Workspace); d->q_ptr = this; d->pModel = ModelPrivate::get(model); + + d->init(model->strategy()->getEntitiesFromMapContainer(handle)); } Workspace::~Workspace() = default; @@ -92,4 +92,4 @@ namespace dspx { } -#include "moc_Workspace.cpp" \ No newline at end of file +#include "moc_Workspace.cpp" diff --git a/src/libs/application/dspxmodel/src/WorkspaceInfo.cpp b/src/libs/application/dspxmodel/src/WorkspaceInfo.cpp index 5c4fea53..06af7875 100644 --- a/src/libs/application/dspxmodel/src/WorkspaceInfo.cpp +++ b/src/libs/application/dspxmodel/src/WorkspaceInfo.cpp @@ -1,7 +1,7 @@ #include "WorkspaceInfo.h" -#include #include +#include #include #include @@ -49,4 +49,4 @@ namespace dspx { } -#include "moc_WorkspaceInfo.cpp" \ No newline at end of file +#include "moc_WorkspaceInfo.cpp" diff --git a/src/libs/application/dspxmodel/src/WorkspaceInfo.h b/src/libs/application/dspxmodel/src/WorkspaceInfo.h index 8f357e46..a382764f 100644 --- a/src/libs/application/dspxmodel/src/WorkspaceInfo.h +++ b/src/libs/application/dspxmodel/src/WorkspaceInfo.h @@ -1,9 +1,10 @@ #ifndef DIFFSCOPE_DSPX_MODEL_WORKSPACEINFO_H #define DIFFSCOPE_DSPX_MODEL_WORKSPACEINFO_H -#include #include +#include + class QJsonObject; namespace dspx { @@ -32,9 +33,8 @@ namespace dspx { friend class ModelPrivate; explicit WorkspaceInfo(Handle handle, Model *model); QScopedPointer d_ptr; - }; } -#endif //DIFFSCOPE_DSPX_MODEL_WORKSPACEINFO_H \ No newline at end of file +#endif //DIFFSCOPE_DSPX_MODEL_WORKSPACEINFO_H diff --git a/src/libs/application/dspxmodel/src/rangehelpers.h b/src/libs/application/dspxmodel/src/rangehelpers.h new file mode 100644 index 00000000..7843ca3a --- /dev/null +++ b/src/libs/application/dspxmodel/src/rangehelpers.h @@ -0,0 +1,121 @@ +#ifndef DIFFSCOPE_DSPX_MODEL_RANGEHELPERS_H +#define DIFFSCOPE_DSPX_MODEL_RANGEHELPERS_H + +#include +#include + +namespace dspx::impl { + + template + class SequenceRange { + public: + SequenceRange(const SequenceType *sequence) : m_sequence(sequence) { + } + + using ItemType = std::remove_pointer_t().firstItem())>; + + class iterator { + public: + using iterator_category = std::bidirectional_iterator_tag; + using value_type = ItemType *; + using difference_type = std::ptrdiff_t; + 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) { + } + + value_type operator*() const { + return m_item; + } + + iterator &operator++() { + if (m_item) { + m_item = m_sequence->nextItem(m_item); + } + return *this; + } + + iterator operator++(int) { + iterator temp = *this; + ++(*this); + return temp; + } + + iterator &operator--() { + if (m_item) { + m_item = m_sequence->previousItem(m_item); + } else { + m_item = m_sequence->lastItem(); + } + return *this; + } + + iterator operator--(int) { + iterator temp = *this; + --(*this); + return temp; + } + + bool operator==(const iterator &other) const { + return m_item == other.m_item; + } + + bool operator!=(const iterator &other) const { + return m_item != other.m_item; + } + + private: + const SequenceType *m_sequence; + ItemType *m_item; + }; + + using const_iterator = iterator; + using reverse_iterator = std::reverse_iterator; + using const_reverse_iterator = std::reverse_iterator; + + iterator begin() const { + return iterator(m_sequence, m_sequence->firstItem()); + } + + iterator end() const { + return iterator(m_sequence, nullptr); + } + + const_iterator cbegin() const { + return begin(); + } + + const_iterator cend() const { + return end(); + } + + reverse_iterator rbegin() const { + return reverse_iterator(end()); + } + + reverse_iterator rend() const { + return reverse_iterator(begin()); + } + + const_reverse_iterator crbegin() const { + return rbegin(); + } + + const_reverse_iterator crend() const { + return rend(); + } + + private: + const SequenceType *m_sequence; + }; + +} + +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.cpp b/src/libs/application/dspxmodel/src/selectionmodel/AnchorNodeSelectionModel.cpp new file mode 100644 index 00000000..7d6fd145 --- /dev/null +++ b/src/libs/application/dspxmodel/src/selectionmodel/AnchorNodeSelectionModel.cpp @@ -0,0 +1,44 @@ +#include "AnchorNodeSelectionModel.h" +#include "AnchorNodeSelectionModel_p.h" + +#include +#include +#include + +namespace dspx { + + AnchorNodeSelectionModel::AnchorNodeSelectionModel(QObject *parent) : QObject(parent), d_ptr(new AnchorNodeSelectionModelPrivate) { + Q_D(AnchorNodeSelectionModel); + d->q_ptr = this; + } + + AnchorNodeSelectionModel::~AnchorNodeSelectionModel() = default; + + AnchorNode *AnchorNodeSelectionModel::currentItem() const { + Q_D(const AnchorNodeSelectionModel); + return d->currentItem; + } + + QList AnchorNodeSelectionModel::selectedItems() const { + Q_D(const AnchorNodeSelectionModel); + return d->selectedItems; + } + + int AnchorNodeSelectionModel::selectedCount() const { + Q_D(const AnchorNodeSelectionModel); + return d->selectedItems.size(); + } + + QList AnchorNodeSelectionModel::paramCurvesAnchorWithSelectedItems() const { + Q_D(const AnchorNodeSelectionModel); + return d->paramCurvesAnchorWithSelectedItems; + } + + ParamCurveSequence *AnchorNodeSelectionModel::paramCurveSequenceWithSelectedItems() const { + Q_D(const AnchorNodeSelectionModel); + return d->paramCurveSequenceWithSelectedItems; + } + +} + +#include "moc_AnchorNodeSelectionModel.cpp" diff --git a/src/libs/application/dspxmodel/src/selectionmodel/AnchorNodeSelectionModel.h b/src/libs/application/dspxmodel/src/selectionmodel/AnchorNodeSelectionModel.h new file mode 100644 index 00000000..f0d31081 --- /dev/null +++ b/src/libs/application/dspxmodel/src/selectionmodel/AnchorNodeSelectionModel.h @@ -0,0 +1,54 @@ +#ifndef DIFFSCOPE_DSPX_MODEL_ANCHORNODESELECTIONMODEL_H +#define DIFFSCOPE_DSPX_MODEL_ANCHORNODESELECTIONMODEL_H + +#include +#include +#include + +#include + +namespace dspx { + + class AnchorNode; + class ParamCurveAnchor; + class ParamCurveSequence; + class AnchorNodeSelectionModelPrivate; + class SelectionModel; + + 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) + Q_PROPERTY(QList selectedItems READ selectedItems NOTIFY selectedItemsChanged) + Q_PROPERTY(int selectedCount READ selectedCount NOTIFY selectedCountChanged) + Q_PROPERTY(QList paramCurvesAnchorWithSelectedItems READ paramCurvesAnchorWithSelectedItems NOTIFY paramCurvesAnchorWithSelectedItemsChanged) + Q_PROPERTY(ParamCurveSequence *paramCurveSequenceWithSelectedItems READ paramCurveSequenceWithSelectedItems NOTIFY paramCurveSequenceWithSelectedItemsChanged) + + public: + ~AnchorNodeSelectionModel() override; + + AnchorNode *currentItem() const; + QList selectedItems() const; + int selectedCount() const; + QList paramCurvesAnchorWithSelectedItems() const; + ParamCurveSequence *paramCurveSequenceWithSelectedItems() const; + + Q_SIGNALS: + void currentItemChanged(); + void selectedItemsChanged(); + void selectedCountChanged(); + void paramCurvesAnchorWithSelectedItemsChanged(); + void paramCurveSequenceWithSelectedItemsChanged(); + + private: + friend class SelectionModel; + explicit AnchorNodeSelectionModel(QObject *parent = nullptr); + QScopedPointer d_ptr; + }; + +} + +#endif //DIFFSCOPE_DSPX_MODEL_ANCHORNODESELECTIONMODEL_H \ No newline at end of file diff --git a/src/libs/application/dspxmodel/src/selectionmodel/AnchorNodeSelectionModel_p.h b/src/libs/application/dspxmodel/src/selectionmodel/AnchorNodeSelectionModel_p.h new file mode 100644 index 00000000..3023358e --- /dev/null +++ b/src/libs/application/dspxmodel/src/selectionmodel/AnchorNodeSelectionModel_p.h @@ -0,0 +1,20 @@ +#ifndef DIFFSCOPE_DSPX_MODEL_ANCHORNODESELECTIONMODEL_P_H +#define DIFFSCOPE_DSPX_MODEL_ANCHORNODESELECTIONMODEL_P_H + +#include + +namespace dspx { + + class AnchorNodeSelectionModelPrivate { + Q_DECLARE_PUBLIC(AnchorNodeSelectionModel) + public: + AnchorNodeSelectionModel *q_ptr; + AnchorNode *currentItem = nullptr; + QList selectedItems; + QList paramCurvesAnchorWithSelectedItems; + ParamCurveSequence *paramCurveSequenceWithSelectedItems = nullptr; + }; + +} + +#endif //DIFFSCOPE_DSPX_MODEL_ANCHORNODESELECTIONMODEL_P_H \ No newline at end of file diff --git a/src/libs/application/dspxmodel/src/selectionmodel/ClipSelectionModel.cpp b/src/libs/application/dspxmodel/src/selectionmodel/ClipSelectionModel.cpp new file mode 100644 index 00000000..dad78e12 --- /dev/null +++ b/src/libs/application/dspxmodel/src/selectionmodel/ClipSelectionModel.cpp @@ -0,0 +1,281 @@ +#include "ClipSelectionModel.h" +#include "ClipSelectionModel_p.h" + +#include +#include + +#include +#include +#include +#include +#include + +namespace dspx { + + 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::connectItem(Clip *item) { + if (connectedItems.contains(item)) { + return; + } + 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); + } + + 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); + } + } + + connectedItems.remove(item); + } + + 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; + } + + 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; + } + + bool ClipSelectionModelPrivate::clearSelection() { + if (selectedItems.isEmpty()) { + return false; + } + const auto items = selectedItems.values(); + bool selectionChanged = false; + for (auto clip : items) { + selectionChanged |= removeFromSelection(clip); + } + return selectionChanged; + } + + 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::setCurrentItem(Clip *item) { + if (!isValidItem(item)) { + item = nullptr; + } + if (currentItem == item) { + return; + } + 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) { + Q_D(ClipSelectionModel); + d->q_ptr = this; + d->selectionModel = parent; + } + + ClipSelectionModel::~ClipSelectionModel() = default; + + Clip *ClipSelectionModel::currentItem() const { + Q_D(const ClipSelectionModel); + return d->currentItem; + } + + QList ClipSelectionModel::selectedItems() const { + Q_D(const ClipSelectionModel); + return d->selectedItems.values(); + } + + int ClipSelectionModel::selectedCount() const { + Q_D(const ClipSelectionModel); + return d->selectedItems.size(); + } + + QList ClipSelectionModel::clipSequencesWithSelectedItems() const { + Q_D(const ClipSelectionModel); + return d->clipSequencesWithSelectedItems.keys(); + } + + bool ClipSelectionModel::isItemSelected(Clip *item) const { + Q_D(const ClipSelectionModel); + 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 new file mode 100644 index 00000000..cf89890e --- /dev/null +++ b/src/libs/application/dspxmodel/src/selectionmodel/ClipSelectionModel.h @@ -0,0 +1,51 @@ +#ifndef DIFFSCOPE_DSPX_MODEL_CLIPSELECTIONMODEL_H +#define DIFFSCOPE_DSPX_MODEL_CLIPSELECTIONMODEL_H + +#include + +#include + +namespace dspx { + + class SelectionModel; + class Clip; + class ClipSequence; + class ClipSelectionModelPrivate; + + 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) + Q_PROPERTY(QList selectedItems READ selectedItems NOTIFY selectedItemsChanged) + Q_PROPERTY(int selectedCount READ selectedCount NOTIFY selectedCountChanged) + Q_PROPERTY(QList clipSequencesWithSelectedItems READ clipSequencesWithSelectedItems NOTIFY clipSequencesWithSelectedItemsChanged) + + public: + ~ClipSelectionModel() override; + + Clip *currentItem() const; + QList selectedItems() const; + int selectedCount() const; + QList clipSequencesWithSelectedItems() const; + + Q_INVOKABLE bool isItemSelected(Clip *item) const; + + Q_SIGNALS: + void currentItemChanged(); + void selectedItemsChanged(); + void selectedCountChanged(); + void clipSequencesWithSelectedItemsChanged(); + void itemSelected(Clip *item, bool selected); + + private: + friend class SelectionModel; + explicit ClipSelectionModel(SelectionModel *parent = nullptr); + QScopedPointer d_ptr; + }; + +} + +#endif //DIFFSCOPE_DSPX_MODEL_CLIPSELECTIONMODEL_H \ No newline at end of file diff --git a/src/libs/application/dspxmodel/src/selectionmodel/ClipSelectionModel_p.h b/src/libs/application/dspxmodel/src/selectionmodel/ClipSelectionModel_p.h new file mode 100644 index 00000000..a499ad37 --- /dev/null +++ b/src/libs/application/dspxmodel/src/selectionmodel/ClipSelectionModel_p.h @@ -0,0 +1,40 @@ +#ifndef DIFFSCOPE_DSPX_MODEL_CLIPSELECTIONMODEL_P_H +#define DIFFSCOPE_DSPX_MODEL_CLIPSELECTIONMODEL_P_H + +#include + +#include +#include + +namespace dspx { + + class SelectionModel; + class ClipSequence; + + class ClipSelectionModelPrivate { + Q_DECLARE_PUBLIC(ClipSelectionModel) + public: + ClipSelectionModel *q_ptr; + SelectionModel *selectionModel; + QSet selectedItems; + Clip *currentItem = nullptr; + QSet connectedItems; + + QHash clipToClipSequence; + QHash> clipSequencesWithSelectedItems; + + 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); + }; + +} + +#endif //DIFFSCOPE_DSPX_MODEL_CLIPSELECTIONMODEL_P_H \ No newline at end of file diff --git a/src/libs/application/dspxmodel/src/selectionmodel/GenericGlobalItemSelectionModelData_p.h b/src/libs/application/dspxmodel/src/selectionmodel/GenericGlobalItemSelectionModelData_p.h new file mode 100644 index 00000000..57acb1e6 --- /dev/null +++ b/src/libs/application/dspxmodel/src/selectionmodel/GenericGlobalItemSelectionModelData_p.h @@ -0,0 +1,197 @@ +#ifndef DIFFSCOPE_DSPX_MODEL_GENERICGLOBALITEMSELECTIONMODELDATA_P_H +#define DIFFSCOPE_DSPX_MODEL_GENERICGLOBALITEMSELECTIONMODELDATA_P_H + +#include + +#include + +namespace dspx { + + template < + class PublicClass, + class PrivateClass, + class Item, + void (Item::*superItemChangedSignal)(), + class SuperItem = void + > + [[deprecated]] class GenericGlobalItemSelectionModelData { + public: + PublicClass *q_ptr; + SelectionModel *selectionModel; + Item *currentItem = nullptr; + QSet selectedItems; + SuperItem *superItem = nullptr; + + void select(Item *item, SelectionModel::SelectionCommand command) { + auto q = q_ptr; + bool selectionUpdatedFlag = false; + bool currentItemUpdatedFlag = false; + + if (item && !static_cast(this)->isAddedToModel(item)) { + return; + } + + if (auto currentSuperItem = static_cast(this)->getSuperItem(item); superItem && currentSuperItem && superItem != currentSuperItem) { + selectionUpdatedFlag = true; + currentItemUpdatedFlag = true; + clearSelection(); + setCurrentItem(nullptr); + static_cast(this)->clearSuperItem(); + } + + if ((command & SelectionModel::ClearPreviousSelection)) { + if (!selectedItems.isEmpty()) { + selectionUpdatedFlag = true; + } + clearSelection(); + } + + if (command & SelectionModel::SetCurrentItem) { + if (currentItem != item) { + currentItemUpdatedFlag = true; + setCurrentItem(item); + } + } + + // Update selection + if (item) { + if ((command & SelectionModel::Select) && (command & SelectionModel::Deselect)) { + // Toggle + selectionUpdatedFlag = true; + if (selectedItems.contains(item)) { + removeFromSelection(item); + } else { + addToSelection(item); + } + } else if (command & SelectionModel::Select) { + // Select only + if (!selectedItems.contains(item)) { + selectionUpdatedFlag = true; + addToSelection(item); + } + } else if (command & SelectionModel::Deselect) { + // Deselect only + if (selectedItems.contains(item)) { + selectionUpdatedFlag = true; + removeFromSelection(item); + } + } + } + + // Emit signals + if (selectionUpdatedFlag) { + Q_EMIT q->selectedItemsChanged(); + Q_EMIT q->selectedCountChanged(); + } + if (currentItemUpdatedFlag) { + Q_EMIT q->currentItemChanged(); + } + } + + void setCurrentItem(Item *item) { + auto q = q_ptr; + if (currentItem) { + if (!selectedItems.contains(currentItem)) { + QObject::disconnect(currentItem, nullptr, q, nullptr); + } + } + currentItem = item; + static_cast(this)->updateSuperItem(getSuperItem(item)); + if (item) { + if (!selectedItems.contains(item)) { + QObject::connect(item, superItemChangedSignal, q, [item, this] { + if (static_cast(this)->isAddedToModel(item)) { + static_cast(this)->updateAssociation(item); + } else { + updateOnItemRemoved(item); + } + }); + QObject::connect(item, &QObject::destroyed, q, [item, this] { + updateOnItemRemoved(item); + }); + } + } + } + + void addToSelection(Item *item) { + auto q = q_ptr; + if (currentItem != item) { + QObject::connect(item, superItemChangedSignal, q, [item, this] { + if (static_cast(this)->isAddedToModel(item)) { + static_cast(this)->updateAssociation(item); + } else { + updateOnItemRemoved(item); + } + }); + QObject::connect(item, &QObject::destroyed, q, [item, this] { + updateOnItemRemoved(item); + }); + } + selectedItems.insert(item); + static_cast(this)->updateSuperItem(getSuperItem(item)); + static_cast(this)->updateAssociation(item); + Q_EMIT q->itemSelected(item, true); + } + void removeFromSelection(Item *item) { + auto q = q_ptr; + if (currentItem != item) { + QObject::disconnect(item, nullptr, q, nullptr); + } + selectedItems.remove(item); + static_cast(this)->removeAssociation(item); + Q_EMIT q->itemSelected(item, false); + } + void clearSelection() { + auto q = q_ptr; + for (auto item : selectedItems) { + if (currentItem != item) { + QObject::disconnect(item, nullptr, q, nullptr); + } + Q_EMIT q->itemSelected(item, false); + } + selectedItems.clear(); + static_cast(this)->clearAssociation(); + } + void updateOnItemRemoved(Item *item) { + auto q = q_ptr; + bool selectionUpdatedFlag = false; + bool currentItemUpdatedFlag = false; + if (currentItem == item) { + currentItemUpdatedFlag = true; + setCurrentItem(nullptr); + } + if (selectedItems.contains(item)) { + selectionUpdatedFlag = true; + removeFromSelection(item); + } + if (selectionUpdatedFlag) { + Q_EMIT q->selectedItemsChanged(); + Q_EMIT q->selectedCountChanged(); + } + if (currentItemUpdatedFlag) { + Q_EMIT q->currentItemChanged(); + } + } + + void clearAll() { + auto q = q_ptr; + clearSelection(); + setCurrentItem(nullptr); + static_cast(this)->clearSuperItem(); + Q_EMIT q->selectedItemsChanged(); + Q_EMIT q->selectedCountChanged(); + Q_EMIT q->currentItemChanged(); + } + + static void updateAssociation(Item *) {} + static void removeAssociation(Item *) {} + static void clearAssociation() {} + static SuperItem *getSuperItem(Item *) { return nullptr; } + void clearSuperItem() { superItem = nullptr; } + void updateSuperItem(SuperItem *superItem_) { superItem = superItem_; } + + }; + +} + +#endif //DIFFSCOPE_DSPX_MODEL_GENERICGLOBALITEMSELECTIONMODELDATA_P_H diff --git a/src/libs/application/dspxmodel/src/selectionmodel/LabelSelectionModel.cpp b/src/libs/application/dspxmodel/src/selectionmodel/LabelSelectionModel.cpp new file mode 100644 index 00000000..3f395fc1 --- /dev/null +++ b/src/libs/application/dspxmodel/src/selectionmodel/LabelSelectionModel.cpp @@ -0,0 +1,176 @@ +#include "LabelSelectionModel.h" +#include "LabelSelectionModel_p.h" + +#include + +#include +#include +#include +#include +#include + +namespace dspx { + + 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