Paste Code
Paste Blends
Paste Images
Index: release/scripts/presets/node_color/apricot.py
===================================================================
--- /dev/null (revision 45822)
+++ release/scripts/presets/node_color/apricot.py (working copy)
@@ -0,0 +1,5 @@
+import bpy
+node = bpy.context.active_node
+
+node.color = (0.9651851654052734, 0.5314834117889404, 0.30546149611473083)
+node.use_custom_color = True
Index: release/scripts/presets/node_color/durian.py
===================================================================
--- /dev/null (revision 45822)
+++ release/scripts/presets/node_color/durian.py (working copy)
@@ -0,0 +1,5 @@
+import bpy
+node = bpy.context.active_node
+
+node.color = (0.9651851654052734, 0.8261134028434753, 0.24349087476730347)
+node.use_custom_color = True
Index: release/scripts/presets/node_color/mango.py
new file mode 100644
===================================================================
--- /dev/null (revision 45822)
+++ release/scripts/presets/node_color/mango.py (working copy)
@@ -0,0 +1,5 @@
+import bpy
+node = bpy.context.active_node
+
+node.color = (0.9651851654052734, 0.6220577359199524, 0.039014507085084915)
+node.use_custom_color = True
Index: release/scripts/presets/node_color/orange.py
new file mode 100644
===================================================================
--- release/scripts/presets/node_color/orange.py (revision 0)
+++ release/scripts/presets/node_color/orange.py (revision 0)
@@ -0,0 +1,5 @@
+import bpy
+node = bpy.context.active_node
+
+node.color = (1.0, 0.46723097562789917, 0.0)
+node.use_custom_color = True
Index: release/scripts/presets/node_color/peach.py
new file mode 100644
===================================================================
--- /dev/null (revision 45822)
+++ release/scripts/presets/node_color/peach.py (working copy)
@@ -0,0 +1,5 @@
+import bpy
+node = bpy.context.active_node
+
+node.color = (1.0, 0.39570868015289307, 0.11025595664978027)
+node.use_custom_color = True
Index: release/scripts/startup/bl_operators/presets.py
===================================================================
--- release/scripts/startup/bl_operators/presets.py (revision 45822)
+++ release/scripts/startup/bl_operators/presets.py (working copy)
@@ -436,6 +436,24 @@
preset_subdir = "tracking_settings"


+class AddPresetNodeColor(AddPresetBase, Operator):
+ '''Add a Node Color Preset'''
+ bl_idname = "node.node_color_preset_add"
+ bl_label = "Add Node Color Preset"
+ preset_menu = "NODE_MT_node_color_presets"
+
+ preset_defines = [
+ "node = bpy.context.active_node"
+ ]
+
+ preset_values = [
+ "node.color",
+ "node.use_custom_color"
+ ]
+
+ preset_subdir = "node_color"
+
+
class AddPresetInterfaceTheme(AddPresetBase, Operator):
'''Add a theme preset'''
bl_idname = "wm.interface_theme_preset_add"
Index: release/scripts/startup/bl_ui/space_node.py
===================================================================
--- release/scripts/startup/bl_ui/space_node.py (revision 45822)
+++ release/scripts/startup/bl_ui/space_node.py (working copy)
@@ -205,5 +205,23 @@
col.prop(snode, "backdrop_y", text="Y")
col.operator("node.backimage_move", text="Move")

+
+class NODE_MT_node_color_presets(Menu):
+ """Predefined node color"""
+ bl_label = "Color Presets"
+ preset_subdir = "node_color"
+ preset_operator = "script.execute_preset"
+ draw = Menu.draw_preset
+
+
+class NODE_MT_node_color_specials(Menu):
+ bl_label = "Node Color Specials"
+
+ def draw(self, context):
+ layout = self.layout
+
+ layout.operator('node.node_copy_color', icon='COPY_ID')
+
+
if __name__ == "__main__": # only for live edit.
bpy.utils.register_module(__name__)
Index: source/blender/blenkernel/BKE_node.h
===================================================================
--- source/blender/blenkernel/BKE_node.h (revision 45822)
+++ source/blender/blenkernel/BKE_node.h (working copy)
@@ -241,6 +241,12 @@
#define NODE_OLD_SHADING 1
#define NODE_NEW_SHADING 2

+/* node resize directions */
+#define NODE_RESIZE_TOP 1
+#define NODE_RESIZE_BOTTOM 2
+#define NODE_RESIZE_RIGHT 4
+#define NODE_RESIZE_LEFT 8
+
/* enum values for input/output */
#define SOCK_IN 1
#define SOCK_OUT 2
@@ -341,7 +347,8 @@
void nodeRemSocketLinks(struct bNodeTree *ntree, struct bNodeSocket *sock);
void nodeInternalRelink(struct bNodeTree *ntree, struct bNode *node);

-void nodeSpaceCoords(struct bNode *node, float *locx, float *locy);
+void nodeToView(struct bNode *node, float x, float y, float *rx, float *ry);
+void nodeFromView(struct bNode *node, float x, float y, float *rx, float *ry);
void nodeAttachNode(struct bNode *node, struct bNode *parent);
void nodeDetachNode(struct bNode *node);

Index: source/blender/blenkernel/intern/node.c
===================================================================
--- source/blender/blenkernel/intern/node.c (revision 45822)
+++ source/blender/blenkernel/intern/node.c (working copy)
@@ -591,40 +591,49 @@
BLI_freelistN(&intlinks);
}

-/* transforms node location to area coords */
-void nodeSpaceCoords(bNode *node, float *locx, float *locy)
+void nodeToView(bNode *node, float x, float y, float *rx, float *ry)
{
if (node->parent) {
- nodeSpaceCoords(node->parent, locx, locy);
- *locx += node->locx;
- *locy += node->locy;
+ nodeToView(node->parent, x + node->locx, y + node->locy, rx, ry);
}
else {
- *locx = node->locx;
- *locy = node->locy;
+ *rx = x + node->locx;
+ *ry = y + node->locy;
+ }
+}
+
+void nodeFromView(bNode *node, float x, float y, float *rx, float *ry)
+{
+ if (node->parent) {
+ nodeFromView(node->parent, x, y, rx, ry);
+ *rx -= node->locx;
+ *ry -= node->locy;
+ }
+ else {
+ *rx = x - node->locx;
+ *ry = y - node->locy;
}
}

void nodeAttachNode(bNode *node, bNode *parent)
{
- float parentx, parenty;
+ float locx, locy;
+ nodeToView(node, 0.0f, 0.0f, &locx, &locy);

node->parent = parent;
/* transform to parent space */
- nodeSpaceCoords(parent, &parentx, &parenty);
- node->locx -= parentx;
- node->locy -= parenty;
+ nodeFromView(parent, locx, locy, &node->locx, &node->locy);
}

void nodeDetachNode(struct bNode *node)
{
- float parentx, parenty;
+ float locx, locy;

if (node->parent) {
- /* transform to "global" (area) space */
- nodeSpaceCoords(node->parent, &parentx, &parenty);
- node->locx += parentx;
- node->locy += parenty;
+ /* transform to view space */
+ nodeToView(node, 0.0f, 0.0f, &locx, &locy);
+ node->locx = locx;
+ node->locy = locy;
node->parent = NULL;
}
}
Index: source/blender/editors/include/ED_node.h
===================================================================
--- source/blender/editors/include/ED_node.h (revision 45822)
+++ source/blender/editors/include/ED_node.h (working copy)
@@ -49,6 +49,7 @@
void ED_node_tree_update(struct SpaceNode *snode, struct Scene *scene);
void ED_node_changed_update(struct ID *id, struct bNode *node);
void ED_node_generic_update(struct Main *bmain, struct bNodeTree *ntree, struct bNode *node);
+void ED_node_sort(struct bNodeTree *ntree);

/* node_edit.c */
void ED_node_shader_default(struct Scene *scene, struct ID *id);
@@ -57,7 +58,7 @@
void ED_node_link_intersect_test(struct ScrArea *sa, int test);
void ED_node_link_insert(struct ScrArea *sa);

-void ED_node_update_hierarchy(struct bContext *C, struct bNodeTree *ntree);
+void ED_node_post_apply_transform(struct bContext *C, struct bNodeTree *ntree);

void ED_node_set_active(struct Main *bmain, struct bNodeTree *ntree, struct bNode *node);
/* node ops.c */
Index: source/blender/editors/interface/interface_draw.c
===================================================================
--- source/blender/editors/interface/interface_draw.c (revision 45822)
+++ source/blender/editors/interface/interface_draw.c (working copy)
@@ -1638,12 +1638,12 @@
}


-void ui_dropshadow(rctf *rct, float radius, float aspect, int UNUSED(select))
+void ui_dropshadow(rctf *rct, float radius, float aspect, int UNUSED(select), float alpha)
{
int i;
float rad;
float a;
- char alpha = 2;
+ float dalpha = alpha * (2.0f / 255.0f), curalpha=0.0f;

glEnable(GL_BLEND);

@@ -1664,9 +1664,9 @@
}

for (; i--; a -= aspect) {
+ curalpha += dalpha;
/* alpha ranges from 2 to 20 or so */
- glColor4ub(0, 0, 0, alpha);
- alpha += 2;
+ glColor4f(0.0f, 0.0f, 0.0f, curalpha);

uiDrawBox(GL_POLYGON, rct->xmin - a, rct->ymin - a, rct->xmax + a, rct->ymax - 10.0f + a, rad + a);
}
Index: source/blender/editors/interface/interface_intern.h
===================================================================
--- source/blender/editors/interface/interface_intern.h (revision 45822)
+++ source/blender/editors/interface/interface_intern.h (working copy)
@@ -444,7 +444,7 @@
extern void ui_draw_aligned_panel(struct uiStyle *style, uiBlock *block, rcti *rect);

/* interface_draw.c */
-extern void ui_dropshadow(rctf *rct, float radius, float aspect, int select);
+extern void ui_dropshadow(rctf *rct, float radius, float aspect, int select, float alpha);

void ui_draw_gradient(rcti *rect, const float hsv[3], int type, float alpha);

Index: source/blender/editors/interface/resources.c
===================================================================
--- source/blender/editors/interface/resources.c (revision 45822)
+++ source/blender/editors/interface/resources.c (working copy)
@@ -837,7 +837,7 @@

/* space node, re-uses syntax color storage */
btheme->tnode = btheme->tv3d;
- rgba_char_args_set(btheme->tnode.edge_select, 255, 255, 255, 255);
+ rgba_char_args_set(btheme->tnode.edge_select, 255, 255, 255, 255); /* wire selected */
rgba_char_args_set(btheme->tnode.syntaxl, 155, 155, 155, 160); /* TH_NODE, backdrop */
rgba_char_args_set(btheme->tnode.syntaxn, 100, 100, 100, 255); /* in/output */
rgba_char_args_set(btheme->tnode.syntaxb, 108, 105, 111, 255); /* operator */
Index: source/blender/editors/space_node/drawnode.c
===================================================================
--- source/blender/editors/space_node/drawnode.c (revision 45822)
+++ source/blender/editors/space_node/drawnode.c (working copy)
@@ -79,6 +79,9 @@

#include "node_intern.h"

+// XXX interface.h
+extern void ui_dropshadow(rctf *rct, float radius, float aspect, int select, float alpha);
+
/* ****************** SOCKET BUTTON DRAW FUNCTIONS ***************** */

static void node_sync_cb(bContext *UNUSED(C), void *snode_v, void *node_v)
@@ -513,15 +516,21 @@
rctf totr= node->totr;
/* right part of node */
totr.xmin = node->totr.xmax-20.0f;
- return BLI_in_rctf(&totr, x, y);
+ if (BLI_in_rctf(&totr, x, y))
+ return NODE_RESIZE_RIGHT;
+ else
+ return 0;
}
else {
- /* rect we're interested in is just the bottom right corner */
+ const float size = 10.0f;
rctf totr= node->totr;
- /* bottom right corner */
- totr.xmin = totr.xmax-10.0f;
- totr.ymax = totr.ymin+10.0f;
- return BLI_in_rctf(&totr, x, y);
+ int dir = 0;
+
+ if (x >= totr.xmax-size && x < totr.xmax && y >= totr.ymin && y < totr.ymax)
+ dir |= NODE_RESIZE_RIGHT;
+ if (x >= totr.xmin && x < totr.xmin+size && y >= totr.ymin && y < totr.ymax)
+ dir |= NODE_RESIZE_LEFT;
+ return dir;
}
}

@@ -550,7 +559,7 @@
int dy;

/* get "global" coords */
- nodeSpaceCoords(gnode, &locx, &locy);
+ nodeToView(gnode, 0.0f, 0.0f, &locx, &locy);

/* center them, is a bit of abuse of locx and locy though */
node_update_nodetree(C, ngroup, locx, locy);
@@ -945,20 +954,157 @@
uiItemR(layout, ptr, "max_iterations", 0, NULL, 0);
}

-static void node_update_frame(const bContext *UNUSED(C), bNodeTree *UNUSED(ntree), bNode *node)
+/* XXX Does a bounding box update by iterating over all children.
+ * Not ideal to do this in every draw call, but doing as transform callback doesn't work,
+ * since the child node totr rects are not updated properly at that point.
+ */
+static void node_update_frame(const bContext *UNUSED(C), bNodeTree *ntree, bNode *node)
+{
+ const float margin = 30.0f;
+ int bbinit;
+ bNode *tnode;
+ rctf rect, noderect;
+ float xmax, ymax;
+
+ /* init rect from current frame size */
+ nodeToView(node, node->offsetx, node->offsety, &rect.xmin, &rect.ymax);
+ nodeToView(node, node->offsetx+node->width, node->offsety-node->height, &rect.xmax, &rect.ymin);
+
+ /* frame can be resized manually only if shrinking is disabled or no children are attached */
+ node->custom1 |= NODE_FRAME_RESIZEABLE;
+ /* for shrinking bbox, initialize the rect from first child node */
+ bbinit = (node->custom1 & NODE_FRAME_SHRINK);
+ /* fit bounding box to all children */
+ for (tnode=ntree->nodes.first; tnode; tnode=tnode->next) {
+ if (tnode->parent!=node)
+ continue;
+
+ /* add margin to node rect */
+ noderect = tnode->totr;
+ noderect.xmin -= margin;
+ noderect.xmax += margin;
+ noderect.ymin -= margin;
+ noderect.ymax += margin;
+
+ /* first child initializes frame */
+ if (bbinit) {
+ bbinit = 0;
+ rect = noderect;
+ node->custom1 &= ~NODE_FRAME_RESIZEABLE;
+ }
+ else
+ BLI_union_rctf(&rect, &noderect);
+ }
+
+ /* now adjust the frame size from view-space bounding box */
+ nodeFromView(node, rect.xmin, rect.ymax, &node->offsetx, &node->offsety);
+ nodeFromView(node, rect.xmax, rect.ymin, &xmax, &ymax);
+ node->width = xmax - node->offsetx;
+ node->height = -ymax + node->offsety;
+
+ node->totr = rect;
+}
+
+static void node_draw_frame(const bContext *C, ARegion *ar, SpaceNode *snode, bNodeTree *UNUSED(ntree), bNode *node)
{
- float locx, locy;
+ rctf *rct= &node->totr;
+ int color_id= node_get_colorid(node);
+ char showname[128]; /* 128 used below */
+ float shadowalpha;
+
+ /* skip if out of view */
+ if (node->totr.xmax < ar->v2d.cur.xmin || node->totr.xmin > ar->v2d.cur.xmax ||
+ node->totr.ymax < ar->v2d.cur.ymin || node->totr.ymin > ar->v2d.cur.ymax) {
+
+ uiEndBlock(C, node->block);
+ node->block= NULL;
+ return;
+ }
+
+ uiSetRoundBox(UI_CNR_ALL);
+ if (node->flag & NODE_CUSTOM_COLOR)
+ shadowalpha = node->color[3];
+ else
+ shadowalpha = 1.0f;
+ ui_dropshadow(rct, BASIS_RAD, snode->aspect, node->flag & SELECT, shadowalpha);
+
+ /* header */
+ if (color_id==TH_NODE)
+ UI_ThemeColorShade(color_id, -20);
+ else
+ UI_ThemeColor(color_id);
+
+ uiSetRoundBox(UI_CNR_TOP_LEFT | UI_CNR_TOP_RIGHT);
+ uiRoundBox(rct->xmin, rct->ymax-NODE_DY, rct->xmax, rct->ymax, BASIS_RAD);
+
+ /* title */
+ if (node->flag & SELECT)
+ UI_ThemeColor(TH_SELECT);
+ else
+ UI_ThemeColorBlendShade(TH_TEXT, color_id, 0.4f, 10);
+
+ BLI_strncpy(showname, nodeLabel(node), sizeof(showname));
+ uiDefBut(node->block, LABEL, 0, showname, (short)(rct->xmin+15), (short)(rct->ymax-NODE_DY),
+ (int)(rct->xmax-rct->xmin-25.0f), NODE_DY, NULL, 0, 0, 0, 0, "");
+
+ /* body */
+ if (node->flag & NODE_CUSTOM_COLOR)
+ glColor4fv(node->color);
+ else
+ UI_ThemeColor4(TH_NODE);
+ glEnable(GL_BLEND);
+ uiSetRoundBox(UI_CNR_BOTTOM_LEFT | UI_CNR_BOTTOM_RIGHT);
+ uiRoundBox(rct->xmin, rct->ymin, rct->xmax, rct->ymax-NODE_DY, BASIS_RAD);
+ glDisable(GL_BLEND);

- /* get "global" coords */
- nodeSpaceCoords(node, &locx, &locy);
+ /* outline active and selected emphasis */
+ if ( node->flag & (NODE_ACTIVE|SELECT) ) {
+ glEnable(GL_BLEND);
+ glEnable( GL_LINE_SMOOTH );
+
+ if (node->flag & NODE_ACTIVE)
+ UI_ThemeColorShadeAlpha(TH_ACTIVE, 0, -40);
+ else
+ UI_ThemeColorShadeAlpha(TH_SELECT, 0, -40);
+ uiSetRoundBox(UI_CNR_ALL);
+ uiDrawBox(GL_LINE_LOOP, rct->xmin, rct->ymin, rct->xmax, rct->ymax, BASIS_RAD);
+
+ glDisable( GL_LINE_SMOOTH );
+ glDisable(GL_BLEND);
+ }
+
+ UI_ThemeClearColor(color_id);
+
+ uiEndBlock(C, node->block);
+ uiDrawBlock(C, node->block);
+ node->block= NULL;
+}

- node->prvr.xmin = locx + NODE_DYS;
- node->prvr.xmax = locx + node->width- NODE_DYS;
+static int node_resize_area_frame(bNode *node, int x, int y)
+{
+ const float size = 10.0f;
+ rctf totr= node->totr;
+ int dir = 0;
+
+ /* shrinking frame size is determined by child nodes */
+ if (!(node->custom1 & NODE_FRAME_RESIZEABLE))
+ return 0;
+
+ if (x >= totr.xmax-size && x < totr.xmax && y >= totr.ymin && y < totr.ymax)
+ dir |= NODE_RESIZE_RIGHT;
+ if (x >= totr.xmin && x < totr.xmin+size && y >= totr.ymin && y < totr.ymax)
+ dir |= NODE_RESIZE_LEFT;
+ if (x >= totr.xmin && x < totr.xmax && y >= totr.ymax-size && y < totr.ymax)
+ dir |= NODE_RESIZE_TOP;
+ if (x >= totr.xmin && x < totr.xmax && y >= totr.ymin && y < totr.ymin+size)
+ dir |= NODE_RESIZE_BOTTOM;
+
+ return dir;
+}

- node->totr.xmin = locx;
- node->totr.xmax = locx + node->width;
- node->totr.ymax = locy;
- node->totr.ymin = locy - node->height;
+static void node_buts_frame_details(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
+{
+ uiItemR(layout, ptr, "shrink", 0, "Shrink", ICON_NONE);
}

static void node_common_set_butfunc(bNodeType *ntype)
@@ -980,7 +1126,10 @@
ntype->drawupdatefunc= node_update_group;
break;
case NODE_FRAME:
+ ntype->drawfunc= node_draw_frame;
ntype->drawupdatefunc= node_update_frame;
+ ntype->uifuncbut= node_buts_frame_details;
+ ntype->resize_area_func= node_resize_area_frame;
break;
}
}
@@ -1175,7 +1324,6 @@
/* only once called */
static void node_shader_set_butfunc(bNodeType *ntype)
{
- ntype->uifuncbut = NULL;
switch(ntype->type) {
/* case NODE_GROUP: note, typeinfo for group is generated... see "XXX ugly hack" */

@@ -1254,7 +1402,6 @@
ntype->uifunc= node_shader_buts_dynamic;
break;
}
- if (ntype->uifuncbut == NULL) ntype->uifuncbut = ntype->uifunc;
}

/* ****************** BUTTON CALLBACKS FOR COMPOSITE NODES ***************** */
@@ -1940,7 +2087,6 @@
/* only once called */
static void node_composit_set_butfunc(bNodeType *ntype)
{
- ntype->uifuncbut = NULL;
switch(ntype->type) {
/* case NODE_GROUP: note, typeinfo for group is generated... see "XXX ugly hack" */

@@ -2106,8 +2252,6 @@
default:
ntype->uifunc= NULL;
}
- if (ntype->uifuncbut == NULL) ntype->uifuncbut = ntype->uifunc;
-
}

