| /******************************************************************************* |
| * Copyright (c) 2005, 2017 IBM Corporation and others. |
| * This program and the accompanying materials are made available under the |
| * terms of the Eclipse Public License v. 2.0 which is available at |
| * http://www.eclipse.org/legal/epl-2.0. |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| *******************************************************************************/ |
| package org.eclipse.dltk.tcl.internal.ui.navigation; |
| |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| |
| import org.eclipse.core.resources.IProject; |
| import org.eclipse.core.resources.IWorkspaceRoot; |
| import org.eclipse.core.resources.ResourcesPlugin; |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.runtime.IAdaptable; |
| import org.eclipse.core.runtime.IProgressMonitor; |
| import org.eclipse.core.runtime.IStatus; |
| import org.eclipse.core.runtime.Status; |
| import org.eclipse.core.runtime.jobs.Job; |
| import org.eclipse.dltk.core.DLTKCore; |
| import org.eclipse.dltk.core.DLTKLanguageManager; |
| import org.eclipse.dltk.core.ElementChangedEvent; |
| import org.eclipse.dltk.core.IDLTKLanguageToolkit; |
| import org.eclipse.dltk.core.IElementChangedListener; |
| import org.eclipse.dltk.core.IExternalSourceModule; |
| import org.eclipse.dltk.core.IModelElement; |
| import org.eclipse.dltk.core.IModelElementDelta; |
| import org.eclipse.dltk.core.IParent; |
| import org.eclipse.dltk.core.IScriptProject; |
| import org.eclipse.dltk.core.ISourceModule; |
| import org.eclipse.dltk.core.ModelException; |
| import org.eclipse.dltk.internal.core.ExternalScriptFolder; |
| import org.eclipse.dltk.internal.ui.editor.EditorUtility; |
| import org.eclipse.dltk.internal.ui.filters.IFilterElementNameProvider; |
| import org.eclipse.dltk.tcl.core.TclLanguageToolkit; |
| import org.eclipse.dltk.tcl.core.TclNature; |
| import org.eclipse.dltk.tcl.internal.ui.TclUI; |
| import org.eclipse.dltk.ui.DLTKPluginImages; |
| import org.eclipse.dltk.ui.ModelElementSorter; |
| import org.eclipse.dltk.ui.actions.CustomFiltersActionGroup; |
| import org.eclipse.dltk.ui.viewsupport.ScriptUILabelProvider; |
| import org.eclipse.jface.action.Action; |
| import org.eclipse.jface.action.IMenuManager; |
| import org.eclipse.jface.action.IToolBarManager; |
| import org.eclipse.jface.action.Separator; |
| import org.eclipse.jface.preference.IPreferenceStore; |
| import org.eclipse.jface.viewers.ITreeContentProvider; |
| import org.eclipse.jface.viewers.ITreeSelection; |
| import org.eclipse.jface.viewers.TreeViewer; |
| import org.eclipse.jface.viewers.Viewer; |
| import org.eclipse.swt.SWT; |
| import org.eclipse.swt.custom.BusyIndicator; |
| import org.eclipse.swt.graphics.Image; |
| import org.eclipse.swt.layout.FillLayout; |
| import org.eclipse.swt.widgets.Composite; |
| import org.eclipse.swt.widgets.Display; |
| import org.eclipse.ui.IActionBars; |
| import org.eclipse.ui.IEditorPart; |
| import org.eclipse.ui.IViewSite; |
| import org.eclipse.ui.PartInitException; |
| import org.eclipse.ui.part.ViewPart; |
| |
| public abstract class ElementsView extends ViewPart { |
| private ElementsTreeViewer treeViewer; |
| protected ElementsContentProvider provider; |
| |
| protected abstract String getPreferencesId(); |
| |
| private CustomFiltersActionGroup fCustomFiltersActionGroup; |
| private boolean lookIntoExternal; |
| |
| protected IPreferenceStore getStore() { |
| return TclUI.getDefault().getPreferenceStore(); |
| } |
| |
| private class ElementsTreeViewer extends TreeViewer |
| implements IFilterElementNameProvider { |
| public ElementsTreeViewer(Composite container, int border) { |
| super(container, border); |
| } |
| |
| @Override |
| public String getElementName(Object element) { |
| return labelProvider.getText(element); |
| } |
| } |
| |
| class ToggleExternalAction extends Action { |
| public ToggleExternalAction() { |
| super(); |
| setText("Show external elements"); |
| // DLTKPluginImages.setLocalImageDescriptors(this, |
| // "package_obj.gif"); //$NON-NLS-1$ |
| setImageDescriptor(DLTKPluginImages |
| .getDescriptor(DLTKPluginImages.IMG_OBJS_PACKAGE)); |
| boolean checked = getStore().getBoolean( |
| getPreferencesId() + "ToggleExternalAction.isChecked"); //$NON-NLS-1$ |
| valueChanged(checked, false); |
| } |
| |
| @Override |
| public void run() { |
| valueChanged(isChecked(), true); |
| } |
| |
| private void valueChanged(final boolean on, boolean store) { |
| setChecked(on); |
| if (store) |
| getStore().setValue( |
| getPreferencesId() + "ToggleExternalAction.isChecked", //$NON-NLS-1$ |
| on); |
| BusyIndicator.showWhile(treeViewer.getControl().getDisplay(), |
| () -> { |
| lookIntoExternal = on; |
| provider.clear(); |
| runAsync(); |
| }); |
| } |
| } |
| |
| class LexicalSortingAction extends Action { |
| private ModelElementSorter fSorter = new ModelElementSorter() { |
| @Override |
| protected String getElementName(Object element) { |
| return labelProvider.getText(element); |
| } |
| |
| @Override |
| public int compare(Viewer viewer, Object e1, Object e2) { |
| Object o1 = e1; |
| Object o2 = e2; |
| if (e1 != null && e1 instanceof ElementList) { |
| o1 = ((ElementList) e1).getFirstElement(); |
| } |
| if (e2 != null && e2 instanceof ElementList) { |
| o2 = ((ElementList) e2).getFirstElement(); |
| } |
| return super.compare(viewer, o1, o2); |
| } |
| }; |
| |
| public LexicalSortingAction() { |
| super(); |
| if (DLTKCore.DEBUG) { |
| System.err.println( |
| "LexicalSortingAction: Need to set correct info here."); |
| } |
| // PlatformUI.getWorkbench().getHelpSystem().setHelp(this, |
| // IDLTKHelpContextIds.LEXICAL_SORTING_OUTLINE_ACTION); |
| setText("Alphabetical Sort"); |
| DLTKPluginImages.setLocalImageDescriptors(this, |
| "alphab_sort_co.gif"); //$NON-NLS-1$ |
| boolean checked = getStore().getBoolean( |
| getPreferencesId() + "LexicalSortingAction.isChecked"); //$NON-NLS-1$ |
| valueChanged(checked, false); |
| } |
| |
| @Override |
| public void run() { |
| valueChanged(isChecked(), true); |
| } |
| |
| private void valueChanged(final boolean on, boolean store) { |
| setChecked(on); |
| BusyIndicator.showWhile(treeViewer.getControl().getDisplay(), |
| () -> treeViewer.setComparator(on ? fSorter : null)); |
| if (store) |
| getStore().setValue( |
| getPreferencesId() + "LexicalSortingAction.isChecked", //$NON-NLS-1$ |
| on); |
| } |
| } |
| |
| protected ElementsViewLabelProvider labelProvider; |
| |
| class ElementList implements IAdaptable { |
| private String name; |
| private List childs; |
| |
| ElementList(String name, List childs) { |
| this.name = name; |
| this.childs = childs; |
| } |
| |
| Object getFirstElement() { |
| if (childs != null && childs.size() > 0) { |
| return childs.get(0); |
| } |
| return null; |
| } |
| |
| @Override |
| public String toString() { |
| return this.name; |
| } |
| |
| @Override |
| public int hashCode() { |
| final int PRIME = 31; |
| int result = 1; |
| result = PRIME * result + ((name == null) ? 0 : name.hashCode()); |
| return result; |
| } |
| |
| @Override |
| public boolean equals(Object obj) { |
| if (this == obj) |
| return true; |
| if (obj == null) |
| return false; |
| if (getClass() != obj.getClass()) |
| return false; |
| final ElementList other = (ElementList) obj; |
| if (name == null) { |
| if (other.name != null) |
| return false; |
| } else if (!name.equals(other.name)) |
| return false; |
| return true; |
| } |
| |
| public List getElements() { |
| return this.childs; |
| } |
| |
| @SuppressWarnings("unchecked") |
| @Override |
| public <T> T getAdapter(Class<T> adapter) { |
| if (adapter == IModelElement.class) { |
| return (T) getFirstElement(); |
| } |
| if (adapter == List.class) { |
| return (T) childs; |
| } |
| return null; |
| } |
| } |
| |
| private class ElementsContentProvider implements ITreeContentProvider { |
| private Object[] NO_ELEMENT = new Object[0]; |
| private Map elements = new HashMap(); |
| |
| public ElementsContentProvider() { |
| DLTKCore.addElementChangedListener(new IElementChangedListener() { |
| @Override |
| public void elementChanged(ElementChangedEvent event) { |
| IModelElementDelta delta = event.getDelta(); |
| processChildren(delta); |
| asyncRefresh(); |
| } |
| |
| private void processChildren(IModelElementDelta delta) { |
| IModelElement element = delta.getElement(); |
| |
| if (delta.getKind() == IModelElementDelta.ADDED) { |
| addElementsJob(element); |
| } |
| if (delta.getKind() == IModelElementDelta.REMOVED) { |
| removeTypesJob(element); |
| } |
| if ((delta.getFlags() |
| & IModelElementDelta.F_ADDED_TO_BUILDPATH) != 0) { |
| addElementsJob(element); |
| } |
| if ((delta.getFlags() |
| & IModelElementDelta.F_REMOVED_FROM_BUILDPATH) != 0) { |
| synchronized (elements) { |
| elements.clear(); |
| } |
| runAsync(); |
| } |
| if ((delta.getFlags() |
| & IModelElementDelta.F_CHILDREN) != 0) { |
| IModelElementDelta[] affectedChildren = delta |
| .getAffectedChildren(); |
| for (int i = 0; i < affectedChildren.length; i++) { |
| IModelElementDelta child = affectedChildren[i]; |
| processChildren(child); |
| } |
| } |
| } |
| }); |
| } |
| |
| @Override |
| public synchronized Object[] getChildren(Object parentElement) { |
| if (parentElement instanceof IWorkspaceRoot) { |
| List result = new ArrayList(); |
| Set keys = elements.keySet(); |
| Iterator i = keys.iterator(); |
| while (i.hasNext()) { |
| Object e = i.next(); |
| Object o = elements.get(e); |
| if (o instanceof IModelElement) { |
| if (!result.contains(o)) { |
| result.add(o); |
| } |
| } else if (o instanceof List) { |
| ElementList el = new ElementList((String) e, (List) o); |
| if (!result.contains(o)) { |
| result.add(el); |
| } |
| } |
| } |
| return result.toArray(); |
| } |
| return NO_ELEMENT; |
| } |
| |
| @Override |
| public Object getParent(Object element) { |
| if (element instanceof IModelElement) { |
| return ResourcesPlugin.getWorkspace().getRoot(); |
| } |
| return NO_ELEMENT; |
| } |
| |
| @Override |
| public boolean hasChildren(Object element) { |
| if (element instanceof IWorkspaceRoot) { |
| return true; |
| } |
| return false; |
| } |
| |
| @Override |
| public Object[] getElements(Object inputElement) { |
| return getChildren(inputElement); |
| } |
| |
| @Override |
| public void dispose() { |
| } |
| |
| @Override |
| public void inputChanged(Viewer viewer, Object oldInput, |
| Object newInput) { |
| } |
| |
| synchronized public void addElement(IModelElement element) { |
| IDLTKLanguageToolkit languageToolkit = DLTKLanguageManager |
| .getLanguageToolkit(element); |
| if (!languageToolkit.getNatureId() |
| .equals(TclLanguageToolkit.getDefault().getNatureId())) { |
| return; |
| } |
| |
| String name = labelProvider.getText(element); |
| if (elements.containsKey(name)) { |
| Object o = elements.get(name); |
| if (o instanceof IModelElement) { |
| List els = new ArrayList(); |
| els.add(o); |
| if (!els.contains(element)) { |
| els.add(element); |
| } |
| elements.put(name, els); |
| } else if (o instanceof List) { |
| List els = (List) o; |
| if (!els.contains(o)) { |
| els.add(element); |
| } |
| } |
| } else { |
| elements.put(name, element); |
| } |
| } |
| |
| synchronized public void clear() { |
| elements.clear(); |
| } |
| |
| synchronized public void removeElement(IModelElement element) { |
| String name = labelProvider.getText(element); |
| if (elements.containsKey(name)) { |
| Object o = elements.get(name); |
| if (o instanceof IModelElement && o.equals(element)) { |
| elements.remove(name); |
| } else if (o instanceof List) { |
| List els = (List) o; |
| els.remove(element); |
| if (els.size() == 0) { |
| elements.remove(name); |
| } |
| } |
| } |
| } |
| } |
| |
| public abstract String getElementName(Object o); |
| |
| public class ElementsViewLabelProvider extends ScriptUILabelProvider { |
| @Override |
| public Image getImage(Object element) { |
| if (element instanceof ElementList) { |
| return getImage(((ElementList) element).getFirstElement()); |
| } |
| /* |
| * if( element instanceof IType ) { IType type = (IType)element; int |
| * flags = 0; try { flags = type.getFlags(); } catch (ModelException |
| * e) { // TODO Auto-generated catch block e.printStackTrace(); } if |
| * ((flags & Modifiers.AccNameSpace) == 0) return |
| * DLTKPluginImages.DESC_OBJS_CLASS.createImage(); return |
| * DLTKPluginImages.DESC_OBJS_NAMESPACE.createImage(); } |
| */ |
| return super.getImage(element); |
| } |
| |
| public String getOriginalText(Object element) { |
| return super.getText(element); |
| } |
| |
| @Override |
| public String getText(Object element) { |
| String text = getElementName(element); |
| if (text != null) { |
| return text; |
| } |
| if (element instanceof ElementList) { |
| return ((ElementList) element).toString(); |
| } |
| return super.getText(element); |
| } |
| } |
| |
| public abstract boolean isElement(IModelElement e); |
| |
| public abstract boolean needProcessChildren(IModelElement e); |
| |
| private void addElements(final IModelElement element, |
| IProgressMonitor monitor) { |
| if (monitor.isCanceled()) |
| return; |
| monitor.worked(1); |
| if (isElement(element)) { |
| provider.addElement(element); |
| |
| } |
| if (element instanceof ISourceModule) |
| asyncRefresh(); |
| boolean bad = !lookIntoExternal |
| && (element instanceof ExternalScriptFolder |
| || element instanceof IExternalSourceModule); |
| if (element instanceof IParent && !bad) { |
| IModelElement[] children = null; |
| try { |
| children = ((IParent) element).getChildren(); |
| } catch (ModelException e) { |
| if (DLTKCore.DEBUG) { |
| e.printStackTrace(); |
| } |
| return; |
| } |
| if (children != null) { |
| for (int j = 0; j < children.length; ++j) { |
| addElements(children[j], monitor); |
| } |
| } |
| } |
| // asyncRefresh(); |
| monitor.done(); |
| } |
| |
| public abstract String getJobTitle(); |
| |
| private void addElementsJob(final IModelElement element) { |
| Job job = new Job(getTitle()) { |
| @Override |
| protected IStatus run(IProgressMonitor monitor) { |
| addElements(element, monitor); |
| monitor.done(); |
| return Status.OK_STATUS; |
| } |
| }; |
| job.schedule(); |
| } |
| |
| private void removeElements(IModelElement element) { |
| IDLTKLanguageToolkit languageToolkit = DLTKLanguageManager |
| .getLanguageToolkit(element); |
| if (languageToolkit != null && !languageToolkit.getNatureId() |
| .equals(TclLanguageToolkit.getDefault().getNatureId())) { |
| return; |
| } |
| if (isElement(element)) { |
| provider.removeElement(element); |
| asyncRefresh(); |
| } |
| if (element instanceof IParent) { |
| IModelElement[] children = null; |
| try { |
| children = ((IParent) element).getChildren(); |
| } catch (ModelException e) { |
| if (DLTKCore.DEBUG) { |
| e.printStackTrace(); |
| } |
| return; |
| } |
| if (children != null) { |
| for (int j = 0; j < children.length; ++j) { |
| removeElements(children[j]); |
| } |
| } |
| } |
| } |
| |
| private void removeTypesJob(final IModelElement element) { |
| Job job = new Job(getTitle()) { |
| @Override |
| protected IStatus run(IProgressMonitor monitor) { |
| removeElements(element); |
| monitor.done(); |
| return Status.OK_STATUS; |
| } |
| }; |
| job.schedule(); |
| } |
| |
| private void asyncRefresh() { |
| Display.getDefault().asyncExec(() -> { |
| if (treeViewer != null && !treeViewer.getTree().isDisposed()) { |
| treeViewer.refresh(true); |
| } |
| }); |
| } |
| |
| private Job currentJob; |
| |
| protected void runAsync() { |
| if (currentJob != null && currentJob.getResult() == null) { |
| currentJob.cancel(); |
| provider.clear(); |
| currentJob = null; |
| } |
| Job job = new Job(getJobTitle()) { |
| @Override |
| protected IStatus run(IProgressMonitor monitor) { |
| IProject[] projects = ResourcesPlugin.getWorkspace().getRoot() |
| .getProjects(); |
| // monitor.beginTask(getTitle(), projects.length); |
| for (int i = 0; i < projects.length; ++i) { |
| if (monitor.isCanceled()) |
| return Status.OK_STATUS; |
| try { |
| if (projects[i].isAccessible() |
| && projects[i].hasNature(TclNature.NATURE_ID)) { |
| IScriptProject project = DLTKCore |
| .create(projects[i]); |
| if (project != null) { |
| addElements(project, monitor); |
| } |
| } |
| } catch (CoreException e) { |
| e.printStackTrace(); |
| } |
| monitor.worked(1); |
| } |
| asyncRefresh(); |
| monitor.done(); |
| return Status.OK_STATUS; |
| } |
| }; |
| job.schedule(); |
| currentJob = job; |
| } |
| |
| @Override |
| public void createPartControl(Composite parent) { |
| Composite container = new Composite(parent, SWT.NONE); |
| container.setLayout(new FillLayout()); |
| treeViewer = new ElementsTreeViewer(container, SWT.BORDER); |
| provider = new ElementsContentProvider(); |
| labelProvider = new ElementsViewLabelProvider(); |
| treeViewer.setLabelProvider(labelProvider); |
| treeViewer.setContentProvider(provider); |
| treeViewer.setInput(ResourcesPlugin.getWorkspace().getRoot()); |
| treeViewer.addDoubleClickListener(event -> { |
| ITreeSelection selection = (ITreeSelection) (event.getSelection()); |
| Object source = selection.getFirstElement(); |
| if (source == null) |
| return; |
| try { |
| if (source instanceof IModelElement) { |
| IEditorPart editor1 = EditorUtility.openInEditor(source); |
| EditorUtility.revealInEditor(editor1, |
| (IModelElement) source); |
| } else if (source instanceof ElementList) { |
| ElementList el = (ElementList) source; |
| List elements = el.getElements(); |
| if (elements != null) { |
| for (int i = 0; i < elements.size(); ++i) { |
| Object element = elements.get(i); |
| if (element instanceof IModelElement) { |
| IEditorPart editor2 = EditorUtility |
| .openInEditor(element); |
| EditorUtility.revealInEditor(editor2, |
| (IModelElement) element); |
| } else |
| EditorUtility.openInEditor(element); |
| } |
| } |
| } |
| } catch (PartInitException e1) { |
| e1.printStackTrace(); |
| } catch (ModelException e2) { |
| e2.printStackTrace(); |
| } |
| }); |
| IViewSite site = (IViewSite) this.getSite(); |
| IActionBars bars = site.getActionBars(); |
| registerToolbarActions(bars); |
| runAsync(); |
| } |
| |
| private void registerToolbarActions(IActionBars actionBars) { |
| IToolBarManager toolBarManager = actionBars.getToolBarManager(); |
| if (toolBarManager != null) { |
| toolBarManager.add(new LexicalSortingAction()); |
| toolBarManager.add(new ToggleExternalAction()); |
| } |
| // registerSpecialToolbarActions(actionBars); |
| // Custom filter group |
| fCustomFiltersActionGroup = new CustomFiltersActionGroup(this, |
| treeViewer) { |
| }; |
| fCustomFiltersActionGroup.fillActionBars(actionBars); |
| /* |
| * actionBars.getMenuManager().add( new PackageFilterAction(this, |
| * treeViewer)); |
| */ |
| IMenuManager viewMenuManager = actionBars.getMenuManager(); |
| viewMenuManager.add(new Separator("EndFilterGroup")); //$NON-NLS-1$ |
| // fToggleLinkingAction= new ToggleLinkingAction(this); |
| // viewMenuManager.add(fToggleLinkingAction); |
| } |
| |
| @Override |
| public void dispose() { |
| if (fCustomFiltersActionGroup != null) { |
| fCustomFiltersActionGroup.dispose(); |
| fCustomFiltersActionGroup = null; |
| } |
| super.dispose(); |
| } |
| |
| @Override |
| public void setFocus() { |
| } |
| |
| public String getOriginalElementText(Object o) { |
| return labelProvider.getOriginalText(o); |
| } |
| } |