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.text.contentassist.ContextInformationPopup;
|
|
14
|
131
|
15 import dwtx.jface.text.contentassist.ContentAssistEvent; // packageimport
|
|
16 import dwtx.jface.text.contentassist.Helper; // packageimport
|
|
17 import dwtx.jface.text.contentassist.PopupCloser; // packageimport
|
|
18 import dwtx.jface.text.contentassist.IContentAssistant; // packageimport
|
|
19 import dwtx.jface.text.contentassist.CompletionProposal; // packageimport
|
|
20 import dwtx.jface.text.contentassist.ICompletionProposalExtension5; // packageimport
|
|
21 import dwtx.jface.text.contentassist.IContextInformationValidator; // packageimport
|
|
22 import dwtx.jface.text.contentassist.IContentAssistListener; // packageimport
|
|
23 import dwtx.jface.text.contentassist.ICompletionProposalExtension6; // packageimport
|
|
24 import dwtx.jface.text.contentassist.ICompletionListener; // packageimport
|
|
25 import dwtx.jface.text.contentassist.ICompletionProposalExtension2; // packageimport
|
|
26 import dwtx.jface.text.contentassist.IContentAssistantExtension4; // packageimport
|
|
27 import dwtx.jface.text.contentassist.ContextInformation; // packageimport
|
|
28 import dwtx.jface.text.contentassist.ICompletionProposalExtension3; // packageimport
|
|
29 import dwtx.jface.text.contentassist.ContextInformationValidator; // packageimport
|
|
30 import dwtx.jface.text.contentassist.ICompletionProposal; // packageimport
|
|
31 import dwtx.jface.text.contentassist.IContentAssistProcessor; // packageimport
|
|
32 import dwtx.jface.text.contentassist.AdditionalInfoController; // packageimport
|
|
33 import dwtx.jface.text.contentassist.IContextInformationPresenter; // packageimport
|
|
34 import dwtx.jface.text.contentassist.ICompletionProposalExtension4; // packageimport
|
|
35 import dwtx.jface.text.contentassist.ICompletionListenerExtension; // packageimport
|
|
36 import dwtx.jface.text.contentassist.IContextInformationExtension; // packageimport
|
|
37 import dwtx.jface.text.contentassist.IContentAssistantExtension2; // packageimport
|
|
38 import dwtx.jface.text.contentassist.ContentAssistSubjectControlAdapter; // packageimport
|
|
39 import dwtx.jface.text.contentassist.CompletionProposalPopup; // packageimport
|
|
40 import dwtx.jface.text.contentassist.ICompletionProposalExtension; // packageimport
|
|
41 import dwtx.jface.text.contentassist.IContextInformation; // packageimport
|
|
42 import dwtx.jface.text.contentassist.IContentAssistantExtension3; // packageimport
|
|
43 import dwtx.jface.text.contentassist.ContentAssistant; // packageimport
|
|
44 import dwtx.jface.text.contentassist.IContentAssistantExtension; // packageimport
|
|
45 import dwtx.jface.text.contentassist.JFaceTextMessages; // packageimport
|
|
46
|
|
47
|
129
|
48 import dwt.dwthelper.utils;
|
|
49
|
|
50 import java.util.Iterator;
|
|
51 import java.util.Stack;
|
|
52
|
|
53 import dwt.DWT;
|
|
54 import dwt.custom.BusyIndicator;
|
|
55 import dwt.custom.StyledText;
|
|
56 import dwt.events.KeyEvent;
|
|
57 import dwt.events.SelectionAdapter;
|
|
58 import dwt.events.SelectionEvent;
|
|
59 import dwt.events.SelectionListener;
|
|
60 import dwt.events.VerifyEvent;
|
|
61 import dwt.graphics.Color;
|
|
62 import dwt.graphics.Point;
|
|
63 import dwt.graphics.Rectangle;
|
|
64 import dwt.layout.GridData;
|
|
65 import dwt.layout.GridLayout;
|
|
66 import dwt.widgets.Control;
|
|
67 import dwt.widgets.Display;
|
|
68 import dwt.widgets.Shell;
|
|
69 import dwt.widgets.Table;
|
|
70 import dwt.widgets.TableItem;
|
|
71 import dwtx.jface.contentassist.IContentAssistSubjectControl;
|
|
72 import dwtx.jface.text.ITextViewer;
|
|
73 import dwtx.jface.text.TextPresentation;
|
|
74
|
|
75
|
|
76 /**
|
|
77 * This class is used to present context information to the user.
|
|
78 * If multiple contexts are valid at the current cursor location,
|
|
79 * a list is presented from which the user may choose one context.
|
|
80 * Once the user makes their choice, or if there was only a single
|
|
81 * possible context, the context information is shown in a tool tip like popup. <p>
|
|
82 * If the tool tip is visible and the user wants to see context information of
|
|
83 * a context embedded into the one for which context information is displayed,
|
|
84 * context information for the embedded context is shown. As soon as the
|
|
85 * cursor leaves the embedded context area, the context information for
|
|
86 * the embedding context is shown again.
|
|
87 *
|
|
88 * @see IContextInformation
|
|
89 * @see IContextInformationValidator
|
|
90 */
|
|
91 class ContextInformationPopup : IContentAssistListener {
|
|
92
|
|
93
|
|
94 /**
|
|
95 * Represents the state necessary for embedding contexts.
|
|
96 *
|
|
97 * @since 2.0
|
|
98 */
|
|
99 static class ContextFrame {
|
|
100
|
|
101 final int fBeginOffset;
|
|
102 final int fOffset;
|
|
103 final int fVisibleOffset;
|
|
104 final IContextInformation fInformation;
|
|
105 final IContextInformationValidator fValidator;
|
|
106 final IContextInformationPresenter fPresenter;
|
|
107
|
|
108 /*
|
|
109 * @since 3.1
|
|
110 */
|
|
111 public ContextFrame(IContextInformation information, int beginOffset, int offset, int visibleOffset, IContextInformationValidator validator, IContextInformationPresenter presenter) {
|
|
112 fInformation = information;
|
|
113 fBeginOffset = beginOffset;
|
|
114 fOffset = offset;
|
|
115 fVisibleOffset = visibleOffset;
|
|
116 fValidator = validator;
|
|
117 fPresenter = presenter;
|
|
118 }
|
|
119
|
|
120 /*
|
|
121 * @see java.lang.Object#equals(java.lang.Object)
|
|
122 * @since 3.0
|
|
123 */
|
|
124 public bool equals(Object obj) {
|
|
125 if (obj instanceof ContextFrame) {
|
|
126 ContextFrame frame= (ContextFrame) obj;
|
|
127 return fInformation.equals(frame.fInformation) && fBeginOffset is frame.fBeginOffset;
|
|
128 }
|
|
129 return super.equals(obj);
|
|
130 }
|
|
131
|
|
132 /*
|
|
133 * @see java.lang.Object#hashCode()
|
|
134 * @since 3.1
|
|
135 */
|
|
136 public int hashCode() {
|
|
137 return (fInformation.hashCode() << 16) | fBeginOffset;
|
|
138 }
|
|
139 }
|
|
140
|
|
141 private ITextViewer fViewer;
|
|
142 private ContentAssistant fContentAssistant;
|
|
143
|
|
144 private PopupCloser fPopupCloser= new PopupCloser();
|
|
145 private Shell fContextSelectorShell;
|
|
146 private Table fContextSelectorTable;
|
|
147 private IContextInformation[] fContextSelectorInput;
|
|
148 private String fLineDelimiter= null;
|
|
149
|
|
150 private Shell fContextInfoPopup;
|
|
151 private StyledText fContextInfoText;
|
|
152 private TextPresentation fTextPresentation;
|
|
153
|
|
154 private Stack fContextFrameStack= new Stack();
|
|
155 /**
|
|
156 * The content assist subject control.
|
|
157 *
|
|
158 * @since 3.0
|
|
159 */
|
|
160 private IContentAssistSubjectControl fContentAssistSubjectControl;
|
|
161 /**
|
|
162 * The content assist subject control adapter.
|
|
163 *
|
|
164 * @since 3.0
|
|
165 */
|
|
166 private ContentAssistSubjectControlAdapter fContentAssistSubjectControlAdapter;
|
|
167
|
|
168 /**
|
|
169 * Selection listener on the text widget which is active
|
|
170 * while a context information pop up is shown.
|
|
171 *
|
|
172 * @since 3.0
|
|
173 */
|
|
174 private SelectionListener fTextWidgetSelectionListener;
|
|
175
|
|
176 /**
|
|
177 * The last removed context frame is remembered in order to not re-query the
|
|
178 * user about which context should be used.
|
|
179 *
|
|
180 * @since 3.0
|
|
181 */
|
|
182 private ContextFrame fLastContext= null;
|
|
183
|
|
184 /**
|
|
185 * Creates a new context information popup.
|
|
186 *
|
|
187 * @param contentAssistant the content assist for computing the context information
|
|
188 * @param viewer the viewer on top of which the context information is shown
|
|
189 */
|
|
190 public ContextInformationPopup(ContentAssistant contentAssistant, ITextViewer viewer) {
|
|
191 fContentAssistant= contentAssistant;
|
|
192 fViewer= viewer;
|
|
193 fContentAssistSubjectControlAdapter= new ContentAssistSubjectControlAdapter(fViewer);
|
|
194 }
|
|
195
|
|
196 /**
|
|
197 * Creates a new context information popup.
|
|
198 *
|
|
199 * @param contentAssistant the content assist for computing the context information
|
|
200 * @param contentAssistSubjectControl the content assist subject control on top of which the context information is shown
|
|
201 * @since 3.0
|
|
202 */
|
|
203 public ContextInformationPopup(ContentAssistant contentAssistant, IContentAssistSubjectControl contentAssistSubjectControl) {
|
|
204 fContentAssistant= contentAssistant;
|
|
205 fContentAssistSubjectControl= contentAssistSubjectControl;
|
|
206 fContentAssistSubjectControlAdapter= new ContentAssistSubjectControlAdapter(fContentAssistSubjectControl);
|
|
207 }
|
|
208
|
|
209 /**
|
|
210 * Shows all possible contexts for the given cursor position of the viewer.
|
|
211 *
|
|
212 * @param autoActivated <code>true</code> if auto activated
|
|
213 * @return a potential error message or <code>null</code> in case of no error
|
|
214 */
|
|
215 public String showContextProposals(final bool autoActivated) {
|
|
216 final Control control= fContentAssistSubjectControlAdapter.getControl();
|
|
217 BusyIndicator.showWhile(control.getDisplay(), new Runnable() {
|
|
218 public void run() {
|
|
219
|
|
220 int offset= fContentAssistSubjectControlAdapter.getSelectedRange().x;
|
|
221
|
|
222 IContextInformation[] contexts= computeContextInformation(offset);
|
|
223 int count = (contexts is null ? 0 : contexts.length);
|
|
224 if (count is 1) {
|
|
225
|
|
226 ContextFrame frame= createContextFrame(contexts[0], offset);
|
|
227 if (isDuplicate(frame))
|
|
228 validateContextInformation();
|
|
229 else
|
|
230 // Show context information directly
|
|
231 internalShowContextInfo(frame);
|
|
232
|
|
233 } else if (count > 0) {
|
|
234
|
|
235 // if any of the proposed context matches any of the contexts on the stack,
|
|
236 // assume that one (so, if context info is invoked repeatedly, the current
|
|
237 // info is kept)
|
|
238 for (int i= 0; i < contexts.length; i++) {
|
|
239 IContextInformation info= contexts[i];
|
|
240 ContextFrame frame= createContextFrame(info, offset);
|
|
241
|
|
242 // check top of stack and stored context
|
|
243 if (isDuplicate(frame)) {
|
|
244 validateContextInformation();
|
|
245 return;
|
|
246 }
|
|
247
|
|
248 if (isLastFrame(frame)) {
|
|
249 internalShowContextInfo(frame);
|
|
250 return;
|
|
251 }
|
|
252
|
|
253 // also check all other contexts
|
|
254 for (Iterator it= fContextFrameStack.iterator(); it.hasNext(); ) {
|
|
255 ContextFrame stackFrame= (ContextFrame) it.next();
|
|
256 if (stackFrame.equals(frame)) {
|
|
257 validateContextInformation();
|
|
258 return;
|
|
259 }
|
|
260 }
|
|
261 }
|
|
262
|
|
263 // otherwise:
|
|
264 // Precise context must be selected
|
|
265
|
|
266 if (fLineDelimiter is null)
|
|
267 fLineDelimiter= fContentAssistSubjectControlAdapter.getLineDelimiter();
|
|
268
|
|
269 createContextSelector();
|
|
270 setContexts(contexts);
|
|
271 displayContextSelector();
|
|
272 }
|
|
273 }
|
|
274 });
|
|
275
|
|
276 return getErrorMessage();
|
|
277 }
|
|
278
|
|
279 /**
|
|
280 * Displays the given context information for the given offset.
|
|
281 *
|
|
282 * @param info the context information
|
|
283 * @param offset the offset
|
|
284 * @since 2.0
|
|
285 */
|
|
286 public void showContextInformation(final IContextInformation info, final int offset) {
|
|
287 Control control= fContentAssistSubjectControlAdapter.getControl();
|
|
288 BusyIndicator.showWhile(control.getDisplay(), new Runnable() {
|
|
289 public void run() {
|
|
290 if (info is null)
|
|
291 validateContextInformation();
|
|
292 else {
|
|
293 ContextFrame frame= createContextFrame(info, offset);
|
|
294 if (isDuplicate(frame))
|
|
295 validateContextInformation();
|
|
296 else
|
|
297 internalShowContextInfo(frame);
|
|
298 hideContextSelector();
|
|
299 }
|
|
300 }
|
|
301 });
|
|
302 }
|
|
303
|
|
304 /**
|
|
305 * Displays the given context information for the given offset.
|
|
306 *
|
|
307 * @param frame the context frame to display, or <code>null</code>
|
|
308 * @since 3.0
|
|
309 */
|
|
310 private void internalShowContextInfo(ContextFrame frame) {
|
|
311 if (frame !is null) {
|
|
312 fContextFrameStack.push(frame);
|
|
313 if (fContextFrameStack.size() is 1)
|
|
314 fLastContext= null;
|
|
315 internalShowContextFrame(frame, fContextFrameStack.size() is 1);
|
|
316 validateContextInformation();
|
|
317 }
|
|
318 }
|
|
319
|
|
320 /**
|
|
321 * Creates a context frame for the given offset.
|
|
322 *
|
|
323 * @param information the context information
|
|
324 * @param offset the offset
|
|
325 * @return the created context frame
|
|
326 * @since 3.0
|
|
327 */
|
|
328 private ContextFrame createContextFrame(IContextInformation information, int offset) {
|
|
329 IContextInformationValidator validator= fContentAssistSubjectControlAdapter.getContextInformationValidator(fContentAssistant, offset);
|
|
330
|
|
331 if (validator !is null) {
|
|
332 int beginOffset= (information instanceof IContextInformationExtension) ? ((IContextInformationExtension) information).getContextInformationPosition() : offset;
|
|
333 if (beginOffset is -1) beginOffset= offset;
|
|
334 int visibleOffset= fContentAssistSubjectControlAdapter.getWidgetSelectionRange().x - (offset - beginOffset);
|
|
335 IContextInformationPresenter presenter = fContentAssistSubjectControlAdapter.getContextInformationPresenter(fContentAssistant, offset);
|
|
336 return new ContextFrame(information, beginOffset, offset, visibleOffset, validator, presenter);
|
|
337 }
|
|
338
|
|
339 return null;
|
|
340 }
|
|
341
|
|
342 /**
|
|
343 * Compares <code>frame</code> with the top of the stack, returns <code>true</code>
|
|
344 * if the frames are the same.
|
|
345 *
|
|
346 * @param frame the frame to check
|
|
347 * @return <code>true</code> if <code>frame</code> matches the top of the stack
|
|
348 * @since 3.0
|
|
349 */
|
|
350 private bool isDuplicate(ContextFrame frame) {
|
|
351 if (frame is null)
|
|
352 return false;
|
|
353 if (fContextFrameStack.isEmpty())
|
|
354 return false;
|
|
355 // stack not empty
|
|
356 ContextFrame top= (ContextFrame) fContextFrameStack.peek();
|
|
357 return frame.equals(top);
|
|
358 }
|
|
359
|
|
360 /**
|
|
361 * Compares <code>frame</code> with most recently removed context frame, returns <code>true</code>
|
|
362 * if the frames are the same.
|
|
363 *
|
|
364 * @param frame the frame to check
|
|
365 * @return <code>true</code> if <code>frame</code> matches the most recently removed
|
|
366 * @since 3.0
|
|
367 */
|
|
368 private bool isLastFrame(ContextFrame frame) {
|
|
369 return frame !is null && frame.equals(fLastContext);
|
|
370 }
|
|
371
|
|
372 /**
|
|
373 * Shows the given context frame.
|
|
374 *
|
|
375 * @param frame the frame to display
|
|
376 * @param initial <code>true</code> if this is the first frame to be displayed
|
|
377 * @since 2.0
|
|
378 */
|
|
379 private void internalShowContextFrame(ContextFrame frame, bool initial) {
|
|
380
|
|
381 fContentAssistSubjectControlAdapter.installValidator(frame);
|
|
382
|
|
383 if (frame.fPresenter !is null) {
|
|
384 if (fTextPresentation is null)
|
|
385 fTextPresentation= new TextPresentation();
|
|
386 fContentAssistSubjectControlAdapter.installContextInformationPresenter(frame);
|
|
387 frame.fPresenter.updatePresentation(frame.fOffset, fTextPresentation);
|
|
388 }
|
|
389
|
|
390 createContextInfoPopup();
|
|
391
|
|
392 fContextInfoText.setText(frame.fInformation.getInformationDisplayString());
|
|
393 if (fTextPresentation !is null)
|
|
394 TextPresentation.applyTextPresentation(fTextPresentation, fContextInfoText);
|
|
395 resize(frame.fVisibleOffset);
|
|
396
|
|
397 if (initial) {
|
|
398 if (fContentAssistant.addContentAssistListener(this, ContentAssistant.CONTEXT_INFO_POPUP)) {
|
|
399 if (fContentAssistSubjectControlAdapter.getControl() !is null) {
|
|
400 fTextWidgetSelectionListener= new SelectionAdapter() {
|
|
401 /*
|
|
402 * @see dwt.events.SelectionAdapter#widgetSelected(dwt.events.SelectionEvent)
|
|
403 */
|
|
404 public void widgetSelected(SelectionEvent e) {
|
|
405 validateContextInformation();
|
|
406 }};
|
|
407 fContentAssistSubjectControlAdapter.addSelectionListener(fTextWidgetSelectionListener);
|
|
408 }
|
|
409 fContentAssistant.addToLayout(this, fContextInfoPopup, ContentAssistant.LayoutManager.LAYOUT_CONTEXT_INFO_POPUP, frame.fVisibleOffset);
|
|
410 fContextInfoPopup.setVisible(true);
|
|
411 }
|
|
412 } else {
|
|
413 fContentAssistant.layout(ContentAssistant.LayoutManager.LAYOUT_CONTEXT_INFO_POPUP, frame.fVisibleOffset);
|
|
414 }
|
|
415 }
|
|
416
|
|
417 /**
|
|
418 * Computes all possible context information for the given offset.
|
|
419 *
|
|
420 * @param offset the offset
|
|
421 * @return all possible context information for the given offset
|
|
422 * @since 2.0
|
|
423 */
|
|
424 private IContextInformation[] computeContextInformation(int offset) {
|
|
425 return fContentAssistSubjectControlAdapter.computeContextInformation(fContentAssistant, offset);
|
|
426 }
|
|
427
|
|
428 /**
|
|
429 *Returns the error message generated while computing context information.
|
|
430 *
|
|
431 * @return the error message
|
|
432 */
|
|
433 private String getErrorMessage() {
|
|
434 return fContentAssistant.getErrorMessage();
|
|
435 }
|
|
436
|
|
437 /**
|
|
438 * Creates the context information popup. This is the tool tip like overlay window.
|
|
439 */
|
|
440 private void createContextInfoPopup() {
|
|
441 if (Helper.okToUse(fContextInfoPopup))
|
|
442 return;
|
|
443
|
|
444 Control control= fContentAssistSubjectControlAdapter.getControl();
|
|
445 Display display= control.getDisplay();
|
|
446
|
|
447 fContextInfoPopup= new Shell(control.getShell(), DWT.NO_TRIM | DWT.ON_TOP);
|
|
448 fContextInfoPopup.setBackground(display.getSystemColor(DWT.COLOR_BLACK));
|
|
449
|
|
450 fContextInfoText= new StyledText(fContextInfoPopup, DWT.MULTI | DWT.READ_ONLY | DWT.WRAP);
|
|
451
|
|
452 Color c= fContentAssistant.getContextInformationPopupBackground();
|
|
453 if (c is null)
|
|
454 c= display.getSystemColor(DWT.COLOR_INFO_BACKGROUND);
|
|
455 fContextInfoText.setBackground(c);
|
|
456
|
|
457 c= fContentAssistant.getContextInformationPopupForeground();
|
|
458 if (c is null)
|
|
459 c= display.getSystemColor(DWT.COLOR_INFO_FOREGROUND);
|
|
460 fContextInfoText.setForeground(c);
|
|
461 }
|
|
462
|
|
463 /**
|
|
464 * Resizes the context information popup.
|
|
465 *
|
|
466 * @param offset the caret offset in widget coordinates
|
|
467 * @since 2.0
|
|
468 */
|
|
469 private void resize(int offset) {
|
|
470 Point size= fContextInfoText.computeSize(DWT.DEFAULT, DWT.DEFAULT, true);
|
|
471 final int TEXT_PAD= 0;
|
|
472 final int BORDER_PAD= 2;
|
|
473 final int PAD= TEXT_PAD + BORDER_PAD;
|
|
474 size.x += PAD;
|
|
475 Rectangle bounds= fContentAssistant.getLayoutManager().computeBoundsAboveBelow(fContextInfoPopup, size, offset);
|
|
476 if (bounds.width < size.x)
|
|
477 // we don't fit on the screen - try again and wrap
|
|
478 size= fContextInfoText.computeSize(bounds.width - PAD, DWT.DEFAULT, true);
|
|
479
|
|
480 size.x += TEXT_PAD;
|
|
481 fContextInfoText.setSize(size);
|
|
482 fContextInfoText.setLocation(1,1);
|
|
483 size.x += BORDER_PAD;
|
|
484 size.y += BORDER_PAD;
|
|
485 fContextInfoPopup.setSize(size);
|
|
486 }
|
|
487
|
|
488 /**
|
|
489 * Hides the context information popup.
|
|
490 */
|
|
491 private void hideContextInfoPopup() {
|
|
492
|
|
493 if (Helper.okToUse(fContextInfoPopup)) {
|
|
494
|
|
495 int size= fContextFrameStack.size();
|
|
496 if (size > 0) {
|
|
497 fLastContext= (ContextFrame) fContextFrameStack.pop();
|
|
498 -- size;
|
|
499 }
|
|
500
|
|
501 if (size > 0) {
|
|
502 ContextFrame current= (ContextFrame) fContextFrameStack.peek();
|
|
503 internalShowContextFrame(current, false);
|
|
504 } else {
|
|
505
|
|
506 fContentAssistant.removeContentAssistListener(this, ContentAssistant.CONTEXT_INFO_POPUP);
|
|
507
|
|
508 if (fContentAssistSubjectControlAdapter.getControl() !is null)
|
|
509 fContentAssistSubjectControlAdapter.removeSelectionListener(fTextWidgetSelectionListener);
|
|
510 fTextWidgetSelectionListener= null;
|
|
511
|
|
512 fContextInfoPopup.setVisible(false);
|
|
513 fContextInfoPopup.dispose();
|
|
514 fContextInfoPopup= null;
|
|
515
|
|
516 if (fTextPresentation !is null) {
|
|
517 fTextPresentation.clear();
|
|
518 fTextPresentation= null;
|
|
519 }
|
|
520 }
|
|
521 }
|
|
522
|
|
523 if (fContextInfoPopup is null)
|
|
524 fContentAssistant.contextInformationClosed();
|
|
525 }
|
|
526
|
|
527 /**
|
|
528 * Creates the context selector in case the user has the choice between multiple valid contexts
|
|
529 * at a given offset.
|
|
530 */
|
|
531 private void createContextSelector() {
|
|
532 if (Helper.okToUse(fContextSelectorShell))
|
|
533 return;
|
|
534
|
|
535 Control control= fContentAssistSubjectControlAdapter.getControl();
|
|
536 fContextSelectorShell= new Shell(control.getShell(), DWT.ON_TOP | DWT.RESIZE);
|
|
537 GridLayout layout= new GridLayout();
|
|
538 layout.marginWidth= 0;
|
|
539 layout.marginHeight= 0;
|
|
540 fContextSelectorShell.setLayout(layout);
|
|
541 fContextSelectorShell.setBackground(control.getDisplay().getSystemColor(DWT.COLOR_BLACK));
|
|
542
|
|
543
|
|
544 fContextSelectorTable= new Table(fContextSelectorShell, DWT.H_SCROLL | DWT.V_SCROLL);
|
|
545 fContextSelectorTable.setLocation(1, 1);
|
|
546 GridData gd= new GridData(GridData.FILL_BOTH);
|
|
547 gd.heightHint= fContextSelectorTable.getItemHeight() * 10;
|
|
548 gd.widthHint= 300;
|
|
549 fContextSelectorTable.setLayoutData(gd);
|
|
550
|
|
551 fContextSelectorShell.pack(true);
|
|
552
|
|
553 Color c= fContentAssistant.getContextSelectorBackground();
|
|
554 if (c is null)
|
|
555 c= control.getDisplay().getSystemColor(DWT.COLOR_INFO_BACKGROUND);
|
|
556 fContextSelectorTable.setBackground(c);
|
|
557
|
|
558 c= fContentAssistant.getContextSelectorForeground();
|
|
559 if (c is null)
|
|
560 c= control.getDisplay().getSystemColor(DWT.COLOR_INFO_FOREGROUND);
|
|
561 fContextSelectorTable.setForeground(c);
|
|
562
|
|
563 fContextSelectorTable.addSelectionListener(new SelectionListener() {
|
|
564 public void widgetSelected(SelectionEvent e) {
|
|
565 }
|
|
566
|
|
567 public void widgetDefaultSelected(SelectionEvent e) {
|
|
568 insertSelectedContext();
|
|
569 hideContextSelector();
|
|
570 }
|
|
571 });
|
|
572
|
|
573 fPopupCloser.install(fContentAssistant, fContextSelectorTable);
|
|
574
|
|
575 fContextSelectorTable.setHeaderVisible(false);
|
|
576 fContentAssistant.addToLayout(this, fContextSelectorShell, ContentAssistant.LayoutManager.LAYOUT_CONTEXT_SELECTOR, fContentAssistant.getSelectionOffset());
|
|
577 }
|
|
578
|
|
579 /**
|
|
580 * Returns the minimal required height for the popup, may return 0 if the popup has not been
|
|
581 * created yet.
|
|
582 *
|
|
583 * @return the minimal height
|
|
584 * @since 3.3
|
|
585 */
|
|
586 int getMinimalHeight() {
|
|
587 int height= 0;
|
|
588 if (Helper.okToUse(fContextSelectorTable)) {
|
|
589 int items= fContextSelectorTable.getItemHeight() * 10;
|
|
590 Rectangle trim= fContextSelectorTable.computeTrim(0, 0, DWT.DEFAULT, items);
|
|
591 height= trim.height;
|
|
592 }
|
|
593 return height;
|
|
594 }
|
|
595
|
|
596 /**
|
|
597 * Causes the context information of the context selected in the context selector
|
|
598 * to be displayed in the context information popup.
|
|
599 */
|
|
600 private void insertSelectedContext() {
|
|
601 int i= fContextSelectorTable.getSelectionIndex();
|
|
602
|
|
603 if (i < 0 || i >= fContextSelectorInput.length)
|
|
604 return;
|
|
605
|
|
606 int offset= fContentAssistSubjectControlAdapter.getSelectedRange().x;
|
|
607 internalShowContextInfo(createContextFrame(fContextSelectorInput[i], offset));
|
|
608 }
|
|
609
|
|
610 /**
|
|
611 * Sets the contexts in the context selector to the given set.
|
|
612 *
|
|
613 * @param contexts the possible contexts
|
|
614 */
|
|
615 private void setContexts(IContextInformation[] contexts) {
|
|
616 if (Helper.okToUse(fContextSelectorTable)) {
|
|
617
|
|
618 fContextSelectorInput= contexts;
|
|
619
|
|
620 fContextSelectorTable.setRedraw(false);
|
|
621 fContextSelectorTable.removeAll();
|
|
622
|
|
623 TableItem item;
|
|
624 IContextInformation t;
|
|
625 for (int i= 0; i < contexts.length; i++) {
|
|
626 t= contexts[i];
|
|
627 item= new TableItem(fContextSelectorTable, DWT.NULL);
|
|
628 if (t.getImage() !is null)
|
|
629 item.setImage(t.getImage());
|
|
630 item.setText(t.getContextDisplayString());
|
|
631 }
|
|
632
|
|
633 fContextSelectorTable.select(0);
|
|
634 fContextSelectorTable.setRedraw(true);
|
|
635 }
|
|
636 }
|
|
637
|
|
638 /**
|
|
639 * Displays the context selector.
|
|
640 */
|
|
641 private void displayContextSelector() {
|
|
642 if (fContentAssistant.addContentAssistListener(this, ContentAssistant.CONTEXT_SELECTOR))
|
|
643 fContextSelectorShell.setVisible(true);
|
|
644 }
|
|
645
|
|
646 /**
|
|
647 * Hides the context selector.
|
|
648 */
|
|
649 private void hideContextSelector() {
|
|
650 if (Helper.okToUse(fContextSelectorShell)) {
|
|
651 fContentAssistant.removeContentAssistListener(this, ContentAssistant.CONTEXT_SELECTOR);
|
|
652
|
|
653 fPopupCloser.uninstall();
|
|
654 fContextSelectorShell.setVisible(false);
|
|
655 fContextSelectorShell.dispose();
|
|
656 fContextSelectorShell= null;
|
|
657 }
|
|
658
|
|
659 if (!Helper.okToUse(fContextInfoPopup))
|
|
660 fContentAssistant.contextInformationClosed();
|
|
661 }
|
|
662
|
|
663 /**
|
|
664 *Returns whether the context selector has the focus.
|
|
665 *
|
|
666 * @return <code>true</code> if the context selector has the focus
|
|
667 */
|
|
668 public bool hasFocus() {
|
|
669 if (Helper.okToUse(fContextSelectorShell))
|
|
670 return (fContextSelectorShell.isFocusControl() || fContextSelectorTable.isFocusControl());
|
|
671
|
|
672 return false;
|
|
673 }
|
|
674
|
|
675 /**
|
|
676 * Hides context selector and context information popup.
|
|
677 */
|
|
678 public void hide() {
|
|
679 hideContextSelector();
|
|
680 hideContextInfoPopup();
|
|
681 }
|
|
682
|
|
683 /**
|
|
684 * Returns whether this context information popup is active. I.e., either
|
|
685 * a context selector or context information is displayed.
|
|
686 *
|
|
687 * @return <code>true</code> if the context selector is active
|
|
688 */
|
|
689 public bool isActive() {
|
|
690 return (Helper.okToUse(fContextInfoPopup) || Helper.okToUse(fContextSelectorShell));
|
|
691 }
|
|
692
|
|
693 /*
|
|
694 * @see IContentAssistListener#verifyKey(VerifyEvent)
|
|
695 */
|
|
696 public bool verifyKey(VerifyEvent e) {
|
|
697 if (Helper.okToUse(fContextSelectorShell))
|
|
698 return contextSelectorKeyPressed(e);
|
|
699 if (Helper.okToUse(fContextInfoPopup))
|
|
700 return contextInfoPopupKeyPressed(e);
|
|
701 return true;
|
|
702 }
|
|
703
|
|
704 /**
|
|
705 * Processes a key stroke in the context selector.
|
|
706 *
|
|
707 * @param e the verify event describing the key stroke
|
|
708 * @return <code>true</code> if processing can be stopped
|
|
709 */
|
|
710 private bool contextSelectorKeyPressed(VerifyEvent e) {
|
|
711
|
|
712 char key= e.character;
|
|
713 if (key is 0) {
|
|
714
|
|
715 int newSelection= fContextSelectorTable.getSelectionIndex();
|
|
716 int visibleRows= (fContextSelectorTable.getSize().y / fContextSelectorTable.getItemHeight()) - 1;
|
|
717 int itemCount= fContextSelectorTable.getItemCount();
|
|
718 switch (e.keyCode) {
|
|
719 case DWT.ARROW_UP :
|
|
720 newSelection -= 1;
|
|
721 if (newSelection < 0)
|
|
722 newSelection= itemCount - 1;
|
|
723 break;
|
|
724
|
|
725 case DWT.ARROW_DOWN :
|
|
726 newSelection += 1;
|
|
727 if (newSelection > itemCount - 1)
|
|
728 newSelection= 0;
|
|
729 break;
|
|
730
|
|
731 case DWT.PAGE_DOWN :
|
|
732 newSelection += visibleRows;
|
|
733 if (newSelection >= itemCount)
|
|
734 newSelection= itemCount - 1;
|
|
735 break;
|
|
736
|
|
737 case DWT.PAGE_UP :
|
|
738 newSelection -= visibleRows;
|
|
739 if (newSelection < 0)
|
|
740 newSelection= 0;
|
|
741 break;
|
|
742
|
|
743 case DWT.HOME :
|
|
744 newSelection= 0;
|
|
745 break;
|
|
746
|
|
747 case DWT.END :
|
|
748 newSelection= itemCount - 1;
|
|
749 break;
|
|
750
|
|
751 default :
|
|
752 if (e.keyCode !is DWT.CAPS_LOCK && e.keyCode !is DWT.MOD1 && e.keyCode !is DWT.MOD2 && e.keyCode !is DWT.MOD3 && e.keyCode !is DWT.MOD4)
|
|
753 hideContextSelector();
|
|
754 return true;
|
|
755 }
|
|
756
|
|
757 fContextSelectorTable.setSelection(newSelection);
|
|
758 fContextSelectorTable.showSelection();
|
|
759 e.doit= false;
|
|
760 return false;
|
|
761
|
|
762 } else if ('\t' is key) {
|
|
763 // switch focus to selector shell
|
|
764 e.doit= false;
|
|
765 fContextSelectorShell.setFocus();
|
|
766 return false;
|
|
767 } else if (key is DWT.ESC) {
|
|
768 e.doit= false;
|
|
769 hideContextSelector();
|
|
770 }
|
|
771
|
|
772 return true;
|
|
773 }
|
|
774
|
|
775 /**
|
|
776 * Processes a key stroke while the info popup is up.
|
|
777 *
|
|
778 * @param e the verify event describing the key stroke
|
|
779 * @return <code>true</code> if processing can be stopped
|
|
780 */
|
|
781 private bool contextInfoPopupKeyPressed(KeyEvent e) {
|
|
782
|
|
783 char key= e.character;
|
|
784 if (key is 0) {
|
|
785
|
|
786 switch (e.keyCode) {
|
|
787 case DWT.ARROW_LEFT:
|
|
788 case DWT.ARROW_RIGHT:
|
|
789 validateContextInformation();
|
|
790 break;
|
|
791 default:
|
|
792 if (e.keyCode !is DWT.CAPS_LOCK && e.keyCode !is DWT.MOD1 && e.keyCode !is DWT.MOD2 && e.keyCode !is DWT.MOD3 && e.keyCode !is DWT.MOD4)
|
|
793 hideContextInfoPopup();
|
|
794 break;
|
|
795 }
|
|
796
|
|
797 } else if (key is DWT.ESC) {
|
|
798 e.doit= false;
|
|
799 hideContextInfoPopup();
|
|
800 } else {
|
|
801 validateContextInformation();
|
|
802 }
|
|
803 return true;
|
|
804 }
|
|
805
|
|
806 /*
|
|
807 * @see IEventConsumer#processEvent(VerifyEvent)
|
|
808 */
|
|
809 public void processEvent(VerifyEvent event) {
|
|
810 if (Helper.okToUse(fContextSelectorShell))
|
|
811 contextSelectorProcessEvent(event);
|
|
812 if (Helper.okToUse(fContextInfoPopup))
|
|
813 contextInfoPopupProcessEvent(event);
|
|
814 }
|
|
815
|
|
816 /**
|
|
817 * Processes a key stroke in the context selector.
|
|
818 *
|
|
819 * @param e the verify event describing the key stroke
|
|
820 */
|
|
821 private void contextSelectorProcessEvent(VerifyEvent e) {
|
|
822
|
|
823 if (e.start is e.end && e.text !is null && e.text.equals(fLineDelimiter)) {
|
|
824 e.doit= false;
|
|
825 insertSelectedContext();
|
|
826 }
|
|
827
|
|
828 hideContextSelector();
|
|
829 }
|
|
830
|
|
831 /**
|
|
832 * Processes a key stroke while the info popup is up.
|
|
833 *
|
|
834 * @param e the verify event describing the key stroke
|
|
835 */
|
|
836 private void contextInfoPopupProcessEvent(VerifyEvent e) {
|
|
837 if (e.start !is e.end && (e.text is null || e.text.length() is 0))
|
|
838 validateContextInformation();
|
|
839 }
|
|
840
|
|
841 /**
|
|
842 * Validates the context information for the viewer's actual cursor position.
|
|
843 */
|
|
844 private void validateContextInformation() {
|
|
845 /*
|
|
846 * Post the code in the event queue in order to ensure that the
|
|
847 * action described by this verify key event has already been executed.
|
|
848 * Otherwise, we'd validate the context information based on the
|
|
849 * pre-key-stroke state.
|
|
850 */
|
|
851 if (!Helper.okToUse(fContextInfoPopup))
|
|
852 return;
|
|
853
|
|
854 fContextInfoPopup.getDisplay().asyncExec(new Runnable() {
|
|
855
|
|
856 private ContextFrame fFrame= (ContextFrame) fContextFrameStack.peek();
|
|
857
|
|
858 public void run() {
|
|
859 // only do this if no other frames have been added in between
|
|
860 if (!fContextFrameStack.isEmpty() && fFrame is fContextFrameStack.peek()) {
|
|
861 int offset= fContentAssistSubjectControlAdapter.getSelectedRange().x;
|
|
862
|
|
863 // iterate all contexts on the stack
|
|
864 while (Helper.okToUse(fContextInfoPopup) && !fContextFrameStack.isEmpty()) {
|
|
865 ContextFrame top= (ContextFrame) fContextFrameStack.peek();
|
|
866 if (top.fValidator is null || !top.fValidator.isContextInformationValid(offset)) {
|
|
867 hideContextInfoPopup(); // loop variant: reduces the number of contexts on the stack
|
|
868 } else if (top.fPresenter !is null && top.fPresenter.updatePresentation(offset, fTextPresentation)) {
|
|
869 int widgetOffset= fContentAssistSubjectControlAdapter.getWidgetSelectionRange().x;
|
|
870 TextPresentation.applyTextPresentation(fTextPresentation, fContextInfoText);
|
|
871 resize(widgetOffset);
|
|
872 break;
|
|
873 } else
|
|
874 break;
|
|
875 }
|
|
876 }
|
|
877 }
|
|
878 });
|
|
879 }
|
|
880 }
|