Toggle links tool for Node Editor
authorJuho Vepsalainen <bebraw@gmail.com>
Thu, 27 Dec 2007 10:17:33 +0000 (10:17 +0000)
committerJuho Vepsalainen <bebraw@gmail.com>
Thu, 27 Dec 2007 10:17:33 +0000 (10:17 +0000)
This commit adds a new tool, Toggle Links, to the node editor. This tool
allows the user to toggle the status (linked/not linked) between desired
sockets. The tool can be used either by using the f key or the menus.

This functionality is analogue to one found in object editing modes except
for its additional toggle functionality.

To use this tool, the user has to first select an input and an output socket.
Selecting is done by clicking with right mouse button on a socket. After the
tool has been invoked, the link between those two sockets is toggled. The
result may vary based on existing linkage.

There can be only one input and one output selected at maximum in a node
tree. This means that if the user selects a socket while one of the same
type is already selected, the old one will be deselected.

The tool complements the current way of connecting nodes. One possible use
for it is to use it to review output of nodes by using a viewer node. Just
select wanted input socket of a viewer node, set it visible and use selection
of an output socket in conjuction with f key to show the output in the viewer
node. Select another output and hit f to see its output and so on.

source/blender/blenkernel/BKE_node.h
source/blender/blenkernel/intern/node.c
source/blender/include/BSE_node.h
source/blender/makesdna/DNA_node_types.h
source/blender/src/drawnode.c
source/blender/src/editnode.c
source/blender/src/header_node.c
source/blender/src/toolbox.c

index 2ffa1d205da935551fb3430d454dc0bad1695dfb..b9df1e45bae90e41f2903ed6a5cd1f10b083fc4d 100644 (file)
@@ -143,6 +143,7 @@ void                        nodeVerifyType(struct bNodeTree *ntree, struct bNode *node);
 
 void                   nodeAddToPreview(struct bNode *, float *, int, int);
 
+void                   nodeUnlinkNode(struct bNodeTree *ntree, struct bNode *node);
 struct bNode   *nodeAddNodeType(struct bNodeTree *ntree, int type, struct bNodeTree *ngroup);
 void                   nodeFreeNode(struct bNodeTree *ntree, struct bNode *node);
 struct bNode   *nodeCopyNode(struct bNodeTree *ntree, struct bNode *node);
@@ -150,6 +151,8 @@ struct bNode        *nodeCopyNode(struct bNodeTree *ntree, struct bNode *node);
 struct bNodeLink *nodeAddLink(struct bNodeTree *ntree, struct bNode *fromnode, struct bNodeSocket *fromsock, struct bNode *tonode, struct bNodeSocket *tosock);
 void                   nodeRemLink(struct bNodeTree *ntree, struct bNodeLink *link);
 
+int                    nodeFindNode(struct bNodeTree *ntree, struct bNodeSocket *sock, struct bNode **nodep, int *sockindex);
+
 struct bNodeLink *nodeFindLink(struct bNodeTree *ntree, struct bNodeSocket *from, struct bNodeSocket *to);
 int                            nodeCountSocketLinks(struct bNodeTree *ntree, struct bNodeSocket *sock);
 
index 651115b7180bda22f078b7a70a411f04b452ef83..4bcacae9fc5c7adab22052c5dd6a900e7f7e132c 100644 (file)
@@ -483,6 +483,20 @@ bNode *nodeMakeGroupFromSelected(bNodeTree *ntree)
                        BLI_addtail(&ngroup->nodes, node);
                        node->locx-= 0.5f*(min[0]+max[0]);
                        node->locy-= 0.5f*(min[1]+max[1]);
+
+                       /* set selin and selout of the nodetree */
+                       for(sock= node->inputs.first; sock; sock= sock->next) {
+                               if(sock->flag & SOCK_SEL) {
+                                       ngroup->selin= sock;
+                                       break;
+                               }
+                       }
+                       for(sock= node->outputs.first; sock; sock= sock->next) {
+                               if(sock->flag & SOCK_SEL) {
+                                       ngroup->selout= sock;
+                                       break;
+                               }
+                       }
                }
        }
 
@@ -653,7 +667,8 @@ void nodeGroupSocketUseFlags(bNodeTree *ngroup)
        }
 }
 
