KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > jface > internal > text > link > contentassist > ContextInformationPopup2


1 /*******************************************************************************
2  * Copyright (c) 2000, 2005 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  *******************************************************************************/

11
12 package org.eclipse.jface.internal.text.link.contentassist;
13
14
15 import java.util.Stack JavaDoc;
16
17 import org.eclipse.jface.text.ITextViewer;
18 import org.eclipse.jface.text.TextPresentation;
19 import org.eclipse.jface.text.contentassist.IContextInformation;
20 import org.eclipse.jface.text.contentassist.IContextInformationExtension;
21 import org.eclipse.jface.text.contentassist.IContextInformationPresenter;
22 import org.eclipse.jface.text.contentassist.IContextInformationValidator;
23 import org.eclipse.swt.SWT;
24 import org.eclipse.swt.custom.BusyIndicator;
25 import org.eclipse.swt.custom.StyledText;
26 import org.eclipse.swt.events.KeyEvent;
27 import org.eclipse.swt.events.SelectionEvent;
28 import org.eclipse.swt.events.SelectionListener;
29 import org.eclipse.swt.events.VerifyEvent;
30 import org.eclipse.swt.graphics.Color;
31 import org.eclipse.swt.graphics.Point;
32 import org.eclipse.swt.layout.GridData;
33 import org.eclipse.swt.layout.GridLayout;
34 import org.eclipse.swt.widgets.Control;
35 import org.eclipse.swt.widgets.Display;
36 import org.eclipse.swt.widgets.Shell;
37 import org.eclipse.swt.widgets.Table;
38 import org.eclipse.swt.widgets.TableItem;
39
40
41 /**
42  * This class is used to present context information to the user.
43  * If multiple contexts are valid at the current cursor location,
44  * a list is presented from which the user may choose one context.
45  * Once the user makes their choice, or if there was only a single
46  * possible context, the context information is shown in a tooltip like popup. <p>
47  * If the tooltip is visible and the user wants to see context information of
48  * a context embedded into the one for which context information is displayed,
49  * context information for the embedded context is shown. As soon as the
50  * cursor leaves the embedded context area, the context information for
51  * the embedding context is shown again.
52  *
53  * @see IContextInformation
54  * @see IContextInformationValidator
55  */

