d0403d023d80dfd95c5a9e38b490a833a3a35e9e
[blender.git] / source / blender / editors / animation / anim_channels.c
1 /**
2  * $Id: editaction.c 17746 2008-12-08 11:19:44Z aligorith $
3  *
4  * ***** BEGIN GPL LICENSE BLOCK *****
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software Foundation,
18  * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
19  *
20  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
21  * All rights reserved.
22  *
23  * The Original Code is: all of this file.
24  *
25  * Contributor(s): Joshua Leung
26  *
27  * ***** END GPL LICENSE BLOCK *****
28  */
29
30 #include <math.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <float.h>
34
35 #ifdef HAVE_CONFIG_H
36 #include <config.h>
37 #endif
38
39 #include "MEM_guardedalloc.h"
40
41 #include "BLI_blenlib.h"
42 #include "BLI_arithb.h"
43
44 #include "DNA_listBase.h"
45 #include "DNA_action_types.h"
46 #include "DNA_armature_types.h"
47 #include "DNA_camera_types.h"
48 #include "DNA_curve_types.h"
49 #include "DNA_ipo_types.h"
50 #include "DNA_object_types.h"
51 #include "DNA_screen_types.h"
52 #include "DNA_scene_types.h"
53 #include "DNA_space_types.h"
54 #include "DNA_constraint_types.h"
55 #include "DNA_key_types.h"
56 #include "DNA_lamp_types.h"
57 #include "DNA_material_types.h"
58 #include "DNA_userdef_types.h"
59 #include "DNA_gpencil_types.h"
60 #include "DNA_windowmanager_types.h"
61
62 #include "RNA_access.h"
63 #include "RNA_define.h"
64
65 #include "BKE_action.h"
66 #include "BKE_depsgraph.h"
67 #include "BKE_ipo.h"
68 #include "BKE_key.h"
69 #include "BKE_material.h"
70 #include "BKE_object.h"
71 #include "BKE_context.h"
72 #include "BKE_utildefines.h"
73
74 #include "UI_interface.h"
75 #include "UI_resources.h"
76 #include "UI_view2d.h"
77
78 #include "ED_anim_api.h"
79 #include "ED_keyframes_edit.h" // XXX move the select modes out of there!
80 #include "ED_screen.h"
81 #include "ED_space_api.h"
82
83 #include "WM_api.h"
84 #include "WM_types.h"
85
86 /* ************************************************************************** */
87 /* CHANNELS API */
88
89 /* -------------------------- Internal Macros ------------------------------- */
90
91 /* set/clear/toggle macro 
92  *      - channel - channel with a 'flag' member that we're setting
93  *      - smode - 0=clear, 1=set, 2=toggle
94  *      - sflag - bitflag to set
95  */
96 #define ACHANNEL_SET_FLAG(channel, smode, sflag) \
97         { \
98                 if (smode == ACHANNEL_SETFLAG_TOGGLE)   (channel)->flag ^= (sflag); \
99                 else if (smode == ACHANNEL_SETFLAG_ADD) (channel)->flag |= (sflag); \
100                 else                                                                    (channel)->flag &= ~(sflag); \
101         }
102
103 /* -------------------------- Internal Tools -------------------------------- */
104
105 /* -------------------------- Exposed API ----------------------------------- */
106
107
108
109 /* Deselect all animation channels 
110  *      - data: pointer to datatype, as contained in bAnimContext
111  *      - datatype: the type of data that 'data' represents (eAnim_ChannelType)
112  *      - test: check if deselecting instead of selecting
113  *      - sel: eAnimChannels_SetFlag;
114  */
115 void ANIM_deselect_anim_channels (void *data, short datatype, short test, short sel)
116 {
117         ListBase anim_data = {NULL, NULL};
118         bAnimListElem *ale;
119         int filter;
120         
121         /* filter data */
122         filter= ANIMFILTER_VISIBLE;
123         ANIM_animdata_filter(&anim_data, filter, data, datatype);
124         
125         /* See if we should be selecting or deselecting */
126         if (test) {
127                 for (ale= anim_data.first; ale; ale= ale->next) {
128                         if (sel == 0) 
129                                 break;
130                         
131                         switch (ale->type) {
132                                 case ANIMTYPE_OBJECT:
133                                         if (ale->flag & SELECT)
134                                                 sel= ACHANNEL_SETFLAG_CLEAR;
135                                         break;
136                                 case ANIMTYPE_FILLACTD:
137                                         if (ale->flag & ACTC_SELECTED)
138                                                 sel= ACHANNEL_SETFLAG_CLEAR;
139                                         break;
140                                 case ANIMTYPE_GROUP:
141                                         if (ale->flag & AGRP_SELECTED)
142                                                 sel= ACHANNEL_SETFLAG_CLEAR;
143                                         break;
144                                 case ANIMTYPE_ACHAN:
145                                         if (ale->flag & ACHAN_SELECTED) 
146                                                 sel= ACHANNEL_SETFLAG_CLEAR;
147                                         break;
148                                 case ANIMTYPE_CONCHAN:
149                                         if (ale->flag & CONSTRAINT_CHANNEL_SELECT) 
150                                                 sel= ACHANNEL_SETFLAG_CLEAR;
151                                         break;
152                                 case ANIMTYPE_ICU:
153                                         if (ale->flag & IPO_SELECT)
154                                                 sel= ACHANNEL_SETFLAG_CLEAR;
155                                         break;
156                         }
157                 }
158         }
159                 
160         /* Now set the flags */
161         for (ale= anim_data.first; ale; ale= ale->next) {
162                 switch (ale->type) {
163                         case ANIMTYPE_OBJECT:
164                         {
165                                 Base *base= (Base *)ale->data;
166                                 Object *ob= base->object;
167                                 
168                                 ACHANNEL_SET_FLAG(base, sel, SELECT);
169                                 ACHANNEL_SET_FLAG(ob, sel, SELECT);
170                         }
171                                 break;
172                         case ANIMTYPE_FILLACTD:
173                         {
174                                 bAction *act= (bAction *)ale->data;
175                                 
176                                 ACHANNEL_SET_FLAG(act, sel, ACTC_SELECTED);
177                         }
178                                 break;
179                         case ANIMTYPE_GROUP:
180                         {
181                                 bActionGroup *agrp= (bActionGroup *)ale->data;
182                                 
183                                 ACHANNEL_SET_FLAG(agrp, sel, AGRP_SELECTED);
184                                 agrp->flag &= ~AGRP_ACTIVE;
185                         }
186                                 break;
187                         case ANIMTYPE_ACHAN:
188                         {
189                                 bActionChannel *achan= (bActionChannel *)ale->data;
190                                 
191                                 ACHANNEL_SET_FLAG(achan, sel, ACHAN_SELECTED);
192                                 
193                                 //select_poseelement_by_name(achan->name, sel); // XXX
194                                 achan->flag &= ~ACHAN_HILIGHTED;
195                         }
196                                 break;
197                         case ANIMTYPE_CONCHAN:
198                         {
199                                 bConstraintChannel *conchan= (bConstraintChannel *)ale->data;
200                                 
201                                 ACHANNEL_SET_FLAG(conchan, sel, CONSTRAINT_CHANNEL_SELECT);
202                         }
203                                 break;
204                         case ANIMTYPE_ICU:
205                         {
206                                 IpoCurve *icu= (IpoCurve *)ale->data;
207                                 
208                                 ACHANNEL_SET_FLAG(icu, sel, IPO_SELECT);
209                                 icu->flag &= ~IPO_ACTIVE;
210                         }
211                                 break;
212                 }
213         }
214         
215         /* Cleanup */
216         BLI_freelistN(&anim_data);
217 }
218
219 /* ************************************************************************** */
220 /* OPERATORS */
221
222 /* ********************** Select All Operator *********************** */
223
224 static int animchannels_deselectall_exec(bContext *C, wmOperator *op)
225 {
226         bAnimContext ac;
227         
228         /* get editor data */
229         if (ANIM_animdata_get_context(C, &ac) == 0)
230                 return OPERATOR_CANCELLED;
231                 
232         /* 'standard' behaviour - check if selected, then apply relevant selection */
233         if (RNA_boolean_get(op->ptr, "invert"))
234                 ANIM_deselect_anim_channels(ac.data, ac.datatype, 0, ACHANNEL_SETFLAG_TOGGLE);
235         else
236                 ANIM_deselect_anim_channels(ac.data, ac.datatype, 1, ACHANNEL_SETFLAG_ADD);
237         
238         /* set notifier tha things have changed */
239         ED_area_tag_redraw(CTX_wm_area(C)); // FIXME... should be updating 'keyframes' data context or so instead!
240         
241         return OPERATOR_FINISHED;
242 }
243  
244 void ANIM_OT_channels_deselectall (wmOperatorType *ot)
245 {
246         /* identifiers */
247         ot->name= "Select All";
248         ot->idname= "ANIM_OT_channels_deselectall";
249         
250         /* api callbacks */
251         ot->exec= animchannels_deselectall_exec;
252         ot->poll= ED_operator_areaactive;
253         
254         /* flags */
255         ot->flag= OPTYPE_REGISTER/*|OPTYPE_UNDO*/;
256         
257         /* props */
258         RNA_def_property(ot->srna, "invert", PROP_BOOLEAN, PROP_NONE);
259 }
260
261 /* ******************** Mouse-Click Operator *********************** */
262 /* Depending on the channel that was clicked on, the mouse click will activate whichever
263  * part of the channel is relevant.
264  *
265  * NOTE: eventually, this should probably be phased out when many of these things are replaced with buttons
266  */
267
268 static void mouse_anim_channels (bAnimContext *ac, float x, int channel_index, short selectmode)
269 {
270         ListBase anim_data = {NULL, NULL};
271         bAnimListElem *ale;
272         int filter;
273         
274         /* get the channel that was clicked on */
275                 /* filter channels */
276         filter= (ANIMFILTER_FORDRAWING | ANIMFILTER_VISIBLE | ANIMFILTER_CHANNELS);
277         filter= ANIM_animdata_filter(&anim_data, filter, ac->data, ac->datatype);
278         
279                 /* get channel from index */
280         ale= BLI_findlink(&anim_data, channel_index);
281         if (ale == NULL) {
282                 /* channel not found */
283                 printf("Error: animation channel not found in mouse_anim_channels() \n");
284                         // XXX remove me..
285                 printf("\t channel index = %d, channels = %d\n", channel_index, filter);
286                 
287                 BLI_freelistN(&anim_data);
288                 return;
289         }
290         
291         /* action to take depends on what channel we've got */
292         switch (ale->type) {
293                 case ANIMTYPE_OBJECT:
294                         {
295                                 bDopeSheet *ads= (bDopeSheet *)ac->data;
296                                 Scene *sce= (Scene *)ads->source;
297                                 Base *base= (Base *)ale->data;
298                                 Object *ob= base->object;
299                                 
300                                 if (x < 16) {
301                                         /* toggle expand */
302                                         ob->nlaflag ^= OB_ADS_COLLAPSED;
303                                 }
304                                 else {
305                                         /* set selection status */
306                                         // FIXME: this needs to use the new stuff...
307                                         if (selectmode == SELECT_INVERT) {
308                                                 /* swap select */
309                                                 base->flag ^= SELECT;
310                                                 ob->flag= base->flag;
311                                         }
312                                         else {
313                                                 Base *b;
314                                                 
315                                                 /* deleselect all */
316                                                 for (b= sce->base.first; b; b= b->next) {
317                                                         b->flag &= ~SELECT;
318                                                         b->object->flag= b->flag;
319                                                 }
320                                                 
321                                                 /* select object now */
322                                                 base->flag |= SELECT;
323                                                 ob->flag |= SELECT;
324                                         }
325                                         
326                                         //set_active_base(base);        /* editview.c */
327                                 }
328                         }
329                                 break;
330                 case ANIMTYPE_FILLIPOD:
331                         {
332                                 Object *ob= (Object *)ale->data;
333                                 ob->nlaflag ^= OB_ADS_SHOWIPO;
334                         }
335                                 break;
336                 case ANIMTYPE_FILLACTD:
337                         {
338                                 bAction *act= (bAction *)ale->data;
339                                 act->flag ^= ACTC_EXPANDED;
340                         }
341                                 break;
342                 case ANIMTYPE_FILLCOND:
343                         {
344                                 Object *ob= (Object *)ale->data;
345                                 ob->nlaflag ^= OB_ADS_SHOWCONS;
346                         }
347                                 break;
348                 case ANIMTYPE_FILLMATD:
349                         {
350                                 Object *ob= (Object *)ale->data;
351                                 ob->nlaflag ^= OB_ADS_SHOWMATS;
352                         }
353                                 break;
354                                 
355                 case ANIMTYPE_DSMAT:
356                         {
357                                 Material *ma= (Material *)ale->data;
358                                 ma->flag ^= MA_DS_EXPAND;
359                         }
360                                 break;
361                 case ANIMTYPE_DSLAM:
362                         {
363                                 Lamp *la= (Lamp *)ale->data;
364                                 la->flag ^= LA_DS_EXPAND;
365                         }
366                                 break;
367                 case ANIMTYPE_DSCAM:
368                         {
369                                 Camera *ca= (Camera *)ale->data;
370                                 ca->flag ^= CAM_DS_EXPAND;
371                         }
372                                 break;
373                 case ANIMTYPE_DSCUR:
374                         {
375                                 Curve *cu= (Curve *)ale->data;
376                                 cu->flag ^= CU_DS_EXPAND;
377                         }
378                                 break;
379                 case ANIMTYPE_DSSKEY:
380                         {
381                                 Key *key= (Key *)ale->data;
382                                 key->flag ^= KEYBLOCK_DS_EXPAND;
383                         }
384                                 break;
385                         
386                 case ANIMTYPE_GROUP: 
387                         {
388                                 bActionGroup *agrp= (bActionGroup *)ale->data;
389                                 short offset= (ac->datatype == ANIMCONT_DOPESHEET)? 21 : 0;
390                                 
391                                 if ((x < (offset+17)) && (agrp->channels.first)) {
392                                         /* toggle expand */
393                                         agrp->flag ^= AGRP_EXPANDED;
394                                 }
395                                 else if (x >= (ACHANNEL_NAMEWIDTH-ACHANNEL_BUTTON_WIDTH)) {
396                                         /* toggle protection/locking */
397                                         agrp->flag ^= AGRP_PROTECTED;
398                                 }
399                                 else {
400                                         /* select/deselect group */
401                                         if (selectmode == SELECT_INVERT) {
402                                                 /* inverse selection status of group */
403                                                 //select_action_group(act, agrp, SELECT_INVERT);
404                                         }
405                                         else if (/*G.qual == (LR_CTRLKEY|LR_SHIFTKEY)*/selectmode == -1) {
406                                                 // FIXME: need a special case for this!
407                                                 /* select all in group (and deselect everthing else) */ 
408                                                 //select_action_group_channels(act, agrp);
409                                                 //select_action_group(act, agrp, SELECT_ADD);
410                                         }
411                                         else {
412                                                 /* select group by itself */
413                                                 ANIM_deselect_anim_channels(ac->data, ac->datatype, 0, ACHANNEL_SETFLAG_CLEAR);
414                                                 //select_action_group(act, agrp, SELECT_ADD);
415                                         }
416                                         
417                                         // XXX
418                                         agrp->flag ^= AGRP_SELECTED;
419                                 }
420                         }
421                         break;
422                 case ANIMTYPE_ACHAN:
423                         {
424                                 bActionChannel *achan= (bActionChannel *)ale->data;
425                                 short offset= (ac->datatype == ANIMCONT_DOPESHEET)? 21 : 0;
426                                 
427                                 if (x >= (ACHANNEL_NAMEWIDTH-ACHANNEL_BUTTON_WIDTH)) {
428                                         /* toggle protect */
429                                         achan->flag ^= ACHAN_PROTECTED;
430                                 }
431                                 else if ((x >= (ACHANNEL_NAMEWIDTH-2*ACHANNEL_BUTTON_WIDTH)) && (achan->ipo)) {
432                                         /* toggle mute */
433                                         achan->ipo->muteipo = (achan->ipo->muteipo)? 0: 1;
434                                 }
435                                 else if (x <= (offset+17)) {
436                                         /* toggle expand */
437                                         achan->flag ^= ACHAN_EXPANDED;
438                                 }                               
439                                 else {
440                                         /* select/deselect achan */             
441                                         if (selectmode == SELECT_INVERT) {
442                                                 //select_channel(act, achan, SELECT_INVERT);
443                                         }
444                                         else {
445                                                 ANIM_deselect_anim_channels(ac->data, ac->datatype, 0, ACHANNEL_SETFLAG_CLEAR);
446                                                 //select_channel(act, achan, SELECT_ADD);
447                                         }
448                                         
449                                         /* messy... set active bone */
450                                         //select_poseelement_by_name(achan->name, 2);
451                                         
452                                         // XXX for now only
453                                         achan->flag ^= ACHAN_SELECTED;
454                                 }
455                         }
456                                 break;
457                 case ANIMTYPE_FILLIPO:
458                         {
459                                 bActionChannel *achan= (bActionChannel *)ale->data;
460                                 
461                                 achan->flag ^= ACHAN_SHOWIPO;
462                                 
463                                 if ((x > 24) && (achan->flag & ACHAN_SHOWIPO)) {
464                                         /* select+make active achan */          
465                                         ANIM_deselect_anim_channels(ac->data, ac->datatype, 0, ACHANNEL_SETFLAG_CLEAR);
466                                         //select_channel(act, achan, SELECT_ADD);
467                                         
468                                         /* messy... set active bone */
469                                         //select_poseelement_by_name(achan->name, 2);
470                                         
471                                         // XXX for now only
472                                         achan->flag ^= ACHAN_SELECTED;
473                                 }       
474                         }
475                         break;
476                 case ANIMTYPE_FILLCON:
477                         {
478                                 bActionChannel *achan= (bActionChannel *)ale->data;
479                                 
480                                 achan->flag ^= ACHAN_SHOWCONS;
481                                 
482                                 if ((x > 24) && (achan->flag & ACHAN_SHOWCONS)) {
483                                         /* select+make active achan */  
484                                         ANIM_deselect_anim_channels(ac->data, ac->datatype, 0, ACHANNEL_SETFLAG_CLEAR);
485                                         //select_channel(act, achan, SELECT_ADD);
486                                         
487                                         /* messy... set active bone */
488                                         //select_poseelement_by_name(achan->name, 2);
489                                         
490                                         // XXX for now only
491                                         achan->flag ^= ACHAN_SELECTED;
492                                 }       
493                         }
494                         break;
495                 case ANIMTYPE_ICU: 
496                         {
497                                 IpoCurve *icu= (IpoCurve *)ale->data;
498                                 
499                                 if (x >= (ACHANNEL_NAMEWIDTH-ACHANNEL_BUTTON_WIDTH)) {
500                                         /* toggle protection */
501                                         icu->flag ^= IPO_PROTECT;
502                                 }
503                                 else if (x >= (ACHANNEL_NAMEWIDTH-2*ACHANNEL_BUTTON_WIDTH)) {
504                                         /* toggle mute */
505                                         icu->flag ^= IPO_MUTE;
506                                 }
507                                 else {
508                                         /* select/deselect */
509                                         //select_icu_channel(act, icu, SELECT_INVERT);
510                                         
511                                         // XXX for now only
512                                         icu->flag ^= IPO_SELECT;
513                                 }
514                         }
515                         break;
516                 case ANIMTYPE_CONCHAN:
517                         {
518                                 bConstraintChannel *conchan= (bConstraintChannel *)ale->data;
519                                 
520                                 if (x >= (ACHANNEL_NAMEWIDTH-16)) {
521                                         /* toggle protection */
522                                         conchan->flag ^= CONSTRAINT_CHANNEL_PROTECTED;
523                                 }
524                                 else if ((x >= (ACHANNEL_NAMEWIDTH-32)) && (conchan->ipo)) {
525                                         /* toggle mute */
526                                         conchan->ipo->muteipo = (conchan->ipo->muteipo)? 0: 1;
527                                 }
528                                 else {
529                                         /* select/deselect */
530                                         //select_constraint_channel(act, conchan, SELECT_INVERT);
531                                         
532                                         // XXX for now only
533                                         conchan->flag ^= CONSTRAINT_CHANNEL_SELECT;
534                                 }
535                         }
536                                 break;
537                 case ANIMTYPE_GPDATABLOCK:
538                         {
539                                 bGPdata *gpd= (bGPdata *)ale->data;
540                                 
541                                 /* toggle expand */
542                                 gpd->flag ^= GP_DATA_EXPAND;
543                         }
544                                 break;
545                 case ANIMTYPE_GPLAYER:
546                         {
547 #if 0 // XXX future of this is unclear
548                                 bGPdata *gpd= (bGPdata *)ale->owner;
549                                 bGPDlayer *gpl= (bGPDlayer *)ale->data;
550                                 
551                                 if (x >= (ACHANNEL_NAMEWIDTH-16)) {
552                                         /* toggle lock */
553                                         gpl->flag ^= GP_LAYER_LOCKED;
554                                 }
555                                 else if (x >= (ACHANNEL_NAMEWIDTH-32)) {
556                                         /* toggle hide */
557                                         gpl->flag ^= GP_LAYER_HIDE;
558                                 }
559                                 else {
560                                         /* select/deselect */
561                                         //if (G.qual & LR_SHIFTKEY) {
562                                                 //select_gplayer_channel(gpd, gpl, SELECT_INVERT);
563                                         //}
564                                         //else {
565                                                 //deselect_gpencil_layers(data, 0);
566                                                 //select_gplayer_channel(gpd, gpl, SELECT_INVERT);
567                                         //}
568                                 }
569 #endif // XXX future of this is unclear
570                         }
571                                 break;
572                 case ANIMTYPE_SHAPEKEY:
573                         /* TODO: shapekey channels cannot be selected atm... */
574                         break;
575                 default:
576                         printf("Error: Invalid channel type in mouse_anim_channels() \n");
577         }
578         
579         /* free channels */
580         BLI_freelistN(&anim_data);
581 }
582
583 /* ------------------- */
584
585 /* handle clicking */
586 static int animchannels_mouseclick_invoke(bContext *C, wmOperator *op, wmEvent *event)
587 {
588         bAnimContext ac;
589         Scene *scene;
590         ARegion *ar;
591         View2D *v2d;
592         int mval[2], channel_index;
593         short selectmode;
594         float x, y;
595         
596         
597         /* get editor data */
598         if (ANIM_animdata_get_context(C, &ac) == 0)
599                 return OPERATOR_CANCELLED;
600                 
601         /* get useful pointers from animation context data */
602         scene= ac.scene;
603         ar= ac.ar;
604         v2d= &ar->v2d;
605         
606         /* get mouse coordinates (in region coordinates) */
607         mval[0]= (event->x - ar->winrct.xmin);
608         mval[1]= (event->y - ar->winrct.ymin);
609         
610         /* select mode is either replace (deselect all, then add) or add/extend */
611         if (RNA_boolean_get(op->ptr, "extend_select"))
612                 selectmode= SELECT_INVERT;
613         else
614                 selectmode= SELECT_REPLACE;
615         
616         /* figure out which channel user clicked in 
617          * Note: although channels technically start at y= ACHANNEL_FIRST, we need to adjust by half a channel's height
618          *              so that the tops of channels get caught ok. Since ACHANNEL_FIRST is really ACHANNEL_HEIGHT, we simply use
619          *              ACHANNEL_HEIGHT_HALF.
620          */
621         UI_view2d_region_to_view(v2d, mval[0], mval[1], &x, &y);
622         UI_view2d_listview_view_to_cell(v2d, ACHANNEL_NAMEWIDTH, ACHANNEL_STEP, 0, (float)ACHANNEL_HEIGHT_HALF, x, y, NULL, &channel_index);
623         
624         /* handle mouse-click in the relevant channel then */
625         mouse_anim_channels(&ac, x, channel_index, selectmode);
626         
627         /* set notifier tha things have changed */
628         ED_area_tag_redraw(CTX_wm_area(C)); // FIXME... should be updating 'keyframes' data context or so instead!
629         
630         return OPERATOR_FINISHED;
631 }
632  
633 void ANIM_OT_channels_mouseclick (wmOperatorType *ot)
634 {
635         /* identifiers */
636         ot->name= "Mouse Click on Channels";
637         ot->idname= "ANIM_OT_channels_mouseclick";
638         
639         /* api callbacks */
640         ot->invoke= animchannels_mouseclick_invoke;
641         ot->poll= ED_operator_areaactive;
642         
643         /* id-props */
644         RNA_def_property(ot->srna, "extend_select", PROP_BOOLEAN, PROP_NONE); // SHIFTKEY
645 }
646
647 /* ************************************************************************** */
648 /* Operator Registration */
649
650 void ED_operatortypes_animchannels(void)
651 {
652         WM_operatortype_append(ANIM_OT_channels_deselectall);
653         WM_operatortype_append(ANIM_OT_channels_mouseclick);
654 }
655
656 void ED_keymap_animchannels(wmWindowManager *wm)
657 {
658         ListBase *keymap = WM_keymap_listbase(wm, "Animation_Channels", 0, 0);
659         
660         /* click-select */
661                 // XXX for now, only leftmouse.... 
662         WM_keymap_add_item(keymap, "ANIM_OT_channels_mouseclick", LEFTMOUSE, KM_PRESS, 0, 0);
663         RNA_boolean_set(WM_keymap_add_item(keymap, "ANIM_OT_channels_mouseclick", LEFTMOUSE, KM_PRESS, KM_SHIFT, 0)->ptr, "extend_select", 1);
664         
665         /* deselect all */
666         WM_keymap_add_item(keymap, "ANIM_OT_channels_deselectall", AKEY, KM_PRESS, 0, 0);
667         RNA_boolean_set(WM_keymap_add_item(keymap, "ANIM_OT_channels_deselectall", IKEY, KM_PRESS, KM_CTRL, 0)->ptr, "invert", 1);
668 }
669
670 /* ************************************************************************** */