/*******************************************************************************
* Copyright (c) 2009 the CHISEL group and contributors.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Del Myers -- initial API and implementation
*******************************************************************************/
package org.eclipse.zest.custom.uml.viewers;
import java.util.ArrayList;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IMenuListener;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.viewers.ContentViewer;
import org.eclipse.jface.viewers.ILabelProvider;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredContentProvider;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseTrackListener;
import org.eclipse.swt.events.PaintEvent;
import org.eclipse.swt.events.PaintListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.layout.RowLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.Widget;
public class BreadCrumbViewer extends ContentViewer {
private static class BreadCrumbItem extends Composite implements Listener {
private Label imageLabel;
private Label textLabel;
private Image image;
private String text;
/**
* @param parent
* @param style
*/
public BreadCrumbItem(Composite parent) {
super(parent, SWT.NONE);
GridLayout layout = new GridLayout(2, false);
layout.horizontalSpacing=3;
layout.verticalSpacing=0;
layout.marginHeight=0;
layout.marginWidth=3;
setLayout(layout);
}
/**
*
*/
private void hook() {
if (imageLabel != null) {
imageLabel.addListener(SWT.MouseDown, this);
imageLabel.addListener(SWT.MouseUp, this);
imageLabel.addListener(SWT.MouseDoubleClick, this);
imageLabel.addListener(SWT.KeyDown, this);
imageLabel.addListener(SWT.KeyUp, this);
}
textLabel.addListener(SWT.MouseDown, this);
textLabel.addListener(SWT.MouseUp, this);
textLabel.addListener(SWT.MouseDoubleClick, this);
textLabel.addListener(SWT.KeyDown, this);
textLabel.addListener(SWT.KeyUp, this);
}
public void setText(String text) {
this.text = text;
rebuild();
}
public void setImage(Image image) {
this.image = image;
rebuild();
}
private void rebuild() {
if (imageLabel != null && !imageLabel.isDisposed()) {
imageLabel.dispose();
}
if (textLabel != null && !textLabel.isDisposed()) {
textLabel.dispose();
}
if (image != null) {
imageLabel = new Label(this, SWT.NONE);
imageLabel.setLayoutData(new GridData(SWT.BEGINNING, SWT.BEGINNING, false, false));
imageLabel.setImage(image);
}
textLabel = new Label(this, SWT.NONE);
GridData textLayoutData = new GridData(SWT.BEGINNING, SWT.BEGINNING, true, true);
if (image == null) {
textLayoutData.horizontalSpan=2;
}
textLabel.setText((text != null) ? text : "");
textLabel.setLayoutData(textLayoutData);
hook();
}
/* (non-Javadoc)
* @see org.eclipse.swt.widgets.Listener#handleEvent(org.eclipse.swt.widgets.Event)
*/
public void handleEvent(Event e) {
Event e2 = new Event();
e2.button=e.button;
e2.character=e.character;
e2.count=e.count;
e2.data=getData();
e2.detail=e.detail;
e2.display=getDisplay();
e2.doit=e.doit;
e2.end=e.end;
e2.gc=e.gc;
e2.height=e.height;
e2.index=0;
e2.item=null;
e2.keyCode=e.keyCode;
e2.start=e.start;
e2.stateMask=e.stateMask;
e2.text=e.text;
e2.time=e.time;
e2.type=e.type;
e2.widget=this;
e2.width=e.width;
if (e.widget instanceof Control) {
Point p = new Point(e.x, e.y);
p = ((Control)e.widget).toDisplay(p);
p = toControl(p);
e2.x = p.x;
e2.y = p.y;
} else {
e2.x = 0;
e2.y = 0;
}
notifyListeners(e2.type, e2);
}
}
private class MenuSelectionAction extends Action {
private Object element;
public MenuSelectionAction(Object element) {
ILabelProvider provider = (ILabelProvider) getLabelProvider();
setText("-> " + provider.getText(element));
Image i = provider.getImage(element);
if (i != null) {
setImageDescriptor(ImageDescriptor.createFromImage(i));
}
this.element = element;
}
/* (non-Javadoc)
* @see org.eclipse.jface.action.Action#run()
*/
@Override
public void run() {
setSelection(new StructuredSelection(element));
}
}
private class InternalMenuListener implements IMenuListener {
/* (non-Javadoc)
* @see org.eclipse.jface.action.IMenuListener#menuAboutToShow(org.eclipse.jface.action.IMenuManager)
*/
public void menuAboutToShow(IMenuManager manager) {
for (BreadCrumbItem w : links) {
manager.add(new MenuSelectionAction(w.getData()));
}
}
}
private Composite control;
private Composite linksComposite;
private ArrayList<BreadCrumbItem> links;
private Listener labelSelectionListener;
private ISelection selection;
private Menu menu;
public BreadCrumbViewer(Composite parent, int style) {
control = new Composite(parent, SWT.FLAT | SWT.BORDER);
control.setBackground(parent.getBackground());
control.setLayout(new GridLayout(2, false));
linksComposite = new Composite(control, SWT.FLAT);
linksComposite.setBackground(parent.getBackground());
GridData lcData = new GridData(SWT.LEFT, SWT.FILL, true, false);
lcData.heightHint = control.getFont().getFontData()[0].getHeight() + 10;
linksComposite.setLayoutData(lcData);
this.links = new ArrayList<BreadCrumbItem>();
RowLayout linksLayout = new RowLayout();
linksLayout.fill = true;
linksLayout.wrap = false;
linksLayout.justify =false;
linksLayout.pack = true;
linksComposite.setLayout(linksLayout);
Button menuButton = new Button(control, SWT.PUSH | SWT.RIGHT | SWT.FLAT);
menuButton.addPaintListener(new PaintListener(){
public void paintControl(PaintEvent e) {
if (e.widget instanceof Control) {
Control c = (Control) e.widget;
Rectangle bounds = c.getBounds();
e.gc.fillRectangle(new Rectangle(0,0,bounds.width, bounds.height));
if (e.widget.equals(e.widget.getDisplay().getCursorControl())) {
e.gc.setBackground(e.widget.getDisplay().getSystemColor(SWT.COLOR_WHITE));
e.gc.fillRoundRectangle(0,0, bounds.width-1,bounds.height-1,3,3);
e.gc.drawRoundRectangle(0,0, bounds.width-1,bounds.height-1,3,3);
}
int x = bounds.width/2 - 3;
int y = bounds.height/2 - 3;
e.gc.setAntialias(SWT.ON);
e.gc.setLineWidth(1);
e.gc.drawLine(x, y, x+3, y+3);
e.gc.drawLine(x+3, y+3, x, y+6);
e.gc.drawLine(x+3, y, x+6, y+3);
e.gc.drawLine(x+6, y+3, x+3, y+6);
}
}
});
menuButton.addMouseTrackListener(new MouseTrackListener(){
public void mouseEnter(MouseEvent e) {
}
public void mouseExit(MouseEvent e) {
((Button)e.widget).redraw();
}
public void mouseHover(MouseEvent e) {
}
});
//menuButton.setEnabled(true);
menuButton.setToolTipText("Show Trace To Root");
MenuManager manager = new MenuManager("Show Trace To Root");
this.menu = manager.createContextMenu(menuButton);
manager.setRemoveAllWhenShown(true);
manager.addMenuListener(new InternalMenuListener());
menuButton.addSelectionListener(new SelectionAdapter(){
@Override
public void widgetSelected(SelectionEvent e) {
menu.setVisible(true);
}
});
//for some reason, we have to manually dispose the menu. It isn't done automatically.
menuButton.addDisposeListener(new DisposeListener(){
public void widgetDisposed(DisposeEvent e) {
if (menu != null && !menu.isDisposed()) {
menu.dispose();
}
}
});
GridData buttonData = new GridData(SWT.BEGINNING, SWT.TOP, false, false);
buttonData.widthHint=16;
buttonData.heightHint=16;
menuButton.setLayoutData(buttonData);
labelSelectionListener = new Listener() {
public void handleEvent(Event event) {
switch (event.type) {
case SWT.KeyDown:
if (event.character != ' ' && event.character != '\n') {
return;
}
case SWT.MouseUp:
if (event.button != 1) {
return;
}
}
ISelection selection = new StructuredSelection(event.widget.getData());
setSelection(selection);
}
};
}
/* (non-Javadoc)
* @see org.eclipse.jface.viewers.Viewer#getControl()
*/
@Override
public Control getControl() {
return control;
}
/* (non-Javadoc)
* @see org.eclipse.jface.viewers.Viewer#getSelection()
*/
@Override
public ISelection getSelection() {
return selection;
}
/* (non-Javadoc)
* @see org.eclipse.jface.viewers.Viewer#refresh()
*/
@Override
public void refresh() {
//always do a complete refresh.
for (Widget child : linksComposite.getChildren()) {
child.dispose();
}
links.clear();
Object[] elements = ((IStructuredContentProvider)getContentProvider()).getElements(getInput());
for (int i = 0; i < elements.length-1; i++) {
BreadCrumbItem linkLabel = new BreadCrumbItem(linksComposite);
linkLabel.setCursor(linkLabel.getDisplay().getSystemCursor(SWT.CURSOR_HAND));
linkLabel.addListener(SWT.KeyDown, labelSelectionListener);
linkLabel.addListener(SWT.MouseUp, labelSelectionListener);
linkLabel.setBackground(linksComposite.getBackground());
update(linkLabel, elements[i]);
links.add(linkLabel);
Label arrow = new Label(linksComposite, SWT.NONE);
arrow.setText("->");
arrow.setBackground(control.getBackground());
}
if (elements.length > 0) {
BreadCrumbItem linkLabel = new BreadCrumbItem(linksComposite);
linkLabel.setCursor(linkLabel.getDisplay().getSystemCursor(SWT.CURSOR_HAND));
linkLabel.addListener(SWT.KeyDown, labelSelectionListener);
linkLabel.addListener(SWT.MouseUp, labelSelectionListener);
linkLabel.setBackground(control.getBackground());
update(linkLabel, elements[elements.length-1]);
links.add(linkLabel);
}
linksComposite.layout(true, true);
control.layout(true, true);
}
/**
* @param linkLabel
* @param object
*/
private void update(BreadCrumbItem widget, Object element) {
ILabelProvider provider = (ILabelProvider) getLabelProvider();
widget.setData(element);
widget.setText(provider.getText(element));
widget.setImage(provider.getImage(element));
widget.setForeground(widget.getDisplay().getSystemColor(SWT.COLOR_BLUE));
widget.setCursor(widget.getDisplay().getSystemCursor(SWT.CURSOR_HAND));
}
/* (non-Javadoc)
* @see org.eclipse.jface.viewers.Viewer#setSelection(org.eclipse.jface.viewers.ISelection, boolean)
*/
@Override
public void setSelection(ISelection selection, boolean reveal) {
this.selection = selection;
fireSelectionChanged(new SelectionChangedEvent(this, selection));
}
/* (non-Javadoc)
* @see org.eclipse.jface.viewers.Viewer#inputChanged(java.lang.Object, java.lang.Object)
*/
@Override
protected void inputChanged(Object input, Object oldInput) {
refresh();
}
}