2.5: X11
[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_texture.h"
60 #include "BKE_scene.h"
61 #include "BKE_utildefines.h"
62
63 #include "ED_previewrender.h"
64
65 #include "BIF_gl.h"
66
67 #include "BLI_arithb.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_space_api.h"
76 #include "ED_screen.h"
77 #include "ED_transform.h"
78 #include "ED_types.h"
79
80 #include "RNA_access.h"
81 #include "RNA_define.h"
82
83 #include "WM_api.h"
84 #include "WM_types.h"
85
86 #include "UI_view2d.h"
87  
88 #include "node_intern.h"
89
90 #define SOCK_IN         1
91 #define SOCK_OUT        2
92
93 /* ***************** composite job manager ********************** */
94
95 typedef struct CompoJob {
96         Scene *scene;
97         bNodeTree *ntree;
98         bNodeTree *localtree;
99         short *stop;
100         short *do_update;
101 } CompoJob;
102
103 /* called by compo, only to check job 'stop' value */
104 static int compo_breakjob(void *cjv)
105 {
106         CompoJob *cj= cjv;
107         
108         return *(cj->stop);
109 }
110
111 /* called by compo, wmJob sends notifier */
112 static void compo_redrawjob(void *cjv, char *str)
113 {
114         CompoJob *cj= cjv;
115         
116         *(cj->do_update)= 1;
117 }
118
119 static void compo_freejob(void *cjv)
120 {
121         CompoJob *cj= cjv;
122
123         if(cj->localtree) {
124                 ntreeLocalMerge(cj->localtree, cj->ntree);
125         }
126         MEM_freeN(cj);
127 }
128
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)
132 {
133         CompoJob *cj= cjv;
134
135         cj->localtree= ntreeLocalize(cj->ntree);
136 }
137
138 /* called before redraw notifiers, it moves finished previews over */
139 static void compo_updatejob(void *cjv)
140 {
141         CompoJob *cj= cjv;
142         
143         ntreeLocalSync(cj->localtree, cj->ntree);
144 }
145
146
147 /* only this runs inside thread */
148 static void compo_startjob(void *cjv, short *stop, short *do_update)
149 {
150         CompoJob *cj= cjv;
151         bNodeTree *ntree= cj->localtree;
152         
153         if(cj->scene->use_nodes==0)
154                 return;
155         
156         cj->stop= stop;
157         cj->do_update= do_update;
158         
159         ntree->test_break= compo_breakjob;
160         ntree->tbh= cj;
161         ntree->stats_draw= compo_redrawjob;
162         ntree->sdh= cj;
163         
164         // XXX BIF_store_spare();
165         
166         ntreeCompositExecTree(ntree, &cj->scene->r, 1); /* 1 is do_previews */
167         
168         ntree->test_break= NULL;
169         ntree->stats_draw= NULL;
170
171 }
172
173 void snode_composite_job(const bContext *C, ScrArea *sa)
174 {
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");
178         
179         /* customdata for preview thread */
180         cj->scene= CTX_data_scene(C);
181         cj->ntree= snode->nodetree;
182         
183         /* setup job */
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);
187         
188         WM_jobs_start(CTX_wm_manager(C), steve);
189         
190 }
191
192 /* ***************************************** */
193
194 /* also checks for edited groups */
195 bNode *editnode_get_active(bNodeTree *ntree)
196 {
197         bNode *node;
198         
199         /* check for edited group */
200         for(node= ntree->nodes.first; node; node= node->next)
201                 if(node->flag & NODE_GROUP_EDIT)
202                         break;
203         if(node)
204                 return nodeGetActive((bNodeTree *)node->id);
205         else
206                 return nodeGetActive(ntree);
207 }
208
209 void snode_handle_recalc(bContext *C, SpaceNode *snode)
210 {
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);
218         }
219 }
220
221 #if 0
222 static int image_detect_file_sequence(int *start_p, int *frames_p, char *str)
223 {
224         SpaceFile *sfile;
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;
228
229         sfile= scrarea_find_space_of_type(curarea, SPACE_FILE);
230         if(sfile==NULL || sfile->filelist==NULL)
231                 return 0;
232
233         /* find first frame */
234         found= 0;
235         minframe= 0;
236
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);
241
242                         if(!found || frame < minframe) {
243                                 BLI_strncpy(filename, name, sizeof(name));
244                                 minframe= frame;
245                                 found= 1;
246                         }
247                 }
248         }
249
250         /* not one frame found */
251         if(!found)
252                 return 0;
253
254         /* counter number of following frames */
255         found= 1;
256         totframe= 0;
257
258         for(frame=minframe; found; frame++) {
259                 found= 0;
260                 BLI_strncpy(name, filename, sizeof(name));
261                 BLI_stringenc(name, head, tail, numlen, frame);
262
263                 for(a=0; a<sfile->totfile; a++) {
264                         if(sfile->filelist[a].flags & ACTIVE) {
265                                 if(strcmp(sfile->filelist[a].relname, name) == 0) {
266                                         found= 1;
267                                         totframe++;
268                                         break;
269                                 }
270                         }
271                 }
272         }
273
274         if(totframe > 1) {
275                 BLI_strncpy(str, sfile->dir, sizeof(name));
276                 strcat(str, filename);
277
278                 *start_p= minframe;
279                 *frames_p= totframe;
280                 return 1;
281         }
282
283         return 0;
284 }
285
286 static void load_node_image(char *str)  /* called from fileselect */
287 {
288         SpaceNode *snode= curarea->spacedata.first;
289         bNode *node= nodeGetActive(snode->edittree);
290         Image *ima= NULL;
291         ImageUser *iuser= node->storage;
292         char filename[FILE_MAX];
293         int start=0, frames=0, sequence;
294
295         sequence= image_detect_file_sequence(&start, &frames, filename);
296         if(sequence)
297                 str= filename;
298         
299         ima= BKE_add_image_file(str);
300         if(ima) {
301                 if(node->id)
302                         node->id->us--;
303                 
304                 node->id= &ima->id;
305                 id_us_plus(node->id);
306
307                 BLI_strncpy(node->name, node->id->name+2, 21);
308
309                 if(sequence) {
310                         ima->source= IMA_SRC_SEQUENCE;
311                         iuser->frames= frames;
312                         iuser->offset= start-1;
313                 }
314                                    
315                 BKE_image_signal(ima, node->storage, IMA_SIGNAL_RELOAD);
316                 
317                 NodeTagChanged(snode->edittree, node);
318                 // XXX snode_handle_recalc(C, snode);
319         }
320 }
321
322 static void set_node_imagepath(char *str)       /* called from fileselect */
323 {
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 ));
327 }
328
329 #endif /* 0 */
330
331 bNode *snode_get_editgroup(SpaceNode *snode)
332 {
333         bNode *gnode;
334         
335         /* get the groupnode */
336         for(gnode= snode->nodetree->nodes.first; gnode; gnode= gnode->next)
337                 if(gnode->flag & NODE_GROUP_EDIT)
338                         break;
339         return gnode;
340 }
341
342 #if 0
343
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)
347 {
348         RenderData rd;
349         Scene *scene= NULL;
350         int scemode, actlay;
351         
352         /* the button press won't show up otherwise, button hilites disabled */
353         force_draw(0);
354         
355         if(node->id && node->id!=(ID *)G.scene) {
356                 scene= G.scene;
357                 set_scene_bg((Scene *)node->id);
358                 rd= G.scene->r;
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;
366         }
367         
368         scemode= G.scene->r.scemode;
369         actlay= G.scene->r.actlay;
370
371         G.scene->r.scemode |= R_SINGLE_LAYER|R_COMP_RERENDER;
372         G.scene->r.actlay= node->custom1;
373         
374         BIF_do_render(0);
375         
376         G.scene->r.scemode= scemode;
377         G.scene->r.actlay= actlay;
378
379         node->custom2= 0;
380         
381         if(scene) {
382                 G.scene->r= rd;
383                 set_scene_bg(scene);
384         }
385 }
386
387 static void composit_node_event(SpaceNode *snode, short event)
388 {
389         
390         switch(event) {
391                 case B_REDR:
392                         // allqueue(REDRAWNODE, 1);
393                         break;
394                 case B_NODE_LOADIMAGE:
395                 {
396                         bNode *node= nodeGetActive(snode->edittree);
397                         char name[FILE_MAXDIR+FILE_MAXFILE];
398                         
399                         if(node->id)
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);
404                         } else {
405                                 activate_fileselect(FILE_SPECIAL, "SELECT IMAGE", name, load_node_image);
406                         }
407                         break;
408                 }
409                 case B_NODE_SETIMAGE:
410                 {
411                         bNode *node= nodeGetActive(snode->edittree);
412                         char name[FILE_MAXDIR+FILE_MAXFILE];
413                         
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);
417                         } else {
418                                 activate_fileselect(FILE_SPECIAL, "SELECT OUTPUT DIR", name, set_node_imagepath);
419                         }
420                         break;
421                 }
422                 case B_NODE_TREE_EXEC:
423                         // XXX                  snode_handle_recalc(snode);
424                         break;          
425                 default:
426                         /* B_NODE_EXEC */
427                 {
428                         bNode *node= BLI_findlink(&snode->edittree->nodes, event-B_NODE_EXEC);
429                         if(node) {
430                                 NodeTagChanged(snode->edittree, node);
431                                 /* don't use NodeTagIDChanged, it gives far too many recomposites for image, scene layers, ... */
432                                 
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);
437                                         
438                                         composite_node_render(snode, node);
439                                         // XXX                  snode_handle_recalc(snode);
440                                         
441                                         /* add another event, a render can go fullscreen and open new window */
442                                         addqueue(curarea->win, UI_BUT_EVENT, B_NODE_TREE_EXEC);
443                                 }
444                                 else {
445                                         node= snode_get_editgroup(snode);
446                                         if(node)
447                                                 NodeTagIDChanged(snode->nodetree, node->id);
448                                         
449                                         // XXX                  snode_handle_recalc(snode);
450                                 }
451                         }
452                 }                       
453         }
454 }
455
456 static void texture_node_event(SpaceNode *snode, short event)
457 {
458         switch(event) {
459                 case B_REDR:
460                         // allqueue(REDRAWNODE, 1);
461                         break;
462                 case B_NODE_LOADIMAGE:
463                 {
464                         bNode *node= nodeGetActive(snode->edittree);
465                         char name[FILE_MAXDIR+FILE_MAXFILE];
466                         
467                         if(node->id)
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);
472                         } else {
473                                 activate_fileselect(FILE_SPECIAL, "SELECT IMAGE", name, load_node_image);
474                         }
475                         break;
476                 }
477                 default:
478                         /* B_NODE_EXEC */
479                         ntreeTexCheckCyclics( snode->nodetree );
480                         // XXX                  snode_handle_recalc(snode);
481                         // allqueue(REDRAWNODE, 1);
482                         break;
483         }
484 }
485
486 #endif /* 0  */
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)
490 {
491         bNode *in, *out;
492         bNodeSocket *fromsock, *tosock;
493         
494         /* but lets check it anyway */
495         if(ma->nodetree) {
496                 printf("error in shader initialize\n");
497                 return;
498         }
499         
500         ma->nodetree= ntreeAddTree(NTREE_SHADER);
501         
502         out= nodeAddNodeType(ma->nodetree, SH_NODE_OUTPUT, NULL, NULL);
503         out->locx= 300.0f; out->locy= 300.0f;
504         
505         in= nodeAddNodeType(ma->nodetree, SH_NODE_MATERIAL, NULL, NULL);
506         in->locx= 10.0f; in->locy= 300.0f;
507         nodeSetActive(ma->nodetree, in);
508         
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);
513         
514         ntreeSolveOrder(ma->nodetree);  /* needed for pointers */
515 }
516
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)
520 {
521         bNode *in, *out;
522         bNodeSocket *fromsock, *tosock;
523         
524         /* but lets check it anyway */
525         if(sce->nodetree) {
526                 printf("error in composit initialize\n");
527                 return;
528         }
529         
530         sce->nodetree= ntreeAddTree(NTREE_COMPOSIT);
531         
532         out= nodeAddNodeType(sce->nodetree, CMP_NODE_COMPOSITE, NULL, NULL);
533         out->locx= 300.0f; out->locy= 400.0f;
534         out->id= &sce->id;
535         
536         in= nodeAddNodeType(sce->nodetree, CMP_NODE_R_LAYERS, NULL, NULL);
537         in->locx= 10.0f; in->locy= 400.0f;
538         in->id= &sce->id;
539         nodeSetActive(sce->nodetree, in);
540         
541         /* links from color to color */
542         fromsock= in->outputs.first;
543         tosock= out->inputs.first;
544         nodeAddLink(sce->nodetree, in, fromsock, out, tosock);
545         
546         ntreeSolveOrder(sce->nodetree); /* needed for pointers */
547         
548         // XXX ntreeCompositForceHidden(sce->nodetree);
549 }
550
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)
554 {
555         bNode *in, *out;
556         bNodeSocket *fromsock, *tosock;
557         
558         /* but lets check it anyway */
559         if(tx->nodetree) {
560                 printf("error in texture initialize\n");
561                 return;
562         }
563         
564         tx->nodetree= ntreeAddTree(NTREE_TEXTURE);
565         
566         out= nodeAddNodeType(tx->nodetree, TEX_NODE_OUTPUT, NULL, NULL);
567         out->locx= 300.0f; out->locy= 300.0f;
568         
569         in= nodeAddNodeType(tx->nodetree, TEX_NODE_CHECKER, NULL, NULL);
570         in->locx= 10.0f; in->locy= 300.0f;
571         nodeSetActive(tx->nodetree, in);
572         
573         fromsock= in->outputs.first;
574         tosock= out->inputs.first;
575         nodeAddLink(tx->nodetree, in, fromsock, out, tosock);
576         
577         ntreeSolveOrder(tx->nodetree);  /* needed for pointers */
578         ntreeTexUpdatePreviews(tx->nodetree); /* XXX texture nodes should follow shader node methods (ton) */
579 }
580
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)
583 {
584         Object *ob= OBACT;
585         bNode *node= NULL;
586         
587         snode->nodetree= NULL;
588         snode->id= snode->from= NULL;
589         
590         if(snode->treetype==NTREE_SHADER) {
591                 /* need active object, or we allow pinning... */
592                 if(ob) {
593                         Material *ma= give_current_material(ob, ob->actcol);
594                         if(ma) {
595                                 snode->from= material_from(ob, ob->actcol);
596                                 snode->id= &ma->id;
597                                 snode->nodetree= ma->nodetree;
598                         }
599                 }
600         }
601         else if(snode->treetype==NTREE_COMPOSIT) {
602                 snode->from= NULL;
603                 snode->id= &scene->id;
604                 
605                 /* bit clumsy but reliable way to see if we draw first time */
606                 if(snode->nodetree==NULL)
607                         ntreeCompositForceHidden(scene->nodetree, scene);
608                 
609                 snode->nodetree= scene->nodetree;
610         }
611         else if(snode->treetype==NTREE_TEXTURE) {
612                 Tex *tx= NULL;
613
614                 if(snode->texfrom==SNODE_TEX_OBJECT) {
615                         if(ob) {
616                                 tx= give_current_texture(ob, ob->actcol);
617                                 snode->from= (ID *)ob;
618                         }
619                 }
620                 else if(snode->texfrom==SNODE_TEX_WORLD) {
621                         tx= give_current_world_texture(scene);
622                         snode->from= (ID *)scene->world;
623                 }
624                 else {
625                         MTex *mtex= NULL;
626                         
627                         if(G.f & G_SCULPTMODE) {
628                                 Sculpt *sd= scene->toolsettings->sculpt;
629                                 if(sd && sd->brush)
630                                         if(sd->brush->texact != -1)
631                                                 mtex= sd->brush->mtex[sd->brush->texact];
632                         }
633                         else {
634                                 Brush *br= scene->toolsettings->imapaint.brush;
635                                 if(br) 
636                                         mtex= br->mtex[br->texact];
637                         }
638                         
639                         if(mtex) {
640                                 snode->from= (ID *)scene;
641                                 tx= mtex->tex;
642                         }
643                 }
644                 
645                 if(tx) {
646                         snode->id= &tx->id;
647                         snode->nodetree= tx->nodetree;
648                 }
649         }
650         
651         /* find editable group */
652         if(snode->nodetree)
653                 for(node= snode->nodetree->nodes.first; node; node= node->next)
654                         if(node->flag & NODE_GROUP_EDIT)
655                                 break;
656         
657         if(node && node->id)
658                 snode->edittree= (bNodeTree *)node->id;
659         else
660                 snode->edittree= snode->nodetree;
661 }
662
663 #if 0
664 /* on activate image viewer, check if we show it */
665 static void node_active_image(Image *ima)
666 {
667         ScrArea *sa;
668         SpaceImage *sima= NULL;
669         
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)
675                                 break;
676                 }
677         }
678         if(sa && sima) {
679                 sima->image= ima;
680                 scrarea_queue_winredraw(sa);
681                 scrarea_queue_headredraw(sa);
682         }
683 }
684 #endif /* 0 */
685
686 void node_set_active(SpaceNode *snode, bNode *node)
687 {
688         
689         nodeSetActive(snode->edittree, node);
690         
691         #if 0
692         // XXX
693         if(node->type!=NODE_GROUP) {
694                 
695                 /* tree specific activate calls */
696                 if(snode->treetype==NTREE_SHADER) {
697                         
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);
701                         if(node->id)
702                                 ; // XXX BIF_preview_changed(-1);       /* temp hack to force texture preview to update */
703                         
704                         // allqueue(REDRAWBUTSSHADING, 1);
705                         // allqueue(REDRAWIPO, 0);
706                 }
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)) {
710                                 bNode *tnode;
711                                 int was_output= node->flag & NODE_DO_OUTPUT;
712
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;
716                                 
717                                 node->flag |= NODE_DO_OUTPUT;
718                                 if(was_output==0) {
719                                         bNode *gnode;
720                                         
721                                         NodeTagChanged(snode->edittree, node);
722                                         
723                                         /* if inside group, tag entire group */
724                                         gnode= snode_get_editgroup(snode);
725                                         if(gnode)
726                                                 NodeTagIDChanged(snode->nodetree, gnode->id);
727                                         
728                                         // XXX                  snode_handle_recalc(snode);
729                                 }
730                                 
731                                 /* addnode() doesnt link this yet... */
732                                 node->id= (ID *)BKE_image_verify_viewer(IMA_TYPE_COMPOSITE, "Viewer Node");
733                         }
734                         else if(node->type==CMP_NODE_IMAGE) {
735                                 if(node->id)
736                                         node_active_image((Image *)node->id);
737                         }
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);
742                                 }
743                         }
744                 }
745                 else if(snode->treetype==NTREE_TEXTURE) {
746                         if(node->id)
747                                 ; // XXX BIF_preview_changed(-1);
748                         // allqueue(REDRAWBUTSSHADING, 1);
749                         // allqueue(REDRAWIPO, 0);
750                 }
751         }
752         #endif /* 0 */
753 }
754
755 void snode_make_group_editable(SpaceNode *snode, bNode *gnode)
756 {
757         bNode *node;
758         
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;
762         
763         if(gnode==NULL) {
764                 /* with NULL argument we do a toggle */
765                 if(snode->edittree==snode->nodetree)
766                         gnode= nodeGetActive(snode->nodetree);
767         }
768         
769         if(gnode && gnode->type==NODE_GROUP && gnode->id) {
770                 if(gnode->id->lib) {
771                         // XXX if(okee("Make Group Local"))
772                         //      ntreeMakeLocal((bNodeTree *)gnode->id);
773                         // else
774                                 return;
775                 }
776                 gnode->flag |= NODE_GROUP_EDIT;
777                 snode->edittree= (bNodeTree *)gnode->id;
778                 
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;
783                 
784         }
785         else 
786                 snode->edittree= snode->nodetree;
787         
788         ntreeSolveOrder(snode->nodetree);
789         
790         /* finally send out events for new active node */
791         if(snode->treetype==NTREE_SHADER) {
792                 // allqueue(REDRAWBUTSSHADING, 0);
793                 
794                 // XXX BIF_preview_changed(-1); /* temp hack to force texture preview to update */
795         }
796         
797         // allqueue(REDRAWNODE, 0);
798 }
799
800 #if 0
801
802 void node_ungroup(SpaceNode *snode)
803 {
804         bNode *gnode;
805
806         /* are we inside of a group? */
807         gnode= snode_get_editgroup(snode);
808         if(gnode)
809                 snode_make_group_editable(snode, NULL);
810         
811         gnode= nodeGetActive(snode->edittree);
812         if(gnode==NULL) return;
813         
814         if(gnode->type!=NODE_GROUP)
815                 error("Not a group");
816         else {
817                 if(nodeGroupUnGroup(snode->edittree, gnode)) {
818                         
819                         // allqueue(REDRAWNODE, 0);
820                 }
821                 else
822                         error("Can't ungroup");
823         }
824 }
825
826 #endif /* 0 */
827 /* when links in groups change, inputs/outputs change, nodes added/deleted... */
828 static void snode_verify_groups(SpaceNode *snode)
829 {
830         bNode *gnode;
831         
832         gnode= snode_get_editgroup(snode);
833         
834         /* does all materials */
835         if(gnode)
836                 nodeVerifyGroup((bNodeTree *)gnode->id);
837         
838 }
839
840 #if 0
841
842 static void node_addgroup(SpaceNode *snode)
843 {
844         bNodeTree *ngroup;
845         int tot= 0, offs, val;
846         char *strp;
847         
848         if(snode->edittree!=snode->nodetree) {
849                 error("Can not add a Group in a Group");
850                 return;
851         }
852         
853         /* construct menu with choices */
854         for(ngroup= G.main->nodetree.first; ngroup; ngroup= ngroup->id.next) {
855                 if(ngroup->type==snode->treetype)
856                         tot++;
857         }
858         if(tot==0) {
859                 error("No groups available in database");
860                 return;
861         }
862         strp= MEM_mallocN(32*tot+32, "menu");
863         strcpy(strp, "Add Group %t");
864         offs= strlen(strp);
865         
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);
869         }       
870         
871         val= pupmenu(strp);
872         if(val>=0) {
873                 ngroup= BLI_findlink(&G.main->nodetree, val);
874                 if(ngroup) {
875                         bNode *node= nodeAddNodeType(snode->edittree, NODE_GROUP, ngroup, NULL);
876                         
877                         /* generics */
878                         if(node) {
879                                 float locx, locy;
880                                 short mval[2];
881
882                                 node_deselectall(snode, 0);
883                                 
884                                 getmouseco_areawin(mval);
885                                 areamouseco_to_ipoco(G.v2d, mval, &locx, &locy);
886                                 
887                                 node->locx= locx;
888                                 node->locy= locy + 60.0f;               // arbitrary.. so its visible
889                                 node->flag |= SELECT;
890                                 
891                                 id_us_plus(node->id);
892                                 
893                                 node_set_active(snode, node);
894                         }
895                 }                       
896         }
897         MEM_freeN(strp);
898 }
899
900 #endif /* 0 */
901
902 /* ************************** Node generic ************** */
903
904 /* allows to walk the list in order of visibility */
905 bNode *next_node(bNodeTree *ntree)
906 {
907         static bNode *current=NULL, *last= NULL;
908         
909         if(ntree) {
910                 /* set current to the first selected node */
911                 for(current= ntree->nodes.last; current; current= current->prev)
912                         if(current->flag & NODE_SELECT)
913                                 break;
914                 
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)
918                                 break;
919                 
920                 if(current==NULL)
921                         current= last;
922                 
923                 return NULL;
924         }
925         /* no nodes, or we are ready */
926         if(current==NULL)
927                 return NULL;
928         
929         /* now we walk the list backwards, but we always return current */
930         if(current->flag & NODE_SELECT) {
931                 bNode *node= current;
932                 
933                 /* find previous selected */
934                 current= current->prev;
935                 while(current && (current->flag & NODE_SELECT)==0)
936                         current= current->prev;
937                 
938                 /* find first unselected */
939                 if(current==NULL)
940                         current= last;
941                 
942                 return node;
943         }
944         else {
945                 bNode *node= current;
946                 
947                 /* find previous unselected */
948                 current= current->prev;
949                 while(current && (current->flag & NODE_SELECT))
950                         current= current->prev;
951                 
952                 return node;
953         }
954         
955         return NULL;
956 }
957
958 /* is rct in visible part of node? */
959 static bNode *visible_node(SpaceNode *snode, rctf *rct)
960 {
961         bNode *tnode;
962         
963         for(next_node(snode->edittree); (tnode=next_node(NULL));) {
964                 if(BLI_isect_rctf(&tnode->totr, rct, NULL))
965                         break;
966         }
967         return tnode;
968 }
969
970 void snode_home(ScrArea *sa, ARegion *ar, SpaceNode* snode)
971 {
972         bNode *node;
973         rctf *cur, *tot;
974         float oldwidth, oldheight, width, height;
975         int first= 1;
976         
977         cur= &ar->v2d.cur;
978         tot= &ar->v2d.tot;
979         
980         oldwidth= cur->xmax - cur->xmin;
981         oldheight= cur->ymax - cur->ymin;
982         
983         cur->xmin= cur->ymin= 0.0f;
984         cur->xmax=ar->winx;
985         cur->xmax= ar->winy;
986         
987         if(snode->edittree) {
988                 for(node= snode->edittree->nodes.first; node; node= node->next) {
989                         if(first) {
990                                 first= 0;
991                                 ar->v2d.cur= node->totr;
992                         }
993                         else {
994                                 BLI_union_rctf(cur, &node->totr);
995                         }
996                 }
997         }
998         
999         snode->xof= 0;
1000         snode->yof= 0;
1001         width= cur->xmax - cur->xmin;
1002         height= cur->ymax- cur->ymin;
1003         if(width > height) {
1004                 float newheight;
1005                 newheight= oldheight * width/oldwidth;
1006                 cur->ymin= cur->ymin - newheight/4;
1007                 cur->ymax= cur->ymin + newheight;
1008         }
1009         else {
1010                 float newwidth;
1011                 newwidth= oldwidth * height/oldheight;
1012                 cur->xmin= cur->xmin - newwidth/4;
1013                 cur->xmax= cur->xmin + newwidth;
1014         }
1015         
1016         ar->v2d.tot= ar->v2d.cur;
1017         UI_view2d_curRect_validate(&ar->v2d);
1018 }
1019
1020 #if 0
1021 static void snode_bg_viewmove(SpaceNode *snode)
1022 {
1023         ScrArea *sa;
1024         Image *ima;
1025         ImBuf *ibuf;
1026         Window *win;
1027         short mval[2], mvalo[2];
1028         short rectx, recty, xmin, xmax, ymin, ymax, pad;
1029         int oldcursor;
1030         
1031         ima= BKE_image_verify_viewer(IMA_TYPE_COMPOSITE, "Viewer Node");
1032         ibuf= BKE_image_get_ibuf(ima, NULL);
1033         
1034         sa = snode->area;
1035         
1036         if(ibuf) {
1037                 rectx = ibuf->x;
1038                 recty = ibuf->y;
1039         } else {
1040                 rectx = recty = 1;
1041         }
1042         
1043         pad = 10;
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;
1048         
1049         getmouseco_sc(mvalo);
1050         
1051         /* store the old cursor to temporarily change it */
1052         oldcursor=get_cursor();
1053         win=winlay_get_active_window();
1054         
1055         SetBlenderCursor(BC_NSEW_SCROLLCURSOR);
1056         
1057         while(get_mbut()&(L_MOUSE|M_MOUSE)) {
1058                 
1059                 getmouseco_sc(mval);
1060                 
1061                 if(mvalo[0]!=mval[0] || mvalo[1]!=mval[1]) {
1062                         
1063                         snode->xof -= (mvalo[0]-mval[0]);
1064                         snode->yof -= (mvalo[1]-mval[1]);
1065                         
1066                         /* prevent dragging image outside of the window and losing it! */
1067                         CLAMP(snode->xof, xmin, xmax);
1068                         CLAMP(snode->yof, ymin, ymax);
1069                         
1070                         mvalo[0]= mval[0];
1071                         mvalo[1]= mval[1];
1072                         
1073                         scrarea_do_windraw(curarea);
1074                         screen_swapbuffers();
1075                 }
1076                 else BIF_wait_for_statechange();
1077         }
1078         
1079         window_set_cursor(win, oldcursor);
1080 }
1081 #endif
1082
1083 /* ********************** size widget operator ******************** */
1084
1085 typedef struct NodeSizeWidget {
1086         float mxstart;
1087         float oldwidth;
1088 } NodeSizeWidget;
1089
1090 static int node_resize_modal(bContext *C, wmOperator *op, wmEvent *event)
1091 {
1092         SpaceNode *snode= (SpaceNode*)CTX_wm_space_data(C);
1093         ARegion *ar= CTX_wm_region(C);
1094         bNode *node= editnode_get_active(snode->edittree);
1095         NodeSizeWidget *nsw= op->customdata;
1096         float mx, my;
1097         
1098         switch (event->type) {
1099                 case MOUSEMOVE:
1100                         
1101                         UI_view2d_region_to_view(&ar->v2d, event->x - ar->winrct.xmin, event->y - ar->winrct.ymin, 
1102                                                                          &mx, &my);
1103                         
1104                         if(node->flag & NODE_HIDDEN) {
1105                                 node->miniwidth= nsw->oldwidth + mx - nsw->mxstart;
1106                                 CLAMP(node->miniwidth, 0.0f, 100.0f);
1107                         }
1108                         else {
1109                                 node->width= nsw->oldwidth + mx - nsw->mxstart;
1110                                 CLAMP(node->width, node->typeinfo->minwidth, node->typeinfo->maxwidth);
1111                         }
1112                         // XXX
1113                         if(snode->nodetree->type == NTREE_TEXTURE)
1114                                 ntreeTexUpdatePreviews(snode->nodetree); /* XXX texture nodes should follow shader node methods (ton) */
1115                                 
1116                         ED_region_tag_redraw(ar);
1117
1118                         break;
1119                         
1120                 case LEFTMOUSE:
1121                 case MIDDLEMOUSE:
1122                 case RIGHTMOUSE:
1123                         
1124                         MEM_freeN(nsw);
1125                         op->customdata= NULL;
1126                         
1127                         return OPERATOR_FINISHED;
1128         }
1129         
1130         return OPERATOR_RUNNING_MODAL;
1131 }
1132
1133 static int node_resize_invoke(bContext *C, wmOperator *op, wmEvent *event)
1134 {
1135         SpaceNode *snode= (SpaceNode*)CTX_wm_space_data(C);
1136         ARegion *ar= CTX_wm_region(C);
1137         bNode *node= editnode_get_active(snode->edittree);
1138         
1139         if(node) {
1140                 rctf totr;
1141                 
1142                 UI_view2d_region_to_view(&ar->v2d, event->x - ar->winrct.xmin, event->y - ar->winrct.ymin, 
1143                                                                  &snode->mx, &snode->my);
1144                 totr= node->totr;
1145                 totr.xmin= totr.xmax-10.0f;
1146                 totr.ymax= totr.ymin+10.0f;
1147                 
1148                 if(BLI_in_rctf(&totr, snode->mx, snode->my)) {
1149                         NodeSizeWidget *nsw= MEM_callocN(sizeof(NodeSizeWidget), "size widget op data");
1150                         
1151                         op->customdata= nsw;
1152                         nsw->mxstart= snode->mx;
1153                         
1154                         /* store old */
1155                         if(node->flag & NODE_HIDDEN)
1156                                 nsw->oldwidth= node->miniwidth;
1157                         else
1158                                 nsw->oldwidth= node->width;
1159                         
1160                         /* add modal handler */
1161                         WM_event_add_modal_handler(C, &CTX_wm_window(C)->handlers, op);
1162
1163                         return OPERATOR_RUNNING_MODAL;
1164                 }
1165         }
1166         return OPERATOR_PASS_THROUGH;
1167 }
1168
1169 void NODE_OT_resize(wmOperatorType *ot)
1170 {
1171         /* identifiers */
1172         ot->name= "Resize Node";
1173         ot->idname= "NODE_OT_resize";
1174         
1175         /* api callbacks */
1176         ot->invoke= node_resize_invoke;
1177         ot->modal= node_resize_modal;
1178         ot->poll= ED_operator_node_active;
1179         
1180         /* flags */
1181         ot->flag= OPTYPE_BLOCKING;
1182 }
1183
1184
1185 #if 0
1186
1187 /* ******************** rename ******************* */
1188
1189 void node_rename(SpaceNode *snode)
1190 {
1191         bNode *node, *rename_node;
1192         short found_node= 0;
1193
1194         /* check if a node is selected */
1195         for(node= snode->edittree->nodes.first; node; node= node->next) {
1196                 if(node->flag & SELECT) {
1197                         found_node= 1;
1198                         break;
1199                 }
1200         }
1201
1202         if(found_node) {
1203                 rename_node= nodeGetActive(snode->edittree);
1204                 node_rename_but((char *)rename_node->username);
1205         
1206                 // allqueue(REDRAWNODE, 1);
1207         }
1208 }
1209
1210 /* ********************** select ******************** */
1211
1212 /* used in buttons to check context, also checks for edited groups */
1213 bNode *editnode_get_active_idnode(bNodeTree *ntree, short id_code)
1214 {
1215         return nodeGetActiveID(ntree, id_code);
1216 }
1217
1218 /* used in buttons to check context, also checks for edited groups */
1219 Material *editnode_get_active_material(Material *ma)
1220 {
1221         if(ma && ma->use_nodes && ma->nodetree) {
1222                 bNode *node= editnode_get_active_idnode(ma->nodetree, ID_MA);
1223                 if(node)
1224                         return (Material *)node->id;
1225                 else
1226                         return NULL;
1227         }
1228         return ma;
1229 }
1230 #endif /* 0 */
1231
1232
1233 /* no undo here! */
1234 void node_deselectall(SpaceNode *snode, int swap)
1235 {
1236         bNode *node;
1237         
1238         if(swap) {
1239                 for(node= snode->edittree->nodes.first; node; node= node->next)
1240                         if(node->flag & SELECT)
1241                                 break;
1242                 if(node==NULL) {
1243                         for(node= snode->edittree->nodes.first; node; node= node->next)
1244                                 node->flag |= SELECT;
1245                         return;
1246                 }
1247                 /* else pass on to deselect */
1248         }
1249         
1250         for(node= snode->edittree->nodes.first; node; node= node->next)
1251                 node->flag &= ~SELECT;
1252 }
1253
1254
1255 int node_has_hidden_sockets(bNode *node)
1256 {
1257         bNodeSocket *sock;
1258         
1259         for(sock= node->inputs.first; sock; sock= sock->next)
1260                 if(sock->flag & SOCK_HIDDEN)
1261                         return 1;
1262         for(sock= node->outputs.first; sock; sock= sock->next)
1263                 if(sock->flag & SOCK_HIDDEN)
1264                         return 1;
1265         return 0;
1266 }
1267
1268
1269 static void node_hide_unhide_sockets(SpaceNode *snode, bNode *node)
1270 {
1271         bNodeSocket *sock;
1272         
1273         /* unhide all */
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;
1279         }
1280         else {
1281                 bNode *gnode= snode_get_editgroup(snode);
1282                 
1283                 /* hiding inside group should not break links in other group users */
1284                 if(gnode) {
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;
1294                 }
1295                 else {
1296                         /* hide unused sockets */
1297                         for(sock= node->inputs.first; sock; sock= sock->next) {
1298                                 if(sock->link==NULL)
1299                                         sock->flag |= SOCK_HIDDEN;
1300                         }
1301                         for(sock= node->outputs.first; sock; sock= sock->next) {
1302                                 if(nodeCountSocketLinks(snode->edittree, sock)==0)
1303                                         sock->flag |= SOCK_HIDDEN;
1304                         }
1305                 }
1306         }
1307
1308         // allqueue(REDRAWNODE, 1);
1309         snode_verify_groups(snode);
1310
1311 }
1312
1313 /*static*/ int do_header_node(SpaceNode *snode, bNode *node, float mx, float my)
1314 {
1315         rctf totr= node->totr;
1316         
1317         totr.ymin= totr.ymax-20.0f;
1318         
1319         totr.xmax= totr.xmin+15.0f;
1320         if(BLI_in_rctf(&totr, mx, my)) {
1321                 node->flag |= NODE_HIDDEN;
1322                 // allqueue(REDRAWNODE, 0);
1323                 return 1;
1324         }       
1325         
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);
1332                         return 1;
1333                 }
1334                 totr.xmin-=18.0f;
1335         }
1336         if(node->type == NODE_GROUP) {
1337                 if(BLI_in_rctf(&totr, mx, my)) {
1338                         snode_make_group_editable(snode, node);
1339                         return 1;
1340                 }
1341                 totr.xmin-=18.0f;
1342         }
1343         if(node->typeinfo->flag & NODE_OPTIONS) {
1344                 if(BLI_in_rctf(&totr, mx, my)) {
1345                         node->flag ^= NODE_OPTIONS;
1346                         // allqueue(REDRAWNODE, 0);
1347                         return 1;
1348                 }
1349                 totr.xmin-=18.0f;
1350         }
1351         /* hide unused sockets */
1352         if(BLI_in_rctf(&totr, mx, my)) {
1353                 node_hide_unhide_sockets(snode, node);
1354         }
1355         
1356         
1357         totr= node->totr;
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);
1362                 return 1;
1363         }
1364         return 0;
1365 }
1366
1367 /*static*/ int do_header_hidden_node(SpaceNode *snode, bNode *node, float mx, float my)
1368 {
1369         rctf totr= node->totr;
1370         
1371         totr.xmax= totr.xmin+15.0f;
1372         if(BLI_in_rctf(&totr, mx, my)) {
1373                 node->flag &= ~NODE_HIDDEN;
1374                 // allqueue(REDRAWNODE, 0);
1375                 return 1;
1376         }       
1377         
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);
1382                 return 1;
1383         }
1384         return 0;
1385 }
1386
1387 static void node_link_viewer(SpaceNode *snode, bNode *tonode)
1388 {
1389         bNode *node;
1390
1391         /* context check */
1392         if(tonode==NULL || tonode->outputs.first==NULL)
1393                 return;
1394         if( ELEM(tonode->type, CMP_NODE_VIEWER, CMP_NODE_SPLITVIEWER)) 
1395                 return;
1396         
1397         /* get viewer */
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)
1401                                 break;
1402                 
1403         if(node) {
1404                 bNodeLink *link;
1405                 
1406                 /* get link to viewer */
1407                 for(link= snode->edittree->links.first; link; link= link->next)
1408                         if(link->tonode==node)
1409                                 break;
1410
1411                 if(link) {
1412                         link->fromnode= tonode;
1413                         link->fromsock= tonode->outputs.first;
1414                         NodeTagChanged(snode->edittree, node);
1415                         
1416 // XXX                  snode_handle_recalc(snode);
1417                 }
1418         }
1419 }
1420
1421
1422 void node_active_link_viewer(SpaceNode *snode)
1423 {
1424         bNode *node= editnode_get_active(snode->edittree);
1425         if(node)
1426                 node_link_viewer(snode, node);
1427 }
1428
1429 /* return 0, nothing done */
1430 /*static*/ int node_mouse_groupheader(SpaceNode *snode)
1431 {
1432         bNode *gnode;
1433         float mx=0, my=0;
1434 // XXX  short mval[2];
1435         
1436         gnode= snode_get_editgroup(snode);
1437         if(gnode==NULL) return 0;
1438         
1439 // XXX  getmouseco_areawin(mval);
1440 // XXX  areamouseco_to_ipoco(G.v2d, mval, &mx, &my);
1441         
1442         /* click in header or outside? */
1443         if(BLI_in_rctf(&gnode->totr, mx, my)==0) {
1444                 rctf rect= gnode->totr;
1445                 
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 */
1449 //              else
1450 // XXX                  transform_nodes(snode->nodetree, 'g', "Move group");
1451                 
1452                 return 1;
1453         }
1454         return 0;
1455 }
1456
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)
1460 {
1461         bNode *node;
1462         bNodeSocket *sock;
1463         rctf rect;
1464         
1465         /* check if we click in a socket */
1466         for(node= snode->edittree->nodes.first; node; node= node->next) {
1467                 
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;
1472                 
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;
1481                         }
1482                 }
1483                 
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)) {
1489                                                         *nodep= node;
1490                                                         *sockp= sock;
1491                                                         return 1;
1492                                                 }
1493                                         }
1494                                 }
1495                         }
1496                 }
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)) {
1502                                                         *nodep= node;
1503                                                         *sockp= sock;
1504                                                         return 1;
1505                                                 }
1506                                         }
1507                                 }
1508                         }
1509                 }
1510         }
1511         return 0;
1512 }
1513
1514 static int node_socket_hilights(SpaceNode *snode, int in_out)
1515 {
1516         bNode *node;
1517         bNodeSocket *sock, *tsock, *socksel= NULL;
1518         short redraw= 0;
1519         
1520         if(snode->edittree==NULL) return 0;
1521         
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;
1527                                 redraw++;
1528                                 socksel= sock;
1529                         }
1530                 }
1531                 for(sock= node->outputs.first; sock; sock= sock->next) {
1532                         if(sock->flag & SELECT) {
1533                                 sock->flag &= ~SELECT;
1534                                 redraw++;
1535                                 socksel= sock;
1536                         }
1537                 }
1538         }
1539         
1540         // XXX mousepos should be set here!
1541         
1542         if(find_indicated_socket(snode, &node, &tsock, in_out)) {
1543                 tsock->flag |= SELECT;
1544                 if(redraw==1 && tsock==socksel) redraw= 0;
1545                 else redraw= 1;
1546         }
1547         
1548         return redraw;
1549 }
1550
1551 /* ****************** Add *********************** */
1552
1553 void snode_autoconnect(SpaceNode *snode, bNode *node_to, int flag)
1554 {
1555         bNodeSocket *sock, *sockfrom[8];
1556         bNode *node, *nodefrom[8];
1557         int totsock= 0, socktype=0;
1558
1559         if(node_to==NULL || node_to->inputs.first==NULL)
1560                 return;
1561         
1562         /* no inputs for node allowed (code it) */
1563
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;
1568
1569         
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;
1577                                         totsock++;
1578                                         if(totsock>7)
1579                                                 break;
1580                                 }
1581                         }
1582                 }
1583                 if(totsock>7)
1584                         break;
1585         }
1586
1587         /* now just get matching socket types and create links */
1588         for(sock= node_to->inputs.first; sock; sock= sock->next) {
1589                 int a;
1590                 
1591                 for(a=0; a<totsock; a++) {
1592                         if(sockfrom[a]) {
1593                                 if(sock->type==sockfrom[a]->type && sock->type==socktype) {
1594                                         nodeAddLink(snode->edittree, nodefrom[a], sockfrom[a], node_to, sock);
1595                                         sockfrom[a]= NULL;
1596                                         break;
1597                                 }
1598                         }
1599                 }
1600         }
1601         
1602         ntreeSolveOrder(snode->edittree);
1603 }
1604
1605
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)
1608 {
1609         bNode *node= NULL, *gnode;
1610         
1611         node_deselectall(snode, 0);
1612         
1613         if(type>=NODE_DYNAMIC_MENU) {
1614                 node= nodeAddNodeType(snode->edittree, type, NULL, NULL);
1615         }
1616         else if(type>=NODE_GROUP_MENU) {
1617                 if(snode->edittree!=snode->nodetree) {
1618                         // XXX error("Can not add a Group in a Group");
1619                         return NULL;
1620                 }
1621                 else {
1622                         bNodeTree *ngroup= BLI_findlink(&G.main->nodetree, type-NODE_GROUP_MENU);
1623                         if(ngroup)
1624                                 node= nodeAddNodeType(snode->edittree, NODE_GROUP, ngroup, NULL);
1625                 }
1626         }
1627         else
1628                 node= nodeAddNodeType(snode->edittree, type, NULL, NULL);
1629         
1630         /* generics */
1631         if(node) {
1632                 node->locx= locx;
1633                 node->locy= locy + 60.0f;               // arbitrary.. so its visible
1634                 node->flag |= SELECT;
1635                 
1636                 gnode= snode_get_editgroup(snode);
1637                 if(gnode) {
1638                         node->locx -= gnode->locx;
1639                         node->locy -= gnode->locy;
1640                 }
1641
1642                 snode_verify_groups(snode);
1643                 node_set_active(snode, node);
1644                 
1645                 if(snode->nodetree->type==NTREE_COMPOSIT) {
1646                         if(ELEM(node->type, CMP_NODE_R_LAYERS, CMP_NODE_COMPOSITE))
1647                                 node->id = &scene->id;
1648                         
1649                         ntreeCompositForceHidden(snode->edittree, scene);
1650                 }
1651                         
1652                 if(node->id)
1653                         id_us_plus(node->id);
1654                         
1655                 NodeTagChanged(snode->edittree, node);
1656         }
1657         
1658         if(snode->nodetree->type==NTREE_TEXTURE) {
1659                 ntreeTexCheckCyclics(snode->edittree);
1660                 ntreeTexUpdatePreviews(snode->edittree); /* XXX texture nodes should follow shader node methods (ton) */
1661         }
1662         
1663         return node;
1664 }
1665
1666 #if 0
1667
1668 void node_mute(SpaceNode *snode)
1669 {
1670         bNode *node;
1671
1672         /* no disabling inside of groups */
1673         if(snode_get_editgroup(snode))
1674                 return;
1675         
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;
1681                                 else
1682                                         node->flag |= NODE_MUTED;
1683                         }
1684                 }
1685         }
1686         
1687
1688 }
1689
1690 #endif
1691
1692 int node_duplicate_exec(bContext *C, wmOperator *op)
1693 {
1694         SpaceNode *snode= (SpaceNode*)CTX_wm_space_data(C);
1695         
1696         ntreeCopyTree(snode->edittree, 1);      /* 1 == internally selected nodes */
1697         
1698         ntreeSolveOrder(snode->edittree);
1699         snode_verify_groups(snode);
1700         snode_handle_recalc(C, snode);
1701
1702         return OPERATOR_FINISHED;
1703 }
1704
1705 static int node_duplicate_invoke(bContext *C, wmOperator *op, wmEvent *event)
1706 {
1707         node_duplicate_exec(C, op);
1708         
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);
1711         
1712         return OPERATOR_FINISHED;
1713 }
1714
1715 void NODE_OT_duplicate(wmOperatorType *ot)
1716 {
1717         
1718         /* identifiers */
1719         ot->name= "Duplicate Nodes";
1720         ot->description = "Duplicate the nodes.";
1721         ot->idname= "NODE_OT_duplicate";
1722         
1723         /* api callbacks */
1724         ot->invoke= node_duplicate_invoke;
1725         ot->exec= node_duplicate_exec;
1726         
1727         ot->poll= ED_operator_node_active;
1728         
1729         /* flags */
1730         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
1731         
1732         /* to give to transform */
1733         RNA_def_int(ot->srna, "mode", TFM_TRANSLATION, 0, INT_MAX, "Mode", "", 0, INT_MAX);
1734 }
1735
1736 #if 0
1737
1738 static void node_insert_convertor(SpaceNode *snode, bNodeLink *link)
1739 {
1740         bNode *newnode= NULL;
1741         
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);
1747                 else
1748                         newnode= NULL;
1749         }
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);
1755                 else
1756                         newnode= NULL;
1757         }
1758         
1759         if(newnode) {
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;
1764                 
1765                 nodeAddLink(snode->edittree, newnode, newnode->outputs.first, link->tonode, link->tosock);
1766                 link->tonode= newnode;
1767                 link->tosock= newnode->inputs.first;
1768         }
1769 }
1770
1771
1772 #endif /* 0 */
1773
1774 /* *************************** add link op ******************** */
1775
1776 /* temp data to pass on to modal */
1777 typedef struct NodeLinkDrag
1778 {
1779         bNode *node;
1780         bNodeSocket *sock;
1781         bNodeLink *link;
1782         int in_out;
1783 } NodeLinkDrag;
1784
1785 /*static*/ void reset_sel_socket(SpaceNode *snode, int in_out)
1786 {
1787         bNode *node;
1788         bNodeSocket *sock;
1789         
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;
1794                 }
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;
1798                 }
1799         }
1800 }
1801
1802
1803 static void node_remove_extra_links(SpaceNode *snode, bNodeSocket *tsock, bNodeLink *link)
1804 {
1805         bNodeLink *tlink;
1806         bNodeSocket *sock;
1807         
1808         if(tsock && nodeCountSocketLinks(snode->edittree, link->tosock) > tsock->limit) {
1809                 
1810                 for(tlink= snode->edittree->links.first; tlink; tlink= tlink->next) {
1811                         if(link!=tlink && tlink->tosock==link->tosock)
1812                                 break;
1813                 }
1814                 if(tlink) {
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)
1819                                                 break;
1820                         }
1821                         if(sock) {
1822                                 tlink->tosock= sock;
1823                                 sock->flag &= ~SOCK_HIDDEN;
1824                         }
1825                         else {
1826                                 nodeRemLink(snode->edittree, tlink);
1827                         }
1828                 }
1829         }
1830 }
1831
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)
1835 {
1836         SpaceNode *snode= (SpaceNode*)CTX_wm_space_data(C);
1837         ARegion *ar= CTX_wm_region(C);
1838         NodeLinkDrag *nldrag= op->customdata;
1839         bNode *tnode, *node;
1840         bNodeSocket *tsock= NULL, *sock;
1841         bNodeLink *link;
1842         int in_out;
1843
1844         in_out= nldrag->in_out;
1845         node= nldrag->node;
1846         sock= nldrag->sock;
1847         link= nldrag->link;
1848         
1849         UI_view2d_region_to_view(&ar->v2d, event->x - ar->winrct.xmin, event->y - ar->winrct.ymin, 
1850                                                          &snode->mx, &snode->my);
1851
1852         switch (event->type) {
1853                 case MOUSEMOVE:
1854                         
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 */
1862                                                 }
1863                                         }
1864                                 }
1865                                 else {
1866                                         link->tonode= NULL;
1867                                         link->tosock= NULL;
1868                                 }
1869                         }
1870                         else {
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 */
1878                                                         }
1879                                                 }
1880                                         }
1881                                 }
1882                                 else {
1883                                         link->fromnode= NULL;
1884                                         link->fromsock= NULL;
1885                                 }
1886                         }
1887                         /* hilight target sockets only */
1888                         node_socket_hilights(snode, in_out==SOCK_OUT?SOCK_IN:SOCK_OUT);
1889                         ED_region_tag_redraw(ar);
1890                         break;
1891                         
1892                 case LEFTMOUSE:
1893                 case RIGHTMOUSE:
1894                 case MIDDLEMOUSE:
1895         
1896                         /* remove link? */
1897                         if(link->tonode==NULL || link->fromnode==NULL) {
1898                                 nodeRemLink(snode->edittree, link);
1899                         }
1900                         else {
1901                                 /* send changed events for original tonode and new */
1902                                 if(link->tonode) 
1903                                         NodeTagChanged(snode->edittree, link->tonode);
1904                                 
1905                                 /* we might need to remove a link */
1906                                 if(in_out==SOCK_OUT) node_remove_extra_links(snode, link->tosock, link);
1907                         }
1908                         
1909                         ntreeSolveOrder(snode->edittree);
1910                         snode_verify_groups(snode);
1911                         snode_handle_recalc(C, snode);
1912                         
1913                         MEM_freeN(op->customdata);
1914                         op->customdata= NULL;
1915                         
1916                         return OPERATOR_FINISHED;
1917         }
1918         
1919         return OPERATOR_RUNNING_MODAL;
1920 }
1921
1922 /* return 1 when socket clicked */
1923 static int node_link_init(SpaceNode *snode, NodeLinkDrag *nldrag)
1924 {
1925         bNodeLink *link;
1926         
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)
1930                         return SOCK_OUT;
1931                 else {
1932                         /* find if we break a link */
1933                         for(link= snode->edittree->links.first; link; link= link->next) {
1934                                 if(link->fromsock==nldrag->sock)
1935                                         break;
1936                         }
1937                         if(link) {
1938                                 nldrag->node= link->tonode;
1939                                 nldrag->sock= link->tosock;
1940                                 nodeRemLink(snode->edittree, link);
1941                                 return SOCK_IN;
1942                         }
1943                 }
1944         }
1945         /* or an input? */
1946         else if(find_indicated_socket(snode, &nldrag->node, &nldrag->sock, SOCK_IN)) {
1947                 if(nodeCountSocketLinks(snode->edittree, nldrag->sock) < nldrag->sock->limit)
1948                         return SOCK_IN;
1949                 else {
1950                         /* find if we break a link */
1951                         for(link= snode->edittree->links.first; link; link= link->next) {
1952                                 if(link->tosock==nldrag->sock)
1953                                         break;
1954                         }
1955                         if(link) {
1956                                 /* send changed event to original tonode */
1957                                 if(link->tonode) 
1958                                         NodeTagChanged(snode->edittree, link->tonode);
1959                                 
1960                                 nldrag->node= link->fromnode;
1961                                 nldrag->sock= link->fromsock;
1962                                 nodeRemLink(snode->edittree, link);
1963                                 return SOCK_OUT;
1964                         }
1965                 }
1966         }
1967         
1968         return 0;
1969 }
1970
1971 static int node_link_invoke(bContext *C, wmOperator *op, wmEvent *event)
1972 {
1973         SpaceNode *snode= (SpaceNode*)CTX_wm_space_data(C);
1974         ARegion *ar= CTX_wm_region(C);
1975         NodeLinkDrag *nldrag= MEM_callocN(sizeof(NodeLinkDrag), "drag link op customdata");
1976         
1977         UI_view2d_region_to_view(&ar->v2d, event->x - ar->winrct.xmin, event->y - ar->winrct.ymin, 
1978                                                          &snode->mx, &snode->my);
1979
1980         nldrag->in_out= node_link_init(snode, nldrag);
1981                 
1982         if(nldrag->in_out) {
1983                 op->customdata= nldrag;
1984                 
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);
1988                 else
1989                         nldrag->link= nodeAddLink(snode->edittree, NULL, NULL, nldrag->node, nldrag->sock);
1990                 
1991                 /* add modal handler */
1992                 WM_event_add_modal_handler(C, &CTX_wm_window(C)->handlers, op);
1993                 
1994                 return OPERATOR_RUNNING_MODAL;
1995         }
1996         else {
1997                 MEM_freeN(nldrag);
1998                 return OPERATOR_CANCELLED|OPERATOR_PASS_THROUGH;
1999         }
2000 }
2001
2002 void NODE_OT_link(wmOperatorType *ot)
2003 {
2004         /* identifiers */
2005         ot->name= "Link Nodes";
2006         ot->idname= "NODE_OT_link";
2007         
2008         /* api callbacks */
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;
2013         
2014         /* flags */
2015         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO|OPTYPE_BLOCKING;
2016 }
2017
2018
2019 void node_delete(SpaceNode *snode)
2020 {
2021         bNode *node, *next;
2022         bNodeSocket *sock;
2023         
2024         for(node= snode->edittree->nodes.first; node; node= next) {
2025                 next= 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;
2030
2031                         for(sock= node->outputs.first; sock; sock= sock->next)
2032                                 if(snode->edittree->selout == sock) snode->edittree->selout= NULL;
2033
2034                         /* check id user here, nodeFreeNode is called for free dbase too */
2035                         if(node->id)
2036                                 node->id->us--;
2037                         nodeFreeNode(snode->edittree, node);
2038                 }
2039         }
2040         
2041         snode_verify_groups(snode);
2042         // NODE_FIX_ME
2043         // snode_handle_recalc(snode);
2044         // allqueue(REDRAWNODE, 1);
2045 }
2046
2047
2048 void node_hide(SpaceNode *snode)
2049 {
2050         bNode *node;
2051         int nothidden=0, ishidden=0;
2052         
2053         for(node= snode->edittree->nodes.first; node; node= node->next) {
2054                 if(node->flag & SELECT) {
2055                         if(node->flag & NODE_HIDDEN)
2056                                 ishidden++;
2057                         else
2058                                 nothidden++;
2059                 }
2060         }
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;
2065                         else 
2066                                 node->flag &= ~NODE_HIDDEN;
2067                 }
2068         }
2069 }
2070
2071 #if 0
2072
2073 void node_insert_key(SpaceNode *snode)
2074 {
2075         bNode *node= editnode_get_active(snode->edittree);
2076         
2077         if(node == NULL)
2078                 return;
2079         
2080         if(node->type==CMP_NODE_TIME) {
2081                 if(node->custom1<node->custom2) {
2082
2083                         CurveMapping *cumap= node->storage;
2084                         float fval, curval;
2085                 
2086                         curval= (float)(CFRA - node->custom1)/(float)(node->custom2-node->custom1);
2087                         fval= curvemapping_evaluateF(cumap, 0, curval);
2088                         
2089                         if(fbutton(&fval, 0.0f, 1.0f, 10, 10, "Insert Value")) {
2090                                 curvemap_insert(cumap->cm, curval, fval);
2091
2092                         }
2093                 }
2094         }
2095 }
2096
2097 void node_select_linked(SpaceNode *snode, int out)
2098 {
2099         bNodeLink *link;
2100         bNode *node;
2101         
2102         /* NODE_TEST is the free flag */
2103         for(node= snode->edittree->nodes.first; node; node= node->next)
2104                 node->flag &= ~NODE_TEST;
2105
2106         for(link= snode->edittree->links.first; link; link= link->next) {
2107                 if(out) {
2108                         if(link->fromnode->flag & NODE_SELECT)
2109                                 link->tonode->flag |= NODE_TEST;
2110                 }
2111                 else {
2112                         if(link->tonode->flag & NODE_SELECT)
2113                                 link->fromnode->flag |= NODE_TEST;
2114                 }
2115         }
2116         
2117         for(node= snode->edittree->nodes.first; node; node= node->next)
2118                 if(node->flag & NODE_TEST)
2119                         node->flag |= NODE_SELECT;
2120         
2121 }
2122
2123 /* makes a link between selected output and input sockets */
2124 void node_make_link(SpaceNode *snode)
2125 {
2126         bNode *fromnode, *tonode;
2127         bNodeLink *link;
2128         bNodeSocket *outsock= snode->edittree->selout;
2129         bNodeSocket *insock= snode->edittree->selin;
2130
2131         if(!insock || !outsock) return;
2132         if(nodeFindLink(snode->edittree, outsock, insock)) return;
2133
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);
2139         }
2140         else return;
2141
2142         ntreeSolveOrder(snode->edittree);
2143         snode_verify_groups(snode);
2144         // XXX                  snode_handle_recalc(snode);
2145
2146 }
2147 #endif
2148
2149 /* ********************** Cut Link operator ***************** */
2150
2151 #define LINK_RESOL 12
2152 static int cut_links_intersect(bNodeLink *link, float mcoords[][2], int tot)
2153 {
2154         float coord_array[LINK_RESOL+1][2];
2155         int i, b;
2156         
2157         if(node_link_bezier_points(NULL, NULL, link, coord_array, LINK_RESOL)) {
2158
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)
2162                                         return 1;
2163         }
2164         return 0;
2165 }
2166
2167 static int cut_links_exec(bContext *C, wmOperator *op)
2168 {
2169         SpaceNode *snode= (SpaceNode*)CTX_wm_space_data(C);
2170         ARegion *ar= CTX_wm_region(C);
2171         float mcoords[256][2];
2172         int i= 0;
2173         
2174         RNA_BEGIN(op->ptr, itemptr, "path") {
2175                 float loc[2];
2176                 
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]);
2180                 i++;
2181                 if(i>= 256) break;
2182         }
2183         RNA_END;
2184         
2185         if(i>1) {
2186                 bNodeLink *link, *next;
2187                 
2188                 for(link= snode->edittree->links.first; link; link= next) {
2189                         next= link->next;
2190                         
2191                         if(cut_links_intersect(link, mcoords, i)) {
2192                                 NodeTagChanged(snode->edittree, link->tonode);
2193                                 nodeRemLink(snode->edittree, link);
2194                         }
2195                 }
2196
2197                 ntreeSolveOrder(snode->edittree);
2198                 snode_verify_groups(snode);
2199                 snode_handle_recalc(C, snode);
2200                 
2201                 return OPERATOR_FINISHED;
2202         }
2203         
2204         return OPERATOR_PASS_THROUGH;;
2205 }
2206
2207 void NODE_OT_links_cut(wmOperatorType *ot)
2208 {
2209         PropertyRNA *prop;
2210         
2211         ot->name= "Cut links";
2212         ot->idname= "NODE_OT_links_cut";
2213         
2214         ot->invoke= WM_gesture_lines_invoke;
2215         ot->modal= WM_gesture_lines_modal;
2216         ot->exec= cut_links_exec;
2217         
2218         ot->poll= ED_operator_node_active;
2219         
2220         /* flags */
2221         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
2222         
2223         prop= RNA_def_property(ot->srna, "path", PROP_COLLECTION, PROP_NONE);
2224         RNA_def_property_struct_runtime(prop, &RNA_OperatorMousePath);
2225         /* internal */
2226         RNA_def_int(ot->srna, "cursor", BC_KNIFECURSOR, 0, INT_MAX, "Cursor", "", 0, INT_MAX);
2227 }
2228
2229 /* ******************************** */
2230
2231 /* goes over all scenes, reads render layerss */
2232 void node_read_renderlayers(SpaceNode *snode)
2233 {
2234         Scene *curscene= NULL; // XXX
2235         Scene *scene;
2236         bNode *node;
2237
2238         /* first tag scenes unread */
2239         for(scene= G.main->scene.first; scene; scene= scene->id.next) 
2240                 scene->id.flag |= LIB_DOIT;
2241
2242         for(node= snode->edittree->nodes.first; node; node= node->next) {
2243                 if(node->type==CMP_NODE_R_LAYERS) {
2244                         ID *id= node->id;
2245                         if(id->flag & LIB_DOIT) {
2246                                 RE_ReadRenderResult(curscene, (Scene *)id);
2247                                 ntreeCompositTagRender((Scene *)id);
2248                                 id->flag &= ~LIB_DOIT;
2249                         }
2250                 }
2251         }
2252         
2253         // XXX                  snode_handle_recalc(snode);
2254 }
2255
2256 void node_read_fullsamplelayers(SpaceNode *snode)
2257 {
2258         Scene *curscene= NULL; // XXX
2259         Render *re= RE_NewRender(curscene->id.name);
2260
2261         WM_cursor_wait(1);
2262
2263         //BIF_init_render_callbacks(re, 1);
2264         RE_MergeFullSample(re, curscene, snode->nodetree);
2265         //BIF_end_render_callbacks();
2266         
2267         // allqueue(REDRAWNODE, 1);
2268         // allqueue(REDRAWIMAGE, 1);
2269         
2270         WM_cursor_wait(0);
2271 }
2272
2273 /* called from header_info, when deleting a scene
2274  * goes over all scenes other than the input, checks if they have
2275  * render layer nodes referencing the to-be-deleted scene, and
2276  * resets them to NULL. */
2277
2278 /* XXX needs to get current scene then! */
2279 void clear_scene_in_nodes(Scene *sce)
2280 {
2281         Scene *sce1;
2282         bNode *node;
2283
2284         sce1= G.main->scene.first;
2285         while(sce1) {
2286                 if(sce1!=sce) {
2287                         if (sce1->nodetree) {
2288                                 for(node= sce1->nodetree->nodes.first; node; node= node->next) {
2289                                         if(node->type==CMP_NODE_R_LAYERS) {
2290                                                 Scene *nodesce= (Scene *)node->id;
2291                                                 
2292                                                 if (nodesce==sce) node->id = NULL;
2293                                         }
2294                                 }
2295                         }
2296                 }
2297                 sce1= sce1->id.next;
2298         }
2299 }
2300
2301 void imagepaint_composite_tags(bNodeTree *ntree, Image *image, ImageUser *iuser)
2302 {
2303         bNode *node;
2304         
2305         if(ntree==NULL)
2306                 return;
2307         
2308         /* search for renderresults */
2309         if(image->type==IMA_TYPE_R_RESULT) {
2310                 for(node= ntree->nodes.first; node; node= node->next) {
2311                         if(node->type==CMP_NODE_R_LAYERS && node->id==NULL) {
2312                                 /* imageuser comes from ImageWin, so indexes are offset 1 */
2313                                 if(node->custom1==iuser->layer-1)
2314                                         NodeTagChanged(ntree, node);
2315                         }
2316                 }
2317         }
2318         else {
2319                 for(node= ntree->nodes.first; node; node= node->next) {
2320                         if(node->id== &image->id)
2321                                 NodeTagChanged(ntree, node);
2322                 }
2323         }
2324 }
2325
2326 /* ********************** */
2327
2328 void node_make_group(SpaceNode *snode)
2329 {
2330         bNode *gnode;
2331         
2332         if(snode->edittree!=snode->nodetree) {
2333 // XXX          error("Can not add a new Group in a Group");
2334                 return;
2335         }
2336         
2337         /* for time being... is too complex to handle */
2338         if(snode->treetype==NTREE_COMPOSIT) {
2339                 for(gnode=snode->nodetree->nodes.first; gnode; gnode= gnode->next) {
2340                         if(gnode->flag & SELECT)
2341                                 if(gnode->type==CMP_NODE_R_LAYERS)
2342                                         break;
2343                 }
2344                 if(gnode) {
2345 // XXX                  error("Can not add RenderLayer in a Group");
2346                         return;
2347                 }
2348         }
2349         
2350         gnode= nodeMakeGroupFromSelected(snode->nodetree);
2351         if(gnode==NULL) {
2352 // XXX          error("Can not make Group");
2353         }
2354         else {
2355                 nodeSetActive(snode->nodetree, gnode);
2356                 ntreeSolveOrder(snode->nodetree);
2357         }
2358 }
2359
2360 #if 0
2361
2362 /* ******************** main event loop ****************** */
2363
2364 void winqreadnodespace(ScrArea *sa, void *spacedata, BWinEvent *evt)
2365 {
2366         SpaceNode *snode= spacedata;
2367         bNode *actnode;
2368         bNodeSocket *actsock;
2369         unsigned short event= evt->event;
2370         short val= evt->val, doredraw=0, fromlib= 0;
2371         
2372         if(sa->win==0) return;
2373         
2374         if(snode->nodetree==NULL) {
2375                 /* no other events should be handled, but floating panels still should get handled */
2376                 uiDoBlocks(&curarea->uiblocks, event, 1);
2377                 return;
2378         }
2379         
2380         if(val) {
2381                 if( node_uiDoBlocks(sa, event)!=UI_NOTHING ) event= 0;
2382                 
2383                 fromlib= (snode->id && snode->id->lib);
2384                 
2385                 switch(event) {
2386                 case LEFTMOUSE:
2387                         if(gpencil_do_paint(sa, L_MOUSE)) {
2388                                 return;
2389                         }
2390                         else if(fromlib) {
2391                                 if(node_mouse_groupheader(snode)==0)
2392                                         //node_mouse_select(snode, event);
2393                         }
2394                         else {
2395                                 
2396                                 if(G.qual & LR_CTRLKEY)
2397                                         if(gesture())
2398                                                 break;
2399                                         
2400 //                              if(node_add_link(snode)==0)
2401                                         if(node_mouse_groupheader(snode)==0)
2402 //                                              if(node_mouse_select(snode, event)==0)
2403                                                         node_border_link_delete(snode);
2404                         }
2405                         break;
2406                         
2407                 case RIGHTMOUSE: 
2408                         if(gpencil_do_paint(sa, R_MOUSE)) {
2409                                 return;
2410                         }
2411                         else if(find_indicated_socket(snode, &actnode, &actsock, SOCK_IN)) {
2412                                 if(actsock->flag & SOCK_SEL) {
2413                                         snode->edittree->selin= NULL;
2414                                         actsock->flag&= ~SOCK_SEL;
2415                                 }
2416                                 else {
2417                                         snode->edittree->selin= actsock;
2418                                         reset_sel_socket(snode, SOCK_IN);
2419                                         actsock->flag|= SOCK_SEL;
2420                                 }
2421                         }
2422                         else if(find_indicated_socket(snode, &actnode, &actsock, SOCK_OUT)) {
2423                                 if(actsock->flag & SOCK_SEL) {
2424                                         snode->edittree->selout= NULL;
2425                                         actsock->flag&= ~SOCK_SEL;
2426                                 }
2427                                 else {
2428                                         snode->edittree->selout= actsock;
2429                                         reset_sel_socket(snode, SOCK_OUT);
2430                                         actsock->flag|= SOCK_SEL;
2431                                 }
2432                         }
2433 //                      else if(!node_mouse_select(snode, event)) 
2434 //                              toolbox_n();
2435
2436                         break;
2437                 case MIDDLEMOUSE:
2438                         if((snode->flag & SNODE_BACKDRAW) && (snode->treetype==NTREE_COMPOSIT)
2439                            && (G.qual==LR_SHIFTKEY)) {
2440                                 snode_bg_viewmove(snode);
2441                         } else {
2442                                 view2dmove(event);
2443                         }
2444                 case WHEELUPMOUSE:
2445                 case WHEELDOWNMOUSE:
2446                         view2dmove(event);      /* in drawipo.c */
2447                         break;
2448                         
2449                 case MOUSEY:
2450                         doredraw= node_socket_hilights(snode, SOCK_IN|SOCK_OUT);
2451                         break;
2452                 
2453                 case UI_BUT_EVENT:
2454                         /* future: handlerize this! */
2455                         if(snode->treetype==NTREE_SHADER)
2456                                 shader_node_event(snode, val);
2457                         else if(snode->treetype==NTREE_COMPOSIT)
2458                                 composit_node_event(snode, val);
2459                         else if(snode->treetype==NTREE_TEXTURE)
2460                                 texture_node_event(snode, val);
2461                         break;
2462                         
2463                 case PADPLUSKEY:
2464                         snode_zoom_in(sa);
2465                         doredraw= 1;
2466                         break;
2467                 case PADMINUS:
2468                         snode_zoom_out(sa);
2469                         doredraw= 1;
2470                         break;
2471                 case HOMEKEY:
2472                         snode_home(sa, snode);
2473                         doredraw= 1;
2474                         break;
2475                 case TABKEY:
2476                         if(fromlib) fromlib= -1;
2477                         else snode_make_group_editable(snode, NULL);
2478                         break;
2479                         
2480                 case AKEY:
2481                         if(G.qual==LR_SHIFTKEY) {
2482                                 if(fromlib) fromlib= -1;
2483                                 else toolbox_n_add();
2484                         }
2485                         else if(G.qual==0) {
2486                                 node_deselectall(snode, 1);
2487                         }
2488                         break;
2489                 case BKEY:
2490                         if(G.qual==0)
2491                                 node_border_select(snode);
2492                         break;
2493                 case CKEY:      /* sort again, showing cyclics */
2494                         ntreeSolveOrder(snode->edittree);
2495                         doredraw= 1;
2496                         break;
2497                 case DKEY:
2498                         if(G.qual==LR_SHIFTKEY) {
2499                                 if(fromlib) fromlib= -1;
2500                                 else node_adduplicate(snode);
2501                         }
2502                         break;
2503                 case EKEY:
2504                         // XXX                  snode_handle_recalc(snode);
2505                         break;
2506                 case FKEY:
2507                         node_make_link(snode);
2508                         break;
2509                 case GKEY:
2510                         if(fromlib) fromlib= -1;
2511                         else {
2512                                 if(G.qual==LR_CTRLKEY) {
2513                                         if(okee("Make Group"))
2514                                                 node_make_group(snode);
2515                                 }
2516                                 else if(G.qual==LR_ALTKEY) {
2517                                         if(okee("Ungroup"))
2518                                                 node_ungroup(snode);
2519                                 }
2520                                 else if(G.qual==LR_SHIFTKEY) {
2521                                         node_addgroup(snode);
2522                                 }
2523 //                              else
2524 // XXX                                  transform_nodes(snode->edittree, 'g', "Move Node");
2525                         }
2526                         break;
2527                 case HKEY:
2528                         node_hide(snode);
2529                         break;
2530                 case IKEY:
2531                         node_insert_key(snode);
2532                         break;
2533                 case LKEY:
2534                         node_select_linked(snode, G.qual==LR_SHIFTKEY);
2535                         break;
2536                 case MKEY:
2537                         node_mute(snode);
2538                         break;
2539                 case RKEY:
2540                         if(G.qual==LR_CTRLKEY) {
2541                                 node_rename(snode);
2542                         } 
2543                         else if(G.qual==LR_SHIFTKEY) {
2544                                 if(okee("Read saved Full Sample Layers"))
2545                                         node_read_fullsamplelayers(snode);
2546                         }
2547                         else {
2548                                 if(okee("Read saved Render Layers"))
2549                                         node_read_renderlayers(snode);
2550                         }
2551                         break;
2552                 case DELKEY:
2553                 case XKEY:
2554                         if(G.qual==LR_ALTKEY) {
2555                                 gpencil_delete_menu();
2556                         }
2557                         else {
2558                                 if(fromlib) fromlib= -1;
2559                                 else node_delete(snode);
2560                         }
2561                         break;
2562                 }
2563         }
2564
2565         if(fromlib==-1)
2566                 error_libdata();
2567         if(doredraw)
2568                 scrarea_queue_winredraw(sa);
2569         if(doredraw==2)
2570                 scrarea_queue_headredraw(sa);
2571 }
2572 #endif
2573
2574 static int node_delete_selection_exec(bContext *C, wmOperator *op)
2575 {
2576         SpaceNode *snode= (SpaceNode*)CTX_wm_space_data(C);
2577         ARegion *ar= CTX_wm_region(C);
2578         
2579         node_delete(snode);
2580         ED_region_tag_redraw(ar);
2581         WM_event_add_notifier(C, NC_SCENE|ND_NODES, NULL); /* Do we need to pass the scene? */
2582
2583         return OPERATOR_FINISHED;
2584 }
2585
2586 /* operators */
2587
2588 void NODE_OT_delete(wmOperatorType *ot)
2589 {
2590         
2591         /* identifiers */
2592         ot->name= "Delete";
2593         ot->idname= "NODE_OT_delete";
2594         
2595         /* api callbacks */
2596         ot->exec= node_delete_selection_exec;
2597         ot->poll= ED_operator_node_active;
2598         
2599         /* flags */
2600         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
2601 }
2602