Remove SEQ_ prefix for blender::seq namespace and ED_sequencer for blender::ed::vse namespace Pull Request: https://projects.blender.org/blender/blender/pulls/135560
1024 lines
29 KiB
C++
1024 lines
29 KiB
C++
/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
|
|
*
|
|
* SPDX-License-Identifier: GPL-2.0-or-later */
|
|
|
|
/** \file
|
|
* \ingroup spseq
|
|
*/
|
|
|
|
#include "BLI_listbase.h"
|
|
#include "BLI_map.hh"
|
|
#include "BLI_math_base.h"
|
|
#include "BLI_set.hh"
|
|
|
|
#include "DNA_scene_types.h"
|
|
|
|
#include "BKE_context.hh"
|
|
#include "BKE_report.hh"
|
|
#include "BKE_scene.hh"
|
|
|
|
#include "ED_select_utils.hh"
|
|
#include "ED_sequencer.hh"
|
|
|
|
#include "SEQ_connect.hh"
|
|
#include "SEQ_iterator.hh"
|
|
#include "SEQ_relations.hh"
|
|
#include "SEQ_retiming.hh"
|
|
#include "SEQ_select.hh"
|
|
#include "SEQ_sequencer.hh"
|
|
#include "SEQ_time.hh"
|
|
#include "SEQ_transform.hh"
|
|
|
|
#include "WM_api.hh"
|
|
|
|
#include "RNA_define.hh"
|
|
|
|
#include "UI_view2d.hh"
|
|
|
|
/* Own include. */
|
|
#include "sequencer_intern.hh"
|
|
|
|
namespace blender::ed::vse {
|
|
|
|
bool sequencer_retiming_mode_is_active(const bContext *C)
|
|
{
|
|
Editing *ed = seq::editing_get(CTX_data_scene(C));
|
|
if (ed == nullptr) {
|
|
return false;
|
|
}
|
|
return seq::retiming_selection_get(ed).size() > 0;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------- */
|
|
/** \name Retiming Data Show
|
|
* \{ */
|
|
|
|
static void sequencer_retiming_data_show_selection(ListBase *seqbase)
|
|
{
|
|
LISTBASE_FOREACH (Strip *, strip, seqbase) {
|
|
if ((strip->flag & SELECT) == 0) {
|
|
continue;
|
|
}
|
|
if (!seq::retiming_is_allowed(strip)) {
|
|
continue;
|
|
}
|
|
strip->flag |= SEQ_SHOW_RETIMING;
|
|
}
|
|
}
|
|
|
|
static void sequencer_retiming_data_hide_selection(ListBase *seqbase)
|
|
{
|
|
LISTBASE_FOREACH (Strip *, strip, seqbase) {
|
|
if ((strip->flag & SELECT) == 0) {
|
|
continue;
|
|
}
|
|
if (!seq::retiming_is_allowed(strip)) {
|
|
continue;
|
|
}
|
|
strip->flag &= ~SEQ_SHOW_RETIMING;
|
|
}
|
|
}
|
|
|
|
static void sequencer_retiming_data_hide_all(ListBase *seqbase)
|
|
{
|
|
LISTBASE_FOREACH (Strip *, strip, seqbase) {
|
|
strip->flag &= ~SEQ_SHOW_RETIMING;
|
|
}
|
|
}
|
|
|
|
static int sequencer_retiming_data_show_exec(bContext *C, wmOperator * /*op*/)
|
|
{
|
|
Scene *scene = CTX_data_scene(C);
|
|
Editing *ed = seq::editing_get(scene);
|
|
Strip *strip_act = seq::select_active_get(scene);
|
|
|
|
if (strip_act == nullptr) {
|
|
return OPERATOR_CANCELLED;
|
|
}
|
|
|
|
if (sequencer_retiming_mode_is_active(C)) {
|
|
sequencer_retiming_data_hide_all(ed->seqbasep);
|
|
}
|
|
else if (seq::retiming_data_is_editable(strip_act)) {
|
|
sequencer_retiming_data_hide_selection(ed->seqbasep);
|
|
}
|
|
else {
|
|
sequencer_retiming_data_show_selection(ed->seqbasep);
|
|
}
|
|
|
|
WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene);
|
|
return OPERATOR_FINISHED;
|
|
}
|
|
|
|
void SEQUENCER_OT_retiming_show(wmOperatorType *ot)
|
|
{
|
|
/* identifiers */
|
|
ot->name = "Retime Strips";
|
|
ot->description = "Show retiming keys in selected strips";
|
|
ot->idname = "SEQUENCER_OT_retiming_show";
|
|
|
|
/* api callbacks */
|
|
ot->exec = sequencer_retiming_data_show_exec;
|
|
ot->poll = sequencer_editing_initialized_and_active;
|
|
|
|
/* flags */
|
|
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
|
}
|
|
|
|
/** \} */
|
|
|
|
static bool retiming_poll(bContext *C)
|
|
{
|
|
const Editing *ed = seq::editing_get(CTX_data_scene(C));
|
|
if (ed == nullptr) {
|
|
return false;
|
|
}
|
|
Strip *strip = ed->act_seq;
|
|
if (strip == nullptr) {
|
|
return false;
|
|
}
|
|
if (!seq::retiming_is_allowed(strip)) {
|
|
CTX_wm_operator_poll_msg_set(C, "This strip type cannot be retimed");
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------- */
|
|
/** \name Retiming Reset
|
|
* \{ */
|
|
|
|
static int sequencer_retiming_reset_exec(bContext *C, wmOperator * /*op*/)
|
|
{
|
|
Scene *scene = CTX_data_scene(C);
|
|
const Editing *ed = seq::editing_get(scene);
|
|
|
|
for (Strip *strip : seq::query_selected_strips(ed->seqbasep)) {
|
|
seq::retiming_reset(scene, strip);
|
|
}
|
|
|
|
WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene);
|
|
return OPERATOR_FINISHED;
|
|
}
|
|
|
|
void SEQUENCER_OT_retiming_reset(wmOperatorType *ot)
|
|
{
|
|
/* identifiers */
|
|
ot->name = "Reset Retiming";
|
|
ot->description = "Reset strip retiming";
|
|
ot->idname = "SEQUENCER_OT_retiming_reset";
|
|
|
|
/* api callbacks */
|
|
ot->exec = sequencer_retiming_reset_exec;
|
|
ot->poll = retiming_poll;
|
|
|
|
/* flags */
|
|
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
|
}
|
|
|
|
/** \} */
|
|
|
|
static SeqRetimingKey *ensure_left_and_right_keys(const bContext *C, Strip *strip)
|
|
{
|
|
Scene *scene = CTX_data_scene(C);
|
|
seq::retiming_data_ensure(strip);
|
|
seq::retiming_add_key(scene, strip, left_fake_key_frame_get(C, strip));
|
|
return seq::retiming_add_key(scene, strip, right_fake_key_frame_get(C, strip));
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \name Retiming Add Key
|
|
* \{ */
|
|
|
|
static bool retiming_key_add_new_for_seq(bContext *C,
|
|
wmOperator *op,
|
|
Strip *strip,
|
|
const int timeline_frame)
|
|
{
|
|
Scene *scene = CTX_data_scene(C);
|
|
const float scene_fps = float(scene->r.frs_sec) / float(scene->r.frs_sec_base);
|
|
const float frame_index = (BKE_scene_frame_get(scene) - seq::time_start_frame_get(strip)) *
|
|
seq::time_media_playback_rate_factor_get(strip, scene_fps);
|
|
const SeqRetimingKey *key = seq::retiming_find_segment_start_key(strip, frame_index);
|
|
|
|
if (key != nullptr && seq::retiming_key_is_transition_start(key)) {
|
|
BKE_report(op->reports, RPT_WARNING, "Cannot create key inside of speed transition");
|
|
return false;
|
|
}
|
|
|
|
const float end_frame = strip->start + seq::time_strip_length_get(scene, strip);
|
|
if (strip->start > timeline_frame || end_frame < timeline_frame) {
|
|
return false;
|
|
}
|
|
|
|
ensure_left_and_right_keys(C, strip);
|
|
seq::retiming_add_key(scene, strip, timeline_frame);
|
|
return true;
|
|
}
|
|
|
|
static int retiming_key_add_from_selection(bContext *C,
|
|
wmOperator *op,
|
|
blender::Span<Strip *> strips,
|
|
const int timeline_frame)
|
|
{
|
|
bool inserted = false;
|
|
|
|
for (Strip *strip : strips) {
|
|
if (!seq::retiming_is_allowed(strip)) {
|
|
continue;
|
|
}
|
|
inserted |= retiming_key_add_new_for_seq(C, op, strip, timeline_frame);
|
|
}
|
|
|
|
return inserted ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
|
|
}
|
|
|
|
static int retiming_key_add_to_editable_strips(bContext *C,
|
|
wmOperator *op,
|
|
const int timeline_frame)
|
|
{
|
|
Scene *scene = CTX_data_scene(C);
|
|
Editing *ed = seq::editing_get(scene);
|
|
bool inserted = false;
|
|
|
|
blender::Map selection = seq::retiming_selection_get(ed);
|
|
if (selection.is_empty()) {
|
|
return OPERATOR_CANCELLED;
|
|
}
|
|
|
|
for (Strip *strip : selection.values()) {
|
|
inserted |= retiming_key_add_new_for_seq(C, op, strip, timeline_frame);
|
|
}
|
|
|
|
return inserted ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
|
|
}
|
|
|
|
static int sequencer_retiming_key_add_exec(bContext *C, wmOperator *op)
|
|
{
|
|
Scene *scene = CTX_data_scene(C);
|
|
|
|
float timeline_frame;
|
|
if (RNA_struct_property_is_set(op->ptr, "timeline_frame")) {
|
|
timeline_frame = RNA_int_get(op->ptr, "timeline_frame");
|
|
}
|
|
else {
|
|
timeline_frame = BKE_scene_frame_get(scene);
|
|
}
|
|
|
|
int ret_val;
|
|
blender::VectorSet<Strip *> strips = selected_strips_from_context(C);
|
|
if (!strips.is_empty()) {
|
|
ret_val = retiming_key_add_from_selection(C, op, strips, timeline_frame);
|
|
}
|
|
else {
|
|
ret_val = retiming_key_add_to_editable_strips(C, op, timeline_frame);
|
|
}
|
|
|
|
WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene);
|
|
return ret_val;
|
|
}
|
|
|
|
void SEQUENCER_OT_retiming_key_add(wmOperatorType *ot)
|
|
{
|
|
/* identifiers */
|
|
ot->name = "Add Retiming Key";
|
|
ot->description = "Add retiming Key";
|
|
ot->idname = "SEQUENCER_OT_retiming_key_add";
|
|
|
|
/* api callbacks */
|
|
ot->exec = sequencer_retiming_key_add_exec;
|
|
ot->poll = retiming_poll;
|
|
|
|
/* flags */
|
|
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
|
|
|
RNA_def_int(ot->srna,
|
|
"timeline_frame",
|
|
0,
|
|
0,
|
|
INT_MAX,
|
|
"Timeline Frame",
|
|
"Frame where key will be added",
|
|
0,
|
|
INT_MAX);
|
|
}
|
|
|
|
/** \} */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \name Retiming Add Freeze Frame
|
|
* \{ */
|
|
|
|
static bool freeze_frame_add_new_for_seq(const bContext *C,
|
|
const wmOperator *op,
|
|
Strip *strip,
|
|
const int timeline_frame,
|
|
const int duration)
|
|
{
|
|
Scene *scene = CTX_data_scene(C);
|
|
ensure_left_and_right_keys(C, strip);
|
|
|
|
// ensure L+R key
|
|
SeqRetimingKey *key = seq::retiming_add_key(scene, strip, timeline_frame);
|
|
|
|
if (key == nullptr) {
|
|
BKE_report(op->reports, RPT_WARNING, "Cannot create freeze frame");
|
|
return false;
|
|
}
|
|
|
|
if (seq::retiming_key_is_transition_start(key)) {
|
|
BKE_report(op->reports, RPT_WARNING, "Cannot create key inside of speed transition");
|
|
return false;
|
|
}
|
|
|
|
SeqRetimingKey *freeze = seq::retiming_add_freeze_frame(scene, strip, key, duration);
|
|
|
|
if (freeze == nullptr) {
|
|
BKE_report(op->reports, RPT_WARNING, "Cannot create freeze frame");
|
|
return false;
|
|
}
|
|
|
|
deselect_all_strips(scene);
|
|
seq::retiming_selection_append(freeze);
|
|
|
|
seq::relations_invalidate_cache_raw(scene, strip);
|
|
return true;
|
|
}
|
|
|
|
static bool freeze_frame_add_from_strip_selection(bContext *C,
|
|
const wmOperator *op,
|
|
const int duration)
|
|
{
|
|
Scene *scene = CTX_data_scene(C);
|
|
blender::VectorSet<Strip *> strips = selected_strips_from_context(C);
|
|
strips.remove_if([&](Strip *strip) { return !seq::retiming_is_allowed(strip); });
|
|
const int timeline_frame = BKE_scene_frame_get(scene);
|
|
bool success = false;
|
|
|
|
for (Strip *strip : strips) {
|
|
success |= freeze_frame_add_new_for_seq(C, op, strip, timeline_frame, duration);
|
|
seq::relations_invalidate_cache_raw(scene, strip);
|
|
}
|
|
return success;
|
|
}
|
|
|
|
static bool freeze_frame_add_from_retiming_selection(const bContext *C,
|
|
const wmOperator *op,
|
|
const int duration)
|
|
{
|
|
Scene *scene = CTX_data_scene(C);
|
|
bool success = false;
|
|
|
|
blender::Map selection = seq::retiming_selection_get(seq::editing_get(scene));
|
|
|
|
for (auto item : selection.items()) {
|
|
const int timeline_frame = seq::retiming_key_timeline_frame_get(scene, item.value, item.key);
|
|
success |= freeze_frame_add_new_for_seq(C, op, item.value, timeline_frame, duration);
|
|
seq::relations_invalidate_cache_raw(scene, item.value);
|
|
}
|
|
return success;
|
|
}
|
|
|
|
static int sequencer_retiming_freeze_frame_add_exec(bContext *C, wmOperator *op)
|
|
{
|
|
Scene *scene = CTX_data_scene(C);
|
|
bool success = false;
|
|
|
|
int duration = 1;
|
|
|
|
if (RNA_property_is_set(op->ptr, RNA_struct_find_property(op->ptr, "duration"))) {
|
|
duration = RNA_int_get(op->ptr, "duration");
|
|
}
|
|
|
|
if (sequencer_retiming_mode_is_active(C)) {
|
|
success = freeze_frame_add_from_retiming_selection(C, op, duration);
|
|
}
|
|
else {
|
|
success = freeze_frame_add_from_strip_selection(C, op, duration);
|
|
}
|
|
|
|
WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene);
|
|
|
|
return success ? OPERATOR_FINISHED : OPERATOR_PASS_THROUGH;
|
|
}
|
|
|
|
void SEQUENCER_OT_retiming_freeze_frame_add(wmOperatorType *ot)
|
|
{
|
|
/* identifiers */
|
|
ot->name = "Add Freeze Frame";
|
|
ot->description = "Add freeze frame";
|
|
ot->idname = "SEQUENCER_OT_retiming_freeze_frame_add";
|
|
|
|
/* api callbacks */
|
|
ot->exec = sequencer_retiming_freeze_frame_add_exec;
|
|
ot->poll = retiming_poll;
|
|
|
|
/* flags */
|
|
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
|
|
|
/* properties */
|
|
RNA_def_int(ot->srna,
|
|
"duration",
|
|
0,
|
|
0,
|
|
INT_MAX,
|
|
"Duration",
|
|
"Duration of freeze frame segment",
|
|
0,
|
|
INT_MAX);
|
|
}
|
|
|
|
/** \} */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \name Retiming Add Speed Transition
|
|
* \{ */
|
|
|
|
static bool transition_add_new_for_seq(const bContext *C,
|
|
const wmOperator *op,
|
|
Strip *strip,
|
|
const int timeline_frame,
|
|
const int duration)
|
|
{
|
|
Scene *scene = CTX_data_scene(C);
|
|
|
|
// ensure L+R key
|
|
ensure_left_and_right_keys(C, strip);
|
|
SeqRetimingKey *key = seq::retiming_add_key(scene, strip, timeline_frame);
|
|
|
|
if (key == nullptr) {
|
|
key = seq::retiming_key_get_by_timeline_frame(scene, strip, timeline_frame);
|
|
}
|
|
|
|
if (seq::retiming_is_last_key(strip, key) || key->strip_frame_index == 0) {
|
|
BKE_report(op->reports, RPT_WARNING, "Cannot create transition from first or last key");
|
|
return false;
|
|
}
|
|
|
|
SeqRetimingKey *transition = seq::retiming_add_transition(scene, strip, key, duration);
|
|
|
|
if (transition == nullptr) {
|
|
BKE_report(op->reports, RPT_WARNING, "Cannot create transition");
|
|
return false;
|
|
}
|
|
|
|
deselect_all_strips(scene);
|
|
seq::retiming_selection_append(transition);
|
|
|
|
seq::relations_invalidate_cache_raw(scene, strip);
|
|
return true;
|
|
}
|
|
|
|
static bool transition_add_from_retiming_selection(const bContext *C,
|
|
const wmOperator *op,
|
|
const int duration)
|
|
{
|
|
Scene *scene = CTX_data_scene(C);
|
|
bool success = false;
|
|
|
|
blender::Map selection = seq::retiming_selection_get(seq::editing_get(scene));
|
|
|
|
for (auto item : selection.items()) {
|
|
const int timeline_frame = seq::retiming_key_timeline_frame_get(scene, item.value, item.key);
|
|
success |= transition_add_new_for_seq(C, op, item.value, timeline_frame, duration);
|
|
}
|
|
return success;
|
|
}
|
|
|
|
static int sequencer_retiming_transition_add_exec(bContext *C, wmOperator *op)
|
|
{
|
|
Scene *scene = CTX_data_scene(C);
|
|
bool success = false;
|
|
|
|
int duration = 1;
|
|
|
|
if (RNA_property_is_set(op->ptr, RNA_struct_find_property(op->ptr, "duration"))) {
|
|
duration = RNA_int_get(op->ptr, "duration");
|
|
}
|
|
|
|
if (sequencer_retiming_mode_is_active(C)) {
|
|
success = transition_add_from_retiming_selection(C, op, duration);
|
|
}
|
|
else {
|
|
BKE_report(op->reports, RPT_WARNING, "Retiming key must be selected");
|
|
return false;
|
|
}
|
|
|
|
WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene);
|
|
|
|
return success ? OPERATOR_FINISHED : OPERATOR_PASS_THROUGH;
|
|
}
|
|
|
|
void SEQUENCER_OT_retiming_transition_add(wmOperatorType *ot)
|
|
{
|
|
/* identifiers */
|
|
ot->name = "Add Speed Transition";
|
|
ot->description = "Add smooth transition between 2 retimed segments";
|
|
ot->idname = "SEQUENCER_OT_retiming_transition_add";
|
|
|
|
/* api callbacks */
|
|
ot->exec = sequencer_retiming_transition_add_exec;
|
|
ot->poll = retiming_poll;
|
|
|
|
/* flags */
|
|
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
|
|
|
/* properties */
|
|
RNA_def_int(ot->srna,
|
|
"duration",
|
|
0,
|
|
0,
|
|
INT_MAX,
|
|
"Duration",
|
|
"Duration of freeze frame segment",
|
|
0,
|
|
INT_MAX);
|
|
}
|
|
|
|
/** \} */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \name Retiming Delete Key
|
|
* \{ */
|
|
|
|
static int sequencer_retiming_key_delete_exec(bContext *C, wmOperator * /*op*/)
|
|
{
|
|
Scene *scene = CTX_data_scene(C);
|
|
|
|
blender::Map selection = seq::retiming_selection_get(seq::editing_get(scene));
|
|
blender::Vector<Strip *> strips_to_handle;
|
|
|
|
if (!sequencer_retiming_mode_is_active(C) || selection.size() == 0) {
|
|
return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH;
|
|
}
|
|
|
|
for (Strip *strip : selection.values()) {
|
|
strips_to_handle.append_non_duplicates(strip);
|
|
}
|
|
|
|
for (Strip *strip : strips_to_handle) {
|
|
blender::Vector<SeqRetimingKey *> keys_to_delete;
|
|
for (auto item : selection.items()) {
|
|
if (item.value != strip) {
|
|
continue;
|
|
}
|
|
keys_to_delete.append(item.key);
|
|
}
|
|
|
|
seq::retiming_remove_multiple_keys(strip, keys_to_delete);
|
|
}
|
|
|
|
for (Strip *strip : strips_to_handle) {
|
|
seq::relations_invalidate_cache_raw(scene, strip);
|
|
}
|
|
|
|
WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene);
|
|
return OPERATOR_FINISHED;
|
|
}
|
|
|
|
static int sequencer_retiming_key_delete_invoke(bContext *C, wmOperator *op, const wmEvent *event)
|
|
{
|
|
Scene *scene = CTX_data_scene(C);
|
|
ListBase *markers = &scene->markers;
|
|
|
|
if (!BLI_listbase_is_empty(markers)) {
|
|
ARegion *region = CTX_wm_region(C);
|
|
if (region && (region->regiontype == RGN_TYPE_WINDOW)) {
|
|
/* Bounding box of 30 pixels is used for markers shortcuts,
|
|
* prevent conflict with markers shortcuts here. */
|
|
if (event->mval[1] <= 30) {
|
|
return OPERATOR_PASS_THROUGH;
|
|
}
|
|
}
|
|
}
|
|
|
|
return sequencer_retiming_key_delete_exec(C, op);
|
|
}
|
|
|
|
void SEQUENCER_OT_retiming_key_delete(wmOperatorType *ot)
|
|
{
|
|
|
|
/* Identifiers. */
|
|
ot->name = "Delete Retiming Keys";
|
|
ot->idname = "SEQUENCER_OT_retiming_key_delete";
|
|
ot->description = "Delete selected strips from the sequencer";
|
|
|
|
/* Api callbacks. */
|
|
ot->invoke = sequencer_retiming_key_delete_invoke;
|
|
ot->exec = sequencer_retiming_key_delete_exec;
|
|
ot->poll = retiming_poll;
|
|
|
|
/* Flags. */
|
|
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
|
}
|
|
|
|
/** \} */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \name Retiming Set Segment Speed
|
|
* \{ */
|
|
|
|
/* Return speed of existing segment or strip. Assume 1 element is selected. */
|
|
static float strip_speed_get(bContext *C, const wmOperator * /*op*/)
|
|
{
|
|
/* Strip mode. */
|
|
if (!sequencer_retiming_mode_is_active(C)) {
|
|
blender::VectorSet<Strip *> strips = selected_strips_from_context(C);
|
|
if (strips.size() == 1) {
|
|
Strip *strip = strips[0];
|
|
SeqRetimingKey *key = ensure_left_and_right_keys(C, strip);
|
|
return seq::retiming_key_speed_get(strip, key);
|
|
}
|
|
}
|
|
|
|
Scene *scene = CTX_data_scene(C);
|
|
blender::Map selection = seq::retiming_selection_get(seq::editing_get(scene));
|
|
/* Retiming mode. */
|
|
if (selection.size() == 1) {
|
|
for (auto item : selection.items()) {
|
|
return seq::retiming_key_speed_get(item.value, item.key);
|
|
}
|
|
}
|
|
|
|
return 1.0f;
|
|
}
|
|
|
|
static int strip_speed_set_exec(bContext *C, const wmOperator *op)
|
|
{
|
|
Scene *scene = CTX_data_scene(C);
|
|
blender::VectorSet<Strip *> strips = selected_strips_from_context(C);
|
|
strips.remove_if([&](Strip *strip) { return !seq::retiming_is_allowed(strip); });
|
|
|
|
for (Strip *strip : strips) {
|
|
SeqRetimingKey *key = ensure_left_and_right_keys(C, strip);
|
|
|
|
if (key == nullptr) {
|
|
continue;
|
|
}
|
|
/* TODO: it would be nice to multiply speed with complex retiming by a factor. */
|
|
seq::retiming_key_speed_set(
|
|
scene, strip, key, RNA_float_get(op->ptr, "speed") / 100.0f, false);
|
|
|
|
ListBase *seqbase = seq::active_seqbase_get(seq::editing_get(scene));
|
|
if (seq::transform_test_overlap(scene, seqbase, strip)) {
|
|
seq::transform_seqbase_shuffle(seqbase, strip, scene);
|
|
}
|
|
|
|
seq::relations_invalidate_cache_raw(scene, strip);
|
|
}
|
|
|
|
WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene);
|
|
return OPERATOR_FINISHED;
|
|
}
|
|
|
|
static int segment_speed_set_exec(const bContext *C,
|
|
const wmOperator *op,
|
|
blender::Map<SeqRetimingKey *, Strip *> selection)
|
|
{
|
|
Scene *scene = CTX_data_scene(C);
|
|
ListBase *seqbase = seq::active_seqbase_get(seq::editing_get(scene));
|
|
|
|
for (auto item : selection.items()) {
|
|
seq::retiming_key_speed_set(scene,
|
|
item.value,
|
|
item.key,
|
|
RNA_float_get(op->ptr, "speed") / 100.0f,
|
|
RNA_boolean_get(op->ptr, "keep_retiming"));
|
|
|
|
if (seq::transform_test_overlap(scene, seqbase, item.value)) {
|
|
seq::transform_seqbase_shuffle(seqbase, item.value, scene);
|
|
}
|
|
|
|
seq::relations_invalidate_cache_raw(scene, item.value);
|
|
}
|
|
|
|
WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene);
|
|
return OPERATOR_FINISHED;
|
|
}
|
|
|
|
static int sequencer_retiming_segment_speed_set_exec(bContext *C, wmOperator *op)
|
|
{
|
|
const Scene *scene = CTX_data_scene(C);
|
|
|
|
/* Strip mode. */
|
|
if (!sequencer_retiming_mode_is_active(C)) {
|
|
return strip_speed_set_exec(C, op);
|
|
}
|
|
|
|
blender::Map selection = seq::retiming_selection_get(seq::editing_get(scene));
|
|
|
|
/* Retiming mode. */
|
|
if (selection.size() > 0) {
|
|
return segment_speed_set_exec(C, op, selection);
|
|
}
|
|
|
|
BKE_report(op->reports, RPT_ERROR, "No keys or strips selected");
|
|
return OPERATOR_CANCELLED;
|
|
}
|
|
|
|
static int sequencer_retiming_segment_speed_set_invoke(bContext *C,
|
|
wmOperator *op,
|
|
const wmEvent *event)
|
|
{
|
|
if (!RNA_struct_property_is_set(op->ptr, "speed")) {
|
|
RNA_float_set(op->ptr, "speed", strip_speed_get(C, op) * 100.0f);
|
|
return WM_operator_props_popup(C, op, event);
|
|
}
|
|
|
|
return sequencer_retiming_segment_speed_set_exec(C, op);
|
|
}
|
|
|
|
void SEQUENCER_OT_retiming_segment_speed_set(wmOperatorType *ot)
|
|
{
|
|
/* identifiers */
|
|
ot->name = "Set Speed";
|
|
ot->description = "Set speed of retimed segment";
|
|
ot->idname = "SEQUENCER_OT_retiming_segment_speed_set";
|
|
|
|
/* api callbacks */
|
|
ot->invoke = sequencer_retiming_segment_speed_set_invoke;
|
|
ot->exec = sequencer_retiming_segment_speed_set_exec;
|
|
ot->poll = retiming_poll;
|
|
|
|
/* flags */
|
|
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
|
|
|
/* properties */
|
|
RNA_def_float(ot->srna,
|
|
"speed",
|
|
100.0f,
|
|
0.001f,
|
|
FLT_MAX,
|
|
"Speed",
|
|
"New speed of retimed segment",
|
|
0.1f,
|
|
FLT_MAX);
|
|
|
|
RNA_def_boolean(ot->srna,
|
|
"keep_retiming",
|
|
true,
|
|
"Preserve Current Retiming",
|
|
"Keep speed of other segments unchanged, change strip length instead");
|
|
}
|
|
|
|
/** \} */
|
|
|
|
static bool select_key(const Editing *ed,
|
|
SeqRetimingKey *key,
|
|
const bool toggle,
|
|
const bool deselect_all)
|
|
{
|
|
bool changed = false;
|
|
|
|
if (deselect_all) {
|
|
changed = seq::retiming_selection_clear(ed);
|
|
}
|
|
|
|
if (key == nullptr) {
|
|
return changed;
|
|
}
|
|
|
|
if (toggle && seq::retiming_selection_contains(ed, key)) {
|
|
seq::retiming_selection_remove(key);
|
|
}
|
|
else {
|
|
seq::retiming_selection_append(key);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool select_connected_keys(const Scene *scene,
|
|
const SeqRetimingKey *source,
|
|
const Strip *source_owner)
|
|
{
|
|
if (!seq::is_strip_connected(source_owner)) {
|
|
return false;
|
|
}
|
|
|
|
const int frame = seq::retiming_key_timeline_frame_get(scene, source_owner, source);
|
|
bool changed = false;
|
|
blender::VectorSet<Strip *> connections = seq::get_connected_strips(source_owner);
|
|
for (Strip *connection : connections) {
|
|
SeqRetimingKey *con_key = seq::retiming_key_get_by_timeline_frame(scene, connection, frame);
|
|
|
|
if (con_key) {
|
|
seq::retiming_selection_copy(con_key, source);
|
|
changed = true;
|
|
}
|
|
}
|
|
return changed;
|
|
}
|
|
|
|
int sequencer_retiming_select_linked_time(bContext *C,
|
|
wmOperator *op,
|
|
SeqRetimingKey *key,
|
|
const Strip *key_owner)
|
|
{
|
|
Scene *scene = CTX_data_scene(C);
|
|
Editing *ed = seq::editing_get(scene);
|
|
|
|
if (!RNA_boolean_get(op->ptr, "extend")) {
|
|
seq::retiming_selection_clear(ed);
|
|
}
|
|
for (; key <= seq::retiming_last_key_get(key_owner); key++) {
|
|
select_key(ed, key, false, false);
|
|
select_connected_keys(scene, key, key_owner);
|
|
}
|
|
WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene);
|
|
return OPERATOR_FINISHED;
|
|
}
|
|
|
|
int sequencer_retiming_key_select_exec(bContext *C,
|
|
wmOperator *op,
|
|
SeqRetimingKey *key,
|
|
const Strip *key_owner)
|
|
{
|
|
if (RNA_boolean_get(op->ptr, "linked_time")) {
|
|
return sequencer_retiming_select_linked_time(C, op, key, key_owner);
|
|
}
|
|
|
|
Scene *scene = CTX_data_scene(C);
|
|
Editing *ed = seq::editing_get(scene);
|
|
|
|
const bool wait_to_deselect_others = RNA_boolean_get(op->ptr, "wait_to_deselect_others");
|
|
const bool toggle = RNA_boolean_get(op->ptr, "toggle");
|
|
bool deselect_all = RNA_boolean_get(op->ptr, "deselect_all");
|
|
deselect_all |= !toggle;
|
|
|
|
/* Clicked on an unselected key. */
|
|
if (!seq::retiming_selection_contains(ed, key) && !toggle) {
|
|
select_key(ed, key, false, deselect_all);
|
|
select_connected_keys(scene, key, key_owner);
|
|
}
|
|
|
|
/* Clicked on a key that is already selected, waiting to click release. */
|
|
if (wait_to_deselect_others && !toggle) {
|
|
return OPERATOR_RUNNING_MODAL;
|
|
}
|
|
|
|
/* The key is already selected, but deselect other selected keys after click is released if no
|
|
* transform or toggle happened. */
|
|
bool changed = select_key(ed, key, toggle, deselect_all);
|
|
if (!toggle) {
|
|
changed |= select_connected_keys(scene, key, key_owner);
|
|
}
|
|
|
|
WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene);
|
|
return changed ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
|
|
}
|
|
|
|
static void realize_fake_keys_in_rect(bContext *C, Strip *strip, const rctf &rectf)
|
|
{
|
|
const Scene *scene = CTX_data_scene(C);
|
|
|
|
const int content_start = seq::time_start_frame_get(strip);
|
|
const int left_key_frame = max_ii(content_start, seq::time_left_handle_frame_get(scene, strip));
|
|
const int content_end = seq::time_content_end_frame_get(scene, strip);
|
|
const int right_key_frame = min_ii(content_end, seq::time_right_handle_frame_get(scene, strip));
|
|
|
|
/* Realize "fake" keys. */
|
|
if (left_key_frame > rectf.xmin && left_key_frame < rectf.xmax) {
|
|
seq::retiming_add_key(scene, strip, left_key_frame);
|
|
}
|
|
if (right_key_frame > rectf.xmin && right_key_frame < rectf.xmax) {
|
|
seq::retiming_add_key(scene, strip, right_key_frame);
|
|
}
|
|
}
|
|
|
|
int sequencer_retiming_box_select_exec(bContext *C, wmOperator *op)
|
|
{
|
|
const Scene *scene = CTX_data_scene(C);
|
|
const View2D *v2d = UI_view2d_fromcontext(C);
|
|
Editing *ed = seq::editing_get(scene);
|
|
|
|
if (ed == nullptr) {
|
|
return OPERATOR_CANCELLED;
|
|
}
|
|
|
|
const eSelectOp sel_op = eSelectOp(RNA_enum_get(op->ptr, "mode"));
|
|
bool changed = false;
|
|
|
|
if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
|
|
changed |= seq::retiming_selection_clear(ed);
|
|
}
|
|
|
|
rctf rectf;
|
|
WM_operator_properties_border_to_rctf(op, &rectf);
|
|
UI_view2d_region_to_view_rctf(v2d, &rectf, &rectf);
|
|
|
|
blender::Set<SeqRetimingKey *> and_keys;
|
|
|
|
for (Strip *strip : sequencer_visible_strips_get(C)) {
|
|
if (strip->machine < rectf.ymin || strip->machine > rectf.ymax) {
|
|
continue;
|
|
}
|
|
if (!seq::retiming_data_is_editable(strip)) {
|
|
continue;
|
|
}
|
|
realize_fake_keys_in_rect(C, strip, rectf);
|
|
|
|
for (SeqRetimingKey &key : seq::retiming_keys_get(strip)) {
|
|
const int key_frame = seq::retiming_key_timeline_frame_get(scene, strip, &key);
|
|
const int strip_start = seq::time_left_handle_frame_get(scene, strip);
|
|
const int strip_end = seq::time_right_handle_frame_get(scene, strip);
|
|
if (key_frame < strip_start || key_frame > strip_end) {
|
|
continue;
|
|
}
|
|
if (key_frame > rectf.xmax || key_frame < rectf.xmin) {
|
|
continue;
|
|
}
|
|
|
|
switch (sel_op) {
|
|
case SEL_OP_ADD:
|
|
case SEL_OP_SET: {
|
|
seq::retiming_selection_append(&key);
|
|
break;
|
|
}
|
|
case SEL_OP_SUB: {
|
|
seq::retiming_selection_remove(&key);
|
|
break;
|
|
}
|
|
case SEL_OP_XOR: { /* Toggle */
|
|
if (seq::retiming_selection_contains(ed, &key)) {
|
|
seq::retiming_selection_remove(&key);
|
|
}
|
|
else {
|
|
seq::retiming_selection_append(&key);
|
|
}
|
|
break;
|
|
case SEL_OP_AND: {
|
|
if (seq::retiming_selection_contains(ed, &key)) {
|
|
and_keys.add(&key);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
changed = true;
|
|
}
|
|
}
|
|
|
|
if (and_keys.size() > 0) {
|
|
seq::retiming_selection_clear(ed);
|
|
for (auto *key : and_keys) {
|
|
seq::retiming_selection_append(key);
|
|
}
|
|
}
|
|
|
|
return changed ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
|
|
}
|
|
|
|
int sequencer_retiming_select_all_exec(bContext *C, wmOperator *op)
|
|
{
|
|
Scene *scene = CTX_data_scene(C);
|
|
int action = RNA_enum_get(op->ptr, "action");
|
|
|
|
blender::VectorSet<Strip *> strips = all_strips_from_context(C);
|
|
|
|
if (action == SEL_TOGGLE) {
|
|
action = SEL_SELECT;
|
|
for (Strip *strip : strips) {
|
|
if (!seq::retiming_data_is_editable(strip)) {
|
|
continue;
|
|
}
|
|
for (SeqRetimingKey &key : seq::retiming_keys_get(strip)) {
|
|
if (key.flag & SEQ_KEY_SELECTED) {
|
|
action = SEL_DESELECT;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (action == SEL_DESELECT) {
|
|
seq::retiming_selection_clear(seq::editing_get(scene));
|
|
}
|
|
|
|
for (Strip *strip : strips) {
|
|
if (!seq::retiming_data_is_editable(strip)) {
|
|
continue;
|
|
}
|
|
for (SeqRetimingKey &key : seq::retiming_keys_get(strip)) {
|
|
switch (action) {
|
|
case SEL_SELECT:
|
|
key.flag |= SEQ_KEY_SELECTED;
|
|
break;
|
|
case SEL_INVERT:
|
|
if (key.flag & SEQ_KEY_SELECTED) {
|
|
key.flag &= ~SEQ_KEY_SELECTED;
|
|
}
|
|
else {
|
|
key.flag |= SEQ_KEY_SELECTED;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene);
|
|
return OPERATOR_FINISHED;
|
|
}
|
|
|
|
} // namespace blender::ed::vse
|