-static void find_node_with_socket(bNodeTree *ntree, bNodeSocket *sock, bNode **nodep, int *sockindex)
+/* finds a node based on given socket */
+int nodeFindNode(bNodeTree *ntree, bNodeSocket *sock, bNode **nodep, int *sockindex)
 {
        bNode *node;
        bNodeSocket *tsock;
@@ -671,13 +686,15 @@ static void find_node_with_socket(bNodeTree *ntree, bNodeSocket *sock, bNode **n
                if(tsock)
                        break;
        }
+
        if(node) {
                *nodep= node;
-               *sockindex= index;
-       }
-       else {
-               *nodep= NULL;
+               if(sockindex) *sockindex= index;
+               return 1;
        }
+       
+       *nodep= NULL;
+       return 0;
 }
 
 /* returns 1 if its OK */
@@ -717,7 +734,7 @@ int nodeGroupUnGroup(bNodeTree *ntree, bNode *gnode)
        for(link= ntree->links.first; link; link= link->next) {
                if(link->tonode==gnode) {
                        /* link->tosock->tosock is on the node we look for */
-                       find_node_with_socket(ngroup, link->tosock->tosock, &nextn, &index);
+                       nodeFindNode(ngroup, link->tosock->tosock, &nextn, &index);
                        if(nextn==NULL) printf("wrong stuff!\n");
                        else if(nextn->new_node==NULL) printf("wrong stuff too!\n");
                        else {
@@ -727,7 +744,7 @@ int nodeGroupUnGroup(bNodeTree *ntree, bNode *gnode)
                }
                else if(link->fromnode==gnode) {
                        /* link->fromsock->tosock is on the node we look for */
-                       find_node_with_socket(ngroup, link->fromsock->tosock, &nextn, &index);
+                       nodeFindNode(ngroup, link->fromsock->tosock, &nextn, &index);
                        if(nextn==NULL) printf("1 wrong stuff!\n");
                        else if(nextn->new_node==NULL) printf("1 wrong stuff too!\n");
                        else {
@@ -898,6 +915,28 @@ bNodeTree *ntreeCopyTree(bNodeTree *ntree, int internal_select)
                                nnode->flag |= NODE_SELECT;
                        }
                        node->flag &= ~NODE_ACTIVE;
+
+                       /* deselect original sockets */
+                       for(sock= node->inputs.first; sock; sock= sock->next) {
+                               if(sock->flag & SOCK_SEL) sock->flag&= ~SOCK_SEL;
+                       }
+                       for(sock= node->outputs.first; sock; sock= sock->next) {
+                               if(sock->flag & SOCK_SEL) sock->flag&= ~SOCK_SEL;
+                       }
+                       
+                       /* set tree selin and selout to new sockets */
+                       for(sock= nnode->inputs.first; sock; sock= sock->next) {
+                               if(sock->flag & SOCK_SEL) {
+                                       ntree->selin= sock;
+                                       break;
+                               }
+                       }
+                       for(sock= nnode->outputs.first; sock; sock= sock->next) {
+                               if(sock->flag & SOCK_SEL) {
+                                       ntree->selout= sock;
+                                       break;
+                               }
+                       }
                }
                if(node==last) break;
        }
@@ -941,7 +980,7 @@ bNodeTree *ntreeCopyTree(bNodeTree *ntree, int internal_select)
 /* ************** Free stuff ********** */
 
 /* goes over entire tree */
-static void node_unlink_node(bNodeTree *ntree, bNode *node)
+void nodeUnlinkNode(bNodeTree *ntree, bNode *node)
 {
        bNodeLink *link, *next;
        bNodeSocket *sock;
@@ -985,7 +1024,7 @@ static void composit_free_node_cache(bNode *node)
 
 void nodeFreeNode(bNodeTree *ntree, bNode *node)
 {
-       node_unlink_node(ntree, node);
+       nodeUnlinkNode(ntree, node);
        BLI_remlink(&ntree->nodes, node);
 
        /* since it is called while free database, node->id is undefined */
index 171a277e8e69aaf94c13c637deffb3973e19212b..5862918953c9850e8e840bd9612a12571314b93d 100644 (file)
@@ -72,6 +72,7 @@ void snode_make_group_editable(struct SpaceNode *snode, struct bNode *gnode);
 void node_hide(struct SpaceNode *snode);
 void node_read_renderlayers(struct SpaceNode *snode);
 void clear_scene_in_nodes(struct Scene *sce);
+void node_toggle_link(struct SpaceNode *snode);
 
 void node_transform_ext(int mode, int unused);
 void node_shader_default(struct Material *ma);
index 5f20939fc236c75e5ea2013368088c021f81c0c5..b5084c418848c399b5e37be44cab36221f1c9ab2 100644 (file)
@@ -92,7 +92,8 @@ typedef struct bNodeSocket {
 #define SOCK_IN_USE                            4
                /* unavailable is for dynamic sockets */
 #define SOCK_UNAVAIL                   8
-
+               /* flag for selection status */
+#define SOCK_SEL                       16
 #
 #
 typedef struct bNodePreview {
@@ -167,6 +168,10 @@ typedef struct bNodeTree {
        ListBase alltypes;                              /* type definitions */
        struct bNodeType *owntype;              /* for groups or dynamic trees, no read/write */
 
+       /* selected input/output socket */
+       bNodeSocket *selin;
+       bNodeSocket *selout;
+
        /* callbacks */
        void (*timecursor)(int nr);
        void (*stats_draw)(char *str);
index b0c55d104f32dbac5570cd089d9f6055fab8ac06..cc65b10c8e73497986d4ca933f1b7e9367b5359f 100644 (file)
@@ -1988,7 +1988,8 @@ static void draw_nodespace_back(ScrArea *sa, SpaceNode *snode)
 }
 
 /* nice AA filled circle */
-static void socket_circle_draw(float x, float y, float size, int type, int select)
+/* this might have some more generic use */
+static void circle_draw(float x, float y, float size, int type, int col[3])
 {
        /* 16 values of sin function */
        static float si[16] = {
@@ -2006,28 +2007,7 @@ static void socket_circle_draw(float x, float y, float size, int type, int selec
        };
        int a;
        
-       if(select==0) {
-               if(type==-1)
-                       glColor3ub(0, 0, 0);
-               else if(type==SOCK_VALUE)
-                       glColor3ub(160, 160, 160);
-               else if(type==SOCK_VECTOR)
-                       glColor3ub(100, 100, 200);
-               else if(type==SOCK_RGBA)
-                       glColor3ub(200, 200, 40);
-               else 
-                       glColor3ub(100, 200, 100);
-       }
-       else {
-               if(type==SOCK_VALUE)
-                       glColor3ub(200, 200, 200);
-               else if(type==SOCK_VECTOR)
-                       glColor3ub(140, 140, 240);
-               else if(type==SOCK_RGBA)
-                       glColor3ub(240, 240, 100);
-               else 
-                       glColor3ub(140, 240, 140);
-       }
+       glColor3ub(col[0], col[1], col[2]);
        
        glBegin(GL_POLYGON);
        for(a=0; a<16; a++)
@@ -2045,6 +2025,41 @@ static void socket_circle_draw(float x, float y, float size, int type, int selec
        glDisable(GL_BLEND);
 }
 
+static void socket_circle_draw(bNodeSocket *sock, float size)
+{
+       int col[3];
+       
+       /* choose color based on sock flags */
+       if(sock->flag & SELECT) {
+               if(sock->flag & SOCK_SEL) {
+                       col[0]= 240; col[1]= 200; col[2]= 40;}
+               else if(sock->type==SOCK_VALUE) {
+                       col[0]= 200; col[1]= 200; col[2]= 200;}
+               else if(sock->type==SOCK_VECTOR) {
+                       col[0]= 140; col[1]= 140; col[2]= 240;}
+               else if(sock->type==SOCK_RGBA) {
+                       col[0]= 240; col[1]= 240; col[2]= 100;}
+               else {
+                       col[0]= 140; col[1]= 240; col[2]= 140;}
+       }
+       else if(sock->flag & SOCK_SEL) {
+               col[0]= 200; col[1]= 160; col[2]= 0;}
+       else {
+               if(sock->type==-1) {
+                       col[0]= 0; col[1]= 0; col[2]= 0;}
+               else if(sock->type==SOCK_VALUE) {
+                       col[0]= 160; col[1]= 160; col[2]= 160;}
+               else if(sock->type==SOCK_VECTOR) {
+                       col[0]= 100; col[1]= 100; col[2]= 200;}
+               else if(sock->type==SOCK_RGBA) {
+                       col[0]= 200; col[1]= 200; col[2]= 40;}
+               else { 
+                       col[0]= 100; col[1]= 200; col[2]= 100;}
+       }
+       
+       circle_draw(sock->locx, sock->locy, size, sock->type, col);
+}
+
 /* not a callback */
 static void node_draw_preview(bNodePreview *preview, rctf *prv)
 {
@@ -2459,7 +2474,7 @@ static void node_draw_basis(ScrArea *sa, SpaceNode *snode, bNode *node)
        /* socket inputs, buttons */
        for(sock= node->inputs.first; sock; sock= sock->next) {
                if(!(sock->flag & (SOCK_HIDDEN|SOCK_UNAVAIL))) {
-                       socket_circle_draw(sock->locx, sock->locy, NODE_SOCKSIZE, sock->type, sock->flag & SELECT);
+                       socket_circle_draw(sock, NODE_SOCKSIZE);
                        
                        if(block && sock->link==NULL) {
                                float *butpoin= sock->ns.vec;
@@ -2501,7 +2516,7 @@ static void node_draw_basis(ScrArea *sa, SpaceNode *snode, bNode *node)
        /* socket outputs */
        for(sock= node->outputs.first; sock; sock= sock->next) {
                if(!(sock->flag & (SOCK_HIDDEN|SOCK_UNAVAIL))) {
-                       socket_circle_draw(sock->locx, sock->locy, NODE_SOCKSIZE, sock->type, sock->flag & SELECT);
+                       socket_circle_draw(sock, NODE_SOCKSIZE);
                        
                        BIF_ThemeColor(TH_TEXT);
                        ofs= 0;
@@ -2589,12 +2604,12 @@ void node_draw_hidden(SpaceNode *snode, bNode *node)
        /* sockets */
        for(sock= node->inputs.first; sock; sock= sock->next) {
                if(!(sock->flag & (SOCK_HIDDEN|SOCK_UNAVAIL)))
-                       socket_circle_draw(sock->locx, sock->locy, NODE_SOCKSIZE, sock->type, sock->flag & SELECT);
+                       socket_circle_draw(sock, NODE_SOCKSIZE);
        }
        
        for(sock= node->outputs.first; sock; sock= sock->next) {
                if(!(sock->flag & (SOCK_HIDDEN|SOCK_UNAVAIL)))
-                       socket_circle_draw(sock->locx, sock->locy, NODE_SOCKSIZE, sock->type, sock->flag & SELECT);
+                       socket_circle_draw(sock, NODE_SOCKSIZE);
        }
 }
 
@@ -2805,10 +2820,10 @@ static void node_draw_group(ScrArea *sa, SpaceNode *snode, bNode *gnode)
        /* group sockets */
        for(sock= gnode->inputs.first; sock; sock= sock->next)
                if(!(sock->flag & (SOCK_HIDDEN|SOCK_UNAVAIL)))
-                       socket_circle_draw(sock->locx, sock->locy, NODE_SOCKSIZE, sock->type, sock->flag & SELECT);
+                       socket_circle_draw(sock, NODE_SOCKSIZE);
        for(sock= gnode->outputs.first; sock; sock= sock->next)
                if(!(sock->flag & (SOCK_HIDDEN|SOCK_UNAVAIL)))
-                       socket_circle_draw(sock->locx, sock->locy, NODE_SOCKSIZE, sock->type, sock->flag & SELECT);
+                       socket_circle_draw(sock, NODE_SOCKSIZE);
 
        /* and finally the whole tree */
        node_draw_nodetree(sa, snode, ngroup);
index a14b0ccc389e928550c74cc86c186f662b49ed8d..d035908994cf6965a7b1a49b067e20c4d29f7c95 100644 (file)
@@ -830,6 +830,23 @@ static void snode_bg_viewmove(SpaceNode *snode)
        window_set_cursor(win, oldcursor);
 }
 
+static void reset_sel_socket(SpaceNode *snode, int in_out)
+{
+       bNode *node;
+       bNodeSocket *sock;
+       
+       for(node= snode->edittree->nodes.first; node; node= node->next) {
+               if(in_out & SOCK_IN) {
+                       for(sock= node->inputs.first; sock; sock= sock->next)
+                               if(sock->flag & SOCK_SEL) sock->flag&= ~SOCK_SEL;
+               }
+               if(in_out & SOCK_OUT) {
+                       for(sock= node->outputs.first; sock; sock= sock->next)
+                               if(sock->flag & SOCK_SEL) sock->flag&= ~SOCK_SEL;
+               }
+       }
+}
+
 /* checks mouse position, and returns found node/socket */
 /* type is SOCK_IN and/or SOCK_OUT */
 static int find_indicated_socket(SpaceNode *snode, bNode **nodep, bNodeSocket **sockp, int in_out)
@@ -1568,6 +1585,33 @@ static void node_insert_convertor(SpaceNode *snode, bNodeLink *link)
 
 #endif
 
+static void node_remove_extra_links(SpaceNode *snode, bNodeSocket *tsock, bNodeLink *link)
+{
+       bNodeLink *tlink;
+       bNodeSocket *sock;
+       
+       if(tsock && nodeCountSocketLinks(snode->edittree, link->tosock) > tsock->limit) {
+               
+               for(tlink= snode->edittree->links.first; tlink; tlink= tlink->next) {
+                       if(link!=tlink && tlink->tosock==link->tosock)
+                               break;
+               }
+               if(tlink) {
+                       /* is there a free input socket with same type? */
+                       for(sock= tlink->tonode->inputs.first; sock; sock= sock->next) {
+                               if(sock->type==tlink->fromsock->type)
+                                       if(nodeCountSocketLinks(snode->edittree, sock) < sock->limit)
+                                               break;
+                       }
+                       if(sock)
+                               tlink->tosock= sock;
+                       else {
+                               nodeRemLink(snode->edittree, tlink);
+                       }
+               }
+       }
+}
+
 /* loop that adds a nodelink, called by function below  */
 /* in_out = starting socket */
 static int node_add_link_drag(SpaceNode *snode, bNode *node, bNodeSocket *sock, int in_out)
@@ -1638,35 +1682,12 @@ static int node_add_link_drag(SpaceNode *snode, bNode *node, bNodeSocket *sock,
                nodeRemLink(snode->edittree, link);
        }
        else {
-               bNodeLink *tlink;
-
                /* send changed events for original tonode and new */
                if(link->tonode) 
                        NodeTagChanged(snode->edittree, link->tonode);
                
                /* we might need to remove a link */
-               if(in_out==SOCK_OUT) {
-                       if(tsock && nodeCountSocketLinks(snode->edittree, link->tosock) > tsock->limit) {
-                               
-                               for(tlink= snode->edittree->links.first; tlink; tlink= tlink->next) {
-                                       if(link!=tlink && tlink->tosock==link->tosock)
-                                               break;
-                               }
-                               if(tlink) {
-                                       /* is there a free input socket with same type? */
-                                       for(tsock= tlink->tonode->inputs.first; tsock; tsock= tsock->next) {
-                                               if(tsock->type==tlink->fromsock->type)
-                                                       if(nodeCountSocketLinks(snode->edittree, tsock) < tsock->limit)
-                                                               break;
-                                       }
-                                       if(tsock)
-                                               tlink->tosock= tsock;
-                                       else {
-                                               nodeRemLink(snode->edittree, tlink);
-                                       }
-                               }                                       
-                       }
-               }
+               if(in_out==SOCK_OUT) node_remove_extra_links(snode, tsock, link);
        }
        
        ntreeSolveOrder(snode->edittree);
@@ -1733,10 +1754,18 @@ static int node_add_link(SpaceNode *snode)
 void node_delete(SpaceNode *snode)
 {
        bNode *node, *next;
+       bNodeSocket *sock;
        
        for(node= snode->edittree->nodes.first; node; node= next) {
                next= node->next;
                if(node->flag & SELECT) {
+                       /* set selin and selout NULL if the sockets belong to a node to be deleted */
+                       for(sock= node->inputs.first; sock; sock= sock->next)
+                               if(snode->edittree->selin == sock) snode->edittree->selin= NULL;
+
+                       for(sock= node->outputs.first; sock; sock= sock->next)
+                               if(snode->edittree->selout == sock) snode->edittree->selout= NULL;
+
                        /* check id user here, nodeFreeNode is called for free dbase too */
                        if(node->id)
                                node->id->us--;
@@ -1826,6 +1855,36 @@ void node_select_linked(SpaceNode *snode, int out)
        allqueue(REDRAWNODE, 1);
 }
 
+void node_toggle_link(SpaceNode *snode)
+{
+       bNode *fromnode, *tonode;
+       bNodeLink *remlink, *link;
+       bNodeSocket *outsock= snode->edittree->selout;
+       bNodeSocket *insock= snode->edittree->selin;
+
+       if(!insock || !outsock) return;
+
+       remlink= nodeFindLink(snode->edittree, outsock, insock);
+       
+       if(remlink) nodeRemLink(snode->edittree, remlink);
+       else {
+               if(nodeFindNode(snode->edittree, outsock, &fromnode, NULL) &&
+                  nodeFindNode(snode->edittree, insock, &tonode, NULL)) {
+                       link= nodeAddLink(snode->edittree, fromnode, outsock, tonode, insock);
+                       NodeTagChanged(snode->edittree, tonode);
+                       node_remove_extra_links(snode, insock, link);
+               }
+               else return;
+       }
+
+       ntreeSolveOrder(snode->edittree);
+       snode_verify_groups(snode);
+       snode_handle_recalc(snode);
+
+       allqueue(REDRAWNODE, 0);
+       BIF_undo_push("Toggle Link");
+}
+
 static void node_border_link_delete(SpaceNode *snode)
 {
        rcti rect;
@@ -2091,6 +2150,8 @@ static int node_uiDoBlocks(ScrArea *sa, short event)
 void winqreadnodespace(ScrArea *sa, void *spacedata, BWinEvent *evt)
 {
        SpaceNode *snode= spacedata;
+       bNode *actnode;
+       bNodeSocket *actsock;
        unsigned short event= evt->event;
        short val= evt->val, doredraw=0, fromlib= 0;
        
@@ -2118,7 +2179,29 @@ void winqreadnodespace(ScrArea *sa, void *spacedata, BWinEvent *evt)
                        break;
                        
                case RIGHTMOUSE: 
-                       if(!node_mouse_select(snode, event)) 
+                       if(find_indicated_socket(snode, &actnode, &actsock, SOCK_IN)) {
+                               if(actsock->flag & SOCK_SEL) {
+                                       snode->edittree->selin= NULL;
+                                       actsock->flag&= ~SOCK_SEL;
+                               }
+                               else {
+                                       snode->edittree->selin= actsock;
+                                       reset_sel_socket(snode, SOCK_IN);
+                                       actsock->flag|= SOCK_SEL;
+                               }
+                       }
+                       else if(find_indicated_socket(snode, &actnode, &actsock, SOCK_OUT)) {
+                               if(actsock->flag & SOCK_SEL) {
+                                       snode->edittree->selout= NULL;
+                                       actsock->flag&= ~SOCK_SEL;
+                               }
+                               else {
+                                       snode->edittree->selout= actsock;
+                                       reset_sel_socket(snode, SOCK_OUT);
+                                       actsock->flag|= SOCK_SEL;
+                               }
+                       }
+                       else if(!node_mouse_select(snode, event)) 
                                toolbox_n();
 
                        break;
@@ -2194,6 +2277,9 @@ void winqreadnodespace(ScrArea *sa, void *spacedata, BWinEvent *evt)
                case EKEY:
                        snode_handle_recalc(snode);
                        break;
+               case FKEY:
+                       node_toggle_link(snode);
+                       break;
                case GKEY:
                        if(fromlib) fromlib= -1;
                        else {
index ebdd42690f5271155ab0f6edce345e0db2292189..7255dcd328678ea0b5c9d792ec1971907d08620e 100644 (file)
@@ -513,6 +513,9 @@ static void do_node_nodemenu(void *arg, int event)
                case 10: /* execute */
                        addqueue(curarea->win, UI_BUT_EVENT, B_NODE_TREE_EXEC);
                        break;
+               case 11: /* toggle link */
+                       node_toggle_link(snode);
+                       break;
        }
        
        if(fromlib==-1) error_libdata();
@@ -536,6 +539,10 @@ static uiBlock *node_nodemenu(void *arg_unused)
        uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Duplicate|Shift D", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 1, 2, "");
        uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Delete|X", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 1, 3, "");
        
+       uiDefBut(block, SEPR, 0, "",        0, yco-=6, menuwidth, 6, NULL, 0.0, 0.0, 0, 0, "");
+
+       uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Toggle Link|F", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 1, 11, "");
+
        uiDefBut(block, SEPR, 0, "",        0, yco-=6, menuwidth, 6, NULL, 0.0, 0.0, 0, 0, "");
        
        uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Make Group|Ctrl G", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 1, 4, "");
index 969b9d4657b61b33f8aee48977be52467c0bff6f..244e1ffd7c3511e02004f8fe84ee7ac8670bd535 100644 (file)
@@ -1648,6 +1648,8 @@ static TBitem tb_node_node[]= {
        {       0, "Duplicate|Shift D", TB_SHIFT|'d',           NULL},
        {       0, "Delete|X", 'x',             NULL},
        {       0, "SEPR",              0, NULL},
+       {       0, "Toggle Link|F", 'f', NULL},
+       {       0, "SEPR",              0, NULL},
        {       0, "Make Group|Ctrl G", TB_CTRL|'g',            NULL},
        {       0, "Ungroup|Alt G", TB_ALT|'g',                 NULL},
        {       0, "Edit Group|Tab", TB_TAB, NULL},