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