Ejemplo n.º 1
0
static int node_select_all_exec(bContext *C, wmOperator *op)
{
	SpaceNode *snode = CTX_wm_space_node(C);
	ListBase *node_lb = &snode->edittree->nodes;
	bNode *node;
	int action = RNA_enum_get(op->ptr, "action");

	if (action == SEL_TOGGLE) {
		if (ED_node_select_check(node_lb))
			action = SEL_DESELECT;
		else
			action = SEL_SELECT;
	}

	for (node = node_lb->first; node; node = node->next) {
		switch (action) {
			case SEL_SELECT:
				nodeSetSelected(node, true);
				break;
			case SEL_DESELECT:
				nodeSetSelected(node, false);
				break;
			case SEL_INVERT:
				nodeSetSelected(node, !(node->flag & SELECT));
				break;
		}
	}

	ED_node_sort(snode->edittree);
	
	WM_event_add_notifier(C, NC_NODE | NA_SELECTED, NULL);
	return OPERATOR_FINISHED;
}
Ejemplo n.º 2
0
/* return 1 if we need redraw otherwise zero. */
int node_select_same_type(SpaceNode *snode)
{
	bNode *nac, *p;
	int redraw;

	/* search for the active node. */
	for (nac = snode->edittree->nodes.first; nac; nac = nac->next) {
		if (nac->flag & SELECT)
			break;
	}

	/* no active node, return. */
	if (!nac)
		return(0);

	redraw = 0;
	for (p = snode->edittree->nodes.first; p; p = p->next) {
		if (p->type != nac->type && p->flag & SELECT) {
			/* if it's selected but different type, unselect */
			redraw = 1;
			nodeSetSelected(p, FALSE);
		}
		else if (p->type == nac->type && (!(p->flag & SELECT))) {
			/* if it's the same type and is not selected, select! */
			redraw = 1;
			nodeSetSelected(p, TRUE);
		}
	}
	return(redraw);
}
Ejemplo n.º 3
0
static int node_borderselect_exec(bContext *C, wmOperator *op)
{
	SpaceNode *snode = CTX_wm_space_node(C);
	ARegion *ar = CTX_wm_region(C);
	bNode *node;
	rctf rectf;
	int gesture_mode = RNA_int_get(op->ptr, "gesture_mode");
	const bool extend = RNA_boolean_get(op->ptr, "extend");
	
	WM_operator_properties_border_to_rctf(op, &rectf);
	UI_view2d_region_to_view_rctf(&ar->v2d, &rectf, &rectf);
	
	for (node = snode->edittree->nodes.first; node; node = node->next) {
		if (BLI_rctf_isect(&rectf, &node->totr, NULL)) {
			nodeSetSelected(node, (gesture_mode == GESTURE_MODAL_SELECT));
		}
		else if (!extend) {
			nodeSetSelected(node, false);
		}
	}
	
	ED_node_sort(snode->edittree);
	
	WM_event_add_notifier(C, NC_NODE | NA_SELECTED, NULL);

	return OPERATOR_FINISHED;
}
Ejemplo n.º 4
0
static int node_select_linked_from_exec(bContext *C, wmOperator *UNUSED(op))
{
	SpaceNode *snode = CTX_wm_space_node(C);
	bNodeLink *link;
	bNode *node;
	
	for (node = snode->edittree->nodes.first; node; node = node->next)
		node->flag &= ~NODE_TEST;

	for (link = snode->edittree->links.first; link; link = link->next) {
		if (nodeLinkIsHidden(link))
			continue;
		if (link->fromnode && link->tonode && (link->tonode->flag & NODE_SELECT))
			link->fromnode->flag |= NODE_TEST;
	}
	
	for (node = snode->edittree->nodes.first; node; node = node->next) {
		if (node->flag & NODE_TEST)
			nodeSetSelected(node, true);
	}
	
	ED_node_sort(snode->edittree);
	
	WM_event_add_notifier(C, NC_NODE | NA_SELECTED, NULL);
	return OPERATOR_FINISHED;
}
Ejemplo n.º 5
0
static bool do_lasso_select_node(bContext *C, const int mcords[][2], short moves, short select)
{
	SpaceNode *snode = CTX_wm_space_node(C);
	bNode *node;

	ARegion *ar = CTX_wm_region(C);

	rcti rect;
	bool changed = false;

	/* get rectangle from operator */
	BLI_lasso_boundbox(&rect, mcords, moves);

	/* do actual selection */
	for (node = snode->edittree->nodes.first; node; node = node->next) {
		int screen_co[2];
		const float cent[2] = {BLI_rctf_cent_x(&node->totr),
		                       BLI_rctf_cent_y(&node->totr)};

		/* marker in screen coords */
		if (UI_view2d_view_to_region_clip(&ar->v2d, cent[0], cent[1], &screen_co[0], &screen_co[1]) &&
		    BLI_rcti_isect_pt(&rect, screen_co[0], screen_co[1]) &&
		    BLI_lasso_is_point_inside(mcords, moves, screen_co[0], screen_co[1], INT_MAX))
		{
			nodeSetSelected(node, select);
			changed = true;
		}
	}

	if (changed) {
		WM_event_add_notifier(C, NC_NODE | NA_SELECTED, NULL);
	}

	return changed;
}
Ejemplo n.º 6
0
static int node_circleselect_exec(bContext *C, wmOperator *op)
{
	SpaceNode *snode = CTX_wm_space_node(C);
	ARegion *ar = CTX_wm_region(C);
	bNode *node;

	int x, y, radius, gesture_mode;
	float offset[2];

	float zoom  = (float)(BLI_rcti_size_x(&ar->winrct)) / (float)(BLI_rctf_size_x(&ar->v2d.cur));

	gesture_mode = RNA_int_get(op->ptr, "gesture_mode");

	/* get operator properties */
	x = RNA_int_get(op->ptr, "x");
	y = RNA_int_get(op->ptr, "y");
	radius = RNA_int_get(op->ptr, "radius");

	UI_view2d_region_to_view(&ar->v2d, x, y, &offset[0], &offset[1]);

	for (node = snode->edittree->nodes.first; node; node = node->next) {
		if (BLI_rctf_isect_circle(&node->totr, offset, radius / zoom)) {
			nodeSetSelected(node, (gesture_mode == GESTURE_MODAL_SELECT));
		}
	}

	WM_event_add_notifier(C, NC_NODE | NA_SELECTED, NULL);

	return OPERATOR_FINISHED;
}
Ejemplo n.º 7
0
/* no undo here! */
void node_deselect_all(SpaceNode *snode)
{
	bNode *node;
	
	for (node = snode->edittree->nodes.first; node; node = node->next)
		nodeSetSelected(node, false);
}
Ejemplo n.º 8
0
static bool node_select_grouped_name(SpaceNode *snode, bNode *node_act, const bool from_right)
{
	bNode *node;
	bool changed = false;
	const unsigned int delims[] = {'.', '-', '_', '\0'};
	size_t index_act, index_curr;
	char *sep, *suf_act, *suf_curr;

	index_act = BLI_str_partition_ex_utf8(node_act->name, delims, &sep, &suf_act, from_right);

	if (index_act > 0) {
		for (node = snode->edittree->nodes.first; node; node = node->next) {
			if ((node->flag & SELECT) == 0) {
				index_curr = BLI_str_partition_ex_utf8(node->name, delims, &sep, &suf_curr, from_right);
				if ((from_right && STREQ(suf_act, suf_curr)) ||
				    (!from_right && (index_act == index_curr) && STREQLEN(node_act->name, node->name, index_act)))
				{
					nodeSetSelected(node, true);
					changed = true;
				}
			}
		}
	}

	return changed;
}
Ejemplo n.º 9
0
void node_select_single(bContext *C, bNode *node)
{
	Main *bmain = CTX_data_main(C);
	SpaceNode *snode = CTX_wm_space_node(C);
	bNode *tnode;
	
	for (tnode = snode->edittree->nodes.first; tnode; tnode = tnode->next)
		if (tnode != node)
			nodeSetSelected(tnode, false);
	nodeSetSelected(node, true);
	
	ED_node_set_active(bmain, snode->edittree, node);
	ED_node_set_active_viewer_key(snode);
	
	ED_node_sort(snode->edittree);
	
	WM_event_add_notifier(C, NC_NODE | NA_SELECTED, NULL);
}
Ejemplo n.º 10
0
static int node_select_grouped_exec(bContext *C, wmOperator *op)
{
	SpaceNode *snode = CTX_wm_space_node(C);
	bNode *node_act = nodeGetActive(snode->edittree);
	bNode *node;
	bool changed = false;
	const bool extend = RNA_boolean_get(op->ptr, "extend");
	const int type = RNA_enum_get(op->ptr, "type");

	if (!extend) {
		for (node = snode->edittree->nodes.first; node; node = node->next) {
			nodeSetSelected(node, false);
		}
	}
	nodeSetSelected(node_act, true);

	switch (type) {
		case NODE_SELECT_GROUPED_TYPE:
			changed = node_select_grouped_type(snode, node_act);
			break;
		case NODE_SELECT_GROUPED_COLOR:
			changed = node_select_grouped_color(snode, node_act);
			break;
		case NODE_SELECT_GROUPED_PREFIX:
			changed = node_select_grouped_name(snode, node_act, false);
			break;
		case NODE_SELECT_GROUPED_SUFIX:
			changed = node_select_grouped_name(snode, node_act, true);
			break;
		default:
			break;
	}

	if (changed) {
		ED_node_sort(snode->edittree);
		WM_event_add_notifier(C, NC_NODE | NA_SELECTED, NULL);
		return OPERATOR_FINISHED;
	}

	return OPERATOR_CANCELLED;
}
Ejemplo n.º 11
0
/* return 1 if we need redraw, otherwise zero.
 * dir can be 0 == next or 0 != prev.
 */
