diff --git a/.github/actions/initialize-build-environment/action.yml b/.github/actions/initialize-build-environment/action.yml index 18aed6d3..12e10df1 100644 --- a/.github/actions/initialize-build-environment/action.yml +++ b/.github/actions/initialize-build-environment/action.yml @@ -39,7 +39,7 @@ runs: HOMEBREW_NO_INSTALL_CLEANUP: 1 run: | brew update - brew install ninja + brew install ninja ccache imagemagick create-dmg - name: Install Qt uses: jurplel/install-qt-action@v4 diff --git a/.github/actions/initialize-vcpkg/action.yml b/.github/actions/initialize-vcpkg/action.yml index e19dd5ab..1d083ef5 100644 --- a/.github/actions/initialize-vcpkg/action.yml +++ b/.github/actions/initialize-vcpkg/action.yml @@ -31,7 +31,7 @@ runs: QT_DIR: ${{ env.Qt_ROOT_DIR }}/lib/cmake/Qt6 Qt6_DIR: ${{ env.Qt_ROOT_DIR }}/lib/cmake/Qt6 VCPKG_CMAKE_RELEASE_BUILD_TYPE: "RelWithDebInfo" - VCPKG_KEEP_ENV_VARS: "QT_DIR;Qt6_DIR;VCPKG_CMAKE_RELEASE_BUILD_TYPE;VCPKG_BUILD_TYPE" + VCPKG_KEEP_ENV_VARS: "QT_DIR;Qt6_DIR;VCPKG_CMAKE_RELEASE_BUILD_TYPE;VCPKG_BUILD_TYPE;MACOSX_DEPLOYMENT_TARGET" shell: pwsh run: | if (!(Test-Path $env:VCPKG_DEFAULT_BINARY_CACHE)) { diff --git a/.github/instructions/viewmodel-binding.instructions.md b/.github/instructions/viewmodel-binding.instructions.md new file mode 100644 index 00000000..50eddd12 --- /dev/null +++ b/.github/instructions/viewmodel-binding.instructions.md @@ -0,0 +1,103 @@ +# View-Model ↔ Document Binding Prompt + +Use this prompt to guide new view-model bindings for future document elements. Keep property specifics out; focus on state design, controller-driven transitions, transactions, and synchronization patterns. Derive patterns from existing implementations (e.g., `TrackViewModelContextData`, `LabelViewModelContextData`, `TempoViewModelContextData`). + +## Core Goals +- Mirror document collections to view-model collections with clear ownership and mapping tables in both directions. +- Drive UI interactions through controllers; never mutate document state directly from QML. +- Guard against infinite loops by conditioning view→doc updates on active states. +- Wrap mutating operations in transactions with start/commit/abort semantics tied to state entry/exit. + +- Instrument every state with entry/exit logging (use `qCInfo` with a per-context logging category) to trace flows during debugging, even for states without handlers. + +## State Machine Design +- Build a `QStateMachine` with explicit states for idle, rubber-band selection, move/drag, edit/adjust flows, and per-property operations as needed. +- Keep transitions driven by signals emitted from interaction controllers and internal guards (e.g., started/not-started, commit/abort, finish). +- Example (move flow, list rotation): + - `idle` → `movePending` on `moveTransactionWillStart` (from controller). + - `movePending` → `moveProcessing` on `moveTransactionStarted`; → `idle` on `moveTransactionNotStarted`. + - `moveProcessing` → `moveCommitting` on `moveTransactionWillCommit`; → `moveAborting` on `moveTransactionWillAbort`. + - `moveCommitting`/`moveAborting` → `idle` (reset guards). +- Example (edit flow, text-like): + - `idle` → `namePending` on `nameTransactionWillStart`. + - `namePending` → `nameProgressing` on `nameTransactionStarted`; → `idle` if not started. + - `nameProgressing` → `nameCommitting` on commit; → `nameAborting` on abort. + - `nameCommitting`/`nameAborting` → `idle`. +- Rubber-band selection: `idle` → `rubberBandDragging` on start, back to `idle` on finish. + +## Controller-Driven Transitions +- Wire controller signals to emit local signals that the state machine consumes (two patterns): + 1) **Started/Committed/Aborted**: drag/move and text edits use started/commit/abort triplets. + 2) **Started/Finished**: toggle/slider/height edits use started/finished pairs. +- Set the current target item/index when the controller signals a start; clear it on commit/finish/abort handlers. +- For rotations or list moves: only propagate view→doc when in the move-processing state; otherwise apply doc→view rotations. + +## Transactions +- Begin a transaction when entering the corresponding "pending" state. Abort immediately if the controller could not start. +- On commit states: if the new value differs, write to the document and `commitTransaction` with a descriptive label; otherwise `abortTransaction`. +- On abort states: always `abortTransaction` and reset local guards (`target`, flags like `moveChanged`). +- Track whether any change occurred during move/drag (`moveChanged`) to avoid committing no-op transactions. + +## Synchronization Patterns +- Maintain bidirectional maps: `doc -> view` and `view -> doc`. Insert/remove bindings on collection signals (`itemInserted`/`itemRemoved`), not "aboutTo" when you need the item fully constructed. +- When binding a new item: + - Create the view-model item, insert into both maps and the view-model collection at the correct index. + - Connect doc→view signals to update view items, guarded by equality checks. + - Connect view→doc signals but gate them with state checks (only honor during the relevant progressing/doing states; otherwise revert the view to the doc value). + - Initialize view properties from the doc model after wiring connections. +- Selection sync: listen to document selection model `itemSelected` and mark the view item selected; initialize selection for pre-selected items after binding. +- Rotation sync: doc→view rotations apply when *not* moving; view→doc rotations apply only while the move state is active, and should mark a change flag. + +## Example Snippets +- **Doc→View guarded update** (avoid loops): + ```cpp + connect(control, &ControlType::propertyChanged, viewItem, [=](auto value) { + if (viewItem->property() == value) return; + viewItem->setProperty(value); + }); + ``` +- **View→Doc gated by state**: + ```cpp + connect(viewItem, &ViewType::propertyChanged, docItem, [=] { + if (!stateMachine->configuration().contains(propertyProgressingState)) { + viewItem->setProperty(docItem->property()); + return; + } + // defer actual write to commit handler + }); + ``` +- **Transaction commit handler**: + ```cpp + void ContextData::onNameCommittingStateEntered() { + if (!target || nameTxId == Invalid) { target = {}; return; } + auto viewItem = viewMap.value(target); + if (viewItem->name() == target->name()) { + tx->abortTransaction(nameTxId); + } else { + target->setName(viewItem->name()); + tx->commitTransaction(nameTxId, tr("Renaming item")); + } + nameTxId = {}; target = {}; + } + ``` +- **Rotate handling**: + ```cpp + connect(docList, &List::rotated, this, [=](int l, int m, int r) { + if (stateMachine->configuration().contains(moveProcessingState)) return; + viewList->rotate(l, m, r); + }); + connect(viewList, &ViewList::rotated, this, [=](int l, int m, int r) { + if (!stateMachine->configuration().contains(moveProcessingState)) return; + moveChanged = true; + docList->rotate(l, m, r); + }); + ``` + +## Implementation Checklist +- Define states and transitions before binding to controllers; start the state machine immediately. +- Create controllers via context helper methods; hook all relevant signals to emit local transition signals and set the current target. +- Bind document collections first, then replay existing selection to the view. +- For each commit/finish handler: compare values, write document, commit transaction; otherwise abort. Always reset `target` and flags. +- Keep all strings ASCII; add concise comments only where non-obvious. + +Use this prompt verbatim when extending bindings to new document elements to maintain consistent interaction, transaction, and synchronization behavior across the codebase. diff --git a/.github/workflows/dev-build.yml b/.github/workflows/dev-build.yml index 03dca6ce..7dd6dc8c 100644 --- a/.github/workflows/dev-build.yml +++ b/.github/workflows/dev-build.yml @@ -16,6 +16,7 @@ on: - 'LICENSE' - 'crowdin.yml' - '.github/**' + - '**/translations/*.ts' workflow_dispatch: inputs: identifier: @@ -40,16 +41,16 @@ jobs: os: - windows-2025 # - ubuntu-24.04 - # - macos-15 - + - macos-15 env: - QT_VERSION: 6.9.2 + QT_VERSION: 6.10.1 VCPKG_REF: 74e6536215718009aae747d86d84b78376bf9e09 INNOSETUP_REF: is-6_5_4 VERSION_IDENTIFIER: ${{ github.sha }}${{ github.event.inputs.identifier && '.' || '' }}${{ github.event.inputs.identifier }} BUILD_DIR: ${{ github.workspace }}/build/build INSTALL_DIR: ${{ github.workspace }}/build/install CCACHE_DIR: ${{ github.workspace }}/build/ccache + MACOSX_DEPLOYMENT_TARGET: 13.0 runs-on: ${{ matrix.os }} @@ -87,7 +88,10 @@ jobs: -InstallDir $env:INSTALL_DIR ` -VersionIdentifier $env:VERSION_IDENTIFIER ` ${{ github.event.inputs.use_ccache == 'true' && '-CCache' || '' }} - Write-Output ARTIFACT_NAME=$($output.ApplicationName)_$($output.Semver -replace '[\.\-\+]', '_') >> $env:GITHUB_ENV + Write-Output ARTIFACT_NAME=$($output.ApplicationName)_$($output.Semver -replace '[\.\-\+]', '_')_${{ runner.os }}_${{ runner.arch }} >> $env:GITHUB_ENV + Write-Output APPLICATION_SEMVER=$($output.Semver) >> $env:GITHUB_ENV + Write-Output APPLICATION_DISPLAY_NAME=$($output.ApplicationDisplayName) >> $env:GITHUB_ENV + Write-Output APPLICATION_NAME=$($output.ApplicationName) >> $env:GITHUB_ENV Write-Output INSTALLER_FILE_BASE=$($output.InstallerFileBase) >> $env:GITHUB_ENV - name: Save CCache cache @@ -102,11 +106,22 @@ jobs: $output = & ./scripts/ci/Collect-Symbol-Files.ps1 -VcpkgRootDir $env:VCPKG_ROOT_DIR -InstallDir $env:INSTALL_DIR Write-Output SYMBOL_FILES_PATH=$($output.Path) >> $env:GITHUB_ENV - - name: Pack + - name: Create InnoSetup installer (Windows) + if: ${{ runner.os == 'Windows' }} run: | $output = & ./scripts/ci/Pack.ps1 -BuildDir $env:BUILD_DIR -InstallerFileBase $env:INSTALLER_FILE_BASE -InnoSetupCommit $env:INNOSETUP_REF Write-Output PACKAGE_PATH=$($output.Path) >> $env:GITHUB_ENV + - name: Create DMG installer (macOS) + if: ${{ runner.os == 'macOS' }} + run: | + $output = & ./scripts/ci/Create-DMG.ps1 ` + -AppPath $(Join-Path $env:INSTALL_DIR $env:APPLICATION_NAME'.app') ` + -Semver $env:APPLICATION_SEMVER ` + -ApplicationDisplayName $env:APPLICATION_DISPLAY_NAME ` + -InstallerFileBase $env:INSTALLER_FILE_BASE + Write-Output PACKAGE_PATH=$($output) >> $env:GITHUB_ENV + - name: Upload symbol files uses: actions/upload-artifact@v4 with: diff --git a/crowdin.yml b/crowdin.yml index 14e32c6a..35758df7 100644 --- a/crowdin.yml +++ b/crowdin.yml @@ -1,13 +1,5 @@ files: - - source: /src/plugins/coreplugin/res/translations/Core_en_US.ts - translation: /src/plugins/coreplugin/res/translations/Core_%locale_with_underscore%.ts - - source: /src/plugins/audio/res/translations/org.diffscope.audio_en_US.ts - translation: /src/plugins/audio/res/translations/org.diffscope.audio_%locale_with_underscore%.ts - - source: /src/plugins/welcomewizard/res/translations/org.diffscope.welcomewizard_en_US.ts - translation: /src/plugins/welcomewizard/res/translations/org.diffscope.welcomewizard_%locale_with_underscore%.ts - - source: /src/plugins/achievement/res/translations/org.diffscope.achievement_en_US.ts - translation: /src/plugins/achievement/res/translations/org.diffscope.achievement_%locale_with_underscore%.ts - - source: /src/plugins/maintenance/res/translations/org.diffscope.maintenance_en_US.ts - translation: /src/plugins/maintenance/res/translations/org.diffscope.maintenance_%locale_with_underscore%.ts - - source: /src/libs/application/uishell/share/translations/uishell_en_US.ts - translation: /src/libs/application/uishell/share/translations/uishell_%locale_with_underscore%.ts + - source: '/**/translations/*_en_US.ts' + translation: '/%original_path%/%file_name%_%locale_with_underscore%.ts' + translation_replace: + '_en_US': '' diff --git a/scripts/ci/Build.ps1 b/scripts/ci/Build.ps1 index d7bee6de..0afa4394 100644 --- a/scripts/ci/Build.ps1 +++ b/scripts/ci/Build.ps1 @@ -78,6 +78,8 @@ Write-Host "Semver: $semver" $installerFileBase = "${applicationName}_$($semver -replace '[\.\-\+]', '_')_installer" +$depsDir = (Get-ChildItem -Path $(Join-Path $VcpkgRootDir installed) | Where-Object {$_.Name -ne "vcpkg"})[0].FullName + cmake -S . -B $(Resolve-Path $BuildDir) -G Ninja ` -DCMAKE_BUILD_TYPE=RelWithDebInfo ` "-DCMAKE_TOOLCHAIN_FILE=$(Join-Path $VcpkgRootDir scripts/buildsystems/vcpkg.cmake)" ` @@ -85,6 +87,8 @@ cmake -S . -B $(Resolve-Path $BuildDir) -G Ninja ` "-DCMAKE_CXX_COMPILER_LAUNCHER=$($CCache ? 'ccache' : '')" ` -DCMAKE_MSVC_DEBUG_INFORMATION_FORMAT=Embedded ` -DCK_ENABLE_CONSOLE:BOOL=FALSE ` + -DQT_NO_PRIVATE_MODULE_WARNING:BOOL=ON ` + "-DQMSETUP_APPLOCAL_DEPS_PATHS_RELWITHDEBINFO=$(Join-Path $depsDir lib)" ` -DAPPLICATION_INSTALL:BOOL=ON ` -DAPPLICATION_CONFIGURE_INSTALLER:BOOL=ON ` -DINNOSETUP_USE_UNOFFICIAL_LANGUAGE:BOOL=ON ` diff --git a/scripts/ci/Collect-Symbol-Files.ps1 b/scripts/ci/Collect-Symbol-Files.ps1 index 5c4e9206..914b3c52 100644 --- a/scripts/ci/Collect-Symbol-Files.ps1 +++ b/scripts/ci/Collect-Symbol-Files.ps1 @@ -47,6 +47,7 @@ if ($IsWindows) { } dsymutil $dllFile.FullName -o "$pdbTargetDirectory/$($dllFile.Name).dSYM" strip -S $dllFile.FullName + codesign --force --sign - $dllFile.FullName } else { Write-Host "Skip: $dllFile" } diff --git a/scripts/ci/Create-DMG.ps1 b/scripts/ci/Create-DMG.ps1 index f46f57d3..c4dd5544 100644 --- a/scripts/ci/Create-DMG.ps1 +++ b/scripts/ci/Create-DMG.ps1 @@ -32,6 +32,8 @@ New-Item -ItemType Directory -Path $TempDir | Out-Null $Bg1xOut = Join-Path $TempDir "dmg_background.png" $Bg2xOut = Join-Path $TempDir "dmg_background@2x.png" $BgTiff = Join-Path $TempDir "dmg_background.tiff" +$AppBundleName = "$ApplicationDisplayName.app" +$AppBundlePath = Join-Path $TempDir $AppBundleName $VersionText = "Version $Semver" @@ -87,16 +89,24 @@ try { Remove-Item $DmgPath -Force } + if (Test-Path $AppBundlePath) { + Remove-Item $AppBundlePath -Recurse -Force + } + + Move-Item -Path $AppPath -Destination $AppBundlePath + + & codesign --deep --force --sign - $AppBundlePath | Write-Host + <# TODO: create-dmg currently places hidden .background file to the right of the visible area, so we have to leave some space for the horizontal scroll bar #> & create-dmg ` --volname "$ApplicationDisplayName" ` --background "$BgTiff" ` --window-size 600 448 ` --icon-size 128 ` - --icon "$(Split-Path $AppPath -Leaf)" 132 280 ` + --icon "$(Split-Path $AppBundlePath -Leaf)" 132 280 ` --app-drop-link 468 280 ` "$DmgPath" ` - "$AppPath" | Write-Host + "$AppBundlePath" | Write-Host if ($LASTEXITCODE -ne 0) { throw "create-dmg failed" diff --git a/scripts/vcpkg b/scripts/vcpkg index 5e0443fe..6fb07583 160000 --- a/scripts/vcpkg +++ b/scripts/vcpkg @@ -1 +1 @@ -Subproject commit 5e0443fea12ce12f165fb16d7546b20a64894615 +Subproject commit 6fb07583f1e76206975508f33a21bfddce799bc0 diff --git a/src/app/CMakeLists.txt b/src/app/CMakeLists.txt index 4264e630..d95d1d65 100644 --- a/src/app/CMakeLists.txt +++ b/src/app/CMakeLists.txt @@ -29,6 +29,7 @@ endif() ck_configure_application( ${_ico_args} ${_icns_args} + INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/info.plist.in ) qm_configure_target(${PROJECT_NAME} @@ -70,6 +71,12 @@ ck_add_shared_files( # SRC conf/${CK_PLATFORM_LOWER}/qtmediate.json DEST ${CK_BUILD_QT_CONF_DIR} # qtmediate.json ) +if(EXISTS ${CMAKE_CURRENT_BINARY_DIR}/icons/dspx.icns) + ck_add_shared_files( + SRC ${CMAKE_CURRENT_BINARY_DIR}/icons/dspx.icns DEST ${CK_BUILD_DATA_DIR} + ) +endif() + file(GLOB _icons icons/*) list(FILTER _icons EXCLUDE REGEX CMakeLists.txt) diff --git a/src/app/info.plist.in b/src/app/info.plist.in new file mode 100644 index 00000000..c10a6e46 --- /dev/null +++ b/src/app/info.plist.in @@ -0,0 +1,92 @@ + + + + + + CFBundleDevelopmentRegion + English + + CFBundleExecutable + @APPLICATION_NAME@ + + CFBundleIconFile + app.icns + + CFBundleIdentifier + @APPLICATION_NAME@ + + CFBundleInfoDictionaryVersion + 6.0 + + CFBundleName + @APPLICATION_DISPLAY_NAME@ + + CFBundlePackageType + APPL + + CFBundleShortVersionString + @APPLICATION_SEMVER@ + + CFBundleSignature + ???? + + CFBundleVersion + @APPLICATION_SEMVER@ + + CSResourcesFileMapped + + + NSHumanReadableCopyright + @RC_COPYRIGHT@ + + UTExportedTypeDeclarations + + + UTTypeIdentifier + org.diffscope.dspx + + UTTypeDescription + DiffScope Project Exchange Format + + UTTypeConformsTo + + public.json + + + UTTypeTagSpecification + + public.filename-extension + + dspx + + + public.mime-type + application/vnd.openvpi.dspx+json + + + + + CFBundleDocumentTypes + + + CFBundleTypeName + DiffScope Project File + + CFBundleTypeRole + Editor + + LSItemContentTypes + + org.diffscope.dspx + + + CFBundleTypeIconFile + dspx.icns + + LSHandlerRank + Default + + + + + diff --git a/src/app/main.cpp b/src/app/main.cpp index b1b4c6f8..041d94f7 100644 --- a/src/app/main.cpp +++ b/src/app/main.cpp @@ -39,6 +39,8 @@ static QSettings::Format getJsonSettingsFormat() { static QQmlEngine *engine{}; +static constexpr char kNoWarningLastInitialization[] = "--no-warning-last-initialization"; + class MyLoaderSpec : public Loader::LoaderSpec { public: MyLoaderSpec() { @@ -55,6 +57,11 @@ class MyLoaderSpec : public Loader::LoaderSpec { QStringLiteral("/config.json"); pluginPaths << ApplicationInfo::applicationLocation(ApplicationInfo::BuiltinPlugins); coreName = QStringLiteral("org.diffscope.core"); + extraArguments << Argument{ + {kNoWarningLastInitialization}, + {}, + QStringLiteral("Suppress warning about 'Last initialization was aborted abnormally'") + }; } QSettings *createExtensionSystemSettings(QSettings::Scope scope) override { @@ -102,11 +109,18 @@ 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")); - 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({ diff --git a/src/libs/3rdparty/choruskit b/src/libs/3rdparty/choruskit index ead08258..e40b3316 160000 --- a/src/libs/3rdparty/choruskit +++ b/src/libs/3rdparty/choruskit @@ -1 +1 @@ -Subproject commit ead082581b7116763a27e29f733fa13e4edb963b +Subproject commit e40b331652f9a81eea9be505305dbb198abd8dae diff --git a/src/libs/3rdparty/opendspx b/src/libs/3rdparty/opendspx index 0a4bb52e..45fbaf09 160000 --- a/src/libs/3rdparty/opendspx +++ b/src/libs/3rdparty/opendspx @@ -1 +1 @@ -Subproject commit 0a4bb52e0f199d74d288eda2c9c41bfe2d34d80d +Subproject commit 45fbaf0987083c57bf5a06c0adcbd88956d0334a diff --git a/src/libs/3rdparty/qactionkit b/src/libs/3rdparty/qactionkit index 42b9cd90..573def48 160000 --- a/src/libs/3rdparty/qactionkit +++ b/src/libs/3rdparty/qactionkit @@ -1 +1 @@ -Subproject commit 42b9cd9053d76aeb8d66728a650827d97f40f11a +Subproject commit 573def486e3f8c0f62768bcdcbc490b24cbd59af diff --git a/src/libs/3rdparty/scopicflow b/src/libs/3rdparty/scopicflow index 06238363..d32af0d9 160000 --- a/src/libs/3rdparty/scopicflow +++ b/src/libs/3rdparty/scopicflow @@ -1 +1 @@ -Subproject commit 062383639e43b2baa9c3ae20674c8dd153d3a73e +Subproject commit d32af0d99f7e6fd1023412c2686fde559a5da7db diff --git a/src/libs/3rdparty/svscraft b/src/libs/3rdparty/svscraft index 70fab799..1289b602 160000 --- a/src/libs/3rdparty/svscraft +++ b/src/libs/3rdparty/svscraft @@ -1 +1 @@ -Subproject commit 70fab7997428056a557a82a8e3dce37941c15795 +Subproject commit 1289b602146a559e1c9d69f3347043e8cc935920 diff --git a/src/libs/application/dspxmodel/src/AnchorNode.cpp b/src/libs/application/dspxmodel/src/AnchorNode.cpp index 22eaba9f..47aae0d2 100644 --- a/src/libs/application/dspxmodel/src/AnchorNode.cpp +++ b/src/libs/application/dspxmodel/src/AnchorNode.cpp @@ -19,8 +19,9 @@ namespace dspx { 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")); + if ((interp_ != AnchorNode::None && interp_ != AnchorNode::Linear && interp_ != AnchorNode::Hermite)) { + if (auto engine = qjsEngine(q)) + engine->throwError(QJSValue::RangeError, QStringLiteral("Interpolation mode must be one of None, Linear, or Hermite")); return; } setInterpUnchecked(interp_); @@ -33,8 +34,9 @@ namespace dspx { 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")); + if (x_ < 0) { + if (auto engine = qjsEngine(q)) + engine->throwError(QJSValue::RangeError, QStringLiteral("Position must be greater or equal to 0")); return; } setXUnchecked(x_); diff --git a/src/libs/application/dspxmodel/src/ClipTime.cpp b/src/libs/application/dspxmodel/src/ClipTime.cpp index 6e80b886..478bc8d6 100644 --- a/src/libs/application/dspxmodel/src/ClipTime.cpp +++ b/src/libs/application/dspxmodel/src/ClipTime.cpp @@ -37,8 +37,9 @@ namespace dspx { 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")); + if (length_ < 0) { + if (auto engine = qjsEngine(q)) + engine->throwError(QJSValue::RangeError, QStringLiteral("Length must be greater than or equal to 0")); return; } setLengthUnchecked(length_); @@ -51,8 +52,9 @@ namespace dspx { 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")); + if (clipStart_ < 0) { + if (auto engine = qjsEngine(q)) + engine->throwError(QJSValue::RangeError, QStringLiteral("ClipStart must be greater than or equal to 0")); return; } setClipStartUnchecked(clipStart_); @@ -65,8 +67,9 @@ namespace dspx { 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")); + if (clipLen_ < 0) { + if (auto engine = qjsEngine(q)) + engine->throwError(QJSValue::RangeError, QStringLiteral("ClipLen must be greater than or equal to 0")); return; } setClipLenUnchecked(clipLen_); diff --git a/src/libs/application/dspxmodel/src/Control.cpp b/src/libs/application/dspxmodel/src/Control.cpp index d0c823ca..62d68586 100644 --- a/src/libs/application/dspxmodel/src/Control.cpp +++ b/src/libs/application/dspxmodel/src/Control.cpp @@ -33,8 +33,9 @@ namespace dspx { 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")); + if ((gain_ < 0)) { + if (auto engine = qjsEngine(q)) + engine->throwError(QJSValue::RangeError, QStringLiteral("Gain must be greater or equal to 0")); return; } setGainUnchecked(gain_); @@ -47,8 +48,9 @@ namespace dspx { 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]")); + if ((pan_ < -1 || pan_ > 1)) { + if (auto engine = qjsEngine(q)) + engine->throwError(QJSValue::RangeError, QStringLiteral("Pan must be in range [-1.0, 1.0]")); return; } setPanUnchecked(pan_); diff --git a/src/libs/application/dspxmodel/src/Control.h b/src/libs/application/dspxmodel/src/Control.h index 20135658..777294e7 100644 --- a/src/libs/application/dspxmodel/src/Control.h +++ b/src/libs/application/dspxmodel/src/Control.h @@ -14,7 +14,7 @@ namespace dspx { class ControlPrivate; - class Control : public QObject { + class DSPX_MODEL_EXPORT Control : public QObject { Q_OBJECT QML_ELEMENT QML_UNCREATABLE("") diff --git a/src/libs/application/dspxmodel/src/EntityObject.cpp b/src/libs/application/dspxmodel/src/EntityObject.cpp index a42f104a..b95fc8a6 100644 --- a/src/libs/application/dspxmodel/src/EntityObject.cpp +++ b/src/libs/application/dspxmodel/src/EntityObject.cpp @@ -22,7 +22,7 @@ namespace dspx { EntityObject::~EntityObject() { Q_D(EntityObject); if (d->model && d->handle) { - + Q_ASSERT(false && "EntityObject::~EntityObject: handle is not null. You should call Model::destroyItem() to delete EntityObject."); } } diff --git a/src/libs/application/dspxmodel/src/Global.cpp b/src/libs/application/dspxmodel/src/Global.cpp index e687463f..3747dcaa 100644 --- a/src/libs/application/dspxmodel/src/Global.cpp +++ b/src/libs/application/dspxmodel/src/Global.cpp @@ -34,8 +34,9 @@ namespace dspx { 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]")); + if ((centShift < -50 || centShift > 50)) { + if (auto engine = qjsEngine(q)) + engine->throwError(QJSValue::RangeError, QStringLiteral("Cent shift must be in range [-50, 50]")); return; } setCentShiftUnchecked(centShift); diff --git a/src/libs/application/dspxmodel/src/Global.h b/src/libs/application/dspxmodel/src/Global.h index 651f2129..8eeaa2ca 100644 --- a/src/libs/application/dspxmodel/src/Global.h +++ b/src/libs/application/dspxmodel/src/Global.h @@ -5,6 +5,8 @@ #include +#include + namespace QDspx { struct Global; } @@ -17,7 +19,7 @@ namespace dspx { class GlobalPrivate; - class Global : public QObject { + class DSPX_MODEL_EXPORT Global : public QObject { Q_OBJECT QML_ELEMENT QML_UNCREATABLE("") diff --git a/src/libs/application/dspxmodel/src/Label.cpp b/src/libs/application/dspxmodel/src/Label.cpp index 7fdfecfe..2b508749 100644 --- a/src/libs/application/dspxmodel/src/Label.cpp +++ b/src/libs/application/dspxmodel/src/Label.cpp @@ -19,8 +19,9 @@ namespace dspx { 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")); + if (pos_ < -0) { + if (auto engine = qjsEngine(q)) + engine->throwError(QJSValue::RangeError, QStringLiteral("Pos must be greater or equal to 0")); return; } setPosUnchecked(pos_); diff --git a/src/libs/application/dspxmodel/src/Master.cpp b/src/libs/application/dspxmodel/src/Master.cpp index 95dd4e05..7f971fc6 100644 --- a/src/libs/application/dspxmodel/src/Master.cpp +++ b/src/libs/application/dspxmodel/src/Master.cpp @@ -16,18 +16,36 @@ namespace dspx { public: Master *q_ptr; ModelPrivate *pModel; + Handle handle; BusControl *control; + bool multiChannelOutput{}; }; Master::Master(Model *model) : QObject(model), d_ptr(new MasterPrivate) { Q_D(Master); d->q_ptr = this; d->pModel = ModelPrivate::get(model); + d->handle = model->handle(); d->control = d->pModel->createObject(model->handle()); } void Master::handleProxySetEntityProperty(int property, const QVariant &value) { Q_D(Master); - ModelPrivate::proxySetEntityPropertyNotify(d->control, property, value); + switch (property) { + case ModelStrategy::P_ControlGain: + case ModelStrategy::P_ControlPan: + case ModelStrategy::P_ControlMute: { + ModelPrivate::proxySetEntityPropertyNotify(d->control, property, value); + break; + } + case ModelStrategy::P_MultiChannelOutput: { + d->multiChannelOutput = value.toBool(); + Q_EMIT multiChannelOutputChanged(d->multiChannelOutput); + break; + } + default: + Q_UNREACHABLE(); + } + } Master::~Master() = default; @@ -35,6 +53,14 @@ namespace dspx { Q_D(const Master); return d->control; } + bool Master::multiChannelOutput() const { + Q_D(const Master); + return d->multiChannelOutput; + } + void Master::setMultiChannelOutput(bool multiChannelOutput) { + Q_D(Master); + d->pModel->strategy->setEntityProperty(d->handle, ModelStrategy::P_MultiChannelOutput, multiChannelOutput); + } QDspx::Master Master::toQDspx() const { return { diff --git a/src/libs/application/dspxmodel/src/Master.h b/src/libs/application/dspxmodel/src/Master.h index 507ae783..d0ac456d 100644 --- a/src/libs/application/dspxmodel/src/Master.h +++ b/src/libs/application/dspxmodel/src/Master.h @@ -1,9 +1,10 @@ #ifndef DIFFSCOPE_DSPX_MODEL_MASTER_H #define DIFFSCOPE_DSPX_MODEL_MASTER_H +#include #include -#include +#include namespace QDspx { struct Master; @@ -19,21 +20,27 @@ namespace dspx { class MasterPrivate; - class Master : public QObject { + class DSPX_MODEL_EXPORT Master : public QObject { Q_OBJECT QML_ELEMENT QML_UNCREATABLE("") Q_DECLARE_PRIVATE(Master) Q_PROPERTY(BusControl *control READ control CONSTANT) - + Q_PROPERTY(bool multiChannelOutput READ multiChannelOutput WRITE setMultiChannelOutput NOTIFY multiChannelOutputChanged) public: ~Master() override; BusControl *control() const; + bool multiChannelOutput() const; + void setMultiChannelOutput(bool multiChannelOutput); + QDspx::Master toQDspx() const; void fromQDspx(const QDspx::Master &master); + Q_SIGNALS: + void multiChannelOutputChanged(bool multiChannelOutput); + private: friend class ModelPrivate; explicit Master(Model *model); diff --git a/src/libs/application/dspxmodel/src/Model.cpp b/src/libs/application/dspxmodel/src/Model.cpp index a91105e0..cce0485a 100644 --- a/src/libs/application/dspxmodel/src/Model.cpp +++ b/src/libs/application/dspxmodel/src/Model.cpp @@ -210,7 +210,7 @@ namespace dspx { } QDspx::Model Model::toQDspx() const { - return { + QDspx::Model model = { .version = QDspx::Model::V1, .content = { .global = global()->toQDspx(), @@ -220,6 +220,17 @@ namespace dspx { .workspace = workspace()->toQDspx(), } }; + model.content.workspace["diffscope"] = QJsonObject{ + {"loop", QJsonObject{ + {"enabled", timeline()->isLoopEnabled()}, + {"start", timeline()->loopStart()}, + {"length", timeline()->loopLength()}, + }}, + {"master", QJsonObject{ + {"multiChannelOutput", master()->multiChannelOutput()} + }} + }; + return model; } void Model::fromQDspx(const QDspx::Model &model) { @@ -229,6 +240,25 @@ namespace dspx { d->timeline->fromQDspx(model.content.timeline); d->tracks->fromQDspx(model.content.tracks); d->workspace->fromQDspx(model.content.workspace); + { + 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() { @@ -353,10 +383,17 @@ namespace dspx { } case ModelStrategy::P_ControlGain: case ModelStrategy::P_ControlPan: - case ModelStrategy::P_ControlMute: { + case ModelStrategy::P_ControlMute: + case ModelStrategy::P_MultiChannelOutput: { ModelPrivate::proxySetEntityPropertyNotify(d->master, property, value); break; } + case ModelStrategy::P_LoopEnabled: + case ModelStrategy::P_LoopLength: + case ModelStrategy::P_LoopStart: { + ModelPrivate::proxySetEntityPropertyNotify(d->timeline, property, value); + break; + } default: Q_UNREACHABLE(); } diff --git a/src/libs/application/dspxmodel/src/ModelStrategy.h b/src/libs/application/dspxmodel/src/ModelStrategy.h index 0a56fbfa..d36200c6 100644 --- a/src/libs/application/dspxmodel/src/ModelStrategy.h +++ b/src/libs/application/dspxmodel/src/ModelStrategy.h @@ -61,15 +61,21 @@ namespace dspx { P_ControlGain, P_ControlMute, P_ControlPan, + P_ControlRecord, P_ControlSolo, P_Denominator, P_EditorId, P_EditorName, + P_Height, P_JsonObject, P_KeyNumber, P_Language, P_Length, + P_LoopEnabled, + P_LoopLength, + P_LoopStart, P_Measure, + P_MultiChannelOutput, P_Name, P_Numerator, P_Onset, @@ -236,6 +242,10 @@ namespace dspx { auto v = value.toInt(); return v >= 0; }; + static auto validateIntGreaterZero = [](const QVariant &value) { + auto v = value.toInt(); + return v > 0; + }; static auto validateDoubleGreaterOrEqualZero = [](const QVariant &value) { auto v = value.toDouble(); return v >= 0; @@ -280,7 +290,11 @@ namespace dspx { {P_EditorName, QMetaType::QString}, {P_ControlGain, QMetaType::Double}, {P_ControlPan, QMetaType::Double, validatePan}, - {P_ControlMute, QMetaType::Bool} + {P_ControlMute, QMetaType::Bool}, + {P_MultiChannelOutput, QMetaType::Bool}, + {P_LoopEnabled, QMetaType::Bool}, + {P_LoopStart, QMetaType::Int, validateIntGreaterOrEqualZero}, + {P_LoopLength, QMetaType::Int, validateIntGreaterZero}, }; case EI_Label: return { {P_Position, QMetaType::Int, validateIntGreaterOrEqualZero}, @@ -348,7 +362,9 @@ namespace dspx { {P_ControlGain, QMetaType::Double}, {P_ControlPan, QMetaType::Double, validatePan}, {P_ControlMute, QMetaType::Bool}, - {P_ControlSolo, QMetaType::Bool} + {P_ControlRecord, QMetaType::Bool}, + {P_ControlSolo, QMetaType::Bool}, + {P_Height, QMetaType::Double, validateDoubleGreaterOrEqualZero} }; case EI_WorkspaceInfo: return { {P_JsonObject, QMetaType::QJsonObject} diff --git a/src/libs/application/dspxmodel/src/Note.cpp b/src/libs/application/dspxmodel/src/Note.cpp index 9888c90c..3760f649 100644 --- a/src/libs/application/dspxmodel/src/Note.cpp +++ b/src/libs/application/dspxmodel/src/Note.cpp @@ -23,8 +23,9 @@ namespace dspx { 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]")); + if ((centShift_ < -50 || centShift_ > 50)) { + if (auto engine = qjsEngine(q)) + engine->throwError(QJSValue::RangeError, QStringLiteral("CentShift must be in range [-50, 50]")); return; } setCentShiftUnchecked(centShift_); @@ -37,8 +38,9 @@ namespace dspx { 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]")); + if ((keyNum_ < 0 || keyNum_ > 127)) { + if (auto engine = qjsEngine(q)) + engine->throwError(QJSValue::RangeError, QStringLiteral("KeyNum must be in range [0, 127]")); return; } setKeyNumUnchecked(keyNum_); @@ -51,8 +53,9 @@ namespace dspx { 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")); + if (length_ < 0) { + if (auto engine = qjsEngine(q)) + engine->throwError(QJSValue::RangeError, QStringLiteral("Length must be greater than or equal to 0")); return; } setLengthUnchecked(length_); @@ -65,8 +68,9 @@ namespace dspx { 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")); + if (pos_ < 0) { + if (auto engine = qjsEngine(q)) + engine->throwError(QJSValue::RangeError, QStringLiteral("Pos must be greater than or equal to 0")); return; } setPosUnchecked(pos_); diff --git a/src/libs/application/dspxmodel/src/Tempo.cpp b/src/libs/application/dspxmodel/src/Tempo.cpp index 0da9844a..da07fa53 100644 --- a/src/libs/application/dspxmodel/src/Tempo.cpp +++ b/src/libs/application/dspxmodel/src/Tempo.cpp @@ -19,8 +19,9 @@ namespace dspx { 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")); + if (pos_ < 0) { + if (auto engine = qjsEngine(q)) + engine->throwError(QJSValue::RangeError, QStringLiteral("Pos must be greater or equal to 0")); return; } setPosUnchecked(pos_); @@ -33,8 +34,9 @@ namespace dspx { 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]")); + if ((value_ < 10.0 || value_ > 1000.0)) { + if (auto engine = qjsEngine(q)) + engine->throwError(QJSValue::RangeError, QStringLiteral("Value must be in range [10.0, 1000.0]")); return; } setValueUnchecked(value_); diff --git a/src/libs/application/dspxmodel/src/TimeSignature.cpp b/src/libs/application/dspxmodel/src/TimeSignature.cpp index 5148bf3d..8f0fa068 100644 --- a/src/libs/application/dspxmodel/src/TimeSignature.cpp +++ b/src/libs/application/dspxmodel/src/TimeSignature.cpp @@ -23,8 +23,9 @@ namespace dspx { 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")); + if (index_ < 0) { + if (auto engine = qjsEngine(q)) + engine->throwError(QJSValue::RangeError, QStringLiteral("Index must be greater or equal to 0")); return; } setIndexUnchecked(index_); @@ -37,8 +38,9 @@ namespace dspx { 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")); + if (numerator_ < 1) { + if (auto engine = qjsEngine(q)) + engine->throwError(QJSValue::RangeError, QStringLiteral("Numerator must be greater or equal to 1")); return; } setNumeratorUnchecked(numerator_); diff --git a/src/libs/application/dspxmodel/src/Timeline.cpp b/src/libs/application/dspxmodel/src/Timeline.cpp index 7119ef2c..b73df2ac 100644 --- a/src/libs/application/dspxmodel/src/Timeline.cpp +++ b/src/libs/application/dspxmodel/src/Timeline.cpp @@ -1,7 +1,10 @@ #include "Timeline.h" +#include + #include +#include #include #include #include @@ -14,12 +17,69 @@ namespace dspx { public: Timeline *q_ptr; ModelPrivate *pModel; + Handle handle; + + bool loopEnabled{false}; + int loopStart{0}; + int loopLength{1920}; + + void setLoopStartUnchecked(int loopStart_) { + Q_Q(Timeline); + pModel->strategy->setEntityProperty(handle, ModelStrategy::P_LoopStart, loopStart_); + } + + void setLoopStart(int loopStart_) { + Q_Q(Timeline); + if (loopStart_ < 0) { + if (auto engine = qjsEngine(q)) + engine->throwError(QJSValue::RangeError, QStringLiteral("Loop start must be greater or equal to 0")); + return; + } + setLoopStartUnchecked(loopStart_); + } + + void setLoopLengthUnchecked(int loopLength_) { + Q_Q(Timeline); + pModel->strategy->setEntityProperty(handle, ModelStrategy::P_LoopLength, loopLength_); + } + + void setLoopLength(int loopLength_) { + Q_Q(Timeline); + if (loopLength_ <= 0) { + if (auto engine = qjsEngine(q)) + engine->throwError(QJSValue::RangeError, QStringLiteral("Loop length must be greater than 0")); + return; + } + setLoopLengthUnchecked(loopLength_); + } }; - 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 +99,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->setLoopStartUnchecked(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->setLoopLengthUnchecked(loopLength); + } + QDspx::Timeline Timeline::toQDspx() const { return { labels()->toQDspx(), diff --git a/src/libs/application/dspxmodel/src/Timeline.h b/src/libs/application/dspxmodel/src/Timeline.h index 8ce439e2..2352052f 100644 --- a/src/libs/application/dspxmodel/src/Timeline.h +++ b/src/libs/application/dspxmodel/src/Timeline.h @@ -29,6 +29,9 @@ namespace dspx { Q_PROPERTY(LabelSequence *labels READ labels CONSTANT) Q_PROPERTY(TempoSequence *tempos READ tempos CONSTANT) Q_PROPERTY(TimeSignatureSequence *timeSignatures READ timeSignatures CONSTANT) + Q_PROPERTY(bool loopEnabled READ isLoopEnabled WRITE setLoopEnabled NOTIFY loopEnabledChanged) + Q_PRIVATE_PROPERTY(d_func(), int loopStart MEMBER loopStart WRITE setLoopStart NOTIFY loopStartChanged) + Q_PRIVATE_PROPERTY(d_func(), int loopLength MEMBER loopLength WRITE setLoopLength NOTIFY loopLengthChanged) public: ~Timeline() override; @@ -36,12 +39,27 @@ namespace dspx { TempoSequence *tempos() const; TimeSignatureSequence *timeSignatures() const; + bool isLoopEnabled() const; + void setLoopEnabled(bool enabled); + + int loopStart() const; + void setLoopStart(int loopStart); + + int loopLength() const; + void setLoopLength(int loopLength); + QDspx::Timeline toQDspx() const; void fromQDspx(const QDspx::Timeline &timeline); + Q_SIGNALS: + void loopEnabledChanged(bool enabled); + void loopStartChanged(int loopStart); + void loopLengthChanged(int loopLength); + private: friend class ModelPrivate; explicit Timeline(Model *model); + void handleProxySetEntityProperty(int property, const QVariant &value); QScopedPointer d_ptr; }; diff --git a/src/libs/application/dspxmodel/src/Track.cpp b/src/libs/application/dspxmodel/src/Track.cpp index cb10f594..a0e7140d 100644 --- a/src/libs/application/dspxmodel/src/Track.cpp +++ b/src/libs/application/dspxmodel/src/Track.cpp @@ -31,6 +31,7 @@ namespace dspx { d->pModel = ModelPrivate::get(model); d->name = d->pModel->strategy->getEntityProperty(handle, ModelStrategy::P_Name).toString(); d->colorId = d->pModel->strategy->getEntityProperty(handle, ModelStrategy::P_ColorId).toInt(); + d->height = d->pModel->strategy->getEntityProperty(handle, ModelStrategy::P_Height).toDouble(); d->control = d->pModel->createObject(handle); d->workspace = d->pModel->createObject(d->pModel->strategy->getAssociatedSubEntity(handle, ModelStrategy::R_Workspace)); d->clips = d->pModel->createObject(this, d->pModel->strategy->getAssociatedSubEntity(handle, ModelStrategy::R_Children)); @@ -53,6 +54,16 @@ namespace dspx { d->pModel->strategy->setEntityProperty(handle(), ModelStrategy::P_ColorId, colorId); } + double Track::height() const { + Q_D(const Track); + return d->height; + } + + void Track::setHeight(double height) { + Q_D(Track); + d->pModel->strategy->setEntityProperty(handle(), ModelStrategy::P_Height, height); + } + TrackControl *Track::control() const { Q_D(const Track); return d->control; @@ -80,7 +91,11 @@ namespace dspx { .clips = clips()->toQDspx(), .workspace = workspace()->toQDspx(), }; - track.workspace["diffscope"]["colorId"] = colorId(); + track.workspace["diffscope"] = QJsonObject{ + {"colorId", colorId()}, + {"height", height()}, + {"record", control()->record()}, + }; return track; } @@ -90,6 +105,8 @@ namespace dspx { clips()->fromQDspx(track.clips); workspace()->fromQDspx(track.workspace); setColorId(track.workspace["diffscope"]["colorId"].toInt()); + setHeight(track.workspace["diffscope"]["height"].toDouble(80)); + control()->setRecord(track.workspace["diffscope"]["record"].toBool()); } TrackList *Track::trackList() const { @@ -110,9 +127,15 @@ namespace dspx { Q_EMIT colorIdChanged(d->colorId); break; } + case ModelStrategy::P_Height: { + d->height = value.toDouble(); + Q_EMIT heightChanged(d->height); + break; + } case ModelStrategy::P_ControlGain: case ModelStrategy::P_ControlPan: case ModelStrategy::P_ControlMute: + case ModelStrategy::P_ControlRecord: case ModelStrategy::P_ControlSolo: { ModelPrivate::proxySetEntityPropertyNotify(d->control, property, value); break; diff --git a/src/libs/application/dspxmodel/src/Track.h b/src/libs/application/dspxmodel/src/Track.h index c686f3d6..c49fd48c 100644 --- a/src/libs/application/dspxmodel/src/Track.h +++ b/src/libs/application/dspxmodel/src/Track.h @@ -26,6 +26,7 @@ namespace dspx { Q_DECLARE_PRIVATE(Track) Q_PROPERTY(ClipSequence *clips READ clips CONSTANT) Q_PROPERTY(int colorId READ colorId WRITE setColorId NOTIFY colorIdChanged) + Q_PROPERTY(double height READ height WRITE setHeight NOTIFY heightChanged) Q_PROPERTY(TrackControl *control READ control CONSTANT) Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged) Q_PROPERTY(Workspace *workspace READ workspace CONSTANT) @@ -39,6 +40,9 @@ namespace dspx { int colorId() const; void setColorId(int colorId); + double height() const; + void setHeight(double height); + TrackControl *control() const; QString name() const; @@ -54,6 +58,7 @@ namespace dspx { Q_SIGNALS: void nameChanged(const QString &name); void colorIdChanged(int colorId); + void heightChanged(double height); void trackListChanged(); protected: diff --git a/src/libs/application/dspxmodel/src/TrackControl.cpp b/src/libs/application/dspxmodel/src/TrackControl.cpp index 1e543252..f3142173 100644 --- a/src/libs/application/dspxmodel/src/TrackControl.cpp +++ b/src/libs/application/dspxmodel/src/TrackControl.cpp @@ -13,6 +13,7 @@ namespace dspx { TrackControl *q_ptr; ModelPrivate *pModel; bool solo; + bool record; }; TrackControl::TrackControl(Handle handle, Model *model) : Control(handle, model), d_ptr(new TrackControlPrivate) { @@ -20,6 +21,7 @@ namespace dspx { d->q_ptr = this; d->pModel = ModelPrivate::get(model); d->solo = d->pModel->strategy->getEntityProperty(handle, ModelStrategy::P_ControlSolo).toBool(); + d->record = d->pModel->strategy->getEntityProperty(handle, ModelStrategy::P_ControlRecord).toBool(); } TrackControl::~TrackControl() = default; @@ -34,6 +36,16 @@ namespace dspx { d->pModel->strategy->setEntityProperty(handle(), ModelStrategy::P_ControlSolo, solo); } + bool TrackControl::record() const { + Q_D(const TrackControl); + return d->record; + } + + void TrackControl::setRecord(bool record) { + Q_D(TrackControl); + d->pModel->strategy->setEntityProperty(handle(), ModelStrategy::P_ControlRecord, record); + } + QDspx::TrackControl TrackControl::toQDspx() const { return { .gain = gain(), @@ -57,6 +69,11 @@ namespace dspx { Q_EMIT soloChanged(d->solo); break; } + case ModelStrategy::P_ControlRecord: { + d->record = value.toBool(); + Q_EMIT recordChanged(d->record); + break; + } default: { Control::handleProxySetEntityProperty(property, value); } diff --git a/src/libs/application/dspxmodel/src/TrackControl.h b/src/libs/application/dspxmodel/src/TrackControl.h index d7018840..5d077a01 100644 --- a/src/libs/application/dspxmodel/src/TrackControl.h +++ b/src/libs/application/dspxmodel/src/TrackControl.h @@ -11,12 +11,13 @@ namespace dspx { class TrackControlPrivate; - class TrackControl : public Control { + class DSPX_MODEL_EXPORT TrackControl : public Control { Q_OBJECT QML_ELEMENT QML_UNCREATABLE("") Q_DECLARE_PRIVATE(TrackControl) Q_PROPERTY(bool solo READ solo WRITE setSolo NOTIFY soloChanged) + Q_PROPERTY(bool record READ record WRITE setRecord NOTIFY recordChanged) public: ~TrackControl() override; @@ -24,11 +25,15 @@ namespace dspx { bool solo() const; void setSolo(bool solo); + bool record() const; + void setRecord(bool record); + QDspx::TrackControl toQDspx() const; void fromQDspx(const QDspx::TrackControl &trackControl); Q_SIGNALS: void soloChanged(bool solo); + void recordChanged(bool record); private: friend class ModelPrivate; diff --git a/src/libs/application/dspxmodel/src/Track_p.h b/src/libs/application/dspxmodel/src/Track_p.h index 4f597df9..4a4caeba 100644 --- a/src/libs/application/dspxmodel/src/Track_p.h +++ b/src/libs/application/dspxmodel/src/Track_p.h @@ -13,6 +13,7 @@ namespace dspx { ClipSequence *clips; QString name; int colorId; + double height; TrackControl *control; Workspace *workspace; TrackList *trackList; diff --git a/src/libs/application/dspxmodel/src/Vibrato.cpp b/src/libs/application/dspxmodel/src/Vibrato.cpp index 73c84e31..be5c2d3a 100644 --- a/src/libs/application/dspxmodel/src/Vibrato.cpp +++ b/src/libs/application/dspxmodel/src/Vibrato.cpp @@ -44,8 +44,9 @@ namespace dspx { 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]")); + if ((end_ < 0.0 || end_ > 1.0)) { + if (auto engine = qjsEngine(q)) + engine->throwError(QJSValue::RangeError, QStringLiteral("End must be in range [0, 1]")); return; } setEndUnchecked(end_); @@ -58,8 +59,9 @@ namespace dspx { 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")); + if (freq_ < 0.0) { + if (auto engine = qjsEngine(q)) + engine->throwError(QJSValue::RangeError, QStringLiteral("Freq must be greater than or equal to 0")); return; } setFreqUnchecked(freq_); @@ -72,8 +74,9 @@ namespace dspx { 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]")); + if ((phase_ < 0.0 || phase_ > 1.0)) { + if (auto engine = qjsEngine(q)) + engine->throwError(QJSValue::RangeError, QStringLiteral("Phase must be in range [0, 1]")); return; } setPhaseUnchecked(phase_); @@ -86,8 +89,9 @@ namespace dspx { 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]")); + if ((start_ < 0.0 || start_ > 1.0)) { + if (auto engine = qjsEngine(q)) + engine->throwError(QJSValue::RangeError, QStringLiteral("Start must be in range [0, 1]")); return; } setStartUnchecked(start_); diff --git a/src/libs/application/dspxmodel/src/selectionmodel/SelectionModel.cpp b/src/libs/application/dspxmodel/src/selectionmodel/SelectionModel.cpp index 8dc589dd..b5c542a5 100644 --- a/src/libs/application/dspxmodel/src/selectionmodel/SelectionModel.cpp +++ b/src/libs/application/dspxmodel/src/selectionmodel/SelectionModel.cpp @@ -167,22 +167,22 @@ namespace dspx { if (targetSelectionType != d->selectionType) { switch (d->selectionType) { case ST_AnchorNode: - // d->anchorNodeSelectionModel->d_func()->clearAll(); + // d->anchorNodeSelectionModel->d_func()->select(nullptr, command); break; case ST_Clip: - d->clipSelectionModel->d_func()->clearAll(); + d->clipSelectionModel->d_func()->select(nullptr, command); break; case ST_Label: - d->labelSelectionModel->d_func()->clearAll(); + d->labelSelectionModel->d_func()->select(nullptr, command); break; case ST_Note: - d->noteSelectionModel->d_func()->clearAll(); + d->noteSelectionModel->d_func()->select(nullptr, command); break; case ST_Tempo: - d->tempoSelectionModel->d_func()->clearAll(); + d->tempoSelectionModel->d_func()->select(nullptr, command); break; case ST_Track: - d->trackSelectionModel->d_func()->clearAll(); + d->trackSelectionModel->d_func()->select(nullptr, command); break; default: break; diff --git a/src/libs/application/dspxmodel/src/selectionmodel/TrackSelectionModel.cpp b/src/libs/application/dspxmodel/src/selectionmodel/TrackSelectionModel.cpp index fb0d040e..124889a7 100644 --- a/src/libs/application/dspxmodel/src/selectionmodel/TrackSelectionModel.cpp +++ b/src/libs/application/dspxmodel/src/selectionmodel/TrackSelectionModel.cpp @@ -9,9 +9,10 @@ namespace dspx { return item->trackList() == selectionModel->model()->tracks(); } - TrackSelectionModel::TrackSelectionModel(QObject *parent) : QObject(parent), d_ptr(new TrackSelectionModelPrivate) { + TrackSelectionModel::TrackSelectionModel(SelectionModel *parent) : QObject(parent), d_ptr(new TrackSelectionModelPrivate) { Q_D(TrackSelectionModel); d->q_ptr = this; + d->selectionModel = parent; } TrackSelectionModel::~TrackSelectionModel() = default; diff --git a/src/libs/application/dspxmodel/src/selectionmodel/TrackSelectionModel.h b/src/libs/application/dspxmodel/src/selectionmodel/TrackSelectionModel.h index 0b4a975c..215b64a9 100644 --- a/src/libs/application/dspxmodel/src/selectionmodel/TrackSelectionModel.h +++ b/src/libs/application/dspxmodel/src/selectionmodel/TrackSelectionModel.h @@ -35,7 +35,7 @@ namespace dspx { private: friend class SelectionModel; - explicit TrackSelectionModel(QObject *parent = nullptr); + explicit TrackSelectionModel(SelectionModel *parent = nullptr); QScopedPointer d_ptr; }; diff --git a/src/libs/application/transactional/TransactionController.cpp b/src/libs/application/transactional/TransactionController.cpp index ce52e860..a03a4d9f 100644 --- a/src/libs/application/transactional/TransactionController.cpp +++ b/src/libs/application/transactional/TransactionController.cpp @@ -82,7 +82,7 @@ namespace Core { Q_D(TransactionController); // Validate range - if (step < 0 || step > totalSteps()) { + if (step > totalSteps()) { return; } @@ -288,6 +288,10 @@ namespace Core { if (wasExclusive) { Q_EMIT q->exclusiveToTransactionChanged(false); } + if (cleanStep >= currentStep) { + cleanStep = -1; + Q_EMIT q->cleanStepChanged(cleanStep); + } Q_EMIT q->currentStepChanged(currentStep); Q_EMIT q->totalStepsChanged(q->totalSteps()); Q_EMIT q->transactionCommitted(name); diff --git a/src/libs/application/uishell/share/translations/uishell.ts b/src/libs/application/uishell/share/translations/uishell.ts new file mode 100644 index 00000000..0eb11a54 --- /dev/null +++ b/src/libs/application/uishell/share/translations/uishell.ts @@ -0,0 +1,584 @@ + + + + + ActionLayoutsEditor + + + Action + Action + + + + Menu + Menu + + + + Action group + Action group + + + + Separator + Separator + + + + Stretch + Stretch + + + + BubbleNotification + + + Do Not Show Again + Do Not Show Again + + + + Collapse to Notifications Panel + Collapse to Notifications Panel + + + + Clear + Clear + + + + Abort + Abort + + + + DigitalClock + + + Digital clock + Digital clock + + + + FileView + + + New project + New project + + + + No result found + No result found + + + + HomeWindow + + + Empty + Empty + + + Open + Open + + + Open File Location + Open File Location + + + Reveal in %1 + Reveal in %1 + + + Remove from "Recovery Files" + Remove from "Recovery Files" + + + Remove from "Recent Files" + Remove from "Recent Files" + + + Recent Files + Recent Files + + + Recovery Files + Recovery Files + + + New project + New project + + + Search + Search + + + Grid view + Grid view + + + List view + List view + + + No result found + No result found + + + No recovery file +If %1 crashes, automatic recovery files will be displayed here. + No recovery file +If %1 crashes, automatic recovery files will be displayed here. + + + + InitializationFailureWarningDialog + + + %1 exited abnormally during the last initialization + %1 exited abnormally during the last initialization + + + + This is probably caused by a faulty plugin. Please <a href="#logs">check the logs</a> for more information.<br/><br/>You may view the plugin list and disable some plugins. + This is probably caused by a faulty plugin. Please <a href="#logs">check the logs</a> for more information.<br/><br/>You may view the plugin list and disable some plugins. + + + + LyricPronunciationEditor + + + Lyric Pronunciation Editor + Lyric Pronunciation Editor + + + + Current pronunciation is %1. Candidate pronunciations are %2. Activate to modify pronunciation. + Current pronunciation is %1. Candidate pronunciations are %2. Activate to modify pronunciation. + + + + Customize pronunciation of "%1" + Customize pronunciation of "%1" + + + + OK + OK + + + + Edit lyric + Edit lyric + + + + Customize Pronunciation... + Customize Pronunciation... + + + + Edit Lyric... + Edit Lyric... + + + + Insert Cell Before + Insert Cell Before + + + + Insert Cell After + Insert Cell After + + + + Delete Cell + Delete Cell + + + + + Insert Line Before + Insert Line Before + + + + + Insert Line After + Insert Line After + + + + + Delete Line + Delete Line + + + + Lyrics Line %1 + Lyrics Line %1 + + + + Append Cell + Append Cell + + + + PluginView + + + Restart %1? + Restart %1? + + + + After restart, plugin changes will be applied. + After restart, plugin changes will be applied. + + + + Enabling %1 will also enable the following plugins: + Enabling %1 will also enable the following plugins: + + + + + Continue? + Continue? + + + + Disabling %1 will also disable the following plugins: + Disabling %1 will also disable the following plugins: + + + + + Restart required + Restart required + + + + Back + Back + + + + Plugin name + Plugin name + + + + Vendor + Vendor + + + + Version + Version + + + + Visit plugin homepage + Visit plugin homepage + + + + Description + Description + + + + Required + Required + + + + Enabled + Enabled + + + + Disabled + Disabled + + + + Plugin status: Error + Plugin status: Error + + + + Plugin status: Not loaded + Plugin status: Not loaded + + + + Plugin status: Loaded + Plugin status: Loaded + + + + Error details + Error details + + + + Details + Details + + + + Dependencies + Dependencies + + + + Plugins required by this plugin, including indirect dependencies + Plugins required by this plugin, including indirect dependencies + + + + Dependents + Dependents + + + + Plugins that require this plugin, including indirect dependants + Plugins that require this plugin, including indirect dependants + + + + Additional Info + Additional Info + + + + Identifier + Identifier + + + + Category + Category + + + + Path + Path + + + + Reveal in %1 + Reveal in %1 + + + Finder + Finder + + + File Explorer + File Explorer + + + + Compat version + Compat version + + + + Copyright + Copyright + + + + License + License + + + + This plugin does not require other plugins. + This plugin does not require other plugins. + + + + No plugins require this plugin. + No plugins require this plugin. + + + + Plugin is not available on this platform. + Plugin is not available on this platform. + + + + Plugin is enabled as dependency of an enabled plugin. + Plugin is enabled as dependency of an enabled plugin. + + + + Plugin is enabled by command line argument. + Plugin is enabled by command line argument. + + + + Plugin is disabled by command line argument. + Plugin is disabled by command line argument. + + + + Search + Search + + + + No result found + No result found + + + + ProjectWindow + + + Empty + Empty + + + + Left Docking View + Left Docking View + + + + Top Docking View + Top Docking View + + + + Bottom Docking View + Bottom Docking View + + + + Right Docking View + Right Docking View + + + + SettingDialog + + + Settings + Settings + + + + + Search + Search + + + + No result found + No result found + + + + This page is in compatibility mode + This page is in compatibility mode + + + + This settings page cannot be displayed because its type is not supported. + This settings page cannot be displayed because its type is not supported. + + + + Cannot Apply Settings + Cannot Apply Settings + + + + Failed to apply "%1" + Failed to apply "%1" + + + + OK + OK + + + + Cancel + Cancel + + + + Apply + Apply + + + + TempoTimeSignatureIndicator + + + Tempo + Tempo + + + + Time Signature + Time Signature + + + + WelcomeWizardDialog + + + + Welcome + Welcome + + + + Logo of %1 + Logo of %1 + + + + This wizard will help you complete the initial configuration + This wizard will help you complete the initial configuration + + + + Skip + Skip + + + + Previous + Previous + + + + Continue + Continue + + + + Next + Next + + + + Finish + Finish + + + diff --git a/src/libs/application/uishell/share/translations/uishell_en_US.ts b/src/libs/application/uishell/share/translations/uishell_en_US.ts index 5e0fb864..91e0caa2 100644 --- a/src/libs/application/uishell/share/translations/uishell_en_US.ts +++ b/src/libs/application/uishell/share/translations/uishell_en_US.ts @@ -1,571 +1,584 @@ - + ActionLayoutsEditor - - Action - Action + + Action + Action - - Menu - Menu + + Menu + Menu - - Action group - Action group + + Action group + Action group - - Separator - Separator + + Separator + Separator - - Stretch - Stretch + + Stretch + Stretch - - + + BubbleNotification - - Do Not Show Again - Do Not Show Again + + Do Not Show Again + Do Not Show Again - - Collapse to Notifications Panel - Collapse to Notifications Panel + + Collapse to Notifications Panel + Collapse to Notifications Panel - - Clear - Clear + + Clear + Clear - - Abort - Abort + + Abort + Abort - - + + DigitalClock - - Digital clock - Digital clock + + Digital clock + Digital clock - - + + + FileView + + + New project + New project + + + + No result found + No result found + + + HomeWindow - - Empty - Empty + + Empty + Empty - - Open - Open + Open + Open - Open File Location - Open File Location + Open File Location + Open File Location - - Reveal in %1 - Reveal in %1 + Reveal in %1 + Reveal in %1 - - Remove from "Recovery Files" - Remove from "Recovery Files" + Remove from "Recovery Files" + Remove from "Recovery Files" - - Remove from "Recent Files" - Remove from "Recent Files" + Remove from "Recent Files" + Remove from "Recent Files" - - Recent Files - Recent Files + Recent Files + Recent Files - - Recovery Files - Recovery Files + Recovery Files + Recovery Files - - New project - New project + New project + New project - - - Search - Search + Search + Search - - Grid view - Grid view + Grid view + Grid view - - List view - List view + List view + List view - - No result found - No result found + No result found + No result found - - No recovery file + No recovery file If %1 crashes, automatic recovery files will be displayed here. - No recovery file + No recovery file If %1 crashes, automatic recovery files will be displayed here. - - + + InitializationFailureWarningDialog - - %1 exited abnormally during the last initialization - %1 exited abnormally during the last initialization + + %1 exited abnormally during the last initialization + %1 exited abnormally during the last initialization - - This is probably caused by a faulty plugin. Please <a href="#logs">check the logs</a> for more information.<br/><br/>You may view the plugin list and disable some plugins. - This is probably caused by a faulty plugin. Please <a href="#logs">check the logs</a> for more information.<br/><br/>You may view the plugin list and disable some plugins. + + This is probably caused by a faulty plugin. Please <a href="#logs">check the logs</a> for more information.<br/><br/>You may view the plugin list and disable some plugins. + This is probably caused by a faulty plugin. Please <a href="#logs">check the logs</a> for more information.<br/><br/>You may view the plugin list and disable some plugins. - - + + LyricPronunciationEditor - - Lyric Pronunciation Editor - Lyric Pronunciation Editor + + Lyric Pronunciation Editor + Lyric Pronunciation Editor - - Current pronunciation is %1. Candidate pronunciations are %2. Activate to modify pronunciation. - Current pronunciation is %1. Candidate pronunciations are %2. Activate to modify pronunciation. + + Current pronunciation is %1. Candidate pronunciations are %2. Activate to modify pronunciation. + Current pronunciation is %1. Candidate pronunciations are %2. Activate to modify pronunciation. - - Customize pronunciation of "%1" - Customize pronunciation of "%1" + + Customize pronunciation of "%1" + Customize pronunciation of "%1" - - OK - OK + + OK + OK - - Edit lyric - Edit lyric + + Edit lyric + Edit lyric - - Customize Pronunciation... - Customize Pronunciation... + + Customize Pronunciation... + Customize Pronunciation... - - Edit Lyric... - Edit Lyric... + + Edit Lyric... + Edit Lyric... - - Insert Cell Before - Insert Cell Before + + Insert Cell Before + Insert Cell Before - - Insert Cell After - Insert Cell After + + Insert Cell After + Insert Cell After - - Delete Cell - Delete Cell + + Delete Cell + Delete Cell - - - Insert Line Before - Insert Line Before + + + Insert Line Before + Insert Line Before - - - Insert Line After - Insert Line After + + + Insert Line After + Insert Line After - - - Delete Line - Delete Line + + + Delete Line + Delete Line - - Lyrics Line %1 - Lyrics Line %1 + + Lyrics Line %1 + Lyrics Line %1 - - Append Cell - Append Cell + + Append Cell + Append Cell - - + + PluginView - - Restart %1? - Restart %1? + + Restart %1? + Restart %1? - - After restart, plugin changes will be applied. - After restart, plugin changes will be applied. + + After restart, plugin changes will be applied. + After restart, plugin changes will be applied. - - Enabling %1 will also enable the following plugins: - Enabling %1 will also enable the following plugins: + + Enabling %1 will also enable the following plugins: + Enabling %1 will also enable the following plugins: - - - Continue? - Continue? + + + Continue? + Continue? - - Disabling %1 will also disable the following plugins: - Disabling %1 will also disable the following plugins: + + Disabling %1 will also disable the following plugins: + Disabling %1 will also disable the following plugins: - - - Restart required - Restart required + + + Restart required + Restart required - - Back - Back + + Back + Back - - Plugin name - Plugin name + + Plugin name + Plugin name - - Vendor - Vendor + + Vendor + Vendor - - Version - Version + + Version + Version - - Visit plugin homepage - Visit plugin homepage + + Visit plugin homepage + Visit plugin homepage - - Description - Description + + Description + Description - - Required - Required + + Required + Required - - Enabled - Enabled + + Enabled + Enabled - - Disabled - Disabled + + Disabled + Disabled - - Plugin status: Error - Plugin status: Error + + Plugin status: Error + Plugin status: Error - - Plugin status: Not loaded - Plugin status: Not loaded + + Plugin status: Not loaded + Plugin status: Not loaded - - Plugin status: Loaded - Plugin status: Loaded + + Plugin status: Loaded + Plugin status: Loaded - - Error details - Error details + + Error details + Error details - - Details - Details + + Details + Details - - Dependencies - Dependencies + + Dependencies + Dependencies - - Plugins required by this plugin, including indirect dependencies - Plugins required by this plugin, including indirect dependencies + + Plugins required by this plugin, including indirect dependencies + Plugins required by this plugin, including indirect dependencies - - Dependents - Dependents + + Dependents + Dependents - - Plugins that require this plugin, including indirect dependants - Plugins that require this plugin, including indirect dependants + + Plugins that require this plugin, including indirect dependants + Plugins that require this plugin, including indirect dependants - - Additional Info - Additional Info + + Additional Info + Additional Info - - Identifier - Identifier + + Identifier + Identifier - - Category - Category + + Category + Category - - Path - Path + + Path + Path - - Reveal in %1 - Reveal in %1 + + Reveal in %1 + Reveal in %1 - Finder - Finder + Finder + Finder - File Explorer - File Explorer + File Explorer + File Explorer - - Compat version - Compat version + + Compat version + Compat version - - Copyright - Copyright + + Copyright + Copyright - - License - License + + License + License - - This plugin does not require other plugins. - This plugin does not require other plugins. + + This plugin does not require other plugins. + This plugin does not require other plugins. - - No plugins require this plugin. - No plugins require this plugin. + + No plugins require this plugin. + No plugins require this plugin. - - Plugin is not available on this platform. - Plugin is not available on this platform. + + Plugin is not available on this platform. + Plugin is not available on this platform. - - Plugin is enabled as dependency of an enabled plugin. - Plugin is enabled as dependency of an enabled plugin. + + Plugin is enabled as dependency of an enabled plugin. + Plugin is enabled as dependency of an enabled plugin. - - Plugin is enabled by command line argument. - Plugin is enabled by command line argument. + + Plugin is enabled by command line argument. + Plugin is enabled by command line argument. - - Plugin is disabled by command line argument. - Plugin is disabled by command line argument. + + Plugin is disabled by command line argument. + Plugin is disabled by command line argument. - - Search - Search + + Search + Search - - No result found - No result found + + No result found + No result found - - + + ProjectWindow - - Empty - Empty + + Empty + Empty - - Left Docking View - Left Docking View + + Left Docking View + Left Docking View - - Top Docking View - Top Docking View + + Top Docking View + Top Docking View - - Bottom Docking View - Bottom Docking View + + Bottom Docking View + Bottom Docking View - - Right Docking View - Right Docking View + + Right Docking View + Right Docking View - - + + SettingDialog - - Settings - Settings + + Settings + Settings + + + + + Search + Search + + + + No result found + No result found - - - Search - Search + + This page is in compatibility mode + This page is in compatibility mode - - No result found - No result found + + This settings page cannot be displayed because its type is not supported. + This settings page cannot be displayed because its type is not supported. - - This page is in compatibility mode - This page is in compatibility mode + + Cannot Apply Settings + Cannot Apply Settings - - This settings page cannot be displayed because its type is not supported. - This settings page cannot be displayed because its type is not supported. + + Failed to apply "%1" + Failed to apply "%1" - - Cannot Apply Settings - Cannot Apply Settings + + OK + OK - - Failed to apply "%1" - Failed to apply "%1" + + Cancel + Cancel - - OK - OK + + Apply + Apply + + + TempoTimeSignatureIndicator - - Cancel - Cancel + + Tempo + - - Apply - Apply + + Time Signature + - - + + WelcomeWizardDialog - - - Welcome - Welcome + + + Welcome + Welcome - - Logo of %1 - Logo of %1 + + Logo of %1 + Logo of %1 - - This wizard will help you complete the initial configuration - This wizard will help you complete the initial configuration + + This wizard will help you complete the initial configuration + This wizard will help you complete the initial configuration - - Skip - Skip + + Skip + Skip - - Previous - Previous + + Previous + Previous - - Continue - Continue + + Continue + Continue - - Next - Next + + Next + Next - - Finish - Finish + + Finish + Finish - + diff --git a/src/libs/application/uishell/share/translations/uishell_ja_JP.ts b/src/libs/application/uishell/share/translations/uishell_ja_JP.ts index 7eb0d3a0..94f96677 100644 --- a/src/libs/application/uishell/share/translations/uishell_ja_JP.ts +++ b/src/libs/application/uishell/share/translations/uishell_ja_JP.ts @@ -4,27 +4,27 @@ ActionLayoutsEditor - + Action Action - + Menu Menu - + Action group Action group - + Separator Separator - + Stretch Stretch @@ -32,22 +32,22 @@ BubbleNotification - + Do Not Show Again Do Not Show Again - + Collapse to Notifications Panel Collapse to Notifications Panel - + Clear Clear - + Abort Abort @@ -60,15 +60,27 @@ Digital clock + + FileView + + + New project + New project + + + + No result found + No result found + + HomeWindow - + Empty Empty - Open Open @@ -77,58 +89,46 @@ Open File Location - Reveal in %1 Reveal in %1 - Remove from "Recovery Files" Remove from "Recovery Files" - Remove from "Recent Files" Remove from "Recent Files" - Recent Files Recent Files - Recovery Files Recovery Files - New project New project - - Search Search - Grid view Grid view - List view List view - No result found No result found - No recovery file If %1 crashes, automatic recovery files will be displayed here. No recovery file @@ -258,123 +258,123 @@ If %1 crashes, automatic recovery files will be displayed here. Disabling %1 will also disable the following plugins: - - + + Restart required Restart required - + Back Back - + Plugin name Plugin name - + Vendor Vendor - + Version Version - + Visit plugin homepage Visit plugin homepage - + Description Description - + Required Required - + Enabled Enabled - + Disabled Disabled - + Plugin status: Error Plugin status: Error - + Plugin status: Not loaded Plugin status: Not loaded - + Plugin status: Loaded Plugin status: Loaded - + Error details Error details - + Details Details - + Dependencies Dependencies - + Plugins required by this plugin, including indirect dependencies Plugins required by this plugin, including indirect dependencies - + Dependents Dependents - + Plugins that require this plugin, including indirect dependants Plugins that require this plugin, including indirect dependants - + Additional Info Additional Info - + Identifier Identifier - + Category Category - + Path Path - + Reveal in %1 Reveal in %1 @@ -387,57 +387,57 @@ If %1 crashes, automatic recovery files will be displayed here. File Explorer - + Compat version Compat version - + Copyright Copyright - + License License - + This plugin does not require other plugins. This plugin does not require other plugins. - + No plugins require this plugin. No plugins require this plugin. - + Plugin is not available on this platform. Plugin is not available on this platform. - + Plugin is enabled as dependency of an enabled plugin. Plugin is enabled as dependency of an enabled plugin. - + Plugin is enabled by command line argument. Plugin is enabled by command line argument. - + Plugin is disabled by command line argument. Plugin is disabled by command line argument. - + Search Search - + No result found No result found @@ -445,27 +445,27 @@ If %1 crashes, automatic recovery files will be displayed here. ProjectWindow - + Empty Empty - + Left Docking View Left Docking View - + Top Docking View Top Docking View - + Bottom Docking View Bottom Docking View - + Right Docking View Right Docking View @@ -478,52 +478,65 @@ If %1 crashes, automatic recovery files will be displayed here. Settings - + Search Search - + No result found No result found - + This page is in compatibility mode This page is in compatibility mode - + This settings page cannot be displayed because its type is not supported. This settings page cannot be displayed because its type is not supported. - + Cannot Apply Settings Cannot Apply Settings - + Failed to apply "%1" Failed to apply "%1" - + OK OK - + Cancel Cancel - + Apply Apply + + TempoTimeSignatureIndicator + + + Tempo + Tempo + + + + Time Signature + Time Signature + + WelcomeWizardDialog diff --git a/src/libs/application/uishell/share/translations/uishell_zh_CN.ts b/src/libs/application/uishell/share/translations/uishell_zh_CN.ts index 87eb5532..8d8bea39 100644 --- a/src/libs/application/uishell/share/translations/uishell_zh_CN.ts +++ b/src/libs/application/uishell/share/translations/uishell_zh_CN.ts @@ -4,27 +4,27 @@ ActionLayoutsEditor - + Action 操作 - + Menu 菜单 - + Action group 操作组 - + Separator 分隔线 - + Stretch 拉伸 @@ -32,22 +32,22 @@ BubbleNotification - + Do Not Show Again 不再显示 - + Collapse to Notifications Panel 折叠到通知面板 - + Clear 清除 - + Abort 中止 @@ -60,15 +60,27 @@ 数字时钟 + + FileView + + + New project + 新建工程 + + + + No result found + 未找到结果 + + HomeWindow - + Empty - Open 打开 @@ -77,58 +89,46 @@ 打开文件位置 - Reveal in %1 在 %1 中显示 - Remove from "Recovery Files" 从“恢复文件”移除 - Remove from "Recent Files" 从“最近文件”移除 - Recent Files 最近文件 - Recovery Files 恢复文件 - New project 新建工程 - - Search 搜索 - Grid view 网格视图 - List view 列表视图 - No result found 未找到结果 - No recovery file If %1 crashes, automatic recovery files will be displayed here. 无恢复文件 @@ -258,123 +258,123 @@ If %1 crashes, automatic recovery files will be displayed here. 禁用 %1 还会禁用以下插件: - - + + Restart required 需要重新启动 - + Back 返回 - + Plugin name 插件名 - + Vendor 供应商 - + Version 版本 - + Visit plugin homepage 访问插件主页 - + Description 描述 - + Required 必需 - + Enabled 已启用 - + Disabled 已禁用 - + Plugin status: Error 插件状态:错误 - + Plugin status: Not loaded 插件状态:未加载 - + Plugin status: Loaded 插件状态:已加载 - + Error details 错误详情 - + Details 详情 - + Dependencies 依赖项 - + Plugins required by this plugin, including indirect dependencies 此插件需要的其他插件,包括间接依赖 - + Dependents 被依赖项 - + Plugins that require this plugin, including indirect dependants 需要此插件的其他插件,包括间接依赖 - + Additional Info 附加信息 - + Identifier 标识符 - + Category 类别 - + Path 路径 - + Reveal in %1 在 %1 中显示 @@ -387,57 +387,57 @@ If %1 crashes, automatic recovery files will be displayed here. 文件资源管理器 - + Compat version 兼容版本 - + Copyright 版权 - + License 许可证 - + This plugin does not require other plugins. 此插件不需要其他插件。 - + No plugins require this plugin. 没有插件需要此插件。 - + Plugin is not available on this platform. 插件在当前平台上不可用。 - + Plugin is enabled as dependency of an enabled plugin. 插件作为一个已启用的插件的依赖项而启用。 - + Plugin is enabled by command line argument. 插件被命令行参数启用。 - + Plugin is disabled by command line argument. 插件被命令行参数禁用。 - + Search 搜索 - + No result found 未找到结果 @@ -445,27 +445,27 @@ If %1 crashes, automatic recovery files will be displayed here. ProjectWindow - + Empty - + Left Docking View 左侧停靠视图 - + Top Docking View 顶部停靠视图 - + Bottom Docking View 底部停靠视图 - + Right Docking View 右侧停靠视图 @@ -478,52 +478,65 @@ If %1 crashes, automatic recovery files will be displayed here. 设置 - + Search 搜索 - + No result found 未找到结果 - + This page is in compatibility mode 此页面处于兼容模式 - + This settings page cannot be displayed because its type is not supported. 此设置页面无法被显示,因为其类型不受支持。 - + Cannot Apply Settings 无法应用设置 - + Failed to apply "%1" 应用“%1”失败 - + OK 确定 - + Cancel 取消 - + Apply 应用 + + TempoTimeSignatureIndicator + + + Tempo + 曲速 + + + + Time Signature + 拍号 + + WelcomeWizardDialog diff --git a/src/libs/application/uishell/share/translations/uishell_zh_TW.ts b/src/libs/application/uishell/share/translations/uishell_zh_TW.ts index 4880c73c..d0a04747 100644 --- a/src/libs/application/uishell/share/translations/uishell_zh_TW.ts +++ b/src/libs/application/uishell/share/translations/uishell_zh_TW.ts @@ -4,27 +4,27 @@ ActionLayoutsEditor - + Action Action - + Menu Menu - + Action group Action group - + Separator Separator - + Stretch Stretch @@ -32,22 +32,22 @@ BubbleNotification - + Do Not Show Again Do Not Show Again - + Collapse to Notifications Panel Collapse to Notifications Panel - + Clear Clear - + Abort Abort @@ -60,15 +60,27 @@ Digital clock + + FileView + + + New project + New project + + + + No result found + No result found + + HomeWindow - + Empty Empty - Open Open @@ -77,58 +89,46 @@ Open File Location - Reveal in %1 Reveal in %1 - Remove from "Recovery Files" Remove from "Recovery Files" - Remove from "Recent Files" Remove from "Recent Files" - Recent Files Recent Files - Recovery Files Recovery Files - New project New project - - Search Search - Grid view Grid view - List view List view - No result found No result found - No recovery file If %1 crashes, automatic recovery files will be displayed here. No recovery file @@ -258,123 +258,123 @@ If %1 crashes, automatic recovery files will be displayed here. Disabling %1 will also disable the following plugins: - - + + Restart required Restart required - + Back Back - + Plugin name Plugin name - + Vendor Vendor - + Version Version - + Visit plugin homepage Visit plugin homepage - + Description Description - + Required Required - + Enabled Enabled - + Disabled Disabled - + Plugin status: Error Plugin status: Error - + Plugin status: Not loaded Plugin status: Not loaded - + Plugin status: Loaded Plugin status: Loaded - + Error details Error details - + Details Details - + Dependencies Dependencies - + Plugins required by this plugin, including indirect dependencies Plugins required by this plugin, including indirect dependencies - + Dependents Dependents - + Plugins that require this plugin, including indirect dependants Plugins that require this plugin, including indirect dependants - + Additional Info Additional Info - + Identifier Identifier - + Category Category - + Path Path - + Reveal in %1 Reveal in %1 @@ -387,57 +387,57 @@ If %1 crashes, automatic recovery files will be displayed here. File Explorer - + Compat version Compat version - + Copyright Copyright - + License License - + This plugin does not require other plugins. This plugin does not require other plugins. - + No plugins require this plugin. No plugins require this plugin. - + Plugin is not available on this platform. Plugin is not available on this platform. - + Plugin is enabled as dependency of an enabled plugin. Plugin is enabled as dependency of an enabled plugin. - + Plugin is enabled by command line argument. Plugin is enabled by command line argument. - + Plugin is disabled by command line argument. Plugin is disabled by command line argument. - + Search Search - + No result found No result found @@ -445,27 +445,27 @@ If %1 crashes, automatic recovery files will be displayed here. ProjectWindow - + Empty Empty - + Left Docking View Left Docking View - + Top Docking View Top Docking View - + Bottom Docking View Bottom Docking View - + Right Docking View Right Docking View @@ -478,52 +478,65 @@ If %1 crashes, automatic recovery files will be displayed here. Settings - + Search Search - + No result found No result found - + This page is in compatibility mode This page is in compatibility mode - + This settings page cannot be displayed because its type is not supported. This settings page cannot be displayed because its type is not supported. - + Cannot Apply Settings Cannot Apply Settings - + Failed to apply "%1" Failed to apply "%1" - + OK OK - + Cancel Cancel - + Apply Apply + + TempoTimeSignatureIndicator + + + Tempo + Tempo + + + + Time Signature + Time Signature + + WelcomeWizardDialog diff --git a/src/libs/application/uishell/src/PlatformWindowModifiedHelper.cpp b/src/libs/application/uishell/src/PlatformWindowModifiedHelper.cpp new file mode 100644 index 00000000..333ed534 --- /dev/null +++ b/src/libs/application/uishell/src/PlatformWindowModifiedHelper.cpp @@ -0,0 +1,53 @@ +#include "PlatformWindowModifiedHelper_p.h" + +#include + +namespace UIShell { + + PlatformWindowModifiedHelper::PlatformWindowModifiedHelper(QObject *parent) : QObject(parent) { + } + + PlatformWindowModifiedHelper::~PlatformWindowModifiedHelper() = default; + + QWindow *PlatformWindowModifiedHelper::window() const { + return m_window; + } + + void PlatformWindowModifiedHelper::setWindow(QWindow *window) { + if (m_window == window) + return; + + m_window = window; + Q_EMIT windowChanged(); + + // Update platform window modified state when window changes + updatePlatformWindowModified(); + } + + bool PlatformWindowModifiedHelper::windowModified() const { + return m_windowModified; + } + + void PlatformWindowModifiedHelper::setWindowModified(bool modified) { + if (m_windowModified == modified) + return; + + m_windowModified = modified; + Q_EMIT windowModifiedChanged(); + + // Update platform window modified state when modified flag changes + updatePlatformWindowModified(); + } + + void PlatformWindowModifiedHelper::updatePlatformWindowModified() { + if (!m_window) + return; + + QPlatformWindow *platformWindow = m_window->handle(); + if (!platformWindow) + return; + + platformWindow->setWindowModified(m_windowModified); + } + +} diff --git a/src/libs/application/uishell/src/PlatformWindowModifiedHelper_p.h b/src/libs/application/uishell/src/PlatformWindowModifiedHelper_p.h new file mode 100644 index 00000000..aa7e0f69 --- /dev/null +++ b/src/libs/application/uishell/src/PlatformWindowModifiedHelper_p.h @@ -0,0 +1,40 @@ +#ifndef UISHELL_PLATFORMWINDOWMODIFIEDHELPER_P_H +#define UISHELL_PLATFORMWINDOWMODIFIEDHELPER_P_H + +#include +#include +#include + +namespace UIShell { + + class PlatformWindowModifiedHelper : public QObject { + Q_OBJECT + QML_ELEMENT + + Q_PROPERTY(QWindow *window READ window WRITE setWindow NOTIFY windowChanged) + Q_PROPERTY(bool windowModified READ windowModified WRITE setWindowModified NOTIFY windowModifiedChanged) + + public: + explicit PlatformWindowModifiedHelper(QObject *parent = nullptr); + ~PlatformWindowModifiedHelper() override; + + QWindow *window() const; + void setWindow(QWindow *window); + + bool windowModified() const; + void setWindowModified(bool modified); + + Q_SIGNALS: + void windowChanged(); + void windowModifiedChanged(); + + private: + void updatePlatformWindowModified(); + + QWindow *m_window = nullptr; + bool m_windowModified = false; + }; + +} + +#endif // UISHELL_PLATFORMWINDOWMODIFIEDHELPER_P_H diff --git a/src/libs/application/uishell/src/qml/ProjectWindow.qml b/src/libs/application/uishell/src/qml/ProjectWindow.qml index 8bffa724..64075618 100644 --- a/src/libs/application/uishell/src/qml/ProjectWindow.qml +++ b/src/libs/application/uishell/src/qml/ProjectWindow.qml @@ -176,7 +176,7 @@ Window { id: titleBarArea Layout.fillWidth: true Layout.fillHeight: !window.isMacOS - Layout.preferredHeight: window.isMacOS ? titleBar.height + toolBar.height : 0 + Layout.preferredHeight: window.isMacOS ? titleBar.height + (toolBar.visible ? toolBar.height : 0) : 0 RowLayout { anchors.right: parent.right visible: !window.isMacOS @@ -238,7 +238,7 @@ Window { } } PaneSeparator { - visible: !window.isMacOS + visible: !window.isMacOS && ((separatedMenuParent.visible && menuBar.visualVisible) || toolBar.visible) } Rectangle { id: separatedMenuParent @@ -249,7 +249,7 @@ Window { // FIXME remove spacing when visual invisible } PaneSeparator { - visible: separatedMenuParent.visible && menuBar.visualVisible + visible: separatedMenuParent.visible && menuBar.visualVisible && toolBar.visible } ToolBar { id: toolBar @@ -317,7 +317,9 @@ Window { } } } - PaneSeparator {} + PaneSeparator { + + } Item { id: mainPane readonly property double minimumPanelSize: 64 @@ -429,7 +431,9 @@ Window { } } } - PaneSeparator {} + PaneSeparator { + visible: statusBar.visible + } ToolBar { id: statusBar Accessible.role: Accessible.StatusBar diff --git a/src/plugins/audio/res/translations/org.diffscope.audio.ts b/src/plugins/audio/res/translations/org.diffscope.audio.ts new file mode 100644 index 00000000..db57ea97 --- /dev/null +++ b/src/plugins/audio/res/translations/org.diffscope.audio.ts @@ -0,0 +1,511 @@ + + + + + + + + Main Menu + Main Menu + + + + Application + + + Version %1 + Version %1 + + + + Copyright © %1-%2 %3. All rights reserved. + Copyright © %1-%2 %3. All rights reserved. + + + + Application::ActionText + + Main Menu + Main Menu + + + + Audio::Internal::AudioAndMidiPage + + + Audio and MIDI + Audio and MIDI + + + + Configure audio and MIDI preferences + Configure audio and MIDI preferences + + + + Audio::Internal::AudioOutputPage + + + Audio Output + Audio Output + + + + Configure the audio driver and device for output + Configure the audio driver and device for output + + + + Audio::Internal::AudioOutputSettingsHelper + + + + + + + + + + + + + + + + + + (Not working) + (Not working) + + + + Cannot initialize %1 driver + Cannot initialize %1 driver + + + + + + + + + + Default device + Default device + + + + Audio device %1 is not available + Audio device %1 is not available + + + + Cannot set sample rate to %1 + Cannot set sample rate to %1 + + + + Cannot set buffer size to %1 + Cannot set buffer size to %1 + + + + Audio::Internal::AudioPlugin + + + Initializing audio plugin... + Initializing audio plugin... + + + + Failed to initialize audio output system + Failed to initialize audio output system + + + + %1 will not play sound because no available audio output device found. + +Please go to Settings > Audio and MIDI > Audio Output to check the device status. + %1 will not play sound because no available audio output device found. + +Please go to Settings > Audio and MIDI > Audio Output to check the device status. + + + + Audio::Internal::AudioTroubleshootingDialog + + + Audio Output Troubleshooting Wizard + Audio Output Troubleshooting Wizard + + + + Audio::Internal::ConfigurePage + + + <h3>Configure Audio Device</h3> + <h3>Configure Audio Device</h3> + + + + Please select a working audio driver and device. + Please select a working audio driver and device. + + + + <p>Please test all audio drivers and devices one by one, and try different sample rates and buffer sizes.</p><p>Some devices listed on your computer may be virtual devices and may not output sound to physical hardware.</p> + <p>Please test all audio drivers and devices one by one, and try different sample rates and buffer sizes.</p><p>Some devices listed on your computer may be virtual devices and may not output sound to physical hardware.</p> + + + + Audio d&river + Audio d&river + + + + Audio &device + Audio &device + + + + &Sample rate + &Sample rate + + + + &Buffer size + &Buffer size + + + + &Test + &Test + + + + <p>Before testing, please make sure the device you are monitoring is the one you selected, especially if your computer has multiple audio devices (e.g., speakers and headphones), and the device has been connected to your computer.</p><p>Also please check whether the device is muted or the volume is set to zero.</p> + <p>Before testing, please make sure the device you are monitoring is the one you selected, especially if your computer has multiple audio devices (e.g., speakers and headphones), and the device has been connected to your computer.</p><p>Also please check whether the device is muted or the volume is set to zero.</p> + + + + Have you selected a working audio device which plays sound after clicking the "Test" button? + Have you selected a working audio device which plays sound after clicking the "Test" button? + + + + &Yes + &Yes + + + + I have selected a working audio device + I have selected a working audio device + + + + &No + &No + + + + The device is either not available or not able to play sound + The device is either not available or not able to play sound + + + + Audio::Internal::ExternalCheckPage + + + Please ensure that your audio devices are properly connected to your computer and configured in your system settings. + Please ensure that your audio devices are properly connected to your computer and configured in your system settings. + + + + &Open System Settings + &Open System Settings + + + + &Open Control Panel + &Open Control Panel + + + + <p>Also please check whether other applications can play sound.</p><p>If another audio application (e.g., a DAW) can play sound normally, please review that application’s settings. It may be using "exclusive mode", which allows only that program to access the audio device.</p> + <p>Also please check whether other applications can play sound.</p><p>If another audio application (e.g., a DAW) can play sound normally, please review that application’s settings. It may be using "exclusive mode", which allows only that program to access the audio device.</p> + + + + Are audio devices configured properly and able to be played by other applications? + Are audio devices configured properly and able to be played by other applications? + + + + &Yes + &Yes + + + + The audio device work well on my system. I want to retry configuring it in %1 + The audio device work well on my system. I want to retry configuring it in %1 + + + + Not &exactly + Not &exactly + + + + Even though other applications can play sound, I fail to configure the audio device in %1 + Even though other applications can play sound, I fail to configure the audio device in %1 + + + + &No + &No + + + + Other applications also cannot play sound + Other applications also cannot play sound + + + + &I'm not sure + &I'm not sure + + + + %1 cannot open the control panel on your system. Please open it manually + %1 cannot open the control panel on your system. Please open it manually + + + + Audio::Internal::ResultFailPage + + + Please send us a feedback. + Please send us a feedback. + + + + <p>When sending feedback, please include the following information (if possible): <ul><li>A screenshot of the "Audio Output" settings page</li><li>A screenshot of your system audio settings</li><li>The audio output configurations you have tried and any error messages</li><li>The log of application</li></ul></p> + <p>When sending feedback, please include the following information (if possible): <ul><li>A screenshot of the "Audio Output" settings page</li><li>A screenshot of your system audio settings</li><li>The audio output configurations you have tried and any error messages</li><li>The log of application</li></ul></p> + + + + Audio::Internal::ResultOkPage + + + <h3>Everything Is OK</h3> + <h3>Everything Is OK</h3> + + + + There are no problems with audio output at the moment. + There are no problems with audio output at the moment. + + + + <p>If you still cannot hear sound when playing back your project, please check for issues within the project itself (e.g., muted tracks or gain set to negative infinity).</p> + <p>If you still cannot hear sound when playing back your project, please check for issues within the project itself (e.g., muted tracks or gain set to negative infinity).</p> + + + + Audio::Internal::TestPage + + + <h3>Test Audio Device</h3> + <h3>Test Audio Device</h3> + + + + The audio device appears to be working. Please click the "Test" button to check whether it can play sound. + The audio device appears to be working. Please click the "Test" button to check whether it can play sound. + + + + &Test + &Test + + + + <p>Before testing, please make sure the device you are monitoring is the one you selected, especially if your computer has multiple audio devices (e.g., speakers and headphones), and the device has been properly connected to your computer.</p><p>Also please check whether the device is muted or the volume is set to zero.</p> + <p>Before testing, please make sure the device you are monitoring is the one you selected, especially if your computer has multiple audio devices (e.g., speakers and headphones), and the device has been properly connected to your computer.</p><p>Also please check whether the device is muted or the volume is set to zero.</p> + + + + Is any sound played after clicking the "Test" button? + Is any sound played after clicking the "Test" button? + + + + &Yes + &Yes + + + + I can hear the test sound played by the audio device + I can hear the test sound played by the audio device + + + + &No + &No + + + + I cannot hear any sound played by the audio device + I cannot hear any sound played by the audio device + + + + Audio::Internal::WelcomePage + + + <h3>Audio Output Troubleshooting Wizard</h3> + <h3>Audio Output Troubleshooting Wizard</h3> + + + + This wizard will help you diagnose and resolve problems with audio output. + This wizard will help you diagnose and resolve problems with audio output. + + + + &Continue + &Continue + + + + AudioOutputPage + + + Device Parameters + Device Parameters + + + + Audio driver + Audio driver + + + + Audio device + Audio device + + + + Sample rate + Sample rate + + + + Buffer size + Buffer size + + + + Troubleshoot... + Troubleshoot... + + + + Test + Test + + + + Open Control Panel + Open Control Panel + + + + Mixer + Mixer + + + + Device gain + Device gain + + + + Device pan + Device pan + + + + Hot Plug + Hot Plug + + + + When device change detected + When device change detected + + + + Always show notification + Always show notification + + + + Show notification only when the current device is removed + Show notification only when the current device is removed + + + + Do not show notification + Do not show notification + + + + AudioOutputWelcomeWizardPage + + + Audio Output + Audio Output + + + + Configure audio output device parameters + Configure audio output device parameters + + + + Audio driver + Audio driver + + + + Audio device + Audio device + + + + Sample rate + Sample rate + + + + Buffer size + Buffer size + + + + Test + Test + + + + Open Control Panel + Open Control Panel + + + + Click "Test" button to check whether audio output is configured correctly + Click "Test" button to check whether audio output is configured correctly + + + diff --git a/src/plugins/audio/res/translations/org.diffscope.audio_en_US.ts b/src/plugins/audio/res/translations/org.diffscope.audio_en_US.ts index af6d958b..526c8321 100644 --- a/src/plugins/audio/res/translations/org.diffscope.audio_en_US.ts +++ b/src/plugins/audio/res/translations/org.diffscope.audio_en_US.ts @@ -1,494 +1,511 @@ - + + + + + Main Menu + Main Menu + + + + Application + + + Version %1 + + + + + Copyright © %1-%2 %3. All rights reserved. + + + + Application::ActionText - - Main Menu - Main Menu + Main Menu + Main Menu - - + + Audio::Internal::AudioAndMidiPage - - Audio and MIDI - Audio and MIDI + + Audio and MIDI + Audio and MIDI - - Configure audio and MIDI preferences - Configure audio and MIDI preferences + + Configure audio and MIDI preferences + Configure audio and MIDI preferences - - + + Audio::Internal::AudioOutputPage - - Audio Output - Audio Output + + Audio Output + Audio Output - - Configure the audio driver and device for output - Configure the audio driver and device for output + + Configure the audio driver and device for output + Configure the audio driver and device for output - - + + Audio::Internal::AudioOutputSettingsHelper - - - - - - - - - - - - - - - - - - - (Not working) - (Not working) - - - - Cannot initialize %1 driver - Cannot initialize %1 driver - - - - - - - - - - Default device - Default device - - - - - Audio device %1 is not available - Audio device %1 is not available - - - - Cannot set sample rate to %1 - Cannot set sample rate to %1 - - - - Cannot set buffer size to %1 - Cannot set buffer size to %1 - - - + + + + + + + + + + + + + + + + + (Not working) + (Not working) + + + + Cannot initialize %1 driver + Cannot initialize %1 driver + + + + + + + + + + Default device + Default device + + + + Audio device %1 is not available + Audio device %1 is not available + + + + Cannot set sample rate to %1 + Cannot set sample rate to %1 + + + + Cannot set buffer size to %1 + Cannot set buffer size to %1 + + + Audio::Internal::AudioPlugin - - Initializing audio plugin... - Initializing audio plugin... + + Initializing audio plugin... + Initializing audio plugin... - - Failed to initialize audio output system - Failed to initialize audio output system + + Failed to initialize audio output system + Failed to initialize audio output system - - %1 will not play sound because no available audio output device found. + + %1 will not play sound because no available audio output device found. Please go to Settings > Audio and MIDI > Audio Output to check the device status. - %1 will not play sound because no available audio output device found. + %1 will not play sound because no available audio output device found. Please go to Settings > Audio and MIDI > Audio Output to check the device status. - - + + Audio::Internal::AudioTroubleshootingDialog - - Audio Output Troubleshooting Wizard - Audio Output Troubleshooting Wizard + + Audio Output Troubleshooting Wizard + Audio Output Troubleshooting Wizard - - + + Audio::Internal::ConfigurePage - - <h3>Configure Audio Device</h3> - <h3>Configure Audio Device</h3> + + <h3>Configure Audio Device</h3> + <h3>Configure Audio Device</h3> - - Please select a working audio driver and device. - Please select a working audio driver and device. + + Please select a working audio driver and device. + Please select a working audio driver and device. - - <p>Please test all audio drivers and devices one by one, and try different sample rates and buffer sizes.</p><p>Some devices listed on your computer may be virtual devices and may not output sound to physical hardware.</p> - <p>Please test all audio drivers and devices one by one, and try different sample rates and buffer sizes.</p><p>Some devices listed on your computer may be virtual devices and may not output sound to physical hardware.</p> + + <p>Please test all audio drivers and devices one by one, and try different sample rates and buffer sizes.</p><p>Some devices listed on your computer may be virtual devices and may not output sound to physical hardware.</p> + <p>Please test all audio drivers and devices one by one, and try different sample rates and buffer sizes.</p><p>Some devices listed on your computer may be virtual devices and may not output sound to physical hardware.</p> - - Audio d&river - Audio d&river + + Audio d&river + Audio d&river - - Audio &device - Audio &device + + Audio &device + Audio &device - - &Sample rate - &Sample rate + + &Sample rate + &Sample rate - - &Buffer size - &Buffer size + + &Buffer size + &Buffer size - - &Test - &Test + + &Test + &Test - - <p>Before testing, please make sure the device you are monitoring is the one you selected, especially if your computer has multiple audio devices (e.g., speakers and headphones), and the device has been connected to your computer.</p><p>Also please check whether the device is muted or the volume is set to zero.</p> - <p>Before testing, please make sure the device you are monitoring is the one you selected, especially if your computer has multiple audio devices (e.g., speakers and headphones), and the device has been connected to your computer.</p><p>Also please check whether the device is muted or the volume is set to zero.</p> + + <p>Before testing, please make sure the device you are monitoring is the one you selected, especially if your computer has multiple audio devices (e.g., speakers and headphones), and the device has been connected to your computer.</p><p>Also please check whether the device is muted or the volume is set to zero.</p> + <p>Before testing, please make sure the device you are monitoring is the one you selected, especially if your computer has multiple audio devices (e.g., speakers and headphones), and the device has been connected to your computer.</p><p>Also please check whether the device is muted or the volume is set to zero.</p> - - Have you selected a working audio device which plays sound after clicking the "Test" button? - Have you selected a working audio device which plays sound after clicking the "Test" button? + + Have you selected a working audio device which plays sound after clicking the "Test" button? + Have you selected a working audio device which plays sound after clicking the "Test" button? - - &Yes - &Yes + + &Yes + &Yes - - I have selected a working audio device - I have selected a working audio device + + I have selected a working audio device + I have selected a working audio device - - &No - &No + + &No + &No - - The device is either not available or not able to play sound - The device is either not available or not able to play sound + + The device is either not available or not able to play sound + The device is either not available or not able to play sound - - + + Audio::Internal::ExternalCheckPage - - Please ensure that your audio devices are properly connected to your computer and configured in your system settings. - Please ensure that your audio devices are properly connected to your computer and configured in your system settings. + + Please ensure that your audio devices are properly connected to your computer and configured in your system settings. + Please ensure that your audio devices are properly connected to your computer and configured in your system settings. - - &Open System Settings - &Open System Settings + + &Open System Settings + &Open System Settings - - &Open Control Panel - &Open Control Panel + + &Open Control Panel + &Open Control Panel - - <p>Also please check whether other applications can play sound.</p><p>If another audio application (e.g., a DAW) can play sound normally, please review that application’s settings. It may be using "exclusive mode", which allows only that program to access the audio device.</p> - <p>Also please check whether other applications can play sound.</p><p>If another audio application (e.g., a DAW) can play sound normally, please review that application’s settings. It may be using "exclusive mode", which allows only that program to access the audio device.</p> + + <p>Also please check whether other applications can play sound.</p><p>If another audio application (e.g., a DAW) can play sound normally, please review that application’s settings. It may be using "exclusive mode", which allows only that program to access the audio device.</p> + <p>Also please check whether other applications can play sound.</p><p>If another audio application (e.g., a DAW) can play sound normally, please review that application’s settings. It may be using "exclusive mode", which allows only that program to access the audio device.</p> - - Are audio devices configured properly and able to be played by other applications? - Are audio devices configured properly and able to be played by other applications? + + Are audio devices configured properly and able to be played by other applications? + Are audio devices configured properly and able to be played by other applications? - - &Yes - &Yes + + &Yes + &Yes - - The audio device work well on my system. I want to retry configuring it in %1 - The audio device work well on my system. I want to retry configuring it in %1 + + The audio device work well on my system. I want to retry configuring it in %1 + The audio device work well on my system. I want to retry configuring it in %1 - - Not &exactly - Not &exactly + + Not &exactly + Not &exactly - - Even though other applications can play sound, I fail to configure the audio device in %1 - Even though other applications can play sound, I fail to configure the audio device in %1 + + Even though other applications can play sound, I fail to configure the audio device in %1 + Even though other applications can play sound, I fail to configure the audio device in %1 - - &No - &No + + &No + &No - - Other applications also cannot play sound - Other applications also cannot play sound + + Other applications also cannot play sound + Other applications also cannot play sound - - &I'm not sure - &I'm not sure + + &I'm not sure + &I'm not sure - - %1 cannot open the control panel on your system. Please open it manually - %1 cannot open the control panel on your system. Please open it manually + + %1 cannot open the control panel on your system. Please open it manually + %1 cannot open the control panel on your system. Please open it manually - - + + Audio::Internal::ResultFailPage - - Please send us a feedback. - Please send us a feedback. + + Please send us a feedback. + Please send us a feedback. - - <p>When sending feedback, please include the following information (if possible): <ul><li>A screenshot of the "Audio Output" settings page</li><li>A screenshot of your system audio settings</li><li>The audio output configurations you have tried and any error messages</li><li>The log of application</li></ul></p> - <p>When sending feedback, please include the following information (if possible): <ul><li>A screenshot of the "Audio Output" settings page</li><li>A screenshot of your system audio settings</li><li>The audio output configurations you have tried and any error messages</li><li>The log of application</li></ul></p> + + <p>When sending feedback, please include the following information (if possible): <ul><li>A screenshot of the "Audio Output" settings page</li><li>A screenshot of your system audio settings</li><li>The audio output configurations you have tried and any error messages</li><li>The log of application</li></ul></p> + <p>When sending feedback, please include the following information (if possible): <ul><li>A screenshot of the "Audio Output" settings page</li><li>A screenshot of your system audio settings</li><li>The audio output configurations you have tried and any error messages</li><li>The log of application</li></ul></p> - - + + Audio::Internal::ResultOkPage - - <h3>Everything Is OK</h3> - <h3>Everything Is OK</h3> + + <h3>Everything Is OK</h3> + <h3>Everything Is OK</h3> - - There are no problems with audio output at the moment. - There are no problems with audio output at the moment. + + There are no problems with audio output at the moment. + There are no problems with audio output at the moment. - - <p>If you still cannot hear sound when playing back your project, please check for issues within the project itself (e.g., muted tracks or gain set to negative infinity).</p> - <p>If you still cannot hear sound when playing back your project, please check for issues within the project itself (e.g., muted tracks or gain set to negative infinity).</p> + + <p>If you still cannot hear sound when playing back your project, please check for issues within the project itself (e.g., muted tracks or gain set to negative infinity).</p> + <p>If you still cannot hear sound when playing back your project, please check for issues within the project itself (e.g., muted tracks or gain set to negative infinity).</p> - - + + Audio::Internal::TestPage - - <h3>Test Audio Device</h3> - <h3>Test Audio Device</h3> + + <h3>Test Audio Device</h3> + <h3>Test Audio Device</h3> - - The audio device appears to be working. Please click the "Test" button to check whether it can play sound. - The audio device appears to be working. Please click the "Test" button to check whether it can play sound. + + The audio device appears to be working. Please click the "Test" button to check whether it can play sound. + The audio device appears to be working. Please click the "Test" button to check whether it can play sound. - - &Test - &Test + + &Test + &Test - - <p>Before testing, please make sure the device you are monitoring is the one you selected, especially if your computer has multiple audio devices (e.g., speakers and headphones), and the device has been properly connected to your computer.</p><p>Also please check whether the device is muted or the volume is set to zero.</p> - <p>Before testing, please make sure the device you are monitoring is the one you selected, especially if your computer has multiple audio devices (e.g., speakers and headphones), and the device has been properly connected to your computer.</p><p>Also please check whether the device is muted or the volume is set to zero.</p> + + <p>Before testing, please make sure the device you are monitoring is the one you selected, especially if your computer has multiple audio devices (e.g., speakers and headphones), and the device has been properly connected to your computer.</p><p>Also please check whether the device is muted or the volume is set to zero.</p> + <p>Before testing, please make sure the device you are monitoring is the one you selected, especially if your computer has multiple audio devices (e.g., speakers and headphones), and the device has been properly connected to your computer.</p><p>Also please check whether the device is muted or the volume is set to zero.</p> - - Is any sound played after clicking the "Test" button? - Is any sound played after clicking the "Test" button? + + Is any sound played after clicking the "Test" button? + Is any sound played after clicking the "Test" button? - - &Yes - &Yes + + &Yes + &Yes - - I can hear the test sound played by the audio device - I can hear the test sound played by the audio device + + I can hear the test sound played by the audio device + I can hear the test sound played by the audio device - - &No - &No + + &No + &No - - I cannot hear any sound played by the audio device - I cannot hear any sound played by the audio device + + I cannot hear any sound played by the audio device + I cannot hear any sound played by the audio device - - + + Audio::Internal::WelcomePage - - <h3>Audio Output Troubleshooting Wizard</h3> - <h3>Audio Output Troubleshooting Wizard</h3> + + <h3>Audio Output Troubleshooting Wizard</h3> + <h3>Audio Output Troubleshooting Wizard</h3> - - This wizard will help you diagnose and resolve problems with audio output. - This wizard will help you diagnose and resolve problems with audio output. + + This wizard will help you diagnose and resolve problems with audio output. + This wizard will help you diagnose and resolve problems with audio output. - - &Continue - &Continue + + &Continue + &Continue - - + + AudioOutputPage - - Device Parameters - Device Parameters + + Device Parameters + Device Parameters - - Audio driver - Audio driver + + Audio driver + Audio driver - - Audio device - Audio device + + Audio device + Audio device - - Sample rate - Sample rate + + Sample rate + Sample rate - - Buffer size - Buffer size + + Buffer size + Buffer size - - Troubleshoot... - Troubleshoot... + + Troubleshoot... + Troubleshoot... - - Test - Test + + Test + Test - - Open Control Panel - Open Control Panel + + Open Control Panel + Open Control Panel - - Mixer - Mixer + + Mixer + Mixer - - Device gain - Device gain + + Device gain + Device gain - - Device pan - Device pan + + Device pan + Device pan - - Hot Plug - Hot Plug + + Hot Plug + Hot Plug - - When device change detected - When device change detected + + When device change detected + When device change detected - - Always show notification - Always show notification + + Always show notification + Always show notification - - Show notification only when the current device is removed - Show notification only when the current device is removed + + Show notification only when the current device is removed + Show notification only when the current device is removed - - Do not show notification - Do not show notification + + Do not show notification + Do not show notification - - + + AudioOutputWelcomeWizardPage - - Audio Output - Audio Output + + Audio Output + Audio Output - - Configure audio output device parameters - Configure audio output device parameters + + Configure audio output device parameters + Configure audio output device parameters - - Audio driver - Audio driver + + Audio driver + Audio driver - - Audio device - Audio device + + Audio device + Audio device - - Sample rate - Sample rate + + Sample rate + Sample rate - - Buffer size - Buffer size + + Buffer size + Buffer size - - Test - Test + + Test + Test - - Open Control Panel - Open Control Panel + + Open Control Panel + Open Control Panel - - Click "Test" button to check whether audio output is configured correctly - Click "Test" button to check whether audio output is configured correctly + + Click "Test" button to check whether audio output is configured correctly + Click "Test" button to check whether audio output is configured correctly - + diff --git a/src/plugins/audio/res/translations/org.diffscope.audio_ja_JP.ts b/src/plugins/audio/res/translations/org.diffscope.audio_ja_JP.ts index f703f905..09543cd8 100644 --- a/src/plugins/audio/res/translations/org.diffscope.audio_ja_JP.ts +++ b/src/plugins/audio/res/translations/org.diffscope.audio_ja_JP.ts @@ -1,10 +1,30 @@ + + + + + Main Menu + Main Menu + + + + Application + + + Version %1 + Version %1 + + + + Copyright © %1-%2 %3. All rights reserved. + Copyright © %1-%2 %3. All rights reserved. + + Application::ActionText - Main Menu Main Menu @@ -12,12 +32,12 @@ Audio::Internal::AudioAndMidiPage - + Audio and MIDI Audio and MIDI - + Configure audio and MIDI preferences Configure audio and MIDI preferences @@ -25,12 +45,12 @@ Audio::Internal::AudioOutputPage - + Audio Output Audio Output - + Configure the audio driver and device for output Configure the audio driver and device for output @@ -38,56 +58,53 @@ Audio::Internal::AudioOutputSettingsHelper - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + (Not working) (Not working) - + Cannot initialize %1 driver Cannot initialize %1 driver - - - - - - - + + + + + + + Default device Default device - - + Audio device %1 is not available Audio device %1 is not available - + Cannot set sample rate to %1 Cannot set sample rate to %1 - + Cannot set buffer size to %1 Cannot set buffer size to %1 @@ -95,17 +112,17 @@ Audio::Internal::AudioPlugin - + Initializing audio plugin... Initializing audio plugin... - + Failed to initialize audio output system Failed to initialize audio output system - + %1 will not play sound because no available audio output device found. Please go to Settings > Audio and MIDI > Audio Output to check the device status. @@ -117,7 +134,7 @@ Please go to Settings > Audio and MIDI > Audio Output to check the device Audio::Internal::AudioTroubleshootingDialog - + Audio Output Troubleshooting Wizard Audio Output Troubleshooting Wizard @@ -125,72 +142,72 @@ Please go to Settings > Audio and MIDI > Audio Output to check the device Audio::Internal::ConfigurePage - + <h3>Configure Audio Device</h3> <h3>Configure Audio Device</h3> - + Please select a working audio driver and device. Please select a working audio driver and device. - + <p>Please test all audio drivers and devices one by one, and try different sample rates and buffer sizes.</p><p>Some devices listed on your computer may be virtual devices and may not output sound to physical hardware.</p> <p>Please test all audio drivers and devices one by one, and try different sample rates and buffer sizes.</p><p>Some devices listed on your computer may be virtual devices and may not output sound to physical hardware.</p> - + Audio d&river Audio d&river - + Audio &device Audio &device - + &Sample rate &Sample rate - + &Buffer size &Buffer size - + &Test &Test - + <p>Before testing, please make sure the device you are monitoring is the one you selected, especially if your computer has multiple audio devices (e.g., speakers and headphones), and the device has been connected to your computer.</p><p>Also please check whether the device is muted or the volume is set to zero.</p> <p>Before testing, please make sure the device you are monitoring is the one you selected, especially if your computer has multiple audio devices (e.g., speakers and headphones), and the device has been connected to your computer.</p><p>Also please check whether the device is muted or the volume is set to zero.</p> - + Have you selected a working audio device which plays sound after clicking the "Test" button? Have you selected a working audio device which plays sound after clicking the "Test" button? - + &Yes &Yes - + I have selected a working audio device I have selected a working audio device - + &No &No - + The device is either not available or not able to play sound The device is either not available or not able to play sound @@ -198,67 +215,67 @@ Please go to Settings > Audio and MIDI > Audio Output to check the device Audio::Internal::ExternalCheckPage - + Please ensure that your audio devices are properly connected to your computer and configured in your system settings. Please ensure that your audio devices are properly connected to your computer and configured in your system settings. - + &Open System Settings &Open System Settings - + &Open Control Panel &Open Control Panel - + <p>Also please check whether other applications can play sound.</p><p>If another audio application (e.g., a DAW) can play sound normally, please review that application’s settings. It may be using "exclusive mode", which allows only that program to access the audio device.</p> <p>Also please check whether other applications can play sound.</p><p>If another audio application (e.g., a DAW) can play sound normally, please review that application’s settings. It may be using "exclusive mode", which allows only that program to access the audio device.</p> - + Are audio devices configured properly and able to be played by other applications? Are audio devices configured properly and able to be played by other applications? - + &Yes &Yes - + The audio device work well on my system. I want to retry configuring it in %1 The audio device work well on my system. I want to retry configuring it in %1 - + Not &exactly Not &exactly - + Even though other applications can play sound, I fail to configure the audio device in %1 Even though other applications can play sound, I fail to configure the audio device in %1 - + &No &No - + Other applications also cannot play sound Other applications also cannot play sound - + &I'm not sure &I'm not sure - + %1 cannot open the control panel on your system. Please open it manually %1 cannot open the control panel on your system. Please open it manually @@ -266,12 +283,12 @@ Please go to Settings > Audio and MIDI > Audio Output to check the device Audio::Internal::ResultFailPage - + Please send us a feedback. Please send us a feedback. - + <p>When sending feedback, please include the following information (if possible): <ul><li>A screenshot of the "Audio Output" settings page</li><li>A screenshot of your system audio settings</li><li>The audio output configurations you have tried and any error messages</li><li>The log of application</li></ul></p> <p>When sending feedback, please include the following information (if possible): <ul><li>A screenshot of the "Audio Output" settings page</li><li>A screenshot of your system audio settings</li><li>The audio output configurations you have tried and any error messages</li><li>The log of application</li></ul></p> @@ -279,17 +296,17 @@ Please go to Settings > Audio and MIDI > Audio Output to check the device Audio::Internal::ResultOkPage - + <h3>Everything Is OK</h3> <h3>Everything Is OK</h3> - + There are no problems with audio output at the moment. There are no problems with audio output at the moment. - + <p>If you still cannot hear sound when playing back your project, please check for issues within the project itself (e.g., muted tracks or gain set to negative infinity).</p> <p>If you still cannot hear sound when playing back your project, please check for issues within the project itself (e.g., muted tracks or gain set to negative infinity).</p> @@ -297,47 +314,47 @@ Please go to Settings > Audio and MIDI > Audio Output to check the device Audio::Internal::TestPage - + <h3>Test Audio Device</h3> <h3>Test Audio Device</h3> - + The audio device appears to be working. Please click the "Test" button to check whether it can play sound. The audio device appears to be working. Please click the "Test" button to check whether it can play sound. - + &Test &Test - + <p>Before testing, please make sure the device you are monitoring is the one you selected, especially if your computer has multiple audio devices (e.g., speakers and headphones), and the device has been properly connected to your computer.</p><p>Also please check whether the device is muted or the volume is set to zero.</p> <p>Before testing, please make sure the device you are monitoring is the one you selected, especially if your computer has multiple audio devices (e.g., speakers and headphones), and the device has been properly connected to your computer.</p><p>Also please check whether the device is muted or the volume is set to zero.</p> - + Is any sound played after clicking the "Test" button? Is any sound played after clicking the "Test" button? - + &Yes &Yes - + I can hear the test sound played by the audio device I can hear the test sound played by the audio device - + &No &No - + I cannot hear any sound played by the audio device I cannot hear any sound played by the audio device @@ -345,17 +362,17 @@ Please go to Settings > Audio and MIDI > Audio Output to check the device Audio::Internal::WelcomePage - + <h3>Audio Output Troubleshooting Wizard</h3> <h3>Audio Output Troubleshooting Wizard</h3> - + This wizard will help you diagnose and resolve problems with audio output. This wizard will help you diagnose and resolve problems with audio output. - + &Continue &Continue diff --git a/src/plugins/audio/res/translations/org.diffscope.audio_zh_CN.ts b/src/plugins/audio/res/translations/org.diffscope.audio_zh_CN.ts index e1c26314..c17c0764 100644 --- a/src/plugins/audio/res/translations/org.diffscope.audio_zh_CN.ts +++ b/src/plugins/audio/res/translations/org.diffscope.audio_zh_CN.ts @@ -1,10 +1,30 @@ + + + + + Main Menu + 主菜单 + + + + Application + + + Version %1 + Version %1 + + + + Copyright © %1-%2 %3. All rights reserved. + Copyright © %1-%2 %3. All rights reserved. + + Application::ActionText - Main Menu 主菜单 @@ -12,12 +32,12 @@ Audio::Internal::AudioAndMidiPage - + Audio and MIDI 音频与 MIDI - + Configure audio and MIDI preferences 配置音频和 MIDI 首选项 @@ -25,12 +45,12 @@ Audio::Internal::AudioOutputPage - + Audio Output 音频输出 - + Configure the audio driver and device for output 配置用于输出的音频设备 @@ -38,56 +58,53 @@ Audio::Internal::AudioOutputSettingsHelper - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + (Not working) (不工作) - + Cannot initialize %1 driver 无法初始化 %1 驱动 - - - - - - - + + + + + + + Default device 默认设备 - - + Audio device %1 is not available 音频设备 %1 不可用 - + Cannot set sample rate to %1 无法设置采样率为 %1 - + Cannot set buffer size to %1 无法设置缓冲区大小为 %1 @@ -95,17 +112,17 @@ Audio::Internal::AudioPlugin - + Initializing audio plugin... 正在初始化音频插件... - + Failed to initialize audio output system 初始化音频输出系统失败 - + %1 will not play sound because no available audio output device found. Please go to Settings > Audio and MIDI > Audio Output to check the device status. @@ -117,7 +134,7 @@ Please go to Settings > Audio and MIDI > Audio Output to check the device Audio::Internal::AudioTroubleshootingDialog - + Audio Output Troubleshooting Wizard Audio Output Troubleshooting Wizard @@ -125,72 +142,72 @@ Please go to Settings > Audio and MIDI > Audio Output to check the device Audio::Internal::ConfigurePage - + <h3>Configure Audio Device</h3> <h3>Configure Audio Device</h3> - + Please select a working audio driver and device. Please select a working audio driver and device. - + <p>Please test all audio drivers and devices one by one, and try different sample rates and buffer sizes.</p><p>Some devices listed on your computer may be virtual devices and may not output sound to physical hardware.</p> <p>Please test all audio drivers and devices one by one, and try different sample rates and buffer sizes.</p><p>Some devices listed on your computer may be virtual devices and may not output sound to physical hardware.</p> - + Audio d&river Audio d&river - + Audio &device Audio &device - + &Sample rate &Sample rate - + &Buffer size &Buffer size - + &Test &Test - + <p>Before testing, please make sure the device you are monitoring is the one you selected, especially if your computer has multiple audio devices (e.g., speakers and headphones), and the device has been connected to your computer.</p><p>Also please check whether the device is muted or the volume is set to zero.</p> <p>Before testing, please make sure the device you are monitoring is the one you selected, especially if your computer has multiple audio devices (e.g., speakers and headphones), and the device has been connected to your computer.</p><p>Also please check whether the device is muted or the volume is set to zero.</p> - + Have you selected a working audio device which plays sound after clicking the "Test" button? Have you selected a working audio device which plays sound after clicking the "Test" button? - + &Yes &Yes - + I have selected a working audio device I have selected a working audio device - + &No &No - + The device is either not available or not able to play sound The device is either not available or not able to play sound @@ -198,67 +215,67 @@ Please go to Settings > Audio and MIDI > Audio Output to check the device Audio::Internal::ExternalCheckPage - + Please ensure that your audio devices are properly connected to your computer and configured in your system settings. Please ensure that your audio devices are properly connected to your computer and configured in your system settings. - + &Open System Settings &Open System Settings - + &Open Control Panel &Open Control Panel - + <p>Also please check whether other applications can play sound.</p><p>If another audio application (e.g., a DAW) can play sound normally, please review that application’s settings. It may be using "exclusive mode", which allows only that program to access the audio device.</p> <p>Also please check whether other applications can play sound.</p><p>If another audio application (e.g., a DAW) can play sound normally, please review that application’s settings. It may be using "exclusive mode", which allows only that program to access the audio device.</p> - + Are audio devices configured properly and able to be played by other applications? Are audio devices configured properly and able to be played by other applications? - + &Yes &Yes - + The audio device work well on my system. I want to retry configuring it in %1 The audio device work well on my system. I want to retry configuring it in %1 - + Not &exactly Not &exactly - + Even though other applications can play sound, I fail to configure the audio device in %1 Even though other applications can play sound, I fail to configure the audio device in %1 - + &No &No - + Other applications also cannot play sound Other applications also cannot play sound - + &I'm not sure &I'm not sure - + %1 cannot open the control panel on your system. Please open it manually %1 cannot open the control panel on your system. Please open it manually @@ -266,12 +283,12 @@ Please go to Settings > Audio and MIDI > Audio Output to check the device Audio::Internal::ResultFailPage - + Please send us a feedback. Please send us a feedback. - + <p>When sending feedback, please include the following information (if possible): <ul><li>A screenshot of the "Audio Output" settings page</li><li>A screenshot of your system audio settings</li><li>The audio output configurations you have tried and any error messages</li><li>The log of application</li></ul></p> <p>When sending feedback, please include the following information (if possible): <ul><li>A screenshot of the "Audio Output" settings page</li><li>A screenshot of your system audio settings</li><li>The audio output configurations you have tried and any error messages</li><li>The log of application</li></ul></p> @@ -279,17 +296,17 @@ Please go to Settings > Audio and MIDI > Audio Output to check the device Audio::Internal::ResultOkPage - + <h3>Everything Is OK</h3> <h3>Everything Is OK</h3> - + There are no problems with audio output at the moment. There are no problems with audio output at the moment. - + <p>If you still cannot hear sound when playing back your project, please check for issues within the project itself (e.g., muted tracks or gain set to negative infinity).</p> <p>If you still cannot hear sound when playing back your project, please check for issues within the project itself (e.g., muted tracks or gain set to negative infinity).</p> @@ -297,47 +314,47 @@ Please go to Settings > Audio and MIDI > Audio Output to check the device Audio::Internal::TestPage - + <h3>Test Audio Device</h3> <h3>Test Audio Device</h3> - + The audio device appears to be working. Please click the "Test" button to check whether it can play sound. The audio device appears to be working. Please click the "Test" button to check whether it can play sound. - + &Test &Test - + <p>Before testing, please make sure the device you are monitoring is the one you selected, especially if your computer has multiple audio devices (e.g., speakers and headphones), and the device has been properly connected to your computer.</p><p>Also please check whether the device is muted or the volume is set to zero.</p> <p>Before testing, please make sure the device you are monitoring is the one you selected, especially if your computer has multiple audio devices (e.g., speakers and headphones), and the device has been properly connected to your computer.</p><p>Also please check whether the device is muted or the volume is set to zero.</p> - + Is any sound played after clicking the "Test" button? Is any sound played after clicking the "Test" button? - + &Yes &Yes - + I can hear the test sound played by the audio device I can hear the test sound played by the audio device - + &No &No - + I cannot hear any sound played by the audio device I cannot hear any sound played by the audio device @@ -345,17 +362,17 @@ Please go to Settings > Audio and MIDI > Audio Output to check the device Audio::Internal::WelcomePage - + <h3>Audio Output Troubleshooting Wizard</h3> <h3>Audio Output Troubleshooting Wizard</h3> - + This wizard will help you diagnose and resolve problems with audio output. This wizard will help you diagnose and resolve problems with audio output. - + &Continue &Continue diff --git a/src/plugins/audio/res/translations/org.diffscope.audio_zh_TW.ts b/src/plugins/audio/res/translations/org.diffscope.audio_zh_TW.ts index b2a0bea4..ebb39df3 100644 --- a/src/plugins/audio/res/translations/org.diffscope.audio_zh_TW.ts +++ b/src/plugins/audio/res/translations/org.diffscope.audio_zh_TW.ts @@ -1,10 +1,30 @@ + + + + + Main Menu + Main Menu + + + + Application + + + Version %1 + Version %1 + + + + Copyright © %1-%2 %3. All rights reserved. + Copyright © %1-%2 %3. All rights reserved. + + Application::ActionText - Main Menu Main Menu @@ -12,12 +32,12 @@ Audio::Internal::AudioAndMidiPage - + Audio and MIDI Audio and MIDI - + Configure audio and MIDI preferences Configure audio and MIDI preferences @@ -25,12 +45,12 @@ Audio::Internal::AudioOutputPage - + Audio Output Audio Output - + Configure the audio driver and device for output Configure the audio driver and device for output @@ -38,56 +58,53 @@ Audio::Internal::AudioOutputSettingsHelper - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + (Not working) (Not working) - + Cannot initialize %1 driver Cannot initialize %1 driver - - - - - - - + + + + + + + Default device Default device - - + Audio device %1 is not available Audio device %1 is not available - + Cannot set sample rate to %1 Cannot set sample rate to %1 - + Cannot set buffer size to %1 Cannot set buffer size to %1 @@ -95,17 +112,17 @@ Audio::Internal::AudioPlugin - + Initializing audio plugin... Initializing audio plugin... - + Failed to initialize audio output system Failed to initialize audio output system - + %1 will not play sound because no available audio output device found. Please go to Settings > Audio and MIDI > Audio Output to check the device status. @@ -117,7 +134,7 @@ Please go to Settings > Audio and MIDI > Audio Output to check the device Audio::Internal::AudioTroubleshootingDialog - + Audio Output Troubleshooting Wizard Audio Output Troubleshooting Wizard @@ -125,72 +142,72 @@ Please go to Settings > Audio and MIDI > Audio Output to check the device Audio::Internal::ConfigurePage - + <h3>Configure Audio Device</h3> <h3>Configure Audio Device</h3> - + Please select a working audio driver and device. Please select a working audio driver and device. - + <p>Please test all audio drivers and devices one by one, and try different sample rates and buffer sizes.</p><p>Some devices listed on your computer may be virtual devices and may not output sound to physical hardware.</p> <p>Please test all audio drivers and devices one by one, and try different sample rates and buffer sizes.</p><p>Some devices listed on your computer may be virtual devices and may not output sound to physical hardware.</p> - + Audio d&river Audio d&river - + Audio &device Audio &device - + &Sample rate &Sample rate - + &Buffer size &Buffer size - + &Test &Test - + <p>Before testing, please make sure the device you are monitoring is the one you selected, especially if your computer has multiple audio devices (e.g., speakers and headphones), and the device has been connected to your computer.</p><p>Also please check whether the device is muted or the volume is set to zero.</p> <p>Before testing, please make sure the device you are monitoring is the one you selected, especially if your computer has multiple audio devices (e.g., speakers and headphones), and the device has been connected to your computer.</p><p>Also please check whether the device is muted or the volume is set to zero.</p> - + Have you selected a working audio device which plays sound after clicking the "Test" button? Have you selected a working audio device which plays sound after clicking the "Test" button? - + &Yes &Yes - + I have selected a working audio device I have selected a working audio device - + &No &No - + The device is either not available or not able to play sound The device is either not available or not able to play sound @@ -198,67 +215,67 @@ Please go to Settings > Audio and MIDI > Audio Output to check the device Audio::Internal::ExternalCheckPage - + Please ensure that your audio devices are properly connected to your computer and configured in your system settings. Please ensure that your audio devices are properly connected to your computer and configured in your system settings. - + &Open System Settings &Open System Settings - + &Open Control Panel &Open Control Panel - + <p>Also please check whether other applications can play sound.</p><p>If another audio application (e.g., a DAW) can play sound normally, please review that application’s settings. It may be using "exclusive mode", which allows only that program to access the audio device.</p> <p>Also please check whether other applications can play sound.</p><p>If another audio application (e.g., a DAW) can play sound normally, please review that application’s settings. It may be using "exclusive mode", which allows only that program to access the audio device.</p> - + Are audio devices configured properly and able to be played by other applications? Are audio devices configured properly and able to be played by other applications? - + &Yes &Yes - + The audio device work well on my system. I want to retry configuring it in %1 The audio device work well on my system. I want to retry configuring it in %1 - + Not &exactly Not &exactly - + Even though other applications can play sound, I fail to configure the audio device in %1 Even though other applications can play sound, I fail to configure the audio device in %1 - + &No &No - + Other applications also cannot play sound Other applications also cannot play sound - + &I'm not sure &I'm not sure - + %1 cannot open the control panel on your system. Please open it manually %1 cannot open the control panel on your system. Please open it manually @@ -266,12 +283,12 @@ Please go to Settings > Audio and MIDI > Audio Output to check the device Audio::Internal::ResultFailPage - + Please send us a feedback. Please send us a feedback. - + <p>When sending feedback, please include the following information (if possible): <ul><li>A screenshot of the "Audio Output" settings page</li><li>A screenshot of your system audio settings</li><li>The audio output configurations you have tried and any error messages</li><li>The log of application</li></ul></p> <p>When sending feedback, please include the following information (if possible): <ul><li>A screenshot of the "Audio Output" settings page</li><li>A screenshot of your system audio settings</li><li>The audio output configurations you have tried and any error messages</li><li>The log of application</li></ul></p> @@ -279,17 +296,17 @@ Please go to Settings > Audio and MIDI > Audio Output to check the device Audio::Internal::ResultOkPage - + <h3>Everything Is OK</h3> <h3>Everything Is OK</h3> - + There are no problems with audio output at the moment. There are no problems with audio output at the moment. - + <p>If you still cannot hear sound when playing back your project, please check for issues within the project itself (e.g., muted tracks or gain set to negative infinity).</p> <p>If you still cannot hear sound when playing back your project, please check for issues within the project itself (e.g., muted tracks or gain set to negative infinity).</p> @@ -297,47 +314,47 @@ Please go to Settings > Audio and MIDI > Audio Output to check the device Audio::Internal::TestPage - + <h3>Test Audio Device</h3> <h3>Test Audio Device</h3> - + The audio device appears to be working. Please click the "Test" button to check whether it can play sound. The audio device appears to be working. Please click the "Test" button to check whether it can play sound. - + &Test &Test - + <p>Before testing, please make sure the device you are monitoring is the one you selected, especially if your computer has multiple audio devices (e.g., speakers and headphones), and the device has been properly connected to your computer.</p><p>Also please check whether the device is muted or the volume is set to zero.</p> <p>Before testing, please make sure the device you are monitoring is the one you selected, especially if your computer has multiple audio devices (e.g., speakers and headphones), and the device has been properly connected to your computer.</p><p>Also please check whether the device is muted or the volume is set to zero.</p> - + Is any sound played after clicking the "Test" button? Is any sound played after clicking the "Test" button? - + &Yes &Yes - + I can hear the test sound played by the audio device I can hear the test sound played by the audio device - + &No &No - + I cannot hear any sound played by the audio device I cannot hear any sound played by the audio device @@ -345,17 +362,17 @@ Please go to Settings > Audio and MIDI > Audio Output to check the device Audio::Internal::WelcomePage - + <h3>Audio Output Troubleshooting Wizard</h3> <h3>Audio Output Troubleshooting Wizard</h3> - + This wizard will help you diagnose and resolve problems with audio output. This wizard will help you diagnose and resolve problems with audio output. - + &Continue &Continue diff --git a/src/plugins/coreplugin/core/CoreInterface.cpp b/src/plugins/coreplugin/core/CoreInterface.cpp index a44ea4d5..5c920fc2 100644 --- a/src/plugins/coreplugin/core/CoreInterface.cpp +++ b/src/plugins/coreplugin/core/CoreInterface.cpp @@ -253,23 +253,23 @@ namespace Core { .version = QDspx::Model::V1, .content = { .global = {}, - .master = { - .control = { - .gain = 1, - .pan = 0, - .mute = false, - } - }, .timeline = { .labels = {}, .tempos = {{0, 120}}, .timeSignatures = {{0, 4, 4}} + }, + .tracks = { + QDspx::Track{ + .name = tr("Unnamed track") + } } } }; qCInfo(lcCoreInterface) << "New file"; auto projectDocumentContext = std::make_unique(); - projectDocumentContext->newFile(defaultModel, false); + if (!projectDocumentContext->newFile(defaultModel, false)) { + return nullptr; + } auto windowInterface = createProjectWindow(projectDocumentContext.release()); return windowInterface; } diff --git a/src/plugins/coreplugin/internal/BehaviorPreference.cpp b/src/plugins/coreplugin/internal/BehaviorPreference.cpp index 102e40f1..ab0f5de1 100644 --- a/src/plugins/coreplugin/internal/BehaviorPreference.cpp +++ b/src/plugins/coreplugin/internal/BehaviorPreference.cpp @@ -58,7 +58,7 @@ namespace Core::Internal { Q_D(BehaviorPreference); auto settings = RuntimeInterface::settings(); settings->beginGroup(staticMetaObject.className()); - d->startupBehavior = settings->value("startupBehavior", QVariant::fromValue(SB_CloseHomeWindowAfterOpeningProject)).value(); + d->startupBehavior = settings->value("startupBehavior", QVariant::fromValue(SB_CloseHomeWindowAfterOpeningProject | SB_OpenHomeWindowWhenLastProjectClosed)).value(); emit startupBehaviorChanged(); d->useSystemLanguage = settings->value("useSystemLanguage", true).toBool(); emit useSystemLanguageChanged(); diff --git a/src/plugins/coreplugin/internal/BehaviorPreference.h b/src/plugins/coreplugin/internal/BehaviorPreference.h index aecc332d..08dc4c89 100644 --- a/src/plugins/coreplugin/internal/BehaviorPreference.h +++ b/src/plugins/coreplugin/internal/BehaviorPreference.h @@ -62,6 +62,7 @@ namespace Core::Internal { enum StartupBehaviorFlag { SB_CreateNewProject = 0x01, SB_CloseHomeWindowAfterOpeningProject = 0x02, + SB_OpenHomeWindowWhenLastProjectClosed = 0x04, }; Q_ENUM(StartupBehaviorFlag) Q_DECLARE_FLAGS(StartupBehavior, StartupBehaviorFlag) diff --git a/src/plugins/coreplugin/internal/CorePlugin.cpp b/src/plugins/coreplugin/internal/CorePlugin.cpp index d7566c55..13364250 100644 --- a/src/plugins/coreplugin/internal/CorePlugin.cpp +++ b/src/plugins/coreplugin/internal/CorePlugin.cpp @@ -1,13 +1,5 @@ #include "CorePlugin.h" -#ifdef Q_OS_WIN -# include -# include -# include -# include -# include -#endif - #include #include @@ -37,6 +29,7 @@ #include #include +#include #include @@ -51,6 +44,7 @@ #include #include #include +#include #include #include #include @@ -66,6 +60,9 @@ #include #include #include +#include +#include +#include static auto getCoreActionExtension() { return QAK_STATIC_ACTION_EXTENSION(coreplugin); @@ -131,6 +128,9 @@ namespace Core::Internal { static constexpr char kOpenSettingsArg[] = "--open-settings"; static constexpr char kNewProjectArg[] = "--new"; + static constexpr char kNoWarningLastRun[] = "--no-warning-last-run"; + static constexpr char kFatalWithoutPlugins[] = "--fatal-without-plugins"; + static constexpr char kNoWarningPluginError[] = "--no-warning-plugin-error"; static ActionWindowInterfaceBase *initializeGui(const QStringList &options, const QString &workingDirectory, const QStringList &args) { qCDebug(lcCorePlugin) << "Initializing GUI" << options << args; @@ -197,8 +197,12 @@ namespace Core::Internal { } void CorePlugin::extensionsInitialized() { - auto settings = RuntimeInterface::settings(); - settings->setValue("lastInitializationAbortedFlag", false); + RuntimeInterface::splash()->showMessage(tr("Plugins loading complete, preparing for subsequent initialization...")); + for (auto plugin : ExtensionSystem::PluginManager::plugins()) { + qCInfo(lcCorePlugin) << "Plugin" << plugin->name() << "enabled =" << plugin->isEffectivelyEnabled() << "enabledBySettings =" << plugin->isEnabledBySettings() << "hasError =" << plugin->hasError(); + } + checkLastRun(); + checkPlugins(); } bool CorePlugin::delayedInitialize() { @@ -237,85 +241,15 @@ namespace Core::Internal { return QObject::eventFilter(obj, event); } -#ifdef Q_OS_WIN - static void initializeWindowsJumpList() { - CoInitialize(nullptr); - - CComPtr pcdl; - HRESULT hr = pcdl.CoCreateInstance(CLSID_DestinationList, nullptr, CLSCTX_INPROC_SERVER); - if (FAILED(hr)) { - CoUninitialize(); - return; - } - - UINT cMinSlots; - CComPtr poaRemoved; - hr = pcdl->BeginList(&cMinSlots, IID_PPV_ARGS(&poaRemoved)); - if (FAILED(hr)) { - CoUninitialize(); - return; - } - - CComPtr poc; - hr = poc.CoCreateInstance(CLSID_EnumerableObjectCollection, nullptr, CLSCTX_INPROC_SERVER); - if (FAILED(hr)) { - CoUninitialize(); - return; - } - - CComPtr psl; - hr = psl.CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER); - if (FAILED(hr)) { - CoUninitialize(); - return; - } - - auto appPath = QApplication::applicationFilePath().toStdWString(); - psl->SetPath(appPath.c_str()); - psl->SetArguments(L"--new"); - - CComPtr pps; - hr = psl->QueryInterface(IID_PPV_ARGS(&pps)); - if (SUCCEEDED(hr)) { - PROPVARIANT propvar; - InitPropVariantFromString(L"New Project", &propvar); - pps->SetValue(PKEY_Title, propvar); - PropVariantClear(&propvar); - pps->Commit(); - } - - poc->AddObject(psl); - - CComPtr poa; - hr = poc->QueryInterface(IID_PPV_ARGS(&poa)); - if (SUCCEEDED(hr)) { - pcdl->AddUserTasks(poa); - } - - pcdl->CommitList(); - CoUninitialize(); - } -#endif - -#ifdef Q_OS_MACOS - static void initializeMacOSJumpList() { - } -#endif - void CorePlugin::initializeSingletons() { new CoreInterface(this); new BehaviorPreference(this); + new DspxClipboard(this); } void CorePlugin::initializeActions() { CoreInterface::actionRegistry()->addExtension(::getCoreActionExtension()); CoreInterface::actionRegistry()->addIconManifest(":/diffscope/coreplugin/icons/config.json"); - // TODO: move to icon manifest later - const auto addIcon = [&](const QString &id, const QString &iconName) { - QAK::ActionIcon icon; - icon.addUrl("image://fluent-system-icons/" + iconName); - CoreInterface::actionRegistry()->addIcon("", id, icon); - }; } void CorePlugin::initializeSettings() const { @@ -341,6 +275,7 @@ namespace Core::Internal { HomeWindowInterfaceRegistry::instance()->attach(); ProjectWindowInterfaceRegistry::instance()->attach(); ProjectWindowInterfaceRegistry::instance()->attach(); + ProjectWindowInterfaceRegistry::instance()->attach(); ProjectWindowInterfaceRegistry::instance()->attach(); ProjectWindowInterfaceRegistry::instance()->attach(); ProjectWindowInterfaceRegistry::instance()->attach(); @@ -350,6 +285,30 @@ namespace Core::Internal { HomeWindowInterfaceRegistry::instance()->attach(); ProjectWindowInterfaceRegistry::instance()->attach(); ProjectWindowInterfaceRegistry::instance()->attach(); + ProjectWindowInterfaceRegistry::instance()->attach(); + + auto windowSystem = CoreInterface::windowSystem(); + connect(windowSystem, &WindowSystem::windowAboutToDestroy, this, [](WindowInterface *windowInterface) { + if (!qobject_cast(windowInterface)) { + return; + } + + const auto startupBehavior = BehaviorPreference::startupBehavior(); + const bool closeHomeAfterOpening = startupBehavior & BehaviorPreference::SB_CloseHomeWindowAfterOpeningProject; + const bool reopenHomeOnLastProjectClosed = startupBehavior & BehaviorPreference::SB_OpenHomeWindowWhenLastProjectClosed; + + if (closeHomeAfterOpening && !reopenHomeOnLastProjectClosed) { + return; + } + + const bool hasOtherProjectWindow = std::ranges::any_of(CoreInterface::windowSystem()->windows(), [](auto w) { + return qobject_cast(w); + }); + + if (!hasOtherProjectWindow) { + CoreInterface::showHome(); + } + }); } void CorePlugin::initializeBehaviorPreference() { @@ -402,11 +361,7 @@ namespace Core::Internal { } void CorePlugin::initializeJumpList() { -#ifdef Q_OS_WIN - initializeWindowsJumpList(); -#elif defined(Q_OS_MACOS) - initializeMacOSJumpList(); -#endif + PlatformJumpListHelper::initializePlatformJumpList(); } void CorePlugin::initializeHelpContents() { @@ -419,4 +374,64 @@ namespace Core::Internal { } } + void CorePlugin::checkLastRun() { + auto settings = RuntimeInterface::settings(); + settings->setValue("lastInitializationAbortedFlag", false); + bool lastRunWarningSuppressed = QApplication::arguments().contains(kNoWarningLastRun); + if (settings->value("lastRunTerminatedAbnormally").toBool() && !lastRunWarningSuppressed) { + qCWarning(lcCorePlugin) << "Last run terminated abnormally"; + SVS::MessageBox::warning(RuntimeInterface::qmlEngine(), nullptr, tr("Last run terminated abnormally"), tr("%1 did not exit normally during its last run.\n\nTo check for unsaved files, please go to Recovery Files.").arg(QApplication::applicationDisplayName())); + } + settings->setValue("lastRunTerminatedAbnormally", true); + RuntimeInterface::addExitCallback([](int exitCode) { + if (exitCode == 0) { + RuntimeInterface::settings()->setValue("lastRunTerminatedAbnormally", false); + } + }); + } + + void CorePlugin::checkPlugins() { + auto args = QApplication::arguments(); + if (auto i = args.indexOf(kFatalWithoutPlugins); i != -1) { + QList pluginIdList; + for (auto j = i + 1; j < args.size(); ++j) { + if (args[j].startsWith('-')) + break; + pluginIdList.append(args[j]); + } + QList requiredPluginsWithError; + for (auto plugin : ExtensionSystem::PluginManager::plugins()) { + if (pluginIdList.contains(plugin->name())) { + pluginIdList.removeAll(plugin->name()); + if (plugin->hasError()) { + requiredPluginsWithError.append(plugin->name()); + } + } + } + if (!pluginIdList.isEmpty()) { + qFatal() << "Missing plugins specified by" << kFatalWithoutPlugins << ":" << pluginIdList; + } + if (!requiredPluginsWithError.isEmpty()) { + qFatal() << "Plugins specified by" << kFatalWithoutPlugins << "have errors:" << requiredPluginsWithError; + } + } + + QList errorPlugins; + std::ranges::copy( + ExtensionSystem::PluginManager::plugins() | std::views::filter([](auto *plugin) { + return plugin->hasError(); + }), + std::back_inserter(errorPlugins) + ); + bool isPluginErrorSuppressed = QApplication::arguments().contains(kNoWarningPluginError); + if (!errorPlugins.isEmpty() && !isPluginErrorSuppressed) { + static auto errorMessage = tr("Errors occurred while loading some plugins:\n\n%1\n\nPlease go to Plugins to see more details."); + QStringList pluginErrorMessages; + std::ranges::transform(errorPlugins, std::back_inserter(pluginErrorMessages), [](auto plugin) { + return tr("%1 (%2)").arg(plugin->displayName(), plugin->name()); + }); + SVS::MessageBox::critical(RuntimeInterface::qmlEngine(), nullptr, tr("Plugin Error"), errorMessage.arg(pluginErrorMessages.join("\n"))); + } + } + } diff --git a/src/plugins/coreplugin/internal/CorePlugin.h b/src/plugins/coreplugin/internal/CorePlugin.h index 4af66ca3..04e96d5e 100644 --- a/src/plugins/coreplugin/internal/CorePlugin.h +++ b/src/plugins/coreplugin/internal/CorePlugin.h @@ -25,11 +25,15 @@ namespace Core::Internal { void initializeSingletons(); static void initializeActions(); void initializeSettings() const; - static void initializeWindows(); + void initializeWindows(); static void initializeBehaviorPreference(); static void initializeColorScheme(); static void initializeJumpList(); void initializeHelpContents(); + + static void checkLastRun(); + static void checkPlugins(); + }; } diff --git a/src/plugins/coreplugin/internal/PlatformJumpListHelper.cpp b/src/plugins/coreplugin/internal/PlatformJumpListHelper.cpp new file mode 100644 index 00000000..a98558ce --- /dev/null +++ b/src/plugins/coreplugin/internal/PlatformJumpListHelper.cpp @@ -0,0 +1,79 @@ +#include "PlatformJumpListHelper.h" + +#include + +#ifdef Q_OS_WIN +# include +# include +# include +# include +# include +#elif defined(Q_OS_MACOS) +// TODO: Add macOS jump list (dock menu) implementation when available +#endif + +namespace Core::Internal { + + void PlatformJumpListHelper::initializePlatformJumpList() { +#ifdef Q_OS_WIN + CoInitialize(nullptr); + + CComPtr pcdl; + HRESULT hr = pcdl.CoCreateInstance(CLSID_DestinationList, nullptr, CLSCTX_INPROC_SERVER); + if (FAILED(hr)) { + CoUninitialize(); + return; + } + + UINT cMinSlots; + CComPtr poaRemoved; + hr = pcdl->BeginList(&cMinSlots, IID_PPV_ARGS(&poaRemoved)); + if (FAILED(hr)) { + CoUninitialize(); + return; + } + + CComPtr poc; + hr = poc.CoCreateInstance(CLSID_EnumerableObjectCollection, nullptr, CLSCTX_INPROC_SERVER); + if (FAILED(hr)) { + CoUninitialize(); + return; + } + + CComPtr psl; + hr = psl.CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER); + if (FAILED(hr)) { + CoUninitialize(); + return; + } + + auto appPath = QApplication::applicationFilePath().toStdWString(); + psl->SetPath(appPath.c_str()); + psl->SetArguments(L"--new"); + + CComPtr pps; + hr = psl->QueryInterface(IID_PPV_ARGS(&pps)); + if (SUCCEEDED(hr)) { + PROPVARIANT propvar; + InitPropVariantFromString(L"New Project", &propvar); + pps->SetValue(PKEY_Title, propvar); + PropVariantClear(&propvar); + pps->Commit(); + } + + poc->AddObject(psl); + + CComPtr poa; + hr = poc->QueryInterface(IID_PPV_ARGS(&poa)); + if (SUCCEEDED(hr)) { + pcdl->AddUserTasks(poa); + } + + pcdl->CommitList(); + CoUninitialize(); +#elif defined(Q_OS_MACOS) + // TODO: Add macOS jump list (dock menu) implementation when available +#endif + } + +} diff --git a/src/plugins/coreplugin/internal/PlatformJumpListHelper.h b/src/plugins/coreplugin/internal/PlatformJumpListHelper.h new file mode 100644 index 00000000..2d361a80 --- /dev/null +++ b/src/plugins/coreplugin/internal/PlatformJumpListHelper.h @@ -0,0 +1,13 @@ +#ifndef DIFFSCOPE_COREPLUGIN_PLATFORMJUMPLISTHELPER_H +#define DIFFSCOPE_COREPLUGIN_PLATFORMJUMPLISTHELPER_H + +namespace Core::Internal { + + class PlatformJumpListHelper { + public: + static void initializePlatformJumpList(); + }; + +} + +#endif // DIFFSCOPE_COREPLUGIN_PLATFORMJUMPLISTHELPER_H diff --git a/src/plugins/coreplugin/internal/addon/CloseSaveCheckAddOn.cpp b/src/plugins/coreplugin/internal/addon/CloseSaveCheckAddOn.cpp new file mode 100644 index 00000000..b82d8e32 --- /dev/null +++ b/src/plugins/coreplugin/internal/addon/CloseSaveCheckAddOn.cpp @@ -0,0 +1,77 @@ +#include "CloseSaveCheckAddOn.h" + +#include + +#include +#include +#include + +#include + +#include + +#include +#include +#include + +namespace Core::Internal { + + CloseSaveCheckAddOn::CloseSaveCheckAddOn(QObject *parent) : WindowInterfaceAddOn(parent) { + } + + CloseSaveCheckAddOn::~CloseSaveCheckAddOn() = default; + + void CloseSaveCheckAddOn::initialize() { + auto windowInterface = windowHandle()->cast(); + windowInterface->addCloseCallback([this] { + return handleCloseRequest(); + }); + } + + void CloseSaveCheckAddOn::extensionsInitialized() { + } + + bool CloseSaveCheckAddOn::delayedInitialize() { + return WindowInterfaceAddOn::delayedInitialize(); + } + + bool CloseSaveCheckAddOn::handleCloseRequest() const { + auto windowInterface = windowHandle()->cast(); + auto projectContext = windowInterface->projectDocumentContext(); + auto document = projectContext->document(); + auto transactionController = document->transactionController(); + auto fileLocker = projectContext->fileLocker(); + if (!fileLocker) { + return true; + } + + const bool isClean = transactionController->cleanStep() == transactionController->currentStep(); + const bool notModifiedExternally = !fileLocker->isFileModifiedSinceLastSave(); + const bool isSaved = isClean && notModifiedExternally; + + if (isSaved) { + return true; + } + + auto button = SVS::MessageBox::warning( + RuntimeInterface::qmlEngine(), + windowInterface->window(), + tr("Do you want to save before closing?"), + tr("If you choose not to save, a copy of the current project file will be created to help recover your work in case of accidental incorrect operation."), + SVS::SVSCraft::Yes | SVS::SVSCraft::No | SVS::SVSCraft::Cancel, + SVS::SVSCraft::Yes + ); + + if (button == SVS::SVSCraft::Yes) { + return windowInterface->save(); + } + if (button == SVS::SVSCraft::No) { + QDir runtimeDataDir(ApplicationInfo::applicationLocation(ApplicationInfo::RuntimeData)); + auto tempPath = runtimeDataDir.filePath(QStringLiteral("latest_unsaved_project.dspx")); + return projectContext->saveCopy(tempPath); + } + + return false; + } + +} diff --git a/src/plugins/coreplugin/internal/addon/CloseSaveCheckAddOn.h b/src/plugins/coreplugin/internal/addon/CloseSaveCheckAddOn.h new file mode 100644 index 00000000..40817074 --- /dev/null +++ b/src/plugins/coreplugin/internal/addon/CloseSaveCheckAddOn.h @@ -0,0 +1,24 @@ +#ifndef DIFFSCOPE_COREPLUGIN_CLOSESAVECHECKADDON_H +#define DIFFSCOPE_COREPLUGIN_CLOSESAVECHECKADDON_H + +#include + +namespace Core::Internal { + + class CloseSaveCheckAddOn : public WindowInterfaceAddOn { + Q_OBJECT + public: + explicit CloseSaveCheckAddOn(QObject *parent = nullptr); + ~CloseSaveCheckAddOn() override; + + void initialize() override; + void extensionsInitialized() override; + bool delayedInitialize() override; + + private: + bool handleCloseRequest() const; + }; + +} + +#endif //DIFFSCOPE_COREPLUGIN_CLOSESAVECHECKADDON_H diff --git a/src/plugins/coreplugin/internal/addon/InsertItemAddOn.cpp b/src/plugins/coreplugin/internal/addon/InsertItemAddOn.cpp new file mode 100644 index 00000000..b07dc669 --- /dev/null +++ b/src/plugins/coreplugin/internal/addon/InsertItemAddOn.cpp @@ -0,0 +1,38 @@ +#include "InsertItemAddOn.h" + +#include + +#include + +#include + +#include + +namespace Core::Internal { + + InsertItemAddOn::InsertItemAddOn(QObject *parent) : WindowInterfaceAddOn(parent) { + } + + InsertItemAddOn::~InsertItemAddOn() = default; + + void InsertItemAddOn::initialize() { + auto windowInterface = windowHandle()->cast(); + QQmlComponent component(RuntimeInterface::qmlEngine(), "DiffScope.Core", "InsertItemAddOnActions"); + if (component.isError()) { + qFatal() << component.errorString(); + } + auto o = component.createWithInitialProperties({ + {"addOn", QVariant::fromValue(this)}, + }); + o->setParent(this); + QMetaObject::invokeMethod(o, "registerToContext", windowInterface->actionContext()); + } + + void InsertItemAddOn::extensionsInitialized() { + } + + bool InsertItemAddOn::delayedInitialize() { + return WindowInterfaceAddOn::delayedInitialize(); + } + +} diff --git a/src/plugins/coreplugin/internal/addon/InsertItemAddOn.h b/src/plugins/coreplugin/internal/addon/InsertItemAddOn.h new file mode 100644 index 00000000..e87aff51 --- /dev/null +++ b/src/plugins/coreplugin/internal/addon/InsertItemAddOn.h @@ -0,0 +1,25 @@ +#ifndef DIFFSCOPE_COREPLUGIN_INSERTITEMADDON_H +#define DIFFSCOPE_COREPLUGIN_INSERTITEMADDON_H + +#include + +#include + +namespace Core::Internal { + + class InsertItemAddOn : public WindowInterfaceAddOn { + Q_OBJECT + QML_ELEMENT + QML_UNCREATABLE("") + public: + explicit InsertItemAddOn(QObject *parent = nullptr); + ~InsertItemAddOn() override; + + void initialize() override; + void extensionsInitialized() override; + bool delayedInitialize() override; + }; + +} + +#endif //DIFFSCOPE_COREPLUGIN_INSERTITEMADDON_H \ No newline at end of file diff --git a/src/plugins/coreplugin/internal/addon/MetadataAddOn.cpp b/src/plugins/coreplugin/internal/addon/MetadataAddOn.cpp index fc5ec98d..04d9a243 100644 --- a/src/plugins/coreplugin/internal/addon/MetadataAddOn.cpp +++ b/src/plugins/coreplugin/internal/addon/MetadataAddOn.cpp @@ -2,6 +2,7 @@ #include #include +#include #include @@ -20,16 +21,31 @@ namespace Core::Internal { auto windowInterface = windowHandle()->cast(); // Create MetadataPanel component and add it to action context - QQmlComponent component(RuntimeInterface::qmlEngine(), "DiffScope.Core", "MetadataPanel", this); - if (component.isError()) { - qFatal() << component.errorString(); + { + QQmlComponent component(RuntimeInterface::qmlEngine(), "DiffScope.Core", "MetadataPanel", this); + if (component.isError()) { + qFatal() << component.errorString(); + } + auto o = component.createWithInitialProperties({ + {"addOn", QVariant::fromValue(this)}, + }, + RuntimeInterface::qmlEngine()->rootContext()); + o->setParent(this); + windowInterface->actionContext()->addAction("org.diffscope.core.panel.metadata", o->property("metadataPanelComponent").value()); + } + + // Register metadata actions defined in QML + { + QQmlComponent component(RuntimeInterface::qmlEngine(), "DiffScope.Core", "MetadataAddOnActions"); + if (component.isError()) { + qFatal() << component.errorString(); + } + auto o = component.createWithInitialProperties({ + {"addOn", QVariant::fromValue(this)}, + }); + o->setParent(this); + QMetaObject::invokeMethod(o, "registerToContext", windowInterface->actionContext()); } - auto o = component.createWithInitialProperties({ - {"addOn", QVariant::fromValue(this)}, - }, - RuntimeInterface::qmlEngine()->rootContext()); - o->setParent(this); - windowInterface->actionContext()->addAction("org.diffscope.core.panel.metadata", o->property("metadataPanelComponent").value()); } void MetadataAddOn::extensionsInitialized() { diff --git a/src/plugins/coreplugin/internal/addon/TimelineAddOn.cpp b/src/plugins/coreplugin/internal/addon/TimelineAddOn.cpp index dbf6e2d8..ea3ffc06 100644 --- a/src/plugins/coreplugin/internal/addon/TimelineAddOn.cpp +++ b/src/plugins/coreplugin/internal/addon/TimelineAddOn.cpp @@ -4,6 +4,7 @@ #include #include +#include #include @@ -13,11 +14,22 @@ #include #include #include + +#include +#include + +#include + #include #include #include +#include +#include namespace Core::Internal { + + Q_STATIC_LOGGING_CATEGORY(lcTimelineAddOn, "diffscope.core.timelineaddon") + TimelineAddOn::TimelineAddOn(QObject *parent) : WindowInterfaceAddOn(parent) { } TimelineAddOn::~TimelineAddOn() = default; @@ -44,6 +56,7 @@ namespace Core::Internal { Q_EMIT tempoTextChanged(); Q_EMIT timeSignatureTextChanged(); }); + connect(windowInterface->projectDocumentContext()->document()->model()->timeline(), &dspx::Timeline::loopEnabledChanged, this, &TimelineAddOn::loopEnabledChanged); } void TimelineAddOn::extensionsInitialized() { } @@ -86,6 +99,24 @@ namespace Core::Internal { return QApplication::doubleClickInterval(); } + bool TimelineAddOn::isLoopEnabled() const { + auto windowInterface = windowHandle()->cast(); + return windowInterface->projectDocumentContext()->document()->model()->timeline()->isLoopEnabled(); + } + + void TimelineAddOn::setLoopEnabled(bool enabled) { + auto windowInterface = windowHandle()->cast(); + qCInfo(lcTimelineAddOn) << "Toggle loop:" << enabled; + windowInterface->projectDocumentContext()->document()->transactionController()->beginScopedTransaction(enabled ? tr("Enabling loop") : tr("Disabling loop"), [=] { + auto dspxTimeline = windowInterface->projectDocumentContext()->document()->model()->timeline(); + dspxTimeline->setLoopEnabled(enabled); + return true; + }, [=, this] { + qCCritical(lcTimelineAddOn()) << "Failed to edit loop in exclusive transaction"; + Q_EMIT loopEnabledChanged(); + }); + } + static inline QString absoluteMusicTimePromptText(const SVS::MusicTime &t) { if (t.tick() == 0) { return Core::Internal::TimelineAddOn::tr("measure %L1, beat %L2").arg(t.measure() + 1).arg(t.beat() + 1); diff --git a/src/plugins/coreplugin/internal/addon/TimelineAddOn.h b/src/plugins/coreplugin/internal/addon/TimelineAddOn.h index 60c47375..f747ddf3 100644 --- a/src/plugins/coreplugin/internal/addon/TimelineAddOn.h +++ b/src/plugins/coreplugin/internal/addon/TimelineAddOn.h @@ -19,6 +19,7 @@ namespace Core::Internal { Q_PROPERTY(bool showMusicTime READ showMusicTime WRITE setShowMusicTime NOTIFY showMusicTimeChanged) Q_PROPERTY(bool showAbsoluteTime READ showAbsoluteTime WRITE setShowAbsoluteTime NOTIFY showMusicTimeChanged) Q_PROPERTY(int doubleClickInterval READ doubleClickInterval) + Q_PROPERTY(bool loopEnabled READ isLoopEnabled WRITE setLoopEnabled NOTIFY loopEnabledChanged) public: explicit TimelineAddOn(QObject *parent = nullptr); ~TimelineAddOn() override; @@ -44,6 +45,9 @@ namespace Core::Internal { static int doubleClickInterval(); + bool isLoopEnabled() const; + void setLoopEnabled(bool enabled); + Q_INVOKABLE void execQuickJump(const QString &initialText = {}) const; Q_INVOKABLE void goToStart() const; @@ -63,6 +67,7 @@ namespace Core::Internal { void tempoTextChanged(); void timeSignatureTextChanged(); void showMusicTimeChanged(); + void loopEnabledChanged(); private: bool m_showMusicTime{true}; diff --git a/src/plugins/coreplugin/internal/addon/ViewVisibilityAddOn.cpp b/src/plugins/coreplugin/internal/addon/ViewVisibilityAddOn.cpp index df217462..3dcc2ee1 100644 --- a/src/plugins/coreplugin/internal/addon/ViewVisibilityAddOn.cpp +++ b/src/plugins/coreplugin/internal/addon/ViewVisibilityAddOn.cpp @@ -53,21 +53,25 @@ namespace Core::Internal { qCDebug(lcViewVisibilityAddOn) << "Left side bar" << leftSideBarVisible; auto leftDockingView = window->property("leftDockingView").value(); leftDockingView->setProperty("barSize", leftSideBarVisible ? 32 : 0); + leftDockingView->setProperty("barVisible", leftSideBarVisible); auto rightSideBarVisible = !settings->value(QString::number(RightSideBar)).value(); qCDebug(lcViewVisibilityAddOn) << "Right side bar" << rightSideBarVisible; auto rightDockingView = window->property("rightDockingView").value(); rightDockingView->setProperty("barSize", rightSideBarVisible ? 32 : 0); + rightDockingView->setProperty("barVisible", rightSideBarVisible); auto topSideBarVisible = !settings->value(QString::number(TopSideBar)).value(); qCDebug(lcViewVisibilityAddOn) << "Top side bar" << topSideBarVisible; auto topDockingView = window->property("topDockingView").value(); topDockingView->setProperty("barSize", topSideBarVisible ? 32 : 0); + topDockingView->setProperty("barVisible", topSideBarVisible); auto bottomSideBarVisible = !settings->value(QString::number(BottomSideBar)).value(); qCDebug(lcViewVisibilityAddOn) << "Bottom side bar" << bottomSideBarVisible; auto bottomDockingView = window->property("bottomDockingView").value(); bottomDockingView->setProperty("barSize", bottomSideBarVisible ? 32 : 0); + bottomDockingView->setProperty("barVisible", bottomSideBarVisible); auto statusBarVisible = !settings->value(QString::number(StatusBar)).value(); qCDebug(lcViewVisibilityAddOn) << "Status bar" << statusBarVisible; @@ -97,18 +101,22 @@ namespace Core::Internal { } else if (option == LeftSideBar) { auto leftDockingView = window->property("leftDockingView").value(); qCInfo(lcViewVisibilityAddOn) << "Left side bar" << visible; + leftDockingView->setProperty("barVisible", visible); leftDockingView->setProperty("barSize", visible ? 32 : 0); } else if (option == RightSideBar) { auto rightDockingView = window->property("rightDockingView").value(); qCInfo(lcViewVisibilityAddOn) << "Right side bar" << visible; + rightDockingView->setProperty("barVisible", visible); rightDockingView->setProperty("barSize", visible ? 32 : 0); } else if (option == TopSideBar) { auto topDockingView = window->property("topDockingView").value(); qCInfo(lcViewVisibilityAddOn) << "Top side bar" << visible; + topDockingView->setProperty("barVisible", visible); topDockingView->setProperty("barSize", visible ? 32 : 0); } else if (option == BottomSideBar) { auto bottomDockingView = window->property("bottomDockingView").value(); qCInfo(lcViewVisibilityAddOn) << "Bottom side bar" << visible; + bottomDockingView->setProperty("barVisible", visible); bottomDockingView->setProperty("barSize", visible ? 32 : 0); } else if (option == StatusBar) { auto statusBar = window->property("statusBar").value(); diff --git a/src/plugins/coreplugin/internal/settings/GeneralPage.cpp b/src/plugins/coreplugin/internal/settings/GeneralPage.cpp index 8f5e14d9..4997b984 100644 --- a/src/plugins/coreplugin/internal/settings/GeneralPage.cpp +++ b/src/plugins/coreplugin/internal/settings/GeneralPage.cpp @@ -121,7 +121,7 @@ namespace Core::Internal { title.arg(QApplication::applicationDisplayName()), text.arg(QApplication::applicationDisplayName()) ) == SVS::SVSCraft::Yes) { - CoreInterface::restartApplication(); + RuntimeInterface::restartApplication(); } } return ISettingPage::accept(); diff --git a/src/plugins/coreplugin/plugin.json.in b/src/plugins/coreplugin/plugin.json.in index 644f551f..85ef86bf 100644 --- a/src/plugins/coreplugin/plugin.json.in +++ b/src/plugins/coreplugin/plugin.json.in @@ -12,12 +12,25 @@ ], "Arguments" : [ { - "Name" : "--open-settings", - "Description" : "Open settings on startup" + "Name": "--open-settings", + "Description": "Open settings on startup" }, { - "Name" : "--new", - "Description" : "Create a new project on startup, ignoring default startup behavior" + "Name": "--new", + "Description": "Create a new project on startup, ignoring default startup behavior" + }, + { + "Name": "--no-warning-last-run", + "Description": "Suppress warning about 'Last run terminated abnormally'" + }, + { + "Name": "--fatal-without-plugins", + "Parameter": "plugin_list", + "Description": "Fatal if any of the specified plugins are not loaded" + }, + { + "Name": "--no-warning-plugin-error", + "Description": "Suppress warning about plugin errors" } ], "Category": "Core", diff --git a/src/plugins/coreplugin/project/DspxDocument.cpp b/src/plugins/coreplugin/project/DspxDocument.cpp deleted file mode 100644 index c9cc2044..00000000 --- a/src/plugins/coreplugin/project/DspxDocument.cpp +++ /dev/null @@ -1,71 +0,0 @@ -#include "DspxDocument.h" -#include "DspxDocument_p.h" - -#include - -#include -#include -#include - -#include -#include - -namespace Core { - - class TransactionalModelStrategy : public TransactionalStrategy { - public: - explicit TransactionalModelStrategy(dspx::UndoableModelStrategy *undoableModelStrategy, QObject *parent = nullptr) - : TransactionalStrategy(parent), m_strategy(undoableModelStrategy) { - } - - void beginTransaction() override { - m_strategy->undoStack()->beginMacro(""); - } - void abortTransaction() override { - m_strategy->undoStack()->endMacro(); - m_strategy->undoStack()->undo(); - } - void commitTransaction() override { - m_strategy->undoStack()->endMacro(); - } - void moveCurrentStepBy(int count) override { - m_strategy->undoStack()->setIndex(m_strategy->undoStack()->index() + count); - } - - private: - dspx::UndoableModelStrategy *m_strategy; - - }; - - DspxDocument::DspxDocument(QObject *parent) : QObject(parent), d_ptr(new DspxDocumentPrivate) { - Q_D(DspxDocument); - d->q_ptr = this; - auto modelStrategy = new dspx::UndoableModelStrategy; // TODO use substate in future - d->model = new dspx::Model(modelStrategy, this); - modelStrategy->setParent(d->model); - d->selectionModel = new dspx::SelectionModel(d->model, this); - auto transactionalStrategy = new TransactionalModelStrategy(modelStrategy); - d->transactionController = new TransactionController(transactionalStrategy, this); - transactionalStrategy->setParent(d->transactionController); - } - - DspxDocument::~DspxDocument() = default; - - dspx::Model *DspxDocument::model() const { - Q_D(const DspxDocument); - return d->model; - } - - dspx::SelectionModel *DspxDocument::selectionModel() const { - Q_D(const DspxDocument); - return d->selectionModel; - } - - TransactionController *DspxDocument::transactionController() const { - Q_D(const DspxDocument); - return d->transactionController; - } - -} - -#include "moc_DspxDocument.cpp" diff --git a/src/plugins/coreplugin/project/DspxDocument_p.h b/src/plugins/coreplugin/project/DspxDocument_p.h deleted file mode 100644 index b92d6bc7..00000000 --- a/src/plugins/coreplugin/project/DspxDocument_p.h +++ /dev/null @@ -1,17 +0,0 @@ -#ifndef DIFFSCOPE_COREPLUGIN_DSPXDOCUMENT_P_H -#define DIFFSCOPE_COREPLUGIN_DSPXDOCUMENT_P_H - -#include - -namespace Core { - class DspxDocumentPrivate { - Q_DECLARE_PUBLIC(DspxDocument) - public: - DspxDocument *q_ptr; - dspx::Model *model; - dspx::SelectionModel *selectionModel; - TransactionController *transactionController; - }; -} - -#endif //DIFFSCOPE_COREPLUGIN_DSPXDOCUMENT_P_H diff --git a/src/plugins/coreplugin/project/clipboard/DspxClipboard.cpp b/src/plugins/coreplugin/project/clipboard/DspxClipboard.cpp new file mode 100644 index 00000000..e5293c1d --- /dev/null +++ b/src/plugins/coreplugin/project/clipboard/DspxClipboard.cpp @@ -0,0 +1,137 @@ +#include "DspxClipboard.h" +#include "DspxClipboard_p.h" + +#include +#include +#include + +namespace Core { + + void DspxClipboardPrivate::handleClipboardChanged() { + Q_Q(DspxClipboard); + if (mode == DspxClipboard::Global) + Q_EMIT q->changed(); + } + + static QList supportedTypes() { + static const QList types = { + DspxClipboardData::Tempo, + DspxClipboardData::Label, + DspxClipboardData::Track, + DspxClipboardData::Clip, + DspxClipboardData::Note + }; + return types; + } + + static DspxClipboard *m_instance = nullptr; + + DspxClipboard::DspxClipboard(QObject *parent) : QObject(parent), d_ptr(new DspxClipboardPrivate) { + Q_D(DspxClipboard); + Q_ASSERT(!m_instance); + m_instance = this; + d->q_ptr = this; + auto *clipboard = QGuiApplication::clipboard(); + connect(clipboard, &QClipboard::dataChanged, this, [this] { + Q_D(DspxClipboard); + d->handleClipboardChanged(); + }); + } + + DspxClipboard::~DspxClipboard() { + m_instance = nullptr; + } + + DspxClipboard *DspxClipboard::instance() { + return m_instance; + } + + DspxClipboard::Mode DspxClipboard::mode() const { + Q_D(const DspxClipboard); + return d->mode; + } + + void DspxClipboard::setMode(Mode mode) { + Q_D(DspxClipboard); + if (d->mode == mode) + return; + d->mode = mode; + Q_EMIT changed(); + } + + void DspxClipboard::copy(const QList &data, QMimeData *additionalMimeData) { + Q_D(DspxClipboard); + if (d->mode == Internal) { + d->internalData.clear(); + for (const auto &item : data) { + auto type = item.type(); + if (d->internalData.contains(type)) + continue; + d->internalData.insert(type, item); + } + Q_EMIT changed(); + return; + } + + auto *mimeData = additionalMimeData ? additionalMimeData : new QMimeData; + + for (const auto &item : data) { + const auto type = item.type(); + const auto mimeType = DspxClipboardData::mimeType(type); + if (mimeType.isEmpty() || mimeData->hasFormat(mimeType)) + continue; + mimeData->setData(mimeType, item.toData()); + } + + QGuiApplication::clipboard()->setMimeData(mimeData); + } + + DspxClipboardData DspxClipboard::paste(DspxClipboardData::Type expectedType, bool *ok) { + if (ok) + *ok = false; + + Q_D(DspxClipboard); + + if (d->mode == Internal) { + const auto it = d->internalData.constFind(expectedType); + if (it != d->internalData.cend() && it->has_value()) { + if (ok) + *ok = true; + return it->value(); + } + return {}; + } + + const auto mimeType = DspxClipboardData::mimeType(expectedType); + const QMimeData *mimeData = QGuiApplication::clipboard()->mimeData(); + if (!mimeData || mimeType.isEmpty() || !mimeData->hasFormat(mimeType)) + return {}; + + return DspxClipboardData::fromData(mimeData->data(mimeType), expectedType, ok); + } + + QList DspxClipboard::availablePasteTypes() const { + QList types; + Q_D(const DspxClipboard); + + if (d->mode == Internal) { + for (auto it = d->internalData.cbegin(); it != d->internalData.cend(); ++it) { + if (it.value().has_value()) + types.append(it.key()); + } + return types; + } + + const QMimeData *mimeData = QGuiApplication::clipboard()->mimeData(); + if (!mimeData) + return types; + + for (const auto type : supportedTypes()) { + const auto mimeType = DspxClipboardData::mimeType(type); + if (!mimeType.isEmpty() && mimeData->hasFormat(mimeType)) + types.append(type); + } + return types; + } + +} diff --git a/src/plugins/coreplugin/project/clipboard/DspxClipboard.h b/src/plugins/coreplugin/project/clipboard/DspxClipboard.h new file mode 100644 index 00000000..8b5af649 --- /dev/null +++ b/src/plugins/coreplugin/project/clipboard/DspxClipboard.h @@ -0,0 +1,47 @@ +#ifndef DIFFSCOPE_COREPLUGIN_DSPXCLIPBOARD_H +#define DIFFSCOPE_COREPLUGIN_DSPXCLIPBOARD_H + +#include + +#include + +class QMimeData; + +namespace Core { + + class DspxClipboardPrivate; + + class CORE_EXPORT DspxClipboard : public QObject { + Q_OBJECT + Q_PROPERTY(Mode mode READ mode WRITE setMode NOTIFY changed) + Q_DECLARE_PRIVATE(DspxClipboard) + + public: + enum Mode { + Global, + Internal + }; + Q_ENUM(Mode) + + explicit DspxClipboard(QObject *parent = nullptr); + ~DspxClipboard() override; + + static DspxClipboard *instance(); + + Mode mode() const; + void setMode(Mode mode); + + void copy(const QList &data, QMimeData *additionalMimeData); + DspxClipboardData paste(DspxClipboardData::Type expectedType, bool *ok = nullptr); + QList availablePasteTypes() const; + + Q_SIGNALS: + void changed(); + + private: + QScopedPointer d_ptr; + }; + +} + +#endif //DIFFSCOPE_COREPLUGIN_DSPXCLIPBOARD_H diff --git a/src/plugins/coreplugin/project/clipboard/DspxClipboardData.cpp b/src/plugins/coreplugin/project/clipboard/DspxClipboardData.cpp new file mode 100644 index 00000000..99b0302a --- /dev/null +++ b/src/plugins/coreplugin/project/clipboard/DspxClipboardData.cpp @@ -0,0 +1,150 @@ +#include "DspxClipboardData.h" + +#include +#include +#include + +#include +#include + +namespace Core { + + QString DspxClipboardData::mimeType(Type type) { + switch (type) { + case Tempo: + return "application/x.diffscope.clipboard.tempo+json"; + case Label: + return "application/x.diffscope.clipboard.label+json"; + case Track: + return "application/x.diffscope.clipboard.track+json"; + case Clip: + return "application/x.diffscope.clipboard.clip+json"; + case Note: + return "application/x.diffscope.clipboard.note+json"; + default: + return {}; + } + } + + DspxClipboardData::Type DspxClipboardData::typeFromMimeType(const QString &mimeType, bool *ok) { + if (ok) + *ok = true; + if (mimeType == "application/x.diffscope.clipboard.tempo+json") { + return Tempo; + } + if (mimeType == "application/x.diffscope.clipboard.label+json") { + return Label; + } + if (mimeType == "application/x.diffscope.clipboard.track+json") { + return Track; + } + if (mimeType == "application/x.diffscope.clipboard.clip+json") { + return Clip; + } + if (mimeType == "application/x.diffscope.clipboard.note+json") { + return Note; + } + if (ok) + *ok = false; + return {}; + } + + QByteArray DspxClipboardData::toData() const { + QJsonObject json; + json.insert("version", QDspx::Serializer::versionToText(QDspx::Model::V1)); + json.insert("playhead", m_playhead); + json.insert("absolute", m_absolute); + json.insert("track", m_track); + QJsonArray dataArray; + QDspx::SerializationErrorList errors; + auto toJsonArray = [&] { + for (const auto &item : std::get(m_data)) { + dataArray.append(QDspx::JsonConverterV1::toJson(item, errors, {})); + } + }; + switch (type()) { + case Tempo: + toJsonArray.operator()(); + break; + case Label: + toJsonArray.operator() + + + + Preferences + Preferences + + + + + + + Window + Window + + + + Application + Application + + + + + + + + Home + Home + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Edit + Edit + + + + + Time Indicator + Time Indicator + + + + + + + + + + + + + + + Timeline + Timeline + + + + + Core + Core + + + + + + + + + + Panel + Panel + + + + + Widget + Widget + + + + + + + + + + + + + + + + + + + + + + + View + View + + + + + + + + + + + + + + + + + + Workspace + Workspace + + + + + + + + Help + Help + + + Application::ActionDescription - - Create a project in a new window - Create a project in a new window + + Create a project in a new window + Create a project in a new window - - Open an existing project in a new window - Open an existing project in a new window + + Create a project from template in a new window + - - Open application settings - Open application settings + + Open an existing project in a new window + Open an existing project in a new window - - Open plugin configuration dialog - Open plugin configuration dialog + + Open application settings + Open application settings - - Switch to home window if it is opened, or else open home window - Switch to home window if it is opened, or else open home window + + Open plugin configuration dialog + Open plugin configuration dialog - - Quit DiffScope - Quit DiffScope + + Switch to home window if it is opened, or else open home window + Switch to home window if it is opened, or else open home window - - Toggle "Recent Files" page - Toggle "Recent Files" page + + Quit DiffScope + Quit DiffScope - - Toggle "Recovery Files" page - Toggle "Recovery Files" page + + Toggle "Recent Files" page + Toggle "Recent Files" page - - Toggle grid view in "Recent Files" page - Toggle grid view in "Recent Files" page + + Toggle "Recovery Files" page + Toggle "Recovery Files" page - - Toggle list view in "Recent Files" page - Toggle list view in "Recent Files" page + + Toggle grid view in "Recent Files" page + Toggle grid view in "Recent Files" page - - Show music time (measure:beat:tick) on the time indicator - Show music time (measure:beat:tick) on the time indicator + + Toggle list view in "Recent Files" page + Toggle list view in "Recent Files" page - - Show absolute time (minute:second.millisecond) on the time indicator - Show absolute time (minute:second.millisecond) on the time indicator + + Show music time (measure:beat:tick) on the time indicator + Show music time (measure:beat:tick) on the time indicator - - Navigate to a specific position - Navigate to a specific position + + Show absolute time (minute:second.millisecond) on the time indicator + Show absolute time (minute:second.millisecond) on the time indicator - - Quickly navigate to a specific position - Quickly navigate to a specific position + + Navigate to a specific position + Navigate to a specific position - - Navigate to the start of current project - Navigate to the start of current project + + Quickly navigate to a specific position + Quickly navigate to a specific position - - Navigate to the end of current project - Navigate to the end of current project + + Navigate to the start of current project + Navigate to the start of current project - - Reset project end time based on project content - Reset project end time based on project content + + Navigate to the end of current project + Navigate to the end of current project - - Apply the default workspace layout - Apply the default workspace layout + + Reset project end time based on project content + Reset project end time based on project content - - Show or hide the main menu bar - Show or hide the main menu bar + + Apply the default workspace layout + Apply the default workspace layout - - Show or hide the main tool bar - Show or hide the main tool bar + + Show or hide the main menu bar + Show or hide the main menu bar - - Show or hide the left side bar - Show or hide the left side bar + + Show or hide the main tool bar + Show or hide the main tool bar - - Show or hide the right side bar - Show or hide the right side bar + + Show or hide the left side bar + Show or hide the left side bar - - Show or hide the top side bar - Show or hide the top side bar + + Show or hide the right side bar + Show or hide the right side bar - - Show or hide the bottom side bar - Show or hide the bottom side bar + + Show or hide the top side bar + Show or hide the top side bar - - Show or hide the status bar - Show or hide the status bar + + Show or hide the bottom side bar + Show or hide the bottom side bar - - Switch to the next project window - Switch to the next project window + + Show or hide the status bar + Show or hide the status bar - - Switch to the previous project window - Switch to the previous project window + + Switch to the next project window + Switch to the next project window - - Show help contents - Show help contents + + Switch to the previous project window + Switch to the previous project window - - Find and trigger action by name - Find and trigger action by name + + Show help contents + Show help contents - - Show information about DiffScope - Show information about DiffScope + + Find and trigger action by name + Find and trigger action by name - - Show information about Qt - Show information about Qt + + Show information about DiffScope + Show information about DiffScope - - + + + Show information about Qt + Show information about Qt + + + Application::ActionText - - &New - &New + + &New + &New + + + + New from Template... + + + + + &Open... + &Open... + + + + Open &Recent File + + + + + &Save + &Save + + + + Save &As... + + + + + Save Copy As... + + + + + Se&ttings... + Se&ttings... + + + + Pl&ugins... + Pl&ugins... + + + + Show &Home Window + Show &Home Window + + + + E&xit + E&xit + + + + Recent Files + Recent Files - - &Open... - &Open... + + Recovery Files + Recovery Files - - &Save - &Save + + Grid View + Grid View - - Se&ttings... - Se&ttings... + + List View + List View - - Pl&ugins... - Pl&ugins... + + &Undo + &Undo - - Show &Home Window - Show &Home Window + + &Redo + &Redo - - E&xit - E&xit + + Cu&t + Cu&t - - Recent Files - Recent Files + + &Copy + &Copy - - Recovery Files - Recovery Files + + &Paste + &Paste - - Grid View - Grid View + Paste Special... + Paste Special... - - List View - List View + + &Delete + &Delete - - &Undo - &Undo + + Select &All + Select &All - - &Redo - &Redo + + Dese&lect + Dese&lect - - Cu&t - Cu&t + + Select Current + Select Current - - &Copy - &Copy + + Select &Up + Select &Up - - &Paste - &Paste + + Select &Down + Select &Down - - Paste Special... - Paste Special... + + Select &Left + Select &Left - - &Delete - &Delete + + Select &Right + Select &Right - - Select &All - Select &All + + Move &Up + Move &Up - - Dese&lect - Dese&lect + + Move &Down + Move &Down - - Select Current - Select Current + + Move &Left + Move &Left - - Select &Up - Select &Up + + Move &Right + Move &Right - - Select &Down - Select &Down + + &Move... + &Move... - - Select &Left - Select &Left + + Scroll &Up + Scroll &Up - - Select &Right - Select &Right + + Scroll &Down + Scroll &Down - - Move &Up - Move &Up + + Scroll &Left + Scroll &Left - - Move &Down - Move &Down + + Scroll &Right + Scroll &Right - - Move &Left - Move &Left + + Move Cursor &Up + Move Cursor &Up - - Move &Right - Move &Right + + Move Cursor &Down + Move Cursor &Down - - &Move... - &Move... + + Move Cursor &Left + Move Cursor &Left - - Scroll &Up - Scroll &Up + + Move Cursor &Right + Move Cursor &Right - - Scroll &Down - Scroll &Down + + Extend Selection &Up + Extend Selection &Up - - Scroll &Left - Scroll &Left + + Extend Selection &Down + Extend Selection &Down - - Scroll &Right - Scroll &Right + + Extend Selection &Left + Extend Selection &Left - - Move Cursor &Up - Move Cursor &Up + + Extend Selection &Right + Extend Selection &Right - - Move Cursor &Down - Move Cursor &Down + + Shrink Selection &Up + Shrink Selection &Up - - Move Cursor &Left - Move Cursor &Left + + Shrink Selection &Down + Shrink Selection &Down - - Move Cursor &Right - Move Cursor &Right + + Shrink Selection &Left + Shrink Selection &Left - - Extend Selection &Up - Extend Selection &Up + + Shrink Selection &Right + Shrink Selection &Right - - Extend Selection &Down - Extend Selection &Down + + Page &Up + Page &Up - - Extend Selection &Left - Extend Selection &Left + + Page &Down + Page &Down - - Extend Selection &Right - Extend Selection &Right + + Page &Left + Page &Left - - Shrink Selection &Up - Shrink Selection &Up + + Page &Right + Page &Right - - Shrink Selection &Down - Shrink Selection &Down + + Scroll to &Top + Scroll to &Top - - Shrink Selection &Left - Shrink Selection &Left + + Scroll to &Bottom + Scroll to &Bottom - - Shrink Selection &Right - Shrink Selection &Right + + Scroll to &Start + Scroll to &Start - - Page &Up - Page &Up + + Scroll to &End + Scroll to &End - - Page &Down - Page &Down + + Scroll to Current Time + Scroll to Current Time - - Page &Left - Page &Left + + Show &Music Time + Show &Music Time - - Page &Right - Page &Right + + Show &Absolute Time + Show &Absolute Time - - Scroll to &Top - Scroll to &Top + + &Go To... + &Go To... - - Scroll to &Bottom - Scroll to &Bottom + + &Quick Jump... + &Quick Jump... - - Scroll to &Start - Scroll to &Start + + Go to &Start + Go to &Start - - Scroll to &End - Scroll to &End + + Go to Previous Measure + Go to Previous Measure - - Scroll to Current Time - Scroll to Current Time + + Go to Previous Beat + Go to Previous Beat - - Show &Music Time - Show &Music Time + + Go to Previous Tick + Go to Previous Tick - - Show &Absolute Time - Show &Absolute Time + + Go to &End + Go to &End - - &Go To... - &Go To... + + Go to Next Measure + Go to Next Measure - - &Quick Jump... - &Quick Jump... + + Go to Next Beat + Go to Next Beat - - Go to &Start - Go to &Start + + Go to Next Tick + Go to Next Tick - - Go to Previous Measure - Go to Previous Measure + + Go Inside View Range + Go Inside View Range - - Go to Previous Beat - Go to Previous Beat + + Workspace Layout... + Workspace Layout... - - Go to Previous Tick - Go to Previous Tick + + Always Show &Menu Bar + - - Go to &End - Go to &End + + Home Menu + - - Go to Next Measure - Go to Next Measure + + Window + Window - - Go to Next Beat - Go to Next Beat + + &Reset Project Time Range + &Reset Project Time Range - - Go to Next Tick - Go to Next Tick + + Default &Layout + Default &Layout - - Go Inside View Range - Go Inside View Range + + &Custom Layouts + &Custom Layouts - - Workspace Layout... - Workspace Layout... + + &Save Current Layout As... + &Save Current Layout As... - - &Reset Project Time Range - &Reset Project Time Range + + &All Panels + &All Panels - - Default &Layout - Default &Layout + + None (Left Top) + None (Left Top) - - &Custom Layouts - &Custom Layouts + + None (Left Bottom) + None (Left Bottom) - - &Save Current Layout As... - &Save Current Layout As... + + None (Right Top) + None (Right Top) - - &All Panels - &All Panels + + None (Right Bottom) + None (Right Bottom) - - None (Left Top) - None (Left Top) + + None (Top Left) + None (Top Left) - - None (Left Bottom) - None (Left Bottom) + + None (Top Right) + None (Top Right) - - None (Right Top) - None (Right Top) + + None (Bottom Left) + None (Bottom Left) - - None (Right Bottom) - None (Right Bottom) + + None (Bottom Right) + None (Bottom Right) - - None (Top Left) - None (Top Left) + + &Floating Panels + &Floating Panels - - None (Top Right) - None (Top Right) + + Add &New Panel or Action + Add &New Panel or Action - - None (Bottom Left) - None (Bottom Left) + Show &Menu Bar + Show &Menu Bar - - None (Bottom Right) - None (Bottom Right) + + Show Too&l Bar + Show Too&l Bar - - &Floating Panels - &Floating Panels + + Show &Left Side Bar + Show &Left Side Bar - - Add &New Panel or Action - Add &New Panel or Action + + Show &Right Side Bar + Show &Right Side Bar - - Show &Menu Bar - Show &Menu Bar + + Show &Top Side Bar + Show &Top Side Bar - - Show Too&l Bar - Show Too&l Bar + + Show &Bottom Side Bar + Show &Bottom Side Bar - - Show &Left Side Bar - Show &Left Side Bar + + Show &Status Bar + Show &Status Bar - - Show &Right Side Bar - Show &Right Side Bar + + &Next Project Window + &Next Project Window - - Show &Top Side Bar - Show &Top Side Bar + + &Previous Project Window + &Previous Project Window - - Show &Bottom Side Bar - Show &Bottom Side Bar + + Pro&ject Windows + Pro&ject Windows - - Show &Status Bar - Show &Status Bar + + &Help + &Help - - &Next Project Window - &Next Project Window + + &Find Actions... + &Find Actions... - - &Previous Project Window - &Previous Project Window + + &About DiffScope + &About DiffScope - - Pro&ject Windows - Pro&ject Windows + + About &Qt + About &Qt - - &Help - &Help + + Run DSPX Inspector + - - &Find Actions... - &Find Actions... + + Main Menu + Main Menu - - &About DiffScope - &About DiffScope + + Main Tool Bar Left + Main Tool Bar Left - - About &Qt - About &Qt + + Main Tool Bar Middle + Main Tool Bar Middle - - Main Menu - Main Menu + + Main Tool Bar Right + Main Tool Bar Right - - Main Tool Bar Left - Main Tool Bar Left + + Main Status Bar + Main Status Bar - - Main Tool Bar Middle - Main Tool Bar Middle + + Home Panels + - - Main Tool Bar Right - Main Tool Bar Right + + Home Navigation + Home Navigation - - Main Status Bar - Main Status Bar + + Home Tool + Home Tool - - Home Navigation - Home Navigation + + Navigation Panels + - - Home Tool - Home Tool + + Preferences + Preferences - - Preferences - Preferences + Help + Help - - Help - Help + + Workspace Panel Widgets + Workspace Panel Widgets - - Workspace Panel Widgets - Workspace Panel Widgets + + Status Text + Status Text - - Status Text - Status Text + + Properties + Properties - - Properties - Properties + + Plugins + Plugins - - Plugins - Plugins + Arrangement + Arrangement - - Arrangement - Arrangement + Mixer + Mixer - - Mixer - Mixer + Piano Roll + Piano Roll - - Piano Roll - Piano Roll + + Notifications + Notifications - - Notifications - Notifications + + Tips + Tips - - Tips - Tips + + Metadata + - - Digital Clock - Digital Clock + + Digital Clock + Digital Clock - - &File - &File + + Tempo Time Signature Indicator + - - File Open Actions - File Open Actions + + &File + &File - - Preference Actions - Preference Actions + + File Open Actions + File Open Actions - - &Edit - &Edit + + File Save Actions + - - Undo Actions - Undo Actions + + Preference Actions + Preference Actions - - Generic - Generic + + &Edit + &Edit - - Navigation - Navigation + + Undo Actions + Undo Actions - - Move Cu&rsor - Move Cu&rsor + + Generic + Generic - - &Move - &Move + + Navigation + Navigation - - &Select - &Select + + Move Cu&rsor + Move Cu&rsor - - &Extend Selection - &Extend Selection + + &Move + &Move - - Shrin&k Selection - Shrin&k Selection + + &Select + &Select - - Timeline - Timeline + + &Extend Selection + &Extend Selection - - Time Indicator Timecode Format Actions - Time Indicator Timecode Format Actions + + Shrin&k Selection + Shrin&k Selection - - Timeline Go to Actions - Timeline Go to Actions + + Timeline + Timeline - - Timeline Back Navigation Actions - Timeline Back Navigation Actions + + Time Indicator Timecode Format Actions + Time Indicator Timecode Format Actions - - Timeline Forward Navigation Actions - Timeline Forward Navigation Actions + + Timeline Go to Actions + Timeline Go to Actions - - &View - &View + + Timeline Back Navigation Actions + Timeline Back Navigation Actions - - Workspace Actions - Workspace Actions + + Timeline Forward Navigation Actions + Timeline Forward Navigation Actions - - &Workspace - &Workspace + + &View + &View - - Workspace Layouts - Workspace Layouts + + Workspace Actions + Workspace Actions - - Opened &Docking Panels - Opened &Docking Panels + + &Workspace + &Workspace - - Dock Action to Side Bar - Dock Action to Side Bar + + Workspace Layouts + Workspace Layouts - - Scroll Actions - Scroll Actions + + Opened &Docking Panels + Opened &Docking Panels - - Scroll - Scroll + + Dock Action to Side Bar + Dock Action to Side Bar - - Scroll by Page - Scroll by Page + + Scroll Actions + Scroll Actions - - Scroll To - Scroll To + + Scroll + Scroll - - View Visibility Actions - View Visibility Actions + + Scroll by Page + Scroll by Page - - &Window - &Window + + Scroll To + Scroll To - - Project Window Actions - Project Window Actions + + View Visibility Actions + View Visibility Actions - - About Actions - About Actions + + &Window + &Window - - Tool Bar Timeline Navigation Actions - Tool Bar Timeline Navigation Actions + + Project Window Actions + Project Window Actions - - + + + About Actions + About Actions + + + + Dspx Inspector Actions + + + + + Tool Bar Timeline Navigation Actions + Tool Bar Timeline Navigation Actions + + + ColorSchemePage - - - Color - Color + + + Color + Color + + + + Properties + Properties - - Properties - Properties + + Edit + Edit - - Edit - Edit + + + OK + OK - - - OK - OK + + Preview + Preview - - Preview - Preview + + Built-in + Built-in - - Built-in - Built-in + + Preset name + Preset name - - Preset name - Preset name + + Presets with the same name will be overwritten. + Presets with the same name will be overwritten. - - Presets with the same name will be overwritten. - Presets with the same name will be overwritten. + + + Color scheme files (*.dat) + Color scheme files (*.dat) - - - Color scheme files (*.dat) - Color scheme files (*.dat) + + + All files (*) + All files (*) - - - All files (*) - All files (*) + + Accent color + Accent color - - Accent color - Accent color + + Warning color + Warning color - - Warning color - Warning color + + Error color + Error color - - Error color - Error color + + Button color + Button color - - Button color - Button color + + Input box color + Input box color - - Input box color - Input box color + + Scroll bar color + Scroll bar color - - Scroll bar color - Scroll bar color + + Border color + Border color - - Border color - Border color + + Primary background color + Primary background color - - Primary background color - Primary background color + + Secondary background color + Secondary background color - - Secondary background color - Secondary background color + + Tertiary background color + Tertiary background color - - Tertiary background color - Tertiary background color + + Quaternary background color + Quaternary background color - - Quaternary background color - Quaternary background color + + Splitter color + Splitter color - - Splitter color - Splitter color + + Pane separator color + - - Primary foreground color - Primary foreground color + + Primary foreground color + Primary foreground color - - Secondary foreground color - Secondary foreground color + + Secondary foreground color + Secondary foreground color - - Link color - Link color + + Link color + Link color - - Navigation color - Navigation color + + Navigation color + Navigation color - - Shadow color - Shadow color + + Shadow color + Shadow color - - Highlight color - Highlight color + + Highlight color + Highlight color - - Flat button high contrast border color - Flat button high contrast border color + + Flat button high contrast border color + Flat button high contrast border color - - Control disabled color change - Control disabled color change + + Control disabled color change + Control disabled color change - - Foreground disabled color change - Foreground disabled color change + + Foreground disabled color change + Foreground disabled color change - - Control hovered color change - Control hovered color change + + Control hovered color change + Control hovered color change - - Foreground hovered color change - Foreground hovered color change + + Foreground hovered color change + Foreground hovered color change - - Control pressed color change - Control pressed color change + + Control pressed color change + Control pressed color change - - Foreground pressed color change - Foreground pressed color change + + Foreground pressed color change + Foreground pressed color change - - Control checked color change - Control checked color change + + Control checked color change + Control checked color change - - Annotation popup title color change - Annotation popup title color change + + Annotation popup title color change + Annotation popup title color change - - Annotation popup content color change - Annotation popup content color change + + Annotation popup content color change + Annotation popup content color change - - Docking panel header active color change - Docking panel header active color change + + Docking panel header active color change + Docking panel header active color change - - Preset - Preset + + Preset + Preset - - Preset Actions - Preset Actions + + Preset Actions + Preset Actions - - Save As... - Save As... + + Save As... + Save As... - - Rename... - Rename... + + Rename... + Rename... - - Delete - Delete + + Delete + Delete - - Import from File... - Import from File... + + Import from File... + Import from File... - - Export to File... - Export to File... + + Export to File... + Export to File... - - + + ColorSchemeWelcomeWizardPage - - Color Scheme - Color Scheme + + Color Scheme + Color Scheme + + + + Choose a color scheme for %1 + Choose a color scheme for %1 - - Choose a color scheme for %1 - Choose a color scheme for %1 + + Dark + Dark - - Dark - Dark + + Light + Light - - Light - Light + + High contrast + High contrast - - High contrast - High contrast + + You can also create a custom color theme later in Settings > Appearance > Color Scheme + You can also create a custom color theme later in Settings > Appearance > Color Scheme + + + Core::ActionWindowInterfaceBase - - You can also create a custom color theme later in Settings > Appearance > Color Scheme - You can also create a custom color theme later in Settings > Appearance > Color Scheme + + Toggle "%1" + Toggle "%1" - - + + + Open Menu "%1"... + + + + Core::CoreInterface - - About %1 - About %1 + + About %1 + About %1 + + + + Core::DspxInspectorDialog + + + File path + + + + + &Browse + + + + + &Run Check + + + + + &Problems + + + + + DSPX Inspector + + + + + Null + + + + + Boolean + + + + + Integer + + + + + Number + + + + + String + + + + + Array + + + + + Object + + + + + Fatal: Failed to open file + + + + + Path + + + + + + Error code + + + + + + Error text + + + + + Fatal: Failed to parse JSON + + + + + Offset in file + + + + + The file is not a valid JSON document. + + + + + Fatal: Root is not an object + + + + + The root of JSON document is not an object. + + + + + Fatal: Unrecognized version + + + + + Actual version + + + + + This project file may have been created with a newer version of %1 or another application, and its version is not recognized by %1. Please try exporting the project from the application with which it was created as a version compatible with your current %1. + + + + + Invalid data type + + + + + Expected data type + + + + + , + + + + + Actual data type + + + + + The value at the specific path is not of the expected data type. + + + + + Invalid object type + + + + + Expected object type + + + + + Actual object type + + + + + The object at the specific path is not of the expected object type. + + + + + Range constraint violation + + + + + Expected maximum value + + + + + + None + None + + + + Expected minimum value + + + + + Actual value + + + + + The value of the property at the specific path is outside the allowed range. The value must be between the expected minimum and maximum value (inclusive). + + + + + Expected enum value + + + + + Actual enum value + + + + + Enum constraint violation + + + + + The value of the property at the specific path is not one of the allowed enum values. + + + + + Missing property + + + + + Missing properties + + + + + One or more properties are missing in the object at the specific path. + + + + + Redundant property + + + + + Redundant properties + + + + + One or more properties are redundant in the object at the specific path. + + + + + Index + + + + + Overlapping items + + + + + Items at specific indexes in the array at the specific path overlap. + + + + + Zero-length range + + + + + The range length of the entity object at the specific path is zero. + + + + + Erroneous clip range + + + + + The clipping range of the clip entity object at the specific path exceeds its range limit. + + + + + Erroneous clip position + + + + + The position of the clip entity object at the specific path exceeds the view range limit. It might be not visible in the viewport. + + + + + Safe range limit exceeded + + + + + The position of the entity object at the specific path exceeds the safe project length limit (4,000,000 ticks). + + + + + File Created With Another Application + + + + + Editor ID + + + + + Editor name + + + + + This project file was created with another application. Some features may not be fully compatible or may behave differently. + + + + + File Created With Incompatible Version + + + + + Version + + + + + This project file was created with an newer version or test version of %1. Some features may not be fully compatible or may behave differently. + + + + + No problems found + + + + + The project file is valid and no problems were found. + + + + + Core::EditTempoTimeSignatureScenario + + + Editing tempo + + + + + Editing time signature + - - + + + Core::Internal::AfterSavingNotifyAddOn + + + The file has been saved to %1 + + + + Core::Internal::AppearancePage - - Appearance - Appearance + + Appearance + Appearance + + + + Configure how %1 looks like + Configure how %1 looks like + + + + Core::Internal::CloseSaveCheckAddOn + + + Do you want to save before closing? + - - Configure how %1 looks like - Configure how %1 looks like + + If you choose not to save, a copy of the current project file will be created to help recover your work in case of accidental incorrect operation. + - - + + Core::Internal::ColorSchemeCollection - - Scopic Dark - Scopic Dark + + Scopic Dark + Scopic Dark - - Scopic Light - Scopic Light + + Scopic Light + Scopic Light - - Scopic High Contrast - Scopic High Contrast + + Scopic High Contrast + Scopic High Contrast - - Failed to Import Preset - Failed to Import Preset + + Failed to Import Preset + Failed to Import Preset - - - Unable to open file "%1" - Unable to open file "%1" + + + Unable to open file "%1" + Unable to open file "%1" - - Failed to import preset - Failed to import preset + + Failed to import preset + Failed to import preset - - Invalid format in file "%1" - Invalid format in file "%1" + + Invalid format in file "%1" + Invalid format in file "%1" - - Failed to export preset - Failed to export preset + + Failed to export preset + Failed to export preset - - (Unsaved preset) - (Unsaved preset) + + (Unsaved preset) + (Unsaved preset) - - + + Core::Internal::ColorSchemePage - - Color Scheme - Color Scheme + + Color Scheme + Color Scheme - - Configure the colors and visual effects of various components - Configure the colors and visual effects of various components + + Configure the colors and visual effects of various components + Configure the colors and visual effects of various components - - Alpha: %L1 - Alpha: %L1 + + Alpha: %L1 + Alpha: %L1 - - Saturation (HSV): %L1 - Saturation (HSV): %L1 + + Saturation (HSV): %L1 + Saturation (HSV): %L1 - - Value: %L1 - Value: %L1 + + Value: %L1 + Value: %L1 - - Saturation (HSL): %L1 - Saturation (HSL): %L1 + + Saturation (HSL): %L1 + Saturation (HSL): %L1 - - Lightness: %L1 - Lightness: %L1 + + Lightness: %L1 + Lightness: %L1 - - QColor::lighter(): %L1 - QColor::lighter(): %L1 + + QColor::lighter(): %L1 + QColor::lighter(): %L1 - - Top Blend: %1 - Top Blend: %1 + + Top Blend: %1 + Top Blend: %1 - - Bottom Blend: %1 - Bottom Blend: %1 + + Bottom Blend: %1 + Bottom Blend: %1 - - Syntax error at line %L1: Missing colon in declaration - Syntax error at line %L1: Missing colon in declaration + + Syntax error at line %L1: Missing colon in declaration + Syntax error at line %L1: Missing colon in declaration - - Syntax error at line %L1: Empty property name - Syntax error at line %L1: Empty property name + + Syntax error at line %L1: Empty property name + Syntax error at line %L1: Empty property name - - Syntax error at line %L1: Empty property value - Syntax error at line %L1: Empty property value + + Syntax error at line %L1: Empty property value + Syntax error at line %L1: Empty property value - - Syntax error at line %L1: Invalid 'alpha' value - Syntax error at line %L1: Invalid 'alpha' value + + Syntax error at line %L1: Invalid 'alpha' value + Syntax error at line %L1: Invalid 'alpha' value - - Syntax error at line %L1: Invalid 'saturation' value - Syntax error at line %L1: Invalid 'saturation' value + + Syntax error at line %L1: Invalid 'saturation' value + Syntax error at line %L1: Invalid 'saturation' value - - Syntax error at line %L1: Invalid 'value' value - Syntax error at line %L1: Invalid 'value' value + + Syntax error at line %L1: Invalid 'value' value + Syntax error at line %L1: Invalid 'value' value - - Syntax error at line %L1: Invalid 'hsl-saturation' value - Syntax error at line %L1: Invalid 'hsl-saturation' value + + Syntax error at line %L1: Invalid 'hsl-saturation' value + Syntax error at line %L1: Invalid 'hsl-saturation' value - - Syntax error at line %L1: Invalid 'lightness' value - Syntax error at line %L1: Invalid 'lightness' value + + Syntax error at line %L1: Invalid 'lightness' value + Syntax error at line %L1: Invalid 'lightness' value - - Syntax error at line %L1: Invalid 'lighter' value (must be a positive integer) - Syntax error at line %L1: Invalid 'lighter' value (must be a positive integer) + + Syntax error at line %L1: Invalid 'lighter' value (must be a positive integer) + Syntax error at line %L1: Invalid 'lighter' value (must be a positive integer) - - Syntax error at line %L1: Invalid color value for 'top-blend' - Syntax error at line %L1: Invalid color value for 'top-blend' + + Syntax error at line %L1: Invalid color value for 'top-blend' + Syntax error at line %L1: Invalid color value for 'top-blend' - - Syntax error at line %L1: Invalid color value for 'bottom-blend' - Syntax error at line %L1: Invalid color value for 'bottom-blend' + + Syntax error at line %L1: Invalid color value for 'bottom-blend' + Syntax error at line %L1: Invalid color value for 'bottom-blend' - - Syntax error at line %L1: Unknown property '%2' - Syntax error at line %L1: Unknown property '%2' + + Syntax error at line %L1: Unknown property '%2' + Syntax error at line %L1: Unknown property '%2' - - + + Core::Internal::CorePlugin - - Initializing core plugin... - Initializing core plugin... + + Initializing core plugin... + Initializing core plugin... - - Initializing GUI... - Initializing GUI... + + Initializing GUI... + Initializing GUI... - - + + + Core::Internal::FileBackupPage + + + File and Backup + + + + + Configure file handling and backup behaviors of %1 + + + + Core::Internal::FindActionsAddOn - - Find actions - Find actions + + Find actions + Find actions - - + + Core::Internal::FindActionsModel - - Toggle "%1" - Toggle "%1" + + Toggle "%1" + Toggle "%1" - - %1: %2 - %1: %2 + + Open Menu "%1"... + - - recently used - recently used + + %1: %2 + %1: %2 - - + + + recently used + recently used + + + Core::Internal::GeneralPage - - General - General + + General + General - - Configure general behaviors of %1 - Configure general behaviors of %1 + + Configure general behaviors of %1 + Configure general behaviors of %1 - - Restart %1 - Restart %1 + + Restart %1 + Restart %1 - - Restart %1 to apply language changes? - Restart %1 to apply language changes? + + Restart %1 to apply language changes? + Restart %1 to apply language changes? - - + + Core::Internal::KeyMapPage - - Keymap - Keymap + + Keymap + Keymap - - Configure shortcuts of actions - Configure shortcuts of actions + + Configure shortcuts of actions + Configure shortcuts of actions - - + + Core::Internal::LogPage - - Log - Log + + Log + Log - - Configure log output and archiving behaviors - Configure log output and archiving behaviors + + Configure log output and archiving behaviors + Configure log output and archiving behaviors - - + + Core::Internal::MenuPage - - Menus and Toolbars - Menus and Toolbars + + Menus and Toolbars + Menus and Toolbars - - Configure the layout of menus and toolbars - Configure the layout of menus and toolbars + + Configure the layout of menus and toolbars + Configure the layout of menus and toolbars - - + + Core::Internal::NotificationAddOn - - %1 (+%Ln notification(s)) - - %1 (+%Ln notification) - %1 (+%Ln notifications) - - - - + + %1 (+%Ln notification(s)) + + %1 (+%Ln notification) + %1 (+%Ln notifications) + + + + Core::Internal::ProjectStartupTimerAddOn - - Initializing project window... - Initializing project window... + + Initializing project window... + Initializing project window... + + + + Project window initialized in %1 seconds + Project window initialized in %1 seconds - - Project window initialized in %1 seconds - Project window initialized in %1 seconds + + Project window initialized + Project window initialized + + + Core::Internal::RecentFileAddOn - - Project window initialized - Project window initialized + + <i>File moved or deleted</i> + - - + + Core::Internal::TimeIndicatorPage - - Time Indicator - Time Indicator + + Time Indicator + Time Indicator - - Configure time indicator display and interaction behaviors - Configure time indicator display and interaction behaviors + + Configure time indicator display and interaction behaviors + Configure time indicator display and interaction behaviors - - + + Core::Internal::TimelineAddOn - - measure %L1, beat %L2 - measure %L1, beat %L2 + + measure %L1, beat %L2 + measure %L1, beat %L2 - - measure %L1, beat %L2, tick %L3 - measure %L1, beat %L2, tick %L3 + + measure %L1, beat %L2, tick %L3 + measure %L1, beat %L2, tick %L3 - - %Ln minute(s) - absolute time - - %Ln minute - %Ln minutes - + + %Ln minute(s) + absolute time + + %Ln minute + %Ln minutes + - - %Ln second(s) - absolute time - - %Ln second - %Ln seconds - + + %Ln second(s) + absolute time + + %Ln second + %Ln seconds + - - %Ln millisecond(s) - absolute time - - %Ln millisecond - %Ln milliseconds - + + %Ln millisecond(s) + absolute time + + %Ln millisecond + %Ln milliseconds + - - - %1 %2 - absolute minute second - %1 %2 + + + %1 %2 + absolute minute second + %1 %2 - - %1 %2 - absolute second millisecond - %1 %2 + + %1 %2 + absolute second millisecond + %1 %2 - - %1 %2 %3 - absolute minute second millisecond - %1 %2 %3 + + %1 %2 %3 + absolute minute second millisecond + %1 %2 %3 - - %Ln minute(s) - relative time - - %Ln minute - %Ln minutes - + + %Ln minute(s) + relative time + + %Ln minute + %Ln minutes + - - %Ln second(s) - relative time - - %Ln second - %Ln seconds - + + %Ln second(s) + relative time + + %Ln second + %Ln seconds + - - %Ln millisecond(s) - relative time - - %Ln millisecond(s) - %Ln milliseconds - + + %Ln millisecond(s) + relative time + + %Ln millisecond(s) + %Ln milliseconds + - - - %1 %2 - relative minute second - %1 %2 + + + %1 %2 + relative minute second + %1 %2 - - %1 %2 - relative second millisecond - %1 %2 + + %1 %2 + relative second millisecond + %1 %2 - - %1 %2 %3 - relative minute second millisecond - %1 %2 %3 + + %1 %2 %3 + relative minute second millisecond + %1 %2 %3 - - - - - - - - - - Go to %1 - Go to %1 + + + + + + + + + + Go to %1 + Go to %1 - - - absolute time... - absolute time... + + + absolute time... + absolute time... - - %1 (%2) - %1 (%2) + + %1 (%2) + %1 (%2) - - Move backward by %1 - Move backward by %1 + + Move backward by %1 + Move backward by %1 - - Move forward by %1 - Move forward by %1 + + Move forward by %1 + Move forward by %1 - - %1 (to %2) - %1 (to %2) + + %1 (to %2) + %1 (to %2) - - - - - + + + + + The time offset exceeds the boundary and has been adjusted to zero - + The time offset exceeds the boundary and has been adjusted to zero - - the end of project (%1) - the end of project (%1) + + the end of project (%1) + the end of project (%1) - - previous measure (%1) - previous measure (%1) + + previous measure (%1) + previous measure (%1) - - previous beat (%1) - previous beat (%1) + + previous beat (%1) + previous beat (%1) - - previous tick (%1) - previous tick (%1) + + previous tick (%1) + previous tick (%1) - - next measure (%1) - next measure (%1) + + next measure (%1) + next measure (%1) - - next beat (%1) - next beat (%1) + + next beat (%1) + next beat (%1) - - next tick (%1) - next tick (%1) + + next tick (%1) + next tick (%1) - - - Type "?" to view tips - Type "?" to view tips + + + Type "?" to view tips + Type "?" to view tips - - - Invalid format - Invalid format + + + Invalid format + Invalid format - - Jump to - Jump to + + Jump to + Jump to - - Input should not be empty - Input should not be empty + + Input should not be empty + Input should not be empty - - + + Core::Internal::ViewVisibilityAddOn - - Please take attention - Please take attention + Please take attention + Please take attention - - After hiding the menu bar, it can be difficult to show it again. Make sure you know how to do this. + After hiding the menu bar, it can be difficult to show it again. Make sure you know how to do this. Continue? - After hiding the menu bar, it can be difficult to show it again. Make sure you know how to do this. + After hiding the menu bar, it can be difficult to show it again. Make sure you know how to do this. Continue? - - + + Core::Internal::WorkspaceAddOn - - Save Current Layout As... - Save Current Layout As... + + Save Current Layout As... + Save Current Layout As... + + + + Default Layout + Default Layout + + + + custom layout + custom layout + + + + Workspace layout actions + Workspace layout actions + + + + Apply + Apply + + + + Rename + Rename + + + + Delete + Delete + + + + Custom layout "%1" actions + Custom layout "%1" actions + + + + Core::OpenSaveProjectFileScenario + + + DiffScope Project Exchange Format (*.dspx) + + + + + All Files (*) + + + + + Failed to open file + + + + + Failed to save file + + + + + Open DSPX Inspector + + + + + Failed to parse file content + + + + + %1 + +You can check for problems in the file with DSPX Inspector. + + + + + File created with another application + + + + + name unknown + + + + + File created with incompatible %1 version + + + + + Additional check failed + + + + + Core::ProjectWindowInterface + + + File Modified Externally + + + + + The file has been modified by another program since it was last saved. + +Do you want to save as a new file or overwrite it? + + + + + Save As... + Save As... + + + + Overwrite + + + + + + Untitled + + + + EditTempoDialog - - Default Layout - Default Layout + + Edit Tempo + - - custom layout - custom layout + + Tempo + - - Workspace layout actions - Workspace layout actions + + Position + - - Apply - Apply + + Modify existing one + - - Rename - Rename + + Insert new one + + + + EditTimeSignatureDialog - - Delete - Delete + + Edit Time Signature + - - Custom layout "%1" actions - Custom layout "%1" actions + + Time signature + - - + + + Numerator + + + + + Denominator + + + + + Position + + + + + Modify existing one + + + + + Insert new one + + + + + FileBackupPage + + + File + File + + + + Lock opened files + + + + + Locking an open file prevents it from being modified by other programs. Change to this option will take effect only for projects opened after the change + + + + + Check for external modifications when saving a file + + + + GeneralPage - - Startup - Startup + + Startup + Startup - - When starting %1 - When starting %1 + + When starting %1 + When starting %1 - - Open the home window - Open the home window + + Open the home window + Open the home window - - Create a new project - Create a new project + + Create a new project + Create a new project - - Open previous projects on startup automatically - Open previous projects on startup automatically + Open previous projects on startup automatically + Open previous projects on startup automatically - - Close the home window after opening a project - Close the home window after opening a project + + Close the home window after opening a project + Close the home window after opening a project - - - Language - Language + + + Language + Language - - Use system language - Use system language + + Use system language + Use system language - - (Restart required) - (Restart required) + + (Restart required) + (Restart required) - - Notification - Notification + + Notification + Notification - - Play sound alert when a notification bubble is sent - Play sound alert when a notification bubble is sent + + Play sound alert when a notification bubble is sent + Play sound alert when a notification bubble is sent - - Timeout for auto hiding notification bubbles - Timeout for auto hiding notification bubbles + + Timeout for auto hiding notification bubbles + Timeout for auto hiding notification bubbles - - milliseconds - milliseconds + + milliseconds + milliseconds - - Reset All "Do Not Show Again" - Reset All "Do Not Show Again" + + Reset All "Do Not Show Again" + Reset All "Do Not Show Again" - - Find Actions - Find Actions + + Window + Window - - Number of "recently used" records - Number of "recently used" records + + Memorize window position and size + - - Clear History - Clear History + + Find Actions + Find Actions - - History cleared - History cleared + + Number of "recently used" records + Number of "recently used" records - - Network Proxy - Network Proxy + + Clear History + Clear History - - No proxy - No proxy + + History cleared + History cleared - - Use system proxy - Use system proxy + + Network Proxy + Network Proxy - - Manually configure proxy - Manually configure proxy + + No proxy + No proxy - - Type - Type + + Use system proxy + Use system proxy - - SOCK5 - SOCK5 + + Manually configure proxy + Manually configure proxy - - HTTP - HTTP + + Type + Type - - Hostname - Hostname + + SOCK5 + SOCK5 - - Port - Port + + HTTP + HTTP - - Authentication - Authentication + + Hostname + Hostname - - Username - Username + + Port + Port - - Password - Password + + Authentication + Authentication - Updates - Updates + + Username + Username - Check for updates on startup - Check for updates on startup + + Password + Password - Type of update to check for - Type of update to check for + Updates + Updates - Stable - Stable + Check for updates on startup + Check for updates on startup - Beta - Beta + Type of update to check for + Type of update to check for - Check for Updates - Check for Updates + Stable + Stable - - + + Beta + Beta + + + Check for Updates + Check for Updates + + + + LoadingFailedFallbackPanel + + + + Failed to load component + + + + + This component cannot be loaded because it is not registered to the application. The plugin providing this component might be disabled or no longer available. + + + + + An error occurred while loading this component. + + + + + Component identifier + + + + + Error + Error + + + LogPage - - Debug - Debug + + Debug + Debug - - Info - Info + + Info + Info - - Warning - Warning + + Warning + Warning - - Critical - Critical + + Critical + Critical - - Fatal - Fatal + + Fatal + Fatal - - File Logging - File Logging + + File Logging + File Logging - - File log level - File log level + + File log level + File log level - - Max file size - Max file size + + Max file size + Max file size - - - KiB - KiB + + + KiB + KiB - - Max archive size - Max archive size + + Max archive size + Max archive size - - Max archive days - Max archive days + + Max archive days + Max archive days - - Compress level - Compress level + + Compress level + Compress level - - (0 = no compress, 9 = best compress) - (0 = no compress, 9 = best compress) + + (0 = no compress, 9 = best compress) + (0 = no compress, 9 = best compress) - - Console Logging - Console Logging + + Console Logging + Console Logging - - Console log level is overridden to "Debug" in a debug build - Console log level is overridden to "Debug" in a debug build + + Console log level is overridden to "Debug" in a debug build + Console log level is overridden to "Debug" in a debug build - - Console log level - Console log level + + Console log level + Console log level - - Prettify console output - Prettify console output + + Prettify console output + Prettify console output - - Log Location - Log Location + + Log Location + Log Location - - Open in %1 - Open in %1 + + Open in %1 + Open in %1 - - + + MenuPage - - Add - Add + + Add + Add + + + + Add Action or Menu... + Add Action or Menu... + + + + Add Separator + Add Separator + + + + Add Stretch + Add Stretch + + + + Edit Icon... + Edit Icon... + + + + Move Up + Move Up + + + + Move Down + Move Down - - Add Action or Menu... - Add Action or Menu... + + Remove + Remove - - Add Separator - Add Separator + + Restore + Restore - - Add Stretch - Add Stretch + + Restore This Menu + Restore This Menu - - Edit Icon... - Edit Icon... + + Restore All + Restore All - - Move Up - Move Up + + Search + Search + + + MetadataPanel - - Move Down - Move Down + + Edit + Edit - - Remove - Remove + + Path + - - Restore - Restore + + Unspecified + - - Restore This Menu - Restore This Menu + + Reveal in %1 + - - Restore All - Restore All + + Title + - - Search - Search + + Author + - - + + + Cent Shift + + + + NotificationsPanel - - Clear All - Clear All + + Clear All + Clear All - - No notification - No notification + + No notification + No notification - - + + PluginDialog - - Plugins - Plugins + + Plugins + Plugins - - + + ProjectActions - - Click to show details - Click to show details + + Click to show details + Click to show details + + + + ProjectWindow + + + Untitled + + + + + Modified Externally + + + + + Unsaved + + + + + RecentFilesPanel + + + + + + Search + Search + + + + + Grid view + + + + + + List view + + + + + Clear Recovery Files + + + + + Clear Recent Files + - - + + + No recovery file +If %1 exits abnormally, automatic recovery files will be displayed here. + + + + + No recent files + + + + + Open + + + + + Reveal in %1 + + + + + Remove from "Recovery Files" + + + + + Remove from "Recent Files" + + + + TimeIndicatorPage - - Toggle timecode format - Toggle timecode format + + Toggle timecode format + Toggle timecode format - - Show "Go To" - Show "Go To" + + Show "Go To" + Show "Go To" - - Show "Quick Jump" - Show "Quick Jump" + + Show "Quick Jump" + Show "Quick Jump" - - Display - Display + + Display + Display - - Show background - Show background + + Show background + Show background - - Fine-tune character spacing - Fine-tune character spacing + + Fine-tune character spacing + Fine-tune character spacing - - Enabling fine-tuning of character spacing can prevent text width changes caused by timecode changes - Enabling fine-tuning of character spacing can prevent text width changes caused by timecode changes + + Enabling fine-tuning of character spacing can prevent text width changes caused by timecode changes + Enabling fine-tuning of character spacing can prevent text width changes caused by timecode changes - - Show slider on hover - Show slider on hover + + Show slider on hover + Show slider on hover - - Interaction Behavior - Interaction Behavior + + Interaction Behavior + Interaction Behavior - - Click action - Click action + + Click action + Click action - - Double-click action - Double-click action + + Double-click action + Double-click action - - Press-and-hold action - Press-and-hold action + + Press-and-hold action + Press-and-hold action - - + + TimelineAddOnActions - - Current project time - Current project time + + Current project time + Current project time + + + + Slide to adjust current project time + Slide to adjust current project time + + + + UndoAddOnActions + + + Undo %1 + + + + + Undo + + + + + Redo %1 + - - Slide to adjust current project time - Slide to adjust current project time + + Redo + - - + + WorkspaceAddOnActions - - &Apply - &Apply + + &Apply + &Apply - - &Rename - &Rename + + &Rename + &Rename - - &Delete - &Delete + + &Delete + &Delete - - - - None - None + + + + None + None - - %1 (Left Top) - %1 (Left Top) + + %1 (Left Top) + %1 (Left Top) - - %1 (Left Bottom) - %1 (Left Bottom) + + %1 (Left Bottom) + %1 (Left Bottom) - - %1 (Right Top) - %1 (Right Top) + + %1 (Right Top) + %1 (Right Top) - - %1 (Right Bottom) - %1 (Right Bottom) + + %1 (Right Bottom) + %1 (Right Bottom) - - %1 (Top Left) - %1 (Top Left) + + %1 (Top Left) + %1 (Top Left) - - %1 (Top Right) - %1 (Top Right) + + %1 (Top Right) + %1 (Top Right) - - %1 (Bottom Left) - %1 (Bottom Left) + + %1 (Bottom Left) + %1 (Bottom Left) - - %1 (Bottom Right) - %1 (Bottom Right) + + %1 (Bottom Right) + %1 (Bottom Right) - - Add Action... - Add Action... + + Add Action... + Add Action... - - Error - Error + + Error + Error - - Failed to create panel "%1" - Failed to create panel "%1" + + Failed to create panel "%1" + Failed to create panel "%1" - - + + WorkspaceAddOnHelper - - The current workspace does not contain any panels - The current workspace does not contain any panels + + The current workspace does not contain any panels + The current workspace does not contain any panels - - Workspace data might be erroneous. You can try restoring the default workspace. - Workspace data might be erroneous. You can try restoring the default workspace. + + Workspace data might be erroneous. You can try restoring the default workspace. + Workspace data might be erroneous. You can try restoring the default workspace. - - Restore Default Workspace - Restore Default Workspace + + Restore Default Workspace + Restore Default Workspace - - Custom layout name - Custom layout name + + Custom layout name + Custom layout name - - Name should not be empty - Name should not be empty + + Name should not be empty + Name should not be empty - - New name should not be the same as old name - New name should not be the same as old name + + New name should not be the same as old name + New name should not be the same as old name - - Custom presets with the same name will be overwritten - Custom presets with the same name will be overwritten + + Custom presets with the same name will be overwritten + Custom presets with the same name will be overwritten - - Delete - Delete + + Delete + Delete - - Delete layout "%1"? - Delete layout "%1"? + + Delete layout "%1"? + Delete layout "%1"? - - Drag to the sidebar to add "%1" - Drag to the sidebar to add "%1" + + Drag to the sidebar to add "%1" + Drag to the sidebar to add "%1" - + diff --git a/src/plugins/coreplugin/res/translations/org.diffscope.core_ja_JP.ts b/src/plugins/coreplugin/res/translations/org.diffscope.core_ja_JP.ts index e9bede8c..ed8c1a03 100644 --- a/src/plugins/coreplugin/res/translations/org.diffscope.core_ja_JP.ts +++ b/src/plugins/coreplugin/res/translations/org.diffscope.core_ja_JP.ts @@ -91,17 +91,25 @@ <p>Based on Qt version %1.<br>Copyright © 2019-%2 Team OpenVPI. All rights reserved.</p> - <p>A professional singing-voice-synthesis editor powered by DiffSinger</p><p>Version %1</p><p>Copyright © %2-%3 Team OpenVPI. All rights reserved.</p><p>Visit <a href="%4">%4</a> for more information.</p> <p>A professional singing-voice-synthesis editor powered by DiffSinger</p><p>Version %1</p><p>Copyright © %2-%3 Team OpenVPI. All rights reserved.</p><p>Visit <a href="%4">%4</a> for more information.</p> - <h3>License</h3><p>Licensed under the Apache License, Version 2.0.<br>You may obtain a copy of the License at %1.</p><p>This application is distributed <b>AS IS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND</b>, either express or implied.</p> <h3>License</h3><p>Licensed under the Apache License, Version 2.0.<br>You may obtain a copy of the License at %1.</p><p>This application is distributed <b>AS IS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND</b>, either express or implied.</p> - + + <p>A professional singing-voice-synthesis editor powered by DiffSinger</p><p>Version %1</p><p>Copyright © %2-%3 %4. All rights reserved.</p><p>Visit <a href="%5">%5</a> for more information.</p> + <p>A professional singing-voice-synthesis editor powered by DiffSinger</p><p>Version %1</p><p>Copyright © %2-%3 %4. All rights reserved.</p><p>Visit <a href="%5">%5</a> for more information.</p> + + + + <h3>License</h3><p>Licensed under the Apache License, Version 2.0.<br>You may obtain a copy of the License at %1.</p><p>This application is distributed <b>AS IS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND</b>, either express or implied.</p><p>This license does not apply to plugins. Please refer to Plugins to view the licenses applicable to each individual plugin.</p> + <h3>License</h3><p>Licensed under the Apache License, Version 2.0.<br>You may obtain a copy of the License at %1.</p><p>This application is distributed <b>AS IS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND</b>, either express or implied.</p><p>This license does not apply to plugins. Please refer to Plugins to view the licenses applicable to each individual plugin.</p> + + + <h3>Build Information</h3><p>Branch: %1<br>Commit: %2<br>Build date: %3<br>Toolchain: %4 %5 %6</p> <h3>Build Information</h3><p>Branch: %1<br>Commit: %2<br>Build date: %3<br>Toolchain: %4 %5 %6</p> @@ -109,170 +117,186 @@ <h3>Build Information</h3><p>Version: %1<br>Branch: %2<br>Commit: %3<br>Build date: %4<br>Toolchain: %5 %6 %7</p> <h3>Build Information</h3><p>Version: %1<br>Branch: %2<br>Commit: %3<br>Build date: %4<br>Toolchain: %5 %6 %7</p> + + + Version %1 + Version %1 + + + + Copyright © %1-%2 %3. All rights reserved. + Copyright © %1-%2 %3. All rights reserved. + Application::ActionClass - - - + + + + + + + File File - - + + Preferences Preferences - - - - + + + + Window Window - + Application Application - - - - + + + + + Home Home - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Edit Edit - - + + Time Indicator Time Indicator - - - - - - - - - - - - + + + + + + + + + + + + Timeline Timeline - - + + Core Core - - - - - - - + + + + + + + Panel Panel - + + Widget Widget - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + View View - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + Workspace Workspace - - - - + + + + + Help Help @@ -280,157 +304,162 @@ Application::ActionDescription - + Create a project in a new window Create a project in a new window - + + Create a project from template in a new window + Create a project from template in a new window + + + Open an existing project in a new window Open an existing project in a new window - + Open application settings Open application settings - + Open plugin configuration dialog Open plugin configuration dialog - + Switch to home window if it is opened, or else open home window Switch to home window if it is opened, or else open home window - + Quit DiffScope Quit DiffScope - + Toggle "Recent Files" page Toggle "Recent Files" page - + Toggle "Recovery Files" page Toggle "Recovery Files" page - + Toggle grid view in "Recent Files" page Toggle grid view in "Recent Files" page - + Toggle list view in "Recent Files" page Toggle list view in "Recent Files" page - + Show music time (measure:beat:tick) on the time indicator Show music time (measure:beat:tick) on the time indicator - + Show absolute time (minute:second.millisecond) on the time indicator Show absolute time (minute:second.millisecond) on the time indicator - + Navigate to a specific position Navigate to a specific position - + Quickly navigate to a specific position Quickly navigate to a specific position - + Navigate to the start of current project Navigate to the start of current project - + Navigate to the end of current project Navigate to the end of current project - + Reset project end time based on project content Reset project end time based on project content - + Apply the default workspace layout Apply the default workspace layout - + Show or hide the main menu bar Show or hide the main menu bar - + Show or hide the main tool bar Show or hide the main tool bar - + Show or hide the left side bar Show or hide the left side bar - + Show or hide the right side bar Show or hide the right side bar - + Show or hide the top side bar Show or hide the top side bar - + Show or hide the bottom side bar Show or hide the bottom side bar - + Show or hide the status bar Show or hide the status bar - + Switch to the next project window Switch to the next project window - + Switch to the previous project window Switch to the previous project window - + Show help contents Show help contents - + Find and trigger action by name Find and trigger action by name - + Show information about DiffScope Show information about DiffScope - + Show information about Qt Show information about Qt @@ -438,747 +467,811 @@ Application::ActionText - + &New &New - + + New from Template... + New from Template... + + + &Open... &Open... - + + Open &Recent File + Open &Recent File + + + &Save &Save - + + Save &As... + Save &As... + + + + Save Copy As... + Save Copy As... + + + Se&ttings... Se&ttings... - + Pl&ugins... Pl&ugins... - + Show &Home Window Show &Home Window - + E&xit E&xit - + Recent Files Recent Files - + Recovery Files Recovery Files - + Grid View Grid View - + List View List View - + &Undo &Undo - + &Redo &Redo - + Cu&t Cu&t - + &Copy &Copy - + &Paste &Paste - Paste Special... Paste Special... - + &Delete &Delete - + Select &All Select &All - + Dese&lect Dese&lect - + Select Current Select Current - + Select &Up Select &Up - + Select &Down Select &Down - + Select &Left Select &Left - + Select &Right Select &Right - + Move &Up Move &Up - + Move &Down Move &Down - + Move &Left Move &Left - + Move &Right Move &Right - + &Move... &Move... - + Scroll &Up Scroll &Up - + Scroll &Down Scroll &Down - + Scroll &Left Scroll &Left - + Scroll &Right Scroll &Right - + Move Cursor &Up Move Cursor &Up - + Move Cursor &Down Move Cursor &Down - + Move Cursor &Left Move Cursor &Left - + Move Cursor &Right Move Cursor &Right - + Extend Selection &Up Extend Selection &Up - + Extend Selection &Down Extend Selection &Down - + Extend Selection &Left Extend Selection &Left - + Extend Selection &Right Extend Selection &Right - + Shrink Selection &Up Shrink Selection &Up - + Shrink Selection &Down Shrink Selection &Down - + Shrink Selection &Left Shrink Selection &Left - + Shrink Selection &Right Shrink Selection &Right - + Page &Up Page &Up - + Page &Down Page &Down - + Page &Left Page &Left - + Page &Right Page &Right - + Scroll to &Top Scroll to &Top - + Scroll to &Bottom Scroll to &Bottom - + Scroll to &Start Scroll to &Start - + Scroll to &End Scroll to &End - + Scroll to Current Time Scroll to Current Time - + Show &Music Time Show &Music Time - + Show &Absolute Time Show &Absolute Time - + &Go To... &Go To... - + &Quick Jump... &Quick Jump... - + Go to &Start Go to &Start - + Go to Previous Measure Go to Previous Measure - + Go to Previous Beat Go to Previous Beat - + Go to Previous Tick Go to Previous Tick - + Go to &End Go to &End - + Go to Next Measure Go to Next Measure - + Go to Next Beat Go to Next Beat - + Go to Next Tick Go to Next Tick - + Go Inside View Range Go Inside View Range - + Workspace Layout... Workspace Layout... - + + Always Show &Menu Bar + Always Show &Menu Bar + + + + Home Menu + Home Menu + + + + Window + Window + + + &Reset Project Time Range &Reset Project Time Range - + Default &Layout Default &Layout - + &Custom Layouts &Custom Layouts - + &Save Current Layout As... &Save Current Layout As... - + &All Panels &All Panels - + None (Left Top) None (Left Top) - + None (Left Bottom) None (Left Bottom) - + None (Right Top) None (Right Top) - + None (Right Bottom) None (Right Bottom) - + None (Top Left) None (Top Left) - + None (Top Right) None (Top Right) - + None (Bottom Left) None (Bottom Left) - + None (Bottom Right) None (Bottom Right) - + &Floating Panels &Floating Panels - + Add &New Panel or Action Add &New Panel or Action - Show &Menu Bar Show &Menu Bar - + Show Too&l Bar Show Too&l Bar - + Show &Left Side Bar Show &Left Side Bar - + Show &Right Side Bar Show &Right Side Bar - + Show &Top Side Bar Show &Top Side Bar - + Show &Bottom Side Bar Show &Bottom Side Bar - + Show &Status Bar Show &Status Bar - + &Next Project Window &Next Project Window - + &Previous Project Window &Previous Project Window - + Pro&ject Windows Pro&ject Windows - + &Help &Help - + &Find Actions... &Find Actions... - + &About DiffScope &About DiffScope - + About &Qt About &Qt - + + Run DSPX Inspector + Run DSPX Inspector + + + Main Menu Main Menu - + Main Tool Bar Left Main Tool Bar Left - + Main Tool Bar Middle Main Tool Bar Middle - + Main Tool Bar Right Main Tool Bar Right - + Main Status Bar Main Status Bar - + + Home Panels + Home Panels + + + Home Navigation Home Navigation - + Home Tool Home Tool - + + Navigation Panels + Navigation Panels + + + Preferences Preferences - Help Help - + Workspace Panel Widgets Workspace Panel Widgets - + Status Text Status Text - + Properties Properties - + Plugins Plugins - Arrangement Arrangement - Mixer Mixer - Piano Roll Piano Roll - + Notifications Notifications - + Tips Tips - + + Metadata + Metadata + + + Digital Clock Digital Clock - + + Tempo Time Signature Indicator + Tempo Time Signature Indicator + + + &File &File - + File Open Actions File Open Actions - + + File Save Actions + File Save Actions + + + Preference Actions Preference Actions - + &Edit &Edit - + Undo Actions Undo Actions - + Generic Generic - + Navigation Navigation - + Move Cu&rsor Move Cu&rsor - + &Move &Move - + &Select &Select - + &Extend Selection &Extend Selection - + Shrin&k Selection Shrin&k Selection - + Timeline Timeline - + Time Indicator Timecode Format Actions Time Indicator Timecode Format Actions - + Timeline Go to Actions Timeline Go to Actions - + Timeline Back Navigation Actions Timeline Back Navigation Actions - + Timeline Forward Navigation Actions Timeline Forward Navigation Actions - + &View &View - + Workspace Actions Workspace Actions - + &Workspace &Workspace - + Workspace Layouts Workspace Layouts - + Opened &Docking Panels Opened &Docking Panels - + Dock Action to Side Bar Dock Action to Side Bar - + Scroll Actions Scroll Actions - + Scroll Scroll - + Scroll by Page Scroll by Page - + Scroll To Scroll To - + View Visibility Actions View Visibility Actions - + &Window &Window - + Project Window Actions Project Window Actions - + About Actions About Actions - + + Dspx Inspector Actions + Dspx Inspector Actions + + + Tool Bar Timeline Navigation Actions Tool Bar Timeline Navigation Actions @@ -1301,121 +1394,126 @@ + Pane separator color + Pane separator color + + + Primary foreground color Primary foreground color - + Secondary foreground color Secondary foreground color - + Link color Link color - + Navigation color Navigation color - + Shadow color Shadow color - + Highlight color Highlight color - + Flat button high contrast border color Flat button high contrast border color - + Control disabled color change Control disabled color change - + Foreground disabled color change Foreground disabled color change - + Control hovered color change Control hovered color change - + Foreground hovered color change Foreground hovered color change - + Control pressed color change Control pressed color change - + Foreground pressed color change Foreground pressed color change - + Control checked color change Control checked color change - + Annotation popup title color change Annotation popup title color change - + Annotation popup content color change Annotation popup content color change - + Docking panel header active color change Docking panel header active color change - + Preset Preset - + Preset Actions Preset Actions - + Save As... Save As... - + Rename... Rename... - + Delete Delete - + Import from File... Import from File... - + Export to File... Export to File... @@ -1453,72 +1551,470 @@ You can also create a custom color theme later in Settings > Appearance > Color Scheme + + Core::ActionWindowInterfaceBase + + + Toggle "%1" + Toggle "%1" + + + + Open Menu "%1"... + Open Menu "%1"... + + Core::CoreInterface - + About %1 About %1 + + Core::DspxInspectorDialog + + + File path + File path + + + + &Browse + &Browse + + + + &Run Check + &Run Check + + + + &Problems + &Problems + + + + DSPX Inspector + DSPX Inspector + + + + Null + Null + + + + Boolean + Boolean + + + + Integer + Integer + + + + Number + Number + + + + String + String + + + + Array + Array + + + + Object + Object + + + + Fatal: Failed to open file + Fatal: Failed to open file + + + + Path + Path + + + + + Error code + Error code + + + + + Error text + Error text + + + + Fatal: Failed to parse JSON + Fatal: Failed to parse JSON + + + + Offset in file + Offset in file + + + + The file is not a valid JSON document. + The file is not a valid JSON document. + + + + Fatal: Root is not an object + Fatal: Root is not an object + + + + The root of JSON document is not an object. + The root of JSON document is not an object. + + + + Fatal: Unrecognized version + Fatal: Unrecognized version + + + + Actual version + Actual version + + + + This project file may have been created with a newer version of %1 or another application, and its version is not recognized by %1. Please try exporting the project from the application with which it was created as a version compatible with your current %1. + This project file may have been created with a newer version of %1 or another application, and its version is not recognized by %1. Please try exporting the project from the application with which it was created as a version compatible with your current %1. + + + + Invalid data type + Invalid data type + + + + Expected data type + Expected data type + + + + , + , + + + + Actual data type + Actual data type + + + + The value at the specific path is not of the expected data type. + The value at the specific path is not of the expected data type. + + + + Invalid object type + Invalid object type + + + + Expected object type + Expected object type + + + + Actual object type + Actual object type + + + + The object at the specific path is not of the expected object type. + The object at the specific path is not of the expected object type. + + + + Range constraint violation + Range constraint violation + + + + Expected maximum value + Expected maximum value + + + + + None + None + + + + Expected minimum value + Expected minimum value + + + + Actual value + Actual value + + + + The value of the property at the specific path is outside the allowed range. The value must be between the expected minimum and maximum value (inclusive). + The value of the property at the specific path is outside the allowed range. The value must be between the expected minimum and maximum value (inclusive). + + + + Expected enum value + Expected enum value + + + + Actual enum value + Actual enum value + + + + Enum constraint violation + Enum constraint violation + + + + The value of the property at the specific path is not one of the allowed enum values. + The value of the property at the specific path is not one of the allowed enum values. + + + + Missing property + Missing property + + + + Missing properties + Missing properties + + + + One or more properties are missing in the object at the specific path. + One or more properties are missing in the object at the specific path. + + + + Redundant property + Redundant property + + + + Redundant properties + Redundant properties + + + + One or more properties are redundant in the object at the specific path. + One or more properties are redundant in the object at the specific path. + + + + Index + Index + + + + Overlapping items + Overlapping items + + + + Items at specific indexes in the array at the specific path overlap. + Items at specific indexes in the array at the specific path overlap. + + + + Zero-length range + Zero-length range + + + + The range length of the entity object at the specific path is zero. + The range length of the entity object at the specific path is zero. + + + + Erroneous clip range + Erroneous clip range + + + + The clipping range of the clip entity object at the specific path exceeds its range limit. + The clipping range of the clip entity object at the specific path exceeds its range limit. + + + + Erroneous clip position + Erroneous clip position + + + + The position of the clip entity object at the specific path exceeds the view range limit. It might be not visible in the viewport. + The position of the clip entity object at the specific path exceeds the view range limit. It might be not visible in the viewport. + + + + Safe range limit exceeded + Safe range limit exceeded + + + + The position of the entity object at the specific path exceeds the safe project length limit (4,000,000 ticks). + The position of the entity object at the specific path exceeds the safe project length limit (4,000,000 ticks). + + + + File Created With Another Application + File Created With Another Application + + + + Editor ID + Editor ID + + + + Editor name + Editor name + + + + This project file was created with another application. Some features may not be fully compatible or may behave differently. + This project file was created with another application. Some features may not be fully compatible or may behave differently. + + + + File Created With Incompatible Version + File Created With Incompatible Version + + + + Version + Version + + + + This project file was created with an newer version or test version of %1. Some features may not be fully compatible or may behave differently. + This project file was created with an newer version or test version of %1. Some features may not be fully compatible or may behave differently. + + + + No problems found + No problems found + + + + The project file is valid and no problems were found. + The project file is valid and no problems were found. + + + + Core::EditTempoTimeSignatureScenario + + + Editing tempo + Editing tempo + + + + Editing time signature + Editing time signature + + + + Core::Internal::AfterSavingNotifyAddOn + + + The file has been saved to %1 + The file has been saved to %1 + + Core::Internal::AppearancePage - + Appearance Appearance - + Configure how %1 looks like Configure how %1 looks like + + Core::Internal::CloseSaveCheckAddOn + + + Do you want to save before closing? + Do you want to save before closing? + + + + If you choose not to save, a copy of the current project file will be created to help recover your work in case of accidental incorrect operation. + If you choose not to save, a copy of the current project file will be created to help recover your work in case of accidental incorrect operation. + + Core::Internal::ColorSchemeCollection - + Scopic Dark Scopic Dark - + Scopic Light Scopic Light - + Scopic High Contrast Scopic High Contrast - + Failed to Import Preset Failed to Import Preset - - + + Unable to open file "%1" Unable to open file "%1" - + Failed to import preset Failed to import preset - + Invalid format in file "%1" Invalid format in file "%1" - + Failed to export preset Failed to export preset - + (Unsaved preset) (Unsaved preset) @@ -1526,112 +2022,112 @@ Core::Internal::ColorSchemePage - + Color Scheme Color Scheme - + Configure the colors and visual effects of various components Configure the colors and visual effects of various components - + Alpha: %L1 Alpha: %L1 - + Saturation (HSV): %L1 Saturation (HSV): %L1 - + Value: %L1 Value: %L1 - + Saturation (HSL): %L1 Saturation (HSL): %L1 - + Lightness: %L1 Lightness: %L1 - + QColor::lighter(): %L1 QColor::lighter(): %L1 - + Top Blend: %1 Top Blend: %1 - + Bottom Blend: %1 Bottom Blend: %1 - + Syntax error at line %L1: Missing colon in declaration Syntax error at line %L1: Missing colon in declaration - + Syntax error at line %L1: Empty property name Syntax error at line %L1: Empty property name - + Syntax error at line %L1: Empty property value Syntax error at line %L1: Empty property value - + Syntax error at line %L1: Invalid 'alpha' value Syntax error at line %L1: Invalid 'alpha' value - + Syntax error at line %L1: Invalid 'saturation' value Syntax error at line %L1: Invalid 'saturation' value - + Syntax error at line %L1: Invalid 'value' value Syntax error at line %L1: Invalid 'value' value - + Syntax error at line %L1: Invalid 'hsl-saturation' value Syntax error at line %L1: Invalid 'hsl-saturation' value - + Syntax error at line %L1: Invalid 'lightness' value Syntax error at line %L1: Invalid 'lightness' value - + Syntax error at line %L1: Invalid 'lighter' value (must be a positive integer) Syntax error at line %L1: Invalid 'lighter' value (must be a positive integer) - + Syntax error at line %L1: Invalid color value for 'top-blend' Syntax error at line %L1: Invalid color value for 'top-blend' - + Syntax error at line %L1: Invalid color value for 'bottom-blend' Syntax error at line %L1: Invalid color value for 'bottom-blend' - + Syntax error at line %L1: Unknown property '%2' Syntax error at line %L1: Unknown property '%2' @@ -1639,20 +2135,33 @@ Core::Internal::CorePlugin - + Initializing core plugin... Initializing core plugin... - + Initializing GUI... Initializing GUI... + + Core::Internal::FileBackupPage + + + File and Backup + File and Backup + + + + Configure file handling and backup behaviors of %1 + Configure file handling and backup behaviors of %1 + + Core::Internal::FindActionsAddOn - + Find actions Find actions @@ -1660,17 +2169,22 @@ Core::Internal::FindActionsModel - + Toggle "%1" Toggle "%1" - + + Open Menu "%1"... + Open Menu "%1"... + + + %1: %2 %1: %2 - + recently used recently used @@ -1678,22 +2192,22 @@ Core::Internal::GeneralPage - + General General - + Configure general behaviors of %1 Configure general behaviors of %1 - + Restart %1 %1 を再起動 - + Restart %1 to apply language changes? 言語の変更を適用するには %1 を再起動しますか? @@ -1701,12 +2215,12 @@ Core::Internal::KeyMapPage - + Keymap Keymap - + Configure shortcuts of actions Configure shortcuts of actions @@ -1714,12 +2228,12 @@ Core::Internal::LogPage - + Log Log - + Configure log output and archiving behaviors Configure log output and archiving behaviors @@ -1727,12 +2241,12 @@ Core::Internal::MenuPage - + Menus and Toolbars Menus and Toolbars - + Configure the layout of menus and toolbars Configure the layout of menus and toolbars @@ -1740,7 +2254,7 @@ Core::Internal::NotificationAddOn - + %1 (+%Ln notification(s)) %1 (+%Ln notification(s)) @@ -1750,30 +2264,38 @@ Core::Internal::ProjectStartupTimerAddOn - + Initializing project window... Initializing project window... - + Project window initialized in %1 seconds Project window initialized in %1 seconds - + Project window initialized Project window initialized + + Core::Internal::RecentFileAddOn + + + <i>File moved or deleted</i> + <i>File moved or deleted</i> + + Core::Internal::TimeIndicatorPage - + Time Indicator Time Indicator - + Configure time indicator display and interaction behaviors Configure time indicator display and interaction behaviors @@ -1781,17 +2303,17 @@ Core::Internal::TimelineAddOn - + measure %L1, beat %L2 measure %L1, beat %L2 - + measure %L1, beat %L2, tick %L3 measure %L1, beat %L2, tick %L3 - + %Ln minute(s) absolute time @@ -1799,7 +2321,7 @@ - + %Ln second(s) absolute time @@ -1807,7 +2329,7 @@ - + %Ln millisecond(s) absolute time @@ -1815,26 +2337,26 @@ - - + + %1 %2 absolute minute second %1 %2 - + %1 %2 absolute second millisecond %1 %2 - + %1 %2 %3 absolute minute second millisecond %1 %2 %3 - + %Ln minute(s) relative time @@ -1842,7 +2364,7 @@ - + %Ln second(s) relative time @@ -1850,7 +2372,7 @@ - + %Ln millisecond(s) relative time @@ -1858,127 +2380,127 @@ - - + + %1 %2 relative minute second %1 %2 - + %1 %2 relative second millisecond %1 %2 - + %1 %2 %3 relative minute second millisecond %1 %2 %3 - - - - - - - - - + + + + + + + + + Go to %1 Go to %1 - - + + absolute time... absolute time... - + %1 (%2) %1 (%2) - + Move backward by %1 Move backward by %1 - + Move forward by %1 Move forward by %1 - + %1 (to %2) %1 (to %2) - - - - + + + + The time offset exceeds the boundary and has been adjusted to zero The time offset exceeds the boundary and has been adjusted to zero - + the end of project (%1) the end of project (%1) - + previous measure (%1) previous measure (%1) - + previous beat (%1) previous beat (%1) - + previous tick (%1) previous tick (%1) - + next measure (%1) next measure (%1) - + next beat (%1) next beat (%1) - + next tick (%1) next tick (%1) - - + + Type "?" to view tips Type "?" to view tips - - + + Invalid format Invalid format - + Jump to Jump to - + Input should not be empty Input should not be empty @@ -1986,12 +2508,10 @@ The time offset exceeds the boundary and has been adjusted to zero Core::Internal::ViewVisibilityAddOn - Please take attention Please take attention - After hiding the menu bar, it can be difficult to show it again. Make sure you know how to do this. Continue? @@ -2003,119 +2523,312 @@ Continue? Core::Internal::WorkspaceAddOn - + Save Current Layout As... Save Current Layout As... - + Default Layout Default Layout - + custom layout custom layout - + Workspace layout actions Workspace layout actions - + Apply Apply - + Rename Rename - + Delete Delete - + Custom layout "%1" actions Custom layout "%1" actions + + Core::OpenSaveProjectFileScenario + + + DiffScope Project Exchange Format (*.dspx) + DiffScope Project Exchange Format (*.dspx) + + + + All Files (*) + All Files (*) + + + + Failed to open file + Failed to open file + + + + Failed to save file + Failed to save file + + + + Open DSPX Inspector + Open DSPX Inspector + + + + Failed to parse file content + Failed to parse file content + + + + %1 + +You can check for problems in the file with DSPX Inspector. + %1 + +You can check for problems in the file with DSPX Inspector. + + + + File created with another application + File created with another application + + + + name unknown + name unknown + + + + File created with incompatible %1 version + File created with incompatible %1 version + + + + Additional check failed + Additional check failed + + + + Core::ProjectWindowInterface + + + File Modified Externally + File Modified Externally + + + + The file has been modified by another program since it was last saved. + +Do you want to save as a new file or overwrite it? + The file has been modified by another program since it was last saved. + +Do you want to save as a new file or overwrite it? + + + + Save As... + Save As... + + + + Overwrite + Overwrite + + + + + Untitled + Untitled + + + + EditTempoDialog + + + Edit Tempo + Edit Tempo + + + + Tempo + Tempo + + + + Position + Position + + + + Modify existing one + Modify existing one + + + + Insert new one + Insert new one + + + + EditTimeSignatureDialog + + + Edit Time Signature + Edit Time Signature + + + + Time signature + Time signature + + + + Numerator + Numerator + + + + Denominator + Denominator + + + + Position + Position + + + + Modify existing one + Modify existing one + + + + Insert new one + Insert new one + + + + FileBackupPage + + + File + File + + + + Lock opened files + Lock opened files + + + + Locking an open file prevents it from being modified by other programs. Change to this option will take effect only for projects opened after the change + Locking an open file prevents it from being modified by other programs. Change to this option will take effect only for projects opened after the change + + + + Check for external modifications when saving a file + Check for external modifications when saving a file + + GeneralPage - + Startup Startup - + When starting %1 When starting %1 - + Open the home window Open the home window - + Create a new project Create a new project - Open previous projects on startup automatically Open previous projects on startup automatically - + Close the home window after opening a project Close the home window after opening a project - - + + Language Language - + Use system language Use system language - + (Restart required) (Restart required) - + Notification Notification - + Play sound alert when a notification bubble is sent Play sound alert when a notification bubble is sent - + Timeout for auto hiding notification bubbles Timeout for auto hiding notification bubbles - + milliseconds milliseconds - + Reset All "Do Not Show Again" Reset All "Do Not Show Again" + + + Window + Window + + + + Memorize window position and size + Memorize window position and size + Find Actions @@ -2221,6 +2934,35 @@ Continue? Check for Updates + + LoadingFailedFallbackPanel + + + + Failed to load component + Failed to load component + + + + This component cannot be loaded because it is not registered to the application. The plugin providing this component might be disabled or no longer available. + This component cannot be loaded because it is not registered to the application. The plugin providing this component might be disabled or no longer available. + + + + An error occurred while loading this component. + An error occurred while loading this component. + + + + Component identifier + Component identifier + + + + Error + Error + + LogPage @@ -2383,6 +3125,44 @@ Continue? Search + + MetadataPanel + + + Edit + Edit + + + + Path + Path + + + + Unspecified + Unspecified + + + + Reveal in %1 + Reveal in %1 + + + + Title + Title + + + + Author + Author + + + + Cent Shift + Cent Shift + + NotificationsPanel @@ -2391,7 +3171,7 @@ Continue? Clear All - + No notification No notification @@ -2399,7 +3179,7 @@ Continue? PluginDialog - + Plugins Plugins @@ -2407,11 +3187,94 @@ Continue? ProjectActions - + Click to show details Click to show details + + ProjectWindow + + + Untitled + Untitled + + + + Modified Externally + Modified Externally + + + + Unsaved + Unsaved + + + + RecentFilesPanel + + + + + + Search + Search + + + + + Grid view + Grid view + + + + + List view + List view + + + + Clear Recovery Files + Clear Recovery Files + + + + Clear Recent Files + Clear Recent Files + + + + No recovery file +If %1 exits abnormally, automatic recovery files will be displayed here. + No recovery file +If %1 exits abnormally, automatic recovery files will be displayed here. + + + + No recent files + No recent files + + + + Open + Open + + + + Reveal in %1 + Reveal in %1 + + + + Remove from "Recovery Files" + Remove from "Recovery Files" + + + + Remove from "Recent Files" + Remove from "Recent Files" + + TimeIndicatorPage @@ -2488,6 +3351,29 @@ Continue? Slide to adjust current project time + + UndoAddOnActions + + + Undo %1 + Undo %1 + + + + Undo + Undo + + + + Redo %1 + Redo %1 + + + + Redo + Redo + + WorkspaceAddOnActions @@ -2508,62 +3394,62 @@ Continue? - + None None - + %1 (Left Top) %1 (Left Top) - + %1 (Left Bottom) %1 (Left Bottom) - + %1 (Right Top) %1 (Right Top) - + %1 (Right Bottom) %1 (Right Bottom) - + %1 (Top Left) %1 (Top Left) - + %1 (Top Right) %1 (Top Right) - + %1 (Bottom Left) %1 (Bottom Left) - + %1 (Bottom Right) %1 (Bottom Right) - + Add Action... Add Action... - + Error Error - + Failed to create panel "%1" Failed to create panel "%1" @@ -2571,52 +3457,52 @@ Continue? WorkspaceAddOnHelper - + The current workspace does not contain any panels The current workspace does not contain any panels - + Workspace data might be erroneous. You can try restoring the default workspace. Workspace data might be erroneous. You can try restoring the default workspace. - + Restore Default Workspace Restore Default Workspace - + Custom layout name Custom layout name - + Name should not be empty Name should not be empty - + New name should not be the same as old name New name should not be the same as old name - + Custom presets with the same name will be overwritten Custom presets with the same name will be overwritten - + Delete Delete - + Delete layout "%1"? Delete layout "%1"? - + Drag to the sidebar to add "%1" Drag to the sidebar to add "%1" diff --git a/src/plugins/coreplugin/res/translations/org.diffscope.core_zh_CN.ts b/src/plugins/coreplugin/res/translations/org.diffscope.core_zh_CN.ts index fbd19307..ea96f436 100644 --- a/src/plugins/coreplugin/res/translations/org.diffscope.core_zh_CN.ts +++ b/src/plugins/coreplugin/res/translations/org.diffscope.core_zh_CN.ts @@ -84,195 +84,219 @@ Application <p>A professional singing-voice-synthesis editor powered by DiffSinger</p><p>Visit <a href="https://diffscope.org/">diffscope.org</a> for more information.</p> - <p>基于 DiffSinger 的专业歌声合成编辑器</p><p>访问 <a href="https://diffscope.org/">diffscope.org</a> 获取更多信息。</p> + <p>A professional singing-voice-synthesis editor powered by DiffSinger</p><p>Visit <a href="https://diffscope.org/">diffscope.org</a> for more information.</p> <p>Based on Qt version %1.<br>Copyright © 2019-%2 Team OpenVPI. All rights reserved.</p> - <p>基于 Qt 版本 %1。<br>版权所有 © 2019-%2 Team OpenVPI。保留所有权利。</p> + <p>Based on Qt version %1.<br>Copyright © 2019-%2 Team OpenVPI. All rights reserved.</p> - <p>A professional singing-voice-synthesis editor powered by DiffSinger</p><p>Version %1</p><p>Copyright © %2-%3 Team OpenVPI. All rights reserved.</p><p>Visit <a href="%4">%4</a> for more information.</p> <p>基于 DiffSinger 的专业歌声合成编辑器</p><p>版本 %1</p><p>版权所有 © %2-%3 Team OpenVPI。 保留所有权利。</p><p>访问 <a href="%4">%4</a> 获取更多信息。</p> - <h3>License</h3><p>Licensed under the Apache License, Version 2.0.<br>You may obtain a copy of the License at %1.</p><p>This application is distributed <b>AS IS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND</b>, either express or implied.</p> <h3>许可协议</h3><p>根据 Apache License, Version 2.0 授权。<br>你可以在 %1 获取许可协议副本。</p><p>本应用程序以<b>原样分发,不提供任何明示或暗示的担保或条件</b>。</p> - + + <p>A professional singing-voice-synthesis editor powered by DiffSinger</p><p>Version %1</p><p>Copyright © %2-%3 %4. All rights reserved.</p><p>Visit <a href="%5">%5</a> for more information.</p> + <p>基于 DiffSinger 的专业歌声合成编辑器</p><p>版本 %1</p><p>版权所有 © %2-%3 %4。保留所有权利。</p><p>访问 <a href="%5">%5</a> 获取更多信息。</p> + + + + <h3>License</h3><p>Licensed under the Apache License, Version 2.0.<br>You may obtain a copy of the License at %1.</p><p>This application is distributed <b>AS IS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND</b>, either express or implied.</p><p>This license does not apply to plugins. Please refer to Plugins to view the licenses applicable to each individual plugin.</p> + <h3>许可协议</h3><p>根据 Apache License, Version 2.0 授权。<br>你可以在 %1 获取许可协议副本。</p><p>本应用程序以<b>原样分发,不提供任何明示或暗示的担保或条件</b>。</p><p>此许可协议不适用于插件。请参阅“插件”以查看适用于每个插件的许可协议。</p> + + + <h3>Build Information</h3><p>Branch: %1<br>Commit: %2<br>Build date: %3<br>Toolchain: %4 %5 %6</p> <h3>构建信息</h3><p>分支:%1<br>提交:%2<br>构建日期:%3<br>工具链:%4 %5 %6</p> <h3>Build Information</h3><p>Version: %1<br>Branch: %2<br>Commit: %3<br>Build date: %4<br>Toolchain: %5 %6 %7</p> - <h3>构建信息</h3><p>版本:%1<br>分支:%2<br>提交:%3<br>构建日期:%4<br>工具链:%5 %6 %7</p> + <h3>Build Information</h3><p>Version: %1<br>Branch: %2<br>Commit: %3<br>Build date: %4<br>Toolchain: %5 %6 %7</p> + + + + Version %1 + 版本 %1 + + + + Copyright © %1-%2 %3. All rights reserved. + 版权所有 © %1-%2 %3。保留所有权利。 Application::ActionClass - - - + + + + + + + File 文件 - - + + Preferences 首选项 - - - - + + + + Window 窗口 - + Application 应用程序 - - - - + + + + + Home 主页 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Edit 编辑 - - + + Time Indicator 时间指示器 - - - - - - - - - - - - + + + + + + + + + + + + Timeline 时间轴 - - + + Core 核心 - - - - - - - + + + + + + + Panel 面板 - + + Widget 小组件 - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + View 视图 - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + Workspace 工作区 - - - - + + + + + Help 帮助 @@ -280,157 +304,162 @@ Application::ActionDescription - + Create a project in a new window 在新窗口中创建工程 - + + Create a project from template in a new window + 在新窗口中从模板创建工程 + + + Open an existing project in a new window 在新窗口中打开现有项目 - + Open application settings 打开应用程序设置 - + Open plugin configuration dialog 打开插件配置对话框 - + Switch to home window if it is opened, or else open home window 如果主窗口已打开则切换到主窗口,否则打开主窗口 - + Quit DiffScope 退出 DiffScope - + Toggle "Recent Files" page 切换“最近文件”页面 - + Toggle "Recovery Files" page 切换“恢复文件”页面 - + Toggle grid view in "Recent Files" page 在“最近文件”页面中切换网格视图 - + Toggle list view in "Recent Files" page 在“最近文件”页面中切换列表视图 - + Show music time (measure:beat:tick) on the time indicator 在时间指示器上显示音乐时间(小节:拍:刻) - + Show absolute time (minute:second.millisecond) on the time indicator 在时间指示器上显示绝对时间(分:秒:毫秒) - + Navigate to a specific position 导航到指定位置 - + Quickly navigate to a specific position 快速导航到指定位置 - + Navigate to the start of current project 导航到当前工程的开始 - + Navigate to the end of current project 导航到当前工程的结束 - + Reset project end time based on project content 基于工程内容重置工程结束时间 - + Apply the default workspace layout 应用默认工作区布局 - + Show or hide the main menu bar 显示或隐藏主菜单栏 - + Show or hide the main tool bar 显示或隐藏主工具栏 - + Show or hide the left side bar 显示或隐藏左侧边栏 - + Show or hide the right side bar 显示或隐藏右侧边栏 - + Show or hide the top side bar 显示或隐藏顶部边栏 - + Show or hide the bottom side bar 显示或隐藏底部边栏 - + Show or hide the status bar 显示或隐藏状态栏 - + Switch to the next project window 切换到下一个工程窗口 - + Switch to the previous project window 切换到上一个工程窗口 - + Show help contents 显示帮助内容 - + Find and trigger action by name 使用名称查找并触发操作 - + Show information about DiffScope 显示关于 DiffScope 的信息 - + Show information about Qt 显示关于 Qt 的信息 @@ -438,747 +467,811 @@ Application::ActionText - + &New 新建(&N) - + + New from Template... + 从模板新建... + + + &Open... 打开(&O)... - + + Open &Recent File + 打开最近的文件(&R) + + + &Save 保存(&S) - + + Save &As... + 另存为(&A)... + + + + Save Copy As... + 保存副本为... + + + Se&ttings... 设置(&T)... - + Pl&ugins... 插件(&P)... - + Show &Home Window 显示主窗口(&H) - + E&xit 退出(&X) - + Recent Files 最近文件 - + Recovery Files 恢复文件 - + Grid View 网格视图 - + List View 列表视图 - + &Undo 撤销(&U) - + &Redo 重做(&R) - + Cu&t 剪切(&T) - + &Copy 复制(&C) - + &Paste 粘贴(&P) - Paste Special... 选择性粘贴... - + &Delete 删除(&D) - + Select &All 全选(&A) - + Dese&lect 取消全选(&L) - + Select Current 选择当前 - + Select &Up 向上选择(&U) - + Select &Down 向下选择(&D) - + Select &Left 向左选择(&L) - + Select &Right 向右选择(&R) - + Move &Up 上移(&U) - + Move &Down 下移(&D) - + Move &Left 左移(&L) - + Move &Right 右移(&R) - + &Move... 移动(&M)... - + Scroll &Up 向上滚动(&U) - + Scroll &Down 向下滚动(&U) - + Scroll &Left 向左滚动(&U) - + Scroll &Right 向右滚动(&U) - + Move Cursor &Up 上移光标(&U) - + Move Cursor &Down 下移光标(&D) - + Move Cursor &Left 左移光标(&L) - + Move Cursor &Right 右移光标(&R) - + Extend Selection &Up 向上扩展选区(&U) - + Extend Selection &Down 向下扩展选区(&D) - + Extend Selection &Left 向左扩展选区(&L) - + Extend Selection &Right 向右扩展选区(&R) - + Shrink Selection &Up 向上收缩选区(&U) - + Shrink Selection &Down 向下收缩选区(&D) - + Shrink Selection &Left 向左收缩选区(&L) - + Shrink Selection &Right 向右收缩选区(&R) - + Page &Up 向上翻页(&U) - + Page &Down 向下翻页(&D) - + Page &Left 向左翻页(&L) - + Page &Right 向右翻页(&R) - + Scroll to &Top 滚动到顶部(&T) - + Scroll to &Bottom 滚动到底部(&B) - + Scroll to &Start 滚动到开始(&S) - + Scroll to &End 滚动到结束(&E) - + Scroll to Current Time 滚动到当前时间 - + Show &Music Time 显示音乐时间(&M) - + Show &Absolute Time 显示绝对时间(&A) - + &Go To... 跳转到(&G)... - + &Quick Jump... 快速跳转(&Q)... - + Go to &Start 转到开始(&S) - + Go to Previous Measure 转到上一小节 - + Go to Previous Beat 转到上一拍 - + Go to Previous Tick 转到上一刻 - + Go to &End 转到结束(&E) - + Go to Next Measure 转到下一小节 - + Go to Next Beat 转到下一拍 - + Go to Next Tick 转到下一刻 - + Go Inside View Range 转到视图区域内 - + Workspace Layout... 工作区布局... - + + Always Show &Menu Bar + 始终显示菜单栏(&M) + + + + Home Menu + 主窗口菜单 + + + + Window + 窗口 + + + &Reset Project Time Range 重置项目时间范围(&R) - + Default &Layout 默认布局(&L) - + &Custom Layouts 自定义布局(&C) - + &Save Current Layout As... 另存为当前布局(&S)... - + &All Panels 所有面板(&A) - + None (Left Top) 无(左上) - + None (Left Bottom) 无(左下) - + None (Right Top) 无(右上) - + None (Right Bottom) 无(右下) - + None (Top Left) 无(上左) - + None (Top Right) 无(上右) - + None (Bottom Left) 无(下左) - + None (Bottom Right) 无(下右) - + &Floating Panels 浮动面板(&F) - + Add &New Panel or Action 添加新面板或操作(&N) - Show &Menu Bar 显示菜单栏(&M) - + Show Too&l Bar 显示工具栏(&L) - + Show &Left Side Bar 显示左侧边栏(&L) - + Show &Right Side Bar 显示右侧边栏(&R) - + Show &Top Side Bar 显示顶部边栏(&T) - + Show &Bottom Side Bar 显示底部边栏(&B) - + Show &Status Bar 显示状态栏(&S) - + &Next Project Window 下一个工程窗口(&N) - + &Previous Project Window 上一个工程窗口(&P) - + Pro&ject Windows 工程窗口(&J) - + &Help 帮助(&H) - + &Find Actions... 查找操作(&F)... - + &About DiffScope 关于 DiffScope(&A) - + About &Qt 关于 &Qt - + + Run DSPX Inspector + 运行 DSPX 检查器 + + + Main Menu 主菜单 - + Main Tool Bar Left 主工具栏左 - + Main Tool Bar Middle 主工具栏中 - + Main Tool Bar Right 主工具栏右 - + Main Status Bar 主状态栏 - + + Home Panels + 主窗口面板 + + + Home Navigation - 主页导航 + 主窗口导航 - + Home Tool - 主页工具 + 主窗口工具 - + + Navigation Panels + 导航面板 + + + Preferences 首选项 - Help 帮助 - + Workspace Panel Widgets 工作区面板小部件 - + Status Text 状态文本 - + Properties 属性 - + Plugins 插件 - Arrangement 编曲 - Mixer 混音器 - Piano Roll 钢琴卷帘 - + Notifications 通知 - + Tips 提示 - + + Metadata + 元数据 + + + Digital Clock 数字时钟 - + + Tempo Time Signature Indicator + 曲速拍号指示器 + + + &File 文件(&F) - + File Open Actions 文件打开操作 - + + File Save Actions + 文件保存操作 + + + Preference Actions 首选项操作 - + &Edit 编辑(&E) - + Undo Actions 撤销操作 - + Generic 通用的 - + Navigation 导航 - + Move Cu&rsor 移动光标(&R) - + &Move 移动(&M) - + &Select 选择(&S) - + &Extend Selection 扩展选区(&E) - + Shrin&k Selection 收缩选区(&K) - + Timeline 时间轴 - + Time Indicator Timecode Format Actions 时间指示器时间码格式操作 - + Timeline Go to Actions 时间轴跳转操作 - + Timeline Back Navigation Actions 时间轴后退导航操作 - + Timeline Forward Navigation Actions 时间轴前进导航操作 - + &View 视图(&V) - + Workspace Actions 工作区操作 - + &Workspace 工作区(&W) - + Workspace Layouts 工作区布局 - + Opened &Docking Panels 已打开的停靠面板(&D) - + Dock Action to Side Bar 停靠操作到侧边栏 - + Scroll Actions 滚动操作 - + Scroll 滚动 - + Scroll by Page 按页滚动 - + Scroll To 滚到到 - + View Visibility Actions 视图可见性操作 - + &Window 窗口(&W) - + Project Window Actions 工程窗口操作 - + About Actions 关于操作 - + + Dspx Inspector Actions + DSPX 检查器操作 + + + Tool Bar Timeline Navigation Actions 工具栏时间轴导航操作 @@ -1301,121 +1394,126 @@ + Pane separator color + 窗格分割线颜色 + + + Primary foreground color 主前景色 - + Secondary foreground color 次前景色 - + Link color 链接颜色 - + Navigation color 导航颜色 - + Shadow color 阴影颜色 - + Highlight color 高亮颜色 - + Flat button high contrast border color 扁平按钮高对比度边框颜色 - + Control disabled color change 控件禁用时颜色变化 - + Foreground disabled color change 前景禁用时颜色变化 - + Control hovered color change 控件悬停时颜色变化 - + Foreground hovered color change 前景悬停时颜色变化 - + Control pressed color change 控件按下时颜色变化 - + Foreground pressed color change 前景按下时颜色变化 - + Control checked color change 控件选中时颜色变化 - + Annotation popup title color change 注释弹窗标题颜色变化 - + Annotation popup content color change 注释弹窗内容颜色变化 - + Docking panel header active color change 停靠面板标题激活时颜色变化 - + Preset 预设 - + Preset Actions 预设操作 - + Save As... 另存为... - + Rename... 重命名... - + Delete 删除 - + Import from File... 从文件导入... - + Export to File... 导出到文件... @@ -1453,72 +1551,470 @@ 稍后你还可以在 设置 > 外观 > 配色方案 中创建自定义颜色主题 + + Core::ActionWindowInterfaceBase + + + Toggle "%1" + 切换“%1” + + + + Open Menu "%1"... + 打开菜单“%1”... + + Core::CoreInterface - + About %1 关于 %1 + + Core::DspxInspectorDialog + + + File path + 文件路径 + + + + &Browse + 浏览(&B) + + + + &Run Check + 运行检查(&R) + + + + &Problems + 问题(&P) + + + + DSPX Inspector + DSPX 检查器 + + + + Null + 空值 + + + + Boolean + 布尔值 + + + + Integer + 整数 + + + + Number + 数值 + + + + String + 字符串 + + + + Array + 数组 + + + + Object + 对象 + + + + Fatal: Failed to open file + 严重错误:打开文件失败 + + + + Path + 路径 + + + + + Error code + 错误码 + + + + + Error text + 错误文本 + + + + Fatal: Failed to parse JSON + 严重错误:解析 JSON 失败 + + + + Offset in file + 在文件中的偏移 + + + + The file is not a valid JSON document. + 此文件不是有效的 JSON 文档。 + + + + Fatal: Root is not an object + 严重错误:根元素不是对象 + + + + The root of JSON document is not an object. + JSON 文档的根元素不是对象。 + + + + Fatal: Unrecognized version + 严重错误:无法识别版本 + + + + Actual version + 实际版本 + + + + This project file may have been created with a newer version of %1 or another application, and its version is not recognized by %1. Please try exporting the project from the application with which it was created as a version compatible with your current %1. + 该工程文件可能是使用更高版本的 %1 或其他应用程序创建的,而 %1 无法识别其版本。请尝试从创建该工程的应用程序中导出与当前 %1 兼容的版本。 + + + + Invalid data type + 无效的数据类型 + + + + Expected data type + 预期数据类型 + + + + , + + + + + Actual data type + 实际数据类型 + + + + The value at the specific path is not of the expected data type. + 位于指定路径的值不是预期的数据类型。 + + + + Invalid object type + 无效的对象类型 + + + + Expected object type + 预期对象类型 + + + + Actual object type + 实际对象类型 + + + + The object at the specific path is not of the expected object type. + 位于指定路径的对象不是预期的数据类型。 + + + + Range constraint violation + 范围约束违反 + + + + Expected maximum value + 预期最大值 + + + + + None + + + + + Expected minimum value + 预期最小值 + + + + Actual value + 实际值 + + + + The value of the property at the specific path is outside the allowed range. The value must be between the expected minimum and maximum value (inclusive). + 位于指定路径的属性值超出了允许的范围。该值必须介于预期最小值和最大值之间(包含最小值和最大值)。 + + + + Expected enum value + 预期枚举值 + + + + Actual enum value + 实际枚举值 + + + + Enum constraint violation + 枚举约束违反 + + + + The value of the property at the specific path is not one of the allowed enum values. + 位于指定路径的属性值不是允许的枚举值之一。 + + + + Missing property + 缺少属性 + + + + Missing properties + 缺少属性 + + + + One or more properties are missing in the object at the specific path. + 位于指定路径的对象缺少一个或多个属性。 + + + + Redundant property + 冗余属性 + + + + Redundant properties + 冗余属性 + + + + One or more properties are redundant in the object at the specific path. + 位于指定路径的对象中存在一个或多个冗余属性。 + + + + Index + 索引 + + + + Overlapping items + 重叠的项目 + + + + Items at specific indexes in the array at the specific path overlap. + 数组中位于指定路径的特定索引处的项目重叠。 + + + + Zero-length range + 零长度区间 + + + + The range length of the entity object at the specific path is zero. + 位于指定路径的实体对象的区间长度为零。 + + + + Erroneous clip range + 剪辑范围错误 + + + + The clipping range of the clip entity object at the specific path exceeds its range limit. + 位于指定路径的剪辑实体对象的裁剪范围超过了其范围限制。 + + + + Erroneous clip position + 剪辑位置错误 + + + + The position of the clip entity object at the specific path exceeds the view range limit. It might be not visible in the viewport. + 位于指定路径的剪辑实体对象的位置超出了视图范围限制,可能在视口中不可见。 + + + + Safe range limit exceeded + 超出安全范围限制 + + + + The position of the entity object at the specific path exceeds the safe project length limit (4,000,000 ticks). + 位于指定路径的实体对象超出了安全工程长度限制(4,000,000 刻)。 + + + + File Created With Another Application + 使用其他应用程序创建的文件 + + + + Editor ID + 编辑器标识符 + + + + Editor name + 编辑器名称 + + + + This project file was created with another application. Some features may not be fully compatible or may behave differently. + 此工程文件由其他应用程序创建。某些功能可能不完全兼容或运行方式有所不同。 + + + + File Created With Incompatible Version + 使用不兼容的版本创建的文件 + + + + Version + 版本 + + + + This project file was created with an newer version or test version of %1. Some features may not be fully compatible or may behave differently. + 此工程文件是用 %1 的较新版本或测试版本创建的。某些功能可能不完全兼容或行为有所不同。 + + + + No problems found + 未发现问题 + + + + The project file is valid and no problems were found. + 工程文件有效,未发现任何问题。 + + + + Core::EditTempoTimeSignatureScenario + + + Editing tempo + 编辑曲速 + + + + Editing time signature + 编辑拍号 + + + + Core::Internal::AfterSavingNotifyAddOn + + + The file has been saved to %1 + 文件已保存至 %1 + + Core::Internal::AppearancePage - + Appearance 外观 - + Configure how %1 looks like 配置 %1 的外观 + + Core::Internal::CloseSaveCheckAddOn + + + Do you want to save before closing? + 你想在关闭前保存吗? + + + + If you choose not to save, a copy of the current project file will be created to help recover your work in case of accidental incorrect operation. + 如果你选择不保存,当前项目文件的副本将会被创建,以便在意外误操作时帮助你恢复工作。 + + Core::Internal::ColorSchemeCollection - + Scopic Dark Scopic 暗色 - + Scopic Light Scopic 亮色 - + Scopic High Contrast Scopic 高对比度 - + Failed to Import Preset 预设导入失败 - - + + Unable to open file "%1" 无法打开文件“%1” - + Failed to import preset 预设导入失败 - + Invalid format in file "%1" 文件“%1”格式无效 - + Failed to export preset 预设导出失败 - + (Unsaved preset) (未保存的预设) @@ -1526,112 +2022,112 @@ Core::Internal::ColorSchemePage - + Color Scheme 配色方案 - + Configure the colors and visual effects of various components 配置各组件的颜色和视觉效果 - + Alpha: %L1 透明度:%L1 - + Saturation (HSV): %L1 饱和度(HSV):%L1 - + Value: %L1 明度:%L1 - + Saturation (HSL): %L1 饱和度(HSL):%L1 - + Lightness: %L1 亮度:%L1 - + QColor::lighter(): %L1 QColor::lighter():%L1 - + Top Blend: %1 顶部混合:%1 - + Bottom Blend: %1 底部混合:%1 - + Syntax error at line %L1: Missing colon in declaration 第 %L1 行语法错误:声明缺少冒号 - + Syntax error at line %L1: Empty property name 第 %L1 行语法错误:属性名为空 - + Syntax error at line %L1: Empty property value 第 %L1 行语法错误:属性值为空 - + Syntax error at line %L1: Invalid 'alpha' value 第 %L1 行语法错误:“alpha”值无效 - + Syntax error at line %L1: Invalid 'saturation' value 第 %L1 行语法错误:“saturation”值无效 - + Syntax error at line %L1: Invalid 'value' value 第 %L1 行语法错误:“value”值无效 - + Syntax error at line %L1: Invalid 'hsl-saturation' value 第 %L1 行语法错误:“hsl-saturation”值无效 - + Syntax error at line %L1: Invalid 'lightness' value 第 %L1 行语法错误:“lightness”值无效 - + Syntax error at line %L1: Invalid 'lighter' value (must be a positive integer) 第 %L1 行语法错误:“lighter”值无效(必须为正整数) - + Syntax error at line %L1: Invalid color value for 'top-blend' 第 %L1 行语法错误:“top-blend”颜色值无效 - + Syntax error at line %L1: Invalid color value for 'bottom-blend' 第 %L1 行语法错误:“bottom-blend”颜色值无效 - + Syntax error at line %L1: Unknown property '%2' 第 %L1 行语法错误:未知属性“%2” @@ -1639,20 +2135,33 @@ Core::Internal::CorePlugin - + Initializing core plugin... 正在初始化核心插件... - + Initializing GUI... 正在初始化图形界面... + + Core::Internal::FileBackupPage + + + File and Backup + 文件和备份 + + + + Configure file handling and backup behaviors of %1 + 配置 %1 的文件处理和备份行为 + + Core::Internal::FindActionsAddOn - + Find actions 查找操作 @@ -1660,17 +2169,22 @@ Core::Internal::FindActionsModel - + Toggle "%1" 切换“%1” - + + Open Menu "%1"... + 打开菜单“%1”... + + + %1: %2 %1:%2 - + recently used 最近使用 @@ -1678,22 +2192,22 @@ Core::Internal::GeneralPage - + General 通用 - + Configure general behaviors of %1 配置 %1 的通用行为 - + Restart %1 重新启动 %1 - + Restart %1 to apply language changes? 重新启动 %1 以应用语言更改? @@ -1701,12 +2215,12 @@ Core::Internal::KeyMapPage - + Keymap 键盘映射 - + Configure shortcuts of actions 配置操作的快捷键 @@ -1714,12 +2228,12 @@ Core::Internal::LogPage - + Log 日志 - + Configure log output and archiving behaviors 配置日志输出和归档行为 @@ -1727,12 +2241,12 @@ Core::Internal::MenuPage - + Menus and Toolbars 菜单和工具栏 - + Configure the layout of menus and toolbars 配置菜单和工具栏的布局 @@ -1740,7 +2254,7 @@ Core::Internal::NotificationAddOn - + %1 (+%Ln notification(s)) %1(+%Ln 个通知) @@ -1750,30 +2264,38 @@ Core::Internal::ProjectStartupTimerAddOn - + Initializing project window... 正在初始化工程窗口... - + Project window initialized in %1 seconds 工程窗口初始化完成,用时 %1 秒 - + Project window initialized 工程窗口初始化完成 + + Core::Internal::RecentFileAddOn + + + <i>File moved or deleted</i> + <i>文件被移动或删除</i> + + Core::Internal::TimeIndicatorPage - + Time Indicator 时间指示器 - + Configure time indicator display and interaction behaviors 配置时间指示器的显示和交互行为 @@ -1781,17 +2303,17 @@ Core::Internal::TimelineAddOn - + measure %L1, beat %L2 第 %L1 小节,第 %L2 拍 - + measure %L1, beat %L2, tick %L3 第 %L1 小节,第 %L2 拍,第 %L3 刻 - + %Ln minute(s) absolute time @@ -1799,7 +2321,7 @@ - + %Ln second(s) absolute time @@ -1807,7 +2329,7 @@ - + %Ln millisecond(s) absolute time @@ -1815,26 +2337,26 @@ - - + + %1 %2 absolute minute second %1 %2处 - + %1 %2 absolute second millisecond %1 %2处 - + %1 %2 %3 absolute minute second millisecond %1 %2 %3处 - + %Ln minute(s) relative time @@ -1842,7 +2364,7 @@ - + %Ln second(s) relative time @@ -1850,7 +2372,7 @@ - + %Ln millisecond(s) relative time @@ -1858,127 +2380,127 @@ - - + + %1 %2 relative minute second %1 %2 - + %1 %2 relative second millisecond %1 %2 - + %1 %2 %3 relative minute second millisecond %1 %2 %3 - - - - - - - - - + + + + + + + + + Go to %1 转到 %1 - - + + absolute time... 绝对时间... - + %1 (%2) %1(%2) - + Move backward by %1 后移 %1 - + Move forward by %1 前移 %1 - + %1 (to %2) %1 (移动到 %2) - - - - + + + + The time offset exceeds the boundary and has been adjusted to zero 时间偏移超出边界,已被调整到零 - + the end of project (%1) 工程结束(%1) - + previous measure (%1) 上一小节(%1) - + previous beat (%1) 上一拍(%1) - + previous tick (%1) 上一刻(%1) - + next measure (%1) 下一小节(%1) - + next beat (%1) 下一拍(%1) - + next tick (%1) 下一刻(%1) - - + + Type "?" to view tips 键入“?”以查看提示 - - + + Invalid format 无效格式 - + Jump to 跳转到 - + Input should not be empty 输入不应为空 @@ -1986,12 +2508,10 @@ The time offset exceeds the boundary and has been adjusted to zero Core::Internal::ViewVisibilityAddOn - Please take attention 请注意 - After hiding the menu bar, it can be difficult to show it again. Make sure you know how to do this. Continue? @@ -2003,119 +2523,312 @@ Continue? Core::Internal::WorkspaceAddOn - + Save Current Layout As... 保存当前布局为... - + Default Layout 默认布局 - + custom layout 自定义布局 - + Workspace layout actions 工作区布局操作 - + Apply 应用 - + Rename 重命名 - + Delete 删除 - + Custom layout "%1" actions 自定义布局“%1”操作 + + Core::OpenSaveProjectFileScenario + + + DiffScope Project Exchange Format (*.dspx) + DiffScope 工程交换格式 (*.dspx) + + + + All Files (*) + 所有文件 (*) + + + + Failed to open file + 打开文件失败 + + + + Failed to save file + 保存文件失败 + + + + Open DSPX Inspector + 打开 DSPX 检查器 + + + + Failed to parse file content + 解析文件内容失败 + + + + %1 + +You can check for problems in the file with DSPX Inspector. + %1 + +你可以使用 DSPX Inspector 检查文件中的问题。 + + + + File created with another application + 使用其他应用程序创建的文件 + + + + name unknown + 名称未知 + + + + File created with incompatible %1 version + 使用不兼容的 %1 版本创建的文件 + + + + Additional check failed + 附加检查失败 + + + + Core::ProjectWindowInterface + + + File Modified Externally + 文件在外部修改 + + + + The file has been modified by another program since it was last saved. + +Do you want to save as a new file or overwrite it? + 该文件自上次保存后已被其他程序修改。 + +你想另存为新文件还是覆盖现有文件? + + + + Save As... + 另存为... + + + + Overwrite + 覆盖 + + + + + Untitled + 未命名 + + + + EditTempoDialog + + + Edit Tempo + 编辑曲速 + + + + Tempo + 曲速 + + + + Position + 位置 + + + + Modify existing one + 修改现有的 + + + + Insert new one + 添加新的 + + + + EditTimeSignatureDialog + + + Edit Time Signature + 编辑拍号 + + + + Time signature + 拍号 + + + + Numerator + 分子 + + + + Denominator + 分母 + + + + Position + 位置 + + + + Modify existing one + 修改现有的 + + + + Insert new one + 添加新的 + + + + FileBackupPage + + + File + 文件 + + + + Lock opened files + 锁定已打开的文件 + + + + Locking an open file prevents it from being modified by other programs. Change to this option will take effect only for projects opened after the change + 锁定已打开的文件可防止其他程序对其进行修改。此选项的更改仅对更改后打开的项目生效 + + + + Check for external modifications when saving a file + 保存文件时检查外部修改 + + GeneralPage - + Startup 启动 - + When starting %1 启动 %1 时 - + Open the home window 打开主窗口 - + Create a new project 创建新工程 - Open previous projects on startup automatically 自动打开之前的工程 - + Close the home window after opening a project - 打开工程后关闭主页窗口 + 打开工程后关闭主窗口 - - + + Language 语言 - + Use system language 使用系统语言 - + (Restart required) (需要重启) - + Notification 通知 - + Play sound alert when a notification bubble is sent 发送通知气泡时播放提示音 - + Timeout for auto hiding notification bubbles 通知气泡自动隐藏超时时间 - + milliseconds 毫秒 - + Reset All "Do Not Show Again" 重置所有“不再显示” + + + Window + 窗口 + + + + Memorize window position and size + 记住窗口的位置和大小 + Find Actions @@ -2198,27 +2911,56 @@ Continue? Updates - 更新 + Updates Check for updates on startup - 启动时检查更新 + Check for updates on startup Type of update to check for - 检查的更新类型 + Type of update to check for Stable - 稳定版 + Stable Beta - 测试版 + Beta Check for Updates - 检查更新 + Check for Updates + + + + LoadingFailedFallbackPanel + + + + Failed to load component + 组件加载失败 + + + + This component cannot be loaded because it is not registered to the application. The plugin providing this component might be disabled or no longer available. + 此组件无法加载,因为它未注册到应用程序中。提供此组件的插件可能已被禁用或不可用。 + + + + An error occurred while loading this component. + 加载此组件时发生错误。 + + + + Component identifier + 组件标识符 + + + + Error + 错误 @@ -2383,6 +3125,44 @@ Continue? 搜索 + + MetadataPanel + + + Edit + 编辑 + + + + Path + 路径 + + + + Unspecified + 未指定 + + + + Reveal in %1 + 在 %1 中显示 + + + + Title + 标题 + + + + Author + 作者 + + + + Cent Shift + 音分偏移 + + NotificationsPanel @@ -2391,7 +3171,7 @@ Continue? 全部清除 - + No notification 无通知 @@ -2399,7 +3179,7 @@ Continue? PluginDialog - + Plugins 插件 @@ -2407,11 +3187,94 @@ Continue? ProjectActions - + Click to show details 单击以显示详情 + + ProjectWindow + + + Untitled + 未命名 + + + + Modified Externally + 被外部修改 + + + + Unsaved + 未保存 + + + + RecentFilesPanel + + + + + + Search + 搜索 + + + + + Grid view + 表格视图 + + + + + List view + 列表视图 + + + + Clear Recovery Files + 清空恢复文件 + + + + Clear Recent Files + 清空最近文件 + + + + No recovery file +If %1 exits abnormally, automatic recovery files will be displayed here. + 没有恢复文件 +如果 %1 异常退出,此处将显示自动恢复文件。 + + + + No recent files + 无最近文件 + + + + Open + 打开 + + + + Reveal in %1 + 在 %1 中显示 + + + + Remove from "Recovery Files" + 从“恢复文件”中移除 + + + + Remove from "Recent Files" + 从“最近文件”中移除 + + TimeIndicatorPage @@ -2488,6 +3351,29 @@ Continue? 滑动以调整当前工程时间 + + UndoAddOnActions + + + Undo %1 + 撤销 %1 + + + + Undo + 撤销 + + + + Redo %1 + 重做 %1 + + + + Redo + 重做 + + WorkspaceAddOnActions @@ -2508,62 +3394,62 @@ Continue? - + None - + %1 (Left Top) %1(左上) - + %1 (Left Bottom) %1(左下) - + %1 (Right Top) %1(右上) - + %1 (Right Bottom) %1(右下) - + %1 (Top Left) %1(上左) - + %1 (Top Right) %1(上右) - + %1 (Bottom Left) %1(下左) - + %1 (Bottom Right) %1(下右) - + Add Action... 添加操作... - + Error 错误 - + Failed to create panel "%1" 创建面板“%1”失败 @@ -2571,52 +3457,52 @@ Continue? WorkspaceAddOnHelper - + The current workspace does not contain any panels 当前工作区不包含任何面板 - + Workspace data might be erroneous. You can try restoring the default workspace. 工作区数据可能有错误。你可以尝试恢复到默认工作区。 - + Restore Default Workspace 恢复到默认工作区 - + Custom layout name 自定义布局名称 - + Name should not be empty 名称不能为空 - + New name should not be the same as old name 新名称不能与旧名称相同 - + Custom presets with the same name will be overwritten 同名自定义预设将被覆盖 - + Delete 删除 - + Delete layout "%1"? 删除布局“%1”? - + Drag to the sidebar to add "%1" 拖动到侧边栏以添加“%1” diff --git a/src/plugins/coreplugin/res/translations/org.diffscope.core_zh_TW.ts b/src/plugins/coreplugin/res/translations/org.diffscope.core_zh_TW.ts index a9960dd2..131f9b94 100644 --- a/src/plugins/coreplugin/res/translations/org.diffscope.core_zh_TW.ts +++ b/src/plugins/coreplugin/res/translations/org.diffscope.core_zh_TW.ts @@ -91,17 +91,25 @@ <p>Based on Qt version %1.<br>Copyright © 2019-%2 Team OpenVPI. All rights reserved.</p> - <p>A professional singing-voice-synthesis editor powered by DiffSinger</p><p>Version %1</p><p>Copyright © %2-%3 Team OpenVPI. All rights reserved.</p><p>Visit <a href="%4">%4</a> for more information.</p> <p>A professional singing-voice-synthesis editor powered by DiffSinger</p><p>Version %1</p><p>Copyright © %2-%3 Team OpenVPI. All rights reserved.</p><p>Visit <a href="%4">%4</a> for more information.</p> - <h3>License</h3><p>Licensed under the Apache License, Version 2.0.<br>You may obtain a copy of the License at %1.</p><p>This application is distributed <b>AS IS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND</b>, either express or implied.</p> <h3>License</h3><p>Licensed under the Apache License, Version 2.0.<br>You may obtain a copy of the License at %1.</p><p>This application is distributed <b>AS IS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND</b>, either express or implied.</p> - + + <p>A professional singing-voice-synthesis editor powered by DiffSinger</p><p>Version %1</p><p>Copyright © %2-%3 %4. All rights reserved.</p><p>Visit <a href="%5">%5</a> for more information.</p> + <p>A professional singing-voice-synthesis editor powered by DiffSinger</p><p>Version %1</p><p>Copyright © %2-%3 %4. All rights reserved.</p><p>Visit <a href="%5">%5</a> for more information.</p> + + + + <h3>License</h3><p>Licensed under the Apache License, Version 2.0.<br>You may obtain a copy of the License at %1.</p><p>This application is distributed <b>AS IS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND</b>, either express or implied.</p><p>This license does not apply to plugins. Please refer to Plugins to view the licenses applicable to each individual plugin.</p> + <h3>License</h3><p>Licensed under the Apache License, Version 2.0.<br>You may obtain a copy of the License at %1.</p><p>This application is distributed <b>AS IS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND</b>, either express or implied.</p><p>This license does not apply to plugins. Please refer to Plugins to view the licenses applicable to each individual plugin.</p> + + + <h3>Build Information</h3><p>Branch: %1<br>Commit: %2<br>Build date: %3<br>Toolchain: %4 %5 %6</p> <h3>Build Information</h3><p>Branch: %1<br>Commit: %2<br>Build date: %3<br>Toolchain: %4 %5 %6</p> @@ -109,170 +117,186 @@ <h3>Build Information</h3><p>Version: %1<br>Branch: %2<br>Commit: %3<br>Build date: %4<br>Toolchain: %5 %6 %7</p> <h3>Build Information</h3><p>Version: %1<br>Branch: %2<br>Commit: %3<br>Build date: %4<br>Toolchain: %5 %6 %7</p> + + + Version %1 + Version %1 + + + + Copyright © %1-%2 %3. All rights reserved. + Copyright © %1-%2 %3. All rights reserved. + Application::ActionClass - - - + + + + + + + File File - - + + Preferences Preferences - - - - + + + + Window Window - + Application Application - - - - + + + + + Home Home - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Edit Edit - - + + Time Indicator Time Indicator - - - - - - - - - - - - + + + + + + + + + + + + Timeline Timeline - - + + Core Core - - - - - - - + + + + + + + Panel Panel - + + Widget Widget - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + View View - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + Workspace Workspace - - - - + + + + + Help Help @@ -280,157 +304,162 @@ Application::ActionDescription - + Create a project in a new window Create a project in a new window - + + Create a project from template in a new window + Create a project from template in a new window + + + Open an existing project in a new window Open an existing project in a new window - + Open application settings Open application settings - + Open plugin configuration dialog Open plugin configuration dialog - + Switch to home window if it is opened, or else open home window Switch to home window if it is opened, or else open home window - + Quit DiffScope Quit DiffScope - + Toggle "Recent Files" page Toggle "Recent Files" page - + Toggle "Recovery Files" page Toggle "Recovery Files" page - + Toggle grid view in "Recent Files" page Toggle grid view in "Recent Files" page - + Toggle list view in "Recent Files" page Toggle list view in "Recent Files" page - + Show music time (measure:beat:tick) on the time indicator Show music time (measure:beat:tick) on the time indicator - + Show absolute time (minute:second.millisecond) on the time indicator Show absolute time (minute:second.millisecond) on the time indicator - + Navigate to a specific position Navigate to a specific position - + Quickly navigate to a specific position Quickly navigate to a specific position - + Navigate to the start of current project Navigate to the start of current project - + Navigate to the end of current project Navigate to the end of current project - + Reset project end time based on project content Reset project end time based on project content - + Apply the default workspace layout Apply the default workspace layout - + Show or hide the main menu bar Show or hide the main menu bar - + Show or hide the main tool bar Show or hide the main tool bar - + Show or hide the left side bar Show or hide the left side bar - + Show or hide the right side bar Show or hide the right side bar - + Show or hide the top side bar Show or hide the top side bar - + Show or hide the bottom side bar Show or hide the bottom side bar - + Show or hide the status bar Show or hide the status bar - + Switch to the next project window Switch to the next project window - + Switch to the previous project window Switch to the previous project window - + Show help contents Show help contents - + Find and trigger action by name Find and trigger action by name - + Show information about DiffScope Show information about DiffScope - + Show information about Qt Show information about Qt @@ -438,747 +467,811 @@ Application::ActionText - + &New &New - + + New from Template... + New from Template... + + + &Open... &Open... - + + Open &Recent File + Open &Recent File + + + &Save &Save - + + Save &As... + Save &As... + + + + Save Copy As... + Save Copy As... + + + Se&ttings... Se&ttings... - + Pl&ugins... Pl&ugins... - + Show &Home Window Show &Home Window - + E&xit E&xit - + Recent Files Recent Files - + Recovery Files Recovery Files - + Grid View Grid View - + List View List View - + &Undo &Undo - + &Redo &Redo - + Cu&t Cu&t - + &Copy &Copy - + &Paste &Paste - Paste Special... Paste Special... - + &Delete &Delete - + Select &All Select &All - + Dese&lect Dese&lect - + Select Current Select Current - + Select &Up Select &Up - + Select &Down Select &Down - + Select &Left Select &Left - + Select &Right Select &Right - + Move &Up Move &Up - + Move &Down Move &Down - + Move &Left Move &Left - + Move &Right Move &Right - + &Move... &Move... - + Scroll &Up Scroll &Up - + Scroll &Down Scroll &Down - + Scroll &Left Scroll &Left - + Scroll &Right Scroll &Right - + Move Cursor &Up Move Cursor &Up - + Move Cursor &Down Move Cursor &Down - + Move Cursor &Left Move Cursor &Left - + Move Cursor &Right Move Cursor &Right - + Extend Selection &Up Extend Selection &Up - + Extend Selection &Down Extend Selection &Down - + Extend Selection &Left Extend Selection &Left - + Extend Selection &Right Extend Selection &Right - + Shrink Selection &Up Shrink Selection &Up - + Shrink Selection &Down Shrink Selection &Down - + Shrink Selection &Left Shrink Selection &Left - + Shrink Selection &Right Shrink Selection &Right - + Page &Up Page &Up - + Page &Down Page &Down - + Page &Left Page &Left - + Page &Right Page &Right - + Scroll to &Top Scroll to &Top - + Scroll to &Bottom Scroll to &Bottom - + Scroll to &Start Scroll to &Start - + Scroll to &End Scroll to &End - + Scroll to Current Time Scroll to Current Time - + Show &Music Time Show &Music Time - + Show &Absolute Time Show &Absolute Time - + &Go To... &Go To... - + &Quick Jump... &Quick Jump... - + Go to &Start Go to &Start - + Go to Previous Measure Go to Previous Measure - + Go to Previous Beat Go to Previous Beat - + Go to Previous Tick Go to Previous Tick - + Go to &End Go to &End - + Go to Next Measure Go to Next Measure - + Go to Next Beat Go to Next Beat - + Go to Next Tick Go to Next Tick - + Go Inside View Range Go Inside View Range - + Workspace Layout... Workspace Layout... - + + Always Show &Menu Bar + Always Show &Menu Bar + + + + Home Menu + Home Menu + + + + Window + Window + + + &Reset Project Time Range &Reset Project Time Range - + Default &Layout Default &Layout - + &Custom Layouts &Custom Layouts - + &Save Current Layout As... &Save Current Layout As... - + &All Panels &All Panels - + None (Left Top) None (Left Top) - + None (Left Bottom) None (Left Bottom) - + None (Right Top) None (Right Top) - + None (Right Bottom) None (Right Bottom) - + None (Top Left) None (Top Left) - + None (Top Right) None (Top Right) - + None (Bottom Left) None (Bottom Left) - + None (Bottom Right) None (Bottom Right) - + &Floating Panels &Floating Panels - + Add &New Panel or Action Add &New Panel or Action - Show &Menu Bar Show &Menu Bar - + Show Too&l Bar Show Too&l Bar - + Show &Left Side Bar Show &Left Side Bar - + Show &Right Side Bar Show &Right Side Bar - + Show &Top Side Bar Show &Top Side Bar - + Show &Bottom Side Bar Show &Bottom Side Bar - + Show &Status Bar Show &Status Bar - + &Next Project Window &Next Project Window - + &Previous Project Window &Previous Project Window - + Pro&ject Windows Pro&ject Windows - + &Help &Help - + &Find Actions... &Find Actions... - + &About DiffScope &About DiffScope - + About &Qt About &Qt - + + Run DSPX Inspector + Run DSPX Inspector + + + Main Menu Main Menu - + Main Tool Bar Left Main Tool Bar Left - + Main Tool Bar Middle Main Tool Bar Middle - + Main Tool Bar Right Main Tool Bar Right - + Main Status Bar Main Status Bar - + + Home Panels + Home Panels + + + Home Navigation Home Navigation - + Home Tool Home Tool - + + Navigation Panels + Navigation Panels + + + Preferences Preferences - Help Help - + Workspace Panel Widgets Workspace Panel Widgets - + Status Text Status Text - + Properties Properties - + Plugins Plugins - Arrangement Arrangement - Mixer Mixer - Piano Roll Piano Roll - + Notifications Notifications - + Tips Tips - + + Metadata + Metadata + + + Digital Clock Digital Clock - + + Tempo Time Signature Indicator + Tempo Time Signature Indicator + + + &File &File - + File Open Actions File Open Actions - + + File Save Actions + File Save Actions + + + Preference Actions Preference Actions - + &Edit &Edit - + Undo Actions Undo Actions - + Generic Generic - + Navigation Navigation - + Move Cu&rsor Move Cu&rsor - + &Move &Move - + &Select &Select - + &Extend Selection &Extend Selection - + Shrin&k Selection Shrin&k Selection - + Timeline Timeline - + Time Indicator Timecode Format Actions Time Indicator Timecode Format Actions - + Timeline Go to Actions Timeline Go to Actions - + Timeline Back Navigation Actions Timeline Back Navigation Actions - + Timeline Forward Navigation Actions Timeline Forward Navigation Actions - + &View &View - + Workspace Actions Workspace Actions - + &Workspace &Workspace - + Workspace Layouts Workspace Layouts - + Opened &Docking Panels Opened &Docking Panels - + Dock Action to Side Bar Dock Action to Side Bar - + Scroll Actions Scroll Actions - + Scroll Scroll - + Scroll by Page Scroll by Page - + Scroll To Scroll To - + View Visibility Actions View Visibility Actions - + &Window &Window - + Project Window Actions Project Window Actions - + About Actions About Actions - + + Dspx Inspector Actions + Dspx Inspector Actions + + + Tool Bar Timeline Navigation Actions Tool Bar Timeline Navigation Actions @@ -1301,121 +1394,126 @@ + Pane separator color + Pane separator color + + + Primary foreground color Primary foreground color - + Secondary foreground color Secondary foreground color - + Link color Link color - + Navigation color Navigation color - + Shadow color Shadow color - + Highlight color Highlight color - + Flat button high contrast border color Flat button high contrast border color - + Control disabled color change Control disabled color change - + Foreground disabled color change Foreground disabled color change - + Control hovered color change Control hovered color change - + Foreground hovered color change Foreground hovered color change - + Control pressed color change Control pressed color change - + Foreground pressed color change Foreground pressed color change - + Control checked color change Control checked color change - + Annotation popup title color change Annotation popup title color change - + Annotation popup content color change Annotation popup content color change - + Docking panel header active color change Docking panel header active color change - + Preset Preset - + Preset Actions Preset Actions - + Save As... Save As... - + Rename... Rename... - + Delete Delete - + Import from File... Import from File... - + Export to File... Export to File... @@ -1453,72 +1551,470 @@ You can also create a custom color theme later in Settings > Appearance > Color Scheme + + Core::ActionWindowInterfaceBase + + + Toggle "%1" + Toggle "%1" + + + + Open Menu "%1"... + Open Menu "%1"... + + Core::CoreInterface - + About %1 About %1 + + Core::DspxInspectorDialog + + + File path + File path + + + + &Browse + &Browse + + + + &Run Check + &Run Check + + + + &Problems + &Problems + + + + DSPX Inspector + DSPX Inspector + + + + Null + Null + + + + Boolean + Boolean + + + + Integer + Integer + + + + Number + Number + + + + String + String + + + + Array + Array + + + + Object + Object + + + + Fatal: Failed to open file + Fatal: Failed to open file + + + + Path + Path + + + + + Error code + Error code + + + + + Error text + Error text + + + + Fatal: Failed to parse JSON + Fatal: Failed to parse JSON + + + + Offset in file + Offset in file + + + + The file is not a valid JSON document. + The file is not a valid JSON document. + + + + Fatal: Root is not an object + Fatal: Root is not an object + + + + The root of JSON document is not an object. + The root of JSON document is not an object. + + + + Fatal: Unrecognized version + Fatal: Unrecognized version + + + + Actual version + Actual version + + + + This project file may have been created with a newer version of %1 or another application, and its version is not recognized by %1. Please try exporting the project from the application with which it was created as a version compatible with your current %1. + This project file may have been created with a newer version of %1 or another application, and its version is not recognized by %1. Please try exporting the project from the application with which it was created as a version compatible with your current %1. + + + + Invalid data type + Invalid data type + + + + Expected data type + Expected data type + + + + , + , + + + + Actual data type + Actual data type + + + + The value at the specific path is not of the expected data type. + The value at the specific path is not of the expected data type. + + + + Invalid object type + Invalid object type + + + + Expected object type + Expected object type + + + + Actual object type + Actual object type + + + + The object at the specific path is not of the expected object type. + The object at the specific path is not of the expected object type. + + + + Range constraint violation + Range constraint violation + + + + Expected maximum value + Expected maximum value + + + + + None + None + + + + Expected minimum value + Expected minimum value + + + + Actual value + Actual value + + + + The value of the property at the specific path is outside the allowed range. The value must be between the expected minimum and maximum value (inclusive). + The value of the property at the specific path is outside the allowed range. The value must be between the expected minimum and maximum value (inclusive). + + + + Expected enum value + Expected enum value + + + + Actual enum value + Actual enum value + + + + Enum constraint violation + Enum constraint violation + + + + The value of the property at the specific path is not one of the allowed enum values. + The value of the property at the specific path is not one of the allowed enum values. + + + + Missing property + Missing property + + + + Missing properties + Missing properties + + + + One or more properties are missing in the object at the specific path. + One or more properties are missing in the object at the specific path. + + + + Redundant property + Redundant property + + + + Redundant properties + Redundant properties + + + + One or more properties are redundant in the object at the specific path. + One or more properties are redundant in the object at the specific path. + + + + Index + Index + + + + Overlapping items + Overlapping items + + + + Items at specific indexes in the array at the specific path overlap. + Items at specific indexes in the array at the specific path overlap. + + + + Zero-length range + Zero-length range + + + + The range length of the entity object at the specific path is zero. + The range length of the entity object at the specific path is zero. + + + + Erroneous clip range + Erroneous clip range + + + + The clipping range of the clip entity object at the specific path exceeds its range limit. + The clipping range of the clip entity object at the specific path exceeds its range limit. + + + + Erroneous clip position + Erroneous clip position + + + + The position of the clip entity object at the specific path exceeds the view range limit. It might be not visible in the viewport. + The position of the clip entity object at the specific path exceeds the view range limit. It might be not visible in the viewport. + + + + Safe range limit exceeded + Safe range limit exceeded + + + + The position of the entity object at the specific path exceeds the safe project length limit (4,000,000 ticks). + The position of the entity object at the specific path exceeds the safe project length limit (4,000,000 ticks). + + + + File Created With Another Application + File Created With Another Application + + + + Editor ID + Editor ID + + + + Editor name + Editor name + + + + This project file was created with another application. Some features may not be fully compatible or may behave differently. + This project file was created with another application. Some features may not be fully compatible or may behave differently. + + + + File Created With Incompatible Version + File Created With Incompatible Version + + + + Version + Version + + + + This project file was created with an newer version or test version of %1. Some features may not be fully compatible or may behave differently. + This project file was created with an newer version or test version of %1. Some features may not be fully compatible or may behave differently. + + + + No problems found + No problems found + + + + The project file is valid and no problems were found. + The project file is valid and no problems were found. + + + + Core::EditTempoTimeSignatureScenario + + + Editing tempo + Editing tempo + + + + Editing time signature + Editing time signature + + + + Core::Internal::AfterSavingNotifyAddOn + + + The file has been saved to %1 + The file has been saved to %1 + + Core::Internal::AppearancePage - + Appearance Appearance - + Configure how %1 looks like Configure how %1 looks like + + Core::Internal::CloseSaveCheckAddOn + + + Do you want to save before closing? + Do you want to save before closing? + + + + If you choose not to save, a copy of the current project file will be created to help recover your work in case of accidental incorrect operation. + If you choose not to save, a copy of the current project file will be created to help recover your work in case of accidental incorrect operation. + + Core::Internal::ColorSchemeCollection - + Scopic Dark Scopic Dark - + Scopic Light Scopic Light - + Scopic High Contrast Scopic High Contrast - + Failed to Import Preset Failed to Import Preset - - + + Unable to open file "%1" Unable to open file "%1" - + Failed to import preset Failed to import preset - + Invalid format in file "%1" Invalid format in file "%1" - + Failed to export preset Failed to export preset - + (Unsaved preset) (Unsaved preset) @@ -1526,112 +2022,112 @@ Core::Internal::ColorSchemePage - + Color Scheme Color Scheme - + Configure the colors and visual effects of various components Configure the colors and visual effects of various components - + Alpha: %L1 Alpha: %L1 - + Saturation (HSV): %L1 Saturation (HSV): %L1 - + Value: %L1 Value: %L1 - + Saturation (HSL): %L1 Saturation (HSL): %L1 - + Lightness: %L1 Lightness: %L1 - + QColor::lighter(): %L1 QColor::lighter(): %L1 - + Top Blend: %1 Top Blend: %1 - + Bottom Blend: %1 Bottom Blend: %1 - + Syntax error at line %L1: Missing colon in declaration Syntax error at line %L1: Missing colon in declaration - + Syntax error at line %L1: Empty property name Syntax error at line %L1: Empty property name - + Syntax error at line %L1: Empty property value Syntax error at line %L1: Empty property value - + Syntax error at line %L1: Invalid 'alpha' value Syntax error at line %L1: Invalid 'alpha' value - + Syntax error at line %L1: Invalid 'saturation' value Syntax error at line %L1: Invalid 'saturation' value - + Syntax error at line %L1: Invalid 'value' value Syntax error at line %L1: Invalid 'value' value - + Syntax error at line %L1: Invalid 'hsl-saturation' value Syntax error at line %L1: Invalid 'hsl-saturation' value - + Syntax error at line %L1: Invalid 'lightness' value Syntax error at line %L1: Invalid 'lightness' value - + Syntax error at line %L1: Invalid 'lighter' value (must be a positive integer) Syntax error at line %L1: Invalid 'lighter' value (must be a positive integer) - + Syntax error at line %L1: Invalid color value for 'top-blend' Syntax error at line %L1: Invalid color value for 'top-blend' - + Syntax error at line %L1: Invalid color value for 'bottom-blend' Syntax error at line %L1: Invalid color value for 'bottom-blend' - + Syntax error at line %L1: Unknown property '%2' Syntax error at line %L1: Unknown property '%2' @@ -1639,20 +2135,33 @@ Core::Internal::CorePlugin - + Initializing core plugin... Initializing core plugin... - + Initializing GUI... Initializing GUI... + + Core::Internal::FileBackupPage + + + File and Backup + File and Backup + + + + Configure file handling and backup behaviors of %1 + Configure file handling and backup behaviors of %1 + + Core::Internal::FindActionsAddOn - + Find actions Find actions @@ -1660,17 +2169,22 @@ Core::Internal::FindActionsModel - + Toggle "%1" Toggle "%1" - + + Open Menu "%1"... + Open Menu "%1"... + + + %1: %2 %1: %2 - + recently used recently used @@ -1678,22 +2192,22 @@ Core::Internal::GeneralPage - + General General - + Configure general behaviors of %1 Configure general behaviors of %1 - + Restart %1 重新啟動 %1 - + Restart %1 to apply language changes? 重新啟動 %1 以應用語言變更? @@ -1701,12 +2215,12 @@ Core::Internal::KeyMapPage - + Keymap Keymap - + Configure shortcuts of actions Configure shortcuts of actions @@ -1714,12 +2228,12 @@ Core::Internal::LogPage - + Log Log - + Configure log output and archiving behaviors Configure log output and archiving behaviors @@ -1727,12 +2241,12 @@ Core::Internal::MenuPage - + Menus and Toolbars Menus and Toolbars - + Configure the layout of menus and toolbars Configure the layout of menus and toolbars @@ -1740,7 +2254,7 @@ Core::Internal::NotificationAddOn - + %1 (+%Ln notification(s)) %1 (+%Ln notification(s)) @@ -1750,30 +2264,38 @@ Core::Internal::ProjectStartupTimerAddOn - + Initializing project window... Initializing project window... - + Project window initialized in %1 seconds Project window initialized in %1 seconds - + Project window initialized Project window initialized + + Core::Internal::RecentFileAddOn + + + <i>File moved or deleted</i> + <i>File moved or deleted</i> + + Core::Internal::TimeIndicatorPage - + Time Indicator Time Indicator - + Configure time indicator display and interaction behaviors Configure time indicator display and interaction behaviors @@ -1781,17 +2303,17 @@ Core::Internal::TimelineAddOn - + measure %L1, beat %L2 measure %L1, beat %L2 - + measure %L1, beat %L2, tick %L3 measure %L1, beat %L2, tick %L3 - + %Ln minute(s) absolute time @@ -1799,7 +2321,7 @@ - + %Ln second(s) absolute time @@ -1807,7 +2329,7 @@ - + %Ln millisecond(s) absolute time @@ -1815,26 +2337,26 @@ - - + + %1 %2 absolute minute second %1 %2 - + %1 %2 absolute second millisecond %1 %2 - + %1 %2 %3 absolute minute second millisecond %1 %2 %3 - + %Ln minute(s) relative time @@ -1842,7 +2364,7 @@ - + %Ln second(s) relative time @@ -1850,7 +2372,7 @@ - + %Ln millisecond(s) relative time @@ -1858,127 +2380,127 @@ - - + + %1 %2 relative minute second %1 %2 - + %1 %2 relative second millisecond %1 %2 - + %1 %2 %3 relative minute second millisecond %1 %2 %3 - - - - - - - - - + + + + + + + + + Go to %1 Go to %1 - - + + absolute time... absolute time... - + %1 (%2) %1 (%2) - + Move backward by %1 Move backward by %1 - + Move forward by %1 Move forward by %1 - + %1 (to %2) %1 (to %2) - - - - + + + + The time offset exceeds the boundary and has been adjusted to zero The time offset exceeds the boundary and has been adjusted to zero - + the end of project (%1) the end of project (%1) - + previous measure (%1) previous measure (%1) - + previous beat (%1) previous beat (%1) - + previous tick (%1) previous tick (%1) - + next measure (%1) next measure (%1) - + next beat (%1) next beat (%1) - + next tick (%1) next tick (%1) - - + + Type "?" to view tips Type "?" to view tips - - + + Invalid format Invalid format - + Jump to Jump to - + Input should not be empty Input should not be empty @@ -1986,12 +2508,10 @@ The time offset exceeds the boundary and has been adjusted to zero Core::Internal::ViewVisibilityAddOn - Please take attention Please take attention - After hiding the menu bar, it can be difficult to show it again. Make sure you know how to do this. Continue? @@ -2003,119 +2523,312 @@ Continue? Core::Internal::WorkspaceAddOn - + Save Current Layout As... Save Current Layout As... - + Default Layout Default Layout - + custom layout custom layout - + Workspace layout actions Workspace layout actions - + Apply Apply - + Rename Rename - + Delete Delete - + Custom layout "%1" actions Custom layout "%1" actions + + Core::OpenSaveProjectFileScenario + + + DiffScope Project Exchange Format (*.dspx) + DiffScope Project Exchange Format (*.dspx) + + + + All Files (*) + All Files (*) + + + + Failed to open file + Failed to open file + + + + Failed to save file + Failed to save file + + + + Open DSPX Inspector + Open DSPX Inspector + + + + Failed to parse file content + Failed to parse file content + + + + %1 + +You can check for problems in the file with DSPX Inspector. + %1 + +You can check for problems in the file with DSPX Inspector. + + + + File created with another application + File created with another application + + + + name unknown + name unknown + + + + File created with incompatible %1 version + File created with incompatible %1 version + + + + Additional check failed + Additional check failed + + + + Core::ProjectWindowInterface + + + File Modified Externally + File Modified Externally + + + + The file has been modified by another program since it was last saved. + +Do you want to save as a new file or overwrite it? + The file has been modified by another program since it was last saved. + +Do you want to save as a new file or overwrite it? + + + + Save As... + Save As... + + + + Overwrite + Overwrite + + + + + Untitled + Untitled + + + + EditTempoDialog + + + Edit Tempo + Edit Tempo + + + + Tempo + Tempo + + + + Position + Position + + + + Modify existing one + Modify existing one + + + + Insert new one + Insert new one + + + + EditTimeSignatureDialog + + + Edit Time Signature + Edit Time Signature + + + + Time signature + Time signature + + + + Numerator + Numerator + + + + Denominator + Denominator + + + + Position + Position + + + + Modify existing one + Modify existing one + + + + Insert new one + Insert new one + + + + FileBackupPage + + + File + File + + + + Lock opened files + Lock opened files + + + + Locking an open file prevents it from being modified by other programs. Change to this option will take effect only for projects opened after the change + Locking an open file prevents it from being modified by other programs. Change to this option will take effect only for projects opened after the change + + + + Check for external modifications when saving a file + Check for external modifications when saving a file + + GeneralPage - + Startup Startup - + When starting %1 When starting %1 - + Open the home window Open the home window - + Create a new project Create a new project - Open previous projects on startup automatically Open previous projects on startup automatically - + Close the home window after opening a project Close the home window after opening a project - - + + Language Language - + Use system language Use system language - + (Restart required) (Restart required) - + Notification Notification - + Play sound alert when a notification bubble is sent Play sound alert when a notification bubble is sent - + Timeout for auto hiding notification bubbles Timeout for auto hiding notification bubbles - + milliseconds milliseconds - + Reset All "Do Not Show Again" Reset All "Do Not Show Again" + + + Window + Window + + + + Memorize window position and size + Memorize window position and size + Find Actions @@ -2221,6 +2934,35 @@ Continue? Check for Updates + + LoadingFailedFallbackPanel + + + + Failed to load component + Failed to load component + + + + This component cannot be loaded because it is not registered to the application. The plugin providing this component might be disabled or no longer available. + This component cannot be loaded because it is not registered to the application. The plugin providing this component might be disabled or no longer available. + + + + An error occurred while loading this component. + An error occurred while loading this component. + + + + Component identifier + Component identifier + + + + Error + Error + + LogPage @@ -2383,6 +3125,44 @@ Continue? Search + + MetadataPanel + + + Edit + Edit + + + + Path + Path + + + + Unspecified + Unspecified + + + + Reveal in %1 + Reveal in %1 + + + + Title + Title + + + + Author + Author + + + + Cent Shift + Cent Shift + + NotificationsPanel @@ -2391,7 +3171,7 @@ Continue? Clear All - + No notification No notification @@ -2399,7 +3179,7 @@ Continue? PluginDialog - + Plugins Plugins @@ -2407,11 +3187,94 @@ Continue? ProjectActions - + Click to show details Click to show details + + ProjectWindow + + + Untitled + Untitled + + + + Modified Externally + Modified Externally + + + + Unsaved + Unsaved + + + + RecentFilesPanel + + + + + + Search + Search + + + + + Grid view + Grid view + + + + + List view + List view + + + + Clear Recovery Files + Clear Recovery Files + + + + Clear Recent Files + Clear Recent Files + + + + No recovery file +If %1 exits abnormally, automatic recovery files will be displayed here. + No recovery file +If %1 exits abnormally, automatic recovery files will be displayed here. + + + + No recent files + No recent files + + + + Open + Open + + + + Reveal in %1 + Reveal in %1 + + + + Remove from "Recovery Files" + Remove from "Recovery Files" + + + + Remove from "Recent Files" + Remove from "Recent Files" + + TimeIndicatorPage @@ -2488,6 +3351,29 @@ Continue? Slide to adjust current project time + + UndoAddOnActions + + + Undo %1 + Undo %1 + + + + Undo + Undo + + + + Redo %1 + Redo %1 + + + + Redo + Redo + + WorkspaceAddOnActions @@ -2508,62 +3394,62 @@ Continue? - + None None - + %1 (Left Top) %1 (Left Top) - + %1 (Left Bottom) %1 (Left Bottom) - + %1 (Right Top) %1 (Right Top) - + %1 (Right Bottom) %1 (Right Bottom) - + %1 (Top Left) %1 (Top Left) - + %1 (Top Right) %1 (Top Right) - + %1 (Bottom Left) %1 (Bottom Left) - + %1 (Bottom Right) %1 (Bottom Right) - + Add Action... Add Action... - + Error Error - + Failed to create panel "%1" Failed to create panel "%1" @@ -2571,52 +3457,52 @@ Continue? WorkspaceAddOnHelper - + The current workspace does not contain any panels The current workspace does not contain any panels - + Workspace data might be erroneous. You can try restoring the default workspace. Workspace data might be erroneous. You can try restoring the default workspace. - + Restore Default Workspace Restore Default Workspace - + Custom layout name Custom layout name - + Name should not be empty Name should not be empty - + New name should not be the same as old name New name should not be the same as old name - + Custom presets with the same name will be overwritten Custom presets with the same name will be overwritten - + Delete Delete - + Delete layout "%1"? Delete layout "%1"? - + Drag to the sidebar to add "%1" Drag to the sidebar to add "%1" diff --git a/src/plugins/coreplugin/windows/ProjectWindowInterface.cpp b/src/plugins/coreplugin/windows/ProjectWindowInterface.cpp index d4bb43f6..3a503ade 100644 --- a/src/plugins/coreplugin/windows/ProjectWindowInterface.cpp +++ b/src/plugins/coreplugin/windows/ProjectWindowInterface.cpp @@ -43,6 +43,7 @@ namespace Core { ProjectTimeline *projectTimeline; EditActionsHandlerRegistry *mainEditActionsHandlerRegistry; ProjectDocumentContext *projectDocumentContext; + void init() { Q_Q(ProjectWindowInterface); initActionContext(); @@ -205,7 +206,7 @@ namespace Core { } return icon; }(); - win->setIcon(QIcon(":/diffscope/icons/dspx/24x24.png")); + win->setIcon(dspxIcon); QString path; if (d->projectDocumentContext->fileLocker() && !d->projectDocumentContext->fileLocker()->path().isEmpty()) { path = d->projectDocumentContext->fileLocker()->path(); @@ -215,6 +216,7 @@ namespace Core { win->setFilePath(path); return win; } + ProjectWindowInterface::ProjectWindowInterface(ProjectDocumentContext *projectDocumentContext, QObject *parent) : ProjectWindowInterface(*new ProjectWindowInterfacePrivate, parent) { Q_D(ProjectWindowInterface); d->projectDocumentContext = projectDocumentContext; diff --git a/src/plugins/coreplugin/windows/ProjectWindowInterface.h b/src/plugins/coreplugin/windows/ProjectWindowInterface.h index bb4b438b..39bedcee 100644 --- a/src/plugins/coreplugin/windows/ProjectWindowInterface.h +++ b/src/plugins/coreplugin/windows/ProjectWindowInterface.h @@ -58,6 +58,8 @@ namespace Core { Q_INVOKABLE bool saveAs(); Q_INVOKABLE bool saveCopy(); + + protected: QWindow *createWindow(QObject *parent) const override; diff --git a/src/plugins/importexportmanager/core/ConverterCollection.cpp b/src/plugins/importexportmanager/core/ConverterCollection.cpp index 21515812..d0055e69 100644 --- a/src/plugins/importexportmanager/core/ConverterCollection.cpp +++ b/src/plugins/importexportmanager/core/ConverterCollection.cpp @@ -6,7 +6,9 @@ namespace ImportExportManager { ConverterCollection *m_instance = nullptr; - ConverterCollection::ConverterCollection(QObject *parent) : Core::ObjectPool(parent) { + static QList s_fileConverters; + + ConverterCollection::ConverterCollection(QObject *parent) : QObject(parent) { Q_ASSERT(!m_instance); m_instance = this; } @@ -19,8 +21,12 @@ namespace ImportExportManager { return m_instance; } - QList ConverterCollection::fileConverters() const { - return getObjects(); + QList ConverterCollection::fileConverters() { + return s_fileConverters; + } + + void ConverterCollection::addFileConverter(FileConverter *converter) { + s_fileConverters.append(converter); } } diff --git a/src/plugins/importexportmanager/core/ConverterCollection.h b/src/plugins/importexportmanager/core/ConverterCollection.h index d865ecb7..fba93dd3 100644 --- a/src/plugins/importexportmanager/core/ConverterCollection.h +++ b/src/plugins/importexportmanager/core/ConverterCollection.h @@ -1,7 +1,7 @@ #ifndef CONVERTERCOLLECTION_H #define CONVERTERCOLLECTION_H -#include +#include #include @@ -13,14 +13,15 @@ namespace ImportExportManager { class FileConverter; - class IMPORT_EXPORT_MANAGER_EXPORT ConverterCollection : public Core::ObjectPool { + class IMPORT_EXPORT_MANAGER_EXPORT ConverterCollection : public QObject { Q_OBJECT public: ~ConverterCollection() override; static ConverterCollection *instance(); - QList fileConverters() const; + static QList fileConverters(); + static void addFileConverter(FileConverter *converter); private: friend class Internal::ImportExportManagerPlugin; diff --git a/src/plugins/importexportmanager/internal/ImportExportManagerPlugin.cpp b/src/plugins/importexportmanager/internal/ImportExportManagerPlugin.cpp index c30be16c..c6dce039 100644 --- a/src/plugins/importexportmanager/internal/ImportExportManagerPlugin.cpp +++ b/src/plugins/importexportmanager/internal/ImportExportManagerPlugin.cpp @@ -38,8 +38,8 @@ namespace ImportExportManager::Internal { Core::HomeWindowInterfaceRegistry::instance()->attach(); Core::ProjectWindowInterfaceRegistry::instance()->attach(); - ConverterCollection::instance()->addObject(new DspxFileImporter); - ConverterCollection::instance()->addObject(new DspxFileExporter); + ConverterCollection::addFileConverter(new DspxFileImporter); + ConverterCollection::addFileConverter(new DspxFileExporter); return true; } diff --git a/src/plugins/importexportmanager/internal/addon/FileImportExportAddOn.cpp b/src/plugins/importexportmanager/internal/addon/FileImportExportAddOn.cpp index 77cd61a4..d22a1213 100644 --- a/src/plugins/importexportmanager/internal/addon/FileImportExportAddOn.cpp +++ b/src/plugins/importexportmanager/internal/addon/FileImportExportAddOn.cpp @@ -23,10 +23,13 @@ #include +#include + #include #include #include #include +#include #include #include @@ -85,7 +88,7 @@ namespace ImportExportManager::Internal { } QList FileImportExportAddOn::importConverters(const QString &path) { - auto converters = ConverterCollection::instance()->fileConverters(); + auto converters = ConverterCollection::fileConverters(); auto filtered = converters | std::views::filter([&](const FileConverter *converter) { if (converter->mode() != FileConverter::Import) { @@ -112,7 +115,7 @@ namespace ImportExportManager::Internal { } QList FileImportExportAddOn::exportConverters() { - auto converters = ConverterCollection::instance()->fileConverters(); + auto converters = ConverterCollection::fileConverters(); auto filtered = converters | std::views::filter([&](const FileConverter *converter) { return converter->mode() == FileConverter::Export; }); @@ -120,26 +123,58 @@ namespace ImportExportManager::Internal { } void FileImportExportAddOn::execImport(FileConverter *converter) const { + qCInfo(lcFileImportExportAddOn) << "Exec import" << converter << converter->name(); + if (!converter->runPreExecCheck()) { + qCInfo(lcFileImportExportAddOn) << "Import pre-exec check failed"; + return; + } auto settings = Core::RuntimeInterface::settings(); settings->beginGroup(staticMetaObject.className()); auto defaultDir = settings->value(QStringLiteral("defaultImportExportDir"), QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation)).toString(); settings->endGroup(); - auto path = QFileDialog::getOpenFileName(nullptr, {}, defaultDir, converter->fileDialogFilters().join(";;")); + auto path = QFileDialog::getOpenFileName(nullptr, tr("Import File"), defaultDir, converter->fileDialogFilters().join(";;")); if (path.isEmpty()) { + qCInfo(lcFileImportExportAddOn) << "Import canceled: file not selected"; return; } settings->beginGroup(staticMetaObject.className()); settings->setValue(QStringLiteral("defaultImportExportDir"), QFileInfo(path).absolutePath()); settings->endGroup(); QDspx::Model model; - converter->execImport(path, model, windowHandle()->window()); + if (!converter->execImport(path, model, windowHandle()->window())) { + qCInfo(lcFileImportExportAddOn) << "Import failed or canceled"; + return; + } auto projectDocumentContext = std::make_unique(); projectDocumentContext->newFile(model, false); Core::CoreInterface::createProjectWindow(projectDocumentContext.release()); } - void FileImportExportAddOn::execExport(FileConverter *converter) { - // TODO + void FileImportExportAddOn::execExport(FileConverter *converter) const { + qCInfo(lcFileImportExportAddOn) << "Exec export" << converter << converter->name() << "from window" << windowHandle(); + if (!converter->runPreExecCheck()) { + qCInfo(lcFileImportExportAddOn) << "Export pre-exec check failed"; + return; + } + Q_ASSERT(qobject_cast(windowHandle())); + auto projectDocumentContext = windowHandle()->cast()->projectDocumentContext(); + auto model = projectDocumentContext->document()->model()->toQDspx(); + auto settings = Core::RuntimeInterface::settings(); + settings->beginGroup(staticMetaObject.className()); + auto defaultDir = settings->value(QStringLiteral("defaultImportExportDir"), QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation)).toString(); + settings->endGroup(); + auto path = QFileDialog::getSaveFileName(nullptr, tr("Export File"), defaultDir, converter->fileDialogFilters().join(";;")); + if (path.isEmpty()) { + qCInfo(lcFileImportExportAddOn) << "Export canceled: file not selected"; + return; + } + settings->beginGroup(staticMetaObject.className()); + settings->setValue(QStringLiteral("defaultImportExportDir"), QFileInfo(path).absolutePath()); + settings->endGroup(); + if (!converter->execExport(path, model, windowHandle()->window())) { + qCInfo(lcFileImportExportAddOn) << "Export failed or canceled"; + return; + } } bool FileImportExportAddOn::isHomeWindow() const { diff --git a/src/plugins/importexportmanager/internal/addon/FileImportExportAddOn.h b/src/plugins/importexportmanager/internal/addon/FileImportExportAddOn.h index cce7f2f7..8d595836 100644 --- a/src/plugins/importexportmanager/internal/addon/FileImportExportAddOn.h +++ b/src/plugins/importexportmanager/internal/addon/FileImportExportAddOn.h @@ -25,7 +25,7 @@ namespace ImportExportManager::Internal { static QList importConverters(const QString &path = {}); static QList exportConverters(); Q_INVOKABLE void execImport(FileConverter *converter) const; - Q_INVOKABLE void execExport(FileConverter *converter); + Q_INVOKABLE void execExport(FileConverter *converter) const; bool isHomeWindow() const; }; diff --git a/src/plugins/importexportmanager/internal/fileconverters/DspxFileExporter.cpp b/src/plugins/importexportmanager/internal/fileconverters/DspxFileExporter.cpp index e840cdf8..958f4b6c 100644 --- a/src/plugins/importexportmanager/internal/fileconverters/DspxFileExporter.cpp +++ b/src/plugins/importexportmanager/internal/fileconverters/DspxFileExporter.cpp @@ -1,7 +1,31 @@ #include "DspxFileExporter.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include + +#include + +#include + namespace ImportExportManager::Internal { + Q_STATIC_LOGGING_CATEGORY(lcDspxFileExporter, "diffscope.importexportmanager.dspxfileexporter"); + DspxFileExporter::DspxFileExporter(QObject *parent) : FileConverter(parent) { setName(tr("DSPX")); setDescription(tr("Export as DSPX file compatible with older versions of DiffScope and other editors")); @@ -12,8 +36,70 @@ namespace ImportExportManager::Internal { DspxFileExporter::~DspxFileExporter() = default; - bool DspxFileExporter::execExport(const QString &path, const QDspx::Model &model, QWindow *window) { - // TODO + bool DspxFileExporter::execExport(const QString &path, const QDspx::Model &model_, QWindow *window) { + Core::OpenSaveProjectFileScenario scenario; + scenario.setWindow(window); + QDialog dlg; + dlg.setWindowTitle(tr("Export DSPX")); + auto layout = new QVBoxLayout; + auto formLayout = new QFormLayout; + auto versionComboBox = new QComboBox; + versionComboBox->addItem(QStringLiteral("1.0.0"), QVariant::fromValue(QDspx::Model::Version::V1)); + formLayout->addRow(tr("Format version"), versionComboBox); + auto keepWorkspaceDataCheckBox = new QCheckBox(tr("Keep Workspace data")); + keepWorkspaceDataCheckBox->setChecked(true); + formLayout->addRow(keepWorkspaceDataCheckBox); + auto workspaceInstruction = new QLabel(tr("Workspace data is not part of the DSPX standard and is editor-specific. Different editors may interpret the same fields differently, which can potentially lead to unexpected behavior or compatibility issues.")); + workspaceInstruction->setWordWrap(true); + formLayout->addRow(workspaceInstruction); + layout->addLayout(formLayout); + layout->addStretch(); + auto buttonLayout = new QHBoxLayout; + buttonLayout->addStretch(); + auto button = new QPushButton(tr("Export")); + button->setDefault(true); + connect(button, &QPushButton::clicked, &dlg, &QDialog::accept); + buttonLayout->addWidget(button); + layout->addLayout(buttonLayout); + dlg.setLayout(layout); + dlg.setModal(true); + if (dlg.exec() != QDialog::Accepted) { + return false; + } + auto model = model_; + model.version = versionComboBox->currentData().value(); + qCDebug(lcDspxFileExporter) << "Keep Workspace data:" << keepWorkspaceDataCheckBox->isChecked(); + if (!keepWorkspaceDataCheckBox->isChecked()) { + model.content.workspace.clear(); + for (auto &track : model.content.tracks) { + track.workspace.clear(); + for (auto &clip : track.clips) { + clip->workspace.clear(); + if (clip->type != QDspx::Clip::Singing) { + continue; + } + auto singingClip = clip.staticCast(); + for (auto ¬e : singingClip->notes) { + note.workspace.clear(); + } + } + } + } + QFile file(path); + if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate)) { + qCCritical(lcDspxFileExporter) << "Failed to write file:" << path << file.errorString(); + scenario.showSaveFailMessageBox(path, file.errorString()); + return false; + } + QDspx::SerializationErrorList errors; + auto data = QDspx::Serializer::serialize(model, errors, QDspx::Serializer::CheckError); + auto bytesWritten = file.write(data); + if (bytesWritten != data.size()) { + qCCritical(lcDspxFileExporter) << "Failed to write file:" << path << file.errorString(); + scenario.showSaveFailMessageBox(path, file.errorString()); + return false; + } + file.close(); return true; } diff --git a/src/plugins/importexportmanager/internal/fileconverters/DspxFileImporter.cpp b/src/plugins/importexportmanager/internal/fileconverters/DspxFileImporter.cpp index 2f567d60..bee4e1c1 100644 --- a/src/plugins/importexportmanager/internal/fileconverters/DspxFileImporter.cpp +++ b/src/plugins/importexportmanager/internal/fileconverters/DspxFileImporter.cpp @@ -1,7 +1,29 @@ #include "DspxFileImporter.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include + +#include + +#include + namespace ImportExportManager::Internal { + Q_STATIC_LOGGING_CATEGORY(lcDspxFileImporter, "diffscope.importexportmanager.dspxfileimporter"); + DspxFileImporter::DspxFileImporter(QObject *parent) : FileConverter(parent) { setName(tr("DSPX")); setDescription(tr("Import DSPX file compatible with DiffScope")); @@ -13,7 +35,61 @@ namespace ImportExportManager::Internal { DspxFileImporter::~DspxFileImporter() = default; bool DspxFileImporter::execImport(const QString &path, QDspx::Model &model, QWindow *window) { - // TODO + Core::OpenSaveProjectFileScenario scenario; + scenario.setWindow(window); + QFile file(path); + if (!file.open(QIODevice::ReadOnly)) { + qCCritical(lcDspxFileImporter) << "Failed to read file:" << path << file.errorString(); + scenario.showOpenFailMessageBox(path, file.errorString()); + return false; + } + QDspx::SerializationErrorList errors; + model = QDspx::Serializer::deserialize(file.readAll(), errors); + file.close(); + if (errors.containsFatal() || errors.containsError()) { + qCCritical(lcDspxFileImporter) << "Failed to deserialize file:" << path; + scenario.showDeserializationFailMessageBox(path); + return false; + } + QDialog dlg; + dlg.setWindowTitle(tr("Import DSPX")); + auto layout = new QVBoxLayout; + auto keepWorkspaceDataCheckBox = new QCheckBox(tr("Keep Workspace data")); + keepWorkspaceDataCheckBox->setChecked(true); + layout->addWidget(keepWorkspaceDataCheckBox); + auto workspaceInstruction = new QLabel(tr("Workspace data is not part of the DSPX standard and is editor-specific. Different editors may interpret the same fields differently, which can potentially lead to unexpected behavior or compatibility issues.")); + workspaceInstruction->setWordWrap(true); + layout->addWidget(workspaceInstruction); + layout->addStretch(); + auto buttonLayout = new QHBoxLayout; + buttonLayout->addStretch(); + auto button = new QPushButton(tr("Import")); + button->setDefault(true); + connect(button, &QPushButton::clicked, &dlg, &QDialog::accept); + buttonLayout->addWidget(button); + layout->addLayout(buttonLayout); + dlg.setLayout(layout); + dlg.setModal(true); + if (dlg.exec() != QDialog::Accepted) { + return false; + } + qCDebug(lcDspxFileImporter) << "Keep Workspace data:" << keepWorkspaceDataCheckBox->isChecked(); + if (!keepWorkspaceDataCheckBox->isChecked()) { + model.content.workspace.clear(); + for (auto &track : model.content.tracks) { + track.workspace.clear(); + for (auto &clip : track.clips) { + clip->workspace.clear(); + if (clip->type != QDspx::Clip::Singing) { + continue; + } + auto singingClip = clip.staticCast(); + for (auto ¬e : singingClip->notes) { + note.workspace.clear(); + } + } + } + } return true; } diff --git a/src/plugins/importexportmanager/qml/actions/ImportActions.qml b/src/plugins/importexportmanager/qml/actions/ImportActions.qml index 23564be4..ef4ce304 100644 --- a/src/plugins/importexportmanager/qml/actions/ImportActions.qml +++ b/src/plugins/importexportmanager/qml/actions/ImportActions.qml @@ -25,9 +25,7 @@ ActionCollection { required property QtObject modelData text: modelData.name DescriptiveAction.statusTip: modelData.description - onTriggered: { - d.addOn.execImport(modelData) - } + onTriggered: Qt.callLater(() => d.addOn.execImport(modelData)) } } onObjectAdded: (index, object) => { diff --git a/src/plugins/importexportmanager/qml/actions/ProjectImportExportActions.qml b/src/plugins/importexportmanager/qml/actions/ProjectImportExportActions.qml index 05522870..c31003a6 100644 --- a/src/plugins/importexportmanager/qml/actions/ProjectImportExportActions.qml +++ b/src/plugins/importexportmanager/qml/actions/ProjectImportExportActions.qml @@ -24,9 +24,7 @@ ActionCollection { required property QtObject modelData text: modelData.name DescriptiveAction.statusTip: modelData.description - onTriggered: { - d.addOn.execExport(modelData) - } + onTriggered: Qt.callLater(() => d.addOn.execExport(modelData)) } } onObjectAdded: (index, object) => { diff --git a/src/plugins/importexportmanager/res/translations/org.diffscope.importexportmanager.ts b/src/plugins/importexportmanager/res/translations/org.diffscope.importexportmanager.ts new file mode 100644 index 00000000..ae1fb682 --- /dev/null +++ b/src/plugins/importexportmanager/res/translations/org.diffscope.importexportmanager.ts @@ -0,0 +1,195 @@ + + + + + + + + Main Menu + Main Menu + + + + Application + + + Version %1 + Version %1 + + + + Copyright © %1-%2 %3. All rights reserved. + Copyright © %1-%2 %3. All rights reserved. + + + + Application::ActionClass + + + + File + File + + + + Project + Project + + + + + Edit + Edit + + + + Panel + Panel + + + + Application::ActionText + + + &Import + &Import + + + + &Export + &Export + + + + Import as Tracks... + Import as Tracks... + + + + Copy Special... + Copy Special... + + + + Paste Special... + Paste Special... + + + + Import + Import + + + + Import Export Actions + Import Export Actions + + + + Import Actions + Import Actions + + + + ImportExportManager::Internal::DspxFileExporter + + + DSPX + DSPX + + + + Export as DSPX file compatible with older versions of DiffScope and other editors + Export as DSPX file compatible with older versions of DiffScope and other editors + + + + DiffScope Project Exchange Format (*.dspx) + DiffScope Project Exchange Format (*.dspx) + + + + Export DSPX + Export DSPX + + + + Format version + Format version + + + + Keep Workspace data + Keep Workspace data + + + + Workspace data is not part of the DSPX standard and is editor-specific. Different editors may interpret the same fields differently, which can potentially lead to unexpected behavior or compatibility issues. + Workspace data is not part of the DSPX standard and is editor-specific. Different editors may interpret the same fields differently, which can potentially lead to unexpected behavior or compatibility issues. + + + + Export + Export + + + + ImportExportManager::Internal::DspxFileImporter + + + DSPX + DSPX + + + + Import DSPX file compatible with DiffScope + Import DSPX file compatible with DiffScope + + + + DiffScope Project Exchange Format (*.dspx) + DiffScope Project Exchange Format (*.dspx) + + + + Import DSPX + Import DSPX + + + + Keep Workspace data + Keep Workspace data + + + + Workspace data is not part of the DSPX standard and is editor-specific. Different editors may interpret the same fields differently, which can potentially lead to unexpected behavior or compatibility issues. + Workspace data is not part of the DSPX standard and is editor-specific. Different editors may interpret the same fields differently, which can potentially lead to unexpected behavior or compatibility issues. + + + + Import + Import + + + + ImportExportManager::Internal::FileImportExportAddOn + + + Import File + Import File + + + + Export File + Export File + + + + ImportPanel + + + Select a format to import + Select a format to import + + + diff --git a/src/plugins/importexportmanager/res/translations/org.diffscope.importexportmanager_en_US.ts b/src/plugins/importexportmanager/res/translations/org.diffscope.importexportmanager_en_US.ts index b262e3bb..86cea7e8 100644 --- a/src/plugins/importexportmanager/res/translations/org.diffscope.importexportmanager_en_US.ts +++ b/src/plugins/importexportmanager/res/translations/org.diffscope.importexportmanager_en_US.ts @@ -1,4 +1,195 @@ + + + + + Main Menu + + + + + Application + + + Version %1 + + + + + Copyright © %1-%2 %3. All rights reserved. + + + + + Application::ActionClass + + + + File + + + + + Project + + + + + + Edit + + + + + Panel + + + + + Application::ActionText + + + &Import + + + + + &Export + + + + + Import as Tracks... + + + + + Copy Special... + + + + + Paste Special... + + + + + Import + + + + + Import Export Actions + + + + + Import Actions + + + + + ImportExportManager::Internal::DspxFileExporter + + + DSPX + + + + + Export as DSPX file compatible with older versions of DiffScope and other editors + + + + + DiffScope Project Exchange Format (*.dspx) + + + + + Export DSPX + + + + + Format version + + + + + Keep Workspace data + + + + + Workspace data is not part of the DSPX standard and is editor-specific. Different editors may interpret the same fields differently, which can potentially lead to unexpected behavior or compatibility issues. + + + + + Export + + + + + ImportExportManager::Internal::DspxFileImporter + + + DSPX + + + + + Import DSPX file compatible with DiffScope + + + + + DiffScope Project Exchange Format (*.dspx) + + + + + Import DSPX + + + + + Keep Workspace data + + + + + Workspace data is not part of the DSPX standard and is editor-specific. Different editors may interpret the same fields differently, which can potentially lead to unexpected behavior or compatibility issues. + + + + + Import + + + + + ImportExportManager::Internal::FileImportExportAddOn + + + Import File + + + + + Export File + + + + + ImportPanel + + + Select a format to import + + + diff --git a/src/plugins/importexportmanager/res/translations/org.diffscope.importexportmanager_ja_JP.ts b/src/plugins/importexportmanager/res/translations/org.diffscope.importexportmanager_ja_JP.ts index 85f2f934..136c8b02 100644 --- a/src/plugins/importexportmanager/res/translations/org.diffscope.importexportmanager_ja_JP.ts +++ b/src/plugins/importexportmanager/res/translations/org.diffscope.importexportmanager_ja_JP.ts @@ -1,4 +1,195 @@ + + + + + Main Menu + Main Menu + + + + Application + + + Version %1 + Version %1 + + + + Copyright © %1-%2 %3. All rights reserved. + Copyright © %1-%2 %3. All rights reserved. + + + + Application::ActionClass + + + + File + File + + + + Project + Project + + + + + Edit + Edit + + + + Panel + Panel + + + + Application::ActionText + + + &Import + &Import + + + + &Export + &Export + + + + Import as Tracks... + Import as Tracks... + + + + Copy Special... + Copy Special... + + + + Paste Special... + Paste Special... + + + + Import + Import + + + + Import Export Actions + Import Export Actions + + + + Import Actions + Import Actions + + + + ImportExportManager::Internal::DspxFileExporter + + + DSPX + DSPX + + + + Export as DSPX file compatible with older versions of DiffScope and other editors + Export as DSPX file compatible with older versions of DiffScope and other editors + + + + DiffScope Project Exchange Format (*.dspx) + DiffScope Project Exchange Format (*.dspx) + + + + Export DSPX + Export DSPX + + + + Format version + Format version + + + + Keep Workspace data + Keep Workspace data + + + + Workspace data is not part of the DSPX standard and is editor-specific. Different editors may interpret the same fields differently, which can potentially lead to unexpected behavior or compatibility issues. + Workspace data is not part of the DSPX standard and is editor-specific. Different editors may interpret the same fields differently, which can potentially lead to unexpected behavior or compatibility issues. + + + + Export + Export + + + + ImportExportManager::Internal::DspxFileImporter + + + DSPX + DSPX + + + + Import DSPX file compatible with DiffScope + Import DSPX file compatible with DiffScope + + + + DiffScope Project Exchange Format (*.dspx) + DiffScope Project Exchange Format (*.dspx) + + + + Import DSPX + Import DSPX + + + + Keep Workspace data + Keep Workspace data + + + + Workspace data is not part of the DSPX standard and is editor-specific. Different editors may interpret the same fields differently, which can potentially lead to unexpected behavior or compatibility issues. + Workspace data is not part of the DSPX standard and is editor-specific. Different editors may interpret the same fields differently, which can potentially lead to unexpected behavior or compatibility issues. + + + + Import + Import + + + + ImportExportManager::Internal::FileImportExportAddOn + + + Import File + Import File + + + + Export File + Export File + + + + ImportPanel + + + Select a format to import + Select a format to import + + diff --git a/src/plugins/importexportmanager/res/translations/org.diffscope.importexportmanager_zh_CN.ts b/src/plugins/importexportmanager/res/translations/org.diffscope.importexportmanager_zh_CN.ts index 9c60864f..a16da2bb 100644 --- a/src/plugins/importexportmanager/res/translations/org.diffscope.importexportmanager_zh_CN.ts +++ b/src/plugins/importexportmanager/res/translations/org.diffscope.importexportmanager_zh_CN.ts @@ -1,4 +1,195 @@ + + + + + Main Menu + 主菜单 + + + + Application + + + Version %1 + 版本 %1 + + + + Copyright © %1-%2 %3. All rights reserved. + 版权所有 © %1-%2 %3。保留所有权利。 + + + + Application::ActionClass + + + + File + 文件 + + + + Project + 工程 + + + + + Edit + 编辑 + + + + Panel + 面板 + + + + Application::ActionText + + + &Import + 导入(&I) + + + + &Export + 导出(&E) + + + + Import as Tracks... + 导入为轨道... + + + + Copy Special... + 选择性复制... + + + + Paste Special... + 选择性粘贴... + + + + Import + 导入 + + + + Import Export Actions + 导入导出操作 + + + + Import Actions + 导入操作 + + + + ImportExportManager::Internal::DspxFileExporter + + + DSPX + DSPX + + + + Export as DSPX file compatible with older versions of DiffScope and other editors + 导出为与旧版 DiffScope 和其他编辑器兼容的 DSPX 文件 + + + + DiffScope Project Exchange Format (*.dspx) + DiffScope 工程交换格式 (*.dspx) + + + + Export DSPX + 导出 DSPX + + + + Format version + 格式版本 + + + + Keep Workspace data + 保留“Workspace”数据 + + + + Workspace data is not part of the DSPX standard and is editor-specific. Different editors may interpret the same fields differently, which can potentially lead to unexpected behavior or compatibility issues. + “Workspace”数据不是 DSPX 标准的一部分,而特定于编辑器。不同的编辑器可能使用不同的方式解读同一字段,这会导致潜在的意外行为或兼容性问题。 + + + + Export + 导出 + + + + ImportExportManager::Internal::DspxFileImporter + + + DSPX + DSPX + + + + Import DSPX file compatible with DiffScope + 导入适用于 DiffScope 的 DSPX 文件 + + + + DiffScope Project Exchange Format (*.dspx) + DiffScope 工程交换格式 (*.dspx) + + + + Import DSPX + 导入 DSPX + + + + Keep Workspace data + 保留“Workspace”数据 + + + + Workspace data is not part of the DSPX standard and is editor-specific. Different editors may interpret the same fields differently, which can potentially lead to unexpected behavior or compatibility issues. + “Workspace”数据不是 DSPX 标准的一部分,而特定于编辑器。不同的编辑器可能使用不同的方式解读同一字段,这会导致潜在的意外行为或兼容性问题。 + + + + Import + 导入 + + + + ImportExportManager::Internal::FileImportExportAddOn + + + Import File + 导入文件 + + + + Export File + 导出文件 + + + + ImportPanel + + + Select a format to import + 选择要导入的格式 + + diff --git a/src/plugins/importexportmanager/res/translations/org.diffscope.importexportmanager_zh_TW.ts b/src/plugins/importexportmanager/res/translations/org.diffscope.importexportmanager_zh_TW.ts index 2316ddac..0466e045 100644 --- a/src/plugins/importexportmanager/res/translations/org.diffscope.importexportmanager_zh_TW.ts +++ b/src/plugins/importexportmanager/res/translations/org.diffscope.importexportmanager_zh_TW.ts @@ -1,4 +1,195 @@ + + + + + Main Menu + Main Menu + + + + Application + + + Version %1 + Version %1 + + + + Copyright © %1-%2 %3. All rights reserved. + Copyright © %1-%2 %3. All rights reserved. + + + + Application::ActionClass + + + + File + File + + + + Project + Project + + + + + Edit + Edit + + + + Panel + Panel + + + + Application::ActionText + + + &Import + &Import + + + + &Export + &Export + + + + Import as Tracks... + Import as Tracks... + + + + Copy Special... + Copy Special... + + + + Paste Special... + Paste Special... + + + + Import + Import + + + + Import Export Actions + Import Export Actions + + + + Import Actions + Import Actions + + + + ImportExportManager::Internal::DspxFileExporter + + + DSPX + DSPX + + + + Export as DSPX file compatible with older versions of DiffScope and other editors + Export as DSPX file compatible with older versions of DiffScope and other editors + + + + DiffScope Project Exchange Format (*.dspx) + DiffScope Project Exchange Format (*.dspx) + + + + Export DSPX + Export DSPX + + + + Format version + Format version + + + + Keep Workspace data + Keep Workspace data + + + + Workspace data is not part of the DSPX standard and is editor-specific. Different editors may interpret the same fields differently, which can potentially lead to unexpected behavior or compatibility issues. + Workspace data is not part of the DSPX standard and is editor-specific. Different editors may interpret the same fields differently, which can potentially lead to unexpected behavior or compatibility issues. + + + + Export + Export + + + + ImportExportManager::Internal::DspxFileImporter + + + DSPX + DSPX + + + + Import DSPX file compatible with DiffScope + Import DSPX file compatible with DiffScope + + + + DiffScope Project Exchange Format (*.dspx) + DiffScope Project Exchange Format (*.dspx) + + + + Import DSPX + Import DSPX + + + + Keep Workspace data + Keep Workspace data + + + + Workspace data is not part of the DSPX standard and is editor-specific. Different editors may interpret the same fields differently, which can potentially lead to unexpected behavior or compatibility issues. + Workspace data is not part of the DSPX standard and is editor-specific. Different editors may interpret the same fields differently, which can potentially lead to unexpected behavior or compatibility issues. + + + + Import + Import + + + + ImportExportManager::Internal::FileImportExportAddOn + + + Import File + Import File + + + + Export File + Export File + + + + ImportPanel + + + Select a format to import + Select a format to import + + diff --git a/src/plugins/libresvipformatconverter/internal/LibreSVIPFormatConverterPlugin.cpp b/src/plugins/libresvipformatconverter/internal/LibreSVIPFormatConverterPlugin.cpp index c01b2343..14e0306c 100644 --- a/src/plugins/libresvipformatconverter/internal/LibreSVIPFormatConverterPlugin.cpp +++ b/src/plugins/libresvipformatconverter/internal/LibreSVIPFormatConverterPlugin.cpp @@ -21,8 +21,8 @@ namespace LibreSVIPFormatConverter::Internal { bool LibreSVIPFormatConverterPlugin::initialize(const QStringList &arguments, QString *errorMessage) { Core::RuntimeInterface::translationManager()->addTranslationPath(pluginSpec()->location() + QStringLiteral("/translations")); - ImportExportManager::ConverterCollection::instance()->addObject(new LibreSVIPFileImporter); - ImportExportManager::ConverterCollection::instance()->addObject(new LibreSVIPFileExporter); + ImportExportManager::ConverterCollection::addFileConverter(new LibreSVIPFileImporter); + ImportExportManager::ConverterCollection::addFileConverter(new LibreSVIPFileExporter); return true; } diff --git a/src/plugins/libresvipformatconverter/res/translations/org.diffscope.libresvipformatconverter.ts b/src/plugins/libresvipformatconverter/res/translations/org.diffscope.libresvipformatconverter.ts new file mode 100644 index 00000000..49484788 --- /dev/null +++ b/src/plugins/libresvipformatconverter/res/translations/org.diffscope.libresvipformatconverter.ts @@ -0,0 +1,43 @@ + + + + + Application + + + Version %1 + Version %1 + + + + Copyright © %1-%2 %3. All rights reserved. + Copyright © %1-%2 %3. All rights reserved. + + + + LibreSVIPFormatConverter::Internal::LibreSVIPFileExporter + + + Multiple formats (export via LibreSVIP) + Multiple formats (export via LibreSVIP) + + + + Export as project files of other editors (such as VOCALOID, OpenUtau, etc.) via LibreSVIP + Export as project files of other editors (such as VOCALOID, OpenUtau, etc.) via LibreSVIP + + + + LibreSVIPFormatConverter::Internal::LibreSVIPFileImporter + + + Multiple formats (import via LibreSVIP) + Multiple formats (import via LibreSVIP) + + + + Import project files of other editors (such as VOCALOID, OpenUtau, etc.) via LibreSVIP + Import project files of other editors (such as VOCALOID, OpenUtau, etc.) via LibreSVIP + + + diff --git a/src/plugins/libresvipformatconverter/res/translations/org.diffscope.libresvipformatconverter_en_US.ts b/src/plugins/libresvipformatconverter/res/translations/org.diffscope.libresvipformatconverter_en_US.ts index b262e3bb..d87b5624 100644 --- a/src/plugins/libresvipformatconverter/res/translations/org.diffscope.libresvipformatconverter_en_US.ts +++ b/src/plugins/libresvipformatconverter/res/translations/org.diffscope.libresvipformatconverter_en_US.ts @@ -1,4 +1,43 @@ + + Application + + + Version %1 + + + + + Copyright © %1-%2 %3. All rights reserved. + + + + + LibreSVIPFormatConverter::Internal::LibreSVIPFileExporter + + + Multiple formats (export via LibreSVIP) + + + + + Export as project files of other editors (such as VOCALOID, OpenUtau, etc.) via LibreSVIP + + + + + LibreSVIPFormatConverter::Internal::LibreSVIPFileImporter + + + Multiple formats (import via LibreSVIP) + + + + + Import project files of other editors (such as VOCALOID, OpenUtau, etc.) via LibreSVIP + + + diff --git a/src/plugins/libresvipformatconverter/res/translations/org.diffscope.libresvipformatconverter_ja_JP.ts b/src/plugins/libresvipformatconverter/res/translations/org.diffscope.libresvipformatconverter_ja_JP.ts index 85f2f934..c75a2f70 100644 --- a/src/plugins/libresvipformatconverter/res/translations/org.diffscope.libresvipformatconverter_ja_JP.ts +++ b/src/plugins/libresvipformatconverter/res/translations/org.diffscope.libresvipformatconverter_ja_JP.ts @@ -1,4 +1,43 @@ + + Application + + + Version %1 + Version %1 + + + + Copyright © %1-%2 %3. All rights reserved. + Copyright © %1-%2 %3. All rights reserved. + + + + LibreSVIPFormatConverter::Internal::LibreSVIPFileExporter + + + Multiple formats (export via LibreSVIP) + Multiple formats (export via LibreSVIP) + + + + Export as project files of other editors (such as VOCALOID, OpenUtau, etc.) via LibreSVIP + Export as project files of other editors (such as VOCALOID, OpenUtau, etc.) via LibreSVIP + + + + LibreSVIPFormatConverter::Internal::LibreSVIPFileImporter + + + Multiple formats (import via LibreSVIP) + Multiple formats (import via LibreSVIP) + + + + Import project files of other editors (such as VOCALOID, OpenUtau, etc.) via LibreSVIP + Import project files of other editors (such as VOCALOID, OpenUtau, etc.) via LibreSVIP + + diff --git a/src/plugins/libresvipformatconverter/res/translations/org.diffscope.libresvipformatconverter_zh_CN.ts b/src/plugins/libresvipformatconverter/res/translations/org.diffscope.libresvipformatconverter_zh_CN.ts index 9c60864f..7064c8ba 100644 --- a/src/plugins/libresvipformatconverter/res/translations/org.diffscope.libresvipformatconverter_zh_CN.ts +++ b/src/plugins/libresvipformatconverter/res/translations/org.diffscope.libresvipformatconverter_zh_CN.ts @@ -1,4 +1,43 @@ + + Application + + + Version %1 + 版本 %1 + + + + Copyright © %1-%2 %3. All rights reserved. + 版权所有 © %1-%2 %3。保留所有权利。 + + + + LibreSVIPFormatConverter::Internal::LibreSVIPFileExporter + + + Multiple formats (export via LibreSVIP) + 多种格式(通过 LibreSVIP 导出) + + + + Export as project files of other editors (such as VOCALOID, OpenUtau, etc.) via LibreSVIP + 通过 LibreSVIP 导出为其他编辑器(例如 VOCALOID、OpenUtau 等)的工程文件 + + + + LibreSVIPFormatConverter::Internal::LibreSVIPFileImporter + + + Multiple formats (import via LibreSVIP) + 多种格式(通过 LibreSVIP 导入) + + + + Import project files of other editors (such as VOCALOID, OpenUtau, etc.) via LibreSVIP + 通过 LibreSVIP 导入其他编辑器(例如 VOCALOID、OpenUtau 等)的工程文件 + + diff --git a/src/plugins/libresvipformatconverter/res/translations/org.diffscope.libresvipformatconverter_zh_TW.ts b/src/plugins/libresvipformatconverter/res/translations/org.diffscope.libresvipformatconverter_zh_TW.ts index 2316ddac..f29d0625 100644 --- a/src/plugins/libresvipformatconverter/res/translations/org.diffscope.libresvipformatconverter_zh_TW.ts +++ b/src/plugins/libresvipformatconverter/res/translations/org.diffscope.libresvipformatconverter_zh_TW.ts @@ -1,4 +1,43 @@ + + Application + + + Version %1 + Version %1 + + + + Copyright © %1-%2 %3. All rights reserved. + Copyright © %1-%2 %3. All rights reserved. + + + + LibreSVIPFormatConverter::Internal::LibreSVIPFileExporter + + + Multiple formats (export via LibreSVIP) + Multiple formats (export via LibreSVIP) + + + + Export as project files of other editors (such as VOCALOID, OpenUtau, etc.) via LibreSVIP + Export as project files of other editors (such as VOCALOID, OpenUtau, etc.) via LibreSVIP + + + + LibreSVIPFormatConverter::Internal::LibreSVIPFileImporter + + + Multiple formats (import via LibreSVIP) + Multiple formats (import via LibreSVIP) + + + + Import project files of other editors (such as VOCALOID, OpenUtau, etc.) via LibreSVIP + Import project files of other editors (such as VOCALOID, OpenUtau, etc.) via LibreSVIP + + diff --git a/src/plugins/maintenance/CMakeLists.txt b/src/plugins/maintenance/CMakeLists.txt index b5cb78d3..6ecefda5 100644 --- a/src/plugins/maintenance/CMakeLists.txt +++ b/src/plugins/maintenance/CMakeLists.txt @@ -21,6 +21,8 @@ diffscope_add_builtin_plugin(${PROJECT_NAME} QAKCore QAKQuick coreplugin + opendspx::model + opendspx::serializer INCLUDE_PRIVATE internal/** ) diff --git a/src/plugins/maintenance/internal/MaintenancePlugin.cpp b/src/plugins/maintenance/internal/MaintenancePlugin.cpp index 628414d6..c1252de4 100644 --- a/src/plugins/maintenance/internal/MaintenancePlugin.cpp +++ b/src/plugins/maintenance/internal/MaintenancePlugin.cpp @@ -16,6 +16,7 @@ #include #include +#include #include static auto getMaintenanceActionExtension() { @@ -35,6 +36,7 @@ namespace Maintenance { Core::CoreInterface::actionRegistry()->addExtension(::getMaintenanceActionExtension()); Core::HomeWindowInterfaceRegistry::instance()->attach(); Core::ProjectWindowInterfaceRegistry::instance()->attach(); + Core::ProjectWindowInterfaceRegistry::instance()->attach(); new ApplicationUpdateChecker(this); diff --git a/src/plugins/maintenance/internal/addons/ViewJsonAddOn.cpp b/src/plugins/maintenance/internal/addons/ViewJsonAddOn.cpp new file mode 100644 index 00000000..8c1623f7 --- /dev/null +++ b/src/plugins/maintenance/internal/addons/ViewJsonAddOn.cpp @@ -0,0 +1,112 @@ +#include "ViewJsonAddOn.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include +#include + +#include + +#include +#include +#include + +namespace Maintenance { + + Q_STATIC_LOGGING_CATEGORY(lcViewJsonAddOn, "diffscope.maintenance.viewjsonaddon") + + ViewJsonAddOn::ViewJsonAddOn(QObject *parent) : Core::WindowInterfaceAddOn(parent) { + } + + ViewJsonAddOn::~ViewJsonAddOn() = default; + + void ViewJsonAddOn::initialize() { + generateSessionId(); + + auto windowInterface = windowHandle()->cast(); + QQmlComponent component(Core::RuntimeInterface::qmlEngine(), "DiffScope.Maintenance", "ViewJsonAddOnActions"); + if (component.isError()) { + qFatal() << component.errorString(); + } + auto object = component.createWithInitialProperties({{"addOn", QVariant::fromValue(this)}}); + object->setParent(this); + QMetaObject::invokeMethod(object, "registerToContext", windowInterface->actionContext()); + } + + void ViewJsonAddOn::extensionsInitialized() { + } + + bool ViewJsonAddOn::delayedInitialize() { + return WindowInterfaceAddOn::delayedInitialize(); + } + + void ViewJsonAddOn::generateSessionId() { + auto uuidBytes = QUuid::createUuid().toRfc4122(); + auto base64 = uuidBytes.toBase64(QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals); + m_sessionId = QString::fromLatin1(base64); + } + + QByteArray ViewJsonAddOn::serializeJson() const { + auto model = windowHandle()->cast()->projectDocumentContext()->document()->model()->toQDspx(); + QDspx::SerializationErrorList errors; + auto data = QDspx::Serializer::serialize(model, errors, QDspx::Serializer::CheckError); + data = QJsonDocument::fromJson(data).toJson(QJsonDocument::Indented); + return data; + } + + void ViewJsonAddOn::generateJsonFileAndOpen() { + const auto data = serializeJson(); + + auto windowInterface = windowHandle()->cast(); + auto attemptWrite = [&](const QString &sessionId) -> std::optional { + auto path = QDir(QDir::tempPath()).filePath(sessionId + QStringLiteral(".json")); + QSaveFile file(path); + if (!file.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate)) { + qCWarning(lcViewJsonAddOn) << "Failed to open the file for writing" << path << file.errorString(); + return std::nullopt; + } + if (file.write(data) == -1) { + qCWarning(lcViewJsonAddOn) << "Failed to write the file" << path << file.errorString(); + return std::nullopt; + } + if (!file.commit()) { + qCWarning(lcViewJsonAddOn) << "Failed to commit the file" << path << file.errorString(); + return std::nullopt; + } + return path; + }; + + auto path = attemptWrite(m_sessionId); + if (!path) { + generateSessionId(); + path = attemptWrite(m_sessionId); + if (!path) { + auto errorPath = QDir(QDir::tempPath()).filePath(m_sessionId + QStringLiteral(".json")); + SVS::MessageBox::critical( + Core::RuntimeInterface::qmlEngine(), + windowInterface ? windowInterface->window() : nullptr, + tr("Failed to create JSON file"), + tr("Could not create temporary JSON file:\n\n%1").arg(QDir::toNativeSeparators(errorPath)) + ); + return; + } + } + + QDesktopServices::openUrl(QUrl::fromLocalFile(*path)); + } + +} diff --git a/src/plugins/maintenance/internal/addons/ViewJsonAddOn.h b/src/plugins/maintenance/internal/addons/ViewJsonAddOn.h new file mode 100644 index 00000000..552fcfac --- /dev/null +++ b/src/plugins/maintenance/internal/addons/ViewJsonAddOn.h @@ -0,0 +1,33 @@ +#ifndef DIFFSCOPE_MAINTENANCE_VIEWJSONADDON_H +#define DIFFSCOPE_MAINTENANCE_VIEWJSONADDON_H + +#include + +#include + +namespace Maintenance { + + class ViewJsonAddOn : public Core::WindowInterfaceAddOn { + Q_OBJECT + QML_ELEMENT + QML_UNCREATABLE("") + public: + explicit ViewJsonAddOn(QObject *parent = nullptr); + ~ViewJsonAddOn() override; + + void initialize() override; + void extensionsInitialized() override; + bool delayedInitialize() override; + + Q_INVOKABLE void generateJsonFileAndOpen(); + + private: + void generateSessionId(); + QByteArray serializeJson() const; + + QString m_sessionId; + }; + +} + +#endif //DIFFSCOPE_MAINTENANCE_VIEWJSONADDON_H diff --git a/src/plugins/maintenance/qml/ViewJsonAddOnActions.qml b/src/plugins/maintenance/qml/ViewJsonAddOnActions.qml new file mode 100644 index 00000000..0a469714 --- /dev/null +++ b/src/plugins/maintenance/qml/ViewJsonAddOnActions.qml @@ -0,0 +1,25 @@ +import QtQml +import QtQuick +import QtQuick.Controls + +import SVSCraft.UIComponents +import SVSCraft.UIComponents.impl + +import QActionKit + +import DiffScope.Maintenance + +ActionCollection { + id: d + + required property ViewJsonAddOn addOn + + ActionItem { + actionId: "org.diffscope.maintenance.diagnosis.viewJson" + Action { + onTriggered: () => { + Qt.callLater(() => d.addOn.generateJsonFileAndOpen()) + } + } + } +} diff --git a/src/plugins/maintenance/res/org.diffscope.maintenance_actions.xml b/src/plugins/maintenance/res/org.diffscope.maintenance_actions.xml index 71a4bb10..1df6074b 100644 --- a/src/plugins/maintenance/res/org.diffscope.maintenance_actions.xml +++ b/src/plugins/maintenance/res/org.diffscope.maintenance_actions.xml @@ -19,6 +19,7 @@ + @@ -27,12 +28,15 @@ - + - - - - + + + + + + + @@ -48,7 +52,7 @@ - + diff --git a/src/plugins/maintenance/res/translations/org.diffscope.maintenance.ts b/src/plugins/maintenance/res/translations/org.diffscope.maintenance.ts new file mode 100644 index 00000000..db80d2b5 --- /dev/null +++ b/src/plugins/maintenance/res/translations/org.diffscope.maintenance.ts @@ -0,0 +1,252 @@ + + + + + + + + Main Menu + Main Menu + + + + Application + + + Version %1 + Version %1 + + + + Copyright © %1-%2 %3. All rights reserved. + Copyright © %1-%2 %3. All rights reserved. + + + + Application::ActionClass + + + + + + + Diagnosis + Diagnosis + + + + + + Get Involved + Get Involved + + + + + Update + Update + + + + Application::ActionDescription + + + View the JSON code of the current project in the default editor + View the JSON code of the current project in the default editor + + + + Report bugs, request new features, or ask for support + Report bugs, request new features, or ask for support + + + + Application::ActionText + + + Generate Diagnostic Report... + Generate Diagnostic Report... + + + + Open Logs Directory + Open Logs Directory + + + + Open Data Directory + Open Data Directory + + + + Open Plugins Directory + Open Plugins Directory + + + + View JSON Code of Project + View JSON Code of Project + + + + Report Issue + Report Issue + + + + Contribute to DiffScope + Contribute to DiffScope + + + + Join the Community + Join the Community + + + + Check for Updates + Check for Updates + + + + View Release Log + View Release Log + + + + Diagnosis + Diagnosis + + + + Open Directory Actions + Open Directory Actions + + + Diagnosis Actions + Diagnosis Actions + + + + Get Involved Actions + Get Involved Actions + + + + Update Actions + Update Actions + + + + Maintenance::MaintenanceAddOn + + Important Notice + Important Notice + + + + Disclaimer + Disclaimer + + + + <p>The diagnostic report may contain <b>sensitive information</b> (e.g., usernames, hostnames, network addresses, personalized configurations).</p> +<p>If you do not wish to make such information public, please <b>do not</b> share this report on public platforms like GitHub Issues or online chat rooms.</p> +<p>You may follow the feedback guidelines to send the report privately via email to the DiffScope development team.</p> +<p>Please note that the DiffScope development team <b>does not guarantee</b> that the report will not be disclosed to third parties. It is your decision whether to send the report.</p> + + <p>The diagnostic report may contain <b>sensitive information</b> (e.g., usernames, hostnames, network addresses, personalized configurations).</p> +<p>If you do not wish to make such information public, please <b>do not</b> share this report on public platforms like GitHub Issues or online chat rooms.</p> +<p>You may follow the feedback guidelines to send the report privately via email to the DiffScope development team.</p> +<p>Please note that the DiffScope development team <b>does not guarantee</b> that the report will not be disclosed to third parties. It is your decision whether to send the report.</p> + + + + + Save Diagnostic Report + Save Diagnostic Report + + + + Error + Error + + + + Failed to open the file for writing: + +%1 + Failed to open the file for writing: + +%1 + + + + Diagnostic Report Generated + Diagnostic Report Generated + + + + The identifier of report is: + The identifier of report is: + + + + Maintenance::UpdatePage + + + Update + Update + + + + Configure update settings for %1 + Configure update settings for %1 + + + + Maintenance::ViewJsonAddOn + + + Failed to create JSON file + Failed to create JSON file + + + + Could not create temporary JSON file: + +%1 + Could not create temporary JSON file: + +%1 + + + + UpdatePage + + + Update Settings + Update Settings + + + + Check for updates on startup + Check for updates on startup + + + + Type of update to check for + Type of update to check for + + + + Stable + Stable + + + + Beta + Beta + + + diff --git a/src/plugins/maintenance/res/translations/org.diffscope.maintenance_en_US.ts b/src/plugins/maintenance/res/translations/org.diffscope.maintenance_en_US.ts index 08fa3bb1..56a4169c 100644 --- a/src/plugins/maintenance/res/translations/org.diffscope.maintenance_en_US.ts +++ b/src/plugins/maintenance/res/translations/org.diffscope.maintenance_en_US.ts @@ -1,198 +1,250 @@ - - + + - - Main Menu - Main Menu + + Main Menu + Main Menu - - + + + Application + + + Version %1 + + + + + Copyright © %1-%2 %3. All rights reserved. + + + + Application::ActionClass - - - - - Diagnosis - Diagnosis + + + + + + Diagnosis + Diagnosis - - - - Get Involved - Get Involved + + + + Get Involved + Get Involved - - - Update - Update + + + Update + Update - - + + Application::ActionDescription - - Report bugs, request new features, or ask for support - Report bugs, request new features, or ask for support + + View the JSON code of the current project in the default editor + + + + + Report bugs, request new features, or ask for support + Report bugs, request new features, or ask for support - - + + Application::ActionText - - Generate Diagnostic Report... - Generate Diagnostic Report... + + Generate Diagnostic Report... + Generate Diagnostic Report... + + + + Open Logs Directory + Open Logs Directory + + + + Open Data Directory + Open Data Directory - - Open Logs Directory - Open Logs Directory + + Open Plugins Directory + Open Plugins Directory - - Open Data Directory - Open Data Directory + + View JSON Code of Project + - - Open Plugins Directory - Open Plugins Directory + + Report Issue + Report Issue - - Report Issue - Report Issue + + Contribute to DiffScope + Contribute to DiffScope - - Contribute to DiffScope - Contribute to DiffScope + + Join the Community + Join the Community - - Join the Community - Join the Community + + Check for Updates + Check for Updates - - Check for Updates - Check for Updates + + View Release Log + View Release Log - - View Release Log - View Release Log + + Diagnosis + Diagnosis - - Diagnosis Actions - Diagnosis Actions + + Open Directory Actions + - - Get Involved Actions - Get Involved Actions + Diagnosis Actions + Diagnosis Actions - - Update Actions - Update Actions + + Get Involved Actions + Get Involved Actions - - + + + Update Actions + Update Actions + + + Maintenance::MaintenanceAddOn - - Important Notice - Important Notice + Important Notice + Important Notice - - <p>The diagnostic report may contain <b>sensitive information</b> (e.g., usernames, hostnames, network addresses, personalized configurations).</p> + + Disclaimer + + + + + <p>The diagnostic report may contain <b>sensitive information</b> (e.g., usernames, hostnames, network addresses, personalized configurations).</p> <p>If you do not wish to make such information public, please <b>do not</b> share this report on public platforms like GitHub Issues or online chat rooms.</p> <p>You may follow the feedback guidelines to send the report privately via email to the DiffScope development team.</p> <p>Please note that the DiffScope development team <b>does not guarantee</b> that the report will not be disclosed to third parties. It is your decision whether to send the report.</p> - <p>The diagnostic report may contain <b>sensitive information</b> (e.g., usernames, hostnames, network addresses, personalized configurations).</p> + <p>The diagnostic report may contain <b>sensitive information</b> (e.g., usernames, hostnames, network addresses, personalized configurations).</p> <p>If you do not wish to make such information public, please <b>do not</b> share this report on public platforms like GitHub Issues or online chat rooms.</p> <p>You may follow the feedback guidelines to send the report privately via email to the DiffScope development team.</p> <p>Please note that the DiffScope development team <b>does not guarantee</b> that the report will not be disclosed to third parties. It is your decision whether to send the report.</p> - - Save Diagnostic Report - Save Diagnostic Report + + Save Diagnostic Report + Save Diagnostic Report - - Error - Error + + Error + Error - - Failed to open the file for writing: + + Failed to open the file for writing: %1 - Failed to open the file for writing: + Failed to open the file for writing: %1 - - Diagnostic Report Generated - Diagnostic Report Generated + + Diagnostic Report Generated + Diagnostic Report Generated - - The identifier of report is: - The identifier of report is: + + The identifier of report is: + The identifier of report is: - - + + Maintenance::UpdatePage - - Update - Update + + Update + Update + + + + Configure update settings for %1 + Configure update settings for %1 + + + + Maintenance::ViewJsonAddOn + + + Failed to create JSON file + - - Configure update settings for %1 - Configure update settings for %1 + + Could not create temporary JSON file: + +%1 + - - + + UpdatePage - - Update Settings - Update Settings + + Update Settings + Update Settings - - Check for updates on startup - Check for updates on startup + + Check for updates on startup + Check for updates on startup - - Type of update to check for - Type of update to check for + + Type of update to check for + Type of update to check for - - Stable - Stable + + Stable + Stable - - Beta - Beta + + Beta + Beta - + diff --git a/src/plugins/maintenance/res/translations/org.diffscope.maintenance_ja_JP.ts b/src/plugins/maintenance/res/translations/org.diffscope.maintenance_ja_JP.ts index d074d896..a1a92cb2 100644 --- a/src/plugins/maintenance/res/translations/org.diffscope.maintenance_ja_JP.ts +++ b/src/plugins/maintenance/res/translations/org.diffscope.maintenance_ja_JP.ts @@ -4,31 +4,45 @@ - + Main Menu Main Menu + + Application + + + Version %1 + Version %1 + + + + Copyright © %1-%2 %3. All rights reserved. + Copyright © %1-%2 %3. All rights reserved. + + Application::ActionClass - - - - + + + + + Diagnosis Diagnosis - - - + + + Get Involved Get Involved - - + + Update Update @@ -36,7 +50,12 @@ Application::ActionDescription - + + View the JSON code of the current project in the default editor + View the JSON code of the current project in the default editor + + + Report bugs, request new features, or ask for support Report bugs, request new features, or ask for support @@ -44,62 +63,76 @@ Application::ActionText - + Generate Diagnostic Report... Generate Diagnostic Report... - + Open Logs Directory Open Logs Directory - + Open Data Directory Open Data Directory - + Open Plugins Directory Open Plugins Directory - + + View JSON Code of Project + View JSON Code of Project + + + Report Issue Report Issue - + Contribute to DiffScope Contribute to DiffScope - + Join the Community Join the Community - + Check for Updates Check for Updates - + View Release Log View Release Log - + + Diagnosis + Diagnosis + + + + Open Directory Actions + Open Directory Actions + + Diagnosis Actions Diagnosis Actions - + Get Involved Actions Get Involved Actions - + Update Actions Update Actions @@ -107,12 +140,16 @@ Maintenance::MaintenanceAddOn - Important Notice Important Notice - + + Disclaimer + Disclaimer + + + <p>The diagnostic report may contain <b>sensitive information</b> (e.g., usernames, hostnames, network addresses, personalized configurations).</p> <p>If you do not wish to make such information public, please <b>do not</b> share this report on public platforms like GitHub Issues or online chat rooms.</p> <p>You may follow the feedback guidelines to send the report privately via email to the DiffScope development team.</p> @@ -125,17 +162,17 @@ - + Save Diagnostic Report Save Diagnostic Report - + Error Error - + Failed to open the file for writing: %1 @@ -144,12 +181,12 @@ %1 - + Diagnostic Report Generated Diagnostic Report Generated - + The identifier of report is: The identifier of report is: @@ -157,16 +194,33 @@ Maintenance::UpdatePage - + Update Update - + Configure update settings for %1 Configure update settings for %1 + + Maintenance::ViewJsonAddOn + + + Failed to create JSON file + Failed to create JSON file + + + + Could not create temporary JSON file: + +%1 + Could not create temporary JSON file: + +%1 + + UpdatePage diff --git a/src/plugins/maintenance/res/translations/org.diffscope.maintenance_zh_CN.ts b/src/plugins/maintenance/res/translations/org.diffscope.maintenance_zh_CN.ts index cb72de72..2c173a7c 100644 --- a/src/plugins/maintenance/res/translations/org.diffscope.maintenance_zh_CN.ts +++ b/src/plugins/maintenance/res/translations/org.diffscope.maintenance_zh_CN.ts @@ -4,31 +4,45 @@ - + Main Menu 主菜单 + + Application + + + Version %1 + 版本 %1 + + + + Copyright © %1-%2 %3. All rights reserved. + 版权所有 © %1-%2 %3。保留所有权利。 + + Application::ActionClass - - - - + + + + + Diagnosis 诊断 - - - + + + Get Involved 参与 - - + + Update 更新 @@ -36,7 +50,12 @@ Application::ActionDescription - + + View the JSON code of the current project in the default editor + 在默认编辑器中查看当前工程的 JSON 代码 + + + Report bugs, request new features, or ask for support 报告缺陷,请求新特性,或者寻求支持 @@ -44,62 +63,76 @@ Application::ActionText - + Generate Diagnostic Report... 生成诊断报告... - + Open Logs Directory 打开日志目录 - + Open Data Directory 打开数据目录 - + Open Plugins Directory 打开插件目录 - + + View JSON Code of Project + 查看工程 JSON 代码 + + + Report Issue 报告问题 - + Contribute to DiffScope 贡献 DiffScope - + Join the Community 加入社区 - + Check for Updates 检查更新 - + View Release Log 查看发布日志 - + + Diagnosis + 诊断 + + + + Open Directory Actions + 打开目录操作 + + Diagnosis Actions 诊断操作 - + Get Involved Actions 参与操作 - + Update Actions 更新操作 @@ -107,12 +140,16 @@ Maintenance::MaintenanceAddOn - Important Notice 重要提示 - + + Disclaimer + 免责声明 + + + <p>The diagnostic report may contain <b>sensitive information</b> (e.g., usernames, hostnames, network addresses, personalized configurations).</p> <p>If you do not wish to make such information public, please <b>do not</b> share this report on public platforms like GitHub Issues or online chat rooms.</p> <p>You may follow the feedback guidelines to send the report privately via email to the DiffScope development team.</p> @@ -125,17 +162,17 @@ - + Save Diagnostic Report 保存诊断报告 - + Error 错误 - + Failed to open the file for writing: %1 @@ -144,12 +181,12 @@ %1 - + Diagnostic Report Generated 诊断报告已生成 - + The identifier of report is: 报告标识符为: @@ -157,16 +194,33 @@ Maintenance::UpdatePage - + Update 更新 - + Configure update settings for %1 配置 %1 的更新设置 + + Maintenance::ViewJsonAddOn + + + Failed to create JSON file + 创建 JSON 文件失败 + + + + Could not create temporary JSON file: + +%1 + 无法创建临时 JSON 文件: + +%1 + + UpdatePage diff --git a/src/plugins/maintenance/res/translations/org.diffscope.maintenance_zh_TW.ts b/src/plugins/maintenance/res/translations/org.diffscope.maintenance_zh_TW.ts index 81e41175..3c1b6a63 100644 --- a/src/plugins/maintenance/res/translations/org.diffscope.maintenance_zh_TW.ts +++ b/src/plugins/maintenance/res/translations/org.diffscope.maintenance_zh_TW.ts @@ -4,31 +4,45 @@ - + Main Menu Main Menu + + Application + + + Version %1 + Version %1 + + + + Copyright © %1-%2 %3. All rights reserved. + Copyright © %1-%2 %3. All rights reserved. + + Application::ActionClass - - - - + + + + + Diagnosis Diagnosis - - - + + + Get Involved Get Involved - - + + Update Update @@ -36,7 +50,12 @@ Application::ActionDescription - + + View the JSON code of the current project in the default editor + View the JSON code of the current project in the default editor + + + Report bugs, request new features, or ask for support Report bugs, request new features, or ask for support @@ -44,62 +63,76 @@ Application::ActionText - + Generate Diagnostic Report... Generate Diagnostic Report... - + Open Logs Directory Open Logs Directory - + Open Data Directory Open Data Directory - + Open Plugins Directory Open Plugins Directory - + + View JSON Code of Project + View JSON Code of Project + + + Report Issue Report Issue - + Contribute to DiffScope Contribute to DiffScope - + Join the Community Join the Community - + Check for Updates Check for Updates - + View Release Log View Release Log - + + Diagnosis + Diagnosis + + + + Open Directory Actions + Open Directory Actions + + Diagnosis Actions Diagnosis Actions - + Get Involved Actions Get Involved Actions - + Update Actions Update Actions @@ -107,12 +140,16 @@ Maintenance::MaintenanceAddOn - Important Notice Important Notice - + + Disclaimer + Disclaimer + + + <p>The diagnostic report may contain <b>sensitive information</b> (e.g., usernames, hostnames, network addresses, personalized configurations).</p> <p>If you do not wish to make such information public, please <b>do not</b> share this report on public platforms like GitHub Issues or online chat rooms.</p> <p>You may follow the feedback guidelines to send the report privately via email to the DiffScope development team.</p> @@ -125,17 +162,17 @@ - + Save Diagnostic Report Save Diagnostic Report - + Error Error - + Failed to open the file for writing: %1 @@ -144,12 +181,12 @@ %1 - + Diagnostic Report Generated Diagnostic Report Generated - + The identifier of report is: The identifier of report is: @@ -157,16 +194,33 @@ Maintenance::UpdatePage - + Update Update - + Configure update settings for %1 Configure update settings for %1 + + Maintenance::ViewJsonAddOn + + + Failed to create JSON file + Failed to create JSON file + + + + Could not create temporary JSON file: + +%1 + Could not create temporary JSON file: + +%1 + + UpdatePage diff --git a/src/plugins/midiformatconverter/internal/MIDIFormatConverterPlugin.cpp b/src/plugins/midiformatconverter/internal/MIDIFormatConverterPlugin.cpp index f6e0a28d..709840a9 100644 --- a/src/plugins/midiformatconverter/internal/MIDIFormatConverterPlugin.cpp +++ b/src/plugins/midiformatconverter/internal/MIDIFormatConverterPlugin.cpp @@ -21,8 +21,8 @@ namespace MIDIFormatConverter::Internal { bool MIDIFormatConverterPlugin::initialize(const QStringList &arguments, QString *errorMessage) { Core::RuntimeInterface::translationManager()->addTranslationPath(pluginSpec()->location() + QStringLiteral("/translations")); - ImportExportManager::ConverterCollection::instance()->addObject(new MIDIFileImporter); - ImportExportManager::ConverterCollection::instance()->addObject(new MIDIFileExporter); + ImportExportManager::ConverterCollection::addFileConverter(new MIDIFileImporter); + ImportExportManager::ConverterCollection::addFileConverter(new MIDIFileExporter); return true; } diff --git a/src/plugins/midiformatconverter/res/translations/org.diffscope.midiformatconverter.ts b/src/plugins/midiformatconverter/res/translations/org.diffscope.midiformatconverter.ts new file mode 100644 index 00000000..6f98ed43 --- /dev/null +++ b/src/plugins/midiformatconverter/res/translations/org.diffscope.midiformatconverter.ts @@ -0,0 +1,53 @@ + + + + + Application + + + Version %1 + Version %1 + + + + Copyright © %1-%2 %3. All rights reserved. + Copyright © %1-%2 %3. All rights reserved. + + + + MIDIFormatConverter::Internal::MIDIFileExporter + + + MIDI + MIDI + + + + Export as Standard MIDI file + Export as Standard MIDI file + + + + Standard MIDI File (*.mid *.midi *.smf) + Standard MIDI File (*.mid *.midi *.smf) + + + + MIDIFormatConverter::Internal::MIDIFileImporter + + + MIDI + MIDI + + + + Import Standard MIDI file + Import Standard MIDI file + + + + Standard MIDI File (*.mid *.midi *.smf) + Standard MIDI File (*.mid *.midi *.smf) + + + diff --git a/src/plugins/midiformatconverter/res/translations/org.diffscope.midiformatconverter_en_US.ts b/src/plugins/midiformatconverter/res/translations/org.diffscope.midiformatconverter_en_US.ts index b262e3bb..3ec7cc6b 100644 --- a/src/plugins/midiformatconverter/res/translations/org.diffscope.midiformatconverter_en_US.ts +++ b/src/plugins/midiformatconverter/res/translations/org.diffscope.midiformatconverter_en_US.ts @@ -1,4 +1,53 @@ + + Application + + + Version %1 + + + + + Copyright © %1-%2 %3. All rights reserved. + + + + + MIDIFormatConverter::Internal::MIDIFileExporter + + + MIDI + + + + + Export as Standard MIDI file + + + + + Standard MIDI File (*.mid *.midi *.smf) + + + + + MIDIFormatConverter::Internal::MIDIFileImporter + + + MIDI + + + + + Import Standard MIDI file + + + + + Standard MIDI File (*.mid *.midi *.smf) + + + diff --git a/src/plugins/midiformatconverter/res/translations/org.diffscope.midiformatconverter_ja_JP.ts b/src/plugins/midiformatconverter/res/translations/org.diffscope.midiformatconverter_ja_JP.ts index 85f2f934..16bdad18 100644 --- a/src/plugins/midiformatconverter/res/translations/org.diffscope.midiformatconverter_ja_JP.ts +++ b/src/plugins/midiformatconverter/res/translations/org.diffscope.midiformatconverter_ja_JP.ts @@ -1,4 +1,53 @@ + + Application + + + Version %1 + Version %1 + + + + Copyright © %1-%2 %3. All rights reserved. + Copyright © %1-%2 %3. All rights reserved. + + + + MIDIFormatConverter::Internal::MIDIFileExporter + + + MIDI + MIDI + + + + Export as Standard MIDI file + Export as Standard MIDI file + + + + Standard MIDI File (*.mid *.midi *.smf) + Standard MIDI File (*.mid *.midi *.smf) + + + + MIDIFormatConverter::Internal::MIDIFileImporter + + + MIDI + MIDI + + + + Import Standard MIDI file + Import Standard MIDI file + + + + Standard MIDI File (*.mid *.midi *.smf) + Standard MIDI File (*.mid *.midi *.smf) + + diff --git a/src/plugins/midiformatconverter/res/translations/org.diffscope.midiformatconverter_zh_CN.ts b/src/plugins/midiformatconverter/res/translations/org.diffscope.midiformatconverter_zh_CN.ts index 9c60864f..db68f2d8 100644 --- a/src/plugins/midiformatconverter/res/translations/org.diffscope.midiformatconverter_zh_CN.ts +++ b/src/plugins/midiformatconverter/res/translations/org.diffscope.midiformatconverter_zh_CN.ts @@ -1,4 +1,53 @@ + + Application + + + Version %1 + 版本 %1 + + + + Copyright © %1-%2 %3. All rights reserved. + 版权所有 © %1-%2 %3。保留所有权利。 + + + + MIDIFormatConverter::Internal::MIDIFileExporter + + + MIDI + MIDI + + + + Export as Standard MIDI file + 导出为标准 MIDI 文件 + + + + Standard MIDI File (*.mid *.midi *.smf) + 标准 MIDI 文件 (*.mid *.midi *.smf) + + + + MIDIFormatConverter::Internal::MIDIFileImporter + + + MIDI + MIDI + + + + Import Standard MIDI file + 导入标准 MIDI 文件 + + + + Standard MIDI File (*.mid *.midi *.smf) + 标准 MIDI 文件 (*.mid *.midi *.smf) + + diff --git a/src/plugins/midiformatconverter/res/translations/org.diffscope.midiformatconverter_zh_TW.ts b/src/plugins/midiformatconverter/res/translations/org.diffscope.midiformatconverter_zh_TW.ts index 2316ddac..e07c056f 100644 --- a/src/plugins/midiformatconverter/res/translations/org.diffscope.midiformatconverter_zh_TW.ts +++ b/src/plugins/midiformatconverter/res/translations/org.diffscope.midiformatconverter_zh_TW.ts @@ -1,4 +1,53 @@ + + Application + + + Version %1 + Version %1 + + + + Copyright © %1-%2 %3. All rights reserved. + Copyright © %1-%2 %3. All rights reserved. + + + + MIDIFormatConverter::Internal::MIDIFileExporter + + + MIDI + MIDI + + + + Export as Standard MIDI file + Export as Standard MIDI file + + + + Standard MIDI File (*.mid *.midi *.smf) + Standard MIDI File (*.mid *.midi *.smf) + + + + MIDIFormatConverter::Internal::MIDIFileImporter + + + MIDI + MIDI + + + + Import Standard MIDI file + Import Standard MIDI file + + + + Standard MIDI File (*.mid *.midi *.smf) + Standard MIDI File (*.mid *.midi *.smf) + + diff --git a/src/plugins/visualeditor/core/ArrangementPanelInterface.cpp b/src/plugins/visualeditor/core/ArrangementPanelInterface.cpp index 11d913f1..9c3f14ad 100644 --- a/src/plugins/visualeditor/core/ArrangementPanelInterface.cpp +++ b/src/plugins/visualeditor/core/ArrangementPanelInterface.cpp @@ -11,8 +11,10 @@ #include #include #include +#include #include #include +#include #include #include @@ -117,10 +119,12 @@ namespace VisualEditor { d->timeViewModel = new sflow::TimeViewModel(this); d->timeLayoutViewModel = new sflow::TimeLayoutViewModel(this); - d->timelineInteractionController = new sflow::TimelineInteractionController(this); + d->trackListLayoutViewModel = new sflow::TrackListLayoutViewModel(this); d->scrollBehaviorViewModel = new sflow::ScrollBehaviorViewModel(this); + d->timelineInteractionController = ProjectViewModelContext::of(d->windowHandle)->createAndBindTimelineInteractionController(); d->labelSequenceInteractionControllerOfTempo = ProjectViewModelContext::of(d->windowHandle)->createAndBindLabelSequenceInteractionControllerOfTempo(); d->labelSequenceInteractionControllerOfLabel = ProjectViewModelContext::of(d->windowHandle)->createAndBindLabelSequenceInteractionControllerOfLabel(); + d->trackListInteractionController = ProjectViewModelContext::of(d->windowHandle)->createAndBindTrackListInteractionController(); d->positionAlignmentManipulator = new PositionAlignmentManipulator(this); d->positionAlignmentManipulator->setTimeLayoutViewModel(d->timeLayoutViewModel); @@ -177,6 +181,12 @@ namespace VisualEditor { Q_D(const ArrangementPanelInterface); return d->timeLayoutViewModel; } + + sflow::TrackListLayoutViewModel *ArrangementPanelInterface::trackListLayoutViewModel() const { + Q_D(const ArrangementPanelInterface); + return d->trackListLayoutViewModel; + } + sflow::ScrollBehaviorViewModel *ArrangementPanelInterface::scrollBehaviorViewModel() const { Q_D(const ArrangementPanelInterface); return d->scrollBehaviorViewModel; @@ -195,6 +205,11 @@ namespace VisualEditor { return d->labelSequenceInteractionControllerOfLabel; } + sflow::TrackListInteractionController *ArrangementPanelInterface::trackListInteractionController() const { + Q_D(const ArrangementPanelInterface); + return d->trackListInteractionController; + } + PositionAlignmentManipulator *ArrangementPanelInterface::positionAlignmentManipulator() const { Q_D(const ArrangementPanelInterface); return d->positionAlignmentManipulator; diff --git a/src/plugins/visualeditor/core/ArrangementPanelInterface.h b/src/plugins/visualeditor/core/ArrangementPanelInterface.h index 35bdb11d..c1e8e558 100644 --- a/src/plugins/visualeditor/core/ArrangementPanelInterface.h +++ b/src/plugins/visualeditor/core/ArrangementPanelInterface.h @@ -14,6 +14,8 @@ namespace sflow { class TimelineInteractionController; class ScrollBehaviorViewModel; class LabelSequenceInteractionController; + class TrackListLayoutViewModel; + class TrackListInteractionController; } namespace Core { @@ -39,10 +41,12 @@ namespace VisualEditor { Q_PROPERTY(Core::ProjectWindowInterface *windowHandle READ windowHandle CONSTANT) Q_PROPERTY(sflow::TimeViewModel *timeViewModel READ timeViewModel CONSTANT) Q_PROPERTY(sflow::TimeLayoutViewModel *timeLayoutViewModel READ timeLayoutViewModel CONSTANT) + Q_PROPERTY(sflow::TrackListLayoutViewModel *trackListLayoutViewModel READ trackListLayoutViewModel CONSTANT) Q_PROPERTY(sflow::ScrollBehaviorViewModel *scrollBehaviorViewModel READ scrollBehaviorViewModel CONSTANT) Q_PROPERTY(sflow::TimelineInteractionController *timelineInteractionController READ timelineInteractionController CONSTANT) Q_PROPERTY(sflow::LabelSequenceInteractionController *labelSequenceInteractionControllerOfTempo READ labelSequenceInteractionControllerOfTempo CONSTANT) Q_PROPERTY(sflow::LabelSequenceInteractionController *labelSequenceInteractionControllerOfLabel READ labelSequenceInteractionControllerOfLabel CONSTANT) + Q_PROPERTY(sflow::TrackListInteractionController *trackListInteractionController READ trackListInteractionController CONSTANT) Q_PROPERTY(PositionAlignmentManipulator *positionAlignmentManipulator READ positionAlignmentManipulator CONSTANT) Q_PROPERTY(AutoPageScrollingManipulator *autoPageScrollingManipulator READ autoPageScrollingManipulator CONSTANT) Q_PROPERTY(QQuickItem *arrangementView READ arrangementView CONSTANT) @@ -59,10 +63,12 @@ namespace VisualEditor { sflow::TimeViewModel *timeViewModel() const; sflow::TimeLayoutViewModel *timeLayoutViewModel() const; + sflow::TrackListLayoutViewModel *trackListLayoutViewModel() const; sflow::ScrollBehaviorViewModel *scrollBehaviorViewModel() const; sflow::TimelineInteractionController *timelineInteractionController() const; sflow::LabelSequenceInteractionController *labelSequenceInteractionControllerOfTempo() const; sflow::LabelSequenceInteractionController *labelSequenceInteractionControllerOfLabel() const; + sflow::TrackListInteractionController *trackListInteractionController() const; PositionAlignmentManipulator *positionAlignmentManipulator() const; AutoPageScrollingManipulator *autoPageScrollingManipulator() const; diff --git a/src/plugins/visualeditor/core/ArrangementPanelInterface_p.h b/src/plugins/visualeditor/core/ArrangementPanelInterface_p.h index a2df4729..88bc4a73 100644 --- a/src/plugins/visualeditor/core/ArrangementPanelInterface_p.h +++ b/src/plugins/visualeditor/core/ArrangementPanelInterface_p.h @@ -17,10 +17,12 @@ namespace VisualEditor { sflow::TimeViewModel *timeViewModel; sflow::TimeLayoutViewModel *timeLayoutViewModel; + sflow::TrackListLayoutViewModel *trackListLayoutViewModel; sflow::TimelineInteractionController *timelineInteractionController; sflow::ScrollBehaviorViewModel *scrollBehaviorViewModel; sflow::LabelSequenceInteractionController *labelSequenceInteractionControllerOfTempo; sflow::LabelSequenceInteractionController *labelSequenceInteractionControllerOfLabel; + sflow::TrackListInteractionController *trackListInteractionController; PositionAlignmentManipulator *positionAlignmentManipulator; AutoPageScrollingManipulator *autoPageScrollingManipulator; diff --git a/src/plugins/visualeditor/core/MixerPanelInterface.cpp b/src/plugins/visualeditor/core/MixerPanelInterface.cpp new file mode 100644 index 00000000..6f061325 --- /dev/null +++ b/src/plugins/visualeditor/core/MixerPanelInterface.cpp @@ -0,0 +1,159 @@ +#include "MixerPanelInterface.h" +#include "MixerPanelInterface_p.h" + +#include +#include +#include + +#include + +#include +#include +#include + +#include + +#include +#include +#include + +namespace VisualEditor { + + static Qt::KeyboardModifier getModifier(Internal::EditorPreference::ScrollModifier modifier) { + switch (modifier) { + case Internal::EditorPreference::SM_Control: + return Qt::ControlModifier; + case Internal::EditorPreference::SM_Alt: + return Qt::AltModifier; + case Internal::EditorPreference::SM_Shift: + return Qt::ShiftModifier; + } + Q_UNREACHABLE(); + } + + static sflow::ScrollBehaviorViewModel::ScrollTypes getScrollTypes(MixerPanelInterface::Tool tool) { + if (tool == MixerPanelInterface::HandTool) { + return sflow::ScrollBehaviorViewModel::Wheel | sflow::ScrollBehaviorViewModel::Pinch | sflow::ScrollBehaviorViewModel::MiddleButton | sflow::ScrollBehaviorViewModel::LeftButton; + } + return sflow::ScrollBehaviorViewModel::Wheel | sflow::ScrollBehaviorViewModel::Pinch | sflow::ScrollBehaviorViewModel::MiddleButton; + } + + void MixerPanelInterfacePrivate::bindScrollBehaviorViewModel() const { + Q_Q(const MixerPanelInterface); + scrollBehaviorViewModel->setAlternateAxisModifier(getModifier(Internal::EditorPreference::alternateAxisModifier())); + scrollBehaviorViewModel->setZoomModifier(getModifier(Internal::EditorPreference::zoomModifier())); + scrollBehaviorViewModel->setPageModifier(getModifier(Internal::EditorPreference::pageModifier())); + scrollBehaviorViewModel->setUsePageModifierAsAlternateAxisZoom(Internal::EditorPreference::usePageModifierAsAlternateAxisZoom()); + scrollBehaviorViewModel->setAutoScroll(Internal::EditorPreference::middleButtonAutoScroll()); + + QObject::connect(Internal::EditorPreference::instance(), &Internal::EditorPreference::alternateAxisModifierChanged, scrollBehaviorViewModel, [=, this] { + scrollBehaviorViewModel->setAlternateAxisModifier(getModifier(Internal::EditorPreference::alternateAxisModifier())); + }); + QObject::connect(Internal::EditorPreference::instance(), &Internal::EditorPreference::zoomModifierChanged, scrollBehaviorViewModel, [=, this] { + scrollBehaviorViewModel->setZoomModifier(getModifier(Internal::EditorPreference::zoomModifier())); + }); + QObject::connect(Internal::EditorPreference::instance(), &Internal::EditorPreference::pageModifierChanged, scrollBehaviorViewModel, [=, this] { + scrollBehaviorViewModel->setPageModifier(getModifier(Internal::EditorPreference::pageModifier())); + }); + QObject::connect(Internal::EditorPreference::instance(), &Internal::EditorPreference::usePageModifierAsAlternateAxisZoomChanged, scrollBehaviorViewModel, [=, this] { + scrollBehaviorViewModel->setUsePageModifierAsAlternateAxisZoom(Internal::EditorPreference::usePageModifierAsAlternateAxisZoom()); + }); + QObject::connect(Internal::EditorPreference::instance(), &Internal::EditorPreference::middleButtonAutoScrollChanged, scrollBehaviorViewModel, [=, this] { + scrollBehaviorViewModel->setAutoScroll(Internal::EditorPreference::middleButtonAutoScroll()); + }); + + scrollBehaviorViewModel->setScrollTypes(getScrollTypes(tool)); + QObject::connect(q, &MixerPanelInterface::toolChanged, scrollBehaviorViewModel, [=, this] { + scrollBehaviorViewModel->setScrollTypes(getScrollTypes(tool)); + }); + } + + MixerPanelInterface::MixerPanelInterface(Internal::MixerAddOn *addOn, Core::ProjectWindowInterface *windowHandle) : QObject(windowHandle), d_ptr(new MixerPanelInterfacePrivate) { + Q_D(MixerPanelInterface); + Q_ASSERT(windowHandle->getObjects(staticMetaObject.className()).isEmpty()); + windowHandle->addObject(staticMetaObject.className(), this); + d->q_ptr = this; + d->windowHandle = windowHandle; + d->addon = addOn; + + d->trackListLayoutViewModel = new sflow::TrackListLayoutViewModel(this); + d->masterTrackListLayoutViewModel = new sflow::TrackListLayoutViewModel(this); + d->scrollBehaviorViewModel = new sflow::ScrollBehaviorViewModel(this); + d->trackListInteractionController = ProjectViewModelContext::of(d->windowHandle)->createAndBindTrackListInteractionController(); + d->masterTrackListInteractionController = ProjectViewModelContext::of(d->windowHandle)->createAndBindTrackListInteractionControllerOfMaster(); + + QQmlComponent component(Core::RuntimeInterface::qmlEngine(), "DiffScope.VisualEditor", "MixerView"); + if (component.isError()) { + qFatal() << component.errorString(); + } + auto o = component.createWithInitialProperties({ + {"addOn", QVariant::fromValue(d->addon)}, + {"mixerPanelInterface", QVariant::fromValue(this)} + }); + if (component.isError()) { + qFatal() << component.errorString(); + } + o->setParent(this); + d->mixerView = qobject_cast(o); + Q_ASSERT(d->mixerView); + + d->bindScrollBehaviorViewModel(); + } + + MixerPanelInterface::~MixerPanelInterface() = default; + + MixerPanelInterface *MixerPanelInterface::of(const Core::ProjectWindowInterface *windowHandle) { + return qobject_cast(windowHandle->getFirstObject(staticMetaObject.className())); + } + + Core::ProjectWindowInterface *MixerPanelInterface::windowHandle() const { + Q_D(const MixerPanelInterface); + return d->windowHandle; + } + + sflow::TrackListLayoutViewModel *MixerPanelInterface::trackListLayoutViewModel() const { + Q_D(const MixerPanelInterface); + return d->trackListLayoutViewModel; + } + + sflow::TrackListLayoutViewModel *MixerPanelInterface::masterTrackListLayoutViewModel() const { + Q_D(const MixerPanelInterface); + return d->masterTrackListLayoutViewModel; + } + + sflow::ScrollBehaviorViewModel *MixerPanelInterface::scrollBehaviorViewModel() const { + Q_D(const MixerPanelInterface); + return d->scrollBehaviorViewModel; + } + + sflow::TrackListInteractionController *MixerPanelInterface::trackListInteractionController() const { + Q_D(const MixerPanelInterface); + return d->trackListInteractionController; + } + + sflow::TrackListInteractionController *MixerPanelInterface::masterTrackListInteractionController() const { + Q_D(const MixerPanelInterface); + return d->masterTrackListInteractionController; + } + + QQuickItem *MixerPanelInterface::mixerView() const { + Q_D(const MixerPanelInterface); + return d->mixerView; + } + + MixerPanelInterface::Tool MixerPanelInterface::tool() const { + Q_D(const MixerPanelInterface); + return d->tool; + } + + void MixerPanelInterface::setTool(Tool tool) { + Q_D(MixerPanelInterface); + if (d->tool != tool) { + d->tool = tool; + Q_EMIT toolChanged(); + } + } + +} + +#include "moc_MixerPanelInterface.cpp" diff --git a/src/plugins/visualeditor/core/MixerPanelInterface.h b/src/plugins/visualeditor/core/MixerPanelInterface.h new file mode 100644 index 00000000..35fbfbc4 --- /dev/null +++ b/src/plugins/visualeditor/core/MixerPanelInterface.h @@ -0,0 +1,77 @@ +#ifndef DIFFSCOPE_VISUALEDITOR_MIXERPANELINTERFACE_H +#define DIFFSCOPE_VISUALEDITOR_MIXERPANELINTERFACE_H + +#include +#include + +#include + +class QQuickItem; + +namespace sflow { + class ScrollBehaviorViewModel; + class TrackListLayoutViewModel; + class TrackListInteractionController; +} + +namespace Core { + class ProjectWindowInterface; +} + +namespace VisualEditor { + + namespace Internal { + class MixerAddOn; + } + + class MixerPanelInterfacePrivate; + + class VISUAL_EDITOR_EXPORT MixerPanelInterface : public QObject { + Q_OBJECT + QML_ELEMENT + QML_UNCREATABLE("") + Q_DECLARE_PRIVATE(MixerPanelInterface) + Q_PROPERTY(Core::ProjectWindowInterface *windowHandle READ windowHandle CONSTANT) + Q_PROPERTY(sflow::TrackListLayoutViewModel *trackListLayoutViewModel READ trackListLayoutViewModel CONSTANT) + Q_PROPERTY(sflow::TrackListLayoutViewModel *masterTrackListLayoutViewModel READ masterTrackListLayoutViewModel CONSTANT) + Q_PROPERTY(sflow::ScrollBehaviorViewModel *scrollBehaviorViewModel READ scrollBehaviorViewModel CONSTANT) + Q_PROPERTY(sflow::TrackListInteractionController *trackListInteractionController READ trackListInteractionController CONSTANT) + Q_PROPERTY(sflow::TrackListInteractionController *masterTrackListInteractionController READ masterTrackListInteractionController CONSTANT) + Q_PROPERTY(QQuickItem *mixerView READ mixerView CONSTANT) + Q_PROPERTY(Tool tool READ tool WRITE setTool NOTIFY toolChanged) + + public: + ~MixerPanelInterface() override; + + static MixerPanelInterface *of(const Core::ProjectWindowInterface *windowHandle); + + Core::ProjectWindowInterface *windowHandle() const; + + sflow::TrackListLayoutViewModel *trackListLayoutViewModel() const; + sflow::TrackListLayoutViewModel *masterTrackListLayoutViewModel() const; + sflow::ScrollBehaviorViewModel *scrollBehaviorViewModel() const; + sflow::TrackListInteractionController *trackListInteractionController() const; + sflow::TrackListInteractionController *masterTrackListInteractionController() const; + + QQuickItem *mixerView() const; + + enum Tool { + PointerTool, + HandTool, + }; + Q_ENUM(Tool) + Tool tool() const; + void setTool(Tool tool); + + Q_SIGNALS: + void toolChanged(); + + private: + friend class Internal::MixerAddOn; + explicit MixerPanelInterface(Internal::MixerAddOn *addOn, Core::ProjectWindowInterface *windowHandle); + QScopedPointer d_ptr; + }; + +} + +#endif //DIFFSCOPE_VISUALEDITOR_MIXERPANELINTERFACE_H diff --git a/src/plugins/visualeditor/core/MixerPanelInterface_p.h b/src/plugins/visualeditor/core/MixerPanelInterface_p.h new file mode 100644 index 00000000..01f3e8df --- /dev/null +++ b/src/plugins/visualeditor/core/MixerPanelInterface_p.h @@ -0,0 +1,32 @@ +#ifndef DIFFSCOPE_VISUALEDITOR_MIXERPANELINTERFACE_P_H +#define DIFFSCOPE_VISUALEDITOR_MIXERPANELINTERFACE_P_H + +#include + +namespace VisualEditor { + + class MixerPanelInterfacePrivate { + Q_DECLARE_PUBLIC(MixerPanelInterface) + public: + MixerPanelInterface *q_ptr; + + Core::ProjectWindowInterface *windowHandle; + + Internal::MixerAddOn *addon; + + sflow::TrackListLayoutViewModel *trackListLayoutViewModel; + sflow::TrackListLayoutViewModel *masterTrackListLayoutViewModel; + sflow::ScrollBehaviorViewModel *scrollBehaviorViewModel; + sflow::TrackListInteractionController *trackListInteractionController; + sflow::TrackListInteractionController *masterTrackListInteractionController; + + QQuickItem *mixerView; + + MixerPanelInterface::Tool tool{MixerPanelInterface::PointerTool}; + + void bindScrollBehaviorViewModel() const; + }; + +} + +#endif //DIFFSCOPE_VISUALEDITOR_MIXERPANELINTERFACE_P_H diff --git a/src/plugins/visualeditor/core/PlaybackViewModelContextData.cpp b/src/plugins/visualeditor/core/PlaybackViewModelContextData.cpp deleted file mode 100644 index 6d1ad3cc..00000000 --- a/src/plugins/visualeditor/core/PlaybackViewModelContextData.cpp +++ /dev/null @@ -1,52 +0,0 @@ -#include "PlaybackViewModelContextData_p.h" - -#include - -#include - -#include -#include - -#include - -namespace VisualEditor { - - Q_STATIC_LOGGING_CATEGORY(lcPlaybackViewModelContextData, "diffscope.visualeditor.playbackviewmodelcontextdata") - - void PlaybackViewModelContextData::init() { - Q_Q(ProjectViewModelContext); - windowHandle = q->windowHandle(); - playbackViewModel = new sflow::PlaybackViewModel(q); - } - - void PlaybackViewModelContextData::bindPlaybackViewModel() { - auto projectTimeline = windowHandle->projectTimeline(); - QObject::connect(projectTimeline, &Core::ProjectTimeline::positionChanged, playbackViewModel, [=] { - if (playbackViewModel->primaryPosition() == projectTimeline->position()) - return; - qCDebug(lcPlaybackViewModelContextData) << "Project timeline position updated" << projectTimeline->position(); - playbackViewModel->setPrimaryPosition(projectTimeline->position()); - }); - QObject::connect(projectTimeline, &Core::ProjectTimeline::lastPositionChanged, playbackViewModel, [=] { - if (playbackViewModel->secondaryPosition() == projectTimeline->lastPosition()) - return; - qCDebug(lcPlaybackViewModelContextData) << "Project timeline last position updated" << projectTimeline->lastPosition(); - playbackViewModel->setSecondaryPosition(projectTimeline->lastPosition()); - }); - QObject::connect(playbackViewModel, &sflow::PlaybackViewModel::primaryPositionChanged, projectTimeline, [=] { - if (projectTimeline->position() == playbackViewModel->primaryPosition()) - return; - qCDebug(lcPlaybackViewModelContextData) << "Playback view model primary position updated" << playbackViewModel->primaryPosition(); - projectTimeline->setPosition(playbackViewModel->primaryPosition()); - }); - QObject::connect(playbackViewModel, &sflow::PlaybackViewModel::secondaryPositionChanged, projectTimeline, [=] { - if (projectTimeline->lastPosition() == playbackViewModel->secondaryPosition()) - return; - qCDebug(lcPlaybackViewModelContextData) << "Playback view model secondary position updated" << playbackViewModel->secondaryPosition(); - projectTimeline->setLastPosition(playbackViewModel->secondaryPosition()); - }); - playbackViewModel->setPrimaryPosition(projectTimeline->position()); - playbackViewModel->setSecondaryPosition(projectTimeline->lastPosition()); - } - -} diff --git a/src/plugins/visualeditor/core/PlaybackViewModelContextData_p.h b/src/plugins/visualeditor/core/PlaybackViewModelContextData_p.h deleted file mode 100644 index ff107997..00000000 --- a/src/plugins/visualeditor/core/PlaybackViewModelContextData_p.h +++ /dev/null @@ -1,22 +0,0 @@ -#ifndef DIFFSCOPE_VISUALEDITOR_PLAYBACKVIEWMODELCONTEXTDATA_P_H -#define DIFFSCOPE_VISUALEDITOR_PLAYBACKVIEWMODELCONTEXTDATA_P_H - -#include - -namespace VisualEditor { - - class PlaybackViewModelContextData { - Q_DECLARE_PUBLIC(ProjectViewModelContext) - public: - ProjectViewModelContext *q_ptr; - - Core::ProjectWindowInterface *windowHandle; - sflow::PlaybackViewModel *playbackViewModel; - - void init(); - void bindPlaybackViewModel(); - }; - -} - -#endif //DIFFSCOPE_VISUALEDITOR_PLAYBACKVIEWMODELCONTEXTDATA_P_H diff --git a/src/plugins/visualeditor/core/ProjectViewModelContext.cpp b/src/plugins/visualeditor/core/ProjectViewModelContext.cpp index d160b868..11e57f79 100644 --- a/src/plugins/visualeditor/core/ProjectViewModelContext.cpp +++ b/src/plugins/visualeditor/core/ProjectViewModelContext.cpp @@ -1,24 +1,32 @@ #include "ProjectViewModelContext.h" #include "ProjectViewModelContext_p.h" +#include + #include +#include #include #include +#include #include +#include +#include #include #include +#include #include #include #include #include +#include #include #include #include - -#include +#include +#include namespace VisualEditor { @@ -56,6 +64,16 @@ namespace VisualEditor { d->labelData->q_ptr = this; d->labelData->init(); d->labelData->bindLabelSequenceViewModel(); + + d->trackData = std::make_unique(); + d->trackData->q_ptr = this; + d->trackData->init(); + d->trackData->bindTrackListViewModel(); + + d->masterTrackData = std::make_unique(); + d->masterTrackData->q_ptr = this; + d->masterTrackData->init(); + d->masterTrackData->bindMasterTrackViewModel(); } ProjectViewModelContext::~ProjectViewModelContext() = default; @@ -88,6 +106,16 @@ namespace VisualEditor { return d->labelData->labelSequenceViewModel; } + sflow::ListViewModel *ProjectViewModelContext::trackListViewModel() const { + Q_D(const ProjectViewModelContext); + return d->trackData->trackListViewModel; + } + + sflow::ListViewModel *ProjectViewModelContext::masterTrackListViewModel() const { + Q_D(const ProjectViewModelContext); + return d->masterTrackData->masterTrackListViewModel; + } + sflow::SelectionController *ProjectViewModelContext::tempoSelectionController() const { Q_D(const ProjectViewModelContext); return d->tempoData->tempoSelectionController; @@ -98,6 +126,16 @@ namespace VisualEditor { return d->labelData->labelSelectionController; } + sflow::SelectionController *ProjectViewModelContext::trackSelectionController() const { + Q_D(const ProjectViewModelContext); + return d->trackData->trackSelectionController; + } + + sflow::TimelineInteractionController *ProjectViewModelContext::createAndBindTimelineInteractionController(QObject *parent) { + Q_D(ProjectViewModelContext); + return d->playbackData->createController(parent); + } + sflow::LabelSequenceInteractionController *ProjectViewModelContext::createAndBindLabelSequenceInteractionControllerOfTempo(QObject *parent) { Q_D(ProjectViewModelContext); return d->tempoData->createController(parent); @@ -108,6 +146,16 @@ namespace VisualEditor { return d->labelData->createController(parent); } + sflow::TrackListInteractionController *ProjectViewModelContext::createAndBindTrackListInteractionController(QObject *parent) { + Q_D(ProjectViewModelContext); + return d->trackData->createController(parent); + } + + sflow::TrackListInteractionController *ProjectViewModelContext::createAndBindTrackListInteractionControllerOfMaster(QObject *parent) { + Q_D(ProjectViewModelContext); + return d->masterTrackData->createController(parent); + } + dspx::Tempo *ProjectViewModelContext::getTempoDocumentItemFromViewItem(sflow::LabelViewModel *viewItem) const { Q_D(const ProjectViewModelContext); return d->tempoData->tempoDocumentItemMap.value(viewItem); @@ -128,6 +176,16 @@ namespace VisualEditor { return d->labelData->labelViewItemMap.value(item); } + dspx::Track *ProjectViewModelContext::getTrackDocumentItemFromViewItem(sflow::TrackViewModel *viewItem) const { + Q_D(const ProjectViewModelContext); + return d->trackData->trackDocumentItemMap.value(viewItem); + } + + sflow::TrackViewModel *ProjectViewModelContext::getTrackViewItemFromDocumentItem(dspx::Track *item) const { + Q_D(const ProjectViewModelContext); + return d->trackData->trackViewItemMap.value(item); + } + } #include "moc_ProjectViewModelContext.cpp" diff --git a/src/plugins/visualeditor/core/ProjectViewModelContext.h b/src/plugins/visualeditor/core/ProjectViewModelContext.h index 785ff297..52be9d0f 100644 --- a/src/plugins/visualeditor/core/ProjectViewModelContext.h +++ b/src/plugins/visualeditor/core/ProjectViewModelContext.h @@ -12,11 +12,16 @@ namespace sflow { class LabelSequenceInteractionController; class LabelViewModel; class SelectionController; + class TimelineInteractionController; + class ListViewModel; + class TrackListInteractionController; + class TrackViewModel; } namespace dspx { class Label; class Tempo; + class Track; } namespace Core { @@ -44,6 +49,9 @@ namespace VisualEditor { Q_PROPERTY(sflow::SelectionController *tempoSelectionController READ tempoSelectionController CONSTANT) Q_PROPERTY(sflow::PointSequenceViewModel *labelSequenceViewModel READ labelSequenceViewModel CONSTANT) Q_PROPERTY(sflow::SelectionController *labelSelectionController READ labelSelectionController CONSTANT) + Q_PROPERTY(sflow::ListViewModel *trackListViewModel READ trackListViewModel CONSTANT) + Q_PROPERTY(sflow::SelectionController *trackSelectionController READ trackSelectionController CONSTANT) + Q_PROPERTY(sflow::ListViewModel *masterTrackListViewModel READ masterTrackListViewModel CONSTANT) public: ~ProjectViewModelContext() override; @@ -57,17 +65,25 @@ namespace VisualEditor { sflow::PlaybackViewModel *playbackViewModel() const; sflow::PointSequenceViewModel *tempoSequenceViewModel() const; sflow::PointSequenceViewModel *labelSequenceViewModel() const; + sflow::ListViewModel *trackListViewModel() const; + sflow::ListViewModel *masterTrackListViewModel() const; sflow::SelectionController *tempoSelectionController() const; sflow::SelectionController *labelSelectionController() const; + sflow::SelectionController *trackSelectionController() const; + Q_INVOKABLE sflow::TimelineInteractionController *createAndBindTimelineInteractionController(QObject *parent = nullptr); Q_INVOKABLE sflow::LabelSequenceInteractionController *createAndBindLabelSequenceInteractionControllerOfTempo(QObject *parent = nullptr); Q_INVOKABLE sflow::LabelSequenceInteractionController *createAndBindLabelSequenceInteractionControllerOfLabel(QObject *parent = nullptr); + Q_INVOKABLE sflow::TrackListInteractionController *createAndBindTrackListInteractionController(QObject *parent = nullptr); + Q_INVOKABLE sflow::TrackListInteractionController *createAndBindTrackListInteractionControllerOfMaster(QObject *parent = nullptr); Q_INVOKABLE dspx::Tempo *getTempoDocumentItemFromViewItem(sflow::LabelViewModel *viewItem) const; Q_INVOKABLE sflow::LabelViewModel *getTempoViewItemFromDocumentItem(dspx::Tempo *item) const; Q_INVOKABLE dspx::Label *getLabelDocumentItemFromViewItem(sflow::LabelViewModel *viewItem) const; Q_INVOKABLE sflow::LabelViewModel *getLabelViewItemFromDocumentItem(dspx::Label *item) const; + Q_INVOKABLE dspx::Track *getTrackDocumentItemFromViewItem(sflow::TrackViewModel *viewItem) const; + Q_INVOKABLE sflow::TrackViewModel *getTrackViewItemFromDocumentItem(dspx::Track *item) const; private: diff --git a/src/plugins/visualeditor/core/ProjectViewModelContext_p.h b/src/plugins/visualeditor/core/ProjectViewModelContext_p.h index 2ee8c8dc..7f94f209 100644 --- a/src/plugins/visualeditor/core/ProjectViewModelContext_p.h +++ b/src/plugins/visualeditor/core/ProjectViewModelContext_p.h @@ -10,6 +10,8 @@ namespace VisualEditor { class PlaybackViewModelContextData; class TempoViewModelContextData; class LabelViewModelContextData; + class TrackViewModelContextData; + class MasterTrackViewModelContextData; class ProjectViewModelContextAttachedType : public QObject { Q_OBJECT @@ -33,6 +35,8 @@ namespace VisualEditor { std::unique_ptr playbackData; std::unique_ptr tempoData; std::unique_ptr labelData; + std::unique_ptr trackData; + std::unique_ptr masterTrackData; }; } diff --git a/src/plugins/visualeditor/core/LabelSelectionController.cpp b/src/plugins/visualeditor/core/selectioncontrollers/LabelSelectionController.cpp similarity index 93% rename from src/plugins/visualeditor/core/LabelSelectionController.cpp rename to src/plugins/visualeditor/core/selectioncontrollers/LabelSelectionController.cpp index 8041995f..d9dc3cbf 100644 --- a/src/plugins/visualeditor/core/LabelSelectionController.cpp +++ b/src/plugins/visualeditor/core/selectioncontrollers/LabelSelectionController.cpp @@ -31,6 +31,7 @@ namespace VisualEditor { selectionModel = q->windowHandle()->projectDocumentContext()->document()->selectionModel(); labelSelectionModel = q->windowHandle()->projectDocumentContext()->document()->selectionModel()->labelSelectionModel(); connect(labelSelectionModel, &dspx::LabelSelectionModel::currentItemChanged, this, &SelectionController::currentItemChanged); + connect(selectionModel, &dspx::SelectionModel::selectionTypeChanged, this, &SelectionController::editScopeFocusedChanged); } LabelSelectionController::~LabelSelectionController() = default; @@ -88,4 +89,8 @@ namespace VisualEditor { return q->getLabelViewItemFromDocumentItem(labelSelectionModel->currentItem()); } + bool LabelSelectionController::editScopeFocused() const { + return selectionModel->selectionType() == dspx::SelectionModel::ST_Label; + } + } diff --git a/src/plugins/visualeditor/core/LabelSelectionController_p.h b/src/plugins/visualeditor/core/selectioncontrollers/LabelSelectionController_p.h similarity index 95% rename from src/plugins/visualeditor/core/LabelSelectionController_p.h rename to src/plugins/visualeditor/core/selectioncontrollers/LabelSelectionController_p.h index 9d3aa5fe..75318fcf 100644 --- a/src/plugins/visualeditor/core/LabelSelectionController_p.h +++ b/src/plugins/visualeditor/core/selectioncontrollers/LabelSelectionController_p.h @@ -26,6 +26,7 @@ namespace VisualEditor { QObjectList getItemsBetween(QObject *startItem, QObject *endItem) const override; void select(QObject *item, SelectionCommand command) override; QObject *currentItem() const override; + bool editScopeFocused() const override; private: ProjectViewModelContext *q; diff --git a/src/plugins/visualeditor/core/TempoSelectionController.cpp b/src/plugins/visualeditor/core/selectioncontrollers/TempoSelectionController.cpp similarity index 93% rename from src/plugins/visualeditor/core/TempoSelectionController.cpp rename to src/plugins/visualeditor/core/selectioncontrollers/TempoSelectionController.cpp index 6e979b93..8e285ac0 100644 --- a/src/plugins/visualeditor/core/TempoSelectionController.cpp +++ b/src/plugins/visualeditor/core/selectioncontrollers/TempoSelectionController.cpp @@ -31,6 +31,7 @@ namespace VisualEditor { selectionModel = q->windowHandle()->projectDocumentContext()->document()->selectionModel(); tempoSelectionModel = q->windowHandle()->projectDocumentContext()->document()->selectionModel()->tempoSelectionModel(); connect(tempoSelectionModel, &dspx::TempoSelectionModel::currentItemChanged, this, &SelectionController::currentItemChanged); + connect(selectionModel, &dspx::SelectionModel::selectionTypeChanged, this, &SelectionController::editScopeFocusedChanged); } TempoSelectionController::~TempoSelectionController() = default; @@ -88,4 +89,8 @@ namespace VisualEditor { return q->getTempoViewItemFromDocumentItem(tempoSelectionModel->currentItem()); } + bool TempoSelectionController::editScopeFocused() const { + return selectionModel->selectionType() == dspx::SelectionModel::ST_Tempo; + } + } diff --git a/src/plugins/visualeditor/core/TempoSelectionController_p.h b/src/plugins/visualeditor/core/selectioncontrollers/TempoSelectionController_p.h similarity index 95% rename from src/plugins/visualeditor/core/TempoSelectionController_p.h rename to src/plugins/visualeditor/core/selectioncontrollers/TempoSelectionController_p.h index 17cbe110..07b66200 100644 --- a/src/plugins/visualeditor/core/TempoSelectionController_p.h +++ b/src/plugins/visualeditor/core/selectioncontrollers/TempoSelectionController_p.h @@ -26,6 +26,7 @@ namespace VisualEditor { QObjectList getItemsBetween(QObject *startItem, QObject *endItem) const override; void select(QObject *item, SelectionCommand command) override; QObject *currentItem() const override; + bool editScopeFocused() const override; private: ProjectViewModelContext *q; diff --git a/src/plugins/visualeditor/core/selectioncontrollers/TrackSelectionController.cpp b/src/plugins/visualeditor/core/selectioncontrollers/TrackSelectionController.cpp new file mode 100644 index 00000000..862140eb --- /dev/null +++ b/src/plugins/visualeditor/core/selectioncontrollers/TrackSelectionController.cpp @@ -0,0 +1,98 @@ +#include "TrackSelectionController_p.h" + +#include +#include + +#include + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +namespace VisualEditor { + + Q_STATIC_LOGGING_CATEGORY(lcTrackSelectionController, "diffscope.visualeditor.trackselectioncontroller") + + TrackSelectionController::TrackSelectionController(ProjectViewModelContext *parent) + : SelectionController(parent), q(parent) { + trackList = q->windowHandle()->projectDocumentContext()->document()->model()->tracks(); + selectionModel = q->windowHandle()->projectDocumentContext()->document()->selectionModel(); + trackSelectionModel = selectionModel->trackSelectionModel(); + connect(trackSelectionModel, &dspx::TrackSelectionModel::currentItemChanged, this, &SelectionController::currentItemChanged); + connect(selectionModel, &dspx::SelectionModel::selectionTypeChanged, this, &SelectionController::editScopeFocusedChanged); + } + + TrackSelectionController::~TrackSelectionController() = default; + + QObjectList TrackSelectionController::getSelectedItems() const { + QObjectList viewItems; + std::ranges::transform(trackSelectionModel->selectedItems(), std::back_inserter(viewItems), [=, this](dspx::Track *item) { + auto viewItem = q->getTrackViewItemFromDocumentItem(item); + Q_ASSERT(viewItem); + return viewItem; + }); + return viewItems; + } + + QObjectList TrackSelectionController::getItemsBetween(QObject *startItem, QObject *endItem) const { + auto startDocumentItem = startItem ? q->getTrackDocumentItemFromViewItem(qobject_cast(startItem)) : trackList->item(0); + auto endDocumentItem = endItem ? q->getTrackDocumentItemFromViewItem(qobject_cast(endItem)) : trackList->item(trackList->size() - 1); + Q_ASSERT(startDocumentItem && endDocumentItem); + + const auto &items = trackList->items(); + const int startIndex = items.indexOf(startDocumentItem); + const int endIndex = items.indexOf(endDocumentItem); + Q_ASSERT(startIndex >= 0 && endIndex >= 0); + + const int from = std::min(startIndex, endIndex); + const int to = std::max(startIndex, endIndex); + + QObjectList viewItems; + for (int i = from; i <= to; ++i) { + auto viewItem = q->getTrackViewItemFromDocumentItem(items.at(i)); + Q_ASSERT(viewItem); + viewItems.append(viewItem); + } + return viewItems; + } + + void TrackSelectionController::select(QObject *item, SelectionCommand command) { + qCDebug(lcTrackSelectionController) << "Track view item selected" << item << command; + dspx::SelectionModel::SelectionCommand documentSelectionCommand = {}; + if (command & Select) { + documentSelectionCommand |= dspx::SelectionModel::Select; + } + if (command & Deselect) { + documentSelectionCommand |= dspx::SelectionModel::Deselect; + } + if (command & ClearPreviousSelection) { + documentSelectionCommand |= dspx::SelectionModel::ClearPreviousSelection; + } + if (command & SetCurrentItem) { + documentSelectionCommand |= dspx::SelectionModel::SetCurrentItem; + } + auto documentItem = q->getTrackDocumentItemFromViewItem(qobject_cast(item)); + Q_ASSERT(!item || documentItem); + selectionModel->select(documentItem, documentSelectionCommand, dspx::SelectionModel::ST_Track); + } + + QObject *TrackSelectionController::currentItem() const { + return q->getTrackViewItemFromDocumentItem(trackSelectionModel->currentItem()); + } + + bool TrackSelectionController::editScopeFocused() const { + return selectionModel->selectionType() == dspx::SelectionModel::ST_Track; + } + +} diff --git a/src/plugins/visualeditor/core/selectioncontrollers/TrackSelectionController_p.h b/src/plugins/visualeditor/core/selectioncontrollers/TrackSelectionController_p.h new file mode 100644 index 00000000..c500376d --- /dev/null +++ b/src/plugins/visualeditor/core/selectioncontrollers/TrackSelectionController_p.h @@ -0,0 +1,38 @@ +#ifndef DIFFSCOPE_VISUALEDITOR_TRACKSELECTIONCONTROLLER_P_H +#define DIFFSCOPE_VISUALEDITOR_TRACKSELECTIONCONTROLLER_P_H + +#include + +namespace dspx { + class SelectionModel; + class Track; + class TrackSelectionModel; + class TrackList; +} + +namespace VisualEditor { + + class ProjectViewModelContext; + + class TrackSelectionController : public sflow::SelectionController { + Q_OBJECT + public: + explicit TrackSelectionController(ProjectViewModelContext *parent); + ~TrackSelectionController() override; + + QObjectList getSelectedItems() const override; + QObjectList getItemsBetween(QObject *startItem, QObject *endItem) const override; + void select(QObject *item, SelectionCommand command) override; + QObject *currentItem() const override; + bool editScopeFocused() const override; + + private: + ProjectViewModelContext *q; + dspx::TrackList *trackList; + dspx::SelectionModel *selectionModel; + dspx::TrackSelectionModel *trackSelectionModel; + }; + +} + +#endif //DIFFSCOPE_VISUALEDITOR_TRACKSELECTIONCONTROLLER_P_H diff --git a/src/plugins/visualeditor/core/LabelViewModelContextData.cpp b/src/plugins/visualeditor/core/viewmodelbinding/LabelViewModelContextData.cpp similarity index 94% rename from src/plugins/visualeditor/core/LabelViewModelContextData.cpp rename to src/plugins/visualeditor/core/viewmodelbinding/LabelViewModelContextData.cpp index 7132c4ef..9cbf287b 100644 --- a/src/plugins/visualeditor/core/LabelViewModelContextData.cpp +++ b/src/plugins/visualeditor/core/viewmodelbinding/LabelViewModelContextData.cpp @@ -1,5 +1,4 @@ #include "LabelViewModelContextData_p.h" -#include "LabelSelectionController_p.h" #include #include @@ -25,6 +24,8 @@ #include #include +#include + namespace VisualEditor { Q_STATIC_LOGGING_CATEGORY(lcLabelViewModelContextData, "diffscope.visualeditor.labelviewmodelcontextdata") @@ -260,25 +261,17 @@ namespace VisualEditor { controller->setInteraction(sflow::LabelSequenceInteractionController::SelectByRubberBand); controller->setItemInteraction(sflow::LabelSequenceInteractionController::Move | sflow::LabelSequenceInteractionController::Select); - connect(controller, &sflow::LabelSequenceInteractionController::interactionOperationStarted, this, [=](QQuickItem *, sflow::LabelSequenceInteractionController::InteractionFlag type) { - if (type == sflow::LabelSequenceInteractionController::SelectByRubberBand) { - Q_EMIT rubberBandDragWillStart(); - } + connect(controller, &sflow::LabelSequenceInteractionController::rubberBandDraggingStarted, this, [=](QQuickItem *) { + Q_EMIT rubberBandDragWillStart(); }); - connect(controller, &sflow::LabelSequenceInteractionController::interactionOperationFinished, this, [=](QQuickItem *, sflow::LabelSequenceInteractionController::InteractionFlag type) { - if (type == sflow::LabelSequenceInteractionController::SelectByRubberBand) { - Q_EMIT rubberBandDragWillFinish(); - } + connect(controller, &sflow::LabelSequenceInteractionController::rubberBandDraggingFinished, this, [=](QQuickItem *) { + Q_EMIT rubberBandDragWillFinish(); }); - connect(controller, &sflow::LabelSequenceInteractionController::itemInteractionOperationStarted, this, [=](QQuickItem *, sflow::LabelViewModel *, sflow::LabelSequenceInteractionController::ItemInteractionFlag type) { - if (type == sflow::LabelSequenceInteractionController::Move) { - Q_EMIT moveTransactionWillStart(); - } + connect(controller, &sflow::LabelSequenceInteractionController::movingStarted, this, [=](QQuickItem *, sflow::LabelViewModel *) { + Q_EMIT moveTransactionWillStart(); }); - connect(controller, &sflow::LabelSequenceInteractionController::itemInteractionOperationFinished, this, [=](QQuickItem *, sflow::LabelViewModel *, sflow::LabelSequenceInteractionController::ItemInteractionFlag type) { - if (type == sflow::LabelSequenceInteractionController::Move) { - Q_EMIT moveTransactionWillFinish(); - } + connect(controller, &sflow::LabelSequenceInteractionController::movingFinished, this, [=](QQuickItem *, sflow::LabelViewModel *) { + Q_EMIT moveTransactionWillFinish(); }); connect(controller, &sflow::LabelSequenceInteractionController::inPlaceEditOperationTriggered, this, [=](QQuickItem *labelSequenceItem, sflow::LabelViewModel *viewItem, sflow::LabelSequenceInteractionController::InPlaceEditOperation type) { switch (type) { @@ -336,6 +329,11 @@ namespace VisualEditor { } void LabelViewModelContextData::onMovingStateExited() { + if (transactionalUpdatedLabels.isEmpty()) { + document->transactionController()->abortTransaction(moveTransactionId); + moveTransactionId = {}; + return; + } for (auto viewItem : transactionalUpdatedLabels) { auto item = labelDocumentItemMap.value(viewItem); Q_ASSERT(item); diff --git a/src/plugins/visualeditor/core/LabelViewModelContextData_p.h b/src/plugins/visualeditor/core/viewmodelbinding/LabelViewModelContextData_p.h similarity index 100% rename from src/plugins/visualeditor/core/LabelViewModelContextData_p.h rename to src/plugins/visualeditor/core/viewmodelbinding/LabelViewModelContextData_p.h diff --git a/src/plugins/visualeditor/core/viewmodelbinding/MasterTrackViewModelContextData.cpp b/src/plugins/visualeditor/core/viewmodelbinding/MasterTrackViewModelContextData.cpp new file mode 100644 index 00000000..1a11b3dd --- /dev/null +++ b/src/plugins/visualeditor/core/viewmodelbinding/MasterTrackViewModelContextData.cpp @@ -0,0 +1,438 @@ +#include "MasterTrackViewModelContextData_p.h" + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include +#include +#include + +namespace VisualEditor { + + Q_STATIC_LOGGING_CATEGORY(lcMasterTrackViewModelContextData, "diffscope.visualeditor.mastertrackviewmodelcontextdata") + + static inline double toDecibel(double gain) { + return SVS::DecibelLinearizer::gainToDecibels(gain); + } + + static inline double toLinear(double decibel) { + return SVS::DecibelLinearizer::decibelsToGain(decibel); + } + + void MasterTrackViewModelContextData::initStateMachine() { + stateMachine = new QStateMachine(QState::ExclusiveStates, this); + + idleState = new QState; + + mutePendingState = new QState; + muteEditingState = new QState; + muteFinishingState = new QState; + + gainPendingState = new QState; + gainProgressingState = new QState; + gainCommittingState = new QState; + gainAbortingState = new QState; + + panPendingState = new QState; + panProgressingState = new QState; + panCommittingState = new QState; + panAbortingState = new QState; + + multiChannelPendingState = new QState; + multiChannelEditingState = new QState; + multiChannelFinishingState = new QState; + + stateMachine->addState(idleState); + + stateMachine->addState(mutePendingState); + stateMachine->addState(muteEditingState); + stateMachine->addState(muteFinishingState); + + stateMachine->addState(gainPendingState); + stateMachine->addState(gainProgressingState); + stateMachine->addState(gainCommittingState); + stateMachine->addState(gainAbortingState); + + stateMachine->addState(panPendingState); + stateMachine->addState(panProgressingState); + stateMachine->addState(panCommittingState); + stateMachine->addState(panAbortingState); + + stateMachine->addState(multiChannelPendingState); + stateMachine->addState(multiChannelEditingState); + stateMachine->addState(multiChannelFinishingState); + + stateMachine->setInitialState(idleState); + stateMachine->start(); + + idleState->addTransition(this, &MasterTrackViewModelContextData::muteTransactionWillStart, mutePendingState); + mutePendingState->addTransition(this, &MasterTrackViewModelContextData::muteTransactionStarted, muteEditingState); + mutePendingState->addTransition(this, &MasterTrackViewModelContextData::muteTransactionNotStarted, idleState); + muteEditingState->addTransition(this, &MasterTrackViewModelContextData::muteTransactionWillFinish, muteFinishingState); + muteFinishingState->addTransition(idleState); + + idleState->addTransition(this, &MasterTrackViewModelContextData::gainTransactionWillStart, gainPendingState); + gainPendingState->addTransition(this, &MasterTrackViewModelContextData::gainTransactionStarted, gainProgressingState); + gainPendingState->addTransition(this, &MasterTrackViewModelContextData::gainTransactionNotStarted, idleState); + gainProgressingState->addTransition(this, &MasterTrackViewModelContextData::gainTransactionWillCommit, gainCommittingState); + gainProgressingState->addTransition(this, &MasterTrackViewModelContextData::gainTransactionWillAbort, gainAbortingState); + gainCommittingState->addTransition(idleState); + gainAbortingState->addTransition(idleState); + + idleState->addTransition(this, &MasterTrackViewModelContextData::panTransactionWillStart, panPendingState); + panPendingState->addTransition(this, &MasterTrackViewModelContextData::panTransactionStarted, panProgressingState); + panPendingState->addTransition(this, &MasterTrackViewModelContextData::panTransactionNotStarted, idleState); + panProgressingState->addTransition(this, &MasterTrackViewModelContextData::panTransactionWillCommit, panCommittingState); + panProgressingState->addTransition(this, &MasterTrackViewModelContextData::panTransactionWillAbort, panAbortingState); + panCommittingState->addTransition(idleState); + panAbortingState->addTransition(idleState); + + idleState->addTransition(this, &MasterTrackViewModelContextData::multiChannelTransactionWillStart, multiChannelPendingState); + multiChannelPendingState->addTransition(this, &MasterTrackViewModelContextData::multiChannelTransactionStarted, multiChannelEditingState); + multiChannelPendingState->addTransition(this, &MasterTrackViewModelContextData::multiChannelTransactionNotStarted, idleState); + multiChannelEditingState->addTransition(this, &MasterTrackViewModelContextData::multiChannelTransactionWillFinish, multiChannelFinishingState); + multiChannelFinishingState->addTransition(idleState); + + auto logEntered = [](const char *name) { qCInfo(lcMasterTrackViewModelContextData) << name << "entered"; }; + auto logExited = [](const char *name) { qCInfo(lcMasterTrackViewModelContextData) << name << "exited"; }; + + connect(idleState, &QState::entered, this, [=] { + logEntered("Idle state"); + }); + connect(idleState, &QState::exited, this, [=] { + logExited("Idle state"); + }); + + connect(mutePendingState, &QState::entered, this, [=, this] { + logEntered("Mute pending state"); + onMutePendingStateEntered(); + }); + connect(mutePendingState, &QState::exited, this, [=] { + logExited("Mute pending state"); + }); + connect(muteEditingState, &QState::entered, this, [=] { + logEntered("Mute editing state"); + }); + connect(muteEditingState, &QState::exited, this, [=] { + logExited("Mute editing state"); + }); + connect(muteFinishingState, &QState::entered, this, [=, this] { + logEntered("Mute finishing state"); + onMuteFinishingStateEntered(); + }); + connect(muteFinishingState, &QState::exited, this, [=] { + logExited("Mute finishing state"); + }); + + connect(gainPendingState, &QState::entered, this, [=, this] { + logEntered("Gain pending state"); + onGainPendingStateEntered(); + }); + connect(gainPendingState, &QState::exited, this, [=] { + logExited("Gain pending state"); + }); + connect(gainProgressingState, &QState::entered, this, [=] { + logEntered("Gain progressing state"); + }); + connect(gainProgressingState, &QState::exited, this, [=] { + logExited("Gain progressing state"); + }); + connect(gainCommittingState, &QState::entered, this, [=, this] { + logEntered("Gain committing state"); + onGainCommittingStateEntered(); + }); + connect(gainCommittingState, &QState::exited, this, [=] { + logExited("Gain committing state"); + }); + connect(gainAbortingState, &QState::entered, this, [=, this] { + logEntered("Gain aborting state"); + onGainAbortingStateEntered(); + }); + connect(gainAbortingState, &QState::exited, this, [=] { + logExited("Gain aborting state"); + }); + + connect(panPendingState, &QState::entered, this, [=, this] { + logEntered("Pan pending state"); + onPanPendingStateEntered(); + }); + connect(panPendingState, &QState::exited, this, [=] { + logExited("Pan pending state"); + }); + connect(panProgressingState, &QState::entered, this, [=] { + logEntered("Pan progressing state"); + }); + connect(panProgressingState, &QState::exited, this, [=] { + logExited("Pan progressing state"); + }); + connect(panCommittingState, &QState::entered, this, [=, this] { + logEntered("Pan committing state"); + onPanCommittingStateEntered(); + }); + connect(panCommittingState, &QState::exited, this, [=] { + logExited("Pan committing state"); + }); + connect(panAbortingState, &QState::entered, this, [=, this] { + logEntered("Pan aborting state"); + onPanAbortingStateEntered(); + }); + connect(panAbortingState, &QState::exited, this, [=] { + logExited("Pan aborting state"); + }); + + connect(multiChannelPendingState, &QState::entered, this, [=, this] { + logEntered("Multi-channel pending state"); + onMultiChannelPendingStateEntered(); + }); + connect(multiChannelPendingState, &QState::exited, this, [=] { + logExited("Multi-channel pending state"); + }); + connect(multiChannelEditingState, &QState::entered, this, [=] { + logEntered("Multi-channel editing state"); + }); + connect(multiChannelEditingState, &QState::exited, this, [=] { + logExited("Multi-channel editing state"); + }); + connect(multiChannelFinishingState, &QState::entered, this, [=, this] { + logEntered("Multi-channel finishing state"); + onMultiChannelFinishingStateEntered(); + }); + connect(multiChannelFinishingState, &QState::exited, this, [=] { + logExited("Multi-channel finishing state"); + }); + } + + void MasterTrackViewModelContextData::init() { + Q_Q(ProjectViewModelContext); + document = q->windowHandle()->projectDocumentContext()->document(); + master = document->model()->master(); + + masterTrackListViewModel = new sflow::ListViewModel(q); + masterTrackViewModel = new sflow::TrackViewModel(masterTrackListViewModel); + + initStateMachine(); + } + + void MasterTrackViewModelContextData::bindMasterTrackViewModel() { + auto control = master->control(); + + connect(control, &dspx::Control::muteChanged, masterTrackViewModel, [=](bool mute) { + if (masterTrackViewModel->isMute() == mute) { + return; + } + masterTrackViewModel->setMute(mute); + }); + connect(control, &dspx::Control::gainChanged, masterTrackViewModel, [=](double gain) { + const double db = toDecibel(gain); + if (qFuzzyCompare(masterTrackViewModel->gain(), db)) { + return; + } + masterTrackViewModel->setGain(db); + }); + connect(control, &dspx::Control::panChanged, masterTrackViewModel, [=](double pan) { + if (qFuzzyCompare(masterTrackViewModel->pan(), pan)) { + return; + } + masterTrackViewModel->setPan(pan); + }); + connect(master, &dspx::Master::multiChannelOutputChanged, this, [=](bool enabled) { + if (masterTrackViewModel->multiChannelOutput() == enabled) { + return; + } + syncingFromModel = true; + masterTrackViewModel->setMultiChannelOutput(enabled); + syncingFromModel = false; + }); + + connect(masterTrackViewModel, &sflow::TrackViewModel::muteChanged, this, [=] { + if (!stateMachine->configuration().contains(muteEditingState)) { + masterTrackViewModel->setMute(control->mute()); + return; + } + }); + connect(masterTrackViewModel, &sflow::TrackViewModel::gainChanged, this, [=] { + if (!stateMachine->configuration().contains(gainProgressingState)) { + masterTrackViewModel->setGain(toDecibel(control->gain())); + return; + } + }); + connect(masterTrackViewModel, &sflow::TrackViewModel::panChanged, this, [=] { + if (!stateMachine->configuration().contains(panProgressingState)) { + masterTrackViewModel->setPan(control->pan()); + return; + } + }); + connect(masterTrackViewModel, &sflow::TrackViewModel::multiChannelOutputChanged, this, [=] { + if (syncingFromModel) { + return; + } + Q_EMIT multiChannelTransactionWillStart(); + QMetaObject::invokeMethod(this, [this] { + Q_EMIT multiChannelTransactionWillFinish(); + }, Qt::QueuedConnection); + }); + + masterTrackViewModel->setName(tr("Master")); + masterTrackViewModel->setMute(control->mute()); + masterTrackViewModel->setGain(toDecibel(control->gain())); + masterTrackViewModel->setPan(control->pan()); + masterTrackViewModel->setMultiChannelOutput(master->multiChannelOutput()); + + masterTrackListViewModel->insertItem(0, masterTrackViewModel); + } + + sflow::TrackListInteractionController *MasterTrackViewModelContextData::createController(QObject *parent) { + auto controller = new sflow::TrackListInteractionController(parent); + controller->setInteraction({}); + controller->setItemInteraction(sflow::TrackListInteractionController::EditMute | sflow::TrackListInteractionController::EditGain | sflow::TrackListInteractionController::EditPan | sflow::TrackListInteractionController::EditMultiChannelOutput); + + connect(controller, &sflow::TrackListInteractionController::muteEditingStarted, this, [=](QQuickItem *, int) { + Q_EMIT muteTransactionWillStart(); + }); + connect(controller, &sflow::TrackListInteractionController::muteEditingFinished, this, [=](QQuickItem *, int) { + Q_EMIT muteTransactionWillFinish(); + }); + + connect(controller, &sflow::TrackListInteractionController::gainEditingStarted, this, [=](QQuickItem *, int) { + Q_EMIT gainTransactionWillStart(); + }); + connect(controller, &sflow::TrackListInteractionController::gainEditingCommitted, this, [=](QQuickItem *, int) { + Q_EMIT gainTransactionWillCommit(); + }); + connect(controller, &sflow::TrackListInteractionController::gainEditingAborted, this, [=](QQuickItem *, int) { + Q_EMIT gainTransactionWillAbort(); + }); + + connect(controller, &sflow::TrackListInteractionController::panEditingStarted, this, [=](QQuickItem *, int) { + Q_EMIT panTransactionWillStart(); + }); + connect(controller, &sflow::TrackListInteractionController::panEditingCommitted, this, [=](QQuickItem *, int) { + Q_EMIT panTransactionWillCommit(); + }); + connect(controller, &sflow::TrackListInteractionController::panEditingAborted, this, [=](QQuickItem *, int) { + Q_EMIT panTransactionWillAbort(); + }); + + return controller; + } + + void MasterTrackViewModelContextData::onMutePendingStateEntered() { + muteTransactionId = document->transactionController()->beginTransaction(); + if (muteTransactionId != Core::TransactionController::TransactionId::Invalid) { + Q_EMIT muteTransactionStarted(); + } else { + Q_EMIT muteTransactionNotStarted(); + } + } + + void MasterTrackViewModelContextData::onMuteFinishingStateEntered() { + if (muteTransactionId == Core::TransactionController::TransactionId::Invalid) { + return; + } + const bool newValue = masterTrackViewModel->isMute(); + if (newValue == master->control()->mute()) { + document->transactionController()->abortTransaction(muteTransactionId); + } else { + master->control()->setMute(newValue); + document->transactionController()->commitTransaction(muteTransactionId, tr("Editing master mute")); + } + muteTransactionId = {}; + } + + void MasterTrackViewModelContextData::onGainPendingStateEntered() { + gainTransactionId = document->transactionController()->beginTransaction(); + if (gainTransactionId != Core::TransactionController::TransactionId::Invalid) { + Q_EMIT gainTransactionStarted(); + } else { + Q_EMIT gainTransactionNotStarted(); + } + } + + void MasterTrackViewModelContextData::onGainCommittingStateEntered() { + if (gainTransactionId == Core::TransactionController::TransactionId::Invalid) { + return; + } + const double newLinear = toLinear(masterTrackViewModel->gain()); + if (qFuzzyCompare(newLinear, master->control()->gain())) { + document->transactionController()->abortTransaction(gainTransactionId); + } else { + master->control()->setGain(newLinear); + document->transactionController()->commitTransaction(gainTransactionId, tr("Adjusting master gain")); + } + gainTransactionId = {}; + } + + void MasterTrackViewModelContextData::onGainAbortingStateEntered() { + document->transactionController()->abortTransaction(gainTransactionId); + gainTransactionId = {}; + } + + void MasterTrackViewModelContextData::onPanPendingStateEntered() { + panTransactionId = document->transactionController()->beginTransaction(); + if (panTransactionId != Core::TransactionController::TransactionId::Invalid) { + Q_EMIT panTransactionStarted(); + } else { + Q_EMIT panTransactionNotStarted(); + } + } + + void MasterTrackViewModelContextData::onPanCommittingStateEntered() { + if (panTransactionId == Core::TransactionController::TransactionId::Invalid) { + return; + } + const double newPan = masterTrackViewModel->pan(); + if (qFuzzyCompare(newPan, master->control()->pan())) { + document->transactionController()->abortTransaction(panTransactionId); + } else { + master->control()->setPan(newPan); + document->transactionController()->commitTransaction(panTransactionId, tr("Adjusting master pan")); + } + panTransactionId = {}; + } + + void MasterTrackViewModelContextData::onPanAbortingStateEntered() { + document->transactionController()->abortTransaction(panTransactionId); + panTransactionId = {}; + } + + void MasterTrackViewModelContextData::onMultiChannelPendingStateEntered() { + multiChannelTransactionId = document->transactionController()->beginTransaction(); + if (multiChannelTransactionId != Core::TransactionController::TransactionId::Invalid) { + Q_EMIT multiChannelTransactionStarted(); + } else { + syncingFromModel = true; + masterTrackViewModel->setMultiChannelOutput(master->multiChannelOutput()); + syncingFromModel = false; + Q_EMIT multiChannelTransactionNotStarted(); + } + } + + void MasterTrackViewModelContextData::onMultiChannelFinishingStateEntered() { + if (multiChannelTransactionId == Core::TransactionController::TransactionId::Invalid) { + return; + } + const bool newValue = masterTrackViewModel->multiChannelOutput(); + if (newValue == master->multiChannelOutput()) { + document->transactionController()->abortTransaction(multiChannelTransactionId); + } else { + master->setMultiChannelOutput(newValue); + document->transactionController()->commitTransaction(multiChannelTransactionId, tr("Editing master multi-channel output")); + } + multiChannelTransactionId = {}; + } + +} diff --git a/src/plugins/visualeditor/core/viewmodelbinding/MasterTrackViewModelContextData_p.h b/src/plugins/visualeditor/core/viewmodelbinding/MasterTrackViewModelContextData_p.h new file mode 100644 index 00000000..cd7aa082 --- /dev/null +++ b/src/plugins/visualeditor/core/viewmodelbinding/MasterTrackViewModelContextData_p.h @@ -0,0 +1,114 @@ +#ifndef DIFFSCOPE_VISUALEDITOR_MASTERTRACKVIEWMODELCONTEXTDATA_P_H +#define DIFFSCOPE_VISUALEDITOR_MASTERTRACKVIEWMODELCONTEXTDATA_P_H + +#include + +#include +#include + +class QState; +class QStateMachine; +class QQuickItem; + +namespace dspx { + class Master; +} + +namespace sflow { + class ListViewModel; + class TrackListInteractionController; + class TrackViewModel; +} + +namespace Core { + class DspxDocument; +} + +namespace VisualEditor { + + class MasterTrackViewModelContextData : public QObject { + Q_OBJECT + Q_DECLARE_PUBLIC(ProjectViewModelContext) + public: + ProjectViewModelContext *q_ptr{}; + + Core::DspxDocument *document{}; + dspx::Master *master{}; + + sflow::ListViewModel *masterTrackListViewModel{}; + sflow::TrackViewModel *masterTrackViewModel{}; + + QStateMachine *stateMachine{}; + QState *idleState{}; + + QState *mutePendingState{}; + QState *muteEditingState{}; + QState *muteFinishingState{}; + + QState *gainPendingState{}; + QState *gainProgressingState{}; + QState *gainCommittingState{}; + QState *gainAbortingState{}; + + QState *panPendingState{}; + QState *panProgressingState{}; + QState *panCommittingState{}; + QState *panAbortingState{}; + + QState *multiChannelPendingState{}; + QState *multiChannelEditingState{}; + QState *multiChannelFinishingState{}; + + Core::TransactionController::TransactionId muteTransactionId{}; + Core::TransactionController::TransactionId gainTransactionId{}; + Core::TransactionController::TransactionId panTransactionId{}; + Core::TransactionController::TransactionId multiChannelTransactionId{}; + + bool syncingFromModel{false}; + + void init(); + void initStateMachine(); + void bindMasterTrackViewModel(); + sflow::TrackListInteractionController *createController(QObject *parent); + + void onMutePendingStateEntered(); + void onMuteFinishingStateEntered(); + + void onGainPendingStateEntered(); + void onGainCommittingStateEntered(); + void onGainAbortingStateEntered(); + + void onPanPendingStateEntered(); + void onPanCommittingStateEntered(); + void onPanAbortingStateEntered(); + + void onMultiChannelPendingStateEntered(); + void onMultiChannelFinishingStateEntered(); + + Q_SIGNALS: + void muteTransactionWillStart(); + void muteTransactionStarted(); + void muteTransactionNotStarted(); + void muteTransactionWillFinish(); + + void gainTransactionWillStart(); + void gainTransactionStarted(); + void gainTransactionNotStarted(); + void gainTransactionWillCommit(); + void gainTransactionWillAbort(); + + void panTransactionWillStart(); + void panTransactionStarted(); + void panTransactionNotStarted(); + void panTransactionWillCommit(); + void panTransactionWillAbort(); + + void multiChannelTransactionWillStart(); + void multiChannelTransactionStarted(); + void multiChannelTransactionNotStarted(); + void multiChannelTransactionWillFinish(); + }; + +} + +#endif //DIFFSCOPE_VISUALEDITOR_MASTERTRACKVIEWMODELCONTEXTDATA_P_H diff --git a/src/plugins/visualeditor/core/viewmodelbinding/PlaybackViewModelContextData.cpp b/src/plugins/visualeditor/core/viewmodelbinding/PlaybackViewModelContextData.cpp new file mode 100644 index 00000000..6b02f60d --- /dev/null +++ b/src/plugins/visualeditor/core/viewmodelbinding/PlaybackViewModelContextData.cpp @@ -0,0 +1,252 @@ +#include "PlaybackViewModelContextData_p.h" + +#include + +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include + +namespace VisualEditor { + + Q_STATIC_LOGGING_CATEGORY(lcPlaybackViewModelContextData, "diffscope.visualeditor.playbackviewmodelcontextdata") + + void PlaybackViewModelContextData::initStateMachine() { + stateMachine = new QStateMachine(QState::ExclusiveStates, this); + idleState = new QState; + rubberBandDraggingState = new QState; + positionIndicatorMovingState = new QState; + loopRangeAdjustPendingState = new QState; + loopRangeAdjustingState = new QState; + stateMachine->addState(idleState); + stateMachine->addState(rubberBandDraggingState); + stateMachine->addState(positionIndicatorMovingState); + stateMachine->addState(loopRangeAdjustPendingState); + stateMachine->addState(loopRangeAdjustingState); + stateMachine->setInitialState(idleState); + stateMachine->start(); + + idleState->addTransition(this, &PlaybackViewModelContextData::rubberBandDragWillStart, rubberBandDraggingState); + rubberBandDraggingState->addTransition(this, &PlaybackViewModelContextData::rubberBandDragWillFinish, idleState); + + idleState->addTransition(this, &PlaybackViewModelContextData::positionIndicatorMoveWillStart, positionIndicatorMovingState); + positionIndicatorMovingState->addTransition(this, &PlaybackViewModelContextData::positionIndicatorMoveWillFinish, idleState); + + idleState->addTransition(this, &PlaybackViewModelContextData::loopRangeTransactionWillStart, loopRangeAdjustPendingState); + loopRangeAdjustPendingState->addTransition(this, &PlaybackViewModelContextData::loopRangeTransactionStarted, loopRangeAdjustingState); + loopRangeAdjustPendingState->addTransition(this, &PlaybackViewModelContextData::loopRangeTransactionNotStarted, idleState); + loopRangeAdjustingState->addTransition(this, &PlaybackViewModelContextData::loopRangeTransactionWillFinish, idleState); + + connect(idleState, &QState::entered, this, [=, this] { + qCInfo(lcPlaybackViewModelContextData) << "Idle state entered"; + }); + connect(idleState, &QState::exited, this, [=, this] { + qCInfo(lcPlaybackViewModelContextData) << "Idle state exited"; + }); + connect(rubberBandDraggingState, &QState::entered, this, [=, this] { + qCInfo(lcPlaybackViewModelContextData) << "Rubber band dragging state entered"; + }); + connect(rubberBandDraggingState, &QState::exited, this, [=, this] { + qCInfo(lcPlaybackViewModelContextData) << "Rubber band dragging state exited"; + }); + connect(positionIndicatorMovingState, &QState::entered, this, [=, this] { + qCInfo(lcPlaybackViewModelContextData) << "Position indicator moving state entered"; + onPositionIndicatorMovingStateEntered(); + }); + connect(positionIndicatorMovingState, &QState::exited, this, [=, this] { + qCInfo(lcPlaybackViewModelContextData) << "Position indicator moving state exited"; + onPositionIndicatorMovingStateExited(); + }); + connect(loopRangeAdjustPendingState, &QState::entered, this, [=, this] { + qCInfo(lcPlaybackViewModelContextData) << "Loop range adjust pending state entered"; + onLoopRangeAdjustPendingStateEntered(); + }); + connect(loopRangeAdjustPendingState, &QState::exited, this, [=, this] { + qCInfo(lcPlaybackViewModelContextData) << "Loop range adjust pending state exited"; + }); + connect(loopRangeAdjustingState, &QState::entered, this, [=, this] { + qCInfo(lcPlaybackViewModelContextData) << "Loop range adjusting state entered"; + }); + connect(loopRangeAdjustingState, &QState::exited, this, [=, this] { + qCInfo(lcPlaybackViewModelContextData) << "Loop range adjusting state exited"; + onLoopRangeAdjustingStateExited(); + }); + } + + void PlaybackViewModelContextData::init() { + Q_Q(ProjectViewModelContext); + document = q->windowHandle()->projectDocumentContext()->document(); + timeline = document->model()->timeline(); + windowHandle = q->windowHandle(); + playbackViewModel = new sflow::PlaybackViewModel(q); + + initStateMachine(); + } + + void PlaybackViewModelContextData::bindPlaybackViewModel() { + auto projectTimeline = windowHandle->projectTimeline(); + auto documentTimeline = timeline; + QObject::connect(projectTimeline, &Core::ProjectTimeline::positionChanged, playbackViewModel, [=] { + if (playbackViewModel->primaryPosition() == projectTimeline->position()) + return; + qCDebug(lcPlaybackViewModelContextData) << "Project timeline position updated" << projectTimeline->position(); + playbackViewModel->setPrimaryPosition(projectTimeline->position()); + }); + QObject::connect(projectTimeline, &Core::ProjectTimeline::lastPositionChanged, playbackViewModel, [=] { + if (playbackViewModel->secondaryPosition() == projectTimeline->lastPosition()) + return; + qCDebug(lcPlaybackViewModelContextData) << "Project timeline last position updated" << projectTimeline->lastPosition(); + playbackViewModel->setSecondaryPosition(projectTimeline->lastPosition()); + }); + QObject::connect(playbackViewModel, &sflow::PlaybackViewModel::primaryPositionChanged, projectTimeline, [=] { + if (projectTimeline->position() == playbackViewModel->primaryPosition()) + return; + qCDebug(lcPlaybackViewModelContextData) << "Playback view model primary position updated" << playbackViewModel->primaryPosition(); + projectTimeline->setPosition(playbackViewModel->primaryPosition()); + }); + QObject::connect(playbackViewModel, &sflow::PlaybackViewModel::secondaryPositionChanged, projectTimeline, [=] { + if (projectTimeline->lastPosition() == playbackViewModel->secondaryPosition()) + return; + qCDebug(lcPlaybackViewModelContextData) << "Playback view model secondary position updated" << playbackViewModel->secondaryPosition(); + projectTimeline->setLastPosition(playbackViewModel->secondaryPosition()); + }); + playbackViewModel->setPrimaryPosition(projectTimeline->position()); + playbackViewModel->setSecondaryPosition(projectTimeline->lastPosition()); + + QObject::connect(documentTimeline, &dspx::Timeline::loopStartChanged, playbackViewModel, [=] { + const auto loopStart = documentTimeline->loopStart(); + if (playbackViewModel->loopStart() == loopStart) + return; + qCDebug(lcPlaybackViewModelContextData) << "Document timeline loop start updated" << loopStart; + playbackViewModel->setLoopStart(loopStart); + }); + QObject::connect(documentTimeline, &dspx::Timeline::loopLengthChanged, playbackViewModel, [=] { + const int loopLength = documentTimeline->loopLength(); + const int targetLength = documentTimeline->isLoopEnabled() ? loopLength : -1; + if (playbackViewModel->loopLength() == targetLength) + return; + qCDebug(lcPlaybackViewModelContextData) << "Document timeline loop length updated" << targetLength; + playbackViewModel->setLoopLength(targetLength); + }); + QObject::connect(documentTimeline, &dspx::Timeline::loopEnabledChanged, playbackViewModel, [=](bool enabled) { + const int targetLength = enabled ? documentTimeline->loopLength() : -1; + if (playbackViewModel->loopLength() == targetLength) + return; + qCDebug(lcPlaybackViewModelContextData) << "Document timeline loop enabled changed" << enabled << "-> length" << targetLength; + playbackViewModel->setLoopLength(targetLength); + }); + + QObject::connect(playbackViewModel, &sflow::PlaybackViewModel::loopStartChanged, this, [=, this] { + if (!stateMachine->configuration().contains(loopRangeAdjustingState)) { + const int loopStart = documentTimeline->loopStart(); + if (playbackViewModel->loopStart() != loopStart) { + playbackViewModel->setLoopStart(loopStart); + } + return; + } + const int clampedLoopStart = std::max(0, playbackViewModel->loopStart()); + if (clampedLoopStart != playbackViewModel->loopStart()) { + playbackViewModel->setLoopStart(clampedLoopStart); + } + qCDebug(lcPlaybackViewModelContextData) << "Loop view start updated" << clampedLoopStart; + pendingLoopStart = clampedLoopStart; + }); + + QObject::connect(playbackViewModel, &sflow::PlaybackViewModel::loopLengthChanged, this, [=, this] { + if (!stateMachine->configuration().contains(loopRangeAdjustingState)) { + const int loopLength = documentTimeline->isLoopEnabled() ? documentTimeline->loopLength() : -1; + if (playbackViewModel->loopLength() != loopLength) { + playbackViewModel->setLoopLength(loopLength); + } + return; + } + const int clampedLoopLength = std::max(1, playbackViewModel->loopLength()); + if (clampedLoopLength != playbackViewModel->loopLength()) { + playbackViewModel->setLoopLength(clampedLoopLength); + } + qCDebug(lcPlaybackViewModelContextData) << "Loop view length updated" << clampedLoopLength; + pendingLoopLength = clampedLoopLength; + }); + + playbackViewModel->setLoopStart(documentTimeline->loopStart()); + playbackViewModel->setLoopLength(documentTimeline->isLoopEnabled() ? documentTimeline->loopLength() : -1); + } + + sflow::TimelineInteractionController *PlaybackViewModelContextData::createController(QObject *parent) { + auto controller = new sflow::TimelineInteractionController(parent); + controller->setInteraction(sflow::TimelineInteractionController::MovePositionIndicator | sflow::TimelineInteractionController::ZoomByRubberBand | sflow::TimelineInteractionController::AdjustLoopRange); + + connect(controller, &sflow::TimelineInteractionController::rubberBandDraggingStarted, this, [=](QQuickItem *) { + Q_EMIT rubberBandDragWillStart(); + }); + connect(controller, &sflow::TimelineInteractionController::rubberBandDraggingFinished, this, [=](QQuickItem *) { + Q_EMIT rubberBandDragWillFinish(); + }); + connect(controller, &sflow::TimelineInteractionController::positionIndicatorMovingStarted, this, [=](QQuickItem *) { + Q_EMIT positionIndicatorMoveWillStart(); + }); + connect(controller, &sflow::TimelineInteractionController::positionIndicatorMovingFinished, this, [=](QQuickItem *) { + Q_EMIT positionIndicatorMoveWillFinish(); + }); + connect(controller, &sflow::TimelineInteractionController::loopRangeAdjustingStarted, this, [=](QQuickItem *) { + Q_EMIT loopRangeTransactionWillStart(); + }); + connect(controller, &sflow::TimelineInteractionController::loopRangeAdjustingFinished, this, [=](QQuickItem *) { + Q_EMIT loopRangeTransactionWillFinish(); + }); + + return controller; + } + + void PlaybackViewModelContextData::onLoopRangeAdjustPendingStateEntered() { + loopRangeTransactionId = document->transactionController()->beginTransaction(); + if (loopRangeTransactionId != Core::TransactionController::TransactionId::Invalid) { + pendingLoopStart = timeline->loopStart(); + pendingLoopLength = timeline->loopLength(); + Q_EMIT loopRangeTransactionStarted(); + } else { + Q_EMIT loopRangeTransactionNotStarted(); + } + } + + void PlaybackViewModelContextData::onPositionIndicatorMovingStateEntered() { + // TODO + } + + void PlaybackViewModelContextData::onPositionIndicatorMovingStateExited() { + // TODO + } + + void PlaybackViewModelContextData::onLoopRangeAdjustingStateExited() { + if (loopRangeTransactionId == Core::TransactionController::TransactionId::Invalid) { + return; + } + + const bool hasChange = pendingLoopStart != timeline->loopStart() || pendingLoopLength != timeline->loopLength(); + if (hasChange) { + timeline->setLoopStart(pendingLoopStart); + timeline->setLoopLength(pendingLoopLength); + document->transactionController()->commitTransaction(loopRangeTransactionId, tr("Adjust loop range")); + } else { + document->transactionController()->abortTransaction(loopRangeTransactionId); + } + + loopRangeTransactionId = {}; + pendingLoopStart = {}; + pendingLoopLength = {}; + } + +} diff --git a/src/plugins/visualeditor/core/viewmodelbinding/PlaybackViewModelContextData_p.h b/src/plugins/visualeditor/core/viewmodelbinding/PlaybackViewModelContextData_p.h new file mode 100644 index 00000000..541a6fa3 --- /dev/null +++ b/src/plugins/visualeditor/core/viewmodelbinding/PlaybackViewModelContextData_p.h @@ -0,0 +1,75 @@ +#ifndef DIFFSCOPE_VISUALEDITOR_PLAYBACKVIEWMODELCONTEXTDATA_P_H +#define DIFFSCOPE_VISUALEDITOR_PLAYBACKVIEWMODELCONTEXTDATA_P_H + +#include + +#include + +#include + +class QState; +class QStateMachine; +class QQuickItem; + +namespace sflow { + class TimelineInteractionController; +} + +namespace dspx { + class Timeline; +} + +namespace Core { + class DspxDocument; +} + +namespace VisualEditor { + + class PlaybackViewModelContextData : public QObject { + Q_OBJECT + Q_DECLARE_PUBLIC(ProjectViewModelContext) + public: + ProjectViewModelContext *q_ptr; + + Core::DspxDocument *document; + Core::ProjectWindowInterface *windowHandle; + dspx::Timeline *timeline; + sflow::PlaybackViewModel *playbackViewModel; + + QStateMachine *stateMachine; + QState *idleState; + QState *rubberBandDraggingState; + QState *positionIndicatorMovingState; + QState *loopRangeAdjustPendingState; + QState *loopRangeAdjustingState; + + Core::TransactionController::TransactionId loopRangeTransactionId{}; + int pendingLoopStart{}; + int pendingLoopLength{}; + + void init(); + void initStateMachine(); + void bindPlaybackViewModel(); + sflow::TimelineInteractionController *createController(QObject *parent); + + void onLoopRangeAdjustPendingStateEntered(); + void onLoopRangeAdjustingStateExited(); + void onPositionIndicatorMovingStateEntered(); + void onPositionIndicatorMovingStateExited(); + + Q_SIGNALS: + void rubberBandDragWillStart(); + void rubberBandDragWillFinish(); + + void positionIndicatorMoveWillStart(); + void positionIndicatorMoveWillFinish(); + + void loopRangeTransactionWillStart(); + void loopRangeTransactionStarted(); + void loopRangeTransactionNotStarted(); + void loopRangeTransactionWillFinish(); + }; + +} + +#endif //DIFFSCOPE_VISUALEDITOR_PLAYBACKVIEWMODELCONTEXTDATA_P_H diff --git a/src/plugins/visualeditor/core/TempoViewModelContextData.cpp b/src/plugins/visualeditor/core/viewmodelbinding/TempoViewModelContextData.cpp similarity index 91% rename from src/plugins/visualeditor/core/TempoViewModelContextData.cpp rename to src/plugins/visualeditor/core/viewmodelbinding/TempoViewModelContextData.cpp index b0490ac7..4c693f6d 100644 --- a/src/plugins/visualeditor/core/TempoViewModelContextData.cpp +++ b/src/plugins/visualeditor/core/viewmodelbinding/TempoViewModelContextData.cpp @@ -1,5 +1,4 @@ #include "TempoViewModelContextData_p.h" -#include "TempoSelectionController_p.h" #include #include @@ -27,6 +26,7 @@ #include #include +#include namespace VisualEditor { @@ -182,25 +182,17 @@ namespace VisualEditor { controller->setInteraction(sflow::LabelSequenceInteractionController::SelectByRubberBand); controller->setItemInteraction(sflow::LabelSequenceInteractionController::Move | sflow::LabelSequenceInteractionController::Select); - connect(controller, &sflow::LabelSequenceInteractionController::interactionOperationStarted, this, [=](QQuickItem *, sflow::LabelSequenceInteractionController::InteractionFlag type) { - if (type == sflow::LabelSequenceInteractionController::SelectByRubberBand) { - Q_EMIT rubberBandDragWillStart(); - } + connect(controller, &sflow::LabelSequenceInteractionController::rubberBandDraggingStarted, this, [=](QQuickItem *) { + Q_EMIT rubberBandDragWillStart(); }); - connect(controller, &sflow::LabelSequenceInteractionController::interactionOperationFinished, this, [=](QQuickItem *, sflow::LabelSequenceInteractionController::InteractionFlag type) { - if (type == sflow::LabelSequenceInteractionController::SelectByRubberBand) { - Q_EMIT rubberBandDragWillFinish(); - } + connect(controller, &sflow::LabelSequenceInteractionController::rubberBandDraggingFinished, this, [=](QQuickItem *) { + Q_EMIT rubberBandDragWillFinish(); }); - connect(controller, &sflow::LabelSequenceInteractionController::itemInteractionOperationStarted, this, [=](QQuickItem *, sflow::LabelViewModel *, sflow::LabelSequenceInteractionController::ItemInteractionFlag type) { - if (type == sflow::LabelSequenceInteractionController::Move) { - Q_EMIT moveTransactionWillStart(); - } + connect(controller, &sflow::LabelSequenceInteractionController::movingStarted, this, [=](QQuickItem *, sflow::LabelViewModel *) { + Q_EMIT moveTransactionWillStart(); }); - connect(controller, &sflow::LabelSequenceInteractionController::itemInteractionOperationFinished, this, [=](QQuickItem *, sflow::LabelViewModel *, sflow::LabelSequenceInteractionController::ItemInteractionFlag type) { - if (type == sflow::LabelSequenceInteractionController::Move) { - Q_EMIT moveTransactionWillFinish(); - } + connect(controller, &sflow::LabelSequenceInteractionController::movingFinished, this, [=](QQuickItem *, sflow::LabelViewModel *) { + Q_EMIT moveTransactionWillFinish(); }); connect(controller, &sflow::LabelSequenceInteractionController::doubleClicked, this, [=](QQuickItem *labelSequenceItem, int position) { @@ -234,6 +226,11 @@ namespace VisualEditor { void TempoViewModelContextData::onMovingStateExited() { Q_Q(ProjectViewModelContext); QSet updatedItems; + if (transactionalUpdatedTempos.isEmpty()) { + document->transactionController()->abortTransaction(moveTransactionId); + moveTransactionId = {}; + return; + } for (auto viewItem : transactionalUpdatedTempos) { auto item = tempoDocumentItemMap.value(viewItem); Q_ASSERT(item); diff --git a/src/plugins/visualeditor/core/TempoViewModelContextData_p.h b/src/plugins/visualeditor/core/viewmodelbinding/TempoViewModelContextData_p.h similarity index 100% rename from src/plugins/visualeditor/core/TempoViewModelContextData_p.h rename to src/plugins/visualeditor/core/viewmodelbinding/TempoViewModelContextData_p.h diff --git a/src/plugins/visualeditor/core/viewmodelbinding/TrackViewModelContextData.cpp b/src/plugins/visualeditor/core/viewmodelbinding/TrackViewModelContextData.cpp new file mode 100644 index 00000000..04cf8e7c --- /dev/null +++ b/src/plugins/visualeditor/core/viewmodelbinding/TrackViewModelContextData.cpp @@ -0,0 +1,905 @@ +#include "TrackViewModelContextData_p.h" + +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +namespace VisualEditor { + + Q_STATIC_LOGGING_CATEGORY(lcTrackViewModelContextData, "diffscope.visualeditor.trackviewmodelcontextdata") + + static inline double toDecibel(double gain) { + return SVS::DecibelLinearizer::gainToDecibels(gain); + } + + static inline double toLinear(double decibel) { + return SVS::DecibelLinearizer::decibelsToGain(decibel); + } + + static inline double getClampedHeight(dspx::Track *track) { + return qMax(40.0, track->height()); + } + + void TrackViewModelContextData::initStateMachine() { + stateMachine = new QStateMachine(QState::ExclusiveStates, this); + + idleState = new QState; + rubberBandDraggingState = new QState; + + movePendingState = new QState; + moveProcessingState = new QState; + moveCommittingState = new QState; + moveAbortingState = new QState; + + mutePendingState = new QState; + muteEditingState = new QState; + muteFinishingState = new QState; + + soloPendingState = new QState; + soloEditingState = new QState; + soloFinishingState = new QState; + + recordPendingState = new QState; + recordEditingState = new QState; + recordFinishingState = new QState; + + namePendingState = new QState; + nameProgressingState = new QState; + nameCommittingState = new QState; + nameAbortingState = new QState; + + gainPendingState = new QState; + gainProgressingState = new QState; + gainCommittingState = new QState; + gainAbortingState = new QState; + + panPendingState = new QState; + panProgressingState = new QState; + panCommittingState = new QState; + panAbortingState = new QState; + + heightPendingState = new QState; + heightEditingState = new QState; + heightFinishingState = new QState; + + stateMachine->addState(idleState); + stateMachine->addState(rubberBandDraggingState); + + stateMachine->addState(movePendingState); + stateMachine->addState(moveProcessingState); + stateMachine->addState(moveCommittingState); + stateMachine->addState(moveAbortingState); + + stateMachine->addState(mutePendingState); + stateMachine->addState(muteEditingState); + stateMachine->addState(muteFinishingState); + + stateMachine->addState(soloPendingState); + stateMachine->addState(soloEditingState); + stateMachine->addState(soloFinishingState); + + stateMachine->addState(recordPendingState); + stateMachine->addState(recordEditingState); + stateMachine->addState(recordFinishingState); + + stateMachine->addState(namePendingState); + stateMachine->addState(nameProgressingState); + stateMachine->addState(nameCommittingState); + stateMachine->addState(nameAbortingState); + + stateMachine->addState(gainPendingState); + stateMachine->addState(gainProgressingState); + stateMachine->addState(gainCommittingState); + stateMachine->addState(gainAbortingState); + + stateMachine->addState(panPendingState); + stateMachine->addState(panProgressingState); + stateMachine->addState(panCommittingState); + stateMachine->addState(panAbortingState); + + stateMachine->addState(heightPendingState); + stateMachine->addState(heightEditingState); + stateMachine->addState(heightFinishingState); + + stateMachine->setInitialState(idleState); + stateMachine->start(); + + idleState->addTransition(this, &TrackViewModelContextData::rubberBandDragWillStart, rubberBandDraggingState); + rubberBandDraggingState->addTransition(this, &TrackViewModelContextData::rubberBandDragWillFinish, idleState); + + idleState->addTransition(this, &TrackViewModelContextData::moveTransactionWillStart, movePendingState); + movePendingState->addTransition(this, &TrackViewModelContextData::moveTransactionStarted, moveProcessingState); + movePendingState->addTransition(this, &TrackViewModelContextData::moveTransactionNotStarted, idleState); + moveProcessingState->addTransition(this, &TrackViewModelContextData::moveTransactionWillCommit, moveCommittingState); + moveProcessingState->addTransition(this, &TrackViewModelContextData::moveTransactionWillAbort, moveAbortingState); + moveCommittingState->addTransition(idleState); + moveAbortingState->addTransition(idleState); + + idleState->addTransition(this, &TrackViewModelContextData::muteTransactionWillStart, mutePendingState); + mutePendingState->addTransition(this, &TrackViewModelContextData::muteTransactionStarted, muteEditingState); + mutePendingState->addTransition(this, &TrackViewModelContextData::muteTransactionNotStarted, idleState); + muteEditingState->addTransition(this, &TrackViewModelContextData::muteTransactionWillFinish, muteFinishingState); + muteFinishingState->addTransition(idleState); + + idleState->addTransition(this, &TrackViewModelContextData::soloTransactionWillStart, soloPendingState); + soloPendingState->addTransition(this, &TrackViewModelContextData::soloTransactionStarted, soloEditingState); + soloPendingState->addTransition(this, &TrackViewModelContextData::soloTransactionNotStarted, idleState); + soloEditingState->addTransition(this, &TrackViewModelContextData::soloTransactionWillFinish, soloFinishingState); + soloFinishingState->addTransition(idleState); + + idleState->addTransition(this, &TrackViewModelContextData::recordTransactionWillStart, recordPendingState); + recordPendingState->addTransition(this, &TrackViewModelContextData::recordTransactionStarted, recordEditingState); + recordPendingState->addTransition(this, &TrackViewModelContextData::recordTransactionNotStarted, idleState); + recordEditingState->addTransition(this, &TrackViewModelContextData::recordTransactionWillFinish, recordFinishingState); + recordFinishingState->addTransition(idleState); + + idleState->addTransition(this, &TrackViewModelContextData::nameTransactionWillStart, namePendingState); + namePendingState->addTransition(this, &TrackViewModelContextData::nameTransactionStarted, nameProgressingState); + namePendingState->addTransition(this, &TrackViewModelContextData::nameTransactionNotStarted, idleState); + nameProgressingState->addTransition(this, &TrackViewModelContextData::nameTransactionWillCommit, nameCommittingState); + nameProgressingState->addTransition(this, &TrackViewModelContextData::nameTransactionWillAbort, nameAbortingState); + nameCommittingState->addTransition(idleState); + nameAbortingState->addTransition(idleState); + + idleState->addTransition(this, &TrackViewModelContextData::gainTransactionWillStart, gainPendingState); + gainPendingState->addTransition(this, &TrackViewModelContextData::gainTransactionStarted, gainProgressingState); + gainPendingState->addTransition(this, &TrackViewModelContextData::gainTransactionNotStarted, idleState); + gainProgressingState->addTransition(this, &TrackViewModelContextData::gainTransactionWillCommit, gainCommittingState); + gainProgressingState->addTransition(this, &TrackViewModelContextData::gainTransactionWillAbort, gainAbortingState); + gainCommittingState->addTransition(idleState); + gainAbortingState->addTransition(idleState); + + idleState->addTransition(this, &TrackViewModelContextData::panTransactionWillStart, panPendingState); + panPendingState->addTransition(this, &TrackViewModelContextData::panTransactionStarted, panProgressingState); + panPendingState->addTransition(this, &TrackViewModelContextData::panTransactionNotStarted, idleState); + panProgressingState->addTransition(this, &TrackViewModelContextData::panTransactionWillCommit, panCommittingState); + panProgressingState->addTransition(this, &TrackViewModelContextData::panTransactionWillAbort, panAbortingState); + panCommittingState->addTransition(idleState); + panAbortingState->addTransition(idleState); + + idleState->addTransition(this, &TrackViewModelContextData::heightTransactionWillStart, heightPendingState); + heightPendingState->addTransition(this, &TrackViewModelContextData::heightTransactionStarted, heightEditingState); + heightPendingState->addTransition(this, &TrackViewModelContextData::heightTransactionNotStarted, idleState); + heightEditingState->addTransition(this, &TrackViewModelContextData::heightTransactionWillFinish, heightFinishingState); + heightFinishingState->addTransition(idleState); + + auto logEntered = [](const char *name) { qCInfo(lcTrackViewModelContextData) << name << "entered"; }; + auto logExited = [](const char *name) { qCInfo(lcTrackViewModelContextData) << name << "exited"; }; + + connect(idleState, &QState::entered, this, [=] { + logEntered("Idle state"); + }); + connect(idleState, &QState::exited, this, [=] { + logExited("Idle state"); + }); + connect(rubberBandDraggingState, &QState::entered, this, [=] { + logEntered("Rubber band dragging state"); + }); + connect(rubberBandDraggingState, &QState::exited, this, [=] { + logExited("Rubber band dragging state"); + }); + + connect(movePendingState, &QState::entered, this, [=, this] { + logEntered("Move pending state"); + onMovePendingStateEntered(); + }); + connect(movePendingState, &QState::exited, this, [=] { + logExited("Move pending state"); + }); + connect(moveProcessingState, &QState::entered, this, [=] { + logEntered("Move processing state"); + }); + connect(moveProcessingState, &QState::exited, this, [=] { + logExited("Move processing state"); + }); + connect(moveCommittingState, &QState::entered, this, [=, this] { + logEntered("Move committing state"); + onMoveCommittingStateEntered(); + }); + connect(moveCommittingState, &QState::exited, this, [=] { + logExited("Move committing state"); + }); + connect(moveAbortingState, &QState::entered, this, [=, this] { + logEntered("Move aborting state"); + onMoveAbortingStateEntered(); + }); + connect(moveAbortingState, &QState::exited, this, [=] { + logExited("Move aborting state"); + }); + + connect(mutePendingState, &QState::entered, this, [=, this] { + logEntered("Mute pending state"); + onMutePendingStateEntered(); + }); + connect(mutePendingState, &QState::exited, this, [=] { + logExited("Mute pending state"); + }); + connect(muteEditingState, &QState::entered, this, [=] { + logEntered("Mute editing state"); + }); + connect(muteEditingState, &QState::exited, this, [=] { + logExited("Mute editing state"); + }); + connect(muteFinishingState, &QState::entered, this, [=, this] { + logEntered("Mute finishing state"); + onMuteFinishingStateEntered(); + }); + connect(muteFinishingState, &QState::exited, this, [=] { + logExited("Mute finishing state"); + }); + + connect(soloPendingState, &QState::entered, this, [=, this] { + logEntered("Solo pending state"); + onSoloPendingStateEntered(); + }); + connect(soloPendingState, &QState::exited, this, [=] { + logExited("Solo pending state"); + }); + connect(soloEditingState, &QState::entered, this, [=] { + logEntered("Solo editing state"); + }); + connect(soloEditingState, &QState::exited, this, [=] { + logExited("Solo editing state"); + }); + connect(soloFinishingState, &QState::entered, this, [=, this] { + logEntered("Solo finishing state"); + onSoloFinishingStateEntered(); + }); + connect(soloFinishingState, &QState::exited, this, [=] { + logExited("Solo finishing state"); + }); + + connect(recordPendingState, &QState::entered, this, [=, this] { + logEntered("Record pending state"); + onRecordPendingStateEntered(); + }); + connect(recordPendingState, &QState::exited, this, [=] { + logExited("Record pending state"); + }); + connect(recordEditingState, &QState::entered, this, [=] { + logEntered("Record editing state"); + }); + connect(recordEditingState, &QState::exited, this, [=] { + logExited("Record editing state"); + }); + connect(recordFinishingState, &QState::entered, this, [=, this] { + logEntered("Record finishing state"); + onRecordFinishingStateEntered(); + }); + connect(recordFinishingState, &QState::exited, this, [=] { + logExited("Record finishing state"); + }); + + connect(namePendingState, &QState::entered, this, [=, this] { + logEntered("Name pending state"); + onNamePendingStateEntered(); + }); + connect(namePendingState, &QState::exited, this, [=] { + logExited("Name pending state"); + }); + connect(nameProgressingState, &QState::entered, this, [=] { + logEntered("Name progressing state"); + }); + connect(nameProgressingState, &QState::exited, this, [=] { + logExited("Name progressing state"); + }); + connect(nameCommittingState, &QState::entered, this, [=, this] { + logEntered("Name committing state"); + onNameCommittingStateEntered(); + }); + connect(nameCommittingState, &QState::exited, this, [=] { + logExited("Name committing state"); + }); + connect(nameAbortingState, &QState::entered, this, [=, this] { + logEntered("Name aborting state"); + onNameAbortingStateEntered(); + }); + connect(nameAbortingState, &QState::exited, this, [=] { + logExited("Name aborting state"); + }); + + connect(gainPendingState, &QState::entered, this, [=, this] { + logEntered("Gain pending state"); + onGainPendingStateEntered(); + }); + connect(gainPendingState, &QState::exited, this, [=] { + logExited("Gain pending state"); + }); + connect(gainProgressingState, &QState::entered, this, [=] { + logEntered("Gain progressing state"); + }); + connect(gainProgressingState, &QState::exited, this, [=] { + logExited("Gain progressing state"); + }); + connect(gainCommittingState, &QState::entered, this, [=, this] { + logEntered("Gain committing state"); + onGainCommittingStateEntered(); + }); + connect(gainCommittingState, &QState::exited, this, [=] { + logExited("Gain committing state"); + }); + connect(gainAbortingState, &QState::entered, this, [=, this] { + logEntered("Gain aborting state"); + onGainAbortingStateEntered(); + }); + connect(gainAbortingState, &QState::exited, this, [=] { + logExited("Gain aborting state"); + }); + + connect(panPendingState, &QState::entered, this, [=, this] { + logEntered("Pan pending state"); + onPanPendingStateEntered(); + }); + connect(panPendingState, &QState::exited, this, [=] { + logExited("Pan pending state"); + }); + connect(panProgressingState, &QState::entered, this, [=] { + logEntered("Pan progressing state"); + }); + connect(panProgressingState, &QState::exited, this, [=] { + logExited("Pan progressing state"); + }); + connect(panCommittingState, &QState::entered, this, [=, this] { + logEntered("Pan committing state"); + onPanCommittingStateEntered(); + }); + connect(panCommittingState, &QState::exited, this, [=] { + logExited("Pan committing state"); + }); + connect(panAbortingState, &QState::entered, this, [=, this] { + logEntered("Pan aborting state"); + onPanAbortingStateEntered(); + }); + connect(panAbortingState, &QState::exited, this, [=] { + logExited("Pan aborting state"); + }); + + connect(heightPendingState, &QState::entered, this, [=, this] { + logEntered("Height pending state"); + onHeightPendingStateEntered(); + }); + connect(heightPendingState, &QState::exited, this, [=] { + logExited("Height pending state"); + }); + connect(heightEditingState, &QState::entered, this, [=] { + logEntered("Height editing state"); + }); + connect(heightEditingState, &QState::exited, this, [=] { + logExited("Height editing state"); + }); + connect(heightFinishingState, &QState::entered, this, [=, this] { + logEntered("Height finishing state"); + onHeightFinishingStateEntered(); + }); + connect(heightFinishingState, &QState::exited, this, [=] { + logExited("Height finishing state"); + }); + } + + void TrackViewModelContextData::init() { + Q_Q(ProjectViewModelContext); + document = q->windowHandle()->projectDocumentContext()->document(); + trackList = document->model()->tracks(); + trackSelectionModel = document->selectionModel()->trackSelectionModel(); + + trackListViewModel = new sflow::ListViewModel(q); + trackSelectionController = new TrackSelectionController(q); + + initStateMachine(); + } + + void TrackViewModelContextData::bindTrackListViewModel() { + connect(trackList, &dspx::TrackList::itemInserted, trackListViewModel, [=, this](int index, dspx::Track *item) { + bindTrackDocumentItem(index, item); + }); + connect(trackList, &dspx::TrackList::itemRemoved, trackListViewModel, [=, this](int index, dspx::Track *item) { + unbindTrackDocumentItem(index, item); + }); + connect(trackList, &dspx::TrackList::rotated, this, [=, this](int leftIndex, int middleIndex, int rightIndex) { + if (stateMachine->configuration().contains(moveProcessingState)) { + return; + } + trackListViewModel->rotate(leftIndex, middleIndex, rightIndex); + }); + + connect(trackListViewModel, &sflow::ListViewModel::rotated, this, [=, this](int leftIndex, int middleIndex, int rightIndex) { + if (!stateMachine->configuration().contains(moveProcessingState)) { + return; + } + moveChanged = true; + trackList->rotate(leftIndex, middleIndex, rightIndex); + }); + + const auto &items = trackList->items(); + for (int i = 0; i < items.size(); ++i) { + bindTrackDocumentItem(i, items.at(i)); + } + + connect(trackSelectionModel, &dspx::TrackSelectionModel::itemSelected, this, [=, this](dspx::Track *item, bool selected) { + auto viewItem = trackViewItemMap.value(item); + Q_ASSERT(viewItem); + viewItem->setSelected(selected); + }); + + for (auto *selectedItem : trackSelectionModel->selectedItems()) { + auto viewItem = trackViewItemMap.value(selectedItem); + if (!viewItem) { + continue; + } + viewItem->setSelected(true); + } + } + + void TrackViewModelContextData::bindTrackDocumentItem(int index, dspx::Track *item) { + if (trackViewItemMap.contains(item)) { + return; + } + + auto viewItem = new sflow::TrackViewModel(trackListViewModel); + viewItem->setColor(QColor::fromHsl(120, 100, 80)); // TODO + trackViewItemMap.insert(item, viewItem); + trackDocumentItemMap.insert(viewItem, item); + + auto control = item->control(); + + connect(item, &dspx::Track::nameChanged, viewItem, [=] { + if (viewItem->name() == item->name()) { + return; + } + viewItem->setName(item->name()); + }); + connect(item, &dspx::Track::heightChanged, viewItem, [=] { + if (viewItem->rowHeight() == getClampedHeight(item)) { + return; + } + viewItem->setRowHeight(getClampedHeight(item)); + }); + connect(control, &dspx::TrackControl::muteChanged, viewItem, [=](bool mute) { + if (viewItem->isMute() == mute) { + return; + } + viewItem->setMute(mute); + }); + connect(control, &dspx::TrackControl::soloChanged, viewItem, [=](bool solo) { + if (viewItem->isSolo() == solo) { + return; + } + viewItem->setSolo(solo); + }); + connect(control, &dspx::TrackControl::recordChanged, viewItem, [=](bool record) { + if (viewItem->isRecord() == record) { + return; + } + viewItem->setRecord(record); + }); + connect(control, &dspx::TrackControl::gainChanged, viewItem, [=](double gain) { + const double db = toDecibel(gain); + if (viewItem->gain() == db) { + return; + } + viewItem->setGain(db); + }); + connect(control, &dspx::TrackControl::panChanged, viewItem, [=](double pan) { + if (viewItem->pan() == pan) { + return; + } + viewItem->setPan(pan); + }); + + connect(viewItem, &sflow::TrackViewModel::nameChanged, item, [=] { + if (!stateMachine->configuration().contains(nameProgressingState)) { + viewItem->setName(item->name()); + return; + } + }); + connect(viewItem, &sflow::TrackViewModel::muteChanged, item, [=] { + if (!stateMachine->configuration().contains(muteEditingState)) { + viewItem->setMute(control->mute()); + return; + } + }); + connect(viewItem, &sflow::TrackViewModel::soloChanged, item, [=] { + if (!stateMachine->configuration().contains(soloEditingState)) { + viewItem->setSolo(control->solo()); + return; + } + }); + connect(viewItem, &sflow::TrackViewModel::recordChanged, item, [=] { + if (!stateMachine->configuration().contains(recordEditingState)) { + viewItem->setRecord(control->record()); + return; + } + }); + connect(viewItem, &sflow::TrackViewModel::gainChanged, item, [=] { + if (!stateMachine->configuration().contains(gainProgressingState)) { + viewItem->setGain(toDecibel(control->gain())); + return; + } + }); + connect(viewItem, &sflow::TrackViewModel::panChanged, item, [=] { + if (!stateMachine->configuration().contains(panProgressingState)) { + viewItem->setPan(control->pan()); + return; + } + }); + connect(viewItem, &sflow::TrackViewModel::rowHeightChanged, item, [=] { + if (!stateMachine->configuration().contains(heightEditingState)) { + viewItem->setRowHeight(getClampedHeight(item)); + return; + } + }); + + viewItem->setName(item->name()); + viewItem->setRowHeight(getClampedHeight(item)); + viewItem->setMute(control->mute()); + viewItem->setSolo(control->solo()); + viewItem->setRecord(control->record()); + viewItem->setGain(toDecibel(control->gain())); + viewItem->setPan(control->pan()); + + trackListViewModel->insertItem(index, viewItem); + } + + void TrackViewModelContextData::unbindTrackDocumentItem(int index, dspx::Track *item) { + Q_UNUSED(index) + if (!trackViewItemMap.contains(item)) { + return; + } + auto viewItem = trackViewItemMap.take(item); + trackDocumentItemMap.remove(viewItem); + + disconnect(item, nullptr, viewItem, nullptr); + disconnect(viewItem, nullptr, item, nullptr); + + trackListViewModel->removeItem(trackListViewModel->items().indexOf(viewItem)); + + viewItem->deleteLater(); + } + + sflow::TrackListInteractionController *TrackViewModelContextData::createController(QObject *parent) { + auto controller = new sflow::TrackListInteractionController(parent); + controller->setInteraction(sflow::TrackListInteractionController::SelectByRubberBand); + controller->setItemInteraction(sflow::TrackListInteractionController::DragMove | sflow::TrackListInteractionController::Select | sflow::TrackListInteractionController::EditMute | sflow::TrackListInteractionController::EditSolo | sflow::TrackListInteractionController::EditRecord | sflow::TrackListInteractionController::EditName | sflow::TrackListInteractionController::EditGain | sflow::TrackListInteractionController::EditPan | sflow::TrackListInteractionController::AdjustHeight); + + connect(controller, &sflow::TrackListInteractionController::rubberBandDraggingStarted, this, [=](QQuickItem *) { + Q_EMIT rubberBandDragWillStart(); + }); + connect(controller, &sflow::TrackListInteractionController::rubberBandDraggingFinished, this, [=](QQuickItem *) { + Q_EMIT rubberBandDragWillFinish(); + }); + + connect(controller, &sflow::TrackListInteractionController::dragMovingStarted, this, [=](QQuickItem *, int index) { + targetTrack = trackList->item(index); + Q_EMIT moveTransactionWillStart(); + }); + connect(controller, &sflow::TrackListInteractionController::dragMovingCommitted, this, [=](QQuickItem *, int) { + Q_EMIT moveTransactionWillCommit(); + }); + connect(controller, &sflow::TrackListInteractionController::dragMovingAborted, this, [=](QQuickItem *, int) { + Q_EMIT moveTransactionWillAbort(); + }); + + connect(controller, &sflow::TrackListInteractionController::muteEditingStarted, this, [=](QQuickItem *, int index) { + targetTrack = trackList->item(index); + Q_EMIT muteTransactionWillStart(); + }); + connect(controller, &sflow::TrackListInteractionController::muteEditingFinished, this, [=](QQuickItem *, int) { + Q_EMIT muteTransactionWillFinish(); + }); + + connect(controller, &sflow::TrackListInteractionController::soloEditingStarted, this, [=](QQuickItem *, int index) { + targetTrack = trackList->item(index); + Q_EMIT soloTransactionWillStart(); + }); + connect(controller, &sflow::TrackListInteractionController::soloEditingFinished, this, [=](QQuickItem *, int) { + Q_EMIT soloTransactionWillFinish(); + }); + + connect(controller, &sflow::TrackListInteractionController::recordEditingStarted, this, [=](QQuickItem *, int index) { + targetTrack = trackList->item(index); + Q_EMIT recordTransactionWillStart(); + }); + connect(controller, &sflow::TrackListInteractionController::recordEditingFinished, this, [=](QQuickItem *, int) { + Q_EMIT recordTransactionWillFinish(); + }); + + connect(controller, &sflow::TrackListInteractionController::nameEditingStarted, this, [=](QQuickItem *, int index) { + targetTrack = trackList->item(index); + Q_EMIT nameTransactionWillStart(); + }); + connect(controller, &sflow::TrackListInteractionController::nameEditingCommitted, this, [=](QQuickItem *, int) { + Q_EMIT nameTransactionWillCommit(); + }); + connect(controller, &sflow::TrackListInteractionController::nameEditingAborted, this, [=](QQuickItem *, int) { + Q_EMIT nameTransactionWillAbort(); + }); + + connect(controller, &sflow::TrackListInteractionController::gainEditingStarted, this, [=](QQuickItem *, int index) { + targetTrack = trackList->item(index); + Q_EMIT gainTransactionWillStart(); + }); + connect(controller, &sflow::TrackListInteractionController::gainEditingCommitted, this, [=](QQuickItem *, int) { + Q_EMIT gainTransactionWillCommit(); + }); + connect(controller, &sflow::TrackListInteractionController::gainEditingAborted, this, [=](QQuickItem *, int) { + Q_EMIT gainTransactionWillAbort(); + }); + + connect(controller, &sflow::TrackListInteractionController::panEditingStarted, this, [=](QQuickItem *, int index) { + targetTrack = trackList->item(index); + Q_EMIT panTransactionWillStart(); + }); + connect(controller, &sflow::TrackListInteractionController::panEditingCommitted, this, [=](QQuickItem *, int) { + Q_EMIT panTransactionWillCommit(); + }); + connect(controller, &sflow::TrackListInteractionController::panEditingAborted, this, [=](QQuickItem *, int) { + Q_EMIT panTransactionWillAbort(); + }); + + connect(controller, &sflow::TrackListInteractionController::heightAdjustingStarted, this, [=](QQuickItem *, int index) { + targetTrack = trackList->item(index); + Q_EMIT heightTransactionWillStart(); + }); + connect(controller, &sflow::TrackListInteractionController::heightAdjustingFinished, this, [=](QQuickItem *, int) { + Q_EMIT heightTransactionWillFinish(); + }); + + return controller; + } + + void TrackViewModelContextData::onMovePendingStateEntered() { + moveTransactionId = document->transactionController()->beginTransaction(); + moveChanged = false; + if (moveTransactionId != Core::TransactionController::TransactionId::Invalid) { + Q_EMIT moveTransactionStarted(); + } else { + Q_EMIT moveTransactionNotStarted(); + } + } + + void TrackViewModelContextData::onMoveCommittingStateEntered() { + if (!moveChanged) { + document->transactionController()->abortTransaction(moveTransactionId); + } else { + document->transactionController()->commitTransaction(moveTransactionId, tr("Moving track")); + } + moveTransactionId = {}; + targetTrack = {}; + moveChanged = false; + } + + void TrackViewModelContextData::onMoveAbortingStateEntered() { + document->transactionController()->abortTransaction(moveTransactionId); + moveTransactionId = {}; + targetTrack = {}; + moveChanged = false; + } + + void TrackViewModelContextData::onMutePendingStateEntered() { + muteTransactionId = document->transactionController()->beginTransaction(); + if (muteTransactionId != Core::TransactionController::TransactionId::Invalid) { + Q_EMIT muteTransactionStarted(); + } else { + Q_EMIT muteTransactionNotStarted(); + } + } + + void TrackViewModelContextData::onMuteFinishingStateEntered() { + if (!targetTrack || muteTransactionId == Core::TransactionController::TransactionId::Invalid) { + targetTrack = {}; + return; + } + auto viewItem = trackViewItemMap.value(targetTrack); + Q_ASSERT(viewItem); + const bool newValue = viewItem->isMute(); + if (newValue == targetTrack->control()->mute()) { + document->transactionController()->abortTransaction(muteTransactionId); + } else { + targetTrack->control()->setMute(newValue); + document->transactionController()->commitTransaction(muteTransactionId, tr("Editing track mute")); + } + muteTransactionId = {}; + targetTrack = {}; + } + + void TrackViewModelContextData::onSoloPendingStateEntered() { + soloTransactionId = document->transactionController()->beginTransaction(); + if (soloTransactionId != Core::TransactionController::TransactionId::Invalid) { + Q_EMIT soloTransactionStarted(); + } else { + Q_EMIT soloTransactionNotStarted(); + } + } + + void TrackViewModelContextData::onSoloFinishingStateEntered() { + if (!targetTrack || soloTransactionId == Core::TransactionController::TransactionId::Invalid) { + targetTrack = {}; + return; + } + auto viewItem = trackViewItemMap.value(targetTrack); + Q_ASSERT(viewItem); + const bool newValue = viewItem->isSolo(); + if (newValue == targetTrack->control()->solo()) { + document->transactionController()->abortTransaction(soloTransactionId); + } else { + targetTrack->control()->setSolo(newValue); + document->transactionController()->commitTransaction(soloTransactionId, tr("Editing track solo")); + } + soloTransactionId = {}; + targetTrack = {}; + } + + void TrackViewModelContextData::onRecordPendingStateEntered() { + recordTransactionId = document->transactionController()->beginTransaction(); + if (recordTransactionId != Core::TransactionController::TransactionId::Invalid) { + Q_EMIT recordTransactionStarted(); + } else { + Q_EMIT recordTransactionNotStarted(); + } + } + + void TrackViewModelContextData::onRecordFinishingStateEntered() { + if (!targetTrack || recordTransactionId == Core::TransactionController::TransactionId::Invalid) { + targetTrack = {}; + return; + } + auto viewItem = trackViewItemMap.value(targetTrack); + Q_ASSERT(viewItem); + const bool newValue = viewItem->isRecord(); + if (newValue == targetTrack->control()->record()) { + document->transactionController()->abortTransaction(recordTransactionId); + } else { + targetTrack->control()->setRecord(newValue); + document->transactionController()->commitTransaction(recordTransactionId, tr("Editing track record")); + } + recordTransactionId = {}; + targetTrack = {}; + } + + void TrackViewModelContextData::onNamePendingStateEntered() { + nameTransactionId = document->transactionController()->beginTransaction(); + if (nameTransactionId != Core::TransactionController::TransactionId::Invalid) { + Q_EMIT nameTransactionStarted(); + } else { + Q_EMIT nameTransactionNotStarted(); + } + } + + void TrackViewModelContextData::onNameCommittingStateEntered() { + if (!targetTrack || nameTransactionId == Core::TransactionController::TransactionId::Invalid) { + targetTrack = {}; + return; + } + auto viewItem = trackViewItemMap.value(targetTrack); + Q_ASSERT(viewItem); + if (viewItem->name() == targetTrack->name()) { + document->transactionController()->abortTransaction(nameTransactionId); + } else { + targetTrack->setName(viewItem->name()); + document->transactionController()->commitTransaction(nameTransactionId, tr("Renaming track")); + } + nameTransactionId = {}; + targetTrack = {}; + } + + void TrackViewModelContextData::onNameAbortingStateEntered() { + document->transactionController()->abortTransaction(nameTransactionId); + nameTransactionId = {}; + targetTrack = {}; + } + + void TrackViewModelContextData::onGainPendingStateEntered() { + gainTransactionId = document->transactionController()->beginTransaction(); + if (gainTransactionId != Core::TransactionController::TransactionId::Invalid) { + Q_EMIT gainTransactionStarted(); + } else { + Q_EMIT gainTransactionNotStarted(); + } + } + + void TrackViewModelContextData::onGainCommittingStateEntered() { + if (!targetTrack || gainTransactionId == Core::TransactionController::TransactionId::Invalid) { + targetTrack = {}; + return; + } + auto viewItem = trackViewItemMap.value(targetTrack); + Q_ASSERT(viewItem); + const double newLinear = toLinear(viewItem->gain()); + if (qFuzzyCompare(newLinear, targetTrack->control()->gain())) { + document->transactionController()->abortTransaction(gainTransactionId); + } else { + targetTrack->control()->setGain(newLinear); + document->transactionController()->commitTransaction(gainTransactionId, tr("Adjusting track gain")); + } + gainTransactionId = {}; + targetTrack = {}; + } + + void TrackViewModelContextData::onGainAbortingStateEntered() { + document->transactionController()->abortTransaction(gainTransactionId); + gainTransactionId = {}; + targetTrack = {}; + } + + void TrackViewModelContextData::onPanPendingStateEntered() { + panTransactionId = document->transactionController()->beginTransaction(); + if (panTransactionId != Core::TransactionController::TransactionId::Invalid) { + Q_EMIT panTransactionStarted(); + } else { + Q_EMIT panTransactionNotStarted(); + } + } + + void TrackViewModelContextData::onPanCommittingStateEntered() { + if (!targetTrack || panTransactionId == Core::TransactionController::TransactionId::Invalid) { + targetTrack = {}; + return; + } + auto viewItem = trackViewItemMap.value(targetTrack); + Q_ASSERT(viewItem); + const double newPan = viewItem->pan(); + if (qFuzzyCompare(newPan, targetTrack->control()->pan())) { + document->transactionController()->abortTransaction(panTransactionId); + } else { + targetTrack->control()->setPan(newPan); + document->transactionController()->commitTransaction(panTransactionId, tr("Adjusting track pan")); + } + panTransactionId = {}; + targetTrack = {}; + } + + void TrackViewModelContextData::onPanAbortingStateEntered() { + document->transactionController()->abortTransaction(panTransactionId); + panTransactionId = {}; + targetTrack = {}; + } + + void TrackViewModelContextData::onHeightPendingStateEntered() { + heightTransactionId = document->transactionController()->beginTransaction(); + if (heightTransactionId != Core::TransactionController::TransactionId::Invalid) { + Q_EMIT heightTransactionStarted(); + } else { + Q_EMIT heightTransactionNotStarted(); + } + } + + void TrackViewModelContextData::onHeightFinishingStateEntered() { + if (!targetTrack || heightTransactionId == Core::TransactionController::TransactionId::Invalid) { + targetTrack = {}; + return; + } + auto viewItem = trackViewItemMap.value(targetTrack); + Q_ASSERT(viewItem); + const double newHeight = viewItem->rowHeight(); + if (qFuzzyCompare(newHeight, getClampedHeight(targetTrack))) { + document->transactionController()->abortTransaction(heightTransactionId); + } else { + targetTrack->setHeight(newHeight); + document->transactionController()->commitTransaction(heightTransactionId, tr("Resizing track")); + } + heightTransactionId = {}; + targetTrack = {}; + } + +} diff --git a/src/plugins/visualeditor/core/viewmodelbinding/TrackViewModelContextData_p.h b/src/plugins/visualeditor/core/viewmodelbinding/TrackViewModelContextData_p.h new file mode 100644 index 00000000..c11d593b --- /dev/null +++ b/src/plugins/visualeditor/core/viewmodelbinding/TrackViewModelContextData_p.h @@ -0,0 +1,188 @@ +#ifndef DIFFSCOPE_VISUALEDITOR_TRACKVIEWMODELCONTEXTDATA_P_H +#define DIFFSCOPE_VISUALEDITOR_TRACKVIEWMODELCONTEXTDATA_P_H + +#include +#include + +#include +#include + +class QState; +class QStateMachine; +class QQuickItem; + +namespace dspx { + class Track; + class TrackList; + class TrackSelectionModel; +} + +namespace sflow { + class ListViewModel; + class TrackListInteractionController; + class TrackViewModel; +} + +namespace Core { + class DspxDocument; +} + +namespace VisualEditor { + + class TrackSelectionController; + + class TrackViewModelContextData : public QObject { + Q_OBJECT + Q_DECLARE_PUBLIC(ProjectViewModelContext) + public: + ProjectViewModelContext *q_ptr; + + Core::DspxDocument *document; + dspx::TrackList *trackList; + dspx::TrackSelectionModel *trackSelectionModel; + + sflow::ListViewModel *trackListViewModel; + TrackSelectionController *trackSelectionController; + + QHash trackViewItemMap; + QHash trackDocumentItemMap; + + QStateMachine *stateMachine; + QState *idleState; + QState *rubberBandDraggingState; + + QState *movePendingState; + QState *moveProcessingState; + QState *moveCommittingState; + QState *moveAbortingState; + + QState *mutePendingState; + QState *muteEditingState; + QState *muteFinishingState; + + QState *soloPendingState; + QState *soloEditingState; + QState *soloFinishingState; + + QState *recordPendingState; + QState *recordEditingState; + QState *recordFinishingState; + + QState *namePendingState; + QState *nameProgressingState; + QState *nameCommittingState; + QState *nameAbortingState; + + QState *gainPendingState; + QState *gainProgressingState; + QState *gainCommittingState; + QState *gainAbortingState; + + QState *panPendingState; + QState *panProgressingState; + QState *panCommittingState; + QState *panAbortingState; + + QState *heightPendingState; + QState *heightEditingState; + QState *heightFinishingState; + + Core::TransactionController::TransactionId moveTransactionId{}; + Core::TransactionController::TransactionId muteTransactionId{}; + Core::TransactionController::TransactionId soloTransactionId{}; + Core::TransactionController::TransactionId recordTransactionId{}; + Core::TransactionController::TransactionId nameTransactionId{}; + Core::TransactionController::TransactionId gainTransactionId{}; + Core::TransactionController::TransactionId panTransactionId{}; + Core::TransactionController::TransactionId heightTransactionId{}; + + dspx::Track *targetTrack{}; + bool moveChanged{false}; + + void init(); + void initStateMachine(); + void bindTrackListViewModel(); + void bindTrackDocumentItem(int index, dspx::Track *item); + void unbindTrackDocumentItem(int index, dspx::Track *item); + sflow::TrackListInteractionController *createController(QObject *parent); + + void onMovePendingStateEntered(); + void onMoveCommittingStateEntered(); + void onMoveAbortingStateEntered(); + + void onMutePendingStateEntered(); + void onMuteFinishingStateEntered(); + + void onSoloPendingStateEntered(); + void onSoloFinishingStateEntered(); + + void onRecordPendingStateEntered(); + void onRecordFinishingStateEntered(); + + void onNamePendingStateEntered(); + void onNameCommittingStateEntered(); + void onNameAbortingStateEntered(); + + void onGainPendingStateEntered(); + void onGainCommittingStateEntered(); + void onGainAbortingStateEntered(); + + void onPanPendingStateEntered(); + void onPanCommittingStateEntered(); + void onPanAbortingStateEntered(); + + void onHeightPendingStateEntered(); + void onHeightFinishingStateEntered(); + + Q_SIGNALS: + void rubberBandDragWillStart(); + void rubberBandDragWillFinish(); + + void moveTransactionWillStart(); + void moveTransactionStarted(); + void moveTransactionNotStarted(); + void moveTransactionWillCommit(); + void moveTransactionWillAbort(); + + void muteTransactionWillStart(); + void muteTransactionStarted(); + void muteTransactionNotStarted(); + void muteTransactionWillFinish(); + + void soloTransactionWillStart(); + void soloTransactionStarted(); + void soloTransactionNotStarted(); + void soloTransactionWillFinish(); + + void recordTransactionWillStart(); + void recordTransactionStarted(); + void recordTransactionNotStarted(); + void recordTransactionWillFinish(); + + void nameTransactionWillStart(); + void nameTransactionStarted(); + void nameTransactionNotStarted(); + void nameTransactionWillCommit(); + void nameTransactionWillAbort(); + + void gainTransactionWillStart(); + void gainTransactionStarted(); + void gainTransactionNotStarted(); + void gainTransactionWillCommit(); + void gainTransactionWillAbort(); + + void panTransactionWillStart(); + void panTransactionStarted(); + void panTransactionNotStarted(); + void panTransactionWillCommit(); + void panTransactionWillAbort(); + + void heightTransactionWillStart(); + void heightTransactionStarted(); + void heightTransactionNotStarted(); + void heightTransactionWillFinish(); + }; + +} + +#endif //DIFFSCOPE_VISUALEDITOR_TRACKVIEWMODELCONTEXTDATA_P_H diff --git a/src/plugins/visualeditor/internal/EditorPreference.cpp b/src/plugins/visualeditor/internal/EditorPreference.cpp index bbc89164..80f68ec8 100644 --- a/src/plugins/visualeditor/internal/EditorPreference.cpp +++ b/src/plugins/visualeditor/internal/EditorPreference.cpp @@ -17,6 +17,7 @@ namespace VisualEditor::Internal { bool middleButtonAutoScroll{}; int autoDurationPositionAlignment{48}; bool enableTemporarySnapOff{true}; + bool trackListOnRight{}; bool trackCursorPosition{true}; }; @@ -53,6 +54,10 @@ namespace VisualEditor::Internal { emit autoDurationPositionAlignmentChanged(); d->enableTemporarySnapOff = settings->value("enableTemporarySnapOff", true).toBool(); emit enableTemporarySnapOffChanged(); + d->trackListOnRight = settings->value("trackListOnRight", false).toBool(); + emit trackListOnRightChanged(); + d->trackCursorPosition = settings->value("trackCursorPosition", true).toBool(); + emit trackCursorPositionChanged(); settings->endGroup(); } @@ -67,6 +72,8 @@ namespace VisualEditor::Internal { settings->setValue("middleButtonAutoScroll", d->middleButtonAutoScroll); settings->setValue("autoDurationPositionAlignment", d->autoDurationPositionAlignment); settings->setValue("enableTemporarySnapOff", d->enableTemporarySnapOff); + settings->setValue("trackListOnRight", d->trackListOnRight); + settings->setValue("trackCursorPosition", d->trackCursorPosition); settings->endGroup(); } @@ -164,6 +171,20 @@ namespace VisualEditor::Internal { d->enableTemporarySnapOff = enableTemporarySnapOff; emit m_instance->enableTemporarySnapOffChanged(); } + + bool EditorPreference::trackListOnRight() { + M_INSTANCE_D; + return d->trackListOnRight; + } + + void EditorPreference::setTrackListOnRight(bool trackListOnRight) { + M_INSTANCE_D; + if (d->trackListOnRight == trackListOnRight) + return; + d->trackListOnRight = trackListOnRight; + emit m_instance->trackListOnRightChanged(); + } + bool EditorPreference::trackCursorPosition() { M_INSTANCE_D; return d->trackCursorPosition; diff --git a/src/plugins/visualeditor/internal/EditorPreference.h b/src/plugins/visualeditor/internal/EditorPreference.h index 0f475e0f..0f362e1c 100644 --- a/src/plugins/visualeditor/internal/EditorPreference.h +++ b/src/plugins/visualeditor/internal/EditorPreference.h @@ -26,6 +26,7 @@ namespace VisualEditor::Internal { Q_PROPERTY(bool middleButtonAutoScroll READ middleButtonAutoScroll WRITE setMiddleButtonAutoScroll NOTIFY middleButtonAutoScrollChanged) Q_PROPERTY(int autoDurationPositionAlignment READ autoDurationPositionAlignment WRITE setAutoDurationPositionAlignment NOTIFY autoDurationPositionAlignmentChanged) Q_PROPERTY(bool enableTemporarySnapOff READ enableTemporarySnapOff WRITE setEnableTemporarySnapOff NOTIFY enableTemporarySnapOffChanged) + Q_PROPERTY(bool trackListOnRight READ trackListOnRight WRITE setTrackListOnRight NOTIFY trackListOnRightChanged) Q_PROPERTY(bool trackCursorPosition READ trackCursorPosition WRITE setTrackCursorPosition NOTIFY trackCursorPositionChanged) public: @@ -68,6 +69,9 @@ namespace VisualEditor::Internal { static bool enableTemporarySnapOff(); static void setEnableTemporarySnapOff(bool enableTemporarySnapOff); + static bool trackListOnRight(); + static void setTrackListOnRight(bool trackListOnRight); + static bool trackCursorPosition(); static void setTrackCursorPosition(bool trackCursorPosition); @@ -79,6 +83,7 @@ namespace VisualEditor::Internal { void middleButtonAutoScrollChanged(); void autoDurationPositionAlignmentChanged(); void enableTemporarySnapOffChanged(); + void trackListOnRightChanged(); void trackCursorPositionChanged(); private: diff --git a/src/plugins/visualeditor/internal/VisualEditorPlugin.cpp b/src/plugins/visualeditor/internal/VisualEditorPlugin.cpp index d998082a..0f66ae47 100644 --- a/src/plugins/visualeditor/internal/VisualEditorPlugin.cpp +++ b/src/plugins/visualeditor/internal/VisualEditorPlugin.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include static auto getVisualEditorActionExtension() { @@ -68,6 +69,7 @@ namespace VisualEditor::Internal { void VisualEditorPlugin::initializeWindows() { Core::ProjectWindowInterfaceRegistry::instance()->attach(); Core::ProjectWindowInterfaceRegistry::instance()->attach(); + Core::ProjectWindowInterfaceRegistry::instance()->attach(); Core::ProjectWindowInterfaceRegistry::instance()->attach(); Core::ProjectWindowInterfaceRegistry::instance()->attach(); } diff --git a/src/plugins/visualeditor/internal/addon/ArrangementAddOn.cpp b/src/plugins/visualeditor/internal/addon/ArrangementAddOn.cpp index ae84690e..612419ee 100644 --- a/src/plugins/visualeditor/internal/addon/ArrangementAddOn.cpp +++ b/src/plugins/visualeditor/internal/addon/ArrangementAddOn.cpp @@ -53,7 +53,6 @@ namespace VisualEditor::Internal { // TODO windowInterface->actionContext()->addAction("org.diffscope.visualeditor.panel.pianoRoll", new QQmlComponent(Core::RuntimeInterface::qmlEngine(), "DiffScope.VisualEditor", "PianoRollPanel", this)); - windowInterface->actionContext()->addAction("org.diffscope.visualeditor.panel.mixer", new QQmlComponent(Core::RuntimeInterface::qmlEngine(), "DiffScope.VisualEditor", "MixerPanel", this)); } void ArrangementAddOn::extensionsInitialized() {} diff --git a/src/plugins/visualeditor/internal/addon/MixerAddOn.cpp b/src/plugins/visualeditor/internal/addon/MixerAddOn.cpp new file mode 100644 index 00000000..4fff089a --- /dev/null +++ b/src/plugins/visualeditor/internal/addon/MixerAddOn.cpp @@ -0,0 +1,70 @@ +#include "MixerAddOn.h" + +#include +#include +#include + +#include + +#include + +#include + +#include + +namespace VisualEditor::Internal { + + MixerAddOn::MixerAddOn(QObject *parent) : WindowInterfaceAddOn(parent) { + } + + MixerAddOn::~MixerAddOn() = default; + + void MixerAddOn::initialize() { + auto windowInterface = windowHandle()->cast(); + new MixerPanelInterface(this, windowInterface); + + { + QQmlComponent component(Core::RuntimeInterface::qmlEngine(), "DiffScope.VisualEditor", "MixerAddOnActions"); + if (component.isError()) { + qFatal() << component.errorString(); + } + auto o = component.createWithInitialProperties({ + {"addOn", QVariant::fromValue(this)}, + }); + if (component.isError()) { + qFatal() << component.errorString(); + } + o->setParent(this); + QMetaObject::invokeMethod(o, "registerToContext", windowInterface->actionContext()); + } + + { + QQmlComponent component(Core::RuntimeInterface::qmlEngine(), "DiffScope.VisualEditor", "MixerPanel", this); + if (component.isError()) { + qFatal() << component.errorString(); + } + auto o = component.createWithInitialProperties({ + {"addOn", QVariant::fromValue(this)}, + }); + if (component.isError()) { + qFatal() << component.errorString(); + } + o->setParent(this); + windowInterface->actionContext()->addAction("org.diffscope.visualeditor.panel.mixer", o->property("mixerPanelComponent").value()); + } + } + + void MixerAddOn::extensionsInitialized() { + } + + bool MixerAddOn::delayedInitialize() { + return WindowInterfaceAddOn::delayedInitialize(); + } + + MixerPanelInterface *MixerAddOn::mixerPanelInterface() const { + return MixerPanelInterface::of(windowHandle()->cast()); + } + +} + +#include "moc_MixerAddOn.cpp" diff --git a/src/plugins/visualeditor/internal/addon/MixerAddOn.h b/src/plugins/visualeditor/internal/addon/MixerAddOn.h new file mode 100644 index 00000000..66c910ba --- /dev/null +++ b/src/plugins/visualeditor/internal/addon/MixerAddOn.h @@ -0,0 +1,28 @@ +#ifndef DIFFSCOPE_VISUAL_EDITOR_MIXERADDON_H +#define DIFFSCOPE_VISUAL_EDITOR_MIXERADDON_H + +#include + +namespace VisualEditor { + class MixerPanelInterface; +} + +namespace VisualEditor::Internal { + + class MixerAddOn : public Core::WindowInterfaceAddOn { + Q_OBJECT + Q_PROPERTY(VisualEditor::MixerPanelInterface *mixerPanelInterface READ mixerPanelInterface CONSTANT) + public: + explicit MixerAddOn(QObject *parent = nullptr); + ~MixerAddOn() override; + + void initialize() override; + void extensionsInitialized() override; + bool delayedInitialize() override; + + MixerPanelInterface *mixerPanelInterface() const; + }; + +} + +#endif //DIFFSCOPE_VISUAL_EDITOR_MIXERADDON_H diff --git a/src/plugins/visualeditor/internal/settings/EditorPage.cpp b/src/plugins/visualeditor/internal/settings/EditorPage.cpp index 9109f574..20b12bb2 100644 --- a/src/plugins/visualeditor/internal/settings/EditorPage.cpp +++ b/src/plugins/visualeditor/internal/settings/EditorPage.cpp @@ -59,6 +59,8 @@ namespace VisualEditor::Internal { qCDebug(lcEditorPage) << m_widget->property("autoDurationPositionAlignment"); m_widget->setProperty("enableTemporarySnapOff", EditorPreference::instance()->property("enableTemporarySnapOff")); qCDebug(lcEditorPage) << m_widget->property("enableTemporarySnapOff"); + m_widget->setProperty("trackListOnRight", EditorPreference::instance()->property("trackListOnRight")); + qCDebug(lcEditorPage) << m_widget->property("trackListOnRight"); m_widget->setProperty("trackCursorPosition", EditorPreference::instance()->property("trackCursorPosition")); qCDebug(lcEditorPage) << m_widget->property("trackCursorPosition"); m_widget->setProperty("started", true); @@ -81,6 +83,8 @@ namespace VisualEditor::Internal { EditorPreference::instance()->setProperty("autoDurationPositionAlignment", m_widget->property("autoDurationPositionAlignment")); qCDebug(lcEditorPage) << "enableTemporarySnapOff" << m_widget->property("enableTemporarySnapOff"); EditorPreference::instance()->setProperty("enableTemporarySnapOff", m_widget->property("enableTemporarySnapOff")); + qCDebug(lcEditorPage) << "trackListOnRight" << m_widget->property("trackListOnRight"); + EditorPreference::instance()->setProperty("trackListOnRight", m_widget->property("trackListOnRight")); qCDebug(lcEditorPage) << "trackCursorPosition" << m_widget->property("trackCursorPosition"); EditorPreference::instance()->setProperty("trackCursorPosition", m_widget->property("trackCursorPosition")); EditorPreference::instance()->save(); diff --git a/src/plugins/visualeditor/qml/ArrangementView.qml b/src/plugins/visualeditor/qml/ArrangementView.qml index c0c9b913..4ff7cff5 100644 --- a/src/plugins/visualeditor/qml/ArrangementView.qml +++ b/src/plugins/visualeditor/qml/ArrangementView.qml @@ -36,9 +36,10 @@ Item { id: splitView anchors.fill: parent orientation: Qt.Horizontal - Item { - // TODO - SplitView.preferredWidth: 200 + readonly property bool rightAligned: EditorPreference.trackListOnRight + readonly property Item trackArea: Item { + id: trackArea + SplitView.preferredWidth: 360 ColumnLayout { anchors.fill: parent spacing: 0 @@ -81,7 +82,10 @@ Item { id: layout required property string modelData required property int index - readonly property Item item: additionalTrackRepeater.itemAt(index)?.item ?? null + readonly property Item item: { + let a = additionalTrackRepeater.count - additionalTrackRepeater.count + return additionalTrackRepeater.itemAt(a + index)?.item ?? null + } readonly property double itemSize: 12 Layout.fillWidth: true spacing: 0 @@ -116,8 +120,8 @@ Item { Layout.fillHeight: true icon.height: layout.itemSize icon.width: layout.itemSize - icon.source: layout.item.ActionInstantiator.icon.source - icon.color: layout.item.ActionInstantiator.icon.color.valid ? layout.item.ActionInstantiator.icon.color : Theme.foregroundPrimaryColor + icon.source: layout.item?.ActionInstantiator.icon.source ?? "" + icon.color: layout.item?.ActionInstantiator.icon.color.valid ? layout.item.ActionInstantiator.icon.color : Theme.foregroundPrimaryColor text: view.addOn.additionalTrackLoader.componentName(layout.modelData) color: Theme.foregroundPrimaryColor font.pixelSize: layout.itemSize * 0.75 @@ -178,10 +182,20 @@ Item { id: trackListContainer Layout.fillWidth: true Layout.fillHeight: true + TrackList { + anchors.fill: parent + trackListViewModel: view.projectViewModelContext?.trackListViewModel ?? null + trackListLayoutViewModel: view.arrangementPanelInterface?.trackListLayoutViewModel ?? null + scrollBehaviorViewModel: view.arrangementPanelInterface?.scrollBehaviorViewModel ?? null + trackListInteractionController: view.arrangementPanelInterface?.trackListInteractionController ?? null + selectionController: view.projectViewModelContext?.trackSelectionController ?? null + rightAligned: splitView.rightAligned + } } } } - Item { + readonly property Item clipArea: Item { + id: clipArea SplitView.fillWidth: true clip: true ColumnLayout { @@ -197,7 +211,10 @@ Item { timelineInteractionController: view.arrangementPanelInterface?.timelineInteractionController ?? null property bool dragging: false + property bool adjustingLoop: false + property int loopAdjustmentOperation: 0 + // TODO move this to C++ code Connections { target: timeline.playbackViewModel enabled: timeline.dragging @@ -206,18 +223,49 @@ Item { } } + Connections { + target: timeline.playbackViewModel + enabled: timeline.adjustingLoop + function onLoopStartChanged() { + if (timeline.loopAdjustmentOperation === TimelineInteractionController.AdjustRange || timeline.loopAdjustmentOperation === TimelineInteractionController.AdjustStart) { + timeline.timeLayoutViewModel.cursorPosition = timeline.playbackViewModel.loopStart + } else if (timeline.loopAdjustmentOperation === TimelineInteractionController.AdjustEnd) { + timeline.timeLayoutViewModel.cursorPosition = timeline.playbackViewModel.loopStart + timeline.playbackViewModel.loopLength + } + } + + function onLoopLengthChanged() { + if (timeline.loopAdjustmentOperation === TimelineInteractionController.AdjustEnd) { + timeline.timeLayoutViewModel.cursorPosition = timeline.playbackViewModel.loopStart + timeline.playbackViewModel.loopLength + } + } + } + Connections { target: timeline.timelineInteractionController - function onInteractionOperationStarted(timeline_, interactionFlag) { - if (timeline_ === timeline && interactionFlag === TimelineInteractionController.MovePositionIndicator) { + function onPositionIndicatorMovingStarted(timeline_) { + if (timeline_ === timeline) { timeline.dragging = true } } - function onInteractionOperationFinished(timeline_, interactionFlag) { - if (timeline_ === timeline && interactionFlag === LabelSequenceInteractionController.MovePositionIndicator) { + function onPositionIndicatorMovingFinished(timeline_) { + if (timeline_ === timeline) { timeline.dragging = false } } + + function onRubberBandDraggingStarted() { + timeline.timeLayoutViewModel.cursorPosition = -1 + } + + function onLoopRangeAdjustingStarted(_, adjustmentOperation) { + timeline.adjustingLoop = true + timeline.loopAdjustmentOperation = adjustmentOperation + } + + function onLoopRangeAdjustingFinished() { + timeline.adjustingLoop = false + } } } Rectangle { @@ -265,6 +313,7 @@ Item { acceptedButtons: Qt.NoButton enabled: !(view.arrangementPanelInterface?.mouseTrackingDisabled) hoverEnabled: true + cursorShape: undefined TimeManipulator { id: timeManipulator target: parent @@ -284,6 +333,22 @@ Item { } } } + + function updateContainer() { + while (count) { + takeItem(0) + } + if (rightAligned) { + addItem(clipArea) + addItem(trackArea) + } else { + addItem(trackArea) + addItem(clipArea) + } + } + + Component.onCompleted: updateContainer() + onRightAlignedChanged: updateContainer() } } \ No newline at end of file diff --git a/src/plugins/visualeditor/qml/MixerView.qml b/src/plugins/visualeditor/qml/MixerView.qml new file mode 100644 index 00000000..6e02c8a7 --- /dev/null +++ b/src/plugins/visualeditor/qml/MixerView.qml @@ -0,0 +1,49 @@ +import QtQml +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts + +import SVSCraft +import SVSCraft.UIComponents + +import dev.sjimo.ScopicFlow +import dev.sjimo.ScopicFlow.Internal + +Item { + id: view + + required property QtObject addOn + required property MixerPanelInterface mixerPanelInterface + + readonly property ProjectViewModelContext projectViewModelContext: addOn?.windowHandle.ProjectViewModelContext.context ?? null + + anchors.fill: parent + + SplitView { + id: splitView + anchors.fill: parent + orientation: Qt.Horizontal + + Mixer { + id: trackMixer + SplitView.fillWidth: true + trackListViewModel: view.projectViewModelContext?.trackListViewModel ?? null + trackListLayoutViewModel: view.mixerPanelInterface?.trackListLayoutViewModel ?? null + scrollBehaviorViewModel: view.mixerPanelInterface?.scrollBehaviorViewModel ?? null + trackListInteractionController: view.mixerPanelInterface?.trackListInteractionController ?? null + selectionController: view.projectViewModelContext?.trackSelectionController ?? null + } + + Mixer { + id: masterMixer + // TODO There might be more bus tracks in the future + SplitView.minimumWidth: 128 + SplitView.maximumWidth: 128 + trackListViewModel: view.projectViewModelContext?.masterTrackListViewModel ?? null + trackListLayoutViewModel: view.mixerPanelInterface?.masterTrackListLayoutViewModel ?? null + scrollBehaviorViewModel: view.mixerPanelInterface?.scrollBehaviorViewModel ?? null + trackListInteractionController: view.mixerPanelInterface?.masterTrackListInteractionController ?? null + selectionController: null + } + } +} diff --git a/src/plugins/visualeditor/qml/TimelineContextMenuHelper.qml b/src/plugins/visualeditor/qml/TimelineContextMenuHelper.qml index b36ec99f..b0df01a0 100644 --- a/src/plugins/visualeditor/qml/TimelineContextMenuHelper.qml +++ b/src/plugins/visualeditor/qml/TimelineContextMenuHelper.qml @@ -37,7 +37,7 @@ EditTempoTimeSignatureScenario { id: menu required property int position Action { - text: helper.musicTimeline.create(0, 0, menu.position).toString() + text: helper.musicTimeline?.create(0, 0, menu.position).toString() ?? "" enabled: false } MenuSeparator { diff --git a/src/plugins/visualeditor/qml/actions/MixerAddOnActions.qml b/src/plugins/visualeditor/qml/actions/MixerAddOnActions.qml new file mode 100644 index 00000000..a1af05e1 --- /dev/null +++ b/src/plugins/visualeditor/qml/actions/MixerAddOnActions.qml @@ -0,0 +1,41 @@ +import QtQml +import QtQml.Models +import QtQuick +import QtQuick.Controls + +import SVSCraft +import SVSCraft.UIComponents +import SVSCraft.UIComponents.impl + +import QActionKit + +ActionCollection { + id: d + + required property QtObject addOn + readonly property MixerPanelInterface mixerPanelInterface: addOn?.mixerPanelInterface ?? null + + ActionItem { + actionId: "org.diffscope.visualeditor.mixerPanel.pointerTool" + Action { + checkable: true + checked: d.mixerPanelInterface?.tool === MixerPanelInterface.PointerTool + onTriggered: () => { + d.mixerPanelInterface.tool = MixerPanelInterface.PointerTool + Qt.callLater(() => GlobalHelper.setProperty(this, "checked", true)) + } + } + } + + ActionItem { + actionId: "org.diffscope.visualeditor.mixerPanel.handTool" + Action { + checkable: true + checked: d.mixerPanelInterface?.tool === MixerPanelInterface.HandTool + onTriggered: () => { + d.mixerPanelInterface.tool = MixerPanelInterface.HandTool + Qt.callLater(() => GlobalHelper.setProperty(this, "checked", true)) + } + } + } +} diff --git a/src/plugins/visualeditor/qml/additionaltracks/LabelTrack.qml b/src/plugins/visualeditor/qml/additionaltracks/LabelTrack.qml index 1829c238..9b8f7961 100644 --- a/src/plugins/visualeditor/qml/additionaltracks/LabelTrack.qml +++ b/src/plugins/visualeditor/qml/additionaltracks/LabelTrack.qml @@ -34,6 +34,7 @@ QtObject { property LabelViewModel itemBeingDragged: null + // TODO move this to C++ code Connections { target: control.itemBeingDragged function onPositionChanged() { @@ -43,16 +44,21 @@ QtObject { Connections { target: control.labelSequenceInteractionController - function onItemInteractionOperationStarted(labelSequence, item, interactionFlag) { - if (labelSequence === control && interactionFlag === LabelSequenceInteractionController.Move) { + function onMovingStarted(labelSequence, item) { + if (labelSequence === control) { control.itemBeingDragged = item } } - function onItemInteractionOperationFinished(labelSequence, item, interactionFlag) { - if (labelSequence === control && interactionFlag === LabelSequenceInteractionController.Move) { + function onMovingFinished(labelSequence, item) { + if (labelSequence === control) { control.itemBeingDragged = null } } + + function onRubberBandDraggingStarted() { + control.timeLayoutViewModel.cursorPosition = -1 + } + } } diff --git a/src/plugins/visualeditor/qml/additionaltracks/TempoTrack.qml b/src/plugins/visualeditor/qml/additionaltracks/TempoTrack.qml index 379a12cc..93943dd0 100644 --- a/src/plugins/visualeditor/qml/additionaltracks/TempoTrack.qml +++ b/src/plugins/visualeditor/qml/additionaltracks/TempoTrack.qml @@ -34,6 +34,7 @@ QtObject { property LabelViewModel itemBeingDragged: null + // TODO move this to C++ code Connections { target: control.itemBeingDragged function onPositionChanged() { @@ -43,16 +44,20 @@ QtObject { Connections { target: control.labelSequenceInteractionController - function onItemInteractionOperationStarted(labelSequence, item, interactionFlag) { - if (labelSequence === control && interactionFlag === LabelSequenceInteractionController.Move) { + function onMovingStarted(labelSequence, item, interactionFlag) { + if (labelSequence === control) { control.itemBeingDragged = item } } - function onItemInteractionOperationFinished(labelSequence, item, interactionFlag) { - if (labelSequence === control && interactionFlag === LabelSequenceInteractionController.Move) { + function onMovingFinished(labelSequence, item, interactionFlag) { + if (labelSequence === control) { control.itemBeingDragged = null } } + + function onRubberBandDraggingStarted() { + control.timeLayoutViewModel.cursorPosition = -1 + } } } diff --git a/src/plugins/visualeditor/qml/panels/MixerPanel.qml b/src/plugins/visualeditor/qml/panels/MixerPanel.qml index e0ec0b2a..2d8659a3 100644 --- a/src/plugins/visualeditor/qml/panels/MixerPanel.qml +++ b/src/plugins/visualeditor/qml/panels/MixerPanel.qml @@ -8,6 +8,38 @@ import SVSCraft.UIComponents import DiffScope.UIShell -ActionDockingPane { +import QActionKit +QtObject { + id: d + required property QtObject addOn + + readonly property Component mixerPanelComponent: ActionDockingPane { + function loadState(state) { + if (!state) + return + d.addOn.mixerPanelInterface.tool = state.tool + } + + function saveState() { + return { + tool: d.addOn?.mixerPanelInterface.tool, + } + } + + header: ToolBarContainer { + anchors.fill: parent + property MenuActionInstantiator instantiator: MenuActionInstantiator { + actionId: "org.diffscope.visualeditor.mixerPanelToolBar" + context: d.addOn?.windowHandle.actionContext ?? null + separatorComponent: ToolBarContainerSeparator { + } + stretchComponent: ToolBarContainerStretch { + } + Component.onCompleted: forceUpdateLayouts() + } + } + + data: [d.addOn?.mixerPanelInterface.mixerView ?? null] + } } \ No newline at end of file diff --git a/src/plugins/visualeditor/qml/settings/EditorPage.qml b/src/plugins/visualeditor/qml/settings/EditorPage.qml index 21525d3c..08a37a50 100644 --- a/src/plugins/visualeditor/qml/settings/EditorPage.qml +++ b/src/plugins/visualeditor/qml/settings/EditorPage.qml @@ -20,6 +20,7 @@ ScrollView { property bool middleButtonAutoScroll: false property int autoDurationPositionAlignment: 20 property bool enableTemporarySnapOff: false + property bool trackListOnRight: false property bool trackCursorPosition: false onAlternateAxisModifierChanged: if (started) pageHandle.markDirty() @@ -29,6 +30,7 @@ ScrollView { onMiddleButtonAutoScrollChanged: if (started) pageHandle.markDirty() onAutoDurationPositionAlignmentChanged: if (started) pageHandle.markDirty() onEnableTemporarySnapOffChanged: if (started) pageHandle.markDirty() + onTrackListOnRightChanged: if (started) pageHandle.markDirty() onTrackCursorPositionChanged: if (started) pageHandle.markDirty() anchors.fill: parent @@ -160,6 +162,32 @@ ScrollView { } } + GroupBox { + title: qsTr("Arrangement") + TextMatcherItem on title { + matcher: page.matcher + } + Layout.fillWidth: true + + GridLayout { + anchors.fill: parent + columns: 3 + + Label { + text: qsTr("Track list position") + TextMatcherItem on text { matcher: page.matcher } + } + Item { + Layout.fillWidth: true + } + ComboBox { + model: [qsTr("Left"), qsTr("Right")] + currentIndex: page.trackListOnRight ? 1 : 0 + onActivated: (index) => page.trackListOnRight = (index === 1) + } + } + } + GroupBox { title: qsTr("Miscellaneous") TextMatcherItem on title { diff --git a/src/plugins/visualeditor/res/org.diffscope.visualeditor_actions.xml b/src/plugins/visualeditor/res/org.diffscope.visualeditor_actions.xml index c58f5d83..75294b66 100644 --- a/src/plugins/visualeditor/res/org.diffscope.visualeditor_actions.xml +++ b/src/plugins/visualeditor/res/org.diffscope.visualeditor_actions.xml @@ -18,6 +18,8 @@ + + @@ -48,8 +50,15 @@ + + + + + + + - + diff --git a/src/plugins/visualeditor/res/translations/org.diffscope.visualeditor.ts b/src/plugins/visualeditor/res/translations/org.diffscope.visualeditor.ts new file mode 100644 index 00000000..d21a2afc --- /dev/null +++ b/src/plugins/visualeditor/res/translations/org.diffscope.visualeditor.ts @@ -0,0 +1,438 @@ + + + + + + + + Main Menu + Main Menu + + + + Application + + + Version %1 + Version %1 + + + + Copyright © %1-%2 %3. All rights reserved. + Copyright © %1-%2 %3. All rights reserved. + + + + Application::ActionClass + + + + + + + + + + Arrangement Panel + Arrangement Panel + + + + + + Panel + Panel + + + + File + File + + + + Application::ActionText + + + Pointer Tool + Pointer Tool + + + + Pencil Tool + Pencil Tool + + + + Hand Tool + Hand Tool + + + + Snap + Snap + + + + Snap Duration + Snap Duration + + + + Snap Tuplet + Snap Tuplet + + + + Auto Page Scrolling + Auto Page Scrolling + + + + Additional Tracks + Additional Tracks + + + + Arrangement + Arrangement + + + + Mixer + Mixer + + + + Piano Roll + Piano Roll + + + + Additional Track Widgets + Additional Track Widgets + + + + Label + Label + + + + Tempo + Tempo + + + + Arrangement Panel Tool Bar + Arrangement Panel Tool Bar + + + + Tool Actions + Tool Actions + + + + Snap Actions + Snap Actions + + + + Arrangement Panel Timeline Tool Bar + Arrangement Panel Timeline Tool Bar + + + + New + New + + + + ArrangementAddOnActions + + + Auto + Auto + + + + + None + None + + + + Whole note + Whole note + + + + Half note + Half note + + + + Quarter note + Quarter note + + + + 8th note + 8th note + + + + 16th note + 16th note + + + + 32nd note + 32nd note + + + + 64th note + 64th note + + + + 128th note + 128th note + + + + Triplet + Triplet + + + + Quintuplet + Quintuplet + + + + ArrangementView + + + Move Up + Move Up + + + + Move Down + Move Down + + + + Remove + Remove + + + + EditorPage + + + Move and Zoom + Move and Zoom + + + + Horizontal scroll + Horizontal scroll + + + + Zoom + Zoom + + + + Scroll by page + Scroll by page + + + + Horizontal zoom + Horizontal zoom + + + + Middle button/hand tool scroll mode + Middle button/hand tool scroll mode + + + + Dragging + Dragging + + + + Auto scrolling + Auto scrolling + + + + Snap + Snap + + + + Auto-snap length + Auto-snap length + + + + Temporarily disable snap when pressing %1 + Temporarily disable snap when pressing %1 + + + + Miscellaneous + Miscellaneous + + + + Track cursor position + Track cursor position + + + + SnapControl + + + Snap + Snap + + + + Auto + Auto + + + + None + None + + + + Whole note + Whole note + + + + Half note + Half note + + + + Quarter note + Quarter note + + + + 8th note + 8th note + + + + 16th note + 16th note + + + + 32nd note + 32nd note + + + + 64th note + 64th note + + + + 128th note + 128th note + + + + Triplet + Triplet + + + + Quintuplet + Quintuplet + + + + TimelineContextMenuHelper + + + Insert Time Signature + Insert Time Signature + + + + + Move Playhead Here + Move Playhead Here + + + + Edit Time Signature + Edit Time Signature + + + + Remove Time Signature + Remove Time Signature + + + + VisualEditor::Internal::EditorPage + + + Editor + Editor + + + + Configure editor appearance and interaction behaviors + Configure editor appearance and interaction behaviors + + + + + + Scroll + Scroll + + + + VisualEditor::LabelViewModelContextData + + + Moving label + Moving label + + + + Editing label + Editing label + + + + Inserting label + Inserting label + + + + VisualEditor::TempoViewModelContextData + + + Moving tempo + Moving tempo + + + diff --git a/src/plugins/visualeditor/res/translations/org.diffscope.visualeditor_en_US.ts b/src/plugins/visualeditor/res/translations/org.diffscope.visualeditor_en_US.ts index b262e3bb..00bb6f63 100644 --- a/src/plugins/visualeditor/res/translations/org.diffscope.visualeditor_en_US.ts +++ b/src/plugins/visualeditor/res/translations/org.diffscope.visualeditor_en_US.ts @@ -1,4 +1,438 @@ + + + + + Main Menu + + + + + Application + + + Version %1 + + + + + Copyright © %1-%2 %3. All rights reserved. + + + + + Application::ActionClass + + + + + + + + + + Arrangement Panel + + + + + + + Panel + + + + + File + + + + + Application::ActionText + + + Pointer Tool + + + + + Pencil Tool + + + + + Hand Tool + + + + + Snap + + + + + Snap Duration + + + + + Snap Tuplet + + + + + Auto Page Scrolling + + + + + Additional Tracks + + + + + Arrangement + + + + + Mixer + + + + + Piano Roll + + + + + Additional Track Widgets + + + + + Label + + + + + Tempo + + + + + Arrangement Panel Tool Bar + + + + + Tool Actions + + + + + Snap Actions + + + + + Arrangement Panel Timeline Tool Bar + + + + + New + + + + + ArrangementAddOnActions + + + Auto + + + + + + None + + + + + Whole note + + + + + Half note + + + + + Quarter note + + + + + 8th note + + + + + 16th note + + + + + 32nd note + + + + + 64th note + + + + + 128th note + + + + + Triplet + + + + + Quintuplet + + + + + ArrangementView + + + Move Up + + + + + Move Down + + + + + Remove + + + + + EditorPage + + + Move and Zoom + + + + + Horizontal scroll + + + + + Zoom + + + + + Scroll by page + + + + + Horizontal zoom + + + + + Middle button/hand tool scroll mode + + + + + Dragging + + + + + Auto scrolling + + + + + Snap + + + + + Auto-snap length + + + + + Temporarily disable snap when pressing %1 + + + + + Miscellaneous + + + + + Track cursor position + + + + + SnapControl + + + Snap + + + + + Auto + + + + + None + + + + + Whole note + + + + + Half note + + + + + Quarter note + + + + + 8th note + + + + + 16th note + + + + + 32nd note + + + + + 64th note + + + + + 128th note + + + + + Triplet + + + + + Quintuplet + + + + + TimelineContextMenuHelper + + + Insert Time Signature + + + + + + Move Playhead Here + + + + + Edit Time Signature + + + + + Remove Time Signature + + + + + VisualEditor::Internal::EditorPage + + + Editor + + + + + Configure editor appearance and interaction behaviors + + + + + + + Scroll + + + + + VisualEditor::LabelViewModelContextData + + + Moving label + + + + + Editing label + + + + + Inserting label + + + + + VisualEditor::TempoViewModelContextData + + + Moving tempo + + + diff --git a/src/plugins/visualeditor/res/translations/org.diffscope.visualeditor_ja_JP.ts b/src/plugins/visualeditor/res/translations/org.diffscope.visualeditor_ja_JP.ts index 85f2f934..020ac2f1 100644 --- a/src/plugins/visualeditor/res/translations/org.diffscope.visualeditor_ja_JP.ts +++ b/src/plugins/visualeditor/res/translations/org.diffscope.visualeditor_ja_JP.ts @@ -1,4 +1,438 @@ + + + + + Main Menu + Main Menu + + + + Application + + + Version %1 + Version %1 + + + + Copyright © %1-%2 %3. All rights reserved. + Copyright © %1-%2 %3. All rights reserved. + + + + Application::ActionClass + + + + + + + + + + Arrangement Panel + Arrangement Panel + + + + + + Panel + Panel + + + + File + File + + + + Application::ActionText + + + Pointer Tool + Pointer Tool + + + + Pencil Tool + Pencil Tool + + + + Hand Tool + Hand Tool + + + + Snap + Snap + + + + Snap Duration + Snap Duration + + + + Snap Tuplet + Snap Tuplet + + + + Auto Page Scrolling + Auto Page Scrolling + + + + Additional Tracks + Additional Tracks + + + + Arrangement + Arrangement + + + + Mixer + Mixer + + + + Piano Roll + Piano Roll + + + + Additional Track Widgets + Additional Track Widgets + + + + Label + Label + + + + Tempo + Tempo + + + + Arrangement Panel Tool Bar + Arrangement Panel Tool Bar + + + + Tool Actions + Tool Actions + + + + Snap Actions + Snap Actions + + + + Arrangement Panel Timeline Tool Bar + Arrangement Panel Timeline Tool Bar + + + + New + New + + + + ArrangementAddOnActions + + + Auto + Auto + + + + + None + None + + + + Whole note + Whole note + + + + Half note + Half note + + + + Quarter note + Quarter note + + + + 8th note + 8th note + + + + 16th note + 16th note + + + + 32nd note + 32nd note + + + + 64th note + 64th note + + + + 128th note + 128th note + + + + Triplet + Triplet + + + + Quintuplet + Quintuplet + + + + ArrangementView + + + Move Up + Move Up + + + + Move Down + Move Down + + + + Remove + Remove + + + + EditorPage + + + Move and Zoom + Move and Zoom + + + + Horizontal scroll + Horizontal scroll + + + + Zoom + Zoom + + + + Scroll by page + Scroll by page + + + + Horizontal zoom + Horizontal zoom + + + + Middle button/hand tool scroll mode + Middle button/hand tool scroll mode + + + + Dragging + Dragging + + + + Auto scrolling + Auto scrolling + + + + Snap + Snap + + + + Auto-snap length + Auto-snap length + + + + Temporarily disable snap when pressing %1 + Temporarily disable snap when pressing %1 + + + + Miscellaneous + Miscellaneous + + + + Track cursor position + Track cursor position + + + + SnapControl + + + Snap + Snap + + + + Auto + Auto + + + + None + None + + + + Whole note + Whole note + + + + Half note + Half note + + + + Quarter note + Quarter note + + + + 8th note + 8th note + + + + 16th note + 16th note + + + + 32nd note + 32nd note + + + + 64th note + 64th note + + + + 128th note + 128th note + + + + Triplet + Triplet + + + + Quintuplet + Quintuplet + + + + TimelineContextMenuHelper + + + Insert Time Signature + Insert Time Signature + + + + + Move Playhead Here + Move Playhead Here + + + + Edit Time Signature + Edit Time Signature + + + + Remove Time Signature + Remove Time Signature + + + + VisualEditor::Internal::EditorPage + + + Editor + Editor + + + + Configure editor appearance and interaction behaviors + Configure editor appearance and interaction behaviors + + + + + + Scroll + Scroll + + + + VisualEditor::LabelViewModelContextData + + + Moving label + Moving label + + + + Editing label + Editing label + + + + Inserting label + Inserting label + + + + VisualEditor::TempoViewModelContextData + + + Moving tempo + Moving tempo + + diff --git a/src/plugins/visualeditor/res/translations/org.diffscope.visualeditor_zh_CN.ts b/src/plugins/visualeditor/res/translations/org.diffscope.visualeditor_zh_CN.ts index 9c60864f..27f5eee2 100644 --- a/src/plugins/visualeditor/res/translations/org.diffscope.visualeditor_zh_CN.ts +++ b/src/plugins/visualeditor/res/translations/org.diffscope.visualeditor_zh_CN.ts @@ -1,4 +1,438 @@ + + + + + Main Menu + 主菜单 + + + + Application + + + Version %1 + 版本 %1 + + + + Copyright © %1-%2 %3. All rights reserved. + 版权所有 © %1-%2 %3。保留所有权利。 + + + + Application::ActionClass + + + + + + + + + + Arrangement Panel + 编曲面板 + + + + + + Panel + 面板 + + + + File + 文件 + + + + Application::ActionText + + + Pointer Tool + 指针工具 + + + + Pencil Tool + 铅笔工具 + + + + Hand Tool + 手型工具 + + + + Snap + 对齐 + + + + Snap Duration + 对齐时长 + + + + Snap Tuplet + 对齐连音 + + + + Auto Page Scrolling + 自动页面滚动 + + + + Additional Tracks + 附加轨道 + + + + Arrangement + 编曲 + + + + Mixer + 混音器 + + + + Piano Roll + 钢琴卷帘 + + + + Additional Track Widgets + 附加轨道小组件 + + + + Label + 标签 + + + + Tempo + 曲速 + + + + Arrangement Panel Tool Bar + 编曲面板工具栏 + + + + Tool Actions + 工具操作 + + + + Snap Actions + 对齐操作 + + + + Arrangement Panel Timeline Tool Bar + 编曲面板时间轴工具栏 + + + + New + 新建 + + + + ArrangementAddOnActions + + + Auto + 自动 + + + + + None + + + + + Whole note + 全音符 + + + + Half note + 二分音符 + + + + Quarter note + 四分音符 + + + + 8th note + 八分音符 + + + + 16th note + 16 分音符 + + + + 32nd note + 32 分音符 + + + + 64th note + 64 分音符 + + + + 128th note + 128 分音符 + + + + Triplet + 三连音 + + + + Quintuplet + 五连音 + + + + ArrangementView + + + Move Up + 上移 + + + + Move Down + 下移 + + + + Remove + 移除 + + + + EditorPage + + + Move and Zoom + 移动和缩放 + + + + Horizontal scroll + 水平滚动 + + + + Zoom + 缩放 + + + + Scroll by page + 按页滚动 + + + + Horizontal zoom + 水平缩放 + + + + Middle button/hand tool scroll mode + 中键/手型工具滚动模式 + + + + Dragging + 拖动 + + + + Auto scrolling + 自动滚动 + + + + Snap + 对齐 + + + + Auto-snap length + 自动对齐长度 + + + + Temporarily disable snap when pressing %1 + 当按下 %1 时临时禁用对齐 + + + + Miscellaneous + 杂项 + + + + Track cursor position + 跟踪光标位置 + + + + SnapControl + + + Snap + 对齐 + + + + Auto + 自动 + + + + None + + + + + Whole note + 全音符 + + + + Half note + 二分音符 + + + + Quarter note + 四分音符 + + + + 8th note + 八分音符 + + + + 16th note + 16 分音符 + + + + 32nd note + 32 分音符 + + + + 64th note + 64 分音符 + + + + 128th note + 128 分音符 + + + + Triplet + 三连音 + + + + Quintuplet + 五连音 + + + + TimelineContextMenuHelper + + + Insert Time Signature + 插入拍号 + + + + + Move Playhead Here + 移动播放头至此处 + + + + Edit Time Signature + 编辑拍号 + + + + Remove Time Signature + 移除拍号 + + + + VisualEditor::Internal::EditorPage + + + Editor + 编辑器 + + + + Configure editor appearance and interaction behaviors + 配置编辑器外观和交互行为 + + + + + + Scroll + 滚动 + + + + VisualEditor::LabelViewModelContextData + + + Moving label + 移动标签 + + + + Editing label + 编辑标签 + + + + Inserting label + 插入标签 + + + + VisualEditor::TempoViewModelContextData + + + Moving tempo + 移动曲速 + + diff --git a/src/plugins/visualeditor/res/translations/org.diffscope.visualeditor_zh_TW.ts b/src/plugins/visualeditor/res/translations/org.diffscope.visualeditor_zh_TW.ts index 2316ddac..ffab4c8b 100644 --- a/src/plugins/visualeditor/res/translations/org.diffscope.visualeditor_zh_TW.ts +++ b/src/plugins/visualeditor/res/translations/org.diffscope.visualeditor_zh_TW.ts @@ -1,4 +1,438 @@ + + + + + Main Menu + Main Menu + + + + Application + + + Version %1 + Version %1 + + + + Copyright © %1-%2 %3. All rights reserved. + Copyright © %1-%2 %3. All rights reserved. + + + + Application::ActionClass + + + + + + + + + + Arrangement Panel + Arrangement Panel + + + + + + Panel + Panel + + + + File + File + + + + Application::ActionText + + + Pointer Tool + Pointer Tool + + + + Pencil Tool + Pencil Tool + + + + Hand Tool + Hand Tool + + + + Snap + Snap + + + + Snap Duration + Snap Duration + + + + Snap Tuplet + Snap Tuplet + + + + Auto Page Scrolling + Auto Page Scrolling + + + + Additional Tracks + Additional Tracks + + + + Arrangement + Arrangement + + + + Mixer + Mixer + + + + Piano Roll + Piano Roll + + + + Additional Track Widgets + Additional Track Widgets + + + + Label + Label + + + + Tempo + Tempo + + + + Arrangement Panel Tool Bar + Arrangement Panel Tool Bar + + + + Tool Actions + Tool Actions + + + + Snap Actions + Snap Actions + + + + Arrangement Panel Timeline Tool Bar + Arrangement Panel Timeline Tool Bar + + + + New + New + + + + ArrangementAddOnActions + + + Auto + Auto + + + + + None + None + + + + Whole note + Whole note + + + + Half note + Half note + + + + Quarter note + Quarter note + + + + 8th note + 8th note + + + + 16th note + 16th note + + + + 32nd note + 32nd note + + + + 64th note + 64th note + + + + 128th note + 128th note + + + + Triplet + Triplet + + + + Quintuplet + Quintuplet + + + + ArrangementView + + + Move Up + Move Up + + + + Move Down + Move Down + + + + Remove + Remove + + + + EditorPage + + + Move and Zoom + Move and Zoom + + + + Horizontal scroll + Horizontal scroll + + + + Zoom + Zoom + + + + Scroll by page + Scroll by page + + + + Horizontal zoom + Horizontal zoom + + + + Middle button/hand tool scroll mode + Middle button/hand tool scroll mode + + + + Dragging + Dragging + + + + Auto scrolling + Auto scrolling + + + + Snap + Snap + + + + Auto-snap length + Auto-snap length + + + + Temporarily disable snap when pressing %1 + Temporarily disable snap when pressing %1 + + + + Miscellaneous + Miscellaneous + + + + Track cursor position + Track cursor position + + + + SnapControl + + + Snap + Snap + + + + Auto + Auto + + + + None + None + + + + Whole note + Whole note + + + + Half note + Half note + + + + Quarter note + Quarter note + + + + 8th note + 8th note + + + + 16th note + 16th note + + + + 32nd note + 32nd note + + + + 64th note + 64th note + + + + 128th note + 128th note + + + + Triplet + Triplet + + + + Quintuplet + Quintuplet + + + + TimelineContextMenuHelper + + + Insert Time Signature + Insert Time Signature + + + + + Move Playhead Here + Move Playhead Here + + + + Edit Time Signature + Edit Time Signature + + + + Remove Time Signature + Remove Time Signature + + + + VisualEditor::Internal::EditorPage + + + Editor + Editor + + + + Configure editor appearance and interaction behaviors + Configure editor appearance and interaction behaviors + + + + + + Scroll + Scroll + + + + VisualEditor::LabelViewModelContextData + + + Moving label + Moving label + + + + Editing label + Editing label + + + + Inserting label + Inserting label + + + + VisualEditor::TempoViewModelContextData + + + Moving tempo + Moving tempo + + diff --git a/src/plugins/welcomewizard/res/translations/org.diffscope.welcomewizard.ts b/src/plugins/welcomewizard/res/translations/org.diffscope.welcomewizard.ts new file mode 100644 index 00000000..3b67b6fd --- /dev/null +++ b/src/plugins/welcomewizard/res/translations/org.diffscope.welcomewizard.ts @@ -0,0 +1,49 @@ + + + + + + + + Main Menu + Main Menu + + + + Application + + + Version %1 + Version %1 + + + + Copyright © %1-%2 %3. All rights reserved. + Copyright © %1-%2 %3. All rights reserved. + + + + Application::ActionClass + + Welcome Wizard + Welcome Wizard + + + + Welcomewizard + Welcomewizard + + + + Application::ActionText + + Main Menu + Main Menu + + + + Welcome Wizard + Welcome Wizard + + + diff --git a/src/plugins/welcomewizard/res/translations/org.diffscope.welcomewizard_en_US.ts b/src/plugins/welcomewizard/res/translations/org.diffscope.welcomewizard_en_US.ts index 2f4d2b16..9e1644eb 100644 --- a/src/plugins/welcomewizard/res/translations/org.diffscope.welcomewizard_en_US.ts +++ b/src/plugins/welcomewizard/res/translations/org.diffscope.welcomewizard_en_US.ts @@ -1,25 +1,49 @@ - + + + + + Main Menu + Main Menu + + + + Application + + + Version %1 + + + + + Copyright © %1-%2 %3. All rights reserved. + + + + Application::ActionClass - - Welcome Wizard - Welcome Wizard + Welcome Wizard + Welcome Wizard + + + + Welcomewizard + - - + + Application::ActionText - - Main Menu - Main Menu + Main Menu + Main Menu - - Welcome Wizard - Welcome Wizard + + Welcome Wizard + Welcome Wizard - + diff --git a/src/plugins/welcomewizard/res/translations/org.diffscope.welcomewizard_ja_JP.ts b/src/plugins/welcomewizard/res/translations/org.diffscope.welcomewizard_ja_JP.ts index de9546ff..1d391cd6 100644 --- a/src/plugins/welcomewizard/res/translations/org.diffscope.welcomewizard_ja_JP.ts +++ b/src/plugins/welcomewizard/res/translations/org.diffscope.welcomewizard_ja_JP.ts @@ -1,23 +1,47 @@ + + + + + Main Menu + Main Menu + + + + Application + + + Version %1 + Version %1 + + + + Copyright © %1-%2 %3. All rights reserved. + Copyright © %1-%2 %3. All rights reserved. + + Application::ActionClass - Welcome Wizard Welcome Wizard + + + Welcomewizard + Welcomewizard + Application::ActionText - Main Menu Main Menu - + Welcome Wizard Welcome Wizard diff --git a/src/plugins/welcomewizard/res/translations/org.diffscope.welcomewizard_zh_CN.ts b/src/plugins/welcomewizard/res/translations/org.diffscope.welcomewizard_zh_CN.ts index 5ee95b5d..eebdcd80 100644 --- a/src/plugins/welcomewizard/res/translations/org.diffscope.welcomewizard_zh_CN.ts +++ b/src/plugins/welcomewizard/res/translations/org.diffscope.welcomewizard_zh_CN.ts @@ -1,23 +1,47 @@ + + + + + Main Menu + 主菜单 + + + + Application + + + Version %1 + 版本 %1 + + + + Copyright © %1-%2 %3. All rights reserved. + 版权所有 © %1-%2 %3。保留所有权利。 + + Application::ActionClass - Welcome Wizard 欢迎向导 + + + Welcomewizard + 欢迎向导 + Application::ActionText - Main Menu 主菜单 - + Welcome Wizard 欢迎向导 diff --git a/src/plugins/welcomewizard/res/translations/org.diffscope.welcomewizard_zh_TW.ts b/src/plugins/welcomewizard/res/translations/org.diffscope.welcomewizard_zh_TW.ts index 85ee6790..57655b56 100644 --- a/src/plugins/welcomewizard/res/translations/org.diffscope.welcomewizard_zh_TW.ts +++ b/src/plugins/welcomewizard/res/translations/org.diffscope.welcomewizard_zh_TW.ts @@ -1,23 +1,47 @@ + + + + + Main Menu + Main Menu + + + + Application + + + Version %1 + Version %1 + + + + Copyright © %1-%2 %3. All rights reserved. + Copyright © %1-%2 %3. All rights reserved. + + Application::ActionClass - Welcome Wizard Welcome Wizard + + + Welcomewizard + Welcomewizard + Application::ActionText - Main Menu Main Menu - + Welcome Wizard Welcome Wizard