/*******************************************************************************
* Copyright (c) 2009, 2013 EclipseSource and others.
* 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:
* EclipseSource - initial API and implementation
******************************************************************************/
package org.eclipse.rap.examples.pages;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.eclipse.jface.viewers.CellEditor;
import org.eclipse.jface.viewers.CellLabelProvider;
import org.eclipse.jface.viewers.CheckStateChangedEvent;
import org.eclipse.jface.viewers.CheckboxTreeViewer;
import org.eclipse.jface.viewers.ColumnViewer;
import org.eclipse.jface.viewers.ColumnViewerEditor;
import org.eclipse.jface.viewers.ColumnViewerEditorActivationEvent;
import org.eclipse.jface.viewers.ColumnViewerEditorActivationStrategy;
import org.eclipse.jface.viewers.ColumnViewerToolTipSupport;
import org.eclipse.jface.viewers.ICellModifier;
import org.eclipse.jface.viewers.ICheckStateListener;
import org.eclipse.jface.viewers.ITreeContentProvider;
import org.eclipse.jface.viewers.ITreeSelection;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.TextCellEditor;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.TreeViewerEditor;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerCell;
import org.eclipse.rap.examples.ExampleUtil;
import org.eclipse.rap.examples.IExamplePage;
import org.eclipse.rap.examples.pages.internal.ImageUtil;
import org.eclipse.rap.rwt.SingletonUtil;
import org.eclipse.swt.SWT;
import org.eclipse.swt.dnd.DND;
import org.eclipse.swt.dnd.DragSourceAdapter;
import org.eclipse.swt.dnd.DragSourceEvent;
import org.eclipse.swt.dnd.DropTargetAdapter;
import org.eclipse.swt.dnd.DropTargetEvent;
import org.eclipse.swt.dnd.Transfer;
import org.eclipse.swt.dnd.TransferData;
import org.eclipse.swt.events.FocusAdapter;
import org.eclipse.swt.events.FocusEvent;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Device;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.FontData;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Item;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.swt.widgets.TreeColumn;
public class TreeViewerExample implements IExamplePage {
private static final int EDITOR_ACTIVATE = ColumnViewerEditor.TABBING_HORIZONTAL
| ColumnViewerEditor.TABBING_MOVE_TO_ROW_NEIGHBOR
| ColumnViewerEditor.TABBING_VERTICAL
| ColumnViewerEditor.KEYBOARD_ACTIVATION;
private final static int MODERN_STYLE = 0;
private final static int TABLE_STYLE = 1;
private TreeViewer currentViewer;
private TreeViewer simpleTree;
private TreeViewer complexTree;
private int newItem;
/////////////////
// create widgets
public void createControl( Composite parent ) {
parent.setLayout( ExampleUtil.createMainLayout( 2 ) );
createTopLeft( parent );
createTopRight( parent );
createFooter( parent );
setFocus();
}
private void createTopLeft( Composite parent ) {
Composite composite = new Composite( parent, SWT.NONE );
composite.setLayoutData( ExampleUtil.createFillData() );
composite.setLayout( ExampleUtil.createFillLayout(true) );
simpleTree = createSimpleTree( composite );
}
private void createTopRight( Composite parent ) {
Composite composite = new Composite( parent, SWT.NONE );
composite.setLayoutData( ExampleUtil.createFillData() );
composite.setLayout( ExampleUtil.createFillLayout(true) );
complexTree = createComplexTree( composite );
}
private void createFooter( Composite parent ) {
Composite footerComp = new Composite( parent, SWT.NONE );
footerComp.setLayout( ExampleUtil.createRowLayout( SWT.HORIZONTAL, true ) );
createControlButtons( footerComp );
}
private void setFocus() {
Tree tree = simpleTree.getTree();
tree.forceFocus();
tree.select( tree.getItem( 0 ) );
}
private void createControlButtons( Composite parent ) {
Button newButton = new Button( parent, SWT.PUSH );
newButton.setText( "New Item" );
newButton.addSelectionListener( new NewButtonSelectionHandler() );
Button removeButton = new Button( parent, SWT.PUSH );
removeButton.setText( "Remove Item(s)" );
removeButton.addSelectionListener( new RemoveButtonSelectionHandler() );
}
private TreeViewer createSimpleTree( Composite parent ) {
Tree tree = new Tree( parent, SWT.BORDER | SWT.MULTI | SWT.FULL_SELECTION );
TreeViewer result = new TreeViewer( tree );
result.setContentProvider( new TreeContentProvider() );
TreeLabelProvider labelProvider = new TreeLabelProvider( parent.getDisplay(), MODERN_STYLE );
result.setLabelProvider( labelProvider );
result.setInput( createModel() );
result.expandAll();
tree.addFocusListener( new TreeFocusGainedHandler() );
addDNDSupport( result );
addCellEditor( result );
addToolTipSupport( result );
return result;
}
private TreeViewer createComplexTree( Composite parent ) {
int style = SWT.BORDER | SWT.MULTI | SWT.FULL_SELECTION | SWT.CHECK;
Tree tree = new Tree( parent, style );
createColumn( tree, "City", SWT.LEFT, 185 );
createColumn( tree, "Timezone", SWT.CENTER, 85 );
createColumn( tree, "Offset", SWT.CENTER, 65 );
tree.setLinesVisible( true );
tree.setHeaderVisible( true );
CheckboxTreeViewer result = new CheckboxTreeViewer( tree );
result.addCheckStateListener( new TreeCheckStateListener( result ) );
result.setContentProvider( new TreeContentProvider() );
TreeLabelProvider labelProvider
= new TreeLabelProvider( parent.getDisplay(), TABLE_STYLE );
result.setLabelProvider( labelProvider );
result.setInput( createModel() );
result.expandAll();
tree.addFocusListener( new TreeFocusGainedHandler() );
addDNDSupport( result );
addCellEditor( result );
addToolTipSupport( result );
return result;
}
private static TreeColumn createColumn( Tree parent, String name, int style, int width ) {
TreeColumn result = new TreeColumn( parent, style );
result.setText( name );
result.setWidth( width );
result.setMoveable( true );
result.setResizable( true );
return result;
}
//////////////
// Drag & Drop
private static void addDNDSupport( TreeViewer viewer ) {
Transfer[] types = new Transfer[] { TreeObjectTransfer.getInstance() };
TreeDragListener dragListener = new TreeDragListener( viewer );
viewer.addDragSupport( DND.DROP_MOVE, types, dragListener );
TreeDropListener dropListener = new TreeDropListener( viewer );
viewer.addDropSupport( DND.DROP_MOVE, types, dropListener );
}
//////////////
// Cell-editor
private static void addCellEditor( TreeViewer viewer ) {
CellEditor[] editors = new CellEditor[] {
new TextCellEditor( viewer.getTree() ),
new TextCellEditor( viewer.getTree() ),
new TextCellEditor( viewer.getTree() )
};
viewer.setCellEditors( editors );
viewer.setCellModifier( new CellModifier( viewer ) );
String[] columnProperties = new String[] { "Name", "Timezone", "Offset" };
viewer.setColumnProperties( columnProperties );
ColumnViewerEditorActivationStrategy activationStrategy
= new CellEditorActivationStrategy( viewer );
TreeViewerEditor.create( viewer, activationStrategy, EDITOR_ACTIVATE );
}
private static void addToolTipSupport( TreeViewer viewer ) {
ColumnViewerToolTipSupport.enableFor( viewer );
}
private static TreeObject createModel() {
TreeObject result = new TreeObject( "" );
TreeObject asia = new TreeObject( "Asia" );
result.addChild( asia );
asia.addChild( new City( "Hong Kong", "HKT", +8 ) );
asia.addChild( new City( "Tokyo", "JST", +9 ) );
TreeObject europe = new TreeObject( "Europe" );
result.addChild( europe );
europe.addChild( new City( "Lisbon", "WET", 0 ) );
europe.addChild( new City( "Berlin", "CET", +1 ) );
europe.addChild( new City( "Sofia", "EET", +2 ) );
europe.addChild( new City( "Moscow", "MT", +3 ) );
TreeObject northAmerica = new TreeObject( "North America" );
result.addChild( northAmerica );
northAmerica.addChild( new City( "New York", "EST", -5 ) );
northAmerica.addChild( new City( "Chicago", "CST", -6 ) );
northAmerica.addChild( new City( "Los Angeles", "PST", -8 ) );
northAmerica.addChild( new City( "Anchorage", "AKST", -9 ) );
return result;
}
private final class TreeFocusGainedHandler extends FocusAdapter {
@Override
public void focusGained( FocusEvent event ) {
currentViewer = null;
Tree currentTree = ( Tree )event.widget;
if( simpleTree.getTree() == currentTree ) {
currentViewer = simpleTree;
} else if( complexTree.getTree() == currentTree ) {
currentViewer = complexTree;
}
}
}
private static class TreeCheckStateListener implements ICheckStateListener {
private final CheckboxTreeViewer viewer;
TreeCheckStateListener( CheckboxTreeViewer viewer ) {
this.viewer = viewer;
}
public void checkStateChanged( CheckStateChangedEvent event ) {
TreeObject treeObject = ( TreeObject )event.getElement();
boolean checked = event.getChecked();
updateChildren( checked, treeObject );
updateParent( treeObject );
}
private void updateParent( TreeObject treeObject ) {
TreeObject parent = treeObject.getParent();
if( parent != null ) {
boolean parentChecked = true;
TreeObject[] children = parent.getChildren();
for( int i = 0; parentChecked && i < children.length; i++ ) {
TreeObject child = children[ i ];
if( !viewer.getChecked( child ) ) {
parentChecked = false;
}
}
viewer.setChecked( parent, parentChecked );
updateParent( parent );
}
}
private void updateChildren( boolean checked, TreeObject parent ) {
TreeObject[] children = parent.getChildren();
for( int i = 0; i < children.length; i++ ) {
TreeObject treeObject = children[ i ];
viewer.setChecked( treeObject, checked );
if( treeObject.hasChildren() ) {
updateChildren( checked, treeObject );
}
}
}
}
private final class RemoveButtonSelectionHandler extends SelectionAdapter {
@Override
public void widgetSelected( SelectionEvent event ) {
if( currentViewer != null && !currentViewer.getSelection().isEmpty() ) {
ITreeSelection sel = ( ITreeSelection )currentViewer.getSelection();
Iterator iterator = sel.iterator();
TreeObject parent = null;
while( iterator.hasNext() ) {
TreeObject obj = ( TreeObject )iterator.next();
parent = obj.getParent();
if( parent != null ) {
parent.removeChild( obj );
}
}
if( parent != null ) {
TreeObject newSel = null;
if( parent.getParent() == null ) {
TreeObject[] children = parent.getChildren();
if( children.length > 0 ) {
newSel = children[ 0 ];
}
} else {
newSel = parent;
}
if( newSel != null ) {
currentViewer.setSelection( new StructuredSelection( newSel ) );
}
}
currentViewer.refresh();
currentViewer.getTree().forceFocus();
}
}
}
private final class NewButtonSelectionHandler extends SelectionAdapter {
@Override
public void widgetSelected( SelectionEvent event ) {
if( currentViewer != null && !currentViewer.getSelection().isEmpty() ) {
ITreeSelection sel = ( ITreeSelection )currentViewer.getSelection();
TreeObject obj = ( TreeObject )sel.getFirstElement();
newItem++;
TreeObject newObject = new City( "New Item " + newItem , "", 0 );
obj.addChild( newObject );
currentViewer.expandToLevel( obj, 1 );
currentViewer.refresh();
currentViewer.setSelection( new StructuredSelection( newObject ) );
currentViewer.getTree().forceFocus();
}
}
}
//////////////
// Drag & Drop
private static class TreeDragListener extends DragSourceAdapter {
private final TreeViewer viewer;
private Object dragData;
TreeDragListener( TreeViewer viewer ) {
this.viewer = viewer;
}
@Override
public void dragStart( DragSourceEvent event ) {
dragData = getTreeObject( event.x, event.y );
}
@Override
public void dragSetData( DragSourceEvent event ) {
event.data = dragData;
}
@Override
public void dragFinished( DragSourceEvent event ) {
viewer.refresh();
}
private TreeObject getTreeObject( int x, int y ) {
TreeObject result = null;
ViewerCell cell = viewer.getCell( new Point( x, y ) );
if( cell != null ) {
result = ( TreeObject )cell.getElement();
}
return result;
}
}
private static class TreeDropListener extends DropTargetAdapter {
private final TreeViewer viewer;
public TreeDropListener( TreeViewer viewer ) {
this.viewer = viewer;
}
@Override
public void dragEnter( DropTargetEvent event ){
event.feedback = DND.FEEDBACK_EXPAND | DND.FEEDBACK_SELECT | DND.FEEDBACK_SCROLL;
}
@Override
public void drop( DropTargetEvent event ) {
if( event.data == null || event.item == null ) {
event.detail = DND.DROP_NONE;
} else {
TreeObject draggedObject = ( TreeObject )event.data;
TreeObject targetObject = ( TreeObject )event.item.getData();
if( isValidDrop( draggedObject, targetObject ) ) {
draggedObject.getParent().removeChild( draggedObject );
targetObject.addChild( draggedObject );
viewer.refresh();
}
}
}
private static boolean isValidDrop( TreeObject draggedObject, TreeObject targetObject ) {
boolean result = false;
if( draggedObject != null && targetObject != null ) {
result = true;
TreeObject current = targetObject;
while( current != null && result ) {
result = current != draggedObject;
current = current.getParent();
}
}
return result;
}
}
private static class TreeObjectTransfer extends Transfer {
private static final String TYPE_NAME = "treeObject";
private static final int TYPE_ID = registerType( TYPE_NAME );
private TreeObjectTransfer() {
}
public static TreeObjectTransfer getInstance() {
return SingletonUtil.getSessionInstance( TreeObjectTransfer.class );
}
@Override
protected int[] getTypeIds() {
return new int[]{ TYPE_ID };
}
@Override
protected String[] getTypeNames() {
return new String[]{ TYPE_NAME };
}
@Override
public TransferData[] getSupportedTypes() {
int[] types = getTypeIds();
TransferData[] result = new TransferData[ types.length ];
for( int i = 0; i < types.length; i++ ) {
result[ i ] = new TransferData();
result[ i ].type = types[ i ];
}
return result;
}
@Override
public boolean isSupportedType( TransferData transferData ) {
boolean result = false;
if( transferData != null ) {
int[] types = getTypeIds();
for( int i = 0; !result && i < types.length; i++ ) {
if( transferData.type == types[ i ] ) {
result = true;
}
}
}
return result;
}
@Override
public void javaToNative( Object object, TransferData transferData ) {
transferData.data = object;
}
@Override
public Object nativeToJava( TransferData transferData ) {
return transferData.data;
}
}
private static final class CellModifier implements ICellModifier {
private final TreeViewer viewer;
CellModifier( TreeViewer viewer ) {
this.viewer = viewer;
}
public boolean canModify( Object element, String property ) {
return element instanceof City || property.equals( "Name" );
}
public Object getValue( Object element, String property ) {
String result = "";
TreeObject treeObject = ( TreeObject )element;
if( property.equals( "Name" ) ) {
result = treeObject.getTitle();
} else if( property.equals( "Timezone" ) ) {
City city = ( City )treeObject;
result = city.getTimeZone();
} else if( property.equals( "Offset" ) ) {
City city = ( City )treeObject;
result = String.valueOf( city.getOffset() );
} else {
throw new IllegalArgumentException( "Unkown property " + property );
}
return result;
}
public void modify( Object element, String property, Object value ) {
TreeObject treeObject;
if( element instanceof Item ) {
treeObject = ( TreeObject )( ( Item ) element ).getData();
} else {
treeObject = ( TreeObject )element;
}
String string = ( String )value;
if( property.equals( "Name" ) ) {
treeObject.setName( string );
} else if( property.equals( "Timezone" ) ) {
City city = ( City )treeObject;
city.setTimeZone( string );
} else if( property.equals( "Offset" ) ) {
City city = ( City )treeObject;
try {
city.setOffset( Integer.parseInt( string ) );
} catch( NumberFormatException e ) {
}
} else {
throw new IllegalArgumentException( "Unkown property " + property );
}
viewer.update( treeObject, new String[]{ property } );
}
}
private static final class CellEditorActivationStrategy
extends ColumnViewerEditorActivationStrategy
{
CellEditorActivationStrategy( ColumnViewer viewer ) {
super( viewer );
}
@Override
protected boolean isEditorActivationEvent( ColumnViewerEditorActivationEvent event ) {
boolean isTraversal = event.eventType == ColumnViewerEditorActivationEvent.TRAVERSAL;
boolean isDoubleClick
= event.eventType == ColumnViewerEditorActivationEvent.MOUSE_DOUBLE_CLICK_SELECTION;
boolean isProgrammatic = event.eventType == ColumnViewerEditorActivationEvent.PROGRAMMATIC;
return isTraversal || isDoubleClick || isProgrammatic;
}
}
////////
// Model
private static class TreeObject {
private final List<TreeObject> children;
private String name;
private TreeObject parent;
public TreeObject( String name ) {
setName( name );
children = new ArrayList<TreeObject>();
}
public void setParent( TreeObject parent ) {
this.parent = parent;
}
public TreeObject getParent() {
return parent;
}
public void setName( String name ) {
this.name = name;
}
public String getTitle() {
return name;
}
public void addChild( TreeObject child ) {
children.add( child );
child.setParent( this );
}
public void removeChild( TreeObject child ) {
children.remove( child );
child.setParent( null );
}
public TreeObject[] getChildren() {
TreeObject[] result = new TreeObject[ children.size() ];
children.toArray( result );
return result;
}
public boolean hasChildren() {
return children.size() > 0;
}
@Override
public String toString() {
return name;
}
}
private static class City extends TreeObject {
private String timezone;
private int offset;
public City( String name, String timezone, int offset ) {
super( name );
setTimeZone( timezone );
setOffset( offset );
}
private void setOffset( int offset ) {
this.offset = offset;
}
public void setTimeZone( String tz ) {
timezone = tz;
}
private int getOffset() {
return offset;
}
public String getTimeZone() {
return timezone;
}
}
private static final class TreeLabelProvider extends CellLabelProvider {
private static final String ICON_GREENDOT = "greendot.gif";
private static final String ICON_WORLD = "world.gif";
private static final String ICON_EARTH = "earth-icon.png";
private static final int COLUMN_TEXT = 0;
private static final int COLUMN_OFFSET = 2;
private static final int COLUMN_TIMEZONE = 1;
private final Device device;
private final Image continentImage;
private final Image cityImage;
private final Font cityFont;
private final Font continentFont;
private final Color timezoneTextColor;
private final Color offsetTextColor;
private final int style;
TreeLabelProvider( Device device, int style ) {
this.device = device;
this.style = style;
cityFont = createFont( "Times New Roman", 13, SWT.NONE );
continentFont = createFont( "Arial", 14, SWT.ITALIC );
timezoneTextColor = new Color( device, 239, 41, 41 );
offsetTextColor = new Color( device, 252, 175, 62 );
if( style == MODERN_STYLE ) {
continentImage = ImageUtil.getImage( device, ICON_EARTH );
cityImage = ImageUtil.getImage( device, ICON_GREENDOT );
} else {
continentImage = ImageUtil.getImage( device, ICON_WORLD );
cityImage = ImageUtil.getImage( device, ICON_GREENDOT );
}
}
@Override
public void update( ViewerCell cell ) {
TreeObject treeObject = ( TreeObject )cell.getElement();
int columnIndex = cell.getColumnIndex();
switch( columnIndex ) {
case COLUMN_TEXT:
updateName( cell, treeObject );
break;
case COLUMN_TIMEZONE:
updateTimeZone( cell, treeObject );
break;
case COLUMN_OFFSET:
updateOffset( cell, treeObject );
break;
}
}
@Override
public String getToolTipText( Object element ) {
String result = "";
if( element instanceof City ) {
City city = ( City )element;
String name = city.getTitle();
String timeZone = city.getTimeZone();
String utcOffset = getUTCOffset( city );
result = name + " (" + timeZone + ", " + utcOffset + ")";
}
return result;
}
private void updateName( ViewerCell cell, TreeObject treeObject ) {
cell.setText( treeObject.name );
if( style == MODERN_STYLE ) {
if( treeObject instanceof City ) {
cell.setFont( cityFont );
} else {
cell.setFont( continentFont );
}
}
cell.setImage( treeObject instanceof City ? cityImage : continentImage );
}
private void updateTimeZone( ViewerCell cell, TreeObject treeObject ) {
if( treeObject instanceof City ) {
City city = ( City )treeObject;
cell.setText( city.getTimeZone() );
if( style == TABLE_STYLE ) {
cell.setForeground( timezoneTextColor );
}
}
}
private void updateOffset( ViewerCell cell, TreeObject treeObject ) {
if( treeObject instanceof City ) {
if( style == TABLE_STYLE ) {
cell.setForeground( offsetTextColor );
}
City city = ( City )treeObject;
cell.setText( getUTCOffset( city ) );
}
}
private Font createFont( String name, int size, int style ) {
FontData fontData = new FontData( name, size, style );
return new Font( device, fontData );
}
private static String getUTCOffset( City city ) {
String sign = city.getOffset() >= 0 ? "-" : "";
return "UTC " + sign + String.valueOf( city.getOffset() );
}
}
private static class TreeContentProvider implements ITreeContentProvider {
public Object[] getElements( Object parent ) {
return getChildren( parent );
}
public Object getParent( Object child ) {
Object result = null;
if( child instanceof City ) {
result = ( ( City )child ).getParent();
}
return result;
}
public Object[] getChildren( Object parent ) {
Object[] result = new Object[ 0 ];
if( parent instanceof TreeObject ) {
result = ( ( TreeObject )parent ).getChildren();
}
return result;
}
public boolean hasChildren( Object parent ) {
boolean result = false;
if( parent instanceof TreeObject ) {
result = ( ( TreeObject )parent ).hasChildren();
}
return result;
}
public void inputChanged( Viewer viewer, Object oldInput, Object newInput ) {
}
public void dispose() {
}
}
}