/* ****************** BUTTON CALLBACKS FOR TEXTURE NODES ***************** */
@@ -2218,7 +2362,6 @@
/* only once called */
static void node_texture_set_butfunc(bNodeType *ntype)
{
- ntype->uifuncbut = NULL;
if ( ntype->type >= TEX_NODE_PROC && ntype->type < TEX_NODE_PROC_MAX ) {
ntype->uifunc = node_texture_buts_proc;
}
@@ -2260,7 +2403,6 @@
ntype->uifunc = node_texture_buts_output;
break;
}
- if (ntype->uifuncbut == NULL) ntype->uifuncbut = ntype->uifunc;
}

/* ******* init draw callbacks for all tree types, only called in usiblender.c, once ************* */
Index: source/blender/editors/space_node/node_buttons.c
===================================================================
--- source/blender/editors/space_node/node_buttons.c (revision 45822)
+++ source/blender/editors/space_node/node_buttons.c (working copy)
@@ -89,8 +89,8 @@
SpaceNode *snode= CTX_wm_space_node(C);
bNodeTree *ntree= (snode) ? snode->edittree : NULL;
bNode *node = (ntree) ? nodeGetActive(ntree) : NULL; // xxx... for editing group nodes
- uiLayout *layout= pa->layout;
- PointerRNA ptr;
+ uiLayout *layout= pa->layout, *row, *col;
+ PointerRNA ptr, opptr;

/* verify pointers, and create RNA pointer for the node */
if (ELEM(NULL, ntree, node))
@@ -109,6 +109,20 @@
uiItemO(layout, NULL, 0, "NODE_OT_hide_socket_toggle");
uiItemS(layout);

+ row = uiLayoutRow(layout, 0);
+ uiItemM(row, (bContext *)C, "NODE_MT_node_color_presets", NULL, 0);
+ uiItemM(row, (bContext *)C, "NODE_MT_node_color_specials", "", ICON_DOWNARROW_HLT);
+ uiItemO(row, "", ICON_ZOOMIN, "node.node_color_preset_add");
+ opptr = uiItemFullO(row, "node.node_color_preset_add", "", ICON_ZOOMOUT, NULL, WM_OP_INVOKE_DEFAULT, UI_ITEM_O_RETURN_PROPS);
+ RNA_boolean_set(&opptr, "remove_active", 1);
+
+ row = uiLayoutRow(layout, 0);
+ uiItemR(row, &ptr, "use_custom_color", UI_ITEM_R_ICON_ONLY, NULL, ICON_NONE);
+ col = uiLayoutColumn(row, 0);
+ if (!(node->flag & NODE_CUSTOM_COLOR))
+ uiLayoutSetEnabled(col, 0);
+ uiItemR(col, &ptr, "color", 0, "", 0);
+
/* draw this node's settings */
if (node->typeinfo && node->typeinfo->uifuncbut)
node->typeinfo->uifuncbut(layout, (bContext *)C, &ptr);
Index: source/blender/editors/space_node/node_draw.c
===================================================================
--- source/blender/editors/space_node/node_draw.c (revision 45822)
+++ source/blender/editors/space_node/node_draw.c (working copy)
@@ -83,7 +83,7 @@
#define NODE_GROUP_FRAME 120

// XXX interface.h
-extern void ui_dropshadow(rctf *rct, float radius, float aspect, int select);
+extern void ui_dropshadow(rctf *rct, float radius, float aspect, int select, float alpha);

/* XXX update functions for node editor are a mess, needs a clear concept */
void ED_node_tree_update(SpaceNode *snode, Scene *scene)
@@ -167,6 +167,110 @@
ntreeTexCheckCyclics(ntree);
}

+static int compare_nodes(bNode *a, bNode *b)
+{
+ bNode *parent;
+ /* These tell if either the node or any of the parent nodes is selected.
+ * A selected parent means an unselected node is also in foreground!
+ */
+ int a_select=(a->flag & NODE_SELECT), b_select=(b->flag & NODE_SELECT);
+ int a_active=(a->flag & NODE_ACTIVE), b_active=(b->flag & NODE_ACTIVE);
+
+ /* if one is an ancestor of the other */
+ /* XXX there might be a better sorting algorithm for stable topological sort, this is O(n^2) worst case */
+ for (parent = a->parent; parent; parent=parent->parent) {
+ /* if b is an ancestor, it is always behind a */
+ if (parent==b)
+ return 1;
+ /* any selected ancestor moves the node forward */
+ if (parent->flag & NODE_ACTIVE)
+ a_active = 1;
+ if (parent->flag & NODE_SELECT)
+ a_select = 1;
+ }
+ for (parent = b->parent; parent; parent=parent->parent) {
+ /* if a is an ancestor, it is always behind b */
+ if (parent==a)
+ return 0;
+ /* any selected ancestor moves the node forward */
+ if (parent->flag & NODE_ACTIVE)
+ b_active = 1;
+ if (parent->flag & NODE_SELECT)
+ b_select = 1;
+ }
+
+ /* if one of the nodes is in the background and the other not */
+ if ((a->flag & NODE_BACKGROUND) && !(b->flag & NODE_BACKGROUND))
+ return 0;
+ else if (!(a->flag & NODE_BACKGROUND) && (b->flag & NODE_BACKGROUND))
+ return 1;
+
+ /* if one has a higher selection state (active > selected > nothing) */
+ if (!b_active && a_active)
+ return 1;
+ else if (!b_select && (a_active || a_select))
+ return 1;
+
+ return 0;
+}
+
+/* Sorts nodes by selection: unselected nodes first, then selected,
+ * then the active node at the very end. Relative order is kept intact!
+ */
+void ED_node_sort(bNodeTree *ntree)
+{
+ /* merge sort is the algorithm of choice here */
+ bNode *first_a, *first_b, *node_a, *node_b, *tmp;
+ int totnodes= BLI_countlist(&ntree->nodes);
+ int k, a, b;
+
+ k = 1;
+ while (k < totnodes) {
+ first_a = first_b = ntree->nodes.first;
+
+ do {
+ /* setup first_b pointer */
+ for (b=0; b < k && first_b; ++b) {
+ first_b = first_b->next;
+ }
+ /* all batches merged? */
+ if (first_b==NULL)
+ break;
+
+ /* merge batches */
+ node_a = first_a;
+ node_b = first_b;
+ a = b = 0;
+ while (a < k && b < k && node_b) {
+ if (compare_nodes(node_a, node_b)==0) {
+ node_a = node_a->next;
+ ++a;
+ }
+ else {
+ tmp = node_b;
+ node_b = node_b->next;
+ ++b;
+ BLI_remlink(&ntree->nodes, tmp);
+ BLI_insertlinkbefore(&ntree->nodes, node_a, tmp);
+ }
+ }
+
+ /* setup first pointers for next batch */
+ first_b = node_b;
+ for (; b < k; ++b) {
+ /* all nodes sorted? */
+ if (first_b==NULL)
+ break;
+ first_b = first_b->next;
+ }
+ first_a = first_b;
+ } while (first_b);
+
+ k = k << 1;
+ }
+}
+
+
static void do_node_internal_buttons(bContext *C, void *node_v, int event)
{
if (event==B_NODE_EXEC) {
@@ -176,24 +280,6 @@
}
}

-
-static void node_scaling_widget(int color_id, float aspect, float xmin, float ymin, float xmax, float ymax)
-{
- float dx;
- float dy;
-
- dx= 0.5f*(xmax-xmin);
- dy= 0.5f*(ymax-ymin);
-
- UI_ThemeColorShade(color_id, +30);
- fdrawline(xmin, ymin, xmax, ymax);
- fdrawline(xmin+dx, ymin, xmax, ymax-dy);
-
- UI_ThemeColorShade(color_id, -10);
- fdrawline(xmin, ymin+aspect, xmax, ymax+aspect);
- fdrawline(xmin+dx, ymin+aspect, xmax, ymax-dy+aspect);
-}
-
static void node_uiblocks_init(const bContext *C, bNodeTree *ntree)
{
bNode *node;
@@ -223,7 +309,7 @@
int buty;

/* get "global" coords */
- nodeSpaceCoords(node, &locx, &locy);
+ nodeToView(node, 0.0f, 0.0f, &locx, &locy);
dy= locy;

/* header */
@@ -349,7 +435,7 @@
int totin=0, totout=0, tot;

/* get "global" coords */
- nodeSpaceCoords(node, &locx, &locy);
+ nodeToView(node, 0.0f, 0.0f, &locx, &locy);

/* calculate minimal radius */
for (nsock= node->inputs.first; nsock; nsock= nsock->next)
@@ -409,7 +495,7 @@
node_update_basis(C, ntree, node);
}

-static int node_get_colorid(bNode *node)
+int node_get_colorid(bNode *node)
{
if (node->typeinfo->nclass==NODE_CLASS_INPUT)
return TH_NODE_IN_OUT;
@@ -582,6 +668,7 @@
int color_id= node_get_colorid(node);
char showname[128]; /* 128 used below */
View2D *v2d = &ar->v2d;
+ float shadowalpha;

/* hurmf... another candidate for callback, have to see how this works first */
if (node->id && node->block && snode->treetype==NTREE_SHADER)
@@ -596,8 +683,12 @@
return;
}

- uiSetRoundBox(UI_CNR_TOP_LEFT | UI_CNR_TOP_RIGHT | UI_CNR_BOTTOM_LEFT);
- ui_dropshadow(rct, BASIS_RAD, snode->aspect, node->flag & SELECT);
+ uiSetRoundBox(UI_CNR_ALL);
+ if (node->flag & NODE_CUSTOM_COLOR)
+ shadowalpha = node->color[3];
+ else
+ shadowalpha = 1.0f;
+ ui_dropshadow(rct, BASIS_RAD, snode->aspect, node->flag & SELECT, shadowalpha);

/* header */
if (color_id==TH_NODE)
@@ -643,7 +734,7 @@

/* title */
if (node->flag & SELECT)
- UI_ThemeColor(TH_TEXT_HI);
+ UI_ThemeColor(TH_SELECT);
else
UI_ThemeColorBlendShade(TH_TEXT, color_id, 0.4f, 10);

@@ -679,27 +770,27 @@
(int)(iconofs - rct->xmin-18.0f), NODE_DY, NULL, 0, 0, 0, 0, "");

/* body */
- UI_ThemeColor4(TH_NODE);
+ if (node->flag & NODE_CUSTOM_COLOR)
+ glColor4fv(node->color);
+ else
+ UI_ThemeColor4(TH_NODE);
glEnable(GL_BLEND);
- uiSetRoundBox(UI_CNR_BOTTOM_LEFT);
+ uiSetRoundBox(UI_CNR_BOTTOM_LEFT | UI_CNR_BOTTOM_RIGHT);
uiRoundBox(rct->xmin, rct->ymin, rct->xmax, rct->ymax-NODE_DY, BASIS_RAD);
glDisable(GL_BLEND);

- /* scaling indicator */
- node_scaling_widget(TH_NODE, snode->aspect, rct->xmax-BASIS_RAD*snode->aspect, rct->ymin, rct->xmax, rct->ymin+BASIS_RAD*snode->aspect);
-
/* outline active and selected emphasis */
if ( node->flag & (NODE_ACTIVE|SELECT) ) {
glEnable(GL_BLEND);
glEnable( GL_LINE_SMOOTH );
- /* using different shades of TH_TEXT_HI for the empasis, like triangle */
- if ( node->flag & NODE_ACTIVE )
- UI_ThemeColorShadeAlpha(TH_TEXT_HI, 0, -40);
- else
- UI_ThemeColorShadeAlpha(TH_TEXT_HI, -20, -120);
- uiSetRoundBox(UI_CNR_TOP_LEFT | UI_CNR_TOP_RIGHT | UI_CNR_BOTTOM_LEFT); // round all corners except lower right
- uiDrawBox(GL_LINE_LOOP, rct->xmin, rct->ymin, rct->xmax, rct->ymax, BASIS_RAD);
-
+
+ if (node->flag & NODE_ACTIVE)
+ UI_ThemeColorShadeAlpha(TH_ACTIVE, 0, -40);
+ else
+ UI_ThemeColorShadeAlpha(TH_SELECT, 0, -40);
+ uiSetRoundBox(UI_CNR_ALL); // round all corners except lower right
+ uiDrawBox(GL_LINE_LOOP, rct->xmin, rct->ymin, rct->xmax, rct->ymax, BASIS_RAD);
+
glDisable( GL_LINE_SMOOTH );
glDisable(GL_BLEND);
}
@@ -772,10 +863,15 @@
float socket_size= NODE_SOCKSIZE*U.dpi/72;
int color_id= node_get_colorid(node);
char showname[128]; /* 128 is used below */
+ float shadowalpha;

/* shadow */
uiSetRoundBox(UI_CNR_ALL);
- ui_dropshadow(rct, hiddenrad, snode->aspect, node->flag & SELECT);
+ if (node->flag & NODE_CUSTOM_COLOR)
+ shadowalpha = node->color[3];
+ else
+ shadowalpha = 1.0f;
+ ui_dropshadow(rct, hiddenrad, snode->aspect, node->flag & SELECT, shadowalpha);

/* body */
UI_ThemeColor(color_id);
@@ -787,19 +883,20 @@
if ( node->flag & (NODE_ACTIVE|SELECT) ) {
glEnable(GL_BLEND);
glEnable( GL_LINE_SMOOTH );
- /* using different shades of TH_TEXT_HI for the empasis, like triangle */
- if ( node->flag & NODE_ACTIVE )
- UI_ThemeColorShadeAlpha(TH_TEXT_HI, 0, -40);
- else
- UI_ThemeColorShadeAlpha(TH_TEXT_HI, -20, -120);
- uiDrawBox(GL_LINE_LOOP, rct->xmin, rct->ymin, rct->xmax, rct->ymax, hiddenrad);
+
+ if (node->flag & NODE_ACTIVE)
+ UI_ThemeColorShadeAlpha(TH_ACTIVE, 0, -40);
+ else
+ UI_ThemeColorShadeAlpha(TH_SELECT, 0, -40);
+ uiDrawBox(GL_LINE_LOOP, rct->xmin, rct->ymin, rct->xmax, rct->ymax, hiddenrad);
+
glDisable( GL_LINE_SMOOTH );
glDisable(GL_BLEND);
}

/* title */
if (node->flag & SELECT)
- UI_ThemeColor(TH_TEXT_HI);
+ UI_ThemeColor(TH_SELECT);
else
UI_ThemeColorBlendShade(TH_TEXT, color_id, 0.4f, 10);

@@ -823,7 +920,7 @@
node_draw_mute_line(&ar->v2d, snode, node);

if (node->flag & SELECT)
- UI_ThemeColor(TH_TEXT_HI);
+ UI_ThemeColor(TH_SELECT);
else
UI_ThemeColor(TH_TEXT);

@@ -864,6 +961,69 @@
node->block= NULL;
}

+int node_get_resize_cursor(int directions)
+{
+ if (directions==0)
+ return CURSOR_STD;
+ else if ((directions & ~(NODE_RESIZE_TOP|NODE_RESIZE_BOTTOM))==0)
+ return BC_NS_SCROLLCURSOR;
+ else if ((directions & ~(NODE_RESIZE_RIGHT|NODE_RESIZE_LEFT))==0)
+ return BC_EW_SCROLLCURSOR;
+ else
+ return BC_NSEW_SCROLLCURSOR;
+}
+
+static void node_set_cursor(bContext *C)
+{
+ SpaceNode *snode = CTX_wm_space_node(C);
+ bNodeTree *ntree = snode->edittree;
+ bNode *node;
+ bNodeSocket *sock;
+ int cursor = CURSOR_STD;
+
+ if (!ntree)
+ return;
+
+ if (node_find_indicated_socket(snode, &node, &sock, SOCK_IN|SOCK_OUT)) {
+ /* pass */
+ }
+ else {
+ /* check nodes front to back */
+ for (node=ntree->nodes.last; node; node=node->prev) {
+ if (BLI_in_rctf(&node->totr, snode->mx, snode->my))
+ break; /* first hit on node stops */
+ }
+ if (node) {
+ int dir = node->typeinfo->resize_area_func(node, snode->mx, snode->my);
+ cursor = node_get_resize_cursor(dir);
+ }
+ }
+
+ WM_cursor_set(CTX_wm_window(C), cursor);
+}
+
+int node_ui_handler(bContext *C, wmEvent *event, void *UNUSED(userdata))
+{
+ SpaceNode *snode = CTX_wm_space_node(C);
+ ARegion *ar = CTX_wm_region(C);
+ int retval = WM_UI_HANDLER_CONTINUE;
+
+ if (!ar || (ar->regiontype != RGN_TYPE_WINDOW))
+ return retval;
+
+ if (event->type == MOUSEMOVE) {
+ /* convert mouse coordinates to v2d space */
+ UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &snode->mx, &snode->my);
+ node_set_cursor(C);
+ }
+
+ return retval;
+}
+
+void node_ui_handler_remove(bContext *UNUSED(C), void *UNUSED(userdata))
+{
+}
+
void node_draw_default(const bContext *C, ARegion *ar, SpaceNode *snode, bNodeTree *ntree, bNode *node)
{
if (node->flag & NODE_HIDDEN)
@@ -882,7 +1042,8 @@
{
bNode *node;

- for (node= ntree->nodes.first; node; node= node->next) {
+ /* update nodes front to back, so children sizes get updated before parents */
+ for (node= ntree->nodes.last; node; node= node->prev) {
/* XXX little hack */
node->locx += offsetx;
node->locy += offsety;
@@ -908,6 +1069,14 @@

if (ntree==NULL) return; /* groups... */

+ /* draw background nodes, last nodes in front */
+ for (a=0, node= ntree->nodes.first; node; node=node->next, a++) {
+ if (!(node->flag & NODE_BACKGROUND))
+ continue;
+ node->nr= a; /* index of node in list, used for exec event code */
+ node_draw(C, ar, snode, ntree, node);
+ }
+
/* node lines */
glEnable(GL_BLEND);
glEnable(GL_LINE_SMOOTH);
@@ -916,8 +1085,10 @@
glDisable(GL_LINE_SMOOTH);
glDisable(GL_BLEND);

- /* draw nodes, last nodes in front */
+ /* draw foreground nodes, last nodes in front */
for (a=0, node= ntree->nodes.first; node; node=node->next, a++) {
+ if (node->flag & NODE_BACKGROUND)
+ continue;
node->nr= a; /* index of node in list, used for exec event code */
node_draw(C, ar, snode, ntree, node);
}
Index: source/blender/editors/space_node/node_edit.c
===================================================================
--- source/blender/editors/space_node/node_edit.c (revision 45822)
+++ source/blender/editors/space_node/node_edit.c (working copy)
@@ -693,53 +693,13 @@
}
}

-static int inside_rctf(rctf *bounds, rctf *rect)
+void ED_node_post_apply_transform(bContext *UNUSED(C), bNodeTree *UNUSED(ntree))
{
- return (bounds->xmin <= rect->xmin && bounds->xmax >= rect->xmax
- && bounds->ymin <= rect->ymin && bounds->ymax >= rect->ymax);
-}
-
-static void node_frame_attach_nodes(bNodeTree *UNUSED(ntree), bNode *frame)
-{
- bNode *node;
-
- /* only check nodes on top of the frame for attaching */
- for (node=frame->next; node; node=node->next) {
- if (node->parent==frame) {
- /* detach nodes that went outside the frame */
- if (!inside_rctf(&frame->totr, &node->totr))
- nodeDetachNode(node);
- }
- else if (node->flag & NODE_SELECT && node->parent==NULL) {
- /* attach selected, still unparented nodes */
- if (inside_rctf(&frame->totr, &node->totr))
- nodeAttachNode(node, frame);
- }
- }
-}
-
-void ED_node_update_hierarchy(bContext *UNUSED(C), bNodeTree *ntree)
-{
- bNode *node;
-
/* XXX This does not work due to layout functions relying on node->block,
* which only exists during actual drawing. Can we rely on valid totr rects?
*/
/* make sure nodes have correct bounding boxes after transform */
-// node_update_nodetree(C, ntree, 0.0f, 0.0f);
-
- /* all selected nodes are re-parented */
- for (node=ntree->nodes.last; node; node=node->prev) {
- if (node->flag & NODE_SELECT && node->parent)
- nodeDetachNode(node);
- }
-
- /* update higher Z-level nodes first */
- for (node=ntree->nodes.last; node; node=node->prev) {
- /* XXX callback? */
- if (node->type==NODE_FRAME)
- node_frame_attach_nodes(ntree, node);
- }
+ /* node_update_nodetree(C, ntree, 0.0f, 0.0f); */
}

/* ***************** generic operator functions for nodes ***************** */
@@ -1495,36 +1455,130 @@

typedef struct NodeSizeWidget {
float mxstart, mystart;
+ float oldlocx, oldlocy;
+ float oldoffsetx, oldoffsety;
float oldwidth, oldheight;
float oldminiwidth;
+ int directions;
} NodeSizeWidget;

+static void node_resize_init(bContext *C, wmOperator *op, wmEvent *UNUSED(event), bNode *node, int dir)
+{
+ SpaceNode *snode = CTX_wm_space_node(C);
+
+ NodeSizeWidget *nsw= MEM_callocN(sizeof(NodeSizeWidget), "size widget op data");
+
+ op->customdata= nsw;
+ nsw->mxstart= snode->mx;
+ nsw->mystart= snode->my;
+
+ /* store old */
+ nsw->oldlocx= node->locx;
+ nsw->oldlocy= node->locy;
+ nsw->oldoffsetx= node->offsetx;
+ nsw->oldoffsety= node->offsety;
+ nsw->oldwidth= node->width;
+ nsw->oldheight= node->height;
+ nsw->oldminiwidth= node->miniwidth;
+ nsw->directions = dir;
+
+ WM_cursor_modal(CTX_wm_window(C), node_get_resize_cursor(dir));
+ /* add modal handler */
+ WM_event_add_modal_handler(C, op);
+}
+
+static void node_resize_exit(bContext *C, wmOperator *op, int UNUSED(cancel))
+{
+ WM_cursor_restore(CTX_wm_window(C));
+
+ MEM_freeN(op->customdata);
+ op->customdata= NULL;
+}
+
static int node_resize_modal(bContext *C, wmOperator *op, wmEvent *event)
{
SpaceNode *snode= CTX_wm_space_node(C);
ARegion *ar= CTX_wm_region(C);
bNode *node= editnode_get_active(snode->edittree);
NodeSizeWidget *nsw= op->customdata;
- float mx, my;
+ float mx, my, dx, dy;

switch (event->type) {
case MOUSEMOVE:

- UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1],
- &mx, &my);
+ UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &mx, &my);
+ dx = mx - nsw->mxstart;
+ dy = my - nsw->mystart;

if (node) {
if (node->flag & NODE_HIDDEN) {
- node->miniwidth= nsw->oldminiwidth + mx - nsw->mxstart;
- CLAMP(node->miniwidth, 0.0f, 100.0f);
+ float widthmin = 0.0f;
+ float widthmax = 100.0f;
+ if (nsw->directions & NODE_RESIZE_RIGHT) {
+ node->miniwidth= nsw->oldminiwidth + dx;
+ CLAMP(node->miniwidth, widthmin, widthmax);
+ }
+ if (nsw->directions & NODE_RESIZE_LEFT) {
+ float locmax = nsw->oldlocx + nsw->oldminiwidth;
+
+ node->locx= nsw->oldlocx + dx;
+ CLAMP(node->locx, locmax - widthmax, locmax - widthmin);
+ node->miniwidth= locmax - node->locx;
+ }
}
else {
- node->width= nsw->oldwidth + mx - nsw->mxstart;
- CLAMP(node->width, UI_DPI_FAC*node->typeinfo->minwidth, UI_DPI_FAC*node->typeinfo->maxwidth);
+ float widthmin = UI_DPI_FAC*node->typeinfo->minwidth;
+ float widthmax = UI_DPI_FAC*node->typeinfo->maxwidth;
+ if (nsw->directions & NODE_RESIZE_RIGHT) {
+ node->width= nsw->oldwidth + dx;
+ CLAMP(node->width, widthmin, widthmax);
+ }
+ if (nsw->directions & NODE_RESIZE_LEFT) {
+ float locmax = nsw->oldlocx + nsw->oldwidth;
+
+ node->locx= nsw->oldlocx + dx;
+ CLAMP(node->locx, locmax - widthmax, locmax - widthmin);
+ node->width= locmax - node->locx;
+ }
}
+
/* height works the other way round ... */
- node->height= nsw->oldheight - my + nsw->mystart;
- CLAMP(node->height, node->typeinfo->minheight, node->typeinfo->maxheight);
+ {
+ float heightmin = UI_DPI_FAC*node->typeinfo->minheight;
+ float heightmax = UI_DPI_FAC*node->typeinfo->maxheight;
+ if (nsw->directions & NODE_RESIZE_TOP) {
+ float locmin = nsw->oldlocy - nsw->oldheight;
+
+ node->locy= nsw->oldlocy + dy;
+ CLAMP(node->locy, locmin + heightmin, locmin + heightmax);
+ node->height= node->locy - locmin;
+ }
+ if (nsw->directions & NODE_RESIZE_BOTTOM) {
+ node->height= nsw->oldheight - dy;
+ CLAMP(node->height, heightmin, heightmax);
+ }
+ }
+
+ /* XXX make callback? */
+ if (node->type == NODE_FRAME) {
+ /* keep the offset symmetric around center point */
+ if (nsw->directions & NODE_RESIZE_LEFT) {
+ node->locx = nsw->oldlocx + 0.5f*dx;
+ node->offsetx = nsw->oldoffsetx + 0.5f*dx;
+ }
+ if (nsw->directions & NODE_RESIZE_RIGHT) {
+ node->locx = nsw->oldlocx + 0.5f*dx;
+ node->offsetx = nsw->oldoffsetx - 0.5f*dx;
+ }
+ if (nsw->directions & NODE_RESIZE_TOP) {
+ node->locy = nsw->oldlocy + 0.5f*dy;
+ node->offsety = nsw->oldoffsety + 0.5f*dy;
+ }
+ if (nsw->directions & NODE_RESIZE_BOTTOM) {
+ node->locy = nsw->oldlocy + 0.5f*dy;
+ node->offsety = nsw->oldoffsety - 0.5f*dy;
+ }
+ }
}

ED_region_tag_redraw(ar);
@@ -1535,10 +1589,8 @@
case MIDDLEMOUSE:
case RIGHTMOUSE:

- MEM_freeN(nsw);
- op->customdata= NULL;
-
- ED_node_update_hierarchy(C, snode->edittree);
+ node_resize_exit(C, op, 0);
+ ED_node_post_apply_transform(C, snode->edittree);

return OPERATOR_FINISHED;
}
@@ -1551,37 +1603,24 @@
SpaceNode *snode= CTX_wm_space_node(C);
ARegion *ar= CTX_wm_region(C);
bNode *node= editnode_get_active(snode->edittree);
+ int dir;

