Plan document open actions in app core
This commit is contained in:
@@ -394,6 +394,9 @@ Known local toolchain state:
|
|||||||
- `pano_cli classify-open` exposes the `pp_app_core` document-open route
|
- `pano_cli classify-open` exposes the `pp_app_core` document-open route
|
||||||
contract as JSON and is covered for project files, ABR imports, PPBR
|
contract as JSON and is covered for project files, ABR imports, PPBR
|
||||||
imports, and malformed path rejection.
|
imports, and malformed path rejection.
|
||||||
|
- `pano_cli plan-open-route` exposes `pp_app_core` document-open action
|
||||||
|
planning as JSON and is covered for clean project open, dirty project
|
||||||
|
discard-prompt, and ABR import-prompt states.
|
||||||
- `pano_cli plan-document-file` exposes `pp_app_core` document-name
|
- `pano_cli plan-document-file` exposes `pp_app_core` document-name
|
||||||
validation, legacy `.ppi` path construction, and overwrite-prompt decisions
|
validation, legacy `.ppi` path construction, and overwrite-prompt decisions
|
||||||
as JSON and is covered for save-now and existing-target overwrite states.
|
as JSON and is covered for save-now and existing-target overwrite states.
|
||||||
@@ -417,9 +420,9 @@ Known local toolchain state:
|
|||||||
directory/stem targets, picked-directory stems, MP4 suggested names, and
|
directory/stem targets, picked-directory stems, MP4 suggested names, and
|
||||||
invalid export naming inputs.
|
invalid export naming inputs.
|
||||||
- `pp_app_core_document_session_tests` covers clean and dirty app session,
|
- `pp_app_core_document_session_tests` covers clean and dirty app session,
|
||||||
save-request, save-before-workflow, document file target, overwrite, and
|
document-open action planning, save-request, save-before-workflow, document
|
||||||
save-version target decisions without requiring a window, canvas, or message
|
file target, overwrite, and save-version target decisions without requiring
|
||||||
box.
|
a window, canvas, or message box.
|
||||||
- `pp_ui_core` consumes vcpkg tinyxml2 only when `PP_USE_VCPKG_TINYXML2=ON`
|
- `pp_ui_core` consumes vcpkg tinyxml2 only when `PP_USE_VCPKG_TINYXML2=ON`
|
||||||
through the vcpkg preset; default and Android validation still use the
|
through the vcpkg preset; default and Android validation still use the
|
||||||
retained vendored fallback tracked by DEBT-0012.
|
retained vendored fallback tracked by DEBT-0012.
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ and validation command.
|
|||||||
| Capability | Current Area | Target Owner | Required Tests |
|
| Capability | Current Area | Target Owner | Required Tests |
|
||||||
| --- | --- | --- | --- |
|
| --- | --- | --- | --- |
|
||||||
| PPI open/save | `Canvas`, serializer, dialogs | `pp_document`, `pp_assets`, `pano_cli` | Round-trip tiny project, old-version fixture, corrupt/truncated fixture |
|
| PPI open/save | `Canvas`, serializer, dialogs | `pp_document`, `pp_assets`, `pano_cli` | Round-trip tiny project, old-version fixture, corrupt/truncated fixture |
|
||||||
| Open-document routing | `App::open_document` | `pp_app_core`, `pano_cli`, `pp_panopainter_ui`, `pp_document`, `pp_assets` | Project/ABR/PPBR route tests, malformed path tests, CLI route smoke, app open smoke |
|
| Open-document routing | `App::open_document` | `pp_app_core`, `pano_cli`, `pp_panopainter_ui`, `pp_document`, `pp_assets` | Project/ABR/PPBR route tests, malformed path tests, open-action plan tests, CLI route/action smoke, app open smoke |
|
||||||
| Document session decisions | `App::open_document`, `App::request_close`, save hotkeys, file menu, dialogs | `pp_app_core`, `pano_cli`, `pp_panopainter_ui` | Clean/dirty/prompt-open/save/save-as/save-version/save-before-workflow/name/overwrite/version-target decision tests, CLI session, document-file, and document-version smoke, app close/open/save/new/browse smoke |
|
| Document session decisions | `App::open_document`, `App::request_close`, save hotkeys, file menu, dialogs | `pp_app_core`, `pano_cli`, `pp_panopainter_ui` | Clean/dirty/prompt-open/save/save-as/save-version/save-before-workflow/name/overwrite/version-target decision tests, CLI session, document-file, and document-version smoke, app close/open/save/new/browse smoke |
|
||||||
| Version metadata | `scripts/pre-build.py`, `version.*` | build system, `pp_foundation` | Generated header smoke test, missing-tag behavior |
|
| Version metadata | `scripts/pre-build.py`, `version.*` | build system, `pp_foundation` | Generated header smoke test, missing-tag behavior |
|
||||||
| Thumbnail generation/read | `Canvas`, `Image` | `pp_assets`, `pp_paint_renderer` | Golden thumbnail, corrupt input |
|
| Thumbnail generation/read | `Canvas`, `Image` | `pp_assets`, `pp_paint_renderer` | Golden thumbnail, corrupt input |
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ agent or engineer to remove them without reconstructing context from chat.
|
|||||||
| --- | --- | --- | --- | --- | --- | --- |
|
| --- | --- | --- | --- | --- | --- | --- |
|
||||||
| DEBT-0001 | Open | Modernization | Existing platform build files remain alongside new CMake | Required for incremental migration without losing platform coverage | Existing platform builds plus new CMake configure | Remove after all platform builds consume shared CMake targets |
|
| DEBT-0001 | Open | Modernization | Existing platform build files remain alongside new CMake | Required for incremental migration without losing platform coverage | Existing platform builds plus new CMake configure | Remove after all platform builds consume shared CMake targets |
|
||||||
| DEBT-0002 | Open | Modernization | Vendored SDK and patched libraries retained initially | Some dependencies are SDK-only, patched, or have platform-specific binaries | Dependency inventory and platform build smoke tests | Replace with vcpkg packages or document permanent vendored status after triplet evaluation |
|
| DEBT-0002 | Open | Modernization | Vendored SDK and patched libraries retained initially | Some dependencies are SDK-only, patched, or have platform-specific binaries | Dependency inventory and platform build smoke tests | Replace with vcpkg packages or document permanent vendored status after triplet evaluation |
|
||||||
| DEBT-0003 | Open | Modernization | Existing singletons remain during initial split; `App::open_document`, `App::request_close`, file-menu save actions, `NodeCanvas` save hotkeys, new/open/browse dirty-document workflow prompts, new/save-as document file naming and overwrite decisions, save-version target decisions, export target naming/path decisions, `pano_cli classify-open`, `pano_cli plan-document-file`, `pano_cli plan-document-version`, `pano_cli plan-export-target`, and `pano_cli simulate-app-session` now consume pure `pp_app_core` route/session/export contracts, but document loading, saving, and export execution still reach legacy `Canvas::I` and UI singletons | Avoid behavior changes while introducing component boundaries | App launch and component tests; `pp_app_core_document_route_tests`; `pp_app_core_document_export_tests`; `pp_app_core_document_session_tests`; `pano_cli classify-open --path D:/Paint/demo.ppi`; `pano_cli plan-document-file --work-dir D:/Paint --name demo --target-exists`; `pano_cli plan-document-version --directory D:/Paint --doc-name demo.01 --existing-path D:/Paint/demo.02.ppi`; `pano_cli plan-export-target --kind file --work-dir D:/Paint --doc-name demo --extension .png`; `pano_cli simulate-app-session --unsaved --save-intent save-dirty-version`; `pano_cli simulate-app-session --no-canvas`; `ctest --preset desktop-fast --build-config Debug` | Replace singleton reaches with context/service injection at component boundaries |
|
| DEBT-0003 | Open | Modernization | Existing singletons remain during initial split; `App::open_document`, `App::request_close`, file-menu save actions, `NodeCanvas` save hotkeys, new/open/browse dirty-document workflow prompts, new/save-as document file naming and overwrite decisions, save-version target decisions, export target naming/path decisions, `pano_cli classify-open`, `pano_cli plan-open-route`, `pano_cli plan-document-file`, `pano_cli plan-document-version`, `pano_cli plan-export-target`, and `pano_cli simulate-app-session` now consume pure `pp_app_core` route/session/export contracts, but document loading, brush import execution, saving, and export execution still reach legacy `Canvas::I`/UI singletons | Avoid behavior changes while introducing component boundaries | App launch and component tests; `pp_app_core_document_route_tests`; `pp_app_core_document_export_tests`; `pp_app_core_document_session_tests`; `pano_cli classify-open --path D:/Paint/demo.ppi`; `pano_cli plan-open-route --path D:/Paint/demo.ppi --unsaved`; `pano_cli plan-document-file --work-dir D:/Paint --name demo --target-exists`; `pano_cli plan-document-version --directory D:/Paint --doc-name demo.01 --existing-path D:/Paint/demo.02.ppi`; `pano_cli plan-export-target --kind file --work-dir D:/Paint --doc-name demo --extension .png`; `pano_cli simulate-app-session --unsaved --save-intent save-dirty-version`; `pano_cli simulate-app-session --no-canvas`; `ctest --preset desktop-fast --build-config Debug` | Replace singleton reaches with context/service injection at component boundaries |
|
||||||
| DEBT-0004 | Open | Modernization | Android, Linux, WebGL, Apple, and AppX build files remain platform-specific until root CMake alignment reaches them | Prevent platform regressions during incremental migration; raw Windows `.sln/.vcxproj` files were removed on 2026-05-31 by user decision | `cmake --preset windows-msvc-default`; platform-specific configure/build smoke checks as each platform is migrated | Root CMake owns every platform source list and package path |
|
| DEBT-0004 | Open | Modernization | Android, Linux, WebGL, Apple, and AppX build files remain platform-specific until root CMake alignment reaches them | Prevent platform regressions during incremental migration; raw Windows `.sln/.vcxproj` files were removed on 2026-05-31 by user decision | `cmake --preset windows-msvc-default`; platform-specific configure/build smoke checks as each platform is migrated | Root CMake owns every platform source list and package path |
|
||||||
| DEBT-0005 | Open | Modernization | Temporary local CTest harness is used before Catch2 is wired through vcpkg | `vcpkg` is not currently on PATH, but headless tests need to run now | `ctest --preset desktop-fast --build-config Debug` | Replace `tests/test_harness.h` tests with Catch2 tests once vcpkg toolchain/presets are validated |
|
| DEBT-0005 | Open | Modernization | Temporary local CTest harness is used before Catch2 is wired through vcpkg | `vcpkg` is not currently on PATH, but headless tests need to run now | `ctest --preset desktop-fast --build-config Debug` | Replace `tests/test_harness.h` tests with Catch2 tests once vcpkg toolchain/presets are validated |
|
||||||
| DEBT-0007 | Open | Modernization | `vcpkg.json` and `windows-msvc-vcpkg-headless` are validated for the headless Windows component matrix, but app targets still use vendored libraries and Android/Apple triplets are not proven | Dependency migration must stay incremental while SDK/patched/vendor dependencies remain in use | `$env:VCPKG_ROOT="C:\Program Files\Microsoft Visual Studio\2022\Community\VC\vcpkg"; cmake --preset windows-msvc-vcpkg-headless`; `ctest --preset desktop-fast-vcpkg --build-config Debug` | Component targets consume vcpkg packages where reliable and desktop app, Android, and Apple triplets are validated or explicitly documented as permanent vendor exceptions |
|
| DEBT-0007 | Open | Modernization | `vcpkg.json` and `windows-msvc-vcpkg-headless` are validated for the headless Windows component matrix, but app targets still use vendored libraries and Android/Apple triplets are not proven | Dependency migration must stay incremental while SDK/patched/vendor dependencies remain in use | `$env:VCPKG_ROOT="C:\Program Files\Microsoft Visual Studio\2022\Community\VC\vcpkg"; cmake --preset windows-msvc-vcpkg-headless`; `ctest --preset desktop-fast-vcpkg --build-config Debug` | Component targets consume vcpkg packages where reliable and desktop app, Android, and Apple triplets are validated or explicitly documented as permanent vendor exceptions |
|
||||||
|
|||||||
@@ -423,9 +423,11 @@ the applied stroke payload survives inspect/load round-trip automation, with a
|
|||||||
rejection smoke test for unsafe tiny canvas dimensions.
|
rejection smoke test for unsafe tiny canvas dimensions.
|
||||||
`pano_cli classify-open` exposes the pure `pp_app_core` document-open route
|
`pano_cli classify-open` exposes the pure `pp_app_core` document-open route
|
||||||
contract for project files, ABR imports, PPBR imports, and malformed path
|
contract for project files, ABR imports, PPBR imports, and malformed path
|
||||||
rejection. `pano_cli simulate-app-session` exposes the pure `pp_app_core`
|
rejection. `pano_cli plan-open-route` exposes the pure `pp_app_core`
|
||||||
session decisions used by project-open, app-close, save, save-as, and
|
document-open action plan for clean project open, dirty project prompt, and
|
||||||
save-version flows, plus the save-before-continue workflow gate used by
|
brush-import prompt flows. `pano_cli simulate-app-session` exposes the pure
|
||||||
|
`pp_app_core` session decisions used by project-open, app-close, save, save-as,
|
||||||
|
and save-version flows, plus the save-before-continue workflow gate used by
|
||||||
new-document/open/browse dialogs. `pano_cli plan-document-file` exposes the
|
new-document/open/browse dialogs. `pano_cli plan-document-file` exposes the
|
||||||
same app-core document-name validation, legacy `.ppi` path construction, and
|
same app-core document-name validation, legacy `.ppi` path construction, and
|
||||||
overwrite prompt decision used by new-document and save-as dialogs.
|
overwrite prompt decision used by new-document and save-as dialogs.
|
||||||
|
|||||||
10
src/app.cpp
10
src/app.cpp
@@ -193,7 +193,10 @@ void App::open_document(std::string path)
|
|||||||
if (!route)
|
if (!route)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (route.value().kind == pp::app::DocumentOpenKind::import_abr)
|
const bool has_unsaved_project =
|
||||||
|
route.value().kind == pp::app::DocumentOpenKind::open_project && Canvas::I->m_unsaved;
|
||||||
|
const auto open_plan = pp::app::plan_document_open(route.value().kind, has_unsaved_project);
|
||||||
|
if (open_plan == pp::app::DocumentOpenPlanAction::prompt_import_abr)
|
||||||
{
|
{
|
||||||
auto mb = message_box("Import ABR", "Would you like to import the brushes?", true);
|
auto mb = message_box("Import ABR", "Would you like to import the brushes?", true);
|
||||||
mb->on_submit = [this, path] (Node* target) {
|
mb->on_submit = [this, path] (Node* target) {
|
||||||
@@ -201,7 +204,7 @@ void App::open_document(std::string path)
|
|||||||
target->destroy();
|
target->destroy();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
else if (route.value().kind == pp::app::DocumentOpenKind::import_ppbr)
|
else if (open_plan == pp::app::DocumentOpenPlanAction::prompt_import_ppbr)
|
||||||
{
|
{
|
||||||
auto mb = message_box("Import PPBR", "Would you like to import the brushes?", true);
|
auto mb = message_box("Import PPBR", "Would you like to import the brushes?", true);
|
||||||
mb->on_submit = [this, path] (Node* target) {
|
mb->on_submit = [this, path] (Node* target) {
|
||||||
@@ -239,8 +242,7 @@ void App::open_document(std::string path)
|
|||||||
});
|
});
|
||||||
ActionManager::clear();
|
ActionManager::clear();
|
||||||
};
|
};
|
||||||
const auto open_decision = pp::app::plan_project_open(Canvas::I->m_unsaved);
|
if (open_plan == pp::app::DocumentOpenPlanAction::open_project_now)
|
||||||
if (open_decision == pp::app::ProjectOpenDecision::open_now)
|
|
||||||
{
|
{
|
||||||
open_action();
|
open_action();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "app_core/document_route.h"
|
||||||
#include "foundation/result.h"
|
#include "foundation/result.h"
|
||||||
|
|
||||||
#include <cctype>
|
#include <cctype>
|
||||||
@@ -46,6 +47,13 @@ enum class DocumentFileWriteDecision {
|
|||||||
prompt_overwrite,
|
prompt_overwrite,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class DocumentOpenPlanAction {
|
||||||
|
open_project_now,
|
||||||
|
prompt_discard_unsaved_project,
|
||||||
|
prompt_import_abr,
|
||||||
|
prompt_import_ppbr,
|
||||||
|
};
|
||||||
|
|
||||||
struct DocumentFileTarget {
|
struct DocumentFileTarget {
|
||||||
std::string name;
|
std::string name;
|
||||||
std::string directory;
|
std::string directory;
|
||||||
@@ -64,6 +72,24 @@ struct DocumentVersionTarget {
|
|||||||
: ProjectOpenDecision::open_now;
|
: ProjectOpenDecision::open_now;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr DocumentOpenPlanAction plan_document_open(
|
||||||
|
DocumentOpenKind kind,
|
||||||
|
bool has_unsaved_changes) noexcept
|
||||||
|
{
|
||||||
|
switch (kind) {
|
||||||
|
case DocumentOpenKind::import_abr:
|
||||||
|
return DocumentOpenPlanAction::prompt_import_abr;
|
||||||
|
case DocumentOpenKind::import_ppbr:
|
||||||
|
return DocumentOpenPlanAction::prompt_import_ppbr;
|
||||||
|
case DocumentOpenKind::open_project:
|
||||||
|
return has_unsaved_changes
|
||||||
|
? DocumentOpenPlanAction::prompt_discard_unsaved_project
|
||||||
|
: DocumentOpenPlanAction::open_project_now;
|
||||||
|
}
|
||||||
|
|
||||||
|
return DocumentOpenPlanAction::open_project_now;
|
||||||
|
}
|
||||||
|
|
||||||
[[nodiscard]] constexpr CloseRequestDecision plan_close_request(
|
[[nodiscard]] constexpr CloseRequestDecision plan_close_request(
|
||||||
bool has_unsaved_changes,
|
bool has_unsaved_changes,
|
||||||
bool close_prompt_already_open) noexcept
|
bool close_prompt_already_open) noexcept
|
||||||
|
|||||||
@@ -381,6 +381,24 @@ if(TARGET pano_cli)
|
|||||||
LABELS "app;integration;desktop-fast;fuzz"
|
LABELS "app;integration;desktop-fast;fuzz"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
add_test(NAME pano_cli_plan_open_route_project_clean_smoke
|
||||||
|
COMMAND pano_cli plan-open-route --path "D:/Paint/Scenes/demo.ppi")
|
||||||
|
set_tests_properties(pano_cli_plan_open_route_project_clean_smoke PROPERTIES
|
||||||
|
LABELS "app;integration;desktop-fast"
|
||||||
|
PASS_REGULAR_EXPRESSION "\"command\":\"plan-open-route\".*\"kind\":\"open-project\".*\"unsaved\":false.*\"action\":\"open-project-now\"")
|
||||||
|
|
||||||
|
add_test(NAME pano_cli_plan_open_route_project_unsaved_smoke
|
||||||
|
COMMAND pano_cli plan-open-route --path "D:/Paint/Scenes/demo.ppi" --unsaved)
|
||||||
|
set_tests_properties(pano_cli_plan_open_route_project_unsaved_smoke PROPERTIES
|
||||||
|
LABELS "app;integration;desktop-fast"
|
||||||
|
PASS_REGULAR_EXPRESSION "\"command\":\"plan-open-route\".*\"kind\":\"open-project\".*\"unsaved\":true.*\"action\":\"prompt-discard-unsaved-project\"")
|
||||||
|
|
||||||
|
add_test(NAME pano_cli_plan_open_route_abr_import_smoke
|
||||||
|
COMMAND pano_cli plan-open-route --path "D:/Paint/Brushes/clouds.ABR" --unsaved)
|
||||||
|
set_tests_properties(pano_cli_plan_open_route_abr_import_smoke PROPERTIES
|
||||||
|
LABELS "app;integration;desktop-fast"
|
||||||
|
PASS_REGULAR_EXPRESSION "\"command\":\"plan-open-route\".*\"kind\":\"import-abr\".*\"unsaved\":true.*\"action\":\"prompt-import-abr\"")
|
||||||
|
|
||||||
add_test(NAME pano_cli_plan_document_file_save_now_smoke
|
add_test(NAME pano_cli_plan_document_file_save_now_smoke
|
||||||
COMMAND pano_cli plan-document-file --work-dir D:/Paint --name demo)
|
COMMAND pano_cli plan-document-file --work-dir D:/Paint --name demo)
|
||||||
set_tests_properties(pano_cli_plan_document_file_save_now_smoke PROPERTIES
|
set_tests_properties(pano_cli_plan_document_file_save_now_smoke PROPERTIES
|
||||||
|
|||||||
@@ -15,6 +15,38 @@ void project_open_dirty_document_prompts_for_discard(pp::tests::Harness& harness
|
|||||||
pp::app::plan_project_open(true) == pp::app::ProjectOpenDecision::prompt_discard_unsaved);
|
pp::app::plan_project_open(true) == pp::app::ProjectOpenDecision::prompt_discard_unsaved);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void document_open_project_respects_unsaved_state(pp::tests::Harness& harness)
|
||||||
|
{
|
||||||
|
PP_EXPECT(
|
||||||
|
harness,
|
||||||
|
pp::app::plan_document_open(pp::app::DocumentOpenKind::open_project, false)
|
||||||
|
== pp::app::DocumentOpenPlanAction::open_project_now);
|
||||||
|
PP_EXPECT(
|
||||||
|
harness,
|
||||||
|
pp::app::plan_document_open(pp::app::DocumentOpenKind::open_project, true)
|
||||||
|
== pp::app::DocumentOpenPlanAction::prompt_discard_unsaved_project);
|
||||||
|
}
|
||||||
|
|
||||||
|
void document_open_brush_imports_prompt_regardless_of_unsaved_state(pp::tests::Harness& harness)
|
||||||
|
{
|
||||||
|
PP_EXPECT(
|
||||||
|
harness,
|
||||||
|
pp::app::plan_document_open(pp::app::DocumentOpenKind::import_abr, false)
|
||||||
|
== pp::app::DocumentOpenPlanAction::prompt_import_abr);
|
||||||
|
PP_EXPECT(
|
||||||
|
harness,
|
||||||
|
pp::app::plan_document_open(pp::app::DocumentOpenKind::import_abr, true)
|
||||||
|
== pp::app::DocumentOpenPlanAction::prompt_import_abr);
|
||||||
|
PP_EXPECT(
|
||||||
|
harness,
|
||||||
|
pp::app::plan_document_open(pp::app::DocumentOpenKind::import_ppbr, false)
|
||||||
|
== pp::app::DocumentOpenPlanAction::prompt_import_ppbr);
|
||||||
|
PP_EXPECT(
|
||||||
|
harness,
|
||||||
|
pp::app::plan_document_open(pp::app::DocumentOpenKind::import_ppbr, true)
|
||||||
|
== pp::app::DocumentOpenPlanAction::prompt_import_ppbr);
|
||||||
|
}
|
||||||
|
|
||||||
void close_clean_document_executes_immediately(pp::tests::Harness& harness)
|
void close_clean_document_executes_immediately(pp::tests::Harness& harness)
|
||||||
{
|
{
|
||||||
PP_EXPECT(
|
PP_EXPECT(
|
||||||
@@ -206,6 +238,10 @@ int main()
|
|||||||
pp::tests::Harness harness;
|
pp::tests::Harness harness;
|
||||||
harness.run("project open clean document executes immediately", project_open_clean_document_executes_immediately);
|
harness.run("project open clean document executes immediately", project_open_clean_document_executes_immediately);
|
||||||
harness.run("project open dirty document prompts for discard", project_open_dirty_document_prompts_for_discard);
|
harness.run("project open dirty document prompts for discard", project_open_dirty_document_prompts_for_discard);
|
||||||
|
harness.run("document open project respects unsaved state", document_open_project_respects_unsaved_state);
|
||||||
|
harness.run(
|
||||||
|
"document open brush imports prompt regardless of unsaved state",
|
||||||
|
document_open_brush_imports_prompt_regardless_of_unsaved_state);
|
||||||
harness.run("close clean document executes immediately", close_clean_document_executes_immediately);
|
harness.run("close clean document executes immediately", close_clean_document_executes_immediately);
|
||||||
harness.run("close dirty document opens one prompt", close_dirty_document_opens_one_prompt);
|
harness.run("close dirty document opens one prompt", close_dirty_document_opens_one_prompt);
|
||||||
harness.run("save clean existing document is no op", save_clean_existing_document_is_no_op);
|
harness.run("save clean existing document is no op", save_clean_existing_document_is_no_op);
|
||||||
|
|||||||
@@ -97,6 +97,11 @@ struct ClassifyOpenArgs {
|
|||||||
std::string path;
|
std::string path;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct PlanOpenRouteArgs {
|
||||||
|
std::string path;
|
||||||
|
bool unsaved = false;
|
||||||
|
};
|
||||||
|
|
||||||
struct PlanDocumentFileArgs {
|
struct PlanDocumentFileArgs {
|
||||||
std::string work_directory;
|
std::string work_directory;
|
||||||
std::string name;
|
std::string name;
|
||||||
@@ -275,6 +280,22 @@ const char* project_open_decision_name(pp::app::ProjectOpenDecision decision) no
|
|||||||
return "open-now";
|
return "open-now";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const char* document_open_plan_action_name(pp::app::DocumentOpenPlanAction action) noexcept
|
||||||
|
{
|
||||||
|
switch (action) {
|
||||||
|
case pp::app::DocumentOpenPlanAction::open_project_now:
|
||||||
|
return "open-project-now";
|
||||||
|
case pp::app::DocumentOpenPlanAction::prompt_discard_unsaved_project:
|
||||||
|
return "prompt-discard-unsaved-project";
|
||||||
|
case pp::app::DocumentOpenPlanAction::prompt_import_abr:
|
||||||
|
return "prompt-import-abr";
|
||||||
|
case pp::app::DocumentOpenPlanAction::prompt_import_ppbr:
|
||||||
|
return "prompt-import-ppbr";
|
||||||
|
}
|
||||||
|
|
||||||
|
return "open-project-now";
|
||||||
|
}
|
||||||
|
|
||||||
const char* close_request_decision_name(pp::app::CloseRequestDecision decision) noexcept
|
const char* close_request_decision_name(pp::app::CloseRequestDecision decision) noexcept
|
||||||
{
|
{
|
||||||
switch (decision) {
|
switch (decision) {
|
||||||
@@ -377,6 +398,7 @@ void print_help()
|
|||||||
<< " import-image --path FILE [--document-width N] [--document-height N] [--face N] [--x N] [--y N]\n"
|
<< " import-image --path FILE [--document-width N] [--document-height N] [--face N] [--x N] [--y N]\n"
|
||||||
<< " inspect-project --path FILE\n"
|
<< " inspect-project --path FILE\n"
|
||||||
<< " classify-open --path FILE\n"
|
<< " classify-open --path FILE\n"
|
||||||
|
<< " plan-open-route --path FILE [--unsaved]\n"
|
||||||
<< " plan-document-file --work-dir DIR --name NAME [--target-exists]\n"
|
<< " plan-document-file --work-dir DIR --name NAME [--target-exists]\n"
|
||||||
<< " plan-document-version --directory DIR --doc-name NAME [--existing-path FILE]\n"
|
<< " plan-document-version --directory DIR --doc-name NAME [--existing-path FILE]\n"
|
||||||
<< " plan-export-target --kind file|collection|stem|name --doc-name NAME [--work-dir DIR] [--directory DIR] [--extension EXT] [--suffix SUFFIX]\n"
|
<< " plan-export-target --kind file|collection|stem|name --doc-name NAME [--work-dir DIR] [--directory DIR] [--extension EXT] [--suffix SUFFIX]\n"
|
||||||
@@ -1221,6 +1243,60 @@ int classify_open(int argc, char** argv)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pp::foundation::Status parse_plan_open_route_args(
|
||||||
|
int argc,
|
||||||
|
char** argv,
|
||||||
|
PlanOpenRouteArgs& args)
|
||||||
|
{
|
||||||
|
for (int i = 2; i < argc; ++i) {
|
||||||
|
const std::string_view key(argv[i]);
|
||||||
|
if (key == "--path") {
|
||||||
|
if (i + 1 >= argc) {
|
||||||
|
return pp::foundation::Status::invalid_argument("missing value for option");
|
||||||
|
}
|
||||||
|
args.path = argv[++i];
|
||||||
|
} else if (key == "--unsaved") {
|
||||||
|
args.unsaved = true;
|
||||||
|
} else {
|
||||||
|
return pp::foundation::Status::invalid_argument("unknown option");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (args.path.empty()) {
|
||||||
|
return pp::foundation::Status::invalid_argument("path must not be empty");
|
||||||
|
}
|
||||||
|
|
||||||
|
return pp::foundation::Status::success();
|
||||||
|
}
|
||||||
|
|
||||||
|
int plan_open_route(int argc, char** argv)
|
||||||
|
{
|
||||||
|
PlanOpenRouteArgs args;
|
||||||
|
const auto status = parse_plan_open_route_args(argc, argv, args);
|
||||||
|
if (!status.ok()) {
|
||||||
|
print_error("plan-open-route", status.message);
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto route = pp::app::classify_document_open_path(args.path);
|
||||||
|
if (!route) {
|
||||||
|
print_error("plan-open-route", route.status().message);
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto action = pp::app::plan_document_open(route.value().kind, args.unsaved);
|
||||||
|
std::cout << "{\"ok\":true,\"command\":\"plan-open-route\""
|
||||||
|
<< ",\"route\":{\"kind\":\"" << document_open_kind_name(route.value().kind)
|
||||||
|
<< "\",\"path\":\"" << json_escape(route.value().path)
|
||||||
|
<< "\",\"directory\":\"" << json_escape(route.value().directory)
|
||||||
|
<< "\",\"name\":\"" << json_escape(route.value().name)
|
||||||
|
<< "\",\"extension\":\"" << json_escape(route.value().extension)
|
||||||
|
<< "\"},\"state\":{\"unsaved\":" << json_bool(args.unsaved)
|
||||||
|
<< "},\"plan\":{\"action\":\"" << document_open_plan_action_name(action)
|
||||||
|
<< "\"}}\n";
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
pp::foundation::Status parse_plan_document_file_args(
|
pp::foundation::Status parse_plan_document_file_args(
|
||||||
int argc,
|
int argc,
|
||||||
char** argv,
|
char** argv,
|
||||||
@@ -3475,6 +3551,10 @@ int main(int argc, char** argv)
|
|||||||
return classify_open(argc, argv);
|
return classify_open(argc, argv);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (command == "plan-open-route") {
|
||||||
|
return plan_open_route(argc, argv);
|
||||||
|
}
|
||||||
|
|
||||||
if (command == "plan-document-file") {
|
if (command == "plan-document-file") {
|
||||||
return plan_document_file(argc, argv);
|
return plan_document_file(argc, argv);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user