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