Fix for bug #7875: node editor creates links to nowhere, fixes at
[plumiferos.git] / source / blender / src / editnode.c
blobe2356fb03dbd055577ea540d5e0c9448375801da
1 /**
2 * $Id:
4 * ***** BEGIN GPL LICENSE BLOCK *****
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software Foundation,
18 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20 * The Original Code is Copyright (C) 2005 Blender Foundation.
21 * All rights reserved.
23 * The Original Code is: all of this file.
25 * Contributor(s): none yet.
27 * ***** END GPL LICENSE BLOCK *****
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <math.h>
33 #include <string.h>
35 #include "MEM_guardedalloc.h"
37 #include "DNA_action_types.h"
38 #include "DNA_color_types.h"
39 #include "DNA_image_types.h"
40 #include "DNA_ipo_types.h"
41 #include "DNA_object_types.h"
42 #include "DNA_material_types.h"
43 #include "DNA_node_types.h"
44 #include "DNA_space_types.h"
45 #include "DNA_screen_types.h"
46 #include "DNA_scene_types.h"
47 #include "DNA_userdef_types.h"
49 #include "BKE_colortools.h"
50 #include "BKE_global.h"
51 #include "BKE_image.h"
52 #include "BKE_library.h"
53 #include "BKE_main.h"
54 #include "BKE_node.h"
55 #include "BKE_material.h"
56 #include "BKE_scene.h"
57 #include "BKE_utildefines.h"
59 #include "BIF_cursors.h"
60 #include "BIF_editview.h"
61 #include "BIF_gl.h"
62 #include "BIF_graphics.h"
63 #include "BIF_interface.h"
64 #include "BIF_mywindow.h"
65 #include "BIF_previewrender.h"
66 #include "BIF_resources.h"
67 #include "BIF_renderwin.h"
68 #include "BIF_space.h"
69 #include "BIF_screen.h"
70 #include "BIF_toolbox.h"
72 #include "BSE_drawipo.h"
73 #include "BSE_edit.h"
74 #include "BSE_filesel.h"
75 #include "BSE_headerbuttons.h"
76 #include "BSE_node.h"
78 #include "BLI_blenlib.h"
79 #include "BLI_arithb.h"
81 #include "BDR_editobject.h"
83 #include "RE_pipeline.h"
84 #include "IMB_imbuf_types.h"
86 #include "blendef.h"
87 #include "butspace.h"
88 #include "PIL_time.h"
89 #include "mydevice.h"
90 #include "winlay.h"
93 /* currently called from BIF_preview_changed */
94 void snode_tag_dirty(SpaceNode *snode)
96 bNode *node;
98 if(snode->treetype==NTREE_SHADER) {
99 if(snode->nodetree) {
100 for(node= snode->nodetree->nodes.first; node; node= node->next) {
101 if(node->type==SH_NODE_OUTPUT)
102 node->lasty= 0;
104 snode->flag |= SNODE_DO_PREVIEW; /* this adds an afterqueue on a redraw, to allow button previews to work first */
107 allqueue(REDRAWNODE, 1);
110 static void shader_node_previewrender(ScrArea *sa, SpaceNode *snode)
112 bNode *node;
114 if(snode->id==NULL) return;
115 if( ((Material *)snode->id )->use_nodes==0 ) return;
117 for(node= snode->nodetree->nodes.first; node; node= node->next) {
118 if(node->type==SH_NODE_OUTPUT) {
119 if(node->flag & NODE_DO_OUTPUT) {
120 if(node->lasty<PREVIEW_RENDERSIZE-2) {
121 RenderInfo ri;
122 // int test= node->lasty;
124 ri.curtile = 0;
125 ri.tottile = 0;
126 ri.rect = NULL;
127 ri.pr_rectx = PREVIEW_RENDERSIZE;
128 ri.pr_recty = PREVIEW_RENDERSIZE;
130 BIF_previewrender(snode->id, &ri, NULL, PR_DO_RENDER); /* sends redraw event */
131 if(ri.rect) MEM_freeN(ri.rect);
133 /* when not finished... */
134 if(ri.curtile<ri.tottile)
135 addafterqueue(sa->win, RENDERPREVIEW, 1);
136 // if(test!=node->lasty)
137 // printf("node rendered to %d\n", node->lasty);
139 break;
147 static void snode_handle_recalc(SpaceNode *snode)
149 if(snode->treetype==NTREE_SHADER) {
150 BIF_preview_changed(ID_MA); /* signals buttons windows and node editors */
152 else if(snode->treetype==NTREE_COMPOSIT) {
153 if(G.scene->use_nodes) {
154 snode->nodetree->timecursor= set_timecursor;
155 G.afbreek= 0;
156 snode->nodetree->test_break= blender_test_break;
158 BIF_store_spare();
160 ntreeCompositExecTree(snode->nodetree, &G.scene->r, 1); /* 1 is do_previews */
162 snode->nodetree->timecursor= NULL;
163 snode->nodetree->test_break= NULL;
164 waitcursor(0);
166 allqueue(REDRAWNODE, 1);
167 allqueue(REDRAWIMAGE, 1);
168 if(G.scene->r.scemode & R_DOCOMP) {
169 BIF_redraw_render_rect(); /* seems to screwup display? */
170 mywinset(curarea->win);
176 static void shader_node_event(SpaceNode *snode, short event)
178 switch(event) {
179 case B_REDR:
180 allqueue(REDRAWNODE, 1);
181 break;
182 default:
183 /* B_NODE_EXEC */
184 snode_handle_recalc(snode);
185 break;
190 static void load_node_image(char *str) /* called from fileselect */
192 SpaceNode *snode= curarea->spacedata.first;
193 bNode *node= nodeGetActive(snode->edittree);
194 Image *ima= NULL;
196 ima= BKE_add_image_file(str);
197 if(ima) {
198 if(node->id)
199 node->id->us--;
201 node->id= &ima->id;
202 id_us_plus(node->id);
204 BLI_strncpy(node->name, node->id->name+2, 21);
206 BKE_image_signal(ima, node->storage, IMA_SIGNAL_RELOAD);
208 NodeTagChanged(snode->edittree, node);
209 snode_handle_recalc(snode);
210 allqueue(REDRAWNODE, 0);
214 static bNode *snode_get_editgroup(SpaceNode *snode)
216 bNode *gnode;
218 /* get the groupnode */
219 for(gnode= snode->nodetree->nodes.first; gnode; gnode= gnode->next)
220 if(gnode->flag & NODE_GROUP_EDIT)
221 break;
222 return gnode;
225 /* node has to be of type 'render layers' */
226 /* is a bit clumsy copying renderdata here... scene nodes use render size of current render */
227 static void composite_node_render(SpaceNode *snode, bNode *node)
229 RenderData rd;
230 Scene *scene= NULL;
231 int scemode, actlay;
233 /* the button press won't show up otherwise, button hilites disabled */
234 force_draw(0);
236 if(node->id && node->id!=(ID *)G.scene) {
237 scene= G.scene;
238 set_scene_bg((Scene *)node->id);
239 rd= G.scene->r;
240 G.scene->r.xsch= scene->r.xsch;
241 G.scene->r.ysch= scene->r.ysch;
242 G.scene->r.size= scene->r.size;
243 G.scene->r.mode &= ~(R_BORDER|R_DOCOMP);
244 G.scene->r.mode |= scene->r.mode & R_BORDER;
245 G.scene->r.border= scene->r.border;
246 G.scene->r.cfra= scene->r.cfra;
249 scemode= G.scene->r.scemode;
250 actlay= G.scene->r.actlay;
252 G.scene->r.scemode |= R_SINGLE_LAYER;
253 G.scene->r.actlay= node->custom1;
255 BIF_do_render(0);
257 G.scene->r.scemode= scemode;
258 G.scene->r.actlay= actlay;
260 node->custom2= 0;
262 if(scene) {
263 G.scene->r= rd;
264 set_scene_bg(scene);
268 static void composit_node_event(SpaceNode *snode, short event)
271 switch(event) {
272 case B_REDR:
273 allqueue(REDRAWNODE, 1);
274 break;
275 case B_NODE_LOADIMAGE:
277 bNode *node= nodeGetActive(snode->edittree);
278 char name[FILE_MAXDIR+FILE_MAXFILE];
280 if(node->id)
281 strcpy(name, ((Image *)node->id)->name);
282 else strcpy(name, U.textudir);
284 activate_fileselect(FILE_SPECIAL, "SELECT IMAGE", name, load_node_image);
285 break;
287 case B_NODE_TREE_EXEC:
288 snode_handle_recalc(snode);
289 break;
290 default:
291 /* B_NODE_EXEC */
293 bNode *node= BLI_findlink(&snode->edittree->nodes, event-B_NODE_EXEC);
294 if(node) {
295 NodeTagChanged(snode->edittree, node);
296 /* don't use NodeTagIDChanged, it gives far too many recomposites for image, scene layers, ... */
298 /* not the best implementation of the world... but we need it to work now :) */
299 if(node->type==CMP_NODE_R_LAYERS && node->custom2) {
300 /* new event, a render can go fullscreen and open new window */
301 addqueue(curarea->win, UI_BUT_EVENT, B_NODE_TREE_EXEC);
302 composite_node_render(snode, node);
303 snode_handle_recalc(snode);
305 else {
306 node= snode_get_editgroup(snode);
307 if(node)
308 NodeTagIDChanged(snode->nodetree, node->id);
310 snode_handle_recalc(snode);
318 /* assumes nothing being done in ntree yet, sets the default in/out node */
319 /* called from shading buttons or header */
320 void node_shader_default(Material *ma)
322 bNode *in, *out;
323 bNodeSocket *fromsock, *tosock;
325 /* but lets check it anyway */
326 if(ma->nodetree) {
327 printf("error in shader initialize\n");
328 return;
331 ma->nodetree= ntreeAddTree(NTREE_SHADER);
333 out= nodeAddNodeType(ma->nodetree, SH_NODE_OUTPUT, NULL);
334 out->locx= 300.0f; out->locy= 300.0f;
336 in= nodeAddNodeType(ma->nodetree, SH_NODE_MATERIAL, NULL);
337 in->locx= 10.0f; in->locy= 300.0f;
338 nodeSetActive(ma->nodetree, in);
340 /* only a link from color to color */
341 fromsock= in->outputs.first;
342 tosock= out->inputs.first;
343 nodeAddLink(ma->nodetree, in, fromsock, out, tosock);
345 ntreeSolveOrder(ma->nodetree); /* needed for pointers */
348 /* assumes nothing being done in ntree yet, sets the default in/out node */
349 /* called from shading buttons or header */
350 void node_composit_default(Scene *sce)
352 bNode *in, *out;
353 bNodeSocket *fromsock, *tosock;
355 /* but lets check it anyway */
356 if(sce->nodetree) {
357 printf("error in composit initialize\n");
358 return;
361 sce->nodetree= ntreeAddTree(NTREE_COMPOSIT);
363 out= nodeAddNodeType(sce->nodetree, CMP_NODE_COMPOSITE, NULL);
364 out->locx= 300.0f; out->locy= 400.0f;
366 in= nodeAddNodeType(sce->nodetree, CMP_NODE_R_LAYERS, NULL);
367 in->locx= 10.0f; in->locy= 400.0f;
368 nodeSetActive(sce->nodetree, in);
370 /* links from color to color */
371 fromsock= in->outputs.first;
372 tosock= out->inputs.first;
373 nodeAddLink(sce->nodetree, in, fromsock, out, tosock);
375 ntreeSolveOrder(sce->nodetree); /* needed for pointers */
377 ntreeCompositForceHidden(sce->nodetree);
380 /* Here we set the active tree(s), even called for each redraw now, so keep it fast :) */
381 void snode_set_context(SpaceNode *snode)
383 Object *ob= OBACT;
384 bNode *node= NULL;
386 snode->nodetree= NULL;
387 snode->id= snode->from= NULL;
389 if(snode->treetype==NTREE_SHADER) {
390 /* need active object, or we allow pinning... */
391 if(ob) {
392 Material *ma= give_current_material(ob, ob->actcol);
393 if(ma) {
394 snode->from= material_from(ob, ob->actcol);
395 snode->id= &ma->id;
396 snode->nodetree= ma->nodetree;
400 else if(snode->treetype==NTREE_COMPOSIT) {
401 snode->from= NULL;
402 snode->id= &G.scene->id;
404 /* bit clumsy but reliable way to see if we draw first time */
405 if(snode->nodetree==NULL)
406 ntreeCompositForceHidden(G.scene->nodetree);
408 snode->nodetree= G.scene->nodetree;
411 /* find editable group */
412 if(snode->nodetree)
413 for(node= snode->nodetree->nodes.first; node; node= node->next)
414 if(node->flag & NODE_GROUP_EDIT)
415 break;
417 if(node && node->id)
418 snode->edittree= (bNodeTree *)node->id;
419 else
420 snode->edittree= snode->nodetree;
423 /* on activate image viewer, check if we show it */
424 static void node_active_image(Image *ima)
426 ScrArea *sa;
427 SpaceImage *sima= NULL;
429 /* find an imagewindow showing render result */
430 for(sa=G.curscreen->areabase.first; sa; sa= sa->next) {
431 if(sa->spacetype==SPACE_IMAGE) {
432 sima= sa->spacedata.first;
433 if(sima->image && sima->image->source!=IMA_SRC_VIEWER)
434 break;
437 if(sa && sima) {
438 sima->image= ima;
439 scrarea_queue_winredraw(sa);
440 scrarea_queue_headredraw(sa);
445 static void node_set_active(SpaceNode *snode, bNode *node)
448 nodeSetActive(snode->edittree, node);
450 if(node->type!=NODE_GROUP) {
452 /* tree specific activate calls */
453 if(snode->treetype==NTREE_SHADER) {
455 /* when we select a material, active texture is cleared, for buttons */
456 if(node->id && GS(node->id->name)==ID_MA)
457 nodeClearActiveID(snode->edittree, ID_TE);
458 if(node->id)
459 BIF_preview_changed(-1); /* temp hack to force texture preview to update */
461 allqueue(REDRAWBUTSSHADING, 1);
462 allqueue(REDRAWIPO, 0);
464 else if(snode->treetype==NTREE_COMPOSIT) {
465 /* make active viewer, currently only 1 supported... */
466 if( ELEM(node->type, CMP_NODE_VIEWER, CMP_NODE_SPLITVIEWER)) {
467 bNode *tnode;
468 int was_output= node->flag & NODE_DO_OUTPUT;
470 for(tnode= snode->edittree->nodes.first; tnode; tnode= tnode->next)
471 if( ELEM(tnode->type, CMP_NODE_VIEWER, CMP_NODE_SPLITVIEWER))
472 tnode->flag &= ~NODE_DO_OUTPUT;
474 node->flag |= NODE_DO_OUTPUT;
475 if(was_output==0) {
476 bNode *gnode;
478 NodeTagChanged(snode->edittree, node);
480 /* if inside group, tag entire group */
481 gnode= snode_get_editgroup(snode);
482 if(gnode)
483 NodeTagIDChanged(snode->nodetree, gnode->id);
485 snode_handle_recalc(snode);
488 /* addnode() doesnt link this yet... */
489 node->id= (ID *)BKE_image_verify_viewer(IMA_TYPE_COMPOSITE, "Viewer Node");
491 else if(node->type==CMP_NODE_IMAGE) {
492 if(node->id)
493 node_active_image((Image *)node->id);
495 else if(node->type==CMP_NODE_R_LAYERS) {
496 if(node->id==NULL || node->id==(ID *)G.scene) {
497 G.scene->r.actlay= node->custom1;
498 allqueue(REDRAWBUTSSCENE, 0);
505 void snode_make_group_editable(SpaceNode *snode, bNode *gnode)
507 bNode *node;
509 /* make sure nothing has group editing on */
510 for(node= snode->nodetree->nodes.first; node; node= node->next)
511 node->flag &= ~NODE_GROUP_EDIT;
513 if(gnode==NULL) {
514 /* with NULL argument we do a toggle */
515 if(snode->edittree==snode->nodetree)
516 gnode= nodeGetActive(snode->nodetree);
519 if(gnode && gnode->type==NODE_GROUP && gnode->id) {
520 if(gnode->id->lib) {
521 if(okee("Make Group Local"))
522 ntreeMakeLocal((bNodeTree *)gnode->id);
523 else
524 return;
526 gnode->flag |= NODE_GROUP_EDIT;
527 snode->edittree= (bNodeTree *)gnode->id;
529 /* deselect all other nodes, so we can also do grabbing of entire subtree */
530 for(node= snode->nodetree->nodes.first; node; node= node->next)
531 node->flag &= ~SELECT;
532 gnode->flag |= SELECT;
535 else
536 snode->edittree= snode->nodetree;
538 ntreeSolveOrder(snode->nodetree);
540 /* finally send out events for new active node */
541 if(snode->treetype==NTREE_SHADER) {
542 allqueue(REDRAWBUTSSHADING, 0);
544 BIF_preview_changed(-1); /* temp hack to force texture preview to update */
547 allqueue(REDRAWNODE, 0);
550 void node_ungroup(SpaceNode *snode)
552 bNode *gnode;
554 /* are we inside of a group? */
555 gnode= snode_get_editgroup(snode);
556 if(gnode)
557 snode_make_group_editable(snode, NULL);
559 gnode= nodeGetActive(snode->edittree);
560 if(gnode==NULL) return;
562 if(gnode->type!=NODE_GROUP)
563 error("Not a group");
564 else {
565 if(nodeGroupUnGroup(snode->edittree, gnode)) {
567 BIF_undo_push("Deselect all nodes");
568 allqueue(REDRAWNODE, 0);
570 else
571 error("Can't ungroup");
575 /* when links in groups change, inputs/outputs change, nodes added/deleted... */
576 static void snode_verify_groups(SpaceNode *snode)
578 bNode *gnode;
580 gnode= snode_get_editgroup(snode);
582 /* does all materials */
583 if(gnode)
584 nodeVerifyGroup((bNodeTree *)gnode->id);
588 static void node_addgroup(SpaceNode *snode)
590 bNodeTree *ngroup;
591 int tot= 0, offs, val;
592 char *strp;
594 if(snode->edittree!=snode->nodetree) {
595 error("Can not add a Group in a Group");
596 return;
599 /* construct menu with choices */
600 for(ngroup= G.main->nodetree.first; ngroup; ngroup= ngroup->id.next) {
601 if(ngroup->type==snode->treetype)
602 tot++;
604 if(tot==0) {
605 error("No groups available in database");
606 return;
608 strp= MEM_mallocN(32*tot+32, "menu");
609 strcpy(strp, "Add Group %t");
610 offs= strlen(strp);
612 for(tot=0, ngroup= G.main->nodetree.first; ngroup; ngroup= ngroup->id.next, tot++) {
613 if(ngroup->type==snode->treetype)
614 offs+= sprintf(strp+offs, "|%s %%x%d", ngroup->id.name+2, tot);
617 val= pupmenu(strp);
618 if(val>=0) {
619 ngroup= BLI_findlink(&G.main->nodetree, val);
620 if(ngroup) {
621 bNode *node= nodeAddNodeType(snode->edittree, NODE_GROUP, ngroup);
623 /* generics */
624 if(node) {
625 float locx, locy;
626 short mval[2];
628 node_deselectall(snode, 0);
630 getmouseco_areawin(mval);
631 areamouseco_to_ipoco(G.v2d, mval, &locx, &locy);
633 node->locx= locx;
634 node->locy= locy + 60.0f; // arbitrary.. so its visible
635 node->flag |= SELECT;
637 id_us_plus(node->id);
639 node_set_active(snode, node);
640 BIF_undo_push("Add Node");
644 MEM_freeN(strp);
648 /* ************************** Node generic ************** */
650 /* allows to walk the list in order of visibility */
651 static bNode *next_node(bNodeTree *ntree)
653 static bNode *current=NULL, *last= NULL;
655 if(ntree) {
656 /* set current to the first selected node */
657 for(current= ntree->nodes.last; current; current= current->prev)
658 if(current->flag & NODE_SELECT)
659 break;
661 /* set last to the first unselected node */
662 for(last= ntree->nodes.last; last; last= last->prev)
663 if((last->flag & NODE_SELECT)==0)
664 break;
666 if(current==NULL)
667 current= last;
669 return NULL;
671 /* no nodes, or we are ready */
672 if(current==NULL)
673 return NULL;
675 /* now we walk the list backwards, but we always return current */
676 if(current->flag & NODE_SELECT) {
677 bNode *node= current;
679 /* find previous selected */
680 current= current->prev;
681 while(current && (current->flag & NODE_SELECT)==0)
682 current= current->prev;
684 /* find first unselected */
685 if(current==NULL)
686 current= last;
688 return node;
690 else {
691 bNode *node= current;
693 /* find previous unselected */
694 current= current->prev;
695 while(current && (current->flag & NODE_SELECT))
696 current= current->prev;
698 return node;
701 return NULL;
704 /* is rct in visible part of node? */
705 static bNode *visible_node(SpaceNode *snode, rctf *rct)
707 bNode *tnode;
709 for(next_node(snode->edittree); (tnode=next_node(NULL));) {
710 if(BLI_isect_rctf(&tnode->totr, rct, NULL))
711 break;
713 return tnode;
716 void snode_home(ScrArea *sa, SpaceNode *snode)
718 bNode *node;
719 int first= 1;
721 snode->v2d.cur.xmin= snode->v2d.cur.ymin= 0.0f;
722 snode->v2d.cur.xmax= sa->winx;
723 snode->v2d.cur.xmax= sa->winy;
725 if(snode->edittree) {
726 for(node= snode->edittree->nodes.first; node; node= node->next) {
727 if(first) {
728 first= 0;
729 snode->v2d.cur= node->totr;
731 else {
732 BLI_union_rctf(&snode->v2d.cur, &node->totr);
736 snode->v2d.tot= snode->v2d.cur;
738 snode->xof = snode->yof = 0.0;
740 test_view2d(G.v2d, sa->winx, sa->winy);
744 void snode_zoom_out(ScrArea *sa)
746 float dx;
748 dx= (float)(0.15*(G.v2d->cur.xmax-G.v2d->cur.xmin));
749 G.v2d->cur.xmin-= dx;
750 G.v2d->cur.xmax+= dx;
751 dx= (float)(0.15*(G.v2d->cur.ymax-G.v2d->cur.ymin));
752 G.v2d->cur.ymin-= dx;
753 G.v2d->cur.ymax+= dx;
754 test_view2d(G.v2d, sa->winx, sa->winy);
757 void snode_zoom_in(ScrArea *sa)
759 float dx;
761 dx= (float)(0.1154*(G.v2d->cur.xmax-G.v2d->cur.xmin));
762 G.v2d->cur.xmin+= dx;
763 G.v2d->cur.xmax-= dx;
764 dx= (float)(0.1154*(G.v2d->cur.ymax-G.v2d->cur.ymin));
765 G.v2d->cur.ymin+= dx;
766 G.v2d->cur.ymax-= dx;
767 test_view2d(G.v2d, sa->winx, sa->winy);
770 static void snode_bg_viewmove(SpaceNode *snode)
772 ScrArea *sa;
773 Image *ima;
774 ImBuf *ibuf;
775 Window *win;
776 short mval[2], mvalo[2];
777 short rectx, recty, xmin, xmax, ymin, ymax, pad;
778 int oldcursor;
780 ima= BKE_image_verify_viewer(IMA_TYPE_COMPOSITE, "Viewer Node");
781 ibuf= BKE_image_get_ibuf(ima, NULL);
783 sa = snode->area;
785 if(ibuf) {
786 rectx = ibuf->x;
787 recty = ibuf->y;
788 } else {
789 rectx = recty = 1;
792 pad = 10;
793 xmin = -(sa->winx/2) - rectx/2 + pad;
794 xmax = sa->winx/2 + rectx/2 - pad;
795 ymin = -(sa->winy/2) - recty/2 + pad;
796 ymax = sa->winy/2 + recty/2 - pad;
798 getmouseco_sc(mvalo);
800 /* store the old cursor to temporarily change it */
801 oldcursor=get_cursor();
802 win=winlay_get_active_window();
804 SetBlenderCursor(BC_NSEW_SCROLLCURSOR);
806 while(get_mbut()&(L_MOUSE|M_MOUSE)) {
808 getmouseco_sc(mval);
810 if(mvalo[0]!=mval[0] || mvalo[1]!=mval[1]) {
812 snode->xof -= (mvalo[0]-mval[0]);
813 snode->yof -= (mvalo[1]-mval[1]);
815 /* prevent dragging image outside of the window and losing it! */
816 CLAMP(snode->xof, xmin, xmax);
817 CLAMP(snode->yof, ymin, ymax);
819 mvalo[0]= mval[0];
820 mvalo[1]= mval[1];
822 scrarea_do_windraw(curarea);
823 screen_swapbuffers();
825 else BIF_wait_for_statechange();
828 window_set_cursor(win, oldcursor);
831 static void reset_sel_socket(SpaceNode *snode, int in_out)
833 bNode *node;
834 bNodeSocket *sock;
836 for(node= snode->edittree->nodes.first; node; node= node->next) {
837 if(in_out & SOCK_IN) {
838 for(sock= node->inputs.first; sock; sock= sock->next)
839 if(sock->flag & SOCK_SEL) sock->flag&= ~SOCK_SEL;
841 if(in_out & SOCK_OUT) {
842 for(sock= node->outputs.first; sock; sock= sock->next)
843 if(sock->flag & SOCK_SEL) sock->flag&= ~SOCK_SEL;
848 /* checks mouse position, and returns found node/socket */
849 /* type is SOCK_IN and/or SOCK_OUT */
850 static int find_indicated_socket(SpaceNode *snode, bNode **nodep, bNodeSocket **sockp, int in_out)
852 bNode *node;
853 bNodeSocket *sock;
854 rctf rect;
855 short mval[2];
857 getmouseco_areawin(mval);
859 /* check if we click in a socket */
860 for(node= snode->edittree->nodes.first; node; node= node->next) {
862 areamouseco_to_ipoco(G.v2d, mval, &rect.xmin, &rect.ymin);
864 rect.xmin -= NODE_SOCKSIZE+3;
865 rect.ymin -= NODE_SOCKSIZE+3;
866 rect.xmax = rect.xmin + 2*NODE_SOCKSIZE+6;
867 rect.ymax = rect.ymin + 2*NODE_SOCKSIZE+6;
869 if (!(node->flag & NODE_HIDDEN)) {
870 /* extra padding inside and out - allow dragging on the text areas too */
871 if (in_out == SOCK_IN) {
872 rect.xmax += NODE_SOCKSIZE;
873 rect.xmin -= NODE_SOCKSIZE*4;
874 } else if (in_out == SOCK_OUT) {
875 rect.xmax += NODE_SOCKSIZE*4;
876 rect.xmin -= NODE_SOCKSIZE;
880 if(in_out & SOCK_IN) {
881 for(sock= node->inputs.first; sock; sock= sock->next) {
882 if(!(sock->flag & (SOCK_HIDDEN|SOCK_UNAVAIL))) {
883 if(BLI_in_rctf(&rect, sock->locx, sock->locy)) {
884 if(node == visible_node(snode, &rect)) {
885 *nodep= node;
886 *sockp= sock;
887 return 1;
893 if(in_out & SOCK_OUT) {
894 for(sock= node->outputs.first; sock; sock= sock->next) {
895 if(!(sock->flag & (SOCK_HIDDEN|SOCK_UNAVAIL))) {
896 if(BLI_in_rctf(&rect, sock->locx, sock->locy)) {
897 if(node == visible_node(snode, &rect)) {
898 *nodep= node;
899 *sockp= sock;
900 return 1;
907 return 0;
910 /* ********************* transform ****************** */
912 /* releases on event, only intern (for extern see below) */
913 /* we need argument ntree to allow operations on edittree or nodetree */
914 static void transform_nodes(bNodeTree *ntree, char mode, char *undostr)
916 bNode *node;
917 float mxstart, mystart, mx, my, *oldlocs, *ol;
918 int cont=1, tot=0, cancel=0, firsttime=1;
919 short mval[2], mvalo[2];
921 /* count total */
922 for(node= ntree->nodes.first; node; node= node->next)
923 if(node->flag & SELECT) tot++;
925 if(tot==0) return;
927 /* store oldlocs */
928 ol= oldlocs= MEM_mallocN(sizeof(float)*2*tot, "oldlocs transform");
929 for(node= ntree->nodes.first; node; node= node->next) {
930 if(node->flag & SELECT) {
931 ol[0]= node->locx; ol[1]= node->locy;
932 ol+= 2;
936 getmouseco_areawin(mvalo);
937 areamouseco_to_ipoco(G.v2d, mvalo, &mxstart, &mystart);
939 while(cont) {
941 getmouseco_areawin(mval);
942 if(mval[0]!=mvalo[0] || mval[1]!=mvalo[1] || firsttime) {
944 firsttime= 0;
946 areamouseco_to_ipoco(G.v2d, mval, &mx, &my);
947 mvalo[0]= mval[0];
948 mvalo[1]= mval[1];
950 for(ol= oldlocs, node= ntree->nodes.first; node; node= node->next) {
951 if(node->flag & SELECT) {
952 node->locx= ol[0] + mx-mxstart;
953 node->locy= ol[1] + my-mystart;
954 ol+= 2;
958 force_draw(0);
960 else
961 PIL_sleep_ms(10);
963 while (qtest()) {
964 short val;
965 unsigned short event= extern_qread(&val);
967 switch (event) {
968 case LEFTMOUSE:
969 case SPACEKEY:
970 case RETKEY:
971 cont=0;
972 break;
973 case ESCKEY:
974 case RIGHTMOUSE:
975 if(val) {
976 cancel=1;
977 cont=0;
979 break;
980 default:
981 if(val) arrows_move_cursor(event);
982 break;
988 if(cancel) {
989 for(ol= oldlocs, node= ntree->nodes.first; node; node= node->next) {
990 if(node->flag & SELECT) {
991 node->locx= ol[0];
992 node->locy= ol[1];
993 ol+= 2;
998 else
999 BIF_undo_push(undostr);
1001 allqueue(REDRAWNODE, 1);
1002 MEM_freeN(oldlocs);
1005 /* external call, also for callback */
1006 void node_transform_ext(int mode, int unused)
1008 SpaceNode *snode= curarea->spacedata.first;
1010 transform_nodes(snode->edittree, 'g', "Move Node");
1014 /* releases on event, only 1 node */
1015 static void scale_node(SpaceNode *snode, bNode *node)
1017 float mxstart, mystart, mx, my, oldwidth;
1018 int cont=1, cancel=0;
1019 short mval[2], mvalo[2];
1021 /* store old */
1022 if(node->flag & NODE_HIDDEN)
1023 oldwidth= node->miniwidth;
1024 else
1025 oldwidth= node->width;
1027 getmouseco_areawin(mvalo);
1028 areamouseco_to_ipoco(G.v2d, mvalo, &mxstart, &mystart);
1030 while(cont) {
1032 getmouseco_areawin(mval);
1033 if(mval[0]!=mvalo[0] || mval[1]!=mvalo[1]) {
1035 areamouseco_to_ipoco(G.v2d, mval, &mx, &my);
1036 mvalo[0]= mval[0];
1037 mvalo[1]= mval[1];
1039 if(node->flag & NODE_HIDDEN) {
1040 node->miniwidth= oldwidth + mx-mxstart;
1041 CLAMP(node->miniwidth, 0.0f, 100.0f);
1043 else {
1044 node->width= oldwidth + mx-mxstart;
1045 CLAMP(node->width, node->typeinfo->minwidth, node->typeinfo->maxwidth);
1048 force_draw(0);
1050 else
1051 PIL_sleep_ms(10);
1053 while (qtest()) {
1054 short val;
1055 unsigned short event= extern_qread(&val);
1057 switch (event) {
1058 case LEFTMOUSE:
1059 case SPACEKEY:
1060 case RETKEY:
1061 cont=0;
1062 break;
1063 case ESCKEY:
1064 case RIGHTMOUSE:
1065 if(val) {
1066 cancel=1;
1067 cont=0;
1069 break;
1075 if(cancel) {
1076 node->width= oldwidth;
1078 else
1079 BIF_undo_push("Scale Node");
1081 allqueue(REDRAWNODE, 1);
1084 void node_rename_cb(SpaceNode *snode)
1086 bNode *node;
1088 for(node= snode->edittree->nodes.first; node; node= node->next) {
1089 if(node->flag & SELECT) {
1090 draw_node_rename_panel(node->name);
1091 allqueue(REDRAWNODE, 1);
1092 break;
1097 /* ********************** select ******************** */
1099 /* used in buttons to check context, also checks for edited groups */
1100 bNode *editnode_get_active_idnode(bNodeTree *ntree, short id_code)
1102 return nodeGetActiveID(ntree, id_code);
1105 /* used in buttons to check context, also checks for edited groups */
1106 Material *editnode_get_active_material(Material *ma)
1108 if(ma && ma->use_nodes && ma->nodetree) {
1109 bNode *node= editnode_get_active_idnode(ma->nodetree, ID_MA);
1110 if(node)
1111 return (Material *)node->id;
1112 else
1113 return NULL;
1115 return ma;
1118 /* used in buttons to check context, also checks for edited groups */
1119 bNode *editnode_get_active(bNodeTree *ntree)
1121 bNode *node;
1123 /* check for edited group */
1124 for(node= ntree->nodes.first; node; node= node->next)
1125 if(node->flag & NODE_GROUP_EDIT)
1126 break;
1127 if(node)
1128 return nodeGetActive((bNodeTree *)node->id);
1129 else
1130 return nodeGetActive(ntree);
1133 /* select the same type, no undo here!! */
1134 void node_select_same_type(SpaceNode *snode)
1136 bNode *ac, *p;
1137 int redraw;
1139 for (ac= snode->edittree->nodes.first; ac; ac= ac->next) {
1140 if (ac->flag & SELECT)
1141 break;
1144 if (!ac)
1145 return;
1147 redraw= 0;
1148 for (p= snode->edittree->nodes.first; p; p= p->next) {
1149 if (p->type != ac->type && p->flag & SELECT) {
1150 redraw= 1;
1151 p->flag&= ~SELECT;
1153 else if (p->type == ac->type && (!(p->flag & SELECT))) {
1154 redraw= 1;
1155 p->flag|= SELECT;
1159 if (redraw)
1160 allqueue(REDRAWNODE, 0);
1163 /* no undo here! */
1164 void node_deselectall(SpaceNode *snode, int swap)
1166 bNode *node;
1168 if(swap) {
1169 for(node= snode->edittree->nodes.first; node; node= node->next)
1170 if(node->flag & SELECT)
1171 break;
1172 if(node==NULL) {
1173 for(node= snode->edittree->nodes.first; node; node= node->next)
1174 node->flag |= SELECT;
1175 allqueue(REDRAWNODE, 0);
1176 return;
1178 /* else pass on to deselect */
1181 for(node= snode->edittree->nodes.first; node; node= node->next)
1182 node->flag &= ~SELECT;
1184 allqueue(REDRAWNODE, 0);
1187 int node_has_hidden_sockets(bNode *node)
1189 bNodeSocket *sock;
1191 for(sock= node->inputs.first; sock; sock= sock->next)
1192 if(sock->flag & SOCK_HIDDEN)
1193 return 1;
1194 for(sock= node->outputs.first; sock; sock= sock->next)
1195 if(sock->flag & SOCK_HIDDEN)
1196 return 1;
1197 return 0;
1201 static void node_hide_unhide_sockets(SpaceNode *snode, bNode *node)
1203 bNodeSocket *sock;
1205 /* unhide all */
1206 if( node_has_hidden_sockets(node) ) {
1207 for(sock= node->inputs.first; sock; sock= sock->next)
1208 sock->flag &= ~SOCK_HIDDEN;
1209 for(sock= node->outputs.first; sock; sock= sock->next)
1210 sock->flag &= ~SOCK_HIDDEN;
1212 else {
1213 bNode *gnode= snode_get_editgroup(snode);
1215 /* hiding inside group should not break links in other group users */
1216 if(gnode) {
1217 nodeGroupSocketUseFlags((bNodeTree *)gnode->id);
1218 for(sock= node->inputs.first; sock; sock= sock->next)
1219 if(!(sock->flag & SOCK_IN_USE))
1220 if(sock->link==NULL)
1221 sock->flag |= SOCK_HIDDEN;
1222 for(sock= node->outputs.first; sock; sock= sock->next)
1223 if(!(sock->flag & SOCK_IN_USE))
1224 if(nodeCountSocketLinks(snode->edittree, sock)==0)
1225 sock->flag |= SOCK_HIDDEN;
1227 else {
1228 /* hide unused sockets */
1229 for(sock= node->inputs.first; sock; sock= sock->next) {
1230 if(sock->link==NULL)
1231 sock->flag |= SOCK_HIDDEN;
1233 for(sock= node->outputs.first; sock; sock= sock->next) {
1234 if(nodeCountSocketLinks(snode->edittree, sock)==0)
1235 sock->flag |= SOCK_HIDDEN;
1240 allqueue(REDRAWNODE, 1);
1241 snode_verify_groups(snode);
1242 BIF_undo_push("Hide/Unhide sockets");
1246 static int do_header_node(SpaceNode *snode, bNode *node, float mx, float my)
1248 rctf totr= node->totr;
1250 totr.ymin= totr.ymax-20.0f;
1252 totr.xmax= totr.xmin+15.0f;
1253 if(BLI_in_rctf(&totr, mx, my)) {
1254 node->flag |= NODE_HIDDEN;
1255 allqueue(REDRAWNODE, 0);
1256 return 1;
1259 totr.xmax= node->totr.xmax;
1260 totr.xmin= totr.xmax-18.0f;
1261 if(node->typeinfo->flag & NODE_PREVIEW) {
1262 if(BLI_in_rctf(&totr, mx, my)) {
1263 node->flag ^= NODE_PREVIEW;
1264 allqueue(REDRAWNODE, 0);
1265 return 1;
1267 totr.xmin-=18.0f;
1269 if(node->type == NODE_GROUP) {
1270 if(BLI_in_rctf(&totr, mx, my)) {
1271 snode_make_group_editable(snode, node);
1272 return 1;
1274 totr.xmin-=18.0f;
1276 if(node->typeinfo->flag & NODE_OPTIONS) {
1277 if(BLI_in_rctf(&totr, mx, my)) {
1278 node->flag ^= NODE_OPTIONS;
1279 allqueue(REDRAWNODE, 0);
1280 return 1;
1282 totr.xmin-=18.0f;
1284 /* hide unused sockets */
1285 if(BLI_in_rctf(&totr, mx, my)) {
1286 node_hide_unhide_sockets(snode, node);
1290 totr= node->totr;
1291 totr.xmin= totr.xmax-10.0f;
1292 totr.ymax= totr.ymin+10.0f;
1293 if(BLI_in_rctf(&totr, mx, my)) {
1294 scale_node(snode, node);
1295 return 1;
1297 return 0;
1300 static int do_header_hidden_node(SpaceNode *snode, bNode *node, float mx, float my)
1302 rctf totr= node->totr;
1304 totr.xmax= totr.xmin+15.0f;
1305 if(BLI_in_rctf(&totr, mx, my)) {
1306 node->flag &= ~NODE_HIDDEN;
1307 allqueue(REDRAWNODE, 0);
1308 return 1;
1311 totr.xmax= node->totr.xmax;
1312 totr.xmin= node->totr.xmax-15.0f;
1313 if(BLI_in_rctf(&totr, mx, my)) {
1314 scale_node(snode, node);
1315 return 1;
1317 return 0;
1320 static void node_link_viewer(SpaceNode *snode, bNode *tonode)
1322 bNode *node;
1324 /* context check */
1325 if(tonode==NULL || tonode->outputs.first==NULL)
1326 return;
1327 if( ELEM(tonode->type, CMP_NODE_VIEWER, CMP_NODE_SPLITVIEWER))
1328 return;
1330 /* get viewer */
1331 for(node= snode->edittree->nodes.first; node; node= node->next)
1332 if( ELEM(node->type, CMP_NODE_VIEWER, CMP_NODE_SPLITVIEWER))
1333 if(node->flag & NODE_DO_OUTPUT)
1334 break;
1336 if(node) {
1337 bNodeLink *link;
1339 /* get link to viewer */
1340 for(link= snode->edittree->links.first; link; link= link->next)
1341 if(link->tonode==node)
1342 break;
1344 if(link) {
1345 link->fromnode= tonode;
1346 link->fromsock= tonode->outputs.first;
1347 NodeTagChanged(snode->edittree, node);
1349 snode_handle_recalc(snode);
1355 void node_active_link_viewer(SpaceNode *snode)
1357 bNode *node= editnode_get_active(snode->edittree);
1358 if(node)
1359 node_link_viewer(snode, node);
1362 /* return 0: nothing done */
1363 static int node_mouse_select(SpaceNode *snode, unsigned short event)
1365 bNode *node;
1366 float mx, my;
1367 short mval[2];
1369 getmouseco_areawin(mval);
1370 areamouseco_to_ipoco(G.v2d, mval, &mx, &my);
1372 for(next_node(snode->edittree); (node=next_node(NULL));) {
1374 /* first check for the headers or scaling widget */
1375 if(node->flag & NODE_HIDDEN) {
1376 if(do_header_hidden_node(snode, node, mx, my))
1377 return 1;
1379 else {
1380 if(do_header_node(snode, node, mx, my))
1381 return 1;
1384 /* node body */
1385 if(BLI_in_rctf(&node->totr, mx, my))
1386 break;
1388 if(node) {
1389 if((G.qual & LR_SHIFTKEY)==0)
1390 node_deselectall(snode, 0);
1392 if(G.qual & LR_SHIFTKEY) {
1393 if(node->flag & SELECT)
1394 node->flag &= ~SELECT;
1395 else
1396 node->flag |= SELECT;
1398 else
1399 node->flag |= SELECT;
1401 node_set_active(snode, node);
1403 /* viewer linking */
1404 if(G.qual & LR_CTRLKEY)
1405 node_link_viewer(snode, node);
1407 /* not so nice (no event), but function below delays redraw otherwise */
1408 force_draw(0);
1410 std_rmouse_transform(node_transform_ext); /* does undo push for select */
1412 return 1;
1414 return 0;
1417 /* return 0, nothing done */
1418 static int node_mouse_groupheader(SpaceNode *snode)
1420 bNode *gnode;
1421 float mx, my;
1422 short mval[2];
1424 gnode= snode_get_editgroup(snode);
1425 if(gnode==NULL) return 0;
1427 getmouseco_areawin(mval);
1428 areamouseco_to_ipoco(G.v2d, mval, &mx, &my);
1430 /* click in header or outside? */
1431 if(BLI_in_rctf(&gnode->totr, mx, my)==0) {
1432 rctf rect= gnode->totr;
1434 rect.ymax += NODE_DY;
1435 if(BLI_in_rctf(&rect, mx, my)==0)
1436 snode_make_group_editable(snode, NULL); /* toggles, so exits editmode */
1437 else
1438 transform_nodes(snode->nodetree, 'g', "Move group");
1440 return 1;
1442 return 0;
1445 static int node_socket_hilights(SpaceNode *snode, int in_out)
1447 bNode *node;
1448 bNodeSocket *sock, *tsock, *socksel= NULL;
1449 float mx, my;
1450 short mval[2], redraw= 0;
1452 if(snode->edittree==NULL) return 0;
1454 getmouseco_areawin(mval);
1455 areamouseco_to_ipoco(G.v2d, mval, &mx, &my);
1457 /* deselect socks */
1458 for(node= snode->edittree->nodes.first; node; node= node->next) {
1459 for(sock= node->inputs.first; sock; sock= sock->next) {
1460 if(sock->flag & SELECT) {
1461 sock->flag &= ~SELECT;
1462 redraw++;
1463 socksel= sock;
1466 for(sock= node->outputs.first; sock; sock= sock->next) {
1467 if(sock->flag & SELECT) {
1468 sock->flag &= ~SELECT;
1469 redraw++;
1470 socksel= sock;
1475 if(find_indicated_socket(snode, &node, &tsock, in_out)) {
1476 tsock->flag |= SELECT;
1477 if(redraw==1 && tsock==socksel) redraw= 0;
1478 else redraw= 1;
1481 return redraw;
1484 void node_border_select(SpaceNode *snode)
1486 bNode *node;
1487 rcti rect;
1488 rctf rectf;
1489 short val, mval[2];
1491 if ( (val = get_border(&rect, 3)) ) {
1493 mval[0]= rect.xmin;
1494 mval[1]= rect.ymin;
1495 areamouseco_to_ipoco(G.v2d, mval, &rectf.xmin, &rectf.ymin);
1496 mval[0]= rect.xmax;
1497 mval[1]= rect.ymax;
1498 areamouseco_to_ipoco(G.v2d, mval, &rectf.xmax, &rectf.ymax);
1500 for(node= snode->edittree->nodes.first; node; node= node->next) {
1501 if(BLI_isect_rctf(&rectf, &node->totr, NULL)) {
1502 if(val==LEFTMOUSE)
1503 node->flag |= SELECT;
1504 else
1505 node->flag &= ~SELECT;
1508 allqueue(REDRAWNODE, 1);
1509 BIF_undo_push("Border select nodes");
1513 /* ****************** Add *********************** */
1515 void snode_autoconnect(SpaceNode *snode, bNode *node_to, int flag)
1517 bNodeSocket *sock, *sockfrom[8];
1518 bNode *node, *nodefrom[8];
1519 int totsock= 0, socktype=0;
1521 if(node_to==NULL || node_to->inputs.first==NULL)
1522 return;
1524 /* no inputs for node allowed (code it) */
1526 /* connect first 1 socket type now */
1527 for(sock= node_to->inputs.first; sock; sock= sock->next)
1528 if(socktype<sock->type)
1529 socktype= sock->type;
1532 /* find potential sockets, max 8 should work */
1533 for(node= snode->edittree->nodes.first; node; node= node->next) {
1534 if((node->flag & flag) && node!=node_to) {
1535 for(sock= node->outputs.first; sock; sock= sock->next) {
1536 if(!(sock->flag & (SOCK_HIDDEN|SOCK_UNAVAIL))) {
1537 sockfrom[totsock]= sock;
1538 nodefrom[totsock]= node;
1539 totsock++;
1540 if(totsock>7)
1541 break;
1545 if(totsock>7)
1546 break;
1549 /* now just get matching socket types and create links */
1550 for(sock= node_to->inputs.first; sock; sock= sock->next) {
1551 int a;
1553 for(a=0; a<totsock; a++) {
1554 if(sockfrom[a]) {
1555 if(sock->type==sockfrom[a]->type && sock->type==socktype) {
1556 nodeAddLink(snode->edittree, nodefrom[a], sockfrom[a], node_to, sock);
1557 sockfrom[a]= NULL;
1558 break;
1564 ntreeSolveOrder(snode->edittree);
1567 /* can be called from menus too, but they should do own undopush and redraws */
1568 bNode *node_add_node(SpaceNode *snode, int type, float locx, float locy)
1570 bNode *node= NULL, *gnode;
1572 node_deselectall(snode, 0);
1574 if(type>=NODE_GROUP_MENU) {
1575 if(snode->edittree!=snode->nodetree) {
1576 error("Can not add a Group in a Group");
1577 return NULL;
1579 else {
1580 bNodeTree *ngroup= BLI_findlink(&G.main->nodetree, type-NODE_GROUP_MENU);
1581 if(ngroup)
1582 node= nodeAddNodeType(snode->edittree, NODE_GROUP, ngroup);
1585 else
1586 node= nodeAddNodeType(snode->edittree, type, NULL);
1588 /* generics */
1589 if(node) {
1590 node->locx= locx;
1591 node->locy= locy + 60.0f; // arbitrary.. so its visible
1592 node->flag |= SELECT;
1594 gnode= snode_get_editgroup(snode);
1595 if(gnode) {
1596 node->locx -= gnode->locx;
1597 node->locy -= gnode->locy;
1600 snode_verify_groups(snode);
1601 node_set_active(snode, node);
1603 if(node->id)
1604 id_us_plus(node->id);
1606 if(snode->nodetree->type==NTREE_COMPOSIT)
1607 ntreeCompositForceHidden(snode->edittree);
1609 NodeTagChanged(snode->edittree, node);
1611 return node;
1614 void node_mute(SpaceNode *snode)
1616 bNode *node;
1617 int need_recalc;
1619 /* no disabling inside of groups */
1620 if(snode_get_editgroup(snode))
1621 return;
1623 need_recalc= 0;
1624 for(node= snode->edittree->nodes.first; node; node= node->next) {
1625 if(node->flag & SELECT) {
1626 if(node->inputs.first && node->outputs.first) {
1628 NodeTagChanged(snode->edittree, node);
1629 if(!need_recalc)
1630 need_recalc= 1;
1632 if(node->flag & NODE_MUTED)
1633 node->flag &= ~NODE_MUTED;
1634 else
1635 node->flag |= NODE_MUTED;
1640 if(need_recalc)
1641 snode_handle_recalc(snode);
1643 allqueue(REDRAWNODE, 0);
1644 BIF_undo_push("Enable/Disable nodes");
1648 void node_adduplicate(SpaceNode *snode)
1651 ntreeCopyTree(snode->edittree, 1); /* 1 == internally selected nodes */
1653 ntreeSolveOrder(snode->edittree);
1654 snode_verify_groups(snode);
1655 snode_handle_recalc(snode);
1657 transform_nodes(snode->edittree, 'g', "Duplicate");
1660 #if 0
1661 static void node_insert_convertor(SpaceNode *snode, bNodeLink *link)
1663 bNode *newnode= NULL;
1665 if(link->fromsock->type==SOCK_RGBA && link->tosock->type==SOCK_VALUE) {
1666 if(snode->edittree->type==NTREE_SHADER)
1667 newnode= node_add_node(snode, SH_NODE_RGBTOBW, 0.0f, 0.0f);
1668 else if(snode->edittree->type==NTREE_COMPOSIT)
1669 newnode= node_add_node(snode, CMP_NODE_RGBTOBW, 0.0f, 0.0f);
1670 else
1671 newnode= NULL;
1673 else if(link->fromsock->type==SOCK_VALUE && link->tosock->type==SOCK_RGBA) {
1674 if(snode->edittree->type==NTREE_SHADER)
1675 newnode= node_add_node(snode, SH_NODE_VALTORGB, 0.0f, 0.0f);
1676 else if(snode->edittree->type==NTREE_COMPOSIT)
1677 newnode= node_add_node(snode, CMP_NODE_VALTORGB, 0.0f, 0.0f);
1678 else
1679 newnode= NULL;
1682 if(newnode) {
1683 /* dangerous assumption to use first in/out socks, but thats fine for now */
1684 newnode->flag |= NODE_HIDDEN;
1685 newnode->locx= 0.5f*(link->fromsock->locx + link->tosock->locx);
1686 newnode->locy= 0.5f*(link->fromsock->locy + link->tosock->locy) + HIDDEN_RAD;
1688 nodeAddLink(snode->edittree, newnode, newnode->outputs.first, link->tonode, link->tosock);
1689 link->tonode= newnode;
1690 link->tosock= newnode->inputs.first;
1694 #endif
1696 static void node_remove_extra_links(SpaceNode *snode, bNodeSocket *tsock, bNodeLink *link)
1698 bNodeLink *tlink;
1699 bNodeSocket *sock;
1701 if(tsock && nodeCountSocketLinks(snode->edittree, link->tosock) > tsock->limit) {
1703 for(tlink= snode->edittree->links.first; tlink; tlink= tlink->next) {
1704 if(link!=tlink && tlink->tosock==link->tosock)
1705 break;
1707 if(tlink) {
1708 /* is there a free input socket with same type? */
1709 for(sock= tlink->tonode->inputs.first; sock; sock= sock->next) {
1710 if(sock->type==tlink->fromsock->type)
1711 if(nodeCountSocketLinks(snode->edittree, sock) < sock->limit)
1712 break;
1714 if(sock) {
1715 tlink->tosock= sock;
1716 sock->flag &= ~SOCK_HIDDEN;
1718 else {
1719 nodeRemLink(snode->edittree, tlink);
1725 /* loop that adds a nodelink, called by function below */
1726 /* in_out = starting socket */
1727 static int node_add_link_drag(SpaceNode *snode, bNode *node, bNodeSocket *sock, int in_out)
1729 bNode *tnode;
1730 bNodeSocket *tsock= NULL;
1731 bNodeLink *link= NULL;
1732 short mval[2], mvalo[2], firsttime=1; /* firsttime reconnects a link broken by caller */
1734 /* we make a temporal link */
1735 if(in_out==SOCK_OUT)
1736 link= nodeAddLink(snode->edittree, node, sock, NULL, NULL);
1737 else
1738 link= nodeAddLink(snode->edittree, NULL, NULL, node, sock);
1740 getmouseco_areawin(mvalo);
1741 while (get_mbut() & L_MOUSE) {
1743 getmouseco_areawin(mval);
1744 if(mval[0]!=mvalo[0] || mval[1]!=mvalo[1] || firsttime) {
1745 firsttime= 0;
1747 mvalo[0]= mval[0];
1748 mvalo[1]= mval[1];
1750 if(in_out==SOCK_OUT) {
1751 if(find_indicated_socket(snode, &tnode, &tsock, SOCK_IN)) {
1752 if(nodeFindLink(snode->edittree, sock, tsock)==NULL) {
1753 if(tnode!=node && link->tonode!=tnode && link->tosock!= tsock) {
1754 link->tonode= tnode;
1755 link->tosock= tsock;
1756 ntreeSolveOrder(snode->edittree); /* for interactive red line warning */
1760 else {
1761 link->tonode= NULL;
1762 link->tosock= NULL;
1765 else {
1766 if(find_indicated_socket(snode, &tnode, &tsock, SOCK_OUT)) {
1767 if(nodeFindLink(snode->edittree, sock, tsock)==NULL) {
1768 if(nodeCountSocketLinks(snode->edittree, tsock) < tsock->limit) {
1769 if(tnode!=node && link->fromnode!=tnode && link->fromsock!= tsock) {
1770 link->fromnode= tnode;
1771 link->fromsock= tsock;
1772 ntreeSolveOrder(snode->edittree); /* for interactive red line warning */
1777 else {
1778 link->fromnode= NULL;
1779 link->fromsock= NULL;
1782 /* hilight target sockets only */
1783 node_socket_hilights(snode, in_out==SOCK_OUT?SOCK_IN:SOCK_OUT);
1785 force_draw(0);
1787 else BIF_wait_for_statechange();
1790 /* remove link? */
1791 if(link->tonode==NULL || link->fromnode==NULL) {
1792 nodeRemLink(snode->edittree, link);
1794 else {
1795 /* send changed events for original tonode and new */
1796 if(link->tonode)
1797 NodeTagChanged(snode->edittree, link->tonode);
1799 /* we might need to remove a link */
1800 if(in_out==SOCK_OUT) node_remove_extra_links(snode, tsock, link);
1803 ntreeSolveOrder(snode->edittree);
1804 snode_verify_groups(snode);
1805 snode_handle_recalc(snode);
1807 allqueue(REDRAWNODE, 0);
1808 BIF_undo_push("Add link");
1810 return 1;
1813 /* return 1 when socket clicked */
1814 static int node_add_link(SpaceNode *snode)
1816 bNode *node;
1817 bNodeLink *link;
1818 bNodeSocket *sock;
1820 /* output indicated? */
1821 if(find_indicated_socket(snode, &node, &sock, SOCK_OUT)) {
1822 if(nodeCountSocketLinks(snode->edittree, sock)<sock->limit)
1823 return node_add_link_drag(snode, node, sock, SOCK_OUT);
1824 else {
1825 /* find if we break a link */
1826 for(link= snode->edittree->links.first; link; link= link->next) {
1827 if(link->fromsock==sock)
1828 break;
1830 if(link) {
1831 node= link->tonode;
1832 sock= link->tosock;
1833 nodeRemLink(snode->edittree, link);
1834 return node_add_link_drag(snode, node, sock, SOCK_IN);
1838 /* or an input? */
1839 else if(find_indicated_socket(snode, &node, &sock, SOCK_IN)) {
1840 if(nodeCountSocketLinks(snode->edittree, sock)<sock->limit)
1841 return node_add_link_drag(snode, node, sock, SOCK_IN);
1842 else {
1843 /* find if we break a link */
1844 for(link= snode->edittree->links.first; link; link= link->next) {
1845 if(link->tosock==sock)
1846 break;
1848 if(link) {
1849 /* send changed event to original tonode */
1850 if(link->tonode)
1851 NodeTagChanged(snode->edittree, link->tonode);
1853 node= link->fromnode;
1854 sock= link->fromsock;
1855 nodeRemLink(snode->edittree, link);
1856 return node_add_link_drag(snode, node, sock, SOCK_OUT);
1861 return 0;
1864 void node_delete(SpaceNode *snode)
1866 bNode *node, *next;
1867 bNodeSocket *sock;
1869 for(node= snode->edittree->nodes.first; node; node= next) {
1870 next= node->next;
1871 if(node->flag & SELECT) {
1872 /* set selin and selout NULL if the sockets belong to a node to be deleted */
1873 for(sock= node->inputs.first; sock; sock= sock->next)
1874 if(snode->edittree->selin == sock) snode->edittree->selin= NULL;
1876 for(sock= node->outputs.first; sock; sock= sock->next)
1877 if(snode->edittree->selout == sock) snode->edittree->selout= NULL;
1879 /* check id user here, nodeFreeNode is called for free dbase too */
1880 if(node->id)
1881 node->id->us--;
1882 nodeFreeNode(snode->edittree, node);
1886 snode_verify_groups(snode);
1887 snode_handle_recalc(snode);
1888 BIF_undo_push("Delete nodes");
1889 allqueue(REDRAWNODE, 1);
1892 void node_hide(SpaceNode *snode)
1894 bNode *node;
1895 int nothidden=0, ishidden=0;
1897 for(node= snode->edittree->nodes.first; node; node= node->next) {
1898 if(node->flag & SELECT) {
1899 if(node->flag & NODE_HIDDEN)
1900 ishidden++;
1901 else
1902 nothidden++;
1905 for(node= snode->edittree->nodes.first; node; node= node->next) {
1906 if(node->flag & SELECT) {
1907 if( (ishidden && nothidden) || ishidden==0)
1908 node->flag |= NODE_HIDDEN;
1909 else
1910 node->flag &= ~NODE_HIDDEN;
1913 BIF_undo_push("Hide nodes");
1914 allqueue(REDRAWNODE, 1);
1917 void node_insert_key(SpaceNode *snode)
1919 bNode *node= editnode_get_active(snode->edittree);
1921 if(node->type==CMP_NODE_TIME) {
1922 if(node->custom1<node->custom2) {
1924 CurveMapping *cumap= node->storage;
1925 float fval, curval;
1927 curval= (float)(CFRA - node->custom1)/(float)(node->custom2-node->custom1);
1928 fval= curvemapping_evaluateF(cumap, 0, curval);
1930 if(fbutton(&fval, 0.0f, 1.0f, 10, 10, "Insert Value")) {
1931 curvemap_insert(cumap->cm, curval, fval);
1933 BIF_undo_push("Insert key in Time node");
1934 allqueue(REDRAWNODE, 1);
1940 void node_select_linked(SpaceNode *snode, int out)
1942 bNodeLink *link;
1943 bNode *node;
1945 /* NODE_TEST is the free flag */
1946 for(node= snode->edittree->nodes.first; node; node= node->next)
1947 node->flag &= ~NODE_TEST;
1949 for(link= snode->edittree->links.first; link; link= link->next) {
1950 if(out) {
1951 if(link->fromnode->flag & NODE_SELECT)
1952 link->tonode->flag |= NODE_TEST;
1954 else {
1955 if(link->tonode->flag & NODE_SELECT)
1956 link->fromnode->flag |= NODE_TEST;
1960 for(node= snode->edittree->nodes.first; node; node= node->next)
1961 if(node->flag & NODE_TEST)
1962 node->flag |= NODE_SELECT;
1964 BIF_undo_push("Select Linked nodes");
1965 allqueue(REDRAWNODE, 1);
1968 /* makes a link between selected output and input sockets */
1969 void node_make_link(SpaceNode *snode)
1971 bNode *fromnode, *tonode;
1972 bNodeLink *link;
1973 bNodeSocket *outsock= snode->edittree->selout;
1974 bNodeSocket *insock= snode->edittree->selin;
1976 if(!insock || !outsock) return;
1977 if(nodeFindLink(snode->edittree, outsock, insock)) return;
1979 if(nodeFindNode(snode->edittree, outsock, &fromnode, NULL) &&
1980 nodeFindNode(snode->edittree, insock, &tonode, NULL)) {
1981 link= nodeAddLink(snode->edittree, fromnode, outsock, tonode, insock);
1982 NodeTagChanged(snode->edittree, tonode);
1983 node_remove_extra_links(snode, insock, link);
1985 else return;
1987 ntreeSolveOrder(snode->edittree);
1988 snode_verify_groups(snode);
1989 snode_handle_recalc(snode);
1991 allqueue(REDRAWNODE, 0);
1992 BIF_undo_push("Make Link Between Sockets");
1995 static void node_border_link_delete(SpaceNode *snode)
1997 rcti rect;
1998 short val, mval[2], mvalo[2];
2000 /* to make this work more friendly, we first wait for a mouse move */
2001 getmouseco_areawin(mvalo);
2002 while (get_mbut() & L_MOUSE) {
2003 getmouseco_areawin(mval);
2004 if(mval[0]!=mvalo[0] || mval[1]!=mvalo[1])
2005 break;
2006 else BIF_wait_for_statechange();
2008 if((get_mbut() & L_MOUSE)==0)
2009 return;
2011 /* now change cursor and draw border */
2012 setcursor_space(SPACE_NODE, CURSOR_VPAINT);
2014 if ( (val = get_border(&rect, 2)) ) {
2015 if(rect.xmin<rect.xmax && rect.ymin<rect.ymax) {
2016 //#define NODE_MAXPICKBUF 256
2017 bNodeLink *link, *next;
2018 GLuint buffer[256];
2019 rctf rectf;
2020 int code=0, hits;
2022 mval[0]= rect.xmin;
2023 mval[1]= rect.ymin;
2024 areamouseco_to_ipoco(&snode->v2d, mval, &rectf.xmin, &rectf.ymin);
2025 mval[0]= rect.xmax;
2026 mval[1]= rect.ymax;
2027 areamouseco_to_ipoco(&snode->v2d, mval, &rectf.xmax, &rectf.ymax);
2029 myortho2(rectf.xmin, rectf.xmax, rectf.ymin, rectf.ymax);
2031 glSelectBuffer(256, buffer);
2032 glRenderMode(GL_SELECT);
2033 glInitNames();
2034 glPushName(-1);
2036 /* draw links */
2037 for(link= snode->edittree->links.first; link; link= link->next) {
2038 glLoadName(code++);
2039 node_draw_link(snode, link);
2042 hits= glRenderMode(GL_RENDER);
2043 glPopName();
2044 if(hits>0) {
2045 int a;
2046 for(a=0; a<hits; a++) {
2047 bNodeLink *link= BLI_findlink(&snode->edittree->links, buffer[ (4 * a) + 3]);
2048 if(link)
2049 link->fromnode= NULL; /* first tag for delete, otherwise indices are wrong */
2051 for(link= snode->edittree->links.first; link; link= next) {
2052 next= link->next;
2053 if(link->fromnode==NULL) {
2054 NodeTagChanged(snode->edittree, link->tonode);
2055 nodeRemLink(snode->edittree, link);
2058 ntreeSolveOrder(snode->edittree);
2059 snode_verify_groups(snode);
2060 snode_handle_recalc(snode);
2062 allqueue(REDRAWNODE, 0);
2063 BIF_undo_push("Erase links");
2067 setcursor_space(SPACE_NODE, CURSOR_STD);
2070 /* goes over all scenes, reads render layerss */
2071 void node_read_renderlayers(SpaceNode *snode)
2073 Scene *scene;
2074 bNode *node;
2076 /* first tag scenes unread */
2077 for(scene= G.main->scene.first; scene; scene= scene->id.next)
2078 scene->id.flag |= LIB_DOIT;
2080 for(node= snode->edittree->nodes.first; node; node= node->next) {
2081 if(node->type==CMP_NODE_R_LAYERS) {
2082 ID *id= node->id;
2083 if(id==NULL) id= (ID *)G.scene;
2084 if(id->flag & LIB_DOIT) {
2085 RE_ReadRenderResult(G.scene, (Scene *)id);
2086 ntreeCompositTagRender((Scene *)id);
2087 id->flag &= ~LIB_DOIT;
2092 snode_handle_recalc(snode);
2095 /* called from header_info, when deleting a scene
2096 * goes over all scenes other than the input, checks if they have
2097 * render layer nodes referencing the to-be-deleted scene, and
2098 * resets them to NULL. */
2099 void clear_scene_in_nodes(Scene *sce)
2101 Scene *sce1;
2102 bNode *node;
2104 sce1= G.main->scene.first;
2105 while(sce1) {
2106 if(sce1!=sce) {
2107 if (sce1->nodetree) {
2108 for(node= sce1->nodetree->nodes.first; node; node= node->next) {
2109 if(node->type==CMP_NODE_R_LAYERS) {
2110 Scene *nodesce= (Scene *)node->id;
2112 if (nodesce==sce) node->id = NULL;
2117 sce1= sce1->id.next;
2122 /* gets active viewer user */
2123 struct ImageUser *ntree_get_active_iuser(bNodeTree *ntree)
2125 bNode *node;
2127 if(ntree)
2128 for(node= ntree->nodes.first; node; node= node->next)
2129 if( ELEM(node->type, CMP_NODE_VIEWER, CMP_NODE_SPLITVIEWER))
2130 if(node->flag & NODE_DO_OUTPUT)
2131 return node->storage;
2132 return NULL;
2135 void imagepaint_composite_tags(bNodeTree *ntree, Image *image, ImageUser *iuser)
2137 bNode *node;
2139 if(ntree==NULL)
2140 return;
2142 /* search for renderresults */
2143 if(image->type==IMA_TYPE_R_RESULT) {
2144 for(node= ntree->nodes.first; node; node= node->next) {
2145 if(node->type==CMP_NODE_R_LAYERS && node->id==NULL) {
2146 /* imageuser comes from ImageWin, so indexes are offset 1 */
2147 if(node->custom1==iuser->layer-1)
2148 NodeTagChanged(ntree, node);
2152 else {
2153 for(node= ntree->nodes.first; node; node= node->next) {
2154 if(node->id== &image->id)
2155 NodeTagChanged(ntree, node);
2160 /* ********************** */
2162 void node_make_group(SpaceNode *snode)
2164 bNode *gnode;
2166 if(snode->edittree!=snode->nodetree) {
2167 error("Can not add a new Group in a Group");
2168 return;
2171 /* for time being... is too complex to handle */
2172 if(snode->treetype==NTREE_COMPOSIT) {
2173 for(gnode=snode->nodetree->nodes.first; gnode; gnode= gnode->next) {
2174 if(gnode->flag & SELECT)
2175 if(gnode->type==CMP_NODE_R_LAYERS)
2176 break;
2178 if(gnode) {
2179 error("Can not add RenderLayer in a Group");
2180 return;
2184 gnode= nodeMakeGroupFromSelected(snode->nodetree);
2185 if(gnode==NULL) {
2186 error("Can not make Group");
2188 else {
2189 nodeSetActive(snode->nodetree, gnode);
2190 ntreeSolveOrder(snode->nodetree);
2191 allqueue(REDRAWNODE, 0);
2192 BIF_undo_push("Make Node Group");
2196 /* ******************** main event loop ****************** */
2198 /* special version to prevent overlapping buttons, has a bit of hack... */
2199 /* yes, check for example composit_node_event(), file window use... */
2200 static int node_uiDoBlocks(ScrArea *sa, short event)
2202 SpaceNode *snode= sa->spacedata.first;
2203 ListBase *lb= &sa->uiblocks;
2204 ListBase listb= *lb;
2205 bNode *node;
2206 rctf rect;
2207 void *prev, *next;
2208 int retval= UI_NOTHING;
2209 short mval[2];
2211 getmouseco_areawin(mval);
2212 areamouseco_to_ipoco(G.v2d, mval, &rect.xmin, &rect.ymin);
2214 /* this happens after filesel usage... */
2215 if(lb->first==NULL) {
2216 return UI_NOTHING;
2219 rect.xmin -= 2.0f;
2220 rect.ymin -= 2.0f;
2221 rect.xmax = rect.xmin + 4.0f;
2222 rect.ymax = rect.ymin + 4.0f;
2224 for(node= snode->edittree->nodes.first; node; node= node->next) {
2225 uiBlock *block;
2226 char str[32];
2228 /* retreive unique block name, see also drawnode.c */
2229 sprintf(str, "node buttons %p", node);
2230 block= uiGetBlock(str, sa);
2232 if(block) {
2233 if(node == visible_node(snode, &rect)) {
2235 /* when there's menus, the prev pointer becomes zero! */
2236 prev= ((struct Link *)block)->prev;
2237 next= ((struct Link *)block)->next;
2238 ((struct Link *)block)->prev= NULL;
2239 ((struct Link *)block)->next= NULL;
2241 lb->first= lb->last= block;
2242 retval= uiDoBlocks(lb, event);
2244 ((struct Link *)block)->prev= prev;
2245 ((struct Link *)block)->next= next;
2247 break;
2252 *lb= listb;
2254 return retval;
2257 void winqreadnodespace(ScrArea *sa, void *spacedata, BWinEvent *evt)
2259 SpaceNode *snode= spacedata;
2260 bNode *actnode;
2261 bNodeSocket *actsock;
2262 unsigned short event= evt->event;
2263 short val= evt->val, doredraw=0, fromlib= 0;
2265 if(sa->win==0) return;
2266 if(snode->nodetree==NULL) return;
2268 if(val) {
2270 if( node_uiDoBlocks(sa, event)!=UI_NOTHING ) event= 0;
2272 fromlib= (snode->id && snode->id->lib);
2274 switch(event) {
2275 case LEFTMOUSE:
2276 if(fromlib) {
2277 if(node_mouse_groupheader(snode)==0)
2278 node_mouse_select(snode, event);
2280 else {
2282 if(G.qual & LR_CTRLKEY) {
2283 gesture();
2284 } else {
2285 if(node_add_link(snode)==0)
2286 if(node_mouse_groupheader(snode)==0)
2287 if(node_mouse_select(snode, event)==0)
2288 node_border_link_delete(snode);
2291 break;
2293 case RIGHTMOUSE:
2294 if(find_indicated_socket(snode, &actnode, &actsock, SOCK_IN)) {
2295 if(actsock->flag & SOCK_SEL) {
2296 snode->edittree->selin= NULL;
2297 actsock->flag&= ~SOCK_SEL;
2299 else {
2300 snode->edittree->selin= actsock;
2301 reset_sel_socket(snode, SOCK_IN);
2302 actsock->flag|= SOCK_SEL;
2305 else if(find_indicated_socket(snode, &actnode, &actsock, SOCK_OUT)) {
2306 if(actsock->flag & SOCK_SEL) {
2307 snode->edittree->selout= NULL;
2308 actsock->flag&= ~SOCK_SEL;
2310 else {
2311 snode->edittree->selout= actsock;
2312 reset_sel_socket(snode, SOCK_OUT);
2313 actsock->flag|= SOCK_SEL;
2316 else if(!node_mouse_select(snode, event))
2317 toolbox_n();
2319 break;
2320 case MIDDLEMOUSE:
2321 if (G.qual==LR_SHIFTKEY) {
2322 snode_bg_viewmove(snode);
2323 } else {
2324 view2dmove(event);
2326 case WHEELUPMOUSE:
2327 case WHEELDOWNMOUSE:
2328 view2dmove(event); /* in drawipo.c */
2329 break;
2331 case MOUSEY:
2332 doredraw= node_socket_hilights(snode, SOCK_IN|SOCK_OUT);
2333 break;
2335 case UI_BUT_EVENT:
2336 /* future: handlerize this! */
2337 if(snode->treetype==NTREE_SHADER)
2338 shader_node_event(snode, val);
2339 else if(snode->treetype==NTREE_COMPOSIT)
2340 composit_node_event(snode, val);
2341 break;
2343 case RENDERPREVIEW:
2344 if(snode->treetype==NTREE_SHADER)
2345 shader_node_previewrender(sa, snode);
2346 break;
2348 case PADPLUSKEY:
2349 snode_zoom_in(sa);
2350 doredraw= 1;
2351 break;
2352 case PADMINUS:
2353 snode_zoom_out(sa);
2354 doredraw= 1;
2355 break;
2356 case HOMEKEY:
2357 snode_home(sa, snode);
2358 doredraw= 1;
2359 break;
2360 case TABKEY:
2361 if(fromlib) fromlib= -1;
2362 else snode_make_group_editable(snode, NULL);
2363 break;
2365 case AKEY:
2366 if(G.qual==LR_SHIFTKEY) {
2367 if(fromlib) fromlib= -1;
2368 else toolbox_n_add();
2370 else if(G.qual==0) {
2371 node_deselectall(snode, 1);
2372 BIF_undo_push("Deselect all nodes");
2374 else if(G.qual & LR_CTRLKEY) {
2375 node_select_same_type(snode);
2376 BIF_undo_push("Select same nodes");
2378 break;
2379 case BKEY:
2380 if(G.qual==0)
2381 node_border_select(snode);
2382 break;
2383 case CKEY: /* sort again, showing cyclics */
2384 ntreeSolveOrder(snode->edittree);
2385 doredraw= 1;
2386 break;
2387 case DKEY:
2388 if(G.qual==LR_SHIFTKEY) {
2389 if(fromlib) fromlib= -1;
2390 else node_adduplicate(snode);
2392 break;
2393 case EKEY:
2394 snode_handle_recalc(snode);
2395 break;
2396 case FKEY:
2397 node_make_link(snode);
2398 break;
2399 case GKEY:
2400 if(fromlib) fromlib= -1;
2401 else {
2402 if(G.qual==LR_CTRLKEY) {
2403 if(okee("Make Group"))
2404 node_make_group(snode);
2406 else if(G.qual==LR_ALTKEY) {
2407 if(okee("Ungroup"))
2408 node_ungroup(snode);
2410 else if(G.qual==LR_SHIFTKEY) {
2411 node_addgroup(snode);
2413 else
2414 transform_nodes(snode->edittree, 'g', "Move Node");
2416 break;
2417 case HKEY:
2418 node_hide(snode);
2419 break;
2420 case IKEY:
2421 node_insert_key(snode);
2422 break;
2423 case LKEY:
2424 node_select_linked(snode, G.qual==LR_SHIFTKEY);
2425 break;
2426 case MKEY:
2427 node_mute(snode);
2428 break;
2429 case RKEY:
2430 if(G.qual==LR_CTRLKEY)
2431 node_rename_cb(snode);
2432 else if(okee("Read saved Render Layers"))
2433 node_read_renderlayers(snode);
2434 break;
2435 case DELKEY:
2436 case XKEY:
2437 if(fromlib) fromlib= -1;
2438 else node_delete(snode);
2439 break;
2443 if(fromlib==-1)
2444 error_libdata();
2445 if(doredraw)
2446 scrarea_queue_winredraw(sa);
2447 if(doredraw==2)
2448 scrarea_queue_headredraw(sa);