Fix T69931: Materials with keyframes duplicated by 'make single user' are linked.
[blender.git] / source / blender / editors / space_nla / nla_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) 2009 Blender Foundation, Joshua Leung
17  * All rights reserved.
18  */
19
20 /** \file
21  * \ingroup spnla
22  */
23
24 #include <string.h>
25 #include <stdio.h>
26
27 #include "DNA_anim_types.h"
28 #include "DNA_scene_types.h"
29
30 #include "MEM_guardedalloc.h"
31
32 #include "BLI_blenlib.h"
33
34 #include "BKE_nla.h"
35 #include "BKE_context.h"
36 #include "BKE_screen.h"
37
38 #include "ED_anim_api.h"
39 #include "ED_keyframes_edit.h"
40 #include "ED_screen.h"
41 #include "ED_select_utils.h"
42
43 #include "RNA_access.h"
44 #include "RNA_define.h"
45
46 #include "WM_api.h"
47 #include "WM_types.h"
48
49 #include "UI_view2d.h"
50 #include "UI_interface.h"
51
52 #include "nla_intern.h"  // own include
53
54 /* ******************** Utilities ***************************************** */
55
56 /* Convert SELECT_* flags to ACHANNEL_SETFLAG_* flags */
57 static short selmodes_to_flagmodes(short sel)
58 {
59   /* convert selection modes to selection modes */
60   switch (sel) {
61     case SELECT_SUBTRACT:
62       return ACHANNEL_SETFLAG_CLEAR;
63
64     case SELECT_INVERT:
65       return ACHANNEL_SETFLAG_INVERT;
66
67     case SELECT_ADD:
68     default:
69       return ACHANNEL_SETFLAG_ADD;
70   }
71 }
72
73 /* ******************** Deselect All Operator ***************************** */
74 /* This operator works in one of three ways:
75  * 1) (de)select all (AKEY) - test if select all or deselect all
76  * 2) invert all (CTRL-IKEY) - invert selection of all keyframes
77  * 3) (de)select all - no testing is done; only for use internal tools as normal function...
78  */
79
80 enum {
81   DESELECT_STRIPS_NOTEST = 0,
82   DESELECT_STRIPS_TEST,
83   DESELECT_STRIPS_CLEARACTIVE,
84 } /*eDeselectNlaStrips*/;
85
86 /* Deselects strips in the NLA Editor
87  * - This is called by the deselect all operator, as well as other ones!
88  *
89  * - test: check if select or deselect all (1) or clear all active (2)
90  * - sel: how to select keyframes
91  * 0 = deselect
92  * 1 = select
93  * 2 = invert
94  */
95 static void deselect_nla_strips(bAnimContext *ac, short test, short sel)
96 {
97   ListBase anim_data = {NULL, NULL};
98   bAnimListElem *ale;
99   int filter;
100   short smode;
101
102   /* determine type-based settings */
103   // FIXME: double check whether ANIMFILTER_LIST_VISIBLE is needed!
104   filter = (ANIMFILTER_DATA_VISIBLE);
105
106   /* filter data */
107   ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
108
109   /* See if we should be selecting or deselecting */
110   if (test == DESELECT_STRIPS_TEST) {
111     for (ale = anim_data.first; ale; ale = ale->next) {
112       NlaTrack *nlt = (NlaTrack *)ale->data;
113       NlaStrip *strip;
114
115       /* if any strip is selected, break out, since we should now be deselecting */
116       for (strip = nlt->strips.first; strip; strip = strip->next) {
117         if (strip->flag & NLASTRIP_FLAG_SELECT) {
118           sel = SELECT_SUBTRACT;
119           break;
120         }
121       }
122
123       if (sel == SELECT_SUBTRACT) {
124         break;
125       }
126     }
127   }
128
129   /* convert selection modes to selection modes */
130   smode = selmodes_to_flagmodes(sel);
131
132   /* Now set the flags */
133   for (ale = anim_data.first; ale; ale = ale->next) {
134     NlaTrack *nlt = (NlaTrack *)ale->data;
135     NlaStrip *strip;
136
137     /* apply same selection to all strips */
138     for (strip = nlt->strips.first; strip; strip = strip->next) {
139       /* set selection */
140       if (test != DESELECT_STRIPS_CLEARACTIVE) {
141         ACHANNEL_SET_FLAG(strip, smode, NLASTRIP_FLAG_SELECT);
142       }
143
144       /* clear active flag */
145       /* TODO: for clear active,
146        * do we want to limit this to only doing this on a certain set of tracks though? */
147       strip->flag &= ~NLASTRIP_FLAG_ACTIVE;
148     }
149   }
150
151   /* Cleanup */
152   ANIM_animdata_freelist(&anim_data);
153 }
154
155 /* ------------------- */
156
157 static int nlaedit_deselectall_exec(bContext *C, wmOperator *op)
158 {
159   bAnimContext ac;
160
161   /* get editor data */
162   if (ANIM_animdata_get_context(C, &ac) == 0) {
163     return OPERATOR_CANCELLED;
164   }
165
166   /* 'standard' behavior - check if selected, then apply relevant selection */
167   const int action = RNA_enum_get(op->ptr, "action");
168   switch (action) {
169     case SEL_TOGGLE:
170       deselect_nla_strips(&ac, DESELECT_STRIPS_TEST, SELECT_ADD);
171       break;
172     case SEL_SELECT:
173       deselect_nla_strips(&ac, DESELECT_STRIPS_NOTEST, SELECT_ADD);
174       break;
175     case SEL_DESELECT:
176       deselect_nla_strips(&ac, DESELECT_STRIPS_NOTEST, SELECT_SUBTRACT);
177       break;
178     case SEL_INVERT:
179       deselect_nla_strips(&ac, DESELECT_STRIPS_NOTEST, SELECT_INVERT);
180       break;
181     default:
182       BLI_assert(0);
183       break;
184   }
185
186   /* set notifier that things have changed */
187   WM_event_add_notifier(C, NC_ANIMATION | ND_NLA | NA_SELECTED, NULL);
188
189   return OPERATOR_FINISHED;
190 }
191
192 void NLA_OT_select_all(wmOperatorType *ot)
193 {
194   /* identifiers */
195   ot->name = "(De)select All";
196   ot->idname = "NLA_OT_select_all";
197   ot->description = "Select or deselect all NLA-Strips";
198
199   /* api callbacks */
200   ot->exec = nlaedit_deselectall_exec;
201   ot->poll = nlaop_poll_tweakmode_off;
202
203   /* flags */
204   ot->flag = OPTYPE_REGISTER /*|OPTYPE_UNDO*/;
205
206   /* properties */
207   WM_operator_properties_select_all(ot);
208 }
209
210 /* ******************** Box Select Operator **************************** */
211 /**
212  * This operator currently works in one of three ways:
213  * - BKEY     - 1: all strips within region are selected #NLAEDIT_BOX_ALLSTRIPS.
214  * - ALT-BKEY - depending on which axis of the region was larger.
215  *   - 2: x-axis, so select all frames within frame range #NLAEDIT_BOXSEL_FRAMERANGE.
216  *   - 3: y-axis, so select all frames within channels that region included
217  *     #NLAEDIT_BOXSEL_CHANNELS.
218  */
219
220 /* defines for box_select mode */
221 enum {
222   NLA_BOXSEL_ALLSTRIPS = 0,
223   NLA_BOXSEL_FRAMERANGE,
224   NLA_BOXSEL_CHANNELS,
225 } /* eNLAEDIT_BoxSelect_Mode */;
226
227 static void box_select_nla_strips(bAnimContext *ac, rcti rect, short mode, short selectmode)
228 {
229   ListBase anim_data = {NULL, NULL};
230   bAnimListElem *ale;
231   int filter;
232
233   SpaceNla *snla = (SpaceNla *)ac->sl;
234   View2D *v2d = &ac->ar->v2d;
235   rctf rectf;
236
237   /* convert border-region to view coordinates */
238   UI_view2d_region_to_view(v2d, rect.xmin, rect.ymin + 2, &rectf.xmin, &rectf.ymin);
239   UI_view2d_region_to_view(v2d, rect.xmax, rect.ymax - 2, &rectf.xmax, &rectf.ymax);
240
241   /* filter data */
242   filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_LIST_CHANNELS);
243   ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
244
245   /* convert selection modes to selection modes */
246   selectmode = selmodes_to_flagmodes(selectmode);
247
248   /* loop over data, doing box select */
249   float ymax = NLACHANNEL_FIRST_TOP(ac);
250   for (ale = anim_data.first; ale; ale = ale->next, ymax -= NLACHANNEL_STEP(snla)) {
251     float ymin = ymax - NLACHANNEL_HEIGHT(snla);
252
253     /* perform vertical suitability check (if applicable) */
254     if ((mode == NLA_BOXSEL_FRAMERANGE) || !((ymax < rectf.ymin) || (ymin > rectf.ymax))) {
255       /* loop over data selecting (only if NLA-Track) */
256       if (ale->type == ANIMTYPE_NLATRACK) {
257         NlaTrack *nlt = (NlaTrack *)ale->data;
258         NlaStrip *strip;
259
260         /* only select strips if they fall within the required ranges (if applicable) */
261         for (strip = nlt->strips.first; strip; strip = strip->next) {
262           if ((mode == NLA_BOXSEL_CHANNELS) ||
263               BKE_nlastrip_within_bounds(strip, rectf.xmin, rectf.xmax)) {
264             /* set selection */
265             ACHANNEL_SET_FLAG(strip, selectmode, NLASTRIP_FLAG_SELECT);
266
267             /* clear active flag */
268             strip->flag &= ~NLASTRIP_FLAG_ACTIVE;
269           }
270         }
271       }
272     }
273   }
274
275   /* cleanup */
276   ANIM_animdata_freelist(&anim_data);
277 }
278
279 /* ------------------- */
280
281 static void nlaedit_strip_at_region_position(
282     bAnimContext *ac, float region_x, float region_y, bAnimListElem **r_ale, NlaStrip **r_strip)
283 {
284   *r_ale = NULL;
285   *r_strip = NULL;
286
287   SpaceNla *snla = (SpaceNla *)ac->sl;
288   View2D *v2d = &ac->ar->v2d;
289
290   float view_x, view_y;
291   int channel_index;
292   UI_view2d_region_to_view(v2d, region_x, region_y, &view_x, &view_y);
293   UI_view2d_listview_view_to_cell(
294       0, NLACHANNEL_STEP(snla), 0, NLACHANNEL_FIRST_TOP(ac), view_x, view_y, NULL, &channel_index);
295
296   ListBase anim_data = {NULL, NULL};
297   int filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_LIST_CHANNELS);
298   ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
299
300   /* x-range to check is +/- 7 (in screen/region-space) on either side of mouse click
301    * (that is the size of keyframe icons, so user should be expecting similar tolerances)
302    */
303   float xmin = UI_view2d_region_to_view_x(v2d, region_x - 7);
304   float xmax = UI_view2d_region_to_view_x(v2d, region_x + 7);
305
306   bAnimListElem *ale = BLI_findlink(&anim_data, channel_index);
307   if (ale != NULL) {
308     if (ale->type == ANIMTYPE_NLATRACK) {
309       NlaTrack *nlt = (NlaTrack *)ale->data;
310
311       for (NlaStrip *strip = nlt->strips.first; strip; strip = strip->next) {
312         if (BKE_nlastrip_within_bounds(strip, xmin, xmax)) {
313           *r_ale = ale;
314           *r_strip = strip;
315
316           BLI_remlink(&anim_data, ale);
317         }
318       }
319     }
320   }
321
322   ANIM_animdata_freelist(&anim_data);
323 }
324
325 static bool nlaedit_mouse_is_over_strip(bAnimContext *ac, const int mval[2])
326 {
327   bAnimListElem *ale;
328   NlaStrip *strip;
329   nlaedit_strip_at_region_position(ac, mval[0], mval[1], &ale, &strip);
330
331   if (ale != NULL) {
332     BLI_assert(strip != NULL);
333     MEM_freeN(ale);
334     return true;
335   }
336   return false;
337 }
338
339 static int nlaedit_box_select_invoke(bContext *C, wmOperator *op, const wmEvent *event)
340 {
341   bAnimContext ac;
342   if (ANIM_animdata_get_context(C, &ac) == 0) {
343     return OPERATOR_CANCELLED;
344   }
345
346   bool tweak = RNA_boolean_get(op->ptr, "tweak");
347   if (tweak && nlaedit_mouse_is_over_strip(&ac, event->mval)) {
348     return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH;
349   }
350   return WM_gesture_box_invoke(C, op, event);
351 }
352
353 static int nlaedit_box_select_exec(bContext *C, wmOperator *op)
354 {
355   bAnimContext ac;
356   rcti rect;
357   short mode = 0;
358
359   /* get editor data */
360   if (ANIM_animdata_get_context(C, &ac) == 0) {
361     return OPERATOR_CANCELLED;
362   }
363
364   const eSelectOp sel_op = RNA_enum_get(op->ptr, "mode");
365   const int selectmode = (sel_op != SEL_OP_SUB) ? SELECT_ADD : SELECT_SUBTRACT;
366   if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
367     deselect_nla_strips(&ac, DESELECT_STRIPS_TEST, SELECT_SUBTRACT);
368   }
369
370   /* get settings from operator */
371   WM_operator_properties_border_to_rcti(op, &rect);
372
373   /* selection 'mode' depends on whether box_select region only matters on one axis */
374   if (RNA_boolean_get(op->ptr, "axis_range")) {
375     /* mode depends on which axis of the range is larger to determine which axis to use.
376      * - Checking this in region-space is fine,
377      *   as it's fundamentally still going to be a different rect size.
378      * - The frame-range select option is favored over the channel one (x over y),
379      *   as frame-range one is often.
380      *   Used for tweaking timing when "blocking", while channels is not that useful.
381      */
382     if (BLI_rcti_size_x(&rect) >= BLI_rcti_size_y(&rect)) {
383       mode = NLA_BOXSEL_FRAMERANGE;
384     }
385     else {
386       mode = NLA_BOXSEL_CHANNELS;
387     }
388   }
389   else {
390     mode = NLA_BOXSEL_ALLSTRIPS;
391   }
392
393   /* apply box_select action */
394   box_select_nla_strips(&ac, rect, mode, selectmode);
395
396   /* set notifier that things have changed */
397   WM_event_add_notifier(C, NC_ANIMATION | ND_NLA | NA_SELECTED, NULL);
398
399   return OPERATOR_FINISHED;
400 }
401
402 void NLA_OT_select_box(wmOperatorType *ot)
403 {
404   /* identifiers */
405   ot->name = "Box Select";
406   ot->idname = "NLA_OT_select_box";
407   ot->description = "Use box selection to grab NLA-Strips";
408
409   /* api callbacks */
410   ot->invoke = nlaedit_box_select_invoke;
411   ot->exec = nlaedit_box_select_exec;
412   ot->modal = WM_gesture_box_modal;
413   ot->cancel = WM_gesture_box_cancel;
414
415   ot->poll = nlaop_poll_tweakmode_off;
416
417   /* flags */
418   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
419
420   /* properties */
421   RNA_def_boolean(ot->srna, "axis_range", 0, "Axis Range", "");
422
423   PropertyRNA *prop = RNA_def_boolean(
424       ot->srna, "tweak", 0, "Tweak", "Operator has been activated using a tweak event");
425   RNA_def_property_flag(prop, PROP_SKIP_SAVE);
426
427   WM_operator_properties_gesture_box(ot);
428   WM_operator_properties_select_operation_simple(ot);
429 }
430
431 /* ******************** Select Left/Right Operator ************************* */
432 /* Select keyframes left/right of the current frame indicator */
433
434 /* defines for left-right select tool */
435 static const EnumPropertyItem prop_nlaedit_leftright_select_types[] = {
436     {NLAEDIT_LRSEL_TEST, "CHECK", 0, "Check if Select Left or Right", ""},
437     {NLAEDIT_LRSEL_LEFT, "LEFT", 0, "Before current frame", ""},
438     {NLAEDIT_LRSEL_RIGHT, "RIGHT", 0, "After current frame", ""},
439     {0, NULL, 0, NULL, NULL},
440 };
441
442 /* ------------------- */
443
444 static void nlaedit_select_leftright(bContext *C,
445                                      bAnimContext *ac,
446                                      short leftright,
447                                      short select_mode)
448 {
449   ListBase anim_data = {NULL, NULL};
450   bAnimListElem *ale;
451   int filter;
452
453   Scene *scene = ac->scene;
454   float xmin, xmax;
455
456   /* if currently in tweakmode, exit tweakmode first */
457   if (scene->flag & SCE_NLA_EDIT_ON) {
458     WM_operator_name_call(C, "NLA_OT_tweakmode_exit", WM_OP_EXEC_DEFAULT, NULL);
459   }
460
461   /* if select mode is replace, deselect all keyframes (and channels) first */
462   if (select_mode == SELECT_REPLACE) {
463     select_mode = SELECT_ADD;
464
465     /* - deselect all other keyframes, so that just the newly selected remain
466      * - channels aren't deselected, since we don't re-select any as a consequence
467      */
468     deselect_nla_strips(ac, 0, SELECT_SUBTRACT);
469   }
470
471   /* get range, and get the right flag-setting mode */
472   if (leftright == NLAEDIT_LRSEL_LEFT) {
473     xmin = MINAFRAMEF;
474     xmax = (float)(CFRA + 0.1f);
475   }
476   else {
477     xmin = (float)(CFRA - 0.1f);
478     xmax = MAXFRAMEF;
479   }
480
481   select_mode = selmodes_to_flagmodes(select_mode);
482
483   /* filter data */
484   filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE);
485   ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
486
487   /* select strips on the side where most data occurs */
488   for (ale = anim_data.first; ale; ale = ale->next) {
489     NlaTrack *nlt = (NlaTrack *)ale->data;
490     NlaStrip *strip;
491
492     /* check each strip to see if it is appropriate */
493     for (strip = nlt->strips.first; strip; strip = strip->next) {
494       if (BKE_nlastrip_within_bounds(strip, xmin, xmax)) {
495         ACHANNEL_SET_FLAG(strip, select_mode, NLASTRIP_FLAG_SELECT);
496       }
497     }
498   }
499
500   /* Cleanup */
501   ANIM_animdata_freelist(&anim_data);
502 }
503
504 /* ------------------- */
505
506 static int nlaedit_select_leftright_exec(bContext *C, wmOperator *op)
507 {
508   bAnimContext ac;
509   short leftright = RNA_enum_get(op->ptr, "mode");
510   short selectmode;
511
512   /* get editor data */
513   if (ANIM_animdata_get_context(C, &ac) == 0) {
514     return OPERATOR_CANCELLED;
515   }
516
517   /* select mode is either replace (deselect all, then add) or add/extend */
518   if (RNA_boolean_get(op->ptr, "extend")) {
519     selectmode = SELECT_INVERT;
520   }
521   else {
522     selectmode = SELECT_REPLACE;
523   }
524
525   /* if "test" mode is set, we don't have any info to set this with */
526   if (leftright == NLAEDIT_LRSEL_TEST) {
527     return OPERATOR_CANCELLED;
528   }
529
530   /* do the selecting now */
531   nlaedit_select_leftright(C, &ac, leftright, selectmode);
532
533   /* set notifier that keyframe selection (and channels too) have changed */
534   WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_SELECTED, NULL);
535   WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_SELECTED, NULL);
536
537   return OPERATOR_FINISHED;
538 }
539
540 static int nlaedit_select_leftright_invoke(bContext *C, wmOperator *op, const wmEvent *event)
541 {
542   bAnimContext ac;
543   short leftright = RNA_enum_get(op->ptr, "mode");
544
545   /* get editor data */
546   if (ANIM_animdata_get_context(C, &ac) == 0) {
547     return OPERATOR_CANCELLED;
548   }
549
550   /* handle mode-based testing */
551   if (leftright == NLAEDIT_LRSEL_TEST) {
552     Scene *scene = ac.scene;
553     ARegion *ar = ac.ar;
554     View2D *v2d = &ar->v2d;
555     float x;
556
557     /* determine which side of the current frame mouse is on */
558     x = UI_view2d_region_to_view_x(v2d, event->mval[0]);
559     if (x < CFRA) {
560       RNA_enum_set(op->ptr, "mode", NLAEDIT_LRSEL_LEFT);
561     }
562     else {
563       RNA_enum_set(op->ptr, "mode", NLAEDIT_LRSEL_RIGHT);
564     }
565   }
566
567   /* perform selection */
568   return nlaedit_select_leftright_exec(C, op);
569 }
570
571 void NLA_OT_select_leftright(wmOperatorType *ot)
572 {
573   PropertyRNA *prop;
574
575   /* identifiers */
576   ot->name = "Select Left/Right";
577   ot->idname = "NLA_OT_select_leftright";
578   ot->description = "Select strips to the left or the right of the current frame";
579
580   /* api callbacks  */
581   ot->invoke = nlaedit_select_leftright_invoke;
582   ot->exec = nlaedit_select_leftright_exec;
583   ot->poll = ED_operator_nla_active;
584
585   /* flags */
586   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
587
588   /* properties */
589   ot->prop = RNA_def_enum(
590       ot->srna, "mode", prop_nlaedit_leftright_select_types, NLAEDIT_LRSEL_TEST, "Mode", "");
591   RNA_def_property_flag(ot->prop, PROP_SKIP_SAVE);
592
593   prop = RNA_def_boolean(ot->srna, "extend", 0, "Extend Select", "");
594   RNA_def_property_flag(prop, PROP_SKIP_SAVE);
595 }
596
597 /* ******************** Mouse-Click Select Operator *********************** */
598
599 /* select strip directly under mouse */
600 static void mouse_nla_strips(
601     bContext *C, bAnimContext *ac, const int mval[2], short select_mode, const bool deselect_all)
602 {
603   Scene *scene = ac->scene;
604
605   bAnimListElem *ale = NULL;
606   NlaStrip *strip = NULL;
607   nlaedit_strip_at_region_position(ac, mval[0], mval[1], &ale, &strip);
608
609   /* if currently in tweakmode, exit tweakmode before changing selection states
610    * now that we've found our target...
611    */
612   if (scene->flag & SCE_NLA_EDIT_ON) {
613     WM_operator_name_call(C, "NLA_OT_tweakmode_exit", WM_OP_EXEC_DEFAULT, NULL);
614   }
615
616   /* For replacing selection, if we have something to select, we have to clear existing selection.
617    * The same goes if we found nothing to select, and deselect_all is true
618    * (deselect on nothing behavior). */
619   if ((strip != NULL && select_mode == SELECT_REPLACE) || (strip == NULL && deselect_all)) {
620     /* reset selection mode for next steps */
621     select_mode = SELECT_ADD;
622
623     /* deselect all strips */
624     deselect_nla_strips(ac, 0, SELECT_SUBTRACT);
625
626     /* deselect all other channels first */
627     ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, 0, ACHANNEL_SETFLAG_CLEAR);
628   }
629
630   /* only select strip if we clicked on a valid channel and hit something */
631   if (ale != NULL) {
632     /* select the strip accordingly (if a matching one was found) */
633     if (strip != NULL) {
634       select_mode = selmodes_to_flagmodes(select_mode);
635       ACHANNEL_SET_FLAG(strip, select_mode, NLASTRIP_FLAG_SELECT);
636
637       /* if we selected it, we can make it active too
638        * - we always need to clear the active strip flag though...
639        * - as well as selecting its track...
640        */
641       deselect_nla_strips(ac, DESELECT_STRIPS_CLEARACTIVE, 0);
642
643       if (strip->flag & NLASTRIP_FLAG_SELECT) {
644         strip->flag |= NLASTRIP_FLAG_ACTIVE;
645
646         /* Highlight NLA-Track */
647         if (ale->type == ANIMTYPE_NLATRACK) {
648           NlaTrack *nlt = (NlaTrack *)ale->data;
649
650           nlt->flag |= NLATRACK_SELECTED;
651           int filter = ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE |
652                        ANIMFILTER_LIST_CHANNELS;
653           ANIM_set_active_channel(ac, ac->data, ac->datatype, filter, nlt, ANIMTYPE_NLATRACK);
654         }
655       }
656     }
657
658     /* free this channel */
659     MEM_freeN(ale);
660   }
661 }
662
663 /* ------------------- */
664
665 /* handle clicking */
666 static int nlaedit_clickselect_invoke(bContext *C, wmOperator *op, const wmEvent *event)
667 {
668   bAnimContext ac;
669
670   /* get editor data */
671   if (ANIM_animdata_get_context(C, &ac) == 0) {
672     return OPERATOR_CANCELLED;
673   }
674
675   /* select mode is either replace (deselect all, then add) or add/extend */
676   const short selectmode = RNA_boolean_get(op->ptr, "extend") ? SELECT_INVERT : SELECT_REPLACE;
677   const bool deselect_all = RNA_boolean_get(op->ptr, "deselect_all");
678
679   /* select strips based upon mouse position */
680   mouse_nla_strips(C, &ac, event->mval, selectmode, deselect_all);
681
682   /* set notifier that things have changed */
683   WM_event_add_notifier(C, NC_ANIMATION | ND_NLA | NA_SELECTED, NULL);
684
685   /* for tweak grab to work */
686   return OPERATOR_FINISHED | OPERATOR_PASS_THROUGH;
687 }
688
689 void NLA_OT_click_select(wmOperatorType *ot)
690 {
691   PropertyRNA *prop;
692
693   /* identifiers */
694   ot->name = "Select";
695   ot->idname = "NLA_OT_click_select";
696   ot->description = "Handle clicks to select NLA Strips";
697
698   /* api callbacks - absolutely no exec() this yet... */
699   ot->invoke = nlaedit_clickselect_invoke;
700   ot->poll = ED_operator_nla_active;
701
702   /* flags */
703   ot->flag = OPTYPE_UNDO;
704
705   /* properties */
706   prop = RNA_def_boolean(ot->srna, "extend", 0, "Extend Select", "");  // SHIFTKEY
707   RNA_def_property_flag(prop, PROP_SKIP_SAVE);
708
709   prop = RNA_def_boolean(ot->srna,
710                          "deselect_all",
711                          false,
712                          "Deselect On Nothing",
713                          "Deselect all when nothing under the cursor");
714   RNA_def_property_flag(prop, PROP_SKIP_SAVE);
715 }
716
717 /* *********************************************** */