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
8
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/* -------------------------------------------------------------------- */
59
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
456
457/* -------------------------------------------------------------------- */
460
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. */
522 ot->poll = sequencer_edit_poll;
523
524 /* Flags. */
525 ot->flag = OPTYPE_UNDO;
526
528}
529
531
532/* -------------------------------------------------------------------- */
535
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. */
571 ot->poll = sequencer_edit_poll;
572
573 /* Flags. */
574 ot->flag = OPTYPE_UNDO;
575}
576
578
579/* -------------------------------------------------------------------- */
582
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);
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
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,
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);
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);
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 }
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) {
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
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. */
1336 ot->exec = sequencer_select_exec;
1337 ot->invoke = sequencer_select_invoke;
1338 ot->modal = WM_generic_select_modal;
1340 ot->get_name = ED_select_pick_get_name;
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
1390
1391/* -------------------------------------------------------------------- */
1394
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
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);
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
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
1495
1496/* -------------------------------------------------------------------- */
1499
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. */
1605 ot->poll = sequencer_edit_poll;
1606
1607 /* Flags. */
1608 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1609}
1610
1612
1613/* -------------------------------------------------------------------- */
1616
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. */
1641 ot->poll = sequencer_edit_poll;
1642
1643 /* Flags. */
1644 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1645}
1646
1648
1649/* -------------------------------------------------------------------- */
1652
1654{
1655 Scene *scene = CTX_data_scene(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. */
1701 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1702
1703 /* Properties. */
1704 PropertyRNA *prop;
1705 prop = RNA_def_boolean(ot->srna, "extend", false, "Extend", "Extend the selection");
1707}
1708
1710
1711/* -------------------------------------------------------------------- */
1714
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. */
1741 ot->poll = sequencer_edit_poll;
1742
1743 /* Flags. */
1744 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1745}
1746
1748
1749/* -------------------------------------------------------------------- */
1752
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. */
1849 ot->poll = sequencer_edit_poll;
1850
1851 /* Flags. */
1852 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1853
1854 /* Properties. */
1855 RNA_def_enum(ot->srna,
1856 "side",
1859 "Side",
1860 "The side of the handle that is selected");
1861}
1862
1864
1865/* -------------------------------------------------------------------- */
1868
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
1939
1940/* -------------------------------------------------------------------- */
1943
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. */
1994 ot->poll = sequencer_edit_poll;
1995
1996 /* Flags. */
1997 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1998
1999 /* Properties. */
2000 RNA_def_enum(ot->srna,
2001 "side",
2004 "Side",
2005 "The side to which the selection is applied");
2006}
2007
2009
2010/* -------------------------------------------------------------------- */
2013
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);
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);
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
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
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. */
2212 ot->modal = WM_gesture_box_modal;
2213 ot->cancel = WM_gesture_box_cancel;
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
2245
2246/* -------------------------------------------------------------------- */
2249
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. */
2585 ot->invoke = WM_menu_invoke;
2587 ot->poll = sequencer_edit_poll;
2588
2589 /* Flags. */
2590 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2591
2592 /* Properties. */
2593 ot->prop = RNA_def_enum(ot->srna, "type", sequencer_prop_select_grouped_types, 0, "Type", "");
2594 RNA_def_boolean(ot->srna,
2595 "extend",
2596 false,
2597 "Extend",
2598 "Extend selection instead of deselecting everything first");
2599 RNA_def_boolean(ot->srna,
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
#define SEQ_STRIP_OFSBOTTOM
@ SEQ_RIGHTSEL
@ SEQ_LEFTSEL
#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.
Group Output data from inside of a node group A color picker Mix two input colors RGB to Convert a color s luminance to a grayscale value Generate a normal vector and a dot product Brightness Control the brightness and contrast of the input color Vector Map input vector components with curves Camera Retrieve information about the camera and how it relates to the current shading point s position Clamp a value between a minimum and a maximum Vector Perform vector math operation Invert Invert a producing a negative Combine Generate a color from its and blue channels(Deprecated)") DefNode(ShaderNode
in reality light always falls off quadratically Particle Retrieve the data of the particle that spawned the object for example to give variation to multiple instances of an object Point Retrieve information about points in a point cloud Retrieve the edges of an object as it appears to Cycles topology will always appear triangulated Convert a blackbody temperature to an RGB value Normal Generate a perturbed normal from an RGB normal map image Typically used for faking highly detailed surfaces Generate an OSL shader from a file or text data block Image Sample an image file as a texture Gabor Generate Gabor noise Gradient Generate interpolated color and intensity values based on the input vector Magic Generate a psychedelic color texture Voronoi Generate Worley noise based on the distance to random points Typically used to generate textures such as or biological cells Brick Generate a procedural texture producing bricks Texture Retrieve multiple types of texture coordinates nTypically used as inputs for texture nodes Vector Convert a point
@ PROP_SKIP_SAVE
Definition RNA_types.hh:245
#define C
Definition RandGen.cpp:29
@ 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
#define U
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
VecBase< int32_t, 2 > int2
VecBase< float, 2 > float2
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)
int sequencer_select_exec(bContext *C, wmOperator *op)
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)
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_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_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)
@ 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 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 Editing * ed
struct RenderData r
ListBase markers
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
struct ReportList * reports
struct PointerRNA * ptr
ccl_device_inline int abs(int x)
Definition util/math.h:120
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)
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)