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): David Millan Escriva, Juho Vepsäläinen, Nathan Letwory
27 * ***** END GPL LICENSE BLOCK *****
35 #include "MEM_guardedalloc.h"
37 #include "DNA_action_types.h"
38 #include "DNA_brush_types.h"
39 #include "DNA_color_types.h"
40 #include "DNA_image_types.h"
41 #include "DNA_ipo_types.h"
42 #include "DNA_object_types.h"
43 #include "DNA_material_types.h"
44 #include "DNA_texture_types.h"
45 #include "DNA_node_types.h"
46 #include "DNA_space_types.h"
47 #include "DNA_screen_types.h"
48 #include "DNA_scene_types.h"
49 #include "DNA_userdef_types.h"
51 #include "BKE_context.h"
52 #include "BKE_colortools.h"
53 #include "BKE_global.h"
54 #include "BKE_image.h"
55 #include "BKE_library.h"
58 #include "BKE_material.h"
59 #include "BKE_texture.h"
60 #include "BKE_scene.h"
61 #include "BKE_utildefines.h"
63 #include "ED_previewrender.h"
67 #include "BLI_arithb.h"
68 #include "BLI_blenlib.h"
69 #include "BLI_storage_types.h"
71 #include "RE_pipeline.h"
73 #include "IMB_imbuf_types.h"
75 #include "ED_space_api.h"
76 #include "ED_screen.h"
77 #include "ED_transform.h"
80 #include "RNA_access.h"
81 #include "RNA_define.h"
86 #include "UI_view2d.h"
88 #include "node_intern.h"
93 /* ***************** composite job manager ********************** */
95 typedef struct CompoJob {
103 /* called by compo, only to check job 'stop' value */
104 static int compo_breakjob(void *cjv)
111 /* called by compo, wmJob sends notifier */
112 static void compo_redrawjob(void *cjv, char *str)
119 static void compo_freejob(void *cjv)
124 ntreeLocalMerge(cj->localtree, cj->ntree);
129 /* only now we copy the nodetree, so adding many jobs while
130 sliding buttons doesn't frustrate */
131 static void compo_initjob(void *cjv)
135 cj->localtree= ntreeLocalize(cj->ntree);
138 /* called before redraw notifiers, it moves finished previews over */
139 static void compo_updatejob(void *cjv)
143 ntreeLocalSync(cj->localtree, cj->ntree);
147 /* only this runs inside thread */
148 static void compo_startjob(void *cjv, short *stop, short *do_update)
151 bNodeTree *ntree= cj->localtree;
153 if(cj->scene->use_nodes==0)
157 cj->do_update= do_update;
159 ntree->test_break= compo_breakjob;
161 ntree->stats_draw= compo_redrawjob;
164 // XXX BIF_store_spare();
166 ntreeCompositExecTree(ntree, &cj->scene->r, 1); /* 1 is do_previews */
168 ntree->test_break= NULL;
169 ntree->stats_draw= NULL;
173 void snode_composite_job(const bContext *C, ScrArea *sa)
175 SpaceNode *snode= sa->spacedata.first;
176 wmJob *steve= WM_jobs_get(CTX_wm_manager(C), CTX_wm_window(C), sa);
177 CompoJob *cj= MEM_callocN(sizeof(CompoJob), "compo job");
179 /* customdata for preview thread */
180 cj->scene= CTX_data_scene(C);
181 cj->ntree= snode->nodetree;
184 WM_jobs_customdata(steve, cj, compo_freejob);
185 WM_jobs_timer(steve, 0.1, NC_SCENE, NC_SCENE|ND_COMPO_RESULT);
186 WM_jobs_callbacks(steve, compo_startjob, compo_initjob, compo_updatejob);
188 WM_jobs_start(CTX_wm_manager(C), steve);
192 /* ***************************************** */
194 /* also checks for edited groups */
195 bNode *editnode_get_active(bNodeTree *ntree)
199 /* check for edited group */
200 for(node= ntree->nodes.first; node; node= node->next)
201 if(node->flag & NODE_GROUP_EDIT)
204 return nodeGetActive((bNodeTree *)node->id);
206 return nodeGetActive(ntree);
209 void snode_handle_recalc(bContext *C, SpaceNode *snode)
211 if(snode->treetype==NTREE_SHADER)
212 WM_event_add_notifier(C, NC_MATERIAL|ND_NODES, snode->id);
213 else if(snode->treetype==NTREE_COMPOSIT)
214 WM_event_add_notifier(C, NC_SCENE|ND_NODES, snode->id);
215 else if(snode->treetype==NTREE_TEXTURE) {
216 // ntreeTexUpdatePreviews(snode->nodetree); /* XXX texture nodes should follow shader node methods (ton) */
217 // XXX BIF_preview_changed(ID_TE);
222 static int image_detect_file_sequence(int *start_p, int *frames_p, char *str)
225 char name[FILE_MAX], head[FILE_MAX], tail[FILE_MAX], filename[FILE_MAX];
226 int a, frame, totframe, found, minframe;
227 unsigned short numlen;
229 sfile= scrarea_find_space_of_type(curarea, SPACE_FILE);
230 if(sfile==NULL || sfile->filelist==NULL)
233 /* find first frame */
237 for(a=0; a<sfile->totfile; a++) {
238 if(sfile->filelist[a].flags & ACTIVE) {
239 BLI_strncpy(name, sfile->filelist[a].relname, sizeof(name));
240 frame= BLI_stringdec(name, head, tail, &numlen);
242 if(!found || frame < minframe) {
243 BLI_strncpy(filename, name, sizeof(name));
250 /* not one frame found */
254 /* counter number of following frames */
258 for(frame=minframe; found; frame++) {
260 BLI_strncpy(name, filename, sizeof(name));
261 BLI_stringenc(name, head, tail, numlen, frame);
263 for(a=0; a<sfile->totfile; a++) {
264 if(sfile->filelist[a].flags & ACTIVE) {
265 if(strcmp(sfile->filelist[a].relname, name) == 0) {
275 BLI_strncpy(str, sfile->dir, sizeof(name));
276 strcat(str, filename);
286 static void load_node_image(char *str) /* called from fileselect */
288 SpaceNode *snode= curarea->spacedata.first;
289 bNode *node= nodeGetActive(snode->edittree);
291 ImageUser *iuser= node->storage;
292 char filename[FILE_MAX];
293 int start=0, frames=0, sequence;
295 sequence= image_detect_file_sequence(&start, &frames, filename);
299 ima= BKE_add_image_file(str);
305 id_us_plus(node->id);
307 BLI_strncpy(node->name, node->id->name+2, 21);
310 ima->source= IMA_SRC_SEQUENCE;
311 iuser->frames= frames;
312 iuser->offset= start-1;
315 BKE_image_signal(ima, node->storage, IMA_SIGNAL_RELOAD);
317 NodeTagChanged(snode->edittree, node);
318 // XXX snode_handle_recalc(C, snode);
322 static void set_node_imagepath(char *str) /* called from fileselect */
324 SpaceNode *snode= curarea->spacedata.first;
325 bNode *node= nodeGetActive(snode->edittree);
326 BLI_strncpy(((NodeImageFile *)node->storage)->name, str, sizeof( ((NodeImageFile *)node->storage)->name ));
331 bNode *snode_get_editgroup(SpaceNode *snode)
335 /* get the groupnode */
336 for(gnode= snode->nodetree->nodes.first; gnode; gnode= gnode->next)
337 if(gnode->flag & NODE_GROUP_EDIT)
344 /* node has to be of type 'render layers' */
345 /* is a bit clumsy copying renderdata here... scene nodes use render size of current render */
346 static void composite_node_render(SpaceNode *snode, bNode *node)
352 /* the button press won't show up otherwise, button hilites disabled */
355 if(node->id && node->id!=(ID *)G.scene) {
357 set_scene_bg((Scene *)node->id);
359 G.scene->r.xsch= scene->r.xsch;
360 G.scene->r.ysch= scene->r.ysch;
361 G.scene->r.size= scene->r.size;
362 G.scene->r.mode &= ~(R_BORDER|R_DOCOMP);
363 G.scene->r.mode |= scene->r.mode & R_BORDER;
364 G.scene->r.border= scene->r.border;
365 G.scene->r.cfra= scene->r.cfra;
368 scemode= G.scene->r.scemode;
369 actlay= G.scene->r.actlay;
371 G.scene->r.scemode |= R_SINGLE_LAYER|R_COMP_RERENDER;
372 G.scene->r.actlay= node->custom1;
376 G.scene->r.scemode= scemode;
377 G.scene->r.actlay= actlay;
387 static void composit_node_event(SpaceNode *snode, short event)
392 // allqueue(REDRAWNODE, 1);
394 case B_NODE_LOADIMAGE:
396 bNode *node= nodeGetActive(snode->edittree);
397 char name[FILE_MAXDIR+FILE_MAXFILE];
400 strcpy(name, ((Image *)node->id)->name);
401 else strcpy(name, U.textudir);
402 if (G.qual & LR_CTRLKEY) {
403 activate_imageselect(FILE_SPECIAL, "SELECT IMAGE", name, load_node_image);
405 activate_fileselect(FILE_SPECIAL, "SELECT IMAGE", name, load_node_image);
409 case B_NODE_SETIMAGE:
411 bNode *node= nodeGetActive(snode->edittree);
412 char name[FILE_MAXDIR+FILE_MAXFILE];
414 strcpy(name, ((NodeImageFile *)node->storage)->name);
415 if (G.qual & LR_CTRLKEY) {
416 activate_imageselect(FILE_SPECIAL, "SELECT OUTPUT DIR", name, set_node_imagepath);
418 activate_fileselect(FILE_SPECIAL, "SELECT OUTPUT DIR", name, set_node_imagepath);
422 case B_NODE_TREE_EXEC:
423 // XXX snode_handle_recalc(snode);
428 bNode *node= BLI_findlink(&snode->edittree->nodes, event-B_NODE_EXEC);
430 NodeTagChanged(snode->edittree, node);
431 /* don't use NodeTagIDChanged, it gives far too many recomposites for image, scene layers, ... */
433 /* not the best implementation of the world... but we need it to work now :) */
434 if(node->type==CMP_NODE_R_LAYERS && node->custom2) {
435 /* add event for this window (after render curarea can be changed) */
436 addqueue(curarea->win, UI_BUT_EVENT, B_NODE_TREE_EXEC);
438 composite_node_render(snode, node);
439 // XXX snode_handle_recalc(snode);
441 /* add another event, a render can go fullscreen and open new window */
442 addqueue(curarea->win, UI_BUT_EVENT, B_NODE_TREE_EXEC);
445 node= snode_get_editgroup(snode);
447 NodeTagIDChanged(snode->nodetree, node->id);
449 // XXX snode_handle_recalc(snode);
456 static void texture_node_event(SpaceNode *snode, short event)
460 // allqueue(REDRAWNODE, 1);
462 case B_NODE_LOADIMAGE:
464 bNode *node= nodeGetActive(snode->edittree);
465 char name[FILE_MAXDIR+FILE_MAXFILE];
468 strcpy(name, ((Image *)node->id)->name);
469 else strcpy(name, U.textudir);
470 if (G.qual & LR_CTRLKEY) {
471 activate_imageselect(FILE_SPECIAL, "SELECT IMAGE", name, load_node_image);
473 activate_fileselect(FILE_SPECIAL, "SELECT IMAGE", name, load_node_image);
479 ntreeTexCheckCyclics( snode->nodetree );
480 // XXX snode_handle_recalc(snode);
481 // allqueue(REDRAWNODE, 1);
487 /* assumes nothing being done in ntree yet, sets the default in/out node */
488 /* called from shading buttons or header */
489 void node_shader_default(Material *ma)
492 bNodeSocket *fromsock, *tosock;
494 /* but lets check it anyway */
496 printf("error in shader initialize\n");
500 ma->nodetree= ntreeAddTree(NTREE_SHADER);
502 out= nodeAddNodeType(ma->nodetree, SH_NODE_OUTPUT, NULL, NULL);
503 out->locx= 300.0f; out->locy= 300.0f;
505 in= nodeAddNodeType(ma->nodetree, SH_NODE_MATERIAL, NULL, NULL);
506 in->locx= 10.0f; in->locy= 300.0f;
507 nodeSetActive(ma->nodetree, in);
509 /* only a link from color to color */
510 fromsock= in->outputs.first;
511 tosock= out->inputs.first;
512 nodeAddLink(ma->nodetree, in, fromsock, out, tosock);
514 ntreeSolveOrder(ma->nodetree); /* needed for pointers */
517 /* assumes nothing being done in ntree yet, sets the default in/out node */
518 /* called from shading buttons or header */
519 void node_composit_default(Scene *sce)
522 bNodeSocket *fromsock, *tosock;
524 /* but lets check it anyway */
526 printf("error in composit initialize\n");
530 sce->nodetree= ntreeAddTree(NTREE_COMPOSIT);
532 out= nodeAddNodeType(sce->nodetree, CMP_NODE_COMPOSITE, NULL, NULL);
533 out->locx= 300.0f; out->locy= 400.0f;
536 in= nodeAddNodeType(sce->nodetree, CMP_NODE_R_LAYERS, NULL, NULL);
537 in->locx= 10.0f; in->locy= 400.0f;
539 nodeSetActive(sce->nodetree, in);
541 /* links from color to color */
542 fromsock= in->outputs.first;
543 tosock= out->inputs.first;
544 nodeAddLink(sce->nodetree, in, fromsock, out, tosock);
546 ntreeSolveOrder(sce->nodetree); /* needed for pointers */
548 // XXX ntreeCompositForceHidden(sce->nodetree);
551 /* assumes nothing being done in ntree yet, sets the default in/out node */
552 /* called from shading buttons or header */
553 void node_texture_default(Tex *tx)
556 bNodeSocket *fromsock, *tosock;
558 /* but lets check it anyway */
560 printf("error in texture initialize\n");
564 tx->nodetree= ntreeAddTree(NTREE_TEXTURE);
566 out= nodeAddNodeType(tx->nodetree, TEX_NODE_OUTPUT, NULL, NULL);
567 out->locx= 300.0f; out->locy= 300.0f;
569 in= nodeAddNodeType(tx->nodetree, TEX_NODE_CHECKER, NULL, NULL);
570 in->locx= 10.0f; in->locy= 300.0f;
571 nodeSetActive(tx->nodetree, in);
573 fromsock= in->outputs.first;
574 tosock= out->inputs.first;
575 nodeAddLink(tx->nodetree, in, fromsock, out, tosock);
577 ntreeSolveOrder(tx->nodetree); /* needed for pointers */
578 ntreeTexUpdatePreviews(tx->nodetree); /* XXX texture nodes should follow shader node methods (ton) */
581 /* Here we set the active tree(s), even called for each redraw now, so keep it fast :) */
582 void snode_set_context(SpaceNode *snode, Scene *scene)
587 snode->nodetree= NULL;
588 snode->id= snode->from= NULL;
590 if(snode->treetype==NTREE_SHADER) {
591 /* need active object, or we allow pinning... */
593 Material *ma= give_current_material(ob, ob->actcol);
595 snode->from= material_from(ob, ob->actcol);
597 snode->nodetree= ma->nodetree;
601 else if(snode->treetype==NTREE_COMPOSIT) {
603 snode->id= &scene->id;
605 /* bit clumsy but reliable way to see if we draw first time */
606 if(snode->nodetree==NULL)
607 ntreeCompositForceHidden(scene->nodetree, scene);
609 snode->nodetree= scene->nodetree;
611 else if(snode->treetype==NTREE_TEXTURE) {
614 if(snode->texfrom==SNODE_TEX_OBJECT) {
616 tx= give_current_texture(ob, ob->actcol);
617 snode->from= (ID *)ob;
620 else if(snode->texfrom==SNODE_TEX_WORLD) {
621 tx= give_current_world_texture(scene);
622 snode->from= (ID *)scene->world;
627 if(ob && ob->mode & OB_MODE_SCULPT) {
628 Sculpt *sd= scene->toolsettings->sculpt;
630 if(sd->brush->texact != -1)
631 mtex= sd->brush->mtex[sd->brush->texact];
634 Brush *br= scene->toolsettings->imapaint.brush;
636 mtex= br->mtex[br->texact];
640 snode->from= (ID *)scene;
647 snode->nodetree= tx->nodetree;
651 /* find editable group */
653 for(node= snode->nodetree->nodes.first; node; node= node->next)
654 if(node->flag & NODE_GROUP_EDIT)
658 snode->edittree= (bNodeTree *)node->id;
660 snode->edittree= snode->nodetree;
664 /* on activate image viewer, check if we show it */
665 static void node_active_image(Image *ima)
668 SpaceImage *sima= NULL;
670 /* find an imagewindow showing render result */
671 for(sa=G.curscreen->areabase.first; sa; sa= sa->next) {
672 if(sa->spacetype==SPACE_IMAGE) {
673 sima= sa->spacedata.first;
674 if(sima->image && sima->image->source!=IMA_SRC_VIEWER)
680 scrarea_queue_winredraw(sa);
681 scrarea_queue_headredraw(sa);
686 void node_set_active(SpaceNode *snode, bNode *node)
689 nodeSetActive(snode->edittree, node);
693 if(node->type!=NODE_GROUP) {
695 /* tree specific activate calls */
696 if(snode->treetype==NTREE_SHADER) {
698 /* when we select a material, active texture is cleared, for buttons */
699 if(node->id && GS(node->id->name)==ID_MA)
700 nodeClearActiveID(snode->edittree, ID_TE);
702 ; // XXX BIF_preview_changed(-1); /* temp hack to force texture preview to update */
704 // allqueue(REDRAWBUTSSHADING, 1);
705 // allqueue(REDRAWIPO, 0);
707 else if(snode->treetype==NTREE_COMPOSIT) {
708 /* make active viewer, currently only 1 supported... */
709 if( ELEM(node->type, CMP_NODE_VIEWER, CMP_NODE_SPLITVIEWER)) {
711 int was_output= node->flag & NODE_DO_OUTPUT;
713 for(tnode= snode->edittree->nodes.first; tnode; tnode= tnode->next)
714 if( ELEM(tnode->type, CMP_NODE_VIEWER, CMP_NODE_SPLITVIEWER))
715 tnode->flag &= ~NODE_DO_OUTPUT;
717 node->flag |= NODE_DO_OUTPUT;
721 NodeTagChanged(snode->edittree, node);
723 /* if inside group, tag entire group */
724 gnode= snode_get_editgroup(snode);
726 NodeTagIDChanged(snode->nodetree, gnode->id);
728 // XXX snode_handle_recalc(snode);
731 /* addnode() doesnt link this yet... */
732 node->id= (ID *)BKE_image_verify_viewer(IMA_TYPE_COMPOSITE, "Viewer Node");
734 else if(node->type==CMP_NODE_IMAGE) {
736 node_active_image((Image *)node->id);
738 else if(node->type==CMP_NODE_R_LAYERS) {
739 if(node->id==NULL || node->id==(ID *)G.scene) {
740 G.scene->r.actlay= node->custom1;
741 // allqueue(REDRAWBUTSSCENE, 0);
745 else if(snode->treetype==NTREE_TEXTURE) {
747 ; // XXX BIF_preview_changed(-1);
748 // allqueue(REDRAWBUTSSHADING, 1);
749 // allqueue(REDRAWIPO, 0);
755 void snode_make_group_editable(SpaceNode *snode, bNode *gnode)
759 /* make sure nothing has group editing on */
760 for(node= snode->nodetree->nodes.first; node; node= node->next)
761 node->flag &= ~NODE_GROUP_EDIT;
764 /* with NULL argument we do a toggle */
765 if(snode->edittree==snode->nodetree)
766 gnode= nodeGetActive(snode->nodetree);
769 if(gnode && gnode->type==NODE_GROUP && gnode->id) {
771 // XXX if(okee("Make Group Local"))
772 // ntreeMakeLocal((bNodeTree *)gnode->id);
776 gnode->flag |= NODE_GROUP_EDIT;
777 snode->edittree= (bNodeTree *)gnode->id;
779 /* deselect all other nodes, so we can also do grabbing of entire subtree */
780 for(node= snode->nodetree->nodes.first; node; node= node->next)
781 node->flag &= ~SELECT;
782 gnode->flag |= SELECT;
786 snode->edittree= snode->nodetree;
788 ntreeSolveOrder(snode->nodetree);
790 /* finally send out events for new active node */
791 if(snode->treetype==NTREE_SHADER) {
792 // allqueue(REDRAWBUTSSHADING, 0);
794 // XXX BIF_preview_changed(-1); /* temp hack to force texture preview to update */
797 // allqueue(REDRAWNODE, 0);
802 void node_ungroup(SpaceNode *snode)
806 /* are we inside of a group? */
807 gnode= snode_get_editgroup(snode);
809 snode_make_group_editable(snode, NULL);
811 gnode= nodeGetActive(snode->edittree);
812 if(gnode==NULL) return;
814 if(gnode->type!=NODE_GROUP)
815 error("Not a group");
817 if(nodeGroupUnGroup(snode->edittree, gnode)) {
819 // allqueue(REDRAWNODE, 0);
822 error("Can't ungroup");
827 /* when links in groups change, inputs/outputs change, nodes added/deleted... */
828 static void snode_verify_groups(SpaceNode *snode)
832 gnode= snode_get_editgroup(snode);
834 /* does all materials */
836 nodeVerifyGroup((bNodeTree *)gnode->id);
842 static void node_addgroup(SpaceNode *snode)
845 int tot= 0, offs, val;
848 if(snode->edittree!=snode->nodetree) {
849 error("Can not add a Group in a Group");
853 /* construct menu with choices */
854 for(ngroup= G.main->nodetree.first; ngroup; ngroup= ngroup->id.next) {
855 if(ngroup->type==snode->treetype)
859 error("No groups available in database");
862 strp= MEM_mallocN(32*tot+32, "menu");
863 strcpy(strp, "Add Group %t");
866 for(tot=0, ngroup= G.main->nodetree.first; ngroup; ngroup= ngroup->id.next, tot++) {
867 if(ngroup->type==snode->treetype)
868 offs+= sprintf(strp+offs, "|%s %%x%d", ngroup->id.name+2, tot);
873 ngroup= BLI_findlink(&G.main->nodetree, val);
875 bNode *node= nodeAddNodeType(snode->edittree, NODE_GROUP, ngroup, NULL);
882 node_deselectall(snode, 0);
884 getmouseco_areawin(mval);
885 areamouseco_to_ipoco(G.v2d, mval, &locx, &locy);
888 node->locy= locy + 60.0f; // arbitrary.. so its visible
889 node->flag |= SELECT;
891 id_us_plus(node->id);
893 node_set_active(snode, node);
902 /* ************************** Node generic ************** */
904 /* allows to walk the list in order of visibility */
905 bNode *next_node(bNodeTree *ntree)
907 static bNode *current=NULL, *last= NULL;
910 /* set current to the first selected node */
911 for(current= ntree->nodes.last; current; current= current->prev)
912 if(current->flag & NODE_SELECT)
915 /* set last to the first unselected node */
916 for(last= ntree->nodes.last; last; last= last->prev)
917 if((last->flag & NODE_SELECT)==0)
925 /* no nodes, or we are ready */
929 /* now we walk the list backwards, but we always return current */
930 if(current->flag & NODE_SELECT) {
931 bNode *node= current;
933 /* find previous selected */
934 current= current->prev;
935 while(current && (current->flag & NODE_SELECT)==0)
936 current= current->prev;
938 /* find first unselected */
945 bNode *node= current;
947 /* find previous unselected */
948 current= current->prev;
949 while(current && (current->flag & NODE_SELECT))
950 current= current->prev;
958 /* is rct in visible part of node? */
959 static bNode *visible_node(SpaceNode *snode, rctf *rct)
963 for(next_node(snode->edittree); (tnode=next_node(NULL));) {
964 if(BLI_isect_rctf(&tnode->totr, rct, NULL))
970 void snode_home(ScrArea *sa, ARegion *ar, SpaceNode* snode)
974 float oldwidth, oldheight, width, height;
980 oldwidth= cur->xmax - cur->xmin;
981 oldheight= cur->ymax - cur->ymin;
983 cur->xmin= cur->ymin= 0.0f;
987 if(snode->edittree) {
988 for(node= snode->edittree->nodes.first; node; node= node->next) {
991 ar->v2d.cur= node->totr;
994 BLI_union_rctf(cur, &node->totr);
1001 width= cur->xmax - cur->xmin;
1002 height= cur->ymax- cur->ymin;
1003 if(width > height) {
1005 newheight= oldheight * width/oldwidth;
1006 cur->ymin= cur->ymin - newheight/4;
1007 cur->ymax= cur->ymin + newheight;
1011 newwidth= oldwidth * height/oldheight;
1012 cur->xmin= cur->xmin - newwidth/4;
1013 cur->xmax= cur->xmin + newwidth;
1016 ar->v2d.tot= ar->v2d.cur;
1017 UI_view2d_curRect_validate(&ar->v2d);
1021 static void snode_bg_viewmove(SpaceNode *snode)
1027 short mval[2], mvalo[2];
1028 short rectx, recty, xmin, xmax, ymin, ymax, pad;
1031 ima= BKE_image_verify_viewer(IMA_TYPE_COMPOSITE, "Viewer Node");
1032 ibuf= BKE_image_get_ibuf(ima, NULL);
1044 xmin = -(sa->winx/2) - rectx/2 + pad;
1045 xmax = sa->winx/2 + rectx/2 - pad;
1046 ymin = -(sa->winy/2) - recty/2 + pad;
1047 ymax = sa->winy/2 + recty/2 - pad;
1049 getmouseco_sc(mvalo);
1051 /* store the old cursor to temporarily change it */
1052 oldcursor=get_cursor();
1053 win=winlay_get_active_window();
1055 SetBlenderCursor(BC_NSEW_SCROLLCURSOR);
1057 while(get_mbut()&(L_MOUSE|M_MOUSE)) {
1059 getmouseco_sc(mval);
1061 if(mvalo[0]!=mval[0] || mvalo[1]!=mval[1]) {
1063 snode->xof -= (mvalo[0]-mval[0]);
1064 snode->yof -= (mvalo[1]-mval[1]);
1066 /* prevent dragging image outside of the window and losing it! */
1067 CLAMP(snode->xof, xmin, xmax);
1068 CLAMP(snode->yof, ymin, ymax);
1073 scrarea_do_windraw(curarea);
1074 screen_swapbuffers();
1076 else BIF_wait_for_statechange();
1079 window_set_cursor(win, oldcursor);
1083 /* ********************** size widget operator ******************** */
1085 typedef struct NodeSizeWidget {
1090 static int node_resize_modal(bContext *C, wmOperator *op, wmEvent *event)
1092 SpaceNode *snode= CTX_wm_space_node(C);
1093 ARegion *ar= CTX_wm_region(C);
1094 bNode *node= editnode_get_active(snode->edittree);
1095 NodeSizeWidget *nsw= op->customdata;
1098 switch (event->type) {
1101 UI_view2d_region_to_view(&ar->v2d, event->x - ar->winrct.xmin, event->y - ar->winrct.ymin,
1104 if(node->flag & NODE_HIDDEN) {
1105 node->miniwidth= nsw->oldwidth + mx - nsw->mxstart;
1106 CLAMP(node->miniwidth, 0.0f, 100.0f);
1109 node->width= nsw->oldwidth + mx - nsw->mxstart;
1110 CLAMP(node->width, node->typeinfo->minwidth, node->typeinfo->maxwidth);
1113 if(snode->nodetree->type == NTREE_TEXTURE)
1114 ntreeTexUpdatePreviews(snode->nodetree); /* XXX texture nodes should follow shader node methods (ton) */
1116 ED_region_tag_redraw(ar);
1125 op->customdata= NULL;
1127 return OPERATOR_FINISHED;
1130 return OPERATOR_RUNNING_MODAL;
1133 static int node_resize_invoke(bContext *C, wmOperator *op, wmEvent *event)
1135 SpaceNode *snode= CTX_wm_space_node(C);
1136 ARegion *ar= CTX_wm_region(C);
1137 bNode *node= editnode_get_active(snode->edittree);
1142 UI_view2d_region_to_view(&ar->v2d, event->x - ar->winrct.xmin, event->y - ar->winrct.ymin,
1143 &snode->mx, &snode->my);
1145 totr.xmin= totr.xmax-10.0f;
1146 totr.ymax= totr.ymin+10.0f;
1148 if(BLI_in_rctf(&totr, snode->mx, snode->my)) {
1149 NodeSizeWidget *nsw= MEM_callocN(sizeof(NodeSizeWidget), "size widget op data");
1151 op->customdata= nsw;
1152 nsw->mxstart= snode->mx;
1155 if(node->flag & NODE_HIDDEN)
1156 nsw->oldwidth= node->miniwidth;
1158 nsw->oldwidth= node->width;
1160 /* add modal handler */
1161 WM_event_add_modal_handler(C, &CTX_wm_window(C)->handlers, op);
1163 return OPERATOR_RUNNING_MODAL;
1166 return OPERATOR_PASS_THROUGH;
1169 void NODE_OT_resize(wmOperatorType *ot)
1172 ot->name= "Resize Node";
1173 ot->idname= "NODE_OT_resize";
1176 ot->invoke= node_resize_invoke;
1177 ot->modal= node_resize_modal;
1178 ot->poll= ED_operator_node_active;
1181 ot->flag= OPTYPE_BLOCKING;
1187 /* ******************** rename ******************* */
1189 void node_rename(SpaceNode *snode)
1191 bNode *node, *rename_node;
1192 short found_node= 0;
1194 /* check if a node is selected */
1195 for(node= snode->edittree->nodes.first; node; node= node->next) {
1196 if(node->flag & SELECT) {
1203 rename_node= nodeGetActive(snode->edittree);
1204 node_rename_but((char *)rename_node->username);
1206 // allqueue(REDRAWNODE, 1);
1210 /* ********************** select ******************** */
1212 /* used in buttons to check context, also checks for edited groups */
1213 bNode *editnode_get_active_idnode(bNodeTree *ntree, short id_code)
1215 return nodeGetActiveID(ntree, id_code);
1218 /* used in buttons to check context, also checks for edited groups */
1219 Material *editnode_get_active_material(Material *ma)
1221 if(ma && ma->use_nodes && ma->nodetree) {
1222 bNode *node= editnode_get_active_idnode(ma->nodetree, ID_MA);
1224 return (Material *)node->id;
1234 void node_deselectall(SpaceNode *snode, int swap)
1239 for(node= snode->edittree->nodes.first; node; node= node->next)
1240 if(node->flag & SELECT)
1243 for(node= snode->edittree->nodes.first; node; node= node->next)
1244 node->flag |= SELECT;
1247 /* else pass on to deselect */
1250 for(node= snode->edittree->nodes.first; node; node= node->next)
1251 node->flag &= ~SELECT;
1255 int node_has_hidden_sockets(bNode *node)
1259 for(sock= node->inputs.first; sock; sock= sock->next)
1260 if(sock->flag & SOCK_HIDDEN)
1262 for(sock= node->outputs.first; sock; sock= sock->next)
1263 if(sock->flag & SOCK_HIDDEN)
1269 static void node_hide_unhide_sockets(SpaceNode *snode, bNode *node)
1274 if( node_has_hidden_sockets(node) ) {
1275 for(sock= node->inputs.first; sock; sock= sock->next)
1276 sock->flag &= ~SOCK_HIDDEN;
1277 for(sock= node->outputs.first; sock; sock= sock->next)
1278 sock->flag &= ~SOCK_HIDDEN;
1281 bNode *gnode= snode_get_editgroup(snode);
1283 /* hiding inside group should not break links in other group users */
1285 nodeGroupSocketUseFlags((bNodeTree *)gnode->id);
1286 for(sock= node->inputs.first; sock; sock= sock->next)
1287 if(!(sock->flag & SOCK_IN_USE))
1288 if(sock->link==NULL)
1289 sock->flag |= SOCK_HIDDEN;
1290 for(sock= node->outputs.first; sock; sock= sock->next)
1291 if(!(sock->flag & SOCK_IN_USE))
1292 if(nodeCountSocketLinks(snode->edittree, sock)==0)
1293 sock->flag |= SOCK_HIDDEN;
1296 /* hide unused sockets */
1297 for(sock= node->inputs.first; sock; sock= sock->next) {
1298 if(sock->link==NULL)
1299 sock->flag |= SOCK_HIDDEN;
1301 for(sock= node->outputs.first; sock; sock= sock->next) {
1302 if(nodeCountSocketLinks(snode->edittree, sock)==0)
1303 sock->flag |= SOCK_HIDDEN;
1308 // allqueue(REDRAWNODE, 1);
1309 snode_verify_groups(snode);
1313 /*static*/ int do_header_node(SpaceNode *snode, bNode *node, float mx, float my)
1315 rctf totr= node->totr;
1317 totr.ymin= totr.ymax-20.0f;
1319 totr.xmax= totr.xmin+15.0f;
1320 if(BLI_in_rctf(&totr, mx, my)) {
1321 node->flag |= NODE_HIDDEN;
1322 // allqueue(REDRAWNODE, 0);
1326 totr.xmax= node->totr.xmax;
1327 totr.xmin= totr.xmax-18.0f;
1328 if(node->typeinfo->flag & NODE_PREVIEW) {
1329 if(BLI_in_rctf(&totr, mx, my)) {
1330 node->flag ^= NODE_PREVIEW;
1331 // allqueue(REDRAWNODE, 0);
1336 if(node->type == NODE_GROUP) {
1337 if(BLI_in_rctf(&totr, mx, my)) {
1338 snode_make_group_editable(snode, node);
1343 if(node->typeinfo->flag & NODE_OPTIONS) {
1344 if(BLI_in_rctf(&totr, mx, my)) {
1345 node->flag ^= NODE_OPTIONS;
1346 // allqueue(REDRAWNODE, 0);
1351 /* hide unused sockets */
1352 if(BLI_in_rctf(&totr, mx, my)) {
1353 node_hide_unhide_sockets(snode, node);
1358 totr.xmin= totr.xmax-10.0f;
1359 totr.ymax= totr.ymin+10.0f;
1360 if(BLI_in_rctf(&totr, mx, my)) {
1361 // scale_node(snode, node);
1367 /*static*/ int do_header_hidden_node(SpaceNode *snode, bNode *node, float mx, float my)
1369 rctf totr= node->totr;
1371 totr.xmax= totr.xmin+15.0f;
1372 if(BLI_in_rctf(&totr, mx, my)) {
1373 node->flag &= ~NODE_HIDDEN;
1374 // allqueue(REDRAWNODE, 0);
1378 totr.xmax= node->totr.xmax;
1379 totr.xmin= node->totr.xmax-15.0f;
1380 if(BLI_in_rctf(&totr, mx, my)) {
1381 // scale_node(snode, node);
1387 static void node_link_viewer(SpaceNode *snode, bNode *tonode)
1392 if(tonode==NULL || tonode->outputs.first==NULL)
1394 if( ELEM(tonode->type, CMP_NODE_VIEWER, CMP_NODE_SPLITVIEWER))
1398 for(node= snode->edittree->nodes.first; node; node= node->next)
1399 if( ELEM(node->type, CMP_NODE_VIEWER, CMP_NODE_SPLITVIEWER))
1400 if(node->flag & NODE_DO_OUTPUT)
1406 /* get link to viewer */
1407 for(link= snode->edittree->links.first; link; link= link->next)
1408 if(link->tonode==node)
1412 link->fromnode= tonode;
1413 link->fromsock= tonode->outputs.first;
1414 NodeTagChanged(snode->edittree, node);
1416 // XXX snode_handle_recalc(snode);
1422 void node_active_link_viewer(SpaceNode *snode)
1424 bNode *node= editnode_get_active(snode->edittree);
1426 node_link_viewer(snode, node);
1429 /* return 0, nothing done */
1430 /*static*/ int node_mouse_groupheader(SpaceNode *snode)
1434 // XXX short mval[2];
1436 gnode= snode_get_editgroup(snode);
1437 if(gnode==NULL) return 0;
1439 // XXX getmouseco_areawin(mval);
1440 // XXX areamouseco_to_ipoco(G.v2d, mval, &mx, &my);
1442 /* click in header or outside? */
1443 if(BLI_in_rctf(&gnode->totr, mx, my)==0) {
1444 rctf rect= gnode->totr;
1446 rect.ymax += NODE_DY;
1447 if(BLI_in_rctf(&rect, mx, my)==0)
1448 snode_make_group_editable(snode, NULL); /* toggles, so exits editmode */
1450 // XXX transform_nodes(snode->nodetree, 'g', "Move group");
1457 /* checks snode->mouse position, and returns found node/socket */
1458 /* type is SOCK_IN and/or SOCK_OUT */
1459 static int find_indicated_socket(SpaceNode *snode, bNode **nodep, bNodeSocket **sockp, int in_out)
1465 /* check if we click in a socket */
1466 for(node= snode->edittree->nodes.first; node; node= node->next) {
1468 rect.xmin = snode->mx - NODE_SOCKSIZE+3;
1469 rect.ymin = snode->my - NODE_SOCKSIZE+3;
1470 rect.xmax = rect.xmin + 2*NODE_SOCKSIZE+6;
1471 rect.ymax = rect.ymin + 2*NODE_SOCKSIZE+6;
1473 if (!(node->flag & NODE_HIDDEN)) {
1474 /* extra padding inside and out - allow dragging on the text areas too */
1475 if (in_out == SOCK_IN) {
1476 rect.xmax += NODE_SOCKSIZE;
1477 rect.xmin -= NODE_SOCKSIZE*4;
1478 } else if (in_out == SOCK_OUT) {
1479 rect.xmax += NODE_SOCKSIZE*4;
1480 rect.xmin -= NODE_SOCKSIZE;
1484 if(in_out & SOCK_IN) {
1485 for(sock= node->inputs.first; sock; sock= sock->next) {
1486 if(!(sock->flag & (SOCK_HIDDEN|SOCK_UNAVAIL))) {
1487 if(BLI_in_rctf(&rect, sock->locx, sock->locy)) {
1488 if(node == visible_node(snode, &rect)) {
1497 if(in_out & SOCK_OUT) {
1498 for(sock= node->outputs.first; sock; sock= sock->next) {
1499 if(!(sock->flag & (SOCK_HIDDEN|SOCK_UNAVAIL))) {
1500 if(BLI_in_rctf(&rect, sock->locx, sock->locy)) {
1501 if(node == visible_node(snode, &rect)) {
1514 static int node_socket_hilights(SpaceNode *snode, int in_out)
1517 bNodeSocket *sock, *tsock, *socksel= NULL;
1520 if(snode->edittree==NULL) return 0;
1522 /* deselect socks */
1523 for(node= snode->edittree->nodes.first; node; node= node->next) {
1524 for(sock= node->inputs.first; sock; sock= sock->next) {
1525 if(sock->flag & SELECT) {
1526 sock->flag &= ~SELECT;
1531 for(sock= node->outputs.first; sock; sock= sock->next) {
1532 if(sock->flag & SELECT) {
1533 sock->flag &= ~SELECT;
1540 // XXX mousepos should be set here!
1542 if(find_indicated_socket(snode, &node, &tsock, in_out)) {
1543 tsock->flag |= SELECT;
1544 if(redraw==1 && tsock==socksel) redraw= 0;
1551 /* ****************** Add *********************** */
1553 void snode_autoconnect(SpaceNode *snode, bNode *node_to, int flag)
1555 bNodeSocket *sock, *sockfrom[8];
1556 bNode *node, *nodefrom[8];
1557 int totsock= 0, socktype=0;
1559 if(node_to==NULL || node_to->inputs.first==NULL)
1562 /* no inputs for node allowed (code it) */
1564 /* connect first 1 socket type now */
1565 for(sock= node_to->inputs.first; sock; sock= sock->next)
1566 if(socktype<sock->type)
1567 socktype= sock->type;
1570 /* find potential sockets, max 8 should work */
1571 for(node= snode->edittree->nodes.first; node; node= node->next) {
1572 if((node->flag & flag) && node!=node_to) {
1573 for(sock= node->outputs.first; sock; sock= sock->next) {
1574 if(!(sock->flag & (SOCK_HIDDEN|SOCK_UNAVAIL))) {
1575 sockfrom[totsock]= sock;
1576 nodefrom[totsock]= node;
1587 /* now just get matching socket types and create links */
1588 for(sock= node_to->inputs.first; sock; sock= sock->next) {
1591 for(a=0; a<totsock; a++) {
1593 if(sock->type==sockfrom[a]->type && sock->type==socktype) {
1594 nodeAddLink(snode->edittree, nodefrom[a], sockfrom[a], node_to, sock);
1602 ntreeSolveOrder(snode->edittree);
1606 /* can be called from menus too, but they should do own undopush and redraws */
1607 bNode *node_add_node(SpaceNode *snode, Scene *scene, int type, float locx, float locy)
1609 bNode *node= NULL, *gnode;
1611 node_deselectall(snode, 0);
1613 if(type>=NODE_DYNAMIC_MENU) {
1614 node= nodeAddNodeType(snode->edittree, type, NULL, NULL);
1616 else if(type>=NODE_GROUP_MENU) {
1617 if(snode->edittree!=snode->nodetree) {
1618 // XXX error("Can not add a Group in a Group");
1622 bNodeTree *ngroup= BLI_findlink(&G.main->nodetree, type-NODE_GROUP_MENU);
1624 node= nodeAddNodeType(snode->edittree, NODE_GROUP, ngroup, NULL);
1628 node= nodeAddNodeType(snode->edittree, type, NULL, NULL);
1633 node->locy= locy + 60.0f; // arbitrary.. so its visible
1634 node->flag |= SELECT;
1636 gnode= snode_get_editgroup(snode);
1638 node->locx -= gnode->locx;
1639 node->locy -= gnode->locy;
1642 snode_verify_groups(snode);
1643 node_set_active(snode, node);
1645 if(snode->nodetree->type==NTREE_COMPOSIT) {
1646 if(ELEM(node->type, CMP_NODE_R_LAYERS, CMP_NODE_COMPOSITE))
1647 node->id = &scene->id;
1649 ntreeCompositForceHidden(snode->edittree, scene);
1653 id_us_plus(node->id);
1655 NodeTagChanged(snode->edittree, node);
1658 if(snode->nodetree->type==NTREE_TEXTURE) {
1659 ntreeTexCheckCyclics(snode->edittree);
1660 ntreeTexUpdatePreviews(snode->edittree); /* XXX texture nodes should follow shader node methods (ton) */
1668 void node_mute(SpaceNode *snode)
1672 /* no disabling inside of groups */
1673 if(snode_get_editgroup(snode))
1676 for(node= snode->edittree->nodes.first; node; node= node->next) {
1677 if(node->flag & SELECT) {
1678 if(node->inputs.first && node->outputs.first) {
1679 if(node->flag & NODE_MUTED)
1680 node->flag &= ~NODE_MUTED;
1682 node->flag |= NODE_MUTED;
1692 int node_duplicate_exec(bContext *C, wmOperator *op)
1694 SpaceNode *snode= CTX_wm_space_node(C);
1696 ntreeCopyTree(snode->edittree, 1); /* 1 == internally selected nodes */
1698 ntreeSolveOrder(snode->edittree);
1699 snode_verify_groups(snode);
1700 snode_handle_recalc(C, snode);
1702 return OPERATOR_FINISHED;
1705 static int node_duplicate_invoke(bContext *C, wmOperator *op, wmEvent *event)
1707 node_duplicate_exec(C, op);
1709 RNA_int_set(op->ptr, "mode", TFM_TRANSLATION);
1710 WM_operator_name_call(C, "TFM_OT_transform", WM_OP_INVOKE_REGION_WIN, op->ptr);
1712 return OPERATOR_FINISHED;
1715 void NODE_OT_duplicate(wmOperatorType *ot)
1719 ot->name= "Duplicate Nodes";
1720 ot->description = "Duplicate the nodes.";
1721 ot->idname= "NODE_OT_duplicate";
1724 ot->invoke= node_duplicate_invoke;
1725 ot->exec= node_duplicate_exec;
1727 ot->poll= ED_operator_node_active;
1730 ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
1732 /* to give to transform */
1733 RNA_def_int(ot->srna, "mode", TFM_TRANSLATION, 0, INT_MAX, "Mode", "", 0, INT_MAX);
1738 static void node_insert_convertor(SpaceNode *snode, bNodeLink *link)
1740 bNode *newnode= NULL;
1742 if(link->fromsock->type==SOCK_RGBA && link->tosock->type==SOCK_VALUE) {
1743 if(snode->edittree->type==NTREE_SHADER)
1744 newnode= node_add_node(snode, SH_NODE_RGBTOBW, 0.0f, 0.0f);
1745 else if(snode->edittree->type==NTREE_COMPOSIT)
1746 newnode= node_add_node(snode, CMP_NODE_RGBTOBW, 0.0f, 0.0f);
1750 else if(link->fromsock->type==SOCK_VALUE && link->tosock->type==SOCK_RGBA) {
1751 if(snode->edittree->type==NTREE_SHADER)
1752 newnode= node_add_node(snode, SH_NODE_VALTORGB, 0.0f, 0.0f);
1753 else if(snode->edittree->type==NTREE_COMPOSIT)
1754 newnode= node_add_node(snode, CMP_NODE_VALTORGB, 0.0f, 0.0f);
1760 /* dangerous assumption to use first in/out socks, but thats fine for now */
1761 newnode->flag |= NODE_HIDDEN;
1762 newnode->locx= 0.5f*(link->fromsock->locx + link->tosock->locx);
1763 newnode->locy= 0.5f*(link->fromsock->locy + link->tosock->locy) + HIDDEN_RAD;
1765 nodeAddLink(snode->edittree, newnode, newnode->outputs.first, link->tonode, link->tosock);
1766 link->tonode= newnode;
1767 link->tosock= newnode->inputs.first;
1774 /* *************************** add link op ******************** */
1776 /* temp data to pass on to modal */
1777 typedef struct NodeLinkDrag
1785 /*static*/ void reset_sel_socket(SpaceNode *snode, int in_out)
1790 for(node= snode->edittree->nodes.first; node; node= node->next) {
1791 if(in_out & SOCK_IN) {
1792 for(sock= node->inputs.first; sock; sock= sock->next)
1793 if(sock->flag & SOCK_SEL) sock->flag&= ~SOCK_SEL;
1795 if(in_out & SOCK_OUT) {
1796 for(sock= node->outputs.first; sock; sock= sock->next)
1797 if(sock->flag & SOCK_SEL) sock->flag&= ~SOCK_SEL;
1803 static void node_remove_extra_links(SpaceNode *snode, bNodeSocket *tsock, bNodeLink *link)
1808 if(tsock && nodeCountSocketLinks(snode->edittree, link->tosock) > tsock->limit) {
1810 for(tlink= snode->edittree->links.first; tlink; tlink= tlink->next) {
1811 if(link!=tlink && tlink->tosock==link->tosock)
1815 /* is there a free input socket with same type? */
1816 for(sock= tlink->tonode->inputs.first; sock; sock= sock->next) {
1817 if(sock->type==tlink->fromsock->type)
1818 if(nodeCountSocketLinks(snode->edittree, sock) < sock->limit)
1822 tlink->tosock= sock;
1823 sock->flag &= ~SOCK_HIDDEN;
1826 nodeRemLink(snode->edittree, tlink);
1832 /* loop that adds a nodelink, called by function below */
1833 /* in_out = starting socket */
1834 static int node_link_modal(bContext *C, wmOperator *op, wmEvent *event)
1836 SpaceNode *snode= CTX_wm_space_node(C);
1837 ARegion *ar= CTX_wm_region(C);
1838 NodeLinkDrag *nldrag= op->customdata;
1839 bNode *tnode, *node;
1840 bNodeSocket *tsock= NULL, *sock;
1844 in_out= nldrag->in_out;
1849 UI_view2d_region_to_view(&ar->v2d, event->x - ar->winrct.xmin, event->y - ar->winrct.ymin,
1850 &snode->mx, &snode->my);
1852 switch (event->type) {
1855 if(in_out==SOCK_OUT) {
1856 if(find_indicated_socket(snode, &tnode, &tsock, SOCK_IN)) {
1857 if(nodeFindLink(snode->edittree, sock, tsock)==NULL) {
1858 if(tnode!=node && link->tonode!=tnode && link->tosock!= tsock) {
1859 link->tonode= tnode;
1860 link->tosock= tsock;
1861 ntreeSolveOrder(snode->edittree); /* for interactive red line warning */
1871 if(find_indicated_socket(snode, &tnode, &tsock, SOCK_OUT)) {
1872 if(nodeFindLink(snode->edittree, sock, tsock)==NULL) {
1873 if(nodeCountSocketLinks(snode->edittree, tsock) < tsock->limit) {
1874 if(tnode!=node && link->fromnode!=tnode && link->fromsock!= tsock) {
1875 link->fromnode= tnode;
1876 link->fromsock= tsock;
1877 ntreeSolveOrder(snode->edittree); /* for interactive red line warning */
1883 link->fromnode= NULL;
1884 link->fromsock= NULL;
1887 /* hilight target sockets only */
1888 node_socket_hilights(snode, in_out==SOCK_OUT?SOCK_IN:SOCK_OUT);
1889 ED_region_tag_redraw(ar);
1897 if(link->tonode==NULL || link->fromnode==NULL) {
1898 nodeRemLink(snode->edittree, link);
1901 /* send changed events for original tonode and new */
1903 NodeTagChanged(snode->edittree, link->tonode);
1905 /* we might need to remove a link */
1906 if(in_out==SOCK_OUT) node_remove_extra_links(snode, link->tosock, link);
1909 ntreeSolveOrder(snode->edittree);
1910 snode_verify_groups(snode);
1911 snode_handle_recalc(C, snode);
1913 MEM_freeN(op->customdata);
1914 op->customdata= NULL;
1916 return OPERATOR_FINISHED;
1919 return OPERATOR_RUNNING_MODAL;
1922 /* return 1 when socket clicked */
1923 static int node_link_init(SpaceNode *snode, NodeLinkDrag *nldrag)
1927 /* output indicated? */
1928 if(find_indicated_socket(snode, &nldrag->node, &nldrag->sock, SOCK_OUT)) {
1929 if(nodeCountSocketLinks(snode->edittree, nldrag->sock) < nldrag->sock->limit)
1932 /* find if we break a link */
1933 for(link= snode->edittree->links.first; link; link= link->next) {
1934 if(link->fromsock==nldrag->sock)
1938 nldrag->node= link->tonode;
1939 nldrag->sock= link->tosock;
1940 nodeRemLink(snode->edittree, link);
1946 else if(find_indicated_socket(snode, &nldrag->node, &nldrag->sock, SOCK_IN)) {
1947 if(nodeCountSocketLinks(snode->edittree, nldrag->sock) < nldrag->sock->limit)
1950 /* find if we break a link */
1951 for(link= snode->edittree->links.first; link; link= link->next) {
1952 if(link->tosock==nldrag->sock)
1956 /* send changed event to original tonode */
1958 NodeTagChanged(snode->edittree, link->tonode);
1960 nldrag->node= link->fromnode;
1961 nldrag->sock= link->fromsock;
1962 nodeRemLink(snode->edittree, link);
1971 static int node_link_invoke(bContext *C, wmOperator *op, wmEvent *event)
1973 SpaceNode *snode= CTX_wm_space_node(C);
1974 ARegion *ar= CTX_wm_region(C);
1975 NodeLinkDrag *nldrag= MEM_callocN(sizeof(NodeLinkDrag), "drag link op customdata");
1977 UI_view2d_region_to_view(&ar->v2d, event->x - ar->winrct.xmin, event->y - ar->winrct.ymin,
1978 &snode->mx, &snode->my);
1980 nldrag->in_out= node_link_init(snode, nldrag);
1982 if(nldrag->in_out) {
1983 op->customdata= nldrag;
1985 /* we make a temporal link */
1986 if(nldrag->in_out==SOCK_OUT)
1987 nldrag->link= nodeAddLink(snode->edittree, nldrag->node, nldrag->sock, NULL, NULL);
1989 nldrag->link= nodeAddLink(snode->edittree, NULL, NULL, nldrag->node, nldrag->sock);
1991 /* add modal handler */
1992 WM_event_add_modal_handler(C, &CTX_wm_window(C)->handlers, op);
1994 return OPERATOR_RUNNING_MODAL;
1998 return OPERATOR_CANCELLED|OPERATOR_PASS_THROUGH;
2002 void NODE_OT_link(wmOperatorType *ot)
2005 ot->name= "Link Nodes";
2006 ot->idname= "NODE_OT_link";
2009 ot->invoke= node_link_invoke;
2010 ot->modal= node_link_modal;
2011 // ot->exec= node_link_exec;
2012 ot->poll= ED_operator_node_active;
2015 ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO|OPTYPE_BLOCKING;
2019 void node_delete(SpaceNode *snode)
2024 for(node= snode->edittree->nodes.first; node; node= next) {
2026 if(node->flag & SELECT) {
2027 /* set selin and selout NULL if the sockets belong to a node to be deleted */
2028 for(sock= node->inputs.first; sock; sock= sock->next)
2029 if(snode->edittree->selin == sock) snode->edittree->selin= NULL;
2031 for(sock= node->outputs.first; sock; sock= sock->next)
2032 if(snode->edittree->selout == sock) snode->edittree->selout= NULL;
2034 /* check id user here, nodeFreeNode is called for free dbase too */
2037 nodeFreeNode(snode->edittree, node);
2041 snode_verify_groups(snode);
2043 // snode_handle_recalc(snode);
2044 // allqueue(REDRAWNODE, 1);
2048 void node_hide(SpaceNode *snode)
2051 int nothidden=0, ishidden=0;
2053 for(node= snode->edittree->nodes.first; node; node= node->next) {
2054 if(node->flag & SELECT) {
2055 if(node->flag & NODE_HIDDEN)
2061 for(node= snode->edittree->nodes.first; node; node= node->next) {
2062 if(node->flag & SELECT) {
2063 if( (ishidden && nothidden) || ishidden==0)
2064 node->flag |= NODE_HIDDEN;
2066 node->flag &= ~NODE_HIDDEN;
2073 void node_insert_key(SpaceNode *snode)
2075 bNode *node= editnode_get_active(snode->edittree);
2080 if(node->type==CMP_NODE_TIME) {
2081 if(node->custom1<node->custom2) {
2083 CurveMapping *cumap= node->storage;
2086 curval= (float)(CFRA - node->custom1)/(float)(node->custom2-node->custom1);
2087 fval= curvemapping_evaluateF(cumap, 0, curval);
2089 if(fbutton(&fval, 0.0f, 1.0f, 10, 10, "Insert Value")) {
2090 curvemap_insert(cumap->cm, curval, fval);
2097 void node_select_linked(SpaceNode *snode, int out)
2102 /* NODE_TEST is the free flag */
2103 for(node= snode->edittree->nodes.first; node; node= node->next)
2104 node->flag &= ~NODE_TEST;
2106 for(link= snode->edittree->links.first; link; link= link->next) {
2108 if(link->fromnode->flag & NODE_SELECT)
2109 link->tonode->flag |= NODE_TEST;
2112 if(link->tonode->flag & NODE_SELECT)
2113 link->fromnode->flag |= NODE_TEST;
2117 for(node= snode->edittree->nodes.first; node; node= node->next)
2118 if(node->flag & NODE_TEST)
2119 node->flag |= NODE_SELECT;
2123 /* makes a link between selected output and input sockets */
2124 void node_make_link(SpaceNode *snode)
2126 bNode *fromnode, *tonode;
2128 bNodeSocket *outsock= snode->edittree->selout;
2129 bNodeSocket *insock= snode->edittree->selin;
2131 if(!insock || !outsock) return;
2132 if(nodeFindLink(snode->edittree, outsock, insock)) return;
2134 if(nodeFindNode(snode->edittree, outsock, &fromnode, NULL) &&
2135 nodeFindNode(snode->edittree, insock, &tonode, NULL)) {
2136 link= nodeAddLink(snode->edittree, fromnode, outsock, tonode, insock);
2137 NodeTagChanged(snode->edittree, tonode);
2138 node_remove_extra_links(snode, insock, link);
2142 ntreeSolveOrder(snode->edittree);
2143 snode_verify_groups(snode);
2144 // XXX snode_handle_recalc(snode);
2149 /* ********************** Cut Link operator ***************** */
2151 #define LINK_RESOL 12
2152 static int cut_links_intersect(bNodeLink *link, float mcoords[][2], int tot)
2154 float coord_array[LINK_RESOL+1][2];
2157 if(node_link_bezier_points(NULL, NULL, link, coord_array, LINK_RESOL)) {
2159 for(i=0; i<tot-1; i++)
2160 for(b=0; b<LINK_RESOL-1; b++)
2161 if(IsectLL2Df(mcoords[i], mcoords[i+1], coord_array[b], coord_array[b+1]) > 0)
2167 static int cut_links_exec(bContext *C, wmOperator *op)
2169 SpaceNode *snode= CTX_wm_space_node(C);
2170 ARegion *ar= CTX_wm_region(C);
2171 float mcoords[256][2];
2174 RNA_BEGIN(op->ptr, itemptr, "path") {
2177 RNA_float_get_array(&itemptr, "loc", loc);
2178 UI_view2d_region_to_view(&ar->v2d, (short)loc[0], (short)loc[1],
2179 &mcoords[i][0], &mcoords[i][1]);
2186 bNodeLink *link, *next;
2188 for(link= snode->edittree->links.first; link; link= next) {
2191 if(cut_links_intersect(link, mcoords, i)) {
2192 NodeTagChanged(snode->edittree, link->tonode);
2193 nodeRemLink(snode->edittree, link);
2197 ntreeSolveOrder(snode->edittree);
2198 snode_verify_groups(snode);
2199 snode_handle_recalc(C, snode);
2201 return OPERATOR_FINISHED;
2204 return OPERATOR_PASS_THROUGH;;
2207 void NODE_OT_links_cut(wmOperatorType *ot)
2211 ot->name= "Cut links";
2212 ot->idname= "NODE_OT_links_cut";
2214 ot->invoke= WM_gesture_lines_invoke;
2215 ot->modal= WM_gesture_lines_modal;
2216 ot->exec= cut_links_exec;
2218 ot->poll= ED_operator_node_active;
2221 ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
2223 prop= RNA_def_property(ot->srna, "path", PROP_COLLECTION, PROP_NONE);
2224 RNA_def_property_struct_runtime(prop, &RNA_OperatorMousePath);
2226 RNA_def_int(ot->srna, "cursor", BC_KNIFECURSOR, 0, INT_MAX, "Cursor", "", 0, INT_MAX);
2229 /* ******************************** */
2231 /* goes over all scenes, reads render layerss */
2232 void node_read_renderlayers(SpaceNode *snode)
2234 Scene *curscene= NULL; // XXX
2238 /* first tag scenes unread */
2239 for(scene= G.main->scene.first; scene; scene= scene->id.next)
2240 scene->id.flag |= LIB_DOIT;
2242 for(node= snode->edittree->nodes.first; node; node= node->next) {
2243 if(node->type==CMP_NODE_R_LAYERS) {
2245 if(id->flag & LIB_DOIT) {
2246 RE_ReadRenderResult(curscene, (Scene *)id);
2247 ntreeCompositTagRender((Scene *)id);
2248 id->flag &= ~LIB_DOIT;
2253 // XXX snode_handle_recalc(snode);
2256 void node_read_fullsamplelayers(SpaceNode *snode)
2258 Scene *curscene= NULL; // XXX
2259 Render *re= RE_NewRender(curscene->id.name);
2263 //BIF_init_render_callbacks(re, 1);
2264 RE_MergeFullSample(re, curscene, snode->nodetree);
2265 //BIF_end_render_callbacks();
2267 // allqueue(REDRAWNODE, 1);
2268 // allqueue(REDRAWIMAGE, 1);
2273 void imagepaint_composite_tags(bNodeTree *ntree, Image *image, ImageUser *iuser)
2280 /* search for renderresults */
2281 if(image->type==IMA_TYPE_R_RESULT) {
2282 for(node= ntree->nodes.first; node; node= node->next) {
2283 if(node->type==CMP_NODE_R_LAYERS && node->id==NULL) {
2284 /* imageuser comes from ImageWin, so indexes are offset 1 */
2285 if(node->custom1==iuser->layer-1)
2286 NodeTagChanged(ntree, node);
2291 for(node= ntree->nodes.first; node; node= node->next) {
2292 if(node->id== &image->id)
2293 NodeTagChanged(ntree, node);
2298 /* ********************** */
2300 void node_make_group(SpaceNode *snode)
2304 if(snode->edittree!=snode->nodetree) {
2305 // XXX error("Can not add a new Group in a Group");
2309 /* for time being... is too complex to handle */
2310 if(snode->treetype==NTREE_COMPOSIT) {
2311 for(gnode=snode->nodetree->nodes.first; gnode; gnode= gnode->next) {
2312 if(gnode->flag & SELECT)
2313 if(gnode->type==CMP_NODE_R_LAYERS)
2317 // XXX error("Can not add RenderLayer in a Group");
2322 gnode= nodeMakeGroupFromSelected(snode->nodetree);
2324 // XXX error("Can not make Group");
2327 nodeSetActive(snode->nodetree, gnode);
2328 ntreeSolveOrder(snode->nodetree);
2334 /* ******************** main event loop ****************** */
2336 void winqreadnodespace(ScrArea *sa, void *spacedata, BWinEvent *evt)
2338 SpaceNode *snode= spacedata;
2340 bNodeSocket *actsock;
2341 unsigned short event= evt->event;
2342 short val= evt->val, doredraw=0, fromlib= 0;
2344 if(sa->win==0) return;
2346 if(snode->nodetree==NULL) {
2347 /* no other events should be handled, but floating panels still should get handled */
2348 uiDoBlocks(&curarea->uiblocks, event, 1);
2353 if( node_uiDoBlocks(sa, event)!=UI_NOTHING ) event= 0;
2355 fromlib= (snode->id && snode->id->lib);
2359 if(gpencil_do_paint(sa, L_MOUSE)) {
2363 if(node_mouse_groupheader(snode)==0)
2364 //node_mouse_select(snode, event);
2368 if(G.qual & LR_CTRLKEY)
2372 // if(node_add_link(snode)==0)
2373 if(node_mouse_groupheader(snode)==0)
2374 // if(node_mouse_select(snode, event)==0)
2375 node_border_link_delete(snode);
2380 if(gpencil_do_paint(sa, R_MOUSE)) {
2383 else if(find_indicated_socket(snode, &actnode, &actsock, SOCK_IN)) {
2384 if(actsock->flag & SOCK_SEL) {
2385 snode->edittree->selin= NULL;
2386 actsock->flag&= ~SOCK_SEL;
2389 snode->edittree->selin= actsock;
2390 reset_sel_socket(snode, SOCK_IN);
2391 actsock->flag|= SOCK_SEL;
2394 else if(find_indicated_socket(snode, &actnode, &actsock, SOCK_OUT)) {
2395 if(actsock->flag & SOCK_SEL) {
2396 snode->edittree->selout= NULL;
2397 actsock->flag&= ~SOCK_SEL;
2400 snode->edittree->selout= actsock;
2401 reset_sel_socket(snode, SOCK_OUT);
2402 actsock->flag|= SOCK_SEL;
2405 // else if(!node_mouse_select(snode, event))
2410 if((snode->flag & SNODE_BACKDRAW) && (snode->treetype==NTREE_COMPOSIT)
2411 && (G.qual==LR_SHIFTKEY)) {
2412 snode_bg_viewmove(snode);
2417 case WHEELDOWNMOUSE:
2418 view2dmove(event); /* in drawipo.c */
2422 doredraw= node_socket_hilights(snode, SOCK_IN|SOCK_OUT);
2426 /* future: handlerize this! */
2427 if(snode->treetype==NTREE_SHADER)
2428 shader_node_event(snode, val);
2429 else if(snode->treetype==NTREE_COMPOSIT)
2430 composit_node_event(snode, val);
2431 else if(snode->treetype==NTREE_TEXTURE)
2432 texture_node_event(snode, val);
2444 snode_home(sa, snode);
2448 if(fromlib) fromlib= -1;
2449 else snode_make_group_editable(snode, NULL);
2453 if(G.qual==LR_SHIFTKEY) {
2454 if(fromlib) fromlib= -1;
2455 else toolbox_n_add();
2457 else if(G.qual==0) {
2458 node_deselectall(snode, 1);
2463 node_border_select(snode);
2465 case CKEY: /* sort again, showing cyclics */
2466 ntreeSolveOrder(snode->edittree);
2470 if(G.qual==LR_SHIFTKEY) {
2471 if(fromlib) fromlib= -1;
2472 else node_adduplicate(snode);
2476 // XXX snode_handle_recalc(snode);
2479 node_make_link(snode);
2482 if(fromlib) fromlib= -1;
2484 if(G.qual==LR_CTRLKEY) {
2485 if(okee("Make Group"))
2486 node_make_group(snode);
2488 else if(G.qual==LR_ALTKEY) {
2490 node_ungroup(snode);
2492 else if(G.qual==LR_SHIFTKEY) {
2493 node_addgroup(snode);
2496 // XXX transform_nodes(snode->edittree, 'g', "Move Node");
2503 node_insert_key(snode);
2506 node_select_linked(snode, G.qual==LR_SHIFTKEY);
2512 if(G.qual==LR_CTRLKEY) {
2515 else if(G.qual==LR_SHIFTKEY) {
2516 if(okee("Read saved Full Sample Layers"))
2517 node_read_fullsamplelayers(snode);
2520 if(okee("Read saved Render Layers"))
2521 node_read_renderlayers(snode);
2526 if(G.qual==LR_ALTKEY) {
2527 gpencil_delete_menu();
2530 if(fromlib) fromlib= -1;
2531 else node_delete(snode);
2540 scrarea_queue_winredraw(sa);
2542 scrarea_queue_headredraw(sa);
2546 static int node_delete_selection_exec(bContext *C, wmOperator *op)
2548 SpaceNode *snode= CTX_wm_space_node(C);
2549 ARegion *ar= CTX_wm_region(C);
2552 ED_region_tag_redraw(ar);
2553 WM_event_add_notifier(C, NC_SCENE|ND_NODES, NULL); /* Do we need to pass the scene? */
2555 return OPERATOR_FINISHED;
2560 void NODE_OT_delete(wmOperatorType *ot)
2565 ot->idname= "NODE_OT_delete";
2568 ot->exec= node_delete_selection_exec;
2569 ot->poll= ED_operator_node_active;
2572 ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;