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