blob: 30f6b752cb9ec9beac761de4f91b4156aff84eb9 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2013 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.e4.tools.event.spy.internal.ui;
import java.util.ArrayList;
import org.eclipse.core.databinding.beans.PojoObservables;
import org.eclipse.core.databinding.observable.IObservable;
import org.eclipse.core.databinding.observable.list.IObservableList;
import org.eclipse.core.databinding.observable.list.WritableList;
import org.eclipse.core.databinding.observable.map.IObservableMap;
import org.eclipse.core.databinding.observable.masterdetail.IObservableFactory;
import org.eclipse.e4.tools.event.spy.internal.model.CapturedEvent;
import org.eclipse.e4.tools.event.spy.internal.model.IEventItem;
import org.eclipse.e4.tools.event.spy.internal.model.ItemToFilter;
import org.eclipse.e4.tools.event.spy.internal.util.JDTUtils;
import org.eclipse.jface.databinding.viewers.ObservableListTreeContentProvider;
import org.eclipse.jface.databinding.viewers.ObservableMapLabelProvider;
import org.eclipse.jface.databinding.viewers.TreeStructureAdvisor;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.dnd.Clipboard;
import org.eclipse.swt.dnd.TextTransfer;
import org.eclipse.swt.dnd.Transfer;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.events.FocusAdapter;
import org.eclipse.swt.events.FocusEvent;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.events.KeyListener;
import org.eclipse.swt.events.MouseAdapter;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseMoveListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Cursor;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.FontData;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.RGB;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.graphics.Resource;
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.Tree;
import org.eclipse.swt.widgets.TreeColumn;
import org.eclipse.swt.widgets.TreeItem;
public class CapturedEventTree extends TreeViewer {
private ICapturedEventTreeListener listener;
private WritableList capturedEvents;
private Clipboard clipboard;
private TreeItemCursor treeItemCursor;
private TreeItemForeground treeItemForeground;
private TreeItemBackground treeItemBackground;
private TreeItemFont treeItemFont;
private SelectedTreeItem selectedClassNameTreeItem;
private SelectedTreeItem selectedTreeItem;
public CapturedEventTree(Composite parent) {
super(parent, SWT.BORDER | SWT.H_SCROLL | SWT.V_SCROLL | SWT.FULL_SELECTION);
getTree().setHeaderVisible(true);
getTree().setLinesVisible(true);
TreeColumn column = new TreeColumn(getTree(), SWT.LEFT);
column.setText(ItemToFilter.Topic.toString());
column.setWidth(350);
column = new TreeColumn(getTree(), SWT.LEFT);
column.setText(ItemToFilter.Publisher.toString());
column.setWidth(150);
column = new TreeColumn(getTree(), SWT.LEFT);
column.setText(ItemToFilter.ChangedElement.toString());
column.setWidth(150);
ObservableListTreeContentProvider contentProvider = new ObservableListTreeContentProvider(
new CapturedEventsObservableFactory(), new CapturedEventsTreeStructureAdvisor());
setContentProvider(contentProvider);
IObservableMap[] attributes = PojoObservables.observeMaps(contentProvider.getKnownElements(), IEventItem.class,
new String[] { "name", "param1", "param2" });
setLabelProvider(new ObservableMapLabelProvider(attributes));
capturedEvents = new WritableList(new ArrayList<CapturedEvent>(), CapturedEvent.class);
setInput(capturedEvents);
clipboard = new Clipboard(getTree().getDisplay());
createTreeItemResources();
addTreeEventListeners();
}
private static class CapturedEventsTreeStructureAdvisor extends TreeStructureAdvisor {
@Override
public Boolean hasChildren(Object element) {
if (element instanceof CapturedEvent) {
return !((CapturedEvent) element).getParameters().isEmpty();
}
return false;
}
}
private static class CapturedEventsObservableFactory implements IObservableFactory {
@Override
public IObservable createObservable(Object target) {
if (target instanceof IObservableList) {
return (IObservableList) target;
}
if (target instanceof CapturedEvent) {
return PojoObservables.observeList(target, "parameters");
}
return null;
}
}
private void createTreeItemResources() {
Display display = getTree().getDisplay();
treeItemCursor = new TreeItemCursor(getTree().getCursor(), display.getSystemCursor(SWT.CURSOR_HAND));
treeItemForeground = new TreeItemForeground(new Color(display, new RGB(0, 0, 120)),
display.getSystemColor(SWT.COLOR_LIST_SELECTION_TEXT), display.getSystemColor(SWT.COLOR_BLACK));
treeItemBackground = new TreeItemBackground(display.getSystemColor(SWT.COLOR_LIST_SELECTION),
getTree().getBackground());
Font currentFont = getTree().getFont();
FontData currentFontData = currentFont.getFontData()[0];
treeItemFont = new TreeItemFont(currentFont,
new Font(display, currentFontData.getName(), currentFontData.getHeight(), SWT.ITALIC));
selectedClassNameTreeItem = new SelectedTreeItem() {
@Override
public void clear() {
redrawTreeItem(getTreeItem(), getColumnIndex());
super.clear();
Tree tree = getTree();
if (tree.getCursor() != treeItemCursor.getDefaultCursor()) {
tree.setCursor(treeItemCursor.getDefaultCursor());
}
}
};
selectedTreeItem = new SelectedTreeItem() {
@Override
public void clear() {
redrawTreeItem(getTreeItem(), getColumnIndex());
super.clear();
}
};
}
private void addTreeEventListeners() {
getTree().addDisposeListener(new DisposeListener() {
@Override
public void widgetDisposed(DisposeEvent e) {
if (clipboard != null && !clipboard.isDisposed()) {
clipboard.dispose();
}
disposeResource(treeItemForeground.getParamColor());
disposeResource(treeItemFont.getSelectedClassNameFont());
}
private void disposeResource(Resource resource) {
if (resource != null && !resource.isDisposed()) {
resource.dispose();
}
}
});
getTree().addMouseMoveListener(new MouseMoveListener() {
@Override
public void mouseMove(MouseEvent e) {
selectedClassNameTreeItem.clear();
// we can select and finally open the class only when 'ctrl' is
// pressed
if ((e.stateMask & SWT.CTRL) != SWT.CTRL) {
return;
}
TreeItem item = getTree().getItem(new Point(e.x, e.y));
int index = getSelectedColumnIndex(item, e.x, e.y);
if (index > 0 /* we check the 2nd and 3rd column only */ && item
.getParentItem() == null /*
* we don't check parameters at
* this moment
*/) {
String text = item.getText(index);
if (JDTUtils.containsClassName(text)) {
selectedClassNameTreeItem.setText(text);
selectedClassNameTreeItem.setColumnIndex(index);
selectedClassNameTreeItem.setTreeItem(item);
getTree().setCursor(treeItemCursor.getPointerCursor());
redrawTreeItem(item, index);
}
}
}
});
getTree().addMouseListener(new MouseAdapter() {
@Override
public void mouseDown(MouseEvent e) {
TreeItem item = getTree().getItem(new Point(e.x, e.y));
updateSelectedTreeItem(item, getSelectedColumnIndex(item, e.x, e.y));
if (listener != null && (e.stateMask & SWT.CTRL) == SWT.CTRL
&& selectedClassNameTreeItem.getText() != null) {
listener.treeItemWithClassNameClicked(selectedClassNameTreeItem.getText());
}
}
});
getTree().addListener(SWT.EraseItem, new Listener() {
@Override
public void handleEvent(Event event) {
if ((event.detail & SWT.FOREGROUND) == SWT.FOREGROUND) {
event.detail &= ~SWT.FOREGROUND;
}
if ((event.detail & SWT.SELECTED) == SWT.SELECTED) {
event.detail &= ~SWT.SELECTED;
}
}
});
getTree().addListener(SWT.PaintItem, new Listener() {
@Override
public void handleEvent(Event event) {
TreeItem item = (TreeItem) event.item;
String text = item.getText(event.index);
int xOffset = item.getParentItem() != null ? 10 : 2;
Rectangle rec = item.getBounds(event.index);
event.gc.setFont(getFont(item, event.index));
event.gc.setForeground(getForeground(item, event.index));
event.gc.setBackground(getBackground(item, event.index));
event.gc.fillRectangle(rec.x, rec.y, rec.width, rec.height);
event.gc.drawText(text, event.x + xOffset, event.y, true);
}
});
getTree().addKeyListener(new KeyListener() {
@Override
public void keyPressed(KeyEvent e) {
String text = selectedTreeItem.getText();
if (text == null || (e.stateMask & SWT.CTRL) != SWT.CTRL) {
return;
}
if (e.keyCode == 'c' && text.trim().length() > 0) {
clipboard.setContents(new Object[] { text }, new Transfer[] { TextTransfer.getInstance() });
} else if (e.keyCode == SWT.ARROW_LEFT) {
updateSelectedTreeItem(selectedTreeItem.getTreeItem(),
Math.max(0, selectedTreeItem.getColumnIndex() - 1));
} else if (e.keyCode == SWT.ARROW_RIGHT) {
updateSelectedTreeItem(selectedTreeItem.getTreeItem(),
Math.min(getTree().getColumnCount() - 1, selectedTreeItem.getColumnIndex() + 1));
}
}
@Override
public void keyReleased(KeyEvent e) {
selectedClassNameTreeItem.clear();
}
});
getTree().addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
if ((e.stateMask & SWT.BUTTON1) != SWT.BUTTON1) {
updateSelectedTreeItem((TreeItem) e.item,
selectedTreeItem.getText() != null ? selectedTreeItem.getColumnIndex() : 0);
}
}
});
getTree().addFocusListener(new FocusAdapter() {
@Override
public void focusLost(FocusEvent e) {
selectedClassNameTreeItem.clear();
}
});
}
private void updateSelectedTreeItem(TreeItem item, int columnIndex) {
if (columnIndex > -1) {
selectedTreeItem.clear(); // clear old selection
selectedTreeItem.setTreeItem(item);
selectedTreeItem.setColumnIndex(columnIndex);
selectedTreeItem.setText(item.getText(columnIndex));
redrawTreeItem(item, columnIndex);
}
}
private Color getForeground(TreeItem item, int index) {
if (selectedTreeItem.getTreeItem() == item && selectedTreeItem.getColumnIndex() == index) {
return treeItemForeground.getSelectedColor();
}
if (item.getParentItem() != null) {
return treeItemForeground.getParamColor();
}
return treeItemForeground.getDefaultColor();
}
private Color getBackground(TreeItem item, int index) {
if (selectedTreeItem.getTreeItem() == item && selectedTreeItem.getColumnIndex() == index) {
return treeItemBackground.getSelectedColor();
}
return treeItemBackground.getDefaultColor();
}
private Font getFont(TreeItem item, int columnIndex) {
if (selectedClassNameTreeItem.getTreeItem() == item
&& selectedClassNameTreeItem.getColumnIndex() == columnIndex) {
return treeItemFont.getSelectedClassNameFont();
}
return treeItemFont.getDefaultFont();
}
private void redrawTreeItem(TreeItem item, int columnIndex) {
if (item != null && !item.isDisposed()) {
Rectangle rec = item.getBounds(columnIndex);
getTree().redraw(rec.x, rec.y, rec.width, rec.height, true);
}
}
private int getSelectedColumnIndex(TreeItem item, int mouseX, int mouseY) {
for (int i = 0; item != null && i < getTree().getColumnCount(); i++) {
Rectangle rec = item.getBounds(i);
if (mouseX >= rec.x && mouseX <= rec.x + rec.width) {
return i;
}
}
return -1;
}
public void addEvent(CapturedEvent event) {
capturedEvents.add(event);
}
public void setListener(ICapturedEventTreeListener listener) {
this.listener = listener;
}
public void removeAll() {
capturedEvents.clear();
}
private static class TreeItemForeground {
private final Color paramColor;
private final Color selectedColor;
private final Color defaultColor;
public TreeItemForeground(Color paramColor, Color selectedColor, Color defaultColor) {
this.paramColor = paramColor;
this.selectedColor = selectedColor;
this.defaultColor = defaultColor;
}
public Color getParamColor() {
return paramColor;
}
public Color getSelectedColor() {
return selectedColor;
}
public Color getDefaultColor() {
return defaultColor;
}
}
private static class TreeItemBackground {
private final Color selectedColor;
private final Color defaultColor;
public TreeItemBackground(Color selectedColor, Color defaultColor) {
this.selectedColor = selectedColor;
this.defaultColor = defaultColor;
}
public Color getSelectedColor() {
return selectedColor;
}
public Color getDefaultColor() {
return defaultColor;
}
}
private static class TreeItemCursor {
private final Cursor defaultCursor;
private final Cursor pointerCursor;
public TreeItemCursor(Cursor defaultCursor, Cursor pointerCursor) {
this.defaultCursor = defaultCursor;
this.pointerCursor = pointerCursor;
}
public Cursor getDefaultCursor() {
return defaultCursor;
}
public Cursor getPointerCursor() {
return pointerCursor;
}
}
private static class TreeItemFont {
private final Font defaultFont;
private final Font selectedClassNameFont;
public TreeItemFont(Font defaultFont, Font selectedClassNameFont) {
this.defaultFont = defaultFont;
this.selectedClassNameFont = selectedClassNameFont;
}
public Font getDefaultFont() {
return defaultFont;
}
public Font getSelectedClassNameFont() {
return selectedClassNameFont;
}
}
private static class SelectedTreeItem {
private TreeItem treeItem;
private int columnIndex;
private String text;
public SelectedTreeItem() {
clear();
}
public void setTreeItem(TreeItem treeItem) {
this.treeItem = treeItem;
}
public TreeItem getTreeItem() {
return treeItem;
}
public void setColumnIndex(int columnIndex) {
this.columnIndex = columnIndex;
}
public int getColumnIndex() {
return columnIndex;
}
public void setText(String text) {
this.text = text;
}
public String getText() {
return text;
}
public void clear() {
treeItem = null;
columnIndex = -1;
text = null;
}
}
}