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
9 * IBM Corporation - initial API and implementation
10 *******************************************************************************/
11 package net.sourceforge.phpdt.internal.ui.text;
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.PHPeclipsePlugin;
26 import org.eclipse.core.runtime.CoreException;
27 import org.eclipse.jface.text.IInformationControl;
28 import org.eclipse.jface.text.IInformationControlExtension;
29 import org.eclipse.jface.text.IInformationControlExtension2;
30 import org.eclipse.jface.viewers.AbstractTreeViewer;
31 import org.eclipse.jface.viewers.IBaseLabelProvider;
32 import org.eclipse.jface.viewers.ILabelProvider;
33 import org.eclipse.jface.viewers.IStructuredSelection;
34 import org.eclipse.jface.viewers.StructuredSelection;
35 import org.eclipse.jface.viewers.StructuredViewer;
36 import org.eclipse.jface.viewers.TreeViewer;
37 import org.eclipse.jface.viewers.Viewer;
38 import org.eclipse.jface.viewers.ViewerFilter;
39 import org.eclipse.swt.SWT;
40 import org.eclipse.swt.events.DisposeListener;
41 import org.eclipse.swt.events.FocusListener;
42 import org.eclipse.swt.events.KeyEvent;
43 import org.eclipse.swt.events.KeyListener;
44 import org.eclipse.swt.events.ModifyEvent;
45 import org.eclipse.swt.events.ModifyListener;
46 import org.eclipse.swt.events.SelectionEvent;
47 import org.eclipse.swt.events.SelectionListener;
48 import org.eclipse.swt.graphics.Color;
49 import org.eclipse.swt.graphics.FontMetrics;
50 import org.eclipse.swt.graphics.GC;
51 import org.eclipse.swt.graphics.Point;
52 import org.eclipse.swt.graphics.Rectangle;
53 import org.eclipse.swt.layout.GridData;
54 import org.eclipse.swt.layout.GridLayout;
55 import org.eclipse.swt.widgets.Composite;
56 import org.eclipse.swt.widgets.Control;
57 import org.eclipse.swt.widgets.Display;
58 import org.eclipse.swt.widgets.Label;
59 import org.eclipse.swt.widgets.Layout;
60 import org.eclipse.swt.widgets.Shell;
61 import org.eclipse.swt.widgets.Text;
62 import org.eclipse.swt.widgets.Tree;
63 import org.eclipse.swt.widgets.TreeItem;
68 * To change this generated comment edit the template variable "typecomment":
69 * Window>Preferences>Java>Templates.
70 * To enable and disable the creation of type comments go to
71 * Window>Preferences>Java>Code Generation.
73 public class JavaOutlineInformationControl implements IInformationControl, IInformationControlExtension, IInformationControlExtension2 {
77 * The NamePatternFilter selects the elements which
78 * match the given string patterns.
80 * The following characters have special meaning:
87 private static class NamePatternFilter extends ViewerFilter {
88 private String fPattern;
89 private StringMatcher fMatcher;
90 private ILabelProvider fLabelProvider;
91 private Viewer fViewer;
93 private StringMatcher getMatcher() {
99 * Method declared on ViewerFilter.
101 public boolean select(Viewer viewer, Object parentElement, Object element) {
102 if (fMatcher == null)
105 ILabelProvider labelProvider= getLabelProvider(viewer);
107 String matchName= null;
108 if (labelProvider != null)
109 matchName= ((ILabelProvider)labelProvider).getText(element);
110 else if (element instanceof IJavaElement)
111 matchName= ((IJavaElement) element).getElementName();
113 if (matchName != null && fMatcher.match(matchName))
116 return hasUnfilteredChild(viewer, element);
119 private ILabelProvider getLabelProvider(Viewer viewer) {
120 if (fViewer == viewer)
121 return fLabelProvider;
123 fLabelProvider= null;
124 IBaseLabelProvider baseLabelProvider= null;
125 if (viewer instanceof StructuredViewer)
126 baseLabelProvider= ((StructuredViewer)viewer).getLabelProvider();
128 if (baseLabelProvider instanceof ILabelProvider)
129 fLabelProvider= (ILabelProvider)baseLabelProvider;
131 return fLabelProvider;
134 private boolean hasUnfilteredChild(Viewer viewer, Object element) {
135 IJavaElement[] children;
136 if (element instanceof IParent) {
138 children= ((IParent)element).getChildren();
139 } catch (JavaModelException ex) {
142 for (int i= 0; i < children.length; i++)
143 if (select(viewer, element, children[i]))
150 * Sets the patterns to filter out for the receiver.
152 * The following characters have special meaning:
157 public void setPattern(String pattern) {
159 if (fPattern == null) {
163 boolean ignoreCase= pattern.toLowerCase().equals(pattern);
164 fMatcher= new StringMatcher(pattern, ignoreCase, false);
169 private static class BorderFillLayout extends Layout {
171 /** The border widths. */
172 final int fBorderSize;
175 * Creates a fill layout with a border.
177 public BorderFillLayout(int borderSize) {
179 throw new IllegalArgumentException();
180 fBorderSize= borderSize;
184 * Returns the border size.
186 public int getBorderSize() {
191 * @see org.eclipse.swt.widgets.Layout#computeSize(org.eclipse.swt.widgets.Composite, int, int, boolean)
193 protected Point computeSize(Composite composite, int wHint, int hHint, boolean flushCache) {
195 Control[] children= composite.getChildren();
196 Point minSize= new Point(0, 0);
198 if (children != null) {
199 for (int i= 0; i < children.length; i++) {
200 Point size= children[i].computeSize(wHint, hHint, flushCache);
201 minSize.x= Math.max(minSize.x, size.x);
202 minSize.y= Math.max(minSize.y, size.y);
206 minSize.x += fBorderSize * 2 + RIGHT_MARGIN;
207 minSize.y += fBorderSize * 2;
212 * @see org.eclipse.swt.widgets.Layout#layout(org.eclipse.swt.widgets.Composite, boolean)
214 protected void layout(Composite composite, boolean flushCache) {
216 Control[] children= composite.getChildren();
217 Point minSize= new Point(composite.getClientArea().width, composite.getClientArea().height);
219 if (children != null) {
220 for (int i= 0; i < children.length; i++) {
221 Control child= children[i];
222 child.setSize(minSize.x - fBorderSize * 2, minSize.y - fBorderSize * 2);
223 child.setLocation(fBorderSize, fBorderSize);
230 /** Border thickness in pixels. */
231 private static final int BORDER= 1;
232 /** Right margin in pixels. */
233 private static final int RIGHT_MARGIN= 3;
235 /** The control's shell */
236 private Shell fShell;
238 Composite fComposite;
239 /** The control's text widget */
240 private Text fFilterText;
241 /** The control's tree widget */
242 private TreeViewer fTreeViewer;
243 /** The control width constraint */
244 private int fMaxWidth= -1;
245 /** The control height constraint */
246 private int fMaxHeight= -1;
248 private StringMatcher fStringMatcher;
252 * Creates a tree information control with the given shell as parent. The given
253 * style is applied to the tree widget.
255 * @param parent the parent shell
256 * @param style the additional styles for the tree widget
258 public JavaOutlineInformationControl(Shell parent, int style) {
259 this(parent, SWT.RESIZE, style);
263 * Creates a tree information control with the given shell as parent.
264 * No additional styles are applied.
266 * @param parent the parent shell
268 public JavaOutlineInformationControl(Shell parent) {
269 this(parent, SWT.NONE);
273 * Creates a tree information control with the given shell as parent. The given
274 * styles are applied to the shell and the tree widget.
276 * @param parent the parent shell
277 * @param shellStyle the additional styles for the shell
278 * @param treeStyle the additional styles for the tree widget
280 public JavaOutlineInformationControl(Shell parent, int shellStyle, int treeStyle) {
281 fShell= new Shell(parent, shellStyle);
282 Display display= fShell.getDisplay();
283 fShell.setBackground(display.getSystemColor(SWT.COLOR_BLACK));
285 // Composite for filter text and tree
286 fComposite= new Composite(fShell,SWT.RESIZE);
287 GridLayout layout= new GridLayout(1, false);
288 fComposite.setLayout(layout);
289 fComposite.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
291 createFilterText(fComposite);
292 createTreeViewer(fComposite, treeStyle);
294 int border= ((shellStyle & SWT.NO_TRIM) == 0) ? 0 : BORDER;
295 fShell.setLayout(new BorderFillLayout(border));
297 setInfoSystemColor();
301 private void createTreeViewer(Composite parent, int style) {
302 Tree tree= new Tree(parent, SWT.SINGLE | (style & ~SWT.MULTI));
303 GridData data= new GridData(GridData.FILL_BOTH);
304 tree.setLayoutData(data);
306 fTreeViewer= new TreeViewer(tree);
308 // Hide import declartions but show the container
309 // fTreeViewer.addFilter(new ViewerFilter() {
310 // public boolean select(Viewer viewer, Object parentElement, Object element) {
311 // return !(element instanceof IImportDeclaration);
315 fTreeViewer.setContentProvider(new StandardJavaElementContentProvider(true, true));
316 fTreeViewer.setSorter(new JavaElementSorter());
317 fTreeViewer.setAutoExpandLevel(AbstractTreeViewer.ALL_LEVELS);
319 AppearanceAwareLabelProvider lprovider= new AppearanceAwareLabelProvider(
320 AppearanceAwareLabelProvider.DEFAULT_TEXTFLAGS | JavaElementLabels.F_APP_TYPE_SIGNATURE,
321 AppearanceAwareLabelProvider.DEFAULT_IMAGEFLAGS
323 fTreeViewer.setLabelProvider(new DecoratingJavaLabelProvider(lprovider));
325 fTreeViewer.getTree().addKeyListener(new KeyListener() {
326 public void keyPressed(KeyEvent e) {
327 if (e.character == 0x1B) // ESC
330 public void keyReleased(KeyEvent e) {
335 fTreeViewer.getTree().addSelectionListener(new SelectionListener() {
336 public void widgetSelected(SelectionEvent e) {
339 public void widgetDefaultSelected(SelectionEvent e) {
340 gotoSelectedElement();
345 private Text createFilterText(Composite parent) {
346 fFilterText= new Text(parent, SWT.FLAT);
348 GridData data= new GridData();
349 GC gc= new GC(parent);
350 gc.setFont(parent.getFont());
351 FontMetrics fontMetrics= gc.getFontMetrics();
354 data.heightHint= org.eclipse.jface.dialogs.Dialog.convertHeightInCharsToPixels(fontMetrics, 1);
355 data.horizontalAlignment= GridData.FILL;
356 data.verticalAlignment= GridData.BEGINNING;
357 fFilterText.setLayoutData(data);
359 fFilterText.addKeyListener(new KeyListener() {
360 public void keyPressed(KeyEvent e) {
361 if (e.keyCode == 0x0D) // return
362 gotoSelectedElement();
363 if (e.keyCode == SWT.ARROW_DOWN)
364 fTreeViewer.getTree().setFocus();
365 if (e.keyCode == SWT.ARROW_UP)
366 fTreeViewer.getTree().setFocus();
367 if (e.character == 0x1B) // ESC
370 public void keyReleased(KeyEvent e) {
375 // Horizonral separator line
376 Label separator= new Label(parent, SWT.SEPARATOR | SWT.HORIZONTAL | SWT.LINE_DOT);
377 separator.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
382 private void setInfoSystemColor() {
383 Display display= fShell.getDisplay();
384 setForegroundColor(display.getSystemColor(SWT.COLOR_INFO_FOREGROUND));
385 setBackgroundColor(display.getSystemColor(SWT.COLOR_INFO_BACKGROUND));
388 private void installFilter() {
389 final NamePatternFilter viewerFilter= new NamePatternFilter();
390 fTreeViewer.addFilter(viewerFilter);
391 fFilterText.setText(""); //$NON-NLS-1$
393 fFilterText.addModifyListener(new ModifyListener() {
394 public void modifyText(ModifyEvent e) {
395 String pattern= fFilterText.getText();
396 if (pattern != null) {
397 int length= pattern.length();
400 else if (pattern.charAt(length -1 ) != '*')
401 pattern= pattern + '*';
404 viewerFilter.setPattern(pattern);
405 fStringMatcher= viewerFilter.getMatcher();
406 fTreeViewer.getControl().setRedraw(false);
407 fTreeViewer.refresh();
408 fTreeViewer.expandAll();
410 fTreeViewer.getControl().setRedraw(true);
415 private void gotoSelectedElement() {
416 Object selectedElement= ((IStructuredSelection)fTreeViewer.getSelection()).getFirstElement();
417 if (selectedElement != null) {
420 OpenActionUtil.open(selectedElement, true);
421 } catch (CoreException ex) {
422 PHPeclipsePlugin.log(ex);
428 * Selects the first element in the tree which
429 * matches the current filter pattern.
431 private void selectFirstMatch() {
432 Tree tree= fTreeViewer.getTree();
433 Object element= findElement(tree.getItems());
435 fTreeViewer.setSelection(new StructuredSelection(element), true);
437 fTreeViewer.setSelection(StructuredSelection.EMPTY);
440 private IJavaElement findElement(TreeItem[] items) {
441 ILabelProvider labelProvider= (ILabelProvider)fTreeViewer.getLabelProvider();
442 for (int i= 0; i < items.length; i++) {
443 IJavaElement element= (IJavaElement)items[i].getData();
444 if (fStringMatcher == null)
447 if (element != null) {
448 String label= labelProvider.getText(element);
449 if (fStringMatcher.match(label))
453 element= findElement(items[i].getItems());
461 * @see IInformationControl#setInformation(String)
463 public void setInformation(String information) {
464 // this method is ignored, see IInformationControlExtension2
468 * @see IInformationControlExtension2#setInput(Object)
470 public void setInput(Object information) {
471 fFilterText.setText(""); //$NON-NLS-1$
472 if (information == null || information instanceof String) {
476 IJavaElement je= (IJavaElement)information;
477 IJavaElement sel= null;
478 ICompilationUnit cu= (ICompilationUnit)je.getAncestor(IJavaElement.COMPILATION_UNIT);
482 sel= je.getAncestor(IJavaElement.CLASS_FILE);
483 fTreeViewer.setInput(sel);
484 fTreeViewer.setSelection(new StructuredSelection(information));
488 * @see IInformationControl#setVisible(boolean)
490 public void setVisible(boolean visible) {
491 fShell.setVisible(visible);
495 * @see IInformationControl#dispose()
497 public void dispose() {
498 if (fShell != null) {
499 if (!fShell.isDisposed())
509 * @see org.eclipse.jface.text.IInformationControlExtension#hasContents()
511 public boolean hasContents() {
512 return fTreeViewer != null && fTreeViewer.getInput() != null;
516 * @see org.eclipse.jface.text.IInformationControl#setSizeConstraints(int, int)
518 public void setSizeConstraints(int maxWidth, int maxHeight) {
520 fMaxHeight= maxHeight;
524 * @see org.eclipse.jface.text.IInformationControl#computeSizeHint()
526 public Point computeSizeHint() {
527 return fShell.computeSize(SWT.DEFAULT, SWT.DEFAULT);
531 * @see IInformationControl#setLocation(Point)
533 public void setLocation(Point location) {
534 Rectangle trim= fShell.computeTrim(0, 0, 0, 0);
535 Point textLocation= fComposite.getLocation();
536 location.x += trim.x - textLocation.x;
537 location.y += trim.y - textLocation.y;
538 fShell.setLocation(location);
542 * @see IInformationControl#setSize(int, int)
544 public void setSize(int width, int height) {
545 fShell.setSize(width, height);
549 * @see IInformationControl#addDisposeListener(DisposeListener)
551 public void addDisposeListener(DisposeListener listener) {
552 fShell.addDisposeListener(listener);
556 * @see IInformationControl#removeDisposeListener(DisposeListener)
558 public void removeDisposeListener(DisposeListener listener) {
559 fShell.removeDisposeListener(listener);
563 * @see IInformationControl#setForegroundColor(Color)
565 public void setForegroundColor(Color foreground) {
566 fTreeViewer.getTree().setForeground(foreground);
567 fFilterText.setForeground(foreground);
568 fComposite.setForeground(foreground);
572 * @see IInformationControl#setBackgroundColor(Color)
574 public void setBackgroundColor(Color background) {
575 fTreeViewer.getTree().setBackground(background);
576 fFilterText.setBackground(background);
577 fComposite.setBackground(background);
581 * @see IInformationControl#isFocusControl()
583 public boolean isFocusControl() {
584 return fTreeViewer.getControl().isFocusControl() || fFilterText.isFocusControl();
588 * @see IInformationControl#setFocus()
590 public void setFocus() {
592 fFilterText.setFocus();
596 * @see IInformationControl#addFocusListener(FocusListener)
598 public void addFocusListener(FocusListener listener) {
599 fShell.addFocusListener(listener);
603 * @see IInformationControl#removeFocusListener(FocusListener)
605 public void removeFocusListener(FocusListener listener) {
606 fShell.removeFocusListener(listener);