Preview Range Tweak:
[blender.git] / source / blender / editors / space_node / node_edit.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): David Millan Escriva, Juho Vepsäläinen, Nathan Letwory
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_brush_types.h"
39 #include "DNA_color_types.h"
40 #include "DNA_image_types.h"
41 #include "DNA_ipo_types.h"
42 #include "DNA_object_types.h"
43 #include "DNA_material_types.h"
44 #include "DNA_texture_types.h"
45 #include "DNA_node_types.h"
46 #include "DNA_space_types.h"
47 #include "DNA_screen_types.h"
48 #include "DNA_scene_types.h"
49 #include "DNA_userdef_types.h"
50
51 #include "BKE_context.h"
52 #include "BKE_colortools.h"
53 #include "BKE_global.h"
54 #include "BKE_image.h"
55 #include "BKE_library.h"
56 #include "BKE_main.h"
57 #include "BKE_node.h"
58 #include "BKE_material.h"
59 #include "BKE_paint.h"
60 #include "BKE_texture.h"
61 #include "BKE_report.h"
62 #include "BKE_scene.h"
63 #include "BKE_utildefines.h"
64
65 #include "BIF_gl.h"
66
67 #include "BLI_math.h"
68 #include "BLI_blenlib.h"
69 #include "BLI_storage_types.h"
70
71 #include "RE_pipeline.h"
72
73 #include "IMB_imbuf_types.h"
74
75 #include "ED_node.h"
76 #include "ED_render.h"
77 #include "ED_screen.h"
78 #include "ED_space_api.h"
79 #include "ED_transform.h"
80 #include "ED_types.h"
81
82 #include "RNA_access.h"
83 #include "RNA_define.h"
84
85 #include "WM_api.h"
86 #include "WM_types.h"
87
88 #include "UI_interface.h"
89 #include "UI_view2d.h"
90  
91 #include "node_intern.h"
92
93 #define SOCK_IN         1
94 #define SOCK_OUT        2
95
96 /* ***************** composite job manager ********************** */
97
98 typedef struct CompoJob {
99         Scene *scene;
100         bNodeTree *ntree;
101         bNodeTree *localtree;
102         short *stop;
103         short *do_update;
104 } CompoJob;
105
106 /* called by compo, only to check job 'stop' value */
107 static int compo_breakjob(void *cjv)
108 {
109         CompoJob *cj= cjv;
110         
111         return *(cj->stop);
112 }
113
114 /* called by compo, wmJob sends notifier */
115 static void compo_redrawjob(void *cjv, char *str)
116 {
117         CompoJob *cj= cjv;
118         
119         *(cj->do_update)= 1;
120 }
121
122 static void compo_freejob(void *cjv)
123 {
124         CompoJob *cj= cjv;
125
126         if(cj->localtree) {
127                 ntreeLocalMerge(cj->localtree, cj->ntree);
128         }
129         MEM_freeN(cj);
130 }
131
132 /* only now we copy the nodetree, so adding many jobs while
133    sliding buttons doesn't frustrate */
134 static void compo_initjob(void *cjv)
135 {
136         CompoJob *cj= cjv;
137
138         cj->localtree= ntreeLocalize(cj->ntree);
139 }
140
141 /* called before redraw notifiers, it moves finished previews over */
142 static void compo_updatejob(void *cjv)
143 {
144         CompoJob *cj= cjv;
145         
146         ntreeLocalSync(cj->localtree, cj->ntree);
147 }
148
149
150 /* only this runs inside thread */
151 static void compo_startjob(void *cjv, short *stop, short *do_update)
152 {
153         CompoJob *cj= cjv;
154         bNodeTree *ntree= cj->localtree;
155
156         if(cj->scene->use_nodes==0)
157                 return;
158         
159         cj->stop= stop;
160         cj->do_update= do_update;
161         
162         ntree->test_break= compo_breakjob;
163         ntree->tbh= cj;
164         ntree->stats_draw= compo_redrawjob;
165         ntree->sdh= cj;
166         
167         // XXX BIF_store_spare();
168         
169         ntreeCompositExecTree(ntree, &cj->scene->r, 1); /* 1 is do_previews */
170         
171         ntree->test_break= NULL;
172         ntree->stats_draw= NULL;
173
174 }
175
176 void snode_composite_job(const bContext *C, ScrArea *sa)
177 {
178         SpaceNode *snode= sa->spacedata.first;
179         wmJob *steve;
180         CompoJob *cj;
181
182         steve= WM_jobs_get(CTX_wm_manager(C), CTX_wm_window(C), sa, WM_JOB_EXCL_RENDER);
183         cj= MEM_callocN(sizeof(CompoJob), "compo job");
184         
185         /* customdata for preview thread */
186         cj->scene= CTX_data_scene(C);
187         cj->ntree= snode->nodetree;
188         
189         /* setup job */
190         WM_jobs_customdata(steve, cj, compo_freejob);
191         WM_jobs_timer(steve, 0.1, NC_SCENE, NC_SCENE|ND_COMPO_RESULT);
192         WM_jobs_callbacks(steve, compo_startjob, compo_initjob, compo_updatejob);
193         
194         WM_jobs_start(CTX_wm_manager(C), steve);
195         
196 }
197
198 /* ***************************************** */
199
200 /* also checks for edited groups */
201 bNode *editnode_get_active(bNodeTree *ntree)
202 {
203         bNode *node;
204         
205         /* check for edited group */
206         for(node= ntree->nodes.first; node; node= node->next)
207                 if(node->flag & NODE_GROUP_EDIT)
208                         break;
209         if(node)
210                 return nodeGetActive((bNodeTree *)node->id);
211         else
212                 return nodeGetActive(ntree);
213 }
214
215 void snode_handle_recalc(bContext *C, SpaceNode *snode)
216 {
217         if(snode->treetype==NTREE_SHADER)
218                 WM_event_add_notifier(C, NC_MATERIAL|ND_NODES, snode->id);
219         else if(snode->treetype==NTREE_COMPOSIT)
220                 WM_event_add_notifier(C, NC_SCENE|ND_NODES, snode->id);
221         else if(snode->treetype==NTREE_TEXTURE)
222                 WM_event_add_notifier(C, NC_TEXTURE|ND_NODES, snode->id);
223 }
224
225 bNode *node_tree_get_editgroup(bNodeTree *nodetree)
226 {
227         bNode *gnode;
228         
229         /* get the groupnode */
230         for(gnode= nodetree->nodes.first; gnode; gnode= gnode->next)
231                 if(gnode->flag & NODE_GROUP_EDIT)
232                         break;
233         return gnode;
234 }
235
236 /* assumes nothing being done in ntree yet, sets the default in/out node */
237 /* called from shading buttons or header */
238 void ED_node_shader_default(Material *ma)
239 {
240         bNode *in, *out;
241         bNodeSocket *fromsock, *tosock;
242         
243         /* but lets check it anyway */
244         if(ma->nodetree) {
245                 printf("error in shader initialize\n");
246                 return;
247         }
248         
249         ma->nodetree= ntreeAddTree(NTREE_SHADER);
250         
251         out= nodeAddNodeType(ma->nodetree, SH_NODE_OUTPUT, NULL, NULL);
252         out->locx= 300.0f; out->locy= 300.0f;
253         
254         in= nodeAddNodeType(ma->nodetree, SH_NODE_MATERIAL, NULL, NULL);
255         in->locx= 10.0f; in->locy= 300.0f;
256         nodeSetActive(ma->nodetree, in);
257         
258         /* only a link from color to color */
259         fromsock= in->outputs.first;
260         tosock= out->inputs.first;
261         nodeAddLink(ma->nodetree, in, fromsock, out, tosock);
262         
263         ntreeSolveOrder(ma->nodetree);  /* needed for pointers */
264 }
265
266 /* assumes nothing being done in ntree yet, sets the default in/out node */
267 /* called from shading buttons or header */
268 void ED_node_composit_default(Scene *sce)
269 {
270         bNode *in, *out;
271         bNodeSocket *fromsock, *tosock;
272         
273         /* but lets check it anyway */
274         if(sce->nodetree) {
275                 printf("error in composit initialize\n");
276                 return;
277         }
278         
279         sce->nodetree= ntreeAddTree(NTREE_COMPOSIT);
280         
281         out= nodeAddNodeType(sce->nodetree, CMP_NODE_COMPOSITE, NULL, NULL);
282         out->locx= 300.0f; out->locy= 400.0f;
283         out->id= &sce->id;
284         
285         in= nodeAddNodeType(sce->nodetree, CMP_NODE_R_LAYERS, NULL, NULL);
286         in->locx= 10.0f; in->locy= 400.0f;
287         in->id= &sce->id;
288         nodeSetActive(sce->nodetree, in);
289         
290         /* links from color to color */
291         fromsock= in->outputs.first;
292         tosock= out->inputs.first;
293         nodeAddLink(sce->nodetree, in, fromsock, out, tosock);
294         
295         ntreeSolveOrder(sce->nodetree); /* needed for pointers */
296         
297         // XXX ntreeCompositForceHidden(sce->nodetree);
298 }
299
300 /* assumes nothing being done in ntree yet, sets the default in/out node */
301 /* called from shading buttons or header */
302 void ED_node_texture_default(Tex *tx)
303 {
304         bNode *in, *out;
305         bNodeSocket *fromsock, *tosock;
306         
307         /* but lets check it anyway */
308         if(tx->nodetree) {
309                 printf("error in texture initialize\n");
310                 return;
311         }
312         
313         tx->nodetree= ntreeAddTree(NTREE_TEXTURE);
314         
315         out= nodeAddNodeType(tx->nodetree, TEX_NODE_OUTPUT, NULL, NULL);
316         out->locx= 300.0f; out->locy= 300.0f;
317         
318         in= nodeAddNodeType(tx->nodetree, TEX_NODE_CHECKER, NULL, NULL);
319         in->locx= 10.0f; in->locy= 300.0f;
320         nodeSetActive(tx->nodetree, in);
321         
322         fromsock= in->outputs.first;
323         tosock= out->inputs.first;
324         nodeAddLink(tx->nodetree, in, fromsock, out, tosock);
325         
326         ntreeSolveOrder(tx->nodetree);  /* needed for pointers */
327 }
328
329 void node_tree_from_ID(ID *id, bNodeTree **ntree, bNodeTree **edittree, int *treetype)
330 {
331         bNode *node= NULL;
332         short idtype= GS(id->name);
333
334         if(idtype == ID_MA) {
335                 *ntree= ((Material*)id)->nodetree;
336                 if(treetype) *treetype= NTREE_SHADER;
337         }
338         else if(idtype == ID_SCE) {
339                 *ntree= ((Scene*)id)->nodetree;
340                 if(treetype) *treetype= NTREE_COMPOSIT;
341         }
342         else if(idtype == ID_TE) {
343                 *ntree= ((Tex*)id)->nodetree;
344                 if(treetype) *treetype= NTREE_TEXTURE;
345         }
346
347         /* find editable group */
348         if(edittree) {
349                 if(*ntree)
350                         for(node= (*ntree)->nodes.first; node; node= node->next)
351                                 if(node->flag & NODE_GROUP_EDIT)
352                                         break;
353                 
354                 if(node && node->id)
355                         *edittree= (bNodeTree *)node->id;
356                 else
357                         *edittree= *ntree;
358         }
359 }
360
361 /* Here we set the active tree(s), even called for each redraw now, so keep it fast :) */
362 void snode_set_context(SpaceNode *snode, Scene *scene)
363 {
364         Object *ob= OBACT;
365         
366         snode->nodetree= NULL;
367         snode->edittree= NULL;
368         snode->id= snode->from= NULL;
369         
370         if(snode->treetype==NTREE_SHADER) {
371                 /* need active object, or we allow pinning... */
372                 if(ob) {
373                         Material *ma= give_current_material(ob, ob->actcol);
374                         if(ma) {
375                                 snode->from= &ob->id;
376                                 snode->id= &ma->id;
377                         }
378                 }
379         }
380         else if(snode->treetype==NTREE_COMPOSIT) {
381                 snode->from= NULL;
382                 snode->id= &scene->id;
383                 
384                 /* bit clumsy but reliable way to see if we draw first time */
385                 if(snode->nodetree==NULL)
386                         ntreeCompositForceHidden(scene->nodetree, scene);
387         }
388         else if(snode->treetype==NTREE_TEXTURE) {
389                 Tex *tx= NULL;
390
391                 if(snode->texfrom==SNODE_TEX_OBJECT) {
392                         if(ob) {
393                                 tx= give_current_object_texture(ob);
394
395                                 if(ob->type == OB_LAMP)
396                                         snode->from= (ID*)ob->data;
397                                 else
398                                         snode->from= (ID*)give_current_material(ob, ob->actcol);
399
400                                 /* from is not set fully for material nodes, should be ID + Node then */
401                         }
402                 }
403                 else if(snode->texfrom==SNODE_TEX_WORLD) {
404                         tx= give_current_world_texture(scene->world);
405                         snode->from= (ID *)scene->world;
406                 }
407                 else {
408                         Brush *brush= NULL;
409                         
410                         if(ob && (ob->mode & OB_MODE_SCULPT))
411                                 brush= paint_brush(&scene->toolsettings->sculpt->paint);
412                         else
413                                 brush= paint_brush(&scene->toolsettings->imapaint.paint);
414
415                         snode->from= (ID *)brush;
416                         tx= give_current_brush_texture(brush);
417                 }
418                 
419                 snode->id= &tx->id;
420         }
421
422         if(snode->id)
423                 node_tree_from_ID(snode->id, &snode->nodetree, &snode->edittree, NULL);
424 }
425
426 void node_set_active(SpaceNode *snode, bNode *node)
427 {
428         nodeSetActive(snode->edittree, node);
429         
430         if(node->type!=NODE_GROUP) {
431                 /* tree specific activate calls */
432                 if(snode->treetype==NTREE_SHADER) {
433                         // XXX
434 #if 0
435                         
436                         /* when we select a material, active texture is cleared, for buttons */
437                         if(node->id && GS(node->id->name)==ID_MA)
438                                 nodeClearActiveID(snode->edittree, ID_TE);
439                         if(node->id)
440                                 ; // XXX BIF_preview_changed(-1);       /* temp hack to force texture preview to update */
441                         
442                         // allqueue(REDRAWBUTSSHADING, 1);
443                         // allqueue(REDRAWIPO, 0);
444 #endif
445                 }
446                 else if(snode->treetype==NTREE_COMPOSIT) {
447                         Scene *scene= (Scene*)snode->id;
448
449                         /* make active viewer, currently only 1 supported... */
450                         if( ELEM(node->type, CMP_NODE_VIEWER, CMP_NODE_SPLITVIEWER)) {
451                                 bNode *tnode;
452                                 int was_output= (node->flag & NODE_DO_OUTPUT);
453
454                                 for(tnode= snode->edittree->nodes.first; tnode; tnode= tnode->next)
455                                         if( ELEM(tnode->type, CMP_NODE_VIEWER, CMP_NODE_SPLITVIEWER))
456                                                 tnode->flag &= ~NODE_DO_OUTPUT;
457                                 
458                                 node->flag |= NODE_DO_OUTPUT;
459                                 if(was_output==0) {
460                                         bNode *gnode;
461                                         
462                                         NodeTagChanged(snode->edittree, node);
463                                         
464                                         /* if inside group, tag entire group */
465                                         gnode= node_tree_get_editgroup(snode->nodetree);
466                                         if(gnode)
467                                                 NodeTagIDChanged(snode->nodetree, gnode->id);
468                                         
469                                         ED_node_changed_update(snode->id, node);
470                                 }
471                                 
472                                 /* addnode() doesnt link this yet... */
473                                 node->id= (ID *)BKE_image_verify_viewer(IMA_TYPE_COMPOSITE, "Viewer Node");
474                         }
475                         else if(node->type==CMP_NODE_R_LAYERS) {
476                                 if(node->id==NULL || node->id==(ID *)scene) {
477                                         scene->r.actlay= node->custom1;
478                                 }
479                         }
480                 }
481                 else if(snode->treetype==NTREE_TEXTURE) {
482                         // XXX
483 #if 0
484                         if(node->id)
485                                 ; // XXX BIF_preview_changed(-1);
486                         // allqueue(REDRAWBUTSSHADING, 1);
487                         // allqueue(REDRAWIPO, 0);
488 #endif
489                 }
490         }
491 }
492
493 /* when links in groups change, inputs/outputs change, nodes added/deleted... */
494 void node_tree_verify_groups(bNodeTree *nodetree)
495 {
496         bNode *gnode;
497         
498         gnode= node_tree_get_editgroup(nodetree);
499         
500         /* does all materials */
501         if(gnode)
502                 nodeVerifyGroup((bNodeTree *)gnode->id);
503         
504 }
505
506 /* ***************** Edit Group operator ************* */
507
508 void snode_make_group_editable(SpaceNode *snode, bNode *gnode)
509 {
510         bNode *node;
511         
512         /* make sure nothing has group editing on */
513         for(node= snode->nodetree->nodes.first; node; node= node->next)
514                 node->flag &= ~NODE_GROUP_EDIT;
515         
516         if(gnode==NULL) {
517                 /* with NULL argument we do a toggle */
518                 if(snode->edittree==snode->nodetree)
519                         gnode= nodeGetActive(snode->nodetree);
520         }
521         
522         if(gnode && gnode->type==NODE_GROUP && gnode->id) {
523                 if(gnode->id->lib)
524                         ntreeMakeLocal((bNodeTree *)gnode->id);
525
526                 gnode->flag |= NODE_GROUP_EDIT;
527                 snode->edittree= (bNodeTree *)gnode->id;
528                 
529                 /* deselect all other nodes, so we can also do grabbing of entire subtree */
530                 for(node= snode->nodetree->nodes.first; node; node= node->next)
531                         node->flag &= ~SELECT;
532                 gnode->flag |= SELECT;
533                 
534         }
535         else 
536                 snode->edittree= snode->nodetree;
537         
538         ntreeSolveOrder(snode->nodetree);
539 }
540
541 static int node_group_edit_exec(bContext *C, wmOperator *op)
542 {
543         SpaceNode *snode = CTX_wm_space_node(C);
544         bNode *gnode;
545
546         gnode= nodeGetActive(snode->edittree);
547         snode_make_group_editable(snode, gnode);
548
549         WM_event_add_notifier(C, NC_SCENE|ND_NODES, NULL);
550
551         return OPERATOR_FINISHED;
552 }
553
554 static int node_group_edit_invoke(bContext *C, wmOperator *op, wmEvent *event)
555 {
556         SpaceNode *snode = CTX_wm_space_node(C);
557         bNode *gnode;
558
559         gnode= nodeGetActive(snode->edittree);
560         if(gnode && gnode->type==NODE_GROUP && gnode->id && gnode->id->lib) {
561                 uiPupMenuOkee(C, op->type->idname, "Make group local?");
562                 return OPERATOR_CANCELLED;
563         }
564
565         return node_group_edit_exec(C, op);
566 }
567
568 void NODE_OT_group_edit(wmOperatorType *ot)
569 {
570         /* identifiers */
571         ot->name = "Edit Group";
572         ot->description = "Edit node group.";
573         ot->idname = "NODE_OT_group_edit";
574         
575         /* api callbacks */
576         ot->invoke = node_group_edit_invoke;
577         ot->exec = node_group_edit_exec;
578         ot->poll = ED_operator_node_active;
579         
580         /* flags */
581         ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
582 }
583
584 /* ******************** Ungroup operator ********************** */
585
586 static int node_group_ungroup_exec(bContext *C, wmOperator *op)
587 {
588         SpaceNode *snode = CTX_wm_space_node(C);
589         bNode *gnode;
590
591         /* are we inside of a group? */
592         gnode= node_tree_get_editgroup(snode->nodetree);
593         if(gnode)
594                 snode_make_group_editable(snode, NULL);
595         
596         gnode= nodeGetActive(snode->edittree);
597         if(gnode==NULL)
598                 return OPERATOR_CANCELLED;
599         
600         if(gnode->type!=NODE_GROUP) {
601                 BKE_report(op->reports, RPT_ERROR, "Not a group");
602                 return OPERATOR_CANCELLED;
603         }
604         else if(!nodeGroupUnGroup(snode->edittree, gnode)) {
605                 BKE_report(op->reports, RPT_ERROR, "Can't ungroup");
606                 return OPERATOR_CANCELLED;
607         }
608
609         WM_event_add_notifier(C, NC_SCENE|ND_NODES, NULL);
610
611         return OPERATOR_FINISHED;
612 }
613
614 void NODE_OT_group_ungroup(wmOperatorType *ot)
615 {
616         /* identifiers */
617         ot->name = "Ungroup";
618         ot->description = "Ungroup selected nodes.";
619         ot->idname = "NODE_OT_group_ungroup";
620         
621         /* api callbacks */
622         ot->exec = node_group_ungroup_exec;
623         ot->poll = ED_operator_node_active;
624         
625         /* flags */
626         ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
627 }
628
629 /* ************************** Node generic ************** */
630
631 /* allows to walk the list in order of visibility */
632 bNode *next_node(bNodeTree *ntree)
633 {
634         static bNode *current=NULL, *last= NULL;
635         
636         if(ntree) {
637                 /* set current to the first selected node */
638                 for(current= ntree->nodes.last; current; current= current->prev)
639                         if(current->flag & NODE_SELECT)
640                                 break;
641                 
642                 /* set last to the first unselected node */
643                 for(last= ntree->nodes.last; last; last= last->prev)
644                         if((last->flag & NODE_SELECT)==0)
645                                 break;
646                 
647                 if(current==NULL)
648                         current= last;
649                 
650                 return NULL;
651         }
652         /* no nodes, or we are ready */
653         if(current==NULL)
654                 return NULL;
655         
656         /* now we walk the list backwards, but we always return current */
657         if(current->flag & NODE_SELECT) {
658                 bNode *node= current;
659                 
660                 /* find previous selected */
661                 current= current->prev;
662                 while(current && (current->flag & NODE_SELECT)==0)
663                         current= current->prev;
664                 
665                 /* find first unselected */
666                 if(current==NULL)
667                         current= last;
668                 
669                 return node;
670         }
671         else {
672                 bNode *node= current;
673                 
674                 /* find previous unselected */
675                 current= current->prev;
676                 while(current && (current->flag & NODE_SELECT))
677                         current= current->prev;
678                 
679                 return node;
680         }
681         
682         return NULL;
683 }
684
685 /* is rct in visible part of node? */
686 static bNode *visible_node(SpaceNode *snode, rctf *rct)
687 {
688         bNode *tnode;
689         
690         for(next_node(snode->edittree); (tnode=next_node(NULL));) {
691                 if(BLI_isect_rctf(&tnode->totr, rct, NULL))
692                         break;
693         }
694         return tnode;
695 }
696
697 #if 0
698 static void snode_bg_viewmove(SpaceNode *snode)
699 {
700         ScrArea *sa;
701         Image *ima;
702         ImBuf *ibuf;
703         Window *win;
704         short mval[2], mvalo[2];
705         short rectx, recty, xmin, xmax, ymin, ymax, pad;
706         int oldcursor;
707         
708         ima= BKE_image_verify_viewer(IMA_TYPE_COMPOSITE, "Viewer Node");
709         ibuf= BKE_image_get_ibuf(ima, NULL);
710         
711         sa = snode->area;
712         
713         if(ibuf) {
714                 rectx = ibuf->x;
715                 recty = ibuf->y;
716         } else {
717                 rectx = recty = 1;
718         }
719         
720         pad = 10;
721         xmin = -(sa->winx/2) - rectx/2 + pad;
722         xmax = sa->winx/2 + rectx/2 - pad;
723         ymin = -(sa->winy/2) - recty/2 + pad;
724         ymax = sa->winy/2 + recty/2 - pad;
725         
726         getmouseco_sc(mvalo);
727         
728         /* store the old cursor to temporarily change it */
729         oldcursor=get_cursor();
730         win=winlay_get_active_window();
731         
732         SetBlenderCursor(BC_NSEW_SCROLLCURSOR);
733         
734         while(get_mbut()&(L_MOUSE|M_MOUSE)) {
735                 
736                 getmouseco_sc(mval);
737                 
738                 if(mvalo[0]!=mval[0] || mvalo[1]!=mval[1]) {
739                         
740                         snode->xof -= (mvalo[0]-mval[0]);
741                         snode->yof -= (mvalo[1]-mval[1]);
742                         
743                         /* prevent dragging image outside of the window and losing it! */
744                         CLAMP(snode->xof, xmin, xmax);
745                         CLAMP(snode->yof, ymin, ymax);
746                         
747                         mvalo[0]= mval[0];
748                         mvalo[1]= mval[1];
749                         
750                         scrarea_do_windraw(curarea);
751                         screen_swapbuffers();
752                 }
753                 else BIF_wait_for_statechange();
754         }
755         
756         window_set_cursor(win, oldcursor);
757 }
758 #endif
759
760 /* ********************** size widget operator ******************** */
761
762 typedef struct NodeSizeWidget {
763         float mxstart;
764         float oldwidth;
765 } NodeSizeWidget;
766
767 static int node_resize_modal(bContext *C, wmOperator *op, wmEvent *event)
768 {
769         SpaceNode *snode= CTX_wm_space_node(C);
770         ARegion *ar= CTX_wm_region(C);
771         bNode *node= editnode_get_active(snode->edittree);
772         NodeSizeWidget *nsw= op->customdata;
773         float mx, my;
774         
775         switch (event->type) {
776                 case MOUSEMOVE:
777                         
778                         UI_view2d_region_to_view(&ar->v2d, event->x - ar->winrct.xmin, event->y - ar->winrct.ymin, 
779                                                                          &mx, &my);
780                         
781                         if (node) {
782                                 if(node->flag & NODE_HIDDEN) {
783                                         node->miniwidth= nsw->oldwidth + mx - nsw->mxstart;
784                                         CLAMP(node->miniwidth, 0.0f, 100.0f);
785                                 }
786                                 else {
787                                         node->width= nsw->oldwidth + mx - nsw->mxstart;
788                                         CLAMP(node->width, node->typeinfo->minwidth, node->typeinfo->maxwidth);
789                                 }
790                         }
791                                 
792                         ED_region_tag_redraw(ar);
793
794                         break;
795                         
796                 case LEFTMOUSE:
797                 case MIDDLEMOUSE:
798                 case RIGHTMOUSE:
799                         
800                         MEM_freeN(nsw);
801                         op->customdata= NULL;
802                         
803                         return OPERATOR_FINISHED;
804         }
805         
806         return OPERATOR_RUNNING_MODAL;
807 }
808
809 static int node_resize_invoke(bContext *C, wmOperator *op, wmEvent *event)
810 {
811         SpaceNode *snode= CTX_wm_space_node(C);
812         ARegion *ar= CTX_wm_region(C);
813         bNode *node= editnode_get_active(snode->edittree);
814         
815         if(node) {
816                 rctf totr;
817                 
818                 /* convert mouse coordinates to v2d space */
819                 UI_view2d_region_to_view(&ar->v2d, event->x - ar->winrct.xmin, event->y - ar->winrct.ymin, 
820                                                                  &snode->mx, &snode->my);
821                 
822                 /* rect we're interested in is just the bottom right corner */
823                 totr= node->totr;
824                 totr.xmin= totr.xmax-10.0f;
825                 totr.ymax= totr.ymin+10.0f;
826                 
827                 if(BLI_in_rctf(&totr, snode->mx, snode->my)) {
828                         NodeSizeWidget *nsw= MEM_callocN(sizeof(NodeSizeWidget), "size widget op data");
829                         
830                         op->customdata= nsw;
831                         nsw->mxstart= snode->mx;
832                         
833                         /* store old */
834                         if(node->flag & NODE_HIDDEN)
835                                 nsw->oldwidth= node->miniwidth;
836                         else
837                                 nsw->oldwidth= node->width;
838                         
839                         /* add modal handler */
840                         WM_event_add_modal_handler(C, op);
841
842                         return OPERATOR_RUNNING_MODAL;
843                 }
844         }
845         return OPERATOR_CANCELLED|OPERATOR_PASS_THROUGH;
846 }
847
848 void NODE_OT_resize(wmOperatorType *ot)
849 {
850         /* identifiers */
851         ot->name= "Resize Node";
852         ot->idname= "NODE_OT_resize";
853         
854         /* api callbacks */
855         ot->invoke= node_resize_invoke;
856         ot->modal= node_resize_modal;
857         ot->poll= ED_operator_node_active;
858         
859         /* flags */
860         ot->flag= OPTYPE_BLOCKING;
861 }
862
863 /* ********************** select ******************** */
864
865
866 /* no undo here! */
867 void node_deselectall(SpaceNode *snode)
868 {
869         bNode *node;
870         
871         for(node= snode->edittree->nodes.first; node; node= node->next)
872                 node->flag &= ~SELECT;
873 }
874
875 int node_has_hidden_sockets(bNode *node)
876 {
877         bNodeSocket *sock;
878         
879         for(sock= node->inputs.first; sock; sock= sock->next)
880                 if(sock->flag & SOCK_HIDDEN)
881                         return 1;
882         for(sock= node->outputs.first; sock; sock= sock->next)
883                 if(sock->flag & SOCK_HIDDEN)
884                         return 1;
885         return 0;
886 }
887
888 static void node_link_viewer(SpaceNode *snode, bNode *tonode)
889 {
890         bNode *node;
891
892         /* context check */
893         if(tonode==NULL || tonode->outputs.first==NULL)
894                 return;
895         if( ELEM(tonode->type, CMP_NODE_VIEWER, CMP_NODE_SPLITVIEWER)) 
896                 return;
897         
898         /* get viewer */
899         for(node= snode->edittree->nodes.first; node; node= node->next)
900                 if( ELEM(node->type, CMP_NODE_VIEWER, CMP_NODE_SPLITVIEWER)) 
901                         if(node->flag & NODE_DO_OUTPUT)
902                                 break;
903                 
904         if(node) {
905                 bNodeLink *link;
906                 
907                 /* get link to viewer */
908                 for(link= snode->edittree->links.first; link; link= link->next)
909                         if(link->tonode==node)
910                                 break;
911
912                 if(link) {
913                         link->fromnode= tonode;
914                         link->fromsock= tonode->outputs.first;
915                         NodeTagChanged(snode->edittree, node);
916                 }
917         }
918 }
919
920
921 void node_active_link_viewer(SpaceNode *snode)
922 {
923         bNode *node= editnode_get_active(snode->edittree);
924         if(node)
925                 node_link_viewer(snode, node);
926 }
927
928 /* return 0, nothing done */
929 /*static*/ int node_mouse_groupheader(SpaceNode *snode)
930 {
931         bNode *gnode;
932         float mx=0, my=0;
933 // XXX  short mval[2];
934         
935         gnode= node_tree_get_editgroup(snode->nodetree);
936         if(gnode==NULL) return 0;
937         
938 // XXX  getmouseco_areawin(mval);
939 // XXX  areamouseco_to_ipoco(G.v2d, mval, &mx, &my);
940         
941         /* click in header or outside? */
942         if(BLI_in_rctf(&gnode->totr, mx, my)==0) {
943                 rctf rect= gnode->totr;
944                 
945                 rect.ymax += NODE_DY;
946                 if(BLI_in_rctf(&rect, mx, my)==0)
947                         snode_make_group_editable(snode, NULL); /* toggles, so exits editmode */
948 //              else
949 // XXX                  transform_nodes(snode->nodetree, 'g', "Move group");
950                 
951                 return 1;
952         }
953         return 0;
954 }
955
956 /* checks snode->mouse position, and returns found node/socket */
957 /* type is SOCK_IN and/or SOCK_OUT */
958 static int find_indicated_socket(SpaceNode *snode, bNode **nodep, bNodeSocket **sockp, int in_out)
959 {
960         bNode *node;
961         bNodeSocket *sock;
962         rctf rect;
963         
964         /* check if we click in a socket */
965         for(node= snode->edittree->nodes.first; node; node= node->next) {
966                 
967                 rect.xmin = snode->mx - (NODE_SOCKSIZE+4);
968                 rect.ymin = snode->my - (NODE_SOCKSIZE+4);
969                 rect.xmax = snode->mx + (NODE_SOCKSIZE+4);
970                 rect.ymax = snode->my + (NODE_SOCKSIZE+4);
971                 
972                 if (!(node->flag & NODE_HIDDEN)) {
973                         /* extra padding inside and out - allow dragging on the text areas too */
974                         if (in_out == SOCK_IN) {
975                                 rect.xmax += NODE_SOCKSIZE;
976                                 rect.xmin -= NODE_SOCKSIZE*4;
977                         } else if (in_out == SOCK_OUT) {
978                                 rect.xmax += NODE_SOCKSIZE*4;
979                                 rect.xmin -= NODE_SOCKSIZE;
980                         }
981                 }
982                 
983                 if(in_out & SOCK_IN) {
984                         for(sock= node->inputs.first; sock; sock= sock->next) {
985                                 if(!(sock->flag & (SOCK_HIDDEN|SOCK_UNAVAIL))) {
986                                         if(BLI_in_rctf(&rect, sock->locx, sock->locy)) {
987                                                 if(node == visible_node(snode, &rect)) {
988                                                         *nodep= node;
989                                                         *sockp= sock;
990                                                         return 1;
991                                                 }
992                                         }
993                                 }
994                         }
995                 }
996                 if(in_out & SOCK_OUT) {
997                         for(sock= node->outputs.first; sock; sock= sock->next) {
998                                 if(!(sock->flag & (SOCK_HIDDEN|SOCK_UNAVAIL))) {
999                                         if(BLI_in_rctf(&rect, sock->locx, sock->locy)) {
1000                                                 if(node == visible_node(snode, &rect)) {
1001                                                         *nodep= node;
1002                                                         *sockp= sock;
1003                                                         return 1;
1004                                                 }
1005                                         }
1006                                 }
1007                         }
1008                 }
1009         }
1010         return 0;
1011 }
1012
1013 static int node_socket_hilights(SpaceNode *snode, int in_out)
1014 {
1015         bNode *node;
1016         bNodeSocket *sock, *tsock, *socksel= NULL;
1017         short redraw= 0;
1018         
1019         if(snode->edittree==NULL) return 0;
1020         
1021         /* deselect sockets */
1022         for(node= snode->edittree->nodes.first; node; node= node->next) {
1023                 for(sock= node->inputs.first; sock; sock= sock->next) {
1024                         if(sock->flag & SELECT) {
1025                                 sock->flag &= ~SELECT;
1026                                 redraw++;
1027                                 socksel= sock;
1028                         }
1029                 }
1030                 for(sock= node->outputs.first; sock; sock= sock->next) {
1031                         if(sock->flag & SELECT) {
1032                                 sock->flag &= ~SELECT;
1033                                 redraw++;
1034                                 socksel= sock;
1035                         }
1036                 }
1037         }
1038         
1039         // XXX mousepos should be set here!
1040         
1041         if(find_indicated_socket(snode, &node, &tsock, in_out)) {
1042                 tsock->flag |= SELECT;
1043                 if(redraw==1 && tsock==socksel) redraw= 0;
1044                 else redraw= 1;
1045         }
1046         
1047         return redraw;
1048 }
1049
1050 /* ****************** Add *********************** */
1051
1052
1053 typedef struct bNodeListItem {
1054         struct bNodeListItem *next, *prev;
1055         struct bNode *node;     
1056 } bNodeListItem;
1057
1058 int sort_nodes_locx(void *a, void *b)
1059 {
1060         bNodeListItem *nli1 = (bNodeListItem *)a;
1061         bNodeListItem *nli2 = (bNodeListItem *)b;
1062         bNode *node1 = nli1->node;
1063         bNode *node2 = nli2->node;
1064         
1065         if (node1->locx > node2->locx)
1066                 return 1;
1067         else 
1068                 return 0;
1069 }
1070
1071 static int socket_is_available(bNodeTree *ntree, bNodeSocket *sock, int allow_used)
1072 {
1073         if (sock->flag & (SOCK_HIDDEN|SOCK_UNAVAIL))
1074                 return 0;
1075         
1076         if (!allow_used) {
1077                 if (nodeCountSocketLinks(ntree, sock) > 0)
1078                         return 0;
1079         }
1080         return 1;
1081 }
1082
1083 static bNodeSocket *best_socket_output(bNodeTree *ntree, bNode *node, bNodeSocket *sock_target, int allow_multiple)
1084 {
1085         bNodeSocket *sock;
1086         
1087         /* first try to find a socket with a matching name */
1088         for (sock=node->outputs.first; sock; sock=sock->next) {
1089
1090                 if (!socket_is_available(ntree, sock, allow_multiple))
1091                         continue;
1092
1093                 /* check for same types */
1094                 if (sock->type == sock_target->type) {
1095                         if (strcmp(sock->name, sock_target->name)==0)
1096                                 return sock;
1097                 }
1098         }
1099         
1100         /* otherwise settle for the first available socket of the right type */
1101         for (sock=node->outputs.first; sock; sock=sock->next) {
1102
1103                 if (!socket_is_available(ntree, sock, allow_multiple))
1104                         continue;
1105                 
1106                 /* check for same types */
1107                 if (sock->type == sock_target->type) {
1108                         return sock;
1109                 }
1110         }
1111         
1112         return NULL;
1113 }
1114
1115 /* this is a bit complicated, but designed to prioritise finding 
1116  * sockets of higher types, such as image, first */
1117 static bNodeSocket *best_socket_input(bNodeTree *ntree, bNode *node, int num, int replace)
1118 {
1119         bNodeSocket *sock;
1120         int socktype, maxtype=0;
1121         int a = 0;
1122         
1123         for (sock=node->inputs.first; sock; sock=sock->next) {
1124                 maxtype = MAX2(sock->type, maxtype);
1125         }
1126         
1127         /* find sockets of higher 'types' first (i.e. image) */
1128         for (socktype=maxtype; socktype >= 0; socktype--) {
1129                 for (sock=node->inputs.first; sock; sock=sock->next) {
1130                         
1131                         if (!socket_is_available(ntree, sock, replace)) {
1132                                 a++;
1133                                 continue;
1134                         }
1135                                 
1136                         if (sock->type == socktype) {
1137                                 /* increment to make sure we don't keep finding 
1138                                  * the same socket on every attempt running this function */
1139                                 a++;
1140                                 if (a > num)
1141                                         return sock;
1142                         }
1143                 }
1144         }
1145         
1146         return NULL;
1147 }
1148
1149 void snode_autoconnect(SpaceNode *snode, int allow_multiple, int replace)
1150 {
1151         ListBase *nodelist = MEM_callocN(sizeof(ListBase), "items_list");
1152         bNodeListItem *nli;
1153         bNode *node;
1154         int i;
1155         
1156         for(node= snode->edittree->nodes.first; node; node= node->next) {
1157                 if(node->flag & NODE_SELECT) {
1158                         nli = MEM_mallocN(sizeof(bNodeListItem), "temporary node list item");
1159                         nli->node = node;
1160                         BLI_addtail(nodelist, nli);
1161                 }
1162         }
1163         
1164         /* sort nodes left to right */
1165         BLI_sortlist(nodelist, sort_nodes_locx);
1166         
1167         for (nli=nodelist->first; nli; nli=nli->next) {
1168                 bNode *node_fr, *node_to;
1169                 bNodeSocket *sock_fr, *sock_to;
1170                 
1171                 if (nli->next == NULL) break;
1172                 
1173                 node_fr = nli->node;
1174                 node_to = nli->next->node;
1175                 
1176                 /* check over input sockets first */
1177                 for (i=0; i<BLI_countlist(&node_to->inputs); i++) {
1178                         
1179                         /* find the best guess input socket */
1180                         sock_to = best_socket_input(snode->edittree, node_to, i, replace);
1181                         if (!sock_to) continue;
1182                         
1183                         /* check for an appropriate output socket to connect from */
1184                         sock_fr = best_socket_output(snode->edittree, node_fr, sock_to, allow_multiple);
1185                         if (!sock_fr) continue;
1186                         
1187                         /* then we can connect */
1188                         if (replace)
1189                                 nodeRemSocketLinks(snode->edittree, sock_to);
1190                         nodeAddLink(snode->edittree, node_fr, sock_fr, node_to, sock_to);
1191                         NodeTagChanged(snode->edittree, node_to);
1192                         break;
1193                 }
1194         }
1195         
1196         ntreeSolveOrder(snode->edittree);
1197         
1198         BLI_freelistN(nodelist);
1199         MEM_freeN(nodelist);
1200 }
1201
1202 /* can be called from menus too, but they should do own undopush and redraws */
1203 bNode *node_add_node(SpaceNode *snode, Scene *scene, int type, float locx, float locy)
1204 {
1205         bNode *node= NULL, *gnode;
1206         
1207         node_deselectall(snode);
1208         
1209         if(type>=NODE_DYNAMIC_MENU) {
1210                 node= nodeAddNodeType(snode->edittree, type, NULL, NULL);
1211         }
1212         else if(type>=NODE_GROUP_MENU) {
1213                 if(snode->edittree!=snode->nodetree) {
1214                         // XXX error("Can not add a Group in a Group");
1215                         return NULL;
1216                 }
1217                 else {
1218                         bNodeTree *ngroup= BLI_findlink(&G.main->nodetree, type-NODE_GROUP_MENU);
1219                         if(ngroup)
1220                                 node= nodeAddNodeType(snode->edittree, NODE_GROUP, ngroup, NULL);
1221                 }
1222         }
1223         else
1224                 node= nodeAddNodeType(snode->edittree, type, NULL, NULL);
1225         
1226         /* generics */
1227         if(node) {
1228                 node->locx= locx;
1229                 node->locy= locy + 60.0f;               // arbitrary.. so its visible
1230                 node->flag |= SELECT;
1231                 
1232                 gnode= node_tree_get_editgroup(snode->nodetree);
1233                 if(gnode) {
1234                         node->locx -= gnode->locx;
1235                         node->locy -= gnode->locy;
1236                 }
1237
1238                 node_tree_verify_groups(snode->nodetree);
1239                 node_set_active(snode, node);
1240                 
1241                 if(snode->nodetree->type==NTREE_COMPOSIT) {
1242                         if(ELEM4(node->type, CMP_NODE_R_LAYERS, CMP_NODE_COMPOSITE, CMP_NODE_DEFOCUS, CMP_NODE_OUTPUT_FILE))
1243                                 node->id = &scene->id;
1244                         
1245                         ntreeCompositForceHidden(snode->edittree, scene);
1246                 }
1247                         
1248                 if(node->id)
1249                         id_us_plus(node->id);
1250                         
1251                 NodeTagChanged(snode->edittree, node);
1252         }
1253         
1254         if(snode->nodetree->type==NTREE_TEXTURE) {
1255                 ntreeTexCheckCyclics(snode->edittree);
1256         }
1257         
1258         return node;
1259 }
1260
1261 /* ****************** Duplicate *********************** */
1262
1263 static int node_duplicate_exec(bContext *C, wmOperator *op)
1264 {
1265         SpaceNode *snode= CTX_wm_space_node(C);
1266         
1267         ntreeCopyTree(snode->edittree, 1);      /* 1 == internally selected nodes */
1268         
1269         ntreeSolveOrder(snode->edittree);
1270         node_tree_verify_groups(snode->nodetree);
1271         snode_handle_recalc(C, snode);
1272
1273         return OPERATOR_FINISHED;
1274 }
1275
1276 void NODE_OT_duplicate(wmOperatorType *ot)
1277 {
1278         /* identifiers */
1279         ot->name= "Duplicate Nodes";
1280         ot->description = "Duplicate the nodes.";
1281         ot->idname= "NODE_OT_duplicate";
1282         
1283         /* api callbacks */
1284         ot->exec= node_duplicate_exec;
1285         ot->poll= ED_operator_node_active;
1286         
1287         /* flags */
1288         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
1289 }
1290
1291 /* *************************** add link op ******************** */
1292
1293 /* temp data to pass on to modal */
1294 typedef struct NodeLinkDrag
1295 {
1296         bNode *node;
1297         bNodeSocket *sock;
1298         bNodeLink *link;
1299         int in_out;
1300 } NodeLinkDrag;
1301
1302 static void node_remove_extra_links(SpaceNode *snode, bNodeSocket *tsock, bNodeLink *link)
1303 {
1304         bNodeLink *tlink;
1305         bNodeSocket *sock;
1306         
1307         if(tsock && nodeCountSocketLinks(snode->edittree, link->tosock) > tsock->limit) {
1308                 
1309                 for(tlink= snode->edittree->links.first; tlink; tlink= tlink->next) {
1310                         if(link!=tlink && tlink->tosock==link->tosock)
1311                                 break;
1312                 }
1313                 if(tlink) {
1314                         /* is there a free input socket with same type? */
1315                         for(sock= tlink->tonode->inputs.first; sock; sock= sock->next) {
1316                                 if(sock->type==tlink->fromsock->type)
1317                                         if(nodeCountSocketLinks(snode->edittree, sock) < sock->limit)
1318                                                 break;
1319                         }
1320                         if(sock) {
1321                                 tlink->tosock= sock;
1322                                 sock->flag &= ~SOCK_HIDDEN;
1323                         }
1324                         else {
1325                                 nodeRemLink(snode->edittree, tlink);
1326                         }
1327                 }
1328         }
1329 }
1330
1331 /* loop that adds a nodelink, called by function below  */
1332 /* in_out = starting socket */
1333 static int node_link_modal(bContext *C, wmOperator *op, wmEvent *event)
1334 {
1335         SpaceNode *snode= CTX_wm_space_node(C);
1336         ARegion *ar= CTX_wm_region(C);
1337         NodeLinkDrag *nldrag= op->customdata;
1338         bNode *tnode, *node;
1339         bNodeSocket *tsock= NULL, *sock;
1340         bNodeLink *link;
1341         int in_out;
1342
1343         in_out= nldrag->in_out;
1344         node= nldrag->node;
1345         sock= nldrag->sock;
1346         link= nldrag->link;
1347         
1348         UI_view2d_region_to_view(&ar->v2d, event->x - ar->winrct.xmin, event->y - ar->winrct.ymin, 
1349                                                          &snode->mx, &snode->my);
1350
1351         switch (event->type) {
1352                 case MOUSEMOVE:
1353                         
1354                         if(in_out==SOCK_OUT) {
1355                                 if(find_indicated_socket(snode, &tnode, &tsock, SOCK_IN)) {
1356                                         if(nodeFindLink(snode->edittree, sock, tsock)==NULL) {
1357                                                 if(tnode!=node  && link->tonode!=tnode && link->tosock!= tsock) {
1358                                                         link->tonode= tnode;
1359                                                         link->tosock= tsock;
1360                                                         ntreeSolveOrder(snode->edittree);       /* for interactive red line warning */
1361                                                 }
1362                                         }
1363                                 }
1364                                 else {
1365                                         link->tonode= NULL;
1366                                         link->tosock= NULL;
1367                                 }
1368                         }
1369                         else {
1370                                 if(find_indicated_socket(snode, &tnode, &tsock, SOCK_OUT)) {
1371                                         if(nodeFindLink(snode->edittree, sock, tsock)==NULL) {
1372                                                 if(nodeCountSocketLinks(snode->edittree, tsock) < tsock->limit) {
1373                                                         if(tnode!=node && link->fromnode!=tnode && link->fromsock!= tsock) {
1374                                                                 link->fromnode= tnode;
1375                                                                 link->fromsock= tsock;
1376                                                                 ntreeSolveOrder(snode->edittree);       /* for interactive red line warning */
1377                                                         }
1378                                                 }
1379                                         }
1380                                 }
1381                                 else {
1382                                         link->fromnode= NULL;
1383                                         link->fromsock= NULL;
1384                                 }
1385                         }
1386                         /* hilight target sockets only */
1387                         node_socket_hilights(snode, in_out==SOCK_OUT?SOCK_IN:SOCK_OUT);
1388                         ED_region_tag_redraw(ar);
1389                         break;
1390                         
1391                 case LEFTMOUSE:
1392                 case RIGHTMOUSE:
1393                 case MIDDLEMOUSE:
1394         
1395                         /* remove link? */
1396                         if(link->tonode==NULL || link->fromnode==NULL) {
1397                                 nodeRemLink(snode->edittree, link);
1398                         }
1399                         else {
1400                                 /* send changed events for original tonode and new */
1401                                 if(link->tonode) 
1402                                         NodeTagChanged(snode->edittree, link->tonode);
1403                                 
1404                                 /* we might need to remove a link */
1405                                 if(in_out==SOCK_OUT) node_remove_extra_links(snode, link->tosock, link);
1406                         }
1407                         
1408                         ntreeSolveOrder(snode->edittree);
1409                         node_tree_verify_groups(snode->nodetree);
1410                         snode_handle_recalc(C, snode);
1411                         
1412                         MEM_freeN(op->customdata);
1413                         op->customdata= NULL;
1414                         
1415                         return OPERATOR_FINISHED;
1416         }
1417         
1418         return OPERATOR_RUNNING_MODAL;
1419 }
1420
1421 /* return 1 when socket clicked */
1422 static int node_link_init(SpaceNode *snode, NodeLinkDrag *nldrag)
1423 {
1424         bNodeLink *link;
1425         
1426         /* output indicated? */
1427         if(find_indicated_socket(snode, &nldrag->node, &nldrag->sock, SOCK_OUT)) {
1428                 if(nodeCountSocketLinks(snode->edittree, nldrag->sock) < nldrag->sock->limit)
1429                         return SOCK_OUT;
1430                 else {
1431                         /* find if we break a link */
1432                         for(link= snode->edittree->links.first; link; link= link->next) {
1433                                 if(link->fromsock==nldrag->sock)
1434                                         break;
1435                         }
1436                         if(link) {
1437                                 nldrag->node= link->tonode;
1438                                 nldrag->sock= link->tosock;
1439                                 nodeRemLink(snode->edittree, link);
1440                                 return SOCK_IN;
1441                         }
1442                 }
1443         }
1444         /* or an input? */
1445         else if(find_indicated_socket(snode, &nldrag->node, &nldrag->sock, SOCK_IN)) {
1446                 if(nodeCountSocketLinks(snode->edittree, nldrag->sock) < nldrag->sock->limit)
1447                         return SOCK_IN;
1448                 else {
1449                         /* find if we break a link */
1450                         for(link= snode->edittree->links.first; link; link= link->next) {
1451                                 if(link->tosock==nldrag->sock)
1452                                         break;
1453                         }
1454                         if(link) {
1455                                 /* send changed event to original tonode */
1456                                 if(link->tonode) 
1457                                         NodeTagChanged(snode->edittree, link->tonode);
1458                                 
1459                                 nldrag->node= link->fromnode;
1460                                 nldrag->sock= link->fromsock;
1461                                 nodeRemLink(snode->edittree, link);
1462                                 return SOCK_OUT;
1463                         }
1464                 }
1465         }
1466         
1467         return 0;
1468 }
1469
1470 static int node_link_invoke(bContext *C, wmOperator *op, wmEvent *event)
1471 {
1472         SpaceNode *snode= CTX_wm_space_node(C);
1473         ARegion *ar= CTX_wm_region(C);
1474         NodeLinkDrag *nldrag= MEM_callocN(sizeof(NodeLinkDrag), "drag link op customdata");
1475         
1476         UI_view2d_region_to_view(&ar->v2d, event->x - ar->winrct.xmin, event->y - ar->winrct.ymin, 
1477                                                          &snode->mx, &snode->my);
1478
1479         nldrag->in_out= node_link_init(snode, nldrag);
1480                 
1481         if(nldrag->in_out) {
1482                 op->customdata= nldrag;
1483                 
1484                 /* we make a temporal link */
1485                 if(nldrag->in_out==SOCK_OUT)
1486                         nldrag->link= nodeAddLink(snode->edittree, nldrag->node, nldrag->sock, NULL, NULL);
1487                 else
1488                         nldrag->link= nodeAddLink(snode->edittree, NULL, NULL, nldrag->node, nldrag->sock);
1489                 
1490                 /* add modal handler */
1491                 WM_event_add_modal_handler(C, op);
1492                 
1493                 return OPERATOR_RUNNING_MODAL;
1494         }
1495         else {
1496                 MEM_freeN(nldrag);
1497                 return OPERATOR_CANCELLED|OPERATOR_PASS_THROUGH;
1498         }
1499 }
1500
1501 void NODE_OT_link(wmOperatorType *ot)
1502 {
1503         /* identifiers */
1504         ot->name= "Link Nodes";
1505         ot->idname= "NODE_OT_link";
1506         
1507         /* api callbacks */
1508         ot->invoke= node_link_invoke;
1509         ot->modal= node_link_modal;
1510 //      ot->exec= node_link_exec;
1511         ot->poll= ED_operator_node_active;
1512         
1513         /* flags */
1514         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO|OPTYPE_BLOCKING;
1515 }
1516
1517 /* ********************** Make Link operator ***************** */
1518
1519 /* makes a link between selected output and input sockets */
1520 static int node_make_link_exec(bContext *C, wmOperator *op)
1521 {
1522         SpaceNode *snode= CTX_wm_space_node(C);
1523         int replace = RNA_boolean_get(op->ptr, "replace");
1524
1525         snode_autoconnect(snode, 0, replace);
1526
1527         node_tree_verify_groups(snode->nodetree);
1528         snode_handle_recalc(C, snode);
1529         
1530         return OPERATOR_FINISHED;
1531 }
1532
1533 void NODE_OT_link_make(wmOperatorType *ot)
1534 {
1535         /* identifiers */
1536         ot->name= "Make Links";
1537         ot->description= "Makes a link between selected output in input sockets.";
1538         ot->idname= "NODE_OT_link_make";
1539         
1540         /* callbacks */
1541         ot->exec= node_make_link_exec;
1542         ot->poll= ED_operator_node_active; // XXX we need a special poll which checks that there are selected input/output sockets
1543         
1544         /* flags */
1545         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
1546         
1547         RNA_def_boolean(ot->srna, "replace", 0, "Replace", "Replace socket connections with the new links");
1548 }
1549
1550 /* ********************** Cut Link operator ***************** */
1551
1552 #define LINK_RESOL 12
1553 static int cut_links_intersect(bNodeLink *link, float mcoords[][2], int tot)
1554 {
1555         float coord_array[LINK_RESOL+1][2];
1556         int i, b;
1557         
1558         if(node_link_bezier_points(NULL, NULL, link, coord_array, LINK_RESOL)) {
1559
1560                 for(i=0; i<tot-1; i++)
1561                         for(b=0; b<LINK_RESOL-1; b++)
1562                                 if(isect_line_line_v2(mcoords[i], mcoords[i+1], coord_array[b], coord_array[b+1]) > 0)
1563                                         return 1;
1564         }
1565         return 0;
1566 }
1567
1568 static int cut_links_exec(bContext *C, wmOperator *op)
1569 {
1570         SpaceNode *snode= CTX_wm_space_node(C);
1571         ARegion *ar= CTX_wm_region(C);
1572         float mcoords[256][2];
1573         int i= 0;
1574         
1575         RNA_BEGIN(op->ptr, itemptr, "path") {
1576                 float loc[2];
1577                 
1578                 RNA_float_get_array(&itemptr, "loc", loc);
1579                 UI_view2d_region_to_view(&ar->v2d, (short)loc[0], (short)loc[1], 
1580                                                                  &mcoords[i][0], &mcoords[i][1]);
1581                 i++;
1582                 if(i>= 256) break;
1583         }
1584         RNA_END;
1585         
1586         if(i>1) {
1587                 bNodeLink *link, *next;
1588                 
1589                 for(link= snode->edittree->links.first; link; link= next) {
1590                         next= link->next;
1591                         
1592                         if(cut_links_intersect(link, mcoords, i)) {
1593                                 NodeTagChanged(snode->edittree, link->tonode);
1594                                 nodeRemLink(snode->edittree, link);
1595                         }
1596                 }
1597                 
1598                 ntreeSolveOrder(snode->edittree);
1599                 node_tree_verify_groups(snode->nodetree);
1600                 snode_handle_recalc(C, snode);
1601                 
1602                 return OPERATOR_FINISHED;
1603         }
1604         
1605         return OPERATOR_CANCELLED|OPERATOR_PASS_THROUGH;
1606 }
1607
1608 void NODE_OT_links_cut(wmOperatorType *ot)
1609 {
1610         PropertyRNA *prop;
1611         
1612         ot->name= "Cut links";
1613         ot->idname= "NODE_OT_links_cut";
1614         
1615         ot->invoke= WM_gesture_lines_invoke;
1616         ot->modal= WM_gesture_lines_modal;
1617         ot->exec= cut_links_exec;
1618         
1619         ot->poll= ED_operator_node_active;
1620         
1621         /* flags */
1622         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
1623         
1624         prop= RNA_def_property(ot->srna, "path", PROP_COLLECTION, PROP_NONE);
1625         RNA_def_property_struct_runtime(prop, &RNA_OperatorMousePath);
1626         /* internal */
1627         RNA_def_int(ot->srna, "cursor", BC_KNIFECURSOR, 0, INT_MAX, "Cursor", "", 0, INT_MAX);
1628 }
1629
1630 /* ******************************** */
1631 // XXX some code needing updating to operators...
1632
1633 /* goes over all scenes, reads render layerss */
1634 void node_read_renderlayers(SpaceNode *snode)
1635 {
1636         Scene *curscene= NULL; // XXX
1637         Scene *scene;
1638         bNode *node;
1639
1640         /* first tag scenes unread */
1641         for(scene= G.main->scene.first; scene; scene= scene->id.next) 
1642                 scene->id.flag |= LIB_DOIT;
1643
1644         for(node= snode->edittree->nodes.first; node; node= node->next) {
1645                 if(node->type==CMP_NODE_R_LAYERS) {
1646                         ID *id= node->id;
1647                         if(id->flag & LIB_DOIT) {
1648                                 RE_ReadRenderResult(curscene, (Scene *)id);
1649                                 ntreeCompositTagRender((Scene *)id);
1650                                 id->flag &= ~LIB_DOIT;
1651                         }
1652                 }
1653         }
1654         
1655         // XXX                  snode_handle_recalc(snode);
1656 }
1657
1658 void node_read_fullsamplelayers(SpaceNode *snode)
1659 {
1660         Scene *curscene= NULL; // XXX
1661         Render *re= RE_NewRender(curscene->id.name);
1662
1663         WM_cursor_wait(1);
1664
1665         //BIF_init_render_callbacks(re, 1);
1666         RE_MergeFullSample(re, curscene, snode->nodetree);
1667         //BIF_end_render_callbacks();
1668         
1669         // allqueue(REDRAWNODE, 1);
1670         // allqueue(REDRAWIMAGE, 1);
1671         
1672         WM_cursor_wait(0);
1673 }
1674
1675 void imagepaint_composite_tags(bNodeTree *ntree, Image *image, ImageUser *iuser)
1676 {
1677         bNode *node;
1678         
1679         if(ntree==NULL)
1680                 return;
1681         
1682         /* search for renderresults */
1683         if(image->type==IMA_TYPE_R_RESULT) {
1684                 for(node= ntree->nodes.first; node; node= node->next) {
1685                         if(node->type==CMP_NODE_R_LAYERS && node->id==NULL) {
1686                                 /* imageuser comes from ImageWin, so indexes are offset 1 */
1687                                 if(node->custom1==iuser->layer-1)
1688                                         NodeTagChanged(ntree, node);
1689                         }
1690                 }
1691         }
1692         else {
1693                 for(node= ntree->nodes.first; node; node= node->next) {
1694                         if(node->id== &image->id)
1695                                 NodeTagChanged(ntree, node);
1696                 }
1697         }
1698 }
1699
1700 /* ****************** Make Group operator ******************* */
1701
1702 static int node_group_make_exec(bContext *C, wmOperator *op)
1703 {
1704         SpaceNode *snode = CTX_wm_space_node(C);
1705         bNode *gnode;
1706         
1707         if(snode->edittree!=snode->nodetree) {
1708                 BKE_report(op->reports, RPT_ERROR, "Can not add a new Group in a Group");
1709                 return OPERATOR_CANCELLED;
1710         }
1711         
1712         /* for time being... is too complex to handle */
1713         if(snode->treetype==NTREE_COMPOSIT) {
1714                 for(gnode=snode->nodetree->nodes.first; gnode; gnode= gnode->next) {
1715                         if(gnode->flag & SELECT)
1716                                 if(gnode->type==CMP_NODE_R_LAYERS)
1717                                         break;
1718                 }
1719                 
1720                 if(gnode) {
1721                         BKE_report(op->reports, RPT_ERROR, "Can not add RenderLayer in a Group");
1722                         return OPERATOR_CANCELLED;
1723                 }
1724         }
1725         
1726         gnode= nodeMakeGroupFromSelected(snode->nodetree);
1727         if(gnode==NULL) {
1728                 BKE_report(op->reports, RPT_ERROR, "Can not make Group");
1729                 return OPERATOR_CANCELLED;
1730         }
1731         else {
1732                 nodeSetActive(snode->nodetree, gnode);
1733                 ntreeSolveOrder(snode->nodetree);
1734         }
1735         
1736         snode_handle_recalc(C, snode);
1737         
1738         return OPERATOR_FINISHED;
1739 }
1740
1741 void NODE_OT_group_make(wmOperatorType *ot)
1742 {
1743         /* identifiers */
1744         ot->name = "Group";
1745         ot->description = "Make group from selected nodes.";
1746         ot->idname = "NODE_OT_group_make";
1747         
1748         /* api callbacks */
1749         ot->exec = node_group_make_exec;
1750         ot->poll = ED_operator_node_active;
1751         
1752         /* flags */
1753         ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
1754 }
1755
1756 /* ****************** Hide operator *********************** */
1757
1758 static int node_hide_exec(bContext *C, wmOperator *op)
1759 {
1760         SpaceNode *snode= CTX_wm_space_node(C);
1761         bNode *node;
1762         int nothidden=0, ishidden=0;
1763         
1764         /* sanity checking (poll callback checks this already) */
1765         if((snode == NULL) || (snode->edittree == NULL))
1766                 return OPERATOR_CANCELLED;
1767         
1768         for(node= snode->edittree->nodes.first; node; node= node->next) {
1769                 if(node->flag & SELECT) {
1770                         if(node->flag & NODE_HIDDEN)
1771                                 ishidden++;
1772                         else
1773                                 nothidden++;
1774                 }
1775         }
1776         for(node= snode->edittree->nodes.first; node; node= node->next) {
1777                 if(node->flag & SELECT) {
1778                         if( (ishidden && nothidden) || ishidden==0)
1779                                 node->flag |= NODE_HIDDEN;
1780                         else 
1781                                 node->flag &= ~NODE_HIDDEN;
1782                 }
1783         }
1784         
1785         snode_handle_recalc(C, snode);
1786         
1787         return OPERATOR_FINISHED;
1788 }
1789
1790 void NODE_OT_hide(wmOperatorType *ot)
1791 {
1792         /* identifiers */
1793         ot->name= "Hide";
1794         ot->description= "Toggle hiding of the nodes.";
1795         ot->idname= "NODE_OT_hide";
1796         
1797         /* callbacks */
1798         ot->exec= node_hide_exec;
1799         ot->poll= ED_operator_node_active;
1800         
1801         /* flags */
1802         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
1803 }
1804
1805 /* ****************** Mute operator *********************** */
1806
1807 static int node_mute_exec(bContext *C, wmOperator *op)
1808 {
1809         SpaceNode *snode= CTX_wm_space_node(C);
1810         bNode *node;
1811
1812         /* no disabling inside of groups */
1813         if(node_tree_get_editgroup(snode->nodetree))
1814                 return OPERATOR_CANCELLED;
1815         
1816         for(node= snode->edittree->nodes.first; node; node= node->next) {
1817                 if(node->flag & SELECT) {
1818                         if(node->inputs.first && node->outputs.first) {
1819                                 node->flag ^= NODE_MUTED;
1820                         }
1821                 }
1822         }
1823         
1824         snode_handle_recalc(C, snode);
1825         
1826         return OPERATOR_FINISHED;
1827 }
1828
1829 void NODE_OT_mute(wmOperatorType *ot)
1830 {
1831         /* identifiers */
1832         ot->name= "Mute";
1833         ot->description= "Toggle muting of the nodes.";
1834         ot->idname= "NODE_OT_mute";
1835         
1836         /* callbacks */
1837         ot->exec= node_mute_exec;
1838         ot->poll= ED_operator_node_active;
1839         
1840         /* flags */
1841         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
1842 }
1843
1844 /* ****************** Delete operator ******************* */
1845
1846 static int node_delete_exec(bContext *C, wmOperator *op)
1847 {
1848         SpaceNode *snode= CTX_wm_space_node(C);
1849         bNode *node, *next;
1850         
1851         for(node= snode->edittree->nodes.first; node; node= next) {
1852                 next= node->next;
1853                 if(node->flag & SELECT) {
1854                         /* check id user here, nodeFreeNode is called for free dbase too */
1855                         if(node->id)
1856                                 node->id->us--;
1857                         nodeFreeNode(snode->edittree, node);
1858                 }
1859         }
1860         
1861         node_tree_verify_groups(snode->nodetree);
1862
1863         snode_handle_recalc(C, snode);
1864         
1865         return OPERATOR_FINISHED;
1866 }
1867
1868 void NODE_OT_delete(wmOperatorType *ot)
1869 {
1870         /* identifiers */
1871         ot->name= "Delete";
1872         ot->description = "Delete selected nodes.";
1873         ot->idname= "NODE_OT_delete";
1874         
1875         /* api callbacks */
1876         ot->exec= node_delete_exec;
1877         ot->poll= ED_operator_node_active;
1878         
1879         /* flags */
1880         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
1881 }
1882
1883 /* ****************** Show Cyclic Dependencies Operator  ******************* */
1884
1885 static int node_show_cycles_exec(bContext *C, wmOperator *op)
1886 {
1887         SpaceNode *snode= CTX_wm_space_node(C);
1888         
1889         /* this is just a wrapper around this call... */
1890         ntreeSolveOrder(snode->edittree);
1891         snode_handle_recalc(C, snode);
1892         
1893         return OPERATOR_FINISHED;
1894 }
1895
1896 void NODE_OT_show_cyclic_dependencies(wmOperatorType *ot)
1897 {
1898         /* identifiers */
1899         ot->name= "Show Cyclic Dependencies";
1900         ot->description= "Sort the nodes and show the cyclic dependencies between the nodes.";
1901         ot->idname= "NODE_OT_show_cyclic_dependencies";
1902         
1903         /* callbacks */
1904         ot->exec= node_show_cycles_exec;
1905         ot->poll= ED_operator_node_active;
1906         
1907         /* flags */
1908         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
1909 }
1910
1911