Automatically hide unused (=unlinked) node sockets in collapsed ("hidden") nodes...
[blender.git] / source / blender / editors / space_node / node_draw.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) 2008 Blender Foundation.
19  * All rights reserved.
20  *
21  * The Original Code is: all of this file.
22  * Contributor(s): Nathan Letwory
23  *
24  * ***** END GPL LICENSE BLOCK *****
25  */
26
27 /** \file blender/editors/space_node/node_draw.c
28  *  \ingroup spnode
29  */
30
31
32 #include <math.h>
33 #include <stdio.h>
34 #include <string.h>
35
36 #include "MEM_guardedalloc.h"
37
38 #include "DNA_node_types.h"
39 #include "DNA_lamp_types.h"
40 #include "DNA_material_types.h"
41 #include "DNA_object_types.h"
42 #include "DNA_scene_types.h"
43 #include "DNA_space_types.h"
44 #include "DNA_screen_types.h"
45 #include "DNA_world_types.h"
46
47 #include "BLI_math.h"
48 #include "BLI_blenlib.h"
49 #include "BLI_threads.h"
50 #include "BLI_utildefines.h"
51
52 #include "BKE_context.h"
53 #include "BKE_depsgraph.h"
54 #include "BKE_main.h"
55 #include "BKE_node.h"
56
57 #include "BIF_gl.h"
58 #include "BIF_glutil.h"
59
60 #include "WM_api.h"
61 #include "WM_types.h"
62
63 #include "ED_node.h"
64 #include "ED_gpencil.h"
65
66 #include "UI_interface.h"
67 #include "UI_interface_icons.h"
68 #include "UI_resources.h"
69 #include "UI_view2d.h"
70
71 #include "RNA_access.h"
72
73 #include "NOD_composite.h"
74 #include "NOD_shader.h"
75
76 #include "intern/node_util.h"
77
78 #include "node_intern.h"
79
80 /* width of socket columns in group display */
81 #define NODE_GROUP_FRAME                120
82
83 // XXX interface.h
84 extern void ui_dropshadow(rctf *rct, float radius, float aspect, int select);
85
86 /* XXX update functions for node editor are a mess, needs a clear concept */
87 void ED_node_tree_update(SpaceNode *snode, Scene *scene)
88 {
89         snode_set_context(snode, scene);
90         
91         if(snode->nodetree && snode->nodetree->id.us==0)
92                 snode->nodetree->id.us= 1;
93 }
94
95 void ED_node_changed_update(ID *id, bNode *node)
96 {
97         bNodeTree *nodetree, *edittree;
98         int treetype;
99
100         node_tree_from_ID(id, &nodetree, &edittree, &treetype);
101
102         if(treetype==NTREE_SHADER) {
103                 DAG_id_tag_update(id, 0);
104
105                 if(GS(id->name) == ID_MA)
106                         WM_main_add_notifier(NC_MATERIAL|ND_SHADING_DRAW, id);
107                 else if(GS(id->name) == ID_LA)
108                         WM_main_add_notifier(NC_LAMP|ND_LIGHTING_DRAW, id);
109                 else if(GS(id->name) == ID_WO)
110                         WM_main_add_notifier(NC_WORLD|ND_WORLD_DRAW, id);
111         }
112         else if(treetype==NTREE_COMPOSIT) {
113                 if(node)
114                         nodeUpdate(edittree, node);
115                 /* don't use NodeTagIDChanged, it gives far too many recomposites for image, scene layers, ... */
116                         
117                 node= node_tree_get_editgroup(nodetree);
118                 if(node)
119                         nodeUpdateID(nodetree, node->id);
120
121                 WM_main_add_notifier(NC_SCENE|ND_NODES, id);
122         }                       
123         else if(treetype==NTREE_TEXTURE) {
124                 DAG_id_tag_update(id, 0);
125                 WM_main_add_notifier(NC_TEXTURE|ND_NODES, id);
126         }
127 }
128
129 static int has_nodetree(bNodeTree *ntree, bNodeTree *lookup)
130 {
131         bNode *node;
132         
133         if(ntree == lookup)
134                 return 1;
135         
136         for(node=ntree->nodes.first; node; node=node->next)
137                 if(node->type == NODE_GROUP && node->id)
138                         if(has_nodetree((bNodeTree*)node->id, lookup))
139                                 return 1;
140         
141         return 0;
142 }
143
144 typedef struct NodeUpdateCalldata {
145         bNodeTree *ntree;
146         bNode *node;
147 } NodeUpdateCalldata;
148 static void node_generic_update_cb(void *calldata, ID *owner_id, bNodeTree *ntree)
149 {
150         NodeUpdateCalldata *cd= (NodeUpdateCalldata*)calldata;
151         /* check if nodetree uses the group stored in calldata */
152         if (has_nodetree(ntree, cd->ntree))
153                 ED_node_changed_update(owner_id, cd->node);
154 }
155 void ED_node_generic_update(Main *bmain, bNodeTree *ntree, bNode *node)
156 {
157         bNodeTreeType *tti= ntreeGetType(ntree->type);
158         NodeUpdateCalldata cd;
159         cd.ntree = ntree;
160         cd.node = node;
161         /* look through all datablocks, to support groups */
162         tti->foreach_nodetree(bmain, &cd, node_generic_update_cb);
163         
164         if(ntree->type == NTREE_TEXTURE)
165                 ntreeTexCheckCyclics(ntree);
166 }
167
168 static void do_node_internal_buttons(bContext *C, void *node_v, int event)
169 {
170         if(event==B_NODE_EXEC) {
171                 SpaceNode *snode= CTX_wm_space_node(C);
172                 if(snode && snode->id)
173                         ED_node_changed_update(snode->id, node_v);
174         }
175 }
176
177
178 static void node_scaling_widget(int color_id, float aspect, float xmin, float ymin, float xmax, float ymax)
179 {
180         float dx;
181         float dy;
182         
183         dx= 0.5f*(xmax-xmin);
184         dy= 0.5f*(ymax-ymin);
185         
186         UI_ThemeColorShade(color_id, +30);      
187         fdrawline(xmin, ymin, xmax, ymax);
188         fdrawline(xmin+dx, ymin, xmax, ymax-dy);
189         
190         UI_ThemeColorShade(color_id, -10);
191         fdrawline(xmin, ymin+aspect, xmax, ymax+aspect);
192         fdrawline(xmin+dx, ymin+aspect, xmax, ymax-dy+aspect);
193 }
194
195 static void node_uiblocks_init(const bContext *C, bNodeTree *ntree)
196 {
197         bNode *node;
198         char str[32];
199         
200         /* add node uiBlocks in drawing order - prevents events going to overlapping nodes */
201         
202         for(node= ntree->nodes.first; node; node=node->next) {
203                         /* ui block */
204                         sprintf(str, "node buttons %p", (void *)node);
205                         node->block= uiBeginBlock(C, CTX_wm_region(C), str, UI_EMBOSS);
206                         uiBlockSetHandleFunc(node->block, do_node_internal_buttons, node);
207                         
208                         /* this cancels events for background nodes */
209                         uiBlockSetFlag(node->block, UI_BLOCK_CLIP_EVENTS);
210         }
211 }
212
213 /* based on settings in node, sets drawing rect info. each redraw! */
214 static void node_update_basis(const bContext *C, bNodeTree *ntree, bNode *node)
215 {
216         uiLayout *layout;
217         PointerRNA ptr;
218         bNodeSocket *nsock;
219         float locx, locy;
220         float dy;
221         int buty;
222         
223         /* get "global" coords */
224         nodeSpaceCoords(node, &locx, &locy);
225         dy= locy;
226         
227         /* header */
228         dy-= NODE_DY;
229         
230         /* little bit space in top */
231         if(node->outputs.first)
232                 dy-= NODE_DYS/2;
233
234         /* output sockets */
235         for(nsock= node->outputs.first; nsock; nsock= nsock->next) {
236                 if(!(nsock->flag & (SOCK_HIDDEN|SOCK_UNAVAIL))) {
237                         nsock->locx= locx + node->width;
238                         nsock->locy= dy - NODE_DYS;
239                         dy-= NODE_DY;
240                 }
241         }
242
243         node->prvr.xmin= locx + NODE_DYS;
244         node->prvr.xmax= locx + node->width- NODE_DYS;
245
246         /* preview rect? */
247         if(node->flag & NODE_PREVIEW) {
248                 /* only recalculate size when there's a preview actually, otherwise we use stored result */
249                 BLI_lock_thread(LOCK_PREVIEW);
250
251                 if(node->preview && node->preview->rect) {
252                         float aspect= 1.0f;
253                         
254                         if(node->preview && node->preview->xsize && node->preview->ysize) 
255                                 aspect= (float)node->preview->ysize/(float)node->preview->xsize;
256                         
257                         dy-= NODE_DYS/2;
258                         node->prvr.ymax= dy;
259                         
260                         if(aspect <= 1.0f)
261                                 node->prvr.ymin= dy - aspect*(node->width-NODE_DY);
262                         else {
263                                 float dx= (node->width - NODE_DYS) - (node->width- NODE_DYS)/aspect;    /* width correction of image */
264                                 
265                                 node->prvr.ymin= dy - (node->width-NODE_DY);
266                                 
267                                 node->prvr.xmin+= 0.5f*dx;
268                                 node->prvr.xmax-= 0.5f*dx;
269                         }
270
271                         dy= node->prvr.ymin - NODE_DYS/2;
272
273                         /* make sure that maximums are bigger or equal to minimums */
274                         if(node->prvr.xmax < node->prvr.xmin) SWAP(float, node->prvr.xmax, node->prvr.xmin);
275                         if(node->prvr.ymax < node->prvr.ymin) SWAP(float, node->prvr.ymax, node->prvr.ymin);
276                 }
277                 else {
278                         float oldh= node->prvr.ymax - node->prvr.ymin;
279                         if(oldh==0.0f)
280                                 oldh= 0.6f*node->width-NODE_DY;
281                         dy-= NODE_DYS/2;
282                         node->prvr.ymax= dy;
283                         node->prvr.ymin= dy - oldh;
284                         dy= node->prvr.ymin - NODE_DYS/2;
285                 }
286
287                 BLI_unlock_thread(LOCK_PREVIEW);
288         }
289
290         /* buttons rect? */
291         if((node->flag & NODE_OPTIONS) && node->typeinfo->uifunc) {
292                 dy-= NODE_DYS/2;
293
294                 /* set this for uifunc() that don't use layout engine yet */
295                 node->butr.xmin= 0;
296                 node->butr.xmax= node->width - 2*NODE_DYS;
297                 node->butr.ymin= 0;
298                 node->butr.ymax= 0;
299                 
300                 RNA_pointer_create(&ntree->id, &RNA_Node, node, &ptr);
301                 
302                 layout= uiBlockLayout(node->block, UI_LAYOUT_VERTICAL, UI_LAYOUT_PANEL,
303                                                           locx+NODE_DYS, dy, node->butr.xmax, NODE_DY, UI_GetStyle());
304                 
305                 node->typeinfo->uifunc(layout, (bContext *)C, &ptr);
306                 
307                 uiBlockEndAlign(node->block);
308                 uiBlockLayoutResolve(node->block, NULL, &buty);
309                 
310                 dy= buty - NODE_DYS/2;
311         }
312
313         /* input sockets */
314         for(nsock= node->inputs.first; nsock; nsock= nsock->next) {
315                 if(!(nsock->flag & (SOCK_HIDDEN|SOCK_UNAVAIL))) {
316                         nsock->locx= locx;
317                         nsock->locy= dy - NODE_DYS;
318                         dy-= NODE_DY;
319                 }
320         }
321         
322         /* little bit space in end */
323         if(node->inputs.first || (node->flag & (NODE_OPTIONS|NODE_PREVIEW))==0 )
324                 dy-= NODE_DYS/2;
325
326         node->totr.xmin= locx;
327         node->totr.xmax= locx + node->width;
328         node->totr.ymax= locy;
329         node->totr.ymin= MIN2(dy, locy-2*NODE_DY);
330         
331         /* Set the block bounds to clip mouse events from underlying nodes.
332          * Add a margin for sockets on each side.
333          */
334         uiExplicitBoundsBlock(node->block,
335                                                   node->totr.xmin - NODE_SOCKSIZE,
336                                                   node->totr.ymin,
337                                                   node->totr.xmax + NODE_SOCKSIZE,
338                                                   node->totr.ymax);
339 }
340
341 /* based on settings in node, sets drawing rect info. each redraw! */
342 static void node_update_hidden(bNode *node)
343 {
344         bNodeSocket *nsock;
345         float locx, locy;
346         float rad, drad, hiddenrad= HIDDEN_RAD;
347         int totin=0, totout=0, tot;
348         
349         /* get "global" coords */
350         nodeSpaceCoords(node, &locx, &locy);
351
352         /* calculate minimal radius */
353         for(nsock= node->inputs.first; nsock; nsock= nsock->next)
354                 if(!(nsock->flag & (SOCK_HIDDEN|SOCK_UNAVAIL)) && (nsock->flag & SOCK_IN_USE))
355                         totin++;
356         for(nsock= node->outputs.first; nsock; nsock= nsock->next)
357                 if(!(nsock->flag & (SOCK_HIDDEN|SOCK_UNAVAIL)) && (nsock->flag & SOCK_IN_USE))
358                         totout++;
359         
360         tot= MAX2(totin, totout);
361         if(tot>4) {
362                 hiddenrad += 5.0f*(float)(tot-4);
363         }
364         
365         node->totr.xmin= locx;
366         node->totr.xmax= locx + 3*hiddenrad + node->miniwidth;
367         node->totr.ymax= locy + (hiddenrad - 0.5f*NODE_DY);
368         node->totr.ymin= node->totr.ymax - 2*hiddenrad;
369         
370         /* output sockets */
371         rad=drad= (float)M_PI/(1.0f + (float)totout);
372         
373         for(nsock= node->outputs.first; nsock; nsock= nsock->next) {
374                 if(!(nsock->flag & (SOCK_HIDDEN|SOCK_UNAVAIL)) && (nsock->flag & SOCK_IN_USE)) {
375                         nsock->locx= node->totr.xmax - hiddenrad + (float)sin(rad)*hiddenrad;
376                         nsock->locy= node->totr.ymin + hiddenrad + (float)cos(rad)*hiddenrad;
377                         rad+= drad;
378                 }
379         }
380         
381         /* input sockets */
382         rad=drad= - (float)M_PI/(1.0f + (float)totin);
383         
384         for(nsock= node->inputs.first; nsock; nsock= nsock->next) {
385                 if(!(nsock->flag & (SOCK_HIDDEN|SOCK_UNAVAIL)) && (nsock->flag & SOCK_IN_USE)) {
386                         nsock->locx= node->totr.xmin + hiddenrad + (float)sin(rad)*hiddenrad;
387                         nsock->locy= node->totr.ymin + hiddenrad + (float)cos(rad)*hiddenrad;
388                         rad+= drad;
389                 }
390         }
391
392         /* Set the block bounds to clip mouse events from underlying nodes.
393          * Add a margin for sockets on each side.
394          */
395         uiExplicitBoundsBlock(node->block,
396                                                   node->totr.xmin - NODE_SOCKSIZE,
397                                                   node->totr.ymin,
398                                                   node->totr.xmax + NODE_SOCKSIZE,
399                                                   node->totr.ymax);
400 }
401
402 void node_update_default(const bContext *C, bNodeTree *ntree, bNode *node)
403 {
404         if(node->flag & NODE_HIDDEN)
405                 node_update_hidden(node);
406         else
407                 node_update_basis(C, ntree, node);
408 }
409
410 static int node_get_colorid(bNode *node)
411 {
412         if(node->typeinfo->nclass==NODE_CLASS_INPUT)
413                 return TH_NODE_IN_OUT;
414         if(node->typeinfo->nclass==NODE_CLASS_OUTPUT) {
415                 if(node->flag & NODE_DO_OUTPUT)
416                         return TH_NODE_IN_OUT;
417                 else
418                         return TH_NODE;
419         }
420         if(node->typeinfo->nclass==NODE_CLASS_CONVERTOR)
421                 return TH_NODE_CONVERTOR;
422         if(ELEM3(node->typeinfo->nclass, NODE_CLASS_OP_COLOR, NODE_CLASS_OP_VECTOR, NODE_CLASS_OP_FILTER))
423                 return TH_NODE_OPERATOR;
424         if(node->typeinfo->nclass==NODE_CLASS_GROUP)
425                 return TH_NODE_GROUP;
426         return TH_NODE;
427 }
428
429 /* note: in cmp_util.c is similar code, for node_compo_pass_on()
430  *       the same goes for shader and texture nodes. */
431 /* note: in node_edit.c is similar code, for untangle node */
432 static void node_draw_mute_line(View2D *v2d, SpaceNode *snode, bNode *node)
433 {
434         ListBase links;
435         LinkInOutsMuteNode *lnk;
436         bNodeLink link= {NULL};
437         int i;
438
439         if(node->typeinfo->mutelinksfunc == NULL)
440                 return;
441
442         /* Get default muting links (as bNodeSocket pointers). */
443         links = node->typeinfo->mutelinksfunc(snode->edittree, node, NULL, NULL, NULL, NULL);
444
445         glEnable(GL_BLEND);
446         glEnable(GL_LINE_SMOOTH);
447
448         link.fromnode = link.tonode = node;
449         for(lnk = links.first; lnk; lnk = lnk->next) {
450                 for(i = 0; i < lnk->num_outs; i++) {
451                         link.fromsock = (bNodeSocket*)(lnk->in);
452                         link.tosock   = (bNodeSocket*)(lnk->outs)+i;
453                         node_draw_link_bezier(v2d, snode, &link, TH_REDALERT, 0, TH_WIRE, 0, TH_WIRE);
454                 }
455                 /* If num_outs > 1, lnk->outs was an allocated table of pointers... */
456                 if(i > 1)
457                         MEM_freeN(lnk->outs);
458         }
459
460         glDisable(GL_BLEND);
461         glDisable(GL_LINE_SMOOTH);
462
463         BLI_freelistN(&links);
464 }
465
466 /* this might have some more generic use */
467 static void node_circle_draw(float x, float y, float size, char *col)
468 {
469         /* 16 values of sin function */
470         static float si[16] = {
471                 0.00000000f, 0.39435585f,0.72479278f,0.93775213f,
472                 0.99871650f,0.89780453f,0.65137248f,0.29936312f,
473                 -0.10116832f,-0.48530196f,-0.79077573f,-0.96807711f,
474                 -0.98846832f,-0.84864425f,-0.57126821f,-0.20129852f
475         };
476         /* 16 values of cos function */
477         static float co[16] ={
478                 1.00000000f,0.91895781f,0.68896691f,0.34730525f,
479                 -0.05064916f,-0.44039415f,-0.75875812f,-0.95413925f,
480                 -0.99486932f,-0.87434661f,-0.61210598f,-0.25065253f,
481                 0.15142777f,0.52896401f,0.82076344f,0.97952994f,
482         };
483         int a;
484         
485         glColor3ub(col[0], col[1], col[2]);
486         
487         glBegin(GL_POLYGON);
488         for(a=0; a<16; a++)
489                 glVertex2f(x+size*si[a], y+size*co[a]);
490         glEnd();
491         
492         glColor4ub(0, 0, 0, 150);
493         glEnable(GL_BLEND);
494         glEnable( GL_LINE_SMOOTH );
495         glBegin(GL_LINE_LOOP);
496         for(a=0; a<16; a++)
497                 glVertex2f(x+size*si[a], y+size*co[a]);
498         glEnd();
499         glDisable( GL_LINE_SMOOTH );
500         glDisable(GL_BLEND);
501 }
502
503 void node_socket_circle_draw(bNodeTree *UNUSED(ntree), bNodeSocket *sock, float size)
504 {
505         bNodeSocketType *stype = ntreeGetSocketType(sock->type);
506         node_circle_draw(sock->locx, sock->locy, size, stype->ui_color);
507 }
508
509 /* **************  Socket callbacks *********** */
510
511 /* not a callback */
512 static void node_draw_preview(bNodePreview *preview, rctf *prv)
513 {
514         float xscale= (prv->xmax-prv->xmin)/((float)preview->xsize);
515         float yscale= (prv->ymax-prv->ymin)/((float)preview->ysize);
516         float tile= (prv->xmax - prv->xmin) / 10.0f;
517         float x, y;
518         
519         /* draw checkerboard backdrop to show alpha */
520         glColor3ub(120, 120, 120);
521         glRectf(prv->xmin, prv->ymin, prv->xmax, prv->ymax);
522         glColor3ub(160, 160, 160);
523         
524         for(y=prv->ymin; y<prv->ymax; y+=tile*2) {
525                 for(x=prv->xmin; x<prv->xmax; x+=tile*2) {
526                         float tilex= tile, tiley= tile;
527
528                         if(x+tile > prv->xmax)
529                                 tilex= prv->xmax-x;
530                         if(y+tile > prv->ymax)
531                                 tiley= prv->ymax-y;
532
533                         glRectf(x, y, x + tilex, y + tiley);
534                 }
535         }
536         for(y=prv->ymin+tile; y<prv->ymax; y+=tile*2) {
537                 for(x=prv->xmin+tile; x<prv->xmax; x+=tile*2) {
538                         float tilex= tile, tiley= tile;
539
540                         if(x+tile > prv->xmax)
541                                 tilex= prv->xmax-x;
542                         if(y+tile > prv->ymax)
543                                 tiley= prv->ymax-y;
544
545                         glRectf(x, y, x + tilex, y + tiley);
546                 }
547         }
548         
549         glPixelZoom(xscale, yscale);
550
551         glEnable(GL_BLEND);
552         glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );    /* premul graphics */
553         
554         glColor4f(1.0, 1.0, 1.0, 1.0);
555         glaDrawPixelsTex(prv->xmin, prv->ymin, preview->xsize, preview->ysize, GL_UNSIGNED_BYTE, preview->rect);
556         
557         glDisable(GL_BLEND);
558         glPixelZoom(1.0f, 1.0f);
559
560         UI_ThemeColorShadeAlpha(TH_BACK, -15, +100);
561         fdrawbox(prv->xmin, prv->ymin, prv->xmax, prv->ymax);
562         
563 }
564
565 /* common handle function for operator buttons that need to select the node first */
566 static void node_toggle_button_cb(struct bContext *C, void *node_argv, void *op_argv)
567 {
568         bNode *node = (bNode*)node_argv;
569         const char *opname = (const char *)op_argv;
570         
571         /* select & activate only the button's node */
572         node_select_single(C, node);
573         
574         WM_operator_name_call(C, opname, WM_OP_INVOKE_DEFAULT, NULL);
575 }
576
577 static void node_draw_basis(const bContext *C, ARegion *ar, SpaceNode *snode, bNodeTree *ntree, bNode *node)
578 {
579         bNodeSocket *sock;
580         rctf *rct= &node->totr;
581         float iconofs;
582         /* float socket_size= NODE_SOCKSIZE*U.dpi/72; */ /* UNUSED */
583         float iconbutw= 0.8f*UI_UNIT_X;
584         int color_id= node_get_colorid(node);
585         char showname[128]; /* 128 used below */
586         View2D *v2d = &ar->v2d;
587         
588         /* hurmf... another candidate for callback, have to see how this works first */
589         if(node->id && node->block && snode->treetype==NTREE_SHADER)
590                 nodeShaderSynchronizeID(node, 0);
591         
592         /* skip if out of view */
593         if (node->totr.xmax < ar->v2d.cur.xmin || node->totr.xmin > ar->v2d.cur.xmax ||
594                         node->totr.ymax < ar->v2d.cur.ymin || node->totr.ymin > ar->v2d.cur.ymax) {
595                 
596                 uiEndBlock(C, node->block);
597                 node->block= NULL;
598                 return;
599         }
600         
601         uiSetRoundBox(UI_CNR_TOP_LEFT | UI_CNR_TOP_RIGHT | UI_CNR_BOTTOM_LEFT);
602         ui_dropshadow(rct, BASIS_RAD, snode->aspect, node->flag & SELECT);
603         
604         /* header */
605         if(color_id==TH_NODE)
606                 UI_ThemeColorShade(color_id, -20);
607         else
608                 UI_ThemeColor(color_id);
609         
610         if(node->flag & NODE_MUTED)
611                 UI_ThemeColorBlend(color_id, TH_REDALERT, 0.5f);
612
613         uiSetRoundBox(UI_CNR_TOP_LEFT | UI_CNR_TOP_RIGHT);
614         uiRoundBox(rct->xmin, rct->ymax-NODE_DY, rct->xmax, rct->ymax, BASIS_RAD);
615         
616         /* show/hide icons */
617         iconofs= rct->xmax - 7.0f;
618         
619         /* preview */
620         if(node->typeinfo->flag & NODE_PREVIEW) {
621                 uiBut *but;
622                 iconofs-=iconbutw;
623                 uiBlockSetEmboss(node->block, UI_EMBOSSN);
624                 but = uiDefIconBut(node->block, TOGBUT, B_REDR, ICON_MATERIAL,
625                                                    iconofs, rct->ymax-NODE_DY, iconbutw, UI_UNIT_Y, NULL, 0, 0, 0, 0, "");
626                 uiButSetFunc(but, node_toggle_button_cb, node, (void*)"NODE_OT_preview_toggle");
627                 /* XXX this does not work when node is activated and the operator called right afterwards,
628                  * since active ID is not updated yet (needs to process the notifier).
629                  * This can only work as visual indicator!
630                  */
631 //              if (!(node->flag & (NODE_ACTIVE_ID|NODE_DO_OUTPUT)))
632 //                      uiButSetFlag(but, UI_BUT_DISABLED);
633                 uiBlockSetEmboss(node->block, UI_EMBOSS);
634         }
635         /* group edit */
636         if(node->type == NODE_GROUP) {
637                 uiBut *but;
638                 iconofs-=iconbutw;
639                 uiBlockSetEmboss(node->block, UI_EMBOSSN);
640                 but = uiDefIconBut(node->block, TOGBUT, B_REDR, ICON_NODETREE,
641                                                    iconofs, rct->ymax-NODE_DY, iconbutw, UI_UNIT_Y, NULL, 0, 0, 0, 0, "");
642                 uiButSetFunc(but, node_toggle_button_cb, node, (void*)"NODE_OT_group_edit");
643                 uiBlockSetEmboss(node->block, UI_EMBOSS);
644         }
645         
646         /* title */
647         if(node->flag & SELECT) 
648                 UI_ThemeColor(TH_TEXT_HI);
649         else
650                 UI_ThemeColorBlendShade(TH_TEXT, color_id, 0.4f, 10);
651         
652         /* open/close entirely? */
653         {
654                 uiBut *but;
655                 int but_size = UI_UNIT_X *0.6f;
656                 /* XXX button uses a custom triangle draw below, so make it invisible without icon */
657                 uiBlockSetEmboss(node->block, UI_EMBOSSN);
658                 but = uiDefBut(node->block, TOGBUT, B_REDR, "",
659                                            rct->xmin+10.0f-but_size/2, rct->ymax-NODE_DY/2.0f-but_size/2, but_size, but_size, NULL, 0, 0, 0, 0, "");
660                 uiButSetFunc(but, node_toggle_button_cb, node, (void*)"NODE_OT_hide_toggle");
661                 uiBlockSetEmboss(node->block, UI_EMBOSS);
662                 
663                 /* custom draw function for this button */
664                 UI_DrawTriIcon(rct->xmin+10.0f, rct->ymax-NODE_DY/2.0f, 'v');
665         }
666         
667         /* this isn't doing anything for the label, so commenting out
668         if(node->flag & SELECT) 
669                 UI_ThemeColor(TH_TEXT_HI);
670         else
671                 UI_ThemeColor(TH_TEXT); */
672         
673         BLI_strncpy(showname, nodeLabel(node), sizeof(showname));
674         
675         //if(node->flag & NODE_MUTED)
676         //      sprintf(showname, "[%s]", showname);
677         
678         uiDefBut(node->block, LABEL, 0, showname, (short)(rct->xmin+15), (short)(rct->ymax-NODE_DY), 
679                          (int)(iconofs - rct->xmin-18.0f), NODE_DY,  NULL, 0, 0, 0, 0, "");
680
681         /* body */
682         UI_ThemeColor4(TH_NODE);
683         glEnable(GL_BLEND);
684         uiSetRoundBox(UI_CNR_BOTTOM_LEFT);
685         uiRoundBox(rct->xmin, rct->ymin, rct->xmax, rct->ymax-NODE_DY, BASIS_RAD);
686         glDisable(GL_BLEND);
687
688         /* scaling indicator */
689         node_scaling_widget(TH_NODE, snode->aspect, rct->xmax-BASIS_RAD*snode->aspect, rct->ymin, rct->xmax, rct->ymin+BASIS_RAD*snode->aspect);
690
691         /* outline active and selected emphasis */
692         if( node->flag & (NODE_ACTIVE|SELECT) ) {
693                 glEnable(GL_BLEND);
694                 glEnable( GL_LINE_SMOOTH );
695                         /* using different shades of TH_TEXT_HI for the empasis, like triangle */
696                         if( node->flag & NODE_ACTIVE ) 
697                                 UI_ThemeColorShadeAlpha(TH_TEXT_HI, 0, -40);
698                         else
699                                 UI_ThemeColorShadeAlpha(TH_TEXT_HI, -20, -120);
700                         uiSetRoundBox(UI_CNR_TOP_LEFT | UI_CNR_TOP_RIGHT | UI_CNR_BOTTOM_LEFT); // round all corners except lower right
701                         uiDrawBox(GL_LINE_LOOP, rct->xmin, rct->ymin, rct->xmax, rct->ymax, BASIS_RAD);
702                         
703                 glDisable( GL_LINE_SMOOTH );
704                 glDisable(GL_BLEND);
705         }
706         
707         /* disable lines */
708         if(node->flag & NODE_MUTED)
709                 node_draw_mute_line(v2d, snode, node);
710
711         
712         /* socket inputs, buttons */
713         for(sock= node->inputs.first; sock; sock= sock->next) {
714                 bNodeSocketType *stype= ntreeGetSocketType(sock->type);
715                 
716                 if(sock->flag & (SOCK_HIDDEN|SOCK_UNAVAIL))
717                         continue;
718                 
719                 node_socket_circle_draw(ntree, sock, NODE_SOCKSIZE);
720                 
721                 if (sock->link || (sock->flag & SOCK_HIDE_VALUE)) {
722                         uiDefBut(node->block, LABEL, 0, sock->name, sock->locx+NODE_DYS, sock->locy-NODE_DYS, node->width-NODE_DY, NODE_DY,
723                                          NULL, 0, 0, 0, 0, "");
724                 }
725                 else {
726                         if (stype->buttonfunc)
727                                 stype->buttonfunc(C, node->block, ntree, node, sock, sock->name, sock->locx+NODE_DYS, sock->locy-NODE_DYS, node->width-NODE_DY);
728                 }
729         }
730         
731         /* socket outputs */
732         for(sock= node->outputs.first; sock; sock= sock->next) {
733                 PointerRNA sockptr;
734                 float slen;
735                 int ofs;
736                 
737                 RNA_pointer_create((ID*)ntree, &RNA_NodeSocket, sock, &sockptr);
738                 
739                 if(sock->flag & (SOCK_HIDDEN|SOCK_UNAVAIL))
740                         continue;
741                 
742                 node_socket_circle_draw(ntree, sock, NODE_SOCKSIZE);
743                 
744                 ofs= 0;
745                 UI_ThemeColor(TH_TEXT);
746                 slen= snode->aspect*UI_GetStringWidth(sock->name);
747                 while(slen > node->width) {
748                         ofs++;
749                         slen= snode->aspect*UI_GetStringWidth(sock->name+ofs);
750                 }
751                 uiDefBut(node->block, LABEL, 0, sock->name+ofs, (short)(sock->locx-15.0f-slen), (short)(sock->locy-9.0f), 
752                                  (short)(node->width-NODE_DY), NODE_DY,  NULL, 0, 0, 0, 0, "");
753         }
754         
755         /* preview */
756         if(node->flag & NODE_PREVIEW) {
757                 BLI_lock_thread(LOCK_PREVIEW);
758                 if(node->preview && node->preview->rect && !BLI_rctf_is_empty(&node->prvr))
759                         node_draw_preview(node->preview, &node->prvr);
760                 BLI_unlock_thread(LOCK_PREVIEW);
761         }
762         
763         UI_ThemeClearColor(color_id);
764                 
765         uiEndBlock(C, node->block);
766         uiDrawBlock(C, node->block);
767         node->block= NULL;
768 }
769
770 static void node_draw_hidden(const bContext *C, ARegion *ar, SpaceNode *snode, bNode *node)
771 {
772         bNodeSocket *sock;
773         rctf *rct= &node->totr;
774         float dx, centy= 0.5f*(rct->ymax+rct->ymin);
775         float hiddenrad= 0.5f*(rct->ymax-rct->ymin);
776         float socket_size= NODE_SOCKSIZE*U.dpi/72;
777         int color_id= node_get_colorid(node);
778         char showname[128];     /* 128 is used below */
779         
780         /* shadow */
781         uiSetRoundBox(UI_CNR_ALL);
782         ui_dropshadow(rct, hiddenrad, snode->aspect, node->flag & SELECT);
783
784         /* body */
785         UI_ThemeColor(color_id);
786         if(node->flag & NODE_MUTED)
787                 UI_ThemeColorBlend(color_id, TH_REDALERT, 0.5f);
788         uiRoundBox(rct->xmin, rct->ymin, rct->xmax, rct->ymax, hiddenrad);
789         
790         /* outline active and selected emphasis */
791         if( node->flag & (NODE_ACTIVE|SELECT) ) {
792                 glEnable(GL_BLEND);
793                 glEnable( GL_LINE_SMOOTH );
794                         /* using different shades of TH_TEXT_HI for the empasis, like triangle */
795                         if( node->flag & NODE_ACTIVE ) 
796                                 UI_ThemeColorShadeAlpha(TH_TEXT_HI, 0, -40);
797                         else
798                                 UI_ThemeColorShadeAlpha(TH_TEXT_HI, -20, -120);
799                         uiDrawBox(GL_LINE_LOOP, rct->xmin, rct->ymin, rct->xmax, rct->ymax, hiddenrad);
800                 glDisable( GL_LINE_SMOOTH );
801                 glDisable(GL_BLEND);
802         }
803         
804         /* title */
805         if(node->flag & SELECT) 
806                 UI_ThemeColor(TH_TEXT_HI);
807         else
808                 UI_ThemeColorBlendShade(TH_TEXT, color_id, 0.4f, 10);
809         
810         /* open entirely icon */
811         {
812                 uiBut *but;
813                 int but_size = UI_UNIT_X *0.6f;
814                 /* XXX button uses a custom triangle draw below, so make it invisible without icon */
815                 uiBlockSetEmboss(node->block, UI_EMBOSSN);
816                 but = uiDefBut(node->block, TOGBUT, B_REDR, "",
817                                            rct->xmin+10.0f-but_size/2, centy-but_size/2, but_size, but_size, NULL, 0, 0, 0, 0, "");
818                 uiButSetFunc(but, node_toggle_button_cb, node, (void*)"NODE_OT_hide_toggle");
819                 uiBlockSetEmboss(node->block, UI_EMBOSS);
820                 
821                 /* custom draw function for this button */
822                 UI_DrawTriIcon(rct->xmin+10.0f, centy, 'h');
823         }
824         
825         /* disable lines */
826         if(node->flag & NODE_MUTED)
827                 node_draw_mute_line(&ar->v2d, snode, node);     
828         
829         if(node->flag & SELECT) 
830                 UI_ThemeColor(TH_TEXT_HI);
831         else
832                 UI_ThemeColor(TH_TEXT);
833         
834         if(node->miniwidth>0.0f) {
835                 BLI_strncpy(showname, nodeLabel(node), sizeof(showname));
836                 
837                 //if(node->flag & NODE_MUTED)
838                 //      sprintf(showname, "[%s]", showname);
839
840                 uiDefBut(node->block, LABEL, 0, showname, (short)(rct->xmin+15), (short)(centy-10), 
841                                  (int)(rct->xmax - rct->xmin-18.0f -12.0f), NODE_DY,  NULL, 0, 0, 0, 0, "");
842         }       
843
844         /* scale widget thing */
845         UI_ThemeColorShade(color_id, -10);      
846         dx= 10.0f;
847         fdrawline(rct->xmax-dx, centy-4.0f, rct->xmax-dx, centy+4.0f);
848         fdrawline(rct->xmax-dx-3.0f*snode->aspect, centy-4.0f, rct->xmax-dx-3.0f*snode->aspect, centy+4.0f);
849         
850         UI_ThemeColorShade(color_id, +30);
851         dx-= snode->aspect;
852         fdrawline(rct->xmax-dx, centy-4.0f, rct->xmax-dx, centy+4.0f);
853         fdrawline(rct->xmax-dx-3.0f*snode->aspect, centy-4.0f, rct->xmax-dx-3.0f*snode->aspect, centy+4.0f);
854         
855         /* sockets */
856         for(sock= node->inputs.first; sock; sock= sock->next) {
857                 if(!(sock->flag & (SOCK_HIDDEN|SOCK_UNAVAIL)) && (sock->flag & SOCK_IN_USE))
858                         node_socket_circle_draw(snode->nodetree, sock, socket_size);
859         }
860         
861         for(sock= node->outputs.first; sock; sock= sock->next) {
862                 if(!(sock->flag & (SOCK_HIDDEN|SOCK_UNAVAIL)) && (sock->flag & SOCK_IN_USE))
863                         node_socket_circle_draw(snode->nodetree, sock, socket_size);
864         }
865         
866         uiEndBlock(C, node->block);
867         uiDrawBlock(C, node->block);
868         node->block= NULL;
869 }
870
871 void node_draw_default(const bContext *C, ARegion *ar, SpaceNode *snode, bNodeTree *ntree, bNode *node)
872 {
873         if(node->flag & NODE_HIDDEN)
874                 node_draw_hidden(C, ar, snode, node);
875         else
876                 node_draw_basis(C, ar, snode, ntree, node);
877 }
878
879 static void node_update(const bContext *C, bNodeTree *ntree, bNode *node)
880 {
881         if (node->typeinfo->drawupdatefunc)
882                 node->typeinfo->drawupdatefunc(C, ntree, node);
883 }
884
885 void node_update_nodetree(const bContext *C, bNodeTree *ntree, float offsetx, float offsety)
886 {
887         bNode *node;
888         
889         for(node= ntree->nodes.first; node; node= node->next) {
890                 /* XXX little hack */
891                 node->locx += offsetx;
892                 node->locy += offsety;
893                 
894                 node_update(C, ntree, node);
895                 
896                 node->locx -= offsetx;
897                 node->locy -= offsety;
898         }
899 }
900
901 static void node_draw(const bContext *C, ARegion *ar, SpaceNode *snode, bNodeTree *ntree, bNode *node)
902 {
903         if (node->typeinfo->drawfunc)
904                 node->typeinfo->drawfunc(C, ar, snode, ntree, node);
905 }
906
907 void node_draw_nodetree(const bContext *C, ARegion *ar, SpaceNode *snode, bNodeTree *ntree)
908 {
909         bNode *node;
910         bNodeLink *link;
911         int a;
912         
913         if(ntree==NULL) return;         /* groups... */
914         
915         /* node lines */
916         glEnable(GL_BLEND);
917         glEnable(GL_LINE_SMOOTH);
918         for(link= ntree->links.first; link; link= link->next)
919                 node_draw_link(&ar->v2d, snode, link);
920         glDisable(GL_LINE_SMOOTH);
921         glDisable(GL_BLEND);
922         
923         /* draw nodes, last nodes in front */
924         for(a=0, node= ntree->nodes.first; node; node=node->next, a++) {
925                 node->nr= a;            /* index of node in list, used for exec event code */
926                 node_draw(C, ar, snode, ntree, node);
927         }
928 }
929
930 void drawnodespace(const bContext *C, ARegion *ar, View2D *v2d)
931 {
932         View2DScrollers *scrollers;
933         SpaceNode *snode= CTX_wm_space_node(C);
934         Scene *scene= CTX_data_scene(C);
935         int color_manage = scene->r.color_mgt_flag & R_COLOR_MANAGEMENT;
936         bNodeLinkDrag *nldrag;
937         
938         UI_ThemeClearColor(TH_BACK);
939         glClear(GL_COLOR_BUFFER_BIT);
940
941         UI_view2d_view_ortho(v2d);
942         
943         //uiFreeBlocksWin(&sa->uiblocks, sa->win);
944
945         /* only set once */
946         glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
947         glEnable(GL_MAP1_VERTEX_3);
948
949         /* aspect+font, set each time */
950         snode->aspect= (v2d->cur.xmax - v2d->cur.xmin)/((float)ar->winx);
951         // XXX snode->curfont= uiSetCurFont_ext(snode->aspect);
952
953         UI_view2d_constant_grid_draw(v2d);
954         /* backdrop */
955         draw_nodespace_back_pix(ar, snode, color_manage);
956         
957         /* nodes */
958         snode_set_context(snode, CTX_data_scene(C));
959         
960         if(snode->nodetree) {
961                 bNode *node;
962                 
963                 node_uiblocks_init(C, snode->nodetree);
964                 
965                 /* uiBlocks must be initialized in drawing order for correct event clipping.
966                  * Node group internal blocks added after the main group block.
967                  */
968                 for(node= snode->nodetree->nodes.first; node; node= node->next) {
969                         if(node->flag & NODE_GROUP_EDIT)
970                                 node_uiblocks_init(C, (bNodeTree *)node->id);
971                 }
972                 
973                 node_update_nodetree(C, snode->nodetree, 0.0f, 0.0f);
974                 node_draw_nodetree(C, ar, snode, snode->nodetree);
975                 
976                 #if 0
977                 /* active group */
978                 for(node= snode->nodetree->nodes.first; node; node= node->next) {
979                         if(node->flag & NODE_GROUP_EDIT)
980                                 node_draw_group(C, ar, snode, snode->nodetree, node);
981                 }
982                 #endif
983         }
984         
985         /* temporary links */
986         glEnable(GL_BLEND);
987         glEnable(GL_LINE_SMOOTH);
988         for(nldrag= snode->linkdrag.first; nldrag; nldrag= nldrag->next)
989                 node_draw_link(&ar->v2d, snode, nldrag->link);
990         glDisable(GL_LINE_SMOOTH);
991         glDisable(GL_BLEND);
992         
993         /* draw grease-pencil ('canvas' strokes) */
994         if (/*(snode->flag & SNODE_DISPGP) &&*/ (snode->nodetree))
995                 draw_gpencil_view2d((bContext*)C, 1);
996         
997         /* reset view matrix */
998         UI_view2d_view_restore(C);
999         
1000         /* draw grease-pencil (screen strokes, and also paintbuffer) */
1001         if (/*(snode->flag & SNODE_DISPGP) && */(snode->nodetree))
1002                 draw_gpencil_view2d((bContext*)C, 0);
1003         
1004         /* scrollers */
1005         scrollers= UI_view2d_scrollers_calc(C, v2d, 10, V2D_GRID_CLAMP, V2D_ARG_DUMMY, V2D_ARG_DUMMY);
1006         UI_view2d_scrollers_draw(C, v2d, scrollers);
1007         UI_view2d_scrollers_free(scrollers);
1008 }