Whitespace fixes
[blender.git] / source / blender / editors / animation / anim_channels_edit.c
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * The Original Code is Copyright (C) 2009 Blender Foundation, Joshua Leung
19  * All rights reserved.
20  *
21  * Contributor(s): Joshua Leung
22  *
23  * ***** END GPL LICENSE BLOCK *****
24  */
25
26 /** \file blender/editors/animation/anim_channels_edit.c
27  *  \ingroup edanimation
28  */
29
30
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h> 
34
35 #include "MEM_guardedalloc.h"
36
37 #include "BLI_blenlib.h"
38 #include "BLI_utildefines.h"
39 #include "BKE_library.h"
40
41 #include "DNA_anim_types.h"
42 #include "DNA_object_types.h"
43 #include "DNA_scene_types.h"
44 #include "DNA_key_types.h"
45 #include "DNA_gpencil_types.h"
46 #include "DNA_mask_types.h"
47
48 #include "RNA_access.h"
49 #include "RNA_define.h"
50
51 #include "BKE_action.h"
52 #include "BKE_fcurve.h"
53 #include "BKE_gpencil.h"
54 #include "BKE_context.h"
55 #include "BKE_mask.h"
56 #include "BKE_global.h"
57
58 #include "UI_view2d.h"
59
60 #include "ED_anim_api.h"
61 #include "ED_keyframes_edit.h" // XXX move the select modes out of there!
62 #include "ED_screen.h"
63
64 #include "WM_api.h"
65 #include "WM_types.h"
66
67 /* ************************************************************************** */
68 /* CHANNELS API - Exposed API */
69
70 /* -------------------------- Selection ------------------------------------- */
71
72 /* Set the given animation-channel as the active one for the active context */
73 // TODO: extend for animdata types...
74 void ANIM_set_active_channel(bAnimContext *ac, void *data, short datatype, int filter, void *channel_data, short channel_type)
75 {
76         ListBase anim_data = {NULL, NULL};
77         bAnimListElem *ale;
78         
79         /* try to build list of filtered items */
80         ANIM_animdata_filter(ac, &anim_data, filter, data, datatype);
81         if (anim_data.first == NULL)
82                 return;
83                 
84         /* only clear the 'active' flag for the channels of the same type */
85         for (ale = anim_data.first; ale; ale = ale->next) {
86                 /* skip if types don't match */
87                 if (channel_type != ale->type)
88                         continue;
89                 
90                 /* flag to set depends on type */
91                 switch (ale->type) {
92                         case ANIMTYPE_GROUP:
93                         {
94                                 bActionGroup *agrp = (bActionGroup *)ale->data;
95                                 
96                                 ACHANNEL_SET_FLAG(agrp, ACHANNEL_SETFLAG_CLEAR, AGRP_ACTIVE);
97                         }
98                         break;
99                         case ANIMTYPE_FCURVE:
100                         {
101                                 FCurve *fcu = (FCurve *)ale->data;
102                                 
103                                 ACHANNEL_SET_FLAG(fcu, ACHANNEL_SETFLAG_CLEAR, FCURVE_ACTIVE);
104                         }
105                         break;
106                         case ANIMTYPE_NLATRACK:
107                         {
108                                 NlaTrack *nlt = (NlaTrack *)ale->data;
109                                 
110                                 ACHANNEL_SET_FLAG(nlt, ACHANNEL_SETFLAG_CLEAR, NLATRACK_ACTIVE);
111                         }
112                         break;
113                         
114                         case ANIMTYPE_FILLACTD: /* Action Expander */
115                         case ANIMTYPE_DSMAT:    /* Datablock AnimData Expanders */
116                         case ANIMTYPE_DSLAM:
117                         case ANIMTYPE_DSCAM:
118                         case ANIMTYPE_DSCUR:
119                         case ANIMTYPE_DSSKEY:
120                         case ANIMTYPE_DSWOR:
121                         case ANIMTYPE_DSPART:
122                         case ANIMTYPE_DSMBALL:
123                         case ANIMTYPE_DSARM:
124                         case ANIMTYPE_DSMESH:
125                         case ANIMTYPE_DSTEX:
126                         case ANIMTYPE_DSLAT:
127                         case ANIMTYPE_DSSPK:
128                         {
129                                 /* need to verify that this data is valid for now */
130                                 if (ale->adt) {
131                                         ACHANNEL_SET_FLAG(ale->adt, ACHANNEL_SETFLAG_CLEAR, ADT_UI_ACTIVE);
132                                 }
133                         }
134                         break;
135                 }
136         }
137         
138         /* set active flag */
139         if (channel_data) {
140                 switch (channel_type) {
141                         case ANIMTYPE_GROUP:
142                         {
143                                 bActionGroup *agrp = (bActionGroup *)channel_data;
144                                 agrp->flag |= AGRP_ACTIVE;
145                         }
146                         break;
147                         case ANIMTYPE_FCURVE:
148                         {
149                                 FCurve *fcu = (FCurve *)channel_data;
150                                 fcu->flag |= FCURVE_ACTIVE;
151                         }
152                         break;
153                         case ANIMTYPE_NLATRACK:
154                         {
155                                 NlaTrack *nlt = (NlaTrack *)channel_data;
156                                 nlt->flag |= NLATRACK_ACTIVE;
157                         }
158                         break;
159                                 
160                         case ANIMTYPE_FILLACTD: /* Action Expander */
161                         case ANIMTYPE_DSMAT:    /* Datablock AnimData Expanders */
162                         case ANIMTYPE_DSLAM:
163                         case ANIMTYPE_DSCAM:
164                         case ANIMTYPE_DSCUR:
165                         case ANIMTYPE_DSSKEY:
166                         case ANIMTYPE_DSWOR:
167                         case ANIMTYPE_DSPART:
168                         case ANIMTYPE_DSMBALL:
169                         case ANIMTYPE_DSARM:
170                         case ANIMTYPE_DSMESH:
171                         case ANIMTYPE_DSLAT:
172                         case ANIMTYPE_DSSPK:
173                         {
174                                 /* need to verify that this data is valid for now */
175                                 if (ale && ale->adt) {
176                                         ale->adt->flag |= ADT_UI_ACTIVE;
177                                 }
178                         }
179                         break;
180                 }
181         }
182         
183         /* clean up */
184         BLI_freelistN(&anim_data);
185 }
186
187 /* Deselect all animation channels 
188  *      - data: pointer to datatype, as contained in bAnimContext
189  *      - datatype: the type of data that 'data' represents (eAnimCont_Types)
190  *      - test: check if deselecting instead of selecting
191  *      - sel: eAnimChannels_SetFlag;
192  */
193 void ANIM_deselect_anim_channels(bAnimContext *ac, void *data, short datatype, short test, short sel)
194 {
195         ListBase anim_data = {NULL, NULL};
196         bAnimListElem *ale;
197         int filter;
198         
199         /* filter data */
200         /* NOTE: no list visible, otherwise, we get dangling */
201         filter = ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_CHANNELS;
202         ANIM_animdata_filter(ac, &anim_data, filter, data, datatype);
203         
204         /* See if we should be selecting or deselecting */
205         if (test) {
206                 for (ale = anim_data.first; ale; ale = ale->next) {
207                         if (sel == 0) 
208                                 break;
209                         
210                         switch (ale->type) {
211                                 case ANIMTYPE_SCENE:
212                                         if (ale->flag & SCE_DS_SELECTED)
213                                                 sel = ACHANNEL_SETFLAG_CLEAR;
214                                         break;
215                                 case ANIMTYPE_OBJECT:
216                                 #if 0   /* for now, do not take object selection into account, since it gets too annoying */
217                                         if (ale->flag & SELECT)
218                                                 sel = ACHANNEL_SETFLAG_CLEAR;
219                                 #endif
220                                         break;
221                                 case ANIMTYPE_GROUP:
222                                         if (ale->flag & AGRP_SELECTED)
223                                                 sel = ACHANNEL_SETFLAG_CLEAR;
224                                         break;
225                                 case ANIMTYPE_FCURVE:
226                                         if (ale->flag & FCURVE_SELECTED)
227                                                 sel = ACHANNEL_SETFLAG_CLEAR;
228                                         break;
229                                 case ANIMTYPE_SHAPEKEY:
230                                         if (ale->flag & KEYBLOCK_SEL)
231                                                 sel = ACHANNEL_SETFLAG_CLEAR;
232                                         break;
233                                 case ANIMTYPE_NLATRACK:
234                                         if (ale->flag & NLATRACK_SELECTED)
235                                                 sel = ACHANNEL_SETFLAG_CLEAR;
236                                         break;
237                                         
238                                 case ANIMTYPE_FILLACTD: /* Action Expander */
239                                 case ANIMTYPE_DSMAT:    /* Datablock AnimData Expanders */
240                                 case ANIMTYPE_DSLAM:
241                                 case ANIMTYPE_DSCAM:
242                                 case ANIMTYPE_DSCUR:
243                                 case ANIMTYPE_DSSKEY:
244                                 case ANIMTYPE_DSWOR:
245                                 case ANIMTYPE_DSPART:
246                                 case ANIMTYPE_DSMBALL:
247                                 case ANIMTYPE_DSARM:
248                                 case ANIMTYPE_DSMESH:
249                                 case ANIMTYPE_DSNTREE:
250                                 case ANIMTYPE_DSTEX:
251                                 case ANIMTYPE_DSLAT:
252                                 case ANIMTYPE_DSSPK:
253                                 {
254                                         if ((ale->adt) && (ale->adt->flag & ADT_UI_SELECTED))
255                                                 sel = ACHANNEL_SETFLAG_CLEAR;
256                                 }
257                                 break;
258                                         
259                                 case ANIMTYPE_GPLAYER:
260                                         if (ale->flag & GP_LAYER_SELECT)
261                                                 sel = ACHANNEL_SETFLAG_CLEAR;
262                                         break;
263                                 case ANIMTYPE_MASKLAYER:
264                                         if (ale->flag & MASK_LAYERFLAG_SELECT)
265                                                 sel = ACHANNEL_SETFLAG_CLEAR;
266                                         break;
267                         }
268                 }
269         }
270                 
271         /* Now set the flags */
272         for (ale = anim_data.first; ale; ale = ale->next) {
273                 switch (ale->type) {
274                         case ANIMTYPE_SCENE:
275                         {
276                                 Scene *scene = (Scene *)ale->data;
277                                 
278                                 ACHANNEL_SET_FLAG(scene, sel, SCE_DS_SELECTED);
279                                 
280                                 if (scene->adt) {
281                                         ACHANNEL_SET_FLAG(scene, sel, ADT_UI_SELECTED);
282                                 }
283                         }
284                         break;
285                         case ANIMTYPE_OBJECT:
286                         #if 0   /* for now, do not take object selection into account, since it gets too annoying */
287                                 {
288                                         Base *base = (Base *)ale->data;
289                                         Object *ob = base->object;
290                                         
291                                         ACHANNEL_SET_FLAG(base, sel, SELECT);
292                                         ACHANNEL_SET_FLAG(ob, sel, SELECT);
293                                         
294                                         if (ob->adt) {
295                                                 ACHANNEL_SET_FLAG(ob, sel, ADT_UI_SELECTED);
296                                         }
297                                 }
298                         #endif
299                                 break;
300                         case ANIMTYPE_GROUP:
301                         {
302                                 bActionGroup *agrp = (bActionGroup *)ale->data;
303                                 
304                                 ACHANNEL_SET_FLAG(agrp, sel, AGRP_SELECTED);
305                                 agrp->flag &= ~AGRP_ACTIVE;
306                         }
307                         break;
308                         case ANIMTYPE_FCURVE:
309                         {
310                                 FCurve *fcu = (FCurve *)ale->data;
311                                 
312                                 ACHANNEL_SET_FLAG(fcu, sel, FCURVE_SELECTED);
313                                 fcu->flag &= ~FCURVE_ACTIVE;
314                         }
315                         break;
316                         case ANIMTYPE_SHAPEKEY:
317                         {
318                                 KeyBlock *kb = (KeyBlock *)ale->data;
319                                 
320                                 ACHANNEL_SET_FLAG(kb, sel, KEYBLOCK_SEL);
321                         }
322                         break;
323                         case ANIMTYPE_NLATRACK:
324                         {
325                                 NlaTrack *nlt = (NlaTrack *)ale->data;
326                                 
327                                 ACHANNEL_SET_FLAG(nlt, sel, NLATRACK_SELECTED);
328                                 nlt->flag &= ~NLATRACK_ACTIVE;
329                         }
330                         break;
331                                 
332                         case ANIMTYPE_FILLACTD: /* Action Expander */
333                         case ANIMTYPE_DSMAT:    /* Datablock AnimData Expanders */
334                         case ANIMTYPE_DSLAM:
335                         case ANIMTYPE_DSCAM:
336                         case ANIMTYPE_DSCUR:
337                         case ANIMTYPE_DSSKEY:
338                         case ANIMTYPE_DSWOR:
339                         case ANIMTYPE_DSPART:
340                         case ANIMTYPE_DSMBALL:
341                         case ANIMTYPE_DSARM:
342                         case ANIMTYPE_DSMESH:
343                         case ANIMTYPE_DSNTREE:
344                         case ANIMTYPE_DSTEX:
345                         case ANIMTYPE_DSLAT:
346                         case ANIMTYPE_DSSPK:
347                         {
348                                 /* need to verify that this data is valid for now */
349                                 if (ale->adt) {
350                                         ACHANNEL_SET_FLAG(ale->adt, sel, ADT_UI_SELECTED);
351                                         ale->adt->flag &= ~ADT_UI_ACTIVE;
352                                 }
353                         }
354                         break;
355                                 
356                         case ANIMTYPE_GPLAYER:
357                         {
358                                 bGPDlayer *gpl = (bGPDlayer *)ale->data;
359                                 
360                                 ACHANNEL_SET_FLAG(gpl, sel, GP_LAYER_SELECT);
361                         }
362                         break;
363                         
364                         case ANIMTYPE_MASKLAYER:
365                         {
366                                 MaskLayer *masklay = (MaskLayer *)ale->data;
367                                 
368                                 ACHANNEL_SET_FLAG(masklay, sel, MASK_LAYERFLAG_SELECT);
369                         }
370                         break;
371                 }
372         }
373         
374         /* Cleanup */
375         BLI_freelistN(&anim_data);
376 }
377
378 /* ---------------------------- Graph Editor ------------------------------------- */
379
380 /* Flush visibility (for Graph Editor) changes up/down hierarchy for changes in the given setting 
381  *      - anim_data: list of the all the anim channels that can be chosen
382  *              -> filtered using ANIMFILTER_CHANNELS only, since if we took VISIBLE too,
383  *                then the channels under closed expanders get ignored...
384  *      - ale_setting: the anim channel (not in the anim_data list directly, though occurring there)
385  *              with the new state of the setting that we want flushed up/down the hierarchy 
386  *      - setting: type of setting to set
387  *      - on: whether the visibility setting has been enabled or disabled 
388  */
389 void ANIM_flush_setting_anim_channels(bAnimContext *ac, ListBase *anim_data, bAnimListElem *ale_setting, int setting, short on)
390 {
391         bAnimListElem *ale, *match = NULL;
392         int prevLevel = 0, matchLevel = 0;
393         
394         /* sanity check */
395         if (ELEM(NULL, anim_data, anim_data->first))
396                 return;
397         
398         /* find the channel that got changed */
399         for (ale = anim_data->first; ale; ale = ale->next) {
400                 /* compare data, and type as main way of identifying the channel */
401                 if ((ale->data == ale_setting->data) && (ale->type == ale_setting->type)) {
402                         /* we also have to check the ID, this is assigned to, since a block may have multiple users */
403                         /* TODO: is the owner-data more revealing? */
404                         if (ale->id == ale_setting->id) {
405                                 match = ale;
406                                 break;
407                         }
408                 }
409         }
410         if (match == NULL) {
411                 printf("ERROR: no channel matching the one changed was found\n");
412                 return;
413         }
414         else {
415                 bAnimChannelType *acf = ANIM_channel_get_typeinfo(ale_setting);
416                 
417                 if (acf == NULL) {
418                         printf("ERROR: no channel info for the changed channel\n");
419                         return;
420                 }
421                 
422                 /* get the level of the channel that was affected
423                  *   - we define the level as simply being the offset for the start of the channel
424                  */
425                 matchLevel = (acf->get_offset) ? acf->get_offset(ac, ale_setting) : 0;
426                 prevLevel = matchLevel;
427         }
428         
429         /* flush up? 
430          *
431          * For Visibility:
432          *      - only flush up if the current state is now enabled (positive 'on' state is default) 
433          *        (otherwise, it's too much work to force the parents to be inactive too)
434          *
435          * For everything else:
436          *      - only flush up if the current state is now disabled (negative 'off' state is default)
437          *        (otherwise, it's too much work to force the parents to be active too)
438          */
439         if ( ((setting == ACHANNEL_SETTING_VISIBLE) && on) ||
440              ((setting != ACHANNEL_SETTING_VISIBLE) && on == 0) )
441         {
442                 /* go backwards in the list, until the highest-ranking element (by indention has been covered) */
443                 for (ale = match->prev; ale; ale = ale->prev) {
444                         bAnimChannelType *acf = ANIM_channel_get_typeinfo(ale);
445                         int level;
446                         
447                         /* if no channel info was found, skip, since this type might not have any useful info */
448                         if (acf == NULL)
449                                 continue;
450                         
451                         /* get the level of the current channel traversed 
452                          *   - we define the level as simply being the offset for the start of the channel
453                          */
454                         level = (acf->get_offset) ? acf->get_offset(ac, ale) : 0;
455                         
456                         /* if the level is 'less than' (i.e. more important) the level we're matching
457                          * but also 'less than' the level just tried (i.e. only the 1st group above grouped F-Curves, 
458                          * when toggling visibility of F-Curves, gets flushed, which should happen if we don't let prevLevel
459                          * get updated below once the first 1st group is found)...
460                          */
461                         if (level < prevLevel) {
462                                 /* flush the new status... */
463                                 ANIM_channel_setting_set(ac, ale, setting, on);
464                                 
465                                 /* store this level as the 'old' level now */
466                                 prevLevel = level;
467                         }
468                         /* if the level is 'greater than' (i.e. less important) than the previous level... */
469                         else if (level > prevLevel) {
470                                 /* if previous level was a base-level (i.e. 0 offset / root of one hierarchy),
471                                  * stop here
472                                  */
473                                 if (prevLevel == 0)
474                                         break;
475                                 /* otherwise, this level weaves into another sibling hierarchy to the previous one just
476                                  * finished, so skip until we get to the parent of this level 
477                                  */
478                                 else
479                                         continue;
480                         }
481                 }
482         }
483         
484         /* flush down (always) */
485         {
486                 /* go forwards in the list, until the lowest-ranking element (by indention has been covered) */
487                 for (ale = match->next; ale; ale = ale->next) {
488                         bAnimChannelType *acf = ANIM_channel_get_typeinfo(ale);
489                         int level;
490                         
491                         /* if no channel info was found, skip, since this type might not have any useful info */
492                         if (acf == NULL)
493                                 continue;
494                         
495                         /* get the level of the current channel traversed 
496                          *   - we define the level as simply being the offset for the start of the channel
497                          */
498                         level = (acf->get_offset) ? acf->get_offset(ac, ale) : 0;
499                         
500                         /* if the level is 'greater than' (i.e. less important) the channel that was changed, 
501                          * flush the new status...
502                          */
503                         if (level > matchLevel)
504                                 ANIM_channel_setting_set(ac, ale, setting, on);
505                         /* however, if the level is 'less than or equal to' the channel that was changed,
506                          * (i.e. the current channel is as important if not more important than the changed channel)
507                          * then we should stop, since we've found the last one of the children we should flush
508                          */
509                         else
510                                 break;
511                         
512                         /* store this level as the 'old' level now */
513                         // prevLevel = level; // XXX: prevLevel is unused
514                 }
515         }
516 }
517
518 /* -------------------------- F-Curves ------------------------------------- */
519
520 /* Delete the given F-Curve from its AnimData block */
521 void ANIM_fcurve_delete_from_animdata(bAnimContext *ac, AnimData *adt, FCurve *fcu)
522 {
523         /* - if no AnimData, we've got nowhere to remove the F-Curve from 
524          *      (this doesn't guarantee that the F-Curve is in there, but at least we tried
525          * - if no F-Curve, there is nothing to remove
526          */
527         if (ELEM(NULL, adt, fcu))
528                 return;
529                 
530         /* remove from whatever list it came from
531          *      - Action Group
532          *      - Action
533          *      - Drivers
534          *      - TODO... some others?
535          */
536         if ((ac) && (ac->datatype == ANIMCONT_DRIVERS)) {
537                 /* driver F-Curve */
538                 BLI_remlink(&adt->drivers, fcu);
539         }
540         else if (adt->action) {
541                 bAction *act = adt->action;
542                 
543                 /* remove from group or action, whichever one "owns" the F-Curve */
544                 if (fcu->grp) {
545                         bActionGroup *agrp = fcu->grp;
546                         
547                         /* remove F-Curve from group+action */
548                         action_groups_remove_channel(act, fcu);
549                         
550                         /* if group has no more channels, remove it too, 
551                          * otherwise can have many dangling groups [#33541]
552                          */
553                         if (agrp->channels.first == NULL) {
554                                 BLI_freelinkN(&act->groups, agrp);
555                         }
556                 }
557                 else {
558                         BLI_remlink(&act->curves, fcu);
559                 }
560                 
561                 /* if action has no more F-Curves as a result of this, unlink it from
562                  * AnimData if it did not come from a NLA Strip being tweaked.
563                  *
564                  * This is done so that we don't have dangling Object+Action entries in
565                  * channel list that are empty, and linger around long after the data they
566                  * are for has disappeared (and probably won't come back).
567                  */
568                 if ((act->curves.first == NULL) && (adt->flag & ADT_NLA_EDIT_ON) == 0) {
569                         id_us_min(&act->id);
570                         adt->action = NULL;
571                 }
572         }
573                 
574         /* free the F-Curve itself */
575         free_fcurve(fcu);
576 }
577
578 /* ************************************************************************** */
579 /* OPERATORS */
580
581 /* ****************** Operator Utilities ********************************** */
582
583 /* poll callback for being in an Animation Editor channels list region */
584 static int animedit_poll_channels_active(bContext *C)
585 {
586         ScrArea *sa = CTX_wm_area(C);
587         
588         /* channels region test */
589         /* TODO: could enhance with actually testing if channels region? */
590         if (ELEM(NULL, sa, CTX_wm_region(C)))
591                 return 0;
592         /* animation editor test */
593         if (ELEM3(sa->spacetype, SPACE_ACTION, SPACE_IPO, SPACE_NLA) == 0)
594                 return 0;
595
596         return 1;
597 }
598
599 /* poll callback for Animation Editor channels list region + not in NLA-tweakmode for NLA */
600 static int animedit_poll_channels_nla_tweakmode_off(bContext *C)
601 {
602         ScrArea *sa = CTX_wm_area(C);
603         Scene *scene = CTX_data_scene(C);
604
605         /* channels region test */
606         /* TODO: could enhance with actually testing if channels region? */
607         if (ELEM(NULL, sa, CTX_wm_region(C)))
608                 return 0;
609         /* animation editor test */
610         if (ELEM3(sa->spacetype, SPACE_ACTION, SPACE_IPO, SPACE_NLA) == 0)
611                 return 0;
612         
613         /* NLA TweakMode test */
614         if (sa->spacetype == SPACE_NLA) {
615                 if ((scene == NULL) || (scene->flag & SCE_NLA_EDIT_ON))
616                         return 0;
617         }
618                 
619         return 1;
620 }
621
622 /* ****************** Rearrange Channels Operator ******************* */
623
624 /* constants for channel rearranging */
625 /* WARNING: don't change exising ones without modifying rearrange func accordingly */
626 enum {
627         REARRANGE_ANIMCHAN_TOP = -2,
628         REARRANGE_ANIMCHAN_UP = -1,
629         REARRANGE_ANIMCHAN_DOWN = 1,
630         REARRANGE_ANIMCHAN_BOTTOM = 2
631 };
632
633 /* defines for rearranging channels */
634 static EnumPropertyItem prop_animchannel_rearrange_types[] = {
635         {REARRANGE_ANIMCHAN_TOP, "TOP", 0, "To Top", ""},
636         {REARRANGE_ANIMCHAN_UP, "UP", 0, "Up", ""},
637         {REARRANGE_ANIMCHAN_DOWN, "DOWN", 0, "Down", ""},
638         {REARRANGE_ANIMCHAN_BOTTOM, "BOTTOM", 0, "To Bottom", ""},
639         {0, NULL, 0, NULL, NULL}
640 };
641
642 /* Reordering "Islands" Defines ----------------------------------- */
643
644 /* Island definition - just a listbase container */
645 typedef struct tReorderChannelIsland {
646         struct tReorderChannelIsland *next, *prev;
647         
648         ListBase channels;  /* channels within this region with the same state */
649         int flag;           /* eReorderIslandFlag */
650 } tReorderChannelIsland;
651
652 /* flags for channel reordering islands */
653 typedef enum eReorderIslandFlag {
654         REORDER_ISLAND_SELECTED         = (1 << 0),   /* island is selected */
655         REORDER_ISLAND_UNTOUCHABLE      = (1 << 1),   /* island should be ignored */
656         REORDER_ISLAND_MOVED            = (1 << 2)    /* island has already been moved */
657 } eReorderIslandFlag;
658
659
660 /* Rearrange Methods --------------------------------------------- */
661
662 static short rearrange_island_ok(tReorderChannelIsland *island)
663 {
664         /* island must not be untouchable */
665         if (island->flag & REORDER_ISLAND_UNTOUCHABLE)
666                 return 0;
667         
668         /* island should be selected to be moved */
669         return (island->flag & REORDER_ISLAND_SELECTED) && !(island->flag & REORDER_ISLAND_MOVED);
670 }
671
672 /* ............................. */
673
674 static short rearrange_island_top(ListBase *list, tReorderChannelIsland *island)
675 {
676         if (rearrange_island_ok(island)) {
677                 /* remove from current position */
678                 BLI_remlink(list, island);
679                 
680                 /* make it first element */
681                 BLI_insertlinkbefore(list, list->first, island);
682                 
683                 return 1;
684         }
685         
686         return 0;
687 }
688
689 static short rearrange_island_up(ListBase *list, tReorderChannelIsland *island)
690 {
691         if (rearrange_island_ok(island)) {
692                 /* moving up = moving before the previous island, otherwise we're in the same place */
693                 tReorderChannelIsland *prev = island->prev;
694                 
695                 if (prev) {
696                         /* remove from current position */
697                         BLI_remlink(list, island);
698                         
699                         /* push it up */
700                         BLI_insertlinkbefore(list, prev, island);
701                         
702                         return 1;
703                 }
704         }
705         
706         return 0;
707 }
708
709 static short rearrange_island_down(ListBase *list, tReorderChannelIsland *island)
710 {
711         if (rearrange_island_ok(island)) {
712                 /* moving down = moving after the next island, otherwise we're in the same place */
713                 tReorderChannelIsland *next = island->next;
714                 
715                 if (next) {
716                         /* can only move past if next is not untouchable (i.e. nothing can go after it) */
717                         if ((next->flag & REORDER_ISLAND_UNTOUCHABLE) == 0) {
718                                 /* remove from current position */
719                                 BLI_remlink(list, island);
720                                 
721                                 /* push it down */
722                                 BLI_insertlinkafter(list, next, island);
723                                 
724                                 return 1;
725                         }
726                 }
727                 /* else: no next channel, so we're at the bottom already, so can't move */
728         }
729         
730         return 0;
731 }
732
733 static short rearrange_island_bottom(ListBase *list, tReorderChannelIsland *island)
734 {
735         if (rearrange_island_ok(island)) {
736                 tReorderChannelIsland *last = list->last;
737                 
738                 /* remove island from current position */
739                 BLI_remlink(list, island);
740                 
741                 /* add before or after the last channel? */
742                 if ((last->flag & REORDER_ISLAND_UNTOUCHABLE) == 0) {
743                         /* can add after it */
744                         BLI_addtail(list, island);
745                 }
746                 else {
747                         /* can at most go just before it, since last cannot be moved */
748                         BLI_insertlinkbefore(list, last, island);
749                         
750                 }
751                 
752                 return 1;
753         }
754         
755         return 0;
756 }
757
758 /* ............................. */
759
760 /* typedef for channel rearranging function 
761  * < list: list that channels belong to
762  * < island: island to be moved
763  * > return[0]: whether operation was a success
764  */
765 typedef short (*AnimChanRearrangeFp)(ListBase *list, tReorderChannelIsland *island);
766
767 /* get rearranging function, given 'rearrange' mode */
768 static AnimChanRearrangeFp rearrange_get_mode_func(short mode)
769 {
770         switch (mode) {
771                 case REARRANGE_ANIMCHAN_TOP:
772                         return rearrange_island_top;
773                 case REARRANGE_ANIMCHAN_UP:
774                         return rearrange_island_up;
775                 case REARRANGE_ANIMCHAN_DOWN:
776                         return rearrange_island_down;
777                 case REARRANGE_ANIMCHAN_BOTTOM:
778                         return rearrange_island_bottom;
779                 default:
780                         return NULL;
781         }
782 }
783
784 /* Rearrange Islands Generics ------------------------------------- */
785
786 /* add channel into list of islands */
787 static void rearrange_animchannel_add_to_islands(ListBase *islands, ListBase *srcList, Link *channel, short type)
788 {
789         tReorderChannelIsland *island = islands->last;  /* always try to add to last island if possible */
790         short is_sel = 0, is_untouchable = 0;
791         
792         /* get flags - selected and untouchable from the channel */
793         switch (type) {
794                 case ANIMTYPE_GROUP:
795                 {
796                         bActionGroup *agrp = (bActionGroup *)channel;
797                         
798                         is_sel = SEL_AGRP(agrp);
799                         is_untouchable = (agrp->flag & AGRP_TEMP) != 0;
800                 }
801                 break;
802                 case ANIMTYPE_FCURVE:
803                 {
804                         FCurve *fcu = (FCurve *)channel;
805                         
806                         is_sel = SEL_FCU(fcu);
807                 }
808                 break;
809                 case ANIMTYPE_NLATRACK:
810                 {
811                         NlaTrack *nlt = (NlaTrack *)channel;
812                         
813                         is_sel = SEL_NLT(nlt);
814                 }
815                 break;
816                         
817                 default:
818                         printf("rearrange_animchannel_add_to_islands(): don't know how to handle channels of type %d\n", type);
819                         return;
820         }
821         
822         /* do we need to add to a new island? */
823         if ((island == NULL) ||                                 /* 1) no islands yet */
824             ((island->flag & REORDER_ISLAND_SELECTED) == 0) ||  /* 2) unselected islands have single channels only - to allow up/down movement */
825             (is_sel == 0))                                      /* 3) if channel is unselected, stop existing island (it was either wrong sel status, or full already) */
826         {
827                 /* create a new island now */
828                 island = MEM_callocN(sizeof(tReorderChannelIsland), "tReorderChannelIsland");
829                 BLI_addtail(islands, island);
830                 
831                 if (is_sel)
832                         island->flag |= REORDER_ISLAND_SELECTED;
833                 if (is_untouchable)
834                         island->flag |= REORDER_ISLAND_UNTOUCHABLE;
835         }
836
837         /* add channel to island - need to remove it from its existing list first though */
838         BLI_remlink(srcList, channel);
839         BLI_addtail(&island->channels, channel);
840 }
841
842 /* flatten islands out into a single list again */
843 static void rearrange_animchannel_flatten_islands(ListBase *islands, ListBase *srcList)
844 {
845         tReorderChannelIsland *island, *isn = NULL;
846         
847         /* make sure srcList is empty now */
848         BLI_assert(srcList->first == NULL);
849         
850         /* go through merging islands */
851         for (island = islands->first; island; island = isn) {
852                 isn = island->next;
853                 
854                 /* merge island channels back to main list, then delete the island */
855                 BLI_movelisttolist(srcList, &island->channels);
856                 BLI_freelinkN(islands, island);
857         }
858 }
859
860 /* ............................. */
861
862 /* performing rearranging of channels using islands */
863 static short rearrange_animchannel_islands(ListBase *list, AnimChanRearrangeFp rearrange_func, short mode, short type)
864 {
865         ListBase islands = {NULL, NULL};
866         Link *channel, *chanNext = NULL;
867         short done = FALSE;
868         
869         /* don't waste effort on an empty list */
870         if (list->first == NULL)
871                 return 0;
872         
873         /* group channels into islands */
874         for (channel = list->first; channel; channel = chanNext) {
875                 chanNext = channel->next;
876                 rearrange_animchannel_add_to_islands(&islands, list, channel, type);
877         }
878         
879         /* perform moving of selected islands now, but only if there is more than one of 'em so that something will happen 
880          *      - scanning of the list is performed in the opposite direction to the direction we're moving things, so that we 
881          *        shouldn't need to encounter items we've moved already
882          */
883         if (islands.first != islands.last) {
884                 tReorderChannelIsland *first = (mode > 0) ? islands.last : islands.first;
885                 tReorderChannelIsland *island, *isn = NULL;
886                 
887                 for (island = first; island; island = isn) {
888                         isn = (mode > 0) ? island->prev : island->next;
889                         
890                         /* perform rearranging */
891                         if (rearrange_func(&islands, island)) {
892                                 island->flag |= REORDER_ISLAND_MOVED;
893                                 done = TRUE;
894                         }
895                 }
896         }
897         
898         /* ungroup islands */
899         rearrange_animchannel_flatten_islands(&islands, list);
900         
901         /* did we do anything? */
902         return done;
903 }
904
905 /* NLA Specific Stuff ----------------------------------------------------- */
906
907 /* Change the order NLA Tracks within NLA Stack
908  * ! NLA tracks are displayed in opposite order, so directions need care
909  *      mode: REARRANGE_ANIMCHAN_*  
910  */
911 static void rearrange_nla_channels(bAnimContext *UNUSED(ac), AnimData *adt, short mode)
912 {
913         AnimChanRearrangeFp rearrange_func;
914         
915         /* hack: invert mode so that functions will work in right order */
916         mode *= -1;
917         
918         /* get rearranging function */
919         rearrange_func = rearrange_get_mode_func(mode);
920         if (rearrange_func == NULL)
921                 return;
922         
923         /* only consider NLA data if it's accessible */
924         //if (EXPANDED_DRVD(adt) == 0)
925         //      return;
926         
927         /* perform rearranging on tracks list */
928         rearrange_animchannel_islands(&adt->nla_tracks, rearrange_func, mode, ANIMTYPE_NLATRACK);
929 }
930
931 /* Drivers Specific Stuff ------------------------------------------------- */
932
933 /* Change the order drivers within AnimData block
934  *      mode: REARRANGE_ANIMCHAN_*  
935  */
936 static void rearrange_driver_channels(bAnimContext *UNUSED(ac), AnimData *adt, short mode)
937 {
938         /* get rearranging function */
939         AnimChanRearrangeFp rearrange_func = rearrange_get_mode_func(mode);
940         
941         if (rearrange_func == NULL)
942                 return;
943         
944         /* only consider drivers if they're accessible */
945         if (EXPANDED_DRVD(adt) == 0)
946                 return;
947         
948         /* perform rearranging on drivers list (drivers are really just F-Curves) */
949         rearrange_animchannel_islands(&adt->drivers, rearrange_func, mode, ANIMTYPE_FCURVE);
950 }
951
952 /* Action Specific Stuff ------------------------------------------------- */
953
954 /* make sure all action-channels belong to a group (and clear action's list) */
955 static void split_groups_action_temp(bAction *act, bActionGroup *tgrp)
956 {
957         bActionGroup *agrp;
958         FCurve *fcu;
959         
960         if (act == NULL)
961                 return;
962         
963         /* Separate F-Curves into lists per group */
964         for (agrp = act->groups.first; agrp; agrp = agrp->next) {
965                 if (agrp->channels.first) {
966                         fcu = agrp->channels.last;
967                         act->curves.first = fcu->next;
968                         
969                         fcu = agrp->channels.first;
970                         fcu->prev = NULL;
971                         
972                         fcu = agrp->channels.last;
973                         fcu->next = NULL;
974                 }
975         }
976         
977         /* Initialize memory for temp-group */
978         memset(tgrp, 0, sizeof(bActionGroup));
979         tgrp->flag |= (AGRP_EXPANDED | AGRP_TEMP);
980         BLI_strncpy(tgrp->name, "#TempGroup", sizeof(tgrp->name));
981         
982         /* Move any action-channels not already moved, to the temp group */
983         if (act->curves.first) {
984                 /* start of list */
985                 fcu = act->curves.first;
986                 fcu->prev = NULL;
987                 tgrp->channels.first = fcu;
988                 act->curves.first = NULL;
989                 
990                 /* end of list */
991                 fcu = act->curves.last;
992                 fcu->next = NULL;
993                 tgrp->channels.last = fcu;
994                 act->curves.last = NULL;
995         }
996         
997         /* Add temp-group to list */
998         BLI_addtail(&act->groups, tgrp);
999 }
1000
1001 /* link lists of channels that groups have */
1002 static void join_groups_action_temp(bAction *act)
1003 {
1004         bActionGroup *agrp;
1005         
1006         for (agrp = act->groups.first; agrp; agrp = agrp->next) {
1007                 ListBase tempGroup;
1008                 
1009                 /* add list of channels to action's channels */
1010                 tempGroup = agrp->channels;
1011                 BLI_movelisttolist(&act->curves, &agrp->channels);
1012                 agrp->channels = tempGroup;
1013                 
1014                 /* clear moved flag */
1015                 agrp->flag &= ~AGRP_MOVED;
1016                 
1017                 /* if temp-group... remove from list (but don't free as it's on the stack!) */
1018                 if (agrp->flag & AGRP_TEMP) {
1019                         BLI_remlink(&act->groups, agrp);
1020                         break;
1021                 }
1022         }
1023 }
1024
1025 /* Change the order of anim-channels within action 
1026  *      mode: REARRANGE_ANIMCHAN_*  
1027  */
1028 static void rearrange_action_channels(bAnimContext *ac, bAction *act, short mode)
1029 {
1030         bActionGroup tgrp;
1031         short do_channels;
1032         
1033         /* get rearranging function */
1034         AnimChanRearrangeFp rearrange_func = rearrange_get_mode_func(mode);
1035         
1036         if (rearrange_func == NULL)
1037                 return;
1038         
1039         /* make sure we're only operating with groups (vs a mixture of groups+curves) */
1040         split_groups_action_temp(act, &tgrp);
1041         
1042         /* rearrange groups first 
1043          *      - the group's channels will only get considered if nothing happened when rearranging the groups
1044          *        i.e. the rearrange function returned 0
1045          */
1046         do_channels = rearrange_animchannel_islands(&act->groups, rearrange_func, mode, ANIMTYPE_GROUP) == 0;
1047         
1048         if (do_channels) {
1049                 bActionGroup *agrp;
1050                 
1051                 for (agrp = act->groups.first; agrp; agrp = agrp->next) {
1052                         /* only consider F-Curves if they're visible (group expanded) */
1053                         if (EXPANDED_AGRP(ac, agrp)) {
1054                                 rearrange_animchannel_islands(&agrp->channels, rearrange_func, mode, ANIMTYPE_FCURVE);
1055                         }
1056                 }
1057         }
1058         
1059         /* assemble lists into one list (and clear moved tags) */
1060         join_groups_action_temp(act);
1061 }
1062
1063 /* ------------------- */
1064
1065 static int animchannels_rearrange_exec(bContext *C, wmOperator *op)
1066 {
1067         bAnimContext ac;
1068         short mode;
1069         
1070         /* get editor data */
1071         if (ANIM_animdata_get_context(C, &ac) == 0)
1072                 return OPERATOR_CANCELLED;
1073                 
1074         /* get mode */
1075         mode = RNA_enum_get(op->ptr, "direction");
1076         
1077         /* method to move channels depends on the editor */
1078         if (ac.datatype == ANIMCONT_GPENCIL) {
1079                 /* Grease Pencil channels */
1080                 printf("Grease Pencil not supported for moving yet\n");
1081         }
1082         else if (ac.datatype == ANIMCONT_MASK) {
1083                 /* Grease Pencil channels */
1084                 printf("Mask does not supported for moving yet\n");
1085         }
1086         else if (ac.datatype == ANIMCONT_ACTION) {
1087                 /* Directly rearrange action's channels */
1088                 rearrange_action_channels(&ac, ac.data, mode);
1089         }
1090         else {
1091                 ListBase anim_data = {NULL, NULL};
1092                 bAnimListElem *ale;
1093                 int filter;
1094                 
1095                 /* get animdata blocks */
1096                 filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_ANIMDATA);
1097                 ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
1098                 
1099                 for (ale = anim_data.first; ale; ale = ale->next) {
1100                         AnimData *adt = ale->data;
1101                         
1102                         switch (ac.datatype) {
1103                                 case ANIMCONT_NLA: /* NLA-tracks only */
1104                                         rearrange_nla_channels(&ac, adt, mode);
1105                                         break;
1106                                 
1107                                 case ANIMCONT_DRIVERS: /* Drivers list only */
1108                                         rearrange_driver_channels(&ac, adt, mode);
1109                                         break;
1110                                 
1111                                 case ANIMCONT_SHAPEKEY: // DOUBLE CHECK ME...
1112                                         
1113                                 default: /* some collection of actions */
1114                                         if (adt->action)
1115                                                 rearrange_action_channels(&ac, adt->action, mode);
1116                                         else if (G.debug & G_DEBUG)
1117                                                 printf("Animdata has no action\n");
1118                                         break;
1119                         }
1120                 }
1121                 
1122                 /* free temp data */
1123                 BLI_freelistN(&anim_data);
1124         }
1125         
1126         /* send notifier that things have changed */
1127         WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, NULL);
1128         
1129         return OPERATOR_FINISHED;
1130 }
1131
1132 static void ANIM_OT_channels_move(wmOperatorType *ot)
1133 {
1134         /* identifiers */
1135         ot->name = "Move Channels";
1136         ot->idname = "ANIM_OT_channels_move";
1137         ot->description = "Rearrange selected animation channels";
1138         
1139         /* api callbacks */
1140         ot->exec = animchannels_rearrange_exec;
1141         ot->poll = animedit_poll_channels_nla_tweakmode_off;
1142         
1143         /* flags */
1144         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1145         
1146         /* props */
1147         ot->prop = RNA_def_enum(ot->srna, "direction", prop_animchannel_rearrange_types, REARRANGE_ANIMCHAN_DOWN, "Direction", "");
1148 }
1149
1150 /* ******************** Group Channel Operator ************************ */
1151
1152 static int animchannels_grouping_poll(bContext *C)
1153 {
1154         ScrArea *sa = CTX_wm_area(C);
1155         SpaceLink *sl;
1156
1157         /* channels region test */
1158         /* TODO: could enhance with actually testing if channels region? */
1159         if (ELEM(NULL, sa, CTX_wm_region(C)))
1160                 return 0;
1161                 
1162         /* animation editor test - must be suitable modes only */
1163         sl = CTX_wm_space_data(C);
1164         
1165         switch (sa->spacetype) {
1166                 /* supported... */
1167                 case SPACE_ACTION:
1168                 {
1169                         SpaceAction *saction = (SpaceAction *)sl;
1170                         
1171                         /* dopesheet and action only - all others are for other datatypes or have no groups */
1172                         if (ELEM(saction->mode, SACTCONT_ACTION, SACTCONT_DOPESHEET) == 0)
1173                                 return 0;
1174                 }
1175                         break;
1176                         
1177                 case SPACE_IPO:
1178                 {
1179                         SpaceIpo *sipo = (SpaceIpo *)sl;
1180                         
1181                         /* drivers can't have groups... */
1182                         if (sipo->mode != SIPO_MODE_ANIMATION)
1183                                 return 0;
1184                 }
1185                         break;
1186                         
1187                 /* unsupported... */
1188                 default:
1189                         return 0;
1190         }
1191         
1192         return 1;
1193 }
1194
1195 /* ----------------------------------------------------------- */
1196
1197 static void animchannels_group_channels(bAnimContext *ac, bAnimListElem *adt_ref, const char name[])
1198 {       
1199         AnimData *adt = adt_ref->adt;
1200         bAction *act = adt->action;
1201         
1202         if (act) {
1203                 ListBase anim_data = {NULL, NULL};
1204                 int filter;
1205                 
1206                 /* find selected F-Curves to re-group */
1207                 filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_SEL);
1208                 ANIM_animdata_filter(ac, &anim_data, filter, adt_ref, ANIMCONT_CHANNEL);
1209                 
1210                 if (anim_data.first) {
1211                         bActionGroup *agrp;
1212                         bAnimListElem *ale;
1213                         
1214                         /* create new group, which should now be part of the action */
1215                         agrp = action_groups_add_new(act, name);
1216                         BLI_assert(agrp != NULL);
1217                         
1218                         /* transfer selected F-Curves across to new group  */
1219                         for (ale = anim_data.first; ale; ale = ale->next) {
1220                                 FCurve *fcu = (FCurve *)ale->data;
1221                                 bActionGroup *grp = fcu->grp;
1222                                 
1223                                 /* remove F-Curve from group, then group too if it is now empty */
1224                                 action_groups_remove_channel(act, fcu);
1225                                 
1226                                 if ((grp) && (grp->channels.first == NULL)) {
1227                                         BLI_freelinkN(&act->groups, grp);
1228                                 }
1229                                 
1230                                 /* add F-Curve to group */
1231                                 action_groups_add_channel(act, agrp, fcu);
1232                         }
1233                 }
1234                 
1235                 /* cleanup */
1236                 BLI_freelistN(&anim_data);
1237         }
1238 }
1239
1240 static int animchannels_group_exec(bContext *C, wmOperator *op)
1241 {
1242         bAnimContext ac;
1243         char name[MAX_NAME];
1244         
1245         /* get editor data */
1246         if (ANIM_animdata_get_context(C, &ac) == 0)
1247                 return OPERATOR_CANCELLED;
1248         
1249         /* get name for new group */
1250         RNA_string_get(op->ptr, "name", name);
1251         
1252         /* XXX: name for group should never be empty... */
1253         if (name[0]) {
1254                 ListBase anim_data = {NULL, NULL};
1255                 bAnimListElem *ale;
1256                 int filter;
1257                 
1258                 /* handle each animdata block separately, so that the regrouping doesn't flow into blocks  */
1259                 filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_ANIMDATA | ANIMFILTER_NODUPLIS);
1260                 ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
1261                 
1262                 for (ale = anim_data.first; ale; ale = ale->next) {
1263                         animchannels_group_channels(&ac, ale, name);
1264                 }
1265                 
1266                 /* free temp data */
1267                 BLI_freelistN(&anim_data);
1268                 
1269                 /* updatss */
1270                 WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, NULL);
1271         }
1272         
1273         return OPERATOR_FINISHED;
1274 }
1275
1276 static void ANIM_OT_channels_group(wmOperatorType *ot)
1277 {
1278         /* identifiers */
1279         ot->name = "Group Channels";
1280         ot->idname = "ANIM_OT_channels_group";
1281         ot->description = "Add selected F-Curves to a new group";
1282         
1283         /* callbacks */
1284         ot->invoke = WM_operator_props_popup;
1285         ot->exec = animchannels_group_exec;
1286         ot->poll = animchannels_grouping_poll;
1287         
1288         /* flags */
1289         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1290         
1291         /* props */
1292         ot->prop = RNA_def_string(ot->srna, "name", "New Group", 
1293                                   sizeof(((bActionGroup *)NULL)->name), 
1294                                   "Name", "Name of newly created group");
1295         /* RNA_def_property_flag(ot->prop, PROP_SKIP_SAVE); */ /* XXX: still not too sure about this - keeping same text is confusing... */
1296 }
1297
1298 /* ----------------------------------------------------------- */
1299
1300 static int animchannels_ungroup_exec(bContext *C, wmOperator *UNUSED(op))
1301 {
1302         bAnimContext ac;
1303         
1304         ListBase anim_data = {NULL, NULL};
1305         bAnimListElem *ale;
1306         int filter;
1307         
1308         /* get editor data */
1309         if (ANIM_animdata_get_context(C, &ac) == 0)
1310                 return OPERATOR_CANCELLED;
1311                 
1312         /* just selected F-Curves... */
1313         filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_SEL | ANIMFILTER_NODUPLIS);
1314         ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
1315         
1316         for (ale = anim_data.first; ale; ale = ale->next) {
1317                 /* find action for this F-Curve... */
1318                 if (ale->adt && ale->adt->action) {
1319                         FCurve  *fcu = (FCurve *)ale->data;
1320                         bAction *act = ale->adt->action;
1321                         
1322                         /* only proceed to remove if F-Curve is in a group... */
1323                         if (fcu->grp) { 
1324                                 bActionGroup *agrp = fcu->grp;
1325                                 
1326                                 /* remove F-Curve from group and add at tail (ungrouped) */
1327                                 action_groups_remove_channel(act, fcu);
1328                                 BLI_addtail(&act->curves, fcu);
1329                                 
1330                                 /* delete group if it is now empty */
1331                                 if (agrp->channels.first == NULL) {
1332                                         BLI_freelinkN(&act->groups, agrp);
1333                                 }
1334                         }
1335                 }
1336         }
1337         
1338         /* cleanup */
1339         BLI_freelistN(&anim_data);
1340         
1341         /* updates */
1342         WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, NULL);
1343         
1344         return OPERATOR_FINISHED;
1345 }
1346
1347 static void ANIM_OT_channels_ungroup(wmOperatorType *ot)
1348 {
1349         /* identifiers */
1350         ot->name = "Ungroup Channels";
1351         ot->idname = "ANIM_OT_channels_ungroup";
1352         ot->description = "Remove selected F-Curves from their current groups";
1353         
1354         /* callbacks */
1355         ot->exec = animchannels_ungroup_exec;
1356         ot->poll = animchannels_grouping_poll;
1357         
1358         /* flags */
1359         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1360 }
1361
1362 /* ******************** Delete Channel Operator *********************** */
1363
1364 static int animchannels_delete_exec(bContext *C, wmOperator *UNUSED(op))
1365 {
1366         bAnimContext ac;
1367         ListBase anim_data = {NULL, NULL};
1368         bAnimListElem *ale;
1369         int filter;
1370         
1371         /* get editor data */
1372         if (ANIM_animdata_get_context(C, &ac) == 0)
1373                 return OPERATOR_CANCELLED;
1374         
1375         /* cannot delete in shapekey */
1376         if (ac.datatype == ANIMCONT_SHAPEKEY) 
1377                 return OPERATOR_CANCELLED;
1378                 
1379                 
1380         /* do groups only first (unless in Drivers mode, where there are none) */
1381         if (ac.datatype != ANIMCONT_DRIVERS) {
1382                 /* filter data */
1383                 filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_SEL | ANIMFILTER_LIST_CHANNELS | ANIMFILTER_FOREDIT | ANIMFILTER_NODUPLIS);
1384                 ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
1385                 
1386                 /* delete selected groups and their associated channels */
1387                 for (ale = anim_data.first; ale; ale = ale->next) {
1388                         /* only groups - don't check other types yet, since they may no-longer exist */
1389                         if (ale->type == ANIMTYPE_GROUP) {
1390                                 bActionGroup *agrp = (bActionGroup *)ale->data;
1391                                 AnimData *adt = ale->adt;
1392                                 FCurve *fcu, *fcn;
1393                                 
1394                                 /* skip this group if no AnimData available, as we can't safely remove the F-Curves */
1395                                 if (adt == NULL)
1396                                         continue;
1397                                 
1398                                 /* delete all of the Group's F-Curves, but no others */
1399                                 for (fcu = agrp->channels.first; fcu && fcu->grp == agrp; fcu = fcn) {
1400                                         fcn = fcu->next;
1401                                         
1402                                         /* remove from group and action, then free */
1403                                         action_groups_remove_channel(adt->action, fcu);
1404                                         free_fcurve(fcu);
1405                                 }
1406                                 
1407                                 /* free the group itself */
1408                                 if (adt->action)
1409                                         BLI_freelinkN(&adt->action->groups, agrp);
1410                                 else
1411                                         MEM_freeN(agrp);
1412                         }
1413                 }
1414                 
1415                 /* cleanup */
1416                 BLI_freelistN(&anim_data);
1417         }
1418         
1419         /* filter data */
1420         filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_SEL | ANIMFILTER_FOREDIT | ANIMFILTER_NODUPLIS);
1421         ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
1422         
1423         /* delete selected data channels */
1424         for (ale = anim_data.first; ale; ale = ale->next) {
1425                 switch (ale->type) {
1426                         case ANIMTYPE_FCURVE: 
1427                         {
1428                                 /* F-Curves if we can identify its parent */
1429                                 AnimData *adt = ale->adt;
1430                                 FCurve *fcu = (FCurve *)ale->data;
1431                                 
1432                                 /* try to free F-Curve */
1433                                 ANIM_fcurve_delete_from_animdata(&ac, adt, fcu);
1434                         }
1435                         break;
1436                                 
1437                         case ANIMTYPE_GPLAYER:
1438                         {
1439                                 /* Grease Pencil layer */
1440                                 bGPdata *gpd = (bGPdata *)ale->id;
1441                                 bGPDlayer *gpl = (bGPDlayer *)ale->data;
1442                                 
1443                                 /* try to delete the layer's data and the layer itself */
1444                                 free_gpencil_frames(gpl);
1445                                 BLI_freelinkN(&gpd->layers, gpl);
1446                         }
1447                         break;
1448                         
1449                         case ANIMTYPE_MASKLAYER:
1450                         {
1451                                 /* Mask layer */
1452                                 Mask *mask = (Mask *)ale->id;
1453                                 MaskLayer *masklay = (MaskLayer *)ale->data;
1454                                 
1455                                 /* try to delete the layer's data and the layer itself */
1456                                 BKE_mask_layer_remove(mask, masklay);
1457                         }
1458                         break;
1459                 }
1460         }
1461         
1462         /* cleanup */
1463         BLI_freelistN(&anim_data);
1464         
1465         /* send notifier that things have changed */
1466         WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, NULL);
1467         
1468         return OPERATOR_FINISHED;
1469 }
1470  
1471 static void ANIM_OT_channels_delete(wmOperatorType *ot)
1472 {
1473         /* identifiers */
1474         ot->name = "Delete Channels";
1475         ot->idname = "ANIM_OT_channels_delete";
1476         ot->description = "Delete all selected animation channels";
1477         
1478         /* api callbacks */
1479         ot->exec = animchannels_delete_exec;
1480         ot->poll = animedit_poll_channels_active;
1481         
1482         /* flags */
1483         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1484 }
1485
1486 /* ******************** Set Channel Visibility Operator *********************** */
1487 /* NOTE: this operator is only valid in the Graph Editor channels region */
1488
1489 static int animchannels_visibility_set_exec(bContext *C, wmOperator *UNUSED(op))
1490 {
1491         bAnimContext ac;
1492         ListBase anim_data = {NULL, NULL};
1493         ListBase all_data = {NULL, NULL};
1494         bAnimListElem *ale;
1495         int filter;
1496         
1497         /* get editor data */
1498         if (ANIM_animdata_get_context(C, &ac) == 0)
1499                 return OPERATOR_CANCELLED;
1500         
1501         /* get list of all channels that selection may need to be flushed to 
1502          * - hierarchy mustn't affect what we have access to here...
1503          */
1504         filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_CHANNELS | ANIMFILTER_NODUPLIS);
1505         ANIM_animdata_filter(&ac, &all_data, filter, ac.data, ac.datatype);
1506                 
1507         /* hide all channels not selected
1508          * - hierarchy matters if we're doing this from the channels region
1509          *   since we only want to apply this to channels we can "see", 
1510          *   and have these affect their relatives
1511          * - but for Graph Editor, this gets used also from main region
1512          *   where hierarchy doesn't apply, as for [#21276]
1513          */
1514         if ((ac.spacetype == SPACE_IPO) && (ac.regiontype != RGN_TYPE_CHANNELS)) {
1515                 /* graph editor (case 2) */
1516                 filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_UNSEL | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_NODUPLIS);
1517         }
1518         else {
1519                 /* standard case */
1520                 filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_UNSEL | ANIMFILTER_NODUPLIS);
1521         }
1522         ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
1523         
1524         for (ale = anim_data.first; ale; ale = ale->next) {
1525                 /* clear setting first */
1526                 ANIM_channel_setting_set(&ac, ale, ACHANNEL_SETTING_VISIBLE, ACHANNEL_SETFLAG_CLEAR);
1527                 
1528                 /* now also flush selection status as appropriate 
1529                  * NOTE: in some cases, this may result in repeat flushing being performed
1530                  */
1531                 ANIM_flush_setting_anim_channels(&ac, &all_data, ale, ACHANNEL_SETTING_VISIBLE, 0);
1532         }
1533         
1534         BLI_freelistN(&anim_data);
1535         
1536         /* make all the selected channels visible */
1537         filter = (ANIMFILTER_SEL | ANIMFILTER_NODUPLIS);
1538         ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
1539
1540         for (ale = anim_data.first; ale; ale = ale->next) {
1541                 /* hack: skip object channels for now, since flushing those will always flush everything, but they are always included */
1542                 /* TODO: find out why this is the case, and fix that */
1543                 if (ale->type == ANIMTYPE_OBJECT)
1544                         continue;
1545                 
1546                 /* enable the setting */
1547                 ANIM_channel_setting_set(&ac, ale, ACHANNEL_SETTING_VISIBLE, ACHANNEL_SETFLAG_ADD);
1548                 
1549                 /* now, also flush selection status up/down as appropriate */
1550                 ANIM_flush_setting_anim_channels(&ac, &all_data, ale, ACHANNEL_SETTING_VISIBLE, 1);
1551         }
1552         
1553         BLI_freelistN(&anim_data);
1554         BLI_freelistN(&all_data);
1555         
1556         
1557         /* send notifier that things have changed */
1558         WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, NULL);
1559         
1560         return OPERATOR_FINISHED;
1561 }
1562
1563 static void ANIM_OT_channels_visibility_set(wmOperatorType *ot)
1564 {
1565         /* identifiers */
1566         ot->name = "Set Visibility";
1567         ot->idname = "ANIM_OT_channels_visibility_set";
1568         ot->description = "Make only the selected animation channels visible in the Graph Editor";
1569         
1570         /* api callbacks */
1571         ot->exec = animchannels_visibility_set_exec;
1572         ot->poll = ED_operator_graphedit_active;
1573         
1574         /* flags */
1575         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1576 }
1577
1578
1579 /* ******************** Toggle Channel Visibility Operator *********************** */
1580 /* NOTE: this operator is only valid in the Graph Editor channels region */
1581
1582 static int animchannels_visibility_toggle_exec(bContext *C, wmOperator *UNUSED(op))
1583 {
1584         bAnimContext ac;
1585         ListBase anim_data = {NULL, NULL};
1586         ListBase all_data = {NULL, NULL};
1587         bAnimListElem *ale;
1588         int filter;
1589         short vis = ACHANNEL_SETFLAG_ADD;
1590         
1591         /* get editor data */
1592         if (ANIM_animdata_get_context(C, &ac) == 0)
1593                 return OPERATOR_CANCELLED;
1594                 
1595         /* get list of all channels that selection may need to be flushed to 
1596          * - hierarchy mustn't affect what we have access to here...
1597          */
1598         filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_CHANNELS | ANIMFILTER_NODUPLIS);
1599         ANIM_animdata_filter(&ac, &all_data, filter, ac.data, ac.datatype);
1600                 
1601         /* filter data
1602          * - restrict this to only applying on settings we can get to in the list
1603          */
1604         filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_SEL | ANIMFILTER_NODUPLIS);
1605         ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
1606         
1607         /* See if we should be making showing all selected or hiding */
1608         for (ale = anim_data.first; ale; ale = ale->next) {
1609                 /* set the setting in the appropriate way (if available) */
1610                 if (ANIM_channel_setting_get(&ac, ale, ACHANNEL_SETTING_VISIBLE)) {
1611                         vis = ACHANNEL_SETFLAG_CLEAR;
1612                         break;
1613                 }
1614         }
1615
1616         /* Now set the flags */
1617         for (ale = anim_data.first; ale; ale = ale->next) {
1618                 /* hack: skip object channels for now, since flushing those will always flush everything, but they are always included */
1619                 /* TODO: find out why this is the case, and fix that */
1620                 if (ale->type == ANIMTYPE_OBJECT)
1621                         continue;
1622                 
1623                 /* change the setting */
1624                 ANIM_channel_setting_set(&ac, ale, ACHANNEL_SETTING_VISIBLE, vis);
1625                 
1626                 /* now, also flush selection status up/down as appropriate */
1627                 ANIM_flush_setting_anim_channels(&ac, &all_data, ale, ACHANNEL_SETTING_VISIBLE, (vis == ACHANNEL_SETFLAG_ADD));
1628         }
1629         
1630         /* cleanup */
1631         BLI_freelistN(&anim_data);
1632         BLI_freelistN(&all_data);
1633         
1634         /* send notifier that things have changed */
1635         WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, NULL);
1636         
1637         return OPERATOR_FINISHED;
1638 }
1639
1640 static void ANIM_OT_channels_visibility_toggle(wmOperatorType *ot)
1641 {
1642         /* identifiers */
1643         ot->name = "Toggle Visibility";
1644         ot->idname = "ANIM_OT_channels_visibility_toggle";
1645         ot->description = "Toggle visibility in Graph Editor of all selected animation channels";
1646         
1647         /* api callbacks */
1648         ot->exec = animchannels_visibility_toggle_exec;
1649         ot->poll = ED_operator_graphedit_active;
1650         
1651         /* flags */
1652         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1653 }
1654
1655 /* ********************** Set Flags Operator *********************** */
1656
1657 /* defines for setting animation-channel flags */
1658 static EnumPropertyItem prop_animchannel_setflag_types[] = {
1659         {ACHANNEL_SETFLAG_TOGGLE, "TOGGLE", 0, "Toggle", ""},
1660         {ACHANNEL_SETFLAG_CLEAR, "DISABLE", 0, "Disable", ""},
1661         {ACHANNEL_SETFLAG_ADD, "ENABLE", 0, "Enable", ""},
1662         {ACHANNEL_SETFLAG_INVERT, "INVERT", 0, "Invert", ""},
1663         {0, NULL, 0, NULL, NULL}
1664 };
1665
1666 /* defines for set animation-channel settings */
1667 // TODO: could add some more types, but those are really quite dependent on the mode...
1668 static EnumPropertyItem prop_animchannel_settings_types[] = {
1669         {ACHANNEL_SETTING_PROTECT, "PROTECT", 0, "Protect", ""},
1670         {ACHANNEL_SETTING_MUTE, "MUTE", 0, "Mute", ""},
1671         {0, NULL, 0, NULL, NULL}
1672 };
1673
1674
1675 /* ------------------- */
1676
1677 /* Set/clear a particular flag (setting) for all selected + visible channels 
1678  *      setting: the setting to modify
1679  *      mode: eAnimChannels_SetFlag
1680  *      onlysel: only selected channels get the flag set
1681  */
1682 // TODO: enable a setting which turns flushing on/off?
1683 static void setflag_anim_channels(bAnimContext *ac, short setting, short mode, short onlysel, short flush)
1684 {
1685         ListBase anim_data = {NULL, NULL};
1686         ListBase all_data = {NULL, NULL};
1687         bAnimListElem *ale;
1688         int filter;
1689         
1690         /* filter data that we need if flush is on */
1691         if (flush) {
1692                 /* get list of all channels that selection may need to be flushed to 
1693                  * - hierarchy visibility needs to be ignored so that settings can get flushed
1694                  *   "down" inside closed containers
1695                  */
1696                 filter = ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_CHANNELS;
1697                 ANIM_animdata_filter(ac, &all_data, filter, ac->data, ac->datatype);
1698         }
1699         
1700         /* filter data that we're working on 
1701          * - hierarchy matters if we're doing this from the channels region
1702          *   since we only want to apply this to channels we can "see", 
1703          *   and have these affect their relatives
1704          * - but for Graph Editor, this gets used also from main region
1705          *   where hierarchy doesn't apply [#21276]
1706          */
1707         if ((ac->spacetype == SPACE_IPO) && (ac->regiontype != RGN_TYPE_CHANNELS)) {
1708                 /* graph editor (case 2) */
1709                 filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_CHANNELS | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_NODUPLIS);
1710         }
1711         else {
1712                 /* standard case */
1713                 filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_LIST_CHANNELS | ANIMFILTER_NODUPLIS);
1714         }
1715         if (onlysel) filter |= ANIMFILTER_SEL;
1716         ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
1717         
1718         /* if toggling, check if disable or enable */
1719         if (mode == ACHANNEL_SETFLAG_TOGGLE) {
1720                 /* default to turn all on, unless we encounter one that's on... */
1721                 mode = ACHANNEL_SETFLAG_ADD;
1722                 
1723                 /* see if we should turn off instead... */
1724                 for (ale = anim_data.first; ale; ale = ale->next) {
1725                         /* set the setting in the appropriate way (if available) */
1726                         if (ANIM_channel_setting_get(ac, ale, setting) > 0) {
1727                                 mode = ACHANNEL_SETFLAG_CLEAR;
1728                                 break;
1729                         }
1730                 }
1731         }
1732         
1733         /* apply the setting */
1734         for (ale = anim_data.first; ale; ale = ale->next) {
1735                 /* skip channel if setting is not available */
1736                 if (ANIM_channel_setting_get(ac, ale, setting) == -1)
1737                         continue;
1738                 
1739                 /* set the setting in the appropriate way */
1740                 ANIM_channel_setting_set(ac, ale, setting, mode);
1741                 
1742                 /* if flush status... */
1743                 if (flush)
1744                         ANIM_flush_setting_anim_channels(ac, &all_data, ale, setting, mode);
1745         }
1746         
1747         BLI_freelistN(&anim_data);
1748         BLI_freelistN(&all_data);
1749 }
1750
1751 /* ------------------- */
1752
1753 static int animchannels_setflag_exec(bContext *C, wmOperator *op)
1754 {
1755         bAnimContext ac;
1756         short mode, setting;
1757         short flush = 1;
1758         
1759         /* get editor data */
1760         if (ANIM_animdata_get_context(C, &ac) == 0)
1761                 return OPERATOR_CANCELLED;
1762                 
1763         /* mode (eAnimChannels_SetFlag), setting (eAnimChannel_Settings) */
1764         mode = RNA_enum_get(op->ptr, "mode");
1765         setting = RNA_enum_get(op->ptr, "type");
1766         
1767         /* check if setting is flushable */
1768         if (setting == ACHANNEL_SETTING_EXPAND)
1769                 flush = 0;
1770         
1771         /* modify setting 
1772          *      - only selected channels are affected
1773          */
1774         setflag_anim_channels(&ac, setting, mode, 1, flush);
1775         
1776         /* send notifier that things have changed */
1777         WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, NULL);
1778         
1779         return OPERATOR_FINISHED;
1780 }
1781
1782 /* duplicate of 'ANIM_OT_channels_setting_toggle' for menu title only, weak! */
1783 static void ANIM_OT_channels_setting_enable(wmOperatorType *ot)
1784 {
1785         PropertyRNA *prop;
1786         
1787         /* identifiers */
1788         ot->name = "Enable Channel Setting";
1789         ot->idname = "ANIM_OT_channels_setting_enable";
1790         ot->description = "Enable specified setting on all selected animation channels";
1791         
1792         /* api callbacks */
1793         ot->invoke = WM_menu_invoke;
1794         ot->exec = animchannels_setflag_exec;
1795         ot->poll = animedit_poll_channels_active;
1796         
1797         /* flags */
1798         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1799         
1800         /* props */
1801         /* flag-setting mode */
1802         prop = RNA_def_enum(ot->srna, "mode", prop_animchannel_setflag_types, ACHANNEL_SETFLAG_ADD, "Mode", "");
1803         RNA_def_property_flag(prop, PROP_HIDDEN);
1804         /* setting to set */
1805         ot->prop = RNA_def_enum(ot->srna, "type", prop_animchannel_settings_types, 0, "Type", "");
1806 }
1807 /* duplicate of 'ANIM_OT_channels_setting_toggle' for menu title only, weak! */
1808 static void ANIM_OT_channels_setting_disable(wmOperatorType *ot)
1809 {
1810         PropertyRNA *prop;
1811         
1812         /* identifiers */
1813         ot->name = "Disable Channel Setting";
1814         ot->idname = "ANIM_OT_channels_setting_disable";
1815         ot->description = "Disable specified setting on all selected animation channels";
1816         
1817         /* api callbacks */
1818         ot->invoke = WM_menu_invoke;
1819         ot->exec = animchannels_setflag_exec;
1820         ot->poll = animedit_poll_channels_active;
1821         
1822         /* flags */
1823         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1824         
1825         /* props */
1826         /* flag-setting mode */
1827         prop = RNA_def_enum(ot->srna, "mode", prop_animchannel_setflag_types, ACHANNEL_SETFLAG_CLEAR, "Mode", "");
1828         RNA_def_property_flag(prop, PROP_HIDDEN); /* internal hack - don't expose */
1829         /* setting to set */
1830         ot->prop = RNA_def_enum(ot->srna, "type", prop_animchannel_settings_types, 0, "Type", "");
1831 }
1832
1833 static void ANIM_OT_channels_setting_toggle(wmOperatorType *ot)
1834 {
1835         PropertyRNA *prop;
1836         
1837         /* identifiers */
1838         ot->name = "Toggle Channel Setting";
1839         ot->idname = "ANIM_OT_channels_setting_toggle";
1840         ot->description = "Toggle specified setting on all selected animation channels";
1841         
1842         /* api callbacks */
1843         ot->invoke = WM_menu_invoke;
1844         ot->exec = animchannels_setflag_exec;
1845         ot->poll = animedit_poll_channels_active;
1846         
1847         /* flags */
1848         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1849         
1850         /* props */
1851         /* flag-setting mode */
1852         prop = RNA_def_enum(ot->srna, "mode", prop_animchannel_setflag_types, ACHANNEL_SETFLAG_TOGGLE, "Mode", "");
1853         RNA_def_property_flag(prop, PROP_HIDDEN); /* internal hack - don't expose */
1854         /* setting to set */
1855         ot->prop = RNA_def_enum(ot->srna, "type", prop_animchannel_settings_types, 0, "Type", "");
1856 }
1857
1858 static void ANIM_OT_channels_editable_toggle(wmOperatorType *ot)
1859 {
1860         PropertyRNA *prop;
1861         
1862         /* identifiers */
1863         ot->name = "Toggle Channel Editability";
1864         ot->idname = "ANIM_OT_channels_editable_toggle";
1865         ot->description = "Toggle editability of selected channels";
1866         
1867         /* api callbacks */
1868         ot->exec = animchannels_setflag_exec;
1869         ot->poll = animedit_poll_channels_active;
1870         
1871         /* flags */
1872         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1873         
1874         /* props */
1875         /* flag-setting mode */
1876         RNA_def_enum(ot->srna, "mode", prop_animchannel_setflag_types, ACHANNEL_SETFLAG_TOGGLE, "Mode", "");
1877         /* setting to set */
1878         prop = RNA_def_enum(ot->srna, "type", prop_animchannel_settings_types, ACHANNEL_SETTING_PROTECT, "Type", "");
1879         RNA_def_property_flag(prop, PROP_HIDDEN); /* internal hack - don't expose */
1880 }
1881
1882 /* ********************** Expand Channels Operator *********************** */
1883
1884 static int animchannels_expand_exec(bContext *C, wmOperator *op)
1885 {
1886         bAnimContext ac;
1887         short onlysel = 1;
1888         
1889         /* get editor data */
1890         if (ANIM_animdata_get_context(C, &ac) == 0)
1891                 return OPERATOR_CANCELLED;
1892                 
1893         /* only affect selected channels? */
1894         if (RNA_boolean_get(op->ptr, "all"))
1895                 onlysel = 0;
1896         
1897         /* modify setting */
1898         setflag_anim_channels(&ac, ACHANNEL_SETTING_EXPAND, ACHANNEL_SETFLAG_ADD, onlysel, 0);
1899         
1900         /* send notifier that things have changed */
1901         WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, NULL);
1902         
1903         return OPERATOR_FINISHED;
1904 }
1905
1906 static void ANIM_OT_channels_expand(wmOperatorType *ot)
1907 {
1908         /* identifiers */
1909         ot->name = "Expand Channels";
1910         ot->idname = "ANIM_OT_channels_expand";
1911         ot->description = "Expand (i.e. open) all selected expandable animation channels";
1912         
1913         /* api callbacks */
1914         ot->exec = animchannels_expand_exec;
1915         ot->poll = animedit_poll_channels_active;
1916         
1917         /* flags */
1918         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1919         
1920         /* props */
1921         ot->prop = RNA_def_boolean(ot->srna, "all", 1, "All", "Expand all channels (not just selected ones)");
1922 }
1923
1924 /* ********************** Collapse Channels Operator *********************** */
1925
1926 static int animchannels_collapse_exec(bContext *C, wmOperator *op)
1927 {
1928         bAnimContext ac;
1929         short onlysel = 1;
1930         
1931         /* get editor data */
1932         if (ANIM_animdata_get_context(C, &ac) == 0)
1933                 return OPERATOR_CANCELLED;
1934                 
1935         /* only affect selected channels? */
1936         if (RNA_boolean_get(op->ptr, "all"))
1937                 onlysel = 0;
1938         
1939         /* modify setting */
1940         setflag_anim_channels(&ac, ACHANNEL_SETTING_EXPAND, ACHANNEL_SETFLAG_CLEAR, onlysel, 0);
1941         
1942         /* send notifier that things have changed */
1943         WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, NULL);
1944         
1945         return OPERATOR_FINISHED;
1946 }
1947
1948 static void ANIM_OT_channels_collapse(wmOperatorType *ot)
1949 {
1950         /* identifiers */
1951         ot->name = "Collapse Channels";
1952         ot->idname = "ANIM_OT_channels_collapse";
1953         ot->description = "Collapse (i.e. close) all selected expandable animation channels";
1954         
1955         /* api callbacks */
1956         ot->exec = animchannels_collapse_exec;
1957         ot->poll = animedit_poll_channels_active;
1958         
1959         /* flags */
1960         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1961         
1962         /* props */
1963         ot->prop = RNA_def_boolean(ot->srna, "all", 1, "All", "Collapse all channels (not just selected ones)");
1964 }
1965
1966 /* ******************* Reenable Disabled Operator ******************* */
1967
1968 static int animchannels_enable_poll(bContext *C)
1969 {
1970         ScrArea *sa = CTX_wm_area(C);
1971
1972         /* channels region test */
1973         /* TODO: could enhance with actually testing if channels region? */
1974         if (ELEM(NULL, sa, CTX_wm_region(C)))
1975                 return 0;
1976                 
1977         /* animation editor test - Action/Dopesheet/etc. and Graph only */
1978         if (ELEM(sa->spacetype, SPACE_ACTION, SPACE_IPO) == 0)
1979                 return 0;
1980                 
1981         return 1;
1982 }
1983
1984 static int animchannels_enable_exec(bContext *C, wmOperator *UNUSED(op))
1985 {
1986         bAnimContext ac;
1987         
1988         ListBase anim_data = {NULL, NULL};
1989         bAnimListElem *ale;
1990         int filter;
1991         
1992         /* get editor data */
1993         if (ANIM_animdata_get_context(C, &ac) == 0)
1994                 return OPERATOR_CANCELLED;
1995         
1996         /* filter data */
1997         filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_NODUPLIS);
1998         ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
1999         
2000         /* loop through filtered data and clean curves */
2001         for (ale = anim_data.first; ale; ale = ale->next) {
2002                 FCurve *fcu = (FCurve *)ale->data;
2003                 
2004                 /* remove disabled flags from F-Curves */
2005                 fcu->flag &= ~FCURVE_DISABLED;
2006                 
2007                 /* for drivers, let's do the same too */
2008                 if (fcu->driver)
2009                         fcu->driver->flag &= ~DRIVER_FLAG_INVALID;
2010                         
2011                 /* tag everything for updates - in particular, this is needed to get drivers working again */
2012                 ANIM_list_elem_update(ac.scene, ale);
2013         }
2014         
2015         /* free temp data */
2016         BLI_freelistN(&anim_data);
2017                 
2018         /* send notifier that things have changed */
2019         WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, NULL);
2020         
2021         return OPERATOR_FINISHED;
2022 }
2023
2024 static void ANIM_OT_channels_fcurves_enable(wmOperatorType *ot)
2025 {
2026         /* identifiers */
2027         ot->name = "Revive Disabled F-Curves";
2028         ot->idname = "ANIM_OT_channels_fcurves_enable";
2029         ot->description = "Clears 'disabled' tag from all F-Curves to get broken F-Curves working again";
2030         
2031         /* api callbacks */
2032         ot->exec = animchannels_enable_exec;
2033         ot->poll = animchannels_enable_poll;
2034         
2035         /* flags */
2036         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2037 }
2038
2039 /* ********************** Select All Operator *********************** */
2040
2041 static int animchannels_deselectall_exec(bContext *C, wmOperator *op)
2042 {
2043         bAnimContext ac;
2044         
2045         /* get editor data */
2046         if (ANIM_animdata_get_context(C, &ac) == 0)
2047                 return OPERATOR_CANCELLED;
2048                 
2049         /* 'standard' behavior - check if selected, then apply relevant selection */
2050         if (RNA_boolean_get(op->ptr, "invert"))
2051                 ANIM_deselect_anim_channels(&ac, ac.data, ac.datatype, 0, ACHANNEL_SETFLAG_TOGGLE);
2052         else
2053                 ANIM_deselect_anim_channels(&ac, ac.data, ac.datatype, 1, ACHANNEL_SETFLAG_ADD);
2054         
2055         /* send notifier that things have changed */
2056         WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_SELECTED, NULL);
2057         
2058         return OPERATOR_FINISHED;
2059 }
2060  
2061 static void ANIM_OT_channels_select_all_toggle(wmOperatorType *ot)
2062 {
2063         /* identifiers */
2064         ot->name = "Select All";
2065         ot->idname = "ANIM_OT_channels_select_all_toggle";
2066         ot->description = "Toggle selection of all animation channels";
2067         
2068         /* api callbacks */
2069         ot->exec = animchannels_deselectall_exec;
2070         ot->poll = animedit_poll_channels_nla_tweakmode_off;
2071         
2072         /* flags */
2073         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2074         
2075         /* props */
2076         ot->prop = RNA_def_boolean(ot->srna, "invert", 0, "Invert", "");
2077 }
2078
2079 /* ******************** Borderselect Operator *********************** */
2080
2081 static void borderselect_anim_channels(bAnimContext *ac, rcti *rect, short selectmode)
2082 {
2083         ListBase anim_data = {NULL, NULL};
2084         bAnimListElem *ale;
2085         int filter;
2086         
2087         SpaceNla *snla = (SpaceNla *)ac->sl;
2088         View2D *v2d = &ac->ar->v2d;
2089         rctf rectf;
2090         float ymin, ymax;
2091         
2092         /* set initial y extents */
2093         if (ac->datatype == ANIMCONT_NLA) {
2094                 ymin = (float)(-NLACHANNEL_HEIGHT(snla));
2095                 ymax = 0.0f;
2096         }
2097         else {
2098                 ymin = 0.0f;
2099                 ymax = (float)(-ACHANNEL_HEIGHT);
2100         }
2101         
2102         /* convert border-region to view coordinates */
2103         UI_view2d_region_to_view(v2d, rect->xmin, rect->ymin + 2, &rectf.xmin, &rectf.ymin);
2104         UI_view2d_region_to_view(v2d, rect->xmax, rect->ymax - 2, &rectf.xmax, &rectf.ymax);
2105         
2106         /* filter data */
2107         filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_LIST_CHANNELS);
2108         ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
2109         
2110         /* loop over data, doing border select */
2111         for (ale = anim_data.first; ale; ale = ale->next) {
2112                 if (ac->datatype == ANIMCONT_NLA)
2113                         ymin = ymax - NLACHANNEL_STEP(snla);
2114                 else
2115                         ymin = ymax - ACHANNEL_STEP;
2116                 
2117                 /* if channel is within border-select region, alter it */
2118                 if (!((ymax < rectf.ymin) || (ymin > rectf.ymax))) {
2119                         /* set selection flags only */
2120                         ANIM_channel_setting_set(ac, ale, ACHANNEL_SETTING_SELECT, selectmode);
2121                         
2122                         /* type specific actions */
2123                         switch (ale->type) {
2124                                 case ANIMTYPE_GROUP:
2125                                 {
2126                                         bActionGroup *agrp = (bActionGroup *)ale->data;
2127                                         
2128                                         /* always clear active flag after doing this */
2129                                         agrp->flag &= ~AGRP_ACTIVE;
2130                                 }
2131                                 break;
2132                                 case ANIMTYPE_NLATRACK:
2133                                 {
2134                                         NlaTrack *nlt = (NlaTrack *)ale->data;
2135                                         
2136                                         /* for now, it's easier just to do this here manually, as defining a new type 
2137                                          * currently adds complications when doing other stuff 
2138                                          */
2139                                         ACHANNEL_SET_FLAG(nlt, selectmode, NLATRACK_SELECTED);
2140                                 }
2141                                 break;
2142                         }
2143                 }
2144                 
2145                 /* set minimum extent to be the maximum of the next channel */
2146                 ymax = ymin;
2147         }
2148         
2149         /* cleanup */
2150         BLI_freelistN(&anim_data);
2151 }
2152
2153 /* ------------------- */
2154
2155 static int animchannels_borderselect_exec(bContext *C, wmOperator *op)
2156 {
2157         bAnimContext ac;
2158         rcti rect;
2159         short selectmode = 0;
2160         int gesture_mode, extend;
2161         
2162         /* get editor data */
2163         if (ANIM_animdata_get_context(C, &ac) == 0)
2164                 return OPERATOR_CANCELLED;
2165         
2166         /* get settings from operator */
2167         WM_operator_properties_border_to_rcti(op, &rect);
2168         
2169         gesture_mode = RNA_int_get(op->ptr, "gesture_mode");
2170         extend = RNA_boolean_get(op->ptr, "extend");
2171
2172         if (!extend)
2173                 ANIM_deselect_anim_channels(&ac, ac.data, ac.datatype, 1, ACHANNEL_SETFLAG_CLEAR);
2174
2175         if (gesture_mode == GESTURE_MODAL_SELECT)
2176                 selectmode = ACHANNEL_SETFLAG_ADD;
2177         else
2178                 selectmode = ACHANNEL_SETFLAG_CLEAR;
2179         
2180         /* apply borderselect animation channels */
2181         borderselect_anim_channels(&ac, &rect, selectmode);
2182         
2183         /* send notifier that things have changed */
2184         WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_SELECTED, NULL);
2185         
2186         return OPERATOR_FINISHED;
2187
2188
2189 static void ANIM_OT_channels_select_border(wmOperatorType *ot)
2190 {
2191         /* identifiers */
2192         ot->name = "Border Select";
2193         ot->idname = "ANIM_OT_channels_select_border";
2194         ot->description = "Select all animation channels within the specified region";
2195         
2196         /* api callbacks */
2197         ot->invoke = WM_border_select_invoke;
2198         ot->exec = animchannels_borderselect_exec;
2199         ot->modal = WM_border_select_modal;
2200         ot->cancel = WM_border_select_cancel;
2201         
2202         ot->poll = animedit_poll_channels_nla_tweakmode_off;
2203         
2204         /* flags */
2205         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2206         
2207         /* rna */
2208         WM_operator_properties_gesture_border(ot, TRUE);
2209 }
2210
2211 /* ******************* Rename Operator ***************************** */
2212 /* Allow renaming some channels by clicking on them */
2213
2214 static void rename_anim_channels(bAnimContext *ac, int channel_index)
2215 {
2216         ListBase anim_data = {NULL, NULL};
2217         bAnimChannelType *acf;
2218         bAnimListElem *ale;
2219         int filter;
2220         
2221         /* get the channel that was clicked on */
2222         /* filter channels */
2223         filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_LIST_CHANNELS);
2224         ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
2225         
2226         /* get channel from index */
2227         ale = BLI_findlink(&anim_data, channel_index);
2228         if (ale == NULL) {
2229                 /* channel not found */
2230                 if (G.debug & G_DEBUG)
2231                         printf("Error: animation channel (index = %d) not found in rename_anim_channels()\n", channel_index);
2232                 
2233                 BLI_freelistN(&anim_data);
2234                 return;
2235         }
2236         
2237         /* check that channel can be renamed */
2238         acf = ANIM_channel_get_typeinfo(ale);
2239         if (acf && acf->name_prop) {
2240                 PointerRNA ptr;
2241                 PropertyRNA *prop;
2242                 
2243                 /* ok if we can get name property to edit from this channel */
2244                 if (acf->name_prop(ale, &ptr, &prop)) {
2245                         /* actually showing the rename textfield is done on redraw,
2246                          * so here we just store the index of this channel in the 
2247                          * dopesheet data, which will get utilized when drawing the
2248                          * channel...
2249                          *
2250                          * +1 factor is for backwards compat issues
2251                          */
2252                         if (ac->ads) {
2253                                 ac->ads->renameIndex = channel_index + 1;
2254                         }
2255                 }
2256         }
2257         
2258         /* free temp data and tag for refresh */
2259         BLI_freelistN(&anim_data);
2260         ED_region_tag_redraw(ac->ar);
2261 }
2262
2263 static int animchannels_rename_invoke(bContext *C, wmOperator *UNUSED(op), wmEvent *evt)
2264 {
2265         bAnimContext ac;
2266         ARegion *ar;
2267         View2D *v2d;
2268         int channel_index;
2269         float x, y;
2270         
2271         /* get editor data */
2272         if (ANIM_animdata_get_context(C, &ac) == 0)
2273                 return OPERATOR_CANCELLED;
2274                 
2275         /* get useful pointers from animation context data */
2276         ar = ac.ar;
2277         v2d = &ar->v2d;
2278         
2279         /* figure out which channel user clicked in 
2280          * Note: although channels technically start at (y = ACHANNEL_FIRST), we need to adjust by half a channel's height
2281          *              so that the tops of channels get caught ok. Since ACHANNEL_FIRST is really ACHANNEL_HEIGHT, we simply use
2282          *              ACHANNEL_HEIGHT_HALF.
2283          */
2284         UI_view2d_region_to_view(v2d, evt->mval[0], evt->mval[1], &x, &y);
2285         
2286         if (ac.datatype == ANIMCONT_NLA) {
2287                 SpaceNla *snla = (SpaceNla *)ac.sl;
2288                 UI_view2d_listview_view_to_cell(v2d, NLACHANNEL_NAMEWIDTH, NLACHANNEL_STEP(snla), 0, (float)NLACHANNEL_HEIGHT_HALF(snla), x, y, NULL, &channel_index);
2289         }
2290         else {
2291                 UI_view2d_listview_view_to_cell(v2d, ACHANNEL_NAMEWIDTH, ACHANNEL_STEP, 0, (float)ACHANNEL_HEIGHT_HALF, x, y, NULL, &channel_index);
2292         }
2293         
2294         /* handle click */
2295         rename_anim_channels(&ac, channel_index);
2296         
2297         return OPERATOR_FINISHED;
2298 }
2299
2300 static void ANIM_OT_channels_rename(wmOperatorType *ot)
2301 {
2302         /* identifiers */
2303         ot->name = "Rename Channels";
2304         ot->idname = "ANIM_OT_channels_rename";
2305         ot->description = "Rename animation channel under mouse";
2306         
2307         /* api callbacks */
2308         ot->invoke = animchannels_rename_invoke;
2309         ot->poll = animedit_poll_channels_active;
2310 }
2311
2312 /* ******************** Mouse-Click Operator *********************** */
2313 /* Handle selection changes due to clicking on channels. Settings will get caught by UI code... */
2314
2315 static int mouse_anim_channels(bAnimContext *ac, float UNUSED(x), int channel_index, short selectmode)
2316 {
2317         ListBase anim_data = {NULL, NULL};
2318         bAnimListElem *ale;
2319         int filter;
2320         int notifierFlags = 0;
2321         
2322         /* get the channel that was clicked on */
2323         /* filter channels */
2324         filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_LIST_CHANNELS);
2325         ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
2326         
2327         /* get channel from index */
2328         ale = BLI_findlink(&anim_data, channel_index);
2329         if (ale == NULL) {
2330                 /* channel not found */
2331                 if (G.debug & G_DEBUG)
2332                         printf("Error: animation channel (index = %d) not found in mouse_anim_channels()\n", channel_index);
2333                 
2334                 BLI_freelistN(&anim_data);
2335                 return 0;
2336         }
2337
2338         /* selectmode -1 is a special case for ActionGroups only, which selects all of the channels underneath it only... */
2339         /* TODO: should this feature be extended to work with other channel types too? */
2340         if ((selectmode == -1) && (ale->type != ANIMTYPE_GROUP)) {
2341                 /* normal channels should not behave normally in this case */
2342                 BLI_freelistN(&anim_data);
2343                 return 0;
2344         }
2345
2346         /* action to take depends on what channel we've got */
2347         /* WARNING: must keep this in sync with the equivalent function in nla_channels.c */
2348         switch (ale->type) {
2349                 case ANIMTYPE_SCENE:
2350                 {
2351                         Scene *sce = (Scene *)ale->data;
2352                         AnimData *adt = sce->adt;
2353                         
2354                         /* set selection status */
2355                         if (selectmode == SELECT_INVERT) {
2356                                 /* swap select */
2357                                 sce->flag ^= SCE_DS_SELECTED;
2358                                 if (adt) adt->flag ^= ADT_UI_SELECTED;
2359                         }
2360                         else {
2361                                 sce->flag |= SCE_DS_SELECTED;
2362                                 if (adt) adt->flag |= ADT_UI_SELECTED;
2363                         }
2364                         
2365                         notifierFlags |= (ND_ANIMCHAN | NA_SELECTED);
2366                 }
2367                 break;
2368                 case ANIMTYPE_OBJECT:
2369                 {
2370                         bDopeSheet *ads = (bDopeSheet *)ac->data;
2371                         Scene *sce = (Scene *)ads->source;
2372                         Base *base = (Base *)ale->data;
2373                         Object *ob = base->object;
2374                         AnimData *adt = ob->adt;
2375                         
2376                         /* set selection status */
2377                         if (selectmode == SELECT_INVERT) {
2378                                 /* swap select */
2379                                 base->flag ^= SELECT;
2380                                 ob->flag = base->flag;
2381                                 
2382                                 if (adt) adt->flag ^= ADT_UI_SELECTED;
2383                         }
2384                         else {
2385                                 Base *b;
2386                                 
2387                                 /* deselect all */
2388                                 /* TODO: should this deselect all other types of channels too? */
2389                                 for (b = sce->base.first; b; b = b->next) {
2390                                         b->flag &= ~SELECT;
2391                                         b->object->flag = b->flag;
2392                                         if (b->object->adt) b->object->adt->flag &= ~(ADT_UI_SELECTED | ADT_UI_ACTIVE);
2393                                 }
2394                                 
2395                                 /* select object now */
2396                                 base->flag |= SELECT;
2397                                 ob->flag |= SELECT;
2398                                 if (adt) adt->flag |= ADT_UI_SELECTED;
2399                         }
2400                         
2401                         if ((adt) && (adt->flag & ADT_UI_SELECTED))
2402                                 adt->flag |= ADT_UI_ACTIVE;
2403                         
2404                         notifierFlags |= (ND_ANIMCHAN | NA_SELECTED);
2405                 }
2406                 break;
2407                 
2408                 case ANIMTYPE_FILLACTD: /* Action Expander */
2409                 case ANIMTYPE_DSMAT:    /* Datablock AnimData Expanders */
2410                 case ANIMTYPE_DSLAM:
2411                 case ANIMTYPE_DSCAM:
2412                 case ANIMTYPE_DSCUR:
2413                 case ANIMTYPE_DSSKEY:
2414                 case ANIMTYPE_DSWOR:
2415                 case ANIMTYPE_DSPART:
2416                 case ANIMTYPE_DSMBALL:
2417                 case ANIMTYPE_DSARM:
2418                 case ANIMTYPE_DSMESH:
2419                 case ANIMTYPE_DSNTREE:
2420                 case ANIMTYPE_DSTEX:
2421                 case ANIMTYPE_DSLAT:
2422                 case ANIMTYPE_DSSPK:
2423                 {
2424                         /* sanity checking... */
2425                         if (ale->adt) {
2426                                 /* select/deselect */
2427                                 if (selectmode == SELECT_INVERT) {
2428                                         /* inverse selection status of this AnimData block only */
2429                                         ale->adt->flag ^= ADT_UI_SELECTED;
2430                                 }
2431                                 else {
2432                                         /* select AnimData block by itself */
2433                                         ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, 0, ACHANNEL_SETFLAG_CLEAR);
2434                                         ale->adt->flag |= ADT_UI_SELECTED;
2435                                 }
2436                                 
2437                                 /* set active? */
2438                                 if ((ale->adt) && (ale->adt->flag & ADT_UI_SELECTED))
2439                                         ale->adt->flag |= ADT_UI_ACTIVE;
2440                         }
2441                         
2442                         notifierFlags |= (ND_ANIMCHAN | NA_SELECTED);
2443                 }
2444                 break;
2445                 
2446                 case ANIMTYPE_GROUP: 
2447                 {
2448                         bActionGroup *agrp = (bActionGroup *)ale->data;
2449                         
2450                         /* select/deselect group */
2451                         if (selectmode == SELECT_INVERT) {
2452                                 /* inverse selection status of this group only */
2453                                 agrp->flag ^= AGRP_SELECTED;
2454                         }
2455                         else if (selectmode == -1) {
2456                                 /* select all in group (and deselect everthing else) */
2457                                 FCurve *fcu;
2458                                 
2459                                 /* deselect all other channels */
2460                                 ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, 0, ACHANNEL_SETFLAG_CLEAR);
2461                                 
2462                                 /* only select channels in group and group itself */
2463                                 for (fcu = agrp->channels.first; fcu && fcu->grp == agrp; fcu = fcu->next)
2464                                         fcu->flag |= FCURVE_SELECTED;
2465                                 agrp->flag |= AGRP_SELECTED;
2466                         }
2467                         else {
2468                                 /* select group by itself */
2469                                 ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, 0, ACHANNEL_SETFLAG_CLEAR);
2470                                 agrp->flag |= AGRP_SELECTED;
2471                         }
2472                         
2473                         /* if group is selected now, make group the 'active' one in the visible list */
2474                         if (agrp->flag & AGRP_SELECTED)
2475                                 ANIM_set_active_channel(ac, ac->data, ac->datatype, filter, agrp, ANIMTYPE_GROUP);
2476                         else
2477                                 ANIM_set_active_channel(ac, ac->data, ac->datatype, filter, NULL, ANIMTYPE_GROUP);
2478                                 
2479                         notifierFlags |= (ND_ANIMCHAN | NA_SELECTED);
2480                 }
2481                 break;
2482                 case ANIMTYPE_FCURVE: 
2483                 {
2484                         FCurve *fcu = (FCurve *)ale->data;
2485                         
2486                         /* select/deselect */
2487                         if (selectmode == SELECT_INVERT) {
2488                                 /* inverse selection status of this F-Curve only */
2489                                 fcu->flag ^= FCURVE_SELECTED;
2490                         }
2491                         else {
2492                                 /* select F-Curve by itself */
2493                                 ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, 0, ACHANNEL_SETFLAG_CLEAR);
2494                                 fcu->flag |= FCURVE_SELECTED;
2495                         }
2496                         
2497                         /* if F-Curve is selected now, make F-Curve the 'active' one in the visible list */
2498                         if (fcu->flag & FCURVE_SELECTED)
2499                                 ANIM_set_active_channel(ac, ac->data, ac->datatype, filter, fcu, ANIMTYPE_FCURVE);
2500                                 
2501                         notifierFlags |= (ND_ANIMCHAN | NA_SELECTED);
2502                 }
2503                 break;
2504                 case ANIMTYPE_SHAPEKEY: 
2505                 {
2506                         KeyBlock *kb = (KeyBlock *)ale->data;
2507                         
2508                         /* select/deselect */
2509                         if (selectmode == SELECT_INVERT) {
2510                                 /* inverse selection status of this ShapeKey only */
2511                                 kb->flag ^= KEYBLOCK_SEL;
2512                         }
2513                         else {
2514                                 /* select ShapeKey by itself */
2515                                 ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, 0, ACHANNEL_SETFLAG_CLEAR);
2516                                 kb->flag |= KEYBLOCK_SEL;
2517                         }
2518                                 
2519                         notifierFlags |= (ND_ANIMCHAN | NA_SELECTED);
2520                 }
2521                 break;
2522                 case ANIMTYPE_GPDATABLOCK:
2523                 {
2524                         bGPdata *gpd = (bGPdata *)ale->data;
2525                         
2526                         /* toggle expand 
2527                          *      - although the triangle widget already allows this, the whole channel can also be used for this purpose
2528                          */
2529                         gpd->flag ^= GP_DATA_EXPAND;
2530                         
2531                         notifierFlags |= (ND_ANIMCHAN | NA_EDITED);
2532                 }
2533                 break;
2534                 case ANIMTYPE_GPLAYER:
2535                 {
2536                         bGPDlayer *gpl = (bGPDlayer *)ale->data;
2537                         
2538                         /* select/deselect */
2539                         if (selectmode == SELECT_INVERT) {
2540                                 /* invert selection status of this layer only */
2541                                 gpl->flag ^= GP_LAYER_SELECT;
2542                         }
2543                         else {
2544                                 /* select layer by itself */
2545                                 ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, 0, ACHANNEL_SETFLAG_CLEAR);
2546                                 gpl->flag |= GP_LAYER_SELECT;
2547                         }
2548                         
2549                         notifierFlags |= (ND_ANIMCHAN | NA_EDITED);
2550                 }
2551                 break;
2552                 case ANIMTYPE_MASKDATABLOCK:
2553                 {
2554                         Mask *mask = (Mask *)ale->data;
2555                         
2556                         /* toggle expand
2557                          *      - although the triangle widget already allows this, the whole channel can also be used for this purpose
2558                          */
2559                         mask->flag ^= MASK_ANIMF_EXPAND;
2560                         
2561                         notifierFlags |= (ND_ANIMCHAN | NA_EDITED);
2562                 }
2563                 break;
2564                 case ANIMTYPE_MASKLAYER:
2565                 {
2566                         MaskLayer *masklay = (MaskLayer *)ale->data;
2567                         
2568                         /* select/deselect */
2569                         if (selectmode == SELECT_INVERT) {
2570                                 /* invert selection status of this layer only */
2571                                 masklay->flag ^= MASK_LAYERFLAG_SELECT;
2572                         }
2573                         else {
2574                                 /* select layer by itself */
2575                                 ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, 0, ACHANNEL_SETFLAG_CLEAR);
2576                                 masklay->flag |= MASK_LAYERFLAG_SELECT;
2577                         }
2578                         
2579                         notifierFlags |= (ND_ANIMCHAN | NA_EDITED);
2580                 }
2581                 break;
2582                 default:
2583                         if (G.debug & G_DEBUG)
2584                                 printf("Error: Invalid channel type in mouse_anim_channels()\n");
2585         }
2586         
2587         /* free channels */
2588         BLI_freelistN(&anim_data);
2589         
2590         /* return notifier flags */
2591         return notifierFlags;
2592 }
2593
2594 /* ------------------- */
2595
2596 /* handle clicking */
2597 static int animchannels_mouseclick_invoke(bContext *C, wmOperator *op, wmEvent *event)
2598 {
2599         bAnimContext ac;
2600         ARegion *ar;
2601         View2D *v2d;
2602         int channel_index;
2603         int notifierFlags = 0;
2604         short selectmode;
2605         float x, y;
2606         
2607         
2608         /* get editor data */
2609         if (ANIM_animdata_get_context(C, &ac) == 0)
2610                 return OPERATOR_CANCELLED;
2611                 
2612         /* get useful pointers from animation context data */
2613         ar = ac.ar;
2614         v2d = &ar->v2d;
2615         
2616         /* select mode is either replace (deselect all, then add) or add/extend */
2617         if (RNA_boolean_get(op->ptr, "extend"))
2618                 selectmode = SELECT_INVERT;
2619         else if (RNA_boolean_get(op->ptr, "children_only"))
2620                 selectmode = -1;  /* this is a bit of a special case for ActionGroups only... should it be removed or extended to all instead? */
2621         else
2622                 selectmode = SELECT_REPLACE;
2623         
2624         /* figure out which channel user clicked in 
2625          * Note: although channels technically start at (y = ACHANNEL_FIRST), we need to adjust by half a channel's height
2626          *              so that the tops of channels get caught ok. Since ACHANNEL_FIRST is really ACHANNEL_HEIGHT, we simply use
2627          *              ACHANNEL_HEIGHT_HALF.
2628          */
2629         UI_view2d_region_to_view(v2d, event->mval[0], event->mval[1], &x, &y);
2630         UI_view2d_listview_view_to_cell(v2d, ACHANNEL_NAMEWIDTH, ACHANNEL_STEP, 0, (float)ACHANNEL_HEIGHT_HALF, x, y, NULL, &channel_index);
2631         
2632         /* handle mouse-click in the relevant channel then */
2633         notifierFlags = mouse_anim_channels(&ac, x, channel_index, selectmode);
2634         
2635         /* set notifier that things have changed */
2636         WM_event_add_notifier(C, NC_ANIMATION | notifierFlags, NULL);
2637         
2638         return OPERATOR_FINISHED;
2639 }
2640  
2641 static void ANIM_OT_channels_click(wmOperatorType *ot)
2642 {
2643         PropertyRNA *prop;
2644         
2645         /* identifiers */
2646         ot->name = "Mouse Click on Channels";
2647         ot->idname = "ANIM_OT_channels_click";
2648         ot->description = "Handle mouse-clicks over animation channels";
2649         
2650         /* api callbacks */
2651         ot->invoke = animchannels_mouseclick_invoke;
2652         ot->poll = animedit_poll_channels_active;
2653         
2654         /* flags */
2655         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2656         
2657         /* properties */
2658         /* NOTE: don't save settings, otherwise, can end up with some weird behaviour (sticky extend) */
2659         prop = RNA_def_boolean(ot->srna, "extend", 0, "Extend Select", ""); // SHIFTKEY
2660         RNA_def_property_flag(prop, PROP_SKIP_SAVE);
2661         
2662         prop = RNA_def_boolean(ot->srna, "children_only", 0, "Select Children Only", ""); // CTRLKEY|SHIFTKEY
2663         RNA_def_property_flag(prop, PROP_SKIP_SAVE);
2664 }
2665
2666 /* ************************************************************************** */
2667 /* Operator Registration */
2668
2669 void ED_operatortypes_animchannels(void)
2670 {
2671         WM_operatortype_append(ANIM_OT_channels_select_all_toggle);
2672         WM_operatortype_append(ANIM_OT_channels_select_border);
2673         
2674         WM_operatortype_append(ANIM_OT_channels_click);
2675         WM_operatortype_append(ANIM_OT_channels_rename);
2676         
2677         WM_operatortype_append(ANIM_OT_channels_setting_enable);
2678         WM_operatortype_append(ANIM_OT_channels_setting_disable);
2679         WM_operatortype_append(ANIM_OT_channels_setting_toggle);
2680
2681         WM_operatortype_append(ANIM_OT_channels_delete);
2682
2683         /* XXX does this need to be a separate operator? */
2684         WM_operatortype_append(ANIM_OT_channels_editable_toggle);
2685         
2686         WM_operatortype_append(ANIM_OT_channels_move);
2687         
2688         WM_operatortype_append(ANIM_OT_channels_expand);
2689         WM_operatortype_append(ANIM_OT_channels_collapse);
2690         
2691         WM_operatortype_append(ANIM_OT_channels_visibility_toggle);
2692         WM_operatortype_append(ANIM_OT_channels_visibility_set);
2693         
2694         WM_operatortype_append(ANIM_OT_channels_fcurves_enable);
2695         
2696         WM_operatortype_append(ANIM_OT_channels_group);
2697         WM_operatortype_append(ANIM_OT_channels_ungroup);
2698 }
2699
2700 // TODO: check on a poll callback for this, to get hotkeys into menus
2701 void ED_keymap_animchannels(wmKeyConfig *keyconf)
2702 {
2703         wmKeyMap *keymap = WM_keymap_find(keyconf, "Animation Channels", 0, 0);
2704         wmKeyMapItem *kmi;
2705
2706         /* selection */
2707         /* click-select */
2708         /* XXX for now, only leftmouse.... */
2709         WM_keymap_add_item(keymap, "ANIM_OT_channels_click", LEFTMOUSE, KM_PRESS, 0, 0);
2710         RNA_boolean_set(WM_keymap_add_item(keymap, "ANIM_OT_channels_click", LEFTMOUSE, KM_PRESS, KM_SHIFT, 0)->ptr, "extend", TRUE);
2711         RNA_boolean_set(WM_keymap_add_item(keymap, "ANIM_OT_channels_click", LEFTMOUSE, KM_PRESS, KM_CTRL | KM_SHIFT, 0)->ptr, "children_only", TRUE);
2712
2713         /* rename */
2714         WM_keymap_add_item(keymap, "ANIM_OT_channels_rename", LEFTMOUSE, KM_PRESS, KM_CTRL, 0);
2715         
2716         /* deselect all */
2717         WM_keymap_add_item(keymap, "ANIM_OT_channels_select_all_toggle", AKEY, KM_PRESS, 0, 0);
2718         RNA_boolean_set(WM_keymap_add_item(keymap, "ANIM_OT_channels_select_all_toggle", IKEY, KM_PRESS, KM_CTRL, 0)->ptr, "invert", TRUE);
2719         
2720         /* borderselect */
2721         WM_keymap_add_item(keymap, "ANIM_OT_channels_select_border", BKEY, KM_PRESS, 0, 0);
2722         WM_keymap_add_item(keymap, "ANIM_OT_channels_select_border", EVT_TWEAK_L, KM_ANY, 0, 0);
2723         
2724         /* delete */
2725         WM_keymap_add_item(keymap, "ANIM_OT_channels_delete", XKEY, KM_PRESS, 0, 0);
2726         WM_keymap_add_item(keymap, "ANIM_OT_channels_delete", DELKEY, KM_PRESS, 0, 0);
2727         
2728         /* settings */
2729         WM_keymap_add_item(keymap, "ANIM_OT_channels_setting_toggle", WKEY, KM_PRESS, KM_SHIFT, 0);
2730         WM_keymap_add_item(keymap, "ANIM_OT_channels_setting_enable", WKEY, KM_PRESS, KM_CTRL | KM_SHIFT, 0);
2731         WM_keymap_add_item(keymap, "ANIM_OT_channels_setting_disable", WKEY, KM_PRESS, KM_ALT, 0);
2732         
2733         /* settings - specialized hotkeys */
2734         WM_keymap_add_item(keymap, "ANIM_OT_channels_editable_toggle", TABKEY, KM_PRESS, 0, 0);
2735         
2736         /* expand/collapse */
2737         WM_keymap_add_item(keymap, "ANIM_OT_channels_expand", PADPLUSKEY, KM_PRESS, 0, 0);
2738         WM_keymap_add_item(keymap, "ANIM_OT_channels_collapse", PADMINUS, KM_PRESS, 0, 0);
2739         
2740         kmi = WM_keymap_add_item(keymap, "ANIM_OT_channels_expand", PADPLUSKEY, KM_PRESS, KM_CTRL, 0);
2741         RNA_boolean_set(kmi->ptr, "all", FALSE);
2742         kmi = WM_keymap_add_item(keymap, "ANIM_OT_channels_collapse", PADMINUS, KM_PRESS, KM_CTRL, 0);
2743         RNA_boolean_set(kmi->ptr, "all", FALSE);
2744
2745         /* rearranging */
2746         RNA_enum_set(WM_keymap_add_item(keymap, "ANIM_OT_channels_move", PAGEUPKEY, KM_PRESS, 0, 0)->ptr, "direction", REARRANGE_ANIMCHAN_UP);
2747         RNA_enum_set(WM_keymap_add_item(keymap, "ANIM_OT_channels_move", PAGEDOWNKEY, KM_PRESS, 0, 0)->ptr, "direction", REARRANGE_ANIMCHAN_DOWN);
2748         RNA_enum_set(WM_keymap_add_item(keymap, "ANIM_OT_channels_move", PAGEUPKEY, KM_PRESS, KM_SHIFT, 0)->ptr, "direction", REARRANGE_ANIMCHAN_TOP);
2749         RNA_enum_set(WM_keymap_add_item(keymap, "ANIM_OT_channels_move", PAGEDOWNKEY, KM_PRESS, KM_SHIFT, 0)->ptr, "direction", REARRANGE_ANIMCHAN_BOTTOM);
2750         
2751         /* grouping */
2752         WM_keymap_add_item(keymap, "ANIM_OT_channels_group", GKEY, KM_PRESS, KM_CTRL, 0);
2753         WM_keymap_add_item(keymap, "ANIM_OT_channels_ungroup", GKEY, KM_PRESS, KM_ALT, 0);
2754         
2755         /* Graph Editor only */
2756         WM_keymap_add_item(keymap, "ANIM_OT_channels_visibility_set", VKEY, KM_PRESS, 0, 0);
2757         WM_keymap_add_item(keymap, "ANIM_OT_channels_visibility_toggle", VKEY, KM_PRESS, KM_SHIFT, 0);
2758 }
2759
2760 /* ************************************************************************** */