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