if (node) {
/* convert mouse coordinates to v2d space */
UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1],
- &snode->mx, &snode->my);
-
- if (node->typeinfo->resize_area_func(node, snode->mx, snode->my)) {
- NodeSizeWidget *nsw= MEM_callocN(sizeof(NodeSizeWidget), "size widget op data");
-
- op->customdata= nsw;
- nsw->mxstart= snode->mx;
- nsw->mystart= snode->my;
-
- /* store old */
- nsw->oldwidth= node->width;
- nsw->oldheight= node->height;
- nsw->oldminiwidth= node->miniwidth;
-
- /* add modal handler */
- WM_event_add_modal_handler(C, op);
-
+ &snode->mx, &snode->my);
+ dir = node->typeinfo->resize_area_func(node, snode->mx, snode->my);
+ if (dir != 0) {
+ node_resize_init(C, op, event, node, dir);
return OPERATOR_RUNNING_MODAL;
}
}
return OPERATOR_CANCELLED|OPERATOR_PASS_THROUGH;
}

-static int node_resize_cancel(bContext *UNUSED(C), wmOperator *op)
+static int node_resize_cancel(bContext *C, wmOperator *op)
{
- MEM_freeN(op->customdata);
- op->customdata= NULL;
+ node_resize_exit(C, op, 1);

return OPERATOR_CANCELLED;
}
@@ -2155,6 +2194,27 @@

/* ****************** Duplicate *********************** */

+static void node_duplicate_reparent_recursive(bNode *node)
+{
+ bNode *parent;
+
+ node->flag |= NODE_TEST;
+
+ /* find first selected parent */
+ for (parent=node->parent; parent; parent=parent->parent) {
+ if (parent->flag & SELECT) {
+ if (!(parent->flag & NODE_TEST))
+ node_duplicate_reparent_recursive(parent);
+ break;
+ }
+ }
+ /* reparent node copy to parent copy */
+ if (parent) {
+ nodeDetachNode(node->new_node);
+ nodeAttachNode(node->new_node, parent->new_node);
+ }
+}
+
static int node_duplicate_exec(bContext *C, wmOperator *op)
{
SpaceNode *snode= CTX_wm_space_node(C);
@@ -2216,6 +2276,19 @@
break;
}

+ /* clear flags for recursive depth-first iteration */
+ for (node= ntree->nodes.first; node; node= node->next)
+ node->flag &= ~NODE_TEST;
+ /* reparent copied nodes */
+ for (node= ntree->nodes.first; node; node= node->next) {
+ if ((node->flag & SELECT) && !(node->flag & NODE_TEST))
+ node_duplicate_reparent_recursive(node);
+
+ /* only has to check old nodes */
+ if (node==lastnode)
+ break;
+ }
+
/* deselect old nodes, select the copies instead */
for (node= ntree->nodes.first; node; node= node->next) {
if (node->flag & SELECT) {
@@ -3650,3 +3723,218 @@
/* flags */
ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
}
+
+/* ****************** Copy Node Color ******************* */
+
+static int node_copy_color_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ SpaceNode *snode = CTX_wm_space_node(C);
+ bNodeTree *ntree = snode->edittree;
+ bNode *node, *tnode;
+
+ if (!ntree)
+ return OPERATOR_CANCELLED;
+ node = nodeGetActive(ntree);
+ if (!node)
+ return OPERATOR_CANCELLED;
+
+ for (tnode=ntree->nodes.first; tnode; tnode=tnode->next) {
+ if (tnode->flag & NODE_SELECT && tnode != node) {
+ if (node->flag & NODE_CUSTOM_COLOR) {
+ tnode->flag |= NODE_CUSTOM_COLOR;
+ copy_v3_v3(tnode->color, node->color);
+ }
+ else
+ tnode->flag &= ~NODE_CUSTOM_COLOR;
+ }
+ }
+
+ WM_event_add_notifier(C, NC_NODE|ND_DISPLAY, NULL);
+
+ return OPERATOR_FINISHED;
+}
+
+void NODE_OT_node_copy_color(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Copy Color";
+ ot->description = "Copy color to all selected nodes";
+ ot->idname = "NODE_OT_node_copy_color";
+
+ /* api callbacks */
+ ot->exec = node_copy_color_exec;
+ ot->poll = ED_operator_node_active;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
+}
+
+/* ****************** Set Parent ******************* */
+
+static int node_parent_set_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ SpaceNode *snode = CTX_wm_space_node(C);
+ bNodeTree *ntree = snode->edittree;
+ bNode *frame = nodeGetActive(ntree), *node;
+ if (!frame || frame->type != NODE_FRAME)
+ return OPERATOR_CANCELLED;
+
+ for (node=ntree->nodes.first; node; node=node->next) {
+ if (node == frame)
+ continue;
+ if (node->flag & NODE_SELECT) {
+ nodeDetachNode(node);
+ nodeAttachNode(node, frame);
+ }
+ }
+
+ ED_node_sort(ntree);
+ WM_event_add_notifier(C, NC_NODE|ND_DISPLAY, NULL);
+
+ return OPERATOR_FINISHED;
+}
+
+void NODE_OT_parent_set(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Make Parent";
+ ot->description = "Attach selected nodes";
+ ot->idname = "NODE_OT_parent_set";
+
+ /* api callbacks */
+ ot->exec = node_parent_set_exec;
+ ot->poll = ED_operator_node_active;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
+}
+
+/* ****************** Clear Parent ******************* */
+
+static int node_parent_clear_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ SpaceNode *snode = CTX_wm_space_node(C);
+ bNodeTree *ntree = snode->edittree;
+ bNode *node;
+
+ for (node=ntree->nodes.first; node; node=node->next) {
+ if (node->flag & NODE_SELECT) {
+ nodeDetachNode(node);
+ }
+ }
+
+ ED_node_sort(ntree);
+ WM_event_add_notifier(C, NC_NODE|ND_DISPLAY, NULL);
+
+ return OPERATOR_FINISHED;
+}
+
+void NODE_OT_parent_clear(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Clear Parent";
+ ot->description = "Detach selected nodes";
+ ot->idname = "NODE_OT_parent_clear";
+
+ /* api callbacks */
+ ot->exec = node_parent_clear_exec;
+ ot->poll = ED_operator_node_active;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
+}
+
+/* ****************** Join Nodes ******************* */
+
+/* tags for depth-first search */
+#define NODE_JOIN_DONE 1
+#define NODE_JOIN_IS_DESCENDANT 2
+
+static void node_join_attach_recursive(bNode *node, bNode *frame)
+{
+ node->done |= NODE_JOIN_DONE;
+
+ if (node == frame) {
+ node->done |= NODE_JOIN_IS_DESCENDANT;
+ }
+ else if (node->parent) {
+ /* call recursively */
+ if (!(node->parent->done & NODE_JOIN_DONE))
+ node_join_attach_recursive(node->parent, frame);
+
+ /* in any case: if the parent is a descendant, so is the child */
+ if (node->parent->done & NODE_JOIN_IS_DESCENDANT)
+ node->done |= NODE_JOIN_IS_DESCENDANT;
+
+ if (node->flag & NODE_TEST) {
+ /* if parent is not an decendant of the frame, reattach the node */
+ if (!(node->parent->done & NODE_JOIN_IS_DESCENDANT)) {
+ nodeDetachNode(node);
+ nodeAttachNode(node, frame);
+ node->done |= NODE_JOIN_IS_DESCENDANT;
+ }
+ }
+ }
+ else if (node->flag & NODE_TEST) {
+ nodeAttachNode(node, frame);
+ node->done |= NODE_JOIN_IS_DESCENDANT;
+ }
+}
+
+static int node_join_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ SpaceNode *snode = CTX_wm_space_node(C);
+ Main *bmain = CTX_data_main(C);
+ Scene *scene = CTX_data_scene(C);
+ bNodeTree *ntree = snode->edittree;
+ bNode *node, *frame;
+ bNodeTemplate ntemp;
+
+ /* XXX save selection: node_add_node call below sets the new frame as single active+selected node */
+ for (node=ntree->nodes.first; node; node=node->next) {
+ if (node->flag & NODE_SELECT)
+ node->flag |= NODE_TEST;
+ else
+ node->flag &= ~NODE_TEST;
+ }
+
+ ntemp.main = bmain;
+ ntemp.scene = scene;
+ ntemp.type = NODE_FRAME;
+ frame = node_add_node(snode, bmain, scene, &ntemp, 0.0f, 0.0f);
+
+ /* reset tags */
+ for (node=ntree->nodes.first; node; node=node->next)
+ node->done = 0;
+
+ for (node=ntree->nodes.first; node; node=node->next) {
+ if (!(node->done & NODE_JOIN_DONE))
+ node_join_attach_recursive(node, frame);
+ }
+
+ /* restore selection */
+ for (node=ntree->nodes.first; node; node=node->next) {
+ if (node->flag & NODE_TEST)
+ node->flag |= NODE_SELECT;
+ }
+
+ ED_node_sort(ntree);
+ WM_event_add_notifier(C, NC_NODE|ND_DISPLAY, NULL);
+
+ return OPERATOR_FINISHED;
+}
+
+void NODE_OT_join(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Join Nodes";
+ ot->description = "Attaches selected nodes to a new common frame";
+ ot->idname = "NODE_OT_join";
+
+ /* api callbacks */
+ ot->exec = node_join_exec;
+ ot->poll = ED_operator_node_active;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
+}
Index: source/blender/editors/space_node/node_intern.h
===================================================================
--- source/blender/editors/space_node/node_intern.h (revision 45822)
+++ source/blender/editors/space_node/node_intern.h (working copy)
@@ -41,6 +41,7 @@
struct View2D;
struct bContext;
struct wmWindowManager;
+struct wmEvent;
struct bNodeTemplate;
struct bNode;
struct bNodeSocket;
@@ -65,13 +66,18 @@
void node_menus_register(void);

/* node_draw.c */
+int node_get_colorid(struct bNode *node);
void node_socket_circle_draw(struct bNodeTree *ntree, struct bNodeSocket *sock, float size);
+int node_get_resize_cursor(int directions);
void node_draw_default(const struct bContext *C, struct ARegion *ar, struct SpaceNode *snode, struct bNodeTree *ntree, struct bNode *node);
void node_update_default(const struct bContext *C, struct bNodeTree *ntree, struct bNode *node);
void node_update_nodetree(const struct bContext *C, struct bNodeTree *ntree, float offsetx, float offsety);
void node_draw_nodetree(const struct bContext *C, struct ARegion *ar, struct SpaceNode *snode, struct bNodeTree *ntree);
void drawnodespace(const bContext *C, ARegion *ar, View2D *v2d);

+int node_ui_handler(struct bContext *C, struct wmEvent *event, void *userdata);
+void node_ui_handler_remove(struct bContext *C, void *userdata);
+
/* node_buttons.c */
void node_buttons_register(struct ARegionType *art);
void NODE_OT_properties(struct wmOperatorType *ot);
@@ -149,6 +155,7 @@
void NODE_OT_hide_socket_toggle(struct wmOperatorType *ot);
void NODE_OT_preview_toggle(struct wmOperatorType *ot);
void NODE_OT_options_toggle(struct wmOperatorType *ot);
+void NODE_OT_node_copy_color(struct wmOperatorType *ot);

void NODE_OT_show_cyclic_dependencies(struct wmOperatorType *ot);
void NODE_OT_link_viewer(struct wmOperatorType *ot);
@@ -167,6 +174,10 @@
void NODE_OT_output_file_add_socket(struct wmOperatorType *ot);
void NODE_OT_output_file_remove_active_socket(struct wmOperatorType *ot);

+void NODE_OT_parent_set(struct wmOperatorType *ot);
+void NODE_OT_parent_clear(struct wmOperatorType *ot);
+void NODE_OT_join(struct wmOperatorType *ot);
+
extern const char *node_context_dir[];

// XXXXXX
Index: source/blender/editors/space_node/node_ops.c
===================================================================
--- source/blender/editors/space_node/node_ops.c (revision 45822)
+++ source/blender/editors/space_node/node_ops.c (working copy)
@@ -68,6 +68,7 @@
WM_operatortype_append(NODE_OT_options_toggle);
WM_operatortype_append(NODE_OT_hide_socket_toggle);
WM_operatortype_append(NODE_OT_show_cyclic_dependencies);
+ WM_operatortype_append(NODE_OT_node_copy_color);

WM_operatortype_append(NODE_OT_duplicate);
WM_operatortype_append(NODE_OT_delete);
@@ -103,6 +104,10 @@

WM_operatortype_append(NODE_OT_output_file_add_socket);
WM_operatortype_append(NODE_OT_output_file_remove_active_socket);
+
+ WM_operatortype_append(NODE_OT_parent_set);
+ WM_operatortype_append(NODE_OT_parent_clear);
+ WM_operatortype_append(NODE_OT_join);
}

void ED_operatormacros_node(void)
@@ -186,6 +191,10 @@
/* modified operator call for duplicating with input links */
WM_keymap_add_item(keymap, "NODE_OT_duplicate_move_keep_inputs", DKEY, KM_PRESS, KM_SHIFT|KM_CTRL, 0);

+ WM_keymap_add_item(keymap, "NODE_OT_parent_set", PKEY, KM_PRESS, KM_CTRL, 0);
+ WM_keymap_add_item(keymap, "NODE_OT_parent_clear", PKEY, KM_PRESS, KM_ALT, 0);
+ WM_keymap_add_item(keymap, "NODE_OT_join", JKEY, KM_PRESS, KM_CTRL, 0);
+
WM_keymap_add_item(keymap, "NODE_OT_hide_toggle", HKEY, KM_PRESS, 0, 0);
WM_keymap_add_item(keymap, "NODE_OT_mute_toggle", MKEY, KM_PRESS, 0, 0);
WM_keymap_add_item(keymap, "NODE_OT_preview_toggle", HKEY, KM_PRESS, KM_SHIFT, 0);
Index: source/blender/editors/space_node/node_select.c
===================================================================
--- source/blender/editors/space_node/node_select.c (revision 45822)
+++ source/blender/editors/space_node/node_select.c (working copy)
@@ -70,109 +70,6 @@
return NULL;
}

-static int compare_nodes(bNode *a, bNode *b)
-{
- bNode *parent;
- /* These tell if either the node or any of the parent nodes is selected.
- * A selected parent means an unselected node is also in foreground!
- */
- int a_select=(a->flag & NODE_SELECT), b_select=(b->flag & NODE_SELECT);
- int a_active=(a->flag & NODE_ACTIVE), b_active=(b->flag & NODE_ACTIVE);
-
- /* if one is an ancestor of the other */
- /* XXX there might be a better sorting algorithm for stable topological sort, this is O(n^2) worst case */
- for (parent = a->parent; parent; parent=parent->parent) {
- /* if b is an ancestor, it is always behind a */
- if (parent==b)
- return 1;
- /* any selected ancestor moves the node forward */
- if (parent->flag & NODE_ACTIVE)
- a_active = 1;
- if (parent->flag & NODE_SELECT)
- a_select = 1;
- }
- for (parent = b->parent; parent; parent=parent->parent) {
- /* if a is an ancestor, it is always behind b */
- if (parent==a)
- return 0;
- /* any selected ancestor moves the node forward */
- if (parent->flag & NODE_ACTIVE)
- b_active = 1;
- if (parent->flag & NODE_SELECT)
- b_select = 1;
- }
-
- /* if one of the nodes is in the background and the other not */
- if ((a->flag & NODE_BACKGROUND) && !(b->flag & NODE_BACKGROUND))
- return 0;
- else if (!(a->flag & NODE_BACKGROUND) && (b->flag & NODE_BACKGROUND))
- return 1;
-
- /* if one has a higher selection state (active > selected > nothing) */
- if (!b_active && a_active)
- return 1;
- else if (!b_select && (a_active || a_select))
- return 1;
-
- return 0;
-}
-
-/* Sorts nodes by selection: unselected nodes first, then selected,
- * then the active node at the very end. Relative order is kept intact!
- */
-static void node_sort(bNodeTree *ntree)
-{
- /* merge sort is the algorithm of choice here */
- bNode *first_a, *first_b, *node_a, *node_b, *tmp;
- int totnodes= BLI_countlist(&ntree->nodes);
- int k, a, b;
-
- k = 1;
- while (k < totnodes) {
- first_a = first_b = ntree->nodes.first;
-
- do {
- /* setup first_b pointer */
- for (b=0; b < k && first_b; ++b) {
- first_b = first_b->next;
- }
- /* all batches merged? */
- if (first_b==NULL)
- break;
-
- /* merge batches */
- node_a = first_a;
- node_b = first_b;
- a = b = 0;
- while (a < k && b < k && node_b) {
- if (compare_nodes(node_a, node_b)==0) {
- node_a = node_a->next;
- ++a;
- }
- else {
- tmp = node_b;
- node_b = node_b->next;
- ++b;
- BLI_remlink(&ntree->nodes, tmp);
- BLI_insertlinkbefore(&ntree->nodes, node_a, tmp);
- }
- }
-
- /* setup first pointers for next batch */
- first_b = node_b;
- for (; b < k; ++b) {
- /* all nodes sorted? */
- if (first_b==NULL)
- break;
- first_b = first_b->next;
- }
- first_a = first_b;
- } while (first_b);
-
- k = k << 1;
- }
-}
-
void node_select(bNode *node)
{
node->flag |= SELECT;
@@ -407,7 +304,7 @@

ED_node_set_active(bmain, snode->edittree, node);

- node_sort(snode->edittree);
+ ED_node_sort(snode->edittree);

WM_event_add_notifier(C, NC_NODE|NA_SELECTED, NULL);
}
@@ -479,7 +376,7 @@

