Blender V2.61 - r43446

graph_select.c

Go to the documentation of this file.
00001 /*
00002  * ***** BEGIN GPL LICENSE BLOCK *****
00003  *
00004  * This program is free software; you can redistribute it and/or
00005  * modify it under the terms of the GNU General Public License
00006  * as published by the Free Software Foundation; either version 2
00007  * of the License, or (at your option) any later version.
00008  *
00009  * This program is distributed in the hope that it will be useful,
00010  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  * GNU General Public License for more details.
00013  *
00014  * You should have received a copy of the GNU General Public License
00015  * along with this program; if not, write to the Free Software Foundation,
00016  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00017  *
00018  * The Original Code is Copyright (C) 2008 Blender Foundation
00019  *
00020  * Contributor(s): Joshua Leung
00021  *
00022  * ***** END GPL LICENSE BLOCK *****
00023  */
00024 
00030 #include <math.h>
00031 #include <stdlib.h>
00032 #include <string.h>
00033 #include <float.h>
00034 
00035 #include "MEM_guardedalloc.h"
00036 
00037 #include "BLI_blenlib.h"
00038 #include "BLI_math.h"
00039 #include "BLI_utildefines.h"
00040 
00041 #include "DNA_anim_types.h"
00042 #include "DNA_object_types.h"
00043 #include "DNA_screen_types.h"
00044 #include "DNA_scene_types.h"
00045 #include "DNA_space_types.h"
00046 
00047 #include "RNA_access.h"
00048 #include "RNA_define.h"
00049 
00050 #include "BKE_fcurve.h"
00051 #include "BKE_nla.h"
00052 #include "BKE_context.h"
00053 
00054 #include "UI_view2d.h"
00055 
00056 #include "ED_anim_api.h"
00057 #include "ED_keyframes_edit.h"
00058 #include "ED_markers.h"
00059 
00060 #include "WM_api.h"
00061 #include "WM_types.h"
00062 
00063 #include "graph_intern.h"
00064 
00065 
00066 /* ************************************************************************** */
00067 /* KEYFRAMES STUFF */
00068 
00069 /* ******************** Deselect All Operator ***************************** */
00070 /* This operator works in one of three ways:
00071  *  1) (de)select all (AKEY) - test if select all or deselect all
00072  *  2) invert all (CTRL-IKEY) - invert selection of all keyframes
00073  *  3) (de)select all - no testing is done; only for use internal tools as normal function...
00074  */
00075 
00076 /* Deselects keyframes in the Graph Editor
00077  *  - This is called by the deselect all operator, as well as other ones!
00078  *
00079  *  - test: check if select or deselect all
00080  *  - sel: how to select keyframes 
00081  *      0 = deselect
00082  *      1 = select
00083  *      2 = invert
00084  *  - do_channels: whether to affect selection status of channels
00085  */
00086 static void deselect_graph_keys (bAnimContext *ac, short test, short sel, short do_channels)
00087 {
00088     ListBase anim_data = {NULL, NULL};
00089     bAnimListElem *ale;
00090     int filter;
00091     
00092     SpaceIpo *sipo= (SpaceIpo *)ac->sl;
00093     KeyframeEditData ked= {{NULL}};
00094     KeyframeEditFunc test_cb, sel_cb;
00095     
00096     /* determine type-based settings */
00097     filter= (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_NODUPLIS);
00098     
00099     /* filter data */
00100     ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
00101     
00102     /* init BezTriple looping data */
00103     test_cb= ANIM_editkeyframes_ok(BEZT_OK_SELECTED);
00104     
00105     /* See if we should be selecting or deselecting */
00106     if (test) {
00107         for (ale= anim_data.first; ale; ale= ale->next) {
00108             if (ANIM_fcurve_keyframes_loop(&ked, ale->key_data, NULL, test_cb, NULL)) {
00109                 sel= SELECT_SUBTRACT;
00110                 break;
00111             }
00112         }
00113     }
00114     
00115     /* convert sel to selectmode, and use that to get editor */
00116     sel_cb= ANIM_editkeyframes_select(sel);
00117     
00118     /* Now set the flags */
00119     for (ale= anim_data.first; ale; ale= ale->next) {
00120         FCurve *fcu= (FCurve *)ale->key_data;
00121         
00122         /* Keyframes First */
00123         ANIM_fcurve_keyframes_loop(&ked, ale->key_data, NULL, sel_cb, NULL);
00124         
00125         /* affect channel selection status? */
00126         if (do_channels) {
00127             /* only change selection of channel when the visibility of keyframes doesn't depend on this */
00128             if ((sipo->flag & SIPO_SELCUVERTSONLY) == 0) {
00129                 /* deactivate the F-Curve, and deselect if deselecting keyframes.
00130                  * otherwise select the F-Curve too since we've selected all the keyframes
00131                  */
00132                 if (sel == SELECT_SUBTRACT) 
00133                     fcu->flag &= ~FCURVE_SELECTED;
00134                 else
00135                     fcu->flag |= FCURVE_SELECTED;
00136             }
00137             
00138             /* always deactivate all F-Curves if we perform batch ops for selection */
00139             fcu->flag &= ~FCURVE_ACTIVE;
00140         }
00141     }
00142     
00143     /* Cleanup */
00144     BLI_freelistN(&anim_data);
00145 }
00146 
00147 /* ------------------- */
00148 
00149 static int graphkeys_deselectall_exec(bContext *C, wmOperator *op)
00150 {
00151     bAnimContext ac;
00152     
00153     /* get editor data */
00154     if (ANIM_animdata_get_context(C, &ac) == 0)
00155         return OPERATOR_CANCELLED;
00156         
00157     /* 'standard' behaviour - check if selected, then apply relevant selection */
00158     if (RNA_boolean_get(op->ptr, "invert"))
00159         deselect_graph_keys(&ac, 0, SELECT_INVERT, TRUE);
00160     else
00161         deselect_graph_keys(&ac, 1, SELECT_ADD, TRUE);
00162     
00163     /* set notifier that things have changed */
00164     WM_event_add_notifier(C, NC_ANIMATION|ND_KEYFRAME|NA_SELECTED, NULL);
00165     
00166     return OPERATOR_FINISHED;
00167 }
00168  
00169 void GRAPH_OT_select_all_toggle (wmOperatorType *ot)
00170 {
00171     /* identifiers */
00172     ot->name= "Select All";
00173     ot->idname= "GRAPH_OT_select_all_toggle";
00174     ot->description= "Toggle selection of all keyframes";
00175     
00176     /* api callbacks */
00177     ot->exec= graphkeys_deselectall_exec;
00178     ot->poll= graphop_visible_keyframes_poll;
00179     
00180     /* flags */
00181     ot->flag= OPTYPE_REGISTER/*|OPTYPE_UNDO*/;
00182     
00183     /* props */
00184     ot->prop= RNA_def_boolean(ot->srna, "invert", 0, "Invert", "");
00185 }
00186 
00187 /* ******************** Border Select Operator **************************** */
00188 /* This operator currently works in one of three ways:
00189  *  -> BKEY     - 1) all keyframes within region are selected (validation with BEZT_OK_REGION)
00190  *  -> ALT-BKEY - depending on which axis of the region was larger...
00191  *      -> 2) x-axis, so select all frames within frame range (validation with BEZT_OK_FRAMERANGE)
00192  *      -> 3) y-axis, so select all frames within channels that region included (validation with BEZT_OK_VALUERANGE)
00193  */
00194 
00195 /* Borderselect only selects keyframes now, as overshooting handles often get caught too,
00196  * which means that they may be inadvertantly moved as well. However, incl_handles overrides
00197  * this, and allow handles to be considered independently too.
00198  * Also, for convenience, handles should get same status as keyframe (if it was within bounds).
00199  */
00200 static void borderselect_graphkeys (bAnimContext *ac, rcti rect, short mode, short selectmode, short incl_handles)
00201 {
00202     ListBase anim_data = {NULL, NULL};
00203     bAnimListElem *ale;
00204     int filter, mapping_flag;
00205     
00206     SpaceIpo *sipo= (SpaceIpo *)ac->sl;
00207     KeyframeEditData ked;
00208     KeyframeEditFunc ok_cb, select_cb;
00209     View2D *v2d= &ac->ar->v2d;
00210     rctf rectf;
00211     
00212     /* convert mouse coordinates to frame ranges and channel coordinates corrected for view pan/zoom */
00213     UI_view2d_region_to_view(v2d, rect.xmin, rect.ymin, &rectf.xmin, &rectf.ymin);
00214     UI_view2d_region_to_view(v2d, rect.xmax, rect.ymax, &rectf.xmax, &rectf.ymax);
00215     
00216     /* filter data */
00217     filter= (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_NODUPLIS);
00218     ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
00219     
00220     /* get beztriple editing/validation funcs  */
00221     select_cb= ANIM_editkeyframes_select(selectmode);
00222     ok_cb= ANIM_editkeyframes_ok(mode);
00223     
00224     /* init editing data */
00225     memset(&ked, 0, sizeof(KeyframeEditData));
00226     ked.data= &rectf;
00227     
00228     /* treat handles separately? */
00229     if (incl_handles) {
00230         ked.iterflags |= KEYFRAME_ITER_INCL_HANDLES;
00231         mapping_flag= 0;
00232     }
00233     else
00234         mapping_flag= ANIM_UNITCONV_ONLYKEYS;
00235     
00236     /* loop over data, doing border select */
00237     for (ale= anim_data.first; ale; ale= ale->next) {
00238         AnimData *adt= ANIM_nla_mapping_get(ac, ale);
00239         FCurve *fcu= (FCurve *)ale->key_data;
00240         
00241         /* apply unit corrections */
00242         ANIM_unit_mapping_apply_fcurve(ac->scene, ale->id, ale->key_data, mapping_flag);
00243         
00244         /* apply NLA mapping to all the keyframes, since it's easier than trying to
00245          * guess when a callback might use something different
00246          */
00247         if (adt)
00248             ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 0, incl_handles==0);
00249         
00250         /* set horizontal range (if applicable) 
00251          * NOTE: these values are only used for x-range and y-range but not region 
00252          *      (which uses ked.data, i.e. rectf)
00253          */
00254         if (mode != BEZT_OK_VALUERANGE) {
00255             ked.f1= rectf.xmin;
00256             ked.f2= rectf.xmax;
00257         }
00258         else {
00259             ked.f1= rectf.ymin;
00260             ked.f2= rectf.ymax;
00261         }
00262         
00263         /* firstly, check if any keyframes will be hit by this */
00264         if (ANIM_fcurve_keyframes_loop(&ked, fcu, NULL, ok_cb, NULL)) {
00265             /* select keyframes that are in the appropriate places */
00266             ANIM_fcurve_keyframes_loop(&ked, fcu, ok_cb, select_cb, NULL);
00267             
00268             /* only change selection of channel when the visibility of keyframes doesn't depend on this */
00269             if ((sipo->flag & SIPO_SELCUVERTSONLY) == 0) {
00270                 /* select the curve too now that curve will be touched */
00271                 if (selectmode == SELECT_ADD)
00272                     fcu->flag |= FCURVE_SELECTED;
00273             }
00274         }
00275         
00276         /* un-apply NLA mapping from all the keyframes */
00277         if (adt)
00278             ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 1, incl_handles==0);
00279             
00280         /* unapply unit corrections */
00281         ANIM_unit_mapping_apply_fcurve(ac->scene, ale->id, ale->key_data, ANIM_UNITCONV_RESTORE|mapping_flag);
00282     }
00283     
00284     /* cleanup */
00285     BLI_freelistN(&anim_data);
00286 }
00287 
00288 /* ------------------- */
00289 
00290 static int graphkeys_borderselect_exec(bContext *C, wmOperator *op)
00291 {
00292     bAnimContext ac;
00293     rcti rect;
00294     short mode=0, selectmode=0;
00295     short incl_handles;
00296     int extend;
00297     
00298     /* get editor data */
00299     if (ANIM_animdata_get_context(C, &ac) == 0)
00300         return OPERATOR_CANCELLED;
00301 
00302     /* clear all selection if not extending selection */
00303     extend= RNA_boolean_get(op->ptr, "extend");
00304     if (!extend)
00305         deselect_graph_keys(&ac, 1, SELECT_SUBTRACT, TRUE);
00306 
00307     /* get select mode 
00308      *  - 'gesture_mode' from the operator specifies how to select
00309      *  - 'include_handles' from the operator specifies whether to include handles in the selection
00310      */
00311     if (RNA_int_get(op->ptr, "gesture_mode")==GESTURE_MODAL_SELECT)
00312         selectmode= SELECT_ADD;
00313     else
00314         selectmode= SELECT_SUBTRACT;
00315         
00316     incl_handles = RNA_boolean_get(op->ptr, "include_handles");
00317     
00318     /* get settings from operator */
00319     rect.xmin= RNA_int_get(op->ptr, "xmin");
00320     rect.ymin= RNA_int_get(op->ptr, "ymin");
00321     rect.xmax= RNA_int_get(op->ptr, "xmax");
00322     rect.ymax= RNA_int_get(op->ptr, "ymax");
00323     
00324     /* selection 'mode' depends on whether borderselect region only matters on one axis */
00325     if (RNA_boolean_get(op->ptr, "axis_range")) {
00326         /* mode depends on which axis of the range is larger to determine which axis to use 
00327          *  - checking this in region-space is fine, as it's fundamentally still going to be a different rect size
00328          *  - the frame-range select option is favoured over the channel one (x over y), as frame-range one is often
00329          *    used for tweaking timing when "blocking", while channels is not that useful...
00330          */
00331         if ((rect.xmax - rect.xmin) >= (rect.ymax - rect.ymin))
00332             mode= BEZT_OK_FRAMERANGE;
00333         else
00334             mode= BEZT_OK_VALUERANGE;
00335     }
00336     else 
00337         mode= BEZT_OK_REGION;
00338     
00339     /* apply borderselect action */
00340     borderselect_graphkeys(&ac, rect, mode, selectmode, incl_handles);
00341     
00342     /* send notifier that keyframe selection has changed */
00343     WM_event_add_notifier(C, NC_ANIMATION|ND_KEYFRAME|NA_SELECTED, NULL);
00344     
00345     return OPERATOR_FINISHED;
00346 } 
00347 
00348 void GRAPH_OT_select_border(wmOperatorType *ot)
00349 {
00350     /* identifiers */
00351     ot->name= "Border Select";
00352     ot->idname= "GRAPH_OT_select_border";
00353     ot->description= "Select all keyframes within the specified region";
00354     
00355     /* api callbacks */
00356     ot->invoke= WM_border_select_invoke;
00357     ot->exec= graphkeys_borderselect_exec;
00358     ot->modal= WM_border_select_modal;
00359     ot->cancel= WM_border_select_cancel;
00360     
00361     ot->poll= graphop_visible_keyframes_poll;
00362     
00363     /* flags */
00364     ot->flag= OPTYPE_REGISTER/*|OPTYPE_UNDO*/;
00365     
00366     /* rna */
00367     WM_operator_properties_gesture_border(ot, TRUE);
00368     
00369     ot->prop= RNA_def_boolean(ot->srna, "axis_range", 0, "Axis Range", "");
00370     RNA_def_boolean(ot->srna, "include_handles", 0, "Include Handles", "Are handles tested individually against the selection criteria");
00371 }
00372 
00373 /* ******************** Column Select Operator **************************** */
00374 /* This operator works in one of four ways:
00375  *  - 1) select all keyframes in the same frame as a selected one  (KKEY)
00376  *  - 2) select all keyframes in the same frame as the current frame marker (CTRL-KKEY)
00377  *  - 3) select all keyframes in the same frame as a selected markers (SHIFT-KKEY)
00378  *  - 4) select all keyframes that occur between selected markers (ALT-KKEY)
00379  */
00380 
00381 /* defines for column-select mode */
00382 static EnumPropertyItem prop_column_select_types[] = {
00383     {GRAPHKEYS_COLUMNSEL_KEYS, "KEYS", 0, "On Selected Keyframes", ""},
00384     {GRAPHKEYS_COLUMNSEL_CFRA, "CFRA", 0, "On Current Frame", ""},
00385     {GRAPHKEYS_COLUMNSEL_MARKERS_COLUMN, "MARKERS_COLUMN", 0, "On Selected Markers", ""},
00386     {GRAPHKEYS_COLUMNSEL_MARKERS_BETWEEN, "MARKERS_BETWEEN", 0, "Between Min/Max Selected Markers", ""},
00387     {0, NULL, 0, NULL, NULL}
00388 };
00389 
00390 /* ------------------- */ 
00391 
00392 /* Selects all visible keyframes between the specified markers */
00393 /* TODO, this is almost an _exact_ duplicate of a function of the same name in action_select.c
00394  * should de-duplicate - campbell */
00395 static void markers_selectkeys_between (bAnimContext *ac)
00396 {
00397     ListBase anim_data = {NULL, NULL};
00398     bAnimListElem *ale;
00399     int filter;
00400     
00401     KeyframeEditFunc ok_cb, select_cb;
00402     KeyframeEditData ked= {{NULL}};
00403     float min, max;
00404     
00405     /* get extreme markers */
00406     ED_markers_get_minmax(ac->markers, 1, &min, &max);
00407     min -= 0.5f;
00408     max += 0.5f;
00409     
00410     /* get editing funcs + data */
00411     ok_cb= ANIM_editkeyframes_ok(BEZT_OK_FRAMERANGE);
00412     select_cb= ANIM_editkeyframes_select(SELECT_ADD);
00413 
00414     ked.f1= min;
00415     ked.f2= max;
00416     
00417     /* filter data */
00418     filter= (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_NODUPLIS);
00419     ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
00420     
00421     /* select keys in-between */
00422     for (ale= anim_data.first; ale; ale= ale->next) {
00423         AnimData *adt= ANIM_nla_mapping_get(ac, ale);
00424 
00425         if (adt) {
00426             ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 0, 1);
00427             ANIM_fcurve_keyframes_loop(&ked, ale->key_data, ok_cb, select_cb, NULL);
00428             ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 1, 1);
00429         }
00430         else {
00431             ANIM_fcurve_keyframes_loop(&ked, ale->key_data, ok_cb, select_cb, NULL);
00432         }
00433     }
00434     
00435     /* Cleanup */
00436     BLI_freelistN(&anim_data);
00437 }
00438 
00439 
00440 /* Selects all visible keyframes in the same frames as the specified elements */
00441 static void columnselect_graph_keys (bAnimContext *ac, short mode)
00442 {
00443     ListBase anim_data= {NULL, NULL};
00444     bAnimListElem *ale;
00445     int filter;
00446     
00447     Scene *scene= ac->scene;
00448     CfraElem *ce;
00449     KeyframeEditFunc select_cb, ok_cb;
00450     KeyframeEditData ked;
00451     
00452     /* initialise keyframe editing data */
00453     memset(&ked, 0, sizeof(KeyframeEditData));
00454     
00455     /* build list of columns */
00456     switch (mode) {
00457         case GRAPHKEYS_COLUMNSEL_KEYS: /* list of selected keys */
00458             filter= (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_NODUPLIS);
00459             ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
00460             
00461             for (ale= anim_data.first; ale; ale= ale->next)
00462                 ANIM_fcurve_keyframes_loop(&ked, ale->key_data, NULL, bezt_to_cfraelem, NULL);
00463             
00464             BLI_freelistN(&anim_data);
00465             break;
00466             
00467         case GRAPHKEYS_COLUMNSEL_CFRA: /* current frame */
00468             /* make a single CfraElem for storing this */
00469             ce= MEM_callocN(sizeof(CfraElem), "cfraElem");
00470             BLI_addtail(&ked.list, ce);
00471             
00472             ce->cfra= (float)CFRA;
00473             break;
00474             
00475         case GRAPHKEYS_COLUMNSEL_MARKERS_COLUMN: /* list of selected markers */
00476             ED_markers_make_cfra_list(ac->markers, &ked.list, SELECT);
00477             break;
00478             
00479         default: /* invalid option */
00480             return;
00481     }
00482     
00483     /* set up BezTriple edit callbacks */
00484     select_cb= ANIM_editkeyframes_select(SELECT_ADD);
00485     ok_cb= ANIM_editkeyframes_ok(BEZT_OK_FRAME);
00486     
00487     /* loop through all of the keys and select additional keyframes
00488      * based on the keys found to be selected above
00489      */
00490     filter= (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_NODUPLIS);
00491     ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
00492     
00493     for (ale= anim_data.first; ale; ale= ale->next) {
00494         AnimData *adt= ANIM_nla_mapping_get(ac, ale);
00495         
00496         /* loop over cfraelems (stored in the KeyframeEditData->list)
00497          *  - we need to do this here, as we can apply fewer NLA-mapping conversions
00498          */
00499         for (ce= ked.list.first; ce; ce= ce->next) {
00500             /* set frame for validation callback to refer to */
00501             ked.f1= BKE_nla_tweakedit_remap(adt, ce->cfra, NLATIME_CONVERT_UNMAP);
00502 
00503             /* select elements with frame number matching cfraelem */
00504             ANIM_fcurve_keyframes_loop(&ked, ale->key_data, ok_cb, select_cb, NULL);
00505         }
00506     }
00507     
00508     /* free elements */
00509     BLI_freelistN(&ked.list);
00510     BLI_freelistN(&anim_data);
00511 }
00512 
00513 /* ------------------- */
00514 
00515 static int graphkeys_columnselect_exec(bContext *C, wmOperator *op)
00516 {
00517     bAnimContext ac;
00518     short mode;
00519     
00520     /* get editor data */
00521     if (ANIM_animdata_get_context(C, &ac) == 0)
00522         return OPERATOR_CANCELLED;
00523         
00524     /* action to take depends on the mode */
00525     mode= RNA_enum_get(op->ptr, "mode");
00526     
00527     if (mode == GRAPHKEYS_COLUMNSEL_MARKERS_BETWEEN)
00528         markers_selectkeys_between(&ac);
00529     else
00530         columnselect_graph_keys(&ac, mode);
00531     
00532     /* set notifier that keyframe selection has changed */
00533     WM_event_add_notifier(C, NC_ANIMATION|ND_KEYFRAME|NA_SELECTED, NULL);
00534     
00535     return OPERATOR_FINISHED;
00536 }
00537  
00538 void GRAPH_OT_select_column (wmOperatorType *ot)
00539 {
00540     /* identifiers */
00541     ot->name= "Select All";
00542     ot->idname= "GRAPH_OT_select_column";
00543     ot->description= "Select all keyframes on the specified frame(s)";
00544     
00545     /* api callbacks */
00546     ot->exec= graphkeys_columnselect_exec;
00547     ot->poll= graphop_visible_keyframes_poll;
00548     
00549     /* flags */
00550     ot->flag= OPTYPE_REGISTER/*|OPTYPE_UNDO*/;
00551     
00552     /* props */
00553     ot->prop= RNA_def_enum(ot->srna, "mode", prop_column_select_types, 0, "Mode", "");
00554 }
00555 
00556 /* ******************** Select Linked Operator *********************** */
00557 
00558 static int graphkeys_select_linked_exec (bContext *C, wmOperator *UNUSED(op))
00559 {
00560     bAnimContext ac;
00561     
00562     ListBase anim_data= {NULL, NULL};
00563     bAnimListElem *ale;
00564     int filter;
00565     
00566     KeyframeEditFunc ok_cb = ANIM_editkeyframes_ok(BEZT_OK_SELECTED);
00567     KeyframeEditFunc sel_cb = ANIM_editkeyframes_select(SELECT_ADD);
00568     
00569     /* get editor data */
00570     if (ANIM_animdata_get_context(C, &ac) == 0)
00571         return OPERATOR_CANCELLED;
00572     
00573     /* loop through all of the keys and select additional keyframes based on these */
00574     filter= (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_NODUPLIS);
00575     ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
00576     
00577     for (ale= anim_data.first; ale; ale= ale->next) {
00578         FCurve *fcu= (FCurve *)ale->key_data;
00579         
00580         /* check if anything selected? */
00581         if (ANIM_fcurve_keyframes_loop(NULL, fcu, NULL, ok_cb, NULL)) {
00582             /* select every keyframe in this curve then */
00583             ANIM_fcurve_keyframes_loop(NULL, fcu, NULL, sel_cb, NULL);
00584         }
00585     }
00586     
00587     /* Cleanup */
00588     BLI_freelistN(&anim_data);
00589     
00590     /* set notifier that keyframe selection has changed */
00591     WM_event_add_notifier(C, NC_ANIMATION|ND_KEYFRAME|NA_SELECTED, NULL);
00592     
00593     return OPERATOR_FINISHED;
00594 }
00595 
00596 void GRAPH_OT_select_linked (wmOperatorType *ot)
00597 {
00598     /* identifiers */
00599     ot->name = "Select Linked";
00600     ot->idname= "GRAPH_OT_select_linked";
00601     ot->description = "Select keyframes occurring in the same F-Curves as selected ones";
00602     
00603     /* api callbacks */
00604     ot->exec= graphkeys_select_linked_exec;
00605     ot->poll= graphop_visible_keyframes_poll;
00606     
00607     /* flags */
00608     ot->flag= OPTYPE_REGISTER/*|OPTYPE_UNDO*/;
00609 }
00610 
00611 /* ******************** Select More/Less Operators *********************** */
00612 
00613 /* Common code to perform selection */
00614 static void select_moreless_graph_keys (bAnimContext *ac, short mode)
00615 {
00616     ListBase anim_data= {NULL, NULL};
00617     bAnimListElem *ale;
00618     int filter;
00619     
00620     KeyframeEditData ked;
00621     KeyframeEditFunc build_cb;
00622     
00623     
00624     /* init selmap building data */
00625     build_cb= ANIM_editkeyframes_buildselmap(mode);
00626     memset(&ked, 0, sizeof(KeyframeEditData)); 
00627     
00628     /* loop through all of the keys and select additional keyframes based on these */
00629     filter= (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_NODUPLIS);
00630     ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
00631     
00632     for (ale= anim_data.first; ale; ale= ale->next) {
00633         FCurve *fcu= (FCurve *)ale->key_data;
00634         
00635         /* only continue if F-Curve has keyframes */
00636         if (fcu->bezt == NULL)
00637             continue;
00638         
00639         /* build up map of whether F-Curve's keyframes should be selected or not */
00640         ked.data= MEM_callocN(fcu->totvert, "selmap graphEdit");
00641         ANIM_fcurve_keyframes_loop(&ked, fcu, NULL, build_cb, NULL);
00642         
00643         /* based on this map, adjust the selection status of the keyframes */
00644         ANIM_fcurve_keyframes_loop(&ked, fcu, NULL, bezt_selmap_flush, NULL);
00645         
00646         /* free the selmap used here */
00647         MEM_freeN(ked.data);
00648         ked.data= NULL;
00649     }
00650     
00651     /* Cleanup */
00652     BLI_freelistN(&anim_data);
00653 }
00654 
00655 /* ----------------- */
00656 
00657 static int graphkeys_select_more_exec (bContext *C, wmOperator *UNUSED(op))
00658 {
00659     bAnimContext ac;
00660     
00661     /* get editor data */
00662     if (ANIM_animdata_get_context(C, &ac) == 0)
00663         return OPERATOR_CANCELLED;
00664     
00665     /* perform select changes */
00666     select_moreless_graph_keys(&ac, SELMAP_MORE);
00667     
00668     /* set notifier that keyframe selection has changed */
00669     WM_event_add_notifier(C, NC_ANIMATION|ND_KEYFRAME|NA_SELECTED, NULL);
00670     
00671     return OPERATOR_FINISHED;
00672 }
00673 
00674 void GRAPH_OT_select_more (wmOperatorType *ot)
00675 {
00676     /* identifiers */
00677     ot->name = "Select More";
00678     ot->idname= "GRAPH_OT_select_more";
00679     ot->description = "Select keyframes beside already selected ones";
00680     
00681     /* api callbacks */
00682     ot->exec= graphkeys_select_more_exec;
00683     ot->poll= graphop_visible_keyframes_poll;
00684     
00685     /* flags */
00686     ot->flag= OPTYPE_REGISTER/*|OPTYPE_UNDO*/;
00687 }
00688 
00689 /* ----------------- */
00690 
00691 static int graphkeys_select_less_exec (bContext *C, wmOperator *UNUSED(op))
00692 {
00693     bAnimContext ac;
00694     
00695     /* get editor data */
00696     if (ANIM_animdata_get_context(C, &ac) == 0)
00697         return OPERATOR_CANCELLED;
00698     
00699     /* perform select changes */
00700     select_moreless_graph_keys(&ac, SELMAP_LESS);
00701     
00702     /* set notifier that keyframe selection has changed */
00703     WM_event_add_notifier(C, NC_ANIMATION|ND_KEYFRAME|NA_SELECTED, NULL);
00704     
00705     return OPERATOR_FINISHED;
00706 }
00707 
00708 void GRAPH_OT_select_less (wmOperatorType *ot)
00709 {
00710     /* identifiers */
00711     ot->name = "Select Less";
00712     ot->idname= "GRAPH_OT_select_less";
00713     ot->description = "Deselect keyframes on ends of selection islands";
00714     
00715     /* api callbacks */
00716     ot->exec= graphkeys_select_less_exec;
00717     ot->poll= graphop_visible_keyframes_poll;
00718     
00719     /* flags */
00720     ot->flag= OPTYPE_REGISTER/*|OPTYPE_UNDO*/;
00721 }
00722 
00723 /* ******************** Select Left/Right Operator ************************* */
00724 /* Select keyframes left/right of the current frame indicator */
00725 
00726 /* defines for left-right select tool */
00727 static EnumPropertyItem prop_graphkeys_leftright_select_types[] = {
00728     {GRAPHKEYS_LRSEL_TEST, "CHECK", 0, "Check if Select Left or Right", ""},
00729     {GRAPHKEYS_LRSEL_LEFT, "LEFT", 0, "Before current frame", ""},
00730     {GRAPHKEYS_LRSEL_RIGHT, "RIGHT", 0, "After current frame", ""},
00731     {0, NULL, 0, NULL, NULL}
00732 };
00733 
00734 /* --------------------------------- */
00735 
00736 static void graphkeys_select_leftright (bAnimContext *ac, short leftright, short select_mode)
00737 {
00738     ListBase anim_data = {NULL, NULL};
00739     bAnimListElem *ale;
00740     int filter;
00741     
00742     KeyframeEditFunc ok_cb, select_cb;
00743     KeyframeEditData ked= {{NULL}};
00744     Scene *scene= ac->scene;
00745     
00746     /* if select mode is replace, deselect all keyframes (and channels) first */
00747     if (select_mode==SELECT_REPLACE) {
00748         select_mode= SELECT_ADD;
00749         
00750         /* - deselect all other keyframes, so that just the newly selected remain
00751          * - channels aren't deselected, since we don't re-select any as a consequence
00752          */
00753         deselect_graph_keys(ac, 0, SELECT_SUBTRACT, FALSE);
00754     }
00755     
00756     /* set callbacks and editing data */
00757     ok_cb= ANIM_editkeyframes_ok(BEZT_OK_FRAMERANGE);
00758     select_cb= ANIM_editkeyframes_select(select_mode);
00759     
00760     if (leftright == GRAPHKEYS_LRSEL_LEFT) {
00761         ked.f1 = MINAFRAMEF;
00762         ked.f2 = (float)(CFRA + 0.1f);
00763     } 
00764     else {
00765         ked.f1 = (float)(CFRA - 0.1f);
00766         ked.f2 = MAXFRAMEF;
00767     }
00768     
00769     /* filter data */
00770     filter= (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_NODUPLIS);
00771     ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
00772         
00773     /* select keys */
00774     for (ale= anim_data.first; ale; ale= ale->next) {
00775         AnimData *adt= ANIM_nla_mapping_get(ac, ale);
00776         
00777         if (adt) {
00778             ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 0, 1);
00779             ANIM_fcurve_keyframes_loop(&ked, ale->key_data, ok_cb, select_cb, NULL);
00780             ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 1, 1);
00781         }
00782         else
00783             ANIM_fcurve_keyframes_loop(&ked, ale->key_data, ok_cb, select_cb, NULL);
00784     }
00785 
00786     /* Cleanup */
00787     BLI_freelistN(&anim_data);
00788 }
00789 
00790 /* ----------------- */
00791 
00792 static int graphkeys_select_leftright_exec (bContext *C, wmOperator *op)
00793 {
00794     bAnimContext ac;
00795     short leftright = RNA_enum_get(op->ptr, "mode");
00796     short selectmode;
00797     
00798     /* get editor data */
00799     if (ANIM_animdata_get_context(C, &ac) == 0)
00800         return OPERATOR_CANCELLED;
00801     
00802     /* select mode is either replace (deselect all, then add) or add/extend */
00803     if (RNA_boolean_get(op->ptr, "extend"))
00804         selectmode= SELECT_INVERT;
00805     else
00806         selectmode= SELECT_REPLACE;
00807         
00808     /* if "test" mode is set, we don't have any info to set this with */
00809     if (leftright == GRAPHKEYS_LRSEL_TEST)
00810         return OPERATOR_CANCELLED;
00811     
00812     /* do the selecting now */
00813     graphkeys_select_leftright(&ac, leftright, selectmode);
00814     
00815     /* set notifier that keyframe selection (and channels too) have changed */
00816     WM_event_add_notifier(C, NC_ANIMATION|ND_KEYFRAME|ND_ANIMCHAN|NA_SELECTED, NULL);
00817     
00818     return OPERATOR_FINISHED;
00819 }
00820 
00821 static int graphkeys_select_leftright_invoke (bContext *C, wmOperator *op, wmEvent *event)
00822 {
00823     bAnimContext ac;
00824     short leftright = RNA_enum_get(op->ptr, "mode");
00825     
00826     /* get editor data */
00827     if (ANIM_animdata_get_context(C, &ac) == 0)
00828         return OPERATOR_CANCELLED;
00829         
00830     /* handle mode-based testing */
00831     if (leftright == GRAPHKEYS_LRSEL_TEST) {
00832         Scene *scene= ac.scene;
00833         ARegion *ar= ac.ar;
00834         View2D *v2d= &ar->v2d;
00835         float x;
00836 
00837         /* determine which side of the current frame mouse is on */
00838         UI_view2d_region_to_view(v2d, event->mval[0], event->mval[1], &x, NULL);
00839         if (x < CFRA)
00840             RNA_enum_set(op->ptr, "mode", GRAPHKEYS_LRSEL_LEFT);
00841         else    
00842             RNA_enum_set(op->ptr, "mode", GRAPHKEYS_LRSEL_RIGHT);
00843     }
00844     
00845     /* perform selection */
00846     return graphkeys_select_leftright_exec(C, op);
00847 }
00848 
00849 void GRAPH_OT_select_leftright (wmOperatorType *ot)
00850 {
00851     /* identifiers */
00852     ot->name= "Select Left/Right";
00853     ot->idname= "GRAPH_OT_select_leftright";
00854     ot->description= "Select keyframes to the left or the right of the current frame";
00855     
00856     /* api callbacks  */
00857     ot->invoke= graphkeys_select_leftright_invoke;
00858     ot->exec= graphkeys_select_leftright_exec;
00859     ot->poll= graphop_visible_keyframes_poll;
00860     
00861     /* flags */
00862     ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
00863     
00864     /* id-props */
00865     ot->prop= RNA_def_enum(ot->srna, "mode", prop_graphkeys_leftright_select_types, GRAPHKEYS_LRSEL_TEST, "Mode", "");
00866     RNA_def_boolean(ot->srna, "extend", 0, "Extend Select", "");
00867 }
00868 
00869 /* ******************** Mouse-Click Select Operator *********************** */
00870 /* This operator works in one of three ways:
00871  *  - 1) keyframe under mouse - no special modifiers
00872  *  - 2) all keyframes on the same side of current frame indicator as mouse - ALT modifier
00873  *  - 3) column select all keyframes in frame under mouse - CTRL modifier
00874  *
00875  * In addition to these basic options, the SHIFT modifier can be used to toggle the 
00876  * selection mode between replacing the selection (without) and inverting the selection (with).
00877  */
00878 
00879 /* temp info for caching handle vertices close */
00880 typedef struct tNearestVertInfo {
00881     struct tNearestVertInfo *next, *prev;
00882     
00883     FCurve *fcu;        /* F-Curve that keyframe comes from */
00884     
00885     BezTriple *bezt;    /* keyframe to consider */
00886     FPoint *fpt;        /* sample point to consider */
00887     
00888     short hpoint;       /* the handle index that we hit (eHandleIndex) */
00889     short sel;          /* whether the handle is selected or not */
00890     int dist;           /* distance from mouse to vert */
00891 } tNearestVertInfo;
00892 
00893 /* Tags for the type of graph vert that we have */
00894 typedef enum eGraphVertIndex {
00895     NEAREST_HANDLE_LEFT = -1,
00896     NEAREST_HANDLE_KEY,
00897     NEAREST_HANDLE_RIGHT
00898 } eGraphVertIndex; 
00899 
00900 /* Tolerance for absolute radius (in pixels) of the vert from the cursor to use */
00901 // TODO: perhaps this should depend a bit on the size that the user set the vertices to be?
00902 #define GVERTSEL_TOL    10
00903 
00904 /* ....... */
00905 
00906 /* check if its ok to select a handle */
00907 // XXX also need to check for int-values only?
00908 static int fcurve_handle_sel_check(SpaceIpo *sipo, BezTriple *bezt)
00909 {
00910     if (sipo->flag & SIPO_NOHANDLES) return 0;
00911     if ((sipo->flag & SIPO_SELVHANDLESONLY) && BEZSELECTED(bezt)==0) return 0;
00912     return 1;
00913 }
00914 
00915 /* check if the given vertex is within bounds or not */
00916 // TODO: should we return if we hit something?
00917 static void nearest_fcurve_vert_store (ListBase *matches, View2D *v2d, FCurve *fcu, BezTriple *bezt, FPoint *fpt, short hpoint, const int mval[2])
00918 {
00919     /* Keyframes or Samples? */
00920     if (bezt) {
00921         int screen_co[2], dist;
00922         
00923         /* convert from data-space to screen coordinates 
00924          * NOTE: hpoint+1 gives us 0,1,2 respectively for each handle, 
00925          *  needed to access the relevant vertex coordinates in the 3x3 
00926          *  'vec' matrix
00927          */
00928         UI_view2d_view_to_region(v2d, bezt->vec[hpoint+1][0], bezt->vec[hpoint+1][1], &screen_co[0], &screen_co[1]);
00929         
00930         /* check if distance from mouse cursor to vert in screen space is within tolerance */
00931             // XXX: inlined distance calculation, since we cannot do this on ints using the math lib...
00932         //dist = len_v2v2(mval, screen_co);
00933         dist = sqrt((mval[0] - screen_co[0])*(mval[0] - screen_co[0]) + 
00934                     (mval[1] - screen_co[1])*(mval[1] - screen_co[1]));
00935         
00936         if (dist <= GVERTSEL_TOL) {
00937             tNearestVertInfo *nvi = (tNearestVertInfo *)matches->last;
00938             short replace = 0;
00939             
00940             /* if there is already a point for the F-Curve, check if this point is closer than that was */
00941             if ((nvi) && (nvi->fcu == fcu)) {
00942                 /* replace if we are closer, or if equal and that one wasn't selected but we are... */
00943                 if ( (nvi->dist > dist) || ((nvi->sel == 0) && BEZSELECTED(bezt)) )
00944                     replace= 1;
00945             }
00946             /* add new if not replacing... */
00947             if (replace == 0)
00948                 nvi = MEM_callocN(sizeof(tNearestVertInfo), "Nearest Graph Vert Info - Bezt");
00949             
00950             /* store values */
00951             nvi->fcu = fcu;
00952             nvi->bezt = bezt;
00953             nvi->hpoint = hpoint;
00954             nvi->dist = dist;
00955             
00956             nvi->sel= BEZSELECTED(bezt); // XXX... should this use the individual verts instead?
00957             
00958             /* add to list of matches if appropriate... */
00959             if (replace == 0)
00960                 BLI_addtail(matches, nvi);
00961         }
00962     }
00963     else if (fpt) {
00964         // TODO...
00965     }
00966 } 
00967 
00968 /* helper for find_nearest_fcurve_vert() - build the list of nearest matches */
00969 static void get_nearest_fcurve_verts_list (bAnimContext *ac, const int mval[2], ListBase *matches)
00970 {
00971     ListBase anim_data = {NULL, NULL};
00972     bAnimListElem *ale;
00973     int filter;
00974     
00975     SpaceIpo *sipo= (SpaceIpo *)ac->sl;
00976     View2D *v2d= &ac->ar->v2d;
00977     
00978     /* get curves to search through 
00979      *  - if the option to only show keyframes that belong to selected F-Curves is enabled,
00980      *    include the 'only selected' flag...
00981      */
00982     filter= (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_NODUPLIS);
00983     if (sipo->flag & SIPO_SELCUVERTSONLY)   // FIXME: this should really be check for by the filtering code...
00984         filter |= ANIMFILTER_SEL;
00985     ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
00986     
00987     for (ale= anim_data.first; ale; ale= ale->next) {
00988         FCurve *fcu= (FCurve *)ale->key_data;
00989         AnimData *adt= ANIM_nla_mapping_get(ac, ale);
00990         
00991         /* apply unit corrections */
00992         ANIM_unit_mapping_apply_fcurve(ac->scene, ale->id, ale->key_data, 0);
00993         
00994         /* apply NLA mapping to all the keyframes */
00995         if (adt)
00996             ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 0, 0);
00997         
00998         if (fcu->bezt) {
00999             BezTriple *bezt1=fcu->bezt, *prevbezt=NULL;
01000             int i;
01001             
01002             for (i=0; i < fcu->totvert; i++, prevbezt=bezt1, bezt1++) {
01003                 /* keyframe */
01004                 nearest_fcurve_vert_store(matches, v2d, fcu, bezt1, NULL, NEAREST_HANDLE_KEY, mval);
01005                 
01006                 /* handles - only do them if they're visible */
01007                 if (fcurve_handle_sel_check(sipo, bezt1) && (fcu->totvert > 1)) {
01008                     /* first handle only visible if previous segment had handles */
01009                     if ( (!prevbezt && (bezt1->ipo==BEZT_IPO_BEZ)) || (prevbezt && (prevbezt->ipo==BEZT_IPO_BEZ)) )
01010                     {
01011                         nearest_fcurve_vert_store(matches, v2d, fcu, bezt1, NULL, NEAREST_HANDLE_LEFT, mval);
01012                     }
01013                     
01014                     /* second handle only visible if this segment is bezier */
01015                     if (bezt1->ipo == BEZT_IPO_BEZ) 
01016                     {
01017                         nearest_fcurve_vert_store(matches, v2d, fcu, bezt1, NULL, NEAREST_HANDLE_RIGHT, mval);
01018                     }
01019                 }
01020             }
01021         }
01022         else if (fcu->fpt) {
01023             // TODO; do this for samples too
01024             
01025         }
01026         
01027         /* un-apply NLA mapping from all the keyframes */
01028         if (adt)
01029             ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 1, 0);
01030         
01031         /* unapply unit corrections */
01032         ANIM_unit_mapping_apply_fcurve(ac->scene, ale->id, ale->key_data, ANIM_UNITCONV_RESTORE);
01033     }
01034     
01035     /* free channels */
01036     BLI_freelistN(&anim_data);
01037 }
01038 
01039 /* helper for find_nearest_fcurve_vert() - get the best match to use */
01040 static tNearestVertInfo *get_best_nearest_fcurve_vert (ListBase *matches)
01041 {
01042     tNearestVertInfo *nvi = NULL;
01043     short found = 0;
01044     
01045     /* abort if list is empty */
01046     if (matches->first == NULL) 
01047         return NULL;
01048         
01049     /* if list only has 1 item, remove it from the list and return */
01050     if (matches->first == matches->last) {
01051         /* need to remove from the list, otherwise it gets freed and then we can't return it */
01052         nvi= matches->first;
01053         BLI_remlink(matches, nvi);
01054         
01055         return nvi;
01056     }
01057     
01058     /* try to find the first selected F-Curve vert, then take the one after it */
01059     for (nvi = matches->first; nvi; nvi = nvi->next) {
01060         /* which mode of search are we in: find first selected, or find vert? */
01061         if (found) {
01062             /* just take this vert now that we've found the selected one 
01063              *  - we'll need to remove this from the list so that it can be returned to the original caller
01064              */
01065             BLI_remlink(matches, nvi);
01066             return nvi;
01067         }
01068         else {
01069             /* if vert is selected, we've got what we want... */
01070             if (nvi->sel)
01071                 found= 1;
01072         }
01073     }
01074     
01075     /* if we're still here, this means that we failed to find anything appropriate in the first pass,
01076      * so just take the first item now...
01077      */
01078     nvi = matches->first;
01079     BLI_remlink(matches, nvi);
01080     return nvi;
01081 }
01082 
01083 /* Find the nearest vertices (either a handle or the keyframe) that are nearest to the mouse cursor (in area coordinates) 
01084  * NOTE: the match info found must still be freed 
01085  */
01086 static tNearestVertInfo *find_nearest_fcurve_vert (bAnimContext *ac, const int mval[2])
01087 {
01088     ListBase matches = {NULL, NULL};
01089     tNearestVertInfo *nvi;
01090     
01091     /* step 1: get the nearest verts */
01092     get_nearest_fcurve_verts_list(ac, mval, &matches);
01093     
01094     /* step 2: find the best vert */
01095     nvi= get_best_nearest_fcurve_vert(&matches);
01096     
01097     BLI_freelistN(&matches);
01098     
01099     /* return the best vert found */
01100     return nvi;
01101 }
01102 
01103 /* ------------------- */
01104 
01105 /* option 1) select keyframe directly under mouse */
01106 static void mouse_graph_keys (bAnimContext *ac, const int mval[2], short select_mode, short curves_only)
01107 {
01108     SpaceIpo *sipo= (SpaceIpo *)ac->sl;
01109     tNearestVertInfo *nvi;
01110     BezTriple *bezt= NULL;
01111     
01112     /* find the beztriple that we're selecting, and the handle that was clicked on */
01113     nvi = find_nearest_fcurve_vert(ac, mval);
01114     
01115     /* check if anything to select */
01116     if (nvi == NULL)    
01117         return;
01118     
01119     /* deselect all other curves? */
01120     if (select_mode == SELECT_REPLACE) {
01121         /* reset selection mode */
01122         select_mode= SELECT_ADD;
01123         
01124         /* deselect all other keyframes (+ F-Curves too) */
01125         deselect_graph_keys(ac, 0, SELECT_SUBTRACT, TRUE);
01126         
01127         /* deselect other channels too, but only only do this if 
01128          * selection of channel when the visibility of keyframes 
01129          * doesn't depend on this 
01130          */
01131         if ((sipo->flag & SIPO_SELCUVERTSONLY) == 0)
01132             ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, 0, ACHANNEL_SETFLAG_CLEAR);
01133     }
01134     
01135     /* if points can be selected on this F-Curve */
01136     // TODO: what about those with no keyframes?
01137     if ((curves_only == 0) && ((nvi->fcu->flag & FCURVE_PROTECTED)==0)) {
01138         /* only if there's keyframe */
01139         if (nvi->bezt) {
01140             bezt= nvi->bezt; /* used to check bezt seletion is set */
01141             /* depends on selection mode */
01142             if (select_mode == SELECT_INVERT) {
01143                 /* keyframe - invert select of all */
01144                 if (nvi->hpoint == NEAREST_HANDLE_KEY) {
01145                     if (BEZSELECTED(bezt)) {
01146                         BEZ_DESEL(bezt);
01147                     }
01148                     else {
01149                         BEZ_SEL(bezt);
01150                     }
01151                 }
01152                 
01153                 /* handles - toggle selection of relevant handle */
01154                 else if (nvi->hpoint == NEAREST_HANDLE_LEFT) {
01155                     /* toggle selection */
01156                     bezt->f1 ^= SELECT;
01157                 }
01158                 else {
01159                     /* toggle selection */
01160                     bezt->f3 ^= SELECT;
01161                 }
01162             }
01163             else {
01164                 /* if the keyframe was clicked on, select all verts of given beztriple */
01165                 if (nvi->hpoint == NEAREST_HANDLE_KEY) {
01166                     BEZ_SEL(bezt);
01167                 }
01168                 /* otherwise, select the handle that applied */
01169                 else if (nvi->hpoint == NEAREST_HANDLE_LEFT) 
01170                     bezt->f1 |= SELECT;
01171                 else 
01172                     bezt->f3 |= SELECT;
01173             }
01174         }
01175         else if (nvi->fpt) {
01176             // TODO: need to handle sample points
01177         }
01178     }
01179     else {
01180         KeyframeEditFunc select_cb;
01181         KeyframeEditData ked;
01182         
01183         /* initialise keyframe editing data */
01184         memset(&ked, 0, sizeof(KeyframeEditData));
01185         
01186         /* set up BezTriple edit callbacks */
01187         select_cb= ANIM_editkeyframes_select(select_mode);
01188         
01189         /* select all keyframes */
01190         ANIM_fcurve_keyframes_loop(&ked, nvi->fcu, NULL, select_cb, NULL);
01191     }
01192     
01193     /* only change selection of channel when the visibility of keyframes doesn't depend on this */
01194     if ((sipo->flag & SIPO_SELCUVERTSONLY) == 0) {
01195         /* select or deselect curve? */
01196         if (bezt) {
01197             /* take selection status from item that got hit, to prevent flip/flop on channel 
01198              * selection status when shift-selecting (i.e. "SELECT_INVERT") points
01199              */
01200             if (BEZSELECTED(bezt))
01201                 nvi->fcu->flag |= FCURVE_SELECTED;
01202             else
01203                 nvi->fcu->flag &= ~FCURVE_SELECTED;
01204         }
01205         else {
01206             /* didn't hit any channel, so just apply that selection mode to the curve's selection status */
01207             if (select_mode == SELECT_INVERT)
01208                 nvi->fcu->flag ^= FCURVE_SELECTED;
01209             else if (select_mode == SELECT_ADD)
01210                 nvi->fcu->flag |= FCURVE_SELECTED;
01211         }
01212     }
01213 
01214     /* set active F-Curve (NOTE: sync the filter flags with findnearest_fcurve_vert) */
01215     /* needs to be called with (sipo->flag & SIPO_SELCUVERTSONLY) otherwise the active flag won't be set [#26452] */
01216     if (nvi->fcu->flag & FCURVE_SELECTED) {
01217         int filter= (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_NODUPLIS);
01218         ANIM_set_active_channel(ac, ac->data, ac->datatype, filter, nvi->fcu, ANIMTYPE_FCURVE);
01219     }
01220 
01221     /* free temp sample data for filtering */
01222     MEM_freeN(nvi);
01223 }
01224 
01225 /* Option 2) Selects all the keyframes on either side of the current frame (depends on which side the mouse is on) */
01226 /* (see graphkeys_select_leftright) */
01227 
01228 /* Option 3) Selects all visible keyframes in the same frame as the mouse click */
01229 static void graphkeys_mselect_column (bAnimContext *ac, const int mval[2], short select_mode)
01230 {
01231     ListBase anim_data= {NULL, NULL};
01232     bAnimListElem *ale;
01233     int filter;
01234     
01235     KeyframeEditFunc select_cb, ok_cb;
01236     KeyframeEditData ked;
01237     tNearestVertInfo *nvi;
01238     float selx = (float)ac->scene->r.cfra;
01239     
01240     /* find the beztriple that we're selecting, and the handle that was clicked on */
01241     nvi = find_nearest_fcurve_vert(ac, mval);
01242     
01243     /* check if anything to select */
01244     if (nvi == NULL)    
01245         return;
01246     
01247     /* get frame number on which elements should be selected */
01248     // TODO: should we restrict to integer frames only?
01249     if (nvi->bezt)
01250         selx= nvi->bezt->vec[1][0];
01251     else if (nvi->fpt)
01252         selx= nvi->fpt->vec[0];
01253     
01254     /* if select mode is replace, deselect all keyframes first */
01255     if (select_mode==SELECT_REPLACE) {
01256         /* reset selection mode to add to selection */
01257         select_mode= SELECT_ADD;
01258         
01259         /* - deselect all other keyframes, so that just the newly selected remain
01260          * - channels aren't deselected, since we don't re-select any as a consequence
01261          */
01262         deselect_graph_keys(ac, 0, SELECT_SUBTRACT, FALSE);
01263     }
01264     
01265     /* initialise keyframe editing data */
01266     memset(&ked, 0, sizeof(KeyframeEditData));
01267     
01268     /* set up BezTriple edit callbacks */
01269     select_cb= ANIM_editkeyframes_select(select_mode);
01270     ok_cb= ANIM_editkeyframes_ok(BEZT_OK_FRAME);
01271     
01272     /* loop through all of the keys and select additional keyframes
01273      * based on the keys found to be selected above
01274      */
01275     filter= (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_NODUPLIS);
01276     ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
01277     
01278     for (ale= anim_data.first; ale; ale= ale->next) {
01279         AnimData *adt= ANIM_nla_mapping_get(ac, ale);
01280         
01281         /* set frame for validation callback to refer to */
01282         if (adt)
01283             ked.f1= BKE_nla_tweakedit_remap(adt, selx, NLATIME_CONVERT_UNMAP);
01284         else
01285             ked.f1= selx;
01286         
01287         /* select elements with frame number matching cfra */
01288         ANIM_fcurve_keyframes_loop(&ked, ale->key_data, ok_cb, select_cb, NULL);
01289     }
01290     
01291     /* free elements */
01292     MEM_freeN(nvi);
01293     BLI_freelistN(&ked.list);
01294     BLI_freelistN(&anim_data);
01295 }
01296  
01297 /* ------------------- */
01298 
01299 /* handle clicking */
01300 static int graphkeys_clickselect_invoke(bContext *C, wmOperator *op, wmEvent *event)
01301 {
01302     bAnimContext ac;
01303     short selectmode;
01304 
01305     /* get editor data */
01306     if (ANIM_animdata_get_context(C, &ac) == 0)
01307         return OPERATOR_CANCELLED;
01308 
01309     /* select mode is either replace (deselect all, then add) or add/extend */
01310     if (RNA_boolean_get(op->ptr, "extend"))
01311         selectmode= SELECT_INVERT;
01312     else
01313         selectmode= SELECT_REPLACE;
01314     
01315     /* figure out action to take */
01316     if (RNA_boolean_get(op->ptr, "column")) {
01317         /* select all keyframes in the same frame as the one that was under the mouse */
01318         graphkeys_mselect_column(&ac, event->mval, selectmode);
01319     }
01320     else if (RNA_boolean_get(op->ptr, "curves")) {
01321         /* select all keyframes in the same F-Curve as the one under the mouse */
01322         mouse_graph_keys(&ac, event->mval, selectmode, 1);
01323     }
01324     else {
01325         /* select keyframe under mouse */
01326         mouse_graph_keys(&ac, event->mval, selectmode, 0);
01327     }
01328     
01329     /* set notifier that keyframe selection (and also channel selection in some cases) has changed */
01330     WM_event_add_notifier(C, NC_ANIMATION|ND_KEYFRAME|ND_ANIMCHAN|NA_SELECTED, NULL);
01331     
01332     /* for tweak grab to work */
01333     return OPERATOR_FINISHED|OPERATOR_PASS_THROUGH;
01334 }
01335  
01336 void GRAPH_OT_clickselect (wmOperatorType *ot)
01337 {
01338     /* identifiers */
01339     ot->name= "Mouse Select Keys";
01340     ot->idname= "GRAPH_OT_clickselect";
01341     ot->description= "Select keyframes by clicking on them";
01342     
01343     /* api callbacks */
01344     ot->invoke= graphkeys_clickselect_invoke;
01345     ot->poll= graphop_visible_keyframes_poll;
01346     
01347     /* id-props */
01348     RNA_def_boolean(ot->srna, "extend", 0, "Extend Select", ""); // SHIFTKEY
01349     RNA_def_boolean(ot->srna, "column", 0, "Column Select", "Select all keyframes that occur on the same frame as the one under the mouse"); // ALTKEY
01350     RNA_def_boolean(ot->srna, "curves", 0, "Only Curves", "Select all the keyframes in the curve"); // CTRLKEY + ALTKEY
01351 }
01352 
01353 /* ************************************************************************** */