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