460 lines
16 KiB
C++
460 lines
16 KiB
C++
#pragma once
|
|
|
|
#include "foundation/result.h"
|
|
|
|
#include <string>
|
|
#include <string_view>
|
|
#include <utility>
|
|
|
|
namespace pp::app {
|
|
|
|
struct DocumentExportFileTarget {
|
|
std::string path;
|
|
std::string suggested_name;
|
|
};
|
|
|
|
struct DocumentExportCollectionTarget {
|
|
std::string directory;
|
|
std::string stem_path;
|
|
};
|
|
|
|
struct DocumentExportStemTarget {
|
|
std::string stem_path;
|
|
};
|
|
|
|
struct DocumentExportSuggestedName {
|
|
std::string name;
|
|
};
|
|
|
|
enum class DocumentExportCollectionDestination {
|
|
work_directory_collection,
|
|
picked_directory_stem,
|
|
};
|
|
|
|
enum class DocumentExportStartDecision {
|
|
start_now,
|
|
show_license_disabled,
|
|
unavailable_no_canvas,
|
|
};
|
|
|
|
enum class DocumentExportMenuKind {
|
|
jpeg,
|
|
png,
|
|
layers,
|
|
cube_faces,
|
|
depth,
|
|
animation_frames,
|
|
animation_mp4,
|
|
timelapse,
|
|
};
|
|
|
|
enum class DocumentExportMenuAction {
|
|
show_jpeg_dialog,
|
|
show_png_dialog,
|
|
show_layers_dialog,
|
|
show_cube_faces_dialog,
|
|
show_depth_dialog,
|
|
show_animation_frames_dialog,
|
|
show_animation_mp4_dialog,
|
|
show_timelapse_dialog,
|
|
show_license_disabled,
|
|
unavailable_no_canvas,
|
|
};
|
|
|
|
enum class DocumentExportCollectionKind {
|
|
layers,
|
|
animation_frames,
|
|
};
|
|
|
|
enum class DocumentVideoExportKind {
|
|
animation_mp4,
|
|
timelapse,
|
|
};
|
|
|
|
struct DocumentExportMenuPlan {
|
|
DocumentExportMenuKind kind = DocumentExportMenuKind::jpeg;
|
|
DocumentExportMenuAction action = DocumentExportMenuAction::show_jpeg_dialog;
|
|
bool opens_dialog = true;
|
|
};
|
|
|
|
struct DocumentExportCollectionTargetPlan {
|
|
DocumentExportCollectionKind kind = DocumentExportCollectionKind::layers;
|
|
DocumentExportCollectionDestination destination = DocumentExportCollectionDestination::picked_directory_stem;
|
|
std::string_view suffix;
|
|
};
|
|
|
|
class DocumentExportMenuServices {
|
|
public:
|
|
virtual ~DocumentExportMenuServices() = default;
|
|
|
|
virtual void show_jpeg_dialog() = 0;
|
|
virtual void show_png_dialog() = 0;
|
|
virtual void show_layers_dialog() = 0;
|
|
virtual void show_cube_faces_dialog() = 0;
|
|
virtual void show_depth_dialog() = 0;
|
|
virtual void show_animation_frames_dialog() = 0;
|
|
virtual void show_animation_mp4_dialog() = 0;
|
|
virtual void show_timelapse_dialog() = 0;
|
|
virtual void show_license_disabled() = 0;
|
|
};
|
|
|
|
class DocumentExportServices {
|
|
public:
|
|
virtual ~DocumentExportServices() = default;
|
|
|
|
virtual bool create_directory(std::string_view directory) = 0;
|
|
virtual void export_equirectangular(const DocumentExportFileTarget& target) = 0;
|
|
virtual void export_layers_to_stem(const DocumentExportStemTarget& target) = 0;
|
|
virtual void export_layers_to_collection(const DocumentExportCollectionTarget& target) = 0;
|
|
virtual void export_animation_frames_to_stem(const DocumentExportStemTarget& target) = 0;
|
|
virtual void export_animation_frames_to_collection(const DocumentExportCollectionTarget& target) = 0;
|
|
virtual void export_depth(std::string_view document_name) = 0;
|
|
virtual void export_cube_faces(std::string_view document_name) = 0;
|
|
};
|
|
|
|
class DocumentVideoExportServices {
|
|
public:
|
|
virtual ~DocumentVideoExportServices() = default;
|
|
|
|
virtual void export_animation_mp4(std::string_view path) = 0;
|
|
virtual void export_timelapse_mp4(std::string_view path) = 0;
|
|
virtual void show_animation_export_success(std::string_view path) = 0;
|
|
virtual void show_timelapse_export_success(std::string_view path) = 0;
|
|
};
|
|
|
|
[[nodiscard]] constexpr DocumentExportStartDecision plan_document_export_start(
|
|
bool requires_license,
|
|
bool license_valid,
|
|
bool has_canvas) noexcept
|
|
{
|
|
if (requires_license && !license_valid) {
|
|
return DocumentExportStartDecision::show_license_disabled;
|
|
}
|
|
|
|
return has_canvas
|
|
? DocumentExportStartDecision::start_now
|
|
: DocumentExportStartDecision::unavailable_no_canvas;
|
|
}
|
|
|
|
[[nodiscard]] constexpr bool document_export_menu_requires_license(
|
|
DocumentExportMenuKind kind) noexcept
|
|
{
|
|
switch (kind) {
|
|
case DocumentExportMenuKind::animation_mp4:
|
|
case DocumentExportMenuKind::timelapse:
|
|
return true;
|
|
case DocumentExportMenuKind::jpeg:
|
|
case DocumentExportMenuKind::png:
|
|
case DocumentExportMenuKind::layers:
|
|
case DocumentExportMenuKind::cube_faces:
|
|
case DocumentExportMenuKind::depth:
|
|
case DocumentExportMenuKind::animation_frames:
|
|
return false;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
[[nodiscard]] constexpr DocumentExportMenuAction document_export_menu_dialog_action(
|
|
DocumentExportMenuKind kind) noexcept
|
|
{
|
|
switch (kind) {
|
|
case DocumentExportMenuKind::jpeg:
|
|
return DocumentExportMenuAction::show_jpeg_dialog;
|
|
case DocumentExportMenuKind::png:
|
|
return DocumentExportMenuAction::show_png_dialog;
|
|
case DocumentExportMenuKind::layers:
|
|
return DocumentExportMenuAction::show_layers_dialog;
|
|
case DocumentExportMenuKind::cube_faces:
|
|
return DocumentExportMenuAction::show_cube_faces_dialog;
|
|
case DocumentExportMenuKind::depth:
|
|
return DocumentExportMenuAction::show_depth_dialog;
|
|
case DocumentExportMenuKind::animation_frames:
|
|
return DocumentExportMenuAction::show_animation_frames_dialog;
|
|
case DocumentExportMenuKind::animation_mp4:
|
|
return DocumentExportMenuAction::show_animation_mp4_dialog;
|
|
case DocumentExportMenuKind::timelapse:
|
|
return DocumentExportMenuAction::show_timelapse_dialog;
|
|
}
|
|
|
|
return DocumentExportMenuAction::show_jpeg_dialog;
|
|
}
|
|
|
|
[[nodiscard]] constexpr DocumentExportMenuPlan plan_document_export_menu_action(
|
|
DocumentExportMenuKind kind,
|
|
bool has_canvas,
|
|
bool license_valid) noexcept
|
|
{
|
|
DocumentExportMenuPlan plan;
|
|
plan.kind = kind;
|
|
plan.action = document_export_menu_dialog_action(kind);
|
|
|
|
const auto start = plan_document_export_start(
|
|
document_export_menu_requires_license(kind),
|
|
license_valid,
|
|
has_canvas);
|
|
if (start == DocumentExportStartDecision::show_license_disabled) {
|
|
plan.action = DocumentExportMenuAction::show_license_disabled;
|
|
plan.opens_dialog = false;
|
|
} else if (start == DocumentExportStartDecision::unavailable_no_canvas) {
|
|
plan.action = DocumentExportMenuAction::unavailable_no_canvas;
|
|
plan.opens_dialog = false;
|
|
}
|
|
|
|
return plan;
|
|
}
|
|
|
|
[[nodiscard]] constexpr std::string_view document_export_collection_suffix(
|
|
DocumentExportCollectionKind kind) noexcept
|
|
{
|
|
switch (kind) {
|
|
case DocumentExportCollectionKind::layers:
|
|
return "_layers";
|
|
case DocumentExportCollectionKind::animation_frames:
|
|
return "_frames";
|
|
}
|
|
|
|
return {};
|
|
}
|
|
|
|
[[nodiscard]] constexpr DocumentExportCollectionTargetPlan plan_document_export_collection_target(
|
|
DocumentExportCollectionKind kind,
|
|
bool use_work_directory_collection) noexcept
|
|
{
|
|
return {
|
|
kind,
|
|
use_work_directory_collection
|
|
? DocumentExportCollectionDestination::work_directory_collection
|
|
: DocumentExportCollectionDestination::picked_directory_stem,
|
|
document_export_collection_suffix(kind),
|
|
};
|
|
}
|
|
|
|
[[nodiscard]] inline pp::foundation::Result<DocumentExportFileTarget> make_document_export_file_target(
|
|
std::string_view work_directory,
|
|
std::string_view document_name,
|
|
std::string_view extension)
|
|
{
|
|
if (document_name.empty()) {
|
|
return pp::foundation::Result<DocumentExportFileTarget>::failure(
|
|
pp::foundation::Status::invalid_argument("document name must not be empty"));
|
|
}
|
|
|
|
if (extension.empty()) {
|
|
return pp::foundation::Result<DocumentExportFileTarget>::failure(
|
|
pp::foundation::Status::invalid_argument("extension must not be empty"));
|
|
}
|
|
|
|
DocumentExportFileTarget target;
|
|
target.suggested_name.reserve(document_name.size() + extension.size());
|
|
target.suggested_name += document_name;
|
|
target.suggested_name += extension;
|
|
target.path.reserve(work_directory.size() + target.suggested_name.size() + 1);
|
|
target.path += work_directory;
|
|
target.path += "/";
|
|
target.path += target.suggested_name;
|
|
return pp::foundation::Result<DocumentExportFileTarget>::success(std::move(target));
|
|
}
|
|
|
|
[[nodiscard]] inline pp::foundation::Result<DocumentExportCollectionTarget> make_document_export_collection_target(
|
|
std::string_view work_directory,
|
|
std::string_view document_name,
|
|
std::string_view suffix)
|
|
{
|
|
if (document_name.empty()) {
|
|
return pp::foundation::Result<DocumentExportCollectionTarget>::failure(
|
|
pp::foundation::Status::invalid_argument("document name must not be empty"));
|
|
}
|
|
|
|
DocumentExportCollectionTarget target;
|
|
target.directory.reserve(work_directory.size() + document_name.size() + suffix.size() + 1);
|
|
target.directory += work_directory;
|
|
target.directory += "/";
|
|
target.directory += document_name;
|
|
target.directory += suffix;
|
|
target.stem_path.reserve(target.directory.size() + document_name.size() + 1);
|
|
target.stem_path += target.directory;
|
|
target.stem_path += "/";
|
|
target.stem_path += document_name;
|
|
return pp::foundation::Result<DocumentExportCollectionTarget>::success(std::move(target));
|
|
}
|
|
|
|
[[nodiscard]] inline pp::foundation::Result<DocumentExportStemTarget> make_document_export_stem_target(
|
|
std::string_view directory,
|
|
std::string_view document_name)
|
|
{
|
|
if (document_name.empty()) {
|
|
return pp::foundation::Result<DocumentExportStemTarget>::failure(
|
|
pp::foundation::Status::invalid_argument("document name must not be empty"));
|
|
}
|
|
|
|
DocumentExportStemTarget target;
|
|
target.stem_path.reserve(directory.size() + document_name.size() + 1);
|
|
target.stem_path += directory;
|
|
target.stem_path += "/";
|
|
target.stem_path += document_name;
|
|
return pp::foundation::Result<DocumentExportStemTarget>::success(std::move(target));
|
|
}
|
|
|
|
[[nodiscard]] inline pp::foundation::Result<DocumentExportSuggestedName> make_document_export_suggested_name(
|
|
std::string_view document_name,
|
|
std::string_view suffix)
|
|
{
|
|
if (document_name.empty()) {
|
|
return pp::foundation::Result<DocumentExportSuggestedName>::failure(
|
|
pp::foundation::Status::invalid_argument("document name must not be empty"));
|
|
}
|
|
|
|
DocumentExportSuggestedName target;
|
|
target.name.reserve(document_name.size() + suffix.size());
|
|
target.name += document_name;
|
|
target.name += suffix;
|
|
return pp::foundation::Result<DocumentExportSuggestedName>::success(std::move(target));
|
|
}
|
|
|
|
[[nodiscard]] inline pp::foundation::Status execute_document_export_file(
|
|
const DocumentExportFileTarget& target,
|
|
DocumentExportServices& services)
|
|
{
|
|
if (target.path.empty() || target.suggested_name.empty()) {
|
|
return pp::foundation::Status::invalid_argument("export file target requires a path and suggested name");
|
|
}
|
|
|
|
services.export_equirectangular(target);
|
|
return pp::foundation::Status::success();
|
|
}
|
|
|
|
[[nodiscard]] inline pp::foundation::Status execute_document_export_stem(
|
|
DocumentExportCollectionKind kind,
|
|
const DocumentExportStemTarget& target,
|
|
DocumentExportServices& services)
|
|
{
|
|
if (target.stem_path.empty()) {
|
|
return pp::foundation::Status::invalid_argument("export stem target requires a stem path");
|
|
}
|
|
|
|
switch (kind) {
|
|
case DocumentExportCollectionKind::layers:
|
|
services.export_layers_to_stem(target);
|
|
return pp::foundation::Status::success();
|
|
case DocumentExportCollectionKind::animation_frames:
|
|
services.export_animation_frames_to_stem(target);
|
|
return pp::foundation::Status::success();
|
|
}
|
|
|
|
return pp::foundation::Status::invalid_argument("unknown document export collection kind");
|
|
}
|
|
|
|
[[nodiscard]] inline pp::foundation::Status execute_document_export_collection(
|
|
DocumentExportCollectionKind kind,
|
|
const DocumentExportCollectionTarget& target,
|
|
DocumentExportServices& services)
|
|
{
|
|
if (target.directory.empty() || target.stem_path.empty()) {
|
|
return pp::foundation::Status::invalid_argument("export collection target requires a directory and stem path");
|
|
}
|
|
|
|
if (!services.create_directory(target.directory)) {
|
|
return pp::foundation::Status::success();
|
|
}
|
|
|
|
switch (kind) {
|
|
case DocumentExportCollectionKind::layers:
|
|
services.export_layers_to_collection(target);
|
|
return pp::foundation::Status::success();
|
|
case DocumentExportCollectionKind::animation_frames:
|
|
services.export_animation_frames_to_collection(target);
|
|
return pp::foundation::Status::success();
|
|
}
|
|
|
|
return pp::foundation::Status::invalid_argument("unknown document export collection kind");
|
|
}
|
|
|
|
[[nodiscard]] inline pp::foundation::Status execute_document_export_depth(
|
|
std::string_view document_name,
|
|
DocumentExportServices& services)
|
|
{
|
|
if (document_name.empty()) {
|
|
return pp::foundation::Status::invalid_argument("document name must not be empty");
|
|
}
|
|
|
|
services.export_depth(document_name);
|
|
return pp::foundation::Status::success();
|
|
}
|
|
|
|
[[nodiscard]] inline pp::foundation::Status execute_document_export_cube_faces(
|
|
std::string_view document_name,
|
|
DocumentExportServices& services)
|
|
{
|
|
if (document_name.empty()) {
|
|
return pp::foundation::Status::invalid_argument("document name must not be empty");
|
|
}
|
|
|
|
services.export_cube_faces(document_name);
|
|
return pp::foundation::Status::success();
|
|
}
|
|
|
|
[[nodiscard]] inline pp::foundation::Status execute_document_video_export(
|
|
DocumentVideoExportKind kind,
|
|
std::string_view path,
|
|
DocumentVideoExportServices& services)
|
|
{
|
|
if (path.empty()) {
|
|
return pp::foundation::Status::invalid_argument("video export path must not be empty");
|
|
}
|
|
|
|
switch (kind) {
|
|
case DocumentVideoExportKind::animation_mp4:
|
|
services.export_animation_mp4(path);
|
|
services.show_animation_export_success(path);
|
|
return pp::foundation::Status::success();
|
|
case DocumentVideoExportKind::timelapse:
|
|
services.export_timelapse_mp4(path);
|
|
services.show_timelapse_export_success(path);
|
|
return pp::foundation::Status::success();
|
|
}
|
|
|
|
return pp::foundation::Status::invalid_argument("unknown document video export kind");
|
|
}
|
|
|
|
[[nodiscard]] inline pp::foundation::Status execute_document_export_menu_plan(
|
|
const DocumentExportMenuPlan& plan,
|
|
DocumentExportMenuServices& services)
|
|
{
|
|
switch (plan.action) {
|
|
case DocumentExportMenuAction::show_jpeg_dialog:
|
|
services.show_jpeg_dialog();
|
|
return pp::foundation::Status::success();
|
|
case DocumentExportMenuAction::show_png_dialog:
|
|
services.show_png_dialog();
|
|
return pp::foundation::Status::success();
|
|
case DocumentExportMenuAction::show_layers_dialog:
|
|
services.show_layers_dialog();
|
|
return pp::foundation::Status::success();
|
|
case DocumentExportMenuAction::show_cube_faces_dialog:
|
|
services.show_cube_faces_dialog();
|
|
return pp::foundation::Status::success();
|
|
case DocumentExportMenuAction::show_depth_dialog:
|
|
services.show_depth_dialog();
|
|
return pp::foundation::Status::success();
|
|
case DocumentExportMenuAction::show_animation_frames_dialog:
|
|
services.show_animation_frames_dialog();
|
|
return pp::foundation::Status::success();
|
|
case DocumentExportMenuAction::show_animation_mp4_dialog:
|
|
services.show_animation_mp4_dialog();
|
|
return pp::foundation::Status::success();
|
|
case DocumentExportMenuAction::show_timelapse_dialog:
|
|
services.show_timelapse_dialog();
|
|
return pp::foundation::Status::success();
|
|
case DocumentExportMenuAction::show_license_disabled:
|
|
services.show_license_disabled();
|
|
return pp::foundation::Status::success();
|
|
case DocumentExportMenuAction::unavailable_no_canvas:
|
|
return pp::foundation::Status::success();
|
|
}
|
|
|
|
return pp::foundation::Status::invalid_argument("unknown document export menu action");
|
|
}
|
|
|
|
}
|