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