|
Blender
V2.59
|
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 }