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