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