refactor: split Core into EngineCore (engine) and Core (UI)
EngineCore (new app/coreengine.{h,cpp}) owns every engine-safe part of
the old Core singleton: CoreParams, lifecycle of the engine managers,
UndoStack, tool/snapping/timecode state, locale, autorecovery, recent
projects, footage filters, clipboard, project registry, type
declarations, and the proxy toggle. UI dependencies are inverted
through hooks instead: status-bar/cache-full signals and std::function
handlers for image-sequence confirmation, footage relink, OTIO import,
project save/close and layout load (same pattern as
Config::ErrorHandler).
Core (app/) now derives from EngineCore and keeps only UI behavior:
the main window, dialogs, panel heuristics, import/export flows and
project lifecycle presentation. Its public API is unchanged (all
inherited), and Core::instance() covariantly static_casts the engine
singleton. The render worker constructs EngineCore directly, making it
the first binary that no longer needs the UI side of Core.
~25 engine call sites move from core.h to coreengine.h; a dozen more
drop a vestigial core.h include (gaining direct includes for symbols
they were borrowing transitively). Full gtest suite green (1986 tests,
0 failures).
This commit is contained in:
@@ -19,6 +19,8 @@
|
|||||||
set(OLIVE_SOURCES
|
set(OLIVE_SOURCES
|
||||||
core.h
|
core.h
|
||||||
core.cpp
|
core.cpp
|
||||||
|
coreengine.h
|
||||||
|
coreengine.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
#set(OLIVE_RESOURCES)
|
#set(OLIVE_RESOURCES)
|
||||||
|
|||||||
@@ -32,7 +32,6 @@
|
|||||||
#include "common/dropworkflowbehavior.h"
|
#include "common/dropworkflowbehavior.h"
|
||||||
#include "common/filefunctions.h"
|
#include "common/filefunctions.h"
|
||||||
#include "common/xmlutils.h"
|
#include "common/xmlutils.h"
|
||||||
#include "core.h"
|
|
||||||
#include "timeline/timelinecommon.h"
|
#include "timeline/timelinecommon.h"
|
||||||
#include "ui/colorcoding.h"
|
#include "ui/colorcoding.h"
|
||||||
|
|
||||||
|
|||||||
+8
-2
@@ -56,6 +56,14 @@ public:
|
|||||||
|
|
||||||
static void set_error_handler(ErrorHandler handler);
|
static void set_error_handler(ErrorHandler handler);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Report an error through the registered error handler
|
||||||
|
*
|
||||||
|
* Public so engine-layer code (e.g. EngineCore) can surface errors to
|
||||||
|
* the user without depending on the UI itself.
|
||||||
|
*/
|
||||||
|
static void report_error(const QString &title, const QString &message);
|
||||||
|
|
||||||
QVariant operator[](const QString &) const;
|
QVariant operator[](const QString &) const;
|
||||||
|
|
||||||
QVariant &operator[](const QString &);
|
QVariant &operator[](const QString &);
|
||||||
@@ -79,8 +87,6 @@ private:
|
|||||||
|
|
||||||
static ErrorHandler error_handler_;
|
static ErrorHandler error_handler_;
|
||||||
|
|
||||||
static void report_error(const QString &title, const QString &message);
|
|
||||||
|
|
||||||
static QString get_config_file_path();
|
static QString get_config_file_path();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
+70
-801
File diff suppressed because it is too large
Load Diff
+101
-464
@@ -22,18 +22,7 @@
|
|||||||
#ifndef OAK_CORE_H
|
#ifndef OAK_CORE_H
|
||||||
#define OAK_CORE_H
|
#define OAK_CORE_H
|
||||||
|
|
||||||
#include <olive/core/core.h>
|
#include "coreengine.h"
|
||||||
#include <QFileInfoList>
|
|
||||||
#include <QList>
|
|
||||||
#include <QTimer>
|
|
||||||
#include <QTranslator>
|
|
||||||
|
|
||||||
#include "node/project/footage/footage.h"
|
|
||||||
#include "node/project.h"
|
|
||||||
#include "node/project/sequence/sequence.h"
|
|
||||||
#include "task/task.h"
|
|
||||||
#include "tool/tool.h"
|
|
||||||
#include "undo/undostack.h"
|
|
||||||
|
|
||||||
namespace olive
|
namespace olive
|
||||||
{
|
{
|
||||||
@@ -43,257 +32,104 @@ class MainWindow;
|
|||||||
/**
|
/**
|
||||||
* @brief The main central Olive application instance_
|
* @brief The main central Olive application instance_
|
||||||
*
|
*
|
||||||
* This runs both in GUI and CLI modes (and handles what to init based on that).
|
* This is the UI-facing derivation of EngineCore. It runs both in GUI and
|
||||||
* It also contains various global functions/variables for use throughout Olive.
|
* CLI modes (and handles what to init based on that). All UI-independent
|
||||||
|
* engine state lives in the base class EngineCore; this class adds the main
|
||||||
|
* window, dialogs and other user interaction on top of it.
|
||||||
*
|
*
|
||||||
* The "public slots" are usually user-triggered actions and can be connected to UI elements (e.g. creating a folder,
|
* The "public slots" are usually user-triggered actions and can be connected to UI elements (e.g. creating a folder,
|
||||||
* opening the import dialog, etc.)
|
* opening the import dialog, etc.)
|
||||||
*/
|
*/
|
||||||
class Core : public QObject {
|
class Core : public EngineCore {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
class CoreParams {
|
|
||||||
public:
|
|
||||||
CoreParams();
|
|
||||||
|
|
||||||
enum RunMode { k_run_normal, k_headless_export, k_headless_pre_cache };
|
|
||||||
|
|
||||||
bool fullscreen() const
|
|
||||||
{
|
|
||||||
return run_fullscreen_;
|
|
||||||
}
|
|
||||||
|
|
||||||
void set_fullscreen(bool e)
|
|
||||||
{
|
|
||||||
run_fullscreen_ = e;
|
|
||||||
}
|
|
||||||
|
|
||||||
RunMode run_mode() const
|
|
||||||
{
|
|
||||||
return mode_;
|
|
||||||
}
|
|
||||||
|
|
||||||
void set_run_mode(RunMode m)
|
|
||||||
{
|
|
||||||
mode_ = m;
|
|
||||||
}
|
|
||||||
|
|
||||||
const QString startup_project() const
|
|
||||||
{
|
|
||||||
return startup_project_;
|
|
||||||
}
|
|
||||||
|
|
||||||
void set_startup_project(const QString &p)
|
|
||||||
{
|
|
||||||
startup_project_ = p;
|
|
||||||
}
|
|
||||||
|
|
||||||
const QString &startup_language() const
|
|
||||||
{
|
|
||||||
return startup_language_;
|
|
||||||
}
|
|
||||||
|
|
||||||
void set_startup_language(const QString &s)
|
|
||||||
{
|
|
||||||
startup_language_ = s;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool crash_on_startup() const
|
|
||||||
{
|
|
||||||
return crash_;
|
|
||||||
}
|
|
||||||
|
|
||||||
void set_crash_on_startup(bool e)
|
|
||||||
{
|
|
||||||
crash_ = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
RunMode mode_;
|
|
||||||
|
|
||||||
QString startup_project_;
|
|
||||||
|
|
||||||
QString startup_language_;
|
|
||||||
|
|
||||||
bool run_fullscreen_;
|
|
||||||
|
|
||||||
bool crash_;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Core Constructor
|
* @brief Core Constructor
|
||||||
*
|
*
|
||||||
* Currently empty
|
* Registers the UI handlers that EngineCore uses to request user
|
||||||
*/
|
* interaction.
|
||||||
|
*/
|
||||||
Core(const CoreParams ¶ms);
|
Core(const CoreParams ¶ms);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Core object accessible from anywhere in the code
|
* @brief Core object accessible from anywhere in the code
|
||||||
*
|
*
|
||||||
* Use this to access Core functions.
|
* Use this to access Core functions. This is simply EngineCore::instance()
|
||||||
*/
|
* cast to Core, which is safe because the application entry point (main())
|
||||||
static Core *instance();
|
* always constructs a Core.
|
||||||
|
*/
|
||||||
static QString footage_file_dialog_filter();
|
static Core *instance()
|
||||||
static QStringList allowed_footage_extensions();
|
|
||||||
static bool is_footage_extension_allowed(const QString &path);
|
|
||||||
|
|
||||||
const CoreParams &core_params() const
|
|
||||||
{
|
{
|
||||||
return core_params_;
|
return static_cast<Core *>(EngineCore::instance());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Start Olive Core
|
* @brief Start Olive Core
|
||||||
*
|
*
|
||||||
* Main application launcher. Parses command line arguments and constructs main window (if entering a GUI mode).
|
* Main application launcher. Starts the engine first, then the GUI (if entering a GUI mode).
|
||||||
*/
|
*/
|
||||||
void start();
|
void start();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Stop Olive Core
|
* @brief Stop Olive Core
|
||||||
*
|
*
|
||||||
* Ends all threads and frees all memory ready for the application to exit.
|
* Tears down the UI services first, then the engine, ready for the application to exit.
|
||||||
*/
|
*/
|
||||||
void stop();
|
void stop();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Retrieve main window instance_
|
* @brief Retrieve main window instance_
|
||||||
*
|
*
|
||||||
* @return
|
* @return
|
||||||
*
|
*
|
||||||
* Pointer to the olive::MainWindow object, or nullptr if running in CLI mode.
|
* Pointer to the olive::MainWindow object, or nullptr if running in CLI mode.
|
||||||
*/
|
*/
|
||||||
MainWindow *main_window();
|
MainWindow *main_window();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Retrieve UndoStack object
|
* @brief Import a list of files
|
||||||
*/
|
*
|
||||||
UndoStack *undo_stack();
|
* FIXME: I kind of hate this, it needs a model to update correctly. Is there a way that Items can signal enough to
|
||||||
|
* make passing references to the model unnecessary?
|
||||||
/**
|
*
|
||||||
* @brief Import a list of files
|
* @param urls
|
||||||
*
|
*/
|
||||||
* FIXME: I kind of hate this, it needs a model to update correctly. Is there a way that Items can signal enough to
|
|
||||||
* make passing references to the model unnecessary?
|
|
||||||
*
|
|
||||||
* @param urls
|
|
||||||
*/
|
|
||||||
void import_files(const QStringList &urls, Folder *parent);
|
void import_files(const QStringList &urls, Folder *parent);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Get the currently active tool
|
* @brief Get the currently active project
|
||||||
*/
|
*
|
||||||
const Tool::Item &tool() const;
|
* Uses the UI/Panel system to determine which Project was the last focused on and assumes this is the active Project
|
||||||
|
* that the user wishes to work on.
|
||||||
/**
|
*
|
||||||
* @brief Get the currently selected object that the add tool should make (if the add tool is active)
|
* @return
|
||||||
*/
|
*
|
||||||
const Tool::AddableObject &get_selected_addable_object() const;
|
* The active Project file, or nullptr if the heuristic couldn't find one.
|
||||||
|
*/
|
||||||
/**
|
|
||||||
* @brief Get the currently selected node that the transition tool should make (if the transition tool is active)
|
|
||||||
*/
|
|
||||||
const QString &get_selected_transition() const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Get current snapping value
|
|
||||||
*/
|
|
||||||
const bool &snapping() const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Returns a list of the most recently opened/saved projects
|
|
||||||
*/
|
|
||||||
const QStringList &get_recent_projects() const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Get the currently active project
|
|
||||||
*
|
|
||||||
* Uses the UI/Panel system to determine which Project was the last focused on and assumes this is the active Project
|
|
||||||
* that the user wishes to work on.
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
*
|
|
||||||
* The active Project file, or nullptr if the heuristic couldn't find one.
|
|
||||||
*/
|
|
||||||
Project *get_active_project() const;
|
Project *get_active_project() const;
|
||||||
Folder *get_selected_folder_in_active_project() const;
|
Folder *get_selected_folder_in_active_project() const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Gets current timecode display mode
|
* @brief Show a dialog to the user to rename a set of nodes
|
||||||
*/
|
*/
|
||||||
Timecode::Display get_timecode_display() const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Sets current timecode display mode
|
|
||||||
*/
|
|
||||||
void set_timecode_display(Timecode::Display d);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Set how frequently an autorecovery should be saved (if the project has changed, see SetProjectModified())
|
|
||||||
*/
|
|
||||||
void set_autorecovery_interval(int minutes);
|
|
||||||
|
|
||||||
static void copy_string_to_clipboard(const QString &s);
|
|
||||||
|
|
||||||
static QString paste_string_from_clipboard();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Recursively count files in a file/directory list
|
|
||||||
*/
|
|
||||||
static int count_files_in_file_list(const QFileInfoList &filenames);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Show a dialog to the user to rename a set of nodes
|
|
||||||
*/
|
|
||||||
bool label_nodes(const QVector<Node *> &nodes,
|
bool label_nodes(const QVector<Node *> &nodes,
|
||||||
MultiUndoCommand *parent = nullptr);
|
MultiUndoCommand *parent = nullptr);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Create a new sequence named appropriately for the active project
|
* @brief Opens a project from the recently opened list
|
||||||
*/
|
*/
|
||||||
static Sequence *create_new_sequence_for_project(const QString &format,
|
|
||||||
Project *project);
|
|
||||||
static Sequence *create_new_sequence_for_project(Project *project)
|
|
||||||
{
|
|
||||||
return create_new_sequence_for_project(tr("Sequence %1"), project);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Opens a project from the recently opened list
|
|
||||||
*/
|
|
||||||
void open_project_from_recent_list(int index);
|
void open_project_from_recent_list(int index);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Closes a project
|
* @brief Closes a project
|
||||||
*/
|
*/
|
||||||
bool close_project(bool auto_open_new, bool ignore_modified = false);
|
bool close_project(bool auto_open_new, bool ignore_modified = false);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Runs a modal cache task on the currently active sequence
|
* @brief Runs a modal cache task on the currently active sequence
|
||||||
*/
|
*/
|
||||||
void cache_active_sequence(bool in_out_only);
|
void cache_active_sequence(bool in_out_only);
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Check each footage object for whether it still exists or has changed
|
|
||||||
*/
|
|
||||||
bool validate_footage_in_loaded_project(Project *project,
|
|
||||||
const QString &project_saved_url);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Changes the current language
|
|
||||||
*/
|
|
||||||
bool set_language(const QString &locale);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Show message in main window's status bar
|
|
||||||
*
|
|
||||||
* Shorthand for Core::instance()->main_window()->statusBar()->showMessage();
|
|
||||||
*/
|
|
||||||
void show_status_bar_message(const QString &s, int timeout = 0);
|
|
||||||
|
|
||||||
void clear_status_bar_message();
|
|
||||||
|
|
||||||
void open_recovery_project(const QString &filename);
|
void open_recovery_project(const QString &filename);
|
||||||
|
|
||||||
void open_node_in_viewer(ViewerOutput *viewer);
|
void open_node_in_viewer(ViewerOutput *viewer);
|
||||||
@@ -301,307 +137,112 @@ public:
|
|||||||
void open_export_dialog_for_viewer(ViewerOutput *viewer,
|
void open_export_dialog_for_viewer(ViewerOutput *viewer,
|
||||||
bool start_still_image);
|
bool start_still_image);
|
||||||
|
|
||||||
bool is_magic_enabled() const
|
|
||||||
{
|
|
||||||
return magic_;
|
|
||||||
}
|
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
/**
|
/**
|
||||||
* @brief Starts an open file dialog to load a project from file
|
* @brief Starts an open file dialog to load a project from file
|
||||||
*/
|
*/
|
||||||
void open_project();
|
void open_project();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Saves the current project
|
* @brief Saves the current project
|
||||||
*/
|
*/
|
||||||
bool save_project();
|
bool save_project();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Performs a "save as" on the current project
|
* @brief Performs a "save as" on the current project
|
||||||
*/
|
*/
|
||||||
bool save_project_as();
|
bool save_project_as();
|
||||||
|
|
||||||
void revert_project();
|
void revert_project();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Set the current application-wide tool
|
* @brief Show an About dialog
|
||||||
*
|
*/
|
||||||
* @param tool
|
|
||||||
*/
|
|
||||||
void set_tool(const Tool::Item &tool);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Set the current snapping setting
|
|
||||||
*/
|
|
||||||
void set_snapping(const bool &b);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Globally enable or disable decoding from proxy media
|
|
||||||
*
|
|
||||||
* When disabled, all footage decodes from its original media regardless of
|
|
||||||
* each footage's individual proxy setting. The per-footage settings are
|
|
||||||
* preserved and take effect again when this is re-enabled.
|
|
||||||
*/
|
|
||||||
void set_use_proxy_media(bool enabled);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Show an About dialog
|
|
||||||
*/
|
|
||||||
void dialog_about_show();
|
void dialog_about_show();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Open the import footage dialog and import the files selected (runs ImportFiles())
|
* @brief Open the import footage dialog and import the files selected (runs ImportFiles())
|
||||||
*/
|
*/
|
||||||
void dialog_import_show();
|
void dialog_import_show();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Show Preferences dialog
|
* @brief Show Preferences dialog
|
||||||
*/
|
*/
|
||||||
void dialog_preferences_show(int start_tab = 0);
|
void dialog_preferences_show(int start_tab = 0);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Show Project Properties dialog
|
* @brief Show Project Properties dialog
|
||||||
*/
|
*/
|
||||||
void dialog_project_properties_show();
|
void dialog_project_properties_show();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Show Export dialog
|
* @brief Show Export dialog
|
||||||
*/
|
*/
|
||||||
void dialog_export_show();
|
void dialog_export_show();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Show OTIO import dialog
|
* @brief Show OTIO import dialog
|
||||||
*/
|
*/
|
||||||
#ifdef USE_OTIO
|
#ifdef USE_OTIO
|
||||||
bool DialogImportOTIOShow(const QList<Sequence *> &sequences);
|
bool DialogImportOTIOShow(const QList<Sequence *> &sequences);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Create a new folder in the currently active project
|
* @brief Create a new folder in the currently active project
|
||||||
*/
|
*/
|
||||||
void create_new_folder();
|
void create_new_folder();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Create a new sequence in the currently active project
|
* @brief Create a new sequence in the currently active project
|
||||||
*/
|
*/
|
||||||
void create_new_sequence();
|
void create_new_sequence();
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Set the currently selected object that the add tool should make
|
|
||||||
*/
|
|
||||||
void set_selected_addable_object(const Tool::AddableObject &obj);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Set the currently selected object that the add tool should make
|
|
||||||
*/
|
|
||||||
void set_selected_transition_object(const QString &obj);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Clears the list of recently opened/saved projects
|
|
||||||
*/
|
|
||||||
void clear_open_recent_list();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Creates a new empty project and opens it
|
|
||||||
*/
|
|
||||||
void create_new_project();
|
|
||||||
|
|
||||||
void check_for_auto_recoveries();
|
void check_for_auto_recoveries();
|
||||||
|
|
||||||
void browse_auto_recoveries();
|
void browse_auto_recoveries();
|
||||||
|
|
||||||
void request_pixel_sampling_in_viewers(bool e);
|
|
||||||
|
|
||||||
void warn_cache_full();
|
|
||||||
|
|
||||||
void set_magic(bool e)
|
|
||||||
{
|
|
||||||
magic_ = e;
|
|
||||||
}
|
|
||||||
|
|
||||||
signals:
|
|
||||||
/**
|
|
||||||
* @brief Signal emitted when the tool is changed from somewhere
|
|
||||||
*/
|
|
||||||
void tool_changed(const Tool::Item &tool);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Signal emitted when addable object changes through SetSelectedAddableObject
|
|
||||||
*/
|
|
||||||
void addable_object_changed(Tool::AddableObject o);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Signal emitted when the snapping setting is changed
|
|
||||||
*/
|
|
||||||
void snapping_changed(const bool &b);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Signal emitted when the default timecode display mode changed
|
|
||||||
*/
|
|
||||||
void timecode_display_changed(Timecode::Display d);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Signal emitted when a change is made to the open recent list
|
|
||||||
*/
|
|
||||||
void open_recent_list_changed();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Enable mouse color sampling functionality on all viewers
|
|
||||||
*
|
|
||||||
* This can be slow, so we only turn it on when we need it.
|
|
||||||
*/
|
|
||||||
void color_picker_enabled(bool e);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief A viewer with color picked enabled has emitted a color
|
|
||||||
*/
|
|
||||||
void color_picker_color_emitted(const Color &reference, const Color &display);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/**
|
/**
|
||||||
* @brief Get the file filter than can be used with QFileDialog to open and save compatible projects
|
* @brief Get the file filter than can be used with QFileDialog to open and save compatible projects
|
||||||
*/
|
*/
|
||||||
static QString get_project_filter(bool include_any_filter);
|
static QString get_project_filter(bool include_any_filter);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Returns the filename where the recently opened/saved projects should be stored
|
* @brief Start GUI portion of Olive
|
||||||
*/
|
*
|
||||||
static QString get_recent_projects_file_path();
|
* Starts services and objects required for the GUI of Olive. It's guaranteed that running without this function will
|
||||||
|
* create an application instance_ that is completely valid minus the UI (e.g. for CLI modes).
|
||||||
/**
|
*/
|
||||||
* @brief Called only on startup to set the locale
|
|
||||||
*/
|
|
||||||
void set_startup_locale();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Adds a filename to the top of the recently opened projects list (or moves it if it already exists)
|
|
||||||
*/
|
|
||||||
void push_recently_opened_project(const QString &s);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Declare custom types/classes for Qt's signal/slot system
|
|
||||||
*
|
|
||||||
* Qt's signal/slot system requires types to be declared. In the interest of doing this only at startup, we contain
|
|
||||||
* them all in a function here.
|
|
||||||
*/
|
|
||||||
void declare_types_for_qt();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Start GUI portion of Olive
|
|
||||||
*
|
|
||||||
* Starts services and objects required for the GUI of Olive. It's guaranteed that running without this function will
|
|
||||||
* create an application instance_ that is completely valid minus the UI (e.g. for CLI modes).
|
|
||||||
*/
|
|
||||||
void start_gui(bool full_screen);
|
void start_gui(bool full_screen);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Internal function for saving a project to a file
|
* @brief Internal function for saving a project to a file
|
||||||
*/
|
*/
|
||||||
void save_project_internal(const QString &override_filename = QString());
|
void save_project_internal(const QString &override_filename = QString());
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Retrieves the currently most active sequence for exporting
|
* @brief Retrieves the currently most active sequence for exporting
|
||||||
*/
|
*/
|
||||||
ViewerOutput *get_sequence_to_export();
|
ViewerOutput *get_sequence_to_export();
|
||||||
|
|
||||||
static QString get_auto_recovery_index_filename();
|
|
||||||
|
|
||||||
void save_unrecovered_list();
|
|
||||||
|
|
||||||
bool revert_project_internal(bool by_opening_existing);
|
bool revert_project_internal(bool by_opening_existing);
|
||||||
|
|
||||||
void save_recent_projects_list();
|
/**
|
||||||
|
* @brief Shows the "disk cache full" warning (connected to EngineCore::cache_full_warning_requested)
|
||||||
|
*/
|
||||||
|
void show_cache_full_warning();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Adds a project to the "open projects" list
|
* @brief Applies a new active project to the main window (connected to EngineCore::active_project_changed)
|
||||||
*/
|
*/
|
||||||
void add_open_project(olive::Project *p, bool add_to_recents = false);
|
void on_active_project_changed(Project *p);
|
||||||
|
|
||||||
bool add_open_project_from_task(Task *task, bool add_to_recents);
|
|
||||||
|
|
||||||
void set_active_project(Project *p);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Internal main window object
|
* @brief Internal main window object
|
||||||
*/
|
*/
|
||||||
MainWindow *main_window_;
|
MainWindow *main_window_;
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief List of currently open projects
|
|
||||||
*/
|
|
||||||
Project *open_project_;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Currently active tool
|
|
||||||
*/
|
|
||||||
Tool::Item tool_;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Currently active addable object
|
|
||||||
*/
|
|
||||||
Tool::AddableObject addable_object_;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Currently selected transition
|
|
||||||
*/
|
|
||||||
QString selected_transition_;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Current snapping setting
|
|
||||||
*/
|
|
||||||
bool snapping_;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Internal timer for saving autorecovery files
|
|
||||||
*/
|
|
||||||
QTimer autorecovery_timer_;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Application-wide undo stack instance_
|
|
||||||
*/
|
|
||||||
UndoStack undo_stack_;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief List of most recently opened/saved projects
|
|
||||||
*/
|
|
||||||
QStringList recent_projects_;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Parameters set up in main() determining how the program should run
|
|
||||||
*/
|
|
||||||
CoreParams core_params_;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Static singleton core instance_
|
|
||||||
*/
|
|
||||||
static Core *instance_;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Internal translator
|
|
||||||
*/
|
|
||||||
QTranslator *translator_;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief List of projects that are unsaved but have autorecovery projects
|
|
||||||
*/
|
|
||||||
QVector<QUuid> autorecovered_projects_;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Do something debug related
|
|
||||||
*/
|
|
||||||
bool magic_;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief How many widgets currently need pixel sampling access
|
|
||||||
*/
|
|
||||||
int pixel_sampling_users_;
|
|
||||||
|
|
||||||
bool shown_cache_full_warning_;
|
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void save_autorecovery();
|
|
||||||
|
|
||||||
void project_save_succeeded(Task *task);
|
void project_save_succeeded(Task *task);
|
||||||
|
|
||||||
bool add_open_project_from_task_and_add_to_recents(Task *task)
|
bool add_open_project_from_task_and_add_to_recents(Task *task)
|
||||||
@@ -613,17 +254,13 @@ private slots:
|
|||||||
|
|
||||||
bool confirm_image_sequence(const QString &filename);
|
bool confirm_image_sequence(const QString &filename);
|
||||||
|
|
||||||
void project_was_modified(bool e);
|
|
||||||
|
|
||||||
bool start_headless_export();
|
bool start_headless_export();
|
||||||
|
|
||||||
void open_startup_project();
|
void open_startup_project();
|
||||||
|
|
||||||
void add_recovery_project_from_task(Task *task);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Internal project open
|
* @brief Internal project open
|
||||||
*/
|
*/
|
||||||
void open_project_internal(const QString &filename,
|
void open_project_internal(const QString &filename,
|
||||||
bool recovery_project = false);
|
bool recovery_project = false);
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,924 @@
|
|||||||
|
/***
|
||||||
|
|
||||||
|
Olive - Non-Linear Video Editor
|
||||||
|
Copyright (C) 2022 Olive Team
|
||||||
|
Modifications Copyright (C) 2025 mikesolar
|
||||||
|
|
||||||
|
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 <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
***/
|
||||||
|
|
||||||
|
#include "coreengine.h"
|
||||||
|
|
||||||
|
#include <QClipboard>
|
||||||
|
#include <QDateTime>
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QDir>
|
||||||
|
#include <QFile>
|
||||||
|
#include <QFileInfo>
|
||||||
|
#include <QGuiApplication>
|
||||||
|
#include <QLocale>
|
||||||
|
#include <QStandardPaths>
|
||||||
|
#include <QTextStream>
|
||||||
|
|
||||||
|
#include "audio/audiovisualwaveform.h"
|
||||||
|
#include "codec/conformmanager.h"
|
||||||
|
#include "codec/decoder.h"
|
||||||
|
#include "codec/proxymanager.h"
|
||||||
|
#include "common/filefunctions.h"
|
||||||
|
#include "config/config.h"
|
||||||
|
#include "node/color/colormanager/colormanager.h"
|
||||||
|
#include "node/factory.h"
|
||||||
|
#include "node/project/serializer/serializer.h"
|
||||||
|
#include "render/framemanager.h"
|
||||||
|
#include "render/rendermanager.h"
|
||||||
|
#include "task/project/load/loadbasetask.h"
|
||||||
|
#include "task/taskmanager.h"
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
|
||||||
|
QStringList footage_video_extensions()
|
||||||
|
{
|
||||||
|
return QStringList{
|
||||||
|
QStringLiteral("mp4"), QStringLiteral("mov"), QStringLiteral("m4v"),
|
||||||
|
QStringLiteral("avi"), QStringLiteral("mpg"), QStringLiteral("mpeg"),
|
||||||
|
QStringLiteral("m2ts"), QStringLiteral("mts"), QStringLiteral("ts"),
|
||||||
|
QStringLiteral("webm"), QStringLiteral("wmv"), QStringLiteral("flv"),
|
||||||
|
QStringLiteral("3gp"), QStringLiteral("3g2"), QStringLiteral("mxf")
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList footage_audio_extensions()
|
||||||
|
{
|
||||||
|
return QStringList{ QStringLiteral("wav"), QStringLiteral("mp3"),
|
||||||
|
QStringLiteral("flac"), QStringLiteral("aac"),
|
||||||
|
QStringLiteral("ogg"), QStringLiteral("opus"),
|
||||||
|
QStringLiteral("m4a"), QStringLiteral("alac"),
|
||||||
|
QStringLiteral("aif"), QStringLiteral("aiff"),
|
||||||
|
QStringLiteral("aifc"), QStringLiteral("wma") };
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList footage_image_extensions()
|
||||||
|
{
|
||||||
|
return QStringList{ QStringLiteral("png"), QStringLiteral("jpg"),
|
||||||
|
QStringLiteral("jpeg"), QStringLiteral("tif"),
|
||||||
|
QStringLiteral("tiff"), QStringLiteral("bmp"),
|
||||||
|
QStringLiteral("gif"), QStringLiteral("exr"),
|
||||||
|
QStringLiteral("dpx"), QStringLiteral("webp") };
|
||||||
|
}
|
||||||
|
|
||||||
|
QString build_footage_filter_group(const QString &label,
|
||||||
|
const QStringList &extensions)
|
||||||
|
{
|
||||||
|
QStringList patterns;
|
||||||
|
patterns.reserve(extensions.size());
|
||||||
|
for (const QString &ext : extensions) {
|
||||||
|
patterns.append(QStringLiteral("*.%1").arg(ext));
|
||||||
|
}
|
||||||
|
|
||||||
|
return QStringLiteral("%1 (%2)").arg(label,
|
||||||
|
patterns.join(QLatin1Char(' ')));
|
||||||
|
}
|
||||||
|
|
||||||
|
QString build_footage_file_dialog_filter()
|
||||||
|
{
|
||||||
|
QStringList all = footage_video_extensions() + footage_audio_extensions() +
|
||||||
|
footage_image_extensions();
|
||||||
|
all.removeDuplicates();
|
||||||
|
|
||||||
|
QStringList groups;
|
||||||
|
groups << build_footage_filter_group(QObject::tr("Common Media Files"), all);
|
||||||
|
groups << build_footage_filter_group(QObject::tr("Video Files"),
|
||||||
|
footage_video_extensions());
|
||||||
|
groups << build_footage_filter_group(QObject::tr("Audio Files"),
|
||||||
|
footage_audio_extensions());
|
||||||
|
groups << build_footage_filter_group(QObject::tr("Image Files"),
|
||||||
|
footage_image_extensions());
|
||||||
|
|
||||||
|
return groups.join(QStringLiteral(";;"));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
namespace olive
|
||||||
|
{
|
||||||
|
|
||||||
|
EngineCore *EngineCore::instance_ = nullptr;
|
||||||
|
|
||||||
|
EngineCore::EngineCore(const CoreParams ¶ms)
|
||||||
|
: open_project_(nullptr)
|
||||||
|
, tool_(Tool::k_pointer)
|
||||||
|
, addable_object_(Tool::k_addable_empty)
|
||||||
|
, snapping_(true)
|
||||||
|
, core_params_(params)
|
||||||
|
, magic_(false)
|
||||||
|
, pixel_sampling_users_(0)
|
||||||
|
, shown_cache_full_warning_(false)
|
||||||
|
{
|
||||||
|
// Store reference to this object, making the assumption that EngineCore will only ever be made in
|
||||||
|
// main(). This will obviously break if not.
|
||||||
|
instance_ = this;
|
||||||
|
|
||||||
|
translator_ = new QTranslator(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
EngineCore *EngineCore::instance()
|
||||||
|
{
|
||||||
|
return instance_;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString EngineCore::footage_file_dialog_filter()
|
||||||
|
{
|
||||||
|
return build_footage_file_dialog_filter();
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList EngineCore::allowed_footage_extensions()
|
||||||
|
{
|
||||||
|
QStringList all = footage_video_extensions() + footage_audio_extensions() +
|
||||||
|
footage_image_extensions();
|
||||||
|
all.removeDuplicates();
|
||||||
|
return all;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool EngineCore::is_footage_extension_allowed(const QString &path)
|
||||||
|
{
|
||||||
|
const QString ext = QFileInfo(path).suffix().toLower();
|
||||||
|
if (ext.isEmpty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return allowed_footage_extensions().contains(ext);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EngineCore::declare_types_for_qt()
|
||||||
|
{
|
||||||
|
qRegisterMetaType<olive::core::Rational>();
|
||||||
|
qRegisterMetaType<NodeValue>();
|
||||||
|
qRegisterMetaType<NodeValueTable>();
|
||||||
|
qRegisterMetaType<NodeValueDatabase>();
|
||||||
|
qRegisterMetaType<FramePtr>();
|
||||||
|
qRegisterMetaType<SampleBuffer>();
|
||||||
|
qRegisterMetaType<AudioParams>();
|
||||||
|
qRegisterMetaType<NodeKeyframe::Type>();
|
||||||
|
qRegisterMetaType<Decoder::RetrieveState>();
|
||||||
|
qRegisterMetaType<olive::core::TimeRange>();
|
||||||
|
qRegisterMetaType<olive::core::Color>();
|
||||||
|
qRegisterMetaType<olive::AudioVisualWaveform>();
|
||||||
|
qRegisterMetaType<olive::VideoParams>();
|
||||||
|
qRegisterMetaType<olive::VideoParams::Interlacing>();
|
||||||
|
qRegisterMetaType<olive::MainWindowLayoutInfo>();
|
||||||
|
qRegisterMetaType<olive::RenderTicketPtr>();
|
||||||
|
}
|
||||||
|
|
||||||
|
void EngineCore::start()
|
||||||
|
{
|
||||||
|
// Load application config
|
||||||
|
Config::load();
|
||||||
|
|
||||||
|
// Set locale based on either startup arg, config, or auto-detect
|
||||||
|
set_startup_locale();
|
||||||
|
|
||||||
|
// Declare custom types for Qt signal/slot system
|
||||||
|
declare_types_for_qt();
|
||||||
|
|
||||||
|
// Set up node factory/library
|
||||||
|
NodeFactory::initialize();
|
||||||
|
|
||||||
|
// Set up color manager's default config
|
||||||
|
ColorManager::set_up_default_config();
|
||||||
|
|
||||||
|
// Initialize task manager
|
||||||
|
TaskManager::create_instance();
|
||||||
|
|
||||||
|
// Initialize ConformManager
|
||||||
|
ConformManager::create_instance();
|
||||||
|
|
||||||
|
// Initialize ProxyManager
|
||||||
|
ProxyManager::create_instance();
|
||||||
|
|
||||||
|
// Initialize RenderManager
|
||||||
|
RenderManager::create_instance();
|
||||||
|
|
||||||
|
// Initialize FrameManager
|
||||||
|
FrameManager::create_instance();
|
||||||
|
|
||||||
|
// Initialize project serializers
|
||||||
|
ProjectSerializer::initialize();
|
||||||
|
|
||||||
|
qInfo() << "Using Qt version:" << qVersion();
|
||||||
|
|
||||||
|
// Start autorecovery timer using the config value as its interval
|
||||||
|
set_autorecovery_interval(OAK_CONFIG("AutorecoveryInterval").toInt());
|
||||||
|
connect(&autorecovery_timer_, &QTimer::timeout, this,
|
||||||
|
&EngineCore::save_autorecovery);
|
||||||
|
autorecovery_timer_.start();
|
||||||
|
|
||||||
|
// Load recently opened projects list
|
||||||
|
{
|
||||||
|
QFile recent_projects_file(get_recent_projects_file_path());
|
||||||
|
if (recent_projects_file.open(QFile::ReadOnly | QFile::Text)) {
|
||||||
|
QString r = QString::fromUtf8(recent_projects_file.readAll());
|
||||||
|
if (!r.isEmpty()) {
|
||||||
|
recent_projects_ = r.split('\n');
|
||||||
|
}
|
||||||
|
recent_projects_file.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
emit open_recent_list_changed();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Manual crash triggering
|
||||||
|
if (core_params_.crash_on_startup()) {
|
||||||
|
const int interval = 5000;
|
||||||
|
qInfo() << "Manual crash was triggered. Application will crash in"
|
||||||
|
<< interval << "ms";
|
||||||
|
QTimer *crash_timer = new QTimer(this);
|
||||||
|
crash_timer->setInterval(interval);
|
||||||
|
connect(crash_timer, &QTimer::timeout, this, [] { abort(); });
|
||||||
|
crash_timer->start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void EngineCore::stop()
|
||||||
|
{
|
||||||
|
// Assume all projects have closed gracefully and no auto-recovery is necessary
|
||||||
|
autorecovered_projects_.clear();
|
||||||
|
save_unrecovered_list();
|
||||||
|
|
||||||
|
// Save Config
|
||||||
|
Config::save();
|
||||||
|
|
||||||
|
ProjectSerializer::destroy();
|
||||||
|
|
||||||
|
ConformManager::destroy_instance();
|
||||||
|
|
||||||
|
ProxyManager::destroy_instance();
|
||||||
|
|
||||||
|
FrameManager::destroy_instance();
|
||||||
|
|
||||||
|
RenderManager::destroy_instance();
|
||||||
|
|
||||||
|
TaskManager::destroy_instance();
|
||||||
|
|
||||||
|
NodeFactory::destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
UndoStack *EngineCore::undo_stack()
|
||||||
|
{
|
||||||
|
return &undo_stack_;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Tool::Item &EngineCore::tool() const
|
||||||
|
{
|
||||||
|
return tool_;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Tool::AddableObject &EngineCore::get_selected_addable_object() const
|
||||||
|
{
|
||||||
|
return addable_object_;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QString &EngineCore::get_selected_transition() const
|
||||||
|
{
|
||||||
|
return selected_transition_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void EngineCore::set_selected_addable_object(const Tool::AddableObject &obj)
|
||||||
|
{
|
||||||
|
addable_object_ = obj;
|
||||||
|
emit addable_object_changed(addable_object_);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EngineCore::set_selected_transition_object(const QString &obj)
|
||||||
|
{
|
||||||
|
selected_transition_ = obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
void EngineCore::clear_open_recent_list()
|
||||||
|
{
|
||||||
|
recent_projects_.clear();
|
||||||
|
save_recent_projects_list();
|
||||||
|
emit open_recent_list_changed();
|
||||||
|
}
|
||||||
|
|
||||||
|
void EngineCore::create_new_project()
|
||||||
|
{
|
||||||
|
// If we already have an empty/new project, switch to it
|
||||||
|
bool closed = close_project_handler_ ? close_project_handler_() :
|
||||||
|
close_open_project_without_prompt();
|
||||||
|
if (closed) {
|
||||||
|
Project *p = new Project();
|
||||||
|
p->initialize();
|
||||||
|
add_open_project(p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const bool &EngineCore::snapping() const
|
||||||
|
{
|
||||||
|
return snapping_;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QStringList &EngineCore::get_recent_projects() const
|
||||||
|
{
|
||||||
|
return recent_projects_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void EngineCore::set_tool(const Tool::Item &tool)
|
||||||
|
{
|
||||||
|
tool_ = tool;
|
||||||
|
|
||||||
|
emit tool_changed(tool_);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EngineCore::set_snapping(const bool &b)
|
||||||
|
{
|
||||||
|
snapping_ = b;
|
||||||
|
|
||||||
|
emit snapping_changed(snapping_);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EngineCore::set_use_proxy_media(bool enabled)
|
||||||
|
{
|
||||||
|
Config::current()[QStringLiteral("UseProxyMedia")] = enabled;
|
||||||
|
|
||||||
|
// Invalidate all footage so viewers re-evaluate with the new proxy state
|
||||||
|
if (open_project_) {
|
||||||
|
for (Node *n : open_project_->nodes()) {
|
||||||
|
if (Footage *footage = dynamic_cast<Footage *>(n)) {
|
||||||
|
footage->invalidate_all(Footage::k_filename_input);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void EngineCore::add_open_project(Project *p, bool add_to_recents)
|
||||||
|
{
|
||||||
|
// Ensure project is not open at the moment
|
||||||
|
if (open_project_ == p) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we currently have an empty project, close it first
|
||||||
|
if (open_project_) {
|
||||||
|
if (close_project_handler_) {
|
||||||
|
// The return value is intentionally ignored, preserving the
|
||||||
|
// historical behavior of this function
|
||||||
|
close_project_handler_();
|
||||||
|
} else {
|
||||||
|
close_open_project_without_prompt();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
set_active_project(p);
|
||||||
|
|
||||||
|
if (!p->filename().isEmpty() && add_to_recents) {
|
||||||
|
push_recently_opened_project(p->filename());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool EngineCore::add_open_project_from_task(Task *task, bool add_to_recents)
|
||||||
|
{
|
||||||
|
ProjectLoadBaseTask *load_task = static_cast<ProjectLoadBaseTask *>(task);
|
||||||
|
|
||||||
|
if (!load_task->is_cancelled()) {
|
||||||
|
Project *project = load_task->get_loaded_project();
|
||||||
|
|
||||||
|
if (validate_footage_in_loaded_project(project, project->get_saved_url())) {
|
||||||
|
add_open_project(project, add_to_recents);
|
||||||
|
|
||||||
|
if (load_layout_handler_) {
|
||||||
|
load_layout_handler_(load_task->get_loaded_layout());
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
delete project;
|
||||||
|
create_new_project();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void EngineCore::set_active_project(Project *p)
|
||||||
|
{
|
||||||
|
open_project_ = p;
|
||||||
|
RenderManager::instance()->set_project(p);
|
||||||
|
|
||||||
|
// The UI layer sets the project on the main window and tracks its
|
||||||
|
// modified state through this signal
|
||||||
|
emit active_project_changed(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool EngineCore::confirm_image_sequence(const QString &filename)
|
||||||
|
{
|
||||||
|
if (confirm_image_sequence_handler_) {
|
||||||
|
return confirm_image_sequence_handler_(filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Without a UI handler (headless), accept the image sequence
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef USE_OTIO
|
||||||
|
bool EngineCore::show_otio_import_dialog(const QList<Sequence *> &sequences)
|
||||||
|
{
|
||||||
|
if (otio_import_handler_) {
|
||||||
|
return otio_import_handler_(sequences);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Without a UI handler (headless), accept the import
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void EngineCore::add_recovery_project_from_task(Task *task)
|
||||||
|
{
|
||||||
|
if (add_open_project_from_task(task, false)) {
|
||||||
|
ProjectLoadBaseTask *load_task =
|
||||||
|
static_cast<ProjectLoadBaseTask *>(task);
|
||||||
|
|
||||||
|
Project *project = load_task->get_loaded_project();
|
||||||
|
|
||||||
|
// Clearing the filename will force the user to re-save it somewhere else
|
||||||
|
project->set_filename(QString());
|
||||||
|
|
||||||
|
// Forcing a UUID regeneration will prevent it from saving auto-recoveries in the same place
|
||||||
|
// the original project did
|
||||||
|
project->regenerate_uuid();
|
||||||
|
|
||||||
|
// Setting modified will ensure that the program doesn't close and lose the project without
|
||||||
|
// prompting the user first
|
||||||
|
project->set_modified(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QString EngineCore::get_auto_recovery_index_filename()
|
||||||
|
{
|
||||||
|
return QDir(QStandardPaths::writableLocation(
|
||||||
|
QStandardPaths::AppLocalDataLocation))
|
||||||
|
.filePath(QStringLiteral("unrecovered"));
|
||||||
|
}
|
||||||
|
|
||||||
|
void EngineCore::save_unrecovered_list()
|
||||||
|
{
|
||||||
|
QFile autorecovery_index(get_auto_recovery_index_filename());
|
||||||
|
|
||||||
|
if (autorecovered_projects_.isEmpty()) {
|
||||||
|
// Recovery list is empty, delete file if exists
|
||||||
|
if (autorecovery_index.exists()) {
|
||||||
|
autorecovery_index.remove();
|
||||||
|
}
|
||||||
|
} else if (autorecovery_index.open(QFile::WriteOnly)) {
|
||||||
|
// Overwrite recovery list with current list
|
||||||
|
QTextStream ts(&autorecovery_index);
|
||||||
|
|
||||||
|
bool first = true;
|
||||||
|
foreach (const QUuid &uuid, autorecovered_projects_) {
|
||||||
|
if (first) {
|
||||||
|
first = false;
|
||||||
|
} else {
|
||||||
|
ts << QStringLiteral("\n");
|
||||||
|
}
|
||||||
|
ts << uuid.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
autorecovery_index.close();
|
||||||
|
} else {
|
||||||
|
qWarning() << "Failed to save unrecovered list";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void EngineCore::save_recent_projects_list()
|
||||||
|
{
|
||||||
|
// Save recently opened projects
|
||||||
|
QFile recent_projects_file(get_recent_projects_file_path());
|
||||||
|
if (recent_projects_file.open(QFile::WriteOnly | QFile::Text)) {
|
||||||
|
recent_projects_file.write(recent_projects_.join('\n').toUtf8());
|
||||||
|
recent_projects_file.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void EngineCore::save_autorecovery()
|
||||||
|
{
|
||||||
|
if (OAK_CONFIG("AutorecoveryEnabled").toBool()) {
|
||||||
|
if (open_project_ && !open_project_->has_autorecovery_been_saved()) {
|
||||||
|
QDir project_autorecovery_dir(
|
||||||
|
QDir(FileFunctions::get_auto_recovery_root())
|
||||||
|
.filePath(open_project_->get_uuid().toString()));
|
||||||
|
if (FileFunctions::directory_is_valid(project_autorecovery_dir)) {
|
||||||
|
QString this_autorecovery_path =
|
||||||
|
project_autorecovery_dir.filePath(
|
||||||
|
QStringLiteral("%1.ove").arg(QString::number(
|
||||||
|
QDateTime::currentSecsSinceEpoch())));
|
||||||
|
|
||||||
|
// The actual save goes through the UI layer since it
|
||||||
|
// involves UI state (the main window layout)
|
||||||
|
if (save_project_handler_) {
|
||||||
|
save_project_handler_(this_autorecovery_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
open_project_->set_autorecovery_saved(true);
|
||||||
|
|
||||||
|
// Keep track of projects that where the "newest" save is the recovery project
|
||||||
|
if (!autorecovered_projects_.contains(
|
||||||
|
open_project_->get_uuid())) {
|
||||||
|
autorecovered_projects_.append(open_project_->get_uuid());
|
||||||
|
}
|
||||||
|
|
||||||
|
qDebug() << "Saved auto-recovery to:" << this_autorecovery_path;
|
||||||
|
|
||||||
|
// Write human-readable real name so it's not just a UUID
|
||||||
|
{
|
||||||
|
QFile realname_file(project_autorecovery_dir.filePath(
|
||||||
|
QStringLiteral("realname.txt")));
|
||||||
|
realname_file.open(QFile::WriteOnly);
|
||||||
|
realname_file.write(
|
||||||
|
open_project_->pretty_filename().toUtf8());
|
||||||
|
realname_file.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t max_recoveries_per_file =
|
||||||
|
OAK_CONFIG("AutorecoveryMaximum").toLongLong();
|
||||||
|
|
||||||
|
// Since we write an extra file, increment total allowed files by 1
|
||||||
|
max_recoveries_per_file++;
|
||||||
|
|
||||||
|
// Delete old entries
|
||||||
|
QStringList recovery_files = project_autorecovery_dir.entryList(
|
||||||
|
QDir::Files | QDir::NoDotAndDotDot, QDir::Name);
|
||||||
|
while (recovery_files.size() > max_recoveries_per_file) {
|
||||||
|
bool deleted = false;
|
||||||
|
for (int i = 0; i < recovery_files.size(); i++) {
|
||||||
|
const QString &f = recovery_files.at(i);
|
||||||
|
|
||||||
|
if (f.endsWith(QStringLiteral(".ove"),
|
||||||
|
Qt::CaseInsensitive)) {
|
||||||
|
QString delete_full_path =
|
||||||
|
project_autorecovery_dir.filePath(f);
|
||||||
|
qDebug()
|
||||||
|
<< "Deleted old recovery:" << delete_full_path;
|
||||||
|
QFile::remove(delete_full_path);
|
||||||
|
recovery_files.removeAt(i);
|
||||||
|
deleted = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!deleted) {
|
||||||
|
// For some reason none of the files were deletable. Break so we don't end up in
|
||||||
|
// an infinite loop.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// The engine cannot show dialogs, report through the
|
||||||
|
// application's registered error handler instead
|
||||||
|
Config::report_error(
|
||||||
|
tr("Auto-Recovery Error"),
|
||||||
|
tr("Failed to save auto-recovery to \"%1\". "
|
||||||
|
"Oak Video Editor may not have permission to this directory.")
|
||||||
|
.arg(project_autorecovery_dir.absolutePath()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save index
|
||||||
|
save_unrecovered_list();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Timecode::Display EngineCore::get_timecode_display() const
|
||||||
|
{
|
||||||
|
return static_cast<Timecode::Display>(
|
||||||
|
OAK_CONFIG("TimecodeDisplay").toInt());
|
||||||
|
}
|
||||||
|
|
||||||
|
void EngineCore::set_timecode_display(Timecode::Display d)
|
||||||
|
{
|
||||||
|
OAK_CONFIG("TimecodeDisplay") = d;
|
||||||
|
|
||||||
|
emit timecode_display_changed(d);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EngineCore::set_autorecovery_interval(int minutes)
|
||||||
|
{
|
||||||
|
// Convert minutes to milliseconds
|
||||||
|
autorecovery_timer_.setInterval(minutes * 60000);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EngineCore::copy_string_to_clipboard(const QString &s)
|
||||||
|
{
|
||||||
|
QGuiApplication::clipboard()->setText(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString EngineCore::paste_string_from_clipboard()
|
||||||
|
{
|
||||||
|
return QGuiApplication::clipboard()->text();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString EngineCore::get_recent_projects_file_path()
|
||||||
|
{
|
||||||
|
return QDir(FileFunctions::get_configuration_location())
|
||||||
|
.filePath(QStringLiteral("recent"));
|
||||||
|
}
|
||||||
|
|
||||||
|
void EngineCore::set_startup_locale()
|
||||||
|
{
|
||||||
|
// Set language
|
||||||
|
if (!core_params_.startup_language().isEmpty()) {
|
||||||
|
if (translator_->load(core_params_.startup_language()) &&
|
||||||
|
QCoreApplication::installTranslator(translator_)) {
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
qWarning()
|
||||||
|
<< "Failed to load translation file. Falling back to defaults.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QString use_locale = OAK_CONFIG("Language").toString();
|
||||||
|
|
||||||
|
if (use_locale.isEmpty()) {
|
||||||
|
// No configured locale, auto-detect the system's locale
|
||||||
|
use_locale = QLocale::system().name();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!set_language(use_locale)) {
|
||||||
|
qWarning() << "Trying to use locale" << use_locale
|
||||||
|
<< "but couldn't find a translation for it";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void EngineCore::show_status_bar_message(const QString &s, int timeout)
|
||||||
|
{
|
||||||
|
emit status_message_show(s, timeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EngineCore::clear_status_bar_message()
|
||||||
|
{
|
||||||
|
emit status_message_clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void EngineCore::request_pixel_sampling_in_viewers(bool e)
|
||||||
|
{
|
||||||
|
if (e) {
|
||||||
|
if (pixel_sampling_users_ == 0) {
|
||||||
|
// Signal to start pixel sampling
|
||||||
|
emit color_picker_enabled(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
pixel_sampling_users_++;
|
||||||
|
} else {
|
||||||
|
pixel_sampling_users_--;
|
||||||
|
|
||||||
|
if (pixel_sampling_users_ == 0) {
|
||||||
|
// Signal to end pixel sampling
|
||||||
|
emit color_picker_enabled(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void EngineCore::warn_cache_full()
|
||||||
|
{
|
||||||
|
if (!shown_cache_full_warning_) {
|
||||||
|
shown_cache_full_warning_ = true;
|
||||||
|
|
||||||
|
emit cache_full_warning_requested();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void EngineCore::push_recently_opened_project(const QString &s)
|
||||||
|
{
|
||||||
|
if (s.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int existing_index = recent_projects_.indexOf(s);
|
||||||
|
|
||||||
|
if (existing_index >= 0) {
|
||||||
|
recent_projects_.move(existing_index, 0);
|
||||||
|
} else {
|
||||||
|
recent_projects_.prepend(s);
|
||||||
|
|
||||||
|
const int k_maximum_recent_projects = 10;
|
||||||
|
while (recent_projects_.size() > k_maximum_recent_projects) {
|
||||||
|
recent_projects_.removeLast();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
save_recent_projects_list();
|
||||||
|
|
||||||
|
emit open_recent_list_changed();
|
||||||
|
}
|
||||||
|
|
||||||
|
void EngineCore::on_project_saved(Project *p)
|
||||||
|
{
|
||||||
|
push_recently_opened_project(p->filename());
|
||||||
|
|
||||||
|
p->set_modified(false);
|
||||||
|
|
||||||
|
autorecovered_projects_.removeOne(p->get_uuid());
|
||||||
|
save_unrecovered_list();
|
||||||
|
}
|
||||||
|
|
||||||
|
void EngineCore::remove_recently_opened_project(int index)
|
||||||
|
{
|
||||||
|
recent_projects_.removeAt(index);
|
||||||
|
|
||||||
|
save_recent_projects_list();
|
||||||
|
|
||||||
|
emit open_recent_list_changed();
|
||||||
|
}
|
||||||
|
|
||||||
|
int EngineCore::count_files_in_file_list(const QFileInfoList &filenames)
|
||||||
|
{
|
||||||
|
int file_count = 0;
|
||||||
|
|
||||||
|
foreach (const QFileInfo &f, filenames) {
|
||||||
|
// For some reason QDir::NoDotAndDotDot doesn't work with entryInfoList, so we have to check manually
|
||||||
|
if (f.fileName() == "." || f.fileName() == "..") {
|
||||||
|
continue;
|
||||||
|
} else if (f.isDir()) {
|
||||||
|
QFileInfoList info_list =
|
||||||
|
QDir(f.absoluteFilePath()).entryInfoList();
|
||||||
|
|
||||||
|
file_count += count_files_in_file_list(info_list);
|
||||||
|
} else {
|
||||||
|
file_count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return file_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
Sequence *EngineCore::create_new_sequence_for_project(const QString &format,
|
||||||
|
Project *project)
|
||||||
|
{
|
||||||
|
Sequence *new_sequence = new Sequence();
|
||||||
|
|
||||||
|
// Get default name for this sequence (in the format "Sequence N", the first that doesn't exist)
|
||||||
|
int sequence_number = 1;
|
||||||
|
QString sequence_name;
|
||||||
|
do {
|
||||||
|
sequence_name = format.arg(sequence_number);
|
||||||
|
sequence_number++;
|
||||||
|
} while (project->root()->child_exists_with_name(sequence_name));
|
||||||
|
new_sequence->set_label(sequence_name);
|
||||||
|
|
||||||
|
return new_sequence;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString strip_windows_drive_letter(QString s)
|
||||||
|
{
|
||||||
|
// HACK: On Windows, absolute paths are saved with a drive letter (e.g. "C:\video.mp4"). Below,
|
||||||
|
// we use Qt's relative path system to resolve when an entire project may be in a different
|
||||||
|
// folder, but the files are all in the same place relatively to the project. Unfortunately,
|
||||||
|
// Qt chooses not to understand paths from Windows on non-Windows platforms, which causes
|
||||||
|
// this to break when a project is moving from Windows to non-Windows. To resolve that, if
|
||||||
|
// we're on a non-Windows platform and we detect a Windows path (i.e. a path with a drive
|
||||||
|
// letter at the start), we strip it off. We also convert any back-slashes to forward-slashes
|
||||||
|
// because on Windows they are interchangeable and on non-Windows they are not.
|
||||||
|
#ifndef Q_OS_WINDOWS
|
||||||
|
if (s.size() >= 2) {
|
||||||
|
if (s.at(0).isLetter() && s.at(1) == ':') {
|
||||||
|
s = s.mid(2);
|
||||||
|
s.replace('\\', '/');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool EngineCore::validate_footage_in_loaded_project(Project *project,
|
||||||
|
const QString &project_saved_url)
|
||||||
|
{
|
||||||
|
QVector<Footage *> footage_we_couldnt_validate;
|
||||||
|
|
||||||
|
for (Node *n : project->nodes()) {
|
||||||
|
if (Footage *footage = dynamic_cast<Footage *>(n)) {
|
||||||
|
QString footage_fn = strip_windows_drive_letter(footage->filename());
|
||||||
|
QString project_fn = strip_windows_drive_letter(project_saved_url);
|
||||||
|
|
||||||
|
if (!QFileInfo::exists(footage_fn) &&
|
||||||
|
!project_saved_url.isEmpty()) {
|
||||||
|
// If the footage doesn't exist, it might have moved with the project
|
||||||
|
const QString &project_current_url = project->filename();
|
||||||
|
|
||||||
|
if (project_current_url != project_fn) {
|
||||||
|
// Project has definitely moved, try to resolve relative paths
|
||||||
|
QDir saved_dir(QFileInfo(project_fn).dir());
|
||||||
|
QDir true_dir(QFileInfo(project_current_url).dir());
|
||||||
|
|
||||||
|
QString relative_filename =
|
||||||
|
saved_dir.relativeFilePath(footage_fn);
|
||||||
|
QString transformed_abs_filename =
|
||||||
|
true_dir.filePath(relative_filename);
|
||||||
|
|
||||||
|
if (QFileInfo::exists(transformed_abs_filename)) {
|
||||||
|
// Use this file instead
|
||||||
|
qInfo() << "Resolved" << footage_fn << "relatively to"
|
||||||
|
<< transformed_abs_filename;
|
||||||
|
footage->set_filename(transformed_abs_filename);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (QFileInfo::exists(footage->filename())) {
|
||||||
|
// Assume valid
|
||||||
|
footage->set_valid();
|
||||||
|
} else {
|
||||||
|
footage_we_couldnt_validate.append(footage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!footage_we_couldnt_validate.isEmpty()) {
|
||||||
|
// Let the UI layer offer to relink the missing footage
|
||||||
|
if (relink_handler_ && !relink_handler_(footage_we_couldnt_validate)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool EngineCore::set_language(const QString &locale)
|
||||||
|
{
|
||||||
|
QCoreApplication::removeTranslator(translator_);
|
||||||
|
|
||||||
|
QString resource_path = QStringLiteral(":/ts/%1").arg(locale);
|
||||||
|
if (translator_->load(resource_path) &&
|
||||||
|
QCoreApplication::installTranslator(translator_)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool EngineCore::close_open_project_without_prompt()
|
||||||
|
{
|
||||||
|
if (open_project_) {
|
||||||
|
// For safety, the undo stack is cleared so no commands try to affect a freed project
|
||||||
|
undo_stack_.clear();
|
||||||
|
|
||||||
|
Project *tmp = open_project_;
|
||||||
|
set_active_project(nullptr);
|
||||||
|
delete tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void EngineCore::set_confirm_image_sequence_handler(
|
||||||
|
ConfirmImageSequenceHandler handler)
|
||||||
|
{
|
||||||
|
confirm_image_sequence_handler_ = std::move(handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EngineCore::set_relink_handler(FootageRelinkHandler handler)
|
||||||
|
{
|
||||||
|
relink_handler_ = std::move(handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EngineCore::set_save_project_handler(SaveProjectHandler handler)
|
||||||
|
{
|
||||||
|
save_project_handler_ = std::move(handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EngineCore::set_close_project_handler(CloseProjectHandler handler)
|
||||||
|
{
|
||||||
|
close_project_handler_ = std::move(handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EngineCore::set_load_layout_handler(LoadLayoutHandler handler)
|
||||||
|
{
|
||||||
|
load_layout_handler_ = std::move(handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef USE_OTIO
|
||||||
|
void EngineCore::set_otio_import_handler(OtioImportHandler handler)
|
||||||
|
{
|
||||||
|
otio_import_handler_ = std::move(handler);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
EngineCore::CoreParams::CoreParams()
|
||||||
|
: mode_(k_run_normal)
|
||||||
|
, run_fullscreen_(false)
|
||||||
|
, crash_(false)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,617 @@
|
|||||||
|
/***
|
||||||
|
|
||||||
|
Olive - Non-Linear Video Editor
|
||||||
|
Copyright (C) 2022 Olive Team
|
||||||
|
Modifications Copyright (C) 2025 mikesolar
|
||||||
|
|
||||||
|
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 <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
***/
|
||||||
|
|
||||||
|
#ifndef OAK_COREENGINE_H
|
||||||
|
#define OAK_COREENGINE_H
|
||||||
|
|
||||||
|
#include <olive/core/core.h>
|
||||||
|
#include <QFileInfoList>
|
||||||
|
#include <QList>
|
||||||
|
#include <QTimer>
|
||||||
|
#include <QTranslator>
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
#include "node/project/footage/footage.h"
|
||||||
|
#include "node/project.h"
|
||||||
|
#include "node/project/sequence/sequence.h"
|
||||||
|
#include "node/project/serializer/mainwindowlayoutinfo.h"
|
||||||
|
#include "task/task.h"
|
||||||
|
#include "tool/tool.h"
|
||||||
|
#include "undo/undostack.h"
|
||||||
|
|
||||||
|
namespace olive
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief The UI-independent engine core of the Olive application
|
||||||
|
*
|
||||||
|
* EngineCore holds the global application state that does not depend on the
|
||||||
|
* UI (no widgets, dialogs, panels or windows) and can therefore run headless
|
||||||
|
* (e.g. in the render worker or in tests). The UI layer (olive::Core)
|
||||||
|
* derives from this class and registers handlers / connects signals for
|
||||||
|
* everything that requires user interaction (see the std::function handlers
|
||||||
|
* below, modelled after Config::ErrorHandler).
|
||||||
|
*
|
||||||
|
* This class must only include engine-side headers (olive/core, node/, undo/,
|
||||||
|
* task/, timeline/, codec/, common/, config/, tool/, audio/, render/). It must
|
||||||
|
* never include widget/, dialog/, panel/, window/ or ui/ headers.
|
||||||
|
*/
|
||||||
|
class EngineCore : public QObject {
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
class CoreParams {
|
||||||
|
public:
|
||||||
|
CoreParams();
|
||||||
|
|
||||||
|
enum RunMode { k_run_normal, k_headless_export, k_headless_pre_cache };
|
||||||
|
|
||||||
|
bool fullscreen() const
|
||||||
|
{
|
||||||
|
return run_fullscreen_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_fullscreen(bool e)
|
||||||
|
{
|
||||||
|
run_fullscreen_ = e;
|
||||||
|
}
|
||||||
|
|
||||||
|
RunMode run_mode() const
|
||||||
|
{
|
||||||
|
return mode_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_run_mode(RunMode m)
|
||||||
|
{
|
||||||
|
mode_ = m;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QString startup_project() const
|
||||||
|
{
|
||||||
|
return startup_project_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_startup_project(const QString &p)
|
||||||
|
{
|
||||||
|
startup_project_ = p;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QString &startup_language() const
|
||||||
|
{
|
||||||
|
return startup_language_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_startup_language(const QString &s)
|
||||||
|
{
|
||||||
|
startup_language_ = s;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool crash_on_startup() const
|
||||||
|
{
|
||||||
|
return crash_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_crash_on_startup(bool e)
|
||||||
|
{
|
||||||
|
crash_ = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
RunMode mode_;
|
||||||
|
|
||||||
|
QString startup_project_;
|
||||||
|
|
||||||
|
QString startup_language_;
|
||||||
|
|
||||||
|
bool run_fullscreen_;
|
||||||
|
|
||||||
|
bool crash_;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief EngineCore Constructor
|
||||||
|
*/
|
||||||
|
EngineCore(const CoreParams ¶ms);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief EngineCore object accessible from anywhere in the code
|
||||||
|
*
|
||||||
|
* Use this to access engine functions. This assumes the object is only
|
||||||
|
* ever constructed once at the application entry point.
|
||||||
|
*/
|
||||||
|
static EngineCore *instance();
|
||||||
|
|
||||||
|
static QString footage_file_dialog_filter();
|
||||||
|
static QStringList allowed_footage_extensions();
|
||||||
|
static bool is_footage_extension_allowed(const QString &path);
|
||||||
|
|
||||||
|
const CoreParams &core_params() const
|
||||||
|
{
|
||||||
|
return core_params_;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Start the engine
|
||||||
|
*
|
||||||
|
* Initializes everything that is independent of the UI: config, locale,
|
||||||
|
* meta types, and the global engine managers (NodeFactory, ColorManager,
|
||||||
|
* TaskManager, ConformManager, ProxyManager, RenderManager, FrameManager,
|
||||||
|
* ProjectSerializer). Also loads the recent projects list and starts the
|
||||||
|
* autorecovery timer.
|
||||||
|
*/
|
||||||
|
void start();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Stop the engine
|
||||||
|
*
|
||||||
|
* Frees the global engine managers and saves the config. In a UI build,
|
||||||
|
* call this after the UI services have been torn down.
|
||||||
|
*/
|
||||||
|
void stop();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Retrieve UndoStack object
|
||||||
|
*/
|
||||||
|
UndoStack *undo_stack();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the currently active tool
|
||||||
|
*/
|
||||||
|
const Tool::Item &tool() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the currently selected object that the add tool should make (if the add tool is active)
|
||||||
|
*/
|
||||||
|
const Tool::AddableObject &get_selected_addable_object() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the currently selected node that the transition tool should make (if the transition tool is active)
|
||||||
|
*/
|
||||||
|
const QString &get_selected_transition() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get current snapping value
|
||||||
|
*/
|
||||||
|
const bool &snapping() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns a list of the most recently opened/saved projects
|
||||||
|
*/
|
||||||
|
const QStringList &get_recent_projects() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Gets current timecode display mode
|
||||||
|
*/
|
||||||
|
Timecode::Display get_timecode_display() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Sets current timecode display mode
|
||||||
|
*/
|
||||||
|
void set_timecode_display(Timecode::Display d);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set how frequently an autorecovery should be saved (if the project has changed, see SetProjectModified())
|
||||||
|
*/
|
||||||
|
void set_autorecovery_interval(int minutes);
|
||||||
|
|
||||||
|
static void copy_string_to_clipboard(const QString &s);
|
||||||
|
|
||||||
|
static QString paste_string_from_clipboard();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Recursively count files in a file/directory list
|
||||||
|
*/
|
||||||
|
static int count_files_in_file_list(const QFileInfoList &filenames);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Create a new sequence named appropriately for the given project
|
||||||
|
*/
|
||||||
|
static Sequence *create_new_sequence_for_project(const QString &format,
|
||||||
|
Project *project);
|
||||||
|
static Sequence *create_new_sequence_for_project(Project *project)
|
||||||
|
{
|
||||||
|
return create_new_sequence_for_project(tr("Sequence %1"), project);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Check each footage object for whether it still exists or has changed
|
||||||
|
*
|
||||||
|
* Missing footage is passed to the relink handler registered by the UI
|
||||||
|
* layer. Without a handler, the project is accepted as-is.
|
||||||
|
*/
|
||||||
|
bool validate_footage_in_loaded_project(Project *project,
|
||||||
|
const QString &project_saved_url);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Changes the current language
|
||||||
|
*/
|
||||||
|
bool set_language(const QString &locale);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Show a message in the status bar
|
||||||
|
*
|
||||||
|
* The engine cannot show UI itself, so this only emits
|
||||||
|
* status_message_show(). The UI layer connects the signal to the main
|
||||||
|
* window's status bar.
|
||||||
|
*/
|
||||||
|
void show_status_bar_message(const QString &s, int timeout = 0);
|
||||||
|
|
||||||
|
void clear_status_bar_message();
|
||||||
|
|
||||||
|
bool is_magic_enabled() const
|
||||||
|
{
|
||||||
|
return magic_;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Handler confirming that a file should be imported as an image sequence
|
||||||
|
*
|
||||||
|
* Registered by the UI layer (e.g. a QMessageBox-based prompt). Without
|
||||||
|
* a handler, the import is accepted.
|
||||||
|
*/
|
||||||
|
using ConfirmImageSequenceHandler =
|
||||||
|
std::function<bool(const QString &filename)>;
|
||||||
|
void set_confirm_image_sequence_handler(ConfirmImageSequenceHandler handler);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Handler offering to relink footage that could not be validated
|
||||||
|
*
|
||||||
|
* Registered by the UI layer (e.g. a FootageRelinkDialog). Should return
|
||||||
|
* false to reject the project. Without a handler, the project is accepted.
|
||||||
|
*/
|
||||||
|
using FootageRelinkHandler = std::function<bool(QVector<Footage *>)>;
|
||||||
|
void set_relink_handler(FootageRelinkHandler handler);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Handler performing the actual project save for autorecovery
|
||||||
|
*
|
||||||
|
* Registered by the UI layer, since saving involves UI state (the main
|
||||||
|
* window layout). Without a handler, the actual file write is skipped.
|
||||||
|
*/
|
||||||
|
using SaveProjectHandler =
|
||||||
|
std::function<void(const QString &override_filename)>;
|
||||||
|
void set_save_project_handler(SaveProjectHandler handler);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Handler closing the currently open project
|
||||||
|
*
|
||||||
|
* Registered by the UI layer, which may prompt the user to save first.
|
||||||
|
* Should return false if the close was cancelled. Without a handler, the
|
||||||
|
* project is closed silently.
|
||||||
|
*/
|
||||||
|
using CloseProjectHandler = std::function<bool()>;
|
||||||
|
void set_close_project_handler(CloseProjectHandler handler);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Handler applying a loaded main window layout after a project load
|
||||||
|
*/
|
||||||
|
using LoadLayoutHandler =
|
||||||
|
std::function<void(const MainWindowLayoutInfo &layout)>;
|
||||||
|
void set_load_layout_handler(LoadLayoutHandler handler);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Update engine state after a project was successfully saved
|
||||||
|
*
|
||||||
|
* Pushes the project to the top of the recent list, clears its modified
|
||||||
|
* flag and removes it from the unrecovered list.
|
||||||
|
*/
|
||||||
|
void on_project_saved(Project *p);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Removes a project from the recently opened list (e.g. if it no longer exists)
|
||||||
|
*/
|
||||||
|
void remove_recently_opened_project(int index);
|
||||||
|
|
||||||
|
#ifdef USE_OTIO
|
||||||
|
/**
|
||||||
|
* @brief Handler showing the OTIO import options dialog
|
||||||
|
*
|
||||||
|
* Registered by the UI layer. Without a handler, the import is accepted.
|
||||||
|
*/
|
||||||
|
using OtioImportHandler =
|
||||||
|
std::function<bool(const QList<Sequence *> &sequences)>;
|
||||||
|
void set_otio_import_handler(OtioImportHandler handler);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
/**
|
||||||
|
* @brief Set the current application-wide tool
|
||||||
|
*
|
||||||
|
* @param tool
|
||||||
|
*/
|
||||||
|
void set_tool(const Tool::Item &tool);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set the current snapping setting
|
||||||
|
*/
|
||||||
|
void set_snapping(const bool &b);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Globally enable or disable decoding from proxy media
|
||||||
|
*
|
||||||
|
* When disabled, all footage decodes from its original media regardless of
|
||||||
|
* each footage's individual proxy setting. The per-footage settings are
|
||||||
|
* preserved and take effect again when this is re-enabled.
|
||||||
|
*/
|
||||||
|
void set_use_proxy_media(bool enabled);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set the currently selected object that the add tool should make
|
||||||
|
*/
|
||||||
|
void set_selected_addable_object(const Tool::AddableObject &obj);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set the currently selected object that the add tool should make
|
||||||
|
*/
|
||||||
|
void set_selected_transition_object(const QString &obj);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Clears the list of recently opened/saved projects
|
||||||
|
*/
|
||||||
|
void clear_open_recent_list();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Creates a new empty project and opens it
|
||||||
|
*
|
||||||
|
* The currently open project is closed through the close handler, so the
|
||||||
|
* UI layer may prompt to save first.
|
||||||
|
*/
|
||||||
|
void create_new_project();
|
||||||
|
|
||||||
|
void request_pixel_sampling_in_viewers(bool e);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Warn that the disk cache is full (at most once)
|
||||||
|
*
|
||||||
|
* Only emits cache_full_warning_requested(); the UI layer shows the
|
||||||
|
* actual dialog.
|
||||||
|
*/
|
||||||
|
void warn_cache_full();
|
||||||
|
|
||||||
|
void set_magic(bool e)
|
||||||
|
{
|
||||||
|
magic_ = e;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Invokable forwarder for the image sequence confirmation
|
||||||
|
*
|
||||||
|
* Import tasks run on worker threads and call this slot through
|
||||||
|
* QMetaObject::invokeMethod with Qt::BlockingQueuedConnection. It runs
|
||||||
|
* on the main thread and forwards to the registered handler; without a
|
||||||
|
* handler, the import is accepted.
|
||||||
|
*/
|
||||||
|
bool confirm_image_sequence(const QString &filename);
|
||||||
|
|
||||||
|
#ifdef USE_OTIO
|
||||||
|
/**
|
||||||
|
* @brief Invokable forwarder for the OTIO import dialog
|
||||||
|
*
|
||||||
|
* Same threading pattern as confirm_image_sequence().
|
||||||
|
*/
|
||||||
|
bool show_otio_import_dialog(const QList<Sequence *> &sequences);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
signals:
|
||||||
|
/**
|
||||||
|
* @brief Signal emitted when the tool is changed from somewhere
|
||||||
|
*/
|
||||||
|
void tool_changed(const Tool::Item &tool);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Signal emitted when addable object changes through SetSelectedAddableObject
|
||||||
|
*/
|
||||||
|
void addable_object_changed(Tool::AddableObject o);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Signal emitted when the snapping setting is changed
|
||||||
|
*/
|
||||||
|
void snapping_changed(const bool &b);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Signal emitted when the default timecode display mode changed
|
||||||
|
*/
|
||||||
|
void timecode_display_changed(Timecode::Display d);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Signal emitted when a change is made to the open recent list
|
||||||
|
*/
|
||||||
|
void open_recent_list_changed();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Enable mouse color sampling functionality on all viewers
|
||||||
|
*
|
||||||
|
* This can be slow, so we only turn it on when we need it.
|
||||||
|
*/
|
||||||
|
void color_picker_enabled(bool e);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief A viewer with color picked enabled has emitted a color
|
||||||
|
*/
|
||||||
|
void color_picker_color_emitted(const Color &reference, const Color &display);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Request showing a message in the main window's status bar
|
||||||
|
*/
|
||||||
|
void status_message_show(const QString &message, int timeout);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Request clearing the main window's status bar
|
||||||
|
*/
|
||||||
|
void status_message_clear();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Request showing the "disk cache full" warning to the user
|
||||||
|
*/
|
||||||
|
void cache_full_warning_requested();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Signal emitted when the active (open) project changed
|
||||||
|
*
|
||||||
|
* The UI layer uses this to set the project on the main window.
|
||||||
|
*/
|
||||||
|
void active_project_changed(Project *p);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
/**
|
||||||
|
* @brief Adds a project to the "open projects" list
|
||||||
|
*/
|
||||||
|
void add_open_project(olive::Project *p, bool add_to_recents = false);
|
||||||
|
|
||||||
|
bool add_open_project_from_task(Task *task, bool add_to_recents);
|
||||||
|
|
||||||
|
void set_active_project(Project *p);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Currently open project
|
||||||
|
*
|
||||||
|
* Protected so the UI layer (olive::Core) can read it.
|
||||||
|
*/
|
||||||
|
Project *open_project_;
|
||||||
|
|
||||||
|
static QString get_auto_recovery_index_filename();
|
||||||
|
|
||||||
|
protected slots:
|
||||||
|
void add_recovery_project_from_task(Task *task);
|
||||||
|
|
||||||
|
private:
|
||||||
|
/**
|
||||||
|
* @brief Returns the filename where the recently opened/saved projects should be stored
|
||||||
|
*/
|
||||||
|
static QString get_recent_projects_file_path();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Called only on startup to set the locale
|
||||||
|
*/
|
||||||
|
void set_startup_locale();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Adds a filename to the top of the recently opened projects list (or moves it if it already exists)
|
||||||
|
*/
|
||||||
|
void push_recently_opened_project(const QString &s);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Declare custom types/classes for Qt's signal/slot system
|
||||||
|
*
|
||||||
|
* Qt's signal/slot system requires types to be declared. In the interest of doing this only at startup, we contain
|
||||||
|
* them all in a function here.
|
||||||
|
*/
|
||||||
|
void declare_types_for_qt();
|
||||||
|
|
||||||
|
void save_unrecovered_list();
|
||||||
|
|
||||||
|
void save_recent_projects_list();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Close the open project without any user prompt
|
||||||
|
*
|
||||||
|
* Fallback for the close handler when no UI layer is present.
|
||||||
|
*/
|
||||||
|
bool close_open_project_without_prompt();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Currently active tool
|
||||||
|
*/
|
||||||
|
Tool::Item tool_;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Currently active addable object
|
||||||
|
*/
|
||||||
|
Tool::AddableObject addable_object_;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Currently selected transition
|
||||||
|
*/
|
||||||
|
QString selected_transition_;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Current snapping setting
|
||||||
|
*/
|
||||||
|
bool snapping_;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Internal timer for saving autorecovery files
|
||||||
|
*/
|
||||||
|
QTimer autorecovery_timer_;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Application-wide undo stack instance_
|
||||||
|
*/
|
||||||
|
UndoStack undo_stack_;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief List of most recently opened/saved projects
|
||||||
|
*/
|
||||||
|
QStringList recent_projects_;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Parameters set up in main() determining how the program should run
|
||||||
|
*/
|
||||||
|
CoreParams core_params_;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Static singleton engine instance_
|
||||||
|
*/
|
||||||
|
static EngineCore *instance_;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Internal translator
|
||||||
|
*/
|
||||||
|
QTranslator *translator_;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief List of projects that are unsaved but have autorecovery projects
|
||||||
|
*/
|
||||||
|
QVector<QUuid> autorecovered_projects_;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Do something debug related
|
||||||
|
*/
|
||||||
|
bool magic_;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief How many widgets currently need pixel sampling access
|
||||||
|
*/
|
||||||
|
int pixel_sampling_users_;
|
||||||
|
|
||||||
|
bool shown_cache_full_warning_;
|
||||||
|
|
||||||
|
ConfirmImageSequenceHandler confirm_image_sequence_handler_;
|
||||||
|
|
||||||
|
FootageRelinkHandler relink_handler_;
|
||||||
|
|
||||||
|
SaveProjectHandler save_project_handler_;
|
||||||
|
|
||||||
|
CloseProjectHandler close_project_handler_;
|
||||||
|
|
||||||
|
LoadLayoutHandler load_layout_handler_;
|
||||||
|
|
||||||
|
#ifdef USE_OTIO
|
||||||
|
OtioImportHandler otio_import_handler_;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void save_autorecovery();
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // OAK_COREENGINE_H
|
||||||
@@ -43,6 +43,7 @@
|
|||||||
#include "common/debug.h"
|
#include "common/debug.h"
|
||||||
#include "node/project/serializer/serializer.h"
|
#include "node/project/serializer/serializer.h"
|
||||||
#include "version.h"
|
#include "version.h"
|
||||||
|
#include "window/mainwindow/mainwindow.h"
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#include <QOffscreenSurface>
|
#include <QOffscreenSurface>
|
||||||
|
|||||||
@@ -27,7 +27,7 @@
|
|||||||
#include "common/define.h"
|
#include "common/define.h"
|
||||||
#include "common/filefunctions.h"
|
#include "common/filefunctions.h"
|
||||||
#include "config/config.h"
|
#include "config/config.h"
|
||||||
#include "core.h"
|
#include "node/project.h"
|
||||||
|
|
||||||
namespace olive
|
namespace olive
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -25,7 +25,7 @@
|
|||||||
#include <QMetaObject>
|
#include <QMetaObject>
|
||||||
|
|
||||||
|
|
||||||
#include "core.h"
|
#include "coreengine.h"
|
||||||
#include "node/color/colormanager/colormanager.h"
|
#include "node/color/colormanager/colormanager.h"
|
||||||
#include "render/lutlibrary.h"
|
#include "render/lutlibrary.h"
|
||||||
#include "render/previewautocacher.h"
|
#include "render/previewautocacher.h"
|
||||||
@@ -204,8 +204,8 @@ void OCIOLutNode::set_last_error(const QString &error) const
|
|||||||
|
|
||||||
// Make the error visible to the user instead of failing silently, but only
|
// Make the error visible to the user instead of failing silently, but only
|
||||||
// from the main process (the render worker has no status bar)
|
// from the main process (the render worker has no status bar)
|
||||||
if (!error.isEmpty() && is_main_process() && Core::instance()) {
|
if (!error.isEmpty() && is_main_process() && EngineCore::instance()) {
|
||||||
Core::instance()->show_status_bar_message(error, 10000);
|
EngineCore::instance()->show_status_bar_message(error, 10000);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,6 @@
|
|||||||
#include "cornerpindistortnode.h"
|
#include "cornerpindistortnode.h"
|
||||||
|
|
||||||
#include "common/lerp.h"
|
#include "common/lerp.h"
|
||||||
#include "core.h"
|
|
||||||
|
|
||||||
namespace olive
|
namespace olive
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -22,7 +22,6 @@
|
|||||||
#include "cropdistortnode.h"
|
#include "cropdistortnode.h"
|
||||||
|
|
||||||
#include "common/util.h"
|
#include "common/util.h"
|
||||||
#include "core.h"
|
|
||||||
#include "node/sliderdisplaytype.h"
|
#include "node/sliderdisplaytype.h"
|
||||||
|
|
||||||
namespace olive
|
namespace olive
|
||||||
|
|||||||
@@ -25,7 +25,6 @@
|
|||||||
#include <QVector2D>
|
#include <QVector2D>
|
||||||
|
|
||||||
#include "common/util.h"
|
#include "common/util.h"
|
||||||
#include "core.h"
|
|
||||||
#include "node/nodeundo.h"
|
#include "node/nodeundo.h"
|
||||||
|
|
||||||
namespace olive
|
namespace olive
|
||||||
|
|||||||
@@ -26,7 +26,7 @@
|
|||||||
#include <QTextDocument>
|
#include <QTextDocument>
|
||||||
|
|
||||||
#include "common/html.h"
|
#include "common/html.h"
|
||||||
#include "core.h"
|
#include "coreengine.h"
|
||||||
#include "node/project.h"
|
#include "node/project.h"
|
||||||
#include "node/nodeundo.h"
|
#include "node/nodeundo.h"
|
||||||
|
|
||||||
@@ -300,7 +300,7 @@ void TextGeneratorV3::gizmo_deactivated()
|
|||||||
|
|
||||||
void TextGeneratorV3::set_vertical_alignment_undoable(Qt::Alignment a)
|
void TextGeneratorV3::set_vertical_alignment_undoable(Qt::Alignment a)
|
||||||
{
|
{
|
||||||
Core::instance()->undo_stack()->push(
|
EngineCore::instance()->undo_stack()->push(
|
||||||
new NodeParamSetStandardValueCommand(NodeInput(this,
|
new NodeParamSetStandardValueCommand(NodeInput(this,
|
||||||
k_vertical_alignment_input),
|
k_vertical_alignment_input),
|
||||||
get_our_alignment_from_qts(a)),
|
get_our_alignment_from_qts(a)),
|
||||||
|
|||||||
@@ -21,7 +21,7 @@
|
|||||||
|
|
||||||
#include "text.h"
|
#include "text.h"
|
||||||
|
|
||||||
#include "core.h"
|
#include "coreengine.h"
|
||||||
#include "undo/undocommand.h"
|
#include "undo/undocommand.h"
|
||||||
|
|
||||||
namespace olive
|
namespace olive
|
||||||
@@ -45,7 +45,7 @@ void TextGizmo::update_input_html(const QString &s, const Rational &time)
|
|||||||
MultiUndoCommand *command = new MultiUndoCommand();
|
MultiUndoCommand *command = new MultiUndoCommand();
|
||||||
Node::set_value_at_time(input_.input(), time, s, input_.track(), command,
|
Node::set_value_at_time(input_.input(), time, s, input_.track(), command,
|
||||||
true);
|
true);
|
||||||
Core::instance()->undo_stack()->push(command, tr("Edit Text"));
|
EngineCore::instance()->undo_stack()->push(command, tr("Edit Text"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,7 +21,6 @@
|
|||||||
|
|
||||||
#include "inputdragger.h"
|
#include "inputdragger.h"
|
||||||
|
|
||||||
#include "core.h"
|
|
||||||
#include "node.h"
|
#include "node.h"
|
||||||
#include "nodeundo.h"
|
#include "nodeundo.h"
|
||||||
|
|
||||||
|
|||||||
@@ -27,7 +27,6 @@
|
|||||||
#include <QFile>
|
#include <QFile>
|
||||||
|
|
||||||
#include "common/lerp.h"
|
#include "common/lerp.h"
|
||||||
#include "core.h"
|
|
||||||
#include "config/config.h"
|
#include "config/config.h"
|
||||||
#include "node/group/group.h"
|
#include "node/group/group.h"
|
||||||
#include "node/project/serializer/typeserializer.h"
|
#include "node/project/serializer/typeserializer.h"
|
||||||
|
|||||||
@@ -22,7 +22,7 @@
|
|||||||
#include "viewer.h"
|
#include "viewer.h"
|
||||||
|
|
||||||
#include "config/config.h"
|
#include "config/config.h"
|
||||||
#include "core.h"
|
#include "coreengine.h"
|
||||||
#include "node/traverser.h"
|
#include "node/traverser.h"
|
||||||
|
|
||||||
namespace olive
|
namespace olive
|
||||||
@@ -107,7 +107,7 @@ QVariant ViewerOutput::data(const DataType &d) const
|
|||||||
case duration: {
|
case duration: {
|
||||||
Rational using_timebase;
|
Rational using_timebase;
|
||||||
Timecode::Display using_display =
|
Timecode::Display using_display =
|
||||||
Core::instance()->get_timecode_display();
|
EngineCore::instance()->get_timecode_display();
|
||||||
|
|
||||||
// Get first enabled streams
|
// Get first enabled streams
|
||||||
VideoParams video = get_first_enabled_video_stream();
|
VideoParams video = get_first_enabled_video_stream();
|
||||||
|
|||||||
@@ -27,7 +27,6 @@
|
|||||||
#include "common/current.h"
|
#include "common/current.h"
|
||||||
#include "common/qtutils.h"
|
#include "common/qtutils.h"
|
||||||
#include "common/xmlutils.h"
|
#include "common/xmlutils.h"
|
||||||
#include "core.h"
|
|
||||||
#include "node/color/ociobase/ociobase.h"
|
#include "node/color/ociobase/ociobase.h"
|
||||||
#include "node/factory.h"
|
#include "node/factory.h"
|
||||||
#include "node/group/group.h"
|
#include "node/group/group.h"
|
||||||
|
|||||||
@@ -27,13 +27,15 @@
|
|||||||
#include <QImage>
|
#include <QImage>
|
||||||
#include <QPainter>
|
#include <QPainter>
|
||||||
#include <QStandardPaths>
|
#include <QStandardPaths>
|
||||||
|
#include <QTimer>
|
||||||
|
|
||||||
#include "codec/decoder.h"
|
#include "codec/decoder.h"
|
||||||
#include "common/filefunctions.h"
|
#include "common/filefunctions.h"
|
||||||
#include "common/qtutils.h"
|
#include "common/qtutils.h"
|
||||||
#include "common/xmlutils.h"
|
#include "common/xmlutils.h"
|
||||||
#include "config/config.h"
|
#include "config/config.h"
|
||||||
#include "core.h"
|
#include "node/color/colormanager/colormanager.h"
|
||||||
|
#include "node/project.h"
|
||||||
#include "render/job/footagejob.h"
|
#include "render/job/footagejob.h"
|
||||||
#include "ui/icons/icons.h"
|
#include "ui/icons/icons.h"
|
||||||
|
|
||||||
|
|||||||
@@ -26,7 +26,7 @@
|
|||||||
#include <QXmlStreamReader>
|
#include <QXmlStreamReader>
|
||||||
|
|
||||||
#include "common/xmlutils.h"
|
#include "common/xmlutils.h"
|
||||||
#include "core.h"
|
#include "coreengine.h"
|
||||||
#include "node/group/group.h"
|
#include "node/group/group.h"
|
||||||
#include "serializer190219.h"
|
#include "serializer190219.h"
|
||||||
#include "serializer210528.h"
|
#include "serializer210528.h"
|
||||||
@@ -156,7 +156,7 @@ ProjectSerializer::Result ProjectSerializer::load(Project *project,
|
|||||||
ProjectSerializer::Result ProjectSerializer::paste(LoadType load_type,
|
ProjectSerializer::Result ProjectSerializer::paste(LoadType load_type,
|
||||||
Project *project)
|
Project *project)
|
||||||
{
|
{
|
||||||
QString clipboard = Core::paste_string_from_clipboard();
|
QString clipboard = EngineCore::paste_string_from_clipboard();
|
||||||
if (clipboard.isEmpty()) {
|
if (clipboard.isEmpty()) {
|
||||||
return k_no_data;
|
return k_no_data;
|
||||||
}
|
}
|
||||||
@@ -257,7 +257,7 @@ ProjectSerializer::Result ProjectSerializer::copy(const SaveData &data)
|
|||||||
ProjectSerializer::Result res = ProjectSerializer::save(&writer, data);
|
ProjectSerializer::Result res = ProjectSerializer::save(&writer, data);
|
||||||
|
|
||||||
if (res == k_success) {
|
if (res == k_success) {
|
||||||
Core::copy_string_to_clipboard(copy_str);
|
EngineCore::copy_string_to_clipboard(copy_str);
|
||||||
}
|
}
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
|
|||||||
@@ -22,7 +22,7 @@
|
|||||||
#include "ofxCore.h"
|
#include "ofxCore.h"
|
||||||
#include "ofxMessage.h"
|
#include "ofxMessage.h"
|
||||||
#include "common/current.h"
|
#include "common/current.h"
|
||||||
#include "core.h"
|
#include "coreengine.h"
|
||||||
#include "dialog/progress/progress.h"
|
#include "dialog/progress/progress.h"
|
||||||
#include "node/output/viewer/viewer.h"
|
#include "node/output/viewer/viewer.h"
|
||||||
#include "panel/panelmanager.h"
|
#include "panel/panelmanager.h"
|
||||||
@@ -401,7 +401,7 @@ OfxStatus OlivePluginInstance::editEnd()
|
|||||||
"Edit Parameters");
|
"Edit Parameters");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Core::instance()->undo_stack()->push(edit_command_, label);
|
EngineCore::instance()->undo_stack()->push(edit_command_, label);
|
||||||
edit_command_ = nullptr;
|
edit_command_ = nullptr;
|
||||||
edit_label_.clear();
|
edit_label_.clear();
|
||||||
edit_first_label_.clear();
|
edit_first_label_.clear();
|
||||||
@@ -437,7 +437,7 @@ void OlivePluginInstance::submit_undo_command(UndoCommand *command,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Core::instance()->undo_stack()->push(command, label);
|
EngineCore::instance()->undo_stack()->push(command, label);
|
||||||
}
|
}
|
||||||
|
|
||||||
void OlivePluginInstance::progressStart(const std::string &message,
|
void OlivePluginInstance::progressStart(const std::string &message,
|
||||||
|
|||||||
@@ -19,6 +19,7 @@
|
|||||||
|
|
||||||
#include "paraminstance.h"
|
#include "paraminstance.h"
|
||||||
|
|
||||||
|
#include "coreengine.h"
|
||||||
#include "oliveplugininstance.h"
|
#include "oliveplugininstance.h"
|
||||||
|
|
||||||
namespace olive
|
namespace olive
|
||||||
@@ -47,7 +48,7 @@ void submit_undo_command(const std::shared_ptr<PluginNode> &node,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Core::instance()->undo_stack()->push(command, label);
|
EngineCore::instance()->undo_stack()->push(command, label);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,7 +30,6 @@
|
|||||||
#include "ofxhParam.h"
|
#include "ofxhParam.h"
|
||||||
#include "node/nodeundo.h"
|
#include "node/nodeundo.h"
|
||||||
#include "node/plugins/plugin.h"
|
#include "node/plugins/plugin.h"
|
||||||
#include "core.h"
|
|
||||||
#include "undo/undocommand.h"
|
#include "undo/undocommand.h"
|
||||||
#include "common/current.h"
|
#include "common/current.h"
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|||||||
@@ -30,7 +30,7 @@
|
|||||||
|
|
||||||
#include "common/filefunctions.h"
|
#include "common/filefunctions.h"
|
||||||
#include "config/config.h"
|
#include "config/config.h"
|
||||||
#include "core.h"
|
#include "coreengine.h"
|
||||||
#include "dialog/diskcache/diskcachedialog.h"
|
#include "dialog/diskcache/diskcachedialog.h"
|
||||||
|
|
||||||
namespace olive
|
namespace olive
|
||||||
@@ -370,7 +370,7 @@ bool DiskCacheFolder::delete_least_recent()
|
|||||||
bool e = delete_file_internal(hash_to_delete);
|
bool e = delete_file_internal(hash_to_delete);
|
||||||
|
|
||||||
if (e) {
|
if (e) {
|
||||||
Core::instance()->warn_cache_full();
|
EngineCore::instance()->warn_cache_full();
|
||||||
}
|
}
|
||||||
|
|
||||||
return e;
|
return e;
|
||||||
|
|||||||
@@ -41,7 +41,7 @@
|
|||||||
#include <QVector3D>
|
#include <QVector3D>
|
||||||
#include <QVector4D>
|
#include <QVector4D>
|
||||||
#include "pluginrenderer.h"
|
#include "pluginrenderer.h"
|
||||||
#include "core.h"
|
#include "coreengine.h"
|
||||||
#include "undo/undostack.h"
|
#include "undo/undostack.h"
|
||||||
#include "pluginSupport/oliveclip.h"
|
#include "pluginSupport/oliveclip.h"
|
||||||
#include "pluginSupport/oliveplugininstance.h"
|
#include "pluginSupport/oliveplugininstance.h"
|
||||||
@@ -1350,7 +1350,7 @@ static void mark_render_failure(olive::TexturePtr destination)
|
|||||||
/// Show an error dialog and undo the last operation. Must be called from the GUI thread.
|
/// Show an error dialog and undo the last operation. Must be called from the GUI thread.
|
||||||
static void show_error_dialog_and_undo(const QString &message)
|
static void show_error_dialog_and_undo(const QString &message)
|
||||||
{
|
{
|
||||||
if (auto *core = olive::Core::instance()) {
|
if (auto *core = olive::EngineCore::instance()) {
|
||||||
if (auto *stack = core->undo_stack()) {
|
if (auto *stack = core->undo_stack()) {
|
||||||
if (stack->can_undo()) {
|
if (stack->can_undo()) {
|
||||||
stack->undo();
|
stack->undo();
|
||||||
|
|||||||
@@ -25,7 +25,6 @@
|
|||||||
#include <QThread>
|
#include <QThread>
|
||||||
|
|
||||||
#include "config/config.h"
|
#include "config/config.h"
|
||||||
#include "core.h"
|
|
||||||
#ifdef OAK_ENABLE_DYNAMIC_RENDER_BACKEND
|
#ifdef OAK_ENABLE_DYNAMIC_RENDER_BACKEND
|
||||||
#include "render/backend/dynamicrenderer.h"
|
#include "render/backend/dynamicrenderer.h"
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -26,7 +26,7 @@
|
|||||||
#include <QCoreApplication>
|
#include <QCoreApplication>
|
||||||
#include <QtMath>
|
#include <QtMath>
|
||||||
|
|
||||||
#include "core.h"
|
#include "common/xmlutils.h"
|
||||||
#include "ofxImageEffect.h"
|
#include "ofxImageEffect.h"
|
||||||
|
|
||||||
namespace olive
|
namespace olive
|
||||||
|
|||||||
@@ -25,7 +25,7 @@
|
|||||||
#include <QFileInfo>
|
#include <QFileInfo>
|
||||||
|
|
||||||
#include "config/config.h"
|
#include "config/config.h"
|
||||||
#include "core.h"
|
#include "coreengine.h"
|
||||||
#include "node/nodeundo.h"
|
#include "node/nodeundo.h"
|
||||||
#include "node/project/footage/footage.h"
|
#include "node/project/footage/footage.h"
|
||||||
|
|
||||||
@@ -41,7 +41,7 @@ ProjectImportTask::ProjectImportTask(Folder *folder,
|
|||||||
filenames_.append(QFileInfo(f));
|
filenames_.append(QFileInfo(f));
|
||||||
}
|
}
|
||||||
|
|
||||||
file_count_ = Core::count_files_in_file_list(filenames_);
|
file_count_ = EngineCore::count_files_in_file_list(filenames_);
|
||||||
|
|
||||||
set_title(tr("Importing %n file(s)", nullptr, file_count_));
|
set_title(tr("Importing %n file(s)", nullptr, file_count_));
|
||||||
}
|
}
|
||||||
@@ -176,7 +176,7 @@ void ProjectImportTask::validate_image_sequence(Footage *footage,
|
|||||||
// user just in case...
|
// user just in case...
|
||||||
bool is_sequence;
|
bool is_sequence;
|
||||||
|
|
||||||
QMetaObject::invokeMethod(Core::instance(), "confirm_image_sequence",
|
QMetaObject::invokeMethod(EngineCore::instance(), "confirm_image_sequence",
|
||||||
Qt::BlockingQueuedConnection,
|
Qt::BlockingQueuedConnection,
|
||||||
Q_RETURN_ARG(bool, is_sequence),
|
Q_RETURN_ARG(bool, is_sequence),
|
||||||
Q_ARG(QString, footage->filename()));
|
Q_ARG(QString, footage->filename()));
|
||||||
|
|||||||
@@ -33,7 +33,7 @@
|
|||||||
#include <QFileInfo>
|
#include <QFileInfo>
|
||||||
#include <QThread>
|
#include <QThread>
|
||||||
|
|
||||||
#include "core.h"
|
#include "coreengine.h"
|
||||||
#include "node/audio/volume/volume.h"
|
#include "node/audio/volume/volume.h"
|
||||||
#include "node/block/clip/clip.h"
|
#include "node/block/clip/clip.h"
|
||||||
#include "node/block/gap/gap.h"
|
#include "node/block/gap/gap.h"
|
||||||
@@ -134,7 +134,7 @@ bool LoadOTIOTask::Run()
|
|||||||
// Dialog has to be called from the main thread so we pass the list of sequences here.
|
// Dialog has to be called from the main thread so we pass the list of sequences here.
|
||||||
bool accepted = false;
|
bool accepted = false;
|
||||||
QMetaObject::invokeMethod(
|
QMetaObject::invokeMethod(
|
||||||
Core::instance(), "DialogImportOTIOShow", Qt::BlockingQueuedConnection,
|
EngineCore::instance(), "show_otio_import_dialog", Qt::BlockingQueuedConnection,
|
||||||
Q_RETURN_ARG(bool, accepted),
|
Q_RETURN_ARG(bool, accepted),
|
||||||
Q_ARG(QList<Sequence *>, timeline_sequnce_map.values()));
|
Q_ARG(QList<Sequence *>, timeline_sequnce_map.values()));
|
||||||
|
|
||||||
|
|||||||
@@ -26,7 +26,6 @@
|
|||||||
#include <QXmlStreamWriter>
|
#include <QXmlStreamWriter>
|
||||||
|
|
||||||
#include "common/filefunctions.h"
|
#include "common/filefunctions.h"
|
||||||
#include "core.h"
|
|
||||||
#include "node/project/serializer/serializer.h"
|
#include "node/project/serializer/serializer.h"
|
||||||
|
|
||||||
namespace olive
|
namespace olive
|
||||||
|
|||||||
@@ -26,7 +26,7 @@
|
|||||||
#include "common/qtutils.h"
|
#include "common/qtutils.h"
|
||||||
#include "common/xmlutils.h"
|
#include "common/xmlutils.h"
|
||||||
#include "config/config.h"
|
#include "config/config.h"
|
||||||
#include "core.h"
|
#include "node/project.h"
|
||||||
#include "ui/colorcoding.h"
|
#include "ui/colorcoding.h"
|
||||||
|
|
||||||
namespace olive
|
namespace olive
|
||||||
|
|||||||
@@ -21,7 +21,7 @@
|
|||||||
|
|
||||||
#include "undocommand.h"
|
#include "undocommand.h"
|
||||||
|
|
||||||
#include "core.h"
|
#include "node/project.h"
|
||||||
|
|
||||||
namespace olive
|
namespace olive
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -40,7 +40,7 @@
|
|||||||
|
|
||||||
#include "common/qtutils.h"
|
#include "common/qtutils.h"
|
||||||
#include "config/config.h"
|
#include "config/config.h"
|
||||||
#include "core.h"
|
#include "coreengine.h"
|
||||||
#include "node/factory.h"
|
#include "node/factory.h"
|
||||||
#include "node/input/multicam/multicamnode.h"
|
#include "node/input/multicam/multicamnode.h"
|
||||||
#include "node/project/serializer/serializer.h"
|
#include "node/project/serializer/serializer.h"
|
||||||
@@ -128,11 +128,13 @@ public:
|
|||||||
|
|
||||||
bool initialize_runtime()
|
bool initialize_runtime()
|
||||||
{
|
{
|
||||||
// Create a minimal Core instance so that code paths calling Core::instance()
|
// Create a minimal EngineCore instance so that code paths calling
|
||||||
// (e.g. ViewerOutput::data for timecode display) do not dereference null.
|
// EngineCore::instance() (e.g. ViewerOutput::data for timecode display)
|
||||||
// The worker is short-lived; leaking this on exit is harmless.
|
// do not dereference null. The worker has no UI, so the plain engine
|
||||||
if (!olive::Core::instance()) {
|
// core is sufficient. The worker is short-lived; leaking this on exit
|
||||||
new olive::Core(olive::Core::CoreParams());
|
// is harmless.
|
||||||
|
if (!olive::EngineCore::instance()) {
|
||||||
|
new olive::EngineCore(olive::EngineCore::CoreParams());
|
||||||
}
|
}
|
||||||
|
|
||||||
olive::Config::load();
|
olive::Config::load();
|
||||||
|
|||||||
Reference in New Issue
Block a user