Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChristian W. Damus2016-11-01 19:05:44 +0000
committerChristian W. Damus2016-11-02 00:56:05 +0000
commit4a48cdce7f043c5eb617ed0917d1dcbd785425de (patch)
tree1a7fa7125d5733f09755221a41a22dd809932c14
parent698f97c06c22bcce81327c3c64f7440ac9d130eb (diff)
downloadorg.eclipse.papyrus-4a48cdce7f043c5eb617ed0917d1dcbd785425de.tar.gz
org.eclipse.papyrus-4a48cdce7f043c5eb617ed0917d1dcbd785425de.tar.xz
org.eclipse.papyrus-4a48cdce7f043c5eb617ed0917d1dcbd785425de.zip
Bug 501833: [Properties] Content-assist does not work on Mac
Hook the content-assist invocation to the text viewer on the user's preferred content-assist keybinding (as registered in the Keys preference page), defaulting to CTRL+Space on all platforms, as the content-assist command itself does. https://bugs.eclipse.org/bugs/show_bug.cgi?id=501833 Change-Id: I639c489ee42672474f869ec5b7492fffea925b07
-rw-r--r--plugins/infra/ui/org.eclipse.papyrus.infra.widgets/META-INF/MANIFEST.MF6
-rw-r--r--plugins/infra/ui/org.eclipse.papyrus.infra.widgets/pom.xml2
-rw-r--r--plugins/infra/ui/org.eclipse.papyrus.infra.widgets/src/org/eclipse/papyrus/infra/widgets/editors/PlatformUIUtils.java229
-rw-r--r--plugins/infra/ui/org.eclipse.papyrus.infra.widgets/src/org/eclipse/papyrus/infra/widgets/editors/StringEditorWithCompletionWrapper.java41
4 files changed, 255 insertions, 23 deletions
diff --git a/plugins/infra/ui/org.eclipse.papyrus.infra.widgets/META-INF/MANIFEST.MF b/plugins/infra/ui/org.eclipse.papyrus.infra.widgets/META-INF/MANIFEST.MF
index aa58721fba9..d39b9e7a275 100644
--- a/plugins/infra/ui/org.eclipse.papyrus.infra.widgets/META-INF/MANIFEST.MF
+++ b/plugins/infra/ui/org.eclipse.papyrus.infra.widgets/META-INF/MANIFEST.MF
@@ -9,7 +9,9 @@ Require-Bundle: org.eclipse.core.databinding;bundle-version="[1.6.0,2.0.0)";visi
org.eclipse.papyrus.infra.services.labelprovider;bundle-version="[1.2.0,2.0.0)";visibility:=reexport,
org.eclipse.jface.text;bundle-version="[3.11.0,4.0.0)";visibility:=reexport,
org.eclipse.emf.ecore;bundle-version="[2.12.0,3.0.0)";visibility:=reexport,
- org.eclipse.nebula.widgets.richtext;bundle-version="[1.0.0,2.0.0)"
+ org.eclipse.nebula.widgets.richtext;bundle-version="[1.0.0,2.0.0)",
+ org.eclipse.e4.ui.model.workbench;bundle-version="[1.2.0,2.0.0)",
+ org.eclipse.e4.core.contexts;bundle-version="[1.5.0,2.0.0)"
Export-Package: org.eclipse.papyrus.infra.widgets,
org.eclipse.papyrus.infra.widgets.creation,
org.eclipse.papyrus.infra.widgets.databinding,
@@ -25,7 +27,7 @@ Export-Package: org.eclipse.papyrus.infra.widgets,
org.eclipse.papyrus.infra.widgets.wizard.pages
Bundle-Vendor: %providerName
Bundle-ActivationPolicy: lazy
-Bundle-Version: 2.0.0.qualifier
+Bundle-Version: 2.0.1.qualifier
Bundle-Name: %pluginName
Bundle-Localization: plugin
Bundle-ManifestVersion: 2
diff --git a/plugins/infra/ui/org.eclipse.papyrus.infra.widgets/pom.xml b/plugins/infra/ui/org.eclipse.papyrus.infra.widgets/pom.xml
index c7067be8e76..f5408ec1ba5 100644
--- a/plugins/infra/ui/org.eclipse.papyrus.infra.widgets/pom.xml
+++ b/plugins/infra/ui/org.eclipse.papyrus.infra.widgets/pom.xml
@@ -7,6 +7,6 @@
<version>0.0.1-SNAPSHOT</version>
</parent>
<artifactId>org.eclipse.papyrus.infra.widgets</artifactId>
- <version>2.0.0-SNAPSHOT</version>
+ <version>2.0.1-SNAPSHOT</version>
<packaging>eclipse-plugin</packaging>
</project>
diff --git a/plugins/infra/ui/org.eclipse.papyrus.infra.widgets/src/org/eclipse/papyrus/infra/widgets/editors/PlatformUIUtils.java b/plugins/infra/ui/org.eclipse.papyrus.infra.widgets/src/org/eclipse/papyrus/infra/widgets/editors/PlatformUIUtils.java
new file mode 100644
index 00000000000..1ac74fdd0ea
--- /dev/null
+++ b/plugins/infra/ui/org.eclipse.papyrus.infra.widgets/src/org/eclipse/papyrus/infra/widgets/editors/PlatformUIUtils.java
@@ -0,0 +1,229 @@
+/*****************************************************************************
+ * Copyright (c) 2016 Christian W. Damus and others.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Christian W. Damus - Initial API and implementation
+ *
+ *****************************************************************************/
+
+package org.eclipse.papyrus.infra.widgets.editors;
+
+import java.util.Iterator;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.Spliterator;
+import java.util.Spliterators;
+import java.util.function.Consumer;
+import java.util.stream.Stream;
+import java.util.stream.StreamSupport;
+
+import org.eclipse.e4.ui.model.application.ui.basic.MPart;
+import org.eclipse.jface.bindings.IBindingManagerListener;
+import org.eclipse.jface.bindings.Trigger;
+import org.eclipse.jface.bindings.TriggerSequence;
+import org.eclipse.jface.bindings.keys.KeyStroke;
+import org.eclipse.jface.bindings.keys.ParseException;
+import org.eclipse.jface.bindings.keys.SWTKeySupport;
+import org.eclipse.papyrus.infra.widgets.Activator;
+import org.eclipse.swt.events.KeyAdapter;
+import org.eclipse.swt.events.KeyEvent;
+import org.eclipse.swt.events.KeyListener;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.ui.IWorkbench;
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.IWorkbenchPart;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.keys.IBindingService;
+
+import com.google.common.collect.AbstractIterator;
+
+/**
+ * Some utilities for working with the Eclipse Platform UI.
+ */
+class PlatformUIUtils {
+ private static final String MODEL_ELEMENT_KEY = "modelElement"; //$NON-NLS-1$
+
+ /**
+ * Not instantiable by clients.
+ */
+ private PlatformUIUtils() {
+ super();
+ }
+
+ /**
+ * Obtains the parent chain, starting from the {@code control} itself, of a control.
+ *
+ * @param control
+ * a control
+ *
+ * @return a stream providing the {@code control} and all of its parent chain,
+ * in order, up to the topmost control in the shell
+ */
+ static Stream<Control> parentChain(Control control) {
+ Iterator<Control> iterator = new AbstractIterator<Control>() {
+ private Control next = control;
+
+ @Override
+ protected Control computeNext() {
+ Control result = next;
+ if (result != null) {
+ next = result.getParent();
+ } else {
+ result = endOfData();
+ }
+ return result;
+ }
+ };
+
+ Spliterator<Control> spliterator = Spliterators.spliteratorUnknownSize(
+ iterator,
+ Spliterator.DISTINCT | Spliterator.ORDERED | Spliterator.NONNULL);
+
+ return StreamSupport.stream(spliterator, false);
+ }
+
+ /**
+ * Obtains a service, by its interface class, from a {@code control}. This is
+ * useful in contexts where it is not known what
+ * {@linkplain IWorkbenchPart workbench part} contains the {@code control}.
+ *
+ * @param control
+ * a control
+ * @param api
+ * the service interface to retrieve
+ *
+ * @return the requested service, or {@code null} if it cannot be determined
+ * (usually because the {@code control} is not in a workbench window)
+ */
+ static <S> S getService(Control control, Class<S> api) {
+ Optional<S> result = parentChain(control)
+ .map(c -> c.getData(MODEL_ELEMENT_KEY))
+ .filter(MPart.class::isInstance).map(MPart.class::cast)
+ .map(MPart::getContext)
+ .map(ctx -> ctx.get(api))
+ .filter(Objects::nonNull)
+ .findFirst();
+
+ return result.orElseGet(() -> bestEffortService(api));
+ }
+
+ private static <S> S bestEffortService(Class<S> api) {
+ S result;
+
+ IWorkbench workbench = PlatformUI.getWorkbench();
+
+ IWorkbenchWindow window = workbench.getActiveWorkbenchWindow();
+ if (window == null) {
+ // Take the first available
+ IWorkbenchWindow[] allWindows = workbench.getWorkbenchWindows();
+ if (allWindows.length > 0) {
+ window = allWindows[0];
+ }
+ }
+
+ if (window != null) {
+ IWorkbenchPage page = window.getActivePage();
+ if (page == null) {
+ // Take the first available
+ IWorkbenchPage[] allPages = window.getPages();
+ if (allPages.length > 0) {
+ page = allPages[0];
+ }
+ }
+
+ if (page != null) {
+ IWorkbenchPart activePart = page.getActivePart();
+ if (activePart != null) {
+ result = activePart.getSite().getService(api);
+ } else {
+ result = window.getService(api);
+ }
+ } else {
+ result = window.getService(api);
+ }
+ } else {
+ result = workbench.getService(api);
+ }
+
+ return result;
+ }
+
+ /**
+ * Installs an ad hoc handler for the specified command on a {@code control} that otherwise
+ * would not use the standard Eclipse framework for invocation of that command.
+ *
+ * @param control
+ * a control
+ * @param commandID
+ * the unique identifier of the command to be handled
+ * @param defaultKeystroke
+ * in case no keybinding is registered, a default keystroke to
+ * listen for (per the {@link KeyStroke#getInstance(String) keystroke parser}). May be
+ * {@link null} to respect the user's choice to unbind the command
+ * @param action
+ * the command handler to invoke
+ */
+ static void handleCommand(Control control, String commandID, String defaultKeystroke, Runnable action) {
+ IBindingService bindingService = PlatformUIUtils.getService(control, IBindingService.class);
+ Consumer<IBindingService> onBindingsChanged = new Consumer<IBindingService>() {
+ private KeyListener keyListener;
+
+ @Override
+ public void accept(IBindingService bindings) {
+ // Remove current listener, if any
+ if (keyListener != null) {
+ control.removeKeyListener(keyListener);
+ keyListener = null;
+ }
+
+ // Recompute our trigger
+ TriggerSequence triggerSeq = bindings.getBestActiveBindingFor(commandID);
+ if (triggerSeq != null) {
+ try {
+ Trigger trigger;
+
+ // We can only handle single-stroke triggers
+ Trigger[] triggers = triggerSeq.getTriggers();
+ if (triggers.length == 1) {
+ trigger = triggers[0];
+ } else if (defaultKeystroke != null) {
+ trigger = KeyStroke.getInstance(defaultKeystroke); // $NON-NLS-1$
+ } else {
+ trigger = null;
+ }
+
+ if (trigger != null) {
+ keyListener = new KeyAdapter() {
+ @Override
+ public void keyPressed(KeyEvent e) {
+ int accel = SWTKeySupport.convertEventToUnmodifiedAccelerator(e);
+ KeyStroke stroke = SWTKeySupport.convertAcceleratorToKeyStroke(accel);
+ if (stroke.equals(trigger)) {
+ action.run();
+ }
+ }
+ };
+ control.addKeyListener(keyListener);
+ }
+ } catch (ParseException e) {
+ Activator.log.error(String.format("Cannot represent %s trigger as a KeyStroke", defaultKeystroke), e); //$NON-NLS-1$
+ }
+ }
+ }
+ };
+
+ IBindingManagerListener bindingListener = event -> onBindingsChanged.accept(bindingService);
+ bindingService.addBindingManagerListener(bindingListener);
+
+ // Prime the pump
+ onBindingsChanged.accept(bindingService);
+
+ control.addDisposeListener(__ -> bindingService.removeBindingManagerListener(bindingListener));
+ }
+}
diff --git a/plugins/infra/ui/org.eclipse.papyrus.infra.widgets/src/org/eclipse/papyrus/infra/widgets/editors/StringEditorWithCompletionWrapper.java b/plugins/infra/ui/org.eclipse.papyrus.infra.widgets/src/org/eclipse/papyrus/infra/widgets/editors/StringEditorWithCompletionWrapper.java
index 98d34d0acd5..bac77c467d1 100644
--- a/plugins/infra/ui/org.eclipse.papyrus.infra.widgets/src/org/eclipse/papyrus/infra/widgets/editors/StringEditorWithCompletionWrapper.java
+++ b/plugins/infra/ui/org.eclipse.papyrus.infra.widgets/src/org/eclipse/papyrus/infra/widgets/editors/StringEditorWithCompletionWrapper.java
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Copyright (c) 2015 CEA LIST and others.
+ * Copyright (c) 2015, 2016 CEA LIST, Christian W. Damus, and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
@@ -8,6 +8,7 @@
*
* Contributors:
* CEA LIST - Initial API and implementation
+ * Christian W. Damus - bug 501833
*
*****************************************************************************/
@@ -26,8 +27,6 @@ import org.eclipse.papyrus.infra.widgets.util.IPapyrusConverter;
import org.eclipse.papyrus.infra.widgets.util.ISetPapyrusConverter;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.StyledText;
-import org.eclipse.swt.events.KeyAdapter;
-import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
@@ -37,7 +36,9 @@ import org.eclipse.swt.widgets.Display;
* This class allows to build a StyledText widget allowing the completion
*
*/
-public class StringEditorWithCompletionWrapper implements ISetPapyrusConverter{
+public class StringEditorWithCompletionWrapper implements ISetPapyrusConverter {
+
+ private static final String CONTENT_ASSIST_COMMAND_ID = "org.eclipse.ui.edit.text.contentAssist.proposals"; //$NON-NLS-1$
/**
* the parser used to convert object to string and string to object
@@ -95,11 +96,11 @@ public class StringEditorWithCompletionWrapper implements ISetPapyrusConverter{
buildControls(parent, style);
}
-
+
/**
*
* @return
- * the text viewer used
+ * the text viewer used
*/
public TextViewer getTextViewer() {
return this.textViewer;
@@ -108,7 +109,7 @@ public class StringEditorWithCompletionWrapper implements ISetPapyrusConverter{
/**
*
* @return
- * the styled text used or <code>null</code>
+ * the styled text used or <code>null</code>
*/
public StyledText getTextWidget() {
if (this.textViewer != null) {
@@ -121,7 +122,7 @@ public class StringEditorWithCompletionWrapper implements ISetPapyrusConverter{
/**
*
* @return
- * <code>true</code> if the content assist is currently opened
+ * <code>true</code> if the content assist is currently opened
*/
public boolean isContentAssistOpened() {
return delayedIsOpen;
@@ -130,7 +131,7 @@ public class StringEditorWithCompletionWrapper implements ISetPapyrusConverter{
private ContentAssistant assistant;
private IContentAssistProcessor processor;
-
+
private void buildControls(Composite parent, int style) {
// setLayout(new FillLayout());
textViewer = new TextViewer(parent, SWT.SINGLE | SWT.V_SCROLL | style);
@@ -144,16 +145,16 @@ public class StringEditorWithCompletionWrapper implements ISetPapyrusConverter{
this.processor = parser.getCompletionProcessor(null);
assistant.setContentAssistProcessor(this.processor, IDocument.DEFAULT_CONTENT_TYPE);
}
-
+
// Manage the sorter for the completion proposal
assistant.setSorter(new ICompletionProposalSorter() {
-
+
@Override
public int compare(final ICompletionProposal p1, final ICompletionProposal p2) {
return p1.getDisplayString().compareTo(p2.getDisplayString());
}
});
-
+
assistant.install(textViewer);
assistant.addCompletionListener(new ICompletionListener() {
@@ -167,6 +168,7 @@ public class StringEditorWithCompletionWrapper implements ISetPapyrusConverter{
// reset open status asynchronously.
Display.getDefault().asyncExec(new Runnable() {
+ @Override
public void run() {
delayedIsOpen = true;
}
@@ -178,6 +180,7 @@ public class StringEditorWithCompletionWrapper implements ISetPapyrusConverter{
// reset open status asynchronously.
Display.getDefault().asyncExec(new Runnable() {
+ @Override
public void run() {
delayedIsOpen = false;
}
@@ -185,13 +188,11 @@ public class StringEditorWithCompletionWrapper implements ISetPapyrusConverter{
}
});
- textViewer.getControl().addKeyListener(new KeyAdapter() {
- public void keyPressed(KeyEvent e) {
- if (SWT.CTRL == e.stateMask && SWT.SPACE == e.character) {
- assistant.showPossibleCompletions();
- }
- }
- });
+
+ // Hook up the user's preferred key binding for content assist
+ PlatformUIUtils.handleCommand(textViewer.getControl(), CONTENT_ASSIST_COMMAND_ID,
+ "CTRL+SPACE", //$NON-NLS-1$ // The default on all platforms
+ assistant::showPossibleCompletions);
}
/**
@@ -202,7 +203,7 @@ public class StringEditorWithCompletionWrapper implements ISetPapyrusConverter{
@Override
public void setPapyrusConverter(IPapyrusConverter parser) {
this.parser = parser;
- if (parser != null && assistant!=null && processor==null) {
+ if (parser != null && assistant != null && processor == null) {
this.processor = parser.getCompletionProcessor(null);
assistant.setContentAssistProcessor(this.processor, IDocument.DEFAULT_CONTENT_TYPE);
}

Back to the top