2022-07-06 14:08:56 +02:00
|
|
|
RED.contextMenu = (function () {
|
2022-06-16 14:59:14 +02:00
|
|
|
|
|
|
|
let menu;
|
|
|
|
|
|
|
|
function disposeMenu() {
|
|
|
|
$(document).off("mousedown.red-ui-workspace-context-menu");
|
|
|
|
if (menu) {
|
|
|
|
menu.remove();
|
|
|
|
}
|
|
|
|
menu = null;
|
|
|
|
}
|
|
|
|
function show(options) {
|
|
|
|
if (menu) {
|
|
|
|
menu.remove()
|
|
|
|
}
|
2022-10-26 00:44:59 +02:00
|
|
|
let menuItems = []
|
|
|
|
if (options.options) {
|
|
|
|
menuItems = options.options
|
|
|
|
} else if (options.type === 'workspace') {
|
|
|
|
const selection = RED.view.selection()
|
|
|
|
const noSelection = !selection || Object.keys(selection).length === 0
|
|
|
|
const hasSelection = (selection.nodes && selection.nodes.length > 0);
|
|
|
|
const hasMultipleSelection = hasSelection && selection.nodes.length > 1;
|
|
|
|
const virtulLinks = (selection.links && selection.links.filter(e => !!e.link)) || [];
|
|
|
|
const wireLinks = (selection.links && selection.links.filter(e => !e.link)) || [];
|
|
|
|
const hasLinks = wireLinks.length > 0;
|
|
|
|
const isSingleLink = !hasSelection && hasLinks && wireLinks.length === 1
|
|
|
|
const isMultipleLinks = !hasSelection && hasLinks && wireLinks.length > 1
|
|
|
|
const canDelete = hasSelection || hasLinks
|
|
|
|
const isGroup = hasSelection && selection.nodes.length === 1 && selection.nodes[0].type === 'group'
|
2023-02-24 00:36:51 +01:00
|
|
|
const canEdit = !RED.workspaces.isLocked()
|
2022-10-26 00:44:59 +02:00
|
|
|
const canRemoveFromGroup = hasSelection && !!selection.nodes[0].g
|
2022-11-17 13:55:57 +01:00
|
|
|
const isAllGroups = hasSelection && selection.nodes.filter(n => n.type !== 'group').length === 0
|
|
|
|
const hasGroup = hasSelection && selection.nodes.filter(n => n.type === 'group' ).length > 0
|
2022-10-26 00:44:59 +02:00
|
|
|
const offset = $("#red-ui-workspace-chart").offset()
|
2022-07-01 11:31:50 +02:00
|
|
|
|
2022-10-26 00:44:59 +02:00
|
|
|
let addX = options.x - offset.left + $("#red-ui-workspace-chart").scrollLeft()
|
|
|
|
let addY = options.y - offset.top + $("#red-ui-workspace-chart").scrollTop()
|
2022-06-16 14:59:14 +02:00
|
|
|
|
2022-10-26 00:44:59 +02:00
|
|
|
if (RED.view.snapGrid) {
|
|
|
|
const gridSize = RED.view.gridSize()
|
|
|
|
addX = gridSize * Math.floor(addX / gridSize)
|
|
|
|
addY = gridSize * Math.floor(addY / gridSize)
|
|
|
|
}
|
2022-06-16 14:59:14 +02:00
|
|
|
|
2022-10-26 00:44:59 +02:00
|
|
|
menuItems.push(
|
2022-11-17 13:55:57 +01:00
|
|
|
{ onselect: 'core:show-action-list', onpostselect: function () { } }
|
|
|
|
)
|
|
|
|
|
|
|
|
const insertOptions = []
|
|
|
|
menuItems.push({ label: RED._("contextMenu.insert"), options: insertOptions })
|
|
|
|
insertOptions.push(
|
2022-10-26 00:44:59 +02:00
|
|
|
{
|
2022-11-17 13:55:57 +01:00
|
|
|
label: RED._("contextMenu.node"),
|
|
|
|
onselect: function () {
|
|
|
|
RED.view.showQuickAddDialog({
|
|
|
|
position: [addX, addY],
|
|
|
|
touchTrigger: true,
|
|
|
|
splice: isSingleLink ? selection.links[0] : undefined,
|
|
|
|
// spliceMultiple: isMultipleLinks
|
|
|
|
})
|
|
|
|
},
|
|
|
|
disabled: !canEdit
|
|
|
|
},
|
|
|
|
(hasLinks) ? { // has least 1 wire selected
|
|
|
|
label: RED._("contextMenu.junction"),
|
|
|
|
onselect: 'core:split-wires-with-junctions',
|
|
|
|
disabled: !canEdit || !hasLinks
|
|
|
|
} : {
|
|
|
|
label: RED._("contextMenu.junction"),
|
|
|
|
onselect: function () {
|
|
|
|
const nn = {
|
|
|
|
_def: { defaults: {} },
|
|
|
|
type: 'junction',
|
|
|
|
z: RED.workspaces.active(),
|
|
|
|
id: RED.nodes.id(),
|
|
|
|
x: addX,
|
|
|
|
y: addY,
|
|
|
|
w: 0, h: 0,
|
|
|
|
outputs: 1,
|
|
|
|
inputs: 1,
|
2023-02-23 23:48:08 +01:00
|
|
|
dirty: true,
|
|
|
|
moved: true
|
2022-11-17 13:55:57 +01:00
|
|
|
}
|
2023-03-02 22:13:39 +01:00
|
|
|
const junction = RED.nodes.addJunction(nn);
|
2022-11-17 13:55:57 +01:00
|
|
|
const historyEvent = {
|
|
|
|
dirty: RED.nodes.dirty(),
|
|
|
|
t: 'add',
|
2023-03-02 22:13:39 +01:00
|
|
|
junctions: [junction]
|
2022-07-01 11:31:50 +02:00
|
|
|
}
|
2022-11-17 13:55:57 +01:00
|
|
|
RED.history.push(historyEvent);
|
|
|
|
RED.nodes.dirty(true);
|
2023-03-02 22:13:39 +01:00
|
|
|
RED.view.select({nodes: [junction] });
|
2022-11-17 13:55:57 +01:00
|
|
|
RED.view.redraw(true)
|
|
|
|
},
|
|
|
|
disabled: !canEdit
|
|
|
|
},
|
|
|
|
{
|
|
|
|
label: RED._("contextMenu.linkNodes"),
|
|
|
|
onselect: 'core:split-wire-with-link-nodes',
|
|
|
|
disabled: !canEdit || !hasLinks
|
|
|
|
},
|
|
|
|
null,
|
|
|
|
{ onselect: 'core:show-import-dialog', label: RED._('common.label.import')},
|
2022-12-03 22:29:27 +01:00
|
|
|
{ onselect: 'core:show-examples-import-dialog', label: RED._('menu.label.importExample') }
|
2022-11-17 13:55:57 +01:00
|
|
|
)
|
|
|
|
if (hasSelection && canEdit) {
|
|
|
|
const nodeOptions = []
|
|
|
|
if (!hasMultipleSelection && !isGroup) {
|
|
|
|
nodeOptions.push(
|
|
|
|
{ onselect: 'core:show-node-help' },
|
|
|
|
null
|
|
|
|
)
|
|
|
|
}
|
|
|
|
nodeOptions.push(
|
|
|
|
{ onselect: 'core:enable-selected-nodes' },
|
|
|
|
{ onselect: 'core:disable-selected-nodes' },
|
|
|
|
null,
|
|
|
|
{ onselect: 'core:show-selected-node-labels' },
|
|
|
|
{ onselect: 'core:hide-selected-node-labels' }
|
|
|
|
)
|
|
|
|
menuItems.push({
|
2022-12-03 22:29:27 +01:00
|
|
|
label: RED._('sidebar.info.node'),
|
2022-11-17 13:55:57 +01:00
|
|
|
options: nodeOptions
|
|
|
|
})
|
|
|
|
menuItems.push({
|
2022-12-03 22:29:27 +01:00
|
|
|
label: RED._('sidebar.info.group'),
|
2022-11-17 13:55:57 +01:00
|
|
|
options: [
|
|
|
|
{ onselect: 'core:group-selection' },
|
|
|
|
{ onselect: 'core:ungroup-selection', disabled: !hasGroup },
|
2022-10-26 00:44:59 +02:00
|
|
|
]
|
2022-11-17 13:55:57 +01:00
|
|
|
})
|
2023-03-01 11:02:26 +01:00
|
|
|
if (hasGroup) {
|
|
|
|
menuItems[menuItems.length - 1].options.push(
|
|
|
|
{ onselect: 'core:merge-selection-to-group', label: RED._("menu.label.groupMergeSelection") }
|
|
|
|
)
|
|
|
|
|
|
|
|
}
|
2022-11-17 13:55:57 +01:00
|
|
|
if (canRemoveFromGroup) {
|
|
|
|
menuItems[menuItems.length - 1].options.push(
|
|
|
|
{ onselect: 'core:remove-selection-from-group', label: RED._("menu.label.groupRemoveSelection") }
|
|
|
|
)
|
|
|
|
}
|
2023-03-01 11:02:26 +01:00
|
|
|
menuItems[menuItems.length - 1].options.push(
|
|
|
|
null,
|
|
|
|
{ onselect: 'core:copy-group-style', disabled: !hasGroup },
|
|
|
|
{ onselect: 'core:paste-group-style', disabled: !hasGroup}
|
|
|
|
)
|
2022-11-17 13:55:57 +01:00
|
|
|
}
|
|
|
|
if (canEdit && hasMultipleSelection) {
|
|
|
|
menuItems.push({
|
2022-12-03 22:29:27 +01:00
|
|
|
label: RED._('menu.label.arrange'),
|
2022-11-17 13:55:57 +01:00
|
|
|
options: [
|
|
|
|
{ label:RED._("menu.label.alignLeft"), onselect: "core:align-selection-to-left"},
|
|
|
|
{ label:RED._("menu.label.alignCenter"), onselect: "core:align-selection-to-center"},
|
|
|
|
{ label:RED._("menu.label.alignRight"), onselect: "core:align-selection-to-right"},
|
|
|
|
null,
|
|
|
|
{ label:RED._("menu.label.alignTop"), onselect: "core:align-selection-to-top"},
|
|
|
|
{ label:RED._("menu.label.alignMiddle"), onselect: "core:align-selection-to-middle"},
|
|
|
|
{ label:RED._("menu.label.alignBottom"), onselect: "core:align-selection-to-bottom"},
|
|
|
|
null,
|
|
|
|
{ label:RED._("menu.label.distributeHorizontally"), onselect: "core:distribute-selection-horizontally"},
|
|
|
|
{ label:RED._("menu.label.distributeVertically"), onselect: "core:distribute-selection-vertically"}
|
|
|
|
]
|
|
|
|
})
|
|
|
|
}
|
2022-06-16 14:59:14 +02:00
|
|
|
|
|
|
|
|
2022-11-17 13:55:57 +01:00
|
|
|
menuItems.push(
|
2022-06-16 14:59:14 +02:00
|
|
|
null,
|
2022-10-26 00:44:59 +02:00
|
|
|
{ onselect: 'core:undo', disabled: RED.history.list().length === 0 },
|
|
|
|
{ onselect: 'core:redo', disabled: RED.history.listRedo().length === 0 },
|
|
|
|
null,
|
2022-11-01 11:48:48 +01:00
|
|
|
{ onselect: 'core:cut-selection-to-internal-clipboard', label: RED._("keyboard.cutNode"), disabled: !canEdit || !hasSelection },
|
2022-10-26 00:44:59 +02:00
|
|
|
{ onselect: 'core:copy-selection-to-internal-clipboard', label: RED._("keyboard.copyNode"), disabled: !hasSelection },
|
2022-11-01 11:48:48 +01:00
|
|
|
{ onselect: 'core:paste-from-internal-clipboard', label: RED._("keyboard.pasteNode"), disabled: !canEdit || !RED.view.clipboard() },
|
|
|
|
{ onselect: 'core:delete-selection', disabled: !canEdit || !canDelete },
|
2022-12-03 22:29:27 +01:00
|
|
|
{ onselect: 'core:delete-selection-and-reconnect', label: RED._('keyboard.deleteReconnect'), disabled: !canEdit || !canDelete },
|
2022-10-26 00:44:59 +02:00
|
|
|
{ onselect: 'core:show-export-dialog', label: RED._("menu.label.export") },
|
2022-11-01 11:48:48 +01:00
|
|
|
{ onselect: 'core:select-all-nodes' },
|
2022-06-16 14:59:14 +02:00
|
|
|
)
|
|
|
|
}
|
2022-07-07 09:47:26 +02:00
|
|
|
|
|
|
|
var direction = "right";
|
2022-07-07 12:39:28 +02:00
|
|
|
var MENU_WIDTH = 500; // can not use menu width here
|
2022-07-07 09:47:26 +02:00
|
|
|
if ((options.x -$(document).scrollLeft()) >
|
2022-07-07 12:39:28 +02:00
|
|
|
($(window).width() -MENU_WIDTH)) {
|
2022-07-07 09:47:26 +02:00
|
|
|
direction = "left";
|
|
|
|
}
|
2022-10-26 00:44:59 +02:00
|
|
|
|
2022-06-16 14:59:14 +02:00
|
|
|
menu = RED.menu.init({
|
2022-07-07 09:47:26 +02:00
|
|
|
direction: direction,
|
2022-06-16 14:59:14 +02:00
|
|
|
onpreselect: function() {
|
|
|
|
disposeMenu()
|
|
|
|
},
|
2022-07-06 14:08:56 +02:00
|
|
|
onpostselect: function () {
|
2022-06-16 14:59:14 +02:00
|
|
|
RED.view.focus()
|
|
|
|
},
|
|
|
|
options: menuItems
|
|
|
|
});
|
|
|
|
|
2022-07-06 14:08:56 +02:00
|
|
|
menu.attr("id", "red-ui-workspace-context-menu");
|
2022-06-16 14:59:14 +02:00
|
|
|
menu.css({
|
|
|
|
position: "absolute"
|
|
|
|
})
|
|
|
|
menu.appendTo("body");
|
|
|
|
|
|
|
|
// TODO: prevent the menu from overflowing the window.
|
|
|
|
|
|
|
|
var top = options.y
|
|
|
|
var left = options.x
|
|
|
|
|
2022-07-06 14:08:56 +02:00
|
|
|
if (top + menu.height() - $(document).scrollTop() > $(window).height()) {
|
|
|
|
top -= (top + menu.height()) - $(window).height() + 22;
|
2022-06-16 14:59:14 +02:00
|
|
|
}
|
2022-07-06 14:08:56 +02:00
|
|
|
if (left + menu.width() - $(document).scrollLeft() > $(window).width()) {
|
|
|
|
left -= (left + menu.width()) - $(window).width() + 18;
|
2022-06-16 14:59:14 +02:00
|
|
|
}
|
|
|
|
menu.css({
|
2022-07-06 14:08:56 +02:00
|
|
|
top: top + "px",
|
|
|
|
left: left + "px"
|
2022-06-16 14:59:14 +02:00
|
|
|
})
|
|
|
|
$(".red-ui-menu.red-ui-menu-dropdown").hide();
|
2022-07-06 14:08:56 +02:00
|
|
|
$(document).on("mousedown.red-ui-workspace-context-menu", function (evt) {
|
2022-06-16 14:59:14 +02:00
|
|
|
if (menu && menu[0].contains(evt.target)) {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
disposeMenu()
|
|
|
|
});
|
|
|
|
menu.show();
|
2022-07-06 14:05:18 +02:00
|
|
|
// set focus to first item so that pressing escape key closes the menu
|
|
|
|
$("#red-ui-workspace-context-menu :first(ul) > a").trigger("focus")
|
2022-06-16 14:59:14 +02:00
|
|
|
|
|
|
|
}
|
2022-07-06 14:05:18 +02:00
|
|
|
// Allow escape key hook and other editor events to close context menu
|
|
|
|
RED.keyboard.add("red-ui-workspace-context-menu", "escape", function () { RED.contextMenu.hide() })
|
2022-07-06 14:08:56 +02:00
|
|
|
RED.events.on("editor:open", function () { RED.contextMenu.hide() });
|
|
|
|
RED.events.on("search:open", function () { RED.contextMenu.hide() });
|
|
|
|
RED.events.on("type-search:open", function () { RED.contextMenu.hide() });
|
|
|
|
RED.events.on("actionList:open", function () { RED.contextMenu.hide() });
|
|
|
|
RED.events.on("view:selection-changed", function () { RED.contextMenu.hide() });
|
2022-06-16 14:59:14 +02:00
|
|
|
return {
|
2022-06-20 21:51:31 +02:00
|
|
|
show: show,
|
|
|
|
hide: disposeMenu
|
2022-06-16 14:59:14 +02:00
|
|
|
}
|
|
|
|
})()
|