Merge branch 'blender-v2.81-release'
[blender.git] / source / blender / editors / space_sequencer / sequencer_select.c
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  *
16  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
17  * All rights reserved.
18  */
19
20 /** \file
21  * \ingroup spseq
22  */
23
24 #include <stdlib.h>
25 #include <math.h>
26 #include <string.h>
27
28 #include "BLI_blenlib.h"
29 #include "BLI_utildefines.h"
30
31 #include "DNA_scene_types.h"
32
33 #include "BKE_context.h"
34 #include "BKE_report.h"
35 #include "BKE_sequencer.h"
36
37 #include "WM_api.h"
38 #include "WM_types.h"
39
40 #include "RNA_define.h"
41
42 /* for menu/popup icons etc etc*/
43
44 #include "ED_outliner.h"
45 #include "ED_screen.h"
46 #include "ED_sequencer.h"
47 #include "ED_select_utils.h"
48
49 #include "UI_view2d.h"
50
51 /* own include */
52 #include "sequencer_intern.h"
53
54 static void *find_nearest_marker(int UNUSED(d1), int UNUSED(d2))
55 {
56   return NULL;
57 }
58
59 static void select_surrounding_handles(Scene *scene, Sequence *test) /* XXX BRING BACK */
60 {
61   Sequence *neighbor;
62
63   neighbor = find_neighboring_sequence(scene, test, SEQ_SIDE_LEFT, -1);
64   if (neighbor) {
65     /* Only select neighbor handle if matching handle from test seq is also selected,
66      * or if neighbor was not selected at all up till now.
67      * Otherwise, we get odd mismatch when shift-alt-rmb selecting neighbor strips... */
68     if (!(neighbor->flag & SELECT) || (test->flag & SEQ_LEFTSEL)) {
69       neighbor->flag |= SEQ_RIGHTSEL;
70     }
71     neighbor->flag |= SELECT;
72     recurs_sel_seq(neighbor);
73   }
74   neighbor = find_neighboring_sequence(scene, test, SEQ_SIDE_RIGHT, -1);
75   if (neighbor) {
76     if (!(neighbor->flag & SELECT) || (test->flag & SEQ_RIGHTSEL)) { /* See comment above. */
77       neighbor->flag |= SEQ_LEFTSEL;
78     }
79     neighbor->flag |= SELECT;
80     recurs_sel_seq(neighbor);
81   }
82 }
83
84 /* used for mouse selection and for SEQUENCER_OT_select_active_side() */
85 static void select_active_side(ListBase *seqbase, int sel_side, int channel, int frame)
86 {
87   Sequence *seq;
88
89   for (seq = seqbase->first; seq; seq = seq->next) {
90     if (channel == seq->machine) {
91       switch (sel_side) {
92         case SEQ_SIDE_LEFT:
93           if (frame > (seq->startdisp)) {
94             seq->flag &= ~(SEQ_RIGHTSEL | SEQ_LEFTSEL);
95             seq->flag |= SELECT;
96           }
97           break;
98         case SEQ_SIDE_RIGHT:
99           if (frame < (seq->startdisp)) {
100             seq->flag &= ~(SEQ_RIGHTSEL | SEQ_LEFTSEL);
101             seq->flag |= SELECT;
102           }
103           break;
104         case SEQ_SIDE_BOTH:
105           seq->flag &= ~(SEQ_RIGHTSEL | SEQ_LEFTSEL);
106           seq->flag |= SELECT;
107           break;
108       }
109     }
110   }
111 }
112
113 /* used for mouse selection and for SEQUENCER_OT_select_active_side() */
114 static void select_linked_time(ListBase *seqbase, Sequence *seq_link)
115 {
116   Sequence *seq;
117
118   for (seq = seqbase->first; seq; seq = seq->next) {
119     if (seq_link->machine != seq->machine) {
120       int left_match = (seq->startdisp == seq_link->startdisp) ? 1 : 0;
121       int right_match = (seq->enddisp == seq_link->enddisp) ? 1 : 0;
122
123       if (left_match && right_match) {
124         /* a direct match, copy the selection settinhs */
125         seq->flag &= ~(SELECT | SEQ_LEFTSEL | SEQ_RIGHTSEL);
126         seq->flag |= seq_link->flag & (SELECT | SEQ_LEFTSEL | SEQ_RIGHTSEL);
127
128         recurs_sel_seq(seq);
129       }
130       else if (seq_link->flag & SELECT && (left_match || right_match)) {
131
132         /* clear for reselection */
133         seq->flag &= ~(SEQ_LEFTSEL | SEQ_RIGHTSEL);
134
135         if (left_match && seq_link->flag & SEQ_LEFTSEL) {
136           seq->flag |= SELECT | SEQ_LEFTSEL;
137         }
138
139         if (right_match && seq_link->flag & SEQ_RIGHTSEL) {
140           seq->flag |= SELECT | SEQ_RIGHTSEL;
141         }
142
143         recurs_sel_seq(seq);
144       }
145     }
146   }
147 }
148
149 #if 0  // BRING BACK
150 void select_surround_from_last(Scene *scene)
151 {
152   Sequence *seq = get_last_seq(scene);
153
154   if (seq == NULL) {
155     return;
156   }
157
158   select_surrounding_handles(scene, seq);
159 }
160 #endif
161
162 void ED_sequencer_select_sequence_single(Scene *scene, Sequence *seq, bool deselect_all)
163 {
164   Editing *ed = BKE_sequencer_editing_get(scene, false);
165
166   if (deselect_all) {
167     ED_sequencer_deselect_all(scene);
168   }
169
170   BKE_sequencer_active_set(scene, seq);
171
172   if ((seq->type == SEQ_TYPE_IMAGE) || (seq->type == SEQ_TYPE_MOVIE)) {
173     if (seq->strip) {
174       BLI_strncpy(ed->act_imagedir, seq->strip->dir, FILE_MAXDIR);
175     }
176   }
177   else if (seq->type == SEQ_TYPE_SOUND_RAM) {
178     if (seq->strip) {
179       BLI_strncpy(ed->act_sounddir, seq->strip->dir, FILE_MAXDIR);
180     }
181   }
182   seq->flag |= SELECT;
183   recurs_sel_seq(seq);
184 }
185
186 #if 0
187 static void select_neighbor_from_last(Scene *scene, int lr)
188 {
189   Sequence *seq = BKE_sequencer_active_get(scene);
190   Sequence *neighbor;
191   bool changed = false;
192   if (seq) {
193     neighbor = find_neighboring_sequence(scene, seq, lr, -1);
194     if (neighbor) {
195       switch (lr) {
196         case SEQ_SIDE_LEFT:
197           neighbor->flag |= SELECT;
198           recurs_sel_seq(neighbor);
199           neighbor->flag |= SEQ_RIGHTSEL;
200           seq->flag |= SEQ_LEFTSEL;
201           break;
202         case SEQ_SIDE_RIGHT:
203           neighbor->flag |= SELECT;
204           recurs_sel_seq(neighbor);
205           neighbor->flag |= SEQ_LEFTSEL;
206           seq->flag |= SEQ_RIGHTSEL;
207           break;
208       }
209       seq->flag |= SELECT;
210       changed = true;
211     }
212   }
213   if (changed) {
214     /* pass */
215   }
216 }
217 #endif
218
219 /* (de)select operator */
220 static int sequencer_de_select_all_exec(bContext *C, wmOperator *op)
221 {
222   int action = RNA_enum_get(op->ptr, "action");
223
224   Scene *scene = CTX_data_scene(C);
225   Editing *ed = BKE_sequencer_editing_get(scene, false);
226   Sequence *seq;
227
228   if (action == SEL_TOGGLE) {
229     action = SEL_SELECT;
230     for (seq = ed->seqbasep->first; seq; seq = seq->next) {
231       if (seq->flag & SEQ_ALLSEL) {
232         action = SEL_DESELECT;
233         break;
234       }
235     }
236   }
237
238   for (seq = ed->seqbasep->first; seq; seq = seq->next) {
239     switch (action) {
240       case SEL_SELECT:
241         seq->flag &= ~(SEQ_LEFTSEL + SEQ_RIGHTSEL);
242         seq->flag |= SELECT;
243         break;
244       case SEL_DESELECT:
245         seq->flag &= ~SEQ_ALLSEL;
246         break;
247       case SEL_INVERT:
248         if (seq->flag & SEQ_ALLSEL) {
249           seq->flag &= ~SEQ_ALLSEL;
250         }
251         else {
252           seq->flag &= ~(SEQ_LEFTSEL + SEQ_RIGHTSEL);
253           seq->flag |= SELECT;
254         }
255         break;
256     }
257   }
258
259   ED_outliner_select_sync_from_sequence_tag(C);
260
261   WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER | NA_SELECTED, scene);
262
263   return OPERATOR_FINISHED;
264 }
265
266 void SEQUENCER_OT_select_all(struct wmOperatorType *ot)
267 {
268   /* identifiers */
269   ot->name = "(De)select All";
270   ot->idname = "SEQUENCER_OT_select_all";
271   ot->description = "Select or deselect all strips";
272
273   /* api callbacks */
274   ot->exec = sequencer_de_select_all_exec;
275   ot->poll = sequencer_edit_poll;
276
277   /* flags */
278   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
279
280   WM_operator_properties_select_all(ot);
281 }
282
283 /* (de)select operator */
284 static int sequencer_select_inverse_exec(bContext *C, wmOperator *UNUSED(op))
285 {
286   Scene *scene = CTX_data_scene(C);
287   Editing *ed = BKE_sequencer_editing_get(scene, false);
288   Sequence *seq;
289
290   for (seq = ed->seqbasep->first; seq; seq = seq->next) {
291     if (seq->flag & SELECT) {
292       seq->flag &= ~SEQ_ALLSEL;
293     }
294     else {
295       seq->flag &= ~(SEQ_LEFTSEL + SEQ_RIGHTSEL);
296       seq->flag |= SELECT;
297     }
298   }
299
300   ED_outliner_select_sync_from_sequence_tag(C);
301
302   WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER | NA_SELECTED, scene);
303
304   return OPERATOR_FINISHED;
305 }
306
307 void SEQUENCER_OT_select_inverse(struct wmOperatorType *ot)
308 {
309   /* identifiers */
310   ot->name = "Select Inverse";
311   ot->idname = "SEQUENCER_OT_select_inverse";
312   ot->description = "Select unselected strips";
313
314   /* api callbacks */
315   ot->exec = sequencer_select_inverse_exec;
316   ot->poll = sequencer_edit_poll;
317
318   /* flags */
319   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
320 }
321
322 static int sequencer_select_exec(bContext *C, wmOperator *op)
323 {
324   View2D *v2d = UI_view2d_fromcontext(C);
325   Scene *scene = CTX_data_scene(C);
326   Editing *ed = BKE_sequencer_editing_get(scene, false);
327   const bool extend = RNA_boolean_get(op->ptr, "extend");
328   const bool deselect_all = RNA_boolean_get(op->ptr, "deselect_all");
329   const bool linked_handle = RNA_boolean_get(op->ptr, "linked_handle");
330   const bool linked_time = RNA_boolean_get(op->ptr, "linked_time");
331   bool wait_to_deselect_others = RNA_boolean_get(op->ptr, "wait_to_deselect_others");
332   int left_right = RNA_enum_get(op->ptr, "left_right");
333   int mval[2];
334   int ret_value = OPERATOR_CANCELLED;
335
336   mval[0] = RNA_int_get(op->ptr, "mouse_x");
337   mval[1] = RNA_int_get(op->ptr, "mouse_y");
338
339   Sequence *seq, *neighbor, *act_orig;
340   int hand, sel_side;
341   TimeMarker *marker;
342
343   if (ed == NULL) {
344     return OPERATOR_CANCELLED;
345   }
346
347   if (extend) {
348     wait_to_deselect_others = false;
349   }
350
351   marker = find_nearest_marker(SCE_MARKERS, 1);  // XXX - dummy function for now
352
353   seq = find_nearest_seq(scene, v2d, &hand, mval);
354
355   // XXX - not nice, Ctrl+RMB needs to do left_right only when not over a strip
356   if (seq && linked_time && (left_right == SEQ_SELECT_LR_MOUSE)) {
357     left_right = SEQ_SELECT_LR_NONE;
358   }
359
360   if (marker) {
361     int oldflag;
362     /* select timeline marker */
363     if (extend) {
364       oldflag = marker->flag;
365       if (oldflag & SELECT) {
366         marker->flag &= ~SELECT;
367       }
368       else {
369         marker->flag |= SELECT;
370       }
371     }
372     else {
373       /* XXX, in 2.4x, seq selection used to deselect all, need to re-thnik this for 2.5 */
374       /* deselect_markers(0, 0); */
375       marker->flag |= SELECT;
376     }
377
378     ret_value = OPERATOR_FINISHED;
379   }
380   else if (left_right != SEQ_SELECT_LR_NONE) {
381     /* use different logic for this */
382     float x;
383     if (extend == false) {
384       ED_sequencer_deselect_all(scene);
385     }
386
387     switch (left_right) {
388       case SEQ_SELECT_LR_MOUSE:
389         x = UI_view2d_region_to_view_x(v2d, mval[0]);
390         break;
391       case SEQ_SELECT_LR_LEFT:
392         x = CFRA - 1.0f;
393         break;
394       case SEQ_SELECT_LR_RIGHT:
395       default:
396         x = CFRA;
397         break;
398     }
399
400     SEQP_BEGIN (ed, seq) {
401       if (((x < CFRA) && (seq->enddisp <= CFRA)) || ((x >= CFRA) && (seq->startdisp >= CFRA))) {
402         seq->flag |= SELECT;
403         recurs_sel_seq(seq);
404       }
405     }
406     SEQ_END;
407
408     {
409       SpaceSeq *sseq = CTX_wm_space_seq(C);
410       if (sseq && sseq->flag & SEQ_MARKER_TRANS) {
411         TimeMarker *tmarker;
412
413         for (tmarker = scene->markers.first; tmarker; tmarker = tmarker->next) {
414           if (((x < CFRA) && (tmarker->frame <= CFRA)) ||
415               ((x >= CFRA) && (tmarker->frame >= CFRA))) {
416             tmarker->flag |= SELECT;
417           }
418           else {
419             tmarker->flag &= ~SELECT;
420           }
421         }
422       }
423     }
424
425     ret_value = OPERATOR_FINISHED;
426   }
427   else {
428     act_orig = ed->act_seq;
429
430     if (seq) {
431       /* Are we trying to select a handle that's already selected? */
432       const bool handle_selected = ((hand == SEQ_SIDE_LEFT) && (seq->flag & SEQ_LEFTSEL)) ||
433                                    ((hand == SEQ_SIDE_RIGHT) && (seq->flag & SEQ_RIGHTSEL));
434
435       if (wait_to_deselect_others && (seq->flag & SELECT) &&
436           (hand == SEQ_SIDE_NONE || handle_selected)) {
437         ret_value = OPERATOR_RUNNING_MODAL;
438       }
439       else if (!extend && !linked_handle) {
440         ED_sequencer_deselect_all(scene);
441         ret_value = OPERATOR_FINISHED;
442       }
443       else {
444         ret_value = OPERATOR_FINISHED;
445       }
446
447       BKE_sequencer_active_set(scene, seq);
448
449       if ((seq->type == SEQ_TYPE_IMAGE) || (seq->type == SEQ_TYPE_MOVIE)) {
450         if (seq->strip) {
451           BLI_strncpy(ed->act_imagedir, seq->strip->dir, FILE_MAXDIR);
452         }
453       }
454       else if (seq->type == SEQ_TYPE_SOUND_RAM) {
455         if (seq->strip) {
456           BLI_strncpy(ed->act_sounddir, seq->strip->dir, FILE_MAXDIR);
457         }
458       }
459
460       /* On Alt selection, select the strip and bordering handles */
461       if (linked_handle) {
462         if (!ELEM(hand, SEQ_SIDE_LEFT, SEQ_SIDE_RIGHT)) {
463           /* First click selects the strip and its adjacent handles (if valid).
464            * Second click selects the strip,
465            * both of its handles and its adjacent handles (if valid). */
466           const bool is_striponly_selected = ((seq->flag & SEQ_ALLSEL) == SELECT);
467
468           if (!extend) {
469             ED_sequencer_deselect_all(scene);
470           }
471           seq->flag &= ~SEQ_ALLSEL;
472           seq->flag |= is_striponly_selected ? SEQ_ALLSEL : SELECT;
473           select_surrounding_handles(scene, seq);
474         }
475         else {
476           /* always select the strip under the cursor */
477           seq->flag |= SELECT;
478
479           /* First click selects adjacent handles on that side.
480            * Second click selects all strips in that direction.
481            * If there are no adjacent strips, it just selects all in that direction.
482            */
483           sel_side = hand;
484           neighbor = find_neighboring_sequence(scene, seq, sel_side, -1);
485           if (neighbor) {
486             switch (sel_side) {
487               case SEQ_SIDE_LEFT:
488                 if ((seq->flag & SEQ_LEFTSEL) && (neighbor->flag & SEQ_RIGHTSEL)) {
489                   if (extend == 0) {
490                     ED_sequencer_deselect_all(scene);
491                   }
492                   seq->flag |= SELECT;
493
494                   select_active_side(ed->seqbasep, SEQ_SIDE_LEFT, seq->machine, seq->startdisp);
495                 }
496                 else {
497                   if (extend == 0) {
498                     ED_sequencer_deselect_all(scene);
499                   }
500                   seq->flag |= SELECT;
501
502                   neighbor->flag |= SELECT;
503                   recurs_sel_seq(neighbor);
504                   neighbor->flag |= SEQ_RIGHTSEL;
505                   seq->flag |= SEQ_LEFTSEL;
506                 }
507                 break;
508               case SEQ_SIDE_RIGHT:
509                 if ((seq->flag & SEQ_RIGHTSEL) && (neighbor->flag & SEQ_LEFTSEL)) {
510                   if (extend == 0) {
511                     ED_sequencer_deselect_all(scene);
512                   }
513                   seq->flag |= SELECT;
514
515                   select_active_side(ed->seqbasep, SEQ_SIDE_RIGHT, seq->machine, seq->startdisp);
516                 }
517                 else {
518                   if (extend == 0) {
519                     ED_sequencer_deselect_all(scene);
520                   }
521                   seq->flag |= SELECT;
522
523                   neighbor->flag |= SELECT;
524                   recurs_sel_seq(neighbor);
525                   neighbor->flag |= SEQ_LEFTSEL;
526                   seq->flag |= SEQ_RIGHTSEL;
527                 }
528                 break;
529             }
530           }
531           else {
532             if (extend == 0) {
533               ED_sequencer_deselect_all(scene);
534             }
535             select_active_side(ed->seqbasep, sel_side, seq->machine, seq->startdisp);
536           }
537         }
538
539         ret_value = OPERATOR_FINISHED;
540       }
541       else {
542         if (extend && (seq->flag & SELECT) && ed->act_seq == act_orig) {
543           switch (hand) {
544             case SEQ_SIDE_NONE:
545               if (linked_handle == 0) {
546                 seq->flag &= ~SEQ_ALLSEL;
547               }
548               break;
549             case SEQ_SIDE_LEFT:
550               seq->flag ^= SEQ_LEFTSEL;
551               break;
552             case SEQ_SIDE_RIGHT:
553               seq->flag ^= SEQ_RIGHTSEL;
554               break;
555           }
556           ret_value = OPERATOR_FINISHED;
557         }
558         else {
559           seq->flag |= SELECT;
560           if (hand == SEQ_SIDE_LEFT) {
561             seq->flag |= SEQ_LEFTSEL;
562           }
563           if (hand == SEQ_SIDE_RIGHT) {
564             seq->flag |= SEQ_RIGHTSEL;
565           }
566         }
567       }
568
569       recurs_sel_seq(seq);
570
571       if (linked_time) {
572         select_linked_time(ed->seqbasep, seq);
573       }
574
575       BLI_assert((ret_value & OPERATOR_CANCELLED) == 0);
576     }
577     else if (deselect_all) {
578       ED_sequencer_deselect_all(scene);
579       ret_value = OPERATOR_FINISHED;
580     }
581   }
582
583   ED_outliner_select_sync_from_sequence_tag(C);
584
585   WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER | NA_SELECTED, scene);
586
587   return ret_value;
588 }
589
590 void SEQUENCER_OT_select(wmOperatorType *ot)
591 {
592   static const EnumPropertyItem sequencer_select_left_right_types[] = {
593       {SEQ_SELECT_LR_NONE, "NONE", 0, "None", "Don't do left-right selection"},
594       {SEQ_SELECT_LR_MOUSE, "MOUSE", 0, "Mouse", "Use mouse position for selection"},
595       {SEQ_SELECT_LR_LEFT, "LEFT", 0, "Left", "Select left"},
596       {SEQ_SELECT_LR_RIGHT, "RIGHT", 0, "Right", "Select right"},
597       {0, NULL, 0, NULL, NULL},
598   };
599   PropertyRNA *prop;
600
601   /* identifiers */
602   ot->name = "Select";
603   ot->idname = "SEQUENCER_OT_select";
604   ot->description = "Select a strip (last selected becomes the \"active strip\")";
605
606   /* api callbacks */
607   ot->exec = sequencer_select_exec;
608   ot->invoke = WM_generic_select_invoke;
609   ot->modal = WM_generic_select_modal;
610   ot->poll = ED_operator_sequencer_active;
611
612   /* flags */
613   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
614
615   /* properties */
616   WM_operator_properties_generic_select(ot);
617   RNA_def_boolean(ot->srna, "extend", 0, "Extend", "Extend the selection");
618   prop = RNA_def_boolean(ot->srna,
619                          "deselect_all",
620                          false,
621                          "Deselect On Nothing",
622                          "Deselect all when nothing under the cursor");
623   RNA_def_property_flag(prop, PROP_SKIP_SAVE);
624   RNA_def_boolean(
625       ot->srna, "linked_handle", 0, "Linked Handle", "Select handles next to the active strip");
626   /* for animation this is an enum but atm having an enum isn't useful for us */
627   RNA_def_enum(ot->srna,
628                "left_right",
629                sequencer_select_left_right_types,
630                0,
631                "Left/Right",
632                "Select based on the current frame side the cursor is on");
633   RNA_def_boolean(
634       ot->srna, "linked_time", 0, "Linked Time", "Select other strips at the same time");
635 }
636
637 /* run recursively to select linked */
638 static bool select_more_less_seq__internal(Scene *scene, bool sel, const bool linked)
639 {
640   Editing *ed = BKE_sequencer_editing_get(scene, false);
641   Sequence *seq, *neighbor;
642   bool changed = false;
643   int isel;
644
645   if (ed == NULL) {
646     return changed;
647   }
648
649   if (sel) {
650     sel = SELECT;
651     isel = 0;
652   }
653   else {
654     sel = 0;
655     isel = SELECT;
656   }
657
658   if (!linked) {
659     /* if not linked we only want to touch each seq once, newseq */
660     for (seq = ed->seqbasep->first; seq; seq = seq->next) {
661       seq->tmp = NULL;
662     }
663   }
664
665   for (seq = ed->seqbasep->first; seq; seq = seq->next) {
666     if ((seq->flag & SELECT) == sel) {
667       if (linked || (seq->tmp == NULL)) {
668         /* only get unselected neighbors */
669         neighbor = find_neighboring_sequence(scene, seq, SEQ_SIDE_LEFT, isel);
670         if (neighbor) {
671           if (sel) {
672             neighbor->flag |= SELECT;
673             recurs_sel_seq(neighbor);
674           }
675           else {
676             neighbor->flag &= ~SELECT;
677           }
678           if (!linked) {
679             neighbor->tmp = (Sequence *)1;
680           }
681           changed = true;
682         }
683         neighbor = find_neighboring_sequence(scene, seq, SEQ_SIDE_RIGHT, isel);
684         if (neighbor) {
685           if (sel) {
686             neighbor->flag |= SELECT;
687             recurs_sel_seq(neighbor);
688           }
689           else {
690             neighbor->flag &= ~SELECT;
691           }
692           if (!linked) {
693             neighbor->tmp = (Sequence *)1;
694           }
695           changed = true;
696         }
697       }
698     }
699   }
700
701   return changed;
702 }
703
704 /* select more operator */
705 static int sequencer_select_more_exec(bContext *C, wmOperator *UNUSED(op))
706 {
707   Scene *scene = CTX_data_scene(C);
708
709   if (!select_more_less_seq__internal(scene, true, false)) {
710     return OPERATOR_CANCELLED;
711   }
712
713   ED_outliner_select_sync_from_sequence_tag(C);
714
715   WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER | NA_SELECTED, scene);
716
717   return OPERATOR_FINISHED;
718 }
719
720 void SEQUENCER_OT_select_more(wmOperatorType *ot)
721 {
722   /* identifiers */
723   ot->name = "Select More";
724   ot->idname = "SEQUENCER_OT_select_more";
725   ot->description = "Select more strips adjacent to the current selection";
726
727   /* api callbacks */
728   ot->exec = sequencer_select_more_exec;
729   ot->poll = sequencer_edit_poll;
730
731   /* flags */
732   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
733
734   /* properties */
735 }
736
737 /* select less operator */
738 static int sequencer_select_less_exec(bContext *C, wmOperator *UNUSED(op))
739 {
740   Scene *scene = CTX_data_scene(C);
741
742   if (!select_more_less_seq__internal(scene, false, false)) {
743     return OPERATOR_CANCELLED;
744   }
745
746   ED_outliner_select_sync_from_sequence_tag(C);
747
748   WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER | NA_SELECTED, scene);
749
750   return OPERATOR_FINISHED;
751 }
752
753 void SEQUENCER_OT_select_less(wmOperatorType *ot)
754 {
755   /* identifiers */
756   ot->name = "Select Less";
757   ot->idname = "SEQUENCER_OT_select_less";
758   ot->description = "Shrink the current selection of adjacent selected strips";
759
760   /* api callbacks */
761   ot->exec = sequencer_select_less_exec;
762   ot->poll = sequencer_edit_poll;
763
764   /* flags */
765   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
766
767   /* properties */
768 }
769
770 /* select pick linked operator (uses the mouse) */
771 static int sequencer_select_linked_pick_invoke(bContext *C, wmOperator *op, const wmEvent *event)
772 {
773   Scene *scene = CTX_data_scene(C);
774   View2D *v2d = UI_view2d_fromcontext(C);
775
776   bool extend = RNA_boolean_get(op->ptr, "extend");
777
778   Sequence *mouse_seq;
779   int selected, hand;
780
781   /* this works like UV, not mesh */
782   mouse_seq = find_nearest_seq(scene, v2d, &hand, event->mval);
783   if (!mouse_seq) {
784     return OPERATOR_FINISHED; /* user error as with mesh?? */
785   }
786
787   if (extend == 0) {
788     ED_sequencer_deselect_all(scene);
789   }
790
791   mouse_seq->flag |= SELECT;
792   recurs_sel_seq(mouse_seq);
793
794   selected = 1;
795   while (selected) {
796     selected = select_more_less_seq__internal(scene, 1, 1);
797   }
798
799   ED_outliner_select_sync_from_sequence_tag(C);
800
801   WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER | NA_SELECTED, scene);
802
803   return OPERATOR_FINISHED;
804 }
805
806 void SEQUENCER_OT_select_linked_pick(wmOperatorType *ot)
807 {
808   /* identifiers */
809   ot->name = "Select Pick Linked";
810   ot->idname = "SEQUENCER_OT_select_linked_pick";
811   ot->description = "Select a chain of linked strips nearest to the mouse pointer";
812
813   /* api callbacks */
814   ot->invoke = sequencer_select_linked_pick_invoke;
815   ot->poll = ED_operator_sequencer_active;
816
817   /* flags */
818   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
819
820   /* properties */
821   RNA_def_boolean(ot->srna, "extend", 0, "Extend", "Extend the selection");
822 }
823
824 /* select linked operator */
825 static int sequencer_select_linked_exec(bContext *C, wmOperator *UNUSED(op))
826 {
827   Scene *scene = CTX_data_scene(C);
828   bool selected;
829
830   selected = true;
831   while (selected) {
832     selected = select_more_less_seq__internal(scene, true, true);
833   }
834
835   ED_outliner_select_sync_from_sequence_tag(C);
836
837   WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER | NA_SELECTED, scene);
838
839   return OPERATOR_FINISHED;
840 }
841
842 void SEQUENCER_OT_select_linked(wmOperatorType *ot)
843 {
844   /* identifiers */
845   ot->name = "Select Linked";
846   ot->idname = "SEQUENCER_OT_select_linked";
847   ot->description = "Select all strips adjacent to the current selection";
848
849   /* api callbacks */
850   ot->exec = sequencer_select_linked_exec;
851   ot->poll = sequencer_edit_poll;
852
853   /* flags */
854   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
855
856   /* properties */
857 }
858
859 /* select handles operator */
860 static int sequencer_select_handles_exec(bContext *C, wmOperator *op)
861 {
862   Scene *scene = CTX_data_scene(C);
863   Editing *ed = BKE_sequencer_editing_get(scene, false);
864   Sequence *seq;
865   int sel_side = RNA_enum_get(op->ptr, "side");
866
867   for (seq = ed->seqbasep->first; seq; seq = seq->next) {
868     if (seq->flag & SELECT) {
869       switch (sel_side) {
870         case SEQ_SIDE_LEFT:
871           seq->flag &= ~SEQ_RIGHTSEL;
872           seq->flag |= SEQ_LEFTSEL;
873           break;
874         case SEQ_SIDE_RIGHT:
875           seq->flag &= ~SEQ_LEFTSEL;
876           seq->flag |= SEQ_RIGHTSEL;
877           break;
878         case SEQ_SIDE_BOTH:
879           seq->flag |= SEQ_LEFTSEL | SEQ_RIGHTSEL;
880           break;
881       }
882     }
883   }
884
885   ED_outliner_select_sync_from_sequence_tag(C);
886
887   WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER | NA_SELECTED, scene);
888
889   return OPERATOR_FINISHED;
890 }
891
892 void SEQUENCER_OT_select_handles(wmOperatorType *ot)
893 {
894   /* identifiers */
895   ot->name = "Select Handles";
896   ot->idname = "SEQUENCER_OT_select_handles";
897   ot->description = "Select gizmo handles on the sides of the selected strip";
898
899   /* api callbacks */
900   ot->exec = sequencer_select_handles_exec;
901   ot->poll = sequencer_edit_poll;
902
903   /* flags */
904   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
905
906   /* properties */
907   RNA_def_enum(ot->srna,
908                "side",
909                prop_side_types,
910                SEQ_SIDE_BOTH,
911                "Side",
912                "The side of the handle that is selected");
913 }
914
915 /* select side operator */
916 static int sequencer_select_active_side_exec(bContext *C, wmOperator *op)
917 {
918   Scene *scene = CTX_data_scene(C);
919   Editing *ed = BKE_sequencer_editing_get(scene, false);
920   Sequence *seq_act = BKE_sequencer_active_get(scene);
921
922   if (ed == NULL || seq_act == NULL) {
923     return OPERATOR_CANCELLED;
924   }
925
926   seq_act->flag |= SELECT;
927
928   select_active_side(
929       ed->seqbasep, RNA_enum_get(op->ptr, "side"), seq_act->machine, seq_act->startdisp);
930
931   ED_outliner_select_sync_from_sequence_tag(C);
932
933   WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER | NA_SELECTED, scene);
934
935   return OPERATOR_FINISHED;
936 }
937
938 void SEQUENCER_OT_select_active_side(wmOperatorType *ot)
939 {
940   /* identifiers */
941   ot->name = "Select Active Side";
942   ot->idname = "SEQUENCER_OT_select_active_side";
943   ot->description = "Select strips on the nominated side of the active strip";
944
945   /* api callbacks */
946   ot->exec = sequencer_select_active_side_exec;
947   ot->poll = sequencer_edit_poll;
948
949   /* flags */
950   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
951
952   /* properties */
953   RNA_def_enum(ot->srna,
954                "side",
955                prop_side_types,
956                SEQ_SIDE_BOTH,
957                "Side",
958                "The side of the handle that is selected");
959 }
960
961 /* box_select operator */
962 static int sequencer_box_select_exec(bContext *C, wmOperator *op)
963 {
964   Scene *scene = CTX_data_scene(C);
965   Editing *ed = BKE_sequencer_editing_get(scene, false);
966   if (ed == NULL) {
967     return OPERATOR_CANCELLED;
968   }
969
970   View2D *v2d = UI_view2d_fromcontext(C);
971
972   const eSelectOp sel_op = RNA_enum_get(op->ptr, "mode");
973   const bool select = (sel_op != SEL_OP_SUB);
974   if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
975     ED_sequencer_deselect_all(scene);
976   }
977
978   rctf rectf;
979   WM_operator_properties_border_to_rctf(op, &rectf);
980   UI_view2d_region_to_view_rctf(v2d, &rectf, &rectf);
981
982   for (Sequence *seq = ed->seqbasep->first; seq; seq = seq->next) {
983     rctf rq;
984     seq_rectf(seq, &rq);
985     if (BLI_rctf_isect(&rq, &rectf, NULL)) {
986       SET_FLAG_FROM_TEST(seq->flag, select, SELECT);
987       recurs_sel_seq(seq);
988     }
989   }
990
991   ED_outliner_select_sync_from_sequence_tag(C);
992
993   WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER | NA_SELECTED, scene);
994
995   return OPERATOR_FINISHED;
996 }
997
998 /* ****** Box Select ****** */
999 static int sequencer_box_select_invoke(bContext *C, wmOperator *op, const wmEvent *event)
1000 {
1001   Scene *scene = CTX_data_scene(C);
1002   View2D *v2d = &CTX_wm_region(C)->v2d;
1003
1004   const bool tweak = RNA_boolean_get(op->ptr, "tweak");
1005
1006   if (tweak) {
1007     int hand_dummy;
1008     Sequence *seq = find_nearest_seq(scene, v2d, &hand_dummy, event->mval);
1009     if (seq != NULL) {
1010       return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH;
1011     }
1012   }
1013
1014   return WM_gesture_box_invoke(C, op, event);
1015 }
1016
1017 void SEQUENCER_OT_select_box(wmOperatorType *ot)
1018 {
1019   /* identifiers */
1020   ot->name = "Box Select";
1021   ot->idname = "SEQUENCER_OT_select_box";
1022   ot->description = "Select strips using box selection";
1023
1024   /* api callbacks */
1025   ot->invoke = sequencer_box_select_invoke;
1026   ot->exec = sequencer_box_select_exec;
1027   ot->modal = WM_gesture_box_modal;
1028   ot->cancel = WM_gesture_box_cancel;
1029
1030   ot->poll = ED_operator_sequencer_active;
1031
1032   /* flags */
1033   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1034
1035   /* properties */
1036   WM_operator_properties_gesture_box(ot);
1037   WM_operator_properties_select_operation_simple(ot);
1038
1039   PropertyRNA *prop = RNA_def_boolean(
1040       ot->srna, "tweak", 0, "Tweak", "Operator has been activated using a tweak event");
1041   RNA_def_property_flag(prop, PROP_SKIP_SAVE);
1042 }
1043
1044 /* ****** Selected Grouped ****** */
1045
1046 enum {
1047   SEQ_SELECT_GROUP_TYPE,
1048   SEQ_SELECT_GROUP_TYPE_BASIC,
1049   SEQ_SELECT_GROUP_TYPE_EFFECT,
1050   SEQ_SELECT_GROUP_DATA,
1051   SEQ_SELECT_GROUP_EFFECT,
1052   SEQ_SELECT_GROUP_EFFECT_LINK,
1053   SEQ_SELECT_GROUP_OVERLAP,
1054 };
1055
1056 static const EnumPropertyItem sequencer_prop_select_grouped_types[] = {
1057     {SEQ_SELECT_GROUP_TYPE, "TYPE", 0, "Type", "Shared strip type"},
1058     {SEQ_SELECT_GROUP_TYPE_BASIC,
1059      "TYPE_BASIC",
1060      0,
1061      "Global Type",
1062      "All strips of same basic type (Graphical or Sound)"},
1063     {SEQ_SELECT_GROUP_TYPE_EFFECT,
1064      "TYPE_EFFECT",
1065      0,
1066      "Effect Type",
1067      "Shared strip effect type (if active strip is not an effect one, select all non-effect "
1068      "strips)"},
1069     {SEQ_SELECT_GROUP_DATA, "DATA", 0, "Data", "Shared data (scene, image, sound, etc.)"},
1070     {SEQ_SELECT_GROUP_EFFECT, "EFFECT", 0, "Effect", "Shared effects"},
1071     {SEQ_SELECT_GROUP_EFFECT_LINK,
1072      "EFFECT_LINK",
1073      0,
1074      "Effect/Linked",
1075      "Other strips affected by the active one (sharing some time, and below or effect-assigned)"},
1076     {SEQ_SELECT_GROUP_OVERLAP, "OVERLAP", 0, "Overlap", "Overlapping time"},
1077     {0, NULL, 0, NULL, NULL},
1078 };
1079
1080 #define SEQ_IS_SOUND(_seq) ((_seq->type & SEQ_TYPE_SOUND_RAM) && !(_seq->type & SEQ_TYPE_EFFECT))
1081
1082 #define SEQ_IS_EFFECT(_seq) ((_seq->type & SEQ_TYPE_EFFECT) != 0)
1083
1084 #define SEQ_USE_DATA(_seq) \
1085   (ELEM(_seq->type, SEQ_TYPE_SCENE, SEQ_TYPE_MOVIECLIP, SEQ_TYPE_MASK) || SEQ_HAS_PATH(_seq))
1086
1087 #define SEQ_CHANNEL_CHECK(_seq, _chan) (ELEM((_chan), 0, (_seq)->machine))
1088
1089 static bool select_grouped_type(Editing *ed, Sequence *actseq, const int channel)
1090 {
1091   Sequence *seq;
1092   bool changed = false;
1093
1094   SEQP_BEGIN (ed, seq) {
1095     if (SEQ_CHANNEL_CHECK(seq, channel) && seq->type == actseq->type) {
1096       seq->flag |= SELECT;
1097       changed = true;
1098     }
1099   }
1100   SEQ_END;
1101
1102   return changed;
1103 }
1104
1105 static bool select_grouped_type_basic(Editing *ed, Sequence *actseq, const int channel)
1106 {
1107   Sequence *seq;
1108   bool changed = false;
1109   const bool is_sound = SEQ_IS_SOUND(actseq);
1110
1111   SEQP_BEGIN (ed, seq) {
1112     if (SEQ_CHANNEL_CHECK(seq, channel) && (is_sound ? SEQ_IS_SOUND(seq) : !SEQ_IS_SOUND(seq))) {
1113       seq->flag |= SELECT;
1114       changed = true;
1115     }
1116   }
1117   SEQ_END;
1118
1119   return changed;
1120 }
1121
1122 static bool select_grouped_type_effect(Editing *ed, Sequence *actseq, const int channel)
1123 {
1124   Sequence *seq;
1125   bool changed = false;
1126   const bool is_effect = SEQ_IS_EFFECT(actseq);
1127
1128   SEQP_BEGIN (ed, seq) {
1129     if (SEQ_CHANNEL_CHECK(seq, channel) &&
1130         (is_effect ? SEQ_IS_EFFECT(seq) : !SEQ_IS_EFFECT(seq))) {
1131       seq->flag |= SELECT;
1132       changed = true;
1133     }
1134   }
1135   SEQ_END;
1136
1137   return changed;
1138 }
1139
1140 static bool select_grouped_data(Editing *ed, Sequence *actseq, const int channel)
1141 {
1142   Sequence *seq;
1143   bool changed = false;
1144   const char *dir = actseq->strip ? actseq->strip->dir : NULL;
1145
1146   if (!SEQ_USE_DATA(actseq)) {
1147     return changed;
1148   }
1149
1150   if (SEQ_HAS_PATH(actseq) && dir) {
1151     SEQP_BEGIN (ed, seq) {
1152       if (SEQ_CHANNEL_CHECK(seq, channel) && SEQ_HAS_PATH(seq) && seq->strip &&
1153           STREQ(seq->strip->dir, dir)) {
1154         seq->flag |= SELECT;
1155         changed = true;
1156       }
1157     }
1158     SEQ_END;
1159   }
1160   else if (actseq->type == SEQ_TYPE_SCENE) {
1161     Scene *sce = actseq->scene;
1162     SEQP_BEGIN (ed, seq) {
1163       if (SEQ_CHANNEL_CHECK(seq, channel) && seq->type == SEQ_TYPE_SCENE && seq->scene == sce) {
1164         seq->flag |= SELECT;
1165         changed = true;
1166       }
1167     }
1168     SEQ_END;
1169   }
1170   else if (actseq->type == SEQ_TYPE_MOVIECLIP) {
1171     MovieClip *clip = actseq->clip;
1172     SEQP_BEGIN (ed, seq) {
1173       if (SEQ_CHANNEL_CHECK(seq, channel) && seq->type == SEQ_TYPE_MOVIECLIP &&
1174           seq->clip == clip) {
1175         seq->flag |= SELECT;
1176         changed = true;
1177       }
1178     }
1179     SEQ_END;
1180   }
1181   else if (actseq->type == SEQ_TYPE_MASK) {
1182     struct Mask *mask = actseq->mask;
1183     SEQP_BEGIN (ed, seq) {
1184       if (SEQ_CHANNEL_CHECK(seq, channel) && seq->type == SEQ_TYPE_MASK && seq->mask == mask) {
1185         seq->flag |= SELECT;
1186         changed = true;
1187       }
1188     }
1189     SEQ_END;
1190   }
1191
1192   return changed;
1193 }
1194
1195 static bool select_grouped_effect(Editing *ed, Sequence *actseq, const int channel)
1196 {
1197   Sequence *seq;
1198   bool changed = false;
1199   bool effects[SEQ_TYPE_MAX + 1];
1200   int i;
1201
1202   for (i = 0; i <= SEQ_TYPE_MAX; i++) {
1203     effects[i] = false;
1204   }
1205
1206   SEQP_BEGIN (ed, seq) {
1207     if (SEQ_CHANNEL_CHECK(seq, channel) && (seq->type & SEQ_TYPE_EFFECT) &&
1208         ELEM(actseq, seq->seq1, seq->seq2, seq->seq3)) {
1209       effects[seq->type] = true;
1210     }
1211   }
1212   SEQ_END;
1213
1214   SEQP_BEGIN (ed, seq) {
1215     if (SEQ_CHANNEL_CHECK(seq, channel) && effects[seq->type]) {
1216       if (seq->seq1) {
1217         seq->seq1->flag |= SELECT;
1218       }
1219       if (seq->seq2) {
1220         seq->seq2->flag |= SELECT;
1221       }
1222       if (seq->seq3) {
1223         seq->seq3->flag |= SELECT;
1224       }
1225       changed = true;
1226     }
1227   }
1228   SEQ_END;
1229
1230   return changed;
1231 }
1232
1233 static bool select_grouped_time_overlap(Editing *ed, Sequence *actseq)
1234 {
1235   Sequence *seq;
1236   bool changed = false;
1237
1238   SEQP_BEGIN (ed, seq) {
1239     if (!((seq->startdisp >= actseq->enddisp) || (seq->enddisp < actseq->startdisp))) {
1240       seq->flag |= SELECT;
1241       changed = true;
1242     }
1243   }
1244   SEQ_END;
1245
1246   return changed;
1247 }
1248
1249 static bool select_grouped_effect_link(Editing *ed, Sequence *actseq, const int channel)
1250 {
1251   Sequence *seq = NULL;
1252   bool changed = false;
1253   const bool is_audio = ((actseq->type == SEQ_TYPE_META) || SEQ_IS_SOUND(actseq));
1254   int startdisp = actseq->startdisp;
1255   int enddisp = actseq->enddisp;
1256   int machine = actseq->machine;
1257   SeqIterator iter;
1258
1259   SEQP_BEGIN (ed, seq) {
1260     seq->tmp = NULL;
1261   }
1262   SEQ_END;
1263
1264   actseq->tmp = POINTER_FROM_INT(true);
1265
1266   for (BKE_sequence_iterator_begin(ed, &iter, true); iter.valid;
1267        BKE_sequence_iterator_next(&iter)) {
1268     seq = iter.seq;
1269
1270     /* Ignore all seqs already selected! */
1271     /* Ignore all seqs not sharing some time with active one. */
1272     /* Ignore all seqs of incompatible types (audio vs video). */
1273     if (!SEQ_CHANNEL_CHECK(seq, channel) || (seq->flag & SELECT) || (seq->startdisp >= enddisp) ||
1274         (seq->enddisp < startdisp) || (!is_audio && SEQ_IS_SOUND(seq)) ||
1275         (is_audio && !((seq->type == SEQ_TYPE_META) || SEQ_IS_SOUND(seq)))) {
1276       continue;
1277     }
1278
1279     /* If the seq is an effect one, we need extra checking! */
1280     if (SEQ_IS_EFFECT(seq) && ((seq->seq1 && seq->seq1->tmp) || (seq->seq2 && seq->seq2->tmp) ||
1281                                (seq->seq3 && seq->seq3->tmp))) {
1282       if (startdisp > seq->startdisp) {
1283         startdisp = seq->startdisp;
1284       }
1285       if (enddisp < seq->enddisp) {
1286         enddisp = seq->enddisp;
1287       }
1288       if (machine < seq->machine) {
1289         machine = seq->machine;
1290       }
1291
1292       seq->tmp = POINTER_FROM_INT(true);
1293
1294       seq->flag |= SELECT;
1295       changed = true;
1296
1297       /* Unfortunately, we must restart checks from the beginning. */
1298       BKE_sequence_iterator_end(&iter);
1299       BKE_sequence_iterator_begin(ed, &iter, true);
1300     }
1301
1302     /* Video strips below active one, or any strip for audio (order do no matters here!). */
1303     else if (seq->machine < machine || is_audio) {
1304       seq->flag |= SELECT;
1305       changed = true;
1306     }
1307   }
1308   BKE_sequence_iterator_end(&iter);
1309
1310   return changed;
1311 }
1312
1313 #undef SEQ_IS_SOUND
1314 #undef SEQ_IS_EFFECT
1315 #undef SEQ_USE_DATA
1316
1317 static int sequencer_select_grouped_exec(bContext *C, wmOperator *op)
1318 {
1319   Scene *scene = CTX_data_scene(C);
1320   Editing *ed = BKE_sequencer_editing_get(scene, false);
1321   Sequence *seq, *actseq = BKE_sequencer_active_get(scene);
1322
1323   const int type = RNA_enum_get(op->ptr, "type");
1324   const int channel = RNA_boolean_get(op->ptr, "use_active_channel") ? actseq->machine : 0;
1325   const bool extend = RNA_boolean_get(op->ptr, "extend");
1326
1327   bool changed = false;
1328
1329   if (actseq == NULL) {
1330     BKE_report(op->reports, RPT_ERROR, "No active sequence!");
1331     return OPERATOR_CANCELLED;
1332   }
1333
1334   if (!extend) {
1335     SEQP_BEGIN (ed, seq) {
1336       seq->flag &= ~SELECT;
1337       changed = true;
1338     }
1339     SEQ_END;
1340   }
1341
1342   switch (type) {
1343     case SEQ_SELECT_GROUP_TYPE:
1344       changed |= select_grouped_type(ed, actseq, channel);
1345       break;
1346     case SEQ_SELECT_GROUP_TYPE_BASIC:
1347       changed |= select_grouped_type_basic(ed, actseq, channel);
1348       break;
1349     case SEQ_SELECT_GROUP_TYPE_EFFECT:
1350       changed |= select_grouped_type_effect(ed, actseq, channel);
1351       break;
1352     case SEQ_SELECT_GROUP_DATA:
1353       changed |= select_grouped_data(ed, actseq, channel);
1354       break;
1355     case SEQ_SELECT_GROUP_EFFECT:
1356       changed |= select_grouped_effect(ed, actseq, channel);
1357       break;
1358     case SEQ_SELECT_GROUP_EFFECT_LINK:
1359       changed |= select_grouped_effect_link(ed, actseq, channel);
1360       break;
1361     case SEQ_SELECT_GROUP_OVERLAP:
1362       changed |= select_grouped_time_overlap(ed, actseq);
1363       break;
1364     default:
1365       BLI_assert(0);
1366       break;
1367   }
1368
1369   if (changed) {
1370     ED_outliner_select_sync_from_sequence_tag(C);
1371     WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER | NA_SELECTED, scene);
1372     return OPERATOR_FINISHED;
1373   }
1374
1375   return OPERATOR_CANCELLED;
1376 }
1377
1378 void SEQUENCER_OT_select_grouped(wmOperatorType *ot)
1379 {
1380   /* identifiers */
1381   ot->name = "Select Grouped";
1382   ot->description = "Select all strips grouped by various properties";
1383   ot->idname = "SEQUENCER_OT_select_grouped";
1384
1385   /* api callbacks */
1386   ot->invoke = WM_menu_invoke;
1387   ot->exec = sequencer_select_grouped_exec;
1388   ot->poll = sequencer_edit_poll;
1389
1390   /* flags */
1391   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1392
1393   /* properties */
1394   ot->prop = RNA_def_enum(ot->srna, "type", sequencer_prop_select_grouped_types, 0, "Type", "");
1395   RNA_def_boolean(ot->srna,
1396                   "extend",
1397                   false,
1398                   "Extend",
1399                   "Extend selection instead of deselecting everything first");
1400   RNA_def_boolean(ot->srna,
1401                   "use_active_channel",
1402                   false,
1403                   "Same Channel",
1404                   "Only consider strips on the same channel as the active one");
1405 }