001: /*******************************************************************************
002: * Copyright (c) 2006, 2007 IBM Corporation and others.
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * IBM Corporation - initial API and implementation
010: *******************************************************************************/package org.eclipse.ui.fieldassist;
011:
012: import org.eclipse.core.commands.AbstractHandler;
013: import org.eclipse.core.commands.ExecutionEvent;
014: import org.eclipse.core.commands.IHandler;
015: import org.eclipse.jface.fieldassist.ContentProposalAdapter;
016: import org.eclipse.jface.fieldassist.ControlDecoration;
017: import org.eclipse.jface.fieldassist.FieldDecoration;
018: import org.eclipse.jface.fieldassist.FieldDecorationRegistry;
019: import org.eclipse.jface.fieldassist.IContentProposalProvider;
020: import org.eclipse.jface.fieldassist.IControlContentAdapter;
021: import org.eclipse.osgi.util.NLS;
022: import org.eclipse.swt.SWT;
023: import org.eclipse.swt.events.DisposeEvent;
024: import org.eclipse.swt.events.DisposeListener;
025: import org.eclipse.swt.events.FocusEvent;
026: import org.eclipse.swt.events.FocusListener;
027: import org.eclipse.swt.widgets.Control;
028: import org.eclipse.ui.PlatformUI;
029: import org.eclipse.ui.handlers.IHandlerActivation;
030: import org.eclipse.ui.handlers.IHandlerService;
031: import org.eclipse.ui.internal.WorkbenchMessages;
032: import org.eclipse.ui.keys.IBindingService;
033:
034: /**
035: * ContentAssistCommandAdapter extends {@link ContentProposalAdapter} to invoke
036: * content proposals using a specified {@link org.eclipse.ui.commands.ICommand}.
037: * The ability to specify a {@link org.eclipse.jface.bindings.keys.KeyStroke}
038: * that explicitly invokes content proposals is hidden by this class, and
039: * instead the String id of a command is used. If no command id is specified by
040: * the client, then the default workbench content assist command is used.
041: * <p>
042: * As of 3.3, ContentAssistCommandAdapter can be optionally configured to
043: * install the content assist decoration on its control.
044: * <p>
045: * This class is not intended to be subclassed.
046: *
047: * @since 3.2
048: */
049: public class ContentAssistCommandAdapter extends ContentProposalAdapter {
050:
051: private static final String CONTENT_ASSIST_DECORATION_ID = "org.eclipse.ui.fieldAssist.ContentAssistField"; //$NON-NLS-1$
052: private String commandId;
053:
054: /**
055: * The command id used for content assist. (value
056: * <code>"org.eclipse.ui.edit.text.contentAssist.proposals"</code>)
057: */
058: public static final String CONTENT_PROPOSAL_COMMAND = "org.eclipse.ui.edit.text.contentAssist.proposals"; //$NON-NLS-1$
059:
060: // Default autoactivation delay in milliseconds
061: // TODO: This should eventually be controlled by
062: // a platform UI preference.
063: private static final int DEFAULT_AUTO_ACTIVATION_DELAY = 500;
064:
065: private IHandlerService handlerService;
066:
067: private IHandlerActivation activeHandler;
068:
069: private IHandler proposalHandler = new AbstractHandler() {
070: public Object execute(ExecutionEvent event) {
071: openProposalPopup();
072: return null;
073: }
074:
075: };
076: private ControlDecoration decoration;
077:
078: /**
079: * Construct a content proposal adapter that can assist the user with
080: * choosing content for the field. No visual indicator of content assist is
081: * shown.
082: *
083: * @param control
084: * the control for which the adapter is providing content assist.
085: * May not be <code>null</code>.
086: * @param controlContentAdapter
087: * the <code>IControlContentAdapter</code> used to obtain and
088: * update the control's contents as proposals are accepted. May
089: * not be <code>null</code>.
090: * @param proposalProvider
091: * the <code>IContentProposalProvider</code> used to obtain
092: * content proposals for this control, or <code>null</code> if
093: * no content proposal is available.
094: * @param commandId
095: * the String id of the command that will invoke the content
096: * assistant. If not supplied, the default value will be
097: * "org.eclipse.ui.edit.text.contentAssist.proposals".
098: * @param autoActivationCharacters
099: * An array of characters that trigger auto-activation of content
100: * proposal. If specified, these characters will trigger
101: * auto-activation of the proposal popup, regardless of the
102: * specified command id.
103: */
104: public ContentAssistCommandAdapter(Control control,
105: IControlContentAdapter controlContentAdapter,
106: IContentProposalProvider proposalProvider,
107: String commandId, char[] autoActivationCharacters) {
108: this (control, controlContentAdapter, proposalProvider,
109: commandId, autoActivationCharacters, false);
110: }
111:
112: /**
113: * Construct a content proposal adapter that can assist the user with
114: * choosing content for the field.
115: *
116: * @param control
117: * the control for which the adapter is providing content assist.
118: * May not be <code>null</code>.
119: * @param controlContentAdapter
120: * the <code>IControlContentAdapter</code> used to obtain and
121: * update the control's contents as proposals are accepted. May
122: * not be <code>null</code>.
123: * @param proposalProvider
124: * the <code>IContentProposalProvider</code> used to obtain
125: * content proposals for this control, or <code>null</code> if
126: * no content proposal is available.
127: * @param commandId
128: * the String id of the command that will invoke the content
129: * assistant. If not supplied, the default value will be
130: * "org.eclipse.ui.edit.text.contentAssist.proposals".
131: * @param autoActivationCharacters
132: * An array of characters that trigger auto-activation of content
133: * proposal. If specified, these characters will trigger
134: * auto-activation of the proposal popup, regardless of the
135: * specified command id.
136: * @param installDecoration
137: * A boolean that specifies whether a content assist control
138: * decoration should be installed. The client is responsible for
139: * ensuring that adequate space is reserved for the decoration.
140: * Clients that want more fine-grained control of the
141: * decoration's location or appearance should use
142: * <code>false</code> for this parameter, creating their own
143: * {@link ControlDecoration} and managing it directly.
144: * @since 3.3
145: */
146: public ContentAssistCommandAdapter(Control control,
147: IControlContentAdapter controlContentAdapter,
148: IContentProposalProvider proposalProvider,
149: String commandId, char[] autoActivationCharacters,
150: boolean installDecoration) {
151: super (control, controlContentAdapter, proposalProvider, null,
152: autoActivationCharacters);
153: this .commandId = commandId;
154: if (commandId == null) {
155: this .commandId = CONTENT_PROPOSAL_COMMAND;
156: }
157:
158: // If no autoactivation characters were specified, set them to the empty
159: // array so that we don't get the alphanumeric auto-trigger of our
160: // superclass.
161: if (autoActivationCharacters == null) {
162: this .setAutoActivationCharacters(new char[] {});
163: }
164: // Set a default autoactivation delay.
165: setAutoActivationDelay(DEFAULT_AUTO_ACTIVATION_DELAY);
166:
167: // Add listeners to the control to manage activation of the handler
168: addListeners(control);
169:
170: // Cache the handler service so we don't have to retrieve it each time
171: this .handlerService = (IHandlerService) PlatformUI
172: .getWorkbench().getService(IHandlerService.class);
173: if (installDecoration) {
174: // Note top left is used for compatibility with 3.2, although
175: // this may change to center alignment in the future.
176: decoration = new ControlDecoration(control, SWT.TOP
177: | SWT.LEFT);
178: decoration.setShowOnlyOnFocus(true);
179: FieldDecoration dec = getContentAssistFieldDecoration();
180: decoration.setImage(dec.getImage());
181: decoration.setDescriptionText(dec.getDescription());
182: }
183:
184: }
185:
186: /*
187: * Add the listeners needed in order to activate the content assist command
188: * on the control.
189: */
190: private void addListeners(Control control) {
191: control.addFocusListener(new FocusListener() {
192: public void focusLost(FocusEvent e) {
193: if (activeHandler != null) {
194: handlerService.deactivateHandler(activeHandler);
195: activeHandler = null;
196: }
197: }
198:
199: public void focusGained(FocusEvent e) {
200: if (isEnabled()) {
201: if (activeHandler == null) {
202: activeHandler = handlerService.activateHandler(
203: commandId, proposalHandler);
204: }
205: } else {
206: if (activeHandler != null) {
207: handlerService.deactivateHandler(activeHandler);
208: }
209: activeHandler = null;
210: }
211: }
212: });
213: control.addDisposeListener(new DisposeListener() {
214: public void widgetDisposed(DisposeEvent e) {
215: if (activeHandler != null) {
216: handlerService.deactivateHandler(activeHandler);
217: activeHandler = null;
218: }
219:
220: }
221: });
222: }
223:
224: /**
225: * Return the string command ID of the command used to invoke content
226: * assist.
227: *
228: * @return the command ID of the command that invokes content assist.
229: */
230: public String getCommandId() {
231: return commandId;
232: }
233:
234: /*
235: * Return the field decoration that should be used to indicate that content
236: * assist is available for a field. Ensure that the decoration text includes
237: * the correct key binding.
238: *
239: * @return the {@link FieldDecoration} that should be used to show content
240: * assist.
241: *
242: * @since 3.3
243: */
244: private FieldDecoration getContentAssistFieldDecoration() {
245: FieldDecorationRegistry registry = FieldDecorationRegistry
246: .getDefault();
247: // Look for a decoration installed for this particular command id.
248: String decId = CONTENT_ASSIST_DECORATION_ID + getCommandId();
249: FieldDecoration dec = registry.getFieldDecoration(decId);
250:
251: // If there is not one, base ours on the standard JFace one.
252: if (dec == null) {
253: FieldDecoration originalDec = registry
254: .getFieldDecoration(FieldDecorationRegistry.DEC_CONTENT_PROPOSAL);
255:
256: registry.registerFieldDecoration(decId, null, originalDec
257: .getImage());
258: dec = registry.getFieldDecoration(decId);
259: }
260: // Always update the decoration text since the key binding may
261: // have changed since it was last retrieved.
262: IBindingService bindingService = (IBindingService) PlatformUI
263: .getWorkbench().getService(IBindingService.class);
264: dec
265: .setDescription(NLS
266: .bind(
267: WorkbenchMessages.ContentAssist_Cue_Description_Key,
268: bindingService
269: .getBestActiveBindingFormattedFor(getCommandId())));
270:
271: // Now return the field decoration
272: return dec;
273: }
274:
275: /*
276: * (non-Javadoc)
277: *
278: * Overridden to hide and show the content assist decoration
279: *
280: * @see org.eclipse.jface.fieldassist.ContentProposalAdapter#setEnabled(boolean)
281: * @since 3.3
282: */
283: public void setEnabled(boolean enabled) {
284: super.setEnabled(enabled);
285: if (decoration == null) {
286: return;
287: }
288: if (enabled) {
289: decoration.show();
290: } else {
291: decoration.hide();
292: }
293: }
294: }
|