misc changes
[phpeclipse.git] / net.sourceforge.phpeclipse / src / net / sourceforge / phpeclipse / phpeditor / JavaOutlinePage.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.phpeclipse.phpeditor;
12
13 import java.util.Enumeration;
14 import java.util.Hashtable;
15 import java.util.List;
16 import java.util.Vector;
17
18 import net.sourceforge.phpdt.core.ElementChangedEvent;
19 import net.sourceforge.phpdt.core.ICompilationUnit;
20 import net.sourceforge.phpdt.core.IElementChangedListener;
21 import net.sourceforge.phpdt.core.IJavaElement;
22 import net.sourceforge.phpdt.core.IJavaElementDelta;
23 import net.sourceforge.phpdt.core.IMember;
24 import net.sourceforge.phpdt.core.IParent;
25 import net.sourceforge.phpdt.core.ISourceRange;
26 import net.sourceforge.phpdt.core.ISourceReference;
27 import net.sourceforge.phpdt.core.IType;
28 import net.sourceforge.phpdt.core.JavaCore;
29 import net.sourceforge.phpdt.core.JavaModelException;
30 import net.sourceforge.phpdt.internal.ui.PHPUiImages;
31 import net.sourceforge.phpdt.internal.ui.actions.AbstractToggleLinkingAction;
32 import net.sourceforge.phpdt.internal.ui.actions.CompositeActionGroup;
33 import net.sourceforge.phpdt.internal.ui.dnd.JdtViewerDragAdapter;
34 import net.sourceforge.phpdt.internal.ui.dnd.TransferDragSourceListener;
35 import net.sourceforge.phpdt.internal.ui.packageview.SelectionTransferDragAdapter;
36 import net.sourceforge.phpdt.internal.ui.viewsupport.AppearanceAwareLabelProvider;
37 import net.sourceforge.phpdt.internal.ui.viewsupport.DecoratingJavaLabelProvider;
38 import net.sourceforge.phpdt.internal.ui.viewsupport.JavaElementLabels;
39 import net.sourceforge.phpdt.internal.ui.viewsupport.StatusBarUpdater;
40 import net.sourceforge.phpdt.ui.JavaElementSorter;
41 import net.sourceforge.phpdt.ui.JavaUI;
42 import net.sourceforge.phpdt.ui.PreferenceConstants;
43 import net.sourceforge.phpdt.ui.ProblemsLabelDecorator.ProblemsLabelChangedEvent;
44 import net.sourceforge.phpdt.ui.actions.GenerateActionGroup;
45 import net.sourceforge.phpdt.ui.actions.MemberFilterActionGroup;
46 import net.sourceforge.phpeclipse.PHPeclipsePlugin;
47
48 import org.eclipse.core.resources.IResource;
49 import org.eclipse.core.runtime.IAdaptable;
50 import org.eclipse.jface.action.Action;
51 import org.eclipse.jface.action.IAction;
52 import org.eclipse.jface.action.IMenuListener;
53 import org.eclipse.jface.action.IMenuManager;
54 import org.eclipse.jface.action.IStatusLineManager;
55 import org.eclipse.jface.action.IToolBarManager;
56 import org.eclipse.jface.action.MenuManager;
57 import org.eclipse.jface.action.Separator;
58 import org.eclipse.jface.preference.IPreferenceStore;
59 import org.eclipse.jface.text.Assert;
60 import org.eclipse.jface.text.ITextSelection;
61 import org.eclipse.jface.util.IPropertyChangeListener;
62 import org.eclipse.jface.util.ListenerList;
63 import org.eclipse.jface.util.PropertyChangeEvent;
64 import org.eclipse.jface.viewers.IBaseLabelProvider;
65 import org.eclipse.jface.viewers.IPostSelectionProvider;
66 import org.eclipse.jface.viewers.ISelection;
67 import org.eclipse.jface.viewers.ISelectionChangedListener;
68 import org.eclipse.jface.viewers.IStructuredSelection;
69 import org.eclipse.jface.viewers.ITreeContentProvider;
70 import org.eclipse.jface.viewers.LabelProviderChangedEvent;
71 import org.eclipse.jface.viewers.SelectionChangedEvent;
72 import org.eclipse.jface.viewers.StructuredSelection;
73 import org.eclipse.jface.viewers.TreeViewer;
74 import org.eclipse.jface.viewers.Viewer;
75 import org.eclipse.jface.viewers.ViewerFilter;
76 import org.eclipse.swt.SWT;
77 import org.eclipse.swt.custom.BusyIndicator;
78 import org.eclipse.swt.dnd.DND;
79 import org.eclipse.swt.dnd.Transfer;
80 import org.eclipse.swt.events.KeyEvent;
81 import org.eclipse.swt.widgets.Composite;
82 import org.eclipse.swt.widgets.Control;
83 import org.eclipse.swt.widgets.Display;
84 import org.eclipse.swt.widgets.Item;
85 import org.eclipse.swt.widgets.Menu;
86 import org.eclipse.swt.widgets.Tree;
87 import org.eclipse.swt.widgets.Widget;
88 import org.eclipse.ui.IActionBars;
89 import org.eclipse.ui.actions.ActionContext;
90 import org.eclipse.ui.actions.ActionGroup;
91 import org.eclipse.ui.model.IWorkbenchAdapter;
92 import org.eclipse.ui.model.WorkbenchAdapter;
93 import org.eclipse.ui.part.IPageSite;
94 import org.eclipse.ui.part.IShowInSource;
95 import org.eclipse.ui.part.IShowInTarget;
96 import org.eclipse.ui.part.IShowInTargetList;
97 import org.eclipse.ui.part.Page;
98 import org.eclipse.ui.part.ShowInContext;
99 import org.eclipse.ui.texteditor.ITextEditorActionConstants;
100 import org.eclipse.ui.texteditor.IUpdate;
101 import org.eclipse.ui.texteditor.TextEditorAction;
102 import org.eclipse.ui.texteditor.TextOperationAction;
103 import org.eclipse.ui.views.contentoutline.IContentOutlinePage;
104 import org.eclipse.ui.views.navigator.LocalSelectionTransfer;
105
106 /**
107  * The content outline page of the Java editor. The viewer implements a proprietary update mechanism based on Java model deltas. It
108  * does not react on domain changes. It is specified to show the content of ICompilationUnits and IClassFiles. Pulishes its context
109  * menu under <code>JavaPlugin.getDefault().getPluginId() + ".outline"</code>.
110  */
111 public class JavaOutlinePage extends Page implements IContentOutlinePage, IAdaptable, IPostSelectionProvider {
112
113   static Object[] NO_CHILDREN = new Object[0];
114
115   /**
116    * The element change listener of the java outline viewer.
117    * 
118    * @see IElementChangedListener
119    */
120   class ElementChangedListener implements IElementChangedListener {
121
122     public void elementChanged(final ElementChangedEvent e) {
123
124       if (getControl() == null)
125         return;
126
127       Display d = getControl().getDisplay();
128       if (d != null) {
129         d.asyncExec(new Runnable() {
130           public void run() {
131             ICompilationUnit cu = (ICompilationUnit) fInput;
132             IJavaElement base = cu;
133             if (fTopLevelTypeOnly) {
134               base = getMainType(cu);
135               if (base == null) {
136                 if (fOutlineViewer != null)
137                   fOutlineViewer.refresh(true);
138                 return;
139               }
140             }
141             IJavaElementDelta delta = findElement(base, e.getDelta());
142             if (delta != null && fOutlineViewer != null) {
143               fOutlineViewer.reconcile(delta);
144             }
145           }
146         });
147       }
148     }
149
150     protected IJavaElementDelta findElement(IJavaElement unit, IJavaElementDelta delta) {
151
152       if (delta == null || unit == null)
153         return null;
154
155       IJavaElement element = delta.getElement();
156
157       if (unit.equals(element))
158         return delta;
159
160       if (element.getElementType() > IJavaElement.CLASS_FILE)
161         return null;
162
163       IJavaElementDelta[] children = delta.getAffectedChildren();
164       if (children == null || children.length == 0)
165         return null;
166
167       for (int i = 0; i < children.length; i++) {
168         IJavaElementDelta d = findElement(unit, children[i]);
169         if (d != null)
170           return d;
171       }
172
173       return null;
174     }
175   };
176
177   static class NoClassElement extends WorkbenchAdapter implements IAdaptable {
178     /*
179      * @see java.lang.Object#toString()
180      */
181     public String toString() {
182       return PHPEditorMessages.getString("JavaOutlinePage.error.NoTopLevelType"); //$NON-NLS-1$
183     }
184
185     /*
186      * @see org.eclipse.core.runtime.IAdaptable#getAdapter(Class)
187      */
188     public Object getAdapter(Class clas) {
189       if (clas == IWorkbenchAdapter.class)
190         return this;
191       return null;
192     }
193   }
194
195   /**
196    * Content provider for the children of an ICompilationUnit or an IClassFile
197    * 
198    * @see ITreeContentProvider
199    */
200   class ChildrenProvider implements ITreeContentProvider {
201
202     private Object[] NO_CLASS = new Object[] { new NoClassElement() };
203
204     private ElementChangedListener fListener;
205
206     protected boolean matches(IJavaElement element) {
207       if (element.getElementType() == IJavaElement.METHOD) {
208         String name = element.getElementName();
209         return (name != null && name.indexOf('<') >= 0);
210       }
211       return false;
212     }
213
214     protected IJavaElement[] filter(IJavaElement[] children) {
215       boolean initializers = false;
216       for (int i = 0; i < children.length; i++) {
217         if (matches(children[i])) {
218           initializers = true;
219           break;
220         }
221       }
222
223       if (!initializers)
224         return children;
225
226       Vector v = new Vector();
227       for (int i = 0; i < children.length; i++) {
228         if (matches(children[i]))
229           continue;
230         v.addElement(children[i]);
231       }
232
233       IJavaElement[] result = new IJavaElement[v.size()];
234       v.copyInto(result);
235       return result;
236     }
237
238     public Object[] getChildren(Object parent) {
239       if (parent instanceof IParent) {
240         IParent c = (IParent) parent;
241         try {
242           return filter(c.getChildren());
243         } catch (JavaModelException x) {
244           PHPeclipsePlugin.log(x);
245         }
246       }
247       return NO_CHILDREN;
248     }
249
250     public Object[] getElements(Object parent) {
251       if (fTopLevelTypeOnly) {
252         if (parent instanceof ICompilationUnit) {
253           try {
254             IType type = getMainType((ICompilationUnit) parent);
255             return type != null ? type.getChildren() : NO_CLASS;
256           } catch (JavaModelException e) {
257             PHPeclipsePlugin.log(e);
258           }
259         }
260         //                                              else if (parent instanceof IClassFile) {
261         //                                                      try {
262         //                                                              IType type= getMainType((IClassFile) parent);
263         //                                                              return type != null ? type.getChildren() : NO_CLASS;
264         //                                                      } catch (JavaModelException e) {
265         //                                                              JavaPlugin.log(e);
266         //                                                      }
267         //                                              }
268       }
269       return getChildren(parent);
270     }
271
272     public Object getParent(Object child) {
273       if (child instanceof IJavaElement) {
274         IJavaElement e = (IJavaElement) child;
275         return e.getParent();
276       }
277       return null;
278     }
279
280     public boolean hasChildren(Object parent) {
281       if (parent instanceof IParent) {
282         IParent c = (IParent) parent;
283         try {
284           IJavaElement[] children = filter(c.getChildren());
285           return (children != null && children.length > 0);
286         } catch (JavaModelException x) {
287           PHPeclipsePlugin.log(x);
288         }
289       }
290       return false;
291     }
292
293     public boolean isDeleted(Object o) {
294       return false;
295     }
296
297     public void dispose() {
298       if (fListener != null) {
299         JavaCore.removeElementChangedListener(fListener);
300         fListener = null;
301       }
302     }
303
304     /*
305      * @see IContentProvider#inputChanged(Viewer, Object, Object)
306      */
307     public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
308       boolean isCU = (newInput instanceof ICompilationUnit);
309
310       if (isCU && fListener == null) {
311         fListener = new ElementChangedListener();
312         JavaCore.addElementChangedListener(fListener);
313       } else if (!isCU && fListener != null) {
314         JavaCore.removeElementChangedListener(fListener);
315         fListener = null;
316       }
317     }
318   };
319
320   class JavaOutlineViewer extends TreeViewer {
321
322     /**
323      * Indicates an item which has been reused. At the point of its reuse it has been expanded. This field is used to communicate
324      * between <code>internalExpandToLevel</code> and <code>reuseTreeItem</code>.
325      */
326     private Item fReusedExpandedItem;
327
328     private boolean fReorderedMembers;
329     private boolean fForceFireSelectionChanged;
330
331     public JavaOutlineViewer(Tree tree) {
332       super(tree);
333       setAutoExpandLevel(ALL_LEVELS);
334     }
335
336     /**
337      * Investigates the given element change event and if affected incrementally updates the outline.
338      */
339     /**
340          * Investigates the given element change event and if affected
341          * incrementally updates the Java outline.
342          * 
343          * @param delta the Java element delta used to reconcile the Java outline
344          */
345         public void reconcile(IJavaElementDelta delta) {
346                 fReorderedMembers= false;
347                 fForceFireSelectionChanged= false;
348                 if (getSorter() == null) {
349                         if (fTopLevelTypeOnly
350                                 && delta.getElement() instanceof IType
351                                 && (delta.getKind() & IJavaElementDelta.ADDED) != 0)
352                         {
353                                 refresh(true);
354
355                         } else {
356                                 Widget w= findItem(fInput);
357                                 if (w != null && !w.isDisposed())
358                                         update(w, delta);
359                                 if (fForceFireSelectionChanged)
360                                         fireSelectionChanged(new SelectionChangedEvent(getSite().getSelectionProvider(), this.getSelection()));
361                                 if (fReorderedMembers) {
362                                         refresh(false);
363                                         fReorderedMembers= false;
364                         }
365                         }
366                 } else {
367                         // just for now
368                         refresh(true);
369                 }
370         }
371 //    public void reconcile(IJavaElementDelta delta) {
372 //      fReorderedMembers = false;
373 //      if (getSorter() == null) {
374 //        if (fTopLevelTypeOnly && delta.getElement() instanceof IType && (delta.getKind() & IJavaElementDelta.ADDED) != 0) {
375 //          refresh(true);
376 //
377 //        } else {
378 //          Widget w = findItem(fInput);
379 //          if (w != null && !w.isDisposed())
380 //            update(w, delta);
381 //          if (fReorderedMembers) {
382 //            refresh(false);
383 //            fReorderedMembers = false;
384 //          }
385 //        }
386 //      } else {
387 //        // just for now
388 //        refresh(true);
389 //      }
390 //    }
391
392     /*
393      * @see TreeViewer#internalExpandToLevel
394      */
395     protected void internalExpandToLevel(Widget node, int level) {
396       if (node instanceof Item) {
397         Item i = (Item) node;
398         if (i.getData() instanceof IJavaElement) {
399           IJavaElement je = (IJavaElement) i.getData();
400           if (je.getElementType() == IJavaElement.IMPORT_CONTAINER || isInnerType(je)) {
401             if (i != fReusedExpandedItem) {
402               setExpanded(i, false);
403               return;
404             }
405           }
406         }
407       }
408       super.internalExpandToLevel(node, level);
409     }
410
411     protected void reuseTreeItem(Item item, Object element) {
412
413       // remove children
414       Item[] c = getChildren(item);
415       if (c != null && c.length > 0) {
416
417         if (getExpanded(item))
418           fReusedExpandedItem = item;
419
420         for (int k = 0; k < c.length; k++) {
421           if (c[k].getData() != null)
422             disassociate(c[k]);
423           c[k].dispose();
424         }
425       }
426
427       updateItem(item, element);
428       updatePlus(item, element);
429       internalExpandToLevel(item, ALL_LEVELS);
430
431       fReusedExpandedItem = null;
432       fForceFireSelectionChanged= true;
433     }
434
435     protected boolean mustUpdateParent(IJavaElementDelta delta, IJavaElement element) {
436 //      if (element instanceof IMethod) {
437 //        if ((delta.getKind() & IJavaElementDelta.ADDED) != 0) {
438 //          try {
439 //            return ((IMethod) element).isMainMethod();
440 //          } catch (JavaModelException e) {
441 //            PHPeclipsePlugin.log(e.getStatus());
442 //          }
443 //        }
444 //        return "main".equals(element.getElementName()); //$NON-NLS-1$
445 //      }
446       return false;
447     }
448     /*
449          * @see org.eclipse.jface.viewers.AbstractTreeViewer#isExpandable(java.lang.Object)
450          */
451         public boolean isExpandable(Object element) {
452                 if (hasFilters()) {
453                         return getFilteredChildren(element).length > 0;
454                 }
455                 return super.isExpandable(element);
456         }
457     protected ISourceRange getSourceRange(IJavaElement element) throws JavaModelException {
458       if (element instanceof IMember)// && !(element instanceof IInitializer))
459         return ((IMember) element).getNameRange();
460       if (element instanceof ISourceReference)
461         return ((ISourceReference) element).getSourceRange();
462       return null;
463     }
464
465     protected boolean overlaps(ISourceRange range, int start, int end) {
466       return start <= (range.getOffset() + range.getLength() - 1) && range.getOffset() <= end;
467     }
468
469     protected boolean filtered(IJavaElement parent, IJavaElement child) {
470
471       Object[] result = new Object[] { child };
472       ViewerFilter[] filters = getFilters();
473       for (int i = 0; i < filters.length; i++) {
474         result = filters[i].filter(this, parent, result);
475         if (result.length == 0)
476           return true;
477       }
478
479       return false;
480     }
481
482     protected void update(Widget w, IJavaElementDelta delta) {
483
484       Item item;
485
486       IJavaElement parent = delta.getElement();
487       IJavaElementDelta[] affected = delta.getAffectedChildren();
488       Item[] children = getChildren(w);
489
490       boolean doUpdateParent = false;
491       boolean doUpdateParentsPlus = false;
492
493       Vector deletions = new Vector();
494       Vector additions = new Vector();
495
496       for (int i = 0; i < affected.length; i++) {
497         IJavaElementDelta affectedDelta = affected[i];
498         IJavaElement affectedElement = affectedDelta.getElement();
499         int status = affected[i].getKind();
500
501         // find tree item with affected element
502         int j;
503         for (j = 0; j < children.length; j++)
504           if (affectedElement.equals(children[j].getData()))
505             break;
506
507         if (j == children.length) {
508           //        remove from collapsed parent
509           if ((status & IJavaElementDelta.REMOVED) != 0) {
510             doUpdateParentsPlus = true;
511             continue;
512           }
513           // addition
514           if ((status & IJavaElementDelta.CHANGED) != 0 && (affectedDelta.getFlags() & IJavaElementDelta.F_MODIFIERS) != 0
515               && !filtered(parent, affectedElement)) {
516             additions.addElement(affectedDelta);
517           }
518           continue;
519         }
520
521         item = children[j];
522
523         // removed
524         if ((status & IJavaElementDelta.REMOVED) != 0) {
525           deletions.addElement(item);
526           doUpdateParent = doUpdateParent || mustUpdateParent(affectedDelta, affectedElement);
527
528           // changed
529         } else if ((status & IJavaElementDelta.CHANGED) != 0) {
530           int change = affectedDelta.getFlags();
531           doUpdateParent = doUpdateParent || mustUpdateParent(affectedDelta, affectedElement);
532
533           if ((change & IJavaElementDelta.F_MODIFIERS) != 0) {
534             if (filtered(parent, affectedElement))
535               deletions.addElement(item);
536             else
537               updateItem(item, affectedElement);
538           }
539
540           if ((change & IJavaElementDelta.F_CONTENT) != 0)
541             updateItem(item, affectedElement);
542
543           if ((change & IJavaElementDelta.F_CHILDREN) != 0)
544             update(item, affectedDelta);
545
546           if ((change & IJavaElementDelta.F_REORDER) != 0)
547             fReorderedMembers = true;
548         }
549       }
550
551       // find all elements to add
552       IJavaElementDelta[] add = delta.getAddedChildren();
553       if (additions.size() > 0) {
554         IJavaElementDelta[] tmp = new IJavaElementDelta[add.length + additions.size()];
555         System.arraycopy(add, 0, tmp, 0, add.length);
556         for (int i = 0; i < additions.size(); i++)
557           tmp[i + add.length] = (IJavaElementDelta) additions.elementAt(i);
558         add = tmp;
559       }
560
561       // add at the right position
562       go2: for (int i = 0; i < add.length; i++) {
563
564         try {
565
566           IJavaElement e = add[i].getElement();
567           if (filtered(parent, e))
568             continue go2;
569
570           doUpdateParent = doUpdateParent || mustUpdateParent(add[i], e);
571           ISourceRange rng = getSourceRange(e);
572           int start = rng.getOffset();
573           int end = start + rng.getLength() - 1;
574
575           Item last = null;
576           item = null;
577           children = getChildren(w);
578
579           for (int j = 0; j < children.length; j++) {
580             item = children[j];
581             IJavaElement r = (IJavaElement) item.getData();
582
583             if (r == null) {
584               // parent node collapsed and not be opened before -> do nothing
585               continue go2;
586             }
587
588             try {
589               rng = getSourceRange(r);
590               if (overlaps(rng, start, end)) {
591
592                 // be tolerant if the delta is not correct, or if
593                 // the tree has been updated other than by a delta
594                 reuseTreeItem(item, e);
595                 continue go2;
596
597               } else if (rng.getOffset() > start) {
598
599                 if (last != null && deletions.contains(last)) {
600                   // reuse item
601                   deletions.removeElement(last);
602                   reuseTreeItem(last, (Object) e);
603                 } else {
604                   // nothing to reuse
605                   createTreeItem(w, (Object) e, j);
606                 }
607                 continue go2;
608               }
609
610             } catch (JavaModelException x) {
611               // stumbled over deleted element
612             }
613
614             last = item;
615           }
616
617           // add at the end of the list
618           if (last != null && deletions.contains(last)) {
619             // reuse item
620             deletions.removeElement(last);
621             reuseTreeItem(last, e);
622           } else {
623             // nothing to reuse
624             createTreeItem(w, e, -1);
625           }
626
627         } catch (JavaModelException x) {
628           // the element to be added is not present -> don't add it
629         }
630       }
631
632       // remove items which haven't been reused
633       Enumeration e = deletions.elements();
634       while (e.hasMoreElements()) {
635         item = (Item) e.nextElement();
636         disassociate(item);
637         item.dispose();
638       }
639
640       if (doUpdateParent)
641         updateItem(w, delta.getElement());
642       if (!doUpdateParent && doUpdateParentsPlus && w instanceof Item)
643         updatePlus((Item) w, delta.getElement());
644     }
645
646     /*
647      * @see ContentViewer#handleLabelProviderChanged(LabelProviderChangedEvent)
648      */
649     protected void handleLabelProviderChanged(LabelProviderChangedEvent event) {
650       Object input = getInput();
651       if (event instanceof ProblemsLabelChangedEvent) {
652         ProblemsLabelChangedEvent e = (ProblemsLabelChangedEvent) event;
653         if (e.isMarkerChange() && input instanceof ICompilationUnit) {
654           return; // marker changes can be ignored
655         }
656       }
657       // look if the underlying resource changed
658       Object[] changed = event.getElements();
659       if (changed != null) {
660         IResource resource = getUnderlyingResource();
661         if (resource != null) {
662           for (int i = 0; i < changed.length; i++) {
663             if (changed[i] != null && changed[i].equals(resource)) {
664               // change event to a full refresh
665               event = new LabelProviderChangedEvent((IBaseLabelProvider) event.getSource());
666               break;
667             }
668           }
669         }
670       }
671       super.handleLabelProviderChanged(event);
672     }
673
674     private IResource getUnderlyingResource() {
675       Object input = getInput();
676       if (input instanceof ICompilationUnit) {
677         ICompilationUnit cu = (ICompilationUnit) input;
678         if (cu.isWorkingCopy()) {
679           return cu.getOriginalElement().getResource();
680         } else {
681           return cu.getResource();
682         }
683       }
684       //                                        else if (input instanceof IClassFile) {
685       //                                                return ((IClassFile) input).getResource();
686       //                                        }
687       return null;
688     }
689
690   };
691
692   class LexicalSortingAction extends Action {
693
694     private JavaElementSorter fSorter = new JavaElementSorter();
695
696     public LexicalSortingAction() {
697       super();
698       //                                        WorkbenchHelp.setHelp(this, IJavaHelpContextIds.LEXICAL_SORTING_OUTLINE_ACTION);
699       setText(PHPEditorMessages.getString("JavaOutlinePage.Sort.label")); //$NON-NLS-1$
700       PHPUiImages.setLocalImageDescriptors(this, "alphab_sort_co.gif"); //$NON-NLS-1$
701       setToolTipText(PHPEditorMessages.getString("JavaOutlinePage.Sort.tooltip")); //$NON-NLS-1$
702       setDescription(PHPEditorMessages.getString("JavaOutlinePage.Sort.description")); //$NON-NLS-1$
703
704       boolean checked = PHPeclipsePlugin.getDefault().getPreferenceStore().getBoolean("LexicalSortingAction.isChecked"); //$NON-NLS-1$
705       valueChanged(checked, false);
706     }
707
708     public void run() {
709       valueChanged(isChecked(), true);
710     }
711
712     private void valueChanged(final boolean on, boolean store) {
713       setChecked(on);
714       BusyIndicator.showWhile(fOutlineViewer.getControl().getDisplay(), new Runnable() {
715         public void run() {
716           fOutlineViewer.setSorter(on ? fSorter : null);
717         }
718       });
719
720       if (store)
721         PHPeclipsePlugin.getDefault().getPreferenceStore().setValue("LexicalSortingAction.isChecked", on); //$NON-NLS-1$
722     }
723   };
724
725   class ClassOnlyAction extends Action {
726
727     public ClassOnlyAction() {
728       super();
729       //                                WorkbenchHelp.setHelp(this, IJavaHelpContextIds.GO_INTO_TOP_LEVEL_TYPE_ACTION);
730       setText(PHPEditorMessages.getString("JavaOutlinePage.GoIntoTopLevelType.label")); //$NON-NLS-1$
731       setToolTipText(PHPEditorMessages.getString("JavaOutlinePage.GoIntoTopLevelType.tooltip")); //$NON-NLS-1$
732       setDescription(PHPEditorMessages.getString("JavaOutlinePage.GoIntoTopLevelType.description")); //$NON-NLS-1$
733       PHPUiImages.setLocalImageDescriptors(this, "gointo_toplevel_type.gif"); //$NON-NLS-1$
734
735       IPreferenceStore preferenceStore = PHPeclipsePlugin.getDefault().getPreferenceStore();
736       boolean showclass = preferenceStore.getBoolean("GoIntoTopLevelTypeAction.isChecked"); //$NON-NLS-1$
737       setTopLevelTypeOnly(showclass);
738     }
739
740     /*
741      * @see org.eclipse.jface.action.Action#run()
742      */
743     public void run() {
744       setTopLevelTypeOnly(!fTopLevelTypeOnly);
745     }
746
747     private void setTopLevelTypeOnly(boolean show) {
748       fTopLevelTypeOnly = show;
749       setChecked(show);
750       fOutlineViewer.refresh(false);
751
752       IPreferenceStore preferenceStore = PHPeclipsePlugin.getDefault().getPreferenceStore();
753       preferenceStore.setValue("GoIntoTopLevelTypeAction.isChecked", show); //$NON-NLS-1$
754     }
755   };
756
757   /**
758    * This action toggles whether this Java Outline page links its selection to the active editor.
759    * 
760    * @since 3.0
761    */
762   public class ToggleLinkingAction extends AbstractToggleLinkingAction {
763
764     JavaOutlinePage fJavaOutlinePage;
765
766     /**
767      * Constructs a new action.
768      * 
769      * @param outlinePage
770      *          the Java outline page
771      */
772     public ToggleLinkingAction(JavaOutlinePage outlinePage) {
773       boolean isLinkingEnabled = PreferenceConstants.getPreferenceStore().getBoolean(
774           PreferenceConstants.EDITOR_SYNC_OUTLINE_ON_CURSOR_MOVE);
775       setChecked(isLinkingEnabled);
776       fJavaOutlinePage = outlinePage;
777     }
778
779     /**
780      * Runs the action.
781      */
782     public void run() {
783       PreferenceConstants.getPreferenceStore().setValue(PreferenceConstants.EDITOR_SYNC_OUTLINE_ON_CURSOR_MOVE, isChecked());
784       if (isChecked() && fEditor != null)
785         fEditor.synchronizeOutlinePage(fEditor.computeHighlightRangeSourceReference(), false);
786     }
787
788   }
789
790   /** A flag to show contents of top level type only */
791   private boolean fTopLevelTypeOnly;
792
793   private IJavaElement fInput;
794
795   private String fContextMenuID;
796
797   private Menu fMenu;
798
799   private JavaOutlineViewer fOutlineViewer;
800
801   private PHPEditor fEditor;
802
803   private MemberFilterActionGroup fMemberFilterActionGroup;
804
805   private ListenerList fSelectionChangedListeners = new ListenerList();
806
807   private ListenerList fPostSelectionChangedListeners = new ListenerList();
808
809   private Hashtable fActions = new Hashtable();
810
811   private TogglePresentationAction fTogglePresentation;
812
813   private GotoErrorAction fPreviousError;
814
815   private GotoErrorAction fNextError;
816
817   private TextEditorAction fShowJavadoc;
818
819   private TextOperationAction fUndo;
820
821   private TextOperationAction fRedo;
822
823   private ToggleLinkingAction fToggleLinkingAction;
824
825   private CompositeActionGroup fActionGroups;
826
827   //    private CCPActionGroup fCCPActionGroup;
828
829   private IPropertyChangeListener fPropertyChangeListener;
830   /**
831          * Custom filter action group.
832          * @since 3.0
833          */
834 //  private CustomFiltersActionGroup fCustomFiltersActionGroup;
835         
836   public JavaOutlinePage(String contextMenuID, PHPEditor editor) {
837     super();
838
839     Assert.isNotNull(editor);
840
841     fContextMenuID = contextMenuID;
842     fEditor = editor;
843
844     fTogglePresentation = new TogglePresentationAction();
845     fPreviousError = new GotoErrorAction("PreviousError.", false); //$NON-NLS-1$
846     fPreviousError.setImageDescriptor(PHPUiImages.DESC_TOOL_GOTO_PREV_ERROR);
847     fNextError = new GotoErrorAction("NextError.", true); //$NON-NLS-1$
848     fNextError.setImageDescriptor(PHPUiImages.DESC_TOOL_GOTO_NEXT_ERROR);
849     fShowJavadoc = (TextEditorAction) fEditor.getAction("ShowJavaDoc"); //$NON-NLS-1$
850     fUndo = (TextOperationAction) fEditor.getAction(ITextEditorActionConstants.UNDO);
851     fRedo = (TextOperationAction) fEditor.getAction(ITextEditorActionConstants.REDO);
852
853     fTogglePresentation.setEditor(editor);
854     fPreviousError.setEditor(editor);
855     fNextError.setEditor(editor);
856
857     fPropertyChangeListener = new IPropertyChangeListener() {
858       public void propertyChange(PropertyChangeEvent event) {
859         doPropertyChange(event);
860       }
861     };
862     PHPeclipsePlugin.getDefault().getPreferenceStore().addPropertyChangeListener(fPropertyChangeListener);
863   }
864
865   /**
866    * Returns the primary type of a compilation unit (has the same name as the compilation unit).
867    * 
868    * @param compilationUnit
869    *          the compilation unit
870    * @return returns the primary type of the compilation unit, or <code>null</code> if is does not have one
871    */
872   protected IType getMainType(ICompilationUnit compilationUnit) {
873     String name = compilationUnit.getElementName();
874     int index = name.indexOf('.');
875     if (index != -1)
876       name = name.substring(0, index);
877     IType type = compilationUnit.getType(name);
878     return type.exists() ? type : null;
879   }
880
881   /**
882    * Returns the primary type of a class file.
883    * 
884    * @param classFile
885    *          the class file
886    * @return returns the primary type of the class file, or <code>null</code> if is does not have one
887    */
888   //    protected IType getMainType(IClassFile classFile) {
889   //            try {
890   //                    IType type= classFile.getType();
891   //                    return type != null && type.exists() ? type : null;
892   //            } catch (JavaModelException e) {
893   //                    return null;
894   //            }
895   //    }
896   /*
897    * (non-Javadoc) Method declared on Page
898    */
899   public void init(IPageSite pageSite) {
900     super.init(pageSite);
901   }
902
903   private void doPropertyChange(PropertyChangeEvent event) {
904     if (fOutlineViewer != null) {
905       if (PreferenceConstants.APPEARANCE_MEMBER_SORT_ORDER.equals(event.getProperty())) {
906         fOutlineViewer.refresh(false);
907       }
908     }
909   }
910
911   /*
912    * @see ISelectionProvider#addSelectionChangedListener(ISelectionChangedListener)
913    */
914   public void addSelectionChangedListener(ISelectionChangedListener listener) {
915     if (fOutlineViewer != null)
916       fOutlineViewer.addPostSelectionChangedListener(listener);
917     else
918       fSelectionChangedListeners.add(listener);
919   }
920
921   /*
922    * @see ISelectionProvider#removeSelectionChangedListener(ISelectionChangedListener)
923    */
924   public void removeSelectionChangedListener(ISelectionChangedListener listener) {
925     if (fOutlineViewer != null)
926       fOutlineViewer.removePostSelectionChangedListener(listener);
927     else
928       fSelectionChangedListeners.remove(listener);
929   }
930
931   /*
932    * @see ISelectionProvider#setSelection(ISelection)
933    */
934   public void setSelection(ISelection selection) {
935     if (fOutlineViewer != null)
936       fOutlineViewer.setSelection(selection);
937   }
938
939   /*
940    * @see ISelectionProvider#getSelection()
941    */
942   public ISelection getSelection() {
943     if (fOutlineViewer == null)
944       return StructuredSelection.EMPTY;
945     return fOutlineViewer.getSelection();
946   }
947
948   //  private void registerToolbarActions() {
949   //
950   //    IToolBarManager toolBarManager = getSite().getActionBars().getToolBarManager();
951   //    if (toolBarManager != null) {
952   //      toolBarManager.add(new ClassOnlyAction());
953   //      toolBarManager.add(new LexicalSortingAction());
954   //
955   //      fMemberFilterActionGroup = new MemberFilterActionGroup(fOutlineViewer, "JavaOutlineViewer"); //$NON-NLS-1$
956   //      fMemberFilterActionGroup.contributeToToolBar(toolBarManager);
957   //      
958   //    }
959   //  }
960
961   private void registerToolbarActions(IActionBars actionBars) {
962
963     IToolBarManager toolBarManager = actionBars.getToolBarManager();
964     if (toolBarManager != null) {
965       toolBarManager.add(new LexicalSortingAction());
966
967       fMemberFilterActionGroup = new MemberFilterActionGroup(fOutlineViewer, "org.eclipse.jdt.ui.JavaOutlinePage"); //$NON-NLS-1$
968       fMemberFilterActionGroup.contributeToToolBar(toolBarManager);
969
970       //                fCustomFiltersActionGroup.fillActionBars(actionBars);
971
972       IMenuManager menu = actionBars.getMenuManager();
973       menu.add(new Separator("EndFilterGroup")); //$NON-NLS-1$
974
975       fToggleLinkingAction = new ToggleLinkingAction(this);
976       menu.add(new ClassOnlyAction());
977       menu.add(fToggleLinkingAction);
978     }
979   }
980
981   /*
982    * @see IPage#createControl
983    */
984   public void createControl(Composite parent) {
985
986     Tree tree = new Tree(parent, SWT.MULTI);
987
988     AppearanceAwareLabelProvider lprovider = new AppearanceAwareLabelProvider(AppearanceAwareLabelProvider.DEFAULT_TEXTFLAGS
989         | JavaElementLabels.F_APP_TYPE_SIGNATURE, AppearanceAwareLabelProvider.DEFAULT_IMAGEFLAGS);
990
991     fOutlineViewer = new JavaOutlineViewer(tree);
992     fOutlineViewer.setContentProvider(new ChildrenProvider());
993     fOutlineViewer.setLabelProvider(new DecoratingJavaLabelProvider(lprovider));
994
995     Object[] listeners = fSelectionChangedListeners.getListeners();
996     for (int i = 0; i < listeners.length; i++) {
997       fSelectionChangedListeners.remove(listeners[i]);
998       fOutlineViewer.addPostSelectionChangedListener((ISelectionChangedListener) listeners[i]);
999     }
1000
1001     listeners = fPostSelectionChangedListeners.getListeners();
1002     for (int i = 0; i < listeners.length; i++) {
1003       fPostSelectionChangedListeners.remove(listeners[i]);
1004       fOutlineViewer.addPostSelectionChangedListener((ISelectionChangedListener) listeners[i]);
1005     }
1006
1007     MenuManager manager = new MenuManager(fContextMenuID, fContextMenuID);
1008     manager.setRemoveAllWhenShown(true);
1009     manager.addMenuListener(new IMenuListener() {
1010       public void menuAboutToShow(IMenuManager manager) {
1011         contextMenuAboutToShow(manager);
1012       }
1013     });
1014     fMenu = manager.createContextMenu(tree);
1015     tree.setMenu(fMenu);
1016
1017     IPageSite site = getSite();
1018     site.registerContextMenu(PHPeclipsePlugin.getPluginId() + ".outline", manager, fOutlineViewer); //$NON-NLS-1$
1019     site.setSelectionProvider(fOutlineViewer);
1020
1021     // we must create the groups after we have set the selection provider to the site
1022     fActionGroups = new CompositeActionGroup(new ActionGroup[] {
1023     //                          new OpenViewActionGroup(this),
1024         //                              fCCPActionGroup= new CCPActionGroup(this),
1025         new GenerateActionGroup(this) });
1026     //                          new RefactorActionGroup(this),
1027     //                          new JavaSearchActionGroup(this)});
1028
1029     // register global actions
1030     IActionBars bars = site.getActionBars();
1031
1032     bars.setGlobalActionHandler(ITextEditorActionConstants.UNDO, fUndo);
1033     bars.setGlobalActionHandler(ITextEditorActionConstants.REDO, fRedo);
1034     bars.setGlobalActionHandler(ITextEditorActionConstants.PREVIOUS, fPreviousError);
1035     bars.setGlobalActionHandler(ITextEditorActionConstants.NEXT, fNextError);
1036     //          bars.setGlobalActionHandler(PHPdtActionConstants.SHOW_PHP_DOC, fShowJavadoc);
1037     bars.setGlobalActionHandler(IJavaEditorActionConstants.TOGGLE_PRESENTATION, fTogglePresentation);
1038     // http://dev.eclipse.org/bugs/show_bug.cgi?id=18968
1039     bars.setGlobalActionHandler(IJavaEditorActionConstants.PREVIOUS_ERROR, fPreviousError);
1040     bars.setGlobalActionHandler(IJavaEditorActionConstants.NEXT_ERROR, fNextError);
1041
1042     fActionGroups.fillActionBars(bars);
1043
1044     IStatusLineManager statusLineManager = site.getActionBars().getStatusLineManager();
1045     if (statusLineManager != null) {
1046       StatusBarUpdater updater = new StatusBarUpdater(statusLineManager);
1047       fOutlineViewer.addPostSelectionChangedListener(updater);
1048     }
1049
1050     registerToolbarActions(bars);
1051
1052     fOutlineViewer.setInput(fInput);
1053     //    fOutlineViewer.getControl().addKeyListener(new KeyAdapter() {
1054     //      public void keyPressed(KeyEvent e) {
1055     //        handleKeyReleased(e);
1056     //      }
1057     //    });
1058     //
1059     //    initDragAndDrop();
1060   }
1061
1062   public void dispose() {
1063
1064     if (fEditor == null)
1065       return;
1066
1067     if (fMemberFilterActionGroup != null) {
1068       fMemberFilterActionGroup.dispose();
1069       fMemberFilterActionGroup = null;
1070     }
1071
1072     fEditor.outlinePageClosed();
1073     fEditor = null;
1074
1075     fSelectionChangedListeners.clear();
1076     fSelectionChangedListeners = null;
1077
1078     fPostSelectionChangedListeners.clear();
1079     fPostSelectionChangedListeners = null;
1080
1081     if (fPropertyChangeListener != null) {
1082       PHPeclipsePlugin.getDefault().getPreferenceStore().removePropertyChangeListener(fPropertyChangeListener);
1083       fPropertyChangeListener = null;
1084     }
1085
1086     if (fMenu != null && !fMenu.isDisposed()) {
1087       fMenu.dispose();
1088       fMenu = null;
1089     }
1090
1091     if (fActionGroups != null)
1092       fActionGroups.dispose();
1093
1094     fTogglePresentation.setEditor(null);
1095     fPreviousError.setEditor(null);
1096     fNextError.setEditor(null);
1097
1098     fOutlineViewer = null;
1099
1100     super.dispose();
1101   }
1102
1103   public Control getControl() {
1104     if (fOutlineViewer != null)
1105       return fOutlineViewer.getControl();
1106     return null;
1107   }
1108
1109   public void setInput(IJavaElement inputElement) {
1110     fInput = inputElement;
1111     if (fOutlineViewer != null)
1112       fOutlineViewer.setInput(fInput);
1113   }
1114
1115   public void select(ISourceReference reference) {
1116     if (fOutlineViewer != null) {
1117
1118       ISelection s = fOutlineViewer.getSelection();
1119       if (s instanceof IStructuredSelection) {
1120         IStructuredSelection ss = (IStructuredSelection) s;
1121         List elements = ss.toList();
1122         if (!elements.contains(reference)) {
1123           s = (reference == null ? StructuredSelection.EMPTY : new StructuredSelection(reference));
1124           fOutlineViewer.setSelection(s, true);
1125         }
1126       }
1127     }
1128   }
1129
1130   public void setAction(String actionID, IAction action) {
1131     Assert.isNotNull(actionID);
1132     if (action == null)
1133       fActions.remove(actionID);
1134     else
1135       fActions.put(actionID, action);
1136   }
1137
1138   public IAction getAction(String actionID) {
1139     Assert.isNotNull(actionID);
1140     return (IAction) fActions.get(actionID);
1141   }
1142
1143   /**
1144    * Answer the property defined by key.
1145    */
1146   public Object getAdapter(Class key) {
1147     if (key == IShowInSource.class) {
1148       return getShowInSource();
1149     }
1150     if (key == IShowInTargetList.class) {
1151       return new IShowInTargetList() {
1152         public String[] getShowInTargetIds() {
1153           return new String[] { JavaUI.ID_PACKAGES };
1154         }
1155
1156       };
1157     }
1158     if (key == IShowInTarget.class) {
1159       return getShowInTarget();
1160     }
1161
1162     return null;
1163   }
1164
1165   /**
1166    * Convenience method to add the action installed under the given actionID to the specified group of the menu.
1167    */
1168   protected void addAction(IMenuManager menu, String group, String actionID) {
1169     IAction action = getAction(actionID);
1170     if (action != null) {
1171       if (action instanceof IUpdate)
1172         ((IUpdate) action).update();
1173
1174       if (action.isEnabled()) {
1175         IMenuManager subMenu = menu.findMenuUsingPath(group);
1176         if (subMenu != null)
1177           subMenu.add(action);
1178         else
1179           menu.appendToGroup(group, action);
1180       }
1181     }
1182   }
1183
1184   protected void contextMenuAboutToShow(IMenuManager menu) {
1185
1186     PHPeclipsePlugin.createStandardGroups(menu);
1187
1188     IStructuredSelection selection = (IStructuredSelection) getSelection();
1189     fActionGroups.setContext(new ActionContext(selection));
1190     fActionGroups.fillContextMenu(menu);
1191   }
1192
1193   /*
1194    * @see Page#setFocus()
1195    */
1196   public void setFocus() {
1197     if (fOutlineViewer != null)
1198       fOutlineViewer.getControl().setFocus();
1199   }
1200
1201   /**
1202    * Checkes whether a given Java element is an inner type.
1203    */
1204   private boolean isInnerType(IJavaElement element) {
1205
1206     if (element.getElementType() == IJavaElement.TYPE) {
1207       IJavaElement parent = element.getParent();
1208       int type = parent.getElementType();
1209       return (type != IJavaElement.COMPILATION_UNIT && type != IJavaElement.CLASS_FILE);
1210     }
1211
1212     return false;
1213   }
1214
1215   /**
1216    * Handles key events in viewer.
1217    */
1218   private void handleKeyReleased(KeyEvent event) {
1219
1220     if (event.stateMask != 0)
1221       return;
1222
1223     IAction action = null;
1224     //          if (event.character == SWT.DEL) {
1225     //                  action= fCCPActionGroup.getDeleteAction();
1226     //          }
1227
1228     if (action != null && action.isEnabled())
1229       action.run();
1230   }
1231
1232   /**
1233    * Returns the <code>IShowInSource</code> for this view.
1234    */
1235   protected IShowInSource getShowInSource() {
1236     return new IShowInSource() {
1237       public ShowInContext getShowInContext() {
1238         return new ShowInContext(null, getSite().getSelectionProvider().getSelection());
1239       }
1240     };
1241   }
1242
1243   /**
1244    * Returns the <code>IShowInTarget</code> for this view.
1245    */
1246   protected IShowInTarget getShowInTarget() {
1247     return new IShowInTarget() {
1248       public boolean show(ShowInContext context) {
1249         ISelection sel = context.getSelection();
1250         if (sel instanceof ITextSelection) {
1251           ITextSelection tsel = (ITextSelection) sel;
1252           int offset = tsel.getOffset();
1253           IJavaElement element = fEditor.getElementAt(offset);
1254           if (element != null) {
1255             setSelection(new StructuredSelection(element));
1256             return true;
1257           }
1258         }
1259         return false;
1260       }
1261     };
1262   }
1263
1264   private void initDragAndDrop() {
1265     int ops = DND.DROP_COPY | DND.DROP_MOVE | DND.DROP_LINK;
1266     Transfer[] transfers = new Transfer[] { LocalSelectionTransfer.getInstance() };
1267
1268     // Drop Adapter
1269     //          TransferDropTargetListener[] dropListeners= new TransferDropTargetListener[] {
1270     //                  new SelectionTransferDropAdapter(fOutlineViewer)
1271     //          };
1272     //          fOutlineViewer.addDropSupport(ops | DND.DROP_DEFAULT, transfers, new DelegatingDropAdapter(dropListeners));
1273
1274     // Drag Adapter
1275     TransferDragSourceListener[] dragListeners = new TransferDragSourceListener[] { new SelectionTransferDragAdapter(fOutlineViewer) };
1276     fOutlineViewer.addDragSupport(ops, transfers, new JdtViewerDragAdapter(fOutlineViewer, dragListeners));
1277   }
1278
1279   /*
1280    * @see org.eclipse.jface.text.IPostSelectionProvider#addPostSelectionChangedListener(org.eclipse.jface.viewers.ISelectionChangedListener)
1281    */
1282   public void addPostSelectionChangedListener(ISelectionChangedListener listener) {
1283     if (fOutlineViewer != null)
1284       fOutlineViewer.addPostSelectionChangedListener(listener);
1285     else
1286       fPostSelectionChangedListeners.add(listener);
1287   }
1288
1289   /*
1290    * @see org.eclipse.jface.text.IPostSelectionProvider#removePostSelectionChangedListener(org.eclipse.jface.viewers.ISelectionChangedListener)
1291    */
1292   public void removePostSelectionChangedListener(ISelectionChangedListener listener) {
1293     if (fOutlineViewer != null)
1294       fOutlineViewer.removePostSelectionChangedListener(listener);
1295     else
1296       fPostSelectionChangedListeners.remove(listener);
1297   }
1298 }