refactor(timeline): de-Qt oaktimeline and wrap it in a pure C ABI
- de-Qt all 11 sources: markers/workarea lose QObject/signals (list re-sort via direct resort() call, drawing moves to the app layer), explicit unique_ptr ownership for marker lists and viewer's workarea/markers - timeline undo command families (split/pointer/ripple/general/track) now hold oaknode C handles and do all graph work through the oaknode C ABI (no adapter layer); gaps are attached to the project graph before insertion, orphan handles tracked and freed - fix the de-Qt severed Block->Track length-changed chain with a direct block_length_changed() call (positions were stale after split/trim) - expand oaknode C API by ~20 functions needed by timeline (copy_in_graph, block kind/casts, connect_element, input arrays, tracklist family, marker/workarea borrowed outlets) - pure C ABI in include/timeline (marker/workarea/edit, command factories returning OakUndoCommand), oakcommon XML get_native accessors - oaknode<->oaktimeline resolve each other at runtime; standalone drivers link both into test binaries - tests: oaktimeline 117, regressions oakcommon 193 / oaknode 96 / oakrender 42 / oakcodec 18 / oakaudio 36 all green
This commit is contained in:
@@ -475,3 +475,57 @@ int oaknode_transition_get_connected_in_block(OakNodeBlock *transition,
|
||||
*out = wrap(t->connected_in_block());
|
||||
return OAKNODE_OK;
|
||||
}
|
||||
|
||||
int oaknode_clip_add_cache_passthrough_from(OakNodeBlock *clip,
|
||||
OakNodeBlock *other)
|
||||
{
|
||||
if (!clip || !other) {
|
||||
return OAKNODE_E_INVALID;
|
||||
}
|
||||
|
||||
olive::ClipBlock *c = clip_impl(clip);
|
||||
olive::ClipBlock *o = clip_impl(other);
|
||||
if (!c || !o) {
|
||||
return OAKNODE_E_INVALID;
|
||||
}
|
||||
|
||||
try {
|
||||
c->add_cache_passthrough_from(o);
|
||||
return OAKNODE_OK;
|
||||
} catch (...) {
|
||||
return OAKNODE_E_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
OakNodeNode *oaknode_block_as_node(OakNodeBlock *block)
|
||||
{
|
||||
return reinterpret_cast<OakNodeNode *>(block);
|
||||
}
|
||||
|
||||
OakNodeBlock *oaknode_block_from_node(OakNodeNode *node)
|
||||
{
|
||||
if (!node) {
|
||||
return NULL;
|
||||
}
|
||||
olive::Node *n = reinterpret_cast<olive::Node *>(node);
|
||||
return wrap(dynamic_cast<olive::Block *>(n));
|
||||
}
|
||||
|
||||
int oaknode_block_get_kind(OakNodeBlock *block, int *out_kind)
|
||||
{
|
||||
if (!block || !out_kind) {
|
||||
return OAKNODE_E_INVALID;
|
||||
}
|
||||
|
||||
olive::Block *b = impl(block);
|
||||
if (dynamic_cast<olive::TransitionBlock *>(b)) {
|
||||
*out_kind = OAKNODE_BLOCK_TRANSITION;
|
||||
} else if (dynamic_cast<olive::ClipBlock *>(b)) {
|
||||
*out_kind = OAKNODE_BLOCK_CLIP;
|
||||
} else if (dynamic_cast<olive::GapBlock *>(b)) {
|
||||
*out_kind = OAKNODE_BLOCK_GAP;
|
||||
} else {
|
||||
*out_kind = OAKNODE_BLOCK_OTHER;
|
||||
}
|
||||
return OAKNODE_OK;
|
||||
}
|
||||
|
||||
@@ -25,6 +25,10 @@
|
||||
|
||||
#include "nodeundo.h"
|
||||
|
||||
#include "project.h"
|
||||
|
||||
#include "output/viewer/viewer.h"
|
||||
|
||||
#include "valueconvert.h"
|
||||
|
||||
namespace
|
||||
@@ -1028,3 +1032,201 @@ void oaknode_node_free(OakNodeNode *node)
|
||||
} catch (...) {
|
||||
}
|
||||
}
|
||||
|
||||
OakNodeNode *oaknode_node_copy_in_graph(OakNodeNode *node,
|
||||
OakUndoCommand **out_command)
|
||||
{
|
||||
if (!node || !out_command) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
try {
|
||||
auto *command = new olive::MultiUndoCommand();
|
||||
olive::Node *copy =
|
||||
olive::Node::copy_node_in_graph(to_node(node), command);
|
||||
if (!copy) {
|
||||
delete command;
|
||||
return NULL;
|
||||
}
|
||||
*out_command = oaknode_c_api::wrap_command(command);
|
||||
if (!*out_command) {
|
||||
delete command;
|
||||
return NULL;
|
||||
}
|
||||
oaknode_c_api::alive_inc();
|
||||
return from_node(copy);
|
||||
} catch (...) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
OakUndoCommand *oaknode_command_create_remove_node(OakNodeNode *node)
|
||||
{
|
||||
if (!node) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
try {
|
||||
return oaknode_c_api::wrap_command(
|
||||
new olive::NodeRemoveWithExclusiveDependenciesAndDisconnect(
|
||||
to_node(node)));
|
||||
} catch (...) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
int oaknode_node_get_project(const OakNodeNode *node, OakNodeProject **out)
|
||||
{
|
||||
if (!node || !out) {
|
||||
return OAKNODE_E_INVALID;
|
||||
}
|
||||
*out = reinterpret_cast<OakNodeProject *>(to_node(node)->project());
|
||||
return OAKNODE_OK;
|
||||
}
|
||||
|
||||
int oaknode_node_input_array_insert(OakNodeNode *node, const char *input_id,
|
||||
int index)
|
||||
{
|
||||
if (!node || !input_id) {
|
||||
return OAKNODE_E_INVALID;
|
||||
}
|
||||
if (!has_input(to_node(node), input_id)) {
|
||||
return OAKNODE_E_NOT_FOUND;
|
||||
}
|
||||
|
||||
try {
|
||||
to_node(node)->input_array_insert(input_id, index);
|
||||
return OAKNODE_OK;
|
||||
} catch (...) {
|
||||
return OAKNODE_E_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
int oaknode_node_input_array_remove(OakNodeNode *node, const char *input_id,
|
||||
int index)
|
||||
{
|
||||
if (!node || !input_id) {
|
||||
return OAKNODE_E_INVALID;
|
||||
}
|
||||
if (!has_input(to_node(node), input_id)) {
|
||||
return OAKNODE_E_NOT_FOUND;
|
||||
}
|
||||
|
||||
try {
|
||||
to_node(node)->input_array_remove(input_id, index);
|
||||
return OAKNODE_OK;
|
||||
} catch (...) {
|
||||
return OAKNODE_E_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
int oaknode_node_connect_element(OakNodeNode *output_node,
|
||||
OakNodeNode *input_node,
|
||||
const char *input_id, int element)
|
||||
{
|
||||
if (!output_node || !input_node) {
|
||||
return OAKNODE_E_INVALID;
|
||||
}
|
||||
if (!has_input(to_node(input_node), input_id)) {
|
||||
return OAKNODE_E_NOT_FOUND;
|
||||
}
|
||||
|
||||
try {
|
||||
olive::Node::connect_edge(
|
||||
to_node(output_node),
|
||||
olive::NodeInput(to_node(input_node), input_id, element));
|
||||
return OAKNODE_OK;
|
||||
} catch (...) {
|
||||
return OAKNODE_E_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
int oaknode_node_disconnect_element(OakNodeNode *input_node,
|
||||
const char *input_id, int element)
|
||||
{
|
||||
if (!input_node) {
|
||||
return OAKNODE_E_INVALID;
|
||||
}
|
||||
if (!has_input(to_node(input_node), input_id)) {
|
||||
return OAKNODE_E_NOT_FOUND;
|
||||
}
|
||||
|
||||
try {
|
||||
olive::Node *output = to_node(input_node)->get_connected_output(
|
||||
input_id, element);
|
||||
if (!output) {
|
||||
return OAKNODE_E_NOT_FOUND;
|
||||
}
|
||||
|
||||
olive::Node::disconnect_edge(
|
||||
output, olive::NodeInput(to_node(input_node), input_id, element));
|
||||
return OAKNODE_OK;
|
||||
} catch (...) {
|
||||
return OAKNODE_E_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
OakUndoCommand *oaknode_command_create_add_node(OakNodeProject *graph,
|
||||
OakNodeNode *node)
|
||||
{
|
||||
if (!graph || !node) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
try {
|
||||
return oaknode_c_api::wrap_command(
|
||||
new olive::NodeAddCommand(
|
||||
reinterpret_cast<olive::Project *>(graph), to_node(node)));
|
||||
} catch (...) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
OakUndoCommand *oaknode_command_create_set_position_recursive(
|
||||
OakNodeNode *node, OakNodeNode *context, double x, double y)
|
||||
{
|
||||
if (!node || !context) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
try {
|
||||
return oaknode_c_api::wrap_command(
|
||||
new olive::NodeSetPositionAndDependenciesRecursivelyCommand(
|
||||
to_node(node), to_node(context),
|
||||
olive::Node::Position(olive::PointF(x, y))));
|
||||
} catch (...) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
int oaknode_node_get_markers(const OakNodeNode *node,
|
||||
OakNodeMarkerList **out)
|
||||
{
|
||||
if (!node || !out) {
|
||||
return OAKNODE_E_INVALID;
|
||||
}
|
||||
|
||||
const olive::Node *n = to_node(node);
|
||||
if (auto *v = dynamic_cast<const olive::ViewerOutput *>(n)) {
|
||||
*out = reinterpret_cast<OakNodeMarkerList *>(v->get_markers());
|
||||
} else {
|
||||
*out = NULL;
|
||||
}
|
||||
return OAKNODE_OK;
|
||||
}
|
||||
|
||||
int oaknode_node_get_work_area(const OakNodeNode *node,
|
||||
OakNodeWorkArea **out)
|
||||
{
|
||||
if (!node || !out) {
|
||||
return OAKNODE_E_INVALID;
|
||||
}
|
||||
|
||||
const olive::Node *n = to_node(node);
|
||||
if (auto *v = dynamic_cast<const olive::ViewerOutput *>(n)) {
|
||||
*out = reinterpret_cast<OakNodeWorkArea *>(v->get_work_area());
|
||||
} else {
|
||||
*out = NULL;
|
||||
}
|
||||
return OAKNODE_OK;
|
||||
}
|
||||
|
||||
@@ -318,3 +318,8 @@ int oaknode_sequence_set_audio_params(OakNodeSequence *sequence, int index,
|
||||
}
|
||||
return OAKNODE_OK;
|
||||
}
|
||||
|
||||
OakNodeNode *oaknode_sequence_as_node(OakNodeSequence *sequence)
|
||||
{
|
||||
return reinterpret_cast<OakNodeNode *>(sequence);
|
||||
}
|
||||
|
||||
@@ -24,6 +24,8 @@
|
||||
|
||||
#include "alivecount.h"
|
||||
|
||||
#include "valueconvert.h"
|
||||
|
||||
#include "block/block.h"
|
||||
#include "output/track/track.h"
|
||||
#include "output/track/tracklist.h"
|
||||
@@ -572,3 +574,89 @@ int oaknode_tracklist_remove_track(OakNodeTrackList *list, OakNodeTrack *track)
|
||||
}
|
||||
return OAKNODE_OK;
|
||||
}
|
||||
|
||||
int oaknode_track_get_nearest_block_before_or_at(OakNodeTrack *track,
|
||||
int numerator, int denominator, OakNodeBlock **out)
|
||||
{
|
||||
olive::Track *t = impl(track);
|
||||
if (!t || !out) {
|
||||
return OAKNODE_E_INVALID;
|
||||
}
|
||||
*out = wrap_block(
|
||||
t->nearest_block_before_or_at(olive::core::Rational(numerator, denominator)));
|
||||
return OAKNODE_OK;
|
||||
}
|
||||
|
||||
int oaknode_track_get_nearest_block_after_or_at(OakNodeTrack *track,
|
||||
int numerator, int denominator, OakNodeBlock **out)
|
||||
{
|
||||
olive::Track *t = impl(track);
|
||||
if (!t || !out) {
|
||||
return OAKNODE_E_INVALID;
|
||||
}
|
||||
*out = wrap_block(
|
||||
t->nearest_block_after_or_at(olive::core::Rational(numerator, denominator)));
|
||||
return OAKNODE_OK;
|
||||
}
|
||||
|
||||
int oaknode_tracklist_get_sequence(OakNodeTrackList *list,
|
||||
OakNodeSequence **out)
|
||||
{
|
||||
if (!list || !out) {
|
||||
return OAKNODE_E_INVALID;
|
||||
}
|
||||
*out = reinterpret_cast<OakNodeSequence *>(list_impl(list)->parent());
|
||||
return OAKNODE_OK;
|
||||
}
|
||||
|
||||
int oaknode_tracklist_get_track_input_id(OakNodeTrackList *list, char *buf,
|
||||
int buf_size)
|
||||
{
|
||||
if (!list) {
|
||||
return OAKNODE_E_INVALID;
|
||||
}
|
||||
return oaknode_c_api::copy_string(list_impl(list)->track_input(), buf,
|
||||
buf_size);
|
||||
}
|
||||
|
||||
int oaknode_tracklist_array_append(OakNodeTrackList *list)
|
||||
{
|
||||
if (!list) {
|
||||
return OAKNODE_E_INVALID;
|
||||
}
|
||||
try {
|
||||
list_impl(list)->array_append();
|
||||
return OAKNODE_OK;
|
||||
} catch (...) {
|
||||
return OAKNODE_E_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
int oaknode_tracklist_array_remove_last(OakNodeTrackList *list)
|
||||
{
|
||||
if (!list) {
|
||||
return OAKNODE_E_INVALID;
|
||||
}
|
||||
try {
|
||||
list_impl(list)->array_remove_last();
|
||||
return OAKNODE_OK;
|
||||
} catch (...) {
|
||||
return OAKNODE_E_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
int oaknode_tracklist_get_array_index_from_cache_index(
|
||||
OakNodeTrackList *list, int cache_index, int *out_index)
|
||||
{
|
||||
if (!list || !out_index) {
|
||||
return OAKNODE_E_INVALID;
|
||||
}
|
||||
*out_index =
|
||||
list_impl(list)->get_array_index_from_cache_index(cache_index);
|
||||
return OAKNODE_OK;
|
||||
}
|
||||
|
||||
OakNodeNode *oaknode_track_as_node(OakNodeTrack *track)
|
||||
{
|
||||
return reinterpret_cast<OakNodeNode *>(track);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user