/*** Oak - Non-Linear Video Editor Copyright (C) 2026 Oak Team This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . ***/ #ifndef OAKENGINE_PROJECT_H #define OAKENGINE_PROJECT_H #include "export.h" #include "init.h" #ifdef __cplusplus extern "C" { #endif /** * @file project.h * @brief C ABI for Oak project files (.ove) * * An OakEngineProject wraps the engine's olive::Project node graph. Projects * are created empty with oakengine_project_create() and must then be given * content exactly once: either oakengine_project_new() for a blank project or * oakengine_project_load() to read a project file (the engine's project * serializers require a fresh, uninitialized project, so loading into a * project that already has content is rejected with OAKENGINE_E_STATE). * * Sequences are owned by their project through Qt's QObject parent chain; * OakEngineSequence handles are borrowed views whose lifetime follows the * project. See timeline.h for the sequence function family. * * Conventions (matching oakengine/ipc.h): * - Booleans are int (1/0). * - Return codes: 0 (OAKENGINE_OK) on success, negative OAKENGINE_E_* on * failure. * - String output uses the buf/size convention: the return value is the * number of characters that would have been written excluding the NUL, * so buf == NULL or a short buffer queries the required size. The output * is NUL-terminated whenever buf_size > 0. A negative return value is an * OAKENGINE_E_* error code. * - NULL handles are accepted everywhere and yield a no-op / zero result / * OAKENGINE_E_INVALID. * * Undo/redo note: the engine's undo stack is a process-wide singleton held by * EngineCore (EngineCore::undo_stack()), not a per-project object. The * project_undo/project_redo family therefore operates on that global stack * and is not scoped to `self`; oakengine_project_new() and * oakengine_project_load() clear it, matching the application's * new/open-project behavior. */ /** * @brief Opaque project handle. Owned by the caller; release with * oakengine_project_free(). */ typedef struct OakEngineProject OakEngineProject; /** * @brief Opaque sequence (timeline) handle. * * Typedef'd here so project.h and timeline.h can be included in any order. * Handles are borrowed from their owning OakEngineProject (QObject parent * chain) and must NOT be freed; they become invalid when the project is * freed. The sequence function family lives in oakengine/timeline.h. */ typedef struct OakEngineSequence OakEngineSequence; /** * @brief Allocate an empty project shell. Owned by the caller. * * The project has no content until exactly one of oakengine_project_new() or * oakengine_project_load() succeeds on it. */ OAKENGINE_API OakEngineProject *oakengine_project_create(void); /** * @brief Destroy a project and everything it owns (including sequences). * NULL-safe. */ OAKENGINE_API void oakengine_project_free(OakEngineProject *self); /** * @brief Initialize `self` as a new, blank project (untitled, not modified). * * Clears the global undo stack. Returns OAKENGINE_E_STATE if the project * already has content (new/load may only be applied once). */ OAKENGINE_API int oakengine_project_new(OakEngineProject *self); /** * @brief Load project content from a .ove file. * * On success the project's filename is set to `path` and the modified flag is * cleared; the global undo stack is cleared. Footage is validated like the * engine does (EngineCore::validate_footage_in_loaded_project): files that * moved together with the project are resolved relatively; missing footage is * tolerated (no relink UI exists at this layer, so the project is accepted * as-is). On failure a human-readable reason is written to `err` using the * buf/size convention (err may be NULL) and a negative code is returned. */ OAKENGINE_API int oakengine_project_load(OakEngineProject *self, const char *path, char *err, int err_size); /** * @brief Save the project to `path`. * * `path` == NULL saves under the project's current filename * (Project::filename()); saving an untitled project without a path fails with * OAKENGINE_E_INVALID. Files are gzip-compressed unless the target name ends * in ".ovexml" (mirrors the application behavior). On success the project's * filename is set to the target and the modified flag is cleared * (EngineCore::on_project_saved() semantics, minus the UI recent-list). */ OAKENGINE_API int oakengine_project_save(OakEngineProject *self, const char *path); OAKENGINE_API int oakengine_project_is_modified(const OakEngineProject *self); /** * @brief Set the project modified flag (Project::set_modified). */ OAKENGINE_API int oakengine_project_set_modified(OakEngineProject *self, int modified); /** * @brief Project display name (Project::name(): the filename's base name, or * "(untitled)"). Uses the buf/size convention. */ OAKENGINE_API int oakengine_project_name(const OakEngineProject *self, char *buf, int buf_size); /** * @brief Full path the project was loaded from / saved to, or "" if untitled * (Project::filename()). Uses the buf/size convention. */ OAKENGINE_API int oakengine_project_filename(const OakEngineProject *self, char *buf, int buf_size); /** * @brief Number of footage items in the project. */ OAKENGINE_API int oakengine_project_footage_count(const OakEngineProject *self); /** * @brief Stored filename of the footage item at `index` (Footage::filename(), * as recorded in the project file; may be relative). Uses the buf/size * convention; returns OAKENGINE_E_NOT_FOUND for an out-of-range index. */ OAKENGINE_API int oakengine_project_footage_filename( const OakEngineProject *self, int index, char *buf, int buf_size); /** * @brief 1 if the footage file at `index` exists on disk, 0 if not. * * Mirrors the engine's validation rule: the stored path is checked as-is * first, then resolved relative to the project file's directory (footage * that moved together with the project stays online). Returns * OAKENGINE_E_NOT_FOUND for an out-of-range index. */ OAKENGINE_API int oakengine_project_footage_is_online(const OakEngineProject *self, int index); /* ---- Undo (global singleton stack, see the file comment above) ---------- */ OAKENGINE_API int oakengine_project_can_undo(const OakEngineProject *self); OAKENGINE_API int oakengine_project_can_redo(const OakEngineProject *self); OAKENGINE_API int oakengine_project_undo(OakEngineProject *self); OAKENGINE_API int oakengine_project_redo(OakEngineProject *self); /** * @brief Number of sequences (timelines) in the project. */ OAKENGINE_API int oakengine_project_sequence_count(const OakEngineProject *self); /** * @brief Borrowed handle of the sequence at `index`, or NULL when out of * range. Do NOT free; the sequence is owned by the project. */ OAKENGINE_API OakEngineSequence * oakengine_project_sequence_at(const OakEngineProject *self, int index); #ifdef __cplusplus } #endif #endif /* OAKENGINE_PROJECT_H */