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:
authorCampbell Barton <ideasman42@gmail.com>2012-08-01 23:11:17 +0400
committerCampbell Barton <ideasman42@gmail.com>2012-08-01 23:11:17 +0400
commit689c6133ea7f659fc61679c83ca216338533f98d (patch)
tree149ff60e91a4295a302cb0cacbf9a9631f8edad6
parentce90041239d6987ee47d1ef971c6b396ce8cf6e1 (diff)
split node_edit.c into separate files (add, group, relationshops), was almost 5000 loc.
-rw-r--r--source/blender/editors/include/ED_node.h6
-rw-r--r--source/blender/editors/space_node/CMakeLists.txt3
-rw-r--r--source/blender/editors/space_node/node_add.c431
-rw-r--r--source/blender/editors/space_node/node_edit.c2755
-rw-r--r--source/blender/editors/space_node/node_group.c1164
-rw-r--r--source/blender/editors/space_node/node_intern.h72
-rw-r--r--source/blender/editors/space_node/node_relationships.c1446
7 files changed, 3101 insertions, 2776 deletions
diff --git a/source/blender/editors/include/ED_node.h b/source/blender/editors/include/ED_node.h
index 8c79ad8f3e8..ca468a15771 100644
--- a/source/blender/editors/include/ED_node.h
+++ b/source/blender/editors/include/ED_node.h
@@ -61,12 +61,14 @@ 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_relationships.c */
+void ED_node_link_intersect_test(struct ScrArea *sa, int test);
+void ED_node_link_insert(struct ScrArea *sa);
+
/* node_edit.c */
void ED_node_shader_default(struct Scene *scene, struct ID *id);
void ED_node_composit_default(struct Scene *sce);
void ED_node_texture_default(struct Tex *tex);
-void ED_node_link_intersect_test(struct ScrArea *sa, int test);
-void ED_node_link_insert(struct ScrArea *sa);
int ED_node_select_check(ListBase *lb);
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);
diff --git a/source/blender/editors/space_node/CMakeLists.txt b/source/blender/editors/space_node/CMakeLists.txt
index 2d926a50f98..793a167b929 100644
--- a/source/blender/editors/space_node/CMakeLists.txt
+++ b/source/blender/editors/space_node/CMakeLists.txt
@@ -42,9 +42,12 @@ set(INC_SYS
set(SRC
drawnode.c
+ node_add.c
node_buttons.c
node_draw.c
node_edit.c
+ node_group.c
+ node_relationships.c
node_header.c
node_ops.c
node_select.c
diff --git a/source/blender/editors/space_node/node_add.c b/source/blender/editors/space_node/node_add.c
new file mode 100644
index 00000000000..5e58737606f
--- /dev/null
+++ b/source/blender/editors/space_node/node_add.c
@@ -0,0 +1,431 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2005 Blender Foundation.
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): David Millan Escriva, Juho Vepsäläinen, Nathan Letwory
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/editors/space_node/node_relationships.c
+ * \ingroup spnode
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <string.h>
+#include <errno.h>
+
+#include "MEM_guardedalloc.h"
+
+#include "DNA_ID.h"
+#include "DNA_lamp_types.h"
+#include "DNA_material_types.h"
+#include "DNA_node_types.h"
+#include "DNA_object_types.h"
+#include "DNA_particle_types.h"
+#include "DNA_scene_types.h"
+#include "DNA_world_types.h"
+#include "DNA_action_types.h"
+#include "DNA_anim_types.h"
+
+#include "BLI_math.h"
+#include "BLI_blenlib.h"
+#include "BLI_utildefines.h"
+
+#include "BKE_action.h"
+#include "BKE_animsys.h"
+#include "BKE_context.h"
+#include "BKE_depsgraph.h"
+#include "BKE_global.h"
+#include "BKE_image.h"
+#include "BKE_library.h"
+#include "BKE_main.h"
+#include "BKE_node.h"
+#include "BKE_material.h"
+#include "BKE_modifier.h"
+#include "BKE_paint.h"
+#include "BKE_scene.h"
+#include "BKE_screen.h"
+#include "BKE_texture.h"
+#include "BKE_report.h"
+
+#include "RE_pipeline.h"
+
+#include "IMB_imbuf_types.h"
+
+#include "ED_node.h"
+#include "ED_image.h"
+#include "ED_screen.h"
+#include "ED_space_api.h"
+#include "ED_render.h"
+
+#include "RNA_access.h"
+#include "RNA_define.h"
+#include "RNA_enum_types.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+#include "UI_view2d.h"
+
+#include "IMB_imbuf.h"
+
+#include "RNA_enum_types.h"
+
+#include "GPU_material.h"
+
+#include "node_intern.h"
+#include "NOD_socket.h"
+
+
+/* can be called from menus too, but they should do own undopush and redraws */
+bNode *node_add_node(SpaceNode *snode, Main *bmain, Scene *scene,
+ bNodeTemplate *ntemp, float locx, float locy)
+{
+ bNode *node = NULL, *gnode;
+
+ node_deselect_all(snode);
+
+ node = nodeAddNode(snode->edittree, ntemp);
+
+ /* generics */
+ if (node) {
+ node_select(node);
+
+ gnode = node_tree_get_editgroup(snode->nodetree);
+ // arbitrary y offset of 60 so its visible
+ if (gnode) {
+ nodeFromView(gnode, locx, locy + 60.0f, &node->locx, &node->locy);
+ }
+ else {
+ node->locx = locx;
+ node->locy = locy + 60.0f;
+ }
+
+ ntreeUpdateTree(snode->edittree);
+ ED_node_set_active(bmain, snode->edittree, node);
+
+ if (snode->nodetree->type == NTREE_COMPOSIT) {
+ if (ELEM4(node->type, CMP_NODE_R_LAYERS, CMP_NODE_COMPOSITE, CMP_NODE_DEFOCUS, CMP_NODE_OUTPUT_FILE)) {
+ node->id = &scene->id;
+ }
+ else if (ELEM3(node->type, CMP_NODE_MOVIECLIP, CMP_NODE_MOVIEDISTORTION, CMP_NODE_STABILIZE2D)) {
+ node->id = (ID *)scene->clip;
+ }
+
+ ntreeCompositForceHidden(snode->edittree, scene);
+ }
+
+ if (node->id)
+ id_us_plus(node->id);
+
+
+ if (snode->flag & SNODE_USE_HIDDEN_PREVIEW)
+ node->flag &= ~NODE_PREVIEW;
+
+ snode_update(snode, node);
+ }
+
+ if (snode->nodetree->type == NTREE_TEXTURE) {
+ ntreeTexCheckCyclics(snode->edittree);
+ }
+
+ return node;
+}
+
+/* ********************** Add reroute operator ***************** */
+static int add_reroute_intersect_check(bNodeLink *link, float mcoords[][2], int tot, float result[2])
+{
+ float coord_array[NODE_LINK_RESOL + 1][2];
+ int i, b;
+
+ if (node_link_bezier_points(NULL, NULL, link, coord_array, NODE_LINK_RESOL)) {
+
+ for (i = 0; i < tot - 1; i++)
+ for (b = 0; b < NODE_LINK_RESOL; b++)
+ if (isect_line_line_v2(mcoords[i], mcoords[i + 1], coord_array[b], coord_array[b + 1]) > 0) {
+ result[0] = (mcoords[i][0] + mcoords[i + 1][0]) / 2.0f;
+ result[1] = (mcoords[i][1] + mcoords[i + 1][1]) / 2.0f;
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static int add_reroute_exec(bContext *C, wmOperator *op)
+{
+ SpaceNode *snode = CTX_wm_space_node(C);
+ ARegion *ar = CTX_wm_region(C);
+ bNode *gnode = node_tree_get_editgroup(snode->nodetree);
+ float mcoords[256][2];
+ int i = 0;
+
+ RNA_BEGIN(op->ptr, itemptr, "path")
+ {
+ float loc[2];
+
+ RNA_float_get_array(&itemptr, "loc", loc);
+ UI_view2d_region_to_view(&ar->v2d, (short)loc[0], (short)loc[1],
+ &mcoords[i][0], &mcoords[i][1]);
+ i++;
+ if (i >= 256) break;
+ }
+ RNA_END;
+
+ if (i > 1) {
+ bNodeLink *link;
+ float insertPoint[2];
+
+ ED_preview_kill_jobs(C);
+
+ for (link = snode->edittree->links.first; link; link = link->next) {
+ if (add_reroute_intersect_check(link, mcoords, i, insertPoint)) {
+ bNodeTemplate ntemp;
+ bNode *rerouteNode;
+
+ node_deselect_all(snode);
+
+ ntemp.type = NODE_REROUTE;
+ rerouteNode = nodeAddNode(snode->edittree, &ntemp);
+ if (gnode) {
+ nodeFromView(gnode, insertPoint[0], insertPoint[1], &rerouteNode->locx, &rerouteNode->locy);
+ }
+ else {
+ rerouteNode->locx = insertPoint[0];
+ rerouteNode->locy = insertPoint[1];
+ }
+
+ nodeAddLink(snode->edittree, link->fromnode, link->fromsock, rerouteNode, rerouteNode->inputs.first);
+ link->fromnode = rerouteNode;
+ link->fromsock = rerouteNode->outputs.first;
+
+ break; // add one reroute at the time.
+ }
+ }
+
+ ntreeUpdateTree(snode->edittree);
+ snode_notify(C, snode);
+ snode_dag_update(C, snode);
+
+ return OPERATOR_FINISHED;
+ }
+
+ return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH;
+}
+
+void NODE_OT_add_reroute(wmOperatorType *ot)
+{
+ PropertyRNA *prop;
+
+ ot->name = "Add reroute";
+ ot->idname = "NODE_OT_add_reroute";
+
+ ot->invoke = WM_gesture_lines_invoke;
+ ot->modal = WM_gesture_lines_modal;
+ ot->exec = add_reroute_exec;
+ ot->cancel = WM_gesture_lines_cancel;
+
+ ot->poll = ED_operator_node_active;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ prop = RNA_def_property(ot->srna, "path", PROP_COLLECTION, PROP_NONE);
+ RNA_def_property_struct_runtime(prop, &RNA_OperatorMousePath);
+ /* internal */
+ RNA_def_int(ot->srna, "cursor", BC_CROSSCURSOR, 0, INT_MAX, "Cursor", "", 0, INT_MAX);
+}
+
+
+/* ****************** Add File Node Operator ******************* */
+
+static int node_add_file_exec(bContext *C, wmOperator *op)
+{
+ Main *bmain = CTX_data_main(C);
+ Scene *scene = CTX_data_scene(C);
+ SpaceNode *snode = CTX_wm_space_node(C);
+ bNode *node;
+ Image *ima = NULL;
+ bNodeTemplate ntemp;
+
+ /* check input variables */
+ if (RNA_struct_property_is_set(op->ptr, "filepath")) {
+ char path[FILE_MAX];
+ RNA_string_get(op->ptr, "filepath", path);
+
+ errno = 0;
+
+ ima = BKE_image_load_exists(path);
+
+ if (!ima) {
+ BKE_reportf(op->reports, RPT_ERROR, "Can't read image: \"%s\", %s", path, errno ? strerror(errno) : "Unsupported format");
+ return OPERATOR_CANCELLED;
+ }
+ }
+ else if (RNA_struct_property_is_set(op->ptr, "name")) {
+ char name[MAX_ID_NAME - 2];
+ RNA_string_get(op->ptr, "name", name);
+ ima = (Image *)BKE_libblock_find_name(ID_IM, name);
+
+ if (!ima) {
+ BKE_reportf(op->reports, RPT_ERROR, "Image named \"%s\", not found", name);
+ return OPERATOR_CANCELLED;
+ }
+ }
+
+ node_deselect_all(snode);
+
+ switch (snode->nodetree->type) {
+ case NTREE_SHADER:
+ ntemp.type = SH_NODE_TEX_IMAGE;
+ break;
+ case NTREE_TEXTURE:
+ ntemp.type = TEX_NODE_IMAGE;
+ break;
+ case NTREE_COMPOSIT:
+ ntemp.type = CMP_NODE_IMAGE;
+ break;
+ default:
+ return OPERATOR_CANCELLED;
+ }
+
+ ED_preview_kill_jobs(C);
+
+ node = node_add_node(snode, bmain, scene, &ntemp, snode->mx, snode->my);
+
+ if (!node) {
+ BKE_report(op->reports, RPT_WARNING, "Could not add an image node");
+ return OPERATOR_CANCELLED;
+ }
+
+ node->id = (ID *)ima;
+ id_us_plus(node->id);
+
+ snode_notify(C, snode);
+ snode_dag_update(C, snode);
+
+ return OPERATOR_FINISHED;
+}
+
+static int node_add_file_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);
+
+ if (RNA_struct_property_is_set(op->ptr, "filepath") || RNA_struct_property_is_set(op->ptr, "name"))
+ return node_add_file_exec(C, op);
+ else
+ return WM_operator_filesel(C, op, event);
+}
+
+void NODE_OT_add_file(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Add File Node";
+ ot->description = "Add a file node to the current node editor";
+ ot->idname = "NODE_OT_add_file";
+
+ /* callbacks */
+ ot->exec = node_add_file_exec;
+ ot->invoke = node_add_file_invoke;
+ ot->poll = ED_operator_node_active;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ WM_operator_properties_filesel(ot, FOLDERFILE | IMAGEFILE, FILE_SPECIAL, FILE_OPENFILE, WM_FILESEL_FILEPATH, FILE_DEFAULTDISPLAY); //XXX TODO, relative_path
+ RNA_def_string(ot->srna, "name", "Image", MAX_ID_NAME - 2, "Name", "Datablock name to assign");
+}
+
+
+/********************** New node tree operator *********************/
+
+static int new_node_tree_exec(bContext *C, wmOperator *op)
+{
+ SpaceNode *snode;
+ bNodeTree *ntree;
+ PointerRNA ptr, idptr;
+ PropertyRNA *prop;
+ int treetype;
+ char treename[MAX_ID_NAME - 2] = "NodeTree";
+
+ /* retrieve state */
+ snode = CTX_wm_space_node(C);
+
+ if (RNA_struct_property_is_set(op->ptr, "type"))
+ treetype = RNA_enum_get(op->ptr, "type");
+ else
+ treetype = snode->treetype;
+
+ if (RNA_struct_property_is_set(op->ptr, "name"))
+ RNA_string_get(op->ptr, "name", treename);
+
+ ntree = ntreeAddTree(treename, treetype, 0);
+ if (!ntree)
+ return OPERATOR_CANCELLED;
+
+ /* hook into UI */
+ uiIDContextProperty(C, &ptr, &prop);
+
+ if (prop) {
+ RNA_id_pointer_create(&ntree->id, &idptr);
+ RNA_property_pointer_set(&ptr, prop, idptr);
+ /* RNA_property_pointer_set increases the user count,
+ * fixed here as the editor is the initial user.
+ */
+ --ntree->id.us;
+ RNA_property_update(C, &ptr, prop);
+ }
+ else if (snode) {
+ Scene *scene = CTX_data_scene(C);
+ snode->nodetree = ntree;
+
+ ED_node_tree_update(snode, scene);
+ }
+
+ return OPERATOR_FINISHED;
+}
+
+void NODE_OT_new_node_tree(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "New Node Tree";
+ ot->idname = "NODE_OT_new_node_tree";
+ ot->description = "Create a new node tree";
+
+ /* api callbacks */
+ ot->exec = new_node_tree_exec;
+ ot->poll = ED_operator_node_active;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ RNA_def_enum(ot->srna, "type", nodetree_type_items, NTREE_COMPOSIT, "Tree Type", "");
+ RNA_def_string(ot->srna, "name", "NodeTree", MAX_ID_NAME - 2, "Name", "");
+}
diff --git a/source/blender/editors/space_node/node_edit.c b/source/blender/editors/space_node/node_edit.c
index f020b306db3..481c6544255 100644
--- a/source/blender/editors/space_node/node_edit.c
+++ b/source/blender/editors/space_node/node_edit.c
@@ -100,12 +100,6 @@
#include "node_intern.h"
#include "NOD_socket.h"
-static EnumPropertyItem socket_in_out_items[] = {
- { SOCK_IN, "SOCK_IN", 0, "Input", "" },
- { SOCK_OUT, "SOCK_OUT", 0, "Output", "" },
- { 0, NULL, 0, NULL, NULL },
-};
-
/* ***************** composite job manager ********************** */
typedef struct CompoJob {
@@ -223,7 +217,7 @@ void snode_composite_job(const bContext *C, ScrArea *sa)
/* ***************************************** */
/* operator poll callback */
-static int composite_node_active(bContext *C)
+int composite_node_active(bContext *C)
{
if (ED_operator_node_active(C)) {
SpaceNode *snode = CTX_wm_space_node(C);
@@ -234,7 +228,7 @@ static int composite_node_active(bContext *C)
}
/* also checks for edited groups */
-static bNode *editnode_get_active(bNodeTree *ntree)
+bNode *editnode_get_active(bNodeTree *ntree)
{
bNode *node;
@@ -610,7 +604,7 @@ void snode_set_context(SpaceNode *snode, Scene *scene)
node_tree_from_ID(snode->id, &snode->nodetree, &snode->edittree, NULL);
}
-static void snode_update(SpaceNode *snode, bNode *node)
+void snode_update(SpaceNode *snode, bNode *node)
{
bNode *gnode;
@@ -789,743 +783,6 @@ static void edit_node_properties_get(wmOperator *op, bNodeTree *ntree, bNode **r
}
#endif
-/* ***************** Edit Group operator ************* */
-
-void snode_make_group_editable(SpaceNode *snode, bNode *gnode)
-{
- bNode *node;
-
- /* make sure nothing has group editing on */
- for (node = snode->nodetree->nodes.first; node; node = node->next) {
- nodeGroupEditClear(node);
-
- /* while we're here, clear texture active */
- if (node->typeinfo->nclass == NODE_CLASS_TEXTURE) {
- /* this is not 100% sure to be reliable, see comment on the flag */
- node->flag &= ~NODE_ACTIVE_TEXTURE;
- }
- }
-
- if (gnode == NULL) {
- /* with NULL argument we do a toggle */
- if (snode->edittree == snode->nodetree)
- gnode = nodeGetActive(snode->nodetree);
- }
-
- if (gnode) {
- snode->edittree = nodeGroupEditSet(gnode, 1);
-
- /* deselect all other nodes, so we can also do grabbing of entire subtree */
- for (node = snode->nodetree->nodes.first; node; node = node->next) {
- node_deselect(node);
-
- if (node->typeinfo->nclass == NODE_CLASS_TEXTURE) {
- /* this is not 100% sure to be reliable, see comment on the flag */
- node->flag &= ~NODE_ACTIVE_TEXTURE;
- }
- }
- node_select(gnode);
- }
- else
- snode->edittree = snode->nodetree;
-}
-
-static int node_group_edit_exec(bContext *C, wmOperator *UNUSED(op))
-{
- SpaceNode *snode = CTX_wm_space_node(C);
-
- ED_preview_kill_jobs(C);
-
- if (snode->nodetree == snode->edittree) {
- bNode *gnode = nodeGetActive(snode->edittree);
- snode_make_group_editable(snode, gnode);
- }
- else
- snode_make_group_editable(snode, NULL);
-
- WM_event_add_notifier(C, NC_SCENE | ND_NODES, NULL);
-
- return OPERATOR_FINISHED;
-}
-
-static int node_group_edit_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(event))
-{
- SpaceNode *snode = CTX_wm_space_node(C);
- bNode *gnode;
-
- /* XXX callback? */
- if (snode->nodetree == snode->edittree) {
- gnode = nodeGetActive(snode->edittree);
- if (gnode && gnode->id && GS(gnode->id->name) == ID_NT && gnode->id->lib) {
- uiPupMenuOkee(C, op->type->idname, "Make group local?");
- return OPERATOR_CANCELLED;
- }
- }
-
- return node_group_edit_exec(C, op);
-}
-
-void NODE_OT_group_edit(wmOperatorType *ot)
-{
- /* identifiers */
- ot->name = "Edit Group";
- ot->description = "Edit node group";
- ot->idname = "NODE_OT_group_edit";
-
- /* api callbacks */
- ot->invoke = node_group_edit_invoke;
- ot->exec = node_group_edit_exec;
- ot->poll = ED_operator_node_active;
-
- /* flags */
- ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
-}
-
-/* ***************** Add Group Socket operator ************* */
-
-static int node_group_socket_add_exec(bContext *C, wmOperator *op)
-{
- SpaceNode *snode = CTX_wm_space_node(C);
- int in_out = -1;
- char name[MAX_NAME] = "";
- int type = SOCK_FLOAT;
- bNodeTree *ngroup = snode->edittree;
- /* bNodeSocket *sock; */ /* UNUSED */
-
- ED_preview_kill_jobs(C);
-
- if (RNA_struct_property_is_set(op->ptr, "name"))
- RNA_string_get(op->ptr, "name", name);
-
- if (RNA_struct_property_is_set(op->ptr, "type"))
- type = RNA_enum_get(op->ptr, "type");
-
- if (RNA_struct_property_is_set(op->ptr, "in_out"))
- in_out = RNA_enum_get(op->ptr, "in_out");
- else
- return OPERATOR_CANCELLED;
-
- /* using placeholder subtype first */
- /* sock = */ /* UNUSED */ node_group_add_socket(ngroup, name, type, in_out);
-
- ntreeUpdateTree(ngroup);
-
- snode_notify(C, snode);
-
- return OPERATOR_FINISHED;
-}
-
-void NODE_OT_group_socket_add(wmOperatorType *ot)
-{
- /* identifiers */
- ot->name = "Add Group Socket";
- ot->description = "Add node group socket";
- ot->idname = "NODE_OT_group_socket_add";
-
- /* api callbacks */
- ot->exec = node_group_socket_add_exec;
- ot->poll = ED_operator_node_active;
-
- /* flags */
- ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
-
- RNA_def_enum(ot->srna, "in_out", socket_in_out_items, SOCK_IN, "Socket Type", "Input or Output");
- RNA_def_string(ot->srna, "name", "", MAX_NAME, "Name", "Group socket name");
- RNA_def_enum(ot->srna, "type", node_socket_type_items, SOCK_FLOAT, "Type", "Type of the group socket");
-}
-
-/* ***************** Remove Group Socket operator ************* */
-
-static int node_group_socket_remove_exec(bContext *C, wmOperator *op)
-{
- SpaceNode *snode = CTX_wm_space_node(C);
- int index = -1;
- int in_out = -1;
- bNodeTree *ngroup = snode->edittree;
- bNodeSocket *sock;
-
- ED_preview_kill_jobs(C);
-
- if (RNA_struct_property_is_set(op->ptr, "index"))
- index = RNA_int_get(op->ptr, "index");
- else
- return OPERATOR_CANCELLED;
-
- if (RNA_struct_property_is_set(op->ptr, "in_out"))
- in_out = RNA_enum_get(op->ptr, "in_out");
- else
- return OPERATOR_CANCELLED;
-
- sock = (bNodeSocket *)BLI_findlink(in_out == SOCK_IN ? &ngroup->inputs : &ngroup->outputs, index);
- if (sock) {
- node_group_remove_socket(ngroup, sock, in_out);
- ntreeUpdateTree(ngroup);
-
- snode_notify(C, snode);
- }
-
- return OPERATOR_FINISHED;
-}
-
-void NODE_OT_group_socket_remove(wmOperatorType *ot)
-{
- /* identifiers */
- ot->name = "Remove Group Socket";
- ot->description = "Remove a node group socket";
- ot->idname = "NODE_OT_group_socket_remove";
-
- /* api callbacks */
- ot->exec = node_group_socket_remove_exec;
- ot->poll = ED_operator_node_active;
-
- /* flags */
- ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
-
- RNA_def_int(ot->srna, "index", 0, 0, INT_MAX, "Index", "", 0, INT_MAX);
- RNA_def_enum(ot->srna, "in_out", socket_in_out_items, SOCK_IN, "Socket Type", "Input or Output");
-}
-
-/* ***************** Move Group Socket Up operator ************* */
-
-static int node_group_socket_move_up_exec(bContext *C, wmOperator *op)
-{
- SpaceNode *snode = CTX_wm_space_node(C);
- int index = -1;
- int in_out = -1;
- bNodeTree *ngroup = snode->edittree;
- bNodeSocket *sock, *prev;
-
- ED_preview_kill_jobs(C);
-
- if (RNA_struct_property_is_set(op->ptr, "index"))
- index = RNA_int_get(op->ptr, "index");
- else
- return OPERATOR_CANCELLED;
-
- if (RNA_struct_property_is_set(op->ptr, "in_out"))
- in_out = RNA_enum_get(op->ptr, "in_out");
- else
- return OPERATOR_CANCELLED;
-
- /* swap */
- if (in_out == SOCK_IN) {
- sock = (bNodeSocket *)BLI_findlink(&ngroup->inputs, index);
- prev = sock->prev;
- /* can't move up the first socket */
- if (!prev)
- return OPERATOR_CANCELLED;
- BLI_remlink(&ngroup->inputs, sock);
- BLI_insertlinkbefore(&ngroup->inputs, prev, sock);
-
- ngroup->update |= NTREE_UPDATE_GROUP_IN;
- }
- else if (in_out == SOCK_OUT) {
- sock = (bNodeSocket *)BLI_findlink(&ngroup->outputs, index);
- prev = sock->prev;
- /* can't move up the first socket */
- if (!prev)
- return OPERATOR_CANCELLED;
- BLI_remlink(&ngroup->outputs, sock);
- BLI_insertlinkbefore(&ngroup->outputs, prev, sock);
-
- ngroup->update |= NTREE_UPDATE_GROUP_OUT;
- }
- ntreeUpdateTree(ngroup);
-
- snode_notify(C, snode);
-
- return OPERATOR_FINISHED;
-}
-
-void NODE_OT_group_socket_move_up(wmOperatorType *ot)
-{
- /* identifiers */
- ot->name = "Move Group Socket Up";
- ot->description = "Move up node group socket";
- ot->idname = "NODE_OT_group_socket_move_up";
-
- /* api callbacks */
- ot->exec = node_group_socket_move_up_exec;
- ot->poll = ED_operator_node_active;
-
- /* flags */
- ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
-
- RNA_def_int(ot->srna, "index", 0, 0, INT_MAX, "Index", "", 0, INT_MAX);
- RNA_def_enum(ot->srna, "in_out", socket_in_out_items, SOCK_IN, "Socket Type", "Input or Output");
-}
-
-/* ***************** Move Group Socket Up operator ************* */
-
-static int node_group_socket_move_down_exec(bContext *C, wmOperator *op)
-{
- SpaceNode *snode = CTX_wm_space_node(C);
- int index = -1;
- int in_out = -1;
- bNodeTree *ngroup = snode->edittree;
- bNodeSocket *sock, *next;
-
- ED_preview_kill_jobs(C);
-
- if (RNA_struct_property_is_set(op->ptr, "index"))
- index = RNA_int_get(op->ptr, "index");
- else
- return OPERATOR_CANCELLED;
-
- if (RNA_struct_property_is_set(op->ptr, "in_out"))
- in_out = RNA_enum_get(op->ptr, "in_out");
- else
- return OPERATOR_CANCELLED;
-
- /* swap */
- if (in_out == SOCK_IN) {
- sock = (bNodeSocket *)BLI_findlink(&ngroup->inputs, index);
- next = sock->next;
- /* can't move down the last socket */
- if (!next)
- return OPERATOR_CANCELLED;
- BLI_remlink(&ngroup->inputs, sock);
- BLI_insertlinkafter(&ngroup->inputs, next, sock);
-
- ngroup->update |= NTREE_UPDATE_GROUP_IN;
- }
- else if (in_out == SOCK_OUT) {
- sock = (bNodeSocket *)BLI_findlink(&ngroup->outputs, index);
- next = sock->next;
- /* can't move down the last socket */
- if (!next)
- return OPERATOR_CANCELLED;
- BLI_remlink(&ngroup->outputs, sock);
- BLI_insertlinkafter(&ngroup->outputs, next, sock);
-
- ngroup->update |= NTREE_UPDATE_GROUP_OUT;
- }
- ntreeUpdateTree(ngroup);
-
- snode_notify(C, snode);
-
- return OPERATOR_FINISHED;
-}
-
-void NODE_OT_group_socket_move_down(wmOperatorType *ot)
-{
- /* identifiers */
- ot->name = "Move Group Socket Down";
- ot->description = "Move down node group socket";
- ot->idname = "NODE_OT_group_socket_move_down";
-
- /* api callbacks */
- ot->exec = node_group_socket_move_down_exec;
- ot->poll = ED_operator_node_active;
-
- /* flags */
- ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
-
- RNA_def_int(ot->srna, "index", 0, 0, INT_MAX, "Index", "", 0, INT_MAX);
- RNA_def_enum(ot->srna, "in_out", socket_in_out_items, SOCK_IN, "Socket Type", "Input or Output");
-}
-
-/* ******************** Ungroup operator ********************** */
-
-/* returns 1 if its OK */
-static int node_group_ungroup(bNodeTree *ntree, bNode *gnode)
-{
- bNodeLink *link, *linkn;
- bNode *node, *nextn;
- bNodeTree *ngroup, *wgroup;
- ListBase anim_basepaths = {NULL, NULL};
-
- ngroup = (bNodeTree *)gnode->id;
- if (ngroup == NULL) return 0;
-
- /* clear new pointers, set in copytree */
- for (node = ntree->nodes.first; node; node = node->next)
- node->new_node = NULL;
-
- /* wgroup is a temporary copy of the NodeTree we're merging in
- * - all of wgroup's nodes are transferred across to their new home
- * - ngroup (i.e. the source NodeTree) is left unscathed
- */
- wgroup = ntreeCopyTree(ngroup);
-
- /* add the nodes into the ntree */
- for (node = wgroup->nodes.first; node; node = nextn) {
- nextn = node->next;
-
- /* 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 (wgroup->adt) {
- PointerRNA ptr;
- char *path;
-
- RNA_pointer_create(&wgroup->id, &RNA_Node, node, &ptr);
- path = RNA_path_from_ID_to_struct(&ptr);
-
- if (path)
- BLI_addtail(&anim_basepaths, BLI_genericNodeN(path));
- }
-
- /* migrate node */
- BLI_remlink(&wgroup->nodes, node);
- BLI_addtail(&ntree->nodes, node);
-
- /* ensure unique node name in the nodee tree */
- nodeUniqueName(ntree, node);
-
- node->locx += gnode->locx;
- node->locy += gnode->locy;
-
- node->flag |= NODE_SELECT;
- }
-
- /* restore external links to and from the gnode */
- for (link = ntree->links.first; link; link = link->next) {
- if (link->fromnode == gnode) {
- if (link->fromsock->groupsock) {
- bNodeSocket *gsock = link->fromsock->groupsock;
- if (gsock->link) {
- if (gsock->link->fromnode) {
- /* NB: using the new internal copies here! the groupsock pointer still maps to the old tree */
- link->fromnode = (gsock->link->fromnode ? gsock->link->fromnode->new_node : NULL);
- link->fromsock = gsock->link->fromsock->new_sock;
- }
- else {
- /* group output directly maps to group input */
- bNodeSocket *insock = node_group_find_input(gnode, gsock->link->fromsock);
- if (insock->link) {
- link->fromnode = insock->link->fromnode;
- link->fromsock = insock->link->fromsock;
- }
- }
- }
- else {
- /* copy the default input value from the group socket default to the external socket */
- node_socket_convert_default_value(link->tosock->type, link->tosock->default_value, gsock->type, gsock->default_value);
- }
- }
- }
- }
- /* remove internal output links, these are not used anymore */
- for (link = wgroup->links.first; link; link = linkn) {
- linkn = link->next;
- if (!link->tonode)
- nodeRemLink(wgroup, link);
- }
- /* restore links from internal nodes */
- for (link = wgroup->links.first; link; link = linkn) {
- linkn = link->next;
- /* indicates link to group input */
- if (!link->fromnode) {
- /* NB: can't use find_group_node_input here,
- * because gnode sockets still point to the old tree!
- */
- bNodeSocket *insock;
- for (insock = gnode->inputs.first; insock; insock = insock->next)
- if (insock->groupsock->new_sock == link->fromsock)
- break;
- if (insock->link) {
- link->fromnode = insock->link->fromnode;
- link->fromsock = insock->link->fromsock;
- }
- else {
- /* copy the default input value from the group node socket default to the internal socket */
- node_socket_convert_default_value(link->tosock->type, link->tosock->default_value, insock->type, insock->default_value);
- nodeRemLink(wgroup, link);
- }
- }
- }
-
- /* add internal links to the ntree */
- for (link = wgroup->links.first; link; link = linkn) {
- linkn = link->next;
- BLI_remlink(&wgroup->links, link);
- BLI_addtail(&ntree->links, link);
- }
-
- /* and copy across the animation,
- * note that the animation data's action can be NULL here */
- if (wgroup->adt) {
- LinkData *ld, *ldn = NULL;
- bAction *waction;
-
- /* firstly, wgroup needs to temporary dummy action that can be destroyed, as it shares copies */
- waction = wgroup->adt->action = BKE_action_copy(wgroup->adt->action);
-
- /* now perform the moving */
- BKE_animdata_separate_by_basepath(&wgroup->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);
- }
-
- /* free temp action too */
- if (waction) {
- BKE_libblock_free(&G.main->action, waction);
- }
- }
-
- /* delete the group instance. this also removes old input links! */
- nodeFreeNode(ntree, gnode);
-
- /* free the group tree (takes care of user count) */
- BKE_libblock_free(&G.main->nodetree, wgroup);
-
- ntree->update |= NTREE_UPDATE_NODES | NTREE_UPDATE_LINKS;
-
- return 1;
-}
-
-static int node_group_ungroup_exec(bContext *C, wmOperator *op)
-{
- SpaceNode *snode = CTX_wm_space_node(C);
- bNode *gnode;
-
- ED_preview_kill_jobs(C);
-
- /* are we inside of a group? */
- gnode = node_tree_get_editgroup(snode->nodetree);
- if (gnode)
- snode_make_group_editable(snode, NULL);
-
- gnode = nodeGetActive(snode->edittree);
- if (gnode == NULL)
- return OPERATOR_CANCELLED;
-
- if (gnode->type != NODE_GROUP) {
- BKE_report(op->reports, RPT_WARNING, "Not a group");
- return OPERATOR_CANCELLED;
- }
- else if (node_group_ungroup(snode->nodetree, gnode)) {
- ntreeUpdateTree(snode->nodetree);
- }
- else {
- BKE_report(op->reports, RPT_WARNING, "Can't ungroup");
- return OPERATOR_CANCELLED;
- }
-
- snode_notify(C, snode);
- snode_dag_update(C, snode);
-
- return OPERATOR_FINISHED;
-}
-
-void NODE_OT_group_ungroup(wmOperatorType *ot)
-{
- /* identifiers */
- ot->name = "Ungroup";
- ot->description = "Ungroup selected nodes";
- ot->idname = "NODE_OT_group_ungroup";
-
- /* api callbacks */
- ot->exec = node_group_ungroup_exec;
- ot->poll = ED_operator_node_active;
-
- /* flags */
- ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
-}
-
-/* ******************** Separate operator ********************** */
-
-/* returns 1 if its OK */
-static int node_group_separate_selected(bNodeTree *ntree, bNode *gnode, int make_copy)
-{
- bNodeLink *link, *link_next;
- bNode *node, *node_next, *newnode;
- bNodeTree *ngroup;
- ListBase anim_basepaths = {NULL, NULL};
-
- ngroup = (bNodeTree *)gnode->id;
- if (ngroup == NULL) return 0;
-
- /* deselect all nodes in the target tree */
- for (node = ntree->nodes.first; node; node = node->next)
- node_deselect(node);
-
- /* 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;
-
- 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);
-
- newnode->locx += gnode->locx;
- newnode->locy += gnode->locy;
- }
-
- /* 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;
-}
-
-typedef enum eNodeGroupSeparateType {
- NODE_GS_COPY,
- NODE_GS_MOVE
-} eNodeGroupSeparateType;
-
-/* Operator Property */
-EnumPropertyItem node_group_separate_types[] = {
- {NODE_GS_COPY, "COPY", 0, "Copy", "Copy to parent node tree, keep group intact"},
- {NODE_GS_MOVE, "MOVE", 0, "Move", "Move to parent node tree, remove from group"},
- {0, NULL, 0, NULL, NULL}
-};
-
-static int node_group_separate_exec(bContext *C, wmOperator *op)
-{
- SpaceNode *snode = CTX_wm_space_node(C);
- bNode *gnode;
- int type = RNA_enum_get(op->ptr, "type");
-
- ED_preview_kill_jobs(C);
-
- /* are we inside of a group? */
- gnode = node_tree_get_editgroup(snode->nodetree);
- if (!gnode) {
- BKE_report(op->reports, RPT_WARNING, "Not inside node group");
- return OPERATOR_CANCELLED;
- }
-
- switch (type) {
- case NODE_GS_COPY:
- if (!node_group_separate_selected(snode->nodetree, gnode, 1)) {
- BKE_report(op->reports, RPT_WARNING, "Can't separate nodes");
- return OPERATOR_CANCELLED;
- }
- break;
- case NODE_GS_MOVE:
- if (!node_group_separate_selected(snode->nodetree, gnode, 0)) {
- BKE_report(op->reports, RPT_WARNING, "Can't separate nodes");
- return OPERATOR_CANCELLED;
- }
- break;
- }
-
- /* switch to parent tree */
- snode_make_group_editable(snode, NULL);
-
- ntreeUpdateTree(snode->nodetree);
-
- snode_notify(C, snode);
- snode_dag_update(C, snode);
-
- return OPERATOR_FINISHED;
-}
-
-static int node_group_separate_invoke(bContext *C, wmOperator *UNUSED(op), wmEvent *UNUSED(event))
-{
- uiPopupMenu *pup = uiPupMenuBegin(C, "Separate", ICON_NONE);
- uiLayout *layout = uiPupMenuLayout(pup);
-
- uiLayoutSetOperatorContext(layout, WM_OP_EXEC_DEFAULT);
- uiItemEnumO(layout, "NODE_OT_group_separate", NULL, 0, "type", NODE_GS_COPY);
- uiItemEnumO(layout, "NODE_OT_group_separate", NULL, 0, "type", NODE_GS_MOVE);
-
- uiPupMenuEnd(C, pup);
-
- return OPERATOR_CANCELLED;
-}
-
-void NODE_OT_group_separate(wmOperatorType *ot)
-{
- /* identifiers */
- ot->name = "Separate";
- ot->description = "Separate selected nodes from the node group";
- ot->idname = "NODE_OT_group_separate";
-
- /* api callbacks */
- ot->invoke = node_group_separate_invoke;
- ot->exec = node_group_separate_exec;
- ot->poll = ED_operator_node_active;
-
- /* flags */
- ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
-
- RNA_def_enum(ot->srna, "type", node_group_separate_types, NODE_GS_COPY, "Type", "");
-}
-
/* ************************** Node generic ************** */
/* is rct in visible part of node? */
@@ -2081,140 +1338,6 @@ void node_set_hidden_sockets(SpaceNode *snode, bNode *node, int set)
}
}
-static int node_link_viewer(const bContext *C, bNode *tonode)
-{
- SpaceNode *snode = CTX_wm_space_node(C);
- bNode *node;
- bNodeLink *link;
- bNodeSocket *sock;
-
- /* context check */
- if (tonode == NULL || tonode->outputs.first == NULL)
- return OPERATOR_CANCELLED;
- if (ELEM(tonode->type, CMP_NODE_VIEWER, CMP_NODE_SPLITVIEWER))
- return OPERATOR_CANCELLED;
-
- /* get viewer */
- for (node = snode->edittree->nodes.first; node; node = node->next)
- if (ELEM(node->type, CMP_NODE_VIEWER, CMP_NODE_SPLITVIEWER))
- if (node->flag & NODE_DO_OUTPUT)
- break;
- /* no viewer, we make one active */
- if (node == NULL) {
- for (node = snode->edittree->nodes.first; node; node = node->next) {
- if (ELEM(node->type, CMP_NODE_VIEWER, CMP_NODE_SPLITVIEWER)) {
- node->flag |= NODE_DO_OUTPUT;
- break;
- }
- }
- }
-
- sock = NULL;
-
- /* try to find an already connected socket to cycle to the next */
- if (node) {
- link = NULL;
- for (link = snode->edittree->links.first; link; link = link->next)
- if (link->tonode == node && link->fromnode == tonode)
- if (link->tosock == node->inputs.first)
- break;
- if (link) {
- /* unlink existing connection */
- sock = link->fromsock;
- nodeRemLink(snode->edittree, link);
-
- /* find a socket after the previously connected socket */
- for (sock = sock->next; sock; sock = sock->next)
- if (!nodeSocketIsHidden(sock))
- break;
- }
- }
-
- /* find a socket starting from the first socket */
- if (!sock) {
- for (sock = tonode->outputs.first; sock; sock = sock->next)
- if (!nodeSocketIsHidden(sock))
- break;
- }
-
- if (sock) {
- /* add a new viewer if none exists yet */
- if (!node) {
- Main *bmain = CTX_data_main(C);
- Scene *scene = CTX_data_scene(C);
- bNodeTemplate ntemp;
-
- ntemp.type = CMP_NODE_VIEWER;
- /* XXX location is a quick hack, just place it next to the linked socket */
- node = node_add_node(snode, bmain, scene, &ntemp, sock->locx + 100, sock->locy);
- if (!node)
- return OPERATOR_CANCELLED;
-
- link = NULL;
- }
- else {
- /* get link to viewer */
- for (link = snode->edittree->links.first; link; link = link->next)
- if (link->tonode == node && link->tosock == node->inputs.first)
- break;
- }
-
- if (link == NULL) {
- nodeAddLink(snode->edittree, tonode, sock, node, node->inputs.first);
- }
- else {
- link->fromnode = tonode;
- link->fromsock = sock;
- /* make sure the dependency sorting is updated */
- snode->edittree->update |= NTREE_UPDATE_LINKS;
- }
- ntreeUpdateTree(snode->edittree);
- snode_update(snode, node);
- }
-
- return OPERATOR_FINISHED;
-}
-
-
-static int node_active_link_viewer(bContext *C, wmOperator *UNUSED(op))
-{
- SpaceNode *snode = CTX_wm_space_node(C);
- bNode *node;
-
- node = editnode_get_active(snode->edittree);
-
- if (!node)
- return OPERATOR_CANCELLED;
-
- ED_preview_kill_jobs(C);
-
- if (node_link_viewer(C, node) == OPERATOR_CANCELLED)
- return OPERATOR_CANCELLED;
-
- snode_notify(C, snode);
-
- return OPERATOR_FINISHED;
-}
-
-
-
-void NODE_OT_link_viewer(wmOperatorType *ot)
-{
- /* identifiers */
- ot->name = "Link to Viewer Node";
- ot->description = "Link to viewer node";
- ot->idname = "NODE_OT_link_viewer";
-
- /* api callbacks */
- ot->exec = node_active_link_viewer;
- ot->poll = composite_node_active;
-
- /* flags */
- ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
-}
-
-
-
/* return 0, nothing done */
static int UNUSED_FUNCTION(node_mouse_groupheader) (SpaceNode * snode)
{
@@ -2331,278 +1454,6 @@ int node_find_indicated_socket(SpaceNode *snode, bNode **nodep, bNodeSocket **so
return 0;
}
-static int outside_group_rect(SpaceNode *snode)
-{
- bNode *gnode = node_tree_get_editgroup(snode->nodetree);
- if (gnode) {
- return (snode->mx < gnode->totr.xmin ||
- snode->mx >= gnode->totr.xmax ||
- snode->my < gnode->totr.ymin ||
- snode->my >= gnode->totr.ymax);
- }
- return 0;
-}
-
-/* ****************** Add *********************** */
-
-
-typedef struct bNodeListItem {
- struct bNodeListItem *next, *prev;
- struct bNode *node;
-} bNodeListItem;
-
-static int sort_nodes_locx(void *a, void *b)
-{
- bNodeListItem *nli1 = (bNodeListItem *)a;
- bNodeListItem *nli2 = (bNodeListItem *)b;
- bNode *node1 = nli1->node;
- bNode *node2 = nli2->node;
-
- if (node1->locx > node2->locx)
- return 1;
- else
- return 0;
-}
-
-static int socket_is_available(bNodeTree *UNUSED(ntree), bNodeSocket *sock, int allow_used)
-{
- if (nodeSocketIsHidden(sock))
- return 0;
-
- if (!allow_used && (sock->flag & SOCK_IN_USE))
- return 0;
-
- return 1;
-}
-
-static bNodeSocket *best_socket_output(bNodeTree *ntree, bNode *node, bNodeSocket *sock_target, int allow_multiple)
-{
- bNodeSocket *sock;
-
- /* first look for selected output */
- for (sock = node->outputs.first; sock; sock = sock->next) {
- if (!socket_is_available(ntree, sock, allow_multiple))
- continue;
-
- if (sock->flag & SELECT)
- return sock;
- }
-
- /* try to find a socket with a matching name */
- for (sock = node->outputs.first; sock; sock = sock->next) {
- if (!socket_is_available(ntree, sock, allow_multiple))
- continue;
-
- /* check for same types */
- if (sock->type == sock_target->type) {
- if (strcmp(sock->name, sock_target->name) == 0)
- return sock;
- }
- }
-
- /* otherwise settle for the first available socket of the right type */
- for (sock = node->outputs.first; sock; sock = sock->next) {
-
- if (!socket_is_available(ntree, sock, allow_multiple))
- continue;
-
- /* check for same types */
- if (sock->type == sock_target->type) {
- return sock;
- }
- }
-
- return NULL;
-}
-
-/* this is a bit complicated, but designed to prioritize finding
- * sockets of higher types, such as image, first */
-static bNodeSocket *best_socket_input(bNodeTree *ntree, bNode *node, int num, int replace)
-{
- bNodeSocket *sock;
- int socktype, maxtype = 0;
- int a = 0;
-
- for (sock = node->inputs.first; sock; sock = sock->next) {
- maxtype = MAX2(sock->type, maxtype);
- }
-
- /* find sockets of higher 'types' first (i.e. image) */
- for (socktype = maxtype; socktype >= 0; socktype--) {
- for (sock = node->inputs.first; sock; sock = sock->next) {
-
- if (!socket_is_available(ntree, sock, replace)) {
- a++;
- continue;
- }
-
- if (sock->type == socktype) {
- /* increment to make sure we don't keep finding
- * the same socket on every attempt running this function */
- a++;
- if (a > num)
- return sock;
- }
- }
- }
-
- return NULL;
-}
-
-static int snode_autoconnect_input(SpaceNode *snode, bNode *node_fr, bNodeSocket *sock_fr, bNode *node_to, bNodeSocket *sock_to, int replace)
-{
- bNodeTree *ntree = snode->edittree;
- bNodeLink *link;
-
- /* then we can connect */
- if (replace)
- nodeRemSocketLinks(ntree, sock_to);
-
- link = nodeAddLink(ntree, node_fr, sock_fr, node_to, sock_to);
- /* validate the new link */
- ntreeUpdateTree(ntree);
- if (!(link->flag & NODE_LINK_VALID)) {
- nodeRemLink(ntree, link);
- return 0;
- }
-
- snode_update(snode, node_to);
- return 1;
-}
-
-void snode_autoconnect(SpaceNode *snode, int allow_multiple, int replace)
-{
- bNodeTree *ntree = snode->edittree;
- ListBase *nodelist = MEM_callocN(sizeof(ListBase), "items_list");
- bNodeListItem *nli;
- bNode *node;
- int i, numlinks = 0;
-
- for (node = ntree->nodes.first; node; node = node->next) {
- if (node->flag & NODE_SELECT) {
- nli = MEM_mallocN(sizeof(bNodeListItem), "temporary node list item");
- nli->node = node;
- BLI_addtail(nodelist, nli);
- }
- }
-
- /* sort nodes left to right */
- BLI_sortlist(nodelist, sort_nodes_locx);
-
- for (nli = nodelist->first; nli; nli = nli->next) {
- bNode *node_fr, *node_to;
- bNodeSocket *sock_fr, *sock_to;
- int has_selected_inputs = 0;
-
- if (nli->next == NULL) break;
-
- node_fr = nli->node;
- node_to = nli->next->node;
-
- /* if there are selected sockets, connect those */
- for (sock_to = node_to->inputs.first; sock_to; sock_to = sock_to->next) {
- if (sock_to->flag & SELECT) {
- has_selected_inputs = 1;
-
- if (!socket_is_available(ntree, sock_to, replace))
- continue;
-
- /* check for an appropriate output socket to connect from */
- sock_fr = best_socket_output(ntree, node_fr, sock_to, allow_multiple);
- if (!sock_fr)
- continue;
-
- if (snode_autoconnect_input(snode, node_fr, sock_fr, node_to, sock_to, replace))
- ++numlinks;
- }
- }
-
- if (!has_selected_inputs) {
- /* no selected inputs, connect by finding suitable match */
- int num_inputs = BLI_countlist(&node_to->inputs);
-
- for (i = 0; i < num_inputs; i++) {
-
- /* find the best guess input socket */
- sock_to = best_socket_input(ntree, node_to, i, replace);
- if (!sock_to)
- continue;
-
- /* check for an appropriate output socket to connect from */
- sock_fr = best_socket_output(ntree, node_fr, sock_to, allow_multiple);
- if (!sock_fr)
- continue;
-
- if (snode_autoconnect_input(snode, node_fr, sock_fr, node_to, sock_to, replace)) {
- ++numlinks;
- break;
- }
- }
- }
- }
-
- if (numlinks > 0) {
- ntreeUpdateTree(ntree);
- }
-
- BLI_freelistN(nodelist);
- MEM_freeN(nodelist);
-}
-
-/* can be called from menus too, but they should do own undopush and redraws */
-bNode *node_add_node(SpaceNode *snode, Main *bmain, Scene *scene, bNodeTemplate *ntemp, float locx, float locy)
-{
- bNode *node = NULL, *gnode;
-
- node_deselect_all(snode);
-
- node = nodeAddNode(snode->edittree, ntemp);
-
- /* generics */
- if (node) {
- node_select(node);
-
- gnode = node_tree_get_editgroup(snode->nodetree);
- // arbitrary y offset of 60 so its visible
- if (gnode) {
- nodeFromView(gnode, locx, locy + 60.0f, &node->locx, &node->locy);
- }
- else {
- node->locx = locx;
- node->locy = locy + 60.0f;
- }
-
- ntreeUpdateTree(snode->edittree);
- ED_node_set_active(bmain, snode->edittree, node);
-
- if (snode->nodetree->type == NTREE_COMPOSIT) {
- if (ELEM4(node->type, CMP_NODE_R_LAYERS, CMP_NODE_COMPOSITE, CMP_NODE_DEFOCUS, CMP_NODE_OUTPUT_FILE)) {
- node->id = &scene->id;
- }
- else if (ELEM3(node->type, CMP_NODE_MOVIECLIP, CMP_NODE_MOVIEDISTORTION, CMP_NODE_STABILIZE2D)) {
- node->id = (ID *)scene->clip;
- }
-
- ntreeCompositForceHidden(snode->edittree, scene);
- }
-
- if (node->id)
- id_us_plus(node->id);
-
-
- if (snode->flag & SNODE_USE_HIDDEN_PREVIEW)
- node->flag &= ~NODE_PREVIEW;
-
- snode_update(snode, node);
- }
-
- if (snode->nodetree->type == NTREE_TEXTURE) {
- ntreeTexCheckCyclics(snode->edittree);
- }
-
- return node;
-}
-
/* ****************** Duplicate *********************** */
static void node_duplicate_reparent_recursive(bNode *node)
@@ -2742,769 +1593,6 @@ void NODE_OT_duplicate(wmOperatorType *ot)
RNA_def_boolean(ot->srna, "keep_inputs", 0, "Keep Inputs", "Keep the input links to duplicated nodes");
}
-/* *************************** add link op ******************** */
-
-static void node_remove_extra_links(SpaceNode *snode, bNodeSocket *tsock, bNodeLink *link)
-{
- bNodeLink *tlink;
- bNodeSocket *sock;
-
- if (tsock && nodeCountSocketLinks(snode->edittree, link->tosock) > tsock->limit) {
-
- for (tlink = snode->edittree->links.first; tlink; tlink = tlink->next) {
- if (link != tlink && tlink->tosock == link->tosock)
- break;
- }
- if (tlink) {
- /* try to move the existing link to the next available socket */
- if (tlink->tonode) {
- /* is there a free input socket with the target type? */
- for (sock = tlink->tonode->inputs.first; sock; sock = sock->next) {
- if (sock->type == tlink->tosock->type)
- if (nodeCountSocketLinks(snode->edittree, sock) < sock->limit)
- break;
- }
- if (sock) {
- tlink->tosock = sock;
- sock->flag &= ~SOCK_HIDDEN;
- }
- else {
- nodeRemLink(snode->edittree, tlink);
- }
- }
- else
- nodeRemLink(snode->edittree, tlink);
-
- snode->edittree->update |= NTREE_UPDATE_LINKS;
- }
- }
-}
-
-/* loop that adds a nodelink, called by function below */
-/* in_out = starting socket */
-static int node_link_modal(bContext *C, wmOperator *op, wmEvent *event)
-{
- SpaceNode *snode = CTX_wm_space_node(C);
- ARegion *ar = CTX_wm_region(C);
- bNodeLinkDrag *nldrag = op->customdata;
- bNodeTree *ntree = snode->edittree;
- bNode *tnode;
- bNodeSocket *tsock = NULL;
- bNodeLink *link;
- LinkData *linkdata;
- int in_out;
-
- in_out = nldrag->in_out;
-
- UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1],
- &snode->mx, &snode->my);
-
- switch (event->type) {
- case MOUSEMOVE:
-
- if (in_out == SOCK_OUT) {
- if (node_find_indicated_socket(snode, &tnode, &tsock, SOCK_IN)) {
- for (linkdata = nldrag->links.first; linkdata; linkdata = linkdata->next) {
- link = linkdata->data;
-
- /* skip if this is already the target socket */
- if (link->tosock == tsock)
- continue;
- /* skip if socket is on the same node as the fromsock */
- if (tnode && link->fromnode == tnode)
- continue;
-
- /* attach links to the socket */
- link->tonode = tnode;
- link->tosock = tsock;
- /* add it to the node tree temporarily */
- if (BLI_findindex(&ntree->links, link) < 0)
- BLI_addtail(&ntree->links, link);
-
- ntree->update |= NTREE_UPDATE_LINKS;
- }
- ntreeUpdateTree(ntree);
- }
- else {
- int do_update = FALSE;
- for (linkdata = nldrag->links.first; linkdata; linkdata = linkdata->next) {
- link = linkdata->data;
-
- if (link->tonode || link->tosock) {
- BLI_remlink(&ntree->links, link);
- link->prev = link->next = NULL;
- link->tonode = NULL;
- link->tosock = NULL;
-
- ntree->update |= NTREE_UPDATE_LINKS;
- do_update = TRUE;
- }
- }
- if (do_update) {
- ntreeUpdateTree(ntree);
- }
- }
- }
- else {
- if (node_find_indicated_socket(snode, &tnode, &tsock, SOCK_OUT)) {
- for (linkdata = nldrag->links.first; linkdata; linkdata = linkdata->next) {
- link = linkdata->data;
-
- /* skip if this is already the target socket */
- if (link->fromsock == tsock)
- continue;
- /* skip if socket is on the same node as the fromsock */
- if (tnode && link->tonode == tnode)
- continue;
-
- /* attach links to the socket */
- link->fromnode = tnode;
- link->fromsock = tsock;
- /* add it to the node tree temporarily */
- if (BLI_findindex(&ntree->links, link) < 0)
- BLI_addtail(&ntree->links, link);
-
- ntree->update |= NTREE_UPDATE_LINKS;
- }
- ntreeUpdateTree(ntree);
- }
- else {
- int do_update = FALSE;
- for (linkdata = nldrag->links.first; linkdata; linkdata = linkdata->next) {
- link = linkdata->data;
-
- if (link->fromnode || link->fromsock) {
- BLI_remlink(&ntree->links, link);
- link->prev = link->next = NULL;
- link->fromnode = NULL;
- link->fromsock = NULL;
-
- ntree->update |= NTREE_UPDATE_LINKS;
- do_update = TRUE;
- }
- }
- if (do_update) {
- ntreeUpdateTree(ntree);
- }
- }
- }
-
- ED_region_tag_redraw(ar);
- break;
-
- case LEFTMOUSE:
- case RIGHTMOUSE:
- case MIDDLEMOUSE: {
- for (linkdata = nldrag->links.first; linkdata; linkdata = linkdata->next) {
- link = linkdata->data;
-
- if (link->tosock && link->fromsock) {
- /* send changed events for original tonode and new */
- if (link->tonode)
- snode_update(snode, link->tonode);
-
- /* we might need to remove a link */
- if (in_out == SOCK_OUT)
- node_remove_extra_links(snode, link->tosock, link);
-
- /* when linking to group outputs, update the socket type */
- /* XXX this should all be part of a generic update system */
- if (!link->tonode) {
- if (link->tosock->type != link->fromsock->type)
- nodeSocketSetType(link->tosock, link->fromsock->type);
- }
- }
- else if (outside_group_rect(snode) && (link->tonode || link->fromnode)) {
- /* automatically add new group socket */
- if (link->tonode && link->tosock) {
- link->fromsock = node_group_expose_socket(ntree, link->tosock, SOCK_IN);
- link->fromnode = NULL;
- if (BLI_findindex(&ntree->links, link) < 0)
- BLI_addtail(&ntree->links, link);
-
- ntree->update |= NTREE_UPDATE_GROUP_IN | NTREE_UPDATE_LINKS;
- }
- else if (link->fromnode && link->fromsock) {
- link->tosock = node_group_expose_socket(ntree, link->fromsock, SOCK_OUT);
- link->tonode = NULL;
- if (BLI_findindex(&ntree->links, link) < 0)
- BLI_addtail(&ntree->links, link);
-
- ntree->update |= NTREE_UPDATE_GROUP_OUT | NTREE_UPDATE_LINKS;
- }
- }
- else
- nodeRemLink(ntree, link);
- }
-
- ntreeUpdateTree(ntree);
- snode_notify(C, snode);
- snode_dag_update(C, snode);
-
- BLI_remlink(&snode->linkdrag, nldrag);
- /* links->data pointers are either held by the tree or freed already */
- BLI_freelistN(&nldrag->links);
- MEM_freeN(nldrag);
-
- return OPERATOR_FINISHED;
- }
- }
-
- return OPERATOR_RUNNING_MODAL;
-}
-
-/* return 1 when socket clicked */
-static bNodeLinkDrag *node_link_init(SpaceNode *snode, int detach)
-{
- bNode *node;
- bNodeSocket *sock;
- bNodeLink *link, *link_next, *oplink;
- bNodeLinkDrag *nldrag = NULL;
- LinkData *linkdata;
- int num_links;
-
- /* output indicated? */
- if (node_find_indicated_socket(snode, &node, &sock, SOCK_OUT)) {
- nldrag = MEM_callocN(sizeof(bNodeLinkDrag), "drag link op customdata");
-
- num_links = nodeCountSocketLinks(snode->edittree, sock);
- if (num_links > 0 && (num_links >= sock->limit || detach)) {
- /* dragged links are fixed on input side */
- nldrag->in_out = SOCK_IN;
- /* detach current links and store them in the operator data */
- for (link = snode->edittree->links.first; link; link = link_next) {
- link_next = link->next;
- if (link->fromsock == sock) {
- linkdata = MEM_callocN(sizeof(LinkData), "drag link op link data");
- linkdata->data = oplink = MEM_callocN(sizeof(bNodeLink), "drag link op link");
- *oplink = *link;
- oplink->next = oplink->prev = NULL;
- BLI_addtail(&nldrag->links, linkdata);
- nodeRemLink(snode->edittree, link);
- }
- }
- }
- else {
- /* dragged links are fixed on output side */
- nldrag->in_out = SOCK_OUT;
- /* create a new link */
- linkdata = MEM_callocN(sizeof(LinkData), "drag link op link data");
- linkdata->data = oplink = MEM_callocN(sizeof(bNodeLink), "drag link op link");
- oplink->fromnode = node;
- oplink->fromsock = sock;
- BLI_addtail(&nldrag->links, linkdata);
- }
- }
- /* or an input? */
- else if (node_find_indicated_socket(snode, &node, &sock, SOCK_IN)) {
- nldrag = MEM_callocN(sizeof(bNodeLinkDrag), "drag link op customdata");
-
- num_links = nodeCountSocketLinks(snode->edittree, sock);
- if (num_links > 0 && (num_links >= sock->limit || detach)) {
- /* dragged links are fixed on output side */
- nldrag->in_out = SOCK_OUT;
- /* detach current links and store them in the operator data */
- for (link = snode->edittree->links.first; link; link = link_next) {
- link_next = link->next;
- if (link->tosock == sock) {
- linkdata = MEM_callocN(sizeof(LinkData), "drag link op link data");
- linkdata->data = oplink = MEM_callocN(sizeof(bNodeLink), "drag link op link");
- *oplink = *link;
- oplink->next = oplink->prev = NULL;
- BLI_addtail(&nldrag->links, linkdata);
- nodeRemLink(snode->edittree, link);
-
- /* send changed event to original link->tonode */
- if (node)
- snode_update(snode, node);
- }
- }
- }
- else {
- /* dragged links are fixed on input side */
- nldrag->in_out = SOCK_IN;
- /* create a new link */
- linkdata = MEM_callocN(sizeof(LinkData), "drag link op link data");
- linkdata->data = oplink = MEM_callocN(sizeof(bNodeLink), "drag link op link");
- oplink->tonode = node;
- oplink->tosock = sock;
- BLI_addtail(&nldrag->links, linkdata);
- }
- }
-
- return nldrag;
-}
-
-static int node_link_invoke(bContext *C, wmOperator *op, wmEvent *event)
-{
- SpaceNode *snode = CTX_wm_space_node(C);
- ARegion *ar = CTX_wm_region(C);
- bNodeLinkDrag *nldrag;
- int detach = RNA_boolean_get(op->ptr, "detach");
-
- UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1],
- &snode->mx, &snode->my);
-
- ED_preview_kill_jobs(C);
-
- nldrag = node_link_init(snode, detach);
-
- if (nldrag) {
- op->customdata = nldrag;
- BLI_addtail(&snode->linkdrag, nldrag);
-
- /* add modal handler */
- WM_event_add_modal_handler(C, op);
-
- return OPERATOR_RUNNING_MODAL;
- }
- else
- return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH;
-}
-
-static int node_link_cancel(bContext *C, wmOperator *op)
-{
- SpaceNode *snode = CTX_wm_space_node(C);
- bNodeLinkDrag *nldrag = op->customdata;
-
- BLI_remlink(&snode->linkdrag, nldrag);
-
- BLI_freelistN(&nldrag->links);
- MEM_freeN(nldrag);
-
- return OPERATOR_CANCELLED;
-}
-
-void NODE_OT_link(wmOperatorType *ot)
-{
- /* identifiers */
- ot->name = "Link Nodes";
- ot->idname = "NODE_OT_link";
- ot->description = "Use the mouse to create a link between two nodes";
-
- /* api callbacks */
- ot->invoke = node_link_invoke;
- ot->modal = node_link_modal;
-// ot->exec = node_link_exec;
- ot->poll = ED_operator_node_active;
- ot->cancel = node_link_cancel;
-
- /* flags */
- ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING;
-
- RNA_def_boolean(ot->srna, "detach", FALSE, "Detach", "Detach and redirect existing links");
-}
-
-/* ********************** Make Link operator ***************** */
-
-/* makes a link between selected output and input sockets */
-static int node_make_link_exec(bContext *C, wmOperator *op)
-{
- SpaceNode *snode = CTX_wm_space_node(C);
- int replace = RNA_boolean_get(op->ptr, "replace");
-
- ED_preview_kill_jobs(C);
-
- snode_autoconnect(snode, 1, replace);
-
- /* deselect sockets after linking */
- node_deselect_all_input_sockets(snode, 0);
- node_deselect_all_output_sockets(snode, 0);
-
- ntreeUpdateTree(snode->edittree);
- snode_notify(C, snode);
- snode_dag_update(C, snode);
-
- return OPERATOR_FINISHED;
-}
-
-void NODE_OT_link_make(wmOperatorType *ot)
-{
- /* identifiers */
- ot->name = "Make Links";
- ot->description = "Makes a link between selected output in input sockets";
- ot->idname = "NODE_OT_link_make";
-
- /* callbacks */
- ot->exec = node_make_link_exec;
- ot->poll = ED_operator_node_active; // XXX we need a special poll which checks that there are selected input/output sockets
-
- /* flags */
- ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
-
- RNA_def_boolean(ot->srna, "replace", 0, "Replace", "Replace socket connections with the new links");
-}
-
-/* ********************** Add reroute operator ***************** */
-#define LINK_RESOL 12
-static int add_reroute_intersect_check(bNodeLink *link, float mcoords[][2], int tot, float result[2])
-{
- float coord_array[LINK_RESOL + 1][2];
- int i, b;
-
- if (node_link_bezier_points(NULL, NULL, link, coord_array, LINK_RESOL)) {
-
- for (i = 0; i < tot - 1; i++)
- for (b = 0; b < LINK_RESOL; b++)
- if (isect_line_line_v2(mcoords[i], mcoords[i + 1], coord_array[b], coord_array[b + 1]) > 0) {
- result[0] = (mcoords[i][0] + mcoords[i + 1][0]) / 2.0f;
- result[1] = (mcoords[i][1] + mcoords[i + 1][1]) / 2.0f;
- return 1;
- }
- }
- return 0;
-}
-
-static int add_reroute_exec(bContext *C, wmOperator *op)
-{
- SpaceNode *snode = CTX_wm_space_node(C);
- ARegion *ar = CTX_wm_region(C);
- bNode *gnode = node_tree_get_editgroup(snode->nodetree);
- float mcoords[256][2];
- int i = 0;
-
- RNA_BEGIN(op->ptr, itemptr, "path")
- {
- float loc[2];
-
- RNA_float_get_array(&itemptr, "loc", loc);
- UI_view2d_region_to_view(&ar->v2d, (short)loc[0], (short)loc[1],
- &mcoords[i][0], &mcoords[i][1]);
- i++;
- if (i >= 256) break;
- }
- RNA_END;
-
- if (i > 1) {
- bNodeLink *link;
- float insertPoint[2];
-
- ED_preview_kill_jobs(C);
-
- for (link = snode->edittree->links.first; link; link = link->next) {
- if (add_reroute_intersect_check(link, mcoords, i, insertPoint)) {
- bNodeTemplate ntemp;
- bNode *rerouteNode;
-
- node_deselect_all(snode);
-
- ntemp.type = NODE_REROUTE;
- rerouteNode = nodeAddNode(snode->edittree, &ntemp);
- if (gnode) {
- nodeFromView(gnode, insertPoint[0], insertPoint[1], &rerouteNode->locx, &rerouteNode->locy);
- }
- else {
- rerouteNode->locx = insertPoint[0];
- rerouteNode->locy = insertPoint[1];
- }
-
- nodeAddLink(snode->edittree, link->fromnode, link->fromsock, rerouteNode, rerouteNode->inputs.first);
- link->fromnode = rerouteNode;
- link->fromsock = rerouteNode->outputs.first;
-
- break; // add one reroute at the time.
- }
- }
-
- ntreeUpdateTree(snode->edittree);
- snode_notify(C, snode);
- snode_dag_update(C, snode);
-
- return OPERATOR_FINISHED;
- }
-
- return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH;
-}
-
-void NODE_OT_add_reroute(wmOperatorType *ot)
-{
- PropertyRNA *prop;
-
- ot->name = "Add reroute";
- ot->idname = "NODE_OT_add_reroute";
-
- ot->invoke = WM_gesture_lines_invoke;
- ot->modal = WM_gesture_lines_modal;
- ot->exec = add_reroute_exec;
- ot->cancel = WM_gesture_lines_cancel;
-
- ot->poll = ED_operator_node_active;
-
- /* flags */
- ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
-
- prop = RNA_def_property(ot->srna, "path", PROP_COLLECTION, PROP_NONE);
- RNA_def_property_struct_runtime(prop, &RNA_OperatorMousePath);
- /* internal */
- RNA_def_int(ot->srna, "cursor", BC_CROSSCURSOR, 0, INT_MAX, "Cursor", "", 0, INT_MAX);
-}
-
-
-/* ********************** Cut Link operator ***************** */
-static int cut_links_intersect(bNodeLink *link, float mcoords[][2], int tot)
-{
- float coord_array[LINK_RESOL + 1][2];
- int i, b;
-
- if (node_link_bezier_points(NULL, NULL, link, coord_array, LINK_RESOL)) {
-
- for (i = 0; i < tot - 1; i++)
- for (b = 0; b < LINK_RESOL; b++)
- if (isect_line_line_v2(mcoords[i], mcoords[i + 1], coord_array[b], coord_array[b + 1]) > 0)
- return 1;
- }
- return 0;
-}
-
-static int cut_links_exec(bContext *C, wmOperator *op)
-{
- SpaceNode *snode = CTX_wm_space_node(C);
- ARegion *ar = CTX_wm_region(C);
- float mcoords[256][2];
- int i = 0;
-
- RNA_BEGIN(op->ptr, itemptr, "path")
- {
- float loc[2];
-
- RNA_float_get_array(&itemptr, "loc", loc);
- UI_view2d_region_to_view(&ar->v2d, (int)loc[0], (int)loc[1],
- &mcoords[i][0], &mcoords[i][1]);
- i++;
- if (i >= 256) break;
- }
- RNA_END;
-
- if (i > 1) {
- bNodeLink *link, *next;
-
- ED_preview_kill_jobs(C);
-
- for (link = snode->edittree->links.first; link; link = next) {
- next = link->next;
-
- if (cut_links_intersect(link, mcoords, i)) {
- snode_update(snode, link->tonode);
- nodeRemLink(snode->edittree, link);
- }
- }
-
- ntreeUpdateTree(snode->edittree);
- snode_notify(C, snode);
- snode_dag_update(C, snode);
-
- return OPERATOR_FINISHED;
- }
-
- return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH;
-}
-
-void NODE_OT_links_cut(wmOperatorType *ot)
-{
- PropertyRNA *prop;
-
- ot->name = "Cut links";
- ot->idname = "NODE_OT_links_cut";
- ot->description = "Use the mouse to cut (remove) some links";
-
- ot->invoke = WM_gesture_lines_invoke;
- ot->modal = WM_gesture_lines_modal;
- ot->exec = cut_links_exec;
- ot->cancel = WM_gesture_lines_cancel;
-
- ot->poll = ED_operator_node_active;
-
- /* flags */
- ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
-
- prop = RNA_def_property(ot->srna, "path", PROP_COLLECTION, PROP_NONE);
- RNA_def_property_struct_runtime(prop, &RNA_OperatorMousePath);
- /* internal */
- RNA_def_int(ot->srna, "cursor", BC_KNIFECURSOR, 0, INT_MAX, "Cursor", "", 0, INT_MAX);
-}
-
-/* ********************** Detach links operator ***************** */
-
-static int detach_links_exec(bContext *C, wmOperator *UNUSED(op))
-{
- SpaceNode *snode = CTX_wm_space_node(C);
- bNodeTree *ntree = snode->edittree;
- bNode *node;
-
- ED_preview_kill_jobs(C);
-
- for (node = ntree->nodes.first; node; node = node->next) {
- if (node->flag & SELECT) {
- nodeInternalRelink(ntree, node);
- }
- }
-
- ntreeUpdateTree(ntree);
-
- snode_notify(C, snode);
- snode_dag_update(C, snode);
-
- return OPERATOR_FINISHED;
-}
-
-void NODE_OT_links_detach(wmOperatorType *ot)
-{
- ot->name = "Detach Links";
- ot->idname = "NODE_OT_links_detach";
- ot->description = "Remove all links to selected nodes, and try to connect neighbor nodes together";
-
- ot->exec = detach_links_exec;
- ot->poll = ED_operator_node_active;
-
- /* flags */
- ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
-}
-
-/* ********************* automatic node insert on dragging ******************* */
-
-/* assumes sockets in list */
-static bNodeSocket *socket_best_match(ListBase *sockets)
-{
- bNodeSocket *sock;
- int type, maxtype = 0;
-
- /* find type range */
- for (sock = sockets->first; sock; sock = sock->next)
- maxtype = MAX2(sock->type, maxtype);
-
- /* try all types, starting from 'highest' (i.e. colors, vectors, values) */
- for (type = maxtype; type >= 0; --type) {
- for (sock = sockets->first; sock; sock = sock->next) {
- if (!nodeSocketIsHidden(sock) && type == sock->type) {
- return sock;
- }
- }
- }
-
- /* no visible sockets, unhide first of highest type */
- for (type = maxtype; type >= 0; --type) {
- for (sock = sockets->first; sock; sock = sock->next) {
- if (type == sock->type) {
- sock->flag &= ~SOCK_HIDDEN;
- return sock;
- }
- }
- }
-
- return NULL;
-}
-
-/* prevent duplicate testing code below */
-static SpaceNode *ed_node_link_conditions(ScrArea *sa, bNode **select)
-{
- SpaceNode *snode = sa ? sa->spacedata.first : NULL;
- bNode *node;
- bNodeLink *link;
-
- /* no unlucky accidents */
- if (sa == NULL || sa->spacetype != SPACE_NODE) return NULL;
-
- *select = NULL;
-
- for (node = snode->edittree->nodes.first; node; node = node->next) {
- if (node->flag & SELECT) {
- if (*select)
- break;
- else
- *select = node;
- }
- }
- /* only one selected */
- if (node || *select == NULL) return NULL;
-
- /* correct node */
- if ((*select)->inputs.first == NULL || (*select)->outputs.first == NULL) return NULL;
-
- /* test node for links */
- for (link = snode->edittree->links.first; link; link = link->next) {
- if (link->tonode == *select || link->fromnode == *select)
- return NULL;
- }
-
- return snode;
-}
-
-/* assumes link with NODE_LINKFLAG_HILITE set */
-void ED_node_link_insert(ScrArea *sa)
-{
- bNode *node, *select;
- SpaceNode *snode = ed_node_link_conditions(sa, &select);
- bNodeLink *link;
- bNodeSocket *sockto;
-
- if (snode == NULL) return;
-
- /* get the link */
- for (link = snode->edittree->links.first; link; link = link->next)
- if (link->flag & NODE_LINKFLAG_HILITE)
- break;
-
- if (link) {
- node = link->tonode;
- sockto = link->tosock;
-
- link->tonode = select;
- link->tosock = socket_best_match(&select->inputs);
- link->flag &= ~NODE_LINKFLAG_HILITE;
-
- nodeAddLink(snode->edittree, select, socket_best_match(&select->outputs), node, sockto);
- ntreeUpdateTree(snode->edittree); /* needed for pointers */
- snode_update(snode, select);
- ED_node_changed_update(snode->id, select);
- }
-}
-
-
-/* test == 0, clear all intersect flags */
-void ED_node_link_intersect_test(ScrArea *sa, int test)
-{
- bNode *select;
- SpaceNode *snode = ed_node_link_conditions(sa, &select);
- bNodeLink *link, *selink = NULL;
- float mcoords[6][2];
-
- if (snode == NULL) return;
-
- /* clear flags */
- for (link = snode->edittree->links.first; link; link = link->next)
- link->flag &= ~NODE_LINKFLAG_HILITE;
-
- if (test == 0) return;
-
- /* okay, there's 1 node, without links, now intersect */
- mcoords[0][0] = select->totr.xmin;
- mcoords[0][1] = select->totr.ymin;
- mcoords[1][0] = select->totr.xmax;
- mcoords[1][1] = select->totr.ymin;
- mcoords[2][0] = select->totr.xmax;
- mcoords[2][1] = select->totr.ymax;
- mcoords[3][0] = select->totr.xmin;
- mcoords[3][1] = select->totr.ymax;
- mcoords[4][0] = select->totr.xmin;
- mcoords[4][1] = select->totr.ymin;
- mcoords[5][0] = select->totr.xmax;
- mcoords[5][1] = select->totr.ymax;
-
- /* we only tag a single link for intersect now */
- /* idea; use header dist when more? */
- for (link = snode->edittree->links.first; link; link = link->next) {
-
- if (cut_links_intersect(link, mcoords, 5)) { /* intersect code wants edges */
- if (selink)
- break;
- selink = link;
- }
- }
-
- if (link == NULL && selink)
- selink->flag |= NODE_LINKFLAG_HILITE;
-}
-
int ED_node_select_check(ListBase *lb)
{
bNode *node;
@@ -3638,7 +1726,6 @@ int node_render_changed_exec(bContext *C, wmOperator *UNUSED(op))
void NODE_OT_render_changed(wmOperatorType *ot)
{
-
ot->name = "Render Changed Layer";
ot->idname = "NODE_OT_render_changed";
ot->description = "Render current scene, when input node's layer has been changed";
@@ -3651,326 +1738,6 @@ void NODE_OT_render_changed(wmOperatorType *ot)
ot->flag = 0;
}
-
-/* ****************** Make Group operator ******************* */
-
-static int node_group_make_test(bNodeTree *ntree, bNode *gnode)
-{
- bNode *node;
- bNodeLink *link;
- int totnode = 0;
-
- /* is there something to group? also do some clearing */
- for (node = ntree->nodes.first; node; node = node->next) {
- if (node == gnode)
- continue;
-
- if (node->flag & NODE_SELECT) {
- /* no groups in groups */
- if (node->type == NODE_GROUP)
- return 0;
- totnode++;
- }
-
- node->done = 0;
- }
- if (totnode == 0) return 0;
-
- /* check if all connections are OK, no unselected node has both
- * inputs and outputs to a selection */
- for (link = ntree->links.first; link; link = link->next) {
- if (link->fromnode && link->tonode && link->fromnode->flag & NODE_SELECT && link->fromnode != gnode)
- link->tonode->done |= 1;
- if (link->fromnode && link->tonode && link->tonode->flag & NODE_SELECT && link->tonode != gnode)
- link->fromnode->done |= 2;
- }
-
- for (node = ntree->nodes.first; node; node = node->next) {
- if (node == gnode)
- continue;
- if ((node->flag & NODE_SELECT) == 0)
- if (node->done == 3)
- break;
- }
- if (node)
- return 0;
-
- return 1;
-}
-
-static void node_get_selected_minmax(bNodeTree *ntree, bNode *gnode, float *min, float *max)
-{
- bNode *node;
- INIT_MINMAX2(min, max);
- for (node = ntree->nodes.first; node; node = node->next) {
- if (node == gnode)
- continue;
- if (node->flag & NODE_SELECT) {
- DO_MINMAX2((&node->locx), min, max);
- }
- }
-}
-
-static int node_group_make_insert_selected(bNodeTree *ntree, bNode *gnode)
-{
- bNodeTree *ngroup = (bNodeTree *)gnode->id;
- bNodeLink *link, *linkn;
- bNode *node, *nextn;
- bNodeSocket *gsock;
- ListBase anim_basepaths = {NULL, NULL};
- float min[2], max[2];
-
- /* deselect all nodes in the target tree */
- for (node = ngroup->nodes.first; node; node = node->next)
- node_deselect(node);
-
- node_get_selected_minmax(ntree, gnode, min, max);
-
- /* move nodes over */
- for (node = ntree->nodes.first; node; node = nextn) {
- nextn = node->next;
- if (node == gnode)
- continue;
- if (node->flag & NODE_SELECT) {
- /* 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);
-
- node->locx -= 0.5f * (min[0] + max[0]);
- node->locy -= 0.5f * (min[1] + max[1]);
- }
- }
-
- /* 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);
-
- /* relink external sockets */
- for (link = ntree->links.first; link; link = linkn) {
- int fromselect = (link->fromnode && (link->fromnode->flag & NODE_SELECT) && link->fromnode != gnode);
- int toselect = (link->tonode && (link->tonode->flag & NODE_SELECT) && link->tonode != gnode);
- linkn = link->next;
-
- if (gnode && ((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) {
- gsock = node_group_expose_socket(ngroup, link->tosock, SOCK_IN);
- link->tosock->link = nodeAddLink(ngroup, NULL, gsock, link->tonode, link->tosock);
- link->tosock = node_group_add_extern_socket(ntree, &gnode->inputs, SOCK_IN, gsock);
- link->tonode = gnode;
- }
- else if (fromselect) {
- /* search for existing group node socket */
- for (gsock = ngroup->outputs.first; gsock; gsock = gsock->next)
- if (gsock->link && gsock->link->fromsock == link->fromsock)
- break;
- if (!gsock) {
- gsock = node_group_expose_socket(ngroup, link->fromsock, SOCK_OUT);
- gsock->link = nodeAddLink(ngroup, link->fromnode, link->fromsock, NULL, gsock);
- link->fromsock = node_group_add_extern_socket(ntree, &gnode->outputs, SOCK_OUT, gsock);
- }
- else
- link->fromsock = node_group_find_output(gnode, gsock);
- link->fromnode = gnode;
- }
- }
-
- /* update of the group tree */
- ngroup->update |= NTREE_UPDATE;
- /* update of the tree containing the group instance node */
- ntree->update |= NTREE_UPDATE_NODES | NTREE_UPDATE_LINKS;
-
- return 1;
-}
-
-static bNode *node_group_make_from_selected(bNodeTree *ntree)
-{
- bNode *gnode;
- bNodeTree *ngroup;
- float min[2], max[2];
- bNodeTemplate ntemp;
-
- node_get_selected_minmax(ntree, NULL, min, max);
-
- /* new nodetree */
- ngroup = ntreeAddTree("NodeGroup", ntree->type, NODE_GROUP);
-
- /* make group node */
- ntemp.type = NODE_GROUP;
- ntemp.ngroup = ngroup;
- gnode = nodeAddNode(ntree, &ntemp);
- gnode->locx = 0.5f * (min[0] + max[0]);
- gnode->locy = 0.5f * (min[1] + max[1]);
-
- node_group_make_insert_selected(ntree, gnode);
-
- /* update of the tree containing the group instance node */
- ntree->update |= NTREE_UPDATE_NODES;
-
- return gnode;
-}
-
-typedef enum eNodeGroupMakeType {
- NODE_GM_NEW,
- NODE_GM_INSERT
-} eNodeGroupMakeType;
-
-/* Operator Property */
-EnumPropertyItem node_group_make_types[] = {
- {NODE_GM_NEW, "NEW", 0, "New", "Create a new node group from selected nodes"},
- {NODE_GM_INSERT, "INSERT", 0, "Insert", "Insert into active node group"},
- {0, NULL, 0, NULL, NULL}
-};
-
-static int node_group_make_exec(bContext *C, wmOperator *op)
-{
- SpaceNode *snode = CTX_wm_space_node(C);
- bNode *gnode;
- int type = RNA_enum_get(op->ptr, "type");
-
- if (snode->edittree != snode->nodetree) {
- BKE_report(op->reports, RPT_WARNING, "Can not add a new Group in a Group");
- return OPERATOR_CANCELLED;
- }
-
- /* for time being... is too complex to handle */
- if (snode->treetype == NTREE_COMPOSIT) {
- for (gnode = snode->nodetree->nodes.first; gnode; gnode = gnode->next) {
- if (gnode->flag & SELECT)
- if (gnode->type == CMP_NODE_R_LAYERS)
- break;
- }
-
- if (gnode) {
- BKE_report(op->reports, RPT_WARNING, "Can not add RenderLayer in a Group");
- return OPERATOR_CANCELLED;
- }
- }
-
- ED_preview_kill_jobs(C);
-
- switch (type) {
- case NODE_GM_NEW:
- if (node_group_make_test(snode->nodetree, NULL)) {
- gnode = node_group_make_from_selected(snode->nodetree);
- }
- else {
- BKE_report(op->reports, RPT_WARNING, "Can not make Group");
- return OPERATOR_CANCELLED;
- }
- break;
- case NODE_GM_INSERT:
- gnode = nodeGetActive(snode->nodetree);
- if (!gnode || gnode->type != NODE_GROUP) {
- BKE_report(op->reports, RPT_WARNING, "No active Group node");
- return OPERATOR_CANCELLED;
- }
- if (node_group_make_test(snode->nodetree, gnode)) {
- node_group_make_insert_selected(snode->nodetree, gnode);
- }
- else {
- BKE_report(op->reports, RPT_WARNING, "Can not insert into Group");
- return OPERATOR_CANCELLED;
- }
- break;
- }
-
- if (gnode) {
- nodeSetActive(snode->nodetree, gnode);
- snode_make_group_editable(snode, gnode);
- }
-
- if (gnode)
- ntreeUpdateTree((bNodeTree *)gnode->id);
- ntreeUpdateTree(snode->nodetree);
-
- snode_notify(C, snode);
- snode_dag_update(C, snode);
-
- return OPERATOR_FINISHED;
-}
-
-static int node_group_make_invoke(bContext *C, wmOperator *UNUSED(op), wmEvent *UNUSED(event))
-{
- SpaceNode *snode = CTX_wm_space_node(C);
- bNode *act = nodeGetActive(snode->edittree);
- uiPopupMenu *pup = uiPupMenuBegin(C, "Make Group", ICON_NONE);
- uiLayout *layout = uiPupMenuLayout(pup);
-
- uiLayoutSetOperatorContext(layout, WM_OP_EXEC_DEFAULT);
- uiItemEnumO(layout, "NODE_OT_group_make", NULL, 0, "type", NODE_GM_NEW);
-
- /* if active node is a group, add insert option */
- if (act && act->type == NODE_GROUP) {
- uiItemEnumO(layout, "NODE_OT_group_make", NULL, 0, "type", NODE_GM_INSERT);
- }
-
- uiPupMenuEnd(C, pup);
-
- return OPERATOR_CANCELLED;
-}
-
-void NODE_OT_group_make(wmOperatorType *ot)
-{
- /* identifiers */
- ot->name = "Group";
- ot->description = "Make group from selected nodes";
- ot->idname = "NODE_OT_group_make";
-
- /* api callbacks */
- ot->invoke = node_group_make_invoke;
- ot->exec = node_group_make_exec;
- ot->poll = ED_operator_node_active;
-
- /* flags */
- ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
-
- RNA_def_enum(ot->srna, "type", node_group_make_types, NODE_GM_NEW, "Type", "");
-}
-
/* ****************** Hide operator *********************** */
static void node_flag_toggle_exec(SpaceNode *snode, int toggle_flag)
@@ -4278,203 +2045,6 @@ void NODE_OT_delete_reconnect(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
-/* ****************** Show Cyclic Dependencies Operator ******************* */
-
-static int node_show_cycles_exec(bContext *C, wmOperator *UNUSED(op))
-{
- SpaceNode *snode = CTX_wm_space_node(C);
-
- /* this is just a wrapper around this call... */
- ntreeUpdateTree(snode->nodetree);
- snode_notify(C, snode);
-
- return OPERATOR_FINISHED;
-}
-
-void NODE_OT_show_cyclic_dependencies(wmOperatorType *ot)
-{
- /* identifiers */
- ot->name = "Show Cyclic Dependencies";
- ot->description = "Sort the nodes and show the cyclic dependencies between the nodes";
- ot->idname = "NODE_OT_show_cyclic_dependencies";
-
- /* callbacks */
- ot->exec = node_show_cycles_exec;
- ot->poll = ED_operator_node_active;
-
- /* flags */
- ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
-}
-
-/* ****************** Add File Node Operator ******************* */
-
-static int node_add_file_exec(bContext *C, wmOperator *op)
-{
- Main *bmain = CTX_data_main(C);
- Scene *scene = CTX_data_scene(C);
- SpaceNode *snode = CTX_wm_space_node(C);
- bNode *node;
- Image *ima = NULL;
- bNodeTemplate ntemp;
-
- /* check input variables */
- if (RNA_struct_property_is_set(op->ptr, "filepath")) {
- char path[FILE_MAX];
- RNA_string_get(op->ptr, "filepath", path);
-
- errno = 0;
-
- ima = BKE_image_load_exists(path);
-
- if (!ima) {
- BKE_reportf(op->reports, RPT_ERROR, "Can't read image: \"%s\", %s", path, errno ? strerror(errno) : "Unsupported format");
- return OPERATOR_CANCELLED;
- }
- }
- else if (RNA_struct_property_is_set(op->ptr, "name")) {
- char name[MAX_ID_NAME - 2];
- RNA_string_get(op->ptr, "name", name);
- ima = (Image *)BKE_libblock_find_name(ID_IM, name);
-
- if (!ima) {
- BKE_reportf(op->reports, RPT_ERROR, "Image named \"%s\", not found", name);
- return OPERATOR_CANCELLED;
- }
- }
-
- node_deselect_all(snode);
-
- switch (snode->nodetree->type) {
- case NTREE_SHADER:
- ntemp.type = SH_NODE_TEX_IMAGE;
- break;
- case NTREE_TEXTURE:
- ntemp.type = TEX_NODE_IMAGE;
- break;
- case NTREE_COMPOSIT:
- ntemp.type = CMP_NODE_IMAGE;
- break;
- default:
- return OPERATOR_CANCELLED;
- }
-
- ED_preview_kill_jobs(C);
-
- node = node_add_node(snode, bmain, scene, &ntemp, snode->mx, snode->my);
-
- if (!node) {
- BKE_report(op->reports, RPT_WARNING, "Could not add an image node");
- return OPERATOR_CANCELLED;
- }
-
- node->id = (ID *)ima;
- id_us_plus(node->id);
-
- snode_notify(C, snode);
- snode_dag_update(C, snode);
-
- return OPERATOR_FINISHED;
-}
-
-static int node_add_file_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);
-
- if (RNA_struct_property_is_set(op->ptr, "filepath") || RNA_struct_property_is_set(op->ptr, "name"))
- return node_add_file_exec(C, op);
- else
- return WM_operator_filesel(C, op, event);
-}
-
-void NODE_OT_add_file(wmOperatorType *ot)
-{
- /* identifiers */
- ot->name = "Add File Node";
- ot->description = "Add a file node to the current node editor";
- ot->idname = "NODE_OT_add_file";
-
- /* callbacks */
- ot->exec = node_add_file_exec;
- ot->invoke = node_add_file_invoke;
- ot->poll = ED_operator_node_active;
-
- /* flags */
- ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
-
- WM_operator_properties_filesel(ot, FOLDERFILE | IMAGEFILE, FILE_SPECIAL, FILE_OPENFILE, WM_FILESEL_FILEPATH, FILE_DEFAULTDISPLAY); //XXX TODO, relative_path
- RNA_def_string(ot->srna, "name", "Image", MAX_ID_NAME - 2, "Name", "Datablock name to assign");
-}
-
-/********************** New node tree operator *********************/
-
-static int new_node_tree_exec(bContext *C, wmOperator *op)
-{
- SpaceNode *snode;
- bNodeTree *ntree;
- PointerRNA ptr, idptr;
- PropertyRNA *prop;
- int treetype;
- char treename[MAX_ID_NAME - 2] = "NodeTree";
-
- /* retrieve state */
- snode = CTX_wm_space_node(C);
-
- if (RNA_struct_property_is_set(op->ptr, "type"))
- treetype = RNA_enum_get(op->ptr, "type");
- else
- treetype = snode->treetype;
-
- if (RNA_struct_property_is_set(op->ptr, "name"))
- RNA_string_get(op->ptr, "name", treename);
-
- ntree = ntreeAddTree(treename, treetype, 0);
- if (!ntree)
- return OPERATOR_CANCELLED;
-
- /* hook into UI */
- uiIDContextProperty(C, &ptr, &prop);
-
- if (prop) {
- RNA_id_pointer_create(&ntree->id, &idptr);
- RNA_property_pointer_set(&ptr, prop, idptr);
- /* RNA_property_pointer_set increases the user count,
- * fixed here as the editor is the initial user.
- */
- --ntree->id.us;
- RNA_property_update(C, &ptr, prop);
- }
- else if (snode) {
- Scene *scene = CTX_data_scene(C);
- snode->nodetree = ntree;
-
- ED_node_tree_update(snode, scene);
- }
-
- return OPERATOR_FINISHED;
-}
-
-void NODE_OT_new_node_tree(wmOperatorType *ot)
-{
- /* identifiers */
- ot->name = "New Node Tree";
- ot->idname = "NODE_OT_new_node_tree";
- ot->description = "Create a new node tree";
-
- /* api callbacks */
- ot->exec = new_node_tree_exec;
- ot->poll = ED_operator_node_active;
-
- /* flags */
- ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
-
- RNA_def_enum(ot->srna, "type", nodetree_type_items, NTREE_COMPOSIT, "Tree Type", "");
- RNA_def_string(ot->srna, "name", "NodeTree", MAX_ID_NAME - 2, "Name", "");
-}
/* ****************** File Output Add Socket ******************* */
@@ -4486,18 +2056,18 @@ static int node_output_file_add_socket_exec(bContext *C, wmOperator *op)
bNodeTree *ntree;
bNode *node;
char file_path[MAX_NAME];
-
+
ptr = CTX_data_pointer_get(C, "node");
if (!ptr.data)
return OPERATOR_CANCELLED;
node = ptr.data;
ntree = ptr.id.data;
-
+
RNA_string_get(op->ptr, "file_path", file_path);
ntreeCompositOutputFileAddSocket(ntree, node, file_path, &scene->r.im_format);
-
+
snode_notify(C, snode);
-
+
return OPERATOR_FINISHED;
}
@@ -4507,14 +2077,14 @@ void NODE_OT_output_file_add_socket(wmOperatorType *ot)
ot->name = "Add File Node Socket";
ot->description = "Add a new input to a file output node";
ot->idname = "NODE_OT_output_file_add_socket";
-
+
/* callbacks */
ot->exec = node_output_file_add_socket_exec;
ot->poll = composite_node_active;
-
+
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
-
+
RNA_def_string(ot->srna, "file_path", "Image", MAX_NAME, "File Path", "Sub-path of the output file");
}
@@ -4667,308 +2237,3 @@ void NODE_OT_node_copy_color(wmOperatorType *ot)
/* 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 = "Attach 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 = "Attach 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 = "Detach 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_group.c b/source/blender/editors/space_node/node_group.c
new file mode 100644
index 00000000000..41924d926d2
--- /dev/null
+++ b/source/blender/editors/space_node/node_group.c
@@ -0,0 +1,1164 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2005 Blender Foundation.
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): David Millan Escriva, Juho Vepsäläinen, Nathan Letwory
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/editors/space_node/node_group.c
+ * \ingroup spnode
+ */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <string.h>
+#include <errno.h>
+
+#include "MEM_guardedalloc.h"
+
+#include "DNA_ID.h"
+#include "DNA_lamp_types.h"
+#include "DNA_material_types.h"
+#include "DNA_node_types.h"
+#include "DNA_object_types.h"
+#include "DNA_particle_types.h"
+#include "DNA_scene_types.h"
+#include "DNA_world_types.h"
+#include "DNA_action_types.h"
+#include "DNA_anim_types.h"
+
+#include "BLI_math.h"
+#include "BLI_blenlib.h"
+#include "BLI_utildefines.h"
+
+#include "BKE_action.h"
+#include "BKE_animsys.h"
+#include "BKE_context.h"
+#include "BKE_depsgraph.h"
+#include "BKE_global.h"
+#include "BKE_image.h"
+#include "BKE_library.h"
+#include "BKE_main.h"
+#include "BKE_node.h"
+#include "BKE_material.h"
+#include "BKE_modifier.h"
+#include "BKE_paint.h"
+#include "BKE_scene.h"
+#include "BKE_screen.h"
+#include "BKE_texture.h"
+#include "BKE_report.h"
+
+#include "RE_pipeline.h"
+
+#include "IMB_imbuf_types.h"
+
+#include "ED_node.h"
+#include "ED_image.h"
+#include "ED_screen.h"
+#include "ED_space_api.h"
+#include "ED_render.h"
+
+#include "RNA_access.h"
+#include "RNA_define.h"
+#include "RNA_enum_types.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+#include "UI_view2d.h"
+
+#include "IMB_imbuf.h"
+
+#include "RNA_enum_types.h"
+
+#include "GPU_material.h"
+
+#include "node_intern.h"
+#include "NOD_socket.h"
+
+static EnumPropertyItem socket_in_out_items[] = {
+ { SOCK_IN, "SOCK_IN", 0, "Input", "" },
+ { SOCK_OUT, "SOCK_OUT", 0, "Output", "" },
+ { 0, NULL, 0, NULL, NULL },
+};
+
+/* ***************** Edit Group operator ************* */
+
+void snode_make_group_editable(SpaceNode *snode, bNode *gnode)
+{
+ bNode *node;
+
+ /* make sure nothing has group editing on */
+ for (node = snode->nodetree->nodes.first; node; node = node->next) {
+ nodeGroupEditClear(node);
+
+ /* while we're here, clear texture active */
+ if (node->typeinfo->nclass == NODE_CLASS_TEXTURE) {
+ /* this is not 100% sure to be reliable, see comment on the flag */
+ node->flag &= ~NODE_ACTIVE_TEXTURE;
+ }
+ }
+
+ if (gnode == NULL) {
+ /* with NULL argument we do a toggle */
+ if (snode->edittree == snode->nodetree)
+ gnode = nodeGetActive(snode->nodetree);
+ }
+
+ if (gnode) {
+ snode->edittree = nodeGroupEditSet(gnode, 1);
+
+ /* deselect all other nodes, so we can also do grabbing of entire subtree */
+ for (node = snode->nodetree->nodes.first; node; node = node->next) {
+ node_deselect(node);
+
+ if (node->typeinfo->nclass == NODE_CLASS_TEXTURE) {
+ /* this is not 100% sure to be reliable, see comment on the flag */
+ node->flag &= ~NODE_ACTIVE_TEXTURE;
+ }
+ }
+ node_select(gnode);
+ }
+ else
+ snode->edittree = snode->nodetree;
+}
+
+static int node_group_edit_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ SpaceNode *snode = CTX_wm_space_node(C);
+
+ ED_preview_kill_jobs(C);
+
+ if (snode->nodetree == snode->edittree) {
+ bNode *gnode = nodeGetActive(snode->edittree);
+ snode_make_group_editable(snode, gnode);
+ }
+ else
+ snode_make_group_editable(snode, NULL);
+
+ WM_event_add_notifier(C, NC_SCENE | ND_NODES, NULL);
+
+ return OPERATOR_FINISHED;
+}
+
+static int node_group_edit_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(event))
+{
+ SpaceNode *snode = CTX_wm_space_node(C);
+ bNode *gnode;
+
+ /* XXX callback? */
+ if (snode->nodetree == snode->edittree) {
+ gnode = nodeGetActive(snode->edittree);
+ if (gnode && gnode->id && GS(gnode->id->name) == ID_NT && gnode->id->lib) {
+ uiPupMenuOkee(C, op->type->idname, "Make group local?");
+ return OPERATOR_CANCELLED;
+ }
+ }
+
+ return node_group_edit_exec(C, op);
+}
+
+void NODE_OT_group_edit(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Edit Group";
+ ot->description = "Edit node group";
+ ot->idname = "NODE_OT_group_edit";
+
+ /* api callbacks */
+ ot->invoke = node_group_edit_invoke;
+ ot->exec = node_group_edit_exec;
+ ot->poll = ED_operator_node_active;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+}
+
+/* ***************** Add Group Socket operator ************* */
+
+static int node_group_socket_add_exec(bContext *C, wmOperator *op)
+{
+ SpaceNode *snode = CTX_wm_space_node(C);
+ int in_out = -1;
+ char name[MAX_NAME] = "";
+ int type = SOCK_FLOAT;
+ bNodeTree *ngroup = snode->edittree;
+ /* bNodeSocket *sock; */ /* UNUSED */
+
+ ED_preview_kill_jobs(C);
+
+ if (RNA_struct_property_is_set(op->ptr, "name"))
+ RNA_string_get(op->ptr, "name", name);
+
+ if (RNA_struct_property_is_set(op->ptr, "type"))
+ type = RNA_enum_get(op->ptr, "type");
+
+ if (RNA_struct_property_is_set(op->ptr, "in_out"))
+ in_out = RNA_enum_get(op->ptr, "in_out");
+ else
+ return OPERATOR_CANCELLED;
+
+ /* using placeholder subtype first */
+ /* sock = */ /* UNUSED */ node_group_add_socket(ngroup, name, type, in_out);
+
+ ntreeUpdateTree(ngroup);
+
+ snode_notify(C, snode);
+
+ return OPERATOR_FINISHED;
+}
+
+void NODE_OT_group_socket_add(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Add Group Socket";
+ ot->description = "Add node group socket";
+ ot->idname = "NODE_OT_group_socket_add";
+
+ /* api callbacks */
+ ot->exec = node_group_socket_add_exec;
+ ot->poll = ED_operator_node_active;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ RNA_def_enum(ot->srna, "in_out", socket_in_out_items, SOCK_IN, "Socket Type", "Input or Output");
+ RNA_def_string(ot->srna, "name", "", MAX_NAME, "Name", "Group socket name");
+ RNA_def_enum(ot->srna, "type", node_socket_type_items, SOCK_FLOAT, "Type", "Type of the group socket");
+}
+
+/* ***************** Remove Group Socket operator ************* */
+
+static int node_group_socket_remove_exec(bContext *C, wmOperator *op)
+{
+ SpaceNode *snode = CTX_wm_space_node(C);
+ int index = -1;
+ int in_out = -1;
+ bNodeTree *ngroup = snode->edittree;
+ bNodeSocket *sock;
+
+ ED_preview_kill_jobs(C);
+
+ if (RNA_struct_property_is_set(op->ptr, "index"))
+ index = RNA_int_get(op->ptr, "index");
+ else
+ return OPERATOR_CANCELLED;
+
+ if (RNA_struct_property_is_set(op->ptr, "in_out"))
+ in_out = RNA_enum_get(op->ptr, "in_out");
+ else
+ return OPERATOR_CANCELLED;
+
+ sock = (bNodeSocket *)BLI_findlink(in_out == SOCK_IN ? &ngroup->inputs : &ngroup->outputs, index);
+ if (sock) {
+ node_group_remove_socket(ngroup, sock, in_out);
+ ntreeUpdateTree(ngroup);
+
+ snode_notify(C, snode);
+ }
+
+ return OPERATOR_FINISHED;
+}
+
+void NODE_OT_group_socket_remove(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Remove Group Socket";
+ ot->description = "Remove a node group socket";
+ ot->idname = "NODE_OT_group_socket_remove";
+
+ /* api callbacks */
+ ot->exec = node_group_socket_remove_exec;
+ ot->poll = ED_operator_node_active;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ RNA_def_int(ot->srna, "index", 0, 0, INT_MAX, "Index", "", 0, INT_MAX);
+ RNA_def_enum(ot->srna, "in_out", socket_in_out_items, SOCK_IN, "Socket Type", "Input or Output");
+}
+
+/* ***************** Move Group Socket Up operator ************* */
+
+static int node_group_socket_move_up_exec(bContext *C, wmOperator *op)
+{
+ SpaceNode *snode = CTX_wm_space_node(C);
+ int index = -1;
+ int in_out = -1;
+ bNodeTree *ngroup = snode->edittree;
+ bNodeSocket *sock, *prev;
+
+ ED_preview_kill_jobs(C);
+
+ if (RNA_struct_property_is_set(op->ptr, "index"))
+ index = RNA_int_get(op->ptr, "index");
+ else
+ return OPERATOR_CANCELLED;
+
+ if (RNA_struct_property_is_set(op->ptr, "in_out"))
+ in_out = RNA_enum_get(op->ptr, "in_out");
+ else
+ return OPERATOR_CANCELLED;
+
+ /* swap */
+ if (in_out == SOCK_IN) {
+ sock = (bNodeSocket *)BLI_findlink(&ngroup->inputs, index);
+ prev = sock->prev;
+ /* can't move up the first socket */
+ if (!prev)
+ return OPERATOR_CANCELLED;
+ BLI_remlink(&ngroup->inputs, sock);
+ BLI_insertlinkbefore(&ngroup->inputs, prev, sock);
+
+ ngroup->update |= NTREE_UPDATE_GROUP_IN;
+ }
+ else if (in_out == SOCK_OUT) {
+ sock = (bNodeSocket *)BLI_findlink(&ngroup->outputs, index);
+ prev = sock->prev;
+ /* can't move up the first socket */
+ if (!prev)
+ return OPERATOR_CANCELLED;
+ BLI_remlink(&ngroup->outputs, sock);
+ BLI_insertlinkbefore(&ngroup->outputs, prev, sock);
+
+ ngroup->update |= NTREE_UPDATE_GROUP_OUT;
+ }
+ ntreeUpdateTree(ngroup);
+
+ snode_notify(C, snode);
+
+ return OPERATOR_FINISHED;
+}
+
+void NODE_OT_group_socket_move_up(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Move Group Socket Up";
+ ot->description = "Move up node group socket";
+ ot->idname = "NODE_OT_group_socket_move_up";
+
+ /* api callbacks */
+ ot->exec = node_group_socket_move_up_exec;
+ ot->poll = ED_operator_node_active;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ RNA_def_int(ot->srna, "index", 0, 0, INT_MAX, "Index", "", 0, INT_MAX);
+ RNA_def_enum(ot->srna, "in_out", socket_in_out_items, SOCK_IN, "Socket Type", "Input or Output");
+}
+
+/* ***************** Move Group Socket Up operator ************* */
+
+static int node_group_socket_move_down_exec(bContext *C, wmOperator *op)
+{
+ SpaceNode *snode = CTX_wm_space_node(C);
+ int index = -1;
+ int in_out = -1;
+ bNodeTree *ngroup = snode->edittree;
+ bNodeSocket *sock, *next;
+
+ ED_preview_kill_jobs(C);
+
+ if (RNA_struct_property_is_set(op->ptr, "index"))
+ index = RNA_int_get(op->ptr, "index");
+ else
+ return OPERATOR_CANCELLED;
+
+ if (RNA_struct_property_is_set(op->ptr, "in_out"))
+ in_out = RNA_enum_get(op->ptr, "in_out");
+ else
+ return OPERATOR_CANCELLED;
+
+ /* swap */
+ if (in_out == SOCK_IN) {
+ sock = (bNodeSocket *)BLI_findlink(&ngroup->inputs, index);
+ next = sock->next;
+ /* can't move down the last socket */
+ if (!next)
+ return OPERATOR_CANCELLED;
+ BLI_remlink(&ngroup->inputs, sock);
+ BLI_insertlinkafter(&ngroup->inputs, next, sock);
+
+ ngroup->update |= NTREE_UPDATE_GROUP_IN;
+ }
+ else if (in_out == SOCK_OUT) {
+ sock = (bNodeSocket *)BLI_findlink(&ngroup->outputs, index);
+ next = sock->next;
+ /* can't move down the last socket */
+ if (!next)
+ return OPERATOR_CANCELLED;
+ BLI_remlink(&ngroup->outputs, sock);
+ BLI_insertlinkafter(&ngroup->outputs, next, sock);
+
+ ngroup->update |= NTREE_UPDATE_GROUP_OUT;
+ }
+ ntreeUpdateTree(ngroup);
+
+ snode_notify(C, snode);
+
+ return OPERATOR_FINISHED;
+}
+
+void NODE_OT_group_socket_move_down(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Move Group Socket Down";
+ ot->description = "Move down node group socket";
+ ot->idname = "NODE_OT_group_socket_move_down";
+
+ /* api callbacks */
+ ot->exec = node_group_socket_move_down_exec;
+ ot->poll = ED_operator_node_active;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ RNA_def_int(ot->srna, "index", 0, 0, INT_MAX, "Index", "", 0, INT_MAX);
+ RNA_def_enum(ot->srna, "in_out", socket_in_out_items, SOCK_IN, "Socket Type", "Input or Output");
+}
+
+/* ******************** Ungroup operator ********************** */
+
+/* returns 1 if its OK */
+static int node_group_ungroup(bNodeTree *ntree, bNode *gnode)
+{
+ bNodeLink *link, *linkn;
+ bNode *node, *nextn;
+ bNodeTree *ngroup, *wgroup;
+ ListBase anim_basepaths = {NULL, NULL};
+
+ ngroup = (bNodeTree *)gnode->id;
+ if (ngroup == NULL) return 0;
+
+ /* clear new pointers, set in copytree */
+ for (node = ntree->nodes.first; node; node = node->next)
+ node->new_node = NULL;
+
+ /* wgroup is a temporary copy of the NodeTree we're merging in
+ * - all of wgroup's nodes are transferred across to their new home
+ * - ngroup (i.e. the source NodeTree) is left unscathed
+ */
+ wgroup = ntreeCopyTree(ngroup);
+
+ /* add the nodes into the ntree */
+ for (node = wgroup->nodes.first; node; node = nextn) {
+ nextn = node->next;
+
+ /* 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 (wgroup->adt) {
+ PointerRNA ptr;
+ char *path;
+
+ RNA_pointer_create(&wgroup->id, &RNA_Node, node, &ptr);
+ path = RNA_path_from_ID_to_struct(&ptr);
+
+ if (path)
+ BLI_addtail(&anim_basepaths, BLI_genericNodeN(path));
+ }
+
+ /* migrate node */
+ BLI_remlink(&wgroup->nodes, node);
+ BLI_addtail(&ntree->nodes, node);
+
+ /* ensure unique node name in the nodee tree */
+ nodeUniqueName(ntree, node);
+
+ node->locx += gnode->locx;
+ node->locy += gnode->locy;
+
+ node->flag |= NODE_SELECT;
+ }
+
+ /* restore external links to and from the gnode */
+ for (link = ntree->links.first; link; link = link->next) {
+ if (link->fromnode == gnode) {
+ if (link->fromsock->groupsock) {
+ bNodeSocket *gsock = link->fromsock->groupsock;
+ if (gsock->link) {
+ if (gsock->link->fromnode) {
+ /* NB: using the new internal copies here! the groupsock pointer still maps to the old tree */
+ link->fromnode = (gsock->link->fromnode ? gsock->link->fromnode->new_node : NULL);
+ link->fromsock = gsock->link->fromsock->new_sock;
+ }
+ else {
+ /* group output directly maps to group input */
+ bNodeSocket *insock = node_group_find_input(gnode, gsock->link->fromsock);
+ if (insock->link) {
+ link->fromnode = insock->link->fromnode;
+ link->fromsock = insock->link->fromsock;
+ }
+ }
+ }
+ else {
+ /* copy the default input value from the group socket default to the external socket */
+ node_socket_convert_default_value(link->tosock->type, link->tosock->default_value, gsock->type, gsock->default_value);
+ }
+ }
+ }
+ }
+ /* remove internal output links, these are not used anymore */
+ for (link = wgroup->links.first; link; link = linkn) {
+ linkn = link->next;
+ if (!link->tonode)
+ nodeRemLink(wgroup, link);
+ }
+ /* restore links from internal nodes */
+ for (link = wgroup->links.first; link; link = linkn) {
+ linkn = link->next;
+ /* indicates link to group input */
+ if (!link->fromnode) {
+ /* NB: can't use find_group_node_input here,
+ * because gnode sockets still point to the old tree!
+ */
+ bNodeSocket *insock;
+ for (insock = gnode->inputs.first; insock; insock = insock->next)
+ if (insock->groupsock->new_sock == link->fromsock)
+ break;
+ if (insock->link) {
+ link->fromnode = insock->link->fromnode;
+ link->fromsock = insock->link->fromsock;
+ }
+ else {
+ /* copy the default input value from the group node socket default to the internal socket */
+ node_socket_convert_default_value(link->tosock->type, link->tosock->default_value, insock->type, insock->default_value);
+ nodeRemLink(wgroup, link);
+ }
+ }
+ }
+
+ /* add internal links to the ntree */
+ for (link = wgroup->links.first; link; link = linkn) {
+ linkn = link->next;
+ BLI_remlink(&wgroup->links, link);
+ BLI_addtail(&ntree->links, link);
+ }
+
+ /* and copy across the animation,
+ * note that the animation data's action can be NULL here */
+ if (wgroup->adt) {
+ LinkData *ld, *ldn = NULL;
+ bAction *waction;
+
+ /* firstly, wgroup needs to temporary dummy action that can be destroyed, as it shares copies */
+ waction = wgroup->adt->action = BKE_action_copy(wgroup->adt->action);
+
+ /* now perform the moving */
+ BKE_animdata_separate_by_basepath(&wgroup->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);
+ }
+
+ /* free temp action too */
+ if (waction) {
+ BKE_libblock_free(&G.main->action, waction);
+ }
+ }
+
+ /* delete the group instance. this also removes old input links! */
+ nodeFreeNode(ntree, gnode);
+
+ /* free the group tree (takes care of user count) */
+ BKE_libblock_free(&G.main->nodetree, wgroup);
+
+ ntree->update |= NTREE_UPDATE_NODES | NTREE_UPDATE_LINKS;
+
+ return 1;
+}
+
+static int node_group_ungroup_exec(bContext *C, wmOperator *op)
+{
+ SpaceNode *snode = CTX_wm_space_node(C);
+ bNode *gnode;
+
+ ED_preview_kill_jobs(C);
+
+ /* are we inside of a group? */
+ gnode = node_tree_get_editgroup(snode->nodetree);
+ if (gnode)
+ snode_make_group_editable(snode, NULL);
+
+ gnode = nodeGetActive(snode->edittree);
+ if (gnode == NULL)
+ return OPERATOR_CANCELLED;
+
+ if (gnode->type != NODE_GROUP) {
+ BKE_report(op->reports, RPT_WARNING, "Not a group");
+ return OPERATOR_CANCELLED;
+ }
+ else if (node_group_ungroup(snode->nodetree, gnode)) {
+ ntreeUpdateTree(snode->nodetree);
+ }
+ else {
+ BKE_report(op->reports, RPT_WARNING, "Can't ungroup");
+ return OPERATOR_CANCELLED;
+ }
+
+ snode_notify(C, snode);
+ snode_dag_update(C, snode);
+
+ return OPERATOR_FINISHED;
+}
+
+void NODE_OT_group_ungroup(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Ungroup";
+ ot->description = "Ungroup selected nodes";
+ ot->idname = "NODE_OT_group_ungroup";
+
+ /* api callbacks */
+ ot->exec = node_group_ungroup_exec;
+ ot->poll = ED_operator_node_active;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+}
+
+/* ******************** Separate operator ********************** */
+
+/* returns 1 if its OK */
+static int node_group_separate_selected(bNodeTree *ntree, bNode *gnode, int make_copy)
+{
+ bNodeLink *link, *link_next;
+ bNode *node, *node_next, *newnode;
+ bNodeTree *ngroup;
+ ListBase anim_basepaths = {NULL, NULL};
+
+ ngroup = (bNodeTree *)gnode->id;
+ if (ngroup == NULL) return 0;
+
+ /* deselect all nodes in the target tree */
+ for (node = ntree->nodes.first; node; node = node->next)
+ node_deselect(node);
+
+ /* 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;
+
+ 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);
+
+ newnode->locx += gnode->locx;
+ newnode->locy += gnode->locy;
+ }
+
+ /* 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;
+}
+
+typedef enum eNodeGroupSeparateType {
+ NODE_GS_COPY,
+ NODE_GS_MOVE
+} eNodeGroupSeparateType;
+
+/* Operator Property */
+EnumPropertyItem node_group_separate_types[] = {
+ {NODE_GS_COPY, "COPY", 0, "Copy", "Copy to parent node tree, keep group intact"},
+ {NODE_GS_MOVE, "MOVE", 0, "Move", "Move to parent node tree, remove from group"},
+ {0, NULL, 0, NULL, NULL}
+};
+
+static int node_group_separate_exec(bContext *C, wmOperator *op)
+{
+ SpaceNode *snode = CTX_wm_space_node(C);
+ bNode *gnode;
+ int type = RNA_enum_get(op->ptr, "type");
+
+ ED_preview_kill_jobs(C);
+
+ /* are we inside of a group? */
+ gnode = node_tree_get_editgroup(snode->nodetree);
+ if (!gnode) {
+ BKE_report(op->reports, RPT_WARNING, "Not inside node group");
+ return OPERATOR_CANCELLED;
+ }
+
+ switch (type) {
+ case NODE_GS_COPY:
+ if (!node_group_separate_selected(snode->nodetree, gnode, 1)) {
+ BKE_report(op->reports, RPT_WARNING, "Can't separate nodes");
+ return OPERATOR_CANCELLED;
+ }
+ break;
+ case NODE_GS_MOVE:
+ if (!node_group_separate_selected(snode->nodetree, gnode, 0)) {
+ BKE_report(op->reports, RPT_WARNING, "Can't separate nodes");
+ return OPERATOR_CANCELLED;
+ }
+ break;
+ }
+
+ /* switch to parent tree */
+ snode_make_group_editable(snode, NULL);
+
+ ntreeUpdateTree(snode->nodetree);
+
+ snode_notify(C, snode);
+ snode_dag_update(C, snode);
+
+ return OPERATOR_FINISHED;
+}
+
+static int node_group_separate_invoke(bContext *C, wmOperator *UNUSED(op), wmEvent *UNUSED(event))
+{
+ uiPopupMenu *pup = uiPupMenuBegin(C, "Separate", ICON_NONE);
+ uiLayout *layout = uiPupMenuLayout(pup);
+
+ uiLayoutSetOperatorContext(layout, WM_OP_EXEC_DEFAULT);
+ uiItemEnumO(layout, "NODE_OT_group_separate", NULL, 0, "type", NODE_GS_COPY);
+ uiItemEnumO(layout, "NODE_OT_group_separate", NULL, 0, "type", NODE_GS_MOVE);
+
+ uiPupMenuEnd(C, pup);
+
+ return OPERATOR_CANCELLED;
+}
+
+void NODE_OT_group_separate(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Separate";
+ ot->description = "Separate selected nodes from the node group";
+ ot->idname = "NODE_OT_group_separate";
+
+ /* api callbacks */
+ ot->invoke = node_group_separate_invoke;
+ ot->exec = node_group_separate_exec;
+ ot->poll = ED_operator_node_active;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ RNA_def_enum(ot->srna, "type", node_group_separate_types, NODE_GS_COPY, "Type", "");
+}
+
+/* ****************** Make Group operator ******************* */
+
+static int node_group_make_test(bNodeTree *ntree, bNode *gnode)
+{
+ bNode *node;
+ bNodeLink *link;
+ int totnode = 0;
+
+ /* is there something to group? also do some clearing */
+ for (node = ntree->nodes.first; node; node = node->next) {
+ if (node == gnode)
+ continue;
+
+ if (node->flag & NODE_SELECT) {
+ /* no groups in groups */
+ if (node->type == NODE_GROUP)
+ return 0;
+ totnode++;
+ }
+
+ node->done = 0;
+ }
+ if (totnode == 0) return 0;
+
+ /* check if all connections are OK, no unselected node has both
+ * inputs and outputs to a selection */
+ for (link = ntree->links.first; link; link = link->next) {
+ if (link->fromnode && link->tonode && link->fromnode->flag & NODE_SELECT && link->fromnode != gnode)
+ link->tonode->done |= 1;
+ if (link->fromnode && link->tonode && link->tonode->flag & NODE_SELECT && link->tonode != gnode)
+ link->fromnode->done |= 2;
+ }
+
+ for (node = ntree->nodes.first; node; node = node->next) {
+ if (node == gnode)
+ continue;
+ if ((node->flag & NODE_SELECT) == 0)
+ if (node->done == 3)
+ break;
+ }
+ if (node)
+ return 0;
+
+ return 1;
+}
+
+
+static void node_get_selected_minmax(bNodeTree *ntree, bNode *gnode, float *min, float *max)
+{
+ bNode *node;
+ INIT_MINMAX2(min, max);
+ for (node = ntree->nodes.first; node; node = node->next) {
+ if (node == gnode)
+ continue;
+ if (node->flag & NODE_SELECT) {
+ DO_MINMAX2((&node->locx), min, max);
+ }
+ }
+}
+
+static int node_group_make_insert_selected(bNodeTree *ntree, bNode *gnode)
+{
+ bNodeTree *ngroup = (bNodeTree *)gnode->id;
+ bNodeLink *link, *linkn;
+ bNode *node, *nextn;
+ bNodeSocket *gsock;
+ ListBase anim_basepaths = {NULL, NULL};
+ float min[2], max[2];
+
+ /* deselect all nodes in the target tree */
+ for (node = ngroup->nodes.first; node; node = node->next)
+ node_deselect(node);
+
+ node_get_selected_minmax(ntree, gnode, min, max);
+
+ /* move nodes over */
+ for (node = ntree->nodes.first; node; node = nextn) {
+ nextn = node->next;
+ if (node == gnode)
+ continue;
+ if (node->flag & NODE_SELECT) {
+ /* 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);
+
+ node->locx -= 0.5f * (min[0] + max[0]);
+ node->locy -= 0.5f * (min[1] + max[1]);
+ }
+ }
+
+ /* 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);
+
+ /* relink external sockets */
+ for (link = ntree->links.first; link; link = linkn) {
+ int fromselect = (link->fromnode && (link->fromnode->flag & NODE_SELECT) && link->fromnode != gnode);
+ int toselect = (link->tonode && (link->tonode->flag & NODE_SELECT) && link->tonode != gnode);
+ linkn = link->next;
+
+ if (gnode && ((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) {
+ gsock = node_group_expose_socket(ngroup, link->tosock, SOCK_IN);
+ link->tosock->link = nodeAddLink(ngroup, NULL, gsock, link->tonode, link->tosock);
+ link->tosock = node_group_add_extern_socket(ntree, &gnode->inputs, SOCK_IN, gsock);
+ link->tonode = gnode;
+ }
+ else if (fromselect) {
+ /* search for existing group node socket */
+ for (gsock = ngroup->outputs.first; gsock; gsock = gsock->next)
+ if (gsock->link && gsock->link->fromsock == link->fromsock)
+ break;
+ if (!gsock) {
+ gsock = node_group_expose_socket(ngroup, link->fromsock, SOCK_OUT);
+ gsock->link = nodeAddLink(ngroup, link->fromnode, link->fromsock, NULL, gsock);
+ link->fromsock = node_group_add_extern_socket(ntree, &gnode->outputs, SOCK_OUT, gsock);
+ }
+ else
+ link->fromsock = node_group_find_output(gnode, gsock);
+ link->fromnode = gnode;
+ }
+ }
+
+ /* update of the group tree */
+ ngroup->update |= NTREE_UPDATE;
+ /* update of the tree containing the group instance node */
+ ntree->update |= NTREE_UPDATE_NODES | NTREE_UPDATE_LINKS;
+
+ return 1;
+}
+
+static bNode *node_group_make_from_selected(bNodeTree *ntree)
+{
+ bNode *gnode;
+ bNodeTree *ngroup;
+ float min[2], max[2];
+ bNodeTemplate ntemp;
+
+ node_get_selected_minmax(ntree, NULL, min, max);
+
+ /* new nodetree */
+ ngroup = ntreeAddTree("NodeGroup", ntree->type, NODE_GROUP);
+
+ /* make group node */
+ ntemp.type = NODE_GROUP;
+ ntemp.ngroup = ngroup;
+ gnode = nodeAddNode(ntree, &ntemp);
+ gnode->locx = 0.5f * (min[0] + max[0]);
+ gnode->locy = 0.5f * (min[1] + max[1]);
+
+ node_group_make_insert_selected(ntree, gnode);
+
+ /* update of the tree containing the group instance node */
+ ntree->update |= NTREE_UPDATE_NODES;
+
+ return gnode;
+}
+
+typedef enum eNodeGroupMakeType {
+ NODE_GM_NEW,
+ NODE_GM_INSERT
+} eNodeGroupMakeType;
+
+/* Operator Property */
+EnumPropertyItem node_group_make_types[] = {
+ {NODE_GM_NEW, "NEW", 0, "New", "Create a new node group from selected nodes"},
+ {NODE_GM_INSERT, "INSERT", 0, "Insert", "Insert into active node group"},
+ {0, NULL, 0, NULL, NULL}
+};
+
+static int node_group_make_exec(bContext *C, wmOperator *op)
+{
+ SpaceNode *snode = CTX_wm_space_node(C);
+ bNode *gnode;
+ int type = RNA_enum_get(op->ptr, "type");
+
+ if (snode->edittree != snode->nodetree) {
+ BKE_report(op->reports, RPT_WARNING, "Can not add a new Group in a Group");
+ return OPERATOR_CANCELLED;
+ }
+
+ /* for time being... is too complex to handle */
+ if (snode->treetype == NTREE_COMPOSIT) {
+ for (gnode = snode->nodetree->nodes.first; gnode; gnode = gnode->next) {
+ if (gnode->flag & SELECT)
+ if (gnode->type == CMP_NODE_R_LAYERS)
+ break;
+ }
+
+ if (gnode) {
+ BKE_report(op->reports, RPT_WARNING, "Can not add RenderLayer in a Group");
+ return OPERATOR_CANCELLED;
+ }
+ }
+
+ ED_preview_kill_jobs(C);
+
+ switch (type) {
+ case NODE_GM_NEW:
+ if (node_group_make_test(snode->nodetree, NULL)) {
+ gnode = node_group_make_from_selected(snode->nodetree);
+ }
+ else {
+ BKE_report(op->reports, RPT_WARNING, "Can not make Group");
+ return OPERATOR_CANCELLED;
+ }
+ break;
+ case NODE_GM_INSERT:
+ gnode = nodeGetActive(snode->nodetree);
+ if (!gnode || gnode->type != NODE_GROUP) {
+ BKE_report(op->reports, RPT_WARNING, "No active Group node");
+ return OPERATOR_CANCELLED;
+ }
+ if (node_group_make_test(snode->nodetree, gnode)) {
+ node_group_make_insert_selected(snode->nodetree, gnode);
+ }
+ else {
+ BKE_report(op->reports, RPT_WARNING, "Can not insert into Group");
+ return OPERATOR_CANCELLED;
+ }
+ break;
+ }
+
+ if (gnode) {
+ nodeSetActive(snode->nodetree, gnode);
+ snode_make_group_editable(snode, gnode);
+ }
+
+ if (gnode)
+ ntreeUpdateTree((bNodeTree *)gnode->id);
+ ntreeUpdateTree(snode->nodetree);
+
+ snode_notify(C, snode);
+ snode_dag_update(C, snode);
+
+ return OPERATOR_FINISHED;
+}
+
+static int node_group_make_invoke(bContext *C, wmOperator *UNUSED(op), wmEvent *UNUSED(event))
+{
+ SpaceNode *snode = CTX_wm_space_node(C);
+ bNode *act = nodeGetActive(snode->edittree);
+ uiPopupMenu *pup = uiPupMenuBegin(C, "Make Group", ICON_NONE);
+ uiLayout *layout = uiPupMenuLayout(pup);
+
+ uiLayoutSetOperatorContext(layout, WM_OP_EXEC_DEFAULT);
+ uiItemEnumO(layout, "NODE_OT_group_make", NULL, 0, "type", NODE_GM_NEW);
+
+ /* if active node is a group, add insert option */
+ if (act && act->type == NODE_GROUP) {
+ uiItemEnumO(layout, "NODE_OT_group_make", NULL, 0, "type", NODE_GM_INSERT);
+ }
+
+ uiPupMenuEnd(C, pup);
+
+ return OPERATOR_CANCELLED;
+}
+
+void NODE_OT_group_make(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Group";
+ ot->description = "Make group from selected nodes";
+ ot->idname = "NODE_OT_group_make";
+
+ /* api callbacks */
+ ot->invoke = node_group_make_invoke;
+ ot->exec = node_group_make_exec;
+ ot->poll = ED_operator_node_active;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ RNA_def_enum(ot->srna, "type", node_group_make_types, NODE_GM_NEW, "Type", "");
+}
diff --git a/source/blender/editors/space_node/node_intern.h b/source/blender/editors/space_node/node_intern.h
index 9cd62342e19..5c6a91195a6 100644
--- a/source/blender/editors/space_node/node_intern.h
+++ b/source/blender/editors/space_node/node_intern.h
@@ -121,16 +121,56 @@ int node_link_bezier_points(View2D *v2d, SpaceNode *snode, bNodeLink *link, floa
// void node_draw_link_straight(View2D *v2d, SpaceNode *snode, bNodeLink *link, int th_col1, int do_shaded, int th_col2, int do_triple, int th_col3 );
void draw_nodespace_back_pix(ARegion *ar, SpaceNode *snode, int color_manage);
+
+/* node_add.c */
+bNode *node_add_node(struct SpaceNode *snode, struct Main *bmain, struct Scene *scene,
+ struct bNodeTemplate *ntemp, float locx, float locy);
+void NODE_OT_add_reroute(struct wmOperatorType *ot);
+
+
+/* node_group.c */
+void NODE_OT_group_make(struct wmOperatorType *ot);
+void NODE_OT_group_ungroup(struct wmOperatorType *ot);
+void NODE_OT_group_separate(struct wmOperatorType *ot);
+void NODE_OT_group_edit(struct wmOperatorType *ot);
+void NODE_OT_group_socket_add(struct wmOperatorType *ot);
+void NODE_OT_group_socket_remove(struct wmOperatorType *ot);
+void NODE_OT_group_socket_move_up(struct wmOperatorType *ot);
+void NODE_OT_group_socket_move_down(struct wmOperatorType *ot);
+
+
+/* note_add.c */
+void NODE_OT_add_file(struct wmOperatorType *ot);
+void NODE_OT_new_node_tree(struct wmOperatorType *ot);
+
+
+/* node_relationships.c */
+void NODE_OT_link(struct wmOperatorType *ot);
+void NODE_OT_link_make(struct wmOperatorType *ot);
+void NODE_OT_links_cut(struct wmOperatorType *ot);
+void NODE_OT_links_detach(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);
+
+void NODE_OT_show_cyclic_dependencies(struct wmOperatorType *ot);
+void NODE_OT_link_viewer(struct wmOperatorType *ot);
+
/* node_edit.c */
void node_tree_from_ID(ID *id, bNodeTree **ntree, bNodeTree **edittree, int *treetype);
void snode_notify(bContext *C, SpaceNode *snode);
void snode_dag_update(bContext *C, SpaceNode *snode);
-bNode *node_add_node(struct SpaceNode *snode, struct Main *bmain, struct Scene *scene, struct bNodeTemplate *ntemp, float locx, float locy);
void snode_set_context(SpaceNode *snode, Scene *scene);
void snode_make_group_editable(SpaceNode *snode, bNode *gnode);
void snode_composite_job(const struct bContext *C, ScrArea *sa);
bNode *node_tree_get_editgroup(bNodeTree *ntree);
-void snode_autoconnect(SpaceNode *snode, int allow_multiple, int replace);
+void snode_update(struct SpaceNode *snode, struct bNode *node);
+bNode *editnode_get_active(bNodeTree *ntree);
+int composite_node_active(struct bContext *C);
+
int node_has_hidden_sockets(bNode *node);
void node_set_hidden_sockets(SpaceNode *snode, bNode *node, int set);
int node_render_changed_exec(bContext *, wmOperator *);
@@ -141,21 +181,6 @@ void NODE_OT_delete(struct wmOperatorType *ot);
void NODE_OT_delete_reconnect(struct wmOperatorType *ot);
void NODE_OT_resize(struct wmOperatorType *ot);
-void NODE_OT_link(struct wmOperatorType *ot);
-void NODE_OT_link_make(struct wmOperatorType *ot);
-void NODE_OT_links_cut(struct wmOperatorType *ot);
-void NODE_OT_links_detach(struct wmOperatorType *ot);
-void NODE_OT_add_reroute(struct wmOperatorType *ot);
-
-void NODE_OT_group_make(struct wmOperatorType *ot);
-void NODE_OT_group_ungroup(struct wmOperatorType *ot);
-void NODE_OT_group_separate(struct wmOperatorType *ot);
-void NODE_OT_group_edit(struct wmOperatorType *ot);
-void NODE_OT_group_socket_add(struct wmOperatorType *ot);
-void NODE_OT_group_socket_remove(struct wmOperatorType *ot);
-void NODE_OT_group_socket_move_up(struct wmOperatorType *ot);
-void NODE_OT_group_socket_move_down(struct wmOperatorType *ot);
-
void NODE_OT_mute_toggle(struct wmOperatorType *ot);
void NODE_OT_hide_toggle(struct wmOperatorType *ot);
void NODE_OT_hide_socket_toggle(struct wmOperatorType *ot);
@@ -163,8 +188,6 @@ 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);
void NODE_OT_read_fullsamplelayers(struct wmOperatorType *ot);
void NODE_OT_read_renderlayers(struct wmOperatorType *ot);
void NODE_OT_render_changed(struct wmOperatorType *ot);
@@ -173,20 +196,10 @@ void NODE_OT_backimage_move(struct wmOperatorType *ot);
void NODE_OT_backimage_zoom(struct wmOperatorType *ot);
void NODE_OT_backimage_sample(wmOperatorType *ot);
-void NODE_OT_add_file(struct wmOperatorType *ot);
-
-void NODE_OT_new_node_tree(struct wmOperatorType *ot);
-
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
@@ -198,6 +211,7 @@ extern const char *node_context_dir[];
#define NODE_DY U.widget_unit
#define NODE_MARGIN_X 15
#define NODE_SOCKSIZE 5
+#define NODE_LINK_RESOL 12
// XXX button events (butspace)
enum {
diff --git a/source/blender/editors/space_node/node_relationships.c b/source/blender/editors/space_node/node_relationships.c
new file mode 100644
index 00000000000..4fcc2abece4
--- /dev/null
+++ b/source/blender/editors/space_node/node_relationships.c
@@ -0,0 +1,1446 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2005 Blender Foundation.
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): David Millan Escriva, Juho Vepsäläinen, Nathan Letwory
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/editors/space_node/node_relationships.c
+ * \ingroup spnode
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <string.h>
+#include <errno.h>
+
+#include "MEM_guardedalloc.h"
+
+#include "DNA_ID.h"
+#include "DNA_lamp_types.h"
+#include "DNA_material_types.h"
+#include "DNA_node_types.h"
+#include "DNA_object_types.h"
+#include "DNA_particle_types.h"
+#include "DNA_scene_types.h"
+#include "DNA_world_types.h"
+#include "DNA_action_types.h"
+#include "DNA_anim_types.h"
+
+#include "BLI_math.h"
+#include "BLI_blenlib.h"
+#include "BLI_utildefines.h"
+
+#include "BKE_action.h"
+#include "BKE_animsys.h"
+#include "BKE_context.h"
+#include "BKE_depsgraph.h"
+#include "BKE_global.h"
+#include "BKE_image.h"
+#include "BKE_library.h"
+#include "BKE_main.h"
+#include "BKE_node.h"
+#include "BKE_material.h"
+#include "BKE_modifier.h"
+#include "BKE_paint.h"
+#include "BKE_scene.h"
+#include "BKE_screen.h"
+#include "BKE_texture.h"
+#include "BKE_report.h"
+
+#include "RE_pipeline.h"
+
+#include "IMB_imbuf_types.h"
+
+#include "ED_node.h"
+#include "ED_image.h"
+#include "ED_screen.h"
+#include "ED_space_api.h"
+#include "ED_render.h"
+
+#include "RNA_access.h"
+#include "RNA_define.h"
+#include "RNA_enum_types.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+#include "UI_view2d.h"
+
+#include "IMB_imbuf.h"
+
+#include "RNA_enum_types.h"
+
+#include "GPU_material.h"
+
+#include "node_intern.h"
+#include "NOD_socket.h"
+
+
+/* ****************** Add *********************** */
+
+
+typedef struct bNodeListItem {
+ struct bNodeListItem *next, *prev;
+ struct bNode *node;
+} bNodeListItem;
+
+static int sort_nodes_locx(void *a, void *b)
+{
+ bNodeListItem *nli1 = (bNodeListItem *)a;
+ bNodeListItem *nli2 = (bNodeListItem *)b;
+ bNode *node1 = nli1->node;
+ bNode *node2 = nli2->node;
+
+ if (node1->locx > node2->locx)
+ return 1;
+ else
+ return 0;
+}
+
+static int socket_is_available(bNodeTree *UNUSED(ntree), bNodeSocket *sock, int allow_used)
+{
+ if (nodeSocketIsHidden(sock))
+ return 0;
+
+ if (!allow_used && (sock->flag & SOCK_IN_USE))
+ return 0;
+
+ return 1;
+}
+
+static bNodeSocket *best_socket_output(bNodeTree *ntree, bNode *node, bNodeSocket *sock_target, int allow_multiple)
+{
+ bNodeSocket *sock;
+
+ /* first look for selected output */
+ for (sock = node->outputs.first; sock; sock = sock->next) {
+ if (!socket_is_available(ntree, sock, allow_multiple))
+ continue;
+
+ if (sock->flag & SELECT)
+ return sock;
+ }
+
+ /* try to find a socket with a matching name */
+ for (sock = node->outputs.first; sock; sock = sock->next) {
+ if (!socket_is_available(ntree, sock, allow_multiple))
+ continue;
+
+ /* check for same types */
+ if (sock->type == sock_target->type) {
+ if (strcmp(sock->name, sock_target->name) == 0)
+ return sock;
+ }
+ }
+
+ /* otherwise settle for the first available socket of the right type */
+ for (sock = node->outputs.first; sock; sock = sock->next) {
+
+ if (!socket_is_available(ntree, sock, allow_multiple))
+ continue;
+
+ /* check for same types */
+ if (sock->type == sock_target->type) {
+ return sock;
+ }
+ }
+
+ return NULL;
+}
+
+/* this is a bit complicated, but designed to prioritize finding
+ * sockets of higher types, such as image, first */
+static bNodeSocket *best_socket_input(bNodeTree *ntree, bNode *node, int num, int replace)
+{
+ bNodeSocket *sock;
+ int socktype, maxtype = 0;
+ int a = 0;
+
+ for (sock = node->inputs.first; sock; sock = sock->next) {
+ maxtype = MAX2(sock->type, maxtype);
+ }
+
+ /* find sockets of higher 'types' first (i.e. image) */
+ for (socktype = maxtype; socktype >= 0; socktype--) {
+ for (sock = node->inputs.first; sock; sock = sock->next) {
+
+ if (!socket_is_available(ntree, sock, replace)) {
+ a++;
+ continue;
+ }
+
+ if (sock->type == socktype) {
+ /* increment to make sure we don't keep finding
+ * the same socket on every attempt running this function */
+ a++;
+ if (a > num)
+ return sock;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+static int snode_autoconnect_input(SpaceNode *snode, bNode *node_fr, bNodeSocket *sock_fr, bNode *node_to, bNodeSocket *sock_to, int replace)
+{
+ bNodeTree *ntree = snode->edittree;
+ bNodeLink *link;
+
+ /* then we can connect */
+ if (replace)
+ nodeRemSocketLinks(ntree, sock_to);
+
+ link = nodeAddLink(ntree, node_fr, sock_fr, node_to, sock_to);
+ /* validate the new link */
+ ntreeUpdateTree(ntree);
+ if (!(link->flag & NODE_LINK_VALID)) {
+ nodeRemLink(ntree, link);
+ return 0;
+ }
+
+ snode_update(snode, node_to);
+ return 1;
+}
+
+static void snode_autoconnect(SpaceNode *snode, int allow_multiple, int replace)
+{
+ bNodeTree *ntree = snode->edittree;
+ ListBase *nodelist = MEM_callocN(sizeof(ListBase), "items_list");
+ bNodeListItem *nli;
+ bNode *node;
+ int i, numlinks = 0;
+
+ for (node = ntree->nodes.first; node; node = node->next) {
+ if (node->flag & NODE_SELECT) {
+ nli = MEM_mallocN(sizeof(bNodeListItem), "temporary node list item");
+ nli->node = node;
+ BLI_addtail(nodelist, nli);
+ }
+ }
+
+ /* sort nodes left to right */
+ BLI_sortlist(nodelist, sort_nodes_locx);
+
+ for (nli = nodelist->first; nli; nli = nli->next) {
+ bNode *node_fr, *node_to;
+ bNodeSocket *sock_fr, *sock_to;
+ int has_selected_inputs = 0;
+
+ if (nli->next == NULL) break;
+
+ node_fr = nli->node;
+ node_to = nli->next->node;
+
+ /* if there are selected sockets, connect those */
+ for (sock_to = node_to->inputs.first; sock_to; sock_to = sock_to->next) {
+ if (sock_to->flag & SELECT) {
+ has_selected_inputs = 1;
+
+ if (!socket_is_available(ntree, sock_to, replace))
+ continue;
+
+ /* check for an appropriate output socket to connect from */
+ sock_fr = best_socket_output(ntree, node_fr, sock_to, allow_multiple);
+ if (!sock_fr)
+ continue;
+
+ if (snode_autoconnect_input(snode, node_fr, sock_fr, node_to, sock_to, replace))
+ ++numlinks;
+ }
+ }
+
+ if (!has_selected_inputs) {
+ /* no selected inputs, connect by finding suitable match */
+ int num_inputs = BLI_countlist(&node_to->inputs);
+
+ for (i = 0; i < num_inputs; i++) {
+
+ /* find the best guess input socket */
+ sock_to = best_socket_input(ntree, node_to, i, replace);
+ if (!sock_to)
+ continue;
+
+ /* check for an appropriate output socket to connect from */
+ sock_fr = best_socket_output(ntree, node_fr, sock_to, allow_multiple);
+ if (!sock_fr)
+ continue;
+
+ if (snode_autoconnect_input(snode, node_fr, sock_fr, node_to, sock_to, replace)) {
+ ++numlinks;
+ break;
+ }
+ }
+ }
+ }
+
+ if (numlinks > 0) {
+ ntreeUpdateTree(ntree);
+ }
+
+ BLI_freelistN(nodelist);
+ MEM_freeN(nodelist);
+}
+
+/* *************************** link viewer op ******************** */
+
+static int node_link_viewer(const bContext *C, bNode *tonode)
+{
+ SpaceNode *snode = CTX_wm_space_node(C);
+ bNode *node;
+ bNodeLink *link;
+ bNodeSocket *sock;
+
+ /* context check */
+ if (tonode == NULL || tonode->outputs.first == NULL)
+ return OPERATOR_CANCELLED;
+ if (ELEM(tonode->type, CMP_NODE_VIEWER, CMP_NODE_SPLITVIEWER))
+ return OPERATOR_CANCELLED;
+
+ /* get viewer */
+ for (node = snode->edittree->nodes.first; node; node = node->next)
+ if (ELEM(node->type, CMP_NODE_VIEWER, CMP_NODE_SPLITVIEWER))
+ if (node->flag & NODE_DO_OUTPUT)
+ break;
+ /* no viewer, we make one active */
+ if (node == NULL) {
+ for (node = snode->edittree->nodes.first; node; node = node->next) {
+ if (ELEM(node->type, CMP_NODE_VIEWER, CMP_NODE_SPLITVIEWER)) {
+ node->flag |= NODE_DO_OUTPUT;
+ break;
+ }
+ }
+ }
+
+ sock = NULL;
+
+ /* try to find an already connected socket to cycle to the next */
+ if (node) {
+ link = NULL;
+ for (link = snode->edittree->links.first; link; link = link->next)
+ if (link->tonode == node && link->fromnode == tonode)
+ if (link->tosock == node->inputs.first)
+ break;
+ if (link) {
+ /* unlink existing connection */
+ sock = link->fromsock;
+ nodeRemLink(snode->edittree, link);
+
+ /* find a socket after the previously connected socket */
+ for (sock = sock->next; sock; sock = sock->next)
+ if (!nodeSocketIsHidden(sock))
+ break;
+ }
+ }
+
+ /* find a socket starting from the first socket */
+ if (!sock) {
+ for (sock = tonode->outputs.first; sock; sock = sock->next)
+ if (!nodeSocketIsHidden(sock))
+ break;
+ }
+
+ if (sock) {
+ /* add a new viewer if none exists yet */
+ if (!node) {
+ Main *bmain = CTX_data_main(C);
+ Scene *scene = CTX_data_scene(C);
+ bNodeTemplate ntemp;
+
+ ntemp.type = CMP_NODE_VIEWER;
+ /* XXX location is a quick hack, just place it next to the linked socket */
+ node = node_add_node(snode, bmain, scene, &ntemp, sock->locx + 100, sock->locy);
+ if (!node)
+ return OPERATOR_CANCELLED;
+
+ link = NULL;
+ }
+ else {
+ /* get link to viewer */
+ for (link = snode->edittree->links.first; link; link = link->next)
+ if (link->tonode == node && link->tosock == node->inputs.first)
+ break;
+ }
+
+ if (link == NULL) {
+ nodeAddLink(snode->edittree, tonode, sock, node, node->inputs.first);
+ }
+ else {
+ link->fromnode = tonode;
+ link->fromsock = sock;
+ /* make sure the dependency sorting is updated */
+ snode->edittree->update |= NTREE_UPDATE_LINKS;
+ }
+ ntreeUpdateTree(snode->edittree);
+ snode_update(snode, node);
+ }
+
+ return OPERATOR_FINISHED;
+}
+
+
+static int node_active_link_viewer(bContext *C, wmOperator *UNUSED(op))
+{
+ SpaceNode *snode = CTX_wm_space_node(C);
+ bNode *node;
+
+ node = editnode_get_active(snode->edittree);
+
+ if (!node)
+ return OPERATOR_CANCELLED;
+
+ ED_preview_kill_jobs(C);
+
+ if (node_link_viewer(C, node) == OPERATOR_CANCELLED)
+ return OPERATOR_CANCELLED;
+
+ snode_notify(C, snode);
+
+ return OPERATOR_FINISHED;
+}
+
+
+void NODE_OT_link_viewer(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Link to Viewer Node";
+ ot->description = "Link to viewer node";
+ ot->idname = "NODE_OT_link_viewer";
+
+ /* api callbacks */
+ ot->exec = node_active_link_viewer;
+ ot->poll = composite_node_active;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+}
+
+
+/* *************************** add link op ******************** */
+
+static void node_remove_extra_links(SpaceNode *snode, bNodeSocket *tsock, bNodeLink *link)
+{
+ bNodeLink *tlink;
+ bNodeSocket *sock;
+
+ if (tsock && nodeCountSocketLinks(snode->edittree, link->tosock) > tsock->limit) {
+
+ for (tlink = snode->edittree->links.first; tlink; tlink = tlink->next) {
+ if (link != tlink && tlink->tosock == link->tosock)
+ break;
+ }
+ if (tlink) {
+ /* try to move the existing link to the next available socket */
+ if (tlink->tonode) {
+ /* is there a free input socket with the target type? */
+ for (sock = tlink->tonode->inputs.first; sock; sock = sock->next) {
+ if (sock->type == tlink->tosock->type)
+ if (nodeCountSocketLinks(snode->edittree, sock) < sock->limit)
+ break;
+ }
+ if (sock) {
+ tlink->tosock = sock;
+ sock->flag &= ~SOCK_HIDDEN;
+ }
+ else {
+ nodeRemLink(snode->edittree, tlink);
+ }
+ }
+ else
+ nodeRemLink(snode->edittree, tlink);
+
+ snode->edittree->update |= NTREE_UPDATE_LINKS;
+ }
+ }
+}
+
+static int outside_group_rect(SpaceNode *snode)
+{
+ bNode *gnode = node_tree_get_editgroup(snode->nodetree);
+ if (gnode) {
+ return (snode->mx < gnode->totr.xmin ||
+ snode->mx >= gnode->totr.xmax ||
+ snode->my < gnode->totr.ymin ||
+ snode->my >= gnode->totr.ymax);
+ }
+ return 0;
+}
+
+/* loop that adds a nodelink, called by function below */
+/* in_out = starting socket */
+static int node_link_modal(bContext *C, wmOperator *op, wmEvent *event)
+{
+ SpaceNode *snode = CTX_wm_space_node(C);
+ ARegion *ar = CTX_wm_region(C);
+ bNodeLinkDrag *nldrag = op->customdata;
+ bNodeTree *ntree = snode->edittree;
+ bNode *tnode;
+ bNodeSocket *tsock = NULL;
+ bNodeLink *link;
+ LinkData *linkdata;
+ int in_out;
+
+ in_out = nldrag->in_out;
+
+ UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1],
+ &snode->mx, &snode->my);
+
+ switch (event->type) {
+ case MOUSEMOVE:
+
+ if (in_out == SOCK_OUT) {
+ if (node_find_indicated_socket(snode, &tnode, &tsock, SOCK_IN)) {
+ for (linkdata = nldrag->links.first; linkdata; linkdata = linkdata->next) {
+ link = linkdata->data;
+
+ /* skip if this is already the target socket */
+ if (link->tosock == tsock)
+ continue;
+ /* skip if socket is on the same node as the fromsock */
+ if (tnode && link->fromnode == tnode)
+ continue;
+
+ /* attach links to the socket */
+ link->tonode = tnode;
+ link->tosock = tsock;
+ /* add it to the node tree temporarily */
+ if (BLI_findindex(&ntree->links, link) < 0)
+ BLI_addtail(&ntree->links, link);
+
+ ntree->update |= NTREE_UPDATE_LINKS;
+ }
+ ntreeUpdateTree(ntree);
+ }
+ else {
+ int do_update = FALSE;
+ for (linkdata = nldrag->links.first; linkdata; linkdata = linkdata->next) {
+ link = linkdata->data;
+
+ if (link->tonode || link->tosock) {
+ BLI_remlink(&ntree->links, link);
+ link->prev = link->next = NULL;
+ link->tonode = NULL;
+ link->tosock = NULL;
+
+ ntree->update |= NTREE_UPDATE_LINKS;
+ do_update = TRUE;
+ }
+ }
+ if (do_update) {
+ ntreeUpdateTree(ntree);
+ }
+ }
+ }
+ else {
+ if (node_find_indicated_socket(snode, &tnode, &tsock, SOCK_OUT)) {
+ for (linkdata = nldrag->links.first; linkdata; linkdata = linkdata->next) {
+ link = linkdata->data;
+
+ /* skip if this is already the target socket */
+ if (link->fromsock == tsock)
+ continue;
+ /* skip if socket is on the same node as the fromsock */
+ if (tnode && link->tonode == tnode)
+ continue;
+
+ /* attach links to the socket */
+ link->fromnode = tnode;
+ link->fromsock = tsock;
+ /* add it to the node tree temporarily */
+ if (BLI_findindex(&ntree->links, link) < 0)
+ BLI_addtail(&ntree->links, link);
+
+ ntree->update |= NTREE_UPDATE_LINKS;
+ }
+ ntreeUpdateTree(ntree);
+ }
+ else {
+ int do_update = FALSE;
+ for (linkdata = nldrag->links.first; linkdata; linkdata = linkdata->next) {
+ link = linkdata->data;
+
+ if (link->fromnode || link->fromsock) {
+ BLI_remlink(&ntree->links, link);
+ link->prev = link->next = NULL;
+ link->fromnode = NULL;
+ link->fromsock = NULL;
+
+ ntree->update |= NTREE_UPDATE_LINKS;
+ do_update = TRUE;
+ }
+ }
+ if (do_update) {
+ ntreeUpdateTree(ntree);
+ }
+ }
+ }
+
+ ED_region_tag_redraw(ar);
+ break;
+
+ case LEFTMOUSE:
+ case RIGHTMOUSE:
+ case MIDDLEMOUSE: {
+ for (linkdata = nldrag->links.first; linkdata; linkdata = linkdata->next) {
+ link = linkdata->data;
+
+ if (link->tosock && link->fromsock) {
+ /* send changed events for original tonode and new */
+ if (link->tonode)
+ snode_update(snode, link->tonode);
+
+ /* we might need to remove a link */
+ if (in_out == SOCK_OUT)
+ node_remove_extra_links(snode, link->tosock, link);
+
+ /* when linking to group outputs, update the socket type */
+ /* XXX this should all be part of a generic update system */
+ if (!link->tonode) {
+ if (link->tosock->type != link->fromsock->type)
+ nodeSocketSetType(link->tosock, link->fromsock->type);
+ }
+ }
+ else if (outside_group_rect(snode) && (link->tonode || link->fromnode)) {
+ /* automatically add new group socket */
+ if (link->tonode && link->tosock) {
+ link->fromsock = node_group_expose_socket(ntree, link->tosock, SOCK_IN);
+ link->fromnode = NULL;
+ if (BLI_findindex(&ntree->links, link) < 0)
+ BLI_addtail(&ntree->links, link);
+
+ ntree->update |= NTREE_UPDATE_GROUP_IN | NTREE_UPDATE_LINKS;
+ }
+ else if (link->fromnode && link->fromsock) {
+ link->tosock = node_group_expose_socket(ntree, link->fromsock, SOCK_OUT);
+ link->tonode = NULL;
+ if (BLI_findindex(&ntree->links, link) < 0)
+ BLI_addtail(&ntree->links, link);
+
+ ntree->update |= NTREE_UPDATE_GROUP_OUT | NTREE_UPDATE_LINKS;
+ }
+ }
+ else
+ nodeRemLink(ntree, link);
+ }
+
+ ntreeUpdateTree(ntree);
+ snode_notify(C, snode);
+ snode_dag_update(C, snode);
+
+ BLI_remlink(&snode->linkdrag, nldrag);
+ /* links->data pointers are either held by the tree or freed already */
+ BLI_freelistN(&nldrag->links);
+ MEM_freeN(nldrag);
+
+ return OPERATOR_FINISHED;
+ }
+ }
+
+ return OPERATOR_RUNNING_MODAL;
+}
+
+/* return 1 when socket clicked */
+static bNodeLinkDrag *node_link_init(SpaceNode *snode, int detach)
+{
+ bNode *node;
+ bNodeSocket *sock;
+ bNodeLink *link, *link_next, *oplink;
+ bNodeLinkDrag *nldrag = NULL;
+ LinkData *linkdata;
+ int num_links;
+
+ /* output indicated? */
+ if (node_find_indicated_socket(snode, &node, &sock, SOCK_OUT)) {
+ nldrag = MEM_callocN(sizeof(bNodeLinkDrag), "drag link op customdata");
+
+ num_links = nodeCountSocketLinks(snode->edittree, sock);
+ if (num_links > 0 && (num_links >= sock->limit || detach)) {
+ /* dragged links are fixed on input side */
+ nldrag->in_out = SOCK_IN;
+ /* detach current links and store them in the operator data */
+ for (link = snode->edittree->links.first; link; link = link_next) {
+ link_next = link->next;
+ if (link->fromsock == sock) {
+ linkdata = MEM_callocN(sizeof(LinkData), "drag link op link data");
+ linkdata->data = oplink = MEM_callocN(sizeof(bNodeLink), "drag link op link");
+ *oplink = *link;
+ oplink->next = oplink->prev = NULL;
+ BLI_addtail(&nldrag->links, linkdata);
+ nodeRemLink(snode->edittree, link);
+ }
+ }
+ }
+ else {
+ /* dragged links are fixed on output side */
+ nldrag->in_out = SOCK_OUT;
+ /* create a new link */
+ linkdata = MEM_callocN(sizeof(LinkData), "drag link op link data");
+ linkdata->data = oplink = MEM_callocN(sizeof(bNodeLink), "drag link op link");
+ oplink->fromnode = node;
+ oplink->fromsock = sock;
+ BLI_addtail(&nldrag->links, linkdata);
+ }
+ }
+ /* or an input? */
+ else if (node_find_indicated_socket(snode, &node, &sock, SOCK_IN)) {
+ nldrag = MEM_callocN(sizeof(bNodeLinkDrag), "drag link op customdata");
+
+ num_links = nodeCountSocketLinks(snode->edittree, sock);
+ if (num_links > 0 && (num_links >= sock->limit || detach)) {
+ /* dragged links are fixed on output side */
+ nldrag->in_out = SOCK_OUT;
+ /* detach current links and store them in the operator data */
+ for (link = snode->edittree->links.first; link; link = link_next) {
+ link_next = link->next;
+ if (link->tosock == sock) {
+ linkdata = MEM_callocN(sizeof(LinkData), "drag link op link data");
+ linkdata->data = oplink = MEM_callocN(sizeof(bNodeLink), "drag link op link");
+ *oplink = *link;
+ oplink->next = oplink->prev = NULL;
+ BLI_addtail(&nldrag->links, linkdata);
+ nodeRemLink(snode->edittree, link);
+
+ /* send changed event to original link->tonode */
+ if (node)
+ snode_update(snode, node);
+ }
+ }
+ }
+ else {
+ /* dragged links are fixed on input side */
+ nldrag->in_out = SOCK_IN;
+ /* create a new link */
+ linkdata = MEM_callocN(sizeof(LinkData), "drag link op link data");
+ linkdata->data = oplink = MEM_callocN(sizeof(bNodeLink), "drag link op link");
+ oplink->tonode = node;
+ oplink->tosock = sock;
+ BLI_addtail(&nldrag->links, linkdata);
+ }
+ }
+
+ return nldrag;
+}
+
+static int node_link_invoke(bContext *C, wmOperator *op, wmEvent *event)
+{
+ SpaceNode *snode = CTX_wm_space_node(C);
+ ARegion *ar = CTX_wm_region(C);
+ bNodeLinkDrag *nldrag;
+ int detach = RNA_boolean_get(op->ptr, "detach");
+
+ UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1],
+ &snode->mx, &snode->my);
+
+ ED_preview_kill_jobs(C);
+
+ nldrag = node_link_init(snode, detach);
+
+ if (nldrag) {
+ op->customdata = nldrag;
+ BLI_addtail(&snode->linkdrag, nldrag);
+
+ /* add modal handler */
+ WM_event_add_modal_handler(C, op);
+
+ return OPERATOR_RUNNING_MODAL;
+ }
+ else
+ return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH;
+}
+
+static int node_link_cancel(bContext *C, wmOperator *op)
+{
+ SpaceNode *snode = CTX_wm_space_node(C);
+ bNodeLinkDrag *nldrag = op->customdata;
+
+ BLI_remlink(&snode->linkdrag, nldrag);
+
+ BLI_freelistN(&nldrag->links);
+ MEM_freeN(nldrag);
+
+ return OPERATOR_CANCELLED;
+}
+
+void NODE_OT_link(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Link Nodes";
+ ot->idname = "NODE_OT_link";
+ ot->description = "Use the mouse to create a link between two nodes";
+
+ /* api callbacks */
+ ot->invoke = node_link_invoke;
+ ot->modal = node_link_modal;
+// ot->exec = node_link_exec;
+ ot->poll = ED_operator_node_active;
+ ot->cancel = node_link_cancel;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING;
+
+ RNA_def_boolean(ot->srna, "detach", FALSE, "Detach", "Detach and redirect existing links");
+}
+
+/* ********************** Make Link operator ***************** */
+
+/* makes a link between selected output and input sockets */
+static int node_make_link_exec(bContext *C, wmOperator *op)
+{
+ SpaceNode *snode = CTX_wm_space_node(C);
+ int replace = RNA_boolean_get(op->ptr, "replace");
+
+ ED_preview_kill_jobs(C);
+
+ snode_autoconnect(snode, 1, replace);
+
+ /* deselect sockets after linking */
+ node_deselect_all_input_sockets(snode, 0);
+ node_deselect_all_output_sockets(snode, 0);
+
+ ntreeUpdateTree(snode->edittree);
+ snode_notify(C, snode);
+ snode_dag_update(C, snode);
+
+ return OPERATOR_FINISHED;
+}
+
+void NODE_OT_link_make(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Make Links";
+ ot->description = "Makes a link between selected output in input sockets";
+ ot->idname = "NODE_OT_link_make";
+
+ /* callbacks */
+ ot->exec = node_make_link_exec;
+ ot->poll = ED_operator_node_active; // XXX we need a special poll which checks that there are selected input/output sockets
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ RNA_def_boolean(ot->srna, "replace", 0, "Replace", "Replace socket connections with the new links");
+}
+
+/* ********************** Cut Link operator ***************** */
+static int cut_links_intersect(bNodeLink *link, float mcoords[][2], int tot)
+{
+ float coord_array[NODE_LINK_RESOL + 1][2];
+ int i, b;
+
+ if (node_link_bezier_points(NULL, NULL, link, coord_array, NODE_LINK_RESOL)) {
+
+ for (i = 0; i < tot - 1; i++)
+ for (b = 0; b < NODE_LINK_RESOL; b++)
+ if (isect_line_line_v2(mcoords[i], mcoords[i + 1], coord_array[b], coord_array[b + 1]) > 0)
+ return 1;
+ }
+ return 0;
+}
+
+static int cut_links_exec(bContext *C, wmOperator *op)
+{
+ SpaceNode *snode = CTX_wm_space_node(C);
+ ARegion *ar = CTX_wm_region(C);
+ float mcoords[256][2];
+ int i = 0;
+
+ RNA_BEGIN(op->ptr, itemptr, "path")
+ {
+ float loc[2];
+
+ RNA_float_get_array(&itemptr, "loc", loc);
+ UI_view2d_region_to_view(&ar->v2d, (int)loc[0], (int)loc[1],
+ &mcoords[i][0], &mcoords[i][1]);
+ i++;
+ if (i >= 256) break;
+ }
+ RNA_END;
+
+ if (i > 1) {
+ bNodeLink *link, *next;
+
+ ED_preview_kill_jobs(C);
+
+ for (link = snode->edittree->links.first; link; link = next) {
+ next = link->next;
+
+ if (cut_links_intersect(link, mcoords, i)) {
+ snode_update(snode, link->tonode);
+ nodeRemLink(snode->edittree, link);
+ }
+ }
+
+ ntreeUpdateTree(snode->edittree);
+ snode_notify(C, snode);
+ snode_dag_update(C, snode);
+
+ return OPERATOR_FINISHED;
+ }
+
+ return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH;
+}
+
+void NODE_OT_links_cut(wmOperatorType *ot)
+{
+ PropertyRNA *prop;
+
+ ot->name = "Cut links";
+ ot->idname = "NODE_OT_links_cut";
+ ot->description = "Use the mouse to cut (remove) some links";
+
+ ot->invoke = WM_gesture_lines_invoke;
+ ot->modal = WM_gesture_lines_modal;
+ ot->exec = cut_links_exec;
+ ot->cancel = WM_gesture_lines_cancel;
+
+ ot->poll = ED_operator_node_active;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ prop = RNA_def_property(ot->srna, "path", PROP_COLLECTION, PROP_NONE);
+ RNA_def_property_struct_runtime(prop, &RNA_OperatorMousePath);
+ /* internal */
+ RNA_def_int(ot->srna, "cursor", BC_KNIFECURSOR, 0, INT_MAX, "Cursor", "", 0, INT_MAX);
+}
+
+/* ********************** Detach links operator ***************** */
+
+static int detach_links_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ SpaceNode *snode = CTX_wm_space_node(C);
+ bNodeTree *ntree = snode->edittree;
+ bNode *node;
+
+ ED_preview_kill_jobs(C);
+
+ for (node = ntree->nodes.first; node; node = node->next) {
+ if (node->flag & SELECT) {
+ nodeInternalRelink(ntree, node);
+ }
+ }
+
+ ntreeUpdateTree(ntree);
+
+ snode_notify(C, snode);
+ snode_dag_update(C, snode);
+
+ return OPERATOR_FINISHED;
+}
+
+void NODE_OT_links_detach(wmOperatorType *ot)
+{
+ ot->name = "Detach Links";
+ ot->idname = "NODE_OT_links_detach";
+ ot->description = "Remove all links to selected nodes, and try to connect neighbor nodes together";
+
+ ot->exec = detach_links_exec;
+ ot->poll = ED_operator_node_active;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+}
+
+
+/* ****************** Show Cyclic Dependencies Operator ******************* */
+
+static int node_show_cycles_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ SpaceNode *snode = CTX_wm_space_node(C);
+
+ /* this is just a wrapper around this call... */
+ ntreeUpdateTree(snode->nodetree);
+ snode_notify(C, snode);
+
+ return OPERATOR_FINISHED;
+}
+
+void NODE_OT_show_cyclic_dependencies(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Show Cyclic Dependencies";
+ ot->description = "Sort the nodes and show the cyclic dependencies between the nodes";
+ ot->idname = "NODE_OT_show_cyclic_dependencies";
+
+ /* callbacks */
+ ot->exec = node_show_cycles_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 = "Attach 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 = "Attach 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 = "Detach 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;
+}
+
+/* ********************* automatic node insert on dragging ******************* */
+
+
+/* prevent duplicate testing code below */
+static SpaceNode *ed_node_link_conditions(ScrArea *sa, bNode **select)
+{
+ SpaceNode *snode = sa ? sa->spacedata.first : NULL;
+ bNode *node;
+ bNodeLink *link;
+
+ /* no unlucky accidents */
+ if (sa == NULL || sa->spacetype != SPACE_NODE) return NULL;
+
+ *select = NULL;
+
+ for (node = snode->edittree->nodes.first; node; node = node->next) {
+ if (node->flag & SELECT) {
+ if (*select)
+ break;
+ else
+ *select = node;
+ }
+ }
+ /* only one selected */
+ if (node || *select == NULL) return NULL;
+
+ /* correct node */
+ if ((*select)->inputs.first == NULL || (*select)->outputs.first == NULL) return NULL;
+
+ /* test node for links */
+ for (link = snode->edittree->links.first; link; link = link->next) {
+ if (link->tonode == *select || link->fromnode == *select)
+ return NULL;
+ }
+
+ return snode;
+}
+
+/* test == 0, clear all intersect flags */
+void ED_node_link_intersect_test(ScrArea *sa, int test)
+{
+ bNode *select;
+ SpaceNode *snode = ed_node_link_conditions(sa, &select);
+ bNodeLink *link, *selink = NULL;
+ float mcoords[6][2];
+
+ if (snode == NULL) return;
+
+ /* clear flags */
+ for (link = snode->edittree->links.first; link; link = link->next)
+ link->flag &= ~NODE_LINKFLAG_HILITE;
+
+ if (test == 0) return;
+
+ /* okay, there's 1 node, without links, now intersect */
+ mcoords[0][0] = select->totr.xmin;
+ mcoords[0][1] = select->totr.ymin;
+ mcoords[1][0] = select->totr.xmax;
+ mcoords[1][1] = select->totr.ymin;
+ mcoords[2][0] = select->totr.xmax;
+ mcoords[2][1] = select->totr.ymax;
+ mcoords[3][0] = select->totr.xmin;
+ mcoords[3][1] = select->totr.ymax;
+ mcoords[4][0] = select->totr.xmin;
+ mcoords[4][1] = select->totr.ymin;
+ mcoords[5][0] = select->totr.xmax;
+ mcoords[5][1] = select->totr.ymax;
+
+ /* we only tag a single link for intersect now */
+ /* idea; use header dist when more? */
+ for (link = snode->edittree->links.first; link; link = link->next) {
+
+ if (cut_links_intersect(link, mcoords, 5)) { /* intersect code wants edges */
+ if (selink)
+ break;
+ selink = link;
+ }
+ }
+
+ if (link == NULL && selink)
+ selink->flag |= NODE_LINKFLAG_HILITE;
+}
+
+/* assumes sockets in list */
+static bNodeSocket *socket_best_match(ListBase *sockets)
+{
+ bNodeSocket *sock;
+ int type, maxtype = 0;
+
+ /* find type range */
+ for (sock = sockets->first; sock; sock = sock->next)
+ maxtype = MAX2(sock->type, maxtype);
+
+ /* try all types, starting from 'highest' (i.e. colors, vectors, values) */
+ for (type = maxtype; type >= 0; --type) {
+ for (sock = sockets->first; sock; sock = sock->next) {
+ if (!nodeSocketIsHidden(sock) && type == sock->type) {
+ return sock;
+ }
+ }
+ }
+
+ /* no visible sockets, unhide first of highest type */
+ for (type = maxtype; type >= 0; --type) {
+ for (sock = sockets->first; sock; sock = sock->next) {
+ if (type == sock->type) {
+ sock->flag &= ~SOCK_HIDDEN;
+ return sock;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+/* assumes link with NODE_LINKFLAG_HILITE set */
+void ED_node_link_insert(ScrArea *sa)
+{
+ bNode *node, *select;
+ SpaceNode *snode = ed_node_link_conditions(sa, &select);
+ bNodeLink *link;
+ bNodeSocket *sockto;
+
+ if (snode == NULL) return;
+
+ /* get the link */
+ for (link = snode->edittree->links.first; link; link = link->next)
+ if (link->flag & NODE_LINKFLAG_HILITE)
+ break;
+
+ if (link) {
+ node = link->tonode;
+ sockto = link->tosock;
+
+ link->tonode = select;
+ link->tosock = socket_best_match(&select->inputs);
+ link->flag &= ~NODE_LINKFLAG_HILITE;
+
+ nodeAddLink(snode->edittree, select, socket_best_match(&select->outputs), node, sockto);
+ ntreeUpdateTree(snode->edittree); /* needed for pointers */
+ snode_update(snode, select);
+ ED_node_changed_update(snode->id, select);
+ }
+}