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