Blender V4.5
sequencer_select.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 <cmath>
10#include <cstdlib>
11#include <cstring>
12
13#include "MEM_guardedalloc.h"
14
15#include "BLI_ghash.h"
16#include "BLI_listbase.h"
17#include "BLI_math_geom.h"
18#include "BLI_math_vector.h"
20#include "BLI_string.h"
21#include "BLI_utildefines.h"
22
23#include "DNA_scene_types.h"
24#include "DNA_space_types.h"
25
26#include "BKE_context.hh"
27#include "BKE_report.hh"
28
29#include "BLT_translation.hh"
30
31#include "WM_api.hh"
32#include "WM_types.hh"
33
34#include "RNA_define.hh"
35
36#include "SEQ_channels.hh"
37#include "SEQ_connect.hh"
38#include "SEQ_effects.hh"
39#include "SEQ_iterator.hh"
40#include "SEQ_relations.hh"
41#include "SEQ_retiming.hh"
42#include "SEQ_select.hh"
43#include "SEQ_sequencer.hh"
44#include "SEQ_time.hh"
45#include "SEQ_transform.hh"
46
47/* For menu, popup, icons, etc. */
48
49#include "ED_outliner.hh"
50#include "ED_screen.hh"
51#include "ED_select_utils.hh"
52#include "ED_sequencer.hh"
53
54#include "UI_view2d.hh"
55
56/* Own include. */
57#include "sequencer_intern.hh"
58
59namespace blender::ed::vse {
60
61/* -------------------------------------------------------------------- */
64
66 public:
69
70 MouseCoords(const View2D *v2d, int x, int y)
71 {
72 region[0] = x;
73 region[1] = y;
74 UI_view2d_region_to_view(v2d, x, y, &view[0], &view[1]);
75 }
76};
77
78bool deselect_all_strips(const Scene *scene)
79{
80 Editing *ed = seq::editing_get(scene);
81 bool changed = false;
82
83 if (ed == nullptr) {
84 return changed;
85 }
86
88 for (Strip *strip : strips) {
89 if (strip->flag & STRIP_ALLSEL) {
90 strip->flag &= ~STRIP_ALLSEL;
91 changed = true;
92 }
93 }
94 return changed;
95}
96
97Strip *strip_under_mouse_get(const Scene *scene, const View2D *v2d, const int mval[2])
98{
99 float mouse_co[2];
100 UI_view2d_region_to_view(v2d, mval[0], mval[1], &mouse_co[0], &mouse_co[1]);
101
103 int mouse_channel = int(mouse_co[1]);
104 for (Strip *strip : visible) {
105 if (strip->channel != mouse_channel) {
106 continue;
107 }
108 rctf body;
109 strip_rectf(scene, strip, &body);
110 if (BLI_rctf_isect_pt_v(&body, mouse_co)) {
111 return strip;
112 }
113 }
114
115 return nullptr;
116}
117
119{
120 Scene *scene = CTX_data_scene(C);
121 Editing *ed = seq::editing_get(scene);
124
125 const bool is_preview = sequencer_view_has_preview_poll(C);
126 if (is_preview) {
127 return seq::query_rendered_strips(scene, channels, seqbase, scene->r.cfra, 0);
128 }
129
130 return seq::query_all_strips(seqbase);
131}
132
134{
135 const Scene *scene = CTX_data_scene(C);
136 Editing *ed = seq::editing_get(scene);
139
140 const bool is_preview = sequencer_view_has_preview_poll(C);
141
142 if (is_preview) {
144 scene, channels, seqbase, scene->r.cfra, 0);
145 strips.remove_if([&](Strip *strip) { return (strip->flag & SELECT) == 0; });
146 return strips;
147 }
148
149 return seq::query_selected_strips(seqbase);
150}
151
152static void select_surrounding_handles(Scene *scene, Strip *test) /* XXX BRING BACK */
153{
154 Strip *neighbor;
155
156 neighbor = find_neighboring_strip(scene, test, seq::SIDE_LEFT, -1);
157 if (neighbor) {
158 /* Only select neighbor handle if matching handle from test strip is also selected,
159 * or if neighbor was not selected at all up till now.
160 * Otherwise, we get odd mismatch when shift-alt-rmb selecting neighbor strips... */
161 if (!(neighbor->flag & SELECT) || (test->flag & SEQ_LEFTSEL)) {
162 neighbor->flag |= SEQ_RIGHTSEL;
163 }
164 neighbor->flag |= SELECT;
165 recurs_sel_strip(neighbor);
166 }
167 neighbor = find_neighboring_strip(scene, test, seq::SIDE_RIGHT, -1);
168 if (neighbor) {
169 if (!(neighbor->flag & SELECT) || (test->flag & SEQ_RIGHTSEL)) { /* See comment above. */
170 neighbor->flag |= SEQ_LEFTSEL;
171 }
172 neighbor->flag |= SELECT;
173 recurs_sel_strip(neighbor);
174 }
175}
176
177/* Used for mouse selection in #SEQUENCER_OT_select. */
179 const Scene *scene, ListBase *seqbase, int sel_side, int channel, int frame)
180{
181
182 LISTBASE_FOREACH (Strip *, strip, seqbase) {
183 if (channel == strip->channel) {
184 switch (sel_side) {
185 case seq::SIDE_LEFT:
186 if (frame > seq::time_left_handle_frame_get(scene, strip)) {
187 strip->flag &= ~(SEQ_RIGHTSEL | SEQ_LEFTSEL);
188 strip->flag |= SELECT;
189 }
190 break;
191 case seq::SIDE_RIGHT:
192 if (frame < seq::time_left_handle_frame_get(scene, strip)) {
193 strip->flag &= ~(SEQ_RIGHTSEL | SEQ_LEFTSEL);
194 strip->flag |= SELECT;
195 }
196 break;
197 case seq::SIDE_BOTH:
198 strip->flag &= ~(SEQ_RIGHTSEL | SEQ_LEFTSEL);
199 strip->flag |= SELECT;
200 break;
201 }
202 }
203 }
204}
205
206/* Used for mouse selection in #SEQUENCER_OT_select_side. */
207static void select_active_side_range(const Scene *scene,
208 ListBase *seqbase,
209 const int sel_side,
210 const int frame_ranges[seq::MAX_CHANNELS],
211 const int frame_ignore)
212{
213 LISTBASE_FOREACH (Strip *, strip, seqbase) {
214 if (strip->channel < seq::MAX_CHANNELS) {
215 const int frame = frame_ranges[strip->channel];
216 if (frame == frame_ignore) {
217 continue;
218 }
219 switch (sel_side) {
220 case seq::SIDE_LEFT:
221 if (frame > seq::time_left_handle_frame_get(scene, strip)) {
222 strip->flag &= ~(SEQ_RIGHTSEL | SEQ_LEFTSEL);
223 strip->flag |= SELECT;
224 }
225 break;
226 case seq::SIDE_RIGHT:
227 if (frame < seq::time_left_handle_frame_get(scene, strip)) {
228 strip->flag &= ~(SEQ_RIGHTSEL | SEQ_LEFTSEL);
229 strip->flag |= SELECT;
230 }
231 break;
232 case seq::SIDE_BOTH:
233 strip->flag &= ~(SEQ_RIGHTSEL | SEQ_LEFTSEL);
234 strip->flag |= SELECT;
235 break;
236 }
237 }
238 }
239}
240
241/* Used alongside `select_linked_time` helper function in #SEQUENCER_OT_select. */
242static void select_linked_time_strip(const Scene *scene,
243 const Strip *strip_source,
244 const eStripHandle handle_clicked)
245{
246 ListBase *seqbase = seq::active_seqbase_get(scene->ed);
247 int source_left = seq::time_left_handle_frame_get(scene, strip_source);
248 int source_right = seq::time_right_handle_frame_get(scene, strip_source);
249
250 LISTBASE_FOREACH (Strip *, strip_dest, seqbase) {
251 if (strip_source->channel != strip_dest->channel) {
252 const bool left_match = (seq::time_left_handle_frame_get(scene, strip_dest) == source_left);
253 const bool right_match = (seq::time_right_handle_frame_get(scene, strip_dest) ==
254 source_right);
255
256 if (left_match && right_match) {
257 /* Direct match, copy all selection settings. */
258 strip_dest->flag &= ~STRIP_ALLSEL;
259 strip_dest->flag |= strip_source->flag & (STRIP_ALLSEL);
260 recurs_sel_strip(strip_dest);
261 }
262 else if (left_match && handle_clicked == STRIP_HANDLE_LEFT) {
263 strip_dest->flag &= ~(SELECT | SEQ_LEFTSEL);
264 strip_dest->flag |= strip_source->flag & (SELECT | SEQ_LEFTSEL);
265 recurs_sel_strip(strip_dest);
266 }
267 else if (right_match && handle_clicked == STRIP_HANDLE_RIGHT) {
268 strip_dest->flag &= ~(SELECT | SEQ_RIGHTSEL);
269 strip_dest->flag |= strip_source->flag & (SELECT | SEQ_RIGHTSEL);
270 recurs_sel_strip(strip_dest);
271 }
272 }
273 }
274}
275
276#if 0 /* BRING BACK */
277void select_surround_from_last(Scene *scene)
278{
279 Strip *strip = get_last_seq(scene);
280
281 if (strip == nullptr) {
282 return;
283 }
284
285 select_surrounding_handles(scene, strip);
286}
287#endif
288
289void select_strip_single(Scene *scene, Strip *strip, bool deselect_all)
290{
291 Editing *ed = seq::editing_get(scene);
292
293 if (deselect_all) {
294 deselect_all_strips(scene);
295 }
296
297 seq::select_active_set(scene, strip);
298
300 if (strip->data) {
301 BLI_strncpy(ed->act_imagedir, strip->data->dirpath, FILE_MAXDIR);
302 }
303 }
304 else if (strip->type == STRIP_TYPE_SOUND_RAM) {
305 if (strip->data) {
306 BLI_strncpy(ed->act_sounddir, strip->data->dirpath, FILE_MAXDIR);
307 }
308 }
309 strip->flag |= SELECT;
310 recurs_sel_strip(strip);
311}
312
313void strip_rectf(const Scene *scene, const Strip *strip, rctf *r_rect)
314{
315 r_rect->xmin = seq::time_left_handle_frame_get(scene, strip);
316 r_rect->xmax = seq::time_right_handle_frame_get(scene, strip);
317 r_rect->ymin = strip->channel + STRIP_OFSBOTTOM;
318 r_rect->ymax = strip->channel + STRIP_OFSTOP;
319}
320
321Strip *find_neighboring_strip(const Scene *scene, const Strip *test, const int lr, int sel)
322{
323 /* sel: 0==unselected, 1==selected, -1==don't care. */
324 Editing *ed = seq::editing_get(scene);
325
326 if (ed == nullptr) {
327 return nullptr;
328 }
329
330 if (sel > 0) {
331 sel = SELECT;
332 }
333 LISTBASE_FOREACH (Strip *, strip, ed->seqbasep) {
334 if ((strip != test) && (test->channel == strip->channel) &&
335 ((sel == -1) || (sel && (strip->flag & SELECT)) ||
336 (sel == 0 && (strip->flag & SELECT) == 0)))
337 {
338 switch (lr) {
339 case seq::SIDE_LEFT:
340 if (seq::time_left_handle_frame_get(scene, test) ==
342 {
343 return strip;
344 }
345 break;
346 case seq::SIDE_RIGHT:
347 if (seq::time_right_handle_frame_get(scene, test) ==
349 {
350 return strip;
351 }
352 break;
353 }
354 }
355 }
356 return nullptr;
357}
358
359#if 0
360static void select_neighbor_from_last(Scene *scene, int lr)
361{
362 Strip *strip = seq::SEQ_select_active_get(scene);
363 Strip *neighbor;
364 bool changed = false;
365 if (strip) {
366 neighbor = find_neighboring_strip(scene, strip, lr, -1);
367 if (neighbor) {
368 switch (lr) {
369 case seq::SIDE_LEFT:
370 neighbor->flag |= SELECT;
371 recurs_sel_strip(neighbor);
372 neighbor->flag |= SEQ_RIGHTSEL;
373 strip->flag |= SEQ_LEFTSEL;
374 break;
375 case seq::SIDE_RIGHT:
376 neighbor->flag |= SELECT;
377 recurs_sel_strip(neighbor);
378 neighbor->flag |= SEQ_LEFTSEL;
379 strip->flag |= SEQ_RIGHTSEL;
380 break;
381 }
382 strip->flag |= SELECT;
383 changed = true;
384 }
385 }
386 if (changed) {
387 /* Pass. */
388 }
389}
390#endif
391
392void recurs_sel_strip(Strip *strip_meta)
393{
394 Strip *strip;
395 strip = static_cast<Strip *>(strip_meta->seqbase.first);
396
397 while (strip) {
398
399 if (strip_meta->flag & (SEQ_LEFTSEL + SEQ_RIGHTSEL)) {
400 strip->flag &= ~STRIP_ALLSEL;
401 }
402 else if (strip_meta->flag & SELECT) {
403 strip->flag |= SELECT;
404 }
405 else {
406 strip->flag &= ~STRIP_ALLSEL;
407 }
408
409 if (strip->seqbase.first) {
410 recurs_sel_strip(strip);
411 }
412
413 strip = static_cast<Strip *>(strip->next);
414 }
415}
416
417bool strip_point_image_isect(const Scene *scene, const Strip *strip, float point_view[2])
418{
420 scene, strip);
421 return isect_point_quad_v2(point_view,
422 strip_image_quad[0],
423 strip_image_quad[1],
424 strip_image_quad[2],
425 strip_image_quad[3]);
426}
427
433
435
436/* -------------------------------------------------------------------- */
439
441{
442 int action = RNA_enum_get(op->ptr, "action");
443 Scene *scene = CTX_data_scene(C);
444
446 return OPERATOR_CANCELLED;
447 }
448
450 {
452 }
453
455
456 if (action == SEL_TOGGLE) {
457 action = SEL_SELECT;
458 for (Strip *strip : strips) {
459 if (strip->flag & STRIP_ALLSEL) {
460 action = SEL_DESELECT;
461 break;
462 }
463 }
464 }
465 if (ELEM(action, SEL_INVERT, SEL_SELECT)) {
466 if (action == SEL_INVERT) {
467 for (Strip *strip : strips) {
468 if (strip->flag & STRIP_ALLSEL) {
469 strips.remove(strip);
470 }
471 }
472 }
473 deselect_all_strips(scene);
474 }
475 for (Strip *strip : strips) {
476 switch (action) {
477 case SEL_SELECT:
478 strip->flag |= SELECT;
479 break;
480 case SEL_DESELECT:
481 strip->flag &= ~STRIP_ALLSEL;
482 break;
483 case SEL_INVERT:
484 strip->flag |= SELECT;
485 break;
486 }
487 }
490
491 return OPERATOR_FINISHED;
492}
493
495{
496 /* Identifiers. */
497 ot->name = "(De)select All";
498 ot->idname = "SEQUENCER_OT_select_all";
499 ot->description = "Select or deselect all strips";
500
501 /* API callbacks. */
503 ot->poll = sequencer_edit_poll;
504
505 /* Flags. */
506 ot->flag = OPTYPE_UNDO;
507
509}
510
512
513/* -------------------------------------------------------------------- */
516
518{
519 Scene *scene = CTX_data_scene(C);
520
522 return OPERATOR_CANCELLED;
523 }
524
526
527 for (Strip *strip : strips) {
528 if (strip->flag & SELECT) {
529 strip->flag &= ~STRIP_ALLSEL;
530 }
531 else {
532 strip->flag &= ~(SEQ_LEFTSEL + SEQ_RIGHTSEL);
533 strip->flag |= SELECT;
534 }
535 }
536
539
540 return OPERATOR_FINISHED;
541}
542
544{
545 /* Identifiers. */
546 ot->name = "Select Inverse";
547 ot->idname = "SEQUENCER_OT_select_inverse";
548 ot->description = "Select unselected strips";
549
550 /* API callbacks. */
552 ot->poll = sequencer_edit_poll;
553
554 /* Flags. */
555 ot->flag = OPTYPE_UNDO;
556}
557
559
560/* -------------------------------------------------------------------- */
563
564static void sequencer_select_set_active(Scene *scene, Strip *strip)
565{
566 Editing *ed = seq::editing_get(scene);
567
568 seq::select_active_set(scene, strip);
569
571 if (strip->data) {
572 BLI_strncpy(ed->act_imagedir, strip->data->dirpath, FILE_MAXDIR);
573 }
574 }
575 else if (strip->type == STRIP_TYPE_SOUND_RAM) {
576 if (strip->data) {
577 BLI_strncpy(ed->act_sounddir, strip->data->dirpath, FILE_MAXDIR);
578 }
579 }
580 recurs_sel_strip(strip);
581}
582
584 const View2D *v2d,
585 const int mval[2],
586 Scene *scene)
587{
588 Editing *ed = seq::editing_get(scene);
589
590 const float x = UI_view2d_region_to_view_x(v2d, mval[0]);
592 if (((x < scene->r.cfra) &&
593 (seq::time_right_handle_frame_get(scene, strip_iter) <= scene->r.cfra)) ||
594 ((x >= scene->r.cfra) &&
595 (seq::time_left_handle_frame_get(scene, strip_iter) >= scene->r.cfra)))
596 {
597 /* Select left or right. */
598 strip_iter->flag |= SELECT;
599 recurs_sel_strip(strip_iter);
600 }
601 }
602
603 {
604 SpaceSeq *sseq = CTX_wm_space_seq(C);
605 if (sseq && sseq->flag & SEQ_MARKER_TRANS) {
606
607 LISTBASE_FOREACH (TimeMarker *, tmarker, &scene->markers) {
608 if (((x < scene->r.cfra) && (tmarker->frame <= scene->r.cfra)) ||
609 ((x >= scene->r.cfra) && (tmarker->frame >= scene->r.cfra)))
610 {
611 tmarker->flag |= SELECT;
612 }
613 else {
614 tmarker->flag &= ~SELECT;
615 }
616 }
617 }
618 }
619}
620
622 Strip *strip,
623 const eStripHandle handle_clicked)
624{
625 Scene *scene = CTX_data_scene(C);
626 Editing *ed = seq::editing_get(scene);
627 if (!ELEM(handle_clicked, STRIP_HANDLE_LEFT, STRIP_HANDLE_RIGHT)) {
628 /* First click selects the strip and its adjacent handles (if valid).
629 * Second click selects the strip,
630 * both of its handles and its adjacent handles (if valid). */
631 const bool is_striponly_selected = ((strip->flag & STRIP_ALLSEL) == SELECT);
632 strip->flag &= ~STRIP_ALLSEL;
633 strip->flag |= is_striponly_selected ? STRIP_ALLSEL : SELECT;
634 select_surrounding_handles(scene, strip);
635 }
636 else {
637 /* Always select the strip under the cursor. */
638 strip->flag |= SELECT;
639
640 /* First click selects adjacent handles on that side.
641 * Second click selects all strips in that direction.
642 * If there are no adjacent strips, it just selects all in that direction.
643 */
644 const int sel_side = (handle_clicked == STRIP_HANDLE_LEFT) ? seq::SIDE_LEFT : seq::SIDE_RIGHT;
645
646 Strip *neighbor = find_neighboring_strip(scene, strip, sel_side, -1);
647 if (neighbor) {
648 switch (sel_side) {
649 case seq::SIDE_LEFT:
650 if ((strip->flag & SEQ_LEFTSEL) && (neighbor->flag & SEQ_RIGHTSEL)) {
651 strip->flag |= SELECT;
652 select_active_side(scene,
653 ed->seqbasep,
655 strip->channel,
656 seq::time_left_handle_frame_get(scene, strip));
657 }
658 else {
659 strip->flag |= SELECT;
660 neighbor->flag |= SELECT;
661 recurs_sel_strip(neighbor);
662 neighbor->flag |= SEQ_RIGHTSEL;
663 strip->flag |= SEQ_LEFTSEL;
664 }
665 break;
666 case seq::SIDE_RIGHT:
667 if ((strip->flag & SEQ_RIGHTSEL) && (neighbor->flag & SEQ_LEFTSEL)) {
668 strip->flag |= SELECT;
669 select_active_side(scene,
670 ed->seqbasep,
672 strip->channel,
673 seq::time_left_handle_frame_get(scene, strip));
674 }
675 else {
676 strip->flag |= SELECT;
677 neighbor->flag |= SELECT;
678 recurs_sel_strip(neighbor);
679 neighbor->flag |= SEQ_LEFTSEL;
680 strip->flag |= SEQ_RIGHTSEL;
681 }
682 break;
683 }
684 }
685 else {
686
687 select_active_side(scene,
688 ed->seqbasep,
689 sel_side,
690 strip->channel,
691 seq::time_left_handle_frame_get(scene, strip));
692 }
693 }
694}
695
703
704static int strip_sort_for_depth_select(const void *a, const void *b)
705{
706 const SeqSelect_Link *slink_a = static_cast<const SeqSelect_Link *>(a);
707 const SeqSelect_Link *slink_b = static_cast<const SeqSelect_Link *>(b);
708
709 /* Exactly overlapping strips, sort by channel (so the top-most is first). */
710 if (slink_a->strip->channel < slink_b->strip->channel) {
711 return 1;
712 }
713 if (slink_a->strip->channel > slink_b->strip->channel) {
714 return -1;
715 }
716 return 0;
717}
718
719static int strip_sort_for_center_select(const void *a, const void *b)
720{
721 const SeqSelect_Link *slink_a = static_cast<const SeqSelect_Link *>(a);
722 const SeqSelect_Link *slink_b = static_cast<const SeqSelect_Link *>(b);
723 if (slink_a->center_dist_sq > slink_b->center_dist_sq) {
724 return 1;
725 }
726 if (slink_a->center_dist_sq < slink_b->center_dist_sq) {
727 return -1;
728 }
729
730 /* Exactly overlapping strips, use depth. */
732}
733
740 const bContext *C, const int mval[2], const bool toggle, const bool extend, const bool center)
741{
742 Scene *scene = CTX_data_scene(C);
743 Editing *ed = seq::editing_get(scene);
746 SpaceSeq *sseq = CTX_wm_space_seq(C);
748
749 float mouseco_view[2];
750 UI_view2d_region_to_view(v2d, mval[0], mval[1], &mouseco_view[0], &mouseco_view[1]);
751
752 /* Always update the coordinates (check extended after). */
753 const bool use_cycle = (!WM_cursor_test_motion_and_update(mval) || extend || toggle);
754
755 /* Allow strips this far from the closest center to be included.
756 * This allows cycling over center points which are near enough
757 * to overlapping from the users perspective. */
758 const float center_dist_sq_max = square_f(75.0f * U.pixelsize);
759 const float center_scale_px[2] = {
762 };
763
765 scene, channels, seqbase, scene->r.cfra, sseq->chanshown);
766
767 SeqSelect_Link *slink_active = nullptr;
768 Strip *strip_active = seq::select_active_get(scene);
769 ListBase strips_ordered = {nullptr};
770 for (Strip *strip : strips) {
771 bool isect = false;
772 float center_dist_sq_test = 0.0f;
773 if (center) {
774 /* Detect overlapping center points (scaled by the zoom level). */
776 sub_v2_v2(co, mouseco_view);
777 mul_v2_v2(co, center_scale_px);
778 center_dist_sq_test = len_squared_v2(co);
779 isect = center_dist_sq_test <= center_dist_sq_max;
780 if (isect) {
781 /* Use an active strip penalty for "center" selection when cycle is enabled. */
782 if (use_cycle && (strip == strip_active) && (strip_active->flag & SELECT)) {
783 center_dist_sq_test = square_f(sqrtf(center_dist_sq_test) + (3.0f * U.pixelsize));
784 }
785 }
786 }
787 else {
788 isect = strip_point_image_isect(scene, strip, mouseco_view);
789 }
790
791 if (isect) {
793 slink->strip = strip;
794 slink->center_dist_sq = center_dist_sq_test;
795 BLI_addtail(&strips_ordered, slink);
796
797 if (strip == strip_active) {
798 slink_active = slink;
799 }
800 }
801 }
802
803 BLI_listbase_sort(&strips_ordered,
805
806 SeqSelect_Link *slink_select = static_cast<SeqSelect_Link *>(strips_ordered.first);
807 Strip *strip_select = nullptr;
808 if (slink_select != nullptr) {
809 /* Only use special behavior for the active strip when it's selected. */
810 if ((center == false) && slink_active && (strip_active->flag & SELECT)) {
811 if (use_cycle) {
812 if (slink_active->next) {
813 slink_select = slink_active->next;
814 }
815 }
816 else {
817 /* Match object selection behavior: keep the current active item unless cycle is enabled.
818 * Clicking again in the same location will cycle away from the active object. */
819 slink_select = slink_active;
820 }
821 }
822 strip_select = slink_select->strip;
823 }
824
825 BLI_freelistN(&strips_ordered);
826
827 return strip_select;
828}
829
830bool handle_is_selected(const Strip *strip, const eStripHandle handle)
831{
832 return ((strip->flag & SEQ_LEFTSEL) && (handle == STRIP_HANDLE_LEFT)) ||
833 ((strip->flag & SEQ_RIGHTSEL) && (handle == STRIP_HANDLE_RIGHT));
834}
835
841static bool element_already_selected(const StripSelection &selection)
842{
843 if (selection.strip1 == nullptr) {
844 return false;
845 }
846
847 const bool strip1_already_selected = ((selection.strip1->flag & SELECT) != 0);
848 if (selection.strip2 == nullptr) {
849 if (selection.handle == STRIP_HANDLE_NONE) {
850 return strip1_already_selected && !(selection.strip1->flag & (SEQ_LEFTSEL | SEQ_RIGHTSEL));
851 }
852 return strip1_already_selected && handle_is_selected(selection.strip1, selection.handle);
853 }
854
855 /* If we are here, the strip selection is dual handle. */
856 const bool strip2_already_selected = ((selection.strip2->flag & SELECT) != 0);
857 const int strip1_handle = selection.strip1->flag & (SEQ_RIGHTSEL | SEQ_LEFTSEL);
858 const int strip2_handle = selection.strip2->flag & (SEQ_RIGHTSEL | SEQ_LEFTSEL);
859 /* Handles must be selected in XOR fashion, with `strip1` matching `handle_clicked`. */
860 const bool both_handles_selected = strip1_handle == selection.handle && strip2_handle != 0 &&
861 strip1_handle != strip2_handle;
862 return strip1_already_selected && strip2_already_selected && both_handles_selected;
863}
864
866{
868 sources.add(selection.strip1);
869 if (selection.strip2) {
870 sources.add(selection.strip2);
871 }
872
873 for (Strip *source : sources) {
875 for (Strip *connection : connections) {
876 /* Copy selection settings exactly for connected strips. */
877 connection->flag &= ~STRIP_ALLSEL;
878 connection->flag |= source->flag & (STRIP_ALLSEL);
879 }
880 }
881}
882
884 const StripSelection &selection,
885 VectorSet<Strip *> copy_to)
886{
887 /* TODO(john): Dual handle propagation is not supported for now due to its complexity,
888 * but once we simplify selection assumptions in 5.0 we can add support for it. */
889 if (selection.strip2) {
890 return;
891 }
892
893 Strip *source = selection.strip1;
894 /* Test for neighboring strips in the `copy_to` list. If any border one another, remove them,
895 * since we don't want to mess with dual handles. */
896 blender::VectorSet<Strip *> test(copy_to);
897 test.add(source);
898 for (Strip *test_strip : test) {
899 /* Don't copy left handle over to a `test_strip` that has a strip directly on its left. */
900 if ((source->flag & SEQ_LEFTSEL) &&
901 find_neighboring_strip(scene, test_strip, seq::SIDE_LEFT, -1))
902 {
903 /* If this was the source strip, do not copy handles at all and prematurely return. */
904 if (test_strip == source) {
905 return;
906 }
907 copy_to.remove(test_strip);
908 }
909
910 /* Don't copy right handle over to a `test_strip` that has a strip directly on its right. */
911 if ((source->flag & SEQ_RIGHTSEL) &&
912 find_neighboring_strip(scene, test_strip, seq::SIDE_RIGHT, -1))
913 {
914 /* If this was the source strip, do not copy handles at all and prematurely return. */
915 if (test_strip == source) {
916 return;
917 }
918 copy_to.remove(test_strip);
919 }
920 }
921
922 for (Strip *strip : copy_to) {
923 /* NOTE that this can be `ALLSEL` since `prev_selection` was deselected earlier. */
924 strip->flag &= ~STRIP_ALLSEL;
925 strip->flag |= source->flag & STRIP_ALLSEL;
926 }
927}
928
930 Strip *strip,
931 const eStripHandle handle_clicked,
932 const bool extend,
933 const bool deselect,
934 const bool toggle)
935{
936 const bool is_active = (ed->act_strip == strip);
937
938 /* Exception for active strip handles. */
939 if ((handle_clicked != STRIP_HANDLE_NONE) && (strip->flag & SELECT) && is_active && toggle) {
940 if (handle_clicked == STRIP_HANDLE_LEFT) {
941 strip->flag ^= SEQ_LEFTSEL;
942 }
943 else if (handle_clicked == STRIP_HANDLE_RIGHT) {
944 strip->flag ^= SEQ_RIGHTSEL;
945 }
946 return;
947 }
948
949 /* Select strip. */
950 /* Match object selection behavior. */
951 int action = -1;
952 if (extend) {
953 action = 1;
954 }
955 else if (deselect) {
956 action = 0;
957 }
958 else {
959 if (!((strip->flag & SELECT) && is_active)) {
960 action = 1;
961 }
962 else if (toggle) {
963 action = 0;
964 }
965 }
966
967 if (action == 1) {
968 strip->flag |= SELECT;
969 if (handle_clicked == STRIP_HANDLE_LEFT) {
970 strip->flag |= SEQ_LEFTSEL;
971 }
972 if (handle_clicked == STRIP_HANDLE_RIGHT) {
973 strip->flag |= SEQ_RIGHTSEL;
974 }
975 }
976 else if (action == 0) {
977 strip->flag &= ~STRIP_ALLSEL;
978 }
979}
980
981static void select_linked_time(const Scene *scene,
982 const StripSelection &selection,
983 const bool extend,
984 const bool deselect,
985 const bool toggle)
986{
987 Editing *ed = seq::editing_get(scene);
988
989 sequencer_select_strip_impl(ed, selection.strip1, selection.handle, extend, deselect, toggle);
990 select_linked_time_strip(scene, selection.strip1, selection.handle);
991
992 if (selection.strip2 != nullptr) {
993 eStripHandle strip2_handle_clicked = (selection.handle == STRIP_HANDLE_LEFT) ?
997 ed, selection.strip2, strip2_handle_clicked, extend, deselect, toggle);
998 select_linked_time_strip(scene, selection.strip2, strip2_handle_clicked);
999 }
1000}
1001
1008static float inner_clickable_handle_size_get(const Scene *scene,
1009 const Strip *strip,
1010 const View2D *v2d)
1011{
1012 const float pixelx = 1 / UI_view2d_scale_get_x(v2d);
1013 const float strip_len = seq::time_right_handle_frame_get(scene, strip) -
1014 seq::time_left_handle_frame_get(scene, strip);
1015 return min_ff(15.0f * pixelx * U.pixelsize, strip_len / 4);
1016}
1017
1018bool can_select_handle(const Scene *scene, const Strip *strip, const View2D *v2d)
1019{
1020 if (seq::effect_get_num_inputs(strip->type) > 0) {
1021 return false;
1022 }
1023
1024 Editing *ed = seq::editing_get(scene);
1026 if (seq::transform_is_locked(channels, strip)) {
1027 return false;
1028 }
1029
1030 /* This ensures clickable handles are deactivated when the strip gets too small (25 or 15
1031 * frames). Since the full handle size for a small strip is 1/3 of the strip size (see
1032 * `inner_clickable_handle_size_get`), this means handles cannot be smaller than 25/3 = 8px for
1033 * simple tweaking, 15/3 = 5px for legacy behavior. */
1034 int min_len = 25 * U.pixelsize;
1035 if ((U.sequencer_editor_flag & USER_SEQ_ED_SIMPLE_TWEAKING) == 0) {
1036 min_len = 15 * U.pixelsize;
1037 }
1038
1039 const float pixelx = 1 / UI_view2d_scale_get_x(v2d);
1040 const int strip_len = seq::time_right_handle_frame_get(scene, strip) -
1041 seq::time_left_handle_frame_get(scene, strip);
1042 if (strip_len / pixelx < min_len) {
1043 return false;
1044 }
1045
1046 if (UI_view2d_scale_get_y(v2d) < 16 * U.pixelsize) {
1047 return false;
1048 }
1049
1050 return true;
1051}
1052
1053static void strip_clickable_areas_get(const Scene *scene,
1054 const Strip *strip,
1055 const View2D *v2d,
1056 rctf *r_body,
1057 rctf *r_left_handle,
1058 rctf *r_right_handle)
1059{
1060 strip_rectf(scene, strip, r_body);
1061 *r_left_handle = *r_body;
1062 *r_right_handle = *r_body;
1063
1064 const float handsize = inner_clickable_handle_size_get(scene, strip, v2d);
1065 r_left_handle->xmax = r_body->xmin + handsize;
1066 r_right_handle->xmin = r_body->xmax - handsize;
1067 BLI_rctf_pad(r_left_handle, handsize / 3, 0.0f);
1068 BLI_rctf_pad(r_right_handle, handsize / 3, 0.0f);
1069 BLI_rctf_pad(r_body, -handsize, 0.0f);
1070}
1071
1072static rctf strip_clickable_area_get(const Scene *scene, const View2D *v2d, const Strip *strip)
1073{
1074 rctf body, left, right;
1075 strip_clickable_areas_get(scene, strip, v2d, &body, &left, &right);
1076 BLI_rctf_union(&body, &left);
1077 BLI_rctf_union(&body, &right);
1078 return body;
1079}
1080
1081static float strip_to_frame_distance(const Scene *scene,
1082 const View2D *v2d,
1083 const Strip *strip,
1084 float timeline_frame)
1085{
1086 rctf body, left, right;
1087 strip_clickable_areas_get(scene, strip, v2d, &body, &left, &right);
1088 return BLI_rctf_length_x(&body, timeline_frame);
1089}
1090
1097 const View2D *v2d,
1098 float mouse_co[2])
1099{
1100 Editing *ed = seq::editing_get(scene);
1101
1102 if (ed == nullptr) {
1103 return {};
1104 }
1105
1107 LISTBASE_FOREACH (Strip *, strip, ed->seqbasep) {
1108 if (strip->channel != int(mouse_co[1])) {
1109 continue;
1110 }
1111 if (seq::time_left_handle_frame_get(scene, strip) > v2d->cur.xmax) {
1112 continue;
1113 }
1114 if (seq::time_right_handle_frame_get(scene, strip) < v2d->cur.xmin) {
1115 continue;
1116 }
1117 const rctf body = strip_clickable_area_get(scene, v2d, strip);
1118 if (!BLI_rctf_isect_pt_v(&body, mouse_co)) {
1119 continue;
1120 }
1121 strips.append(strip);
1122 }
1123
1124 std::sort(strips.begin(), strips.end(), [&](const Strip *strip1, const Strip *strip2) {
1125 return strip_to_frame_distance(scene, v2d, strip1, mouse_co[0]) <
1126 strip_to_frame_distance(scene, v2d, strip2, mouse_co[0]);
1127 });
1128
1129 return strips;
1130}
1131
1132static bool strips_are_adjacent(const Scene *scene, const Strip *strip1, const Strip *strip2)
1133{
1134 const int s1_left = seq::time_left_handle_frame_get(scene, strip1);
1135 const int s1_right = seq::time_right_handle_frame_get(scene, strip1);
1136 const int s2_left = seq::time_left_handle_frame_get(scene, strip2);
1137 const int s2_right = seq::time_right_handle_frame_get(scene, strip2);
1138
1139 return s1_right == s2_left || s1_left == s2_right;
1140}
1141
1143 const Strip *strip,
1144 const View2D *v2d,
1145 float mouse_co[2])
1146{
1147 if (!can_select_handle(scene, strip, v2d)) {
1148 return STRIP_HANDLE_NONE;
1149 }
1150
1151 rctf body, left, right;
1152 strip_clickable_areas_get(scene, strip, v2d, &body, &left, &right);
1153 if (BLI_rctf_isect_pt_v(&left, mouse_co)) {
1154 return STRIP_HANDLE_LEFT;
1155 }
1156 if (BLI_rctf_isect_pt_v(&right, mouse_co)) {
1157 return STRIP_HANDLE_RIGHT;
1158 }
1159
1160 return STRIP_HANDLE_NONE;
1161}
1162
1165 const View2D *v2d,
1166 float mouse_co[2])
1167{
1168 const eStripHandle strip1_handle = strip_handle_under_cursor_get(
1169 scene, strips[0], v2d, mouse_co);
1170
1171 if (strip1_handle == STRIP_HANDLE_NONE) {
1172 return false;
1173 }
1174 if (!strips_are_adjacent(scene, strips[0], strips[1])) {
1175 return false;
1176 }
1177 const eStripHandle strip2_handle = strip_handle_under_cursor_get(
1178 scene, strips[1], v2d, mouse_co);
1179 if (strip1_handle == STRIP_HANDLE_RIGHT && strip2_handle != STRIP_HANDLE_LEFT) {
1180 return false;
1181 }
1182 if (strip1_handle == STRIP_HANDLE_LEFT && strip2_handle != STRIP_HANDLE_RIGHT) {
1183 return false;
1184 }
1185
1186 return true;
1187}
1188
1189StripSelection pick_strip_and_handle(const Scene *scene, const View2D *v2d, float mouse_co[2])
1190{
1191 StripSelection selection;
1192 /* Do not pick strips when clicking inside time scrub region. */
1193 float time_scrub_y = v2d->cur.ymax - UI_TIME_SCRUB_MARGIN_Y / UI_view2d_scale_get_y(v2d);
1194 if (mouse_co[1] > time_scrub_y) {
1195 return selection;
1196 }
1197
1198 blender::Vector<Strip *> strips = padded_strips_under_mouse_get(scene, v2d, mouse_co);
1199
1200 if (strips.size() == 0) {
1201 return selection;
1202 }
1203
1204 selection.strip1 = strips[0];
1205 selection.handle = strip_handle_under_cursor_get(scene, selection.strip1, v2d, mouse_co);
1206
1207 if (strips.size() == 2 && (U.sequencer_editor_flag & USER_SEQ_ED_SIMPLE_TWEAKING) != 0 &&
1208 is_mouse_over_both_handles_of_adjacent_strips(scene, strips, v2d, mouse_co))
1209 {
1210 selection.strip2 = strips[1];
1211 }
1212
1213 return selection;
1214}
1215
1217{
1218 const View2D *v2d = UI_view2d_fromcontext(C);
1219 Scene *scene = CTX_data_scene(C);
1220 Editing *ed = seq::editing_get(scene);
1221 ARegion *region = CTX_wm_region(C);
1222
1223 if (ed == nullptr) {
1224 return OPERATOR_CANCELLED;
1225 }
1226
1227 if (region->regiontype == RGN_TYPE_PREVIEW) {
1229 return OPERATOR_CANCELLED;
1230 }
1231 const SpaceSeq *sseq = CTX_wm_space_seq(C);
1232 if (sseq->mainb != SEQ_DRAW_IMG_IMBUF) {
1233 return OPERATOR_CANCELLED;
1234 }
1235 }
1236
1237 const bool was_retiming = sequencer_retiming_mode_is_active(C);
1238
1239 MouseCoords mouse_co(v2d, RNA_int_get(op->ptr, "mouse_x"), RNA_int_get(op->ptr, "mouse_y"));
1240
1241 /* Check to see if the mouse cursor intersects with the retiming box; if so, `strip_key_owner` is
1242 * set. If the cursor intersects with a retiming key, `key` will be set too. */
1243 Strip *strip_key_owner = nullptr;
1244 SeqRetimingKey *key = retiming_mouseover_key_get(C, mouse_co.region, &strip_key_owner);
1245
1246 /* If no key was found, the mouse cursor may still intersect with a "fake key" that has not been
1247 * realized yet. */
1248 if (strip_key_owner != nullptr && key == nullptr &&
1250 seq::retiming_data_is_editable(strip_key_owner))
1251 {
1252 key = try_to_realize_fake_keys(C, strip_key_owner, mouse_co.region);
1253 }
1254
1255 if (key != nullptr) {
1256 if (!was_retiming) {
1257 deselect_all_strips(scene);
1259 }
1260 /* Attempt to realize any other connected strips' fake keys. */
1261 if (seq::is_strip_connected(strip_key_owner)) {
1262 const int key_frame = seq::retiming_key_timeline_frame_get(scene, strip_key_owner, key);
1263 blender::VectorSet<Strip *> connections = seq::connected_strips_get(strip_key_owner);
1264 for (Strip *connection : connections) {
1265 if (key_frame == left_fake_key_frame_get(C, connection) ||
1266 key_frame == right_fake_key_frame_get(C, connection))
1267 {
1268 realize_fake_keys(scene, connection);
1269 }
1270 }
1271 }
1272 return sequencer_retiming_key_select_exec(C, op, key, strip_key_owner);
1273 }
1274
1275 /* We should only reach here if no retiming selection is happening. */
1276 if (was_retiming) {
1279 }
1280
1281 const bool extend = RNA_boolean_get(op->ptr, "extend");
1282 const bool deselect = RNA_boolean_get(op->ptr, "deselect");
1283 const bool deselect_all = RNA_boolean_get(op->ptr, "deselect_all");
1284 const bool toggle = RNA_boolean_get(op->ptr, "toggle");
1285 const bool center = RNA_boolean_get(op->ptr, "center");
1286
1287 StripSelection selection;
1288 if (region->regiontype == RGN_TYPE_PREVIEW) {
1289 selection.strip1 = strip_select_from_preview(C, mouse_co.region, toggle, extend, center);
1290 }
1291 else {
1292 selection = pick_strip_and_handle(scene, v2d, mouse_co.view);
1293 }
1294
1295 /* NOTE: `side_of_frame` and `linked_time` functionality is designed to be shared on one
1296 * keymap, therefore both properties can be true at the same time. */
1297 if (selection.strip1 && RNA_boolean_get(op->ptr, "linked_time")) {
1298 if (!extend && !toggle) {
1299 deselect_all_strips(scene);
1300 }
1301 select_linked_time(scene, selection, extend, deselect, toggle);
1303 sequencer_select_set_active(scene, selection.strip1);
1304 return OPERATOR_FINISHED;
1305 }
1306
1307 /* Select left, right or overlapping the current frame. */
1308 if (RNA_boolean_get(op->ptr, "side_of_frame")) {
1309 if (!extend && !toggle) {
1310 deselect_all_strips(scene);
1311 }
1312 sequencer_select_side_of_frame(C, v2d, mouse_co.region, scene);
1314 return OPERATOR_FINISHED;
1315 }
1316
1317 /* On Alt selection, select the strip and bordering handles. */
1318 if (selection.strip1 && RNA_boolean_get(op->ptr, "linked_handle")) {
1319 if (!extend && !toggle) {
1320 deselect_all_strips(scene);
1321 }
1322 sequencer_select_linked_handle(C, selection.strip1, selection.handle);
1324 sequencer_select_set_active(scene, selection.strip1);
1325 return OPERATOR_FINISHED;
1326 }
1327
1328 const bool wait_to_deselect_others = RNA_boolean_get(op->ptr, "wait_to_deselect_others");
1329 const bool already_selected = element_already_selected(selection);
1330
1331 SpaceSeq *sseq = CTX_wm_space_seq(C);
1332 if (selection.handle != STRIP_HANDLE_NONE && already_selected) {
1334 }
1335 else {
1337 }
1338 const bool ignore_connections = RNA_boolean_get(op->ptr, "ignore_connections");
1339
1340 /* Clicking on already selected element falls on modal operation.
1341 * All strips are deselected on mouse button release unless extend mode is used. */
1342 if (already_selected && wait_to_deselect_others && !toggle && !ignore_connections) {
1344 }
1345
1346 VectorSet<Strip *> copy_to;
1347 /* True if the user selects either handle of a strip that is already selected, meaning that
1348 * handles should be propagated to all currently selected strips. */
1349 bool copy_handles_to_sel = (U.sequencer_editor_flag & USER_SEQ_ED_SIMPLE_TWEAKING) &&
1350 (selection.handle != STRIP_HANDLE_NONE) &&
1351 (selection.strip1->flag & SELECT);
1352
1353 /* TODO(john): Dual handle propagation is not supported for now due to its complexity,
1354 * but once we simplify selection assumptions in 5.0 we can add support for it. */
1355 copy_handles_to_sel &= (selection.strip2 == nullptr);
1356
1357 if (copy_handles_to_sel) {
1359 copy_to.remove(selection.strip1);
1360 copy_to.remove_if([](Strip *strip) { return (strip->type & STRIP_TYPE_EFFECT); });
1361 }
1362
1363 bool changed = false;
1364 /* Deselect everything for now. NOTE that this condition runs for almost every click with no
1365 * modifiers. `sequencer_select_strip_impl` expects this and will re-select any strips in
1366 * `selection`. */
1367 if (deselect_all ||
1368 (selection.strip1 && (extend == false && deselect == false && toggle == false)))
1369 {
1370 changed |= deselect_all_strips(scene);
1371 }
1372
1373 /* Nothing to select, but strips might have been deselected, in which case we should update. */
1374 if (!selection.strip1) {
1375 if (changed) {
1377 }
1378 return changed ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
1379 }
1380
1381 /* Do actual selection. */
1382 sequencer_select_strip_impl(ed, selection.strip1, selection.handle, extend, deselect, toggle);
1383 if (selection.strip2 != nullptr) {
1384 /* Invert handle selection for second strip. */
1385 eStripHandle strip2_handle_clicked = (selection.handle == STRIP_HANDLE_LEFT) ?
1389 ed, selection.strip2, strip2_handle_clicked, extend, deselect, toggle);
1390 }
1391
1392 if (!ignore_connections) {
1393 if (copy_handles_to_sel) {
1394 sequencer_copy_handles_to_selected_strips(scene, selection, copy_to);
1395 }
1396
1398 }
1399
1401 sequencer_select_set_active(scene, selection.strip1);
1402 return OPERATOR_FINISHED;
1403}
1404
1406{
1407 const wmOperatorStatus retval = WM_generic_select_invoke(C, op, event);
1408 ARegion *region = CTX_wm_region(C);
1409 if (region && (region->regiontype == RGN_TYPE_PREVIEW)) {
1410 return WM_operator_flag_only_pass_through_on_press(retval, event);
1411 }
1412 return retval;
1413}
1414
1416{
1417 if (RNA_boolean_get(ptr, "ignore_connections")) {
1418 return CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Select (Unconnected)");
1419 }
1420 if (RNA_boolean_get(ptr, "linked_time")) {
1421 return CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Select (Linked Time)");
1422 }
1423 if (RNA_boolean_get(ptr, "linked_handle")) {
1424 return CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Select (Linked Handle)");
1425 }
1426 if (RNA_boolean_get(ptr, "side_of_frame")) {
1427 return CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Select (Side of Frame)");
1428 }
1429
1431}
1432
1434{
1435 PropertyRNA *prop;
1436
1437 /* Identifiers. */
1438 ot->name = "Select";
1439 ot->idname = "SEQUENCER_OT_select";
1440 ot->description = "Select a strip (last selected becomes the \"active strip\")";
1441
1442 /* API callbacks. */
1443 ot->exec = sequencer_select_exec;
1444 ot->invoke = sequencer_select_invoke;
1445 ot->modal = WM_generic_select_modal;
1447 ot->get_name = sequencer_select_get_name;
1448
1449 /* Flags. */
1450 ot->flag = OPTYPE_UNDO;
1451
1452 /* Properties. */
1454
1456
1457 prop = RNA_def_boolean(
1458 ot->srna,
1459 "center",
1460 false,
1461 "Center",
1462 "Use the object center when selecting, in edit mode used to extend object selection");
1464
1465 prop = RNA_def_boolean(ot->srna,
1466 "linked_handle",
1467 false,
1468 "Linked Handle",
1469 "Select handles next to the active strip");
1471
1472 prop = RNA_def_boolean(ot->srna,
1473 "linked_time",
1474 false,
1475 "Linked Time",
1476 "Select other strips or handles at the same time, or all retiming keys "
1477 "after the current in retiming mode");
1479
1480 prop = RNA_def_boolean(
1481 ot->srna,
1482 "side_of_frame",
1483 false,
1484 "Side of Frame",
1485 "Select all strips on same side of the current frame as the mouse cursor");
1487
1488 prop = RNA_def_boolean(ot->srna,
1489 "ignore_connections",
1490 false,
1491 "Ignore Connections",
1492 "Select strips individually whether or not they are connected");
1494}
1495
1497
1498/* -------------------------------------------------------------------- */
1501
1503{
1504 /* This operator is only used in the RCS keymap by default and is not exposed in any menus. */
1505 const View2D *v2d = UI_view2d_fromcontext(C);
1506 Scene *scene = CTX_data_scene(C);
1507 Editing *ed = seq::editing_get(scene);
1508
1509 if (ed == nullptr) {
1510 return OPERATOR_CANCELLED;
1511 }
1512
1513 if ((U.sequencer_editor_flag & USER_SEQ_ED_SIMPLE_TWEAKING) == 0) {
1515 }
1516
1517 MouseCoords mouse_co(v2d, RNA_int_get(op->ptr, "mouse_x"), RNA_int_get(op->ptr, "mouse_y"));
1518
1519 StripSelection selection = pick_strip_and_handle(scene, v2d, mouse_co.view);
1520 if (selection.strip1 == nullptr || selection.handle == STRIP_HANDLE_NONE) {
1522 }
1523
1524 /* Ignore clicks on retiming keys. */
1525 Strip *strip_key_test = nullptr;
1526 SeqRetimingKey *key = retiming_mouseover_key_get(C, mouse_co.region, &strip_key_test);
1527 if (key != nullptr) {
1529 }
1530
1531 SpaceSeq *sseq = CTX_wm_space_seq(C);
1532 if (element_already_selected(selection)) {
1535 }
1537 deselect_all_strips(scene);
1538
1539 /* Do actual selection. */
1540 sequencer_select_strip_impl(ed, selection.strip1, selection.handle, false, false, false);
1541 if (selection.strip2 != nullptr) {
1542 /* Invert handle selection for second strip */
1543 eStripHandle strip2_handle_clicked = (selection.handle == STRIP_HANDLE_LEFT) ?
1546 sequencer_select_strip_impl(ed, selection.strip2, strip2_handle_clicked, false, false, false);
1547 }
1548
1549 const bool ignore_connections = RNA_boolean_get(op->ptr, "ignore_connections");
1550 if (!ignore_connections) {
1552 }
1553
1556 sequencer_select_set_active(scene, selection.strip1);
1558}
1559
1561 wmOperator *op,
1562 const wmEvent *event)
1563{
1564 ARegion *region = CTX_wm_region(C);
1565
1566 int mval[2];
1567 WM_event_drag_start_mval(event, region, mval);
1568
1569 RNA_int_set(op->ptr, "mouse_x", mval[0]);
1570 RNA_int_set(op->ptr, "mouse_y", mval[1]);
1571
1572 return sequencer_select_handle_exec(C, op);
1573}
1574
1576{
1577 PropertyRNA *prop;
1578
1579 /* Identifiers. */
1580 ot->name = "Select Handle";
1581 ot->idname = "SEQUENCER_OT_select_handle";
1582 ot->description = "Select strip handle";
1583
1584 /* API callbacks. */
1588
1589 /* Flags. */
1590 ot->flag = OPTYPE_UNDO;
1591
1592 /* Properties. */
1594
1595 prop = RNA_def_boolean(ot->srna,
1596 "ignore_connections",
1597 false,
1598 "Ignore Connections",
1599 "Select strips individually whether or not they are connected");
1601}
1602
1604
1605/* -------------------------------------------------------------------- */
1608
1609/* Run recursively to select linked. */
1611{
1612 Editing *ed = seq::editing_get(scene);
1613
1614 if (ed == nullptr) {
1615 return false;
1616 }
1617
1618 bool changed = false;
1619
1621 if ((strip->flag & SELECT) == 0) {
1622 continue;
1623 }
1624 /* Only get unselected neighbors. */
1625 Strip *neighbor = find_neighboring_strip(scene, strip, seq::SIDE_LEFT, 0);
1626 if (neighbor) {
1627 neighbor->flag |= SELECT;
1628 recurs_sel_strip(neighbor);
1629 changed = true;
1630 }
1631 neighbor = find_neighboring_strip(scene, strip, seq::SIDE_RIGHT, 0);
1632 if (neighbor) {
1633 neighbor->flag |= SELECT;
1634 recurs_sel_strip(neighbor);
1635 changed = true;
1636 }
1637 }
1638
1639 return changed;
1640}
1641
1642/* Select only one linked strip on each side. */
1643static bool select_more_less_impl(Scene *scene, bool select_more)
1644{
1645 Editing *ed = seq::editing_get(scene);
1646
1647 if (ed == nullptr) {
1648 return false;
1649 }
1650
1651 GSet *neighbors = BLI_gset_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, "Linked strips");
1652 const int neighbor_selection_filter = select_more ? 0 : SELECT;
1653 const int selection_filter = select_more ? SELECT : 0;
1654
1656 if ((strip->flag & SELECT) != selection_filter) {
1657 continue;
1658 }
1659 Strip *neighbor = find_neighboring_strip(
1660 scene, strip, seq::SIDE_LEFT, neighbor_selection_filter);
1661 if (neighbor) {
1662 BLI_gset_add(neighbors, neighbor);
1663 }
1664 neighbor = find_neighboring_strip(scene, strip, seq::SIDE_RIGHT, neighbor_selection_filter);
1665 if (neighbor) {
1666 BLI_gset_add(neighbors, neighbor);
1667 }
1668 }
1669
1670 bool changed = false;
1671 GSetIterator gsi;
1672 BLI_gsetIterator_init(&gsi, neighbors);
1673 while (!BLI_gsetIterator_done(&gsi)) {
1674 Strip *neighbor = static_cast<Strip *>(BLI_gsetIterator_getKey(&gsi));
1675 if (select_more) {
1676 neighbor->flag |= SELECT;
1677 recurs_sel_strip(neighbor);
1678 }
1679 else {
1680 neighbor->flag &= ~SELECT;
1681 }
1682 changed = true;
1684 }
1685
1686 BLI_gset_free(neighbors, nullptr);
1687 return changed;
1688}
1689
1691{
1692 Scene *scene = CTX_data_scene(C);
1693
1694 if (!select_more_less_impl(scene, true)) {
1695 return OPERATOR_CANCELLED;
1696 }
1697
1699
1701
1702 return OPERATOR_FINISHED;
1703}
1704
1706{
1707 /* Identifiers. */
1708 ot->name = "Select More";
1709 ot->idname = "SEQUENCER_OT_select_more";
1710 ot->description = "Select more strips adjacent to the current selection";
1711
1712 /* API callbacks. */
1714 ot->poll = sequencer_edit_poll;
1715
1716 /* Flags. */
1717 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1718}
1719
1721
1722/* -------------------------------------------------------------------- */
1725
1727{
1728 Scene *scene = CTX_data_scene(C);
1729
1730 if (!select_more_less_impl(scene, false)) {
1731 return OPERATOR_CANCELLED;
1732 }
1733
1735
1737
1738 return OPERATOR_FINISHED;
1739}
1740
1742{
1743 /* Identifiers. */
1744 ot->name = "Select Less";
1745 ot->idname = "SEQUENCER_OT_select_less";
1746 ot->description = "Shrink the current selection of adjacent selected strips";
1747
1748 /* API callbacks. */
1750 ot->poll = sequencer_edit_poll;
1751
1752 /* Flags. */
1753 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1754}
1755
1757
1758/* -------------------------------------------------------------------- */
1761
1763 wmOperator *op,
1764 const wmEvent *event)
1765{
1766 Scene *scene = CTX_data_scene(C);
1767 const View2D *v2d = UI_view2d_fromcontext(C);
1768
1769 bool extend = RNA_boolean_get(op->ptr, "extend");
1770
1771 float mouse_co[2];
1772 UI_view2d_region_to_view(v2d, event->mval[0], event->mval[1], &mouse_co[0], &mouse_co[1]);
1773
1774 /* This works like UV, not mesh. */
1775 StripSelection mouse_selection = pick_strip_and_handle(scene, v2d, mouse_co);
1776 if (!mouse_selection.strip1) {
1777 return OPERATOR_FINISHED; /* User error as with mesh?? */
1778 }
1779
1780 if (extend == 0) {
1781 deselect_all_strips(scene);
1782 }
1783
1784 mouse_selection.strip1->flag |= SELECT;
1785 recurs_sel_strip(mouse_selection.strip1);
1786
1787 bool selected = true;
1788 while (selected) {
1789 selected = select_linked_internal(scene);
1790 }
1791
1793
1795
1796 return OPERATOR_FINISHED;
1797}
1798
1800{
1801 /* Identifiers. */
1802 ot->name = "Select Pick Linked";
1803 ot->idname = "SEQUENCER_OT_select_linked_pick";
1804 ot->description = "Select a chain of linked strips nearest to the mouse pointer";
1805
1806 /* API callbacks. */
1809
1810 /* Flags. */
1811 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1812
1813 /* Properties. */
1814 PropertyRNA *prop;
1815 prop = RNA_def_boolean(ot->srna, "extend", false, "Extend", "Extend the selection");
1817}
1818
1820
1821/* -------------------------------------------------------------------- */
1824
1826{
1827 Scene *scene = CTX_data_scene(C);
1828 bool selected;
1829
1830 selected = true;
1831 while (selected) {
1832 selected = select_linked_internal(scene);
1833 }
1834
1836
1838
1839 return OPERATOR_FINISHED;
1840}
1841
1843{
1844 /* Identifiers. */
1845 ot->name = "Select Linked";
1846 ot->idname = "SEQUENCER_OT_select_linked";
1847 ot->description = "Select all strips adjacent to the current selection";
1848
1849 /* API callbacks. */
1851 ot->poll = sequencer_edit_poll;
1852
1853 /* Flags. */
1854 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1855}
1856
1858
1859/* -------------------------------------------------------------------- */
1862
1863enum {
1870};
1871
1873 {SEQ_SELECT_HANDLES_SIDE_LEFT, "LEFT", 0, "Left", ""},
1874 {SEQ_SELECT_HANDLES_SIDE_RIGHT, "RIGHT", 0, "Right", ""},
1875 {SEQ_SELECT_HANDLES_SIDE_BOTH, "BOTH", 0, "Both", ""},
1876 {SEQ_SELECT_HANDLES_SIDE_LEFT_NEIGHBOR, "LEFT_NEIGHBOR", 0, "Left Neighbor", ""},
1877 {SEQ_SELECT_HANDLES_SIDE_RIGHT_NEIGHBOR, "RIGHT_NEIGHBOR", 0, "Right Neighbor", ""},
1878 {SEQ_SELECT_HANDLES_SIDE_BOTH_NEIGHBORS, "BOTH_NEIGHBORS", 0, "Both Neighbors", ""},
1879 {0, nullptr, 0, nullptr, nullptr},
1880};
1881
1883{
1884 Scene *scene = CTX_data_scene(C);
1885 Editing *ed = seq::editing_get(scene);
1886 int sel_side = RNA_enum_get(op->ptr, "side");
1887 LISTBASE_FOREACH (Strip *, strip, ed->seqbasep) {
1888 if (strip->flag & SELECT) {
1889 Strip *l_neighbor = find_neighboring_strip(scene, strip, seq::SIDE_LEFT, -1);
1890 Strip *r_neighbor = find_neighboring_strip(scene, strip, seq::SIDE_RIGHT, -1);
1891
1892 switch (sel_side) {
1894 strip->flag &= ~SEQ_RIGHTSEL;
1895 strip->flag |= SEQ_LEFTSEL;
1896 break;
1898 strip->flag &= ~SEQ_LEFTSEL;
1899 strip->flag |= SEQ_RIGHTSEL;
1900 break;
1902 strip->flag |= SEQ_LEFTSEL | SEQ_RIGHTSEL;
1903 break;
1905 if (l_neighbor) {
1906 if (!(l_neighbor->flag & SELECT)) {
1907 l_neighbor->flag |= SEQ_RIGHTSEL;
1908 }
1909 }
1910 break;
1912 if (r_neighbor) {
1913 if (!(r_neighbor->flag & SELECT)) {
1914 r_neighbor->flag |= SEQ_LEFTSEL;
1915 }
1916 }
1917 break;
1919 if (l_neighbor) {
1920 if (!(l_neighbor->flag & SELECT)) {
1921 l_neighbor->flag |= SEQ_RIGHTSEL;
1922 }
1923 }
1924 if (r_neighbor) {
1925 if (!(r_neighbor->flag & SELECT)) {
1926 r_neighbor->flag |= SEQ_LEFTSEL;
1927 }
1928 break;
1929 }
1930 }
1931 }
1932 }
1933 /* Select strips */
1934 LISTBASE_FOREACH (Strip *, strip, ed->seqbasep) {
1935 if ((strip->flag & SEQ_LEFTSEL) || (strip->flag & SEQ_RIGHTSEL)) {
1936 if (!(strip->flag & SELECT)) {
1937 strip->flag |= SELECT;
1938 recurs_sel_strip(strip);
1939 }
1940 }
1941 }
1942
1944
1946
1947 return OPERATOR_FINISHED;
1948}
1949
1951{
1952 /* Identifiers. */
1953 ot->name = "Select Handles";
1954 ot->idname = "SEQUENCER_OT_select_handles";
1955 ot->description = "Select gizmo handles on the sides of the selected strip";
1956
1957 /* API callbacks. */
1959 ot->poll = sequencer_edit_poll;
1960
1961 /* Flags. */
1962 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1963
1964 /* Properties. */
1965 RNA_def_enum(ot->srna,
1966 "side",
1969 "Side",
1970 "The side of the handle that is selected");
1971}
1972
1974
1975/* -------------------------------------------------------------------- */
1978
1980{
1981 Scene *scene = CTX_data_scene(C);
1982 Editing *ed = seq::editing_get(scene);
1983 const bool extend = RNA_boolean_get(op->ptr, "extend");
1984 const int side = RNA_enum_get(op->ptr, "side");
1985
1986 if (ed == nullptr) {
1987 return OPERATOR_CANCELLED;
1988 }
1989 if (extend == false) {
1990 deselect_all_strips(scene);
1991 }
1992 const int timeline_frame = scene->r.cfra;
1994 bool test = false;
1995 switch (side) {
1996 case -1:
1997 test = (timeline_frame >= seq::time_right_handle_frame_get(scene, strip));
1998 break;
1999 case 1:
2000 test = (timeline_frame <= seq::time_left_handle_frame_get(scene, strip));
2001 break;
2002 case 2:
2003 test = seq::time_strip_intersects_frame(scene, strip, timeline_frame);
2004 break;
2005 }
2006
2007 if (test) {
2008 strip->flag |= SELECT;
2009 recurs_sel_strip(strip);
2010 }
2011 }
2012
2014
2016
2017 return OPERATOR_FINISHED;
2018}
2019
2021{
2022 static const EnumPropertyItem sequencer_select_left_right_types[] = {
2023 {-1, "LEFT", 0, "Left", "Select to the left of the current frame"},
2024 {1, "RIGHT", 0, "Right", "Select to the right of the current frame"},
2025 {2, "CURRENT", 0, "Current Frame", "Select intersecting with the current frame"},
2026 {0, nullptr, 0, nullptr, nullptr},
2027 };
2028
2029 /* Identifiers. */
2030 ot->name = "Select Side of Frame";
2031 ot->idname = "SEQUENCER_OT_select_side_of_frame";
2032 ot->description = "Select strips relative to the current frame";
2033
2034 /* API callbacks. */
2037
2038 /* Flags. */
2039 ot->flag = OPTYPE_UNDO;
2040
2041 /* Properties. */
2042 PropertyRNA *prop;
2043 prop = RNA_def_boolean(ot->srna, "extend", false, "Extend", "Extend the selection");
2045 ot->prop = RNA_def_enum(ot->srna, "side", sequencer_select_left_right_types, 0, "Side", "");
2046}
2047
2049
2050/* -------------------------------------------------------------------- */
2053
2055{
2056 Scene *scene = CTX_data_scene(C);
2057 Editing *ed = seq::editing_get(scene);
2058
2059 const int sel_side = RNA_enum_get(op->ptr, "side");
2060 const int frame_init = sel_side == seq::SIDE_LEFT ? INT_MIN : INT_MAX;
2061 int frame_ranges[seq::MAX_CHANNELS];
2062 bool selected = false;
2063
2064 copy_vn_i(frame_ranges, ARRAY_SIZE(frame_ranges), frame_init);
2065
2066 LISTBASE_FOREACH (Strip *, strip, ed->seqbasep) {
2067 if (UNLIKELY(strip->channel >= seq::MAX_CHANNELS)) {
2068 continue;
2069 }
2070 int *frame_limit_p = &frame_ranges[strip->channel];
2071 if (strip->flag & SELECT) {
2072 selected = true;
2073 if (sel_side == seq::SIDE_LEFT) {
2074 *frame_limit_p = max_ii(*frame_limit_p, seq::time_left_handle_frame_get(scene, strip));
2075 }
2076 else {
2077 *frame_limit_p = min_ii(*frame_limit_p, seq::time_left_handle_frame_get(scene, strip));
2078 }
2079 }
2080 }
2081
2082 if (selected == false) {
2083 return OPERATOR_CANCELLED;
2084 }
2085
2086 select_active_side_range(scene, ed->seqbasep, sel_side, frame_ranges, frame_init);
2087
2089
2091
2092 return OPERATOR_FINISHED;
2093}
2094
2096{
2097 /* Identifiers. */
2098 ot->name = "Select Side";
2099 ot->idname = "SEQUENCER_OT_select_side";
2100 ot->description = "Select strips on the nominated side of the selected strips";
2101
2102 /* API callbacks. */
2104 ot->poll = sequencer_edit_poll;
2105
2106 /* Flags. */
2107 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2108
2109 /* Properties. */
2110 RNA_def_enum(ot->srna,
2111 "side",
2114 "Side",
2115 "The side to which the selection is applied");
2116}
2117
2119
2120/* -------------------------------------------------------------------- */
2123
2125 const Strip *strip,
2126 const rctf *rect)
2127{
2129 scene, strip);
2130 float rect_quad[4][2] = {{rect->xmax, rect->ymax},
2131 {rect->xmax, rect->ymin},
2132 {rect->xmin, rect->ymin},
2133 {rect->xmin, rect->ymax}};
2134
2135 return strip_point_image_isect(scene, strip, rect_quad[0]) ||
2136 strip_point_image_isect(scene, strip, rect_quad[1]) ||
2137 strip_point_image_isect(scene, strip, rect_quad[2]) ||
2138 strip_point_image_isect(scene, strip, rect_quad[3]) ||
2140 strip_image_quad[0], rect_quad[0], rect_quad[1], rect_quad[2], rect_quad[3]) ||
2142 strip_image_quad[1], rect_quad[0], rect_quad[1], rect_quad[2], rect_quad[3]) ||
2144 strip_image_quad[2], rect_quad[0], rect_quad[1], rect_quad[2], rect_quad[3]) ||
2146 strip_image_quad[3], rect_quad[0], rect_quad[1], rect_quad[2], rect_quad[3]);
2147}
2148
2150 const rctf *rect,
2151 const eSelectOp mode)
2152{
2153 Scene *scene = CTX_data_scene(C);
2154 Editing *ed = seq::editing_get(scene);
2157 SpaceSeq *sseq = CTX_wm_space_seq(C);
2158
2160 scene, channels, seqbase, scene->r.cfra, sseq->chanshown);
2161 for (Strip *strip : strips) {
2162 if (!strip_box_select_rect_image_isect(scene, strip, rect)) {
2163 continue;
2164 }
2165
2166 if (ELEM(mode, SEL_OP_ADD, SEL_OP_SET)) {
2167 strip->flag |= SELECT;
2168 }
2169 else {
2170 BLI_assert(mode == SEL_OP_SUB);
2171 strip->flag &= ~SELECT;
2172 }
2173 }
2174}
2175
2177{
2178 Scene *scene = CTX_data_scene(C);
2180 Editing *ed = seq::editing_get(scene);
2181
2182 if (ed == nullptr) {
2183 return OPERATOR_CANCELLED;
2184 }
2185
2187 {
2189 }
2190
2191 const eSelectOp sel_op = eSelectOp(RNA_enum_get(op->ptr, "mode"));
2192 const bool handles = RNA_boolean_get(op->ptr, "include_handles");
2193 const bool select = (sel_op != SEL_OP_SUB);
2194
2195 bool changed = false;
2196
2197 if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
2198 changed |= deselect_all_strips(scene);
2199 }
2200
2201 rctf rectf;
2203 UI_view2d_region_to_view_rctf(v2d, &rectf, &rectf);
2204
2205 ARegion *region = CTX_wm_region(C);
2206 if (region->regiontype == RGN_TYPE_PREVIEW) {
2208 return OPERATOR_CANCELLED;
2209 }
2210 seq_box_select_strip_from_preview(C, &rectf, sel_op);
2212 return OPERATOR_FINISHED;
2213 }
2214
2215 LISTBASE_FOREACH (Strip *, strip, ed->seqbasep) {
2216 rctf rq;
2217 strip_rectf(scene, strip, &rq);
2218 if (BLI_rctf_isect(&rq, &rectf, nullptr)) {
2219 if (handles) {
2220 /* Get the clickable handle size, ignoring padding. */
2221 float handsize = inner_clickable_handle_size_get(scene, strip, v2d) * 4;
2222
2223 /* Right handle. */
2224 if (rectf.xmax > (seq::time_right_handle_frame_get(scene, strip) - handsize)) {
2225 if (select) {
2226 strip->flag |= SELECT | SEQ_RIGHTSEL;
2227 }
2228 else {
2229 /* Deselect the strip if it's left with no handles selected. */
2230 if ((strip->flag & SEQ_RIGHTSEL) && ((strip->flag & SEQ_LEFTSEL) == 0)) {
2231 strip->flag &= ~SELECT;
2232 }
2233 strip->flag &= ~SEQ_RIGHTSEL;
2234 }
2235
2236 changed = true;
2237 }
2238 /* Left handle. */
2239 if (rectf.xmin < (seq::time_left_handle_frame_get(scene, strip) + handsize)) {
2240 if (select) {
2241 strip->flag |= SELECT | SEQ_LEFTSEL;
2242 }
2243 else {
2244 /* Deselect the strip if it's left with no handles selected. */
2245 if ((strip->flag & SEQ_LEFTSEL) && ((strip->flag & SEQ_RIGHTSEL) == 0)) {
2246 strip->flag &= ~SELECT;
2247 }
2248 strip->flag &= ~SEQ_LEFTSEL;
2249 }
2250 }
2251
2252 changed = true;
2253 }
2254
2255 /* Regular box selection. */
2256 else {
2258 strip->flag &= ~(SEQ_LEFTSEL | SEQ_RIGHTSEL);
2259 changed = true;
2260 }
2261
2262 const bool ignore_connections = RNA_boolean_get(op->ptr, "ignore_connections");
2263 if (!ignore_connections) {
2264 /* Propagate selection to connected strips. */
2265 StripSelection selection;
2266 selection.strip1 = strip;
2268 }
2269 }
2270 }
2271
2272 if (!changed) {
2273 return OPERATOR_CANCELLED;
2274 }
2275
2277
2278 return OPERATOR_FINISHED;
2279}
2280
2282 wmOperator *op,
2283 const wmEvent *event)
2284{
2285 Scene *scene = CTX_data_scene(C);
2286 const View2D *v2d = UI_view2d_fromcontext(C);
2287 ARegion *region = CTX_wm_region(C);
2288
2290 return OPERATOR_CANCELLED;
2291 }
2292
2293 const bool tweak = RNA_boolean_get(op->ptr, "tweak");
2294
2295 if (tweak) {
2296 int mval[2];
2297 float mouse_co[2];
2298 WM_event_drag_start_mval(event, region, mval);
2299 UI_view2d_region_to_view(v2d, mval[0], mval[1], &mouse_co[0], &mouse_co[1]);
2300
2301 StripSelection selection = pick_strip_and_handle(scene, v2d, mouse_co);
2302
2303 if (selection.strip1 != nullptr) {
2305 }
2306 }
2307
2308 return WM_gesture_box_invoke(C, op, event);
2309}
2310
2312{
2313 PropertyRNA *prop;
2314
2315 /* Identifiers. */
2316 ot->name = "Box Select";
2317 ot->idname = "SEQUENCER_OT_select_box";
2318 ot->description = "Select strips using box selection";
2319
2320 /* API callbacks. */
2323 ot->modal = WM_gesture_box_modal;
2324 ot->cancel = WM_gesture_box_cancel;
2325
2327
2328 /* Flags. */
2329 ot->flag = OPTYPE_UNDO;
2330
2331 /* Properties. */
2334
2335 prop = RNA_def_boolean(
2336 ot->srna,
2337 "tweak",
2338 false,
2339 "Tweak",
2340 "Make box select pass through to sequence slide when the cursor is hovering on a strip");
2342
2343 prop = RNA_def_boolean(
2344 ot->srna, "include_handles", false, "Select Handles", "Select the strips and their handles");
2346
2347 prop = RNA_def_boolean(ot->srna,
2348 "ignore_connections",
2349 false,
2350 "Ignore Connections",
2351 "Select strips individually whether or not they are connected");
2353}
2354
2356
2357/* -------------------------------------------------------------------- */
2360
2361enum {
2369};
2370
2372 {SEQ_SELECT_GROUP_TYPE, "TYPE", 0, "Type", "Shared strip type"},
2374 "TYPE_BASIC",
2375 0,
2376 "Global Type",
2377 "All strips of same basic type (graphical or sound)"},
2379 "TYPE_EFFECT",
2380 0,
2381 "Effect Type",
2382 "Shared strip effect type (if active strip is not an effect one, select all non-effect "
2383 "strips)"},
2384 {SEQ_SELECT_GROUP_DATA, "DATA", 0, "Data", "Shared data (scene, image, sound, etc.)"},
2385 {SEQ_SELECT_GROUP_EFFECT, "EFFECT", 0, "Effect", "Shared effects"},
2387 "EFFECT_LINK",
2388 0,
2389 "Effect/Linked",
2390 "Other strips affected by the active one (sharing some time, and below or "
2391 "effect-assigned)"},
2392 {SEQ_SELECT_GROUP_OVERLAP, "OVERLAP", 0, "Overlap", "Overlapping time"},
2393 {0, nullptr, 0, nullptr, nullptr},
2394};
2395
2396#define STRIP_IS_SOUND(_strip) \
2397 ((_strip->type & STRIP_TYPE_SOUND_RAM) && !(_strip->type & STRIP_TYPE_EFFECT))
2398
2399#define STRIP_IS_EFFECT(_strip) ((_strip->type & STRIP_TYPE_EFFECT) != 0)
2400
2401#define STRIP_USE_DATA(_strip) \
2402 (ELEM(_strip->type, STRIP_TYPE_SCENE, STRIP_TYPE_MOVIECLIP, STRIP_TYPE_MASK) || \
2403 STRIP_HAS_PATH(_strip))
2404
2405#define STRIP_CHANNEL_CHECK(_strip, _chan) ELEM((_chan), 0, (_strip)->channel)
2406
2408 ListBase * /*seqbase*/,
2409 Strip *act_strip,
2410 const int channel)
2411{
2412 bool changed = false;
2413
2414 for (Strip *strip : strips) {
2415 if (STRIP_CHANNEL_CHECK(strip, channel) && strip->type == act_strip->type) {
2416 strip->flag |= SELECT;
2417 changed = true;
2418 }
2419 }
2420
2421 return changed;
2422}
2423
2425 ListBase * /*seqbase*/,
2426 Strip *act_strip,
2427 const int channel)
2428{
2429 bool changed = false;
2430 const bool is_sound = STRIP_IS_SOUND(act_strip);
2431
2432 for (Strip *strip : strips) {
2433 if (STRIP_CHANNEL_CHECK(strip, channel) &&
2434 (is_sound ? STRIP_IS_SOUND(strip) : !STRIP_IS_SOUND(strip)))
2435 {
2436 strip->flag |= SELECT;
2437 changed = true;
2438 }
2439 }
2440
2441 return changed;
2442}
2443
2445 ListBase * /*seqbase*/,
2446 Strip *act_strip,
2447 const int channel)
2448{
2449 bool changed = false;
2450 const bool is_effect = STRIP_IS_EFFECT(act_strip);
2451
2452 for (Strip *strip : strips) {
2453 if (STRIP_CHANNEL_CHECK(strip, channel) &&
2454 (is_effect ? STRIP_IS_EFFECT(strip) : !STRIP_IS_EFFECT(strip)))
2455 {
2456 strip->flag |= SELECT;
2457 changed = true;
2458 }
2459 }
2460
2461 return changed;
2462}
2463
2465 ListBase * /*seqbase*/,
2466 Strip *act_strip,
2467 const int channel)
2468{
2469 bool changed = false;
2470 const char *dirpath = act_strip->data ? act_strip->data->dirpath : nullptr;
2471
2472 if (!STRIP_USE_DATA(act_strip)) {
2473 return changed;
2474 }
2475
2476 if (STRIP_HAS_PATH(act_strip) && dirpath) {
2477 for (Strip *strip : strips) {
2478 if (STRIP_CHANNEL_CHECK(strip, channel) && STRIP_HAS_PATH(strip) && strip->data &&
2479 STREQ(strip->data->dirpath, dirpath))
2480 {
2481 strip->flag |= SELECT;
2482 changed = true;
2483 }
2484 }
2485 }
2486 else if (act_strip->type == STRIP_TYPE_SCENE) {
2487 Scene *sce = act_strip->scene;
2488 for (Strip *strip : strips) {
2489 if (STRIP_CHANNEL_CHECK(strip, channel) && strip->type == STRIP_TYPE_SCENE &&
2490 strip->scene == sce)
2491 {
2492 strip->flag |= SELECT;
2493 changed = true;
2494 }
2495 }
2496 }
2497 else if (act_strip->type == STRIP_TYPE_MOVIECLIP) {
2498 MovieClip *clip = act_strip->clip;
2499 for (Strip *strip : strips) {
2500 if (STRIP_CHANNEL_CHECK(strip, channel) && strip->type == STRIP_TYPE_MOVIECLIP &&
2501 strip->clip == clip)
2502 {
2503 strip->flag |= SELECT;
2504 changed = true;
2505 }
2506 }
2507 }
2508 else if (act_strip->type == STRIP_TYPE_MASK) {
2509 Mask *mask = act_strip->mask;
2510 for (Strip *strip : strips) {
2511 if (STRIP_CHANNEL_CHECK(strip, channel) && strip->type == STRIP_TYPE_MASK &&
2512 strip->mask == mask)
2513 {
2514 strip->flag |= SELECT;
2515 changed = true;
2516 }
2517 }
2518 }
2519
2520 return changed;
2521}
2522
2524 ListBase * /*seqbase*/,
2525 Strip *act_strip,
2526 const int channel)
2527{
2528 bool changed = false;
2529 bool effects[STRIP_TYPE_MAX + 1];
2530
2531 for (int i = 0; i <= STRIP_TYPE_MAX; i++) {
2532 effects[i] = false;
2533 }
2534
2535 for (Strip *strip : strips) {
2536 if (STRIP_CHANNEL_CHECK(strip, channel) && (strip->type & STRIP_TYPE_EFFECT) &&
2537 seq::relation_is_effect_of_strip(strip, act_strip))
2538 {
2539 effects[strip->type] = true;
2540 }
2541 }
2542
2543 for (Strip *strip : strips) {
2544 if (STRIP_CHANNEL_CHECK(strip, channel) && effects[strip->type]) {
2545 if (strip->input1) {
2546 strip->input1->flag |= SELECT;
2547 }
2548 if (strip->input2) {
2549 strip->input2->flag |= SELECT;
2550 }
2551 changed = true;
2552 }
2553 }
2554
2555 return changed;
2556}
2557
2558static bool select_grouped_time_overlap(const Scene *scene,
2560 ListBase * /*seqbase*/,
2561 Strip *act_strip)
2562{
2563 bool changed = false;
2564
2565 for (Strip *strip : strips) {
2566 if (seq::time_left_handle_frame_get(scene, strip) <
2567 seq::time_right_handle_frame_get(scene, act_strip) &&
2568 seq::time_right_handle_frame_get(scene, strip) >
2569 seq::time_left_handle_frame_get(scene, act_strip))
2570 {
2571 strip->flag |= SELECT;
2572 changed = true;
2573 }
2574 }
2575
2576 return changed;
2577}
2578
2579/* Query strips that are in lower channel and intersect in time with strip_reference. */
2580static void query_lower_channel_strips(const Scene *scene,
2581 Strip *strip_reference,
2582 ListBase *seqbase,
2584{
2585 LISTBASE_FOREACH (Strip *, strip_test, seqbase) {
2586 if (strip_test->channel > strip_reference->channel) {
2587 continue; /* Not lower channel. */
2588 }
2589 if (seq::time_right_handle_frame_get(scene, strip_test) <=
2590 seq::time_left_handle_frame_get(scene, strip_reference) ||
2591 seq::time_left_handle_frame_get(scene, strip_test) >=
2592 seq::time_right_handle_frame_get(scene, strip_reference))
2593 {
2594 continue; /* Not intersecting in time. */
2595 }
2596 strips.add(strip_test);
2597 }
2598}
2599
2600/* Select all strips within time range and with lower channel of initial selection. Then select
2601 * effect chains of these strips. */
2602static bool select_grouped_effect_link(const Scene *scene,
2604 ListBase *seqbase,
2605 Strip * /*act_strip*/,
2606 const int /*channel*/)
2607{
2608 /* Get collection of strips. */
2609 strips.remove_if([&](Strip *strip) { return (strip->flag & SELECT) == 0; });
2610 const int selected_strip_count = strips.size();
2611 /* XXX: this uses scene as arg, so it does not work with iterator :( I had thought about this,
2612 * but expand function is just so useful... I can just add scene and inject it I guess. */
2615
2616 /* Check if other strips will be affected. */
2617 const bool changed = strips.size() > selected_strip_count;
2618
2619 /* Actual logic. */
2620 for (Strip *strip : strips) {
2621 strip->flag |= SELECT;
2622 }
2623
2624 return changed;
2625}
2626
2627#undef STRIP_IS_SOUND
2628#undef STRIP_IS_EFFECT
2629#undef STRIP_USE_DATA
2630
2632{
2633 Scene *scene = CTX_data_scene(C);
2635 Strip *act_strip = seq::select_active_get(scene);
2636
2637 const bool is_preview = sequencer_view_has_preview_poll(C);
2638 if (is_preview && !sequencer_view_preview_only_poll(C)) {
2639 return OPERATOR_CANCELLED;
2640 }
2641
2643
2644 if (act_strip == nullptr || (is_preview && !strips.contains(act_strip))) {
2645 BKE_report(op->reports, RPT_ERROR, "No active strip!");
2646 return OPERATOR_CANCELLED;
2647 }
2648
2649 const int type = RNA_enum_get(op->ptr, "type");
2650 const int channel = RNA_boolean_get(op->ptr, "use_active_channel") ? act_strip->channel : 0;
2651 const bool extend = RNA_boolean_get(op->ptr, "extend");
2652
2653 bool changed = false;
2654
2655 if (!extend) {
2656 LISTBASE_FOREACH (Strip *, strip, seqbase) {
2657 strip->flag &= ~SELECT;
2658 changed = true;
2659 }
2660 }
2661
2662 switch (type) {
2664 changed |= select_grouped_type(strips, seqbase, act_strip, channel);
2665 break;
2667 changed |= select_grouped_type_basic(strips, seqbase, act_strip, channel);
2668 break;
2670 changed |= select_grouped_type_effect(strips, seqbase, act_strip, channel);
2671 break;
2673 changed |= select_grouped_data(strips, seqbase, act_strip, channel);
2674 break;
2676 changed |= select_grouped_effect(strips, seqbase, act_strip, channel);
2677 break;
2679 changed |= select_grouped_effect_link(scene, strips, seqbase, act_strip, channel);
2680 break;
2682 changed |= select_grouped_time_overlap(scene, strips, seqbase, act_strip);
2683 break;
2684 default:
2685 BLI_assert(0);
2686 break;
2687 }
2688
2689 if (changed) {
2692 return OPERATOR_FINISHED;
2693 }
2694
2695 return OPERATOR_CANCELLED;
2696}
2697
2699{
2700 /* Identifiers. */
2701 ot->name = "Select Grouped";
2702 ot->idname = "SEQUENCER_OT_select_grouped";
2703 ot->description = "Select all strips grouped by various properties";
2704
2705 /* API callbacks. */
2706 ot->invoke = WM_menu_invoke;
2708 ot->poll = sequencer_edit_poll;
2709
2710 /* Flags. */
2711 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2712
2713 /* Properties. */
2714 ot->prop = RNA_def_enum(ot->srna, "type", sequencer_prop_select_grouped_types, 0, "Type", "");
2715 RNA_def_boolean(ot->srna,
2716 "extend",
2717 false,
2718 "Extend",
2719 "Extend selection instead of deselecting everything first");
2720 RNA_def_boolean(ot->srna,
2721 "use_active_channel",
2722 false,
2723 "Same Channel",
2724 "Only consider strips on the same channel as the active one");
2725}
2726
2728
2729} // namespace blender::ed::vse
Scene * CTX_data_scene(const bContext *C)
SpaceSeq * CTX_wm_space_seq(const bContext *C)
ARegion * CTX_wm_region(const bContext *C)
void BKE_report(ReportList *reports, eReportType type, const char *message)
Definition report.cc:126
#define BLI_assert(a)
Definition BLI_assert.h:46
struct GSet GSet
Definition BLI_ghash.h:337
unsigned int BLI_ghashutil_ptrhash(const void *key)
BLI_INLINE bool BLI_gsetIterator_done(const GSetIterator *gsi)
Definition BLI_ghash.h:463
BLI_INLINE void * BLI_gsetIterator_getKey(GSetIterator *gsi)
Definition BLI_ghash.h:455
GSet * BLI_gset_new(GSetHashFP hashfp, GSetCmpFP cmpfp, const char *info) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT
Definition BLI_ghash.cc:944
bool BLI_ghashutil_ptrcmp(const void *a, const void *b)
BLI_INLINE void BLI_gsetIterator_init(GSetIterator *gsi, GSet *gs)
Definition BLI_ghash.h:447
void BLI_gset_free(GSet *gs, GSetKeyFreeFP keyfreefp)
BLI_INLINE void BLI_gsetIterator_step(GSetIterator *gsi)
Definition BLI_ghash.h:459
bool BLI_gset_add(GSet *gs, void *key)
Definition BLI_ghash.cc:966
#define LISTBASE_FOREACH(type, var, list)
void void BLI_freelistN(ListBase *listbase) ATTR_NONNULL(1)
Definition listbase.cc:497
void BLI_addtail(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:111
void void BLI_listbase_sort(ListBase *listbase, int(*cmp)(const void *, const void *)) ATTR_NONNULL(1
MINLINE int min_ii(int a, int b)
MINLINE float min_ff(float a, float b)
MINLINE int max_ii(int a, int b)
MINLINE float square_f(float a)
int isect_point_quad_v2(const float p[2], const float v1[2], const float v2[2], const float v3[2], const float v4[2])
MINLINE float len_squared_v2(const float v[2]) ATTR_WARN_UNUSED_RESULT
MINLINE void sub_v2_v2(float r[2], const float a[2])
MINLINE void mul_v2_v2(float r[2], const float a[2])
void copy_vn_i(int *array_tar, int size, int val)
#define FILE_MAXDIR
void BLI_rctf_union(struct rctf *rct_a, const struct rctf *rct_b)
bool BLI_rctf_isect_pt_v(const struct rctf *rect, const float xy[2])
bool BLI_rctf_isect(const struct rctf *src1, const struct rctf *src2, struct rctf *dest)
float BLI_rctf_length_x(const rctf *rect, float x)
Definition rct.cc:171
void BLI_rctf_pad(struct rctf *rect, float pad_x, float pad_y)
Definition rct.cc:637
char * BLI_strncpy(char *__restrict dst, const char *__restrict src, size_t dst_maxncpy) ATTR_NONNULL(1
#define ARRAY_SIZE(arr)
#define SET_FLAG_FROM_TEST(value, test, flag)
#define UNLIKELY(x)
#define ELEM(...)
#define STREQ(a, b)
#define CTX_IFACE_(context, msgid)
#define BLT_I18NCONTEXT_OPERATOR_DEFAULT
@ RGN_TYPE_PREVIEW
#define STRIP_OFSBOTTOM
@ SEQ_RIGHTSEL
@ SEQ_LEFTSEL
#define STRIP_OFSTOP
@ STRIP_TYPE_SCENE
@ STRIP_TYPE_MOVIECLIP
@ STRIP_TYPE_SOUND_RAM
@ STRIP_TYPE_MAX
@ STRIP_TYPE_IMAGE
@ STRIP_TYPE_MOVIE
@ STRIP_TYPE_EFFECT
@ STRIP_TYPE_MASK
#define STRIP_ALLSEL
#define STRIP_HAS_PATH(_strip)
@ SEQ_DRAW_IMG_IMBUF
@ SPACE_SEQ_DESELECT_STRIP_HANDLE
@ SEQ_MARKER_TRANS
@ USER_SEQ_ED_SIMPLE_TWEAKING
@ OPERATOR_CANCELLED
@ OPERATOR_FINISHED
@ OPERATOR_RUNNING_MODAL
@ OPERATOR_PASS_THROUGH
void ED_outliner_select_sync_from_sequence_tag(const bContext *C)
bool ED_operator_sequencer_active(bContext *C)
eSelectOp
@ SEL_OP_ADD
@ SEL_OP_SUB
@ SEL_OP_SET
@ SEL_SELECT
@ SEL_INVERT
@ SEL_DESELECT
@ SEL_TOGGLE
#define SEL_OP_USE_PRE_DESELECT(sel_op)
std::string ED_select_pick_get_name(wmOperatorType *ot, PointerRNA *ptr)
Read Guarded memory(de)allocation.
@ PROP_SKIP_SAVE
Definition RNA_types.hh:330
#define C
Definition RandGen.cpp:29
#define UI_TIME_SCRUB_MARGIN_Y
Definition UI_view2d.hh:471
View2D * UI_view2d_fromcontext(const bContext *C)
Definition view2d.cc:1854
float UI_view2d_scale_get_y(const View2D *v2d)
Definition view2d.cc:1924
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_region_to_view_x(const View2D *v2d, float x)
Definition view2d.cc:1656
void UI_view2d_region_to_view_rctf(const View2D *v2d, const rctf *rect_src, rctf *rect_dst) ATTR_NONNULL()
Definition view2d.cc:1674
float UI_view2d_scale_get_x(const View2D *v2d)
Definition view2d.cc:1920
#define ND_SEQUENCER
Definition WM_types.hh:434
#define NC_SCENE
Definition WM_types.hh:375
@ OPTYPE_UNDO
Definition WM_types.hh:182
@ OPTYPE_REGISTER
Definition WM_types.hh:180
#define NA_SELECTED
Definition WM_types.hh:586
#define U
bool remove(const Key &key)
bool add(const Key &key)
int64_t size() const
bool contains(const Key &key) const
int64_t remove_if(Predicate &&predicate)
int64_t size() const
void append(const T &value)
MouseCoords(const View2D *v2d, int x, int y)
#define SELECT
#define sqrtf(x)
#define select(A, B, C)
void * MEM_callocN(size_t len, const char *str)
Definition mallocn.cc:118
ccl_device_inline float2 mask(const MaskType mask, const float2 a)
static int left
static bool select_grouped_type_basic(blender::Span< Strip * > strips, ListBase *, Strip *act_strip, const int channel)
static void select_active_side(const Scene *scene, ListBase *seqbase, int sel_side, int channel, int frame)
bool sequencer_edit_poll(bContext *C)
static wmOperatorStatus sequencer_select_linked_pick_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static void sequencer_copy_handles_to_selected_strips(const Scene *scene, const StripSelection &selection, VectorSet< Strip * > copy_to)
static void sequencer_select_linked_handle(const bContext *C, Strip *strip, const eStripHandle handle_clicked)
static Strip * strip_select_from_preview(const bContext *C, const int mval[2], const bool toggle, const bool extend, const bool center)
static void sequencer_select_side_of_frame(const bContext *C, const View2D *v2d, const int mval[2], Scene *scene)
static bool select_grouped_effect(blender::Span< Strip * > strips, ListBase *, Strip *act_strip, const int channel)
static bool element_already_selected(const StripSelection &selection)
static wmOperatorStatus sequencer_select_side_of_frame_exec(bContext *C, wmOperator *op)
void SEQUENCER_OT_select_linked_pick(wmOperatorType *ot)
static std::string sequencer_select_get_name(wmOperatorType *ot, PointerRNA *ptr)
static wmOperatorStatus sequencer_select_more_exec(bContext *C, wmOperator *)
static float inner_clickable_handle_size_get(const Scene *scene, const Strip *strip, const View2D *v2d)
static bool select_more_less_impl(Scene *scene, bool select_more)
void SEQUENCER_OT_select_all(wmOperatorType *ot)
SeqRetimingKey * retiming_mouseover_key_get(const bContext *C, const int mval[2], Strip **r_strip)
int right_fake_key_frame_get(const bContext *C, const Strip *strip)
StripSelection pick_strip_and_handle(const struct Scene *scene, const View2D *v2d, float mouse_co[2])
static void sequencer_select_connected_strips(const StripSelection &selection)
void SEQUENCER_OT_select_box(wmOperatorType *ot)
static bool is_mouse_over_both_handles_of_adjacent_strips(const Scene *scene, blender::Vector< Strip * > strips, const View2D *v2d, float mouse_co[2])
void SEQUENCER_OT_select_inverse(wmOperatorType *ot)
static void select_surrounding_handles(Scene *scene, Strip *test)
static bool strips_are_adjacent(const Scene *scene, const Strip *strip1, const Strip *strip2)
bool handle_is_selected(const Strip *strip, eStripHandle handle)
void SEQUENCER_OT_select_side_of_frame(wmOperatorType *ot)
void select_strip_single(Scene *scene, Strip *strip, bool deselect_all)
static bool is_sound(wmDrag *drag)
void SEQUENCER_OT_select_grouped(wmOperatorType *ot)
void recurs_sel_strip(Strip *strip_meta)
static void seq_box_select_strip_from_preview(const bContext *C, const rctf *rect, const eSelectOp mode)
static int strip_sort_for_depth_select(const void *a, const void *b)
blender::VectorSet< Strip * > all_strips_from_context(bContext *C)
blender::VectorSet< Strip * > selected_strips_from_context(bContext *C)
wmOperatorStatus sequencer_select_exec(bContext *C, wmOperator *op)
void SEQUENCER_OT_select(wmOperatorType *ot)
Strip * find_neighboring_strip(const Scene *scene, const Strip *test, const int lr, int sel)
void SEQUENCER_OT_select_side(wmOperatorType *ot)
static wmOperatorStatus sequencer_select_inverse_exec(bContext *C, wmOperator *)
static wmOperatorStatus sequencer_select_invoke(bContext *C, wmOperator *op, const wmEvent *event)
void SEQUENCER_OT_select_less(wmOperatorType *ot)
static bool select_grouped_type_effect(blender::Span< Strip * > strips, ListBase *, Strip *act_strip, const int channel)
static void select_linked_time_strip(const Scene *scene, const Strip *strip_source, const eStripHandle handle_clicked)
void SEQUENCER_OT_select_more(wmOperatorType *ot)
static void query_lower_channel_strips(const Scene *scene, Strip *strip_reference, ListBase *seqbase, blender::VectorSet< Strip * > &strips)
static void strip_clickable_areas_get(const Scene *scene, const Strip *strip, const View2D *v2d, rctf *r_body, rctf *r_left_handle, rctf *r_right_handle)
static wmOperatorStatus sequencer_select_linked_exec(bContext *C, wmOperator *)
wmOperatorStatus sequencer_retiming_box_select_exec(bContext *C, wmOperator *op)
void SEQUENCER_OT_select_handles(wmOperatorType *ot)
static wmOperatorStatus sequencer_select_handle_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static float strip_to_frame_distance(const Scene *scene, const View2D *v2d, const Strip *strip, float timeline_frame)
SeqRetimingKey * try_to_realize_fake_keys(const bContext *C, Strip *strip, const int mval[2])
static int strip_sort_for_center_select(const void *a, const void *b)
static const EnumPropertyItem prop_select_handles_side_types[]
Strip * strip_under_mouse_get(const Scene *scene, const View2D *v2d, const int mval[2])
void sequencer_select_do_updates(const bContext *C, Scene *scene)
bool can_select_handle(const Scene *scene, const Strip *strip, const View2D *v2d)
void strip_rectf(const Scene *scene, const Strip *strip, rctf *r_rect)
static eStripHandle strip_handle_under_cursor_get(const Scene *scene, const Strip *strip, const View2D *v2d, float mouse_co[2])
bool sequencer_view_preview_only_poll(const bContext *C)
static void select_active_side_range(const Scene *scene, ListBase *seqbase, const int sel_side, const int frame_ranges[seq::MAX_CHANNELS], const int frame_ignore)
bool retiming_keys_can_be_displayed(const SpaceSeq *sseq)
void SEQUENCER_OT_select_handle(wmOperatorType *ot)
static const EnumPropertyItem sequencer_prop_select_grouped_types[]
void realize_fake_keys(const Scene *scene, Strip *strip)
bool deselect_all_strips(const Scene *scene)
static void select_linked_time(const Scene *scene, const StripSelection &selection, const bool extend, const bool deselect, const bool toggle)
bool sequencer_view_has_preview_poll(bContext *C)
wmOperatorStatus sequencer_retiming_select_all_exec(bContext *C, wmOperator *op)
static wmOperatorStatus sequencer_select_side_exec(bContext *C, wmOperator *op)
int left_fake_key_frame_get(const bContext *C, const Strip *strip)
static wmOperatorStatus sequencer_select_handles_exec(bContext *C, wmOperator *op)
static blender::Vector< Strip * > padded_strips_under_mouse_get(const Scene *scene, const View2D *v2d, float mouse_co[2])
static rctf strip_clickable_area_get(const Scene *scene, const View2D *v2d, const Strip *strip)
static wmOperatorStatus sequencer_select_handle_exec(bContext *C, wmOperator *op)
const EnumPropertyItem prop_side_types[]
void SEQUENCER_OT_select_linked(wmOperatorType *ot)
static wmOperatorStatus sequencer_select_grouped_exec(bContext *C, wmOperator *op)
static wmOperatorStatus sequencer_de_select_all_exec(bContext *C, wmOperator *op)
static wmOperatorStatus sequencer_select_less_exec(bContext *C, wmOperator *)
bool strip_point_image_isect(const Scene *scene, const Strip *strip, float point_view[2])
static bool select_linked_internal(Scene *scene)
static wmOperatorStatus sequencer_box_select_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static void sequencer_select_set_active(Scene *scene, Strip *strip)
static void sequencer_select_strip_impl(const Editing *ed, Strip *strip, const eStripHandle handle_clicked, const bool extend, const bool deselect, const bool toggle)
blender::Vector< Strip * > sequencer_visible_strips_get(const bContext *C)
static wmOperatorStatus sequencer_box_select_exec(bContext *C, wmOperator *op)
static bool select_grouped_effect_link(const Scene *scene, blender::VectorSet< Strip * > strips, ListBase *seqbase, Strip *, const int)
static bool select_grouped_time_overlap(const Scene *scene, blender::Span< Strip * > strips, ListBase *, Strip *act_strip)
static bool select_grouped_data(blender::Span< Strip * > strips, ListBase *, Strip *act_strip, const int channel)
static bool strip_box_select_rect_image_isect(const Scene *scene, const Strip *strip, const rctf *rect)
bool sequencer_retiming_mode_is_active(const bContext *C)
wmOperatorStatus sequencer_retiming_key_select_exec(bContext *C, wmOperator *op, SeqRetimingKey *key, const Strip *key_owner)
static bool select_grouped_type(blender::Span< Strip * > strips, ListBase *, Strip *act_strip, const int channel)
int time_right_handle_frame_get(const Scene *scene, const Strip *strip)
VectorSet< Strip * > query_all_strips(ListBase *seqbase)
Definition iterator.cc:118
Array< float2 > image_transform_final_quad_get(const Scene *scene, const Strip *strip)
ListBase * channels_displayed_get(const Editing *ed)
Definition channels.cc:28
VectorSet< Strip * > query_selected_strips(ListBase *seqbase)
Definition iterator.cc:127
int retiming_key_timeline_frame_get(const Scene *scene, const Strip *strip, const SeqRetimingKey *key)
Editing * editing_get(const Scene *scene)
Definition sequencer.cc:272
int time_left_handle_frame_get(const Scene *, const Strip *strip)
void query_strip_effect_chain(const Scene *scene, Strip *reference_strip, ListBase *seqbase, VectorSet< Strip * > &r_strips)
Definition iterator.cc:231
blender::VectorSet< Strip * > connected_strips_get(const Strip *strip)
Strip * select_active_get(const Scene *scene)
constexpr int MAX_CHANNELS
bool is_strip_connected(const Strip *strip)
bool retiming_data_is_editable(const Strip *strip)
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 time_strip_intersects_frame(const Scene *scene, const Strip *strip, const int timeline_frame)
float2 image_transform_origin_offset_pixelspace_get(const Scene *scene, const Strip *strip)
bool retiming_selection_clear(const Editing *ed)
void select_active_set(Scene *scene, Strip *strip)
bool transform_is_locked(ListBase *channels, const Strip *strip)
bool relation_is_effect_of_strip(const Strip *effect, const Strip *input)
VectorSet< Strip * > query_rendered_strips(const Scene *scene, ListBase *channels, ListBase *seqbase, const int timeline_frame, const int displayed_channel)
Definition iterator.cc:205
ListBase * active_seqbase_get(const Editing *ed)
Definition sequencer.cc:420
int effect_get_num_inputs(int strip_type)
Definition effects.cc:286
VecBase< int32_t, 2 > int2
VecBase< float, 2 > float2
void RNA_int_set(PointerRNA *ptr, const char *name, int value)
int RNA_int_get(PointerRNA *ptr, const char *name)
bool RNA_boolean_get(PointerRNA *ptr, const char *name)
int RNA_enum_get(PointerRNA *ptr, const char *name)
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_flag(PropertyRNA *prop, PropertyFlag flag)
#define STRIP_IS_EFFECT(_strip)
#define STRIP_IS_SOUND(_strip)
#define STRIP_USE_DATA(_strip)
#define STRIP_CHANNEL_CHECK(_strip, _chan)
void * first
struct Editing * ed
struct RenderData r
ListBase markers
char dirpath[768]
struct Strip * input1
struct Mask * mask
StripData * data
struct Scene * scene
struct MovieClip * clip
ListBase seqbase
struct Strip * next
struct Strip * input2
float xmax
float xmin
float ymax
float ymin
int mval[2]
Definition WM_types.hh:760
struct ReportList * reports
struct PointerRNA * ptr
i
Definition text_draw.cc:230
ParamHandle ** handles
void WM_event_drag_start_mval(const wmEvent *event, const ARegion *region, int r_mval[2])
bool WM_cursor_test_motion_and_update(const int mval[2])
void WM_event_add_notifier(const bContext *C, uint type, void *reference)
PointerRNA * ptr
Definition wm_files.cc:4227
wmOperatorType * ot
Definition wm_files.cc:4226
void WM_gesture_box_cancel(bContext *C, wmOperator *op)
wmOperatorStatus WM_gesture_box_modal(bContext *C, wmOperator *op, const wmEvent *event)
wmOperatorStatus WM_gesture_box_invoke(bContext *C, wmOperator *op, const wmEvent *event)
void WM_operator_properties_gesture_box(wmOperatorType *ot)
void WM_operator_properties_select_operation_simple(wmOperatorType *ot)
void WM_operator_properties_border_to_rctf(wmOperator *op, rctf *r_rect)
void WM_operator_properties_generic_select(wmOperatorType *ot)
void WM_operator_properties_select_all(wmOperatorType *ot)
void WM_operator_properties_mouse_select(wmOperatorType *ot)
wmOperatorStatus WM_operator_flag_only_pass_through_on_press(wmOperatorStatus retval, const wmEvent *event)
wmOperatorStatus WM_menu_invoke(bContext *C, wmOperator *op, const wmEvent *)
wmOperatorStatus WM_generic_select_modal(bContext *C, wmOperator *op, const wmEvent *event)
wmOperatorStatus WM_generic_select_invoke(bContext *C, wmOperator *op, const wmEvent *event)