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