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