Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLukas Toenne <lukas.toenne@googlemail.com>2012-05-22 18:13:33 +0400
committerLukas Toenne <lukas.toenne@googlemail.com>2012-05-22 18:13:33 +0400
commit53b01d90023a850b17ded5deb9cace354c8e298a (patch)
treeab8ce43ddf8046dc54e2cacc5ff46cbc5806910b /source/blender/editors/space_node
parent85923aff288da072750447b44e492ebe5c59bcce (diff)
A number of new features for the node editor in general and the Frame node in particular.
For an detailed user-level description of new features see the following blogpost: http://code.blender.org/index.php/2012/05/node-editing-tweaks/ TL;DR: * Frame node gets more usable bounding-box behavior * Node resizing has helpful mouse cursor indicators and works on all borders * Node selection/active colors are themeable independently * Customizable background colors for nodes (useful for frames visual distinction).
Diffstat (limited to 'source/blender/editors/space_node')
-rw-r--r--source/blender/editors/space_node/drawnode.c204
-rw-r--r--source/blender/editors/space_node/node_buttons.c18
-rw-r--r--source/blender/editors/space_node/node_draw.c252
-rw-r--r--source/blender/editors/space_node/node_edit.c576
-rw-r--r--source/blender/editors/space_node/node_intern.h14
-rw-r--r--source/blender/editors/space_node/node_ops.c83
-rw-r--r--source/blender/editors/space_node/node_select.c121
-rw-r--r--source/blender/editors/space_node/space_node.c13
8 files changed, 996 insertions, 285 deletions
diff --git a/source/blender/editors/space_node/drawnode.c b/source/blender/editors/space_node/drawnode.c
index 528a48b60df..8b7e0dd0db1 100644
--- a/source/blender/editors/space_node/drawnode.c
+++ b/source/blender/editors/space_node/drawnode.c
@@ -53,6 +53,8 @@
#include "BKE_main.h"
#include "BKE_node.h"
+#include "BLF_api.h"
+
#include "NOD_composite.h"
#include "NOD_shader.h"
@@ -79,6 +81,9 @@
#include "node_intern.h"
+// XXX interface.h
+extern void ui_dropshadow(rctf *rct, float radius, float aspect, int select);
+
/* ****************** SOCKET BUTTON DRAW FUNCTIONS ***************** */
static void node_sync_cb(bContext *UNUSED(C), void *snode_v, void *node_v)
@@ -474,15 +479,21 @@ static int node_resize_area_default(bNode *node, int x, int y)
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;
}
}
@@ -511,7 +522,7 @@ static void node_update_group(const bContext *C, bNodeTree *ntree, bNode *gnode)
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);
@@ -906,20 +917,166 @@ static void node_common_buts_whileloop(uiLayout *layout, bContext *UNUSED(C), Po
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;
+ NodeFrame *data = (NodeFrame *)node->storage;
+ 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 */
+ data->flag |= NODE_FRAME_RESIZEABLE;
+ /* for shrinking bbox, initialize the rect from first child node */
+ bbinit = (data->flag & 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;
+ data->flag &= ~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_label(bNode *node)
{
- float locx, locy;
+ /* XXX font id is crap design */
+ const int fontid = blf_mono_font;
+ NodeFrame *data = (NodeFrame *)node->storage;
+ rctf *rct= &node->totr;
+ int color_id= node_get_colorid(node);
+ char label[128];
+ /* XXX a bit hacky, should use separate align values for x and y */
+ float width, ascender;
+ float x, y;
+
+ BLI_strncpy(label, nodeLabel(node), sizeof(label));
+ BLF_size(fontid, data->label_size, U.dpi);
+
+ /* title color */
+ UI_ThemeColorBlendShade(TH_TEXT, color_id, 0.8f, 10);
+
+ width = BLF_width(fontid, label);
+ ascender = BLF_ascender(fontid);
+
+ x = 0.5f*(rct->xmin + rct->xmax) - 0.5f*width;
+ y = rct->ymax - NODE_DYS - ascender;
+
+ BLF_position(fontid, x, y, 0);
+ BLF_draw(fontid, label, BLF_DRAW_STR_DUMMY_MAX);
+}
+
+static void node_draw_frame(const bContext *C, ARegion *ar, SpaceNode *snode, bNodeTree *UNUSED(ntree), bNode *node)
+{
+ rctf *rct= &node->totr;
+ int color_id= node_get_colorid(node);
+
+ /* 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;
+ }
+
+ /* shadow */
+ node_draw_shadow(snode, node, BASIS_RAD);
+
+ /* body */
+ if (node->flag & NODE_CUSTOM_COLOR)
+ glColor3fv(node->color);
+ else
+ UI_ThemeColor4(TH_NODE);
+ glEnable(GL_BLEND);
+ uiSetRoundBox(UI_CNR_ALL);
+ uiRoundBox(rct->xmin, rct->ymin, rct->xmax, rct->ymax, 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);
+ }
+
+ /* label */
+ node_draw_frame_label(node);
+
+ 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;
+ NodeFrame *data = (NodeFrame *)node->storage;
+ rctf totr= node->totr;
+ int dir = 0;
+
+ /* shrinking frame size is determined by child nodes */
+ if (!(data->flag & 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, "label_size", 0, "Label Size", ICON_NONE);
+ uiItemR(layout, ptr, "shrink", 0, "Shrink", ICON_NONE);
}
static void node_common_set_butfunc(bNodeType *ntype)
@@ -941,7 +1098,10 @@ static void node_common_set_butfunc(bNodeType *ntype)
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;
}
}
@@ -1136,7 +1296,6 @@ static void node_shader_buts_dynamic(uiLayout *layout, bContext *C, PointerRNA *
/* 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" */
@@ -1215,7 +1374,6 @@ static void node_shader_set_butfunc(bNodeType *ntype)
ntype->uifunc= node_shader_buts_dynamic;
break;
}
- if (ntype->uifuncbut == NULL) ntype->uifuncbut = ntype->uifunc;
}
/* ****************** BUTTON CALLBACKS FOR COMPOSITE NODES ***************** */
@@ -2233,7 +2391,6 @@ static void node_composit_buts_viewer_but(uiLayout *layout, bContext *UNUSED(C),
/* 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" */
@@ -2424,8 +2581,6 @@ static void node_composit_set_butfunc(bNodeType *ntype)
default:
ntype->uifunc= NULL;
}
- if (ntype->uifuncbut == NULL) ntype->uifuncbut = ntype->uifunc;
-
}
/* ****************** BUTTON CALLBACKS FOR TEXTURE NODES ***************** */
@@ -2536,7 +2691,6 @@ static void node_texture_buts_output(uiLayout *layout, bContext *UNUSED(C), Poin
/* 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;
}
@@ -2580,10 +2734,6 @@ static void node_texture_set_butfunc(bNodeType *ntype)
break;
}
}
-
- if (ntype->uifuncbut == NULL) {
- ntype->uifuncbut = ntype->uifunc;
- }
}
/* ******* init draw callbacks for all tree types, only called in usiblender.c, once ************* */
diff --git a/source/blender/editors/space_node/node_buttons.c b/source/blender/editors/space_node/node_buttons.c
index c92abf116c4..16810e0fdec 100644
--- a/source/blender/editors/space_node/node_buttons.c
+++ b/source/blender/editors/space_node/node_buttons.c
@@ -89,8 +89,8 @@ static void active_node_panel(const bContext *C, Panel *pa)
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;
- PointerRNA ptr;
+ uiLayout *layout, *row, *col;
+ PointerRNA ptr, opptr;
/* verify pointers, and create RNA pointer for the node */
if (ELEM(NULL, ntree, node))
@@ -113,6 +113,20 @@ static void active_node_panel(const bContext *C, Panel *pa)
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);
diff --git a/source/blender/editors/space_node/node_draw.c b/source/blender/editors/space_node/node_draw.c
index d93b1b1fcc8..7011d605a7e 100644
--- a/source/blender/editors/space_node/node_draw.c
+++ b/source/blender/editors/space_node/node_draw.c
@@ -167,6 +167,110 @@ void ED_node_generic_update(Main *bmain, bNodeTree *ntree, bNode *node)
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 do_node_internal_buttons(bContext *C, void *node_v, int event)
}
}
-
-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 @@ static void node_update_basis(const bContext *C, bNodeTree *ntree, bNode *node)
int buty;
/* get "global" coords */
- nodeSpaceCoords(node, &locx, &locy);
+ nodeToView(node, 0.0f, 0.0f, &locx, &locy);
dy= locy;
/* header */
@@ -350,7 +436,7 @@ static void node_update_hidden(bNode *node)
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)
@@ -410,7 +496,7 @@ void node_update_default(const bContext *C, bNodeTree *ntree, bNode *node)
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;
@@ -573,6 +659,24 @@ static void node_toggle_button_cb(struct bContext *C, void *node_argv, void *op_
WM_operator_name_call(C, opname, WM_OP_INVOKE_DEFAULT, NULL);
}
+void node_draw_shadow(SpaceNode *snode, bNode *node, float radius)
+{
+ rctf *rct = &node->totr;
+
+ uiSetRoundBox(UI_CNR_ALL);
+ if (node->parent==NULL)
+ ui_dropshadow(rct, radius, snode->aspect, node->flag & SELECT);
+ else {
+ const float margin = 3.0f;
+
+ glColor4f(0.0f, 0.0f, 0.0f, 0.33f);
+ glEnable(GL_BLEND);
+ uiRoundBox(rct->xmin-margin, rct->ymin-margin,
+ rct->xmax+margin, rct->ymax+margin, radius+margin);
+ glDisable(GL_BLEND);
+ }
+}
+
static void node_draw_basis(const bContext *C, ARegion *ar, SpaceNode *snode, bNodeTree *ntree, bNode *node)
{
bNodeSocket *sock;
@@ -597,8 +701,8 @@ static void node_draw_basis(const bContext *C, ARegion *ar, SpaceNode *snode, bN
return;
}
- uiSetRoundBox(UI_CNR_TOP_LEFT | UI_CNR_TOP_RIGHT | UI_CNR_BOTTOM_LEFT);
- ui_dropshadow(rct, BASIS_RAD, snode->aspect, node->flag & SELECT);
+ /* shadow */
+ node_draw_shadow(snode, node, BASIS_RAD);
/* header */
if (color_id==TH_NODE)
@@ -644,7 +748,7 @@ static void node_draw_basis(const bContext *C, ARegion *ar, SpaceNode *snode, bN
/* title */
if (node->flag & SELECT)
- UI_ThemeColor(TH_TEXT_HI);
+ UI_ThemeColor(TH_SELECT);
else
UI_ThemeColorBlendShade(TH_TEXT, color_id, 0.4f, 10);
@@ -680,27 +784,27 @@ static void node_draw_basis(const bContext *C, ARegion *ar, SpaceNode *snode, bN
(int)(iconofs - rct->xmin-18.0f), NODE_DY, NULL, 0, 0, 0, 0, "");
/* body */
- UI_ThemeColor4(TH_NODE);
+ if (node->flag & NODE_CUSTOM_COLOR)
+ glColor3fv(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);
+ uiDrawBox(GL_LINE_LOOP, rct->xmin, rct->ymin, rct->xmax, rct->ymax, BASIS_RAD);
+
glDisable(GL_LINE_SMOOTH);
glDisable(GL_BLEND);
}
@@ -758,8 +862,7 @@ static void node_draw_hidden(const bContext *C, ARegion *ar, SpaceNode *snode, b
char showname[128]; /* 128 is used below */
/* shadow */
- uiSetRoundBox(UI_CNR_ALL);
- ui_dropshadow(rct, hiddenrad, snode->aspect, node->flag & SELECT);
+ node_draw_shadow(snode, node, hiddenrad);
/* body */
UI_ThemeColor(color_id);
@@ -771,19 +874,20 @@ static void node_draw_hidden(const bContext *C, ARegion *ar, SpaceNode *snode, b
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);
@@ -807,7 +911,7 @@ static void node_draw_hidden(const bContext *C, ARegion *ar, SpaceNode *snode, b
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);
@@ -848,6 +952,45 @@ static void node_draw_hidden(const bContext *C, ARegion *ar, SpaceNode *snode, b
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 CURSOR_Y_MOVE;
+ else if ((directions & ~(NODE_RESIZE_RIGHT|NODE_RESIZE_LEFT))==0)
+ return CURSOR_X_MOVE;
+ else
+ return CURSOR_EDIT;
+}
+
+void node_set_cursor(wmWindow *win, SpaceNode *snode)
+{
+ bNodeTree *ntree = snode->edittree;
+ bNode *node;
+ bNodeSocket *sock;
+ int cursor = CURSOR_STD;
+
+ if (ntree) {
+ 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(win, cursor);
+}
+
void node_draw_default(const bContext *C, ARegion *ar, SpaceNode *snode, bNodeTree *ntree, bNode *node)
{
if (node->flag & NODE_HIDDEN)
@@ -866,7 +1009,8 @@ void node_update_nodetree(const bContext *C, bNodeTree *ntree, float offsetx, fl
{
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;
@@ -892,6 +1036,14 @@ void node_draw_nodetree(const bContext *C, ARegion *ar, SpaceNode *snode, bNodeT
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);
@@ -900,8 +1052,10 @@ void node_draw_nodetree(const bContext *C, ARegion *ar, SpaceNode *snode, bNodeT
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);
}
diff --git a/source/blender/editors/space_node/node_edit.c b/source/blender/editors/space_node/node_edit.c
index 8847dc3650a..5000e0f2163 100644
--- a/source/blender/editors/space_node/node_edit.c
+++ b/source/blender/editors/space_node/node_edit.c
@@ -711,55 +711,13 @@ void ED_node_set_active(Main *bmain, bNodeTree *ntree, bNode *node)
}
}
-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 ***************** */
@@ -1517,36 +1475,130 @@ void NODE_OT_backimage_sample(wmOperatorType *ot)
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);
@@ -1557,10 +1609,8 @@ static int node_resize_modal(bContext *C, wmOperator *op, wmEvent *event)
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;
}
@@ -1573,37 +1623,24 @@ static int node_resize_invoke(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);
+ 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;
}
@@ -2180,6 +2217,27 @@ bNode *node_add_node(SpaceNode *snode, Main *bmain, Scene *scene, bNodeTemplate
/* ****************** 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);
@@ -2242,6 +2300,19 @@ static int node_duplicate_exec(bContext *C, wmOperator *op)
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) {
@@ -3782,3 +3853,354 @@ void NODE_OT_output_file_move_active_socket(wmOperatorType *ot)
RNA_def_enum(ot->srna, "direction", direction_items, 2, "Direction", "");
}
+
+/* ****************** 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;
+ }
+ }
+
+ ED_node_sort(ntree);
+ 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);
+ }
+ }
+
+ 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;
+ else if (node->flag & NODE_TEST) {
+ /* if parent is not an decendant of the frame, reattach the node */
+ 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;
+}
+
+/* ****************** Attach ******************* */
+
+static int node_attach_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ SpaceNode *snode = CTX_wm_space_node(C);
+ bNodeTree *ntree = snode->edittree;
+ bNode *frame;
+
+ /* check nodes front to back */
+ for (frame=ntree->nodes.last; frame; frame=frame->prev) {
+ /* skip selected, those are the nodes we want to attach */
+ if ((frame->type != NODE_FRAME) || (frame->flag & NODE_SELECT))
+ continue;
+ if (BLI_in_rctf(&frame->totr, snode->mx, snode->my))
+ break;
+ }
+ if (frame) {
+ bNode *node, *parent;
+ for (node=ntree->nodes.last; node; node=node->prev) {
+ if (node->flag & NODE_SELECT) {
+ if (node->parent == NULL) {
+ /* attach all unparented nodes */
+ nodeAttachNode(node, frame);
+ }
+ else {
+ /* attach nodes which share parent with the frame */
+ for (parent=frame->parent; parent; parent=parent->parent)
+ if (parent == node->parent)
+ break;
+ if (parent) {
+ nodeDetachNode(node);
+ nodeAttachNode(node, frame);
+ }
+ }
+ }
+ }
+ }
+
+ ED_node_sort(ntree);
+ WM_event_add_notifier(C, NC_NODE|ND_DISPLAY, NULL);
+
+ return OPERATOR_FINISHED;
+}
+
+static int node_attach_invoke(bContext *C, wmOperator *op, wmEvent *event)
+{
+ ARegion *ar= CTX_wm_region(C);
+ SpaceNode *snode= CTX_wm_space_node(C);
+
+ /* convert mouse coordinates to v2d space */
+ UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &snode->mx, &snode->my);
+
+ return node_attach_exec(C, op);
+}
+
+void NODE_OT_attach(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Attach Nodes";
+ ot->description = "Attaches active node to a frame";
+ ot->idname = "NODE_OT_attach";
+
+ /* api callbacks */
+ ot->exec = node_attach_exec;
+ ot->invoke = node_attach_invoke;
+ ot->poll = ED_operator_node_active;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
+}
+
+/* ****************** Detach ******************* */
+
+/* tags for depth-first search */
+#define NODE_DETACH_DONE 1
+#define NODE_DETACH_IS_DESCENDANT 2
+
+static void node_detach_recursive(bNode *node)
+{
+ node->done |= NODE_DETACH_DONE;
+
+ if (node->parent) {
+ /* call recursively */
+ if (!(node->parent->done & NODE_DETACH_DONE))
+ node_detach_recursive(node->parent);
+
+ /* in any case: if the parent is a descendant, so is the child */
+ if (node->parent->done & NODE_DETACH_IS_DESCENDANT)
+ node->done |= NODE_DETACH_IS_DESCENDANT;
+ else if (node->flag & NODE_SELECT) {
+ /* if parent is not a decendant of a selected node, detach */
+ nodeDetachNode(node);
+ node->done |= NODE_DETACH_IS_DESCENDANT;
+ }
+ }
+ else if (node->flag & NODE_SELECT) {
+ node->done |= NODE_DETACH_IS_DESCENDANT;
+ }
+}
+
+/* detach the root nodes in the current selection */
+static int node_detach_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ SpaceNode *snode = CTX_wm_space_node(C);
+ bNodeTree *ntree = snode->edittree;
+ bNode *node;
+
+ /* reset tags */
+ for (node=ntree->nodes.first; node; node=node->next)
+ node->done = 0;
+ /* detach nodes recursively
+ * relative order is preserved here!
+ */
+ for (node=ntree->nodes.first; node; node=node->next) {
+ if (!(node->done & NODE_DETACH_DONE))
+ node_detach_recursive(node);
+ }
+
+ ED_node_sort(ntree);
+ WM_event_add_notifier(C, NC_NODE|ND_DISPLAY, NULL);
+
+ return OPERATOR_FINISHED;
+}
+
+void NODE_OT_detach(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Detach Nodes";
+ ot->description = "Detaches selected nodes from parents";
+ ot->idname = "NODE_OT_detach";
+
+ /* api callbacks */
+ ot->exec = node_detach_exec;
+ ot->poll = ED_operator_node_active;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
+}
diff --git a/source/blender/editors/space_node/node_intern.h b/source/blender/editors/space_node/node_intern.h
index 802c471c7aa..60177192e2a 100644
--- a/source/blender/editors/space_node/node_intern.h
+++ b/source/blender/editors/space_node/node_intern.h
@@ -40,7 +40,9 @@ struct ARegion;
struct ARegionType;
struct View2D;
struct bContext;
+struct wmWindow;
struct wmWindowManager;
+struct wmEvent;
struct bNodeTemplate;
struct bNode;
struct bNodeSocket;
@@ -67,13 +69,18 @@ ARegion *node_has_buttons_region(ScrArea *sa);
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_shadow(struct SpaceNode *snode, struct bNode *node, float radius);
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);
+void node_set_cursor(struct wmWindow *win, struct SpaceNode *snode);
+
/* node_buttons.c */
void node_buttons_register(struct ARegionType *art);
void NODE_OT_properties(struct wmOperatorType *ot);
@@ -151,6 +158,7 @@ void NODE_OT_hide_toggle(struct wmOperatorType *ot);
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);
@@ -170,6 +178,12 @@ void NODE_OT_output_file_add_socket(struct wmOperatorType *ot);
void NODE_OT_output_file_remove_active_socket(struct wmOperatorType *ot);
void NODE_OT_output_file_move_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);
+void NODE_OT_attach(struct wmOperatorType *ot);
+void NODE_OT_detach(struct wmOperatorType *ot);
+
extern const char *node_context_dir[];
// XXXXXX
diff --git a/source/blender/editors/space_node/node_ops.c b/source/blender/editors/space_node/node_ops.c
index 5d586f08eb0..781a14fca59 100644
--- a/source/blender/editors/space_node/node_ops.c
+++ b/source/blender/editors/space_node/node_ops.c
@@ -68,6 +68,7 @@ void node_operatortypes(void)
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);
@@ -104,6 +105,12 @@ void node_operatortypes(void)
WM_operatortype_append(NODE_OT_output_file_add_socket);
WM_operatortype_append(NODE_OT_output_file_remove_active_socket);
WM_operatortype_append(NODE_OT_output_file_move_active_socket);
+
+ WM_operatortype_append(NODE_OT_parent_set);
+ WM_operatortype_append(NODE_OT_parent_clear);
+ WM_operatortype_append(NODE_OT_join);
+ WM_operatortype_append(NODE_OT_attach);
+ WM_operatortype_append(NODE_OT_detach);
}
void ED_operatormacros_node(void)
@@ -111,10 +118,32 @@ void ED_operatormacros_node(void)
wmOperatorType *ot;
wmOperatorTypeMacro *mot;
- ot = WM_operatortype_append_macro("NODE_OT_duplicate_move", "Duplicate", "Duplicate selected nodes and move them",
+ ot = WM_operatortype_append_macro("NODE_OT_select_link_viewer", "Link Viewer",
+ "Select node and link it to a viewer node",
+ OPTYPE_UNDO);
+ WM_operatortype_macro_define(ot, "NODE_OT_select");
+ WM_operatortype_macro_define(ot, "NODE_OT_link_viewer");
+
+ ot = WM_operatortype_append_macro("NODE_OT_translate_attach", "Move and Attach",
+ "Move nodes and attach to frame",
+ OPTYPE_UNDO|OPTYPE_REGISTER);
+ mot = WM_operatortype_macro_define(ot, "TRANSFORM_OT_translate");
+ RNA_boolean_set(mot->ptr, "release_confirm", TRUE);
+ WM_operatortype_macro_define(ot, "NODE_OT_attach");
+
+ ot = WM_operatortype_append_macro("NODE_OT_detach_translate_attach", "Detach and Move",
+ "Detach nodes, move and attach to frame",
+ OPTYPE_UNDO|OPTYPE_REGISTER);
+ WM_operatortype_macro_define(ot, "NODE_OT_detach");
+ mot = WM_operatortype_macro_define(ot, "TRANSFORM_OT_translate");
+ RNA_boolean_set(mot->ptr, "release_confirm", TRUE);
+ WM_operatortype_macro_define(ot, "NODE_OT_attach");
+
+ ot = WM_operatortype_append_macro("NODE_OT_duplicate_move", "Duplicate",
+ "Duplicate selected nodes and move them",
OPTYPE_UNDO|OPTYPE_REGISTER);
WM_operatortype_macro_define(ot, "NODE_OT_duplicate");
- WM_operatortype_macro_define(ot, "TRANSFORM_OT_translate");
+ WM_operatortype_macro_define(ot, "NODE_OT_translate_attach");
/* modified operator call for duplicating with input links */
ot = WM_operatortype_append_macro("NODE_OT_duplicate_move_keep_inputs", "Duplicate",
@@ -122,12 +151,7 @@ void ED_operatormacros_node(void)
OPTYPE_UNDO|OPTYPE_REGISTER);
mot = WM_operatortype_macro_define(ot, "NODE_OT_duplicate");
RNA_boolean_set(mot->ptr, "keep_inputs", TRUE);
- WM_operatortype_macro_define(ot, "TRANSFORM_OT_translate");
-
- ot = WM_operatortype_append_macro("NODE_OT_select_link_viewer", "Link Viewer",
- "Select node and link it to a viewer node", OPTYPE_UNDO);
- WM_operatortype_macro_define(ot, "NODE_OT_select");
- WM_operatortype_macro_define(ot, "NODE_OT_link_viewer");
+ WM_operatortype_macro_define(ot, "NODE_OT_translate_attach");
ot = WM_operatortype_append_macro("NODE_OT_move_detach_links", "Detach", "Move a node to detach links",
OPTYPE_UNDO|OPTYPE_REGISTER);
@@ -137,8 +161,30 @@ void ED_operatormacros_node(void)
ot = WM_operatortype_append_macro("NODE_OT_move_detach_links_release", "Detach", "Move a node to detach links",
OPTYPE_UNDO|OPTYPE_REGISTER);
WM_operatortype_macro_define(ot, "NODE_OT_links_detach");
- mot = WM_operatortype_macro_define(ot, "TRANSFORM_OT_translate");
- RNA_boolean_set(mot->ptr, "release_confirm", TRUE);
+ mot = WM_operatortype_macro_define(ot, "NODE_OT_translate_attach");
+}
+
+/* helper function for repetitive select operator keymap */
+static void node_select_keymap(wmKeyMap *keymap, int extend)
+{
+ /* modifier combinations */
+ const int mod_single[] = { 0, KM_CTRL, KM_ALT, KM_CTRL|KM_ALT,
+ -1 /* terminator */
+ };
+ const int mod_extend[] = { KM_SHIFT, KM_SHIFT|KM_CTRL,
+ KM_SHIFT|KM_ALT, KM_SHIFT|KM_CTRL|KM_ALT,
+ -1 /* terminator */
+ };
+ const int *mod = (extend ? mod_extend : mod_single);
+ wmKeyMapItem *kmi;
+ int i;
+
+ for (i=0; mod[i] >= 0; ++i) {
+ kmi = WM_keymap_add_item(keymap, "NODE_OT_select", ACTIONMOUSE, KM_PRESS, mod[i], 0);
+ RNA_boolean_set(kmi->ptr, "extend", extend);
+ kmi = WM_keymap_add_item(keymap, "NODE_OT_select", SELECTMOUSE, KM_PRESS, mod[i], 0);
+ RNA_boolean_set(kmi->ptr, "extend", extend);
+ }
}
void node_keymap(struct wmKeyConfig *keyconf)
@@ -157,15 +203,12 @@ void node_keymap(struct wmKeyConfig *keyconf)
/* mouse select in nodes used to be both keys, but perhaps this should be reduced?
* NOTE: mouse-clicks on left-mouse will fall through to allow transform-tweak, but also link/resize
* NOTE 2: socket select is part of the node select operator, to handle overlapping cases
+ * NOTE 3: select op is registered for various combinations of modifier key, so the specialized
+ * grab operators (unlink, attach, etc.) can work easily on single nodes.
*/
- kmi = WM_keymap_add_item(keymap, "NODE_OT_select", ACTIONMOUSE, KM_PRESS, 0, 0);
- RNA_boolean_set(kmi->ptr, "extend", FALSE);
- kmi = WM_keymap_add_item(keymap, "NODE_OT_select", SELECTMOUSE, KM_PRESS, 0, 0);
- RNA_boolean_set(kmi->ptr, "extend", FALSE);
- kmi = WM_keymap_add_item(keymap, "NODE_OT_select", ACTIONMOUSE, KM_PRESS, KM_SHIFT, 0);
- RNA_boolean_set(kmi->ptr, "extend", TRUE);
- kmi = WM_keymap_add_item(keymap, "NODE_OT_select", SELECTMOUSE, KM_PRESS, KM_SHIFT, 0);
- RNA_boolean_set(kmi->ptr, "extend", TRUE);
+ node_select_keymap(keymap, FALSE);
+ node_select_keymap(keymap, TRUE);
+
kmi = WM_keymap_add_item(keymap, "NODE_OT_select_border", EVT_TWEAK_S, KM_ANY, 0, 0);
RNA_boolean_set(kmi->ptr, "tweak", TRUE);
@@ -195,6 +238,10 @@ void node_keymap(struct wmKeyConfig *keyconf)
/* 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);
diff --git a/source/blender/editors/space_node/node_select.c b/source/blender/editors/space_node/node_select.c
index a540f18b3a5..9098c8a4255 100644
--- a/source/blender/editors/space_node/node_select.c
+++ b/source/blender/editors/space_node/node_select.c
@@ -70,109 +70,6 @@ static bNode *node_under_mouse(bNodeTree *ntree, int mx, int my)
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 @@ void node_select_single(bContext *C, bNode *node)
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 @@ static int node_mouse_select(Main *bmain, SpaceNode *snode, ARegion *ar, const i
/* update node order */
if (selected)
- node_sort(snode->edittree);
+ ED_node_sort(snode->edittree);
return selected;
}
@@ -573,7 +470,7 @@ static int node_borderselect_exec(bContext *C, wmOperator *op)
}
}
- node_sort(snode->edittree);
+ ED_node_sort(snode->edittree);
WM_event_add_notifier(C, NC_NODE|NA_SELECTED, NULL);
@@ -645,7 +542,7 @@ static int node_select_all_exec(bContext *C, wmOperator *UNUSED(op))
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 @@ static int node_select_linked_to_exec(bContext *C, wmOperator *UNUSED(op))
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 @@ static int node_select_linked_from_exec(bContext *C, wmOperator *UNUSED(op))
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 @@ static int node_select_same_type_exec(bContext *C, wmOperator *UNUSED(op))
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 @@ static int node_select_same_type_next_exec(bContext *C, wmOperator *UNUSED(op))
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 @@ static int node_select_same_type_prev_exec(bContext *C, wmOperator *UNUSED(op))
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;
diff --git a/source/blender/editors/space_node/space_node.c b/source/blender/editors/space_node/space_node.c
index 3540c20e515..4d5964c72e5 100644
--- a/source/blender/editors/space_node/space_node.c
+++ b/source/blender/editors/space_node/space_node.c
@@ -339,6 +339,17 @@ static void node_buttons_area_draw(const bContext *C, ARegion *ar)
ED_region_panels(C, ar, 1, NULL, -1);
}
+static void node_cursor(wmWindow *win, ScrArea *sa, ARegion *ar)
+{
+ SpaceNode *snode= sa->spacedata.first;
+
+ /* convert mouse coordinates to v2d space */
+ UI_view2d_region_to_view(&ar->v2d, win->eventstate->x - ar->winrct.xmin, win->eventstate->y - ar->winrct.ymin,
+ &snode->mx, &snode->my);
+
+ node_set_cursor(win, snode);
+}
+
/* Initialize main area, setting handlers. */
static void node_main_area_init(wmWindowManager *wm, ARegion *ar)
{
@@ -522,6 +533,8 @@ void ED_spacetype_node(void)
art->init= node_main_area_init;
art->draw= node_main_area_draw;
art->listener= node_region_listener;
+ art->cursor = node_cursor;
+ art->event_cursor = TRUE;
art->keymapflag= ED_KEYMAP_UI|ED_KEYMAP_VIEW2D|ED_KEYMAP_FRAMES|ED_KEYMAP_GPENCIL;
BLI_addhead(&st->regiontypes, art);