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 <stdio.h>
25 #include <string.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_context.h"
35 #include "BKE_nla.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_interface.h"
50 #include "UI_view2d.h"
51 
52 #include "nla_intern.h" /* own include */
53 
54 /* ******************** Utilities ***************************************** */
55 
56 /* Convert SELECT_* flags to ACHANNEL_SETFLAG_* flags */
selmodes_to_flagmodes(short sel)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  */
deselect_nla_strips(bAnimContext * ac,short test,short sel)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 
nlaedit_deselectall_exec(bContext * C,wmOperator * op)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 
NLA_OT_select_all(wmOperatorType * ot)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 
box_select_nla_strips(bAnimContext * ac,rcti rect,short mode,short selectmode)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->region->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 
nlaedit_strip_at_region_position(bAnimContext * ac,float region_x,float region_y,bAnimListElem ** r_ale,NlaStrip ** r_strip)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->region->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       LISTBASE_FOREACH (NlaStrip *, strip, &nlt->strips) {
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 
nlaedit_mouse_is_over_strip(bAnimContext * ac,const int mval[2])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 
nlaedit_box_select_invoke(bContext * C,wmOperator * op,const wmEvent * event)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 
nlaedit_box_select_exec(bContext * C,wmOperator * op)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 
NLA_OT_select_box(wmOperatorType * ot)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 
nlaedit_select_leftright(bContext * C,bAnimContext * ac,short leftright,short select_mode)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 
nlaedit_select_leftright_exec(bContext * C,wmOperator * op)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 
nlaedit_select_leftright_invoke(bContext * C,wmOperator * op,const wmEvent * event)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 *region = ac.region;
554     View2D *v2d = &region->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 
NLA_OT_select_leftright(wmOperatorType * ot)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 */
mouse_nla_strips(bContext * C,bAnimContext * ac,const int mval[2],short select_mode,const bool deselect_all,bool wait_to_deselect_others)600 static int mouse_nla_strips(bContext *C,
601                             bAnimContext *ac,
602                             const int mval[2],
603                             short select_mode,
604                             const bool deselect_all,
605                             bool wait_to_deselect_others)
606 {
607   Scene *scene = ac->scene;
608 
609   bAnimListElem *ale = NULL;
610   NlaStrip *strip = NULL;
611   int ret_value = OPERATOR_FINISHED;
612 
613   nlaedit_strip_at_region_position(ac, mval[0], mval[1], &ale, &strip);
614 
615   /* if currently in tweakmode, exit tweakmode before changing selection states
616    * now that we've found our target...
617    */
618   if (scene->flag & SCE_NLA_EDIT_ON) {
619     WM_operator_name_call(C, "NLA_OT_tweakmode_exit", WM_OP_EXEC_DEFAULT, NULL);
620   }
621 
622   if (select_mode != SELECT_REPLACE) {
623     wait_to_deselect_others = false;
624   }
625 
626   /* For replacing selection, if we have something to select, we have to clear existing selection.
627    * The same goes if we found nothing to select, and deselect_all is true
628    * (deselect on nothing behavior). */
629   if ((strip != NULL && select_mode == SELECT_REPLACE) || (strip == NULL && deselect_all)) {
630     /* reset selection mode for next steps */
631     select_mode = SELECT_ADD;
632 
633     if (strip && wait_to_deselect_others && (strip->flag & DESELECT_STRIPS_CLEARACTIVE)) {
634       ret_value = OPERATOR_RUNNING_MODAL;
635     }
636     else {
637       /* deselect all strips */
638       deselect_nla_strips(ac, 0, SELECT_SUBTRACT);
639 
640       /* deselect all other channels first */
641       ANIM_anim_channels_select_set(ac, ACHANNEL_SETFLAG_CLEAR);
642     }
643   }
644 
645   /* only select strip if we clicked on a valid channel and hit something */
646   if (ale != NULL) {
647     /* select the strip accordingly (if a matching one was found) */
648     if (strip != NULL) {
649       select_mode = selmodes_to_flagmodes(select_mode);
650       ACHANNEL_SET_FLAG(strip, select_mode, NLASTRIP_FLAG_SELECT);
651 
652       /* if we selected it, we can make it active too
653        * - we always need to clear the active strip flag though...
654        * - as well as selecting its track...
655        */
656       deselect_nla_strips(ac, DESELECT_STRIPS_CLEARACTIVE, 0);
657 
658       if (strip->flag & NLASTRIP_FLAG_SELECT) {
659         strip->flag |= NLASTRIP_FLAG_ACTIVE;
660 
661         /* Highlight NLA-Track */
662         if (ale->type == ANIMTYPE_NLATRACK) {
663           NlaTrack *nlt = (NlaTrack *)ale->data;
664 
665           nlt->flag |= NLATRACK_SELECTED;
666           int filter = ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE |
667                        ANIMFILTER_LIST_CHANNELS;
668           ANIM_set_active_channel(ac, ac->data, ac->datatype, filter, nlt, ANIMTYPE_NLATRACK);
669         }
670       }
671     }
672 
673     /* free this channel */
674     MEM_freeN(ale);
675   }
676 
677   return ret_value;
678 }
679 
680 /* ------------------- */
681 
682 /* handle clicking */
nlaedit_clickselect_exec(bContext * C,wmOperator * op)683 static int nlaedit_clickselect_exec(bContext *C, wmOperator *op)
684 {
685   bAnimContext ac;
686   int ret_value;
687 
688   /* get editor data */
689   if (ANIM_animdata_get_context(C, &ac) == 0) {
690     return OPERATOR_CANCELLED;
691   }
692 
693   /* select mode is either replace (deselect all, then add) or add/extend */
694   const short selectmode = RNA_boolean_get(op->ptr, "extend") ? SELECT_INVERT : SELECT_REPLACE;
695   const bool deselect_all = RNA_boolean_get(op->ptr, "deselect_all");
696   const bool wait_to_deselect_others = RNA_boolean_get(op->ptr, "wait_to_deselect_others");
697   int mval[2];
698   mval[0] = RNA_int_get(op->ptr, "mouse_x");
699   mval[1] = RNA_int_get(op->ptr, "mouse_y");
700 
701   /* select strips based upon mouse position */
702   ret_value = mouse_nla_strips(C, &ac, mval, selectmode, deselect_all, wait_to_deselect_others);
703 
704   /* set notifier that things have changed */
705   WM_event_add_notifier(C, NC_ANIMATION | ND_NLA | NA_SELECTED, NULL);
706 
707   /* for tweak grab to work */
708   return ret_value | OPERATOR_PASS_THROUGH;
709 }
710 
NLA_OT_click_select(wmOperatorType * ot)711 void NLA_OT_click_select(wmOperatorType *ot)
712 {
713   PropertyRNA *prop;
714 
715   /* identifiers */
716   ot->name = "Select";
717   ot->idname = "NLA_OT_click_select";
718   ot->description = "Handle clicks to select NLA Strips";
719 
720   /* callbacks */
721   ot->poll = ED_operator_nla_active;
722   ot->exec = nlaedit_clickselect_exec;
723   ot->invoke = WM_generic_select_invoke;
724   ot->modal = WM_generic_select_modal;
725 
726   /* flags */
727   ot->flag = OPTYPE_UNDO;
728 
729   /* properties */
730   WM_operator_properties_generic_select(ot);
731   prop = RNA_def_boolean(ot->srna, "extend", 0, "Extend Select", ""); /* SHIFTKEY */
732   RNA_def_property_flag(prop, PROP_SKIP_SAVE);
733 
734   prop = RNA_def_boolean(ot->srna,
735                          "deselect_all",
736                          false,
737                          "Deselect On Nothing",
738                          "Deselect all when nothing under the cursor");
739   RNA_def_property_flag(prop, PROP_SKIP_SAVE);
740 }
741 
742 /* *********************************************** */
743