diff --git a/integration/eclipse/src/copy-paste/copy-paste.ts b/integration/eclipse/src/copy-paste/copy-paste.ts new file mode 100644 index 00000000..791438d6 --- /dev/null +++ b/integration/eclipse/src/copy-paste/copy-paste.ts @@ -0,0 +1,86 @@ +/******************************************************************************** + * Copyright (c) 2020-2023 EclipseSource and others. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the Eclipse + * Public License v. 2.0 are satisfied: GNU General Public License, version 2 + * with the GNU Classpath Exception which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + ********************************************************************************/ +import { + Action, + CutOperation, + EditorContextService, + IActionDispatcher, + IActionHandler, + IAsyncClipboardService, + PasteOperation, + RequestClipboardDataAction, + TYPES, + ViewerOptions +} from '@eclipse-glsp/client'; +import { inject, injectable } from 'inversify'; + +// Eclipse-specific integration: in Eclipse, we trigger the Copy/Paste actions from +// the IDE Keybindings. We don't use the browser events. This is fine, because we +// don't need browser clipboard support (We use the Eclipse System Clipboard); so +// we don't need special permission from the Browser. + +@injectable() +export class IvyEclipseCopyPasteActionHandler implements IActionHandler { + @inject(TYPES.IActionDispatcher) protected actionDispatcher: IActionDispatcher; + @inject(TYPES.ViewerOptions) protected viewerOptions: ViewerOptions; + @inject(TYPES.IAsyncClipboardService) protected clipboardService: IAsyncClipboardService; + @inject(EditorContextService) protected editorContext: EditorContextService; + + handle(action: Action): void { + switch (action.kind) { + case 'invoke-copy': + this.handleCopy(); + break; + case 'invoke-paste': + this.handlePaste(); + break; + case 'invoke-cut': + this.handleCut(); + break; + } + } + + handleCopy() { + if (this.shouldCopy()) { + this.actionDispatcher.request(RequestClipboardDataAction.create(this.editorContext.get())); + } else { + this.clipboardService.clear(); + } + } + + handleCut() { + if (this.shouldCopy()) { + this.handleCopy(); + this.actionDispatcher.dispatch(CutOperation.create(this.editorContext.get())); + } + } + + handlePaste() { + if (this.isDiagramActive()) { + // In the Eclipse Integration case, the server manages its own clipboard. + // Just pass an empty clipboard data to remain compliant with the API. + const clipboardData = {}; + this.actionDispatcher.dispatch(PasteOperation.create({ clipboardData: clipboardData, editorContext: this.editorContext.get() })); + } + } + + protected shouldCopy(): boolean { + return this.editorContext.get().selectedElementIds.length > 0 && this.isDiagramActive(); + } + protected isDiagramActive(): boolean { + return document.activeElement?.parentElement?.id === this.viewerOptions.baseDiv; + } +} diff --git a/integration/eclipse/src/copy-paste/di.config.ts b/integration/eclipse/src/copy-paste/di.config.ts new file mode 100644 index 00000000..97d65be2 --- /dev/null +++ b/integration/eclipse/src/copy-paste/di.config.ts @@ -0,0 +1,12 @@ +import { configureActionHandler } from '@eclipse-glsp/client'; +import { ContainerModule } from 'inversify'; + +import { IvyEclipseCopyPasteActionHandler } from './copy-paste'; + +const ivyEclipseCopyPasteModule = new ContainerModule((bind, _unbind, isBound) => { + configureActionHandler({ bind, isBound }, 'invoke-copy', IvyEclipseCopyPasteActionHandler); + configureActionHandler({ bind, isBound }, 'invoke-cut', IvyEclipseCopyPasteActionHandler); + configureActionHandler({ bind, isBound }, 'invoke-paste', IvyEclipseCopyPasteActionHandler); +}); + +export default ivyEclipseCopyPasteModule; diff --git a/integration/eclipse/src/delete/delete.ts b/integration/eclipse/src/delete/delete.ts new file mode 100644 index 00000000..110a0d4b --- /dev/null +++ b/integration/eclipse/src/delete/delete.ts @@ -0,0 +1,57 @@ +/******************************************************************************** + * Copyright (c) 2020-2021 EclipseSource and others. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the Eclipse + * Public License v. 2.0 are satisfied: GNU General Public License, version 2 + * with the GNU Classpath Exception which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + ********************************************************************************/ +import { + Action, + DeleteElementOperation, + EditorContextService, + IActionDispatcher, + IActionHandler, + TYPES, + ViewerOptions +} from '@eclipse-glsp/client'; +import { inject, injectable } from 'inversify'; + +export class InvokeDeleteAction implements Action { + static KIND = 'invoke-delete'; + readonly kind = InvokeDeleteAction.KIND; +} + +export function isInvokeDeleteAction(action: Action): action is InvokeDeleteAction { + return action.kind === InvokeDeleteAction.KIND; +} + +@injectable() +export class IvyInvokeDeleteActionHandler implements IActionHandler { + @inject(TYPES.IActionDispatcher) protected actionDispatcher: IActionDispatcher; + @inject(EditorContextService) protected editorContext: EditorContextService; + @inject(TYPES.ViewerOptions) protected viewerOptions: ViewerOptions; + + handle(action: Action): void { + if (isInvokeDeleteAction(action)) { + this.handleDelete(); + } + } + + handleDelete(): void { + if (this.isDiagramActive()) { + this.actionDispatcher.dispatch(DeleteElementOperation.create(this.editorContext.get().selectedElementIds)); + } + } + + protected isDiagramActive(): boolean { + return document.activeElement?.parentElement?.id === this.viewerOptions.baseDiv; + } +} diff --git a/integration/eclipse/src/delete/di.config.ts b/integration/eclipse/src/delete/di.config.ts new file mode 100644 index 00000000..7c92b302 --- /dev/null +++ b/integration/eclipse/src/delete/di.config.ts @@ -0,0 +1,25 @@ +/******************************************************************************** + * Copyright (c) 2020 EclipseSource and others. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the Eclipse + * Public License v. 2.0 are satisfied: GNU General Public License, version 2 + * with the GNU Classpath Exception which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + ********************************************************************************/ +import { configureActionHandler } from '@eclipse-glsp/client'; +import { ContainerModule } from 'inversify'; + +import { IvyInvokeDeleteActionHandler } from './delete'; + +const ivyEclipseDeleteModule = new ContainerModule((bind, _unbind, isBound) => { + configureActionHandler({ bind, isBound }, 'invoke-delete', IvyInvokeDeleteActionHandler); +}); + +export default ivyEclipseDeleteModule; diff --git a/integration/eclipse/src/di.config.ts b/integration/eclipse/src/di.config.ts index 45990df8..15ca43db 100644 --- a/integration/eclipse/src/di.config.ts +++ b/integration/eclipse/src/di.config.ts @@ -14,8 +14,9 @@ import { ConsoleLogger, LogLevel, TYPES } from '@eclipse-glsp/client'; import ivyOpenQuickOutlineModule from './open-quick-outline/di.config'; import ivyToolBarModule from './tool-bar/di.config'; import { IvyEclipseGLSPDiagramServer } from './ivy-eclipse-glsp-diagram-server'; -import { eclipseCopyPasteModule, eclipseDeleteModule } from '@eclipse-glsp/ide'; import { ivyInscriptionModule } from '@axonivy/process-editor-inscription'; +import ivyEclipseCopyPasteModule from './copy-paste/di.config'; +import ivyEclipseDeleteModule from './delete/di.config'; export default function createContainer(widgetId: string): Container { const container = createIvyDiagramContainer(widgetId); @@ -23,8 +24,8 @@ export default function createContainer(widgetId: string): Container { container.rebind(TYPES.ILogger).to(ConsoleLogger).inSingletonScope(); container.rebind(TYPES.LogLevel).toConstantValue(LogLevel.warn); - container.load(eclipseCopyPasteModule); - container.load(eclipseDeleteModule); + container.load(ivyEclipseCopyPasteModule); + container.load(ivyEclipseDeleteModule); container.load(ivyOpenInscriptionModule); container.load(ivyOpenDecoratorBrowserModule); container.load(ivyOpenQuickOutlineModule);