Blender V4.3
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
9#include <cmath>
10#include <cstdlib>
11#include <cstring>
12
13#include "MEM_guardedalloc.h"
14
15#include "BLI_blenlib.h"
16#include "BLI_ghash.h"
17#include "BLI_math_geom.h"
18#include "BLI_math_vector.h"
20#include "BLI_utildefines.h"
21
22#include "DNA_scene_types.h"
23#include "DNA_space_types.h"
24
25#include "BKE_context.hh"
26#include "BKE_report.hh"
27
28#include "WM_api.hh"
29#include "WM_types.hh"
30
31#include "RNA_define.hh"
32
33#include "SEQ_channels.hh"
34#include "SEQ_connect.hh"
35#include "SEQ_effects.hh"
36#include "SEQ_iterator.hh"
37#include "SEQ_relations.hh"
38#include "SEQ_retiming.hh"
39#include "SEQ_select.hh"
40#include "SEQ_sequencer.hh"
41#include "SEQ_time.hh"
42#include "SEQ_transform.hh"
43
44/* For menu, popup, icons, etc. */
45
46#include "ED_outliner.hh"
47#include "ED_screen.hh"
48#include "ED_select_utils.hh"
49#include "ED_sequencer.hh"
50
51#include "UI_view2d.hh"
52
53/* Own include. */
54#include "sequencer_intern.hh"
55
56/* -------------------------------------------------------------------- */
61 public:
64
65 MouseCoords(const View2D *v2d, int x, int y)
66 {
67 region[0] = x;
68 region[1] = y;
69 UI_view2d_region_to_view(v2d, x, y, &view[0], &view[1]);
70 }
71};
72
74{
75 Scene *scene = CTX_data_scene(C);
76 Editing *ed = SEQ_editing_get(scene);
77 ListBase *seqbase = SEQ_active_seqbase_get(ed);
79
80 const bool is_preview = sequencer_view_has_preview_poll(C);
81 if (is_preview) {
82 return SEQ_query_rendered_strips(scene, channels, seqbase, scene->r.cfra, 0);
83 }
84
85 return SEQ_query_all_strips(seqbase);
86}
87
89{
90 const Scene *scene = CTX_data_scene(C);
91 Editing *ed = SEQ_editing_get(scene);
92 ListBase *seqbase = SEQ_active_seqbase_get(ed);
94
95 const bool is_preview = sequencer_view_has_preview_poll(C);
96
97 if (is_preview) {
99 scene, channels, seqbase, scene->r.cfra, 0);
100 strips.remove_if([&](Sequence *seq) { return (seq->flag & SELECT) == 0; });
101 return strips;
102 }
103
104 return SEQ_query_selected_strips(seqbase);
105}
106
107static void select_surrounding_handles(Scene *scene, Sequence *test) /* XXX BRING BACK */
108{
109 Sequence *neighbor;
110
111 neighbor = find_neighboring_sequence(scene, test, SEQ_SIDE_LEFT, -1);
112 if (neighbor) {
113 /* Only select neighbor handle if matching handle from test seq is also selected,
114 * or if neighbor was not selected at all up till now.
115 * Otherwise, we get odd mismatch when shift-alt-rmb selecting neighbor strips... */
116 if (!(neighbor->flag & SELECT) || (test->flag & SEQ_LEFTSEL)) {
117 neighbor->flag |= SEQ_RIGHTSEL;
118 }
119 neighbor->flag |= SELECT;
120 recurs_sel_seq(neighbor);
121 }
122 neighbor = find_neighboring_sequence(scene, test, SEQ_SIDE_RIGHT, -1);
123 if (neighbor) {
124 if (!(neighbor->flag & SELECT) || (test->flag & SEQ_RIGHTSEL)) { /* See comment above. */
125 neighbor->flag |= SEQ_LEFTSEL;
126 }
127 neighbor->flag |= SELECT;
128 recurs_sel_seq(neighbor);
129 }
130}
131
132/* Used for mouse selection in SEQUENCER_OT_select. */
134 const Scene *scene, ListBase *seqbase, int sel_side, int channel, int frame)
135{
136
137 LISTBASE_FOREACH (Sequence *, seq, seqbase) {
138 if (channel == seq->machine) {
139 switch (sel_side) {
140 case SEQ_SIDE_LEFT:
141 if (frame > SEQ_time_left_handle_frame_get(scene, seq)) {
142 seq->flag &= ~(SEQ_RIGHTSEL | SEQ_LEFTSEL);
143 seq->flag |= SELECT;
144 }
145 break;
146 case SEQ_SIDE_RIGHT:
147 if (frame < SEQ_time_left_handle_frame_get(scene, seq)) {
148 seq->flag &= ~(SEQ_RIGHTSEL | SEQ_LEFTSEL);
149 seq->flag |= SELECT;
150 }
151 break;
152 case SEQ_SIDE_BOTH:
153 seq->flag &= ~(SEQ_RIGHTSEL | SEQ_LEFTSEL);
154 seq->flag |= SELECT;
155 break;
156 }
157 }
158 }
159}
160
161/* Used for mouse selection in SEQUENCER_OT_select_side. */
162static void select_active_side_range(const Scene *scene,
163 ListBase *seqbase,
164 const int sel_side,
165 const int frame_ranges[SEQ_MAX_CHANNELS],
166 const int frame_ignore)
167{
168 LISTBASE_FOREACH (Sequence *, seq, seqbase) {
169 if (seq->machine < SEQ_MAX_CHANNELS) {
170 const int frame = frame_ranges[seq->machine];
171 if (frame == frame_ignore) {
172 continue;
173 }
174 switch (sel_side) {
175 case SEQ_SIDE_LEFT:
176 if (frame > SEQ_time_left_handle_frame_get(scene, seq)) {
177 seq->flag &= ~(SEQ_RIGHTSEL | SEQ_LEFTSEL);
178 seq->flag |= SELECT;
179 }
180 break;
181 case SEQ_SIDE_RIGHT:
182 if (frame < SEQ_time_left_handle_frame_get(scene, seq)) {
183 seq->flag &= ~(SEQ_RIGHTSEL | SEQ_LEFTSEL);
184 seq->flag |= SELECT;
185 }
186 break;
187 case SEQ_SIDE_BOTH:
188 seq->flag &= ~(SEQ_RIGHTSEL | SEQ_LEFTSEL);
189 seq->flag |= SELECT;
190 break;
191 }
192 }
193 }
194}
195
196/* Used alongside `select_linked_time` helper function in SEQUENCER_OT_select. */
197static void select_linked_time_seq(const Scene *scene,
198 const Sequence *seq_source,
199 const eSeqHandle handle_clicked)
200{
201 ListBase *seqbase = SEQ_active_seqbase_get(scene->ed);
202 int source_left = SEQ_time_left_handle_frame_get(scene, seq_source);
203 int source_right = SEQ_time_right_handle_frame_get(scene, seq_source);
204
205 LISTBASE_FOREACH (Sequence *, seq_dest, seqbase) {
206 if (seq_source->machine != seq_dest->machine) {
207 const bool left_match = (SEQ_time_left_handle_frame_get(scene, seq_dest) == source_left);
208 const bool right_match = (SEQ_time_right_handle_frame_get(scene, seq_dest) == source_right);
209
210 if (left_match && right_match) {
211 /* Direct match, copy all selection settings. */
212 seq_dest->flag &= ~(SEQ_ALLSEL);
213 seq_dest->flag |= seq_source->flag & (SEQ_ALLSEL);
214 recurs_sel_seq(seq_dest);
215 }
216 else if (left_match && handle_clicked == SEQ_HANDLE_LEFT) {
217 seq_dest->flag &= ~(SELECT | SEQ_LEFTSEL);
218 seq_dest->flag |= seq_source->flag & (SELECT | SEQ_LEFTSEL);
219 recurs_sel_seq(seq_dest);
220 }
221 else if (right_match && handle_clicked == SEQ_HANDLE_RIGHT) {
222 seq_dest->flag &= ~(SELECT | SEQ_RIGHTSEL);
223 seq_dest->flag |= seq_source->flag & (SELECT | SEQ_RIGHTSEL);
224 recurs_sel_seq(seq_dest);
225 }
226 }
227 }
228}
229
230#if 0 /* BRING BACK */
231void select_surround_from_last(Scene *scene)
232{
233 Sequence *seq = get_last_seq(scene);
234
235 if (seq == nullptr) {
236 return;
237 }
238
239 select_surrounding_handles(scene, seq);
240}
241#endif
242
243void ED_sequencer_select_sequence_single(Scene *scene, Sequence *seq, bool deselect_all)
244{
245 Editing *ed = SEQ_editing_get(scene);
246
247 if (deselect_all) {
249 }
250
251 SEQ_select_active_set(scene, seq);
252
254 if (seq->strip) {
256 }
257 }
258 else if (seq->type == SEQ_TYPE_SOUND_RAM) {
259 if (seq->strip) {
261 }
262 }
263 seq->flag |= SELECT;
264 recurs_sel_seq(seq);
265}
266
267void seq_rectf(const Scene *scene, const Sequence *seq, rctf *r_rect)
268{
269 r_rect->xmin = SEQ_time_left_handle_frame_get(scene, seq);
270 r_rect->xmax = SEQ_time_right_handle_frame_get(scene, seq);
271 r_rect->ymin = seq->machine + SEQ_STRIP_OFSBOTTOM;
272 r_rect->ymax = seq->machine + SEQ_STRIP_OFSTOP;
273}
274
275Sequence *find_neighboring_sequence(Scene *scene, Sequence *test, int lr, int sel)
276{
277 /* sel: 0==unselected, 1==selected, -1==don't care. */
278 Editing *ed = SEQ_editing_get(scene);
279
280 if (ed == nullptr) {
281 return nullptr;
282 }
283
284 if (sel > 0) {
285 sel = SELECT;
286 }
287 LISTBASE_FOREACH (Sequence *, seq, ed->seqbasep) {
288 if ((seq != test) && (test->machine == seq->machine) &&
289 ((sel == -1) || (sel && (seq->flag & SELECT)) || (sel == 0 && (seq->flag & SELECT) == 0)))
290 {
291 switch (lr) {
292 case SEQ_SIDE_LEFT:
293 if (SEQ_time_left_handle_frame_get(scene, test) ==
295 {
296 return seq;
297 }
298 break;
299 case SEQ_SIDE_RIGHT:
300 if (SEQ_time_right_handle_frame_get(scene, test) ==
302 {
303 return seq;
304 }
305 break;
306 }
307 }
308 }
309 return nullptr;
310}
311
313 const View2D *v2d,
314 const int mval[2],
315 eSeqHandle *r_hand)
316{
317 Sequence *seq;
318 Editing *ed = SEQ_editing_get(scene);
319 float x, y;
320 float pixelx;
321 float handsize;
322 float displen;
323 *r_hand = SEQ_HANDLE_NONE;
324
325 if (ed == nullptr) {
326 return nullptr;
327 }
328
329 pixelx = BLI_rctf_size_x(&v2d->cur) / BLI_rcti_size_x(&v2d->mask);
330
331 UI_view2d_region_to_view(v2d, mval[0], mval[1], &x, &y);
332
333 seq = static_cast<Sequence *>(ed->seqbasep->first);
334
335 while (seq) {
336 if (seq->machine == int(y)) {
337 /* Check for both normal strips, and strips that have been flipped horizontally. */
338 if (((SEQ_time_left_handle_frame_get(scene, seq) <
339 SEQ_time_right_handle_frame_get(scene, seq)) &&
340 (SEQ_time_left_handle_frame_get(scene, seq) <= x &&
341 SEQ_time_right_handle_frame_get(scene, seq) >= x)) ||
342 ((SEQ_time_left_handle_frame_get(scene, seq) >
343 SEQ_time_right_handle_frame_get(scene, seq)) &&
344 (SEQ_time_left_handle_frame_get(scene, seq) >= x &&
345 SEQ_time_right_handle_frame_get(scene, seq) <= x)))
346 {
348
349 /* Clamp handles to defined size in pixel space. */
350 handsize = 4.0f * sequence_handle_size_get_clamped(scene, seq, pixelx);
351 displen = float(abs(SEQ_time_left_handle_frame_get(scene, seq) -
353
354 /* Don't even try to grab the handles of small strips. */
355 if (displen / pixelx > 16) {
356
357 /* Set the max value to handle to 1/3 of the total len when its
358 * less than 28. This is important because otherwise selecting
359 * handles happens even when you click in the middle. */
360 if ((displen / 3) < 30 * pixelx) {
361 handsize = displen / 3;
362 }
363 else {
364 CLAMP(handsize, 7 * pixelx, 30 * pixelx);
365 }
366
367 if (handsize + SEQ_time_left_handle_frame_get(scene, seq) >= x) {
368 *r_hand = SEQ_HANDLE_LEFT;
369 }
370 else if (-handsize + SEQ_time_right_handle_frame_get(scene, seq) <= x) {
371 *r_hand = SEQ_HANDLE_RIGHT;
372 }
373 }
374 }
375 return seq;
376 }
377 }
378 seq = static_cast<Sequence *>(seq->next);
379 }
380 return nullptr;
381}
382
383#if 0
384static void select_neighbor_from_last(Scene *scene, int lr)
385{
386 Sequence *seq = SEQ_select_active_get(scene);
387 Sequence *neighbor;
388 bool changed = false;
389 if (seq) {
390 neighbor = find_neighboring_sequence(scene, seq, lr, -1);
391 if (neighbor) {
392 switch (lr) {
393 case SEQ_SIDE_LEFT:
394 neighbor->flag |= SELECT;
395 recurs_sel_seq(neighbor);
396 neighbor->flag |= SEQ_RIGHTSEL;
397 seq->flag |= SEQ_LEFTSEL;
398 break;
399 case SEQ_SIDE_RIGHT:
400 neighbor->flag |= SELECT;
401 recurs_sel_seq(neighbor);
402 neighbor->flag |= SEQ_LEFTSEL;
403 seq->flag |= SEQ_RIGHTSEL;
404 break;
405 }
406 seq->flag |= SELECT;
407 changed = true;
408 }
409 }
410 if (changed) {
411 /* Pass. */
412 }
413}
414#endif
415
417{
418 Sequence *seq;
419 seq = static_cast<Sequence *>(seq_meta->seqbase.first);
420
421 while (seq) {
422
423 if (seq_meta->flag & (SEQ_LEFTSEL + SEQ_RIGHTSEL)) {
424 seq->flag &= ~SEQ_ALLSEL;
425 }
426 else if (seq_meta->flag & SELECT) {
427 seq->flag |= SELECT;
428 }
429 else {
430 seq->flag &= ~SEQ_ALLSEL;
431 }
432
433 if (seq->seqbase.first) {
434 recurs_sel_seq(seq);
435 }
436
437 seq = static_cast<Sequence *>(seq->next);
438 }
439}
440
441static bool seq_point_image_isect(const Scene *scene, const Sequence *seq, float point[2])
442{
443 float seq_image_quad[4][2];
444 SEQ_image_transform_final_quad_get(scene, seq, seq_image_quad);
445 return isect_point_quad_v2(
446 point, seq_image_quad[0], seq_image_quad[1], seq_image_quad[2], seq_image_quad[3]);
447}
448
454
457/* -------------------------------------------------------------------- */
462{
463 int action = RNA_enum_get(op->ptr, "action");
464 Scene *scene = CTX_data_scene(C);
465
467 return OPERATOR_CANCELLED;
468 }
469
471 {
473 }
474
476
477 if (action == SEL_TOGGLE) {
478 action = SEL_SELECT;
479 for (Sequence *seq : strips) {
480 if (seq->flag & SEQ_ALLSEL) {
481 action = SEL_DESELECT;
482 break;
483 }
484 }
485 }
486
487 for (Sequence *seq : strips) {
488 switch (action) {
489 case SEL_SELECT:
490 seq->flag &= ~(SEQ_LEFTSEL + SEQ_RIGHTSEL);
491 seq->flag |= SELECT;
492 break;
493 case SEL_DESELECT:
494 seq->flag &= ~SEQ_ALLSEL;
495 break;
496 case SEL_INVERT:
497 if (seq->flag & SEQ_ALLSEL) {
498 seq->flag &= ~SEQ_ALLSEL;
499 }
500 else {
501 seq->flag &= ~(SEQ_LEFTSEL + SEQ_RIGHTSEL);
502 seq->flag |= SELECT;
503 }
504 break;
505 }
506 }
509
510 return OPERATOR_FINISHED;
511}
512
514{
515 /* Identifiers. */
516 ot->name = "(De)select All";
517 ot->idname = "SEQUENCER_OT_select_all";
518 ot->description = "Select or deselect all strips";
519
520 /* Api callbacks. */
523
524 /* Flags. */
526
528}
529
532/* -------------------------------------------------------------------- */
537{
538 Scene *scene = CTX_data_scene(C);
539
541 return OPERATOR_CANCELLED;
542 }
543
545
546 for (Sequence *seq : strips) {
547 if (seq->flag & SELECT) {
548 seq->flag &= ~SEQ_ALLSEL;
549 }
550 else {
551 seq->flag &= ~(SEQ_LEFTSEL + SEQ_RIGHTSEL);
552 seq->flag |= SELECT;
553 }
554 }
555
558
559 return OPERATOR_FINISHED;
560}
561
563{
564 /* Identifiers. */
565 ot->name = "Select Inverse";
566 ot->idname = "SEQUENCER_OT_select_inverse";
567 ot->description = "Select unselected strips";
568
569 /* Api callbacks. */
572
573 /* Flags. */
575}
576
579/* -------------------------------------------------------------------- */
584{
585 Editing *ed = SEQ_editing_get(scene);
586
587 SEQ_select_active_set(scene, seq);
588
590 if (seq->strip) {
592 }
593 }
594 else if (seq->type == SEQ_TYPE_SOUND_RAM) {
595 if (seq->strip) {
597 }
598 }
599 recurs_sel_seq(seq);
600}
601
603 const View2D *v2d,
604 const int mval[2],
605 Scene *scene)
606{
607 Editing *ed = SEQ_editing_get(scene);
608
609 const float x = UI_view2d_region_to_view_x(v2d, mval[0]);
611 if (((x < scene->r.cfra) &&
612 (SEQ_time_right_handle_frame_get(scene, seq_iter) <= scene->r.cfra)) ||
613 ((x >= scene->r.cfra) &&
614 (SEQ_time_left_handle_frame_get(scene, seq_iter) >= scene->r.cfra)))
615 {
616 /* Select left or right. */
617 seq_iter->flag |= SELECT;
618 recurs_sel_seq(seq_iter);
619 }
620 }
621
622 {
623 SpaceSeq *sseq = CTX_wm_space_seq(C);
624 if (sseq && sseq->flag & SEQ_MARKER_TRANS) {
625
626 LISTBASE_FOREACH (TimeMarker *, tmarker, &scene->markers) {
627 if (((x < scene->r.cfra) && (tmarker->frame <= scene->r.cfra)) ||
628 ((x >= scene->r.cfra) && (tmarker->frame >= scene->r.cfra)))
629 {
630 tmarker->flag |= SELECT;
631 }
632 else {
633 tmarker->flag &= ~SELECT;
634 }
635 }
636 }
637 }
638}
639
641 Sequence *seq,
642 const eSeqHandle handle_clicked)
643{
644 Scene *scene = CTX_data_scene(C);
645 Editing *ed = SEQ_editing_get(scene);
646 if (!ELEM(handle_clicked, SEQ_HANDLE_LEFT, SEQ_HANDLE_RIGHT)) {
647 /* First click selects the strip and its adjacent handles (if valid).
648 * Second click selects the strip,
649 * both of its handles and its adjacent handles (if valid). */
650 const bool is_striponly_selected = ((seq->flag & SEQ_ALLSEL) == SELECT);
651 seq->flag &= ~SEQ_ALLSEL;
652 seq->flag |= is_striponly_selected ? SEQ_ALLSEL : SELECT;
653 select_surrounding_handles(scene, seq);
654 }
655 else {
656 /* Always select the strip under the cursor. */
657 seq->flag |= SELECT;
658
659 /* First click selects adjacent handles on that side.
660 * Second click selects all strips in that direction.
661 * If there are no adjacent strips, it just selects all in that direction.
662 */
663 const int sel_side = (handle_clicked == SEQ_HANDLE_LEFT) ? SEQ_SIDE_LEFT : SEQ_SIDE_RIGHT;
664
665 Sequence *neighbor = find_neighboring_sequence(scene, seq, sel_side, -1);
666 if (neighbor) {
667 switch (sel_side) {
668 case SEQ_SIDE_LEFT:
669 if ((seq->flag & SEQ_LEFTSEL) && (neighbor->flag & SEQ_RIGHTSEL)) {
670 seq->flag |= SELECT;
671 select_active_side(scene,
672 ed->seqbasep,
674 seq->machine,
676 }
677 else {
678 seq->flag |= SELECT;
679 neighbor->flag |= SELECT;
680 recurs_sel_seq(neighbor);
681 neighbor->flag |= SEQ_RIGHTSEL;
682 seq->flag |= SEQ_LEFTSEL;
683 }
684 break;
685 case SEQ_SIDE_RIGHT:
686 if ((seq->flag & SEQ_RIGHTSEL) && (neighbor->flag & SEQ_LEFTSEL)) {
687 seq->flag |= SELECT;
688 select_active_side(scene,
689 ed->seqbasep,
691 seq->machine,
693 }
694 else {
695 seq->flag |= SELECT;
696 neighbor->flag |= SELECT;
697 recurs_sel_seq(neighbor);
698 neighbor->flag |= SEQ_LEFTSEL;
699 seq->flag |= SEQ_RIGHTSEL;
700 }
701 break;
702 }
703 }
704 else {
705
707 scene, ed->seqbasep, sel_side, seq->machine, SEQ_time_left_handle_frame_get(scene, seq));
708 }
709 }
710}
711
719
720static int seq_sort_for_depth_select(const void *a, const void *b)
721{
722 const SeqSelect_Link *slink_a = static_cast<const SeqSelect_Link *>(a);
723 const SeqSelect_Link *slink_b = static_cast<const SeqSelect_Link *>(b);
724
725 /* Exactly overlapping strips, sort by machine (so the top-most is first). */
726 if (slink_a->seq->machine < slink_b->seq->machine) {
727 return 1;
728 }
729 if (slink_a->seq->machine > slink_b->seq->machine) {
730 return -1;
731 }
732 return 0;
733}
734
735static int seq_sort_for_center_select(const void *a, const void *b)
736{
737 const SeqSelect_Link *slink_a = static_cast<const SeqSelect_Link *>(a);
738 const SeqSelect_Link *slink_b = static_cast<const SeqSelect_Link *>(b);
739 if (slink_a->center_dist_sq > slink_b->center_dist_sq) {
740 return 1;
741 }
742 if (slink_a->center_dist_sq < slink_b->center_dist_sq) {
743 return -1;
744 }
745
746 /* Exactly overlapping strips, use depth. */
747 return seq_sort_for_depth_select(a, b);
748}
749
756 const bContext *C, const int mval[2], const bool toggle, const bool extend, const bool center)
757{
758 Scene *scene = CTX_data_scene(C);
759 Editing *ed = SEQ_editing_get(scene);
760 ListBase *seqbase = SEQ_active_seqbase_get(ed);
761 ListBase *channels = SEQ_channels_displayed_get(ed);
762 SpaceSeq *sseq = CTX_wm_space_seq(C);
764
765 float mouseco_view[2];
766 UI_view2d_region_to_view(v2d, mval[0], mval[1], &mouseco_view[0], &mouseco_view[1]);
767
768 /* Always update the coordinates (check extended after). */
769 const bool use_cycle = (!WM_cursor_test_motion_and_update(mval) || extend || toggle);
770
771 /* Allow strips this far from the closest center to be included.
772 * This allows cycling over center points which are near enough
773 * to overlapping from the users perspective. */
774 const float center_dist_sq_max = square_f(75.0f * U.pixelsize);
775 const float center_scale_px[2] = {
778 };
779
781 scene, channels, seqbase, scene->r.cfra, sseq->chanshown);
782
783 SeqSelect_Link *slink_active = nullptr;
784 Sequence *seq_active = SEQ_select_active_get(scene);
785 ListBase strips_ordered = {nullptr};
786 for (Sequence *seq : strips) {
787 bool isect = false;
788 float center_dist_sq_test = 0.0f;
789 if (center) {
790 /* Detect overlapping center points (scaled by the zoom level). */
791 float co[2];
793 sub_v2_v2(co, mouseco_view);
794 mul_v2_v2(co, center_scale_px);
795 center_dist_sq_test = len_squared_v2(co);
796 isect = center_dist_sq_test <= center_dist_sq_max;
797 if (isect) {
798 /* Use an active strip penalty for "center" selection when cycle is enabled. */
799 if (use_cycle && (seq == seq_active) && (seq_active->flag & SELECT)) {
800 center_dist_sq_test = square_f(sqrtf(center_dist_sq_test) + (3.0f * U.pixelsize));
801 }
802 }
803 }
804 else {
805 isect = seq_point_image_isect(scene, seq, mouseco_view);
806 }
807
808 if (isect) {
809 SeqSelect_Link *slink = MEM_cnew<SeqSelect_Link>(__func__);
810 slink->seq = seq;
811 slink->center_dist_sq = center_dist_sq_test;
812 BLI_addtail(&strips_ordered, slink);
813
814 if (seq == seq_active) {
815 slink_active = slink;
816 }
817 }
818 }
819
820 BLI_listbase_sort(&strips_ordered,
822
823 SeqSelect_Link *slink_select = static_cast<SeqSelect_Link *>(strips_ordered.first);
824 Sequence *seq_select = nullptr;
825 if (slink_select != nullptr) {
826 /* Only use special behavior for the active strip when it's selected. */
827 if ((center == false) && slink_active && (seq_active->flag & SELECT)) {
828 if (use_cycle) {
829 if (slink_active->next) {
830 slink_select = slink_active->next;
831 }
832 }
833 else {
834 /* Match object selection behavior: keep the current active item unless cycle is enabled.
835 * Clicking again in the same location will cycle away from the active object. */
836 slink_select = slink_active;
837 }
838 }
839 seq_select = slink_select->seq;
840 }
841
842 BLI_freelistN(&strips_ordered);
843
844 return seq_select;
845}
846
848{
849 return ((handle == SEQ_HANDLE_LEFT) && (seq->flag & SEQ_LEFTSEL)) ||
850 ((handle == SEQ_HANDLE_RIGHT) && (seq->flag & SEQ_RIGHTSEL));
851}
852
853static bool element_already_selected(const StripSelection &selection)
854{
855 if (selection.seq1 == nullptr) {
856 return false;
857 }
858 const bool seq1_already_selected = ((selection.seq1->flag & SELECT) != 0);
859 if (selection.seq2 == nullptr) {
860 const bool handle_already_selected = ED_sequencer_handle_is_selected(selection.seq1,
861 selection.handle) ||
862 selection.handle == SEQ_HANDLE_NONE;
863 return seq1_already_selected && handle_already_selected;
864 }
865 const bool seq2_already_selected = ((selection.seq2->flag & SELECT) != 0);
866 const int seq1_handle = selection.seq1->flag & (SEQ_RIGHTSEL | SEQ_LEFTSEL);
867 const int seq2_handle = selection.seq2->flag & (SEQ_RIGHTSEL | SEQ_LEFTSEL);
868 /* Handles must be selected in XOR fashion, with `seq1` matching `handle_clicked`. */
869 const bool both_handles_selected = seq1_handle == selection.handle && seq2_handle != 0 &&
870 seq1_handle != seq2_handle;
871 return seq1_already_selected && seq2_already_selected && both_handles_selected;
872}
873
875{
877 sources.add(selection.seq1);
878 if (selection.seq2) {
879 sources.add(selection.seq2);
880 }
881
882 for (Sequence *source : sources) {
884 for (Sequence *connection : connections) {
885 /* Copy selection settings exactly for connected strips. */
886 connection->flag &= ~(SEQ_ALLSEL);
887 connection->flag |= source->flag & (SEQ_ALLSEL);
888 }
889 }
890}
891
893 Sequence *seq,
894 const eSeqHandle handle_clicked,
895 const bool extend,
896 const bool deselect,
897 const bool toggle)
898{
899 const bool is_active = (ed->act_seq == seq);
900
901 /* Exception for active strip handles. */
902 if ((handle_clicked != SEQ_HANDLE_NONE) && (seq->flag & SELECT) && is_active && toggle) {
903 if (handle_clicked == SEQ_HANDLE_LEFT) {
904 seq->flag ^= SEQ_LEFTSEL;
905 }
906 else if (handle_clicked == SEQ_HANDLE_RIGHT) {
907 seq->flag ^= SEQ_RIGHTSEL;
908 }
909 return;
910 }
911
912 /* Select strip. */
913 /* Match object selection behavior. */
914 int action = -1;
915 if (extend) {
916 action = 1;
917 }
918 else if (deselect) {
919 action = 0;
920 }
921 else {
922 if (!((seq->flag & SELECT) && is_active)) {
923 action = 1;
924 }
925 else if (toggle) {
926 action = 0;
927 }
928 }
929
930 if (action == 1) {
931 seq->flag |= SELECT;
932 if (handle_clicked == SEQ_HANDLE_LEFT) {
933 seq->flag |= SEQ_LEFTSEL;
934 }
935 if (handle_clicked == SEQ_HANDLE_RIGHT) {
936 seq->flag |= SEQ_RIGHTSEL;
937 }
938 }
939 else if (action == 0) {
940 seq->flag &= ~SEQ_ALLSEL;
941 }
942}
943
944static void select_linked_time(const Scene *scene,
945 const StripSelection &selection,
946 const bool extend,
947 const bool deselect,
948 const bool toggle)
949{
950 Editing *ed = SEQ_editing_get(scene);
951
952 sequencer_select_strip_impl(ed, selection.seq1, selection.handle, extend, deselect, toggle);
953 select_linked_time_seq(scene, selection.seq1, selection.handle);
954
955 if (selection.seq2 != nullptr) {
956 eSeqHandle seq2_handle_clicked = (selection.handle == SEQ_HANDLE_LEFT) ? SEQ_HANDLE_RIGHT :
958 sequencer_select_strip_impl(ed, selection.seq2, seq2_handle_clicked, extend, deselect, toggle);
959 select_linked_time_seq(scene, selection.seq2, seq2_handle_clicked);
960 }
961}
962
963/* Similar to `sequence_handle_size_get_clamped()` but allows for larger clickable area. */
964static float clickable_handle_size_get(const Scene *scene, const Sequence *seq, const View2D *v2d)
965{
966 const float pixelx = 1 / UI_view2d_scale_get_x(v2d);
967 const float strip_len = SEQ_time_right_handle_frame_get(scene, seq) -
969 return min_ff(15.0f * pixelx * U.pixelsize, strip_len / 4);
970}
971
972bool ED_sequencer_can_select_handle(const Scene *scene, const Sequence *seq, const View2D *v2d)
973{
974 if (SEQ_effect_get_num_inputs(seq->type) > 0) {
975 return false;
976 }
977
978 Editing *ed = SEQ_editing_get(scene);
979 ListBase *channels = SEQ_channels_displayed_get(ed);
980 if (SEQ_transform_is_locked(channels, seq)) {
981 return false;
982 }
983
984 int min_len = 25 * U.pixelsize;
985 if ((U.sequencer_editor_flag & USER_SEQ_ED_SIMPLE_TWEAKING) == 0) {
986 min_len = 15 * U.pixelsize;
987 }
988
989 const float pixelx = 1 / UI_view2d_scale_get_x(v2d);
990 const int strip_len = SEQ_time_right_handle_frame_get(scene, seq) -
992 if (strip_len / pixelx < min_len) {
993 return false;
994 }
995 return true;
996}
997
998static void strip_clickable_areas_get(const Scene *scene,
999 const Sequence *seq,
1000 const View2D *v2d,
1001 rctf *r_body,
1002 rctf *r_left_handle,
1003 rctf *r_right_handle)
1004{
1005 seq_rectf(scene, seq, r_body);
1006 *r_left_handle = *r_body;
1007 *r_right_handle = *r_body;
1008
1009 const float handsize = clickable_handle_size_get(scene, seq, v2d);
1010 BLI_rctf_pad(r_left_handle, handsize / 3, 0.0f);
1011 BLI_rctf_pad(r_right_handle, handsize / 3, 0.0f);
1012 r_left_handle->xmax = r_body->xmin + handsize;
1013 r_right_handle->xmin = r_body->xmax - handsize;
1014 BLI_rctf_pad(r_body, -handsize, 0.0f);
1015}
1016
1017static rctf strip_clickable_area_get(const Scene *scene, const View2D *v2d, const Sequence *seq)
1018{
1019 rctf body, left, right;
1020 strip_clickable_areas_get(scene, seq, v2d, &body, &left, &right);
1021 BLI_rctf_union(&body, &left);
1022 BLI_rctf_union(&body, &right);
1023 return body;
1024}
1025
1026static float strip_to_frame_distance(const Scene *scene,
1027 const View2D *v2d,
1028 const Sequence *seq,
1029 float timeline_frame)
1030{
1031 rctf body, left, right;
1032 strip_clickable_areas_get(scene, seq, v2d, &body, &left, &right);
1033 return BLI_rctf_length_x(&body, timeline_frame);
1034}
1035
1036/* Get strips that can be selected by click. */
1038 const View2D *v2d,
1039 float mouse_co[2])
1040{
1041 Editing *ed = SEQ_editing_get(scene);
1042
1044 LISTBASE_FOREACH (Sequence *, seq, ed->seqbasep) {
1045 if (seq->machine != int(mouse_co[1])) {
1046 continue;
1047 }
1048 if (SEQ_time_left_handle_frame_get(scene, seq) > v2d->cur.xmax) {
1049 continue;
1050 }
1051 if (SEQ_time_right_handle_frame_get(scene, seq) < v2d->cur.xmin) {
1052 continue;
1053 }
1054 const rctf body = strip_clickable_area_get(scene, v2d, seq);
1055 if (!BLI_rctf_isect_pt_v(&body, mouse_co)) {
1056 continue;
1057 }
1058 strips.append(seq);
1059 }
1060
1061 BLI_assert(strips.size() <= 2);
1062
1063 /* Ensure that `strips[0]` is the strip closest to the mouse cursor. */
1064 if (strips.size() == 2 && strip_to_frame_distance(scene, v2d, strips[0], mouse_co[0]) >
1065 strip_to_frame_distance(scene, v2d, strips[1], mouse_co[0]))
1066 {
1067 std::swap(strips[0], strips[1]);
1068 }
1069
1070 return strips;
1071}
1072
1073static bool strips_are_adjacent(const Scene *scene, const Sequence *seq1, const Sequence *seq2)
1074{
1075 const int s1_left = SEQ_time_left_handle_frame_get(scene, seq1);
1076 const int s1_right = SEQ_time_right_handle_frame_get(scene, seq1);
1077 const int s2_left = SEQ_time_left_handle_frame_get(scene, seq2);
1078 const int s2_right = SEQ_time_right_handle_frame_get(scene, seq2);
1079
1080 return s1_right == s2_left || s1_left == s2_right;
1081}
1082
1084 const Sequence *seq,
1085 const View2D *v2d,
1086 float mouse_co[2])
1087{
1088 if (!ED_sequencer_can_select_handle(scene, seq, v2d)) {
1089 return SEQ_HANDLE_NONE;
1090 }
1091
1092 rctf body, left, right;
1093 strip_clickable_areas_get(scene, seq, v2d, &body, &left, &right);
1094 if (BLI_rctf_isect_pt_v(&left, mouse_co)) {
1095 return SEQ_HANDLE_LEFT;
1096 }
1097 if (BLI_rctf_isect_pt_v(&right, mouse_co)) {
1098 return SEQ_HANDLE_RIGHT;
1099 }
1100
1101 return SEQ_HANDLE_NONE;
1102}
1103
1106 const View2D *v2d,
1107 float mouse_co[2])
1108{
1109 const eSeqHandle seq1_handle = get_strip_handle_under_cursor(scene, strips[0], v2d, mouse_co);
1110
1111 if (seq1_handle == SEQ_HANDLE_NONE) {
1112 return false;
1113 }
1114 if (!strips_are_adjacent(scene, strips[0], strips[1])) {
1115 return false;
1116 }
1117 const eSeqHandle seq2_handle = get_strip_handle_under_cursor(scene, strips[1], v2d, mouse_co);
1118 if (seq1_handle == SEQ_HANDLE_RIGHT && seq2_handle != SEQ_HANDLE_LEFT) {
1119 return false;
1120 }
1121 else if (seq1_handle == SEQ_HANDLE_LEFT && seq2_handle != SEQ_HANDLE_RIGHT) {
1122 return false;
1123 }
1124
1125 return true;
1126}
1127
1129 const View2D *v2d,
1130 float mouse_co[2])
1131{
1132 blender::Vector<Sequence *> strips = mouseover_strips_sorted_get(scene, v2d, mouse_co);
1133
1135
1136 if (strips.size() == 0) {
1137 return selection;
1138 }
1139
1140 selection.seq1 = strips[0];
1141 selection.handle = get_strip_handle_under_cursor(scene, selection.seq1, v2d, mouse_co);
1142
1143 if (strips.size() == 2 && (U.sequencer_editor_flag & USER_SEQ_ED_SIMPLE_TWEAKING) != 0 &&
1144 is_mouse_over_both_handles_of_adjacent_strips(scene, strips, v2d, mouse_co))
1145 {
1146 selection.seq2 = strips[1];
1147 }
1148
1149 return selection;
1150}
1151
1153{
1154 const View2D *v2d = UI_view2d_fromcontext(C);
1155 Scene *scene = CTX_data_scene(C);
1156 Editing *ed = SEQ_editing_get(scene);
1157 ARegion *region = CTX_wm_region(C);
1158
1159 if (ed == nullptr) {
1160 return OPERATOR_CANCELLED;
1161 }
1162
1163 if (region->regiontype == RGN_TYPE_PREVIEW) {
1165 return OPERATOR_CANCELLED;
1166 }
1167 const SpaceSeq *sseq = CTX_wm_space_seq(C);
1168 if (sseq->mainb != SEQ_DRAW_IMG_IMBUF) {
1169 return OPERATOR_CANCELLED;
1170 }
1171 }
1172
1173 bool was_retiming = sequencer_retiming_mode_is_active(C);
1174
1175 MouseCoords mouse_co(v2d, RNA_int_get(op->ptr, "mouse_x"), RNA_int_get(op->ptr, "mouse_y"));
1176
1177 /* Check to see if the mouse cursor intersects with the retiming box; if so, `seq_key_owner` is
1178 * set. If the cursor intersects with a retiming key, `key` will be set too. */
1179 Sequence *seq_key_owner = nullptr;
1180 SeqRetimingKey *key = retiming_mouseover_key_get(C, mouse_co.region, &seq_key_owner);
1181
1182 /* If no key was found, the mouse cursor may still intersect with a "fake key" that has not been
1183 * realized yet. */
1184 if (seq_key_owner != nullptr && key == nullptr &&
1186 SEQ_retiming_data_is_editable(seq_key_owner))
1187 {
1188 key = try_to_realize_fake_keys(C, seq_key_owner, mouse_co.region);
1189 }
1190
1191 if (key != nullptr) {
1192 if (!was_retiming) {
1194 }
1195 /* Attempt to realize any other connected strips' fake keys. */
1196 if (SEQ_is_strip_connected(seq_key_owner)) {
1197 const int key_frame = SEQ_retiming_key_timeline_frame_get(scene, seq_key_owner, key);
1198 blender::VectorSet<Sequence *> connections = SEQ_get_connected_strips(seq_key_owner);
1199 for (Sequence *connection : connections) {
1200 if (key_frame == left_fake_key_frame_get(C, connection) ||
1201 key_frame == right_fake_key_frame_get(C, connection))
1202 {
1203 realize_fake_keys(scene, connection);
1204 }
1205 }
1206 }
1207 return sequencer_retiming_key_select_exec(C, op, key, seq_key_owner);
1208 }
1209
1210 /* We should only reach here if no retiming selection is happening. */
1211 if (was_retiming) {
1214 }
1215
1216 bool extend = RNA_boolean_get(op->ptr, "extend");
1217 bool deselect = RNA_boolean_get(op->ptr, "deselect");
1218 bool deselect_all = RNA_boolean_get(op->ptr, "deselect_all");
1219 bool toggle = RNA_boolean_get(op->ptr, "toggle");
1220 bool center = RNA_boolean_get(op->ptr, "center");
1221
1223 if (region->regiontype == RGN_TYPE_PREVIEW) {
1224 selection.seq1 = seq_select_seq_from_preview(C, mouse_co.region, toggle, extend, center);
1225 }
1226 else {
1227 selection = ED_sequencer_pick_strip_and_handle(scene, v2d, mouse_co.view);
1228 }
1229
1230 /* NOTE: `side_of_frame` and `linked_time` functionality is designed to be shared on one
1231 * keymap, therefore both properties can be true at the same time. */
1232 if (selection.seq1 && RNA_boolean_get(op->ptr, "linked_time")) {
1233 if (!extend && !toggle) {
1235 }
1236 select_linked_time(scene, selection, extend, deselect, toggle);
1238 sequencer_select_set_active(scene, selection.seq1);
1239 return OPERATOR_FINISHED;
1240 }
1241
1242 /* Select left, right or overlapping the current frame. */
1243 if (RNA_boolean_get(op->ptr, "side_of_frame")) {
1244 if (!extend && !toggle) {
1246 }
1247 sequencer_select_side_of_frame(C, v2d, mouse_co.region, scene);
1249 return OPERATOR_FINISHED;
1250 }
1251
1252 /* On Alt selection, select the strip and bordering handles. */
1253 if (selection.seq1 && RNA_boolean_get(op->ptr, "linked_handle")) {
1254 if (!extend && !toggle) {
1256 }
1257 sequencer_select_linked_handle(C, selection.seq1, selection.handle);
1259 sequencer_select_set_active(scene, selection.seq1);
1260 return OPERATOR_FINISHED;
1261 }
1262
1263 const bool wait_to_deselect_others = RNA_boolean_get(op->ptr, "wait_to_deselect_others");
1264 const bool already_selected = element_already_selected(selection);
1265
1266 SpaceSeq *sseq = CTX_wm_space_seq(C);
1267 if (selection.handle != SEQ_HANDLE_NONE && already_selected) {
1268 sseq->flag &= ~SPACE_SEQ_DESELECT_STRIP_HANDLE;
1269 }
1270 else {
1272 }
1273 const bool ignore_connections = RNA_boolean_get(op->ptr, "ignore_connections");
1274
1275 /* Clicking on already selected element falls on modal operation.
1276 * All strips are deselected on mouse button release unless extend mode is used. */
1277 if (already_selected && wait_to_deselect_others && !toggle && !ignore_connections) {
1279 }
1280
1281 bool changed = false;
1282
1283 /* Deselect everything */
1284 if (deselect_all ||
1285 (selection.seq1 && (extend == false && deselect == false && toggle == false)))
1286 {
1287 changed |= ED_sequencer_deselect_all(scene);
1288 }
1289
1290 /* Nothing to select, but strips could be deselected. */
1291 if (!selection.seq1) {
1292 if (changed) {
1294 }
1295 return changed ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
1296 }
1297
1298 /* Do actual selection. */
1299 sequencer_select_strip_impl(ed, selection.seq1, selection.handle, extend, deselect, toggle);
1300 if (selection.seq2 != nullptr) {
1301 /* Invert handle selection for second strip */
1302 eSeqHandle seq2_handle_clicked = (selection.handle == SEQ_HANDLE_LEFT) ? SEQ_HANDLE_RIGHT :
1304 sequencer_select_strip_impl(ed, selection.seq2, seq2_handle_clicked, extend, deselect, toggle);
1305 }
1306
1307 if (!ignore_connections) {
1309 }
1310
1312 sequencer_select_set_active(scene, selection.seq1);
1313 return OPERATOR_FINISHED;
1314}
1315
1316static int sequencer_select_invoke(bContext *C, wmOperator *op, const wmEvent *event)
1317{
1318 const int retval = WM_generic_select_invoke(C, op, event);
1319 ARegion *region = CTX_wm_region(C);
1320 if (region && (region->regiontype == RGN_TYPE_PREVIEW)) {
1321 return WM_operator_flag_only_pass_through_on_press(retval, event);
1322 }
1323 return retval;
1324}
1325
1327{
1328 PropertyRNA *prop;
1329
1330 /* Identifiers. */
1331 ot->name = "Select";
1332 ot->idname = "SEQUENCER_OT_select";
1333 ot->description = "Select a strip (last selected becomes the \"active strip\")";
1334
1335 /* Api callbacks. */
1341
1342 /* Flags. */
1343 ot->flag = OPTYPE_UNDO;
1344
1345 /* Properties. */
1347
1349
1350 prop = RNA_def_boolean(
1351 ot->srna,
1352 "center",
1353 false,
1354 "Center",
1355 "Use the object center when selecting, in edit mode used to extend object selection");
1357
1358 prop = RNA_def_boolean(ot->srna,
1359 "linked_handle",
1360 false,
1361 "Linked Handle",
1362 "Select handles next to the active strip");
1364
1365 prop = RNA_def_boolean(ot->srna,
1366 "linked_time",
1367 false,
1368 "Linked Time",
1369 "Select other strips or handles at the same time, or all retiming keys "
1370 "after the current in retiming mode");
1372
1373 prop = RNA_def_boolean(
1374 ot->srna,
1375 "side_of_frame",
1376 false,
1377 "Side of Frame",
1378 "Select all strips on same side of the current frame as the mouse cursor");
1380
1381 prop = RNA_def_boolean(ot->srna,
1382 "ignore_connections",
1383 false,
1384 "Ignore Connections",
1385 "Select strips individually whether or not they are connected");
1387}
1388
1391/* -------------------------------------------------------------------- */
1396{
1397 const View2D *v2d = UI_view2d_fromcontext(C);
1398 Scene *scene = CTX_data_scene(C);
1399 Editing *ed = SEQ_editing_get(scene);
1400
1401 if (ed == nullptr) {
1402 return OPERATOR_CANCELLED;
1403 }
1404
1405 if ((U.sequencer_editor_flag & USER_SEQ_ED_SIMPLE_TWEAKING) == 0) {
1407 }
1408
1409 MouseCoords mouse_co(v2d, RNA_int_get(op->ptr, "mouse_x"), RNA_int_get(op->ptr, "mouse_y"));
1410
1411 StripSelection selection = ED_sequencer_pick_strip_and_handle(scene, v2d, mouse_co.view);
1412 if (selection.seq1 == nullptr || selection.handle == SEQ_HANDLE_NONE) {
1414 }
1415
1416 /* Ignore clicks on retiming keys. */
1417 Sequence *seq_key_test = nullptr;
1418 SeqRetimingKey *key = retiming_mouseover_key_get(C, mouse_co.region, &seq_key_test);
1419 if (key != nullptr) {
1421 }
1422
1423 SpaceSeq *sseq = CTX_wm_space_seq(C);
1424 if (element_already_selected(selection)) {
1425 sseq->flag &= ~SPACE_SEQ_DESELECT_STRIP_HANDLE;
1427 }
1428 else {
1431 }
1432
1433 /* Do actual selection. */
1434 sequencer_select_strip_impl(ed, selection.seq1, selection.handle, false, false, false);
1435 if (selection.seq2 != nullptr) {
1436 /* Invert handle selection for second strip */
1437 eSeqHandle seq2_handle_clicked = (selection.handle == SEQ_HANDLE_LEFT) ? SEQ_HANDLE_RIGHT :
1439 sequencer_select_strip_impl(ed, selection.seq2, seq2_handle_clicked, false, false, false);
1440 }
1441
1442 const bool ignore_connections = RNA_boolean_get(op->ptr, "ignore_connections");
1443 if (!ignore_connections) {
1445 }
1446
1449 sequencer_select_set_active(scene, selection.seq1);
1451}
1452
1454{
1455 ARegion *region = CTX_wm_region(C);
1456
1457 int mval[2];
1458 WM_event_drag_start_mval(event, region, mval);
1459
1460 RNA_int_set(op->ptr, "mouse_x", mval[0]);
1461 RNA_int_set(op->ptr, "mouse_y", mval[1]);
1462
1463 return sequencer_select_handle_exec(C, op);
1464}
1465
1467{
1468 PropertyRNA *prop;
1469
1470 /* Identifiers. */
1471 ot->name = "Select Handle";
1472 ot->idname = "SEQUENCER_OT_select_handle";
1473 ot->description = "Select strip handle";
1474
1475 /* Api callbacks. */
1479
1480 /* Flags. */
1481 ot->flag = OPTYPE_UNDO;
1482
1483 /* Properties. */
1485
1486 prop = RNA_def_boolean(ot->srna,
1487 "ignore_connections",
1488 false,
1489 "Ignore Connections",
1490 "Select strips individually whether or not they are connected");
1492}
1493
1496/* -------------------------------------------------------------------- */
1500/* Run recursively to select linked. */
1502{
1503 Editing *ed = SEQ_editing_get(scene);
1504
1505 if (ed == nullptr) {
1506 return false;
1507 }
1508
1509 bool changed = false;
1510
1512 if ((seq->flag & SELECT) == 0) {
1513 continue;
1514 }
1515 /* Only get unselected neighbors. */
1516 Sequence *neighbor = find_neighboring_sequence(scene, seq, SEQ_SIDE_LEFT, 0);
1517 if (neighbor) {
1518 neighbor->flag |= SELECT;
1519 recurs_sel_seq(neighbor);
1520 changed = true;
1521 }
1522 neighbor = find_neighboring_sequence(scene, seq, SEQ_SIDE_RIGHT, 0);
1523 if (neighbor) {
1524 neighbor->flag |= SELECT;
1525 recurs_sel_seq(neighbor);
1526 changed = true;
1527 }
1528 }
1529
1530 return changed;
1531}
1532
1533/* Select only one linked strip on each side. */
1534static bool select_more_less_seq__internal(Scene *scene, bool select_more)
1535{
1536 Editing *ed = SEQ_editing_get(scene);
1537
1538 if (ed == nullptr) {
1539 return false;
1540 }
1541
1542 GSet *neighbors = BLI_gset_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, "Linked strips");
1543 const int neighbor_selection_filter = select_more ? 0 : SELECT;
1544 const int selection_filter = select_more ? SELECT : 0;
1545
1547 if ((seq->flag & SELECT) != selection_filter) {
1548 continue;
1549 }
1551 scene, seq, SEQ_SIDE_LEFT, neighbor_selection_filter);
1552 if (neighbor) {
1553 BLI_gset_add(neighbors, neighbor);
1554 }
1555 neighbor = find_neighboring_sequence(scene, seq, SEQ_SIDE_RIGHT, neighbor_selection_filter);
1556 if (neighbor) {
1557 BLI_gset_add(neighbors, neighbor);
1558 }
1559 }
1560
1561 bool changed = false;
1562 GSetIterator gsi;
1563 BLI_gsetIterator_init(&gsi, neighbors);
1564 while (!BLI_gsetIterator_done(&gsi)) {
1565 Sequence *neighbor = static_cast<Sequence *>(BLI_gsetIterator_getKey(&gsi));
1566 if (select_more) {
1567 neighbor->flag |= SELECT;
1568 recurs_sel_seq(neighbor);
1569 }
1570 else {
1571 neighbor->flag &= ~SELECT;
1572 }
1573 changed = true;
1575 }
1576
1577 BLI_gset_free(neighbors, nullptr);
1578 return changed;
1579}
1580
1582{
1583 Scene *scene = CTX_data_scene(C);
1584
1585 if (!select_more_less_seq__internal(scene, true)) {
1586 return OPERATOR_CANCELLED;
1587 }
1588
1590
1592
1593 return OPERATOR_FINISHED;
1594}
1595
1597{
1598 /* Identifiers. */
1599 ot->name = "Select More";
1600 ot->idname = "SEQUENCER_OT_select_more";
1601 ot->description = "Select more strips adjacent to the current selection";
1602
1603 /* Api callbacks. */
1606
1607 /* Flags. */
1609}
1610
1613/* -------------------------------------------------------------------- */
1618{
1619 Scene *scene = CTX_data_scene(C);
1620
1621 if (!select_more_less_seq__internal(scene, false)) {
1622 return OPERATOR_CANCELLED;
1623 }
1624
1626
1628
1629 return OPERATOR_FINISHED;
1630}
1631
1633{
1634 /* Identifiers. */
1635 ot->name = "Select Less";
1636 ot->idname = "SEQUENCER_OT_select_less";
1637 ot->description = "Shrink the current selection of adjacent selected strips";
1638
1639 /* Api callbacks. */
1642
1643 /* Flags. */
1645}
1646
1649/* -------------------------------------------------------------------- */
1654{
1655 Scene *scene = CTX_data_scene(C);
1656 View2D *v2d = UI_view2d_fromcontext(C);
1657
1658 bool extend = RNA_boolean_get(op->ptr, "extend");
1659
1660 Sequence *mouse_seq;
1661 eSeqHandle hand;
1662 int selected;
1663
1664 /* This works like UV, not mesh. */
1665 mouse_seq = find_nearest_seq(scene, v2d, event->mval, &hand);
1666 if (!mouse_seq) {
1667 return OPERATOR_FINISHED; /* User error as with mesh?? */
1668 }
1669
1670 if (extend == 0) {
1672 }
1673
1674 mouse_seq->flag |= SELECT;
1675 recurs_sel_seq(mouse_seq);
1676
1677 selected = 1;
1678 while (selected) {
1679 selected = select_linked_internal(scene);
1680 }
1681
1683
1685
1686 return OPERATOR_FINISHED;
1687}
1688
1690{
1691 /* Identifiers. */
1692 ot->name = "Select Pick Linked";
1693 ot->idname = "SEQUENCER_OT_select_linked_pick";
1694 ot->description = "Select a chain of linked strips nearest to the mouse pointer";
1695
1696 /* Api callbacks. */
1699
1700 /* Flags. */
1702
1703 /* Properties. */
1704 PropertyRNA *prop;
1705 prop = RNA_def_boolean(ot->srna, "extend", false, "Extend", "Extend the selection");
1707}
1708
1711/* -------------------------------------------------------------------- */
1716{
1717 Scene *scene = CTX_data_scene(C);
1718 bool selected;
1719
1720 selected = true;
1721 while (selected) {
1722 selected = select_linked_internal(scene);
1723 }
1724
1726
1728
1729 return OPERATOR_FINISHED;
1730}
1731
1733{
1734 /* Identifiers. */
1735 ot->name = "Select Linked";
1736 ot->idname = "SEQUENCER_OT_select_linked";
1737 ot->description = "Select all strips adjacent to the current selection";
1738
1739 /* Api callbacks. */
1742
1743 /* Flags. */
1745}
1746
1749/* -------------------------------------------------------------------- */
1753enum {
1760};
1761
1763 {SEQ_SELECT_HANDLES_SIDE_LEFT, "LEFT", 0, "Left", ""},
1764 {SEQ_SELECT_HANDLES_SIDE_RIGHT, "RIGHT", 0, "Right", ""},
1765 {SEQ_SELECT_HANDLES_SIDE_BOTH, "BOTH", 0, "Both", ""},
1766 {SEQ_SELECT_HANDLES_SIDE_LEFT_NEIGHBOR, "LEFT_NEIGHBOR", 0, "Left Neighbor", ""},
1767 {SEQ_SELECT_HANDLES_SIDE_RIGHT_NEIGHBOR, "RIGHT_NEIGHBOR", 0, "Right Neighbor", ""},
1768 {SEQ_SELECT_HANDLES_SIDE_BOTH_NEIGHBORS, "BOTH_NEIGHBORS", 0, "Both Neighbors", ""},
1769 {0, nullptr, 0, nullptr, nullptr},
1770};
1771
1773{
1774 Scene *scene = CTX_data_scene(C);
1775 Editing *ed = SEQ_editing_get(scene);
1776 int sel_side = RNA_enum_get(op->ptr, "side");
1777 LISTBASE_FOREACH (Sequence *, seq, ed->seqbasep) {
1778 if (seq->flag & SELECT) {
1779 Sequence *l_neighbor = find_neighboring_sequence(scene, seq, SEQ_SIDE_LEFT, -1);
1780 Sequence *r_neighbor = find_neighboring_sequence(scene, seq, SEQ_SIDE_RIGHT, -1);
1781
1782 switch (sel_side) {
1784 seq->flag &= ~SEQ_RIGHTSEL;
1785 seq->flag |= SEQ_LEFTSEL;
1786 break;
1788 seq->flag &= ~SEQ_LEFTSEL;
1789 seq->flag |= SEQ_RIGHTSEL;
1790 break;
1792 seq->flag |= SEQ_LEFTSEL | SEQ_RIGHTSEL;
1793 break;
1795 if (l_neighbor) {
1796 if (!(l_neighbor->flag & SELECT)) {
1797 l_neighbor->flag |= SEQ_RIGHTSEL;
1798 }
1799 }
1800 break;
1802 if (r_neighbor) {
1803 if (!(r_neighbor->flag & SELECT)) {
1804 r_neighbor->flag |= SEQ_LEFTSEL;
1805 }
1806 }
1807 break;
1809 if (l_neighbor) {
1810 if (!(l_neighbor->flag & SELECT)) {
1811 l_neighbor->flag |= SEQ_RIGHTSEL;
1812 }
1813 }
1814 if (r_neighbor) {
1815 if (!(r_neighbor->flag & SELECT)) {
1816 r_neighbor->flag |= SEQ_LEFTSEL;
1817 }
1818 break;
1819 }
1820 }
1821 }
1822 }
1823 /* Select strips */
1824 LISTBASE_FOREACH (Sequence *, seq, ed->seqbasep) {
1825 if ((seq->flag & SEQ_LEFTSEL) || (seq->flag & SEQ_RIGHTSEL)) {
1826 if (!(seq->flag & SELECT)) {
1827 seq->flag |= SELECT;
1828 recurs_sel_seq(seq);
1829 }
1830 }
1831 }
1832
1834
1836
1837 return OPERATOR_FINISHED;
1838}
1839
1841{
1842 /* Identifiers. */
1843 ot->name = "Select Handles";
1844 ot->idname = "SEQUENCER_OT_select_handles";
1845 ot->description = "Select gizmo handles on the sides of the selected strip";
1846
1847 /* Api callbacks. */
1850
1851 /* Flags. */
1853
1854 /* Properties. */
1856 "side",
1859 "Side",
1860 "The side of the handle that is selected");
1861}
1862
1865/* -------------------------------------------------------------------- */
1870{
1871 Scene *scene = CTX_data_scene(C);
1872 Editing *ed = SEQ_editing_get(scene);
1873 const bool extend = RNA_boolean_get(op->ptr, "extend");
1874 const int side = RNA_enum_get(op->ptr, "side");
1875
1876 if (ed == nullptr) {
1877 return OPERATOR_CANCELLED;
1878 }
1879 if (extend == false) {
1881 }
1882 const int timeline_frame = scene->r.cfra;
1884 bool test = false;
1885 switch (side) {
1886 case -1:
1887 test = (timeline_frame >= SEQ_time_right_handle_frame_get(scene, seq));
1888 break;
1889 case 1:
1890 test = (timeline_frame <= SEQ_time_left_handle_frame_get(scene, seq));
1891 break;
1892 case 2:
1893 test = SEQ_time_strip_intersects_frame(scene, seq, timeline_frame);
1894 break;
1895 }
1896
1897 if (test) {
1898 seq->flag |= SELECT;
1899 recurs_sel_seq(seq);
1900 }
1901 }
1902
1904
1906
1907 return OPERATOR_FINISHED;
1908}
1909
1911{
1912 static const EnumPropertyItem sequencer_select_left_right_types[] = {
1913 {-1, "LEFT", 0, "Left", "Select to the left of the current frame"},
1914 {1, "RIGHT", 0, "Right", "Select to the right of the current frame"},
1915 {2, "CURRENT", 0, "Current Frame", "Select intersecting with the current frame"},
1916 {0, nullptr, 0, nullptr, nullptr},
1917 };
1918
1919 /* Identifiers. */
1920 ot->name = "Select Side of Frame";
1921 ot->idname = "SEQUENCER_OT_select_side_of_frame";
1922 ot->description = "Select strips relative to the current frame";
1923
1924 /* Api callbacks. */
1927
1928 /* Flags. */
1929 ot->flag = OPTYPE_UNDO;
1930
1931 /* Properties. */
1932 PropertyRNA *prop;
1933 prop = RNA_def_boolean(ot->srna, "extend", false, "Extend", "Extend the selection");
1935 ot->prop = RNA_def_enum(ot->srna, "side", sequencer_select_left_right_types, 0, "Side", "");
1936}
1937
1940/* -------------------------------------------------------------------- */
1945{
1946 Scene *scene = CTX_data_scene(C);
1947 Editing *ed = SEQ_editing_get(scene);
1948
1949 const int sel_side = RNA_enum_get(op->ptr, "side");
1950 const int frame_init = sel_side == SEQ_SIDE_LEFT ? INT_MIN : INT_MAX;
1951 int frame_ranges[SEQ_MAX_CHANNELS];
1952 bool selected = false;
1953
1954 copy_vn_i(frame_ranges, ARRAY_SIZE(frame_ranges), frame_init);
1955
1956 LISTBASE_FOREACH (Sequence *, seq, ed->seqbasep) {
1957 if (UNLIKELY(seq->machine >= SEQ_MAX_CHANNELS)) {
1958 continue;
1959 }
1960 int *frame_limit_p = &frame_ranges[seq->machine];
1961 if (seq->flag & SELECT) {
1962 selected = true;
1963 if (sel_side == SEQ_SIDE_LEFT) {
1964 *frame_limit_p = max_ii(*frame_limit_p, SEQ_time_left_handle_frame_get(scene, seq));
1965 }
1966 else {
1967 *frame_limit_p = min_ii(*frame_limit_p, SEQ_time_left_handle_frame_get(scene, seq));
1968 }
1969 }
1970 }
1971
1972 if (selected == false) {
1973 return OPERATOR_CANCELLED;
1974 }
1975
1976 select_active_side_range(scene, ed->seqbasep, sel_side, frame_ranges, frame_init);
1977
1979
1981
1982 return OPERATOR_FINISHED;
1983}
1984
1986{
1987 /* Identifiers. */
1988 ot->name = "Select Side";
1989 ot->idname = "SEQUENCER_OT_select_side";
1990 ot->description = "Select strips on the nominated side of the selected strips";
1991
1992 /* Api callbacks. */
1995
1996 /* Flags. */
1998
1999 /* Properties. */
2001 "side",
2004 "Side",
2005 "The side to which the selection is applied");
2006}
2007
2010/* -------------------------------------------------------------------- */
2015 const Sequence *seq,
2016 const rctf *rect)
2017{
2018 float seq_image_quad[4][2];
2019 SEQ_image_transform_final_quad_get(scene, seq, seq_image_quad);
2020 float rect_quad[4][2] = {{rect->xmax, rect->ymax},
2021 {rect->xmax, rect->ymin},
2022 {rect->xmin, rect->ymin},
2023 {rect->xmin, rect->ymax}};
2024
2025 return seq_point_image_isect(scene, seq, rect_quad[0]) ||
2026 seq_point_image_isect(scene, seq, rect_quad[1]) ||
2027 seq_point_image_isect(scene, seq, rect_quad[2]) ||
2028 seq_point_image_isect(scene, seq, rect_quad[3]) ||
2030 seq_image_quad[0], rect_quad[0], rect_quad[1], rect_quad[2], rect_quad[3]) ||
2032 seq_image_quad[1], rect_quad[0], rect_quad[1], rect_quad[2], rect_quad[3]) ||
2034 seq_image_quad[2], rect_quad[0], rect_quad[1], rect_quad[2], rect_quad[3]) ||
2036 seq_image_quad[3], rect_quad[0], rect_quad[1], rect_quad[2], rect_quad[3]);
2037}
2038
2040 const rctf *rect,
2041 const eSelectOp mode)
2042{
2043 Scene *scene = CTX_data_scene(C);
2044 Editing *ed = SEQ_editing_get(scene);
2045 ListBase *seqbase = SEQ_active_seqbase_get(ed);
2046 ListBase *channels = SEQ_channels_displayed_get(ed);
2047 SpaceSeq *sseq = CTX_wm_space_seq(C);
2048
2050 scene, channels, seqbase, scene->r.cfra, sseq->chanshown);
2051 for (Sequence *seq : strips) {
2052 if (!seq_box_select_rect_image_isect(scene, seq, rect)) {
2053 continue;
2054 }
2055
2056 if (ELEM(mode, SEL_OP_ADD, SEL_OP_SET)) {
2057 seq->flag |= SELECT;
2058 }
2059 else {
2060 BLI_assert(mode == SEL_OP_SUB);
2061 seq->flag &= ~SELECT;
2062 }
2063 }
2064}
2065
2067{
2068 Scene *scene = CTX_data_scene(C);
2069 View2D *v2d = UI_view2d_fromcontext(C);
2070 Editing *ed = SEQ_editing_get(scene);
2071
2072 if (ed == nullptr) {
2073 return OPERATOR_CANCELLED;
2074 }
2075
2077 {
2079 }
2080
2081 const eSelectOp sel_op = eSelectOp(RNA_enum_get(op->ptr, "mode"));
2082 const bool handles = RNA_boolean_get(op->ptr, "include_handles");
2083 const bool select = (sel_op != SEL_OP_SUB);
2084
2085 bool changed = false;
2086
2087 if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
2088 changed |= ED_sequencer_deselect_all(scene);
2089 }
2090
2091 rctf rectf;
2093 UI_view2d_region_to_view_rctf(v2d, &rectf, &rectf);
2094
2095 ARegion *region = CTX_wm_region(C);
2096 if (region->regiontype == RGN_TYPE_PREVIEW) {
2098 return OPERATOR_CANCELLED;
2099 }
2100 seq_box_select_seq_from_preview(C, &rectf, sel_op);
2102 return OPERATOR_FINISHED;
2103 }
2104
2105 LISTBASE_FOREACH (Sequence *, seq, ed->seqbasep) {
2106 rctf rq;
2107 seq_rectf(scene, seq, &rq);
2108 if (BLI_rctf_isect(&rq, &rectf, nullptr)) {
2109 if (handles) {
2110 /* Get the handles draw size. */
2111 float pixelx = BLI_rctf_size_x(&v2d->cur) / BLI_rcti_size_x(&v2d->mask);
2112 float handsize = sequence_handle_size_get_clamped(scene, seq, pixelx) * 4;
2113
2114 /* Right handle. */
2115 if (rectf.xmax > (SEQ_time_right_handle_frame_get(scene, seq) - handsize)) {
2116 if (select) {
2117 seq->flag |= SELECT | SEQ_RIGHTSEL;
2118 }
2119 else {
2120 /* Deselect the strip if it's left with no handles selected. */
2121 if ((seq->flag & SEQ_RIGHTSEL) && ((seq->flag & SEQ_LEFTSEL) == 0)) {
2122 seq->flag &= ~SELECT;
2123 }
2124 seq->flag &= ~SEQ_RIGHTSEL;
2125 }
2126
2127 changed = true;
2128 }
2129 /* Left handle. */
2130 if (rectf.xmin < (SEQ_time_left_handle_frame_get(scene, seq) + handsize)) {
2131 if (select) {
2132 seq->flag |= SELECT | SEQ_LEFTSEL;
2133 }
2134 else {
2135 /* Deselect the strip if it's left with no handles selected. */
2136 if ((seq->flag & SEQ_LEFTSEL) && ((seq->flag & SEQ_RIGHTSEL) == 0)) {
2137 seq->flag &= ~SELECT;
2138 }
2139 seq->flag &= ~SEQ_LEFTSEL;
2140 }
2141 }
2142
2143 changed = true;
2144 }
2145
2146 /* Regular box selection. */
2147 else {
2149 seq->flag &= ~(SEQ_LEFTSEL | SEQ_RIGHTSEL);
2150 changed = true;
2151 }
2152
2153 const bool ignore_connections = RNA_boolean_get(op->ptr, "ignore_connections");
2154 if (!ignore_connections) {
2155 /* Propagate selection to connected strips. */
2157 selection.seq1 = seq;
2159 }
2160 }
2161 }
2162
2163 if (!changed) {
2164 return OPERATOR_CANCELLED;
2165 }
2166
2168
2169 return OPERATOR_FINISHED;
2170}
2171
2173{
2174 Scene *scene = CTX_data_scene(C);
2175 const View2D *v2d = UI_view2d_fromcontext(C);
2176 ARegion *region = CTX_wm_region(C);
2177
2178 if (region->regiontype == RGN_TYPE_PREVIEW && !sequencer_view_preview_only_poll(C)) {
2179 return OPERATOR_CANCELLED;
2180 }
2181
2182 const bool tweak = RNA_boolean_get(op->ptr, "tweak");
2183
2184 if (tweak) {
2185 int mval[2];
2186 float mouse_co[2];
2187 WM_event_drag_start_mval(event, region, mval);
2188 UI_view2d_region_to_view(v2d, mval[0], mval[1], &mouse_co[0], &mouse_co[1]);
2189
2190 StripSelection selection = ED_sequencer_pick_strip_and_handle(scene, v2d, mouse_co);
2191
2192 if (selection.seq1 != nullptr) {
2194 }
2195 }
2196
2197 return WM_gesture_box_invoke(C, op, event);
2198}
2199
2201{
2202 PropertyRNA *prop;
2203
2204 /* Identifiers. */
2205 ot->name = "Box Select";
2206 ot->idname = "SEQUENCER_OT_select_box";
2207 ot->description = "Select strips using box selection";
2208
2209 /* Api callbacks. */
2214
2216
2217 /* Flags. */
2218 ot->flag = OPTYPE_UNDO;
2219
2220 /* Properties. */
2223
2224 prop = RNA_def_boolean(
2225 ot->srna,
2226 "tweak",
2227 false,
2228 "Tweak",
2229 "Make box select pass through to sequence slide when the cursor is hovering on a strip");
2231
2232 prop = RNA_def_boolean(
2233 ot->srna, "include_handles", false, "Select Handles", "Select the strips and their handles");
2235
2236 prop = RNA_def_boolean(ot->srna,
2237 "ignore_connections",
2238 false,
2239 "Ignore Connections",
2240 "Select strips individually whether or not they are connected");
2242}
2243
2246/* -------------------------------------------------------------------- */
2250enum {
2258};
2259
2261 {SEQ_SELECT_GROUP_TYPE, "TYPE", 0, "Type", "Shared strip type"},
2263 "TYPE_BASIC",
2264 0,
2265 "Global Type",
2266 "All strips of same basic type (graphical or sound)"},
2268 "TYPE_EFFECT",
2269 0,
2270 "Effect Type",
2271 "Shared strip effect type (if active strip is not an effect one, select all non-effect "
2272 "strips)"},
2273 {SEQ_SELECT_GROUP_DATA, "DATA", 0, "Data", "Shared data (scene, image, sound, etc.)"},
2274 {SEQ_SELECT_GROUP_EFFECT, "EFFECT", 0, "Effect", "Shared effects"},
2276 "EFFECT_LINK",
2277 0,
2278 "Effect/Linked",
2279 "Other strips affected by the active one (sharing some time, and below or "
2280 "effect-assigned)"},
2281 {SEQ_SELECT_GROUP_OVERLAP, "OVERLAP", 0, "Overlap", "Overlapping time"},
2282 {0, nullptr, 0, nullptr, nullptr},
2283};
2284
2285#define SEQ_IS_SOUND(_seq) ((_seq->type & SEQ_TYPE_SOUND_RAM) && !(_seq->type & SEQ_TYPE_EFFECT))
2286
2287#define SEQ_IS_EFFECT(_seq) ((_seq->type & SEQ_TYPE_EFFECT) != 0)
2288
2289#define SEQ_USE_DATA(_seq) \
2290 (ELEM(_seq->type, SEQ_TYPE_SCENE, SEQ_TYPE_MOVIECLIP, SEQ_TYPE_MASK) || SEQ_HAS_PATH(_seq))
2291
2292#define SEQ_CHANNEL_CHECK(_seq, _chan) ELEM((_chan), 0, (_seq)->machine)
2293
2295 ListBase * /*seqbase*/,
2296 Sequence *actseq,
2297 const int channel)
2298{
2299 bool changed = false;
2300
2301 for (Sequence *seq : strips) {
2302 if (SEQ_CHANNEL_CHECK(seq, channel) && seq->type == actseq->type) {
2303 seq->flag |= SELECT;
2304 changed = true;
2305 }
2306 }
2307
2308 return changed;
2309}
2310
2312 ListBase * /*seqbase*/,
2313 Sequence *actseq,
2314 const int channel)
2315{
2316 bool changed = false;
2317 const bool is_sound = SEQ_IS_SOUND(actseq);
2318
2319 for (Sequence *seq : strips) {
2320 if (SEQ_CHANNEL_CHECK(seq, channel) && (is_sound ? SEQ_IS_SOUND(seq) : !SEQ_IS_SOUND(seq))) {
2321 seq->flag |= SELECT;
2322 changed = true;
2323 }
2324 }
2325
2326 return changed;
2327}
2328
2330 ListBase * /*seqbase*/,
2331 Sequence *actseq,
2332 const int channel)
2333{
2334 bool changed = false;
2335 const bool is_effect = SEQ_IS_EFFECT(actseq);
2336
2337 for (Sequence *seq : strips) {
2338 if (SEQ_CHANNEL_CHECK(seq, channel) && (is_effect ? SEQ_IS_EFFECT(seq) : !SEQ_IS_EFFECT(seq)))
2339 {
2340 seq->flag |= SELECT;
2341 changed = true;
2342 }
2343 }
2344
2345 return changed;
2346}
2347
2349 ListBase * /*seqbase*/,
2350 Sequence *actseq,
2351 const int channel)
2352{
2353 bool changed = false;
2354 const char *dirpath = actseq->strip ? actseq->strip->dirpath : nullptr;
2355
2356 if (!SEQ_USE_DATA(actseq)) {
2357 return changed;
2358 }
2359
2360 if (SEQ_HAS_PATH(actseq) && dirpath) {
2361 for (Sequence *seq : strips) {
2362 if (SEQ_CHANNEL_CHECK(seq, channel) && SEQ_HAS_PATH(seq) && seq->strip &&
2363 STREQ(seq->strip->dirpath, dirpath))
2364 {
2365 seq->flag |= SELECT;
2366 changed = true;
2367 }
2368 }
2369 }
2370 else if (actseq->type == SEQ_TYPE_SCENE) {
2371 Scene *sce = actseq->scene;
2372 for (Sequence *seq : strips) {
2373 if (SEQ_CHANNEL_CHECK(seq, channel) && seq->type == SEQ_TYPE_SCENE && seq->scene == sce) {
2374 seq->flag |= SELECT;
2375 changed = true;
2376 }
2377 }
2378 }
2379 else if (actseq->type == SEQ_TYPE_MOVIECLIP) {
2380 MovieClip *clip = actseq->clip;
2381 for (Sequence *seq : strips) {
2382 if (SEQ_CHANNEL_CHECK(seq, channel) && seq->type == SEQ_TYPE_MOVIECLIP && seq->clip == clip)
2383 {
2384 seq->flag |= SELECT;
2385 changed = true;
2386 }
2387 }
2388 }
2389 else if (actseq->type == SEQ_TYPE_MASK) {
2390 Mask *mask = actseq->mask;
2391 for (Sequence *seq : strips) {
2392 if (SEQ_CHANNEL_CHECK(seq, channel) && seq->type == SEQ_TYPE_MASK && seq->mask == mask) {
2393 seq->flag |= SELECT;
2394 changed = true;
2395 }
2396 }
2397 }
2398
2399 return changed;
2400}
2401
2403 ListBase * /*seqbase*/,
2404 Sequence *actseq,
2405 const int channel)
2406{
2407 bool changed = false;
2408 bool effects[SEQ_TYPE_MAX + 1];
2409
2410 for (int i = 0; i <= SEQ_TYPE_MAX; i++) {
2411 effects[i] = false;
2412 }
2413
2414 for (Sequence *seq : strips) {
2415 if (SEQ_CHANNEL_CHECK(seq, channel) && (seq->type & SEQ_TYPE_EFFECT) &&
2417 {
2418 effects[seq->type] = true;
2419 }
2420 }
2421
2422 for (Sequence *seq : strips) {
2423 if (SEQ_CHANNEL_CHECK(seq, channel) && effects[seq->type]) {
2424 if (seq->seq1) {
2425 seq->seq1->flag |= SELECT;
2426 }
2427 if (seq->seq2) {
2428 seq->seq2->flag |= SELECT;
2429 }
2430 changed = true;
2431 }
2432 }
2433
2434 return changed;
2435}
2436
2437static bool select_grouped_time_overlap(const Scene *scene,
2439 ListBase * /*seqbase*/,
2440 Sequence *actseq)
2441{
2442 bool changed = false;
2443
2444 for (Sequence *seq : strips) {
2445 if (SEQ_time_left_handle_frame_get(scene, seq) <
2446 SEQ_time_right_handle_frame_get(scene, actseq) &&
2448 SEQ_time_left_handle_frame_get(scene, actseq))
2449 {
2450 seq->flag |= SELECT;
2451 changed = true;
2452 }
2453 }
2454
2455 return changed;
2456}
2457
2458/* Query strips that are in lower channel and intersect in time with seq_reference. */
2459static void query_lower_channel_strips(const Scene *scene,
2460 Sequence *seq_reference,
2461 ListBase *seqbase,
2463{
2464 LISTBASE_FOREACH (Sequence *, seq_test, seqbase) {
2465 if (seq_test->machine > seq_reference->machine) {
2466 continue; /* Not lower channel. */
2467 }
2468 if (SEQ_time_right_handle_frame_get(scene, seq_test) <=
2469 SEQ_time_left_handle_frame_get(scene, seq_reference) ||
2470 SEQ_time_left_handle_frame_get(scene, seq_test) >=
2471 SEQ_time_right_handle_frame_get(scene, seq_reference))
2472 {
2473 continue; /* Not intersecting in time. */
2474 }
2475 strips.add(seq_test);
2476 }
2477}
2478
2479/* Select all strips within time range and with lower channel of initial selection. Then select
2480 * effect chains of these strips. */
2481static bool select_grouped_effect_link(const Scene *scene,
2483 ListBase *seqbase,
2484 Sequence * /*actseq*/,
2485 const int /*channel*/)
2486{
2487 /* Get collection of strips. */
2488 strips.remove_if([&](Sequence *seq) { return (seq->flag & SELECT) == 0; });
2489 const int selected_strip_count = strips.size();
2490 /* XXX: this uses scene as arg, so it does not work with iterator :( I had thought about this,
2491 * but expand function is just so useful... I can just add scene and inject it I guess. */
2492 SEQ_iterator_set_expand(scene, seqbase, strips, query_lower_channel_strips);
2494
2495 /* Check if other strips will be affected. */
2496 const bool changed = strips.size() > selected_strip_count;
2497
2498 /* Actual logic. */
2499 for (Sequence *seq : strips) {
2500 seq->flag |= SELECT;
2501 }
2502
2503 return changed;
2504}
2505
2506#undef SEQ_IS_SOUND
2507#undef SEQ_IS_EFFECT
2508#undef SEQ_USE_DATA
2509
2511{
2512 Scene *scene = CTX_data_scene(C);
2514 Sequence *actseq = SEQ_select_active_get(scene);
2515
2516 const bool is_preview = sequencer_view_has_preview_poll(C);
2517 if (is_preview && !sequencer_view_preview_only_poll(C)) {
2518 return OPERATOR_CANCELLED;
2519 }
2520
2522
2523 if (actseq == nullptr || (is_preview && !strips.contains(actseq))) {
2524 BKE_report(op->reports, RPT_ERROR, "No active sequence!");
2525 return OPERATOR_CANCELLED;
2526 }
2527
2528 const int type = RNA_enum_get(op->ptr, "type");
2529 const int channel = RNA_boolean_get(op->ptr, "use_active_channel") ? actseq->machine : 0;
2530 const bool extend = RNA_boolean_get(op->ptr, "extend");
2531
2532 bool changed = false;
2533
2534 if (!extend) {
2535 LISTBASE_FOREACH (Sequence *, seq, seqbase) {
2536 seq->flag &= ~SELECT;
2537 changed = true;
2538 }
2539 }
2540
2541 switch (type) {
2543 changed |= select_grouped_type(strips, seqbase, actseq, channel);
2544 break;
2546 changed |= select_grouped_type_basic(strips, seqbase, actseq, channel);
2547 break;
2549 changed |= select_grouped_type_effect(strips, seqbase, actseq, channel);
2550 break;
2552 changed |= select_grouped_data(strips, seqbase, actseq, channel);
2553 break;
2555 changed |= select_grouped_effect(strips, seqbase, actseq, channel);
2556 break;
2558 changed |= select_grouped_effect_link(scene, strips, seqbase, actseq, channel);
2559 break;
2561 changed |= select_grouped_time_overlap(scene, strips, seqbase, actseq);
2562 break;
2563 default:
2564 BLI_assert(0);
2565 break;
2566 }
2567
2568 if (changed) {
2571 return OPERATOR_FINISHED;
2572 }
2573
2574 return OPERATOR_CANCELLED;
2575}
2576
2578{
2579 /* Identifiers. */
2580 ot->name = "Select Grouped";
2581 ot->idname = "SEQUENCER_OT_select_grouped";
2582 ot->description = "Select all strips grouped by various properties";
2583
2584 /* Api callbacks. */
2588
2589 /* Flags. */
2591
2592 /* Properties. */
2593 ot->prop = RNA_def_enum(ot->srna, "type", sequencer_prop_select_grouped_types, 0, "Type", "");
2595 "extend",
2596 false,
2597 "Extend",
2598 "Extend selection instead of deselecting everything first");
2600 "use_active_channel",
2601 false,
2602 "Same Channel",
2603 "Only consider strips on the same channel as the active one");
2604}
2605
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:125
#define BLI_assert(a)
Definition BLI_assert.h:50
struct GSet GSet
Definition BLI_ghash.h:341
unsigned int BLI_ghashutil_ptrhash(const void *key)
BLI_INLINE bool BLI_gsetIterator_done(const GSetIterator *gsi)
Definition BLI_ghash.h:467
BLI_INLINE void * BLI_gsetIterator_getKey(GSetIterator *gsi)
Definition BLI_ghash.h:459
GSet * BLI_gset_new(GSetHashFP hashfp, GSetCmpFP cmpfp, const char *info) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT
Definition BLI_ghash.c: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:451
void BLI_gset_free(GSet *gs, GSetKeyFreeFP keyfreefp)
Definition BLI_ghash.c:1034
BLI_INLINE void BLI_gsetIterator_step(GSetIterator *gsi)
Definition BLI_ghash.h:463
bool BLI_gset_add(GSet *gs, void *key)
Definition BLI_ghash.c:966
#define LISTBASE_FOREACH(type, var, list)
void void BLI_freelistN(struct ListBase *listbase) ATTR_NONNULL(1)
Definition listbase.cc:496
void void BLI_listbase_sort(struct ListBase *listbase, int(*cmp)(const void *, const void *)) ATTR_NONNULL(1
void BLI_addtail(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:110
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.c:171
BLI_INLINE int BLI_rcti_size_x(const struct rcti *rct)
Definition BLI_rect.h:189
void BLI_rctf_pad(struct rctf *rect, float pad_x, float pad_y)
Definition rct.c:631
BLI_INLINE float BLI_rctf_size_x(const struct rctf *rct)
Definition BLI_rect.h:197
char * BLI_strncpy(char *__restrict dst, const char *__restrict src, size_t dst_maxncpy) ATTR_NONNULL(1
#define CLAMP(a, b, c)
#define ARRAY_SIZE(arr)
#define SET_FLAG_FROM_TEST(value, test, flag)
#define UNLIKELY(x)
#define ELEM(...)
#define STREQ(a, b)
@ RGN_TYPE_PREVIEW
@ SEQ_TYPE_SOUND_RAM
@ SEQ_TYPE_MAX
@ SEQ_TYPE_SCENE
@ SEQ_TYPE_MOVIECLIP
@ SEQ_TYPE_IMAGE
@ SEQ_TYPE_EFFECT
@ SEQ_TYPE_MOVIE
@ SEQ_TYPE_MASK
#define SEQ_HAS_PATH(_seq)
#define SEQ_ALLSEL
@ SEQ_RIGHTSEL
@ SEQ_LEFTSEL
#define SEQ_STRIP_OFSBOTTOM
#define SEQ_STRIP_OFSTOP
@ SEQ_DRAW_IMG_IMBUF
@ SPACE_SEQ_DESELECT_STRIP_HANDLE
@ SEQ_MARKER_TRANS
@ USER_SEQ_ED_SIMPLE_TWEAKING
@ OPERATOR_RUNNING_MODAL
@ OPERATOR_PASS_THROUGH
void ED_outliner_select_sync_from_sequence_tag(bContext *C)
bool ED_operator_sequencer_active(bContext *C)
#define SEL_OP_USE_PRE_DESELECT(sel_op)
@ SEL_SELECT
@ SEL_INVERT
@ SEL_DESELECT
@ SEL_TOGGLE
void std::string ED_select_pick_get_name(wmOperatorType *ot, PointerRNA *ptr)
eSelectOp
@ SEL_OP_ADD
@ SEL_OP_SUB
@ SEL_OP_SET
bool sequencer_retiming_mode_is_active(const bContext *C)
bool ED_sequencer_deselect_all(Scene *scene)
eSeqHandle
@ SEQ_HANDLE_LEFT
@ SEQ_HANDLE_NONE
@ SEQ_HANDLE_RIGHT
Read Guarded memory(de)allocation.
@ PROP_SKIP_SAVE
Definition RNA_types.hh:245
@ SEQ_SIDE_RIGHT
@ SEQ_SIDE_BOTH
@ SEQ_SIDE_LEFT
constexpr int SEQ_MAX_CHANNELS
View2D * UI_view2d_fromcontext(const bContext *C)
Definition view2d.cc:1850
float UI_view2d_scale_get_y(const View2D *v2d)
Definition view2d.cc:1920
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:1663
float UI_view2d_region_to_view_x(const View2D *v2d, float x)
Definition view2d.cc:1652
void UI_view2d_region_to_view_rctf(const View2D *v2d, const rctf *rect_src, rctf *rect_dst) ATTR_NONNULL()
Definition view2d.cc:1670
float UI_view2d_scale_get_x(const View2D *v2d)
Definition view2d.cc:1916
#define ND_SEQUENCER
Definition WM_types.hh:404
@ OPTYPE_UNDO
Definition WM_types.hh:162
@ OPTYPE_REGISTER
Definition WM_types.hh:160
#define NC_SCENE
Definition WM_types.hh:345
#define NA_SELECTED
Definition WM_types.hh:555
unsigned int U
Definition btGjkEpa3.h:78
ListBase * SEQ_channels_displayed_get(Editing *ed)
Definition channels.cc:23
blender::int2 region
blender::float2 view
MouseCoords(const View2D *v2d, int x, int y)
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)
local_group_size(16, 16) .push_constant(Type b
#define SELECT
#define sqrtf(x)
draw_view in_light_buf[] float
int SEQ_effect_get_num_inputs(int seq_type)
Definition effects.cc:3467
VectorSet< Sequence * > SEQ_query_rendered_strips(const Scene *scene, ListBase *channels, ListBase *seqbase, const int timeline_frame, const int displayed_channel)
Definition iterator.cc:184
void SEQ_iterator_set_expand(const Scene *scene, ListBase *seqbase, VectorSet< Sequence * > &strips, void seq_query_func(const Scene *scene, Sequence *seq_reference, ListBase *seqbase, VectorSet< Sequence * > &strips))
Definition iterator.cc:61
void SEQ_query_strip_effect_chain(const Scene *scene, Sequence *reference_strip, ListBase *seqbase, VectorSet< Sequence * > &strips)
Definition iterator.cc:210
VectorSet< Sequence * > SEQ_query_all_strips(ListBase *seqbase)
Definition iterator.cc:97
VectorSet< Sequence * > SEQ_query_selected_strips(ListBase *seqbase)
Definition iterator.cc:106
ccl_device_inline float4 mask(const int4 mask, const float4 a)
ccl_device_inline float4 select(const int4 mask, const float4 a, const float4 b)
static int left
GPU_SHADER_INTERFACE_INFO(overlay_edit_curve_handle_iface, "vert").flat(Type pos vertex_in(1, Type::UINT, "data") .vertex_out(overlay_edit_curve_handle_iface) .geometry_layout(PrimitiveIn Frequency::GEOMETRY storage_buf(1, Qualifier::READ, "uint", "data[]", Frequency::GEOMETRY) .push_constant(Type Frequency::GEOMETRY selection[]
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)
ListBase * SEQ_active_seqbase_get(const Editing *ed)
Definition sequencer.cc:416
Editing * SEQ_editing_get(const Scene *scene)
Definition sequencer.cc:262
static bool is_sound(wmDrag *drag)
bool sequencer_view_preview_only_poll(const bContext *C)
bool sequencer_view_has_preview_poll(bContext *C)
const EnumPropertyItem prop_side_types[]
bool sequencer_edit_poll(bContext *C)
SeqRetimingKey * retiming_mouseover_key_get(const bContext *C, const int mval[2], Sequence **r_seq)
int sequencer_retiming_key_select_exec(bContext *C, wmOperator *op, SeqRetimingKey *key, const Sequence *key_owner)
int left_fake_key_frame_get(const bContext *C, const Sequence *seq)
SeqRetimingKey * try_to_realize_fake_keys(const bContext *C, Sequence *seq, const int mval[2])
float sequence_handle_size_get_clamped(const Scene *scene, Sequence *seq, float pixelx)
int sequencer_retiming_select_all_exec(bContext *C, wmOperator *op)
void realize_fake_keys(const Scene *scene, Sequence *seq)
int right_fake_key_frame_get(const bContext *C, const Sequence *seq)
bool retiming_keys_can_be_displayed(const SpaceSeq *sseq)
int sequencer_retiming_box_select_exec(bContext *C, wmOperator *op)
static int seq_sort_for_center_select(const void *a, const void *b)
static int sequencer_select_handle_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static int sequencer_select_linked_pick_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static int sequencer_box_select_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static void seq_box_select_seq_from_preview(const bContext *C, const rctf *rect, const eSelectOp mode)
static bool seq_box_select_rect_image_isect(const Scene *scene, const Sequence *seq, const rctf *rect)
#define SEQ_USE_DATA(_seq)
static void select_active_side(const Scene *scene, ListBase *seqbase, int sel_side, int channel, int frame)
static void sequencer_select_do_updates(bContext *C, Scene *scene)
void recurs_sel_seq(Sequence *seq_meta)
static bool select_grouped_time_overlap(const Scene *scene, blender::Span< Sequence * > strips, ListBase *, Sequence *actseq)
static int sequencer_box_select_exec(bContext *C, wmOperator *op)
void SEQUENCER_OT_select_side_of_frame(wmOperatorType *ot)
void seq_rectf(const Scene *scene, const Sequence *seq, rctf *r_rect)
void SEQUENCER_OT_select_more(wmOperatorType *ot)
void SEQUENCER_OT_select_all(wmOperatorType *ot)
static void sequencer_select_linked_handle(const bContext *C, Sequence *seq, const eSeqHandle handle_clicked)
static int sequencer_select_less_exec(bContext *C, wmOperator *)
static void sequencer_select_strip_impl(const Editing *ed, Sequence *seq, const eSeqHandle handle_clicked, const bool extend, const bool deselect, const bool toggle)
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)
static int sequencer_select_inverse_exec(bContext *C, wmOperator *)
#define SEQ_CHANNEL_CHECK(_seq, _chan)
static int sequencer_select_handles_exec(bContext *C, wmOperator *op)
static void select_surrounding_handles(Scene *scene, Sequence *test)
void ED_sequencer_select_sequence_single(Scene *scene, Sequence *seq, bool deselect_all)
static int sequencer_de_select_all_exec(bContext *C, wmOperator *op)
void SEQUENCER_OT_select_linked_pick(wmOperatorType *ot)
void SEQUENCER_OT_select_box(wmOperatorType *ot)
static bool strips_are_adjacent(const Scene *scene, const Sequence *seq1, const Sequence *seq2)
static int seq_sort_for_depth_select(const void *a, const void *b)
static float strip_to_frame_distance(const Scene *scene, const View2D *v2d, const Sequence *seq, float timeline_frame)
static void sequencer_select_set_active(Scene *scene, Sequence *seq)
static int sequencer_select_more_exec(bContext *C, wmOperator *)
StripSelection ED_sequencer_pick_strip_and_handle(const Scene *scene, const View2D *v2d, float mouse_co[2])
#define SEQ_IS_EFFECT(_seq)
static const EnumPropertyItem sequencer_prop_select_grouped_types[]
void SEQUENCER_OT_select_linked(wmOperatorType *ot)
void SEQUENCER_OT_select_handle(wmOperatorType *ot)
void SEQUENCER_OT_select_inverse(wmOperatorType *ot)
void SEQUENCER_OT_select_side(wmOperatorType *ot)
@ SEQ_SELECT_HANDLES_SIDE_LEFT_NEIGHBOR
@ SEQ_SELECT_HANDLES_SIDE_LEFT
@ SEQ_SELECT_HANDLES_SIDE_BOTH
@ SEQ_SELECT_HANDLES_SIDE_BOTH_NEIGHBORS
@ SEQ_SELECT_HANDLES_SIDE_RIGHT_NEIGHBOR
@ SEQ_SELECT_HANDLES_SIDE_RIGHT
static bool select_linked_internal(Scene *scene)
blender::VectorSet< Sequence * > ED_sequencer_selected_strips_from_context(bContext *C)
void SEQUENCER_OT_select(wmOperatorType *ot)
blender::VectorSet< Sequence * > all_strips_from_context(bContext *C)
static bool select_grouped_type_effect(blender::Span< Sequence * > strips, ListBase *, Sequence *actseq, const int channel)
static bool select_grouped_effect(blender::Span< Sequence * > strips, ListBase *, Sequence *actseq, const int channel)
static void select_linked_time_seq(const Scene *scene, const Sequence *seq_source, const eSeqHandle handle_clicked)
@ SEQ_SELECT_GROUP_TYPE_EFFECT
@ SEQ_SELECT_GROUP_TYPE_BASIC
@ SEQ_SELECT_GROUP_EFFECT_LINK
@ SEQ_SELECT_GROUP_EFFECT
@ SEQ_SELECT_GROUP_OVERLAP
@ SEQ_SELECT_GROUP_TYPE
@ SEQ_SELECT_GROUP_DATA
static bool select_grouped_data(blender::Span< Sequence * > strips, ListBase *, Sequence *actseq, const int channel)
void SEQUENCER_OT_select_grouped(wmOperatorType *ot)
void SEQUENCER_OT_select_handles(wmOperatorType *ot)
static float clickable_handle_size_get(const Scene *scene, const Sequence *seq, const View2D *v2d)
static Sequence * seq_select_seq_from_preview(const bContext *C, const int mval[2], const bool toggle, const bool extend, const bool center)
Sequence * find_neighboring_sequence(Scene *scene, Sequence *test, int lr, int sel)
static bool select_grouped_effect_link(const Scene *scene, blender::VectorSet< Sequence * > strips, ListBase *seqbase, Sequence *, const int)
bool ED_sequencer_can_select_handle(const Scene *scene, const Sequence *seq, const View2D *v2d)
static const EnumPropertyItem prop_select_handles_side_types[]
static bool element_already_selected(const StripSelection &selection)
Sequence * find_nearest_seq(const Scene *scene, const View2D *v2d, const int mval[2], eSeqHandle *r_hand)
static int sequencer_select_grouped_exec(bContext *C, wmOperator *op)
static rctf strip_clickable_area_get(const Scene *scene, const View2D *v2d, const Sequence *seq)
void SEQUENCER_OT_select_less(wmOperatorType *ot)
static int sequencer_select_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static void query_lower_channel_strips(const Scene *scene, Sequence *seq_reference, ListBase *seqbase, blender::VectorSet< Sequence * > &strips)
bool ED_sequencer_handle_is_selected(const Sequence *seq, eSeqHandle handle)
int sequencer_select_exec(bContext *C, wmOperator *op)
static void sequencer_select_connected_strips(const StripSelection &selection)
static int sequencer_select_side_exec(bContext *C, wmOperator *op)
static bool seq_point_image_isect(const Scene *scene, const Sequence *seq, float point[2])
static int sequencer_select_side_of_frame_exec(bContext *C, wmOperator *op)
static bool is_mouse_over_both_handles_of_adjacent_strips(const Scene *scene, blender::Vector< Sequence * > strips, const View2D *v2d, float mouse_co[2])
static bool select_grouped_type_basic(blender::Span< Sequence * > strips, ListBase *, Sequence *actseq, const int channel)
static void strip_clickable_areas_get(const Scene *scene, const Sequence *seq, const View2D *v2d, rctf *r_body, rctf *r_left_handle, rctf *r_right_handle)
static eSeqHandle get_strip_handle_under_cursor(const Scene *scene, const Sequence *seq, const View2D *v2d, float mouse_co[2])
static void sequencer_select_side_of_frame(const bContext *C, const View2D *v2d, const int mval[2], Scene *scene)
static int sequencer_select_linked_exec(bContext *C, wmOperator *)
static blender::Vector< Sequence * > mouseover_strips_sorted_get(const Scene *scene, const View2D *v2d, float mouse_co[2])
static bool select_more_less_seq__internal(Scene *scene, bool select_more)
#define SEQ_IS_SOUND(_seq)
static bool select_grouped_type(blender::Span< Sequence * > strips, ListBase *, Sequence *actseq, const int channel)
static void select_linked_time(const Scene *scene, const StripSelection &selection, const bool extend, const bool deselect, const bool toggle)
static int sequencer_select_handle_exec(bContext *C, wmOperator *op)
bool SEQ_is_strip_connected(const Sequence *seq)
blender::VectorSet< Sequence * > SEQ_get_connected_strips(const Sequence *seq)
bool SEQ_relation_is_effect_of_strip(const Sequence *effect, const Sequence *input)
bool SEQ_retiming_data_is_editable(const Sequence *seq)
int SEQ_retiming_key_timeline_frame_get(const Scene *scene, const Sequence *seq, const SeqRetimingKey *key)
bool SEQ_retiming_selection_clear(const Editing *ed)
void SEQ_select_active_set(Scene *scene, Sequence *seq)
Sequence * SEQ_select_active_get(const Scene *scene)
int SEQ_time_left_handle_frame_get(const Scene *, const Sequence *seq)
bool SEQ_time_strip_intersects_frame(const Scene *scene, const Sequence *seq, const int timeline_frame)
int SEQ_time_right_handle_frame_get(const Scene *scene, const Sequence *seq)
void SEQ_image_transform_origin_offset_pixelspace_get(const Scene *scene, const Sequence *seq, float r_origin[2])
void SEQ_image_transform_final_quad_get(const Scene *scene, const Sequence *seq, float r_quad[4][2])
bool SEQ_transform_is_locked(ListBase *channels, const Sequence *seq)
bool SEQ_transform_sequence_can_be_translated(const Sequence *seq)
ListBase * seqbasep
Sequence * act_seq
char act_sounddir[1024]
char act_imagedir[1024]
void * first
struct MovieClip * clip
struct Scene * scene
struct Mask * mask
struct Sequence * seq1
struct Sequence * seq2
struct Sequence * next
char dirpath[768]
float xmax
float xmin
float ymax
float ymin
int mval[2]
Definition WM_types.hh:728
const char * name
Definition WM_types.hh:990
bool(* poll)(bContext *C) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1042
const char * idname
Definition WM_types.hh:992
int(* modal)(bContext *C, wmOperator *op, const wmEvent *event) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1036
std::string(* get_name)(wmOperatorType *ot, PointerRNA *ptr)
Definition WM_types.hh:1068
int(* invoke)(bContext *C, wmOperator *op, const wmEvent *event) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1022
int(* exec)(bContext *C, wmOperator *op) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1006
const char * description
Definition WM_types.hh:996
PropertyRNA * prop
Definition WM_types.hh:1092
StructRNA * srna
Definition WM_types.hh:1080
void(* cancel)(bContext *C, wmOperator *op)
Definition WM_types.hh:1028
struct ReportList * reports
struct PointerRNA * ptr
ccl_device_inline int abs(int x)
Definition util/math.h:120
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)
wmOperatorType * ot
Definition wm_files.cc:4125
void WM_gesture_box_cancel(bContext *C, wmOperator *op)
int WM_gesture_box_invoke(bContext *C, wmOperator *op, const wmEvent *event)
int WM_gesture_box_modal(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)
int WM_operator_flag_only_pass_through_on_press(int retval, const wmEvent *event)
int WM_menu_invoke(bContext *C, wmOperator *op, const wmEvent *)
int WM_generic_select_invoke(bContext *C, wmOperator *op, const wmEvent *event)
int WM_generic_select_modal(bContext *C, wmOperator *op, const wmEvent *event)