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