forked from bartvdbraak/blender
Group Nodes made functional: each group now can be re-used (instanced)
with SHIFT+G. This works as well for local groups as library-linked groups. Also fixed that group nodes were copying internal data to the outside, which made it impossible to use the socket-buttons to set individual values for each group-instance. Library-linked groups are prevented from editing. But, try to open a group and it will give a request for 'make local'. The make local rule is identical to other library data in blender, meaning: - if all users of the library data are local -> the library data is flagged 'local', and if needed a unique name is made - if there's mixed users (local and from other library data) it makes a full copy, and assigns this copy to all local users.
This commit is contained in:
parent
03ae9e70b8
commit
e4a0390b39
@ -95,6 +95,7 @@ void ntreeInitTypes(struct bNodeTree *ntree);
|
||||
void ntreeMakeOwnType(struct bNodeTree *ntree);
|
||||
void ntreeFreeTree(struct bNodeTree *ntree);
|
||||
struct bNodeTree *ntreeCopyTree(struct bNodeTree *ntree, int internal_select);
|
||||
void ntreeMakeLocal(struct bNodeTree *ntree);
|
||||
|
||||
void ntreeSocketUseFlags(struct bNodeTree *ntree);
|
||||
|
||||
|
@ -358,7 +358,7 @@ static void curvemap_make_table(CurveMap *cuma, rctf *clipr)
|
||||
if(cuma->table)
|
||||
MEM_freeT(cuma->table);
|
||||
totpoint= (cuma->totpoint-1)*CM_RESOL;
|
||||
fp= allpoints= MEM_mallocT(totpoint*2*sizeof(float), "table");
|
||||
fp= allpoints= MEM_callocT(totpoint*2*sizeof(float), "table");
|
||||
|
||||
for(a=0; a<cuma->totpoint-1; a++, fp += 2*CM_RESOL) {
|
||||
correct_bezpart(bezt[a].vec[1], bezt[a].vec[2], bezt[a+1].vec[0], bezt[a+1].vec[1]);
|
||||
|
@ -725,7 +725,10 @@ bNode *nodeAddNodeType(bNodeTree *ntree, int type, bNodeTree *ngroup)
|
||||
BLI_addtail(&ntree->nodes, node);
|
||||
node->typeinfo= ntype;
|
||||
|
||||
BLI_strncpy(node->name, ntype->name, NODE_MAXSTR);
|
||||
if(ngroup)
|
||||
BLI_strncpy(node->name, ngroup->id.name+2, NODE_MAXSTR);
|
||||
else
|
||||
BLI_strncpy(node->name, ntype->name, NODE_MAXSTR);
|
||||
node->type= ntype->type;
|
||||
node->flag= NODE_SELECT|ntype->flag;
|
||||
node->width= ntype->width;
|
||||
@ -868,6 +871,77 @@ bNodeTree *ntreeAddTree(int type)
|
||||
return ntree;
|
||||
}
|
||||
|
||||
bNodeTree *ntreeCopyTree(bNodeTree *ntree, int internal_select)
|
||||
{
|
||||
bNodeTree *newtree;
|
||||
bNode *node, *nnode, *last;
|
||||
bNodeLink *link, *nlink;
|
||||
bNodeSocket *sock;
|
||||
int a;
|
||||
|
||||
if(ntree==NULL) return NULL;
|
||||
|
||||
if(internal_select==0) {
|
||||
/* is ntree part of library? */
|
||||
for(newtree=G.main->nodetree.first; newtree; newtree= newtree->id.next)
|
||||
if(newtree==ntree) break;
|
||||
if(newtree)
|
||||
newtree= copy_libblock(ntree);
|
||||
else
|
||||
newtree= MEM_dupallocN(ntree);
|
||||
newtree->nodes.first= newtree->nodes.last= NULL;
|
||||
newtree->links.first= newtree->links.last= NULL;
|
||||
}
|
||||
else
|
||||
newtree= ntree;
|
||||
|
||||
last= ntree->nodes.last;
|
||||
for(node= ntree->nodes.first; node; node= node->next) {
|
||||
|
||||
node->new= NULL;
|
||||
if(internal_select==0 || (node->flag & NODE_SELECT)) {
|
||||
nnode= nodeCopyNode(newtree, node); /* sets node->new */
|
||||
if(internal_select) {
|
||||
node->flag &= ~NODE_SELECT;
|
||||
nnode->flag |= NODE_SELECT;
|
||||
}
|
||||
node->flag &= ~NODE_ACTIVE;
|
||||
}
|
||||
if(node==last) break;
|
||||
}
|
||||
|
||||
/* check for copying links */
|
||||
for(link= ntree->links.first; link; link= link->next) {
|
||||
if(link->fromnode->new && link->tonode->new) {
|
||||
nlink= nodeAddLink(newtree, link->fromnode->new, NULL, link->tonode->new, NULL);
|
||||
/* sockets were copied in order */
|
||||
for(a=0, sock= link->fromnode->outputs.first; sock; sock= sock->next, a++) {
|
||||
if(sock==link->fromsock)
|
||||
break;
|
||||
}
|
||||
nlink->fromsock= BLI_findlink(&link->fromnode->new->outputs, a);
|
||||
|
||||
for(a=0, sock= link->tonode->inputs.first; sock; sock= sock->next, a++) {
|
||||
if(sock==link->tosock)
|
||||
break;
|
||||
}
|
||||
nlink->tosock= BLI_findlink(&link->tonode->new->inputs, a);
|
||||
}
|
||||
}
|
||||
|
||||
/* own type definition for group usage */
|
||||
if(internal_select==0) {
|
||||
if(ntree->owntype) {
|
||||
newtree->owntype= MEM_dupallocN(ntree->owntype);
|
||||
if(ntree->owntype->inputs)
|
||||
newtree->owntype->inputs= MEM_dupallocN(ntree->owntype->inputs);
|
||||
if(ntree->owntype->outputs)
|
||||
newtree->owntype->outputs= MEM_dupallocN(ntree->owntype->outputs);
|
||||
}
|
||||
}
|
||||
return newtree;
|
||||
}
|
||||
|
||||
#pragma mark /* ************** Free stuff ********** */
|
||||
|
||||
/* goes over entire tree */
|
||||
@ -972,71 +1046,112 @@ void ntreeFreeTree(bNodeTree *ntree)
|
||||
}
|
||||
}
|
||||
|
||||
bNodeTree *ntreeCopyTree(bNodeTree *ntree, int internal_select)
|
||||
|
||||
void ntreeMakeLocal(bNodeTree *ntree)
|
||||
{
|
||||
bNodeTree *newtree;
|
||||
bNode *node, *nnode, *last;
|
||||
bNodeLink *link, *nlink;
|
||||
bNodeSocket *sock;
|
||||
int a;
|
||||
int local=0, lib=0;
|
||||
|
||||
if(ntree==NULL) return NULL;
|
||||
/* - only lib users: do nothing
|
||||
* - only local users: set flag
|
||||
* - mixed: make copy
|
||||
*/
|
||||
|
||||
if(internal_select==0) {
|
||||
newtree= MEM_dupallocN(ntree);
|
||||
newtree->nodes.first= newtree->nodes.last= NULL;
|
||||
newtree->links.first= newtree->links.last= NULL;
|
||||
if(ntree->id.lib==NULL) return;
|
||||
if(ntree->id.us==1) {
|
||||
ntree->id.lib= 0;
|
||||
ntree->id.flag= LIB_LOCAL;
|
||||
new_id(0, (ID *)ntree, 0);
|
||||
return;
|
||||
}
|
||||
else
|
||||
newtree= ntree;
|
||||
|
||||
last= ntree->nodes.last;
|
||||
for(node= ntree->nodes.first; node; node= node->next) {
|
||||
/* now check users of groups... again typedepending, callback... */
|
||||
if(ntree->type==NTREE_SHADER) {
|
||||
Material *ma;
|
||||
for(ma= G.main->mat.first; ma; ma= ma->id.next) {
|
||||
if(ma->nodetree) {
|
||||
bNode *node;
|
||||
|
||||
/* find if group is in tree */
|
||||
for(node= ma->nodetree->nodes.first; node; node= node->next) {
|
||||
if(node->id == (ID *)ntree) {
|
||||
if(ma->id.lib) lib= 1;
|
||||
else local= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(ntree->type==NTREE_COMPOSIT) {
|
||||
Scene *sce;
|
||||
for(sce= G.main->scene.first; sce; sce= sce->id.next) {
|
||||
if(sce->nodetree) {
|
||||
bNode *node;
|
||||
|
||||
/* find if group is in tree */
|
||||
for(node= sce->nodetree->nodes.first; node; node= node->next) {
|
||||
if(node->id == (ID *)ntree) {
|
||||
if(sce->id.lib) lib= 1;
|
||||
else local= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* if all users are local, we simply make tree local */
|
||||
if(local && lib==0) {
|
||||
ntree->id.lib= NULL;
|
||||
ntree->id.flag= LIB_LOCAL;
|
||||
new_id(0, (ID *)ntree, 0);
|
||||
}
|
||||
else if(local && lib) {
|
||||
/* this is the mixed case, we copy the tree and assign it to local users */
|
||||
bNodeTree *newtree= ntreeCopyTree(ntree, 0);
|
||||
|
||||
node->new= NULL;
|
||||
if(internal_select==0 || (node->flag & NODE_SELECT)) {
|
||||
nnode= nodeCopyNode(newtree, node); /* sets node->new */
|
||||
if(internal_select) {
|
||||
node->flag &= ~NODE_SELECT;
|
||||
nnode->flag |= NODE_SELECT;
|
||||
newtree->id.us= 0;
|
||||
|
||||
if(ntree->type==NTREE_SHADER) {
|
||||
Material *ma;
|
||||
for(ma= G.main->mat.first; ma; ma= ma->id.next) {
|
||||
if(ma->nodetree) {
|
||||
bNode *node;
|
||||
|
||||
/* find if group is in tree */
|
||||
for(node= ma->nodetree->nodes.first; node; node= node->next) {
|
||||
if(node->id == (ID *)ntree) {
|
||||
if(ma->id.lib==NULL) {
|
||||
node->id= &newtree->id;
|
||||
newtree->id.us++;
|
||||
ntree->id.us--;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
node->flag &= ~NODE_ACTIVE;
|
||||
}
|
||||
if(node==last) break;
|
||||
}
|
||||
|
||||
/* check for copying links */
|
||||
for(link= ntree->links.first; link; link= link->next) {
|
||||
if(link->fromnode->new && link->tonode->new) {
|
||||
nlink= nodeAddLink(newtree, link->fromnode->new, NULL, link->tonode->new, NULL);
|
||||
/* sockets were copied in order */
|
||||
for(a=0, sock= link->fromnode->outputs.first; sock; sock= sock->next, a++) {
|
||||
if(sock==link->fromsock)
|
||||
break;
|
||||
else if(ntree->type==NTREE_COMPOSIT) {
|
||||
Scene *sce;
|
||||
for(sce= G.main->scene.first; sce; sce= sce->id.next) {
|
||||
if(sce->nodetree) {
|
||||
bNode *node;
|
||||
|
||||
/* find if group is in tree */
|
||||
for(node= sce->nodetree->nodes.first; node; node= node->next) {
|
||||
if(node->id == (ID *)ntree) {
|
||||
if(sce->id.lib==NULL) {
|
||||
node->id= &newtree->id;
|
||||
newtree->id.us++;
|
||||
ntree->id.us--;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
nlink->fromsock= BLI_findlink(&link->fromnode->new->outputs, a);
|
||||
|
||||
for(a=0, sock= link->tonode->inputs.first; sock; sock= sock->next, a++) {
|
||||
if(sock==link->tosock)
|
||||
break;
|
||||
}
|
||||
nlink->tosock= BLI_findlink(&link->tonode->new->inputs, a);
|
||||
}
|
||||
}
|
||||
|
||||
/* own type definition for group usage */
|
||||
if(internal_select==0) {
|
||||
if(ntree->owntype) {
|
||||
newtree->owntype= MEM_dupallocN(ntree->owntype);
|
||||
if(ntree->owntype->inputs)
|
||||
newtree->owntype->inputs= MEM_dupallocN(ntree->owntype->inputs);
|
||||
if(ntree->owntype->outputs)
|
||||
newtree->owntype->outputs= MEM_dupallocN(ntree->owntype->outputs);
|
||||
}
|
||||
}
|
||||
return newtree;
|
||||
}
|
||||
|
||||
|
||||
#pragma mark /* ************ find stuff *************** */
|
||||
|
||||
bNodeLink *nodeFindLink(bNodeTree *ntree, bNodeSocket *from, bNodeSocket *to)
|
||||
@ -1405,16 +1520,8 @@ static int ntree_begin_exec_tree(bNodeTree *ntree)
|
||||
|
||||
if(node->type==NODE_GROUP) {
|
||||
if(node->id) {
|
||||
|
||||
node->stack_index= index;
|
||||
index+= ntree_begin_exec_tree((bNodeTree *)node->id);
|
||||
|
||||
/* copy internal data from internal nodes to own input sockets */
|
||||
for(sock= node->inputs.first; sock; sock= sock->next) {
|
||||
if(sock->tosock) {
|
||||
sock->ns= sock->tosock->ns;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1203,7 +1203,7 @@ static void direct_link_curvemapping(FileData *fd, CurveMapping *cumap)
|
||||
|
||||
/* ************ READ NODE TREE *************** */
|
||||
|
||||
/* singe node tree, ntree is not NULL */
|
||||
/* singe node tree (also used for material/scene trees), ntree is not NULL */
|
||||
static void lib_link_ntree(FileData *fd, ID *id, bNodeTree *ntree)
|
||||
{
|
||||
bNode *node;
|
||||
@ -1212,30 +1212,33 @@ static void lib_link_ntree(FileData *fd, ID *id, bNodeTree *ntree)
|
||||
node->id= newlibadr_us(fd, id->lib, node->id);
|
||||
}
|
||||
|
||||
/* library linking after fileread */
|
||||
/* library ntree linking after fileread */
|
||||
static void lib_link_nodetree(FileData *fd, Main *main)
|
||||
{
|
||||
bNodeTree *ntree;
|
||||
|
||||
/* only link ID pointers */
|
||||
for(ntree= main->nodetree.first; ntree; ntree= ntree->id.next) {
|
||||
if(ntree->id.flag & LIB_NEEDLINK) {
|
||||
ntree->id.flag -= LIB_NEEDLINK;
|
||||
lib_link_ntree(fd, &ntree->id, ntree);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* verify types for nodes and groups, all data has to be read */
|
||||
static void lib_verify_nodetree(Main *main)
|
||||
{
|
||||
Scene *sce;
|
||||
Material *ma;
|
||||
bNodeTree *ntree;
|
||||
bNode *node;
|
||||
|
||||
/* in multiple steps, first link ID pointers */
|
||||
for(ntree= main->nodetree.first; ntree; ntree= ntree->id.next) {
|
||||
if(ntree->id.flag & LIB_NEEDLINK) {
|
||||
lib_link_ntree(fd, &ntree->id, ntree);
|
||||
}
|
||||
}
|
||||
|
||||
/* now create the own typeinfo structs an verify nodes */
|
||||
/* here we still assume no groups in groups */
|
||||
for(ntree= main->nodetree.first; ntree; ntree= ntree->id.next) {
|
||||
if(ntree->id.flag & LIB_NEEDLINK) {
|
||||
ntree->id.flag -= LIB_NEEDLINK;
|
||||
|
||||
ntreeVerifyTypes(ntree); /* internal nodes, no groups! */
|
||||
ntreeMakeOwnType(ntree); /* for group usage */
|
||||
}
|
||||
ntreeVerifyTypes(ntree); /* internal nodes, no groups! */
|
||||
ntreeMakeOwnType(ntree); /* for group usage */
|
||||
}
|
||||
|
||||
/* now verify all types in material trees, groups are set OK now */
|
||||
@ -1250,6 +1253,8 @@ static void lib_link_nodetree(FileData *fd, Main *main)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* ntree itself has been read! */
|
||||
static void direct_link_nodetree(FileData *fd, bNodeTree *ntree)
|
||||
{
|
||||
@ -5370,7 +5375,7 @@ static void lib_link_all(FileData *fd, Main *main)
|
||||
lib_link_armature(fd, main);
|
||||
lib_link_action(fd, main);
|
||||
lib_link_vfont(fd, main);
|
||||
lib_link_nodetree(fd, main); /* has to be done after materials, it will verify group nodes */
|
||||
lib_link_nodetree(fd, main); /* has to be done after scene/materials, this will verify group nodes */
|
||||
|
||||
lib_link_mesh(fd, main); /* as last: tpage images with users at zero */
|
||||
|
||||
@ -5453,6 +5458,7 @@ BlendFileData *blo_read_file_internal(FileData *fd, BlendReadError *error_r)
|
||||
blo_join_main(&fd->mainlist);
|
||||
|
||||
lib_link_all(fd, bfd->main);
|
||||
lib_verify_nodetree(bfd->main);
|
||||
|
||||
link_global(fd, bfd, fg); /* as last */
|
||||
|
||||
@ -5936,6 +5942,9 @@ static void expand_scene(FileData *fd, Main *mainvar, Scene *sce)
|
||||
}
|
||||
expand_doit(fd, mainvar, sce->camera);
|
||||
expand_doit(fd, mainvar, sce->world);
|
||||
|
||||
if(sce->nodetree)
|
||||
expand_nodetree(fd, mainvar, sce->nodetree);
|
||||
}
|
||||
|
||||
static void expand_camera(FileData *fd, Main *mainvar, Camera *ca)
|
||||
@ -6175,6 +6184,7 @@ void BLO_script_library_append(BlendHandle *bh, char *dir, char *name, int idcod
|
||||
G.main= mainlist.first;
|
||||
|
||||
lib_link_all(fd, G.main);
|
||||
lib_verify_nodetree(G.main);
|
||||
|
||||
DAG_scene_sort(G.scene);
|
||||
}
|
||||
@ -6259,6 +6269,7 @@ void BLO_library_append(SpaceFile *sfile, char *dir, int idcode)
|
||||
G.main= fd->mainlist.first;
|
||||
|
||||
lib_link_all(fd, G.main);
|
||||
lib_verify_nodetree(G.main);
|
||||
|
||||
/* give a base to loose objects */
|
||||
give_base_to_objects(G.scene, &(G.main->object), sfile->flag & FILE_LINK);
|
||||
|
@ -1361,7 +1361,16 @@ static void node_draw_basis(ScrArea *sa, SpaceNode *snode, bNode *node)
|
||||
iconofs-= 18.0f;
|
||||
glEnable(GL_BLEND);
|
||||
BIF_icon_set_aspect(ICON_NODE, snode->aspect);
|
||||
BIF_icon_draw_blended(iconofs, rct->ymax-NODE_DY+2, ICON_NODE, 0, -60);
|
||||
if(node->id->lib) {
|
||||
glPixelTransferf(GL_GREEN_SCALE, 0.7f);
|
||||
glPixelTransferf(GL_BLUE_SCALE, 0.3f);
|
||||
BIF_icon_draw(iconofs, rct->ymax-NODE_DY+2, ICON_NODE);
|
||||
glPixelTransferf(GL_GREEN_SCALE, 1.0f);
|
||||
glPixelTransferf(GL_BLUE_SCALE, 1.0f);
|
||||
}
|
||||
else {
|
||||
BIF_icon_draw_blended(iconofs, rct->ymax-NODE_DY+2, ICON_NODE, 0, -60);
|
||||
}
|
||||
glDisable(GL_BLEND);
|
||||
}
|
||||
if(node->typeinfo->flag & NODE_OPTIONS) {
|
||||
@ -1443,9 +1452,6 @@ static void node_draw_basis(ScrArea *sa, SpaceNode *snode, bNode *node)
|
||||
if(node->block && sock->link==NULL) {
|
||||
float *butpoin= sock->ns.vec;
|
||||
|
||||
if(node->type==NODE_GROUP && sock->tosock)
|
||||
butpoin= sock->tosock->ns.vec;
|
||||
|
||||
if(sock->type==SOCK_VALUE) {
|
||||
bt= uiDefButF(node->block, NUM, B_NODE_EXEC+node->nr, sock->name,
|
||||
(short)sock->locx+NODE_DYS, (short)(sock->locy)-9, (short)node->width-NODE_DY, 17,
|
||||
|
@ -420,6 +420,12 @@ static void snode_make_group_editable(SpaceNode *snode, bNode *gnode)
|
||||
}
|
||||
|
||||
if(gnode && gnode->type==NODE_GROUP && gnode->id) {
|
||||
if(gnode->id->lib) {
|
||||
if(okee("Make Group Local"))
|
||||
ntreeMakeLocal((bNodeTree *)gnode->id);
|
||||
else
|
||||
return;
|
||||
}
|
||||
gnode->flag |= NODE_GROUP_EDIT;
|
||||
snode->edittree= (bNodeTree *)gnode->id;
|
||||
|
||||
@ -432,6 +438,8 @@ static void snode_make_group_editable(SpaceNode *snode, bNode *gnode)
|
||||
else
|
||||
snode->edittree= snode->nodetree;
|
||||
|
||||
ntreeSolveOrder(snode->nodetree);
|
||||
|
||||
/* finally send out events for new active node */
|
||||
if(snode->treetype==NTREE_SHADER) {
|
||||
allqueue(REDRAWBUTSSHADING, 0);
|
||||
@ -474,6 +482,66 @@ static void snode_verify_groups(SpaceNode *snode)
|
||||
|
||||
}
|
||||
|
||||
static void node_addgroup(SpaceNode *snode)
|
||||
{
|
||||
bNodeTree *ngroup;
|
||||
int tot= 0, offs, val;
|
||||
char *strp;
|
||||
|
||||
if(snode->edittree!=snode->nodetree) {
|
||||
error("Can not add a Group in a Group");
|
||||
return;
|
||||
}
|
||||
|
||||
/* construct menu with choices */
|
||||
for(ngroup= G.main->nodetree.first; ngroup; ngroup= ngroup->id.next) {
|
||||
if(ngroup->type==snode->treetype)
|
||||
tot++;
|
||||
}
|
||||
if(tot==0) {
|
||||
error("No groups available in database");
|
||||
return;
|
||||
}
|
||||
strp= MEM_mallocN(32*tot+32, "menu");
|
||||
strcpy(strp, "Add Group %t");
|
||||
offs= strlen(strp);
|
||||
|
||||
for(tot=0, ngroup= G.main->nodetree.first; ngroup; ngroup= ngroup->id.next, tot++) {
|
||||
if(ngroup->type==snode->treetype)
|
||||
offs+= sprintf(strp+offs, "|%s %%x%d", ngroup->id.name+2, tot);
|
||||
}
|
||||
|
||||
val= pupmenu(strp);
|
||||
if(val>=0) {
|
||||
ngroup= BLI_findlink(&G.main->nodetree, val);
|
||||
if(ngroup) {
|
||||
bNode *node= nodeAddNodeType(snode->edittree, NODE_GROUP, ngroup);
|
||||
|
||||
/* generics */
|
||||
if(node) {
|
||||
float locx, locy;
|
||||
short mval[2];
|
||||
|
||||
node_deselectall(snode, 0);
|
||||
|
||||
getmouseco_areawin(mval);
|
||||
areamouseco_to_ipoco(G.v2d, mval, &locx, &locy);
|
||||
|
||||
node->locx= locx;
|
||||
node->locy= locy + 60.0f; // arbitrary.. so its visible
|
||||
node->flag |= SELECT;
|
||||
|
||||
id_us_plus(node->id);
|
||||
|
||||
node_set_active(snode, node);
|
||||
BIF_undo_push("Add Node");
|
||||
}
|
||||
}
|
||||
}
|
||||
MEM_freeN(strp);
|
||||
}
|
||||
|
||||
|
||||
/* ************************** Node generic ************** */
|
||||
|
||||
/* allows to walk the list in order of visibility */
|
||||
@ -1737,6 +1805,9 @@ void winqreadnodespace(ScrArea *sa, void *spacedata, BWinEvent *evt)
|
||||
if(okee("Ungroup"))
|
||||
node_ungroup(snode);
|
||||
}
|
||||
else if(G.qual==LR_SHIFTKEY) {
|
||||
node_addgroup(snode);
|
||||
}
|
||||
else
|
||||
transform_nodes(snode->edittree, 'g', "Translate Node");
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user