129
|
1 /*******************************************************************************
|
|
2 * Copyright (c) 2000, 2008 IBM Corporation and others.
|
|
3 * All rights reserved. This program and the accompanying materials
|
|
4 * are made available under the terms of the Eclipse Public License v1.0
|
|
5 * which accompanies this distribution, and is available at
|
|
6 * http://www.eclipse.org/legal/epl-v10.html
|
|
7 *
|
|
8 * Contributors:
|
|
9 * IBM Corporation - initial API and implementation
|
|
10 * Port to the D programming language:
|
|
11 * Frank Benoit <benoit@tionex.de>
|
|
12 *******************************************************************************/
|
|
13 module dwtx.jface.internal.text.link.contentassist.ContentAssistant2;
|
|
14
|
|
15 import dwt.dwthelper.utils;
|
|
16
|
|
17 import java.util.ArrayList;
|
|
18 import java.util.HashMap;
|
|
19 import java.util.Iterator;
|
|
20 import java.util.List;
|
|
21 import java.util.Map;
|
|
22
|
|
23 import dwt.DWT;
|
|
24 import dwt.DWTError;
|
|
25 import dwt.custom.StyledText;
|
|
26 import dwt.custom.VerifyKeyListener;
|
|
27 import dwt.events.ControlEvent;
|
|
28 import dwt.events.ControlListener;
|
|
29 import dwt.events.DisposeEvent;
|
|
30 import dwt.events.DisposeListener;
|
|
31 import dwt.events.FocusEvent;
|
|
32 import dwt.events.FocusListener;
|
|
33 import dwt.events.MouseEvent;
|
|
34 import dwt.events.MouseListener;
|
|
35 import dwt.events.VerifyEvent;
|
|
36 import dwt.graphics.Color;
|
|
37 import dwt.graphics.Point;
|
|
38 import dwt.graphics.Rectangle;
|
|
39 import dwt.widgets.Control;
|
|
40 import dwt.widgets.Display;
|
|
41 import dwt.widgets.Event;
|
|
42 import dwt.widgets.Listener;
|
|
43 import dwt.widgets.Shell;
|
|
44 import dwt.widgets.Widget;
|
|
45 import dwtx.core.runtime.Assert;
|
|
46 import dwtx.jface.text.BadLocationException;
|
|
47 import dwtx.jface.text.DefaultInformationControl;
|
|
48 import dwtx.jface.text.IEventConsumer;
|
|
49 import dwtx.jface.text.IInformationControl;
|
|
50 import dwtx.jface.text.IInformationControlCreator;
|
|
51 import dwtx.jface.text.ITextViewer;
|
|
52 import dwtx.jface.text.ITextViewerExtension;
|
|
53 import dwtx.jface.text.IViewportListener;
|
|
54 import dwtx.jface.text.IWidgetTokenKeeper;
|
|
55 import dwtx.jface.text.IWidgetTokenKeeperExtension;
|
|
56 import dwtx.jface.text.IWidgetTokenOwner;
|
|
57 import dwtx.jface.text.IWidgetTokenOwnerExtension;
|
|
58 import dwtx.jface.text.TextUtilities;
|
|
59 import dwtx.jface.text.contentassist.CompletionProposal;
|
|
60 import dwtx.jface.text.contentassist.ICompletionProposal;
|
|
61 import dwtx.jface.text.contentassist.ICompletionProposalExtension6;
|
|
62 import dwtx.jface.text.contentassist.IContentAssistProcessor;
|
|
63 import dwtx.jface.text.contentassist.IContentAssistant;
|
|
64 import dwtx.jface.text.contentassist.IContentAssistantExtension;
|
|
65 import dwtx.jface.text.contentassist.IContextInformation;
|
|
66 import dwtx.jface.text.contentassist.IContextInformationPresenter;
|
|
67 import dwtx.jface.text.contentassist.IContextInformationValidator;
|
|
68
|
|
69
|
|
70 /**
|
|
71 * A custom implementation of the <code>IContentAssistant</code> interface.
|
|
72 * This implementation is used by the linked mode UI. This is internal and subject
|
|
73 * to change without notice.
|
|
74 */
|
|
75 public class ContentAssistant2 : IContentAssistant, IContentAssistantExtension, IWidgetTokenKeeper, IWidgetTokenKeeperExtension {
|
|
76
|
|
77 /**
|
|
78 * A generic closer class used to monitor various
|
|
79 * interface events in order to determine whether
|
|
80 * content-assist should be terminated and all
|
|
81 * associated windows closed.
|
|
82 */
|
|
83 class Closer : ControlListener, MouseListener, FocusListener, DisposeListener, IViewportListener {
|
|
84
|
|
85 /** The shell on which we add listeners. */
|
|
86 private Shell fShell;
|
|
87 private long fViewportListenerStartTime;
|
|
88
|
|
89 /**
|
|
90 * Installs this closer on it's viewer's text widget.
|
|
91 */
|
|
92 protected void install() {
|
|
93 Control w= fViewer.getTextWidget();
|
|
94 if (Helper2.okToUse(w)) {
|
|
95
|
|
96 Shell shell= w.getShell();
|
|
97 fShell= shell;
|
|
98 shell.addControlListener(this);
|
|
99
|
|
100 w.addMouseListener(this);
|
|
101 w.addFocusListener(this);
|
|
102
|
|
103 /*
|
|
104 * 1GGYYWK: ITPJUI:ALL - Dismissing editor with code assist up causes lots of Internal Errors
|
|
105 */
|
|
106 w.addDisposeListener(this);
|
|
107 }
|
|
108
|
|
109 fViewer.addViewportListener(this);
|
|
110 fViewportListenerStartTime= System.currentTimeMillis() + 500;
|
|
111 }
|
|
112
|
|
113 /**
|
|
114 * Uninstalls this closer from the viewer's text widget.
|
|
115 */
|
|
116 protected void uninstall() {
|
|
117 Shell shell= fShell;
|
|
118 fShell= null;
|
|
119 if (Helper2.okToUse(shell))
|
|
120 shell.removeControlListener(this);
|
|
121
|
|
122 Control w= fViewer.getTextWidget();
|
|
123 if (Helper2.okToUse(w)) {
|
|
124
|
|
125 w.removeMouseListener(this);
|
|
126 w.removeFocusListener(this);
|
|
127
|
|
128 /*
|
|
129 * 1GGYYWK: ITPJUI:ALL - Dismissing editor with code assist up causes lots of Internal Errors
|
|
130 */
|
|
131 w.removeDisposeListener(this);
|
|
132 }
|
|
133
|
|
134 fViewer.removeViewportListener(this);
|
|
135 }
|
|
136
|
|
137 /*
|
|
138 * @see ControlListener#controlResized(ControlEvent)
|
|
139 */
|
|
140 public void controlResized(ControlEvent e) {
|
|
141 hide();
|
|
142 }
|
|
143
|
|
144 /*
|
|
145 * @see ControlListener#controlMoved(ControlEvent)
|
|
146 */
|
|
147 public void controlMoved(ControlEvent e) {
|
|
148 hide();
|
|
149 }
|
|
150
|
|
151 /*
|
|
152 * @see MouseListener#mouseDown(MouseEvent)
|
|
153 */
|
|
154 public void mouseDown(MouseEvent e) {
|
|
155 hide();
|
|
156 }
|
|
157
|
|
158 /*
|
|
159 * @see MouseListener#mouseUp(MouseEvent)
|
|
160 */
|
|
161 public void mouseUp(MouseEvent e) {
|
|
162 }
|
|
163
|
|
164 /*
|
|
165 * @see MouseListener#mouseDoubleClick(MouseEvent)
|
|
166 */
|
|
167 public void mouseDoubleClick(MouseEvent e) {
|
|
168 hide();
|
|
169 }
|
|
170
|
|
171 /*
|
|
172 * @see FocusListener#focusGained(FocusEvent)
|
|
173 */
|
|
174 public void focusGained(FocusEvent e) {
|
|
175 }
|
|
176
|
|
177 /*
|
|
178 * @see FocusListener#focusLost(FocusEvent)
|
|
179 */
|
|
180 public void focusLost(FocusEvent e) {
|
|
181 if (fViewer !is null) {
|
|
182 Control control= fViewer.getTextWidget();
|
|
183 if (control !is null) {
|
|
184 Display d= control.getDisplay();
|
|
185 if (d !is null) {
|
|
186 d.asyncExec(new Runnable() {
|
|
187 public void run() {
|
|
188 if (!hasFocus())
|
|
189 hide();
|
|
190 }
|
|
191 });
|
|
192 }
|
|
193 }
|
|
194 }
|
|
195 }
|
|
196
|
|
197 /*
|
|
198 * @seeDisposeListener#widgetDisposed(DisposeEvent)
|
|
199 */
|
|
200 public void widgetDisposed(DisposeEvent e) {
|
|
201 /*
|
|
202 * 1GGYYWK: ITPJUI:ALL - Dismissing editor with code assist up causes lots of Internal Errors
|
|
203 */
|
|
204 hide();
|
|
205 }
|
|
206
|
|
207 /*
|
|
208 * @see IViewportListener#viewportChanged(int)
|
|
209 */
|
|
210 public void viewportChanged(int topIndex) {
|
|
211 if (System.currentTimeMillis() > fViewportListenerStartTime)
|
|
212 hide();
|
|
213 }
|
|
214 }
|
|
215
|
|
216 /**
|
|
217 * An implementation of <code>IContentAssistListener</code>, this class is
|
|
218 * used to monitor key events in support of automatic activation
|
|
219 * of the content assistant. If enabled, the implementation utilizes a
|
|
220 * thread to watch for input characters matching the activation
|
|
221 * characters specified by the content assist processor, and if
|
|
222 * detected, will wait the indicated delay interval before
|
|
223 * activating the content assistant.
|
|
224 */
|
|
225 class AutoAssistListener : VerifyKeyListener, Runnable {
|
|
226
|
|
227 private Thread fThread;
|
|
228 private bool fIsReset= false;
|
|
229 private Object fMutex= new Object();
|
|
230 private int fShowStyle;
|
|
231
|
|
232 private final static int SHOW_PROPOSALS= 1;
|
|
233 private final static int SHOW_CONTEXT_INFO= 2;
|
|
234
|
|
235 protected AutoAssistListener() {
|
|
236 }
|
|
237
|
|
238 protected void start(int showStyle) {
|
|
239 fShowStyle= showStyle;
|
|
240 fThread= new Thread(this, ContentAssistMessages.getString("ContentAssistant.assist_delay_timer_name")); //$NON-NLS-1$
|
|
241 fThread.start();
|
|
242 }
|
|
243
|
|
244 public void run() {
|
|
245 try {
|
|
246 while (true) {
|
|
247 synchronized (fMutex) {
|
|
248 if (fAutoActivationDelay !is 0)
|
|
249 fMutex.wait(fAutoActivationDelay);
|
|
250 if (fIsReset) {
|
|
251 fIsReset= false;
|
|
252 continue;
|
|
253 }
|
|
254 }
|
|
255 showAssist(fShowStyle);
|
|
256 break;
|
|
257 }
|
|
258 } catch (InterruptedException e) {
|
|
259 }
|
|
260 fThread= null;
|
|
261 }
|
|
262
|
|
263 protected void reset(int showStyle) {
|
|
264 synchronized (fMutex) {
|
|
265 fShowStyle= showStyle;
|
|
266 fIsReset= true;
|
|
267 fMutex.notifyAll();
|
|
268 }
|
|
269 }
|
|
270
|
|
271 protected void stop() {
|
|
272 Thread threadToStop= fThread;
|
|
273 if (threadToStop !is null)
|
|
274 threadToStop.interrupt();
|
|
275 }
|
|
276
|
|
277 private bool contains(char[] characters, char character) {
|
|
278 if (characters !is null) {
|
|
279 for (int i= 0; i < characters.length; i++) {
|
|
280 if (character is characters[i])
|
|
281 return true;
|
|
282 }
|
|
283 }
|
|
284 return false;
|
|
285 }
|
|
286
|
|
287 public void verifyKey(VerifyEvent e) {
|
|
288 // Only act on typed characters and ignore modifier-only events
|
|
289 if (e.character is 0 && (e.keyCode & DWT.KEYCODE_BIT) is 0)
|
|
290 return;
|
|
291
|
|
292 if (e.character !is 0 && (e.stateMask is DWT.ALT))
|
|
293 return;
|
|
294
|
|
295 int showStyle;
|
|
296 int pos= fViewer.getSelectedRange().x;
|
|
297 char[] activation= getCompletionProposalAutoActivationCharacters(fViewer, pos);
|
|
298
|
|
299 if (contains(activation, e.character) && !fProposalPopup.isActive())
|
|
300 showStyle= SHOW_PROPOSALS;
|
|
301 else {
|
|
302 activation= getContextInformationAutoActivationCharacters(fViewer, pos);
|
|
303 if (contains(activation, e.character) && !fContextInfoPopup.isActive())
|
|
304 showStyle= SHOW_CONTEXT_INFO;
|
|
305 else {
|
|
306 if (fThread !is null && fThread.isAlive())
|
|
307 stop();
|
|
308 return;
|
|
309 }
|
|
310 }
|
|
311
|
|
312 if (fThread !is null && fThread.isAlive())
|
|
313 reset(showStyle);
|
|
314 else
|
|
315 start(showStyle);
|
|
316 }
|
|
317
|
|
318 protected void showAssist(final int showStyle) {
|
|
319 Control control= fViewer.getTextWidget();
|
|
320 Display d= control.getDisplay();
|
|
321 if (d !is null) {
|
|
322 try {
|
|
323 d.syncExec(new Runnable() {
|
|
324 public void run() {
|
|
325 if (showStyle is SHOW_PROPOSALS)
|
|
326 fProposalPopup.showProposals(true);
|
|
327 else if (showStyle is SHOW_CONTEXT_INFO)
|
|
328 fContextInfoPopup.showContextProposals(true);
|
|
329 }
|
|
330 });
|
|
331 } catch (DWTError e) {
|
|
332 }
|
|
333 }
|
|
334 }
|
|
335 }
|
|
336
|
|
337 /**
|
|
338 * The layout manager layouts the various
|
|
339 * windows associated with the content assistant based on the
|
|
340 * settings of the content assistant.
|
|
341 */
|
|
342 class LayoutManager : Listener {
|
|
343
|
|
344 // Presentation types.
|
|
345 /** proposal selector */
|
|
346 public final static int LAYOUT_PROPOSAL_SELECTOR= 0;
|
|
347 /** context selector */
|
|
348 public final static int LAYOUT_CONTEXT_SELECTOR= 1;
|
|
349 /** context info */
|
|
350 public final static int LAYOUT_CONTEXT_INFO_POPUP= 2;
|
|
351
|
|
352 int fContextType= LAYOUT_CONTEXT_SELECTOR;
|
|
353 Shell[] fShells= new Shell[3];
|
|
354 Object[] fPopups= new Object[3];
|
|
355
|
|
356 protected void add(Object popup, Shell shell, int type, int offset) {
|
|
357 Assert.isNotNull(popup);
|
|
358 Assert.isTrue(shell !is null && !shell.isDisposed());
|
|
359 checkType(type);
|
|
360
|
|
361 if (fShells[type] !is shell) {
|
|
362 if (fShells[type] !is null)
|
|
363 fShells[type].removeListener(DWT.Dispose, this);
|
|
364 shell.addListener(DWT.Dispose, this);
|
|
365 fShells[type]= shell;
|
|
366 }
|
|
367
|
|
368 fPopups[type]= popup;
|
|
369 if (type is LAYOUT_CONTEXT_SELECTOR || type is LAYOUT_CONTEXT_INFO_POPUP)
|
|
370 fContextType= type;
|
|
371
|
|
372 layout(type, offset);
|
|
373 adjustListeners(type);
|
|
374 }
|
|
375
|
|
376 protected void checkType(int type) {
|
|
377 Assert.isTrue(type is LAYOUT_PROPOSAL_SELECTOR ||
|
|
378 type is LAYOUT_CONTEXT_SELECTOR || type is LAYOUT_CONTEXT_INFO_POPUP);
|
|
379 }
|
|
380
|
|
381 public void handleEvent(Event event) {
|
|
382 Widget source= event.widget;
|
|
383 source.removeListener(DWT.Dispose, this);
|
|
384
|
|
385 int type= getShellType(source);
|
|
386 checkType(type);
|
|
387 fShells[type]= null;
|
|
388
|
|
389 switch (type) {
|
|
390 case LAYOUT_PROPOSAL_SELECTOR:
|
|
391 if (fContextType is LAYOUT_CONTEXT_SELECTOR &&
|
|
392 Helper2.okToUse(fShells[LAYOUT_CONTEXT_SELECTOR])) {
|
|
393 // Restore event notification to the tip popup.
|
|
394 addContentAssistListener((IContentAssistListener2) fPopups[LAYOUT_CONTEXT_SELECTOR], CONTEXT_SELECTOR);
|
|
395 }
|
|
396 break;
|
|
397
|
|
398 case LAYOUT_CONTEXT_SELECTOR:
|
|
399 if (Helper2.okToUse(fShells[LAYOUT_PROPOSAL_SELECTOR])) {
|
|
400 if (fProposalPopupOrientation is PROPOSAL_STACKED)
|
|
401 layout(LAYOUT_PROPOSAL_SELECTOR, getSelectionOffset());
|
|
402 // Restore event notification to the proposal popup.
|
|
403 addContentAssistListener((IContentAssistListener2) fPopups[LAYOUT_PROPOSAL_SELECTOR], PROPOSAL_SELECTOR);
|
|
404 }
|
|
405 fContextType= LAYOUT_CONTEXT_INFO_POPUP;
|
|
406 break;
|
|
407
|
|
408 case LAYOUT_CONTEXT_INFO_POPUP:
|
|
409 if (Helper2.okToUse(fShells[LAYOUT_PROPOSAL_SELECTOR])) {
|
|
410 if (fContextInfoPopupOrientation is CONTEXT_INFO_BELOW)
|
|
411 layout(LAYOUT_PROPOSAL_SELECTOR, getSelectionOffset());
|
|
412 }
|
|
413 fContextType= LAYOUT_CONTEXT_SELECTOR;
|
|
414 break;
|
|
415 }
|
|
416 }
|
|
417
|
|
418 protected int getShellType(Widget shell) {
|
|
419 for (int i=0; i<fShells.length; i++) {
|
|
420 if (fShells[i] is shell)
|
|
421 return i;
|
|
422 }
|
|
423 return -1;
|
|
424 }
|
|
425
|
|
426 protected void layout(int type, int offset) {
|
|
427 switch (type) {
|
|
428 case LAYOUT_PROPOSAL_SELECTOR:
|
|
429 layoutProposalSelector(offset);
|
|
430 break;
|
|
431 case LAYOUT_CONTEXT_SELECTOR:
|
|
432 layoutContextSelector(offset);
|
|
433 break;
|
|
434 case LAYOUT_CONTEXT_INFO_POPUP:
|
|
435 layoutContextInfoPopup(offset);
|
|
436 break;
|
|
437 }
|
|
438 }
|
|
439
|
|
440 protected void layoutProposalSelector(int offset) {
|
|
441 if (fContextType is LAYOUT_CONTEXT_INFO_POPUP &&
|
|
442 fContextInfoPopupOrientation is CONTEXT_INFO_BELOW &&
|
|
443 Helper2.okToUse(fShells[LAYOUT_CONTEXT_INFO_POPUP])) {
|
|
444 // Stack proposal selector beneath the tip box.
|
|
445 Shell shell= fShells[LAYOUT_PROPOSAL_SELECTOR];
|
|
446 Shell parent= fShells[LAYOUT_CONTEXT_INFO_POPUP];
|
|
447 shell.setLocation(getStackedLocation(shell, parent));
|
|
448 } else if (fContextType !is LAYOUT_CONTEXT_SELECTOR ||
|
|
449 !Helper2.okToUse(fShells[LAYOUT_CONTEXT_SELECTOR])) {
|
|
450 // There are no other presentations to be concerned with,
|
|
451 // so place the proposal selector beneath the cursor line.
|
|
452 Shell shell= fShells[LAYOUT_PROPOSAL_SELECTOR];
|
|
453 shell.setLocation(getBelowLocation(shell, offset));
|
|
454 } else {
|
|
455 switch (fProposalPopupOrientation) {
|
|
456 case PROPOSAL_REMOVE: {
|
|
457 // Remove the tip selector and place the
|
|
458 // proposal selector beneath the cursor line.
|
|
459 fShells[LAYOUT_CONTEXT_SELECTOR].dispose();
|
|
460 Shell shell= fShells[LAYOUT_PROPOSAL_SELECTOR];
|
|
461 shell.setLocation(getBelowLocation(shell, offset));
|
|
462 break;
|
|
463 }
|
|
464 case PROPOSAL_OVERLAY: {
|
|
465 // Overlay the tip selector with the proposal selector.
|
|
466 Shell shell= fShells[LAYOUT_PROPOSAL_SELECTOR];
|
|
467 shell.setLocation(getBelowLocation(shell, offset));
|
|
468 break;
|
|
469 }
|
|
470 case PROPOSAL_STACKED: {
|
|
471 // Stack the proposal selector beneath the tip selector.
|
|
472 Shell shell= fShells[LAYOUT_PROPOSAL_SELECTOR];
|
|
473 Shell parent= fShells[LAYOUT_CONTEXT_SELECTOR];
|
|
474 shell.setLocation(getStackedLocation(shell, parent));
|
|
475 break;
|
|
476 }
|
|
477 }
|
|
478 }
|
|
479 }
|
|
480
|
|
481 protected void layoutContextSelector(int offset) {
|
|
482 // Always place the context selector beneath the cursor line.
|
|
483 Shell shell= fShells[LAYOUT_CONTEXT_SELECTOR];
|
|
484 shell.setLocation(getBelowLocation(shell, offset));
|
|
485
|
|
486 if (Helper2.okToUse(fShells[LAYOUT_PROPOSAL_SELECTOR])) {
|
|
487 switch (fProposalPopupOrientation) {
|
|
488 case PROPOSAL_REMOVE:
|
|
489 // Remove the proposal selector.
|
|
490 fShells[LAYOUT_PROPOSAL_SELECTOR].dispose();
|
|
491 break;
|
|
492
|
|
493 case PROPOSAL_OVERLAY:
|
|
494 // The proposal selector has been overlaid by the tip selector.
|
|
495 break;
|
|
496
|
|
497 case PROPOSAL_STACKED: {
|
|
498 // Stack the proposal selector beneath the tip selector.
|
|
499 shell= fShells[LAYOUT_PROPOSAL_SELECTOR];
|
|
500 Shell parent= fShells[LAYOUT_CONTEXT_SELECTOR];
|
|
501 shell.setLocation(getStackedLocation(shell, parent));
|
|
502 break;
|
|
503 }
|
|
504 }
|
|
505 }
|
|
506 }
|
|
507
|
|
508 protected void layoutContextInfoPopup(int offset) {
|
|
509 switch (fContextInfoPopupOrientation) {
|
|
510 case CONTEXT_INFO_ABOVE: {
|
|
511 // Place the popup above the cursor line.
|
|
512 Shell shell= fShells[LAYOUT_CONTEXT_INFO_POPUP];
|
|
513 shell.setLocation(getAboveLocation(shell, offset));
|
|
514 break;
|
|
515 }
|
|
516 case CONTEXT_INFO_BELOW: {
|
|
517 // Place the popup beneath the cursor line.
|
|
518 Shell parent= fShells[LAYOUT_CONTEXT_INFO_POPUP];
|
|
519 parent.setLocation(getBelowLocation(parent, offset));
|
|
520 if (Helper2.okToUse(fShells[LAYOUT_PROPOSAL_SELECTOR])) {
|
|
521 // Stack the proposal selector beneath the context info popup.
|
|
522 Shell shell= fShells[LAYOUT_PROPOSAL_SELECTOR];
|
|
523 shell.setLocation(getStackedLocation(shell, parent));
|
|
524 }
|
|
525 break;
|
|
526 }
|
|
527 }
|
|
528 }
|
|
529
|
|
530 protected void shiftHorizontalLocation(Point location, Rectangle shellBounds, Rectangle displayBounds) {
|
|
531 if (location.x + shellBounds.width > displayBounds.width)
|
|
532 location.x= displayBounds.width - shellBounds.width;
|
|
533
|
|
534 if (location.x < displayBounds.x)
|
|
535 location.x= displayBounds.x;
|
|
536 }
|
|
537
|
|
538 protected void shiftVerticalLocation(Point location, Rectangle shellBounds, Rectangle displayBounds) {
|
|
539 if (location.y + shellBounds.height > displayBounds.height)
|
|
540 location.y= displayBounds.height - shellBounds.height;
|
|
541
|
|
542 if (location.y < displayBounds.y)
|
|
543 location.y= displayBounds.y;
|
|
544 }
|
|
545
|
|
546 protected Point getAboveLocation(Shell shell, int offset) {
|
|
547 StyledText text= fViewer.getTextWidget();
|
|
548 Point location= text.getLocationAtOffset(offset);
|
|
549 location= text.toDisplay(location);
|
|
550
|
|
551 Rectangle shellBounds= shell.getBounds();
|
|
552 Rectangle displayBounds= shell.getDisplay().getClientArea();
|
|
553
|
|
554 location.y=location.y - shellBounds.height;
|
|
555
|
|
556 shiftHorizontalLocation(location, shellBounds, displayBounds);
|
|
557 shiftVerticalLocation(location, shellBounds, displayBounds);
|
|
558
|
|
559 return location;
|
|
560 }
|
|
561
|
|
562 protected Point getBelowLocation(Shell shell, int offset) {
|
|
563 StyledText text= fViewer.getTextWidget();
|
|
564 Point location= text.getLocationAtOffset(offset);
|
|
565 if (location.x < 0) location.x= 0;
|
|
566 if (location.y < 0) location.y= 0;
|
|
567 location= text.toDisplay(location);
|
|
568
|
|
569 Rectangle shellBounds= shell.getBounds();
|
|
570 Rectangle displayBounds= shell.getDisplay().getClientArea();
|
|
571
|
|
572 location.y= location.y + text.getLineHeight(offset);
|
|
573 shiftHorizontalLocation(location, shellBounds, displayBounds);
|
|
574 shiftVerticalLocation(location, shellBounds, displayBounds);
|
|
575
|
|
576 return location;
|
|
577 }
|
|
578
|
|
579 protected Point getStackedLocation(Shell shell, Shell parent) {
|
|
580 Point p= parent.getLocation();
|
|
581 Point size= parent.getSize();
|
|
582 p.x += size.x / 4;
|
|
583 p.y += size.y;
|
|
584
|
|
585 p= parent.toDisplay(p);
|
|
586
|
|
587 Rectangle shellBounds= shell.getBounds();
|
|
588 Rectangle displayBounds= shell.getDisplay().getClientArea();
|
|
589 shiftHorizontalLocation(p, shellBounds, displayBounds);
|
|
590 shiftVerticalLocation(p, shellBounds, displayBounds);
|
|
591
|
|
592 return p;
|
|
593 }
|
|
594
|
|
595 protected void adjustListeners(int type) {
|
|
596 switch (type) {
|
|
597 case LAYOUT_PROPOSAL_SELECTOR:
|
|
598 if (fContextType is LAYOUT_CONTEXT_SELECTOR &&
|
|
599 Helper2.okToUse(fShells[LAYOUT_CONTEXT_SELECTOR]))
|
|
600 // Disable event notification to the tip selector.
|
|
601 removeContentAssistListener((IContentAssistListener2) fPopups[LAYOUT_CONTEXT_SELECTOR], CONTEXT_SELECTOR);
|
|
602 break;
|
|
603 case LAYOUT_CONTEXT_SELECTOR:
|
|
604 if (Helper2.okToUse(fShells[LAYOUT_PROPOSAL_SELECTOR]))
|
|
605 // Disable event notification to the proposal selector.
|
|
606 removeContentAssistListener((IContentAssistListener2) fPopups[LAYOUT_PROPOSAL_SELECTOR], PROPOSAL_SELECTOR);
|
|
607 break;
|
|
608 case LAYOUT_CONTEXT_INFO_POPUP:
|
|
609 break;
|
|
610 }
|
|
611 }
|
|
612 }
|
|
613
|
|
614 /**
|
|
615 * Internal key listener and event consumer.
|
|
616 */
|
|
617 class InternalListener : VerifyKeyListener, IEventConsumer {
|
|
618
|
|
619 /**
|
|
620 * Verifies key events by notifying the registered listeners.
|
|
621 * Each listener is allowed to indicate that the event has been
|
|
622 * handled and should not be further processed.
|
|
623 *
|
|
624 * @param e the verify event
|
|
625 * @see VerifyKeyListener#verifyKey(dwt.events.VerifyEvent)
|
|
626 */
|
|
627 public void verifyKey(VerifyEvent e) {
|
|
628 IContentAssistListener2[] listeners= (IContentAssistListener2[]) fListeners.clone();
|
|
629 for (int i= 0; i < listeners.length; i++) {
|
|
630 if (listeners[i] !is null) {
|
|
631 if (!listeners[i].verifyKey(e) || !e.doit)
|
|
632 return;
|
|
633 }
|
|
634 }
|
|
635 }
|
|
636
|
|
637 /*
|
|
638 * @see IEventConsumer#processEvent
|
|
639 */
|
|
640 public void processEvent(VerifyEvent event) {
|
|
641
|
|
642 installKeyListener();
|
|
643
|
|
644 IContentAssistListener2[] listeners= (IContentAssistListener2[])fListeners.clone();
|
|
645 for (int i= 0; i < listeners.length; i++) {
|
|
646 if (listeners[i] !is null) {
|
|
647 listeners[i].processEvent(event);
|
|
648 if (!event.doit)
|
|
649 return;
|
|
650 }
|
|
651 }
|
|
652 }
|
|
653 }
|
|
654
|
|
655
|
|
656 // Content-Assist Listener types
|
|
657 final static int CONTEXT_SELECTOR= 0;
|
|
658 final static int PROPOSAL_SELECTOR= 1;
|
|
659 final static int CONTEXT_INFO_POPUP= 2;
|
|
660
|
|
661 /**
|
|
662 * The popup priority: > info pop-ups, < standard content assist.
|
|
663 * Default value: <code>10</code>.
|
|
664 *
|
|
665 * @since 3.0
|
|
666 */
|
|
667 public static final int WIDGET_PRIORITY= 10;
|
|
668
|
|
669
|
|
670 private static final int DEFAULT_AUTO_ACTIVATION_DELAY= 500;
|
|
671
|
|
672 private IInformationControlCreator fInformationControlCreator;
|
|
673 private int fAutoActivationDelay= DEFAULT_AUTO_ACTIVATION_DELAY;
|
|
674 private bool fIsAutoActivated= false;
|
|
675 private bool fIsAutoInserting= false;
|
|
676 private int fProposalPopupOrientation= PROPOSAL_OVERLAY;
|
|
677 private int fContextInfoPopupOrientation= CONTEXT_INFO_ABOVE;
|
|
678 private Map fProcessors;
|
|
679 private String fPartitioning;
|
|
680
|
|
681 private Color fContextInfoPopupBackground;
|
|
682 private Color fContextInfoPopupForeground;
|
|
683 private Color fContextSelectorBackground;
|
|
684 private Color fContextSelectorForeground;
|
|
685
|
|
686 private ITextViewer fViewer;
|
|
687 private String fLastErrorMessage;
|
|
688
|
|
689 private Closer fCloser;
|
|
690 private LayoutManager fLayoutManager;
|
|
691 private AutoAssistListener fAutoAssistListener;
|
|
692 private InternalListener fInternalListener;
|
|
693 private CompletionProposalPopup2 fProposalPopup;
|
|
694 private ContextInformationPopup2 fContextInfoPopup;
|
|
695
|
|
696 private bool fKeyListenerHooked= false;
|
|
697 private IContentAssistListener2[] fListeners= new IContentAssistListener2[4];
|
|
698 private int fCompletionPosition;
|
|
699 private String[] fProposalStrings;
|
|
700 private ICompletionProposal[] fProposals;
|
|
701 private final List fProposalListeners= new ArrayList();
|
|
702
|
|
703 /**
|
|
704 * Tells whether colored label support is enabled.
|
|
705 * @since 3.4
|
|
706 */
|
|
707 private bool fIsColoredLabelsSupportEnabled= false;
|
|
708
|
|
709
|
|
710 /**
|
|
711 * Creates a new content assistant. The content assistant is not automatically activated,
|
|
712 * overlays the completion proposals with context information list if necessary, and
|
|
713 * shows the context information above the location at which it was activated. If auto
|
|
714 * activation will be enabled, without further configuration steps, this content assistant
|
|
715 * is activated after a 500 ms delay. It uses the default partitioning.
|
|
716 */
|
|
717 public ContentAssistant2() {
|
|
718 setContextInformationPopupOrientation(CONTEXT_INFO_ABOVE);
|
|
719 setInformationControlCreator(getInformationControlCreator());
|
|
720
|
|
721 // JavaTextTools textTools= JavaPlugin.getDefault().getJavaTextTools();
|
|
722 // IColorManager manager= textTools.getColorManager();
|
|
723 //
|
|
724 // IPreferenceStore store= JavaPlugin.getDefault().getPreferenceStore();
|
|
725 //
|
|
726 // Color c= getColor(store, PreferenceConstants.CODEASSIST_PROPOSALS_FOREGROUND, manager);
|
|
727 // setProposalSelectorForeground(c);
|
|
728 //
|
|
729 // c= getColor(store, PreferenceConstants.CODEASSIST_PROPOSALS_BACKGROUND, manager);
|
|
730 // setProposalSelectorBackground(c);
|
|
731 }
|
|
732
|
|
733 /**
|
|
734 * Creates an <code>IInformationControlCreator</code> to be used to display context information.
|
|
735 *
|
|
736 * @return an <code>IInformationControlCreator</code> to be used to display context information
|
|
737 */
|
|
738 private IInformationControlCreator getInformationControlCreator() {
|
|
739 return new IInformationControlCreator() {
|
|
740 public IInformationControl createInformationControl(Shell parent) {
|
|
741 return new DefaultInformationControl(parent, false);
|
|
742 }
|
|
743 };
|
|
744 }
|
|
745
|
|
746 /**
|
|
747 * Sets the document partitioning this content assistant is using.
|
|
748 *
|
|
749 * @param partitioning the document partitioning for this content assistant
|
|
750 */
|
|
751 public void setDocumentPartitioning(String partitioning) {
|
|
752 Assert.isNotNull(partitioning);
|
|
753 fPartitioning= partitioning;
|
|
754 }
|
|
755
|
|
756 /*
|
|
757 * @see dwtx.jface.text.contentassist.IContentAssistantExtension#getDocumentPartitioning()
|
|
758 * @since 3.0
|
|
759 */
|
|
760 public String getDocumentPartitioning() {
|
|
761 return fPartitioning;
|
|
762 }
|
|
763
|
|
764 /**
|
|
765 * Registers a given content assist processor for a particular content type.
|
|
766 * If there is already a processor registered for this type, the new processor
|
|
767 * is registered instead of the old one.
|
|
768 *
|
|
769 * @param processor the content assist processor to register, or <code>null</code> to remove an existing one
|
|
770 * @param contentType the content type under which to register
|
|
771 */
|
|
772 public void setContentAssistProcessor(IContentAssistProcessor processor, String contentType) {
|
|
773
|
|
774 Assert.isNotNull(contentType);
|
|
775
|
|
776 if (fProcessors is null)
|
|
777 fProcessors= new HashMap();
|
|
778
|
|
779 if (processor is null)
|
|
780 fProcessors.remove(contentType);
|
|
781 else
|
|
782 fProcessors.put(contentType, processor);
|
|
783 }
|
|
784
|
|
785 /*
|
|
786 * @see IContentAssistant#getContentAssistProcessor
|
|
787 */
|
|
788 public IContentAssistProcessor getContentAssistProcessor(String contentType) {
|
|
789 if (fProcessors is null)
|
|
790 return null;
|
|
791
|
|
792 return (IContentAssistProcessor) fProcessors.get(contentType);
|
|
793 }
|
|
794
|
|
795 /**
|
|
796 * Enables the content assistant's auto activation mode.
|
|
797 *
|
|
798 * @param enabled indicates whether auto activation is enabled or not
|
|
799 */
|
|
800 public void enableAutoActivation(bool enabled) {
|
|
801 fIsAutoActivated= enabled;
|
|
802 manageAutoActivation(fIsAutoActivated);
|
|
803 }
|
|
804
|
|
805 /**
|
|
806 * Enables the content assistant's auto insertion mode. If enabled,
|
|
807 * the content assistant inserts a proposal automatically if it is
|
|
808 * the only proposal. In the case of ambiguities, the user must
|
|
809 * make the choice.
|
|
810 *
|
|
811 * @param enabled indicates whether auto insertion is enabled or not
|
|
812 * @since 2.0
|
|
813 */
|
|
814 public void enableAutoInsert(bool enabled) {
|
|
815 fIsAutoInserting= enabled;
|
|
816 }
|
|
817
|
|
818 /**
|
|
819 * Returns whether this content assistant is in the auto insertion
|
|
820 * mode or not.
|
|
821 *
|
|
822 * @return <code>true</code> if in auto insertion mode
|
|
823 * @since 2.0
|
|
824 */
|
|
825 bool isAutoInserting() {
|
|
826 return fIsAutoInserting;
|
|
827 }
|
|
828
|
|
829 /**
|
|
830 * Installs and uninstall the listeners needed for auto-activation.
|
|
831 * @param start <code>true</code> if listeners must be installed,
|
|
832 * <code>false</code> if they must be removed
|
|
833 * @since 2.0
|
|
834 */
|
|
835 private void manageAutoActivation(bool start) {
|
|
836 if (start) {
|
|
837
|
|
838 if (fViewer !is null && fAutoAssistListener is null) {
|
|
839 fAutoAssistListener= new AutoAssistListener();
|
|
840 if (fViewer instanceof ITextViewerExtension) {
|
|
841 ITextViewerExtension extension= (ITextViewerExtension) fViewer;
|
|
842 extension.appendVerifyKeyListener(fAutoAssistListener);
|
|
843 } else {
|
|
844 StyledText textWidget= fViewer.getTextWidget();
|
|
845 if (Helper2.okToUse(textWidget))
|
|
846 textWidget.addVerifyKeyListener(fAutoAssistListener);
|
|
847 }
|
|
848 }
|
|
849
|
|
850 } else if (fAutoAssistListener !is null) {
|
|
851
|
|
852 if (fViewer instanceof ITextViewerExtension) {
|
|
853 ITextViewerExtension extension= (ITextViewerExtension) fViewer;
|
|
854 extension.removeVerifyKeyListener(fAutoAssistListener);
|
|
855 } else {
|
|
856 StyledText textWidget= fViewer.getTextWidget();
|
|
857 if (Helper2.okToUse(textWidget))
|
|
858 textWidget.removeVerifyKeyListener(fAutoAssistListener);
|
|
859 }
|
|
860
|
|
861 fAutoAssistListener= null;
|
|
862 }
|
|
863 }
|
|
864
|
|
865 /**
|
|
866 * Sets the delay after which the content assistant is automatically invoked
|
|
867 * if the cursor is behind an auto activation character.
|
|
868 *
|
|
869 * @param delay the auto activation delay
|
|
870 */
|
|
871 public void setAutoActivationDelay(int delay) {
|
|
872 fAutoActivationDelay= delay;
|
|
873 }
|
|
874
|
|
875 /**
|
|
876 * Sets the proposal pop-ups' orientation.
|
|
877 * The following values may be used:
|
|
878 * <ul>
|
|
879 * <li>PROPOSAL_OVERLAY<p>
|
|
880 * proposal popup windows should overlay each other
|
|
881 * </li>
|
|
882 * <li>PROPOSAL_REMOVE<p>
|
|
883 * any currently shown proposal popup should be closed
|
|
884 * </li>
|
|
885 * <li>PROPOSAL_STACKED<p>
|
|
886 * proposal popup windows should be vertical stacked, with no overlap,
|
|
887 * beneath the line containing the current cursor location
|
|
888 * </li>
|
|
889 * </ul>
|
|
890 *
|
|
891 * @param orientation the popup's orientation
|
|
892 */
|
|
893 public void setProposalPopupOrientation(int orientation) {
|
|
894 fProposalPopupOrientation= orientation;
|
|
895 }
|
|
896
|
|
897 /**
|
|
898 * Sets the context information popup's orientation.
|
|
899 * The following values may be used:
|
|
900 * <ul>
|
|
901 * <li>CONTEXT_ABOVE<p>
|
|
902 * context information popup should always appear above the line containing
|
|
903 * the current cursor location
|
|
904 * </li>
|
|
905 * <li>CONTEXT_BELOW<p>
|
|
906 * context information popup should always appear below the line containing
|
|
907 * the current cursor location
|
|
908 * </li>
|
|
909 * </ul>
|
|
910 *
|
|
911 * @param orientation the popup's orientation
|
|
912 */
|
|
913 public void setContextInformationPopupOrientation(int orientation) {
|
|
914 fContextInfoPopupOrientation= orientation;
|
|
915 }
|
|
916
|
|
917 /**
|
|
918 * Sets the context information popup's background color.
|
|
919 *
|
|
920 * @param background the background color
|
|
921 */
|
|
922 public void setContextInformationPopupBackground(Color background) {
|
|
923 fContextInfoPopupBackground= background;
|
|
924 }
|
|
925
|
|
926 /**
|
|
927 * Returns the background of the context information popup.
|
|
928 *
|
|
929 * @return the background of the context information popup
|
|
930 * @since 2.0
|
|
931 */
|
|
932 Color getContextInformationPopupBackground() {
|
|
933 return fContextInfoPopupBackground;
|
|
934 }
|
|
935
|
|
936 /**
|
|
937 * Sets the context information popup's foreground color.
|
|
938 *
|
|
939 * @param foreground the foreground color
|
|
940 * @since 2.0
|
|
941 */
|
|
942 public void setContextInformationPopupForeground(Color foreground) {
|
|
943 fContextInfoPopupForeground= foreground;
|
|
944 }
|
|
945
|
|
946 /**
|
|
947 * Returns the foreground of the context information popup.
|
|
948 *
|
|
949 * @return the foreground of the context information popup
|
|
950 * @since 2.0
|
|
951 */
|
|
952 Color getContextInformationPopupForeground() {
|
|
953 return fContextInfoPopupForeground;
|
|
954 }
|
|
955
|
|
956 /**
|
|
957 * Sets the context selector's background color.
|
|
958 *
|
|
959 * @param background the background color
|
|
960 * @since 2.0
|
|
961 */
|
|
962 public void setContextSelectorBackground(Color background) {
|
|
963 fContextSelectorBackground= background;
|
|
964 }
|
|
965
|
|
966 /**
|
|
967 * Returns the background of the context selector.
|
|
968 *
|
|
969 * @return the background of the context selector
|
|
970 * @since 2.0
|
|
971 */
|
|
972 Color getContextSelectorBackground() {
|
|
973 return fContextSelectorBackground;
|
|
974 }
|
|
975
|
|
976 /**
|
|
977 * Sets the context selector's foreground color.
|
|
978 *
|
|
979 * @param foreground the foreground color
|
|
980 * @since 2.0
|
|
981 */
|
|
982 public void setContextSelectorForeground(Color foreground) {
|
|
983 fContextSelectorForeground= foreground;
|
|
984 }
|
|
985
|
|
986 /**
|
|
987 * Returns the foreground of the context selector.
|
|
988 *
|
|
989 * @return the foreground of the context selector
|
|
990 * @since 2.0
|
|
991 */
|
|
992 Color getContextSelectorForeground() {
|
|
993 return fContextSelectorForeground;
|
|
994 }
|
|
995
|
|
996 /**
|
|
997 * Sets the information control creator for the additional information control.
|
|
998 *
|
|
999 * @param creator the information control creator for the additional information control
|
|
1000 * @since 2.0
|
|
1001 */
|
|
1002 public void setInformationControlCreator(IInformationControlCreator creator) {
|
|
1003 fInformationControlCreator= creator;
|
|
1004 }
|
|
1005
|
|
1006 /*
|
|
1007 * @see IContentAssist#install
|
|
1008 */
|
|
1009 public void install(ITextViewer textViewer) {
|
|
1010 Assert.isNotNull(textViewer);
|
|
1011
|
|
1012 fViewer= textViewer;
|
|
1013
|
|
1014 fLayoutManager= new LayoutManager();
|
|
1015 fInternalListener= new InternalListener();
|
|
1016
|
|
1017 AdditionalInfoController2 controller= null;
|
|
1018 if (fInformationControlCreator !is null) {
|
|
1019 int delay= fAutoActivationDelay;
|
|
1020 if (delay is 0)
|
|
1021 delay= DEFAULT_AUTO_ACTIVATION_DELAY;
|
|
1022 delay= Math.round(delay * 1.5f);
|
|
1023 controller= new AdditionalInfoController2(fInformationControlCreator, delay);
|
|
1024 }
|
|
1025 fContextInfoPopup= new ContextInformationPopup2(this, fViewer);
|
|
1026 fProposalPopup= new CompletionProposalPopup2(this, fViewer, controller);
|
|
1027
|
|
1028 manageAutoActivation(fIsAutoActivated);
|
|
1029 }
|
|
1030
|
|
1031 /*
|
|
1032 * @see IContentAssist#uninstall
|
|
1033 */
|
|
1034 public void uninstall() {
|
|
1035
|
|
1036 if (fProposalPopup !is null)
|
|
1037 fProposalPopup.hide();
|
|
1038
|
|
1039 if (fContextInfoPopup !is null)
|
|
1040 fContextInfoPopup.hide();
|
|
1041
|
|
1042 manageAutoActivation(false);
|
|
1043
|
|
1044 if (fCloser !is null) {
|
|
1045 fCloser.uninstall();
|
|
1046 fCloser= null;
|
|
1047 }
|
|
1048
|
|
1049 fViewer= null;
|
|
1050 }
|
|
1051
|
|
1052 /**
|
|
1053 * Adds the given shell of the specified type to the layout.
|
|
1054 * Valid types are defined by <code>LayoutManager</code>.
|
|
1055 *
|
|
1056 * @param popup a content assist popup
|
|
1057 * @param shell the shell of the content-assist popup
|
|
1058 * @param type the type of popup
|
|
1059 * @param visibleOffset the offset at which to layout the popup relative to the offset of the viewer's visible region
|
|
1060 * @since 2.0
|
|
1061 */
|
|
1062 void addToLayout(Object popup, Shell shell, int type, int visibleOffset) {
|
|
1063 fLayoutManager.add(popup, shell, type, visibleOffset);
|
|
1064 }
|
|
1065
|
|
1066 /**
|
|
1067 * Layouts the registered popup of the given type relative to the
|
|
1068 * given offset. The offset is relative to the offset of the viewer's visible region.
|
|
1069 * Valid types are defined by <code>LayoutManager</code>.
|
|
1070 *
|
|
1071 * @param type the type of popup to layout
|
|
1072 * @param visibleOffset the offset at which to layout relative to the offset of the viewer's visible region
|
|
1073 * @since 2.0
|
|
1074 */
|
|
1075 void layout(int type, int visibleOffset) {
|
|
1076 fLayoutManager.layout(type, visibleOffset);
|
|
1077 }
|
|
1078
|
|
1079 /**
|
|
1080 * Notifies the controller that a popup has lost focus.
|
|
1081 *
|
|
1082 * @param e the focus event
|
|
1083 */
|
|
1084 void popupFocusLost(FocusEvent e) {
|
|
1085 fCloser.focusLost(e);
|
|
1086 }
|
|
1087
|
|
1088 /**
|
|
1089 * Returns the offset of the selection relative to the offset of the visible region.
|
|
1090 *
|
|
1091 * @return the offset of the selection relative to the offset of the visible region
|
|
1092 * @since 2.0
|
|
1093 */
|
|
1094 int getSelectionOffset() {
|
|
1095 StyledText text= fViewer.getTextWidget();
|
|
1096 return text.getSelectionRange().x;
|
|
1097 }
|
|
1098
|
|
1099 /**
|
|
1100 * Returns whether the widget token could be acquired.
|
|
1101 * The following are valid listener types:
|
|
1102 * <ul>
|
|
1103 * <li>AUTO_ASSIST
|
|
1104 * <li>CONTEXT_SELECTOR
|
|
1105 * <li>PROPOSAL_SELECTOR
|
|
1106 * <li>CONTEXT_INFO_POPUP
|
|
1107 * <ul>
|
|
1108 * @param type the listener type for which to acquire
|
|
1109 * @return <code>true</code> if the widget token could be acquired
|
|
1110 * @since 2.0
|
|
1111 */
|
|
1112 private bool acquireWidgetToken(int type) {
|
|
1113 switch (type) {
|
|
1114 case CONTEXT_SELECTOR:
|
|
1115 case PROPOSAL_SELECTOR:
|
|
1116 if (fViewer instanceof IWidgetTokenOwner) {
|
|
1117 IWidgetTokenOwner owner= (IWidgetTokenOwner) fViewer;
|
|
1118 return owner.requestWidgetToken(this);
|
|
1119 } else if (fViewer instanceof IWidgetTokenOwnerExtension) {
|
|
1120 IWidgetTokenOwnerExtension extension= (IWidgetTokenOwnerExtension) fViewer;
|
|
1121 return extension.requestWidgetToken(this, WIDGET_PRIORITY);
|
|
1122 }
|
|
1123 }
|
|
1124 return true;
|
|
1125 }
|
|
1126
|
|
1127 /**
|
|
1128 * Registers a content assist listener.
|
|
1129 * The following are valid listener types:
|
|
1130 * <ul>
|
|
1131 * <li>AUTO_ASSIST
|
|
1132 * <li>CONTEXT_SELECTOR
|
|
1133 * <li>PROPOSAL_SELECTOR
|
|
1134 * <li>CONTEXT_INFO_POPUP
|
|
1135 * <ul>
|
|
1136 * Returns whether the listener could be added successfully. A listener
|
|
1137 * can not be added if the widget token could not be acquired.
|
|
1138 *
|
|
1139 * @param listener the listener to register
|
|
1140 * @param type the type of listener
|
|
1141 * @return <code>true</code> if the listener could be added
|
|
1142 */
|
|
1143 bool addContentAssistListener(IContentAssistListener2 listener, int type) {
|
|
1144
|
|
1145 if (acquireWidgetToken(type)) {
|
|
1146
|
|
1147 fListeners[type]= listener;
|
|
1148
|
|
1149 if (getNumberOfListeners() is 1) {
|
|
1150 fCloser= new Closer();
|
|
1151 fCloser.install();
|
|
1152 fViewer.setEventConsumer(fInternalListener);
|
|
1153 installKeyListener();
|
|
1154 }
|
|
1155 return true;
|
|
1156 }
|
|
1157
|
|
1158 return false;
|
|
1159 }
|
|
1160
|
|
1161 /**
|
|
1162 * Installs a key listener on the text viewer's widget.
|
|
1163 */
|
|
1164 private void installKeyListener() {
|
|
1165 if (!fKeyListenerHooked) {
|
|
1166 StyledText text= fViewer.getTextWidget();
|
|
1167 if (Helper2.okToUse(text)) {
|
|
1168
|
|
1169 if (fViewer instanceof ITextViewerExtension) {
|
|
1170 ITextViewerExtension e= (ITextViewerExtension) fViewer;
|
|
1171 e.prependVerifyKeyListener(fInternalListener);
|
|
1172 } else {
|
|
1173 text.addVerifyKeyListener(fInternalListener);
|
|
1174 }
|
|
1175
|
|
1176 fKeyListenerHooked= true;
|
|
1177 }
|
|
1178 }
|
|
1179 }
|
|
1180
|
|
1181 /**
|
|
1182 * Releases the previously acquired widget token if the token
|
|
1183 * is no longer necessary.
|
|
1184 * The following are valid listener types:
|
|
1185 * <ul>
|
|
1186 * <li>AUTO_ASSIST
|
|
1187 * <li>CONTEXT_SELECTOR
|
|
1188 * <li>PROPOSAL_SELECTOR
|
|
1189 * <li>CONTEXT_INFO_POPUP
|
|
1190 * <ul>
|
|
1191 *
|
|
1192 * @param type the listener type
|
|
1193 * @since 2.0
|
|
1194 */
|
|
1195 private void releaseWidgetToken(int type) {
|
|
1196 if (fListeners[CONTEXT_SELECTOR] is null && fListeners[PROPOSAL_SELECTOR] is null) {
|
|
1197 if (fViewer instanceof IWidgetTokenOwner) {
|
|
1198 IWidgetTokenOwner owner= (IWidgetTokenOwner) fViewer;
|
|
1199 owner.releaseWidgetToken(this);
|
|
1200 }
|
|
1201 }
|
|
1202 }
|
|
1203
|
|
1204 /**
|
|
1205 * Unregisters a content assist listener.
|
|
1206 *
|
|
1207 * @param listener the listener to unregister
|
|
1208 * @param type the type of listener
|
|
1209 *
|
|
1210 * @see #addContentAssistListener
|
|
1211 */
|
|
1212 void removeContentAssistListener(IContentAssistListener2 listener, int type) {
|
|
1213 fListeners[type]= null;
|
|
1214
|
|
1215 if (getNumberOfListeners() is 0) {
|
|
1216
|
|
1217 if (fCloser !is null) {
|
|
1218 fCloser.uninstall();
|
|
1219 fCloser= null;
|
|
1220 }
|
|
1221
|
|
1222 uninstallKeyListener();
|
|
1223 fViewer.setEventConsumer(null);
|
|
1224 }
|
|
1225
|
|
1226 releaseWidgetToken(type);
|
|
1227 }
|
|
1228
|
|
1229 /**
|
|
1230 * Uninstall the key listener from the text viewer's widget.
|
|
1231 */
|
|
1232 private void uninstallKeyListener() {
|
|
1233 if (fKeyListenerHooked) {
|
|
1234 StyledText text= fViewer.getTextWidget();
|
|
1235 if (Helper2.okToUse(text)) {
|
|
1236
|
|
1237 if (fViewer instanceof ITextViewerExtension) {
|
|
1238 ITextViewerExtension e= (ITextViewerExtension) fViewer;
|
|
1239 e.removeVerifyKeyListener(fInternalListener);
|
|
1240 } else {
|
|
1241 text.removeVerifyKeyListener(fInternalListener);
|
|
1242 }
|
|
1243
|
|
1244 fKeyListenerHooked= false;
|
|
1245 }
|
|
1246 }
|
|
1247 }
|
|
1248
|
|
1249 /**
|
|
1250 * Returns the number of listeners.
|
|
1251 *
|
|
1252 * @return the number of listeners
|
|
1253 * @since 2.0
|
|
1254 */
|
|
1255 private int getNumberOfListeners() {
|
|
1256 int count= 0;
|
|
1257 for (int i= 0; i <= CONTEXT_INFO_POPUP; i++) {
|
|
1258 if (fListeners[i] !is null)
|
|
1259 ++ count;
|
|
1260 }
|
|
1261 return count;
|
|
1262 }
|
|
1263
|
|
1264 /*
|
|
1265 * @see IContentAssist#showPossibleCompletions
|
|
1266 */
|
|
1267 public String showPossibleCompletions() {
|
|
1268 return fProposalPopup.showProposals(false);
|
|
1269 }
|
|
1270
|
|
1271 /**
|
|
1272 * Hides the proposal popup.
|
|
1273 */
|
|
1274 public void hidePossibleCompletions() {
|
|
1275 if (fProposalPopup !is null)
|
|
1276 fProposalPopup.hide();
|
|
1277 }
|
|
1278
|
|
1279 /**
|
|
1280 * Hides any open pop-ups.
|
|
1281 */
|
|
1282 protected void hide() {
|
|
1283 if (fProposalPopup !is null)
|
|
1284 fProposalPopup.hide();
|
|
1285 if (fContextInfoPopup !is null)
|
|
1286 fContextInfoPopup.hide();
|
|
1287 }
|
|
1288
|
|
1289 /**
|
|
1290 * Callback to signal this content assistant that the presentation of the possible completions has been stopped.
|
|
1291 * @since 2.1
|
|
1292 */
|
|
1293 protected void possibleCompletionsClosed() {
|
|
1294 }
|
|
1295
|
|
1296 /*
|
|
1297 * @see IContentAssist#showContextInformation
|
|
1298 */
|
|
1299 public String showContextInformation() {
|
|
1300 return fContextInfoPopup.showContextProposals(false);
|
|
1301 }
|
|
1302
|
|
1303
|
|
1304 /**
|
|
1305 * Callback to signal this content assistant that the presentation of the context information has been stopped.
|
|
1306 * @since 2.1
|
|
1307 */
|
|
1308 protected void contextInformationClosed() {
|
|
1309 }
|
|
1310
|
|
1311 /**
|
|
1312 * Requests that the specified context information to be shown.
|
|
1313 *
|
|
1314 * @param contextInformation the context information to be shown
|
|
1315 * @param position the position to which the context information refers to
|
|
1316 * @since 2.0
|
|
1317 */
|
|
1318 void showContextInformation(IContextInformation contextInformation, int position) {
|
|
1319 fContextInfoPopup.showContextInformation(contextInformation, position);
|
|
1320 }
|
|
1321
|
|
1322 /**
|
|
1323 * Returns the current content assist error message.
|
|
1324 *
|
|
1325 * @return an error message or <code>null</code> if no error has occurred
|
|
1326 */
|
|
1327 String getErrorMessage() {
|
|
1328 return fLastErrorMessage;
|
|
1329 }
|
|
1330
|
|
1331 /**
|
|
1332 * Returns the content assist processor for the content
|
|
1333 * type of the specified document position.
|
|
1334 *
|
|
1335 * @param viewer the text viewer
|
|
1336 * @param offset a offset within the document
|
|
1337 * @return a content-assist processor or <code>null</code> if none exists
|
|
1338 */
|
|
1339 private IContentAssistProcessor getProcessor(ITextViewer viewer, int offset) {
|
|
1340 try {
|
|
1341 String type= TextUtilities.getContentType(viewer.getDocument(), getDocumentPartitioning(), offset, true);
|
|
1342 return getContentAssistProcessor(type);
|
|
1343 } catch (BadLocationException x) {
|
|
1344 }
|
|
1345 return null;
|
|
1346 }
|
|
1347
|
|
1348 /**
|
|
1349 * Returns an array of completion proposals computed based on
|
|
1350 * the specified document position. The position is used to
|
|
1351 * determine the appropriate content assist processor to invoke.
|
|
1352 *
|
|
1353 * @param viewer the viewer for which to compute the proposals
|
|
1354 * @param position a document position
|
|
1355 * @return an array of completion proposals
|
|
1356 *
|
|
1357 * @see IContentAssistProcessor#computeCompletionProposals
|
|
1358 */
|
|
1359 ICompletionProposal[] computeCompletionProposals(ITextViewer viewer, int position) {
|
|
1360 if (fProposals !is null) {
|
|
1361 return fProposals;
|
|
1362 } else if (fProposalStrings !is null) {
|
|
1363 ICompletionProposal[] result= new ICompletionProposal[fProposalStrings.length];
|
|
1364 for (int i= 0; i < fProposalStrings.length; i++) {
|
|
1365 result[i]= new CompletionProposal(fProposalStrings[i], position, fProposalStrings[i].length(), fProposalStrings[i].length());
|
|
1366 }
|
|
1367 return result;
|
|
1368 } else return null;
|
|
1369 }
|
|
1370
|
|
1371 /**
|
|
1372 * Returns an array of context information objects computed based
|
|
1373 * on the specified document position. The position is used to determine
|
|
1374 * the appropriate content assist processor to invoke.
|
|
1375 *
|
|
1376 * @param viewer the viewer for which to compute the context information
|
|
1377 * @param position a document position
|
|
1378 * @return an array of context information objects
|
|
1379 *
|
|
1380 * @see IContentAssistProcessor#computeContextInformation
|
|
1381 */
|
|
1382 IContextInformation[] computeContextInformation(ITextViewer viewer, int position) {
|
|
1383 fLastErrorMessage= null;
|
|
1384
|
|
1385 IContextInformation[] result= null;
|
|
1386
|
|
1387 IContentAssistProcessor p= getProcessor(viewer, position);
|
|
1388 if (p !is null) {
|
|
1389 result= p.computeContextInformation(viewer, position);
|
|
1390 fLastErrorMessage= p.getErrorMessage();
|
|
1391 }
|
|
1392
|
|
1393 return result;
|
|
1394 }
|
|
1395
|
|
1396 /**
|
|
1397 * Returns the context information validator that should be used to
|
|
1398 * determine when the currently displayed context information should
|
|
1399 * be dismissed. The position is used to determine the appropriate
|
|
1400 * content assist processor to invoke.
|
|
1401 *
|
|
1402 * @param textViewer the text viewer
|
|
1403 * @param offset a document offset
|
|
1404 * @return an validator
|
|
1405 *
|
|
1406 * @see IContentAssistProcessor#getContextInformationValidator
|
|
1407 */
|
|
1408 IContextInformationValidator getContextInformationValidator(ITextViewer textViewer, int offset) {
|
|
1409 IContentAssistProcessor p= getProcessor(textViewer, offset);
|
|
1410 return p !is null ? p.getContextInformationValidator() : null;
|
|
1411 }
|
|
1412
|
|
1413 /**
|
|
1414 * Returns the context information presenter that should be used to
|
|
1415 * display context information. The position is used to determine the appropriate
|
|
1416 * content assist processor to invoke.
|
|
1417 *
|
|
1418 * @param textViewer the text viewer
|
|
1419 * @param offset a document offset
|
|
1420 * @return a presenter
|
|
1421 * @since 2.0
|
|
1422 */
|
|
1423 IContextInformationPresenter getContextInformationPresenter(ITextViewer textViewer, int offset) {
|
|
1424 IContextInformationValidator validator= getContextInformationValidator(textViewer, offset);
|
|
1425 if (validator instanceof IContextInformationPresenter)
|
|
1426 return (IContextInformationPresenter) validator;
|
|
1427 return null;
|
|
1428 }
|
|
1429
|
|
1430 /**
|
|
1431 * Returns the characters which when typed by the user should automatically
|
|
1432 * initiate proposing completions. The position is used to determine the
|
|
1433 * appropriate content assist processor to invoke.
|
|
1434 *
|
|
1435 * @param textViewer the text viewer
|
|
1436 * @param offset a document offset
|
|
1437 * @return the auto activation characters
|
|
1438 *
|
|
1439 * @see IContentAssistProcessor#getCompletionProposalAutoActivationCharacters
|
|
1440 */
|
|
1441 private char[] getCompletionProposalAutoActivationCharacters(ITextViewer textViewer, int offset) {
|
|
1442 IContentAssistProcessor p= getProcessor(textViewer, offset);
|
|
1443 return p !is null ? p.getCompletionProposalAutoActivationCharacters() : null;
|
|
1444 }
|
|
1445
|
|
1446 /**
|
|
1447 * Returns the characters which when typed by the user should automatically
|
|
1448 * initiate the presentation of context information. The position is used
|
|
1449 * to determine the appropriate content assist processor to invoke.
|
|
1450 *
|
|
1451 * @param textViewer the text viewer
|
|
1452 * @param offset a document offset
|
|
1453 * @return the auto activation characters
|
|
1454 *
|
|
1455 * @see IContentAssistProcessor#getContextInformationAutoActivationCharacters
|
|
1456 */
|
|
1457 private char[] getContextInformationAutoActivationCharacters(ITextViewer textViewer, int offset) {
|
|
1458 IContentAssistProcessor p= getProcessor(textViewer, offset);
|
|
1459 return p !is null ? p.getContextInformationAutoActivationCharacters() : null;
|
|
1460 }
|
|
1461
|
|
1462 /*
|
|
1463 * @see dwtx.jface.text.IWidgetTokenKeeper#requestWidgetToken(IWidgetTokenOwner)
|
|
1464 * @since 2.0
|
|
1465 */
|
|
1466 public bool requestWidgetToken(IWidgetTokenOwner owner) {
|
|
1467 hidePossibleCompletions();
|
|
1468 return true;
|
|
1469 }
|
|
1470
|
|
1471 /**
|
|
1472 * @param completionPosition
|
|
1473 */
|
|
1474 public void setCompletionPosition(int completionPosition) {
|
|
1475 fCompletionPosition= completionPosition;
|
|
1476 }
|
|
1477
|
|
1478 /**
|
|
1479 * @return the completion position
|
|
1480 */
|
|
1481 public int getCompletionPosition() {
|
|
1482 return fCompletionPosition;
|
|
1483 }
|
|
1484
|
|
1485 /**
|
|
1486 * @param proposals
|
|
1487 */
|
|
1488 public void setCompletions(String[] proposals) {
|
|
1489 fProposalStrings= proposals;
|
|
1490 }
|
|
1491
|
|
1492 /**
|
|
1493 * @param proposals
|
|
1494 */
|
|
1495 public void setCompletions(ICompletionProposal[] proposals) {
|
|
1496 fProposals= proposals;
|
|
1497 }
|
|
1498
|
|
1499 /*
|
|
1500 * @see dwtx.jface.text.IWidgetTokenKeeperExtension#requestWidgetToken(dwtx.jface.text.IWidgetTokenOwner, int)
|
|
1501 * @since 3.0
|
|
1502 */
|
|
1503 public bool requestWidgetToken(IWidgetTokenOwner owner, int priority) {
|
|
1504 if (priority > WIDGET_PRIORITY) {
|
|
1505 hidePossibleCompletions();
|
|
1506 return true;
|
|
1507 }
|
|
1508 return false;
|
|
1509 }
|
|
1510
|
|
1511 /*
|
|
1512 * @see dwtx.jface.text.IWidgetTokenKeeperExtension#setFocus(dwtx.jface.text.IWidgetTokenOwner)
|
|
1513 * @since 3.0
|
|
1514 */
|
|
1515 public bool setFocus(IWidgetTokenOwner owner) {
|
|
1516 if (fProposalPopup !is null) {
|
|
1517 fProposalPopup.setFocus();
|
|
1518 return fProposalPopup.hasFocus();
|
|
1519 }
|
|
1520 return false;
|
|
1521 }
|
|
1522
|
|
1523 /**
|
|
1524 * Returns whether any popups controlled by the receiver have the input focus.
|
|
1525 *
|
|
1526 * @return <code>true</code> if any of the managed popups have the focus, <code>false</code> otherwise
|
|
1527 */
|
|
1528 public bool hasFocus() {
|
|
1529 return (fProposalPopup !is null && fProposalPopup.hasFocus())
|
|
1530 || (fContextInfoPopup !is null && fContextInfoPopup.hasFocus());
|
|
1531 }
|
|
1532
|
|
1533 /*
|
|
1534 * @see dwtx.jface.text.contentassist.IContentAssistantExtension#completePrefix()
|
|
1535 */
|
|
1536 public String completePrefix() {
|
|
1537 return null;
|
|
1538 }
|
|
1539
|
|
1540 /**
|
|
1541 * @param proposal
|
|
1542 */
|
|
1543 public void fireProposalChosen(ICompletionProposal proposal) {
|
|
1544 List list= new ArrayList(fProposalListeners);
|
|
1545 for (Iterator it= list.iterator(); it.hasNext();) {
|
|
1546 IProposalListener listener= (IProposalListener) it.next();
|
|
1547 listener.proposalChosen(proposal);
|
|
1548 }
|
|
1549
|
|
1550 }
|
|
1551
|
|
1552 /**
|
|
1553 * @param listener
|
|
1554 */
|
|
1555 public void removeProposalListener(IProposalListener listener) {
|
|
1556 fProposalListeners.remove(listener);
|
|
1557 }
|
|
1558
|
|
1559 /**
|
|
1560 * @param listener
|
|
1561 */
|
|
1562 public void addProposalListener(IProposalListener listener) {
|
|
1563 fProposalListeners.add(listener);
|
|
1564 }
|
|
1565
|
|
1566 /**
|
|
1567 * Tells whether the support for colored labels is enabled.
|
|
1568 *
|
|
1569 * @return <code>true</code> if the support for colored labels is enabled, <code>false</code> otherwise
|
|
1570 * @since 3.4
|
|
1571 */
|
|
1572 bool isColoredLabelsSupportEnabled() {
|
|
1573 return fIsColoredLabelsSupportEnabled;
|
|
1574 }
|
|
1575
|
|
1576 /**
|
|
1577 * Enables the support for colored labels in the proposal popup.
|
|
1578 * <p>Completion proposals can implement {@link ICompletionProposalExtension6}
|
|
1579 * to provide colored proposal labels.</p>
|
|
1580 *
|
|
1581 * @param isEnabled if <code>true</code> the support for colored labels is enabled in the proposal popup
|
|
1582 * @since 3.4
|
|
1583 */
|
|
1584 public void enableColoredLabels(bool isEnabled) {
|
|
1585 fIsColoredLabelsSupportEnabled= isEnabled;
|
|
1586 }
|
|
1587 }
|
|
1588
|