Restored Compositor 're-render single layer' functionality (buttons on renderlayer...
[blender.git] / source / blender / editors / space_node / node_edit.c
1 /**
2  * $Id:
3  *
4  * ***** BEGIN GPL LICENSE BLOCK *****
5  *
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. 
10  *
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.
15  *
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.
19  *
20  * The Original Code is Copyright (C) 2005 Blender Foundation.
21  * All rights reserved.
22  *
23  * The Original Code is: all of this file.
24  *
25  * Contributor(s): David Millan Escriva, Juho Vepsäläinen, Nathan Letwory
26  *
27  * ***** END GPL LICENSE BLOCK *****
28  */
29
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <math.h>
33 #include <string.h>
34
35 #include "MEM_guardedalloc.h"
36
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"
50
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"
56 #include "BKE_main.h"
57 #include "BKE_node.h"
58 #include "BKE_material.h"
59 #include "BKE_paint.h"
60 #include "BKE_texture.h"
61 #include "BKE_report.h"
62 #include "BKE_scene.h"
63 #include "BKE_utildefines.h"
64
65 #include "BIF_gl.h"
66
67 #include "BLI_math.h"
68 #include "BLI_blenlib.h"
69 #include "BLI_storage_types.h"
70
71 #include "RE_pipeline.h"
72
73 #include "IMB_imbuf_types.h"
74
75 #include "ED_node.h"
76 #include "ED_render.h"
77 #include "ED_screen.h"
78 #include "ED_space_api.h"
79 #include "ED_transform.h"
80 #include "ED_types.h"
81
82 #include "RNA_access.h"
83 #include "RNA_define.h"
84
85 #include "WM_api.h"
86 #include "WM_types.h"
87
88 #include "UI_interface.h"
89 #include "UI_view2d.h"
90  
91 #include "node_intern.h"
92
93 #define SOCK_IN         1
94 #define SOCK_OUT        2
95
96 /* ***************** composite job manager ********************** */
97
98 typedef struct CompoJob {
99         Scene *scene;
100         bNodeTree *ntree;
101         bNodeTree *localtree;
102         short *stop;
103         short *do_update;
104 } CompoJob;
105
106 /* called by compo, only to check job 'stop' value */
107 static int compo_breakjob(void *cjv)
108 {
109         CompoJob *cj= cjv;
110         
111         return *(cj->stop);
112 }
113
114 /* called by compo, wmJob sends notifier */
115 static void compo_redrawjob(void *cjv, char *str)
116 {
117         CompoJob *cj= cjv;
118         
119         *(cj->do_update)= 1;
120 }
121
122 static void compo_freejob(void *cjv)
123 {
124         CompoJob *cj= cjv;
125
126         if(cj->localtree) {
127                 ntreeLocalMerge(cj->localtree, cj->ntree);
128         }
129         MEM_freeN(cj);
130 }
131
132 /* only now we copy the nodetree, so adding many jobs while
133    sliding buttons doesn't frustrate */
134 static void compo_initjob(void *cjv)
135 {
136         CompoJob *cj= cjv;
137
138         cj->localtree= ntreeLocalize(cj->ntree);
139 }
140
141 /* called before redraw notifiers, it moves finished previews over */
142 static void compo_updatejob(void *cjv)
143 {
144         CompoJob *cj= cjv;
145         
146         ntreeLocalSync(cj->localtree, cj->ntree);
147 }
148
149
150 /* only this runs inside thread */
151 static void compo_startjob(void *cjv, short *stop, short *do_update)
152 {
153         CompoJob *cj= cjv;
154         bNodeTree *ntree= cj->localtree;
155
156         if(cj->scene->use_nodes==0)
157                 return;
158         
159         cj->stop= stop;
160         cj->do_update= do_update;
161         
162         ntree->test_break= compo_breakjob;
163         ntree->tbh= cj;
164         ntree->stats_draw= compo_redrawjob;
165         ntree->sdh= cj;
166         
167         // XXX BIF_store_spare();
168         
169         ntreeCompositExecTree(ntree, &cj->scene->r, 1); /* 1 is do_previews */
170         
171         ntree->test_break= NULL;
172         ntree->stats_draw= NULL;
173
174 }
175
176 void snode_composite_job(const bContext *C, ScrArea *sa)
177 {
178         SpaceNode *snode= sa->spacedata.first;
179         wmJob *steve;
180         CompoJob *cj;
181
182         steve= WM_jobs_get(CTX_wm_manager(C), CTX_wm_window(C), sa, WM_JOB_EXCL_RENDER);
183         cj= MEM_callocN(sizeof(CompoJob), "compo job");
184         
185         /* customdata for preview thread */
186         cj->scene= CTX_data_scene(C);
187         cj->ntree= snode->nodetree;
188         
189         /* setup job */
190         WM_jobs_customdata(steve, cj, compo_freejob);
191         WM_jobs_timer(steve, 0.1, NC_SCENE, NC_SCENE|ND_COMPO_RESULT);
192         WM_jobs_callbacks(steve, compo_startjob, compo_initjob, compo_updatejob);
193         
194         WM_jobs_start(CTX_wm_manager(C), steve);
195         
196 }
197
198 /* ***************************************** */
199
200 /* also checks for edited groups */
201 bNode *editnode_get_active(bNodeTree *ntree)
202 {
203         bNode *node;
204         
205         /* check for edited group */
206         for(node= ntree->nodes.first; node; node= node->next)
207                 if(node->flag & NODE_GROUP_EDIT)
208                         break;
209         if(node)
210                 return nodeGetActive((bNodeTree *)node->id);
211         else
212                 return nodeGetActive(ntree);
213 }
214
215 void snode_handle_recalc(bContext *C, SpaceNode *snode)
216 {
217         if(snode->treetype==NTREE_SHADER)
218                 WM_event_add_notifier(C, NC_MATERIAL|ND_NODES, snode->id);
219         else if(snode->treetype==NTREE_COMPOSIT)
220                 WM_event_add_notifier(C, NC_SCENE|ND_NODES, snode->id);
221         else if(snode->treetype==NTREE_TEXTURE)
222                 WM_event_add_notifier(C, NC_TEXTURE|ND_NODES, snode->id);
223 }
224
225 #if 0
226 static int image_detect_file_sequence(int *start_p, int *frames_p, char *str)
227 {
228         SpaceFile *sfile;
229         char name[FILE_MAX], head[FILE_MAX], tail[FILE_MAX], filename[FILE_MAX];
230         int a, frame, totframe, found, minframe;
231         unsigned short numlen;
232
233         sfile= scrarea_find_space_of_type(curarea, SPACE_FILE);
234         if(sfile==NULL || sfile->filelist==NULL)
235                 return 0;
236
237         /* find first frame */
238         found= 0;
239         minframe= 0;
240
241         for(a=0; a<sfile->totfile; a++) {
242                 if(sfile->filelist[a].flags & ACTIVE) {
243                         BLI_strncpy(name, sfile->filelist[a].relname, sizeof(name));
244                         frame= BLI_stringdec(name, head, tail, &numlen);
245
246                         if(!found || frame < minframe) {
247                                 BLI_strncpy(filename, name, sizeof(name));
248                                 minframe= frame;
249                                 found= 1;
250                         }
251                 }
252         }
253
254         /* not one frame found */
255         if(!found)
256                 return 0;
257
258         /* counter number of following frames */
259         found= 1;
260         totframe= 0;
261
262         for(frame=minframe; found; frame++) {
263                 found= 0;
264                 BLI_strncpy(name, filename, sizeof(name));
265                 BLI_stringenc(name, head, tail, numlen, frame);
266
267                 for(a=0; a<sfile->totfile; a++) {
268                         if(sfile->filelist[a].flags & ACTIVE) {
269                                 if(strcmp(sfile->filelist[a].relname, name) == 0) {
270                                         found= 1;
271                                         totframe++;
272                                         break;
273                                 }
274                         }
275                 }
276         }
277
278         if(totframe > 1) {
279                 BLI_strncpy(str, sfile->dir, sizeof(name));
280                 strcat(str, filename);
281
282                 *start_p= minframe;
283                 *frames_p= totframe;
284                 return 1;
285         }
286
287         return 0;
288 }
289
290 static void load_node_image(char *str)  /* called from fileselect */
291 {
292         SpaceNode *snode= curarea->spacedata.first;
293         bNode *node= nodeGetActive(snode->edittree);
294         Image *ima= NULL;
295         ImageUser *iuser= node->storage;
296         char filename[FILE_MAX];
297         int start=0, frames=0, sequence;
298
299         sequence= image_detect_file_sequence(&start, &frames, filename);
300         if(sequence)
301                 str= filename;
302         
303         ima= BKE_add_image_file(str);
304         if(ima) {
305                 if(node->id)
306                         node->id->us--;
307                 
308                 node->id= &ima->id;
309                 id_us_plus(node->id);
310
311                 BLI_strncpy(node->name, node->id->name+2, 21);
312
313                 if(sequence) {
314                         ima->source= IMA_SRC_SEQUENCE;
315                         iuser->frames= frames;
316                         iuser->offset= start-1;
317                 }
318                                    
319                 BKE_image_signal(ima, node->storage, IMA_SIGNAL_RELOAD);
320                 
321                 NodeTagChanged(snode->edittree, node);
322                 // XXX snode_handle_recalc(C, snode);
323         }
324 }
325
326 static void set_node_imagepath(char *str)       /* called from fileselect */
327 {
328         SpaceNode *snode= curarea->spacedata.first;
329         bNode *node= nodeGetActive(snode->edittree);
330         BLI_strncpy(((NodeImageFile *)node->storage)->name, str, sizeof( ((NodeImageFile *)node->storage)->name ));
331 }
332
333 #endif /* 0 */
334
335 bNode *node_tree_get_editgroup(bNodeTree *nodetree)
336 {
337         bNode *gnode;
338         
339         /* get the groupnode */
340         for(gnode= nodetree->nodes.first; gnode; gnode= gnode->next)
341                 if(gnode->flag & NODE_GROUP_EDIT)
342                         break;
343         return gnode;
344 }
345
346 #if 0
347
348 static void composit_node_event(SpaceNode *snode, short event)
349 {
350         
351         switch(event) {
352                 case B_REDR:
353                         // allqueue(REDRAWNODE, 1);
354                         break;
355                 case B_NODE_SETIMAGE:
356                 {
357                         bNode *node= nodeGetActive(snode->edittree);
358                         char name[FILE_MAXDIR+FILE_MAXFILE];
359                         
360                         strcpy(name, ((NodeImageFile *)node->storage)->name);
361                         if (G.qual & LR_CTRLKEY) {
362                                 activate_imageselect(FILE_SPECIAL, "SELECT OUTPUT DIR", name, set_node_imagepath);
363                         } else {
364                                 activate_fileselect(FILE_SPECIAL, "SELECT OUTPUT DIR", name, set_node_imagepath);
365                         }
366                         break;
367                 }
368                 case B_NODE_TREE_EXEC:
369                         // XXX                  snode_handle_recalc(snode);
370                         break;          
371                 default:
372                         /* B_NODE_EXEC */
373                 {
374                         bNode *node= BLI_findlink(&snode->edittree->nodes, event-B_NODE_EXEC);
375                         if(node) {
376                                 NodeTagChanged(snode->edittree, node);
377                                 /* don't use NodeTagIDChanged, it gives far too many recomposites for image, scene layers, ... */
378                                 
379                                 /* not the best implementation of the world... but we need it to work now :) */
380                                 if(node->type==CMP_NODE_R_LAYERS && node->custom2) {
381                                         /* add event for this window (after render curarea can be changed) */
382                                         addqueue(curarea->win, UI_BUT_EVENT, B_NODE_TREE_EXEC);
383                                         
384                                         composite_node_render(snode, node);
385                                         // XXX                  snode_handle_recalc(snode);
386                                         
387                                         /* add another event, a render can go fullscreen and open new window */
388                                         addqueue(curarea->win, UI_BUT_EVENT, B_NODE_TREE_EXEC);
389                                 }
390                                 else {
391                                         node= node_tree_get_editgroup(snode->nodetree);
392                                         if(node)
393                                                 NodeTagIDChanged(snode->nodetree, node->id);
394                                         
395                                         // XXX                  snode_handle_recalc(snode);
396                                 }
397                         }
398                 }                       
399         }
400 }
401
402 static void texture_node_event(SpaceNode *snode, short event)
403 {
404         switch(event) {
405                 case B_REDR:
406                         // allqueue(REDRAWNODE, 1);
407                         break;
408                 case B_NODE_LOADIMAGE:
409                 {
410                         bNode *node= nodeGetActive(snode->edittree);
411                         char name[FILE_MAXDIR+FILE_MAXFILE];
412                         
413                         if(node->id)
414                                 strcpy(name, ((Image *)node->id)->name);
415                         else strcpy(name, U.textudir);
416                         if (G.qual & LR_CTRLKEY) {
417                                 activate_imageselect(FILE_SPECIAL, "SELECT IMAGE", name, load_node_image);
418                         } else {
419                                 activate_fileselect(FILE_SPECIAL, "SELECT IMAGE", name, load_node_image);
420                         }
421                         break;
422                 }
423                 default:
424                         /* B_NODE_EXEC */
425                         ntreeTexCheckCyclics( snode->nodetree );
426                         // XXX                  snode_handle_recalc(snode);
427                         // allqueue(REDRAWNODE, 1);
428                         break;
429         }
430 }
431
432 #endif /* 0  */
433 /* assumes nothing being done in ntree yet, sets the default in/out node */
434 /* called from shading buttons or header */
435 void ED_node_shader_default(Material *ma)
436 {
437         bNode *in, *out;
438         bNodeSocket *fromsock, *tosock;
439         
440         /* but lets check it anyway */
441         if(ma->nodetree) {
442                 printf("error in shader initialize\n");
443                 return;
444         }
445         
446         ma->nodetree= ntreeAddTree(NTREE_SHADER);
447         
448         out= nodeAddNodeType(ma->nodetree, SH_NODE_OUTPUT, NULL, NULL);
449         out->locx= 300.0f; out->locy= 300.0f;
450         
451         in= nodeAddNodeType(ma->nodetree, SH_NODE_MATERIAL, NULL, NULL);
452         in->locx= 10.0f; in->locy= 300.0f;
453         nodeSetActive(ma->nodetree, in);
454         
455         /* only a link from color to color */
456         fromsock= in->outputs.first;
457         tosock= out->inputs.first;
458         nodeAddLink(ma->nodetree, in, fromsock, out, tosock);
459         
460         ntreeSolveOrder(ma->nodetree);  /* needed for pointers */
461 }
462
463 /* assumes nothing being done in ntree yet, sets the default in/out node */
464 /* called from shading buttons or header */
465 void ED_node_composit_default(Scene *sce)
466 {
467         bNode *in, *out;
468         bNodeSocket *fromsock, *tosock;
469         
470         /* but lets check it anyway */
471         if(sce->nodetree) {
472                 printf("error in composit initialize\n");
473                 return;
474         }
475         
476         sce->nodetree= ntreeAddTree(NTREE_COMPOSIT);
477         
478         out= nodeAddNodeType(sce->nodetree, CMP_NODE_COMPOSITE, NULL, NULL);
479         out->locx= 300.0f; out->locy= 400.0f;
480         out->id= &sce->id;
481         
482         in= nodeAddNodeType(sce->nodetree, CMP_NODE_R_LAYERS, NULL, NULL);
483         in->locx= 10.0f; in->locy= 400.0f;
484         in->id= &sce->id;
485         nodeSetActive(sce->nodetree, in);
486         
487         /* links from color to color */
488         fromsock= in->outputs.first;
489         tosock= out->inputs.first;
490         nodeAddLink(sce->nodetree, in, fromsock, out, tosock);
491         
492         ntreeSolveOrder(sce->nodetree); /* needed for pointers */
493         
494         // XXX ntreeCompositForceHidden(sce->nodetree);
495 }
496
497 /* assumes nothing being done in ntree yet, sets the default in/out node */
498 /* called from shading buttons or header */
499 void ED_node_texture_default(Tex *tx)
500 {
501         bNode *in, *out;
502         bNodeSocket *fromsock, *tosock;
503         
504         /* but lets check it anyway */
505         if(tx->nodetree) {
506                 printf("error in texture initialize\n");
507                 return;
508         }
509         
510         tx->nodetree= ntreeAddTree(NTREE_TEXTURE);
511         
512         out= nodeAddNodeType(tx->nodetree, TEX_NODE_OUTPUT, NULL, NULL);
513         out->locx= 300.0f; out->locy= 300.0f;
514         
515         in= nodeAddNodeType(tx->nodetree, TEX_NODE_CHECKER, NULL, NULL);
516         in->locx= 10.0f; in->locy= 300.0f;
517         nodeSetActive(tx->nodetree, in);
518         
519         fromsock= in->outputs.first;
520         tosock= out->inputs.first;
521         nodeAddLink(tx->nodetree, in, fromsock, out, tosock);
522         
523         ntreeSolveOrder(tx->nodetree);  /* needed for pointers */
524 }
525
526 void node_tree_from_ID(ID *id, bNodeTree **ntree, bNodeTree **edittree, int *treetype)
527 {
528         bNode *node= NULL;
529         short idtype= GS(id->name);
530
531         if(idtype == ID_MA) {
532                 *ntree= ((Material*)id)->nodetree;
533                 if(treetype) *treetype= NTREE_SHADER;
534         }
535         else if(idtype == ID_SCE) {
536                 *ntree= ((Scene*)id)->nodetree;
537                 if(treetype) *treetype= NTREE_COMPOSIT;
538         }
539         else if(idtype == ID_TE) {
540                 *ntree= ((Tex*)id)->nodetree;
541                 if(treetype) *treetype= NTREE_TEXTURE;
542         }
543
544         /* find editable group */
545         if(edittree) {
546                 if(*ntree)
547                         for(node= (*ntree)->nodes.first; node; node= node->next)
548                                 if(node->flag & NODE_GROUP_EDIT)
549                                         break;
550                 
551                 if(node && node->id)
552                         *edittree= (bNodeTree *)node->id;
553                 else
554                         *edittree= *ntree;
555         }
556 }
557
558 /* Here we set the active tree(s), even called for each redraw now, so keep it fast :) */
559 void snode_set_context(SpaceNode *snode, Scene *scene)
560 {
561         Object *ob= OBACT;
562         
563         snode->nodetree= NULL;
564         snode->edittree= NULL;
565         snode->id= snode->from= NULL;
566         
567         if(snode->treetype==NTREE_SHADER) {
568                 /* need active object, or we allow pinning... */
569                 if(ob) {
570                         Material *ma= give_current_material(ob, ob->actcol);
571                         if(ma) {
572                                 snode->from= &ob->id;
573                                 snode->id= &ma->id;
574                         }
575                 }
576         }
577         else if(snode->treetype==NTREE_COMPOSIT) {
578                 snode->from= NULL;
579                 snode->id= &scene->id;
580                 
581                 /* bit clumsy but reliable way to see if we draw first time */
582                 if(snode->nodetree==NULL)
583                         ntreeCompositForceHidden(scene->nodetree, scene);
584         }
585         else if(snode->treetype==NTREE_TEXTURE) {
586                 Tex *tx= NULL;
587
588                 if(snode->texfrom==SNODE_TEX_OBJECT) {
589                         if(ob) {
590                                 tx= give_current_object_texture(ob);
591
592                                 if(ob->type == OB_LAMP)
593                                         snode->from= (ID*)ob->data;
594                                 else
595                                         snode->from= (ID*)give_current_material(ob, ob->actcol);
596
597                                 /* from is not set fully for material nodes, should be ID + Node then */
598                         }
599                 }
600                 else if(snode->texfrom==SNODE_TEX_WORLD) {
601                         tx= give_current_world_texture(scene->world);
602                         snode->from= (ID *)scene->world;
603                 }
604                 else {
605                         Brush *brush= NULL;
606                         
607                         if(ob && (ob->mode & OB_MODE_SCULPT))
608                                 brush= paint_brush(&scene->toolsettings->sculpt->paint);
609                         else
610                                 brush= paint_brush(&scene->toolsettings->imapaint.paint);
611
612                         snode->from= (ID *)brush;
613                         tx= give_current_brush_texture(brush);
614                 }
615                 
616                 snode->id= &tx->id;
617         }
618
619         if(snode->id)
620                 node_tree_from_ID(snode->id, &snode->nodetree, &snode->edittree, NULL);
621 }
622
623 #if 0
624 /* on activate image viewer, check if we show it */
625 static void node_active_image(Image *ima)
626 {
627         ScrArea *sa;
628         SpaceImage *sima= NULL;
629         
630         /* find an imagewindow showing render result */
631         for(sa=G.curscreen->areabase.first; sa; sa= sa->next) {
632                 if(sa->spacetype==SPACE_IMAGE) {
633                         sima= sa->spacedata.first;
634                         if(sima->image && sima->image->source!=IMA_SRC_VIEWER)
635                                 break;
636                 }
637         }
638         if(sa && sima) {
639                 sima->image= ima;
640                 scrarea_queue_winredraw(sa);
641                 scrarea_queue_headredraw(sa);
642         }
643 }
644 #endif /* 0 */
645
646 void node_set_active(SpaceNode *snode, bNode *node)
647 {
648         nodeSetActive(snode->edittree, node);
649         
650         if(node->type!=NODE_GROUP) {
651                 /* tree specific activate calls */
652                 if(snode->treetype==NTREE_SHADER) {
653                         // XXX
654 #if 0
655                         
656                         /* when we select a material, active texture is cleared, for buttons */
657                         if(node->id && GS(node->id->name)==ID_MA)
658                                 nodeClearActiveID(snode->edittree, ID_TE);
659                         if(node->id)
660                                 ; // XXX BIF_preview_changed(-1);       /* temp hack to force texture preview to update */
661                         
662                         // allqueue(REDRAWBUTSSHADING, 1);
663                         // allqueue(REDRAWIPO, 0);
664 #endif
665                 }
666                 else if(snode->treetype==NTREE_COMPOSIT) {
667                         Scene *scene= (Scene*)snode->id;
668
669                         /* make active viewer, currently only 1 supported... */
670                         if( ELEM(node->type, CMP_NODE_VIEWER, CMP_NODE_SPLITVIEWER)) {
671                                 bNode *tnode;
672                                 int was_output= (node->flag & NODE_DO_OUTPUT);
673
674                                 for(tnode= snode->edittree->nodes.first; tnode; tnode= tnode->next)
675                                         if( ELEM(tnode->type, CMP_NODE_VIEWER, CMP_NODE_SPLITVIEWER))
676                                                 tnode->flag &= ~NODE_DO_OUTPUT;
677                                 
678                                 node->flag |= NODE_DO_OUTPUT;
679                                 if(was_output==0) {
680                                         bNode *gnode;
681                                         
682                                         NodeTagChanged(snode->edittree, node);
683                                         
684                                         /* if inside group, tag entire group */
685                                         gnode= node_tree_get_editgroup(snode->nodetree);
686                                         if(gnode)
687                                                 NodeTagIDChanged(snode->nodetree, gnode->id);
688                                         
689                                         ED_node_changed_update(snode->id, node);
690                                 }
691                                 
692                                 /* addnode() doesnt link this yet... */
693                                 node->id= (ID *)BKE_image_verify_viewer(IMA_TYPE_COMPOSITE, "Viewer Node");
694                         }
695                         else if(node->type==CMP_NODE_IMAGE) {
696                                 // XXX
697 #if 0
698                                 if(node->id)
699                                         node_active_image((Image *)node->id);
700 #endif
701                         }
702                         else if(node->type==CMP_NODE_R_LAYERS) {
703                                 if(node->id==NULL || node->id==(ID *)scene) {
704                                         scene->r.actlay= node->custom1;
705                                         // XXX
706                                         // allqueue(REDRAWBUTSSCENE, 0);
707                                 }
708                         }
709                 }
710                 else if(snode->treetype==NTREE_TEXTURE) {
711                         // XXX
712 #if 0
713                         if(node->id)
714                                 ; // XXX BIF_preview_changed(-1);
715                         // allqueue(REDRAWBUTSSHADING, 1);
716                         // allqueue(REDRAWIPO, 0);
717 #endif
718                 }
719         }
720 }
721
722 /* when links in groups change, inputs/outputs change, nodes added/deleted... */
723 void node_tree_verify_groups(bNodeTree *nodetree)
724 {
725         bNode *gnode;
726         
727         gnode= node_tree_get_editgroup(nodetree);
728         
729         /* does all materials */
730         if(gnode)
731                 nodeVerifyGroup((bNodeTree *)gnode->id);
732         
733 }
734
735 /* ***************** Edit Group operator ************* */
736
737 void snode_make_group_editable(SpaceNode *snode, bNode *gnode)
738 {
739         bNode *node;
740         
741         /* make sure nothing has group editing on */
742         for(node= snode->nodetree->nodes.first; node; node= node->next)
743                 node->flag &= ~NODE_GROUP_EDIT;
744         
745         if(gnode==NULL) {
746                 /* with NULL argument we do a toggle */
747                 if(snode->edittree==snode->nodetree)
748                         gnode= nodeGetActive(snode->nodetree);
749         }
750         
751         if(gnode && gnode->type==NODE_GROUP && gnode->id) {
752                 if(gnode->id->lib)
753                         ntreeMakeLocal((bNodeTree *)gnode->id);
754
755                 gnode->flag |= NODE_GROUP_EDIT;
756                 snode->edittree= (bNodeTree *)gnode->id;
757                 
758                 /* deselect all other nodes, so we can also do grabbing of entire subtree */
759                 for(node= snode->nodetree->nodes.first; node; node= node->next)
760                         node->flag &= ~SELECT;
761                 gnode->flag |= SELECT;
762                 
763         }
764         else 
765                 snode->edittree= snode->nodetree;
766         
767         ntreeSolveOrder(snode->nodetree);
768         
769         /* finally send out events for new active node */
770         if(snode->treetype==NTREE_SHADER) {
771                 // allqueue(REDRAWBUTSSHADING, 0);
772                 
773                 // XXX BIF_preview_changed(-1); /* temp hack to force texture preview to update */
774         }
775 }
776
777 static int node_group_edit_exec(bContext *C, wmOperator *op)
778 {
779         SpaceNode *snode = CTX_wm_space_node(C);
780         bNode *gnode;
781
782         gnode= nodeGetActive(snode->edittree);
783         snode_make_group_editable(snode, gnode);
784
785         WM_event_add_notifier(C, NC_SCENE|ND_NODES, NULL);
786
787         return OPERATOR_FINISHED;
788 }
789
790 static int node_group_edit_invoke(bContext *C, wmOperator *op, wmEvent *event)
791 {
792         SpaceNode *snode = CTX_wm_space_node(C);
793         bNode *gnode;
794
795         gnode= nodeGetActive(snode->edittree);
796         if(gnode && gnode->type==NODE_GROUP && gnode->id && gnode->id->lib) {
797                 uiPupMenuOkee(C, op->type->idname, "Make group local?");
798                 return OPERATOR_CANCELLED;
799         }
800
801         return node_group_edit_exec(C, op);
802 }
803
804 void NODE_OT_group_edit(wmOperatorType *ot)
805 {
806         /* identifiers */
807         ot->name = "Edit Group";
808         ot->description = "Edit node group.";
809         ot->idname = "NODE_OT_group_edit";
810         
811         /* api callbacks */
812         ot->invoke = node_group_edit_invoke;
813         ot->exec = node_group_edit_exec;
814         ot->poll = ED_operator_node_active;
815         
816         /* flags */
817         ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
818 }
819
820 /* ******************** Ungroup operator ********************** */
821
822 static int node_group_ungroup_exec(bContext *C, wmOperator *op)
823 {
824         SpaceNode *snode = CTX_wm_space_node(C);
825         bNode *gnode;
826
827         /* are we inside of a group? */
828         gnode= node_tree_get_editgroup(snode->nodetree);
829         if(gnode)
830                 snode_make_group_editable(snode, NULL);
831         
832         gnode= nodeGetActive(snode->edittree);
833         if(gnode==NULL)
834                 return OPERATOR_CANCELLED;
835         
836         if(gnode->type!=NODE_GROUP) {
837                 BKE_report(op->reports, RPT_ERROR, "Not a group");
838                 return OPERATOR_CANCELLED;
839         }
840         else if(!nodeGroupUnGroup(snode->edittree, gnode)) {
841                 BKE_report(op->reports, RPT_ERROR, "Can't ungroup");
842                 return OPERATOR_CANCELLED;
843         }
844
845         WM_event_add_notifier(C, NC_SCENE|ND_NODES, NULL);
846
847         return OPERATOR_FINISHED;
848 }
849
850 void NODE_OT_group_ungroup(wmOperatorType *ot)
851 {
852         /* identifiers */
853         ot->name = "Ungroup";
854         ot->description = "Ungroup selected nodes.";
855         ot->idname = "NODE_OT_group_ungroup";
856         
857         /* api callbacks */
858         ot->exec = node_group_ungroup_exec;
859         ot->poll = ED_operator_node_active;
860         
861         /* flags */
862         ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
863 }
864
865 /* ************************** Node generic ************** */
866
867 /* allows to walk the list in order of visibility */
868 bNode *next_node(bNodeTree *ntree)
869 {
870         static bNode *current=NULL, *last= NULL;
871         
872         if(ntree) {
873                 /* set current to the first selected node */
874                 for(current= ntree->nodes.last; current; current= current->prev)
875                         if(current->flag & NODE_SELECT)
876                                 break;
877                 
878                 /* set last to the first unselected node */
879                 for(last= ntree->nodes.last; last; last= last->prev)
880                         if((last->flag & NODE_SELECT)==0)
881                                 break;
882                 
883                 if(current==NULL)
884                         current= last;
885                 
886                 return NULL;
887         }
888         /* no nodes, or we are ready */
889         if(current==NULL)
890                 return NULL;
891         
892         /* now we walk the list backwards, but we always return current */
893         if(current->flag & NODE_SELECT) {
894                 bNode *node= current;
895                 
896                 /* find previous selected */
897                 current= current->prev;
898                 while(current && (current->flag & NODE_SELECT)==0)
899                         current= current->prev;
900                 
901                 /* find first unselected */
902                 if(current==NULL)
903                         current= last;
904                 
905                 return node;
906         }
907         else {
908                 bNode *node= current;
909                 
910                 /* find previous unselected */
911                 current= current->prev;
912                 while(current && (current->flag & NODE_SELECT))
913                         current= current->prev;
914                 
915                 return node;
916         }
917         
918         return NULL;
919 }
920
921 /* is rct in visible part of node? */
922 static bNode *visible_node(SpaceNode *snode, rctf *rct)
923 {
924         bNode *tnode;
925         
926         for(next_node(snode->edittree); (tnode=next_node(NULL));) {
927                 if(BLI_isect_rctf(&tnode->totr, rct, NULL))
928                         break;
929         }
930         return tnode;
931 }
932
933 #if 0
934 static void snode_bg_viewmove(SpaceNode *snode)
935 {
936         ScrArea *sa;
937         Image *ima;
938         ImBuf *ibuf;
939         Window *win;
940         short mval[2], mvalo[2];
941         short rectx, recty, xmin, xmax, ymin, ymax, pad;
942         int oldcursor;
943         
944         ima= BKE_image_verify_viewer(IMA_TYPE_COMPOSITE, "Viewer Node");
945         ibuf= BKE_image_get_ibuf(ima, NULL);
946         
947         sa = snode->area;
948         
949         if(ibuf) {
950                 rectx = ibuf->x;
951                 recty = ibuf->y;
952         } else {
953                 rectx = recty = 1;
954         }
955         
956         pad = 10;
957         xmin = -(sa->winx/2) - rectx/2 + pad;
958         xmax = sa->winx/2 + rectx/2 - pad;
959         ymin = -(sa->winy/2) - recty/2 + pad;
960         ymax = sa->winy/2 + recty/2 - pad;
961         
962         getmouseco_sc(mvalo);
963         
964         /* store the old cursor to temporarily change it */
965         oldcursor=get_cursor();
966         win=winlay_get_active_window();
967         
968         SetBlenderCursor(BC_NSEW_SCROLLCURSOR);
969         
970         while(get_mbut()&(L_MOUSE|M_MOUSE)) {
971                 
972                 getmouseco_sc(mval);
973                 
974                 if(mvalo[0]!=mval[0] || mvalo[1]!=mval[1]) {
975                         
976                         snode->xof -= (mvalo[0]-mval[0]);
977                         snode->yof -= (mvalo[1]-mval[1]);
978                         
979                         /* prevent dragging image outside of the window and losing it! */
980                         CLAMP(snode->xof, xmin, xmax);
981                         CLAMP(snode->yof, ymin, ymax);
982                         
983                         mvalo[0]= mval[0];
984                         mvalo[1]= mval[1];
985                         
986                         scrarea_do_windraw(curarea);
987                         screen_swapbuffers();
988                 }
989                 else BIF_wait_for_statechange();
990         }
991         
992         window_set_cursor(win, oldcursor);
993 }
994 #endif
995
996 /* ********************** size widget operator ******************** */
997
998 typedef struct NodeSizeWidget {
999         float mxstart;
1000         float oldwidth;
1001 } NodeSizeWidget;
1002
1003 static int node_resize_modal(bContext *C, wmOperator *op, wmEvent *event)
1004 {
1005         SpaceNode *snode= CTX_wm_space_node(C);
1006         ARegion *ar= CTX_wm_region(C);
1007         bNode *node= editnode_get_active(snode->edittree);
1008         NodeSizeWidget *nsw= op->customdata;
1009         float mx, my;
1010         
1011         switch (event->type) {
1012                 case MOUSEMOVE:
1013                         
1014                         UI_view2d_region_to_view(&ar->v2d, event->x - ar->winrct.xmin, event->y - ar->winrct.ymin, 
1015                                                                          &mx, &my);
1016                         
1017                         if (node) {
1018                                 if(node->flag & NODE_HIDDEN) {
1019                                         node->miniwidth= nsw->oldwidth + mx - nsw->mxstart;
1020                                         CLAMP(node->miniwidth, 0.0f, 100.0f);
1021                                 }
1022                                 else {
1023                                         node->width= nsw->oldwidth + mx - nsw->mxstart;
1024                                         CLAMP(node->width, node->typeinfo->minwidth, node->typeinfo->maxwidth);
1025                                 }
1026                         }
1027                                 
1028                         ED_region_tag_redraw(ar);
1029
1030                         break;
1031                         
1032                 case LEFTMOUSE:
1033                 case MIDDLEMOUSE:
1034                 case RIGHTMOUSE:
1035                         
1036                         MEM_freeN(nsw);
1037                         op->customdata= NULL;
1038                         
1039                         return OPERATOR_FINISHED;
1040         }
1041         
1042         return OPERATOR_RUNNING_MODAL;
1043 }
1044
1045 static int node_resize_invoke(bContext *C, wmOperator *op, wmEvent *event)
1046 {
1047         SpaceNode *snode= CTX_wm_space_node(C);
1048         ARegion *ar= CTX_wm_region(C);
1049         bNode *node= editnode_get_active(snode->edittree);
1050         
1051         if(node) {
1052                 rctf totr;
1053                 
1054                 /* convert mouse coordinates to v2d space */
1055                 UI_view2d_region_to_view(&ar->v2d, event->x - ar->winrct.xmin, event->y - ar->winrct.ymin, 
1056                                                                  &snode->mx, &snode->my);
1057                 
1058                 /* rect we're interested in is just the bottom right corner */
1059                 totr= node->totr;
1060                 totr.xmin= totr.xmax-10.0f;
1061                 totr.ymax= totr.ymin+10.0f;
1062                 
1063                 if(BLI_in_rctf(&totr, snode->mx, snode->my)) {
1064                         NodeSizeWidget *nsw= MEM_callocN(sizeof(NodeSizeWidget), "size widget op data");
1065                         
1066                         op->customdata= nsw;
1067                         nsw->mxstart= snode->mx;
1068                         
1069                         /* store old */
1070                         if(node->flag & NODE_HIDDEN)
1071                                 nsw->oldwidth= node->miniwidth;
1072                         else
1073                                 nsw->oldwidth= node->width;
1074                         
1075                         /* add modal handler */
1076                         WM_event_add_modal_handler(C, op);
1077
1078                         return OPERATOR_RUNNING_MODAL;
1079                 }
1080         }
1081         return OPERATOR_CANCELLED|OPERATOR_PASS_THROUGH;
1082 }
1083
1084 void NODE_OT_resize(wmOperatorType *ot)
1085 {
1086         /* identifiers */
1087         ot->name= "Resize Node";
1088         ot->idname= "NODE_OT_resize";
1089         
1090         /* api callbacks */
1091         ot->invoke= node_resize_invoke;
1092         ot->modal= node_resize_modal;
1093         ot->poll= ED_operator_node_active;
1094         
1095         /* flags */
1096         ot->flag= OPTYPE_BLOCKING;
1097 }
1098
1099
1100 #if 0
1101
1102 /* ********************** select ******************** */
1103
1104 /* used in buttons to check context, also checks for edited groups */
1105 bNode *editnode_get_active_idnode(bNodeTree *ntree, short id_code)
1106 {
1107         return nodeGetActiveID(ntree, id_code);
1108 }
1109
1110 /* used in buttons to check context, also checks for edited groups */
1111 Material *editnode_get_active_material(Material *ma)
1112 {
1113         if(ma && ma->use_nodes && ma->nodetree) {
1114                 bNode *node= editnode_get_active_idnode(ma->nodetree, ID_MA);
1115                 if(node)
1116                         return (Material *)node->id;
1117                 else
1118                         return NULL;
1119         }
1120         return ma;
1121 }
1122 #endif /* 0 */
1123
1124
1125 /* no undo here! */
1126 void node_deselectall(SpaceNode *snode)
1127 {
1128         bNode *node;
1129         
1130         for(node= snode->edittree->nodes.first; node; node= node->next)
1131                 node->flag &= ~SELECT;
1132 }
1133
1134 int node_has_hidden_sockets(bNode *node)
1135 {
1136         bNodeSocket *sock;
1137         
1138         for(sock= node->inputs.first; sock; sock= sock->next)
1139                 if(sock->flag & SOCK_HIDDEN)
1140                         return 1;
1141         for(sock= node->outputs.first; sock; sock= sock->next)
1142                 if(sock->flag & SOCK_HIDDEN)
1143                         return 1;
1144         return 0;
1145 }
1146
1147 static void node_link_viewer(SpaceNode *snode, bNode *tonode)
1148 {
1149         bNode *node;
1150
1151         /* context check */
1152         if(tonode==NULL || tonode->outputs.first==NULL)
1153                 return;
1154         if( ELEM(tonode->type, CMP_NODE_VIEWER, CMP_NODE_SPLITVIEWER)) 
1155                 return;
1156         
1157         /* get viewer */
1158         for(node= snode->edittree->nodes.first; node; node= node->next)
1159                 if( ELEM(node->type, CMP_NODE_VIEWER, CMP_NODE_SPLITVIEWER)) 
1160                         if(node->flag & NODE_DO_OUTPUT)
1161                                 break;
1162                 
1163         if(node) {
1164                 bNodeLink *link;
1165                 
1166                 /* get link to viewer */
1167                 for(link= snode->edittree->links.first; link; link= link->next)
1168                         if(link->tonode==node)
1169                                 break;
1170
1171                 if(link) {
1172                         link->fromnode= tonode;
1173                         link->fromsock= tonode->outputs.first;
1174                         NodeTagChanged(snode->edittree, node);
1175                         
1176 // XXX                  snode_handle_recalc(snode);
1177                 }
1178         }
1179 }
1180
1181
1182 void node_active_link_viewer(SpaceNode *snode)
1183 {
1184         bNode *node= editnode_get_active(snode->edittree);
1185         if(node)
1186                 node_link_viewer(snode, node);
1187 }
1188
1189 /* return 0, nothing done */
1190 /*static*/ int node_mouse_groupheader(SpaceNode *snode)
1191 {
1192         bNode *gnode;
1193         float mx=0, my=0;
1194 // XXX  short mval[2];
1195         
1196         gnode= node_tree_get_editgroup(snode->nodetree);
1197         if(gnode==NULL) return 0;
1198         
1199 // XXX  getmouseco_areawin(mval);
1200 // XXX  areamouseco_to_ipoco(G.v2d, mval, &mx, &my);
1201         
1202         /* click in header or outside? */
1203         if(BLI_in_rctf(&gnode->totr, mx, my)==0) {
1204                 rctf rect= gnode->totr;
1205                 
1206                 rect.ymax += NODE_DY;
1207                 if(BLI_in_rctf(&rect, mx, my)==0)
1208                         snode_make_group_editable(snode, NULL); /* toggles, so exits editmode */
1209 //              else
1210 // XXX                  transform_nodes(snode->nodetree, 'g', "Move group");
1211                 
1212                 return 1;
1213         }
1214         return 0;
1215 }
1216
1217 /* checks snode->mouse position, and returns found node/socket */
1218 /* type is SOCK_IN and/or SOCK_OUT */
1219 static int find_indicated_socket(SpaceNode *snode, bNode **nodep, bNodeSocket **sockp, int in_out)
1220 {
1221         bNode *node;
1222         bNodeSocket *sock;
1223         rctf rect;
1224         
1225         /* check if we click in a socket */
1226         for(node= snode->edittree->nodes.first; node; node= node->next) {
1227                 
1228                 rect.xmin = snode->mx - NODE_SOCKSIZE+3;
1229                 rect.ymin = snode->my - NODE_SOCKSIZE+3;
1230                 rect.xmax = rect.xmin + 2*NODE_SOCKSIZE+6;
1231                 rect.ymax = rect.ymin + 2*NODE_SOCKSIZE+6;
1232                 
1233                 if (!(node->flag & NODE_HIDDEN)) {
1234                         /* extra padding inside and out - allow dragging on the text areas too */
1235                         if (in_out == SOCK_IN) {
1236                                 rect.xmax += NODE_SOCKSIZE;
1237                                 rect.xmin -= NODE_SOCKSIZE*4;
1238                         } else if (in_out == SOCK_OUT) {
1239                                 rect.xmax += NODE_SOCKSIZE*4;
1240                                 rect.xmin -= NODE_SOCKSIZE;
1241                         }
1242                 }
1243                 
1244                 if(in_out & SOCK_IN) {
1245                         for(sock= node->inputs.first; sock; sock= sock->next) {
1246                                 if(!(sock->flag & (SOCK_HIDDEN|SOCK_UNAVAIL))) {
1247                                         if(BLI_in_rctf(&rect, sock->locx, sock->locy)) {
1248                                                 if(node == visible_node(snode, &rect)) {
1249                                                         *nodep= node;
1250                                                         *sockp= sock;
1251                                                         return 1;
1252                                                 }
1253                                         }
1254                                 }
1255                         }
1256                 }
1257                 if(in_out & SOCK_OUT) {
1258                         for(sock= node->outputs.first; sock; sock= sock->next) {
1259                                 if(!(sock->flag & (SOCK_HIDDEN|SOCK_UNAVAIL))) {
1260                                         if(BLI_in_rctf(&rect, sock->locx, sock->locy)) {
1261                                                 if(node == visible_node(snode, &rect)) {
1262                                                         *nodep= node;
1263                                                         *sockp= sock;
1264                                                         return 1;
1265                                                 }
1266                                         }
1267                                 }
1268                         }
1269                 }
1270         }
1271         return 0;
1272 }
1273
1274 static int node_socket_hilights(SpaceNode *snode, int in_out)
1275 {
1276         bNode *node;
1277         bNodeSocket *sock, *tsock, *socksel= NULL;
1278         short redraw= 0;
1279         
1280         if(snode->edittree==NULL) return 0;
1281         
1282         /* deselect sockets */
1283         for(node= snode->edittree->nodes.first; node; node= node->next) {
1284                 for(sock= node->inputs.first; sock; sock= sock->next) {
1285                         if(sock->flag & SELECT) {
1286                                 sock->flag &= ~SELECT;
1287                                 redraw++;
1288                                 socksel= sock;
1289                         }
1290                 }
1291                 for(sock= node->outputs.first; sock; sock= sock->next) {
1292                         if(sock->flag & SELECT) {
1293                                 sock->flag &= ~SELECT;
1294                                 redraw++;
1295                                 socksel= sock;
1296                         }
1297                 }
1298         }
1299         
1300         // XXX mousepos should be set here!
1301         
1302         if(find_indicated_socket(snode, &node, &tsock, in_out)) {
1303                 tsock->flag |= SELECT;
1304                 if(redraw==1 && tsock==socksel) redraw= 0;
1305                 else redraw= 1;
1306         }
1307         
1308         return redraw;
1309 }
1310
1311 /* ****************** Add *********************** */
1312
1313
1314 typedef struct bNodeListItem {
1315         struct bNodeListItem *next, *prev;
1316         struct bNode *node;     
1317 } bNodeListItem;
1318
1319 int sort_nodes_locx(void *a, void *b)
1320 {
1321         bNodeListItem *nli1 = (bNodeListItem *)a;
1322         bNodeListItem *nli2 = (bNodeListItem *)b;
1323         bNode *node1 = nli1->node;
1324         bNode *node2 = nli2->node;
1325         
1326         if (node1->locx > node2->locx)
1327                 return 1;
1328         else 
1329                 return 0;
1330 }
1331
1332 static int socket_is_available(bNodeTree *ntree, bNodeSocket *sock, int allow_used)
1333 {
1334         if (sock->flag & (SOCK_HIDDEN|SOCK_UNAVAIL))
1335                 return 0;
1336         
1337         if (!allow_used) {
1338                 if (nodeCountSocketLinks(ntree, sock) > 0)
1339                         return 0;
1340         }
1341         return 1;
1342 }
1343
1344 static bNodeSocket *best_socket_output(bNodeTree *ntree, bNode *node, bNodeSocket *sock_target, int allow_multiple)
1345 {
1346         bNodeSocket *sock;
1347         
1348         /* first try to find a socket with a matching name */
1349         for (sock=node->outputs.first; sock; sock=sock->next) {
1350
1351                 if (!socket_is_available(ntree, sock, allow_multiple))
1352                         continue;
1353
1354                 /* check for same types */
1355                 if (sock->type == sock_target->type) {
1356                         if (strcmp(sock->name, sock_target->name)==0)
1357                                 return sock;
1358                 }
1359         }
1360         
1361         /* otherwise settle for the first available socket of the right type */
1362         for (sock=node->outputs.first; sock; sock=sock->next) {
1363
1364                 if (!socket_is_available(ntree, sock, allow_multiple))
1365                         continue;
1366                 
1367                 /* check for same types */
1368                 if (sock->type == sock_target->type) {
1369                         return sock;
1370                 }
1371         }
1372         
1373         return NULL;
1374 }
1375
1376 /* this is a bit complicated, but designed to prioritise finding 
1377  * sockets of higher types, such as image, first */
1378 static bNodeSocket *best_socket_input(bNodeTree *ntree, bNode *node, int num, int replace)
1379 {
1380         bNodeSocket *sock;
1381         int socktype, maxtype=0;
1382         int a;
1383         
1384         for (sock=node->inputs.first; sock; sock=sock->next) {
1385                 maxtype = MAX2(sock->type, maxtype);
1386         }
1387         
1388         /* find sockets of higher 'types' first (i.e. image) */
1389         for (socktype=maxtype; socktype >= 0; socktype--) {
1390                 for (sock=node->inputs.first; sock; sock=sock->next) {
1391                         
1392                         if (!socket_is_available(ntree, sock, replace)) {
1393                                 a++;
1394                                 continue;
1395                         }
1396                                 
1397                         if (sock->type == socktype) {
1398                                 /* increment to make sure we don't keep finding 
1399                                  * the same socket on every attempt running this function */
1400                                 a++;
1401                                 if (a > num)
1402                                         return sock;
1403                         }
1404                 }
1405         }
1406         
1407         return NULL;
1408 }
1409
1410 void snode_autoconnect(SpaceNode *snode, int allow_multiple, int replace)
1411 {
1412         ListBase *nodelist = MEM_callocN(sizeof(ListBase), "items_list");
1413         bNodeListItem *nli;
1414         bNode *node;
1415         int i;
1416         
1417         for(node= snode->edittree->nodes.first; node; node= node->next) {
1418                 if(node->flag & NODE_SELECT) {
1419                         nli = MEM_mallocN(sizeof(bNodeListItem), "temporary node list item");
1420                         nli->node = node;
1421                         BLI_addtail(nodelist, nli);
1422                 }
1423         }
1424         
1425         /* sort nodes left to right */
1426         BLI_sortlist(nodelist, sort_nodes_locx);
1427         
1428         for (nli=nodelist->first; nli; nli=nli->next) {
1429                 bNode *node_fr, *node_to;
1430                 bNodeSocket *sock_fr, *sock_to;
1431                 
1432                 if (nli->next == NULL) break;
1433                 
1434                 node_fr = nli->node;
1435                 node_to = nli->next->node;
1436                 
1437                 /* check over input sockets first */
1438                 for (i=0; i<BLI_countlist(&node_to->inputs); i++) {
1439                         
1440                         /* find the best guess input socket */
1441                         sock_to = best_socket_input(snode->edittree, node_to, i, replace);
1442                         if (!sock_to) continue;
1443                         
1444                         /* check for an appropriate output socket to connect from */
1445                         sock_fr = best_socket_output(snode->edittree, node_fr, sock_to, allow_multiple);
1446                         if (!sock_fr) continue;
1447                         
1448                         /* then we can connect */
1449                         if (replace)
1450                                 nodeRemSocketLinks(snode->edittree, sock_to);
1451                         nodeAddLink(snode->edittree, node_fr, sock_fr, node_to, sock_to);
1452                         break;
1453                 }
1454         }
1455         
1456         ntreeSolveOrder(snode->edittree);
1457         
1458         BLI_freelistN(nodelist);
1459         MEM_freeN(nodelist);
1460 }
1461
1462 /* can be called from menus too, but they should do own undopush and redraws */
1463 bNode *node_add_node(SpaceNode *snode, Scene *scene, int type, float locx, float locy)
1464 {
1465         bNode *node= NULL, *gnode;
1466         
1467         node_deselectall(snode);
1468         
1469         if(type>=NODE_DYNAMIC_MENU) {
1470                 node= nodeAddNodeType(snode->edittree, type, NULL, NULL);
1471         }
1472         else if(type>=NODE_GROUP_MENU) {
1473                 if(snode->edittree!=snode->nodetree) {
1474                         // XXX error("Can not add a Group in a Group");
1475                         return NULL;
1476                 }
1477                 else {
1478                         bNodeTree *ngroup= BLI_findlink(&G.main->nodetree, type-NODE_GROUP_MENU);
1479                         if(ngroup)
1480                                 node= nodeAddNodeType(snode->edittree, NODE_GROUP, ngroup, NULL);
1481                 }
1482         }
1483         else
1484                 node= nodeAddNodeType(snode->edittree, type, NULL, NULL);
1485         
1486         /* generics */
1487         if(node) {
1488                 node->locx= locx;
1489                 node->locy= locy + 60.0f;               // arbitrary.. so its visible
1490                 node->flag |= SELECT;
1491                 
1492                 gnode= node_tree_get_editgroup(snode->nodetree);
1493                 if(gnode) {
1494                         node->locx -= gnode->locx;
1495                         node->locy -= gnode->locy;
1496                 }
1497
1498                 node_tree_verify_groups(snode->nodetree);
1499                 node_set_active(snode, node);
1500                 
1501                 if(snode->nodetree->type==NTREE_COMPOSIT) {
1502                         if(ELEM3(node->type, CMP_NODE_R_LAYERS, CMP_NODE_COMPOSITE, CMP_NODE_DEFOCUS))
1503                                 node->id = &scene->id;
1504                         
1505                         ntreeCompositForceHidden(snode->edittree, scene);
1506                 }
1507                         
1508                 if(node->id)
1509                         id_us_plus(node->id);
1510                         
1511                 NodeTagChanged(snode->edittree, node);
1512         }
1513         
1514         if(snode->nodetree->type==NTREE_TEXTURE) {
1515                 ntreeTexCheckCyclics(snode->edittree);
1516         }
1517         
1518         return node;
1519 }
1520
1521 /* ****************** Duplicate *********************** */
1522
1523 static int node_duplicate_exec(bContext *C, wmOperator *op)
1524 {
1525         SpaceNode *snode= CTX_wm_space_node(C);
1526         
1527         ntreeCopyTree(snode->edittree, 1);      /* 1 == internally selected nodes */
1528         
1529         ntreeSolveOrder(snode->edittree);
1530         node_tree_verify_groups(snode->nodetree);
1531         snode_handle_recalc(C, snode);
1532
1533         return OPERATOR_FINISHED;
1534 }
1535
1536 void NODE_OT_duplicate(wmOperatorType *ot)
1537 {
1538         /* identifiers */
1539         ot->name= "Duplicate Nodes";
1540         ot->description = "Duplicate the nodes.";
1541         ot->idname= "NODE_OT_duplicate";
1542         
1543         /* api callbacks */
1544         ot->exec= node_duplicate_exec;
1545         ot->poll= ED_operator_node_active;
1546         
1547         /* flags */
1548         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
1549 }
1550
1551 /* *************************** add link op ******************** */
1552
1553 /* temp data to pass on to modal */
1554 typedef struct NodeLinkDrag
1555 {
1556         bNode *node;
1557         bNodeSocket *sock;
1558         bNodeLink *link;
1559         int in_out;
1560 } NodeLinkDrag;
1561
1562 /*static*/ void reset_sel_socket(SpaceNode *snode, int in_out)
1563 {
1564         bNode *node;
1565         bNodeSocket *sock;
1566         
1567         for(node= snode->edittree->nodes.first; node; node= node->next) {
1568                 if(in_out & SOCK_IN) {
1569                         for(sock= node->inputs.first; sock; sock= sock->next)
1570                                 if(sock->flag & SOCK_SEL) sock->flag&= ~SOCK_SEL;
1571                 }
1572                 if(in_out & SOCK_OUT) {
1573                         for(sock= node->outputs.first; sock; sock= sock->next)
1574                                 if(sock->flag & SOCK_SEL) sock->flag&= ~SOCK_SEL;
1575                 }
1576         }
1577 }
1578
1579
1580 static void node_remove_extra_links(SpaceNode *snode, bNodeSocket *tsock, bNodeLink *link)
1581 {
1582         bNodeLink *tlink;
1583         bNodeSocket *sock;
1584         
1585         if(tsock && nodeCountSocketLinks(snode->edittree, link->tosock) > tsock->limit) {
1586                 
1587                 for(tlink= snode->edittree->links.first; tlink; tlink= tlink->next) {
1588                         if(link!=tlink && tlink->tosock==link->tosock)
1589                                 break;
1590                 }
1591                 if(tlink) {
1592                         /* is there a free input socket with same type? */
1593                         for(sock= tlink->tonode->inputs.first; sock; sock= sock->next) {
1594                                 if(sock->type==tlink->fromsock->type)
1595                                         if(nodeCountSocketLinks(snode->edittree, sock) < sock->limit)
1596                                                 break;
1597                         }
1598                         if(sock) {
1599                                 tlink->tosock= sock;
1600                                 sock->flag &= ~SOCK_HIDDEN;
1601                         }
1602                         else {
1603                                 nodeRemLink(snode->edittree, tlink);
1604                         }
1605                 }
1606         }
1607 }
1608
1609 /* loop that adds a nodelink, called by function below  */
1610 /* in_out = starting socket */
1611 static int node_link_modal(bContext *C, wmOperator *op, wmEvent *event)
1612 {
1613         SpaceNode *snode= CTX_wm_space_node(C);
1614         ARegion *ar= CTX_wm_region(C);
1615         NodeLinkDrag *nldrag= op->customdata;
1616         bNode *tnode, *node;
1617         bNodeSocket *tsock= NULL, *sock;
1618         bNodeLink *link;
1619         int in_out;
1620
1621         in_out= nldrag->in_out;
1622         node= nldrag->node;
1623         sock= nldrag->sock;
1624         link= nldrag->link;
1625         
1626         UI_view2d_region_to_view(&ar->v2d, event->x - ar->winrct.xmin, event->y - ar->winrct.ymin, 
1627                                                          &snode->mx, &snode->my);
1628
1629         switch (event->type) {
1630                 case MOUSEMOVE:
1631                         
1632                         if(in_out==SOCK_OUT) {
1633                                 if(find_indicated_socket(snode, &tnode, &tsock, SOCK_IN)) {
1634                                         if(nodeFindLink(snode->edittree, sock, tsock)==NULL) {
1635                                                 if(tnode!=node  && link->tonode!=tnode && link->tosock!= tsock) {
1636                                                         link->tonode= tnode;
1637                                                         link->tosock= tsock;
1638                                                         ntreeSolveOrder(snode->edittree);       /* for interactive red line warning */
1639                                                 }
1640                                         }
1641                                 }
1642                                 else {
1643                                         link->tonode= NULL;
1644                                         link->tosock= NULL;
1645                                 }
1646                         }
1647                         else {
1648                                 if(find_indicated_socket(snode, &tnode, &tsock, SOCK_OUT)) {
1649                                         if(nodeFindLink(snode->edittree, sock, tsock)==NULL) {
1650                                                 if(nodeCountSocketLinks(snode->edittree, tsock) < tsock->limit) {
1651                                                         if(tnode!=node && link->fromnode!=tnode && link->fromsock!= tsock) {
1652                                                                 link->fromnode= tnode;
1653                                                                 link->fromsock= tsock;
1654                                                                 ntreeSolveOrder(snode->edittree);       /* for interactive red line warning */
1655                                                         }
1656                                                 }
1657                                         }
1658                                 }
1659                                 else {
1660                                         link->fromnode= NULL;
1661                                         link->fromsock= NULL;
1662                                 }
1663                         }
1664                         /* hilight target sockets only */
1665                         node_socket_hilights(snode, in_out==SOCK_OUT?SOCK_IN:SOCK_OUT);
1666                         ED_region_tag_redraw(ar);
1667                         break;
1668                         
1669                 case LEFTMOUSE:
1670                 case RIGHTMOUSE:
1671                 case MIDDLEMOUSE:
1672         
1673                         /* remove link? */
1674                         if(link->tonode==NULL || link->fromnode==NULL) {
1675                                 nodeRemLink(snode->edittree, link);
1676                         }
1677                         else {
1678                                 /* send changed events for original tonode and new */
1679                                 if(link->tonode) 
1680                                         NodeTagChanged(snode->edittree, link->tonode);
1681                                 
1682                                 /* we might need to remove a link */
1683                                 if(in_out==SOCK_OUT) node_remove_extra_links(snode, link->tosock, link);
1684                         }
1685                         
1686                         ntreeSolveOrder(snode->edittree);
1687                         node_tree_verify_groups(snode->nodetree);
1688                         snode_handle_recalc(C, snode);
1689                         
1690                         MEM_freeN(op->customdata);
1691                         op->customdata= NULL;
1692                         
1693                         return OPERATOR_FINISHED;
1694         }
1695         
1696         return OPERATOR_RUNNING_MODAL;
1697 }
1698
1699 /* return 1 when socket clicked */
1700 static int node_link_init(SpaceNode *snode, NodeLinkDrag *nldrag)
1701 {
1702         bNodeLink *link;
1703         
1704         /* output indicated? */
1705         if(find_indicated_socket(snode, &nldrag->node, &nldrag->sock, SOCK_OUT)) {
1706                 if(nodeCountSocketLinks(snode->edittree, nldrag->sock) < nldrag->sock->limit)
1707                         return SOCK_OUT;
1708                 else {
1709                         /* find if we break a link */
1710                         for(link= snode->edittree->links.first; link; link= link->next) {
1711                                 if(link->fromsock==nldrag->sock)
1712                                         break;
1713                         }
1714                         if(link) {
1715                                 nldrag->node= link->tonode;
1716                                 nldrag->sock= link->tosock;
1717                                 nodeRemLink(snode->edittree, link);
1718                                 return SOCK_IN;
1719                         }
1720                 }
1721         }
1722         /* or an input? */
1723         else if(find_indicated_socket(snode, &nldrag->node, &nldrag->sock, SOCK_IN)) {
1724                 if(nodeCountSocketLinks(snode->edittree, nldrag->sock) < nldrag->sock->limit)
1725                         return SOCK_IN;
1726                 else {
1727                         /* find if we break a link */
1728                         for(link= snode->edittree->links.first; link; link= link->next) {
1729                                 if(link->tosock==nldrag->sock)
1730                                         break;
1731                         }
1732                         if(link) {
1733                                 /* send changed event to original tonode */
1734                                 if(link->tonode) 
1735                                         NodeTagChanged(snode->edittree, link->tonode);
1736                                 
1737                                 nldrag->node= link->fromnode;
1738                                 nldrag->sock= link->fromsock;
1739                                 nodeRemLink(snode->edittree, link);
1740                                 return SOCK_OUT;
1741                         }
1742                 }
1743         }
1744         
1745         return 0;
1746 }
1747
1748 static int node_link_invoke(bContext *C, wmOperator *op, wmEvent *event)
1749 {
1750         SpaceNode *snode= CTX_wm_space_node(C);
1751         ARegion *ar= CTX_wm_region(C);
1752         NodeLinkDrag *nldrag= MEM_callocN(sizeof(NodeLinkDrag), "drag link op customdata");
1753         
1754         UI_view2d_region_to_view(&ar->v2d, event->x - ar->winrct.xmin, event->y - ar->winrct.ymin, 
1755                                                          &snode->mx, &snode->my);
1756
1757         nldrag->in_out= node_link_init(snode, nldrag);
1758                 
1759         if(nldrag->in_out) {
1760                 op->customdata= nldrag;
1761                 
1762                 /* we make a temporal link */
1763                 if(nldrag->in_out==SOCK_OUT)
1764                         nldrag->link= nodeAddLink(snode->edittree, nldrag->node, nldrag->sock, NULL, NULL);
1765                 else
1766                         nldrag->link= nodeAddLink(snode->edittree, NULL, NULL, nldrag->node, nldrag->sock);
1767                 
1768                 /* add modal handler */
1769                 WM_event_add_modal_handler(C, op);
1770                 
1771                 return OPERATOR_RUNNING_MODAL;
1772         }
1773         else {
1774                 MEM_freeN(nldrag);
1775                 return OPERATOR_CANCELLED|OPERATOR_PASS_THROUGH;
1776         }
1777 }
1778
1779 void NODE_OT_link(wmOperatorType *ot)
1780 {
1781         /* identifiers */
1782         ot->name= "Link Nodes";
1783         ot->idname= "NODE_OT_link";
1784         
1785         /* api callbacks */
1786         ot->invoke= node_link_invoke;
1787         ot->modal= node_link_modal;
1788 //      ot->exec= node_link_exec;
1789         ot->poll= ED_operator_node_active;
1790         
1791         /* flags */
1792         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO|OPTYPE_BLOCKING;
1793 }
1794
1795 /* ********************** Make Link operator ***************** */
1796
1797 /* makes a link between selected output and input sockets */
1798 static int node_make_link_exec(bContext *C, wmOperator *op)
1799 {
1800         SpaceNode *snode= CTX_wm_space_node(C);
1801         int replace = RNA_boolean_get(op->ptr, "replace");
1802
1803         snode_autoconnect(snode, 0, replace);
1804
1805         node_tree_verify_groups(snode->nodetree);
1806         snode_handle_recalc(C, snode);
1807         
1808         return OPERATOR_FINISHED;
1809 }
1810
1811 void NODE_OT_link_make(wmOperatorType *ot)
1812 {
1813         /* identifiers */
1814         ot->name= "Make Links";
1815         ot->description= "Makes a link between selected output in input sockets.";
1816         ot->idname= "NODE_OT_link_make";
1817         
1818         /* callbacks */
1819         ot->exec= node_make_link_exec;
1820         ot->poll= ED_operator_node_active; // XXX we need a special poll which checks that there are selected input/output sockets
1821         
1822         /* flags */
1823         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
1824         
1825         RNA_def_boolean(ot->srna, "replace", 0, "Replace", "Replace socket connections with the new links");
1826 }
1827
1828 /* ********************** Cut Link operator ***************** */
1829
1830 #define LINK_RESOL 12
1831 static int cut_links_intersect(bNodeLink *link, float mcoords[][2], int tot)
1832 {
1833         float coord_array[LINK_RESOL+1][2];
1834         int i, b;
1835         
1836         if(node_link_bezier_points(NULL, NULL, link, coord_array, LINK_RESOL)) {
1837
1838                 for(i=0; i<tot-1; i++)
1839                         for(b=0; b<LINK_RESOL-1; b++)
1840                                 if(isect_line_line_v2(mcoords[i], mcoords[i+1], coord_array[b], coord_array[b+1]) > 0)
1841                                         return 1;
1842         }
1843         return 0;
1844 }
1845
1846 static int cut_links_exec(bContext *C, wmOperator *op)
1847 {
1848         SpaceNode *snode= CTX_wm_space_node(C);
1849         ARegion *ar= CTX_wm_region(C);
1850         float mcoords[256][2];
1851         int i= 0;
1852         
1853         RNA_BEGIN(op->ptr, itemptr, "path") {
1854                 float loc[2];
1855                 
1856                 RNA_float_get_array(&itemptr, "loc", loc);
1857                 UI_view2d_region_to_view(&ar->v2d, (short)loc[0], (short)loc[1], 
1858                                                                  &mcoords[i][0], &mcoords[i][1]);
1859                 i++;
1860                 if(i>= 256) break;
1861         }
1862         RNA_END;
1863         
1864         if(i>1) {
1865                 bNodeLink *link, *next;
1866                 
1867                 for(link= snode->edittree->links.first; link; link= next) {
1868                         next= link->next;
1869                         
1870                         if(cut_links_intersect(link, mcoords, i)) {
1871                                 NodeTagChanged(snode->edittree, link->tonode);
1872                                 nodeRemLink(snode->edittree, link);
1873                         }
1874                 }
1875                 
1876                 ntreeSolveOrder(snode->edittree);
1877                 node_tree_verify_groups(snode->nodetree);
1878                 snode_handle_recalc(C, snode);
1879                 
1880                 return OPERATOR_FINISHED;
1881         }
1882         
1883         return OPERATOR_CANCELLED|OPERATOR_PASS_THROUGH;
1884 }
1885
1886 void NODE_OT_links_cut(wmOperatorType *ot)
1887 {
1888         PropertyRNA *prop;
1889         
1890         ot->name= "Cut links";
1891         ot->idname= "NODE_OT_links_cut";
1892         
1893         ot->invoke= WM_gesture_lines_invoke;
1894         ot->modal= WM_gesture_lines_modal;
1895         ot->exec= cut_links_exec;
1896         
1897         ot->poll= ED_operator_node_active;
1898         
1899         /* flags */
1900         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
1901         
1902         prop= RNA_def_property(ot->srna, "path", PROP_COLLECTION, PROP_NONE);
1903         RNA_def_property_struct_runtime(prop, &RNA_OperatorMousePath);
1904         /* internal */
1905         RNA_def_int(ot->srna, "cursor", BC_KNIFECURSOR, 0, INT_MAX, "Cursor", "", 0, INT_MAX);
1906 }
1907
1908 /* ******************************** */
1909 // XXX some code needing updating to operators...
1910
1911 /* goes over all scenes, reads render layerss */
1912 void node_read_renderlayers(SpaceNode *snode)
1913 {
1914         Scene *curscene= NULL; // XXX
1915         Scene *scene;
1916         bNode *node;
1917
1918         /* first tag scenes unread */
1919         for(scene= G.main->scene.first; scene; scene= scene->id.next) 
1920                 scene->id.flag |= LIB_DOIT;
1921
1922         for(node= snode->edittree->nodes.first; node; node= node->next) {
1923                 if(node->type==CMP_NODE_R_LAYERS) {
1924                         ID *id= node->id;
1925                         if(id->flag & LIB_DOIT) {
1926                                 RE_ReadRenderResult(curscene, (Scene *)id);
1927                                 ntreeCompositTagRender((Scene *)id);
1928                                 id->flag &= ~LIB_DOIT;
1929                         }
1930                 }
1931         }
1932         
1933         // XXX                  snode_handle_recalc(snode);
1934 }
1935
1936 void node_read_fullsamplelayers(SpaceNode *snode)
1937 {
1938         Scene *curscene= NULL; // XXX
1939         Render *re= RE_NewRender(curscene->id.name);
1940
1941         WM_cursor_wait(1);
1942
1943         //BIF_init_render_callbacks(re, 1);
1944         RE_MergeFullSample(re, curscene, snode->nodetree);
1945         //BIF_end_render_callbacks();
1946         
1947         // allqueue(REDRAWNODE, 1);
1948         // allqueue(REDRAWIMAGE, 1);
1949         
1950         WM_cursor_wait(0);
1951 }
1952
1953 void imagepaint_composite_tags(bNodeTree *ntree, Image *image, ImageUser *iuser)
1954 {
1955         bNode *node;
1956         
1957         if(ntree==NULL)
1958                 return;
1959         
1960         /* search for renderresults */
1961         if(image->type==IMA_TYPE_R_RESULT) {
1962                 for(node= ntree->nodes.first; node; node= node->next) {
1963                         if(node->type==CMP_NODE_R_LAYERS && node->id==NULL) {
1964                                 /* imageuser comes from ImageWin, so indexes are offset 1 */
1965                                 if(node->custom1==iuser->layer-1)
1966                                         NodeTagChanged(ntree, node);
1967                         }
1968                 }
1969         }
1970         else {
1971                 for(node= ntree->nodes.first; node; node= node->next) {
1972                         if(node->id== &image->id)
1973                                 NodeTagChanged(ntree, node);
1974                 }
1975         }
1976 }
1977
1978 /* ****************** Make Group operator ******************* */
1979
1980 static int node_group_make_exec(bContext *C, wmOperator *op)
1981 {
1982         SpaceNode *snode = CTX_wm_space_node(C);
1983         bNode *gnode;
1984         
1985         if(snode->edittree!=snode->nodetree) {
1986                 BKE_report(op->reports, RPT_ERROR, "Can not add a new Group in a Group");
1987                 return OPERATOR_CANCELLED;
1988         }
1989         
1990         /* for time being... is too complex to handle */
1991         if(snode->treetype==NTREE_COMPOSIT) {
1992                 for(gnode=snode->nodetree->nodes.first; gnode; gnode= gnode->next) {
1993                         if(gnode->flag & SELECT)
1994                                 if(gnode->type==CMP_NODE_R_LAYERS)
1995                                         break;
1996                 }
1997                 
1998                 if(gnode) {
1999                         BKE_report(op->reports, RPT_ERROR, "Can not add RenderLayer in a Group");
2000                         return OPERATOR_CANCELLED;
2001                 }
2002         }
2003         
2004         gnode= nodeMakeGroupFromSelected(snode->nodetree);
2005         if(gnode==NULL) {
2006                 BKE_report(op->reports, RPT_ERROR, "Can not make Group");
2007                 return OPERATOR_CANCELLED;
2008         }
2009         else {
2010                 nodeSetActive(snode->nodetree, gnode);
2011                 ntreeSolveOrder(snode->nodetree);
2012         }
2013         
2014         snode_handle_recalc(C, snode);
2015         
2016         return OPERATOR_FINISHED;
2017 }
2018
2019 void NODE_OT_group_make(wmOperatorType *ot)
2020 {
2021         /* identifiers */
2022         ot->name = "Group";
2023         ot->description = "Make group from selected nodes.";
2024         ot->idname = "NODE_OT_group_make";
2025         
2026         /* api callbacks */
2027         ot->exec = node_group_make_exec;
2028         ot->poll = ED_operator_node_active;
2029         
2030         /* flags */
2031         ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
2032 }
2033
2034 /* ****************** Hide operator *********************** */
2035
2036 static int node_hide_exec(bContext *C, wmOperator *op)
2037 {
2038         SpaceNode *snode= CTX_wm_space_node(C);
2039         bNode *node;
2040         int nothidden=0, ishidden=0;
2041         
2042         /* sanity checking (poll callback checks this already) */
2043         if((snode == NULL) || (snode->edittree == NULL))
2044                 return OPERATOR_CANCELLED;
2045         
2046         for(node= snode->edittree->nodes.first; node; node= node->next) {
2047                 if(node->flag & SELECT) {
2048                         if(node->flag & NODE_HIDDEN)
2049                                 ishidden++;
2050                         else
2051                                 nothidden++;
2052                 }
2053         }
2054         for(node= snode->edittree->nodes.first; node; node= node->next) {
2055                 if(node->flag & SELECT) {
2056                         if( (ishidden && nothidden) || ishidden==0)
2057                                 node->flag |= NODE_HIDDEN;
2058                         else 
2059                                 node->flag &= ~NODE_HIDDEN;
2060                 }
2061         }
2062         
2063         snode_handle_recalc(C, snode);
2064         
2065         return OPERATOR_FINISHED;
2066 }
2067
2068 void NODE_OT_hide(wmOperatorType *ot)
2069 {
2070         /* identifiers */
2071         ot->name= "Hide";
2072         ot->description= "Toggle hiding of the nodes.";
2073         ot->idname= "NODE_OT_hide";
2074         
2075         /* callbacks */
2076         ot->exec= node_hide_exec;
2077         ot->poll= ED_operator_node_active;
2078         
2079         /* flags */
2080         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
2081 }
2082
2083 /* ****************** Mute operator *********************** */
2084
2085 static int node_mute_exec(bContext *C, wmOperator *op)
2086 {
2087         SpaceNode *snode= CTX_wm_space_node(C);
2088         bNode *node;
2089
2090         /* no disabling inside of groups */
2091         if(node_tree_get_editgroup(snode->nodetree))
2092                 return OPERATOR_CANCELLED;
2093         
2094         for(node= snode->edittree->nodes.first; node; node= node->next) {
2095                 if(node->flag & SELECT) {
2096                         if(node->inputs.first && node->outputs.first) {
2097                                 node->flag ^= NODE_MUTED;
2098                         }
2099                 }
2100         }
2101         
2102         snode_handle_recalc(C, snode);
2103         
2104         return OPERATOR_FINISHED;
2105 }
2106
2107 void NODE_OT_mute(wmOperatorType *ot)
2108 {
2109         /* identifiers */
2110         ot->name= "Mute";
2111         ot->description= "Toggle muting of the nodes.";
2112         ot->idname= "NODE_OT_mute";
2113         
2114         /* callbacks */
2115         ot->exec= node_mute_exec;
2116         ot->poll= ED_operator_node_active;
2117         
2118         /* flags */
2119         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
2120 }
2121
2122 /* ****************** Delete operator ******************* */
2123
2124 static int node_delete_exec(bContext *C, wmOperator *op)
2125 {
2126         SpaceNode *snode= CTX_wm_space_node(C);
2127         bNode *node, *next;
2128         bNodeSocket *sock;
2129         
2130         for(node= snode->edittree->nodes.first; node; node= next) {
2131                 next= node->next;
2132                 if(node->flag & SELECT) {
2133                         /* set selin and selout NULL if the sockets belong to a node to be deleted */
2134                         for(sock= node->inputs.first; sock; sock= sock->next)
2135                                 if(snode->edittree->selin == sock) snode->edittree->selin= NULL;
2136                         
2137                         for(sock= node->outputs.first; sock; sock= sock->next)
2138                                 if(snode->edittree->selout == sock) snode->edittree->selout= NULL;
2139                                 
2140                         /* check id user here, nodeFreeNode is called for free dbase too */
2141                         if(node->id)
2142                                 node->id->us--;
2143                         nodeFreeNode(snode->edittree, node);
2144                 }
2145         }
2146         
2147         node_tree_verify_groups(snode->nodetree);
2148
2149         snode_handle_recalc(C, snode);
2150         
2151         return OPERATOR_FINISHED;
2152 }
2153
2154 void NODE_OT_delete(wmOperatorType *ot)
2155 {
2156         /* identifiers */
2157         ot->name= "Delete";
2158         ot->description = "Delete selected nodes.";
2159         ot->idname= "NODE_OT_delete";
2160         
2161         /* api callbacks */
2162         ot->exec= node_delete_exec;
2163         ot->poll= ED_operator_node_active;
2164         
2165         /* flags */
2166         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
2167 }
2168
2169 /* ****************** Show Cyclic Dependencies Operator  ******************* */
2170
2171 static int node_show_cycles_exec(bContext *C, wmOperator *op)
2172 {
2173         SpaceNode *snode= CTX_wm_space_node(C);
2174         
2175         /* this is just a wrapper around this call... */
2176         ntreeSolveOrder(snode->edittree);
2177         snode_handle_recalc(C, snode);
2178         
2179         return OPERATOR_FINISHED;
2180 }
2181
2182 void NODE_OT_show_cyclic_dependencies(wmOperatorType *ot)
2183 {
2184         /* identifiers */
2185         ot->name= "Show Cyclic Dependencies";
2186         ot->description= "Sort the nodes and show the cyclic dependencies between the nodes.";
2187         ot->idname= "NODE_OT_show_cyclic_dependencies";
2188         
2189         /* callbacks */
2190         ot->exec= node_show_cycles_exec;
2191         ot->poll= ED_operator_node_active;
2192         
2193         /* flags */
2194         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
2195 }
2196
2197