package com.redhat.ceylon.eclipse.code.outline;
import static com.redhat.ceylon.eclipse.code.editor.Navigation.gotoDeclaration;
import static com.redhat.ceylon.eclipse.code.outline.HierarchyMode.HIERARCHY;
import static com.redhat.ceylon.eclipse.code.outline.HierarchyMode.SUBTYPES;
import static com.redhat.ceylon.eclipse.code.outline.HierarchyMode.SUPERTYPES;
import static com.redhat.ceylon.eclipse.code.outline.HierarchyView.showHierarchyView;
import static com.redhat.ceylon.eclipse.ui.CeylonPlugin.PLUGIN_ID;
import static com.redhat.ceylon.eclipse.ui.CeylonPlugin.imageRegistry;
import static com.redhat.ceylon.eclipse.ui.CeylonResources.CEYLON_HIER;
import static com.redhat.ceylon.eclipse.ui.CeylonResources.CEYLON_SUB;
import static com.redhat.ceylon.eclipse.ui.CeylonResources.CEYLON_SUP;
import static com.redhat.ceylon.eclipse.util.EditorUtil.triggersBinding;
import static com.redhat.ceylon.eclipse.util.Nodes.getReferencedDeclaration;
import java.util.StringTokenizer;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.Separator;
import org.eclipse.jface.bindings.TriggerSequence;
import org.eclipse.jface.dialogs.IDialogSettings;
import org.eclipse.jface.viewers.StyledString;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.events.KeyListener;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;
import org.eclipse.swt.widgets.ToolBar;
import org.eclipse.swt.widgets.ToolItem;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.dialogs.PreferencesUtil;
import com.redhat.ceylon.compiler.typechecker.tree.Node;
import com.redhat.ceylon.eclipse.code.editor.CeylonEditor;
import com.redhat.ceylon.eclipse.code.preferences.CeylonFiltersPreferencePage;
import com.redhat.ceylon.eclipse.code.preferences.CeylonOutlinesPreferencePage;
import com.redhat.ceylon.eclipse.ui.CeylonPlugin;
import com.redhat.ceylon.eclipse.ui.CeylonResources;
import com.redhat.ceylon.eclipse.util.EditorUtil;
import com.redhat.ceylon.eclipse.util.Highlights;
import com.redhat.ceylon.eclipse.util.ModelProxy;
import com.redhat.ceylon.ide.common.search.FindContainerVisitor;
import com.redhat.ceylon.model.typechecker.model.Declaration;
import com.redhat.ceylon.model.typechecker.model.Referenceable;
public class HierarchyPopup extends TreeViewPopup {
private static final String EXCLUDE_ORACLE_JDK = "excludeOracleJDK";
private static final String EXCLUDE_JDK = "excludeJDK";
private static final Image SUB_IMAGE = imageRegistry().get(CEYLON_SUB);
private static final Image SUP_IMAGE = imageRegistry().get(CEYLON_SUP);
private static final Image HIER_IMAGE = imageRegistry().get(CEYLON_HIER);
private final class ChangeViewListener implements KeyListener {
@Override
public void keyReleased(KeyEvent e) {}
@Override
public void keyPressed(KeyEvent e) {
if (triggersBinding(e, getCommandBinding())) {
HierarchyMode nextMode =
contentProvider.getMode().next();
contentProvider.setMode(nextMode);
switchMode();
e.doit=false;
}
if (triggersBinding(e, hierarchyBinding)) {
try {
showHierarchyView()
.focusOn(contentProvider.getDeclaration());
close();
}
catch (PartInitException e1) {
e1.printStackTrace();
}
}
}
}
private CeylonHierarchyLabelProvider labelProvider;
private CeylonHierarchyContentProvider contentProvider;
private Label iconLabel;
private TriggerSequence hierarchyBinding;
private ToolItem button1;
private ToolItem button2;
private ToolItem button3;
public HierarchyPopup(CeylonEditor editor, Shell shell, int shellStyle) {
super(shell, shellStyle, PLUGIN_ID + ".editor.hierarchy", editor);
hierarchyBinding =
EditorUtil.getCommandBinding(PLUGIN_ID +
".action.showInHierarchyView");
setInfoText(getStatusFieldText());
}
private void switchMode() {
updateStatusFieldText();
updateTitle();
updateIcon();
updateButtonSelection();
update();
}
/*@Override
protected void adjustBounds() {
Rectangle bounds = getShell().getBounds();
int h = bounds.height;
if (h>400) {
bounds.height=400;
bounds.y = bounds.y + (h-400)/3;
getShell().setBounds(bounds);
}
}*/
@Override
protected TreeViewer createTreeViewer(Composite parent) {
final Tree tree = new Tree(parent, SWT.SINGLE);
GridData gd = new GridData(GridData.FILL_BOTH);
gd.heightHint = tree.getItemHeight() * 12;
tree.setLayoutData(gd);
final TreeViewer treeViewer = new TreeViewer(tree);
IDialogSettings dialogSettings = getDialogSettings();
contentProvider =
new CeylonHierarchyContentProvider(getEditor().getSite(),
"Quick Hierarchy",
dialogSettings.getBoolean(EXCLUDE_JDK),
dialogSettings.get(EXCLUDE_ORACLE_JDK)==null ||
dialogSettings.getBoolean(EXCLUDE_ORACLE_JDK));
labelProvider = new CeylonHierarchyLabelProvider() {
@Override
Font getFont() {
return treeViewer.getControl().getFont();
}
@Override
String getPrefix() {
return getFilterText().getText();
}
@Override
String getViewInterfacesShortcut() {
TriggerSequence binding = getCommandBinding();
return binding==null ? "" :
" (" + binding.format() + " to view)";
}
@Override
boolean isShowingRefinements() {
return contentProvider.isShowingRefinements();
}
};
treeViewer.setContentProvider(contentProvider);
treeViewer.setLabelProvider(labelProvider);
treeViewer.addFilter(new HierarchyNamePatternFilter(getFilterText()));
treeViewer.setAutoExpandLevel(getDefaultLevel());
tree.addKeyListener(new ChangeViewListener());
tree.setFont(CeylonPlugin.getOutlineFont());
// treeViewer.setUseHashlookup(false);
return treeViewer;
}
@Override
protected Text createFilterText(Composite parent) {
Text result = super.createFilterText(parent);
result.addKeyListener(new ChangeViewListener());
return result;
}
@Override
protected String getStatusFieldText() {
String selectHint =
EditorUtil.getEnterBinding() +
" to open";
TriggerSequence binding = getCommandBinding();
if (binding==null) {
return selectHint;
}
String viewHint =
hierarchyBinding==null ? "" :
hierarchyBinding.format() +
" to show in hierarchy view \u00b7 ";
switch (contentProvider.getMode()) {
case SUBTYPES:
return viewHint + binding.format() +
" to show hierarchy" +
" \u00b7 " + selectHint;
case SUPERTYPES:
return viewHint + binding.format() +
" to show subtypes" +
" \u00b7 " + selectHint;
case HIERARCHY:
return viewHint + binding.format() +
" to show supertypes" +
" \u00b7 " + selectHint;
default:
throw new RuntimeException();
}
}
private String getTitleText() {
return contentProvider.getDescription();
}
@Override
protected StyledString styleTitle(final StyledText title) {
StyledString result = new StyledString();
StringTokenizer tokens =
new StringTokenizer(title.getText(), "\u2014", false);
styleDescription(title, result, tokens.nextToken());
result.append("\u2014");
String rest = tokens.nextToken();
int loc = rest.indexOf(" of ") + 4;
result.append(rest.substring(0, loc));
Highlights.styleFragment(result,
rest.substring(loc), false, null,
CeylonPlugin.getOutlineFont());
return result;
}
@Override
protected Control createTitleControl(Composite parent) {
getPopupLayout().copy()
.numColumns(4)
.spacing(6, 6)
.applyTo(parent);
iconLabel = new Label(parent, SWT.NONE);
super.createTitleControl(parent);
updateIcon();
createModeButtons(parent);
return null;
}
private void createModeButtons(Composite parent) {
ToolBar toolBar = new ToolBar(parent, SWT.FLAT);
button1 = new ToolItem(toolBar, SWT.CHECK);
button1.setImage(HIER_IMAGE);
button1.setToolTipText("Show Hierarchy");
button2 = new ToolItem(toolBar, SWT.CHECK);
button2.setImage(SUP_IMAGE);
button2.setToolTipText("Show Supertypes/Generalizations");
button3 = new ToolItem(toolBar, SWT.CHECK);
button3.setImage(SUB_IMAGE);
button3.setToolTipText("Show Subtypes/Refinements");
updateButtonSelection();
button1.addSelectionListener(new SelectionListener() {
@Override
public void widgetSelected(SelectionEvent e) {
if (button1.getSelection()) {
contentProvider.setMode(HIERARCHY);
switchMode();
}
else {
button1.setSelection(true);
}
}
@Override
public void widgetDefaultSelected(SelectionEvent e) {}
});
button2.addSelectionListener(new SelectionListener() {
@Override
public void widgetSelected(SelectionEvent e) {
if (button2.getSelection()) {
contentProvider.setMode(SUPERTYPES);
switchMode();
}
else {
button2.setSelection(true);
}
}
@Override
public void widgetDefaultSelected(SelectionEvent e) {}
});
button3.addSelectionListener(new SelectionListener() {
@Override
public void widgetSelected(SelectionEvent e) {
if (button3.getSelection()) {
contentProvider.setMode(SUBTYPES);
switchMode();
}
else {
button3.setSelection(true);
}
}
@Override
public void widgetDefaultSelected(SelectionEvent e) {}
});
}
private void updateButtonSelection() {
button1.setSelection(contentProvider==null || contentProvider.getMode()==HIERARCHY);
button2.setSelection(contentProvider!=null && contentProvider.getMode()==SUPERTYPES);
button3.setSelection(contentProvider!=null && contentProvider.getMode()==SUBTYPES);
}
public void updateTitle() {
setTitleText(getTitleText());
}
public void updateIcon() {
iconLabel.setImage(getIcon());
}
private Image getIcon() {
String img = contentProvider==null ?
CEYLON_HIER : contentProvider.getMode().image();
return imageRegistry.get(img);
}
@Override
protected String getId() {
return CeylonPlugin.PLUGIN_ID + ".QuickHierarchy";
}
@Override
public void setInput(Object information) {
Declaration dec = getSelectedDeclaration();
ModelProxy input =
dec == null ? null :
new ModelProxy(dec);
inputChanged(input, input);
updateTitle();
}
private Declaration getSelectedDeclaration() {
Node selectedNode = getEditor().getSelectedNode();
Referenceable declaration =
getReferencedDeclaration(selectedNode);
if (declaration==null) {
FindContainerVisitor fcv =
new FindContainerVisitor(selectedNode);
fcv.visit(getEditor().getParseController().getLastCompilationUnit());
Node node = fcv.getStatementOrArgument();
if (node instanceof com.redhat.ceylon.compiler.typechecker.tree.Tree.Declaration) {
declaration = ((com.redhat.ceylon.compiler.typechecker.tree.Tree.Declaration) node).getDeclarationModel();
}
}
if (declaration instanceof Declaration) {
return (Declaration) declaration;
}
else {
return null;
}
}
@Override
protected void gotoSelectedElement() {
Object selectedElement = getSelectedElement();
if (selectedElement instanceof CeylonHierarchyNode) {
dispose();
CeylonHierarchyNode node =
(CeylonHierarchyNode)
selectedElement;
gotoDeclaration(node.getDeclaration());
}
}
@Override
protected void reveal() {
if (contentProvider.isEmpty()) return;
int depth;
if (contentProvider.getMode()==HIERARCHY) {
depth = contentProvider.getDepthInHierarchy();
}
else {
depth = 1;
}
if (contentProvider.isVeryAbstractType()) {
depth+=1;
}
else {
depth+=3;
}
getTreeViewer().expandToLevel(depth);
}
@Override
protected int getDefaultLevel() {
return 1;
}
@Override
protected void fillViewMenu(IMenuManager viewMenu) {
super.fillViewMenu(viewMenu);
viewMenu.add(new Separator());
final Action javaSDKAction =
new Action("Exclude Java SDK",
IAction.AS_CHECK_BOX) {
@Override
public void run() {
boolean checked = isChecked();
contentProvider.setExcludeJDK(checked);
getDialogSettings()
.put(EXCLUDE_JDK, checked);
update();
}
};
javaSDKAction.setChecked(contentProvider.isExcludeJDK());
viewMenu.add(javaSDKAction);
final Action oracleSDKAction =
new Action("Exclude Java SDK Internals",
IAction.AS_CHECK_BOX) {
@Override
public void run() {
boolean checked = isChecked();
contentProvider.setExcludeOracleJDK(checked);
getDialogSettings()
.put(EXCLUDE_ORACLE_JDK, checked);
update();
}
};
oracleSDKAction.setChecked(contentProvider.isExcludeOracleJDK());
viewMenu.add(oracleSDKAction);
viewMenu.add(new Separator());
Action configureAction =
new Action("Configure Labels...",
CeylonPlugin.imageRegistry()
.getDescriptor(CeylonResources.CONFIG_LABELS)) {
@Override
public void run() {
PreferencesUtil.createPreferenceDialogOn(
getShell(),
CeylonOutlinesPreferencePage.ID,
new String[] {
CeylonOutlinesPreferencePage.ID,
CeylonPlugin.COLORS_AND_FONTS_PAGE_ID,
CeylonFiltersPreferencePage.ID
},
null).open();
getTreeViewer().getTree()
.setFont(CeylonPlugin.getOutlineFont());
getTreeViewer().refresh();
}
};
viewMenu.add(configureAction);
}
}