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