/******************************************************************************* * Copyright (c) 2000, 2017 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * *******************************************************************************/ package org.eclipse.dltk.ui.text; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.OperationCanceledException; import org.eclipse.dltk.core.IMember; import org.eclipse.dltk.core.IMethod; import org.eclipse.dltk.core.IModelElement; import org.eclipse.dltk.core.ISourceModule; import org.eclipse.dltk.core.IType; import org.eclipse.dltk.core.ITypeHierarchy; import org.eclipse.dltk.core.ModelException; import org.eclipse.dltk.internal.core.ModelElement; import org.eclipse.dltk.internal.core.hierarchy.TypeHierarchyBuilders; import org.eclipse.dltk.internal.corext.util.Messages; import org.eclipse.dltk.internal.ui.StandardModelElementContentProvider; import org.eclipse.dltk.internal.ui.text.AbstractInformationControl; import org.eclipse.dltk.internal.ui.text.TextMessages; import org.eclipse.dltk.internal.ui.typehierarchy.AbstractHierarchyViewerSorter; import org.eclipse.dltk.internal.ui.util.StringMatcher; import org.eclipse.dltk.ui.DLTKPluginImages; import org.eclipse.dltk.ui.DLTKUIPlugin; import org.eclipse.dltk.ui.ProblemsLabelDecorator; import org.eclipse.dltk.ui.ScriptElementLabels; import org.eclipse.dltk.ui.viewsupport.AppearanceAwareLabelProvider; import org.eclipse.dltk.ui.viewsupport.MemberFilter; import org.eclipse.dltk.ui.viewsupport.StyledDecoratingModelLabelProvider; import org.eclipse.jface.action.Action; import org.eclipse.jface.action.IAction; import org.eclipse.jface.action.IMenuManager; import org.eclipse.jface.preference.IPreferenceStore; import org.eclipse.jface.viewers.AbstractTreeViewer; import org.eclipse.jface.viewers.TreeViewer; import org.eclipse.jface.viewers.Viewer; import org.eclipse.jface.viewers.ViewerFilter; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.BusyIndicator; import org.eclipse.swt.events.KeyAdapter; import org.eclipse.swt.events.KeyEvent; import org.eclipse.swt.graphics.Color; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Item; import org.eclipse.swt.widgets.Shell; import org.eclipse.swt.widgets.Text; import org.eclipse.swt.widgets.Tree; import org.eclipse.swt.widgets.Widget; import org.eclipse.ui.IEditorPart; import org.eclipse.ui.IWorkbenchPage; import org.eclipse.ui.keys.KeySequence; import org.eclipse.ui.keys.SWTKeySupport; /** * Show outline in light-weight control. */ public class ScriptOutlineInformationControl extends AbstractInformationControl { private KeyAdapter fKeyAdapter; private OutlineContentProvider fOutlineContentProvider; private IModelElement fInput = null; private OutlineSorter fOutlineSorter; private OutlineLabelProvider fInnerLabelProvider; protected Color fForegroundColor; private boolean fShowOnlyMainType; private LexicalSortingAction fLexicalSortingAction; private SortByDefiningTypeAction fSortByDefiningTypeAction; // private ShowOnlyMainTypeAction fShowOnlyMainTypeAction; protected Map<IType, ITypeHierarchy> fTypeHierarchies = new HashMap<>(); private final IPreferenceStore fPreferenceStore; /** * Category filter action group. * */ private String fPattern; protected IPreferenceStore getPreferenceStore() { return fPreferenceStore; } private class OutlineLabelProvider extends AppearanceAwareLabelProvider { private boolean fShowDefiningType; private OutlineLabelProvider() { super(AppearanceAwareLabelProvider.DEFAULT_TEXTFLAGS | ScriptElementLabels.F_APP_TYPE_SIGNATURE | ScriptElementLabels.M_APP_RETURNTYPE | ScriptElementLabels.ALL_CATEGORY, AppearanceAwareLabelProvider.DEFAULT_IMAGEFLAGS, getPreferenceStore()); } @Override public String getText(Object element) { String text = super.getText(element); if (fShowDefiningType) { try { IType type = getDefiningType(element); if (type != null) { StringBuffer buf = new StringBuffer( super.getText(type)); buf.append(ScriptElementLabels.CONCAT_STRING); buf.append(text); return buf.toString(); } } catch (ModelException e) { } } return text; } @Override public Color getForeground(Object element) { if (fOutlineContentProvider.isShowingInheritedMembers()) { if (element instanceof IModelElement) { IModelElement je = (IModelElement) element; je = je.getAncestor(IModelElement.SOURCE_MODULE); if (fInput.equals(je)) { return null; } } return fForegroundColor; } return null; } public void setShowDefiningType(boolean showDefiningType) { fShowDefiningType = showDefiningType; } private IType getDefiningType(Object element) throws ModelException { int kind = ((IModelElement) element).getElementType(); if (kind != IModelElement.METHOD && kind != IModelElement.FIELD) { return null; } IType declaringType = ((IMember) element).getDeclaringType(); if (kind != IModelElement.METHOD) { return declaringType; } ITypeHierarchy hierarchy = getSuperTypeHierarchy(declaringType); if (hierarchy == null) { return declaringType; } IMethod method = (IMethod) element; // MethodOverrideTester tester= new // MethodOverrideTester(declaringType, hierarchy); // IMethod res= tester.findDeclaringMethod(method, true); // if (res == null || method.equals(res)) { // return declaringType; // } // return res.getDeclaringType(); return method.getDeclaringType(); } } private class OutlineTreeViewer extends TreeViewer { private boolean fIsFiltering = false; private OutlineTreeViewer(Tree tree) { super(tree); } @Override protected Object[] getFilteredChildren(Object parent) { Object[] result = getRawChildren(parent); int unfilteredChildren = result.length; ViewerFilter[] filters = getFilters(); if (filters != null) { for (int i = 0; i < filters.length; i++) result = filters[i].filter(this, parent, result); } fIsFiltering = unfilteredChildren != result.length; return result; } @Override protected void internalExpandToLevel(Widget node, int level) { if (!fIsFiltering && node instanceof Item) { Item i = (Item) node; if (i.getData() instanceof IModelElement) { IModelElement je = (IModelElement) i.getData(); if (je.getElementType() == IModelElement.IMPORT_CONTAINER || (je.getElementType() == ModelElement.METHOD && !expandMethodChildren((IMethod) je)) || isInnerType(je)) { setExpanded(i, false); return; } } } super.internalExpandToLevel(node, level); } private boolean expandMethodChildren(IMethod method) { try { for (IModelElement child : method.getChildren()) { if (child.getElementType() != IModelElement.FIELD) { return true; } } } catch (ModelException e) { // ignore } return false; } } private class OutlineContentProvider extends StandardModelElementContentProvider { private boolean fShowInheritedMembers; /** * Creates a new Outline content provider. * * @param showInheritedMembers * <code>true</code> iff inherited members are shown */ private OutlineContentProvider(boolean showInheritedMembers) { super(true); fShowInheritedMembers = showInheritedMembers; } public boolean isShowingInheritedMembers() { return fShowInheritedMembers; } public void toggleShowInheritedMembers() { Tree tree = getTreeViewer().getTree(); tree.setRedraw(false); fShowInheritedMembers = !fShowInheritedMembers; getTreeViewer().refresh(); getTreeViewer().expandToLevel(2); // reveal selection Object selectedElement = getSelectedElement(); if (selectedElement != null) getTreeViewer().reveal(selectedElement); tree.setRedraw(true); } @Override public Object[] getChildren(Object element) { if (fShowInheritedMembers && element instanceof IType) { IType type = (IType) element; if (!isInnerType(type)) { ITypeHierarchy th = getSuperTypeHierarchy(type); if (th != null) { List<Object> children = new ArrayList<>(); Collections.addAll(children, super.getChildren(type)); IType[] superClasses = th.getAllSupertypes(type); for (int i = 0, scLength = superClasses.length; i < scLength; i++) { Collections.addAll(children, super.getChildren(superClasses[i])); } return children.toArray(); } } } return super.getChildren(element); } @Override public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { super.inputChanged(viewer, oldInput, newInput); fTypeHierarchies.clear(); } @Override public void dispose() { super.dispose(); fTypeHierarchies.clear(); } } private class OutlineSorter extends AbstractHierarchyViewerSorter { @Override protected ITypeHierarchy getHierarchy(IType type) { return getSuperTypeHierarchy(type); } @Override public boolean isSortByDefiningType() { return fSortByDefiningTypeAction.isChecked(); } @Override public boolean isSortAlphabetically() { return fLexicalSortingAction.isChecked(); } } private class LexicalSortingAction extends Action { private static final String STORE_LEXICAL_SORTING_CHECKED = "LexicalSortingAction.isChecked"; //$NON-NLS-1$ private TreeViewer fOutlineViewer; private LexicalSortingAction(TreeViewer outlineViewer) { super(TextMessages.ScriptOutlineInformationControl_LexicalSortingAction_label, IAction.AS_CHECK_BOX); setToolTipText( TextMessages.ScriptOutlineInformationControl_LexicalSortingAction_tooltip); setDescription( TextMessages.ScriptOutlineInformationControl_LexicalSortingAction_description); DLTKPluginImages.setLocalImageDescriptors(this, "alphab_sort_co.png"); //$NON-NLS-1$ fOutlineViewer = outlineViewer; boolean checked = getDialogSettings() .getBoolean(STORE_LEXICAL_SORTING_CHECKED); setChecked(checked); // PlatformUI.getWorkbench().getHelpSystem().setHelp(this, // IJavaHelpContextIds.LEXICAL_SORTING_BROWSING_ACTION); if (DLTKUIPlugin.isDebug()) { System.err.println("TODO: add help support here"); //$NON-NLS-1$ } } @Override public void run() { valueChanged(isChecked(), true); } private void valueChanged(final boolean on, boolean store) { setChecked(on); BusyIndicator.showWhile(fOutlineViewer.getControl().getDisplay(), () -> fOutlineViewer.refresh(false)); if (store) getDialogSettings().put(STORE_LEXICAL_SORTING_CHECKED, on); } } private class SortByDefiningTypeAction extends Action { private static final String STORE_SORT_BY_DEFINING_TYPE_CHECKED = "SortByDefiningType.isChecked"; //$NON-NLS-1$ private TreeViewer fOutlineViewer; /** * Creates the action. * * @param outlineViewer * the outline viewer */ private SortByDefiningTypeAction(TreeViewer outlineViewer) { super(TextMessages.ScriptOutlineInformationControl_SortByDefiningTypeAction_label); setDescription( TextMessages.ScriptOutlineInformationControl_SortByDefiningTypeAction_description); setToolTipText( TextMessages.ScriptOutlineInformationControl_SortByDefiningTypeAction_tooltip); DLTKPluginImages.setLocalImageDescriptors(this, "definingtype_sort_co.png"); //$NON-NLS-1$ fOutlineViewer = outlineViewer; // PlatformUI.getWorkbench().getHelpSystem().setHelp(this, // IJavaHelpContextIds.SORT_BY_DEFINING_TYPE_ACTION); if (DLTKUIPlugin.isDebug()) { System.err.println("TODO: add help support here"); //$NON-NLS-1$ } boolean state = getDialogSettings() .getBoolean(STORE_SORT_BY_DEFINING_TYPE_CHECKED); setChecked(state); fInnerLabelProvider.setShowDefiningType(state); } @Override public void run() { BusyIndicator.showWhile(fOutlineViewer.getControl().getDisplay(), () -> { fInnerLabelProvider.setShowDefiningType(isChecked()); getDialogSettings().put( STORE_SORT_BY_DEFINING_TYPE_CHECKED, isChecked()); setMatcherString(fPattern, false); fOutlineViewer.refresh(true); // reveal selection Object selectedElement = getSelectedElement(); if (selectedElement != null) fOutlineViewer.reveal(selectedElement); }); } } /** * String matcher that can match two patterns. */ private static class OrStringMatcher extends StringMatcher { private StringMatcher fMatcher1; private StringMatcher fMatcher2; private OrStringMatcher(String pattern1, String pattern2, boolean ignoreCase, boolean foo) { super("", false, false); //$NON-NLS-1$ fMatcher1 = new StringMatcher(pattern1, ignoreCase, false); fMatcher2 = new StringMatcher(pattern2, ignoreCase, false); } @Override public boolean match(String text) { return fMatcher2.match(text) || fMatcher1.match(text); } } /** * Creates a new Script outline information control. * * @param parent * @param shellStyle * @param treeStyle * @param commandId */ @Deprecated public ScriptOutlineInformationControl(Shell parent, int shellStyle, int treeStyle, String commandId) { this(parent, shellStyle, treeStyle, commandId, DLTKUIPlugin.getDefault().getPreferenceStore()); } /** * Creates a new Script outline information control. * * @param parent * @param shellStyle * @param treeStyle * @param commandId * @param preferenceStore * @since 2.0 */ public ScriptOutlineInformationControl(Shell parent, int shellStyle, int treeStyle, String commandId, IPreferenceStore preferenceStore) { super(parent, shellStyle, treeStyle, commandId, true); this.fPreferenceStore = preferenceStore; create(); } /** * @since 2.0 */ @Override protected final boolean isEarlyCreate() { return false; } @Override protected Text createFilterText(Composite parent) { Text text = super.createFilterText(parent); text.addKeyListener(getKeyAdapter()); return text; } @Override protected TreeViewer createTreeViewer(Composite parent, int style) { Tree tree = new Tree(parent, SWT.SINGLE | (style & ~SWT.MULTI)); GridData gd = new GridData(GridData.FILL_BOTH); gd.heightHint = tree.getItemHeight() * 12; tree.setLayoutData(gd); final TreeViewer treeViewer = new OutlineTreeViewer(tree); // Hard-coded filters treeViewer.addFilter(new NamePatternFilter()); treeViewer.addFilter(new MemberFilter()); fForegroundColor = parent.getDisplay() .getSystemColor(SWT.COLOR_DARK_GRAY); fInnerLabelProvider = new OutlineLabelProvider(); fInnerLabelProvider.addLabelDecorator(new ProblemsLabelDecorator(null)); // IDecoratorManager decoratorMgr= // PlatformUI.getWorkbench().getDecoratorManager(); /* * if * (decoratorMgr.getEnabled("org.eclipse.dltk.ui.override.decorator")) * //$NON-NLS-1$ fInnerLabelProvider.addLabelDecorator(new * OverrideIndicatorLabelDecorator(null)); */ treeViewer.setLabelProvider( new StyledDecoratingModelLabelProvider(fInnerLabelProvider)); fLexicalSortingAction = new LexicalSortingAction(treeViewer); fSortByDefiningTypeAction = new SortByDefiningTypeAction(treeViewer); // fShowOnlyMainTypeAction= new ShowOnlyMainTypeAction(treeViewer); // fCategoryFilterActionGroup= new CategoryFilterActionGroup(treeViewer, // getId(), getInputForCategories()); fOutlineContentProvider = new OutlineContentProvider(false); treeViewer.setContentProvider(fOutlineContentProvider); fOutlineSorter = new OutlineSorter(); treeViewer.setComparator(fOutlineSorter); treeViewer.setAutoExpandLevel(AbstractTreeViewer.ALL_LEVELS); treeViewer.getTree().addKeyListener(getKeyAdapter()); return treeViewer; } @Override protected String getStatusFieldText() { KeySequence[] sequences = getInvokingCommandKeySequences(); if (sequences == null || sequences.length == 0) return ""; //$NON-NLS-1$ String keySequence = sequences[0].format(); if (fOutlineContentProvider.isShowingInheritedMembers()) return Messages.format( TextMessages.ScriptOutlineInformationControl_pressToHideInheritedMembers, keySequence); else return Messages.format( TextMessages.ScriptOutlineInformationControl_pressToShowInheritedMembers, keySequence); } @Override protected String getId() { return "org.eclipse.dltk.internal.ui.text.QuickOutline"; //$NON-NLS-1$ } @Override public void setInput(Object information) { if (information == null || information instanceof String) { inputChanged(null, null); return; } IModelElement je = (IModelElement) information; ISourceModule cu = (ISourceModule) je .getAncestor(IModelElement.SOURCE_MODULE); if (cu != null) fInput = cu; inputChanged(fInput, information); // fCategoryFilterActionGroup.setInput(getInputForCategories()); } private KeyAdapter getKeyAdapter() { if (fKeyAdapter == null) { fKeyAdapter = new KeyAdapter() { @Override public void keyPressed(KeyEvent e) { int accelerator = SWTKeySupport .convertEventToUnmodifiedAccelerator(e); KeySequence keySequence = KeySequence.getInstance( SWTKeySupport.convertAcceleratorToKeyStroke( accelerator)); KeySequence[] sequences = getInvokingCommandKeySequences(); if (sequences == null) return; for (int i = 0; i < sequences.length; i++) { if (sequences[i].equals(keySequence)) { e.doit = false; toggleShowInheritedMembers(); return; } } } }; } return fKeyAdapter; } @Override protected void handleStatusFieldClicked() { toggleShowInheritedMembers(); } protected void toggleShowInheritedMembers() { long flags = fInnerLabelProvider.getTextFlags(); flags ^= ScriptElementLabels.ALL_POST_QUALIFIED; fInnerLabelProvider.setTextFlags(flags); fOutlineContentProvider.toggleShowInheritedMembers(); updateStatusFieldText(); // fCategoryFilterActionGroup.setInput(getInputForCategories()); } @Override protected void fillViewMenu(IMenuManager viewMenu) { super.fillViewMenu(viewMenu); // viewMenu.add(fShowOnlyMainTypeAction); // viewMenu.add(new Separator("Sorters")); //$NON-NLS-1$ // viewMenu.add(fLexicalSortingAction); // viewMenu.add(fSortByDefiningTypeAction); // fCategoryFilterActionGroup.setInput(getInputForCategories()); // fCategoryFilterActionGroup.contributeToViewMenu(viewMenu); } @Override protected void setMatcherString(String pattern, boolean update) { fPattern = pattern; if (pattern.length() == 0 || !fSortByDefiningTypeAction.isChecked()) { super.setMatcherString(pattern, update); return; } boolean ignoreCase = pattern.toLowerCase().equals(pattern); String pattern2 = "*" + ScriptElementLabels.CONCAT_STRING + pattern; //$NON-NLS-1$ fStringMatcher = new OrStringMatcher(pattern, pattern2, ignoreCase, false); if (update) stringMatcherUpdated(); } protected ITypeHierarchy getSuperTypeHierarchy(IType type) { ITypeHierarchy th = fTypeHierarchies.get(type); if (th == null) { try { th = TypeHierarchyBuilders.getTypeHierarchy(type, ITypeHierarchy.Mode.SUPERTYPE, getProgressMonitor()); } catch (OperationCanceledException e) { return null; } fTypeHierarchies.put(type, th); } return th; } protected IProgressMonitor getProgressMonitor() { IWorkbenchPage wbPage = DLTKUIPlugin.getActivePage(); if (wbPage == null) return null; IEditorPart editor = wbPage.getActiveEditor(); if (editor == null) return null; return editor.getEditorSite().getActionBars().getStatusLineManager() .getProgressMonitor(); } protected boolean isInnerType(IModelElement element) { if (element != null && element.getElementType() == IModelElement.TYPE) { IType type = (IType) element; // try { return type.getDeclaringType() != null; /* * } catch (ModelException e) { IModelElement parent= * type.getParent(); if (parent != null) { int parentElementType= * parent.getElementType(); return (parentElementType != * IModelElement.SOURCE_MODULE); } } */ } return false; } }