mirror of
https://github.com/zadam/trilium.git
synced 2025-03-01 14:22:32 +01:00
multi-select in note tree and clipboard operations on the selection
This commit is contained in:
parent
99b163a042
commit
274bb32696
@ -3,53 +3,68 @@
|
|||||||
const contextMenu = (function() {
|
const contextMenu = (function() {
|
||||||
const treeEl = $("#tree");
|
const treeEl = $("#tree");
|
||||||
|
|
||||||
let clipboardId = null;
|
let clipboardIds = [];
|
||||||
let clipboardMode = null;
|
let clipboardMode = null;
|
||||||
|
|
||||||
function pasteAfter(node) {
|
function pasteAfter(node) {
|
||||||
if (clipboardMode === 'cut') {
|
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') {
|
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
|
// just do nothing
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
throwError("Unrecognized clipboard mode=" + clipboardMode);
|
throwError("Unrecognized clipboard mode=" + clipboardMode);
|
||||||
}
|
}
|
||||||
|
|
||||||
clipboardId = null;
|
|
||||||
clipboardMode = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function pasteInto(node) {
|
function pasteInto(node) {
|
||||||
if (clipboardMode === 'cut') {
|
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') {
|
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 {
|
else {
|
||||||
throwError("Unrecognized clipboard mode=" + mode);
|
throwError("Unrecognized clipboard mode=" + mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
clipboardId = null;
|
|
||||||
clipboardMode = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function copy(node) {
|
function copy(nodes) {
|
||||||
clipboardId = node.data.note_id;
|
clipboardIds = nodes.map(node => node.data.note_id);
|
||||||
clipboardMode = 'copy';
|
clipboardMode = 'copy';
|
||||||
}
|
}
|
||||||
|
|
||||||
function cut(node) {
|
function cut(nodes) {
|
||||||
clipboardId = node.key;
|
clipboardIds = nodes.map(node => node.key);
|
||||||
clipboardMode = 'cut';
|
clipboardMode = 'cut';
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -77,8 +92,8 @@ const contextMenu = (function() {
|
|||||||
beforeOpen: (event, ui) => {
|
beforeOpen: (event, ui) => {
|
||||||
const node = $.ui.fancytree.getNode(ui.target);
|
const node = $.ui.fancytree.getNode(ui.target);
|
||||||
// Modify menu entries depending on node status
|
// Modify menu entries depending on node status
|
||||||
treeEl.contextmenu("enableEntry", "pasteAfter", clipboardId !== null);
|
treeEl.contextmenu("enableEntry", "pasteAfter", clipboardIds.length > 0);
|
||||||
treeEl.contextmenu("enableEntry", "pasteInto", clipboardId !== null);
|
treeEl.contextmenu("enableEntry", "pasteInto", clipboardIds.length > 0);
|
||||||
|
|
||||||
// Activate node on right-click
|
// Activate node on right-click
|
||||||
node.setActive();
|
node.setActive();
|
||||||
@ -109,10 +124,10 @@ const contextMenu = (function() {
|
|||||||
protected_session.protectSubTree(node.data.note_id, false);
|
protected_session.protectSubTree(node.data.note_id, false);
|
||||||
}
|
}
|
||||||
else if (ui.cmd === "copy") {
|
else if (ui.cmd === "copy") {
|
||||||
copy(node);
|
copy(noteTree.getSelectedNodes());
|
||||||
}
|
}
|
||||||
else if (ui.cmd === "cut") {
|
else if (ui.cmd === "cut") {
|
||||||
cut(node);
|
cut(noteTree.getSelectedNodes());
|
||||||
}
|
}
|
||||||
else if (ui.cmd === "pasteAfter") {
|
else if (ui.cmd === "pasteAfter") {
|
||||||
pasteAfter(node);
|
pasteAfter(node);
|
||||||
|
@ -382,6 +382,18 @@ const noteTree = (function() {
|
|||||||
recentNotes.addRecentNote(currentNoteTreeId, currentNotePath);
|
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) {
|
function initFancyTree(noteTree) {
|
||||||
assertArguments(noteTree);
|
assertArguments(noteTree);
|
||||||
|
|
||||||
@ -389,53 +401,75 @@ const noteTree = (function() {
|
|||||||
"del": node => {
|
"del": node => {
|
||||||
treeChanges.deleteNode(node);
|
treeChanges.deleteNode(node);
|
||||||
},
|
},
|
||||||
"shift+up": node => {
|
"ctrl+up": node => {
|
||||||
const beforeNode = node.getPrevSibling();
|
const beforeNode = node.getPrevSibling();
|
||||||
|
|
||||||
if (beforeNode !== null) {
|
if (beforeNode !== null) {
|
||||||
treeChanges.moveBeforeNode(node, beforeNode);
|
treeChanges.moveBeforeNode(node, beforeNode);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"shift+down": node => {
|
"ctrl+down": node => {
|
||||||
let afterNode = node.getNextSibling();
|
let afterNode = node.getNextSibling();
|
||||||
if (afterNode !== null) {
|
if (afterNode !== null) {
|
||||||
treeChanges.moveAfterNode(node, afterNode);
|
treeChanges.moveAfterNode(node, afterNode);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"shift+left": node => {
|
"ctrl+left": node => {
|
||||||
treeChanges.moveNodeUpInHierarchy(node);
|
treeChanges.moveNodeUpInHierarchy(node);
|
||||||
},
|
},
|
||||||
"shift+right": node => {
|
"ctrl+right": node => {
|
||||||
let toNode = node.getPrevSibling();
|
let toNode = node.getPrevSibling();
|
||||||
|
|
||||||
if (toNode !== null) {
|
if (toNode !== null) {
|
||||||
treeChanges.moveToNode(node, toNode);
|
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 => {
|
"f2": node => {
|
||||||
editTreePrefix.showDialog(node);
|
editTreePrefix.showDialog(node);
|
||||||
},
|
},
|
||||||
"alt+-": node => {
|
"alt+-": node => {
|
||||||
collapseTree(node);
|
collapseTree(node);
|
||||||
},
|
},
|
||||||
"ctrl+c": node => {
|
"ctrl+c": () => {
|
||||||
contextMenu.copy(node);
|
contextMenu.copy(getSelectedNodes());
|
||||||
|
|
||||||
showMessage("Note copied into clipboard.");
|
showMessage("Note(s) copied into clipboard.");
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
"ctrl+x": node => {
|
"ctrl+x": () => {
|
||||||
contextMenu.cut(node);
|
contextMenu.cut(getSelectedNodes());
|
||||||
|
|
||||||
showMessage("Note cut into clipboard.");
|
showMessage("Note(s) cut into clipboard.");
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
"ctrl+v": node => {
|
"ctrl+v": node => {
|
||||||
contextMenu.pasteInto(node);
|
contextMenu.pasteInto(node);
|
||||||
|
|
||||||
showMessage("Note pasted from clipboard into current note.");
|
showMessage("Note(s) pasted from clipboard into current note.");
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
@ -449,22 +483,22 @@ const noteTree = (function() {
|
|||||||
// after opening context menu, standard shortcuts don't work, but they are detected here
|
// after opening context menu, standard shortcuts don't work, but they are detected here
|
||||||
// so we essentially takeover the standard handling with our implementation.
|
// so we essentially takeover the standard handling with our implementation.
|
||||||
"left": node => {
|
"left": node => {
|
||||||
node.navigate($.ui.keyCode.LEFT, true);
|
node.navigate($.ui.keyCode.LEFT, true).then(() => clearSelectedNodes());
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
"right": node => {
|
"right": node => {
|
||||||
node.navigate($.ui.keyCode.RIGHT, true);
|
node.navigate($.ui.keyCode.RIGHT, true).then(() => clearSelectedNodes());
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
"up": node => {
|
"up": node => {
|
||||||
node.navigate($.ui.keyCode.UP, true);
|
node.navigate($.ui.keyCode.UP, true).then(() => clearSelectedNodes());
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
"down": node => {
|
"down": node => {
|
||||||
node.navigate($.ui.keyCode.DOWN, true);
|
node.navigate($.ui.keyCode.DOWN, true).then(() => clearSelectedNodes());
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -476,6 +510,22 @@ const noteTree = (function() {
|
|||||||
extensions: ["hotkeys", "filter", "dnd", "clones"],
|
extensions: ["hotkeys", "filter", "dnd", "clones"],
|
||||||
source: noteTree,
|
source: noteTree,
|
||||||
scrollParent: $("#tree"),
|
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) => {
|
activate: (event, data) => {
|
||||||
const node = data.node.data;
|
const node = data.node.data;
|
||||||
|
|
||||||
@ -769,6 +819,7 @@ const noteTree = (function() {
|
|||||||
setPrefix,
|
setPrefix,
|
||||||
getNotePathTitle,
|
getNotePathTitle,
|
||||||
removeParentChildRelation,
|
removeParentChildRelation,
|
||||||
setParentChildRelation
|
setParentChildRelation,
|
||||||
|
getSelectedNodes
|
||||||
};
|
};
|
||||||
})();
|
})();
|
Loading…
x
Reference in New Issue
Block a user