56 class ContextInformationPopup2 implements IContentAssistListener2 {
57
58
59
60     /**
61      * Represents the state necessary for embedding contexts.
62      * @since 2.0
63      */

64     static class ContextFrame {
65         public int fBeginOffset;
66         public int fOffset;
67         public int fVisibleOffset;
68         public IContextInformation fInformation;
69         public IContextInformationValidator fValidator;
70         public IContextInformationPresenter fPresenter;
71     }
72
73     private ITextViewer fViewer;
74     private ContentAssistant2 fContentAssistant;
75
76     private PopupCloser2 fPopupCloser= new PopupCloser2();
77     private Shell fContextSelectorShell;
78     private Table fContextSelectorTable;
79     private IContextInformation[] fContextSelectorInput;
80     private String JavaDoc fLineDelimiter= null;
81
82     private Shell fContextInfoPopup;
83     private StyledText fContextInfoText;
84     private TextPresentation fTextPresentation;
85
86     private Stack JavaDoc fContextFrameStack= new Stack JavaDoc();
87
88
89     /**
90      * Creates a new context information popup.
91      *
92      * @param contentAssistant the content assist for computing the context information
93      * @param viewer the viewer on top of which the context information is shown
94      */

95     public ContextInformationPopup2(ContentAssistant2 contentAssistant, ITextViewer viewer) {
96         fContentAssistant= contentAssistant;
97         fViewer= viewer;
98     }
99
100     /**
101      * Shows all possible contexts for the given cursor position of the viewer.
102      *
103      * @param autoActivated <code>true</code> if auto activated
104      * @return a potential error message or <code>null</code> in case of no error
105      */

106     public String JavaDoc showContextProposals(final boolean autoActivated) {
107         final StyledText styledText= fViewer.getTextWidget();
108         BusyIndicator.showWhile(styledText.getDisplay(), new Runnable JavaDoc() {
109             public void run() {
110
111                 int position= fViewer.getSelectedRange().x;
112
113                 IContextInformation[] contexts= computeContextInformation(position);
114                 int count = (contexts == null ? 0 : contexts.length);
115                 if (count == 1) {
116
117                     // Show context information directly
118
internalShowContextInfo(contexts[0], position);
119
120                 } else if (count > 0) {
121                     // Precise context must be selected
122

123                     if (fLineDelimiter == null)
124                         fLineDelimiter= styledText.getLineDelimiter();
125
126                     createContextSelector();
127                     setContexts(contexts);
128                     displayContextSelector();
129                     hideContextInfoPopup();
130
131                 } else if (!autoActivated) {
132                     styledText.getDisplay().beep();
133                 }
134             }
135         });
136
137         return getErrorMessage();
138     }
139
140     /**
141      * Displays the given context information for the given offset.
142      *
143      * @param info the context information
144      * @param position the offset
145      * @since 2.0
146      */

147     public void showContextInformation(final IContextInformation info, final int position) {
148         Control control= fViewer.getTextWidget();
149         BusyIndicator.showWhile(control.getDisplay(), new Runnable JavaDoc() {
150             public void run() {
151                 internalShowContextInfo(info, position);
152                 hideContextSelector();
153             }
154         });
155     }
156
157     /**
158      * Displays the given context information for the given offset.
159      *
160      * @param information the context information
161      * @param offset the offset
162      * @since 2.0
163      */

164
165     private void internalShowContextInfo(IContextInformation information, int offset) {
166
167         IContextInformationValidator validator= fContentAssistant.getContextInformationValidator(fViewer, offset);
168
169         if (validator != null) {
170             ContextFrame current= new ContextFrame();
171             current.fInformation= information;
172             current.fBeginOffset= (information instanceof IContextInformationExtension) ? ((IContextInformationExtension) information).getContextInformationPosition() : offset;
173             if (current.fBeginOffset == -1) current.fBeginOffset= offset;
174             current.fOffset= offset;
175             current.fVisibleOffset= fViewer.getTextWidget().getSelectionRange().x - (offset - current.fBeginOffset);
176             current.fValidator= validator;
177             current.fPresenter= fContentAssistant.getContextInformationPresenter(fViewer, offset);
178
179             fContextFrameStack.push(current);
180
181             internalShowContextFrame(current, fContextFrameStack.size() == 1);
182         }
183     }
184
185     /**
186      * Shows the given context frame.
187      *
188      * @param frame the frane to display
189      * @param initial <code>true</code> if this is the first frame to be displayed
190      * @since 2.0
191      */

192     private void internalShowContextFrame(ContextFrame frame, boolean initial) {
193
194         frame.fValidator.install(frame.fInformation, fViewer, frame.fOffset);
195
196         if (frame.fPresenter != null) {
197             if (fTextPresentation == null)
198                 fTextPresentation= new TextPresentation();
199             frame.fPresenter.install(frame.fInformation, fViewer, frame.fBeginOffset);
200             frame.fPresenter.updatePresentation(frame.fOffset, fTextPresentation);
201         }
202
203         createContextInfoPopup();
204
205         fContextInfoText.setText(frame.fInformation.getInformationDisplayString());
206         if (fTextPresentation != null)
207             TextPresentation.applyTextPresentation(fTextPresentation, fContextInfoText);
208         resize();
209
210         if (initial) {
211             if (fContentAssistant.addContentAssistListener(this, ContentAssistant2.CONTEXT_INFO_POPUP)) {
212                 fContentAssistant.addToLayout(this, fContextInfoPopup, ContentAssistant2.LayoutManager.LAYOUT_CONTEXT_INFO_POPUP, frame.fVisibleOffset);
213                 fContextInfoPopup.setVisible(true);
214             }
215         } else {
216             fContentAssistant.layout(ContentAssistant2.LayoutManager.LAYOUT_CONTEXT_INFO_POPUP, frame.fVisibleOffset);
217         }
218     }
219
220     /**
221      * Computes all possible context information for the given offset.
222      *
223      * @param position the offset
224      * @return all possible context information for the given offset
225      * @since 2.0
226      */

227     private IContextInformation[] computeContextInformation(int position) {
228         return fContentAssistant.computeContextInformation(fViewer, position);
229     }
230
231     /**
232      *Returns the error message generated while computing context information.
233      *
234      * @return the error message
235      */

236     private String JavaDoc getErrorMessage() {
237         return fContentAssistant.getErrorMessage();
238     }
239
240     /**
241      * Creates the context information popup. This is the tooltip like overlay window.
242      */

243     private void createContextInfoPopup() {
244         if (Helper2.okToUse(fContextInfoPopup))
245             return;
246
247         Control control= fViewer.getTextWidget();
248         Display display= control.getDisplay();
249
250         fContextInfoPopup= new Shell(control.getShell(), SWT.NO_TRIM | SWT.ON_TOP);
251         fContextInfoPopup.setBackground(display.getSystemColor(SWT.COLOR_BLACK));
252
253         fContextInfoText= new StyledText(fContextInfoPopup, SWT.MULTI | SWT.READ_ONLY);
254
255         Color c= fContentAssistant.getContextInformationPopupBackground();
256         if (c == null)
257             c= display.getSystemColor(SWT.COLOR_INFO_BACKGROUND);
258         fContextInfoText.setBackground(c);
259
260         c= fContentAssistant.getContextInformationPopupForeground();
261         if (c == null)
262             c= display.getSystemColor(SWT.COLOR_INFO_FOREGROUND);
263         fContextInfoText.setForeground(c);
264     }
265
266     /**
267      * Resizes the context information popup.
268      * @since 2.0
269      */

270     private void resize() {
271         Point size= fContextInfoText.computeSize(SWT.DEFAULT, SWT.DEFAULT, true);
272         size.x += 3;
273         fContextInfoText.setSize(size);
274         fContextInfoText.setLocation(1,1);
275         size.x += 2;
276         size.y += 2;
277         fContextInfoPopup.setSize(size);
278     }
279
280     /**
281      *Hides the context information popup.
282      */

283     private void hideContextInfoPopup() {
284
285         if (Helper2.okToUse(fContextInfoPopup)) {
286
287             int size= fContextFrameStack.size();
288             if (size > 0) {
289                 fContextFrameStack.pop();
290                 -- size;
291             }
292
293             if (size > 0) {
294                 ContextFrame current= (ContextFrame) fContextFrameStack.peek();
295                 internalShowContextFrame(current, false);
296             } else {
297
298                 fContentAssistant.removeContentAssistListener(this, ContentAssistant2.CONTEXT_INFO_POPUP);
299
300                 fContextInfoPopup.setVisible(false);
301                 fContextInfoPopup.dispose();
302                 fContextInfoPopup= null;
303
304                 if (fTextPresentation != null) {
305                     fTextPresentation.clear();
306                     fTextPresentation= null;
307                 }
308             }
309         }
310
311         if (fContextInfoPopup == null)
312             fContentAssistant.contextInformationClosed();
313     }
314
315     /**
316      * Creates the context selector in case the user has the choice between multiple valid contexts
317      * at a given offset.
318      */

319     private void createContextSelector() {
320         if (Helper2.okToUse(fContextSelectorShell))
321             return;
322
323         Control control= fViewer.getTextWidget();
324         fContextSelectorShell= new Shell(control.getShell(), SWT.NO_TRIM | SWT.ON_TOP);
325         GridLayout layout= new GridLayout();
326         layout.marginWidth= 0;
327         layout.marginHeight= 0;
328         fContextSelectorShell.setLayout(layout);
329         fContextSelectorShell.setBackground(control.getDisplay().getSystemColor(SWT.COLOR_BLACK));
330
331
332         fContextSelectorTable= new Table(fContextSelectorShell, SWT.H_SCROLL | SWT.V_SCROLL);
333         fContextSelectorTable.setLocation(1, 1);
334         GridData gd= new GridData(GridData.FILL_BOTH);
335         gd.heightHint= fContextSelectorTable.getItemHeight() * 10;
336         gd.widthHint= 300;
337         fContextSelectorTable.setLayoutData(gd);
338
339         fContextSelectorShell.pack(true);
340
341         Color c= fContentAssistant.getContextSelectorBackground();
342         if (c == null)
343             c= control.getDisplay().getSystemColor(SWT.COLOR_INFO_BACKGROUND);
344         fContextSelectorTable.setBackground(c);
345
346         c= fContentAssistant.getContextSelectorForeground();
347         if (c == null)
348             c= control.getDisplay().getSystemColor(SWT.COLOR_INFO_FOREGROUND);
349         fContextSelectorTable.setForeground(c);
350
351         fContextSelectorTable.addSelectionListener(new SelectionListener() {
352             public void widgetSelected(SelectionEvent e) {
353             }
354
355             public void widgetDefaultSelected(SelectionEvent e) {
356                 insertSelectedContext();
357                 hideContextSelector();
358             }
359         });
360
361         fPopupCloser.install(fContentAssistant, fContextSelectorTable);
362
363         fContextSelectorTable.setHeaderVisible(false);
364         fContentAssistant.addToLayout(this, fContextSelectorShell, ContentAssistant2.LayoutManager.LAYOUT_CONTEXT_SELECTOR, fContentAssistant.getSelectionOffset());
365     }
366
367     /**
368      * Causes the context information of the context selected in the context selector
369      * to be displayed in the context information popup.
370      */

371     private void insertSelectedContext() {
372         int i= fContextSelectorTable.getSelectionIndex();
373
374         if (i < 0 || i >= fContextSelectorInput.length)
375             return;
376
377         int position= fViewer.getSelectedRange().x;
378         internalShowContextInfo(fContextSelectorInput[i], position);
379     }
380
381     /**
382      * Sets the contexts in the context selector to the given set.
383      *
384      * @param contexts the possible contexts
385      */

386     private void setContexts(IContextInformation[] contexts) {
387         if (Helper2.okToUse(fContextSelectorTable)) {
388
389             fContextSelectorInput= contexts;
390
391             fContextSelectorTable.setRedraw(false);
392             fContextSelectorTable.removeAll();
393
394             TableItem item;
395             IContextInformation t;
396             for (int i= 0; i < contexts.length; i++) {
397                 t= contexts[i];
398                 item= new TableItem(fContextSelectorTable, SWT.NULL);
399                 if (t.getImage() != null)
400                     item.setImage(t.getImage());
401                 item.setText(t.getContextDisplayString());
402             }
403
404             fContextSelectorTable.select(0);
405             fContextSelectorTable.setRedraw(true);
406         }
407     }
408
409     /**
410      * Displays the context selector.
411      */

412     private void displayContextSelector() {
413         if (fContentAssistant.addContentAssistListener(this, ContentAssistant2.CONTEXT_SELECTOR))
414             fContextSelectorShell.setVisible(true);
415     }
416
417     /**
418      * Hodes the context selector.
419      */

420     private void hideContextSelector() {
421         if (Helper2.okToUse(fContextSelectorShell)) {
422             fContentAssistant.removeContentAssistListener(this, ContentAssistant2.CONTEXT_SELECTOR);
423
424             fPopupCloser.uninstall();
425             fContextSelectorShell.setVisible(false);
426             fContextSelectorShell.dispose();
427             fContextSelectorShell= null;
428         }
429
430         if (!Helper2.okToUse(fContextInfoPopup))
431             fContentAssistant.contextInformationClosed();
432     }
433
434     /**
435      *Returns whether the context selector has the focus.
436      *
437      * @return <code>true</code> if teh context selector has the focus
438      */

439     public boolean hasFocus() {
440         if (Helper2.okToUse(fContextSelectorShell))
441             return (fContextSelectorShell.isFocusControl() || fContextSelectorTable.isFocusControl());
442
443         return false;
444     }
445
446     /**
447      * Hides context selector and context information popup.
448      */

449     public void hide() {
450         hideContextSelector();
451         hideContextInfoPopup();
452     }
453
454     /**
455      * Returns whether this context information popup is active. I.e., either
456      * a context selector or context information is displayed.
457      *
458      * @return <code>true</code> if the context selector is active
459      */

460     public boolean isActive() {
461         return (Helper2.okToUse(fContextInfoPopup) || Helper2.okToUse(fContextSelectorShell));
462     }
463
464     /*
465      * @see IContentAssistListener#verifyKey(VerifyEvent)
466      */

467     public boolean verifyKey(VerifyEvent e) {
468         if (Helper2.okToUse(fContextSelectorShell))
469             return contextSelectorKeyPressed(e);
470         if (Helper2.okToUse(fContextInfoPopup))
471             return contextInfoPopupKeyPressed(e);
472         return true;
473     }
474
475     /**
476      * Processes a key stroke in the context selector.
477      *
478      * @param e the verify event describing the key stroke
479      * @return <code>true</code> if processing can be stopped
480      */

481     private boolean contextSelectorKeyPressed(VerifyEvent e) {
482
483         char key= e.character;
484         if (key == 0) {
485
486             int change;
487             int visibleRows= (fContextSelectorTable.getSize().y / fContextSelectorTable.getItemHeight()) - 1;
488             int selection= fContextSelectorTable.getSelectionIndex();
489
490             switch (e.keyCode) {
491
492                 case SWT.ARROW_UP:
493                     change= (fContextSelectorTable.getSelectionIndex() > 0 ? -1 : 0);
494                     break;
495
496                 case SWT.ARROW_DOWN:
497                     change= (fContextSelectorTable.getSelectionIndex() < fContextSelectorTable.getItemCount() - 1 ? 1 : 0);
498                     break;
499
500                 case SWT.PAGE_DOWN :
501                     change= visibleRows;
502                     if (selection + change >= fContextSelectorTable.getItemCount())
503                         change= fContextSelectorTable.getItemCount() - selection;
504                     break;
505
506                 case SWT.PAGE_UP :
507                     change= -visibleRows;
508                     if (selection + change < 0)
509                         change= -selection;
510                     break;
511
512                 case SWT.HOME :
513                     change= -selection;
514                     break;
515
516                 case SWT.END :
517                     change= fContextSelectorTable.getItemCount() - selection;
518                     break;
519
520                 default:
521                     if (e.keyCode != SWT.MOD1 && e.keyCode != SWT.MOD2 && e.keyCode != SWT.MOD3 && e.keyCode != SWT.MOD4)
522                         hideContextSelector();
523                     return true;
524             }
525
526             fContextSelectorTable.setSelection(selection + change);
527             fContextSelectorTable.showSelection();
528             e.doit= false;
529             return false;
530
531         } else if ('\t' == key) {
532             // switch focus to selector shell
533
e.doit= false;
534             fContextSelectorShell.setFocus();
535             return false;
536         } else if (key == 0x1B) {
537             // terminate on Esc
538
hideContextSelector();
539         }
540
541         return true;
542     }
543
544     /**
545      * Processes a key stroke while the info popup is up.
546      *
547      * @param e the verify event describing the key stroke
548      * @return <code>true</code> if processing can be stopped
549      */

550     private boolean contextInfoPopupKeyPressed(KeyEvent e) {
551
552         char key= e.character;
553         if (key == 0) {
554
555             switch (e.keyCode) {
556
557                 case SWT.ARROW_LEFT:
558                 case SWT.ARROW_RIGHT:
559                     validateContextInformation();
560                     break;
561                 default:
562                     if (e.keyCode != SWT.MOD1 && e.keyCode != SWT.MOD2 && e.keyCode != SWT.MOD3 && e.keyCode != SWT.MOD4)
563                         hideContextInfoPopup();
564                     break;
565             }
566
567         } else if (key == 0x1B) {
568             // terminate on Esc
569
hideContextInfoPopup();
570         } else {
571             validateContextInformation();
572         }
573         return true;
574     }
575
576     /*
577      * @see IEventConsumer#processEvent(VerifyEvent)
578      */

579     public void processEvent(VerifyEvent event) {
580         if (Helper2.okToUse(fContextSelectorShell))
581             contextSelectorProcessEvent(event);
582         if (Helper2.okToUse(fContextInfoPopup))
583             contextInfoPopupProcessEvent(event);
584     }
585
586     /**
587      * Processes a key stroke in the context selector.
588      *
589      * @param e the verify event describing the key stroke
590      */

591     private void contextSelectorProcessEvent(VerifyEvent e) {
592
593         if (e.start == e.end && e.text != null && e.text.equals(fLineDelimiter)) {
594             e.doit= false;
595             insertSelectedContext();
596         }
597
598         hideContextSelector();
599     }
600
601     /**
602      * Processes a key stroke while the info popup is up.
603      *
604      * @param e the verify event describing the key stroke
605      */

606     private void contextInfoPopupProcessEvent(VerifyEvent e) {
607         if (e.start != e.end && (e.text == null || e.text.length() == 0))
608             validateContextInformation();
609     }
610
611     /**
612      * Validates the context information for the viewer's actual cursor position.
613      */

614     private void validateContextInformation() {
615         /*
616          * Post the code in the event queue in order to ensure that the
617          * action described by this verify key event has already beed executed.
618          * Otherwise, we'd validate the context information based on the
619          * pre-key-stroke state.
620          */

621         fContextInfoPopup.getDisplay().asyncExec(new Runnable JavaDoc() {
622
623             private ContextFrame fFrame= (ContextFrame) fContextFrameStack.peek();
624
625             public void run() {
626                 if (Helper2.okToUse(fContextInfoPopup) && fFrame == fContextFrameStack.peek()) {
627                     int offset= fViewer.getSelectedRange().x;
628                     if (fFrame.fValidator == null || !fFrame.fValidator.isContextInformationValid(offset)) {
629                         hideContextInfoPopup();
630                     } else if (fFrame.fPresenter != null && fFrame.fPresenter.updatePresentation(offset, fTextPresentation)) {
631                         TextPresentation.applyTextPresentation(fTextPresentation, fContextInfoText);
632                         resize();
633                     }
634                 }
635             }
636         });
637     }
638 }
639
Popular Tags