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 animatable 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_OPEN(tselem, soops)) do_outliner_keyingset_editop(soops, ks, &te->subtree, mode); } }
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; }
/* 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_OPEN(tselem, soops)) { TreeElement *ten; for (ten = te->subtree.first; ten; ten = ten->next) { outliner_set_coordinates_element(soops, ten, startx + UI_UNIT_X, starty); } } }
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); } } }
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 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); } } }
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); } } }
/* 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_OPEN(tselem, soops)) tree_element_show_hierarchy(scene, soops, &te->subtree); } }
/* 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 animatable 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_OPEN(tselem, soops)) do_outliner_drivers_editop(soops, &te->subtree, reports, mode); } }