code cleanup: headers
[blender.git] / source / blender / editors / space_node / node_edit.c
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version. 
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * The Original Code is Copyright (C) 2005 Blender Foundation.
19  * All rights reserved.
20  *
21  * The Original Code is: all of this file.
22  *
23  * Contributor(s): David Millan Escriva, Juho Vepsäläinen, Nathan Letwory
24  *
25  * ***** END GPL LICENSE BLOCK *****
26  */
27
28 /** \file blender/editors/space_node/node_edit.c
29  *  \ingroup spnode
30  */
31
32 #include "MEM_guardedalloc.h"
33
34 #include "DNA_lamp_types.h"
35 #include "DNA_material_types.h"
36 #include "DNA_node_types.h"
37 #include "DNA_object_types.h"
38 #include "DNA_world_types.h"
39
40 #include "BLI_math.h"
41 #include "BLI_blenlib.h"
42
43 #include "BKE_context.h"
44 #include "BKE_depsgraph.h"
45 #include "BKE_global.h"
46 #include "BKE_image.h"
47 #include "BKE_library.h"
48 #include "BKE_main.h"
49 #include "BKE_node.h"
50 #include "BKE_material.h"
51 #include "BKE_paint.h"
52 #include "BKE_scene.h"
53 #include "BKE_screen.h"
54 #include "BKE_texture.h"
55
56 #include "RE_pipeline.h"
57
58 #include "IMB_imbuf_types.h"
59
60 #include "ED_node.h"  /* own include */
61 #include "ED_image.h"
62 #include "ED_screen.h"
63 #include "ED_space_api.h"
64 #include "ED_render.h"
65
66 #include "RNA_access.h"
67 #include "RNA_define.h"
68
69 #include "WM_api.h"
70 #include "WM_types.h"
71
72 #include "UI_view2d.h"
73
74 #include "IMB_imbuf.h"
75
76
77 #include "GPU_material.h"
78
79 #include "node_intern.h"  /* own include */
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) = TRUE;
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 == FALSE)
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, 0, 1);  /* 1 is do_previews */
166
167         ntree->test_break = NULL;
168         ntree->stats_draw = NULL;
169         ntree->progress = NULL;
170
171 }
172
173 void snode_composite_job(const bContext *C, ScrArea *sa)
174 {
175         SpaceNode *snode = sa->spacedata.first;
176         wmJob *steve;
177         CompoJob *cj;
178
179         steve = WM_jobs_get(CTX_wm_manager(C), CTX_wm_window(C), sa, "Compositing", WM_JOB_EXCL_RENDER | WM_JOB_PROGRESS);
180         cj = MEM_callocN(sizeof(CompoJob), "compo job");
181         
182         /* customdata for preview thread */
183         cj->scene = CTX_data_scene(C);
184         cj->ntree = snode->nodetree;
185         
186         /* setup job */
187         WM_jobs_customdata(steve, cj, compo_freejob);
188         WM_jobs_timer(steve, 0.1, NC_SCENE, NC_SCENE | ND_COMPO_RESULT);
189         WM_jobs_callbacks(steve, compo_startjob, compo_initjob, compo_updatejob, NULL);
190         
191         WM_jobs_start(CTX_wm_manager(C), steve);
192         
193 }
194
195 /* ***************************************** */
196
197 /* operator poll callback */
198 int composite_node_active(bContext *C)
199 {
200         if (ED_operator_node_active(C)) {
201                 SpaceNode *snode = CTX_wm_space_node(C);
202                 if (snode->treetype == NTREE_COMPOSIT)
203                         return 1;
204         }
205         return 0;
206 }
207
208 /* also checks for edited groups */
209 bNode *editnode_get_active(bNodeTree *ntree)
210 {
211         bNode *node;
212         
213         /* check for edited group */
214         for (node = ntree->nodes.first; node; node = node->next)
215                 if (nodeGroupEditGet(node))
216                         break;
217         if (node)
218                 return nodeGetActive((bNodeTree *)node->id);
219         else
220                 return nodeGetActive(ntree);
221 }
222
223 static int has_nodetree(bNodeTree *ntree, bNodeTree *lookup)
224 {
225         bNode *node;
226         
227         if (ntree == lookup)
228                 return 1;
229         
230         for (node = ntree->nodes.first; node; node = node->next)
231                 if (node->type == NODE_GROUP && node->id)
232                         if (has_nodetree((bNodeTree *)node->id, lookup))
233                                 return 1;
234         
235         return 0;
236 }
237
238 static void snode_dag_update_group(void *calldata, ID *owner_id, bNodeTree *ntree)
239 {
240         if (has_nodetree(ntree, calldata))
241                 DAG_id_tag_update(owner_id, 0);
242 }
243
244 void snode_dag_update(bContext *C, SpaceNode *snode)
245 {
246         Main *bmain = CTX_data_main(C);
247
248         /* for groups, update all ID's using this */
249         if (snode->edittree != snode->nodetree) {
250                 bNodeTreeType *tti = ntreeGetType(snode->edittree->type);
251                 tti->foreach_nodetree(bmain, snode->edittree, snode_dag_update_group);
252         }
253
254         DAG_id_tag_update(snode->id, 0);
255 }
256
257 void snode_notify(bContext *C, SpaceNode *snode)
258 {
259         WM_event_add_notifier(C, NC_NODE | NA_EDITED, NULL);
260
261         if (snode->treetype == NTREE_SHADER)
262                 WM_event_add_notifier(C, NC_MATERIAL | ND_NODES, snode->id);
263         else if (snode->treetype == NTREE_COMPOSIT)
264                 WM_event_add_notifier(C, NC_SCENE | ND_NODES, snode->id);
265         else if (snode->treetype == NTREE_TEXTURE)
266                 WM_event_add_notifier(C, NC_TEXTURE | ND_NODES, snode->id);
267 }
268
269 bNode *node_tree_get_editgroup(bNodeTree *nodetree)
270 {
271         bNode *gnode;
272         
273         /* get the groupnode */
274         for (gnode = nodetree->nodes.first; gnode; gnode = gnode->next)
275                 if (nodeGroupEditGet(gnode))
276                         break;
277         return gnode;
278 }
279
280 /* assumes nothing being done in ntree yet, sets the default in/out node */
281 /* called from shading buttons or header */
282 void ED_node_shader_default(Scene *scene, ID *id)
283 {
284         bNode *in, *out;
285         bNodeSocket *fromsock, *tosock, *sock;
286         bNodeTree *ntree;
287         bNodeTemplate ntemp;
288         int output_type, shader_type;
289         float color[3], strength = 1.0f;
290         
291         ntree = ntreeAddTree("Shader Nodetree", NTREE_SHADER, 0);
292
293         switch (GS(id->name)) {
294                 case ID_MA: {
295                         Material *ma = (Material *)id;
296                         ma->nodetree = ntree;
297
298                         if (BKE_scene_use_new_shading_nodes(scene)) {
299                                 output_type = SH_NODE_OUTPUT_MATERIAL;
300                                 shader_type = SH_NODE_BSDF_DIFFUSE;
301                         }
302                         else {
303                                 output_type = SH_NODE_OUTPUT;
304                                 shader_type = SH_NODE_MATERIAL;
305                         }
306
307                         copy_v3_v3(color, &ma->r);
308                         strength = 0.0f;
309                         break;
310                 }
311                 case ID_WO: {
312                         World *wo = (World *)id;
313                         wo->nodetree = ntree;
314
315                         output_type = SH_NODE_OUTPUT_WORLD;
316                         shader_type = SH_NODE_BACKGROUND;
317
318                         copy_v3_v3(color, &wo->horr);
319                         strength = 1.0f;
320                         break;
321                 }
322                 case ID_LA: {
323                         Lamp *la = (Lamp *)id;
324                         la->nodetree = ntree;
325
326                         output_type = SH_NODE_OUTPUT_LAMP;
327                         shader_type = SH_NODE_EMISSION;
328
329                         copy_v3_v3(color, &la->r);
330                         if (la->type == LA_LOCAL || la->type == LA_SPOT || la->type == LA_AREA)
331                                 strength = 100.0f;
332                         else
333                                 strength = 1.0f;
334                         break;
335                 }
336                 default:
337                         printf("ED_node_shader_default called on wrong ID type.\n");
338                         return;
339         }
340         
341         ntemp.type = output_type;
342         out = nodeAddNode(ntree, &ntemp);
343         out->locx = 300.0f; out->locy = 300.0f;
344         
345         ntemp.type = shader_type;
346         in = nodeAddNode(ntree, &ntemp);
347         in->locx = 10.0f; in->locy = 300.0f;
348         nodeSetActive(ntree, in);
349         
350         /* only a link from color to color */
351         fromsock = in->outputs.first;
352         tosock = out->inputs.first;
353         nodeAddLink(ntree, in, fromsock, out, tosock);
354
355         /* default values */
356         if (BKE_scene_use_new_shading_nodes(scene)) {
357                 sock = in->inputs.first;
358                 copy_v3_v3(((bNodeSocketValueRGBA *)sock->default_value)->value, color);
359
360                 if (strength != 0.0f) {
361                         sock = in->inputs.last;
362                         ((bNodeSocketValueFloat *)sock->default_value)->value = strength;
363                 }
364         }
365         
366         ntreeUpdateTree(ntree);
367 }
368
369 /* assumes nothing being done in ntree yet, sets the default in/out node */
370 /* called from shading buttons or header */
371 void ED_node_composit_default(Scene *sce)
372 {
373         bNode *in, *out;
374         bNodeSocket *fromsock, *tosock;
375         bNodeTemplate ntemp;
376         
377         /* but lets check it anyway */
378         if (sce->nodetree) {
379                 if (G.debug & G_DEBUG)
380                         printf("error in composite initialize\n");
381                 return;
382         }
383         
384         sce->nodetree = ntreeAddTree("Compositing Nodetree", NTREE_COMPOSIT, 0);
385
386         sce->nodetree->chunksize = 256;
387         sce->nodetree->edit_quality = NTREE_QUALITY_HIGH;
388         sce->nodetree->render_quality = NTREE_QUALITY_HIGH;
389         
390         ntemp.type = CMP_NODE_COMPOSITE;
391         out = nodeAddNode(sce->nodetree, &ntemp);
392         out->locx = 300.0f; out->locy = 400.0f;
393         out->id = &sce->id;
394         id_us_plus(out->id);
395         
396         ntemp.type = CMP_NODE_R_LAYERS;
397         in = nodeAddNode(sce->nodetree, &ntemp);
398         in->locx = 10.0f; in->locy = 400.0f;
399         in->id = &sce->id;
400         id_us_plus(in->id);
401         nodeSetActive(sce->nodetree, in);
402         
403         /* links from color to color */
404         fromsock = in->outputs.first;
405         tosock = out->inputs.first;
406         nodeAddLink(sce->nodetree, in, fromsock, out, tosock);
407         
408         ntreeUpdateTree(sce->nodetree);
409         
410         // XXX ntreeCompositForceHidden(sce->nodetree);
411 }
412
413 /* assumes nothing being done in ntree yet, sets the default in/out node */
414 /* called from shading buttons or header */
415 void ED_node_texture_default(Tex *tx)
416 {
417         bNode *in, *out;
418         bNodeSocket *fromsock, *tosock;
419         bNodeTemplate ntemp;
420         
421         /* but lets check it anyway */
422         if (tx->nodetree) {
423                 if (G.debug & G_DEBUG)
424                         printf("error in texture initialize\n");
425                 return;
426         }
427         
428         tx->nodetree = ntreeAddTree("Texture Nodetree", NTREE_TEXTURE, 0);
429         
430         ntemp.type = TEX_NODE_OUTPUT;
431         out = nodeAddNode(tx->nodetree, &ntemp);
432         out->locx = 300.0f; out->locy = 300.0f;
433         
434         ntemp.type = TEX_NODE_CHECKER;
435         in = nodeAddNode(tx->nodetree, &ntemp);
436         in->locx = 10.0f; in->locy = 300.0f;
437         nodeSetActive(tx->nodetree, in);
438         
439         fromsock = in->outputs.first;
440         tosock = out->inputs.first;
441         nodeAddLink(tx->nodetree, in, fromsock, out, tosock);
442         
443         ntreeUpdateTree(tx->nodetree);
444 }
445
446 /* id is supposed to contain a node tree */
447 void node_tree_from_ID(ID *id, bNodeTree **ntree, bNodeTree **edittree, int *treetype)
448 {
449         if (id) {
450                 bNode *node = NULL;
451                 short idtype = GS(id->name);
452         
453                 if (idtype == ID_NT) {
454                         *ntree = (bNodeTree *)id;
455                         if (treetype) *treetype = (*ntree)->type;
456                 }
457                 else if (idtype == ID_MA) {
458                         *ntree = ((Material *)id)->nodetree;
459                         if (treetype) *treetype = NTREE_SHADER;
460                 }
461                 else if (idtype == ID_LA) {
462                         *ntree = ((Lamp *)id)->nodetree;
463                         if (treetype) *treetype = NTREE_SHADER;
464                 }
465                 else if (idtype == ID_WO) {
466                         *ntree = ((World *)id)->nodetree;
467                         if (treetype) *treetype = NTREE_SHADER;
468                 }
469                 else if (idtype == ID_SCE) {
470                         *ntree = ((Scene *)id)->nodetree;
471                         if (treetype) *treetype = NTREE_COMPOSIT;
472                 }
473                 else if (idtype == ID_TE) {
474                         *ntree = ((Tex *)id)->nodetree;
475                         if (treetype) *treetype = NTREE_TEXTURE;
476                 }
477                 else {
478                         if (treetype) *treetype = 0;
479                         return;
480                 }
481         
482                 /* find editable group */
483                 if (edittree) {
484                         if (*ntree)
485                                 for (node = (*ntree)->nodes.first; node; node = node->next)
486                                         if (nodeGroupEditGet(node))
487                                                 break;
488                         
489                         if (node && node->id)
490                                 *edittree = (bNodeTree *)node->id;
491                         else
492                                 *edittree = *ntree;
493                 }
494         }
495         else {
496                 *ntree = NULL;
497                 *edittree = NULL;
498                 if (treetype) *treetype = 0;
499         }
500 }
501
502 /* Here we set the active tree(s), even called for each redraw now, so keep it fast :) */
503 void snode_set_context(SpaceNode *snode, Scene *scene)
504 {
505         Object *ob = OBACT;
506         
507         snode->id = snode->from = NULL;
508         
509         if (snode->treetype == NTREE_SHADER) {
510                 /* need active object, or we allow pinning... */
511                 if (snode->shaderfrom == SNODE_SHADER_OBJECT) {
512                         if (ob) {
513                                 if (ob->type == OB_LAMP) {
514                                         snode->from = &ob->id;
515                                         snode->id = ob->data;
516                                 }
517                                 else {
518                                         Material *ma = give_current_material(ob, ob->actcol);
519                                         if (ma) {
520                                                 snode->from = &ob->id;
521                                                 snode->id = &ma->id;
522                                         }
523                                 }
524                         }
525                 }
526                 else { /* SNODE_SHADER_WORLD */
527                         if (scene->world) {
528                                 snode->from = NULL;
529                                 snode->id = &scene->world->id;
530                         }
531                 }
532         }
533         else if (snode->treetype == NTREE_COMPOSIT) {
534                 snode->id = &scene->id;
535                 
536                 /* update output sockets based on available layers */
537                 ntreeCompositForceHidden(scene->nodetree, scene);
538         }
539         else if (snode->treetype == NTREE_TEXTURE) {
540                 Tex *tx = NULL;
541
542                 if (snode->texfrom == SNODE_TEX_OBJECT) {
543                         if (ob) {
544                                 tx = give_current_object_texture(ob);
545
546                                 if (ob->type == OB_LAMP)
547                                         snode->from = (ID *)ob->data;
548                                 else
549                                         snode->from = (ID *)give_current_material(ob, ob->actcol);
550
551                                 /* from is not set fully for material nodes, should be ID + Node then */
552                                 snode->id = &tx->id;
553                         }
554                 }
555                 else if (snode->texfrom == SNODE_TEX_WORLD) {
556                         tx = give_current_world_texture(scene->world);
557                         snode->from = (ID *)scene->world;
558                         snode->id = &tx->id;
559                 }
560                 else {
561                         struct Brush *brush = NULL;
562                         
563                         if (ob && (ob->mode & OB_MODE_SCULPT))
564                                 brush = paint_brush(&scene->toolsettings->sculpt->paint);
565                         else
566                                 brush = paint_brush(&scene->toolsettings->imapaint.paint);
567
568                         if (brush) {
569                                 snode->from = (ID *)brush;
570                                 tx = give_current_brush_texture(brush);
571                                 snode->id = &tx->id;
572                         }
573                 }
574         }
575         else {
576                 if (snode->nodetree && snode->nodetree->type == snode->treetype)
577                         snode->id = &snode->nodetree->id;
578                 else
579                         snode->id = NULL;
580         }
581
582         node_tree_from_ID(snode->id, &snode->nodetree, &snode->edittree, NULL);
583 }
584
585 void snode_update(SpaceNode *snode, bNode *node)
586 {
587         bNode *gnode;
588         
589         if (node)
590                 nodeUpdate(snode->edittree, node);
591         
592         /* if inside group, tag entire group */
593         gnode = node_tree_get_editgroup(snode->nodetree);
594         if (gnode)
595                 nodeUpdateID(snode->nodetree, gnode->id);
596 }
597
598 void ED_node_set_active(Main *bmain, bNodeTree *ntree, bNode *node)
599 {
600         int was_active_texture = (node->flag & NODE_ACTIVE_TEXTURE);
601
602         nodeSetActive(ntree, node);
603         
604         if (node->type != NODE_GROUP) {
605                 int was_output = (node->flag & NODE_DO_OUTPUT);
606                 
607                 /* tree specific activate calls */
608                 if (ntree->type == NTREE_SHADER) {
609                         /* when we select a material, active texture is cleared, for buttons */
610                         if (node->id && ELEM3(GS(node->id->name), ID_MA, ID_LA, ID_WO))
611                                 nodeClearActiveID(ntree, ID_TE);
612                         
613                         if (node->type == SH_NODE_OUTPUT) {
614                                 bNode *tnode;
615                                 
616                                 for (tnode = ntree->nodes.first; tnode; tnode = tnode->next)
617                                         if (tnode->type == SH_NODE_OUTPUT)
618                                                 tnode->flag &= ~NODE_DO_OUTPUT;
619                                 
620                                 node->flag |= NODE_DO_OUTPUT;
621                                 if (was_output == 0)
622                                         ED_node_generic_update(bmain, ntree, node);
623                         }
624
625                         /* if active texture changed, free glsl materials */
626                         if ((node->flag & NODE_ACTIVE_TEXTURE) && !was_active_texture) {
627                                 Material *ma;
628
629                                 for (ma = bmain->mat.first; ma; ma = ma->id.next)
630                                         if (ma->nodetree && ma->use_nodes && has_nodetree(ma->nodetree, ntree))
631                                                 GPU_material_free(ma);
632
633                                 WM_main_add_notifier(NC_IMAGE, NULL);
634                         }
635
636                         WM_main_add_notifier(NC_MATERIAL | ND_NODES, node->id);
637                 }
638                 else if (ntree->type == NTREE_COMPOSIT) {
639                         /* make active viewer, currently only 1 supported... */
640                         if (ELEM(node->type, CMP_NODE_VIEWER, CMP_NODE_SPLITVIEWER)) {
641                                 bNode *tnode;
642                                 
643
644                                 for (tnode = ntree->nodes.first; tnode; tnode = tnode->next)
645                                         if (ELEM(tnode->type, CMP_NODE_VIEWER, CMP_NODE_SPLITVIEWER))
646                                                 tnode->flag &= ~NODE_DO_OUTPUT;
647                                 
648                                 node->flag |= NODE_DO_OUTPUT;
649                                 if (was_output == 0)
650                                         ED_node_generic_update(bmain, ntree, node);
651                                 
652                                 /* addnode() doesnt link this yet... */
653                                 node->id = (ID *)BKE_image_verify_viewer(IMA_TYPE_COMPOSITE, "Viewer Node");
654                         }
655                         else if (node->type == CMP_NODE_R_LAYERS) {
656                                 Scene *scene;
657
658                                 for (scene = bmain->scene.first; scene; scene = scene->id.next) {
659                                         if (scene->nodetree && scene->use_nodes && has_nodetree(scene->nodetree, ntree)) {
660                                                 if (node->id == NULL || node->id == (ID *)scene) {
661                                                         scene->r.actlay = node->custom1;
662                                                 }
663                                         }
664                                 }
665                         }
666                         else if (node->type == CMP_NODE_COMPOSITE) {
667                                 if (was_output == 0) {
668                                         bNode *tnode;
669                                         
670                                         for (tnode = ntree->nodes.first; tnode; tnode = tnode->next)
671                                                 if (tnode->type == CMP_NODE_COMPOSITE)
672                                                         tnode->flag &= ~NODE_DO_OUTPUT;
673                                         
674                                         node->flag |= NODE_DO_OUTPUT;
675                                         ED_node_generic_update(bmain, ntree, node);
676                                 }
677                         }
678                 }
679                 else if (ntree->type == NTREE_TEXTURE) {
680                         // XXX
681 #if 0
682                         if (node->id)
683                                 ;  // XXX BIF_preview_changed(-1);
684                         // allqueue(REDRAWBUTSSHADING, 1);
685                         // allqueue(REDRAWIPO, 0);
686 #endif
687                 }
688         }
689 }
690
691 void ED_node_post_apply_transform(bContext *UNUSED(C), bNodeTree *UNUSED(ntree))
692 {
693         /* XXX This does not work due to layout functions relying on node->block,
694          * which only exists during actual drawing. Can we rely on valid totr rects?
695          */
696         /* make sure nodes have correct bounding boxes after transform */
697         /* node_update_nodetree(C, ntree, 0.0f, 0.0f); */
698 }
699
700 /* ***************** generic operator functions for nodes ***************** */
701
702 #if 0 /* UNUSED */
703
704 static int edit_node_poll(bContext *C)
705 {
706         return ED_operator_node_active(C);
707 }
708
709 static void edit_node_properties(wmOperatorType *ot)
710 {
711         /* XXX could node be a context pointer? */
712         RNA_def_string(ot->srna, "node", "", MAX_NAME, "Node", "");
713         RNA_def_int(ot->srna, "socket", 0, 0, MAX_SOCKET, "Socket", "", 0, MAX_SOCKET);
714         RNA_def_enum(ot->srna, "in_out", socket_in_out_items, SOCK_IN, "Socket Side", "");
715 }
716
717 static int edit_node_invoke_properties(bContext *C, wmOperator *op)
718 {
719         if (!RNA_struct_property_is_set(op->ptr, "node")) {
720                 bNode *node = CTX_data_pointer_get_type(C, "node", &RNA_Node).data;
721                 if (!node)
722                         return 0;
723                 else
724                         RNA_string_set(op->ptr, "node", node->name);
725         }
726         
727         if (!RNA_struct_property_is_set(op->ptr, "in_out"))
728                 RNA_enum_set(op->ptr, "in_out", SOCK_IN);
729         
730         if (!RNA_struct_property_is_set(op->ptr, "socket"))
731                 RNA_int_set(op->ptr, "socket", 0);
732         
733         return 1;
734 }
735
736 static void edit_node_properties_get(wmOperator *op, bNodeTree *ntree, bNode **rnode, bNodeSocket **rsock, int *rin_out)
737 {
738         bNode *node;
739         bNodeSocket *sock = NULL;
740         char nodename[MAX_NAME];
741         int sockindex;
742         int in_out;
743         
744         RNA_string_get(op->ptr, "node", nodename);
745         node = nodeFindNodebyName(ntree, nodename);
746         
747         in_out = RNA_enum_get(op->ptr, "in_out");
748         
749         sockindex = RNA_int_get(op->ptr, "socket");
750         switch (in_out) {
751                 case SOCK_IN:   sock = BLI_findlink(&node->inputs, sockindex);  break;
752                 case SOCK_OUT:  sock = BLI_findlink(&node->outputs, sockindex); break;
753         }
754         
755         if (rnode)
756                 *rnode = node;
757         if (rsock)
758                 *rsock = sock;
759         if (rin_out)
760                 *rin_out = in_out;
761 }
762 #endif
763
764 /* ************************** Node generic ************** */
765
766 /* is rct in visible part of node? */
767 static bNode *visible_node(SpaceNode *snode, rctf *rct)
768 {
769         bNode *node;
770         
771         for (node = snode->edittree->nodes.last; node; node = node->prev) {
772                 if (BLI_rctf_isect(&node->totr, rct, NULL))
773                         break;
774         }
775         return node;
776 }
777
778 /* **************************** */
779
780 typedef struct NodeViewMove {
781         int mvalo[2];
782         int xmin, ymin, xmax, ymax;
783 } NodeViewMove;
784
785 static int snode_bg_viewmove_modal(bContext *C, wmOperator *op, wmEvent *event)
786 {
787         SpaceNode *snode = CTX_wm_space_node(C);
788         ARegion *ar = CTX_wm_region(C);
789         NodeViewMove *nvm = op->customdata;
790
791         switch (event->type) {
792                 case MOUSEMOVE:
793                         
794                         snode->xof -= (nvm->mvalo[0] - event->mval[0]);
795                         snode->yof -= (nvm->mvalo[1] - event->mval[1]);
796                         nvm->mvalo[0] = event->mval[0];
797                         nvm->mvalo[1] = event->mval[1];
798                         
799                         /* prevent dragging image outside of the window and losing it! */
800                         CLAMP(snode->xof, nvm->xmin, nvm->xmax);
801                         CLAMP(snode->yof, nvm->ymin, nvm->ymax);
802                         
803                         ED_region_tag_redraw(ar);
804                         
805                         break;
806                         
807                 case LEFTMOUSE:
808                 case MIDDLEMOUSE:
809                 case RIGHTMOUSE:
810                         
811                         MEM_freeN(nvm);
812                         op->customdata = NULL;
813                         
814                         return OPERATOR_FINISHED;
815         }
816         
817         return OPERATOR_RUNNING_MODAL;
818 }
819
820 static int snode_bg_viewmove_invoke(bContext *C, wmOperator *op, wmEvent *event)
821 {
822         SpaceNode *snode = CTX_wm_space_node(C);
823         ARegion *ar = CTX_wm_region(C);
824         NodeViewMove *nvm;
825         Image *ima;
826         ImBuf *ibuf;
827         const float pad = 32.0f; /* better be bigger then scrollbars */
828
829         void *lock;
830         
831         ima = BKE_image_verify_viewer(IMA_TYPE_COMPOSITE, "Viewer Node");
832         ibuf = BKE_image_acquire_ibuf(ima, NULL, &lock);
833         
834         if (ibuf == NULL) {
835                 BKE_image_release_ibuf(ima, lock);
836                 return OPERATOR_CANCELLED;
837         }
838
839         nvm = MEM_callocN(sizeof(NodeViewMove), "NodeViewMove struct");
840         op->customdata = nvm;
841         nvm->mvalo[0] = event->mval[0];
842         nvm->mvalo[1] = event->mval[1];
843
844         nvm->xmin = -(ar->winx / 2) - (ibuf->x * (0.5f * snode->zoom)) + pad;
845         nvm->xmax =  (ar->winx / 2) + (ibuf->x * (0.5f * snode->zoom)) - pad;
846         nvm->ymin = -(ar->winy / 2) - (ibuf->y * (0.5f * snode->zoom)) + pad;
847         nvm->ymax =  (ar->winy / 2) + (ibuf->y * (0.5f * snode->zoom)) - pad;
848
849         BKE_image_release_ibuf(ima, lock);
850         
851         /* add modal handler */
852         WM_event_add_modal_handler(C, op);
853         
854         return OPERATOR_RUNNING_MODAL;
855 }
856
857 static int snode_bg_viewmove_cancel(bContext *UNUSED(C), wmOperator *op)
858 {
859         MEM_freeN(op->customdata);
860         op->customdata = NULL;
861
862         return OPERATOR_CANCELLED;
863 }
864
865 void NODE_OT_backimage_move(wmOperatorType *ot)
866 {
867         /* identifiers */
868         ot->name = "Background Image Move";
869         ot->description = "Move Node backdrop";
870         ot->idname = "NODE_OT_backimage_move";
871         
872         /* api callbacks */
873         ot->invoke = snode_bg_viewmove_invoke;
874         ot->modal = snode_bg_viewmove_modal;
875         ot->poll = composite_node_active;
876         ot->cancel = snode_bg_viewmove_cancel;
877         
878         /* flags */
879         ot->flag = OPTYPE_BLOCKING | OPTYPE_GRAB_POINTER;
880 }
881
882 static int backimage_zoom(bContext *C, wmOperator *op)
883 {
884         SpaceNode *snode = CTX_wm_space_node(C);
885         ARegion *ar = CTX_wm_region(C);
886         float fac = RNA_float_get(op->ptr, "factor");
887
888         snode->zoom *= fac;
889         ED_region_tag_redraw(ar);
890
891         return OPERATOR_FINISHED;
892 }
893
894
895 void NODE_OT_backimage_zoom(wmOperatorType *ot)
896 {
897         
898         /* identifiers */
899         ot->name = "Background Image Zoom";
900         ot->idname = "NODE_OT_backimage_zoom";
901         ot->description = "Zoom in/out the background image";
902         
903         /* api callbacks */
904         ot->exec = backimage_zoom;
905         ot->poll = composite_node_active;
906         
907         /* flags */
908         ot->flag = OPTYPE_BLOCKING;
909
910         /* internal */
911         RNA_def_float(ot->srna, "factor", 1.2f, 0.0f, 10.0f, "Factor", "", 0.0f, 10.0f);
912 }
913
914 /******************** sample backdrop operator ********************/
915
916 typedef struct ImageSampleInfo {
917         ARegionType *art;
918         void *draw_handle;
919         int x, y;
920         int channels;
921         int color_manage;
922
923         unsigned char col[4];
924         float colf[4];
925
926         int draw;
927 } ImageSampleInfo;
928
929 static void sample_draw(const bContext *C, ARegion *ar, void *arg_info)
930 {
931         Scene *scene = CTX_data_scene(C);
932         ImageSampleInfo *info = arg_info;
933
934         if (info->draw) {
935                 ED_image_draw_info(ar, (scene->r.color_mgt_flag & R_COLOR_MANAGEMENT), info->channels,
936                                    info->x, info->y, info->col, info->colf,
937                                    NULL, NULL /* zbuf - unused for nodes */
938                                    );
939         }
940 }
941
942 static void sample_apply(bContext *C, wmOperator *op, wmEvent *event)
943 {
944         SpaceNode *snode = CTX_wm_space_node(C);
945         ARegion *ar = CTX_wm_region(C);
946         ImageSampleInfo *info = op->customdata;
947         void *lock;
948         Image *ima;
949         ImBuf *ibuf;
950         float fx, fy, bufx, bufy;
951         
952         ima = BKE_image_verify_viewer(IMA_TYPE_COMPOSITE, "Viewer Node");
953         ibuf = BKE_image_acquire_ibuf(ima, NULL, &lock);
954         if (!ibuf) {
955                 info->draw = 0;
956                 return;
957         }
958         
959         if (!ibuf->rect) {
960                 if (info->color_manage)
961                         ibuf->profile = IB_PROFILE_LINEAR_RGB;
962                 else
963                         ibuf->profile = IB_PROFILE_NONE;
964                 IMB_rect_from_float(ibuf);
965         }
966
967         /* map the mouse coords to the backdrop image space */
968         bufx = ibuf->x * snode->zoom;
969         bufy = ibuf->y * snode->zoom;
970         fx = (bufx > 0.0f ? ((float)event->mval[0] - 0.5f * ar->winx - snode->xof) / bufx + 0.5f : 0.0f);
971         fy = (bufy > 0.0f ? ((float)event->mval[1] - 0.5f * ar->winy - snode->yof) / bufy + 0.5f : 0.0f);
972
973         if (fx >= 0.0f && fy >= 0.0f && fx < 1.0f && fy < 1.0f) {
974                 float *fp;
975                 char *cp;
976                 int x = (int)(fx * ibuf->x), y = (int)(fy * ibuf->y);
977
978                 CLAMP(x, 0, ibuf->x - 1);
979                 CLAMP(y, 0, ibuf->y - 1);
980
981                 info->x = x;
982                 info->y = y;
983                 info->draw = 1;
984                 info->channels = ibuf->channels;
985
986                 if (ibuf->rect) {
987                         cp = (char *)(ibuf->rect + y * ibuf->x + x);
988
989                         info->col[0] = cp[0];
990                         info->col[1] = cp[1];
991                         info->col[2] = cp[2];
992                         info->col[3] = cp[3];
993
994                         info->colf[0] = (float)cp[0] / 255.0f;
995                         info->colf[1] = (float)cp[1] / 255.0f;
996                         info->colf[2] = (float)cp[2] / 255.0f;
997                         info->colf[3] = (float)cp[3] / 255.0f;
998                 }
999                 if (ibuf->rect_float) {
1000                         fp = (ibuf->rect_float + (ibuf->channels) * (y * ibuf->x + x));
1001
1002                         info->colf[0] = fp[0];
1003                         info->colf[1] = fp[1];
1004                         info->colf[2] = fp[2];
1005                         info->colf[3] = fp[3];
1006                 }
1007
1008                 ED_node_sample_set(info->colf);
1009         }
1010         else {
1011                 info->draw = 0;
1012                 ED_node_sample_set(NULL);
1013         }
1014
1015         BKE_image_release_ibuf(ima, lock);
1016         
1017         ED_area_tag_redraw(CTX_wm_area(C));
1018 }
1019
1020 static void sample_exit(bContext *C, wmOperator *op)
1021 {
1022         ImageSampleInfo *info = op->customdata;
1023
1024         ED_node_sample_set(NULL);
1025         ED_region_draw_cb_exit(info->art, info->draw_handle);
1026         ED_area_tag_redraw(CTX_wm_area(C));
1027         MEM_freeN(info);
1028 }
1029
1030 static int sample_invoke(bContext *C, wmOperator *op, wmEvent *event)
1031 {
1032         SpaceNode *snode = CTX_wm_space_node(C);
1033         ARegion *ar = CTX_wm_region(C);
1034         ImageSampleInfo *info;
1035
1036         if (snode->treetype != NTREE_COMPOSIT || !(snode->flag & SNODE_BACKDRAW))
1037                 return OPERATOR_CANCELLED;
1038         
1039         info = MEM_callocN(sizeof(ImageSampleInfo), "ImageSampleInfo");
1040         info->art = ar->type;
1041         info->draw_handle = ED_region_draw_cb_activate(ar->type, sample_draw, info, REGION_DRAW_POST_PIXEL);
1042         op->customdata = info;
1043
1044         sample_apply(C, op, event);
1045
1046         WM_event_add_modal_handler(C, op);
1047
1048         return OPERATOR_RUNNING_MODAL;
1049 }
1050
1051 static int sample_modal(bContext *C, wmOperator *op, wmEvent *event)
1052 {
1053         switch (event->type) {
1054                 case LEFTMOUSE:
1055                 case RIGHTMOUSE: // XXX hardcoded
1056                         sample_exit(C, op);
1057                         return OPERATOR_CANCELLED;
1058                 case MOUSEMOVE:
1059                         sample_apply(C, op, event);
1060                         break;
1061         }
1062
1063         return OPERATOR_RUNNING_MODAL;
1064 }
1065
1066 static int sample_cancel(bContext *C, wmOperator *op)
1067 {
1068         sample_exit(C, op);
1069         return OPERATOR_CANCELLED;
1070 }
1071
1072 void NODE_OT_backimage_sample(wmOperatorType *ot)
1073 {
1074         /* identifiers */
1075         ot->name = "Backimage Sample";
1076         ot->idname = "NODE_OT_backimage_sample";
1077         ot->description = "Use mouse to sample background image";
1078         
1079         /* api callbacks */
1080         ot->invoke = sample_invoke;
1081         ot->modal = sample_modal;
1082         ot->cancel = sample_cancel;
1083         ot->poll = ED_operator_node_active;
1084
1085         /* flags */
1086         ot->flag = OPTYPE_BLOCKING;
1087 }
1088
1089 /* ********************** size widget operator ******************** */
1090
1091 typedef struct NodeSizeWidget {
1092         float mxstart, mystart;
1093         float oldlocx, oldlocy;
1094         float oldoffsetx, oldoffsety;
1095         float oldwidth, oldheight;
1096         float oldminiwidth;
1097         int directions;
1098 } NodeSizeWidget;
1099
1100 static void node_resize_init(bContext *C, wmOperator *op, wmEvent *UNUSED(event), bNode *node, int dir)
1101 {
1102         SpaceNode *snode = CTX_wm_space_node(C);
1103         
1104         NodeSizeWidget *nsw = MEM_callocN(sizeof(NodeSizeWidget), "size widget op data");
1105         
1106         op->customdata = nsw;
1107         nsw->mxstart = snode->mx;
1108         nsw->mystart = snode->my;
1109         
1110         /* store old */
1111         nsw->oldlocx = node->locx;
1112         nsw->oldlocy = node->locy;
1113         nsw->oldoffsetx = node->offsetx;
1114         nsw->oldoffsety = node->offsety;
1115         nsw->oldwidth = node->width;
1116         nsw->oldheight = node->height;
1117         nsw->oldminiwidth = node->miniwidth;
1118         nsw->directions = dir;
1119         
1120         WM_cursor_modal(CTX_wm_window(C), node_get_resize_cursor(dir));
1121         /* add modal handler */
1122         WM_event_add_modal_handler(C, op);
1123 }
1124
1125 static void node_resize_exit(bContext *C, wmOperator *op, int UNUSED(cancel))
1126 {
1127         WM_cursor_restore(CTX_wm_window(C));
1128         
1129         MEM_freeN(op->customdata);
1130         op->customdata = NULL;
1131 }
1132
1133 static int node_resize_modal(bContext *C, wmOperator *op, wmEvent *event)
1134 {
1135         SpaceNode *snode = CTX_wm_space_node(C);
1136         ARegion *ar = CTX_wm_region(C);
1137         bNode *node = editnode_get_active(snode->edittree);
1138         NodeSizeWidget *nsw = op->customdata;
1139         float mx, my, dx, dy;
1140         
1141         switch (event->type) {
1142                 case MOUSEMOVE:
1143                         
1144                         UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &mx, &my);
1145                         dx = mx - nsw->mxstart;
1146                         dy = my - nsw->mystart;
1147                         
1148                         if (node) {
1149                                 if (node->flag & NODE_HIDDEN) {
1150                                         float widthmin = 0.0f;
1151                                         float widthmax = 100.0f;
1152                                         if (nsw->directions & NODE_RESIZE_RIGHT) {
1153                                                 node->miniwidth = nsw->oldminiwidth + dx;
1154                                                 CLAMP(node->miniwidth, widthmin, widthmax);
1155                                         }
1156                                         if (nsw->directions & NODE_RESIZE_LEFT) {
1157                                                 float locmax = nsw->oldlocx + nsw->oldminiwidth;
1158                                                 
1159                                                 node->locx = nsw->oldlocx + dx;
1160                                                 CLAMP(node->locx, locmax - widthmax, locmax - widthmin);
1161                                                 node->miniwidth = locmax - node->locx;
1162                                         }
1163                                 }
1164                                 else {
1165                                         float widthmin = UI_DPI_FAC * node->typeinfo->minwidth;
1166                                         float widthmax = UI_DPI_FAC * node->typeinfo->maxwidth;
1167                                         if (nsw->directions & NODE_RESIZE_RIGHT) {
1168                                                 node->width = nsw->oldwidth + dx;
1169                                                 CLAMP(node->width, widthmin, widthmax);
1170                                         }
1171                                         if (nsw->directions & NODE_RESIZE_LEFT) {
1172                                                 float locmax = nsw->oldlocx + nsw->oldwidth;
1173                                                 
1174                                                 node->locx = nsw->oldlocx + dx;
1175                                                 CLAMP(node->locx, locmax - widthmax, locmax - widthmin);
1176                                                 node->width = locmax - node->locx;
1177                                         }
1178                                 }
1179                         
1180                                 /* height works the other way round ... */
1181                                 {
1182                                         float heightmin = UI_DPI_FAC * node->typeinfo->minheight;
1183                                         float heightmax = UI_DPI_FAC * node->typeinfo->maxheight;
1184                                         if (nsw->directions & NODE_RESIZE_TOP) {
1185                                                 float locmin = nsw->oldlocy - nsw->oldheight;
1186                                                 
1187                                                 node->locy = nsw->oldlocy + dy;
1188                                                 CLAMP(node->locy, locmin + heightmin, locmin + heightmax);
1189                                                 node->height = node->locy - locmin;
1190                                         }
1191                                         if (nsw->directions & NODE_RESIZE_BOTTOM) {
1192                                                 node->height = nsw->oldheight - dy;
1193                                                 CLAMP(node->height, heightmin, heightmax);
1194                                         }
1195                                 }
1196                                 
1197                                 /* XXX make callback? */
1198                                 if (node->type == NODE_FRAME) {
1199                                         /* keep the offset symmetric around center point */
1200                                         if (nsw->directions & NODE_RESIZE_LEFT) {
1201                                                 node->locx = nsw->oldlocx + 0.5f * dx;
1202                                                 node->offsetx = nsw->oldoffsetx + 0.5f * dx;
1203                                         }
1204                                         if (nsw->directions & NODE_RESIZE_RIGHT) {
1205                                                 node->locx = nsw->oldlocx + 0.5f * dx;
1206                                                 node->offsetx = nsw->oldoffsetx - 0.5f * dx;
1207                                         }
1208                                         if (nsw->directions & NODE_RESIZE_TOP) {
1209                                                 node->locy = nsw->oldlocy + 0.5f * dy;
1210                                                 node->offsety = nsw->oldoffsety + 0.5f * dy;
1211                                         }
1212                                         if (nsw->directions & NODE_RESIZE_BOTTOM) {
1213                                                 node->locy = nsw->oldlocy + 0.5f * dy;
1214                                                 node->offsety = nsw->oldoffsety - 0.5f * dy;
1215                                         }
1216                                 }
1217                         }
1218                                 
1219                         ED_region_tag_redraw(ar);
1220
1221                         break;
1222                         
1223                 case LEFTMOUSE:
1224                 case MIDDLEMOUSE:
1225                 case RIGHTMOUSE:
1226                         
1227                         node_resize_exit(C, op, 0);
1228                         ED_node_post_apply_transform(C, snode->edittree);
1229                         
1230                         return OPERATOR_FINISHED;
1231         }
1232         
1233         return OPERATOR_RUNNING_MODAL;
1234 }
1235
1236 static int node_resize_invoke(bContext *C, wmOperator *op, wmEvent *event)
1237 {
1238         SpaceNode *snode = CTX_wm_space_node(C);
1239         ARegion *ar = CTX_wm_region(C);
1240         bNode *node = editnode_get_active(snode->edittree);
1241         int dir;
1242         
1243         if (node) {
1244                 /* convert mouse coordinates to v2d space */
1245                 UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1],
1246                                          &snode->mx, &snode->my);
1247                 dir = node->typeinfo->resize_area_func(node, snode->mx, snode->my);
1248                 if (dir != 0) {
1249                         node_resize_init(C, op, event, node, dir);
1250                         return OPERATOR_RUNNING_MODAL;
1251                 }
1252         }
1253         return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH;
1254 }
1255
1256 static int node_resize_cancel(bContext *C, wmOperator *op)
1257 {
1258         node_resize_exit(C, op, 1);
1259
1260         return OPERATOR_CANCELLED;
1261 }
1262
1263 void NODE_OT_resize(wmOperatorType *ot)
1264 {
1265         /* identifiers */
1266         ot->name = "Resize Node";
1267         ot->idname = "NODE_OT_resize";
1268         ot->description = "Resize a node";
1269         
1270         /* api callbacks */
1271         ot->invoke = node_resize_invoke;
1272         ot->modal = node_resize_modal;
1273         ot->poll = ED_operator_node_active;
1274         ot->cancel = node_resize_cancel;
1275         
1276         /* flags */
1277         ot->flag = OPTYPE_BLOCKING;
1278 }
1279
1280
1281 /* ********************** hidden sockets ******************** */
1282
1283 int node_has_hidden_sockets(bNode *node)
1284 {
1285         bNodeSocket *sock;
1286         
1287         for (sock = node->inputs.first; sock; sock = sock->next)
1288                 if (sock->flag & SOCK_HIDDEN)
1289                         return 1;
1290         for (sock = node->outputs.first; sock; sock = sock->next)
1291                 if (sock->flag & SOCK_HIDDEN)
1292                         return 1;
1293         return 0;
1294 }
1295
1296 void node_set_hidden_sockets(SpaceNode *snode, bNode *node, int set)
1297 {
1298         bNodeSocket *sock;
1299
1300         if (set == 0) {
1301                 for (sock = node->inputs.first; sock; sock = sock->next)
1302                         sock->flag &= ~SOCK_HIDDEN;
1303                 for (sock = node->outputs.first; sock; sock = sock->next)
1304                         sock->flag &= ~SOCK_HIDDEN;
1305         }
1306         else {
1307                 /* hide unused sockets */
1308                 for (sock = node->inputs.first; sock; sock = sock->next) {
1309                         if (sock->link == NULL)
1310                                 sock->flag |= SOCK_HIDDEN;
1311                 }
1312                 for (sock = node->outputs.first; sock; sock = sock->next) {
1313                         if (nodeCountSocketLinks(snode->edittree, sock) == 0)
1314                                 sock->flag |= SOCK_HIDDEN;
1315                 }
1316         }
1317 }
1318
1319 /* return 0, nothing done */
1320 static int UNUSED_FUNCTION(node_mouse_groupheader) (SpaceNode * snode)
1321 {
1322         bNode *gnode;
1323         float mx = 0, my = 0;
1324 // XXX  int mval[2];
1325         
1326         gnode = node_tree_get_editgroup(snode->nodetree);
1327         if (gnode == NULL) return 0;
1328         
1329 // XXX  getmouseco_areawin(mval);
1330 // XXX  areamouseco_to_ipoco(G.v2d, mval, &mx, &my);
1331         
1332         /* click in header or outside? */
1333         if (BLI_in_rctf(&gnode->totr, mx, my) == 0) {
1334                 rctf rect = gnode->totr;
1335                 
1336                 rect.ymax += NODE_DY;
1337                 if (BLI_in_rctf(&rect, mx, my) == 0)
1338                         snode_make_group_editable(snode, NULL);  /* toggles, so exits editmode */
1339 //              else
1340 // XXX                  transform_nodes(snode->nodetree, 'g', "Move group");
1341                 
1342                 return 1;
1343         }
1344         return 0;
1345 }
1346
1347 /* checks snode->mouse position, and returns found node/socket */
1348 /* type is SOCK_IN and/or SOCK_OUT */
1349 int node_find_indicated_socket(SpaceNode *snode, bNode **nodep, bNodeSocket **sockp, int in_out)
1350 {
1351         bNode *node;
1352         bNodeSocket *sock;
1353         rctf rect;
1354         
1355         *nodep = NULL;
1356         *sockp = NULL;
1357         
1358         /* check if we click in a socket */
1359         for (node = snode->edittree->nodes.first; node; node = node->next) {
1360                 
1361                 rect.xmin = snode->mx - (NODE_SOCKSIZE + 4);
1362                 rect.ymin = snode->my - (NODE_SOCKSIZE + 4);
1363                 rect.xmax = snode->mx + (NODE_SOCKSIZE + 4);
1364                 rect.ymax = snode->my + (NODE_SOCKSIZE + 4);
1365                 
1366                 if (!(node->flag & NODE_HIDDEN)) {
1367                         /* extra padding inside and out - allow dragging on the text areas too */
1368                         if (in_out == SOCK_IN) {
1369                                 rect.xmax += NODE_SOCKSIZE;
1370                                 rect.xmin -= NODE_SOCKSIZE * 4;
1371                         }
1372                         else if (in_out == SOCK_OUT) {
1373                                 rect.xmax += NODE_SOCKSIZE * 4;
1374                                 rect.xmin -= NODE_SOCKSIZE;
1375                         }
1376                 }
1377                 
1378                 if (in_out & SOCK_IN) {
1379                         for (sock = node->inputs.first; sock; sock = sock->next) {
1380                                 if (!nodeSocketIsHidden(sock)) {
1381                                         if (BLI_in_rctf(&rect, sock->locx, sock->locy)) {
1382                                                 if (node == visible_node(snode, &rect)) {
1383                                                         *nodep = node;
1384                                                         *sockp = sock;
1385                                                         return 1;
1386                                                 }
1387                                         }
1388                                 }
1389                         }
1390                 }
1391                 if (in_out & SOCK_OUT) {
1392                         for (sock = node->outputs.first; sock; sock = sock->next) {
1393                                 if (!nodeSocketIsHidden(sock)) {
1394                                         if (BLI_in_rctf(&rect, sock->locx, sock->locy)) {
1395                                                 if (node == visible_node(snode, &rect)) {
1396                                                         *nodep = node;
1397                                                         *sockp = sock;
1398                                                         return 1;
1399                                                 }
1400                                         }
1401                                 }
1402                         }
1403                 }
1404         }
1405         
1406         /* check group sockets
1407          * NB: using ngroup->outputs as input sockets and vice versa here!
1408          */
1409         if (in_out & SOCK_IN) {
1410                 for (sock = snode->edittree->outputs.first; sock; sock = sock->next) {
1411                         if (!nodeSocketIsHidden(sock)) {
1412                                 if (BLI_in_rctf(&rect, sock->locx, sock->locy)) {
1413                                         *nodep = NULL;   /* NULL node pointer indicates group socket */
1414                                         *sockp = sock;
1415                                         return 1;
1416                                 }
1417                         }
1418                 }
1419         }
1420         if (in_out & SOCK_OUT) {
1421                 for (sock = snode->edittree->inputs.first; sock; sock = sock->next) {
1422                         if (!nodeSocketIsHidden(sock)) {
1423                                 if (BLI_in_rctf(&rect, sock->locx, sock->locy)) {
1424                                         *nodep = NULL;   /* NULL node pointer indicates group socket */
1425                                         *sockp = sock;
1426                                         return 1;
1427                                 }
1428                         }
1429                 }
1430         }
1431         
1432         return 0;
1433 }
1434
1435 /* ****************** Duplicate *********************** */
1436
1437 static void node_duplicate_reparent_recursive(bNode *node)
1438 {
1439         bNode *parent;
1440         
1441         node->flag |= NODE_TEST;
1442         
1443         /* find first selected parent */
1444         for (parent = node->parent; parent; parent = parent->parent) {
1445                 if (parent->flag & SELECT) {
1446                         if (!(parent->flag & NODE_TEST))
1447                                 node_duplicate_reparent_recursive(parent);
1448                         break;
1449                 }
1450         }
1451         /* reparent node copy to parent copy */
1452         if (parent) {
1453                 nodeDetachNode(node->new_node);
1454                 nodeAttachNode(node->new_node, parent->new_node);
1455         }
1456 }
1457
1458 static int node_duplicate_exec(bContext *C, wmOperator *op)
1459 {
1460         SpaceNode *snode = CTX_wm_space_node(C);
1461         bNodeTree *ntree = snode->edittree;
1462         bNode *node, *newnode, *lastnode;
1463         bNodeLink *link, *newlink, *lastlink;
1464         int keep_inputs = RNA_boolean_get(op->ptr, "keep_inputs");
1465         
1466         ED_preview_kill_jobs(C);
1467         
1468         lastnode = ntree->nodes.last;
1469         for (node = ntree->nodes.first; node; node = node->next) {
1470                 if (node->flag & SELECT) {
1471                         newnode = nodeCopyNode(ntree, node);
1472                         
1473                         if (newnode->id) {
1474                                 /* simple id user adjustment, node internal functions don't touch this
1475                                  * but operators and readfile.c do. */
1476                                 id_us_plus(newnode->id);
1477                                 /* to ensure redraws or rerenders happen */
1478                                 ED_node_changed_update(snode->id, newnode);
1479                         }
1480                 }
1481                 
1482                 /* make sure we don't copy new nodes again! */
1483                 if (node == lastnode)
1484                         break;
1485         }
1486         
1487         /* copy links between selected nodes
1488          * NB: this depends on correct node->new_node and sock->new_sock pointers from above copy!
1489          */
1490         lastlink = ntree->links.last;
1491         for (link = ntree->links.first; link; link = link->next) {
1492                 /* This creates new links between copied nodes.
1493                  * If keep_inputs is set, also copies input links from unselected (when fromnode==NULL)!
1494                  */
1495                 if (link->tonode && (link->tonode->flag & NODE_SELECT) &&
1496                     (keep_inputs || (link->fromnode && (link->fromnode->flag & NODE_SELECT))))
1497                 {
1498                         newlink = MEM_callocN(sizeof(bNodeLink), "bNodeLink");
1499                         newlink->flag = link->flag;
1500                         newlink->tonode = link->tonode->new_node;
1501                         newlink->tosock = link->tosock->new_sock;
1502                         if (link->fromnode && (link->fromnode->flag & NODE_SELECT)) {
1503                                 newlink->fromnode = link->fromnode->new_node;
1504                                 newlink->fromsock = link->fromsock->new_sock;
1505                         }
1506                         else {
1507                                 /* input node not copied, this keeps the original input linked */
1508                                 newlink->fromnode = link->fromnode;
1509                                 newlink->fromsock = link->fromsock;
1510                         }
1511                         
1512                         BLI_addtail(&ntree->links, newlink);
1513                 }
1514                 
1515                 /* make sure we don't copy new links again! */
1516                 if (link == lastlink)
1517                         break;
1518         }
1519         
1520         /* clear flags for recursive depth-first iteration */
1521         for (node = ntree->nodes.first; node; node = node->next)
1522                 node->flag &= ~NODE_TEST;
1523         /* reparent copied nodes */
1524         for (node = ntree->nodes.first; node; node = node->next) {
1525                 if ((node->flag & SELECT) && !(node->flag & NODE_TEST))
1526                         node_duplicate_reparent_recursive(node);
1527                 
1528                 /* only has to check old nodes */
1529                 if (node == lastnode)
1530                         break;
1531         }
1532         
1533         /* deselect old nodes, select the copies instead */
1534         for (node = ntree->nodes.first; node; node = node->next) {
1535                 if (node->flag & SELECT) {
1536                         /* has been set during copy above */
1537                         newnode = node->new_node;
1538                         
1539                         node_deselect(node);
1540                         node->flag &= ~NODE_ACTIVE;
1541                         node_select(newnode);
1542                 }
1543                 
1544                 /* make sure we don't copy new nodes again! */
1545                 if (node == lastnode)
1546                         break;
1547         }
1548         
1549         ntreeUpdateTree(snode->edittree);
1550         
1551         snode_notify(C, snode);
1552         snode_dag_update(C, snode);
1553
1554         return OPERATOR_FINISHED;
1555 }
1556
1557 void NODE_OT_duplicate(wmOperatorType *ot)
1558 {
1559         /* identifiers */
1560         ot->name = "Duplicate Nodes";
1561         ot->description = "Duplicate selected nodes";
1562         ot->idname = "NODE_OT_duplicate";
1563         
1564         /* api callbacks */
1565         ot->exec = node_duplicate_exec;
1566         ot->poll = ED_operator_node_active;
1567         
1568         /* flags */
1569         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1570         
1571         RNA_def_boolean(ot->srna, "keep_inputs", 0, "Keep Inputs", "Keep the input links to duplicated nodes");
1572 }
1573
1574 int ED_node_select_check(ListBase *lb)
1575 {
1576         bNode *node;
1577
1578         for (node = lb->first; node; node = node->next) {
1579                 if (node->flag & NODE_SELECT) {
1580                         return TRUE;
1581                 }
1582         }
1583
1584         return FALSE;
1585 }
1586
1587 /* ******************************** */
1588 // XXX some code needing updating to operators...
1589
1590
1591 /* goes over all scenes, reads render layers */
1592 static int node_read_renderlayers_exec(bContext *C, wmOperator *UNUSED(op))
1593 {
1594         Main *bmain = CTX_data_main(C);
1595         SpaceNode *snode = CTX_wm_space_node(C);
1596         Scene *curscene = CTX_data_scene(C), *scene;
1597         bNode *node;
1598
1599         ED_preview_kill_jobs(C);
1600
1601         /* first tag scenes unread */
1602         for (scene = bmain->scene.first; scene; scene = scene->id.next)
1603                 scene->id.flag |= LIB_DOIT;
1604
1605         for (node = snode->edittree->nodes.first; node; node = node->next) {
1606                 if (node->type == CMP_NODE_R_LAYERS) {
1607                         ID *id = node->id;
1608                         if (id->flag & LIB_DOIT) {
1609                                 RE_ReadRenderResult(curscene, (Scene *)id);
1610                                 ntreeCompositTagRender((Scene *)id);
1611                                 id->flag &= ~LIB_DOIT;
1612                         }
1613                 }
1614         }
1615         
1616         snode_notify(C, snode);
1617         snode_dag_update(C, snode);
1618
1619         return OPERATOR_FINISHED;
1620 }
1621
1622 void NODE_OT_read_renderlayers(wmOperatorType *ot)
1623 {
1624         
1625         ot->name = "Read Render Layers";
1626         ot->idname = "NODE_OT_read_renderlayers";
1627         ot->description = "Read all render layers of all used scenes";
1628         
1629         ot->exec = node_read_renderlayers_exec;
1630         
1631         ot->poll = composite_node_active;
1632         
1633         /* flags */
1634         ot->flag = 0;
1635 }
1636
1637 static int node_read_fullsamplelayers_exec(bContext *C, wmOperator *UNUSED(op))
1638 {
1639         Main *bmain = CTX_data_main(C);
1640         SpaceNode *snode = CTX_wm_space_node(C);
1641         Scene *curscene = CTX_data_scene(C);
1642         Render *re = RE_NewRender(curscene->id.name);
1643
1644         WM_cursor_wait(1);
1645         RE_MergeFullSample(re, bmain, curscene, snode->nodetree);
1646         WM_cursor_wait(0);
1647
1648         /* note we are careful to send the right notifier, as otherwise the
1649          * compositor would reexecute and overwrite the full sample result */
1650         WM_event_add_notifier(C, NC_SCENE | ND_COMPO_RESULT, NULL);
1651
1652         return OPERATOR_FINISHED;
1653 }
1654
1655
1656 void NODE_OT_read_fullsamplelayers(wmOperatorType *ot)
1657 {
1658         
1659         ot->name = "Read Full Sample Layers";
1660         ot->idname = "NODE_OT_read_fullsamplelayers";
1661         ot->description = "Read all render layers of current scene, in full sample";
1662         
1663         ot->exec = node_read_fullsamplelayers_exec;
1664         
1665         ot->poll = composite_node_active;
1666         
1667         /* flags */
1668         ot->flag = 0;
1669 }
1670
1671 int node_render_changed_exec(bContext *C, wmOperator *UNUSED(op))
1672 {
1673         Scene *sce = CTX_data_scene(C);
1674         bNode *node;
1675         
1676         for (node = sce->nodetree->nodes.first; node; node = node->next) {
1677                 if (node->id == (ID *)sce && node->need_exec) {
1678                         break;
1679                 }
1680         }
1681         if (node) {
1682                 SceneRenderLayer *srl = BLI_findlink(&sce->r.layers, node->custom1);
1683                 
1684                 if (srl) {
1685                         PointerRNA op_ptr;
1686                         
1687                         WM_operator_properties_create(&op_ptr, "RENDER_OT_render");
1688                         RNA_string_set(&op_ptr, "layer", srl->name);
1689                         RNA_string_set(&op_ptr, "scene", sce->id.name + 2);
1690                         
1691                         /* to keep keypositions */
1692                         sce->r.scemode |= R_NO_FRAME_UPDATE;
1693                         
1694                         WM_operator_name_call(C, "RENDER_OT_render", WM_OP_INVOKE_DEFAULT, &op_ptr);
1695
1696                         WM_operator_properties_free(&op_ptr);
1697                         
1698                         return OPERATOR_FINISHED;
1699                 }
1700                    
1701         }
1702         return OPERATOR_CANCELLED;
1703 }
1704
1705 void NODE_OT_render_changed(wmOperatorType *ot)
1706 {
1707         ot->name = "Render Changed Layer";
1708         ot->idname = "NODE_OT_render_changed";
1709         ot->description = "Render current scene, when input node's layer has been changed";
1710         
1711         ot->exec = node_render_changed_exec;
1712         
1713         ot->poll = composite_node_active;
1714         
1715         /* flags */
1716         ot->flag = 0;
1717 }
1718
1719 /* ****************** Hide operator *********************** */
1720
1721 static void node_flag_toggle_exec(SpaceNode *snode, int toggle_flag)
1722 {
1723         bNode *node;
1724         int tot_eq = 0, tot_neq = 0;
1725
1726         /* Toggles the flag on all selected nodes.
1727          * If the flag is set on all nodes it is unset.
1728          * If the flag is not set on all nodes, it is set.
1729          */
1730         for (node = snode->edittree->nodes.first; node; node = node->next) {
1731                 if (node->flag & SELECT) {
1732                         
1733                         if (toggle_flag == NODE_PREVIEW && (node->typeinfo->flag & NODE_PREVIEW) == 0)
1734                                 continue;
1735                         if (toggle_flag == NODE_OPTIONS && (node->typeinfo->flag & NODE_OPTIONS) == 0)
1736                                 continue;
1737                         
1738                         if (node->flag & toggle_flag)
1739                                 tot_eq++;
1740                         else
1741                                 tot_neq++;
1742                 }
1743         }
1744         for (node = snode->edittree->nodes.first; node; node = node->next) {
1745                 if (node->flag & SELECT) {
1746                         
1747                         if (toggle_flag == NODE_PREVIEW && (node->typeinfo->flag & NODE_PREVIEW) == 0)
1748                                 continue;
1749                         if (toggle_flag == NODE_OPTIONS && (node->typeinfo->flag & NODE_OPTIONS) == 0)
1750                                 continue;
1751                         
1752                         if ( (tot_eq && tot_neq) || tot_eq == 0)
1753                                 node->flag |= toggle_flag;
1754                         else
1755                                 node->flag &= ~toggle_flag;
1756                 }
1757         }
1758 }
1759
1760 static int node_hide_toggle_exec(bContext *C, wmOperator *UNUSED(op))
1761 {
1762         SpaceNode *snode = CTX_wm_space_node(C);
1763         
1764         /* sanity checking (poll callback checks this already) */
1765         if ((snode == NULL) || (snode->edittree == NULL))
1766                 return OPERATOR_CANCELLED;
1767         
1768         node_flag_toggle_exec(snode, NODE_HIDDEN);
1769
1770         WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, NULL);
1771
1772         return OPERATOR_FINISHED;
1773 }
1774
1775 void NODE_OT_hide_toggle(wmOperatorType *ot)
1776 {
1777         /* identifiers */
1778         ot->name = "Hide";
1779         ot->description = "Toggle hiding of selected nodes";
1780         ot->idname = "NODE_OT_hide_toggle";
1781         
1782         /* callbacks */
1783         ot->exec = node_hide_toggle_exec;
1784         ot->poll = ED_operator_node_active;
1785
1786         /* flags */
1787         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1788 }
1789
1790 static int node_preview_toggle_exec(bContext *C, wmOperator *UNUSED(op))
1791 {
1792         SpaceNode *snode = CTX_wm_space_node(C);
1793
1794         /* sanity checking (poll callback checks this already) */
1795         if ((snode == NULL) || (snode->edittree == NULL))
1796                 return OPERATOR_CANCELLED;
1797
1798         ED_preview_kill_jobs(C);
1799
1800         node_flag_toggle_exec(snode, NODE_PREVIEW);
1801
1802         snode_notify(C, snode);
1803
1804         return OPERATOR_FINISHED;
1805 }
1806
1807 void NODE_OT_preview_toggle(wmOperatorType *ot)
1808 {
1809         /* identifiers */
1810         ot->name = "Toggle Node Preview";
1811         ot->description = "Toggle preview display for selected nodes";
1812         ot->idname = "NODE_OT_preview_toggle";
1813
1814         /* callbacks */
1815         ot->exec = node_preview_toggle_exec;
1816         ot->poll = ED_operator_node_active;
1817
1818         /* flags */
1819         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1820 }
1821
1822 static int node_options_toggle_exec(bContext *C, wmOperator *UNUSED(op))
1823 {
1824         SpaceNode *snode = CTX_wm_space_node(C);
1825
1826         /* sanity checking (poll callback checks this already) */
1827         if ((snode == NULL) || (snode->edittree == NULL))
1828                 return OPERATOR_CANCELLED;
1829
1830         node_flag_toggle_exec(snode, NODE_OPTIONS);
1831
1832         WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, NULL);
1833
1834         return OPERATOR_FINISHED;
1835 }
1836
1837 void NODE_OT_options_toggle(wmOperatorType *ot)
1838 {
1839         /* identifiers */
1840         ot->name = "Toggle Node Options";
1841         ot->description = "Toggle option buttons display for selected nodes";
1842         ot->idname = "NODE_OT_options_toggle";
1843
1844         /* callbacks */
1845         ot->exec = node_options_toggle_exec;
1846         ot->poll = ED_operator_node_active;
1847
1848         /* flags */
1849         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1850 }
1851
1852 static int node_socket_toggle_exec(bContext *C, wmOperator *UNUSED(op))
1853 {
1854         SpaceNode *snode = CTX_wm_space_node(C);
1855         bNode *node;
1856         int hidden;
1857
1858         /* sanity checking (poll callback checks this already) */
1859         if ((snode == NULL) || (snode->edittree == NULL))
1860                 return OPERATOR_CANCELLED;
1861
1862         ED_preview_kill_jobs(C);
1863
1864         /* Toggle for all selected nodes */
1865         hidden = 0;
1866         for (node = snode->edittree->nodes.first; node; node = node->next) {
1867                 if (node->flag & SELECT) {
1868                         if (node_has_hidden_sockets(node)) {
1869                                 hidden = 1;
1870                                 break;
1871                         }
1872                 }
1873         }
1874         
1875         for (node = snode->edittree->nodes.first; node; node = node->next) {
1876                 if (node->flag & SELECT) {
1877                         node_set_hidden_sockets(snode, node, !hidden);
1878                 }
1879         }
1880
1881         ntreeUpdateTree(snode->edittree);
1882
1883         WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, NULL);
1884
1885         return OPERATOR_FINISHED;
1886 }
1887
1888 void NODE_OT_hide_socket_toggle(wmOperatorType *ot)
1889 {
1890         /* identifiers */
1891         ot->name = "Toggle Hidden Node Sockets";
1892         ot->description = "Toggle unused node socket display";
1893         ot->idname = "NODE_OT_hide_socket_toggle";
1894
1895         /* callbacks */
1896         ot->exec = node_socket_toggle_exec;
1897         ot->poll = ED_operator_node_active;
1898
1899         /* flags */
1900         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1901 }
1902
1903 /* ****************** Mute operator *********************** */
1904
1905 static int node_mute_exec(bContext *C, wmOperator *UNUSED(op))
1906 {
1907         SpaceNode *snode = CTX_wm_space_node(C);
1908         bNode *node;
1909
1910         ED_preview_kill_jobs(C);
1911
1912         for (node = snode->edittree->nodes.first; node; node = node->next) {
1913                 /* Only allow muting of nodes having a mute func! */
1914                 if ((node->flag & SELECT) && node->typeinfo->internal_connect) {
1915                         node->flag ^= NODE_MUTED;
1916                         snode_update(snode, node);
1917                 }
1918         }
1919         
1920         snode_notify(C, snode);
1921         snode_dag_update(C, snode);
1922         
1923         return OPERATOR_FINISHED;
1924 }
1925
1926 void NODE_OT_mute_toggle(wmOperatorType *ot)
1927 {
1928         /* identifiers */
1929         ot->name = "Toggle Node Mute";
1930         ot->description = "Toggle muting of the nodes";
1931         ot->idname = "NODE_OT_mute_toggle";
1932         
1933         /* callbacks */
1934         ot->exec = node_mute_exec;
1935         ot->poll = ED_operator_node_active;
1936         
1937         /* flags */
1938         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1939 }
1940
1941 /* ****************** Delete operator ******************* */
1942
1943 static int node_delete_exec(bContext *C, wmOperator *UNUSED(op))
1944 {
1945         SpaceNode *snode = CTX_wm_space_node(C);
1946         bNode *node, *next;
1947         
1948         ED_preview_kill_jobs(C);
1949
1950         for (node = snode->edittree->nodes.first; node; node = next) {
1951                 next = node->next;
1952                 if (node->flag & SELECT) {
1953                         /* check id user here, nodeFreeNode is called for free dbase too */
1954                         if (node->id)
1955                                 node->id->us--;
1956                         nodeFreeNode(snode->edittree, node);
1957                 }
1958         }
1959         
1960         ntreeUpdateTree(snode->edittree);
1961
1962         snode_notify(C, snode);
1963         snode_dag_update(C, snode);
1964         
1965         return OPERATOR_FINISHED;
1966 }
1967
1968 void NODE_OT_delete(wmOperatorType *ot)
1969 {
1970         /* identifiers */
1971         ot->name = "Delete";
1972         ot->description = "Delete selected nodes";
1973         ot->idname = "NODE_OT_delete";
1974         
1975         /* api callbacks */
1976         ot->exec = node_delete_exec;
1977         ot->poll = ED_operator_node_active;
1978         
1979         /* flags */
1980         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1981 }
1982
1983 /* ****************** Delete with reconnect ******************* */
1984 static int node_delete_reconnect_exec(bContext *C, wmOperator *UNUSED(op))
1985 {
1986         SpaceNode *snode = CTX_wm_space_node(C);
1987         bNode *node, *next;
1988
1989         ED_preview_kill_jobs(C);
1990
1991         for (node = snode->edittree->nodes.first; node; node = next) {
1992                 next = node->next;
1993                 if (node->flag & SELECT) {
1994                         nodeInternalRelink(snode->edittree, node);
1995                         
1996                         /* check id user here, nodeFreeNode is called for free dbase too */
1997                         if (node->id)
1998                                 node->id->us--;
1999                         nodeFreeNode(snode->edittree, node);
2000                 }
2001         }
2002
2003         ntreeUpdateTree(snode->edittree);
2004
2005         snode_notify(C, snode);
2006         snode_dag_update(C, snode);
2007
2008         return OPERATOR_FINISHED;
2009 }
2010
2011 void NODE_OT_delete_reconnect(wmOperatorType *ot)
2012 {
2013         /* identifiers */
2014         ot->name = "Delete with reconnect";
2015         ot->description = "Delete nodes; will reconnect nodes as if deletion was muted";
2016         ot->idname = "NODE_OT_delete_reconnect";
2017
2018         /* api callbacks */
2019         ot->exec = node_delete_reconnect_exec;
2020         ot->poll = ED_operator_node_active;
2021
2022         /* flags */
2023         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2024 }
2025
2026
2027 /* ****************** File Output Add Socket  ******************* */
2028
2029 static int node_output_file_add_socket_exec(bContext *C, wmOperator *op)
2030 {
2031         Scene *scene = CTX_data_scene(C);
2032         SpaceNode *snode = CTX_wm_space_node(C);
2033         PointerRNA ptr;
2034         bNodeTree *ntree;
2035         bNode *node;
2036         char file_path[MAX_NAME];
2037
2038         ptr = CTX_data_pointer_get(C, "node");
2039         if (!ptr.data)
2040                 return OPERATOR_CANCELLED;
2041         node = ptr.data;
2042         ntree = ptr.id.data;
2043
2044         RNA_string_get(op->ptr, "file_path", file_path);
2045         ntreeCompositOutputFileAddSocket(ntree, node, file_path, &scene->r.im_format);
2046
2047         snode_notify(C, snode);
2048
2049         return OPERATOR_FINISHED;
2050 }
2051
2052 void NODE_OT_output_file_add_socket(wmOperatorType *ot)
2053 {
2054         /* identifiers */
2055         ot->name = "Add File Node Socket";
2056         ot->description = "Add a new input to a file output node";
2057         ot->idname = "NODE_OT_output_file_add_socket";
2058
2059         /* callbacks */
2060         ot->exec = node_output_file_add_socket_exec;
2061         ot->poll = composite_node_active;
2062
2063         /* flags */
2064         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2065
2066         RNA_def_string(ot->srna, "file_path", "Image", MAX_NAME, "File Path", "Sub-path of the output file");
2067 }
2068
2069 /* ****************** Multi File Output Remove Socket  ******************* */
2070
2071 static int node_output_file_remove_active_socket_exec(bContext *C, wmOperator *UNUSED(op))
2072 {
2073         SpaceNode *snode = CTX_wm_space_node(C);
2074         PointerRNA ptr = CTX_data_pointer_get(C, "node");
2075         bNodeTree *ntree;
2076         bNode *node;
2077         
2078         if (!ptr.data)
2079                 return OPERATOR_CANCELLED;
2080         node = ptr.data;
2081         ntree = ptr.id.data;
2082         
2083         if (!ntreeCompositOutputFileRemoveActiveSocket(ntree, node))
2084                 return OPERATOR_CANCELLED;
2085         
2086         snode_notify(C, snode);
2087         
2088         return OPERATOR_FINISHED;
2089 }
2090
2091 void NODE_OT_output_file_remove_active_socket(wmOperatorType *ot)
2092 {
2093         /* identifiers */
2094         ot->name = "Remove File Node Socket";
2095         ot->description = "Remove active input from a file output node";
2096         ot->idname = "NODE_OT_output_file_remove_active_socket";
2097         
2098         /* callbacks */
2099         ot->exec = node_output_file_remove_active_socket_exec;
2100         ot->poll = composite_node_active;
2101         
2102         /* flags */
2103         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2104 }
2105
2106 /* ****************** Multi File Output Move Socket  ******************* */
2107
2108 static int node_output_file_move_active_socket_exec(bContext *C, wmOperator *op)
2109 {
2110         SpaceNode *snode = CTX_wm_space_node(C);
2111         PointerRNA ptr = CTX_data_pointer_get(C, "node");
2112         bNode *node;
2113         NodeImageMultiFile *nimf;
2114         bNodeSocket *sock;
2115         int direction;
2116         
2117         if (!ptr.data)
2118                 return OPERATOR_CANCELLED;
2119         node = ptr.data;
2120         nimf = node->storage;
2121         
2122         sock = BLI_findlink(&node->inputs, nimf->active_input);
2123         if (!sock)
2124                 return OPERATOR_CANCELLED;
2125         
2126         direction = RNA_enum_get(op->ptr, "direction");
2127         
2128         if (direction == 1) {
2129                 bNodeSocket *before = sock->prev;
2130                 if (!before)
2131                         return OPERATOR_CANCELLED;
2132                 BLI_remlink(&node->inputs, sock);
2133                 BLI_insertlinkbefore(&node->inputs, before, sock);
2134                 --nimf->active_input;
2135         }
2136         else {
2137                 bNodeSocket *after = sock->next;
2138                 if (!after)
2139                         return OPERATOR_CANCELLED;
2140                 BLI_remlink(&node->inputs, sock);
2141                 BLI_insertlinkafter(&node->inputs, after, sock);
2142                 ++nimf->active_input;
2143         }
2144         
2145         snode_notify(C, snode);
2146         
2147         return OPERATOR_FINISHED;
2148 }
2149
2150 void NODE_OT_output_file_move_active_socket(wmOperatorType *ot)
2151 {
2152         static EnumPropertyItem direction_items[] = {
2153                 {1, "UP", 0, "Up", ""},
2154                 {2, "DOWN", 0, "Down", ""},
2155                 { 0, NULL, 0, NULL, NULL }
2156         };
2157         
2158         /* identifiers */
2159         ot->name = "Move File Node Socket";
2160         ot->description = "Move the active input of a file output node up or down the list";
2161         ot->idname = "NODE_OT_output_file_move_active_socket";
2162         
2163         /* callbacks */
2164         ot->exec = node_output_file_move_active_socket_exec;
2165         ot->poll = composite_node_active;
2166         
2167         /* flags */
2168         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2169         
2170         RNA_def_enum(ot->srna, "direction", direction_items, 2, "Direction", "");
2171 }
2172
2173 /* ****************** Copy Node Color ******************* */
2174
2175 static int node_copy_color_exec(bContext *C, wmOperator *UNUSED(op))
2176 {
2177         SpaceNode *snode = CTX_wm_space_node(C);
2178         bNodeTree *ntree = snode->edittree;
2179         bNode *node, *tnode;
2180         
2181         if (!ntree)
2182                 return OPERATOR_CANCELLED;
2183         node = nodeGetActive(ntree);
2184         if (!node)
2185                 return OPERATOR_CANCELLED;
2186         
2187         for (tnode = ntree->nodes.first; tnode; tnode = tnode->next) {
2188                 if (tnode->flag & NODE_SELECT && tnode != node) {
2189                         if (node->flag & NODE_CUSTOM_COLOR) {
2190                                 tnode->flag |= NODE_CUSTOM_COLOR;
2191                                 copy_v3_v3(tnode->color, node->color);
2192                         }
2193                         else
2194                                 tnode->flag &= ~NODE_CUSTOM_COLOR;
2195                 }
2196         }
2197
2198         ED_node_sort(ntree);
2199         WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, NULL);
2200
2201         return OPERATOR_FINISHED;
2202 }
2203
2204 void NODE_OT_node_copy_color(wmOperatorType *ot)
2205 {
2206         /* identifiers */
2207         ot->name = "Copy Color";
2208         ot->description = "Copy color to all selected nodes";
2209         ot->idname = "NODE_OT_node_copy_color";
2210
2211         /* api callbacks */
2212         ot->exec = node_copy_color_exec;
2213         ot->poll = ED_operator_node_active;
2214
2215         /* flags */
2216         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2217 }