diff --git a/public/javascripts/context_menu.js b/public/javascripts/context_menu.js index 5659376a5..6f18e7ae7 100644 --- a/public/javascripts/context_menu.js +++ b/public/javascripts/context_menu.js @@ -3,53 +3,68 @@ const contextMenu = (function() { const treeEl = $("#tree"); - let clipboardId = null; + let clipboardIds = []; let clipboardMode = null; function pasteAfter(node) { if (clipboardMode === 'cut') { - const subjectNode = treeUtils.getNodeByKey(clipboardId); + for (const nodeKey of clipboardIds) { + const subjectNode = treeUtils.getNodeByKey(nodeKey); - treeChanges.moveAfterNode(subjectNode, node); + treeChanges.moveAfterNode(subjectNode, node); + } + + clipboardIds = []; + clipboardMode = null; } else if (clipboardMode === 'copy') { - treeChanges.cloneNoteAfter(clipboardId, node.data.note_tree_id); + for (const noteId of clipboardIds) { + treeChanges.cloneNoteAfter(noteId, node.data.note_tree_id); + } + + // copy will keep clipboardIds and clipboardMode so it's possible to paste into multiple places } - else if (clipboardId === null) { + else if (clipboardIds.length === 0) { // just do nothing } else { throwError("Unrecognized clipboard mode=" + clipboardMode); } - - clipboardId = null; - clipboardMode = null; } function pasteInto(node) { if (clipboardMode === 'cut') { - const subjectNode = treeUtils.getNodeByKey(clipboardId); + for (const nodeKey of clipboardIds) { + const subjectNode = treeUtils.getNodeByKey(nodeKey); - treeChanges.moveToNode(subjectNode, node); + treeChanges.moveToNode(subjectNode, node); + } + + clipboardIds = []; + clipboardMode = null; } else if (clipboardMode === 'copy') { - treeChanges.cloneNoteTo(clipboardId, node.data.note_id); + for (const noteId of clipboardIds) { + treeChanges.cloneNoteTo(noteId, node.data.note_id); + } + + // copy will keep clipboardIds and clipboardMode so it's possible to paste into multiple places + } + else if (clipboardIds.length === 0) { + // just do nothing } else { throwError("Unrecognized clipboard mode=" + mode); } - - clipboardId = null; - clipboardMode = null; } - function copy(node) { - clipboardId = node.data.note_id; + function copy(nodes) { + clipboardIds = nodes.map(node => node.data.note_id); clipboardMode = 'copy'; } - function cut(node) { - clipboardId = node.key; + function cut(nodes) { + clipboardIds = nodes.map(node => node.key); clipboardMode = 'cut'; } @@ -77,8 +92,8 @@ const contextMenu = (function() { beforeOpen: (event, ui) => { const node = $.ui.fancytree.getNode(ui.target); // Modify menu entries depending on node status - treeEl.contextmenu("enableEntry", "pasteAfter", clipboardId !== null); - treeEl.contextmenu("enableEntry", "pasteInto", clipboardId !== null); + treeEl.contextmenu("enableEntry", "pasteAfter", clipboardIds.length > 0); + treeEl.contextmenu("enableEntry", "pasteInto", clipboardIds.length > 0); // Activate node on right-click node.setActive(); @@ -109,10 +124,10 @@ const contextMenu = (function() { protected_session.protectSubTree(node.data.note_id, false); } else if (ui.cmd === "copy") { - copy(node); + copy(noteTree.getSelectedNodes()); } else if (ui.cmd === "cut") { - cut(node); + cut(noteTree.getSelectedNodes()); } else if (ui.cmd === "pasteAfter") { pasteAfter(node); diff --git a/public/javascripts/note_tree.js b/public/javascripts/note_tree.js index d9cd89ff7..7bf997e14 100644 --- a/public/javascripts/note_tree.js +++ b/public/javascripts/note_tree.js @@ -382,6 +382,18 @@ const noteTree = (function() { recentNotes.addRecentNote(currentNoteTreeId, currentNotePath); } + function getSelectedNodes() { + return getTree().getSelectedNodes(); + } + + function clearSelectedNodes() { + for (const selectedNode of getSelectedNodes()) { + selectedNode.setSelected(false); + } + + getCurrentNode().setSelected(true); + } + function initFancyTree(noteTree) { assertArguments(noteTree); @@ -389,53 +401,75 @@ const noteTree = (function() { "del": node => { treeChanges.deleteNode(node); }, - "shift+up": node => { + "ctrl+up": node => { const beforeNode = node.getPrevSibling(); if (beforeNode !== null) { treeChanges.moveBeforeNode(node, beforeNode); } }, - "shift+down": node => { + "ctrl+down": node => { let afterNode = node.getNextSibling(); if (afterNode !== null) { treeChanges.moveAfterNode(node, afterNode); } }, - "shift+left": node => { + "ctrl+left": node => { treeChanges.moveNodeUpInHierarchy(node); }, - "shift+right": node => { + "ctrl+right": node => { let toNode = node.getPrevSibling(); if (toNode !== null) { treeChanges.moveToNode(node, toNode); } }, + "shift+up": node => { + node.navigate($.ui.keyCode.UP, true).then(() => { + const currentNode = getCurrentNode(); + + if (currentNode.isSelected()) { + node.setSelected(false); + } + + currentNode.setSelected(true); + }); + }, + "shift+down": node => { + node.navigate($.ui.keyCode.DOWN, true).then(() => { + const currentNode = getCurrentNode(); + + if (currentNode.isSelected()) { + node.setSelected(false); + } + + currentNode.setSelected(true); + }); + }, "f2": node => { editTreePrefix.showDialog(node); }, "alt+-": node => { collapseTree(node); }, - "ctrl+c": node => { - contextMenu.copy(node); + "ctrl+c": () => { + contextMenu.copy(getSelectedNodes()); - showMessage("Note copied into clipboard."); + showMessage("Note(s) copied into clipboard."); return false; }, - "ctrl+x": node => { - contextMenu.cut(node); + "ctrl+x": () => { + contextMenu.cut(getSelectedNodes()); - showMessage("Note cut into clipboard."); + showMessage("Note(s) cut into clipboard."); return false; }, "ctrl+v": node => { contextMenu.pasteInto(node); - showMessage("Note pasted from clipboard into current note."); + showMessage("Note(s) pasted from clipboard into current note."); return false; }, @@ -449,22 +483,22 @@ const noteTree = (function() { // after opening context menu, standard shortcuts don't work, but they are detected here // so we essentially takeover the standard handling with our implementation. "left": node => { - node.navigate($.ui.keyCode.LEFT, true); + node.navigate($.ui.keyCode.LEFT, true).then(() => clearSelectedNodes()); return false; }, "right": node => { - node.navigate($.ui.keyCode.RIGHT, true); + node.navigate($.ui.keyCode.RIGHT, true).then(() => clearSelectedNodes()); return false; }, "up": node => { - node.navigate($.ui.keyCode.UP, true); + node.navigate($.ui.keyCode.UP, true).then(() => clearSelectedNodes()); return false; }, "down": node => { - node.navigate($.ui.keyCode.DOWN, true); + node.navigate($.ui.keyCode.DOWN, true).then(() => clearSelectedNodes()); return false; } @@ -476,6 +510,22 @@ const noteTree = (function() { extensions: ["hotkeys", "filter", "dnd", "clones"], source: noteTree, scrollParent: $("#tree"), + click: (event, data) => { + const targetType = data.targetType; + const node = data.node; + + if (targetType === 'title' || targetType === 'icon') { + node.setActive(); + + if (!event.ctrlKey) { + clearSelectedNodes(); + } + + node.setSelected(true); + + return false; + } + }, activate: (event, data) => { const node = data.node.data; @@ -769,6 +819,7 @@ const noteTree = (function() { setPrefix, getNotePathTitle, removeParentChildRelation, - setParentChildRelation + setParentChildRelation, + getSelectedNodes }; })(); \ No newline at end of file