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 = ®ion->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