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