Request: auto link to viewer node back. Used to be ctrl+click, is now
[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
35 #include "MEM_guardedalloc.h"
36
37 #include "DNA_object_types.h"
38 #include "DNA_material_types.h"
39 #include "DNA_node_types.h"
40 #include "DNA_scene_types.h"
41
42 #include "BKE_context.h"
43 #include "BKE_colortools.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 #include "BKE_scene.h"
54 #include "BKE_utildefines.h"
55
56 #include "BIF_gl.h"
57
58 #include "BLI_math.h"
59 #include "BLI_blenlib.h"
60 #include "BLI_storage_types.h"
61
62 #include "RE_pipeline.h"
63
64
65 #include "ED_node.h"
66 #include "ED_screen.h"
67
68 #include "RNA_access.h"
69 #include "RNA_define.h"
70
71 #include "WM_api.h"
72 #include "WM_types.h"
73
74 #include "UI_interface.h"
75 #include "UI_view2d.h"
76  
77 #include "node_intern.h"
78
79 #define SOCK_IN         1
80 #define SOCK_OUT        2
81
82 /* ***************** composite job manager ********************** */
83
84 typedef struct CompoJob {
85         Scene *scene;
86         bNodeTree *ntree;
87         bNodeTree *localtree;
88         short *stop;
89         short *do_update;
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 *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
136 /* only this runs inside thread */
137 static void compo_startjob(void *cjv, short *stop, short *do_update)
138 {
139         CompoJob *cj= cjv;
140         bNodeTree *ntree= cj->localtree;
141
142         if(cj->scene->use_nodes==0)
143                 return;
144         
145         cj->stop= stop;
146         cj->do_update= do_update;
147         
148         ntree->test_break= compo_breakjob;
149         ntree->tbh= cj;
150         ntree->stats_draw= compo_redrawjob;
151         ntree->sdh= cj;
152         
153         // XXX BIF_store_spare();
154         
155         ntreeCompositExecTree(ntree, &cj->scene->r, 1); /* 1 is do_previews */
156         
157         ntree->test_break= NULL;
158         ntree->stats_draw= NULL;
159
160 }
161
162 void snode_composite_job(const bContext *C, ScrArea *sa)
163 {
164         SpaceNode *snode= sa->spacedata.first;
165         wmJob *steve;
166         CompoJob *cj;
167
168         steve= WM_jobs_get(CTX_wm_manager(C), CTX_wm_window(C), sa, WM_JOB_EXCL_RENDER);
169         cj= MEM_callocN(sizeof(CompoJob), "compo job");
170         
171         /* customdata for preview thread */
172         cj->scene= CTX_data_scene(C);
173         cj->ntree= snode->nodetree;
174         
175         /* setup job */
176         WM_jobs_customdata(steve, cj, compo_freejob);
177         WM_jobs_timer(steve, 0.1, NC_SCENE, NC_SCENE|ND_COMPO_RESULT);
178         WM_jobs_callbacks(steve, compo_startjob, compo_initjob, compo_updatejob);
179         
180         WM_jobs_start(CTX_wm_manager(C), steve);
181         
182 }
183
184 /* ***************************************** */
185
186 /* also checks for edited groups */
187 bNode *editnode_get_active(bNodeTree *ntree)
188 {
189         bNode *node;
190         
191         /* check for edited group */
192         for(node= ntree->nodes.first; node; node= node->next)
193                 if(node->flag & NODE_GROUP_EDIT)
194                         break;
195         if(node)
196                 return nodeGetActive((bNodeTree *)node->id);
197         else
198                 return nodeGetActive(ntree);
199 }
200
201 void snode_notify(bContext *C, SpaceNode *snode)
202 {
203         if(snode->treetype==NTREE_SHADER)
204                 WM_event_add_notifier(C, NC_MATERIAL|ND_NODES, snode->id);
205         else if(snode->treetype==NTREE_COMPOSIT)
206                 WM_event_add_notifier(C, NC_SCENE|ND_NODES, snode->id);
207         else if(snode->treetype==NTREE_TEXTURE)
208                 WM_event_add_notifier(C, NC_TEXTURE|ND_NODES, snode->id);
209 }
210
211 bNode *node_tree_get_editgroup(bNodeTree *nodetree)
212 {
213         bNode *gnode;
214         
215         /* get the groupnode */
216         for(gnode= nodetree->nodes.first; gnode; gnode= gnode->next)
217                 if(gnode->flag & NODE_GROUP_EDIT)
218                         break;
219         return gnode;
220 }
221
222 /* assumes nothing being done in ntree yet, sets the default in/out node */
223 /* called from shading buttons or header */
224 void ED_node_shader_default(Material *ma)
225 {
226         bNode *in, *out;
227         bNodeSocket *fromsock, *tosock;
228         
229         /* but lets check it anyway */
230         if(ma->nodetree) {
231                 if (G.f & G_DEBUG)
232                         printf("error in shader initialize\n");
233                 return;
234         }
235         
236         ma->nodetree= ntreeAddTree(NTREE_SHADER);
237         
238         out= nodeAddNodeType(ma->nodetree, SH_NODE_OUTPUT, NULL, NULL);
239         out->locx= 300.0f; out->locy= 300.0f;
240         
241         in= nodeAddNodeType(ma->nodetree, SH_NODE_MATERIAL, NULL, NULL);
242         in->locx= 10.0f; in->locy= 300.0f;
243         nodeSetActive(ma->nodetree, in);
244         
245         /* only a link from color to color */
246         fromsock= in->outputs.first;
247         tosock= out->inputs.first;
248         nodeAddLink(ma->nodetree, in, fromsock, out, tosock);
249         
250         ntreeSolveOrder(ma->nodetree);  /* needed for pointers */
251 }
252
253 /* assumes nothing being done in ntree yet, sets the default in/out node */
254 /* called from shading buttons or header */
255 void ED_node_composit_default(Scene *sce)
256 {
257         bNode *in, *out;
258         bNodeSocket *fromsock, *tosock;
259         
260         /* but lets check it anyway */
261         if(sce->nodetree) {
262                 if (G.f & G_DEBUG)
263                         printf("error in composite initialize\n");
264                 return;
265         }
266         
267         sce->nodetree= ntreeAddTree(NTREE_COMPOSIT);
268         
269         out= nodeAddNodeType(sce->nodetree, CMP_NODE_COMPOSITE, NULL, NULL);
270         out->locx= 300.0f; out->locy= 400.0f;
271         out->id= &sce->id;
272         
273         in= nodeAddNodeType(sce->nodetree, CMP_NODE_R_LAYERS, NULL, NULL);
274         in->locx= 10.0f; in->locy= 400.0f;
275         in->id= &sce->id;
276         nodeSetActive(sce->nodetree, in);
277         
278         /* links from color to color */
279         fromsock= in->outputs.first;
280         tosock= out->inputs.first;
281         nodeAddLink(sce->nodetree, in, fromsock, out, tosock);
282         
283         ntreeSolveOrder(sce->nodetree); /* needed for pointers */
284         
285         // XXX ntreeCompositForceHidden(sce->nodetree);
286 }
287
288 /* assumes nothing being done in ntree yet, sets the default in/out node */
289 /* called from shading buttons or header */
290 void ED_node_texture_default(Tex *tx)
291 {
292         bNode *in, *out;
293         bNodeSocket *fromsock, *tosock;
294         
295         /* but lets check it anyway */
296         if(tx->nodetree) {
297                 if (G.f & G_DEBUG)
298                         printf("error in texture initialize\n");
299                 return;
300         }
301         
302         tx->nodetree= ntreeAddTree(NTREE_TEXTURE);
303         
304         out= nodeAddNodeType(tx->nodetree, TEX_NODE_OUTPUT, NULL, NULL);
305         out->locx= 300.0f; out->locy= 300.0f;
306         
307         in= nodeAddNodeType(tx->nodetree, TEX_NODE_CHECKER, NULL, NULL);
308         in->locx= 10.0f; in->locy= 300.0f;
309         nodeSetActive(tx->nodetree, in);
310         
311         fromsock= in->outputs.first;
312         tosock= out->inputs.first;
313         nodeAddLink(tx->nodetree, in, fromsock, out, tosock);
314         
315         ntreeSolveOrder(tx->nodetree);  /* needed for pointers */
316 }
317
318 void node_tree_from_ID(ID *id, bNodeTree **ntree, bNodeTree **edittree, int *treetype)
319 {
320         bNode *node= NULL;
321         short idtype= GS(id->name);
322
323         if(idtype == ID_MA) {
324                 *ntree= ((Material*)id)->nodetree;
325                 if(treetype) *treetype= NTREE_SHADER;
326         }
327         else if(idtype == ID_SCE) {
328                 *ntree= ((Scene*)id)->nodetree;
329                 if(treetype) *treetype= NTREE_COMPOSIT;
330         }
331         else if(idtype == ID_TE) {
332                 *ntree= ((Tex*)id)->nodetree;
333                 if(treetype) *treetype= NTREE_TEXTURE;
334         }
335
336         /* find editable group */
337         if(edittree) {
338                 if(*ntree)
339                         for(node= (*ntree)->nodes.first; node; node= node->next)
340                                 if(node->flag & NODE_GROUP_EDIT)
341                                         break;
342                 
343                 if(node && node->id)
344                         *edittree= (bNodeTree *)node->id;
345                 else
346                         *edittree= *ntree;
347         }
348 }
349
350 /* Here we set the active tree(s), even called for each redraw now, so keep it fast :) */
351 void snode_set_context(SpaceNode *snode, Scene *scene)
352 {
353         Object *ob= OBACT;
354         
355         snode->nodetree= NULL;
356         snode->edittree= NULL;
357         snode->id= snode->from= NULL;
358         
359         if(snode->treetype==NTREE_SHADER) {
360                 /* need active object, or we allow pinning... */
361                 if(ob) {
362                         Material *ma= give_current_material(ob, ob->actcol);
363                         if(ma) {
364                                 snode->from= &ob->id;
365                                 snode->id= &ma->id;
366                         }
367                 }
368         }
369         else if(snode->treetype==NTREE_COMPOSIT) {
370                 snode->from= NULL;
371                 snode->id= &scene->id;
372                 
373                 /* bit clumsy but reliable way to see if we draw first time */
374                 if(snode->nodetree==NULL)
375                         ntreeCompositForceHidden(scene->nodetree, scene);
376         }
377         else if(snode->treetype==NTREE_TEXTURE) {
378                 Tex *tx= NULL;
379
380                 if(snode->texfrom==SNODE_TEX_OBJECT) {
381                         if(ob) {
382                                 tx= give_current_object_texture(ob);
383
384                                 if(ob->type == OB_LAMP)
385                                         snode->from= (ID*)ob->data;
386                                 else
387                                         snode->from= (ID*)give_current_material(ob, ob->actcol);
388
389                                 /* from is not set fully for material nodes, should be ID + Node then */
390                                 snode->id= &tx->id;
391                         }
392                 }
393                 else if(snode->texfrom==SNODE_TEX_WORLD) {
394                         tx= give_current_world_texture(scene->world);
395                         snode->from= (ID *)scene->world;
396                         snode->id= &tx->id;
397                 }
398                 else {
399                         Brush *brush= NULL;
400                         
401                         if(ob && (ob->mode & OB_MODE_SCULPT))
402                                 brush= paint_brush(&scene->toolsettings->sculpt->paint);
403                         else
404                                 brush= paint_brush(&scene->toolsettings->imapaint.paint);
405
406                         if (brush) {
407                                 snode->from= (ID *)brush;
408                                 tx= give_current_brush_texture(brush);
409                                 snode->id= &tx->id;
410                         }
411                 }
412         }
413
414         if(snode->id)
415                 node_tree_from_ID(snode->id, &snode->nodetree, &snode->edittree, NULL);
416 }
417
418 void node_set_active(SpaceNode *snode, bNode *node)
419 {
420         nodeSetActive(snode->edittree, node);
421         
422         if(node->type!=NODE_GROUP) {
423                 /* tree specific activate calls */
424                 if(snode->treetype==NTREE_SHADER) {
425                         /* when we select a material, active texture is cleared, for buttons */
426                         if(node->id && GS(node->id->name)==ID_MA)
427                                 nodeClearActiveID(snode->edittree, ID_TE);
428
429                         // XXX
430 #if 0
431                         if(node->id)
432                                 ; // XXX BIF_preview_changed(-1);       /* temp hack to force texture preview to update */
433                         
434                         // allqueue(REDRAWBUTSSHADING, 1);
435                         // allqueue(REDRAWIPO, 0);
436 #endif
437                 }
438                 else if(snode->treetype==NTREE_COMPOSIT) {
439                         Scene *scene= (Scene*)snode->id;
440
441                         /* make active viewer, currently only 1 supported... */
442                         if( ELEM(node->type, CMP_NODE_VIEWER, CMP_NODE_SPLITVIEWER)) {
443                                 bNode *tnode;
444                                 int was_output= (node->flag & NODE_DO_OUTPUT);
445
446                                 for(tnode= snode->edittree->nodes.first; tnode; tnode= tnode->next)
447                                         if( ELEM(tnode->type, CMP_NODE_VIEWER, CMP_NODE_SPLITVIEWER))
448                                                 tnode->flag &= ~NODE_DO_OUTPUT;
449                                 
450                                 node->flag |= NODE_DO_OUTPUT;
451                                 if(was_output==0) {
452                                         bNode *gnode;
453                                         
454                                         NodeTagChanged(snode->edittree, node);
455                                         
456                                         /* if inside group, tag entire group */
457                                         gnode= node_tree_get_editgroup(snode->nodetree);
458                                         if(gnode)
459                                                 NodeTagIDChanged(snode->nodetree, gnode->id);
460                                         
461                                         ED_node_changed_update(snode->id, node);
462                                 }
463                                 
464                                 /* addnode() doesnt link this yet... */
465                                 node->id= (ID *)BKE_image_verify_viewer(IMA_TYPE_COMPOSITE, "Viewer Node");
466                         }
467                         else if(node->type==CMP_NODE_R_LAYERS) {
468                                 if(node->id==NULL || node->id==(ID *)scene) {
469                                         scene->r.actlay= node->custom1;
470                                 }
471                         }
472                 }
473                 else if(snode->treetype==NTREE_TEXTURE) {
474                         // XXX
475 #if 0
476                         if(node->id)
477                                 ; // XXX BIF_preview_changed(-1);
478                         // allqueue(REDRAWBUTSSHADING, 1);
479                         // allqueue(REDRAWIPO, 0);
480 #endif
481                 }
482         }
483 }
484
485 /* when links in groups change, inputs/outputs change, nodes added/deleted... */
486 void node_tree_verify_groups(bNodeTree *nodetree)
487 {
488         bNode *gnode;
489         
490         gnode= node_tree_get_editgroup(nodetree);
491         
492         /* does all materials */
493         if(gnode)
494                 nodeVerifyGroup((bNodeTree *)gnode->id);
495         
496 }
497
498 /* ***************** Edit Group operator ************* */
499
500 void snode_make_group_editable(SpaceNode *snode, bNode *gnode)
501 {
502         bNode *node;
503         
504         /* make sure nothing has group editing on */
505         for(node= snode->nodetree->nodes.first; node; node= node->next)
506                 node->flag &= ~NODE_GROUP_EDIT;
507         
508         if(gnode==NULL) {
509                 /* with NULL argument we do a toggle */
510                 if(snode->edittree==snode->nodetree)
511                         gnode= nodeGetActive(snode->nodetree);
512         }
513         
514         if(gnode && gnode->type==NODE_GROUP && gnode->id) {
515                 if(gnode->id->lib)
516                         ntreeMakeLocal((bNodeTree *)gnode->id);
517
518                 gnode->flag |= NODE_GROUP_EDIT;
519                 snode->edittree= (bNodeTree *)gnode->id;
520                 
521                 /* deselect all other nodes, so we can also do grabbing of entire subtree */
522                 for(node= snode->nodetree->nodes.first; node; node= node->next)
523                         node->flag &= ~SELECT;
524                 gnode->flag |= SELECT;
525                 
526         }
527         else 
528                 snode->edittree= snode->nodetree;
529         
530         ntreeSolveOrder(snode->nodetree);
531 }
532
533 static int node_group_edit_exec(bContext *C, wmOperator *op)
534 {
535         SpaceNode *snode = CTX_wm_space_node(C);
536         bNode *gnode;
537
538         gnode= nodeGetActive(snode->edittree);
539         snode_make_group_editable(snode, gnode);
540
541         WM_event_add_notifier(C, NC_SCENE|ND_NODES, NULL);
542
543         return OPERATOR_FINISHED;
544 }
545
546 static int node_group_edit_invoke(bContext *C, wmOperator *op, wmEvent *event)
547 {
548         SpaceNode *snode = CTX_wm_space_node(C);
549         bNode *gnode;
550
551         gnode= nodeGetActive(snode->edittree);
552         if(gnode && gnode->type==NODE_GROUP && gnode->id && gnode->id->lib) {
553                 uiPupMenuOkee(C, op->type->idname, "Make group local?");
554                 return OPERATOR_CANCELLED;
555         }
556
557         return node_group_edit_exec(C, op);
558 }
559
560 void NODE_OT_group_edit(wmOperatorType *ot)
561 {
562         /* identifiers */
563         ot->name = "Edit Group";
564         ot->description = "Edit node group";
565         ot->idname = "NODE_OT_group_edit";
566         
567         /* api callbacks */
568         ot->invoke = node_group_edit_invoke;
569         ot->exec = node_group_edit_exec;
570         ot->poll = ED_operator_node_active;
571         
572         /* flags */
573         ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
574 }
575
576 /* ******************** Ungroup operator ********************** */
577
578 static int node_group_ungroup_exec(bContext *C, wmOperator *op)
579 {
580         SpaceNode *snode = CTX_wm_space_node(C);
581         bNode *gnode;
582
583         /* are we inside of a group? */
584         gnode= node_tree_get_editgroup(snode->nodetree);
585         if(gnode)
586                 snode_make_group_editable(snode, NULL);
587         
588         gnode= nodeGetActive(snode->edittree);
589         if(gnode==NULL)
590                 return OPERATOR_CANCELLED;
591         
592         if(gnode->type!=NODE_GROUP) {
593                 BKE_report(op->reports, RPT_ERROR, "Not a group");
594                 return OPERATOR_CANCELLED;
595         }
596         else if(!nodeGroupUnGroup(snode->edittree, gnode)) {
597                 BKE_report(op->reports, RPT_ERROR, "Can't ungroup");
598                 return OPERATOR_CANCELLED;
599         }
600
601         WM_event_add_notifier(C, NC_SCENE|ND_NODES, NULL);
602
603         return OPERATOR_FINISHED;
604 }
605
606 void NODE_OT_group_ungroup(wmOperatorType *ot)
607 {
608         /* identifiers */
609         ot->name = "Ungroup";
610         ot->description = "Ungroup selected nodes";
611         ot->idname = "NODE_OT_group_ungroup";
612         
613         /* api callbacks */
614         ot->exec = node_group_ungroup_exec;
615         ot->poll = ED_operator_node_active;
616         
617         /* flags */
618         ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
619 }
620
621 /* ************************** Node generic ************** */
622
623 /* allows to walk the list in order of visibility */
624 bNode *next_node(bNodeTree *ntree)
625 {
626         static bNode *current=NULL, *last= NULL;
627         
628         if(ntree) {
629                 /* set current to the first selected node */
630                 for(current= ntree->nodes.last; current; current= current->prev)
631                         if(current->flag & NODE_SELECT)
632                                 break;
633                 
634                 /* set last to the first unselected node */
635                 for(last= ntree->nodes.last; last; last= last->prev)
636                         if((last->flag & NODE_SELECT)==0)
637                                 break;
638                 
639                 if(current==NULL)
640                         current= last;
641                 
642                 return NULL;
643         }
644         /* no nodes, or we are ready */
645         if(current==NULL)
646                 return NULL;
647         
648         /* now we walk the list backwards, but we always return current */
649         if(current->flag & NODE_SELECT) {
650                 bNode *node= current;
651                 
652                 /* find previous selected */
653                 current= current->prev;
654                 while(current && (current->flag & NODE_SELECT)==0)
655                         current= current->prev;
656                 
657                 /* find first unselected */
658                 if(current==NULL)
659                         current= last;
660                 
661                 return node;
662         }
663         else {
664                 bNode *node= current;
665                 
666                 /* find previous unselected */
667                 current= current->prev;
668                 while(current && (current->flag & NODE_SELECT))
669                         current= current->prev;
670                 
671                 return node;
672         }
673         
674         return NULL;
675 }
676
677 /* is rct in visible part of node? */
678 static bNode *visible_node(SpaceNode *snode, rctf *rct)
679 {
680         bNode *tnode;
681         
682         for(next_node(snode->edittree); (tnode=next_node(NULL));) {
683                 if(BLI_isect_rctf(&tnode->totr, rct, NULL))
684                         break;
685         }
686         return tnode;
687 }
688
689 #if 0
690 static void snode_bg_viewmove(SpaceNode *snode)
691 {
692         ScrArea *sa;
693         Image *ima;
694         ImBuf *ibuf;
695         Window *win;
696         short mval[2], mvalo[2];
697         short rectx, recty, xmin, xmax, ymin, ymax, pad;
698         int oldcursor;
699         
700         ima= BKE_image_verify_viewer(IMA_TYPE_COMPOSITE, "Viewer Node");
701         ibuf= BKE_image_get_ibuf(ima, NULL);
702         
703         sa = snode->area;
704         
705         if(ibuf) {
706                 rectx = ibuf->x;
707                 recty = ibuf->y;
708         } else {
709                 rectx = recty = 1;
710         }
711         
712         pad = 10;
713         xmin = -(sa->winx/2) - rectx/2 + pad;
714         xmax = sa->winx/2 + rectx/2 - pad;
715         ymin = -(sa->winy/2) - recty/2 + pad;
716         ymax = sa->winy/2 + recty/2 - pad;
717         
718         getmouseco_sc(mvalo);
719         
720         /* store the old cursor to temporarily change it */
721         oldcursor=get_cursor();
722         win=winlay_get_active_window();
723         
724         SetBlenderCursor(BC_NSEW_SCROLLCURSOR);
725         
726         while(get_mbut()&(L_MOUSE|M_MOUSE)) {
727                 
728                 getmouseco_sc(mval);
729                 
730                 if(mvalo[0]!=mval[0] || mvalo[1]!=mval[1]) {
731                         
732                         snode->xof -= (mvalo[0]-mval[0]);
733                         snode->yof -= (mvalo[1]-mval[1]);
734                         
735                         /* prevent dragging image outside of the window and losing it! */
736                         CLAMP(snode->xof, xmin, xmax);
737                         CLAMP(snode->yof, ymin, ymax);
738                         
739                         mvalo[0]= mval[0];
740                         mvalo[1]= mval[1];
741                         
742                         scrarea_do_windraw(curarea);
743                         screen_swapbuffers();
744                 }
745                 else BIF_wait_for_statechange();
746         }
747         
748         window_set_cursor(win, oldcursor);
749 }
750 #endif
751
752 /* ********************** size widget operator ******************** */
753
754 typedef struct NodeSizeWidget {
755         float mxstart;
756         float oldwidth;
757 } NodeSizeWidget;
758
759 static int node_resize_modal(bContext *C, wmOperator *op, wmEvent *event)
760 {
761         SpaceNode *snode= CTX_wm_space_node(C);
762         ARegion *ar= CTX_wm_region(C);
763         bNode *node= editnode_get_active(snode->edittree);
764         NodeSizeWidget *nsw= op->customdata;
765         float mx, my;
766         
767         switch (event->type) {
768                 case MOUSEMOVE:
769                         
770                         UI_view2d_region_to_view(&ar->v2d, event->x - ar->winrct.xmin, event->y - ar->winrct.ymin, 
771                                                                          &mx, &my);
772                         
773                         if (node) {
774                                 if(node->flag & NODE_HIDDEN) {
775                                         node->miniwidth= nsw->oldwidth + mx - nsw->mxstart;
776                                         CLAMP(node->miniwidth, 0.0f, 100.0f);
777                                 }
778                                 else {
779                                         node->width= nsw->oldwidth + mx - nsw->mxstart;
780                                         CLAMP(node->width, node->typeinfo->minwidth, node->typeinfo->maxwidth);
781                                 }
782                         }
783                                 
784                         ED_region_tag_redraw(ar);
785
786                         break;
787                         
788                 case LEFTMOUSE:
789                 case MIDDLEMOUSE:
790                 case RIGHTMOUSE:
791                         
792                         MEM_freeN(nsw);
793                         op->customdata= NULL;
794                         
795                         return OPERATOR_FINISHED;
796         }
797         
798         return OPERATOR_RUNNING_MODAL;
799 }
800
801 static int node_resize_invoke(bContext *C, wmOperator *op, wmEvent *event)
802 {
803         SpaceNode *snode= CTX_wm_space_node(C);
804         ARegion *ar= CTX_wm_region(C);
805         bNode *node= editnode_get_active(snode->edittree);
806         
807         if(node) {
808                 rctf totr;
809                 
810                 /* convert mouse coordinates to v2d space */
811                 UI_view2d_region_to_view(&ar->v2d, event->x - ar->winrct.xmin, event->y - ar->winrct.ymin, 
812                                                                  &snode->mx, &snode->my);
813                 
814                 /* rect we're interested in is just the bottom right corner */
815                 totr= node->totr;
816                 totr.xmin= totr.xmax-10.0f;
817                 totr.ymax= totr.ymin+10.0f;
818                 
819                 if(BLI_in_rctf(&totr, snode->mx, snode->my)) {
820                         NodeSizeWidget *nsw= MEM_callocN(sizeof(NodeSizeWidget), "size widget op data");
821                         
822                         op->customdata= nsw;
823                         nsw->mxstart= snode->mx;
824                         
825                         /* store old */
826                         if(node->flag & NODE_HIDDEN)
827                                 nsw->oldwidth= node->miniwidth;
828                         else
829                                 nsw->oldwidth= node->width;
830                         
831                         /* add modal handler */
832                         WM_event_add_modal_handler(C, op);
833
834                         return OPERATOR_RUNNING_MODAL;
835                 }
836         }
837         return OPERATOR_CANCELLED|OPERATOR_PASS_THROUGH;
838 }
839
840 void NODE_OT_resize(wmOperatorType *ot)
841 {
842         /* identifiers */
843         ot->name= "Resize Node";
844         ot->idname= "NODE_OT_resize";
845         
846         /* api callbacks */
847         ot->invoke= node_resize_invoke;
848         ot->modal= node_resize_modal;
849         ot->poll= ED_operator_node_active;
850         
851         /* flags */
852         ot->flag= OPTYPE_BLOCKING;
853 }
854
855 /* ********************** select ******************** */
856
857
858 /* no undo here! */
859 void node_deselectall(SpaceNode *snode)
860 {
861         bNode *node;
862         
863         for(node= snode->edittree->nodes.first; node; node= node->next)
864                 node->flag &= ~SELECT;
865 }
866
867 int node_has_hidden_sockets(bNode *node)
868 {
869         bNodeSocket *sock;
870         
871         for(sock= node->inputs.first; sock; sock= sock->next)
872                 if(sock->flag & SOCK_HIDDEN)
873                         return 1;
874         for(sock= node->outputs.first; sock; sock= sock->next)
875                 if(sock->flag & SOCK_HIDDEN)
876                         return 1;
877         return 0;
878 }
879
880 static void node_link_viewer(SpaceNode *snode, bNode *tonode)
881 {
882         bNode *node;
883
884         /* context check */
885         if(tonode==NULL || tonode->outputs.first==NULL)
886                 return;
887         if( ELEM(tonode->type, CMP_NODE_VIEWER, CMP_NODE_SPLITVIEWER)) 
888                 return;
889         
890         /* get viewer */
891         for(node= snode->edittree->nodes.first; node; node= node->next)
892                 if( ELEM(node->type, CMP_NODE_VIEWER, CMP_NODE_SPLITVIEWER)) 
893                         if(node->flag & NODE_DO_OUTPUT)
894                                 break;
895                 
896         if(node) {
897                 bNodeLink *link;
898                 
899                 /* get link to viewer */
900                 for(link= snode->edittree->links.first; link; link= link->next)
901                         if(link->tonode==node)
902                                 break;
903
904                 if(link) {
905                         link->fromnode= tonode;
906                         link->fromsock= tonode->outputs.first;
907                         NodeTagChanged(snode->edittree, node);
908                 }
909         }
910 }
911
912
913 static int node_active_link_viewer(bContext *C, wmOperator *op)
914 {
915         SpaceNode *snode= CTX_wm_space_node(C);
916         bNode *node;
917         
918         
919         node= editnode_get_active(snode->edittree);
920         
921         if(node) {
922                 node_link_viewer(snode, node);
923                 snode_notify(C, snode);
924         }
925         return OPERATOR_FINISHED;
926 }
927
928
929
930 void NODE_OT_link_viewer(wmOperatorType *ot)
931 {
932         /* identifiers */
933         ot->name= "Link to Viewer Node";
934         ot->description = "Link to Viewer Node";
935         ot->idname= "NODE_OT_link_viewer";
936         
937         /* api callbacks */
938         ot->exec= node_active_link_viewer;
939         ot->poll= ED_operator_node_active;
940         
941         /* flags */
942         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
943 }
944
945
946
947 /* return 0, nothing done */
948 /*static*/ int node_mouse_groupheader(SpaceNode *snode)
949 {
950         bNode *gnode;
951         float mx=0, my=0;
952 // XXX  short mval[2];
953         
954         gnode= node_tree_get_editgroup(snode->nodetree);
955         if(gnode==NULL) return 0;
956         
957 // XXX  getmouseco_areawin(mval);
958 // XXX  areamouseco_to_ipoco(G.v2d, mval, &mx, &my);
959         
960         /* click in header or outside? */
961         if(BLI_in_rctf(&gnode->totr, mx, my)==0) {
962                 rctf rect= gnode->totr;
963                 
964                 rect.ymax += NODE_DY;
965                 if(BLI_in_rctf(&rect, mx, my)==0)
966                         snode_make_group_editable(snode, NULL); /* toggles, so exits editmode */
967 //              else
968 // XXX                  transform_nodes(snode->nodetree, 'g', "Move group");
969                 
970                 return 1;
971         }
972         return 0;
973 }
974
975 /* checks snode->mouse position, and returns found node/socket */
976 /* type is SOCK_IN and/or SOCK_OUT */
977 static int find_indicated_socket(SpaceNode *snode, bNode **nodep, bNodeSocket **sockp, int in_out)
978 {
979         bNode *node;
980         bNodeSocket *sock;
981         rctf rect;
982         
983         /* check if we click in a socket */
984         for(node= snode->edittree->nodes.first; node; node= node->next) {
985                 
986                 rect.xmin = snode->mx - (NODE_SOCKSIZE+4);
987                 rect.ymin = snode->my - (NODE_SOCKSIZE+4);
988                 rect.xmax = snode->mx + (NODE_SOCKSIZE+4);
989                 rect.ymax = snode->my + (NODE_SOCKSIZE+4);
990                 
991                 if (!(node->flag & NODE_HIDDEN)) {
992                         /* extra padding inside and out - allow dragging on the text areas too */
993                         if (in_out == SOCK_IN) {
994                                 rect.xmax += NODE_SOCKSIZE;
995                                 rect.xmin -= NODE_SOCKSIZE*4;
996                         } else if (in_out == SOCK_OUT) {
997                                 rect.xmax += NODE_SOCKSIZE*4;
998                                 rect.xmin -= NODE_SOCKSIZE;
999                         }
1000                 }
1001                 
1002                 if(in_out & SOCK_IN) {
1003                         for(sock= node->inputs.first; sock; sock= sock->next) {
1004                                 if(!(sock->flag & (SOCK_HIDDEN|SOCK_UNAVAIL))) {
1005                                         if(BLI_in_rctf(&rect, sock->locx, sock->locy)) {
1006                                                 if(node == visible_node(snode, &rect)) {
1007                                                         *nodep= node;
1008                                                         *sockp= sock;
1009                                                         return 1;
1010                                                 }
1011                                         }
1012                                 }
1013                         }
1014                 }
1015                 if(in_out & SOCK_OUT) {
1016                         for(sock= node->outputs.first; sock; sock= sock->next) {
1017                                 if(!(sock->flag & (SOCK_HIDDEN|SOCK_UNAVAIL))) {
1018                                         if(BLI_in_rctf(&rect, sock->locx, sock->locy)) {
1019                                                 if(node == visible_node(snode, &rect)) {
1020                                                         *nodep= node;
1021                                                         *sockp= sock;
1022                                                         return 1;
1023                                                 }
1024                                         }
1025                                 }
1026                         }
1027                 }
1028         }
1029         return 0;
1030 }
1031
1032 static int node_socket_hilights(SpaceNode *snode, int in_out)
1033 {
1034         bNode *node;
1035         bNodeSocket *sock, *tsock, *socksel= NULL;
1036         short redraw= 0;
1037         
1038         if(snode->edittree==NULL) return 0;
1039         
1040         /* deselect sockets */
1041         for(node= snode->edittree->nodes.first; node; node= node->next) {
1042                 for(sock= node->inputs.first; sock; sock= sock->next) {
1043                         if(sock->flag & SELECT) {
1044                                 sock->flag &= ~SELECT;
1045                                 redraw++;
1046                                 socksel= sock;
1047                         }
1048                 }
1049                 for(sock= node->outputs.first; sock; sock= sock->next) {
1050                         if(sock->flag & SELECT) {
1051                                 sock->flag &= ~SELECT;
1052                                 redraw++;
1053                                 socksel= sock;
1054                         }
1055                 }
1056         }
1057         
1058         // XXX mousepos should be set here!
1059         
1060         if(find_indicated_socket(snode, &node, &tsock, in_out)) {
1061                 tsock->flag |= SELECT;
1062                 if(redraw==1 && tsock==socksel) redraw= 0;
1063                 else redraw= 1;
1064         }
1065         
1066         return redraw;
1067 }
1068
1069 /* ****************** Add *********************** */
1070
1071
1072 typedef struct bNodeListItem {
1073         struct bNodeListItem *next, *prev;
1074         struct bNode *node;     
1075 } bNodeListItem;
1076
1077 int sort_nodes_locx(void *a, void *b)
1078 {
1079         bNodeListItem *nli1 = (bNodeListItem *)a;
1080         bNodeListItem *nli2 = (bNodeListItem *)b;
1081         bNode *node1 = nli1->node;
1082         bNode *node2 = nli2->node;
1083         
1084         if (node1->locx > node2->locx)
1085                 return 1;
1086         else 
1087                 return 0;
1088 }
1089
1090 static int socket_is_available(bNodeTree *ntree, bNodeSocket *sock, int allow_used)
1091 {
1092         if (sock->flag & (SOCK_HIDDEN|SOCK_UNAVAIL))
1093                 return 0;
1094         
1095         if (!allow_used) {
1096                 if (nodeCountSocketLinks(ntree, sock) > 0)
1097                         return 0;
1098         }
1099         return 1;
1100 }
1101
1102 static bNodeSocket *best_socket_output(bNodeTree *ntree, bNode *node, bNodeSocket *sock_target, int allow_multiple)
1103 {
1104         bNodeSocket *sock;
1105         
1106         /* first try to find a socket with a matching name */
1107         for (sock=node->outputs.first; sock; sock=sock->next) {
1108
1109                 if (!socket_is_available(ntree, sock, allow_multiple))
1110                         continue;
1111
1112                 /* check for same types */
1113                 if (sock->type == sock_target->type) {
1114                         if (strcmp(sock->name, sock_target->name)==0)
1115                                 return sock;
1116                 }
1117         }
1118         
1119         /* otherwise settle for the first available socket of the right type */
1120         for (sock=node->outputs.first; sock; sock=sock->next) {
1121
1122                 if (!socket_is_available(ntree, sock, allow_multiple))
1123                         continue;
1124                 
1125                 /* check for same types */
1126                 if (sock->type == sock_target->type) {
1127                         return sock;
1128                 }
1129         }
1130         
1131         return NULL;
1132 }
1133
1134 /* this is a bit complicated, but designed to prioritise finding 
1135  * sockets of higher types, such as image, first */
1136 static bNodeSocket *best_socket_input(bNodeTree *ntree, bNode *node, int num, int replace)
1137 {
1138         bNodeSocket *sock;
1139         int socktype, maxtype=0;
1140         int a = 0;
1141         
1142         for (sock=node->inputs.first; sock; sock=sock->next) {
1143                 maxtype = MAX2(sock->type, maxtype);
1144         }
1145         
1146         /* find sockets of higher 'types' first (i.e. image) */
1147         for (socktype=maxtype; socktype >= 0; socktype--) {
1148                 for (sock=node->inputs.first; sock; sock=sock->next) {
1149                         
1150                         if (!socket_is_available(ntree, sock, replace)) {
1151                                 a++;
1152                                 continue;
1153                         }
1154                                 
1155                         if (sock->type == socktype) {
1156                                 /* increment to make sure we don't keep finding 
1157                                  * the same socket on every attempt running this function */
1158                                 a++;
1159                                 if (a > num)
1160                                         return sock;
1161                         }
1162                 }
1163         }
1164         
1165         return NULL;
1166 }
1167
1168 void snode_autoconnect(SpaceNode *snode, int allow_multiple, int replace)
1169 {
1170         ListBase *nodelist = MEM_callocN(sizeof(ListBase), "items_list");
1171         bNodeListItem *nli;
1172         bNode *node;
1173         int i;
1174         
1175         for(node= snode->edittree->nodes.first; node; node= node->next) {
1176                 if(node->flag & NODE_SELECT) {
1177                         nli = MEM_mallocN(sizeof(bNodeListItem), "temporary node list item");
1178                         nli->node = node;
1179                         BLI_addtail(nodelist, nli);
1180                 }
1181         }
1182         
1183         /* sort nodes left to right */
1184         BLI_sortlist(nodelist, sort_nodes_locx);
1185         
1186         for (nli=nodelist->first; nli; nli=nli->next) {
1187                 bNode *node_fr, *node_to;
1188                 bNodeSocket *sock_fr, *sock_to;
1189                 
1190                 if (nli->next == NULL) break;
1191                 
1192                 node_fr = nli->node;
1193                 node_to = nli->next->node;
1194                 
1195                 /* check over input sockets first */
1196                 for (i=0; i<BLI_countlist(&node_to->inputs); i++) {
1197                         
1198                         /* find the best guess input socket */
1199                         sock_to = best_socket_input(snode->edittree, node_to, i, replace);
1200                         if (!sock_to) continue;
1201                         
1202                         /* check for an appropriate output socket to connect from */
1203                         sock_fr = best_socket_output(snode->edittree, node_fr, sock_to, allow_multiple);
1204                         if (!sock_fr) continue;
1205                         
1206                         /* then we can connect */
1207                         if (replace)
1208                                 nodeRemSocketLinks(snode->edittree, sock_to);
1209                         nodeAddLink(snode->edittree, node_fr, sock_fr, node_to, sock_to);
1210                         NodeTagChanged(snode->edittree, node_to);
1211                         break;
1212                 }
1213         }
1214         
1215         ntreeSolveOrder(snode->edittree);
1216         
1217         BLI_freelistN(nodelist);
1218         MEM_freeN(nodelist);
1219 }
1220
1221 /* can be called from menus too, but they should do own undopush and redraws */
1222 bNode *node_add_node(SpaceNode *snode, Scene *scene, int type, float locx, float locy)
1223 {
1224         bNode *node= NULL, *gnode;
1225         
1226         node_deselectall(snode);
1227         
1228         if(type>=NODE_DYNAMIC_MENU) {
1229                 node= nodeAddNodeType(snode->edittree, type, NULL, NULL);
1230         }
1231         else if(type>=NODE_GROUP_MENU) {
1232                 if(snode->edittree!=snode->nodetree) {
1233                         // XXX error("Can not add a Group in a Group");
1234                         return NULL;
1235                 }
1236                 else {
1237                         bNodeTree *ngroup= BLI_findlink(&G.main->nodetree, type-NODE_GROUP_MENU);
1238                         if(ngroup)
1239                                 node= nodeAddNodeType(snode->edittree, NODE_GROUP, ngroup, NULL);
1240                 }
1241         }
1242         else
1243                 node= nodeAddNodeType(snode->edittree, type, NULL, NULL);
1244         
1245         /* generics */
1246         if(node) {
1247                 node->locx= locx;
1248                 node->locy= locy + 60.0f;               // arbitrary.. so its visible
1249                 node->flag |= SELECT;
1250                 
1251                 gnode= node_tree_get_editgroup(snode->nodetree);
1252                 if(gnode) {
1253                         node->locx -= gnode->locx;
1254                         node->locy -= gnode->locy;
1255                 }
1256
1257                 node_tree_verify_groups(snode->nodetree);
1258                 node_set_active(snode, node);
1259                 
1260                 if(snode->nodetree->type==NTREE_COMPOSIT) {
1261                         if(ELEM4(node->type, CMP_NODE_R_LAYERS, CMP_NODE_COMPOSITE, CMP_NODE_DEFOCUS, CMP_NODE_OUTPUT_FILE))
1262                                 node->id = &scene->id;
1263                         
1264                         ntreeCompositForceHidden(snode->edittree, scene);
1265                 }
1266                         
1267                 if(node->id)
1268                         id_us_plus(node->id);
1269                         
1270                 NodeTagChanged(snode->edittree, node);
1271         }
1272         
1273         if(snode->nodetree->type==NTREE_TEXTURE) {
1274                 ntreeTexCheckCyclics(snode->edittree);
1275         }
1276         
1277         return node;
1278 }
1279
1280 /* ****************** Duplicate *********************** */
1281
1282 static int node_duplicate_exec(bContext *C, wmOperator *op)
1283 {
1284         SpaceNode *snode= CTX_wm_space_node(C);
1285         
1286         ntreeCopyTree(snode->edittree, 1);      /* 1 == internally selected nodes */
1287         
1288         ntreeSolveOrder(snode->edittree);
1289         node_tree_verify_groups(snode->nodetree);
1290         snode_notify(C, snode);
1291
1292         return OPERATOR_FINISHED;
1293 }
1294
1295 void NODE_OT_duplicate(wmOperatorType *ot)
1296 {
1297         /* identifiers */
1298         ot->name= "Duplicate Nodes";
1299         ot->description = "Duplicate the nodes";
1300         ot->idname= "NODE_OT_duplicate";
1301         
1302         /* api callbacks */
1303         ot->exec= node_duplicate_exec;
1304         ot->poll= ED_operator_node_active;
1305         
1306         /* flags */
1307         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
1308 }
1309
1310 /* *************************** add link op ******************** */
1311
1312 /* temp data to pass on to modal */
1313 typedef struct NodeLinkDrag
1314 {
1315         bNode *node;
1316         bNodeSocket *sock;
1317         bNodeLink *link;
1318         int in_out;
1319 } NodeLinkDrag;
1320
1321 static void node_remove_extra_links(SpaceNode *snode, bNodeSocket *tsock, bNodeLink *link)
1322 {
1323         bNodeLink *tlink;
1324         bNodeSocket *sock;
1325         
1326         if(tsock && nodeCountSocketLinks(snode->edittree, link->tosock) > tsock->limit) {
1327                 
1328                 for(tlink= snode->edittree->links.first; tlink; tlink= tlink->next) {
1329                         if(link!=tlink && tlink->tosock==link->tosock)
1330                                 break;
1331                 }
1332                 if(tlink) {
1333                         /* is there a free input socket with same type? */
1334                         for(sock= tlink->tonode->inputs.first; sock; sock= sock->next) {
1335                                 if(sock->type==tlink->fromsock->type)
1336                                         if(nodeCountSocketLinks(snode->edittree, sock) < sock->limit)
1337                                                 break;
1338                         }
1339                         if(sock) {
1340                                 tlink->tosock= sock;
1341                                 sock->flag &= ~SOCK_HIDDEN;
1342                         }
1343                         else {
1344                                 nodeRemLink(snode->edittree, tlink);
1345                         }
1346                 }
1347         }
1348 }
1349
1350 /* loop that adds a nodelink, called by function below  */
1351 /* in_out = starting socket */
1352 static int node_link_modal(bContext *C, wmOperator *op, wmEvent *event)
1353 {
1354         SpaceNode *snode= CTX_wm_space_node(C);
1355         ARegion *ar= CTX_wm_region(C);
1356         NodeLinkDrag *nldrag= op->customdata;
1357         bNode *tnode, *node;
1358         bNodeSocket *tsock= NULL, *sock;
1359         bNodeLink *link;
1360         int in_out;
1361
1362         in_out= nldrag->in_out;
1363         node= nldrag->node;
1364         sock= nldrag->sock;
1365         link= nldrag->link;
1366         
1367         UI_view2d_region_to_view(&ar->v2d, event->x - ar->winrct.xmin, event->y - ar->winrct.ymin, 
1368                                                          &snode->mx, &snode->my);
1369
1370         switch (event->type) {
1371                 case MOUSEMOVE:
1372                         
1373                         if(in_out==SOCK_OUT) {
1374                                 if(find_indicated_socket(snode, &tnode, &tsock, SOCK_IN)) {
1375                                         if(nodeFindLink(snode->edittree, sock, tsock)==NULL) {
1376                                                 if(tnode!=node  && link->tonode!=tnode && link->tosock!= tsock) {
1377                                                         link->tonode= tnode;
1378                                                         link->tosock= tsock;
1379                                                         ntreeSolveOrder(snode->edittree);       /* for interactive red line warning */
1380                                                 }
1381                                         }
1382                                 }
1383                                 else {
1384                                         link->tonode= NULL;
1385                                         link->tosock= NULL;
1386                                 }
1387                         }
1388                         else {
1389                                 if(find_indicated_socket(snode, &tnode, &tsock, SOCK_OUT)) {
1390                                         if(nodeFindLink(snode->edittree, sock, tsock)==NULL) {
1391                                                 if(nodeCountSocketLinks(snode->edittree, tsock) < tsock->limit) {
1392                                                         if(tnode!=node && link->fromnode!=tnode && link->fromsock!= tsock) {
1393                                                                 link->fromnode= tnode;
1394                                                                 link->fromsock= tsock;
1395                                                                 ntreeSolveOrder(snode->edittree);       /* for interactive red line warning */
1396                                                         }
1397                                                 }
1398                                         }
1399                                 }
1400                                 else {
1401                                         link->fromnode= NULL;
1402                                         link->fromsock= NULL;
1403                                 }
1404                         }
1405                         /* hilight target sockets only */
1406                         node_socket_hilights(snode, in_out==SOCK_OUT?SOCK_IN:SOCK_OUT);
1407                         ED_region_tag_redraw(ar);
1408                         break;
1409                         
1410                 case LEFTMOUSE:
1411                 case RIGHTMOUSE:
1412                 case MIDDLEMOUSE:
1413         
1414                         /* remove link? */
1415                         if(link->tonode==NULL || link->fromnode==NULL) {
1416                                 nodeRemLink(snode->edittree, link);
1417                         }
1418                         else {
1419                                 /* send changed events for original tonode and new */
1420                                 if(link->tonode) 
1421                                         NodeTagChanged(snode->edittree, link->tonode);
1422                                 
1423                                 /* we might need to remove a link */
1424                                 if(in_out==SOCK_OUT) node_remove_extra_links(snode, link->tosock, link);
1425                         }
1426                         
1427                         ntreeSolveOrder(snode->edittree);
1428                         node_tree_verify_groups(snode->nodetree);
1429                         snode_notify(C, snode);
1430                         
1431                         MEM_freeN(op->customdata);
1432                         op->customdata= NULL;
1433                         
1434                         return OPERATOR_FINISHED;
1435         }
1436         
1437         return OPERATOR_RUNNING_MODAL;
1438 }
1439
1440 /* return 1 when socket clicked */
1441 static int node_link_init(SpaceNode *snode, NodeLinkDrag *nldrag)
1442 {
1443         bNodeLink *link;
1444         
1445         /* output indicated? */
1446         if(find_indicated_socket(snode, &nldrag->node, &nldrag->sock, SOCK_OUT)) {
1447                 if(nodeCountSocketLinks(snode->edittree, nldrag->sock) < nldrag->sock->limit)
1448                         return SOCK_OUT;
1449                 else {
1450                         /* find if we break a link */
1451                         for(link= snode->edittree->links.first; link; link= link->next) {
1452                                 if(link->fromsock==nldrag->sock)
1453                                         break;
1454                         }
1455                         if(link) {
1456                                 nldrag->node= link->tonode;
1457                                 nldrag->sock= link->tosock;
1458                                 nodeRemLink(snode->edittree, link);
1459                                 return SOCK_IN;
1460                         }
1461                 }
1462         }
1463         /* or an input? */
1464         else if(find_indicated_socket(snode, &nldrag->node, &nldrag->sock, SOCK_IN)) {
1465                 if(nodeCountSocketLinks(snode->edittree, nldrag->sock) < nldrag->sock->limit)
1466                         return SOCK_IN;
1467                 else {
1468                         /* find if we break a link */
1469                         for(link= snode->edittree->links.first; link; link= link->next) {
1470                                 if(link->tosock==nldrag->sock)
1471                                         break;
1472                         }
1473                         if(link) {
1474                                 /* send changed event to original tonode */
1475                                 if(link->tonode) 
1476                                         NodeTagChanged(snode->edittree, link->tonode);
1477                                 
1478                                 nldrag->node= link->fromnode;
1479                                 nldrag->sock= link->fromsock;
1480                                 nodeRemLink(snode->edittree, link);
1481                                 return SOCK_OUT;
1482                         }
1483                 }
1484         }
1485         
1486         return 0;
1487 }
1488
1489 static int node_link_invoke(bContext *C, wmOperator *op, wmEvent *event)
1490 {
1491         SpaceNode *snode= CTX_wm_space_node(C);
1492         ARegion *ar= CTX_wm_region(C);
1493         NodeLinkDrag *nldrag= MEM_callocN(sizeof(NodeLinkDrag), "drag link op customdata");
1494         
1495         UI_view2d_region_to_view(&ar->v2d, event->x - ar->winrct.xmin, event->y - ar->winrct.ymin, 
1496                                                          &snode->mx, &snode->my);
1497
1498         nldrag->in_out= node_link_init(snode, nldrag);
1499                 
1500         if(nldrag->in_out) {
1501                 op->customdata= nldrag;
1502                 
1503                 /* we make a temporal link */
1504                 if(nldrag->in_out==SOCK_OUT)
1505                         nldrag->link= nodeAddLink(snode->edittree, nldrag->node, nldrag->sock, NULL, NULL);
1506                 else
1507                         nldrag->link= nodeAddLink(snode->edittree, NULL, NULL, nldrag->node, nldrag->sock);
1508                 
1509                 /* add modal handler */
1510                 WM_event_add_modal_handler(C, op);
1511                 
1512                 return OPERATOR_RUNNING_MODAL;
1513         }
1514         else {
1515                 MEM_freeN(nldrag);
1516                 return OPERATOR_CANCELLED|OPERATOR_PASS_THROUGH;
1517         }
1518 }
1519
1520 void NODE_OT_link(wmOperatorType *ot)
1521 {
1522         /* identifiers */
1523         ot->name= "Link Nodes";
1524         ot->idname= "NODE_OT_link";
1525         
1526         /* api callbacks */
1527         ot->invoke= node_link_invoke;
1528         ot->modal= node_link_modal;
1529 //      ot->exec= node_link_exec;
1530         ot->poll= ED_operator_node_active;
1531         
1532         /* flags */
1533         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO|OPTYPE_BLOCKING;
1534 }
1535
1536 /* ********************** Make Link operator ***************** */
1537
1538 /* makes a link between selected output and input sockets */
1539 static int node_make_link_exec(bContext *C, wmOperator *op)
1540 {
1541         SpaceNode *snode= CTX_wm_space_node(C);
1542         int replace = RNA_boolean_get(op->ptr, "replace");
1543
1544         snode_autoconnect(snode, 0, replace);
1545
1546         node_tree_verify_groups(snode->nodetree);
1547         snode_notify(C, snode);
1548         
1549         return OPERATOR_FINISHED;
1550 }
1551
1552 void NODE_OT_link_make(wmOperatorType *ot)
1553 {
1554         /* identifiers */
1555         ot->name= "Make Links";
1556         ot->description= "Makes a link between selected output in input sockets";
1557         ot->idname= "NODE_OT_link_make";
1558         
1559         /* callbacks */
1560         ot->exec= node_make_link_exec;
1561         ot->poll= ED_operator_node_active; // XXX we need a special poll which checks that there are selected input/output sockets
1562         
1563         /* flags */
1564         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
1565         
1566         RNA_def_boolean(ot->srna, "replace", 0, "Replace", "Replace socket connections with the new links");
1567 }
1568
1569 /* ********************** Cut Link operator ***************** */
1570
1571 #define LINK_RESOL 12
1572 static int cut_links_intersect(bNodeLink *link, float mcoords[][2], int tot)
1573 {
1574         float coord_array[LINK_RESOL+1][2];
1575         int i, b;
1576         
1577         if(node_link_bezier_points(NULL, NULL, link, coord_array, LINK_RESOL)) {
1578
1579                 for(i=0; i<tot-1; i++)
1580                         for(b=0; b<LINK_RESOL-1; b++)
1581                                 if(isect_line_line_v2(mcoords[i], mcoords[i+1], coord_array[b], coord_array[b+1]) > 0)
1582                                         return 1;
1583         }
1584         return 0;
1585 }
1586
1587 static int cut_links_exec(bContext *C, wmOperator *op)
1588 {
1589         SpaceNode *snode= CTX_wm_space_node(C);
1590         ARegion *ar= CTX_wm_region(C);
1591         float mcoords[256][2];
1592         int i= 0;
1593         
1594         RNA_BEGIN(op->ptr, itemptr, "path") {
1595                 float loc[2];
1596                 
1597                 RNA_float_get_array(&itemptr, "loc", loc);
1598                 UI_view2d_region_to_view(&ar->v2d, (short)loc[0], (short)loc[1], 
1599                                                                  &mcoords[i][0], &mcoords[i][1]);
1600                 i++;
1601                 if(i>= 256) break;
1602         }
1603         RNA_END;
1604         
1605         if(i>1) {
1606                 bNodeLink *link, *next;
1607                 
1608                 for(link= snode->edittree->links.first; link; link= next) {
1609                         next= link->next;
1610                         
1611                         if(cut_links_intersect(link, mcoords, i)) {
1612                                 NodeTagChanged(snode->edittree, link->tonode);
1613                                 nodeRemLink(snode->edittree, link);
1614                         }
1615                 }
1616                 
1617                 ntreeSolveOrder(snode->edittree);
1618                 node_tree_verify_groups(snode->nodetree);
1619                 snode_notify(C, snode);
1620                 
1621                 return OPERATOR_FINISHED;
1622         }
1623         
1624         return OPERATOR_CANCELLED|OPERATOR_PASS_THROUGH;
1625 }
1626
1627 void NODE_OT_links_cut(wmOperatorType *ot)
1628 {
1629         PropertyRNA *prop;
1630         
1631         ot->name= "Cut links";
1632         ot->idname= "NODE_OT_links_cut";
1633         
1634         ot->invoke= WM_gesture_lines_invoke;
1635         ot->modal= WM_gesture_lines_modal;
1636         ot->exec= cut_links_exec;
1637         
1638         ot->poll= ED_operator_node_active;
1639         
1640         /* flags */
1641         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
1642         
1643         prop= RNA_def_property(ot->srna, "path", PROP_COLLECTION, PROP_NONE);
1644         RNA_def_property_struct_runtime(prop, &RNA_OperatorMousePath);
1645         /* internal */
1646         RNA_def_int(ot->srna, "cursor", BC_KNIFECURSOR, 0, INT_MAX, "Cursor", "", 0, INT_MAX);
1647 }
1648
1649 /* ******************************** */
1650 // XXX some code needing updating to operators...
1651
1652 /* goes over all scenes, reads render layerss */
1653 void node_read_renderlayers(SpaceNode *snode)
1654 {
1655         Scene *curscene= NULL; // XXX
1656         Scene *scene;
1657         bNode *node;
1658
1659         /* first tag scenes unread */
1660         for(scene= G.main->scene.first; scene; scene= scene->id.next) 
1661                 scene->id.flag |= LIB_DOIT;
1662
1663         for(node= snode->edittree->nodes.first; node; node= node->next) {
1664                 if(node->type==CMP_NODE_R_LAYERS) {
1665                         ID *id= node->id;
1666                         if(id->flag & LIB_DOIT) {
1667                                 RE_ReadRenderResult(curscene, (Scene *)id);
1668                                 ntreeCompositTagRender((Scene *)id);
1669                                 id->flag &= ~LIB_DOIT;
1670                         }
1671                 }
1672         }
1673         
1674         // XXX                  snode_notify(snode);
1675 }
1676
1677 void node_read_fullsamplelayers(SpaceNode *snode)
1678 {
1679         Scene *curscene= NULL; // XXX
1680         Render *re= RE_NewRender(curscene->id.name);
1681
1682         WM_cursor_wait(1);
1683
1684         //BIF_init_render_callbacks(re, 1);
1685         RE_MergeFullSample(re, curscene, snode->nodetree);
1686         //BIF_end_render_callbacks();
1687         
1688         // allqueue(REDRAWNODE, 1);
1689         // allqueue(REDRAWIMAGE, 1);
1690         
1691         WM_cursor_wait(0);
1692 }
1693
1694 void imagepaint_composite_tags(bNodeTree *ntree, Image *image, ImageUser *iuser)
1695 {
1696         bNode *node;
1697         
1698         if(ntree==NULL)
1699                 return;
1700         
1701         /* search for renderresults */
1702         if(image->type==IMA_TYPE_R_RESULT) {
1703                 for(node= ntree->nodes.first; node; node= node->next) {
1704                         if(node->type==CMP_NODE_R_LAYERS && node->id==NULL) {
1705                                 /* imageuser comes from ImageWin, so indexes are offset 1 */
1706                                 if(node->custom1==iuser->layer-1)
1707                                         NodeTagChanged(ntree, node);
1708                         }
1709                 }
1710         }
1711         else {
1712                 for(node= ntree->nodes.first; node; node= node->next) {
1713                         if(node->id== &image->id)
1714                                 NodeTagChanged(ntree, node);
1715                 }
1716         }
1717 }
1718
1719 /* ****************** Make Group operator ******************* */
1720
1721 static int node_group_make_exec(bContext *C, wmOperator *op)
1722 {
1723         SpaceNode *snode = CTX_wm_space_node(C);
1724         bNode *gnode;
1725         
1726         if(snode->edittree!=snode->nodetree) {
1727                 BKE_report(op->reports, RPT_ERROR, "Can not add a new Group in a Group");
1728                 return OPERATOR_CANCELLED;
1729         }
1730         
1731         /* for time being... is too complex to handle */
1732         if(snode->treetype==NTREE_COMPOSIT) {
1733                 for(gnode=snode->nodetree->nodes.first; gnode; gnode= gnode->next) {
1734                         if(gnode->flag & SELECT)
1735                                 if(gnode->type==CMP_NODE_R_LAYERS)
1736                                         break;
1737                 }
1738                 
1739                 if(gnode) {
1740                         BKE_report(op->reports, RPT_ERROR, "Can not add RenderLayer in a Group");
1741                         return OPERATOR_CANCELLED;
1742                 }
1743         }
1744         
1745         gnode= nodeMakeGroupFromSelected(snode->nodetree);
1746         if(gnode==NULL) {
1747                 BKE_report(op->reports, RPT_ERROR, "Can not make Group");
1748                 return OPERATOR_CANCELLED;
1749         }
1750         else {
1751                 nodeSetActive(snode->nodetree, gnode);
1752                 ntreeSolveOrder(snode->nodetree);
1753         }
1754         
1755         snode_notify(C, snode);
1756         
1757         return OPERATOR_FINISHED;
1758 }
1759
1760 void NODE_OT_group_make(wmOperatorType *ot)
1761 {
1762         /* identifiers */
1763         ot->name = "Group";
1764         ot->description = "Make group from selected nodes";
1765         ot->idname = "NODE_OT_group_make";
1766         
1767         /* api callbacks */
1768         ot->exec = node_group_make_exec;
1769         ot->poll = ED_operator_node_active;
1770         
1771         /* flags */
1772         ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
1773 }
1774
1775 /* ****************** Hide operator *********************** */
1776
1777 static int node_hide_exec(bContext *C, wmOperator *op)
1778 {
1779         SpaceNode *snode= CTX_wm_space_node(C);
1780         bNode *node;
1781         int nothidden=0, ishidden=0;
1782         
1783         /* sanity checking (poll callback checks this already) */
1784         if((snode == NULL) || (snode->edittree == NULL))
1785                 return OPERATOR_CANCELLED;
1786         
1787         for(node= snode->edittree->nodes.first; node; node= node->next) {
1788                 if(node->flag & SELECT) {
1789                         if(node->flag & NODE_HIDDEN)
1790                                 ishidden++;
1791                         else
1792                                 nothidden++;
1793                 }
1794         }
1795         for(node= snode->edittree->nodes.first; node; node= node->next) {
1796                 if(node->flag & SELECT) {
1797                         if( (ishidden && nothidden) || ishidden==0)
1798                                 node->flag |= NODE_HIDDEN;
1799                         else 
1800                                 node->flag &= ~NODE_HIDDEN;
1801                 }
1802         }
1803         
1804         snode_notify(C, snode);
1805         
1806         return OPERATOR_FINISHED;
1807 }
1808
1809 void NODE_OT_hide(wmOperatorType *ot)
1810 {
1811         /* identifiers */
1812         ot->name= "Hide";
1813         ot->description= "Toggle hiding of the nodes";
1814         ot->idname= "NODE_OT_hide";
1815         
1816         /* callbacks */
1817         ot->exec= node_hide_exec;
1818         ot->poll= ED_operator_node_active;
1819         
1820         /* flags */
1821         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
1822 }
1823
1824 /* ****************** Mute operator *********************** */
1825
1826 static int node_mute_exec(bContext *C, wmOperator *op)
1827 {
1828         SpaceNode *snode= CTX_wm_space_node(C);
1829         bNode *node;
1830
1831         /* no disabling inside of groups */
1832         if(node_tree_get_editgroup(snode->nodetree))
1833                 return OPERATOR_CANCELLED;
1834         
1835         for(node= snode->edittree->nodes.first; node; node= node->next) {
1836                 if(node->flag & SELECT) {
1837                         if(node->inputs.first && node->outputs.first) {
1838                                 node->flag ^= NODE_MUTED;
1839                                 NodeTagChanged(snode->edittree, node);
1840                         }
1841                 }
1842         }
1843         
1844         snode_notify(C, snode);
1845         
1846         return OPERATOR_FINISHED;
1847 }
1848
1849 void NODE_OT_mute(wmOperatorType *ot)
1850 {
1851         /* identifiers */
1852         ot->name= "Mute";
1853         ot->description= "Toggle muting of the nodes";
1854         ot->idname= "NODE_OT_mute";
1855         
1856         /* callbacks */
1857         ot->exec= node_mute_exec;
1858         ot->poll= ED_operator_node_active;
1859         
1860         /* flags */
1861         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
1862 }
1863
1864 /* ****************** Delete operator ******************* */
1865
1866 static int node_delete_exec(bContext *C, wmOperator *op)
1867 {
1868         SpaceNode *snode= CTX_wm_space_node(C);
1869         bNode *node, *next;
1870         
1871         for(node= snode->edittree->nodes.first; node; node= next) {
1872                 next= node->next;
1873                 if(node->flag & SELECT) {
1874                         /* check id user here, nodeFreeNode is called for free dbase too */
1875                         if(node->id)
1876                                 node->id->us--;
1877                         nodeFreeNode(snode->edittree, node);
1878                 }
1879         }
1880         
1881         node_tree_verify_groups(snode->nodetree);
1882
1883         snode_notify(C, snode);
1884         
1885         return OPERATOR_FINISHED;
1886 }
1887
1888 void NODE_OT_delete(wmOperatorType *ot)
1889 {
1890         /* identifiers */
1891         ot->name= "Delete";
1892         ot->description = "Delete selected nodes";
1893         ot->idname= "NODE_OT_delete";
1894         
1895         /* api callbacks */
1896         ot->exec= node_delete_exec;
1897         ot->poll= ED_operator_node_active;
1898         
1899         /* flags */
1900         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
1901 }
1902
1903 /* ****************** Show Cyclic Dependencies Operator  ******************* */
1904
1905 static int node_show_cycles_exec(bContext *C, wmOperator *op)
1906 {
1907         SpaceNode *snode= CTX_wm_space_node(C);
1908         
1909         /* this is just a wrapper around this call... */
1910         ntreeSolveOrder(snode->edittree);
1911         snode_notify(C, snode);
1912         
1913         return OPERATOR_FINISHED;
1914 }
1915
1916 void NODE_OT_show_cyclic_dependencies(wmOperatorType *ot)
1917 {
1918         /* identifiers */
1919         ot->name= "Show Cyclic Dependencies";
1920         ot->description= "Sort the nodes and show the cyclic dependencies between the nodes";
1921         ot->idname= "NODE_OT_show_cyclic_dependencies";
1922         
1923         /* callbacks */
1924         ot->exec= node_show_cycles_exec;
1925         ot->poll= ED_operator_node_active;
1926         
1927         /* flags */
1928         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
1929 }
1930
1931