diff options
-rw-r--r-- | release/scripts/startup/bl_ui/space_node.py | 4 | ||||
-rw-r--r-- | source/blender/blenkernel/BKE_node.h | 7 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/node.c | 109 | ||||
-rw-r--r-- | source/blender/editors/space_node/node_edit.c | 153 | ||||
-rw-r--r-- | source/blender/editors/space_node/node_intern.h | 4 | ||||
-rw-r--r-- | source/blender/editors/space_node/node_ops.c | 6 |
6 files changed, 262 insertions, 21 deletions
diff --git a/release/scripts/startup/bl_ui/space_node.py b/release/scripts/startup/bl_ui/space_node.py index e66d8f70c81..adcf5a9e9df 100644 --- a/release/scripts/startup/bl_ui/space_node.py +++ b/release/scripts/startup/bl_ui/space_node.py @@ -94,6 +94,10 @@ class NODE_HT_header(Header): if toolsettings.snap_node_element != 'INCREMENT': row.prop(toolsettings, "snap_target", text="") + row = layout.row(align=True) + row.operator("node.clipboard_copy", text="", icon='COPYDOWN') + row.operator("node.clipboard_paste", text="", icon='PASTEDOWN') + layout.template_running_jobs() diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index 93b5e748987..00bdbbf4537 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -384,6 +384,13 @@ void nodeFreePreview(struct bNode *node); int nodeSocketIsHidden(struct bNodeSocket *sock); void nodeSocketSetType(struct bNodeSocket *sock, int type); +/* Node Clipboard */ +void nodeClipboardClear(void); +void nodeClipboardAddNode(struct bNode *node); +void nodeClipboardAddLink(struct bNodeLink *link); +const struct ListBase *nodeClipboardGetNodes(void); +const struct ListBase *nodeClipboardGetLinks(void); + /* ************** NODE TYPE ACCESS *************** */ struct bNodeTemplate nodeMakeTemplate(struct bNode *node); diff --git a/source/blender/blenkernel/intern/node.c b/source/blender/blenkernel/intern/node.c index 592bc10424f..565e2de79f8 100644 --- a/source/blender/blenkernel/intern/node.c +++ b/source/blender/blenkernel/intern/node.c @@ -347,9 +347,12 @@ bNode *nodeCopyNode(struct bNodeTree *ntree, struct bNode *node) bNodeSocket *sock, *oldsock; *nnode = *node; - nodeUniqueName(ntree, nnode); - - BLI_addtail(&ntree->nodes, nnode); + /* can be called for nodes outside a node tree (e.g. clipboard) */ + if (ntree) { + nodeUniqueName(ntree, nnode); + + BLI_addtail(&ntree->nodes, nnode); + } BLI_duplicatelist(&nnode->inputs, &node->inputs); oldsock = node->inputs.first; @@ -390,7 +393,8 @@ bNode *nodeCopyNode(struct bNodeTree *ntree, struct bNode *node) nnode->new_node = NULL; nnode->preview = NULL; - ntree->update |= NTREE_UPDATE_NODES; + if (ntree) + ntree->update |= NTREE_UPDATE_NODES; return nnode; } @@ -417,7 +421,7 @@ bNodeLink *nodeAddLink(bNodeTree *ntree, bNode *fromnode, bNodeSocket *fromsock, from = -1; /* OK but flip */ } } - else { + else if (ntree) { /* check tree sockets */ for (sock = ntree->inputs.first; sock; sock = sock->next) if (sock == fromsock) @@ -446,7 +450,7 @@ bNodeLink *nodeAddLink(bNodeTree *ntree, bNode *fromnode, bNodeSocket *fromsock, to = -1; /* OK but flip */ } } - else { + else if (ntree) { /* check tree sockets */ for (sock = ntree->outputs.first; sock; sock = sock->next) if (sock == tosock) @@ -464,7 +468,8 @@ bNodeLink *nodeAddLink(bNodeTree *ntree, bNode *fromnode, bNodeSocket *fromsock, if (from >= 0 && to >= 0) { link = MEM_callocN(sizeof(bNodeLink), "link"); - BLI_addtail(&ntree->links, link); + if (ntree) + BLI_addtail(&ntree->links, link); link->fromnode = fromnode; link->fromsock = fromsock; link->tonode = tonode; @@ -472,26 +477,32 @@ bNodeLink *nodeAddLink(bNodeTree *ntree, bNode *fromnode, bNodeSocket *fromsock, } else if (from <= 0 && to <= 0) { link = MEM_callocN(sizeof(bNodeLink), "link"); - BLI_addtail(&ntree->links, link); + if (ntree) + BLI_addtail(&ntree->links, link); link->fromnode = tonode; link->fromsock = tosock; link->tonode = fromnode; link->tosock = fromsock; } - ntree->update |= NTREE_UPDATE_LINKS; + if (ntree) + ntree->update |= NTREE_UPDATE_LINKS; return link; } void nodeRemLink(bNodeTree *ntree, bNodeLink *link) { - BLI_remlink(&ntree->links, link); + /* can be called for links outside a node tree (e.g. clipboard) */ + if (ntree) + BLI_remlink(&ntree->links, link); + if (link->tosock) link->tosock->link = NULL; MEM_freeN(link); - ntree->update |= NTREE_UPDATE_LINKS; + if (ntree) + ntree->update |= NTREE_UPDATE_LINKS; } void nodeRemSocketLinks(bNodeTree *ntree, bNodeSocket *sock) @@ -885,20 +896,24 @@ static void node_unlink_attached(bNodeTree *ntree, bNode *parent) void nodeFreeNode(bNodeTree *ntree, bNode *node) { - bNodeTreeType *treetype = ntreeGetType(ntree->type); bNodeSocket *sock, *nextsock; - /* remove all references to this node */ - nodeUnlinkNode(ntree, node); - node_unlink_attached(ntree, node); - - BLI_remlink(&ntree->nodes, node); + /* can be called for nodes outside a node tree (e.g. clipboard) */ + if (ntree) { + bNodeTreeType *treetype = ntreeGetType(ntree->type); + + /* remove all references to this node */ + nodeUnlinkNode(ntree, node); + node_unlink_attached(ntree, node); + + BLI_remlink(&ntree->nodes, node); + + if (treetype->free_node_cache) + treetype->free_node_cache(ntree, node); + } /* since it is called while free database, node->id is undefined */ - if (treetype->free_node_cache) - treetype->free_node_cache(ntree, node); - if (node->typeinfo && node->typeinfo->freestoragefunc) node->typeinfo->freestoragefunc(node); @@ -917,7 +932,8 @@ void nodeFreeNode(bNodeTree *ntree, bNode *node) MEM_freeN(node); - ntree->update |= NTREE_UPDATE_NODES; + if (ntree) + ntree->update |= NTREE_UPDATE_NODES; } /* do not free ntree itself here, BKE_libblock_free calls this function too */ @@ -1388,6 +1404,53 @@ void nodeSocketSetType(bNodeSocket *sock, int type) node_socket_free_default_value(old_type, old_default_value); } +/* ************** Node Clipboard *********** */ + +typedef struct bNodeClipboard { + ListBase nodes; + ListBase links; +} bNodeClipboard; + +bNodeClipboard node_clipboard; + +void nodeClipboardClear(void) +{ + bNode *node, *node_next; + bNodeLink *link, *link_next; + + for (link = node_clipboard.links.first; link; link=link_next) { + link_next = link->next; + nodeRemLink(NULL, link); + } + node_clipboard.links.first = node_clipboard.links.last = NULL; + + for (node = node_clipboard.nodes.first; node; node=node_next) { + node_next = node->next; + nodeFreeNode(NULL, node); + } + node_clipboard.nodes.first = node_clipboard.nodes.last = NULL; +} + +void nodeClipboardAddNode(bNode *node) +{ + BLI_addtail(&node_clipboard.nodes, node); +} + +void nodeClipboardAddLink(bNodeLink *link) +{ + BLI_addtail(&node_clipboard.links, link); +} + +const ListBase *nodeClipboardGetNodes(void) +{ + return &node_clipboard.nodes; +} + +const ListBase *nodeClipboardGetLinks(void) +{ + return &node_clipboard.links; +} + /* ************** dependency stuff *********** */ /* node is guaranteed to be not checked before */ @@ -2085,6 +2148,10 @@ static void free_typeinfos(ListBase *list) void init_nodesystem(void) { + /* init clipboard */ + node_clipboard.nodes.first = node_clipboard.nodes.last = NULL; + node_clipboard.links.first = node_clipboard.links.last = NULL; + registerCompositNodes(ntreeGetType(NTREE_COMPOSIT)); registerShaderNodes(ntreeGetType(NTREE_SHADER)); registerTextureNodes(ntreeGetType(NTREE_TEXTURE)); diff --git a/source/blender/editors/space_node/node_edit.c b/source/blender/editors/space_node/node_edit.c index 4b554185933..fa8fa13a645 100644 --- a/source/blender/editors/space_node/node_edit.c +++ b/source/blender/editors/space_node/node_edit.c @@ -2215,3 +2215,156 @@ void NODE_OT_node_copy_color(wmOperatorType *ot) /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } + +/* ****************** Copy to clipboard ******************* */ + +static int node_clipboard_copy_exec(bContext *C, wmOperator *UNUSED(op)) +{ + SpaceNode *snode = CTX_wm_space_node(C); + bNodeTree *ntree = snode->edittree; + bNode *node, *newnode; + bNodeLink *link, *newlink; + + ED_preview_kill_jobs(C); + + /* clear current clipboard */ + nodeClipboardClear(); + + for (node = ntree->nodes.first; node; node = node->next) { + if (node->flag & SELECT) { + newnode = nodeCopyNode(NULL, node); + nodeClipboardAddNode(newnode); + } + } + + /* copy links between selected nodes + * NB: this depends on correct node->new_node and sock->new_sock pointers from above copy! + */ + for (link = ntree->links.first; link; link = link->next) { + /* This creates new links between copied nodes. */ + if (link->tonode && (link->tonode->flag & NODE_SELECT) && + link->fromnode && (link->fromnode->flag & NODE_SELECT)) + { + newlink = MEM_callocN(sizeof(bNodeLink), "bNodeLink"); + newlink->flag = link->flag; + newlink->tonode = link->tonode->new_node; + newlink->tosock = link->tosock->new_sock; + newlink->fromnode = link->fromnode->new_node; + newlink->fromsock = link->fromsock->new_sock; + + nodeClipboardAddLink(newlink); + } + } + + /* reparent copied nodes */ + for (node = ntree->nodes.first; node; node = node->next) { + if ((node->flag & SELECT) && node->parent) { + if (node->parent->flag & SELECT) + node->parent = node->parent->new_node; + else + node->parent = NULL; + } + } + + return OPERATOR_FINISHED; +} + +void NODE_OT_clipboard_copy(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Copy to clipboard"; + ot->description = "Copies selected nodes to the clipboard"; + ot->idname = "NODE_OT_clipboard_copy"; + + /* api callbacks */ + ot->exec = node_clipboard_copy_exec; + ot->poll = ED_operator_node_active; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +/* ****************** Paste from clipboard ******************* */ + +static int node_clipboard_paste_exec(bContext *C, wmOperator *UNUSED(op)) +{ + SpaceNode *snode = CTX_wm_space_node(C); + bNodeTree *ntree = snode->edittree; + bNode *node; + bNodeLink *link; + int num_nodes; + float centerx, centery; + + ED_preview_kill_jobs(C); + + /* deselect old nodes */ + node_deselect_all(snode); + + /* calculate "barycenter" for placing on mouse cursor */ + num_nodes = 0; + centerx = centery = 0.0f; + for (node = nodeClipboardGetNodes()->first; node; node = node->next) { + ++num_nodes; + centerx += node->locx + 0.5f * node->width; + centery += node->locy - 0.5f * node->height; + } + centerx /= num_nodes; + centery /= num_nodes; + + /* copy nodes from clipboard */ + for (node = nodeClipboardGetNodes()->first; node; node = node->next) { + bNode *newnode = nodeCopyNode(ntree, node); + + /* pasted nodes are selected */ + node_select(newnode); + + /* place nodes around the mouse cursor */ + newnode->locx += snode->mx - centerx; + newnode->locy += snode->my - centery; + } + + for (link = nodeClipboardGetLinks()->first; link; link = link->next) { + nodeAddLink(ntree, link->fromnode->new_node, link->fromsock->new_sock, + link->tonode->new_node, link->tosock->new_sock); + } + + /* reparent copied nodes */ + for (node = nodeClipboardGetNodes()->first; node; node = node->next) { + if (node->new_node->parent) + node->new_node->parent = node->new_node->parent->new_node; + } + + ntreeUpdateTree(snode->edittree); + + snode_notify(C, snode); + snode_dag_update(C, snode); + + return OPERATOR_FINISHED; +} + +static int node_clipboard_paste_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_clipboard_paste_exec(C, op); +} + +void NODE_OT_clipboard_paste(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Paste from clipboard"; + ot->description = "Pastes nodes from the clipboard to the active node tree"; + ot->idname = "NODE_OT_clipboard_paste"; + + /* api callbacks */ + ot->exec = node_clipboard_paste_exec; + ot->invoke = node_clipboard_paste_invoke; + ot->poll = ED_operator_node_active; + + /* flags */ + ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO; +} diff --git a/source/blender/editors/space_node/node_intern.h b/source/blender/editors/space_node/node_intern.h index 5c6a91195a6..7077229fae1 100644 --- a/source/blender/editors/space_node/node_intern.h +++ b/source/blender/editors/space_node/node_intern.h @@ -200,6 +200,10 @@ void NODE_OT_output_file_add_socket(struct wmOperatorType *ot); void NODE_OT_output_file_remove_active_socket(struct wmOperatorType *ot); void NODE_OT_output_file_move_active_socket(struct wmOperatorType *ot); +/* Note: clipboard_cut is a simple macro of copy + delete */ +void NODE_OT_clipboard_copy(struct wmOperatorType *ot); +void NODE_OT_clipboard_paste(struct wmOperatorType *ot); + extern const char *node_context_dir[]; // XXXXXX diff --git a/source/blender/editors/space_node/node_ops.c b/source/blender/editors/space_node/node_ops.c index 0ce72848c56..2efeebbfc75 100644 --- a/source/blender/editors/space_node/node_ops.c +++ b/source/blender/editors/space_node/node_ops.c @@ -113,6 +113,9 @@ void node_operatortypes(void) WM_operatortype_append(NODE_OT_join); WM_operatortype_append(NODE_OT_attach); WM_operatortype_append(NODE_OT_detach); + + WM_operatortype_append(NODE_OT_clipboard_copy); + WM_operatortype_append(NODE_OT_clipboard_paste); } void ED_operatormacros_node(void) @@ -281,5 +284,8 @@ void node_keymap(struct wmKeyConfig *keyconf) WM_keymap_add_item(keymap, "NODE_OT_read_fullsamplelayers", RKEY, KM_PRESS, KM_SHIFT, 0); WM_keymap_add_item(keymap, "NODE_OT_render_changed", ZKEY, KM_PRESS, 0, 0); + WM_keymap_add_item(keymap, "NODE_OT_clipboard_copy", CKEY, KM_PRESS, KM_CTRL, 0); + WM_keymap_add_item(keymap, "NODE_OT_clipboard_paste", VKEY, KM_PRESS, KM_CTRL, 0); + transform_keymap_for_space(keyconf, keymap, SPACE_NODE); } |