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