Skip to content

Commit 1fa79fb

Browse files
authored
Propagate the GLSP diagram selection to the Eclipse SelectionService (#73)
* Propagate the GLSP diagram selection to the Eclipse SelectionService Fixes eclipse-glsp/glsp#899 Contributed on behalf of STMicroelectronics
1 parent 338c7f1 commit 1fa79fb

File tree

5 files changed

+231
-3
lines changed

5 files changed

+231
-3
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
- [diagram] Fixed a bug that could cause a crash when closing a diagram editor on Windows [#59](https://github.com/eclipse-glsp/glsp-eclipse-integration/pull/59)
88
- [debug] Fixed a bug to ensure that the system browser opens reliably when using the `Debug (External Browser)` command [#60](https://github.com/eclipse-glsp/glsp-eclipse-integration/pull/60)
99
- [websocket] Fixed a bug that could trigger premature session disposal even if other GLSP clients where associated with this session [#63](https://github.com/eclipse-glsp/glsp-eclipse-integration/pull/63)
10+
- [eclipse] Propagate the GLSP diagram selection to the Eclipse selection service [#73](https://github.com/eclipse-glsp/glsp-eclipse-integration/pull/73) -- Contributed on behalf of STMicroelectronics
1011

1112
### Breaking Changes
1213

server/example/org.eclipse.glsp.ide.workflow.editor/src/org/eclipse/glsp/ide/workflow/editor/WorkflowGLSPEclipseModule.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323
import org.eclipse.glsp.ide.editor.actions.NavigateAction;
2424
import org.eclipse.glsp.ide.editor.actions.RequestExportSvgAction;
2525
import org.eclipse.glsp.ide.editor.actions.handlers.IdeNavigateToExternalTargetActionHandler;
26+
import org.eclipse.glsp.ide.editor.actions.handlers.IdeSelectActionHandler;
27+
import org.eclipse.glsp.ide.editor.actions.handlers.IdeSelectAllActionHandler;
2628
import org.eclipse.glsp.ide.editor.actions.handlers.IdeServerMessageActionHandler;
2729
import org.eclipse.glsp.ide.editor.actions.handlers.IdeServerStatusActionHandler;
2830
import org.eclipse.glsp.ide.editor.actions.handlers.IdeSetDirtyStateActionHandler;
@@ -68,6 +70,8 @@ protected void configureActionHandlers(final MultiBinding<ActionHandler> binding
6870
bindings.add(IdeSetDirtyStateActionHandler.class);
6971
bindings.add(IdeServerStatusActionHandler.class);
7072
bindings.add(InitializeCanvasBoundsActionHandler.class);
73+
bindings.add(IdeSelectActionHandler.class);
74+
bindings.add(IdeSelectAllActionHandler.class);
7175
}
7276

7377
@Override
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/********************************************************************************
2+
* Copyright (c) 2023 EclipseSource and others.
3+
*
4+
* This program and the accompanying materials are made available under the
5+
* terms of the Eclipse Public License v. 2.0 which is available at
6+
* https://www.eclipse.org/legal/epl-2.0.
7+
*
8+
* This Source Code may also be made available under the following Secondary
9+
* Licenses when the conditions for such availability set forth in the Eclipse
10+
* Public License v. 2.0 are satisfied: GNU General Public License, version 2
11+
* with the GNU Classpath Exception which is available at
12+
* https://www.gnu.org/software/classpath/license.html.
13+
*
14+
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
15+
********************************************************************************/
16+
package org.eclipse.glsp.ide.editor.actions.handlers;
17+
18+
import java.util.List;
19+
20+
import org.eclipse.glsp.ide.editor.ui.GLSPIdeEditorPlugin;
21+
import org.eclipse.glsp.server.actions.AbstractActionHandler;
22+
import org.eclipse.glsp.server.actions.Action;
23+
import org.eclipse.glsp.server.actions.SelectAction;
24+
import org.eclipse.glsp.server.model.GModelState;
25+
26+
import com.google.inject.Inject;
27+
28+
/**
29+
* Forwards a {@link SelectAction} to the current editor to update its selection state.
30+
*/
31+
public class IdeSelectActionHandler extends AbstractActionHandler<SelectAction> {
32+
33+
@Inject
34+
protected GModelState modelState;
35+
36+
@Override
37+
protected List<Action> executeAction(final SelectAction selectAction) {
38+
GLSPIdeEditorPlugin.getDefaultGLSPEditorRegistry().getGLSPEditorOrThrow(modelState.getClientId())
39+
.updateSelection(selectAction);
40+
return none();
41+
}
42+
43+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
/********************************************************************************
2+
* Copyright (c) 2023 EclipseSource and others.
3+
*
4+
* This program and the accompanying materials are made available under the
5+
* terms of the Eclipse Public License v. 2.0 which is available at
6+
* https://www.eclipse.org/legal/epl-2.0.
7+
*
8+
* This Source Code may also be made available under the following Secondary
9+
* Licenses when the conditions for such availability set forth in the Eclipse
10+
* Public License v. 2.0 are satisfied: GNU General Public License, version 2
11+
* with the GNU Classpath Exception which is available at
12+
* https://www.gnu.org/software/classpath/license.html.
13+
*
14+
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
15+
********************************************************************************/
16+
package org.eclipse.glsp.ide.editor.actions.handlers;
17+
18+
import java.util.ArrayList;
19+
import java.util.Collections;
20+
import java.util.List;
21+
22+
import org.eclipse.glsp.ide.editor.ui.GLSPIdeEditorPlugin;
23+
import org.eclipse.glsp.server.actions.AbstractActionHandler;
24+
import org.eclipse.glsp.server.actions.Action;
25+
import org.eclipse.glsp.server.actions.SelectAction;
26+
import org.eclipse.glsp.server.actions.SelectAllAction;
27+
import org.eclipse.glsp.server.model.GModelState;
28+
29+
import com.google.inject.Inject;
30+
31+
/**
32+
* Forwards the selection of an {@link SelectAllAction} to the current editor to update its
33+
* selection state.
34+
*/
35+
public class IdeSelectAllActionHandler extends AbstractActionHandler<SelectAllAction> {
36+
37+
@Inject
38+
protected GModelState modelState;
39+
40+
@Override
41+
protected List<Action> executeAction(final SelectAllAction selectAllAction) {
42+
SelectAction selectAction;
43+
if (selectAllAction.isSelect()) {
44+
// We don't know on the server which elements are selectable so we just put all
45+
// elements of the diagram into the selection state.
46+
selectAction = new SelectAction(new ArrayList<>(modelState.getIndex().allIds()));
47+
} else {
48+
selectAction = new SelectAction(Collections.emptyList(), new ArrayList<>(modelState.getIndex().allIds()));
49+
}
50+
GLSPIdeEditorPlugin.getDefaultGLSPEditorRegistry().getGLSPEditorOrThrow(modelState.getClientId())
51+
.updateSelection(selectAction);
52+
return none();
53+
}
54+
55+
}

server/plugins/org.eclipse.glsp.ide.editor/src/org/eclipse/glsp/ide/editor/ui/GLSPDiagramEditor.java

Lines changed: 128 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,29 +15,37 @@
1515
********************************************************************************/
1616
package org.eclipse.glsp.ide.editor.ui;
1717

18+
import static java.util.stream.Collectors.toList;
19+
1820
import java.io.UnsupportedEncodingException;
1921
import java.net.URI;
2022
import java.net.URLEncoder;
23+
import java.util.ArrayList;
2124
import java.util.HashMap;
25+
import java.util.List;
2226
import java.util.Map;
2327
import java.util.Optional;
28+
import java.util.Set;
2429
import java.util.UUID;
2530
import java.util.concurrent.CompletableFuture;
2631
import java.util.concurrent.atomic.AtomicInteger;
2732
import java.util.stream.Stream;
2833

2934
import org.apache.logging.log4j.LogManager;
3035
import org.apache.logging.log4j.Logger;
36+
import org.eclipse.core.commands.common.EventManager;
3137
import org.eclipse.core.resources.IFile;
3238
import org.eclipse.core.resources.IMarker;
3339
import org.eclipse.core.runtime.IProgressMonitor;
3440
import org.eclipse.e4.core.contexts.IEclipseContext;
41+
import org.eclipse.glsp.graph.GModelElement;
3542
import org.eclipse.glsp.ide.editor.GLSPServerManager;
3643
import org.eclipse.glsp.ide.editor.actions.GLSPActionProvider;
3744
import org.eclipse.glsp.ide.editor.actions.InvokeCopyAction;
3845
import org.eclipse.glsp.ide.editor.actions.InvokeCutAction;
3946
import org.eclipse.glsp.ide.editor.actions.InvokeDeleteAction;
4047
import org.eclipse.glsp.ide.editor.actions.InvokePasteAction;
48+
import org.eclipse.glsp.ide.editor.actions.handlers.IdeSelectActionHandler;
4149
import org.eclipse.glsp.ide.editor.di.IdeActionDispatcher;
4250
import org.eclipse.glsp.ide.editor.internal.utils.UrlUtils;
4351
import org.eclipse.glsp.ide.editor.utils.GLSPDiagramEditorMarkerUtil;
@@ -46,6 +54,7 @@
4654
import org.eclipse.glsp.server.actions.Action;
4755
import org.eclipse.glsp.server.actions.ActionDispatcher;
4856
import org.eclipse.glsp.server.actions.SaveModelAction;
57+
import org.eclipse.glsp.server.actions.SelectAction;
4958
import org.eclipse.glsp.server.actions.SelectAllAction;
5059
import org.eclipse.glsp.server.actions.ServerStatusAction;
5160
import org.eclipse.glsp.server.disposable.DisposableCollection;
@@ -60,6 +69,11 @@
6069
import org.eclipse.jetty.server.ServerConnector;
6170
import org.eclipse.jface.action.IAction;
6271
import org.eclipse.jface.action.MenuManager;
72+
import org.eclipse.jface.viewers.ISelection;
73+
import org.eclipse.jface.viewers.ISelectionChangedListener;
74+
import org.eclipse.jface.viewers.ISelectionProvider;
75+
import org.eclipse.jface.viewers.SelectionChangedEvent;
76+
import org.eclipse.jface.viewers.StructuredSelection;
6377
import org.eclipse.swt.SWT;
6478
import org.eclipse.swt.browser.Browser;
6579
import org.eclipse.swt.browser.ProgressListener;
@@ -81,7 +95,7 @@
8195

8296
import com.google.inject.Injector;
8397

84-
public class GLSPDiagramEditor extends EditorPart implements IGotoMarker {
98+
public class GLSPDiagramEditor extends EditorPart implements IGotoMarker, ISelectionProvider {
8599
/**
86100
* {@link IEclipseContext} key for the current client id. The associated value
87101
* is a {@link String}.
@@ -103,6 +117,9 @@ public class GLSPDiagramEditor extends EditorPart implements IGotoMarker {
103117
protected final CompletableFuture<Injector> injector = new CompletableFuture<>();
104118
protected boolean dirty;
105119

120+
protected final SelectionManager selectionListener = new SelectionManager();
121+
private StructuredSelection currentSelection = StructuredSelection.EMPTY;
122+
106123
protected final DisposableCollection toDispose = new DisposableCollection();
107124

108125
protected final Map<String, IAction> globalActions = new HashMap<>(Map.of(
@@ -308,10 +325,12 @@ public void createPartControl(final Composite parent) {
308325

309326
setPartName(generatePartName());
310327

311-
this.browser = createBrowser(root);
328+
browser = createBrowser(root);
312329
setupBrowser(this.browser);
313330

314-
this.statusBar = createStatusBar(root);
331+
statusBar = createStatusBar(root);
332+
333+
getSite().setSelectionProvider(this);
315334
}
316335

317336
protected String generatePartName() {
@@ -454,4 +473,110 @@ public void run() {
454473
}
455474
};
456475
}
476+
477+
protected static class SelectionManager extends EventManager {
478+
479+
public void addSelectionChangedListener(final ISelectionChangedListener listener) {
480+
addListenerObject(listener);
481+
}
482+
483+
public void removeSelectionChangedListener(final ISelectionChangedListener listener) {
484+
removeListenerObject(listener);
485+
}
486+
487+
public void selectionChanged(final SelectionChangedEvent event) {
488+
for (Object listener : getListeners()) {
489+
final ISelectionChangedListener selectionChangedListeners = (ISelectionChangedListener) listener;
490+
UIUtil.asyncExec(() -> selectionChangedListeners.selectionChanged(event));
491+
}
492+
}
493+
494+
}
495+
496+
@Override
497+
public void addSelectionChangedListener(final ISelectionChangedListener listener) {
498+
selectionListener.addSelectionChangedListener(listener);
499+
}
500+
501+
@Override
502+
public void removeSelectionChangedListener(final ISelectionChangedListener listener) {
503+
selectionListener.removeSelectionChangedListener(listener);
504+
}
505+
506+
@Override
507+
public ISelection getSelection() { return currentSelection; }
508+
509+
/**
510+
* Sets the selection on the client by dispatching a corresponding
511+
* {@link SelectAction}.
512+
* <p>
513+
* If a {@link IdeSelectActionHandler} is installed on the server, this will
514+
* also eventually invoke {@link #updateSelection(SelectAction)} to update the
515+
* {@link #currentSelection} in this editor object and notify the registered
516+
* selection listeners.
517+
* </p>
518+
*/
519+
@Override
520+
public void setSelection(final ISelection selection) {
521+
if (!(selection instanceof StructuredSelection)) {
522+
return;
523+
}
524+
525+
getModelStateOnceInitialized().thenAccept(modelState -> {
526+
StructuredSelection structuredSelection = (StructuredSelection) selection;
527+
List<String> toSelect = toGModelElementStream(structuredSelection).map(GModelElement::getId).collect(toList());
528+
Set<String> toDeselect = modelState.getIndex().allIds();
529+
toDeselect.removeAll(toSelect);
530+
dispatch(new SelectAction(toSelect, new ArrayList<>(toDeselect)));
531+
});
532+
}
533+
534+
/**
535+
* Updates the currently selected elements.
536+
* <p>
537+
* In contrast to {@link #setSelection(ISelection)}, this method does not change
538+
* the selection on the client but only notifies the selection listeners and
539+
* updates the list of currently selected elements in this editor object.
540+
* </p>
541+
* <p>
542+
* This method is usually invoked by the {@link IdeSelectActionHandler}, which
543+
* reacts to the {@link SelectAction} (e.g. triggered by the client on select)
544+
* to update the selection and notify listeners in Eclipse.
545+
* </p>
546+
*
547+
* @param selectAction the {@link SelectAction}
548+
*/
549+
public void updateSelection(final SelectAction selectAction) {
550+
getModelStateOnceInitialized().thenAccept(modelState -> {
551+
List<String> selectedIds = selectAction.getSelectedElementsIDs();
552+
List<String> deselectedIds = selectAction.getDeselectedElementsIDs();
553+
List<GModelElement> selectedGModelElements = toGModelElements(selectedIds, modelState);
554+
List<GModelElement> deselectedGModelElements = toGModelElements(deselectedIds, modelState);
555+
List<GModelElement> selection = toGModelElementStream(currentSelection).collect(toList());
556+
557+
selection.removeAll(deselectedGModelElements);
558+
addUnique(selectedGModelElements, selection);
559+
560+
currentSelection = new StructuredSelection(selection);
561+
selectionListener.selectionChanged(new SelectionChangedEvent(this, currentSelection));
562+
});
563+
}
564+
565+
private void addUnique(final List<GModelElement> fromList, final List<GModelElement> toList) {
566+
for (GModelElement newSelectedElement : fromList) {
567+
if (!toList.contains(newSelectedElement)) {
568+
toList.add(newSelectedElement);
569+
}
570+
}
571+
}
572+
573+
@SuppressWarnings("unchecked")
574+
protected Stream<GModelElement> toGModelElementStream(final StructuredSelection selection) {
575+
return selection.toList().stream().filter(GModelElement.class::isInstance).map(GModelElement.class::cast);
576+
}
577+
578+
protected List<GModelElement> toGModelElements(final List<String> ids, final GModelState modelState) {
579+
return ids.stream().map(modelState.getIndex()::get).flatMap(Optional::stream).collect(toList());
580+
}
581+
457582
}

0 commit comments

Comments
 (0)