/* update node order */
if (selected)
- node_sort(snode->edittree);
+ ED_node_sort(snode->edittree);

return selected;
}
@@ -573,7 +470,7 @@
}
}

- node_sort(snode->edittree);
+ ED_node_sort(snode->edittree);

WM_event_add_notifier(C, NC_NODE|NA_SELECTED, NULL);

@@ -645,7 +542,7 @@
node_select(node);
}

- node_sort(snode->edittree);
+ ED_node_sort(snode->edittree);

WM_event_add_notifier(C, NC_NODE|NA_SELECTED, NULL);
return OPERATOR_FINISHED;
@@ -687,7 +584,7 @@
node_select(node);
}

- node_sort(snode->edittree);
+ ED_node_sort(snode->edittree);

WM_event_add_notifier(C, NC_NODE|NA_SELECTED, NULL);
return OPERATOR_FINISHED;
@@ -729,7 +626,7 @@
node_select(node);
}

- node_sort(snode->edittree);
+ ED_node_sort(snode->edittree);

WM_event_add_notifier(C, NC_NODE|NA_SELECTED, NULL);
return OPERATOR_FINISHED;
@@ -758,7 +655,7 @@

node_select_same_type(snode);

- node_sort(snode->edittree);
+ ED_node_sort(snode->edittree);

WM_event_add_notifier(C, NC_NODE|NA_SELECTED, NULL);
return OPERATOR_FINISHED;
@@ -787,7 +684,7 @@

node_select_same_type_np(snode, 0);

- node_sort(snode->edittree);
+ ED_node_sort(snode->edittree);

WM_event_add_notifier(C, NC_NODE|NA_SELECTED, NULL);

@@ -815,7 +712,7 @@

node_select_same_type_np(snode, 1);

- node_sort(snode->edittree);
+ ED_node_sort(snode->edittree);

WM_event_add_notifier(C, NC_NODE|NA_SELECTED, NULL);
return OPERATOR_FINISHED;
Index: source/blender/editors/space_node/space_node.c
===================================================================
--- source/blender/editors/space_node/space_node.c (revision 45822)
+++ source/blender/editors/space_node/space_node.c (working copy)
@@ -358,6 +358,8 @@
lb = WM_dropboxmap_find("Node Editor", SPACE_NODE, RGN_TYPE_WINDOW);

WM_event_add_dropbox_handler(&ar->handlers, lb);
+
+ WM_event_add_ui_handler(NULL, &ar->handlers, node_ui_handler, node_ui_handler_remove, NULL);
}

static void node_main_area_draw(const bContext *C, ARegion *ar)
Index: source/blender/editors/transform/transform_conversions.c
===================================================================
--- source/blender/editors/transform/transform_conversions.c (revision 45822)
+++ source/blender/editors/transform/transform_conversions.c (working copy)
@@ -2281,7 +2281,6 @@
if (t->total==1) {
ED_node_link_intersect_test(t->sa, 1);
}
-
}

