code cleanup
[blender-staging.git] / source / blender / editors / space_node / node_draw.c
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version. 
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * The Original Code is Copyright (C) 2008 Blender Foundation.
19  * All rights reserved.
20  *
21  * The Original Code is: all of this file.
22  * Contributor(s): Nathan Letwory
23  *
24  * ***** END GPL LICENSE BLOCK *****
25  */
26
27 /** \file blender/editors/space_node/node_draw.c
28  *  \ingroup spnode
29  *  \brief higher level node drawing for the node editor.
30  */
31
32 #include "DNA_lamp_types.h"
33 #include "DNA_node_types.h"
34 #include "DNA_material_types.h"
35 #include "DNA_object_types.h"
36 #include "DNA_screen_types.h"
37 #include "DNA_space_types.h"
38 #include "DNA_texture_types.h"
39 #include "DNA_world_types.h"
40
41 #include "BLI_math.h"
42 #include "BLI_blenlib.h"
43
44 #include "BLF_translation.h"
45
46 #include "BKE_context.h"
47 #include "BKE_depsgraph.h"
48 #include "BKE_main.h"
49 #include "BKE_node.h"
50
51 #include "BLF_api.h"
52
53 #include "BIF_gl.h"
54 #include "BIF_glutil.h"
55
56 #include "WM_api.h"
57 #include "WM_types.h"
58
59 #include "ED_node.h"
60 #include "ED_gpencil.h"
61 #include "ED_screen.h"
62 #include "ED_space_api.h"
63
64 #include "UI_resources.h"
65 #include "UI_view2d.h"
66
67 #include "RNA_access.h"
68
69 #include "node_intern.h"  /* own include */
70 #include "COM_compositor.h"
71
72 /* XXX interface.h */
73 extern void ui_dropshadow(const rctf *rct, float radius, float aspect, float alpha, int select);
74
75 void ED_node_tree_update(const bContext *C)
76 {
77         SpaceNode *snode = CTX_wm_space_node(C);
78         snode_set_context(C);
79         
80         if (snode->nodetree && snode->nodetree->id.us == 0)
81                 snode->nodetree->id.us = 1;
82 }
83
84 /* id is supposed to contain a node tree */
85 static bNodeTree *node_tree_from_ID(ID *id)
86 {
87         if (id) {
88                 short idtype = GS(id->name);
89         
90                 switch (idtype) {
91                 case ID_NT:
92                         return (bNodeTree *)id;
93                 case ID_MA:
94                         return ((Material *)id)->nodetree;
95                 case ID_LA:
96                         return ((Lamp *)id)->nodetree;
97                 case ID_WO:
98                         return ((World *)id)->nodetree;
99                 case ID_SCE:
100                         return ((Scene *)id)->nodetree;
101                 case ID_TE:
102                         return ((Tex *)id)->nodetree;
103                 }
104         }
105         
106         return NULL;
107 }
108
109 void ED_node_tag_update_id(ID *id)
110 {
111         bNodeTree *ntree = node_tree_from_ID(id);
112         if (id == NULL)
113                 return;
114         
115         if (ntree->type == NTREE_SHADER) {
116                 DAG_id_tag_update(id, 0);
117                 
118                 if (GS(id->name) == ID_MA)
119                         WM_main_add_notifier(NC_MATERIAL | ND_SHADING_DRAW, id);
120                 else if (GS(id->name) == ID_LA)
121                         WM_main_add_notifier(NC_LAMP | ND_LIGHTING_DRAW, id);
122                 else if (GS(id->name) == ID_WO)
123                         WM_main_add_notifier(NC_WORLD | ND_WORLD_DRAW, id);
124         }
125         else if (ntree->type == NTREE_COMPOSIT) {
126                 WM_main_add_notifier(NC_SCENE | ND_NODES, id);
127         }
128         else if (ntree->type == NTREE_TEXTURE) {
129                 DAG_id_tag_update(id, 0);
130                 WM_main_add_notifier(NC_TEXTURE | ND_NODES, id);
131         }
132 }
133
134 static int has_nodetree(bNodeTree *ntree, bNodeTree *lookup)
135 {
136         bNode *node;
137         
138         if (ntree == lookup)
139                 return 1;
140         
141         for (node = ntree->nodes.first; node; node = node->next)
142                 if (node->type == NODE_GROUP && node->id)
143                         if (has_nodetree((bNodeTree *)node->id, lookup))
144                                 return 1;
145         
146         return 0;
147 }
148
149 void ED_node_tag_update_nodetree(Main *bmain, bNodeTree *ntree)
150 {
151         if (!ntreeIsValid(ntree))
152                 return;
153         
154         /* look through all datablocks, to support groups */
155         FOREACH_NODETREE(bmain, tntree, id) {
156                 /* check if nodetree uses the group */
157                 if (has_nodetree(tntree, ntree))
158                         ED_node_tag_update_id(id);
159         } FOREACH_NODETREE_END
160         
161         if (ntree->type == NTREE_TEXTURE)
162                 ntreeTexCheckCyclics(ntree);
163 }
164
165 static int compare_nodes(bNode *a, bNode *b)
166 {
167         bNode *parent;
168         /* These tell if either the node or any of the parent nodes is selected.
169          * A selected parent means an unselected node is also in foreground!
170          */
171         int a_select = (a->flag & NODE_SELECT), b_select = (b->flag & NODE_SELECT);
172         int a_active = (a->flag & NODE_ACTIVE), b_active = (b->flag & NODE_ACTIVE);
173         
174         /* if one is an ancestor of the other */
175         /* XXX there might be a better sorting algorithm for stable topological sort, this is O(n^2) worst case */
176         for (parent = a->parent; parent; parent = parent->parent) {
177                 /* if b is an ancestor, it is always behind a */
178                 if (parent == b)
179                         return 1;
180                 /* any selected ancestor moves the node forward */
181                 if (parent->flag & NODE_ACTIVE)
182                         a_active = 1;
183                 if (parent->flag & NODE_SELECT)
184                         a_select = 1;
185         }
186         for (parent = b->parent; parent; parent = parent->parent) {
187                 /* if a is an ancestor, it is always behind b */
188                 if (parent == a)
189                         return 0;
190                 /* any selected ancestor moves the node forward */
191                 if (parent->flag & NODE_ACTIVE)
192                         b_active = 1;
193                 if (parent->flag & NODE_SELECT)
194                         b_select = 1;
195         }
196
197         /* if one of the nodes is in the background and the other not */
198         if ((a->flag & NODE_BACKGROUND) && !(b->flag & NODE_BACKGROUND))
199                 return 0;
200         else if (!(a->flag & NODE_BACKGROUND) && (b->flag & NODE_BACKGROUND))
201                 return 1;
202         
203         /* if one has a higher selection state (active > selected > nothing) */
204         if (!b_active && a_active)
205                 return 1;
206         else if (!b_select && (a_active || a_select))
207                 return 1;
208         
209         return 0;
210 }
211
212 /* Sorts nodes by selection: unselected nodes first, then selected,
213  * then the active node at the very end. Relative order is kept intact!
214  */
215 void ED_node_sort(bNodeTree *ntree)
216 {
217         /* merge sort is the algorithm of choice here */
218         bNode *first_a, *first_b, *node_a, *node_b, *tmp;
219         int totnodes = BLI_countlist(&ntree->nodes);
220         int k, a, b;
221         
222         k = 1;
223         while (k < totnodes) {
224                 first_a = first_b = ntree->nodes.first;
225                 
226                 do {
227                         /* setup first_b pointer */
228                         for (b = 0; b < k && first_b; ++b) {
229                                 first_b = first_b->next;
230                         }
231                         /* all batches merged? */
232                         if (first_b == NULL)
233                                 break;
234                         
235                         /* merge batches */
236                         node_a = first_a;
237                         node_b = first_b;
238                         a = b = 0;
239                         while (a < k && b < k && node_b) {
240                                 if (compare_nodes(node_a, node_b) == 0) {
241                                         node_a = node_a->next;
242                                         a++;
243                                 }
244                                 else {
245                                         tmp = node_b;
246                                         node_b = node_b->next;
247                                         b++;
248                                         BLI_remlink(&ntree->nodes, tmp);
249                                         BLI_insertlinkbefore(&ntree->nodes, node_a, tmp);
250                                 }
251                         }
252
253                         /* setup first pointers for next batch */
254                         first_b = node_b;
255                         for (; b < k; ++b) {
256                                 /* all nodes sorted? */
257                                 if (first_b == NULL)
258                                         break;
259                                 first_b = first_b->next;
260                         }
261                         first_a = first_b;
262                 } while (first_b);
263                 
264                 k = k << 1;
265         }
266 }
267
268
269 static void do_node_internal_buttons(bContext *C, void *UNUSED(node_v), int event)
270 {
271         if (event == B_NODE_EXEC) {
272                 SpaceNode *snode = CTX_wm_space_node(C);
273                 if (snode && snode->id)
274                         ED_node_tag_update_id(snode->id);
275         }
276 }
277
278 static void node_uiblocks_init(const bContext *C, bNodeTree *ntree)
279 {
280         bNode *node;
281         char uiblockstr[32];
282         
283         /* add node uiBlocks in drawing order - prevents events going to overlapping nodes */
284         
285         for (node = ntree->nodes.first; node; node = node->next) {
286                 /* ui block */
287                 BLI_snprintf(uiblockstr, sizeof(uiblockstr), "node buttons %p", (void *)node);
288                 node->block = uiBeginBlock(C, CTX_wm_region(C), uiblockstr, UI_EMBOSS);
289                 uiBlockSetHandleFunc(node->block, do_node_internal_buttons, node);
290
291                 /* this cancels events for background nodes */
292                 uiBlockSetFlag(node->block, UI_BLOCK_CLIP_EVENTS);
293         }
294 }
295
296 void node_to_view(struct bNode *node, float x, float y, float *rx, float *ry)
297 {
298         nodeToView(node, x, y, rx, ry);
299         *rx *= UI_DPI_FAC;
300         *ry *= UI_DPI_FAC;
301 }
302
303 void node_from_view(struct bNode *node, float x, float y, float *rx, float *ry)
304 {
305         x /= UI_DPI_FAC;
306         y /= UI_DPI_FAC;
307         nodeFromView(node, x, y, rx, ry);
308 }
309
310
311 /* based on settings in node, sets drawing rect info. each redraw! */
312 static void node_update_basis(const bContext *C, bNodeTree *ntree, bNode *node)
313 {
314         uiLayout *layout, *row;
315         PointerRNA nodeptr, sockptr;
316         bNodeSocket *nsock;
317         float locx, locy;
318         float dy;
319         int buty;
320         
321         RNA_pointer_create(&ntree->id, &RNA_Node, node, &nodeptr);
322         
323         /* get "global" coords */
324         node_to_view(node, 0.0f, 0.0f, &locx, &locy);
325         dy = locy;
326         
327         /* header */
328         dy -= NODE_DY;
329         
330         /* little bit space in top */
331         if (node->outputs.first)
332                 dy -= NODE_DYS / 2;
333         
334         /* output sockets */
335         for (nsock = node->outputs.first; nsock; nsock = nsock->next) {
336                 if (nodeSocketIsHidden(nsock))
337                         continue;
338                 
339                 RNA_pointer_create(&ntree->id, &RNA_NodeSocket, nsock, &sockptr);
340                 
341                 layout = uiBlockLayout(node->block, UI_LAYOUT_VERTICAL, UI_LAYOUT_PANEL,
342                                        locx + NODE_DYS, dy, NODE_WIDTH(node) - NODE_DY, NODE_DY, UI_GetStyle());
343                 /* context pointers for current node and socket */
344                 uiLayoutSetContextPointer(layout, "node", &nodeptr);
345                 uiLayoutSetContextPointer(layout, "socket", &sockptr);
346                 
347                 /* align output buttons to the right */
348                 row = uiLayoutRow(layout, 1);
349                 uiLayoutSetAlignment(row, UI_LAYOUT_ALIGN_RIGHT);
350                 
351                 node->typeinfo->drawoutputfunc((bContext *)C, row, &sockptr, &nodeptr, (nsock->flag & SOCK_IN_USE));
352                 
353                 uiBlockEndAlign(node->block);
354                 uiBlockLayoutResolve(node->block, NULL, &buty);
355                 
356                 /* ensure minimum socket height in case layout is empty */
357                 buty = MIN2(buty, dy - NODE_DY);
358                 
359                 nsock->locx = locx + NODE_WIDTH(node);
360                 /* place the socket circle in the middle of the layout */
361                 nsock->locy = 0.5f * (dy + buty);
362                 dy = buty;
363         }
364
365         node->prvr.xmin = locx + NODE_DYS;
366         node->prvr.xmax = locx + NODE_WIDTH(node) - NODE_DYS;
367
368         /* preview rect? */
369         if (node->flag & NODE_PREVIEW) {
370                 float aspect = 1.0f;
371                 
372                 if (node->preview_xsize && node->preview_ysize) 
373                         aspect = (float)node->preview_ysize / (float)node->preview_xsize;
374                 
375                 dy -= NODE_DYS / 2;
376                 node->prvr.ymax = dy;
377                 
378                 if (aspect <= 1.0f)
379                         node->prvr.ymin = dy - aspect * (NODE_WIDTH(node) - NODE_DY);
380                 else {
381                         /* width correction of image */
382                         /* XXX huh? (ton) */
383                         float dx = (NODE_WIDTH(node) - NODE_DYS) - (NODE_WIDTH(node) - NODE_DYS) / aspect;
384                         
385                         node->prvr.ymin = dy - (NODE_WIDTH(node) - NODE_DY);
386                         
387                         node->prvr.xmin += 0.5f * dx;
388                         node->prvr.xmax -= 0.5f * dx;
389                 }
390                 
391                 dy = node->prvr.ymin - NODE_DYS / 2;
392                 
393                 /* make sure that maximums are bigger or equal to minimums */
394                 if (node->prvr.xmax < node->prvr.xmin) SWAP(float, node->prvr.xmax, node->prvr.xmin);
395                 if (node->prvr.ymax < node->prvr.ymin) SWAP(float, node->prvr.ymax, node->prvr.ymin);
396         }
397
398         /* buttons rect? */
399         /* TODO: NODE_OPTION shall be cleaned up */
400         if (/*(node->flag & NODE_OPTIONS) && */node->typeinfo->uifunc) {
401                 dy -= NODE_DYS / 2;
402
403                 /* set this for uifunc() that don't use layout engine yet */
404                 node->butr.xmin = 0;
405                 node->butr.xmax = NODE_WIDTH(node) - 2 * NODE_DYS;
406                 node->butr.ymin = 0;
407                 node->butr.ymax = 0;
408                 
409                         
410                 layout = uiBlockLayout(node->block, UI_LAYOUT_VERTICAL, UI_LAYOUT_PANEL,
411                                        locx + NODE_DYS, dy, node->butr.xmax, 0, UI_GetStyle());
412                 uiLayoutSetContextPointer(layout, "node", &nodeptr);
413                 
414                 node->typeinfo->uifunc(layout, (bContext *)C, &nodeptr);
415                 
416                 uiBlockEndAlign(node->block);
417                 uiBlockLayoutResolve(node->block, NULL, &buty);
418                         
419                 dy = buty - NODE_DYS / 2;
420         }
421
422         /* input sockets */
423         for (nsock = node->inputs.first; nsock; nsock = nsock->next) {
424                 if (nodeSocketIsHidden(nsock))
425                         continue;
426                 
427                 RNA_pointer_create(&ntree->id, &RNA_NodeSocket, nsock, &sockptr);
428                 
429                 layout = uiBlockLayout(node->block, UI_LAYOUT_VERTICAL, UI_LAYOUT_PANEL,
430                                        locx + NODE_DYS, dy, NODE_WIDTH(node) - NODE_DY, NODE_DY, UI_GetStyle());
431                 /* context pointers for current node and socket */
432                 uiLayoutSetContextPointer(layout, "node", &nodeptr);
433                 uiLayoutSetContextPointer(layout, "socket", &sockptr);
434                 
435                 node->typeinfo->drawinputfunc((bContext *)C, layout, &sockptr, &nodeptr, (nsock->flag & SOCK_IN_USE));
436                 
437                 uiBlockEndAlign(node->block);
438                 uiBlockLayoutResolve(node->block, NULL, &buty);
439                 
440                 /* ensure minimum socket height in case layout is empty */
441                 buty = MIN2(buty, dy - NODE_DY);
442                 
443                 nsock->locx = locx;
444                 /* place the socket circle in the middle of the layout */
445                 nsock->locy = 0.5f * (dy + buty);
446                 dy = buty;
447         }
448         
449         /* little bit space in end */
450         if (node->inputs.first || (node->flag & (NODE_OPTIONS | NODE_PREVIEW)) == 0)
451                 dy -= NODE_DYS / 2;
452
453         node->totr.xmin = locx;
454         node->totr.xmax = locx + NODE_WIDTH(node);
455         node->totr.ymax = locy;
456         node->totr.ymin = min_ff(dy, locy - 2 * NODE_DY);
457         
458         /* Set the block bounds to clip mouse events from underlying nodes.
459          * Add a margin for sockets on each side.
460          */
461         uiExplicitBoundsBlock(node->block,
462                               node->totr.xmin - NODE_SOCKSIZE,
463                               node->totr.ymin,
464                               node->totr.xmax + NODE_SOCKSIZE,
465                               node->totr.ymax);
466 }
467
468 /* based on settings in node, sets drawing rect info. each redraw! */
469 static void node_update_hidden(bNode *node)
470 {
471         bNodeSocket *nsock;
472         float locx, locy;
473         float rad, drad, hiddenrad = HIDDEN_RAD;
474         int totin = 0, totout = 0, tot;
475         
476         /* get "global" coords */
477         node_to_view(node, 0.0f, 0.0f, &locx, &locy);
478
479         /* calculate minimal radius */
480         for (nsock = node->inputs.first; nsock; nsock = nsock->next)
481                 if (!nodeSocketIsHidden(nsock))
482                         totin++;
483         for (nsock = node->outputs.first; nsock; nsock = nsock->next)
484                 if (!nodeSocketIsHidden(nsock))
485                         totout++;
486         
487         tot = MAX2(totin, totout);
488         if (tot > 4) {
489                 hiddenrad += 5.0f * (float)(tot - 4);
490         }
491         
492         node->totr.xmin = locx;
493         node->totr.xmax = locx + 3 * hiddenrad + node->miniwidth;
494         node->totr.ymax = locy + (hiddenrad - 0.5f * NODE_DY);
495         node->totr.ymin = node->totr.ymax - 2 * hiddenrad;
496         
497         /* output sockets */
498         rad = drad = (float)M_PI / (1.0f + (float)totout);
499         
500         for (nsock = node->outputs.first; nsock; nsock = nsock->next) {
501                 if (!nodeSocketIsHidden(nsock)) {
502                         nsock->locx = node->totr.xmax - hiddenrad + sinf(rad) * hiddenrad;
503                         nsock->locy = node->totr.ymin + hiddenrad + cosf(rad) * hiddenrad;
504                         rad += drad;
505                 }
506         }
507         
508         /* input sockets */
509         rad = drad = -(float)M_PI / (1.0f + (float)totin);
510         
511         for (nsock = node->inputs.first; nsock; nsock = nsock->next) {
512                 if (!nodeSocketIsHidden(nsock)) {
513                         nsock->locx = node->totr.xmin + hiddenrad + sinf(rad) * hiddenrad;
514                         nsock->locy = node->totr.ymin + hiddenrad + cosf(rad) * hiddenrad;
515                         rad += drad;
516                 }
517         }
518
519         /* Set the block bounds to clip mouse events from underlying nodes.
520          * Add a margin for sockets on each side.
521          */
522         uiExplicitBoundsBlock(node->block,
523                               node->totr.xmin - NODE_SOCKSIZE,
524                               node->totr.ymin,
525                               node->totr.xmax + NODE_SOCKSIZE,
526                               node->totr.ymax);
527 }
528
529 void node_update_default(const bContext *C, bNodeTree *ntree, bNode *node)
530 {
531         if (node->flag & NODE_HIDDEN)
532                 node_update_hidden(node);
533         else
534                 node_update_basis(C, ntree, node);
535 }
536
537 int node_select_area_default(bNode *node, int x, int y)
538 {
539         return BLI_rctf_isect_pt(&node->totr, x, y);
540 }
541
542 int node_tweak_area_default(bNode *node, int x, int y)
543 {
544         return BLI_rctf_isect_pt(&node->totr, x, y);
545 }
546
547 int node_get_colorid(bNode *node)
548 {
549         switch (node->typeinfo->nclass) {
550                 case NODE_CLASS_INPUT:      return TH_NODE_IN_OUT;
551                 case NODE_CLASS_OUTPUT:     return (node->flag & NODE_DO_OUTPUT) ? TH_NODE_IN_OUT : TH_NODE;
552                 case NODE_CLASS_CONVERTOR:  return TH_NODE_CONVERTOR;
553                 case NODE_CLASS_OP_COLOR:
554                 case NODE_CLASS_OP_VECTOR:
555                 case NODE_CLASS_OP_FILTER:  return TH_NODE_OPERATOR;
556                 case NODE_CLASS_GROUP:      return TH_NODE_GROUP;
557                 case NODE_CLASS_INTERFACE:  return TH_NODE_INTERFACE;
558                 case NODE_CLASS_MATTE:      return TH_NODE_MATTE;
559                 case NODE_CLASS_DISTORT:    return TH_NODE_DISTORT;
560                 default:                    return TH_NODE;
561         }
562 }
563
564 /* note: in cmp_util.c is similar code, for node_compo_pass_on()
565  *       the same goes for shader and texture nodes. */
566 /* note: in node_edit.c is similar code, for untangle node */
567 static void node_draw_mute_line(View2D *v2d, SpaceNode *snode, bNode *node)
568 {
569         bNodeLink *link;
570
571         glEnable(GL_BLEND);
572         glEnable(GL_LINE_SMOOTH);
573
574         for (link = node->internal_links.first; link; link = link->next)
575                 node_draw_link_bezier(v2d, snode, link, TH_REDALERT, 0, TH_WIRE, 0, TH_WIRE);
576
577         glDisable(GL_BLEND);
578         glDisable(GL_LINE_SMOOTH);
579 }
580
581 /* this might have some more generic use */
582 static void node_circle_draw(float x, float y, float size, float *col, int highlight)
583 {
584         /* 16 values of sin function */
585         static float si[16] = {
586                 0.00000000f, 0.39435585f, 0.72479278f, 0.93775213f,
587                 0.99871650f, 0.89780453f, 0.65137248f, 0.29936312f,
588                 -0.10116832f, -0.48530196f, -0.79077573f, -0.96807711f,
589                 -0.98846832f, -0.84864425f, -0.57126821f, -0.20129852f
590         };
591         /* 16 values of cos function */
592         static float co[16] = {
593                 1.00000000f, 0.91895781f, 0.68896691f, 0.34730525f,
594                 -0.05064916f, -0.44039415f, -0.75875812f, -0.95413925f,
595                 -0.99486932f, -0.87434661f, -0.61210598f, -0.25065253f,
596                 0.15142777f, 0.52896401f, 0.82076344f, 0.97952994f,
597         };
598         int a;
599         
600         glColor4fv(col);
601         
602         glEnable(GL_BLEND);
603         glBegin(GL_POLYGON);
604         for (a = 0; a < 16; a++)
605                 glVertex2f(x + size * si[a], y + size * co[a]);
606         glEnd();
607         glDisable(GL_BLEND);
608         
609         if (highlight) {
610                 UI_ThemeColor(TH_TEXT_HI);
611                 glLineWidth(1.5f);
612         }
613         else {
614                 glColor4ub(0, 0, 0, 150);
615         }
616         glEnable(GL_BLEND);
617         glEnable(GL_LINE_SMOOTH);
618         glBegin(GL_LINE_LOOP);
619         for (a = 0; a < 16; a++)
620                 glVertex2f(x + size * si[a], y + size * co[a]);
621         glEnd();
622         glDisable(GL_LINE_SMOOTH);
623         glDisable(GL_BLEND);
624         glLineWidth(1.0f);
625 }
626
627 void node_socket_circle_draw(const bContext *C, bNodeTree *ntree, bNode *node, bNodeSocket *sock, float size, int highlight)
628 {
629         PointerRNA ptr, node_ptr;
630         float color[4];
631         
632         RNA_pointer_create((ID *)ntree, &RNA_NodeSocket, sock, &ptr);
633         RNA_pointer_create((ID *)ntree, &RNA_Node, node, &node_ptr);
634         sock->typeinfo->draw_color((bContext *)C, &ptr, &node_ptr, color);
635         node_circle_draw(sock->locx, sock->locy, size, color, highlight);
636 }
637
638 /* **************  Socket callbacks *********** */
639
640 static void node_draw_preview_background(float tile, rctf *rect)
641 {
642         float x, y;
643         
644         /* draw checkerboard backdrop to show alpha */
645         glColor3ub(120, 120, 120);
646         glRectf(rect->xmin, rect->ymin, rect->xmax, rect->ymax);
647         glColor3ub(160, 160, 160);
648         
649         for (y = rect->ymin; y < rect->ymax; y += tile * 2) {
650                 for (x = rect->xmin; x < rect->xmax; x += tile * 2) {
651                         float tilex = tile, tiley = tile;
652
653                         if (x + tile > rect->xmax)
654                                 tilex = rect->xmax - x;
655                         if (y + tile > rect->ymax)
656                                 tiley = rect->ymax - y;
657
658                         glRectf(x, y, x + tilex, y + tiley);
659                 }
660         }
661         for (y = rect->ymin + tile; y < rect->ymax; y += tile * 2) {
662                 for (x = rect->xmin + tile; x < rect->xmax; x += tile * 2) {
663                         float tilex = tile, tiley = tile;
664
665                         if (x + tile > rect->xmax)
666                                 tilex = rect->xmax - x;
667                         if (y + tile > rect->ymax)
668                                 tiley = rect->ymax - y;
669
670                         glRectf(x, y, x + tilex, y + tiley);
671                 }
672         }
673 }
674
675 /* not a callback */
676 static void node_draw_preview(bNodePreview *preview, rctf *prv)
677 {
678         float xrect = BLI_rctf_size_x(prv);
679         float yrect = BLI_rctf_size_y(prv);
680         float xscale = xrect / ((float)preview->xsize);
681         float yscale = yrect / ((float)preview->ysize);
682         float scale;
683         rctf draw_rect;
684         
685         /* uniform scale and offset */
686         draw_rect = *prv;
687         if (xscale < yscale) {
688                 float offset = 0.5f * (yrect - ((float)preview->ysize) * xscale);
689                 draw_rect.ymin += offset;
690                 draw_rect.ymax -= offset;
691                 scale = xscale;
692         }
693         else {
694                 float offset = 0.5f * (xrect - ((float)preview->xsize) * yscale);
695                 draw_rect.xmin += offset;
696                 draw_rect.xmax -= offset;
697                 scale = yscale;
698         }
699         
700         node_draw_preview_background(BLI_rctf_size_x(prv) / 10.0f, &draw_rect);
701         
702         glEnable(GL_BLEND);
703         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);  /* premul graphics */
704         
705         glColor4f(1.0, 1.0, 1.0, 1.0);
706         glPixelZoom(scale, scale);
707         glaDrawPixelsTex(draw_rect.xmin, draw_rect.ymin, preview->xsize, preview->ysize, GL_UNSIGNED_BYTE, GL_LINEAR, preview->rect);
708         glPixelZoom(1.0f, 1.0f);
709         
710         glDisable(GL_BLEND);
711
712         UI_ThemeColorShadeAlpha(TH_BACK, -15, +100);
713         fdrawbox(draw_rect.xmin, draw_rect.ymin, draw_rect.xmax, draw_rect.ymax);
714 }
715
716 /* common handle function for operator buttons that need to select the node first */
717 static void node_toggle_button_cb(struct bContext *C, void *node_argv, void *op_argv)
718 {
719         bNode *node = (bNode *)node_argv;
720         const char *opname = (const char *)op_argv;
721         
722         /* select & activate only the button's node */
723         node_select_single(C, node);
724         
725         WM_operator_name_call(C, opname, WM_OP_INVOKE_DEFAULT, NULL);
726 }
727
728 void node_draw_shadow(SpaceNode *snode, bNode *node, float radius, float alpha)
729 {
730         rctf *rct = &node->totr;
731         
732         uiSetRoundBox(UI_CNR_ALL);
733         if (node->parent == NULL)
734                 ui_dropshadow(rct, radius, snode->aspect, alpha, node->flag & SELECT);
735         else {
736                 const float margin = 3.0f;
737                 
738                 glColor4f(0.0f, 0.0f, 0.0f, 0.33f);
739                 glEnable(GL_BLEND);
740                 uiRoundBox(rct->xmin - margin, rct->ymin - margin,
741                            rct->xmax + margin, rct->ymax + margin, radius + margin);
742                 glDisable(GL_BLEND);
743         }
744 }
745
746 static void node_draw_basis(const bContext *C, ARegion *ar, SpaceNode *snode, bNodeTree *ntree, bNode *node, bNodeInstanceKey key)
747 {
748         bNodeInstanceHash *previews = CTX_data_pointer_get(C, "node_previews").data;
749         bNodeSocket *sock;
750         rctf *rct = &node->totr;
751         float iconofs;
752         /* float socket_size = NODE_SOCKSIZE*U.dpi/72; */ /* UNUSED */
753         float iconbutw = 0.8f * UI_UNIT_X;
754         int color_id = node_get_colorid(node);
755         char showname[128]; /* 128 used below */
756         View2D *v2d = &ar->v2d;
757         
758         /* XXX hack: copy values from linked ID data where displayed as sockets */
759         if (node->block)
760                 nodeSynchronizeID(node, false);
761         
762         /* skip if out of view */
763         if (BLI_rctf_isect(&node->totr, &ar->v2d.cur, NULL) == FALSE) {
764                 uiEndBlock(C, node->block);
765                 node->block = NULL;
766                 return;
767         }
768         
769         /* shadow */
770         node_draw_shadow(snode, node, BASIS_RAD, 1.0f);
771         
772         /* header */
773         if (color_id == TH_NODE)
774                 UI_ThemeColorShade(color_id, -20);
775         else
776                 UI_ThemeColor(color_id);
777         
778         if (node->flag & NODE_MUTED)
779                 UI_ThemeColorBlend(color_id, TH_REDALERT, 0.5f);
780         
781
782 #ifdef WITH_COMPOSITOR
783         if (ntree->type == NTREE_COMPOSIT && (snode->flag & SNODE_SHOW_HIGHLIGHT)) {
784                 if (COM_isHighlightedbNode(node)) {
785                         UI_ThemeColorBlend(color_id, TH_ACTIVE, 0.5f);
786                 }
787         }
788 #endif
789
790         uiSetRoundBox(UI_CNR_TOP_LEFT | UI_CNR_TOP_RIGHT);
791         uiRoundBox(rct->xmin, rct->ymax - NODE_DY, rct->xmax, rct->ymax, BASIS_RAD);
792         
793         /* show/hide icons */
794         iconofs = rct->xmax - 0.35f * U.widget_unit;
795         
796         /* preview */
797         if (node->typeinfo->flag & NODE_PREVIEW) {
798                 uiBut *but;
799                 iconofs -= iconbutw;
800                 uiBlockSetEmboss(node->block, UI_EMBOSSN);
801                 but = uiDefIconBut(node->block, TOGBUT, B_REDR, ICON_MATERIAL,
802                                    iconofs, rct->ymax - NODE_DY, iconbutw, UI_UNIT_Y, NULL, 0, 0, 0, 0, "");
803                 uiButSetFunc(but, node_toggle_button_cb, node, (void *)"NODE_OT_preview_toggle");
804                 /* XXX this does not work when node is activated and the operator called right afterwards,
805                  * since active ID is not updated yet (needs to process the notifier).
806                  * This can only work as visual indicator!
807                  */
808 //              if (!(node->flag & (NODE_ACTIVE_ID|NODE_DO_OUTPUT)))
809 //                      uiButSetFlag(but, UI_BUT_DISABLED);
810                 uiBlockSetEmboss(node->block, UI_EMBOSS);
811         }
812         /* group edit */
813         if (node->type == NODE_GROUP) {
814                 uiBut *but;
815                 iconofs -= iconbutw;
816                 uiBlockSetEmboss(node->block, UI_EMBOSSN);
817                 but = uiDefIconBut(node->block, TOGBUT, B_REDR, ICON_NODETREE,
818                                    iconofs, rct->ymax - NODE_DY, iconbutw, UI_UNIT_Y, NULL, 0, 0, 0, 0, "");
819                 uiButSetFunc(but, node_toggle_button_cb, node, (void *)"NODE_OT_group_edit");
820                 uiBlockSetEmboss(node->block, UI_EMBOSS);
821         }
822         
823         /* title */
824         if (node->flag & SELECT) 
825                 UI_ThemeColor(TH_SELECT);
826         else
827                 UI_ThemeColorBlendShade(TH_TEXT, color_id, 0.4f, 10);
828         
829         /* open/close entirely? */
830         {
831                 uiBut *but;
832                 int but_size = UI_UNIT_X * 0.6f;
833                 /* XXX button uses a custom triangle draw below, so make it invisible without icon */
834                 uiBlockSetEmboss(node->block, UI_EMBOSSN);
835                 but = uiDefBut(node->block, TOGBUT, B_REDR, "",
836                                rct->xmin + 0.5f * U.widget_unit - but_size / 2, rct->ymax - NODE_DY / 2.0f - but_size / 2,
837                                but_size, but_size, NULL, 0, 0, 0, 0, "");
838                 uiButSetFunc(but, node_toggle_button_cb, node, (void *)"NODE_OT_hide_toggle");
839                 uiBlockSetEmboss(node->block, UI_EMBOSS);
840                 
841                 /* custom draw function for this button */
842                 UI_DrawTriIcon(rct->xmin + 0.5f * U.widget_unit, rct->ymax - NODE_DY / 2.0f, 'v');
843         }
844         
845         /* this isn't doing anything for the label, so commenting out */
846 #if 0
847         if (node->flag & SELECT) 
848                 UI_ThemeColor(TH_TEXT_HI);
849         else
850                 UI_ThemeColor(TH_TEXT);
851 #endif
852         
853         BLI_strncpy(showname, nodeLabel(node), sizeof(showname));
854         
855         //if (node->flag & NODE_MUTED)
856         //      BLI_snprintf(showname, sizeof(showname), "[%s]", showname); /* XXX - don't print into self! */
857         
858         uiDefBut(node->block, LABEL, 0, showname,
859                  (int)(rct->xmin + (NODE_MARGIN_X)), (int)(rct->ymax - NODE_DY),
860                  (short)(iconofs - rct->xmin - 18.0f), (short)NODE_DY,
861                  NULL, 0, 0, 0, 0, "");
862
863         /* body */
864         if (node->flag & NODE_CUSTOM_COLOR)
865                 glColor3fv(node->color);
866         else
867                 UI_ThemeColor4(TH_NODE);
868         glEnable(GL_BLEND);
869         uiSetRoundBox(UI_CNR_BOTTOM_LEFT | UI_CNR_BOTTOM_RIGHT);
870         uiRoundBox(rct->xmin, rct->ymin, rct->xmax, rct->ymax - NODE_DY, BASIS_RAD);
871         glDisable(GL_BLEND);
872
873         /* outline active and selected emphasis */
874         if (node->flag & SELECT) {
875                 
876                 glEnable(GL_BLEND);
877                 glEnable(GL_LINE_SMOOTH);
878                 
879                 if (node->flag & NODE_ACTIVE)
880                         UI_ThemeColorShadeAlpha(TH_ACTIVE, 0, -40);
881                 else
882                         UI_ThemeColorShadeAlpha(TH_SELECT, 0, -40);
883                 
884                 uiSetRoundBox(UI_CNR_ALL);
885                 uiDrawBox(GL_LINE_LOOP, rct->xmin, rct->ymin, rct->xmax, rct->ymax, BASIS_RAD);
886                 
887                 glDisable(GL_LINE_SMOOTH);
888                 glDisable(GL_BLEND);
889         }
890         
891         /* disable lines */
892         if (node->flag & NODE_MUTED)
893                 node_draw_mute_line(v2d, snode, node);
894
895         
896         /* socket inputs, buttons */
897         for (sock = node->inputs.first; sock; sock = sock->next) {
898                 if (nodeSocketIsHidden(sock))
899                         continue;
900                 
901                 node_socket_circle_draw(C, ntree, node, sock, NODE_SOCKSIZE, sock->flag & SELECT);
902         }
903         
904         /* socket outputs */
905         for (sock = node->outputs.first; sock; sock = sock->next) {
906                 if (nodeSocketIsHidden(sock))
907                         continue;
908                 
909                 node_socket_circle_draw(C, ntree, node, sock, NODE_SOCKSIZE, sock->flag & SELECT);
910         }
911         
912         /* preview */
913         if (node->flag & NODE_PREVIEW) {
914                 bNodePreview *preview = previews ? BKE_node_instance_hash_lookup(previews, key) : NULL;
915                 if (preview && preview->rect && !BLI_rctf_is_empty(&node->prvr))
916                         node_draw_preview(preview, &node->prvr);
917         }
918         
919         UI_ThemeClearColor(color_id);
920                 
921         uiEndBlock(C, node->block);
922         uiDrawBlock(C, node->block);
923         node->block = NULL;
924 }
925
926 static void node_draw_hidden(const bContext *C, ARegion *ar, SpaceNode *snode, bNodeTree *ntree, bNode *node, bNodeInstanceKey UNUSED(key))
927 {
928         bNodeSocket *sock;
929         rctf *rct = &node->totr;
930         float dx, centy = BLI_rctf_cent_y(rct);
931         float hiddenrad = BLI_rctf_size_y(rct) / 2.0f;
932         float socket_size = NODE_SOCKSIZE;
933         int color_id = node_get_colorid(node);
934         char showname[128]; /* 128 is used below */
935         
936         /* shadow */
937         node_draw_shadow(snode, node, hiddenrad, 1.0f);
938
939         /* body */
940         UI_ThemeColor(color_id);
941         if (node->flag & NODE_MUTED)
942                 UI_ThemeColorBlend(color_id, TH_REDALERT, 0.5f);
943
944 #ifdef WITH_COMPOSITOR
945         if (ntree->type == NTREE_COMPOSIT && (snode->flag & SNODE_SHOW_HIGHLIGHT)) {
946                 if (COM_isHighlightedbNode(node)) {
947                         UI_ThemeColorBlend(color_id, TH_ACTIVE, 0.5f);
948                 }
949         }
950 #else
951         (void)ntree;
952 #endif
953         
954         uiRoundBox(rct->xmin, rct->ymin, rct->xmax, rct->ymax, hiddenrad);
955         
956         /* outline active and selected emphasis */
957         if (node->flag & SELECT) {
958                 glEnable(GL_BLEND);
959                 glEnable(GL_LINE_SMOOTH);
960                 
961                 if (node->flag & NODE_ACTIVE)
962                         UI_ThemeColorShadeAlpha(TH_ACTIVE, 0, -40);
963                 else
964                         UI_ThemeColorShadeAlpha(TH_SELECT, 0, -40);
965                 uiDrawBox(GL_LINE_LOOP, rct->xmin, rct->ymin, rct->xmax, rct->ymax, hiddenrad);
966                 
967                 glDisable(GL_LINE_SMOOTH);
968                 glDisable(GL_BLEND);
969         }
970         
971         /* title */
972         if (node->flag & SELECT) 
973                 UI_ThemeColor(TH_SELECT);
974         else
975                 UI_ThemeColorBlendShade(TH_TEXT, color_id, 0.4f, 10);
976         
977         /* open entirely icon */
978         {
979                 uiBut *but;
980                 int but_size = UI_UNIT_X * 0.6f;
981                 /* XXX button uses a custom triangle draw below, so make it invisible without icon */
982                 uiBlockSetEmboss(node->block, UI_EMBOSSN);
983                 but = uiDefBut(node->block, TOGBUT, B_REDR, "",
984                                rct->xmin + 10.0f - but_size / 2, centy - but_size / 2,
985                                but_size, but_size, NULL, 0, 0, 0, 0, "");
986                 uiButSetFunc(but, node_toggle_button_cb, node, (void *)"NODE_OT_hide_toggle");
987                 uiBlockSetEmboss(node->block, UI_EMBOSS);
988                 
989                 /* custom draw function for this button */
990                 UI_DrawTriIcon(rct->xmin + 10.0f, centy, 'h');
991         }
992         
993         /* disable lines */
994         if (node->flag & NODE_MUTED)
995                 node_draw_mute_line(&ar->v2d, snode, node);
996         
997         if (node->flag & SELECT) 
998                 UI_ThemeColor(TH_SELECT);
999         else
1000                 UI_ThemeColor(TH_TEXT);
1001         
1002         if (node->miniwidth > 0.0f) {
1003                 BLI_strncpy(showname, nodeLabel(node), sizeof(showname));
1004                 
1005                 //if (node->flag & NODE_MUTED)
1006                 //      BLI_snprintf(showname, sizeof(showname), "[%s]", showname); /* XXX - don't print into self! */
1007
1008                 uiDefBut(node->block, LABEL, 0, showname,
1009                          (int)(rct->xmin + (NODE_MARGIN_X)), (int)(centy - 10),
1010                          (short)(BLI_rctf_size_x(rct) - 18.0f - 12.0f), (short)NODE_DY,
1011                          NULL, 0, 0, 0, 0, "");
1012         }
1013
1014         /* scale widget thing */
1015         UI_ThemeColorShade(color_id, -10);
1016         dx = 10.0f;
1017         fdrawline(rct->xmax - dx, centy - 4.0f, rct->xmax - dx, centy + 4.0f);
1018         fdrawline(rct->xmax - dx - 3.0f * snode->aspect, centy - 4.0f, rct->xmax - dx - 3.0f * snode->aspect, centy + 4.0f);
1019         
1020         UI_ThemeColorShade(color_id, +30);
1021         dx -= snode->aspect;
1022         fdrawline(rct->xmax - dx, centy - 4.0f, rct->xmax - dx, centy + 4.0f);
1023         fdrawline(rct->xmax - dx - 3.0f * snode->aspect, centy - 4.0f, rct->xmax - dx - 3.0f * snode->aspect, centy + 4.0f);
1024
1025         /* sockets */
1026         for (sock = node->inputs.first; sock; sock = sock->next) {
1027                 if (!nodeSocketIsHidden(sock))
1028                         node_socket_circle_draw(C, ntree, node, sock, socket_size, sock->flag & SELECT);
1029         }
1030         
1031         for (sock = node->outputs.first; sock; sock = sock->next) {
1032                 if (!nodeSocketIsHidden(sock))
1033                         node_socket_circle_draw(C, ntree, node, sock, socket_size, sock->flag & SELECT);
1034         }
1035         
1036         uiEndBlock(C, node->block);
1037         uiDrawBlock(C, node->block);
1038         node->block = NULL;
1039 }
1040
1041 int node_get_resize_cursor(int directions)
1042 {
1043         if (directions == 0)
1044                 return CURSOR_STD;
1045         else if ((directions & ~(NODE_RESIZE_TOP | NODE_RESIZE_BOTTOM)) == 0)
1046                 return CURSOR_Y_MOVE;
1047         else if ((directions & ~(NODE_RESIZE_RIGHT | NODE_RESIZE_LEFT)) == 0)
1048                 return CURSOR_X_MOVE;
1049         else
1050                 return CURSOR_EDIT;
1051 }
1052
1053 void node_set_cursor(wmWindow *win, SpaceNode *snode)
1054 {
1055         bNodeTree *ntree = snode->edittree;
1056         bNode *node;
1057         bNodeSocket *sock;
1058         int cursor = CURSOR_STD;
1059         
1060         if (ntreeIsValid(ntree)) {
1061                 if (node_find_indicated_socket(snode, &node, &sock, SOCK_IN | SOCK_OUT)) {
1062                         /* pass */
1063                 }
1064                 else {
1065                         /* check nodes front to back */
1066                         for (node = ntree->nodes.last; node; node = node->prev) {
1067                                 if (BLI_rctf_isect_pt(&node->totr, snode->cursor[0], snode->cursor[1]))
1068                                         break;  /* first hit on node stops */
1069                         }
1070                         if (node) {
1071                                 int dir = node->typeinfo->resize_area_func(node, snode->cursor[0], snode->cursor[1]);
1072                                 cursor = node_get_resize_cursor(dir);
1073                         }
1074                 }
1075         }
1076         
1077         WM_cursor_set(win, cursor);
1078 }
1079
1080 void node_draw_default(const bContext *C, ARegion *ar, SpaceNode *snode, bNodeTree *ntree, bNode *node, bNodeInstanceKey key)
1081 {
1082         if (node->flag & NODE_HIDDEN)
1083                 node_draw_hidden(C, ar, snode, ntree, node, key);
1084         else
1085                 node_draw_basis(C, ar, snode, ntree, node, key);
1086 }
1087
1088 static void node_update(const bContext *C, bNodeTree *ntree, bNode *node)
1089 {
1090         if (node->typeinfo->drawupdatefunc)
1091                 node->typeinfo->drawupdatefunc(C, ntree, node);
1092 }
1093
1094 void node_update_nodetree(const bContext *C, bNodeTree *ntree)
1095 {
1096         bNode *node;
1097         
1098         /* update nodes front to back, so children sizes get updated before parents */
1099         for (node = ntree->nodes.last; node; node = node->prev) {
1100                 node_update(C, ntree, node);
1101         }
1102 }
1103
1104 static void node_draw(const bContext *C, ARegion *ar, SpaceNode *snode, bNodeTree *ntree, bNode *node, bNodeInstanceKey key)
1105 {
1106         if (node->typeinfo->drawfunc)
1107                 node->typeinfo->drawfunc(C, ar, snode, ntree, node, key);
1108 }
1109
1110 #define USE_DRAW_TOT_UPDATE
1111
1112 void node_draw_nodetree(const bContext *C, ARegion *ar, SpaceNode *snode, bNodeTree *ntree, bNodeInstanceKey parent_key)
1113 {
1114         bNode *node;
1115         bNodeInstanceKey key;
1116         bNodeLink *link;
1117         int a;
1118         
1119         if (ntree == NULL) return;      /* groups... */
1120
1121 #ifdef USE_DRAW_TOT_UPDATE
1122         if (ntree->nodes.first) {
1123                 BLI_rctf_init_minmax(&ar->v2d.tot);
1124         }
1125 #endif
1126
1127         /* draw background nodes, last nodes in front */
1128         for (a = 0, node = ntree->nodes.first; node; node = node->next, a++) {
1129
1130 #ifdef USE_DRAW_TOT_UPDATE
1131                 /* unrelated to background nodes, update the v2d->tot,
1132                  * can be anywhere before we draw the scroll bars */
1133                 BLI_rctf_union(&ar->v2d.tot, &node->totr);
1134 #endif
1135
1136                 if (!(node->flag & NODE_BACKGROUND))
1137                         continue;
1138
1139                 key = BKE_node_instance_key(parent_key, ntree, node);
1140                 node->nr = a;        /* index of node in list, used for exec event code */
1141                 node_draw(C, ar, snode, ntree, node, key);
1142         }
1143         
1144         /* node lines */
1145         glEnable(GL_BLEND);
1146         glEnable(GL_LINE_SMOOTH);
1147         for (link = ntree->links.first; link; link = link->next) {
1148                 if (!nodeLinkIsHidden(link))
1149                         node_draw_link(&ar->v2d, snode, link);
1150         }
1151         glDisable(GL_LINE_SMOOTH);
1152         glDisable(GL_BLEND);
1153         
1154         /* draw foreground nodes, last nodes in front */
1155         for (a = 0, node = ntree->nodes.first; node; node = node->next, a++) {
1156                 bNodeInstanceKey key = BKE_node_instance_key(parent_key, ntree, node);
1157                 if (node->flag & NODE_BACKGROUND)
1158                         continue;
1159
1160                 key = BKE_node_instance_key(parent_key, ntree, node);
1161                 node->nr = a;        /* index of node in list, used for exec event code */
1162                 node_draw(C, ar, snode, ntree, node, key);
1163         }
1164 }
1165
1166 /* draw tree path info in lower left corner */
1167 static void draw_tree_path(SpaceNode *snode)
1168 {
1169         char info[256];
1170         
1171         ED_node_tree_path_get_fixedbuf(snode, info, sizeof(info));
1172         
1173         UI_ThemeColor(TH_TEXT_HI);
1174         BLF_draw_default(30, 30, 0.0f, info, sizeof(info));
1175 }
1176
1177 static void snode_setup_v2d(SpaceNode *snode, ARegion *ar, float centerx, float centery)
1178 {
1179         View2D *v2d = &ar->v2d;
1180         
1181         /* shift view to node tree center */
1182         UI_view2d_setcenter(v2d, centerx, centery);
1183         UI_view2d_view_ortho(v2d);
1184         
1185         /* aspect+font, set each time */
1186         snode->aspect = BLI_rctf_size_x(&v2d->cur) / (float)ar->winx;
1187         // XXX snode->curfont = uiSetCurFont_ext(snode->aspect);
1188 }
1189
1190 static void draw_nodetree(const bContext *C, ARegion *ar, bNodeTree *ntree, bNodeInstanceKey parent_key)
1191 {
1192         SpaceNode *snode = CTX_wm_space_node(C);
1193         
1194         node_uiblocks_init(C, ntree);
1195         
1196 #ifdef WITH_COMPOSITOR
1197         if (ntree->type == NTREE_COMPOSIT) {
1198                 COM_startReadHighlights();
1199         }
1200 #endif
1201         
1202         node_update_nodetree(C, ntree);
1203         node_draw_nodetree(C, ar, snode, ntree, parent_key);
1204 }
1205
1206 /* shade the parent node group and add a uiBlock to clip mouse events */
1207 static void draw_group_overlay(const bContext *C, ARegion *ar)
1208 {
1209         View2D *v2d = &ar->v2d;
1210         rctf rect = v2d->cur;
1211         uiBlock *block;
1212         
1213         /* shade node groups to separate them visually */
1214         UI_ThemeColorShadeAlpha(TH_NODE_GROUP, 0, -70);
1215         glEnable(GL_BLEND);
1216         uiSetRoundBox(0);
1217         uiDrawBox(GL_POLYGON, rect.xmin, rect.ymin, rect.xmax, rect.ymax, 0);
1218         glDisable(GL_BLEND);
1219         
1220         /* set the block bounds to clip mouse events from underlying nodes */
1221         block = uiBeginBlock(C, ar, "node tree bounds block", UI_EMBOSS);
1222         uiExplicitBoundsBlock(block, rect.xmin, rect.ymin, rect.xmax, rect.ymax);
1223         uiBlockSetFlag(block, UI_BLOCK_CLIP_EVENTS);
1224         uiEndBlock(C, block);
1225 }
1226
1227 void drawnodespace(const bContext *C, ARegion *ar)
1228 {
1229         View2DScrollers *scrollers;
1230         SpaceNode *snode = CTX_wm_space_node(C);
1231         View2D *v2d = &ar->v2d;
1232
1233         UI_ThemeClearColor(TH_BACK);
1234         glClear(GL_COLOR_BUFFER_BIT);
1235
1236         UI_view2d_view_ortho(v2d);
1237
1238         ED_region_draw_cb_draw(C, ar, REGION_DRAW_PRE_VIEW);
1239
1240         /* only set once */
1241         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1242         glEnable(GL_MAP1_VERTEX_3);
1243         
1244         /* nodes */
1245         snode_set_context(C);
1246         
1247         /* draw parent node trees */
1248         if (snode->treepath.last) {
1249                 static const int max_depth = 2;
1250                 bNodeTreePath *path;
1251                 int depth, curdepth;
1252                 float center[2];
1253                 bNodeTree *ntree;
1254                 bNodeLinkDrag *nldrag;
1255                 LinkData *linkdata;
1256                 
1257                 /* current View2D center, will be set temporarily for parent node trees */
1258                 UI_view2d_getcenter(v2d, &center[0], &center[1]);
1259                 
1260                 /* store new view center in current edittree */
1261                 if (snode->edittree)
1262                         copy_v2_v2(snode->edittree->view_center, center);
1263                 
1264                 depth = 0;
1265                 path = snode->treepath.last;
1266                 while (path->prev && depth < max_depth) {
1267                         path = path->prev;
1268                         ++depth;
1269                 }
1270                 /* parent node trees in the background */
1271                 for (curdepth = depth; curdepth >= 0; path = path->next, --curdepth) {
1272                         ntree = path->nodetree;
1273                         
1274                         if (ntreeIsValid(ntree)) {
1275                                 snode_setup_v2d(snode, ar, ntree->view_center[0], ntree->view_center[1]);
1276                                 
1277                                 if (curdepth == 0) {
1278                                         /* grid, uses theme color based on node path depth */
1279                                         UI_view2d_multi_grid_draw(v2d, (depth > 0 ? TH_NODE_GROUP : TH_BACK), U.widget_unit, 5, 2);
1280                                         
1281                                         /* backdrop */
1282                                         draw_nodespace_back_pix(C, ar, snode);
1283                                 }
1284                                 
1285                                 draw_nodetree(C, ar, ntree, path->parent_key);
1286                                 
1287                                 if (curdepth > 0)
1288                                         draw_group_overlay(C, ar);
1289                         }
1290                 }
1291                 
1292                 /* reset View2D */
1293                 UI_view2d_setcenter(v2d, center[0], center[1]);
1294                 
1295                 /* temporary links */
1296                 glEnable(GL_BLEND);
1297                 glEnable(GL_LINE_SMOOTH);
1298                 for (nldrag = snode->linkdrag.first; nldrag; nldrag = nldrag->next) {
1299                         for (linkdata = nldrag->links.first; linkdata; linkdata = linkdata->next)
1300                                 node_draw_link(v2d, snode, (bNodeLink *)linkdata->data);
1301                 }
1302                 glDisable(GL_LINE_SMOOTH);
1303                 glDisable(GL_BLEND);
1304                 
1305                 if (snode->flag & SNODE_SHOW_GPENCIL) {
1306                         /* draw grease-pencil ('canvas' strokes) */
1307                         draw_gpencil_view2d(C, TRUE);
1308                 }
1309         }
1310         else {
1311                 /* default grid */
1312                 UI_view2d_multi_grid_draw(v2d, TH_BACK, U.widget_unit, 5, 2);
1313                 
1314                 /* backdrop */
1315                 draw_nodespace_back_pix(C, ar, snode);
1316         }
1317         
1318         ED_region_draw_cb_draw(C, ar, REGION_DRAW_POST_VIEW);
1319         
1320         /* reset view matrix */
1321         UI_view2d_view_restore(C);
1322         
1323         if (snode->treepath.last) {
1324                 if (snode->flag & SNODE_SHOW_GPENCIL) {
1325                         /* draw grease-pencil (screen strokes, and also paintbuffer) */
1326                         draw_gpencil_view2d(C, FALSE);
1327                 }
1328         }
1329
1330         /* tree path info */
1331         draw_tree_path(snode);
1332         
1333         /* scrollers */
1334         scrollers = UI_view2d_scrollers_calc(C, v2d, 10, V2D_GRID_CLAMP, V2D_ARG_DUMMY, V2D_ARG_DUMMY);
1335         UI_view2d_scrollers_draw(C, v2d, scrollers);
1336         UI_view2d_scrollers_free(scrollers);
1337 }