e37799c1922b2cc6f2748470d4e04d752e1cd68d
[phpeclipse.git] / net.sourceforge.phpeclipse.ui / src / net / sourceforge / phpdt / internal / ui / text / JavaOutlineInformationControl.java
1 /*******************************************************************************
2  * Copyright (c) 2000, 2003 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials 
4  * are made available under the terms of the Common Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/cpl-v10.html
7  * 
8  * Contributors:
9  *     IBM Corporation - initial API and implementation
10  *******************************************************************************/
11 package net.sourceforge.phpdt.internal.ui.text;
12
13 import net.sourceforge.phpdt.core.ICompilationUnit;
14 import net.sourceforge.phpdt.core.IJavaElement;
15 import net.sourceforge.phpdt.core.IParent;
16 import net.sourceforge.phpdt.core.JavaModelException;
17 import net.sourceforge.phpdt.internal.ui.actions.OpenActionUtil;
18 import net.sourceforge.phpdt.internal.ui.util.StringMatcher;
19 import net.sourceforge.phpdt.internal.ui.viewsupport.AppearanceAwareLabelProvider;
20 import net.sourceforge.phpdt.internal.ui.viewsupport.DecoratingJavaLabelProvider;
21 import net.sourceforge.phpdt.internal.ui.viewsupport.JavaElementLabels;
22 import net.sourceforge.phpdt.ui.JavaElementSorter;
23 import net.sourceforge.phpdt.ui.StandardJavaElementContentProvider;
24 import net.sourceforge.phpeclipse.ui.WebUI;
25 //import net.sourceforge.phpeclipse.PHPeclipsePlugin;
26
27 import org.eclipse.core.runtime.CoreException;
28 import org.eclipse.jface.text.IInformationControl;
29 import org.eclipse.jface.text.IInformationControlExtension;
30 import org.eclipse.jface.text.IInformationControlExtension2;
31 import org.eclipse.jface.viewers.AbstractTreeViewer;
32 import org.eclipse.jface.viewers.IBaseLabelProvider;
33 import org.eclipse.jface.viewers.ILabelProvider;
34 import org.eclipse.jface.viewers.IStructuredSelection;
35 import org.eclipse.jface.viewers.StructuredSelection;
36 import org.eclipse.jface.viewers.StructuredViewer;
37 import org.eclipse.jface.viewers.TreeViewer;
38 import org.eclipse.jface.viewers.Viewer;
39 import org.eclipse.jface.viewers.ViewerFilter;
40 import org.eclipse.swt.SWT;
41 import org.eclipse.swt.events.DisposeListener;
42 import org.eclipse.swt.events.FocusListener;
43 import org.eclipse.swt.events.KeyEvent;
44 import org.eclipse.swt.events.KeyListener;
45 import org.eclipse.swt.events.ModifyEvent;
46 import org.eclipse.swt.events.ModifyListener;
47 import org.eclipse.swt.events.SelectionEvent;
48 import org.eclipse.swt.events.SelectionListener;
49 import org.eclipse.swt.graphics.Color;
50 import org.eclipse.swt.graphics.FontMetrics;
51 import org.eclipse.swt.graphics.GC;
52 import org.eclipse.swt.graphics.Point;
53 import org.eclipse.swt.graphics.Rectangle;
54 import org.eclipse.swt.layout.GridData;
55 import org.eclipse.swt.layout.GridLayout;
56 import org.eclipse.swt.widgets.Composite;
57 import org.eclipse.swt.widgets.Control;
58 import org.eclipse.swt.widgets.Display;
59 import org.eclipse.swt.widgets.Label;
60 import org.eclipse.swt.widgets.Layout;
61 import org.eclipse.swt.widgets.Shell;
62 import org.eclipse.swt.widgets.Text;
63 import org.eclipse.swt.widgets.Tree;
64 import org.eclipse.swt.widgets.TreeItem;
65
66 /**
67  * @author dmegert
68  * 
69  * To change this generated comment edit the template variable "typecomment":
70  * Window>Preferences>Java>Templates. To enable and disable the creation of type
71  * comments go to Window>Preferences>Java>Code Generation.
72  */
73 public class JavaOutlineInformationControl implements IInformationControl,
74                 IInformationControlExtension, IInformationControlExtension2 {
75
76         /**
77          * The NamePatternFilter selects the elements which match the given string
78          * patterns.
79          * <p>
80          * The following characters have special meaning: ? => any character * =>
81          * any string
82          * </p>
83          * 
84          * @since 2.0
85          */
86         private static class NamePatternFilter extends ViewerFilter {
87                 private String fPattern;
88
89                 private StringMatcher fMatcher;
90
91                 private ILabelProvider fLabelProvider;
92
93                 private Viewer fViewer;
94
95                 private StringMatcher getMatcher() {
96                         return fMatcher;
97                 }
98
99                 /*
100                  * (non-Javadoc) Method declared on ViewerFilter.
101                  */
102                 public boolean select(Viewer viewer, Object parentElement,
103                                 Object element) {
104                         if (fMatcher == null)
105                                 return true;
106
107                         ILabelProvider labelProvider = getLabelProvider(viewer);
108
109                         String matchName = null;
110                         if (labelProvider != null)
111                                 matchName = ((ILabelProvider) labelProvider).getText(element);
112                         else if (element instanceof IJavaElement)
113                                 matchName = ((IJavaElement) element).getElementName();
114
115                         if (matchName != null && fMatcher.match(matchName))
116                                 return true;
117
118                         return hasUnfilteredChild(viewer, element);
119                 }
120
121                 private ILabelProvider getLabelProvider(Viewer viewer) {
122                         if (fViewer == viewer)
123                                 return fLabelProvider;
124
125                         fLabelProvider = null;
126                         IBaseLabelProvider baseLabelProvider = null;
127                         if (viewer instanceof StructuredViewer)
128                                 baseLabelProvider = ((StructuredViewer) viewer)
129                                                 .getLabelProvider();
130
131                         if (baseLabelProvider instanceof ILabelProvider)
132                                 fLabelProvider = (ILabelProvider) baseLabelProvider;
133
134                         return fLabelProvider;
135                 }
136
137                 private boolean hasUnfilteredChild(Viewer viewer, Object element) {
138                         IJavaElement[] children;
139                         if (element instanceof IParent) {
140                                 try {
141                                         children = ((IParent) element).getChildren();
142                                 } catch (JavaModelException ex) {
143                                         return false;
144                                 }
145                                 for (int i = 0; i < children.length; i++)
146                                         if (select(viewer, element, children[i]))
147                                                 return true;
148                         }
149                         return false;
150                 }
151
152                 /**
153                  * Sets the patterns to filter out for the receiver.
154                  * <p>
155                  * The following characters have special meaning: ? => any character * =>
156                  * any string
157                  * </p>
158                  */
159                 public void setPattern(String pattern) {
160                         fPattern = pattern;
161                         if (fPattern == null) {
162                                 fMatcher = null;
163                                 return;
164                         }
165                         boolean ignoreCase = pattern.toLowerCase().equals(pattern);
166                         fMatcher = new StringMatcher(pattern, ignoreCase, false);
167                 }
168         }
169
170         private static class BorderFillLayout extends Layout {
171
172                 /** The border widths. */
173                 final int fBorderSize;
174
175                 /**
176                  * Creates a fill layout with a border.
177                  */
178                 public BorderFillLayout(int borderSize) {
179                         if (borderSize < 0)
180                                 throw new IllegalArgumentException();
181                         fBorderSize = borderSize;
182                 }
183
184                 /**
185                  * Returns the border size.
186                  */
187                 public int getBorderSize() {
188                         return fBorderSize;
189                 }
190
191                 /*
192                  * @see org.eclipse.swt.widgets.Layout#computeSize(org.eclipse.swt.widgets.Composite,
193                  *      int, int, boolean)
194                  */
195                 protected Point computeSize(Composite composite, int wHint, int hHint,
196                                 boolean flushCache) {
197
198                         Control[] children = composite.getChildren();
199                         Point minSize = new Point(0, 0);
200
201                         if (children != null) {
202                                 for (int i = 0; i < children.length; i++) {
203                                         Point size = children[i].computeSize(wHint, hHint,
204                                                         flushCache);
205                                         minSize.x = Math.max(minSize.x, size.x);
206                                         minSize.y = Math.max(minSize.y, size.y);
207                                 }
208                         }
209
210                         minSize.x += fBorderSize * 2 + RIGHT_MARGIN;
211                         minSize.y += fBorderSize * 2;
212
213                         return minSize;
214                 }
215
216                 /*
217                  * @see org.eclipse.swt.widgets.Layout#layout(org.eclipse.swt.widgets.Composite,
218                  *      boolean)
219                  */
220                 protected void layout(Composite composite, boolean flushCache) {
221
222                         Control[] children = composite.getChildren();
223                         Point minSize = new Point(composite.getClientArea().width,
224                                         composite.getClientArea().height);
225
226                         if (children != null) {
227                                 for (int i = 0; i < children.length; i++) {
228                                         Control child = children[i];
229                                         child.setSize(minSize.x - fBorderSize * 2, minSize.y
230                                                         - fBorderSize * 2);
231                                         child.setLocation(fBorderSize, fBorderSize);
232                                 }
233                         }
234                 }
235         }
236
237         /** Border thickness in pixels. */
238         private static final int BORDER = 1;
239
240         /** Right margin in pixels. */
241         private static final int RIGHT_MARGIN = 3;
242
243         /** The control's shell */
244         private Shell fShell;
245
246         /** The composite */
247         Composite fComposite;
248
249         /** The control's text widget */
250         private Text fFilterText;
251
252         /** The control's tree widget */
253         private TreeViewer fTreeViewer;
254
255         /** The control width constraint */
256         private int fMaxWidth = -1;
257
258         /** The control height constraint */
259         private int fMaxHeight = -1;
260
261         private StringMatcher fStringMatcher;
262
263         /**
264          * Creates a tree information control with the given shell as parent. The
265          * given style is applied to the tree widget.
266          * 
267          * @param parent
268          *            the parent shell
269          * @param style
270          *            the additional styles for the tree widget
271          */
272         public JavaOutlineInformationControl(Shell parent, int style) {
273                 this(parent, SWT.RESIZE, style);
274         }
275
276         /**
277          * Creates a tree information control with the given shell as parent. No
278          * additional styles are applied.
279          * 
280          * @param parent
281          *            the parent shell
282          */
283 //      public JavaOutlineInformationControl(Shell parent) {
284 //              this(parent, SWT.NONE);
285 //      }
286
287         /**
288          * Creates a tree information control with the given shell as parent. The
289          * given styles are applied to the shell and the tree widget.
290          * 
291          * @param parent
292          *            the parent shell
293          * @param shellStyle
294          *            the additional styles for the shell
295          * @param treeStyle
296          *            the additional styles for the tree widget
297          */
298         public JavaOutlineInformationControl(Shell parent, int shellStyle,
299                         int treeStyle) {
300                 fShell = new Shell(parent, shellStyle);
301                 Display display = fShell.getDisplay();
302                 fShell.setBackground(display.getSystemColor(SWT.COLOR_BLACK));
303
304                 // Composite for filter text and tree
305                 fComposite = new Composite(fShell, SWT.RESIZE);
306                 GridLayout layout = new GridLayout(1, false);
307                 fComposite.setLayout(layout);
308                 fComposite.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
309
310                 createFilterText(fComposite);
311                 createTreeViewer(fComposite, treeStyle);
312
313                 int border = ((shellStyle & SWT.NO_TRIM) == 0) ? 0 : BORDER;
314                 fShell.setLayout(new BorderFillLayout(border));
315
316                 setInfoSystemColor();
317                 installFilter();
318         }
319
320         private void createTreeViewer(Composite parent, int style) {
321                 Tree tree = new Tree(parent, SWT.SINGLE | (style & ~SWT.MULTI));
322                 GridData data = new GridData(GridData.FILL_BOTH);
323                 tree.setLayoutData(data);
324
325                 fTreeViewer = new TreeViewer(tree);
326
327                 // Hide import declartions but show the container
328                 // fTreeViewer.addFilter(new ViewerFilter() {
329                 // public boolean select(Viewer viewer, Object parentElement, Object
330                 // element) {
331                 // return !(element instanceof IImportDeclaration);
332                 // }
333                 // });
334
335                 fTreeViewer.setContentProvider(new StandardJavaElementContentProvider(
336                                 true, true));
337                 fTreeViewer.setSorter(new JavaElementSorter());
338                 fTreeViewer.setAutoExpandLevel(AbstractTreeViewer.ALL_LEVELS);
339
340                 AppearanceAwareLabelProvider lprovider = new AppearanceAwareLabelProvider(
341                                 AppearanceAwareLabelProvider.DEFAULT_TEXTFLAGS
342                                                 | JavaElementLabels.F_APP_TYPE_SIGNATURE,
343                                 AppearanceAwareLabelProvider.DEFAULT_IMAGEFLAGS);
344                 fTreeViewer
345                                 .setLabelProvider(new DecoratingJavaLabelProvider(lprovider));
346
347                 fTreeViewer.getTree().addKeyListener(new KeyListener() {
348                         public void keyPressed(KeyEvent e) {
349                                 if (e.character == 0x1B) // ESC
350                                         dispose();
351                         }
352
353                         public void keyReleased(KeyEvent e) {
354                                 // do nothing
355                         }
356                 });
357
358                 fTreeViewer.getTree().addSelectionListener(new SelectionListener() {
359                         public void widgetSelected(SelectionEvent e) {
360                                 // do nothing
361                         }
362
363                         public void widgetDefaultSelected(SelectionEvent e) {
364                                 gotoSelectedElement();
365                         }
366                 });
367         }
368
369         private Text createFilterText(Composite parent) {
370                 fFilterText = new Text(parent, SWT.FLAT);
371
372                 GridData data = new GridData();
373                 GC gc = new GC(parent);
374                 gc.setFont(parent.getFont());
375                 FontMetrics fontMetrics = gc.getFontMetrics();
376                 gc.dispose();
377
378                 data.heightHint = org.eclipse.jface.dialogs.Dialog
379                                 .convertHeightInCharsToPixels(fontMetrics, 1);
380                 data.horizontalAlignment = GridData.FILL;
381                 data.verticalAlignment = GridData.BEGINNING;
382                 fFilterText.setLayoutData(data);
383
384                 fFilterText.addKeyListener(new KeyListener() {
385                         public void keyPressed(KeyEvent e) {
386                                 if (e.keyCode == 0x0D) // return
387                                         gotoSelectedElement();
388                                 if (e.keyCode == SWT.ARROW_DOWN)
389                                         fTreeViewer.getTree().setFocus();
390                                 if (e.keyCode == SWT.ARROW_UP)
391                                         fTreeViewer.getTree().setFocus();
392                                 if (e.character == 0x1B) // ESC
393                                         dispose();
394                         }
395
396                         public void keyReleased(KeyEvent e) {
397                                 // do nothing
398                         }
399                 });
400
401                 // Horizonral separator line
402                 Label separator = new Label(parent, SWT.SEPARATOR | SWT.HORIZONTAL
403                                 | SWT.LINE_DOT);
404                 separator.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
405
406                 return fFilterText;
407         }
408
409         private void setInfoSystemColor() {
410                 Display display = fShell.getDisplay();
411                 setForegroundColor(display.getSystemColor(SWT.COLOR_INFO_FOREGROUND));
412                 setBackgroundColor(display.getSystemColor(SWT.COLOR_INFO_BACKGROUND));
413         }
414
415         private void installFilter() {
416                 final NamePatternFilter viewerFilter = new NamePatternFilter();
417                 fTreeViewer.addFilter(viewerFilter);
418                 fFilterText.setText(""); //$NON-NLS-1$
419
420                 fFilterText.addModifyListener(new ModifyListener() {
421                         public void modifyText(ModifyEvent e) {
422                                 String pattern = fFilterText.getText();
423                                 if (pattern != null) {
424                                         int length = pattern.length();
425                                         if (length == 0)
426                                                 pattern = null;
427                                         else if (pattern.charAt(length - 1) != '*')
428                                                 pattern = pattern + '*';
429                                 } else
430                                         pattern = null;
431                                 viewerFilter.setPattern(pattern);
432                                 fStringMatcher = viewerFilter.getMatcher();
433                                 fTreeViewer.getControl().setRedraw(false);
434                                 fTreeViewer.refresh();
435                                 fTreeViewer.expandAll();
436                                 selectFirstMatch();
437                                 fTreeViewer.getControl().setRedraw(true);
438                         }
439                 });
440         }
441
442         private void gotoSelectedElement() {
443                 Object selectedElement = ((IStructuredSelection) fTreeViewer
444                                 .getSelection()).getFirstElement();
445                 if (selectedElement != null) {
446                         try {
447                                 dispose();
448                                 OpenActionUtil.open(selectedElement, true);
449                         } catch (CoreException ex) {
450                                 WebUI.log(ex);
451                         }
452                 }
453         }
454
455         /**
456          * Selects the first element in the tree which matches the current filter
457          * pattern.
458          */
459         private void selectFirstMatch() {
460                 Tree tree = fTreeViewer.getTree();
461                 Object element = findElement(tree.getItems());
462                 if (element != null)
463                         fTreeViewer.setSelection(new StructuredSelection(element), true);
464                 else
465                         fTreeViewer.setSelection(StructuredSelection.EMPTY);
466         }
467
468         private IJavaElement findElement(TreeItem[] items) {
469                 ILabelProvider labelProvider = (ILabelProvider) fTreeViewer
470                                 .getLabelProvider();
471                 for (int i = 0; i < items.length; i++) {
472                         IJavaElement element = (IJavaElement) items[i].getData();
473                         if (fStringMatcher == null)
474                                 return element;
475
476                         if (element != null) {
477                                 String label = labelProvider.getText(element);
478                                 if (fStringMatcher.match(label))
479                                         return element;
480                         }
481
482                         element = findElement(items[i].getItems());
483                         if (element != null)
484                                 return element;
485                 }
486                 return null;
487         }
488
489         /*
490          * @see IInformationControl#setInformation(String)
491          */
492         public void setInformation(String information) {
493                 // this method is ignored, see IInformationControlExtension2
494         }
495
496         /*
497          * @see IInformationControlExtension2#setInput(Object)
498          */
499         public void setInput(Object information) {
500                 fFilterText.setText(""); //$NON-NLS-1$
501                 if (information == null || information instanceof String) {
502                         setInput(null);
503                         return;
504                 }
505                 IJavaElement je = (IJavaElement) information;
506                 IJavaElement sel = null;
507                 ICompilationUnit cu = (ICompilationUnit) je
508                                 .getAncestor(IJavaElement.COMPILATION_UNIT);
509                 if (cu != null)
510                         sel = cu;
511                 else
512                         sel = je.getAncestor(IJavaElement.CLASS_FILE);
513                 fTreeViewer.setInput(sel);
514                 fTreeViewer.setSelection(new StructuredSelection(information));
515         }
516
517         /*
518          * @see IInformationControl#setVisible(boolean)
519          */
520         public void setVisible(boolean visible) {
521                 fShell.setVisible(visible);
522         }
523
524         /*
525          * @see IInformationControl#dispose()
526          */
527         public void dispose() {
528                 if (fShell != null) {
529                         if (!fShell.isDisposed())
530                                 fShell.dispose();
531                         fShell = null;
532                         fTreeViewer = null;
533                         fComposite = null;
534                         fFilterText = null;
535                 }
536         }
537
538         /*
539          * @see org.eclipse.jface.text.IInformationControlExtension#hasContents()
540          */
541         public boolean hasContents() {
542                 return fTreeViewer != null && fTreeViewer.getInput() != null;
543         }
544
545         /*
546          * @see org.eclipse.jface.text.IInformationControl#setSizeConstraints(int,
547          *      int)
548          */
549         public void setSizeConstraints(int maxWidth, int maxHeight) {
550                 fMaxWidth = maxWidth;
551                 fMaxHeight = maxHeight;
552         }
553
554         /*
555          * @see org.eclipse.jface.text.IInformationControl#computeSizeHint()
556          */
557         public Point computeSizeHint() {
558                 return fShell.computeSize(SWT.DEFAULT, SWT.DEFAULT);
559         }
560
561         /*
562          * @see IInformationControl#setLocation(Point)
563          */
564         public void setLocation(Point location) {
565                 Rectangle trim = fShell.computeTrim(0, 0, 0, 0);
566                 Point textLocation = fComposite.getLocation();
567                 location.x += trim.x - textLocation.x;
568                 location.y += trim.y - textLocation.y;
569                 fShell.setLocation(location);
570         }
571
572         /*
573          * @see IInformationControl#setSize(int, int)
574          */
575         public void setSize(int width, int height) {
576                 fShell.setSize(width, height);
577         }
578
579         /*
580          * @see IInformationControl#addDisposeListener(DisposeListener)
581          */
582         public void addDisposeListener(DisposeListener listener) {
583                 fShell.addDisposeListener(listener);
584         }
585
586         /*
587          * @see IInformationControl#removeDisposeListener(DisposeListener)
588          */
589         public void removeDisposeListener(DisposeListener listener) {
590                 fShell.removeDisposeListener(listener);
591         }
592
593         /*
594          * @see IInformationControl#setForegroundColor(Color)
595          */
596         public void setForegroundColor(Color foreground) {
597                 fTreeViewer.getTree().setForeground(foreground);
598                 fFilterText.setForeground(foreground);
599                 fComposite.setForeground(foreground);
600         }
601
602         /*
603          * @see IInformationControl#setBackgroundColor(Color)
604          */
605         public void setBackgroundColor(Color background) {
606                 fTreeViewer.getTree().setBackground(background);
607                 fFilterText.setBackground(background);
608                 fComposite.setBackground(background);
609         }
610
611         /*
612          * @see IInformationControl#isFocusControl()
613          */
614         public boolean isFocusControl() {
615                 return fTreeViewer.getControl().isFocusControl()
616                                 || fFilterText.isFocusControl();
617         }
618
619         /*
620          * @see IInformationControl#setFocus()
621          */
622         public void setFocus() {
623                 fShell.forceFocus();
624                 fFilterText.setFocus();
625         }
626
627         /*
628          * @see IInformationControl#addFocusListener(FocusListener)
629          */
630         public void addFocusListener(FocusListener listener) {
631                 fShell.addFocusListener(listener);
632         }
633
634         /*
635          * @see IInformationControl#removeFocusListener(FocusListener)
636          */
637         public void removeFocusListener(FocusListener listener) {
638                 fShell.removeFocusListener(listener);
639         }
640 }