comparison org.eclipse.jface.text/src/org/eclipse/jface/internal/text/link/contentassist/ContextInformationPopup2.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
14
15 module org.eclipse.jface.internal.text.link.contentassist.ContextInformationPopup2;
16
17 import org.eclipse.jface.internal.text.link.contentassist.IProposalListener; // packageimport
18 import org.eclipse.jface.internal.text.link.contentassist.LineBreakingReader; // packageimport
19 import org.eclipse.jface.internal.text.link.contentassist.CompletionProposalPopup2; // packageimport
20 import org.eclipse.jface.internal.text.link.contentassist.ContentAssistMessages; // packageimport
21 import org.eclipse.jface.internal.text.link.contentassist.Helper2; // packageimport
22 import org.eclipse.jface.internal.text.link.contentassist.PopupCloser2; // packageimport
23 import org.eclipse.jface.internal.text.link.contentassist.IContentAssistListener2; // packageimport
24 import org.eclipse.jface.internal.text.link.contentassist.ContentAssistant2; // packageimport
25 import org.eclipse.jface.internal.text.link.contentassist.AdditionalInfoController2; // packageimport
26
27 import java.lang.all;
28 import java.util.Stack;
29 import java.util.Set;
30
31
32
33 import org.eclipse.swt.SWT;
34 import org.eclipse.swt.custom.BusyIndicator;
35 import org.eclipse.swt.custom.StyledText;
36 import org.eclipse.swt.events.KeyEvent;
37 import org.eclipse.swt.events.SelectionEvent;
38 import org.eclipse.swt.events.SelectionListener;
39 import org.eclipse.swt.events.VerifyEvent;
40 import org.eclipse.swt.graphics.Color;
41 import org.eclipse.swt.graphics.Point;
42 import org.eclipse.swt.layout.GridData;
43 import org.eclipse.swt.layout.GridLayout;
44 import org.eclipse.swt.widgets.Control;
45 import org.eclipse.swt.widgets.Display;
46 import org.eclipse.swt.widgets.Shell;
47 import org.eclipse.swt.widgets.Table;
48 import org.eclipse.swt.widgets.TableItem;
49 import org.eclipse.jface.text.ITextViewer;
50 import org.eclipse.jface.text.TextPresentation;
51 import org.eclipse.jface.text.contentassist.IContextInformation;
52 import org.eclipse.jface.text.contentassist.IContextInformationExtension;
53 import org.eclipse.jface.text.contentassist.IContextInformationPresenter;
54 import org.eclipse.jface.text.contentassist.IContextInformationValidator;
55
56
57 /**
58 * This class is used to present context information to the user.
59 * If multiple contexts are valid at the current cursor location,
60 * a list is presented from which the user may choose one context.
61 * Once the user makes their choice, or if there was only a single
62 * possible context, the context information is shown in a tooltip like popup. <p>
63 * If the tooltip is visible and the user wants to see context information of
64 * a context embedded into the one for which context information is displayed,
65 * context information for the embedded context is shown. As soon as the
66 * cursor leaves the embedded context area, the context information for
67 * the embedding context is shown again.
68 *
69 * @see IContextInformation
70 * @see IContextInformationValidator
71 */
72 class ContextInformationPopup2 : IContentAssistListener2 {
73
74
75
76 /**
77 * Represents the state necessary for embedding contexts.
78 * @since 2.0
79 */
80 static class ContextFrame {
81 public int fBeginOffset;
82 public int fOffset;
83 public int fVisibleOffset;
84 public IContextInformation fInformation;
85 public IContextInformationValidator fValidator;
86 public IContextInformationPresenter fPresenter;
87 }
88
89 private ITextViewer fViewer;
90 private ContentAssistant2 fContentAssistant;
91
92 private PopupCloser2 fPopupCloser;
93 private Shell fContextSelectorShell;
94 private Table fContextSelectorTable;
95 private IContextInformation[] fContextSelectorInput;
96 private String fLineDelimiter= null;
97
98 private Shell fContextInfoPopup;
99 private StyledText fContextInfoText;
100 private TextPresentation fTextPresentation;
101
102 private Stack fContextFrameStack;
103
104
105 /**
106 * Creates a new context information popup.
107 *
108 * @param contentAssistant the content assist for computing the context information
109 * @param viewer the viewer on top of which the context information is shown
110 */
111 public this(ContentAssistant2 contentAssistant, ITextViewer viewer) {
112 fPopupCloser= new PopupCloser2();
113 fContextFrameStack= new Stack();
114 fContentAssistant= contentAssistant;
115 fViewer= viewer;
116 }
117
118 /**
119 * Shows all possible contexts for the given cursor position of the viewer.
120 *
121 * @param autoActivated <code>true</code> if auto activated
122 * @return a potential error message or <code>null</code> in case of no error
123 */
124 public String showContextProposals(bool autoActivated) {
125 final StyledText styledText= fViewer.getTextWidget();
126 BusyIndicator.showWhile(styledText.getDisplay(), dgRunnable( (bool autoActivated_, StyledText styledText_ ) {
127 int position= fViewer.getSelectedRange().x;
128
129 IContextInformation[] contexts= computeContextInformation(position);
130 int count = (contexts is null ? 0 : contexts.length);
131 if (count is 1) {
132
133 // Show context information directly
134 internalShowContextInfo(contexts[0], position);
135
136 } else if (count > 0) {
137 // Precise context must be selected
138
139 if (fLineDelimiter is null)
140 fLineDelimiter= styledText_.getLineDelimiter();
141
142 createContextSelector();
143 setContexts(contexts);
144 displayContextSelector();
145 hideContextInfoPopup();
146
147 } else if (!autoActivated_) {
148 styledText_.getDisplay().beep();
149 }
150 }, autoActivated, styledText ));
151
152 return getErrorMessage();
153 }
154
155 /**
156 * Displays the given context information for the given offset.
157 *
158 * @param info the context information
159 * @param position the offset
160 * @since 2.0
161 */
162 public void showContextInformation(IContextInformation info, int position) {
163 Control control= fViewer.getTextWidget();
164 BusyIndicator.showWhile(control.getDisplay(), dgRunnable( (IContextInformation info_, int position_) {
165 internalShowContextInfo(info_, position_);
166 hideContextSelector();
167 }, info, position));
168 }
169
170 /**
171 * Displays the given context information for the given offset.
172 *
173 * @param information the context information
174 * @param offset the offset
175 * @since 2.0
176 */
177
178 private void internalShowContextInfo(IContextInformation information, int offset) {
179
180 IContextInformationValidator validator= fContentAssistant.getContextInformationValidator(fViewer, offset);
181
182 if (validator !is null) {
183 ContextFrame current= new ContextFrame();
184 current.fInformation= information;
185 current.fBeginOffset= ( cast(IContextInformationExtension)information ) ? (cast(IContextInformationExtension) information).getContextInformationPosition() : offset;
186 if (current.fBeginOffset is -1) current.fBeginOffset= offset;
187 current.fOffset= offset;
188 current.fVisibleOffset= fViewer.getTextWidget().getSelectionRange().x - (offset - current.fBeginOffset);
189 current.fValidator= validator;
190 current.fPresenter= fContentAssistant.getContextInformationPresenter(fViewer, offset);
191
192 fContextFrameStack.push(current);
193
194 internalShowContextFrame(current, fContextFrameStack.size() is 1);
195 }
196 }
197
198 /**
199 * Shows the given context frame.
200 *
201 * @param frame the frane to display
202 * @param initial <code>true</code> if this is the first frame to be displayed
203 * @since 2.0
204 */
205 private void internalShowContextFrame(ContextFrame frame, bool initial) {
206
207 frame.fValidator.install(frame.fInformation, fViewer, frame.fOffset);
208
209 if (frame.fPresenter !is null) {
210 if (fTextPresentation is null)
211 fTextPresentation= new TextPresentation();
212 frame.fPresenter.install(frame.fInformation, fViewer, frame.fBeginOffset);
213 frame.fPresenter.updatePresentation(frame.fOffset, fTextPresentation);
214 }
215
216 createContextInfoPopup();
217
218 fContextInfoText.setText(frame.fInformation.getInformationDisplayString());
219 if (fTextPresentation !is null)
220 TextPresentation.applyTextPresentation(fTextPresentation, fContextInfoText);
221 resize();
222
223 if (initial) {
224 if (fContentAssistant.addContentAssistListener(this, ContentAssistant2.CONTEXT_INFO_POPUP)) {
225 fContentAssistant.addToLayout(this, fContextInfoPopup, ContentAssistant2.LayoutManager.LAYOUT_CONTEXT_INFO_POPUP, frame.fVisibleOffset);
226 fContextInfoPopup.setVisible(true);
227 }
228 } else {
229 fContentAssistant.layout(ContentAssistant2.LayoutManager.LAYOUT_CONTEXT_INFO_POPUP, frame.fVisibleOffset);
230 }
231 }
232
233 /**
234 * Computes all possible context information for the given offset.
235 *
236 * @param position the offset
237 * @return all possible context information for the given offset
238 * @since 2.0
239 */
240 private IContextInformation[] computeContextInformation(int position) {
241 return fContentAssistant.computeContextInformation(fViewer, position);
242 }
243
244 /**
245 *Returns the error message generated while computing context information.
246 *
247 * @return the error message
248 */
249 private String getErrorMessage() {
250 return fContentAssistant.getErrorMessage();
251 }
252
253 /**
254 * Creates the context information popup. This is the tooltip like overlay window.
255 */
256 private void createContextInfoPopup() {
257 if (Helper2.okToUse(fContextInfoPopup))
258 return;
259
260 Control control= fViewer.getTextWidget();
261 Display display= control.getDisplay();
262
263 fContextInfoPopup= new Shell(control.getShell(), SWT.NO_TRIM | SWT.ON_TOP);
264 fContextInfoPopup.setBackground(display.getSystemColor(SWT.COLOR_BLACK));
265
266 fContextInfoText= new StyledText(fContextInfoPopup, SWT.MULTI | SWT.READ_ONLY);
267
268 Color c= fContentAssistant.getContextInformationPopupBackground();
269 if (c is null)
270 c= display.getSystemColor(SWT.COLOR_INFO_BACKGROUND);
271 fContextInfoText.setBackground(c);
272
273 c= fContentAssistant.getContextInformationPopupForeground();
274 if (c is null)
275 c= display.getSystemColor(SWT.COLOR_INFO_FOREGROUND);
276 fContextInfoText.setForeground(c);
277 }
278
279 /**
280 * Resizes the context information popup.
281 * @since 2.0
282 */
283 private void resize() {
284 Point size= fContextInfoText.computeSize(SWT.DEFAULT, SWT.DEFAULT, true);
285 size.x += 3;
286 fContextInfoText.setSize(size);
287 fContextInfoText.setLocation(1,1);
288 size.x += 2;
289 size.y += 2;
290 fContextInfoPopup.setSize(size);
291 }
292
293 /**
294 *Hides the context information popup.
295 */
296 private void hideContextInfoPopup() {
297
298 if (Helper2.okToUse(fContextInfoPopup)) {
299
300 int size= fContextFrameStack.size();
301 if (size > 0) {
302 fContextFrameStack.pop();
303 -- size;
304 }
305
306 if (size > 0) {
307 ContextFrame current= cast(ContextFrame) fContextFrameStack.peek();
308 internalShowContextFrame(current, false);
309 } else {
310
311 fContentAssistant.removeContentAssistListener(this, ContentAssistant2.CONTEXT_INFO_POPUP);
312
313 fContextInfoPopup.setVisible(false);
314 fContextInfoPopup.dispose();
315 fContextInfoPopup= null;
316
317 if (fTextPresentation !is null) {
318 fTextPresentation.clear();
319 fTextPresentation= null;
320 }
321 }
322 }
323
324 if (fContextInfoPopup is null)
325 fContentAssistant.contextInformationClosed_package();
326 }
327
328 /**
329 * Creates the context selector in case the user has the choice between multiple valid contexts
330 * at a given offset.
331 */
332 private void createContextSelector() {
333 if (Helper2.okToUse(fContextSelectorShell))
334 return;
335
336 Control control= fViewer.getTextWidget();
337 fContextSelectorShell= new Shell(control.getShell(), SWT.NO_TRIM | SWT.ON_TOP);
338 GridLayout layout= new GridLayout();
339 layout.marginWidth= 0;
340 layout.marginHeight= 0;
341 fContextSelectorShell.setLayout(layout);
342 fContextSelectorShell.setBackground(control.getDisplay().getSystemColor(SWT.COLOR_BLACK));
343
344
345 fContextSelectorTable= new Table(fContextSelectorShell, SWT.H_SCROLL | SWT.V_SCROLL);
346 fContextSelectorTable.setLocation(1, 1);
347 GridData gd= new GridData(GridData.FILL_BOTH);
348 gd.heightHint= fContextSelectorTable.getItemHeight() * 10;
349 gd.widthHint= 300;
350 fContextSelectorTable.setLayoutData(gd);
351
352 fContextSelectorShell.pack(true);
353
354 Color c= fContentAssistant.getContextSelectorBackground();
355 if (c is null)
356 c= control.getDisplay().getSystemColor(SWT.COLOR_INFO_BACKGROUND);
357 fContextSelectorTable.setBackground(c);
358
359 c= fContentAssistant.getContextSelectorForeground();
360 if (c is null)
361 c= control.getDisplay().getSystemColor(SWT.COLOR_INFO_FOREGROUND);
362 fContextSelectorTable.setForeground(c);
363
364 fContextSelectorTable.addSelectionListener(new class() SelectionListener {
365 public void widgetSelected(SelectionEvent e) {
366 }
367
368 public void widgetDefaultSelected(SelectionEvent e) {
369 insertSelectedContext();
370 hideContextSelector();
371 }
372 });
373
374 fPopupCloser.install(fContentAssistant, fContextSelectorTable);
375
376 fContextSelectorTable.setHeaderVisible(false);
377 fContentAssistant.addToLayout(this, fContextSelectorShell, ContentAssistant2.LayoutManager.LAYOUT_CONTEXT_SELECTOR, fContentAssistant.getSelectionOffset());
378 }
379
380 /**
381 * Causes the context information of the context selected in the context selector
382 * to be displayed in the context information popup.
383 */
384 private void insertSelectedContext() {
385 int i= fContextSelectorTable.getSelectionIndex();
386
387 if (i < 0 || i >= fContextSelectorInput.length)
388 return;
389
390 int position= fViewer.getSelectedRange().x;
391 internalShowContextInfo(fContextSelectorInput[i], position);
392 }
393
394 /**
395 * Sets the contexts in the context selector to the given set.
396 *
397 * @param contexts the possible contexts
398 */
399 private void setContexts(IContextInformation[] contexts) {
400 if (Helper2.okToUse(fContextSelectorTable)) {
401
402 fContextSelectorInput= contexts;
403
404 fContextSelectorTable.setRedraw(false);
405 fContextSelectorTable.removeAll();
406
407 TableItem item;
408 IContextInformation t;
409 for (int i= 0; i < contexts.length; i++) {
410 t= contexts[i];
411 item= new TableItem(fContextSelectorTable, SWT.NULL);
412 if (t.getImage() !is null)
413 item.setImage(t.getImage());
414 item.setText(t.getContextDisplayString());
415 }
416
417 fContextSelectorTable.select(0);
418 fContextSelectorTable.setRedraw(true);
419 }
420 }
421
422 /**
423 * Displays the context selector.
424 */
425 private void displayContextSelector() {
426 if (fContentAssistant.addContentAssistListener(this, ContentAssistant2.CONTEXT_SELECTOR))
427 fContextSelectorShell.setVisible(true);
428 }
429
430 /**
431 * Hodes the context selector.
432 */
433 private void hideContextSelector() {
434 if (Helper2.okToUse(fContextSelectorShell)) {
435 fContentAssistant.removeContentAssistListener(this, ContentAssistant2.CONTEXT_SELECTOR);
436
437 fPopupCloser.uninstall();
438 fContextSelectorShell.setVisible(false);
439 fContextSelectorShell.dispose();
440 fContextSelectorShell= null;
441 }
442
443 if (!Helper2.okToUse(fContextInfoPopup))
444 fContentAssistant.contextInformationClosed_package();
445 }
446
447 /**
448 *Returns whether the context selector has the focus.
449 *
450 * @return <code>true</code> if teh context selector has the focus
451 */
452 public bool hasFocus() {
453 if (Helper2.okToUse(fContextSelectorShell))
454 return (fContextSelectorShell.isFocusControl() || fContextSelectorTable.isFocusControl());
455
456 return false;
457 }
458
459 /**
460 * Hides context selector and context information popup.
461 */
462 public void hide() {
463 hideContextSelector();
464 hideContextInfoPopup();
465 }
466
467 /**
468 * Returns whether this context information popup is active. I.e., either
469 * a context selector or context information is displayed.
470 *
471 * @return <code>true</code> if the context selector is active
472 */
473 public bool isActive() {
474 return (Helper2.okToUse(fContextInfoPopup) || Helper2.okToUse(fContextSelectorShell));
475 }
476
477 /*
478 * @see IContentAssistListener#verifyKey(VerifyEvent)
479 */
480 public bool verifyKey(VerifyEvent e) {
481 if (Helper2.okToUse(fContextSelectorShell))
482 return contextSelectorKeyPressed(e);
483 if (Helper2.okToUse(fContextInfoPopup))
484 return contextInfoPopupKeyPressed(e);
485 return true;
486 }
487
488 /**
489 * Processes a key stroke in the context selector.
490 *
491 * @param e the verify event describing the key stroke
492 * @return <code>true</code> if processing can be stopped
493 */
494 private bool contextSelectorKeyPressed(VerifyEvent e) {
495
496 char key= e.character;
497 if (key is 0) {
498
499 int change;
500 int visibleRows= (fContextSelectorTable.getSize().y / fContextSelectorTable.getItemHeight()) - 1;
501 int selection= fContextSelectorTable.getSelectionIndex();
502
503 switch (e.keyCode) {
504
505 case SWT.ARROW_UP:
506 change= (fContextSelectorTable.getSelectionIndex() > 0 ? -1 : 0);
507 break;
508
509 case SWT.ARROW_DOWN:
510 change= (fContextSelectorTable.getSelectionIndex() < fContextSelectorTable.getItemCount() - 1 ? 1 : 0);
511 break;
512
513 case SWT.PAGE_DOWN :
514 change= visibleRows;
515 if (selection + change >= fContextSelectorTable.getItemCount())
516 change= fContextSelectorTable.getItemCount() - selection;
517 break;
518
519 case SWT.PAGE_UP :
520 change= -visibleRows;
521 if (selection + change < 0)
522 change= -selection;
523 break;
524
525 case SWT.HOME :
526 change= -selection;
527 break;
528
529 case SWT.END :
530 change= fContextSelectorTable.getItemCount() - selection;
531 break;
532
533 default:
534 if (e.keyCode !is SWT.MOD1 && e.keyCode !is SWT.MOD2 && e.keyCode !is SWT.MOD3 && e.keyCode !is SWT.MOD4)
535 hideContextSelector();
536 return true;
537 }
538
539 fContextSelectorTable.setSelection(selection + change);
540 fContextSelectorTable.showSelection();
541 e.doit= false;
542 return false;
543
544 } else if ('\t' is key) {
545 // switch focus to selector shell
546 e.doit= false;
547 fContextSelectorShell.setFocus();
548 return false;
549 } else if (key is SWT.ESC) {
550 e.doit= false;
551 hideContextSelector();
552 }
553
554 return true;
555 }
556
557 /**
558 * Processes a key stroke while the info popup is up.
559 *
560 * @param e the verify event describing the key stroke
561 * @return <code>true</code> if processing can be stopped
562 */
563 private bool contextInfoPopupKeyPressed(KeyEvent e) {
564
565 char key= e.character;
566 if (key is 0) {
567
568 switch (e.keyCode) {
569 case SWT.ARROW_LEFT:
570 case SWT.ARROW_RIGHT:
571 validateContextInformation();
572 break;
573 default:
574 if (e.keyCode !is SWT.MOD1 && e.keyCode !is SWT.MOD2 && e.keyCode !is SWT.MOD3 && e.keyCode !is SWT.MOD4)
575 hideContextInfoPopup();
576 break;
577 }
578
579 } else if (key is SWT.ESC) {
580 e.doit= false;
581 hideContextInfoPopup();
582 } else {
583 validateContextInformation();
584 }
585 return true;
586 }
587
588 /*
589 * @see IEventConsumer#processEvent(VerifyEvent)
590 */
591 public void processEvent(VerifyEvent event) {
592 if (Helper2.okToUse(fContextSelectorShell))
593 contextSelectorProcessEvent(event);
594 if (Helper2.okToUse(fContextInfoPopup))
595 contextInfoPopupProcessEvent(event);
596 }
597
598 /**
599 * Processes a key stroke in the context selector.
600 *
601 * @param e the verify event describing the key stroke
602 */
603 private void contextSelectorProcessEvent(VerifyEvent e) {
604
605 if (e.start is e.end && e.text !is null && e.text.equals(fLineDelimiter)) {
606 e.doit= false;
607 insertSelectedContext();
608 }
609
610 hideContextSelector();
611 }
612
613 /**
614 * Processes a key stroke while the info popup is up.
615 *
616 * @param e the verify event describing the key stroke
617 */
618 private void contextInfoPopupProcessEvent(VerifyEvent e) {
619 if (e.start !is e.end && (e.text is null || e.text.length() is 0))
620 validateContextInformation();
621 }
622
623 /**
624 * Validates the context information for the viewer's actual cursor position.
625 */
626 private void validateContextInformation() {
627 /*
628 * Post the code in the event queue in order to ensure that the
629 * action described by this verify key event has already beed executed.
630 * Otherwise, we'd validate the context information based on the
631 * pre-key-stroke state.
632 */
633 fContextInfoPopup.getDisplay().asyncExec(dgRunnable( (ContextFrame fFrame_) {
634
635 ContextFrame fFrame= fFrame_;
636
637 if (Helper2.okToUse(fContextInfoPopup) && fFrame is fContextFrameStack.peek()) {
638 int offset= fViewer.getSelectedRange().x;
639 if (fFrame.fValidator is null || !fFrame.fValidator.isContextInformationValid(offset)) {
640 hideContextInfoPopup();
641 } else if (fFrame.fPresenter !is null && fFrame.fPresenter.updatePresentation(offset, fTextPresentation)) {
642 TextPresentation.applyTextPresentation(fTextPresentation, fContextInfoText);
643 resize();
644 }
645 }
646 }, cast(ContextFrame) fContextFrameStack.peek()));
647 }
648 }