package damp.ekeko.snippets.gui;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import org.eclipse.jface.text.TextViewer;
import org.eclipse.jface.viewers.DoubleClickEvent;
import org.eclipse.jface.viewers.IDoubleClickListener;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.TreeViewerColumn;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.SashForm;
import org.eclipse.swt.custom.StyleRange;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.MenuItem;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.swt.widgets.TreeColumn;
import org.eclipse.swt.widgets.TreeItem;
import clojure.lang.IFn;
import damp.ekeko.snippets.EkekoSnippetsPlugin;
import damp.ekeko.snippets.data.SnippetOperator;
import damp.ekeko.snippets.data.TemplateGroup;
public class TemplateGroupViewer extends Composite {
public static IFn FN_SNIPPET_VALUE_FOR_IDENTIFIER;
public Object getValueForIdentifierInTemplate(Object identifier) {
return FN_SNIPPET_VALUE_FOR_IDENTIFIER.invoke(cljTemplate, identifier);
}
private TextViewer textViewerSnippet;
private TreeViewer snippetTreeViewer;
private TreeViewerColumn snippetKindCol;
private TreeViewerColumn snippetPropCol;
private TreeViewerColumn snippetNodeCol;
private TreeViewerColumn snippetDirectivesCol;
private List<TemplateGroupViewerNodeSelectionListener> nodeSelectionListeners;
private List<TemplateGroupViewerNodeDoubleClickListener> nodeDoubleClickListeners;
private TemplateGroup jGroup;
private Object cljTemplate, cljNode;
private TreeViewerColumn snippetElementCol;
//private TextViewer textViewerNode;
private List<StyleRange> hyperlinks;
private StyleRange lastUnderlined;
private TemplateEditor parentTemplateEditor;
public void setParentTemplateEditor(TemplateEditor editor) {
parentTemplateEditor = editor;
}
public TemplateEditor getParentTemplateEditor() {
return parentTemplateEditor;
}
public TemplateGroupViewer(Composite parent, int style) {
super(parent, SWT.NONE);
nodeSelectionListeners = new LinkedList<TemplateGroupViewerNodeSelectionListener>();
nodeDoubleClickListeners = new LinkedList<TemplateGroupViewerNodeDoubleClickListener>();
hyperlinks = new LinkedList<StyleRange>();
//Composite composite = this;
//gridLayout.marginWidth = 0;
//gridLayout.marginHeight = 0;
//composite.setLayout(gridLayout);
this.setLayout(new FillLayout());
SashForm composite = new SashForm(this, SWT.VERTICAL);
textViewerSnippet = new TextViewer(composite, SWT.BORDER | SWT.H_SCROLL | SWT.V_SCROLL);
final StyledText styledText = textViewerSnippet.getTextWidget();
//GridData gd_styledText = new GridData(SWT.FILL, SWT.TOP, true, false, 1, 1);
//gd_styledText.heightHint = 100;
//styledText.setLayoutData(gd_styledText);
textViewerSnippet.setEditable(false);
styledText.setFont(EkekoSnippetsPlugin.getEditorFont());
styledText.setCaret(null);
//Label label = new Label(getShell(), SWT.SINGLE);
//RGB background = label.getBackground().getRGB();
//label.dispose();
/*
textViewerNode = new TextViewer(composite, SWT.NONE | SWT.WRAP | SWT.READ_ONLY | SWT.NO_FOCUS);
StyledText textViewerNodeText = textViewerNode.getTextWidget();
GridData gd_styledTextNode = new GridData(SWT.FILL, SWT.TOP, true, false, 1, 1);
//gd_styledTextNode.heightHint = 100;
textViewerNodeText.setLayoutData(gd_styledTextNode);
//textViewerNodeText.setFont(EkekoSnippetsPlugin.getEditorFont());
textViewerNodeText.setBackground(getDisplay().getSystemColor(SWT.COLOR_WIDGET_BACKGROUND));
textViewerNodeText.setCaret(null);
*/
snippetTreeViewer = new TreeViewer(composite, SWT.BORDER | SWT.H_SCROLL | SWT.V_SCROLL | SWT.MULTI | SWT.FULL_SELECTION);
snippetTreeViewer.setAutoExpandLevel(1);
Tree treeSnippet = snippetTreeViewer.getTree();
treeSnippet.setHeaderVisible(true);
treeSnippet.setLinesVisible(true);
//treeSnippet.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1));
snippetNodeCol = new TreeViewerColumn(snippetTreeViewer, SWT.NONE);
TreeColumn trclmnNode = snippetNodeCol.getColumn();
trclmnNode.setWidth(150);
trclmnNode.setText("Element");
snippetElementCol = new TreeViewerColumn(snippetTreeViewer, SWT.NONE);
TreeColumn templateElementColCol = snippetElementCol.getColumn();
templateElementColCol.setWidth(150);
templateElementColCol.setText("Textual Representation");
snippetKindCol = new TreeViewerColumn(snippetTreeViewer, SWT.NONE);
TreeColumn snippetKindColCol = snippetKindCol.getColumn();
snippetKindColCol.setWidth(150);
snippetKindColCol.setText("Element Kind");
snippetPropCol = new TreeViewerColumn(snippetTreeViewer, SWT.NONE);
TreeColumn trclmnProperty = snippetPropCol.getColumn();
trclmnProperty.setWidth(150);
trclmnProperty.setText("Description");
snippetDirectivesCol = new TreeViewerColumn(snippetTreeViewer, SWT.NONE);
TreeColumn snippetDirectivesColCol = snippetDirectivesCol.getColumn();
snippetDirectivesColCol.setWidth(150);
snippetDirectivesColCol.setText("Directives");
composite.setWeights(new int[] {144, 153});
snippetTreeViewer.setContentProvider(new TemplateTreeContentProvider());
treeSnippet.addListener(SWT.Selection, new Listener() {
public void handleEvent(Event e) {
onNodeSelectionInternal();
}
});
// Set context menu for selected tree nodes
Menu popupMenu = new Menu(treeSnippet);
MenuItem deleteItem = new MenuItem(popupMenu, SWT.NONE);
deleteItem.setText("Remove node");
deleteItem.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
List<Object> nodes = getSelectedSnippetNodes();
cljTemplate = jGroup.getSnippet(nodes.get(0));
for (Object node : nodes) {
Object snippet = jGroup.getSnippet(node);
jGroup.applyOperator(SnippetOperator.operatorFromId("remove-node"),
SnippetOperator.getOperands(jGroup, snippet, node, SnippetOperator.operatorFromId("remove-node")));
}
updateWidgets();
parentTemplateEditor.becomeDirty();
}
});
MenuItem replaceByWildCardItem = new MenuItem(popupMenu, SWT.NONE);
replaceByWildCardItem.setText("Replace by wildcard");
replaceByWildCardItem.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
List<Object> nodes = getSelectedSnippetNodes();
cljTemplate = jGroup.getSnippet(nodes.get(0));
for (Object node : nodes) {
Object snippet = jGroup.getSnippet(node);
jGroup.applyOperator(SnippetOperator.operatorFromId("replace-by-wildcard"),
SnippetOperator.getOperands(jGroup, snippet, node, SnippetOperator.operatorFromId("replace-by-wildcard")));
}
updateWidgets();
parentTemplateEditor.becomeDirty();
}
});
treeSnippet.setMenu(popupMenu);
snippetTreeViewer.addDoubleClickListener(new IDoubleClickListener() {
@Override
public void doubleClick(DoubleClickEvent event) {
onNodeDoubleClickInternal();
}
});
styledText.setBlockSelection(false);
styledText.addListener(SWT.MouseMove, new Listener() {
@Override
public void handleEvent(Event event) {
try {
int offset = styledText.getOffsetAtLocation(new Point(event.x, event.y));
StyleRange smallestEncompassing = getSmallestEncompassingHyperlink(offset);
if(lastUnderlined != null) {
if(lastUnderlined.equals(smallestEncompassing))
return;
StyleRange[] styleRanges = styledText.getStyleRanges(lastUnderlined.start, lastUnderlined.length);
for(StyleRange range : styleRanges) {
range.underline = false;
}
styledText.replaceStyleRanges(lastUnderlined.start, lastUnderlined.length, styleRanges);
lastUnderlined = null;
}
if(smallestEncompassing != null) {
lastUnderlined = smallestEncompassing;
StyleRange[] styleRanges = styledText.getStyleRanges(smallestEncompassing.start, smallestEncompassing.length);
for(StyleRange range : styleRanges) {
range.underlineStyle = SWT.UNDERLINE_LINK;
range.underline = true;
}
styledText.replaceStyleRanges(smallestEncompassing.start, smallestEncompassing.length, styleRanges);
//styledText.redraw();
}
}
catch(IllegalArgumentException e) {
//when mouse cursor out on boundary of widget
}
}
});
styledText.addListener(SWT.MouseDown, new Listener() {
@Override
public void handleEvent(Event event) {
try {
int offset = styledText.getOffsetAtLocation(new Point(event.x, event.y));
StyleRange smallestEncompassing = getSmallestEncompassingHyperlink(offset);
if(smallestEncompassing != null) {
Object valueIdentifier = smallestEncompassing.data;
if(valueIdentifier != null) {
Object valueForIdentifierInTemplateGroup = getValueForIdentifierInTemplate(valueIdentifier);
if(valueForIdentifierInTemplateGroup != null) {
snippetTreeViewer.setSelection(new StructuredSelection(valueForIdentifierInTemplateGroup), true);
//updateTextFields();
onNodeSelectionInternal();
snippetTreeViewer.getControl().setFocus();
}
}
}
} catch (IllegalArgumentException e) {
// no character under event.x, event.y
}
}
});
}
private StyleRange getSmallestEncompassingHyperlink(int offset) {
StyleRange smallestEncompassing = null;
for(StyleRange link : hyperlinks) {
//find candidate
if(link.start <= offset && offset <= link.start + link.length) {
//find candidate with smallest length
if(smallestEncompassing == null) {
smallestEncompassing = link;
}
else {
if(link.length <= smallestEncompassing.length) {
smallestEncompassing = link;
}
}
}
}
return smallestEncompassing;
}
private void onNodeDoubleClickInternal() {
TemplateGroupViewerNodeSelectionEvent event = new TemplateGroupViewerNodeSelectionEvent(this, jGroup, cljTemplate, cljNode);
for(TemplateGroupViewerNodeDoubleClickListener listener : nodeDoubleClickListeners) {
listener.nodeDoubleClicked(event);
}
}
private void onNodeSelectionInternal() {
updateTextFields();
TemplateGroupViewerNodeSelectionEvent event = new TemplateGroupViewerNodeSelectionEvent(this, jGroup, cljTemplate, cljNode);
for(TemplateGroupViewerNodeSelectionListener listener : nodeSelectionListeners) {
listener.nodeSelected(event);
}
}
public boolean addNodeSelectionListener(TemplateGroupViewerNodeSelectionListener listener) {
return nodeSelectionListeners.add(listener);
}
public boolean removeNodeSelectionListener(TemplateGroupViewerNodeSelectionListener listener) {
return nodeSelectionListeners.remove(listener);
}
public boolean addNodeDoubleClickListener(TemplateGroupViewerNodeDoubleClickListener listener) {
return nodeDoubleClickListeners.add(listener);
}
public boolean removeNodeDoubleClickListener(TemplateGroupViewerNodeDoubleClickListener listener) {
return nodeDoubleClickListeners.remove(listener);
}
public Object getSelectedSnippetNode() {
IStructuredSelection selection = (IStructuredSelection) snippetTreeViewer.getSelection();
cljNode = selection.getFirstElement();
return cljNode;
}
public List<Object> getSelectedSnippetNodes() {
IStructuredSelection selection = (IStructuredSelection) snippetTreeViewer.getSelection();
cljNode = selection.getFirstElement();
return selection.toList();
}
public Object getSelectedSnippet() {
cljTemplate = jGroup.getSnippet(getSelectedSnippetNode());
return cljTemplate;
}
private void updateTextFields() {
StyledText textWidget = textViewerSnippet.getTextWidget();
Object selectedSnippet = getSelectedSnippet();
if(selectedSnippet == null) {
textWidget.setText("");
return;
}
Object selectedSnippetNode = getSelectedSnippetNode();
TemplatePrettyPrinter prettyprinter = new TemplatePrettyPrinter(jGroup);
prettyprinter.setHighlightNode(selectedSnippetNode);
textWidget.setText(prettyprinter.prettyPrintSnippet(selectedSnippet));
for(StyleRange range : prettyprinter.getStyleRanges())
textWidget.setStyleRange(range);
this.hyperlinks = prettyprinter.getHyperlinks();
}
public void clearSelection() {
cljTemplate = null;
cljNode = null;
// snippetTreeViewer.setSelection(null);
}
public void updateWidgets() {
setInput(jGroup, cljTemplate, cljNode);
updateTextFields();
}
public void setSelection(Object cljTemplate, Object cljNode) {
if(cljNode != null) {
//set selection to node
snippetTreeViewer.setSelection(new StructuredSelection(cljNode), true);
}
else if (cljTemplate != null) {
//set selection to template
snippetTreeViewer.setSelection(new StructuredSelection(cljTemplate), true);
}
else {
//set selection to first template in template group
Tree tree = snippetTreeViewer.getTree();
TreeItem[] items = tree.getItems();
if(items.length > 0)
tree.setSelection(items[0]);
}
}
public void setInput(TemplateGroup jGroup, Object cljTemplate, Object cljNode) {
this.jGroup = jGroup;
this.cljTemplate = cljTemplate;
this.cljNode = cljNode;
Object cljGroup = jGroup.getGroup();
snippetElementCol.setLabelProvider(new TemplateTreeLabelProviders.ElementColumnLabelProvider(cljGroup));
snippetNodeCol.setLabelProvider(new TemplateTreeLabelProviders.NodeColumnLabelProvider(cljGroup));
snippetPropCol.setLabelProvider(new TemplateTreeLabelProviders.PropertyColumnLabelProvider(cljGroup));
snippetKindCol.setLabelProvider(new TemplateTreeLabelProviders.KindColumnLabelProvider(cljGroup));
snippetDirectivesCol.setLabelProvider(new TemplateTreeLabelProviders.DirectivesColumnLabelProvider(cljGroup));
snippetTreeViewer.setInput(cljGroup);
setSelection(cljTemplate, cljNode);
onNodeSelectionInternal();
}
@Override
public boolean setFocus() {
return snippetTreeViewer.getControl().setFocus();
}
}