int node_select_same_type_np(SpaceNode *snode, int dir)
{
	bNode *nac, *p, *tnode;

	/* search the active one. */
	for (nac = snode->edittree->nodes.first; nac; nac = nac->next) {
		if (nac->flag & SELECT)
			break;
	}

	/* no active node, return. */
	if (!nac)
		return(0);

	if (dir == 0)
		p = nac->next;
	else
		p = nac->prev;

	while (p) {
		/* Now search the next with the same type. */
		if (p->type == nac->type)
			break;

		if (dir == 0)
			p = p->next;
		else
			p = p->prev;
	}

	if (p) {
		for (tnode = snode->edittree->nodes.first; tnode; tnode = tnode->next)
			if (tnode != p)
				nodeSetSelected(tnode, FALSE);
		nodeSetSelected(p, TRUE);
		return(1);
	}
	return(0);
}
Ejemplo n.º 12
0
static bool node_select_grouped_color(SpaceNode *snode, bNode *node_act)
{
	bNode *node;
	bool changed = false;

	for (node = snode->edittree->nodes.first; node; node = node->next) {
		if ((node->flag & SELECT) == 0) {
			if (compare_v3v3(node->color, node_act->color, 0.005f)) {
				nodeSetSelected(node, true);
				changed = true;
			}
		}
	}

	return changed;
}
Ejemplo n.º 13
0
static bool node_select_grouped_type(SpaceNode *snode, bNode *node_act)
{
	bNode *node;
	bool changed = false;

	for (node = snode->edittree->nodes.first; node; node = node->next) {
		if ((node->flag & SELECT) == 0) {
			if (node->type == node_act->type) {
				nodeSetSelected(node, true);
				changed = true;
			}
		}
	}

	return changed;
}
Ejemplo n.º 14
0
static bool node_select_grouped_name(SpaceNode *snode, bNode *node_act, const bool from_right)
{
	bNode *node;
	bool changed = false;
	const unsigned int delims[] = {'.', '-', '_', '\0'};
	size_t pref_len_act, pref_len_curr;
	const char *sep, *suf_act, *suf_curr;

	pref_len_act = BLI_str_partition_ex_utf8(node_act->name, NULL, delims, &sep, &suf_act, from_right);

	/* Note: in case we are searching for suffix, and found none, use whole name as suffix. */
	if (from_right && !(sep && suf_act)) {
		pref_len_act = 0;
		suf_act = node_act->name;
	}

	for (node = snode->edittree->nodes.first; node; node = node->next) {
		if (node->flag & SELECT) {
			continue;
		}
		pref_len_curr = BLI_str_partition_ex_utf8(node->name, NULL, delims, &sep, &suf_curr, from_right);

		/* Same as with active node name! */
		if (from_right && !(sep && suf_curr)) {
			pref_len_curr = 0;
			suf_curr = node->name;
		}

		if ((from_right && STREQ(suf_act, suf_curr)) ||
		    (!from_right && (pref_len_act == pref_len_curr) && STREQLEN(node_act->name, node->name, pref_len_act)))
		{
			nodeSetSelected(node, true);
			changed = true;
		}
	}

	return changed;
}
Ejemplo n.º 15
0
static void node_toggle(bNode *node)
{
	nodeSetSelected(node, !(node->flag & SELECT));
}
Ejemplo n.º 16
0
/* returns 1 if its OK */
static int node_group_separate_selected(bNodeTree *ntree, bNodeTree *ngroup, float offx, float offy, int make_copy)
{
	bNodeLink *link, *link_next;
	bNode *node, *node_next, *newnode;
	ListBase anim_basepaths = {NULL, NULL};
	
	/* deselect all nodes in the target tree */
	for (node = ntree->nodes.first; node; node = node->next)
		nodeSetSelected(node, FALSE);
	
	/* clear new pointers, set in nodeCopyNode */
	for (node = ngroup->nodes.first; node; node = node->next)
		node->new_node = NULL;
	
	/* add selected nodes into the ntree */
	for (node = ngroup->nodes.first; node; node = node_next) {
		node_next = node->next;
		if (!(node->flag & NODE_SELECT))
			continue;
		
		/* ignore interface nodes */
		if (ELEM(node->type, NODE_GROUP_INPUT, NODE_GROUP_OUTPUT)) {
			nodeSetSelected(node, FALSE);
			continue;
		}
		
		if (make_copy) {
			/* make a copy */
			newnode = nodeCopyNode(ngroup, node);
		}
		else {
			/* use the existing node */
			newnode = node;
		}
		
		/* keep track of this node's RNA "base" path (the part of the path identifying the node) 
		 * if the old nodetree has animation data which potentially covers this node
		 */
		if (ngroup->adt) {
			PointerRNA ptr;
			char *path;
			
			RNA_pointer_create(&ngroup->id, &RNA_Node, newnode, &ptr);
			path = RNA_path_from_ID_to_struct(&ptr);
			
			if (path)
				BLI_addtail(&anim_basepaths, BLI_genericNodeN(path));
		}
		
		/* ensure valid parent pointers, detach if parent stays inside the group */
		if (newnode->parent && !(newnode->parent->flag & NODE_SELECT))
			nodeDetachNode(newnode);
		
		/* migrate node */
		BLI_remlink(&ngroup->nodes, newnode);
		BLI_addtail(&ntree->nodes, newnode);
		
		/* ensure unique node name in the node tree */
		nodeUniqueName(ntree, newnode);

		if (!newnode->parent) {
			newnode->locx += offx;
			newnode->locy += offy;		
		}
	}
	
	/* add internal links to the ntree */
	for (link = ngroup->links.first; link; link = link_next) {
		int fromselect = (link->fromnode && (link->fromnode->flag & NODE_SELECT));
		int toselect = (link->tonode && (link->tonode->flag & NODE_SELECT));
		link_next = link->next;
		
		if (make_copy) {
			/* make a copy of internal links */
			if (fromselect && toselect)
				nodeAddLink(ntree, link->fromnode->new_node, link->fromsock->new_sock, link->tonode->new_node, link->tosock->new_sock);
		}
		else {
			/* move valid links over, delete broken links */
			if (fromselect && toselect) {
				BLI_remlink(&ngroup->links, link);
				BLI_addtail(&ntree->links, link);
			}
			else if (fromselect || toselect) {
				nodeRemLink(ngroup, link);
			}
		}
	}
	
	/* and copy across the animation,
	 * note that the animation data's action can be NULL here */
	if (ngroup->adt) {
		LinkData *ld, *ldn = NULL;
		
		/* now perform the moving */
		BKE_animdata_separate_by_basepath(&ngroup->id, &ntree->id, &anim_basepaths);
		
		/* paths + their wrappers need to be freed */
		for (ld = anim_basepaths.first; ld; ld = ldn) {
			ldn = ld->next;
			
			MEM_freeN(ld->data);
			BLI_freelinkN(&anim_basepaths, ld);
		}
	}
	
	ntree->update |= NTREE_UPDATE_NODES | NTREE_UPDATE_LINKS;
	if (!make_copy)
		ngroup->update |= NTREE_UPDATE_NODES | NTREE_UPDATE_LINKS;
	
	return 1;
}
Ejemplo n.º 17
0
static void node_group_make_insert_selected(const bContext *C, bNodeTree *ntree, bNode *gnode)
{
	bNodeTree *ngroup = (bNodeTree *)gnode->id;
	bNodeLink *link, *linkn;
	bNode *node, *nextn;
	bNodeSocket *sock;
	ListBase anim_basepaths = {NULL, NULL};
	float min[2], max[2], center[2];
	int totselect;
	int expose_all = FALSE;
	bNode *input_node, *output_node;
	
	/* XXX rough guess, not nice but we don't have access to UI constants here ... */
	static const float offsetx = 200;
	static const float offsety = 0.0f;
	
	/* deselect all nodes in the target tree */
	for (node = ngroup->nodes.first; node; node = node->next)
		nodeSetSelected(node, FALSE);
	
	totselect = node_get_selected_minmax(ntree, gnode, min, max);
	add_v2_v2v2(center, min, max);
	mul_v2_fl(center, 0.5f);
	
	/* auto-add interface for "solo" nodes */
	if (totselect == 1)
		expose_all = TRUE;
	
	/* move nodes over */
	for (node = ntree->nodes.first; node; node = nextn) {
		nextn = node->next;
		if (node_group_make_use_node(node, gnode)) {
			/* keep track of this node's RNA "base" path (the part of the pat identifying the node) 
			 * if the old nodetree has animation data which potentially covers this node
			 */
			if (ntree->adt) {
				PointerRNA ptr;
				char *path;
				
				RNA_pointer_create(&ntree->id, &RNA_Node, node, &ptr);
				path = RNA_path_from_ID_to_struct(&ptr);
				
				if (path)
					BLI_addtail(&anim_basepaths, BLI_genericNodeN(path));
			}
			
			/* ensure valid parent pointers, detach if parent stays outside the group */
			if (node->parent && !(node->parent->flag & NODE_SELECT))
				nodeDetachNode(node);
			
			/* change node-collection membership */
			BLI_remlink(&ntree->nodes, node);
			BLI_addtail(&ngroup->nodes, node);
			
			/* ensure unique node name in the ngroup */
			nodeUniqueName(ngroup, node);
		}
	}
	
	/* move animation data over */
	if (ntree->adt) {
		LinkData *ld, *ldn = NULL;
		
		BKE_animdata_separate_by_basepath(&ntree->id, &ngroup->id, &anim_basepaths);
		
		/* paths + their wrappers need to be freed */
		for (ld = anim_basepaths.first; ld; ld = ldn) {
			ldn = ld->next;
			
			MEM_freeN(ld->data);
			BLI_freelinkN(&anim_basepaths, ld);
		}
	}
	
	/* node groups don't use internal cached data */
	ntreeFreeCache(ngroup);
	
	/* create input node */
	input_node = nodeAddStaticNode(C, ngroup, NODE_GROUP_INPUT);
	input_node->locx = min[0] - center[0] - offsetx;
	input_node->locy = -offsety;
	
	/* create output node */
	output_node = nodeAddStaticNode(C, ngroup, NODE_GROUP_OUTPUT);
	output_node->locx = max[0] - center[0] + offsetx;
	output_node->locy = -offsety;
	
	/* relink external sockets */
	for (link = ntree->links.first; link; link = linkn) {
		int fromselect = node_group_make_use_node(link->fromnode, gnode);
		int toselect = node_group_make_use_node(link->tonode, gnode);
		
		linkn = link->next;
		
		if ((fromselect && link->tonode == gnode) || (toselect && link->fromnode == gnode)) {
			/* remove all links to/from the gnode.
			 * this can remove link information, but there's no general way to preserve it.
			 */
			nodeRemLink(ntree, link);
		}
		else if (fromselect && toselect) {
			BLI_remlink(&ntree->links, link);
			BLI_addtail(&ngroup->links, link);
		}
		else if (toselect) {
			bNodeSocket *iosock = ntreeAddSocketInterfaceFromSocket(ngroup, link->tonode, link->tosock);
			bNodeSocket *input_sock;
			
			/* update the group node and interface node sockets,
			 * so the new interface socket can be linked.
			 */
			node_group_verify(ntree, gnode, (ID *)ngroup);
			node_group_input_verify(ngroup, input_node, (ID *)ngroup);
			
			/* create new internal link */
			input_sock = node_group_input_find_socket(input_node, iosock->identifier);
			nodeAddLink(ngroup, input_node, input_sock, link->tonode, link->tosock);
			
			/* redirect external link */
			link->tonode = gnode;
			link->tosock = node_group_find_input_socket(gnode, iosock->identifier);
		}
		else if (fromselect) {
			bNodeSocket *iosock = ntreeAddSocketInterfaceFromSocket(ngroup, link->fromnode, link->fromsock);
			bNodeSocket *output_sock;
			
			/* update the group node and interface node sockets,
			 * so the new interface socket can be linked.
			 */
			node_group_verify(ntree, gnode, (ID *)ngroup);
			node_group_output_verify(ngroup, output_node, (ID *)ngroup);

			/* create new internal link */
			output_sock = node_group_output_find_socket(output_node, iosock->identifier);
			nodeAddLink(ngroup, link->fromnode, link->fromsock, output_node, output_sock);
			
			/* redirect external link */
			link->fromnode = gnode;
			link->fromsock = node_group_find_output_socket(gnode, iosock->identifier);
		}
	}

	/* move nodes in the group to the center */
	for (node = ngroup->nodes.first; node; node = node->next) {
		if (node_group_make_use_node(node, gnode) && !node->parent) {
			node->locx -= center[0];
			node->locy -= center[1];
		}
	}
	
	/* expose all unlinked sockets too */
	if (expose_all) {
		for (node = ngroup->nodes.first; node; node = node->next) {
			if (node_group_make_use_node(node, gnode)) {
				for (sock = node->inputs.first; sock; sock = sock->next) {
					bNodeSocket *iosock, *input_sock;
					int skip = FALSE;
					for (link = ngroup->links.first; link; link = link->next) {
						if (link->tosock == sock) {
							skip = TRUE;
							break;
						}
					}
					if (skip)
						continue;
					
					iosock = ntreeAddSocketInterfaceFromSocket(ngroup, node, sock);
					
					node_group_input_verify(ngroup, input_node, (ID *)ngroup);
					
					/* create new internal link */
					input_sock = node_group_input_find_socket(input_node, iosock->identifier);
					nodeAddLink(ngroup, input_node, input_sock, node, sock);
				}
				
				for (sock = node->outputs.first; sock; sock = sock->next) {
					bNodeSocket *iosock, *output_sock;
					int skip = FALSE;
					for (link = ngroup->links.first; link; link = link->next)
						if (link->fromsock == sock)
							skip = TRUE;
					if (skip)
						continue;
					
					iosock = ntreeAddSocketInterfaceFromSocket(ngroup, node, sock);
					
					node_group_output_verify(ngroup, output_node, (ID *)ngroup);
					
					/* create new internal link */
					output_sock = node_group_output_find_socket(output_node, iosock->identifier);
					nodeAddLink(ngroup, node, sock, output_node, output_sock);
				}
			}
		}
	}

	/* update of the group tree */
	ngroup->update |= NTREE_UPDATE | NTREE_UPDATE_LINKS;
	/* update of the tree containing the group instance node */
	ntree->update |= NTREE_UPDATE_NODES | NTREE_UPDATE_LINKS;
}
Ejemplo n.º 18
0
static int node_mouse_select(Main *bmain, SpaceNode *snode, ARegion *ar, const int mval[2], short extend)
{
	bNode *node, *tnode;
	bNodeSocket *sock, *tsock;
	float cursor[2];
	int selected = 0;
	
	/* get mouse coordinates in view2d space */
	UI_view2d_region_to_view(&ar->v2d, mval[0], mval[1], &cursor[0], &cursor[1]);
	
	if (extend) {
		/* first do socket selection, these generally overlap with nodes.
		 * socket selection only in extend mode.
		 */
		if (node_find_indicated_socket(snode, &node, &sock, cursor, SOCK_IN)) {
			node_socket_toggle(node, sock, 1);
			selected = 1;
		}
		else if (node_find_indicated_socket(snode, &node, &sock, cursor, SOCK_OUT)) {
			if (sock->flag & SELECT) {
				node_socket_deselect(node, sock, 1);
			}
			else {
				/* only allow one selected output per node, for sensible linking.
				 * allows selecting outputs from different nodes though.
				 */
				if (node) {
					for (tsock = node->outputs.first; tsock; tsock = tsock->next)
						node_socket_deselect(node, tsock, 1);
				}
				node_socket_select(node, sock);
			}
			selected = 1;
		}
		else {
			/* find the closest visible node */
			node = node_under_mouse_select(snode->edittree, cursor[0], cursor[1]);
			
			if (node) {
				if ((node->flag & SELECT) && (node->flag & NODE_ACTIVE) == 0) {
					/* if node is selected but not active make it active
					 * before it'll be desleected
					 */
					ED_node_set_active(bmain, snode->edittree, node);
				}
				else {
					node_toggle(node);
					ED_node_set_active(bmain, snode->edittree, node);
				}

				selected = 1;
			}
		}
	}
	else {  /* extend == 0 */
		
		/* find the closest visible node */
		node = node_under_mouse_select(snode->edittree, cursor[0], cursor[1]);
		
		if (node) {
			for (tnode = snode->edittree->nodes.first; tnode; tnode = tnode->next) {
				nodeSetSelected(tnode, false);
			}
			nodeSetSelected(node, true);
			ED_node_set_active(bmain, snode->edittree, node);
			selected = 1;
		}
	}
	
	/* update node order */
	if (selected) {
		ED_node_set_active_viewer_key(snode);
		ED_node_sort(snode->edittree);
	}
	
	return selected;
}