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