Blender  V2.59
outliner_edit.c
Go to the documentation of this file.
00001 /*
00002  * $Id: outliner_edit.c 39294 2011-08-11 06:40:04Z gsrb3d $
00003  *
00004  * ***** BEGIN GPL LICENSE BLOCK *****
00005  *
00006  * This program is free software; you can redistribute it and/or
00007  * modify it under the terms of the GNU General Public License
00008  * as published by the Free Software Foundation; either version 2
00009  * of the License, or (at your option) any later version. 
00010  *
00011  * This program is distributed in the hope that it will be useful,
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  * GNU General Public License for more details.
00015  *
00016  * You should have received a copy of the GNU General Public License
00017  * along with this program; if not, write to the Free Software Foundation,
00018  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00019  *
00020  * The Original Code is Copyright (C) 2004 Blender Foundation.
00021  * All rights reserved.
00022  *
00023  * The Original Code is: all of this file.
00024  *
00025  * Contributor(s): Joshua Leung
00026  *
00027  * ***** END GPL LICENSE BLOCK *****
00028  */
00029 
00034 #include <math.h>
00035 #include <string.h>
00036 #include <stdlib.h>
00037 #include <stddef.h>
00038 
00039 #include "MEM_guardedalloc.h"
00040 
00041 #include "DNA_anim_types.h"
00042 #include "DNA_armature_types.h"
00043 #include "DNA_constraint_types.h"
00044 #include "DNA_camera_types.h"
00045 #include "DNA_group_types.h"
00046 #include "DNA_key_types.h"
00047 #include "DNA_lamp_types.h"
00048 #include "DNA_material_types.h"
00049 #include "DNA_mesh_types.h"
00050 #include "DNA_meta_types.h"
00051 #include "DNA_particle_types.h"
00052 #include "DNA_scene_types.h"
00053 #include "DNA_world_types.h"
00054 #include "DNA_sequence_types.h"
00055 #include "DNA_object_types.h"
00056 
00057 #include "BLI_blenlib.h"
00058 #include "BLI_utildefines.h"
00059 #include "BLI_math_base.h"
00060 
00061 #if defined WIN32 && !defined _LIBC
00062 # include "BLI_fnmatch.h" /* use fnmatch included in blenlib */
00063 #else
00064 #  ifndef _GNU_SOURCE
00065 #    define _GNU_SOURCE
00066 #  endif
00067 # include <fnmatch.h>
00068 #endif
00069 
00070 
00071 #include "BKE_animsys.h"
00072 #include "BKE_context.h"
00073 #include "BKE_deform.h"
00074 #include "BKE_depsgraph.h"
00075 #include "BKE_fcurve.h"
00076 #include "BKE_global.h"
00077 #include "BKE_group.h"
00078 #include "BKE_library.h"
00079 #include "BKE_main.h"
00080 #include "BKE_modifier.h"
00081 #include "BKE_report.h"
00082 #include "BKE_scene.h"
00083 #include "BKE_sequencer.h"
00084 
00085 #include "ED_armature.h"
00086 #include "ED_object.h"
00087 #include "ED_screen.h"
00088 #include "ED_util.h"
00089 
00090 #include "WM_api.h"
00091 #include "WM_types.h"
00092 
00093 #include "BIF_gl.h"
00094 #include "BIF_glutil.h"
00095 
00096 #include "UI_interface.h"
00097 #include "UI_interface_icons.h"
00098 #include "UI_resources.h"
00099 #include "UI_view2d.h"
00100 
00101 #include "RNA_access.h"
00102 #include "RNA_define.h"
00103 #include "RNA_enum_types.h"
00104 
00105 #include "ED_keyframing.h"
00106 
00107 #include "outliner_intern.h"
00108 
00109 /* ************************************************************** */
00110 /* Unused Utilities */
00111 // XXX: where to place these?
00112 
00113 /* This is not used anywhere at the moment */
00114 #if 0
00115 /* return 1 when levels were opened */
00116 static int outliner_open_back(SpaceOops *soops, TreeElement *te)
00117 {
00118         TreeStoreElem *tselem;
00119         int retval= 0;
00120         
00121         for (te= te->parent; te; te= te->parent) {
00122                 tselem= TREESTORE(te);
00123                 if (tselem->flag & TSE_CLOSED) { 
00124                         tselem->flag &= ~TSE_CLOSED;
00125                         retval= 1;
00126                 }
00127         }
00128         return retval;
00129 }
00130 
00131 static void outliner_open_reveal(SpaceOops *soops, ListBase *lb, TreeElement *teFind, int *found)
00132 {
00133         TreeElement *te;
00134         TreeStoreElem *tselem;
00135         
00136         for (te= lb->first; te; te= te->next) {
00137                 /* check if this tree-element was the one we're seeking */
00138                 if (te == teFind) {
00139                         *found= 1;
00140                         return;
00141                 }
00142                 
00143                 /* try to see if sub-tree contains it then */
00144                 outliner_open_reveal(soops, &te->subtree, teFind, found);
00145                 if (*found) {
00146                         tselem= TREESTORE(te);
00147                         if (tselem->flag & TSE_CLOSED) 
00148                                 tselem->flag &= ~TSE_CLOSED;
00149                         return;
00150                 }
00151         }
00152 }
00153 #endif
00154 
00155 /* ************************************************************** */
00156 /* Click Activated */
00157 
00158 /* Toggle Open/Closed ------------------------------------------- */
00159 
00160 static int do_outliner_item_openclose(bContext *C, SpaceOops *soops, TreeElement *te, int all, const float mval[2])
00161 {
00162         
00163         if(mval[1]>te->ys && mval[1]<te->ys+UI_UNIT_Y) {
00164                 TreeStoreElem *tselem= TREESTORE(te);
00165                 
00166                 /* all below close/open? */
00167                 if(all) {
00168                         tselem->flag &= ~TSE_CLOSED;
00169                         outliner_set_flag(soops, &te->subtree, TSE_CLOSED, !outliner_has_one_flag(soops, &te->subtree, TSE_CLOSED, 1));
00170                 }
00171                 else {
00172                         if(tselem->flag & TSE_CLOSED) tselem->flag &= ~TSE_CLOSED;
00173                         else tselem->flag |= TSE_CLOSED;
00174                 }
00175                 
00176                 return 1;
00177         }
00178         
00179         for(te= te->subtree.first; te; te= te->next) {
00180                 if(do_outliner_item_openclose(C, soops, te, all, mval)) 
00181                         return 1;
00182         }
00183         return 0;
00184         
00185 }
00186 
00187 /* event can enterkey, then it opens/closes */
00188 static int outliner_item_openclose(bContext *C, wmOperator *op, wmEvent *event)
00189 {
00190         ARegion *ar= CTX_wm_region(C);
00191         SpaceOops *soops= CTX_wm_space_outliner(C);
00192         TreeElement *te;
00193         float fmval[2];
00194         int all= RNA_boolean_get(op->ptr, "all");
00195         
00196         UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], fmval, fmval+1);
00197         
00198         for(te= soops->tree.first; te; te= te->next) {
00199                 if(do_outliner_item_openclose(C, soops, te, all, fmval)) 
00200                         break;
00201         }
00202 
00203         ED_region_tag_redraw(ar);
00204         
00205         return OPERATOR_FINISHED;
00206 }
00207 
00208 void OUTLINER_OT_item_openclose(wmOperatorType *ot)
00209 {
00210         ot->name= "Open/Close Item";
00211         ot->idname= "OUTLINER_OT_item_openclose";
00212         ot->description= "Toggle whether item under cursor is enabled or closed";
00213         
00214         ot->invoke= outliner_item_openclose;
00215         
00216         ot->poll= ED_operator_outliner_active;
00217         
00218         RNA_def_boolean(ot->srna, "all", 1, "All", "Close or open all items.");
00219 }
00220 
00221 /* Rename --------------------------------------------------- */
00222 
00223 static int do_outliner_item_rename(bContext *C, ARegion *ar, SpaceOops *soops, TreeElement *te, const float mval[2])
00224 {       
00225         ReportList *reports= CTX_wm_reports(C); // XXX
00226         
00227         if(mval[1]>te->ys && mval[1]<te->ys+UI_UNIT_Y) {
00228                 TreeStoreElem *tselem= TREESTORE(te);
00229                 
00230                 /* name and first icon */
00231                 if(mval[0]>te->xs+UI_UNIT_X && mval[0]<te->xend) {
00232                         
00233                         /* can't rename rna datablocks entries */
00234                         if(ELEM3(tselem->type, TSE_RNA_STRUCT, TSE_RNA_PROPERTY, TSE_RNA_ARRAY_ELEM))
00235                            ;
00236                         else if(ELEM10(tselem->type, TSE_ANIM_DATA, TSE_NLA, TSE_DEFGROUP_BASE, TSE_CONSTRAINT_BASE, TSE_MODIFIER_BASE, TSE_SCRIPT_BASE, TSE_POSE_BASE, TSE_POSEGRP_BASE, TSE_R_LAYER_BASE, TSE_R_PASS)) 
00237                                         BKE_report(reports, RPT_WARNING, "Cannot edit builtin name");
00238                         else if(ELEM3(tselem->type, TSE_SEQUENCE, TSE_SEQ_STRIP, TSE_SEQUENCE_DUP))
00239                                 BKE_report(reports, RPT_WARNING, "Cannot edit sequence name");
00240                         else if(tselem->id->lib) {
00241                                 // XXX                                          error_libdata();
00242                         } 
00243                         else if(te->idcode == ID_LI && te->parent) {
00244                                 BKE_report(reports, RPT_WARNING, "Cannot edit the path of an indirectly linked library");
00245                         } 
00246                         else {
00247                                 tselem->flag |= TSE_TEXTBUT;
00248                                 ED_region_tag_redraw(ar);
00249                         }
00250                 }
00251                 return 1;
00252         }
00253         
00254         for(te= te->subtree.first; te; te= te->next) {
00255                 if(do_outliner_item_rename(C, ar, soops, te, mval)) return 1;
00256         }
00257         return 0;
00258 }
00259 
00260 static int outliner_item_rename(bContext *C, wmOperator *UNUSED(op), wmEvent *event)
00261 {
00262         ARegion *ar= CTX_wm_region(C);
00263         SpaceOops *soops= CTX_wm_space_outliner(C);
00264         TreeElement *te;
00265         float fmval[2];
00266         
00267         UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], fmval, fmval+1);
00268         
00269         for(te= soops->tree.first; te; te= te->next) {
00270                 if(do_outliner_item_rename(C, ar, soops, te, fmval)) break;
00271         }
00272         
00273         return OPERATOR_FINISHED;
00274 }
00275 
00276 
00277 void OUTLINER_OT_item_rename(wmOperatorType *ot)
00278 {
00279         ot->name= "Rename Item";
00280         ot->idname= "OUTLINER_OT_item_rename";
00281         ot->description= "Rename item under cursor";
00282         
00283         ot->invoke= outliner_item_rename;
00284         
00285         ot->poll= ED_operator_outliner_active;
00286 }
00287 
00288 /* ************************************************************** */
00289 /* Setting Toggling Operators */
00290 
00291 /* =============================================== */
00292 /* Toggling Utilities (Exported) */
00293 
00294 /* Apply Settings ------------------------------- */
00295 
00296 static int outliner_count_levels(SpaceOops *soops, ListBase *lb, int curlevel)
00297 {
00298         TreeElement *te;
00299         int level=curlevel, lev;
00300         
00301         for(te= lb->first; te; te= te->next) {
00302                 
00303                 lev= outliner_count_levels(soops, &te->subtree, curlevel+1);
00304                 if(lev>level) level= lev;
00305         }
00306         return level;
00307 }
00308 
00309 int outliner_has_one_flag(SpaceOops *soops, ListBase *lb, short flag, short curlevel)
00310 {
00311         TreeElement *te;
00312         TreeStoreElem *tselem;
00313         int level;
00314         
00315         for(te= lb->first; te; te= te->next) {
00316                 tselem= TREESTORE(te);
00317                 if(tselem->flag & flag) return curlevel;
00318                 
00319                 level= outliner_has_one_flag(soops, &te->subtree, flag, curlevel+1);
00320                 if(level) return level;
00321         }
00322         return 0;
00323 }
00324 
00325 void outliner_set_flag(SpaceOops *soops, ListBase *lb, short flag, short set)
00326 {
00327         TreeElement *te;
00328         TreeStoreElem *tselem;
00329         
00330         for(te= lb->first; te; te= te->next) {
00331                 tselem= TREESTORE(te);
00332                 if(set==0) tselem->flag &= ~flag;
00333                 else tselem->flag |= flag;
00334                 outliner_set_flag(soops, &te->subtree, flag, set);
00335         }
00336 }
00337 
00338 /* Restriction Columns ------------------------------- */
00339 
00340 /* same check needed for both object operation and restrict column button func
00341  * return 0 when in edit mode (cannot restrict view or select)
00342  * otherwise return 1 */
00343 int common_restrict_check(bContext *C, Object *ob)
00344 {
00345         /* Don't allow hide an object in edit mode,
00346          * check the bug #22153 and #21609, #23977
00347          */
00348         Object *obedit= CTX_data_edit_object(C);
00349         if (obedit && obedit == ob) {
00350                 /* found object is hidden, reset */
00351                 if (ob->restrictflag & OB_RESTRICT_VIEW)
00352                         ob->restrictflag &= ~OB_RESTRICT_VIEW;
00353                 /* found object is unselectable, reset */
00354                 if (ob->restrictflag & OB_RESTRICT_SELECT)
00355                         ob->restrictflag &= ~OB_RESTRICT_SELECT;
00356                 return 0;
00357         }
00358         
00359         return 1;
00360 }
00361 
00362 /* =============================================== */
00363 /* Restriction toggles */
00364 
00365 /* Toggle Visibility ---------------------------------------- */
00366 
00367 void object_toggle_visibility_cb(bContext *C, Scene *scene, TreeElement *te, TreeStoreElem *UNUSED(tsep), TreeStoreElem *tselem)
00368 {
00369         Base *base= (Base *)te->directdata;
00370         Object *ob = (Object *)tselem->id;
00371         
00372         /* add check for edit mode */
00373         if(!common_restrict_check(C, ob)) return;
00374         
00375         if(base || (base= object_in_scene(ob, scene))) {
00376                 if((base->object->restrictflag ^= OB_RESTRICT_VIEW)) {
00377                         ED_base_object_select(base, BA_DESELECT);
00378                 }
00379         }
00380 }
00381 
00382 static int outliner_toggle_visibility_exec(bContext *C, wmOperator *UNUSED(op))
00383 {
00384         SpaceOops *soops= CTX_wm_space_outliner(C);
00385         Scene *scene= CTX_data_scene(C);
00386         ARegion *ar= CTX_wm_region(C);
00387         
00388         outliner_do_object_operation(C, scene, soops, &soops->tree, object_toggle_visibility_cb);
00389         
00390         WM_event_add_notifier(C, NC_SCENE|ND_OB_VISIBLE, scene);
00391         ED_region_tag_redraw(ar);
00392         
00393         return OPERATOR_FINISHED;
00394 }
00395 
00396 void OUTLINER_OT_visibility_toggle(wmOperatorType *ot)
00397 {
00398         /* identifiers */
00399         ot->name= "Toggle Visibility";
00400         ot->idname= "OUTLINER_OT_visibility_toggle";
00401         ot->description= "Toggle the visibility of selected items";
00402         
00403         /* callbacks */
00404         ot->exec= outliner_toggle_visibility_exec;
00405         ot->poll= ED_operator_outliner_active_no_editobject;
00406         
00407         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
00408 }
00409 
00410 /* Toggle Selectability ---------------------------------------- */
00411 
00412 void object_toggle_selectability_cb(bContext *UNUSED(C), Scene *scene, TreeElement *te, TreeStoreElem *UNUSED(tsep), TreeStoreElem *tselem)
00413 {
00414         Base *base= (Base *)te->directdata;
00415         
00416         if(base==NULL) base= object_in_scene((Object *)tselem->id, scene);
00417         if(base) {
00418                 base->object->restrictflag^=OB_RESTRICT_SELECT;
00419         }
00420 }
00421 
00422 static int outliner_toggle_selectability_exec(bContext *C, wmOperator *UNUSED(op))
00423 {
00424         SpaceOops *soops= CTX_wm_space_outliner(C);
00425         Scene *scene= CTX_data_scene(C);
00426         ARegion *ar= CTX_wm_region(C);
00427         
00428         outliner_do_object_operation(C, scene, soops, &soops->tree, object_toggle_selectability_cb);
00429         
00430         WM_event_add_notifier(C, NC_SCENE|ND_OB_SELECT, scene);
00431         ED_region_tag_redraw(ar);
00432         
00433         return OPERATOR_FINISHED;
00434 }
00435 
00436 void OUTLINER_OT_selectability_toggle(wmOperatorType *ot)
00437 {
00438         /* identifiers */
00439         ot->name= "Toggle Selectability";
00440         ot->idname= "OUTLINER_OT_selectability_toggle";
00441         ot->description= "Toggle the selectability";
00442         
00443         /* callbacks */
00444         ot->exec= outliner_toggle_selectability_exec;
00445         ot->poll= ED_operator_outliner_active_no_editobject;
00446         
00447         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
00448 }
00449 
00450 /* Toggle Renderability ---------------------------------------- */
00451 
00452 void object_toggle_renderability_cb(bContext *UNUSED(C), Scene *scene, TreeElement *te, TreeStoreElem *UNUSED(tsep), TreeStoreElem *tselem)
00453 {
00454         Base *base= (Base *)te->directdata;
00455         
00456         if(base==NULL) base= object_in_scene((Object *)tselem->id, scene);
00457         if(base) {
00458                 base->object->restrictflag^=OB_RESTRICT_RENDER;
00459         }
00460 }
00461 
00462 static int outliner_toggle_renderability_exec(bContext *C, wmOperator *UNUSED(op))
00463 {
00464         SpaceOops *soops= CTX_wm_space_outliner(C);
00465         Scene *scene= CTX_data_scene(C);
00466         ARegion *ar= CTX_wm_region(C);
00467         
00468         outliner_do_object_operation(C, scene, soops, &soops->tree, object_toggle_renderability_cb);
00469         
00470         ED_region_tag_redraw(ar);
00471         
00472         return OPERATOR_FINISHED;
00473 }
00474 
00475 void OUTLINER_OT_renderability_toggle(wmOperatorType *ot)
00476 {
00477         /* identifiers */
00478         ot->name= "Toggle Renderability";
00479         ot->idname= "OUTLINER_OT_renderability_toggle";
00480         ot->description= "Toggle the renderability of selected items";
00481         
00482         /* callbacks */
00483         ot->exec= outliner_toggle_renderability_exec;
00484         ot->poll= ED_operator_outliner_active;
00485         
00486         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
00487 }
00488 
00489 /* =============================================== */
00490 /* Outliner setting toggles */
00491 
00492 /* Toggle Expanded (Outliner) ---------------------------------------- */
00493 
00494 static int outliner_toggle_expanded_exec(bContext *C, wmOperator *UNUSED(op))
00495 {
00496         SpaceOops *soops= CTX_wm_space_outliner(C);
00497         ARegion *ar= CTX_wm_region(C);
00498         
00499         if (outliner_has_one_flag(soops, &soops->tree, TSE_CLOSED, 1))
00500                 outliner_set_flag(soops, &soops->tree, TSE_CLOSED, 0);
00501         else 
00502                 outliner_set_flag(soops, &soops->tree, TSE_CLOSED, 1);
00503         
00504         ED_region_tag_redraw(ar);
00505         
00506         return OPERATOR_FINISHED;
00507 }
00508 
00509 void OUTLINER_OT_expanded_toggle(wmOperatorType *ot)
00510 {
00511         /* identifiers */
00512         ot->name= "Expand/Collapse All";
00513         ot->idname= "OUTLINER_OT_expanded_toggle";
00514         ot->description= "Expand/Collapse all items";
00515         
00516         /* callbacks */
00517         ot->exec= outliner_toggle_expanded_exec;
00518         ot->poll= ED_operator_outliner_active;
00519         
00520         /* no undo or registry, UI option */
00521 }
00522 
00523 /* Toggle Selected (Outliner) ---------------------------------------- */
00524 
00525 static int outliner_toggle_selected_exec(bContext *C, wmOperator *UNUSED(op))
00526 {
00527         SpaceOops *soops= CTX_wm_space_outliner(C);
00528         ARegion *ar= CTX_wm_region(C);
00529         Scene *scene= CTX_data_scene(C);
00530         
00531         if (outliner_has_one_flag(soops, &soops->tree, TSE_SELECTED, 1))
00532                 outliner_set_flag(soops, &soops->tree, TSE_SELECTED, 0);
00533         else 
00534                 outliner_set_flag(soops, &soops->tree, TSE_SELECTED, 1);
00535         
00536         soops->storeflag |= SO_TREESTORE_REDRAW;
00537         
00538         WM_event_add_notifier(C, NC_SCENE|ND_OB_SELECT, scene);
00539         ED_region_tag_redraw(ar);
00540         
00541         return OPERATOR_FINISHED;
00542 }
00543 
00544 void OUTLINER_OT_selected_toggle(wmOperatorType *ot)
00545 {
00546         /* identifiers */
00547         ot->name= "Toggle Selected";
00548         ot->idname= "OUTLINER_OT_selected_toggle";
00549         ot->description= "Toggle the Outliner selection of items";
00550         
00551         /* callbacks */
00552         ot->exec= outliner_toggle_selected_exec;
00553         ot->poll= ED_operator_outliner_active;
00554         
00555         /* no undo or registry, UI option */
00556 }
00557 
00558 /* ************************************************************** */
00559 /* Hotkey Only Operators */
00560 
00561 /* Show Active --------------------------------------------------- */
00562 
00563 static int outliner_show_active_exec(bContext *C, wmOperator *UNUSED(op))
00564 {
00565         SpaceOops *so= CTX_wm_space_outliner(C);
00566         Scene *scene= CTX_data_scene(C);
00567         ARegion *ar= CTX_wm_region(C);
00568         View2D *v2d= &ar->v2d;
00569         
00570         TreeElement *te;
00571         int xdelta, ytop;
00572         
00573         // TODO: make this get this info from context instead...
00574         if (OBACT == NULL) 
00575                 return OPERATOR_CANCELLED;
00576         
00577         te= outliner_find_id(so, &so->tree, (ID *)OBACT);
00578         if (te) {
00579                 /* make te->ys center of view */
00580                 ytop= (int)(te->ys + (v2d->mask.ymax - v2d->mask.ymin)/2);
00581                 if (ytop>0) ytop= 0;
00582                 
00583                 v2d->cur.ymax= (float)ytop;
00584                 v2d->cur.ymin= (float)(ytop-(v2d->mask.ymax - v2d->mask.ymin));
00585                 
00586                 /* make te->xs ==> te->xend center of view */
00587                 xdelta = (int)(te->xs - v2d->cur.xmin);
00588                 v2d->cur.xmin += xdelta;
00589                 v2d->cur.xmax += xdelta;
00590                 
00591                 so->storeflag |= SO_TREESTORE_REDRAW;
00592         }
00593         
00594         ED_region_tag_redraw(ar);
00595         
00596         return OPERATOR_FINISHED;
00597 }
00598 
00599 void OUTLINER_OT_show_active(wmOperatorType *ot)
00600 {
00601         /* identifiers */
00602         ot->name= "Show Active";
00603         ot->idname= "OUTLINER_OT_show_active";
00604         ot->description= "Adjust the view so that the active Object is shown centered";
00605         
00606         /* callbacks */
00607         ot->exec= outliner_show_active_exec;
00608         ot->poll= ED_operator_outliner_active;
00609 }
00610 
00611 /* View Panning --------------------------------------------------- */
00612 
00613 static int outliner_scroll_page_exec(bContext *C, wmOperator *op)
00614 {
00615         ARegion *ar= CTX_wm_region(C);
00616         int dy= ar->v2d.mask.ymax - ar->v2d.mask.ymin;
00617         int up= 0;
00618         
00619         if(RNA_boolean_get(op->ptr, "up"))
00620                 up= 1;
00621 
00622         if(up == 0) dy= -dy;
00623         ar->v2d.cur.ymin+= dy;
00624         ar->v2d.cur.ymax+= dy;
00625         
00626         ED_region_tag_redraw(ar);
00627         
00628         return OPERATOR_FINISHED;
00629 }
00630 
00631 
00632 void OUTLINER_OT_scroll_page(wmOperatorType *ot)
00633 {
00634         /* identifiers */
00635         ot->name= "Scroll Page";
00636         ot->idname= "OUTLINER_OT_scroll_page";
00637         ot->description= "Scroll page up or down";
00638         
00639         /* callbacks */
00640         ot->exec= outliner_scroll_page_exec;
00641         ot->poll= ED_operator_outliner_active;
00642         
00643         /* properties */
00644         RNA_def_boolean(ot->srna, "up", 0, "Up", "Scroll up one page.");
00645 }
00646 
00647 /* Search ------------------------------------------------------- */
00648 // TODO: probably obsolete now with filtering?
00649 
00650 #if 0
00651 
00652 /* recursive helper for function below */
00653 static void outliner_set_coordinates_element(SpaceOops *soops, TreeElement *te, int startx, int *starty)
00654 {
00655         TreeStoreElem *tselem= TREESTORE(te);
00656         
00657         /* store coord and continue, we need coordinates for elements outside view too */
00658         te->xs= (float)startx;
00659         te->ys= (float)(*starty);
00660         *starty-= UI_UNIT_Y;
00661         
00662         if((tselem->flag & TSE_CLOSED)==0) {
00663                 TreeElement *ten;
00664                 for(ten= te->subtree.first; ten; ten= ten->next) {
00665                         outliner_set_coordinates_element(soops, ten, startx+UI_UNIT_X, starty);
00666                 }
00667         }
00668         
00669 }
00670 
00671 /* to retrieve coordinates with redrawing the entire tree */
00672 static void outliner_set_coordinates(ARegion *ar, SpaceOops *soops)
00673 {
00674         TreeElement *te;
00675         int starty= (int)(ar->v2d.tot.ymax)-UI_UNIT_Y;
00676         int startx= 0;
00677         
00678         for(te= soops->tree.first; te; te= te->next) {
00679                 outliner_set_coordinates_element(soops, te, startx, &starty);
00680         }
00681 }
00682 
00683 /* find next element that has this name */
00684 static TreeElement *outliner_find_named(SpaceOops *soops, ListBase *lb, char *name, int flags, TreeElement *prev, int *prevFound)
00685 {
00686         TreeElement *te, *tes;
00687         
00688         for (te= lb->first; te; te= te->next) {
00689                 int found = outliner_filter_has_name(te, name, flags);
00690                 
00691                 if(found) {
00692                         /* name is right, but is element the previous one? */
00693                         if (prev) {
00694                                 if ((te != prev) && (*prevFound)) 
00695                                         return te;
00696                                 if (te == prev) {
00697                                         *prevFound = 1;
00698                                 }
00699                         }
00700                         else
00701                                 return te;
00702                 }
00703                 
00704                 tes= outliner_find_named(soops, &te->subtree, name, flags, prev, prevFound);
00705                 if(tes) return tes;
00706         }
00707 
00708         /* nothing valid found */
00709         return NULL;
00710 }
00711 
00712 static void outliner_find_panel(Scene *UNUSED(scene), ARegion *ar, SpaceOops *soops, int again, int flags) 
00713 {
00714         ReportList *reports = NULL; // CTX_wm_reports(C);
00715         TreeElement *te= NULL;
00716         TreeElement *last_find;
00717         TreeStoreElem *tselem;
00718         int ytop, xdelta, prevFound=0;
00719         char name[32];
00720         
00721         /* get last found tree-element based on stored search_tse */
00722         last_find= outliner_find_tse(soops, &soops->search_tse);
00723         
00724         /* determine which type of search to do */
00725         if (again && last_find) {
00726                 /* no popup panel - previous + user wanted to search for next after previous */         
00727                 BLI_strncpy(name, soops->search_string, sizeof(name));
00728                 flags= soops->search_flags;
00729                 
00730                 /* try to find matching element */
00731                 te= outliner_find_named(soops, &soops->tree, name, flags, last_find, &prevFound);
00732                 if (te==NULL) {
00733                         /* no more matches after previous, start from beginning again */
00734                         prevFound= 1;
00735                         te= outliner_find_named(soops, &soops->tree, name, flags, last_find, &prevFound);
00736                 }
00737         }
00738         else {
00739                 /* pop up panel - no previous, or user didn't want search after previous */
00740                 strcpy(name, "");
00741 // XXX          if (sbutton(name, 0, sizeof(name)-1, "Find: ") && name[0]) {
00742 //                      te= outliner_find_named(soops, &soops->tree, name, flags, NULL, &prevFound);
00743 //              }
00744 //              else return; /* XXX RETURN! XXX */
00745         }
00746 
00747         /* do selection and reveal */
00748         if (te) {
00749                 tselem= TREESTORE(te);
00750                 if (tselem) {
00751                         /* expand branches so that it will be visible, we need to get correct coordinates */
00752                         if( outliner_open_back(soops, te))
00753                                 outliner_set_coordinates(ar, soops);
00754                         
00755                         /* deselect all visible, and select found element */
00756                         outliner_set_flag(soops, &soops->tree, TSE_SELECTED, 0);
00757                         tselem->flag |= TSE_SELECTED;
00758                         
00759                         /* make te->ys center of view */
00760                         ytop= (int)(te->ys + (ar->v2d.mask.ymax-ar->v2d.mask.ymin)/2);
00761                         if(ytop>0) ytop= 0;
00762                         ar->v2d.cur.ymax= (float)ytop;
00763                         ar->v2d.cur.ymin= (float)(ytop-(ar->v2d.mask.ymax-ar->v2d.mask.ymin));
00764                         
00765                         /* make te->xs ==> te->xend center of view */
00766                         xdelta = (int)(te->xs - ar->v2d.cur.xmin);
00767                         ar->v2d.cur.xmin += xdelta;
00768                         ar->v2d.cur.xmax += xdelta;
00769                         
00770                         /* store selection */
00771                         soops->search_tse= *tselem;
00772                         
00773                         BLI_strncpy(soops->search_string, name, 33);
00774                         soops->search_flags= flags;
00775                         
00776                         /* redraw */
00777                         soops->storeflag |= SO_TREESTORE_REDRAW;
00778                 }
00779         }
00780         else {
00781                 /* no tree-element found */
00782                 BKE_report(reports, RPT_WARNING, "Not found: %s", name);
00783         }
00784 }
00785 #endif
00786 
00787 /* Show One Level ----------------------------------------------- */
00788 
00789 /* helper function for Show/Hide one level operator */
00790 static void outliner_openclose_level(SpaceOops *soops, ListBase *lb, int curlevel, int level, int open)
00791 {
00792         TreeElement *te;
00793         TreeStoreElem *tselem;
00794         
00795         for(te= lb->first; te; te= te->next) {
00796                 tselem= TREESTORE(te);
00797                 
00798                 if(open) {
00799                         if(curlevel<=level) tselem->flag &= ~TSE_CLOSED;
00800                 }
00801                 else {
00802                         if(curlevel>=level) tselem->flag |= TSE_CLOSED;
00803                 }
00804                 
00805                 outliner_openclose_level(soops, &te->subtree, curlevel+1, level, open);
00806         }
00807 }
00808 
00809 static int outliner_one_level_exec(bContext *C, wmOperator *op)
00810 {
00811         SpaceOops *soops= CTX_wm_space_outliner(C);
00812         ARegion *ar= CTX_wm_region(C);
00813         int add= RNA_boolean_get(op->ptr, "open");
00814         int level;
00815         
00816         level= outliner_has_one_flag(soops, &soops->tree, TSE_CLOSED, 1);
00817         if(add==1) {
00818                 if(level) outliner_openclose_level(soops, &soops->tree, 1, level, 1);
00819         }
00820         else {
00821                 if(level==0) level= outliner_count_levels(soops, &soops->tree, 0);
00822                 if(level) outliner_openclose_level(soops, &soops->tree, 1, level-1, 0);
00823         }
00824         
00825         ED_region_tag_redraw(ar);
00826         
00827         return OPERATOR_FINISHED;
00828 }
00829 
00830 void OUTLINER_OT_show_one_level(wmOperatorType *ot)
00831 {
00832         /* identifiers */
00833         ot->name= "Show/Hide One Level";
00834         ot->idname= "OUTLINER_OT_show_one_level";
00835         ot->description= "Expand/collapse all entries by one level";
00836         
00837         /* callbacks */
00838         ot->exec= outliner_one_level_exec;
00839         ot->poll= ED_operator_outliner_active;
00840         
00841         /* no undo or registry, UI option */
00842         
00843         /* properties */
00844         RNA_def_boolean(ot->srna, "open", 1, "Open", "Expand all entries one level deep.");
00845 }
00846 
00847 /* Show Hierarchy ----------------------------------------------- */
00848 
00849 /* helper function for tree_element_shwo_hierarchy() - recursively checks whether subtrees have any objects*/
00850 static int subtree_has_objects(SpaceOops *soops, ListBase *lb)
00851 {
00852         TreeElement *te;
00853         TreeStoreElem *tselem;
00854         
00855         for(te= lb->first; te; te= te->next) {
00856                 tselem= TREESTORE(te);
00857                 if(tselem->type==0 && te->idcode==ID_OB) return 1;
00858                 if( subtree_has_objects(soops, &te->subtree)) return 1;
00859         }
00860         return 0;
00861 }
00862 
00863 /* recursive helper function for Show Hierarchy operator */
00864 static void tree_element_show_hierarchy(Scene *scene, SpaceOops *soops, ListBase *lb)
00865 {
00866         TreeElement *te;
00867         TreeStoreElem *tselem;
00868 
00869         /* open all object elems, close others */
00870         for(te= lb->first; te; te= te->next) {
00871                 tselem= TREESTORE(te);
00872                 
00873                 if(tselem->type==0) {
00874                         if(te->idcode==ID_SCE) {
00875                                 if(tselem->id!=(ID *)scene) tselem->flag |= TSE_CLOSED;
00876                                         else tselem->flag &= ~TSE_CLOSED;
00877                         }
00878                         else if(te->idcode==ID_OB) {
00879                                 if(subtree_has_objects(soops, &te->subtree)) tselem->flag &= ~TSE_CLOSED;
00880                                 else tselem->flag |= TSE_CLOSED;
00881                         }
00882                 }
00883                 else tselem->flag |= TSE_CLOSED;
00884                 
00885                 if(tselem->flag & TSE_CLOSED); else tree_element_show_hierarchy(scene, soops, &te->subtree);
00886         }
00887 }
00888 
00889 /* show entire object level hierarchy */
00890 static int outliner_show_hierarchy_exec(bContext *C, wmOperator *UNUSED(op))
00891 {
00892         SpaceOops *soops= CTX_wm_space_outliner(C);
00893         ARegion *ar= CTX_wm_region(C);
00894         Scene *scene= CTX_data_scene(C);
00895         
00896         /* recursively open/close levels */
00897         tree_element_show_hierarchy(scene, soops, &soops->tree);
00898         
00899         ED_region_tag_redraw(ar);
00900         
00901         return OPERATOR_FINISHED;
00902 }
00903 
00904 void OUTLINER_OT_show_hierarchy(wmOperatorType *ot)
00905 {
00906         /* identifiers */
00907         ot->name= "Show Hierarchy";
00908         ot->idname= "OUTLINER_OT_show_hierarchy";
00909         ot->description= "Open all object entries and close all others";
00910         
00911         /* callbacks */
00912         ot->exec= outliner_show_hierarchy_exec;
00913         ot->poll= ED_operator_outliner_active; //  TODO: shouldn't be allowed in RNA views...
00914         
00915         /* no undo or registry, UI option */
00916 }
00917 
00918 /* ************************************************************** */
00919 /* ANIMATO OPERATIONS */
00920 /* KeyingSet and Driver Creation - Helper functions */
00921 
00922 /* specialised poll callback for these operators to work in Datablocks view only */
00923 static int ed_operator_outliner_datablocks_active(bContext *C)
00924 {
00925         ScrArea *sa= CTX_wm_area(C);
00926         if ((sa) && (sa->spacetype==SPACE_OUTLINER)) {
00927                 SpaceOops *so= CTX_wm_space_outliner(C);
00928                 return (so->outlinevis == SO_DATABLOCKS);
00929         }
00930         return 0;
00931 }
00932 
00933 
00934 /* Helper func to extract an RNA path from selected tree element 
00935  * NOTE: the caller must zero-out all values of the pointers that it passes here first, as
00936  * this function does not do that yet 
00937  */
00938 static void tree_element_to_path(SpaceOops *soops, TreeElement *te, TreeStoreElem *tselem, 
00939                                                         ID **id, char **path, int *array_index, short *flag, short *UNUSED(groupmode))
00940 {
00941         ListBase hierarchy = {NULL, NULL};
00942         LinkData *ld;
00943         TreeElement *tem, *temnext, *temsub;
00944         TreeStoreElem *tse, *tsenext;
00945         PointerRNA *ptr, *nextptr;
00946         PropertyRNA *prop;
00947         char *newpath=NULL;
00948         
00949         /* optimise tricks:
00950          *      - Don't do anything if the selected item is a 'struct', but arrays are allowed
00951          */
00952         if (tselem->type == TSE_RNA_STRUCT)
00953                 return;
00954         
00955         /* Overview of Algorithm:
00956          *      1. Go up the chain of parents until we find the 'root', taking note of the 
00957          *         levels encountered in reverse-order (i.e. items are added to the start of the list
00958          *      for more convenient looping later)
00959          *      2. Walk down the chain, adding from the first ID encountered 
00960          *         (which will become the 'ID' for the KeyingSet Path), and build a  
00961          *              path as we step through the chain
00962          */
00963          
00964         /* step 1: flatten out hierarchy of parents into a flat chain */
00965         for (tem= te->parent; tem; tem= tem->parent) {
00966                 ld= MEM_callocN(sizeof(LinkData), "LinkData for tree_element_to_path()");
00967                 ld->data= tem;
00968                 BLI_addhead(&hierarchy, ld);
00969         }
00970         
00971         /* step 2: step down hierarchy building the path (NOTE: addhead in previous loop was needed so that we can loop like this) */
00972         for (ld= hierarchy.first; ld; ld= ld->next) {
00973                 /* get data */
00974                 tem= (TreeElement *)ld->data;
00975                 tse= TREESTORE(tem);
00976                 ptr= &tem->rnaptr;
00977                 prop= tem->directdata;
00978                 
00979                 /* check if we're looking for first ID, or appending to path */
00980                 if (*id) {
00981                         /* just 'append' property to path 
00982                          *      - to prevent memory leaks, we must write to newpath not path, then free old path + swap them
00983                          */
00984                         if(tse->type == TSE_RNA_PROPERTY) {
00985                                 if(RNA_property_type(prop) == PROP_POINTER) {
00986                                         /* for pointer we just append property name */
00987                                         newpath= RNA_path_append(*path, ptr, prop, 0, NULL);
00988                                 }
00989                                 else if(RNA_property_type(prop) == PROP_COLLECTION) {
00990                                         char buf[128], *name;
00991                                         
00992                                         temnext= (TreeElement*)(ld->next->data);
00993                                         tsenext= TREESTORE(temnext);
00994                                         
00995                                         nextptr= &temnext->rnaptr;
00996                                         name= RNA_struct_name_get_alloc(nextptr, buf, sizeof(buf));
00997                                         
00998                                         if(name) {
00999                                                 /* if possible, use name as a key in the path */
01000                                                 newpath= RNA_path_append(*path, NULL, prop, 0, name);
01001                                                 
01002                                                 if(name != buf)
01003                                                         MEM_freeN(name);
01004                                         }
01005                                         else {
01006                                                 /* otherwise use index */
01007                                                 int index= 0;
01008                                                 
01009                                                 for(temsub=tem->subtree.first; temsub; temsub=temsub->next, index++)
01010                                                         if(temsub == temnext)
01011                                                                 break;
01012                                                 
01013                                                 newpath= RNA_path_append(*path, NULL, prop, index, NULL);
01014                                         }
01015                                         
01016                                         ld= ld->next;
01017                                 }
01018                         }
01019                         
01020                         if(newpath) {
01021                                 if (*path) MEM_freeN(*path);
01022                                 *path= newpath;
01023                                 newpath= NULL;
01024                         }
01025                 }
01026                 else {
01027                         /* no ID, so check if entry is RNA-struct, and if that RNA-struct is an ID datablock to extract info from */
01028                         if (tse->type == TSE_RNA_STRUCT) {
01029                                 /* ptr->data not ptr->id.data seems to be the one we want, since ptr->data is sometimes the owner of this ID? */
01030                                 if(RNA_struct_is_ID(ptr->type)) {
01031                                         *id= (ID *)ptr->data;
01032                                         
01033                                         /* clear path */
01034                                         if(*path) {
01035                                                 MEM_freeN(*path);
01036                                                 path= NULL;
01037                                         }
01038                                 }
01039                         }
01040                 }
01041         }
01042 
01043         /* step 3: if we've got an ID, add the current item to the path */
01044         if (*id) {
01045                 /* add the active property to the path */
01046                 ptr= &te->rnaptr;
01047                 prop= te->directdata;
01048                 
01049                 /* array checks */
01050                 if (tselem->type == TSE_RNA_ARRAY_ELEM) {
01051                         /* item is part of an array, so must set the array_index */
01052                         *array_index= te->index;
01053                 }
01054                 else if (RNA_property_array_length(ptr, prop)) {
01055                         /* entire array was selected, so keyframe all */
01056                         *flag |= KSP_FLAG_WHOLE_ARRAY;
01057                 }
01058                 
01059                 /* path */
01060                 newpath= RNA_path_append(*path, NULL, prop, 0, NULL);
01061                 if (*path) MEM_freeN(*path);
01062                 *path= newpath;
01063         }
01064 
01065         /* free temp data */
01066         BLI_freelistN(&hierarchy);
01067 }
01068 
01069 /* =============================================== */
01070 /* Driver Operations */
01071 
01072 /* These operators are only available in databrowser mode for now, as
01073  * they depend on having RNA paths and/or hierarchies available.
01074  */
01075 enum {
01076         DRIVERS_EDITMODE_ADD    = 0,
01077         DRIVERS_EDITMODE_REMOVE,
01078 } /*eDrivers_EditModes*/;
01079 
01080 /* Utilities ---------------------------------- */ 
01081 
01082 /* Recursively iterate over tree, finding and working on selected items */
01083 static void do_outliner_drivers_editop(SpaceOops *soops, ListBase *tree, ReportList *reports, short mode)
01084 {
01085         TreeElement *te;
01086         TreeStoreElem *tselem;
01087         
01088         for (te= tree->first; te; te=te->next) {
01089                 tselem= TREESTORE(te);
01090                 
01091                 /* if item is selected, perform operation */
01092                 if (tselem->flag & TSE_SELECTED) {
01093                         ID *id= NULL;
01094                         char *path= NULL;
01095                         int array_index= 0;
01096                         short flag= 0;
01097                         short groupmode= KSP_GROUP_KSNAME;
01098                         
01099                         /* check if RNA-property described by this selected element is an animateable prop */
01100                         if (ELEM(tselem->type, TSE_RNA_PROPERTY, TSE_RNA_ARRAY_ELEM) && RNA_property_animateable(&te->rnaptr, te->directdata)) {
01101                                 /* get id + path + index info from the selected element */
01102                                 tree_element_to_path(soops, te, tselem, 
01103                                                 &id, &path, &array_index, &flag, &groupmode);
01104                         }
01105                         
01106                         /* only if ID and path were set, should we perform any actions */
01107                         if (id && path) {
01108                                 short dflags = CREATEDRIVER_WITH_DEFAULT_DVAR;
01109                                 int arraylen = 1;
01110                                 
01111                                 /* array checks */
01112                                 if (flag & KSP_FLAG_WHOLE_ARRAY) {
01113                                         /* entire array was selected, so add drivers for all */
01114                                         arraylen= RNA_property_array_length(&te->rnaptr, te->directdata);
01115                                 }
01116                                 else
01117                                         arraylen= array_index;
01118                                 
01119                                 /* we should do at least one step */
01120                                 if (arraylen == array_index)
01121                                         arraylen++;
01122                                 
01123                                 /* for each array element we should affect, add driver */
01124                                 for (; array_index < arraylen; array_index++) {
01125                                         /* action depends on mode */
01126                                         switch (mode) {
01127                                                 case DRIVERS_EDITMODE_ADD:
01128                                                 {
01129                                                         /* add a new driver with the information obtained (only if valid) */
01130                                                         ANIM_add_driver(reports, id, path, array_index, dflags, DRIVER_TYPE_PYTHON);
01131                                                 }
01132                                                         break;
01133                                                 case DRIVERS_EDITMODE_REMOVE:
01134                                                 {
01135                                                         /* remove driver matching the information obtained (only if valid) */
01136                                                         ANIM_remove_driver(reports, id, path, array_index, dflags);
01137                                                 }
01138                                                         break;
01139                                         }
01140                                 }
01141                                 
01142                                 /* free path, since it had to be generated */
01143                                 MEM_freeN(path);
01144                         }
01145                         
01146                         
01147                 }
01148                 
01149                 /* go over sub-tree */
01150                 if ((tselem->flag & TSE_CLOSED)==0)
01151                         do_outliner_drivers_editop(soops, &te->subtree, reports, mode);
01152         }
01153 }
01154 
01155 /* Add Operator ---------------------------------- */
01156 
01157 static int outliner_drivers_addsel_exec(bContext *C, wmOperator *op)
01158 {
01159         SpaceOops *soutliner= CTX_wm_space_outliner(C);
01160         
01161         /* check for invalid states */
01162         if (soutliner == NULL)
01163                 return OPERATOR_CANCELLED;
01164         
01165         /* recursively go into tree, adding selected items */
01166         do_outliner_drivers_editop(soutliner, &soutliner->tree, op->reports, DRIVERS_EDITMODE_ADD);
01167         
01168         /* send notifiers */
01169         WM_event_add_notifier(C, NC_ANIMATION|ND_FCURVES_ORDER, NULL); // XXX
01170         
01171         return OPERATOR_FINISHED;
01172 }
01173 
01174 void OUTLINER_OT_drivers_add_selected(wmOperatorType *ot)
01175 {
01176         /* api callbacks */
01177         ot->idname= "OUTLINER_OT_drivers_add_selected";
01178         ot->name= "Add Drivers for Selected";
01179         ot->description= "Add drivers to selected items";
01180         
01181         /* api callbacks */
01182         ot->exec= outliner_drivers_addsel_exec;
01183         ot->poll= ed_operator_outliner_datablocks_active;
01184         
01185         /* flags */
01186         ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
01187 }
01188 
01189 
01190 /* Remove Operator ---------------------------------- */
01191 
01192 static int outliner_drivers_deletesel_exec(bContext *C, wmOperator *op)
01193 {
01194         SpaceOops *soutliner= CTX_wm_space_outliner(C);
01195         
01196         /* check for invalid states */
01197         if (soutliner == NULL)
01198                 return OPERATOR_CANCELLED;
01199         
01200         /* recursively go into tree, adding selected items */
01201         do_outliner_drivers_editop(soutliner, &soutliner->tree, op->reports, DRIVERS_EDITMODE_REMOVE);
01202         
01203         /* send notifiers */
01204         WM_event_add_notifier(C, ND_KEYS, NULL); // XXX
01205         
01206         return OPERATOR_FINISHED;
01207 }
01208 
01209 void OUTLINER_OT_drivers_delete_selected(wmOperatorType *ot)
01210 {
01211         /* identifiers */
01212         ot->idname= "OUTLINER_OT_drivers_delete_selected";
01213         ot->name= "Delete Drivers for Selected";
01214         ot->description= "Delete drivers assigned to selected items";
01215         
01216         /* api callbacks */
01217         ot->exec= outliner_drivers_deletesel_exec;
01218         ot->poll= ed_operator_outliner_datablocks_active;
01219         
01220         /* flags */
01221         ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
01222 }
01223 
01224 /* =============================================== */
01225 /* Keying Set Operations */
01226 
01227 /* These operators are only available in databrowser mode for now, as
01228  * they depend on having RNA paths and/or hierarchies available.
01229  */
01230 enum {
01231         KEYINGSET_EDITMODE_ADD  = 0,
01232         KEYINGSET_EDITMODE_REMOVE,
01233 } /*eKeyingSet_EditModes*/;
01234 
01235 /* Utilities ---------------------------------- */ 
01236  
01237 /* find the 'active' KeyingSet, and add if not found (if adding is allowed) */
01238 // TODO: should this be an API func?
01239 static KeyingSet *verify_active_keyingset(Scene *scene, short add)
01240 {
01241         KeyingSet *ks= NULL;
01242         
01243         /* sanity check */
01244         if (scene == NULL)
01245                 return NULL;
01246         
01247         /* try to find one from scene */
01248         if (scene->active_keyingset > 0)
01249                 ks= BLI_findlink(&scene->keyingsets, scene->active_keyingset-1);
01250                 
01251         /* add if none found */
01252         // XXX the default settings have yet to evolve
01253         if ((add) && (ks==NULL)) {
01254                 ks= BKE_keyingset_add(&scene->keyingsets, NULL, KEYINGSET_ABSOLUTE, 0);
01255                 scene->active_keyingset= BLI_countlist(&scene->keyingsets);
01256         }
01257         
01258         return ks;
01259 }
01260 
01261 /* Recursively iterate over tree, finding and working on selected items */
01262 static void do_outliner_keyingset_editop(SpaceOops *soops, KeyingSet *ks, ListBase *tree, short mode)
01263 {
01264         TreeElement *te;
01265         TreeStoreElem *tselem;
01266         
01267         for (te= tree->first; te; te=te->next) {
01268                 tselem= TREESTORE(te);
01269                 
01270                 /* if item is selected, perform operation */
01271                 if (tselem->flag & TSE_SELECTED) {
01272                         ID *id= NULL;
01273                         char *path= NULL;
01274                         int array_index= 0;
01275                         short flag= 0;
01276                         short groupmode= KSP_GROUP_KSNAME;
01277                         
01278                         /* check if RNA-property described by this selected element is an animateable prop */
01279                         if (ELEM(tselem->type, TSE_RNA_PROPERTY, TSE_RNA_ARRAY_ELEM) && RNA_property_animateable(&te->rnaptr, te->directdata)) {
01280                                 /* get id + path + index info from the selected element */
01281                                 tree_element_to_path(soops, te, tselem, 
01282                                                 &id, &path, &array_index, &flag, &groupmode);
01283                         }
01284                         
01285                         /* only if ID and path were set, should we perform any actions */
01286                         if (id && path) {
01287                                 /* action depends on mode */
01288                                 switch (mode) {
01289                                         case KEYINGSET_EDITMODE_ADD:
01290                                         {
01291                                                 /* add a new path with the information obtained (only if valid) */
01292                                                 // TODO: what do we do with group name? for now, we don't supply one, and just let this use the KeyingSet name
01293                                                 BKE_keyingset_add_path(ks, id, NULL, path, array_index, flag, groupmode);
01294                                                 ks->active_path= BLI_countlist(&ks->paths);
01295                                         }
01296                                                 break;
01297                                         case KEYINGSET_EDITMODE_REMOVE:
01298                                         {
01299                                                 /* find the relevant path, then remove it from the KeyingSet */
01300                                                 KS_Path *ksp= BKE_keyingset_find_path(ks, id, NULL, path, array_index, groupmode);
01301                                                 
01302                                                 if (ksp) {
01303                                                         /* free path's data */
01304                                                         BKE_keyingset_free_path(ks, ksp);
01305 
01306                                                         ks->active_path= 0;
01307                                                 }
01308                                         }
01309                                                 break;
01310                                 }
01311                                 
01312                                 /* free path, since it had to be generated */
01313                                 MEM_freeN(path);
01314                         }
01315                 }
01316                 
01317                 /* go over sub-tree */
01318                 if ((tselem->flag & TSE_CLOSED)==0)
01319                         do_outliner_keyingset_editop(soops, ks, &te->subtree, mode);
01320         }
01321 }
01322 
01323 /* Add Operator ---------------------------------- */
01324 
01325 static int outliner_keyingset_additems_exec(bContext *C, wmOperator *op)
01326 {
01327         SpaceOops *soutliner= CTX_wm_space_outliner(C);
01328         Scene *scene= CTX_data_scene(C);
01329         KeyingSet *ks= verify_active_keyingset(scene, 1);
01330         
01331         /* check for invalid states */
01332         if (ks == NULL) {
01333                 BKE_report(op->reports, RPT_ERROR, "Operation requires an Active Keying Set");
01334                 return OPERATOR_CANCELLED;
01335         }
01336         if (soutliner == NULL)
01337                 return OPERATOR_CANCELLED;
01338         
01339         /* recursively go into tree, adding selected items */
01340         do_outliner_keyingset_editop(soutliner, ks, &soutliner->tree, KEYINGSET_EDITMODE_ADD);
01341         
01342         /* send notifiers */
01343         WM_event_add_notifier(C, NC_SCENE|ND_KEYINGSET, NULL);
01344         
01345         return OPERATOR_FINISHED;
01346 }
01347 
01348 void OUTLINER_OT_keyingset_add_selected(wmOperatorType *ot)
01349 {
01350         /* identifiers */
01351         ot->idname= "OUTLINER_OT_keyingset_add_selected";
01352         ot->name= "Keying Set Add Selected";
01353         ot->description= "Add selected items (blue-grey rows) to active Keying Set";
01354         
01355         /* api callbacks */
01356         ot->exec= outliner_keyingset_additems_exec;
01357         ot->poll= ed_operator_outliner_datablocks_active;
01358         
01359         /* flags */
01360         ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
01361 }
01362 
01363 
01364 /* Remove Operator ---------------------------------- */
01365 
01366 static int outliner_keyingset_removeitems_exec(bContext *C, wmOperator *UNUSED(op))
01367 {
01368         SpaceOops *soutliner= CTX_wm_space_outliner(C);
01369         Scene *scene= CTX_data_scene(C);
01370         KeyingSet *ks= verify_active_keyingset(scene, 1);
01371         
01372         /* check for invalid states */
01373         if (soutliner == NULL)
01374                 return OPERATOR_CANCELLED;
01375         
01376         /* recursively go into tree, adding selected items */
01377         do_outliner_keyingset_editop(soutliner, ks, &soutliner->tree, KEYINGSET_EDITMODE_REMOVE);
01378         
01379         /* send notifiers */
01380         WM_event_add_notifier(C, NC_SCENE|ND_KEYINGSET, NULL);
01381         
01382         return OPERATOR_FINISHED;
01383 }
01384 
01385 void OUTLINER_OT_keyingset_remove_selected(wmOperatorType *ot)
01386 {
01387         /* identifiers */
01388         ot->idname= "OUTLINER_OT_keyingset_remove_selected";
01389         ot->name= "Keying Set Remove Selected";
01390         ot->description = "Remove selected items (blue-grey rows) from active Keying Set";
01391         
01392         /* api callbacks */
01393         ot->exec= outliner_keyingset_removeitems_exec;
01394         ot->poll= ed_operator_outliner_datablocks_active;
01395         
01396         /* flags */
01397         ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
01398 }