diff options
Diffstat (limited to 'plugins/uml/xtext/org.eclipse.papyrus.uml.xtext.integration.ui/src/org/eclipse/papyrus/uml/xtext')
6 files changed, 1161 insertions, 1078 deletions
diff --git a/plugins/uml/xtext/org.eclipse.papyrus.uml.xtext.integration.ui/src/org/eclipse/papyrus/uml/xtext/integration/ActiveEditorTracker.java b/plugins/uml/xtext/org.eclipse.papyrus.uml.xtext.integration.ui/src/org/eclipse/papyrus/uml/xtext/integration/ActiveEditorTracker.java index 594ac16ef70..78fdd756ea3 100644 --- a/plugins/uml/xtext/org.eclipse.papyrus.uml.xtext.integration.ui/src/org/eclipse/papyrus/uml/xtext/integration/ActiveEditorTracker.java +++ b/plugins/uml/xtext/org.eclipse.papyrus.uml.xtext.integration.ui/src/org/eclipse/papyrus/uml/xtext/integration/ActiveEditorTracker.java @@ -1,314 +1,328 @@ -package org.eclipse.papyrus.uml.xtext.integration;
-
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Map.Entry;
-
-import org.eclipse.core.resources.IProject;
-import org.eclipse.emf.ecore.resource.ResourceSet;
-import org.eclipse.emf.edit.domain.EditingDomain;
-import org.eclipse.emf.edit.domain.IEditingDomainProvider;
-import org.eclipse.ui.IEditorInput;
-import org.eclipse.ui.IEditorPart;
-import org.eclipse.ui.IEditorReference;
-import org.eclipse.ui.IFileEditorInput;
-import org.eclipse.ui.IPageListener;
-import org.eclipse.ui.IPartListener;
-import org.eclipse.ui.IStartup;
-import org.eclipse.ui.IWindowListener;
-import org.eclipse.ui.IWorkbenchPage;
-import org.eclipse.ui.IWorkbenchPart;
-import org.eclipse.ui.IWorkbenchPartReference;
-import org.eclipse.ui.IWorkbenchWindow;
-import org.eclipse.ui.PlatformUI;
-
-/**
- * Get the last active editor in general or of a specific type.
- *
- * Please note that the ActiveEditorTracker currently only supports one single
- * workbench window properly.
- *
- * @author patrick.koenemann@itemis.de
- * @author alexander.nyssen@itemis.de
- *
- */
-public class ActiveEditorTracker implements IPageListener, IPartListener,
- IStartup, IWindowListener {
-
- private static final String SINGLETON_MSG = "This class is a singleton and may only be instantiated once!";
-
- private IWorkbenchWindow workbenchWindow;
-
- private Map<String, IEditorPart> activeEditors = new HashMap<String, IEditorPart>();
-
- private String lastActiveEditorId;
-
- private IWorkbenchPage activePage;
-
- private static ActiveEditorTracker INSTANCE;
-
- public ActiveEditorTracker() {
- if (INSTANCE != null)
- throw new IllegalStateException(SINGLETON_MSG);
- INSTANCE = this;
- }
-
- public void earlyStartup() {
- PlatformUI.getWorkbench().addWindowListener(this);
- }
-
- /**
- * @return The last active editor in the current active workbench page.
- */
- public static IEditorPart getLastActiveEditor() {
- if (INSTANCE == null) {
- // not yet initialized, e.g. when another early startups blocks us!
- // Let's try to get the current active editor instead.
- if (PlatformUI.getWorkbench() != null) {
- if (PlatformUI.getWorkbench().getActiveWorkbenchWindow() != null) {
- return PlatformUI.getWorkbench().getActiveWorkbenchWindow()
- .getActivePage().getActiveEditor();
- }
- }
- return null;
- }
- return INSTANCE.getLastActiveEditorInternal();
- }
-
- /**
- * @return The last active editor with the given editor ID in the current
- * active workbench page.
- */
- public static IEditorPart getLastEditor(String editorId) {
- if (INSTANCE == null) {
- // not yet initialized, e.g. when another early startups blocks us!
- // Let's try to get any editor with the specified id instead.
- if (PlatformUI.getWorkbench() != null) {
- final IWorkbenchWindow window = PlatformUI.getWorkbench()
- .getActiveWorkbenchWindow();
- if (window != null) {
- final IWorkbenchPage page = window.getActivePage();
- if (page != null) {
- for (IEditorReference ref : page.getEditorReferences()) {
- if (ref.getId().equals(editorId)) {
- return ref.getEditor(false);
- }
- }
- }
- }
- }
- return null;
- }
- return INSTANCE.getEditorById(editorId);
- }
-
- /**
- *
- * @return The EMF resource set of the last active editor (if it is still
- * open).
- */
- public static ResourceSet getLastActiveEditorResourceSet() {
- final IEditorPart editor = getLastActiveEditor();
- if (editor == null)
- return null;
- EditingDomain domain = null;
- if (editor instanceof IEditingDomainProvider) {
- domain = ((IEditingDomainProvider) editor).getEditingDomain();
- } else if (editor.getAdapter(IEditingDomainProvider.class) != null) {
- domain = ((IEditingDomainProvider) editor
- .getAdapter(IEditingDomainProvider.class))
- .getEditingDomain();
- } else if (editor.getAdapter(EditingDomain.class) != null) {
- domain = (EditingDomain) editor.getAdapter(EditingDomain.class);
- }
- if (domain == null) {
- return null;
- }
-
- return domain.getResourceSet();
- }
-
- /**
- * @return The project which contains the file that is open in the last
- * active editor in the current workbench page.
- */
- public static IProject getLastActiveEditorProject() {
- final IEditorPart editor = getLastActiveEditor();
- if (editor == null)
- return null;
- final IEditorInput editorInput = editor.getEditorInput();
- if (editorInput instanceof IFileEditorInput) {
- final IFileEditorInput input = (IFileEditorInput) editorInput;
- return input.getFile().getProject();
- }
- return null;
- }
-
- public void pageActivated(IWorkbenchPage page) {
- this.activePage = page;
- }
-
- public void pageClosed(IWorkbenchPage page) {
- if (page == activePage) {
- activePage = null;
- }
- lastActiveEditorId = null;
- }
-
- public void pageOpened(IWorkbenchPage page) {
- // do nothing
- System.out.println("PAGE OPENED: " + page);
- }
-
- public void partActivated(IWorkbenchPart part) {
- if (part instanceof IEditorPart) {
- setActiveEditor((IEditorPart) part);
- }
- }
-
- public void partBroughtToTop(IWorkbenchPart part) {
- if (part instanceof IEditorPart) {
- setActiveEditor((IEditorPart) part);
- }
- }
-
- public void partClosed(IWorkbenchPart part) {
- if (part instanceof IEditorPart) {
- String id = null;
- for (Entry<String, IEditorPart> entry : activeEditors.entrySet()) {
- if (entry.getValue().equals(part)) {
- id = entry.getKey();
- break;
- }
- }
- if (id != null) {
- activeEditors.remove(id);
- if (id.equals(lastActiveEditorId)) {
- lastActiveEditorId = null;
- }
- }
- }
- }
-
- public void partDeactivated(IWorkbenchPart part) {
- if (part instanceof IEditorPart) {
- // do nothing
- }
- }
-
- private IEditorPart getLastActiveEditorInternal() {
- if (activePage == null) {
- initialize(PlatformUI.getWorkbench().getActiveWorkbenchWindow());
- if (activePage == null)
- return null;
- }
- boolean updated = false;
- if (lastActiveEditorId == null) {
- final IEditorPart editor = activePage.getActiveEditor();
- if (editor != null) {
- setActiveEditor(editor);
- }
- updated = true;
- }
- IEditorPart editor = getEditorById(lastActiveEditorId);
- if (editor == null && !updated) {
- editor = activePage.getActiveEditor();
- if (editor != null) {
- setActiveEditor(editor);
- }
- }
- return editor;
- }
-
- private IEditorPart getEditorById(String editorId) {
- if (activePage == null || editorId == null)
- return null;
- final IEditorPart editor = activeEditors.get(editorId);
- final String id = checkEditorAndGetId(editor);
- if (id != null && id.equals(editorId)) {
- return editor;
- }
- return null;
- }
-
- private String checkEditorAndGetId(IEditorPart editor) {
- if (editor == null)
- return null;
- for (IEditorReference ref : activePage.getEditorReferences()) {
- if (editor.equals(ref.getEditor(false))) {
- return ref.getId();
- }
- }
- return null;
- }
-
- public IWorkbenchPage getActivePage() {
- return activePage;
- }
-
- /**
- * Set the active editor
- */
- private void setActiveEditor(IEditorPart part) {
- if (part == null) {
- lastActiveEditorId = null;
- return;
- }
- final IWorkbenchPartReference reference = activePage.getReference(part);
- if (reference == null)
- throw new IllegalStateException("Impossible?!");
- lastActiveEditorId = reference.getId();
- activeEditors.put(lastActiveEditorId, part);
- }
-
- public void partOpened(IWorkbenchPart part) {
- if (part instanceof IEditorPart) {
- setActiveEditor((IEditorPart) part);
- }
- }
-
- public void dispose() {
- if (workbenchWindow == null) {
- return;
- }
- workbenchWindow.removePageListener(this);
- workbenchWindow.getPartService().removePartListener(this);
- workbenchWindow = null;
- }
-
- public void windowActivated(IWorkbenchWindow window) {
- initialize(window);
- }
-
- protected void initialize(IWorkbenchWindow window) {
- if (workbenchWindow != null && !workbenchWindow.equals(window)) {
- /*
- * TODO: implement logic for keeping track of editors in multiple
- * workbench windows!
- */
- }
- this.workbenchWindow = window;
- if (window == null)
- return;
- this.activePage = window.getActivePage();
- final IEditorPart editor = this.activePage.getActiveEditor();
- if (editor != null) {
- lastActiveEditorId = checkEditorAndGetId(editor);
- activeEditors.put(lastActiveEditorId, editor);
- }
- window.addPageListener(this);
- window.getPartService().addPartListener(this);
- }
-
- public void windowDeactivated(IWorkbenchWindow window) {
- // not of interest
- }
-
- public void windowClosed(IWorkbenchWindow window) {
- dispose();
- }
-
- public void windowOpened(IWorkbenchWindow window) {
- // not of interest
- }
+/***************************************************************************** +* Copyright (c) 2010 CEA LIST. +* +* + * 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: +* Itemis - Initial API and implementation +* +*****************************************************************************/ + +package org.eclipse.papyrus.uml.xtext.integration; + +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; + +import org.eclipse.core.resources.IProject; +import org.eclipse.emf.ecore.resource.ResourceSet; +import org.eclipse.emf.edit.domain.EditingDomain; +import org.eclipse.emf.edit.domain.IEditingDomainProvider; +import org.eclipse.ui.IEditorInput; +import org.eclipse.ui.IEditorPart; +import org.eclipse.ui.IEditorReference; +import org.eclipse.ui.IFileEditorInput; +import org.eclipse.ui.IPageListener; +import org.eclipse.ui.IPartListener; +import org.eclipse.ui.IStartup; +import org.eclipse.ui.IWindowListener; +import org.eclipse.ui.IWorkbenchPage; +import org.eclipse.ui.IWorkbenchPart; +import org.eclipse.ui.IWorkbenchPartReference; +import org.eclipse.ui.IWorkbenchWindow; +import org.eclipse.ui.PlatformUI; + +/** + * Get the last active editor in general or of a specific type. + * + * Please note that the ActiveEditorTracker currently only supports one single + * workbench window properly. + * + * @author patrick.koenemann@itemis.de + * @author alexander.nyssen@itemis.de + * + */ +public class ActiveEditorTracker implements IPageListener, IPartListener, + IStartup, IWindowListener { + + private static final String SINGLETON_MSG = "This class is a singleton and may only be instantiated once!"; + + private IWorkbenchWindow workbenchWindow; + + private Map<String, IEditorPart> activeEditors = new HashMap<String, IEditorPart>(); + + private String lastActiveEditorId; + + private IWorkbenchPage activePage; + + private static ActiveEditorTracker INSTANCE; + + public ActiveEditorTracker() { + if (INSTANCE != null) + throw new IllegalStateException(SINGLETON_MSG); + INSTANCE = this; + } + + public void earlyStartup() { + PlatformUI.getWorkbench().addWindowListener(this); + } + + /** + * @return The last active editor in the current active workbench page. + */ + public static IEditorPart getLastActiveEditor() { + if (INSTANCE == null) { + // not yet initialized, e.g. when another early startups blocks us! + // Let's try to get the current active editor instead. + if (PlatformUI.getWorkbench() != null) { + if (PlatformUI.getWorkbench().getActiveWorkbenchWindow() != null) { + return PlatformUI.getWorkbench().getActiveWorkbenchWindow() + .getActivePage().getActiveEditor(); + } + } + return null; + } + return INSTANCE.getLastActiveEditorInternal(); + } + + /** + * @return The last active editor with the given editor ID in the current + * active workbench page. + */ + public static IEditorPart getLastEditor(String editorId) { + if (INSTANCE == null) { + // not yet initialized, e.g. when another early startups blocks us! + // Let's try to get any editor with the specified id instead. + if (PlatformUI.getWorkbench() != null) { + final IWorkbenchWindow window = PlatformUI.getWorkbench() + .getActiveWorkbenchWindow(); + if (window != null) { + final IWorkbenchPage page = window.getActivePage(); + if (page != null) { + for (IEditorReference ref : page.getEditorReferences()) { + if (ref.getId().equals(editorId)) { + return ref.getEditor(false); + } + } + } + } + } + return null; + } + return INSTANCE.getEditorById(editorId); + } + + /** + * + * @return The EMF resource set of the last active editor (if it is still + * open). + */ + public static ResourceSet getLastActiveEditorResourceSet() { + final IEditorPart editor = getLastActiveEditor(); + if (editor == null) + return null; + EditingDomain domain = null; + if (editor instanceof IEditingDomainProvider) { + domain = ((IEditingDomainProvider) editor).getEditingDomain(); + } else if (editor.getAdapter(IEditingDomainProvider.class) != null) { + domain = ((IEditingDomainProvider) editor + .getAdapter(IEditingDomainProvider.class)) + .getEditingDomain(); + } else if (editor.getAdapter(EditingDomain.class) != null) { + domain = (EditingDomain) editor.getAdapter(EditingDomain.class); + } + if (domain == null) { + return null; + } + + return domain.getResourceSet(); + } + + /** + * @return The project which contains the file that is open in the last + * active editor in the current workbench page. + */ + public static IProject getLastActiveEditorProject() { + final IEditorPart editor = getLastActiveEditor(); + if (editor == null) + return null; + final IEditorInput editorInput = editor.getEditorInput(); + if (editorInput instanceof IFileEditorInput) { + final IFileEditorInput input = (IFileEditorInput) editorInput; + return input.getFile().getProject(); + } + return null; + } + + public void pageActivated(IWorkbenchPage page) { + this.activePage = page; + } + + public void pageClosed(IWorkbenchPage page) { + if (page == activePage) { + activePage = null; + } + lastActiveEditorId = null; + } + + public void pageOpened(IWorkbenchPage page) { + // do nothing + System.out.println("PAGE OPENED: " + page); + } + + public void partActivated(IWorkbenchPart part) { + if (part instanceof IEditorPart) { + setActiveEditor((IEditorPart) part); + } + } + + public void partBroughtToTop(IWorkbenchPart part) { + if (part instanceof IEditorPart) { + setActiveEditor((IEditorPart) part); + } + } + + public void partClosed(IWorkbenchPart part) { + if (part instanceof IEditorPart) { + String id = null; + for (Entry<String, IEditorPart> entry : activeEditors.entrySet()) { + if (entry.getValue().equals(part)) { + id = entry.getKey(); + break; + } + } + if (id != null) { + activeEditors.remove(id); + if (id.equals(lastActiveEditorId)) { + lastActiveEditorId = null; + } + } + } + } + + public void partDeactivated(IWorkbenchPart part) { + if (part instanceof IEditorPart) { + // do nothing + } + } + + private IEditorPart getLastActiveEditorInternal() { + if (activePage == null) { + initialize(PlatformUI.getWorkbench().getActiveWorkbenchWindow()); + if (activePage == null) + return null; + } + boolean updated = false; + if (lastActiveEditorId == null) { + final IEditorPart editor = activePage.getActiveEditor(); + if (editor != null) { + setActiveEditor(editor); + } + updated = true; + } + IEditorPart editor = getEditorById(lastActiveEditorId); + if (editor == null && !updated) { + editor = activePage.getActiveEditor(); + if (editor != null) { + setActiveEditor(editor); + } + } + return editor; + } + + private IEditorPart getEditorById(String editorId) { + if (activePage == null || editorId == null) + return null; + final IEditorPart editor = activeEditors.get(editorId); + final String id = checkEditorAndGetId(editor); + if (id != null && id.equals(editorId)) { + return editor; + } + return null; + } + + private String checkEditorAndGetId(IEditorPart editor) { + if (editor == null) + return null; + for (IEditorReference ref : activePage.getEditorReferences()) { + if (editor.equals(ref.getEditor(false))) { + return ref.getId(); + } + } + return null; + } + + public IWorkbenchPage getActivePage() { + return activePage; + } + + /** + * Set the active editor + */ + private void setActiveEditor(IEditorPart part) { + if (part == null) { + lastActiveEditorId = null; + return; + } + final IWorkbenchPartReference reference = activePage.getReference(part); + if (reference == null) + throw new IllegalStateException("Impossible?!"); + lastActiveEditorId = reference.getId(); + activeEditors.put(lastActiveEditorId, part); + } + + public void partOpened(IWorkbenchPart part) { + if (part instanceof IEditorPart) { + setActiveEditor((IEditorPart) part); + } + } + + public void dispose() { + if (workbenchWindow == null) { + return; + } + workbenchWindow.removePageListener(this); + workbenchWindow.getPartService().removePartListener(this); + workbenchWindow = null; + } + + public void windowActivated(IWorkbenchWindow window) { + initialize(window); + } + + protected void initialize(IWorkbenchWindow window) { + if (workbenchWindow != null && !workbenchWindow.equals(window)) { + /* + * TODO: implement logic for keeping track of editors in multiple + * workbench windows! + */ + } + this.workbenchWindow = window; + if (window == null) + return; + this.activePage = window.getActivePage(); + final IEditorPart editor = this.activePage.getActiveEditor(); + if (editor != null) { + lastActiveEditorId = checkEditorAndGetId(editor); + activeEditors.put(lastActiveEditorId, editor); + } + window.addPageListener(this); + window.getPartService().addPartListener(this); + } + + public void windowDeactivated(IWorkbenchWindow window) { + // not of interest + } + + public void windowClosed(IWorkbenchWindow window) { + dispose(); + } + + public void windowOpened(IWorkbenchWindow window) { + // not of interest + } }
\ No newline at end of file diff --git a/plugins/uml/xtext/org.eclipse.papyrus.uml.xtext.integration.ui/src/org/eclipse/papyrus/uml/xtext/integration/CompletionProposalAdapter.java b/plugins/uml/xtext/org.eclipse.papyrus.uml.xtext.integration.ui/src/org/eclipse/papyrus/uml/xtext/integration/CompletionProposalAdapter.java index 2c7ded5372b..fcdef840a5f 100644 --- a/plugins/uml/xtext/org.eclipse.papyrus.uml.xtext.integration.ui/src/org/eclipse/papyrus/uml/xtext/integration/CompletionProposalAdapter.java +++ b/plugins/uml/xtext/org.eclipse.papyrus.uml.xtext.integration.ui/src/org/eclipse/papyrus/uml/xtext/integration/CompletionProposalAdapter.java @@ -1,622 +1,636 @@ -package org.eclipse.papyrus.uml.xtext.integration;
-
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-
-import org.eclipse.core.runtime.ListenerList;
-import org.eclipse.jface.bindings.keys.KeyStroke;
-import org.eclipse.jface.fieldassist.ContentProposalAdapter;
-import org.eclipse.jface.text.contentassist.CompletionProposal;
-import org.eclipse.jface.text.contentassist.ContentAssistEvent;
-import org.eclipse.jface.text.contentassist.ContentAssistant;
-import org.eclipse.jface.text.contentassist.ICompletionListener;
-import org.eclipse.jface.text.contentassist.ICompletionProposal;
-import org.eclipse.jface.text.contentassist.IContentAssistant;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.widgets.Control;
-import org.eclipse.swt.widgets.Display;
-import org.eclipse.swt.widgets.Event;
-import org.eclipse.swt.widgets.Listener;
-
-/**
- * This is a stripped copy from {@link ContentProposalAdapter} that delegates
- * the popup to a {@link CompletionProposal} managed by a
- * {@link IContentAssistant}.
- *
- * @author patrick.koenemann@itemis.de
- *
- * ansgar.radermacher@cea.fr: added delayedIsPopupOpen()
- *
- */
-public class CompletionProposalAdapter implements ICompletionListener {
-
- private final IContentAssistant contentAssistant;
-
- protected boolean delayedIsOpen;
-
- /**
- * <p>
- * This adapter installs listener on the given control and delegates the
- * completion proposal popup request to the given {@link IContentAssistant}.
- * </p>
- *
- * <p>
- * FIXME: Parameter <code>autoActivationCharacters</code> is untested.
- * </p>
- *
- * @param control
- * @param contentAssistant
- * @param keyStroke
- * @param autoActivationCharacters
- */
- public CompletionProposalAdapter(Control control,
- IContentAssistant contentAssistant, KeyStroke keyStroke,
- char[] autoActivationCharacters) {
-
- this.control = control;
-
- this.triggerKeyStroke = keyStroke;
- if (autoActivationCharacters != null) {
- this.autoActivateString = new String(autoActivationCharacters);
- }
-
- this.contentAssistant = contentAssistant;
- addControlListener(control);
- addContentProposalListener(new ICompletionProposalListener() {
-
- public void proposalPopupOpened(CompletionProposalAdapter adapter) {
- }
-
- public void proposalPopupClosed(CompletionProposalAdapter adapter) {
- // reset open status asynchronously.
- Display.getDefault().asyncExec(new Runnable() {
-
- public void run() {
- delayedIsOpen = false;
- }
- });
- }
- });
- }
-
- /**
- * Flag that controls the printing of debug info.
- */
- public static final boolean DEBUG = false;
-
- /*
- * The control for which content proposals are provided.
- */
- private Control control;
-
- /*
- * The keystroke that signifies content proposals should be shown.
- */
- private KeyStroke triggerKeyStroke;
-
- /*
- * The String containing characters that auto-activate the popup.
- */
- private String autoActivateString;
-
- /*
- * The listener we install on the control.
- */
- private Listener controlListener;
-
- /*
- * The list of IContentProposalListener2 listeners.
- */
- private ListenerList proposalListeners2 = new ListenerList();
-
- /*
- * Flag that indicates whether the adapter is enabled. In some cases,
- * adapters may be installed but depend upon outside state.
- */
- private boolean isEnabled = true;
-
- /*
- * The delay in milliseconds used when autoactivating the popup.
- */
- private int autoActivationDelay = 0;
-
- /*
- * A boolean indicating whether a keystroke has been received. Used to see
- * if an autoactivation delay was interrupted by a keystroke.
- */
- private boolean receivedKeyDown;
-
- /**
- * Get the control on which the content proposal adapter is installed.
- *
- * @return the control on which the proposal adapter is installed.
- */
- public Control getControl() {
- return control;
- }
-
- /**
- * Return a boolean indicating whether the receiver is enabled.
- *
- * @return <code>true</code> if the adapter is enabled, and
- * <code>false</code> if it is not.
- */
- public boolean isEnabled() {
- return isEnabled;
- }
-
- /**
- * Return the array of characters on which the popup is autoactivated.
- *
- * @return An array of characters that trigger auto-activation of content
- * proposal. If specified, these characters will trigger
- * auto-activation of the proposal popup, regardless of whether an
- * explicit invocation keyStroke was specified. If this parameter is
- * <code>null</code>, then only a specified keyStroke will invoke
- * content proposal. If this value is <code>null</code> and the
- * keyStroke value is <code>null</code>, then all alphanumeric
- * characters will auto-activate content proposal.
- */
- public char[] getAutoActivationCharacters() {
- if (autoActivateString == null) {
- return null;
- }
- return autoActivateString.toCharArray();
- }
-
- /**
- * Set the array of characters that will trigger autoactivation of the
- * popup.
- *
- * @param autoActivationCharacters
- * An array of characters that trigger auto-activation of content
- * proposal. If specified, these characters will trigger
- * auto-activation of the proposal popup, regardless of whether
- * an explicit invocation keyStroke was specified. If this
- * parameter is <code>null</code>, then only a specified
- * keyStroke will invoke content proposal. If this parameter is
- * <code>null</code> and the keyStroke value is <code>null</code>
- * , then all alphanumeric characters will auto-activate content
- * proposal.
- *
- */
- public void setAutoActivationCharacters(char[] autoActivationCharacters) {
- if (autoActivationCharacters == null) {
- this.autoActivateString = null;
- } else {
- this.autoActivateString = new String(autoActivationCharacters);
- }
- }
-
- /**
- * Set the delay, in milliseconds, used before any autoactivation is
- * triggered.
- *
- * @return the time in milliseconds that will pass before a popup is
- * automatically opened
- */
- public int getAutoActivationDelay() {
- return autoActivationDelay;
-
- }
-
- /**
- * Set the delay, in milliseconds, used before autoactivation is triggered.
- *
- * @param delay
- * the time in milliseconds that will pass before a popup is
- * automatically opened
- */
- public void setAutoActivationDelay(int delay) {
- autoActivationDelay = delay;
-
- }
-
- /**
- * Set the boolean flag that determines whether the adapter is enabled.
- *
- * @param enabled
- * <code>true</code> if the adapter is enabled and responding to
- * user input, <code>false</code> if it is ignoring user input.
- *
- */
- public void setEnabled(boolean enabled) {
- // If we are disabling it while it's proposing content, close the
- // content proposal popup.
- if (isEnabled && !enabled) {
- // if (popup != null) {
- // popup.close();
- // }
- }
- isEnabled = enabled;
- }
-
- /**
- * Add the specified listener to the list of content proposal listeners that
- * are notified when a content proposal popup is opened or closed.
- */
- public void addContentProposalListener(ICompletionProposalListener listener) {
- proposalListeners2.add(listener);
- }
-
- /**
- * Remove the specified listener from the list of content proposal listeners
- * that are notified when a content proposal popup is opened or closed.
- */
- public void removeContentProposalListener(
- ICompletionProposalListener listener) {
- proposalListeners2.remove(listener);
- }
-
- /*
- * Add our listener to the control. Debug information to be left in until
- * this support is stable on all platforms.
- */
- private void addControlListener(Control control) {
- if (DEBUG) {
- System.out
- .println("ContentProposalListener#installControlListener()"); //$NON-NLS-1$
- }
-
- if (controlListener != null) {
- return;
- }
- controlListener = new Listener() {
- public void handleEvent(Event e) {
- if (!isEnabled) {
- return;
- }
-
- switch (e.type) {
- case SWT.Traverse:
- case SWT.KeyDown:
- if (DEBUG) {
- StringBuffer sb;
- if (e.type == SWT.Traverse) {
- sb = new StringBuffer("Traverse"); //$NON-NLS-1$
- } else {
- sb = new StringBuffer("KeyDown"); //$NON-NLS-1$
- }
- sb.append(" received by adapter"); //$NON-NLS-1$
- dump(sb.toString(), e);
- }
- // If the popup is open, it gets first shot at the
- // keystroke and should set the doit flags appropriately.
- // if (popup != null) {
- // popup.getTargetControlListener().handleEvent(e);
- // if (DEBUG) {
- // StringBuffer sb;
- // if (e.type == SWT.Traverse) {
- // sb = new StringBuffer("Traverse"); //$NON-NLS-1$
- // } else {
- // sb = new StringBuffer("KeyDown"); //$NON-NLS-1$
- // }
- // sb.append(" after being handled by popup"); //$NON-NLS-1$
- // dump(sb.toString(), e);
- // }
- // // See
- // https://bugs.eclipse.org/bugs/show_bug.cgi?id=192633
- // // If the popup is open and this is a valid character, we
- // // want to watch for the modified text.
- // if (propagateKeys && e.character != 0)
- // watchModify = true;
- //
- // return;
- // }
-
- // We were only listening to traverse events for the popup
- if (e.type == SWT.Traverse) {
- return;
- }
-
- // The popup is not open. We are looking at keydown events
- // for a trigger to open the popup.
- if (triggerKeyStroke != null) {
- // Either there are no modifiers for the trigger and we
- // check the character field...
- if ((triggerKeyStroke.getModifierKeys() == KeyStroke.NO_KEY && triggerKeyStroke
- .getNaturalKey() == e.character) ||
- // ...or there are modifiers, in which case the
- // keycode and state must match
- (triggerKeyStroke.getNaturalKey() == e.keyCode && ((triggerKeyStroke
- .getModifierKeys() & e.stateMask) == triggerKeyStroke
- .getModifierKeys()))) {
- // We never propagate the keystroke for an explicit
- // keystroke invocation of the popup
- e.doit = false;
- openProposalPopup(false);
- return;
- }
- }
- /*
- * The triggering keystroke was not invoked. If a character
- * was typed, compare it to the autoactivation characters.
- */
- if (e.character != 0) {
- if (autoActivateString != null) {
- if (autoActivateString.indexOf(e.character) >= 0) {
- autoActivate();
- } else {
- // No autoactivation occurred, so record the key
- // down as a means to interrupt any
- // autoactivation that is pending due to
- // autoactivation delay.
- receivedKeyDown = true;
- // watch the modify so we can close the popup in
- // cases where there is no longer a trigger
- // character in the content
- // watchModify = true;
- }
- } else {
- // The autoactivate string is null. If the trigger
- // is also null, we want to act on any modification
- // to the content. Set a flag so we'll catch this
- // in the modify event.
- if (triggerKeyStroke == null) {
- // watchModify = true;
- }
- }
- } else {
- // A non-character key has been pressed. Interrupt any
- // autoactivation that is pending due to autoactivation
- // delay.
- receivedKeyDown = true;
- }
- break;
-
- // There are times when we want to monitor content changes
- // rather than individual keystrokes to determine whether
- // the popup should be closed or opened based on the entire
- // content of the control.
- // The watchModify flag ensures that we don't autoactivate if
- // the content change was caused by something other than typing.
- // See https://bugs.eclipse.org/bugs/show_bug.cgi?id=183650
- // case SWT.Modify:
- // if (allowsAutoActivate() && watchModify) {
- // if (DEBUG) {
- // dump("Modify event triggers popup open or close", e); //$NON-NLS-1$
- // }
- // watchModify = false;
- // // We are in autoactivation mode, either for specific
- // // characters or for all characters. In either case,
- // // we should close the proposal popup when there is no
- // // content in the control.
- // if (isControlContentEmpty()) {
- // // see https://bugs.eclipse.org/bugs/show_bug.cgi?id=192633
- // closeProposalPopup();
- // } else {
- // // See https://bugs.eclipse.org/bugs/show_bug.cgi?id=147377
- // // Given that we will close the popup when there are
- // // no valid proposals, we must consider reopening it on any
- // // content change when there are no particular autoActivation
- // // characters
- // if (autoActivateString == null) {
- // autoActivate();
- // } else {
- // // Autoactivation characters are defined, but this
- // // modify event does not involve one of them. See
- // // if any of the autoactivation characters are left
- // // in the content and close the popup if none remain.
- // if (!shouldPopupRemainOpen())
- // closeProposalPopup();
- // }
- // }
- // }
- // break;
- default:
- break;
- }
- }
-
- /**
- * Dump the given events to "standard" output.
- *
- * @param who
- * who is dumping the event
- * @param e
- * the event
- */
- private void dump(String who, Event e) {
- StringBuffer sb = new StringBuffer(
- "--- [ContentProposalAdapter]\n"); //$NON-NLS-1$
- sb.append(who);
- sb.append(" - e: keyCode=" + e.keyCode + hex(e.keyCode)); //$NON-NLS-1$
- sb.append("; character=" + e.character + hex(e.character)); //$NON-NLS-1$
- sb.append("; stateMask=" + e.stateMask + hex(e.stateMask)); //$NON-NLS-1$
- sb.append("; doit=" + e.doit); //$NON-NLS-1$
- sb.append("; detail=" + e.detail + hex(e.detail)); //$NON-NLS-1$
- sb.append("; widget=" + e.widget); //$NON-NLS-1$
- System.out.println(sb);
- }
-
- private String hex(int i) {
- return "[0x" + Integer.toHexString(i) + ']'; //$NON-NLS-1$
- }
- };
- control.addListener(SWT.KeyDown, controlListener);
- control.addListener(SWT.Traverse, controlListener);
- control.addListener(SWT.Modify, controlListener);
-
- if (DEBUG) {
- System.out
- .println("ContentProposalAdapter#installControlListener() - installed"); //$NON-NLS-1$
- }
- }
-
- /**
- * Open the proposal popup and display the proposals provided by the
- * proposal provider. If there are no proposals to be shown, do not show the
- * popup. This method returns immediately. That is, it does not wait for the
- * popup to open or a proposal to be selected.
- *
- * @param autoActivated
- * a boolean indicating whether the popup was autoactivated. If
- * false, a beep will sound when no proposals can be shown.
- */
- private void openProposalPopup(boolean autoActivated) {
- if (isValid() && isEnabled()) {
-
- delayedIsOpen = true;
- // XXX here we delegate the request!
- contentAssistant.showPossibleCompletions();
-
- ((ContentAssistant) contentAssistant).addCompletionListener(this);
- }
- }
-
- /**
- * Open the proposal popup and display the proposals provided by the
- * proposal provider. This method returns immediately. That is, it does not
- * wait for a proposal to be selected. This method is used by subclasses to
- * explicitly invoke the opening of the popup. If there are no proposals to
- * show, the popup will not open and a beep will be sounded.
- */
- protected void openProposalPopup() {
- openProposalPopup(false);
- }
-
- /*
- * Check that the control and content adapter are valid.
- */
- private boolean isValid() {
- return control != null && !control.isDisposed();
- }
-
- /**
- * Autoactivation has been triggered. Open the popup using any specified
- * delay.
- */
- private void autoActivate() {
- if (autoActivationDelay > 0) {
- Runnable runnable = new Runnable() {
- public void run() {
- receivedKeyDown = false;
- try {
- Thread.sleep(autoActivationDelay);
- } catch (InterruptedException e) {
- }
- if (!isValid() || receivedKeyDown) {
- return;
- }
- getControl().getDisplay().syncExec(new Runnable() {
- public void run() {
- openProposalPopup(true);
- }
- });
- }
- };
- Thread t = new Thread(runnable);
- t.start();
- } else {
- // Since we do not sleep, we must open the popup
- // in an async exec. This is necessary because
- // this method may be called in the middle of handling
- // some event that will cause the cursor position or
- // other important info to change as a result of this
- // event occurring.
- getControl().getDisplay().asyncExec(new Runnable() {
- public void run() {
- if (isValid()) {
- openProposalPopup(true);
- }
- }
- });
- }
- }
-
- /*
- * The proposal popup has opened. Notify interested listeners.
- */
- private void notifyPopupOpened() {
- if (DEBUG) {
- System.out.println("Notify listeners - popup opened."); //$NON-NLS-1$
- }
- final Object[] listenerArray = proposalListeners2.getListeners();
- for (int i = 0; i < listenerArray.length; i++) {
- ((ICompletionProposalListener) listenerArray[i])
- .proposalPopupOpened(this);
- }
- }
-
- /*
- * The proposal popup has closed. Notify interested listeners.
- */
- private void notifyPopupClosed() {
- if (DEBUG) {
- System.out.println("Notify listeners - popup closed."); //$NON-NLS-1$
- }
- final Object[] listenerArray = proposalListeners2.getListeners();
- for (int i = 0; i < listenerArray.length; i++) {
- ((ICompletionProposalListener) listenerArray[i])
- .proposalPopupClosed(this);
- }
- }
-
- /**
- * Answers a boolean indicating whether the main proposal popup is open.
- *
- * @return <code>true</code> if the proposal popup is open, and
- * <code>false</code> if it is not.
- *
- * @since 3.6
- */
- public boolean isProposalPopupOpen() {
- if (isValid() && isProposalPopupActive())
- return true;
- return false;
- }
-
- /**
- * @return true, if popup is open. Flag whether is reset asynchronously.
- * This means that it still returns true, when isProposalPopupOpen() already
- * returns false. This is useful to filter for instance the escape key
- * once the popup is open.
- */
- public boolean delayedIsPopupOpen() {
- return delayedIsOpen;
- }
-
- /**
- * @return <code>true</code> if the content assistant has the completion
- * proposal popup open; <code>false</code> otherwise.
- */
- private boolean isProposalPopupActive() {
- /*
- * Unfortunately, the method is protected so we use java reflection to
- * access it.
- */
- try {
- final Method m = ContentAssistant.class
- .getDeclaredMethod("isProposalPopupActive"); //$NON-NLS-1$
- m.setAccessible(true);
- try {
- final Object result = m.invoke(contentAssistant);
- if (result != null && result instanceof Boolean) {
- return (Boolean) result;
- } else {
- throw new IllegalStateException(
- "Method is expected to return boolean!");
- }
- } catch (InvocationTargetException e) {
- throw e.getCause(); // cause was thrown by method m.
- }
- } catch (Throwable e) {
- e.printStackTrace();
- return false;
- }
- }
-
- public void assistSessionStarted(ContentAssistEvent event) {
- notifyPopupOpened();
- }
-
- public void assistSessionEnded(ContentAssistEvent event) {
- notifyPopupClosed();
- }
-
- public void selectionChanged(ICompletionProposal proposal,
- boolean smartToggle) {
- // do nothing
- }
-
-}
+/***************************************************************************** +* Copyright (c) 2010 CEA LIST. +* +* + * 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: +* Itemis - Initial API and implementation +* +*****************************************************************************/ + +package org.eclipse.papyrus.uml.xtext.integration; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +import org.eclipse.core.runtime.ListenerList; +import org.eclipse.jface.bindings.keys.KeyStroke; +import org.eclipse.jface.fieldassist.ContentProposalAdapter; +import org.eclipse.jface.text.contentassist.CompletionProposal; +import org.eclipse.jface.text.contentassist.ContentAssistEvent; +import org.eclipse.jface.text.contentassist.ContentAssistant; +import org.eclipse.jface.text.contentassist.ICompletionListener; +import org.eclipse.jface.text.contentassist.ICompletionProposal; +import org.eclipse.jface.text.contentassist.IContentAssistant; +import org.eclipse.swt.SWT; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Event; +import org.eclipse.swt.widgets.Listener; + +/** + * This is a stripped copy from {@link ContentProposalAdapter} that delegates + * the popup to a {@link CompletionProposal} managed by a + * {@link IContentAssistant}. + * + * @author patrick.koenemann@itemis.de + * + * ansgar.radermacher@cea.fr: added delayedIsPopupOpen() + * + */ +public class CompletionProposalAdapter implements ICompletionListener { + + private final IContentAssistant contentAssistant; + + protected boolean delayedIsOpen; + + /** + * <p> + * This adapter installs listener on the given control and delegates the + * completion proposal popup request to the given {@link IContentAssistant}. + * </p> + * + * <p> + * FIXME: Parameter <code>autoActivationCharacters</code> is untested. + * </p> + * + * @param control + * @param contentAssistant + * @param keyStroke + * @param autoActivationCharacters + */ + public CompletionProposalAdapter(Control control, + IContentAssistant contentAssistant, KeyStroke keyStroke, + char[] autoActivationCharacters) { + + this.control = control; + + this.triggerKeyStroke = keyStroke; + if (autoActivationCharacters != null) { + this.autoActivateString = new String(autoActivationCharacters); + } + + this.contentAssistant = contentAssistant; + addControlListener(control); + addContentProposalListener(new ICompletionProposalListener() { + + public void proposalPopupOpened(CompletionProposalAdapter adapter) { + } + + public void proposalPopupClosed(CompletionProposalAdapter adapter) { + // reset open status asynchronously. + Display.getDefault().asyncExec(new Runnable() { + + public void run() { + delayedIsOpen = false; + } + }); + } + }); + } + + /** + * Flag that controls the printing of debug info. + */ + public static final boolean DEBUG = false; + + /* + * The control for which content proposals are provided. + */ + private Control control; + + /* + * The keystroke that signifies content proposals should be shown. + */ + private KeyStroke triggerKeyStroke; + + /* + * The String containing characters that auto-activate the popup. + */ + private String autoActivateString; + + /* + * The listener we install on the control. + */ + private Listener controlListener; + + /* + * The list of IContentProposalListener2 listeners. + */ + private ListenerList proposalListeners2 = new ListenerList(); + + /* + * Flag that indicates whether the adapter is enabled. In some cases, + * adapters may be installed but depend upon outside state. + */ + private boolean isEnabled = true; + + /* + * The delay in milliseconds used when autoactivating the popup. + */ + private int autoActivationDelay = 0; + + /* + * A boolean indicating whether a keystroke has been received. Used to see + * if an autoactivation delay was interrupted by a keystroke. + */ + private boolean receivedKeyDown; + + /** + * Get the control on which the content proposal adapter is installed. + * + * @return the control on which the proposal adapter is installed. + */ + public Control getControl() { + return control; + } + + /** + * Return a boolean indicating whether the receiver is enabled. + * + * @return <code>true</code> if the adapter is enabled, and + * <code>false</code> if it is not. + */ + public boolean isEnabled() { + return isEnabled; + } + + /** + * Return the array of characters on which the popup is autoactivated. + * + * @return An array of characters that trigger auto-activation of content + * proposal. If specified, these characters will trigger + * auto-activation of the proposal popup, regardless of whether an + * explicit invocation keyStroke was specified. If this parameter is + * <code>null</code>, then only a specified keyStroke will invoke + * content proposal. If this value is <code>null</code> and the + * keyStroke value is <code>null</code>, then all alphanumeric + * characters will auto-activate content proposal. + */ + public char[] getAutoActivationCharacters() { + if (autoActivateString == null) { + return null; + } + return autoActivateString.toCharArray(); + } + + /** + * Set the array of characters that will trigger autoactivation of the + * popup. + * + * @param autoActivationCharacters + * An array of characters that trigger auto-activation of content + * proposal. If specified, these characters will trigger + * auto-activation of the proposal popup, regardless of whether + * an explicit invocation keyStroke was specified. If this + * parameter is <code>null</code>, then only a specified + * keyStroke will invoke content proposal. If this parameter is + * <code>null</code> and the keyStroke value is <code>null</code> + * , then all alphanumeric characters will auto-activate content + * proposal. + * + */ + public void setAutoActivationCharacters(char[] autoActivationCharacters) { + if (autoActivationCharacters == null) { + this.autoActivateString = null; + } else { + this.autoActivateString = new String(autoActivationCharacters); + } + } + + /** + * Set the delay, in milliseconds, used before any autoactivation is + * triggered. + * + * @return the time in milliseconds that will pass before a popup is + * automatically opened + */ + public int getAutoActivationDelay() { + return autoActivationDelay; + + } + + /** + * Set the delay, in milliseconds, used before autoactivation is triggered. + * + * @param delay + * the time in milliseconds that will pass before a popup is + * automatically opened + */ + public void setAutoActivationDelay(int delay) { + autoActivationDelay = delay; + + } + + /** + * Set the boolean flag that determines whether the adapter is enabled. + * + * @param enabled + * <code>true</code> if the adapter is enabled and responding to + * user input, <code>false</code> if it is ignoring user input. + * + */ + public void setEnabled(boolean enabled) { + // If we are disabling it while it's proposing content, close the + // content proposal popup. + if (isEnabled && !enabled) { + // if (popup != null) { + // popup.close(); + // } + } + isEnabled = enabled; + } + + /** + * Add the specified listener to the list of content proposal listeners that + * are notified when a content proposal popup is opened or closed. + */ + public void addContentProposalListener(ICompletionProposalListener listener) { + proposalListeners2.add(listener); + } + + /** + * Remove the specified listener from the list of content proposal listeners + * that are notified when a content proposal popup is opened or closed. + */ + public void removeContentProposalListener( + ICompletionProposalListener listener) { + proposalListeners2.remove(listener); + } + + /* + * Add our listener to the control. Debug information to be left in until + * this support is stable on all platforms. + */ + private void addControlListener(Control control) { + if (DEBUG) { + System.out + .println("ContentProposalListener#installControlListener()"); //$NON-NLS-1$ + } + + if (controlListener != null) { + return; + } + controlListener = new Listener() { + public void handleEvent(Event e) { + if (!isEnabled) { + return; + } + + switch (e.type) { + case SWT.Traverse: + case SWT.KeyDown: + if (DEBUG) { + StringBuffer sb; + if (e.type == SWT.Traverse) { + sb = new StringBuffer("Traverse"); //$NON-NLS-1$ + } else { + sb = new StringBuffer("KeyDown"); //$NON-NLS-1$ + } + sb.append(" received by adapter"); //$NON-NLS-1$ + dump(sb.toString(), e); + } + // If the popup is open, it gets first shot at the + // keystroke and should set the doit flags appropriately. + // if (popup != null) { + // popup.getTargetControlListener().handleEvent(e); + // if (DEBUG) { + // StringBuffer sb; + // if (e.type == SWT.Traverse) { + // sb = new StringBuffer("Traverse"); //$NON-NLS-1$ + // } else { + // sb = new StringBuffer("KeyDown"); //$NON-NLS-1$ + // } + // sb.append(" after being handled by popup"); //$NON-NLS-1$ + // dump(sb.toString(), e); + // } + // // See + // https://bugs.eclipse.org/bugs/show_bug.cgi?id=192633 + // // If the popup is open and this is a valid character, we + // // want to watch for the modified text. + // if (propagateKeys && e.character != 0) + // watchModify = true; + // + // return; + // } + + // We were only listening to traverse events for the popup + if (e.type == SWT.Traverse) { + return; + } + + // The popup is not open. We are looking at keydown events + // for a trigger to open the popup. + if (triggerKeyStroke != null) { + // Either there are no modifiers for the trigger and we + // check the character field... + if ((triggerKeyStroke.getModifierKeys() == KeyStroke.NO_KEY && triggerKeyStroke + .getNaturalKey() == e.character) || + // ...or there are modifiers, in which case the + // keycode and state must match + (triggerKeyStroke.getNaturalKey() == e.keyCode && ((triggerKeyStroke + .getModifierKeys() & e.stateMask) == triggerKeyStroke + .getModifierKeys()))) { + // We never propagate the keystroke for an explicit + // keystroke invocation of the popup + e.doit = false; + openProposalPopup(false); + return; + } + } + /* + * The triggering keystroke was not invoked. If a character + * was typed, compare it to the autoactivation characters. + */ + if (e.character != 0) { + if (autoActivateString != null) { + if (autoActivateString.indexOf(e.character) >= 0) { + autoActivate(); + } else { + // No autoactivation occurred, so record the key + // down as a means to interrupt any + // autoactivation that is pending due to + // autoactivation delay. + receivedKeyDown = true; + // watch the modify so we can close the popup in + // cases where there is no longer a trigger + // character in the content + // watchModify = true; + } + } else { + // The autoactivate string is null. If the trigger + // is also null, we want to act on any modification + // to the content. Set a flag so we'll catch this + // in the modify event. + if (triggerKeyStroke == null) { + // watchModify = true; + } + } + } else { + // A non-character key has been pressed. Interrupt any + // autoactivation that is pending due to autoactivation + // delay. + receivedKeyDown = true; + } + break; + + // There are times when we want to monitor content changes + // rather than individual keystrokes to determine whether + // the popup should be closed or opened based on the entire + // content of the control. + // The watchModify flag ensures that we don't autoactivate if + // the content change was caused by something other than typing. + // See https://bugs.eclipse.org/bugs/show_bug.cgi?id=183650 + // case SWT.Modify: + // if (allowsAutoActivate() && watchModify) { + // if (DEBUG) { + // dump("Modify event triggers popup open or close", e); //$NON-NLS-1$ + // } + // watchModify = false; + // // We are in autoactivation mode, either for specific + // // characters or for all characters. In either case, + // // we should close the proposal popup when there is no + // // content in the control. + // if (isControlContentEmpty()) { + // // see https://bugs.eclipse.org/bugs/show_bug.cgi?id=192633 + // closeProposalPopup(); + // } else { + // // See https://bugs.eclipse.org/bugs/show_bug.cgi?id=147377 + // // Given that we will close the popup when there are + // // no valid proposals, we must consider reopening it on any + // // content change when there are no particular autoActivation + // // characters + // if (autoActivateString == null) { + // autoActivate(); + // } else { + // // Autoactivation characters are defined, but this + // // modify event does not involve one of them. See + // // if any of the autoactivation characters are left + // // in the content and close the popup if none remain. + // if (!shouldPopupRemainOpen()) + // closeProposalPopup(); + // } + // } + // } + // break; + default: + break; + } + } + + /** + * Dump the given events to "standard" output. + * + * @param who + * who is dumping the event + * @param e + * the event + */ + private void dump(String who, Event e) { + StringBuffer sb = new StringBuffer( + "--- [ContentProposalAdapter]\n"); //$NON-NLS-1$ + sb.append(who); + sb.append(" - e: keyCode=" + e.keyCode + hex(e.keyCode)); //$NON-NLS-1$ + sb.append("; character=" + e.character + hex(e.character)); //$NON-NLS-1$ + sb.append("; stateMask=" + e.stateMask + hex(e.stateMask)); //$NON-NLS-1$ + sb.append("; doit=" + e.doit); //$NON-NLS-1$ + sb.append("; detail=" + e.detail + hex(e.detail)); //$NON-NLS-1$ + sb.append("; widget=" + e.widget); //$NON-NLS-1$ + System.out.println(sb); + } + + private String hex(int i) { + return "[0x" + Integer.toHexString(i) + ']'; //$NON-NLS-1$ + } + }; + control.addListener(SWT.KeyDown, controlListener); + control.addListener(SWT.Traverse, controlListener); + control.addListener(SWT.Modify, controlListener); + + if (DEBUG) { + System.out + .println("ContentProposalAdapter#installControlListener() - installed"); //$NON-NLS-1$ + } + } + + /** + * Open the proposal popup and display the proposals provided by the + * proposal provider. If there are no proposals to be shown, do not show the + * popup. This method returns immediately. That is, it does not wait for the + * popup to open or a proposal to be selected. + * + * @param autoActivated + * a boolean indicating whether the popup was autoactivated. If + * false, a beep will sound when no proposals can be shown. + */ + private void openProposalPopup(boolean autoActivated) { + if (isValid() && isEnabled()) { + + delayedIsOpen = true; + // XXX here we delegate the request! + contentAssistant.showPossibleCompletions(); + + ((ContentAssistant) contentAssistant).addCompletionListener(this); + } + } + + /** + * Open the proposal popup and display the proposals provided by the + * proposal provider. This method returns immediately. That is, it does not + * wait for a proposal to be selected. This method is used by subclasses to + * explicitly invoke the opening of the popup. If there are no proposals to + * show, the popup will not open and a beep will be sounded. + */ + protected void openProposalPopup() { + openProposalPopup(false); + } + + /* + * Check that the control and content adapter are valid. + */ + private boolean isValid() { + return control != null && !control.isDisposed(); + } + + /** + * Autoactivation has been triggered. Open the popup using any specified + * delay. + */ + private void autoActivate() { + if (autoActivationDelay > 0) { + Runnable runnable = new Runnable() { + public void run() { + receivedKeyDown = false; + try { + Thread.sleep(autoActivationDelay); + } catch (InterruptedException e) { + } + if (!isValid() || receivedKeyDown) { + return; + } + getControl().getDisplay().syncExec(new Runnable() { + public void run() { + openProposalPopup(true); + } + }); + } + }; + Thread t = new Thread(runnable); + t.start(); + } else { + // Since we do not sleep, we must open the popup + // in an async exec. This is necessary because + // this method may be called in the middle of handling + // some event that will cause the cursor position or + // other important info to change as a result of this + // event occurring. + getControl().getDisplay().asyncExec(new Runnable() { + public void run() { + if (isValid()) { + openProposalPopup(true); + } + } + }); + } + } + + /* + * The proposal popup has opened. Notify interested listeners. + */ + private void notifyPopupOpened() { + if (DEBUG) { + System.out.println("Notify listeners - popup opened."); //$NON-NLS-1$ + } + final Object[] listenerArray = proposalListeners2.getListeners(); + for (int i = 0; i < listenerArray.length; i++) { + ((ICompletionProposalListener) listenerArray[i]) + .proposalPopupOpened(this); + } + } + + /* + * The proposal popup has closed. Notify interested listeners. + */ + private void notifyPopupClosed() { + if (DEBUG) { + System.out.println("Notify listeners - popup closed."); //$NON-NLS-1$ + } + final Object[] listenerArray = proposalListeners2.getListeners(); + for (int i = 0; i < listenerArray.length; i++) { + ((ICompletionProposalListener) listenerArray[i]) + .proposalPopupClosed(this); + } + } + + /** + * Answers a boolean indicating whether the main proposal popup is open. + * + * @return <code>true</code> if the proposal popup is open, and + * <code>false</code> if it is not. + * + * @since 3.6 + */ + public boolean isProposalPopupOpen() { + if (isValid() && isProposalPopupActive()) + return true; + return false; + } + + /** + * @return true, if popup is open. Flag whether is reset asynchronously. + * This means that it still returns true, when isProposalPopupOpen() already + * returns false. This is useful to filter for instance the escape key + * once the popup is open. + */ + public boolean delayedIsPopupOpen() { + return delayedIsOpen; + } + + /** + * @return <code>true</code> if the content assistant has the completion + * proposal popup open; <code>false</code> otherwise. + */ + private boolean isProposalPopupActive() { + /* + * Unfortunately, the method is protected so we use java reflection to + * access it. + */ + try { + final Method m = ContentAssistant.class + .getDeclaredMethod("isProposalPopupActive"); //$NON-NLS-1$ + m.setAccessible(true); + try { + final Object result = m.invoke(contentAssistant); + if (result != null && result instanceof Boolean) { + return (Boolean) result; + } else { + throw new IllegalStateException( + "Method is expected to return boolean!"); + } + } catch (InvocationTargetException e) { + throw e.getCause(); // cause was thrown by method m. + } + } catch (Throwable e) { + e.printStackTrace(); + return false; + } + } + + public void assistSessionStarted(ContentAssistEvent event) { + notifyPopupOpened(); + } + + public void assistSessionEnded(ContentAssistEvent event) { + notifyPopupClosed(); + } + + public void selectionChanged(ICompletionProposal proposal, + boolean smartToggle) { + // do nothing + } + +} diff --git a/plugins/uml/xtext/org.eclipse.papyrus.uml.xtext.integration.ui/src/org/eclipse/papyrus/uml/xtext/integration/ICompletionProposalListener.java b/plugins/uml/xtext/org.eclipse.papyrus.uml.xtext.integration.ui/src/org/eclipse/papyrus/uml/xtext/integration/ICompletionProposalListener.java index 16bac79d14a..315a720886b 100644 --- a/plugins/uml/xtext/org.eclipse.papyrus.uml.xtext.integration.ui/src/org/eclipse/papyrus/uml/xtext/integration/ICompletionProposalListener.java +++ b/plugins/uml/xtext/org.eclipse.papyrus.uml.xtext.integration.ui/src/org/eclipse/papyrus/uml/xtext/integration/ICompletionProposalListener.java @@ -1,29 +1,43 @@ -package org.eclipse.papyrus.uml.xtext.integration;
-
-
-/**
- * This interface is used to listen to additional notifications from a
- * {@link CompletionProposalAdapter}.
- *
- * @author patrick.koenemann@itemis.de
- *
- */
-public interface ICompletionProposalListener {
- /**
- * A completion proposal popup has been opened.
- *
- * @param adapter
- * the CompletionProposalAdapter which is adapting content proposal
- * behavior to a control
- */
- public void proposalPopupOpened(CompletionProposalAdapter adapter);
-
- /**
- * A completion proposal popup has been closed.
- *
- * @param adapter
- * the CompletionProposalAdapter which is adapting content proposal
- * behavior to a control
- */
- public void proposalPopupClosed(CompletionProposalAdapter adapter);
+/***************************************************************************** +* Copyright (c) 2010 CEA LIST. +* +* + * 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: +* Itemis - Initial API and implementation +* +*****************************************************************************/ + +package org.eclipse.papyrus.uml.xtext.integration; + + +/** + * This interface is used to listen to additional notifications from a + * {@link CompletionProposalAdapter}. + * + * @author patrick.koenemann@itemis.de + * + */ +public interface ICompletionProposalListener { + /** + * A completion proposal popup has been opened. + * + * @param adapter + * the CompletionProposalAdapter which is adapting content proposal + * behavior to a control + */ + public void proposalPopupOpened(CompletionProposalAdapter adapter); + + /** + * A completion proposal popup has been closed. + * + * @param adapter + * the CompletionProposalAdapter which is adapting content proposal + * behavior to a control + */ + public void proposalPopupClosed(CompletionProposalAdapter adapter); }
\ No newline at end of file diff --git a/plugins/uml/xtext/org.eclipse.papyrus.uml.xtext.integration.ui/src/org/eclipse/papyrus/uml/xtext/integration/StyledTextXtextAdapter.java b/plugins/uml/xtext/org.eclipse.papyrus.uml.xtext.integration.ui/src/org/eclipse/papyrus/uml/xtext/integration/StyledTextXtextAdapter.java index ba3994802da..5906f00e424 100644 --- a/plugins/uml/xtext/org.eclipse.papyrus.uml.xtext.integration.ui/src/org/eclipse/papyrus/uml/xtext/integration/StyledTextXtextAdapter.java +++ b/plugins/uml/xtext/org.eclipse.papyrus.uml.xtext.integration.ui/src/org/eclipse/papyrus/uml/xtext/integration/StyledTextXtextAdapter.java @@ -1,3 +1,16 @@ +/***************************************************************************** +* Copyright (c) 2010 CEA LIST. +* +* + * 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: +* Itemis - Initial API and implementation +* +*****************************************************************************/ package org.eclipse.papyrus.uml.xtext.integration; import java.io.IOException; diff --git a/plugins/uml/xtext/org.eclipse.papyrus.uml.xtext.integration.ui/src/org/eclipse/papyrus/uml/xtext/integration/XtextFakeResourceContext.java b/plugins/uml/xtext/org.eclipse.papyrus.uml.xtext.integration.ui/src/org/eclipse/papyrus/uml/xtext/integration/XtextFakeResourceContext.java index 608aa52351f..18c7d704898 100644 --- a/plugins/uml/xtext/org.eclipse.papyrus.uml.xtext.integration.ui/src/org/eclipse/papyrus/uml/xtext/integration/XtextFakeResourceContext.java +++ b/plugins/uml/xtext/org.eclipse.papyrus.uml.xtext.integration.ui/src/org/eclipse/papyrus/uml/xtext/integration/XtextFakeResourceContext.java @@ -1,3 +1,17 @@ +/***************************************************************************** +* Copyright (c) 2010 CEA LIST. +* +* + * 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: +* Itemis - Initial API and implementation +* +*****************************************************************************/ + package org.eclipse.papyrus.uml.xtext.integration; import java.util.ArrayList; diff --git a/plugins/uml/xtext/org.eclipse.papyrus.uml.xtext.integration.ui/src/org/eclipse/papyrus/uml/xtext/integration/XtextSourceViewerEx.java b/plugins/uml/xtext/org.eclipse.papyrus.uml.xtext.integration.ui/src/org/eclipse/papyrus/uml/xtext/integration/XtextSourceViewerEx.java index c5bb4775f91..2abad008252 100644 --- a/plugins/uml/xtext/org.eclipse.papyrus.uml.xtext.integration.ui/src/org/eclipse/papyrus/uml/xtext/integration/XtextSourceViewerEx.java +++ b/plugins/uml/xtext/org.eclipse.papyrus.uml.xtext.integration.ui/src/org/eclipse/papyrus/uml/xtext/integration/XtextSourceViewerEx.java @@ -1,115 +1,129 @@ -package org.eclipse.papyrus.uml.xtext.integration;
-
-import java.lang.reflect.Field;
-
-import org.eclipse.jface.preference.IPreferenceStore;
-import org.eclipse.jface.text.BadLocationException;
-import org.eclipse.jface.text.IDocument;
-import org.eclipse.jface.text.projection.ProjectionDocument;
-import org.eclipse.jface.text.source.SourceViewerConfiguration;
-import org.eclipse.jface.text.source.projection.ProjectionViewer;
-import org.eclipse.swt.custom.StyledText;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.ui.editors.text.TextSourceViewerConfiguration;
-import org.eclipse.xtext.ui.editor.XtextSourceViewer;
-
-/**
- * Source viewer replacement that implements a workaround for Eclipse bug
- * #352847 to enable that offsets are respected.
- *
- * @author alexander.nyssen@itemis.de
- *
- */
-class XtextSourceViewerEx extends XtextSourceViewer {
-
- private final StyledText styledText;
- private final IPreferenceStore preferenceStore;
-
- public XtextSourceViewerEx(StyledText styledText,
- IPreferenceStore preferenceStore) {
- // super constructor will create a new text widget by calling
- // createControl(). As we want to use the passed in styled text, we have
- // to disable this mechanism.
- super(null, null, null, false, styledText.getStyle());
- this.styledText = styledText;
- this.preferenceStore = preferenceStore;
- super.createControl(styledText.getParent(), styledText.getStyle());
- }
-
- @Override
- protected void createControl(Composite parent, int styles) {
- // do nothing here (will be called by super constructor)
- }
-
- @Override
- protected StyledText createTextWidget(Composite parent, int styles) {
- return styledText;
- }
-
- /**
- * Overwritten to handle offset properly.
- */
- @Override
- protected boolean updateSlaveDocument(IDocument slaveDocument,
- int modelRangeOffset, int modelRangeLength)
- throws BadLocationException {
- if (slaveDocument instanceof ProjectionDocument) {
- ProjectionDocument projection = (ProjectionDocument) slaveDocument;
-
- int offset = modelRangeOffset;
- int length = modelRangeLength;
-
- if (!isProjectionMode()) {
- // mimic original TextViewer behavior
- IDocument master = projection.getMasterDocument();
- int line = master.getLineOfOffset(modelRangeOffset);
- offset += master.getLineOffset(line);
- length = (modelRangeOffset - offset) + modelRangeLength;
- }
-
- try {
- // fHandleProjectionChanges= false;
- setPrivateHandleProjectionChangesField(false);
- projection.replaceMasterDocumentRanges(offset, length);
- } finally {
- // fHandleProjectionChanges= true;
- setPrivateHandleProjectionChangesField(true);
- }
- return true;
- }
- return false;
- }
-
- @Override
- public void configure(SourceViewerConfiguration configuration) {
- // We have to set the preference store via reflection here because Xtext
- // uses package visibility for the setter
- Field declaredField;
- try {
- declaredField = TextSourceViewerConfiguration.class
- .getDeclaredField("fPreferenceStore");
- declaredField.setAccessible(true);
- declaredField.set(configuration, preferenceStore);
- } catch (Exception e) {
- e.printStackTrace();
- }
- super.configure(configuration);
- }
-
- /**
- * Set the private fHandleProjectionChanges field value.
- *
- * @param value
- * the new value.
- */
- private void setPrivateHandleProjectionChangesField(boolean value) {
- try {
- Field declaredField = ProjectionViewer.class
- .getDeclaredField("fHandleProjectionChanges");
- declaredField.setAccessible(true);
- declaredField.set(this, value);
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
-}
+/***************************************************************************** +* Copyright (c) 2010 CEA LIST. +* +* + * 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: +* Itemis - Initial API and implementation +* +*****************************************************************************/ + +package org.eclipse.papyrus.uml.xtext.integration; + +import java.lang.reflect.Field; + +import org.eclipse.jface.preference.IPreferenceStore; +import org.eclipse.jface.text.BadLocationException; +import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.projection.ProjectionDocument; +import org.eclipse.jface.text.source.SourceViewerConfiguration; +import org.eclipse.jface.text.source.projection.ProjectionViewer; +import org.eclipse.swt.custom.StyledText; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.ui.editors.text.TextSourceViewerConfiguration; +import org.eclipse.xtext.ui.editor.XtextSourceViewer; + +/** + * Source viewer replacement that implements a workaround for Eclipse bug + * #352847 to enable that offsets are respected. + * + * @author alexander.nyssen@itemis.de + * + */ +class XtextSourceViewerEx extends XtextSourceViewer { + + private final StyledText styledText; + private final IPreferenceStore preferenceStore; + + public XtextSourceViewerEx(StyledText styledText, + IPreferenceStore preferenceStore) { + // super constructor will create a new text widget by calling + // createControl(). As we want to use the passed in styled text, we have + // to disable this mechanism. + super(null, null, null, false, styledText.getStyle()); + this.styledText = styledText; + this.preferenceStore = preferenceStore; + super.createControl(styledText.getParent(), styledText.getStyle()); + } + + @Override + protected void createControl(Composite parent, int styles) { + // do nothing here (will be called by super constructor) + } + + @Override + protected StyledText createTextWidget(Composite parent, int styles) { + return styledText; + } + + /** + * Overwritten to handle offset properly. + */ + @Override + protected boolean updateSlaveDocument(IDocument slaveDocument, + int modelRangeOffset, int modelRangeLength) + throws BadLocationException { + if (slaveDocument instanceof ProjectionDocument) { + ProjectionDocument projection = (ProjectionDocument) slaveDocument; + + int offset = modelRangeOffset; + int length = modelRangeLength; + + if (!isProjectionMode()) { + // mimic original TextViewer behavior + IDocument master = projection.getMasterDocument(); + int line = master.getLineOfOffset(modelRangeOffset); + offset += master.getLineOffset(line); + length = (modelRangeOffset - offset) + modelRangeLength; + } + + try { + // fHandleProjectionChanges= false; + setPrivateHandleProjectionChangesField(false); + projection.replaceMasterDocumentRanges(offset, length); + } finally { + // fHandleProjectionChanges= true; + setPrivateHandleProjectionChangesField(true); + } + return true; + } + return false; + } + + @Override + public void configure(SourceViewerConfiguration configuration) { + // We have to set the preference store via reflection here because Xtext + // uses package visibility for the setter + Field declaredField; + try { + declaredField = TextSourceViewerConfiguration.class + .getDeclaredField("fPreferenceStore"); + declaredField.setAccessible(true); + declaredField.set(configuration, preferenceStore); + } catch (Exception e) { + e.printStackTrace(); + } + super.configure(configuration); + } + + /** + * Set the private fHandleProjectionChanges field value. + * + * @param value + * the new value. + */ + private void setPrivateHandleProjectionChangesField(boolean value) { + try { + Field declaredField = ProjectionViewer.class + .getDeclaredField("fHandleProjectionChanges"); + declaredField.setAccessible(true); + declaredField.set(this, value); + } catch (Exception e) { + e.printStackTrace(); + } + } +} |