Node UI:
[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 #include <errno.h>
35
36 #include "MEM_guardedalloc.h"
37
38 #include "DNA_object_types.h"
39 #include "DNA_material_types.h"
40 #include "DNA_node_types.h"
41 #include "DNA_scene_types.h"
42
43 #include "BLI_math.h"
44 #include "BLI_blenlib.h"
45 #include "BLI_storage_types.h"
46 #include "BLI_utildefines.h"
47
48 #include "BKE_context.h"
49 #include "BKE_global.h"
50 #include "BKE_image.h"
51 #include "BKE_library.h"
52 #include "BKE_main.h"
53 #include "BKE_node.h"
54 #include "BKE_material.h"
55 #include "BKE_paint.h"
56 #include "BKE_screen.h"
57 #include "BKE_texture.h"
58 #include "BKE_report.h"
59
60 #include "RE_pipeline.h"
61
62 #include "IMB_imbuf_types.h"
63
64 #include "ED_node.h"
65 #include "ED_screen.h"
66 #include "ED_space_api.h"
67 #include "ED_render.h"
68
69 #include "RNA_access.h"
70 #include "RNA_define.h"
71
72 #include "WM_api.h"
73 #include "WM_types.h"
74
75 #include "UI_interface.h"
76 #include "UI_view2d.h"
77
78 #include "IMB_imbuf.h"
79
80 #include "node_intern.h"
81
82 #define SOCK_IN         1
83 #define SOCK_OUT        2
84
85 /* ***************** composite job manager ********************** */
86
87 typedef struct CompoJob {
88         Scene *scene;
89         bNodeTree *ntree;
90         bNodeTree *localtree;
91         short *stop;
92         short *do_update;
93         float *progress;
94 } CompoJob;
95
96 /* called by compo, only to check job 'stop' value */
97 static int compo_breakjob(void *cjv)
98 {
99         CompoJob *cj= cjv;
100         
101         return *(cj->stop);
102 }
103
104 /* called by compo, wmJob sends notifier */
105 static void compo_redrawjob(void *cjv, char *UNUSED(str))
106 {
107         CompoJob *cj= cjv;
108         
109         *(cj->do_update)= 1;
110 }
111
112 static void compo_freejob(void *cjv)
113 {
114         CompoJob *cj= cjv;
115
116         if(cj->localtree) {
117                 ntreeLocalMerge(cj->localtree, cj->ntree);
118         }
119         MEM_freeN(cj);
120 }
121
122 /* only now we copy the nodetree, so adding many jobs while
123    sliding buttons doesn't frustrate */
124 static void compo_initjob(void *cjv)
125 {
126         CompoJob *cj= cjv;
127
128         cj->localtree= ntreeLocalize(cj->ntree);
129 }
130
131 /* called before redraw notifiers, it moves finished previews over */
132 static void compo_updatejob(void *cjv)
133 {
134         CompoJob *cj= cjv;
135         
136         ntreeLocalSync(cj->localtree, cj->ntree);
137 }
138
139 static void compo_progressjob(void *cjv, float progress)
140 {
141         CompoJob *cj= cjv;
142         
143         *(cj->progress) = progress;
144 }
145
146
147 /* only this runs inside thread */
148 static void compo_startjob(void *cjv, short *stop, short *do_update, float *progress)
149 {
150         CompoJob *cj= cjv;
151         bNodeTree *ntree= cj->localtree;
152
153         if(cj->scene->use_nodes==0)
154                 return;
155         
156         cj->stop= stop;
157         cj->do_update= do_update;
158         cj->progress= progress;
159         
160         ntree->test_break= compo_breakjob;
161         ntree->tbh= cj;
162         ntree->stats_draw= compo_redrawjob;
163         ntree->sdh= cj;
164         ntree->progress= compo_progressjob;
165         ntree->prh= 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         ntree->progress= NULL;
174
175 }
176
177 void snode_composite_job(const bContext *C, ScrArea *sa)
178 {
179         SpaceNode *snode= sa->spacedata.first;
180         wmJob *steve;
181         CompoJob *cj;
182
183         steve= WM_jobs_get(CTX_wm_manager(C), CTX_wm_window(C), sa, "Compositing", WM_JOB_EXCL_RENDER|WM_JOB_PROGRESS);
184         cj= MEM_callocN(sizeof(CompoJob), "compo job");
185         
186         /* customdata for preview thread */
187         cj->scene= CTX_data_scene(C);
188         cj->ntree= snode->nodetree;
189         
190         /* setup job */
191         WM_jobs_customdata(steve, cj, compo_freejob);
192         WM_jobs_timer(steve, 0.1, NC_SCENE, NC_SCENE|ND_COMPO_RESULT);
193         WM_jobs_callbacks(steve, compo_startjob, compo_initjob, compo_updatejob, NULL);
194         
195         WM_jobs_start(CTX_wm_manager(C), steve);
196         
197 }
198
199 /* ***************************************** */
200
201 /* operator poll callback */
202 static int composite_node_active(bContext *C)
203 {
204         if( ED_operator_node_active(C)) {
205                 SpaceNode *snode= CTX_wm_space_node(C);
206                 if(snode->treetype==NTREE_COMPOSIT)
207                         return 1;
208         }
209         return 0;
210 }
211
212 /* also checks for edited groups */
213 bNode *editnode_get_active(bNodeTree *ntree)
214 {
215         bNode *node;
216         
217         /* check for edited group */
218         for(node= ntree->nodes.first; node; node= node->next)
219                 if(node->flag & NODE_GROUP_EDIT)
220                         break;
221         if(node)
222                 return nodeGetActive((bNodeTree *)node->id);
223         else
224                 return nodeGetActive(ntree);
225 }
226
227 void snode_notify(bContext *C, SpaceNode *snode)
228 {
229         WM_event_add_notifier(C, NC_NODE|NA_EDITED, NULL);
230
231         if(snode->treetype==NTREE_SHADER)
232                 WM_event_add_notifier(C, NC_MATERIAL|ND_NODES, snode->id);
233         else if(snode->treetype==NTREE_COMPOSIT)
234                 WM_event_add_notifier(C, NC_SCENE|ND_NODES, snode->id);
235         else if(snode->treetype==NTREE_TEXTURE)
236                 WM_event_add_notifier(C, NC_TEXTURE|ND_NODES, snode->id);
237 }
238
239 bNode *node_tree_get_editgroup(bNodeTree *nodetree)
240 {
241         bNode *gnode;
242         
243         /* get the groupnode */
244         for(gnode= nodetree->nodes.first; gnode; gnode= gnode->next)
245                 if(gnode->flag & NODE_GROUP_EDIT)
246                         break;
247         return gnode;
248 }
249
250 /* assumes nothing being done in ntree yet, sets the default in/out node */
251 /* called from shading buttons or header */
252 void ED_node_shader_default(Material *ma)
253 {
254         bNode *in, *out;
255         bNodeSocket *fromsock, *tosock;
256         
257         /* but lets check it anyway */
258         if(ma->nodetree) {
259                 if (G.f & G_DEBUG)
260                         printf("error in shader initialize\n");
261                 return;
262         }
263         
264         ma->nodetree= ntreeAddTree("Shader Nodetree", NTREE_SHADER, FALSE);
265         
266         out= nodeAddNodeType(ma->nodetree, SH_NODE_OUTPUT, NULL, NULL);
267         out->locx= 300.0f; out->locy= 300.0f;
268         
269         in= nodeAddNodeType(ma->nodetree, SH_NODE_MATERIAL, NULL, NULL);
270         in->locx= 10.0f; in->locy= 300.0f;
271         nodeSetActive(ma->nodetree, in);
272         
273         /* only a link from color to color */
274         fromsock= in->outputs.first;
275         tosock= out->inputs.first;
276         nodeAddLink(ma->nodetree, in, fromsock, out, tosock);
277         
278         ntreeSolveOrder(ma->nodetree);  /* needed for pointers */
279 }
280
281 /* assumes nothing being done in ntree yet, sets the default in/out node */
282 /* called from shading buttons or header */
283 void ED_node_composit_default(Scene *sce)
284 {
285         bNode *in, *out;
286         bNodeSocket *fromsock, *tosock;
287         
288         /* but lets check it anyway */
289         if(sce->nodetree) {
290                 if (G.f & G_DEBUG)
291                         printf("error in composite initialize\n");
292                 return;
293         }
294         
295         sce->nodetree= ntreeAddTree("Compositing Nodetree", NTREE_COMPOSIT, FALSE);
296         
297         out= nodeAddNodeType(sce->nodetree, CMP_NODE_COMPOSITE, NULL, NULL);
298         out->locx= 300.0f; out->locy= 400.0f;
299         out->id= &sce->id;
300         id_us_plus(out->id);
301         
302         in= nodeAddNodeType(sce->nodetree, CMP_NODE_R_LAYERS, NULL, NULL);
303         in->locx= 10.0f; in->locy= 400.0f;
304         in->id= &sce->id;
305         id_us_plus(in->id);
306         nodeSetActive(sce->nodetree, in);
307         
308         /* links from color to color */
309         fromsock= in->outputs.first;
310         tosock= out->inputs.first;
311         nodeAddLink(sce->nodetree, in, fromsock, out, tosock);
312         
313         ntreeSolveOrder(sce->nodetree); /* needed for pointers */
314         
315         // XXX ntreeCompositForceHidden(sce->nodetree);
316 }
317
318 /* assumes nothing being done in ntree yet, sets the default in/out node */
319 /* called from shading buttons or header */
320 void ED_node_texture_default(Tex *tx)
321 {
322         bNode *in, *out;
323         bNodeSocket *fromsock, *tosock;
324         
325         /* but lets check it anyway */
326         if(tx->nodetree) {
327                 if (G.f & G_DEBUG)
328                         printf("error in texture initialize\n");
329                 return;
330         }
331         
332         tx->nodetree= ntreeAddTree("Texture Nodetree", NTREE_TEXTURE, FALSE);
333         
334         out= nodeAddNodeType(tx->nodetree, TEX_NODE_OUTPUT, NULL, NULL);
335         out->locx= 300.0f; out->locy= 300.0f;
336         
337         in= nodeAddNodeType(tx->nodetree, TEX_NODE_CHECKER, NULL, NULL);
338         in->locx= 10.0f; in->locy= 300.0f;
339         nodeSetActive(tx->nodetree, in);
340         
341         fromsock= in->outputs.first;
342         tosock= out->inputs.first;
343         nodeAddLink(tx->nodetree, in, fromsock, out, tosock);
344         
345         ntreeSolveOrder(tx->nodetree);  /* needed for pointers */
346 }
347
348 /* id is supposed to contain a node tree */
349 void node_tree_from_ID(ID *id, bNodeTree **ntree, bNodeTree **edittree, int *treetype)
350 {
351         bNode *node= NULL;
352         short idtype= GS(id->name);
353
354         if(idtype == ID_MA) {
355                 *ntree= ((Material*)id)->nodetree;
356                 if(treetype) *treetype= NTREE_SHADER;
357         }
358         else if(idtype == ID_SCE) {
359                 *ntree= ((Scene*)id)->nodetree;
360                 if(treetype) *treetype= NTREE_COMPOSIT;
361         }
362         else if(idtype == ID_TE) {
363                 *ntree= ((Tex*)id)->nodetree;
364                 if(treetype) *treetype= NTREE_TEXTURE;
365         }
366         else {
367                 if(treetype) *treetype= 0;
368                 return;
369         }
370
371         /* find editable group */
372         if(edittree) {
373                 if(*ntree)
374                         for(node= (*ntree)->nodes.first; node; node= node->next)
375                                 if(node->flag & NODE_GROUP_EDIT)
376                                         break;
377                 
378                 if(node && node->id)
379                         *edittree= (bNodeTree *)node->id;
380                 else
381                         *edittree= *ntree;
382         }
383 }
384
385 /* Here we set the active tree(s), even called for each redraw now, so keep it fast :) */
386 void snode_set_context(SpaceNode *snode, Scene *scene)
387 {
388         Object *ob= OBACT;
389         
390         snode->nodetree= NULL;
391         snode->edittree= NULL;
392         snode->id= snode->from= NULL;
393         
394         if(snode->treetype==NTREE_SHADER) {
395                 /* need active object, or we allow pinning... */
396                 if(ob) {
397                         Material *ma= give_current_material(ob, ob->actcol);
398                         if(ma) {
399                                 snode->from= &ob->id;
400                                 snode->id= &ma->id;
401                         }
402                 }
403         }
404         else if(snode->treetype==NTREE_COMPOSIT) {
405                 snode->from= NULL;
406                 snode->id= &scene->id;
407                 
408                 /* bit clumsy but reliable way to see if we draw first time */
409                 if(snode->nodetree==NULL)
410                         ntreeCompositForceHidden(scene->nodetree, scene);
411         }
412         else if(snode->treetype==NTREE_TEXTURE) {
413                 Tex *tx= NULL;
414
415                 if(snode->texfrom==SNODE_TEX_OBJECT) {
416                         if(ob) {
417                                 tx= give_current_object_texture(ob);
418
419                                 if(ob->type == OB_LAMP)
420                                         snode->from= (ID*)ob->data;
421                                 else
422                                         snode->from= (ID*)give_current_material(ob, ob->actcol);
423
424                                 /* from is not set fully for material nodes, should be ID + Node then */
425                                 snode->id= &tx->id;
426                         }
427                 }
428                 else if(snode->texfrom==SNODE_TEX_WORLD) {
429                         tx= give_current_world_texture(scene->world);
430                         snode->from= (ID *)scene->world;
431                         snode->id= &tx->id;
432                 }
433                 else {
434                         struct Brush *brush= NULL;
435                         
436                         if(ob && (ob->mode & OB_MODE_SCULPT))
437                                 brush= paint_brush(&scene->toolsettings->sculpt->paint);
438                         else
439                                 brush= paint_brush(&scene->toolsettings->imapaint.paint);
440
441                         if (brush) {
442                                 snode->from= (ID *)brush;
443                                 tx= give_current_brush_texture(brush);
444                                 snode->id= &tx->id;
445                         }
446                 }
447         }
448
449         if(snode->id)
450                 node_tree_from_ID(snode->id, &snode->nodetree, &snode->edittree, NULL);
451 }
452
453 void node_set_active(SpaceNode *snode, bNode *node)
454 {
455         nodeSetActive(snode->edittree, node);
456         
457         if(node->type!=NODE_GROUP) {
458                 int was_output= (node->flag & NODE_DO_OUTPUT);
459                 
460                 /* tree specific activate calls */
461                 if(snode->treetype==NTREE_SHADER) {
462                         /* when we select a material, active texture is cleared, for buttons */
463                         if(node->id && GS(node->id->name)==ID_MA)
464                                 nodeClearActiveID(snode->edittree, ID_TE);
465                         
466                         if(node->type==SH_NODE_OUTPUT) {
467                                 bNode *tnode;
468                                 
469                                 for(tnode= snode->edittree->nodes.first; tnode; tnode= tnode->next)
470                                         if( tnode->type==SH_NODE_OUTPUT)
471                                                 tnode->flag &= ~NODE_DO_OUTPUT;
472                                 
473                                 node->flag |= NODE_DO_OUTPUT;
474                                 if(was_output==0)
475                                         ED_node_changed_update(snode->id, node);
476                         }
477
478                         // XXX
479 #if 0
480                         if(node->id)
481                                 ; // XXX BIF_preview_changed(-1);       /* temp hack to force texture preview to update */
482                         
483                         // allqueue(REDRAWBUTSSHADING, 1);
484                         // allqueue(REDRAWIPO, 0);
485 #endif
486                 }
487                 else if(snode->treetype==NTREE_COMPOSIT) {
488                         Scene *scene= (Scene*)snode->id;
489
490                         /* make active viewer, currently only 1 supported... */
491                         if( ELEM(node->type, CMP_NODE_VIEWER, CMP_NODE_SPLITVIEWER)) {
492                                 bNode *tnode;
493                                 
494
495                                 for(tnode= snode->edittree->nodes.first; tnode; tnode= tnode->next)
496                                         if( ELEM(tnode->type, CMP_NODE_VIEWER, CMP_NODE_SPLITVIEWER))
497                                                 tnode->flag &= ~NODE_DO_OUTPUT;
498                                 
499                                 node->flag |= NODE_DO_OUTPUT;
500                                 if(was_output==0) {
501                                         bNode *gnode;
502                                         
503                                         NodeTagChanged(snode->edittree, node);
504                                         
505                                         /* if inside group, tag entire group */
506                                         gnode= node_tree_get_editgroup(snode->nodetree);
507                                         if(gnode)
508                                                 NodeTagIDChanged(snode->nodetree, gnode->id);
509                                         
510                                         ED_node_changed_update(snode->id, node);
511                                 }
512                                 
513                                 /* addnode() doesnt link this yet... */
514                                 node->id= (ID *)BKE_image_verify_viewer(IMA_TYPE_COMPOSITE, "Viewer Node");
515                         }
516                         else if(node->type==CMP_NODE_R_LAYERS) {
517                                 if(node->id==NULL || node->id==(ID *)scene) {
518                                         scene->r.actlay= node->custom1;
519                                 }
520                         }
521                         else if(node->type==CMP_NODE_COMPOSITE) {
522                                 bNode *tnode;
523                                 
524                                 for(tnode= snode->edittree->nodes.first; tnode; tnode= tnode->next)
525                                         if( tnode->type==CMP_NODE_COMPOSITE)
526                                                 tnode->flag &= ~NODE_DO_OUTPUT;
527                                 
528                                 node->flag |= NODE_DO_OUTPUT;
529                                 ED_node_changed_update(snode->id, node);
530                         }
531                 }
532                 else if(snode->treetype==NTREE_TEXTURE) {
533                         // XXX
534 #if 0
535                         if(node->id)
536                                 ; // XXX BIF_preview_changed(-1);
537                         // allqueue(REDRAWBUTSSHADING, 1);
538                         // allqueue(REDRAWIPO, 0);
539 #endif
540                 }
541         }
542 }
543
544 /* when links in groups change, inputs/outputs change, nodes added/deleted... */
545 void node_tree_verify_groups(bNodeTree *nodetree)
546 {
547         bNode *gnode;
548         
549         gnode= node_tree_get_editgroup(nodetree);
550         
551         /* does all materials */
552         if(gnode)
553                 nodeVerifyGroup((bNodeTree *)gnode->id);
554         
555 }
556
557 /* ***************** Edit Group operator ************* */
558
559 void snode_make_group_editable(SpaceNode *snode, bNode *gnode)
560 {
561         bNode *node;
562         
563         /* make sure nothing has group editing on */
564         for(node= snode->nodetree->nodes.first; node; node= node->next)
565                 node->flag &= ~NODE_GROUP_EDIT;
566         
567         if(gnode==NULL) {
568                 /* with NULL argument we do a toggle */
569                 if(snode->edittree==snode->nodetree)
570                         gnode= nodeGetActive(snode->nodetree);
571         }
572         
573         if(gnode && gnode->type==NODE_GROUP && gnode->id) {
574                 if(gnode->id->lib)
575                         ntreeMakeLocal((bNodeTree *)gnode->id);
576
577                 gnode->flag |= NODE_GROUP_EDIT;
578                 snode->edittree= (bNodeTree *)gnode->id;
579                 
580                 /* deselect all other nodes, so we can also do grabbing of entire subtree */
581                 for(node= snode->nodetree->nodes.first; node; node= node->next)
582                         node->flag &= ~SELECT;
583                 gnode->flag |= SELECT;
584                 
585         }
586         else 
587                 snode->edittree= snode->nodetree;
588         
589         ntreeSolveOrder(snode->nodetree);
590 }
591
592 static int node_group_edit_exec(bContext *C, wmOperator *UNUSED(op))
593 {
594         SpaceNode *snode = CTX_wm_space_node(C);
595         bNode *gnode;
596
597         ED_preview_kill_jobs(C);
598
599         gnode= nodeGetActive(snode->edittree);
600         snode_make_group_editable(snode, gnode);
601
602         WM_event_add_notifier(C, NC_SCENE|ND_NODES, NULL);
603
604         return OPERATOR_FINISHED;
605 }
606
607 static int node_group_edit_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(event))
608 {
609         SpaceNode *snode = CTX_wm_space_node(C);
610         bNode *gnode;
611
612         gnode= nodeGetActive(snode->edittree);
613         if(gnode && gnode->type==NODE_GROUP && gnode->id && gnode->id->lib) {
614                 uiPupMenuOkee(C, op->type->idname, "Make group local?");
615                 return OPERATOR_CANCELLED;
616         }
617
618         return node_group_edit_exec(C, op);
619 }
620
621 void NODE_OT_group_edit(wmOperatorType *ot)
622 {
623         /* identifiers */
624         ot->name = "Edit Group";
625         ot->description = "Edit node group";
626         ot->idname = "NODE_OT_group_edit";
627         
628         /* api callbacks */
629         ot->invoke = node_group_edit_invoke;
630         ot->exec = node_group_edit_exec;
631         ot->poll = ED_operator_node_active;
632         
633         /* flags */
634         ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
635 }
636
637 /* ******************** Ungroup operator ********************** */
638
639 static int node_group_ungroup_exec(bContext *C, wmOperator *op)
640 {
641         SpaceNode *snode = CTX_wm_space_node(C);
642         bNode *gnode;
643
644         ED_preview_kill_jobs(C);
645
646         /* are we inside of a group? */
647         gnode= node_tree_get_editgroup(snode->nodetree);
648         if(gnode)
649                 snode_make_group_editable(snode, NULL);
650         
651         gnode= nodeGetActive(snode->edittree);
652         if(gnode==NULL)
653                 return OPERATOR_CANCELLED;
654         
655         if(gnode->type!=NODE_GROUP) {
656                 BKE_report(op->reports, RPT_WARNING, "Not a group");
657                 return OPERATOR_CANCELLED;
658         }
659         else if(!nodeGroupUnGroup(snode->edittree, gnode)) {
660                 BKE_report(op->reports, RPT_WARNING, "Can't ungroup");
661                 return OPERATOR_CANCELLED;
662         }
663
664         WM_event_add_notifier(C, NC_SCENE|ND_NODES, NULL);
665
666         return OPERATOR_FINISHED;
667 }
668
669 void NODE_OT_group_ungroup(wmOperatorType *ot)
670 {
671         /* identifiers */
672         ot->name = "Ungroup";
673         ot->description = "Ungroup selected nodes";
674         ot->idname = "NODE_OT_group_ungroup";
675         
676         /* api callbacks */
677         ot->exec = node_group_ungroup_exec;
678         ot->poll = ED_operator_node_active;
679         
680         /* flags */
681         ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
682 }
683
684 /* ************************** Node generic ************** */
685
686 /* allows to walk the list in order of visibility */
687 bNode *next_node(bNodeTree *ntree)
688 {
689         static bNode *current=NULL, *last= NULL;
690         
691         if(ntree) {
692                 /* set current to the first selected node */
693                 for(current= ntree->nodes.last; current; current= current->prev)
694                         if(current->flag & NODE_SELECT)
695                                 break;
696                 
697                 /* set last to the first unselected node */
698                 for(last= ntree->nodes.last; last; last= last->prev)
699                         if((last->flag & NODE_SELECT)==0)
700                                 break;
701                 
702                 if(current==NULL)
703                         current= last;
704                 
705                 return NULL;
706         }
707         /* no nodes, or we are ready */
708         if(current==NULL)
709                 return NULL;
710         
711         /* now we walk the list backwards, but we always return current */
712         if(current->flag & NODE_SELECT) {
713                 bNode *node= current;
714                 
715                 /* find previous selected */
716                 current= current->prev;
717                 while(current && (current->flag & NODE_SELECT)==0)
718                         current= current->prev;
719                 
720                 /* find first unselected */
721                 if(current==NULL)
722                         current= last;
723                 
724                 return node;
725         }
726         else {
727                 bNode *node= current;
728                 
729                 /* find previous unselected */
730                 current= current->prev;
731                 while(current && (current->flag & NODE_SELECT))
732                         current= current->prev;
733                 
734                 return node;
735         }
736         
737         return NULL;
738 }
739
740 /* is rct in visible part of node? */
741 static bNode *visible_node(SpaceNode *snode, rctf *rct)
742 {
743         bNode *tnode;
744         
745         for(next_node(snode->edittree); (tnode=next_node(NULL));) {
746                 if(BLI_isect_rctf(&tnode->totr, rct, NULL))
747                         break;
748         }
749         return tnode;
750 }
751
752 /* **************************** */
753
754 typedef struct NodeViewMove {
755         short mvalo[2];
756         int xmin, ymin, xmax, ymax;
757 } NodeViewMove;
758
759 static int snode_bg_viewmove_modal(bContext *C, wmOperator *op, wmEvent *event)
760 {
761         SpaceNode *snode= CTX_wm_space_node(C);
762         ARegion *ar= CTX_wm_region(C);
763         NodeViewMove *nvm= op->customdata;
764
765         switch (event->type) {
766                 case MOUSEMOVE:
767                         
768                         snode->xof -= (nvm->mvalo[0]-event->mval[0]);
769                         snode->yof -= (nvm->mvalo[1]-event->mval[1]);
770                         nvm->mvalo[0]= event->mval[0];
771                         nvm->mvalo[1]= event->mval[1];
772                         
773                         /* prevent dragging image outside of the window and losing it! */
774                         CLAMP(snode->xof, nvm->xmin, nvm->xmax);
775                         CLAMP(snode->yof, nvm->ymin, nvm->ymax);
776                         
777                         ED_region_tag_redraw(ar);
778                         
779                         break;
780                         
781                 case LEFTMOUSE:
782                 case MIDDLEMOUSE:
783                 case RIGHTMOUSE:
784                         
785                         MEM_freeN(nvm);
786                         op->customdata= NULL;
787             
788                         WM_event_add_notifier(C, NC_SPACE|ND_SPACE_NODE, NULL);
789                         
790                         return OPERATOR_FINISHED;
791         }
792         
793         return OPERATOR_RUNNING_MODAL;
794 }
795
796 static int snode_bg_viewmove_invoke(bContext *C, wmOperator *op, wmEvent *event)
797 {
798         ARegion *ar= CTX_wm_region(C);
799         NodeViewMove *nvm;
800         Image *ima;
801         ImBuf *ibuf;
802         int pad= 10;
803         void *lock;
804         
805         ima= BKE_image_verify_viewer(IMA_TYPE_COMPOSITE, "Viewer Node");
806         ibuf= BKE_image_acquire_ibuf(ima, NULL, &lock);
807         
808         if(ibuf == NULL) {
809                 BKE_image_release_ibuf(ima, lock);
810                 return OPERATOR_CANCELLED;
811         }
812
813         nvm= MEM_callocN(sizeof(NodeViewMove), "NodeViewMove struct");
814         op->customdata= nvm;
815         nvm->mvalo[0]= event->mval[0];
816         nvm->mvalo[1]= event->mval[1];
817
818         nvm->xmin = -(ar->winx/2) - ibuf->x/2 + pad;
819         nvm->xmax = ar->winx/2 + ibuf->x/2 - pad;
820         nvm->ymin = -(ar->winy/2) - ibuf->y/2 + pad;
821         nvm->ymax = ar->winy/2 + ibuf->y/2 - pad;
822
823         BKE_image_release_ibuf(ima, lock);
824         
825         /* add modal handler */
826         WM_event_add_modal_handler(C, op);
827         
828         return OPERATOR_RUNNING_MODAL;
829 }
830
831
832 void NODE_OT_backimage_move(wmOperatorType *ot)
833 {
834         /* identifiers */
835         ot->name= "Background Image Move";
836         ot->description = "Move Node backdrop";
837         ot->idname= "NODE_OT_backimage_move";
838         
839         /* api callbacks */
840         ot->invoke= snode_bg_viewmove_invoke;
841         ot->modal= snode_bg_viewmove_modal;
842         ot->poll= composite_node_active;
843         
844         /* flags */
845         ot->flag= OPTYPE_BLOCKING;
846 }
847
848 static int backimage_zoom(bContext *C, wmOperator *op)
849 {
850         SpaceNode *snode= CTX_wm_space_node(C);
851         ARegion *ar= CTX_wm_region(C);
852         float fac= RNA_float_get(op->ptr, "factor");
853
854         snode->zoom *= fac;
855         ED_region_tag_redraw(ar);
856
857         return OPERATOR_FINISHED;
858 }
859
860
861 void NODE_OT_backimage_zoom(wmOperatorType *ot)
862 {
863         
864         /* identifiers */
865         ot->name= "Background Image Zoom";
866         ot->idname= "NODE_OT_backimage_zoom";
867         
868         /* api callbacks */
869         ot->exec= backimage_zoom;
870         ot->poll= composite_node_active;
871         
872         /* flags */
873         ot->flag= OPTYPE_BLOCKING;
874
875         /* internal */
876         RNA_def_float(ot->srna, "factor", 1.2f, 0.0f, 10.0f, "Factor", "", 0.0f, 10.0f);
877 }
878
879 /******************** sample backdrop operator ********************/
880
881 typedef struct ImageSampleInfo {
882         ARegionType *art;
883         void *draw_handle;
884         int x, y;
885         int channels;
886         int color_manage;
887
888         char col[4];
889         float colf[4];
890
891         int draw;
892 } ImageSampleInfo;
893
894 static void sample_draw(const bContext *UNUSED(C), ARegion *ar, void *arg_info)
895 {
896         ImageSampleInfo *info= arg_info;
897
898         draw_nodespace_color_info(ar, info->channels, info->x, info->y, info->col, info->colf);
899 }
900
901 static void sample_apply(bContext *C, wmOperator *op, wmEvent *event)
902 {
903         SpaceNode *snode= CTX_wm_space_node(C);
904         ARegion *ar= CTX_wm_region(C);
905         ImageSampleInfo *info= op->customdata;
906         void *lock;
907         Image *ima;
908         ImBuf *ibuf;
909         float fx, fy, bufx, bufy;
910         int mx, my;
911         
912         ima= BKE_image_verify_viewer(IMA_TYPE_COMPOSITE, "Viewer Node");
913         ibuf= BKE_image_acquire_ibuf(ima, NULL, &lock);
914         if(!ibuf)
915                 return;
916         
917         if(!ibuf->rect) {
918                 if(info->color_manage)
919                         ibuf->profile = IB_PROFILE_LINEAR_RGB;
920                 else
921                         ibuf->profile = IB_PROFILE_NONE;
922                 IMB_rect_from_float(ibuf);
923         }
924
925         mx= event->x - ar->winrct.xmin;
926         my= event->y - ar->winrct.ymin;
927         /* map the mouse coords to the backdrop image space */
928         bufx = ibuf->x * snode->zoom;
929         bufy = ibuf->y * snode->zoom;
930         fx = (bufx > 0.0f ? ((float)mx - 0.5f*ar->winx - snode->xof) / bufx + 0.5f : 0.0f);
931         fy = (bufy > 0.0f ? ((float)my - 0.5f*ar->winy - snode->yof) / bufy + 0.5f : 0.0f);
932
933         if(fx>=0.0 && fy>=0.0 && fx<1.0 && fy<1.0) {
934                 float *fp;
935                 char *cp;
936                 int x= (int)(fx*ibuf->x), y= (int)(fy*ibuf->y);
937
938                 CLAMP(x, 0, ibuf->x-1);
939                 CLAMP(y, 0, ibuf->y-1);
940
941                 info->x= x;
942                 info->y= y;
943                 info->draw= 1;
944                 info->channels= ibuf->channels;
945
946                 if(ibuf->rect) {
947                         cp= (char *)(ibuf->rect + y*ibuf->x + x);
948
949                         info->col[0]= cp[0];
950                         info->col[1]= cp[1];
951                         info->col[2]= cp[2];
952                         info->col[3]= cp[3];
953
954                         info->colf[0]= (float)cp[0]/255.0f;
955                         info->colf[1]= (float)cp[1]/255.0f;
956                         info->colf[2]= (float)cp[2]/255.0f;
957                         info->colf[3]= (float)cp[3]/255.0f;
958                 }
959                 if(ibuf->rect_float) {
960                         fp= (ibuf->rect_float + (ibuf->channels)*(y*ibuf->x + x));
961
962                         info->colf[0]= fp[0];
963                         info->colf[1]= fp[1];
964                         info->colf[2]= fp[2];
965                         info->colf[3]= fp[3];
966                 }
967         }
968         else
969                 info->draw= 0;
970
971         BKE_image_release_ibuf(ima, lock);
972         
973         ED_area_tag_redraw(CTX_wm_area(C));
974 }
975
976 static void sample_exit(bContext *C, wmOperator *op)
977 {
978         ImageSampleInfo *info= op->customdata;
979
980         ED_region_draw_cb_exit(info->art, info->draw_handle);
981         ED_area_tag_redraw(CTX_wm_area(C));
982         MEM_freeN(info);
983 }
984
985 static int sample_invoke(bContext *C, wmOperator *op, wmEvent *event)
986 {
987         SpaceNode *snode= CTX_wm_space_node(C);
988         ARegion *ar= CTX_wm_region(C);
989         ImageSampleInfo *info;
990
991         if(snode->treetype!=NTREE_COMPOSIT || !(snode->flag & SNODE_BACKDRAW))
992                 return OPERATOR_CANCELLED;
993         
994         info= MEM_callocN(sizeof(ImageSampleInfo), "ImageSampleInfo");
995         info->art= ar->type;
996         info->draw_handle = ED_region_draw_cb_activate(ar->type, sample_draw, info, REGION_DRAW_POST_PIXEL);
997         op->customdata= info;
998
999         sample_apply(C, op, event);
1000
1001         WM_event_add_modal_handler(C, op);
1002
1003         return OPERATOR_RUNNING_MODAL;
1004 }
1005
1006 static int sample_modal(bContext *C, wmOperator *op, wmEvent *event)
1007 {
1008         switch(event->type) {
1009                 case LEFTMOUSE:
1010                 case RIGHTMOUSE: // XXX hardcoded
1011                         sample_exit(C, op);
1012                         return OPERATOR_CANCELLED;
1013                 case MOUSEMOVE:
1014                         sample_apply(C, op, event);
1015                         break;
1016         }
1017
1018         return OPERATOR_RUNNING_MODAL;
1019 }
1020
1021 static int sample_cancel(bContext *C, wmOperator *op)
1022 {
1023         sample_exit(C, op);
1024         return OPERATOR_CANCELLED;
1025 }
1026
1027 void NODE_OT_backimage_sample(wmOperatorType *ot)
1028 {
1029         /* identifiers */
1030         ot->name= "Backimage Sample";
1031         ot->idname= "NODE_OT_backimage_sample";
1032         
1033         /* api callbacks */
1034         ot->invoke= sample_invoke;
1035         ot->modal= sample_modal;
1036         ot->cancel= sample_cancel;
1037         ot->poll= ED_operator_node_active;
1038
1039         /* flags */
1040         ot->flag= OPTYPE_BLOCKING;
1041 }
1042
1043 /* ********************** size widget operator ******************** */
1044
1045 typedef struct NodeSizeWidget {
1046         float mxstart;
1047         float oldwidth;
1048 } NodeSizeWidget;
1049
1050 static int node_resize_modal(bContext *C, wmOperator *op, wmEvent *event)
1051 {
1052         SpaceNode *snode= CTX_wm_space_node(C);
1053         ARegion *ar= CTX_wm_region(C);
1054         bNode *node= editnode_get_active(snode->edittree);
1055         NodeSizeWidget *nsw= op->customdata;
1056         float mx, my;
1057         
1058         switch (event->type) {
1059                 case MOUSEMOVE:
1060                         
1061                         UI_view2d_region_to_view(&ar->v2d, event->x - ar->winrct.xmin, event->y - ar->winrct.ymin, 
1062                                                                          &mx, &my);
1063                         
1064                         if (node) {
1065                                 if(node->flag & NODE_HIDDEN) {
1066                                         node->miniwidth= nsw->oldwidth + mx - nsw->mxstart;
1067                                         CLAMP(node->miniwidth, 0.0f, 100.0f);
1068                                 }
1069                                 else {
1070                                         node->width= nsw->oldwidth + mx - nsw->mxstart;
1071                                         CLAMP(node->width, node->typeinfo->minwidth, node->typeinfo->maxwidth);
1072                                 }
1073                         }
1074                                 
1075                         ED_region_tag_redraw(ar);
1076
1077                         break;
1078                         
1079                 case LEFTMOUSE:
1080                 case MIDDLEMOUSE:
1081                 case RIGHTMOUSE:
1082                         
1083                         MEM_freeN(nsw);
1084                         op->customdata= NULL;
1085                         
1086                         return OPERATOR_FINISHED;
1087         }
1088         
1089         return OPERATOR_RUNNING_MODAL;
1090 }
1091
1092 static int node_resize_invoke(bContext *C, wmOperator *op, wmEvent *event)
1093 {
1094         SpaceNode *snode= CTX_wm_space_node(C);
1095         ARegion *ar= CTX_wm_region(C);
1096         bNode *node= editnode_get_active(snode->edittree);
1097         
1098         if(node) {
1099                 rctf totr;
1100                 
1101                 /* convert mouse coordinates to v2d space */
1102                 UI_view2d_region_to_view(&ar->v2d, event->x - ar->winrct.xmin, event->y - ar->winrct.ymin, 
1103                                                                  &snode->mx, &snode->my);
1104                 
1105                 /* rect we're interested in is just the bottom right corner */
1106                 totr= node->totr;
1107                 totr.xmin= totr.xmax-10.0f;
1108                 totr.ymax= totr.ymin+10.0f;
1109                 
1110                 if(BLI_in_rctf(&totr, snode->mx, snode->my)) {
1111                         NodeSizeWidget *nsw= MEM_callocN(sizeof(NodeSizeWidget), "size widget op data");
1112                         
1113                         op->customdata= nsw;
1114                         nsw->mxstart= snode->mx;
1115                         
1116                         /* store old */
1117                         if(node->flag & NODE_HIDDEN)
1118                                 nsw->oldwidth= node->miniwidth;
1119                         else
1120                                 nsw->oldwidth= node->width;
1121                         
1122                         /* add modal handler */
1123                         WM_event_add_modal_handler(C, op);
1124
1125                         return OPERATOR_RUNNING_MODAL;
1126                 }
1127         }
1128         return OPERATOR_CANCELLED|OPERATOR_PASS_THROUGH;
1129 }
1130
1131 void NODE_OT_resize(wmOperatorType *ot)
1132 {
1133         /* identifiers */
1134         ot->name= "Resize Node";
1135         ot->idname= "NODE_OT_resize";
1136         
1137         /* api callbacks */
1138         ot->invoke= node_resize_invoke;
1139         ot->modal= node_resize_modal;
1140         ot->poll= ED_operator_node_active;
1141         
1142         /* flags */
1143         ot->flag= OPTYPE_BLOCKING;
1144 }
1145
1146 /* ********************** select ******************** */
1147
1148
1149 /* no undo here! */
1150 void node_deselectall(SpaceNode *snode)
1151 {
1152         bNode *node;
1153         
1154         for(node= snode->edittree->nodes.first; node; node= node->next)
1155                 node->flag &= ~SELECT;
1156 }
1157
1158 /* return 1 if we need redraw otherwise zero. */
1159 int node_select_same_type(SpaceNode *snode)
1160 {
1161         bNode *nac, *p;
1162         int redraw;
1163
1164         /* search for the active node. */
1165         for (nac= snode->edittree->nodes.first; nac; nac= nac->next) {
1166                 if (nac->flag & SELECT)
1167                         break;
1168         }
1169
1170         /* no active node, return. */
1171         if (!nac)
1172                 return(0);
1173
1174         redraw= 0;
1175         for (p= snode->edittree->nodes.first; p; p= p->next) {
1176                 if (p->type != nac->type && p->flag & SELECT) {
1177                         /* if it's selected but different type, unselect */
1178                         redraw= 1;
1179                         p->flag &= ~SELECT;
1180                 }
1181                 else if (p->type == nac->type && (!(p->flag & SELECT))) {
1182                         /* if it's the same type and is not selected, select! */
1183                         redraw= 1;
1184                         p->flag |= SELECT;
1185                 }
1186         }
1187         return(redraw);
1188 }
1189
1190 /* return 1 if we need redraw, otherwise zero.
1191  * dir can be 0 == next or 0 != prev.
1192  */
1193 int node_select_same_type_np(SpaceNode *snode, int dir)
1194 {
1195         bNode *nac, *p;
1196
1197         /* search the active one. */
1198         for (nac= snode->edittree->nodes.first; nac; nac= nac->next) {
1199                 if (nac->flag & SELECT)
1200                         break;
1201         }
1202
1203         /* no active node, return. */
1204         if (!nac)
1205                 return(0);
1206
1207         if (dir == 0)
1208                 p= nac->next;
1209         else
1210                 p= nac->prev;
1211
1212         while (p) {
1213                 /* Now search the next with the same type. */
1214                 if (p->type == nac->type)
1215                         break;
1216
1217                 if (dir == 0)
1218                         p= p->next;
1219                 else
1220                         p= p->prev;
1221         }
1222
1223         if (p) {
1224                 node_deselectall(snode);
1225                 p->flag |= SELECT;
1226                 return(1);
1227         }
1228         return(0);
1229 }
1230
1231 int node_has_hidden_sockets(bNode *node)
1232 {
1233         bNodeSocket *sock;
1234         
1235         for(sock= node->inputs.first; sock; sock= sock->next)
1236                 if(sock->flag & SOCK_HIDDEN)
1237                         return 1;
1238         for(sock= node->outputs.first; sock; sock= sock->next)
1239                 if(sock->flag & SOCK_HIDDEN)
1240                         return 1;
1241         return 0;
1242 }
1243
1244 static void node_link_viewer(SpaceNode *snode, bNode *tonode)
1245 {
1246         bNode *node;
1247
1248         /* context check */
1249         if(tonode==NULL || tonode->outputs.first==NULL)
1250                 return;
1251         if( ELEM(tonode->type, CMP_NODE_VIEWER, CMP_NODE_SPLITVIEWER)) 
1252                 return;
1253         
1254         /* get viewer */
1255         for(node= snode->edittree->nodes.first; node; node= node->next)
1256                 if( ELEM(node->type, CMP_NODE_VIEWER, CMP_NODE_SPLITVIEWER)) 
1257                         if(node->flag & NODE_DO_OUTPUT)
1258                                 break;
1259         /* no viewer, we make one active */
1260         if(node==NULL) {
1261                 for(node= snode->edittree->nodes.first; node; node= node->next) {
1262                         if( ELEM(node->type, CMP_NODE_VIEWER, CMP_NODE_SPLITVIEWER)) {
1263                                 node->flag |= NODE_DO_OUTPUT;
1264                                 break;
1265                         }
1266                 }
1267         }
1268                 
1269         if(node) {
1270                 bNodeLink *link;
1271                 bNodeSocket *sock= NULL;
1272
1273                 /* try to find an already connected socket to cycle to the next */
1274                 for(link= snode->edittree->links.first; link; link= link->next)
1275                         if(link->tonode==node && link->fromnode==tonode)
1276                                 if(link->tosock==node->inputs.first)
1277                                         break;
1278
1279                 if(link) {
1280                         /* unlink existing connection */
1281                         sock= link->fromsock;
1282                         nodeRemLink(snode->edittree, link);
1283
1284                         /* find a socket after the previously connected socket */
1285                         for(sock=sock->next; sock; sock= sock->next)
1286                                 if(!(sock->flag & (SOCK_HIDDEN|SOCK_UNAVAIL)))
1287                                         break;
1288                 }
1289
1290                 /* find a socket starting from the first socket */
1291                 if(!sock) {
1292                         for(sock= tonode->outputs.first; sock; sock= sock->next)
1293                                 if(!(sock->flag & (SOCK_HIDDEN|SOCK_UNAVAIL)))
1294                                         break;
1295                 }
1296                 
1297                 if(sock) {
1298                         /* get link to viewer */
1299                         for(link= snode->edittree->links.first; link; link= link->next)
1300                                 if(link->tonode==node && link->tosock==node->inputs.first)
1301                                         break;
1302                         
1303                         if(link==NULL) {
1304                                 nodeAddLink(snode->edittree, tonode, sock, node, node->inputs.first);
1305                         }
1306                         else {
1307                                 link->fromnode= tonode;
1308                                 link->fromsock= sock;
1309                         }
1310                         ntreeSolveOrder(snode->edittree);
1311                         NodeTagChanged(snode->edittree, node);
1312                 }
1313         }
1314 }
1315
1316
1317 static int node_active_link_viewer(bContext *C, wmOperator *UNUSED(op))
1318 {
1319         SpaceNode *snode= CTX_wm_space_node(C);
1320         bNode *node;
1321         
1322         node= editnode_get_active(snode->edittree);
1323         
1324         if(!node)
1325                 return OPERATOR_CANCELLED;
1326
1327         ED_preview_kill_jobs(C);
1328
1329         node_link_viewer(snode, node);
1330         snode_notify(C, snode);
1331
1332         return OPERATOR_FINISHED;
1333 }
1334
1335
1336
1337 void NODE_OT_link_viewer(wmOperatorType *ot)
1338 {
1339         /* identifiers */
1340         ot->name= "Link to Viewer Node";
1341         ot->description = "Link to Viewer Node";
1342         ot->idname= "NODE_OT_link_viewer";
1343         
1344         /* api callbacks */
1345         ot->exec= node_active_link_viewer;
1346         ot->poll= ED_operator_node_active;
1347         
1348         /* flags */
1349         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
1350 }
1351
1352
1353
1354 /* return 0, nothing done */
1355 /*static*/ int node_mouse_groupheader(SpaceNode *snode)
1356 {
1357         bNode *gnode;
1358         float mx=0, my=0;
1359 // XXX  short mval[2];
1360         
1361         gnode= node_tree_get_editgroup(snode->nodetree);
1362         if(gnode==NULL) return 0;
1363         
1364 // XXX  getmouseco_areawin(mval);
1365 // XXX  areamouseco_to_ipoco(G.v2d, mval, &mx, &my);
1366         
1367         /* click in header or outside? */
1368         if(BLI_in_rctf(&gnode->totr, mx, my)==0) {
1369                 rctf rect= gnode->totr;
1370                 
1371                 rect.ymax += NODE_DY;
1372                 if(BLI_in_rctf(&rect, mx, my)==0)
1373                         snode_make_group_editable(snode, NULL); /* toggles, so exits editmode */
1374 //              else
1375 // XXX                  transform_nodes(snode->nodetree, 'g', "Move group");
1376                 
1377                 return 1;
1378         }
1379         return 0;
1380 }
1381
1382 /* checks snode->mouse position, and returns found node/socket */
1383 /* type is SOCK_IN and/or SOCK_OUT */
1384 static int find_indicated_socket(SpaceNode *snode, bNode **nodep, bNodeSocket **sockp, int in_out)
1385 {
1386         bNode *node;
1387         bNodeSocket *sock;
1388         rctf rect;
1389         
1390         /* check if we click in a socket */
1391         for(node= snode->edittree->nodes.first; node; node= node->next) {
1392                 
1393                 rect.xmin = snode->mx - (NODE_SOCKSIZE+4);
1394                 rect.ymin = snode->my - (NODE_SOCKSIZE+4);
1395                 rect.xmax = snode->mx + (NODE_SOCKSIZE+4);
1396                 rect.ymax = snode->my + (NODE_SOCKSIZE+4);
1397                 
1398                 if (!(node->flag & NODE_HIDDEN)) {
1399                         /* extra padding inside and out - allow dragging on the text areas too */
1400                         if (in_out == SOCK_IN) {
1401                                 rect.xmax += NODE_SOCKSIZE;
1402                                 rect.xmin -= NODE_SOCKSIZE*4;
1403                         } else if (in_out == SOCK_OUT) {
1404                                 rect.xmax += NODE_SOCKSIZE*4;
1405                                 rect.xmin -= NODE_SOCKSIZE;
1406                         }
1407                 }
1408                 
1409                 if(in_out & SOCK_IN) {
1410                         for(sock= node->inputs.first; sock; sock= sock->next) {
1411                                 if(!(sock->flag & (SOCK_HIDDEN|SOCK_UNAVAIL))) {
1412                                         if(BLI_in_rctf(&rect, sock->locx, sock->locy)) {
1413                                                 if(node == visible_node(snode, &rect)) {
1414                                                         *nodep= node;
1415                                                         *sockp= sock;
1416                                                         return 1;
1417                                                 }
1418                                         }
1419                                 }
1420                         }
1421                 }
1422                 if(in_out & SOCK_OUT) {
1423                         for(sock= node->outputs.first; sock; sock= sock->next) {
1424                                 if(!(sock->flag & (SOCK_HIDDEN|SOCK_UNAVAIL))) {
1425                                         if(BLI_in_rctf(&rect, sock->locx, sock->locy)) {
1426                                                 if(node == visible_node(snode, &rect)) {
1427                                                         *nodep= node;
1428                                                         *sockp= sock;
1429                                                         return 1;
1430                                                 }
1431                                         }
1432                                 }
1433                         }
1434                 }
1435         }
1436         return 0;
1437 }
1438
1439 static int node_socket_hilights(SpaceNode *snode, int in_out)
1440 {
1441         bNode *node;
1442         bNodeSocket *sock, *tsock, *socksel= NULL;
1443         short redraw= 0;
1444         
1445         if(snode->edittree==NULL) return 0;
1446         
1447         /* deselect sockets */
1448         for(node= snode->edittree->nodes.first; node; node= node->next) {
1449                 for(sock= node->inputs.first; sock; sock= sock->next) {
1450                         if(sock->flag & SELECT) {
1451                                 sock->flag &= ~SELECT;
1452                                 redraw++;
1453                                 socksel= sock;
1454                         }
1455                 }
1456                 for(sock= node->outputs.first; sock; sock= sock->next) {
1457                         if(sock->flag & SELECT) {
1458                                 sock->flag &= ~SELECT;
1459                                 redraw++;
1460                                 socksel= sock;
1461                         }
1462                 }
1463         }
1464         
1465         // XXX mousepos should be set here!
1466         
1467         if(find_indicated_socket(snode, &node, &tsock, in_out)) {
1468                 tsock->flag |= SELECT;
1469                 if(redraw==1 && tsock==socksel) redraw= 0;
1470                 else redraw= 1;
1471         }
1472         
1473         return redraw;
1474 }
1475
1476 /* ****************** Add *********************** */
1477
1478
1479 typedef struct bNodeListItem {
1480         struct bNodeListItem *next, *prev;
1481         struct bNode *node;     
1482 } bNodeListItem;
1483
1484 int sort_nodes_locx(void *a, void *b)
1485 {
1486         bNodeListItem *nli1 = (bNodeListItem *)a;
1487         bNodeListItem *nli2 = (bNodeListItem *)b;
1488         bNode *node1 = nli1->node;
1489         bNode *node2 = nli2->node;
1490         
1491         if (node1->locx > node2->locx)
1492                 return 1;
1493         else 
1494                 return 0;
1495 }
1496
1497 static int socket_is_available(bNodeTree *ntree, bNodeSocket *sock, int allow_used)
1498 {
1499         if (sock->flag & (SOCK_HIDDEN|SOCK_UNAVAIL))
1500                 return 0;
1501         
1502         if (!allow_used) {
1503                 if (nodeCountSocketLinks(ntree, sock) > 0)
1504                         return 0;
1505         }
1506         return 1;
1507 }
1508
1509 static bNodeSocket *best_socket_output(bNodeTree *ntree, bNode *node, bNodeSocket *sock_target, int allow_multiple)
1510 {
1511         bNodeSocket *sock;
1512         
1513         /* first try to find a socket with a matching name */
1514         for (sock=node->outputs.first; sock; sock=sock->next) {
1515
1516                 if (!socket_is_available(ntree, sock, allow_multiple))
1517                         continue;
1518
1519                 /* check for same types */
1520                 if (sock->type == sock_target->type) {
1521                         if (strcmp(sock->name, sock_target->name)==0)
1522                                 return sock;
1523                 }
1524         }
1525         
1526         /* otherwise settle for the first available socket of the right type */
1527         for (sock=node->outputs.first; sock; sock=sock->next) {
1528
1529                 if (!socket_is_available(ntree, sock, allow_multiple))
1530                         continue;
1531                 
1532                 /* check for same types */
1533                 if (sock->type == sock_target->type) {
1534                         return sock;
1535                 }
1536         }
1537         
1538         return NULL;
1539 }
1540
1541 /* this is a bit complicated, but designed to prioritise finding 
1542  * sockets of higher types, such as image, first */
1543 static bNodeSocket *best_socket_input(bNodeTree *ntree, bNode *node, int num, int replace)
1544 {
1545         bNodeSocket *sock;
1546         int socktype, maxtype=0;
1547         int a = 0;
1548         
1549         for (sock=node->inputs.first; sock; sock=sock->next) {
1550                 maxtype = MAX2(sock->type, maxtype);
1551         }
1552         
1553         /* find sockets of higher 'types' first (i.e. image) */
1554         for (socktype=maxtype; socktype >= 0; socktype--) {
1555                 for (sock=node->inputs.first; sock; sock=sock->next) {
1556                         
1557                         if (!socket_is_available(ntree, sock, replace)) {
1558                                 a++;
1559                                 continue;
1560                         }
1561                                 
1562                         if (sock->type == socktype) {
1563                                 /* increment to make sure we don't keep finding 
1564                                  * the same socket on every attempt running this function */
1565                                 a++;
1566                                 if (a > num)
1567                                         return sock;
1568                         }
1569                 }
1570         }
1571         
1572         return NULL;
1573 }
1574
1575 void snode_autoconnect(SpaceNode *snode, int allow_multiple, int replace)
1576 {
1577         ListBase *nodelist = MEM_callocN(sizeof(ListBase), "items_list");
1578         bNodeListItem *nli;
1579         bNode *node;
1580         int i, numlinks=0;
1581         
1582         for(node= snode->edittree->nodes.first; node; node= node->next) {
1583                 if(node->flag & NODE_SELECT) {
1584                         nli = MEM_mallocN(sizeof(bNodeListItem), "temporary node list item");
1585                         nli->node = node;
1586                         BLI_addtail(nodelist, nli);
1587                 }
1588         }
1589         
1590         /* sort nodes left to right */
1591         BLI_sortlist(nodelist, sort_nodes_locx);
1592         
1593         for (nli=nodelist->first; nli; nli=nli->next) {
1594                 bNode *node_fr, *node_to;
1595                 bNodeSocket *sock_fr, *sock_to;
1596                 
1597                 if (nli->next == NULL) break;
1598                 
1599                 node_fr = nli->node;
1600                 node_to = nli->next->node;
1601                 
1602                 /* check over input sockets first */
1603                 for (i=0; i<BLI_countlist(&node_to->inputs); i++) {
1604                         
1605                         /* find the best guess input socket */
1606                         sock_to = best_socket_input(snode->edittree, node_to, i, replace);
1607                         if (!sock_to) continue;
1608                         
1609                         /* check for an appropriate output socket to connect from */
1610                         sock_fr = best_socket_output(snode->edittree, node_fr, sock_to, allow_multiple);
1611                         if (!sock_fr) continue;
1612                         
1613                         /* then we can connect */
1614                         if (replace)
1615                                 nodeRemSocketLinks(snode->edittree, sock_to);
1616                         nodeAddLink(snode->edittree, node_fr, sock_fr, node_to, sock_to);
1617                         NodeTagChanged(snode->edittree, node_to);
1618                         ++numlinks;
1619                         break;
1620                 }
1621         }
1622         
1623         if (numlinks > 0) {
1624                 node_tree_verify_groups(snode->nodetree);
1625                 ntreeSolveOrder(snode->edittree);
1626         }
1627         
1628         BLI_freelistN(nodelist);
1629         MEM_freeN(nodelist);
1630 }
1631
1632 /* can be called from menus too, but they should do own undopush and redraws */
1633 bNode *node_add_node(SpaceNode *snode, Scene *scene, int type, float locx, float locy)
1634 {
1635         bNode *node= NULL, *gnode;
1636         
1637         node_deselectall(snode);
1638         
1639         if(type>=NODE_DYNAMIC_MENU) {
1640                 node= nodeAddNodeType(snode->edittree, type, NULL, NULL);
1641         }
1642         else if(type>=NODE_GROUP_MENU) {
1643                 if(snode->edittree!=snode->nodetree) {
1644                         // XXX error("Can not add a Group in a Group");
1645                         return NULL;
1646                 }
1647                 else {
1648                         bNodeTree *ngroup= BLI_findlink(&G.main->nodetree, type-NODE_GROUP_MENU);
1649                         if(ngroup)
1650                                 node= nodeAddNodeType(snode->edittree, NODE_GROUP, ngroup, NULL);
1651                 }
1652         }
1653         else
1654                 node= nodeAddNodeType(snode->edittree, type, NULL, NULL);
1655         
1656         /* generics */
1657         if(node) {
1658                 node->locx= locx;
1659                 node->locy= locy + 60.0f;               // arbitrary.. so its visible, (0,0) is top of node
1660                 node->flag |= SELECT;
1661                 
1662                 gnode= node_tree_get_editgroup(snode->nodetree);
1663                 if(gnode) {
1664                         node->locx -= gnode->locx;
1665                         node->locy -= gnode->locy;
1666                 }
1667
1668                 node_tree_verify_groups(snode->nodetree);
1669                 node_set_active(snode, node);
1670                 
1671                 if(snode->nodetree->type==NTREE_COMPOSIT) {
1672                         if(ELEM4(node->type, CMP_NODE_R_LAYERS, CMP_NODE_COMPOSITE, CMP_NODE_DEFOCUS, CMP_NODE_OUTPUT_FILE))
1673                                 node->id = &scene->id;
1674                         
1675                         ntreeCompositForceHidden(snode->edittree, scene);
1676                 }
1677                         
1678                 if(node->id)
1679                         id_us_plus(node->id);
1680                         
1681                 NodeTagChanged(snode->edittree, node);
1682         }
1683         
1684         if(snode->nodetree->type==NTREE_TEXTURE) {
1685                 ntreeTexCheckCyclics(snode->edittree);
1686         }
1687         
1688         return node;
1689 }
1690
1691 /* ****************** Duplicate *********************** */
1692
1693 static int node_duplicate_exec(bContext *C, wmOperator *UNUSED(op))
1694 {
1695         SpaceNode *snode= CTX_wm_space_node(C);
1696         bNode *node;
1697         
1698         ED_preview_kill_jobs(C);
1699
1700         /* simple id user adjustment, node internal functions dont touch this
1701          * but operators and readfile.c do. */
1702         for(node= snode->edittree->nodes.first; node; node= node->next) {
1703                 if(node->flag & SELECT) {
1704                         id_us_plus(node->id);
1705                 }
1706         }
1707
1708         ntreeCopyTree(snode->edittree, 1);      /* 1 == internally selected nodes */
1709         
1710         /* to ensure redraws or rerenders happen */
1711         for(node= snode->edittree->nodes.first; node; node= node->next)
1712                 if(node->flag & SELECT)
1713                         if(node->id)
1714                                 ED_node_changed_update(snode->id, node);
1715         
1716         ntreeSolveOrder(snode->edittree);
1717         node_tree_verify_groups(snode->nodetree);
1718         snode_notify(C, snode);
1719
1720         return OPERATOR_FINISHED;
1721 }
1722
1723 void NODE_OT_duplicate(wmOperatorType *ot)
1724 {
1725         /* identifiers */
1726         ot->name= "Duplicate Nodes";
1727         ot->description = "Duplicate the nodes";
1728         ot->idname= "NODE_OT_duplicate";
1729         
1730         /* api callbacks */
1731         ot->exec= node_duplicate_exec;
1732         ot->poll= ED_operator_node_active;
1733         
1734         /* flags */
1735         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
1736 }
1737
1738 /* *************************** add link op ******************** */
1739
1740 /* temp data to pass on to modal */
1741 typedef struct NodeLinkDrag
1742 {
1743         bNode *node;
1744         bNodeSocket *sock;
1745         bNodeLink *link;
1746         int in_out;
1747 } NodeLinkDrag;
1748
1749 static void node_remove_extra_links(SpaceNode *snode, bNodeSocket *tsock, bNodeLink *link)
1750 {
1751         bNodeLink *tlink;
1752         bNodeSocket *sock;
1753         
1754         if(tsock && nodeCountSocketLinks(snode->edittree, link->tosock) > tsock->limit) {
1755                 
1756                 for(tlink= snode->edittree->links.first; tlink; tlink= tlink->next) {
1757                         if(link!=tlink && tlink->tosock==link->tosock)
1758                                 break;
1759                 }
1760                 if(tlink) {
1761                         /* is there a free input socket with same type? */
1762                         for(sock= tlink->tonode->inputs.first; sock; sock= sock->next) {
1763                                 if(sock->type==tlink->fromsock->type)
1764                                         if(nodeCountSocketLinks(snode->edittree, sock) < sock->limit)
1765                                                 break;
1766                         }
1767                         if(sock) {
1768                                 tlink->tosock= sock;
1769                                 sock->flag &= ~SOCK_HIDDEN;
1770                         }
1771                         else {
1772                                 nodeRemLink(snode->edittree, tlink);
1773                         }
1774                 }
1775         }
1776 }
1777
1778 /* loop that adds a nodelink, called by function below  */
1779 /* in_out = starting socket */
1780 static int node_link_modal(bContext *C, wmOperator *op, wmEvent *event)
1781 {
1782         SpaceNode *snode= CTX_wm_space_node(C);
1783         ARegion *ar= CTX_wm_region(C);
1784         NodeLinkDrag *nldrag= op->customdata;
1785         bNode *tnode, *node;
1786         bNodeSocket *tsock= NULL, *sock;
1787         bNodeLink *link;
1788         int in_out;
1789
1790         in_out= nldrag->in_out;
1791         node= nldrag->node;
1792         sock= nldrag->sock;
1793         link= nldrag->link;
1794         
1795         UI_view2d_region_to_view(&ar->v2d, event->x - ar->winrct.xmin, event->y - ar->winrct.ymin, 
1796                                                          &snode->mx, &snode->my);
1797
1798         switch (event->type) {
1799                 case MOUSEMOVE:
1800                         
1801                         if(in_out==SOCK_OUT) {
1802                                 if(find_indicated_socket(snode, &tnode, &tsock, SOCK_IN)) {
1803                                         if(nodeFindLink(snode->edittree, sock, tsock)==NULL) {
1804                                                 if(tnode!=node  && link->tonode!=tnode && link->tosock!= tsock) {
1805                                                         link->tonode= tnode;
1806                                                         link->tosock= tsock;
1807                                                         ntreeSolveOrder(snode->edittree);       /* for interactive red line warning */
1808                                                 }
1809                                         }
1810                                 }
1811                                 else {
1812                                         link->tonode= NULL;
1813                                         link->tosock= NULL;
1814                                 }
1815                         }
1816                         else {
1817                                 if(find_indicated_socket(snode, &tnode, &tsock, SOCK_OUT)) {
1818                                         if(nodeFindLink(snode->edittree, sock, tsock)==NULL) {
1819                                                 if(nodeCountSocketLinks(snode->edittree, tsock) < tsock->limit) {
1820                                                         if(tnode!=node && link->fromnode!=tnode && link->fromsock!= tsock) {
1821                                                                 link->fromnode= tnode;
1822                                                                 link->fromsock= tsock;
1823                                                                 ntreeSolveOrder(snode->edittree);       /* for interactive red line warning */
1824                                                         }
1825                                                 }
1826                                         }
1827                                 }
1828                                 else {
1829                                         link->fromnode= NULL;
1830                                         link->fromsock= NULL;
1831                                 }
1832                         }
1833                         /* hilight target sockets only */
1834                         node_socket_hilights(snode, in_out==SOCK_OUT?SOCK_IN:SOCK_OUT);
1835                         ED_region_tag_redraw(ar);
1836                         break;
1837                         
1838                 case LEFTMOUSE:
1839                 case RIGHTMOUSE:
1840                 case MIDDLEMOUSE:
1841         
1842                         /* remove link? */
1843                         if(link->tonode==NULL || link->fromnode==NULL) {
1844                                 nodeRemLink(snode->edittree, link);
1845                         }
1846                         else {
1847                                 /* send changed events for original tonode and new */
1848                                 if(link->tonode) 
1849                                         NodeTagChanged(snode->edittree, link->tonode);
1850                                 
1851                                 /* we might need to remove a link */
1852                                 if(in_out==SOCK_OUT) node_remove_extra_links(snode, link->tosock, link);
1853                         }
1854                         
1855                         ntreeSolveOrder(snode->edittree);
1856                         node_tree_verify_groups(snode->nodetree);
1857                         snode_notify(C, snode);
1858                         
1859                         MEM_freeN(op->customdata);
1860                         op->customdata= NULL;
1861                         
1862                         return OPERATOR_FINISHED;
1863         }
1864         
1865         return OPERATOR_RUNNING_MODAL;
1866 }
1867
1868 /* return 1 when socket clicked */
1869 static int node_link_init(SpaceNode *snode, NodeLinkDrag *nldrag)
1870 {
1871         bNodeLink *link;
1872
1873         /* output indicated? */
1874         if(find_indicated_socket(snode, &nldrag->node, &nldrag->sock, SOCK_OUT)) {
1875                 if(nodeCountSocketLinks(snode->edittree, nldrag->sock) < nldrag->sock->limit)
1876                         return SOCK_OUT;
1877                 else {
1878                         /* find if we break a link */
1879                         for(link= snode->edittree->links.first; link; link= link->next) {
1880                                 if(link->fromsock==nldrag->sock)
1881                                         break;
1882                         }
1883                         if(link) {
1884                                 nldrag->node= link->tonode;
1885                                 nldrag->sock= link->tosock;
1886                                 nodeRemLink(snode->edittree, link);
1887                                 return SOCK_IN;
1888                         }
1889                 }
1890         }
1891         /* or an input? */
1892         else if(find_indicated_socket(snode, &nldrag->node, &nldrag->sock, SOCK_IN)) {
1893                 if(nodeCountSocketLinks(snode->edittree, nldrag->sock) < nldrag->sock->limit)
1894                         return SOCK_IN;
1895                 else {
1896                         /* find if we break a link */
1897                         for(link= snode->edittree->links.first; link; link= link->next) {
1898                                 if(link->tosock==nldrag->sock)
1899                                         break;
1900                         }
1901                         if(link) {
1902                                 /* send changed event to original tonode */
1903                                 if(link->tonode) 
1904                                         NodeTagChanged(snode->edittree, link->tonode);
1905                                 
1906                                 nldrag->node= link->fromnode;
1907                                 nldrag->sock= link->fromsock;
1908                                 nodeRemLink(snode->edittree, link);
1909                                 return SOCK_OUT;
1910                         }
1911                 }
1912         }
1913         
1914         return 0;
1915 }
1916
1917 static int node_link_invoke(bContext *C, wmOperator *op, wmEvent *event)
1918 {
1919         SpaceNode *snode= CTX_wm_space_node(C);
1920         ARegion *ar= CTX_wm_region(C);
1921         NodeLinkDrag *nldrag= MEM_callocN(sizeof(NodeLinkDrag), "drag link op customdata");
1922         
1923         UI_view2d_region_to_view(&ar->v2d, event->x - ar->winrct.xmin, event->y - ar->winrct.ymin, 
1924                                                          &snode->mx, &snode->my);
1925
1926         ED_preview_kill_jobs(C);
1927
1928         nldrag->in_out= node_link_init(snode, nldrag);
1929                 
1930         if(nldrag->in_out) {
1931                 op->customdata= nldrag;
1932                 
1933                 /* we make a temporal link */
1934                 if(nldrag->in_out==SOCK_OUT)
1935                         nldrag->link= nodeAddLink(snode->edittree, nldrag->node, nldrag->sock, NULL, NULL);
1936                 else
1937                         nldrag->link= nodeAddLink(snode->edittree, NULL, NULL, nldrag->node, nldrag->sock);
1938                 
1939                 /* add modal handler */
1940                 WM_event_add_modal_handler(C, op);
1941                 
1942                 return OPERATOR_RUNNING_MODAL;
1943         }
1944         else {
1945                 MEM_freeN(nldrag);
1946                 return OPERATOR_CANCELLED|OPERATOR_PASS_THROUGH;
1947         }
1948 }
1949
1950 void NODE_OT_link(wmOperatorType *ot)
1951 {
1952         /* identifiers */
1953         ot->name= "Link Nodes";
1954         ot->idname= "NODE_OT_link";
1955         
1956         /* api callbacks */
1957         ot->invoke= node_link_invoke;
1958         ot->modal= node_link_modal;
1959 //      ot->exec= node_link_exec;
1960         ot->poll= ED_operator_node_active;
1961         
1962         /* flags */
1963         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO|OPTYPE_BLOCKING;
1964 }
1965
1966 /* ********************** Make Link operator ***************** */
1967
1968 /* makes a link between selected output and input sockets */
1969 static int node_make_link_exec(bContext *C, wmOperator *op)
1970 {
1971         SpaceNode *snode= CTX_wm_space_node(C);
1972         int replace = RNA_boolean_get(op->ptr, "replace");
1973
1974         ED_preview_kill_jobs(C);
1975
1976         snode_autoconnect(snode, 1, replace);
1977
1978         node_tree_verify_groups(snode->nodetree);
1979         snode_notify(C, snode);
1980         
1981         return OPERATOR_FINISHED;
1982 }
1983
1984 void NODE_OT_link_make(wmOperatorType *ot)
1985 {
1986         /* identifiers */
1987         ot->name= "Make Links";
1988         ot->description= "Makes a link between selected output in input sockets";
1989         ot->idname= "NODE_OT_link_make";
1990         
1991         /* callbacks */
1992         ot->exec= node_make_link_exec;
1993         ot->poll= ED_operator_node_active; // XXX we need a special poll which checks that there are selected input/output sockets
1994         
1995         /* flags */
1996         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
1997         
1998         RNA_def_boolean(ot->srna, "replace", 0, "Replace", "Replace socket connections with the new links");
1999 }
2000
2001 /* ********************** Cut Link operator ***************** */
2002
2003 #define LINK_RESOL 12
2004 static int cut_links_intersect(bNodeLink *link, float mcoords[][2], int tot)
2005 {
2006         float coord_array[LINK_RESOL+1][2];
2007         int i, b;
2008         
2009         if(node_link_bezier_points(NULL, NULL, link, coord_array, LINK_RESOL)) {
2010
2011                 for(i=0; i<tot-1; i++)
2012                         for(b=0; b<LINK_RESOL; b++)
2013                                 if(isect_line_line_v2(mcoords[i], mcoords[i+1], coord_array[b], coord_array[b+1]) > 0)
2014                                         return 1;
2015         }
2016         return 0;
2017 }
2018
2019 static int cut_links_exec(bContext *C, wmOperator *op)
2020 {
2021         SpaceNode *snode= CTX_wm_space_node(C);
2022         ARegion *ar= CTX_wm_region(C);
2023         float mcoords[256][2];
2024         int i= 0;
2025         
2026         RNA_BEGIN(op->ptr, itemptr, "path") {
2027                 float loc[2];
2028                 
2029                 RNA_float_get_array(&itemptr, "loc", loc);
2030                 UI_view2d_region_to_view(&ar->v2d, (short)loc[0], (short)loc[1], 
2031                                                                  &mcoords[i][0], &mcoords[i][1]);
2032                 i++;
2033                 if(i>= 256) break;
2034         }
2035         RNA_END;
2036         
2037         if(i>1) {
2038                 bNodeLink *link, *next;
2039
2040                 ED_preview_kill_jobs(C);
2041                 
2042                 for(link= snode->edittree->links.first; link; link= next) {
2043                         next= link->next;
2044                         
2045                         if(cut_links_intersect(link, mcoords, i)) {
2046                                 NodeTagChanged(snode->edittree, link->tonode);
2047                                 nodeRemLink(snode->edittree, link);
2048                         }
2049                 }
2050                 
2051                 ntreeSolveOrder(snode->edittree);
2052                 node_tree_verify_groups(snode->nodetree);
2053                 snode_notify(C, snode);
2054                 
2055                 return OPERATOR_FINISHED;
2056         }
2057         
2058         return OPERATOR_CANCELLED|OPERATOR_PASS_THROUGH;
2059 }
2060
2061 void NODE_OT_links_cut(wmOperatorType *ot)
2062 {
2063         PropertyRNA *prop;
2064         
2065         ot->name= "Cut links";
2066         ot->idname= "NODE_OT_links_cut";
2067         
2068         ot->invoke= WM_gesture_lines_invoke;
2069         ot->modal= WM_gesture_lines_modal;
2070         ot->exec= cut_links_exec;
2071         
2072         ot->poll= ED_operator_node_active;
2073         
2074         /* flags */
2075         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
2076         
2077         prop= RNA_def_property(ot->srna, "path", PROP_COLLECTION, PROP_NONE);
2078         RNA_def_property_struct_runtime(prop, &RNA_OperatorMousePath);
2079         /* internal */
2080         RNA_def_int(ot->srna, "cursor", BC_KNIFECURSOR, 0, INT_MAX, "Cursor", "", 0, INT_MAX);
2081 }
2082
2083 /* ******************************** */
2084 // XXX some code needing updating to operators...
2085
2086
2087 /* goes over all scenes, reads render layers */
2088 static int node_read_renderlayers_exec(bContext *C, wmOperator *UNUSED(op))
2089 {
2090         Main *bmain= CTX_data_main(C);
2091         SpaceNode *snode= CTX_wm_space_node(C);
2092         Scene *curscene= CTX_data_scene(C), *scene;
2093         bNode *node;
2094
2095         ED_preview_kill_jobs(C);
2096
2097         /* first tag scenes unread */
2098         for(scene= bmain->scene.first; scene; scene= scene->id.next) 
2099                 scene->id.flag |= LIB_DOIT;
2100
2101         for(node= snode->edittree->nodes.first; node; node= node->next) {
2102                 if(node->type==CMP_NODE_R_LAYERS) {
2103                         ID *id= node->id;
2104                         if(id->flag & LIB_DOIT) {
2105                                 RE_ReadRenderResult(curscene, (Scene *)id);
2106                                 ntreeCompositTagRender((Scene *)id);
2107                                 id->flag &= ~LIB_DOIT;
2108                         }
2109                 }
2110         }
2111         
2112         snode_notify(C, snode);
2113         return OPERATOR_FINISHED;
2114 }
2115
2116 void NODE_OT_read_renderlayers(wmOperatorType *ot)
2117 {
2118         
2119         ot->name= "Read Render Layers";
2120         ot->idname= "NODE_OT_read_renderlayers";
2121         
2122         ot->exec= node_read_renderlayers_exec;
2123         
2124         ot->poll= composite_node_active;
2125         
2126         /* flags */
2127         ot->flag= 0;
2128 }
2129
2130 static int node_read_fullsamplelayers_exec(bContext *C, wmOperator *UNUSED(op))
2131 {
2132         Main *bmain= CTX_data_main(C);
2133         SpaceNode *snode= CTX_wm_space_node(C);
2134         Scene *curscene= CTX_data_scene(C);
2135         Render *re= RE_NewRender(curscene->id.name);
2136
2137         WM_cursor_wait(1);
2138
2139         RE_MergeFullSample(re, bmain, curscene, snode->nodetree);
2140         snode_notify(C, snode);
2141         
2142         WM_cursor_wait(0);
2143         return OPERATOR_FINISHED;
2144 }
2145
2146
2147 void NODE_OT_read_fullsamplelayers(wmOperatorType *ot)
2148 {
2149         
2150         ot->name= "Read Full Sample Layers";
2151         ot->idname= "NODE_OT_read_fullsamplelayers";
2152         
2153         ot->exec= node_read_fullsamplelayers_exec;
2154         
2155         ot->poll= composite_node_active;
2156         
2157         /* flags */
2158         ot->flag= 0;
2159 }
2160
2161
2162 /* ****************** Make Group operator ******************* */
2163
2164 static int node_group_make_exec(bContext *C, wmOperator *op)
2165 {
2166         SpaceNode *snode = CTX_wm_space_node(C);
2167         bNode *gnode;
2168         
2169         if(snode->edittree!=snode->nodetree) {
2170                 BKE_report(op->reports, RPT_WARNING, "Can not add a new Group in a Group");
2171                 return OPERATOR_CANCELLED;
2172         }
2173         
2174         /* for time being... is too complex to handle */
2175         if(snode->treetype==NTREE_COMPOSIT) {
2176                 for(gnode=snode->nodetree->nodes.first; gnode; gnode= gnode->next) {
2177                         if(gnode->flag & SELECT)
2178                                 if(gnode->type==CMP_NODE_R_LAYERS)
2179                                         break;
2180                 }
2181                 
2182                 if(gnode) {
2183                         BKE_report(op->reports, RPT_WARNING, "Can not add RenderLayer in a Group");
2184                         return OPERATOR_CANCELLED;
2185                 }
2186         }
2187
2188         ED_preview_kill_jobs(C);
2189         
2190         gnode= nodeMakeGroupFromSelected(snode->nodetree);
2191         if(gnode==NULL) {
2192                 BKE_report(op->reports, RPT_WARNING, "Can not make Group");
2193                 return OPERATOR_CANCELLED;
2194         }
2195         else {
2196                 nodeSetActive(snode->nodetree, gnode);
2197                 ntreeSolveOrder(snode->nodetree);
2198         }
2199         
2200         snode_notify(C, snode);
2201         
2202         return OPERATOR_FINISHED;
2203 }
2204
2205 void NODE_OT_group_make(wmOperatorType *ot)
2206 {
2207         /* identifiers */
2208         ot->name = "Group";
2209         ot->description = "Make group from selected nodes";
2210         ot->idname = "NODE_OT_group_make";
2211         
2212         /* api callbacks */
2213         ot->exec = node_group_make_exec;
2214         ot->poll = ED_operator_node_active;
2215         
2216         /* flags */
2217         ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
2218 }
2219
2220 /* ****************** Hide operator *********************** */
2221
2222 static void node_flag_toggle_exec(SpaceNode *snode, int toggle_flag)
2223 {
2224         int tot_eq= 0, tot_neq= 0;
2225         bNode *node;
2226
2227         for(node= snode->edittree->nodes.first; node; node= node->next) {
2228                 if(node->flag & SELECT) {
2229
2230                         if(toggle_flag== NODE_PREVIEW && (node->typeinfo->flag & NODE_PREVIEW)==0)
2231                                 continue;
2232
2233                         if(node->flag & toggle_flag)
2234                                 tot_eq++;
2235                         else
2236                                 tot_neq++;
2237                 }
2238         }
2239         for(node= snode->edittree->nodes.first; node; node= node->next) {
2240                 if(node->flag & SELECT) {
2241
2242                         if(toggle_flag== NODE_PREVIEW && (node->typeinfo->flag & NODE_PREVIEW)==0)
2243                                 continue;
2244
2245                         if( (tot_eq && tot_neq) || tot_eq==0)
2246                                 node->flag |= toggle_flag;
2247                         else
2248                                 node->flag &= ~toggle_flag;
2249                 }
2250         }
2251 }
2252
2253 static int node_hide_exec(bContext *C, wmOperator *UNUSED(op))
2254 {
2255         SpaceNode *snode= CTX_wm_space_node(C);
2256         
2257         /* sanity checking (poll callback checks this already) */
2258         if((snode == NULL) || (snode->edittree == NULL))
2259                 return OPERATOR_CANCELLED;
2260         
2261         node_flag_toggle_exec(snode, NODE_HIDDEN);
2262         
2263         snode_notify(C, snode);
2264         
2265         return OPERATOR_FINISHED;
2266 }
2267
2268 void NODE_OT_hide_toggle(wmOperatorType *ot)
2269 {
2270         /* identifiers */
2271         ot->name= "Hide";
2272         ot->description= "Toggle hiding of selected nodes";
2273         ot->idname= "NODE_OT_hide_toggle";
2274         
2275         /* callbacks */
2276         ot->exec= node_hide_exec;
2277         ot->poll= ED_operator_node_active;
2278
2279         /* flags */
2280         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
2281 }
2282
2283 static int node_preview_exec(bContext *C, wmOperator *UNUSED(op))
2284 {
2285         SpaceNode *snode= CTX_wm_space_node(C);
2286
2287         /* sanity checking (poll callback checks this already) */
2288         if((snode == NULL) || (snode->edittree == NULL))
2289                 return OPERATOR_CANCELLED;
2290
2291         ED_preview_kill_jobs(C);
2292
2293         node_flag_toggle_exec(snode, NODE_PREVIEW);
2294
2295         snode_notify(C, snode);
2296
2297         return OPERATOR_FINISHED;
2298 }
2299
2300 void NODE_OT_preview_toggle(wmOperatorType *ot)
2301 {
2302         /* identifiers */
2303         ot->name= "Toggle Node Preview";
2304         ot->description= "Toggle preview display for selected nodes";
2305         ot->idname= "NODE_OT_preview_toggle";
2306
2307         /* callbacks */
2308         ot->exec= node_preview_exec;
2309         ot->poll= ED_operator_node_active;
2310
2311         /* flags */
2312         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
2313 }
2314
2315 static int node_socket_toggle_exec(bContext *C, wmOperator *UNUSED(op))
2316 {
2317         SpaceNode *snode= CTX_wm_space_node(C);
2318         bNode *node;
2319         int hidden= 0;
2320
2321         /* sanity checking (poll callback checks this already) */
2322         if((snode == NULL) || (snode->edittree == NULL))
2323                 return OPERATOR_CANCELLED;
2324
2325         ED_preview_kill_jobs(C);
2326
2327         for(node= snode->edittree->nodes.first; node; node= node->next) {
2328                 if(node->flag & SELECT) {
2329                         if(node_has_hidden_sockets(node)) {
2330                                 hidden= 1;
2331                                 break;
2332                         }
2333                 }
2334         }
2335
2336         for(node= snode->edittree->nodes.first; node; node= node->next) {
2337                 if(node->flag & SELECT) {
2338                         node_set_hidden_sockets(snode, node, !hidden);
2339                 }
2340         }
2341
2342         node_tree_verify_groups(snode->nodetree);
2343
2344         snode_notify(C, snode);
2345
2346         return OPERATOR_FINISHED;
2347 }
2348
2349 void NODE_OT_hide_socket_toggle(wmOperatorType *ot)
2350 {
2351         /* identifiers */
2352         ot->name= "Toggle Hidden Node Sockets";
2353         ot->description= "Toggle unused node socket display";
2354         ot->idname= "NODE_OT_hide_socket_toggle";
2355
2356         /* callbacks */
2357         ot->exec= node_socket_toggle_exec;
2358         ot->poll= ED_operator_node_active;
2359
2360         /* flags */
2361         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
2362 }
2363
2364 /* ****************** Mute operator *********************** */
2365
2366 static int node_mute_exec(bContext *C, wmOperator *UNUSED(op))
2367 {
2368         SpaceNode *snode= CTX_wm_space_node(C);
2369         bNode *node;
2370
2371         /* no disabling inside of groups */
2372         if(node_tree_get_editgroup(snode->nodetree))
2373                 return OPERATOR_CANCELLED;
2374         
2375         ED_preview_kill_jobs(C);
2376
2377         for(node= snode->edittree->nodes.first; node; node= node->next) {
2378                 if(node->flag & SELECT) {
2379                         if(node->inputs.first && node->outputs.first) {
2380                                 node->flag ^= NODE_MUTED;
2381                                 NodeTagChanged(snode->edittree, node);
2382                         }
2383                 }
2384         }
2385         
2386         snode_notify(C, snode);
2387         
2388         return OPERATOR_FINISHED;
2389 }
2390
2391 void NODE_OT_mute_toggle(wmOperatorType *ot)
2392 {
2393         /* identifiers */
2394         ot->name= "Toggle Node Mute";
2395         ot->description= "Toggle muting of the nodes";
2396         ot->idname= "NODE_OT_mute_toggle";
2397         
2398         /* callbacks */
2399         ot->exec= node_mute_exec;
2400         ot->poll= ED_operator_node_active;
2401         
2402         /* flags */
2403         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
2404 }
2405
2406 /* ****************** Delete operator ******************* */
2407
2408 static int node_delete_exec(bContext *C, wmOperator *UNUSED(op))
2409 {
2410         SpaceNode *snode= CTX_wm_space_node(C);
2411         bNode *node, *next;
2412         
2413         ED_preview_kill_jobs(C);
2414
2415         for(node= snode->edittree->nodes.first; node; node= next) {
2416                 next= node->next;
2417                 if(node->flag & SELECT) {
2418                         /* check id user here, nodeFreeNode is called for free dbase too */
2419                         if(node->id)
2420                                 node->id->us--;
2421                         nodeFreeNode(snode->edittree, node);
2422                 }
2423         }
2424         
2425         node_tree_verify_groups(snode->nodetree);
2426
2427         snode_notify(C, snode);
2428         
2429         return OPERATOR_FINISHED;
2430 }
2431
2432 void NODE_OT_delete(wmOperatorType *ot)
2433 {
2434         /* identifiers */
2435         ot->name= "Delete";
2436         ot->description = "Delete selected nodes";
2437         ot->idname= "NODE_OT_delete";
2438         
2439         /* api callbacks */
2440         ot->exec= node_delete_exec;
2441         ot->poll= ED_operator_node_active;
2442         
2443         /* flags */
2444         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
2445 }
2446
2447 /* ****************** Show Cyclic Dependencies Operator  ******************* */
2448
2449 static int node_show_cycles_exec(bContext *C, wmOperator *UNUSED(op))
2450 {
2451         SpaceNode *snode= CTX_wm_space_node(C);
2452         
2453         /* this is just a wrapper around this call... */
2454         ntreeSolveOrder(snode->edittree);
2455         snode_notify(C, snode);
2456         
2457         return OPERATOR_FINISHED;
2458 }
2459
2460 void NODE_OT_show_cyclic_dependencies(wmOperatorType *ot)
2461 {
2462         /* identifiers */
2463         ot->name= "Show Cyclic Dependencies";
2464         ot->description= "Sort the nodes and show the cyclic dependencies between the nodes";
2465         ot->idname= "NODE_OT_show_cyclic_dependencies";
2466         
2467         /* callbacks */
2468         ot->exec= node_show_cycles_exec;
2469         ot->poll= ED_operator_node_active;
2470         
2471         /* flags */
2472         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
2473 }
2474
2475 /* ****************** Add File Node Operator  ******************* */
2476
2477 static int node_add_file_exec(bContext *C, wmOperator *op)
2478 {
2479         Scene *scene= CTX_data_scene(C);
2480         SpaceNode *snode= CTX_wm_space_node(C);
2481         bNode *node;
2482         Image *ima= NULL;
2483         int ntype=0;
2484
2485         /* check input variables */
2486         if (RNA_property_is_set(op->ptr, "filepath"))
2487         {
2488                 char path[FILE_MAX];
2489                 RNA_string_get(op->ptr, "filepath", path);
2490
2491                 errno= 0;
2492
2493                 ima= BKE_add_image_file(path);
2494
2495                 if(!ima) {
2496                         BKE_reportf(op->reports, RPT_ERROR, "Can't read: \"%s\", %s.", path, errno ? strerror(errno) : "Unsupported image format");
2497                         return OPERATOR_CANCELLED;
2498                 }
2499         }
2500         else if(RNA_property_is_set(op->ptr, "name"))
2501         {
2502                 char name[32];
2503                 RNA_string_get(op->ptr, "name", name);
2504                 ima= (Image *)find_id("IM", name);
2505
2506                 if(!ima) {
2507                         BKE_reportf(op->reports, RPT_ERROR, "Image named \"%s\", not found.", name);
2508                         return OPERATOR_CANCELLED;
2509                 }
2510         }
2511         
2512         node_deselectall(snode);
2513         
2514         if (snode->nodetree->type==NTREE_COMPOSIT)
2515                 ntype = CMP_NODE_IMAGE;
2516
2517         ED_preview_kill_jobs(C);
2518         
2519         node = node_add_node(snode, scene, ntype, snode->mx, snode->my);
2520         
2521         if (!node) {
2522                 BKE_report(op->reports, RPT_WARNING, "Could not add an image node.");
2523                 return OPERATOR_CANCELLED;
2524         }
2525         
2526         node->id = (ID *)ima;
2527         
2528         snode_notify(C, snode);
2529         
2530         return OPERATOR_FINISHED;
2531 }
2532
2533 static int node_add_file_invoke(bContext *C, wmOperator *op, wmEvent *event)
2534 {
2535         ARegion *ar= CTX_wm_region(C);
2536         SpaceNode *snode= CTX_wm_space_node(C);
2537         
2538         /* convert mouse coordinates to v2d space */
2539         UI_view2d_region_to_view(&ar->v2d, event->x - ar->winrct.xmin, event->y - ar->winrct.ymin, 
2540                                                          &snode->mx, &snode->my);
2541         
2542         if (RNA_property_is_set(op->ptr, "filepath") || RNA_property_is_set(op->ptr, "name"))
2543                 return node_add_file_exec(C, op);
2544         else
2545                 return WM_operator_filesel(C, op, event);
2546 }
2547
2548 void NODE_OT_add_file(wmOperatorType *ot)
2549 {
2550         /* identifiers */
2551         ot->name= "Add File Node";
2552         ot->description= "Add a file node to the current node editor";
2553         ot->idname= "NODE_OT_add_file";
2554         
2555         /* callbacks */
2556         ot->exec= node_add_file_exec;
2557         ot->invoke= node_add_file_invoke;
2558         ot->poll= composite_node_active;
2559         
2560         /* flags */
2561         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
2562         
2563         WM_operator_properties_filesel(ot, FOLDERFILE|IMAGEFILE, FILE_SPECIAL, FILE_OPENFILE, WM_FILESEL_FILEPATH);  //XXX TODO, relative_path
2564         RNA_def_string(ot->srna, "name", "Image", 24, "Name", "Datablock name to assign.");
2565 }
2566