new version with WorkingCopy Management
[phpeclipse.git] / net.sourceforge.phpeclipse / 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.PHPeclipsePlugin;
25
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;
64
65 /**
66  * @author dmegert
67  *
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.
72  */
73 public class JavaOutlineInformationControl implements IInformationControl, IInformationControlExtension, IInformationControlExtension2 {
74
75
76         /**
77          * The NamePatternFilter selects the elements which
78          * match the given string patterns.
79          * <p>
80          * The following characters have special meaning:
81          *   ? => any character
82          *   * => any string
83          * </p>
84          *
85          * @since 2.0
86          */
87         private static class NamePatternFilter extends ViewerFilter {
88                 private String fPattern;
89                 private StringMatcher fMatcher;
90                 private ILabelProvider fLabelProvider;
91                 private Viewer fViewer;
92
93                 private StringMatcher getMatcher() {
94                         return fMatcher;
95                 }
96
97
98                 /* (non-Javadoc)
99                  * Method declared on ViewerFilter.
100                  */
101                 public boolean select(Viewer viewer, Object parentElement, Object element) {
102                         if (fMatcher == null)
103                                 return true;
104
105                         ILabelProvider labelProvider= getLabelProvider(viewer);
106
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();
112
113                         if (matchName != null && fMatcher.match(matchName))
114                                 return true;
115
116                         return hasUnfilteredChild(viewer, element);
117                 }
118
119                 private ILabelProvider getLabelProvider(Viewer viewer) {
120                         if (fViewer == viewer)
121                                 return fLabelProvider;
122
123                         fLabelProvider= null;
124                         IBaseLabelProvider baseLabelProvider= null;
125                         if (viewer instanceof StructuredViewer)
126                                 baseLabelProvider= ((StructuredViewer)viewer).getLabelProvider();
127
128                         if (baseLabelProvider instanceof ILabelProvider)
129                                 fLabelProvider= (ILabelProvider)baseLabelProvider;
130
131                         return fLabelProvider;
132                 }
133
134                 private boolean hasUnfilteredChild(Viewer viewer, Object element) {
135                         IJavaElement[] children;
136                         if (element instanceof IParent) {
137                                 try {
138                                         children= ((IParent)element).getChildren();
139                                 } catch (JavaModelException ex) {
140                                         return false;
141                                 }
142                                 for (int i= 0; i < children.length; i++)
143                                         if (select(viewer, element, children[i]))
144                                                 return true;
145                         }
146                         return false;
147                 }
148
149                 /**
150                  * Sets the patterns to filter out for the receiver.
151                  * <p>
152                  * The following characters have special meaning:
153                  *   ? => any character
154                  *   * => any string
155                  * </p>
156                  */
157                 public void setPattern(String pattern) {
158                         fPattern= pattern;
159                         if (fPattern == null) {
160                                 fMatcher= null;
161                                 return;
162                         }
163                         boolean ignoreCase= pattern.toLowerCase().equals(pattern);
164                         fMatcher= new StringMatcher(pattern, ignoreCase, false);
165                 }
166         }
167
168
169         private static class BorderFillLayout extends Layout {
170
171                 /** The border widths. */
172                 final int fBorderSize;
173
174                 /**
175                  * Creates a fill layout with a border.
176                  */
177                 public BorderFillLayout(int borderSize) {
178                         if (borderSize < 0)
179                                 throw new IllegalArgumentException();
180                         fBorderSize= borderSize;
181                 }
182
183                 /**
184                  * Returns the border size.
185                  */
186                 public int getBorderSize() {
187                         return fBorderSize;
188                 }
189
190                 /*
191                  * @see org.eclipse.swt.widgets.Layout#computeSize(org.eclipse.swt.widgets.Composite, int, int, boolean)
192                  */
193                 protected Point computeSize(Composite composite, int wHint, int hHint, boolean flushCache) {
194
195                         Control[] children= composite.getChildren();
196                         Point minSize= new Point(0, 0);
197
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);
203                                 }
204                         }
205
206                         minSize.x += fBorderSize * 2 + RIGHT_MARGIN;
207                         minSize.y += fBorderSize * 2;
208
209                         return minSize;
210                 }
211                 /*
212                  * @see org.eclipse.swt.widgets.Layout#layout(org.eclipse.swt.widgets.Composite, boolean)
213                  */
214                 protected void layout(Composite composite, boolean flushCache) {
215
216                         Control[] children= composite.getChildren();
217                         Point minSize= new Point(composite.getClientArea().width, composite.getClientArea().height);
218
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);
224                                 }
225                         }
226                 }
227         }
228
229
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;
234
235         /** The control's shell */
236         private Shell fShell;
237         /** The composite */
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;
247
248         private StringMatcher fStringMatcher;
249
250
251         /**
252          * Creates a tree information control with the given shell as parent. The given
253          * style is applied to the tree widget.
254          *
255          * @param parent the parent shell
256          * @param style the additional styles for the tree widget
257          */
258         public JavaOutlineInformationControl(Shell parent, int style) {
259                 this(parent, SWT.RESIZE, style);
260         }
261
262         /**
263          * Creates a tree information control with the given shell as parent.
264          * No additional styles are applied.
265          *
266          * @param parent the parent shell
267          */
268         public JavaOutlineInformationControl(Shell parent) {
269                 this(parent, SWT.NONE);
270         }
271
272         /**
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.
275          *
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
279          */
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));
284
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));
290                 
291                 createFilterText(fComposite);
292                 createTreeViewer(fComposite, treeStyle);
293
294                 int border= ((shellStyle & SWT.NO_TRIM) == 0) ? 0 : BORDER;
295                 fShell.setLayout(new BorderFillLayout(border));
296                 
297                 setInfoSystemColor();
298                 installFilter();
299         }
300
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);
305
306                 fTreeViewer= new TreeViewer(tree);
307
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);
312 //                      }
313 //              });
314
315                 fTreeViewer.setContentProvider(new StandardJavaElementContentProvider(true, true));
316                 fTreeViewer.setSorter(new JavaElementSorter());
317                 fTreeViewer.setAutoExpandLevel(AbstractTreeViewer.ALL_LEVELS);
318                 
319                 AppearanceAwareLabelProvider lprovider= new AppearanceAwareLabelProvider(
320                         AppearanceAwareLabelProvider.DEFAULT_TEXTFLAGS |  JavaElementLabels.F_APP_TYPE_SIGNATURE,
321                         AppearanceAwareLabelProvider.DEFAULT_IMAGEFLAGS
322                 );
323                 fTreeViewer.setLabelProvider(new DecoratingJavaLabelProvider(lprovider));
324
325                 fTreeViewer.getTree().addKeyListener(new KeyListener() {
326                         public void keyPressed(KeyEvent e)  {
327                                 if (e.character == 0x1B) // ESC
328                                         dispose();
329                         }
330                         public void keyReleased(KeyEvent e) {
331                                 // do nothing
332                         }
333                 });
334
335                 fTreeViewer.getTree().addSelectionListener(new SelectionListener() {
336                         public void widgetSelected(SelectionEvent e) {
337                                 // do nothing
338                         }
339                         public void widgetDefaultSelected(SelectionEvent e) {
340                                 gotoSelectedElement();
341                         }
342                 });
343         }
344
345         private Text createFilterText(Composite parent) {
346                 fFilterText= new Text(parent, SWT.FLAT);
347
348                 GridData data= new GridData();
349                 GC gc= new GC(parent);
350                 gc.setFont(parent.getFont());
351                 FontMetrics fontMetrics= gc.getFontMetrics();
352                 gc.dispose();
353
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);
358                 
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
368                                         dispose();
369                         }
370                         public void keyReleased(KeyEvent e) {
371                                 // do nothing
372                         }
373                 });
374
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));
378
379                 return fFilterText;
380         }
381         
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));
386         }
387
388         private void installFilter() {
389                 final NamePatternFilter viewerFilter= new NamePatternFilter();
390                 fTreeViewer.addFilter(viewerFilter);
391                 fFilterText.setText(""); //$NON-NLS-1$
392
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();
398                                         if (length == 0)
399                                                 pattern= null;
400                                         else if (pattern.charAt(length -1 ) != '*')
401                                                 pattern= pattern + '*';
402                                 } else
403                                         pattern= null;
404                                 viewerFilter.setPattern(pattern);
405                                 fStringMatcher= viewerFilter.getMatcher();
406                                 fTreeViewer.getControl().setRedraw(false);
407                                 fTreeViewer.refresh();
408                                 fTreeViewer.expandAll();
409                                 selectFirstMatch();
410                                 fTreeViewer.getControl().setRedraw(true);
411                         }
412                 });
413         }
414
415         private void gotoSelectedElement() {
416                 Object selectedElement= ((IStructuredSelection)fTreeViewer.getSelection()).getFirstElement();
417                 if (selectedElement != null) {
418                         try {
419                                 dispose();
420                                 OpenActionUtil.open(selectedElement, true);
421                         } catch (CoreException ex) {
422                                 PHPeclipsePlugin.log(ex);
423                         }
424                 }
425         }
426
427         /**
428          * Selects the first element in the tree which
429          * matches the current filter pattern.
430          */
431         private void selectFirstMatch() {
432                 Tree tree= fTreeViewer.getTree();
433                 Object element= findElement(tree.getItems());
434                 if (element != null)
435                         fTreeViewer.setSelection(new StructuredSelection(element), true);
436                 else
437                         fTreeViewer.setSelection(StructuredSelection.EMPTY);
438         }
439
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)
445                                 return element;
446                         
447                         if (element != null) {
448                                 String label= labelProvider.getText(element);
449                                 if (fStringMatcher.match(label))
450                                         return element;
451                         }
452
453                         element= findElement(items[i].getItems());
454                         if (element != null)
455                                 return element;
456                 }
457                 return null;
458         }
459
460         /*
461          * @see IInformationControl#setInformation(String)
462          */
463         public void setInformation(String information) {
464                 // this method is ignored, see IInformationControlExtension2
465         }
466         
467         /*
468          * @see IInformationControlExtension2#setInput(Object)
469          */
470         public void setInput(Object information) {
471                 fFilterText.setText(""); //$NON-NLS-1$
472                 if (information == null || information instanceof String) {
473                         setInput(null);
474                         return;
475                 }
476                 IJavaElement je= (IJavaElement)information;
477                 IJavaElement sel= null;
478                 ICompilationUnit cu= (ICompilationUnit)je.getAncestor(IJavaElement.COMPILATION_UNIT);
479                 if (cu != null)
480                         sel= cu;
481                 else
482                         sel= je.getAncestor(IJavaElement.CLASS_FILE);
483                 fTreeViewer.setInput(sel);
484                 fTreeViewer.setSelection(new StructuredSelection(information));
485         }
486
487         /*
488          * @see IInformationControl#setVisible(boolean)
489          */
490         public void setVisible(boolean visible) {
491                         fShell.setVisible(visible);
492         }
493
494         /*
495          * @see IInformationControl#dispose()
496          */
497         public void dispose() {
498                 if (fShell != null) {
499                         if (!fShell.isDisposed())
500                                 fShell.dispose();
501                         fShell= null;
502                         fTreeViewer= null;
503                         fComposite= null;
504                         fFilterText= null;
505                 }
506         }
507
508         /* 
509          * @see org.eclipse.jface.text.IInformationControlExtension#hasContents()
510          */
511         public boolean hasContents() {
512                 return fTreeViewer != null && fTreeViewer.getInput() != null;
513         }
514
515         /* 
516          * @see org.eclipse.jface.text.IInformationControl#setSizeConstraints(int, int)
517          */
518         public void setSizeConstraints(int maxWidth, int maxHeight) {
519                 fMaxWidth= maxWidth;
520                 fMaxHeight= maxHeight;
521         }
522
523         /* 
524          * @see org.eclipse.jface.text.IInformationControl#computeSizeHint()
525          */
526         public Point computeSizeHint() {
527                 return fShell.computeSize(SWT.DEFAULT, SWT.DEFAULT);
528         }
529
530         /*
531          * @see IInformationControl#setLocation(Point)
532          */
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);           
539         }
540
541         /*
542          * @see IInformationControl#setSize(int, int)
543          */
544         public void setSize(int width, int height) {
545                 fShell.setSize(width, height);
546         }
547
548         /*
549          * @see IInformationControl#addDisposeListener(DisposeListener)
550          */
551         public void addDisposeListener(DisposeListener listener) {
552                 fShell.addDisposeListener(listener);
553         }
554
555         /*
556          * @see IInformationControl#removeDisposeListener(DisposeListener)
557          */
558         public void removeDisposeListener(DisposeListener listener) {
559                 fShell.removeDisposeListener(listener);
560         }
561
562         /*
563          * @see IInformationControl#setForegroundColor(Color)
564          */
565         public void setForegroundColor(Color foreground) {
566                 fTreeViewer.getTree().setForeground(foreground);
567                 fFilterText.setForeground(foreground);
568                 fComposite.setForeground(foreground);
569         }
570
571         /*
572          * @see IInformationControl#setBackgroundColor(Color)
573          */
574         public void setBackgroundColor(Color background) {
575                 fTreeViewer.getTree().setBackground(background);
576                 fFilterText.setBackground(background);
577                 fComposite.setBackground(background);
578         }
579
580         /*
581          * @see IInformationControl#isFocusControl()
582          */
583         public boolean isFocusControl() {
584                 return fTreeViewer.getControl().isFocusControl() || fFilterText.isFocusControl();
585         }
586
587         /*
588          * @see IInformationControl#setFocus()
589          */
590         public void setFocus() {
591                 fShell.forceFocus();
592                 fFilterText.setFocus();
593         }
594
595         /*
596          * @see IInformationControl#addFocusListener(FocusListener)
597          */
598         public void addFocusListener(FocusListener listener) {
599                 fShell.addFocusListener(listener);
600         }
601
602         /*
603          * @see IInformationControl#removeFocusListener(FocusListener)
604          */
605         public void removeFocusListener(FocusListener listener) {
606                 fShell.removeFocusListener(listener);
607         }
608 }