comparison org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/ContextInformationPopup.d @ 12:bc29606a740c

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