Orange; more render & compo stuff!
[blender-staging.git] / source / blender / src / editnode.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): none yet.
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_image_types.h"
39 #include "DNA_ipo_types.h"
40 #include "DNA_object_types.h"
41 #include "DNA_material_types.h"
42 #include "DNA_node_types.h"
43 #include "DNA_space_types.h"
44 #include "DNA_screen_types.h"
45 #include "DNA_scene_types.h"
46 #include "DNA_userdef_types.h"
47
48 #include "BKE_global.h"
49 #include "BKE_image.h"
50 #include "BKE_library.h"
51 #include "BKE_main.h"
52 #include "BKE_node.h"
53 #include "BKE_material.h"
54 #include "BKE_utildefines.h"
55
56 #include "BIF_editview.h"
57 #include "BIF_gl.h"
58 #include "BIF_graphics.h"
59 #include "BIF_interface.h"
60 #include "BIF_mywindow.h"
61 #include "BIF_previewrender.h"
62 #include "BIF_resources.h"
63 #include "BIF_renderwin.h"
64 #include "BIF_space.h"
65 #include "BIF_screen.h"
66 #include "BIF_toolbox.h"
67
68 #include "BSE_drawipo.h"
69 #include "BSE_edit.h"
70 #include "BSE_filesel.h"
71 #include "BSE_headerbuttons.h"
72 #include "BSE_node.h"
73
74 #include "BLI_blenlib.h"
75 #include "BLI_arithb.h"
76
77 #include "BDR_editobject.h"
78
79 #include "blendef.h"
80 #include "butspace.h"
81 #include "PIL_time.h"
82 #include "mydevice.h"
83
84
85 /* currently called from BIF_preview_changed */
86 void snode_tag_dirty(SpaceNode *snode)
87 {
88         bNode *node;
89         
90         if(snode->treetype==NTREE_SHADER) {
91                 if(snode->nodetree) {
92                         for(node= snode->nodetree->nodes.first; node; node= node->next) {
93                                 if(node->type==SH_NODE_OUTPUT)
94                                         node->lasty= 0;
95                         }
96                         snode->flag |= SNODE_DO_PREVIEW;        /* this adds an afterqueue on a redraw, to allow button previews to work first */
97                 }
98         }
99         allqueue(REDRAWNODE, 1);
100 }
101
102 static void shader_node_previewrender(ScrArea *sa, SpaceNode *snode)
103 {
104         bNode *node;
105         
106         if(snode->id==NULL) return;
107         if( ((Material *)snode->id )->use_nodes==0 ) return;
108
109         for(node= snode->nodetree->nodes.first; node; node= node->next) {
110                 if(node->type==SH_NODE_OUTPUT) {
111                         if(node->flag & NODE_DO_OUTPUT) {
112                                 if(node->lasty<PREVIEW_RENDERSIZE-2) {
113                                         RenderInfo ri;  
114 //                                      int test= node->lasty;
115                                         
116                                         ri.cury = node->lasty;
117                                         ri.rect = NULL;
118                                         ri.pr_rectx = PREVIEW_RENDERSIZE;
119                                         ri.pr_recty = PREVIEW_RENDERSIZE;
120                                         
121                                         BIF_previewrender(snode->id, &ri, NULL, PR_DO_RENDER);  /* sends redraw event */
122                                         if(ri.rect) MEM_freeN(ri.rect);
123                                         
124                                         if(ri.cury<PREVIEW_RENDERSIZE-2)
125                                                 addafterqueue(sa->win, RENDERPREVIEW, 1);
126 //                                      if(test!=node->lasty)
127 //                                              printf("node rendered to %d\n", node->lasty);
128
129                                         break;
130                                 }
131                         }
132                 }
133         }
134 }
135
136
137 static void snode_handle_recalc(SpaceNode *snode)
138 {
139         if(snode->treetype==NTREE_SHADER) {
140                 BIF_preview_changed(ID_MA);      /* signals buttons windows and node editors */
141         }
142         else if(snode->treetype==NTREE_COMPOSIT) {
143                 ntreeCompositExecTree(snode->nodetree, &G.scene->r, 1); /* 1 is do_previews */
144                 allqueue(REDRAWNODE, 1);
145                 allqueue(REDRAWIMAGE, 1);
146                 if(G.scene->r.scemode & R_DOCOMP) {
147                         BIF_redraw_render_rect();       /* seems to screwup display? */
148                         mywinset(curarea->win);
149                 }
150         }
151 }
152
153 static void shader_node_event(SpaceNode *snode, short event)
154 {
155         switch(event) {
156                 case B_NODE_EXEC:
157                         snode_handle_recalc(snode);
158                         break;
159                 case B_REDR:
160                         allqueue(REDRAWNODE, 1);
161                         break;
162         }
163 }
164
165 static void load_node_image(char *str)  /* called from fileselect */
166 {
167         SpaceNode *snode= curarea->spacedata.first;
168         bNode *node= nodeGetActive(snode->nodetree);
169         Image *ima= NULL;
170         
171         ima= add_image(str);
172         if(ima) {
173                 if(node->id)
174                         node->id->us--;
175                 
176                 node->id= &ima->id;
177                 ima->id.us++;
178
179                 free_image_buffers(ima);        /* force read again */
180                 ima->ok= 1;
181                 
182                 addqueue(curarea->win, RENDERPREVIEW, 1);
183                 allqueue(REDRAWNODE, 0);
184         }
185 }
186
187 static void composit_node_event(SpaceNode *snode, short event)
188 {
189         
190         switch(event) {
191                 case B_NODE_EXEC:
192                         snode_handle_recalc(snode);
193                         break;
194                 case B_REDR:
195                         allqueue(REDRAWNODE, 1);
196                         break;
197                 case B_NODE_LOADIMAGE:
198                 {
199                         bNode *node= nodeGetActive(snode->nodetree);
200                         char name[FILE_MAXDIR+FILE_MAXFILE];
201                         
202                         if(node->id)
203                                 strcpy(name, ((Image *)node->id)->name);
204                         else strcpy(name, U.textudir);
205                         
206                         activate_fileselect(FILE_SPECIAL, "SELECT IMAGE", name, load_node_image);
207                 }
208         }
209 }
210
211
212 /* assumes nothing being done in ntree yet, sets the default in/out node */
213 /* called from shading buttons or header */
214 void node_shader_default(Material *ma)
215 {
216         bNode *in, *out;
217         bNodeSocket *fromsock, *tosock;
218         
219         /* but lets check it anyway */
220         if(ma->nodetree) {
221                 printf("error in shader initialize\n");
222                 return;
223         }
224         
225         ma->nodetree= ntreeAddTree(NTREE_SHADER);
226         
227         out= nodeAddNodeType(ma->nodetree, SH_NODE_OUTPUT, NULL);
228         out->locx= 300.0f; out->locy= 300.0f;
229         
230         in= nodeAddNodeType(ma->nodetree, SH_NODE_MATERIAL, NULL);
231         in->locx= 10.0f; in->locy= 300.0f;
232         nodeSetActive(ma->nodetree, in);
233         
234         /* only a link from color to color */
235         fromsock= in->outputs.first;
236         tosock= out->inputs.first;
237         nodeAddLink(ma->nodetree, in, fromsock, out, tosock);
238         
239         ntreeSolveOrder(ma->nodetree);  /* needed for pointers */
240 }
241
242 /* assumes nothing being done in ntree yet, sets the default in/out node */
243 /* called from shading buttons or header */
244 void node_composit_default(Scene *sce)
245 {
246         bNode *in, *out;
247         bNodeSocket *fromsock, *tosock;
248         
249         /* but lets check it anyway */
250         if(sce->nodetree) {
251                 printf("error in composit initialize\n");
252                 return;
253         }
254         
255         sce->nodetree= ntreeAddTree(NTREE_COMPOSIT);
256         
257         out= nodeAddNodeType(sce->nodetree, CMP_NODE_VIEWER, NULL);
258         out->locx= 300.0f; out->locy= 300.0f;
259         
260         in= nodeAddNodeType(sce->nodetree, CMP_NODE_R_RESULT, NULL);
261         in->locx= 10.0f; in->locy= 300.0f;
262         nodeSetActive(sce->nodetree, in);
263         
264         /* only a link from color to color */
265         fromsock= in->outputs.first;
266         tosock= out->inputs.first;
267         nodeAddLink(sce->nodetree, in, fromsock, out, tosock);
268         
269         ntreeSolveOrder(sce->nodetree); /* needed for pointers */
270         
271         out->id= find_id("IM", "Compositor");
272         if(out->id==NULL) {
273                 Image *ima= alloc_libblock(&G.main->image, ID_IM, "Compositor");
274                 strcpy(ima->name, "Compositor");
275                 ima->ok= 1;
276                 ima->xrep= ima->yrep= 1;
277                 out->id= &ima->id;
278         }
279 }
280
281 /* Here we set the active tree(s), even called for each redraw now, so keep it fast :) */
282 void snode_set_context(SpaceNode *snode)
283 {
284         Object *ob= OBACT;
285         bNode *node= NULL;
286         
287         snode->nodetree= NULL;
288         snode->id= snode->from= NULL;
289         
290         if(snode->treetype==NTREE_SHADER) {
291                 /* need active object, or we allow pinning... */
292                 if(ob) {
293                         Material *ma= give_current_material(ob, ob->actcol);
294                         if(ma) {
295                                 snode->from= material_from(ob, ob->actcol);
296                                 snode->id= &ma->id;
297                                 snode->nodetree= ma->nodetree;
298                         }
299                 }
300         }
301         else if(snode->treetype==NTREE_COMPOSIT) {
302                 snode->from= NULL;
303                 snode->id= &G.scene->id;
304                 snode->nodetree= G.scene->nodetree;
305         }
306         
307         /* find editable group */
308         if(snode->nodetree)
309                 for(node= snode->nodetree->nodes.first; node; node= node->next)
310                         if(node->flag & NODE_GROUP_EDIT)
311                                 break;
312         
313         if(node && node->id)
314                 snode->edittree= (bNodeTree *)node->id;
315         else
316                 snode->edittree= snode->nodetree;
317 }
318
319 static void node_set_active(SpaceNode *snode, bNode *node)
320 {
321         
322         nodeSetActive(snode->edittree, node);
323         
324         if(node->type!=NODE_GROUP) {
325                 
326                 /* tree specific activate calls */
327                 if(snode->treetype==NTREE_SHADER) {
328                         
329                         /* when we select a material, active texture is cleared, for buttons */
330                         if(node->id && GS(node->id->name)==ID_MA)
331                                 nodeClearActiveID(snode->edittree, ID_TE);
332                         if(node->id)
333                                 BIF_preview_changed(-1);        /* temp hack to force texture preview to update */
334                         
335                         allqueue(REDRAWBUTSSHADING, 1);
336                 }
337                 else if(snode->treetype==NTREE_COMPOSIT) {
338                         /* make active viewer, currently only 1 supported... */
339                         if(node->type==CMP_NODE_VIEWER) {
340                                 bNode *tnode;
341                                 int was_output= node->flag & NODE_DO_OUTPUT;
342
343                                 for(tnode= snode->edittree->nodes.first; tnode; tnode= tnode->next)
344                                         if(tnode->type==CMP_NODE_VIEWER)
345                                                 tnode->flag &= ~NODE_DO_OUTPUT;
346                                 
347                                 node->flag |= NODE_DO_OUTPUT;
348                                 if(was_output==0) snode_handle_recalc(snode);
349                                 
350                                 /* add node doesnt link this yet... */
351                                 if(node->id==NULL) {
352                                         node->id= find_id("IM", "Compositor");
353                                         if(node->id==NULL) {
354                                                 Image *ima= alloc_libblock(&G.main->image, ID_IM, "Compositor");
355                                                 strcpy(ima->name, "Compositor");
356                                                 ima->ok= 1;
357                                                 ima->xrep= ima->yrep= 1;
358                                                 node->id= &ima->id;
359                                         }
360                                         else 
361                                                 node->id->us++;
362                                 }
363                         }
364                 }
365         }
366 }
367
368 static bNode *snode_get_editgroup(SpaceNode *snode)
369 {
370         bNode *gnode;
371         
372         /* get the groupnode */
373         for(gnode= snode->nodetree->nodes.first; gnode; gnode= gnode->next)
374                 if(gnode->flag & NODE_GROUP_EDIT)
375                         break;
376         return gnode;
377 }
378
379 static void snode_make_group_editable(SpaceNode *snode, bNode *gnode)
380 {
381         bNode *node;
382         
383         /* make sure nothing has group editing on */
384         for(node= snode->nodetree->nodes.first; node; node= node->next)
385                 node->flag &= ~NODE_GROUP_EDIT;
386         
387         if(gnode==NULL) {
388                 /* with NULL argument we do a toggle */
389                 if(snode->edittree==snode->nodetree)
390                         gnode= nodeGetActive(snode->nodetree);
391         }
392         
393         if(gnode && gnode->type==NODE_GROUP && gnode->id) {
394                 gnode->flag |= NODE_GROUP_EDIT;
395                 snode->edittree= (bNodeTree *)gnode->id;
396                 
397                 /* deselect all other nodes, so we can also do grabbing of entire subtree */
398                 for(node= snode->nodetree->nodes.first; node; node= node->next)
399                         node->flag &= ~SELECT;
400                 gnode->flag |= SELECT;
401                 
402         }
403         else 
404                 snode->edittree= snode->nodetree;
405         
406         /* finally send out events for new active node */
407         if(snode->treetype==NTREE_SHADER) {
408                 allqueue(REDRAWBUTSSHADING, 0);
409                 
410                 BIF_preview_changed(-1);        /* temp hack to force texture preview to update */
411         }
412         
413         allqueue(REDRAWNODE, 0);
414 }
415
416 static void node_ungroup(SpaceNode *snode)
417 {
418         bNode *gnode;
419         
420         gnode= nodeGetActive(snode->edittree);
421         if(gnode->type!=NODE_GROUP)
422                 error("Not a group");
423         else {
424                 if(nodeGroupUnGroup(snode->edittree, gnode)) {
425                         
426                         ntreeSolveOrder(snode->edittree);
427                         BIF_undo_push("Deselect all nodes");
428                         allqueue(REDRAWNODE, 0);
429                 }
430                 else
431                         error("Can't ungroup");
432         }
433 }
434
435 /* when links in groups change, inputs/outputs change, nodes added/deleted... */
436 static void snode_verify_groups(SpaceNode *snode)
437 {
438         bNode *gnode;
439         
440         gnode= snode_get_editgroup(snode);
441         
442         /* does all materials */
443         if(gnode)
444                 nodeVerifyGroup((bNodeTree *)gnode->id);
445         
446 }
447
448 /* ************************** Node generic ************** */
449
450 /* allows to walk the list in order of visibility */
451 static bNode *next_node(bNodeTree *ntree)
452 {
453         static bNode *current=NULL, *last= NULL;
454         
455         if(ntree) {
456                 /* set current to the first selected node */
457                 for(current= ntree->nodes.last; current; current= current->prev)
458                         if(current->flag & NODE_SELECT)
459                                 break;
460                 
461                 /* set last to the first unselected node */
462                 for(last= ntree->nodes.last; last; last= last->prev)
463                         if((last->flag & NODE_SELECT)==0)
464                                 break;
465                 
466                 if(current==NULL)
467                         current= last;
468                 
469                 return NULL;
470         }
471         /* no nodes, or we are ready */
472         if(current==NULL)
473                 return NULL;
474         
475         /* now we walk the list backwards, but we always return current */
476         if(current->flag & NODE_SELECT) {
477                 bNode *node= current;
478                 
479                 /* find previous selected */
480                 current= current->prev;
481                 while(current && (current->flag & NODE_SELECT)==0)
482                         current= current->prev;
483                 
484                 /* find first unselected */
485                 if(current==NULL)
486                         current= last;
487                 
488                 return node;
489         }
490         else {
491                 bNode *node= current;
492                 
493                 /* find previous unselected */
494                 current= current->prev;
495                 while(current && (current->flag & NODE_SELECT))
496                         current= current->prev;
497                 
498                 return node;
499         }
500         
501         return NULL;
502 }
503
504 /* is rct in visible part of node? */
505 static bNode *visible_node(SpaceNode *snode, rctf *rct)
506 {
507         bNode *tnode;
508         
509         for(next_node(snode->edittree); (tnode=next_node(NULL));) {
510                 if(BLI_isect_rctf(&tnode->totr, rct, NULL))
511                         break;
512         }
513         return tnode;
514 }
515
516 static void snode_home(ScrArea *sa, SpaceNode *snode)
517 {
518         bNode *node;
519         int first= 1;
520         
521         snode->v2d.cur.xmin= snode->v2d.cur.ymin= 0.0f;
522         snode->v2d.cur.xmax= sa->winx;
523         snode->v2d.cur.xmax= sa->winy;
524         
525         for(node= snode->edittree->nodes.first; node; node= node->next) {
526                 if(first) {
527                         first= 0;
528                         snode->v2d.cur= node->totr;
529                 }
530                 else {
531                         BLI_union_rctf(&snode->v2d.cur, &node->totr);
532                 }
533         }
534         snode->v2d.tot= snode->v2d.cur;
535         test_view2d(G.v2d, sa->winx, sa->winy);
536         
537 }
538
539 /* checks mouse position, and returns found node/socket */
540 /* type is SOCK_IN and/or SOCK_OUT */
541 static int find_indicated_socket(SpaceNode *snode, bNode **nodep, bNodeSocket **sockp, int in_out)
542 {
543         bNode *node;
544         bNodeSocket *sock;
545         rctf rect;
546         short mval[2];
547         
548         getmouseco_areawin(mval);
549         areamouseco_to_ipoco(G.v2d, mval, &rect.xmin, &rect.ymin);
550         
551         rect.xmin -= NODE_SOCKSIZE+3;
552         rect.ymin -= NODE_SOCKSIZE+3;
553         rect.xmax = rect.xmin + 2*NODE_SOCKSIZE+6;
554         rect.ymax = rect.ymin + 2*NODE_SOCKSIZE+6;
555         
556         /* check if we click in a socket */
557         for(node= snode->edittree->nodes.first; node; node= node->next) {
558                 if(in_out & SOCK_IN) {
559                         for(sock= node->inputs.first; sock; sock= sock->next) {
560                                 if(!(sock->flag & SOCK_HIDDEN)) {
561                                         if(BLI_in_rctf(&rect, sock->locx, sock->locy)) {
562                                                 if(node == visible_node(snode, &rect)) {
563                                                         *nodep= node;
564                                                         *sockp= sock;
565                                                         return 1;
566                                                 }
567                                         }
568                                 }
569                         }
570                 }
571                 if(in_out & SOCK_OUT) {
572                         for(sock= node->outputs.first; sock; sock= sock->next) {
573                                 if(!(sock->flag & SOCK_HIDDEN)) {
574                                         if(BLI_in_rctf(&rect, sock->locx, sock->locy)) {
575                                                 if(node == visible_node(snode, &rect)) {
576                                                         *nodep= node;
577                                                         *sockp= sock;
578                                                         return 1;
579                                                 }
580                                         }
581                                 }
582                         }
583                 }
584         }
585         return 0;
586 }
587
588 /* ********************* transform ****************** */
589
590 /* releases on event, only intern (for extern see below) */
591 /* we need argument ntree to allow operations on edittree or nodetree */
592 static void transform_nodes(bNodeTree *ntree, char mode, char *undostr)
593 {
594         bNode *node;
595         float mxstart, mystart, mx, my, *oldlocs, *ol;
596         int cont=1, tot=0, cancel=0, firsttime=1;
597         short mval[2], mvalo[2];
598         
599         /* count total */
600         for(node= ntree->nodes.first; node; node= node->next)
601                 if(node->flag & SELECT) tot++;
602         
603         if(tot==0) return;
604         
605         /* store oldlocs */
606         ol= oldlocs= MEM_mallocN(sizeof(float)*2*tot, "oldlocs transform");
607         for(node= ntree->nodes.first; node; node= node->next) {
608                 if(node->flag & SELECT) {
609                         ol[0]= node->locx; ol[1]= node->locy;
610                         ol+= 2;
611                 }
612         }
613         
614         getmouseco_areawin(mvalo);
615         areamouseco_to_ipoco(G.v2d, mvalo, &mxstart, &mystart);
616         
617         while(cont) {
618                 
619                 getmouseco_areawin(mval);
620                 if(mval[0]!=mvalo[0] || mval[1]!=mvalo[1] || firsttime) {
621 //                      char str[64];
622
623                         firsttime= 0;
624                         
625                         areamouseco_to_ipoco(G.v2d, mval, &mx, &my);
626                         mvalo[0]= mval[0];
627                         mvalo[1]= mval[1];
628                         
629                         for(ol= oldlocs, node= ntree->nodes.first; node; node= node->next) {
630                                 if(node->flag & SELECT) {
631                                         node->locx= ol[0] + mx-mxstart;
632                                         node->locy= ol[1] + my-mystart;
633                                         ol+= 2;
634                                 }
635                         }
636                         
637 //                      sprintf(str, "X: %.1f Y: %.1f", mx-mxstart, my-mystart);
638 //                      headerprint(str);
639                         force_draw(0);
640                 }
641                 else
642                         PIL_sleep_ms(10);
643                 
644                 while (qtest()) {
645                         short val;
646                         unsigned short event= extern_qread(&val);
647                         
648                         switch (event) {
649                                 case LEFTMOUSE:
650                                 case SPACEKEY:
651                                 case RETKEY:
652                                         cont=0;
653                                         break;
654                                 case ESCKEY:
655                                 case RIGHTMOUSE:
656                                         if(val) {
657                                                 cancel=1;
658                                                 cont=0;
659                                         }
660                                         break;
661                                 default:
662                                         if(val) arrows_move_cursor(event);
663                                         break;
664                         }
665                 }
666                 
667         }
668         
669         if(cancel) {
670                 for(ol= oldlocs, node= ntree->nodes.first; node; node= node->next) {
671                         if(node->flag & SELECT) {
672                                 node->locx= ol[0];
673                                 node->locy= ol[1];
674                                 ol+= 2;
675                         }
676                 }
677                 
678         }
679         else
680                 BIF_undo_push(undostr);
681         
682         allqueue(REDRAWNODE, 1);
683         MEM_freeN(oldlocs);
684 }
685
686 /* external call, also for callback */
687 void node_transform_ext(int mode, int unused)
688 {
689         SpaceNode *snode= curarea->spacedata.first;
690         
691         transform_nodes(snode->edittree, 'g', "Translate node");
692 }
693
694
695 /* releases on event, only 1 node */
696 static void scale_node(SpaceNode *snode, bNode *node)
697 {
698         float mxstart, mystart, mx, my, oldwidth;
699         int cont=1, cancel=0;
700         short mval[2], mvalo[2];
701         
702         /* store old */
703         if(node->flag & NODE_HIDDEN)
704                 oldwidth= node->miniwidth;
705         else
706                 oldwidth= node->width;
707                 
708         getmouseco_areawin(mvalo);
709         areamouseco_to_ipoco(G.v2d, mvalo, &mxstart, &mystart);
710         
711         while(cont) {
712                 
713                 getmouseco_areawin(mval);
714                 if(mval[0]!=mvalo[0] || mval[1]!=mvalo[1]) {
715                         
716                         areamouseco_to_ipoco(G.v2d, mval, &mx, &my);
717                         mvalo[0]= mval[0];
718                         mvalo[1]= mval[1];
719                         
720                         if(node->flag & NODE_HIDDEN) {
721                                 node->miniwidth= oldwidth + mx-mxstart;
722                                 CLAMP(node->miniwidth, 0.0f, 100.0f);
723                         }
724                         else {
725                                 node->width= oldwidth + mx-mxstart;
726                                 CLAMP(node->width, node->typeinfo->minwidth, node->typeinfo->maxwidth);
727                         }
728                         
729                         force_draw(0);
730                 }
731                 else
732                         PIL_sleep_ms(10);
733                 
734                 while (qtest()) {
735                         short val;
736                         unsigned short event= extern_qread(&val);
737                         
738                         switch (event) {
739                                 case LEFTMOUSE:
740                                 case SPACEKEY:
741                                 case RETKEY:
742                                         cont=0;
743                                         break;
744                                 case ESCKEY:
745                                 case RIGHTMOUSE:
746                                         if(val) {
747                                                 cancel=1;
748                                                 cont=0;
749                                         }
750                                         break;
751                         }
752                 }
753                 
754         }
755         
756         if(cancel) {
757                 node->width= oldwidth;
758         }
759         else
760                 BIF_undo_push("Scale Node");
761         
762         allqueue(REDRAWNODE, 1);
763 }
764
765
766
767 /* ********************** select ******************** */
768
769 /* used in buttons to check context, also checks for edited groups */
770 bNode *editnode_get_active_idnode(bNodeTree *ntree, short id_code)
771 {
772         bNode *node;
773         
774         /* check for edited group */
775         for(node= ntree->nodes.first; node; node= node->next)
776                 if(node->flag & NODE_GROUP_EDIT)
777                         break;
778         if(node)
779                 return nodeGetActiveID((bNodeTree *)node->id, id_code);
780         else
781                 return nodeGetActiveID(ntree, id_code);
782 }
783
784 /* used in buttons to check context, also checks for edited groups */
785 Material *editnode_get_active_material(Material *ma)
786 {
787         if(ma && ma->use_nodes && ma->nodetree) {
788                 bNode *node= editnode_get_active_idnode(ma->nodetree, ID_MA);
789                 if(node)
790                         return (Material *)node->id;
791                 else
792                         return NULL;
793         }
794         return ma;
795 }
796
797 /* used in buttons to check context, also checks for edited groups */
798 bNode *editnode_get_active(bNodeTree *ntree)
799 {
800         bNode *node;
801         
802         /* check for edited group */
803         for(node= ntree->nodes.first; node; node= node->next)
804                 if(node->flag & NODE_GROUP_EDIT)
805                         break;
806         if(node)
807                 return nodeGetActive((bNodeTree *)node->id);
808         else
809                 return nodeGetActive(ntree);
810 }
811
812
813 /* no undo here! */
814 void node_deselectall(SpaceNode *snode, int swap)
815 {
816         bNode *node;
817         
818         if(swap) {
819                 for(node= snode->edittree->nodes.first; node; node= node->next)
820                         if(node->flag & SELECT)
821                                 break;
822                 if(node==NULL) {
823                         for(node= snode->edittree->nodes.first; node; node= node->next)
824                                 node->flag |= SELECT;
825                         allqueue(REDRAWNODE, 0);
826                         return;
827                 }
828                 /* else pass on to deselect */
829         }
830         
831         for(node= snode->edittree->nodes.first; node; node= node->next)
832                 node->flag &= ~SELECT;
833         
834         allqueue(REDRAWNODE, 0);
835 }
836
837 int node_has_hidden_sockets(bNode *node)
838 {
839         bNodeSocket *sock;
840         
841         for(sock= node->inputs.first; sock; sock= sock->next)
842                 if(sock->flag & SOCK_HIDDEN)
843                         return 1;
844         for(sock= node->outputs.first; sock; sock= sock->next)
845                 if(sock->flag & SOCK_HIDDEN)
846                         return 1;
847         return 0;
848 }
849
850
851 static void node_hide_unhide_sockets(SpaceNode *snode, bNode *node)
852 {
853         bNodeSocket *sock;
854         
855         /* unhide all */
856         if( node_has_hidden_sockets(node) ) {
857                 for(sock= node->inputs.first; sock; sock= sock->next)
858                         sock->flag &= ~SOCK_HIDDEN;
859                 for(sock= node->outputs.first; sock; sock= sock->next)
860                         sock->flag &= ~SOCK_HIDDEN;
861         }
862         else {
863                 bNode *gnode= snode_get_editgroup(snode);
864                 
865                 /* hiding inside group should not break links in other group users */
866                 if(gnode) {
867                         nodeGroupSocketUseFlags((bNodeTree *)gnode->id);
868                         for(sock= node->inputs.first; sock; sock= sock->next)
869                                 if(!(sock->flag & SOCK_IN_USE))
870                                         if(sock->link==NULL)
871                                                 sock->flag |= SOCK_HIDDEN;
872                         for(sock= node->outputs.first; sock; sock= sock->next)
873                                 if(!(sock->flag & SOCK_IN_USE))
874                                         if(nodeCountSocketLinks(snode->edittree, sock)==0)
875                                                 sock->flag |= SOCK_HIDDEN;
876                 }
877                 else {
878                         /* hide unused sockets */
879                         for(sock= node->inputs.first; sock; sock= sock->next) {
880                                 if(sock->link==NULL)
881                                         sock->flag |= SOCK_HIDDEN;
882                         }
883                         for(sock= node->outputs.first; sock; sock= sock->next) {
884                                 if(nodeCountSocketLinks(snode->edittree, sock)==0)
885                                         sock->flag |= SOCK_HIDDEN;
886                         }
887                 }
888         }
889
890         allqueue(REDRAWNODE, 1);
891         snode_verify_groups(snode);
892         BIF_undo_push("Hide/Unhide sockets");
893
894 }
895
896 static int do_header_node(SpaceNode *snode, bNode *node, float mx, float my)
897 {
898         rctf totr= node->totr;
899         
900         totr.ymin= totr.ymax-20.0f;
901         
902         totr.xmax= totr.xmin+15.0f;
903         if(BLI_in_rctf(&totr, mx, my)) {
904                 node->flag |= NODE_HIDDEN;
905                 allqueue(REDRAWNODE, 0);
906                 return 1;
907         }       
908         
909         totr.xmax= node->totr.xmax;
910         totr.xmin= totr.xmax-18.0f;
911         if(node->typeinfo->flag & NODE_PREVIEW) {
912                 if(BLI_in_rctf(&totr, mx, my)) {
913                         node->flag ^= NODE_PREVIEW;
914                         allqueue(REDRAWNODE, 0);
915                         return 1;
916                 }
917                 totr.xmin-=18.0f;
918         }
919         if(node->type == NODE_GROUP) {
920                 if(BLI_in_rctf(&totr, mx, my)) {
921                         snode_make_group_editable(snode, node);
922                         return 1;
923                 }
924                 totr.xmin-=18.0f;
925         }
926         if(node->typeinfo->flag & NODE_OPTIONS) {
927                 if(BLI_in_rctf(&totr, mx, my)) {
928                         node->flag ^= NODE_OPTIONS;
929                         allqueue(REDRAWNODE, 0);
930                         return 1;
931                 }
932                 totr.xmin-=18.0f;
933         }
934         if(node->outputs.first) {
935                 if(BLI_in_rctf(&totr, mx, my)) {
936                         node_hide_unhide_sockets(snode, node);
937                 }
938         }
939         
940         
941         totr= node->totr;
942         totr.xmin= totr.xmax-10.0f;
943         totr.ymax= totr.ymin+10.0f;
944         if(BLI_in_rctf(&totr, mx, my)) {
945                 scale_node(snode, node);
946                 return 1;
947         }
948         return 0;
949 }
950
951 static int do_header_hidden_node(SpaceNode *snode, bNode *node, float mx, float my)
952 {
953         rctf totr= node->totr;
954         
955         totr.xmax= totr.xmin+15.0f;
956         if(BLI_in_rctf(&totr, mx, my)) {
957                 node->flag &= ~NODE_HIDDEN;
958                 allqueue(REDRAWNODE, 0);
959                 return 1;
960         }       
961         
962         totr.xmax= node->totr.xmax;
963         totr.xmin= node->totr.xmax-15.0f;
964         if(BLI_in_rctf(&totr, mx, my)) {
965                 scale_node(snode, node);
966                 return 1;
967         }
968         return 0;
969 }
970
971
972 /* return 0: nothing done */
973 static int node_mouse_select(SpaceNode *snode, unsigned short event)
974 {
975         bNode *node;
976         float mx, my;
977         short mval[2];
978         
979         getmouseco_areawin(mval);
980         areamouseco_to_ipoco(G.v2d, mval, &mx, &my);
981         
982         for(next_node(snode->edittree); (node=next_node(NULL));) {
983                 
984                 /* first check for the headers or scaling widget */
985                 if(node->flag & NODE_HIDDEN) {
986                         if(do_header_hidden_node(snode, node, mx, my))
987                                 return 1;
988                 }
989                 else {
990                         if(do_header_node(snode, node, mx, my))
991                                 return 1;
992                 }
993                 
994                 /* node body */
995                 if(BLI_in_rctf(&node->totr, mx, my))
996                         break;
997         }
998         if(node) {
999                 if((G.qual & LR_SHIFTKEY)==0)
1000                         node_deselectall(snode, 0);
1001                 
1002                 if(G.qual & LR_SHIFTKEY) {
1003                         if(node->flag & SELECT)
1004                                 node->flag &= ~SELECT;
1005                         else
1006                                 node->flag |= SELECT;
1007                 }
1008                 else 
1009                         node->flag |= SELECT;
1010                 
1011                 node_set_active(snode, node);
1012                 
1013                 /* not so nice (no event), but function below delays redraw otherwise */
1014                 force_draw(0);
1015                 
1016                 std_rmouse_transform(node_transform_ext);       /* does undo push for select */
1017                 
1018                 return 1;
1019         }
1020         return 0;
1021 }
1022
1023 /* return 0, nothing done */
1024 static int node_mouse_groupheader(SpaceNode *snode)
1025 {
1026         bNode *gnode;
1027         float mx, my;
1028         short mval[2];
1029         
1030         gnode= snode_get_editgroup(snode);
1031         if(gnode==NULL) return 0;
1032         
1033         getmouseco_areawin(mval);
1034         areamouseco_to_ipoco(G.v2d, mval, &mx, &my);
1035         
1036         /* click in header or outside? */
1037         if(BLI_in_rctf(&gnode->totr, mx, my)==0) {
1038                 rctf rect= gnode->totr;
1039                 
1040                 rect.ymax += NODE_DY;
1041                 if(BLI_in_rctf(&rect, mx, my)==0)
1042                         snode_make_group_editable(snode, NULL); /* toggles, so exits editmode */
1043                 else
1044                         transform_nodes(snode->nodetree, 'g', "Move group");
1045                 
1046                 return 1;
1047         }
1048         return 0;
1049 }
1050
1051 static int node_socket_hilights(SpaceNode *snode, int in_out)
1052 {
1053         bNode *node;
1054         bNodeSocket *sock, *tsock, *socksel= NULL;
1055         float mx, my;
1056         short mval[2], redraw= 0;
1057         
1058         if(snode->edittree==NULL) return 0;
1059         
1060         getmouseco_areawin(mval);
1061         areamouseco_to_ipoco(G.v2d, mval, &mx, &my);
1062         
1063         /* deselect socks */
1064         for(node= snode->edittree->nodes.first; node; node= node->next) {
1065                 for(sock= node->inputs.first; sock; sock= sock->next) {
1066                         if(sock->flag & SELECT) {
1067                                 sock->flag &= ~SELECT;
1068                                 redraw++;
1069                                 socksel= sock;
1070                         }
1071                 }
1072                 for(sock= node->outputs.first; sock; sock= sock->next) {
1073                         if(sock->flag & SELECT) {
1074                                 sock->flag &= ~SELECT;
1075                                 redraw++;
1076                                 socksel= sock;
1077                         }
1078                 }
1079         }
1080         
1081         if(find_indicated_socket(snode, &node, &tsock, in_out)) {
1082                 tsock->flag |= SELECT;
1083                 if(redraw==1 && tsock==socksel) redraw= 0;
1084                 else redraw= 1;
1085         }
1086         
1087         return redraw;
1088 }
1089
1090 void node_border_select(SpaceNode *snode)
1091 {
1092         bNode *node;
1093         rcti rect;
1094         rctf rectf;
1095         short val, mval[2];
1096         
1097         if ( (val = get_border(&rect, 3)) ) {
1098                 
1099                 mval[0]= rect.xmin;
1100                 mval[1]= rect.ymin;
1101                 areamouseco_to_ipoco(G.v2d, mval, &rectf.xmin, &rectf.ymin);
1102                 mval[0]= rect.xmax;
1103                 mval[1]= rect.ymax;
1104                 areamouseco_to_ipoco(G.v2d, mval, &rectf.xmax, &rectf.ymax);
1105                 
1106                 for(node= snode->edittree->nodes.first; node; node= node->next) {
1107                         if(BLI_isect_rctf(&rectf, &node->totr, NULL)) {
1108                                 if(val==LEFTMOUSE)
1109                                         node->flag |= SELECT;
1110                                 else
1111                                         node->flag &= ~SELECT;
1112                         }
1113                 }
1114                 allqueue(REDRAWNODE, 1);
1115                 BIF_undo_push("Border select nodes");
1116         }               
1117 }
1118
1119 /* ****************** Add *********************** */
1120
1121 /* can be called from menus too, but they should do own undopush and redraws */
1122 bNode *node_add_node(SpaceNode *snode, int type, float locx, float locy)
1123 {
1124         bNode *node= NULL, *gnode;
1125         
1126         node_deselectall(snode, 0);
1127         
1128         node= nodeAddNodeType(snode->edittree, type, NULL);
1129         
1130         /* generics */
1131         if(node) {
1132                 node->locx= locx;
1133                 node->locy= locy + 60.0f;               // arbitrary.. so its visible
1134                 node->flag |= SELECT;
1135                 
1136                 gnode= snode_get_editgroup(snode);
1137                 if(gnode) {
1138                         node->locx -= gnode->locx;
1139                         node->locy -= gnode->locy;
1140                 }
1141
1142                 node_set_active(snode, node);
1143                 snode_verify_groups(snode);
1144         }
1145         return node;
1146 }
1147
1148 /* hotkey context */
1149 static void node_add_menu(SpaceNode *snode)
1150 {
1151         float locx, locy;
1152         short event, mval[2];
1153         
1154         if(snode->treetype==NTREE_SHADER) {
1155                 /* shader menu, still hardcoded defines... solve */
1156                 event= pupmenu("Add Node%t|Output%x1|Geometry%x108|Material%x100|Texture%x106|Mapping%x109|Normal%x107|RGB Curves%x111|Vector Curves%x110|Value %x102|Color %x101|Mix Color %x103|ColorRamp %x104|Color to BW %x105");
1157                 if(event<1) return;
1158         }
1159         else if(snode->treetype==NTREE_COMPOSIT) {
1160                 /* compo menu, still hardcoded defines... solve */
1161                 event= pupmenu("Add Node%t|Render Result %x221|Composite %x222|Viewer%x201|Image %x220|RGB Curves%x209|AlphaOver %x210|Blur %x211|Filter %x212|Value %x203|Color %x202|Mix %x204|ColorRamp %x205|Color to BW %x206|Map Value %x213|Normal %x207");
1162                 if(event<1) return;
1163         }
1164         else return;
1165         
1166         getmouseco_areawin(mval);
1167         areamouseco_to_ipoco(G.v2d, mval, &locx, &locy);
1168         node_add_node(snode, event, locx, locy);
1169         
1170         snode_handle_recalc(snode);
1171         
1172         BIF_undo_push("Add Node");
1173 }
1174
1175 void node_adduplicate(SpaceNode *snode)
1176 {
1177         
1178         ntreeCopyTree(snode->edittree, 1);      /* 1 == internally selected nodes */
1179         
1180         ntreeSolveOrder(snode->edittree);
1181         snode_verify_groups(snode);
1182         snode_handle_recalc(snode);
1183
1184         transform_nodes(snode->edittree, 'g', "Duplicate");
1185 }
1186
1187 static void node_insert_convertor(SpaceNode *snode, bNodeLink *link)
1188 {
1189         bNode *newnode= NULL;
1190         
1191         if(link->fromsock->type==SOCK_RGBA && link->tosock->type==SOCK_VALUE) {
1192                 if(snode->edittree->type==NTREE_SHADER)
1193                         newnode= node_add_node(snode, SH_NODE_RGBTOBW, 0.0f, 0.0f);
1194                 else if(snode->edittree->type==NTREE_COMPOSIT)
1195                         newnode= node_add_node(snode, CMP_NODE_RGBTOBW, 0.0f, 0.0f);
1196                 else
1197                         newnode= NULL;
1198         }
1199         else if(link->fromsock->type==SOCK_VALUE && link->tosock->type==SOCK_RGBA) {
1200                 if(snode->edittree->type==NTREE_SHADER)
1201                         newnode= node_add_node(snode, SH_NODE_VALTORGB, 0.0f, 0.0f);
1202                 else if(snode->edittree->type==NTREE_COMPOSIT)
1203                         newnode= node_add_node(snode, CMP_NODE_VALTORGB, 0.0f, 0.0f);
1204                 else
1205                         newnode= NULL;
1206         }
1207         
1208         if(newnode) {
1209                 /* dangerous assumption to use first in/out socks, but thats fine for now */
1210                 newnode->flag |= NODE_HIDDEN;
1211                 newnode->locx= 0.5f*(link->fromsock->locx + link->tosock->locx);
1212                 newnode->locy= 0.5f*(link->fromsock->locy + link->tosock->locy) + HIDDEN_RAD;
1213                 
1214                 nodeAddLink(snode->edittree, newnode, newnode->outputs.first, link->tonode, link->tosock);
1215                 link->tonode= newnode;
1216                 link->tosock= newnode->inputs.first;
1217         }
1218 }
1219
1220
1221 /* loop that adds a nodelink, called by function below  */
1222 /* in_out = starting socket */
1223 static int node_add_link_drag(SpaceNode *snode, bNode *node, bNodeSocket *sock, int in_out)
1224 {
1225         bNode *tnode;
1226         bNodeSocket *tsock;
1227         bNodeLink *link= NULL;
1228         short mval[2], mvalo[2], firsttime=1;   /* firsttime reconnects a link broken by caller */
1229         
1230         /* we make a temporal link */
1231         if(in_out==SOCK_OUT)
1232                 link= nodeAddLink(snode->edittree, node, sock, NULL, NULL);
1233         else
1234                 link= nodeAddLink(snode->edittree, NULL, NULL, node, sock);
1235         
1236         getmouseco_areawin(mvalo);
1237         while (get_mbut() & L_MOUSE) {
1238                 
1239                 getmouseco_areawin(mval);
1240                 if(mval[0]!=mvalo[0] || mval[1]!=mvalo[1] || firsttime) {
1241                         firsttime= 0;
1242                         
1243                         mvalo[0]= mval[0];
1244                         mvalo[1]= mval[1];
1245                         
1246                         if(in_out==SOCK_OUT) {
1247                                 if(find_indicated_socket(snode, &tnode, &tsock, SOCK_IN)) {
1248                                         if(nodeFindLink(snode->edittree, sock, tsock)==NULL) {
1249                                                 if(tnode!=node  && link->tonode!=tnode && link->tosock!= tsock) {
1250                                                         link->tonode= tnode;
1251                                                         link->tosock= tsock;
1252                                                         ntreeSolveOrder(snode->edittree);       /* for interactive red line warning */
1253                                                 }
1254                                         }
1255                                 }
1256                                 else {
1257                                         link->tonode= NULL;
1258                                         link->tosock= NULL;
1259                                 }
1260                         }
1261                         else {
1262                                 if(find_indicated_socket(snode, &tnode, &tsock, SOCK_OUT)) {
1263                                         if(nodeFindLink(snode->edittree, sock, tsock)==NULL) {
1264                                                 if(nodeCountSocketLinks(snode->edittree, tsock) < tsock->limit) {
1265                                                         if(tnode!=node && link->fromnode!=tnode && link->fromsock!= tsock) {
1266                                                                 link->fromnode= tnode;
1267                                                                 link->fromsock= tsock;
1268                                                                 ntreeSolveOrder(snode->edittree);       /* for interactive red line warning */
1269                                                         }
1270                                                 }
1271                                         }
1272                                 }
1273                                 else {
1274                                         link->fromnode= NULL;
1275                                         link->fromsock= NULL;
1276                                 }
1277                         }
1278                         /* hilight target sockets only */
1279                         node_socket_hilights(snode, in_out==SOCK_OUT?SOCK_IN:SOCK_OUT);
1280                         
1281                         force_draw(0);
1282                 }
1283                 else BIF_wait_for_statechange();                
1284         }
1285         
1286         if(link->tonode==NULL || link->fromnode==NULL) {
1287                 nodeRemLink(snode->edittree, link);
1288         }
1289         else {
1290                 bNodeLink *tlink;
1291                 /* we might need to remove a link */
1292                 if(in_out==SOCK_OUT) {
1293                         if(nodeCountSocketLinks(snode->edittree, link->tosock) > tsock->limit) {
1294                                 
1295                                 for(tlink= snode->edittree->links.first; tlink; tlink= tlink->next) {
1296                                         if(link!=tlink && tlink->tosock==link->tosock)
1297                                                 break;
1298                                 }
1299                                 if(tlink) {
1300                                         /* is there a free input socket with same type? */
1301                                         for(tsock= tlink->tonode->inputs.first; tsock; tsock= tsock->next) {
1302                                                 if(tsock->type==tlink->fromsock->type)
1303                                                         if(nodeCountSocketLinks(snode->edittree, tsock) < tsock->limit)
1304                                                                 break;
1305                                         }
1306                                         if(tsock)
1307                                                 tlink->tosock= tsock;
1308                                         else {
1309                                                 nodeRemLink(snode->edittree, tlink);
1310                                         }
1311                                 }                                       
1312                         }
1313                 }
1314                 
1315                 /* and last trick: insert a convertor when types dont match */
1316                 if(link->tosock->type!=link->fromsock->type) {
1317                         node_insert_convertor(snode, link);
1318                         ntreeSolveOrder(snode->edittree);               /* so nice do it twice! well, the sort-order can only handle 1 added link at a time */
1319                 }
1320         }
1321         
1322         ntreeSolveOrder(snode->edittree);
1323         snode_verify_groups(snode);
1324         snode_handle_recalc(snode);
1325         
1326         allqueue(REDRAWNODE, 0);
1327         BIF_undo_push("Add link");
1328
1329         return 1;
1330 }
1331
1332 /* return 1 when socket clicked */
1333 static int node_add_link(SpaceNode *snode)
1334 {
1335         bNode *node;
1336         bNodeLink *link;
1337         bNodeSocket *sock;
1338         
1339         /* output indicated? */
1340         if(find_indicated_socket(snode, &node, &sock, SOCK_OUT)) {
1341                 if(nodeCountSocketLinks(snode->edittree, sock)<sock->limit)
1342                         return node_add_link_drag(snode, node, sock, SOCK_OUT);
1343                 else {
1344                         /* find if we break a link */
1345                         for(link= snode->edittree->links.first; link; link= link->next) {
1346                                 if(link->fromsock==sock)
1347                                         break;
1348                         }
1349                         if(link) {
1350                                 node= link->tonode;
1351                                 sock= link->tosock;
1352                                 nodeRemLink(snode->edittree, link);
1353                                 return node_add_link_drag(snode, node, sock, SOCK_IN);
1354                         }
1355                 }
1356         }
1357         /* or an input? */
1358         else if(find_indicated_socket(snode, &node, &sock, SOCK_IN)) {
1359                 if(nodeCountSocketLinks(snode->edittree, sock)<sock->limit)
1360                         return node_add_link_drag(snode, node, sock, SOCK_IN);
1361                 else {
1362                         /* find if we break a link */
1363                         for(link= snode->edittree->links.first; link; link= link->next) {
1364                                 if(link->tosock==sock)
1365                                         break;
1366                         }
1367                         if(link) {
1368                                 node= link->fromnode;
1369                                 sock= link->fromsock;
1370                                 nodeRemLink(snode->edittree, link);
1371                                 return node_add_link_drag(snode, node, sock, SOCK_OUT);
1372                         }
1373                 }
1374         }
1375         
1376         return 0;
1377 }
1378
1379 static void node_delete(SpaceNode *snode)
1380 {
1381         bNode *node, *next;
1382         
1383         for(node= snode->edittree->nodes.first; node; node= next) {
1384                 next= node->next;
1385                 if(node->flag & SELECT)
1386                         nodeFreeNode(snode->edittree, node);
1387         }
1388         
1389         snode_verify_groups(snode);
1390         snode_handle_recalc(snode);
1391         BIF_undo_push("Delete nodes");
1392         allqueue(REDRAWNODE, 1);
1393 }
1394
1395 static void node_hide(SpaceNode *snode)
1396 {
1397         bNode *node;
1398         int nothidden=0, ishidden=0;
1399         
1400         for(node= snode->edittree->nodes.first; node; node= node->next) {
1401                 if(node->flag & SELECT) {
1402                         if(node->flag & NODE_HIDDEN)
1403                                 ishidden++;
1404                         else
1405                                 nothidden++;
1406                 }
1407         }
1408         for(node= snode->edittree->nodes.first; node; node= node->next) {
1409                 if(node->flag & SELECT) {
1410                         if( (ishidden && nothidden) || ishidden==0)
1411                                 node->flag |= NODE_HIDDEN;
1412                         else 
1413                                 node->flag &= ~NODE_HIDDEN;
1414                 }
1415         }
1416         BIF_undo_push("Hide nodes");
1417         allqueue(REDRAWNODE, 1);
1418 }
1419                         
1420
1421 static void node_border_link_delete(SpaceNode *snode)
1422 {
1423         rcti rect;
1424         short val, mval[2], mvalo[2];
1425
1426         /* to make this work more friendly, we first wait for a mouse move */
1427         getmouseco_areawin(mvalo);
1428         while (get_mbut() & L_MOUSE) {
1429                 getmouseco_areawin(mval);
1430                 if(mval[0]!=mvalo[0] || mval[1]!=mvalo[1])
1431                         break;
1432                 else BIF_wait_for_statechange();
1433         }
1434         if((get_mbut() & L_MOUSE)==0)
1435                 return;
1436         
1437         /* now change cursor and draw border */
1438         setcursor_space(SPACE_NODE, CURSOR_VPAINT);
1439         
1440         if ( (val = get_border(&rect, 2)) ) {
1441                 if(rect.xmin<rect.xmax && rect.ymin<rect.ymax) {
1442                         //#define NODE_MAXPICKBUF       256
1443                         bNodeLink *link, *next;
1444                         GLuint buffer[256];
1445                         rctf rectf;
1446                         int code=0, hits;
1447                         
1448                         mval[0]= rect.xmin;
1449                         mval[1]= rect.ymin;
1450                         areamouseco_to_ipoco(&snode->v2d, mval, &rectf.xmin, &rectf.ymin);
1451                         mval[0]= rect.xmax;
1452                         mval[1]= rect.ymax;
1453                         areamouseco_to_ipoco(&snode->v2d, mval, &rectf.xmax, &rectf.ymax);
1454                         
1455                         myortho2(rectf.xmin, rectf.xmax, rectf.ymin, rectf.ymax);
1456                         
1457                         glSelectBuffer(256, buffer); 
1458                         glRenderMode(GL_SELECT);
1459                         glInitNames();
1460                         glPushName(-1);
1461                         
1462                         /* draw links */
1463                         for(link= snode->edittree->links.first; link; link= link->next) {
1464                                 glLoadName(code++);
1465                                 node_draw_link(snode, link);
1466                         }
1467                         
1468                         hits= glRenderMode(GL_RENDER);
1469                         glPopName();
1470                         if(hits>0) {
1471                                 int a;
1472                                 for(a=0; a<hits; a++) {
1473                                         bNodeLink *link= BLI_findlink(&snode->edittree->links, buffer[ (4 * a) + 3]);
1474                                         if(link)
1475                                                 link->tonode= NULL;     /* first tag for delete, otherwise indices are wrong */
1476                                 }
1477                                 for(link= snode->edittree->links.first; link; link= next) {
1478                                         next= link->next;
1479                                         if(link->tonode==NULL) {
1480                                                 nodeRemLink(snode->edittree, link);
1481                                         }
1482                                 }
1483                                 ntreeSolveOrder(snode->edittree);
1484                                 snode_verify_groups(snode);
1485                                 snode_handle_recalc(snode);
1486                         }
1487                         allqueue(REDRAWNODE, 0);
1488                         BIF_undo_push("Erase links");
1489                 }
1490         }
1491         
1492         setcursor_space(SPACE_NODE, CURSOR_STD);
1493 }
1494
1495
1496 /* ********************** */
1497
1498 void node_make_group(SpaceNode *snode)
1499 {
1500         bNode *gnode;
1501         
1502         if(snode->edittree!=snode->nodetree) {
1503                 error("Can not add a new Group in a Group");
1504                 return;
1505         }
1506         
1507         /* for time being... is too complex to handle */
1508         if(snode->treetype==NTREE_COMPOSIT) {
1509                 for(gnode=snode->nodetree->nodes.first; gnode; gnode= gnode->next) {
1510                         if(gnode->flag & SELECT)
1511                                 if(gnode->type==CMP_NODE_R_RESULT)
1512                                         break;
1513                 }
1514                 if(gnode) {
1515                         error("Can not add RenderResult in a Group");
1516                         return;
1517                 }
1518         }
1519         
1520         gnode= nodeMakeGroupFromSelected(snode->nodetree);
1521         if(gnode==NULL) {
1522                 error("Can not make Group");
1523         }
1524         else {
1525                 nodeSetActive(snode->nodetree, gnode);
1526                 ntreeSolveOrder(snode->nodetree);
1527                 allqueue(REDRAWNODE, 0);
1528                 BIF_undo_push("Make Node Group");
1529         }
1530 }
1531
1532 /* ******************** main event loop ****************** */
1533
1534 /* special version to prevent overlapping buttons, has a bit of hack... */
1535 /* yes, check for example composit_node_event(), file window use... */
1536 static int node_uiDoBlocks(SpaceNode *snode, ListBase *lb, short event)
1537 {
1538         bNode *node;
1539         rctf rect;
1540         ListBase listb= *lb;
1541         void *prev;
1542         int retval= UI_NOTHING;
1543         short mval[2];
1544         
1545         getmouseco_areawin(mval);
1546         areamouseco_to_ipoco(G.v2d, mval, &rect.xmin, &rect.ymin);
1547
1548         /* this happens after filesel usage... */
1549         if(lb->first==NULL) {
1550                 return UI_NOTHING;
1551         }
1552         
1553         rect.xmin -= 2.0f;
1554         rect.ymin -= 2.0f;
1555         rect.xmax = rect.xmin + 4.0f;
1556         rect.ymax = rect.ymin + 4.0f;
1557         
1558         for(node= snode->edittree->nodes.first; node; node= node->next) {
1559                 if(node->block) {
1560                         if(node == visible_node(snode, &rect)) {
1561                                 
1562                                 /* when there's menus, the prev pointer becomes zero! */
1563                                 prev= ((struct Link *)node->block)->prev;
1564                                 
1565                                 lb->first= lb->last= node->block;
1566                                 retval= uiDoBlocks(lb, event);
1567                                 
1568                                 ((struct Link *)node->block)->prev= prev;
1569
1570                                 break;
1571                         }
1572                 }
1573         }
1574         
1575         *lb= listb;
1576         
1577         return retval;
1578 }
1579
1580 void winqreadnodespace(ScrArea *sa, void *spacedata, BWinEvent *evt)
1581 {
1582         SpaceNode *snode= spacedata;
1583         float dx;
1584         unsigned short event= evt->event;
1585         short val= evt->val, doredraw=0, fromlib= 0;
1586         
1587         if(sa->win==0) return;
1588         if(snode->nodetree==NULL) return;
1589         
1590         if(val) {
1591
1592                 if( node_uiDoBlocks(snode, &sa->uiblocks, event)!=UI_NOTHING ) event= 0;        
1593
1594                 fromlib= (snode->id && snode->id->lib);
1595                 
1596                 switch(event) {
1597                 case LEFTMOUSE:
1598                         if(fromlib) {
1599                                 if(node_mouse_groupheader(snode)==0)
1600                                         node_mouse_select(snode, event);
1601                         }
1602                         else {
1603                                 if(node_add_link(snode)==0)
1604                                         if(node_mouse_groupheader(snode)==0)
1605                                                 if(node_mouse_select(snode, event)==0)
1606                                                         node_border_link_delete(snode);
1607                         }
1608                         break;
1609                         
1610                 case RIGHTMOUSE: 
1611                         node_mouse_select(snode, event);
1612
1613                         break;
1614                 case MIDDLEMOUSE:
1615                 case WHEELUPMOUSE:
1616                 case WHEELDOWNMOUSE:
1617                         view2dmove(event);      /* in drawipo.c */
1618                         break;
1619                         
1620                 case MOUSEY:
1621                         doredraw= node_socket_hilights(snode, SOCK_IN|SOCK_OUT);
1622                         break;
1623                 
1624                 case UI_BUT_EVENT:
1625                         /* future: handlerize this! */
1626                         if(snode->treetype==NTREE_SHADER)
1627                                 shader_node_event(snode, val);
1628                         else if(snode->treetype==NTREE_COMPOSIT)
1629                                 composit_node_event(snode, val);
1630                         break;
1631                         
1632                 case RENDERPREVIEW:
1633                         if(snode->treetype==NTREE_SHADER)
1634                                 shader_node_previewrender(sa, snode);
1635                         else if(snode->treetype==NTREE_COMPOSIT)
1636                                 snode_handle_recalc(snode);
1637                         break;
1638                         
1639                 case PADPLUSKEY:
1640                         dx= (float)(0.1154*(G.v2d->cur.xmax-G.v2d->cur.xmin));
1641                         G.v2d->cur.xmin+= dx;
1642                         G.v2d->cur.xmax-= dx;
1643                         dx= (float)(0.1154*(G.v2d->cur.ymax-G.v2d->cur.ymin));
1644                         G.v2d->cur.ymin+= dx;
1645                         G.v2d->cur.ymax-= dx;
1646                         test_view2d(G.v2d, sa->winx, sa->winy);
1647                         doredraw= 1;
1648                         break;
1649                 case PADMINUS:
1650                         dx= (float)(0.15*(G.v2d->cur.xmax-G.v2d->cur.xmin));
1651                         G.v2d->cur.xmin-= dx;
1652                         G.v2d->cur.xmax+= dx;
1653                         dx= (float)(0.15*(G.v2d->cur.ymax-G.v2d->cur.ymin));
1654                         G.v2d->cur.ymin-= dx;
1655                         G.v2d->cur.ymax+= dx;
1656                         test_view2d(G.v2d, sa->winx, sa->winy);
1657                         doredraw= 1;
1658                         break;
1659                 case HOMEKEY:
1660                         snode_home(sa, snode);
1661                         doredraw= 1;
1662                         break;
1663                 case TABKEY:
1664                         if(fromlib) fromlib= -1;
1665                         else snode_make_group_editable(snode, NULL);
1666                         break;
1667                         
1668                 case AKEY:
1669                         if(G.qual==LR_SHIFTKEY) {
1670                                 if(fromlib) fromlib= -1;
1671                                 else node_add_menu(snode);
1672                         }
1673                         else if(G.qual==0) {
1674                                 node_deselectall(snode, 1);
1675                                 BIF_undo_push("Deselect all nodes");
1676                         }
1677                         break;
1678                 case BKEY:
1679                         if(G.qual==0)
1680                                 node_border_select(snode);
1681                         break;
1682                 case CKEY:      /* sort again, showing cyclics */
1683                         ntreeSolveOrder(snode->edittree);
1684                         doredraw= 1;
1685                         break;
1686                 case DKEY:
1687                         if(G.qual==LR_SHIFTKEY) {
1688                                 if(fromlib) fromlib= -1;
1689                                 else node_adduplicate(snode);
1690                         }
1691                         break;
1692                 case GKEY:
1693                         if(fromlib) fromlib= -1;
1694                         else {
1695                                 if(G.qual==LR_CTRLKEY) {
1696                                         if(okee("Make Group"))
1697                                                 node_make_group(snode);
1698                                 }
1699                                 else if(G.qual==LR_ALTKEY) {
1700                                         if(okee("Ungroup"))
1701                                                 node_ungroup(snode);
1702                                 }
1703                                 else
1704                                         transform_nodes(snode->edittree, 'g', "Translate Node");
1705                         }
1706                         break;
1707                 case HKEY:
1708                         node_hide(snode);
1709                         break;
1710                         
1711                 case DELKEY:
1712                 case XKEY:
1713                         if(fromlib) fromlib= -1;
1714                         else node_delete(snode);
1715                         break;
1716                 }
1717         }
1718
1719         if(fromlib==-1)
1720                 error("Cannot edit Library Data");
1721         if(doredraw)
1722                 scrarea_queue_winredraw(sa);
1723         if(doredraw==2)
1724                 scrarea_queue_headredraw(sa);
1725 }
1726
1727