From 689c6133ea7f659fc61679c83ca216338533f98d Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Wed, 1 Aug 2012 19:11:17 +0000 Subject: [PATCH] split node_edit.c into separate files (add, group, relationshops), was almost 5000 loc. --- source/blender/editors/include/ED_node.h | 6 +- .../blender/editors/space_node/CMakeLists.txt | 3 + source/blender/editors/space_node/node_add.c | 431 +++ source/blender/editors/space_node/node_edit.c | 2755 +---------------- .../blender/editors/space_node/node_group.c | 1164 +++++++ .../blender/editors/space_node/node_intern.h | 72 +- .../editors/space_node/node_relationships.c | 1446 +++++++++ 7 files changed, 3101 insertions(+), 2776 deletions(-) create mode 100644 source/blender/editors/space_node/node_add.c create mode 100644 source/blender/editors/space_node/node_group.c create mode 100644 source/blender/editors/space_node/node_relationships.c 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 +#include +#include +#include +#include + +#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 +#include +#include +#include +#include + +#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 +#include +#include +#include +#include + +#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); + } +}