doxygen: blender/editors tagged.
[blender.git] / source / blender / editors / space_node / node_draw.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) 2008 Blender Foundation.
21  * All rights reserved.
22  *
23  * The Original Code is: all of this file.
24  * Contributor(s): Nathan Letwory
25  *
26  * ***** END GPL LICENSE BLOCK *****
27  */
28
29 /** \file blender/editors/space_node/node_draw.c
30  *  \ingroup spnode
31  */
32
33
34 #include <math.h>
35 #include <stdio.h>
36 #include <string.h>
37
38 #include "MEM_guardedalloc.h"
39
40 #include "DNA_node_types.h"
41 #include "DNA_material_types.h"
42 #include "DNA_object_types.h"
43 #include "DNA_scene_types.h"
44 #include "DNA_space_types.h"
45 #include "DNA_screen_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 "CMP_node.h"
74 #include "SHD_node.h"
75
76 #include "node_intern.h"
77
78 /* width of socket columns in group display */
79 #define NODE_GROUP_FRAME                120
80
81 // XXX interface.h
82 extern void ui_dropshadow(rctf *rct, float radius, float aspect, int select);
83
84 void ED_node_changed_update(ID *id, bNode *node)
85 {
86         bNodeTree *nodetree, *edittree;
87         int treetype;
88
89         node_tree_from_ID(id, &nodetree, &edittree, &treetype);
90
91         if(treetype==NTREE_SHADER) {
92                 DAG_id_tag_update(id, 0);
93                 WM_main_add_notifier(NC_MATERIAL|ND_SHADING_DRAW, id);
94         }
95         else if(treetype==NTREE_COMPOSIT) {
96                 NodeTagChanged(edittree, node);
97                 /* don't use NodeTagIDChanged, it gives far too many recomposites for image, scene layers, ... */
98                         
99                 node= node_tree_get_editgroup(nodetree);
100                 if(node)
101                         NodeTagIDChanged(nodetree, node->id);
102
103                 WM_main_add_notifier(NC_SCENE|ND_NODES, id);
104         }                       
105         else if(treetype==NTREE_TEXTURE) {
106                 DAG_id_tag_update(id, 0);
107                 WM_main_add_notifier(NC_TEXTURE|ND_NODES, id);
108         }
109 }
110
111 static int has_nodetree(bNodeTree *ntree, bNodeTree *lookup)
112 {
113         bNode *node;
114         
115         if(ntree == lookup)
116                 return 1;
117         
118         for(node=ntree->nodes.first; node; node=node->next)
119                 if(node->type == NODE_GROUP && node->id)
120                         if(has_nodetree((bNodeTree*)node->id, lookup))
121                                 return 1;
122         
123         return 0;
124 }
125
126 void ED_node_generic_update(Main *bmain, bNodeTree *ntree, bNode *node)
127 {
128         Material *ma;
129         Tex *tex;
130         Scene *sce;
131         
132         /* look through all datablocks, to support groups */
133         for(ma=bmain->mat.first; ma; ma=ma->id.next)
134                 if(ma->nodetree && ma->use_nodes && has_nodetree(ma->nodetree, ntree))
135                         ED_node_changed_update(&ma->id, node);
136         
137         for(tex=bmain->tex.first; tex; tex=tex->id.next)
138                 if(tex->nodetree && tex->use_nodes && has_nodetree(tex->nodetree, ntree))
139                         ED_node_changed_update(&tex->id, node);
140         
141         for(sce=bmain->scene.first; sce; sce=sce->id.next)
142                 if(sce->nodetree && sce->use_nodes && has_nodetree(sce->nodetree, ntree))
143                         ED_node_changed_update(&sce->id, node);
144         
145         if(ntree->type == NTREE_TEXTURE)
146                 ntreeTexCheckCyclics(ntree);
147 }
148
149 static void do_node_internal_buttons(bContext *C, void *node_v, int event)
150 {
151         if(event==B_NODE_EXEC) {
152                 SpaceNode *snode= CTX_wm_space_node(C);
153                 if(snode && snode->id)
154                         ED_node_changed_update(snode->id, node_v);
155         }
156 }
157
158
159 static void node_scaling_widget(int color_id, float aspect, float xmin, float ymin, float xmax, float ymax)
160 {
161         float dx;
162         float dy;
163         
164         dx= 0.5f*(xmax-xmin);
165         dy= 0.5f*(ymax-ymin);
166         
167         UI_ThemeColorShade(color_id, +30);      
168         fdrawline(xmin, ymin, xmax, ymax);
169         fdrawline(xmin+dx, ymin, xmax, ymax-dy);
170         
171         UI_ThemeColorShade(color_id, -10);
172         fdrawline(xmin, ymin+aspect, xmax, ymax+aspect);
173         fdrawline(xmin+dx, ymin+aspect, xmax, ymax-dy+aspect);
174 }
175
176 static void node_uiblocks_init(const bContext *C, bNodeTree *ntree)
177 {
178         bNode *node;
179         char str[32];
180         
181         /* add node uiBlocks in reverse order - prevents events going to overlapping nodes */
182         
183         /* process selected nodes first so they're at the start of the uiblocks list */
184         for(node= ntree->nodes.last; node; node= node->prev) {
185                 
186                 if (node->flag & NODE_SELECT) {
187                         /* ui block */
188                         sprintf(str, "node buttons %p", (void *)node);
189                         node->block= uiBeginBlock(C, CTX_wm_region(C), str, UI_EMBOSS);
190                         uiBlockSetHandleFunc(node->block, do_node_internal_buttons, node);
191                 }
192         }
193         
194         /* then the rest */
195         for(node= ntree->nodes.last; node; node= node->prev) {
196                 
197                 if (!(node->flag & (NODE_GROUP_EDIT|NODE_SELECT))) {
198                         /* ui block */
199                         sprintf(str, "node buttons %p", (void *)node);
200                         node->block= uiBeginBlock(C, CTX_wm_region(C), str, UI_EMBOSS);
201                         uiBlockSetHandleFunc(node->block, do_node_internal_buttons, node);
202                 }
203         }
204 }
205
206 /* based on settings in node, sets drawing rect info. each redraw! */
207 static void node_update(const bContext *C, bNodeTree *ntree, bNode *node)
208 {
209         uiLayout *layout;
210         PointerRNA ptr;
211         bNodeSocket *nsock;
212         float dy= node->locy;
213         int buty;
214         
215         /* header */
216         dy-= NODE_DY;
217         
218         /* little bit space in top */
219         if(node->outputs.first)
220                 dy-= NODE_DYS/2;
221
222         /* output sockets */
223         for(nsock= node->outputs.first; nsock; nsock= nsock->next) {
224                 if(!(nsock->flag & (SOCK_HIDDEN|SOCK_UNAVAIL))) {
225                         nsock->locx= node->locx + node->width;
226                         nsock->locy= dy - NODE_DYS;
227                         dy-= NODE_DY;
228                 }
229         }
230
231         node->prvr.xmin= node->locx + NODE_DYS;
232         node->prvr.xmax= node->locx + node->width- NODE_DYS;
233
234         /* preview rect? */
235         if(node->flag & NODE_PREVIEW) {
236                 /* only recalculate size when there's a preview actually, otherwise we use stored result */
237                 BLI_lock_thread(LOCK_PREVIEW);
238
239                 if(node->preview && node->preview->rect) {
240                         float aspect= 1.0f;
241                         
242                         if(node->preview && node->preview->xsize && node->preview->ysize) 
243                                 aspect= (float)node->preview->ysize/(float)node->preview->xsize;
244                         
245                         dy-= NODE_DYS/2;
246                         node->prvr.ymax= dy;
247                         
248                         if(aspect <= 1.0f)
249                                 node->prvr.ymin= dy - aspect*(node->width-NODE_DY);
250                         else {
251                                 float dx= (node->width - NODE_DYS) - (node->width- NODE_DYS)/aspect;    /* width correction of image */
252                                 
253                                 node->prvr.ymin= dy - (node->width-NODE_DY);
254                                 
255                                 node->prvr.xmin+= 0.5f*dx;
256                                 node->prvr.xmax-= 0.5f*dx;
257                         }
258
259                         dy= node->prvr.ymin - NODE_DYS/2;
260
261                         /* make sure that maximums are bigger or equal to minimums */
262                         if(node->prvr.xmax < node->prvr.xmin) SWAP(float, node->prvr.xmax, node->prvr.xmin);
263                         if(node->prvr.ymax < node->prvr.ymin) SWAP(float, node->prvr.ymax, node->prvr.ymin);
264                 }
265                 else {
266                         float oldh= node->prvr.ymax - node->prvr.ymin;
267                         if(oldh==0.0f)
268                                 oldh= 0.6f*node->width-NODE_DY;
269                         dy-= NODE_DYS/2;
270                         node->prvr.ymax= dy;
271                         node->prvr.ymin= dy - oldh;
272                         dy= node->prvr.ymin - NODE_DYS/2;
273                 }
274
275                 BLI_unlock_thread(LOCK_PREVIEW);
276         }
277
278         /* buttons rect? */
279         if((node->flag & NODE_OPTIONS) && node->typeinfo->uifunc) {
280                 dy-= NODE_DYS/2;
281
282                 /* set this for uifunc() that don't use layout engine yet */
283                 node->butr.xmin= 0;
284                 node->butr.xmax= node->width - 2*NODE_DYS;
285                 node->butr.ymin= 0;
286                 node->butr.ymax= 0;
287
288                 RNA_pointer_create(&ntree->id, &RNA_Node, node, &ptr);
289
290                 layout= uiBlockLayout(node->block, UI_LAYOUT_VERTICAL, UI_LAYOUT_PANEL,
291                         node->locx+NODE_DYS, dy, node->butr.xmax, 20, U.uistyles.first);
292
293                 node->typeinfo->uifunc(layout, (bContext *)C, &ptr);
294                 uiBlockEndAlign(node->block);
295                 uiBlockLayoutResolve(node->block, NULL, &buty);
296
297                 dy= buty - NODE_DYS/2;
298         }
299
300         /* input sockets */
301         for(nsock= node->inputs.first; nsock; nsock= nsock->next) {
302                 if(!(nsock->flag & (SOCK_HIDDEN|SOCK_UNAVAIL))) {
303                         nsock->locx= node->locx;
304                         nsock->locy= dy - NODE_DYS;
305                         dy-= NODE_DY;
306                 }
307         }
308         
309         /* little bit space in end */
310         if(node->inputs.first || (node->flag & (NODE_OPTIONS|NODE_PREVIEW))==0 )
311                 dy-= NODE_DYS/2;
312
313         node->totr.xmin= node->locx;
314         node->totr.xmax= node->locx + node->width;
315         node->totr.ymax= node->locy;
316         node->totr.ymin= dy;
317 }
318
319 /* based on settings in node, sets drawing rect info. each redraw! */
320 static void node_update_hidden(bNode *node)
321 {
322         bNodeSocket *nsock;
323         float rad, drad, hiddenrad= HIDDEN_RAD;
324         int totin=0, totout=0, tot;
325         
326         /* calculate minimal radius */
327         for(nsock= node->inputs.first; nsock; nsock= nsock->next)
328                 if(!(nsock->flag & (SOCK_HIDDEN|SOCK_UNAVAIL)))
329                         totin++;
330         for(nsock= node->outputs.first; nsock; nsock= nsock->next)
331                 if(!(nsock->flag & (SOCK_HIDDEN|SOCK_UNAVAIL)))
332                         totout++;
333         
334         tot= MAX2(totin, totout);
335         if(tot>4) {
336                 hiddenrad += 5.0f*(float)(tot-4);
337         }
338         
339         node->totr.xmin= node->locx;
340         node->totr.xmax= node->locx + 3*hiddenrad + node->miniwidth;
341         node->totr.ymax= node->locy + (hiddenrad - 0.5f*NODE_DY);
342         node->totr.ymin= node->totr.ymax - 2*hiddenrad;
343         
344         /* output sockets */
345         rad=drad= (float)M_PI/(1.0f + (float)totout);
346         
347         for(nsock= node->outputs.first; nsock; nsock= nsock->next) {
348                 if(!(nsock->flag & (SOCK_HIDDEN|SOCK_UNAVAIL))) {
349                         nsock->locx= node->totr.xmax - hiddenrad + (float)sin(rad)*hiddenrad;
350                         nsock->locy= node->totr.ymin + hiddenrad + (float)cos(rad)*hiddenrad;
351                         rad+= drad;
352                 }
353         }
354         
355         /* input sockets */
356         rad=drad= - (float)M_PI/(1.0f + (float)totin);
357         
358         for(nsock= node->inputs.first; nsock; nsock= nsock->next) {
359                 if(!(nsock->flag & (SOCK_HIDDEN|SOCK_UNAVAIL))) {
360                         nsock->locx= node->totr.xmin + hiddenrad + (float)sin(rad)*hiddenrad;
361                         nsock->locy= node->totr.ymin + hiddenrad + (float)cos(rad)*hiddenrad;
362                         rad+= drad;
363                 }
364         }
365 }
366
367 static int node_get_colorid(bNode *node)
368 {
369         if(node->typeinfo->nclass==NODE_CLASS_INPUT)
370                 return TH_NODE_IN_OUT;
371         if(node->typeinfo->nclass==NODE_CLASS_OUTPUT) {
372                 if(node->flag & NODE_DO_OUTPUT)
373                         return TH_NODE_IN_OUT;
374                 else
375                         return TH_NODE;
376         }
377         if(node->typeinfo->nclass==NODE_CLASS_CONVERTOR)
378                 return TH_NODE_CONVERTOR;
379         if(ELEM3(node->typeinfo->nclass, NODE_CLASS_OP_COLOR, NODE_CLASS_OP_VECTOR, NODE_CLASS_OP_FILTER))
380                 return TH_NODE_OPERATOR;
381         if(node->typeinfo->nclass==NODE_CLASS_GROUP)
382                 return TH_NODE_GROUP;
383         return TH_NODE;
384 }
385
386 /* based on settings in node, sets drawing rect info. each redraw! */
387 /* note: this assumes only 1 group at a time is drawn (linked data) */
388 /* in node->totr the entire boundbox for the group is stored */
389 static void node_update_group(const bContext *C, bNodeTree *ntree, bNode *gnode)
390 {
391         bNodeTree *ngroup= (bNodeTree *)gnode->id;
392         bNode *node;
393         bNodeSocket *sock, *gsock;
394         rctf *rect= &gnode->totr;
395         int counter;
396         int dy;
397         
398         rect->xmin = rect->xmax = gnode->locx;
399         rect->ymin = rect->ymax = gnode->locy;
400         
401         /* center them, is a bit of abuse of locx and locy though */
402         for(node= ngroup->nodes.first; node; node= node->next) {
403                 node->locx+= gnode->locx;
404                 node->locy+= gnode->locy;
405                 
406                 if(node->flag & NODE_HIDDEN)
407                         node_update_hidden(node);
408                 else
409                         node_update(C, ntree, node);
410                 node->locx-= gnode->locx;
411                 node->locy-= gnode->locy;
412         }
413         counter= 1;
414         for(node= ngroup->nodes.first; node; node= node->next) {
415                 if(counter) {
416                         *rect= node->totr;
417                         counter= 0;
418                 }
419                 else
420                         BLI_union_rctf(rect, &node->totr);
421         }
422         
423         /* add some room for links to group sockets */
424         rect->xmin -= 4*NODE_DY;
425         rect->xmax += 4*NODE_DY;
426         rect->ymin-= NODE_DY;
427         rect->ymax+= NODE_DY;
428         
429         /* input sockets */
430         dy = 0.5f*(rect->ymin+rect->ymax) + NODE_DY*(BLI_countlist(&gnode->inputs)-1);
431         for(gsock=ngroup->inputs.first, sock=gnode->inputs.first; gsock; gsock=gsock->next, sock=sock->next) {
432                 gsock->locx = rect->xmin;
433                 sock->locx = rect->xmin - NODE_GROUP_FRAME;
434                 sock->locy = gsock->locy = dy;
435                 
436                 /* prevent long socket lists from growing out of the group box */
437                 if (dy-3*NODE_DYS < rect->ymin)
438                         rect->ymin = dy-3*NODE_DYS;
439                 if (dy+3*NODE_DYS > rect->ymax)
440                         rect->ymax = dy+3*NODE_DYS;
441                 
442                 dy -= 2*NODE_DY;
443         }
444         
445         /* output sockets */
446         dy = 0.5f*(rect->ymin+rect->ymax) + NODE_DY*(BLI_countlist(&gnode->outputs)-1);
447         for(gsock=ngroup->outputs.first, sock=gnode->outputs.first; gsock; gsock=gsock->next, sock=sock->next) {
448                 gsock->locx = rect->xmax;
449                 sock->locx = rect->xmax + NODE_GROUP_FRAME;
450                 sock->locy = gsock->locy = dy - NODE_DYS;
451                 
452                 /* prevent long socket lists from growing out of the group box */
453                 if (dy-3*NODE_DYS < rect->ymin)
454                         rect->ymin = dy-3*NODE_DYS;
455                 if (dy+3*NODE_DYS > rect->ymax)
456                         rect->ymax = dy+3*NODE_DYS;
457                 
458                 dy -= 2*NODE_DY;
459         }
460 }
461
462 /* note: in cmp_util.c is similar code, for node_compo_pass_on() */
463 static void node_draw_mute_line(View2D *v2d, SpaceNode *snode, bNode *node)
464 {
465         bNodeSocket *valsock= NULL, *colsock= NULL, *vecsock= NULL;
466         bNodeSocket *sock;
467         bNodeLink link= {0};
468         int a;
469         
470         /* connect the first value buffer in with first value out */
471         /* connect the first RGBA buffer in with first RGBA out */
472         
473         /* test the inputs */
474         for(a=0, sock= node->inputs.first; sock; sock= sock->next, a++) {
475                 if(nodeCountSocketLinks(snode->edittree, sock)) {
476                         if(sock->type==SOCK_VALUE && valsock==NULL) valsock= sock;
477                         if(sock->type==SOCK_VECTOR && vecsock==NULL) vecsock= sock;
478                         if(sock->type==SOCK_RGBA && colsock==NULL) colsock= sock;
479                 }
480         }
481         
482         /* outputs, draw lines */
483         glEnable(GL_BLEND);
484         glEnable( GL_LINE_SMOOTH );
485         
486         if(valsock || colsock || vecsock) {
487                 for(a=0, sock= node->outputs.first; sock; sock= sock->next, a++) {
488                         if(nodeCountSocketLinks(snode->edittree, sock)) {
489                                 link.tosock= sock;
490                                 
491                                 if(sock->type==SOCK_VALUE && valsock) {
492                                         link.fromsock= valsock;
493                                         node_draw_link_bezier(v2d, snode, &link, TH_REDALERT, 0, TH_WIRE, 0, TH_WIRE);
494                                         valsock= NULL;
495                                 }
496                                 if(sock->type==SOCK_VECTOR && vecsock) {
497                                         link.fromsock= vecsock;
498                                         node_draw_link_bezier(v2d, snode, &link, TH_REDALERT, 0, TH_WIRE, 0, TH_WIRE);
499                                         vecsock= NULL;
500                                 }
501                                 if(sock->type==SOCK_RGBA && colsock) {
502                                         link.fromsock= colsock;
503                                         node_draw_link_bezier(v2d, snode, &link, TH_REDALERT, 0, TH_WIRE, 0, TH_WIRE);
504                                         colsock= NULL;
505                                 }
506                         }
507                 }
508         }
509         glDisable(GL_BLEND);
510         glDisable( GL_LINE_SMOOTH );
511 }
512
513 /* nice AA filled circle */
514 /* this might have some more generic use */
515 static void circle_draw(float x, float y, float size, int col[3])
516 {
517         /* 16 values of sin function */
518         static float si[16] = {
519                 0.00000000f, 0.39435585f,0.72479278f,0.93775213f,
520                 0.99871650f,0.89780453f,0.65137248f,0.29936312f,
521                 -0.10116832f,-0.48530196f,-0.79077573f,-0.96807711f,
522                 -0.98846832f,-0.84864425f,-0.57126821f,-0.20129852f
523         };
524         /* 16 values of cos function */
525         static float co[16] ={
526                 1.00000000f,0.91895781f,0.68896691f,0.34730525f,
527                 -0.05064916f,-0.44039415f,-0.75875812f,-0.95413925f,
528                 -0.99486932f,-0.87434661f,-0.61210598f,-0.25065253f,
529                 0.15142777f,0.52896401f,0.82076344f,0.97952994f,
530         };
531         int a;
532         
533         glColor3ub(col[0], col[1], col[2]);
534         
535         glBegin(GL_POLYGON);
536         for(a=0; a<16; a++)
537                 glVertex2f(x+size*si[a], y+size*co[a]);
538         glEnd();
539         
540         glColor4ub(0, 0, 0, 150);
541         glEnable(GL_BLEND);
542         glEnable( GL_LINE_SMOOTH );
543         glBegin(GL_LINE_LOOP);
544         for(a=0; a<16; a++)
545                 glVertex2f(x+size*si[a], y+size*co[a]);
546         glEnd();
547         glDisable( GL_LINE_SMOOTH );
548         glDisable(GL_BLEND);
549 }
550
551 static void socket_circle_draw(bNodeSocket *sock, float size)
552 {
553         int col[3];
554         
555         if(sock->type==-1) {
556                 col[0]= 0; col[1]= 0; col[2]= 0;
557         }
558         else if(sock->type==SOCK_VALUE) {
559                 col[0]= 160; col[1]= 160; col[2]= 160;
560         }
561         else if(sock->type==SOCK_VECTOR) {
562                 col[0]= 100; col[1]= 100; col[2]= 200;
563         }
564         else if(sock->type==SOCK_RGBA) {
565                 col[0]= 200; col[1]= 200; col[2]= 40;
566         }
567         else { 
568                 col[0]= 100; col[1]= 200; col[2]= 100;
569         }
570
571         circle_draw(sock->locx, sock->locy, size, col);
572 }
573
574 static void node_sync_cb(bContext *UNUSED(C), void *snode_v, void *node_v)
575 {
576         SpaceNode *snode= snode_v;
577         
578         if(snode->treetype==NTREE_SHADER) {
579                 nodeShaderSynchronizeID(node_v, 1);
580                 // allqueue(REDRAWBUTSSHADING, 0);
581         }
582 }
583
584 /* **************  Socket callbacks *********** */
585
586 /* not a callback */
587 static void node_draw_preview(bNodePreview *preview, rctf *prv)
588 {
589         float xscale= (prv->xmax-prv->xmin)/((float)preview->xsize);
590         float yscale= (prv->ymax-prv->ymin)/((float)preview->ysize);
591         float tile= (prv->xmax - prv->xmin) / 10.0f;
592         float x, y;
593         
594         /* draw checkerboard backdrop to show alpha */
595         glColor3ub(120, 120, 120);
596         glRectf(prv->xmin, prv->ymin, prv->xmax, prv->ymax);
597         glColor3ub(160, 160, 160);
598         
599         for(y=prv->ymin; y<prv->ymax; y+=tile*2) {
600                 for(x=prv->xmin; x<prv->xmax; x+=tile*2) {
601                         float tilex= tile, tiley= tile;
602
603                         if(x+tile > prv->xmax)
604                                 tilex= prv->xmax-x;
605                         if(y+tile > prv->ymax)
606                                 tiley= prv->ymax-y;
607
608                         glRectf(x, y, x + tilex, y + tiley);
609                 }
610         }
611         for(y=prv->ymin+tile; y<prv->ymax; y+=tile*2) {
612                 for(x=prv->xmin+tile; x<prv->xmax; x+=tile*2) {
613                         float tilex= tile, tiley= tile;
614
615                         if(x+tile > prv->xmax)
616                                 tilex= prv->xmax-x;
617                         if(y+tile > prv->ymax)
618                                 tiley= prv->ymax-y;
619
620                         glRectf(x, y, x + tilex, y + tiley);
621                 }
622         }
623         
624         glPixelZoom(xscale, yscale);
625
626         glEnable(GL_BLEND);
627         glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );    /* premul graphics */
628         
629         glColor4f(1.0, 1.0, 1.0, 1.0);
630         glaDrawPixelsTex(prv->xmin, prv->ymin, preview->xsize, preview->ysize, GL_UNSIGNED_BYTE, preview->rect);
631         
632         glDisable(GL_BLEND);
633         glPixelZoom(1.0f, 1.0f);
634
635         UI_ThemeColorShadeAlpha(TH_BACK, -15, +100);
636         fdrawbox(prv->xmin, prv->ymin, prv->xmax, prv->ymax);
637         
638 }
639
640 typedef struct SocketVectorMenuArgs {
641         PointerRNA ptr;
642         int x, y, width;
643         uiButHandleFunc cb;
644         void *arg1, *arg2;
645 } SocketVectorMenuArgs;
646
647 /* NOTE: this is a block-menu, needs 0 events, otherwise the menu closes */
648 static uiBlock *socket_vector_menu(bContext *C, ARegion *ar, void *args_v)
649 {
650         SocketVectorMenuArgs *args= (SocketVectorMenuArgs*)args_v;
651         uiBlock *block;
652         uiLayout *layout;
653         
654         block= uiBeginBlock(C, ar, "socket menu", UI_EMBOSS);
655         uiBlockSetFlag(block, UI_BLOCK_KEEP_OPEN);
656         
657         layout= uiLayoutColumn(uiBlockLayout(block, UI_LAYOUT_VERTICAL, UI_LAYOUT_PANEL, args->x, args->y+2, args->width, 20, U.uistyles.first), 0);
658         
659         uiItemR(layout, &args->ptr, "default_value", UI_ITEM_R_EXPAND, "", ICON_NONE);
660         
661         return block;
662 }
663
664 static void node_draw_socket_button(bNodeTree *ntree, bNodeSocket *sock, const char *name,
665                                                                         uiBlock *block, int x, int y, int width,
666                                                                         uiButHandleFunc cb, void *arg1, void *arg2)
667 {
668         uiBut *bt= NULL;
669         PointerRNA ptr;
670         int labelw;
671         SocketVectorMenuArgs *args;
672         
673         RNA_pointer_create(&ntree->id, &RNA_NodeSocket, sock, &ptr);
674         
675         switch (sock->type) {
676         case SOCK_VALUE:
677                 bt=uiDefButR(block, NUM, B_NODE_EXEC, name,
678                                          x, y+1, width, 17, 
679                                          &ptr, "default_value", 0, sock->ns.min, sock->ns.max, -1, -1, NULL);
680                 if (cb)
681                         uiButSetFunc(bt, cb, arg1, arg2);
682                 break;
683                 
684         case SOCK_VECTOR:
685                 args= MEM_callocN(sizeof(SocketVectorMenuArgs), "SocketVectorMenuArgs");
686         
687                 args->ptr = ptr;
688                 args->x = x;
689                 args->y = y;
690                 args->width = width;
691                 args->cb = cb;
692                 args->arg1 = arg1;
693                 args->arg2 = arg2;
694                 
695                 uiDefBlockButN(block, socket_vector_menu, args, name, 
696                                            x, y+1, width, 17, 
697                                            "");
698                 break;
699                 
700         case SOCK_RGBA:
701                 labelw= width - 40;
702                 
703                 bt=uiDefButR(block, COL, B_NODE_EXEC, "",
704                                          x, y+2, (labelw>0 ? 40 : width), 15, 
705                                          &ptr, "default_value", 0, sock->ns.min, sock->ns.max, -1, -1, NULL);
706                 if (cb)
707                         uiButSetFunc(bt, cb, arg1, arg2);
708                 
709                 if (name[0]!='\0' && labelw>0)
710                         uiDefBut(block, LABEL, 0, name, 
711                                          x + 40, y+2, labelw, 15, 
712                                          NULL, 0, 0, 0, 0, "");
713                 break;
714         }
715 }
716
717 static void node_draw_basis(const bContext *C, ARegion *ar, SpaceNode *snode, bNodeTree *ntree, bNode *node)
718 {
719         bNodeSocket *sock;
720         rctf *rct= &node->totr;
721         float iconofs;
722         int color_id= node_get_colorid(node);
723         char showname[128]; /* 128 used below */
724         View2D *v2d = &ar->v2d;
725         
726         /* hurmf... another candidate for callback, have to see how this works first */
727         if(node->id && node->block && snode->treetype==NTREE_SHADER)
728                 nodeShaderSynchronizeID(node, 0);
729         
730         /* skip if out of view */
731         if (node->totr.xmax < ar->v2d.cur.xmin || node->totr.xmin > ar->v2d.cur.xmax ||
732                         node->totr.ymax < ar->v2d.cur.ymin || node->totr.ymin > ar->v2d.cur.ymax) {
733                 
734                 uiEndBlock(C, node->block);
735                 node->block= NULL;
736                 return;
737         }
738         
739         uiSetRoundBox(15-4);
740         ui_dropshadow(rct, BASIS_RAD, snode->aspect, node->flag & SELECT);
741         
742         /* header */
743         if(color_id==TH_NODE)
744                 UI_ThemeColorShade(color_id, -20);
745         else
746                 UI_ThemeColor(color_id);
747         
748         if(node->flag & NODE_MUTED)
749            UI_ThemeColorBlend(color_id, TH_REDALERT, 0.5f);
750                 
751         uiSetRoundBox(3);
752         uiRoundBox(rct->xmin, rct->ymax-NODE_DY, rct->xmax, rct->ymax, BASIS_RAD);
753         
754         /* show/hide icons, note this sequence is copied in do_header_node() node_state.c */
755         iconofs= rct->xmax - 7.0f;
756         
757         if(node->typeinfo->flag & NODE_PREVIEW) {
758                 int icon_id;
759                 
760                 if(node->flag & (NODE_ACTIVE_ID|NODE_DO_OUTPUT))
761                         icon_id= ICON_MATERIAL;
762                 else
763                         icon_id= ICON_MATERIAL_DATA;
764                 iconofs-=15.0f;
765                 uiDefIconBut(node->block, LABEL, B_REDR, icon_id, iconofs, rct->ymax-NODE_DY,
766                                          UI_UNIT_X, UI_UNIT_Y, NULL, 0.0, 0.0, 1.0, 0.5, "");
767         }
768         if(node->type == NODE_GROUP) {
769                 
770                 iconofs-=15.0f;
771                 uiDefIconBut(node->block, LABEL, B_REDR, ICON_NODETREE, iconofs, rct->ymax-NODE_DY,
772                                          UI_UNIT_X, UI_UNIT_Y, NULL, 0.0, 0.0, 1.0, 0.5, "");
773         }
774         if(node->typeinfo->flag & NODE_OPTIONS) {
775                 iconofs-=15.0f;
776                 uiDefIconBut(node->block, LABEL, B_REDR, ICON_BUTS, iconofs, rct->ymax-NODE_DY,
777                                          UI_UNIT_X, UI_UNIT_Y, NULL, 0.0, 0.0, 1.0, 0.5, "");
778         }
779         {       /* always hide/reveal unused sockets */ 
780                 int shade;
781
782                 iconofs-=15.0f;
783                 // XXX re-enable
784                 /*if(node_has_hidden_sockets(node))
785                         shade= -40;
786                 else*/
787                         shade= -90;
788                 uiDefIconBut(node->block, LABEL, B_REDR, ICON_PLUS, iconofs, rct->ymax-NODE_DY,
789                                                   UI_UNIT_X, UI_UNIT_Y, NULL, 0.0, 0.0, 1.0, 0.5, "");
790         }
791         
792         /* title */
793         if(node->flag & SELECT) 
794                 UI_ThemeColor(TH_TEXT_HI);
795         else
796                 UI_ThemeColorBlendShade(TH_TEXT, color_id, 0.4f, 10);
797         
798         /* open/close entirely? */
799         UI_DrawTriIcon(rct->xmin+10.0f, rct->ymax-NODE_DY/2.0f, 'v');
800         
801         /* this isn't doing anything for the label, so commenting out
802         if(node->flag & SELECT) 
803                 UI_ThemeColor(TH_TEXT_HI);
804         else
805                 UI_ThemeColor(TH_TEXT); */
806         
807         if (node->typeinfo->labelfunc)
808                 BLI_strncpy(showname, node->typeinfo->labelfunc(node), sizeof(showname));
809         else
810                 BLI_strncpy(showname, node->typeinfo->name, sizeof(showname));
811
812         //if(node->flag & NODE_MUTED)
813         //      sprintf(showname, "[%s]", showname);
814         
815         uiDefBut(node->block, LABEL, 0, showname, (short)(rct->xmin+15), (short)(rct->ymax-NODE_DY), 
816                          (int)(iconofs - rct->xmin-18.0f), NODE_DY,  NULL, 0, 0, 0, 0, "");
817
818         /* body */
819         UI_ThemeColor4(TH_NODE);
820         glEnable(GL_BLEND);
821         uiSetRoundBox(8);
822         uiRoundBox(rct->xmin, rct->ymin, rct->xmax, rct->ymax-NODE_DY, BASIS_RAD);
823         glDisable(GL_BLEND);
824
825         /* scaling indicator */
826         node_scaling_widget(TH_NODE, snode->aspect, rct->xmax-BASIS_RAD*snode->aspect, rct->ymin, rct->xmax, rct->ymin+BASIS_RAD*snode->aspect);
827
828         /* outline active and selected emphasis */
829         if( node->flag & (NODE_ACTIVE|SELECT) ) {
830                 glEnable(GL_BLEND);
831                 glEnable( GL_LINE_SMOOTH );
832                         /* using different shades of TH_TEXT_HI for the empasis, like triangle */
833                         if( node->flag & NODE_ACTIVE ) 
834                                 UI_ThemeColorShadeAlpha(TH_TEXT_HI, 0, -40);
835                         else
836                                 UI_ThemeColorShadeAlpha(TH_TEXT_HI, -20, -120);
837                         uiSetRoundBox(15-4); // round all corners except lower right
838                         uiDrawBox(GL_LINE_LOOP, rct->xmin, rct->ymin, rct->xmax, rct->ymax, BASIS_RAD);
839                         
840                 glDisable( GL_LINE_SMOOTH );
841                 glDisable(GL_BLEND);
842         }
843         
844         /* disable lines */
845         if(node->flag & NODE_MUTED)
846                 node_draw_mute_line(v2d, snode, node);
847
848         
849         /* socket inputs, buttons */
850         for(sock= node->inputs.first; sock; sock= sock->next) {
851                 if(!(sock->flag & (SOCK_HIDDEN|SOCK_UNAVAIL))) {
852                         socket_circle_draw(sock, NODE_SOCKSIZE);
853                         
854                         if(node->block && sock->link==NULL) {
855                                 node_draw_socket_button(ntree, sock, sock->name, node->block, sock->locx+NODE_DYS, sock->locy-NODE_DYS, node->width-NODE_DY, node_sync_cb, snode, node);
856                         }
857                         else {
858                                 uiDefBut(node->block, LABEL, 0, sock->name, (short)(sock->locx+7), (short)(sock->locy-9.0f), 
859                                                  (short)(node->width-NODE_DY), NODE_DY,  NULL, 0, 0, 0, 0, "");
860                         }
861                 }
862         }
863         
864         /* socket outputs */
865         for(sock= node->outputs.first; sock; sock= sock->next) {
866                 if(!(sock->flag & (SOCK_HIDDEN|SOCK_UNAVAIL))) {
867                         float slen;
868                         int ofs= 0;
869                         
870                         socket_circle_draw(sock, NODE_SOCKSIZE);
871                         
872                         UI_ThemeColor(TH_TEXT);
873                         slen= snode->aspect*UI_GetStringWidth(sock->name);
874                         while(slen > node->width) {
875                                 ofs++;
876                                 slen= snode->aspect*UI_GetStringWidth(sock->name+ofs);
877                         }
878                         
879                         uiDefBut(node->block, LABEL, 0, sock->name+ofs, (short)(sock->locx-15.0f-slen), (short)(sock->locy-9.0f), 
880                                          (short)(node->width-NODE_DY), NODE_DY,  NULL, 0, 0, 0, 0, "");
881                 }
882         }
883         
884         /* preview */
885         if(node->flag & NODE_PREVIEW) {
886                 BLI_lock_thread(LOCK_PREVIEW);
887                 if(node->preview && node->preview->rect && !BLI_rctf_is_empty(&node->prvr))
888                         node_draw_preview(node->preview, &node->prvr);
889                 BLI_unlock_thread(LOCK_PREVIEW);
890         }
891         
892         UI_ThemeClearColor(color_id);
893                 
894         uiEndBlock(C, node->block);
895         uiDrawBlock(C, node->block);
896         node->block= NULL;
897 }
898
899 static void node_draw_hidden(const bContext *C, ARegion *ar, SpaceNode *snode, bNode *node)
900 {
901         bNodeSocket *sock;
902         rctf *rct= &node->totr;
903         float dx, centy= 0.5f*(rct->ymax+rct->ymin);
904         float hiddenrad= 0.5f*(rct->ymax-rct->ymin);
905         int color_id= node_get_colorid(node);
906         char showname[128];     /* 128 is used below */
907         
908         /* shadow */
909         uiSetRoundBox(15);
910         ui_dropshadow(rct, hiddenrad, snode->aspect, node->flag & SELECT);
911
912         /* body */
913         UI_ThemeColor(color_id);
914         if(node->flag & NODE_MUTED)
915            UI_ThemeColorBlend(color_id, TH_REDALERT, 0.5f);     
916         uiRoundBox(rct->xmin, rct->ymin, rct->xmax, rct->ymax, hiddenrad);
917         
918         /* outline active and selected emphasis */
919         if( node->flag & (NODE_ACTIVE|SELECT) ) {
920                 glEnable(GL_BLEND);
921                 glEnable( GL_LINE_SMOOTH );
922                         /* using different shades of TH_TEXT_HI for the empasis, like triangle */
923                         if( node->flag & NODE_ACTIVE ) 
924                                 UI_ThemeColorShadeAlpha(TH_TEXT_HI, 0, -40);
925                         else
926                                 UI_ThemeColorShadeAlpha(TH_TEXT_HI, -20, -120);
927                         uiDrawBox(GL_LINE_LOOP, rct->xmin, rct->ymin, rct->xmax, rct->ymax, hiddenrad);
928                 glDisable( GL_LINE_SMOOTH );
929                 glDisable(GL_BLEND);
930         }
931         
932         /* title */
933         if(node->flag & SELECT) 
934                 UI_ThemeColor(TH_TEXT_HI);
935         else
936                 UI_ThemeColorBlendShade(TH_TEXT, color_id, 0.4f, 10);
937         
938         /* open entirely icon */
939         UI_DrawTriIcon(rct->xmin+10.0f, centy, 'h');    
940         
941         /* disable lines */
942         if(node->flag & NODE_MUTED)
943                 node_draw_mute_line(&ar->v2d, snode, node);     
944         
945         if(node->flag & SELECT) 
946                 UI_ThemeColor(TH_TEXT_HI);
947         else
948                 UI_ThemeColor(TH_TEXT);
949         
950         if(node->miniwidth>0.0f) {
951                 if (node->typeinfo->labelfunc)
952                         BLI_strncpy(showname, node->typeinfo->labelfunc(node), sizeof(showname));
953                 else
954                         BLI_strncpy(showname, node->typeinfo->name, sizeof(showname));
955                 
956                 //if(node->flag & NODE_MUTED)
957                 //      sprintf(showname, "[%s]", showname);
958
959                 uiDefBut(node->block, LABEL, 0, showname, (short)(rct->xmin+15), (short)(centy-10), 
960                                  (int)(rct->xmax - rct->xmin-18.0f -12.0f), NODE_DY,  NULL, 0, 0, 0, 0, "");
961         }       
962
963         /* scale widget thing */
964         UI_ThemeColorShade(color_id, -10);      
965         dx= 10.0f;
966         fdrawline(rct->xmax-dx, centy-4.0f, rct->xmax-dx, centy+4.0f);
967         fdrawline(rct->xmax-dx-3.0f*snode->aspect, centy-4.0f, rct->xmax-dx-3.0f*snode->aspect, centy+4.0f);
968         
969         UI_ThemeColorShade(color_id, +30);
970         dx-= snode->aspect;
971         fdrawline(rct->xmax-dx, centy-4.0f, rct->xmax-dx, centy+4.0f);
972         fdrawline(rct->xmax-dx-3.0f*snode->aspect, centy-4.0f, rct->xmax-dx-3.0f*snode->aspect, centy+4.0f);
973         
974         /* sockets */
975         for(sock= node->inputs.first; sock; sock= sock->next) {
976                 if(!(sock->flag & (SOCK_HIDDEN|SOCK_UNAVAIL)))
977                         socket_circle_draw(sock, NODE_SOCKSIZE);
978         }
979         
980         for(sock= node->outputs.first; sock; sock= sock->next) {
981                 if(!(sock->flag & (SOCK_HIDDEN|SOCK_UNAVAIL)))
982                         socket_circle_draw(sock, NODE_SOCKSIZE);
983         }
984         
985         uiEndBlock(C, node->block);
986         uiDrawBlock(C, node->block);
987         node->block= NULL;
988 }
989
990 static void node_draw_nodetree(const bContext *C, ARegion *ar, SpaceNode *snode, bNodeTree *ntree)
991 {
992         bNode *node;
993         bNodeLink *link;
994         int a;
995         
996         if(ntree==NULL) return;         /* groups... */
997         
998         /* node lines */
999         glEnable(GL_BLEND);
1000         glEnable(GL_LINE_SMOOTH);
1001         for(link= ntree->links.first; link; link= link->next)
1002                 node_draw_link(&ar->v2d, snode, link);
1003         glDisable(GL_LINE_SMOOTH);
1004         glDisable(GL_BLEND);
1005         
1006         /* not selected first */
1007         for(a=0, node= ntree->nodes.first; node; node= node->next, a++) {
1008                 node->nr= a;            /* index of node in list, used for exec event code */
1009                 if(!(node->flag & SELECT)) {
1010                         if(node->flag & NODE_GROUP_EDIT);
1011                         else if(node->flag & NODE_HIDDEN)
1012                                 node_draw_hidden(C, ar, snode, node);
1013                         else
1014                                 node_draw_basis(C, ar, snode, ntree, node);
1015                 }
1016         }
1017         
1018         /* selected */
1019         for(node= ntree->nodes.first; node; node= node->next) {
1020                 if(node->flag & SELECT) {
1021                         if(node->flag & NODE_GROUP_EDIT);
1022                         else if(node->flag & NODE_HIDDEN)
1023                                 node_draw_hidden(C, ar, snode, node);
1024                         else
1025                                 node_draw_basis(C, ar, snode, ntree, node);
1026                 }
1027         }       
1028 }
1029
1030 static void group_verify_cb(bContext *UNUSED(C), void *UNUSED(snode_v), void *ngroup_v)
1031 {
1032         bNodeTree *ngroup= (bNodeTree*)ngroup_v;
1033         
1034         nodeGroupVerify(ngroup);
1035 }
1036
1037 /* groups are, on creation, centered around 0,0 */
1038 static void node_draw_group(const bContext *C, ARegion *ar, SpaceNode *snode, bNodeTree *ntree, bNode *gnode)
1039 {
1040         bNodeTree *ngroup= (bNodeTree *)gnode->id;
1041         bNodeSocket *sock;
1042         rctf rect= gnode->totr;
1043         int index;
1044         uiLayout *layout;
1045         PointerRNA ptr;
1046         uiBut *bt;
1047         
1048         /* backdrop header */
1049         glEnable(GL_BLEND);
1050         uiSetRoundBox(3);
1051         UI_ThemeColorShadeAlpha(TH_NODE_GROUP, 0, -70);
1052         uiDrawBox(GL_POLYGON, rect.xmin-NODE_GROUP_FRAME, rect.ymax, rect.xmax+NODE_GROUP_FRAME, rect.ymax+26, BASIS_RAD);
1053         
1054         /* backdrop body */
1055         UI_ThemeColorShadeAlpha(TH_BACK, -8, -70);
1056         uiSetRoundBox(0);
1057         uiDrawBox(GL_POLYGON, rect.xmin, rect.ymin, rect.xmax, rect.ymax, BASIS_RAD);
1058
1059         /* input column */
1060         UI_ThemeColorShadeAlpha(TH_BACK, 10, -50);
1061         uiSetRoundBox(8);
1062         uiDrawBox(GL_POLYGON, rect.xmin-NODE_GROUP_FRAME, rect.ymin, rect.xmin, rect.ymax, BASIS_RAD);
1063
1064         /* output column */
1065         UI_ThemeColorShadeAlpha(TH_BACK, 10, -50);
1066         uiSetRoundBox(4);
1067         uiDrawBox(GL_POLYGON, rect.xmax, rect.ymin, rect.xmax+NODE_GROUP_FRAME, rect.ymax, BASIS_RAD);
1068
1069         /* input column separator */
1070         glColor4ub(200, 200, 200, 140);
1071         glBegin(GL_LINES);
1072         glVertex2f(rect.xmin, rect.ymin);
1073         glVertex2f(rect.xmin, rect.ymax);
1074         glEnd();
1075
1076         /* output column separator */
1077         glColor4ub(200, 200, 200, 140);
1078         glBegin(GL_LINES);
1079         glVertex2f(rect.xmax, rect.ymin);
1080         glVertex2f(rect.xmax, rect.ymax);
1081         glEnd();
1082
1083         /* group node outline */
1084         uiSetRoundBox(15);
1085         glColor4ub(200, 200, 200, 140);
1086         glEnable( GL_LINE_SMOOTH );
1087         uiDrawBox(GL_LINE_LOOP, rect.xmin-NODE_GROUP_FRAME, rect.ymin, rect.xmax+NODE_GROUP_FRAME, rect.ymax+26, BASIS_RAD);
1088         glDisable( GL_LINE_SMOOTH );
1089         glDisable(GL_BLEND);
1090         
1091         /* backdrop title */
1092         UI_ThemeColor(TH_TEXT_HI);
1093
1094         layout = uiBlockLayout(gnode->block, UI_LAYOUT_VERTICAL, UI_LAYOUT_PANEL, (short)(rect.xmin+15), (short)(rect.ymax+23),
1095                                                    MIN2((int)(rect.xmax - rect.xmin-18.0f), 140), 20, U.uistyles.first);
1096         RNA_pointer_create(&ntree->id, &RNA_Node, gnode, &ptr);
1097         uiTemplateIDBrowse(layout, (bContext*)C, &ptr, "node_tree", NULL, NULL, NULL);
1098         uiBlockLayoutResolve(gnode->block, NULL, NULL);
1099
1100         /* draw the internal tree nodes and links */
1101         node_draw_nodetree(C, ar, snode, ngroup);
1102
1103         /* group sockets */
1104         for(sock=ngroup->inputs.first, index=0; sock; sock=sock->next, ++index) {
1105                 socket_circle_draw(sock, NODE_SOCKSIZE);
1106                 /* small hack to use socket_circle_draw function with offset */
1107                 sock->locx -= NODE_GROUP_FRAME;
1108                 socket_circle_draw(sock, NODE_SOCKSIZE);
1109                 sock->locx += NODE_GROUP_FRAME;
1110
1111                 bt = uiDefBut(gnode->block, TEX, 0, "", 
1112                                           sock->locx-114, sock->locy+1, 72, NODE_DY,
1113                                           sock->name, 0, 31, 0, 0, "");
1114                 uiButSetFunc(bt, group_verify_cb, snode, ngroup);
1115                 
1116                 node_draw_socket_button(ngroup, sock, "", gnode->block,
1117                                                                 sock->locx-114, sock->locy-NODE_DY, 72,
1118                                                                 NULL, NULL, NULL);
1119
1120                 uiBlockSetDirection(gnode->block, UI_TOP);
1121                 uiBlockBeginAlign(gnode->block);
1122                 bt = uiDefIconButO(gnode->block, BUT, "NODE_OT_group_socket_move_up", 0, ICON_TRIA_UP,
1123                                                    sock->locx-40, sock->locy, 16, 16, "");
1124                 if (!sock->prev)
1125                         uiButSetFlag(bt, UI_BUT_DISABLED);
1126                 RNA_int_set(uiButGetOperatorPtrRNA(bt), "index", index);
1127                 RNA_enum_set(uiButGetOperatorPtrRNA(bt), "in_out", SOCK_IN);
1128                 bt = uiDefIconButO(gnode->block, BUT, "NODE_OT_group_socket_move_down", 0, ICON_TRIA_DOWN,
1129                                                    sock->locx-40, sock->locy-16, 16, 16, "");
1130                 if (!sock->next)
1131                         uiButSetFlag(bt, UI_BUT_DISABLED);
1132                 RNA_int_set(uiButGetOperatorPtrRNA(bt), "index", index);
1133                 RNA_enum_set(uiButGetOperatorPtrRNA(bt), "in_out", SOCK_IN);
1134                 uiBlockEndAlign(gnode->block);
1135                 uiBlockSetDirection(gnode->block, 0);
1136                 
1137                 uiBlockSetEmboss(gnode->block, UI_EMBOSSN);
1138                 bt = uiDefIconButO(gnode->block, BUT, "NODE_OT_group_socket_remove", 0, ICON_X,
1139                                                    sock->locx-22, sock->locy-8, 16, 16, "");
1140                 RNA_int_set(uiButGetOperatorPtrRNA(bt), "index", index);
1141                 RNA_enum_set(uiButGetOperatorPtrRNA(bt), "in_out", SOCK_IN);
1142                 uiBlockSetEmboss(gnode->block, UI_EMBOSS);
1143         }
1144         
1145         for(sock=ngroup->outputs.first, index=0; sock; sock=sock->next, ++index) {
1146                 socket_circle_draw(sock, NODE_SOCKSIZE);
1147                 /* small hack to use socket_circle_draw function with offset */
1148                 sock->locx += NODE_GROUP_FRAME;
1149                 socket_circle_draw(sock, NODE_SOCKSIZE);
1150                 sock->locx -= NODE_GROUP_FRAME;
1151                 
1152                 uiBlockSetEmboss(gnode->block, UI_EMBOSSN);
1153                 bt = uiDefIconButO(gnode->block, BUT, "NODE_OT_group_socket_remove", 0, ICON_X,
1154                                                    sock->locx+6, sock->locy-8, 16, 16, "");
1155                 RNA_int_set(uiButGetOperatorPtrRNA(bt), "index", index);
1156                 RNA_enum_set(uiButGetOperatorPtrRNA(bt), "in_out", SOCK_OUT);
1157                 uiBlockSetEmboss(gnode->block, UI_EMBOSS);
1158                 
1159                 uiBlockSetDirection(gnode->block, UI_TOP);
1160                 uiBlockBeginAlign(gnode->block);
1161                 bt = uiDefIconButO(gnode->block, BUT, "NODE_OT_group_socket_move_up", 0, ICON_TRIA_UP,
1162                                                    sock->locx+24, sock->locy, 16, 16, "");
1163                 if (!sock->prev)
1164                         uiButSetFlag(bt, UI_BUT_DISABLED);
1165                 RNA_int_set(uiButGetOperatorPtrRNA(bt), "index", index);
1166                 RNA_enum_set(uiButGetOperatorPtrRNA(bt), "in_out", SOCK_OUT);
1167                 bt = uiDefIconButO(gnode->block, BUT, "NODE_OT_group_socket_move_down", 0, ICON_TRIA_DOWN,
1168                                                    sock->locx+24, sock->locy-16, 16, 16, "");
1169                 if (!sock->next)
1170                         uiButSetFlag(bt, UI_BUT_DISABLED);
1171                 RNA_int_set(uiButGetOperatorPtrRNA(bt), "index", index);
1172                 RNA_enum_set(uiButGetOperatorPtrRNA(bt), "in_out", SOCK_OUT);
1173                 uiBlockEndAlign(gnode->block);
1174                 uiBlockSetDirection(gnode->block, 0);
1175                 
1176                 if (sock->link) {
1177                         bt = uiDefBut(gnode->block, TEX, 0, "", 
1178                                                   sock->locx+42, sock->locy-NODE_DYS+1, 72, NODE_DY,
1179                                                   sock->name, 0, 31, 0, 0, "");
1180                         uiButSetFunc(bt, group_verify_cb, snode, ngroup);
1181                 }
1182                 else {
1183                         bt = uiDefBut(gnode->block, TEX, 0, "", 
1184                                                   sock->locx+42, sock->locy+1, 72, NODE_DY,
1185                                                   sock->name, 0, 31, 0, 0, "");
1186                         uiButSetFunc(bt, group_verify_cb, snode, ngroup);
1187                         
1188                         node_draw_socket_button(ngroup, sock, "", gnode->block, sock->locx+42, sock->locy-NODE_DY, 72, NULL, NULL, NULL);
1189                 }
1190         }
1191         
1192         uiEndBlock(C, gnode->block);
1193         uiDrawBlock(C, gnode->block);
1194         gnode->block= NULL;
1195 }
1196
1197 void drawnodespace(const bContext *C, ARegion *ar, View2D *v2d)
1198 {
1199         View2DScrollers *scrollers;
1200         SpaceNode *snode= CTX_wm_space_node(C);
1201         Scene *scene= CTX_data_scene(C);
1202         int color_manage = scene->r.color_mgt_flag & R_COLOR_MANAGEMENT;
1203         
1204         UI_ThemeClearColor(TH_BACK);
1205         glClear(GL_COLOR_BUFFER_BIT);
1206
1207         UI_view2d_view_ortho(v2d);
1208         
1209         //uiFreeBlocksWin(&sa->uiblocks, sa->win);
1210
1211         /* only set once */
1212         glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
1213         glEnable(GL_MAP1_VERTEX_3);
1214
1215         /* aspect+font, set each time */
1216         snode->aspect= (v2d->cur.xmax - v2d->cur.xmin)/((float)ar->winx);
1217         // XXX snode->curfont= uiSetCurFont_ext(snode->aspect);
1218
1219         UI_view2d_constant_grid_draw(v2d);
1220         /* backdrop */
1221         draw_nodespace_back_pix(ar, snode, color_manage);
1222         
1223         /* nodes */
1224         snode_set_context(snode, CTX_data_scene(C));
1225         
1226         if(snode->nodetree) {
1227                 bNode *node;
1228                 
1229                 /* init ui blocks for opened node group trees first 
1230                  * so they're in the correct depth stack order */
1231                 for(node= snode->nodetree->nodes.first; node; node= node->next) {
1232                         if(node->flag & NODE_GROUP_EDIT)
1233                                 node_uiblocks_init(C, (bNodeTree *)node->id);
1234                 }
1235
1236                 node_uiblocks_init(C, snode->nodetree);
1237                 
1238                 
1239                 /* for now, we set drawing coordinates on each redraw */
1240                 for(node= snode->nodetree->nodes.first; node; node= node->next) {
1241                         if(node->flag & NODE_GROUP_EDIT)
1242                                 node_update_group(C, snode->nodetree, node);
1243                         else if(node->flag & NODE_HIDDEN)
1244                                 node_update_hidden(node);
1245                         else
1246                                 node_update(C, snode->nodetree, node);
1247                 }
1248
1249                 node_draw_nodetree(C, ar, snode, snode->nodetree);
1250                         
1251                 /* active group */
1252                 for(node= snode->nodetree->nodes.first; node; node= node->next) {
1253                         if(node->flag & NODE_GROUP_EDIT)
1254                                 node_draw_group(C, ar, snode, snode->nodetree, node);
1255                 }
1256         }
1257         
1258         /* draw grease-pencil ('canvas' strokes) */
1259         if (/*(snode->flag & SNODE_DISPGP) &&*/ (snode->nodetree))
1260                 draw_gpencil_view2d((bContext*)C, 1);
1261         
1262         /* reset view matrix */
1263         UI_view2d_view_restore(C);
1264         
1265         /* draw grease-pencil (screen strokes, and also paintbuffer) */
1266         if (/*(snode->flag & SNODE_DISPGP) && */(snode->nodetree))
1267                 draw_gpencil_view2d((bContext*)C, 0);
1268         
1269         /* scrollers */
1270         scrollers= UI_view2d_scrollers_calc(C, v2d, 10, V2D_GRID_CLAMP, V2D_ARG_DUMMY, V2D_ARG_DUMMY);
1271         UI_view2d_scrollers_draw(C, v2d, scrollers);
1272         UI_view2d_scrollers_free(scrollers);
1273 }