Bug 576106 - Add initial support for type hierarchy
diff --git a/org.eclipse.handly.examples.adapter.ui/icons/eview16/type_hierarchy.png b/org.eclipse.handly.examples.adapter.ui/icons/eview16/type_hierarchy.png
new file mode 100644
index 0000000..28231da
--- /dev/null
+++ b/org.eclipse.handly.examples.adapter.ui/icons/eview16/type_hierarchy.png
Binary files differ
diff --git a/org.eclipse.handly.examples.adapter.ui/icons/eview16/type_hierarchy@2x.png b/org.eclipse.handly.examples.adapter.ui/icons/eview16/type_hierarchy@2x.png
new file mode 100644
index 0000000..8b2a9d5
--- /dev/null
+++ b/org.eclipse.handly.examples.adapter.ui/icons/eview16/type_hierarchy@2x.png
Binary files differ
diff --git a/org.eclipse.handly.examples.adapter.ui/plugin.xml b/org.eclipse.handly.examples.adapter.ui/plugin.xml
index af3e7ed..2db6b2d 100644
--- a/org.eclipse.handly.examples.adapter.ui/plugin.xml
+++ b/org.eclipse.handly.examples.adapter.ui/plugin.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<?eclipse version="3.4"?>
<!--
- Copyright (c) 2015, 2018 1C-Soft LLC.
+ Copyright (c) 2015, 2021 1C-Soft LLC.
This program and the accompanying materials are made available under
the terms of the Eclipse Public License 2.0 which is available at
@@ -34,11 +34,20 @@
id="org.eclipse.handly.examples.adapter.ui.JavaCallHierarchyView"
category="org.eclipse.handly.examples"
class="org.eclipse.handly.internal.examples.adapter.ui.callhierarchy.JavaCallHierarchyView"
- name="Method Calls"
+ name="Call Hierarchy"
icon="icons/eview16/call_hierarchy.png"
allowMultiple="true"
restorable="true">
</view>
+ <view
+ id="org.eclipse.handly.examples.adapter.ui.JavaTypeHierarchyView"
+ category="org.eclipse.handly.examples"
+ class="org.eclipse.handly.internal.examples.adapter.ui.typehierarchy.JavaTypeHierarchyView"
+ name="Type Hierarchy"
+ icon="icons/eview16/type_hierarchy.png"
+ allowMultiple="false"
+ restorable="true">
+ </view>
</extension>
<extension
point="org.eclipse.search.searchResultViewPages">
diff --git a/org.eclipse.handly.examples.adapter.ui/src/org/eclipse/handly/internal/examples/adapter/ui/JavaOutlinePage.java b/org.eclipse.handly.examples.adapter.ui/src/org/eclipse/handly/internal/examples/adapter/ui/JavaOutlinePage.java
index 64bc3bc..4b6ce82 100644
--- a/org.eclipse.handly.examples.adapter.ui/src/org/eclipse/handly/internal/examples/adapter/ui/JavaOutlinePage.java
+++ b/org.eclipse.handly.examples.adapter.ui/src/org/eclipse/handly/internal/examples/adapter/ui/JavaOutlinePage.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2015, 2018 1C-Soft LLC.
+ * Copyright (c) 2015, 2021 1C-Soft LLC.
*
* This program and the accompanying materials are made available under
* the terms of the Eclipse Public License 2.0 which is available at
@@ -15,6 +15,7 @@
import org.eclipse.handly.examples.adapter.JavaModelAdapter;
import org.eclipse.handly.internal.examples.adapter.ui.callhierarchy.OpenCallHierarchyAction;
import org.eclipse.handly.internal.examples.adapter.ui.search.FindReferencesAction;
+import org.eclipse.handly.internal.examples.adapter.ui.typehierarchy.OpenTypeHierarchyAction;
import org.eclipse.handly.model.IElementChangeListener;
import org.eclipse.handly.model.adapter.DefaultContentAdapter;
import org.eclipse.handly.model.adapter.IContentAdapter;
@@ -49,6 +50,8 @@
private static final String GROUP_OPEN = "group.open"; //$NON-NLS-1$
private static final String GROUP_SEARCH = "group.search"; //$NON-NLS-1$
+ private final OpenTypeHierarchyAction openTypeHierarchyAction =
+ new OpenTypeHierarchyAction();
private final OpenCallHierarchyAction openCallHierarchyAction =
new OpenCallHierarchyAction();
private final FindReferencesAction findReferencesAction =
@@ -118,6 +121,10 @@
IStructuredSelection selection =
(IStructuredSelection)getSelection();
+ openTypeHierarchyAction.selectionChanged(selection);
+ if (openTypeHierarchyAction.isEnabled())
+ manager.appendToGroup(GROUP_OPEN, openTypeHierarchyAction);
+
openCallHierarchyAction.selectionChanged(selection);
if (openCallHierarchyAction.isEnabled())
manager.appendToGroup(GROUP_OPEN, openCallHierarchyAction);
diff --git a/org.eclipse.handly.examples.adapter.ui/src/org/eclipse/handly/internal/examples/adapter/ui/callhierarchy/OpenCallHierarchyAction.java b/org.eclipse.handly.examples.adapter.ui/src/org/eclipse/handly/internal/examples/adapter/ui/callhierarchy/OpenCallHierarchyAction.java
index c1c486a..06a3be3 100644
--- a/org.eclipse.handly.examples.adapter.ui/src/org/eclipse/handly/internal/examples/adapter/ui/callhierarchy/OpenCallHierarchyAction.java
+++ b/org.eclipse.handly.examples.adapter.ui/src/org/eclipse/handly/internal/examples/adapter/ui/callhierarchy/OpenCallHierarchyAction.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2018, 2019 1C-Soft LLC.
+ * Copyright (c) 2018, 2021 1C-Soft LLC.
*
* This program and the accompanying materials are made available under
* the terms of the Eclipse Public License 2.0 which is available at
@@ -36,7 +36,7 @@
*/
public OpenCallHierarchyAction()
{
- super("Show Calls");
+ super("Open Call Hierarchy");
}
@Override
diff --git a/org.eclipse.handly.examples.adapter.ui/src/org/eclipse/handly/internal/examples/adapter/ui/typehierarchy/JavaTypeHierarchyView.java b/org.eclipse.handly.examples.adapter.ui/src/org/eclipse/handly/internal/examples/adapter/ui/typehierarchy/JavaTypeHierarchyView.java
new file mode 100644
index 0000000..51b7ee6
--- /dev/null
+++ b/org.eclipse.handly.examples.adapter.ui/src/org/eclipse/handly/internal/examples/adapter/ui/typehierarchy/JavaTypeHierarchyView.java
@@ -0,0 +1,455 @@
+/*******************************************************************************
+ * Copyright (c) 2021 1C-Soft LLC.
+ *
+ * This program and the accompanying materials are made available under
+ * the terms of the Eclipse Public License 2.0 which is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * Vladimir Piskarev (1C) - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.handly.internal.examples.adapter.ui.typehierarchy;
+
+import static org.eclipse.jdt.ui.JavaElementLabels.APPEND_ROOT_PATH;
+import static org.eclipse.jdt.ui.JavaElementLabels.COLORIZE;
+import static org.eclipse.jdt.ui.JavaElementLabels.DEFAULT_QUALIFIED;
+import static org.eclipse.jdt.ui.JavaElementLabels.M_APP_RETURNTYPE;
+import static org.eclipse.jdt.ui.JavaElementLabels.M_APP_TYPE_PARAMETERS;
+import static org.eclipse.jdt.ui.JavaElementLabels.M_PARAMETER_NAMES;
+import static org.eclipse.jdt.ui.JavaElementLabels.M_PARAMETER_TYPES;
+import static org.eclipse.jdt.ui.JavaElementLabels.P_COMPRESSED;
+import static org.eclipse.jdt.ui.JavaElementLabels.T_POST_QUALIFIED;
+import static org.eclipse.jdt.ui.JavaElementLabels.T_TYPE_PARAMETERS;
+
+import java.text.MessageFormat;
+import java.util.EnumSet;
+import java.util.Objects;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.handly.context.IContext;
+import org.eclipse.handly.internal.examples.adapter.ui.Activator;
+import org.eclipse.handly.internal.examples.adapter.ui.JavaEditorUtility;
+import org.eclipse.handly.ui.EditorOpener;
+import org.eclipse.handly.ui.typehierarchy.TypeHierarchyKind;
+import org.eclipse.handly.ui.typehierarchy.TypeHierarchyViewPart;
+import org.eclipse.handly.ui.viewer.DeferredTreeContentProvider;
+import org.eclipse.jdt.core.IJavaElement;
+import org.eclipse.jdt.core.IMethod;
+import org.eclipse.jdt.core.IType;
+import org.eclipse.jdt.core.ITypeHierarchy;
+import org.eclipse.jdt.core.JavaModelException;
+import org.eclipse.jdt.ui.JavaElementLabelProvider;
+import org.eclipse.jdt.ui.JavaElementLabels;
+import org.eclipse.jface.action.IStatusLineManager;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.jface.viewers.AbstractTreeViewer;
+import org.eclipse.jface.viewers.ArrayContentProvider;
+import org.eclipse.jface.viewers.DelegatingStyledCellLabelProvider;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.StructuredSelection;
+import org.eclipse.jface.viewers.StyledString;
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.jface.viewers.TreeViewer;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.ui.IWorkbenchPartSite;
+import org.eclipse.ui.PartInitException;
+import org.eclipse.ui.progress.IElementCollector;
+
+/**
+ * Java-specific type hierarchy view.
+ */
+public class JavaTypeHierarchyView
+ extends TypeHierarchyViewPart
+{
+ /**
+ * The view ID.
+ */
+ public static final String ID = Activator.PLUGIN_ID
+ + ".JavaTypeHierarchyView"; //$NON-NLS-1$
+
+ private final TypeHierarchyHolder hierarchyHolder =
+ new TypeHierarchyHolder();
+ private TableViewer methodViewer;
+
+ /**
+ * Creates a new <code>JavaTypeHierarchyView</code>.
+ */
+ public JavaTypeHierarchyView()
+ {
+ super(EnumSet.of(TypeHierarchyKind.SUPERTYPES,
+ TypeHierarchyKind.SUBTYPES));
+ }
+
+ @Override
+ public void createPartControl(Composite parent)
+ {
+ super.createPartControl(parent);
+
+ methodViewer = new TableViewer(getSashForm(), SWT.MULTI);
+ methodViewer.setContentProvider(new ArrayContentProvider());
+ methodViewer.setLabelProvider(new DelegatingStyledCellLabelProvider(
+ new MethodLabelProvider()));
+ methodViewer.addSelectionChangedListener(e -> methodSelectionChanged(
+ e.getSelection()));
+
+ new OpenEditorHelper(methodViewer);
+ }
+
+ @Override
+ public boolean supportsLayoutMode(int layoutMode)
+ {
+ return layoutMode == SWT.VERTICAL || layoutMode == SWT.HORIZONTAL
+ || layoutMode == (SWT.VERTICAL | SWT.HORIZONTAL)
+ || layoutMode == SWT.NONE;
+ }
+
+ @Override
+ public String getTitleToolTip()
+ {
+ return getPartName() + " (Handly Adapter Example)";
+ }
+
+ @Override
+ public void setInputElements(Object[] elements)
+ {
+ if (elements.length > 1)
+ throw new IllegalArgumentException();
+
+ super.setInputElements(elements);
+ }
+
+ @Override
+ protected void onInputElementsChanged(Object[] oldInputElements,
+ Object[] newInputElements)
+ {
+ IType inputType = newInputElements.length > 0
+ ? (IType)newInputElements[0] : null;
+ hierarchyHolder.setInputType(inputType);
+
+ super.onInputElementsChanged(oldInputElements, newInputElements);
+ }
+
+ @Override
+ protected boolean isPossibleInputElement(Object element)
+ {
+ return element instanceof IType;
+ }
+
+ @Override
+ protected HistoryEntry createHistoryEntry(Object[] inputElements)
+ {
+ return new TypeHistoryEntry(inputElements);
+ }
+
+ @Override
+ protected EditorOpener createEditorOpener()
+ {
+ return new EditorOpener(getSite().getPage(),
+ JavaEditorUtility.INSTANCE);
+ }
+
+ @Override
+ protected String computeContentDescription()
+ {
+ Object[] elements = getInputElements();
+ if (elements.length == 0)
+ return ""; //$NON-NLS-1$
+ return MessageFormat.format("''{0}''", JavaElementLabels.getTextLabel(
+ elements[0], T_POST_QUALIFIED | T_TYPE_PARAMETERS | P_COMPRESSED));
+ }
+
+ @Override
+ protected void configureHierarchyViewer(TreeViewer viewer,
+ TypeHierarchyKind kind)
+ {
+ viewer.setUseHashlookup(true);
+ viewer.setAutoExpandLevel(2);
+ viewer.setContentProvider(new TypeHierarchyContentProvider(viewer, kind,
+ hierarchyHolder, getSite()));
+ viewer.setLabelProvider(new JavaElementLabelProvider());
+ }
+
+ @Override
+ protected void setHierarchyViewerInput(TreeViewer viewer,
+ TypeHierarchyKind kind)
+ {
+ super.setHierarchyViewerInput(viewer, kind);
+
+ IType inputType = hierarchyHolder.getInputType();
+ if (inputType != null)
+ viewer.setSelection(new StructuredSelection(inputType), true);
+ }
+
+ @Override
+ protected void refresh(IContext context)
+ {
+ hierarchyHolder.setInputType(hierarchyHolder.getInputType()); // reset type hierarchy
+
+ super.refresh(context);
+ }
+
+ @Override
+ protected void updateStatusLine(IStatusLineManager manager,
+ IStructuredSelection selection)
+ {
+ super.updateStatusLine(manager, selection);
+
+ if (selection.size() == 1)
+ {
+ Object element = selection.getFirstElement();
+ if (element instanceof IJavaElement)
+ {
+ manager.setMessage(JavaElementLabels.getTextLabel(element,
+ DEFAULT_QUALIFIED | APPEND_ROOT_PATH | M_PARAMETER_TYPES
+ | M_PARAMETER_NAMES | M_APP_RETURNTYPE
+ | T_TYPE_PARAMETERS));
+
+ if (!((IJavaElement)element).exists())
+ {
+ manager.setErrorMessage(MessageFormat.format(
+ "''{0}'' no longer exists. Try to refresh the view",
+ JavaElementLabels.getTextLabel(element, 0)));
+ }
+ }
+ }
+ }
+
+ @Override
+ protected void onHierarchySelectionChanged(ISelection selection,
+ TypeHierarchyKind kind)
+ {
+ super.onHierarchySelectionChanged(selection, kind);
+
+ Object element = getSelectedElement(selection);
+
+ IMethod[] methods = null;
+ if (element instanceof IType)
+ {
+ try
+ {
+ methods = ((IType)element).getMethods();
+ }
+ catch (JavaModelException e)
+ {
+ if (!e.isDoesNotExist())
+ Activator.logError(e);
+ }
+ }
+
+ methodViewer.setInput(methods);
+
+ if (methods != null && methods.length > 0)
+ methodViewer.setSelection(new StructuredSelection(methods[0]),
+ true);
+ }
+
+ private void methodSelectionChanged(ISelection selection)
+ {
+ Object element = getSelectedElement(selection);
+ if (element != null)
+ {
+ if (methodViewer.getControl().isFocusControl())
+ {
+ updateStatusLine(
+ getViewSite().getActionBars().getStatusLineManager(),
+ methodViewer.getStructuredSelection());
+
+ try
+ {
+ revealInEditor(element, false, false);
+ }
+ catch (PartInitException e)
+ {
+ // cannot happen: may not open a new editor
+ }
+ }
+ }
+ }
+
+ private static Object getSelectedElement(ISelection selection)
+ {
+ if (selection instanceof IStructuredSelection)
+ {
+ IStructuredSelection ss = (IStructuredSelection)selection;
+ if (ss.size() == 1)
+ return ss.getFirstElement();
+ }
+ return null;
+ }
+
+ private static class TypeHierarchyHolder
+ {
+ private IType inputType;
+ private ITypeHierarchy typeHierarchy;
+
+ void setInputType(IType inputType)
+ {
+ this.inputType = inputType;
+ this.typeHierarchy = null;
+ }
+
+ IType getInputType()
+ {
+ return inputType;
+ }
+
+ ITypeHierarchy getTypeHierarchy()
+ {
+ return typeHierarchy;
+ }
+
+ ITypeHierarchy getOrCreateTypeHierarchy(IProgressMonitor monitor)
+ throws JavaModelException
+ {
+ if (inputType != null && typeHierarchy == null)
+ typeHierarchy = inputType.newTypeHierarchy(monitor);
+ return typeHierarchy;
+ }
+ }
+
+ private static class TypeHierarchyContentProvider
+ extends DeferredTreeContentProvider
+ {
+ private final TypeHierarchyKind hierarchyKind;
+ private final TypeHierarchyHolder hierarchyHolder;
+
+ TypeHierarchyContentProvider(AbstractTreeViewer viewer,
+ TypeHierarchyKind hierarchyKind,
+ TypeHierarchyHolder hierarchyHolder, IWorkbenchPartSite site)
+ {
+ super(viewer, site);
+ this.hierarchyKind = Objects.requireNonNull(hierarchyKind);
+ this.hierarchyHolder = Objects.requireNonNull(hierarchyHolder);
+ }
+
+ @Override
+ public Object[] getElements(Object inputElement)
+ {
+ IType inputType = hierarchyHolder.getInputType();
+ if (inputType != null)
+ return new Object[] { inputType };
+ return new Object[0];
+ }
+
+ @Override
+ public Object[] getChildren(Object parentElement)
+ {
+ if (!(parentElement instanceof IType))
+ return new Object[0];
+ ITypeHierarchy typeHierarchy = hierarchyHolder.getTypeHierarchy();
+ if (typeHierarchy != null)
+ return getChildren(typeHierarchy, parentElement);
+ return getDeferredTreeContentManager().getChildren(parentElement);
+ }
+
+ @Override
+ public Object getParent(Object element)
+ {
+ return null;
+ }
+
+ @Override
+ public boolean hasChildren(Object element)
+ {
+ if (!(element instanceof IType))
+ return false;
+ ITypeHierarchy typeHierarchy = hierarchyHolder.getTypeHierarchy();
+ if (typeHierarchy != null)
+ return getChildren(typeHierarchy, element).length > 0;
+ return true;
+ }
+
+ @Override
+ protected void fetchDeferredChildren(Object parentElement,
+ IElementCollector collector, IProgressMonitor monitor)
+ {
+ ITypeHierarchy typeHierarchy = null;
+ try
+ {
+ typeHierarchy = hierarchyHolder.getOrCreateTypeHierarchy(
+ monitor);
+ }
+ catch (JavaModelException e)
+ {
+ Activator.logError(e);
+ }
+ if (typeHierarchy != null)
+ {
+ collector.add(getChildren(typeHierarchy, parentElement), null);
+ }
+ collector.done();
+ }
+
+ private Object[] getChildren(ITypeHierarchy typeHierarchy,
+ Object parentElement)
+ {
+ IType type = (IType)parentElement;
+ return (hierarchyKind == TypeHierarchyKind.SUBTYPES)
+ ? typeHierarchy.getSubtypes(type) : typeHierarchy.getSupertypes(
+ type);
+ }
+ }
+
+ private static class MethodLabelProvider
+ extends JavaElementLabelProvider
+ {
+ private static final long M_FLAGS = M_PARAMETER_TYPES
+ | M_APP_TYPE_PARAMETERS | M_APP_RETURNTYPE;
+
+ @Override
+ public String getText(Object element)
+ {
+ if (element instanceof IMethod)
+ return JavaElementLabels.getTextLabel(element, M_FLAGS);
+
+ return super.getText(element);
+ }
+
+ @Override
+ public StyledString getStyledText(Object element)
+ {
+ if (element instanceof IMethod)
+ return JavaElementLabels.getStyledTextLabel(element, M_FLAGS
+ | COLORIZE);
+
+ return super.getStyledText(element);
+ }
+ }
+
+ private static class TypeHistoryEntry
+ extends HistoryEntry
+ {
+ TypeHistoryEntry(Object[] inputElements)
+ {
+ super(inputElements);
+ }
+
+ @SuppressWarnings("restriction")
+ @Override
+ public ImageDescriptor getImageDescriptor()
+ {
+ IJavaElement firstElement = (IJavaElement)getInputElements()[0];
+ org.eclipse.jdt.internal.ui.viewsupport.JavaElementImageProvider imageProvider =
+ new org.eclipse.jdt.internal.ui.viewsupport.JavaElementImageProvider();
+ try
+ {
+ return imageProvider.getBaseImageDescriptor(firstElement, 0);
+ }
+ finally
+ {
+ imageProvider.dispose();
+ }
+ }
+
+ @Override
+ protected String getElementLabel(Object element)
+ {
+ return JavaElementLabels.getTextLabel(element, T_POST_QUALIFIED
+ | T_TYPE_PARAMETERS | P_COMPRESSED);
+ }
+ }
+}
diff --git a/org.eclipse.handly.examples.adapter.ui/src/org/eclipse/handly/internal/examples/adapter/ui/typehierarchy/OpenTypeHierarchyAction.java b/org.eclipse.handly.examples.adapter.ui/src/org/eclipse/handly/internal/examples/adapter/ui/typehierarchy/OpenTypeHierarchyAction.java
new file mode 100644
index 0000000..ed99254
--- /dev/null
+++ b/org.eclipse.handly.examples.adapter.ui/src/org/eclipse/handly/internal/examples/adapter/ui/typehierarchy/OpenTypeHierarchyAction.java
@@ -0,0 +1,71 @@
+/*******************************************************************************
+ * Copyright (c) 2021 1C-Soft LLC.
+ *
+ * This program and the accompanying materials are made available under
+ * the terms of the Eclipse Public License 2.0 which is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * Vladimir Piskarev (1C) - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.handly.internal.examples.adapter.ui.typehierarchy;
+
+import org.eclipse.handly.internal.examples.adapter.ui.Activator;
+import org.eclipse.jdt.core.IType;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.ui.IViewPart;
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.PartInitException;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.actions.BaseSelectionListenerAction;
+
+/**
+ * An action that opens the {@link JavaTypeHierarchyView} for the current
+ * selection.
+ */
+public final class OpenTypeHierarchyAction
+ extends BaseSelectionListenerAction
+{
+ /**
+ * Creates a new <code>OpenTypeHierarchyAction</code>.
+ */
+ public OpenTypeHierarchyAction()
+ {
+ super("Open Type Hierarchy");
+ }
+
+ @Override
+ public void run()
+ {
+ IWorkbenchWindow window =
+ PlatformUI.getWorkbench().getActiveWorkbenchWindow();
+ if (window == null)
+ return;
+ IWorkbenchPage page = window.getActivePage();
+ if (page == null)
+ return;
+ try
+ {
+ IViewPart view = page.showView(JavaTypeHierarchyView.ID);
+ if (view instanceof JavaTypeHierarchyView)
+ ((JavaTypeHierarchyView)view).setInputElements(
+ getStructuredSelection().toArray());
+ }
+ catch (PartInitException e)
+ {
+ Activator.logError(e);
+ }
+ }
+
+ @Override
+ protected boolean updateSelection(IStructuredSelection selection)
+ {
+ if (selection.isEmpty() || selection.size() > 1)
+ return false;
+
+ return selection.getFirstElement() instanceof IType;
+ }
+}
diff --git a/org.eclipse.handly.ui/META-INF/MANIFEST.MF b/org.eclipse.handly.ui/META-INF/MANIFEST.MF
index c61e4de..4864ed4 100644
--- a/org.eclipse.handly.ui/META-INF/MANIFEST.MF
+++ b/org.eclipse.handly.ui/META-INF/MANIFEST.MF
@@ -31,5 +31,6 @@
org.eclipse.handly.ui.search,
org.eclipse.handly.ui.text.reconciler,
org.eclipse.handly.ui.texteditor,
+ org.eclipse.handly.ui.typehierarchy,
org.eclipse.handly.ui.viewer,
org.eclipse.handly.ui.workingset
diff --git a/org.eclipse.handly.ui/icons/elcl16/ch_automatic.png b/org.eclipse.handly.ui/icons/elcl16/layout_automatic.png
similarity index 100%
rename from org.eclipse.handly.ui/icons/elcl16/ch_automatic.png
rename to org.eclipse.handly.ui/icons/elcl16/layout_automatic.png
Binary files differ
diff --git a/org.eclipse.handly.ui/icons/elcl16/ch_automatic@2x.png b/org.eclipse.handly.ui/icons/elcl16/layout_automatic@2x.png
similarity index 100%
rename from org.eclipse.handly.ui/icons/elcl16/ch_automatic@2x.png
rename to org.eclipse.handly.ui/icons/elcl16/layout_automatic@2x.png
Binary files differ
diff --git a/org.eclipse.handly.ui/icons/elcl16/ch_horizontal.png b/org.eclipse.handly.ui/icons/elcl16/layout_horizontal.png
similarity index 100%
rename from org.eclipse.handly.ui/icons/elcl16/ch_horizontal.png
rename to org.eclipse.handly.ui/icons/elcl16/layout_horizontal.png
Binary files differ
diff --git a/org.eclipse.handly.ui/icons/elcl16/ch_horizontal@2x.png b/org.eclipse.handly.ui/icons/elcl16/layout_horizontal@2x.png
similarity index 100%
rename from org.eclipse.handly.ui/icons/elcl16/ch_horizontal@2x.png
rename to org.eclipse.handly.ui/icons/elcl16/layout_horizontal@2x.png
Binary files differ
diff --git a/org.eclipse.handly.ui/icons/elcl16/layout_single.png b/org.eclipse.handly.ui/icons/elcl16/layout_single.png
new file mode 100644
index 0000000..ff2d582
--- /dev/null
+++ b/org.eclipse.handly.ui/icons/elcl16/layout_single.png
Binary files differ
diff --git a/org.eclipse.handly.ui/icons/elcl16/layout_single@2x.png b/org.eclipse.handly.ui/icons/elcl16/layout_single@2x.png
new file mode 100644
index 0000000..20cc7ec
--- /dev/null
+++ b/org.eclipse.handly.ui/icons/elcl16/layout_single@2x.png
Binary files differ
diff --git a/org.eclipse.handly.ui/icons/elcl16/ch_vertical.png b/org.eclipse.handly.ui/icons/elcl16/layout_vertical.png
similarity index 100%
rename from org.eclipse.handly.ui/icons/elcl16/ch_vertical.png
rename to org.eclipse.handly.ui/icons/elcl16/layout_vertical.png
Binary files differ
diff --git a/org.eclipse.handly.ui/icons/elcl16/ch_vertical@2x.png b/org.eclipse.handly.ui/icons/elcl16/layout_vertical@2x.png
similarity index 100%
rename from org.eclipse.handly.ui/icons/elcl16/ch_vertical@2x.png
rename to org.eclipse.handly.ui/icons/elcl16/layout_vertical@2x.png
Binary files differ
diff --git a/org.eclipse.handly.ui/icons/elcl16/th_subtypes.png b/org.eclipse.handly.ui/icons/elcl16/th_subtypes.png
new file mode 100644
index 0000000..8cf8fd3
--- /dev/null
+++ b/org.eclipse.handly.ui/icons/elcl16/th_subtypes.png
Binary files differ
diff --git a/org.eclipse.handly.ui/icons/elcl16/th_subtypes@2x.png b/org.eclipse.handly.ui/icons/elcl16/th_subtypes@2x.png
new file mode 100644
index 0000000..10bb534
--- /dev/null
+++ b/org.eclipse.handly.ui/icons/elcl16/th_subtypes@2x.png
Binary files differ
diff --git a/org.eclipse.handly.ui/icons/elcl16/th_supertypes.png b/org.eclipse.handly.ui/icons/elcl16/th_supertypes.png
new file mode 100644
index 0000000..5a79120
--- /dev/null
+++ b/org.eclipse.handly.ui/icons/elcl16/th_supertypes.png
Binary files differ
diff --git a/org.eclipse.handly.ui/icons/elcl16/th_supertypes@2x.png b/org.eclipse.handly.ui/icons/elcl16/th_supertypes@2x.png
new file mode 100644
index 0000000..920e7f4
--- /dev/null
+++ b/org.eclipse.handly.ui/icons/elcl16/th_supertypes@2x.png
Binary files differ
diff --git a/org.eclipse.handly.ui/icons/elcl16/th_types.png b/org.eclipse.handly.ui/icons/elcl16/th_types.png
new file mode 100644
index 0000000..7ceec3f
--- /dev/null
+++ b/org.eclipse.handly.ui/icons/elcl16/th_types.png
Binary files differ
diff --git a/org.eclipse.handly.ui/icons/elcl16/th_types@2x.png b/org.eclipse.handly.ui/icons/elcl16/th_types@2x.png
new file mode 100644
index 0000000..b39603c
--- /dev/null
+++ b/org.eclipse.handly.ui/icons/elcl16/th_types@2x.png
Binary files differ
diff --git a/org.eclipse.handly.ui/src/org/eclipse/handly/internal/ui/Activator.java b/org.eclipse.handly.ui/src/org/eclipse/handly/internal/ui/Activator.java
index 15cbb1c..fe41316 100644
--- a/org.eclipse.handly.ui/src/org/eclipse/handly/internal/ui/Activator.java
+++ b/org.eclipse.handly.ui/src/org/eclipse/handly/internal/ui/Activator.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2014, 2020 1C-Soft LLC and others.
+ * Copyright (c) 2014, 2021 1C-Soft LLC and others.
*
* This program and the accompanying materials are made available under
* the terms of the Eclipse Public License 2.0 which is available at
@@ -40,26 +40,34 @@
public static final String IMG_DLCL_REFRESH = PLUGIN_ID + T_DLCL
+ "refresh.png"; //$NON-NLS-1$
- public static final String IMG_ELCL_CH_AUTOMATIC = PLUGIN_ID + T_ELCL
- + "ch_automatic.png"; //$NON-NLS-1$
public static final String IMG_ELCL_CH_CALLEES = PLUGIN_ID + T_ELCL
+ "ch_callees.png"; //$NON-NLS-1$
public static final String IMG_ELCL_CH_CALLERS = PLUGIN_ID + T_ELCL
+ "ch_callers.png"; //$NON-NLS-1$
- public static final String IMG_ELCL_CH_HORIZONTAL = PLUGIN_ID + T_ELCL
- + "ch_horizontal.png"; //$NON-NLS-1$
- public static final String IMG_ELCL_CH_VERTICAL = PLUGIN_ID + T_ELCL
- + "ch_vertical.png"; //$NON-NLS-1$
public static final String IMG_ELCL_EXPANDALL = PLUGIN_ID + T_ELCL
+ "expandall.png"; //$NON-NLS-1$
public static final String IMG_ELCL_HISTORY_LIST = PLUGIN_ID + T_ELCL
+ "history_list.png"; //$NON-NLS-1$
+ public static final String IMG_ELCL_LAYOUT_AUTOMATIC = PLUGIN_ID + T_ELCL
+ + "layout_automatic.png"; //$NON-NLS-1$
+ public static final String IMG_ELCL_LAYOUT_HORIZONTAL = PLUGIN_ID + T_ELCL
+ + "layout_horizontal.png"; //$NON-NLS-1$
+ public static final String IMG_ELCL_LAYOUT_SINGLE = PLUGIN_ID + T_ELCL
+ + "layout_single.png"; //$NON-NLS-1$
+ public static final String IMG_ELCL_LAYOUT_VERTICAL = PLUGIN_ID + T_ELCL
+ + "layout_vertical.png"; //$NON-NLS-1$
public static final String IMG_ELCL_LEXICAL_SORT = PLUGIN_ID + T_ELCL
+ "lexical_sort.png"; //$NON-NLS-1$
public static final String IMG_ELCL_PIN_VIEW = PLUGIN_ID + T_ELCL
+ "pin_view.png"; //$NON-NLS-1$
public static final String IMG_ELCL_REFRESH = PLUGIN_ID + T_ELCL
+ "refresh.png"; //$NON-NLS-1$
+ public static final String IMG_ELCL_TH_SUPERTYPES = PLUGIN_ID + T_ELCL
+ + "th_supertypes.png"; //$NON-NLS-1$
+ public static final String IMG_ELCL_TH_SUBTYPES = PLUGIN_ID + T_ELCL
+ + "th_subtypes.png"; //$NON-NLS-1$
+ public static final String IMG_ELCL_TH_TYPES = PLUGIN_ID + T_ELCL
+ + "th_types.png"; //$NON-NLS-1$
public static final String IMG_OBJ_SEARCH_OCCURRENCE = PLUGIN_ID + T_OBJ
+ "occ_match.png"; //$NON-NLS-1$
@@ -147,26 +155,34 @@
reg.put(IMG_DLCL_REFRESH, imageDescriptorFromSymbolicName(
IMG_DLCL_REFRESH));
- reg.put(IMG_ELCL_CH_AUTOMATIC, imageDescriptorFromSymbolicName(
- IMG_ELCL_CH_AUTOMATIC));
reg.put(IMG_ELCL_CH_CALLEES, imageDescriptorFromSymbolicName(
IMG_ELCL_CH_CALLEES));
reg.put(IMG_ELCL_CH_CALLERS, imageDescriptorFromSymbolicName(
IMG_ELCL_CH_CALLERS));
- reg.put(IMG_ELCL_CH_HORIZONTAL, imageDescriptorFromSymbolicName(
- IMG_ELCL_CH_HORIZONTAL));
- reg.put(IMG_ELCL_CH_VERTICAL, imageDescriptorFromSymbolicName(
- IMG_ELCL_CH_VERTICAL));
reg.put(IMG_ELCL_EXPANDALL, imageDescriptorFromSymbolicName(
IMG_ELCL_EXPANDALL));
reg.put(IMG_ELCL_HISTORY_LIST, imageDescriptorFromSymbolicName(
IMG_ELCL_HISTORY_LIST));
+ reg.put(IMG_ELCL_LAYOUT_AUTOMATIC, imageDescriptorFromSymbolicName(
+ IMG_ELCL_LAYOUT_AUTOMATIC));
+ reg.put(IMG_ELCL_LAYOUT_HORIZONTAL, imageDescriptorFromSymbolicName(
+ IMG_ELCL_LAYOUT_HORIZONTAL));
+ reg.put(IMG_ELCL_LAYOUT_SINGLE, imageDescriptorFromSymbolicName(
+ IMG_ELCL_LAYOUT_SINGLE));
+ reg.put(IMG_ELCL_LAYOUT_VERTICAL, imageDescriptorFromSymbolicName(
+ IMG_ELCL_LAYOUT_VERTICAL));
reg.put(IMG_ELCL_LEXICAL_SORT, imageDescriptorFromSymbolicName(
IMG_ELCL_LEXICAL_SORT));
reg.put(IMG_ELCL_PIN_VIEW, imageDescriptorFromSymbolicName(
IMG_ELCL_PIN_VIEW));
reg.put(IMG_ELCL_REFRESH, imageDescriptorFromSymbolicName(
IMG_ELCL_REFRESH));
+ reg.put(IMG_ELCL_TH_SUPERTYPES, imageDescriptorFromSymbolicName(
+ IMG_ELCL_TH_SUPERTYPES));
+ reg.put(IMG_ELCL_TH_SUBTYPES, imageDescriptorFromSymbolicName(
+ IMG_ELCL_TH_SUBTYPES));
+ reg.put(IMG_ELCL_TH_TYPES, imageDescriptorFromSymbolicName(
+ IMG_ELCL_TH_TYPES));
reg.put(IMG_OBJ_SEARCH_OCCURRENCE, imageDescriptorFromSymbolicName(
IMG_OBJ_SEARCH_OCCURRENCE));
diff --git a/org.eclipse.handly.ui/src/org/eclipse/handly/ui/PartListenerAdapter.java b/org.eclipse.handly.ui/src/org/eclipse/handly/ui/PartListenerAdapter.java
new file mode 100644
index 0000000..b7ff52d
--- /dev/null
+++ b/org.eclipse.handly.ui/src/org/eclipse/handly/ui/PartListenerAdapter.java
@@ -0,0 +1,54 @@
+/*******************************************************************************
+ * Copyright (c) 2021 1C-Soft LLC.
+ *
+ * This program and the accompanying materials are made available under
+ * the terms of the Eclipse Public License 2.0 which is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * Vladimir Piskarev (1C) - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.handly.ui;
+
+import org.eclipse.ui.IPartListener;
+import org.eclipse.ui.IWorkbenchPart;
+
+/**
+ * Provides default implementations for the methods of {@link IPartListener}.
+ * <p>
+ * Clients may extend this class and override only the methods which they are
+ * interested in.
+ * </p>
+ *
+ * @since 1.6
+ */
+public class PartListenerAdapter
+ implements IPartListener
+{
+ @Override
+ public void partOpened(IWorkbenchPart part)
+ {
+ }
+
+ @Override
+ public void partClosed(IWorkbenchPart part)
+ {
+ }
+
+ @Override
+ public void partActivated(IWorkbenchPart part)
+ {
+ }
+
+ @Override
+ public void partDeactivated(IWorkbenchPart part)
+ {
+ }
+
+ @Override
+ public void partBroughtToTop(IWorkbenchPart part)
+ {
+ }
+}
diff --git a/org.eclipse.handly.ui/src/org/eclipse/handly/ui/callhierarchy/CallHierarchyViewPart.java b/org.eclipse.handly.ui/src/org/eclipse/handly/ui/callhierarchy/CallHierarchyViewPart.java
index 81d57ec..83e23a2 100644
--- a/org.eclipse.handly.ui/src/org/eclipse/handly/ui/callhierarchy/CallHierarchyViewPart.java
+++ b/org.eclipse.handly.ui/src/org/eclipse/handly/ui/callhierarchy/CallHierarchyViewPart.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2018, 2019 1C-Soft LLC.
+ * Copyright (c) 2018, 2021 1C-Soft LLC.
*
* This program and the accompanying materials are made available under
* the terms of the Eclipse Public License 2.0 which is available at
@@ -26,6 +26,7 @@
import org.eclipse.handly.ui.DefaultEditorUtility;
import org.eclipse.handly.ui.EditorOpener;
import org.eclipse.handly.ui.EditorUtility;
+import org.eclipse.handly.ui.PartListenerAdapter;
import org.eclipse.handly.ui.action.HistoryDropDownAction;
import org.eclipse.handly.ui.viewer.ColumnDescription;
import org.eclipse.handly.ui.viewer.DelegatingSelectionProvider;
@@ -141,7 +142,7 @@
private HistoryDropDownAction<HistoryEntry> historyDropDownAction;
private final PinAction pinAction = new PinAction();
- private final IPartListener partListener = new IPartListener()
+ private final IPartListener partListener = new PartListenerAdapter()
{
@Override
public void partActivated(IWorkbenchPart part)
@@ -170,16 +171,6 @@
getViewManager().viewOpenedOrActivated(CallHierarchyViewPart.this);
refresh();
}
-
- @Override
- public void partDeactivated(IWorkbenchPart part)
- {
- }
-
- @Override
- public void partBroughtToTop(IWorkbenchPart part)
- {
- }
};
/**
@@ -662,8 +653,11 @@
public void setFocus()
{
pageBook.setFocus();
- updateStatusLine(getViewSite().getActionBars().getStatusLineManager(),
- (IStructuredSelection)getSite().getSelectionProvider().getSelection());
+ ISelection selection = getSite().getSelectionProvider().getSelection();
+ if (selection instanceof IStructuredSelection)
+ updateStatusLine(
+ getViewSite().getActionBars().getStatusLineManager(),
+ (IStructuredSelection)selection);
}
/**
@@ -1184,12 +1178,15 @@
if (editorRef != null)
{
editor = editorRef.getEditor(true);
- if (activate)
- page.activate(editor);
- else
- page.bringToTop(editor);
- if (callLocation == null)
- editorUtility.revealElement(editor, element);
+ if (editor != null)
+ {
+ if (activate)
+ page.activate(editor);
+ else
+ page.bringToTop(editor);
+ if (callLocation == null)
+ editorUtility.revealElement(editor, element);
+ }
}
}
if (editor != null && callLocation != null)
@@ -1871,7 +1868,7 @@
setToolTipText(
Messages.CallHierarchyViewPart_Layout_horizontal_action_tooltip);
setImageDescriptor(Activator.getImageDescriptor(
- Activator.IMG_ELCL_CH_HORIZONTAL));
+ Activator.IMG_ELCL_LAYOUT_HORIZONTAL));
break;
case SWT.VERTICAL:
setText(
@@ -1879,7 +1876,7 @@
setToolTipText(
Messages.CallHierarchyViewPart_Layout_vertical_action_tooltip);
setImageDescriptor(Activator.getImageDescriptor(
- Activator.IMG_ELCL_CH_VERTICAL));
+ Activator.IMG_ELCL_LAYOUT_VERTICAL));
break;
default:
setText(
@@ -1887,7 +1884,7 @@
setToolTipText(
Messages.CallHierarchyViewPart_Layout_automatic_action_tooltip);
setImageDescriptor(Activator.getImageDescriptor(
- Activator.IMG_ELCL_CH_AUTOMATIC));
+ Activator.IMG_ELCL_LAYOUT_AUTOMATIC));
setChecked(true);
}
}
diff --git a/org.eclipse.handly.ui/src/org/eclipse/handly/ui/typehierarchy/Messages.java b/org.eclipse.handly.ui/src/org/eclipse/handly/ui/typehierarchy/Messages.java
new file mode 100644
index 0000000..acb1d51
--- /dev/null
+++ b/org.eclipse.handly.ui/src/org/eclipse/handly/ui/typehierarchy/Messages.java
@@ -0,0 +1,59 @@
+/*******************************************************************************
+ * Copyright (c) 2021 1C-Soft LLC.
+ *
+ * This program and the accompanying materials are made available under
+ * the terms of the Eclipse Public License 2.0 which is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * Vladimir Piskarev (1C) - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.handly.ui.typehierarchy;
+
+import org.eclipse.osgi.util.NLS;
+
+class Messages
+ extends NLS
+{
+ private static final String BUNDLE_NAME =
+ "org.eclipse.handly.ui.typehierarchy.messages"; //$NON-NLS-1$
+
+ public static String TypeHierarchyViewPart_0__items_selected;
+ public static String TypeHierarchyViewPart_Error_opening_editor;
+ public static String TypeHierarchyViewPart_Focus_on_selection_action_text;
+ public static String TypeHierarchyViewPart_Focus_on_selection_action_tooltip;
+ public static String TypeHierarchyViewPart_History_entry_label__0;
+ public static String TypeHierarchyViewPart_History_entry_label__0__1;
+ public static String TypeHierarchyViewPart_History_entry_label__0__1_more;
+ public static String TypeHierarchyViewPart_Layout_automatic_action_text;
+ public static String TypeHierarchyViewPart_Layout_automatic_action_tooltip;
+ public static String TypeHierarchyViewPart_Layout_horizontal_action_text;
+ public static String TypeHierarchyViewPart_Layout_horizontal_action_tooltip;
+ public static String TypeHierarchyViewPart_Layout_menu;
+ public static String TypeHierarchyViewPart_Layout_single_action_text;
+ public static String TypeHierarchyViewPart_Layout_single_action_tooltip;
+ public static String TypeHierarchyViewPart_Layout_vertical_action_text;
+ public static String TypeHierarchyViewPart_Layout_vertical_action_tooltip;
+ public static String TypeHierarchyViewPart_No_hierarchy_to_display;
+ public static String TypeHierarchyViewPart_Open_selected_element;
+ public static String TypeHierarchyViewPart_Refresh_action_text;
+ public static String TypeHierarchyViewPart_Refresh_action_tooltip;
+ public static String TypeHierarchyViewPart_Show_subtype_hierarchy_action_text;
+ public static String TypeHierarchyViewPart_Show_subtype_hierarchy_action_tooltip;
+ public static String TypeHierarchyViewPart_Show_supertype_hierarchy_action_text;
+ public static String TypeHierarchyViewPart_Show_supertype_hierarchy_action_tooltip;
+ public static String TypeHierarchyViewPart_Show_type_hierarchy_action_text;
+ public static String TypeHierarchyViewPart_Show_type_hierarchy_action_tooltip;
+
+ static
+ {
+ // initialize resource bundle
+ NLS.initializeMessages(BUNDLE_NAME, Messages.class);
+ }
+
+ private Messages()
+ {
+ }
+}
diff --git a/org.eclipse.handly.ui/src/org/eclipse/handly/ui/typehierarchy/TypeHierarchyKind.java b/org.eclipse.handly.ui/src/org/eclipse/handly/ui/typehierarchy/TypeHierarchyKind.java
new file mode 100644
index 0000000..99a3ac0
--- /dev/null
+++ b/org.eclipse.handly.ui/src/org/eclipse/handly/ui/typehierarchy/TypeHierarchyKind.java
@@ -0,0 +1,37 @@
+/*******************************************************************************
+ * Copyright (c) 2021 1C-Soft LLC.
+ *
+ * This program and the accompanying materials are made available under
+ * the terms of the Eclipse Public License 2.0 which is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * Vladimir Piskarev (1C) - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.handly.ui.typehierarchy;
+
+/**
+ * Enumeration of the type hierarchy kinds.
+ *
+ * @since 1.6
+ */
+public enum TypeHierarchyKind
+{
+ /**
+ * A type hierarchy that includes both supertypes and subtypes of a type.
+ * Also known as the inheritance tree.
+ */
+ TYPES,
+
+ /**
+ * The supertype hierarchy.
+ */
+ SUPERTYPES,
+
+ /**
+ * The subtype hierarchy.
+ */
+ SUBTYPES
+}
diff --git a/org.eclipse.handly.ui/src/org/eclipse/handly/ui/typehierarchy/TypeHierarchyViewPart.java b/org.eclipse.handly.ui/src/org/eclipse/handly/ui/typehierarchy/TypeHierarchyViewPart.java
new file mode 100644
index 0000000..232a230
--- /dev/null
+++ b/org.eclipse.handly.ui/src/org/eclipse/handly/ui/typehierarchy/TypeHierarchyViewPart.java
@@ -0,0 +1,1622 @@
+/*******************************************************************************
+ * Copyright (c) 2021 1C-Soft LLC.
+ *
+ * This program and the accompanying materials are made available under
+ * the terms of the Eclipse Public License 2.0 which is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * Vladimir Piskarev (1C) - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.handly.ui.typehierarchy;
+
+import static org.eclipse.handly.context.Contexts.EMPTY_CONTEXT;
+
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.eclipse.handly.context.IContext;
+import org.eclipse.handly.internal.ui.Activator;
+import org.eclipse.handly.ui.DefaultEditorUtility;
+import org.eclipse.handly.ui.EditorOpener;
+import org.eclipse.handly.ui.EditorUtility;
+import org.eclipse.handly.ui.PartListenerAdapter;
+import org.eclipse.handly.ui.action.HistoryDropDownAction;
+import org.eclipse.handly.ui.viewer.DelegatingSelectionProvider;
+import org.eclipse.jface.action.Action;
+import org.eclipse.jface.action.IAction;
+import org.eclipse.jface.action.IMenuListener;
+import org.eclipse.jface.action.IMenuManager;
+import org.eclipse.jface.action.IStatusLineManager;
+import org.eclipse.jface.action.MenuManager;
+import org.eclipse.jface.action.Separator;
+import org.eclipse.jface.dialogs.ErrorDialog;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.ISelectionProvider;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.StructuredViewer;
+import org.eclipse.jface.viewers.TreeViewer;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.SashForm;
+import org.eclipse.swt.events.ControlAdapter;
+import org.eclipse.swt.events.ControlEvent;
+import org.eclipse.swt.events.FocusEvent;
+import org.eclipse.swt.events.FocusListener;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Menu;
+import org.eclipse.ui.IActionBars;
+import org.eclipse.ui.IEditorPart;
+import org.eclipse.ui.IEditorReference;
+import org.eclipse.ui.IMemento;
+import org.eclipse.ui.IPartListener;
+import org.eclipse.ui.IViewSite;
+import org.eclipse.ui.IWorkbenchActionConstants;
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.IWorkbenchPart;
+import org.eclipse.ui.OpenAndLinkWithEditorHelper;
+import org.eclipse.ui.PartInitException;
+import org.eclipse.ui.actions.ActionFactory;
+import org.eclipse.ui.actions.BaseSelectionListenerAction;
+import org.eclipse.ui.part.PageBook;
+import org.eclipse.ui.part.ViewPart;
+
+/**
+ * An abstract base implementation of a type hierarchy view.
+ *
+ * @since 1.6 Provisional API
+ */
+public abstract class TypeHierarchyViewPart
+ extends ViewPart
+{
+ /**
+ * Pop-up menu: name of group for focus actions (value
+ * <code>"group.focus"</code>).
+ */
+ protected static final String GROUP_FOCUS = "group.focus"; //$NON-NLS-1$
+
+ private static final Object[] NO_ELEMENTS = new Object[0];
+
+ private static final String KEY_HIERARCHY_KIND =
+ "org.eclipse.handly.typehierarchy.view.hierarchyKind"; //$NON-NLS-1$
+ private static final String KEY_LAYOUT_MODE =
+ "org.eclipse.handly.typehierarchy.view.layoutMode"; //$NON-NLS-1$
+ private static final String KEY_HORIZONTAL_WEIGHTS =
+ "org.eclipse.handly.typehierarchy.view.horizontalWeights"; //$NON-NLS-1$
+ private static final String KEY_VERTICAL_WEIGHTS =
+ "org.eclipse.handly.typehierarchy.view.verticalWeights"; //$NON-NLS-1$
+
+ private static final int LAYOUT_AUTOMATIC = SWT.HORIZONTAL | SWT.VERTICAL;
+
+ private final Set<TypeHierarchyKind> supportedHierarchyKinds;
+ private Set<TypeHierarchyKind> enabledHierarchyKinds;
+ private TypeHierarchyKind hierarchyKind;
+ private int layoutMode = getDefaultLayoutMode();
+ private boolean layoutAdjusted;
+ private int[] horizontalWeights, verticalWeights;
+ private Object[] inputElements = NO_ELEMENTS;
+ private final List<HistoryEntry> history = new ArrayList<>();
+ private PageBook pageBook, hierarchyPageBook;
+ private Control noHierarchyPage;
+ private SashForm sashForm;
+ private final Map<TypeHierarchyKind, TreeViewer> hierarchyViewers =
+ new HashMap<>();
+ private EditorOpener editorOpener;
+ private SetHierarchyKindAction[] setHierarchyKindActions =
+ new SetHierarchyKindAction[0];
+ private SetLayoutModeAction[] setLayoutModeActions =
+ new SetLayoutModeAction[0];
+ private final FocusOnSelectionAction focusOnSelectionAction =
+ new FocusOnSelectionAction();
+ private HistoryDropDownAction<HistoryEntry> historyDropDownAction;
+ private final RefreshAction refreshAction = new RefreshAction();
+
+ private final IPartListener partListener = new PartListenerAdapter()
+ {
+ @Override
+ public void partOpened(IWorkbenchPart part)
+ {
+ if (part != TypeHierarchyViewPart.this)
+ return;
+
+ refresh();
+ };
+ };
+
+ /**
+ * Constructs a type hierarchy view that supports all of the
+ * type hierarchy kinds.
+ *
+ * @see TypeHierarchyKind
+ * @see #TypeHierarchyViewPart(Set)
+ */
+ public TypeHierarchyViewPart()
+ {
+ this(EnumSet.allOf(TypeHierarchyKind.class));
+ }
+
+ /**
+ * Constructs a type hierarchy view that supports the given
+ * type hierarchy kinds. Clients <b>must not</b> modify the given set
+ * afterwards.
+ *
+ * @param supportedHierarchyKinds not <code>null</code> and not empty
+ */
+ public TypeHierarchyViewPart(Set<TypeHierarchyKind> supportedHierarchyKinds)
+ {
+ if (supportedHierarchyKinds.isEmpty())
+ throw new IllegalArgumentException();
+
+ this.supportedHierarchyKinds = supportedHierarchyKinds;
+ this.enabledHierarchyKinds = supportedHierarchyKinds;
+ this.hierarchyKind = supportedHierarchyKinds.iterator().next();
+
+ makeSetHierarchyKindActions();
+ makeSetLayoutModeActions();
+ refreshAction.setEnabled(false);
+ }
+
+ /**
+ * Returns whether the given elements are possible input elements for this view.
+ * <p>
+ * Default implementation invokes {@link #isPossibleInputElement(Object)} for
+ * each of the given elements until <code>false</code> is returned for an element
+ * (in which case this method will return <code>false</code>) or all elements
+ * have been checked (in which case it will return <code>true</code>).
+ * </p>
+ *
+ * @param elements may be <code>null</code> or may contain null elements,
+ * in which case <code>false</code> will be returned; may be empty,
+ * in which case <code>true</code> will be returned
+ * @return <code>true</code> if the given elements are possible input elements
+ * for this view, and <code>false</code> otherwise
+ */
+ public boolean arePossibleInputElements(Object[] elements)
+ {
+ if (elements == null)
+ return false;
+ for (Object element : elements)
+ {
+ if (!isPossibleInputElement(element))
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Sets the current input elements for this view. Clients <b>must not</b>
+ * modify the given array afterwards.
+ *
+ * @param elements not <code>null</code>, must not contain null elements;
+ * may be empty
+ * @throws IllegalArgumentException if {@link #arePossibleInputElements(Object[])}
+ * returns <code>false</code> for the given elements
+ */
+ public void setInputElements(Object[] elements)
+ {
+ if (!arePossibleInputElements(elements))
+ throw new IllegalArgumentException(Arrays.toString(elements));
+
+ Object[] oldInputElements = inputElements;
+
+ inputElements = elements;
+
+ refreshAction.setEnabled(elements.length > 0);
+
+ if (elements.length > 0)
+ addHistoryEntry(createHistoryEntry(elements));
+
+ onInputElementsChanged(oldInputElements, elements);
+ }
+
+ /**
+ * Returns the current input elements for this view.
+ *
+ * @return the current input elements (never <code>null</code>,
+ * may be empty). Clients <b>must not</b> modify the returned array.
+ */
+ public final Object[] getInputElements()
+ {
+ return inputElements;
+ }
+
+ /**
+ * Returns whether the given hierarchy kind is currently enabled for this view.
+ *
+ * @param kind may be <code>null</code>, in which case <code>false</code>
+ * will be returned
+ * @return <code>true</code> if the given kind is currently enabled,
+ * and <code>false</code> otherwise
+ */
+ public final boolean isEnabledHierarchyKind(TypeHierarchyKind kind)
+ {
+ return enabledHierarchyKinds.contains(kind);
+ }
+
+ /**
+ * Sets the current hierarchy kind for this view.
+ *
+ * @param kind not <code>null</code>
+ * @throws IllegalArgumentException if the given kind is not currently {@link
+ * #isEnabledHierarchyKind(TypeHierarchyKind) enabled} for this view
+ */
+ public void setHierarchyKind(TypeHierarchyKind kind)
+ {
+ if (!isEnabledHierarchyKind(kind))
+ throw new IllegalArgumentException();
+
+ if (kind == hierarchyKind)
+ return;
+
+ TypeHierarchyKind oldKind = hierarchyKind;
+
+ hierarchyKind = kind;
+
+ for (SetHierarchyKindAction action : setHierarchyKindActions)
+ action.setChecked(action.kind == kind);
+
+ onHierarchyKindChanged(oldKind, kind);
+ }
+
+ /**
+ * Returns the current hierarchy kind for this view.
+ *
+ * @return the current hierarchy kind (never <code>null</code>)
+ * @see #setHierarchyKind(TypeHierarchyKind)
+ */
+ public final TypeHierarchyKind getHierarchyKind()
+ {
+ return hierarchyKind;
+ }
+
+ /**
+ * Sets layout mode for this view, which may be one of the constants
+ * {@link SWT#HORIZONTAL} or {@link SWT#VERTICAL}; this method may also
+ * be called with <code>SWT.HORIZONTAL|SWT.VERTICAL</code> for automatic
+ * layout or {@link SWT#NONE} for hierarchy-only layout.
+ * <p>
+ * Note that only a subset of the allowed values for layout mode may be
+ * {@link #supportsLayoutMode(int) supported} by the view.
+ * </p>
+ *
+ * @param layoutMode the given layout mode
+ * @throws IllegalArgumentException if the given layout mode is not
+ * {@link #supportsLayoutMode(int) supported} by the view
+ */
+ public void setLayoutMode(int layoutMode)
+ {
+ if (!supportsLayoutMode(layoutMode))
+ throw new IllegalArgumentException();
+
+ if (layoutMode == this.layoutMode)
+ return;
+
+ int oldLayoutMode = this.layoutMode;
+
+ this.layoutMode = layoutMode;
+
+ for (SetLayoutModeAction action : setLayoutModeActions)
+ action.setChecked(action.layoutMode == layoutMode);
+
+ onLayoutModeChanged(oldLayoutMode, layoutMode);
+ }
+
+ /**
+ * Returns the current layout mode for this view.
+ *
+ * @return the current layout mode
+ * @see #setLayoutMode(int)
+ */
+ public final int getLayoutMode()
+ {
+ return layoutMode;
+ }
+
+ /**
+ * Returns <code>true</code> if the given value represents a supported
+ * layout mode for this view, and <code>false</code> otherwise.
+ * The set of the supported layout mode values does not change over
+ * the lifetime of the view and is never empty.
+ * <p>
+ * Default implementation only supports {@link SWT#NONE}, which represents
+ * hierarchy-only layout.
+ * </p>
+ *
+ * @param layoutMode the layout mode to check
+ * @return <code>true</code> if the given value represents a supported
+ * layout mode for this view, and <code>false</code> otherwise
+ */
+ public boolean supportsLayoutMode(int layoutMode)
+ {
+ return layoutMode == SWT.NONE;
+ }
+
+ /**
+ * Performs a full refresh of the content of this view.
+ */
+ public final void refresh()
+ {
+ refresh(EMPTY_CONTEXT);
+ }
+
+ @Override
+ public void init(IViewSite site, IMemento memento) throws PartInitException
+ {
+ super.init(site, memento);
+ if (memento != null)
+ {
+ if (supportedHierarchyKinds.size() > 1)
+ {
+ String value = memento.getString(KEY_HIERARCHY_KIND);
+ if (value != null)
+ {
+ TypeHierarchyKind kind = null;
+ try
+ {
+ kind = TypeHierarchyKind.valueOf(value);
+ }
+ catch (IllegalArgumentException e)
+ {
+ }
+ if (supportedHierarchyKinds.contains(kind))
+ setHierarchyKind(kind);
+ }
+ }
+
+ Integer layoutMode = memento.getInteger(KEY_LAYOUT_MODE);
+ if (layoutMode != null)
+ {
+ int value = layoutMode.intValue();
+ if (supportsLayoutMode(value))
+ setLayoutMode(value);
+ }
+
+ try
+ {
+ horizontalWeights = toIntArray(memento.getString(
+ KEY_HORIZONTAL_WEIGHTS));
+ }
+ catch (NumberFormatException e)
+ {
+ }
+
+ try
+ {
+ verticalWeights = toIntArray(memento.getString(
+ KEY_VERTICAL_WEIGHTS));
+ }
+ catch (NumberFormatException e)
+ {
+ }
+ }
+ }
+
+ @Override
+ public void saveState(IMemento memento)
+ {
+ super.saveState(memento);
+ if (supportedHierarchyKinds.size() > 1)
+ memento.putString(KEY_HIERARCHY_KIND, hierarchyKind.name());
+ memento.putInteger(KEY_LAYOUT_MODE, layoutMode);
+ saveSashFormWeights();
+ memento.putString(KEY_HORIZONTAL_WEIGHTS, toString(horizontalWeights));
+ memento.putString(KEY_VERTICAL_WEIGHTS, toString(verticalWeights));
+ }
+
+ @Override
+ public void createPartControl(Composite parent)
+ {
+ getSite().getPage().addPartListener(partListener);
+
+ parent.addControlListener(new ControlAdapter()
+ {
+ @Override
+ public void controlResized(ControlEvent e)
+ {
+ if (!layoutAdjusted || layoutMode == LAYOUT_AUTOMATIC)
+ adjustLayout(layoutMode);
+ }
+ });
+
+ DelegatingSelectionProvider selectionProvider =
+ new DelegatingSelectionProvider();
+
+ getSite().setSelectionProvider(selectionProvider);
+
+ selectionProvider.addSelectionChangedListener(e ->
+ {
+ ISelection selection = e.getSelection();
+ if (selection instanceof IStructuredSelection)
+ updateStatusLine(
+ getViewSite().getActionBars().getStatusLineManager(),
+ (IStructuredSelection)selection);
+ });
+
+ pageBook = new PageBook(parent, SWT.NONE);
+
+ noHierarchyPage = createNoHierarchyPage(pageBook);
+
+ sashForm = new SashForm(pageBook, SWT.NONE);
+
+ hierarchyPageBook = new PageBook(sashForm, SWT.NONE);
+
+ if (layoutMode == SWT.NONE)
+ sashForm.setMaximizedControl(hierarchyPageBook);
+
+ for (TypeHierarchyKind kind : supportedHierarchyKinds)
+ {
+ TreeViewer hierarchyViewer = createHierarchyViewer(
+ hierarchyPageBook, kind);
+
+ configureHierarchyViewer(hierarchyViewer, kind);
+
+ initContextMenu(hierarchyViewer.getControl(), (
+ IMenuManager manager) ->
+ {
+ createHierarchyViewerMenuGroups(manager, kind);
+ fillHierarchyViewerMenu(manager, kind);
+ }, getSite().getId(), selectionProvider);
+
+ new OpenEditorHelper(hierarchyViewer);
+
+ hierarchyViewer.getControl().addFocusListener(new FocusListener()
+ {
+ @Override
+ public void focusGained(FocusEvent e)
+ {
+ selectionProvider.setDelegate(hierarchyViewer);
+ onHierarchySelectionChanged(hierarchyViewer.getSelection(),
+ kind);
+ }
+
+ @Override
+ public void focusLost(FocusEvent e)
+ {
+ selectionProvider.setDelegate(null);
+ }
+ });
+
+ hierarchyViewer.addSelectionChangedListener(
+ e -> onHierarchySelectionChanged(e.getSelection(), kind));
+
+ hierarchyViewers.put(kind, hierarchyViewer);
+ }
+
+ editorOpener = createEditorOpener();
+
+ selectionProvider.addSelectionChangedListener(focusOnSelectionAction);
+
+ addRefreshAction(refreshAction);
+
+ getViewSite().getActionBars().setGlobalActionHandler(
+ ActionFactory.REFRESH.getId(), refreshAction);
+
+ for (SetHierarchyKindAction action : setHierarchyKindActions)
+ addSetHierarchyKindAction(action, action.kind);
+
+ for (SetLayoutModeAction action : setLayoutModeActions)
+ addSetLayoutModeAction(action, action.layoutMode);
+
+ historyDropDownAction = createHistoryDropDownAction(
+ new HistoryDropDownAction.History<HistoryEntry>()
+ {
+ @Override
+ public List<HistoryEntry> getHistoryEntries()
+ {
+ return getHistory();
+ }
+
+ @Override
+ public void setHistoryEntries(List<HistoryEntry> entries)
+ {
+ List<HistoryEntry> history = getHistory();
+ history.clear();
+ history.addAll(entries);
+ onHistoryChanged();
+ }
+
+ @Override
+ public HistoryEntry getActiveEntry()
+ {
+ Object[] inputElements = getInputElements();
+ if (inputElements.length == 0)
+ return null;
+ return createHistoryEntry(inputElements);
+ }
+
+ @Override
+ public void setActiveEntry(HistoryEntry entry)
+ {
+ setInputElements(entry.getInputElements());
+ }
+
+ @Override
+ public String getLabel(HistoryEntry entry)
+ {
+ return entry.getLabel();
+ }
+
+ @Override
+ public ImageDescriptor getImageDescriptor(HistoryEntry entry)
+ {
+ return entry.getImageDescriptor();
+ }
+ });
+ historyDropDownAction.setEnabled(!getHistory().isEmpty());
+ addHistoryDropDownAction(historyDropDownAction);
+ }
+
+ @Override
+ public void dispose()
+ {
+ getSite().getPage().removePartListener(partListener);
+
+ super.dispose();
+ }
+
+ @Override
+ public void setFocus()
+ {
+ pageBook.setFocus();
+ }
+
+ /**
+ * Returns whether the given element is a possible input element for this view.
+ *
+ * @param element may be <code>null</code>, in which case <code>false</code>
+ * will be returned
+ * @return <code>true</code> if the given element is a possible input element
+ * for this view, and <code>false</code> otherwise
+ * @see #arePossibleInputElements(Object[])
+ */
+ protected abstract boolean isPossibleInputElement(Object element);
+
+ /**
+ * Creates and returns a history entry for the given input elements.
+ *
+ * @param inputElements never <code>null</code>; never empty
+ * @return the created history entry (not <code>null</code>)
+ */
+ protected abstract HistoryEntry createHistoryEntry(Object[] inputElements);
+
+ /**
+ * Computes the content description for this view.
+ *
+ * @return the computed content description (not <code>null</code>)
+ * @see #getContentDescription()
+ */
+ protected abstract String computeContentDescription();
+
+ /**
+ * Returns whether the view has already been opened and has not yet been
+ * closed.
+ *
+ * @return <code>true</code> if the view is currently open,
+ * and <code>false</code> otherwise
+ */
+ protected final boolean isOpen()
+ {
+ return pageBook != null && !pageBook.isDisposed();
+ }
+
+ /**
+ * Performs a refresh of the content of this view according to options
+ * specified in the given context.
+ *
+ * @param context the operation context (never <code>null</code>)
+ */
+ protected void refresh(IContext context)
+ {
+ if (!isOpen())
+ return;
+
+ setContentDescription(computeContentDescription());
+
+ for (Map.Entry<TypeHierarchyKind, TreeViewer> entry : hierarchyViewers.entrySet())
+ setHierarchyViewerInput(entry.getValue(), entry.getKey());
+
+ updateHierarchyPage();
+ }
+
+ /**
+ * Ensures that the appropriate hierarchy page is shown by this view.
+ * <p>
+ * This method may only be called after the SWT controls for this view
+ * have been created and before they have been disposed.
+ * </p>
+ */
+ protected void updateHierarchyPage()
+ {
+ if (inputElements.length > 0)
+ {
+ TreeViewer hierarchyViewer = hierarchyViewers.get(hierarchyKind);
+ Control hierarchyPage = hierarchyViewer.getControl();
+ pageBook.showPage(sashForm);
+ hierarchyPageBook.showPage(hierarchyPage);
+ hierarchyPage.setFocus();
+ }
+ else
+ {
+ pageBook.showPage(noHierarchyPage);
+ pageBook.setFocus();
+ }
+ }
+
+ /**
+ * A callback that is invoked when the view input elements change.
+ *
+ * @param oldInputElements never <code>null</code>
+ * @param newInputElements never <code>null</code>
+ */
+ protected void onInputElementsChanged(Object[] oldInputElements,
+ Object[] newInputElements)
+ {
+ refresh();
+ }
+
+ /**
+ * A callback that is invoked when the current hierarchy kind changes.
+ *
+ * @param oldKind never <code>null</code>
+ * @param newKind never <code>null</code>
+ */
+ protected void onHierarchyKindChanged(TypeHierarchyKind oldKind,
+ TypeHierarchyKind newKind)
+ {
+ if (!isOpen())
+ return;
+
+ updateHierarchyPage();
+ }
+
+ /**
+ * Returns a set of the supported hierarchy kinds for this view.
+ *
+ * @return the supported hierarchy kinds (never <code>null</code>,
+ * never empty)
+ */
+ protected final Set<TypeHierarchyKind> getSupportedHierarchyKinds()
+ {
+ return supportedHierarchyKinds;
+ }
+
+ /**
+ * Sets the enabled hierarchy kinds for this view. Note that at least one
+ * hierarchy kind must be enabled. Clients <b>must not</b> modify the given
+ * set afterwards.
+ *
+ * @param kinds not <code>null</code> and not empty
+ */
+ protected void setEnabledHierarchyKinds(Set<TypeHierarchyKind> kinds)
+ {
+ if (kinds.isEmpty())
+ throw new IllegalArgumentException();
+
+ if (!supportedHierarchyKinds.containsAll(kinds))
+ throw new IllegalArgumentException();
+
+ if (enabledHierarchyKinds.equals(kinds))
+ return;
+
+ Set<TypeHierarchyKind> oldKinds = enabledHierarchyKinds;
+
+ enabledHierarchyKinds = kinds;
+
+ for (SetHierarchyKindAction action : setHierarchyKindActions)
+ action.setEnabled(kinds.contains(action.kind));
+
+ if (!kinds.contains(hierarchyKind))
+ setHierarchyKind(kinds.iterator().next());
+
+ onEnabledHierarchyKindsChanged(oldKinds, kinds);
+ }
+
+ /**
+ * A callback that is invoked when the set of enabled hierarchy kinds changes.
+ *
+ * @param oldKinds never <code>null</code>
+ * @param newKinds never <code>null</code>
+ */
+ protected void onEnabledHierarchyKindsChanged(
+ Set<TypeHierarchyKind> oldKinds, Set<TypeHierarchyKind> newKinds)
+ {
+ if (!isOpen())
+ return;
+
+ for (TypeHierarchyKind kind : symmetricDifference(oldKinds, newKinds))
+ setHierarchyViewerInput(hierarchyViewers.get(kind), kind);
+ }
+
+ /**
+ * A callback that is invoked when the view layout mode changes.
+ *
+ * @param oldLayoutMode the old layout mode
+ * @param newLayoutMode the new layout mode
+ */
+ protected void onLayoutModeChanged(int oldLayoutMode, int newLayoutMode)
+ {
+ if (!isOpen())
+ return;
+
+ sashForm.setMaximizedControl(newLayoutMode == SWT.NONE
+ ? hierarchyPageBook : null);
+ adjustLayout(newLayoutMode);
+ }
+
+ /**
+ * Updates the status line based on the given selection in this view.
+ * <p>
+ * Default implementation clears the status line message if the selection
+ * is empty or if exactly one element is selected; sets a generic message
+ * of the form "(x) items selected" otherwise. It always clears the error
+ * message.
+ * </p>
+ *
+ * @param manager the status line manager (never <code>null</code>)
+ * @param selection the current selection (never <code>null</code>)
+ */
+ protected void updateStatusLine(IStatusLineManager manager,
+ IStructuredSelection selection)
+ {
+ String message = null;
+ int size = selection.size();
+ if (size > 1)
+ {
+ message = MessageFormat.format(
+ Messages.TypeHierarchyViewPart_0__items_selected, size);
+ }
+ manager.setMessage(message);
+ manager.setErrorMessage(null);
+ }
+
+ /**
+ * Creates and returns a control for the 'no hierarchy' page.
+ * This method is called once, when the part's control is created.
+ * <p>
+ * Default implementation returns a <code>Label</code> telling,
+ * in general terms, that there is no type hierarchy to display.
+ * Subclasses may override this method (e.g., to give details
+ * on what the user needs to do to display a type hierarchy).
+ * </p>
+ *
+ * @param parent the parent composite (never <code>null</code>)
+ * @return the created control (not <code>null</code>)
+ */
+ protected Control createNoHierarchyPage(Composite parent)
+ {
+ Label label = new Label(parent, SWT.LEAD + SWT.WRAP);
+ label.setText(Messages.TypeHierarchyViewPart_No_hierarchy_to_display);
+ return label;
+ }
+
+ /**
+ * Returns the {@link SashForm} created for this view.
+ *
+ * @return the <code>SashForm</code>,
+ * or <code>null</code> if it has yet to be created
+ */
+ protected final SashForm getSashForm()
+ {
+ return sashForm;
+ }
+
+ /**
+ * Returns the hierarchy tree viewer for the given hierarchy kind.
+ *
+ * @param kind the type hierarchy kind (never <code>null</code>)
+ * @return the hierarchy tree viewer,
+ * or <code>null</code> if it has yet to be created
+ * @see #createHierarchyViewer(Composite, TypeHierarchyKind)
+ */
+ protected final TreeViewer getHierarchyViewer(TypeHierarchyKind kind)
+ {
+ return hierarchyViewers.get(kind);
+ }
+
+ /**
+ * Creates and returns a tree viewer that will be used for displaying
+ * the type hierarchy of the given kind. This method only creates the viewer;
+ * it does not configure it. This method is called once, when the part's
+ * control is created.
+ *
+ * @param parent the parent composite (never <code>null</code>)
+ * @param kind the type hierarchy kind (never <code>null</code>)
+ * @return the created viewer (not <code>null</code>)
+ * @see #configureHierarchyViewer(TreeViewer, TypeHierarchyKind)
+ */
+ protected TreeViewer createHierarchyViewer(Composite parent,
+ TypeHierarchyKind kind)
+ {
+ return new TreeViewer(parent, SWT.SINGLE);
+ }
+
+ /**
+ * Configures the newly created hierarchy viewer for the given hierarchy kind.
+ * This method has to set as least a content provider and a label provider.
+ * This method is called once, just after the viewer is created.
+ *
+ * @param viewer the viewer to configure (never <code>null</code>)
+ * @param kind the type hierarchy kind (never <code>null</code>)
+ */
+ protected abstract void configureHierarchyViewer(TreeViewer viewer,
+ TypeHierarchyKind kind);
+
+ /**
+ * Sets the input for the hierarchy viewer for the given hierarchy kind.
+ *
+ * @param viewer the viewer to set the input for (never <code>null</code>)
+ * @param kind the type hierarchy kind (never <code>null</code>)
+ */
+ protected void setHierarchyViewerInput(TreeViewer viewer,
+ TypeHierarchyKind kind)
+ {
+ viewer.setInput(inputElements.length > 0 && isEnabledHierarchyKind(kind)
+ ? inputElements : null);
+ }
+
+ /**
+ * Creates the menu groups for the pop-up menu of the hierarchy viewer of
+ * the given hierarchy kind. This method is called each time the pop-up menu
+ * is about to show, just before {@link #fillHierarchyViewerMenu} is called.
+ * <p>
+ * Default implementation adds groups named {@link #GROUP_FOCUS} and
+ * {@link IWorkbenchActionConstants#MB_ADDITIONS}. Subclasses may extend
+ * or override this method, but should usually keep the default groups.
+ * <p>
+ *
+ * @param manager the menu manager (never <code>null</code>)
+ * @param kind the type hierarchy kind (never <code>null</code>)
+ */
+ protected void createHierarchyViewerMenuGroups(IMenuManager manager,
+ TypeHierarchyKind kind)
+ {
+ manager.add(new Separator(GROUP_FOCUS));
+ manager.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS));
+ }
+
+ /**
+ * Fills the pop-menu for the hierarchy viewer for the given hierarchy kind
+ * using the menu groups created by {@link #createHierarchyViewerMenuGroups}.
+ * This method is called each time the pop-up menu is about to show.
+ * <p>
+ * Default implementation adds generic actions such as 'focus on selection'.
+ * Subclasses may extend or override this method.
+ * </p>
+ *
+ * @param manager the menu manager (never <code>null</code>)
+ * @param kind the type hierarchy kind (never <code>null</code>)
+ */
+ protected void fillHierarchyViewerMenu(IMenuManager manager,
+ TypeHierarchyKind kind)
+ {
+ if (focusOnSelectionAction.isEnabled())
+ manager.appendToGroup(GROUP_FOCUS, focusOnSelectionAction);
+ }
+
+ /**
+ * A callback that is invoked when selection changes in the hierarchy viewer
+ * for the given hierarchy kind.
+ * <p>
+ * Default implementation tries to reveal the selected hierarchy node in an
+ * open editor with {@link #revealInEditor(Object, boolean, boolean)
+ * revealInEditor}.
+ * </p>
+ *
+ * @param selection the new selection (never <code>null</code>)
+ * @param kind the type hierarchy kind (never <code>null</code>)
+ */
+ protected void onHierarchySelectionChanged(ISelection selection,
+ TypeHierarchyKind kind)
+ {
+ Object element = getSelectedElement(selection);
+ if (element != null)
+ {
+ if (hierarchyViewers.get(kind).getControl().isFocusControl())
+ {
+ try
+ {
+ revealInEditor(element, false, false);
+ }
+ catch (PartInitException e)
+ {
+ // cannot happen: may not open a new editor
+ }
+ }
+ }
+ }
+
+ /**
+ * Reveals the given element in an appropriate editor on a best effort basis.
+ * <p>
+ * Default implementation uses the {@link #getEditorOpener() editor opener}.
+ * </p>
+ *
+ * @param element not <code>null</code>
+ * @param activate whether to activate the editor
+ * @param mayOpenNewEditor whether a new editor may be opened
+ * when the element cannot be revealed in an existing editor
+ * @throws PartInitException if a new editor could not be created
+ * or initialized
+ */
+ protected void revealInEditor(Object element, boolean activate,
+ boolean mayOpenNewEditor) throws PartInitException
+ {
+ EditorUtility editorUtility = editorOpener.getEditorUtility();
+ if (editorUtility.getEditorInput(element) == null)
+ return;
+
+ if (mayOpenNewEditor)
+ editorOpener.open(element, activate, true);
+ else
+ {
+ IWorkbenchPage page = editorOpener.getWorkbenchPage();
+ IEditorReference editorRef = editorUtility.findEditor(page,
+ element);
+ if (editorRef != null)
+ {
+ IEditorPart editor = editorRef.getEditor(true);
+ if (editor != null)
+ {
+ if (activate)
+ page.activate(editor);
+ else
+ page.bringToTop(editor);
+ editorUtility.revealElement(editor, element);
+ }
+ }
+ }
+ }
+
+ /**
+ * Returns the editor opener used by this view.
+ *
+ * @return the editor opener,
+ * or <code>null</code> if it has yet to be created
+ * @see #createEditorOpener()
+ */
+ protected final EditorOpener getEditorOpener()
+ {
+ return editorOpener;
+ }
+
+ /**
+ * Creates and returns an editor opener for this view.
+ * This method is called once, when the part's control is created.
+ * <p>
+ * Subclasses may override this method if they require a specific
+ * editor opener.
+ * </p>
+ *
+ * @return the created editor opener (not <code>null</code>)
+ */
+ protected EditorOpener createEditorOpener()
+ {
+ return new EditorOpener(getSite().getPage(),
+ DefaultEditorUtility.INSTANCE);
+ }
+
+ /**
+ * Returns the 'focus on selection' action used by this view.
+ *
+ * @return the 'focus on selection' action
+ */
+ protected final IAction getFocusOnSelectionAction()
+ {
+ return focusOnSelectionAction;
+ }
+
+ /**
+ * Contributes the 'refresh' action to this view.
+ * This method is called once, when the part's control is created.
+ * <p>
+ * Default implementation adds the given action to the view tool bar.
+ * Subclasses may extend or override this method.
+ * </p>
+ *
+ * @param action the 'refresh' action (never <code>null</code>)
+ */
+ protected void addRefreshAction(IAction action)
+ {
+ getViewSite().getActionBars().getToolBarManager().add(action);
+ }
+
+ /**
+ * Contributes a 'set hierarchy kind' action to this view. This method
+ * is called once for each of the 'set hierarchy kind' actions, when the
+ * part's control is created. If this view supports only one hierarchy kind,
+ * no 'set hierarchy kind' action is created and this method is not called.
+ * <p>
+ * Default implementation adds the given action to the view tool bar
+ * as well as to the view menu. Subclasses may extend or override
+ * this method.
+ * </p>
+ *
+ * @param action a 'set hierarchy kind' action
+ * (never <code>null</code>)
+ * @param kind the hierarchy kind set by the given action
+ * (never <code>null</code>)
+ * @see #setHierarchyKind(TypeHierarchyKind)
+ */
+ protected void addSetHierarchyKindAction(IAction action,
+ TypeHierarchyKind kind)
+ {
+ IActionBars actionBars = getViewSite().getActionBars();
+ actionBars.getToolBarManager().add(action);
+ actionBars.getMenuManager().add(action);
+ }
+
+ /**
+ * Contributes a 'set layout mode' action to this view. This method
+ * is called once for each of the 'set layout mode' actions, when the
+ * part's control is created. If this view supports only one layout mode,
+ * no 'set layout mode' action is created and this method is not called.
+ * <p>
+ * Default implementation adds the given action to the 'Layout' sub-menu
+ * of the view menu. The sub-menu is created if necessary. Subclasses may
+ * extend or override this method.
+ * </p>
+ *
+ * @param action a 'set layout mode' action
+ * (never <code>null</code>)
+ * @param layoutMode the layout mode set by the given action
+ * @see #setLayoutMode(int)
+ */
+ protected void addSetLayoutModeAction(IAction action, int layoutMode)
+ {
+ IActionBars actionBars = getViewSite().getActionBars();
+ IMenuManager viewMenu = actionBars.getMenuManager();
+ String id = "layout"; //$NON-NLS-1$
+ IMenuManager layoutSubMenu = viewMenu.findMenuUsingPath(id);
+ if (layoutSubMenu == null)
+ {
+ viewMenu.add(new Separator());
+ layoutSubMenu = new MenuManager(
+ Messages.TypeHierarchyViewPart_Layout_menu, id);
+ viewMenu.add(layoutSubMenu);
+ }
+ layoutSubMenu.add(action);
+ }
+
+ /**
+ * Creates and returns a 'show history list' action for this view.
+ * This method is called once, when the part's control is created.
+ * <p>
+ * Subclasses need to override this method if they extend
+ * {@link HistoryDropDownAction}.
+ * </p>
+ *
+ * @param history never <code>null</code>
+ * @return the created action (not <code>null</code>)
+ */
+ protected HistoryDropDownAction<HistoryEntry> createHistoryDropDownAction(
+ HistoryDropDownAction.History<HistoryEntry> history)
+ {
+ return new HistoryDropDownAction<>(history);
+ }
+
+ /**
+ * Contributes the 'show history list' action to this view.
+ * This method is called once, when the part's control is created.
+ * <p>
+ * Default implementation adds the given action to the view tool bar.
+ * Subclasses may extend or override this method.
+ * </p>
+ *
+ * @param action the 'show history list' action (never <code>null</code>)
+ */
+ protected void addHistoryDropDownAction(IAction action)
+ {
+ getViewSite().getActionBars().getToolBarManager().add(action);
+ }
+
+ /**
+ * Returns the history used by this view; the history is represented by
+ * a "live" list of history entries.
+ *
+ * @return the view history (never <code>null</code>)
+ */
+ protected List<HistoryEntry> getHistory()
+ {
+ return history;
+ }
+
+ /**
+ * A callback that is invoked when the history has been updated.
+ * <p>
+ * Default implementation sets the enabled state of the 'show history list'
+ * action according to whether the history is empty and, if the history
+ * is empty, clears the view input.
+ * </p>
+ *
+ * @see #getHistory()
+ */
+ protected void onHistoryChanged()
+ {
+ boolean empty = getHistory().isEmpty();
+ historyDropDownAction.setEnabled(!empty);
+ if (empty)
+ setInputElements(NO_ELEMENTS);
+ }
+
+ private void addHistoryEntry(HistoryEntry entry)
+ {
+ List<HistoryEntry> history = getHistory();
+ history.remove(entry);
+ history.add(0, entry);
+ onHistoryChanged();
+ }
+
+ private void makeSetHierarchyKindActions()
+ {
+ int size = supportedHierarchyKinds.size();
+ if (size > 1)
+ {
+ setHierarchyKindActions = new SetHierarchyKindAction[size];
+ int i = 0;
+ for (TypeHierarchyKind kind : supportedHierarchyKinds)
+ setHierarchyKindActions[i++] = new SetHierarchyKindAction(kind);
+ }
+ }
+
+ private void makeSetLayoutModeActions()
+ {
+ List<SetLayoutModeAction> actions = new ArrayList<>();
+ if (supportsLayoutMode(SWT.VERTICAL))
+ actions.add(new SetLayoutModeAction(SWT.VERTICAL));
+ if (supportsLayoutMode(SWT.HORIZONTAL))
+ actions.add(new SetLayoutModeAction(SWT.HORIZONTAL));
+ if (supportsLayoutMode(LAYOUT_AUTOMATIC))
+ actions.add(new SetLayoutModeAction(LAYOUT_AUTOMATIC));
+ if (supportsLayoutMode(SWT.NONE))
+ actions.add(new SetLayoutModeAction(SWT.NONE));
+ int size = actions.size();
+ if (size > 1)
+ setLayoutModeActions = actions.toArray(
+ new SetLayoutModeAction[size]);
+ }
+
+ private int getDefaultLayoutMode()
+ {
+ if (supportsLayoutMode(LAYOUT_AUTOMATIC))
+ return LAYOUT_AUTOMATIC;
+ if (supportsLayoutMode(SWT.VERTICAL))
+ return SWT.VERTICAL;
+ if (supportsLayoutMode(SWT.HORIZONTAL))
+ return SWT.HORIZONTAL;
+ if (supportsLayoutMode(SWT.NONE))
+ return SWT.NONE;
+ throw new AssertionError();
+ }
+
+ private void adjustLayout(int layoutMode)
+ {
+ if (layoutMode == SWT.NONE)
+ return;
+
+ Point size = sashForm.getParent().getParent().getSize();
+ if (size.x == 0 || size.y == 0)
+ return;
+
+ int orientation = (layoutMode == LAYOUT_AUTOMATIC) ? (size.x > size.y
+ ? SWT.HORIZONTAL : SWT.VERTICAL) : layoutMode;
+
+ if (sashForm.getOrientation() == orientation && layoutAdjusted)
+ return;
+
+ if (sashForm.getOrientation() != orientation)
+ {
+ saveSashFormWeights();
+ sashForm.setOrientation(orientation);
+ }
+
+ restoreSashFormWeights();
+ layoutAdjusted = true;
+ }
+
+ private void saveSashFormWeights()
+ {
+ if (!layoutAdjusted)
+ return;
+
+ int[] weights = sashForm.getWeights();
+ if (weights.length < 2)
+ return;
+
+ if (sashForm.getOrientation() == SWT.HORIZONTAL)
+ horizontalWeights = weights;
+ else
+ verticalWeights = weights;
+ }
+
+ private void restoreSashFormWeights()
+ {
+ int[] weights = (sashForm.getOrientation() == SWT.HORIZONTAL)
+ ? horizontalWeights : verticalWeights;
+ if (weights != null)
+ sashForm.setWeights(weights);
+ }
+
+ private void initContextMenu(Control parent, IMenuListener listener,
+ String menuId, ISelectionProvider selectionProvider)
+ {
+ MenuManager manager = new MenuManager();
+ manager.setRemoveAllWhenShown(true);
+ manager.addMenuListener(listener);
+ Menu menu = manager.createContextMenu(parent);
+ parent.setMenu(menu);
+ getSite().registerContextMenu(menuId, manager, selectionProvider);
+ }
+
+ private static Object getSelectedElement(ISelection selection)
+ {
+ if (selection instanceof IStructuredSelection)
+ {
+ IStructuredSelection ss = (IStructuredSelection)selection;
+ if (ss.size() == 1)
+ return ss.getFirstElement();
+ }
+ return null;
+ }
+
+ private static Set<TypeHierarchyKind> symmetricDifference(
+ Set<TypeHierarchyKind> kindsA, Set<TypeHierarchyKind> kindsB)
+ {
+ Set<TypeHierarchyKind> result = EnumSet.copyOf(kindsA);
+ for (TypeHierarchyKind kind : kindsB)
+ {
+ if (!result.add(kind))
+ result.remove(kind);
+ }
+ return result;
+ }
+
+ private static String toString(int[] values)
+ {
+ if (values == null)
+ return null;
+ StringBuilder sb = new StringBuilder();
+ int length = values.length;
+ for (int i = 0; i < length; i++)
+ {
+ sb.append(values[i]);
+ if (i < length - 1)
+ sb.append(',');
+ }
+ return sb.toString();
+ }
+
+ private static int[] toIntArray(String s) throws NumberFormatException
+ {
+ if (s == null)
+ return null;
+ String[] values = s.split(","); //$NON-NLS-1$
+ int length = values.length;
+ int[] result = new int[length];
+ for (int i = 0; i < length; i++)
+ {
+ result[i] = Integer.parseInt(values[i]);
+ }
+ return result;
+ }
+
+ /**
+ * Represents an entry of the type hierarchy view history list.
+ */
+ protected abstract static class HistoryEntry
+ {
+ private final Object[] inputElements;
+
+ /**
+ * Constructs a history entry for the given input elements.
+ * Clients <b>must not</b> modify the given array afterwards.
+ *
+ * @param inputElements never <code>null</code>; never empty
+ */
+ protected HistoryEntry(Object[] inputElements)
+ {
+ if (inputElements.length == 0)
+ throw new AssertionError();
+ this.inputElements = inputElements;
+ }
+
+ /**
+ * Returns the input elements for this history entry.
+ *
+ * @return the input elements (never <code>null</code>; never empty).
+ * Clients <b>must not</b> modify the returned array.
+ */
+ public final Object[] getInputElements()
+ {
+ return inputElements;
+ }
+
+ @Override
+ public boolean equals(Object obj)
+ {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ HistoryEntry other = (HistoryEntry)obj;
+ if (!Arrays.equals(inputElements, other.inputElements))
+ return false;
+ return true;
+ }
+
+ @Override
+ public int hashCode()
+ {
+ return Arrays.hashCode(inputElements);
+ }
+
+ /**
+ * Returns a user-readable text label for this history entry.
+ * <p>
+ * Default implementation composes a label based on labels for
+ * input elements. It invokes {@link #getElementLabel(Object)}
+ * to obtain a label for an input element.
+ * </p>
+ *
+ * @return the text label of the history entry (never <code>null</code>)
+ */
+ public String getLabel()
+ {
+ switch (inputElements.length)
+ {
+ case 1:
+ return MessageFormat.format(
+ Messages.TypeHierarchyViewPart_History_entry_label__0,
+ getElementLabel(inputElements[0]));
+ case 2:
+ return MessageFormat.format(
+ Messages.TypeHierarchyViewPart_History_entry_label__0__1,
+ getElementLabel(inputElements[0]), getElementLabel(
+ inputElements[1]));
+ default:
+ return MessageFormat.format(
+ Messages.TypeHierarchyViewPart_History_entry_label__0__1_more,
+ getElementLabel(inputElements[0]), getElementLabel(
+ inputElements[1]));
+ }
+ }
+
+ /**
+ * Returns a user-readable text label for the given element.
+ *
+ * @param element the given element
+ * @return the text label of the element (never <code>null</code>)
+ */
+ protected abstract String getElementLabel(Object element);
+
+ /**
+ * Returns an image descriptor for this history entry.
+ * <p>
+ * Default implementation always returns <code>null</code>.
+ * </p>
+ *
+ * @return the image descriptor of the history entry
+ * (may be <code>null</code>)
+ */
+ public ImageDescriptor getImageDescriptor()
+ {
+ return null;
+ }
+ }
+
+ /**
+ * Helper for opening editors on the viewer's selection.
+ */
+ protected class OpenEditorHelper
+ extends OpenAndLinkWithEditorHelper
+ {
+ /**
+ * Creates a new helper for the given viewer.
+ *
+ * @param viewer the viewer
+ */
+ public OpenEditorHelper(StructuredViewer viewer)
+ {
+ super(viewer);
+ }
+
+ @Override
+ protected void activate(ISelection selection)
+ {
+ Object element = getSelectedElement(selection);
+ if (element != null)
+ {
+ try
+ {
+ revealInEditor(element, true, false);
+ }
+ catch (PartInitException e)
+ {
+ // cannot happen: may not open a new editor
+ }
+ }
+ }
+
+ @Override
+ protected void open(ISelection selection, boolean activate)
+ {
+ Object element = getSelectedElement(selection);
+ if (element != null)
+ {
+ try
+ {
+ revealInEditor(element, activate, true);
+ }
+ catch (PartInitException e)
+ {
+ ErrorDialog.openError(getSite().getShell(),
+ Messages.TypeHierarchyViewPart_Open_selected_element,
+ Messages.TypeHierarchyViewPart_Error_opening_editor,
+ e.getStatus());
+ }
+ }
+ }
+ }
+
+ private class FocusOnSelectionAction
+ extends BaseSelectionListenerAction
+ {
+ FocusOnSelectionAction()
+ {
+ super(
+ Messages.TypeHierarchyViewPart_Focus_on_selection_action_text);
+ setToolTipText(
+ Messages.TypeHierarchyViewPart_Focus_on_selection_action_tooltip);
+ }
+
+ @Override
+ public void run()
+ {
+ setInputElements(getStructuredSelection().toArray());
+ }
+
+ @Override
+ protected boolean updateSelection(IStructuredSelection selection)
+ {
+ return !selection.isEmpty() && arePossibleInputElements(
+ selection.toArray());
+ }
+ }
+
+ private class RefreshAction
+ extends Action
+ {
+ RefreshAction()
+ {
+ setText(Messages.TypeHierarchyViewPart_Refresh_action_text);
+ setToolTipText(
+ Messages.TypeHierarchyViewPart_Refresh_action_tooltip);
+ setImageDescriptor(Activator.getImageDescriptor(
+ Activator.IMG_ELCL_REFRESH));
+ setDisabledImageDescriptor(Activator.getImageDescriptor(
+ Activator.IMG_DLCL_REFRESH));
+ }
+
+ @Override
+ public void run()
+ {
+ refresh();
+ }
+ }
+
+ private class SetHierarchyKindAction
+ extends Action
+ {
+ final TypeHierarchyKind kind;
+
+ SetHierarchyKindAction(TypeHierarchyKind kind)
+ {
+ super(null, AS_RADIO_BUTTON);
+ this.kind = kind;
+ switch (kind)
+ {
+ case TYPES:
+ setText(
+ Messages.TypeHierarchyViewPart_Show_type_hierarchy_action_text);
+ setToolTipText(
+ Messages.TypeHierarchyViewPart_Show_type_hierarchy_action_tooltip);
+ setImageDescriptor(Activator.getImageDescriptor(
+ Activator.IMG_ELCL_TH_TYPES));
+ break;
+ case SUPERTYPES:
+ setText(
+ Messages.TypeHierarchyViewPart_Show_supertype_hierarchy_action_text);
+ setToolTipText(
+ Messages.TypeHierarchyViewPart_Show_supertype_hierarchy_action_tooltip);
+ setImageDescriptor(Activator.getImageDescriptor(
+ Activator.IMG_ELCL_TH_SUPERTYPES));
+ break;
+ case SUBTYPES:
+ setText(
+ Messages.TypeHierarchyViewPart_Show_subtype_hierarchy_action_text);
+ setToolTipText(
+ Messages.TypeHierarchyViewPart_Show_subtype_hierarchy_action_tooltip);
+ setImageDescriptor(Activator.getImageDescriptor(
+ Activator.IMG_ELCL_TH_SUBTYPES));
+ break;
+ }
+ if (kind == getHierarchyKind())
+ setChecked(true);
+ }
+
+ @Override
+ public void run()
+ {
+ setHierarchyKind(kind);
+ }
+ }
+
+ private class SetLayoutModeAction
+ extends Action
+ {
+ final int layoutMode;
+
+ SetLayoutModeAction(int layoutMode)
+ {
+ super(null, AS_RADIO_BUTTON);
+ this.layoutMode = layoutMode;
+ switch (layoutMode)
+ {
+ case SWT.VERTICAL:
+ setText(
+ Messages.TypeHierarchyViewPart_Layout_vertical_action_text);
+ setToolTipText(
+ Messages.TypeHierarchyViewPart_Layout_vertical_action_tooltip);
+ setImageDescriptor(Activator.getImageDescriptor(
+ Activator.IMG_ELCL_LAYOUT_VERTICAL));
+ break;
+ case SWT.HORIZONTAL:
+ setText(
+ Messages.TypeHierarchyViewPart_Layout_horizontal_action_text);
+ setToolTipText(
+ Messages.TypeHierarchyViewPart_Layout_horizontal_action_tooltip);
+ setImageDescriptor(Activator.getImageDescriptor(
+ Activator.IMG_ELCL_LAYOUT_HORIZONTAL));
+ break;
+ case LAYOUT_AUTOMATIC:
+ setText(
+ Messages.TypeHierarchyViewPart_Layout_automatic_action_text);
+ setToolTipText(
+ Messages.TypeHierarchyViewPart_Layout_automatic_action_tooltip);
+ setImageDescriptor(Activator.getImageDescriptor(
+ Activator.IMG_ELCL_LAYOUT_AUTOMATIC));
+ break;
+ case SWT.NONE:
+ setText(
+ Messages.TypeHierarchyViewPart_Layout_single_action_text);
+ setToolTipText(
+ Messages.TypeHierarchyViewPart_Layout_single_action_tooltip);
+ setImageDescriptor(Activator.getImageDescriptor(
+ Activator.IMG_ELCL_LAYOUT_SINGLE));
+ break;
+ default:
+ throw new IllegalArgumentException();
+ }
+ if (layoutMode == getLayoutMode())
+ setChecked(true);
+ }
+
+ @Override
+ public void run()
+ {
+ setLayoutMode(layoutMode);
+ }
+ }
+}
diff --git a/org.eclipse.handly.ui/src/org/eclipse/handly/ui/typehierarchy/messages.properties b/org.eclipse.handly.ui/src/org/eclipse/handly/ui/typehierarchy/messages.properties
new file mode 100644
index 0000000..60eb10b
--- /dev/null
+++ b/org.eclipse.handly.ui/src/org/eclipse/handly/ui/typehierarchy/messages.properties
@@ -0,0 +1,39 @@
+###############################################################################
+# Copyright (c) 2021 1C-Soft LLC.
+#
+# This program and the accompanying materials are made available under
+# the terms of the Eclipse Public License 2.0 which is available at
+# https://www.eclipse.org/legal/epl-2.0/
+#
+# SPDX-License-Identifier: EPL-2.0
+#
+# Contributors:
+# Vladimir Piskarev (1C) - initial API and implementation
+###############################################################################
+
+TypeHierarchyViewPart_0__items_selected={0} items selected
+TypeHierarchyViewPart_Error_opening_editor=An error occurred while opening the editor.
+TypeHierarchyViewPart_Focus_on_selection_action_text=Fo&cus on Selection
+TypeHierarchyViewPart_Focus_on_selection_action_tooltip=Focus on Selection
+TypeHierarchyViewPart_History_entry_label__0={0}
+TypeHierarchyViewPart_History_entry_label__0__1={0}, {1}
+TypeHierarchyViewPart_History_entry_label__0__1_more={0}, {1}, ...
+TypeHierarchyViewPart_Layout_automatic_action_text=&Automatic
+TypeHierarchyViewPart_Layout_automatic_action_tooltip=Automatic
+TypeHierarchyViewPart_Layout_horizontal_action_text=&Horizontal
+TypeHierarchyViewPart_Layout_horizontal_action_tooltip=Horizontal
+TypeHierarchyViewPart_Layout_menu=&Layout
+TypeHierarchyViewPart_Layout_single_action_text=&Hierarchy Only
+TypeHierarchyViewPart_Layout_single_action_tooltip=Hierarchy Only
+TypeHierarchyViewPart_Layout_vertical_action_text=&Vertical
+TypeHierarchyViewPart_Layout_vertical_action_tooltip=Vertical
+TypeHierarchyViewPart_No_hierarchy_to_display=No type hierarchy to display.
+TypeHierarchyViewPart_Open_selected_element=Open Selected Element
+TypeHierarchyViewPart_Refresh_action_text=Re&fresh View
+TypeHierarchyViewPart_Refresh_action_tooltip=Refresh View
+TypeHierarchyViewPart_Show_subtype_hierarchy_action_text=Su&btype Hierarchy
+TypeHierarchyViewPart_Show_subtype_hierarchy_action_tooltip=Show the Subtype Hierarchy
+TypeHierarchyViewPart_Show_supertype_hierarchy_action_text=Su&pertype Hierarchy
+TypeHierarchyViewPart_Show_supertype_hierarchy_action_tooltip=Show the Supertype Hierarchy
+TypeHierarchyViewPart_Show_type_hierarchy_action_text=&Type Hierarchy
+TypeHierarchyViewPart_Show_type_hierarchy_action_tooltip=Show the Type Hierarchy
diff --git a/org.eclipse.handly.ui/src/org/eclipse/handly/ui/typehierarchy/package-info.java b/org.eclipse.handly.ui/src/org/eclipse/handly/ui/typehierarchy/package-info.java
new file mode 100644
index 0000000..d45a073
--- /dev/null
+++ b/org.eclipse.handly.ui/src/org/eclipse/handly/ui/typehierarchy/package-info.java
@@ -0,0 +1,20 @@
+/*******************************************************************************
+ * Copyright (c) 2021 1C-Soft LLC.
+ *
+ * This program and the accompanying materials are made available under
+ * the terms of the Eclipse Public License 2.0 which is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * Vladimir Piskarev (1C) - initial API and implementation
+ *******************************************************************************/
+/**
+ * Provides a framework for type hierarchy views.
+ * <p>
+ * See <code>org.eclipse.handly.internal.examples.adapter.ui.typehierarchy</code>
+ * for usage example.
+ * </p>
+ */
+package org.eclipse.handly.ui.typehierarchy;