/* *** SEQUENCE EDITOR *** */
@@ -4928,10 +4927,11 @@
}
else if (t->spacetype == SPACE_NODE) {
SpaceNode *snode= (SpaceNode *)t->sa->spacedata.first;
- ED_node_update_hierarchy(C, snode->edittree);
-
- if (canceled == 0)
+ if (canceled == 0) {
+ ED_node_post_apply_transform(C, snode->edittree);
+
ED_node_link_insert(t->sa);
+ }

/* clear link line */
ED_node_link_intersect_test(t->sa, 0);
Index: source/blender/makesdna/DNA_node_types.h
===================================================================
--- source/blender/makesdna/DNA_node_types.h (revision 45822)
+++ source/blender/makesdna/DNA_node_types.h (working copy)
@@ -150,11 +150,14 @@
struct bNode *next, *prev, *new_node;

char name[64]; /* MAX_NAME */
- short type, flag;
+ int flag;
+ short type, pad2;
short done, level; /* both for dependency and sorting */
short lasty, menunr; /* lasty: check preview render status, menunr: browse ID blocks */
short stack_index; /* for groupnode, offset in global caller stack */
short nr; /* number of this node in list, used for UI exec events */
+ float color[4]; /* custom user-defined color */
+ int pad3;

ListBase inputs, outputs;
struct bNode *parent; /* parent node */
@@ -164,6 +167,7 @@
float locx, locy; /* root offset for drawing */
float width, height; /* node custom width and height */
float miniwidth; /* node width if hidden */
+ float offsetx, offsety; /* additional offset from loc */

int update; /* update flags */

@@ -205,6 +209,8 @@
#define NODE_TRANSFORM (1<<13)
/* node is active texture */
#define NODE_ACTIVE_TEXTURE (1<<14)
+ /* use a custom color for the node */
+#define NODE_CUSTOM_COLOR (1<<15)

/* node->update */
/* XXX NODE_UPDATE is a generic update flag. More fine-grained updates
@@ -522,6 +528,10 @@
char name[64];
} TexNodeOutput;

+/* frame node flags */
+#define NODE_FRAME_SHRINK 1 /* keep the bounding box minimal */
+#define NODE_FRAME_RESIZEABLE 2 /* test flag, if frame can be resized by user */
+
/* comp channel matte */
#define CMP_NODE_CHANNEL_MATTE_CS_RGB 1
#define CMP_NODE_CHANNEL_MATTE_CS_HSV 2
Index: source/blender/makesrna/intern/rna_nodetree.c
===================================================================
--- source/blender/makesrna/intern/rna_nodetree.c (revision 45822)
+++ source/blender/makesrna/intern/rna_nodetree.c (working copy)
@@ -200,6 +200,8 @@
return &RNA_NodeForLoop;
case NODE_WHILELOOP:
return &RNA_NodeWhileLoop;
+ case NODE_FRAME:
+ return &RNA_NodeFrame;

default:
return &RNA_Node;
@@ -1029,8 +1031,12 @@

static void def_frame(StructRNA *srna)
{
-/* PropertyRNA *prop; */
+ PropertyRNA *prop;

+ prop = RNA_def_property(srna, "shrink", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "custom1", NODE_FRAME_SHRINK);
+ RNA_def_property_ui_text(prop, "Shrink", "Shrink the frame to minimal bounding box");
+ RNA_def_property_update(prop, NC_NODE|ND_DISPLAY, "rna_Node_update");
}

static void def_math(StructRNA *srna)
@@ -3268,6 +3274,18 @@
RNA_def_property_struct_type(prop, "Node");
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
RNA_def_property_ui_text(prop, "Parent", "Parent this node is attached to");
+
+ prop = RNA_def_property(srna, "use_custom_color", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", NODE_CUSTOM_COLOR);
+ RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
+ RNA_def_property_ui_text(prop, "Custom Color", "Use custom color for the node");
+ RNA_def_property_update(prop, NC_NODE|ND_DISPLAY, NULL);
+
+ prop = RNA_def_property(srna, "color", PROP_FLOAT, PROP_COLOR);
+ RNA_def_property_array(prop, 4);
+ RNA_def_property_range(prop, 0.0f, 1.0f);
+ RNA_def_property_ui_text(prop, "Color", "Custom color of the node body");
+ RNA_def_property_update(prop, NC_NODE|ND_DISPLAY, NULL);

prop = RNA_def_property(srna, "show_texture", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", NODE_ACTIVE_TEXTURE);
Index: source/blender/makesrna/intern/rna_userdef.c
===================================================================
--- source/blender/makesrna/intern/rna_userdef.c (revision 45822)
+++ source/blender/makesrna/intern/rna_userdef.c (working copy)
@@ -1520,6 +1520,18 @@
rna_def_userdef_theme_spaces_main(srna);
rna_def_userdef_theme_spaces_list_main(srna);

+ prop = RNA_def_property(srna, "node_selected", PROP_FLOAT, PROP_COLOR_GAMMA);
+ RNA_def_property_float_sdna(prop, NULL, "select");
+ RNA_def_property_array(prop, 3);
+ RNA_def_property_ui_text(prop, "Node Selected", "");
+ RNA_def_property_update(prop, 0, "rna_userdef_update");
+
+ prop = RNA_def_property(srna, "node_active", PROP_FLOAT, PROP_COLOR_GAMMA);
+ RNA_def_property_float_sdna(prop, NULL, "active");
+ RNA_def_property_array(prop, 3);
+ RNA_def_property_ui_text(prop, "Active Node", "");
+ RNA_def_property_update(prop, 0, "rna_userdef_update");
+
prop = RNA_def_property(srna, "wire", PROP_FLOAT, PROP_COLOR_GAMMA);
RNA_def_property_float_sdna(prop, NULL, "wire");
RNA_def_property_array(prop, 3);
Index: source/blender/nodes/intern/node_common.c
===================================================================
--- source/blender/nodes/intern/node_common.c (revision 45822)
+++ source/blender/nodes/intern/node_common.c (working copy)
@@ -809,12 +809,18 @@

/**** FRAME ****/

+static void node_frame_init(bNodeTree *UNUSED(ntree), bNode *node, bNodeTemplate *UNUSED(ntemp))
+{
+ node->custom1 |= NODE_FRAME_SHRINK;
+}
+
void register_node_type_frame(bNodeTreeType *ttype)
{
/* frame type is used for all tree types, needs dynamic allocation */
bNodeType *ntype= MEM_callocN(sizeof(bNodeType), "frame node type");

- node_type_base(ttype, ntype, NODE_FRAME, "Frame", NODE_CLASS_LAYOUT, NODE_BACKGROUND);
+ node_type_base(ttype, ntype, NODE_FRAME, "Frame", NODE_CLASS_LAYOUT, NODE_BACKGROUND|NODE_OPTIONS);
+ node_type_init(ntype, node_frame_init);
node_type_size(ntype, 150, 100, 0);
node_type_compatibility(ntype, NODE_OLD_SHADING|NODE_NEW_SHADING);
  1. Index: release/scripts/presets/node_color/apricot.py
  2. ===================================================================
  3. --- /dev/null   (revision 45822)
  4. +++ release/scripts/presets/node_color/apricot.py       (working copy)
  5. @@ -0,0 +1,5 @@
  6. +import bpy
  7. +node = bpy.context.active_node
  8. +
  9. +node.color = (0.9651851654052734, 0.5314834117889404, 0.30546149611473083)
  10. +node.use_custom_color = True
  11. Index: release/scripts/presets/node_color/durian.py
  12. ===================================================================
  13. --- /dev/null   (revision 45822)
  14. +++ release/scripts/presets/node_color/durian.py        (working copy)
  15. @@ -0,0 +1,5 @@
  16. +import bpy
  17. +node = bpy.context.active_node
  18. +
  19. +node.color = (0.9651851654052734, 0.8261134028434753, 0.24349087476730347)
  20. +node.use_custom_color = True
  21. Index: release/scripts/presets/node_color/mango.py
  22. new file mode 100644
  23. ===================================================================
  24. --- /dev/null   (revision 45822)
  25. +++ release/scripts/presets/node_color/mango.py (working copy)
  26. @@ -0,0 +1,5 @@
  27. +import bpy
  28. +node = bpy.context.active_node
  29. +
  30. +node.color = (0.9651851654052734, 0.6220577359199524, 0.039014507085084915)
  31. +node.use_custom_color = True
  32. Index: release/scripts/presets/node_color/orange.py
  33. new file mode 100644
  34. ===================================================================
  35. --- release/scripts/presets/node_color/orange.py        (revision 0)
  36. +++ release/scripts/presets/node_color/orange.py        (revision 0)
  37. @@ -0,0 +1,5 @@
  38. +import bpy
  39. +node = bpy.context.active_node
  40. +
  41. +node.color = (1.0, 0.46723097562789917, 0.0)
  42. +node.use_custom_color = True
  43. Index: release/scripts/presets/node_color/peach.py
  44. new file mode 100644
  45. ===================================================================
  46. --- /dev/null   (revision 45822)
  47. +++ release/scripts/presets/node_color/peach.py (working copy)
  48. @@ -0,0 +1,5 @@
  49. +import bpy
  50. +node = bpy.context.active_node
  51. +
  52. +node.color = (1.0, 0.39570868015289307, 0.11025595664978027)
  53. +node.use_custom_color = True
  54. Index: release/scripts/startup/bl_operators/presets.py
  55. ===================================================================
  56. --- release/scripts/startup/bl_operators/presets.py     (revision 45822)
  57. +++ release/scripts/startup/bl_operators/presets.py     (working copy)
  58. @@ -436,6 +436,24 @@
  59.      preset_subdir = "tracking_settings"
  60.  
  61.  
  62. +class AddPresetNodeColor(AddPresetBase, Operator):
  63. +    '''Add a Node Color Preset'''
  64. +    bl_idname = "node.node_color_preset_add"
  65. +    bl_label = "Add Node Color Preset"
  66. +    preset_menu = "NODE_MT_node_color_presets"
  67. +
  68. +    preset_defines = [
  69. +        "node = bpy.context.active_node"
  70. +    ]
  71. +
  72. +    preset_values = [
  73. +        "node.color",
  74. +        "node.use_custom_color"
  75. +    ]
  76. +
  77. +    preset_subdir = "node_color"
  78. +
  79. +
  80.  class AddPresetInterfaceTheme(AddPresetBase, Operator):
  81.      '''Add a theme preset'''
  82.      bl_idname = "wm.interface_theme_preset_add"
  83. Index: release/scripts/startup/bl_ui/space_node.py
  84. ===================================================================
  85. --- release/scripts/startup/bl_ui/space_node.py (revision 45822)
  86. +++ release/scripts/startup/bl_ui/space_node.py (working copy)
  87. @@ -205,5 +205,23 @@
  88.          col.prop(snode, "backdrop_y", text="Y")
  89.          col.operator("node.backimage_move", text="Move")
  90.  
  91. +
  92. +class NODE_MT_node_color_presets(Menu):
  93. +    """Predefined node color"""
  94. +    bl_label = "Color Presets"
  95. +    preset_subdir = "node_color"
  96. +    preset_operator = "script.execute_preset"
  97. +    draw = Menu.draw_preset
  98. +
  99. +
  100. +class NODE_MT_node_color_specials(Menu):
  101. +    bl_label = "Node Color Specials"
  102. +
  103. +    def draw(self, context):
  104. +        layout = self.layout
  105. +
  106. +        layout.operator('node.node_copy_color', icon='COPY_ID')
  107. +
  108. +
  109.  if __name__ == "__main__":  # only for live edit.
  110.      bpy.utils.register_module(__name__)
  111. Index: source/blender/blenkernel/BKE_node.h
  112. ===================================================================
  113. --- source/blender/blenkernel/BKE_node.h        (revision 45822)
  114. +++ source/blender/blenkernel/BKE_node.h        (working copy)
  115. @@ -241,6 +241,12 @@
  116.  #define NODE_OLD_SHADING       1
  117.  #define NODE_NEW_SHADING       2
  118.  
  119. +/* node resize directions */
  120. +#define NODE_RESIZE_TOP                1
  121. +#define NODE_RESIZE_BOTTOM     2
  122. +#define NODE_RESIZE_RIGHT      4
  123. +#define NODE_RESIZE_LEFT       8
  124. +
  125.  /* enum values for input/output */
  126.  #define SOCK_IN                1
  127.  #define SOCK_OUT       2
  128. @@ -341,7 +347,8 @@
  129.  void                   nodeRemSocketLinks(struct bNodeTree *ntree, struct bNodeSocket *sock);
  130.  void                   nodeInternalRelink(struct bNodeTree *ntree, struct bNode *node);
  131.  
  132. -void                   nodeSpaceCoords(struct bNode *node, float *locx, float *locy);
  133. +void                   nodeToView(struct bNode *node, float x, float y, float *rx, float *ry);
  134. +void                   nodeFromView(struct bNode *node, float x, float y, float *rx, float *ry);
  135.  void                   nodeAttachNode(struct bNode *node, struct bNode *parent);
  136.  void                   nodeDetachNode(struct bNode *node);
  137.  
  138. Index: source/blender/blenkernel/intern/node.c
  139. ===================================================================
  140. --- source/blender/blenkernel/intern/node.c     (revision 45822)
  141. +++ source/blender/blenkernel/intern/node.c     (working copy)
  142. @@ -591,40 +591,49 @@
  143.         BLI_freelistN(&intlinks);
  144.  }
  145.  
  146. -/* transforms node location to area coords */
  147. -void nodeSpaceCoords(bNode *node, float *locx, float *locy)
  148. +void nodeToView(bNode *node, float x, float y, float *rx, float *ry)
  149.  {
  150.         if (node->parent) {
  151. -               nodeSpaceCoords(node->parent, locx, locy);
  152. -               *locx += node->locx;
  153. -               *locy += node->locy;
  154. +               nodeToView(node->parent, x + node->locx, y + node->locy, rx, ry);
  155.         }
  156.         else {
  157. -               *locx = node->locx;
  158. -               *locy = node->locy;
  159. +               *rx = x + node->locx;
  160. +               *ry = y + node->locy;
  161. +       }
  162. +}
  163. +
  164. +void nodeFromView(bNode *node, float x, float y, float *rx, float *ry)
  165. +{
  166. +       if (node->parent) {
  167. +               nodeFromView(node->parent, x, y, rx, ry);
  168. +               *rx -= node->locx;
  169. +               *ry -= node->locy;
  170. +       }
  171. +       else {
  172. +               *rx = x - node->locx;
  173. +               *ry = y - node->locy;
  174.         }
  175.  }
  176.  
  177.  void nodeAttachNode(bNode *node, bNode *parent)
  178.  {
  179. -       float parentx, parenty;
  180. +       float locx, locy;
  181. +       nodeToView(node, 0.0f, 0.0f, &locx, &locy);
  182.         
  183.         node->parent = parent;
  184.         /* transform to parent space */
  185. -       nodeSpaceCoords(parent, &parentx, &parenty);
  186. -       node->locx -= parentx;
  187. -       node->locy -= parenty;
  188. +       nodeFromView(parent, locx, locy, &node->locx, &node->locy);
  189.  }
  190.  
  191.  void nodeDetachNode(struct bNode *node)
  192.  {
  193. -       float parentx, parenty;
  194. +       float locx, locy;
  195.         
  196.         if (node->parent) {
  197. -               /* transform to "global" (area) space */
  198. -               nodeSpaceCoords(node->parent, &parentx, &parenty);
  199. -               node->locx += parentx;
  200. -               node->locy += parenty;
  201. +               /* transform to view space */
  202. +               nodeToView(node, 0.0f, 0.0f, &locx, &locy);
  203. +               node->locx = locx;
  204. +               node->locy = locy;
  205.                 node->parent = NULL;
  206.         }
  207.  }
  208. Index: source/blender/editors/include/ED_node.h
  209. ===================================================================
  210. --- source/blender/editors/include/ED_node.h    (revision 45822)
  211. +++ source/blender/editors/include/ED_node.h    (working copy)
  212. @@ -49,6 +49,7 @@
  213.  void ED_node_tree_update(struct SpaceNode *snode, struct Scene *scene);
  214.  void ED_node_changed_update(struct ID *id, struct bNode *node);
  215.  void ED_node_generic_update(struct Main *bmain, struct bNodeTree *ntree, struct bNode *node);
  216. +void ED_node_sort(struct bNodeTree *ntree);
  217.  
  218.  /* node_edit.c */
  219.  void ED_node_shader_default(struct Scene *scene, struct ID *id);
  220. @@ -57,7 +58,7 @@
  221.  void ED_node_link_intersect_test(struct ScrArea *sa, int test);
  222.  void ED_node_link_insert(struct ScrArea *sa);
  223.  
  224. -void ED_node_update_hierarchy(struct bContext *C, struct bNodeTree *ntree);
  225. +void ED_node_post_apply_transform(struct bContext *C, struct bNodeTree *ntree);
  226.  
  227.  void ED_node_set_active(struct Main *bmain, struct bNodeTree *ntree, struct bNode *node);
  228.  /* node ops.c */
  229. Index: source/blender/editors/interface/interface_draw.c
  230. ===================================================================
  231. --- source/blender/editors/interface/interface_draw.c   (revision 45822)
  232. +++ source/blender/editors/interface/interface_draw.c   (working copy)
  233. @@ -1638,12 +1638,12 @@
  234.  }
  235.  
  236.  
  237. -void ui_dropshadow(rctf *rct, float radius, float aspect, int UNUSED(select))
  238. +void ui_dropshadow(rctf *rct, float radius, float aspect, int UNUSED(select), float alpha)
  239.  {
  240.         int i;
  241.         float rad;
  242.         float a;
  243. -       char alpha = 2;
  244. +       float dalpha = alpha * (2.0f / 255.0f), curalpha=0.0f;
  245.         
  246.         glEnable(GL_BLEND);
  247.         
  248. @@ -1664,9 +1664,9 @@
  249.         }
  250.  
  251.         for (; i--; a -= aspect) {
  252. +               curalpha += dalpha;
  253.                 /* alpha ranges from 2 to 20 or so */
  254. -               glColor4ub(0, 0, 0, alpha);
  255. -               alpha += 2;
  256. +               glColor4f(0.0f, 0.0f, 0.0f, curalpha);
  257.                
  258.                 uiDrawBox(GL_POLYGON, rct->xmin - a, rct->ymin - a, rct->xmax + a, rct->ymax - 10.0f + a, rad + a);
  259.         }
  260. Index: source/blender/editors/interface/interface_intern.h
  261. ===================================================================
  262. --- source/blender/editors/interface/interface_intern.h (revision 45822)
  263. +++ source/blender/editors/interface/interface_intern.h (working copy)
  264. @@ -444,7 +444,7 @@
  265.  extern void ui_draw_aligned_panel(struct uiStyle *style, uiBlock *block, rcti *rect);
  266.  
  267.  /* interface_draw.c */
  268. -extern void ui_dropshadow(rctf *rct, float radius, float aspect, int select);
  269. +extern void ui_dropshadow(rctf *rct, float radius, float aspect, int select, float alpha);
  270.  
  271.  void ui_draw_gradient(rcti *rect, const float hsv[3], int type, float alpha);
  272.  
  273. Index: source/blender/editors/interface/resources.c
  274. ===================================================================
  275. --- source/blender/editors/interface/resources.c        (revision 45822)
  276. +++ source/blender/editors/interface/resources.c        (working copy)
  277. @@ -837,7 +837,7 @@
  278.         
  279.         /* space node, re-uses syntax color storage */
  280.         btheme->tnode = btheme->tv3d;
  281. -       rgba_char_args_set(btheme->tnode.edge_select, 255, 255, 255, 255);
  282. +       rgba_char_args_set(btheme->tnode.edge_select, 255, 255, 255, 255);      /* wire selected */
  283.         rgba_char_args_set(btheme->tnode.syntaxl, 155, 155, 155, 160);  /* TH_NODE, backdrop */
  284.         rgba_char_args_set(btheme->tnode.syntaxn, 100, 100, 100, 255);  /* in/output */
  285.         rgba_char_args_set(btheme->tnode.syntaxb, 108, 105, 111, 255);  /* operator */
  286. Index: source/blender/editors/space_node/drawnode.c
  287. ===================================================================
  288. --- source/blender/editors/space_node/drawnode.c        (revision 45822)
  289. +++ source/blender/editors/space_node/drawnode.c        (working copy)
  290. @@ -79,6 +79,9 @@
  291.  
  292.  #include "node_intern.h"
  293.  
  294. +// XXX interface.h
  295. +extern void ui_dropshadow(rctf *rct, float radius, float aspect, int select, float alpha);
  296. +
  297.  /* ****************** SOCKET BUTTON DRAW FUNCTIONS ***************** */
  298.  
  299.  static void node_sync_cb(bContext *UNUSED(C), void *snode_v, void *node_v)
  300. @@ -513,15 +516,21 @@
  301.                 rctf totr= node->totr;
  302.                 /* right part of node */
  303.                 totr.xmin = node->totr.xmax-20.0f;
  304. -               return BLI_in_rctf(&totr, x, y);
  305. +               if (BLI_in_rctf(&totr, x, y))
  306. +                       return NODE_RESIZE_RIGHT;
  307. +               else
  308. +                       return 0;
  309.         }
  310.         else {
  311. -               /* rect we're interested in is just the bottom right corner */
  312. +               const float size = 10.0f;
  313.                 rctf totr= node->totr;
  314. -               /* bottom right corner */
  315. -               totr.xmin = totr.xmax-10.0f;
  316. -               totr.ymax = totr.ymin+10.0f;
  317. -               return BLI_in_rctf(&totr, x, y);
  318. +               int dir = 0;
  319. +              
  320. +               if (x >= totr.xmax-size && x < totr.xmax && y >= totr.ymin && y < totr.ymax)
  321. +                       dir |= NODE_RESIZE_RIGHT;
  322. +               if (x >= totr.xmin && x < totr.xmin+size && y >= totr.ymin && y < totr.ymax)
  323. +                       dir |= NODE_RESIZE_LEFT;
  324. +               return dir;
  325.         }
  326.  }
  327.  
  328. @@ -550,7 +559,7 @@
  329.                 int dy;
  330.                
  331.                 /* get "global" coords */
  332. -               nodeSpaceCoords(gnode, &locx, &locy);
  333. +               nodeToView(gnode, 0.0f, 0.0f, &locx, &locy);
  334.                
  335.                 /* center them, is a bit of abuse of locx and locy though */
  336.                 node_update_nodetree(C, ngroup, locx, locy);
  337. @@ -945,20 +954,157 @@
  338.         uiItemR(layout, ptr, "max_iterations", 0, NULL, 0);
  339.  }
  340.  
  341. -static void node_update_frame(const bContext *UNUSED(C), bNodeTree *UNUSED(ntree), bNode *node)
  342. +/* XXX Does a bounding box update by iterating over all children.
  343. + * Not ideal to do this in every draw call, but doing as transform callback doesn't work,
  344. + * since the child node totr rects are not updated properly at that point.
  345. + */
  346. +static void node_update_frame(const bContext *UNUSED(C), bNodeTree *ntree, bNode *node)
  347. +{
  348. +       const float margin = 30.0f;
  349. +       int bbinit;
  350. +       bNode *tnode;
  351. +       rctf rect, noderect;
  352. +       float xmax, ymax;
  353. +       
  354. +       /* init rect from current frame size */
  355. +       nodeToView(node, node->offsetx, node->offsety, &rect.xmin, &rect.ymax);
  356. +       nodeToView(node, node->offsetx+node->width, node->offsety-node->height, &rect.xmax, &rect.ymin);
  357. +       
  358. +       /* frame can be resized manually only if shrinking is disabled or no children are attached */
  359. +       node->custom1 |= NODE_FRAME_RESIZEABLE;
  360. +       /* for shrinking bbox, initialize the rect from first child node */
  361. +       bbinit = (node->custom1 & NODE_FRAME_SHRINK);
  362. +       /* fit bounding box to all children */
  363. +       for (tnode=ntree->nodes.first; tnode; tnode=tnode->next) {
  364. +               if (tnode->parent!=node)
  365. +                       continue;
  366. +              
  367. +               /* add margin to node rect */
  368. +               noderect = tnode->totr;
  369. +               noderect.xmin -= margin;
  370. +               noderect.xmax += margin;
  371. +               noderect.ymin -= margin;
  372. +               noderect.ymax += margin;
  373. +              
  374. +               /* first child initializes frame */
  375. +               if (bbinit) {
  376. +                       bbinit = 0;
  377. +                       rect = noderect;
  378. +                       node->custom1 &= ~NODE_FRAME_RESIZEABLE;
  379. +               }
  380. +               else
  381. +                       BLI_union_rctf(&rect, &noderect);
  382. +       }
  383. +       
  384. +       /* now adjust the frame size from view-space bounding box */
  385. +       nodeFromView(node, rect.xmin, rect.ymax, &node->offsetx, &node->offsety);
  386. +       nodeFromView(node, rect.xmax, rect.ymin, &xmax, &ymax);
  387. +       node->width = xmax - node->offsetx;
  388. +       node->height = -ymax + node->offsety;
  389. +       
  390. +       node->totr = rect;
  391. +}
  392. +
  393. +static void node_draw_frame(const bContext *C, ARegion *ar, SpaceNode *snode, bNodeTree *UNUSED(ntree), bNode *node)
  394.  {
  395. -       float locx, locy;
  396. +       rctf *rct= &node->totr;
  397. +       int color_id= node_get_colorid(node);
  398. +       char showname[128]; /* 128 used below */
  399. +       float shadowalpha;
  400. +       
  401. +       /* skip if out of view */
  402. +       if (node->totr.xmax < ar->v2d.cur.xmin || node->totr.xmin > ar->v2d.cur.xmax ||
  403. +                       node->totr.ymax < ar->v2d.cur.ymin || node->totr.ymin > ar->v2d.cur.ymax) {
  404. +              
  405. +               uiEndBlock(C, node->block);
  406. +               node->block= NULL;
  407. +               return;
  408. +       }
  409. +       
  410. +       uiSetRoundBox(UI_CNR_ALL);
  411. +       if (node->flag & NODE_CUSTOM_COLOR)
  412. +               shadowalpha = node->color[3];
  413. +       else
  414. +               shadowalpha = 1.0f;
  415. +       ui_dropshadow(rct, BASIS_RAD, snode->aspect, node->flag & SELECT, shadowalpha);
  416. +       
  417. +       /* header */
  418. +       if (color_id==TH_NODE)
  419. +               UI_ThemeColorShade(color_id, -20);
  420. +       else
  421. +               UI_ThemeColor(color_id);
  422. +       
  423. +       uiSetRoundBox(UI_CNR_TOP_LEFT | UI_CNR_TOP_RIGHT);
  424. +       uiRoundBox(rct->xmin, rct->ymax-NODE_DY, rct->xmax, rct->ymax, BASIS_RAD);
  425. +       
  426. +       /* title */
  427. +       if (node->flag & SELECT)
  428. +               UI_ThemeColor(TH_SELECT);
  429. +       else
  430. +               UI_ThemeColorBlendShade(TH_TEXT, color_id, 0.4f, 10);
  431. +       
  432. +       BLI_strncpy(showname, nodeLabel(node), sizeof(showname));
  433. +       uiDefBut(node->block, LABEL, 0, showname, (short)(rct->xmin+15), (short)(rct->ymax-NODE_DY),
  434. +                        (int)(rct->xmax-rct->xmin-25.0f), NODE_DY,  NULL, 0, 0, 0, 0, "");
  435. +
  436. +       /* body */
  437. +       if (node->flag & NODE_CUSTOM_COLOR)
  438. +               glColor4fv(node->color);
  439. +       else
  440. +               UI_ThemeColor4(TH_NODE);
  441. +       glEnable(GL_BLEND);
  442. +       uiSetRoundBox(UI_CNR_BOTTOM_LEFT | UI_CNR_BOTTOM_RIGHT);
  443. +       uiRoundBox(rct->xmin, rct->ymin, rct->xmax, rct->ymax-NODE_DY, BASIS_RAD);
  444. +       glDisable(GL_BLEND);
  445.  
  446. -       /* get "global" coords */
  447. -       nodeSpaceCoords(node, &locx, &locy);
  448. +       /* outline active and selected emphasis */
  449. +       if ( node->flag & (NODE_ACTIVE|SELECT) ) {
  450. +               glEnable(GL_BLEND);
  451. +               glEnable( GL_LINE_SMOOTH );
  452. +              
  453. +               if (node->flag & NODE_ACTIVE)
  454. +                       UI_ThemeColorShadeAlpha(TH_ACTIVE, 0, -40);
  455. +               else
  456. +                       UI_ThemeColorShadeAlpha(TH_SELECT, 0, -40);
  457. +               uiSetRoundBox(UI_CNR_ALL);
  458. +               uiDrawBox(GL_LINE_LOOP, rct->xmin, rct->ymin, rct->xmax, rct->ymax, BASIS_RAD);
  459. +              
  460. +               glDisable( GL_LINE_SMOOTH );
  461. +               glDisable(GL_BLEND);
  462. +       }
  463. +       
  464. +       UI_ThemeClearColor(color_id);
  465. +              
  466. +       uiEndBlock(C, node->block);
  467. +       uiDrawBlock(C, node->block);
  468. +       node->block= NULL;
  469. +}
  470.  
  471. -       node->prvr.xmin = locx + NODE_DYS;
  472. -       node->prvr.xmax = locx + node->width- NODE_DYS;
  473. +static int node_resize_area_frame(bNode *node, int x, int y)
  474. +{
  475. +       const float size = 10.0f;
  476. +       rctf totr= node->totr;
  477. +       int dir = 0;
  478. +       
  479. +       /* shrinking frame size is determined by child nodes */
  480. +       if (!(node->custom1 & NODE_FRAME_RESIZEABLE))
  481. +               return 0;
  482. +       
  483. +       if (x >= totr.xmax-size && x < totr.xmax && y >= totr.ymin && y < totr.ymax)
  484. +               dir |= NODE_RESIZE_RIGHT;
  485. +       if (x >= totr.xmin && x < totr.xmin+size && y >= totr.ymin && y < totr.ymax)
  486. +               dir |= NODE_RESIZE_LEFT;
  487. +       if (x >= totr.xmin && x < totr.xmax && y >= totr.ymax-size && y < totr.ymax)
  488. +               dir |= NODE_RESIZE_TOP;
  489. +       if (x >= totr.xmin && x < totr.xmax && y >= totr.ymin && y < totr.ymin+size)
  490. +               dir |= NODE_RESIZE_BOTTOM;
  491. +       
  492. +       return dir;
  493. +}
  494.  
  495. -       node->totr.xmin = locx;
  496. -       node->totr.xmax = locx + node->width;
  497. -       node->totr.ymax = locy;
  498. -       node->totr.ymin = locy - node->height;
  499. +static void node_buts_frame_details(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
  500. +{
  501. +       uiItemR(layout, ptr, "shrink", 0, "Shrink", ICON_NONE);
  502.  }
  503.  
  504.  static void node_common_set_butfunc(bNodeType *ntype)
  505. @@ -980,7 +1126,10 @@
  506.                         ntype->drawupdatefunc= node_update_group;
  507.                         break;
  508.                 case NODE_FRAME:
  509. +                       ntype->drawfunc= node_draw_frame;
  510.                         ntype->drawupdatefunc= node_update_frame;
  511. +                       ntype->uifuncbut= node_buts_frame_details;
  512. +                       ntype->resize_area_func= node_resize_area_frame;
  513.                         break;
  514.         }
  515.  }
  516. @@ -1175,7 +1324,6 @@
  517.  /* only once called */
  518.  static void node_shader_set_butfunc(bNodeType *ntype)
  519.  {
  520. -       ntype->uifuncbut = NULL;
  521.         switch(ntype->type) {
  522.                 /* case NODE_GROUP:      note, typeinfo for group is generated... see "XXX ugly hack" */
  523.  
  524. @@ -1254,7 +1402,6 @@
  525.                         ntype->uifunc= node_shader_buts_dynamic;
  526.                         break;
  527.         }
  528. -               if (ntype->uifuncbut == NULL) ntype->uifuncbut = ntype->uifunc;
  529.  }
  530.  
  531.  /* ****************** BUTTON CALLBACKS FOR COMPOSITE NODES ***************** */
  532. @@ -1940,7 +2087,6 @@
  533.  /* only once called */
  534.  static void node_composit_set_butfunc(bNodeType *ntype)
  535.  {
  536. -       ntype->uifuncbut = NULL;
  537.         switch(ntype->type) {
  538.                 /* case NODE_GROUP:      note, typeinfo for group is generated... see "XXX ugly hack" */
  539.  
  540. @@ -2106,8 +2252,6 @@
  541.                 default:
  542.                         ntype->uifunc= NULL;
  543.         }
  544. -       if (ntype->uifuncbut == NULL) ntype->uifuncbut = ntype->uifunc;
  545. -
  546.  }
  547.  
  548.  /* ****************** BUTTON CALLBACKS FOR TEXTURE NODES ***************** */
  549. @@ -2218,7 +2362,6 @@
  550.  /* only once called */
  551.  static void node_texture_set_butfunc(bNodeType *ntype)
  552.  {
  553. -       ntype->uifuncbut = NULL;
  554.         if ( ntype->type >= TEX_NODE_PROC && ntype->type < TEX_NODE_PROC_MAX ) {
  555.                 ntype->uifunc = node_texture_buts_proc;
  556.         }
  557. @@ -2260,7 +2403,6 @@
  558.                         ntype->uifunc = node_texture_buts_output;
  559.                         break;
  560.         }
  561. -       if (ntype->uifuncbut == NULL) ntype->uifuncbut = ntype->uifunc;
  562.  }
  563.  
  564.  /* ******* init draw callbacks for all tree types, only called in usiblender.c, once ************* */
  565. Index: source/blender/editors/space_node/node_buttons.c
  566. ===================================================================
  567. --- source/blender/editors/space_node/node_buttons.c    (revision 45822)
  568. +++ source/blender/editors/space_node/node_buttons.c    (working copy)
  569. @@ -89,8 +89,8 @@
  570.         SpaceNode *snode= CTX_wm_space_node(C);
  571.         bNodeTree *ntree= (snode) ? snode->edittree : NULL;
  572.         bNode *node = (ntree) ? nodeGetActive(ntree) : NULL; // xxx... for editing group nodes
  573. -       uiLayout *layout= pa->layout;
  574. -       PointerRNA ptr;
  575. +       uiLayout *layout= pa->layout, *row, *col;
  576. +       PointerRNA ptr, opptr;
  577.         
  578.         /* verify pointers, and create RNA pointer for the node */
  579.         if (ELEM(NULL, ntree, node))
  580. @@ -109,6 +109,20 @@
  581.         uiItemO(layout, NULL, 0, "NODE_OT_hide_socket_toggle");
  582.         uiItemS(layout);
  583.  
  584. +       row = uiLayoutRow(layout, 0);
  585. +       uiItemM(row, (bContext *)C, "NODE_MT_node_color_presets", NULL, 0);
  586. +       uiItemM(row, (bContext *)C, "NODE_MT_node_color_specials", "", ICON_DOWNARROW_HLT);
  587. +       uiItemO(row, "", ICON_ZOOMIN, "node.node_color_preset_add");
  588. +       opptr = uiItemFullO(row, "node.node_color_preset_add", "", ICON_ZOOMOUT, NULL, WM_OP_INVOKE_DEFAULT, UI_ITEM_O_RETURN_PROPS);
  589. +       RNA_boolean_set(&opptr, "remove_active", 1);
  590. +
  591. +       row = uiLayoutRow(layout, 0);
  592. +       uiItemR(row, &ptr, "use_custom_color", UI_ITEM_R_ICON_ONLY, NULL, ICON_NONE);
  593. +       col = uiLayoutColumn(row, 0);
  594. +       if (!(node->flag & NODE_CUSTOM_COLOR))
  595. +               uiLayoutSetEnabled(col, 0);
  596. +       uiItemR(col, &ptr, "color", 0, "", 0);
  597. +
  598.         /* draw this node's settings */
  599.         if (node->typeinfo && node->typeinfo->uifuncbut)
  600.                 node->typeinfo->uifuncbut(layout, (bContext *)C, &ptr);
  601. Index: source/blender/editors/space_node/node_draw.c
  602. ===================================================================
  603. --- source/blender/editors/space_node/node_draw.c       (revision 45822)
  604. +++ source/blender/editors/space_node/node_draw.c       (working copy)
  605. @@ -83,7 +83,7 @@
  606.  #define NODE_GROUP_FRAME               120
  607.  
  608.  // XXX interface.h
  609. -extern void ui_dropshadow(rctf *rct, float radius, float aspect, int select);
  610. +extern void ui_dropshadow(rctf *rct, float radius, float aspect, int select, float alpha);
  611.  
  612.  /* XXX update functions for node editor are a mess, needs a clear concept */
  613.  void ED_node_tree_update(SpaceNode *snode, Scene *scene)
  614. @@ -167,6 +167,110 @@
  615.                 ntreeTexCheckCyclics(ntree);
  616.  }
  617.  
  618. +static int compare_nodes(bNode *a, bNode *b)
  619. +{
  620. +       bNode *parent;
  621. +       /* These tell if either the node or any of the parent nodes is selected.
  622. +        * A selected parent means an unselected node is also in foreground!
  623. +        */
  624. +       int a_select=(a->flag & NODE_SELECT), b_select=(b->flag & NODE_SELECT);
  625. +       int a_active=(a->flag & NODE_ACTIVE), b_active=(b->flag & NODE_ACTIVE);
  626. +       
  627. +       /* if one is an ancestor of the other */
  628. +       /* XXX there might be a better sorting algorithm for stable topological sort, this is O(n^2) worst case */
  629. +       for (parent = a->parent; parent; parent=parent->parent) {
  630. +               /* if b is an ancestor, it is always behind a */
  631. +               if (parent==b)
  632. +                       return 1;
  633. +               /* any selected ancestor moves the node forward */
  634. +               if (parent->flag & NODE_ACTIVE)
  635. +                       a_active = 1;
  636. +               if (parent->flag & NODE_SELECT)
  637. +                       a_select = 1;
  638. +       }
  639. +       for (parent = b->parent; parent; parent=parent->parent) {
  640. +               /* if a is an ancestor, it is always behind b */
  641. +               if (parent==a)
  642. +                       return 0;
  643. +               /* any selected ancestor moves the node forward */
  644. +               if (parent->flag & NODE_ACTIVE)
  645. +                       b_active = 1;
  646. +               if (parent->flag & NODE_SELECT)
  647. +                       b_select = 1;
  648. +       }
  649. +
  650. +       /* if one of the nodes is in the background and the other not */
  651. +       if ((a->flag & NODE_BACKGROUND) && !(b->flag & NODE_BACKGROUND))
  652. +               return 0;
  653. +       else if (!(a->flag & NODE_BACKGROUND) && (b->flag & NODE_BACKGROUND))
  654. +               return 1;
  655. +       
  656. +       /* if one has a higher selection state (active > selected > nothing) */
  657. +       if (!b_active && a_active)
  658. +               return 1;
  659. +       else if (!b_select && (a_active || a_select))
  660. +               return 1;
  661. +       
  662. +       return 0;
  663. +}
  664. +
  665. +/* Sorts nodes by selection: unselected nodes first, then selected,
  666. + * then the active node at the very end. Relative order is kept intact!
  667. + */
  668. +void ED_node_sort(bNodeTree *ntree)
  669. +{
  670. +       /* merge sort is the algorithm of choice here */
  671. +       bNode *first_a, *first_b, *node_a, *node_b, *tmp;
  672. +       int totnodes= BLI_countlist(&ntree->nodes);
  673. +       int k, a, b;
  674. +       
  675. +       k = 1;
  676. +       while (k < totnodes) {
  677. +               first_a = first_b = ntree->nodes.first;
  678. +              
  679. +               do {
  680. +                       /* setup first_b pointer */
  681. +                       for (b=0; b < k && first_b; ++b) {
  682. +                               first_b = first_b->next;
  683. +                       }
  684. +                       /* all batches merged? */
  685. +                       if (first_b==NULL)
  686. +                               break;
  687. +                      
  688. +                       /* merge batches */
  689. +                       node_a = first_a;
  690. +                       node_b = first_b;
  691. +                       a = b = 0;
  692. +                       while (a < k && b < k && node_b) {
  693. +                               if (compare_nodes(node_a, node_b)==0) {
  694. +                                       node_a = node_a->next;
  695. +                                       ++a;
  696. +                               }
  697. +                               else {
  698. +                                       tmp = node_b;
  699. +                                       node_b = node_b->next;
  700. +                                       ++b;
  701. +                                       BLI_remlink(&ntree->nodes, tmp);
  702. +                                       BLI_insertlinkbefore(&ntree->nodes, node_a, tmp);
  703. +                               }
  704. +                       }
  705. +
  706. +                       /* setup first pointers for next batch */
  707. +                       first_b = node_b;
  708. +                       for (; b < k; ++b) {
  709. +                               /* all nodes sorted? */
  710. +                               if (first_b==NULL)
  711. +                                       break;
  712. +                               first_b = first_b->next;
  713. +                       }
  714. +                       first_a = first_b;
  715. +               } while (first_b);
  716. +              
  717. +               k = k << 1;
  718. +       }
  719. +}
  720. +
  721. +
  722.  static void do_node_internal_buttons(bContext *C, void *node_v, int event)
  723.  {
  724.         if (event==B_NODE_EXEC) {
  725. @@ -176,24 +280,6 @@
  726.         }
  727.  }
  728.  
  729. -
  730. -static void node_scaling_widget(int color_id, float aspect, float xmin, float ymin, float xmax, float ymax)
  731. -{
  732. -       float dx;
  733. -       float dy;
  734. -       
  735. -       dx= 0.5f*(xmax-xmin);
  736. -       dy= 0.5f*(ymax-ymin);
  737. -       
  738. -       UI_ThemeColorShade(color_id, +30);     
  739. -       fdrawline(xmin, ymin, xmax, ymax);
  740. -       fdrawline(xmin+dx, ymin, xmax, ymax-dy);
  741. -       
  742. -       UI_ThemeColorShade(color_id, -10);
  743. -       fdrawline(xmin, ymin+aspect, xmax, ymax+aspect);
  744. -       fdrawline(xmin+dx, ymin+aspect, xmax, ymax-dy+aspect);
  745. -}
  746. -
  747.  static void node_uiblocks_init(const bContext *C, bNodeTree *ntree)
  748.  {
  749.         bNode *node;
  750. @@ -223,7 +309,7 @@
  751.         int buty;
  752.         
  753.         /* get "global" coords */
  754. -       nodeSpaceCoords(node, &locx, &locy);
  755. +       nodeToView(node, 0.0f, 0.0f, &locx, &locy);
  756.         dy= locy;
  757.         
  758.         /* header */
  759. @@ -349,7 +435,7 @@
  760.         int totin=0, totout=0, tot;
  761.         
  762.         /* get "global" coords */
  763. -       nodeSpaceCoords(node, &locx, &locy);
  764. +       nodeToView(node, 0.0f, 0.0f, &locx, &locy);
  765.  
  766.         /* calculate minimal radius */
  767.         for (nsock= node->inputs.first; nsock; nsock= nsock->next)
  768. @@ -409,7 +495,7 @@
  769.                 node_update_basis(C, ntree, node);
  770.  }
  771.  
  772. -static int node_get_colorid(bNode *node)
  773. +int node_get_colorid(bNode *node)
  774.  {
  775.         if (node->typeinfo->nclass==NODE_CLASS_INPUT)
  776.                 return TH_NODE_IN_OUT;
  777. @@ -582,6 +668,7 @@
  778.         int color_id= node_get_colorid(node);
  779.         char showname[128]; /* 128 used below */
  780.         View2D *v2d = &ar->v2d;
  781. +       float shadowalpha;
  782.         
  783.         /* hurmf... another candidate for callback, have to see how this works first */
  784.         if (node->id && node->block && snode->treetype==NTREE_SHADER)
  785. @@ -596,8 +683,12 @@
  786.                 return;
  787.         }
  788.         
  789. -       uiSetRoundBox(UI_CNR_TOP_LEFT | UI_CNR_TOP_RIGHT | UI_CNR_BOTTOM_LEFT);
  790. -       ui_dropshadow(rct, BASIS_RAD, snode->aspect, node->flag & SELECT);
  791. +       uiSetRoundBox(UI_CNR_ALL);
  792. +       if (node->flag & NODE_CUSTOM_COLOR)
  793. +               shadowalpha = node->color[3];
  794. +       else
  795. +               shadowalpha = 1.0f;
  796. +       ui_dropshadow(rct, BASIS_RAD, snode->aspect, node->flag & SELECT, shadowalpha);
  797.         
  798.         /* header */
  799.         if (color_id==TH_NODE)
  800. @@ -643,7 +734,7 @@
  801.         
  802.         /* title */
  803.         if (node->flag & SELECT)
  804. -               UI_ThemeColor(TH_TEXT_HI);
  805. +               UI_ThemeColor(TH_SELECT);
  806.         else
  807.                 UI_ThemeColorBlendShade(TH_TEXT, color_id, 0.4f, 10);
  808.         
  809. @@ -679,27 +770,27 @@
  810.                          (int)(iconofs - rct->xmin-18.0f), NODE_DY,  NULL, 0, 0, 0, 0, "");
  811.  
  812.         /* body */
  813. -       UI_ThemeColor4(TH_NODE);
  814. +       if (node->flag & NODE_CUSTOM_COLOR)
  815. +               glColor4fv(node->color);
  816. +       else
  817. +               UI_ThemeColor4(TH_NODE);
  818.         glEnable(GL_BLEND);
  819. -       uiSetRoundBox(UI_CNR_BOTTOM_LEFT);
  820. +       uiSetRoundBox(UI_CNR_BOTTOM_LEFT | UI_CNR_BOTTOM_RIGHT);
  821.         uiRoundBox(rct->xmin, rct->ymin, rct->xmax, rct->ymax-NODE_DY, BASIS_RAD);
  822.         glDisable(GL_BLEND);
  823.  
  824. -       /* scaling indicator */
  825. -       node_scaling_widget(TH_NODE, snode->aspect, rct->xmax-BASIS_RAD*snode->aspect, rct->ymin, rct->xmax, rct->ymin+BASIS_RAD*snode->aspect);
  826. -
  827.         /* outline active and selected emphasis */
  828.         if ( node->flag & (NODE_ACTIVE|SELECT) ) {
  829.                 glEnable(GL_BLEND);
  830.                 glEnable( GL_LINE_SMOOTH );
  831. -                       /* using different shades of TH_TEXT_HI for the empasis, like triangle */
  832. -                       if ( node->flag & NODE_ACTIVE )
  833. -                               UI_ThemeColorShadeAlpha(TH_TEXT_HI, 0, -40);
  834. -                       else
  835. -                               UI_ThemeColorShadeAlpha(TH_TEXT_HI, -20, -120);
  836. -                       uiSetRoundBox(UI_CNR_TOP_LEFT | UI_CNR_TOP_RIGHT | UI_CNR_BOTTOM_LEFT); // round all corners except lower right
  837. -                       uiDrawBox(GL_LINE_LOOP, rct->xmin, rct->ymin, rct->xmax, rct->ymax, BASIS_RAD);
  838. -                      
  839. +              
  840. +               if (node->flag & NODE_ACTIVE)
  841. +                       UI_ThemeColorShadeAlpha(TH_ACTIVE, 0, -40);
  842. +               else
  843. +                       UI_ThemeColorShadeAlpha(TH_SELECT, 0, -40);
  844. +               uiSetRoundBox(UI_CNR_ALL); // round all corners except lower right
  845. +               uiDrawBox(GL_LINE_LOOP, rct->xmin, rct->ymin, rct->xmax, rct->ymax, BASIS_RAD);
  846. +              
  847.                 glDisable( GL_LINE_SMOOTH );
  848.                 glDisable(GL_BLEND);
  849.         }
  850. @@ -772,10 +863,15 @@
  851.         float socket_size= NODE_SOCKSIZE*U.dpi/72;
  852.         int color_id= node_get_colorid(node);
  853.         char showname[128];     /* 128 is used below */
  854. +       float shadowalpha;
  855.         
  856.         /* shadow */
  857.         uiSetRoundBox(UI_CNR_ALL);
  858. -       ui_dropshadow(rct, hiddenrad, snode->aspect, node->flag & SELECT);
  859. +       if (node->flag & NODE_CUSTOM_COLOR)
  860. +               shadowalpha = node->color[3];
  861. +       else
  862. +               shadowalpha = 1.0f;
  863. +       ui_dropshadow(rct, hiddenrad, snode->aspect, node->flag & SELECT, shadowalpha);
  864.  
  865.         /* body */
  866.         UI_ThemeColor(color_id);
  867. @@ -787,19 +883,20 @@
  868.         if ( node->flag & (NODE_ACTIVE|SELECT) ) {
  869.                 glEnable(GL_BLEND);
  870.                 glEnable( GL_LINE_SMOOTH );
  871. -                       /* using different shades of TH_TEXT_HI for the empasis, like triangle */
  872. -                       if ( node->flag & NODE_ACTIVE )
  873. -                               UI_ThemeColorShadeAlpha(TH_TEXT_HI, 0, -40);
  874. -                       else
  875. -                               UI_ThemeColorShadeAlpha(TH_TEXT_HI, -20, -120);
  876. -                       uiDrawBox(GL_LINE_LOOP, rct->xmin, rct->ymin, rct->xmax, rct->ymax, hiddenrad);
  877. +              
  878. +               if (node->flag & NODE_ACTIVE)
  879. +                       UI_ThemeColorShadeAlpha(TH_ACTIVE, 0, -40);
  880. +               else
  881. +                       UI_ThemeColorShadeAlpha(TH_SELECT, 0, -40);
  882. +               uiDrawBox(GL_LINE_LOOP, rct->xmin, rct->ymin, rct->xmax, rct->ymax, hiddenrad);
  883. +              
  884.                 glDisable( GL_LINE_SMOOTH );
  885.                 glDisable(GL_BLEND);
  886.         }
  887.         
  888.         /* title */
  889.         if (node->flag & SELECT)
  890. -               UI_ThemeColor(TH_TEXT_HI);
  891. +               UI_ThemeColor(TH_SELECT);
  892.         else
  893.                 UI_ThemeColorBlendShade(TH_TEXT, color_id, 0.4f, 10);
  894.         
  895. @@ -823,7 +920,7 @@
  896.                 node_draw_mute_line(&ar->v2d, snode, node);     
  897.         
  898.         if (node->flag & SELECT)
  899. -               UI_ThemeColor(TH_TEXT_HI);
  900. +               UI_ThemeColor(TH_SELECT);
  901.         else
  902.                 UI_ThemeColor(TH_TEXT);
  903.         
  904. @@ -864,6 +961,69 @@
  905.         node->block= NULL;
  906.  }
  907.  
  908. +int node_get_resize_cursor(int directions)
  909. +{
  910. +       if (directions==0)
  911. +               return CURSOR_STD;
  912. +       else if ((directions & ~(NODE_RESIZE_TOP|NODE_RESIZE_BOTTOM))==0)
  913. +               return BC_NS_SCROLLCURSOR;
  914. +       else if ((directions & ~(NODE_RESIZE_RIGHT|NODE_RESIZE_LEFT))==0)
  915. +               return BC_EW_SCROLLCURSOR;
  916. +       else
  917. +               return BC_NSEW_SCROLLCURSOR;
  918. +}
  919. +
  920. +static void node_set_cursor(bContext *C)
  921. +{
  922. +       SpaceNode *snode = CTX_wm_space_node(C);
  923. +       bNodeTree *ntree = snode->edittree;
  924. +       bNode *node;
  925. +       bNodeSocket *sock;
  926. +       int cursor = CURSOR_STD;
  927. +       
  928. +       if (!ntree)
  929. +               return;
  930. +       
  931. +       if (node_find_indicated_socket(snode, &node, &sock, SOCK_IN|SOCK_OUT)) {
  932. +               /* pass */
  933. +       }
  934. +       else {
  935. +               /* check nodes front to back */
  936. +               for (node=ntree->nodes.last; node; node=node->prev) {
  937. +                       if (BLI_in_rctf(&node->totr, snode->mx, snode->my))
  938. +                               break;  /* first hit on node stops */
  939. +               }
  940. +               if (node) {
  941. +                       int dir = node->typeinfo->resize_area_func(node, snode->mx, snode->my);
  942. +                       cursor = node_get_resize_cursor(dir);
  943. +               }
  944. +       }
  945. +       
  946. +       WM_cursor_set(CTX_wm_window(C), cursor);
  947. +}
  948. +
  949. +int node_ui_handler(bContext *C, wmEvent *event, void *UNUSED(userdata))
  950. +{
  951. +       SpaceNode *snode = CTX_wm_space_node(C);
  952. +       ARegion *ar = CTX_wm_region(C);
  953. +       int retval = WM_UI_HANDLER_CONTINUE;
  954. +       
  955. +       if (!ar || (ar->regiontype != RGN_TYPE_WINDOW))
  956. +               return retval;
  957. +       
  958. +       if (event->type == MOUSEMOVE) {
  959. +               /* convert mouse coordinates to v2d space */
  960. +               UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &snode->mx, &snode->my);
  961. +               node_set_cursor(C);
  962. +       }
  963. +       
  964. +       return retval;
  965. +}
  966. +
  967. +void node_ui_handler_remove(bContext *UNUSED(C), void *UNUSED(userdata))
  968. +{
  969. +}
  970. +
  971.  void node_draw_default(const bContext *C, ARegion *ar, SpaceNode *snode, bNodeTree *ntree, bNode *node)
  972.  {
  973.         if (node->flag & NODE_HIDDEN)
  974. @@ -882,7 +1042,8 @@
  975.  {
  976.         bNode *node;
  977.         
  978. -       for (node= ntree->nodes.first; node; node= node->next) {
  979. +       /* update nodes front to back, so children sizes get updated before parents */
  980. +       for (node= ntree->nodes.last; node; node= node->prev) {
  981.                 /* XXX little hack */
  982.                 node->locx += offsetx;
  983.                 node->locy += offsety;
  984. @@ -908,6 +1069,14 @@
  985.         
  986.         if (ntree==NULL) return;                /* groups... */
  987.         
  988. +       /* draw background nodes, last nodes in front */
  989. +       for (a=0, node= ntree->nodes.first; node; node=node->next, a++) {
  990. +               if (!(node->flag & NODE_BACKGROUND))
  991. +                       continue;
  992. +               node->nr= a;            /* index of node in list, used for exec event code */
  993. +               node_draw(C, ar, snode, ntree, node);
  994. +       }
  995. +       
  996.         /* node lines */
  997.         glEnable(GL_BLEND);
  998.         glEnable(GL_LINE_SMOOTH);
  999. @@ -916,8 +1085,10 @@
  1000.         glDisable(GL_LINE_SMOOTH);
  1001.         glDisable(GL_BLEND);
  1002.         
  1003. -       /* draw nodes, last nodes in front */
  1004. +       /* draw foreground nodes, last nodes in front */
  1005.         for (a=0, node= ntree->nodes.first; node; node=node->next, a++) {
  1006. +               if (node->flag & NODE_BACKGROUND)
  1007. +                       continue;
  1008.                 node->nr= a;            /* index of node in list, used for exec event code */
  1009.                 node_draw(C, ar, snode, ntree, node);
  1010.         }
  1011. Index: source/blender/editors/space_node/node_edit.c
  1012. ===================================================================
  1013. --- source/blender/editors/space_node/node_edit.c       (revision 45822)
  1014. +++ source/blender/editors/space_node/node_edit.c       (working copy)
  1015. @@ -693,53 +693,13 @@
  1016.         }
  1017.  }
  1018.  
  1019. -static int inside_rctf(rctf *bounds, rctf *rect)
  1020. +void ED_node_post_apply_transform(bContext *UNUSED(C), bNodeTree *UNUSED(ntree))
  1021.  {
  1022. -       return (bounds->xmin <= rect->xmin && bounds->xmax >= rect->xmax
  1023. -                       && bounds->ymin <= rect->ymin && bounds->ymax >= rect->ymax);
  1024. -}
  1025. -
  1026. -static void node_frame_attach_nodes(bNodeTree *UNUSED(ntree), bNode *frame)
  1027. -{
  1028. -       bNode *node;
  1029. -       
  1030. -       /* only check nodes on top of the frame for attaching */
  1031. -       for (node=frame->next; node; node=node->next) {
  1032. -               if (node->parent==frame) {
  1033. -                       /* detach nodes that went outside the frame */
  1034. -                       if (!inside_rctf(&frame->totr, &node->totr))
  1035. -                               nodeDetachNode(node);
  1036. -               }
  1037. -               else if (node->flag & NODE_SELECT && node->parent==NULL) {
  1038. -                       /* attach selected, still unparented nodes */
  1039. -                       if (inside_rctf(&frame->totr, &node->totr))
  1040. -                               nodeAttachNode(node, frame);
  1041. -               }
  1042. -       }
  1043. -}
  1044. -
  1045. -void ED_node_update_hierarchy(bContext *UNUSED(C), bNodeTree *ntree)
  1046. -{
  1047. -       bNode *node;
  1048. -       
  1049.         /* XXX This does not work due to layout functions relying on node->block,
  1050.          * which only exists during actual drawing. Can we rely on valid totr rects?
  1051.          */
  1052.         /* make sure nodes have correct bounding boxes after transform */
  1053. -//     node_update_nodetree(C, ntree, 0.0f, 0.0f);
  1054. -       
  1055. -       /* all selected nodes are re-parented */
  1056. -       for (node=ntree->nodes.last; node; node=node->prev) {
  1057. -               if (node->flag & NODE_SELECT && node->parent)
  1058. -                       nodeDetachNode(node);
  1059. -       }
  1060. -       
  1061. -       /* update higher Z-level nodes first */
  1062. -       for (node=ntree->nodes.last; node; node=node->prev) {
  1063. -               /* XXX callback? */
  1064. -               if (node->type==NODE_FRAME)
  1065. -                       node_frame_attach_nodes(ntree, node);
  1066. -       }
  1067. +       /* node_update_nodetree(C, ntree, 0.0f, 0.0f); */
  1068.  }
  1069.  
  1070.  /* ***************** generic operator functions for nodes ***************** */
  1071. @@ -1495,36 +1455,130 @@
  1072.  
  1073.  typedef struct NodeSizeWidget {
  1074.         float mxstart, mystart;
  1075. +       float oldlocx, oldlocy;
  1076. +       float oldoffsetx, oldoffsety;
  1077.         float oldwidth, oldheight;
  1078.         float oldminiwidth;
  1079. +       int directions;
  1080.  } NodeSizeWidget;
  1081.  
  1082. +static void node_resize_init(bContext *C, wmOperator *op, wmEvent *UNUSED(event), bNode *node, int dir)
  1083. +{
  1084. +       SpaceNode *snode = CTX_wm_space_node(C);
  1085. +       
  1086. +       NodeSizeWidget *nsw= MEM_callocN(sizeof(NodeSizeWidget), "size widget op data");
  1087. +       
  1088. +       op->customdata= nsw;
  1089. +       nsw->mxstart= snode->mx;
  1090. +       nsw->mystart= snode->my;
  1091. +       
  1092. +       /* store old */
  1093. +       nsw->oldlocx= node->locx;
  1094. +       nsw->oldlocy= node->locy;
  1095. +       nsw->oldoffsetx= node->offsetx;
  1096. +       nsw->oldoffsety= node->offsety;
  1097. +       nsw->oldwidth= node->width;
  1098. +       nsw->oldheight= node->height;
  1099. +       nsw->oldminiwidth= node->miniwidth;
  1100. +       nsw->directions = dir;
  1101. +       
  1102. +       WM_cursor_modal(CTX_wm_window(C), node_get_resize_cursor(dir));
  1103. +       /* add modal handler */
  1104. +       WM_event_add_modal_handler(C, op);
  1105. +}
  1106. +
  1107. +static void node_resize_exit(bContext *C, wmOperator *op, int UNUSED(cancel))
  1108. +{
  1109. +       WM_cursor_restore(CTX_wm_window(C));
  1110. +       
  1111. +       MEM_freeN(op->customdata);
  1112. +       op->customdata= NULL;
  1113. +}
  1114. +
  1115.  static int node_resize_modal(bContext *C, wmOperator *op, wmEvent *event)
  1116.  {
  1117.         SpaceNode *snode= CTX_wm_space_node(C);
  1118.         ARegion *ar= CTX_wm_region(C);
  1119.         bNode *node= editnode_get_active(snode->edittree);
  1120.         NodeSizeWidget *nsw= op->customdata;
  1121. -       float mx, my;
  1122. +       float mx, my, dx, dy;
  1123.         
  1124.         switch (event->type) {
  1125.                 case MOUSEMOVE:
  1126.                        
  1127. -                       UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1],
  1128. -                                                                        &mx, &my);
  1129. +                       UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &mx, &my);
  1130. +                       dx = mx - nsw->mxstart;
  1131. +                       dy = my - nsw->mystart;
  1132.                        
  1133.                         if (node) {
  1134.                                 if (node->flag & NODE_HIDDEN) {
  1135. -                                       node->miniwidth= nsw->oldminiwidth + mx - nsw->mxstart;
  1136. -                                       CLAMP(node->miniwidth, 0.0f, 100.0f);
  1137. +                                       float widthmin = 0.0f;
  1138. +                                       float widthmax = 100.0f;
  1139. +                                       if (nsw->directions & NODE_RESIZE_RIGHT) {
  1140. +                                               node->miniwidth= nsw->oldminiwidth + dx;
  1141. +                                               CLAMP(node->miniwidth, widthmin, widthmax);
  1142. +                                       }
  1143. +                                       if (nsw->directions & NODE_RESIZE_LEFT) {
  1144. +                                               float locmax = nsw->oldlocx + nsw->oldminiwidth;
  1145. +                                              
  1146. +                                               node->locx= nsw->oldlocx + dx;
  1147. +                                               CLAMP(node->locx, locmax - widthmax, locmax - widthmin);
  1148. +                                               node->miniwidth= locmax - node->locx;
  1149. +                                       }
  1150.                                 }
  1151.                                 else {
  1152. -                                       node->width= nsw->oldwidth + mx - nsw->mxstart;
  1153. -                                       CLAMP(node->width, UI_DPI_FAC*node->typeinfo->minwidth, UI_DPI_FAC*node->typeinfo->maxwidth);
  1154. +                                       float widthmin = UI_DPI_FAC*node->typeinfo->minwidth;
  1155. +                                       float widthmax = UI_DPI_FAC*node->typeinfo->maxwidth;
  1156. +                                       if (nsw->directions & NODE_RESIZE_RIGHT) {
  1157. +                                               node->width= nsw->oldwidth + dx;
  1158. +                                               CLAMP(node->width, widthmin, widthmax);
  1159. +                                       }
  1160. +                                       if (nsw->directions & NODE_RESIZE_LEFT) {
  1161. +                                               float locmax = nsw->oldlocx + nsw->oldwidth;
  1162. +                                              
  1163. +                                               node->locx= nsw->oldlocx + dx;
  1164. +                                               CLAMP(node->locx, locmax - widthmax, locmax - widthmin);
  1165. +                                               node->width= locmax - node->locx;
  1166. +                                       }
  1167.                                 }
  1168. +                      
  1169.                                 /* height works the other way round ... */
  1170. -                               node->height= nsw->oldheight - my + nsw->mystart;
  1171. -                               CLAMP(node->height, node->typeinfo->minheight, node->typeinfo->maxheight);
  1172. +                               {
  1173. +                                       float heightmin = UI_DPI_FAC*node->typeinfo->minheight;
  1174. +                                       float heightmax = UI_DPI_FAC*node->typeinfo->maxheight;
  1175. +                                       if (nsw->directions & NODE_RESIZE_TOP) {
  1176. +                                               float locmin = nsw->oldlocy - nsw->oldheight;
  1177. +                                              
  1178. +                                               node->locy= nsw->oldlocy + dy;
  1179. +                                               CLAMP(node->locy, locmin + heightmin, locmin + heightmax);
  1180. +                                               node->height= node->locy - locmin;
  1181. +                                       }
  1182. +                                       if (nsw->directions & NODE_RESIZE_BOTTOM) {
  1183. +                                               node->height= nsw->oldheight - dy;
  1184. +                                               CLAMP(node->height, heightmin, heightmax);
  1185. +                                       }
  1186. +                               }
  1187. +                              
  1188. +                               /* XXX make callback? */
  1189. +                               if (node->type == NODE_FRAME) {
  1190. +                                       /* keep the offset symmetric around center point */
  1191. +                                       if (nsw->directions & NODE_RESIZE_LEFT) {
  1192. +                                               node->locx = nsw->oldlocx + 0.5f*dx;
  1193. +                                               node->offsetx = nsw->oldoffsetx + 0.5f*dx;
  1194. +                                       }
  1195. +                                       if (nsw->directions & NODE_RESIZE_RIGHT) {
  1196. +                                               node->locx = nsw->oldlocx + 0.5f*dx;
  1197. +                                               node->offsetx = nsw->oldoffsetx - 0.5f*dx;
  1198. +                                       }
  1199. +                                       if (nsw->directions & NODE_RESIZE_TOP) {
  1200. +                                               node->locy = nsw->oldlocy + 0.5f*dy;
  1201. +                                               node->offsety = nsw->oldoffsety + 0.5f*dy;
  1202. +                                       }
  1203. +                                       if (nsw->directions & NODE_RESIZE_BOTTOM) {
  1204. +                                               node->locy = nsw->oldlocy + 0.5f*dy;
  1205. +                                               node->offsety = nsw->oldoffsety - 0.5f*dy;
  1206. +                                       }
  1207. +                               }
  1208.                         }
  1209.                                
  1210.                         ED_region_tag_redraw(ar);
  1211. @@ -1535,10 +1589,8 @@
  1212.                 case MIDDLEMOUSE:
  1213.                 case RIGHTMOUSE:
  1214.                        
  1215. -                       MEM_freeN(nsw);
  1216. -                       op->customdata= NULL;
  1217. -                      
  1218. -                       ED_node_update_hierarchy(C, snode->edittree);
  1219. +                       node_resize_exit(C, op, 0);
  1220. +                       ED_node_post_apply_transform(C, snode->edittree);
  1221.                        
  1222.                         return OPERATOR_FINISHED;
  1223.         }
  1224. @@ -1551,37 +1603,24 @@
  1225.         SpaceNode *snode= CTX_wm_space_node(C);
  1226.         ARegion *ar= CTX_wm_region(C);
  1227.         bNode *node= editnode_get_active(snode->edittree);
  1228. +       int dir;
  1229.         
  1230.         if (node) {
  1231.                 /* convert mouse coordinates to v2d space */
  1232.                 UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1],
  1233. -                                                                &snode->mx, &snode->my);
  1234. -              
  1235. -               if (node->typeinfo->resize_area_func(node, snode->mx, snode->my)) {
  1236. -                       NodeSizeWidget *nsw= MEM_callocN(sizeof(NodeSizeWidget), "size widget op data");
  1237. -                      
  1238. -                       op->customdata= nsw;
  1239. -                       nsw->mxstart= snode->mx;
  1240. -                       nsw->mystart= snode->my;
  1241. -                      
  1242. -                       /* store old */
  1243. -                       nsw->oldwidth= node->width;
  1244. -                       nsw->oldheight= node->height;
  1245. -                       nsw->oldminiwidth= node->miniwidth;
  1246. -                      
  1247. -                       /* add modal handler */
  1248. -                       WM_event_add_modal_handler(C, op);
  1249. -
  1250. +                                        &snode->mx, &snode->my);
  1251. +               dir = node->typeinfo->resize_area_func(node, snode->mx, snode->my);
  1252. +               if (dir != 0) {
  1253. +                       node_resize_init(C, op, event, node, dir);
  1254.                         return OPERATOR_RUNNING_MODAL;
  1255.                 }
  1256.         }
  1257.         return OPERATOR_CANCELLED|OPERATOR_PASS_THROUGH;
  1258.  }
  1259.  
  1260. -static int node_resize_cancel(bContext *UNUSED(C), wmOperator *op)
  1261. +static int node_resize_cancel(bContext *C, wmOperator *op)
  1262.  {
  1263. -       MEM_freeN(op->customdata);
  1264. -       op->customdata= NULL;
  1265. +       node_resize_exit(C, op, 1);
  1266.  
  1267.         return OPERATOR_CANCELLED;
  1268.  }
  1269. @@ -2155,6 +2194,27 @@
  1270.  
  1271.  /* ****************** Duplicate *********************** */
  1272.  
  1273. +static void node_duplicate_reparent_recursive(bNode *node)
  1274. +{
  1275. +       bNode *parent;
  1276. +       
  1277. +       node->flag |= NODE_TEST;
  1278. +       
  1279. +       /* find first selected parent */
  1280. +       for (parent=node->parent; parent; parent=parent->parent) {
  1281. +               if (parent->flag & SELECT) {
  1282. +                       if (!(parent->flag & NODE_TEST))
  1283. +                               node_duplicate_reparent_recursive(parent);
  1284. +                       break;
  1285. +               }
  1286. +       }
  1287. +       /* reparent node copy to parent copy */
  1288. +       if (parent) {
  1289. +               nodeDetachNode(node->new_node);
  1290. +               nodeAttachNode(node->new_node, parent->new_node);
  1291. +       }
  1292. +}
  1293. +
  1294.  static int node_duplicate_exec(bContext *C, wmOperator *op)
  1295.  {
  1296.         SpaceNode *snode= CTX_wm_space_node(C);
  1297. @@ -2216,6 +2276,19 @@
  1298.                         break;
  1299.         }
  1300.         
  1301. +       /* clear flags for recursive depth-first iteration */
  1302. +       for (node= ntree->nodes.first; node; node= node->next)
  1303. +               node->flag &= ~NODE_TEST;
  1304. +       /* reparent copied nodes */
  1305. +       for (node= ntree->nodes.first; node; node= node->next) {
  1306. +               if ((node->flag & SELECT) && !(node->flag & NODE_TEST))
  1307. +                       node_duplicate_reparent_recursive(node);
  1308. +              
  1309. +               /* only has to check old nodes */
  1310. +               if (node==lastnode)
  1311. +                       break;
  1312. +       }
  1313. +       
  1314.         /* deselect old nodes, select the copies instead */
  1315.         for (node= ntree->nodes.first; node; node= node->next) {
  1316.                 if (node->flag & SELECT) {
  1317. @@ -3650,3 +3723,218 @@
  1318.         /* flags */
  1319.         ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
  1320.  }
  1321. +
  1322. +/* ****************** Copy Node Color ******************* */
  1323. +
  1324. +static int node_copy_color_exec(bContext *C, wmOperator *UNUSED(op))
  1325. +{
  1326. +       SpaceNode *snode = CTX_wm_space_node(C);
  1327. +       bNodeTree *ntree = snode->edittree;
  1328. +       bNode *node, *tnode;
  1329. +       
  1330. +       if (!ntree)
  1331. +               return OPERATOR_CANCELLED;
  1332. +       node = nodeGetActive(ntree);
  1333. +       if (!node)
  1334. +               return OPERATOR_CANCELLED;
  1335. +       
  1336. +       for (tnode=ntree->nodes.first; tnode; tnode=tnode->next) {
  1337. +               if (tnode->flag & NODE_SELECT && tnode != node) {
  1338. +                       if (node->flag & NODE_CUSTOM_COLOR) {
  1339. +                               tnode->flag |= NODE_CUSTOM_COLOR;
  1340. +                               copy_v3_v3(tnode->color, node->color);
  1341. +                       }
  1342. +                       else
  1343. +                               tnode->flag &= ~NODE_CUSTOM_COLOR;
  1344. +               }
  1345. +       }
  1346. +
  1347. +       WM_event_add_notifier(C, NC_NODE|ND_DISPLAY, NULL);
  1348. +
  1349. +       return OPERATOR_FINISHED;
  1350. +}
  1351. +
  1352. +void NODE_OT_node_copy_color(wmOperatorType *ot)
  1353. +{
  1354. +       /* identifiers */
  1355. +       ot->name = "Copy Color";
  1356. +       ot->description = "Copy color to all selected nodes";
  1357. +       ot->idname = "NODE_OT_node_copy_color";
  1358. +
  1359. +       /* api callbacks */
  1360. +       ot->exec = node_copy_color_exec;
  1361. +       ot->poll = ED_operator_node_active;
  1362. +
  1363. +       /* flags */
  1364. +       ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
  1365. +}
  1366. +
  1367. +/* ****************** Set Parent ******************* */
  1368. +
  1369. +static int node_parent_set_exec(bContext *C, wmOperator *UNUSED(op))
  1370. +{
  1371. +       SpaceNode *snode = CTX_wm_space_node(C);
  1372. +       bNodeTree *ntree = snode->edittree;
  1373. +       bNode *frame = nodeGetActive(ntree), *node;
  1374. +       if (!frame || frame->type != NODE_FRAME)
  1375. +               return OPERATOR_CANCELLED;
  1376. +       
  1377. +       for (node=ntree->nodes.first; node; node=node->next) {
  1378. +               if (node == frame)
  1379. +                       continue;
  1380. +               if (node->flag & NODE_SELECT) {
  1381. +                       nodeDetachNode(node);
  1382. +                       nodeAttachNode(node, frame);
  1383. +               }
  1384. +       }
  1385. +
  1386. +       ED_node_sort(ntree);
  1387. +       WM_event_add_notifier(C, NC_NODE|ND_DISPLAY, NULL);
  1388. +
  1389. +       return OPERATOR_FINISHED;
  1390. +}
  1391. +
  1392. +void NODE_OT_parent_set(wmOperatorType *ot)
  1393. +{
  1394. +       /* identifiers */
  1395. +       ot->name = "Make Parent";
  1396. +       ot->description = "Attach selected nodes";
  1397. +       ot->idname = "NODE_OT_parent_set";
  1398. +
  1399. +       /* api callbacks */
  1400. +       ot->exec = node_parent_set_exec;
  1401. +       ot->poll = ED_operator_node_active;
  1402. +
  1403. +       /* flags */
  1404. +       ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
  1405. +}
  1406. +
  1407. +/* ****************** Clear Parent ******************* */
  1408. +
  1409. +static int node_parent_clear_exec(bContext *C, wmOperator *UNUSED(op))
  1410. +{
  1411. +       SpaceNode *snode = CTX_wm_space_node(C);
  1412. +       bNodeTree *ntree = snode->edittree;
  1413. +       bNode *node;
  1414. +       
  1415. +       for (node=ntree->nodes.first; node; node=node->next) {
  1416. +               if (node->flag & NODE_SELECT) {
  1417. +                       nodeDetachNode(node);
  1418. +               }
  1419. +       }
  1420. +
  1421. +       ED_node_sort(ntree);
  1422. +       WM_event_add_notifier(C, NC_NODE|ND_DISPLAY, NULL);
  1423. +
  1424. +       return OPERATOR_FINISHED;
  1425. +}
  1426. +
  1427. +void NODE_OT_parent_clear(wmOperatorType *ot)
  1428. +{
  1429. +       /* identifiers */
  1430. +       ot->name = "Clear Parent";
  1431. +       ot->description = "Detach selected nodes";
  1432. +       ot->idname = "NODE_OT_parent_clear";
  1433. +
  1434. +       /* api callbacks */
  1435. +       ot->exec = node_parent_clear_exec;
  1436. +       ot->poll = ED_operator_node_active;
  1437. +
  1438. +       /* flags */
  1439. +       ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
  1440. +}
  1441. +
  1442. +/* ****************** Join Nodes ******************* */
  1443. +
  1444. +/* tags for depth-first search */
  1445. +#define NODE_JOIN_DONE                 1
  1446. +#define NODE_JOIN_IS_DESCENDANT        2
  1447. +
  1448. +static void node_join_attach_recursive(bNode *node, bNode *frame)
  1449. +{
  1450. +       node->done |= NODE_JOIN_DONE;
  1451. +       
  1452. +       if (node == frame) {
  1453. +               node->done |= NODE_JOIN_IS_DESCENDANT;
  1454. +       }
  1455. +       else if (node->parent) {
  1456. +               /* call recursively */
  1457. +               if (!(node->parent->done & NODE_JOIN_DONE))
  1458. +                       node_join_attach_recursive(node->parent, frame);
  1459. +              
  1460. +               /* in any case: if the parent is a descendant, so is the child */
  1461. +               if (node->parent->done & NODE_JOIN_IS_DESCENDANT)
  1462. +                       node->done |= NODE_JOIN_IS_DESCENDANT;
  1463. +              
  1464. +               if (node->flag & NODE_TEST) {
  1465. +                       /* if parent is not an decendant of the frame, reattach the node */
  1466. +                       if (!(node->parent->done & NODE_JOIN_IS_DESCENDANT)) {
  1467. +                               nodeDetachNode(node);
  1468. +                               nodeAttachNode(node, frame);
  1469. +                               node->done |= NODE_JOIN_IS_DESCENDANT;
  1470. +                       }
  1471. +               }
  1472. +       }
  1473. +       else if (node->flag & NODE_TEST) {
  1474. +               nodeAttachNode(node, frame);
  1475. +               node->done |= NODE_JOIN_IS_DESCENDANT;
  1476. +       }
  1477. +}
  1478. +
  1479. +static int node_join_exec(bContext *C, wmOperator *UNUSED(op))
  1480. +{
  1481. +       SpaceNode *snode = CTX_wm_space_node(C);
  1482. +       Main *bmain = CTX_data_main(C);
  1483. +       Scene *scene = CTX_data_scene(C);
  1484. +       bNodeTree *ntree = snode->edittree;
  1485. +       bNode *node, *frame;
  1486. +       bNodeTemplate ntemp;
  1487. +       
  1488. +       /* XXX save selection: node_add_node call below sets the new frame as single active+selected node */
  1489. +       for (node=ntree->nodes.first; node; node=node->next) {
  1490. +               if (node->flag & NODE_SELECT)
  1491. +                       node->flag |= NODE_TEST;
  1492. +               else
  1493. +                       node->flag &= ~NODE_TEST;
  1494. +       }
  1495. +       
  1496. +       ntemp.main = bmain;
  1497. +       ntemp.scene = scene;
  1498. +       ntemp.type = NODE_FRAME;
  1499. +       frame = node_add_node(snode, bmain, scene, &ntemp, 0.0f, 0.0f);
  1500. +       
  1501. +       /* reset tags */
  1502. +       for (node=ntree->nodes.first; node; node=node->next)
  1503. +               node->done = 0;
  1504. +       
  1505. +       for (node=ntree->nodes.first; node; node=node->next) {
  1506. +               if (!(node->done & NODE_JOIN_DONE))
  1507. +                       node_join_attach_recursive(node, frame);
  1508. +       }
  1509. +
  1510. +       /* restore selection */
  1511. +       for (node=ntree->nodes.first; node; node=node->next) {
  1512. +               if (node->flag & NODE_TEST)
  1513. +                       node->flag |= NODE_SELECT;
  1514. +       }
  1515. +
  1516. +       ED_node_sort(ntree);
  1517. +       WM_event_add_notifier(C, NC_NODE|ND_DISPLAY, NULL);
  1518. +
  1519. +       return OPERATOR_FINISHED;
  1520. +}
  1521. +
  1522. +void NODE_OT_join(wmOperatorType *ot)
  1523. +{
  1524. +       /* identifiers */
  1525. +       ot->name = "Join Nodes";
  1526. +       ot->description = "Attaches selected nodes to a new common frame";
  1527. +       ot->idname = "NODE_OT_join";
  1528. +
  1529. +       /* api callbacks */
  1530. +       ot->exec = node_join_exec;
  1531. +       ot->poll = ED_operator_node_active;
  1532. +
  1533. +       /* flags */
  1534. +       ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
  1535. +}
  1536. Index: source/blender/editors/space_node/node_intern.h
  1537. ===================================================================
  1538. --- source/blender/editors/space_node/node_intern.h     (revision 45822)
  1539. +++ source/blender/editors/space_node/node_intern.h     (working copy)
  1540. @@ -41,6 +41,7 @@
  1541.  struct View2D;
  1542.  struct bContext;
  1543.  struct wmWindowManager;
  1544. +struct wmEvent;
  1545.  struct bNodeTemplate;
  1546.  struct bNode;
  1547.  struct bNodeSocket;
  1548. @@ -65,13 +66,18 @@
  1549.  void node_menus_register(void);
  1550.  
  1551.  /* node_draw.c */
  1552. +int node_get_colorid(struct bNode *node);
  1553.  void node_socket_circle_draw(struct bNodeTree *ntree, struct bNodeSocket *sock, float size);
  1554. +int node_get_resize_cursor(int directions);
  1555.  void node_draw_default(const struct bContext *C, struct ARegion *ar, struct SpaceNode *snode, struct bNodeTree *ntree, struct bNode *node);
  1556.  void node_update_default(const struct bContext *C, struct bNodeTree *ntree, struct bNode *node);
  1557.  void node_update_nodetree(const struct bContext *C, struct bNodeTree *ntree, float offsetx, float offsety);
  1558.  void node_draw_nodetree(const struct bContext *C, struct ARegion *ar, struct SpaceNode *snode, struct bNodeTree *ntree);
  1559.  void drawnodespace(const bContext *C, ARegion *ar, View2D *v2d);
  1560.  
  1561. +int node_ui_handler(struct bContext *C, struct wmEvent *event, void *userdata);
  1562. +void node_ui_handler_remove(struct bContext *C, void *userdata);
  1563. +
  1564.  /* node_buttons.c */
  1565.  void node_buttons_register(struct ARegionType *art);
  1566.  void NODE_OT_properties(struct wmOperatorType *ot);
  1567. @@ -149,6 +155,7 @@
  1568.  void NODE_OT_hide_socket_toggle(struct wmOperatorType *ot);
  1569.  void NODE_OT_preview_toggle(struct wmOperatorType *ot);
  1570.  void NODE_OT_options_toggle(struct wmOperatorType *ot);
  1571. +void NODE_OT_node_copy_color(struct wmOperatorType *ot);
  1572.  
  1573.  void NODE_OT_show_cyclic_dependencies(struct wmOperatorType *ot);
  1574.  void NODE_OT_link_viewer(struct wmOperatorType *ot);
  1575. @@ -167,6 +174,10 @@
  1576.  void NODE_OT_output_file_add_socket(struct wmOperatorType *ot);
  1577.  void NODE_OT_output_file_remove_active_socket(struct wmOperatorType *ot);
  1578.  
  1579. +void NODE_OT_parent_set(struct wmOperatorType *ot);
  1580. +void NODE_OT_parent_clear(struct wmOperatorType *ot);
  1581. +void NODE_OT_join(struct wmOperatorType *ot);
  1582. +
  1583.  extern const char *node_context_dir[];
  1584.  
  1585.  // XXXXXX
  1586. Index: source/blender/editors/space_node/node_ops.c
  1587. ===================================================================
  1588. --- source/blender/editors/space_node/node_ops.c        (revision 45822)
  1589. +++ source/blender/editors/space_node/node_ops.c        (working copy)
  1590. @@ -68,6 +68,7 @@
  1591.         WM_operatortype_append(NODE_OT_options_toggle);
  1592.         WM_operatortype_append(NODE_OT_hide_socket_toggle);
  1593.         WM_operatortype_append(NODE_OT_show_cyclic_dependencies);
  1594. +       WM_operatortype_append(NODE_OT_node_copy_color);
  1595.         
  1596.         WM_operatortype_append(NODE_OT_duplicate);
  1597.         WM_operatortype_append(NODE_OT_delete);
  1598. @@ -103,6 +104,10 @@
  1599.         
  1600.         WM_operatortype_append(NODE_OT_output_file_add_socket);
  1601.         WM_operatortype_append(NODE_OT_output_file_remove_active_socket);
  1602. +       
  1603. +       WM_operatortype_append(NODE_OT_parent_set);
  1604. +       WM_operatortype_append(NODE_OT_parent_clear);
  1605. +       WM_operatortype_append(NODE_OT_join);
  1606.  }
  1607.  
  1608.  void ED_operatormacros_node(void)
  1609. @@ -186,6 +191,10 @@
  1610.         /* modified operator call for duplicating with input links */
  1611.         WM_keymap_add_item(keymap, "NODE_OT_duplicate_move_keep_inputs", DKEY, KM_PRESS, KM_SHIFT|KM_CTRL, 0);
  1612.         
  1613. +       WM_keymap_add_item(keymap, "NODE_OT_parent_set", PKEY, KM_PRESS, KM_CTRL, 0);
  1614. +       WM_keymap_add_item(keymap, "NODE_OT_parent_clear", PKEY, KM_PRESS, KM_ALT, 0);
  1615. +       WM_keymap_add_item(keymap, "NODE_OT_join", JKEY, KM_PRESS, KM_CTRL, 0);
  1616. +       
  1617.         WM_keymap_add_item(keymap, "NODE_OT_hide_toggle", HKEY, KM_PRESS, 0, 0);
  1618.         WM_keymap_add_item(keymap, "NODE_OT_mute_toggle", MKEY, KM_PRESS, 0, 0);
  1619.         WM_keymap_add_item(keymap, "NODE_OT_preview_toggle", HKEY, KM_PRESS, KM_SHIFT, 0);
  1620. Index: source/blender/editors/space_node/node_select.c
  1621. ===================================================================
  1622. --- source/blender/editors/space_node/node_select.c     (revision 45822)
  1623. +++ source/blender/editors/space_node/node_select.c     (working copy)
  1624. @@ -70,109 +70,6 @@
  1625.         return NULL;
  1626.  }
  1627.  
  1628. -static int compare_nodes(bNode *a, bNode *b)
  1629. -{
  1630. -       bNode *parent;
  1631. -       /* These tell if either the node or any of the parent nodes is selected.
  1632. -        * A selected parent means an unselected node is also in foreground!
  1633. -        */
  1634. -       int a_select=(a->flag & NODE_SELECT), b_select=(b->flag & NODE_SELECT);
  1635. -       int a_active=(a->flag & NODE_ACTIVE), b_active=(b->flag & NODE_ACTIVE);
  1636. -       
  1637. -       /* if one is an ancestor of the other */
  1638. -       /* XXX there might be a better sorting algorithm for stable topological sort, this is O(n^2) worst case */
  1639. -       for (parent = a->parent; parent; parent=parent->parent) {
  1640. -               /* if b is an ancestor, it is always behind a */
  1641. -               if (parent==b)
  1642. -                       return 1;
  1643. -               /* any selected ancestor moves the node forward */
  1644. -               if (parent->flag & NODE_ACTIVE)
  1645. -                       a_active = 1;
  1646. -               if (parent->flag & NODE_SELECT)
  1647. -                       a_select = 1;
  1648. -       }
  1649. -       for (parent = b->parent; parent; parent=parent->parent) {
  1650. -               /* if a is an ancestor, it is always behind b */
  1651. -               if (parent==a)
  1652. -                       return 0;
  1653. -               /* any selected ancestor moves the node forward */
  1654. -               if (parent->flag & NODE_ACTIVE)
  1655. -                       b_active = 1;
  1656. -               if (parent->flag & NODE_SELECT)
  1657. -                       b_select = 1;
  1658. -       }
  1659. -
  1660. -       /* if one of the nodes is in the background and the other not */
  1661. -       if ((a->flag & NODE_BACKGROUND) && !(b->flag & NODE_BACKGROUND))
  1662. -               return 0;
  1663. -       else if (!(a->flag & NODE_BACKGROUND) && (b->flag & NODE_BACKGROUND))
  1664. -               return 1;
  1665. -       
  1666. -       /* if one has a higher selection state (active > selected > nothing) */
  1667. -       if (!b_active && a_active)
  1668. -               return 1;
  1669. -       else if (!b_select && (a_active || a_select))
  1670. -               return 1;
  1671. -       
  1672. -       return 0;
  1673. -}
  1674. -
  1675. -/* Sorts nodes by selection: unselected nodes first, then selected,
  1676. - * then the active node at the very end. Relative order is kept intact!
  1677. - */
  1678. -static void node_sort(bNodeTree *ntree)
  1679. -{
  1680. -       /* merge sort is the algorithm of choice here */
  1681. -       bNode *first_a, *first_b, *node_a, *node_b, *tmp;
  1682. -       int totnodes= BLI_countlist(&ntree->nodes);
  1683. -       int k, a, b;
  1684. -       
  1685. -       k = 1;
  1686. -       while (k < totnodes) {
  1687. -               first_a = first_b = ntree->nodes.first;
  1688. -              
  1689. -               do {
  1690. -                       /* setup first_b pointer */
  1691. -                       for (b=0; b < k && first_b; ++b) {
  1692. -                               first_b = first_b->next;
  1693. -                       }
  1694. -                       /* all batches merged? */
  1695. -                       if (first_b==NULL)
  1696. -                               break;
  1697. -                      
  1698. -                       /* merge batches */
  1699. -                       node_a = first_a;
  1700. -                       node_b = first_b;
  1701. -                       a = b = 0;
  1702. -                       while (a < k && b < k && node_b) {
  1703. -                               if (compare_nodes(node_a, node_b)==0) {
  1704. -                                       node_a = node_a->next;
  1705. -                                       ++a;
  1706. -                               }
  1707. -                               else {
  1708. -                                       tmp = node_b;
  1709. -                                       node_b = node_b->next;
  1710. -                                       ++b;
  1711. -                                       BLI_remlink(&ntree->nodes, tmp);
  1712. -                                       BLI_insertlinkbefore(&ntree->nodes, node_a, tmp);
  1713. -                               }
  1714. -                       }
  1715. -
  1716. -                       /* setup first pointers for next batch */
  1717. -                       first_b = node_b;
  1718. -                       for (; b < k; ++b) {
  1719. -                               /* all nodes sorted? */
  1720. -                               if (first_b==NULL)
  1721. -                                       break;
  1722. -                               first_b = first_b->next;
  1723. -                       }
  1724. -                       first_a = first_b;
  1725. -               } while (first_b);
  1726. -              
  1727. -               k = k << 1;
  1728. -       }
  1729. -}
  1730. -
  1731.  void node_select(bNode *node)
  1732.  {
  1733.         node->flag |= SELECT;
  1734. @@ -407,7 +304,7 @@
  1735.         
  1736.         ED_node_set_active(bmain, snode->edittree, node);
  1737.         
  1738. -       node_sort(snode->edittree);
  1739. +       ED_node_sort(snode->edittree);
  1740.         
  1741.         WM_event_add_notifier(C, NC_NODE|NA_SELECTED, NULL);
  1742.  }
  1743. @@ -479,7 +376,7 @@
  1744.         
  1745.         /* update node order */
  1746.         if (selected)
  1747. -               node_sort(snode->edittree);
  1748. +               ED_node_sort(snode->edittree);
  1749.         
  1750.         return selected;
  1751.  }
  1752. @@ -573,7 +470,7 @@
  1753.                 }
  1754.         }
  1755.         
  1756. -       node_sort(snode->edittree);
  1757. +       ED_node_sort(snode->edittree);
  1758.         
  1759.         WM_event_add_notifier(C, NC_NODE|NA_SELECTED, NULL);
  1760.  
  1761. @@ -645,7 +542,7 @@
  1762.                         node_select(node);
  1763.         }
  1764.         
  1765. -       node_sort(snode->edittree);
  1766. +       ED_node_sort(snode->edittree);
  1767.         
  1768.         WM_event_add_notifier(C, NC_NODE|NA_SELECTED, NULL);
  1769.         return OPERATOR_FINISHED;
  1770. @@ -687,7 +584,7 @@
  1771.                         node_select(node);
  1772.         }
  1773.         
  1774. -       node_sort(snode->edittree);
  1775. +       ED_node_sort(snode->edittree);
  1776.         
  1777.         WM_event_add_notifier(C, NC_NODE|NA_SELECTED, NULL);
  1778.         return OPERATOR_FINISHED;
  1779. @@ -729,7 +626,7 @@
  1780.                         node_select(node);
  1781.         }
  1782.         
  1783. -       node_sort(snode->edittree);
  1784. +       ED_node_sort(snode->edittree);
  1785.         
  1786.         WM_event_add_notifier(C, NC_NODE|NA_SELECTED, NULL);
  1787.         return OPERATOR_FINISHED;
  1788. @@ -758,7 +655,7 @@
  1789.  
  1790.         node_select_same_type(snode);
  1791.  
  1792. -       node_sort(snode->edittree);
  1793. +       ED_node_sort(snode->edittree);
  1794.  
  1795.         WM_event_add_notifier(C, NC_NODE|NA_SELECTED, NULL);
  1796.         return OPERATOR_FINISHED;
  1797. @@ -787,7 +684,7 @@
  1798.  
  1799.         node_select_same_type_np(snode, 0);
  1800.  
  1801. -       node_sort(snode->edittree);
  1802. +       ED_node_sort(snode->edittree);
  1803.  
  1804.         WM_event_add_notifier(C, NC_NODE|NA_SELECTED, NULL);
  1805.  
  1806. @@ -815,7 +712,7 @@
  1807.  
  1808.         node_select_same_type_np(snode, 1);
  1809.  
  1810. -       node_sort(snode->edittree);
  1811. +       ED_node_sort(snode->edittree);
  1812.  
  1813.         WM_event_add_notifier(C, NC_NODE|NA_SELECTED, NULL);
  1814.         return OPERATOR_FINISHED;
  1815. Index: source/blender/editors/space_node/space_node.c
  1816. ===================================================================
  1817. --- source/blender/editors/space_node/space_node.c      (revision 45822)
  1818. +++ source/blender/editors/space_node/space_node.c      (working copy)
  1819. @@ -358,6 +358,8 @@
  1820.         lb = WM_dropboxmap_find("Node Editor", SPACE_NODE, RGN_TYPE_WINDOW);
  1821.         
  1822.         WM_event_add_dropbox_handler(&ar->handlers, lb);
  1823. +       
  1824. +       WM_event_add_ui_handler(NULL, &ar->handlers, node_ui_handler, node_ui_handler_remove, NULL);
  1825.  }
  1826.  
  1827.  static void node_main_area_draw(const bContext *C, ARegion *ar)
  1828. Index: source/blender/editors/transform/transform_conversions.c
  1829. ===================================================================
  1830. --- source/blender/editors/transform/transform_conversions.c    (revision 45822)
  1831. +++ source/blender/editors/transform/transform_conversions.c    (working copy)
  1832. @@ -2281,7 +2281,6 @@
  1833.         if (t->total==1) {
  1834.                 ED_node_link_intersect_test(t->sa, 1);
  1835.         }
  1836. -       
  1837.  }
  1838.  
  1839.  /* *** SEQUENCE EDITOR *** */
  1840. @@ -4928,10 +4927,11 @@
  1841.         }
  1842.         else if (t->spacetype == SPACE_NODE) {
  1843.                 SpaceNode *snode= (SpaceNode *)t->sa->spacedata.first;
  1844. -               ED_node_update_hierarchy(C, snode->edittree);
  1845. -              
  1846. -               if (canceled == 0)
  1847. +               if (canceled == 0) {
  1848. +                       ED_node_post_apply_transform(C, snode->edittree);
  1849. +                      
  1850.                         ED_node_link_insert(t->sa);
  1851. +               }
  1852.                
  1853.                 /* clear link line */
  1854.                 ED_node_link_intersect_test(t->sa, 0);
  1855. Index: source/blender/makesdna/DNA_node_types.h
  1856. ===================================================================
  1857. --- source/blender/makesdna/DNA_node_types.h    (revision 45822)
  1858. +++ source/blender/makesdna/DNA_node_types.h    (working copy)
  1859. @@ -150,11 +150,14 @@
  1860.         struct bNode *next, *prev, *new_node;
  1861.         
  1862.         char name[64];  /* MAX_NAME */
  1863. -       short type, flag;
  1864. +       int flag;
  1865. +       short type, pad2;
  1866.         short done, level;              /* both for dependency and sorting */
  1867.         short lasty, menunr;    /* lasty: check preview render status, menunr: browse ID blocks */
  1868.         short stack_index;              /* for groupnode, offset in global caller stack */
  1869.         short nr;                               /* number of this node in list, used for UI exec events */
  1870. +       float color[4];                 /* custom user-defined color */
  1871. +       int pad3;
  1872.         
  1873.         ListBase inputs, outputs;
  1874.         struct bNode *parent;   /* parent node */
  1875. @@ -164,6 +167,7 @@
  1876.         float locx, locy;               /* root offset for drawing */
  1877.         float width, height;    /* node custom width and height */
  1878.         float miniwidth;                /* node width if hidden */
  1879. +       float offsetx, offsety; /* additional offset from loc */
  1880.         
  1881.         int update;                             /* update flags */
  1882.         
  1883. @@ -205,6 +209,8 @@
  1884.  #define NODE_TRANSFORM         (1<<13)
  1885.         /* node is active texture */
  1886.  #define NODE_ACTIVE_TEXTURE    (1<<14)
  1887. +       /* use a custom color for the node */
  1888. +#define NODE_CUSTOM_COLOR      (1<<15)
  1889.  
  1890.  /* node->update */
  1891.  /* XXX NODE_UPDATE is a generic update flag. More fine-grained updates
  1892. @@ -522,6 +528,10 @@
  1893.         char name[64];
  1894.  } TexNodeOutput;
  1895.  
  1896. +/* frame node flags */
  1897. +#define NODE_FRAME_SHRINK              1       /* keep the bounding box minimal */
  1898. +#define NODE_FRAME_RESIZEABLE  2       /* test flag, if frame can be resized by user */
  1899. +
  1900.  /* comp channel matte */
  1901.  #define CMP_NODE_CHANNEL_MATTE_CS_RGB  1
  1902.  #define CMP_NODE_CHANNEL_MATTE_CS_HSV  2
  1903. Index: source/blender/makesrna/intern/rna_nodetree.c
  1904. ===================================================================
  1905. --- source/blender/makesrna/intern/rna_nodetree.c       (revision 45822)
  1906. +++ source/blender/makesrna/intern/rna_nodetree.c       (working copy)
  1907. @@ -200,6 +200,8 @@
  1908.                         return &RNA_NodeForLoop;
  1909.                 case NODE_WHILELOOP:
  1910.                         return &RNA_NodeWhileLoop;
  1911. +               case NODE_FRAME:
  1912. +                       return &RNA_NodeFrame;
  1913.                        
  1914.                 default:
  1915.                         return &RNA_Node;
  1916. @@ -1029,8 +1031,12 @@
  1917.  
  1918.  static void def_frame(StructRNA *srna)
  1919.  {
  1920. -/*     PropertyRNA *prop; */
  1921. +       PropertyRNA *prop;
  1922.         
  1923. +       prop = RNA_def_property(srna, "shrink", PROP_BOOLEAN, PROP_NONE);
  1924. +       RNA_def_property_boolean_sdna(prop, NULL, "custom1", NODE_FRAME_SHRINK);
  1925. +       RNA_def_property_ui_text(prop, "Shrink", "Shrink the frame to minimal bounding box");
  1926. +       RNA_def_property_update(prop, NC_NODE|ND_DISPLAY, "rna_Node_update");
  1927.  }
  1928.  
  1929.  static void def_math(StructRNA *srna)
  1930. @@ -3268,6 +3274,18 @@
  1931.         RNA_def_property_struct_type(prop, "Node");
  1932.         RNA_def_property_clear_flag(prop, PROP_EDITABLE);
  1933.         RNA_def_property_ui_text(prop, "Parent", "Parent this node is attached to");
  1934. +       
  1935. +       prop = RNA_def_property(srna, "use_custom_color", PROP_BOOLEAN, PROP_NONE);
  1936. +       RNA_def_property_boolean_sdna(prop, NULL, "flag", NODE_CUSTOM_COLOR);
  1937. +       RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
  1938. +       RNA_def_property_ui_text(prop, "Custom Color", "Use custom color for the node");
  1939. +       RNA_def_property_update(prop, NC_NODE|ND_DISPLAY, NULL);
  1940. +
  1941. +       prop = RNA_def_property(srna, "color", PROP_FLOAT, PROP_COLOR);
  1942. +       RNA_def_property_array(prop, 4);
  1943. +       RNA_def_property_range(prop, 0.0f, 1.0f);
  1944. +       RNA_def_property_ui_text(prop, "Color", "Custom color of the node body");
  1945. +       RNA_def_property_update(prop, NC_NODE|ND_DISPLAY, NULL);
  1946.  
  1947.         prop = RNA_def_property(srna, "show_texture", PROP_BOOLEAN, PROP_NONE);
  1948.         RNA_def_property_boolean_sdna(prop, NULL, "flag", NODE_ACTIVE_TEXTURE);
  1949. Index: source/blender/makesrna/intern/rna_userdef.c
  1950. ===================================================================
  1951. --- source/blender/makesrna/intern/rna_userdef.c        (revision 45822)
  1952. +++ source/blender/makesrna/intern/rna_userdef.c        (working copy)
  1953. @@ -1520,6 +1520,18 @@
  1954.         rna_def_userdef_theme_spaces_main(srna);
  1955.         rna_def_userdef_theme_spaces_list_main(srna);
  1956.  
  1957. +       prop = RNA_def_property(srna, "node_selected", PROP_FLOAT, PROP_COLOR_GAMMA);
  1958. +       RNA_def_property_float_sdna(prop, NULL, "select");
  1959. +       RNA_def_property_array(prop, 3);
  1960. +       RNA_def_property_ui_text(prop, "Node Selected", "");
  1961. +       RNA_def_property_update(prop, 0, "rna_userdef_update");
  1962. +
  1963. +       prop = RNA_def_property(srna, "node_active", PROP_FLOAT, PROP_COLOR_GAMMA);
  1964. +       RNA_def_property_float_sdna(prop, NULL, "active");
  1965. +       RNA_def_property_array(prop, 3);
  1966. +       RNA_def_property_ui_text(prop, "Active Node", "");
  1967. +       RNA_def_property_update(prop, 0, "rna_userdef_update");
  1968. +
  1969.         prop = RNA_def_property(srna, "wire", PROP_FLOAT, PROP_COLOR_GAMMA);
  1970.         RNA_def_property_float_sdna(prop, NULL, "wire");
  1971.         RNA_def_property_array(prop, 3);
  1972. Index: source/blender/nodes/intern/node_common.c
  1973. ===================================================================
  1974. --- source/blender/nodes/intern/node_common.c   (revision 45822)
  1975. +++ source/blender/nodes/intern/node_common.c   (working copy)
  1976. @@ -809,12 +809,18 @@
  1977.  
  1978.  /**** FRAME ****/
  1979.  
  1980. +static void node_frame_init(bNodeTree *UNUSED(ntree), bNode *node, bNodeTemplate *UNUSED(ntemp))
  1981. +{
  1982. +       node->custom1 |= NODE_FRAME_SHRINK;
  1983. +}
  1984. +
  1985.  void register_node_type_frame(bNodeTreeType *ttype)
  1986.  {
  1987.         /* frame type is used for all tree types, needs dynamic allocation */
  1988.         bNodeType *ntype= MEM_callocN(sizeof(bNodeType), "frame node type");
  1989.  
  1990. -       node_type_base(ttype, ntype, NODE_FRAME, "Frame", NODE_CLASS_LAYOUT, NODE_BACKGROUND);
  1991. +       node_type_base(ttype, ntype, NODE_FRAME, "Frame", NODE_CLASS_LAYOUT, NODE_BACKGROUND|NODE_OPTIONS);
  1992. +       node_type_init(ntype, node_frame_init);
  1993.         node_type_size(ntype, 150, 100, 0);
  1994.         node_type_compatibility(ntype, NODE_OLD_SHADING|NODE_NEW_SHADING);
  1995.         
go to heaven