Files
test2/source/blender/editors/space_sequencer/sequencer_retiming.cc
Richard Antalik 68abed543b Refactor: Remove module prefix form symbols in sequnecer namespaces
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
2025-03-06 13:04:39 +01:00

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