Cleanup: comments (long lines) in editors
[blender.git] / source / blender / editors / space_nla / nla_channels.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 #include <stdlib.h>
27 #include <math.h>
28
29 #include "DNA_anim_types.h"
30 #include "DNA_object_types.h"
31 #include "DNA_scene_types.h"
32
33 #include "BLI_blenlib.h"
34 #include "BLI_utildefines.h"
35
36 #include "BKE_animsys.h"
37 #include "BKE_nla.h"
38 #include "BKE_context.h"
39 #include "BKE_global.h"
40 #include "BKE_scene.h"
41 #include "BKE_screen.h"
42 #include "BKE_report.h"
43
44 #include "ED_anim_api.h"
45 #include "ED_keyframes_edit.h"
46 #include "ED_object.h"
47 #include "ED_screen.h"
48
49 #include "RNA_access.h"
50 #include "RNA_define.h"
51
52 #include "WM_api.h"
53 #include "WM_types.h"
54
55 #include "DEG_depsgraph.h"
56 #include "DEG_depsgraph_build.h"
57
58 #include "UI_view2d.h"
59
60 #include "nla_intern.h"  // own include
61
62 /* *********************************************** */
63 /* Operators for NLA channels-list which need to be different
64  * from the standard Animation Editor ones */
65
66 /* ******************** Mouse-Click Operator *********************** */
67 /* Depending on the channel that was clicked on, the mouse click will activate whichever
68  * part of the channel is relevant.
69  *
70  * NOTE: eventually,
71  * this should probably be phased out when many of these things are replaced with buttons
72  * --> Most channels are now selection only.
73  */
74
75 static int mouse_nla_channels(
76     bContext *C, bAnimContext *ac, float x, int channel_index, short selectmode)
77 {
78   ListBase anim_data = {NULL, NULL};
79   bAnimListElem *ale;
80   int filter;
81
82   View2D *v2d = &ac->ar->v2d;
83   int notifierFlags = 0;
84
85   /* get the channel that was clicked on */
86   /* filter channels */
87   filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_LIST_CHANNELS);
88   ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
89
90   /* get channel from index */
91   ale = BLI_findlink(&anim_data, channel_index);
92   if (ale == NULL) {
93     /* channel not found */
94     if (G.debug & G_DEBUG)
95       printf("Error: animation channel (index = %d) not found in mouse_anim_channels()\n",
96              channel_index);
97
98     ANIM_animdata_freelist(&anim_data);
99     return 0;
100   }
101
102   /* action to take depends on what channel we've got */
103   // WARNING: must keep this in sync with the equivalent function in anim_channels_edit.c
104   switch (ale->type) {
105     case ANIMTYPE_SCENE: {
106       Scene *sce = (Scene *)ale->data;
107       AnimData *adt = sce->adt;
108
109       /* set selection status */
110       if (selectmode == SELECT_INVERT) {
111         /* swap select */
112         sce->flag ^= SCE_DS_SELECTED;
113         if (adt)
114           adt->flag ^= ADT_UI_SELECTED;
115       }
116       else {
117         sce->flag |= SCE_DS_SELECTED;
118         if (adt)
119           adt->flag |= ADT_UI_SELECTED;
120       }
121
122       notifierFlags |= (ND_ANIMCHAN | NA_SELECTED);
123       break;
124     }
125     case ANIMTYPE_OBJECT: {
126       ViewLayer *view_layer = ac->view_layer;
127       Base *base = (Base *)ale->data;
128       Object *ob = base->object;
129       AnimData *adt = ob->adt;
130
131       if (nlaedit_is_tweakmode_on(ac) == 0 && (base->flag & BASE_SELECTABLE)) {
132         /* set selection status */
133         if (selectmode == SELECT_INVERT) {
134           /* swap select */
135           ED_object_base_select(base, BA_INVERT);
136           BKE_scene_object_base_flag_sync_from_base(base);
137
138           if (adt)
139             adt->flag ^= ADT_UI_SELECTED;
140         }
141         else {
142           /* deselect all */
143           /* TODO: should this deselect all other types of channels too? */
144           for (Base *b = view_layer->object_bases.first; b; b = b->next) {
145             ED_object_base_select(b, BA_DESELECT);
146             BKE_scene_object_base_flag_sync_from_base(b);
147             if (b->object->adt)
148               b->object->adt->flag &= ~(ADT_UI_SELECTED | ADT_UI_ACTIVE);
149           }
150
151           /* select object now */
152           ED_object_base_select(base, BA_SELECT);
153           BKE_scene_object_base_flag_sync_from_base(base);
154           if (adt)
155             adt->flag |= ADT_UI_SELECTED;
156         }
157
158         /* change active object - regardless of whether it is now selected [T37883] */
159         ED_object_base_activate(C, base); /* adds notifier */
160
161         if ((adt) && (adt->flag & ADT_UI_SELECTED))
162           adt->flag |= ADT_UI_ACTIVE;
163
164         /* notifiers - channel was selected */
165         notifierFlags |= (ND_ANIMCHAN | NA_SELECTED);
166       }
167       break;
168     }
169     case ANIMTYPE_FILLACTD: /* Action Expander */
170     case ANIMTYPE_DSMAT:    /* Datablock AnimData Expanders */
171     case ANIMTYPE_DSLAM:
172     case ANIMTYPE_DSCAM:
173     case ANIMTYPE_DSCACHEFILE:
174     case ANIMTYPE_DSCUR:
175     case ANIMTYPE_DSSKEY:
176     case ANIMTYPE_DSWOR:
177     case ANIMTYPE_DSNTREE:
178     case ANIMTYPE_DSPART:
179     case ANIMTYPE_DSMBALL:
180     case ANIMTYPE_DSARM:
181     case ANIMTYPE_DSMESH:
182     case ANIMTYPE_DSTEX:
183     case ANIMTYPE_DSLAT:
184     case ANIMTYPE_DSLINESTYLE:
185     case ANIMTYPE_DSSPK:
186     case ANIMTYPE_DSGPENCIL:
187     case ANIMTYPE_PALETTE: {
188       /* sanity checking... */
189       if (ale->adt) {
190         /* select/deselect */
191         if (selectmode == SELECT_INVERT) {
192           /* inverse selection status of this AnimData block only */
193           ale->adt->flag ^= ADT_UI_SELECTED;
194         }
195         else {
196           /* select AnimData block by itself */
197           ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, 0, ACHANNEL_SETFLAG_CLEAR);
198           ale->adt->flag |= ADT_UI_SELECTED;
199         }
200
201         /* set active? */
202         if ((ale->adt) && (ale->adt->flag & ADT_UI_SELECTED))
203           ale->adt->flag |= ADT_UI_ACTIVE;
204       }
205
206       notifierFlags |= (ND_ANIMCHAN | NA_SELECTED);
207       break;
208     }
209     case ANIMTYPE_NLATRACK: {
210       NlaTrack *nlt = (NlaTrack *)ale->data;
211       AnimData *adt = ale->adt;
212       short offset;
213
214       /* offset for start of channel (on LHS of channel-list) */
215       if (ale->id) {
216         /* special exception for materials and particles */
217         if (ELEM(GS(ale->id->name), ID_MA, ID_PA))
218           offset = 21 + NLACHANNEL_BUTTON_WIDTH;
219         else
220           offset = 14;
221       }
222       else
223         offset = 0;
224
225       if (x >= (v2d->cur.xmax - NLACHANNEL_BUTTON_WIDTH)) {
226         /* toggle protection (only if there's a toggle there) */
227         nlt->flag ^= NLATRACK_PROTECTED;
228
229         /* notifier flags - channel was edited */
230         notifierFlags |= (ND_ANIMCHAN | NA_EDITED);
231       }
232       else if (x >= (v2d->cur.xmax - 2 * NLACHANNEL_BUTTON_WIDTH)) {
233         /* toggle mute */
234         nlt->flag ^= NLATRACK_MUTED;
235
236         /* notifier flags - channel was edited */
237         notifierFlags |= (ND_ANIMCHAN | NA_EDITED);
238         ale->update |= ANIM_UPDATE_DEPS;
239       }
240       else if (x <= ((NLACHANNEL_BUTTON_WIDTH * 2) + offset)) {
241         /* toggle 'solo' */
242         BKE_nlatrack_solo_toggle(adt, nlt);
243
244         /* notifier flags - channel was edited */
245         notifierFlags |= (ND_ANIMCHAN | NA_EDITED);
246         ale->update |= ANIM_UPDATE_DEPS;
247       }
248       else if (nlaedit_is_tweakmode_on(ac) == 0) {
249         /* set selection */
250         if (selectmode == SELECT_INVERT) {
251           /* inverse selection status of this F-Curve only */
252           nlt->flag ^= NLATRACK_SELECTED;
253         }
254         else {
255           /* select F-Curve by itself */
256           ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, 0, ACHANNEL_SETFLAG_CLEAR);
257           nlt->flag |= NLATRACK_SELECTED;
258         }
259
260         /* if NLA-Track is selected now,
261          * make NLA-Track the 'active' one in the visible list */
262         if (nlt->flag & NLATRACK_SELECTED)
263           ANIM_set_active_channel(ac, ac->data, ac->datatype, filter, nlt, ANIMTYPE_NLATRACK);
264
265         /* notifier flags - channel was selected */
266         notifierFlags |= (ND_ANIMCHAN | NA_SELECTED);
267       }
268       break;
269     }
270     case ANIMTYPE_NLAACTION: {
271       AnimData *adt = BKE_animdata_from_id(ale->id);
272
273       /* button region... */
274       if (x >= (v2d->cur.xmax - NLACHANNEL_BUTTON_WIDTH)) {
275         if (nlaedit_is_tweakmode_on(ac) == 0) {
276           /* 'push-down' action - only usable when not in TweakMode */
277           /* TODO: make this use the operator instead of calling the function directly
278            * however, calling the operator requires that we supply the args,
279            * and that works with proper buttons only */
280           BKE_nla_action_pushdown(adt);
281         }
282         else {
283           /* when in tweakmode, this button becomes the toggle for mapped editing */
284           adt->flag ^= ADT_NLA_EDIT_NOMAP;
285         }
286
287         /* changes to NLA-Action occurred */
288         notifierFlags |= ND_NLA_ACTCHANGE;
289         ale->update |= ANIM_UPDATE_DEPS;
290       }
291       /* OR rest of name... */
292       else {
293         /* NOTE: rest of NLA-Action name doubles for operating on the AnimData block
294          * - this is useful when there's no clear divider, and makes more sense in
295          *   the case of users trying to use this to change actions
296          * - in tweakmode, clicking here gets us out of tweakmode, as changing selection
297          *   while in tweakmode is really evil!
298          * - we disable "solo" flags too, to make it easier to work with stashed actions
299          *   with less trouble
300          */
301         if (nlaedit_is_tweakmode_on(ac)) {
302           /* exit tweakmode immediately */
303           nlaedit_disable_tweakmode(ac, true);
304
305           /* changes to NLA-Action occurred */
306           notifierFlags |= ND_NLA_ACTCHANGE;
307           ale->update |= ANIM_UPDATE_DEPS;
308         }
309         else {
310           /* select/deselect */
311           if (selectmode == SELECT_INVERT) {
312             /* inverse selection status of this AnimData block only */
313             adt->flag ^= ADT_UI_SELECTED;
314           }
315           else {
316             /* select AnimData block by itself */
317             ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, 0, ACHANNEL_SETFLAG_CLEAR);
318             adt->flag |= ADT_UI_SELECTED;
319           }
320
321           /* set active? */
322           if (adt->flag & ADT_UI_SELECTED)
323             adt->flag |= ADT_UI_ACTIVE;
324
325           notifierFlags |= (ND_ANIMCHAN | NA_SELECTED);
326         }
327       }
328       break;
329     }
330     default:
331       if (G.debug & G_DEBUG)
332         printf("Error: Invalid channel type in mouse_nla_channels()\n");
333       break;
334   }
335
336   /* free channels */
337   ANIM_animdata_update(ac, &anim_data);
338   ANIM_animdata_freelist(&anim_data);
339
340   /* return the notifier-flags set */
341   return notifierFlags;
342 }
343
344 /* ------------------- */
345
346 /* handle clicking */
347 static int nlachannels_mouseclick_invoke(bContext *C, wmOperator *op, const wmEvent *event)
348 {
349   bAnimContext ac;
350   SpaceNla *snla;
351   ARegion *ar;
352   View2D *v2d;
353   int channel_index;
354   int notifierFlags = 0;
355   short selectmode;
356   float x, y;
357
358   /* get editor data */
359   if (ANIM_animdata_get_context(C, &ac) == 0)
360     return OPERATOR_CANCELLED;
361
362   /* get useful pointers from animation context data */
363   snla = (SpaceNla *)ac.sl;
364   ar = ac.ar;
365   v2d = &ar->v2d;
366
367   /* select mode is either replace (deselect all, then add) or add/extend */
368   if (RNA_boolean_get(op->ptr, "extend"))
369     selectmode = SELECT_INVERT;
370   else
371     selectmode = SELECT_REPLACE;
372
373   /**
374    * Figure out which channel user clicked in:
375    *
376    * \note Although channels technically start at y= NLACHANNEL_FIRST,
377    * we need to adjust by half a channel's height so that the tops of channels get caught ok.
378    * Since NLACHANNEL_FIRST is really NLACHANNEL_HEIGHT, we simply use NLACHANNEL_HEIGHT_HALF.
379    */
380   UI_view2d_region_to_view(v2d, event->mval[0], event->mval[1], &x, &y);
381   UI_view2d_listview_view_to_cell(v2d,
382                                   NLACHANNEL_NAMEWIDTH,
383                                   NLACHANNEL_STEP(snla),
384                                   0,
385                                   (float)NLACHANNEL_HEIGHT_HALF(snla),
386                                   x,
387                                   y,
388                                   NULL,
389                                   &channel_index);
390
391   /* handle mouse-click in the relevant channel then */
392   notifierFlags = mouse_nla_channels(C, &ac, x, channel_index, selectmode);
393
394   /* set notifier that things have changed */
395   WM_event_add_notifier(C, NC_ANIMATION | notifierFlags, NULL);
396
397   return OPERATOR_FINISHED;
398 }
399
400 void NLA_OT_channels_click(wmOperatorType *ot)
401 {
402   PropertyRNA *prop;
403
404   /* identifiers */
405   ot->name = "Mouse Click on NLA Channels";
406   ot->idname = "NLA_OT_channels_click";
407   ot->description = "Handle clicks to select NLA channels";
408
409   /* api callbacks */
410   ot->invoke = nlachannels_mouseclick_invoke;
411   ot->poll = ED_operator_nla_active;
412
413   /* flags */
414   ot->flag = OPTYPE_UNDO;
415
416   /* props */
417   prop = RNA_def_boolean(ot->srna, "extend", 0, "Extend Select", "");  // SHIFTKEY
418   RNA_def_property_flag(prop, PROP_SKIP_SAVE);
419 }
420
421 /* *********************************************** */
422 /* Special Operators */
423
424 /* ******************** Action Push Down ******************************** */
425
426 static int nlachannels_pushdown_exec(bContext *C, wmOperator *op)
427 {
428   bAnimContext ac;
429   ID *id = NULL;
430   AnimData *adt = NULL;
431   int channel_index = RNA_int_get(op->ptr, "channel_index");
432
433   /* get editor data */
434   if (ANIM_animdata_get_context(C, &ac) == 0)
435     return OPERATOR_CANCELLED;
436
437   /* get anim-channel to use (or more specifically, the animdata block behind it) */
438   if (channel_index == -1) {
439     PointerRNA adt_ptr = {{NULL}};
440
441     /* active animdata block */
442     if (nla_panel_context(C, &adt_ptr, NULL, NULL) == 0 || (adt_ptr.data == NULL)) {
443       BKE_report(op->reports,
444                  RPT_ERROR,
445                  "No active AnimData block to use "
446                  "(select a data-block expander first or set the appropriate flags on an AnimData "
447                  "block)");
448       return OPERATOR_CANCELLED;
449     }
450     else {
451       id = adt_ptr.id.data;
452       adt = adt_ptr.data;
453     }
454   }
455   else {
456     /* indexed channel */
457     ListBase anim_data = {NULL, NULL};
458     bAnimListElem *ale;
459     int filter;
460
461     /* filter channels */
462     filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_LIST_CHANNELS);
463     ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
464
465     /* get channel from index */
466     ale = BLI_findlink(&anim_data, channel_index);
467     if (ale == NULL) {
468       BKE_reportf(op->reports, RPT_ERROR, "No animation channel found at index %d", channel_index);
469       ANIM_animdata_freelist(&anim_data);
470       return OPERATOR_CANCELLED;
471     }
472     else if (ale->type != ANIMTYPE_NLAACTION) {
473       BKE_reportf(op->reports,
474                   RPT_ERROR,
475                   "Animation channel at index %d is not a NLA 'Active Action' channel",
476                   channel_index);
477       ANIM_animdata_freelist(&anim_data);
478       return OPERATOR_CANCELLED;
479     }
480
481     /* grab AnimData from the channel */
482     adt = ale->adt;
483     id = ale->id;
484
485     /* we don't need anything here anymore, so free it all */
486     ANIM_animdata_freelist(&anim_data);
487   }
488
489   /* double-check that we are free to push down here... */
490   if (adt == NULL) {
491     BKE_report(op->reports, RPT_WARNING, "Internal Error - AnimData block is not valid");
492     return OPERATOR_CANCELLED;
493   }
494   else if (nlaedit_is_tweakmode_on(&ac)) {
495     BKE_report(op->reports,
496                RPT_WARNING,
497                "Cannot push down actions while tweaking a strip's action, exit tweak mode first");
498     return OPERATOR_CANCELLED;
499   }
500   else if (adt->action == NULL) {
501     BKE_report(op->reports, RPT_WARNING, "No active action to push down");
502     return OPERATOR_CANCELLED;
503   }
504   else {
505     /* 'push-down' action - only usable when not in TweakMode */
506     BKE_nla_action_pushdown(adt);
507
508     DEG_id_tag_update_ex(CTX_data_main(C), id, ID_RECALC_ANIMATION | ID_RECALC_COPY_ON_WRITE);
509   }
510
511   /* set notifier that things have changed */
512   WM_event_add_notifier(C, NC_ANIMATION | ND_NLA_ACTCHANGE, NULL);
513   return OPERATOR_FINISHED;
514 }
515
516 void NLA_OT_action_pushdown(wmOperatorType *ot)
517 {
518   /* identifiers */
519   ot->name = "Push Down Action";
520   ot->idname = "NLA_OT_action_pushdown";
521   ot->description = "Push action down onto the top of the NLA stack as a new strip";
522
523   /* callbacks */
524   ot->exec = nlachannels_pushdown_exec;
525   ot->poll = nlaop_poll_tweakmode_off;
526
527   /* flags */
528   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
529
530   /* properties */
531   ot->prop = RNA_def_int(ot->srna,
532                          "channel_index",
533                          -1,
534                          -1,
535                          INT_MAX,
536                          "Channel Index",
537                          "Index of NLA action channel to perform pushdown operation on",
538                          0,
539                          INT_MAX);
540   RNA_def_property_flag(ot->prop, PROP_SKIP_SAVE);
541 }
542
543 /* ******************** Action Unlink ******************************** */
544
545 static bool nla_action_unlink_poll(bContext *C)
546 {
547   if (ED_operator_nla_active(C)) {
548     return nla_panel_context(C, NULL, NULL, NULL);
549   }
550
551   /* something failed... */
552   return false;
553 }
554
555 static int nla_action_unlink_exec(bContext *C, wmOperator *op)
556 {
557   PointerRNA adt_ptr;
558   AnimData *adt;
559
560   /* check context and also validity of pointer */
561   if (!nla_panel_context(C, &adt_ptr, NULL, NULL))
562     return OPERATOR_CANCELLED;
563
564   /* get animdata */
565   adt = adt_ptr.data;
566   if (adt == NULL)
567     return OPERATOR_CANCELLED;
568
569   /* do unlinking */
570   if (adt && adt->action) {
571     bool force_delete = RNA_boolean_get(op->ptr, "force_delete");
572     ED_animedit_unlink_action(C, adt_ptr.id.data, adt, adt->action, op->reports, force_delete);
573   }
574
575   return OPERATOR_FINISHED;
576 }
577
578 static int nla_action_unlink_invoke(bContext *C, wmOperator *op, const wmEvent *evt)
579 {
580   /* NOTE: this is hardcoded to match the behavior for the unlink button
581    * (in interface_templates.c) */
582   RNA_boolean_set(op->ptr, "force_delete", evt->shift != 0);
583   return nla_action_unlink_exec(C, op);
584 }
585
586 void NLA_OT_action_unlink(wmOperatorType *ot)
587 {
588   PropertyRNA *prop;
589
590   /* identifiers */
591   ot->name = "Unlink Action";
592   ot->idname = "NLA_OT_action_unlink";
593   ot->description = "Unlink this action from the active action slot (and/or exit Tweak Mode)";
594
595   /* callbacks */
596   ot->invoke = nla_action_unlink_invoke;
597   ot->exec = nla_action_unlink_exec;
598   ot->poll = nla_action_unlink_poll;
599
600   /* properties */
601   prop = RNA_def_boolean(ot->srna,
602                          "force_delete",
603                          false,
604                          "Force Delete",
605                          "Clear Fake User and remove copy stashed in this datablock's NLA stack");
606   RNA_def_property_flag(prop, PROP_SKIP_SAVE);
607 }
608
609 /* ******************** Add Tracks Operator ***************************** */
610 /* Add NLA Tracks to the same AnimData block as a selected track, or above the selected tracks */
611
612 /* helper - add NLA Tracks alongside existing ones */
613 bool nlaedit_add_tracks_existing(bAnimContext *ac, bool above_sel)
614 {
615   ListBase anim_data = {NULL, NULL};
616   bAnimListElem *ale;
617   int filter;
618   AnimData *lastAdt = NULL;
619   bool added = false;
620
621   /* get a list of the (selected) NLA Tracks being shown in the NLA */
622   filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_SEL |
623             ANIMFILTER_NODUPLIS);
624   ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
625
626   /* add tracks... */
627   for (ale = anim_data.first; ale; ale = ale->next) {
628     if (ale->type == ANIMTYPE_NLATRACK) {
629       NlaTrack *nlt = (NlaTrack *)ale->data;
630       AnimData *adt = ale->adt;
631
632       /* check if just adding a new track above this one,
633        * or whether we're adding a new one to the top of the stack that this one belongs to
634        */
635       if (above_sel) {
636         /* just add a new one above this one */
637         BKE_nlatrack_add(adt, nlt);
638         ale->update = ANIM_UPDATE_DEPS;
639         added = true;
640       }
641       else if ((lastAdt == NULL) || (adt != lastAdt)) {
642         /* add one track to the top of the owning AnimData's stack,
643          * then don't add anymore to this stack */
644         BKE_nlatrack_add(adt, NULL);
645         lastAdt = adt;
646         ale->update = ANIM_UPDATE_DEPS;
647         added = true;
648       }
649     }
650   }
651
652   /* free temp data */
653   ANIM_animdata_update(ac, &anim_data);
654   ANIM_animdata_freelist(&anim_data);
655
656   return added;
657 }
658
659 /* helper - add NLA Tracks to empty (and selected) AnimData blocks */
660 bool nlaedit_add_tracks_empty(bAnimContext *ac)
661 {
662   ListBase anim_data = {NULL, NULL};
663   bAnimListElem *ale;
664   int filter;
665   bool added = false;
666
667   /* get a list of the selected AnimData blocks in the NLA */
668   filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_ANIMDATA |
669             ANIMFILTER_SEL | ANIMFILTER_NODUPLIS);
670   ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
671
672   /* check if selected AnimData blocks are empty, and add tracks if so... */
673   for (ale = anim_data.first; ale; ale = ale->next) {
674     AnimData *adt = ale->adt;
675
676     /* sanity check */
677     BLI_assert(adt->flag & ADT_UI_SELECTED);
678
679     /* ensure it is empty */
680     if (BLI_listbase_is_empty(&adt->nla_tracks)) {
681       /* add new track to this AnimData block then */
682       BKE_nlatrack_add(adt, NULL);
683       ale->update = ANIM_UPDATE_DEPS;
684       added = true;
685     }
686   }
687
688   /* cleanup */
689   ANIM_animdata_update(ac, &anim_data);
690   ANIM_animdata_freelist(&anim_data);
691
692   return added;
693 }
694
695 /* ----- */
696
697 static int nlaedit_add_tracks_exec(bContext *C, wmOperator *op)
698 {
699   bAnimContext ac;
700   bool above_sel = RNA_boolean_get(op->ptr, "above_selected");
701   bool op_done = false;
702
703   /* get editor data */
704   if (ANIM_animdata_get_context(C, &ac) == 0)
705     return OPERATOR_CANCELLED;
706
707   /* perform adding in two passes - existing first so that we don't double up for empty */
708   op_done |= nlaedit_add_tracks_existing(&ac, above_sel);
709   op_done |= nlaedit_add_tracks_empty(&ac);
710
711   /* done? */
712   if (op_done) {
713     DEG_relations_tag_update(CTX_data_main(C));
714
715     /* set notifier that things have changed */
716     WM_event_add_notifier(C, NC_ANIMATION | ND_NLA | NA_EDITED, NULL);
717
718     /* done */
719     return OPERATOR_FINISHED;
720   }
721   else {
722     /* failed to add any tracks */
723     BKE_report(
724         op->reports, RPT_WARNING, "Select an existing NLA Track or an empty action line first");
725
726     /* not done */
727     return OPERATOR_CANCELLED;
728   }
729 }
730
731 void NLA_OT_tracks_add(wmOperatorType *ot)
732 {
733   /* identifiers */
734   ot->name = "Add Tracks";
735   ot->idname = "NLA_OT_tracks_add";
736   ot->description = "Add NLA-Tracks above/after the selected tracks";
737
738   /* api callbacks */
739   ot->exec = nlaedit_add_tracks_exec;
740   ot->poll = nlaop_poll_tweakmode_off;
741
742   /* flags */
743   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
744
745   /* properties */
746   RNA_def_boolean(ot->srna,
747                   "above_selected",
748                   0,
749                   "Above Selected",
750                   "Add a new NLA Track above every existing selected one");
751 }
752
753 /* ******************** Delete Tracks Operator ***************************** */
754 /* Delete selected NLA Tracks */
755
756 static int nlaedit_delete_tracks_exec(bContext *C, wmOperator *UNUSED(op))
757 {
758   bAnimContext ac;
759
760   ListBase anim_data = {NULL, NULL};
761   bAnimListElem *ale;
762   int filter;
763
764   /* get editor data */
765   if (ANIM_animdata_get_context(C, &ac) == 0)
766     return OPERATOR_CANCELLED;
767
768   /* get a list of the AnimData blocks being shown in the NLA */
769   filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_SEL |
770             ANIMFILTER_NODUPLIS);
771   ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
772
773   /* delete tracks */
774   for (ale = anim_data.first; ale; ale = ale->next) {
775     if (ale->type == ANIMTYPE_NLATRACK) {
776       NlaTrack *nlt = (NlaTrack *)ale->data;
777       AnimData *adt = ale->adt;
778
779       /* if track is currently 'solo', then AnimData should have its
780        * 'has solo' flag disabled
781        */
782       if (nlt->flag & NLATRACK_SOLO)
783         adt->flag &= ~ADT_NLA_SOLO_TRACK;
784
785       /* call delete on this track - deletes all strips too */
786       BKE_nlatrack_free(&adt->nla_tracks, nlt, true);
787       ale->update = ANIM_UPDATE_DEPS;
788     }
789   }
790
791   /* free temp data */
792   ANIM_animdata_update(&ac, &anim_data);
793   ANIM_animdata_freelist(&anim_data);
794
795   DEG_relations_tag_update(ac.bmain);
796
797   /* set notifier that things have changed */
798   WM_event_add_notifier(C, NC_ANIMATION | ND_NLA | NA_EDITED, NULL);
799
800   /* done */
801   return OPERATOR_FINISHED;
802 }
803
804 void NLA_OT_tracks_delete(wmOperatorType *ot)
805 {
806   /* identifiers */
807   ot->name = "Delete Tracks";
808   ot->idname = "NLA_OT_tracks_delete";
809   ot->description = "Delete selected NLA-Tracks and the strips they contain";
810
811   /* api callbacks */
812   ot->exec = nlaedit_delete_tracks_exec;
813   ot->poll = nlaop_poll_tweakmode_off;
814
815   /* flags */
816   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
817 }
818
819 /* *********************************************** */
820 /* AnimData Related Operators */
821
822 /* ******************** Include Objects Operator ***************************** */
823 /* Include selected objects in NLA Editor, by giving them AnimData blocks
824  * NOTE: This doesn't help for non-object AnimData, where we do not have any effective
825  *       selection mechanism in place. Unfortunately, this means that non-object AnimData
826  *       once again becomes a second-class citizen here. However, at least for the most
827  *       common use case, we now have a nice shortcut again.
828  */
829
830 static int nlaedit_objects_add_exec(bContext *C, wmOperator *UNUSED(op))
831 {
832   bAnimContext ac;
833   SpaceNla *snla;
834
835   /* get editor data */
836   if (ANIM_animdata_get_context(C, &ac) == 0)
837     return OPERATOR_CANCELLED;
838
839   /* ensure that filters are set so that the effect will be immediately visible */
840   snla = (SpaceNla *)ac.sl;
841   if (snla && snla->ads) {
842     snla->ads->filterflag &= ~ADS_FILTER_NLA_NOACT;
843   }
844
845   /* operate on selected objects... */
846   CTX_DATA_BEGIN (C, Object *, ob, selected_objects) {
847     /* ensure that object has AnimData... that's all */
848     BKE_animdata_add_id(&ob->id);
849   }
850   CTX_DATA_END;
851
852   /* set notifier that things have changed */
853   WM_event_add_notifier(C, NC_ANIMATION | ND_NLA | NA_EDITED, NULL);
854
855   /* done */
856   return OPERATOR_FINISHED;
857 }
858
859 void NLA_OT_selected_objects_add(wmOperatorType *ot)
860 {
861   /* identifiers */
862   ot->name = "Include Selected Objects";
863   ot->idname = "NLA_OT_selected_objects_add";
864   ot->description = "Make selected objects appear in NLA Editor by adding Animation Data";
865
866   /* api callbacks */
867   ot->exec = nlaedit_objects_add_exec;
868   ot->poll = nlaop_poll_tweakmode_off;
869
870   /* flags */
871   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
872 }
873
874 /* *********************************************** */