Blender V4.5
sequencer_edit.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include "BLI_string_ref.hh"
10#include "BLI_string_utils.hh"
11#include "BLI_vector.hh"
12#include "MEM_guardedalloc.h"
13
14#include "BLI_fileops.h"
15#include "BLI_listbase.h"
16#include "BLI_math_vector.h"
17#include "BLI_path_utils.hh"
18#include "BLI_string.h"
19#include "BLI_timecode.h"
20#include "BLI_utildefines.h"
21
22#include "BLT_translation.hh"
23
24#include "DNA_anim_types.h"
25#include "DNA_scene_types.h"
26#include "DNA_sound_types.h"
27
28#include "BKE_context.hh"
29#include "BKE_global.hh"
30#include "BKE_library.hh"
31#include "BKE_main.hh"
32#include "BKE_report.hh"
33#include "BKE_sound.h"
34
35#include "SEQ_add.hh"
36#include "SEQ_animation.hh"
37#include "SEQ_channels.hh"
38#include "SEQ_connect.hh"
39#include "SEQ_edit.hh"
40#include "SEQ_effects.hh"
41#include "SEQ_iterator.hh"
42#include "SEQ_prefetch.hh"
43#include "SEQ_relations.hh"
44#include "SEQ_render.hh"
45#include "SEQ_select.hh"
46#include "SEQ_sequencer.hh"
48#include "SEQ_time.hh"
49#include "SEQ_transform.hh"
50#include "SEQ_utils.hh"
51
52#include "ANIM_action_legacy.hh"
53
54#include "WM_api.hh"
55#include "WM_types.hh"
56
57#include "RNA_define.hh"
58#include "RNA_enum_types.hh"
59#include "RNA_prototypes.hh"
60
61/* For menu, popup, icons, etc. */
62#include "ED_fileselect.hh"
63#include "ED_numinput.hh"
64#include "ED_scene.hh"
65#include "ED_screen.hh"
66#include "ED_sequencer.hh"
67
68#include "UI_interface.hh"
69#include "UI_resources.hh"
70#include "UI_view2d.hh"
71
72#include "DEG_depsgraph.hh"
74
75/* Own include. */
76#include "sequencer_intern.hh"
77#include <cstddef>
78
79namespace blender::ed::vse {
80
81/* -------------------------------------------------------------------- */
84
86{
87 return maskedit_poll(C);
88}
89
91{
92 if (sseq && sseq->mainb == SEQ_DRAW_IMG_IMBUF) {
93 return (seq::active_mask_get(scene) != nullptr);
94 }
95
96 return false;
97}
98
100{
101 SpaceSeq *sseq = CTX_wm_space_seq(C);
102
103 if (sseq) {
104 Scene *scene = CTX_data_scene(C);
105 return check_show_maskedit(sseq, scene);
106 }
107
108 return false;
109}
110
111bool check_show_imbuf(const SpaceSeq &sseq)
112{
113 return (sseq.mainb == SEQ_DRAW_IMG_IMBUF) &&
115}
116
117bool check_show_strip(const SpaceSeq &sseq)
118{
120}
121
123{
124 if (!BLI_str_startswith(fcurve->rna_path, "sequence_editor.strips_all[\"")) {
125 return false;
126 }
127
128 if (!BLI_str_endswith(fcurve->rna_path, "\"].color")) {
129 return false;
130 }
131
132 return true;
133}
134
136{
137 if (!scene->adt) {
138 return false;
139 }
140 if (!scene->adt->action) {
141 return false;
142 }
143
146 return true;
147 }
148 }
149
150 return false;
151}
152
154
155/* -------------------------------------------------------------------- */
158
160{
161 return (seq::editing_get(CTX_data_scene(C)) != nullptr);
162}
163
165{
166 if (!sequencer_edit_poll(C)) {
167 return false;
168 }
169 ARegion *region = CTX_wm_region(C);
170 if (!(region && (region->regiontype == RGN_TYPE_CHANNELS))) {
171 return false;
172 }
173 return true;
174}
175
180
181#if 0 /* UNUSED */
182bool sequencer_strip_poll(bContext *C)
183{
184 Editing *ed;
185 return (((ed = seq::editing_get(CTX_data_scene(C))) != nullptr) && (ed->act_strip != nullptr));
186}
187#endif
188
190{
191 Scene *scene = CTX_data_scene(C);
192 if (!ID_IS_EDITABLE(&scene->id)) {
193 return false;
194 }
195 Editing *ed = seq::editing_get(scene);
196 return (ed && (ed->act_strip != nullptr));
197}
198
200{
201 Editing *ed;
202 Strip *strip;
203 return (((ed = seq::editing_get(CTX_data_scene(C))) != nullptr) &&
204 ((strip = ed->act_strip) != nullptr) && STRIP_HAS_PATH(strip));
205}
206
208{
209 SpaceSeq *sseq = CTX_wm_space_seq(C);
210 if (sseq == nullptr) {
211 return false;
212 }
213 if (seq::editing_get(CTX_data_scene(C)) == nullptr) {
214 return false;
215 }
217 (sseq->mainb == SEQ_DRAW_IMG_IMBUF)))
218 {
219 return false;
220 }
221 ARegion *region = CTX_wm_region(C);
222 if (!(region && region->regiontype == RGN_TYPE_PREVIEW)) {
223 return false;
224 }
225
226 return true;
227}
228
230{
231 SpaceSeq *sseq = CTX_wm_space_seq(C);
232 if (sseq == nullptr) {
233 return false;
234 }
235 if (seq::editing_get(CTX_data_scene(C)) == nullptr) {
236 return false;
237 }
238 if (!(ELEM(sseq->view, SEQ_VIEW_PREVIEW) && (sseq->mainb == SEQ_DRAW_IMG_IMBUF))) {
239 return false;
240 }
241 ARegion *region = CTX_wm_region(C);
242 if (!(region && region->regiontype == RGN_TYPE_PREVIEW)) {
243 return false;
244 }
245
246 return true;
247}
248
250{
251 SpaceSeq *sseq = CTX_wm_space_seq(C);
252 if (sseq == nullptr) {
253 return false;
254 }
255 if (!check_show_strip(*sseq)) {
256 return false;
257 }
258 ARegion *region = CTX_wm_region(C);
259 if (!(region && region->regiontype == RGN_TYPE_WINDOW)) {
260 return false;
261 }
262 return true;
263}
264
266{
267 Scene *scene = CTX_data_scene(C);
268 Editing *ed = seq::editing_get(scene);
269
270 if (ed) {
271 Strip *active_strip = seq::select_active_get(scene);
272 if (active_strip && (active_strip->type & STRIP_TYPE_EFFECT)) {
273 return true;
274 }
275 }
276
277 return false;
278}
279
281{
282 Scene *scene = CTX_data_scene(C);
283 Strip *active_strip = seq::select_active_get(scene);
284
285 if (sequencer_effect_poll(C) && seq::effect_get_num_inputs(active_strip->type) == 2) {
286 return true;
287 }
288
289 return false;
290}
291
293
294/* -------------------------------------------------------------------- */
297
299{
300 Scene *scene = CTX_data_scene(C);
301 const bool do_all = RNA_boolean_get(op->ptr, "all");
302 const Editing *ed = seq::editing_get(scene);
303
304 seq::edit_remove_gaps(scene, ed->seqbasep, scene->r.cfra, do_all);
305
308
309 return OPERATOR_FINISHED;
310}
311
313{
314 /* Identifiers. */
315 ot->name = "Remove Gaps";
316 ot->idname = "SEQUENCER_OT_gap_remove";
317 ot->description =
318 "Remove gap at current frame to first strip at the right, independent of selection or "
319 "locked state of strips";
320
321 /* API callbacks. */
322 // ot->invoke = sequencer_snap_invoke;
324 ot->poll = sequencer_edit_poll;
325
326 /* Flags. */
328
329 RNA_def_boolean(ot->srna, "all", false, "All Gaps", "Do all gaps to right of current frame");
330}
331
333
334/* -------------------------------------------------------------------- */
337
339{
340 Scene *scene = CTX_data_scene(C);
341 const int frames = RNA_int_get(op->ptr, "frames");
342 const Editing *ed = seq::editing_get(scene);
343 seq::transform_offset_after_frame(scene, ed->seqbasep, frames, scene->r.cfra);
344
346
347 return OPERATOR_FINISHED;
348}
349
351{
352 /* Identifiers. */
353 ot->name = "Insert Gaps";
354 ot->idname = "SEQUENCER_OT_gap_insert";
355 ot->description =
356 "Insert gap at current frame to first strips at the right, independent of selection or "
357 "locked state of strips";
358
359 /* API callbacks. */
360 // ot->invoke = sequencer_snap_invoke;
362 ot->poll = sequencer_edit_poll;
363
364 /* Flags. */
366
367 RNA_def_int(ot->srna,
368 "frames",
369 10,
370 0,
371 INT_MAX,
372 "Frames",
373 "Frames to insert after current strip",
374 0,
375 1000);
376}
377
379
380/* -------------------------------------------------------------------- */
383
385{
386 Scene *scene = CTX_data_scene(C);
387
388 Editing *ed = seq::editing_get(scene);
390 int snap_frame;
391
392 snap_frame = RNA_int_get(op->ptr, "frame");
393
394 /* Check meta-strips. */
395 LISTBASE_FOREACH (Strip *, strip, ed->seqbasep) {
396 if (strip->flag & SELECT && !seq::transform_is_locked(channels, strip) &&
398 {
399 if ((strip->flag & (SEQ_LEFTSEL + SEQ_RIGHTSEL)) == 0) {
401 scene, strip, (snap_frame - strip->startofs) - strip->start);
402 }
403 else {
404 if (strip->flag & SEQ_LEFTSEL) {
405 seq::time_left_handle_frame_set(scene, strip, snap_frame);
406 }
407 else { /* SEQ_RIGHTSEL */
408 seq::time_right_handle_frame_set(scene, strip, snap_frame);
409 }
410 }
411
413 }
414 }
415
416 /* Test for effects and overlap. */
417 LISTBASE_FOREACH (Strip *, strip, ed->seqbasep) {
418 if (strip->flag & SELECT && !seq::transform_is_locked(channels, strip)) {
419 strip->flag &= ~SEQ_OVERLAP;
420 if (seq::transform_test_overlap(scene, ed->seqbasep, strip)) {
421 seq::transform_seqbase_shuffle(ed->seqbasep, strip, scene);
422 }
423 }
424 }
425
426 /* Recalculate bounds of effect strips, offsetting the keyframes if not snapping any handle. */
427 LISTBASE_FOREACH (Strip *, strip, ed->seqbasep) {
428 if (strip->type & STRIP_TYPE_EFFECT) {
429 const bool either_handle_selected = (strip->flag & (SEQ_LEFTSEL | SEQ_RIGHTSEL)) != 0;
430
431 if (strip->input1 && (strip->input1->flag & SELECT)) {
432 if (!either_handle_selected) {
434 scene, strip, (snap_frame - seq::time_left_handle_frame_get(scene, strip)));
435 }
436 }
437 else if (strip->input2 && (strip->input2->flag & SELECT)) {
438 if (!either_handle_selected) {
440 scene, strip, (snap_frame - seq::time_left_handle_frame_get(scene, strip)));
441 }
442 }
443 }
444 }
445
448
449 return OPERATOR_FINISHED;
450}
451
453 wmOperator *op,
454 const wmEvent * /*event*/)
455{
456 Scene *scene = CTX_data_scene(C);
457
458 int snap_frame;
459
460 snap_frame = scene->r.cfra;
461
462 RNA_int_set(op->ptr, "frame", snap_frame);
463 return sequencer_snap_exec(C, op);
464}
465
467{
468 /* Identifiers. */
469 ot->name = "Snap Strips to the Current Frame";
470 ot->idname = "SEQUENCER_OT_snap";
471 ot->description = "Frame where selected strips will be snapped";
472
473 /* API callbacks. */
474 ot->invoke = sequencer_snap_invoke;
475 ot->exec = sequencer_snap_exec;
476 ot->poll = sequencer_edit_poll;
477
478 /* Flags. */
480
481 RNA_def_int(ot->srna,
482 "frame",
483 0,
484 INT_MIN,
485 INT_MAX,
486 "Frame",
487 "Frame where selected strips will be snapped",
488 INT_MIN,
489 INT_MAX);
490}
491
493
494/* -------------------------------------------------------------------- */
497
498/* Modal Keymap */
499enum {
505};
506
528
530{
531 static const EnumPropertyItem modal_items[] = {
532 {SLIP_MODAL_CONFIRM, "CONFIRM", 0, "Confirm", ""},
533 {SLIP_MODAL_CANCEL, "CANCEL", 0, "Cancel", ""},
534
535 {SLIP_MODAL_PRECISION_ENABLE, "PRECISION_ENABLE", 0, "Precision Enable", ""},
536 {SLIP_MODAL_PRECISION_DISABLE, "PRECISION_DISABLE", 0, "Precision Disable", ""},
537 {SLIP_MODAL_CLAMP_TOGGLE, "CLAMP_TOGGLE", 0, "Clamp Toggle", ""},
538
539 {0, nullptr, 0, nullptr, nullptr},
540 };
541
542 wmKeyMap *keymap = WM_modalkeymap_find(keyconf, "Slip Modal");
543
544 /* This function is called for each space-type, only needs to add map once. */
545 if (keymap && keymap->modal_items) {
546 return;
547 }
548
549 keymap = WM_modalkeymap_ensure(keyconf, "Slip Modal", modal_items);
550
551 /* Assign map to operators. */
552 WM_modalkeymap_assign(keymap, "SEQUENCER_OT_slip");
553}
554
555static void slip_draw_status(bContext *C, const wmOperator *op)
556{
557 SlipData *data = static_cast<SlipData *>(op->customdata);
558
559 WorkspaceStatus status(C);
560
561 status.opmodal(IFACE_("Confirm"), op->type, SLIP_MODAL_CONFIRM);
562 status.opmodal(IFACE_("Cancel"), op->type, SLIP_MODAL_CANCEL);
563
564 status.opmodal(IFACE_("Precision"), op->type, SLIP_MODAL_PRECISION_ENABLE, data->precision);
565
566 if (data->can_clamp) {
567 status.opmodal(IFACE_("Clamp"), op->type, SLIP_MODAL_CLAMP_TOGGLE, data->clamp);
568 }
569 if (data->clamp_warning) {
570 status.item(TIP_("Not enough content to clamp strip(s)"), ICON_ERROR);
571 }
572}
573
574static void slip_update_header(const Scene *scene,
575 ScrArea *area,
576 SlipData *data,
577 const float offset)
578{
579 if (area == nullptr) {
580 return;
581 }
582
583 char msg[UI_MAX_DRAW_STR];
584 if (hasNumInput(&data->num_input)) {
585 char num_str[NUM_STR_REP_LEN];
586 outputNumInput(&data->num_input, num_str, scene->unit);
587 SNPRINTF(msg, IFACE_("Slip Offset: Frames: %s"), num_str);
588 }
589 else {
590 int frame_offset = std::trunc(offset);
591 if (data->show_subframe) {
592 float subframe_offset_sec = (offset - std::trunc(offset)) / FPS;
593 SNPRINTF(msg,
594 IFACE_("Slip Offset: Frames: %d Sound Offset: %.3f"),
595 frame_offset,
596 subframe_offset_sec);
597 }
598 else {
599 SNPRINTF(msg, IFACE_("Slip Offset: Frames: %d"), frame_offset);
600 }
601 }
602
603 ED_area_status_text(area, msg);
604}
605
606static SlipData *slip_data_init(const Scene *scene)
607{
608 Editing *ed = seq::editing_get(scene);
609 SlipData *data = MEM_new<SlipData>("slipdata");
610
613 strips.remove_if([&](Strip *strip) {
614 return ((strip->type & STRIP_TYPE_EFFECT) ||
616 seq::transform_is_locked(channels, strip));
617 });
618 if (strips.is_empty()) {
619 return nullptr;
620 }
621 data->strips = strips;
622
623 data->show_subframe = false;
624 data->clamp_warning = false;
625 data->can_clamp = false;
626
627 data->clamp = true;
628 for (Strip *strip : strips) {
629 strip->flag |= SEQ_SHOW_OFFSETS;
630
631 /* If any strips start out with hold offsets visible, disable clamping on initialization. */
632 if (strip->startofs < 0 || strip->endofs < 0) {
633 data->clamp = false;
634 }
635 /* If any strips do not have enough underlying content to
636 * fill their bounds, show a warning. */
637 if (strip->len < seq::time_right_handle_frame_get(scene, strip) -
639 {
640 data->clamp_warning = true;
641 }
642 /* Strip exists with enough content, we can clamp. */
643 else {
644 data->can_clamp = true;
645 }
646 if (strip->type == STRIP_TYPE_SOUND_RAM) {
647 data->show_subframe = true;
648 }
649 }
650
651 return data;
652}
653
655{
656 Scene *scene = CTX_data_scene(C);
657 ScrArea *area = CTX_wm_area(C);
659
660 SlipData *data = slip_data_init(scene);
661 if (data == nullptr) {
662 return OPERATOR_CANCELLED;
663 }
664 op->customdata = data;
665
666 initNumInput(&data->num_input);
668 v2d, event->mval[0], event->mval[1], &data->init_mouse_co[0], &data->init_mouse_co[1]);
669 data->precision = false;
670 data->prev_offset = 0.0f;
671 data->prev_mval_x = event->mval[0];
672 data->virtual_mval_x = event->mval[0];
673
674 slip_draw_status(C, op);
675 slip_update_header(scene, area, data, 0.0f);
676
678
679 /* Enable cursor wrapping. */
681
682 /* Notify so we draw extensions immediately. */
684
686}
687
688static void slip_strips_delta(wmOperator *op, Scene *scene, SlipData *data, const float delta)
689{
690 float new_offset = data->prev_offset + delta;
691 /* Calculate rounded whole frames between offsets, which cannot be determined from `delta` alone.
692 * For example, 0.9 -> 1.0 would have a `delta` of 0.1 and a `frame_delta` of 1. */
693 int frame_delta = std::trunc(new_offset) - std::trunc(data->prev_offset);
694
695 float subframe_delta = 0.0f;
696 /* Only apply subframe delta if the input is not an integer. */
697 if (std::trunc(delta) != delta) {
698 /* Note that `subframe_delta` has opposite sign to `frame_delta`
699 * when `abs(delta)` < `abs(frame_delta)` to undo its effect. */
700 subframe_delta = delta - frame_delta;
701 }
702
703 bool slip_keyframes = RNA_boolean_get(op->ptr, "slip_keyframes");
704 for (Strip *strip : data->strips) {
705 seq::time_slip_strip(scene, strip, frame_delta, subframe_delta, slip_keyframes);
707 }
708
709 RNA_float_set(op->ptr, "offset", new_offset);
710 data->prev_offset = new_offset;
711}
712
713static void slip_cleanup(bContext *C, wmOperator *op, Scene *scene)
714{
715 ScrArea *area = CTX_wm_area(C);
716 SlipData *data = static_cast<SlipData *>(op->customdata);
717
718 for (Strip *strip : data->strips) {
719 strip->flag &= ~SEQ_SHOW_OFFSETS;
720 }
721
722 MEM_SAFE_DELETE(data);
723 if (area) {
724 ED_area_status_text(area, nullptr);
725 }
726 ED_workspace_status_text(C, nullptr);
729}
730
731/* Returns clamped offset delta relative to current strip positions,
732 * which is the sum of the frame delta and the subframe delta. */
733static float slip_apply_clamp(const Scene *scene, const SlipData *data, float *r_offset)
734{
735 float offset_delta = *r_offset - data->prev_offset;
736
737 if (data->can_clamp) {
738 for (Strip *strip : data->strips) {
739 const float unclamped_start = seq::time_start_frame_get(strip) + strip->sound_offset +
740 offset_delta;
741 const float unclamped_end = seq::time_content_end_frame_get(scene, strip) +
742 strip->sound_offset + offset_delta;
743
744 const float left_handle = seq::time_left_handle_frame_get(scene, strip);
745 const float right_handle = seq::time_right_handle_frame_get(scene, strip);
746
747 float diff = 0;
748
749 /* Clamp hold offsets if the option is currently enabled
750 * and if there are enough frames to fill the strip. */
751 if (data->clamp && strip->len >= right_handle - left_handle) {
752 if (unclamped_start > left_handle) {
753 diff = left_handle - unclamped_start;
754 }
755 else if (unclamped_end < right_handle) {
756 diff = right_handle - unclamped_end;
757 }
758 }
759 /* Always make sure each strip contains at least 1 frame of content,
760 * even if the user hasn't enabled clamping. */
761 else {
762 if (unclamped_start > right_handle - 1) {
763 diff = right_handle - 1 - unclamped_start;
764 }
765
766 if (unclamped_end < left_handle + 1) {
767 diff = left_handle + 1 - unclamped_end;
768 }
769 }
770
771 *r_offset += diff;
772 offset_delta += diff;
773 }
774 }
775
776 return offset_delta;
777}
778
780{
781 Scene *scene = CTX_data_scene(C);
782
783 SlipData *data = slip_data_init(scene);
784 if (data == nullptr) {
785 return OPERATOR_CANCELLED;
786 }
787 op->customdata = data;
788
789 float offset = RNA_float_get(op->ptr, "offset");
790 slip_apply_clamp(scene, data, &offset);
791
792 slip_strips_delta(op, scene, data, offset);
793 slip_cleanup(C, op, scene);
794 return OPERATOR_FINISHED;
795}
796
798 const bContext *C, wmOperator *op, ScrArea *area, SlipData *data, Scene *scene)
799{
800 float offset;
801 applyNumInput(&data->num_input, &offset);
802
803 const float offset_delta = slip_apply_clamp(scene, data, &offset);
804 slip_update_header(scene, area, data, offset);
805 RNA_float_set(op->ptr, "offset", offset);
806
807 slip_strips_delta(op, scene, data, offset_delta);
808
810}
811
813{
815 Scene *scene = CTX_data_scene(C);
816 SlipData *data = static_cast<SlipData *>(op->customdata);
817 ScrArea *area = CTX_wm_area(C);
818 const bool has_num_input = hasNumInput(&data->num_input);
819
820 if (event->val == KM_PRESS && handleNumInput(C, &data->num_input, event)) {
821 slip_handle_num_input(C, op, area, data, scene);
823 }
824
825 if (event->type == EVT_MODAL_MAP) {
826 switch (event->val) {
827 case SLIP_MODAL_CONFIRM: {
828 slip_cleanup(C, op, scene);
829 return OPERATOR_FINISHED;
830 }
831 case SLIP_MODAL_CANCEL: {
832 slip_strips_delta(op, scene, data, -data->prev_offset);
833 slip_cleanup(C, op, scene);
834 return OPERATOR_CANCELLED;
835 }
837 if (!has_num_input) {
838 data->precision = true;
839 /* Align virtual mouse pointer with the truncated frame to avoid jumps. */
840 float mouse_co[2];
841 UI_view2d_region_to_view(v2d, data->virtual_mval_x, 0.0f, &mouse_co[0], &mouse_co[1]);
842 float offset = mouse_co[0] - data->init_mouse_co[0];
843 float subframe_offset = offset - std::trunc(offset);
844 data->virtual_mval_x += -subframe_offset * UI_view2d_scale_get_x(v2d);
845 }
846 break;
848 if (!has_num_input) {
849 data->precision = false;
850 /* If we exit precision mode, make sure we undo the fractional adjustments and align the
851 * virtual mouse pointer. */
852 float to_nearest_frame = -(data->prev_offset - round_fl_to_int(data->prev_offset));
853 slip_strips_delta(op, scene, data, to_nearest_frame);
854 data->virtual_mval_x += to_nearest_frame * UI_view2d_scale_get_x(v2d);
855 }
856 break;
858 data->clamp = !data->clamp;
859 break;
860 default:
861 break;
862 }
863 }
864
865 if (event->type == MOUSEMOVE || event->val == SLIP_MODAL_PRECISION_DISABLE ||
867 {
868 if (!has_num_input) {
869 float mouse_x_delta = event->mval[0] - data->prev_mval_x;
870 data->prev_mval_x += mouse_x_delta;
871 if (data->precision) {
872 mouse_x_delta *= 0.1f;
873 }
874 data->virtual_mval_x += mouse_x_delta;
875
876 float mouse_co[2];
877 UI_view2d_region_to_view(v2d, data->virtual_mval_x, 0.0f, &mouse_co[0], &mouse_co[1]);
878 float offset = mouse_co[0] - data->init_mouse_co[0];
879 if (!data->precision) {
880 offset = std::trunc(offset);
881 }
882
883 float clamped_offset = offset;
884 float clamped_offset_delta = slip_apply_clamp(scene, data, &clamped_offset);
885 /* Also adjust virtual mouse pointer after clamp is applied. */
886 data->virtual_mval_x += (clamped_offset - offset) * UI_view2d_scale_get_x(v2d);
887
888 slip_strips_delta(op, scene, data, clamped_offset_delta);
889 slip_update_header(scene, area, data, clamped_offset);
890
892 }
893 }
894
895 slip_draw_status(C, op);
896
898}
899
901{
902 /* Identifiers. */
903 ot->name = "Slip Strips";
904 ot->idname = "SEQUENCER_OT_slip";
905 ot->description = "Slip the contents of selected strips";
906
907 /* API callbacks. */
908 ot->invoke = sequencer_slip_invoke;
909 ot->modal = sequencer_slip_modal;
910 ot->exec = sequencer_slip_exec;
911 ot->poll = sequencer_edit_poll;
912
913 /* Flags. */
915
916 /* Properties. */
917 PropertyRNA *prop;
918
919 prop = RNA_def_float(ot->srna,
920 "offset",
921 0,
922 -FLT_MAX,
923 FLT_MAX,
924 "Offset",
925 "Offset to the data of the strip",
926 -FLT_MAX,
927 FLT_MAX);
929 RNA_def_boolean(ot->srna,
930 "slip_keyframes",
931 false,
932 "Slip Keyframes",
933 "Move the keyframes alongside the media");
934}
935
937
938/* -------------------------------------------------------------------- */
941
943{
944 Scene *scene = CTX_data_scene(C);
946
947 for (Strip *strip : strips) {
948 if (!RNA_boolean_get(op->ptr, "unselected")) {
949 if (strip->flag & SELECT) {
950 strip->flag |= SEQ_MUTE;
952 }
953 }
954 else {
955 if ((strip->flag & SELECT) == 0) {
956 strip->flag |= SEQ_MUTE;
958 }
959 }
960 }
961
964
965 return OPERATOR_FINISHED;
966}
967
969{
970 /* Identifiers. */
971 ot->name = "Mute Strips";
972 ot->idname = "SEQUENCER_OT_mute";
973 ot->description = "Mute (un)selected strips";
974
975 /* API callbacks. */
976 ot->exec = sequencer_mute_exec;
977 ot->poll = sequencer_edit_poll;
978
979 /* Flags. */
981
983 ot->srna, "unselected", false, "Unselected", "Mute unselected rather than selected strips");
984}
985
987
988/* -------------------------------------------------------------------- */
991
993{
994 Scene *scene = CTX_data_scene(C);
995 Editing *ed = seq::editing_get(scene);
996 ARegion *region = CTX_wm_region(C);
997 const bool is_preview = region && (region->regiontype == RGN_TYPE_PREVIEW) &&
999 LISTBASE_FOREACH (Strip *, strip, ed->seqbasep) {
1000 if (is_preview) {
1001 if (seq::time_strip_intersects_frame(scene, strip, scene->r.cfra) &&
1002 strip->type != STRIP_TYPE_SOUND_RAM)
1003 {
1004 strip->flag &= ~SEQ_MUTE;
1005 seq::relations_invalidate_cache(scene, strip);
1006 }
1007 }
1008 else if (!RNA_boolean_get(op->ptr, "unselected")) {
1009 if (strip->flag & SELECT) {
1010 strip->flag &= ~SEQ_MUTE;
1011 seq::relations_invalidate_cache(scene, strip);
1012 }
1013 }
1014 else {
1015 if ((strip->flag & SELECT) == 0) {
1016 strip->flag &= ~SEQ_MUTE;
1017 seq::relations_invalidate_cache(scene, strip);
1018 }
1019 }
1020 }
1021
1024
1025 return OPERATOR_FINISHED;
1026}
1027
1029{
1030 /* Identifiers. */
1031 ot->name = "Unmute Strips";
1032 ot->idname = "SEQUENCER_OT_unmute";
1033 ot->description = "Unmute (un)selected strips";
1034
1035 /* API callbacks. */
1036 ot->exec = sequencer_unmute_exec;
1037 ot->poll = sequencer_edit_poll;
1038
1039 /* Flags. */
1040 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1041
1042 RNA_def_boolean(ot->srna,
1043 "unselected",
1044 false,
1045 "Unselected",
1046 "Unmute unselected rather than selected strips");
1047}
1048
1050
1051/* -------------------------------------------------------------------- */
1054
1056{
1057 Scene *scene = CTX_data_scene(C);
1058 Editing *ed = seq::editing_get(scene);
1059
1060 LISTBASE_FOREACH (Strip *, strip, ed->seqbasep) {
1061 if (strip->flag & SELECT) {
1062 strip->flag |= SEQ_LOCK;
1063 }
1064 }
1065
1067
1068 return OPERATOR_FINISHED;
1069}
1070
1072{
1073 /* Identifiers. */
1074 ot->name = "Lock Strips";
1075 ot->idname = "SEQUENCER_OT_lock";
1076 ot->description = "Lock strips so they can't be transformed";
1077
1078 /* API callbacks. */
1079 ot->exec = sequencer_lock_exec;
1080 ot->poll = sequencer_edit_poll;
1081
1082 /* Flags. */
1083 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1084}
1085
1087
1088/* -------------------------------------------------------------------- */
1091
1093{
1094 Scene *scene = CTX_data_scene(C);
1095 Editing *ed = seq::editing_get(scene);
1096
1097 LISTBASE_FOREACH (Strip *, strip, ed->seqbasep) {
1098 if (strip->flag & SELECT) {
1099 strip->flag &= ~SEQ_LOCK;
1100 }
1101 }
1102
1104
1105 return OPERATOR_FINISHED;
1106}
1107
1109{
1110 /* Identifiers. */
1111 ot->name = "Unlock Strips";
1112 ot->idname = "SEQUENCER_OT_unlock";
1113 ot->description = "Unlock strips so they can be transformed";
1114
1115 /* API callbacks. */
1116 ot->exec = sequencer_unlock_exec;
1117 ot->poll = sequencer_edit_poll;
1118
1119 /* Flags. */
1120 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1121}
1122
1124
1125/* -------------------------------------------------------------------- */
1128
1130{
1131 Scene *scene = CTX_data_scene(C);
1132 Editing *ed = seq::editing_get(scene);
1133 ListBase *active_seqbase = seq::active_seqbase_get(ed);
1134
1136
1137 if (selected.is_empty()) {
1138 return OPERATOR_CANCELLED;
1139 }
1140
1141 const bool toggle = RNA_boolean_get(op->ptr, "toggle");
1142 if (toggle && seq::are_strips_connected_together(selected)) {
1143 seq::disconnect(selected);
1144 }
1145 else {
1146 seq::connect(selected);
1147 }
1148
1150 return OPERATOR_FINISHED;
1151}
1152
1154{
1155 ot->name = "Connect Strips";
1156 ot->idname = "SEQUENCER_OT_connect";
1157 ot->description = "Link selected strips together for simplified group selection";
1158
1159 ot->exec = sequencer_connect_exec;
1160 ot->poll = sequencer_edit_poll;
1161
1162 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1163
1164 RNA_def_boolean(ot->srna, "toggle", true, "Toggle", "Toggle strip connections");
1165}
1166
1168
1169/* -------------------------------------------------------------------- */
1172
1174{
1175 Scene *scene = CTX_data_scene(C);
1176 Editing *ed = seq::editing_get(scene);
1177 ListBase *active_seqbase = seq::active_seqbase_get(ed);
1178
1180
1181 if (seq::disconnect(selected)) {
1183 return OPERATOR_FINISHED;
1184 }
1185 return OPERATOR_CANCELLED;
1186}
1187
1189{
1190 ot->name = "Disconnect Strips";
1191 ot->idname = "SEQUENCER_OT_disconnect";
1192 ot->description = "Unlink selected strips so that they can be selected individually";
1193
1195 ot->poll = sequencer_edit_poll;
1196
1197 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1198}
1199
1201
1202/* -------------------------------------------------------------------- */
1205
1207{
1208 Main *bmain = CTX_data_main(C);
1209 Scene *scene = CTX_data_scene(C);
1210 Editing *ed = seq::editing_get(scene);
1211 const bool adjust_length = RNA_boolean_get(op->ptr, "adjust_length");
1212
1213 LISTBASE_FOREACH (Strip *, strip, ed->seqbasep) {
1214 if (strip->flag & SELECT) {
1215 seq::add_reload_new_file(bmain, scene, strip, !adjust_length);
1217
1218 if (adjust_length) {
1219 if (seq::transform_test_overlap(scene, ed->seqbasep, strip)) {
1220 seq::transform_seqbase_shuffle(ed->seqbasep, strip, scene);
1221 }
1222 }
1223 }
1224 }
1225
1227
1228 return OPERATOR_FINISHED;
1229}
1230
1232{
1233 PropertyRNA *prop;
1234
1235 /* Identifiers. */
1236 ot->name = "Reload Strips";
1237 ot->idname = "SEQUENCER_OT_reload";
1238 ot->description = "Reload strips in the sequencer";
1239
1240 /* API callbacks. */
1241 ot->exec = sequencer_reload_exec;
1242 ot->poll = sequencer_edit_poll;
1243
1244 /* Flags. */
1245 ot->flag = OPTYPE_REGISTER; /* No undo, the data changed is stored outside 'main'. */
1246
1247 prop = RNA_def_boolean(ot->srna,
1248 "adjust_length",
1249 false,
1250 "Adjust Length",
1251 "Adjust length of strips to their data length");
1253}
1254
1256
1257/* -------------------------------------------------------------------- */
1260
1262{
1263 if (G.is_rendering) {
1264 return false;
1265 }
1266 return sequencer_edit_poll(C);
1267}
1268
1270{
1271 Scene *scene = CTX_data_scene(C);
1272 Editing *ed = seq::editing_get(scene);
1273
1274 seq::relations_free_imbuf(scene, &ed->seqbase, false);
1276 seq::cache_cleanup(scene);
1277
1279
1280 return OPERATOR_FINISHED;
1281}
1282
1284{
1285 /* Identifiers. */
1286 ot->name = "Refresh Sequencer";
1287 ot->idname = "SEQUENCER_OT_refresh_all";
1288 ot->description = "Refresh the sequencer editor";
1289
1290 /* API callbacks. */
1293}
1294
1296
1297/* -------------------------------------------------------------------- */
1300
1302{
1303 if (inputs.size() > 2) {
1304 return "Cannot apply effect to more than 2 strips with video content";
1305 }
1306 if (num_inputs == 2 && inputs.size() != 2) {
1307 return "Exactly 2 selected strips with video content are needed";
1308 }
1309 if (num_inputs == 1 && inputs.size() != 1) {
1310 return "Exactly one selected strip with video content is needed";
1311 }
1312 return "";
1313}
1314
1316 StripType effect_type,
1317 int num_inputs,
1318 bool ignore_active)
1319{
1320 BLI_assert(num_inputs <= 2);
1321 if (num_inputs == 0) {
1322 return {};
1323 }
1324
1325 Editing *ed = seq::editing_get(scene);
1327 /* Ignore sound strips for now (avoids unnecessary errors when connected strips are
1328 * selected together, and the intent to operate on strips with video content is clear). */
1329 inputs.remove_if([&](Strip *strip) { return strip->type == STRIP_TYPE_SOUND_RAM; });
1330
1331 if (ignore_active) {
1332 /* If `ignore_active` is true, this function is being called from the reassign inputs
1333 * operator, meaning the active strip must be the effect strip to reassign. */
1334 Strip *active_strip = seq::select_active_get(scene);
1335 inputs.remove_if([&](Strip *strip) { return strip == active_strip; });
1336 }
1337
1338 while (inputs.size() > num_inputs) {
1339 inputs.pop();
1340 }
1341
1342 if (inputs.size() == 2 && num_inputs == 2) {
1343 Strip *first = inputs[0];
1344 Strip *second = inputs[1];
1345 bool do_swap = false;
1347 /* Sort by timeline frame so 2-input transitions go "from" earlier "to" later. */
1348 const int first_start = seq::time_left_handle_frame_get(scene, first);
1349 const int second_start = seq::time_left_handle_frame_get(scene, second);
1350 do_swap = (first_start > second_start) ||
1351 (first_start == second_start &&
1352 seq::time_right_handle_frame_get(scene, first) >
1353 seq::time_right_handle_frame_get(scene, second));
1354 }
1355 else if (first == seq::select_active_get(scene) ||
1356 (ignore_active && first->channel < second->channel))
1357 {
1358 /* Other 2-input effects (blend-modes) make the active strip the second input. If neither
1359 * strip is active (as in reassign inputs), make it the strip on the lower channel. */
1360 do_swap = true;
1361 }
1362 if (do_swap) {
1363 inputs.clear();
1364 inputs.add(second);
1365 inputs.add(first);
1366 }
1367 }
1368
1369 return inputs;
1370}
1371
1373{
1374 Scene *scene = CTX_data_scene(C);
1375 Strip *active_strip = seq::select_active_get(scene);
1376 const int num_inputs = seq::effect_get_num_inputs(active_strip->type);
1377
1378 if (num_inputs == 0) {
1379 BKE_report(op->reports, RPT_ERROR, "Cannot reassign inputs: strip has no inputs");
1380 return OPERATOR_CANCELLED;
1381 }
1382
1384 scene, StripType(active_strip->type), num_inputs, true);
1385 StringRef error_msg = effect_inputs_validate(inputs, num_inputs);
1386
1387 if (!error_msg.is_empty()) {
1388 BKE_report(op->reports, RPT_ERROR, error_msg.data());
1389 return OPERATOR_CANCELLED;
1390 }
1391
1392 Strip *input1 = inputs[0];
1393 Strip *input2 = inputs.size() == 2 ? inputs[1] : nullptr;
1394
1395 /* Check if reassigning would create recursivity. */
1396 if (seq::relations_render_loop_check(input1, active_strip) ||
1397 seq::relations_render_loop_check(input2, active_strip))
1398 {
1399 BKE_report(op->reports, RPT_ERROR, "Cannot reassign inputs: recursion detected");
1400 return OPERATOR_CANCELLED;
1401 }
1402
1403 active_strip->input1 = input1;
1404 active_strip->input2 = input2;
1405
1406 int old_start = active_strip->start;
1407
1408 /* Force time position update for reassigned effects.
1409 * TODO(Richard): This is because internally startdisp is still used, due to poor performance
1410 * of mapping effect range to inputs. This mapping could be cached though. */
1413
1414 seq::relations_invalidate_cache(scene, active_strip);
1415 seq::offset_animdata(scene, active_strip, (active_strip->start - old_start));
1416
1418
1419 return OPERATOR_FINISHED;
1420}
1421
1423{
1424 /* Identifiers. */
1425 ot->name = "Reassign Inputs";
1426 ot->idname = "SEQUENCER_OT_reassign_inputs";
1427 ot->description = "Reassign the inputs for the effect strip";
1428
1429 /* API callbacks. */
1431 ot->poll = sequencer_effect_poll;
1432
1433 /* Flags. */
1434 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1435}
1436
1438
1439/* -------------------------------------------------------------------- */
1442
1444{
1445 Scene *scene = CTX_data_scene(C);
1446 Strip *active_strip = seq::select_active_get(scene);
1447
1448 if (!(active_strip->type & STRIP_TYPE_EFFECT)) {
1449 BKE_report(op->reports, RPT_ERROR, "Active strip is not an effect strip");
1450 return OPERATOR_CANCELLED;
1451 }
1452
1453 if (seq::effect_get_num_inputs(active_strip->type) != 2 || active_strip->input1 == nullptr ||
1454 active_strip->input2 == nullptr)
1455 {
1456 BKE_report(op->reports, RPT_ERROR, "Strip needs two inputs to swap");
1457 return OPERATOR_CANCELLED;
1458 }
1459
1460 Strip *strip = active_strip->input1;
1461 active_strip->input1 = active_strip->input2;
1462 active_strip->input2 = strip;
1463
1464 seq::relations_invalidate_cache(scene, active_strip);
1466
1467 return OPERATOR_FINISHED;
1468}
1470{
1471 /* Identifiers. */
1472 ot->name = "Swap Inputs";
1473 ot->idname = "SEQUENCER_OT_swap_inputs";
1474 ot->description = "Swap the two inputs of the effect strip";
1475
1476 /* API callbacks. */
1479
1480 /* Flags. */
1481 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1482}
1483
1485
1486/* -------------------------------------------------------------------- */
1489
1490static int mouse_frame_side(View2D *v2d, short mouse_x, int frame)
1491{
1492 int mval[2];
1493 float mouseloc[2];
1494
1495 mval[0] = mouse_x;
1496 mval[1] = 0;
1497
1498 /* Choose the side based on which side of the current frame the mouse is on. */
1499 UI_view2d_region_to_view(v2d, mval[0], mval[1], &mouseloc[0], &mouseloc[1]);
1500
1501 return mouseloc[0] > frame ? seq::SIDE_RIGHT : seq::SIDE_LEFT;
1502}
1503
1505 {seq::SPLIT_SOFT, "SOFT", 0, "Soft", ""},
1506 {seq::SPLIT_HARD, "HARD", 0, "Hard", ""},
1507 {0, nullptr, 0, nullptr, nullptr},
1508};
1509
1511 {seq::SIDE_MOUSE, "MOUSE", 0, "Mouse Position", ""},
1512 {seq::SIDE_LEFT, "LEFT", 0, "Left", ""},
1513 {seq::SIDE_RIGHT, "RIGHT", 0, "Right", ""},
1514 {seq::SIDE_BOTH, "BOTH", 0, "Both", ""},
1515 {seq::SIDE_NO_CHANGE, "NO_CHANGE", 0, "No Change", ""},
1516 {0, nullptr, 0, nullptr, nullptr},
1517};
1518
1519/* Get the splitting side for the Split Strips's operator exec() callback. */
1521{
1522 const int split_side = RNA_enum_get(op->ptr, "side");
1523
1524 /* The mouse position can not be resolved from the exec() as the mouse coordinate is not
1525 * accessible. So fall-back to the RIGHT side instead.
1526 *
1527 * The SEQ_SIDE_MOUSE is used by the Strip menu, together with the EXEC_DEFAULT operator
1528 * context in order to have properly resolved shortcut in the menu. */
1529 if (split_side == seq::SIDE_MOUSE) {
1530 return seq::SIDE_RIGHT;
1531 }
1532
1533 return split_side;
1534}
1535
1537{
1538 Main *bmain = CTX_data_main(C);
1539 Scene *scene = CTX_data_scene(C);
1540 Editing *ed = seq::editing_get(scene);
1541 bool changed = false;
1542 bool strip_selected = false;
1543
1544 const bool use_cursor_position = RNA_boolean_get(op->ptr, "use_cursor_position");
1545
1546 const int split_frame = RNA_struct_property_is_set(op->ptr, "frame") ?
1547 RNA_int_get(op->ptr, "frame") :
1548 scene->r.cfra;
1549 const int split_channel = RNA_int_get(op->ptr, "channel");
1550
1551 const seq::eSplitMethod method = seq::eSplitMethod(RNA_enum_get(op->ptr, "type"));
1552 const int split_side = sequence_split_side_for_exec_get(op);
1553 const bool ignore_selection = RNA_boolean_get(op->ptr, "ignore_selection");
1554 const bool ignore_connections = RNA_boolean_get(op->ptr, "ignore_connections");
1555
1556 seq::prefetch_stop(scene);
1557
1558 LISTBASE_FOREACH_BACKWARD (Strip *, strip, ed->seqbasep) {
1559 if (use_cursor_position && strip->channel != split_channel) {
1560 continue;
1561 }
1562
1563 if (ignore_selection || strip->flag & SELECT) {
1564 const char *error_msg = nullptr;
1565 if (seq::edit_strip_split(bmain,
1566 scene,
1567 ed->seqbasep,
1568 strip,
1569 split_frame,
1570 method,
1571 ignore_connections,
1572 &error_msg) != nullptr)
1573 {
1574 changed = true;
1575 }
1576 if (error_msg != nullptr) {
1577 BKE_report(op->reports, RPT_ERROR, error_msg);
1578 }
1579 }
1580 }
1581
1582 if (changed) { /* Got new strips? */
1583 if (ignore_selection) {
1584 if (use_cursor_position) {
1586 if (seq::time_right_handle_frame_get(scene, strip) == split_frame &&
1587 strip->channel == split_channel)
1588 {
1589 strip_selected = strip->flag & STRIP_ALLSEL;
1590 }
1591 }
1592 if (!strip_selected) {
1594 if (seq::time_left_handle_frame_get(scene, strip) == split_frame &&
1595 strip->channel == split_channel)
1596 {
1597 strip->flag &= ~STRIP_ALLSEL;
1598 }
1599 }
1600 }
1601 }
1602 }
1603 else {
1604 if (split_side != seq::SIDE_BOTH) {
1606 if (split_side == seq::SIDE_LEFT) {
1607 if (seq::time_left_handle_frame_get(scene, strip) >= split_frame) {
1608 strip->flag &= ~STRIP_ALLSEL;
1609 }
1610 }
1611 else {
1612 if (seq::time_right_handle_frame_get(scene, strip) <= split_frame) {
1613 strip->flag &= ~STRIP_ALLSEL;
1614 }
1615 }
1616 }
1617 }
1618 }
1619 }
1620 if (changed) {
1622 return OPERATOR_FINISHED;
1623 }
1624
1625 /* Passthrough to selection if used as tool. */
1627}
1628
1630{
1631 Scene *scene = CTX_data_scene(C);
1633
1634 int split_side = RNA_enum_get(op->ptr, "side");
1635 int split_frame = scene->r.cfra;
1636
1637 if (split_side == seq::SIDE_MOUSE) {
1638 if (ED_operator_sequencer_active(C) && v2d) {
1639 split_side = mouse_frame_side(v2d, event->mval[0], split_frame);
1640 }
1641 else {
1642 split_side = seq::SIDE_BOTH;
1643 }
1644 }
1645 float mouseloc[2];
1646 if (v2d) {
1647 UI_view2d_region_to_view(v2d, event->mval[0], event->mval[1], &mouseloc[0], &mouseloc[1]);
1648 if (RNA_boolean_get(op->ptr, "use_cursor_position")) {
1649 split_frame = round_fl_to_int(mouseloc[0]);
1650 Strip *strip = strip_under_mouse_get(scene, v2d, event->mval);
1651 if (strip == nullptr || split_frame == seq::time_left_handle_frame_get(scene, strip) ||
1652 split_frame == seq::time_right_handle_frame_get(scene, strip))
1653 {
1654 /* Do not pass through to selection. */
1655 return OPERATOR_CANCELLED;
1656 }
1657 }
1658 RNA_int_set(op->ptr, "channel", mouseloc[1]);
1659 }
1660 RNA_int_set(op->ptr, "frame", split_frame);
1661 RNA_enum_set(op->ptr, "side", split_side);
1662 // RNA_enum_set(op->ptr, "type", split_hard);
1663
1664 return sequencer_split_exec(C, op);
1665}
1666
1667static void sequencer_split_ui(bContext * /*C*/, wmOperator *op)
1668{
1669 uiLayout *layout = op->layout;
1670 uiLayoutSetPropSep(layout, true);
1671 uiLayoutSetPropDecorate(layout, false);
1672
1673 uiLayout *row = &layout->row(false);
1674 row->prop(op->ptr, "type", UI_ITEM_R_EXPAND, std::nullopt, ICON_NONE);
1675 layout->prop(op->ptr, "frame", UI_ITEM_NONE, std::nullopt, ICON_NONE);
1676 layout->prop(op->ptr, "side", UI_ITEM_NONE, std::nullopt, ICON_NONE);
1677
1678 layout->separator();
1679
1680 layout->prop(op->ptr, "use_cursor_position", UI_ITEM_NONE, std::nullopt, ICON_NONE);
1681 if (RNA_boolean_get(op->ptr, "use_cursor_position")) {
1682 layout->prop(op->ptr, "channel", UI_ITEM_NONE, std::nullopt, ICON_NONE);
1683 }
1684
1685 layout->separator();
1686
1687 layout->prop(op->ptr, "ignore_connections", UI_ITEM_NONE, std::nullopt, ICON_NONE);
1688}
1689
1691{
1692 /* Identifiers. */
1693 ot->name = "Split Strips";
1694 ot->idname = "SEQUENCER_OT_split";
1695 ot->description = "Split the selected strips in two";
1696
1697 /* API callbacks. */
1698 ot->invoke = sequencer_split_invoke;
1699 ot->exec = sequencer_split_exec;
1700 ot->poll = sequencer_edit_poll;
1701 ot->ui = sequencer_split_ui;
1702
1703 /* Flags. */
1704 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1705
1706 PropertyRNA *prop;
1707 RNA_def_int(ot->srna,
1708 "frame",
1709 0,
1710 INT_MIN,
1711 INT_MAX,
1712 "Frame",
1713 "Frame where selected strips will be split",
1714 INT_MIN,
1715 INT_MAX);
1716 RNA_def_int(ot->srna,
1717 "channel",
1718 0,
1719 INT_MIN,
1720 INT_MAX,
1721 "Channel",
1722 "Channel in which strip will be cut",
1723 INT_MIN,
1724 INT_MAX);
1725 RNA_def_enum(ot->srna,
1726 "type",
1729 "Type",
1730 "The type of split operation to perform on strips");
1731
1732 RNA_def_boolean(ot->srna,
1733 "use_cursor_position",
1734 false,
1735 "Use Cursor Position",
1736 "Split at position of the cursor instead of current frame");
1737
1738 prop = RNA_def_enum(ot->srna,
1739 "side",
1742 "Side",
1743 "The side that remains selected after splitting");
1744
1746
1747 prop = RNA_def_boolean(
1748 ot->srna,
1749 "ignore_selection",
1750 false,
1751 "Ignore Selection",
1752 "Make cut even if strip is not selected preserving selection state after cut");
1753
1755
1756 RNA_def_boolean(ot->srna,
1757 "ignore_connections",
1758 false,
1759 "Ignore Connections",
1760 "Don't propagate split to connected strips");
1761}
1762
1764
1765/* -------------------------------------------------------------------- */
1768
1770{
1771 Scene *scene = CTX_data_scene(C);
1772 Editing *ed = seq::editing_get(scene);
1773 ARegion *region = CTX_wm_region(C);
1774
1775 if (ed == nullptr) {
1776 return OPERATOR_CANCELLED;
1777 }
1778
1779 Strip *active_strip = seq::select_active_get(scene);
1780 ListBase duplicated_strips = {nullptr, nullptr};
1781
1782 /* Special case for duplicating strips in preview: Do not duplicate sound strips,muted
1783 * strips and strips that do not intersect the current frame */
1785 LISTBASE_FOREACH (Strip *, strip, ed->seqbasep) {
1786 if (strip->type == STRIP_TYPE_SOUND_RAM || strip->flag & SEQ_MUTE ||
1787 !(seq::time_strip_intersects_frame(scene, strip, scene->r.cfra)))
1788 {
1789 strip->flag &= ~STRIP_ALLSEL;
1790 }
1791 }
1792 }
1793
1794 seq::seqbase_duplicate_recursive(scene, scene, &duplicated_strips, ed->seqbasep, 0, 0);
1795 deselect_all_strips(scene);
1796
1797 if (duplicated_strips.first == nullptr) {
1798 return OPERATOR_CANCELLED;
1799 }
1800
1801 /* Duplicate animation.
1802 * First backup original curves from scene and duplicate strip curves from backup into scene.
1803 * This way, when pasted strips are renamed, curves are renamed with them. Finally, restore
1804 * original curves from backup.
1805 */
1806 seq::AnimationBackup animation_backup = {{nullptr}};
1807 seq::animation_backup_original(scene, &animation_backup);
1808
1810 Strip *strip_last = static_cast<Strip *>(seqbase->last);
1811
1812 /* Rely on the `duplicated_strips` list being added at the end.
1813 * Their UIDs has been re-generated by the #SEQ_sequence_base_dupli_recursive(). */
1814 BLI_movelisttolist(ed->seqbasep, &duplicated_strips);
1815
1816 /* Handle duplicated strips: set active, select, ensure unique name and duplicate animation
1817 * data. */
1818 for (Strip *strip = strip_last->next; strip; strip = strip->next) {
1819 if (active_strip != nullptr && STREQ(strip->name, active_strip->name)) {
1820 seq::select_active_set(scene, strip);
1821 }
1822 strip->flag &= ~(SEQ_LEFTSEL + SEQ_RIGHTSEL + SEQ_LOCK);
1823 strip->flag |= SEQ_IGNORE_CHANNEL_LOCK;
1824
1825 seq::animation_duplicate_backup_to_scene(scene, strip, &animation_backup);
1826 seq::ensure_unique_name(strip, scene);
1827 }
1828
1829 /* Special case for duplicating strips in preview: handle overlap, because strips won't be
1830 * translated. */
1832 for (Strip *strip = strip_last->next; strip; strip = strip->next) {
1833 if (seq::transform_test_overlap(scene, ed->seqbasep, strip)) {
1834 seq::transform_seqbase_shuffle(ed->seqbasep, strip, scene);
1835 }
1836 strip->flag &= ~SEQ_IGNORE_CHANNEL_LOCK;
1837 }
1838 }
1839
1840 seq::animation_restore_original(scene, &animation_backup);
1841
1845 return OPERATOR_FINISHED;
1846}
1847
1849{
1850 /* Identifiers. */
1851 ot->name = "Duplicate Strips";
1852 ot->idname = "SEQUENCER_OT_duplicate";
1853 ot->description = "Duplicate the selected strips";
1854
1855 /* API callbacks. */
1858
1859 /* Flags. */
1860 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1861}
1862
1864
1865/* -------------------------------------------------------------------- */
1868
1870{
1871 if (strip->type != STRIP_TYPE_SCENE) {
1872 return;
1873 }
1874
1875 Main *bmain = CTX_data_main(C);
1876 if (strip->scene) {
1877 if (ED_scene_delete(C, bmain, strip->scene)) {
1879 }
1880 }
1881}
1882
1884{
1885 Main *bmain = CTX_data_main(C);
1886 Scene *scene = CTX_data_scene(C);
1888 const bool delete_data = RNA_boolean_get(op->ptr, "delete_data");
1889
1891 return OPERATOR_CANCELLED;
1892 }
1893
1894 seq::prefetch_stop(scene);
1895
1896 for (Strip *strip : selected_strips_from_context(C)) {
1897 seq::edit_flag_for_removal(scene, seqbasep, strip);
1898 if (delete_data) {
1900 }
1901 }
1902 seq::edit_remove_flagged_strips(scene, seqbasep);
1903
1905 if (scene->adt && scene->adt->action) {
1907 }
1911 return OPERATOR_FINISHED;
1912}
1913
1915{
1916 Scene *scene = CTX_data_scene(C);
1917 ListBase *markers = &scene->markers;
1918
1919 if (!BLI_listbase_is_empty(markers)) {
1920 ARegion *region = CTX_wm_region(C);
1921 if (region && (region->regiontype == RGN_TYPE_WINDOW)) {
1922 /* Bounding box of 30 pixels is used for markers shortcuts,
1923 * prevent conflict with markers shortcuts here. */
1924 if (event->mval[1] <= 30) {
1925 return OPERATOR_PASS_THROUGH;
1926 }
1927 }
1928 }
1929
1930 return sequencer_delete_exec(C, op);
1931}
1932
1934{
1935 /* Identifiers. */
1936 ot->name = "Delete Strips";
1937 ot->idname = "SEQUENCER_OT_delete";
1938 ot->description = "Delete selected strips from the sequencer";
1939
1940 /* API callbacks. */
1941 ot->invoke = sequencer_delete_invoke;
1942 ot->exec = sequencer_delete_exec;
1943 ot->poll = sequencer_edit_poll;
1944
1945 /* Flags. */
1946 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1947
1948 /* Properties. */
1949 ot->prop = RNA_def_boolean(ot->srna,
1950 "delete_data",
1951 false,
1952 "Delete Data",
1953 "After removing the Strip, delete the associated data also");
1955}
1956
1958
1959/* -------------------------------------------------------------------- */
1962
1964{
1965 Scene *scene = CTX_data_scene(C);
1966 Editing *ed = seq::editing_get(scene);
1967 Strip *strip;
1969
1970 /* For effects, try to find a replacement input. */
1971 for (strip = static_cast<Strip *>(ed->seqbasep->first); strip;
1972 strip = static_cast<Strip *>(strip->next))
1973 {
1974 if (seq::transform_is_locked(channels, strip)) {
1975 continue;
1976 }
1977
1978 if ((strip->type & STRIP_TYPE_EFFECT) == 0 && (strip->flag & SELECT)) {
1979 strip->startofs = strip->endofs = 0;
1980 }
1981 }
1982
1983 /* Update lengths, etc. */
1984 strip = static_cast<Strip *>(ed->seqbasep->first);
1985 while (strip) {
1986 seq::relations_invalidate_cache(scene, strip);
1987 strip = strip->next;
1988 }
1989
1990 for (strip = static_cast<Strip *>(ed->seqbasep->first); strip;
1991 strip = static_cast<Strip *>(strip->next))
1992 {
1993 if ((strip->type & STRIP_TYPE_EFFECT) == 0 && (strip->flag & SELECT)) {
1994 if (seq::transform_test_overlap(scene, ed->seqbasep, strip)) {
1995 seq::transform_seqbase_shuffle(ed->seqbasep, strip, scene);
1996 }
1997 }
1998 }
1999
2001
2002 return OPERATOR_FINISHED;
2003}
2004
2006{
2007 /* Identifiers. */
2008 ot->name = "Clear Strip Offset";
2009 ot->idname = "SEQUENCER_OT_offset_clear";
2010 ot->description = "Clear strip offsets from the start and end frames";
2011
2012 /* API callbacks. */
2014 ot->poll = sequencer_edit_poll;
2015
2016 /* Flags. */
2017 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2018}
2019
2021
2022/* -------------------------------------------------------------------- */
2025
2027{
2028 Scene *scene = CTX_data_scene(C);
2029 Editing *ed = seq::editing_get(scene);
2031
2032 Strip *strip, *strip_new;
2033 StripData *data_new;
2034 StripElem *se, *se_new;
2035 int start_ofs, timeline_frame, frame_end;
2036 int step = RNA_int_get(op->ptr, "length");
2037
2038 strip = static_cast<Strip *>(seqbase->first); /* Poll checks this is valid. */
2039
2040 seq::prefetch_stop(scene);
2041
2042 while (strip) {
2043 if ((strip->flag & SELECT) && (strip->type == STRIP_TYPE_IMAGE) && (strip->len > 1)) {
2044 Strip *strip_next;
2045
2046 /* TODO: remove f-curve and assign to split image strips.
2047 * The old animation system would remove the user of `strip->ipo`. */
2048
2049 start_ofs = timeline_frame = seq::time_left_handle_frame_get(scene, strip);
2050 frame_end = seq::time_right_handle_frame_get(scene, strip);
2051
2052 while (timeline_frame < frame_end) {
2053 /* New strip. */
2054 se = seq::render_give_stripelem(scene, strip, timeline_frame);
2055
2057 scene, scene, seqbase, strip, STRIP_DUPE_UNIQUE_NAME);
2058
2059 strip_new->start = start_ofs;
2060 strip_new->type = STRIP_TYPE_IMAGE;
2061 strip_new->len = 1;
2062 strip_new->flag |= SEQ_SINGLE_FRAME_CONTENT;
2063 strip_new->endofs = 1 - step;
2064
2065 /* New strip. */
2066 data_new = strip_new->data;
2067 data_new->us = 1;
2068
2069 /* New stripdata, only one element now. */
2070 /* Note this assume all elements (images) have the same dimension,
2071 * since we only copy the name here. */
2072 se_new = static_cast<StripElem *>(MEM_reallocN(data_new->stripdata, sizeof(*se_new)));
2073 STRNCPY(se_new->filename, se->filename);
2074 data_new->stripdata = se_new;
2075
2076 if (step > 1) {
2077 strip_new->flag &= ~SEQ_OVERLAP;
2078 if (seq::transform_test_overlap(scene, seqbase, strip_new)) {
2079 seq::transform_seqbase_shuffle(seqbase, strip_new, scene);
2080 }
2081 }
2082
2083 /* XXX, COPY FCURVES */
2084
2085 timeline_frame++;
2086 start_ofs += step;
2087 }
2088
2089 strip_next = static_cast<Strip *>(strip->next);
2090 seq::edit_flag_for_removal(scene, seqbase, strip);
2091 strip = strip_next;
2092 }
2093 else {
2094 strip = strip->next;
2095 }
2096 }
2097
2098 seq::edit_remove_flagged_strips(scene, seqbase);
2100
2101 return OPERATOR_FINISHED;
2102}
2103
2105 wmOperator *op,
2106 const wmEvent *event)
2107{
2109 C, op, event, IFACE_("Separate Sequence Images"), IFACE_("Separate"));
2110}
2111
2113{
2114 /* Identifiers. */
2115 ot->name = "Separate Images";
2116 ot->idname = "SEQUENCER_OT_images_separate";
2117 ot->description = "On image sequence strips, it returns a strip for each image";
2118
2119 /* API callbacks. */
2122 ot->poll = sequencer_edit_poll;
2123
2124 /* Flags. */
2125 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2126
2127 RNA_def_int(ot->srna, "length", 1, 1, INT_MAX, "Length", "Length of each frame", 1, 1000);
2128}
2129
2131
2132/* -------------------------------------------------------------------- */
2135
2137{
2138 Scene *scene = CTX_data_scene(C);
2139 Editing *ed = seq::editing_get(scene);
2140 Strip *active_strip = seq::select_active_get(scene);
2141
2142 seq::prefetch_stop(scene);
2143
2144 if (active_strip && active_strip->type == STRIP_TYPE_META && active_strip->flag & SELECT) {
2145 /* Deselect active meta strip. */
2146 seq::select_active_set(scene, nullptr);
2147 seq::meta_stack_set(scene, active_strip);
2148 }
2149 else {
2150 /* Exit meta-strip if possible. */
2151 if (BLI_listbase_is_empty(&ed->metastack)) {
2152 return OPERATOR_CANCELLED;
2153 }
2154
2155 /* Display parent meta. */
2156 Strip *meta_parent = seq::meta_stack_pop(ed);
2157 seq::select_active_set(scene, meta_parent);
2158 }
2159
2162
2163 return OPERATOR_FINISHED;
2164}
2165
2167{
2168 /* Identifiers. */
2169 ot->name = "Toggle Meta Strip";
2170 ot->idname = "SEQUENCER_OT_meta_toggle";
2171 ot->description = "Toggle a meta-strip (to edit enclosed strips)";
2172
2173 /* API callbacks. */
2175 ot->poll = sequencer_edit_poll;
2176
2177 /* Flags. */
2178 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2179}
2180
2182
2183/* -------------------------------------------------------------------- */
2186
2188{
2189 Scene *scene = CTX_data_scene(C);
2190 Editing *ed = seq::editing_get(scene);
2191 Strip *active_strip = seq::select_active_get(scene);
2192 ListBase *active_seqbase = seq::active_seqbase_get(ed);
2193
2195
2196 if (selected.is_empty()) {
2197 return OPERATOR_CANCELLED;
2198 }
2199
2200 seq::prefetch_stop(scene);
2201
2202 int channel_max = 1, channel_min = INT_MAX, meta_start_frame = MAXFRAME,
2203 meta_end_frame = MINFRAME;
2204 Strip *strip_meta = seq::strip_alloc(active_seqbase, 1, 1, STRIP_TYPE_META);
2205
2206 /* Remove all selected from main list, and put in meta.
2207 * Strip is moved within the same edit, no need to re-generate the UID. */
2208 blender::VectorSet<Strip *> strips_to_move;
2209 strips_to_move.add_multiple(selected);
2211 scene, active_seqbase, strips_to_move, seq::query_strip_connected_and_effect_chain);
2212
2213 for (Strip *strip : strips_to_move) {
2214 seq::relations_invalidate_cache(scene, strip);
2215 BLI_remlink(active_seqbase, strip);
2216 BLI_addtail(&strip_meta->seqbase, strip);
2217 channel_max = max_ii(strip->channel, channel_max);
2218 channel_min = min_ii(strip->channel, channel_min);
2219 meta_start_frame = min_ii(seq::time_left_handle_frame_get(scene, strip), meta_start_frame);
2220 meta_end_frame = max_ii(seq::time_right_handle_frame_get(scene, strip), meta_end_frame);
2221 }
2222
2223 ListBase *channels_cur = seq::channels_displayed_get(ed);
2224 ListBase *channels_meta = &strip_meta->channels;
2225 for (int i = channel_min; i <= channel_max; i++) {
2226 SeqTimelineChannel *channel_cur = seq::channel_get_by_index(channels_cur, i);
2227 SeqTimelineChannel *channel_meta = seq::channel_get_by_index(channels_meta, i);
2228 STRNCPY(channel_meta->name, channel_cur->name);
2229 channel_meta->flag = channel_cur->flag;
2230 }
2231
2232 const int channel = active_strip ? active_strip->channel : channel_max;
2233 seq::strip_channel_set(strip_meta, channel);
2234 BLI_strncpy(strip_meta->name + 2, DATA_("MetaStrip"), sizeof(strip_meta->name) - 2);
2235 seq::strip_unique_name_set(scene, &ed->seqbase, strip_meta);
2236 strip_meta->start = meta_start_frame;
2237 strip_meta->len = meta_end_frame - meta_start_frame;
2238 seq::select_active_set(scene, strip_meta);
2239 if (seq::transform_test_overlap(scene, active_seqbase, strip_meta)) {
2240 seq::transform_seqbase_shuffle(active_seqbase, strip_meta, scene);
2241 }
2242
2246
2247 return OPERATOR_FINISHED;
2248}
2249
2251{
2252 /* Identifiers. */
2253 ot->name = "Make Meta Strip";
2254 ot->idname = "SEQUENCER_OT_meta_make";
2255 ot->description = "Group selected strips into a meta-strip";
2256
2257 /* API callbacks. */
2259 ot->poll = sequencer_edit_poll;
2260
2261 /* Flags. */
2262 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2263}
2264
2266
2267/* -------------------------------------------------------------------- */
2270
2272{
2273 Scene *scene = CTX_data_scene(C);
2274 Editing *ed = seq::editing_get(scene);
2275 Strip *active_strip = seq::select_active_get(scene);
2276
2277 if (active_strip == nullptr || active_strip->type != STRIP_TYPE_META) {
2278 return OPERATOR_CANCELLED;
2279 }
2280
2281 seq::prefetch_stop(scene);
2282
2283 LISTBASE_FOREACH (Strip *, strip, &active_strip->seqbase) {
2284 seq::relations_invalidate_cache(scene, strip);
2285 }
2286
2287 /* Remove all selected from meta, and put in main list.
2288 * Strip is moved within the same edit, no need to re-generate the UID. */
2289 BLI_movelisttolist(ed->seqbasep, &active_strip->seqbase);
2290 BLI_listbase_clear(&active_strip->seqbase);
2291
2292 ListBase *active_seqbase = seq::active_seqbase_get(ed);
2293 seq::edit_flag_for_removal(scene, active_seqbase, active_strip);
2294 seq::edit_remove_flagged_strips(scene, active_seqbase);
2295
2296 /* Test for effects and overlap. */
2297 LISTBASE_FOREACH (Strip *, strip, active_seqbase) {
2298 if (strip->flag & SELECT) {
2299 strip->flag &= ~SEQ_OVERLAP;
2300 if (seq::transform_test_overlap(scene, active_seqbase, strip)) {
2301 seq::transform_seqbase_shuffle(active_seqbase, strip, scene);
2302 }
2303 }
2304 }
2305
2308
2309 return OPERATOR_FINISHED;
2310}
2311
2313{
2314 /* Identifiers. */
2315 ot->name = "UnMeta Strip";
2316 ot->idname = "SEQUENCER_OT_meta_separate";
2317 ot->description = "Put the contents of a meta-strip back in the sequencer";
2318
2319 /* API callbacks. */
2321 ot->poll = sequencer_edit_poll;
2322
2323 /* Flags. */
2324 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2325}
2326
2328
2329/* -------------------------------------------------------------------- */
2332
2333static bool strip_jump_internal(Scene *scene,
2334 const short side,
2335 const bool do_skip_mute,
2336 const bool do_center)
2337{
2338 bool changed = false;
2339 int timeline_frame = scene->r.cfra;
2340 int next_frame = seq::time_find_next_prev_edit(
2341 scene, timeline_frame, side, do_skip_mute, do_center, false);
2342
2343 if (next_frame != timeline_frame) {
2344 scene->r.cfra = next_frame;
2345 changed = true;
2346 }
2347
2348 return changed;
2349}
2350
2352{
2353 /* Prevent changes during render. */
2354 if (G.is_rendering) {
2355 return false;
2356 }
2357
2358 return sequencer_edit_poll(C);
2359}
2360
2362{
2363 Scene *scene = CTX_data_scene(C);
2364 const bool next = RNA_boolean_get(op->ptr, "next");
2365 const bool center = RNA_boolean_get(op->ptr, "center");
2366
2367 /* Currently do_skip_mute is always true. */
2368 if (!strip_jump_internal(scene, next ? seq::SIDE_RIGHT : seq::SIDE_LEFT, true, center)) {
2369 return OPERATOR_CANCELLED;
2370 }
2371
2374
2375 return OPERATOR_FINISHED;
2376}
2377
2379{
2380 /* Identifiers. */
2381 ot->name = "Jump to Strip";
2382 ot->idname = "SEQUENCER_OT_strip_jump";
2383 ot->description = "Move frame to previous edit point";
2384
2385 /* API callbacks. */
2388
2389 /* Flags. */
2390 ot->flag = OPTYPE_UNDO;
2391
2392 /* Properties. */
2393 RNA_def_boolean(ot->srna, "next", true, "Next Strip", "");
2394 RNA_def_boolean(ot->srna, "center", true, "Use Strip Center", "");
2395}
2396
2398
2399/* -------------------------------------------------------------------- */
2402
2404 {seq::SIDE_LEFT, "LEFT", 0, "Left", ""},
2405 {seq::SIDE_RIGHT, "RIGHT", 0, "Right", ""},
2406 {0, nullptr, 0, nullptr, nullptr},
2407};
2408
2409static void swap_strips(Scene *scene, Strip *strip_a, Strip *strip_b)
2410{
2411 int gap = seq::time_left_handle_frame_get(scene, strip_b) -
2412 seq::time_right_handle_frame_get(scene, strip_a);
2413 int strip_a_start;
2414 int strip_b_start;
2415
2416 strip_b_start = (strip_b->start - seq::time_left_handle_frame_get(scene, strip_b)) +
2417 seq::time_left_handle_frame_get(scene, strip_a);
2418 seq::transform_translate_strip(scene, strip_b, strip_b_start - strip_b->start);
2419 seq::relations_invalidate_cache(scene, strip_b);
2420
2421 strip_a_start = (strip_a->start - seq::time_left_handle_frame_get(scene, strip_a)) +
2422 seq::time_right_handle_frame_get(scene, strip_b) + gap;
2423 seq::transform_translate_strip(scene, strip_a, strip_a_start - strip_a->start);
2424 seq::relations_invalidate_cache(scene, strip_a);
2425}
2426
2427static Strip *find_next_prev_strip(Scene *scene, Strip *test, int lr, int sel)
2428{
2429 /* sel: 0==unselected, 1==selected, -1==don't care. */
2430 Strip *strip, *best_strip = nullptr;
2431 Editing *ed = seq::editing_get(scene);
2432
2433 int dist, best_dist;
2434 best_dist = MAXFRAME * 2;
2435
2436 if (ed == nullptr) {
2437 return nullptr;
2438 }
2439
2440 strip = static_cast<Strip *>(ed->seqbasep->first);
2441 while (strip) {
2442 if ((strip != test) && (test->channel == strip->channel) &&
2443 ((sel == -1) || (sel == (strip->flag & SELECT))))
2444 {
2445 dist = MAXFRAME * 2;
2446
2447 switch (lr) {
2448 case seq::SIDE_LEFT:
2449 if (seq::time_right_handle_frame_get(scene, strip) <=
2451 {
2452 dist = seq::time_right_handle_frame_get(scene, test) -
2453 seq::time_left_handle_frame_get(scene, strip);
2454 }
2455 break;
2456 case seq::SIDE_RIGHT:
2457 if (seq::time_left_handle_frame_get(scene, strip) >=
2459 {
2460 dist = seq::time_left_handle_frame_get(scene, strip) -
2462 }
2463 break;
2464 }
2465
2466 if (dist == 0) {
2467 best_strip = strip;
2468 break;
2469 }
2470 if (dist < best_dist) {
2471 best_dist = dist;
2472 best_strip = strip;
2473 }
2474 }
2475 strip = static_cast<Strip *>(strip->next);
2476 }
2477 return best_strip; /* Can be nullptr. */
2478}
2479
2480static bool strip_is_parent(const Strip *par, const Strip *strip)
2481{
2482 return ((par->input1 == strip) || (par->input2 == strip));
2483}
2484
2486{
2487 Scene *scene = CTX_data_scene(C);
2488 Editing *ed = seq::editing_get(scene);
2489 Strip *active_strip = seq::select_active_get(scene);
2491 Strip *strip;
2492 int side = RNA_enum_get(op->ptr, "side");
2493
2494 if (active_strip == nullptr) {
2495 return OPERATOR_CANCELLED;
2496 }
2497
2498 strip = find_next_prev_strip(scene, active_strip, side, -1);
2499
2500 if (strip) {
2501
2502 /* Disallow effect strips. */
2503 if (seq::effect_get_num_inputs(strip->type) >= 1 &&
2504 (strip->effectdata || strip->input1 || strip->input2))
2505 {
2506 return OPERATOR_CANCELLED;
2507 }
2508 if ((seq::effect_get_num_inputs(active_strip->type) >= 1) &&
2509 (active_strip->effectdata || active_strip->input1 || active_strip->input2))
2510 {
2511 return OPERATOR_CANCELLED;
2512 }
2513
2515 if (seq::transform_is_locked(channels, strip) ||
2516 seq::transform_is_locked(channels, active_strip))
2517 {
2518 return OPERATOR_CANCELLED;
2519 }
2520
2521 switch (side) {
2522 case seq::SIDE_LEFT:
2523 swap_strips(scene, strip, active_strip);
2524 break;
2525 case seq::SIDE_RIGHT:
2526 swap_strips(scene, active_strip, strip);
2527 break;
2528 }
2529
2530 /* Do this in a new loop since both effects need to be calculated first. */
2531 LISTBASE_FOREACH (Strip *, istrip, seqbase) {
2532 if ((istrip->type & STRIP_TYPE_EFFECT) &&
2533 (strip_is_parent(istrip, active_strip) || strip_is_parent(istrip, strip)))
2534 {
2535 /* This may now overlap. */
2536 if (seq::transform_test_overlap(scene, seqbase, istrip)) {
2537 seq::transform_seqbase_shuffle(seqbase, istrip, scene);
2538 }
2539 }
2540 }
2541
2543 return OPERATOR_FINISHED;
2544 }
2545
2546 return OPERATOR_CANCELLED;
2547}
2548
2550{
2551 /* Identifiers. */
2552 ot->name = "Swap Strip";
2553 ot->idname = "SEQUENCER_OT_swap";
2554 ot->description = "Swap active strip with strip to the right or left";
2555
2556 /* API callbacks. */
2557 ot->exec = sequencer_swap_exec;
2558 ot->poll = sequencer_edit_poll;
2559
2560 /* Flags. */
2561 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2562
2563 /* Properties. */
2565 ot->srna, "side", prop_side_lr_types, seq::SIDE_RIGHT, "Side", "Side of the strip to swap");
2566}
2567
2569
2570/* -------------------------------------------------------------------- */
2573
2575{
2576 Scene *scene = CTX_data_scene(C);
2577 Strip *active_strip = seq::select_active_get(scene);
2578 StripElem *se = nullptr;
2579
2580 if (active_strip == nullptr || active_strip->data == nullptr) {
2581 return OPERATOR_CANCELLED;
2582 }
2583
2584 switch (active_strip->type) {
2585 case STRIP_TYPE_IMAGE:
2586 se = seq::render_give_stripelem(scene, active_strip, scene->r.cfra);
2587 break;
2588 case STRIP_TYPE_MOVIE:
2589 se = active_strip->data->stripdata;
2590 break;
2591 default:
2592 return OPERATOR_CANCELLED;
2593 }
2594
2595 if (se == nullptr) {
2596 return OPERATOR_CANCELLED;
2597 }
2598
2599 /* Prevent setting the render size if values aren't initialized. */
2600 if (se->orig_width <= 0 || se->orig_height <= 0) {
2601 return OPERATOR_CANCELLED;
2602 }
2603
2604 scene->r.xsch = se->orig_width;
2605 scene->r.ysch = se->orig_height;
2606
2607 active_strip->data->transform->scale_x = active_strip->data->transform->scale_y = 1.0f;
2608 active_strip->data->transform->xofs = active_strip->data->transform->yofs = 0.0f;
2609
2610 seq::relations_invalidate_cache(scene, active_strip);
2613
2614 return OPERATOR_FINISHED;
2615}
2616
2618{
2619 /* Identifiers. */
2620 ot->name = "Set Render Size";
2621 ot->idname = "SEQUENCER_OT_rendersize";
2622 ot->description = "Set render size and aspect from active strip";
2623
2624 /* API callbacks. */
2626 ot->poll = sequencer_edit_poll;
2627
2628 /* Flags. */
2629 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2630}
2631
2633
2634/* -------------------------------------------------------------------- */
2637
2639{
2640 /* Identifiers. */
2641 ot->name = "Copy";
2642 ot->idname = "SEQUENCER_OT_copy";
2643 ot->description = "Copy the selected strips to the internal clipboard";
2644
2645 /* API callbacks. */
2647 ot->poll = sequencer_edit_poll;
2648
2649 /* Flags. */
2650 ot->flag = OPTYPE_REGISTER;
2651}
2652
2654
2655/* -------------------------------------------------------------------- */
2658
2660{
2661 /* Identifiers. */
2662 ot->name = "Paste";
2663 ot->idname = "SEQUENCER_OT_paste";
2664 ot->description = "Paste strips from the internal clipboard";
2665
2666 /* API callbacks. */
2669
2670 /* Flags. */
2671 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2672
2673 /* Properties. */
2675 ot->srna,
2676 "keep_offset",
2677 false,
2678 "Keep Offset",
2679 "Keep strip offset relative to the current frame when pasting");
2681}
2682
2684
2685/* -------------------------------------------------------------------- */
2688
2690{
2691 Scene *scene = CTX_data_scene(C);
2692 Strip *strip_act;
2693 Strip *strip_other;
2694 const char *error_msg;
2695
2696 if (seq::select_active_get_pair(scene, &strip_act, &strip_other) == false) {
2697 BKE_report(op->reports, RPT_ERROR, "Please select two strips");
2698 return OPERATOR_CANCELLED;
2699 }
2700
2701 if (seq::edit_strip_swap(scene, strip_act, strip_other, &error_msg) == false) {
2702 BKE_report(op->reports, RPT_ERROR, error_msg);
2703 return OPERATOR_CANCELLED;
2704 }
2705
2706 if (strip_act->scene_sound) {
2707 BKE_sound_remove_scene_sound(scene, strip_act->scene_sound);
2708 }
2709
2710 if (strip_other->scene_sound) {
2711 BKE_sound_remove_scene_sound(scene, strip_other->scene_sound);
2712 }
2713
2714 strip_act->scene_sound = nullptr;
2715 strip_other->scene_sound = nullptr;
2716
2717 if (strip_act->sound) {
2718 BKE_sound_add_scene_sound_defaults(scene, strip_act);
2719 }
2720 if (strip_other->sound) {
2721 BKE_sound_add_scene_sound_defaults(scene, strip_other);
2722 }
2723
2724 seq::relations_invalidate_cache_raw(scene, strip_act);
2725 seq::relations_invalidate_cache_raw(scene, strip_other);
2726
2728
2729 return OPERATOR_FINISHED;
2730}
2731
2733{
2734 /* Identifiers. */
2735 ot->name = "Sequencer Swap Data";
2736 ot->idname = "SEQUENCER_OT_swap_data";
2737 ot->description = "Swap 2 sequencer strips";
2738
2739 /* API callbacks. */
2742
2743 /* Flags. */
2744 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2745}
2746
2748
2749/* -------------------------------------------------------------------- */
2752
2754 {STRIP_TYPE_CROSS, "CROSS", 0, "Crossfade", "Fade out of one video, fading into another"},
2755 {STRIP_TYPE_ADD, "ADD", 0, "Add", "Add together color channels from two videos"},
2756 {STRIP_TYPE_SUB, "SUBTRACT", 0, "Subtract", "Subtract one strip's color from another"},
2757 {STRIP_TYPE_ALPHAOVER, "ALPHA_OVER", 0, "Alpha Over", "Blend alpha on top of another video"},
2758 {STRIP_TYPE_ALPHAUNDER, "ALPHA_UNDER", 0, "Alpha Under", "Blend alpha below another video"},
2759 {STRIP_TYPE_GAMCROSS, "GAMMA_CROSS", 0, "Gamma Crossfade", "Crossfade with color correction"},
2760 {STRIP_TYPE_MUL, "MULTIPLY", 0, "Multiply", "Multiply color channels from two videos"},
2761 {STRIP_TYPE_WIPE, "WIPE", 0, "Wipe", "Sweep a transition line across the frame"},
2762 {STRIP_TYPE_GLOW, "GLOW", 0, "Glow", "Add blur and brightness to light areas"},
2763 {STRIP_TYPE_TRANSFORM, "TRANSFORM", 0, "Transform", "Apply scale, rotation, or translation"},
2764 {STRIP_TYPE_COLOR, "COLOR", 0, "Color", "Add a simple color strip"},
2765 {STRIP_TYPE_SPEED, "SPEED", 0, "Speed", "Timewarp video strips, modifying playback speed"},
2766 {STRIP_TYPE_MULTICAM, "MULTICAM", 0, "Multicam Selector", "Control active camera angles"},
2767 {STRIP_TYPE_ADJUSTMENT, "ADJUSTMENT", 0, "Adjustment Layer", "Apply nondestructive effects"},
2768 {STRIP_TYPE_GAUSSIAN_BLUR, "GAUSSIAN_BLUR", 0, "Gaussian Blur", "Soften details along axes"},
2769 {STRIP_TYPE_TEXT, "TEXT", 0, "Text", "Add a simple text strip"},
2770 {STRIP_TYPE_COLORMIX, "COLORMIX", 0, "Color Mix", "Combine two strips using blend modes"},
2771 {0, nullptr, 0, nullptr, nullptr},
2772};
2773
2775{
2776 Scene *scene = CTX_data_scene(C);
2777 Strip *strip = seq::select_active_get(scene);
2778 const int old_type = strip->type;
2779 const int new_type = RNA_enum_get(op->ptr, "type");
2780
2781 /* Free previous effect and init new effect. */
2783
2784 if ((strip->type & STRIP_TYPE_EFFECT) == 0) {
2785 return OPERATOR_CANCELLED;
2786 }
2787
2789 BKE_report(op->reports, RPT_ERROR, "New effect takes less or more inputs");
2790 return OPERATOR_CANCELLED;
2791 }
2792
2793 sh = seq::strip_effect_handle_get(strip);
2794 sh.free(strip, true);
2795
2796 strip->type = new_type;
2797
2798 /* If the strip's name is the default (equal to the old effect type),
2799 * rename to the new type to avoid confusion. */
2800 char name_base[MAX_ID_NAME];
2801 int name_num;
2802 BLI_string_split_name_number(strip->name + 2, '.', name_base, &name_num);
2803 if (STREQ(name_base, seq::get_default_stripname_by_type(old_type))) {
2804 seq::edit_strip_name_set(scene, strip, seq::strip_give_name(strip));
2805 seq::ensure_unique_name(strip, scene);
2806 }
2807
2808 sh = seq::strip_effect_handle_get(strip);
2809 sh.init(strip);
2810
2811 seq::relations_invalidate_cache(scene, strip);
2813
2814 return OPERATOR_FINISHED;
2815}
2816
2818{
2819 /* Identifiers. */
2820 ot->name = "Change Effect Type";
2821 ot->idname = "SEQUENCER_OT_change_effect_type";
2822 ot->description = "Replace effect strip with another that takes the same number of inputs";
2823
2824 /* API callbacks. */
2826 ot->poll = sequencer_effect_poll;
2827
2828 /* Flags. */
2829 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2830
2831 ot->prop = RNA_def_enum(ot->srna,
2832 "type",
2835 "Type",
2836 "Strip effect type");
2838}
2839
2841
2842/* -------------------------------------------------------------------- */
2845
2847{
2848 Main *bmain = CTX_data_main(C);
2849 Scene *scene = CTX_data_scene(C);
2850 Strip *strip = seq::select_active_get(scene);
2851 const bool is_relative_path = RNA_boolean_get(op->ptr, "relative_path");
2852 const bool use_placeholders = RNA_boolean_get(op->ptr, "use_placeholders");
2853 int minext_frameme, numdigits;
2854
2855 if (strip->type == STRIP_TYPE_IMAGE) {
2856 char directory[FILE_MAX];
2857 int len;
2858 StripElem *se;
2859
2860 /* Need to find min/max frame for placeholders. */
2861 if (use_placeholders) {
2862 len = sequencer_image_seq_get_minmax_frame(op, strip->sfra, &minext_frameme, &numdigits);
2863 }
2864 else {
2866 }
2867 if (len == 0) {
2868 return OPERATOR_CANCELLED;
2869 }
2870
2871 RNA_string_get(op->ptr, "directory", directory);
2872 if (is_relative_path) {
2873 /* TODO(@ideasman42): shouldn't this already be relative from the filesel?
2874 * (as the 'filepath' is) for now just make relative here,
2875 * but look into changing after 2.60. */
2876 BLI_path_rel(directory, BKE_main_blendfile_path(bmain));
2877 }
2878 STRNCPY(strip->data->dirpath, directory);
2879
2880 if (strip->data->stripdata) {
2881 MEM_freeN(strip->data->stripdata);
2882 }
2883 strip->data->stripdata = se = MEM_calloc_arrayN<StripElem>(len, "stripelem");
2884
2885 if (use_placeholders) {
2886 sequencer_image_seq_reserve_frames(op, se, len, minext_frameme, numdigits);
2887 }
2888 else {
2889 RNA_BEGIN (op->ptr, itemptr, "files") {
2890 char *filename = RNA_string_get_alloc(&itemptr, "name", nullptr, 0, nullptr);
2891 STRNCPY(se->filename, filename);
2892 MEM_freeN(filename);
2893 se++;
2894 }
2895 RNA_END;
2896 }
2897
2898 if (len == 1) {
2900 }
2901 else {
2903 }
2904
2905 /* Reset these else we won't see all the images. */
2906 strip->anim_startofs = strip->anim_endofs = 0;
2907
2908 /* Correct start/end frames so we don't move.
2909 * Important not to set strip->len = len; allow the function to handle it. */
2910 seq::add_reload_new_file(bmain, scene, strip, true);
2911 }
2912 else if (strip->type == STRIP_TYPE_SOUND_RAM) {
2913 bSound *sound = strip->sound;
2914 if (sound == nullptr) {
2915 return OPERATOR_CANCELLED;
2916 }
2917 char filepath[FILE_MAX];
2918 RNA_string_get(op->ptr, "filepath", filepath);
2919 STRNCPY(sound->filepath, filepath);
2920 BKE_sound_load(bmain, sound);
2921 }
2922 else {
2923 /* Lame, set rna filepath. */
2924 PropertyRNA *prop;
2925 char filepath[FILE_MAX];
2926
2927 PointerRNA strip_ptr = RNA_pointer_create_discrete(&scene->id, &RNA_Strip, strip);
2928
2929 RNA_string_get(op->ptr, "filepath", filepath);
2930 prop = RNA_struct_find_property(&strip_ptr, "filepath");
2931 RNA_property_string_set(&strip_ptr, prop, filepath);
2932 RNA_property_update(C, &strip_ptr, prop);
2934 }
2935
2938
2939 return OPERATOR_FINISHED;
2940}
2941
2943 wmOperator *op,
2944 const wmEvent * /*event*/)
2945{
2946 Scene *scene = CTX_data_scene(C);
2947 Strip *strip = seq::select_active_get(scene);
2948 char filepath[FILE_MAX];
2949
2951 filepath, sizeof(filepath), strip->data->dirpath, strip->data->stripdata->filename);
2952
2953 RNA_string_set(op->ptr, "directory", strip->data->dirpath);
2954 RNA_string_set(op->ptr, "filepath", filepath);
2955
2956 /* Set default display depending on strip type. */
2957 if (strip->type == STRIP_TYPE_IMAGE) {
2958 RNA_boolean_set(op->ptr, "filter_movie", false);
2959 }
2960 else {
2961 RNA_boolean_set(op->ptr, "filter_image", false);
2962 }
2963
2965
2967}
2968
2970{
2971 /* Identifiers. */
2972 ot->name = "Change Data/Files";
2973 ot->idname = "SEQUENCER_OT_change_path";
2974
2975 /* API callbacks. */
2979
2980 /* Flags. */
2981 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2982
2991 RNA_def_boolean(ot->srna,
2992 "use_placeholders",
2993 false,
2994 "Use Placeholders",
2995 "Use placeholders for missing frames of the strip");
2996}
2997
2999
3000/* -------------------------------------------------------------------- */
3003
3005{
3007 if (ed == nullptr) {
3008 return false;
3009 }
3010 Strip *strip = ed->act_strip;
3011 return ((strip != nullptr) && (strip->type == STRIP_TYPE_SCENE));
3012}
3014{
3015 Main *bmain = CTX_data_main(C);
3016 Scene *scene = CTX_data_scene(C);
3017 Scene *scene_seq = static_cast<Scene *>(
3018 BLI_findlink(&bmain->scenes, RNA_enum_get(op->ptr, "scene")));
3019
3020 if (scene_seq == nullptr) {
3021 BKE_report(op->reports, RPT_ERROR, "Scene not found");
3022 return OPERATOR_CANCELLED;
3023 }
3024
3025 /* Assign new scene. */
3026 Strip *strip = seq::select_active_get(scene);
3027 if (strip) {
3028 strip->scene = scene_seq;
3029 /* Do a refresh of the sequencer data. */
3033 }
3034
3037
3038 return OPERATOR_FINISHED;
3039}
3040
3042 wmOperator *op,
3043 const wmEvent *event)
3044{
3045 if (!RNA_struct_property_is_set(op->ptr, "scene")) {
3046 return WM_enum_search_invoke(C, op, event);
3047 }
3048
3049 return sequencer_change_scene_exec(C, op);
3050}
3051
3053{
3054 PropertyRNA *prop;
3055
3056 /* Identifiers. */
3057 ot->name = "Change Scene";
3058 ot->idname = "SEQUENCER_OT_change_scene";
3059 ot->description = "Change Scene assigned to Strip";
3060
3061 /* API callbacks. */
3065
3066 /* Flags. */
3067 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
3068
3069 /* Properties. */
3070 prop = RNA_def_enum(ot->srna, "scene", rna_enum_dummy_NULL_items, 0, "Scene", "");
3073 ot->prop = prop;
3074}
3075
3077
3078/* -------------------------------------------------------------------- */
3081
3083static int strip_cmp_time_startdisp_channel(void *thunk, const void *a, const void *b)
3084{
3085 const Scene *scene = static_cast<Scene *>(thunk);
3086 const Strip *strip_a = static_cast<const Strip *>(a);
3087 const Strip *strip_b = static_cast<const Strip *>(b);
3088
3089 int strip_a_start = seq::time_left_handle_frame_get(scene, strip_a);
3090 int strip_b_start = seq::time_left_handle_frame_get(scene, strip_b);
3091
3092 /* If strips have the same start frame favor the one with a higher channel. */
3093 if (strip_a_start == strip_b_start) {
3094 return strip_a->channel > strip_b->channel;
3095 }
3096
3097 return (strip_a_start > strip_b_start);
3098}
3099
3101 wmOperator *op,
3102 const wmEvent * /*event*/)
3103{
3105
3107
3109}
3110
3115
3116static bool strip_get_text_strip_cb(Strip *strip, void *user_data)
3117{
3118 Seq_get_text_cb_data *cd = (Seq_get_text_cb_data *)user_data;
3121 /* Only text strips that are not muted and don't end with negative frame. */
3122 if ((strip->type == STRIP_TYPE_TEXT) && !seq::render_is_muted(channels, strip) &&
3123 (seq::time_right_handle_frame_get(cd->scene, strip) > cd->scene->r.sfra))
3124 {
3125 BLI_addtail(cd->text_seq, MEM_dupallocN(strip));
3126 }
3127 return true;
3128}
3129
3131{
3132 Scene *scene = CTX_data_scene(C);
3133 Strip *strip, *strip_next;
3134 Editing *ed = seq::editing_get(scene);
3135 ListBase text_seq = {nullptr};
3136 int iter = 1; /* Sequence numbers in `.srt` files are 1-indexed. */
3137 FILE *file;
3138 char filepath[FILE_MAX];
3139
3140 if (!RNA_struct_property_is_set_ex(op->ptr, "filepath", false)) {
3141 BKE_report(op->reports, RPT_ERROR, "No filepath given");
3142 return OPERATOR_CANCELLED;
3143 }
3144
3145 RNA_string_get(op->ptr, "filepath", filepath);
3146 BLI_path_extension_ensure(filepath, sizeof(filepath), ".srt");
3147
3148 /* Avoid File write exceptions. */
3149 if (!BLI_exists(filepath)) {
3151 if (!BLI_file_touch(filepath)) {
3152 BKE_report(op->reports, RPT_ERROR, "Can't create subtitle file");
3153 return OPERATOR_CANCELLED;
3154 }
3155 }
3156 else if (!BLI_file_is_writable(filepath)) {
3157 BKE_report(op->reports, RPT_ERROR, "Can't overwrite export file");
3158 return OPERATOR_CANCELLED;
3159 }
3160
3161 if (ed != nullptr) {
3162 Seq_get_text_cb_data cb_data = {&text_seq, scene};
3164 }
3165
3166 if (BLI_listbase_is_empty(&text_seq)) {
3167 BKE_report(op->reports, RPT_ERROR, "No subtitles (text strips) to export");
3168 return OPERATOR_CANCELLED;
3169 }
3170
3172
3173 /* Open and write file. */
3174 file = BLI_fopen(filepath, "w");
3175
3176 for (strip = static_cast<Strip *>(text_seq.first); strip; strip = strip_next) {
3177 TextVars *data = static_cast<TextVars *>(strip->effectdata);
3178 char timecode_str_start[32];
3179 char timecode_str_end[32];
3180
3181 /* Write time-code relative to start frame of scene. Don't allow negative time-codes. */
3183 timecode_str_start,
3184 sizeof(timecode_str_start),
3185 -2,
3186 FRA2TIME(max_ii(seq::time_left_handle_frame_get(scene, strip) - scene->r.sfra, 0)),
3187 FPS,
3190 timecode_str_end,
3191 sizeof(timecode_str_end),
3192 -2,
3193 FRA2TIME(seq::time_right_handle_frame_get(scene, strip) - scene->r.sfra),
3194 FPS,
3196
3197 fprintf(
3198 file, "%d\n%s --> %s\n%s\n\n", iter++, timecode_str_start, timecode_str_end, data->text);
3199
3200 strip_next = static_cast<Strip *>(strip->next);
3201 MEM_freeN(strip);
3202 }
3203
3204 fclose(file);
3205
3206 return OPERATOR_FINISHED;
3207}
3208
3210{
3211 Editing *ed;
3212 Strip *strip;
3213 return (((ed = seq::editing_get(CTX_data_scene(C))) != nullptr) &&
3214 ((strip = ed->act_strip) != nullptr) && (strip->type == STRIP_TYPE_TEXT));
3215}
3216
3218{
3219 /* Identifiers. */
3220 ot->name = "Export Subtitles";
3221 ot->idname = "SEQUENCER_OT_export_subtitles";
3222 ot->description = "Export .srt file containing text strips";
3223
3224 /* API callbacks. */
3228
3229 /* Flags. */
3230 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
3231
3235 FILE_SAVE,
3239}
3240
3242
3243/* -------------------------------------------------------------------- */
3246
3248{
3249 Scene *scene = CTX_data_scene(C);
3250 Editing *ed = seq::editing_get(scene);
3251
3252 int sfra = MAXFRAME;
3253 int efra = -MAXFRAME;
3254 bool selected = false;
3255 const bool preview = RNA_boolean_get(op->ptr, "preview");
3256
3257 LISTBASE_FOREACH (Strip *, strip, ed->seqbasep) {
3258 if (strip->flag & SELECT) {
3259 selected = true;
3260 sfra = min_ii(sfra, seq::time_left_handle_frame_get(scene, strip));
3261 /* Offset of -1 is needed because in the sequencer every frame has width.
3262 * Range from 1 to 1 is drawn as range 1 to 2, because 1 frame long strip starts at frame 1
3263 * and ends at frame 2. See #106480. */
3264 efra = max_ii(efra, seq::time_right_handle_frame_get(scene, strip) - 1);
3265 }
3266 }
3267
3268 if (!selected) {
3269 BKE_report(op->reports, RPT_WARNING, "Select one or more strips");
3270 return OPERATOR_CANCELLED;
3271 }
3272 if (efra < 0) {
3273 BKE_report(op->reports, RPT_ERROR, "Can't set a negative range");
3274 return OPERATOR_CANCELLED;
3275 }
3276
3277 if (preview) {
3278 scene->r.flag |= SCER_PRV_RANGE;
3279 scene->r.psfra = max_ii(0, sfra);
3280 scene->r.pefra = efra;
3281 }
3282 else {
3283 scene->r.flag &= ~SCER_PRV_RANGE;
3284 scene->r.sfra = max_ii(0, sfra);
3285 scene->r.efra = efra;
3286 }
3287
3289
3290 return OPERATOR_FINISHED;
3291}
3292
3294{
3295 PropertyRNA *prop;
3296
3297 /* Identifiers. */
3298 ot->name = "Set Range to Strips";
3299 ot->idname = "SEQUENCER_OT_set_range_to_strips";
3300 ot->description = "Set the frame range to the selected strips start and end";
3301
3302 /* API callbacks. */
3304 ot->poll = sequencer_edit_poll;
3305
3306 /* Flags. */
3307 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
3308
3309 prop = RNA_def_boolean(ot->srna, "preview", false, "Preview", "Set the preview range instead");
3311}
3312
3314
3315/* -------------------------------------------------------------------- */
3318
3319enum {
3324};
3325
3327 {STRIP_TRANSFORM_POSITION, "POSITION", 0, "Position", "Reset strip transform location"},
3328 {STRIP_TRANSFORM_SCALE, "SCALE", 0, "Scale", "Reset strip transform scale"},
3329 {STRIP_TRANSFORM_ROTATION, "ROTATION", 0, "Rotation", "Reset strip transform rotation"},
3330 {STRIP_TRANSFORM_ALL, "ALL", 0, "All", "Reset strip transform location, scale and rotation"},
3331 {0, nullptr, 0, nullptr, nullptr},
3332};
3333
3335{
3336 Scene *scene = CTX_data_scene(C);
3337 const Editing *ed = seq::editing_get(scene);
3338 const int property = RNA_enum_get(op->ptr, "property");
3339
3340 const bool use_autokeyframe = blender::animrig::is_autokey_on(scene);
3341 const bool only_when_keyed = blender::animrig::is_keying_flag(scene,
3343
3344 LISTBASE_FOREACH (Strip *, strip, ed->seqbasep) {
3345 if (strip->flag & SELECT && strip->type != STRIP_TYPE_SOUND_RAM) {
3346 StripTransform *transform = strip->data->transform;
3347 PropertyRNA *prop;
3348 PointerRNA ptr = RNA_pointer_create_discrete(&scene->id, &RNA_StripTransform, transform);
3349 switch (property) {
3351 transform->xofs = 0;
3352 transform->yofs = 0;
3353 if (use_autokeyframe) {
3354 prop = RNA_struct_find_property(&ptr, "offset_x");
3356 C, scene, &ptr, prop, -1, scene->r.cfra, only_when_keyed);
3357 prop = RNA_struct_find_property(&ptr, "offset_y");
3359 C, scene, &ptr, prop, -1, scene->r.cfra, only_when_keyed);
3360 }
3361 break;
3363 transform->scale_x = 1.0f;
3364 transform->scale_y = 1.0f;
3365 if (use_autokeyframe) {
3366 prop = RNA_struct_find_property(&ptr, "scale_x");
3368 C, scene, &ptr, prop, -1, scene->r.cfra, only_when_keyed);
3369 prop = RNA_struct_find_property(&ptr, "scale_y");
3371 C, scene, &ptr, prop, -1, scene->r.cfra, only_when_keyed);
3372 }
3373 break;
3375 transform->rotation = 0.0f;
3376 if (use_autokeyframe) {
3377 prop = RNA_struct_find_property(&ptr, "rotation");
3379 C, scene, &ptr, prop, -1, scene->r.cfra, only_when_keyed);
3380 }
3381 break;
3383 transform->xofs = 0;
3384 transform->yofs = 0;
3385 transform->scale_x = 1.0f;
3386 transform->scale_y = 1.0f;
3387 transform->rotation = 0.0f;
3388 if (use_autokeyframe) {
3389 prop = RNA_struct_find_property(&ptr, "offset_x");
3391 C, scene, &ptr, prop, -1, scene->r.cfra, only_when_keyed);
3392 prop = RNA_struct_find_property(&ptr, "offset_y");
3394 C, scene, &ptr, prop, -1, scene->r.cfra, only_when_keyed);
3395 prop = RNA_struct_find_property(&ptr, "scale_x");
3397 C, scene, &ptr, prop, -1, scene->r.cfra, only_when_keyed);
3398 prop = RNA_struct_find_property(&ptr, "scale_y");
3400 C, scene, &ptr, prop, -1, scene->r.cfra, only_when_keyed);
3401 prop = RNA_struct_find_property(&ptr, "rotation");
3403 C, scene, &ptr, prop, -1, scene->r.cfra, only_when_keyed);
3404 }
3405 break;
3406 }
3407 seq::relations_invalidate_cache(scene, strip);
3408 }
3409 }
3410
3412 return OPERATOR_FINISHED;
3413}
3414
3416{
3417 /* Identifiers. */
3418 ot->name = "Clear Strip Transform";
3419 ot->idname = "SEQUENCER_OT_strip_transform_clear";
3420 ot->description = "Reset image transformation to default value";
3421
3422 /* API callbacks. */
3424 ot->poll = sequencer_edit_poll;
3425
3426 /* Flags. */
3427 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
3428
3429 ot->prop = RNA_def_enum(ot->srna,
3430 "property",
3433 "Property",
3434 "Strip transform property to be reset");
3435}
3436
3438
3439/* -------------------------------------------------------------------- */
3442
3444 {SEQ_SCALE_TO_FIT, "FIT", 0, "Scale to Fit", "Scale image so fits in preview"},
3445 {SEQ_SCALE_TO_FILL, "FILL", 0, "Scale to Fill", "Scale image so it fills preview completely"},
3446 {SEQ_STRETCH_TO_FILL, "STRETCH", 0, "Stretch to Fill", "Stretch image so it fills preview"},
3447 {0, nullptr, 0, nullptr, nullptr},
3448};
3449
3451{
3452 Scene *scene = CTX_data_scene(C);
3453 const Editing *ed = seq::editing_get(scene);
3454 const eSeqImageFitMethod fit_method = eSeqImageFitMethod(RNA_enum_get(op->ptr, "fit_method"));
3455
3456 LISTBASE_FOREACH (Strip *, strip, ed->seqbasep) {
3457 if (strip->flag & SELECT && strip->type != STRIP_TYPE_SOUND_RAM) {
3458 const int timeline_frame = scene->r.cfra;
3459 StripElem *strip_elem = seq::render_give_stripelem(scene, strip, timeline_frame);
3460
3461 if (strip_elem == nullptr) {
3462 continue;
3463 }
3464
3466 strip_elem->orig_width,
3467 strip_elem->orig_height,
3468 scene->r.xsch,
3469 scene->r.ysch,
3470 fit_method);
3471 seq::relations_invalidate_cache(scene, strip);
3472 }
3473 }
3474
3476 return OPERATOR_FINISHED;
3477}
3478
3480{
3481 /* Identifiers. */
3482 ot->name = "Strip Transform Set Fit";
3483 ot->idname = "SEQUENCER_OT_strip_transform_fit";
3484
3485 /* API callbacks. */
3487 ot->poll = sequencer_edit_poll;
3488
3489 /* Flags. */
3490 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
3491
3492 ot->prop = RNA_def_enum(ot->srna,
3493 "fit_method",
3496 "Fit Method",
3497 "Scale fit fit_method");
3498}
3499
3501{
3502 Scene *scene = CTX_data_scene(C);
3503 const Editing *ed = seq::editing_get(scene);
3504 const short color_tag = RNA_enum_get(op->ptr, "color");
3505
3506 LISTBASE_FOREACH (Strip *, strip, ed->seqbasep) {
3507 if (strip->flag & SELECT) {
3508 strip->color_tag = color_tag;
3509 }
3510 }
3511
3513 return OPERATOR_FINISHED;
3514}
3515
3517{
3518 Scene *scene = CTX_data_scene(C);
3519 if (scene == nullptr) {
3520 return false;
3521 }
3522
3523 Editing *ed = seq::editing_get(scene);
3524 if (ed == nullptr) {
3525 return false;
3526 }
3527
3528 Strip *act_strip = ed->act_strip;
3529 return act_strip != nullptr;
3530}
3531
3533{
3534 /* Identifiers. */
3535 ot->name = "Set Color Tag";
3536 ot->idname = "SEQUENCER_OT_strip_color_tag_set";
3537 ot->description = "Set a color tag for the selected strips";
3538
3539 /* API callbacks. */
3542
3543 /* Flags. */
3544 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
3545
3546 RNA_def_enum(ot->srna, "color", rna_enum_strip_color_items, STRIP_COLOR_NONE, "Color Tag", "");
3547}
3548
3550
3551/* -------------------------------------------------------------------- */
3554
3556{
3557 Scene *scene = CTX_data_scene(C);
3558 SpaceSeq *sseq = CTX_wm_space_seq(C);
3559
3560 float cursor_pixel[2];
3561 RNA_float_get_array(op->ptr, "location", cursor_pixel);
3562
3563 blender::float2 cursor_region = seq::image_preview_unit_from_px(scene, cursor_pixel);
3564 copy_v2_v2(sseq->cursor, cursor_region);
3565
3567
3568 /* Use pass-through to allow click-drag to transform the cursor. */
3570}
3571
3573 wmOperator *op,
3574 const wmEvent *event)
3575{
3576 ARegion *region = CTX_wm_region(C);
3577 float cursor_pixel[2];
3579 &region->v2d, event->mval[0], event->mval[1], &cursor_pixel[0], &cursor_pixel[1]);
3580
3581 RNA_float_set_array(op->ptr, "location", cursor_pixel);
3582
3583 return sequencer_set_2d_cursor_exec(C, op);
3584}
3585
3587{
3588 /* identifiers */
3589 ot->name = "Set 2D Cursor";
3590 ot->description = "Set 2D cursor location";
3591 ot->idname = "SEQUENCER_OT_cursor_set";
3592
3593 /* API callbacks. */
3597
3598 /* flags */
3599 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
3600
3601 /* properties */
3603 "location",
3604 2,
3605 nullptr,
3606 -FLT_MAX,
3607 FLT_MAX,
3608 "Location",
3609 "Cursor location in normalized preview coordinates",
3610 -10.0f,
3611 10.0f);
3612}
3613
3615
3616/* -------------------------------------------------------------------- */
3619
3621{
3622 Scene *scene = CTX_data_scene(C);
3623 Editing *ed = seq::editing_get(scene);
3624 Strip *strip = ed->act_strip;
3625
3626 const int old_start = seq::time_left_handle_frame_get(scene, strip);
3627 const int old_end = seq::time_right_handle_frame_get(scene, strip);
3628
3629 Scene *target_scene = strip->scene;
3630
3631 strip->len = target_scene->r.efra - target_scene->r.sfra + 1;
3632 seq::time_left_handle_frame_set(scene, strip, old_start);
3633 seq::time_right_handle_frame_set(scene, strip, old_end);
3634
3638 return OPERATOR_FINISHED;
3639}
3640
3642{
3644 return (ed != nullptr && ed->act_strip != nullptr && ed->act_strip->type == STRIP_TYPE_SCENE);
3645}
3646
3648{
3649 /* identifiers */
3650 ot->name = "Update Scene Frame Range";
3651 ot->description = "Update frame range of scene strip";
3652 ot->idname = "SEQUENCER_OT_scene_frame_range_update";
3653
3654 /* API callbacks. */
3657
3658 /* flags */
3659 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
3660}
3661
3663
3664} // namespace blender::ed::vse
Functions for backward compatibility with the legacy Action API.
ScrArea * CTX_wm_area(const bContext *C)
Scene * CTX_data_scene(const bContext *C)
SpaceSeq * CTX_wm_space_seq(const bContext *C)
Main * CTX_data_main(const bContext *C)
ARegion * CTX_wm_region(const bContext *C)
const char * BKE_main_blendfile_path(const Main *bmain) ATTR_NONNULL()
Definition main.cc:877
void BKE_report(ReportList *reports, eReportType type, const char *message)
Definition report.cc:126
void * BKE_sound_add_scene_sound_defaults(struct Scene *scene, struct Strip *sequence)
void BKE_sound_load(struct Main *bmain, struct bSound *sound)
void BKE_sound_remove_scene_sound(struct Scene *scene, void *handle)
#define BLI_assert(a)
Definition BLI_assert.h:46
File and directory operations.
bool BLI_file_touch(const char *filepath) ATTR_NONNULL(1)
Definition fileops_c.cc:316
int BLI_exists(const char *path) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition storage.cc:373
bool BLI_file_is_writable(const char *filepath) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition fileops_c.cc:291
FILE * BLI_fopen(const char *filepath, const char *mode) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
bool BLI_file_ensure_parent_dir_exists(const char *filepath) ATTR_NONNULL(1)
Definition fileops_c.cc:429
void * BLI_findlink(const ListBase *listbase, int number) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition listbase.cc:534
void void void BLI_movelisttolist(ListBase *dst, ListBase *src) ATTR_NONNULL(1
#define LISTBASE_FOREACH(type, var, list)
BLI_INLINE void BLI_listbase_clear(ListBase *lb)
BLI_INLINE bool BLI_listbase_is_empty(const ListBase *lb)
#define LISTBASE_FOREACH_BACKWARD(type, var, list)
void BLI_addtail(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:111
void BLI_remlink(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:131
void void void BLI_listbase_sort_r(ListBase *listbase, int(*cmp)(void *, const void *, const void *), void *thunk) ATTR_NONNULL(1
MINLINE int round_fl_to_int(float a)
MINLINE int min_ii(int a, int b)
MINLINE int max_ii(int a, int b)
MINLINE void copy_v2_v2(float r[2], const float a[2])
#define FILE_MAX
#define BLI_path_join(...)
bool BLI_path_extension_ensure(char *path, size_t path_maxncpy, const char *ext) ATTR_NONNULL(1
bool void BLI_path_rel(char path[FILE_MAX], const char *basepath) ATTR_NONNULL(1)
int bool BLI_str_startswith(const char *__restrict str, const char *__restrict start) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1
#define SNPRINTF(dst, format,...)
Definition BLI_string.h:599
char * STRNCPY(char(&dst)[N], const char *src)
Definition BLI_string.h:688
int bool bool BLI_str_endswith(const char *__restrict str, const char *__restrict end) ATTR_NONNULL(1
char * BLI_strncpy(char *__restrict dst, const char *__restrict src, size_t dst_maxncpy) ATTR_NONNULL(1
size_t BLI_timecode_string_from_time(char *str, size_t maxncpy, int brevity_level, float time_seconds, double fps, short timecode_style) ATTR_NONNULL()
Definition timecode.cc:22
#define ELEM(...)
#define STREQ(a, b)
#define BLT_I18NCONTEXT_ID_SEQUENCE
#define TIP_(msgid)
#define IFACE_(msgid)
#define DATA_(msgid)
void DEG_id_tag_update(ID *id, unsigned int flags)
void DEG_relations_tag_update(Main *bmain)
@ ID_RECALC_FRAME_CHANGE
Definition DNA_ID.h:1033
@ ID_RECALC_AUDIO
Definition DNA_ID.h:1040
@ ID_RECALC_SEQUENCER_STRIPS
Definition DNA_ID.h:1030
@ ID_RECALC_ANIMATION_NO_FLUSH
Definition DNA_ID.h:1084
eSeqImageFitMethod
@ SEQ_SCALE_TO_FILL
@ SEQ_STRETCH_TO_FILL
@ SEQ_SCALE_TO_FIT
#define MINFRAME
#define FPS
#define FRA2TIME(a)
@ SCER_PRV_RANGE
#define MAXFRAME
@ RGN_TYPE_CHANNELS
@ RGN_TYPE_WINDOW
@ RGN_TYPE_PREVIEW
@ STRIP_TYPE_GAUSSIAN_BLUR
@ STRIP_TYPE_GAMCROSS
@ STRIP_TYPE_SCENE
@ STRIP_TYPE_COLORMIX
@ STRIP_TYPE_WIPE
@ STRIP_TYPE_TEXT
@ STRIP_TYPE_SOUND_RAM
@ STRIP_TYPE_ADD
@ STRIP_TYPE_TRANSFORM
@ STRIP_TYPE_IMAGE
@ STRIP_TYPE_MOVIE
@ STRIP_TYPE_GLOW
@ STRIP_TYPE_SUB
@ STRIP_TYPE_MUL
@ STRIP_TYPE_SPEED
@ STRIP_TYPE_EFFECT
@ STRIP_TYPE_COLOR
@ STRIP_TYPE_ADJUSTMENT
@ STRIP_TYPE_META
@ STRIP_TYPE_MULTICAM
@ STRIP_TYPE_ALPHAUNDER
@ STRIP_TYPE_CROSS
@ STRIP_TYPE_ALPHAOVER
@ SEQ_SHOW_OFFSETS
@ SEQ_SINGLE_FRAME_CONTENT
@ SEQ_RIGHTSEL
@ SEQ_IGNORE_CHANNEL_LOCK
@ SEQ_OVERLAP
@ SEQ_LEFTSEL
@ STRIP_COLOR_NONE
#define STRIP_ALLSEL
#define STRIP_HAS_PATH(_strip)
@ FILE_SORT_DEFAULT
@ FILE_SPECIAL
@ FILE_BLENDER
@ FILE_TYPE_FOLDER
@ SEQ_VIEW_SEQUENCE_PREVIEW
@ SEQ_VIEW_SEQUENCE
@ SEQ_VIEW_PREVIEW
@ FILE_DEFAULTDISPLAY
@ SEQ_DRAW_IMG_IMBUF
@ AUTOKEY_FLAG_INSERTAVAILABLE
@ USER_TIMECODE_SUBRIP
@ OPERATOR_CANCELLED
@ OPERATOR_FINISHED
@ OPERATOR_RUNNING_MODAL
@ OPERATOR_PASS_THROUGH
void ED_fileselect_ensure_default_filepath(bContext *C, wmOperator *op, const char *extension)
Definition filesel.cc:1490
void initNumInput(NumInput *n)
Definition numinput.cc:69
#define NUM_STR_REP_LEN
bool handleNumInput(bContext *C, NumInput *n, const wmEvent *event)
Definition numinput.cc:311
bool applyNumInput(NumInput *n, float *vec)
Definition numinput.cc:189
void outputNumInput(NumInput *n, char *str, const UnitSettings &unit_settings)
Definition numinput.cc:87
bool hasNumInput(const NumInput *n)
Definition numinput.cc:170
bool ED_scene_delete(bContext *C, Main *bmain, Scene *scene) ATTR_NONNULL()
void ED_area_status_text(ScrArea *area, const char *str)
Definition area.cc:872
bool ED_operator_sequencer_active(bContext *C)
void ED_workspace_status_text(bContext *C, const char *str)
Definition area.cc:1040
Read Guarded memory(de)allocation.
#define RNA_BEGIN(sptr, itemptr, propname)
#define RNA_END
const EnumPropertyItem * RNA_scene_without_active_itemf(bContext *C, PointerRNA *ptr, PropertyRNA *prop, bool *r_free)
@ PROP_ENUM_NO_TRANSLATE
Definition RNA_types.hh:406
@ PROP_SKIP_SAVE
Definition RNA_types.hh:330
@ PROP_HIDDEN
Definition RNA_types.hh:324
#define C
Definition RandGen.cpp:29
#define STRIP_DUPE_UNIQUE_NAME
@ UI_ITEM_R_EXPAND
void uiLayoutSetPropSep(uiLayout *layout, bool is_sep)
#define UI_ITEM_NONE
void uiLayoutSetPropDecorate(uiLayout *layout, bool is_sep)
#define UI_MAX_DRAW_STR
View2D * UI_view2d_fromcontext(const bContext *C)
Definition view2d.cc:1854
void UI_view2d_region_to_view(const View2D *v2d, float x, float y, float *r_view_x, float *r_view_y) ATTR_NONNULL()
Definition view2d.cc:1667
float UI_view2d_scale_get_x(const View2D *v2d)
Definition view2d.cc:1920
@ WM_FILESEL_FILES
Definition WM_api.hh:1076
@ WM_FILESEL_DIRECTORY
Definition WM_api.hh:1073
@ WM_FILESEL_RELPATH
Definition WM_api.hh:1072
@ WM_FILESEL_FILEPATH
Definition WM_api.hh:1075
@ FILE_OPENFILE
Definition WM_api.hh:1084
@ FILE_SAVE
Definition WM_api.hh:1085
#define ND_SEQUENCER
Definition WM_types.hh:434
#define ND_SPACE_SEQUENCER
Definition WM_types.hh:532
#define ND_RENDER_OPTIONS
Definition WM_types.hh:432
#define NC_SCENE
Definition WM_types.hh:375
#define ND_FRAME_RANGE
Definition WM_types.hh:448
#define ND_FRAME
Definition WM_types.hh:431
#define NA_REMOVED
Definition WM_types.hh:584
#define ND_SCENEBROWSE
Definition WM_types.hh:429
#define ND_ANIMCHAN
Definition WM_types.hh:493
#define NC_SPACE
Definition WM_types.hh:389
@ KM_PRESS
Definition WM_types.hh:308
@ OPTYPE_BLOCKING
Definition WM_types.hh:184
@ OPTYPE_UNDO
Definition WM_types.hh:182
@ OPTYPE_REGISTER
Definition WM_types.hh:180
@ OPTYPE_GRAB_CURSOR_X
Definition WM_types.hh:190
blender::StringRef BLI_string_split_name_number(const blender::StringRef name_full, const char delim, int &r_number)
BMesh const char void * data
void item(std::string text, int icon1, int icon2=0)
Definition area.cc:979
void opmodal(std::string text, const wmOperatorType *ot, int propvalue, bool inverted=false)
Definition area.cc:1005
constexpr bool is_empty() const
constexpr const char * data() const
void add_multiple(Span< Key > keys)
int64_t remove_if(Predicate &&predicate)
#define SELECT
IMETHOD Vector diff(const Vector &a, const Vector &b, double dt)
Definition frames.inl:1166
VecBase< float, D > step(VecOp< float, D >, VecOp< float, D >) RET
#define MAX_ID_NAME
#define ID_IS_EDITABLE(_id)
#define MEM_reallocN(vmemh, len)
void * MEM_calloc_arrayN(size_t len, size_t size, const char *str)
Definition mallocn.cc:123
void * MEM_dupallocN(const void *vmemh)
Definition mallocn.cc:143
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
static ulong * next
#define G(x, y, z)
Vector< FCurve * > fcurves_for_assigned_action(AnimData *adt)
bool is_autokey_on(const Scene *scene)
bool autokeyframe_property(bContext *C, Scene *scene, PointerRNA *ptr, PropertyRNA *prop, int rnaindex, float cfra, bool only_if_property_keyed)
bool is_keying_flag(const Scene *scene, eKeying_Flag flag)
static wmOperatorStatus sequencer_split_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static wmOperatorStatus sequencer_unmute_exec(bContext *C, wmOperator *op)
static float slip_apply_clamp(const Scene *scene, const SlipData *data, float *r_offset)
void SEQUENCER_OT_meta_toggle(wmOperatorType *ot)
static void slip_update_header(const Scene *scene, ScrArea *area, SlipData *data, const float offset)
bool sequencer_edit_poll(bContext *C)
void SEQUENCER_OT_meta_make(wmOperatorType *ot)
static void sequencer_split_ui(bContext *, wmOperator *op)
static wmOperatorStatus sequencer_slip_modal(bContext *C, wmOperator *op, const wmEvent *event)
static wmOperatorStatus sequencer_export_subtitles_exec(bContext *C, wmOperator *op)
static int sequence_split_side_for_exec_get(wmOperator *op)
void SEQUENCER_OT_images_separate(wmOperatorType *ot)
void SEQUENCER_OT_snap(wmOperatorType *ot)
static bool sequencer_strip_change_scene_poll(bContext *C)
void SEQUENCER_OT_swap_inputs(wmOperatorType *ot)
static wmOperatorStatus sequencer_split_exec(bContext *C, wmOperator *op)
static wmOperatorStatus sequencer_add_duplicate_exec(bContext *C, wmOperator *)
static wmOperatorStatus sequencer_export_subtitles_invoke(bContext *C, wmOperator *op, const wmEvent *)
static wmOperatorStatus sequencer_delete_invoke(bContext *C, wmOperator *op, const wmEvent *event)
void SEQUENCER_OT_offset_clear(wmOperatorType *ot)
static void slip_strips_delta(wmOperator *op, Scene *scene, SlipData *data, const float delta)
static void sequencer_delete_strip_data(bContext *C, Strip *strip)
static void slip_cleanup(bContext *C, wmOperator *op, Scene *scene)
bool has_playback_animation(const Scene *scene)
bool check_show_strip(const SpaceSeq &sseq)
void SEQUENCER_OT_copy(wmOperatorType *ot)
static wmOperatorStatus sequencer_mute_exec(bContext *C, wmOperator *op)
static wmOperatorStatus sequencer_separate_images_invoke(bContext *C, wmOperator *op, const wmEvent *event)
void sequencer_image_seq_reserve_frames(wmOperator *op, StripElem *se, int len, int minframe, int numdigits)
wmOperatorStatus sequencer_clipboard_copy_exec(bContext *C, wmOperator *op)
static wmOperatorStatus sequencer_gap_insert_exec(bContext *C, wmOperator *op)
static wmOperatorStatus sequencer_snap_invoke(bContext *C, wmOperator *op, const wmEvent *)
void SEQUENCER_OT_gap_remove(wmOperatorType *ot)
void SEQUENCER_OT_gap_insert(wmOperatorType *ot)
static wmOperatorStatus sequencer_separate_images_exec(bContext *C, wmOperator *op)
void SEQUENCER_OT_change_scene(wmOperatorType *ot)
static wmOperatorStatus sequencer_swap_inputs_exec(bContext *C, wmOperator *op)
VectorSet< Strip * > strip_effect_get_new_inputs(const Scene *scene, StripType effect_type, int num_inputs, bool ignore_active)
void SEQUENCER_OT_refresh_all(wmOperatorType *ot)
static wmOperatorStatus sequencer_change_path_exec(bContext *C, wmOperator *op)
static const EnumPropertyItem scale_fit_methods[]
blender::VectorSet< Strip * > all_strips_from_context(bContext *C)
static const EnumPropertyItem transform_reset_properties[]
static wmOperatorStatus sequencer_reload_exec(bContext *C, wmOperator *op)
static bool sequencer_strip_is_text_poll(bContext *C)
blender::VectorSet< Strip * > selected_strips_from_context(bContext *C)
void SEQUENCER_OT_strip_transform_clear(wmOperatorType *ot)
static wmOperatorStatus sequencer_snap_exec(bContext *C, wmOperator *op)
void SEQUENCER_OT_lock(wmOperatorType *ot)
static wmOperatorStatus sequencer_delete_exec(bContext *C, wmOperator *op)
const EnumPropertyItem sequencer_prop_effect_types[]
static wmOperatorStatus sequencer_set_2d_cursor_exec(bContext *C, wmOperator *op)
static wmOperatorStatus sequencer_set_2d_cursor_invoke(bContext *C, wmOperator *op, const wmEvent *event)
void SEQUENCER_OT_mute(wmOperatorType *ot)
static wmOperatorStatus sequencer_change_scene_exec(bContext *C, wmOperator *op)
static const EnumPropertyItem prop_side_lr_types[]
static wmOperatorStatus sequencer_refresh_all_exec(bContext *C, wmOperator *)
bool maskedit_poll(bContext *C)
bool sequencer_editing_initialized_and_active(bContext *C)
static wmOperatorStatus sequencer_gap_remove_exec(bContext *C, wmOperator *op)
static bool sequencer_fcurves_targets_color_strip(const FCurve *fcurve)
static bool strip_get_text_strip_cb(Strip *strip, void *user_data)
static wmOperatorStatus sequencer_slip_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static wmOperatorStatus sequencer_change_path_invoke(bContext *C, wmOperator *op, const wmEvent *)
void SEQUENCER_OT_strip_color_tag_set(wmOperatorType *ot)
StringRef effect_inputs_validate(const VectorSet< Strip * > &inputs, int num_inputs)
void SEQUENCER_OT_slip(wmOperatorType *ot)
static wmOperatorStatus sequencer_disconnect_exec(bContext *C, wmOperator *)
void SEQUENCER_OT_unmute(wmOperatorType *ot)
static Strip * find_next_prev_strip(Scene *scene, Strip *test, int lr, int sel)
static wmOperatorStatus sequencer_change_effect_type_exec(bContext *C, wmOperator *op)
void SEQUENCER_OT_connect(wmOperatorType *ot)
static int strip_cmp_time_startdisp_channel(void *thunk, const void *a, const void *b)
static wmOperatorStatus sequencer_strip_transform_fit_exec(bContext *C, wmOperator *op)
void SEQUENCER_OT_change_effect_type(wmOperatorType *ot)
void SEQUENCER_OT_paste(wmOperatorType *ot)
bool sequencer_edit_with_channel_region_poll(bContext *C)
Strip * strip_under_mouse_get(const Scene *scene, const View2D *v2d, const int mval[2])
static wmOperatorStatus sequencer_meta_separate_exec(bContext *C, wmOperator *)
void SEQUENCER_OT_rendersize(wmOperatorType *ot)
void SEQUENCER_OT_scene_frame_range_update(wmOperatorType *ot)
void sequencer_select_do_updates(const bContext *C, Scene *scene)
void SEQUENCER_OT_reload(wmOperatorType *ot)
void SEQUENCER_OT_strip_transform_fit(wmOperatorType *ot)
bool sequencer_view_preview_only_poll(const bContext *C)
bool sequencer_strip_editable_poll(bContext *C)
void SEQUENCER_OT_change_path(wmOperatorType *ot)
static wmOperatorStatus sequencer_rendersize_exec(bContext *C, wmOperator *)
static wmOperatorStatus sequencer_strip_jump_exec(bContext *C, wmOperator *op)
static void swap_strips(Scene *scene, Strip *strip_a, Strip *strip_b)
bool maskedit_mask_poll(bContext *C)
static wmOperatorStatus sequencer_offset_clear_exec(bContext *C, wmOperator *)
static wmOperatorStatus sequencer_connect_exec(bContext *C, wmOperator *op)
static wmOperatorStatus sequencer_meta_make_exec(bContext *C, wmOperator *)
static int mouse_frame_side(View2D *v2d, short mouse_x, int frame)
static void slip_draw_status(bContext *C, const wmOperator *op)
void SEQUENCER_OT_set_range_to_strips(wmOperatorType *ot)
void SEQUENCER_OT_reassign_inputs(wmOperatorType *ot)
void SEQUENCER_OT_unlock(wmOperatorType *ot)
bool deselect_all_strips(const Scene *scene)
static bool sequencer_strip_jump_poll(bContext *C)
static wmOperatorStatus sequencer_set_range_to_strips_exec(bContext *C, wmOperator *op)
bool sequencer_view_has_preview_poll(bContext *C)
bool check_show_imbuf(const SpaceSeq &sseq)
static wmOperatorStatus sequencer_reassign_inputs_exec(bContext *C, wmOperator *op)
static wmOperatorStatus sequencer_meta_toggle_exec(bContext *C, wmOperator *)
void SEQUENCER_OT_swap(wmOperatorType *ot)
void SEQUENCER_OT_duplicate(wmOperatorType *ot)
static wmOperatorStatus sequencer_scene_frame_range_update_exec(bContext *C, wmOperator *)
static const EnumPropertyItem prop_split_types[]
wmOperatorStatus sequencer_clipboard_paste_exec(bContext *C, wmOperator *op)
const EnumPropertyItem prop_side_types[]
static bool sequencer_scene_frame_range_update_poll(bContext *C)
int sequencer_image_seq_get_minmax_frame(wmOperator *op, int sfra, int *r_minframe, int *r_numdigits)
void SEQUENCER_OT_delete(wmOperatorType *ot)
static wmOperatorStatus sequencer_strip_transform_clear_exec(bContext *C, wmOperator *op)
static SlipData * slip_data_init(const Scene *scene)
void SEQUENCER_OT_meta_separate(wmOperatorType *ot)
static bool sequencer_effect_poll(bContext *C)
bool sequencer_view_strips_poll(bContext *C)
static void slip_handle_num_input(const bContext *C, wmOperator *op, ScrArea *area, SlipData *data, Scene *scene)
static wmOperatorStatus sequencer_lock_exec(bContext *C, wmOperator *)
static bool strip_is_parent(const Strip *par, const Strip *strip)
void slip_modal_keymap(wmKeyConfig *keyconf)
static wmOperatorStatus sequencer_slip_exec(bContext *C, wmOperator *op)
void SEQUENCER_OT_export_subtitles(wmOperatorType *ot)
static bool sequencer_swap_inputs_poll(bContext *C)
static wmOperatorStatus sequencer_change_scene_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static wmOperatorStatus sequencer_unlock_exec(bContext *C, wmOperator *)
bool sequencer_strip_has_path_poll(bContext *C)
static wmOperatorStatus sequencer_swap_exec(bContext *C, wmOperator *op)
void SEQUENCER_OT_swap_data(wmOperatorType *ot)
void SEQUENCER_OT_disconnect(wmOperatorType *ot)
void SEQUENCER_OT_split(wmOperatorType *ot)
static bool strip_jump_internal(Scene *scene, const short side, const bool do_skip_mute, const bool do_center)
static bool sequencer_strip_color_tag_set_poll(bContext *C)
bool check_show_maskedit(SpaceSeq *sseq, Scene *scene)
static bool sequencer_refresh_all_poll(bContext *C)
static wmOperatorStatus sequencer_swap_data_exec(bContext *C, wmOperator *op)
void SEQUENCER_OT_cursor_set(wmOperatorType *ot)
void SEQUENCER_OT_strip_jump(wmOperatorType *ot)
static wmOperatorStatus sequencer_strip_color_tag_set_exec(bContext *C, wmOperator *op)
bool render_is_muted(const ListBase *channels, const Strip *strip)
Definition render.cc:2092
void relations_strip_free_anim(Strip *strip)
void prefetch_stop(Scene *scene)
Definition prefetch.cc:283
int time_right_handle_frame_get(const Scene *scene, const Strip *strip)
bool disconnect(Strip *strip)
void relations_invalidate_cache(Scene *scene, Strip *strip)
EffectHandle strip_effect_handle_get(Strip *strip)
Definition effects.cc:249
bool are_strips_connected_together(blender::VectorSet< Strip * > &strip_list)
SeqTimelineChannel * channel_get_by_index(const ListBase *channels, const int channel_index)
Definition channels.cc:65
ListBase * channels_displayed_get(const Editing *ed)
Definition channels.cc:28
void edit_remove_flagged_strips(Scene *scene, ListBase *seqbase)
float time_content_end_frame_get(const Scene *scene, const Strip *strip)
bool relations_render_loop_check(Strip *strip_main, Strip *strip)
void thumbnail_cache_invalidate_strip(Scene *scene, const Strip *strip)
void edit_flag_for_removal(Scene *scene, ListBase *seqbase, Strip *strip)
VectorSet< Strip * > query_selected_strips(ListBase *seqbase)
Definition iterator.cc:127
void transform_translate_strip(Scene *evil_scene, Strip *strip, int delta)
void strip_channel_set(Strip *strip, int channel)
void media_presence_free(Scene *scene)
bool transform_test_overlap(const Scene *scene, Strip *strip1, Strip *strip2)
Editing * editing_get(const Scene *scene)
Definition sequencer.cc:272
void meta_stack_set(const Scene *scene, Strip *dst)
Definition sequencer.cc:461
void edit_strip_name_set(Scene *scene, Strip *strip, const char *new_name)
int time_left_handle_frame_get(const Scene *, const Strip *strip)
void animation_duplicate_backup_to_scene(Scene *scene, Strip *strip, AnimationBackup *backup)
Definition animation.cc:205
void offset_animdata(const Scene *scene, Strip *strip, float ofs)
Definition animation.cc:42
const char * strip_give_name(const Strip *strip)
void relations_invalidate_cache_raw(Scene *scene, Strip *strip)
void connect(Strip *strip1, Strip *strip2)
float time_start_frame_get(const Strip *strip)
bool transform_single_image_check(const Strip *strip)
Strip * strip_duplicate_recursive(const Scene *scene_src, Scene *scene_dst, ListBase *new_seq_list, Strip *strip, int dupe_flag)
Definition sequencer.cc:645
void add_reload_new_file(Main *bmain, Scene *scene, Strip *strip, const bool lock_range)
Definition strip_add.cc:529
Strip * select_active_get(const Scene *scene)
void for_each_callback(ListBase *seqbase, ForEachFunc callback, void *user_data)
Definition iterator.cc:59
void iterator_set_expand(const Scene *scene, ListBase *seqbase, VectorSet< Strip * > &strips, void strip_query_func(const Scene *scene, Strip *strip_reference, ListBase *seqbase, VectorSet< Strip * > &strips))
Definition iterator.cc:82
bool select_active_get_pair(Scene *scene, Strip **r_strip_act, Strip **r_strip_other)
bool time_strip_intersects_frame(const Scene *scene, const Strip *strip, const int timeline_frame)
void time_slip_strip(const Scene *scene, Strip *strip, int frame_delta, float subframe_delta, bool slip_keyframes)
void transform_offset_after_frame(Scene *scene, ListBase *seqbase, const int delta, const int timeline_frame)
const char * get_default_stripname_by_type(int type)
void animation_backup_original(Scene *scene, AnimationBackup *backup)
Definition animation.cc:91
void select_active_set(Scene *scene, Strip *strip)
void cache_cleanup(Scene *scene)
bool edit_strip_swap(Scene *scene, Strip *strip_a, Strip *strip_b, const char **r_error_str)
Definition strip_edit.cc:44
bool transform_is_locked(ListBase *channels, const Strip *strip)
void strip_unique_name_set(Scene *scene, ListBase *seqbasep, Strip *strip)
void animation_restore_original(Scene *scene, AnimationBackup *backup)
Definition animation.cc:113
void time_left_handle_frame_set(const Scene *scene, Strip *strip, int timeline_frame)
void query_strip_connected_and_effect_chain(const Scene *scene, Strip *reference_strip, ListBase *seqbase, VectorSet< Strip * > &r_strips)
Definition iterator.cc:260
ListBase * active_seqbase_get(const Editing *ed)
Definition sequencer.cc:420
void time_right_handle_frame_set(const Scene *scene, Strip *strip, int timeline_frame)
Strip * meta_stack_pop(Editing *ed)
Definition sequencer.cc:485
bool transform_strip_can_be_translated(const Strip *strip)
void relations_free_imbuf(Scene *scene, ListBase *seqbase, bool for_render)
Strip * strip_alloc(ListBase *lb, int timeline_frame, int channel, int type)
Definition sequencer.cc:130
Strip * edit_strip_split(Main *bmain, Scene *scene, ListBase *seqbase, Strip *strip, const int timeline_frame, const eSplitMethod method, const bool ignore_connections, const char **r_error)
void strip_lookup_invalidate(const Editing *ed)
void ensure_unique_name(Strip *strip, Scene *scene)
Mask * active_mask_get(Scene *scene)
void seqbase_duplicate_recursive(const Scene *scene_src, Scene *scene_dst, ListBase *nseqbase, const ListBase *seqbase, int dupe_flag, const int flag)
Definition sequencer.cc:692
bool edit_remove_gaps(Scene *scene, ListBase *seqbase, const int initial_frame, const bool remove_all_gaps)
bool transform_seqbase_shuffle(ListBase *seqbasep, Strip *test, Scene *evil_scene)
int time_find_next_prev_edit(Scene *scene, int timeline_frame, const short side, const bool do_skip_mute, const bool do_center, const bool do_unselected)
void set_scale_to_fit(const Strip *strip, const int image_width, const int image_height, const int preview_width, const int preview_height, const eSeqImageFitMethod fit_method)
StripElem * render_give_stripelem(const Scene *scene, const Strip *strip, int timeline_frame)
Definition render.cc:233
float2 image_preview_unit_from_px(const Scene *scene, const float2 co_src)
int effect_get_num_inputs(int strip_type)
Definition effects.cc:286
VecBase< float, 2 > float2
static blender::bke::bNodeSocketTemplate inputs[]
void RNA_string_set(PointerRNA *ptr, const char *name, const char *value)
PropertyRNA * RNA_struct_find_property(PointerRNA *ptr, const char *identifier)
void RNA_boolean_set(PointerRNA *ptr, const char *name, bool value)
bool RNA_struct_property_is_set_ex(PointerRNA *ptr, const char *identifier, bool use_ghost)
void RNA_int_set(PointerRNA *ptr, const char *name, int value)
void RNA_float_get_array(PointerRNA *ptr, const char *name, float *values)
void RNA_property_update(bContext *C, PointerRNA *ptr, PropertyRNA *prop)
void RNA_string_get(PointerRNA *ptr, const char *name, char *value)
int RNA_int_get(PointerRNA *ptr, const char *name)
char * RNA_string_get_alloc(PointerRNA *ptr, const char *name, char *fixedbuf, int fixedlen, int *r_len)
float RNA_float_get(PointerRNA *ptr, const char *name)
void RNA_float_set(PointerRNA *ptr, const char *name, float value)
bool RNA_struct_property_is_set(PointerRNA *ptr, const char *identifier)
bool RNA_boolean_get(PointerRNA *ptr, const char *name)
void RNA_enum_set(PointerRNA *ptr, const char *name, int value)
void RNA_float_set_array(PointerRNA *ptr, const char *name, const float *values)
PointerRNA RNA_pointer_create_discrete(ID *id, StructRNA *type, void *data)
int RNA_enum_get(PointerRNA *ptr, const char *name)
int RNA_property_collection_length(PointerRNA *ptr, PropertyRNA *prop)
void RNA_property_string_set(PointerRNA *ptr, PropertyRNA *prop, const char *value)
PropertyRNA * RNA_def_float(StructOrFunctionRNA *cont_, const char *identifier, const float default_value, const float hardmin, const float hardmax, const char *ui_name, const char *ui_description, const float softmin, const float softmax)
PropertyRNA * RNA_def_float_vector(StructOrFunctionRNA *cont_, const char *identifier, const int len, const float *default_value, const float hardmin, const float hardmax, const char *ui_name, const char *ui_description, const float softmin, const float softmax)
PropertyRNA * RNA_def_enum(StructOrFunctionRNA *cont_, const char *identifier, const EnumPropertyItem *items, const int default_value, const char *ui_name, const char *ui_description)
PropertyRNA * RNA_def_boolean(StructOrFunctionRNA *cont_, const char *identifier, const bool default_value, const char *ui_name, const char *ui_description)
void RNA_def_property_translation_context(PropertyRNA *prop, const char *context)
void RNA_def_property_flag(PropertyRNA *prop, PropertyFlag flag)
void RNA_def_enum_funcs(PropertyRNA *prop, EnumPropertyItemFunc itemfunc)
void RNA_def_property_ui_range(PropertyRNA *prop, double min, double max, double step, int precision)
PropertyRNA * RNA_def_int(StructOrFunctionRNA *cont_, const char *identifier, const int default_value, const int hardmin, const int hardmax, const char *ui_name, const char *ui_description, const int softmin, const int softmax)
const EnumPropertyItem rna_enum_dummy_NULL_items[]
Definition rna_rna.cc:26
const EnumPropertyItem rna_enum_strip_color_items[]
#define FLT_MAX
Definition stdcycles.h:14
bAction * action
char * rna_path
void * last
void * first
ListBase scenes
Definition BKE_main.hh:245
struct Editing * ed
struct RenderData r
struct AnimData * adt
struct UnitSettings unit
ListBase markers
float cursor[2]
StripTransform * transform
StripElem * stripdata
char dirpath[768]
char filename[256]
void * scene_sound
struct Strip * input1
StripData * data
struct Scene * scene
void * effectdata
struct bSound * sound
ListBase seqbase
struct Strip * next
float sound_offset
char name[64]
struct Strip * input2
ListBase channels
char filepath[1024]
VectorSet< Strip * > strips
void(* free)(Strip *strip, bool do_id_user)
void(* init)(Strip *strip)
void separator(float factor=1.0f, LayoutSeparatorType type=LayoutSeparatorType::Auto)
uiLayout & row(bool align)
void prop(PointerRNA *ptr, PropertyRNA *prop, int index, int value, eUI_Item_Flag flag, std::optional< blender::StringRef > name_opt, int icon, std::optional< blender::StringRef > placeholder=std::nullopt)
wmEventType type
Definition WM_types.hh:754
short val
Definition WM_types.hh:756
int mval[2]
Definition WM_types.hh:760
const void * modal_items
struct ReportList * reports
struct uiLayout * layout
struct wmOperatorType * type
struct PointerRNA * ptr
i
Definition text_draw.cc:230
uint len
void WM_event_add_fileselect(bContext *C, wmOperator *op)
wmEventHandler_Op * WM_event_add_modal_handler(bContext *C, wmOperator *op)
void WM_event_add_notifier(const bContext *C, uint type, void *reference)
@ EVT_MODAL_MAP
@ MOUSEMOVE
PointerRNA * ptr
Definition wm_files.cc:4227
wmOperatorType * ot
Definition wm_files.cc:4226
wmKeyMap * WM_modalkeymap_ensure(wmKeyConfig *keyconf, const char *idname, const EnumPropertyItem *items)
Definition wm_keymap.cc:929
void WM_modalkeymap_assign(wmKeyMap *km, const char *opname)
wmKeyMap * WM_modalkeymap_find(wmKeyConfig *keyconf, const char *idname)
Definition wm_keymap.cc:956
void WM_operator_properties_filesel(wmOperatorType *ot, const int filter, const short type, const eFileSel_Action action, const eFileSel_Flag flag, const short display, const short sort)
wmOperatorStatus WM_operator_props_popup_confirm_ex(bContext *C, wmOperator *op, const wmEvent *, std::optional< std::string > title, std::optional< std::string > confirm_text, const bool cancel_default)
wmOperatorStatus WM_enum_search_invoke(bContext *C, wmOperator *op, const wmEvent *)