/* Used for drag and drop parenting */ TreeElement *outliner_dropzone_parent(bContext *C, wmEvent *event, TreeElement *te, float *fmval) { SpaceOops *soops = CTX_wm_space_outliner(C); TreeStoreElem *tselem = TREESTORE(te); if ((fmval[1] > te->ys) && (fmval[1] < (te->ys + UI_UNIT_Y))) { /* name and first icon */ if ((fmval[0] > te->xs + UI_UNIT_X) && (fmval[0] < te->xend)) { /* always makes active object */ if (te->idcode == ID_OB) { return te; } else { return NULL; } } } /* Not it. Let's look at its children. */ if ((tselem->flag & TSE_CLOSED) == 0 && (te->subtree.first)) { for (te = te->subtree.first; te; te = te->next) { TreeElement *te_valid; te_valid = outliner_dropzone_parent(C, event, te, fmval); if (te_valid) return te_valid; } } return NULL; }
static int do_outliner_item_openclose(bContext *C, SpaceOops *soops, TreeElement *te, int all, const float mval[2]) { if(mval[1]>te->ys && mval[1]<te->ys+UI_UNIT_Y) { TreeStoreElem *tselem= TREESTORE(te); /* all below close/open? */ if(all) { tselem->flag &= ~TSE_CLOSED; outliner_set_flag(soops, &te->subtree, TSE_CLOSED, !outliner_has_one_flag(soops, &te->subtree, TSE_CLOSED, 1)); } else { if(tselem->flag & TSE_CLOSED) tselem->flag &= ~TSE_CLOSED; else tselem->flag |= TSE_CLOSED; } return 1; } for(te= te->subtree.first; te; te= te->next) { if(do_outliner_item_openclose(C, soops, te, all, mval)) return 1; } return 0; }
static int tree_element_active_world(bContext *C, Scene *scene, SpaceOops *soops, TreeElement *te, int set) { TreeElement *tep; TreeStoreElem *tselem=NULL; Scene *sce=NULL; tep= te->parent; if(tep) { tselem= TREESTORE(tep); sce= (Scene *)tselem->id; } if(set) { // make new scene active if(sce && scene != sce) { ED_screen_set_scene(C, sce); } } if(tep==NULL || tselem->id == (ID *)scene) { if(set) { // XXX extern_set_butspace(F8KEY, 0); } else { return 1; } } return 0; }
void outliner_do_object_operation(bContext *C, Scene *scene_act, SpaceOops *soops, ListBase *lb, void (*operation_cb)(bContext *C, Scene *scene, TreeElement *, TreeStoreElem *, TreeStoreElem *)) { TreeElement *te; TreeStoreElem *tselem; for (te = lb->first; te; te = te->next) { tselem = TREESTORE(te); if (tselem->flag & TSE_SELECTED) { if (tselem->type == 0 && te->idcode == ID_OB) { // when objects selected in other scenes... dunno if that should be allowed Scene *scene_owner = (Scene *)outliner_search_back(soops, te, ID_SCE); if (scene_owner && scene_act != scene_owner) { ED_screen_set_scene(C, CTX_wm_screen(C), scene_owner); } /* important to use 'scene_owner' not scene_act else deleting objects can crash. * only use 'scene_act' when 'scene_owner' is NULL, which can happen when the * outliner isn't showing scenes: Visible Layer draw mode for eg. */ operation_cb(C, scene_owner ? scene_owner : scene_act, te, NULL, tselem); } } if (TSELEM_OPEN(tselem, soops)) { outliner_do_object_operation(C, scene_act, soops, &te->subtree, operation_cb); } } }
/* Recursively iterate over tree, finding and working on selected items */ static void do_outliner_keyingset_editop(SpaceOops *soops, KeyingSet *ks, ListBase *tree, short mode) { TreeElement *te; TreeStoreElem *tselem; for (te= tree->first; te; te=te->next) { tselem= TREESTORE(te); /* if item is selected, perform operation */ if (tselem->flag & TSE_SELECTED) { ID *id= NULL; char *path= NULL; int array_index= 0; short flag= 0; short groupmode= KSP_GROUP_KSNAME; /* check if RNA-property described by this selected element is an animateable prop */ if (ELEM(tselem->type, TSE_RNA_PROPERTY, TSE_RNA_ARRAY_ELEM) && RNA_property_animateable(&te->rnaptr, te->directdata)) { /* get id + path + index info from the selected element */ tree_element_to_path(soops, te, tselem, &id, &path, &array_index, &flag, &groupmode); } /* only if ID and path were set, should we perform any actions */ if (id && path) { /* action depends on mode */ switch (mode) { case KEYINGSET_EDITMODE_ADD: { /* add a new path with the information obtained (only if valid) */ // TODO: what do we do with group name? for now, we don't supply one, and just let this use the KeyingSet name BKE_keyingset_add_path(ks, id, NULL, path, array_index, flag, groupmode); ks->active_path= BLI_countlist(&ks->paths); } break; case KEYINGSET_EDITMODE_REMOVE: { /* find the relevant path, then remove it from the KeyingSet */ KS_Path *ksp= BKE_keyingset_find_path(ks, id, NULL, path, array_index, groupmode); if (ksp) { /* free path's data */ BKE_keyingset_free_path(ks, ksp); ks->active_path= 0; } } break; } /* free path, since it had to be generated */ MEM_freeN(path); } } /* go over sub-tree */ if ((tselem->flag & TSE_CLOSED)==0) do_outliner_keyingset_editop(soops, ks, &te->subtree, mode); } }
static int tree_element_set_active_object(bContext *C, Scene *scene, SpaceOops *soops, TreeElement *te, int set, bool recursive) { TreeStoreElem *tselem = TREESTORE(te); Scene *sce; Base *base; Object *ob = NULL; /* if id is not object, we search back */ if (te->idcode == ID_OB) { ob = (Object *)tselem->id; } else { ob = (Object *)outliner_search_back(soops, te, ID_OB); if (ob == OBACT) return 0; } if (ob == NULL) return 0; sce = (Scene *)outliner_search_back(soops, te, ID_SCE); if (sce && scene != sce) { ED_screen_set_scene(C, CTX_wm_screen(C), sce); scene = sce; } /* find associated base in current scene */ base = BKE_scene_base_find(scene, ob); if (base) { if (set == 2) { /* swap select */ if (base->flag & SELECT) ED_base_object_select(base, BA_DESELECT); else ED_base_object_select(base, BA_SELECT); } else { /* deleselect all */ BKE_scene_base_deselect_all(scene); ED_base_object_select(base, BA_SELECT); } if (recursive) { /* Recursive select/deselect for Object hierarchies */ do_outliner_object_select_recursive(scene, ob, (ob->flag & SELECT) != 0); } if (C) { ED_base_object_activate(C, base); /* adds notifier */ WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene); } } if (ob != scene->obedit) ED_object_editmode_exit(C, EM_FREEDATA | EM_FREEUNDO | EM_WAITCURSOR | EM_DO_UNDO); return 1; }
static void outliner_do_libdata_operation(bContext *C, Scene *scene, SpaceOops *soops, ListBase *lb, void (*operation_cb)(bContext *C, Scene *scene, TreeElement *, TreeStoreElem *, TreeStoreElem *)) { TreeElement *te; TreeStoreElem *tselem; for (te=lb->first; te; te= te->next) { tselem= TREESTORE(te); if (tselem->flag & TSE_SELECTED) { if (tselem->type==0) { TreeStoreElem *tsep= TREESTORE(te->parent); operation_cb(C, scene, te, tsep, tselem); } } if (TSELEM_OPEN(tselem,soops)) { outliner_do_libdata_operation(C, scene, soops, &te->subtree, operation_cb); } } }
static void outliner_do_id_set_operation(SpaceOops *soops, int type, ListBase *lb, ID *newid, void (*operation_cb)(TreeElement *, TreeStoreElem *, TreeStoreElem *, ID *)) { TreeElement *te; TreeStoreElem *tselem; for (te = lb->first; te; te = te->next) { tselem = TREESTORE(te); if (tselem->flag & TSE_SELECTED) { if (tselem->type == type) { TreeStoreElem *tsep = te->parent ? TREESTORE(te->parent) : NULL; operation_cb(te, tselem, tsep, newid); } } if (TSELEM_OPEN(tselem, soops)) { outliner_do_id_set_operation(soops, type, &te->subtree, newid, operation_cb); } } }
ID *outliner_search_back(SpaceOops *UNUSED(soops), TreeElement *te, short idcode) { TreeStoreElem *tselem; te = te->parent; while (te) { tselem = TREESTORE(te); if (tselem->type == 0 && te->idcode == idcode) return tselem->id; te = te->parent; } return NULL; }
void outliner_set_flag(SpaceOops *soops, ListBase *lb, short flag, short set) { TreeElement *te; TreeStoreElem *tselem; for(te= lb->first; te; te= te->next) { tselem= TREESTORE(te); if(set==0) tselem->flag &= ~flag; else tselem->flag |= flag; outliner_set_flag(soops, &te->subtree, flag, set); } }
/* helper function for tree_element_shwo_hierarchy() - recursively checks whether subtrees have any objects*/ static int subtree_has_objects(SpaceOops *soops, ListBase *lb) { TreeElement *te; TreeStoreElem *tselem; for(te= lb->first; te; te= te->next) { tselem= TREESTORE(te); if(tselem->type==0 && te->idcode==ID_OB) return 1; if( subtree_has_objects(soops, &te->subtree)) return 1; } return 0; }
/* return 1 when levels were opened */ static int outliner_open_back(SpaceOops *soops, TreeElement *te) { TreeStoreElem *tselem; int retval= 0; for (te= te->parent; te; te= te->parent) { tselem= TREESTORE(te); if (tselem->flag & TSE_CLOSED) { tselem->flag &= ~TSE_CLOSED; retval= 1; } } return retval; }
int outliner_has_one_flag(SpaceOops *soops, ListBase *lb, short flag, short curlevel) { TreeElement *te; TreeStoreElem *tselem; int level; for(te= lb->first; te; te= te->next) { tselem= TREESTORE(te); if(tselem->flag & flag) return curlevel; level= outliner_has_one_flag(soops, &te->subtree, flag, curlevel+1); if(level) return level; } return 0; }
/* recursive helper for function below */ static void outliner_set_coordinates_element(SpaceOops *soops, TreeElement *te, int startx, int *starty) { TreeStoreElem *tselem= TREESTORE(te); /* store coord and continue, we need coordinates for elements outside view too */ te->xs= (float)startx; te->ys= (float)(*starty); *starty-= UI_UNIT_Y; if((tselem->flag & TSE_CLOSED)==0) { TreeElement *ten; for(ten= te->subtree.first; ten; ten= ten->next) { outliner_set_coordinates_element(soops, ten, startx+UI_UNIT_X, starty); } } }
static int outliner_select(SpaceOops *soops, ListBase *lb, int *index, short *selecting) { TreeElement *te; TreeStoreElem *tselem; int change= 0; for (te= lb->first; te && *index >= 0; te=te->next, (*index)--) { tselem= TREESTORE(te); /* if we've encountered the right item, set its 'Outliner' selection status */ if (*index == 0) { /* this should be the last one, so no need to do anything with index */ if ((te->flag & TE_ICONROW)==0) { /* -1 value means toggle testing for now... */ if (*selecting == -1) { if (tselem->flag & TSE_SELECTED) *selecting= 0; else *selecting= 1; } /* set selection */ if (*selecting) tselem->flag |= TSE_SELECTED; else tselem->flag &= ~TSE_SELECTED; change |= 1; } } else if (TSELEM_OPEN(tselem,soops)) { /* Only try selecting sub-elements if we haven't hit the right element yet * * Hack warning: * Index must be reduced before supplying it to the sub-tree to try to do * selection, however, we need to increment it again for the next loop to * function correctly */ (*index)--; change |= outliner_select(soops, &te->subtree, index, selecting); (*index)++; } } return change; }
static void set_operation_types(SpaceOops *soops, ListBase *lb, int *scenelevel, int *objectlevel, int *idlevel, int *datalevel) { TreeElement *te; TreeStoreElem *tselem; for (te = lb->first; te; te = te->next) { tselem = TREESTORE(te); if (tselem->flag & TSE_SELECTED) { if (tselem->type) { if (*datalevel == 0) *datalevel = tselem->type; else if (*datalevel != tselem->type) *datalevel = -1; } else { int idcode = GS(tselem->id->name); switch (idcode) { case ID_SCE: *scenelevel = 1; break; case ID_OB: *objectlevel = 1; break; case ID_ME: case ID_CU: case ID_MB: case ID_LT: case ID_LA: case ID_AR: case ID_CA: case ID_SPK: case ID_MA: case ID_TE: case ID_IP: case ID_IM: case ID_SO: case ID_KE: case ID_WO: case ID_AC: case ID_NLA: case ID_TXT: case ID_GR: if (*idlevel == 0) *idlevel = idcode; else if (*idlevel != idcode) *idlevel = -1; break; } } } if (TSELEM_OPEN(tselem, soops)) { set_operation_types(soops, &te->subtree, scenelevel, objectlevel, idlevel, datalevel); } } }
/* helper function for Show/Hide one level operator */ static void outliner_openclose_level(SpaceOops *soops, ListBase *lb, int curlevel, int level, int open) { TreeElement *te; TreeStoreElem *tselem; for(te= lb->first; te; te= te->next) { tselem= TREESTORE(te); if(open) { if(curlevel<=level) tselem->flag &= ~TSE_CLOSED; } else { if(curlevel>=level) tselem->flag |= TSE_CLOSED; } outliner_openclose_level(soops, &te->subtree, curlevel+1, level, open); } }
/* Find treestore that refers to given ID */ TreeElement *outliner_find_id(SpaceOops *soops, ListBase *lb, ID *id) { TreeElement *te, *tes; TreeStoreElem *tselem; for (te = lb->first; te; te = te->next) { tselem = TREESTORE(te); if (tselem->type == 0) { if (tselem->id == id) return te; /* only deeper on scene or object */ if (te->idcode == ID_OB || te->idcode == ID_SCE || (soops->outlinevis == SO_GROUPS && te->idcode == ID_GR)) { tes = outliner_find_id(soops, &te->subtree, id); if (tes) return tes; } } } return NULL; }
static void outliner_do_data_operation(SpaceOops *soops, int type, int event, ListBase *lb, void (*operation_cb)(int, TreeElement *, TreeStoreElem *)) { TreeElement *te; TreeStoreElem *tselem; for (te=lb->first; te; te= te->next) { tselem= TREESTORE(te); if (tselem->flag & TSE_SELECTED) { if (tselem->type==type) { operation_cb(event, te, tselem); } } if (TSELEM_OPEN(tselem,soops)) { outliner_do_data_operation(soops, type, event, &te->subtree, operation_cb); } } }
static int do_outliner_item_rename(bContext *C, ARegion *ar, SpaceOops *soops, TreeElement *te, const float mval[2]) { ReportList *reports= CTX_wm_reports(C); // XXX if(mval[1]>te->ys && mval[1]<te->ys+UI_UNIT_Y) { TreeStoreElem *tselem= TREESTORE(te); /* name and first icon */ if(mval[0]>te->xs+UI_UNIT_X && mval[0]<te->xend) { do_item_rename(ar, te, tselem, reports) ; } return 1; } for(te= te->subtree.first; te; te= te->next) { if(do_outliner_item_rename(C, ar, soops, te, mval)) return 1; } return 0; }
/* **************** Border Select Tool ****************** */ static void outliner_item_border_select(Scene *scene, SpaceOops *soops, rctf *rectf, TreeElement *te, int gesture_mode) { TreeStoreElem *tselem= TREESTORE(te); if (te->ys <= rectf->ymax && te->ys + UI_UNIT_Y >= rectf->ymin) { if (gesture_mode == GESTURE_MODAL_SELECT) { tselem->flag |= TSE_SELECTED; } else { tselem->flag &= ~TSE_SELECTED; } } /* Look at its children. */ if ((tselem->flag & TSE_CLOSED) == 0) { for (te = te->subtree.first; te; te = te->next) { outliner_item_border_select(scene, soops, rectf, te, gesture_mode); } } return; }
static void outliner_open_reveal(SpaceOops *soops, ListBase *lb, TreeElement *teFind, int *found) { TreeElement *te; TreeStoreElem *tselem; for (te= lb->first; te; te= te->next) { /* check if this tree-element was the one we're seeking */ if (te == teFind) { *found= 1; return; } /* try to see if sub-tree contains it then */ outliner_open_reveal(soops, &te->subtree, teFind, found); if (*found) { tselem= TREESTORE(te); if (tselem->flag & TSE_CLOSED) tselem->flag &= ~TSE_CLOSED; return; } } }
int outliner_dropzone_parent_clear(bContext *C, wmEvent *event, TreeElement *te, float *fmval) { SpaceOops *soops = CTX_wm_space_outliner(C); TreeStoreElem *tselem = TREESTORE(te); /* Check for row */ if ((fmval[1] > te->ys) && (fmval[1] < (te->ys + UI_UNIT_Y))) { /* Ignore drop on scene tree elements */ if ((fmval[0] > te->xs + UI_UNIT_X) && (fmval[0] < te->xend)) { if ((te->idcode == ID_SCE) && !ELEM3(tselem->type, TSE_R_LAYER_BASE, TSE_R_LAYER, TSE_R_PASS)) { return 0; } // Other codes to ignore? } /* Left or right of: (+), first icon, and name */ if ((fmval[0] < (te->xs + UI_UNIT_X)) || (fmval[0] > te->xend)) { return 1; } else if (te->idcode != ID_OB) { return 1; } return 0; // ID_OB, but mouse in undefined dropzone. } /* Not this row. Let's look at its children. */ if ((tselem->flag & TSE_CLOSED) == 0 && (te->subtree.first)) { for (te = te->subtree.first; te; te = te->next) { if (outliner_dropzone_parent_clear(C, event, te, fmval)) return 1; } } return 0; }
/* recursive helper function for Show Hierarchy operator */ static void tree_element_show_hierarchy(Scene *scene, SpaceOops *soops, ListBase *lb) { TreeElement *te; TreeStoreElem *tselem; /* open all object elems, close others */ for(te= lb->first; te; te= te->next) { tselem= TREESTORE(te); if(tselem->type==0) { if(te->idcode==ID_SCE) { if(tselem->id!=(ID *)scene) tselem->flag |= TSE_CLOSED; else tselem->flag &= ~TSE_CLOSED; } else if(te->idcode==ID_OB) { if(subtree_has_objects(soops, &te->subtree)) tselem->flag &= ~TSE_CLOSED; else tselem->flag |= TSE_CLOSED; } } else tselem->flag |= TSE_CLOSED; if(tselem->flag & TSE_CLOSED); else tree_element_show_hierarchy(scene, soops, &te->subtree); } }
static void constraint_cb(int event, TreeElement *te, TreeStoreElem *UNUSED(tselem), void *C_v) { bContext *C = C_v; SpaceOops *soops = CTX_wm_space_outliner(C); bConstraint *constraint = (bConstraint *)te->directdata; Object *ob = (Object *)outliner_search_back(soops, te, ID_OB); if (event == OL_CONSTRAINTOP_ENABLE) { constraint->flag &= ~CONSTRAINT_OFF; ED_object_constraint_update(ob); WM_event_add_notifier(C, NC_OBJECT | ND_CONSTRAINT, ob); } else if (event == OL_CONSTRAINTOP_DISABLE) { constraint->flag = CONSTRAINT_OFF; ED_object_constraint_update(ob); WM_event_add_notifier(C, NC_OBJECT | ND_CONSTRAINT, ob); } else if (event == OL_CONSTRAINTOP_DELETE) { ListBase *lb = NULL; if (TREESTORE(te->parent->parent)->type == TSE_POSE_CHANNEL) { lb = &((bPoseChannel *)te->parent->parent->directdata)->constraints; } else { lb = &ob->constraints; } if (BKE_constraint_remove_ex(lb, ob, constraint, true)) { /* there's no active constraint now, so make sure this is the case */ BKE_constraints_active_set(&ob->constraints, NULL); ED_object_constraint_update(ob); /* needed to set the flags on posebones correctly */ WM_event_add_notifier(C, NC_OBJECT | ND_CONSTRAINT | NA_REMOVED, ob); te->store_elem->flag &= ~TSE_SELECTED; } } }
static int do_outliner_operation_event(bContext *C, Scene *scene, ARegion *ar, SpaceOops *soops, TreeElement *te, const wmEvent *event, const float mval[2]) { ReportList *reports = CTX_wm_reports(C); // XXX... if (mval[1] > te->ys && mval[1] < te->ys + UI_UNIT_Y) { int scenelevel = 0, objectlevel = 0, idlevel = 0, datalevel = 0; TreeStoreElem *tselem = TREESTORE(te); /* select object that's clicked on and popup context menu */ if (!(tselem->flag & TSE_SELECTED)) { if (outliner_has_one_flag(soops, &soops->tree, TSE_SELECTED, 1) ) outliner_set_flag(soops, &soops->tree, TSE_SELECTED, 0); tselem->flag |= TSE_SELECTED; /* redraw, same as outliner_select function */ soops->storeflag |= SO_TREESTORE_REDRAW; ED_region_tag_redraw(ar); } set_operation_types(soops, &soops->tree, &scenelevel, &objectlevel, &idlevel, &datalevel); if (scenelevel) { //if (objectlevel || datalevel || idlevel) error("Mixed selection"); //else pupmenu("Scene Operations%t|Delete"); } else if (objectlevel) { WM_operator_name_call(C, "OUTLINER_OT_object_operation", WM_OP_INVOKE_REGION_WIN, NULL); } else if (idlevel) { if (idlevel == -1 || datalevel) { BKE_report(reports, RPT_WARNING, "Mixed selection"); } else { if (idlevel == ID_GR) WM_operator_name_call(C, "OUTLINER_OT_group_operation", WM_OP_INVOKE_REGION_WIN, NULL); else WM_operator_name_call(C, "OUTLINER_OT_id_operation", WM_OP_INVOKE_REGION_WIN, NULL); } } else if (datalevel) { if (datalevel == -1) { BKE_report(reports, RPT_WARNING, "Mixed selection"); } else { if (datalevel == TSE_ANIM_DATA) WM_operator_name_call(C, "OUTLINER_OT_animdata_operation", WM_OP_INVOKE_REGION_WIN, NULL); else if (datalevel == TSE_DRIVER_BASE) { /* do nothing... no special ops needed yet */ } else if (ELEM3(datalevel, TSE_R_LAYER_BASE, TSE_R_LAYER, TSE_R_PASS)) { /*WM_operator_name_call(C, "OUTLINER_OT_renderdata_operation", WM_OP_INVOKE_REGION_WIN, NULL)*/ } else { WM_operator_name_call(C, "OUTLINER_OT_data_operation", WM_OP_INVOKE_REGION_WIN, NULL); } } } return 1; } for (te = te->subtree.first; te; te = te->next) { if (do_outliner_operation_event(C, scene, ar, soops, te, event, mval)) return 1; } return 0; }
/* Helper func to extract an RNA path from selected tree element * NOTE: the caller must zero-out all values of the pointers that it passes here first, as * this function does not do that yet */ static void tree_element_to_path(SpaceOops *soops, TreeElement *te, TreeStoreElem *tselem, ID **id, char **path, int *array_index, short *flag, short *UNUSED(groupmode)) { ListBase hierarchy = {NULL, NULL}; LinkData *ld; TreeElement *tem, *temnext, *temsub; TreeStoreElem *tse, *tsenext; PointerRNA *ptr, *nextptr; PropertyRNA *prop; char *newpath=NULL; /* optimise tricks: * - Don't do anything if the selected item is a 'struct', but arrays are allowed */ if (tselem->type == TSE_RNA_STRUCT) return; /* Overview of Algorithm: * 1. Go up the chain of parents until we find the 'root', taking note of the * levels encountered in reverse-order (i.e. items are added to the start of the list * for more convenient looping later) * 2. Walk down the chain, adding from the first ID encountered * (which will become the 'ID' for the KeyingSet Path), and build a * path as we step through the chain */ /* step 1: flatten out hierarchy of parents into a flat chain */ for (tem= te->parent; tem; tem= tem->parent) { ld= MEM_callocN(sizeof(LinkData), "LinkData for tree_element_to_path()"); ld->data= tem; BLI_addhead(&hierarchy, ld); } /* step 2: step down hierarchy building the path (NOTE: addhead in previous loop was needed so that we can loop like this) */ for (ld= hierarchy.first; ld; ld= ld->next) { /* get data */ tem= (TreeElement *)ld->data; tse= TREESTORE(tem); ptr= &tem->rnaptr; prop= tem->directdata; /* check if we're looking for first ID, or appending to path */ if (*id) { /* just 'append' property to path * - to prevent memory leaks, we must write to newpath not path, then free old path + swap them */ if(tse->type == TSE_RNA_PROPERTY) { if(RNA_property_type(prop) == PROP_POINTER) { /* for pointer we just append property name */ newpath= RNA_path_append(*path, ptr, prop, 0, NULL); } else if(RNA_property_type(prop) == PROP_COLLECTION) { char buf[128], *name; temnext= (TreeElement*)(ld->next->data); tsenext= TREESTORE(temnext); nextptr= &temnext->rnaptr; name= RNA_struct_name_get_alloc(nextptr, buf, sizeof(buf)); if(name) { /* if possible, use name as a key in the path */ newpath= RNA_path_append(*path, NULL, prop, 0, name); if(name != buf) MEM_freeN(name); } else { /* otherwise use index */ int index= 0; for(temsub=tem->subtree.first; temsub; temsub=temsub->next, index++) if(temsub == temnext) break; newpath= RNA_path_append(*path, NULL, prop, index, NULL); } ld= ld->next; } } if(newpath) { if (*path) MEM_freeN(*path); *path= newpath; newpath= NULL; } } else { /* no ID, so check if entry is RNA-struct, and if that RNA-struct is an ID datablock to extract info from */ if (tse->type == TSE_RNA_STRUCT) { /* ptr->data not ptr->id.data seems to be the one we want, since ptr->data is sometimes the owner of this ID? */ if(RNA_struct_is_ID(ptr->type)) { *id= (ID *)ptr->data; /* clear path */ if(*path) { MEM_freeN(*path); path= NULL; } } } } } /* step 3: if we've got an ID, add the current item to the path */ if (*id) { /* add the active property to the path */ ptr= &te->rnaptr; prop= te->directdata; /* array checks */ if (tselem->type == TSE_RNA_ARRAY_ELEM) { /* item is part of an array, so must set the array_index */ *array_index= te->index; } else if (RNA_property_array_length(ptr, prop)) { /* entire array was selected, so keyframe all */ *flag |= KSP_FLAG_WHOLE_ARRAY; } /* path */ newpath= RNA_path_append(*path, NULL, prop, 0, NULL); if (*path) MEM_freeN(*path); *path= newpath; } /* free temp data */ BLI_freelistN(&hierarchy); }
// TODO: this function needs to be split up! It's getting a bit too large... // Note: "ID" is not always a real ID static TreeElement *outliner_add_element(SpaceOops *soops, ListBase *lb, void *idv, TreeElement *parent, short type, short index) { TreeElement *te; TreeStoreElem *tselem; ID *id = idv; if (ELEM3(type, TSE_RNA_STRUCT, TSE_RNA_PROPERTY, TSE_RNA_ARRAY_ELEM)) { id = ((PointerRNA *)idv)->id.data; if (!id) id = ((PointerRNA *)idv)->data; } /* One exception */ if (type == TSE_ID_BASE) { /* pass */ } else if (id == NULL) { return NULL; } te = MEM_callocN(sizeof(TreeElement), "tree elem"); /* add to the visual tree */ BLI_addtail(lb, te); /* add to the storage */ check_persistent(soops, te, id, type, index); tselem = TREESTORE(te); /* if we are searching for something expand to see child elements */ if (SEARCHING_OUTLINER(soops)) tselem->flag |= TSE_CHILDSEARCH; te->parent = parent; te->index = index; // for data arays if (ELEM3(type, TSE_SEQUENCE, TSE_SEQ_STRIP, TSE_SEQUENCE_DUP)) { /* pass */ } else if (ELEM3(type, TSE_RNA_STRUCT, TSE_RNA_PROPERTY, TSE_RNA_ARRAY_ELEM)) { /* pass */ } else if (type == TSE_ANIM_DATA) { /* pass */ } else if (type == TSE_ID_BASE) { /* pass */ } else { /* do here too, for blend file viewer, own ID_LI then shows file name */ if (GS(id->name) == ID_LI) te->name = ((Library *)id)->name; else te->name = id->name + 2; // default, can be overridden by Library or non-ID data te->idcode = GS(id->name); } if (type == 0) { TreeStoreElem *tsepar = parent ? TREESTORE(parent) : NULL; /* ID datablock */ if (tsepar == NULL || tsepar->type != TSE_ID_BASE) outliner_add_id_contents(soops, te, tselem, id); } else if (type == TSE_ANIM_DATA) { IdAdtTemplate *iat = (IdAdtTemplate *)idv; AnimData *adt = (AnimData *)iat->adt; /* this element's info */ te->name = IFACE_("Animation"); te->directdata = adt; /* Action */ outliner_add_element(soops, &te->subtree, adt->action, te, 0, 0); /* Drivers */ if (adt->drivers.first) { TreeElement *ted = outliner_add_element(soops, &te->subtree, adt, te, TSE_DRIVER_BASE, 0); ID *lastadded = NULL; FCurve *fcu; ted->name = IFACE_("Drivers"); for (fcu = adt->drivers.first; fcu; fcu = fcu->next) { if (fcu->driver && fcu->driver->variables.first) { ChannelDriver *driver = fcu->driver; DriverVar *dvar; for (dvar = driver->variables.first; dvar; dvar = dvar->next) { /* loop over all targets used here */ DRIVER_TARGETS_USED_LOOPER(dvar) { if (lastadded != dtar->id) { // XXX this lastadded check is rather lame, and also fails quite badly... outliner_add_element(soops, &ted->subtree, dtar->id, ted, TSE_LINKED_OB, 0); lastadded = dtar->id; } } DRIVER_TARGETS_LOOPER_END } } }
static void outliner_find_panel(Scene *UNUSED(scene), ARegion *ar, SpaceOops *soops, int again, int flags) { ReportList *reports = NULL; // CTX_wm_reports(C); TreeElement *te= NULL; TreeElement *last_find; TreeStoreElem *tselem; int ytop, xdelta, prevFound=0; char name[32]; /* get last found tree-element based on stored search_tse */ last_find= outliner_find_tse(soops, &soops->search_tse); /* determine which type of search to do */ if (again && last_find) { /* no popup panel - previous + user wanted to search for next after previous */ BLI_strncpy(name, soops->search_string, sizeof(name)); flags= soops->search_flags; /* try to find matching element */ te= outliner_find_named(soops, &soops->tree, name, flags, last_find, &prevFound); if (te==NULL) { /* no more matches after previous, start from beginning again */ prevFound= 1; te= outliner_find_named(soops, &soops->tree, name, flags, last_find, &prevFound); } } else { /* pop up panel - no previous, or user didn't want search after previous */ strcpy(name, ""); // XXX if (sbutton(name, 0, sizeof(name)-1, "Find: ") && name[0]) { // te= outliner_find_named(soops, &soops->tree, name, flags, NULL, &prevFound); // } // else return; /* XXX RETURN! XXX */ } /* do selection and reveal */ if (te) { tselem= TREESTORE(te); if (tselem) { /* expand branches so that it will be visible, we need to get correct coordinates */ if( outliner_open_back(soops, te)) outliner_set_coordinates(ar, soops); /* deselect all visible, and select found element */ outliner_set_flag(soops, &soops->tree, TSE_SELECTED, 0); tselem->flag |= TSE_SELECTED; /* make te->ys center of view */ ytop= (int)(te->ys + (ar->v2d.mask.ymax-ar->v2d.mask.ymin)/2); if(ytop>0) ytop= 0; ar->v2d.cur.ymax= (float)ytop; ar->v2d.cur.ymin= (float)(ytop-(ar->v2d.mask.ymax-ar->v2d.mask.ymin)); /* make te->xs ==> te->xend center of view */ xdelta = (int)(te->xs - ar->v2d.cur.xmin); ar->v2d.cur.xmin += xdelta; ar->v2d.cur.xmax += xdelta; /* store selection */ soops->search_tse= *tselem; BLI_strncpy(soops->search_string, name, 33); soops->search_flags= flags; /* redraw */ soops->storeflag |= SO_TREESTORE_REDRAW; } } else { /* no tree-element found */ BKE_report(reports, RPT_WARNING, "Not found: %s", name); } }
/* Recursively iterate over tree, finding and working on selected items */ static void do_outliner_drivers_editop(SpaceOops *soops, ListBase *tree, ReportList *reports, short mode) { TreeElement *te; TreeStoreElem *tselem; for (te= tree->first; te; te=te->next) { tselem= TREESTORE(te); /* if item is selected, perform operation */ if (tselem->flag & TSE_SELECTED) { ID *id= NULL; char *path= NULL; int array_index= 0; short flag= 0; short groupmode= KSP_GROUP_KSNAME; /* check if RNA-property described by this selected element is an animateable prop */ if (ELEM(tselem->type, TSE_RNA_PROPERTY, TSE_RNA_ARRAY_ELEM) && RNA_property_animateable(&te->rnaptr, te->directdata)) { /* get id + path + index info from the selected element */ tree_element_to_path(soops, te, tselem, &id, &path, &array_index, &flag, &groupmode); } /* only if ID and path were set, should we perform any actions */ if (id && path) { short dflags = CREATEDRIVER_WITH_DEFAULT_DVAR; int arraylen = 1; /* array checks */ if (flag & KSP_FLAG_WHOLE_ARRAY) { /* entire array was selected, so add drivers for all */ arraylen= RNA_property_array_length(&te->rnaptr, te->directdata); } else arraylen= array_index; /* we should do at least one step */ if (arraylen == array_index) arraylen++; /* for each array element we should affect, add driver */ for (; array_index < arraylen; array_index++) { /* action depends on mode */ switch (mode) { case DRIVERS_EDITMODE_ADD: { /* add a new driver with the information obtained (only if valid) */ ANIM_add_driver(reports, id, path, array_index, dflags, DRIVER_TYPE_PYTHON); } break; case DRIVERS_EDITMODE_REMOVE: { /* remove driver matching the information obtained (only if valid) */ ANIM_remove_driver(reports, id, path, array_index, dflags); } break; } } /* free path, since it had to be generated */ MEM_freeN(path); } } /* go over sub-tree */ if ((tselem->flag & TSE_CLOSED)==0) do_outliner_drivers_editop(soops, &te->subtree, reports, mode); } }